Back to home page

OSCL-LXR

 
 

    


0001 /*
0002  * Copyright 2012 Red Hat Inc.
0003  *
0004  * Permission is hereby granted, free of charge, to any person obtaining a
0005  * copy of this software and associated documentation files (the "Software"),
0006  * to deal in the Software without restriction, including without limitation
0007  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
0008  * and/or sell copies of the Software, and to permit persons to whom the
0009  * Software is furnished to do so, subject to the following conditions:
0010  *
0011  * The above copyright notice and this permission notice shall be included in
0012  * all copies or substantial busions of the Software.
0013  *
0014  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
0015  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
0016  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
0017  * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
0018  * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
0019  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
0020  * OTHER DEALINGS IN THE SOFTWARE.
0021  *
0022  * Authors: Ben Skeggs
0023  */
0024 #include "bus.h"
0025 
0026 #ifdef CONFIG_NOUVEAU_I2C_INTERNAL
0027 #define T_TIMEOUT  2200000
0028 #define T_RISEFALL 1000
0029 #define T_HOLD     5000
0030 
0031 static inline void
0032 nvkm_i2c_drive_scl(struct nvkm_i2c_bus *bus, int state)
0033 {
0034     bus->func->drive_scl(bus, state);
0035 }
0036 
0037 static inline void
0038 nvkm_i2c_drive_sda(struct nvkm_i2c_bus *bus, int state)
0039 {
0040     bus->func->drive_sda(bus, state);
0041 }
0042 
0043 static inline int
0044 nvkm_i2c_sense_scl(struct nvkm_i2c_bus *bus)
0045 {
0046     return bus->func->sense_scl(bus);
0047 }
0048 
0049 static inline int
0050 nvkm_i2c_sense_sda(struct nvkm_i2c_bus *bus)
0051 {
0052     return bus->func->sense_sda(bus);
0053 }
0054 
0055 static void
0056 nvkm_i2c_delay(struct nvkm_i2c_bus *bus, u32 nsec)
0057 {
0058     udelay((nsec + 500) / 1000);
0059 }
0060 
0061 static bool
0062 nvkm_i2c_raise_scl(struct nvkm_i2c_bus *bus)
0063 {
0064     u32 timeout = T_TIMEOUT / T_RISEFALL;
0065 
0066     nvkm_i2c_drive_scl(bus, 1);
0067     do {
0068         nvkm_i2c_delay(bus, T_RISEFALL);
0069     } while (!nvkm_i2c_sense_scl(bus) && --timeout);
0070 
0071     return timeout != 0;
0072 }
0073 
0074 static int
0075 i2c_start(struct nvkm_i2c_bus *bus)
0076 {
0077     int ret = 0;
0078 
0079     if (!nvkm_i2c_sense_scl(bus) ||
0080         !nvkm_i2c_sense_sda(bus)) {
0081         nvkm_i2c_drive_scl(bus, 0);
0082         nvkm_i2c_drive_sda(bus, 1);
0083         if (!nvkm_i2c_raise_scl(bus))
0084             ret = -EBUSY;
0085     }
0086 
0087     nvkm_i2c_drive_sda(bus, 0);
0088     nvkm_i2c_delay(bus, T_HOLD);
0089     nvkm_i2c_drive_scl(bus, 0);
0090     nvkm_i2c_delay(bus, T_HOLD);
0091     return ret;
0092 }
0093 
0094 static void
0095 i2c_stop(struct nvkm_i2c_bus *bus)
0096 {
0097     nvkm_i2c_drive_scl(bus, 0);
0098     nvkm_i2c_drive_sda(bus, 0);
0099     nvkm_i2c_delay(bus, T_RISEFALL);
0100 
0101     nvkm_i2c_drive_scl(bus, 1);
0102     nvkm_i2c_delay(bus, T_HOLD);
0103     nvkm_i2c_drive_sda(bus, 1);
0104     nvkm_i2c_delay(bus, T_HOLD);
0105 }
0106 
0107 static int
0108 i2c_bitw(struct nvkm_i2c_bus *bus, int sda)
0109 {
0110     nvkm_i2c_drive_sda(bus, sda);
0111     nvkm_i2c_delay(bus, T_RISEFALL);
0112 
0113     if (!nvkm_i2c_raise_scl(bus))
0114         return -ETIMEDOUT;
0115     nvkm_i2c_delay(bus, T_HOLD);
0116 
0117     nvkm_i2c_drive_scl(bus, 0);
0118     nvkm_i2c_delay(bus, T_HOLD);
0119     return 0;
0120 }
0121 
0122 static int
0123 i2c_bitr(struct nvkm_i2c_bus *bus)
0124 {
0125     int sda;
0126 
0127     nvkm_i2c_drive_sda(bus, 1);
0128     nvkm_i2c_delay(bus, T_RISEFALL);
0129 
0130     if (!nvkm_i2c_raise_scl(bus))
0131         return -ETIMEDOUT;
0132     nvkm_i2c_delay(bus, T_HOLD);
0133 
0134     sda = nvkm_i2c_sense_sda(bus);
0135 
0136     nvkm_i2c_drive_scl(bus, 0);
0137     nvkm_i2c_delay(bus, T_HOLD);
0138     return sda;
0139 }
0140 
0141 static int
0142 nvkm_i2c_get_byte(struct nvkm_i2c_bus *bus, u8 *byte, bool last)
0143 {
0144     int i, bit;
0145 
0146     *byte = 0;
0147     for (i = 7; i >= 0; i--) {
0148         bit = i2c_bitr(bus);
0149         if (bit < 0)
0150             return bit;
0151         *byte |= bit << i;
0152     }
0153 
0154     return i2c_bitw(bus, last ? 1 : 0);
0155 }
0156 
0157 static int
0158 nvkm_i2c_put_byte(struct nvkm_i2c_bus *bus, u8 byte)
0159 {
0160     int i, ret;
0161     for (i = 7; i >= 0; i--) {
0162         ret = i2c_bitw(bus, !!(byte & (1 << i)));
0163         if (ret < 0)
0164             return ret;
0165     }
0166 
0167     ret = i2c_bitr(bus);
0168     if (ret == 1) /* nack */
0169         ret = -EIO;
0170     return ret;
0171 }
0172 
0173 static int
0174 i2c_addr(struct nvkm_i2c_bus *bus, struct i2c_msg *msg)
0175 {
0176     u32 addr = msg->addr << 1;
0177     if (msg->flags & I2C_M_RD)
0178         addr |= 1;
0179     return nvkm_i2c_put_byte(bus, addr);
0180 }
0181 
0182 int
0183 nvkm_i2c_bit_xfer(struct nvkm_i2c_bus *bus, struct i2c_msg *msgs, int num)
0184 {
0185     struct i2c_msg *msg = msgs;
0186     int ret = 0, mcnt = num;
0187 
0188     while (!ret && mcnt--) {
0189         u8 remaining = msg->len;
0190         u8 *ptr = msg->buf;
0191 
0192         ret = i2c_start(bus);
0193         if (ret == 0)
0194             ret = i2c_addr(bus, msg);
0195 
0196         if (msg->flags & I2C_M_RD) {
0197             while (!ret && remaining--)
0198                 ret = nvkm_i2c_get_byte(bus, ptr++, !remaining);
0199         } else {
0200             while (!ret && remaining--)
0201                 ret = nvkm_i2c_put_byte(bus, *ptr++);
0202         }
0203 
0204         msg++;
0205     }
0206 
0207     i2c_stop(bus);
0208     return (ret < 0) ? ret : num;
0209 }
0210 #else
0211 int
0212 nvkm_i2c_bit_xfer(struct nvkm_i2c_bus *bus, struct i2c_msg *msgs, int num)
0213 {
0214     return -ENODEV;
0215 }
0216 #endif