mirror of
				git://git.openwrt.org/openwrt/openwrt.git
				synced 2025-10-31 14:04:26 -04:00 
			
		
		
		
	Right now all brcm2708 patches are extracted from the non-mainline raspberrypi/linux git tree. Many of them are hacks and/or are unneeded in LEDE. Raspberry Pi is getting better and better mainline support so it would be nice to finally start maintaining patches in a cleaner way: 1) Backport patches accepted in upstream tree 2) Start using upstream drivers 3) Pick only these patches that are needed for more complete support Handling above tasks requires grouping patches - ideally using the same prefixes as generic ones. It means we should rename existing patches to use some high prefix. This will allow e.g. use 0xx for backported code. Signed-off-by: Rafał Miłecki <rafal@milecki.pl> Acked-by: Florian Fainelli <f.fainelli@gmail.com> Acked-by: Stijn Tintel <stijn@linux-ipv6.be>
		
			
				
	
	
		
			856 lines
		
	
	
		
			21 KiB
		
	
	
	
		
			Diff
		
	
	
	
	
	
			
		
		
	
	
			856 lines
		
	
	
		
			21 KiB
		
	
	
	
		
			Diff
		
	
	
	
	
	
| From 277c109833c1d78543bec0723ab42f4c79937df9 Mon Sep 17 00:00:00 2001
 | |
| From: Aron Szabo <aron@aron.ws>
 | |
| Date: Sat, 16 Jun 2012 12:15:55 +0200
 | |
| Subject: [PATCH] lirc: added support for RaspberryPi GPIO
 | |
| 
 | |
| lirc_rpi: Use read_current_timer to determine transmitter delay. Thanks to jjmz and others
 | |
| See: https://github.com/raspberrypi/linux/issues/525
 | |
| 
 | |
| lirc: Remove restriction on gpio pins that can be used with lirc
 | |
| 
 | |
| Compute Module, for example could use different pins
 | |
| 
 | |
| lirc_rpi: Add parameter to specify input pin pull
 | |
| 
 | |
| Depending on the connected IR circuitry it might be desirable to change the
 | |
| gpios internal pull from it pull-down default behaviour. Add a module
 | |
| parameter to allow the user to set it explicitly.
 | |
| 
 | |
| Signed-off-by: Julian Scheel <julian@jusst.de>
 | |
| 
 | |
| lirc-rpi: Use the higher-level irq control functions
 | |
| 
 | |
| This module used to access the irq_chip methods of the
 | |
| gpio controller directly, rather than going through the
 | |
| standard enable_irq/irq_set_irq_type functions. This
 | |
| caused problems on pinctrl-bcm2835 which only implements
 | |
| the irq_enable/disable methods and not irq_unmask/mask.
 | |
| 
 | |
| lirc-rpi: Correct the interrupt usage
 | |
| 
 | |
| 1) Correct the use of enable_irq (i.e. don't call it so often)
 | |
| 2) Correct the shutdown sequence.
 | |
| 3) Avoid a bcm2708_gpio driver quirk by setting the irq flags earlier
 | |
| 
 | |
| lirc-rpi: use getnstimeofday instead of read_current_timer
 | |
| 
 | |
| read_current_timer isn't guaranteed to return values in
 | |
| microseconds, and indeed it doesn't on a Pi2.
 | |
| 
 | |
| Issue: linux#827
 | |
| 
 | |
| 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
 | |
| 
 | |
| fix auto-sense in lirc_rpi driver
 | |
| 
 | |
| On a Raspberry Pi 2, the lirc_rpi driver might receive spurious
 | |
| interrupts and change it's low-active / high-active setting.
 | |
| When this happens, the IR remote control stops working.
 | |
| 
 | |
| This patch disables this auto-detection if the 'sense' parameter
 | |
| was set in the device tree, making the driver robust to such
 | |
| spurious interrupts.
 | |
| ---
 | |
|  drivers/staging/media/lirc/Kconfig    |   6 +
 | |
|  drivers/staging/media/lirc/Makefile   |   1 +
 | |
|  drivers/staging/media/lirc/lirc_rpi.c | 734 ++++++++++++++++++++++++++++++++++
 | |
|  include/linux/platform_data/bcm2708.h |  23 ++
 | |
|  4 files changed, 764 insertions(+)
 | |
|  create mode 100644 drivers/staging/media/lirc/lirc_rpi.c
 | |
|  create mode 100644 include/linux/platform_data/bcm2708.h
 | |
| 
 | |
