Explorar o código

ARM: at91/tc: add device tree support to atmel_tclib

Device tree support added to atmel_tclib: the generic Timer Counter
library. This is used by the clocksource/clockevent driver tcb_clksrc.

The current DT enabled platforms are also modified to use it:
- .dtsi files are modified to add Timer Counter Block entries
- alias are created to allow identification of each block
- clkdev lookup tables are added for clocks identification.

Signed-off-by: Nicolas Ferre <nicolas.ferre@atmel.com>
Acked-by: Jean-Christophe PLAGNIOL-VILLARD <plagnioj@jcrosoft.com>
Acked-by: Grant Likely <grant.likely@secretlab.ca>
Nicolas Ferre %!s(int64=13) %!d(string=hai) anos
pai
achega
3a61a5dae4

+ 24 - 0
Documentation/devicetree/bindings/arm/atmel-at91.txt

@@ -6,3 +6,27 @@ PIT Timer required properties:
 - reg: Should contain registers location and length
 - interrupts: Should contain interrupt for the PIT which is the IRQ line
   shared across all System Controller members.
+
+TC/TCLIB Timer required properties:
+- compatible: Should be "atmel,<chip>-pit".
+  <chip> can be "at91rm9200" or "at91sam9x5"
+- reg: Should contain registers location and length
+- interrupts: Should contain all interrupts for the TC block
+  Note that you can specify several interrupt cells if the TC
+  block has one interrupt per channel.
+
+Examples:
+
+One interrupt per TC block:
+	tcb0: timer@fff7c000 {
+		compatible = "atmel,at91rm9200-tcb";
+		reg = <0xfff7c000 0x100>;
+		interrupts = <18 4>;
+	};
+
+One interrupt per TC channel in a TC block:
+	tcb1: timer@fffdc000 {
+		compatible = "atmel,at91rm9200-tcb";
+		reg = <0xfffdc000 0x100>;
+		interrupts = <26 4 27 4 28 4>;
+	};

+ 14 - 0
arch/arm/boot/dts/at91sam9g20.dtsi

@@ -26,6 +26,8 @@
 		gpio0 = &pioA;
 		gpio1 = &pioB;
 		gpio2 = &pioC;
