summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJack Yu <jackcwyu@google.com>2021-04-28 19:50:06 +0800
committerJack Yu <jackcwyu@google.com>2021-05-07 18:40:52 +0800
commitdf1fe3ede4eb0fe127b9b124c499c838b2cad0f7 (patch)
tree599581bcd064962087ffd2d3c1cefbe99f2706b7
parent97017f50c4e9dcdb89eeb7910e3eac64e0555393 (diff)
downloadnfc-df1fe3ede4eb0fe127b9b124c499c838b2cad0f7.tar.gz
Dynamically configure spi pins
There is power leakage if spi pins are not set to low when there is no power supply for ST33. Add sysfs so it can be used for dynamically re-configure spi state according to ST33 power supply condition. Bug: 181043058 Test: SPI pins are set to low by default. Signed-off-by: Jack Yu <jackcwyu@google.com> Change-Id: I657d7e2b348d84eee8f64789f40454716362fcf8
-rw-r--r--ese/st33spi.c177
1 files changed, 158 insertions, 19 deletions
diff --git a/ese/st33spi.c b/ese/st33spi.c
index f2c057f..f07d441 100644
--- a/ese/st33spi.c
+++ b/ese/st33spi.c
@@ -35,6 +35,7 @@
#include <linux/of_gpio.h>
#include <linux/uaccess.h>
+#include <linux/platform_data/spi-s3c64xx.h>
#undef ST33NFC_QCOM
@@ -108,6 +109,11 @@ struct st33spi_data {
int nfcc_needs_poweron;
int sehal_needs_poweron;
int se_is_poweron;
+
+ /* GPIO for SPI_CS */
+ struct s3c64xx_spi_csinfo *st33spi_csinfo;
+ struct pinctrl *pinctrl;
+ int spi_state;
};
#define POWER_MODE_NONE -1
@@ -125,6 +131,94 @@ MODULE_PARM_DESC(bufsiz, "data bytes in biggest supported SPI message");
#define DRIVER_VERSION "2.2.0"
/*-------------------------------------------------------------------------*/
+static int st33spi_pinctrl_configure(struct st33spi_data *st33spi, bool enable)
+{
+ struct pinctrl_state *state;
+ int rc;
+
+ dev_info(&st33spi->spi->dev, "st33spi: configure pinctrl: %d\n", enable);
+
+ if (IS_ERR(st33spi->pinctrl)) {
+ dev_err(&st33spi->spi->dev, "could not get pinctrl\n");
+ return -ENODEV;
+ }
+ if (enable)
+ state = pinctrl_lookup_state(st33spi->pinctrl, "on");
+ else
+ state = pinctrl_lookup_state(st33spi->pinctrl, "off");
+
+ if (!IS_ERR_OR_NULL(state)) {
+ rc = pinctrl_select_state(st33spi->pinctrl, state);
+ if (unlikely(rc))
+ dev_err(&st33spi->spi->dev, "st33spi: failed to set pinctrl state\n");
+ return rc;
+ }
+ dev_err(&st33spi->spi->dev, "st33spi: failed to get pinctrl state\n");
+
+ return -EIO;
+}
+
+static ssize_t st33spi_state_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct spi_device *spi;
+ struct st33spi_data *st33spi;
+ spi = to_spi_device(dev);
+ if (spi == NULL)
+ return -ENODEV;
+
+ st33spi = spi_get_drvdata(spi);
+ if (st33spi == NULL)
+ return -ENODEV;
+
+ return scnprintf(buf, PAGE_SIZE, "state:%d, st33spi_cs:%d\n",
+ st33spi->spi_state, gpio_get_value(st33spi->st33spi_csinfo->line));
+}
+
+static ssize_t st33spi_state_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct spi_device *spi;
+ struct st33spi_data *st33spi;
+ struct s3c64xx_spi_csinfo *cs;
+ int new_spi_state;
+
+ spi = to_spi_device(dev);
+ if (spi == NULL)
+ return -ENODEV;
+
+ st33spi = spi_get_drvdata(spi);
+ if (st33spi == NULL)
+ return -ENODEV;
+
+ cs = st33spi->st33spi_csinfo;
+ if (cs == NULL)
+ return -ENODEV;
+
+ if (!kstrtoint(buf, 10, &new_spi_state)) {
+ st33spi->spi_state = new_spi_state;
+ if (new_spi_state == 0) {
+ st33spi_pinctrl_configure(st33spi, false);
+ } else if (new_spi_state == 33) {
+ st33spi_pinctrl_configure(st33spi, true);
+ } else {
+ dev_err(dev, "%s: incorrect parameter\n", __func__);
+ return -EINVAL;
+ }
+ }
+ return count;
+}
+
+static DEVICE_ATTR_RW(st33spi_state);
+
+static struct attribute *st33spi_attrs[] = {
+ &dev_attr_st33spi_state.attr,
+ NULL,
+};
+
+static struct attribute_group st33spi_attr_grp = {
+ .attrs = st33spi_attrs,
+};
static ssize_t st33spi_sync(struct st33spi_data *st33spi,
struct spi_message *message)
@@ -192,6 +286,11 @@ static ssize_t st33spi_read(struct file *filp, char __user *buf, size_t count,
st33spi = filp->private_data;
+ if (st33spi == NULL || !st33spi->spi_state) {
+ dev_warn(&st33spi->spi->dev, "st33spi: spi is not enabled, abort read process\n");
+ return -EFAULT;
+ }
+
dev_dbg(&st33spi->spi->dev, "st33spi Read: %zu bytes\n", count);
mutex_lock(&st33spi->buf_lock);
@@ -226,6 +325,11 @@ static ssize_t st33spi_write(struct file *filp, const char __user *buf,
st33spi = filp->private_data;
+ if (st33spi == NULL || !st33spi->spi_state) {
+ dev_warn(&st33spi->spi->dev, "st33spi: spi is not enabled, abort write process\n");
+ return -EFAULT;
+ }
+
dev_dbg(&st33spi->spi->dev, "st33spi Write: %zu bytes\n", count);
mutex_lock(&st33spi->buf_lock);
@@ -587,10 +691,9 @@ static long st33spi_ioctl(struct file *filp, unsigned int cmd,
retval = __get_user(tmp, (__u32 __user *)arg);
dev_dbg(&st33spi->spi->dev,
"st33spi ST33SPI_IOC_WR_POWER %d\n", retval);
- if (retval == 0) {
+ if (retval == 0 && st33spi->spi_state) {
st33spi_power_set(st33spi, tmp ? 1 : 0);
- dev_dbg(&st33spi->spi->dev, "SE_POWER_REQ set: %d\n",
- tmp);
+ dev_dbg(&st33spi->spi->dev, "SE_POWER_REQ set: %d\n", tmp);
}
break;
default:
@@ -710,6 +813,13 @@ static int st33spi_open(struct inode *inode, struct file *filp)
goto err_find_dev;
}
+ if (st33spi == NULL || !st33spi->spi_state) {
+ dev_warn(&st33spi->spi->dev,
+ "st33spi: spi is not enabled, abort open process\n");
+ mutex_unlock(&device_list_lock);
+ return -EFAULT;
+ }
+
/* Authorize only 1 process to open the device. */
if (st33spi->users > 0) {
dev_err(&st33spi->spi->dev, "already open\n");
@@ -867,33 +977,32 @@ static inline void st33spi_probe_acpi(struct spi_device *spi)
static int st33spi_parse_dt(struct device *dev, struct st33spi_data *pdata)
{
- int r = 0;
struct device_node *np = dev->of_node;
+ struct device_node *data_np;
const char *power_mode;
+ int st33spi_state;
#ifndef GKI_MODULE
np = of_find_compatible_node(NULL, NULL, "st,st33spi");
#endif
if (!np) {
- return r;
+ return -ENODEV;
}
/* Read power mode. */
power_mode = of_get_property(np, "power_mode", NULL);
if (!power_mode) {
- dev_info(dev, "%s: Default power mode: ST33\n",
- __FILE__);
+ dev_info(dev, "Default power mode: ST33\n");
pdata->power_gpio_mode = POWER_MODE_ST33;
} else if (!strcmp(power_mode, "ST33")) {
- dev_info(dev, "%s: Power mode: ST33\n", __FILE__);
+ dev_info(dev, "Power mode: ST33\n");
pdata->power_gpio_mode = POWER_MODE_ST33;
} else if (!strcmp(power_mode, "none")) {
- dev_info(dev, "%s: Power mode: none\n", __FILE__);
+ dev_info(dev, "Power mode: none\n");
pdata->power_gpio_mode = POWER_MODE_NONE;
} else {
- dev_err(dev, "%s: Power mode unknown: %s\n", __FILE__,
- power_mode);
+ dev_err(dev, "Power mode unknown: %s\n", power_mode);
return -EFAULT;
}
@@ -902,16 +1011,35 @@ static int st33spi_parse_dt(struct device *dev, struct st33spi_data *pdata)
pdata->gpiod_se_reset =
devm_gpiod_get(dev, "esereset", GPIOD_OUT_LOW);
if (IS_ERR(pdata->gpiod_se_reset)) {
- dev_err(dev,
- "%s : Unable to request esereset %d\n",
- __func__,
- IS_ERR(pdata->gpiod_se_reset));
+ dev_err(dev, "Unable to request esereset %d\n",
+ IS_ERR(pdata->gpiod_se_reset));
return -ENODEV;
}
} else {
- dev_err(dev, "%s: ST54H mode not supported", __FILE__);
+ dev_err(dev, "ST54H mode not supported");
+ }
+
+ /* Read default st33spi state. */
+ data_np = of_get_child_by_name(np, "controller-data");
+ if (!data_np) {
+ dev_err(dev, "child node 'controller-data' not found\n");
+ return -ENODEV;
}
- return r;
+
+ if (!of_property_read_u32(data_np, "cs-init-state", &st33spi_state)) {
+ pdata->spi_state = st33spi_state;
+ } else {
+ pdata->spi_state = 0;
+ }
+ dev_info(dev, "Default st33spi state: %d\n", pdata->spi_state);
+
+ pdata->pinctrl = devm_pinctrl_get(dev);
+ if (IS_ERR(pdata->pinctrl)) {
+ dev_err(dev, "could not get pinctrl\n");
+ return -ENODEV;
+ }
+
+ return 0;
}
static int st33spi_probe(struct spi_device *spi)
@@ -932,8 +1060,7 @@ static int st33spi_probe(struct spi_device *spi)
BUILD_BUG_ON(N_SPI_MINORS > 256);
st33spi_major =
__register_chrdev(0, 0, N_SPI_MINORS, "spi", &st33spi_fops);
- dev_info(&spi->dev, "Loading st33spi driver, major: %d\n",
- st33spi_major);
+ dev_info(&spi->dev, "Loading st33spi driver, major: %d\n", st33spi_major);
st33spi_class = class_create(THIS_MODULE, "st33spi");
if (IS_ERR(st33spi_class)) {
@@ -974,6 +1101,12 @@ static int st33spi_probe(struct spi_device *spi)
dev = device_create(st33spi_class, &spi->dev, st33spi->devt,
st33spi, "st33spi");
status = PTR_ERR_OR_ZERO(dev);
+
+ status = sysfs_create_group(&dev->kobj, &st33spi_attr_grp);
+ if (status) {
+ dev_err(&spi->dev, "sysfs_create_group failed\n");
+ }
+ st33spi->st33spi_csinfo = spi_get_ctldata(spi);
} else {
dev_dbg(&spi->dev, "%s : no minor number available!\n",
__FILE__);
@@ -1013,6 +1146,12 @@ static int st33spi_probe(struct spi_device *spi)
if (status == 0) {
spi_set_drvdata(spi, st33spi);
(void)st33spi_parse_dt(&spi->dev, st33spi);
+ if (!st33spi->spi_state) {
+ dev_dbg(&spi->dev, "st33spi: probe - put spi pins to low \n");
+ st33spi_pinctrl_configure(st33spi, false);
+ } else {
+ st33spi_pinctrl_configure(st33spi, true);
+ }
} else {
kfree(st33spi);
}