NFC: Add to support ap6441 & ap6493 nfc function.

This commit is contained in:
gwl
2014-06-11 09:14:43 +08:00
parent feae1869ae
commit 384134cb60
6 changed files with 770 additions and 1 deletions

View File

@@ -492,7 +492,23 @@
poll_delay_ms = <30>;
layout = <1>;
};
nfc-bcm2079x@76 {
compatible = "nfc-bcm2079x.ap6441";
reg = <0x76>;
en_gpio = <&gpio4 GPIO_D3 GPIO_ACTIVE_HIGH>;
wake_gpio = <&gpio5 GPIO_C0 GPIO_ACTIVE_HIGH>;
irq_gpio = <&gpio5 GPIO_B7 IRQ_TYPE_EDGE_RISING>;
status = "disabled";
};
nfc-bcm2079x@77 {
compatible = "nfc-bcm2079x.ap6493";
reg = <0x77>;
en_gpio = <&gpio4 GPIO_D3 GPIO_ACTIVE_HIGH>;
wake_gpio = <&gpio5 GPIO_C0 GPIO_ACTIVE_HIGH>;
irq_gpio = <&gpio5 GPIO_B7 IRQ_TYPE_EDGE_RISING>;
status = "disabled";
};
};
&i2c2 {

View File

@@ -84,6 +84,7 @@ choice
config AP6441
bool "AP6441"
depends on RKWIFI
select BCM2079X_NFC
config AP6476
bool "AP6476"
@@ -92,6 +93,7 @@ choice
config AP6493
bool "AP6493"
depends on RKWIFI
select BCM2079X_NFC
config GB86302I
bool "GB86302I"

View File

@@ -5,6 +5,14 @@
menu "Near Field Communication (NFC) devices"
depends on NFC
config BCM2079X_NFC
tristate "bcm2079x NFC driver"
depends on AP6493 && AP6441
default n
---help---
Say yes if you want bcm2079x Near Field Communication driver.
This is for i2c connected version. If unsure, say N here.
config NFC_PN533
tristate "NXP PN533 USB driver"
depends on USB

View File

@@ -2,6 +2,7 @@
# Makefile for nfc devices
#
obj-$(CONFIG_BCM2079X_NFC) += bcm2079x-i2c.o
obj-$(CONFIG_NFC_PN544) += pn544/
obj-$(CONFIG_NFC_MICROREAD) += microread/
obj-$(CONFIG_NFC_PN533) += pn533.o

698
drivers/nfc/bcm2079x-i2c.c Normal file
View File

@@ -0,0 +1,698 @@
/*
* Copyright (C) 2012 Broadcom Corporation.
*
* 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
*/
#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/poll.h>
#include <linux/version.h>
#ifdef CONFIG_OF
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/of_gpio.h>
#endif
#include <linux/bcm2079x.h>
#define USE_WAKE_LOCK
#ifdef USE_WAKE_LOCK
#include <linux/wakelock.h>
#endif
#define TRUE 1
#define FALSE 0
#define STATE_HIGH 1
#define STATE_LOW 0
/* end of compile options */
/* do not change below */
#define MAX_BUFFER_SIZE 780
/* Read data */
#define PACKET_HEADER_SIZE_NCI (4)
#define PACKET_HEADER_SIZE_HCI (3)
#define PACKET_TYPE_NCI (16)
#define PACKET_TYPE_HCIEV (4)
#define MAX_PACKET_SIZE (PACKET_HEADER_SIZE_NCI + 255)
struct bcm2079x_dev {
wait_queue_head_t read_wq;
struct mutex read_mutex;
struct i2c_client *client;
struct miscdevice bcm2079x_device;
unsigned int wake_gpio;
unsigned int en_gpio;
unsigned int irq_gpio;
bool irq_enabled;
spinlock_t irq_enabled_lock;
unsigned int error_write;
unsigned int error_read;
unsigned int count_read;
unsigned int count_irq;
int original_address;
#ifdef USE_WAKE_LOCK
struct wake_lock wake_lock;
#endif
};
#define BCM2079X_NAME "bcm2079x-i2c"
#define VERSION "AMPAK-BCM2079X-DRIVER-VERSION-1.0.0"
unsigned int nfc_handle = -1;
unsigned int bcm2079x_irq_handle(void *para);
static void bcm2079x_init_stat(struct bcm2079x_dev *bcm2079x_dev)
{
bcm2079x_dev->error_write = 0;
bcm2079x_dev->error_read = 0;
bcm2079x_dev->count_read = 0;
bcm2079x_dev->count_irq = 0;
}
static void bcm2079x_disable_irq(struct bcm2079x_dev *bcm2079x_dev)
{
unsigned long flags;
spin_lock_irqsave(&bcm2079x_dev->irq_enabled_lock, flags);
if (bcm2079x_dev->irq_enabled) {
disable_irq_nosync(bcm2079x_dev->client->irq);
bcm2079x_dev->irq_enabled = false;
}
spin_unlock_irqrestore(&bcm2079x_dev->irq_enabled_lock, flags);
}
static void bcm2079x_enable_irq(struct bcm2079x_dev *bcm2079x_dev)
{
unsigned long flags;
spin_lock_irqsave(&bcm2079x_dev->irq_enabled_lock, flags);
if (!bcm2079x_dev->irq_enabled) {
bcm2079x_dev->irq_enabled = true;
enable_irq(bcm2079x_dev->client->irq);
}
spin_unlock_irqrestore(&bcm2079x_dev->irq_enabled_lock, flags);
}
/*
The alias address 0x79, when sent as a 7-bit address from the host processor
will match the first byte (highest 2 bits) of the default client address
(0x1FA) that is programmed in bcm20791.
When used together with the first byte (0xFA) of the byte sequence below,
it can be used to address the bcm20791 in a system that does not support
10-bit address and change the default address to 0x38.
the new address can be changed by changing the CLIENT_ADDRESS below if 0x38
conflicts with other device on the same i2c bus.
*/
#define ALIAS_ADDRESS 0x79
static void set_client_addr(struct bcm2079x_dev *bcm2079x_dev, int addr)
{
struct i2c_client *client = bcm2079x_dev->client;
client->addr = addr;
if (addr > 0x7F)
client->flags |= I2C_CLIENT_TEN;
else
client->flags &= ~I2C_CLIENT_TEN;
dev_info(&client->dev,
"Set client device changed to (0x%04X) flag = %04x\n",
client->addr, client->flags);
}
static void change_client_addr(struct bcm2079x_dev *bcm2079x_dev, int addr)
{
struct i2c_client *client;
int ret;
int i;
int offset = 1;
char addr_data[] = {
0xFA, 0xF2, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x2A
};
client = bcm2079x_dev->client;
if ((client->flags & I2C_CLIENT_TEN) == I2C_CLIENT_TEN) {
client->addr = ALIAS_ADDRESS;
client->flags &= ~I2C_CLIENT_TEN;
offset = 0;
}
addr_data[5] = addr & 0xFF;
ret = 0;
for (i = 1; i < sizeof(addr_data) - 1; ++i)
ret += addr_data[i];
addr_data[sizeof(addr_data) - 1] = (ret & 0xFF);
dev_info(&client->dev,
"Change client device from (0x%04X) flag = "\
"%04x, addr_data[%d] = %02x\n",
client->addr, client->flags, sizeof(addr_data) - 1,
addr_data[sizeof(addr_data) - 1]);
ret = i2c_master_send(client, addr_data+offset, sizeof(addr_data)-offset);
if (ret != sizeof(addr_data)-offset) {
client->addr = ALIAS_ADDRESS;
client->flags &= ~I2C_CLIENT_TEN;
dev_info(&client->dev,
"Change client device from (0x%04X) flag = "\
"%04x, addr_data[%d] = %02x\n",
client->addr, client->flags, sizeof(addr_data) - 1,
addr_data[sizeof(addr_data) - 1]);
ret = i2c_master_send(client, addr_data, sizeof(addr_data));
}
client->addr = addr_data[5];
dev_info(&client->dev,
"Change client device changed to (0x%04X) flag = %04x, ret = %d\n",
client->addr, client->flags, ret);
}
static irqreturn_t bcm2079x_dev_irq_handler(int irq, void *dev_id)
{
struct bcm2079x_dev *bcm2079x_dev = dev_id;
unsigned long flags;
#ifdef USE_WAKE_LOCK
int wakelockcnt = 0;
#endif
printk("%s: nfc irq handler\n", __func__);
#ifdef USE_WAKE_LOCK
if(!(wakelockcnt = wake_lock_active(&bcm2079x_dev->wake_lock )))
{
printk("irq aquire wake lock\n");
wake_lock(&bcm2079x_dev->wake_lock);
}else
{
// printk("irq wake lock count = %d\n", wakelockcnt);
}
//printk("irq handler ( wake lock %d)...\n", wakelockcnt);
#endif
spin_lock_irqsave(&bcm2079x_dev->irq_enabled_lock, flags);
bcm2079x_dev->count_irq++;
spin_unlock_irqrestore(&bcm2079x_dev->irq_enabled_lock, flags);
wake_up(&bcm2079x_dev->read_wq);
return IRQ_HANDLED;
}
static unsigned int bcm2079x_dev_poll(struct file *filp, poll_table *wait)
{
struct bcm2079x_dev *bcm2079x_dev = filp->private_data;
unsigned int mask = 0;
unsigned long flags;
spin_lock_irqsave(&bcm2079x_dev->irq_enabled_lock, flags);
//Platform specific API
//if(!__gpio_get_value(bcm2079x_dev->irq_gpio) && (bcm2079x_dev->count_irq < 1) )
if(!gpio_get_value(bcm2079x_dev->irq_gpio) && (bcm2079x_dev->count_irq < 1) )
{
spin_unlock_irqrestore(&bcm2079x_dev->irq_enabled_lock, flags);
// printk("poll wait, irq count %d, irq_gpio %d\n", bcm2079x_dev->count_irq, bcm2079x_dev->irq_gpio );
poll_wait(filp, &bcm2079x_dev->read_wq, wait);
}else
{
if (bcm2079x_dev->count_irq < 1)
bcm2079x_dev->count_irq = 1;
spin_unlock_irqrestore(&bcm2079x_dev->irq_enabled_lock, flags);
// printk("poll there is data to read!!! no wait any more.\n");
return (POLLIN | POLLRDNORM);
}
spin_lock_irqsave(&bcm2079x_dev->irq_enabled_lock, flags);
if (bcm2079x_dev->count_irq > 0)
mask |= POLLIN | POLLRDNORM;
spin_unlock_irqrestore(&bcm2079x_dev->irq_enabled_lock, flags);
return mask;
}
static ssize_t bcm2079x_dev_read(struct file *filp, char __user *buf,
size_t count, loff_t *offset)
{
struct bcm2079x_dev *bcm2079x_dev = filp->private_data;
unsigned char tmp[MAX_BUFFER_SIZE];
int total, len, ret;
total = 0;
len = 0;
if (bcm2079x_dev->count_irq > 0)
bcm2079x_dev->count_irq--;
bcm2079x_dev->count_read++;
if (count > MAX_BUFFER_SIZE)
count = MAX_BUFFER_SIZE;
mutex_lock(&bcm2079x_dev->read_mutex);
/** Read the first 4 bytes to include the length of the NCI or HCI packet.
**/
ret = i2c_master_recv(bcm2079x_dev->client, tmp, 4);
if (ret == 4) {
total = ret;
/** First byte is the packet type
**/
switch(tmp[0]) {
case PACKET_TYPE_NCI:
len = tmp[PACKET_HEADER_SIZE_NCI-1];
break;
case PACKET_TYPE_HCIEV:
len = tmp[PACKET_HEADER_SIZE_HCI-1];
if (len == 0)
total--; /*Since payload is 0, decrement total size (from 4 to 3) */
else
len--; /*First byte of payload is in tmp[3] already */
break;
default:
len = 0; /*Unknown packet byte */
break;
} /* switch*/
/** make sure full packet fits in the buffer
**/
if (len > 0 && (len + total) <= count) {
/** read the remainder of the packet.
**/
ret = i2c_master_recv(bcm2079x_dev->client, tmp+total, len);
if (ret == len)
total += len;
} /* if */
} /* if */
mutex_unlock(&bcm2079x_dev->read_mutex);
if (total > count || copy_to_user(buf, tmp, total)) {
dev_err(&bcm2079x_dev->client->dev,
"failed to copy to user space, total = %d\n", total);
total = -EFAULT;
bcm2079x_dev->error_read++;
}
return total;
}
static ssize_t bcm2079x_dev_write(struct file *filp, const char __user *buf,
size_t count, loff_t *offset)
{
struct bcm2079x_dev *bcm2079x_dev = filp->private_data;
char tmp[MAX_BUFFER_SIZE];
int ret;
if (count > MAX_BUFFER_SIZE) {
dev_err(&bcm2079x_dev->client->dev, "out of memory\n");
return -ENOMEM;
}
if (copy_from_user(tmp, buf, count)) {
dev_err(&bcm2079x_dev->client->dev,
"failed to copy from user space\n");
return -EFAULT;
}
mutex_lock(&bcm2079x_dev->read_mutex);
/* Write data */
ret = i2c_master_send(bcm2079x_dev->client, tmp, count);
if (ret != count) {
if ((bcm2079x_dev->client->flags & I2C_CLIENT_TEN) != I2C_CLIENT_TEN && bcm2079x_dev->error_write == 0) {
set_client_addr(bcm2079x_dev, 0x1FA);
ret = i2c_master_send(bcm2079x_dev->client, tmp, count);
if (ret != count)
bcm2079x_dev->error_write++;
set_client_addr(bcm2079x_dev, bcm2079x_dev->original_address);
} else {
dev_err(&bcm2079x_dev->client->dev,
"failed to write %d\n", ret);
ret = -EIO;
bcm2079x_dev->error_write++;
}
}
mutex_unlock(&bcm2079x_dev->read_mutex);
return ret;
}
static int bcm2079x_dev_open(struct inode *inode, struct file *filp)
{
int ret = 0;
struct bcm2079x_dev *bcm2079x_dev = container_of(filp->private_data,
struct bcm2079x_dev,
bcm2079x_device);
filp->private_data = bcm2079x_dev;
bcm2079x_init_stat(bcm2079x_dev);
bcm2079x_enable_irq(bcm2079x_dev);
dev_info(&bcm2079x_dev->client->dev,
"device node major=%d, minor=%d\n", imajor(inode), iminor(inode));
return ret;
}
static long bcm2079x_dev_unlocked_ioctl(struct file *filp,
unsigned int cmd, unsigned long arg)
{
struct bcm2079x_dev *bcm2079x_dev = filp->private_data;
switch (cmd) {
case BCMNFC_READ_FULL_PACKET:
break;
case BCMNFC_READ_MULTI_PACKETS:
break;
case BCMNFC_CHANGE_ADDR:
dev_info(&bcm2079x_dev->client->dev,
"%s, BCMNFC_CHANGE_ADDR (%x, %lx):\n", __func__, cmd,
arg);
change_client_addr(bcm2079x_dev, arg);
break;
case BCMNFC_POWER_CTL:
dev_info(&bcm2079x_dev->client->dev,
"%s, BCMNFC_POWER_CTL (%x, %lx):\n", __func__, cmd, arg);
if (arg != 1)
set_client_addr(bcm2079x_dev, bcm2079x_dev->original_address);
gpio_direction_output(bcm2079x_dev->en_gpio, arg);
gpio_set_value(bcm2079x_dev->en_gpio, arg);
break;
case BCMNFC_WAKE_CTL:
dev_info(&bcm2079x_dev->client->dev,
"%s, BCMNFC_WAKE_CTL (%x, %lx):\n", __func__, cmd, arg);
#ifdef USE_WAKE_LOCK
if(arg != 0)
{
while(wake_lock_active(&bcm2079x_dev->wake_lock ))
{
printk("release wake lock!!!\n");
wake_unlock(&bcm2079x_dev->wake_lock);
}
//wake_lock_timeout(&bcm2079x_dev->wake_lock, HZ*2);
}
#endif
gpio_direction_output(bcm2079x_dev->wake_gpio, arg);
gpio_set_value(bcm2079x_dev->wake_gpio, arg);
break;
default:
dev_err(&bcm2079x_dev->client->dev,
"%s, unknown cmd (%x, %lx)\n", __func__, cmd, arg);
return 0;
}
return 0;
}
static const struct file_operations bcm2079x_dev_fops = {
.owner = THIS_MODULE,
.llseek = no_llseek,
.poll = bcm2079x_dev_poll,
.read = bcm2079x_dev_read,
.write = bcm2079x_dev_write,
.open = bcm2079x_dev_open,
.unlocked_ioctl = bcm2079x_dev_unlocked_ioctl
};
unsigned int bcm2079x_irq_handle(void *para)
{
bcm2079x_dev_irq_handler(0, para);
return 0;
}
#ifdef CONFIG_OF
static int nfc_platdata_parse_dt(struct device *dev, struct bcm2079x_platform_data *data)
{
int ret = 0;
int gpio = -1;
enum of_gpio_flags flags;
struct device_node *node = dev->of_node;
if (!node)
return -ENODEV;
memset(data, 0, sizeof(*data));
gpio = of_get_named_gpio_flags(node, "en_gpio", 0, &flags);
if (gpio_is_valid(gpio)){
data->en_gpio = gpio;
dev_err(dev, "%s: get property: en_gpio = %d\n", __func__, gpio);
} else ret = -1;
gpio = of_get_named_gpio_flags(node, "wake_gpio", 0, &flags);
if (gpio_is_valid(gpio)){
data->wake_gpio = gpio;
dev_err(dev, "%s: get property: wake_gpio = %d\n", __func__, gpio);
} else ret = -1;
gpio = of_get_named_gpio_flags(node, "irq_gpio", 0, &flags);
if (gpio_is_valid(gpio)){
data->irq_gpio = gpio;
dev_err(dev, "%s: get property: irq_gpio = %d\n", __func__, gpio);
} else ret = -1;
return ret;
}
#endif
static int bcm2079x_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
int ret, err = -1;
struct bcm2079x_platform_data *platform_data;
struct bcm2079x_dev *bcm2079x_dev;
err = i2c_check_functionality(client->adapter, I2C_FUNC_I2C);
if (!err) {
dev_info(&client->dev, "Check I2C Functionality Failed.\n");
return -ENODEV;
}
dev_info(&client->dev, "Driver Version: %s\n", VERSION);
dev_info(&client->dev, "%s, probing bcm2079x driver flags = %x\n", __func__, client->flags);
platform_data = client->dev.platform_data;
if (platform_data == NULL) {
#ifdef CONFIG_OF
platform_data = devm_kzalloc(&client->dev, sizeof(struct bcm2079x_platform_data), GFP_KERNEL);
if (!platform_data)
return -ENOMEM;
if (nfc_platdata_parse_dt(&client->dev, platform_data)) {
#endif
dev_err(&client->dev, "nfc probe fail\n");
return -ENODEV;
#ifdef CONFIG_OF
}
client->dev.platform_data = platform_data;
#endif
}
if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
dev_err(&client->dev, "need I2C_FUNC_I2C\n");
return -ENODEV;
}
ret = gpio_request(platform_data->irq_gpio, "nfc_int");
if (ret)
return -ENODEV;
ret = gpio_request(platform_data->en_gpio, "nfc_ven");
if (ret)
goto err_en;
ret = gpio_request(platform_data->wake_gpio, "nfc_firm");
if (ret)
goto err_firm;
gpio_direction_output(platform_data->en_gpio, 0 );
gpio_direction_output(platform_data->wake_gpio, 0);
gpio_set_value(platform_data->en_gpio, 0);
gpio_set_value(platform_data->wake_gpio, 0);
//gpio_direction_input(platform_data->irq_gpio );
bcm2079x_dev = kzalloc(sizeof(*bcm2079x_dev), GFP_KERNEL);
if (bcm2079x_dev == NULL) {
dev_err(&client->dev,
"failed to allocate memory for module data\n");
ret = -ENOMEM;
goto err_exit;
}
client->irq = gpio_to_irq(platform_data->irq_gpio);
if (client->irq == -ENXIO) {
dev_err(&client->dev,"[bcm2079x]: get gpio irq fail.\n");
goto err_exit;
}
bcm2079x_dev->wake_gpio = platform_data->wake_gpio;
bcm2079x_dev->irq_gpio = platform_data->irq_gpio;
bcm2079x_dev->en_gpio = platform_data->en_gpio;
bcm2079x_dev->client = client;
/* init mutex and queues */
init_waitqueue_head(&bcm2079x_dev->read_wq);
mutex_init(&bcm2079x_dev->read_mutex);
spin_lock_init(&bcm2079x_dev->irq_enabled_lock);
bcm2079x_dev->bcm2079x_device.minor = MISC_DYNAMIC_MINOR;
bcm2079x_dev->bcm2079x_device.name = "bcm2079x";
bcm2079x_dev->bcm2079x_device.fops = &bcm2079x_dev_fops;
ret = misc_register(&bcm2079x_dev->bcm2079x_device);
if (ret) {
dev_err(&client->dev, "misc_register failed\n");
goto err_misc_register;
}
dev_info(&client->dev,
"%s, saving address %d\n",
__func__, client->addr);
bcm2079x_dev->original_address = client->addr;
/* request irq. the irq is set whenever the chip has data available
* for reading. it is cleared when all data has been read.
*/
dev_info(&client->dev, "requesting IRQ %d with IRQF_NO_SUSPEND\n", client->irq);
bcm2079x_dev->irq_enabled = true;
//ret = request_irq(client->irq, bcm2079x_dev_irq_handler,
// IRQF_TRIGGER_RISING|IRQF_NO_SUSPEND, client->name, bcm2079x_dev);
//if (ret) {
// dev_err(&client->dev, "request_irq failed\n");
// goto err_request_irq_failed;
//}
//enable_irq_wake(client->irq);
//Platform specific API
//nfc_handle = sw_gpio_irq_request(bcm2079x_dev->irq_gpio, TRIG_EDGE_POSITIVE,
// bcm2079x_irq_handle, bcm2079x_dev);
nfc_handle = request_irq(client->irq, bcm2079x_dev_irq_handler,
IRQF_TRIGGER_RISING|IRQF_NO_SUSPEND, client->name, bcm2079x_dev);
if (nfc_handle != 0) {
dev_err(&client->dev, "request_irq failed.\n");
goto err_request_irq_failed;
}
enable_irq_wake(client->irq);
bcm2079x_disable_irq(bcm2079x_dev);
i2c_set_clientdata(client, bcm2079x_dev);
dev_info(&client->dev,
"%s, probing bcm2079x driver exited successfully\n",
__func__);
#ifdef USE_WAKE_LOCK
wake_lock_init(&bcm2079x_dev->wake_lock , WAKE_LOCK_SUSPEND, "nfcwakelock" );
#endif
return 0;
err_request_irq_failed:
misc_deregister(&bcm2079x_dev->bcm2079x_device);
err_misc_register:
mutex_destroy(&bcm2079x_dev->read_mutex);
kfree(bcm2079x_dev);
err_exit:
gpio_free(platform_data->wake_gpio);
err_firm:
gpio_free(platform_data->en_gpio);
err_en:
gpio_free(platform_data->irq_gpio);
return ret;
}
static int bcm2079x_remove(struct i2c_client *client)
{
struct bcm2079x_dev *bcm2079x_dev;
bcm2079x_dev = i2c_get_clientdata(client);
//Platform specific API
if (nfc_handle == 0)
free_irq(client->irq, bcm2079x_dev);
//sw_gpio_irq_free(nfc_handle);
misc_deregister(&bcm2079x_dev->bcm2079x_device);
mutex_destroy(&bcm2079x_dev->read_mutex);
gpio_free(bcm2079x_dev->irq_gpio);
gpio_free(bcm2079x_dev->en_gpio);
gpio_free(bcm2079x_dev->wake_gpio);
kfree(bcm2079x_dev);
return 0;
}
/*******************************************************************/
/* AP6493 */
/*******************************************************************/
//static const unsigned short normal_i2c[] = {0x77, I2C_CLIENT_END};
/*******************************************************************/
/* AP6441 */
/*******************************************************************/
//static const unsigned short normal_i2c[] = {0x76, I2C_CLIENT_END};
static const struct i2c_device_id bcm2079x_id[] = {
{"bcm2079x-i2c", 0},
{}
};
MODULE_DEVICE_TABLE(i2c, bcm2079x_id);
static struct of_device_id bcm2079x_ids[] = {
{ .compatible = "nfc-bcm2079x.ap6441" },
{ .compatible = "nfc-bcm2079x.ap6493" },
{ }
};
static struct i2c_driver bcm2079x_driver = {
.probe = bcm2079x_probe,
.remove = bcm2079x_remove,
.id_table = bcm2079x_id,
.driver = {
.owner = THIS_MODULE,
.name = "bcm2079x-i2c",
.of_match_table = of_match_ptr(bcm2079x_ids),
},
};
/*
* module load/unload record keeping
*/
static int __init bcm2079x_dev_init(void)
{
printk("Enter %s\n", __func__);
return i2c_add_driver(&bcm2079x_driver);
}
module_init(bcm2079x_dev_init);
static void __exit bcm2079x_dev_exit(void)
{
printk("Enter %s\n", __func__);
i2c_del_driver(&bcm2079x_driver);
}
module_exit(bcm2079x_dev_exit);
MODULE_AUTHOR("Broadcom");
MODULE_DESCRIPTION("NFC bcm2079x driver");
MODULE_LICENSE("GPL");

