misc: pn544: Add PN544 NFC sensor driver

HTC kernel version: evitaul-jb-crc-3.4.10-ec474a3

Change-Id: I9f0cc765dbc3a5be6c29c24f2733eb3632c87044
diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
index f310dac..d0236c2 100644
--- a/drivers/misc/Kconfig
+++ b/drivers/misc/Kconfig
@@ -701,6 +701,12 @@
 	help
 	  A1028 Voice Processor driver implemented by HTC.
 
+config SENSORS_NFC_PN544
+	tristate "PN544 NFC Sensor driver"
+	depends on I2C
+	help
+	  PN544 NFC driver.
+
 source "drivers/misc/c2port/Kconfig"
 source "drivers/misc/eeprom/Kconfig"
 source "drivers/misc/cb710/Kconfig"
diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
index a72ff1b..19e1cda 100644
--- a/drivers/misc/Makefile
+++ b/drivers/misc/Makefile
@@ -77,3 +77,4 @@
 obj-$(CONFIG_SLIMPORT_ANX7808)	+= slimport_anx7808/
 obj-$(CONFIG_CABLE_DETECT_8XXX)	+= cable_detect_8xxx.o
 obj-$(CONFIG_VP_A1028)		+= a1028.o
+obj-$(CONFIG_SENSORS_NFC_PN544)	+= pn544.o
diff --git a/drivers/misc/pn544.c b/drivers/misc/pn544.c
new file mode 100644
index 0000000..851cad7
--- /dev/null
+++ b/drivers/misc/pn544.c
@@ -0,0 +1,793 @@
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/fs.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/list.h>
+#include <linux/i2c.h>
+#include <linux/irq.h>
+#include <linux/jiffies.h>
+#include <linux/uaccess.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/platform_device.h>
+#include <linux/gpio.h>
+#include <linux/miscdevice.h>
+#include <linux/spinlock.h>
+#include <linux/wakelock.h>
+#include <linux/pn544.h>
+#include <mach/board_htc.h>
+
+int is_debug = 0;
+
+#define DBUF(buff,count) \
+	if (is_debug) \
+		for (i = 0; i < count; i++) \
+			printk(KERN_DEBUG "[NFC] %s : [%d] = 0x%x\n", \
+				__func__, i, buff[i]);
+
+#define D(x...)	\
+	if (is_debug)	\
+		printk(KERN_DEBUG "[NFC] " x)
+#define I(x...) printk(KERN_INFO "[NFC] " x)
+#define E(x...) printk(KERN_ERR "[NFC] [Err] " x)
+
+#define MAX_BUFFER_SIZE	512
+#define PN544_RESET_CMD 	0
+#define PN544_DOWNLOAD_CMD	1
+
+#define I2C_RETRY_COUNT 10
+
+struct pn544_dev	{
+	struct class		*pn544_class;
+	struct device		*pn_dev;
+	wait_queue_head_t	read_wq;
+	struct mutex		read_mutex;
+	struct wake_lock io_wake_lock;
+	struct i2c_client	*client;
+	struct miscdevice	pn544_device;
+	unsigned int		irq_gpio;
+	bool			irq_enabled;
+	spinlock_t		irq_enabled_lock;
+	unsigned int 		ven_gpio;
+	unsigned int		ven_value;
+	unsigned int 		firm_gpio;
+	void (*gpio_init) (void);
+	unsigned int 		ven_enable;
+	int boot_mode;
+};
+
+struct pn544_dev *pn_info;
+int ignoreI2C = 0;
+
+static int pn544_RxData(uint8_t *rxData, int length)
+{
+	uint8_t loop_i;
+	struct pn544_dev *pni = pn_info;
+
+	struct i2c_msg msg[] = {
+		{
+		 .addr = pni->client->addr,
+		 .flags = I2C_M_RD,
+		 .len = length,
+		 .buf = rxData,
+		 },
+	};
+
+	D("%s: [addr=%x flag=%x len=%x]\n", __func__,
+		msg[0].addr, msg[0].flags, msg[0].len);
+
+	if (ignoreI2C) {
+		I("ignore pn544_RxData %d\n", ignoreI2C);
+		return 0;
+	}
+
+	rxData[0] = 0;
+
+	for (loop_i = 0; loop_i < I2C_RETRY_COUNT; loop_i++) {
+		D("%s: retry %d ........\n", __func__, loop_i);
+		if (i2c_transfer(pni->client->adapter, msg, 1) > 0)
+			break;
+
+		mdelay(10);
+	}
+
+	if (loop_i >= I2C_RETRY_COUNT) {
+		E("%s Error: retry over %d\n", __func__,
+			I2C_RETRY_COUNT);
+		return -EIO;
+	}
+
+	return 0;
+}
+
+static int pn544_TxData(uint8_t *txData, int length)
+{
+	uint8_t loop_i;
+	struct pn544_dev *pni = pn_info;
+	struct i2c_msg msg[] = {
+		{
+		 .addr = pni->client->addr,
+		 .flags = 0,
+		 .len = length,
+		 .buf = txData,
+		 },
+	};
+
+	D("%s: [addr=%x flag=%x len=%x]\n", __func__,
+		msg[0].addr, msg[0].flags, msg[0].len);
+
+	if (ignoreI2C) {
+		I("ignore pn544_TxData %d\n", ignoreI2C);
+		return 0;
+	}
+
+	for (loop_i = 0; loop_i < I2C_RETRY_COUNT; loop_i++) {
+		D("%s: retry %d ........\n", __func__, loop_i);
+		if (i2c_transfer(pni->client->adapter, msg, 1) > 0)
+			break;
+
+		msleep(10);
+	}
+
+	if (loop_i >= I2C_RETRY_COUNT) {
+		E("%s:  Error: retry over %d\n",
+			__func__, I2C_RETRY_COUNT);
+		return -EIO;
+	}
+
+	return 0;
+}
+
+static void pn544_disable_irq(struct pn544_dev *pn544_dev)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&pn544_dev->irq_enabled_lock, flags);
+	if (pn544_dev->irq_enabled) {
+		disable_irq_nosync(pn544_dev->client->irq);
+		pn544_dev->irq_enabled = false;
+	}
+	spin_unlock_irqrestore(&pn544_dev->irq_enabled_lock, flags);
+}
+
+static irqreturn_t pn544_dev_irq_handler(int irq, void *dev_id)
+{
+	struct pn544_dev *pn544_dev = dev_id;
+
+	pn544_disable_irq(pn544_dev);
+
+	
+	wake_up(&pn544_dev->read_wq);
+
+	return IRQ_HANDLED;
+}
+
+static void pn544_Enable(void)
+{
+	struct pn544_dev *pni = pn_info;
+	unsigned int set_value = pni->ven_enable;
+	I("%s: gpio=%d set_value=%d\n", __func__, pni->ven_gpio, set_value);
+
+	gpio_set_value(pni->ven_gpio, set_value);
+	pni->ven_value = 1;
+}
+
+static void pn544_Disable(void)
+{
+	struct pn544_dev *pni = pn_info;
+	unsigned int set_value = !pni->ven_enable;
+	I("%s: gpio=%d set_value=%d\n", __func__, pni->ven_gpio, set_value);
+
+	gpio_set_value(pni->ven_gpio, set_value);
+	pni->ven_value = 0;
+}
+
+
+static int pn544_isEn(void)
+{
+	struct pn544_dev *pni = pn_info;
+	
+	return pni->ven_value;
+}
+uint8_t read_buffer[MAX_BUFFER_SIZE];
+
+static ssize_t pn544_dev_read(struct file *filp, char __user *buf,
+		size_t count, loff_t *offset)
+{
+	struct pn544_dev *pni = pn_info;
+	int ret;
+	int val;
+	int i;
+	i = 0;
+
+	D("%s: start count = %u\n", __func__, count);
+
+	if (count > MAX_BUFFER_SIZE) {
+		E("%s : count =%d> MAX_BUFFER_SIZE\n", __func__, count);
+		count = MAX_BUFFER_SIZE;
+	}
+
+	val = gpio_get_value(pni->irq_gpio);
+
+	D("%s: reading %zu bytes, irq_gpio = %d\n",
+		__func__, count, val);
+
+	mutex_lock(&pni->read_mutex);
+
+	if (!gpio_get_value(pni->irq_gpio)) {
+		if (filp->f_flags & O_NONBLOCK) {
+			I("%s : f_flags & O_NONBLOCK read again\n", __func__);
+			ret = -EAGAIN;
+			goto fail;
+		}
+
+		pni->irq_enabled = true;
+		enable_irq(pni->client->irq);
+		D("%s: waiting read-event INT, because "
+			"irq_gpio = 0\n", __func__);
+		ret = wait_event_interruptible(pni->read_wq,
+				gpio_get_value(pni->irq_gpio));
+
+		pn544_disable_irq(pni);
+
+		D("%s : wait_event_interruptible done\n", __func__);
+
+		if (ret) {
+			I("pn544_dev_read wait_event_interruptible breaked ret=%d\n", ret);
+			goto fail;
+		}
+
+	}
+
+    wake_lock_timeout(&pni ->io_wake_lock, IO_WAKE_LOCK_TIMEOUT);
+	
+	memset(read_buffer, 0, MAX_BUFFER_SIZE);
+	ret = pn544_RxData(read_buffer, count);
+	mutex_unlock(&pni->read_mutex);
+
+	if (ret < 0) {
+		E("%s: i2c_master_recv returned %d\n", __func__, ret);
+		return ret;
+	}
+	if (ret > count) {
+		E("%s: received too many bytes from i2c (%d)\n",
+			__func__, ret);
+		return -EIO;
+	}
+
+	DBUF(read_buffer, count);
+
+	if (copy_to_user(buf, read_buffer, count)) {
+		E("%s : failed to copy to user space\n", __func__);
+		return -EFAULT;
+	}
+
+	D("%s done count = %u\n", __func__, count);
+	return count;
+
+fail:
+	mutex_unlock(&pni->read_mutex);
+	return ret;
+}
+
+static ssize_t pn544_dev_write(struct file *filp, const char __user *buf,
+		size_t count, loff_t *offset)
+{
+	struct pn544_dev *pni = pn_info;
+	char buffer[MAX_BUFFER_SIZE];
+	int ret;
+	int i;
+	i = 0;
+
+	D("%s: start count = %u\n", __func__, count);
+	wake_lock_timeout(&pni ->io_wake_lock, IO_WAKE_LOCK_TIMEOUT);
+
+	if (count > MAX_BUFFER_SIZE) {
+		E("%s : count =%d> MAX_BUFFER_SIZE\n", __func__, count);
+		count = MAX_BUFFER_SIZE;
+	}
+
+	D("%s : writing %zu bytes\n", __func__, count);
+
+	if (copy_from_user(buffer, buf, count)) {
+		E("%s : failed to copy from user space\n", __func__);
+		return -EFAULT;
+	}
+
+	DBUF(buffer, count);
+
+	
+	ret = pn544_TxData(buffer, count);
+	if (ret < 0) {
+		E("%s : i2c_master_send returned %d\n", __func__, ret);
+		ret = -EIO;
+	} else {
+		D("%s done count = %u\n", __func__, count);
+		return count;
+	}
+
+	return ret;
+}
+
+static int pn544_dev_open(struct inode *inode, struct file *filp)
+{
+	struct pn544_dev *pn544_dev = container_of(filp->private_data,
+						struct pn544_dev,
+						pn544_device);
+
+	filp->private_data = pn544_dev;
+	I("%s : major=%d, minor=%d\n", \
+		__func__, imajor(inode), iminor(inode));
+
+	return 0;
+}
+
+static long pn544_dev_ioctl(struct file *filp,
+		unsigned int cmd, unsigned long arg)
+{
+	struct pn544_dev *pni = pn_info;
+	uint8_t buffer[] = {0x05, 0xF9, 0x04, 0x00, 0xC3, 0xE5};
+
+	switch (cmd) {
+	case PN544_SET_PWR:
+		if (arg == 3) {
+			
+			I("%s Software reset\n", __func__);
+			if (pn544_TxData(buffer, 6) < 0)
+				E("%s, SW-Reset TxData error!\n", __func__);
+		} else if (arg == 2) {
+			I("%s power on with firmware\n", __func__);
+			pn544_Enable();
+			gpio_set_value(pni->firm_gpio, 1);
+			msleep(50);
+			pn544_Disable();
+			msleep(50);
+			pn544_Enable();
+			msleep(50);
+		} else if (arg == 1) {
+			
+			I("%s power on (delay50)\n", __func__);
+			gpio_set_value(pni->firm_gpio, 0);
+			pn544_Enable();
+			msleep(50);
+		} else  if (arg == 0) {
+			
+			I("%s power off (delay50)\n", __func__);
+			gpio_set_value(pni->firm_gpio, 0);
+			pn544_Disable();
+			msleep(50);
+		} else {
+			E("%s bad arg %lu\n", __func__, arg);
+			goto fail;
+		}
+		break;
+	default:
+		E("%s bad ioctl %u\n", __func__, cmd);
+		goto fail;
+	}
+
+	return 0;
+fail:
+	return -EINVAL;
+}
+
+static const struct file_operations pn544_dev_fops = {
+	.owner	= THIS_MODULE,
+	.llseek	= no_llseek,
+	.read	= pn544_dev_read,
+	.write	= pn544_dev_write,
+	.open	= pn544_dev_open,
+	.unlocked_ioctl  = pn544_dev_ioctl,
+};
+
+static ssize_t pn_temp1_show(struct device *dev,
+			struct device_attribute *attr, char *buf)
+{
+	int ret = 0;
+	int val = -1;
+	struct pn544_dev *pni = pn_info;
+	uint8_t buffer[MAX_BUFFER_SIZE];
+	int i = 0;
+
+	I("%s:\n", __func__);
+	val = gpio_get_value(pni->irq_gpio);
+
+	memset(buffer, 0, MAX_BUFFER_SIZE);
+#if 1
+	if (val == 1) {
+		ret = pn544_RxData(buffer, 33);
+		if (ret < 0) {
+			E("%s, i2c Rx error!\n", __func__);
+		} else {
+			for (i = 0; i < 10; i++)
+			I("%s : [%d] = 0x%x\n", __func__, i, buffer[i]);
+		}
+	} else {
+		E("%s, data not ready\n", __func__);
+	}
+#else
+	if (val != 1)
+		E("%s, ####### data not ready -> force to read!#########\n", __func__);
+
+	ret = pn544_RxData(buffer, 33);
+	if (ret < 0) {
+		E("%s, i2c Rx error!\n", __func__);
+	} else {
+		for (i = 0; i < 10; i++)
+		D("%s : [%d] = 0x%x\n", __func__, i, buffer[i]);
+	}
+#endif
+
+	ret = sprintf(buf, "GPIO INT = %d "
+		"Rx:ret=%d [0x%x, 0x%x, 0x%x, 0x%x]\n", val, ret, buffer[0], buffer[1],
+		buffer[2], buffer[3]);
+
+	return ret;
+}
+
+
+#define i2cw_size (20)
+static ssize_t pn_temp1_store(struct device *dev,
+				struct device_attribute *attr,
+				const char *buf, size_t count)
+{
+	int code = -1;
+	int ret = -1;
+	struct pn544_dev *pni = pn_info;
+	uint8_t buffer[] = {0x05, 0xF9, 0x04, 0x00, 0xC3, 0xE5};
+	
+	uint8_t i2cw[i2cw_size];
+	uint32_t scan_data = 0;
+	int i2cw_len = 0;
+	int i = 0;
+	char *ptr;
+
+	sscanf(buf, "%d", &code);
+
+	I("%s: irq = %d,  ven_gpio = %d,  firm_gpio = %d +\n", __func__, \
+		gpio_get_value(pni->irq_gpio), pn544_isEn(), \
+		gpio_get_value(pni->firm_gpio));
+	I("%s: store value = %d\n", __func__, code);
+
+	switch (code) {
+	case 1:
+			I("%s: case 1\n", __func__);
+			ret = pn544_TxData(buffer, 6);
+			if (ret < 0)
+				E("%s, i2c Tx error!\n", __func__);
+			break;
+	case 2:
+			I("%s: case 2 %d\n", __func__, pni->ven_gpio);
+			pn544_Disable();
+			break;
+	case 3:
+			I("%s: case 3 %d\n", __func__, pni->ven_gpio);
+			pn544_Enable();
+			break;
+	case 4:
+			I("%s: case 4 %d\n", __func__, pni->firm_gpio);
+			gpio_set_value(pni->firm_gpio, 0);
+			break;
+	case 5:
+			I("%s: case 5 %d\n", __func__, pni->firm_gpio);
+			gpio_set_value(pni->firm_gpio, 1);
+			break;
+	case 6:
+			memset(i2cw, 0, i2cw_size);
+			sscanf(buf, "%d %d", &code, &i2cw_len);
+			I("%s: case 6 i2cw_len=%u\n", __func__, i2cw_len);
+
+			ptr = strpbrk(buf, " ");	
+			if (ptr != NULL) {
+				for (i = 0 ; i <= i2cw_len ; i++) {
+					sscanf(ptr, "%x", &scan_data);
+					i2cw[i] = (uint8_t)scan_data;
+					I("%s: i2cw[%d]=%x\n", \
+						__func__, i, i2cw[i]);
+					ptr = strpbrk(++ptr, " ");
+					if (ptr == NULL)
+						break;
+				}
+
+				ret = pn544_TxData(i2cw, i2cw_len+1);
+				
+
+				if (ret < 0)
+					E("%s, i2c Tx error!\n", __func__);
+			} else {
+				I("%s: skip no data found\n", __func__);
+			}
+			break;
+	case 7:
+			I("%s: case 7 disable i2c\n", __func__);
+			ignoreI2C = 1;
+			break;
+	case 8:
+			I("%s: case 8 enable i2c\n", __func__);
+			ignoreI2C = 0;
+			break;
+	default:
+			E("%s: case default\n", __func__);
+			break;
+	}
+
+	I("%s: irq = %d,  ven_gpio = %d,  firm_gpio = %d -\n", __func__, \
+		gpio_get_value(pni->irq_gpio), pn544_isEn(), gpio_get_value(pni->firm_gpio));
+	return count;
+}
+
+static DEVICE_ATTR(pn_temp1, 0664, pn_temp1_show, pn_temp1_store);
+
+
+static ssize_t debug_enable_show(struct device *dev,
+			struct device_attribute *attr, char *buf)
+{
+	int ret = 0;
+	I("debug_enable_show\n");
+
+	ret = sprintf(buf, "is_debug=%d\n", is_debug);
+	return ret;
+}
+
+static ssize_t debug_enable_store(struct device *dev,
+				struct device_attribute *attr,
+				const char *buf, size_t count)
+{
+	sscanf(buf, "%d", &is_debug);
+	return count;
+}
+
+static DEVICE_ATTR(debug_enable, 0664, debug_enable_show, debug_enable_store);
+
+static int pn544_probe(struct i2c_client *client,
+		const struct i2c_device_id *id)
+{
+	int ret;
+	struct pn544_i2c_platform_data *platform_data;
+	struct pn544_dev *pni;
+
+	I("%s:\n", __func__);
+
+	platform_data = client->dev.platform_data;
+
+	if (platform_data == NULL) {
+		E("%s : nfc probe fail because platform_data \
+			is NULL\n", __func__);
+		return  -ENODEV;
+	}
+
+	if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+		E("%s : need I2C_FUNC_I2C\n", __func__);
+		return  -ENODEV;
+	}
+
+	
+	ret = gpio_request(platform_data->irq_gpio, "nfc_int");
+	if (ret) {
+		E("%s : request gpio%d fail\n",
+			__func__, platform_data->irq_gpio);
+		ret = -ENODEV;
+		goto err_exit;
+	}
+
+	
+	ret = gpio_request(platform_data->ven_gpio, "nfc_en");
+	if (ret) {
+		E("%s : request gpio %d fail\n",
+			__func__, platform_data->ven_gpio);
+		ret = -ENODEV;
+		goto err_request_gpio_ven;
+	}
+	
+
+	ret = gpio_request(platform_data->firm_gpio, "nfc_firm");
+	if (ret) {
+		E("%s : request gpio %d fail\n",
+			__func__, platform_data->firm_gpio);
+		ret = -ENODEV;
+		goto err_request_gpio_firm;
+	}
+
+	pni = kzalloc(sizeof(struct pn544_dev), GFP_KERNEL);
+	if (pni == NULL) {
+		dev_err(&client->dev, \
+				"pn544_probe : failed to allocate \
+				memory for module data\n");
+		ret = -ENOMEM;
+		goto err_exit;
+	}
+
+	pn_info = pni;
+
+	if (platform_data->gpio_init != NULL) {
+		I("%s: gpio_init\n", __func__);
+		platform_data->gpio_init();
+	}
+
+	pni->irq_gpio = platform_data->irq_gpio;
+	pni->ven_gpio  = platform_data->ven_gpio;
+	pni->firm_gpio  = platform_data->firm_gpio;
+	pni->client   = client;
+	pni->gpio_init = platform_data->gpio_init;
+	pni->ven_enable = !platform_data->ven_isinvert;
+	pni->boot_mode = board_mfg_mode();
+
+	
+
+	
+	init_waitqueue_head(&pni->read_wq);
+	mutex_init(&pni->read_mutex);
+	spin_lock_init(&pni->irq_enabled_lock);
+
+	I("%s: init io_wake_lock\n", __func__);
+	wake_lock_init(&pni->io_wake_lock, WAKE_LOCK_SUSPEND, PN544_I2C_NAME);
+
+	pni->pn544_device.minor = MISC_DYNAMIC_MINOR;
+	pni->pn544_device.name = "pn544";
+	pni->pn544_device.fops = &pn544_dev_fops;
+
+#if 1
+	ret = misc_register(&pni->pn544_device);
+#else
+	ret = 0;
+#endif
+	if (ret) {
+		E("%s : misc_register failed\n", __FILE__);
+		goto err_misc_register;
+	}
+
+
+	I("%s : requesting IRQ %d\n", __func__, client->irq);
+	pni->irq_enabled = true;
+	ret = request_irq(client->irq, pn544_dev_irq_handler,
+			  IRQF_TRIGGER_HIGH, client->name, pni);
+	if (ret) {
+		dev_err(&client->dev, "pn544_probe : request_irq failed\n");
+		goto err_request_irq_failed;
+	}
+	pn544_disable_irq(pni);
+	i2c_set_clientdata(client, pni);
+
+	pni->pn544_class = class_create(THIS_MODULE, "NFC_sensor");
+	if (IS_ERR(pni->pn544_class)) {
+		ret = PTR_ERR(pni->pn544_class);
+		pni->pn544_class = NULL;
+		E("%s : class_create failed\n", __func__);
+		goto err_create_class;
+	}
+
+	pni->pn_dev = device_create(pni->pn544_class, NULL, 0, "%s", "pn544");
+	if (unlikely(IS_ERR(pni->pn_dev))) {
+		ret = PTR_ERR(pni->pn_dev);
+		pni->pn_dev = NULL;
+		E("%s : device_create failed\n", __func__);
+		goto err_create_pn_device;
+	}
+
+	
+	ret = device_create_file(pni->pn_dev, &dev_attr_pn_temp1);
+	if (ret) {
+		E("%s : device_create_file dev_attr_pn_temp1 failed\n", __func__);
+		goto err_create_pn_file;
+	}
+
+	ret = device_create_file(pni->pn_dev, &dev_attr_debug_enable);
+	if (ret) {
+		E("pn544_probe device_create_file dev_attr_debug_enable failed\n");
+		goto err_create_pn_file;
+	}
+
+	
+	if (pni->boot_mode != 5) {
+		I("%s: disable NFC by default (bootmode = %d)\n", __func__, pni->boot_mode);
+		pn544_Disable();
+	}
+
+	I("%s: Probe success!\n", __func__);
+	return 0;
+
+err_create_pn_file:
+	device_unregister(pni->pn_dev);
+err_create_pn_device:
+	class_destroy(pni->pn544_class);
+err_create_class:
+err_request_irq_failed:
+	misc_deregister(&pni->pn544_device);
+err_misc_register:
+	mutex_destroy(&pni->read_mutex);
+	wake_lock_destroy(&pni->io_wake_lock);
+	kfree(pni);
+	pn_info = NULL;
+	gpio_free(platform_data->firm_gpio);
+err_request_gpio_firm:
+	gpio_free(platform_data->ven_gpio);
+err_request_gpio_ven:
+	gpio_free(platform_data->irq_gpio);
+err_exit:
+	E("%s: prob fail\n", __func__);
+	return ret;
+}
+
+static int pn544_remove(struct i2c_client *client)
+{
+	struct pn544_dev *pn544_dev;
+	I("%s:\n", __func__);
+
+	pn544_dev = i2c_get_clientdata(client);
+	free_irq(client->irq, pn544_dev);
+	misc_deregister(&pn544_dev->pn544_device);
+	mutex_destroy(&pn544_dev->read_mutex);
+	wake_lock_destroy(&pn544_dev->io_wake_lock);
+	gpio_free(pn544_dev->irq_gpio);
+	gpio_free(pn544_dev->ven_gpio);
+	gpio_free(pn544_dev->firm_gpio);
+	kfree(pn544_dev);
+	pn_info = NULL;
+
+	return 0;
+}
+
+#ifdef CONFIG_PM
+static int pn544_suspend(struct i2c_client *client, pm_message_t state)
+{
+	struct pn544_dev *pni = pn_info;
+
+	if (pni->ven_value) {
+		pni->irq_enabled = true;
+		enable_irq(pni->client->irq);
+		irq_set_irq_wake(pni->client->irq, 1);
+	}
+	return 0;
+}
+
+static int pn544_resume(struct i2c_client *client)
+{
+	struct pn544_dev *pni = pn_info;
+
+	if (pni->ven_value) {
+		pn544_disable_irq(pni);
+		irq_set_irq_wake(pni->client->irq, 0);
+	}
+	return 0;
+}
+#endif
+
+static const struct i2c_device_id pn544_id[] = {
+	{ "pn544", 0 },
+	{ }
+};
+
+static struct i2c_driver pn544_driver = {
+	.id_table	= pn544_id,
+	.probe		= pn544_probe,
+	.remove		= pn544_remove,
+	.driver		= {
+		.owner	= THIS_MODULE,
+		.name	= "pn544",
+	},
+#if CONFIG_PM
+	.suspend	= pn544_suspend,
+	.resume		= pn544_resume,
+#endif
+};
+
+
+static int __init pn544_dev_init(void)
+{
+	I("%s: Loading pn544 driver\n", __func__);
+	return i2c_add_driver(&pn544_driver);
+}
+module_init(pn544_dev_init);
+
+static void __exit pn544_dev_exit(void)
+{
+	I("%s: Unloading pn544 driver\n", __func__);
+	i2c_del_driver(&pn544_driver);
+}
+module_exit(pn544_dev_exit);
+
+MODULE_AUTHOR("Sylvain Fonteneau");
+MODULE_DESCRIPTION("NFC PN544 driver");
+MODULE_LICENSE("GPL");
diff --git a/include/linux/pn544.h b/include/linux/pn544.h
new file mode 100644
index 0000000..61a7843
--- /dev/null
+++ b/include/linux/pn544.h
@@ -0,0 +1,17 @@
+/*
+ * Copyright (C) 2010 NXP Semiconductors
+ */
+
+#define PN544_I2C_NAME "pn544"
+
+#define PN544_MAGIC	0xE9
+#define PN544_SET_PWR	_IOW(PN544_MAGIC, 0x01, unsigned int)
+#define IO_WAKE_LOCK_TIMEOUT (2*HZ)
+
+struct pn544_i2c_platform_data {
+	void (*gpio_init) (void);
+	unsigned int irq_gpio;
+	unsigned int ven_gpio;
+	unsigned int firm_gpio;
+	unsigned int ven_isinvert;
+};