w1: Add driver for i.MX bus master controller
authorMartin Fuzzey <martin.fuzzey@flowbird.group>
Wed, 24 Oct 2018 08:21:18 +0000 (10:21 +0200)
committerStefano Babic <sbabic@denx.de>
Tue, 1 Jan 2019 13:12:18 +0000 (14:12 +0100)
Two variants of controllers are supported:
V1 (bitwise only) found in
i.MX21, i.MX27, i.MX31, i.MX51
V2 (byte operations) found in
i.MX25, i.MX35, i.MX50, i.MX53

Only tested on i.MX53 hardware but in both modes
(by modifying the device tree).

Signed-off-by: Martin Fuzzey <martin.fuzzey@flowbird.group>
doc/device-tree-bindings/w1/mxc-w1.txt [new file with mode: 0644]
drivers/w1/Kconfig
drivers/w1/Makefile
drivers/w1/mxc_w1.c [new file with mode: 0644]

diff --git a/doc/device-tree-bindings/w1/mxc-w1.txt b/doc/device-tree-bindings/w1/mxc-w1.txt
new file mode 100644 (file)
index 0000000..1fb49cc
--- /dev/null
@@ -0,0 +1,37 @@
+NXP i.MX (MXC) One wire bus master controller
+=======================
+
+Child nodes are required in device tree. The driver will detect
+the devices serial number and then search in the child nodes in the device tree
+for the proper node and try to match it with the device.
+
+Also check doc/device-tree-bindings/w1-eeprom for possible child nodes drivers
+
+Driver:
+- drivers/w1/mxc_w1.c
+
+Required properties:
+- compatible : should be one of
+       "fsl,imx21-owire", "fsl,imx27-owire", "fsl,imx31-owire", "fsl,imx25-owire"
+       "fsl,imx25-owire", "fsl,imx35-owire", "fsl,imx50-owire", "fsl,imx53-owire"
+
+- reg : Address and length of the register set for the device
+
+Optional:
+* none
+
+Example:
+       onewire {
+               compatible = "fsl,imx53-owire";
+               reg = <0x63fa4000 0x4000>;
+       };
+
+Example with child:
+       onewire {
+               compatible = "fsl,imx53-owire";
+               reg = <0x63fa4000 0x4000>;
+
+               eeprom1: eeprom@0 {
+                       compatible = "maxim,ds24xxx";
+               };
+       };
index d6e0457..031bab2 100644 (file)
@@ -20,6 +20,20 @@ config W1_GPIO
        help
          Emulate a 1-wire bus using a GPIO.
 
+config W1_MXC
+       bool "Enable 1-wire controller on i.MX processors"
+       default no
+       depends on ARCH_MX25 || ARCH_MX31 || ARCH_MX5
+       help
+         Support the one wire controller found in some members of the NXP
+         i.MX SoC family.
+         There are currently two silicon variants:
+         V1: i.MX21, i.MX27, i.MX31, i.MX51
+         V2: i.MX25, i.MX35, i.MX50, i.MX53
+         Newer i.MX SoCs such as the i.MX6 do not have one wire controllers.
+
+         The driver supports both silicon variants.
+
 endif
 
 endmenu
index 7fd8697..9825187 100644 (file)
@@ -1,3 +1,4 @@
 obj-$(CONFIG_W1) += w1-uclass.o
 
 obj-$(CONFIG_W1_GPIO) += w1-gpio.o
