uboot/drivers/i2c/meson_i2c.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0+
   2/*
   3 * (C) Copyright 2017 - Beniamino Galvani <b.galvani@gmail.com>
   4 */
   5#include <common.h>
   6#include <log.h>
   7#include <asm/io.h>
   8#include <clk.h>
   9#include <dm.h>
  10#include <i2c.h>
  11#include <linux/bitops.h>
  12#include <linux/delay.h>
  13#include <linux/err.h>
  14
  15#define I2C_TIMEOUT_MS          100
  16
  17/* Control register fields */
  18#define REG_CTRL_START          BIT(0)
  19#define REG_CTRL_ACK_IGNORE     BIT(1)
  20#define REG_CTRL_STATUS         BIT(2)
  21#define REG_CTRL_ERROR          BIT(3)
  22#define REG_CTRL_CLKDIV_SHIFT   12
  23#define REG_CTRL_CLKDIV_MASK    GENMASK(21, 12)
  24#define REG_CTRL_CLKDIVEXT_SHIFT 28
  25#define REG_CTRL_CLKDIVEXT_MASK GENMASK(29, 28)
  26
  27enum {
  28        TOKEN_END = 0,
  29        TOKEN_START,
  30        TOKEN_SLAVE_ADDR_WRITE,
  31        TOKEN_SLAVE_ADDR_READ,
  32        TOKEN_DATA,
  33        TOKEN_DATA_LAST,
  34        TOKEN_STOP,
  35};
  36
  37struct i2c_regs {
  38        u32 ctrl;
  39        u32 slave_addr;
  40        u32 tok_list0;
  41        u32 tok_list1;
  42        u32 tok_wdata0;
  43        u32 tok_wdata1;
  44        u32 tok_rdata0;
  45        u32 tok_rdata1;
  46};
  47
  48struct meson_i2c_data {
  49        unsigned char div_factor;
  50};
  51
  52struct meson_i2c {
  53        const struct meson_i2c_data *data;
  54        struct clk clk;
  55        struct i2c_regs *regs;
  56        struct i2c_msg *msg;    /* Current I2C message */
  57        bool last;              /* Whether the message is the last */
  58        uint count;             /* Number of bytes in the current transfer */
  59        uint pos;               /* Position of current transfer in message */
  60        u32 tokens[2];          /* Sequence of tokens to be written */
  61        uint num_tokens;        /* Number of tokens to be written */
  62};
  63
  64static void meson_i2c_reset_tokens(struct meson_i2c *i2c)
  65{
  66        i2c->tokens[0] = 0;
  67        i2c->tokens[1] = 0;
  68        i2c->num_tokens = 0;
  69}
  70
  71static void meson_i2c_add_token(struct meson_i2c *i2c, int token)
  72{
  73        if (i2c->num_tokens < 8)
  74                i2c->tokens[0] |= (token & 0xf) << (i2c->num_tokens * 4);
  75        else
  76                i2c->tokens[1] |= (token & 0xf) << ((i2c->num_tokens % 8) * 4);
  77
  78        i2c->num_tokens++;
  79}
  80
  81/*
  82 * Retrieve data for the current transfer (which can be at most 8
  83 * bytes) from the device internal buffer.
  84 */
  85static void meson_i2c_get_data(struct meson_i2c *i2c, u8 *buf, int len)
  86{
  87        u32 rdata0, rdata1;
  88        int i;
  89
  90        rdata0 = readl(&i2c->regs->tok_rdata0);
  91        rdata1 = readl(&i2c->regs->tok_rdata1);
  92
  93        debug("meson i2c: read data %08x %08x len %d\n", rdata0, rdata1, len);
  94
  95        for (i = 0; i < min(4, len); i++)
  96                *buf++ = (rdata0 >> i * 8) & 0xff;
  97
  98        for (i = 4; i < min(8, len); i++)
  99                *buf++ = (rdata1 >> (i - 4) * 8) & 0xff;
 100}
 101
 102/*
 103 * Write data for the current transfer (which can be at most 8 bytes)
 104 * to the device internal buffer.
 105 */
 106static void meson_i2c_put_data(struct meson_i2c *i2c, u8 *buf, int len)
 107{
 108        u32 wdata0 = 0, wdata1 = 0;
 109        int i;
 110
 111        for (i = 0; i < min(4, len); i++)
 112                wdata0 |= *buf++ << (i * 8);
 113
 114        for (i = 4; i < min(8, len); i++)
 115                wdata1 |= *buf++ << ((i - 4) * 8);
 116
 117        writel(wdata0, &i2c->regs->tok_wdata0);
 118        writel(wdata1, &i2c->regs->tok_wdata1);
 119
 120        debug("meson i2c: write data %08x %08x len %d\n", wdata0, wdata1, len);
 121}
 122
 123/*
 124 * Prepare the next transfer: pick the next 8 bytes in the remaining
 125 * part of message and write tokens and data (if needed) to the
 126 * device.
 127 */
 128static void meson_i2c_prepare_xfer(struct meson_i2c *i2c)
 129{
 130        bool write = !(i2c->msg->flags & I2C_M_RD);
 131        int i;
 132
 133        i2c->count = min(i2c->msg->len - i2c->pos, 8u);
 134
 135        for (i = 0; i + 1 < i2c->count; i++)
 136                meson_i2c_add_token(i2c, TOKEN_DATA);
 137
 138        if (i2c->count) {
 139                if (write || i2c->pos + i2c->count < i2c->msg->len)
 140                        meson_i2c_add_token(i2c, TOKEN_DATA);
 141                else
 142                        meson_i2c_add_token(i2c, TOKEN_DATA_LAST);
 143        }
 144
 145        if (write)
 146                meson_i2c_put_data(i2c, i2c->msg->buf + i2c->pos, i2c->count);
 147
 148        if (i2c->last && i2c->pos + i2c->count >= i2c->msg->len)
 149                meson_i2c_add_token(i2c, TOKEN_STOP);
 150
 151        writel(i2c->tokens[0], &i2c->regs->tok_list0);
 152        writel(i2c->tokens[1], &i2c->regs->tok_list1);
 153}
 154
 155static void meson_i2c_do_start(struct meson_i2c *i2c, struct i2c_msg *msg)
 156{
 157        int token;
 158
 159        token = (msg->flags & I2C_M_RD) ? TOKEN_SLAVE_ADDR_READ :
 160                TOKEN_SLAVE_ADDR_WRITE;
 161
 162        writel(msg->addr << 1, &i2c->regs->slave_addr);
 163        meson_i2c_add_token(i2c, TOKEN_START);
 164        meson_i2c_add_token(i2c, token);
 165}
 166
 167static int meson_i2c_xfer_msg(struct meson_i2c *i2c, struct i2c_msg *msg,
 168                              int last)
 169{
 170        ulong start;
 171
 172        debug("meson i2c: %s addr %u len %u\n",
 173              (msg->flags & I2C_M_RD) ? "read" : "write",
 174              msg->addr, msg->len);
 175
 176        i2c->msg = msg;
 177        i2c->last = last;
 178        i2c->pos = 0;
 179        i2c->count = 0;
 180
 181        meson_i2c_reset_tokens(i2c);
 182        meson_i2c_do_start(i2c, msg);
 183
 184        do {
 185                meson_i2c_prepare_xfer(i2c);
 186
 187                /* start the transfer */
 188                setbits_le32(&i2c->regs->ctrl, REG_CTRL_START);
 189                start = get_timer(0);
 190                while (readl(&i2c->regs->ctrl) & REG_CTRL_STATUS) {
 191                        if (get_timer(start) > I2C_TIMEOUT_MS) {
 192                                clrbits_le32(&i2c->regs->ctrl, REG_CTRL_START);
 193                                debug("meson i2c: timeout\n");
 194                                return -ETIMEDOUT;
 195                        }
 196                        udelay(1);
 197                }
 198                meson_i2c_reset_tokens(i2c);
 199                clrbits_le32(&i2c->regs->ctrl, REG_CTRL_START);
 200
 201                if (readl(&i2c->regs->ctrl) & REG_CTRL_ERROR) {
 202                        debug("meson i2c: error\n");
 203                        return -EREMOTEIO;
 204                }
 205
 206                if ((msg->flags & I2C_M_RD) && i2c->count) {
 207                        meson_i2c_get_data(i2c, i2c->msg->buf + i2c->pos,
 208                                           i2c->count);
 209                }
 210                i2c->pos += i2c->count;
 211        } while (i2c->pos < msg->len);
 212
 213        return 0;
 214}
 215
 216static int meson_i2c_xfer(struct udevice *bus, struct i2c_msg *msg,
 217                          int nmsgs)
 218{
 219        struct meson_i2c *i2c = dev_get_priv(bus);
 220        int i, ret = 0;
 221
 222        for (i = 0; i < nmsgs; i++) {
 223                ret = meson_i2c_xfer_msg(i2c, msg + i, i == nmsgs - 1);
 224                if (ret)
 225                        return ret;
 226        }
 227
 228        return 0;
 229}
 230
 231static int meson_i2c_set_bus_speed(struct udevice *bus, unsigned int speed)
 232{
 233        struct meson_i2c *i2c = dev_get_priv(bus);
 234        ulong clk_rate;
 235        unsigned int div;
 236
 237        clk_rate = clk_get_rate(&i2c->clk);
 238        if (IS_ERR_VALUE(clk_rate))
 239                return -EINVAL;
 240
 241        div = DIV_ROUND_UP(clk_rate, speed * i2c->data->div_factor);
 242
 243        /* clock divider has 12 bits */
 244        if (div >= (1 << 12)) {
 245                debug("meson i2c: requested bus frequency too low\n");
 246                div = (1 << 12) - 1;
 247        }
 248
 249        clrsetbits_le32(&i2c->regs->ctrl, REG_CTRL_CLKDIV_MASK,
 250                        (div & GENMASK(9, 0)) << REG_CTRL_CLKDIV_SHIFT);
 251
 252        clrsetbits_le32(&i2c->regs->ctrl, REG_CTRL_CLKDIVEXT_MASK,
 253                        (div >> 10) << REG_CTRL_CLKDIVEXT_SHIFT);
 254
 255        debug("meson i2c: set clk %u, src %lu, div %u\n", speed, clk_rate, div);
 256
 257        return 0;
 258}
 259
 260static int meson_i2c_probe(struct udevice *bus)
 261{
 262        struct meson_i2c *i2c = dev_get_priv(bus);
 263        int ret;
 264
 265        i2c->data = (const struct meson_i2c_data *)dev_get_driver_data(bus);
 266
 267        ret = clk_get_by_index(bus, 0, &i2c->clk);
 268        if (ret < 0)
 269                return ret;
 270
 271        ret = clk_enable(&i2c->clk);
 272        if (ret)
 273                return ret;
 274
 275        i2c->regs = dev_read_addr_ptr(bus);
 276        clrbits_le32(&i2c->regs->ctrl, REG_CTRL_START);
 277
 278        return 0;
 279}
 280
 281static const struct dm_i2c_ops meson_i2c_ops = {
 282        .xfer          = meson_i2c_xfer,
 283        .set_bus_speed = meson_i2c_set_bus_speed,
 284};
 285
 286static const struct meson_i2c_data i2c_meson6_data = {
 287        .div_factor = 4,
 288};
 289
 290static const struct meson_i2c_data i2c_gxbb_data = {
 291        .div_factor = 4,
 292};
 293
 294static const struct meson_i2c_data i2c_axg_data = {
 295        .div_factor = 3,
 296};
 297
 298static const struct udevice_id meson_i2c_ids[] = {
 299        {.compatible = "amlogic,meson6-i2c", .data = (ulong)&i2c_meson6_data},
 300        {.compatible = "amlogic,meson-gx-i2c", .data = (ulong)&i2c_gxbb_data},
 301        {.compatible = "amlogic,meson-gxbb-i2c", .data = (ulong)&i2c_gxbb_data},
 302        {.compatible = "amlogic,meson-axg-i2c", .data = (ulong)&i2c_axg_data},
 303        {}
 304};
 305
 306U_BOOT_DRIVER(i2c_meson) = {
 307        .name = "i2c_meson",
 308        .id   = UCLASS_I2C,
 309        .of_match = meson_i2c_ids,
 310        .probe = meson_i2c_probe,
 311        .priv_auto      = sizeof(struct meson_i2c),
 312        .ops = &meson_i2c_ops,
 313};
 314