This driver will put the tcan4x5x device into sleep mode to reduce power
consumption.

Signed-off-by: Sean Nyekjaer <[email protected]>
---
Not many drivers use SPI. So leave this for others to see how it can be
done.
If this is appropriate for upstreaming, please comment :)

 drivers/misc/Kconfig    |   5 ++
 drivers/misc/Makefile   |   3 +
 drivers/misc/tcan4x5x.c | 135 ++++++++++++++++++++++++++++++++++++++++
 3 files changed, 143 insertions(+)
 create mode 100644 drivers/misc/tcan4x5x.c

diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
index dde773ab6b1..2abfdb5575c 100644
--- a/drivers/misc/Kconfig
+++ b/drivers/misc/Kconfig
@@ -170,6 +170,11 @@ config CMD_CROS_EC
          updating its flash, accessing a small saved context area
          and talking to the I2C bus behind the EC (if there is one).
 
+config TCAN4X5X
+       bool "Enable TCAN4X5X"
+       depends on MISC
+       depends on DM_GPIO
+
 config CROS_EC
        bool "Enable Chrome OS EC"
        help
diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
index 1d950f7a0ab..57ebafff121 100644
--- a/drivers/misc/Makefile
+++ b/drivers/misc/Makefile
@@ -29,6 +29,9 @@ ifdef CONFIG_XPL_BUILD
 obj-$(CONFIG_SANDBOX) += spltest_sandbox.o
 endif
 endif
+ifndef CONFIG_SPL_BUILD
+obj-$(CONFIG_TCAN4X5X) += tcan4x5x.o
+endif
 obj-$(CONFIG_ALTERA_SYSID) += altera_sysid.o
 obj-$(CONFIG_ATSHA204A) += atsha204a-i2c.o
 obj-$(CONFIG_CBMEM_CONSOLE) += cbmem_console.o
diff --git a/drivers/misc/tcan4x5x.c b/drivers/misc/tcan4x5x.c
new file mode 100644
index 00000000000..a304f3db9df
--- /dev/null
+++ b/drivers/misc/tcan4x5x.c
@@ -0,0 +1,135 @@
+#include <dm.h>
+#include <linux/delay.h>
+#include <asm/io.h>
+#include <asm-generic/gpio.h>
+#include <spi.h>
+#include <log.h>
+
+#define TCAN4X5X_SPI_INSTRUCTION_WRITE 0x61
+#define TCAN4X5X_SPI_INSTRUCTION_READ 0x41
+
+#define TCAN4X5X_CONFIG 0x800
+#define TCAN4X5X_MODE_BITS (BIT(7) | BIT(6))
+
+struct tcan4x5x_transfer {
+       u8 cmd;
+       __be16 reg;
+       u8 length;
+       __be32 val;
+} __packed;
+
+static int tcan4x5x_write_reg(struct udevice *dev, u16 reg, u32 val)
+{
+       struct tcan4x5x_transfer tx;
+       int ret;
+
+       tx.cmd = TCAN4X5X_SPI_INSTRUCTION_WRITE;
+       tx.reg = cpu_to_be16(reg);
+       tx.length = 1;
+       tx.val = cpu_to_be32(val);
+
+       ret = dm_spi_claim_bus(dev);
+       if (ret)
+               return ret;
+
+       ret = dm_spi_xfer(dev, 64, &tx, NULL, SPI_XFER_ONCE);
+
+       dm_spi_release_bus(dev);
+
+       return ret;
+}
+
+static int tcan4x5x_read_reg(struct udevice *dev, u16 reg, u32 *val)
+{
+       struct tcan4x5x_transfer rx;
+       int ret;
+
+       rx.cmd = TCAN4X5X_SPI_INSTRUCTION_READ;
+       rx.reg = cpu_to_be16(reg);
+       rx.length = 1;
+
+       ret = dm_spi_claim_bus(dev);
+       if (ret)
+               return ret;
+
+       ret = dm_spi_xfer(dev, 64, &rx, &rx, SPI_XFER_ONCE);
+       if (ret)
+               return ret;
+
+       dm_spi_release_bus(dev);
+       if (ret)
+               return ret;
+
+       *val = be32_to_cpu(rx.val);
+
+       return 0;
+}
+
+static int tcan4x5x_enable_sleep(struct udevice *dev)
+{
+       u32 val;
+       int ret;
+
+       ret = tcan4x5x_read_reg(dev, TCAN4X5X_CONFIG, &val);
+       if (ret) {
+               log_err("reg read failed (%d)\n", ret);
+               return ret;
+       }
+
+       log_debug("read %x\n", val);
+       val &= ~(TCAN4X5X_MODE_BITS);
+       log_debug("write %x\n", val);
+
+       ret = tcan4x5x_write_reg(dev, TCAN4X5X_CONFIG, val);
+       if (ret)
+               log_err("reg write failed (%d)\n", ret);
+
+       return ret;
+}
+
+static int tcan4x5x_probe(struct udevice *dev)
+{
+       struct gpio_desc rst;
+       int ret;
+
+       ret = gpio_request_by_name(dev, "reset-gpios", 0, &rst, GPIOD_IS_OUT);
+       if (ret) {
+               log_debug("could not find reset-gpios\n");
+               return ret;
+       }
+
+       ret = dm_gpio_set_value(&rst, 1);
+       if (ret) {
+               log_err("can't set_value for rst gpio");
+               return ret;
+       }
+
+       //tPULSE_WIDTH = 30us
+       udelay(100);
+
+       ret = dm_gpio_set_value(&rst, 0);
+       if (ret) {
+               log_err("can't set_value for rst gpio");
+               return -1;
+       }
+
+       /*
+        * After a RST has taken place, a wait time of ≥ 700 µs should be used
+        * before reading or writing to the TCAN4550-Q1
+        */
+       udelay(1000);
+
+       return tcan4x5x_enable_sleep(dev);
+}
+
+static const struct udevice_id tcan4x5x_ids[] = {
+       { .compatible = "ti,tcan4x5x" },
+       { }
+};
+
+U_BOOT_DRIVER(tcan4x5x) = {
+       .name = "tcan4x5x",
+       .id = UCLASS_MISC,
+       .of_match = tcan4x5x_ids,
+       .probe = tcan4x5x_probe,
+};
-- 
2.55.0

Reply via email to