Skip to contentSkip to conversation

GPIO PL061: Adding Clk framework support

PATCH Superseded Viresh KUMAR 7 participants 29 messages Jun 22, 2010 – Aug 3, 2010

Subsystems (2)
lore
Share & tools
lore
#0 lore raw

PATCH v4 1/1 GPIO PL061: Adding Clk framework support

From: Viresh KUMAR <[email protected]>

GPIO Clk is never enabled on Platforms, like: SPEAr, which are based upon clock framework and use PL061 driver. This patch adds support for Clk enabling and disabling as and when gpios are requested and freed.

Modifications in V4:
- Not registering gpio_request and gpio_free if clk is not found.

Modifications in V3:
- if clk_get returns -ENOENT, then work without clk enable/disable.

---
 drivers/gpio/pl061.c |   49 +++++++++++++++++++++++++++++++++++++++++++------
 1 files changed, 43 insertions(+), 6 deletions(-)
drivers/gpio/pl061.c +43 -6
index 105701a..b826d2b 100644
@@ -11,7 +11,10 @@
  *
  * Data sheet: ARM DDI 0190B, September 2000
  */
+
+#include <linux/clk.h>
 #include <linux/spinlock.h>
+#include <linux/err.h>
 #include <linux/errno.h>
 #include <linux/module.h>
 #include <linux/list.h>
@@ -56,8 +59,26 @@ struct pl061_gpio {
 	void __iomem		*base;
 	unsigned		irq_base;
 	struct gpio_chip	gc;
+	struct clk		*clk;
 };
 
+static int pl061_request(struct gpio_chip *gc, unsigned offset)
+{
+	struct pl061_gpio *chip = container_of(gc, struct pl061_gpio, gc);
+
+	if (offset >= gc->ngpio)
+		return -EINVAL;
+
+	return clk_enable(chip->clk);
+}
+
+static void pl061_free(struct gpio_chip *gc, unsigned offset)
+{
+	struct pl061_gpio *chip = container_of(gc, struct pl061_gpio, gc);
+
+	clk_disable(chip->clk);
+}
+
 static int pl061_direction_input(struct gpio_chip *gc, unsigned offset)
 {
 	struct pl061_gpio *chip = container_of(gc, struct pl061_gpio, gc);
@@ -260,9 +281,18 @@ static int __init pl061_probe(struct amba_device *dev, struct amba_id *id)
 		goto release_region;
 	}
 
-	spin_lock_init(&chip->lock);
-	spin_lock_init(&chip->irq_lock);
-	INIT_LIST_HEAD(&chip->list);
+	chip->clk = clk_get(&dev->dev, NULL);
+	if (IS_ERR(chip->clk)) {
+		ret = PTR_ERR(chip->clk);
+		/* clk Not present */
+		if (ret == -ENOENT)
+			chip->clk = NULL;
+		else
+			goto iounmap;
+	} else {
+		chip->gc.request = pl061_request;
+		chip->gc.free = pl061_free;
+	}
 
 	chip->gc.direction_input = pl061_direction_input;
 	chip->gc.direction_output = pl061_direction_output;
@@ -277,9 +307,13 @@ static int __init pl061_probe(struct amba_device *dev, struct amba_id *id)
 
 	chip->irq_base = pdata->irq_base;
 
+	spin_lock_init(&chip->lock);
+	spin_lock_init(&chip->irq_lock);
+	INIT_LIST_HEAD(&chip->list);
+
 	ret = gpiochip_add(&chip->gc);
 	if (ret)
-		goto iounmap;
+		goto free_clk;
 
 	/*
 	 * irq_chip support
@@ -292,7 +326,7 @@ static int __init pl061_probe(struct amba_device *dev, struct amba_id *id)
 	irq = dev->irq[0];
 	if (irq < 0) {
 		ret = -ENODEV;
-		goto iounmap;
+		goto free_clk;
 	}
 	set_irq_chained_handler(irq, pl061_irq_handler);
 	if (!test_and_set_bit(irq, init_irq)) { /* list initialized? */
@@ -300,7 +334,7 @@ static int __init pl061_probe(struct amba_device *dev, struct amba_id *id)
 		if (chip_list == NULL) {
 			clear_bit(irq, init_irq);
 			ret = -ENOMEM;
-			goto iounmap;
+			goto free_clk;
 		}
 		INIT_LIST_HEAD(chip_list);
 		set_irq_data(irq, chip_list);
@@ -323,6 +357,9 @@ static int __init pl061_probe(struct amba_device *dev, struct amba_id *id)
 
 	return 0;
 
+free_clk:
+	if (chip->clk)
+		clk_put(chip->clk);
 iounmap:
 	iounmap(chip->base);
 release_region:
Signature · 1 line
1.6.0.2
#1 lore raw

PATCH v4 1/1 GPIO PL061: Adding Clk framework support

Hi Viresh,

Quoted 6 lines · Viresh KUMAR

From: Viresh KUMAR <[email protected]>

GPIO Clk is never enabled on Platforms, like: SPEAr, which are based upon clock framework and use PL061 driver. This patch adds support for Clk enabling and disabling as and when gpios are requested and freed.

Quoted 7 lines

Modifications in V4:
- Not registering gpio_request and gpio_free if clk is not found.

Modifications in V3:
- if clk_get returns -ENOENT, then work without clk enable/disable.

baruch

Signature · 3 lines
                                                     ~. .~   Tk Open Systems