| --- a/drivers/staging/media/lirc/Kconfig
 | |
| +++ b/drivers/staging/media/lirc/Kconfig
 | |
| @@ -32,6 +32,12 @@ config LIRC_PARALLEL
 | |
|  	help
 | |
|  	  Driver for Homebrew Parallel Port Receivers
 | |
|  
 | |
| +config LIRC_RPI
 | |
| +	tristate "Homebrew GPIO Port Receiver/Transmitter for the RaspberryPi"
 | |
| +	depends on LIRC
 | |
| +	help
 | |
| +	  Driver for Homebrew GPIO Port Receiver/Transmitter for the RaspberryPi
 | |
| +
 | |
|  config LIRC_SASEM
 | |
|  	tristate "Sasem USB IR Remote"
 | |
|  	depends on LIRC && USB
 | |
| --- a/drivers/staging/media/lirc/Makefile
 | |
| +++ b/drivers/staging/media/lirc/Makefile
 | |
| @@ -6,6 +6,7 @@
 | |
|  obj-$(CONFIG_LIRC_BT829)	+= lirc_bt829.o
 | |
|  obj-$(CONFIG_LIRC_IMON)		+= lirc_imon.o
 | |
|  obj-$(CONFIG_LIRC_PARALLEL)	+= lirc_parallel.o
 | |
| +obj-$(CONFIG_LIRC_RPI)		+= lirc_rpi.o
 | |
|  obj-$(CONFIG_LIRC_SASEM)	+= lirc_sasem.o
 | |
|  obj-$(CONFIG_LIRC_SERIAL)	+= lirc_serial.o
 | |
|  obj-$(CONFIG_LIRC_SIR)		+= lirc_sir.o
 | |
| --- /dev/null
 | |
| +++ b/drivers/staging/media/lirc/lirc_rpi.c
 | |
| @@ -0,0 +1,734 @@
 | |
| +/*
 | |
| + * lirc_rpi.c
 | |
| + *
 | |
| + * lirc_rpi - Device driver that records pulse- and pause-lengths
 | |
| + *	      (space-lengths) (just like the lirc_serial driver does)
 | |
| + *	      between GPIO interrupt events on the Raspberry Pi.
 | |
| + *	      Lots of code has been taken from the lirc_serial module,
 | |
| + *	      so I would like say thanks to the authors.
 | |
| + *
 | |
| + * Copyright (C) 2012 Aron Robert Szabo <aron@reon.hu>,
 | |
| + *		      Michael Bishop <cleverca22@gmail.com>
 | |
| + *  This program is free software; you can redistribute it and/or modify
 | |
| + *  it under the terms of the GNU General Public License as published by
 | |
| + *  the Free Software Foundation; either version 2 of the License, or
 | |
| + *  (at your option) any later version.
 | |
| + *
 | |
| + *  This program is distributed in the hope that it will be useful,
 | |
| + *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 | |
| + *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | |
| + *  GNU General Public License for more details.
 | |
| + *
 | |
| + *  You should have received a copy of the GNU General Public License
 | |
| + *  along with this program; if not, write to the Free Software
 | |
| + *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 | |
| + */
 | |
| +
 | |
| +#include <linux/module.h>
 | |
| +#include <linux/errno.h>
 | |
| +#include <linux/interrupt.h>
 | |
| +#include <linux/sched.h>
 | |
| +#include <linux/kernel.h>
 | |
| +#include <linux/time.h>
 | |
| +#include <linux/timex.h>
 | |
| +#include <linux/timekeeping.h>
 | |
| +#include <linux/string.h>
 | |
| +#include <linux/delay.h>
 | |
| +#include <linux/platform_device.h>
 | |
| +#include <linux/irq.h>
 | |
| +#include <linux/spinlock.h>
 | |
| +#include <media/lirc.h>
 | |
| +#include <media/lirc_dev.h>
 | |
| +#include <linux/gpio.h>
 | |
| +#include <linux/of_platform.h>
 | |
| +#include <linux/platform_data/bcm2708.h>
 | |
| +
 | |
| +#define LIRC_DRIVER_NAME "lirc_rpi"
 | |
| +#define RBUF_LEN 256
 | |
| +#define LIRC_TRANSMITTER_LATENCY 50
 | |
| +
 | |
| +#ifndef MAX_UDELAY_MS
 | |
| +#define MAX_UDELAY_US 5000
 | |
| +#else
 | |
| +#define MAX_UDELAY_US (MAX_UDELAY_MS*1000)
 | |
| +#endif
 | |
