uboot/drivers/i2c/i2c-versatile.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0+
   2/*
   3 * Copyright (c) 2018 Arm Ltd.
   4 * Author: Liviu Dudau <liviu.dudau@foss.arm.com>
   5 *
   6 */
   7
   8#include <common.h>
   9#include <dm.h>
  10#include <errno.h>
  11#include <i2c.h>
  12#include <asm/io.h>
  13#include <clk.h>
  14#include <linux/bitops.h>
  15#include <linux/delay.h>
  16#include <linux/io.h>
  17
  18#define I2C_CONTROL_REG         0x00
  19#define I2C_SET_REG             0x00
  20#define I2C_CLEAR_REG           0x04
  21
  22#define SCL     BIT(0)
  23#define SDA     BIT(1)
  24
  25struct versatile_i2c_priv {
  26        phys_addr_t base;
  27        u32 delay;
  28};
  29
  30static inline void versatile_sda_set(struct versatile_i2c_priv *priv, u8 state)
  31{
  32        writel(SDA, priv->base + (state ? I2C_SET_REG : I2C_CLEAR_REG));
  33        udelay(priv->delay);
  34}
  35
  36static inline int versatile_sda_get(struct versatile_i2c_priv *priv)
  37{
  38        int v = !!(readl(priv->base + I2C_CONTROL_REG) & SDA);
  39
  40        udelay(priv->delay);
  41        return v;
  42}
  43
  44static inline void versatile_scl_set(struct versatile_i2c_priv *priv, u8 state)
  45{
  46        writel(SCL, priv->base + (state ? I2C_SET_REG : I2C_CLEAR_REG));
  47        udelay(priv->delay);
  48}
  49
  50static inline int versatile_scl_get(struct versatile_i2c_priv *priv)
  51{
  52        int v = !!(readl(priv->base + I2C_CONTROL_REG) & SCL);
  53
  54        udelay(priv->delay);
  55        return v;
  56}
  57
  58/* start: SDA goes from high to low while SCL is high */
  59static void versatile_i2c_start(struct versatile_i2c_priv *priv)
  60{
  61        udelay(priv->delay);
  62        versatile_sda_set(priv, 1);
  63        versatile_scl_set(priv, 1);
  64        versatile_sda_set(priv, 0);
  65}
  66
  67/* stop: SDA goes from low to high while SCL is high */
  68static void versatile_i2c_stop(struct versatile_i2c_priv *priv)
  69{
  70        versatile_scl_set(priv, 0);
  71        versatile_sda_set(priv, 0);
  72        versatile_scl_set(priv, 1);
  73        versatile_sda_set(priv, 1);
  74}
  75
  76/* read a bit from the SDA line (data or ACK/NACK) */
  77static u8 versatile_i2c_read_bit(struct versatile_i2c_priv *priv)
  78{
  79        versatile_scl_set(priv, 0);
  80        versatile_sda_set(priv, 1);
  81        versatile_scl_set(priv, 1);
  82        udelay(priv->delay);
  83        return (u8)versatile_sda_get(priv);
  84}
  85
  86/* write a bit on the SDA line */
  87static void versatile_i2c_write_bit(struct versatile_i2c_priv *priv, u8 bit)
  88{
  89        versatile_scl_set(priv, 0);
  90        versatile_sda_set(priv, bit);
  91        versatile_scl_set(priv, 1);
  92        udelay(priv->delay);
  93}
  94
  95/* send a reset sequence of 9 clocks with SDA high */
  96static void versatile_i2c_reset_bus(struct versatile_i2c_priv *priv)
  97{
  98        int i;
  99
 100        for (i = 0; i < 9; i++)
 101                versatile_i2c_write_bit(priv, 1);
 102
 103        versatile_i2c_stop(priv);
 104}
 105
 106/* write byte without start/stop sequence */
 107static int versatile_i2c_write_byte(struct versatile_i2c_priv *priv, u8 byte)
 108{
 109        u8 nak, i;
 110
 111        for (i = 0; i < 8; i++) {
 112                versatile_i2c_write_bit(priv, byte & 0x80);
 113                byte <<= 1;
 114        }
 115
 116        /* read ACK */
 117        nak = versatile_i2c_read_bit(priv);
 118        versatile_scl_set(priv, 0);
 119
 120        return nak;     /* not a nack is an ack */
 121}
 122
 123static int versatile_i2c_read_byte(struct versatile_i2c_priv *priv,
 124                                   u8 *byte, u8 ack)
 125{
 126        u8 i;
 127
 128        *byte = 0;
 129        for (i = 0; i < 8; i++) {
 130                *byte <<= 1;
 131                *byte |= versatile_i2c_read_bit(priv);
 132        }
 133        /* write the nack */
 134        versatile_i2c_write_bit(priv, ack);
 135
 136        return 0;
 137}
 138
 139static int versatile_i2c_send_slave_addr(struct versatile_i2c_priv *priv,
 140                                         struct i2c_msg *msg)
 141{
 142        u8 addr;
 143        int ret;
 144
 145        if (msg->flags & I2C_M_TEN) {
 146                /* 10-bit address, send extended address code first */
 147                addr = 0xf0 | ((msg->addr >> 7) & 0x06);
 148                ret = versatile_i2c_write_byte(priv, addr);
 149                if (ret) {
 150                        versatile_i2c_stop(priv);
 151                        return -EIO;
 152                }
 153
 154                /* remaining bits */
 155                ret = versatile_i2c_write_byte(priv, msg->addr & 0xff);
 156                if (ret) {
 157                        versatile_i2c_stop(priv);
 158                        return -EIO;
 159                }
 160                /* reads need to resend the addr */
 161                if (msg->flags & I2C_M_RD) {
 162                        versatile_i2c_start(priv);
 163                        addr |= 1;
 164                        ret = versatile_i2c_write_byte(priv, addr);
 165                        if (ret) {
 166                                versatile_i2c_stop(priv);
 167                                return -EIO;
 168                        }
 169                }
 170        } else {
 171                /* normal 7-bit address */
 172                addr = msg->addr << 1;
 173                if (msg->flags & I2C_M_RD)
 174                        addr |= 1;
 175                ret = versatile_i2c_write_byte(priv, addr);
 176                if (ret) {
 177                        versatile_i2c_stop(priv);
 178                        return -EIO;
 179                }
 180        }
 181
 182        return 0;
 183}
 184
 185static int versatile_i2c_message_xfer(struct versatile_i2c_priv *priv,
 186                                      struct i2c_msg *msg)
 187{
 188        int i, ret;
 189        u8 ack;
 190
 191        versatile_i2c_start(priv);
 192        if (versatile_i2c_send_slave_addr(priv, msg))
 193                return -EIO;
 194
 195        for (i = 0; i < msg->len; i++) {
 196                if (msg->flags & I2C_M_RD) {
 197                        ack = (msg->len - i - 1) == 0 ? 1 : 0;
 198                        ret = versatile_i2c_read_byte(priv, &msg->buf[i], ack);
 199                } else {
 200                        ret = versatile_i2c_write_byte(priv, msg->buf[i]);
 201                }
 202
 203                if (ret)
 204                        break;
 205        }
 206
 207        versatile_i2c_stop(priv);
 208
 209        return ret;
 210}
 211
 212static int versatile_i2c_xfer(struct udevice *bus,
 213                              struct i2c_msg *msg, int nmsgs)
 214{
 215        struct versatile_i2c_priv *priv = dev_get_priv(bus);
 216        int ret;
 217
 218        for ( ; nmsgs > 0; nmsgs--, msg++) {
 219                ret = versatile_i2c_message_xfer(priv, msg);
 220                if (ret)
 221                        return -EREMOTEIO;
 222        }
 223
 224        return 0;
 225}
 226
 227static int versatile_i2c_chip_probe(struct udevice *bus,
 228                                    uint chip, uint chip_flags)
 229{
 230        /* probe the presence of a slave by writing a 0-size message */
 231        struct i2c_msg msg = { .addr = chip, .flags = chip_flags,
 232                               .len = 0, .buf = NULL };
 233        struct versatile_i2c_priv *priv = dev_get_priv(bus);
 234
 235        return versatile_i2c_message_xfer(priv, &msg);
 236}
 237
 238static int versatile_i2c_set_bus_speed(struct udevice *bus, unsigned int speed)
 239{
 240        struct versatile_i2c_priv *priv = dev_get_priv(bus);
 241
 242        priv->delay = 1000000 / (speed << 2);
 243
 244        versatile_i2c_reset_bus(priv);
 245
 246        return 0;
 247}
 248
 249static int versatile_i2c_probe(struct udevice *dev)
 250{
 251        struct versatile_i2c_priv *priv = dev_get_priv(dev);
 252
 253        priv->base = (phys_addr_t)dev_read_addr(dev);
 254        priv->delay = 25;       /* 25us * 4 = 100kHz */
 255
 256        return 0;
 257}
 258
 259static const struct dm_i2c_ops versatile_i2c_ops = {
 260        .xfer = versatile_i2c_xfer,
 261        .probe_chip = versatile_i2c_chip_probe,
 262        .set_bus_speed = versatile_i2c_set_bus_speed,
 263};
 264
 265static const struct udevice_id versatile_i2c_of_match[] = {
 266        { .compatible = "arm,versatile-i2c" },
 267        { }
 268};
 269
 270U_BOOT_DRIVER(versatile_i2c) = {
 271        .name = "i2c-bus-versatile",
 272        .id = UCLASS_I2C,
 273        .of_match = versatile_i2c_of_match,
 274        .probe = versatile_i2c_probe,
 275        .priv_auto      = sizeof(struct versatile_i2c_priv),
 276        .ops = &versatile_i2c_ops,
 277};
 278