diff --git a/drivers/misc/nxp-pn5xx/Kconfig b/drivers/misc/nxp-pn5xx/Kconfig new file mode 100644 index 0000000000000000000000000000000000000000..4e770199c829980e3e4f74c4134c3f8c8597e0cb --- /dev/null +++ b/drivers/misc/nxp-pn5xx/Kconfig @@ -0,0 +1,12 @@ +config NFC_NXP_PN5XX + tristate "NXP PN5XX based driver" + depends on I2C + select CRC_CCITT + default n + ---help--- + NXP PN5XX driver based on I2C. + This is a driver to provides I2C access to PN5xx NFC Controller devices + + To compile this driver as a module, choose m here. The module will + be called pn5xx_i2c. + Say N if unsure. diff --git a/drivers/misc/nxp-pn5xx/Makefile b/drivers/misc/nxp-pn5xx/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..6a3d9ca1a4f9d165ef52d320de3dc85a3d27a38b --- /dev/null +++ b/drivers/misc/nxp-pn5xx/Makefile @@ -0,0 +1 @@ +obj-y += pn5xx_i2c.o diff --git a/drivers/misc/nxp-pn5xx/pn5xx_i2c.c b/drivers/misc/nxp-pn5xx/pn5xx_i2c.c new file mode 100644 index 0000000000000000000000000000000000000000..6c80871db53fcfd3fe5f68332805ff0b8b9be90e --- /dev/null +++ b/drivers/misc/nxp-pn5xx/pn5xx_i2c.c @@ -0,0 +1,695 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2010 Trusted Logic S.A. + * modifications copyright (C) 2015 NXP B.V. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "pn5xx_i2c.h" +#include +#include +#include + +#define MAX_BUFFER_SIZE 512 + +#define MODE_OFF 0 +#define MODE_RUN 1 +#define MODE_FW 2 + +/* Only pn548, pn547 and pn544 are supported */ +#define CHIP "pn544" +#define DRIVER_CARD "PN54x NFC" +#define DRIVER_DESC "NFC driver for PN54x Family" + +#ifndef CONFIG_OF +#define CONFIG_OF +#endif + +struct pn54x_dev { + wait_queue_head_t read_wq; + struct mutex read_mutex; + struct i2c_client *client; + struct miscdevice pn54x_device; + int ven_gpio; + int firm_gpio; + int irq_gpio; + int clkreq_gpio; + struct regulator *pvdd_reg; + struct regulator *vbat_reg; + struct regulator *pmuvcc_reg; + struct regulator *sevdd_reg; + bool irq_enabled; + spinlock_t irq_enabled_lock; +}; + +/********************************************************** + * Interrupt control and handler + **********************************************************/ +static void pn54x_disable_irq(struct pn54x_dev *pn54x_dev) +{ + unsigned long flags; + + spin_lock_irqsave(&pn54x_dev->irq_enabled_lock, flags); + if (pn54x_dev->irq_enabled) { + disable_irq_nosync(pn54x_dev->client->irq); + pn54x_dev->irq_enabled = false; + } + spin_unlock_irqrestore(&pn54x_dev->irq_enabled_lock, flags); +} + +static irqreturn_t pn54x_dev_irq_handler(int irq, void *dev_id) +{ + struct pn54x_dev *pn54x_dev = dev_id; + + pn54x_disable_irq(pn54x_dev); + + /* Wake up waiting readers */ + wake_up(&pn54x_dev->read_wq); + + return IRQ_HANDLED; +} + +/********************************************************** + * private functions + **********************************************************/ +static int pn544_enable(struct pn54x_dev *dev, int mode) +{ + int r; + + /* turn on the regulators */ + /* -- if the regulators were specified, they're required */ + if (dev->pvdd_reg) { + r = regulator_enable(dev->pvdd_reg); + if (r < 0) { + pr_err("%s: not able to enable pvdd\n", __func__); + return r; + } + } + if (dev->vbat_reg != NULL) { + r = regulator_enable(dev->vbat_reg); + if (r < 0) { + pr_err("%s: not able to enable vbat\n", __func__); + goto enable_exit0; + } + } + if (dev->pmuvcc_reg != NULL) { + r = regulator_enable(dev->pmuvcc_reg); + if (r < 0) { + pr_err("%s: not able to enable pmuvcc\n", __func__); + goto enable_exit1; + } + } + if (dev->sevdd_reg != NULL) { + r = regulator_enable(dev->sevdd_reg); + if (r < 0) { + pr_err("%s: not able to enable sevdd\n", __func__); + goto enable_exit2; + } + } + + if (mode == MODE_RUN) { + if (gpio_is_valid(dev->firm_gpio)) + gpio_set_value_cansleep(dev->firm_gpio, 0); + gpio_set_value_cansleep(dev->ven_gpio, 1); + msleep(100); + } else if (mode == MODE_FW) { + /* power on with firmware download (requires hw reset) + */ + gpio_set_value(dev->ven_gpio, 1); + msleep(20); + if (gpio_is_valid(dev->firm_gpio)) { + gpio_set_value(dev->firm_gpio, 1); + } else { + pr_err("%s Unused Firm GPIO %d\n", __func__, mode); + return GPIO_UNUSED; + } + msleep(20); + gpio_set_value(dev->ven_gpio, 0); + msleep(100); + gpio_set_value(dev->ven_gpio, 1); + msleep(20); + } else { + pr_err("%s bad arg %d\n", __func__, mode); + return -EINVAL; + } + + return 0; + +enable_exit2: + if (dev->pmuvcc_reg) + regulator_disable(dev->pmuvcc_reg); +enable_exit1: + if (dev->vbat_reg) + regulator_disable(dev->vbat_reg); +enable_exit0: + if (dev->pvdd_reg) + regulator_disable(dev->pvdd_reg); + + return r; +} + +static void pn544_disable(struct pn54x_dev *dev) +{ + /* power off */ + if (gpio_is_valid(dev->firm_gpio)) + gpio_set_value_cansleep(dev->firm_gpio, 0); + gpio_set_value_cansleep(dev->ven_gpio, 0); + msleep(100); + + if (dev->sevdd_reg) + regulator_disable(dev->sevdd_reg); + if (dev->pmuvcc_reg) + regulator_disable(dev->pmuvcc_reg); + if (dev->vbat_reg) + regulator_disable(dev->vbat_reg); + if (dev->pvdd_reg) + regulator_disable(dev->pvdd_reg); + +} + +/********************************************************** + * driver functions + **********************************************************/ +static ssize_t pn54x_dev_read(struct file *filp, char __user *buf, + size_t count, loff_t *offset) +{ + struct pn54x_dev *pn54x_dev = filp->private_data; + char tmp[MAX_BUFFER_SIZE]; + int ret; + + if (count > MAX_BUFFER_SIZE) + count = MAX_BUFFER_SIZE; + + pr_debug("%s : reading %zu bytes.\n", __func__, count); + + mutex_lock(&pn54x_dev->read_mutex); + + if (!gpio_get_value(pn54x_dev->irq_gpio)) { + if (filp->f_flags & O_NONBLOCK) { + ret = -EAGAIN; + goto fail; + } + + while (1) { + pn54x_dev->irq_enabled = true; + enable_irq(pn54x_dev->client->irq); + ret = wait_event_interruptible( + pn54x_dev->read_wq, + !pn54x_dev->irq_enabled); + + pn54x_disable_irq(pn54x_dev); + + if (ret) + goto fail; + + if (gpio_get_value(pn54x_dev->irq_gpio)) + break; + + pr_warning("%s: spurious interrupt detected\n", __func__); + } + } + + /* Read data */ + ret = i2c_master_recv(pn54x_dev->client, tmp, count); + + mutex_unlock(&pn54x_dev->read_mutex); + udelay(1000); + + if (ret < 0) { + pr_err("%s: i2c_master_recv returned %d\n", __func__, ret); + return ret; + } + if (ret > count) { + pr_err("%s: received too many bytes from i2c (%d)\n", + __func__, ret); + return -EIO; + } + if (copy_to_user(buf, tmp, ret)) { + pr_warning("%s : failed to copy to user space\n", __func__); + return -EFAULT; + } + return ret; + +fail: + mutex_unlock(&pn54x_dev->read_mutex); + return ret; +} + +static ssize_t pn54x_dev_write(struct file *filp, const char __user *buf, + size_t count, loff_t *offset) +{ + struct pn54x_dev *pn54x_dev; + char tmp[MAX_BUFFER_SIZE]; + int ret; + + pn54x_dev = filp->private_data; + + if (count > MAX_BUFFER_SIZE) + count = MAX_BUFFER_SIZE; + + if (copy_from_user(tmp, buf, count)) { + pr_err("%s : failed to copy from user space\n", __func__); + return -EFAULT; + } + + pr_debug("%s : writing %zu bytes.\n", __func__, count); + /* Write data */ + ret = i2c_master_send(pn54x_dev->client, tmp, count); + if (ret != count) { + pr_err("%s : i2c_master_send returned %d\n", __func__, ret); + ret = -EIO; + } + + udelay(1000); + + return ret; +} + +static int pn54x_dev_open(struct inode *inode, struct file *filp) +{ + struct pn54x_dev *pn54x_dev = container_of(filp->private_data, + struct pn54x_dev, pn54x_device); + + filp->private_data = pn54x_dev; + pn544_enable(pn54x_dev, MODE_RUN); + + return 0; +} + +static int pn54x_dev_release(struct inode *inode, struct file *filp) +{ + struct pn54x_dev *pn54x_dev = filp->private_data; + + pn544_disable(pn54x_dev); + + return 0; +} + +static long pn54x_dev_ioctl(struct file *filp, unsigned int cmd, + unsigned long arg) +{ + struct pn54x_dev *pn54x_dev = filp->private_data; + + switch (cmd) { + case PN544_SET_PWR: + if (arg == 2) { + /* power on w/FW */ + if (pn544_enable(pn54x_dev, arg) == GPIO_UNUSED) + return GPIO_UNUSED; + } else if (arg == 1) { + /* power on */ + pn544_enable(pn54x_dev, arg); + } else if (arg == 0) { + /* power off */ + pn544_disable(pn54x_dev); + } else { + pr_err("%s bad SET_PWR arg %lu\n", __func__, arg); + return -EINVAL; + } + break; + case PN54X_CLK_REQ: + if (arg == 1) { + if (gpio_is_valid(pn54x_dev->clkreq_gpio)) { + gpio_set_value(pn54x_dev->clkreq_gpio, 1); + } else { + pr_err("%s Unused Clkreq GPIO %lu\n", __func__, arg); + return GPIO_UNUSED; + } + } else if (arg == 0) { + if (gpio_is_valid(pn54x_dev->clkreq_gpio)) { + gpio_set_value(pn54x_dev->clkreq_gpio, 0); + } else { + pr_err("%s Unused Clkreq GPIO %lu\n", __func__, arg); + return GPIO_UNUSED; + } + } else { + pr_err("%s bad CLK_REQ arg %lu\n", __func__, arg); + return -EINVAL; + } + break; + default: + pr_err("%s bad ioctl %u\n", __func__, cmd); + return -EINVAL; + } + + return 0; +} + +static const struct file_operations pn54x_dev_fops = { + .owner = THIS_MODULE, + .llseek = no_llseek, + .read = pn54x_dev_read, + .write = pn54x_dev_write, + .open = pn54x_dev_open, + .release = pn54x_dev_release, + .unlocked_ioctl = pn54x_dev_ioctl, +}; + + +/* + * Handlers for alternative sources of platform_data + */ +#ifdef CONFIG_OF +/* + * Translate OpenFirmware node properties into platform_data + */ +static int pn54x_get_pdata(struct device *dev, struct pn544_i2c_platform_data *pdata) +{ + struct device_node *node; + u32 flags; + int val; + + /* make sure there is actually a device tree node */ + node = dev->of_node; + if (!node) + return -ENODEV; + + memset(pdata, 0, sizeof(*pdata)); + + /* read the dev tree data */ + + /* ven pin - enable's power to the chip - REQUIRED */ + val = of_get_named_gpio_flags(node, "enable-gpios", 0, &flags); + if (val < 0) { + dev_err(dev, "VEN GPIO error getting from OF node\n"); + return val; + } + pdata->ven_gpio = val; + + /* firm pin - controls firmware download - OPTIONAL */ + val = of_get_named_gpio_flags(node, "firmware-gpios", 0, &flags); + if (val < 0) { + dev_err(dev, "VEN GPIO error getting from OF node\n"); + return val; + } + pdata->firm_gpio = val; + + /* irq pin - data available irq - REQUIRED */ + val = of_get_named_gpio_flags(node, "interrupt-gpios", 0, &flags); + if (val < 0) { + dev_err(dev, "VEN GPIO error getting from OF node\n"); + return val; + } + pdata->irq_gpio = val; + + /* clkreq pin - controls the clock to the PN547 - OPTIONAL */ + val = of_get_named_gpio_flags(node, "nxp,pn54x-clkreq", 0, &flags); + if (val < 0) { + dev_err(dev, "VEN GPIO error getting from OF node\n"); + return val; + } + pdata->clkreq_gpio = val; + + /* handle the regulator lines - these are optional + * PVdd - pad Vdd (544, 547) + * Vbat - Battery (544, 547) + * PMUVcc - UICC Power (544, 547) + * SEVdd - SE Power (544) + * + * Will attempt to load a matching Regulator Resource for each + * If no resource is provided, then the input will not be controlled + * Example: if only PVdd is provided, it is the only one that will be + * turned on/off. + */ + pdata->pvdd_reg = regulator_get(dev, "nxp,pn54x-pvdd"); + if (IS_ERR(pdata->pvdd_reg)) { + pr_err("%s: could not get nxp,pn54x-pvdd, rc=%ld\n", __func__, PTR_ERR(pdata->pvdd_reg)); + pdata->pvdd_reg = NULL; + } + + pdata->vbat_reg = regulator_get(dev, "nxp,pn54x-vbat"); + if (IS_ERR(pdata->vbat_reg)) { + pr_err("%s: could not get nxp,pn54x-vbat, rc=%ld\n", __func__, PTR_ERR(pdata->vbat_reg)); + pdata->vbat_reg = NULL; + } + + pdata->pmuvcc_reg = regulator_get(dev, "nxp,pn54x-pmuvcc"); + if (IS_ERR(pdata->pmuvcc_reg)) { + pr_err("%s: could not get nxp,pn54x-pmuvcc, rc=%ld\n", __func__, PTR_ERR(pdata->pmuvcc_reg)); + pdata->pmuvcc_reg = NULL; + } + + pdata->sevdd_reg = regulator_get(dev, "nxp,pn54x-sevdd"); + if (IS_ERR(pdata->sevdd_reg)) { + pr_err("%s: could not get nxp,pn54x-sevdd, rc=%ld\n", __func__, PTR_ERR(pdata->sevdd_reg)); + pdata->sevdd_reg = NULL; + } + + return 0; +} +#else +static int pn54x_get_pdata(struct device *dev, struct pn544_i2c_platform_data *pdata) +{ + pdata = dev->platform_data; + return 0; +} +#endif + + +/* + * pn54x_probe + */ +static int pn54x_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + int ret; + struct pn544_i2c_platform_data *pdata; // gpio values, from board file or DT + struct pn544_i2c_platform_data tmp_pdata; + struct pn54x_dev *pn54x_dev; // internal device specific data + + /* ---- retrieve the platform data ---- */ + /* If the dev.platform_data is NULL, then */ + /* attempt to read from the device tree */ + if (!client->dev.platform_data) { + ret = pn54x_get_pdata(&(client->dev), &tmp_pdata); + if (ret) + return ret; + pdata = &tmp_pdata; + } else { + pdata = client->dev.platform_data; + } + + if (pdata == NULL) { + pr_err("%s : nfc probe fail\n", __func__); + return -ENODEV; + } + + /* validate the adapter has basic I2C functionality */ + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { + pr_err("%s : need I2C_FUNC_I2C\n", __func__); + return -ENODEV; + } + + /* reserve the GPIO pins */ + ret = gpio_request(pdata->irq_gpio, "nfc_int"); + if (ret) { + pr_err("%s :not able to get GPIO irq_gpio\n", __func__); + return -ENODEV; + } + ret = gpio_to_irq(pdata->irq_gpio); + if (ret < 0) { + pr_err("%s :not able to map GPIO irq_gpio to an IRQ\n", __func__); + goto err_ven; + } else { + client->irq = ret; + } + + ret = gpio_request(pdata->ven_gpio, "nfc_ven"); + if (ret) { + pr_err("%s :not able to get GPIO ven_gpio\n", __func__); + goto err_ven; + } + + if (gpio_is_valid(pdata->firm_gpio)) { + ret = gpio_request(pdata->firm_gpio, "nfc_firm"); + if (ret) { + pr_err("%s :not able to get GPIO firm_gpio\n", __func__); + goto err_firm; + } + } + + if (gpio_is_valid(pdata->clkreq_gpio)) { + ret = gpio_request(pdata->clkreq_gpio, "nfc_clkreq"); + if (ret) { + pr_err("%s :not able to get GPIO clkreq_gpio\n", __func__); + goto err_clkreq; + } + } + + /* allocate the pn54x driver information structure */ + pn54x_dev = kzalloc(sizeof(*pn54x_dev), GFP_KERNEL); + if (pn54x_dev == NULL) { + dev_err(&client->dev, "failed to allocate memory for module data\n"); + ret = -ENOMEM; + goto err_exit; + } + + /* store the platform data in the driver info struct */ + pn54x_dev->irq_gpio = pdata->irq_gpio; + pn54x_dev->ven_gpio = pdata->ven_gpio; + pn54x_dev->firm_gpio = pdata->firm_gpio; + pn54x_dev->clkreq_gpio = pdata->clkreq_gpio; + pn54x_dev->pvdd_reg = pdata->pvdd_reg; + pn54x_dev->vbat_reg = pdata->vbat_reg; + pn54x_dev->pmuvcc_reg = pdata->pmuvcc_reg; + pn54x_dev->sevdd_reg = pdata->sevdd_reg; + + pn54x_dev->client = client; + + /* finish configuring the I/O */ + ret = gpio_direction_input(pn54x_dev->irq_gpio); + if (ret < 0) { + pr_err("%s :not able to set irq_gpio as input\n", __func__); + goto err_free; + } + + ret = gpio_direction_output(pn54x_dev->ven_gpio, 0); + if (ret < 0) { + pr_err("%s : not able to set ven_gpio as output\n", __func__); + goto err_exit; + } + + if (gpio_is_valid(pn54x_dev->firm_gpio)) { + ret = gpio_direction_output(pn54x_dev->firm_gpio, 0); + if (ret < 0) { + pr_err("%s : not able to set firm_gpio as output\n", + __func__); + goto err_exit; + } + } + + if (gpio_is_valid(pn54x_dev->clkreq_gpio)) { + ret = gpio_direction_output(pn54x_dev->clkreq_gpio, 0); + if (ret < 0) { + pr_err("%s : not able to set clkreq_gpio as output\n", + __func__); + goto err_exit; + } + } + + /* init mutex and queues */ + init_waitqueue_head(&pn54x_dev->read_wq); + mutex_init(&pn54x_dev->read_mutex); + spin_lock_init(&pn54x_dev->irq_enabled_lock); + + /* register as a misc device - character based with one entry point */ + pn54x_dev->pn54x_device.minor = MISC_DYNAMIC_MINOR; + pn54x_dev->pn54x_device.name = CHIP; + pn54x_dev->pn54x_device.fops = &pn54x_dev_fops; + ret = misc_register(&pn54x_dev->pn54x_device); + if (ret) { + pr_err("%s : misc_register failed\n", __FILE__); + goto err_misc_register; + } + + /* request irq. the irq is set whenever the chip has data available + * for reading. it is cleared when all data has been read. + */ + pn54x_dev->irq_enabled = true; + ret = request_irq(client->irq, pn54x_dev_irq_handler, + IRQF_TRIGGER_HIGH, client->name, pn54x_dev); + if (ret) { + dev_err(&client->dev, "request_irq failed\n"); + goto err_request_irq_failed; + } + pn54x_disable_irq(pn54x_dev); + + i2c_set_clientdata(client, pn54x_dev); + + return 0; + +err_request_irq_failed: + misc_deregister(&pn54x_dev->pn54x_device); +err_misc_register: +err_free: + kfree(pn54x_dev); +err_exit: + if (gpio_is_valid(pdata->clkreq_gpio)) + gpio_free(pdata->clkreq_gpio); +err_clkreq: + if (gpio_is_valid(pdata->firm_gpio)) + gpio_free(pdata->firm_gpio); +err_firm: + gpio_free(pdata->ven_gpio); +err_ven: + gpio_free(pdata->irq_gpio); + return ret; +} + +static int pn54x_remove(struct i2c_client *client) +{ + struct pn54x_dev *pn54x_dev; + + pn54x_dev = i2c_get_clientdata(client); + free_irq(client->irq, pn54x_dev); + misc_deregister(&pn54x_dev->pn54x_device); + mutex_destroy(&pn54x_dev->read_mutex); + gpio_free(pn54x_dev->irq_gpio); + gpio_free(pn54x_dev->ven_gpio); + if (gpio_is_valid(pn54x_dev->firm_gpio)) + gpio_free(pn54x_dev->firm_gpio); + if (gpio_is_valid(pn54x_dev->clkreq_gpio)) + gpio_free(pn54x_dev->clkreq_gpio); + regulator_put(pn54x_dev->pvdd_reg); + regulator_put(pn54x_dev->vbat_reg); + regulator_put(pn54x_dev->pmuvcc_reg); + regulator_put(pn54x_dev->sevdd_reg); + + kfree(pn54x_dev); + + return 0; +} + +/* + * + */ +#ifdef CONFIG_OF +static struct of_device_id pn54x_dt_match[] = { + { .compatible = "nxp,pn547", }, + { .compatible = "nxp,pn544", }, + { .compatible = "nxp,pn7150", }, + {}, +}; +MODULE_DEVICE_TABLE(of, pn54x_dt_match); +#endif + +static const struct i2c_device_id pn54x_id[] = { + { "pn547", 0 }, + { }, +}; +MODULE_DEVICE_TABLE(i2c, pn54x_id); + +static struct i2c_driver pn54x_driver = { + .id_table = pn54x_id, + .probe = pn54x_probe, + .remove = pn54x_remove, + .driver = { + .owner = THIS_MODULE, + .name = "pn544", + .of_match_table = pn54x_dt_match, + }, +}; + +/* + * module load/unload record keeping + */ +module_i2c_driver(pn54x_driver); + +MODULE_AUTHOR("Sylvain Fonteneau"); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE("GPL"); diff --git a/drivers/misc/nxp-pn5xx/pn5xx_i2c.h b/drivers/misc/nxp-pn5xx/pn5xx_i2c.h new file mode 100644 index 0000000000000000000000000000000000000000..b6f41216e068d37d504d0e689d5f2370c011cdc4 --- /dev/null +++ b/drivers/misc/nxp-pn5xx/pn5xx_i2c.h @@ -0,0 +1,37 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2010 Trusted Logic S.A. + * modifications copyright (C) 2015 NXP B.V. + */ + +#define PN544_MAGIC 0xE9 + +/* + * PN544 power control via ioctl + * PN544_SET_PWR(0): power off + * PN544_SET_PWR(1): power on + * PN544_SET_PWR(2): reset and power on with firmware download enabled + */ + +#define PWR_OFF 0 +#define PWR_ON 1 +#define PWR_FW 2 + +#define CLK_OFF 0 +#define CLK_ON 1 + +#define GPIO_UNUSED -1 + +#define PN544_SET_PWR _IOW(PN544_MAGIC, 0x01, unsigned int) +#define PN54X_CLK_REQ _IOW(PN544_MAGIC, 0x02, unsigned int) + +struct pn544_i2c_platform_data { + unsigned int irq_gpio; + unsigned int ven_gpio; + unsigned int firm_gpio; + unsigned int clkreq_gpio; + struct regulator *pvdd_reg; + struct regulator *vbat_reg; + struct regulator *pmuvcc_reg; + struct regulator *sevdd_reg; +}; diff --git a/drivers/misc/nxp-pn5xx/sample_devicetree.txt b/drivers/misc/nxp-pn5xx/sample_devicetree.txt new file mode 100644 index 0000000000000000000000000000000000000000..0e81959052fc364b245e35ea74e40d219e8c38a1 --- /dev/null +++ b/drivers/misc/nxp-pn5xx/sample_devicetree.txt @@ -0,0 +1,17 @@ +Example: + +&i2c{ + + status = "okay"; + + pn547: pn547@29 { + + compatible = "nxp,pn547"; + + reg = <0x29>; + clock-frequency = <400000>; + + interrupt-gpios = <&gpio2 17 0>; + enable-gpios = <&gpio4 21 0>; + }; +};