+		tcb0 = &tcb0;
+		tcb1 = &tcb1;
 	};
 	cpus {
 		cpu@0 {
@@ -63,6 +65,18 @@
 				interrupts = <1 4>;
 			};
 
+			tcb0: timer@fffa0000 {
+				compatible = "atmel,at91rm9200-tcb";
+				reg = <0xfffa0000 0x100>;
+				interrupts = <17 4 18 4 19 4>;
+			};
+
+			tcb1: timer@fffdc000 {
+				compatible = "atmel,at91rm9200-tcb";
+				reg = <0xfffdc000 0x100>;
+				interrupts = <26 4 27 4 28 4>;
+			};
+
 			pioA: gpio@fffff400 {
 				compatible = "atmel,at91rm9200-gpio";
 				reg = <0xfffff400 0x100>;

+ 15 - 0
arch/arm/boot/dts/at91sam9g45.dtsi

@@ -27,6 +27,8 @@
 		gpio2 = &pioC;
 		gpio3 = &pioD;
 		gpio4 = &pioE;
+		tcb0 = &tcb0;
+		tcb1 = &tcb1;
 	};
 	cpus {
 		cpu@0 {
@@ -64,6 +66,19 @@
 				interrupts = <1 4>;
 			};
 
+
+			tcb0: timer@fff7c000 {
+				compatible = "atmel,at91rm9200-tcb";
+				reg = <0xfff7c000 0x100>;
+				interrupts = <18 4>;
+			};
+
+			tcb1: timer@fffd4000 {
+				compatible = "atmel,at91rm9200-tcb";
+				reg = <0xfffd4000 0x100>;
+				interrupts = <18 4>;
+			};
+
 			dma: dma-controller@ffffec00 {
 				compatible = "atmel,at91sam9g45-dma";
 				reg = <0xffffec00 0x200>;

+ 7 - 0
arch/arm/mach-at91/at91sam9260.c

@@ -209,6 +209,13 @@ static struct clk_lookup periph_clocks_lookups[] = {
 	CLKDEV_CON_DEV_ID("usart", "fffd0000.serial", &usart3_clk),
 	CLKDEV_CON_DEV_ID("usart", "fffd4000.serial", &usart4_clk),
 	CLKDEV_CON_DEV_ID("usart", "fffd8000.serial", &usart5_clk),
+	/* more tc lookup table for DT entries */
+	CLKDEV_CON_DEV_ID("t0_clk", "fffa0000.timer", &tc0_clk),
+	CLKDEV_CON_DEV_ID("t1_clk", "fffa0000.timer", &tc1_clk),
+	CLKDEV_CON_DEV_ID("t2_clk", "fffa0000.timer", &tc2_clk),
+	CLKDEV_CON_DEV_ID("t0_clk", "fffdc000.timer", &tc3_clk),
+	CLKDEV_CON_DEV_ID("t1_clk", "fffdc000.timer", &tc4_clk),
+	CLKDEV_CON_DEV_ID("t2_clk", "fffdc000.timer", &tc5_clk),
 	/* fake hclk clock */
 	CLKDEV_CON_DEV_ID("hclk", "at91_ohci", &ohci_clk),
 	CLKDEV_CON_ID("pioA", &pioA_clk),

+ 17 - 0
arch/arm/mach-at91/at91sam9260_devices.c

@@ -699,8 +699,25 @@ static struct platform_device at91sam9260_tcb1_device = {
 	.num_resources	= ARRAY_SIZE(tcb1_resources),
 };
 
+#if defined(CONFIG_OF)
+static struct of_device_id tcb_ids[] = {
+	{ .compatible = "atmel,at91rm9200-tcb" },
+	{ /*sentinel*/ }
+};
+#endif
+
 static void __init at91_add_device_tc(void)
 {
+#if defined(CONFIG_OF)
+	struct device_node *np;
+
+	np = of_find_matching_node(NULL, tcb_ids);
+	if (np) {
+		of_node_put(np);
+		return;
+	}
+#endif
+
 	platform_device_register(&at91sam9260_tcb0_device);
 	platform_device_register(&at91sam9260_tcb1_device);
 }

+ 3 - 0
arch/arm/mach-at91/at91sam9g45.c

@@ -229,6 +229,9 @@ static struct clk_lookup periph_clocks_lookups[] = {
 	CLKDEV_CON_DEV_ID("usart", "fff90000.serial", &usart1_clk),
 	CLKDEV_CON_DEV_ID("usart", "fff94000.serial", &usart2_clk),
 	CLKDEV_CON_DEV_ID("usart", "fff98000.serial", &usart3_clk),
+	/* more tc lookup table for DT entries */
+	CLKDEV_CON_DEV_ID("t0_clk", "fff7c000.timer", &tcb0_clk),
+	CLKDEV_CON_DEV_ID("t0_clk", "fffd4000.timer", &tcb0_clk),
 	/* fake hclk clock */
 	CLKDEV_CON_DEV_ID("hclk", "at91_ohci", &uhphs_clk),
 	CLKDEV_CON_ID("pioA", &pioA_clk),

+ 17 - 0
arch/arm/mach-at91/at91sam9g45_devices.c

@@ -1090,8 +1090,25 @@ static struct platform_device at91sam9g45_tcb1_device = {
 	.num_resources	= ARRAY_SIZE(tcb1_resources),
 };
 
+#if defined(CONFIG_OF)
+static struct of_device_id tcb_ids[] = {
+	{ .compatible = "atmel,at91rm9200-tcb" },
+	{ /*sentinel*/ }
+};
+#endif
+
 static void __init at91_add_device_tc(void)
 {
+#if defined(CONFIG_OF)
+	struct device_node *np;
+
+	np = of_find_matching_node(NULL, tcb_ids);
+	if (np) {
+		of_node_put(np);
+		return;
+	}
+#endif
+
 	platform_device_register(&at91sam9g45_tcb0_device);
 	platform_device_register(&at91sam9g45_tcb1_device);
 }

+ 25 - 2
drivers/misc/atmel_tclib.c

@@ -6,8 +6,10 @@
 #include <linux/ioport.h>
 #include <linux/kernel.h>
 #include <linux/platform_device.h>
+#include <linux/module.h>
 #include <linux/slab.h>
 #include <linux/export.h>
+#include <linux/of.h>
 
 /*
  * This is a thin library to solve the problem of how to portably allocate
@@ -48,7 +50,13 @@ struct atmel_tc *atmel_tc_alloc(unsigned block, const char *name)
 
 	spin_lock(&tc_list_lock);
 	list_for_each_entry(tc, &tc_list, node) {
-		if (tc->pdev->id == block) {
+		if (tc->pdev->dev.of_node) {
+			if (of_alias_get_id(tc->pdev->dev.of_node, "tcb")
+					== block) {
+				pdev = tc->pdev;
+				break;
+			}
+		} else if (tc->pdev->id == block) {
 			pdev = tc->pdev;
 			break;
 		}
@@ -105,6 +113,18 @@ void atmel_tc_free(struct atmel_tc *tc)
 }
 EXPORT_SYMBOL_GPL(atmel_tc_free);
 
+#if defined(CONFIG_OF)
+static const struct of_device_id atmel_tcb_dt_ids[] = {
+	{
+		.compatible = "atmel,at91rm9200-tcb",
+	}, {
+		/* sentinel */
+	}
+};
+
+MODULE_DEVICE_TABLE(of, atmel_tcb_dt_ids);
+#endif
+
 static int __init tc_probe(struct platform_device *pdev)
 {
 	struct atmel_tc *tc;
@@ -154,7 +174,10 @@ static int __init tc_probe(struct platform_device *pdev)
 }
 
 static struct platform_driver tc_driver = {
-	.driver.name	= "atmel_tcb",
+	.driver = {
+		.name	= "atmel_tcb",
+		.of_match_table	= of_match_ptr(atmel_tcb_dt_ids),
+	},
 };
 
 static int __init tc_init(void)