misc: Import CXD224X NFC driver
Signed-off-by: Sorayukii <sorayukii69@gmail.com>
This commit is contained in:
parent
66e3dc9382
commit
61d19800e8
11 changed files with 2061 additions and 0 deletions
|
|
@ -869,6 +869,16 @@ config QPNP_MISC
|
|||
and the driver provides an API to check if this interrupt
|
||||
is available on the current PMIC chip.
|
||||
|
||||
config CXD224X_NFC
|
||||
bool "CXD224X driver for FeliCa and NFC"
|
||||
depends on I2C
|
||||
default n
|
||||
help
|
||||
This option enables device driver support for Sony CXD224X
|
||||
series. This driver enables the control of CXD224X series
|
||||
device by using I2C. Furthermore, this driver enables the
|
||||
control of PON using device file.
|
||||
|
||||
source "drivers/misc/c2port/Kconfig"
|
||||
source "drivers/misc/eeprom/Kconfig"
|
||||
source "drivers/misc/cb710/Kconfig"
|
||||
|
|
@ -882,6 +892,7 @@ source "drivers/misc/genwqe/Kconfig"
|
|||
source "drivers/misc/echo/Kconfig"
|
||||
source "drivers/misc/cxl/Kconfig"
|
||||
source "drivers/misc/fpr_FingerprintCard/Kconfig"
|
||||
source "drivers/misc/carillon/Kconfig"
|
||||
endmenu
|
||||
|
||||
config OKL4_LINK_SHBUF
|
||||
|
|
|
|||
|
|
@ -55,6 +55,7 @@ obj-$(CONFIG_QSEECOM) += qseecom.o
|
|||
obj-$(CONFIG_ECHO) += echo/
|
||||
obj-$(CONFIG_VEXPRESS_SYSCFG) += vexpress-syscfg.o
|
||||
obj-$(CONFIG_CXL_BASE) += cxl/
|
||||
obj-$(CONFIG_CXD224X_NFC) += cxd224x-i2c.o
|
||||
obj-$(CONFIG_PANEL) += panel.o
|
||||
obj-$(CONFIG_QPNP_MISC) += qpnp-misc.o
|
||||
obj-$(CONFIG_FPR_FPC) += fpr_FingerprintCard/
|
||||
|
|
@ -91,3 +92,4 @@ $(obj)/lkdtm_rodata_objcopy.o: $(obj)/lkdtm_rodata.o FORCE
|
|||
$(call if_changed,objcopy)
|
||||
|
||||
obj-$(CONFIG_OKL4_LINK_SHBUF) += okl4-link-shbuf.o
|
||||
obj-$(CONFIG_NFC_CARILLON) += carillon/
|
||||
|
|
|
|||
5
drivers/misc/carillon/Kconfig
Normal file
5
drivers/misc/carillon/Kconfig
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
comment "Sony Carillon NFC driver"
|
||||
|
||||
config NFC_CARILLON
|
||||
tristate "Sony Carillon protocol driver (I2C) devices"
|
||||
default n
|
||||
2
drivers/misc/carillon/Makefile
Normal file
2
drivers/misc/carillon/Makefile
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
obj-$(CONFIG_NFC_CARILLON) := sony_carillon_nfc.o
|
||||
sony_carillon_nfc-objs := main_module.o cxd224x/cxd224x-i2c.o bd7602/bd7602.o
|
||||
311
drivers/misc/carillon/bd7602/bd7602.c
Normal file
311
drivers/misc/carillon/bd7602/bd7602.c
Normal file
|
|
@ -0,0 +1,311 @@
|
|||
/* drivers/misc/bd7602.c
|
||||
*
|
||||
* Author: Manabu Yoshida <Manabu.X.Yoshida@sonymobile.com>
|
||||
*
|
||||
* 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; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*/
|
||||
|
||||
#include <linux/err.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/miscdevice.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
#include <asm/uaccess.h>
|
||||
|
||||
#include "bd7602.h"
|
||||
|
||||
#define BD7602_DEVICE_NAME "bd7602"
|
||||
#define BD7602_MODE_MAX 0x04
|
||||
|
||||
struct bd7602_dev {
|
||||
struct mutex mutex;
|
||||
struct i2c_client *client;
|
||||
struct miscdevice bd7602_device;
|
||||
u8 mode;
|
||||
};
|
||||
|
||||
static ssize_t bd7602_mode_store(struct bd7602_dev *bd7602_dev, void __user *buf)
|
||||
{
|
||||
u8 mode;
|
||||
int ret;
|
||||
|
||||
ret = copy_from_user(&mode, buf, sizeof(mode));
|
||||
if (ret != 0) {
|
||||
dev_err(&bd7602_dev->client->dev, "%s: Failed to copy data from user %d\n",
|
||||
__func__, ret);
|
||||
ret = -EIO;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (BD7602_MODE_MAX < mode) {
|
||||
dev_err(&bd7602_dev->client->dev, "%s: Invalid mode 0x%02x\n", __func__, mode);
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
mutex_lock(&bd7602_dev->mutex);
|
||||
bd7602_dev->mode = mode;
|
||||
mutex_unlock(&bd7602_dev->mutex);
|
||||
dev_info(&bd7602_dev->client->dev, "%s: mode: 0x%02x\n", __func__, bd7602_dev->mode);
|
||||
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static ssize_t bd7602_mode_show(struct bd7602_dev *bd7602_dev, void __user *buf)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (!buf) {
|
||||
dev_err(&bd7602_dev->client->dev, "%s: invalid address\n", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
mutex_lock(&bd7602_dev->mutex);
|
||||
ret = copy_to_user(buf, &bd7602_dev->mode, sizeof(bd7602_dev->mode));
|
||||
if (ret != 0) {
|
||||
dev_err(&bd7602_dev->client->dev, "%s: Failed to copy data to user %d\n",
|
||||
__func__, ret);
|
||||
ret = -EIO;
|
||||
}
|
||||
mutex_unlock(&bd7602_dev->mutex);
|
||||
dev_info(&bd7602_dev->client->dev, "%s: mode: 0x%02x\n", __func__, bd7602_dev->mode);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static ssize_t bd7602_value_store(struct bd7602_dev *bd7602_dev, void __user *buf)
|
||||
{
|
||||
u8 data[2];
|
||||
int ret;
|
||||
int len = 2;
|
||||
|
||||
mutex_lock(&bd7602_dev->mutex);
|
||||
data[0] = bd7602_dev->mode;
|
||||
ret = copy_from_user(&data[1], buf, sizeof(u8));
|
||||
if (ret != 0) {
|
||||
mutex_unlock(&bd7602_dev->mutex);
|
||||
dev_err(&bd7602_dev->client->dev, "%s: Failed to copy data from user %d\n",
|
||||
__func__, ret);
|
||||
ret = -EIO;
|
||||
goto out;
|
||||
}
|
||||
ret = i2c_master_send(bd7602_dev->client, data, len);
|
||||
if (ret != len) {
|
||||
mutex_unlock(&bd7602_dev->mutex);
|
||||
dev_err(&bd7602_dev->client->dev, "%s: Failed to write %d\n", __func__, ret);
|
||||
ret = -EIO;
|
||||
goto out;
|
||||
}
|
||||
mutex_unlock(&bd7602_dev->mutex);
|
||||
|
||||
dev_info(&bd7602_dev->client->dev, "%s: mode:0x%02x value:0x%02x\n",
|
||||
__func__, data[0], data[1]);
|
||||
|
||||
out:
|
||||
return ret > 0 ? 0 : ret;
|
||||
}
|
||||
|
||||
static ssize_t bd7602_value_show(struct bd7602_dev *bd7602_dev, void __user *buf)
|
||||
{
|
||||
u8 data;
|
||||
int ret;
|
||||
|
||||
if (!buf) {
|
||||
dev_err(&bd7602_dev->client->dev, "%s: invalid address\n", __func__);
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
mutex_lock(&bd7602_dev->mutex);
|
||||
ret = i2c_master_send(bd7602_dev->client, &bd7602_dev->mode,
|
||||
sizeof(bd7602_dev->mode));
|
||||
if (sizeof(bd7602_dev->mode) != ret) {
|
||||
mutex_unlock(&bd7602_dev->mutex);
|
||||
dev_err(&bd7602_dev->client->dev, "%s: Failed to write %d\n", __func__, ret);
|
||||
ret = -EIO;
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = i2c_master_recv(bd7602_dev->client, &data, sizeof(data));
|
||||
if (sizeof(data) != ret) {
|
||||
mutex_unlock(&bd7602_dev->mutex);
|
||||
dev_err(&bd7602_dev->client->dev, "%s: Failed to read %d\n", __func__, ret);
|
||||
ret = -EIO;
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = copy_to_user(buf, &data, sizeof(data));
|
||||
if (ret != 0) {
|
||||
mutex_unlock(&bd7602_dev->mutex);
|
||||
dev_err(&bd7602_dev->client->dev, "%s: Failed to copy data to user %d\n",
|
||||
__func__, ret);
|
||||
ret = -EIO;
|
||||
goto out;
|
||||
}
|
||||
mutex_unlock(&bd7602_dev->mutex);
|
||||
|
||||
dev_info(&bd7602_dev->client->dev, "%s: mode:0x%02x value:0x%02x\n",
|
||||
__func__, bd7602_dev->mode, data);
|
||||
|
||||
out:
|
||||
return ret > 0 ? 0 : ret;
|
||||
}
|
||||
|
||||
static long bd7602_dev_unlocked_ioctl(struct file *filp,
|
||||
unsigned int cmd, unsigned long arg)
|
||||
{
|
||||
struct bd7602_dev *bd7602_dev = filp->private_data;
|
||||
int ret = -EINVAL;
|
||||
void __user *data = (void __user *)arg;
|
||||
|
||||
if (!bd7602_dev) {
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
switch (cmd) {
|
||||
case BD7602_MODE_STORE:
|
||||
ret = bd7602_mode_store(bd7602_dev, data);
|
||||
break;
|
||||
|
||||
case BD7602_MODE_SHOW:
|
||||
ret = bd7602_mode_show(bd7602_dev, data);
|
||||
break;
|
||||
|
||||
case BD7602_VALUE_STORE:
|
||||
ret = bd7602_value_store(bd7602_dev, data);
|
||||
break;
|
||||
|
||||
case BD7602_VALUE_SHOW:
|
||||
ret = bd7602_value_show(bd7602_dev, data);
|
||||
break;
|
||||
|
||||
default:
|
||||
dev_err(&bd7602_dev->client->dev,
|
||||
"%s, unknown cmd (%x, %lx)\n", __func__, cmd, arg);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int bd7602_dev_open(struct inode *inode, struct file *filp)
|
||||
{
|
||||
struct bd7602_dev *bd7602_dev = container_of(filp->private_data,
|
||||
struct bd7602_dev,
|
||||
bd7602_device);
|
||||
filp->private_data = bd7602_dev;
|
||||
|
||||
dev_info(&bd7602_dev->client->dev, "%s, open\n", __func__);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
const struct file_operations bd7602_dev_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.llseek = no_llseek,
|
||||
.open = bd7602_dev_open,
|
||||
.unlocked_ioctl = bd7602_dev_unlocked_ioctl
|
||||
};
|
||||
|
||||
static int bd7602_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
int ret;
|
||||
struct bd7602_dev *bd7602_dev;
|
||||
|
||||
dev_info(&client->dev, "%s, probing bd7602 driver flags = %x\n",
|
||||
__func__, client->flags);
|
||||
|
||||
if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
|
||||
dev_err(&client->dev, "need I2C_FUNC_I2C\n");
|
||||
ret = -ENODEV;
|
||||
goto err_exit;
|
||||
}
|
||||
|
||||
bd7602_dev = kzalloc(sizeof(*bd7602_dev), GFP_KERNEL);
|
||||
if (bd7602_dev == NULL) {
|
||||
dev_err(&client->dev,
|
||||
"failed to allocate memory for module data\n");
|
||||
ret = -ENOMEM;
|
||||
goto err_kzalloc;
|
||||
}
|
||||
|
||||
bd7602_dev->client = client;
|
||||
i2c_set_clientdata(client, bd7602_dev);
|
||||
|
||||
mutex_init(&bd7602_dev->mutex);
|
||||
|
||||
bd7602_dev->bd7602_device.minor = MISC_DYNAMIC_MINOR;
|
||||
bd7602_dev->bd7602_device.name = "bd7602-i2c";
|
||||
bd7602_dev->bd7602_device.fops = &bd7602_dev_fops;
|
||||
|
||||
ret = misc_register(&bd7602_dev->bd7602_device);
|
||||
if (ret) {
|
||||
dev_err(&client->dev, "misc_register failed\n");
|
||||
goto err_misc_register;
|
||||
}
|
||||
|
||||
dev_info(&client->dev,
|
||||
"%s, probing bd7602 driver exited successfully\n",
|
||||
__func__);
|
||||
return 0;
|
||||
|
||||
err_misc_register:
|
||||
mutex_destroy(&bd7602_dev->mutex);
|
||||
i2c_set_clientdata(client, NULL);
|
||||
kzfree(bd7602_dev);
|
||||
err_kzalloc:
|
||||
err_exit:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int bd7602_remove(struct i2c_client *client)
|
||||
{
|
||||
struct bd7602_dev *bd7602_dev = i2c_get_clientdata(client);
|
||||
|
||||
mutex_destroy(&bd7602_dev->mutex);
|
||||
i2c_set_clientdata(client, NULL);
|
||||
kzfree(bd7602_dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct i2c_device_id bd7602_id[] = {
|
||||
{ BD7602_DEVICE_NAME, 0 },
|
||||
{ }
|
||||
};
|
||||
|
||||
static struct of_device_id bd7602_match_table[] = {
|
||||
{ .compatible = "rohm,bd7602", },
|
||||
{ },
|
||||
};
|
||||
|
||||
static struct i2c_driver bd7602_driver = {
|
||||
.id_table = bd7602_id,
|
||||
.probe = bd7602_probe,
|
||||
.remove = bd7602_remove,
|
||||
.driver = {
|
||||
.owner = THIS_MODULE,
|
||||
.name = BD7602_DEVICE_NAME,
|
||||
.of_match_table = bd7602_match_table,
|
||||
},
|
||||
};
|
||||
|
||||
int bd7602_dev_init(void)
|
||||
{
|
||||
return i2c_add_driver(&bd7602_driver);
|
||||
}
|
||||
|
||||
void bd7602_dev_exit(void)
|
||||
{
|
||||
i2c_del_driver(&bd7602_driver);
|
||||
}
|
||||
|
||||
MODULE_AUTHOR("Manabu Yoshida <Manabu.X.Yoshida@sonymobile.com>");
|
||||
MODULE_DESCRIPTION("ROHM BD7602 Power IC Driver");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
18
drivers/misc/carillon/bd7602/bd7602.h
Normal file
18
drivers/misc/carillon/bd7602/bd7602.h
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
#ifndef _BD7602_H
|
||||
#define _BD7602_H
|
||||
|
||||
#define BD7602_MAGIC 'J'
|
||||
|
||||
/*
|
||||
* BD7602 MODE and VALUE via ioctl
|
||||
* BD7602_MODE_STORE: store target register address
|
||||
* BD7602_MODE_SHOW: retrieve current register address
|
||||
* BD7602_VALUE_STORE: store register value
|
||||
* BD7602_VALUE_SHOW: retrieve register value
|
||||
*/
|
||||
#define BD7602_MODE_STORE _IOW(BD7602_MAGIC, 0x01, char *)
|
||||
#define BD7602_MODE_SHOW _IOR(BD7602_MAGIC, 0x02, char *)
|
||||
#define BD7602_VALUE_STORE _IOW(BD7602_MAGIC, 0x03, char *)
|
||||
#define BD7602_VALUE_SHOW _IOR(BD7602_MAGIC, 0x04, char *)
|
||||
|
||||
#endif
|
||||
843
drivers/misc/carillon/cxd224x/cxd224x-i2c.c
Normal file
843
drivers/misc/carillon/cxd224x/cxd224x-i2c.c
Normal file
|
|
@ -0,0 +1,843 @@
|
|||
/*
|
||||
* cxd224x-i2c.c - cxd224x NFC i2c driver
|
||||
*
|
||||
* Copyright (C) 2013- Sony Corporation.
|
||||
* Copyright (C) 2012 Broadcom Corporation.
|
||||
* Copyright (C) 2017 Sony Mobile Communications Inc.
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* NOTE: This file has been modified by Sony Mobile Communications Inc.
|
||||
* Modifications are licensed under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/clk.h>
|
||||
#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/pinctrl/consumer.h>
|
||||
#include <linux/poll.h>
|
||||
#include <linux/version.h>
|
||||
#include <linux/of_gpio.h>
|
||||
|
||||
#include "cxd224x.h"
|
||||
|
||||
#define CXD224X_WAKE_LOCK_TIMEOUT 1 /* wake timeout for HOSTINT (sec) */
|
||||
|
||||
/* do not change below */
|
||||
#define MAX_BUFFER_SIZE 780
|
||||
|
||||
/* Read data */
|
||||
#define PACKET_HEADER_SIZE_NCI (3)
|
||||
#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)
|
||||
|
||||
/* RESET */
|
||||
#define RESET_ASSERT_MS (1)
|
||||
|
||||
#define CXD224X_PINCTRL_ACTIVE "felica_active"
|
||||
#define CXD224X_PINCTRL_SUSPEND "felica_suspend"
|
||||
|
||||
struct cxd224x_dev {
|
||||
wait_queue_head_t read_wq;
|
||||
struct mutex read_mutex;
|
||||
struct i2c_client *client;
|
||||
struct miscdevice cxd224x_device;
|
||||
struct cxd224x_platform_data *gpio;
|
||||
bool irq_enabled;
|
||||
struct mutex lock;
|
||||
spinlock_t irq_enabled_lock;
|
||||
unsigned int users;
|
||||
unsigned int count_irq;
|
||||
/* Driver message queue */
|
||||
struct workqueue_struct *wqueue;
|
||||
struct work_struct qmsg;
|
||||
struct pinctrl *pinctrl;
|
||||
struct pinctrl_state *gpio_state_active;
|
||||
struct pinctrl_state *gpio_state_suspend;
|
||||
};
|
||||
|
||||
static struct cxd224x_dev *p_cxd224x_dev;
|
||||
static void cxd224x_enable_pon(struct cxd224x_dev *cxd224x_dev);
|
||||
static void cxd224x_conditional_disable_pon(struct cxd224x_dev *cxd224x_dev);
|
||||
|
||||
#if defined(CONFIG_NFC_CXD224X_RST) || defined(CONFIG_NFC_CXD224X_RST_MODULE)
|
||||
static void cxd224x_workqueue(struct work_struct *work)
|
||||
{
|
||||
struct cxd224x_dev *cxd224x_dev = container_of(work, struct cxd224x_dev, qmsg);
|
||||
unsigned long flags;
|
||||
|
||||
dev_info(&cxd224x_dev->client->dev, "%s, xrst assert\n", __func__);
|
||||
spin_lock_irqsave(&cxd224x_dev->irq_enabled_lock, flags);
|
||||
gpio_set_value(cxd224x_dev->gpio->rst_gpio, CXDNFC_RST_ACTIVE);
|
||||
cxd224x_dev->count_irq=0; /* clear irq */
|
||||
spin_unlock_irqrestore(&cxd224x_dev->irq_enabled_lock, flags);
|
||||
|
||||
msleep(RESET_ASSERT_MS);
|
||||
dev_info(&cxd224x_dev->client->dev, "%s, xrst deassert\n", __func__);
|
||||
gpio_set_value(cxd224x_dev->gpio->rst_gpio, ~CXDNFC_RST_ACTIVE & 0x1);
|
||||
}
|
||||
|
||||
static int __init init_wqueue(struct cxd224x_dev *cxd224x_dev)
|
||||
{
|
||||
INIT_WORK(&cxd224x_dev->qmsg, cxd224x_workqueue);
|
||||
cxd224x_dev->wqueue = create_workqueue("cxd224x-i2c_wrokq");
|
||||
if (cxd224x_dev->wqueue == NULL)
|
||||
return -EBUSY;
|
||||
return 0;
|
||||
}
|
||||
#endif /* CONFIG_NFC_CXD224X_RST */
|
||||
|
||||
static void cxd224x_init_stat(struct cxd224x_dev *cxd224x_dev)
|
||||
{
|
||||
cxd224x_dev->count_irq = 0;
|
||||
}
|
||||
|
||||
static void cxd224x_disable_irq(struct cxd224x_dev *cxd224x_dev)
|
||||
{
|
||||
unsigned long flags;
|
||||
spin_lock_irqsave(&cxd224x_dev->irq_enabled_lock, flags);
|
||||
if (cxd224x_dev->irq_enabled) {
|
||||
disable_irq_nosync(cxd224x_dev->client->irq);
|
||||
cxd224x_dev->irq_enabled = false;
|
||||
}
|
||||
spin_unlock_irqrestore(&cxd224x_dev->irq_enabled_lock, flags);
|
||||
}
|
||||
|
||||
static void cxd224x_enable_irq(struct cxd224x_dev *cxd224x_dev)
|
||||
{
|
||||
unsigned long flags;
|
||||
spin_lock_irqsave(&cxd224x_dev->irq_enabled_lock, flags);
|
||||
if (!cxd224x_dev->irq_enabled) {
|
||||
cxd224x_dev->irq_enabled = true;
|
||||
enable_irq(cxd224x_dev->client->irq);
|
||||
}
|
||||
spin_unlock_irqrestore(&cxd224x_dev->irq_enabled_lock, flags);
|
||||
}
|
||||
|
||||
static irqreturn_t cxd224x_dev_irq_handler(int irq, void *dev_id)
|
||||
{
|
||||
struct cxd224x_dev *cxd224x_dev = dev_id;
|
||||
unsigned long flags;
|
||||
|
||||
dev_info(&cxd224x_dev->client->dev, "%s\n", __func__);
|
||||
|
||||
spin_lock_irqsave(&cxd224x_dev->irq_enabled_lock, flags);
|
||||
cxd224x_dev->count_irq++;
|
||||
if (cxd224x_dev->irq_enabled) {
|
||||
cxd224x_enable_pon(cxd224x_dev);
|
||||
}
|
||||
spin_unlock_irqrestore(&cxd224x_dev->irq_enabled_lock, flags);
|
||||
|
||||
wake_up(&cxd224x_dev->read_wq);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static unsigned int cxd224x_dev_poll(struct file *filp, poll_table *wait)
|
||||
{
|
||||
struct cxd224x_dev *cxd224x_dev = filp->private_data;
|
||||
unsigned int mask = 0;
|
||||
unsigned long flags;
|
||||
|
||||
poll_wait(filp, &cxd224x_dev->read_wq, wait);
|
||||
|
||||
spin_lock_irqsave(&cxd224x_dev->irq_enabled_lock, flags);
|
||||
if (cxd224x_dev->count_irq > 0)
|
||||
{
|
||||
mask |= POLLIN | POLLRDNORM;
|
||||
}
|
||||
spin_unlock_irqrestore(&cxd224x_dev->irq_enabled_lock, flags);
|
||||
|
||||
if(mask)
|
||||
pm_wakeup_event(&cxd224x_dev->client->dev,
|
||||
jiffies_to_msecs(CXD224X_WAKE_LOCK_TIMEOUT*HZ));
|
||||
|
||||
return mask;
|
||||
}
|
||||
|
||||
static ssize_t cxd224x_dev_read(struct file *filp, char __user *buf,
|
||||
size_t count, loff_t *offset)
|
||||
{
|
||||
struct cxd224x_dev *cxd224x_dev = filp->private_data;
|
||||
unsigned char tmp[MAX_BUFFER_SIZE];
|
||||
int total, len, ret;
|
||||
unsigned long flags;
|
||||
|
||||
total = 0;
|
||||
len = 0;
|
||||
|
||||
if (count > MAX_BUFFER_SIZE)
|
||||
count = MAX_BUFFER_SIZE;
|
||||
|
||||
spin_lock_irqsave(&cxd224x_dev->irq_enabled_lock, flags);
|
||||
if (cxd224x_dev->count_irq > 0)
|
||||
cxd224x_dev->count_irq--;
|
||||
spin_unlock_irqrestore(&cxd224x_dev->irq_enabled_lock, flags);
|
||||
|
||||
mutex_lock(&cxd224x_dev->read_mutex);
|
||||
|
||||
ret = i2c_master_recv(cxd224x_dev->client, tmp, 3);
|
||||
if (ret == 3 && (tmp[0] != 0xff)) {
|
||||
total = ret;
|
||||
|
||||
len = tmp[PACKET_HEADER_SIZE_NCI-1];
|
||||
|
||||
/** make sure full packet fits in the buffer
|
||||
**/
|
||||
if (len > 0 && (len + total) <= count) {
|
||||
/** read the remainder of the packet.
|
||||
**/
|
||||
ret = i2c_master_recv(cxd224x_dev->client, tmp+total, len);
|
||||
if (ret == len)
|
||||
total += len;
|
||||
}
|
||||
}
|
||||
|
||||
mutex_unlock(&cxd224x_dev->read_mutex);
|
||||
|
||||
if (total > count || copy_to_user(buf, tmp, total)) {
|
||||
dev_err(&cxd224x_dev->client->dev,
|
||||
"failed to copy to user space, total = %d\n", total);
|
||||
total = -EFAULT;
|
||||
}
|
||||
|
||||
return total;
|
||||
}
|
||||
|
||||
static ssize_t cxd224x_dev_write(struct file *filp, const char __user *buf,
|
||||
size_t count, loff_t *offset)
|
||||
{
|
||||
struct cxd224x_dev *cxd224x_dev = filp->private_data;
|
||||
char tmp[MAX_BUFFER_SIZE];
|
||||
int ret;
|
||||
|
||||
if (count > MAX_BUFFER_SIZE) {
|
||||
dev_err(&cxd224x_dev->client->dev, "out of memory\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
if (copy_from_user(tmp, buf, count)) {
|
||||
dev_err(&cxd224x_dev->client->dev,
|
||||
"failed to copy from user space\n");
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
mutex_lock(&cxd224x_dev->read_mutex);
|
||||
/* Write data */
|
||||
|
||||
ret = i2c_master_send(cxd224x_dev->client, tmp, count);
|
||||
if (ret != count) {
|
||||
dev_err(&cxd224x_dev->client->dev,
|
||||
"failed to write %d\n", ret);
|
||||
ret = -EIO;
|
||||
}
|
||||
mutex_unlock(&cxd224x_dev->read_mutex);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int cxd224x_dev_open(struct inode *inode, struct file *filp)
|
||||
{
|
||||
int ret = 0;
|
||||
int call_enable = 0;
|
||||
struct cxd224x_dev *cxd224x_dev = container_of(filp->private_data,
|
||||
struct cxd224x_dev,
|
||||
cxd224x_device);
|
||||
filp->private_data = cxd224x_dev;
|
||||
mutex_lock(&cxd224x_dev->lock);
|
||||
if (!cxd224x_dev->users)
|
||||
{
|
||||
cxd224x_init_stat(cxd224x_dev);
|
||||
call_enable = 1;
|
||||
}
|
||||
cxd224x_dev->users++;
|
||||
if (call_enable)
|
||||
cxd224x_enable_irq(cxd224x_dev);
|
||||
mutex_unlock(&cxd224x_dev->lock);
|
||||
|
||||
dev_info(&cxd224x_dev->client->dev,
|
||||
"open %d,%d users=%d\n", imajor(inode), iminor(inode), cxd224x_dev->users);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int cxd224x_dev_release(struct inode *inode, struct file *filp)
|
||||
{
|
||||
int ret = 0;
|
||||
int call_disable = 0;
|
||||
struct cxd224x_dev *cxd224x_dev = filp->private_data;
|
||||
|
||||
mutex_lock(&cxd224x_dev->lock);
|
||||
cxd224x_dev->users--;
|
||||
if (!cxd224x_dev->users)
|
||||
{
|
||||
call_disable = 1;
|
||||
}
|
||||
if (call_disable)
|
||||
{
|
||||
cxd224x_disable_irq(cxd224x_dev);
|
||||
gpio_set_value(cxd224x_dev->gpio->wake_gpio, 0);
|
||||
}
|
||||
mutex_unlock(&cxd224x_dev->lock);
|
||||
|
||||
dev_info(&cxd224x_dev->client->dev,
|
||||
"release %d,%d users=%d\n", imajor(inode), iminor(inode), cxd224x_dev->users);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void cxd224x_enable_pon(struct cxd224x_dev *cxd224x_dev)
|
||||
{
|
||||
gpio_set_value(cxd224x_dev->gpio->wake_gpio, 1);
|
||||
}
|
||||
|
||||
static void cxd224x_conditional_disable_pon(struct cxd224x_dev *cxd224x_dev)
|
||||
{
|
||||
unsigned long flag;
|
||||
|
||||
spin_lock_irqsave(&cxd224x_dev->irq_enabled_lock, flag);
|
||||
/* Do not disable PON when data is available to be read */
|
||||
if (cxd224x_dev->count_irq == 0)
|
||||
gpio_set_value(cxd224x_dev->gpio->wake_gpio, 0);
|
||||
spin_unlock_irqrestore(&cxd224x_dev->irq_enabled_lock, flag);
|
||||
}
|
||||
|
||||
static long cxd224x_dev_unlocked_ioctl(struct file *filp,
|
||||
unsigned int cmd, unsigned long arg)
|
||||
{
|
||||
struct cxd224x_dev *cxd224x_dev = filp->private_data;
|
||||
|
||||
switch (cmd) {
|
||||
case CXDNFC_RST_CTL:
|
||||
#if defined(CONFIG_NFC_CXD224X_RST) || defined(CONFIG_NFC_CXD224X_RST_MODULE)
|
||||
dev_info(&cxd224x_dev->client->dev, "%s, rst arg=%d\n", __func__, (int)arg);
|
||||
return (queue_work(cxd224x_dev->wqueue, &cxd224x_dev->qmsg) ? 0 : 1);
|
||||
#endif
|
||||
break;
|
||||
case CXDNFC_POWER_CTL:
|
||||
#if defined(CONFIG_NFC_CXD224X_VEN) || defined(CONFIG_NFC_CXD224X_VEN_MODULE)
|
||||
if (arg == 0) {
|
||||
gpio_set_value(cxd224x_dev->en_gpio, 1);
|
||||
} else if (arg == 1) {
|
||||
gpio_set_value(cxd224x_dev->en_gpio, 0);
|
||||
} else {
|
||||
/* do nothing */
|
||||
}
|
||||
#else
|
||||
return 1; /* not support */
|
||||
#endif
|
||||
break;
|
||||
case CXDNFC_WAKE_CTL:
|
||||
if (arg == 0) {
|
||||
/* PON HIGH (normal power mode)*/
|
||||
cxd224x_enable_pon(cxd224x_dev);
|
||||
} else if (arg == 1) {
|
||||
/* PON LOW (low power mode) */
|
||||
cxd224x_conditional_disable_pon(cxd224x_dev);
|
||||
} else {
|
||||
/* do nothing */
|
||||
}
|
||||
break;
|
||||
default:
|
||||
dev_err(&cxd224x_dev->client->dev,
|
||||
"%s, unknown cmd (%x, %lx)\n", __func__, cmd, arg);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct file_operations cxd224x_dev_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.llseek = no_llseek,
|
||||
.poll = cxd224x_dev_poll,
|
||||
.read = cxd224x_dev_read,
|
||||
.write = cxd224x_dev_write,
|
||||
.open = cxd224x_dev_open,
|
||||
.release = cxd224x_dev_release,
|
||||
.unlocked_ioctl = cxd224x_dev_unlocked_ioctl
|
||||
};
|
||||
|
||||
static int cxd224x_pinctrl_init(struct device *dev, struct cxd224x_dev *cxd224x_dev)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
cxd224x_dev->pinctrl = devm_pinctrl_get(dev);
|
||||
if (IS_ERR_OR_NULL(cxd224x_dev->pinctrl)) {
|
||||
dev_err(dev, "error devm_pinctrl_get() failed err:%ld\n",
|
||||
PTR_ERR(cxd224x_dev->pinctrl));
|
||||
ret = PTR_ERR(cxd224x_dev->pinctrl);
|
||||
goto out;
|
||||
}
|
||||
|
||||
cxd224x_dev->gpio_state_active = pinctrl_lookup_state(
|
||||
cxd224x_dev->pinctrl, CXD224X_PINCTRL_ACTIVE);
|
||||
|
||||
if (IS_ERR_OR_NULL(cxd224x_dev->gpio_state_active)) {
|
||||
ret = PTR_ERR(cxd224x_dev->gpio_state_active);
|
||||
dev_info(dev, "note pinctrl_lookup_state(%s) err:%d\n",
|
||||
CXD224X_PINCTRL_ACTIVE, ret);
|
||||
goto out;
|
||||
}
|
||||
|
||||
cxd224x_dev->gpio_state_suspend = pinctrl_lookup_state(
|
||||
cxd224x_dev->pinctrl, CXD224X_PINCTRL_SUSPEND);
|
||||
|
||||
if (IS_ERR_OR_NULL(cxd224x_dev->gpio_state_suspend)) {
|
||||
ret = PTR_ERR(cxd224x_dev->gpio_state_suspend);
|
||||
dev_info(dev, "note pinctrl_lookup_state(%s) err:%d\n",
|
||||
CXD224X_PINCTRL_ACTIVE, ret);
|
||||
goto out;
|
||||
}
|
||||
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void cxd224x_pinctrl_select_state(struct device *dev, bool active)
|
||||
{
|
||||
struct cxd224x_dev *cxd224x_dev = dev_get_drvdata(dev);
|
||||
struct pinctrl_state *pins_state;
|
||||
const char *pins_state_name;
|
||||
|
||||
if (active) {
|
||||
pins_state = cxd224x_dev->gpio_state_active;
|
||||
pins_state_name = CXD224X_PINCTRL_ACTIVE;
|
||||
} else {
|
||||
pins_state = cxd224x_dev->gpio_state_suspend;
|
||||
pins_state_name = CXD224X_PINCTRL_SUSPEND;
|
||||
}
|
||||
|
||||
if (!IS_ERR_OR_NULL(pins_state)) {
|
||||
int ret = pinctrl_select_state(cxd224x_dev->pinctrl,
|
||||
pins_state);
|
||||
if (ret)
|
||||
dev_err(dev, "error pinctrl_select_state(%s) err:%d\n",
|
||||
pins_state_name, ret);
|
||||
} else {
|
||||
dev_err(dev,
|
||||
"error pinctrl state-name:'%s' is not configured\n",
|
||||
pins_state_name);
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(CONFIG_OF)
|
||||
static int cxd224x_parse_dt(struct device *dev,
|
||||
struct cxd224x_platform_data *pdata)
|
||||
{
|
||||
int ret=0;
|
||||
|
||||
/*nfc_int*/
|
||||
pdata->irq_gpio = of_get_named_gpio_flags(dev->of_node, "sony,nfc_int", 0,NULL);
|
||||
if (pdata->irq_gpio < 0) {
|
||||
pr_err( "failed to get \"nfc_int\"\n");
|
||||
goto dt_err;
|
||||
}
|
||||
|
||||
#if defined(CONFIG_NFC_CXD224X_VEN) || defined(CONFIG_NFC_CXD224X_VEN_MODULE)
|
||||
pdata->en_gpio = of_get_named_gpio_flags(dev->of_node, "sony,nfc_ven", 0,NULL);
|
||||
if (pdata->en_gpio< 0) {
|
||||
pr_err( "failed to get \"nfc_ven\"\n");
|
||||
goto dt_err;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(CONFIG_NFC_CXD224X_RST) || defined(CONFIG_NFC_CXD224X_RST_MODULE)
|
||||
pdata->rst_gpio = of_get_named_gpio_flags(dev->of_node, "sony,nfc_rst", 0,NULL);
|
||||
if (pdata->rst_gpio< 0) {
|
||||
pr_err( "failed to get \"nfc_rst\"\n");
|
||||
goto dt_err;
|
||||
}
|
||||
#endif
|
||||
|
||||
pdata->wake_gpio = of_get_named_gpio_flags(dev->of_node, "sony,nfc_wake", 0,NULL);
|
||||
if (pdata->wake_gpio< 0) {
|
||||
pr_err( "failed to get \"nfc_wake\"\n");
|
||||
goto dt_err;
|
||||
}
|
||||
return 0;
|
||||
|
||||
dt_err:
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
|
||||
static int cxd224x_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
int ret;
|
||||
struct cxd224x_platform_data *platform_data=NULL;
|
||||
struct cxd224x_dev *cxd224x_dev;
|
||||
struct clk *felica_clk = NULL;
|
||||
int irq_gpio_ok = 0;
|
||||
#if defined(CONFIG_NFC_CXD224X_VEN) || defined(CONFIG_NFC_CXD224X_VEN_MODULE)
|
||||
int en_gpio_ok = 0;
|
||||
#endif
|
||||
#if defined(CONFIG_NFC_CXD224X_RST) || defined(CONFIG_NFC_CXD224X_RST_MODULE)
|
||||
int rst_gpio_ok = 0;
|
||||
#endif
|
||||
int wake_gpio_ok = 0;
|
||||
|
||||
if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
|
||||
dev_err(&client->dev, "need I2C_FUNC_I2C\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
#if defined(CONFIG_OF)
|
||||
platform_data = kzalloc(sizeof(struct cxd224x_platform_data),
|
||||
GFP_KERNEL);
|
||||
if (platform_data == NULL) {
|
||||
dev_err(&client->dev, "failed to allocate memory\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
ret = cxd224x_parse_dt(&client->dev, platform_data);
|
||||
if (ret) {
|
||||
dev_err(&client->dev, "failed to parse device tree\n");
|
||||
kfree(platform_data);
|
||||
return -ENODEV;
|
||||
}
|
||||
#else
|
||||
platform_data = client->dev.platform_data;
|
||||
|
||||
dev_info(&client->dev, "%s, probing cxd224x driver flags = %x\n", __func__, client->flags);
|
||||
if (platform_data == NULL) {
|
||||
dev_err(&client->dev, "nfc probe fail\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
#endif
|
||||
dev_info(&client->dev, "%s, rst_gpio(%d)\n", __func__, platform_data->rst_gpio);
|
||||
dev_info(&client->dev, "%s, ven_gpio(%d)\n", __func__, platform_data->en_gpio);
|
||||
dev_info(&client->dev, "%s, irq_gpio(%d)\n", __func__, platform_data->irq_gpio);
|
||||
dev_info(&client->dev, "%s, wake_gpio(%d)\n", __func__, platform_data->wake_gpio);
|
||||
|
||||
irq_gpio_ok=1;
|
||||
client->irq = gpio_to_irq(platform_data->irq_gpio);
|
||||
if (client->irq<0)
|
||||
{
|
||||
dev_err(&client->dev, "%s, failed to allocate irq=%d\n", __func__, client->irq);
|
||||
return -ENODEV;
|
||||
}
|
||||
dev_info(&client->dev, "%s, irq(%d)\n", __func__, client->irq);
|
||||
|
||||
#if defined(CONFIG_NFC_CXD224X_VEN) || defined(CONFIG_NFC_CXD224X_VEN_MODULE)
|
||||
ret = gpio_request_one(platform_data->en_gpio, GPIOF_OUT_INIT_LOW, "nfc_cen");
|
||||
if (ret)
|
||||
goto err_exit;
|
||||
en_gpio_ok=1;
|
||||
ret = gpio_direction_output(platform_data->en_gpio, 0);
|
||||
if (ret)
|
||||
return -ENODEV;
|
||||
#endif
|
||||
|
||||
#if defined(CONFIG_NFC_CXD224X_RST) || defined(CONFIG_NFC_CXD224X_RST_MODULE)
|
||||
ret = gpio_request_one(platform_data->rst_gpio, GPIOF_OUT_INIT_HIGH, "nfc_rst");
|
||||
if (ret)
|
||||
goto err_exit;
|
||||
rst_gpio_ok=1;
|
||||
ret = gpio_direction_output(platform_data->rst_gpio, ~CXDNFC_RST_ACTIVE & 0x1);
|
||||
if (ret)
|
||||
return -ENODEV;
|
||||
dev_info(&client->dev, "%s, xrst deassert\n", __func__);
|
||||
#endif
|
||||
|
||||
ret = gpio_request_one(platform_data->wake_gpio, GPIOF_OUT_INIT_LOW, "nfc_wake");
|
||||
if (ret)
|
||||
goto err_exit;
|
||||
wake_gpio_ok=1;
|
||||
ret = gpio_direction_output(platform_data->wake_gpio,0);
|
||||
|
||||
cxd224x_dev = kzalloc(sizeof(*cxd224x_dev), GFP_KERNEL);
|
||||
if (cxd224x_dev == NULL) {
|
||||
dev_err(&client->dev,
|
||||
"failed to allocate memory for module data\n");
|
||||
ret = -ENOMEM;
|
||||
goto err_exit;
|
||||
}
|
||||
|
||||
cxd224x_dev->client = client;
|
||||
cxd224x_dev->gpio = platform_data;
|
||||
cxd224x_dev->users =0;
|
||||
|
||||
/* init mutex and queues */
|
||||
init_waitqueue_head(&cxd224x_dev->read_wq);
|
||||
mutex_init(&cxd224x_dev->read_mutex);
|
||||
mutex_init(&cxd224x_dev->lock);
|
||||
spin_lock_init(&cxd224x_dev->irq_enabled_lock);
|
||||
|
||||
#if defined(CONFIG_NFC_CXD224X_RST) || defined(CONFIG_NFC_CXD224X_RST_MODULE)
|
||||
if (init_wqueue(cxd224x_dev) != 0) {
|
||||
dev_err(&client->dev, "init workqueue failed\n");
|
||||
goto err_exit;
|
||||
}
|
||||
#endif
|
||||
|
||||
ret = cxd224x_pinctrl_init(&client->dev, cxd224x_dev);
|
||||
if (ret) {
|
||||
dev_err(&client->dev, "pinctrl_init failed\n");
|
||||
goto err_pinctrl_init;
|
||||
}
|
||||
|
||||
cxd224x_dev->cxd224x_device.minor = MISC_DYNAMIC_MINOR;
|
||||
cxd224x_dev->cxd224x_device.name = "cxd224x-i2c";
|
||||
cxd224x_dev->cxd224x_device.fops = &cxd224x_dev_fops;
|
||||
|
||||
ret = misc_register(&cxd224x_dev->cxd224x_device);
|
||||
if (ret) {
|
||||
dev_err(&client->dev, "misc_register failed\n");
|
||||
goto err_misc_register;
|
||||
}
|
||||
|
||||
felica_clk = clk_get(&client->dev, "felica_clk");
|
||||
if (IS_ERR(felica_clk)) {
|
||||
dev_info(&client->dev, "Couldn't get felica_clk, XTAL should be used\n");
|
||||
} else {
|
||||
ret = clk_prepare_enable(felica_clk);
|
||||
if (ret) {
|
||||
dev_err(&client->dev, "failed to enable felica_clk\n");
|
||||
goto err_clk_enable;
|
||||
}
|
||||
}
|
||||
|
||||
/* 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\n", client->irq);
|
||||
cxd224x_dev->irq_enabled = true;
|
||||
ret = request_irq(client->irq, cxd224x_dev_irq_handler,
|
||||
IRQF_TRIGGER_FALLING, client->name, cxd224x_dev);
|
||||
if (ret) {
|
||||
dev_err(&client->dev, "request_irq failed\n");
|
||||
goto err_request_irq_failed;
|
||||
}
|
||||
cxd224x_disable_irq(cxd224x_dev);
|
||||
i2c_set_clientdata(client, cxd224x_dev);
|
||||
cxd224x_pinctrl_select_state(&client->dev, true);
|
||||
device_init_wakeup(&client->dev, 1);
|
||||
p_cxd224x_dev = cxd224x_dev;
|
||||
|
||||
dev_info(&client->dev,
|
||||
"%s, probing cxd224x driver exited successfully\n",
|
||||
__func__);
|
||||
return 0;
|
||||
|
||||
err_request_irq_failed:
|
||||
misc_deregister(&cxd224x_dev->cxd224x_device);
|
||||
err_clk_enable:
|
||||
clk_put(felica_clk);
|
||||
err_pinctrl_init:
|
||||
err_misc_register:
|
||||
mutex_destroy(&cxd224x_dev->read_mutex);
|
||||
kfree(cxd224x_dev);
|
||||
err_exit:
|
||||
if(irq_gpio_ok)
|
||||
gpio_free(platform_data->irq_gpio);
|
||||
#if defined(CONFIG_NFC_CXD224X_VEN) || defined(CONFIG_NFC_CXD224X_VEN_MODULE)
|
||||
if(en_gpio_ok)
|
||||
gpio_free(platform_data->en_gpio);
|
||||
#endif
|
||||
#if defined(CONFIG_NFC_CXD224X_RST) || defined(CONFIG_NFC_CXD224X_RST_MODULE)
|
||||
if(rst_gpio_ok)
|
||||
gpio_free(platform_data->rst_gpio);
|
||||
#endif
|
||||
if(wake_gpio_ok)
|
||||
gpio_free(platform_data->wake_gpio);
|
||||
|
||||
#if defined(CONFIG_OF)
|
||||
if(platform_data)
|
||||
kfree(platform_data);
|
||||
#endif
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int cxd224x_remove(struct i2c_client *client)
|
||||
{
|
||||
struct cxd224x_dev *cxd224x_dev;
|
||||
|
||||
cxd224x_dev = i2c_get_clientdata(client);
|
||||
|
||||
device_wakeup_disable(&client->dev);
|
||||
|
||||
free_irq(client->irq, cxd224x_dev);
|
||||
misc_deregister(&cxd224x_dev->cxd224x_device);
|
||||
mutex_destroy(&cxd224x_dev->read_mutex);
|
||||
if(cxd224x_dev->gpio)
|
||||
{
|
||||
gpio_free(cxd224x_dev->gpio->irq_gpio);
|
||||
gpio_free(cxd224x_dev->gpio->wake_gpio);
|
||||
|
||||
#if defined(CONFIG_NFC_CXD224X_VEN) || defined(CONFIG_NFC_CXD224X_VEN_MODULE)
|
||||
gpio_free(cxd224x_dev->gpio->en_gpio);
|
||||
#endif
|
||||
#if defined(CONFIG_NFC_CXD224X_RST) || defined(CONFIG_NFC_CXD224X_RST_MODULE)
|
||||
gpio_free(cxd224x_dev->gpio->rst_gpio);
|
||||
#endif
|
||||
|
||||
#if defined(CONFIG_OF)
|
||||
|
||||
kfree(cxd224x_dev->gpio);
|
||||
#endif
|
||||
}
|
||||
kfree(cxd224x_dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int cxd224x_suspend(struct device *dev)
|
||||
{
|
||||
struct cxd224x_dev *cxd224x_dev = dev_get_drvdata(dev);
|
||||
|
||||
dev_info(dev, "%s\n", __func__);
|
||||
|
||||
if (device_may_wakeup(&cxd224x_dev->client->dev)) {
|
||||
int irq = gpio_to_irq(cxd224x_dev->gpio->irq_gpio);
|
||||
enable_irq_wake(irq);
|
||||
}
|
||||
|
||||
cxd224x_pinctrl_select_state(dev, false);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cxd224x_resume(struct device *dev)
|
||||
{
|
||||
struct cxd224x_dev *cxd224x_dev = dev_get_drvdata(dev);
|
||||
|
||||
dev_info(dev, "%s\n", __func__);
|
||||
|
||||
if (device_may_wakeup(&cxd224x_dev->client->dev)) {
|
||||
int irq = gpio_to_irq(cxd224x_dev->gpio->irq_gpio);
|
||||
disable_irq_wake(irq);
|
||||
}
|
||||
|
||||
cxd224x_pinctrl_select_state(dev, true);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct dev_pm_ops cxd224x_pm_ops = {
|
||||
.suspend = cxd224x_suspend,
|
||||
.resume = cxd224x_resume,
|
||||
};
|
||||
#endif
|
||||
|
||||
static const struct i2c_device_id cxd224x_id[] = {
|
||||
{"cxd224x-i2c", 0},
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, cxd224x_id);
|
||||
|
||||
static struct i2c_driver cxd224x_driver = {
|
||||
.id_table = cxd224x_id,
|
||||
.probe = cxd224x_probe,
|
||||
.remove = cxd224x_remove,
|
||||
.driver = {
|
||||
.owner = THIS_MODULE,
|
||||
.name = "cxd224x-i2c",
|
||||
},
|
||||
};
|
||||
|
||||
static const struct platform_device_id cxd224x_pm_ops_id[] = {
|
||||
{ "cxd224x-pm-ops", 0 },
|
||||
{ }
|
||||
};
|
||||
|
||||
static const struct of_device_id cxd224x_pm_ops_match_table[] = {
|
||||
{ .compatible = "sony,cxd224x-pm-ops" },
|
||||
{ }
|
||||
};
|
||||
|
||||
static int cxd224x_pm_ops_probe(struct platform_device *pdev)
|
||||
{
|
||||
dev_info(&pdev->dev, "%s, probing cxd224x PM OPS driver\n", __func__);
|
||||
|
||||
if (!p_cxd224x_dev)
|
||||
return -ENODEV;
|
||||
|
||||
platform_set_drvdata(pdev, p_cxd224x_dev);
|
||||
|
||||
dev_info(&pdev->dev,
|
||||
"%s, probing cxd224x PM OPS driver successfully\n", __func__);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver cxd224x_pm_ops_driver = {
|
||||
.id_table = cxd224x_pm_ops_id,
|
||||
.probe = cxd224x_pm_ops_probe,
|
||||
.driver = {
|
||||
.name = "cxd224x-pm-ops",
|
||||
.of_match_table = cxd224x_pm_ops_match_table,
|
||||
#ifdef CONFIG_PM
|
||||
.pm = &cxd224x_pm_ops,
|
||||
#endif
|
||||
},
|
||||
};
|
||||
|
||||
/*
|
||||
* module load/unload record keeping
|
||||
*/
|
||||
|
||||
int cxd224x_dev_init(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = i2c_add_driver(&cxd224x_driver);
|
||||
if (ret)
|
||||
goto exit;
|
||||
|
||||
ret = platform_driver_register(&cxd224x_pm_ops_driver);
|
||||
if (ret)
|
||||
goto exit_del_i2c_driver;
|
||||
|
||||
return 0;
|
||||
|
||||
exit_del_i2c_driver:
|
||||
i2c_del_driver(&cxd224x_driver);
|
||||
|
||||
exit:
|
||||
return ret;
|
||||
}
|
||||
|
||||
void cxd224x_dev_exit(void)
|
||||
{
|
||||
if (p_cxd224x_dev)
|
||||
p_cxd224x_dev = NULL;
|
||||
|
||||
i2c_del_driver(&cxd224x_driver);
|
||||
platform_driver_unregister(&cxd224x_pm_ops_driver);
|
||||
}
|
||||
|
||||
MODULE_AUTHOR("Sony");
|
||||
MODULE_DESCRIPTION("NFC cxd224x driver");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
47
drivers/misc/carillon/cxd224x/cxd224x.h
Normal file
47
drivers/misc/carillon/cxd224x/cxd224x.h
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
/*
|
||||
* cxd224x-i2c.c - cxd224x NFC driver
|
||||
*
|
||||
* Copyright (C) 2013- Sony Corporation.
|
||||
* 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 _CXD224X_H
|
||||
#define _CXD224X_H
|
||||
|
||||
#define CXDNFC_MAGIC 'S'
|
||||
/*
|
||||
* CXDNFC power control via ioctl
|
||||
* CXDNFC_POWER_CTL(0): power off
|
||||
* CXDNFC_POWER_CTL(1): power on
|
||||
* CXDNFC_WAKE_CTL(0): PON HIGH (normal power mode)
|
||||
* CXDNFC_WAKE_CTL(1): PON LOW (low power mode)
|
||||
* CXDNFC_WAKE_RST(): assert XRST
|
||||
*/
|
||||
#define CXDNFC_POWER_CTL _IO(CXDNFC_MAGIC, 0x01)
|
||||
#define CXDNFC_WAKE_CTL _IO(CXDNFC_MAGIC, 0x02)
|
||||
#define CXDNFC_RST_CTL _IO(CXDNFC_MAGIC, 0x03)
|
||||
|
||||
#define CXDNFC_RST_ACTIVE 1 /* ActiveHi = 1, ActiveLow = 0 */
|
||||
|
||||
struct cxd224x_platform_data {
|
||||
unsigned int irq_gpio;
|
||||
unsigned int en_gpio;
|
||||
unsigned int wake_gpio;
|
||||
unsigned int rst_gpio;
|
||||
};
|
||||
|
||||
#endif
|
||||
54
drivers/misc/carillon/main_module.c
Normal file
54
drivers/misc/carillon/main_module.c
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
/* main_module.c
|
||||
*
|
||||
* Copyright (C) 2017 Sony Mobile Communications Inc.
|
||||
*
|
||||
* 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; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*/
|
||||
#include <linux/init.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
|
||||
extern int bd7602_dev_init(void);
|
||||
extern void bd7602_dev_exit(void);
|
||||
extern int cxd224x_dev_init(void);
|
||||
extern void cxd224x_dev_exit(void);
|
||||
|
||||
static int __init nfc_carillon_init(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
pr_info("%s\n", __func__);
|
||||
|
||||
ret = bd7602_dev_init();
|
||||
if (ret)
|
||||
goto exit;
|
||||
|
||||
ret = cxd224x_dev_init();
|
||||
if (ret)
|
||||
goto exit_del_bd7602_driver;
|
||||
|
||||
return 0;
|
||||
|
||||
exit_del_bd7602_driver:
|
||||
bd7602_dev_exit();
|
||||
|
||||
exit:
|
||||
return ret;
|
||||
}
|
||||
module_init(nfc_carillon_init);
|
||||
|
||||
static void __exit nfc_carillon_exit(void)
|
||||
{
|
||||
pr_info("%s\n", __func__);
|
||||
|
||||
cxd224x_dev_exit();
|
||||
bd7602_dev_exit();
|
||||
}
|
||||
module_exit(nfc_carillon_exit);
|
||||
|
||||
MODULE_AUTHOR("Sony Mobile Communications Inc.");
|
||||
MODULE_DESCRIPTION("NFC Carillon's component driver");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
739
drivers/misc/cxd224x-i2c.c
Normal file
739
drivers/misc/cxd224x-i2c.c
Normal file
|
|
@ -0,0 +1,739 @@
|
|||
/* drivers/misc/cxd224x-i2c.c
|
||||
*
|
||||
* Copyright (C) 2013 Sony Corporation.
|
||||
*
|
||||
* 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; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*/
|
||||
/*
|
||||
* Copyright (C) 2015 Sony Mobile Communications Inc.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include <linux/clk.h>
|
||||
#include <linux/cxd224x.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/miscdevice.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of_gpio.h>
|
||||
#include <linux/pinctrl/consumer.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/poll.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/wakelock.h>
|
||||
|
||||
/* do not change below */
|
||||
#define MAX_BUFFER_SIZE 780
|
||||
|
||||
/* Read data */
|
||||
#define PACKET_HEADER_SIZE_NCI (3)
|
||||
#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)
|
||||
|
||||
#define CXD224X_WAKE_LOCK_TIMEOUT 3
|
||||
#define CXD224X_WAKE_LOCK_NAME CXD224X_DEVICE_NAME
|
||||
#define CXD224X_WAKE_LOCK_TIMEOUT_LP 3
|
||||
#define CXD224X_WAKE_LOCK_NAME_LP "cxd224x-i2c-lp"
|
||||
|
||||
#define CXD224X_PINCTRL_ACTIVE "felica_active"
|
||||
#define CXD224X_PINCTRL_SUSPEND "felica_suspend"
|
||||
|
||||
struct cxd224x_dev {
|
||||
wait_queue_head_t read_wq;
|
||||
struct mutex read_mutex;
|
||||
struct i2c_client *client;
|
||||
struct miscdevice cxd224x_device;
|
||||
struct pinctrl *pinctrl;
|
||||
struct pinctrl_state *gpio_state_active;
|
||||
struct pinctrl_state *gpio_state_suspend;
|
||||
unsigned int irq_gpio;
|
||||
unsigned int wake_gpio;
|
||||
bool irq_enabled;
|
||||
spinlock_t irq_enabled_lock;
|
||||
unsigned int count_irq;
|
||||
struct wake_lock wakelock;
|
||||
struct wake_lock wakelock_lp;
|
||||
};
|
||||
|
||||
static struct cxd224x_dev *p_cxd224x_dev;
|
||||
|
||||
static void cxd224x_init_stat(struct cxd224x_dev *cxd224x_dev)
|
||||
{
|
||||
cxd224x_dev->count_irq = 0;
|
||||
}
|
||||
|
||||
static void cxd224x_disable_irq(struct cxd224x_dev *cxd224x_dev)
|
||||
{
|
||||
unsigned long flags;
|
||||
spin_lock_irqsave(&cxd224x_dev->irq_enabled_lock, flags);
|
||||
if (cxd224x_dev->irq_enabled) {
|
||||
disable_irq_nosync(cxd224x_dev->client->irq);
|
||||
cxd224x_dev->irq_enabled = false;
|
||||
}
|
||||
spin_unlock_irqrestore(&cxd224x_dev->irq_enabled_lock, flags);
|
||||
}
|
||||
|
||||
static void cxd224x_enable_irq(struct cxd224x_dev *cxd224x_dev)
|
||||
{
|
||||
unsigned long flags;
|
||||
spin_lock_irqsave(&cxd224x_dev->irq_enabled_lock, flags);
|
||||
if (!cxd224x_dev->irq_enabled) {
|
||||
cxd224x_dev->irq_enabled = true;
|
||||
enable_irq(cxd224x_dev->client->irq);
|
||||
}
|
||||
spin_unlock_irqrestore(&cxd224x_dev->irq_enabled_lock, flags);
|
||||
}
|
||||
|
||||
static irqreturn_t cxd224x_dev_irq_handler(int irq, void *dev_id)
|
||||
{
|
||||
struct cxd224x_dev *cxd224x_dev = dev_id;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&cxd224x_dev->irq_enabled_lock, flags);
|
||||
cxd224x_dev->count_irq++;
|
||||
spin_unlock_irqrestore(&cxd224x_dev->irq_enabled_lock, flags);
|
||||
wake_up(&cxd224x_dev->read_wq);
|
||||
|
||||
dev_info(&cxd224x_dev->client->dev, "%s\n", __func__);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static unsigned int cxd224x_dev_poll(struct file *filp, poll_table *wait)
|
||||
{
|
||||
struct cxd224x_dev *cxd224x_dev = filp->private_data;
|
||||
unsigned int mask = 0;
|
||||
unsigned long flags;
|
||||
|
||||
poll_wait(filp, &cxd224x_dev->read_wq, wait);
|
||||
|
||||
spin_lock_irqsave(&cxd224x_dev->irq_enabled_lock, flags);
|
||||
if (cxd224x_dev->count_irq > 0) {
|
||||
cxd224x_dev->count_irq--;
|
||||
mask |= POLLIN | POLLRDNORM;
|
||||
}
|
||||
spin_unlock_irqrestore(&cxd224x_dev->irq_enabled_lock, flags);
|
||||
|
||||
if (mask)
|
||||
wake_lock_timeout(&cxd224x_dev->wakelock,
|
||||
CXD224X_WAKE_LOCK_TIMEOUT * HZ);
|
||||
return mask;
|
||||
}
|
||||
|
||||
static ssize_t cxd224x_dev_read(struct file *filp, char __user *buf,
|
||||
size_t count, loff_t *offset)
|
||||
{
|
||||
struct cxd224x_dev *cxd224x_dev = filp->private_data;
|
||||
unsigned char tmp[MAX_BUFFER_SIZE];
|
||||
int total, len, ret;
|
||||
|
||||
total = 0;
|
||||
len = 0;
|
||||
|
||||
if (count > MAX_BUFFER_SIZE)
|
||||
count = MAX_BUFFER_SIZE;
|
||||
|
||||
mutex_lock(&cxd224x_dev->read_mutex);
|
||||
|
||||
ret = i2c_master_recv(cxd224x_dev->client, tmp, 3);
|
||||
if (ret == 3 && (tmp[0] != 0xff)) {
|
||||
total = ret;
|
||||
|
||||
len = tmp[PACKET_HEADER_SIZE_NCI-1];
|
||||
|
||||
/* make sure full packet fits in the buffer */
|
||||
if (len > 0 && (len + total) <= count) {
|
||||
/* read the remainder of the packet */
|
||||
ret = i2c_master_recv(cxd224x_dev->client, tmp+total,
|
||||
len);
|
||||
if (ret == len)
|
||||
total += len;
|
||||
}
|
||||
}
|
||||
|
||||
mutex_unlock(&cxd224x_dev->read_mutex);
|
||||
|
||||
if (total > count || copy_to_user(buf, tmp, total)) {
|
||||
dev_err(&cxd224x_dev->client->dev,
|
||||
"failed to copy to user space, total = %d\n", total);
|
||||
total = -EFAULT;
|
||||
}
|
||||
|
||||
return total;
|
||||
}
|
||||
|
||||
static ssize_t cxd224x_dev_write(struct file *filp, const char __user *buf,
|
||||
size_t count, loff_t *offset)
|
||||
{
|
||||
struct cxd224x_dev *cxd224x_dev = filp->private_data;
|
||||
char tmp[MAX_BUFFER_SIZE];
|
||||
int ret;
|
||||
|
||||
if (count > MAX_BUFFER_SIZE) {
|
||||
dev_err(&cxd224x_dev->client->dev, "out of memory\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
if (copy_from_user(tmp, buf, count)) {
|
||||
dev_err(&cxd224x_dev->client->dev,
|
||||
"failed to copy from user space\n");
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
mutex_lock(&cxd224x_dev->read_mutex);
|
||||
/* Write data */
|
||||
|
||||
ret = i2c_master_send(cxd224x_dev->client, tmp, count);
|
||||
if (ret != count) {
|
||||
dev_err(&cxd224x_dev->client->dev,
|
||||
"failed to write %d\n", ret);
|
||||
ret = -EIO;
|
||||
}
|
||||
mutex_unlock(&cxd224x_dev->read_mutex);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int cxd224x_dev_open(struct inode *inode, struct file *filp)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
struct cxd224x_dev *cxd224x_dev = container_of(filp->private_data,
|
||||
struct cxd224x_dev,
|
||||
cxd224x_device);
|
||||
filp->private_data = cxd224x_dev;
|
||||
cxd224x_init_stat(cxd224x_dev);
|
||||
cxd224x_enable_irq(cxd224x_dev);
|
||||
dev_info(&cxd224x_dev->client->dev,
|
||||
"%d,%d\n", imajor(inode), iminor(inode));
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct file_operations cxd224x_dev_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.llseek = no_llseek,
|
||||
.poll = cxd224x_dev_poll,
|
||||
.read = cxd224x_dev_read,
|
||||
.write = cxd224x_dev_write,
|
||||
.open = cxd224x_dev_open,
|
||||
};
|
||||
|
||||
static ssize_t cxd224x_dev_wake_ctl_show(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
u8 mode;
|
||||
int ret, size;
|
||||
struct cxd224x_dev *cxd224x_dev = dev_get_drvdata(dev);
|
||||
|
||||
|
||||
if (!cxd224x_dev) {
|
||||
ret = -ENODEV;
|
||||
goto err;
|
||||
}
|
||||
ret = gpio_get_value_cansleep(cxd224x_dev->wake_gpio);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "%s: Unable to read WAKE GPIO\n", __func__);
|
||||
goto err;
|
||||
}
|
||||
mode = !ret;
|
||||
size = sizeof(mode);
|
||||
memcpy(buf, &mode, size);
|
||||
|
||||
return size;
|
||||
|
||||
err:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static ssize_t cxd224x_dev_wake_ctl_store(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t size)
|
||||
{
|
||||
u8 mode;
|
||||
int ret, value;
|
||||
struct cxd224x_dev *cxd224x_dev = dev_get_drvdata(dev);
|
||||
|
||||
if (!cxd224x_dev) {
|
||||
ret = -ENODEV;
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (sizeof(mode) != size) {
|
||||
dev_err(dev, "%s: Invalid size %ld\n", __func__, size);
|
||||
ret = -EINVAL;
|
||||
goto err;
|
||||
}
|
||||
|
||||
memcpy(&mode, buf, size);
|
||||
value = mode ? 0 : 1;
|
||||
if (1 == value) {
|
||||
wake_lock_timeout(&cxd224x_dev->wakelock_lp, CXD224X_WAKE_LOCK_TIMEOUT_LP*HZ);
|
||||
gpio_set_value_cansleep(cxd224x_dev->wake_gpio, 1);
|
||||
} else if (0 == value) {
|
||||
gpio_set_value_cansleep(cxd224x_dev->wake_gpio, 0);
|
||||
wake_unlock(&cxd224x_dev->wakelock_lp);
|
||||
} else {
|
||||
/* do nothing */
|
||||
}
|
||||
return size;
|
||||
|
||||
err:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct device_attribute cxd224x_sysfs_attrs[] = {
|
||||
__ATTR(wake_ctl, S_IRUSR | S_IWUSR, cxd224x_dev_wake_ctl_show,
|
||||
cxd224x_dev_wake_ctl_store),
|
||||
};
|
||||
|
||||
static int cxd224x_pinctrl_init(struct device *dev, struct cxd224x_dev *cxd224x_dev)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
cxd224x_dev->pinctrl = devm_pinctrl_get(dev);
|
||||
if (IS_ERR_OR_NULL(cxd224x_dev->pinctrl)) {
|
||||
dev_err(dev, "error devm_pinctrl_get() failed err:%ld\n",
|
||||
PTR_ERR(cxd224x_dev->pinctrl));
|
||||
ret = PTR_ERR(cxd224x_dev->pinctrl);
|
||||
goto out;
|
||||
}
|
||||
|
||||
cxd224x_dev->gpio_state_active = pinctrl_lookup_state(
|
||||
cxd224x_dev->pinctrl, CXD224X_PINCTRL_ACTIVE);
|
||||
|
||||
if (IS_ERR_OR_NULL(cxd224x_dev->gpio_state_active)) {
|
||||
ret = PTR_ERR(cxd224x_dev->gpio_state_active);
|
||||
dev_info(dev, "note pinctrl_lookup_state(%s) err:%d\n",
|
||||
CXD224X_PINCTRL_ACTIVE, ret);
|
||||
goto out;
|
||||
}
|
||||
|
||||
cxd224x_dev->gpio_state_suspend = pinctrl_lookup_state(
|
||||
cxd224x_dev->pinctrl, CXD224X_PINCTRL_SUSPEND);
|
||||
|
||||
if (IS_ERR_OR_NULL(cxd224x_dev->gpio_state_suspend)) {
|
||||
ret = PTR_ERR(cxd224x_dev->gpio_state_suspend);
|
||||
dev_info(dev, "note pinctrl_lookup_state(%s) err:%d\n",
|
||||
CXD224X_PINCTRL_ACTIVE, ret);
|
||||
goto out;
|
||||
}
|
||||
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void cxd224x_pinctrl_select_state(struct device *dev, bool active)
|
||||
{
|
||||
struct cxd224x_dev *cxd224x_dev = dev_get_drvdata(dev);
|
||||
struct pinctrl_state *pins_state;
|
||||
const char *pins_state_name;
|
||||
|
||||
if (active) {
|
||||
pins_state = cxd224x_dev->gpio_state_active;
|
||||
pins_state_name = CXD224X_PINCTRL_ACTIVE;
|
||||
} else {
|
||||
pins_state = cxd224x_dev->gpio_state_suspend;
|
||||
pins_state_name = CXD224X_PINCTRL_SUSPEND;
|
||||
}
|
||||
|
||||
if (!IS_ERR_OR_NULL(pins_state)) {
|
||||
int ret = pinctrl_select_state(cxd224x_dev->pinctrl,
|
||||
pins_state);
|
||||
if (ret)
|
||||
dev_err(dev, "error pinctrl_select_state(%s) err:%d\n",
|
||||
pins_state_name, ret);
|
||||
} else {
|
||||
dev_err(dev,
|
||||
"error pinctrl state-name:'%s' is not configured\n",
|
||||
pins_state_name);
|
||||
}
|
||||
}
|
||||
|
||||
static int cxd224x_create_sysfs_entries(struct device *dev)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(cxd224x_sysfs_attrs); i++)
|
||||
if (device_create_file(dev, cxd224x_sysfs_attrs + i))
|
||||
goto err_create_file;
|
||||
return 0;
|
||||
|
||||
err_create_file:
|
||||
for (i = i - 1; i >= 0; i--)
|
||||
device_remove_file(dev, cxd224x_sysfs_attrs + i);
|
||||
|
||||
dev_err(dev, "Unable to create sysfs interfaces\n");
|
||||
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
static void cxd224x_remove_sysfs_entries(struct device *dev)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(cxd224x_sysfs_attrs); i++)
|
||||
device_remove_file(dev, cxd224x_sysfs_attrs + i);
|
||||
}
|
||||
|
||||
static struct cxd224x_platform_data *cxd224x_dt_to_pdata(struct device *dev)
|
||||
{
|
||||
int ret;
|
||||
struct device_node *node = dev->of_node;
|
||||
struct cxd224x_platform_data *pdata;
|
||||
|
||||
pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
|
||||
if (!pdata) {
|
||||
dev_err(dev, "unable to allocate memory for platform data\n");
|
||||
ret = -ENOMEM;
|
||||
goto err;
|
||||
}
|
||||
pdata->irq_gpio = of_get_named_gpio(node, "cxd224x,irq_gpio", 0);
|
||||
if (!gpio_is_valid(pdata->irq_gpio)) {
|
||||
ret = -EINVAL;
|
||||
dev_err(dev,
|
||||
"failed to of_get_named_gpio(cxd224x,irq_gpio)\n");
|
||||
goto err;
|
||||
}
|
||||
pdata->wake_gpio = of_get_named_gpio(node, "cxd224x,wake_gpio", 0);
|
||||
if (!gpio_is_valid(pdata->wake_gpio)) {
|
||||
ret = -EINVAL;
|
||||
dev_err(dev,
|
||||
"failed to of_get_named_gpio(cxd224x,wake_gpio)\n");
|
||||
goto err;
|
||||
}
|
||||
|
||||
return pdata;
|
||||
err:
|
||||
if (pdata)
|
||||
devm_kfree(dev, pdata);
|
||||
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
static int cxd224x_gpio_request(struct device *dev,
|
||||
struct cxd224x_platform_data *pdata)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = gpio_request(pdata->irq_gpio, "cxd224x_irq");
|
||||
if (ret)
|
||||
goto err_irq;
|
||||
ret = gpio_request(pdata->wake_gpio, "cxd224x_wake");
|
||||
if (ret)
|
||||
goto err_wake;
|
||||
|
||||
return 0;
|
||||
|
||||
err_wake:
|
||||
gpio_free(pdata->wake_gpio);
|
||||
err_irq:
|
||||
gpio_free(pdata->irq_gpio);
|
||||
|
||||
dev_err(dev, "%s: failed to gpio request %d\n", __func__, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void cxd224x_gpio_free(struct device *dev,
|
||||
struct cxd224x_platform_data *pdata)
|
||||
{
|
||||
gpio_free(pdata->wake_gpio);
|
||||
gpio_free(pdata->irq_gpio);
|
||||
}
|
||||
|
||||
static int cxd224x_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
int ret;
|
||||
struct cxd224x_platform_data *platform_data;
|
||||
struct cxd224x_dev *cxd224x_dev;
|
||||
struct clk *felica_clk = NULL;
|
||||
|
||||
platform_data = client->dev.platform_data;
|
||||
|
||||
dev_info(&client->dev, "%s, probing cxd224x driver flags = %x\n",
|
||||
__func__, client->flags);
|
||||
|
||||
if (client->dev.of_node) {
|
||||
platform_data = cxd224x_dt_to_pdata(&client->dev);
|
||||
if (IS_ERR(platform_data)) {
|
||||
ret = PTR_ERR(platform_data);
|
||||
dev_err(&client->dev, "failed to dt_to_pdata\n");
|
||||
goto err_exit;
|
||||
}
|
||||
client->dev.platform_data = platform_data;
|
||||
}
|
||||
|
||||
if (platform_data == NULL) {
|
||||
dev_err(&client->dev, "nfc probe fail\n");
|
||||
ret = -ENODEV;
|
||||
goto err_exit;
|
||||
}
|
||||
|
||||
if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
|
||||
dev_err(&client->dev, "need I2C_FUNC_I2C\n");
|
||||
ret = -ENODEV;
|
||||
goto err_exit;
|
||||
}
|
||||
|
||||
ret = cxd224x_gpio_request(&client->dev, platform_data);
|
||||
if (ret) {
|
||||
dev_err(&client->dev, "failed to gpio_request\n");
|
||||
goto err_exit;
|
||||
}
|
||||
|
||||
cxd224x_dev = kzalloc(sizeof(*cxd224x_dev), GFP_KERNEL);
|
||||
if (cxd224x_dev == NULL) {
|
||||
dev_err(&client->dev,
|
||||
"failed to allocate memory for module data\n");
|
||||
ret = -ENOMEM;
|
||||
goto err_kzalloc;
|
||||
}
|
||||
|
||||
cxd224x_dev->irq_gpio = platform_data->irq_gpio;
|
||||
cxd224x_dev->wake_gpio = platform_data->wake_gpio;
|
||||
cxd224x_dev->client = client;
|
||||
wake_lock_init(&cxd224x_dev->wakelock, WAKE_LOCK_SUSPEND,
|
||||
CXD224X_WAKE_LOCK_NAME);
|
||||
wake_lock_init(&cxd224x_dev->wakelock_lp, WAKE_LOCK_SUSPEND, CXD224X_WAKE_LOCK_NAME_LP);
|
||||
|
||||
/* init mutex and queues */
|
||||
init_waitqueue_head(&cxd224x_dev->read_wq);
|
||||
mutex_init(&cxd224x_dev->read_mutex);
|
||||
spin_lock_init(&cxd224x_dev->irq_enabled_lock);
|
||||
|
||||
ret = cxd224x_pinctrl_init(&client->dev, cxd224x_dev);
|
||||
if (ret) {
|
||||
dev_err(&client->dev, "pinctrl_init failed\n");
|
||||
goto err_pinctrl_init;
|
||||
}
|
||||
|
||||
cxd224x_dev->cxd224x_device.minor = MISC_DYNAMIC_MINOR;
|
||||
cxd224x_dev->cxd224x_device.name = CXD224X_DEVICE_NAME;
|
||||
cxd224x_dev->cxd224x_device.fops = &cxd224x_dev_fops;
|
||||
|
||||
ret = misc_register(&cxd224x_dev->cxd224x_device);
|
||||
if (ret) {
|
||||
dev_err(&client->dev, "misc_register failed\n");
|
||||
goto err_misc_register;
|
||||
}
|
||||
|
||||
felica_clk = clk_get(&client->dev, "felica_clk");
|
||||
if (IS_ERR(felica_clk)) {
|
||||
dev_err(&client->dev, "Couldn't get felica_clk\n");
|
||||
goto err_clk;
|
||||
}
|
||||
ret = clk_prepare_enable(felica_clk);
|
||||
if (ret) {
|
||||
dev_err(&client->dev, "failed to enable felica_clk\n");
|
||||
goto err_clk_enable;
|
||||
}
|
||||
|
||||
ret = cxd224x_create_sysfs_entries(&client->dev);
|
||||
if (ret) {
|
||||
dev_err(&client->dev, "create_sysfs_entries failed\n");
|
||||
goto err_create_sysfs_entries;
|
||||
}
|
||||
|
||||
/* 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\n", client->irq);
|
||||
cxd224x_dev->irq_enabled = true;
|
||||
ret = request_threaded_irq(client->irq, NULL, cxd224x_dev_irq_handler,
|
||||
IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
|
||||
client->name, cxd224x_dev);
|
||||
if (ret) {
|
||||
dev_err(&client->dev, "request_irq failed\n");
|
||||
goto err_request_irq_failed;
|
||||
}
|
||||
|
||||
cxd224x_disable_irq(cxd224x_dev);
|
||||
i2c_set_clientdata(client, cxd224x_dev);
|
||||
cxd224x_pinctrl_select_state(&client->dev, true);
|
||||
device_init_wakeup(&client->dev, 1);
|
||||
p_cxd224x_dev = cxd224x_dev;
|
||||
|
||||
dev_info(&client->dev,
|
||||
"%s, probing cxd224x driver exited successfully\n",
|
||||
__func__);
|
||||
|
||||
return 0;
|
||||
|
||||
err_request_irq_failed:
|
||||
cxd224x_remove_sysfs_entries(&client->dev);
|
||||
err_create_sysfs_entries:
|
||||
misc_deregister(&cxd224x_dev->cxd224x_device);
|
||||
err_clk_enable:
|
||||
clk_put(felica_clk);
|
||||
err_clk:
|
||||
err_misc_register:
|
||||
err_pinctrl_init:
|
||||
mutex_destroy(&cxd224x_dev->read_mutex);
|
||||
wake_lock_destroy(&cxd224x_dev->wakelock);
|
||||
kzfree(cxd224x_dev);
|
||||
err_kzalloc:
|
||||
cxd224x_gpio_free(&client->dev, platform_data);
|
||||
err_exit:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int cxd224x_remove(struct i2c_client *client)
|
||||
{
|
||||
struct cxd224x_dev *cxd224x_dev = i2c_get_clientdata(client);
|
||||
|
||||
free_irq(client->irq, cxd224x_dev);
|
||||
cxd224x_remove_sysfs_entries(&client->dev);
|
||||
misc_deregister(&cxd224x_dev->cxd224x_device);
|
||||
mutex_destroy(&cxd224x_dev->read_mutex);
|
||||
wake_lock_destroy(&cxd224x_dev->wakelock);
|
||||
wake_lock_destroy(&cxd224x_dev->wakelock_lp);
|
||||
i2c_set_clientdata(client, NULL);
|
||||
kzfree(cxd224x_dev);
|
||||
cxd224x_gpio_free(&client->dev, client->dev.platform_data);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int cxd224x_suspend(struct device *dev)
|
||||
{
|
||||
struct cxd224x_dev *cxd224x_dev = dev_get_drvdata(dev);
|
||||
dev_info(dev, "%s\n", __func__);
|
||||
|
||||
if (device_may_wakeup(&cxd224x_dev->client->dev))
|
||||
enable_irq_wake(cxd224x_dev->client->irq);
|
||||
|
||||
cxd224x_pinctrl_select_state(dev, false);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cxd224x_resume(struct device *dev)
|
||||
{
|
||||
struct cxd224x_dev *cxd224x_dev = dev_get_drvdata(dev);
|
||||
dev_info(dev, "%s\n", __func__);
|
||||
|
||||
if (device_may_wakeup(&cxd224x_dev->client->dev))
|
||||
disable_irq_wake(cxd224x_dev->client->irq);
|
||||
|
||||
cxd224x_pinctrl_select_state(dev, true);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct dev_pm_ops cxd224x_pm_ops = {
|
||||
.suspend = cxd224x_suspend,
|
||||
.resume = cxd224x_resume,
|
||||
};
|
||||
#endif
|
||||
|
||||
static const struct i2c_device_id cxd224x_id[] = {
|
||||
{ CXD224X_DEVICE_NAME, 0 },
|
||||
{ }
|
||||
};
|
||||
|
||||
static struct of_device_id cxd224x_match_table[] = {
|
||||
{ .compatible = "sony,cxd224x", },
|
||||
{ },
|
||||
};
|
||||
|
||||
static struct i2c_driver cxd224x_driver = {
|
||||
.id_table = cxd224x_id,
|
||||
.probe = cxd224x_probe,
|
||||
.remove = cxd224x_remove,
|
||||
.driver = {
|
||||
.owner = THIS_MODULE,
|
||||
.name = CXD224X_DEVICE_NAME,
|
||||
.of_match_table = cxd224x_match_table,
|
||||
},
|
||||
};
|
||||
|
||||
static const struct platform_device_id cxd224x_pm_ops_id[] = {
|
||||
{ CXD224X_PM_OPS_DEVICE_NAME, 0 },
|
||||
{ }
|
||||
};
|
||||
|
||||
static const struct of_device_id cxd224x_pm_ops_match_table[] = {
|
||||
{ .compatible = "sony,cxd224x-pm-ops" },
|
||||
{ }
|
||||
};
|
||||
|
||||
static int cxd224x_pm_ops_probe(struct platform_device *pdev)
|
||||
{
|
||||
dev_info(&pdev->dev, "%s, probing cxd224x PM OPS driver\n", __func__);
|
||||
|
||||
if (!p_cxd224x_dev)
|
||||
return -ENODEV;
|
||||
|
||||
platform_set_drvdata(pdev, p_cxd224x_dev);
|
||||
|
||||
dev_info(&pdev->dev,
|
||||
"%s, probing cxd224x PM OPS driver successfully\n", __func__);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver cxd224x_pm_ops_driver = {
|
||||
.id_table = cxd224x_pm_ops_id,
|
||||
.probe = cxd224x_pm_ops_probe,
|
||||
.driver = {
|
||||
.name = CXD224X_PM_OPS_DEVICE_NAME,
|
||||
.of_match_table = cxd224x_pm_ops_match_table,
|
||||
#ifdef CONFIG_PM
|
||||
.pm = &cxd224x_pm_ops,
|
||||
#endif
|
||||
},
|
||||
};
|
||||
|
||||
static int __init cxd224x_dev_init(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = i2c_add_driver(&cxd224x_driver);
|
||||
|
||||
if (ret)
|
||||
goto exit;
|
||||
|
||||
ret = platform_driver_register(&cxd224x_pm_ops_driver);
|
||||
|
||||
if (ret)
|
||||
goto exit_del_i2c_driver;
|
||||
|
||||
return 0;
|
||||
|
||||
exit_del_i2c_driver:
|
||||
i2c_del_driver(&cxd224x_driver);
|
||||
|
||||
exit:
|
||||
return ret;
|
||||
}
|
||||
module_init(cxd224x_dev_init);
|
||||
|
||||
static void __exit cxd224x_dev_exit(void)
|
||||
{
|
||||
if (p_cxd224x_dev)
|
||||
p_cxd224x_dev = NULL;
|
||||
|
||||
i2c_del_driver(&cxd224x_driver);
|
||||
platform_driver_unregister(&cxd224x_pm_ops_driver);
|
||||
}
|
||||
module_exit(cxd224x_dev_exit);
|
||||
|
||||
MODULE_AUTHOR("Sony");
|
||||
MODULE_DESCRIPTION("NFC cxd224x driver");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
29
include/linux/cxd224x.h
Normal file
29
include/linux/cxd224x.h
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
/* include/linux/cxd224x.h
|
||||
*
|
||||
* Copyright (C) 2012 Sony Corporation.
|
||||
*
|
||||
* 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; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*/
|
||||
/*
|
||||
* Copyright (C) 2015 Sony Mobile Communications Inc.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef _CXD224X_H
|
||||
#define _CXD224X_H
|
||||
|
||||
#define CXD224X_DEVICE_NAME "cxd224x-i2c"
|
||||
#define CXD224X_PM_OPS_DEVICE_NAME "cxd224x-pm-ops"
|
||||
|
||||
struct cxd224x_platform_data {
|
||||
unsigned int irq_gpio;
|
||||
unsigned int wake_gpio;
|
||||
};
|
||||
|
||||
#endif
|
||||
Loading…
Reference in a new issue