uboot/drivers/i2c/ihs_i2c.c
<<
>>
Prefs
   1/*
   2 * (C) Copyright 2013
   3 * Dirk Eibach,  Guntermann & Drunck GmbH, eibach@gdsys.de
   4 *
   5 * SPDX-License-Identifier:     GPL-2.0+
   6 */
   7
   8#include <common.h>
   9#include <i2c.h>
  10#include <gdsys_fpga.h>
  11
  12DECLARE_GLOBAL_DATA_PTR;
  13
  14enum {
  15        I2CINT_ERROR_EV = 1 << 13,
  16        I2CINT_TRANSMIT_EV = 1 << 14,
  17        I2CINT_RECEIVE_EV = 1 << 15,
  18};
  19
  20enum {
  21        I2CMB_WRITE = 1 << 10,
  22        I2CMB_2BYTE = 1 << 11,
  23        I2CMB_HOLD_BUS = 1 << 13,
  24        I2CMB_NATIVE = 2 << 14,
  25};
  26
  27static int wait_for_int(bool read)
  28{
  29        u16 val;
  30        unsigned int ctr = 0;
  31
  32        FPGA_GET_REG(I2C_ADAP_HWNR, i2c.interrupt_status, &val);
  33        while (!(val & (I2CINT_ERROR_EV
  34               | (read ? I2CINT_RECEIVE_EV : I2CINT_TRANSMIT_EV)))) {
  35                udelay(10);
  36                if (ctr++ > 5000) {
  37                        return 1;
  38                }
  39                FPGA_GET_REG(I2C_ADAP_HWNR, i2c.interrupt_status, &val);
  40        }
  41
  42        return (val & I2CINT_ERROR_EV) ? 1 : 0;
  43}
  44
  45static int ihs_i2c_transfer(uchar chip, uchar *buffer, int len, bool read,
  46                            bool is_last)
  47{
  48        u16 val;
  49
  50        FPGA_SET_REG(I2C_ADAP_HWNR, i2c.interrupt_status, I2CINT_ERROR_EV
  51                     | I2CINT_RECEIVE_EV | I2CINT_TRANSMIT_EV);
  52        FPGA_GET_REG(I2C_ADAP_HWNR, i2c.interrupt_status, &val);
  53
  54        if (!read && len) {
  55                val = buffer[0];
  56
  57                if (len > 1)
  58                        val |= buffer[1] << 8;
  59                FPGA_SET_REG(I2C_ADAP_HWNR, i2c.write_mailbox_ext, val);
  60        }
  61
  62        FPGA_SET_REG(I2C_ADAP_HWNR, i2c.write_mailbox,
  63                     I2CMB_NATIVE
  64                     | (read ? 0 : I2CMB_WRITE)
  65                     | (chip << 1)
  66                     | ((len > 1) ? I2CMB_2BYTE : 0)
  67                     | (is_last ? 0 : I2CMB_HOLD_BUS));
  68
  69        if (wait_for_int(read))
  70                return 1;
  71
  72        if (read) {
  73                FPGA_GET_REG(I2C_ADAP_HWNR, i2c.read_mailbox_ext, &val);
  74                buffer[0] = val & 0xff;
  75                if (len > 1)
  76                        buffer[1] = val >> 8;
  77        }
  78
  79        return 0;
  80}
  81
  82static int ihs_i2c_address(uchar chip, uint addr, int alen, bool hold_bus)
  83{
  84        int shift = (alen-1) * 8;
  85
  86        while (alen) {
  87                int transfer = MIN(alen, 2);
  88                uchar buf[2];
  89                bool is_last = alen <= transfer;
  90
  91                buf[0] = addr >> shift;
  92                if (alen > 1)
  93                        buf[1] = addr >> (shift - 8);
  94
  95                if (ihs_i2c_transfer(chip, buf, transfer, false,
  96                                     hold_bus ? false : is_last))
  97                        return 1;
  98
  99                shift -= 16;
 100                alen -= transfer;
 101        }
 102
 103        return 0;
 104}
 105
 106static int ihs_i2c_access(struct i2c_adapter *adap, uchar chip, uint addr,
 107                          int alen, uchar *buffer, int len, bool read)
 108{
 109        if (len <= 0)
 110                return 1;
 111
 112        if (ihs_i2c_address(chip, addr, alen, !read))
 113                return 1;
 114
 115        while (len) {
 116                int transfer = MIN(len, 2);
 117
 118                if (ihs_i2c_transfer(chip, buffer, transfer, read,
 119                                     len <= transfer))
 120                        return 1;
 121
 122                buffer += transfer;
 123                addr += transfer;
 124                len -= transfer;
 125        }
 126
 127        return 0;
 128}
 129
 130
 131static void ihs_i2c_init(struct i2c_adapter *adap, int speed, int slaveaddr)
 132{
 133#ifdef CONFIG_SYS_I2C_INIT_BOARD
 134        /*
 135         * Call board specific i2c bus reset routine before accessing the
 136         * environment, which might be in a chip on that bus. For details
 137         * about this problem see doc/I2C_Edge_Conditions.
 138         */
 139        i2c_init_board();
 140#endif
 141}
 142
 143static int ihs_i2c_probe(struct i2c_adapter *adap, uchar chip)
 144{
 145        uchar buffer[2];
 146
 147        if (ihs_i2c_transfer(chip, buffer, 0, true, true))
 148                return 1;
 149
 150        return 0;
 151}
 152
 153static int ihs_i2c_read(struct i2c_adapter *adap, uchar chip, uint addr,
 154                        int alen, uchar *buffer, int len)
 155{
 156        return ihs_i2c_access(adap, chip, addr, alen, buffer, len, true);
 157}
 158
 159static int ihs_i2c_write(struct i2c_adapter *adap, uchar chip, uint addr,
 160                         int alen, uchar *buffer, int len)
 161{
 162        return ihs_i2c_access(adap, chip, addr, alen, buffer, len, false);
 163}
 164
 165static unsigned int ihs_i2c_set_bus_speed(struct i2c_adapter *adap,
 166                                             unsigned int speed)
 167{
 168        if (speed != adap->speed)
 169                return 1;
 170        return speed;
 171}
 172
 173/*
 174 * Register IHS i2c adapters
 175 */
 176#ifdef CONFIG_SYS_I2C_IHS_CH0
 177U_BOOT_I2C_ADAP_COMPLETE(ihs0, ihs_i2c_init, ihs_i2c_probe,
 178                         ihs_i2c_read, ihs_i2c_write,
 179                         ihs_i2c_set_bus_speed,
 180                         CONFIG_SYS_I2C_IHS_SPEED_0,
 181                         CONFIG_SYS_I2C_IHS_SLAVE_0, 0)
 182#endif
 183#ifdef CONFIG_SYS_I2C_IHS_CH1
 184U_BOOT_I2C_ADAP_COMPLETE(ihs1, ihs_i2c_init, ihs_i2c_probe,
 185                         ihs_i2c_read, ihs_i2c_write,
 186                         ihs_i2c_set_bus_speed,
 187                         CONFIG_SYS_I2C_IHS_SPEED_1,
 188                         CONFIG_SYS_I2C_IHS_SLAVE_1, 1)
 189#endif
 190#ifdef CONFIG_SYS_I2C_IHS_CH2
 191U_BOOT_I2C_ADAP_COMPLETE(ihs2, ihs_i2c_init, ihs_i2c_probe,
 192                         ihs_i2c_read, ihs_i2c_write,
 193                         ihs_i2c_set_bus_speed,
 194                         CONFIG_SYS_I2C_IHS_SPEED_2,
 195                         CONFIG_SYS_I2C_IHS_SLAVE_2, 2)
 196#endif
 197#ifdef CONFIG_SYS_I2C_IHS_CH3
 198U_BOOT_I2C_ADAP_COMPLETE(ihs3, ihs_i2c_init, ihs_i2c_probe,
 199                         ihs_i2c_read, ihs_i2c_write,
 200                         ihs_i2c_set_bus_speed,
 201                         CONFIG_SYS_I2C_IHS_SPEED_3,
 202                         CONFIG_SYS_I2C_IHS_SLAVE_3, 3)
 203#endif
 204