=}------------------------------------------------ooO--U--Ooo------------{=
   - baruch at tkos.co.il - tel: +972.2.679.5364, http://www.tkos.co.il -
#2 lore raw

PATCH v4 1/1 GPIO PL061: Adding Clk framework support

In reply to Viresh KUMAR

Review

Quoted 9 lines · Viresh KUMAR
(quoted diff) +6 -3
-	spin_lock_init(&chip->lock);
-	spin_lock_init(&chip->irq_lock);
-	INIT_LIST_HEAD(&chip->list);
+	chip->clk = clk_get(&dev->dev, NULL);
+	if (IS_ERR(chip->clk)) {
+		ret = PTR_ERR(chip->clk);
+		/* clk Not present */
+		if (ret == -ENOENT)
+			chip->clk = NULL;

Please don't assume that NULL is not a valid value for a 'struct clk'. Eg,

struct clk *clk_get(struct device *dev, const char *id)

{
        return dev && strcmp(dev_name(dev), "mb:16") == 0 ? NULL : ERR_PTR(-ENOENT);
}

However, this patch begs the question about what you're trying to do, as the primecell doesn't take a clock for its operation other than the AMBA bus clock.

As I've already pointed out, the current primecell implementations do not assume that the bus clock is switched - and so they aren't written such that the bus clock will be fiddled with when they do clk_enable and clk_disable on their functional clocks.

As there do seem to be platforms which can switch the bus clock to individual primecells, we need to add this support - and using the existing clk API calls won't do, especially as we have primecell code which accesses registers prior to doing anything with the clk API.

How about we have the core primecell code request the bus clock on driver probe, enable it, and then call the drivers probe method. On driver remove, the bus clock is disabled and put after the drivers remove method has been called. We provide two new function calls, amba_bus_clk_enable() and amba_bus_clk_disable() which primecell drivers can use to control the bus clock to the peripheral - these can be just macros to clk_enable and clk_disable respectively.

So something like the patch below (untested):

 drivers/amba/bus.c       |   35 +++++++++++++++++++++++++++++++----
 include/linux/amba/bus.h |   10 ++++++++++
 2 files changed, 41 insertions(+), 4 deletions(-)
drivers/amba/bus.c +31 -4
index f60b2b6..1b9f5f8 100644
@@ -130,17 +130,44 @@ static int amba_probe(struct device *dev)
 {
 	struct amba_device *pcdev = to_amba_device(dev);
 	struct amba_driver *pcdrv = to_amba_driver(dev->driver);
-	struct amba_id *id;
+	struct amba_id *id = amba_lookup(pcdrv->id_table, pcdev);
+	struct clk *clk;
+	int ret;
+
+	do {
+		pcdev->busck = clk = clk_get(dev, "busck");
+		if (IS_ERR(clk) && PTR_ERR(clk) != -ENOENT) {
+			ret = PTR_ERR(clk);
+			break;
+		}
+
+		if (!IS_ERR(clk)) {
+			ret = clk_enable(clk);
+			if (ret)
+				break;
+		}
 
-	id = amba_lookup(pcdrv->id_table, pcdev);
+		ret = pcdrv->probe(pcdev, id);
+		if (ret == 0)
+			break;
 
-	return pcdrv->probe(pcdev, id);
+		clk_disable(clk);
+	} while (0);
+
+	return ret;
 }
 
 static int amba_remove(struct device *dev)
 {
+	struct amba_device *pcdev = to_amba_device(dev);
 	struct amba_driver *drv = to_amba_driver(dev->driver);
-	return drv->remove(to_amba_device(dev));
+	int ret = drv->remove(pcdev);
+
+	if (!IS_ERR(pcdev->busck)) {
+		clk_disable(pcdev->busck);
+		clk_put(pcdev->busck);
+	}
+	return ret;
 }
 
 static void amba_shutdown(struct device *dev)
include/linux/amba/bus.h +10 -0
index 8b10386..f880c58 100644
@@ -15,13 +15,17 @@
 #define ASMARM_AMBA_H
 
 #include <linux/device.h>
+#include <linux/err.h>
 #include <linux/resource.h>
 
 #define AMBA_NR_IRQS	2
 
+struct clk;
+
 struct amba_device {
 	struct device		dev;
 	struct resource		res;
+	struct clk		*busck;
 	u64			dma_mask;
 	unsigned int		periphid;
 	unsigned int		irq[AMBA_NR_IRQS];
@@ -59,6 +63,12 @@ struct amba_device *amba_find_device(const char *, struct device *, unsigned int
 int amba_request_regions(struct amba_device *, const char *);
 void amba_release_regions(struct amba_device *);
 
+#define amba_bus_clk_enable(d)	\
+	(IS_ERR((d)->busck) ? 0 : clk_enable((d)->busck))
+
+#define amba_bus_clk_disable(d)	\
+	do { if (!IS_ERR((d)->busck)) clk_disable((d)->busck); } while (0)
+
 #define amba_config(d)	(((d)->periphid >> 24) & 0xff)
 #define amba_rev(d)	(((d)->periphid >> 20) & 0x0f)
 #define amba_manf(d)	(((d)->periphid >> 12) & 0xff)
#3 lore raw

PATCH v4 1/1 GPIO PL061: Adding Clk framework support

2010/7/9 Russell King - ARM Linux <[email protected]>:

Quoted 7 lines

How about we have the core primecell code request the bus clock on driver probe, enable it, and then call the drivers probe method. ?On driver remove, the bus clock is disabled and put after the drivers remove method has been called. ?We provide two new function calls, amba_bus_clk_enable() and amba_bus_clk_disable() which primecell drivers can use to control the bus clock to the peripheral - these can be just macros to clk_enable and clk_disable respectively.

This looks mostly good to me (comments below) but will have as a side effect that all clock frameworks on all platforms using PrimeCells will need to be initialized *before* the PrimeCell devices are added.

In U8500 we solved this by moving the clock initialization to the IRQ initialization from the .init_irq field in the machine struct, this change would need that (or something similar) to be done uniformly.

(I can fix it for U300 and other platforms I can test.)

Quoted 26 lines

So something like the patch below (untested):

?drivers/amba/bus.c ? ? ? | ? 35 +++++++++++++++++++++++++++++++---- ?include/linux/amba/bus.h | ? 10 ++++++++++
?2 files changed, 41 insertions(+), 4 deletions(-)

drivers/amba/bus.c +11 -1
index f60b2b6..1b9f5f8 100644
@@ -130,17 +130,44 @@ static int amba_probe(struct device *dev)
 ?{
 ? ? ? ?struct amba_device *pcdev = to_amba_device(dev);
 ? ? ? ?struct amba_driver *pcdrv = to_amba_driver(dev->driver);
- ? ? ? struct amba_id *id;
+ ? ? ? struct amba_id *id = amba_lookup(pcdrv->id_table, pcdev);
+ ? ? ? struct clk *clk;
+ ? ? ? int ret;
+
+ ? ? ? do {
+ ? ? ? ? ? ? ? pcdev->busck = clk = clk_get(dev, "busck");
+ ? ? ? ? ? ? ? if (IS_ERR(clk) && PTR_ERR(clk) != -ENOENT) {
+ ? ? ? ? ? ? ? ? ? ? ? ret = PTR_ERR(clk);
+ ? ? ? ? ? ? ? ? ? ? ? break;
+ ? ? ? ? ? ? ? }
+

drivers/amba/bus.c static int amba_probe(struct device *dev) · line 152

Quoted 10 lines
drivers/amba/bus.c +8 -1
+ ? ? ? ? ? ? ? if (!IS_ERR(clk)) {
+ ? ? ? ? ? ? ? ? ? ? ? ret = clk_enable(clk);
+ ? ? ? ? ? ? ? ? ? ? ? if (ret)
+ ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? break;
+ ? ? ? ? ? ? ? }
 
- ? ? ? id = amba_lookup(pcdrv->id_table, pcdev);
+ ? ? ? ? ? ? ? ret = pcdrv->probe(pcdev, id);
+ ? ? ? ? ? ? ? if (ret == 0)
+ ? ? ? ? ? ? ? ? ? ? ? break;

So this means you always turn on the block clock at probe, and then leave it on if the probe was successful.

Quoted 7 lines
drivers/amba/bus.c +4 -1
- ? ? ? return pcdrv->probe(pcdev, id);
+ ? ? ? ? ? ? ? clk_disable(clk);
+ ? ? ? } while (0);
+
+ ? ? ? return ret;
 ?}

drivers/amba/bus.c static int amba_probe(struct device *dev) · line 166

Quoted 10 lines
drivers/amba/bus.c +5 -1
 
 ?static int amba_remove(struct device *dev)
 ?{
+ ? ? ? struct amba_device *pcdev = to_amba_device(dev);
 ? ? ? ?struct amba_driver *drv = to_amba_driver(dev->driver);
- ? ? ? return drv->remove(to_amba_device(dev));
+ ? ? ? int ret = drv->remove(pcdev);
+
+ ? ? ? if (!IS_ERR(pcdev->busck)) {
+ ? ? ? ? ? ? ? clk_disable(pcdev->busck);

And here it is disabled.

For drivers that need to conserve power, also the block clock has to be gated when the device is not in use, sometimes since that affects some hardware that keep track of the usecount of the clock tree for a larger region of silicon, so the power savings can be drastic.

It would be good if drivers could opt-out of leaving the clock on after probe, some more property perhaps, like "gateme" or so, then the driver will be responsible to clk_enable() the clock when it's needed.

This would make it possible to convert the PrimeCell drivers over to this behaviour later.

Another option would be to always gate them all off, and then sprinkle clk_enable(adev->bus_clk) into every PrimeCell driver probe() and clk_disable(adev->bus_clk) into every PrimeCell driver remove(). This makes things more explicit.

Quoted 21 lines
drivers/amba/bus.c +3 -0
+ ? ? ? ? ? ? ? clk_put(pcdev->busck);
+ ? ? ? }
+ ? ? ? return ret;
 ?}
 
 ?static void amba_shutdown(struct device *dev)
include/linux/amba/bus.h +0 -0
index 8b10386..f880c58 100644
@@ -15,13 +15,17 @@

?#define ASMARM_AMBA_H

?#include <linux/device.h>
+#include <linux/err.h>
?#include <linux/resource.h>

?#define AMBA_NR_IRQS ? 2

include/linux/amba/bus.h +2 -0
+struct clk;
+
Quoted 4 lines

?struct amba_device {
? ? ? ?struct device ? ? ? ? ? dev;
? ? ? ?struct resource ? ? ? ? res;
+ ? ? ? struct clk ? ? ? ? ? ? ?*busck;

Here you could add:
bool gateme

Quoted 7 lines

? ? ? ?u64 ? ? ? ? ? ? ? ? ? ? dma_mask;
? ? ? ?unsigned int ? ? ? ? ? ?periphid;
? ? ? ?unsigned int ? ? ? ? ? ?irq[AMBA_NR_IRQS];

include/linux/amba/bus.h +0 -0
@@ -59,6 +63,12 @@ struct amba_device *amba_find_device(const char *, struct device *, unsigned int
 ?int amba_request_regions(struct amba_device *, const char *);
 ?void amba_release_regions(struct amba_device *);

include/linux/amba/bus.h struct amba_device *amba_find_device(const char *, struct device *, unsigned int · line 74

Quoted 10 lines
include/linux/amba/bus.h +6 -0
 
+#define amba_bus_clk_enable(d) \
+ ? ? ? (IS_ERR((d)->busck) ? 0 : clk_enable((d)->busck))
+
+#define amba_bus_clk_disable(d) ? ? ? ?\
+ ? ? ? do { if (!IS_ERR((d)->busck)) clk_disable((d)->busck); } while (0)
+
 ?#define amba_config(d) (((d)->periphid >> 24) & 0xff)
 ?#define amba_rev(d) ? ?(((d)->periphid >> 20) & 0x0f)
 ?#define amba_manf(d) ? (((d)->periphid >> 12) & 0xff)

Yours,
Linus Walleij

#4 lore raw

PATCH v4 1/1 GPIO PL061: Adding Clk framework support

Quoted 3 lines · Linus Walleij

This looks mostly good to me (comments below) but will have as a side effect that all clock frameworks on all platforms using PrimeCells will need to be initialized *before* the PrimeCell devices are added.

No it doesn't - it needs the clock API to be initialized before the first driver is probed. As drivers are registered during the initcalls, later than postcore level

Quoted 7 lines
Quoted 4 lines

- ? ? ? id = amba_lookup(pcdrv->id_table, pcdev);
+ ? ? ? ? ? ? ? ret = pcdrv->probe(pcdev, id);
+ ? ? ? ? ? ? ? if (ret == 0)
+ ? ? ? ? ? ? ? ? ? ? ? break;

So this means you always turn on the block clock at probe, and then leave it on if the probe was successful.

Correct, which is what drivers currently expect. This is also why two additional amba_* callbacks are supplied - if it is appropriate for a driver to disable their bus clock after being probed, they can do that but they have to participate in that.

Basically, they have to call amba_bus_clk_disable() on successful probe, and then amba_bus_clk_enable() each time that they want to access their registers, followed by an amba_bus_clk_disable() when they've finished. Finally, they must call amba_bus_clk_enable() in their remove method and avoid amba_bus_clk_disable() before returning.

Quoted 5 lines

For drivers that need to conserve power, also the block clock has to be gated when the device is not in use, sometimes since that affects some hardware that keep track of the usecount of the clock tree for a larger region of silicon, so the power savings can be drastic.

You can't have the core code doing that. If you unconditionally turn the bus clock off after probe, what happens when a driver receives an interrupt and tries to access its registers?

Hint: the core code can't know that the driver has registered an IRQ handler.

#5 lore raw

PATCH v4 1/1 GPIO PL061: Adding Clk framework support

Quoted 8 lines · Russell King - ARM Linux
Quoted 3 lines · Linus Walleij

This looks mostly good to me (comments below) but will have as a side effect that all clock frameworks on all platforms using PrimeCells will need to be initialized *before* the PrimeCell devices are added.

No it doesn't - it needs the clock API to be initialized before the first driver is probed. As drivers are registered during the initcalls, later than postcore level

... (to finish this) ...

drivers/amba/bus.c:postcore_initcall(amba_init);

This is the earliest that any driver can be registered, as the AMBA bus type will not be registered until that point. So, provided the clk API is initialized at or before postcore level, AMBA devices are fine.

What we do need to do is add a 'busck' to amba using platforms as the NULL connection name for the function clock in the clkdev tables will match.

#6 lore raw

PATCH v4 1/1 GPIO PL061: Adding Clk framework support

In reply to Russell King - ARM Linux

2010/7/10 Russell King - ARM Linux <[email protected]>:

Quoted 4 lines

Correct, which is what drivers currently expect. ?This is also why two additional amba_* callbacks are supplied - if it is appropriate for a driver to disable their bus clock after being probed, they can do that but they have to participate in that.

Aha I get it, then it looks good...

Yours,
Linus Walleij

#7 lore raw

PATCH v4 1/1 GPIO PL061: Adding Clk framework support

Quoted 8 lines · Linus Walleij

2010/7/10 Russell King - ARM Linux <[email protected]>:

Quoted 4 lines

Correct, which is what drivers currently expect. ?This is also why two additional amba_* callbacks are supplied - if it is appropriate for a driver to disable their bus clock after being probed, they can do that but they have to participate in that.

Aha I get it, then it looks good...

Is that an Ack for the patch?

I think there's one change I'd like, which is to change "busck" to "apb_pclk" to make it clear what it's referring to.

Also, is there any chance you can convert your platform(s) over to use it before I merge this patch?

#8 lore raw

PATCH v4 1/1 GPIO PL061: Adding Clk framework support

2010/7/13 Russell King - ARM Linux <[email protected]>:

Quoted 5 lines · Linus Walleij
Quoted 2 lines

Aha I get it, then it looks good...

Is that an Ack for the patch?

Yep! FWIW

Quoted 2 lines

I think there's one change I'd like, which is to change "busck" to "apb_pclk" to make it clear what it's referring to.

Looks good.

Quoted 2 lines

Also, is there any chance you can convert your platform(s) over to use it before I merge this patch?

I can take a round over the U300 within a few days, Rabin can maybe look at the U8500.

In the U300 (maybe the U8500 as well, I don't know OTOH) the APB pclk and e.g. SSPCLK is simply just wired together and connected to the same clock terminal. This means that currently the PCLK is toggled on/off in unison with the SSPCLK all the time already, and it's working.

Since that works, it means that we can likely insert amba_bus_clk_[disable|enable] at the same sites that we have this in the current code for the external clocks at the same time, atleast for pl011, pl022 and pl180.

Yours,
Linus Walleij

#9 lore raw

PATCH v4 1/1 GPIO PL061: Adding Clk framework support

Quoted 5 lines · Linus Walleij

In the U300 (maybe the U8500 as well, I don't know OTOH) the APB pclk and e.g. SSPCLK is simply just wired together and connected to the same clock terminal. This means that currently the PCLK is toggled on/off in unison with the SSPCLK all the time already, and it's working.

That means you want to alias the device specific apb_pclk to the same struct clk as (eg) the device's sspclk - which means that when the driver wants either PCLK or SSPCLK to be on, the bit to enable them will be turned on.

Quoted 4 lines

Since that works, it means that we can likely insert amba_bus_clk_[disable|enable] at the same sites that we have this in the current code for the external clocks at the same time, atleast for pl011, pl022 and pl180.

Almost - but there's a few corner cases. Basically, what I think you'll need for pl011 is:
1. add an amba_bus_clk_disable() at the end of the successful probe function
2. add amba_bus_clk_enable() at the beginning of the remove function.
3. add amba_bus_clk_enable()/amba_bus_clk_disable() around each of the suspend(), resume(), pl011_console_write(), and pl011_console_get_options() functions.
4. amba_bus_clk_enable() at the start of the startup method.
5. amba_bus_clk_disable() at the end of the shutdown method.

Most of these do tie up with existing clk_enable()s, but there are some additional places you'd need to enable the pclk because of register accesses.

#10 lore raw

PATCH v4 1/1 GPIO PL061: Adding Clk framework support

Quoted 17 lines · Russell King - ARM Linux
Quoted 4 lines · Linus Walleij

Since that works, it means that we can likely insert amba_bus_clk_[disable|enable] at the same sites that we have this in the current code for the external clocks at the same time, atleast for pl011, pl022 and pl180.

Almost - but there's a few corner cases. Basically, what I think you'll need for pl011 is:
1. add an amba_bus_clk_disable() at the end of the successful probe function
2. add amba_bus_clk_enable() at the beginning of the remove function.
3. add amba_bus_clk_enable()/amba_bus_clk_disable() around each of the suspend(), resume(), pl011_console_write(), and pl011_console_get_options() functions.
4. amba_bus_clk_enable() at the start of the startup method.
5. amba_bus_clk_disable() at the end of the shutdown method.

Quoted 4 lines

Most of these do tie up with existing clk_enable()s, but there are some additional places you'd need to enable the pclk because of register accesses.

Russell,

These cases particularly in our architecture where functional and interface clock are tied together (through same gating option) would produce difficult scenarios (as already mentioned by you) and which would be difficult and not so clean to handle.

For example just looking at driver src which disables bus clock (without enabling it) in different scenarios would not be very readable. Further that would vary from architecture to architecture (even for standard drivers).

Quoted 11 lines
Quoted 5 lines
Quoted 5 lines

For drivers that need to conserve power, also the block clock has to be gated when the device is not in use, sometimes since that affects some hardware that keep track of the usecount of the clock tree for a larger region of silicon, so the power savings can be drastic.

You can't have the core code doing that. If you unconditionally turn the bus clock off after probe, what happens when a driver receives an interrupt and tries to access its registers?

Hint: the core code can't know that the driver has registered an IRQ handler.

We haven't seen this kind of issues in our devices, SPEAr as well as U300 (as we have both clocks controlled by same bit). Normally, when device is not in use then interrupts are disabled. When device is used then interrupts and clock are enabled and clocks are not disabled till the time work is finished. So, this condition might not occur that you have landed in interrupt handler with clocks off.

IMO, Clocks enabling is required at following places:
1. During device add where we need to read id registers.
- We can do this by simple enabling/disabling "apb_bus" clk before and after reading registers.
2. In driver, when device is used.
- This can be handled best by the driver itself. We don't require to enable interface clock from amba_probe for ever.

One way to do this cleanly is:
- Add clock structures for devices with connection id "apb_bus". (Platforms can have 2 different structures if they have different
controlling bits otherwise single structure will solve the purpose.)
- amba_device_register can simple get this clk and use it.
- Drivers need to enable/disable both interface and functional clock to save maximum power.

regards,
viresh kumar.

#11 lore raw

PATCH v4 1/1 GPIO PL061: Adding Clk framework support

Quoted 4 lines · Viresh KUMAR

These cases particularly in our architecture where functional and interface clock are tied together (through same gating option) would produce difficult scenarios (as already mentioned by you) and which would be difficult and not so clean to handle.

I don't see the problem.

What I'm proposing means:

1. when the driver probe function is called, pclk is guaranteed to be enabled, and therefore device registers are guaranteed to be accessible. This clock will then stay enabled all the time the device is bound to the driver unless the driver wants to request that it is disabled.

2. if the driver wants to do something with its functional clock, it clk_get() that, and does the standard enable/disable on it at the appropriate time.

So, if pclk and the functional clock are bound together, then at probe time the 'pclk' is obtained and enabled, which sets your enable bit and increases the clk use count to 1.

When the driver gets its functional clock, which happens to be the same as clk structure as 'pclk', calling clk_enable() increases the use count to 2, but doesn't touch the register because the clock is already enabled.

If the driver subsequently calls clk_disable(), this decreases the use count to 1, and because there's still one user, the clock isn't disabled.

Only if the driver wants both its pclk and functional clock disabled will your enable bit be reset.

So, provided a driver participating in pclk control (by disabling it in its probe function) always re-enables pclk before it accesses the device then the pclk control is completely transparent - whether or not it's tied to the functional clock.

If a driver isn't participating in pclk control, it continues to work as is with no alterations - because we guarantee that pclk will be enabled to the device whenever the driver is bound to the device.

This allows us to incrementally add pclk control to each primecell driver.

Quoted 4 lines

For example just looking at driver src which disables bus clock (without enabling it) in different scenarios would not be very readable. Further that would vary from architecture to architecture (even for standard drivers).

How so?

Quoted 7 lines
Quoted 6 lines

You can't have the core code doing that. If you unconditionally turn the bus clock off after probe, what happens when a driver receives an interrupt and tries to access its registers?

Hint: the core code can't know that the driver has registered an IRQ handler.

Quoted 6 lines

We haven't seen this kind of issues in our devices, SPEAr as well as U300 (as we have both clocks controlled by same bit). Normally, when device is not in use then interrupts are disabled. When device is used then interrupts and clock are enabled and clocks are not disabled till the time work is finished. So, this condition might not occur that you have landed in interrupt handler with clocks off.

So what happens with the PL011 driver which accesses the device with the primecell UARTCLK disabled? Eg, when reading the procfs file in /proc/tty/driver/ ?

What about the SPI primecell, which only enables its functional clock when its really required? It accesses device registers without the functional clock enabled?

Basically, we do not guarantee that drivers will have their functional clock enabled prior to accessing their registers.

#12 lore raw

PATCH v4 1/1 GPIO PL061: Adding Clk framework support

Quoted 69 lines · Russell King - ARM Linux
Quoted 4 lines · Viresh KUMAR

These cases particularly in our architecture where functional and interface clock are tied together (through same gating option) would produce difficult scenarios (as already mentioned by you) and which would be difficult and not so clean to handle.

I don't see the problem.

What I'm proposing means:

1. when the driver probe function is called, pclk is guaranteed to be enabled, and therefore device registers are guaranteed to be accessible. This clock will then stay enabled all the time the device is bound to the driver unless the driver wants to request that it is disabled.

2. if the driver wants to do something with its functional clock, it clk_get() that, and does the standard enable/disable on it at the appropriate time.

So, if pclk and the functional clock are bound together, then at probe time the 'pclk' is obtained and enabled, which sets your enable bit and increases the clk use count to 1.

When the driver gets its functional clock, which happens to be the same as clk structure as 'pclk', calling clk_enable() increases the use count to 2, but doesn't touch the register because the clock is already enabled.

If the driver subsequently calls clk_disable(), this decreases the use count to 1, and because there's still one user, the clock isn't disabled.

Only if the driver wants both its pclk and functional clock disabled will your enable bit be reset.

So, provided a driver participating in pclk control (by disabling it in its probe function) always re-enables pclk before it accesses the device then the pclk control is completely transparent - whether or not it's tied to the functional clock.

If a driver isn't participating in pclk control, it continues to work as is with no alterations - because we guarantee that pclk will be enabled to the device whenever the driver is bound to the device.

This allows us to incrementally add pclk control to each primecell driver.

Quoted 4 lines

For example just looking at driver src which disables bus clock (without enabling it) in different scenarios would not be very readable. Further that would vary from architecture to architecture (even for standard drivers).

How so?

Quoted 7 lines
Quoted 6 lines

You can't have the core code doing that. If you unconditionally turn the bus clock off after probe, what happens when a driver receives an interrupt and tries to access its registers?

Hint: the core code can't know that the driver has registered an IRQ handler.

Quoted 6 lines

We haven't seen this kind of issues in our devices, SPEAr as well as U300 (as we have both clocks controlled by same bit). Normally, when device is not in use then interrupts are disabled. When device is used then interrupts and clock are enabled and clocks are not disabled till the time work is finished. So, this condition might not occur that you have landed in interrupt handler with clocks off.

So what happens with the PL011 driver which accesses the device with the primecell UARTCLK disabled? Eg, when reading the procfs file in /proc/tty/driver/ ?

Quoted 7 lines

What about the SPI primecell, which only enables its functional clock when its really required? It accesses device registers without the functional clock enabled?

Basically, we do not guarantee that drivers will have their functional clock enabled prior to accessing their registers.

I got it!!!

Just a little issue, in your patch you were enabling interface clock in amba_probe which is called after reading peripheral id registers in amba_device_register. We need interface clock enabled before reading these registers.

viresh.

#13 lore raw

PATCH v4 1/1 GPIO PL061: Adding Clk framework support

2010/7/15 Viresh KUMAR <[email protected]>:

Quoted 4 lines

Just a little issue, in your patch you were enabling interface clock in amba_probe which is called after reading peripheral id registers in amba_device_register. We need interface clock enabled before reading these registers.

Rabin already noted this and Russell says he'll fix it before applying the patch.

Yours,
Linus Walleij

#14 lore raw

PATCH v4 1/1 GPIO PL061: Adding Clk framework support

In reply to Viresh KUMAR
Quoted 4 lines · Viresh KUMAR

Just a little issue, in your patch you were enabling interface clock in amba_probe which is called after reading peripheral id registers in amba_device_register. We need interface clock enabled before reading these registers.

Yes, I've already updated my patch. I've also renamed a the bus clock functions so that we consistently call this clock 'pclk' after the signal which it refers to.

I had noticed that Linus' patches were referring to 'ahb_pclk' - the slave interface for primcells lives on APB not AHB.

 drivers/amba/bus.c       |   88 ++++++++++++++++++++++++++++++++++++----------
 include/linux/amba/bus.h |   11 ++++++
 2 files changed, 80 insertions(+), 19 deletions(-)
drivers/amba/bus.c +69 -19
index f60b2b6..d31590e 100644
@@ -122,6 +122,31 @@ static int __init amba_init(void)
 
 postcore_initcall(amba_init);
 
+static int amba_get_enable_pclk(struct amba_device *pcdev)
+{
+	struct clk *pclk = clk_get(&pcdev->dev, "apb_pclk");
+	int ret;
+
+	pcdev->pclk = pclk;
+
+	if (IS_ERR(pclk))
+		return PTR_ERR(pclk);
+
+	ret = clk_enable(pclk);
+	if (ret)
+		clk_put(pclk);
+
+	return ret;
+}
+
+static void amba_put_disable_pclk(struct amba_device *pcdev)
+{
+	struct clk *pclk = pcdev->pclk;
+
+	clk_disable(pclk);
+	clk_put(pclk);
+}
+
 /*
  * These are the device model conversion veneers; they convert the
  * device model structures to our more specific structures.
@@ -130,17 +155,33 @@ static int amba_probe(struct device *dev)
 {
 	struct amba_device *pcdev = to_amba_device(dev);
 	struct amba_driver *pcdrv = to_amba_driver(dev->driver);
-	struct amba_id *id;
+	struct amba_id *id = amba_lookup(pcdrv->id_table, pcdev);
+	int ret;
 
-	id = amba_lookup(pcdrv->id_table, pcdev);
+	do {
+		ret = amba_get_enable_pclk(pcdev);
+		if (ret)
+			break;
+
+		ret = pcdrv->probe(pcdev, id);
+		if (ret == 0)
+			break;
 
-	return pcdrv->probe(pcdev, id);
+		amba_put_disable_pclk(pcdev);
+	} while (0);
+
+	return ret;
 }
 
 static int amba_remove(struct device *dev)
 {
+	struct amba_device *pcdev = to_amba_device(dev);
 	struct amba_driver *drv = to_amba_driver(dev->driver);
-	return drv->remove(to_amba_device(dev));
+	int ret = drv->remove(pcdev);
+
+	amba_put_disable_pclk(pcdev);
+
+	return ret;
 }
 
 static void amba_shutdown(struct device *dev)
@@ -203,7 +244,6 @@ static void amba_device_release(struct device *dev)
  */
 int amba_device_register(struct amba_device *dev, struct resource *parent)
 {
-	u32 pid, cid;
 	u32 size;
 	void __iomem *tmp;
 	int i, ret;
@@ -241,25 +281,35 @@ int amba_device_register(struct amba_device *dev, struct resource *parent)
 		goto err_release;
 	}
 
-	/*
-	 * Read pid and cid based on size of resource
-	 * they are located at end of region
-	 */
-	for (pid = 0, i = 0; i < 4; i++)
-		pid |= (readl(tmp + size - 0x20 + 4 * i) & 255) << (i * 8);
-	for (cid = 0, i = 0; i < 4; i++)
-		cid |= (readl(tmp + size - 0x10 + 4 * i) & 255) << (i * 8);
+	ret = amba_get_enable_pclk(dev);
+	if (ret == 0) {
+		u32 pid, cid;
 
-	iounmap(tmp);
+		/*
+		 * Read pid and cid based on size of resource
+		 * they are located@end of region
+		 */
+		for (pid = 0, i = 0; i < 4; i++)
+			pid |= (readl(tmp + size - 0x20 + 4 * i) & 255) <<
+				(i * 8);
+		for (cid = 0, i = 0; i < 4; i++)
+			cid |= (readl(tmp + size - 0x10 + 4 * i) & 255) <<
+				(i * 8);
 
-	if (cid == 0xb105f00d)
-		dev->periphid = pid;
+		amba_put_disable_pclk(dev);
 
-	if (!dev->periphid) {
-		ret = -ENODEV;
-		goto err_release;
+		if (cid == 0xb105f00d)
+			dev->periphid = pid;
+
+		if (!dev->periphid)
+			ret = -ENODEV;
 	}
 
+	iounmap(tmp);
+
+	if (ret)
+		goto err_release;
+
 	ret = device_add(&dev->dev);
 	if (ret)
 		goto err_release;
include/linux/amba/bus.h +11 -0
index 8b10386..b0c1740 100644
@@ -14,14 +14,19 @@
 #ifndef ASMARM_AMBA_H
 #define ASMARM_AMBA_H
 
+#include <linux/clk.h>
 #include <linux/device.h>
+#include <linux/err.h>
 #include <linux/resource.h>
 
 #define AMBA_NR_IRQS	2
 
+struct clk;
+
 struct amba_device {
 	struct device		dev;
 	struct resource		res;
+	struct clk		*pclk;
 	u64			dma_mask;
 	unsigned int		periphid;
 	unsigned int		irq[AMBA_NR_IRQS];
@@ -59,6 +64,12 @@ struct amba_device *amba_find_device(const char *, struct device *, unsigned int
 int amba_request_regions(struct amba_device *, const char *);
 void amba_release_regions(struct amba_device *);
 
+#define amba_pclk_enable(d)	\
+	(IS_ERR((d)->pclk) ? 0 : clk_enable((d)->pclk))
+
+#define amba_pclk_disable(d)	\
+	do { if (!IS_ERR((d)->pclk)) clk_disable((d)->pclk); } while (0)
+
 #define amba_config(d)	(((d)->periphid >> 24) & 0xff)
 #define amba_rev(d)	(((d)->periphid >> 20) & 0x0f)
 #define amba_manf(d)	(((d)->periphid >> 12) & 0xff)
#15 lore raw

PATCH v4 1/1 GPIO PL061: Adding Clk framework support

Quoted 17 lines · Russell King - ARM Linux
drivers/amba/bus.c +17 -0
+static int amba_get_enable_pclk(struct amba_device *pcdev)
+{
+	struct clk *pclk = clk_get(&pcdev->dev, "apb_pclk");
+	int ret;
+
+	pcdev->pclk = pclk;
+
+	if (IS_ERR(pclk))
+		return PTR_ERR(pclk);
+
+	ret = clk_enable(pclk);
+	if (ret)
+		clk_put(pclk);
+
+	return ret;
+}
+

...

Quoted 14 lines
drivers/amba/bus.c +7 -2
@@ -130,17 +155,33 @@ static int amba_probe(struct device *dev)
 {
 	struct amba_device *pcdev = to_amba_device(dev);
 	struct amba_driver *pcdrv = to_amba_driver(dev->driver);
-	struct amba_id *id;
+	struct amba_id *id = amba_lookup(pcdrv->id_table, pcdev);
+	int ret;
 
-	id = amba_lookup(pcdrv->id_table, pcdev);
+	do {
+		ret = amba_get_enable_pclk(pcdev);
+		if (ret)
+			break;
+

drivers/amba/bus.c static int amba_probe(struct device *dev) · line 174

Quoted 10 lines
drivers/amba/bus.c +7 -1
+		ret = pcdrv->probe(pcdev, id);
+		if (ret == 0)
+			break;
 
-	return pcdrv->probe(pcdev, id);
+		amba_put_disable_pclk(pcdev);
+	} while (0);
+
+	return ret;
 }

In your earlier patch, you proceeded with the probe if the error from clk_get() was -ENOENT, but not in this version. Isn't the -ENOENT special handling preferable, since it avoids the need to change all existing platforms?

#16 lore raw

PATCH v4 1/1 GPIO PL061: Adding Clk framework support

Quoted 4 lines · Rabin Vincent

In your earlier patch, you proceeded with the probe if the error from clk_get() was -ENOENT, but not in this version. Isn't the -ENOENT special handling preferable, since it avoids the need to change all existing platforms?

Well, the problem is that most platforms now declare their clocks such that asking for the pclk will match the functional clock.

I have a patch which adds dummy pclks to all ARM AMBA using platforms. This patch comes before the pclk-using patch to so that things shouldn't break.

#17 lore raw

PATCH v4 1/1 GPIO PL061: Adding Clk framework support

In reply to Russell King - ARM Linux
Quoted 13 lines

/*
* These are the device model conversion veneers; they convert the
* device model structures to our more specific structures.

drivers/amba/bus.c +3 -2
@@ -130,17 +155,33 @@ static int amba_probe(struct device *dev)
 {
 	struct amba_device *pcdev = to_amba_device(dev);
 	struct amba_driver *pcdrv = to_amba_driver(dev->driver);
-	struct amba_id *id;
+	struct amba_id *id = amba_lookup(pcdrv->id_table, pcdev);
+	int ret;
 
-	id = amba_lookup(pcdrv->id_table, pcdev);
+	do {

drivers/amba/bus.c static int amba_probe(struct device *dev) · line 170

Quoted 10 lines
drivers/amba/bus.c +8 -1
+		ret = amba_get_enable_pclk(pcdev);
+		if (ret)
+			break;
+
+		ret = pcdrv->probe(pcdev, id);
+		if (ret == 0)
+			break;
 
-	return pcdrv->probe(pcdev, id);
+		amba_put_disable_pclk(pcdev);

Should the AMBA clock be disabled if the probe() fails?

Quoted 5 lines
drivers/amba/bus.c +3 -0
+	} while (0);
+
+	return ret;
 }
 
#18 lore raw

PATCH v4 1/1 GPIO PL061: Adding Clk framework support

Quoted 21 lines · Kevin Wells
Quoted 21 lines

/*
* These are the device model conversion veneers; they convert the
* device model structures to our more specific structures.

drivers/amba/bus.c +10 -2
@@ -130,17 +155,33 @@ static int amba_probe(struct device *dev)
 {
 	struct amba_device *pcdev = to_amba_device(dev);
 	struct amba_driver *pcdrv = to_amba_driver(dev->driver);
-	struct amba_id *id;
+	struct amba_id *id = amba_lookup(pcdrv->id_table, pcdev);
+	int ret;
 
-	id = amba_lookup(pcdrv->id_table, pcdev);
+	do {
+		ret = amba_get_enable_pclk(pcdev);
+		if (ret)
+			break;
+
+		ret = pcdrv->probe(pcdev, id);
+		if (ret == 0)
+			break;
 
Quoted 4 lines

Review static int amba_probe(struct device *dev) · line 170

Quoted 2 lines
(quoted diff) +1 -1
-	return pcdrv->probe(pcdev, id);
+		amba_put_disable_pclk(pcdev);

Should the AMBA clock be disabled if the probe() fails?

This is exactly what happens here.

#19 lore raw

PATCH v4 1/1 GPIO PL061: Adding Clk framework support

Quoted 26 lines

Cc: Viresh KUMAR; rabin at rab.in; Linus Walleij; baruch at tkos.co.il; linux-arm-kernel at lists.infradead.org
Subject: Re: [PATCH v4] GPIO PL061: Adding Clk framework support

Quoted 21 lines · Kevin Wells
Quoted 21 lines

/*
* These are the device model conversion veneers; they convert the
* device model structures to our more specific structures.

drivers/amba/bus.c +10 -2
@@ -130,17 +155,33 @@ static int amba_probe(struct device *dev)
 {
 	struct amba_device *pcdev = to_amba_device(dev);
 	struct amba_driver *pcdrv = to_amba_driver(dev->driver);
-	struct amba_id *id;
+	struct amba_id *id = amba_lookup(pcdrv->id_table, pcdev);
+	int ret;
 
-	id = amba_lookup(pcdrv->id_table, pcdev);
+	do {
+		ret = amba_get_enable_pclk(pcdev);
+		if (ret)
+			break;
+
+		ret = pcdrv->probe(pcdev, id);
+		if (ret == 0)
+			break;
 
Quoted 6 lines
Quoted 4 lines

Review static int amba_probe(struct device *dev) · line 170

Quoted 2 lines
(quoted diff) +1 -1
-	return pcdrv->probe(pcdev, id);
+		amba_put_disable_pclk(pcdev);

Should the AMBA clock be disabled if the probe() fails?

This is exactly what happens here.

I think I got my logic reversed..

One more item I noted. These changes want to keep the clock enabled after the probe - only disabling the clock on amba_remove. Both the AMBA peripheral driver (ie, the LCD or MMCI driver) and the amba_remove function now disable the clocks.

Wouldn't drivers that use clk_disable in their xxx_suspend functions have some problems with this as multiple clk_enables have been called?

ie,
AMBA enables clock in amba_probe() <<< clock use count = 1 pl022 driver enables clock in mmci_probe <<< clock use count = 2 ...SPI stuff happens...
...Linux enters suspend mode...
pl022 driver enters suspend mode, disables clock <<< clock use count = 1

Because the AMBA driver enabled the clock, the clock driver would still see a use count > 1 on suspend and keep the clock enabled. Should the suspend and resume functions in bus.c also disable/enable clocks? Or maybe the peripheral drivers should just handle clocking and the clock enable in amba_probe should be removed?

#20 lore raw

PATCH v4 1/1 GPIO PL061: Adding Clk framework support

2010/8/3 Kevin Wells <[email protected]>:

Quoted 2 lines

Wouldn't drivers that use clk_disable in their xxx_suspend functions have some problems with this as multiple clk_enables have been called?

Russell already answered that, all drivers have to be augmented in the following way (this is for PL011, the same will be valid for any other PrimeCell with aggressive clocking):

[Russell King]

Quoted 9 lines

1. add an amba_bus_clk_disable() at the end of the successful probe function
2. add amba_bus_clk_enable() at the beginning of the remove function.
3. add amba_bus_clk_enable()/amba_bus_clk_disable() around each of the suspend(), resume(), pl011_console_write(), and pl011_console_get_options() functions.
4. amba_bus_clk_enable() at the start of the startup method.
5. amba_bus_clk_disable() at the end of the shutdown method.

Quoted 5 lines

AMBA enables clock in amba_probe() <<< clock use count = 1 pl022 driver enables clock in mmci_probe <<< clock use count = 2 ...SPI stuff happens...
...Linux enters suspend mode...
pl022 driver enters suspend mode, disables clock <<< clock use count = 1

OK I feel some responsibility for that driver so I will try to fix it, maybe I can take a round@PL011 and perhaps MMCI as well.

Yours,
Linus Walleij

#21 lore raw

PATCH v4 1/1 GPIO PL061: Adding Clk framework support

Quoted 19 lines

2010/8/3 Kevin Wells <[email protected]>:

Quoted 2 lines

Wouldn't drivers that use clk_disable in their xxx_suspend functions have some problems with this as multiple clk_enables have been called?

Russell already answered that, all drivers have to be augmented in the following way (this is for PL011, the same will be valid for any other PrimeCell with aggressive clocking):

[Russell King]

Quoted 8 lines

1. add an amba_bus_clk_disable() at the end of the successful probe ?function
2. add amba_bus_clk_enable() at the beginning of the remove function.
3. add amba_bus_clk_enable()/amba_bus_clk_disable() around each of the suspend(), resume(), pl011_console_write(), and ?pl011_console_get_options() functions.
4. amba_bus_clk_enable() at the start of the startup method.
5. amba_bus_clk_disable() at the end of the shutdown method.

Quoted 9 lines
Quoted 5 lines

AMBA enables clock in amba_probe() <<< clock use count = 1 pl022 driver enables clock in mmci_probe <<< clock use count = 2 ...SPI stuff happens...
...Linux enters suspend mode...
pl022 driver enters suspend mode, disables clock <<< clock use count = 1

OK I feel some responsibility for that driver so I will try to fix it, maybe I can take a round at PL011 and perhaps MMCI as well.

Thanks for the info Linus. I'll take an initial try at the pl11x driver..

Kevin

#22 lore raw

PATCH v4 1/1 GPIO PL061: Adding Clk framework support

In reply to Kevin Wells
Quoted 4 lines · Kevin Wells

One more item I noted. These changes want to keep the clock enabled after the probe - only disabling the clock on amba_remove. Both the AMBA peripheral driver (ie, the LCD or MMCI driver) and the amba_remove function now disable the clocks.

Absolutely correct. If we enable the clock for the probe call, and then disable it afterwards, then what do AMBA bus drivers which have registered interrupt routines in their probe function do when they receive an interrupt and want to access their statuc register?

Do we just let them go ahead and fault? That's just stupid.

No, what we do is create a setup where existing drivers continue to work without modification - and that means ensuring that the bus clock is enabled for the duration that the driver is active.

As I've already said, if drivers then want to achieve greater power savings, they need to use the supplied functions to manage the bus clock - with the assumption that they will be called with the bus clock already enabled. If a driver doesn't participate in managing the APB clock, then they don't need to be modified.

Quoted 2 lines

Wouldn't drivers that use clk_disable in their xxx_suspend functions have some problems with this as multiple clk_enables have been called?

Probably, and I left this intentionally open for the time being because it wasn't clear whether the right action was to disable the bus clock across a suspend.

However, thinking about it some more, it's probably safe to disable the bus clock _after_ the driver specific suspend call has successfully completed, and re-enable it before calling the resume method. So let's do this...

#23 lore raw

PATCH v4 1/1 GPIO PL061: Adding Clk framework support

In reply to Kevin Wells

2010/7/30 Kevin Wells <[email protected]>:

Quoted 10 lines
Quoted 10 lines

- ? ? id = amba_lookup(pcdrv->id_table, pcdev);
+ ? ? do {
+ ? ? ? ? ? ? ret = amba_get_enable_pclk(pcdev);
+ ? ? ? ? ? ? if (ret)
+ ? ? ? ? ? ? ? ? ? ? break;
+
+ ? ? ? ? ? ? ret = pcdrv->probe(pcdev, id);
+ ? ? ? ? ? ? if (ret == 0)
+ ? ? ? ? ? ? ? ? ? ? break;

Quoted 4 lines
Quoted 2 lines

- ? ? return pcdrv->probe(pcdev, id);
+ ? ? ? ? ? ? amba_put_disable_pclk(pcdev);

Should the AMBA clock be disabled if the probe() fails?

Yes. This is the pclk for this one PrimeCell. It is first turned on, then the driver attempts to probe and in case that fails there obviously is no driver for this piece of silicon so why should it be clocked?

Yours,
Linus Walleij

#24 lore raw

PATCH v4 1/1 GPIO PL061: Adding Clk framework support

In reply to Russell King - ARM Linux
Quoted 14 lines · Russell King - ARM Linux

Review

Quoted 9 lines · Viresh KUMAR
(quoted diff) +6 -3
-	spin_lock_init(&chip->lock);
-	spin_lock_init(&chip->irq_lock);
-	INIT_LIST_HEAD(&chip->list);
+	chip->clk = clk_get(&dev->dev, NULL);
+	if (IS_ERR(chip->clk)) {
+		ret = PTR_ERR(chip->clk);
+		/* clk Not present */
+		if (ret == -ENOENT)
+			chip->clk = NULL;

Please don't assume that NULL is not a valid value for a 'struct clk'. Eg,

Quoted 4 lines

struct clk *clk_get(struct device *dev, const char *id)

{
        return dev && strcmp(dev_name(dev), "mb:16") == 0 ? NULL : ERR_PTR(-ENOENT);
}

I knew this. This convention is just local to the driver.

Quoted 4 lines

However, this patch begs the question about what you're trying to do, as the primecell doesn't take a clock for its operation other than the AMBA bus clock.

This issue is different from the issue i raised which was common to all amba devices. This is just for this GPIO driver, we need to enable clk before using any gpio pin.

viresh.

#25 lore raw

PATCH v4 1/1 GPIO PL061: Adding Clk framework support

2010/7/12 Viresh KUMAR <[email protected]>:

Quoted 3 lines

This issue is different from the issue i raised which was common to all amba devices. This is just for this GPIO driver, we need to enable clk before using any gpio pin.

I think what Russell means is that all current clocks looked up by the drivers are implicitly of types that control output signals to things like UART RX, SPI out, etc, i.e. clocks that control signals that exit the system.

These are usually fixed, e.g. in ARM Ltd. reference designs there is a fixed 24MHz clock for all of these.

The clock that controls the internal logic in the PrimeCell itself should be modeled into the AMBA (PrimeCell) bus abstraction.

Yours,
Linus Walleij

#26 lore raw

PATCH v4 1/1 GPIO PL061: Adding Clk framework support

In reply to Viresh KUMAR
Quoted 3 lines · Viresh KUMAR

This issue is different from the issue i raised which was common to all amba devices. This is just for this GPIO driver, we need to enable clk before using any gpio pin.

PL061 does not have any other clock signal other than PCLK, the APB (AMBA peripheral bus) clock. This clock needs to be running to access any register in an APB peripheral. The primecell drivers and AMBA support is currently implemented with the assumtion that this clock is permanently running.

If you need to enable a clock before using any GPIO pin, and it's not the APB clock, then that's a platform extension that is outside of the PL061 domain, and probably also affects alternate functions of the GPIO pins as well. That means the PL061 code shouldn't manage that clock signal as it won't know what's going on with the alternate functions.

#27 lore raw

PATCH v4 1/1 GPIO PL061: Adding Clk framework support

Quoted 10 lines · Russell King - ARM Linux
Quoted 3 lines · Viresh KUMAR
Quoted 3 lines

This issue is different from the issue i raised which was common to all amba devices. This is just for this GPIO driver, we need to enable clk before using any gpio pin.

PL061 does not have any other clock signal other than PCLK, the APB (AMBA peripheral bus) clock. This clock needs to be running to access any register in an APB peripheral. The primecell drivers and AMBA support is currently implemented with the assumtion that this clock is permanently running.

Quoted 6 lines

If you need to enable a clock before using any GPIO pin, and it's not the APB clock, then that's a platform extension that is outside of the PL061 domain, and probably also affects alternate functions of the GPIO pins as well. That means the PL061 code shouldn't manage that clock signal as it won't know what's going on with the alternate functions.

Russell,

I agree that this clock is outside of GPIO's domain, but this is how it is done for all other amba and non amba devices too, like pl011, pl022, etc. There also we are handling platform specific clocks in drivers only.

Am i missing something??

viresh.

#28 lore raw

PATCH v4 1/1 GPIO PL061: Adding Clk framework support

Quoted 14 lines · Viresh KUMAR
Quoted 6 lines · Russell King - ARM Linux

PL061 does not have any other clock signal other than PCLK, the APB (AMBA peripheral bus) clock. This clock needs to be running to access any register in an APB peripheral. The primecell drivers and AMBA support is currently implemented with the assumtion that this clock is permanently running.

Quoted 6 lines

If you need to enable a clock before using any GPIO pin, and it's not the APB clock, then that's a platform extension that is outside of the PL061 domain, and probably also affects alternate functions of the GPIO pins as well. That means the PL061 code shouldn't manage that clock signal as it won't know what's going on with the alternate functions.

Quoted 5 lines

Russell,

I agree that this clock is outside of GPIO's domain, but this is how it is done for all other amba and non amba devices too, like pl011, pl022, etc. There also we are handling platform specific clocks in drivers only.

No.

1. All primecells take a clock called PCLK, which is the bus clock which times accesses to the registers from the CPU.

2. PL011 takes a clock, called UARTCLK. PL022 takes a clock, called SSPCLK. PL061 does not take such a clock.

If you look that up in the ARM documentation (DDI0183, DDI0194 and DDI0190 respectively), you'll find these signal listed in the relevant appendix - A.1 for the bus signals and A.2 for the internal on-chip peripheral signals.

The existing clk API usage in primecell drivers covers (2). It does not cover (1). As such, and as PL011 and PL022 do have non-APB clock signals, they have clk API support. As PL061 doesn't have non-APB clock signals, it doesn't have clk API support and shouldn't need it.

If your device is setup such that there is a 'GPIO clock' control, it is either relating to a control for the APB bus clock, or it is something on the pad side of the PL061 block, or is just a dummy control bit to keep programmers happy which does nothing.