+obj-$(CONFIG_W1_MXC) += mxc_w1.o
diff --git a/drivers/w1/mxc_w1.c b/drivers/w1/mxc_w1.c
new file mode 100644 (file)
index 0000000..9279ba3
--- /dev/null
@@ -0,0 +1,232 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Driver for one wire controller in some i.MX Socs
+ *
+ * There are currently two silicon variants:
+ * V1: i.MX21, i.MX27, i.MX31, i.MX51
+ * V2: i.MX25, i.MX35, i.MX50, i.MX53
+ * Newer i.MX SoCs such as the i.MX6 do not have one wire controllers.
+ *
+ * The V1 controller only supports single bit operations.
+ * The V2 controller is backwards compatible on the register level but adds
+ * byte size operations and a "search ROM accelerator mode"
+ *
+ * This driver does not currently support the search ROM accelerator
+ *
+ * Copyright (c) 2018 Flowbird
+ * Martin Fuzzey <martin.fuzzey@flowbird.group>
+ */
+
+#include <asm/arch/clock.h>
+#include <common.h>
+#include <dm.h>
+#include <linux/io.h>
+#include <w1.h>
+
+struct mxc_w1_regs {
+       u16 control;
+#define MXC_W1_CONTROL_RPP     BIT(7)
+#define MXC_W1_CONTROL_PST     BIT(6)
+#define MXC_W1_CONTROL_WR(x)   BIT(5 - (x))
+#define MXC_W1_CONTROL_RDST    BIT(3)
+
+       u16 time_divider;
+       u16 reset;
+
+       /* Registers below on V2 silicon only */
+       u16 command;
+       u16 tx_rx;
+       u16 interrupt;
+#define MXC_W1_INTERRUPT_TBE   BIT(2)
+#define MXC_W1_INTERRUPT_TSRE  BIT(3)
+#define MXC_W1_INTERRUPT_RBF   BIT(4)
+#define MXC_W1_INTERRUPT_RSRF  BIT(5)
+
+       u16 interrupt_en;
+};
+
+struct mxc_w1_pdata {
+       struct mxc_w1_regs *regs;
+};
+
+/*
+ * this is the low level routine to read/write a bit on the One Wire
+ * interface on the hardware. It does write 0 if parameter bit is set
+ * to 0, otherwise a write 1/read.
+ */
+static u8 mxc_w1_touch_bit(struct mxc_w1_pdata *pdata, u8 bit)
+{
+       u16 *ctrl_addr = &pdata->regs->control;
+       u16 mask = MXC_W1_CONTROL_WR(bit);
+       unsigned int timeout_cnt = 400; /* Takes max. 120us according to
+                                        * datasheet.
+                                        */
+
+       writew(mask, ctrl_addr);
+
+       while (timeout_cnt--) {
+               if (!(readw(ctrl_addr) & mask))
+                       break;
+
+               udelay(1);
+       }
+
+       return (readw(ctrl_addr) & MXC_W1_CONTROL_RDST) ? 1 : 0;
+}
+
+static u8 mxc_w1_read_byte(struct udevice *dev)
+{
+       struct mxc_w1_pdata *pdata = dev_get_platdata(dev);
+       struct mxc_w1_regs *regs = pdata->regs;
+       u16 status;
+
+       if (dev_get_driver_data(dev) < 2) {
+               int i;
+               u8 ret = 0;
+
+               for (i = 0; i < 8; i++)
+                       ret |= (mxc_w1_touch_bit(pdata, 1) << i);
+
+               return ret;
+       }
+
+       readw(&regs->tx_rx);
+       writew(0xFF, &regs->tx_rx);
+
+       do {
+               udelay(1); /* Without this bytes are sometimes duplicated... */
+               status = readw(&regs->interrupt);
+       } while (!(status & MXC_W1_INTERRUPT_RBF));
+
+       return (u8)readw(&regs->tx_rx);
+}
+
+static void mxc_w1_write_byte(struct udevice *dev, u8 byte)
+{
+       struct mxc_w1_pdata *pdata = dev_get_platdata(dev);
+       struct mxc_w1_regs *regs = pdata->regs;
+       u16 status;
+
+       if (dev_get_driver_data(dev) < 2) {
+               int i;
+
+               for (i = 0; i < 8; i++)
+                       mxc_w1_touch_bit(pdata, (byte >> i) & 0x1);
+
+               return;
+       }
+
+       readw(&regs->tx_rx);
+       writew(byte, &regs->tx_rx);
+
+       do {
+               udelay(1);
+               status = readw(&regs->interrupt);
+       } while (!(status & MXC_W1_INTERRUPT_TSRE));
+}
+
+static bool mxc_w1_reset(struct udevice *dev)
+{
+       struct mxc_w1_pdata *pdata = dev_get_platdata(dev);
+       u16 reg_val;
+
+       writew(MXC_W1_CONTROL_RPP, &pdata->regs->control);
+
+       do {
+               reg_val = readw(&pdata->regs->control);
+       }  while (reg_val & MXC_W1_CONTROL_RPP);
+
+       return !(reg_val & MXC_W1_CONTROL_PST);
+}
+
+static u8 mxc_w1_triplet(struct udevice *dev, bool bdir)
+{
+       struct mxc_w1_pdata *pdata = dev_get_platdata(dev);
+       u8 id_bit   = mxc_w1_touch_bit(pdata, 1);
+       u8 comp_bit = mxc_w1_touch_bit(pdata, 1);
+       u8 retval;
+
+       if (id_bit && comp_bit)
+               return 0x03;  /* error */
+
+       if (!id_bit && !comp_bit) {
+               /* Both bits are valid, take the direction given */
+               retval = bdir ? 0x04 : 0;
+       } else {
+               /* Only one bit is valid, take that direction */
+               bdir = id_bit;
+               retval = id_bit ? 0x05 : 0x02;
+       }
+
+       mxc_w1_touch_bit(pdata, bdir);
+
+       return retval;
+}
+
+static int mxc_w1_ofdata_to_platdata(struct udevice *dev)
+{
+       struct mxc_w1_pdata *pdata = dev_get_platdata(dev);
+       fdt_addr_t addr;
+
+       addr = devfdt_get_addr(dev);
+       if (addr == FDT_ADDR_T_NONE)
+               return -EINVAL;
+
+       pdata->regs = (struct mxc_w1_regs *)addr;
+
+       return 0;
+};
+
+static int mxc_w1_probe(struct udevice *dev)
+{
+       struct mxc_w1_pdata *pdata = dev_get_platdata(dev);
+       unsigned int clkrate = mxc_get_clock(MXC_IPG_PERCLK);
+       unsigned int clkdiv;
+
+       if (clkrate < 10000000) {
+               dev_err(dev, "input clock frequency (%u Hz) too low\n",
+                       clkrate);
+               return -EINVAL;
+       }
+
+       clkdiv = clkrate / 1000000;
+       clkrate /= clkdiv;
+       if (clkrate < 980000 || clkrate > 1020000) {
+               dev_err(dev, "Incorrect time base frequency %u Hz\n", clkrate);
+               return -EINVAL;
+       }
+
+       writew(clkdiv - 1, &pdata->regs->time_divider);
+
+       return 0;
+}
+
+static const struct w1_ops mxc_w1_ops = {
+       .read_byte      = mxc_w1_read_byte,
+       .reset          = mxc_w1_reset,
+       .triplet        = mxc_w1_triplet,
+       .write_byte     = mxc_w1_write_byte,
+};
+
+static const struct udevice_id mxc_w1_id[] = {
+       { .compatible = "fsl,imx21-owire", .data = 1 },
+       { .compatible = "fsl,imx27-owire", .data = 1 },
+       { .compatible = "fsl,imx31-owire", .data = 1 },
+       { .compatible = "fsl,imx51-owire", .data = 1 },
+
+       { .compatible = "fsl,imx25-owire", .data = 2 },
+       { .compatible = "fsl,imx35-owire", .data = 2 },
+       { .compatible = "fsl,imx50-owire", .data = 2 },
+       { .compatible = "fsl,imx53-owire", .data = 2 },
+       { },
+};
+
+U_BOOT_DRIVER(mxc_w1_drv) = {
+       .id                             = UCLASS_W1,
+       .name                           = "mxc_w1_drv",
+       .of_match                       = mxc_w1_id,
+       .ofdata_to_platdata             = mxc_w1_ofdata_to_platdata,
+       .ops                            = &mxc_w1_ops,
+       .platdata_auto_alloc_size       = sizeof(struct mxc_w1_pdata),
+       .probe                          = mxc_w1_probe,
+};