Loading...
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 | // 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 <dm.h> #include <dm/device_compat.h> #include <linux/bitops.h> #include <linux/delay.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_plat(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(®s->tx_rx); writew(0xFF, ®s->tx_rx); do { udelay(1); /* Without this bytes are sometimes duplicated... */ status = readw(®s->interrupt); } while (!(status & MXC_W1_INTERRUPT_RBF)); return (u8)readw(®s->tx_rx); } static void mxc_w1_write_byte(struct udevice *dev, u8 byte) { struct mxc_w1_pdata *pdata = dev_get_plat(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(®s->tx_rx); writew(byte, ®s->tx_rx); do { udelay(1); status = readw(®s->interrupt); } while (!(status & MXC_W1_INTERRUPT_TSRE)); } static bool mxc_w1_reset(struct udevice *dev) { struct mxc_w1_pdata *pdata = dev_get_plat(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_plat(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_of_to_plat(struct udevice *dev) { struct mxc_w1_pdata *pdata = dev_get_plat(dev); fdt_addr_t addr; addr = dev_read_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_plat(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, .of_to_plat = mxc_w1_of_to_plat, .ops = &mxc_w1_ops, .plat_auto = sizeof(struct mxc_w1_pdata), .probe = mxc_w1_probe, }; |