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