From f3c1830096661e270f11f2a33ffb7274f50c90a6 Mon Sep 17 00:00:00 2001
From: Phil Elwell <phil@raspberrypi.org>
Date: Thu, 18 Dec 2014 16:48:32 +0000
Subject: [PATCH 068/114] lirc-rpi: Add device tree support, and a suitable
 overlay

The overlay supports DT parameters that match the old module
parameters, except that gpio_in_pull should be set using the
strings "up", "down" or "off".

lirc-rpi: Also support pinctrl-bcm2835 in non-DT mode
---
 arch/arm/boot/dts/lirc-rpi-overlay.dts |  57 ++++++++++++
 drivers/staging/media/lirc/lirc_rpi.c  | 154 +++++++++++++++++++++++++++------
 2 files changed, 183 insertions(+), 28 deletions(-)
 create mode 100644 arch/arm/boot/dts/lirc-rpi-overlay.dts

--- /dev/null
+++ b/arch/arm/boot/dts/lirc-rpi-overlay.dts
@@ -0,0 +1,57 @@
+// Definitions for lirc-rpi module
+/dts-v1/;
+/plugin/;
+
+/ {
+	compatible = "brcm,bcm2708";
+
+	fragment@0 {
+		target-path = "/";
+		__overlay__ {
+			lirc_rpi: lirc_rpi {
+				compatible = "rpi,lirc-rpi";
+				pinctrl-names = "default";
+				pinctrl-0 = <&lirc_pins>;
+				status = "okay";
+
+				// Override autodetection of IR receiver circuit
+				// (0 = active high, 1 = active low, -1 = no override )
+				rpi,sense = <0xffffffff>;
+
+				// Software carrier
+				// (0 = off, 1 = on)
+				rpi,softcarrier = <1>;
+
+				// Invert output
+				// (0 = off, 1 = on)
+				rpi,invert = <0>;
+
+				// Enable debugging messages
+				// (0 = off, 1 = on)
+				rpi,debug = <0>;
+			};
+		};
+	};
+
+	fragment@1 {
+		target = <&gpio>;
+		__overlay__ {
+			lirc_pins: lirc_pins {
+				brcm,pins = <17 18>;
+				brcm,function = <1 0>; // out in
+				brcm,pull = <0 1>; // off down
+			};
+		};
+	};
+
+	__overrides__ {
+		gpio_out_pin =  <&lirc_pins>,"brcm,pins:0";
+		gpio_in_pin =   <&lirc_pins>,"brcm,pins:4";
+		gpio_in_pull =  <&lirc_pins>,"brcm,pull:4";
+
+		sense =         <&lirc_rpi>,"rpi,sense:0";
+		softcarrier =   <&lirc_rpi>,"rpi,softcarrier:0";
+		invert =        <&lirc_rpi>,"rpi,invert:0";
+		debug =         <&lirc_rpi>,"rpi,debug:0";
+	};
+};
--- a/drivers/staging/media/lirc/lirc_rpi.c
+++ b/drivers/staging/media/lirc/lirc_rpi.c
@@ -40,6 +40,7 @@
 #include <media/lirc_dev.h>
 #include <mach/gpio.h>
 #include <linux/gpio.h>
+#include <linux/of_platform.h>
 
 #include <linux/platform_data/bcm2708.h>
 
@@ -295,32 +296,117 @@ static int is_right_chip(struct gpio_chi
 	return 0;
 }
 
