linux/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/bit.c
<<
>>
Prefs
   1/*
   2 * Copyright 2012 Red Hat Inc.
   3 *
   4 * Permission is hereby granted, free of charge, to any person obtaining a
   5 * copy of this software and associated documentation files (the "Software"),
   6 * to deal in the Software without restriction, including without limitation
   7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
   8 * and/or sell copies of the Software, and to permit persons to whom the
   9 * Software is furnished to do so, subject to the following conditions:
  10 *
  11 * The above copyright notice and this permission notice shall be included in
  12 * all copies or substantial busions of the Software.
  13 *
  14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
  17 * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
  18 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
  19 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
  20 * OTHER DEALINGS IN THE SOFTWARE.
  21 *
  22 * Authors: Ben Skeggs
  23 */
  24#include "bus.h"
  25
  26#ifdef CONFIG_NOUVEAU_I2C_INTERNAL
  27#define T_TIMEOUT  2200000
  28#define T_RISEFALL 1000
  29#define T_HOLD     5000
  30
  31static inline void
  32nvkm_i2c_drive_scl(struct nvkm_i2c_bus *bus, int state)
  33{
  34        bus->func->drive_scl(bus, state);
  35}
  36
  37static inline void
  38nvkm_i2c_drive_sda(struct nvkm_i2c_bus *bus, int state)
  39{
  40        bus->func->drive_sda(bus, state);
  41}
  42
  43static inline int
  44nvkm_i2c_sense_scl(struct nvkm_i2c_bus *bus)
  45{
  46        return bus->func->sense_scl(bus);
  47}
  48
  49static inline int
  50nvkm_i2c_sense_sda(struct nvkm_i2c_bus *bus)
  51{
  52        return bus->func->sense_sda(bus);
  53}
  54
  55static void
  56nvkm_i2c_delay(struct nvkm_i2c_bus *bus, u32 nsec)
  57{
  58        udelay((nsec + 500) / 1000);
  59}
  60
  61static bool
  62nvkm_i2c_raise_scl(struct nvkm_i2c_bus *bus)
  63{
  64        u32 timeout = T_TIMEOUT / T_RISEFALL;
  65
  66        nvkm_i2c_drive_scl(bus, 1);
  67        do {
  68                nvkm_i2c_delay(bus, T_RISEFALL);
  69        } while (!nvkm_i2c_sense_scl(bus) && --timeout);
  70
  71        return timeout != 0;
  72}
  73
  74static int
  75i2c_start(struct nvkm_i2c_bus *bus)
  76{
  77        int ret = 0;
  78
  79        if (!nvkm_i2c_sense_scl(bus) ||
  80            !nvkm_i2c_sense_sda(bus)) {
  81                nvkm_i2c_drive_scl(bus, 0);
  82                nvkm_i2c_drive_sda(bus, 1);
  83                if (!nvkm_i2c_raise_scl(bus))
  84                        ret = -EBUSY;
  85        }
  86
  87        nvkm_i2c_drive_sda(bus, 0);
  88        nvkm_i2c_delay(bus, T_HOLD);
  89        nvkm_i2c_drive_scl(bus, 0);
  90        nvkm_i2c_delay(bus, T_HOLD);
  91        return ret;
  92}
  93
  94static void
  95i2c_stop(struct nvkm_i2c_bus *bus)
  96{
  97        nvkm_i2c_drive_scl(bus, 0);
  98        nvkm_i2c_drive_sda(bus, 0);
  99        nvkm_i2c_delay(bus, T_RISEFALL);
 100
 101        nvkm_i2c_drive_scl(bus, 1);
 102        nvkm_i2c_delay(bus, T_HOLD);
 103        nvkm_i2c_drive_sda(bus, 1);
 104        nvkm_i2c_delay(bus, T_HOLD);
 105}
 106
 107static int
 108i2c_bitw(struct nvkm_i2c_bus *bus, int sda)
 109{
 110        nvkm_i2c_drive_sda(bus, sda);
 111        nvkm_i2c_delay(bus, T_RISEFALL);
 112
 113        if (!nvkm_i2c_raise_scl(bus))
 114                return -ETIMEDOUT;
 115        nvkm_i2c_delay(bus, T_HOLD);
 116
 117        nvkm_i2c_drive_scl(bus, 0);
 118        nvkm_i2c_delay(bus, T_HOLD);
 119        return 0;
 120}
 121
 122static int
 123i2c_bitr(struct nvkm_i2c_bus *bus)
 124{
 125        int sda;
 126
 127        nvkm_i2c_drive_sda(bus, 1);
 128        nvkm_i2c_delay(bus, T_RISEFALL);
 129
 130        if (!nvkm_i2c_raise_scl(bus))
 131                return -ETIMEDOUT;
 132        nvkm_i2c_delay(bus, T_HOLD);
 133
 134        sda = nvkm_i2c_sense_sda(bus);
 135
 136        nvkm_i2c_drive_scl(bus, 0);
 137        nvkm_i2c_delay(bus, T_HOLD);
 138        return sda;
 139}
 140
 141static int
 142nvkm_i2c_get_byte(struct nvkm_i2c_bus *bus, u8 *byte, bool last)
 143{
 144        int i, bit;
 145
 146        *byte = 0;
 147        for (i = 7; i >= 0; i--) {
 148                bit = i2c_bitr(bus);
 149                if (bit < 0)
 150                        return bit;
 151                *byte |= bit << i;
 152        }
 153
 154        return i2c_bitw(bus, last ? 1 : 0);
 155}
 156
 157static int
 158nvkm_i2c_put_byte(struct nvkm_i2c_bus *bus, u8 byte)
 159{
 160        int i, ret;
 161        for (i = 7; i >= 0; i--) {
 162                ret = i2c_bitw(bus, !!(byte & (1 << i)));
 163                if (ret < 0)
 164                        return ret;
 165        }
 166
 167        ret = i2c_bitr(bus);
 168        if (ret == 1) /* nack */
 169                ret = -EIO;
 170        return ret;
 171}
 172
 173static int
 174i2c_addr(struct nvkm_i2c_bus *bus, struct i2c_msg *msg)
 175{
 176        u32 addr = msg->addr << 1;
 177        if (msg->flags & I2C_M_RD)
 178                addr |= 1;
 179        return nvkm_i2c_put_byte(bus, addr);
 180}
 181
 182int
 183nvkm_i2c_bit_xfer(struct nvkm_i2c_bus *bus, struct i2c_msg *msgs, int num)
 184{
 185        struct i2c_msg *msg = msgs;
 186        int ret = 0, mcnt = num;
 187
 188        while (!ret && mcnt--) {
 189                u8 remaining = msg->len;
 190                u8 *ptr = msg->buf;
 191
 192                ret = i2c_start(bus);
 193                if (ret == 0)
 194                        ret = i2c_addr(bus, msg);
 195
 196                if (msg->flags & I2C_M_RD) {
 197                        while (!ret && remaining--)
 198                                ret = nvkm_i2c_get_byte(bus, ptr++, !remaining);
 199                } else {
 200                        while (!ret && remaining--)
 201                                ret = nvkm_i2c_put_byte(bus, *ptr++);
 202                }
 203
 204                msg++;
 205        }
 206
 207        i2c_stop(bus);
 208        return (ret < 0) ? ret : num;
 209}
 210#else
 211int
 212nvkm_i2c_bit_xfer(struct nvkm_i2c_bus *bus, struct i2c_msg *msgs, int num)
 213{
 214        return -ENODEV;
 215}
 216#endif
 217