44
include/linux/bcm2079x.h Normal file
View File

@@ -0,0 +1,44 @@
/*
* Copyright (C) 2012 Broadcom Corporation.
*
* 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
*/
#ifndef _BCM2079X_H
#define _BCM2079X_H
#define BCMNFC_MAGIC 0xFA
/*
* BCMNFC power control via ioctl
* BCMNFC_POWER_CTL(0): power off
* BCMNFC_POWER_CTL(1): power on
* BCMNFC_WAKE_CTL(0): wake off
* BCMNFC_WAKE_CTL(1): wake on
*/
#define BCMNFC_POWER_CTL _IO(BCMNFC_MAGIC, 0x01)
#define BCMNFC_CHANGE_ADDR _IO(BCMNFC_MAGIC, 0x02)
#define BCMNFC_READ_FULL_PACKET _IO(BCMNFC_MAGIC, 0x03)
#define BCMNFC_SET_WAKE_ACTIVE_STATE _IO(BCMNFC_MAGIC, 0x04)
#define BCMNFC_WAKE_CTL _IO(BCMNFC_MAGIC, 0x05)
#define BCMNFC_READ_MULTI_PACKETS _IO(BCMNFC_MAGIC, 0x06)
struct bcm2079x_platform_data {
unsigned int irq_gpio;
unsigned int en_gpio;
int wake_gpio;
};
#endif