+static inline int read_bool_property(const struct device_node *np,
+				     const char *propname,
+				     bool *out_value)
+{
+	u32 value = 0;
+	int err = of_property_read_u32(np, propname, &value);
+	if (err == 0)
+		*out_value = (value != 0);
+	return err;
+}
+
+static void read_pin_settings(struct device_node *node)
+{
+	u32 pin;
+	int index;
+
+	for (index = 0;
+	     of_property_read_u32_index(
+		     node,
+		     "brcm,pins",
+		     index,
+		     &pin) == 0;
+	     index++) {
+		u32 function;
+		int err;
+		err = of_property_read_u32_index(
+			node,
+			"brcm,function",
+			index,
+			&function);
+		if (err == 0) {
+			if (function == 1) /* Output */
+				gpio_out_pin = pin;
+			else if (function == 0) /* Input */
+				gpio_in_pin = pin;
+		}
+	}
+}
+
 static int init_port(void)
 {
 	int i, nlow, nhigh, ret;
+	struct device_node *node;
+
+	node = lirc_rpi_dev->dev.of_node;
 
 	gpiochip = gpiochip_find("bcm2708_gpio", is_right_chip);
 
-	if (!gpiochip)
+	/*
+	 * Because of the lack of a setpull function, only support
+	 * pinctrl-bcm2835 if using device tree.
+	*/
+	if (!gpiochip && node)
+		gpiochip = gpiochip_find("pinctrl-bcm2835", is_right_chip);
+
+	if (!gpiochip) {
+		pr_err(LIRC_DRIVER_NAME ": gpio chip not found!\n");
 		return -ENODEV;
+	}
+
+	if (node) {
+		struct device_node *pins_node;
+
+		pins_node = of_parse_phandle(node, "pinctrl-0", 0);
+		if (!pins_node) {
+			printk(KERN_ERR LIRC_DRIVER_NAME
+			       ": pinctrl settings not found!\n");
+			ret = -EINVAL;
+			goto exit_init_port;
+		}
+
+		read_pin_settings(pins_node);
+
+		of_property_read_u32(node, "rpi,sense", &sense);
+
+		read_bool_property(node, "rpi,softcarrier", &softcarrier);
+
+		read_bool_property(node, "rpi,invert", &invert);
+
+		read_bool_property(node, "rpi,debug", &debug);
+
+	}
+	else
+	{
+		if (gpio_in_pin >= BCM2708_NR_GPIOS ||
+		    gpio_out_pin >= BCM2708_NR_GPIOS) {
+			ret = -EINVAL;
+			printk(KERN_ERR LIRC_DRIVER_NAME
+			       ": invalid GPIO pin(s) specified!\n");
+			goto exit_init_port;
+		}
+
+		if (gpio_request(gpio_out_pin, LIRC_DRIVER_NAME " ir/out")) {
+			printk(KERN_ALERT LIRC_DRIVER_NAME
+			       ": cant claim gpio pin %d\n", gpio_out_pin);
+			ret = -ENODEV;
+			goto exit_init_port;
+		}
+
+		if (gpio_request(gpio_in_pin, LIRC_DRIVER_NAME " ir/in")) {
+			printk(KERN_ALERT LIRC_DRIVER_NAME
+			       ": cant claim gpio pin %d\n", gpio_in_pin);
+			ret = -ENODEV;
+			goto exit_gpio_free_out_pin;
+		}
+
+		bcm2708_gpio_setpull(gpiochip, gpio_in_pin, gpio_in_pull);
+		gpiochip->direction_input(gpiochip, gpio_in_pin);
+		gpiochip->direction_output(gpiochip, gpio_out_pin, 1);
+	}
 
-	if (gpio_request(gpio_out_pin, LIRC_DRIVER_NAME " ir/out")) {
-		printk(KERN_ALERT LIRC_DRIVER_NAME
-		       ": cant claim gpio pin %d\n", gpio_out_pin);
-		ret = -ENODEV;
-		goto exit_init_port;
-	}
-
-	if (gpio_request(gpio_in_pin, LIRC_DRIVER_NAME " ir/in")) {
-		printk(KERN_ALERT LIRC_DRIVER_NAME
-		       ": cant claim gpio pin %d\n", gpio_in_pin);
-		ret = -ENODEV;
-		goto exit_gpio_free_out_pin;
-	}
-
-	bcm2708_gpio_setpull(gpiochip, gpio_in_pin, gpio_in_pull);
-	gpiochip->direction_input(gpiochip, gpio_in_pin);
-	gpiochip->direction_output(gpiochip, gpio_out_pin, 1);
 	gpiochip->set(gpiochip, gpio_out_pin, invert);
 
 	irq_num = gpiochip->to_irq(gpiochip, gpio_in_pin);
@@ -514,15 +600,23 @@ static struct lirc_driver driver = {
 	.owner		= THIS_MODULE,
 };
 
+static const struct of_device_id lirc_rpi_of_match[] = {
+	{ .compatible = "rpi,lirc-rpi", },
+	{},
+};
+MODULE_DEVICE_TABLE(of, lirc_rpi_of_match);
+
 static struct platform_driver lirc_rpi_driver = {
 	.driver = {
 		.name   = LIRC_DRIVER_NAME,
 		.owner  = THIS_MODULE,
+		.of_match_table = of_match_ptr(lirc_rpi_of_match),
 	},
 };
 
 static int __init lirc_rpi_init(void)
 {
+	struct device_node *node;
 	int result;
 
 	/* Init read buffer. */
@@ -537,15 +631,26 @@ static int __init lirc_rpi_init(void)
 		goto exit_buffer_free;
 	}
 
-	lirc_rpi_dev = platform_device_alloc(LIRC_DRIVER_NAME, 0);
-	if (!lirc_rpi_dev) {
-		result = -ENOMEM;
-		goto exit_driver_unregister;
-	}
+	node = of_find_compatible_node(NULL, NULL,
+				       lirc_rpi_of_match[0].compatible);
 
-	result = platform_device_add(lirc_rpi_dev);
-	if (result)
-		goto exit_device_put;
+	if (node) {
+		/* DT-enabled */
+		lirc_rpi_dev = of_find_device_by_node(node);
+		WARN_ON(lirc_rpi_dev->dev.of_node != node);
+		of_node_put(node);
+	}
+	else {
+		lirc_rpi_dev = platform_device_alloc(LIRC_DRIVER_NAME, 0);
+		if (!lirc_rpi_dev) {
+			result = -ENOMEM;
+			goto exit_driver_unregister;
+		}
+
+		result = platform_device_add(lirc_rpi_dev);
+		if (result)
+			goto exit_device_put;
+	}
 
 	return 0;
 
@@ -577,13 +682,6 @@ static int __init lirc_rpi_init_module(v
 	if (result)
 		return result;
 
-	if (gpio_in_pin >= BCM2708_NR_GPIOS || gpio_out_pin >= BCM2708_NR_GPIOS) {
-		result = -EINVAL;
-		printk(KERN_ERR LIRC_DRIVER_NAME
-		       ": invalid GPIO pin(s) specified!\n");
-		goto exit_rpi;
-	}
-
 	result = init_port();
 	if (result < 0)
 		goto exit_rpi;
