/*
* Artec-3 general port I/O device
*
* Copyright (c) 2007 Axis Communications AB
*
* Authors: Bjorn Wesen (initial version)
* Ola Knutsson (LED handling)
* Johan Adolfsson (read/set directions, write, port G,
* port to ETRAX FS.
* Ricard Wanderlof (PWM for Artpec-3)
*
*/
#include <linux/module.h>
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/ioport.h>
#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/string.h>
#include <linux/poll.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/spinlock.h>
#include <linux/smp_lock.h>
#include <asm/etraxgpio.h>
#include <hwregs/reg_map.h>
#include <hwregs/reg_rdwr.h>
#include <hwregs/gio_defs.h>
#include <hwregs/intr_vect_defs.h>
#include <asm/io.h>
#include <asm/system.h>
#include <asm/irq.h>
#include <asm/arch/mach/pinmux.h>
#ifdef CONFIG_ETRAX_VIRTUAL_GPIO
#include "../i2c.h"
#define VIRT_I2C_ADDR 0x40
#endif
/* The following gio ports on ARTPEC-3 is available:
* pa 32 bits
* pb 32 bits
* pc 16 bits
* each port has a rw_px_dout, r_px_din and rw_px_oe register.
*/
#define GPIO_MAJOR 120 /* experimental MAJOR number */
#define I2C_INTERRUPT_BITS 0x300 /* i2c0_done and i2c1_done bits */
#define D(x)
#if 0
static int dp_cnt;
#define DP(x) \
do { \
dp_cnt++; \
if (dp_cnt % 1000 == 0) \
x; \
} while (0)
#else
#define DP(x)
#endif
static char gpio_name[] = "etrax gpio";
#ifdef CONFIG_ETRAX_VIRTUAL_GPIO
static int virtual_gpio_ioctl(struct file *file, unsigned int cmd,
unsigned long arg);
#endif
static int gpio_ioctl(struct inode *inode, struct file *file,
unsigned int cmd, unsigned long arg);
static ssize_t gpio_write(struct file *file, const char __user *buf,
size_t count, loff_t *off);
static int gpio_open(struct inode *inode, struct file *filp);
static int gpio_release(struct inode *inode, struct file *filp);
static unsigned int gpio_poll(struct file *filp,
struct poll_table_struct *wait);
/* private data per open() of this driver */
struct gpio_private {
struct gpio_private *next;
/* The IO_CFG_WRITE_MODE_VALUE only support 8 bits: */
unsigned char clk_mask;
unsigned char data_mask;
unsigned char write_msb;
unsigned char pad1;
/* These fields are generic */
unsigned long highalarm, lowalarm;
wait_queue_head_t alarm_wq;
int minor;
};
static void gpio_set_alarm(struct gpio_private *priv);
static int gpio_leds_ioctl(unsigned int cmd, unsigned long arg);
static int gpio_pwm_ioctl(struct gpio_private *priv, unsigned int cmd,
unsigned long arg);
/* linked list of alarms to check for */
static struct gpio_private *alarmlist;
static int wanted_interrupts;
static DEFINE_SPINLOCK(gpio_lock);
#define NUM_PORTS (GPIO_MINOR_LAST+1)
#define GIO_REG_RD_ADDR(reg) \
(unsigned long *)(regi_gio + REG_RD_ADDR_gio_##reg)
#define GIO_REG_WR_ADDR(reg) \
(unsigned long *)(regi_gio + REG_WR_ADDR_gio_##reg)
static unsigned long led_dummy;
static unsigned long port_d_dummy; /* Only input on Artpec-3 */
#ifdef CONFIG_ETRAX_VIRTUAL_GPIO
static unsigned long port_e_dummy; /* Non existent on Artpec-3 */
static unsigned long virtual_dummy;
static unsigned long virtual_rw_pv_oe = CONFIG_ETRAX_DEF_GIO_PV_OE;
static unsigned short cached_virtual_gpio_read;
#endif
static unsigned long *data_out[NUM_PORTS] = {
GIO_REG_WR_ADDR(rw_pa_dout),
GIO_REG_WR_ADDR(rw_pb_dout),
&led_dummy,
GIO_REG_WR_ADDR(rw_pc_dout),
&port_d_dummy,
#ifdef CONFIG_ETRAX_VIRTUAL_GPIO
&port_e_dummy,
&virtual_dummy,
#endif
};
static unsigned long *data_in[NUM_PORTS] = {
GIO_REG_RD_ADDR(r_pa_din),
GIO_REG_RD_ADDR(r_pb_din),
&led_dummy,
GIO_REG_RD_ADDR(r_pc_din),
GIO_REG_RD_ADDR(r_pd_din),
#ifdef CONFIG_ETRAX_VIRTUAL_GPIO
&port_e_dummy,
&virtual_dummy,
#endif
};
static unsigned long changeable_dir[NUM_PORTS] = {
CONFIG_ETRAX_PA_CHANGEABLE_DIR,
CONFIG_ETRAX_PB_CHANGEABLE_DIR,
0,
CONFIG_ETRAX_PC_CHANGEABLE_DIR,
0,
#ifdef CONFIG_ETRAX_VIRTUAL_GPIO
0,
CONFIG_ETRAX_PV_CHANGEABLE_DIR,
#endif
};
static unsigned long changeable_bits[NUM_PORTS] = {
CONFIG_ETRAX_PA_CHANGEABLE_BITS,
CONFIG_ETRAX_PB_CHANGEABLE_BITS,
0,
CONFIG_ETRAX_PC_CHANGEABLE_BITS,
0,
#ifdef CONFIG_ETRAX_VIRTUAL_GPIO
0,
CONFIG_ETRAX_PV_CHANGEABLE_BITS,
#endif
};
static unsigned long *dir_oe[NUM_PORTS] = {
GIO_REG_WR_ADDR(rw_pa_oe),
GIO_REG_WR_ADDR(rw_pb_oe),
&led_dummy,
GIO_REG_WR_ADDR(rw_pc_oe),
&port_d_dummy,
#ifdef CONFIG_ETRAX_VIRTUAL_GPIO
&port_e_dummy,
&virtual_rw_pv_oe,
#endif
};
static void gpio_set_alarm(struct gpio_private *priv)
{
int bit;
int intr_cfg;
int mask;
int pins;
unsigned long flags;
spin_lock_irqsave(&gpio_lock, flags);
intr_cfg = REG_RD_INT(gio, regi_gio,