| +
 | |
| +#define dprintk(fmt, args...)					\
 | |
| +	do {							\
 | |
| +		if (debug)					\
 | |
| +			printk(KERN_DEBUG LIRC_DRIVER_NAME ": "	\
 | |
| +			       fmt, ## args);			\
 | |
| +	} while (0)
 | |
| +
 | |
| +/* module parameters */
 | |
| +
 | |
| +/* set the default GPIO input pin */
 | |
| +static int gpio_in_pin = 18;
 | |
| +/* set the default pull behaviour for input pin */
 | |
| +static int gpio_in_pull = BCM2708_PULL_DOWN;
 | |
| +/* set the default GPIO output pin */
 | |
| +static int gpio_out_pin = 17;
 | |
| +/* enable debugging messages */
 | |
| +static bool debug;
 | |
| +/* -1 = auto, 0 = active high, 1 = active low */
 | |
| +static int sense = -1;
 | |
| +/* use softcarrier by default */
 | |
| +static bool softcarrier = 1;
 | |
| +/* 0 = do not invert output, 1 = invert output */
 | |
| +static bool invert = 0;
 | |
| +
 | |
| +struct gpio_chip *gpiochip;
 | |
| +static int irq_num;
 | |
| +static int auto_sense = 1;
 | |
| +
 | |
| +/* forward declarations */
 | |
| +static long send_pulse(unsigned long length);
 | |
| +static void send_space(long length);
 | |
| +static void lirc_rpi_exit(void);
 | |
| +
 | |
| +static struct platform_device *lirc_rpi_dev;
 | |
| +static struct timeval lasttv = { 0, 0 };
 | |
| +static struct lirc_buffer rbuf;
 | |
| +static spinlock_t lock;
 | |
| +
 | |
| +/* initialized/set in init_timing_params() */
 | |
| +static unsigned int freq = 38000;
 | |
| +static unsigned int duty_cycle = 50;
 | |
| +static unsigned long period;
 | |
| +static unsigned long pulse_width;
 | |
| +static unsigned long space_width;
 | |
| +
 | |
| +static void safe_udelay(unsigned long usecs)
 | |
| +{
 | |
| +	while (usecs > MAX_UDELAY_US) {
 | |
| +		udelay(MAX_UDELAY_US);
 | |
| +		usecs -= MAX_UDELAY_US;
 | |
| +	}
 | |
| +	udelay(usecs);
 | |
| +}
 | |
| +
 | |
| +static unsigned long read_current_us(void)
 | |
| +{
 | |
| +	struct timespec now;
 | |
| +	getnstimeofday(&now);
 | |
| +	return (now.tv_sec * 1000000) + (now.tv_nsec/1000);
 | |
| +}
 | |
| +
 | |
| +static int init_timing_params(unsigned int new_duty_cycle,
 | |
| +	unsigned int new_freq)
 | |
| +{
 | |
| +	if (1000 * 1000000L / new_freq * new_duty_cycle / 100 <=
 | |
| +	    LIRC_TRANSMITTER_LATENCY)
 | |
| +		return -EINVAL;
 | |
| +	if (1000 * 1000000L / new_freq * (100 - new_duty_cycle) / 100 <=
 | |
| +	    LIRC_TRANSMITTER_LATENCY)
 | |
| +		return -EINVAL;
 | |
| +	duty_cycle = new_duty_cycle;
 | |
| +	freq = new_freq;
 | |
| +	period = 1000 * 1000000L / freq;
 | |
| +	pulse_width = period * duty_cycle / 100;
 | |
| +	space_width = period - pulse_width;
 | |
| +	dprintk("in init_timing_params, freq=%d pulse=%ld, "
 | |
| +		"space=%ld\n", freq, pulse_width, space_width);
 | |
| +	return 0;
 | |
| +}
 | |
| +
 | |
| +static long send_pulse_softcarrier(unsigned long length)
 | |
| +{
 | |
| +	int flag;
 | |
| +	unsigned long actual, target;
 | |
| +	unsigned long actual_us, initial_us, target_us;
 | |
| +
 | |
| +	length *= 1000;
 | |
| +
 | |
| +	actual = 0; target = 0; flag = 0;
 | |
| +	actual_us = read_current_us();
 | |
| +
 | |
| +	while (actual < length) {
 | |
| +		if (flag) {
 | |
| +			gpiochip->set(gpiochip, gpio_out_pin, invert);
 | |
| +			target += space_width;
 | |
| +		} else {
 | |
| +			gpiochip->set(gpiochip, gpio_out_pin, !invert);
 | |
| +			target += pulse_width;
 | |
| +		}
 | |
| +		initial_us = actual_us;
 | |
| +		target_us = actual_us + (target - actual) / 1000;
 | |
| +		/*
 | |
| +		 * Note - we've checked in ioctl that the pulse/space
 | |
| +		 * widths are big enough so that d is > 0
 | |
| +		 */
 | |
| +		if  ((int)(target_us - actual_us) > 0)
 | |
| +			udelay(target_us - actual_us);
 | |
| +		actual_us = read_current_us();
 | |
| +		actual += (actual_us - initial_us) * 1000;
 | |
| +		flag = !flag;
 | |
| +	}
 | |
| +	return (actual-length) / 1000;
 | |
| +}
 | |
| +
 | |
| +static long send_pulse(unsigned long length)
 | |
| +{
 | |
| +	if (length <= 0)
 | |
| +		return 0;
 | |
| +
 | |
| +	if (softcarrier) {
 | |
| +		return send_pulse_softcarrier(length);
 | |
| +	} else {
 | |
| +		gpiochip->set(gpiochip, gpio_out_pin, !invert);
 | |
| +		safe_udelay(length);
 | |
| +		return 0;
 | |
| +	}
 | |
| +}
 | |
| +
 | |
| +static void send_space(long length)
 | |
| +{
 | |
| +	gpiochip->set(gpiochip, gpio_out_pin, invert);
 | |
| +	if (length <= 0)
 | |
| +		return;
 | |
| +	safe_udelay(length);
 | |
| +}
 | |
| +
 | |
| +static void rbwrite(int l)
 | |
| +{
 | |
| +	if (lirc_buffer_full(&rbuf)) {
 | |
| +		/* no new signals will be accepted */
 | |
| +		dprintk("Buffer overrun\n");
 | |
| +		return;
 | |
| +	}
 | |
| +	lirc_buffer_write(&rbuf, (void *)&l);
 | |
| +}
 | |
| +
 | |
| +static void frbwrite(int l)
 | |
| +{
 | |
| +	/* simple noise filter */
 | |
| +	static int pulse, space;
 | |
| +	static unsigned int ptr;
 | |
| +
 | |
| +	if (ptr > 0 && (l & PULSE_BIT)) {
 | |
| +		pulse += l & PULSE_MASK;
 | |
| +		if (pulse > 250) {
 | |
| +			rbwrite(space);
 | |
| +			rbwrite(pulse | PULSE_BIT);
 | |
| +			ptr = 0;
 | |
| +			pulse = 0;
 | |
| +		}
 | |
| +		return;
 | |
| +	}
 | |
| +	if (!(l & PULSE_BIT)) {
 | |
| +		if (ptr == 0) {
 | |
| +			if (l > 20000) {
 | |
| +				space = l;
 | |
| +				ptr++;
 | |
| +				return;
 | |
| +			}
 | |
| +		} else {
 | |
| +			if (l > 20000) {
 | |
| +				space += pulse;
 | |
| +				if (space > PULSE_MASK)
 | |
| +					space = PULSE_MASK;
 | |
| +				space += l;
 | |
| +				if (space > PULSE_MASK)
 | |
| +					space = PULSE_MASK;
 | |
| +				pulse = 0;
 | |
| +				return;
 | |
| +			}
 | |
| +			rbwrite(space);
 | |
| +			rbwrite(pulse | PULSE_BIT);
 | |
| +			ptr = 0;
 | |
| +			pulse = 0;
 | |
| +		}
 | |
| +	}
 | |
| +	rbwrite(l);
 | |
| +}
 | |
| +
 | |
| +static irqreturn_t irq_handler(int i, void *blah, struct pt_regs *regs)
 | |
| +{
 | |
| +	struct timeval tv;
 | |
| +	long deltv;
 | |
| +	int data;
 | |
| +	int signal;
 | |
| +
 | |
| +	/* use the GPIO signal level */
 | |
| +	signal = gpiochip->get(gpiochip, gpio_in_pin);
 | |
| +
 | |
| +	if (sense != -1) {
 | |
| +		/* get current time */
 | |
| +		do_gettimeofday(&tv);
 | |
| +
 | |
| +		/* calc time since last interrupt in microseconds */
 | |
| +		deltv = tv.tv_sec-lasttv.tv_sec;
 | |
| +		if (tv.tv_sec < lasttv.tv_sec ||
 | |
| +		    (tv.tv_sec == lasttv.tv_sec &&
 | |
| +		     tv.tv_usec < lasttv.tv_usec)) {
 | |
| +			printk(KERN_WARNING LIRC_DRIVER_NAME
 | |
| +			       ": AIEEEE: your clock just jumped backwards\n");
 | |
| +			printk(KERN_WARNING LIRC_DRIVER_NAME
 | |
| +			       ": %d %d %lx %lx %lx %lx\n", signal, sense,
 | |
| +			       tv.tv_sec, lasttv.tv_sec,
 | |
| +			       tv.tv_usec, lasttv.tv_usec);
 | |
| +			data = PULSE_MASK;
 | |
| +		} else if (deltv > 15) {
 | |
| +			data = PULSE_MASK; /* really long time */
 | |
| +			if (!(signal^sense)) {
 | |
| +				/* sanity check */
 | |
| +				printk(KERN_DEBUG LIRC_DRIVER_NAME
 | |
| +				       ": AIEEEE: %d %d %lx %lx %lx %lx\n",
 | |
| +				       signal, sense, tv.tv_sec, lasttv.tv_sec,
 | |
| +				       tv.tv_usec, lasttv.tv_usec);
 | |
| +				/*
 | |
| +				 * detecting pulse while this
 | |
| +				 * MUST be a space!
 | |
| +				 */
 | |
| +				if (auto_sense) {
 | |
| +					sense = sense ? 0 : 1;
 | |
| +				}
 | |
| +			}
 | |
| +		} else {
 | |
| +			data = (int) (deltv*1000000 +
 | |
| +				      (tv.tv_usec - lasttv.tv_usec));
 | |
| +		}
 | |
| +		frbwrite(signal^sense ? data : (data|PULSE_BIT));
 | |
| +		lasttv = tv;
 | |
| +		wake_up_interruptible(&rbuf.wait_poll);
 | |
| +	}
 | |
| +
 | |
| +	return IRQ_HANDLED;
 | |
| +}
 | |
| +
 | |
| +static int is_right_chip(struct gpio_chip *chip, void *data)
 | |
| +{
 | |
| +	dprintk("is_right_chip %s %d\n", chip->label, strcmp(data, chip->label));
 | |
| +
 | |
| +	if (strcmp(data, chip->label) == 0)
 | |
| +		return 1;
 | |
| +	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;
 | |
| +	struct device_node *node;
 | |
| +
 | |
| +	node = lirc_rpi_dev->dev.of_node;
 | |
| +
 | |
| +	gpiochip = gpiochip_find("bcm2708_gpio", is_right_chip);
 | |
| +
 | |
| +	/*
 | |
| +	 * 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");
 | |
| +			return -EINVAL;
 | |
| +		}
 | |
| +
 | |
| +		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 {
 | |
| +		return -EINVAL;
 | |
| +	}
 | |
| +
 | |
| +	gpiochip->set(gpiochip, gpio_out_pin, invert);
 | |
| +
 | |
| +	irq_num = gpiochip->to_irq(gpiochip, gpio_in_pin);
 | |
| +	dprintk("to_irq %d\n", irq_num);
 | |
| +
 | |
| +	/* if pin is high, then this must be an active low receiver. */
 | |
| +	if (sense == -1) {
 | |
| +		/* wait 1/2 sec for the power supply */
 | |
| +		msleep(500);
 | |
| +
 | |
| +		/*
 | |
| +		 * probe 9 times every 0.04s, collect "votes" for
 | |
| +		 * active high/low
 | |
| +		 */
 | |
| +		nlow = 0;
 | |
| +		nhigh = 0;
 | |
| +		for (i = 0; i < 9; i++) {
 | |
| +			if (gpiochip->get(gpiochip, gpio_in_pin))
 | |
| +				nlow++;
 | |
| +			else
 | |
| +				nhigh++;
 | |
| +			msleep(40);
 | |
| +		}
 | |
| +		sense = (nlow >= nhigh ? 1 : 0);
 | |
| +		printk(KERN_INFO LIRC_DRIVER_NAME
 | |
| +		       ": auto-detected active %s receiver on GPIO pin %d\n",
 | |
| +		       sense ? "low" : "high", gpio_in_pin);
 | |
| +	} else {
 | |
| +		printk(KERN_INFO LIRC_DRIVER_NAME
 | |
| +		       ": manually using active %s receiver on GPIO pin %d\n",
 | |
| +		       sense ? "low" : "high", gpio_in_pin);
 | |
| +		auto_sense = 0;
 | |
| +	}
 | |
| +
 | |
| +	return 0;
 | |
| +}
 | |
| +
 | |
| +// called when the character device is opened
 | |
| +static int set_use_inc(void *data)
 | |
| +{
 | |
| +	int result;
 | |
| +
 | |
| +	/* initialize timestamp */
 | |
| +	do_gettimeofday(&lasttv);
 | |
| +
 | |
| +	result = request_irq(irq_num,
 | |
| +			     (irq_handler_t) irq_handler,
 | |
| +			     IRQ_TYPE_EDGE_RISING | IRQ_TYPE_EDGE_FALLING,
 | |
| +			     LIRC_DRIVER_NAME, (void*) 0);
 | |
| +
 | |
| +	switch (result) {
 | |
| +	case -EBUSY:
 | |
| +		printk(KERN_ERR LIRC_DRIVER_NAME
 | |
| +		       ": IRQ %d is busy\n",
 | |
| +		       irq_num);
 | |
| +		return -EBUSY;
 | |
| +	case -EINVAL:
 | |
| +		printk(KERN_ERR LIRC_DRIVER_NAME
 | |
| +		       ": Bad irq number or handler\n");
 | |
| +		return -EINVAL;
 | |
| +	default:
 | |
| +		dprintk("Interrupt %d obtained\n",
 | |
| +			irq_num);
 | |
| +		break;
 | |
| +	};
 | |
| +
 | |
| +	/* initialize pulse/space widths */
 | |
| +	init_timing_params(duty_cycle, freq);
 | |
| +
 | |
| +	return 0;
 | |
| +}
 | |
| +
 | |
| +static void set_use_dec(void *data)
 | |
| +{
 | |
| +	/* GPIO Pin Falling/Rising Edge Detect Disable */
 | |
| +	irq_set_irq_type(irq_num, 0);
 | |
| +	disable_irq(irq_num);
 | |
| +
 | |
| +	free_irq(irq_num, (void *) 0);
 | |
| +
 | |
| +	dprintk(KERN_INFO LIRC_DRIVER_NAME
 | |
| +		": freed IRQ %d\n", irq_num);
 | |
| +}
 | |
| +
 | |
| +static ssize_t lirc_write(struct file *file, const char *buf,
 | |
| +	size_t n, loff_t *ppos)
 | |
| +{
 | |
| +	int i, count;
 | |
| +	unsigned long flags;
 | |
| +	long delta = 0;
 | |
| +	int *wbuf;
 | |
| +
 | |
| +	count = n / sizeof(int);
 | |
| +	if (n % sizeof(int) || count % 2 == 0)
 | |
| +		return -EINVAL;
 | |
| +	wbuf = memdup_user(buf, n);
 | |
| +	if (IS_ERR(wbuf))
 | |
| +		return PTR_ERR(wbuf);
 | |
| +	spin_lock_irqsave(&lock, flags);
 | |
| +
 | |
| +	for (i = 0; i < count; i++) {
 | |
| +		if (i%2)
 | |
| +			send_space(wbuf[i] - delta);
 | |
| +		else
 | |
| +			delta = send_pulse(wbuf[i]);
 | |
| +	}
 | |
| +	gpiochip->set(gpiochip, gpio_out_pin, invert);
 | |
| +
 | |
| +	spin_unlock_irqrestore(&lock, flags);
 | |
| +	kfree(wbuf);
 | |
| +	return n;
 | |
| +}
 | |
| +
 | |
| +static long lirc_ioctl(struct file *filep, unsigned int cmd, unsigned long arg)
 | |
| +{
 | |
| +	int result;
 | |
| +	__u32 value;
 | |
| +
 | |
| +	switch (cmd) {
 | |
| +	case LIRC_GET_SEND_MODE:
 | |
| +		return -ENOIOCTLCMD;
 | |
| +		break;
 | |
| +
 | |
| +	case LIRC_SET_SEND_MODE:
 | |
| +		result = get_user(value, (__u32 *) arg);
 | |
| +		if (result)
 | |
| +			return result;
 | |
| +		/* only LIRC_MODE_PULSE supported */
 | |
| +		if (value != LIRC_MODE_PULSE)
 | |
| +			return -ENOSYS;
 | |
| +		break;
 | |
| +
 | |
| +	case LIRC_GET_LENGTH:
 | |
| +		return -ENOSYS;
 | |
| +		break;
 | |
| +
 | |
| +	case LIRC_SET_SEND_DUTY_CYCLE:
 | |
| +		dprintk("SET_SEND_DUTY_CYCLE\n");
 | |
| +		result = get_user(value, (__u32 *) arg);
 | |
| +		if (result)
 | |
| +			return result;
 | |
| +		if (value <= 0 || value > 100)
 | |
| +			return -EINVAL;
 | |
| +		return init_timing_params(value, freq);
 | |
| +		break;
 | |
| +
 | |
| +	case LIRC_SET_SEND_CARRIER:
 | |
| +		dprintk("SET_SEND_CARRIER\n");
 | |
| +		result = get_user(value, (__u32 *) arg);
 | |
| +		if (result)
 | |
| +			return result;
 | |
| +		if (value > 500000 || value < 20000)
 | |
| +			return -EINVAL;
 | |
| +		return init_timing_params(duty_cycle, value);
 | |
| +		break;
 | |
| +
 | |
| +	default:
 | |
| +		return lirc_dev_fop_ioctl(filep, cmd, arg);
 | |
| +	}
 | |
| +	return 0;
 | |
| +}
 | |
| +
 | |
| +static const struct file_operations lirc_fops = {
 | |
| +	.owner		= THIS_MODULE,
 | |
| +	.write		= lirc_write,
 | |
| +	.unlocked_ioctl	= lirc_ioctl,
 | |
| +	.read		= lirc_dev_fop_read,
 | |
| +	.poll		= lirc_dev_fop_poll,
 | |
| +	.open		= lirc_dev_fop_open,
 | |
| +	.release	= lirc_dev_fop_close,
 | |
| +	.llseek		= no_llseek,
 | |
| +};
 | |
| +
 | |
| +static struct lirc_driver driver = {
 | |
| +	.name		= LIRC_DRIVER_NAME,
 | |
| +	.minor		= -1,
 | |
| +	.code_length	= 1,
 | |
| +	.sample_rate	= 0,
 | |
| +	.data		= NULL,
 | |
| +	.add_to_buf	= NULL,
 | |
| +	.rbuf		= &rbuf,
 | |
| +	.set_use_inc	= set_use_inc,
 | |
| +	.set_use_dec	= set_use_dec,
 | |
| +	.fops		= &lirc_fops,
 | |
| +	.dev		= NULL,
 | |
| +	.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. */
 | |
| +	result = lirc_buffer_init(&rbuf, sizeof(int), RBUF_LEN);
 | |
| +	if (result < 0)
 | |
| +		return -ENOMEM;
 | |
| +
 | |
| +	result = platform_driver_register(&lirc_rpi_driver);
 | |
| +	if (result) {
 | |
| +		printk(KERN_ERR LIRC_DRIVER_NAME
 | |
| +		       ": lirc register returned %d\n", result);
 | |
| +		goto exit_buffer_free;
 | |
| +	}
 | |
| +
 | |
| +	node = of_find_compatible_node(NULL, NULL,
 | |
| +				       lirc_rpi_of_match[0].compatible);
 | |
| +
 | |
| +	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;
 | |
| +
 | |
| +	exit_device_put:
 | |
| +	platform_device_put(lirc_rpi_dev);
 | |
| +
 | |
| +	exit_driver_unregister:
 | |
| +	platform_driver_unregister(&lirc_rpi_driver);
 | |
| +
 | |
| +	exit_buffer_free:
 | |
| +	lirc_buffer_free(&rbuf);
 | |
| +
 | |
| +	return result;
 | |
| +}
 | |
| +
 | |
| +static void lirc_rpi_exit(void)
 | |
| +{
 | |
| +	if (!lirc_rpi_dev->dev.of_node)
 | |
| +		platform_device_unregister(lirc_rpi_dev);
 | |
| +	platform_driver_unregister(&lirc_rpi_driver);
 | |
| +	lirc_buffer_free(&rbuf);
 | |
| +}
 | |
| +
 | |
| +static int __init lirc_rpi_init_module(void)
 | |
| +{
 | |
| +	int result;
 | |
| +
 | |
| +	result = lirc_rpi_init();
 | |
| +	if (result)
 | |
| +		return result;
 | |
| +
 | |
| +	result = init_port();
 | |
| +	if (result < 0)
 | |
| +		goto exit_rpi;
 | |
| +
 | |
| +	driver.features = LIRC_CAN_SET_SEND_DUTY_CYCLE |
 | |
| +			  LIRC_CAN_SET_SEND_CARRIER |
 | |
| +			  LIRC_CAN_SEND_PULSE |
 | |
| +			  LIRC_CAN_REC_MODE2;
 | |
| +
 | |
| +	driver.dev = &lirc_rpi_dev->dev;
 | |
| +	driver.minor = lirc_register_driver(&driver);
 | |
| +
 | |
| +	if (driver.minor < 0) {
 | |
| +		printk(KERN_ERR LIRC_DRIVER_NAME
 | |
| +		       ": device registration failed with %d\n", result);
 | |
| +		result = -EIO;
 | |
| +		goto exit_rpi;
 | |
| +	}
 | |
| +
 | |
| +	printk(KERN_INFO LIRC_DRIVER_NAME ": driver registered!\n");
 | |
| +
 | |
| +	return 0;
 | |
| +
 | |
| +	exit_rpi:
 | |
| +	lirc_rpi_exit();
 | |
| +
 | |
| +	return result;
 | |
| +}
 | |
| +
 | |
| +static void __exit lirc_rpi_exit_module(void)
 | |
| +{
 | |
| +	lirc_unregister_driver(driver.minor);
 | |
| +
 | |
| +	gpio_free(gpio_out_pin);
 | |
| +	gpio_free(gpio_in_pin);
 | |
| +
 | |
| +	lirc_rpi_exit();
 | |
| +
 | |
| +	printk(KERN_INFO LIRC_DRIVER_NAME ": cleaned up module\n");
 | |
| +}
 | |
| +
 | |
| +module_init(lirc_rpi_init_module);
 | |
| +module_exit(lirc_rpi_exit_module);
 | |
| +
 | |
| +MODULE_DESCRIPTION("Infra-red receiver and blaster driver for Raspberry Pi GPIO.");
 | |
| +MODULE_AUTHOR("Aron Robert Szabo <aron@reon.hu>");
 | |
| +MODULE_AUTHOR("Michael Bishop <cleverca22@gmail.com>");
 | |
| +MODULE_LICENSE("GPL");
 | |
| +
 | |
| +module_param(gpio_out_pin, int, S_IRUGO);
 | |
| +MODULE_PARM_DESC(gpio_out_pin, "GPIO output/transmitter pin number of the BCM"
 | |
| +		 " processor. (default 17");
 | |
| +
 | |
| +module_param(gpio_in_pin, int, S_IRUGO);
 | |
| +MODULE_PARM_DESC(gpio_in_pin, "GPIO input pin number of the BCM processor."
 | |
| +		 " (default 18");
 | |
| +
 | |
| +module_param(gpio_in_pull, int, S_IRUGO);
 | |
| +MODULE_PARM_DESC(gpio_in_pull, "GPIO input pin pull configuration."
 | |
| +		 " (0 = off, 1 = up, 2 = down, default down)");
 | |
| +
 | |
| +module_param(sense, int, S_IRUGO);
 | |
| +MODULE_PARM_DESC(sense, "Override autodetection of IR receiver circuit"
 | |
| +		 " (0 = active high, 1 = active low )");
 | |
| +
 | |
| +module_param(softcarrier, bool, S_IRUGO);
 | |
| +MODULE_PARM_DESC(softcarrier, "Software carrier (0 = off, 1 = on, default on)");
 | |
| +
 | |
| +module_param(invert, bool, S_IRUGO);
 | |
| +MODULE_PARM_DESC(invert, "Invert output (0 = off, 1 = on, default off");
 | |
| +
 | |
| +module_param(debug, bool, S_IRUGO | S_IWUSR);
 | |
| +MODULE_PARM_DESC(debug, "Enable debugging messages");
 | |
| --- /dev/null
 | |
| +++ b/include/linux/platform_data/bcm2708.h
 | |
| @@ -0,0 +1,23 @@
 | |
| +/*
 | |
| + * include/linux/platform_data/bcm2708.h
 | |
| + *
 | |
| + * 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.
 | |
| + *
 | |
| + * (C) 2014 Julian Scheel <julian@jusst.de>
 | |
| + *
 | |
| + */
 | |
| +#ifndef __BCM2708_H_
 | |
| +#define __BCM2708_H_
 | |
| +
 | |
| +typedef enum {
 | |
| +	BCM2708_PULL_OFF,
 | |
| +	BCM2708_PULL_UP,
 | |
| +	BCM2708_PULL_DOWN
 | |
| +} bcm2708_gpio_pull_t;
 | |
| +
 | |
| +extern int bcm2708_gpio_setpull(struct gpio_chip *gc, unsigned offset,
 | |
| +		bcm2708_gpio_pull_t value);
 | |
| +
 | |
| +#endif
 |