|
@@ -29,10 +29,6 @@
|
|
|
#include "nouveau_i2c.h"
|
|
|
#include "nouveau_hw.h"
|
|
|
|
|
|
-#define T_TIMEOUT 2200000
|
|
|
-#define T_RISEFALL 1000
|
|
|
-#define T_HOLD 5000
|
|
|
-
|
|
|
static void
|
|
|
i2c_drive_scl(void *data, int state)
|
|
|
{
|
|
@@ -113,175 +109,6 @@ i2c_sense_sda(void *data)
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
-static void
|
|
|
-i2c_delay(struct nouveau_i2c_chan *port, u32 nsec)
|
|
|
-{
|
|
|
- udelay((nsec + 500) / 1000);
|
|
|
-}
|
|
|
-
|
|
|
-static bool
|
|
|
-i2c_raise_scl(struct nouveau_i2c_chan *port)
|
|
|
-{
|
|
|
- u32 timeout = T_TIMEOUT / T_RISEFALL;
|
|
|
-
|
|
|
- i2c_drive_scl(port, 1);
|
|
|
- do {
|
|
|
- i2c_delay(port, T_RISEFALL);
|
|
|
- } while (!i2c_sense_scl(port) && --timeout);
|
|
|
-
|
|
|
- return timeout != 0;
|
|
|
-}
|
|
|
-
|
|
|
-static int
|
|
|
-i2c_start(struct nouveau_i2c_chan *port)
|
|
|
-{
|
|
|
- int ret = 0;
|
|
|
-
|
|
|
- port->state = i2c_sense_scl(port);
|
|
|
- port->state |= i2c_sense_sda(port) << 1;
|
|
|
- if (port->state != 3) {
|
|
|
- i2c_drive_scl(port, 0);
|
|
|
- i2c_drive_sda(port, 1);
|
|
|
- if (!i2c_raise_scl(port))
|
|
|
- ret = -EBUSY;
|
|
|
- }
|
|
|
-
|
|
|
- i2c_drive_sda(port, 0);
|
|
|
- i2c_delay(port, T_HOLD);
|
|
|
- i2c_drive_scl(port, 0);
|
|
|
- i2c_delay(port, T_HOLD);
|
|
|
- return ret;
|
|
|
-}
|
|
|
-
|
|
|
-static void
|
|
|
-i2c_stop(struct nouveau_i2c_chan *port)
|
|
|
-{
|
|
|
- i2c_drive_scl(port, 0);
|
|
|
- i2c_drive_sda(port, 0);
|
|
|
- i2c_delay(port, T_RISEFALL);
|
|
|
-
|
|
|
- i2c_drive_scl(port, 1);
|
|
|
- i2c_delay(port, T_HOLD);
|
|
|
- i2c_drive_sda(port, 1);
|
|
|
- i2c_delay(port, T_HOLD);
|
|
|
-}
|
|
|
-
|
|
|
-static int
|
|
|
-i2c_bitw(struct nouveau_i2c_chan *port, int sda)
|
|
|
-{
|
|
|
- i2c_drive_sda(port, sda);
|
|
|
- i2c_delay(port, T_RISEFALL);
|
|
|
-
|
|
|
- if (!i2c_raise_scl(port))
|
|
|
- return -ETIMEDOUT;
|
|
|
- i2c_delay(port, T_HOLD);
|
|
|
-
|
|
|
- i2c_drive_scl(port, 0);
|
|
|
- i2c_delay(port, T_HOLD);
|
|
|
- return 0;
|
|
|
-}
|
|
|
-
|
|
|
-static int
|
|
|
-i2c_bitr(struct nouveau_i2c_chan *port)
|
|
|
-{
|
|
|
- int sda;
|
|
|
-
|
|
|
- i2c_drive_sda(port, 1);
|
|
|
- i2c_delay(port, T_RISEFALL);
|
|
|
-
|
|
|
- if (!i2c_raise_scl(port))
|
|
|
- return -ETIMEDOUT;
|
|
|
- i2c_delay(port, T_HOLD);
|
|
|
-
|
|
|
- sda = i2c_sense_sda(port);
|
|
|
-
|
|
|
- i2c_drive_scl(port, 0);
|
|
|
- i2c_delay(port, T_HOLD);
|
|
|
- return sda;
|
|
|
-}
|
|
|
-
|
|
|
-static int
|
|
|
-i2c_get_byte(struct nouveau_i2c_chan *port, u8 *byte, bool last)
|
|
|
-{
|
|
|
- int i, bit;
|
|
|
-
|
|
|
- *byte = 0;
|
|
|
- for (i = 7; i >= 0; i--) {
|
|
|
- bit = i2c_bitr(port);
|
|
|
- if (bit < 0)
|
|
|
- return bit;
|
|
|
- *byte |= bit << i;
|
|
|
- }
|
|
|
-
|
|
|
- return i2c_bitw(port, last ? 1 : 0);
|
|
|
-}
|
|
|
-
|
|
|
-static int
|
|
|
-i2c_put_byte(struct nouveau_i2c_chan *port, u8 byte)
|
|
|
-{
|
|
|
- int i, ret;
|
|
|
- for (i = 7; i >= 0; i--) {
|
|
|
- ret = i2c_bitw(port, !!(byte & (1 << i)));
|
|
|
- if (ret < 0)
|
|
|
- return ret;
|
|
|
- }
|
|
|
-
|
|
|
- ret = i2c_bitr(port);
|
|
|
- if (ret == 1) /* nack */
|
|
|
- ret = -EIO;
|
|
|
- return ret;
|
|
|
-}
|
|
|
-
|
|
|
-static int
|
|
|
-i2c_addr(struct nouveau_i2c_chan *port, struct i2c_msg *msg)
|
|
|
-{
|
|
|
- u32 addr = msg->addr << 1;
|
|
|
- if (msg->flags & I2C_M_RD)
|
|
|
- addr |= 1;
|
|
|
- return i2c_put_byte(port, addr);
|
|
|
-}
|
|
|
-
|
|
|
-static int
|
|
|
-i2c_bit_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
|
|
|
-{
|
|
|
- struct nouveau_i2c_chan *port = (struct nouveau_i2c_chan *)adap;
|
|
|
- struct i2c_msg *msg = msgs;
|
|
|
- int ret = 0, mcnt = num;
|
|
|
-
|
|
|
- while (!ret && mcnt--) {
|
|
|
- u8 remaining = msg->len;
|
|
|
- u8 *ptr = msg->buf;
|
|
|
-
|
|
|
- ret = i2c_start(port);
|
|
|
- if (ret == 0)
|
|
|
- ret = i2c_addr(port, msg);
|
|
|
-
|
|
|
- if (msg->flags & I2C_M_RD) {
|
|
|
- while (!ret && remaining--)
|
|
|
- ret = i2c_get_byte(port, ptr++, !remaining);
|
|
|
- } else {
|
|
|
- while (!ret && remaining--)
|
|
|
- ret = i2c_put_byte(port, *ptr++);
|
|
|
- }
|
|
|
-
|
|
|
- msg++;
|
|
|
- }
|
|
|
-
|
|
|
- i2c_stop(port);
|
|
|
- return (ret < 0) ? ret : num;
|
|
|
-}
|
|
|
-
|
|
|
-static u32
|
|
|
-i2c_bit_func(struct i2c_adapter *adap)
|
|
|
-{
|
|
|
- return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
|
|
|
-}
|
|
|
-
|
|
|
-const struct i2c_algorithm nouveau_i2c_bit_algo = {
|
|
|
- .master_xfer = i2c_bit_xfer,
|
|
|
- .functionality = i2c_bit_func
|
|
|
-};
|
|
|
-
|
|
|
static const uint32_t nv50_i2c_port[] = {
|
|
|
0x00e138, 0x00e150, 0x00e168, 0x00e180,
|
|
|
0x00e254, 0x00e274, 0x00e764, 0x00e780,
|
|
@@ -384,12 +211,10 @@ nouveau_i2c_init(struct drm_device *dev)
|
|
|
case 0: /* NV04:NV50 */
|
|
|
port->drive = entry[0];
|
|
|
port->sense = entry[1];
|
|
|
- port->adapter.algo = &nouveau_i2c_bit_algo;
|
|
|
break;
|
|
|
case 4: /* NV4E */
|
|
|
port->drive = 0x600800 + entry[1];
|
|
|
port->sense = port->drive;
|
|
|
- port->adapter.algo = &nouveau_i2c_bit_algo;
|
|
|
break;
|
|
|
case 5: /* NV50- */
|
|
|
port->drive = entry[0] & 0x0f;
|
|
@@ -402,7 +227,6 @@ nouveau_i2c_init(struct drm_device *dev)
|
|
|
port->drive = 0x00d014 + (port->drive * 0x20);
|
|
|
port->sense = port->drive;
|
|
|
}
|
|
|
- port->adapter.algo = &nouveau_i2c_bit_algo;
|
|
|
break;
|
|
|
case 6: /* NV50- DP AUX */
|
|
|
port->drive = entry[0];
|
|
@@ -413,7 +237,7 @@ nouveau_i2c_init(struct drm_device *dev)
|
|
|
break;
|
|
|
}
|
|
|
|
|
|
- if (!port->adapter.algo) {
|
|
|
+ if (!port->adapter.algo && !port->drive) {
|
|
|
NV_ERROR(dev, "I2C%d: type %d index %x/%x unknown\n",
|
|
|
i, port->type, port->drive, port->sense);
|
|
|
kfree(port);
|
|
@@ -429,7 +253,26 @@ nouveau_i2c_init(struct drm_device *dev)
|
|
|
port->dcb = ROM32(entry[0]);
|
|
|
i2c_set_adapdata(&port->adapter, i2c);
|
|
|
|
|
|
- ret = i2c_add_adapter(&port->adapter);
|
|
|
+ if (port->adapter.algo != &nouveau_dp_i2c_algo) {
|
|
|
+ port->adapter.algo_data = &port->bit;
|
|
|
+ port->bit.udelay = 10;
|
|
|
+ port->bit.timeout = usecs_to_jiffies(2200);
|
|
|
+ port->bit.data = port;
|
|
|
+ port->bit.setsda = i2c_drive_sda;
|
|
|
+ port->bit.setscl = i2c_drive_scl;
|
|
|
+ port->bit.getsda = i2c_sense_sda;
|
|
|
+ port->bit.getscl = i2c_sense_scl;
|
|
|
+
|
|
|
+ i2c_drive_scl(port, 0);
|
|
|
+ i2c_drive_sda(port, 1);
|
|
|
+ i2c_drive_scl(port, 1);
|
|
|
+
|
|
|
+ ret = i2c_bit_add_bus(&port->adapter);
|
|
|
+ } else {
|
|
|
+ port->adapter.algo = &nouveau_dp_i2c_algo;
|
|
|
+ ret = i2c_add_adapter(&port->adapter);
|
|
|
+ }
|
|
|
+
|
|
|
if (ret) {
|
|
|
NV_ERROR(dev, "I2C%d: failed register: %d\n", i, ret);
|
|
|
kfree(port);
|