summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBen Skeggs <bskeggs@redhat.com>2014-05-29 11:07:16 +1000
committerBen Skeggs <bskeggs@redhat.com>2014-06-11 16:02:08 +1000
commit4c72915fbeee7e44b7320bd3dea97aea2451d0df (patch)
treec288d0fc5467e1fffd1f6b5c3f2a3dc541014e17
parent59d57799e1ee481f3bcee64c271ca5c090f54fcc (diff)
i2c: balance port acquire/release
This was a half-finished hack before, just enough to handle the shared aux/i2c pad thing on G94 and up. We got lucky with locking etc up until now, as this was (generally) all protected by the DRM mode_config lock. It's about to become a lot more likely to hit the races. Signed-off-by: Ben Skeggs <bskeggs@redhat.com>
-rw-r--r--lib/core/os.h11
-rw-r--r--nvkm/include/subdev/i2c.h2
-rw-r--r--nvkm/subdev/i2c/aux.c32
-rw-r--r--nvkm/subdev/i2c/base.c30
-rw-r--r--nvkm/subdev/i2c/bit.c6
5 files changed, 63 insertions, 18 deletions
diff --git a/lib/core/os.h b/lib/core/os.h
index 867d8128..456daa77 100644
--- a/lib/core/os.h
+++ b/lib/core/os.h
@@ -78,8 +78,9 @@ typedef dma_addr_t resource_size_t;
#define unlikely(a) (a)
#define BIT(a) (1UL << (a))
-#define jiffies (ktime_to_us(ktime_get()))
-#define usecs_to_jiffies(a) (a)
+#define jiffies (ktime_to_ns(ktime_get()))
+#define nsecs_to_jiffies(a) (a)
+#define usecs_to_jiffies(a) nsecs_to_jiffies((a) * 1000)
#define msecs_to_jiffies(a) usecs_to_jiffies((a) * 1000)
#define HZ 1000000
@@ -906,6 +907,7 @@ typedef struct __wait_queue_head {
#define init_waitqueue_head(wq)
#define wake_up(wq)
+#define wake_up_all(wq)
#define wait_event(wq,cond) do { \
usleep(1); \
@@ -916,7 +918,7 @@ typedef struct __wait_queue_head {
})
#define wait_event_timeout(wq,cond,jiffies) ({ \
- unsigned long _t = (jiffies) / 10; \
+ unsigned long _t = (jiffies) / 10000; \
do { \
if (cond) \
break; \
@@ -1034,13 +1036,14 @@ i2c_bit_algo = {
struct i2c_algo_bit_data {
int udelay;
- int timeout;
+ unsigned long timeout;
void *data;
void (*setsda) (void *data, int state);
void (*setscl) (void *data, int state);
int (*getsda) (void *data);
int (*getscl) (void *data);
int (*pre_xfer)(struct i2c_adapter *);
+ void (*post_xfer)(struct i2c_adapter *);
};
static inline int
diff --git a/nvkm/include/subdev/i2c.h b/nvkm/include/subdev/i2c.h
index aaa36158..9c150568 100644
--- a/nvkm/include/subdev/i2c.h
+++ b/nvkm/include/subdev/i2c.h
@@ -62,6 +62,8 @@ struct nouveau_i2c {
struct nouveau_i2c_port *(*find)(struct nouveau_i2c *, u8 index);
struct nouveau_i2c_port *(*find_type)(struct nouveau_i2c *, u16 type);
+ int (*acquire)(struct nouveau_i2c_port *, unsigned long timeout);
+ void (*release)(struct nouveau_i2c_port *);
int (*identify)(struct nouveau_i2c *, int index,
const char *what, struct nouveau_i2c_board_info *,
bool (*match)(struct nouveau_i2c_port *,
diff --git a/nvkm/subdev/i2c/aux.c b/nvkm/subdev/i2c/aux.c
index cad742bd..02eb42be 100644
--- a/nvkm/subdev/i2c/aux.c
+++ b/nvkm/subdev/i2c/aux.c
@@ -27,10 +27,14 @@
int
nv_rdaux(struct nouveau_i2c_port *port, u32 addr, u8 *data, u8 size)
{
+ struct nouveau_i2c *i2c = nouveau_i2c(port);
if (port->func->aux) {
- if (port->func->acquire)
- port->func->acquire(port);
- return port->func->aux(port, true, 9, addr, data, size);
+ int ret = i2c->acquire(port, 0);
+ if (ret == 0) {
+ ret = port->func->aux(port, true, 9, addr, data, size);
+ i2c->release(port);
+ }
+ return ret;
}
return -ENODEV;
}
@@ -38,10 +42,14 @@ nv_rdaux(struct nouveau_i2c_port *port, u32 addr, u8 *data, u8 size)
int
nv_wraux(struct nouveau_i2c_port *port, u32 addr, u8 *data, u8 size)
{
+ struct nouveau_i2c *i2c = nouveau_i2c(port);
if (port->func->aux) {
- if (port->func->acquire)
- port->func->acquire(port);
- return port->func->aux(port, true, 8, addr, data, size);
+ int ret = i2c->acquire(port, 0);
+ if (ret == 0) {
+ ret = port->func->aux(port, true, 8, addr, data, size);
+ i2c->release(port);
+ }
+ return ret;
}
return -ENODEV;
}
@@ -50,13 +58,16 @@ static int
aux_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
{
struct nouveau_i2c_port *port = adap->algo_data;
+ struct nouveau_i2c *i2c = nouveau_i2c(port);
struct i2c_msg *msg = msgs;
int ret, mcnt = num;
if (!port->func->aux)
return -ENODEV;
- if ( port->func->acquire)
- port->func->acquire(port);
+
+ ret = i2c->acquire(port, 0);
+ if (ret)
+ return ret;
while (mcnt--) {
u8 remaining = msg->len;
@@ -75,8 +86,10 @@ aux_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
cmd |= 4; /* MOT */
ret = port->func->aux(port, true, cmd, msg->addr, ptr, cnt);
- if (ret < 0)
+ if (ret < 0) {
+ i2c->release(port);
return ret;
+ }
ptr += cnt;
remaining -= cnt;
@@ -85,6 +98,7 @@ aux_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
msg++;
}
+ i2c->release(port);
return num;
}
diff --git a/nvkm/subdev/i2c/base.c b/nvkm/subdev/i2c/base.c
index 0e360576..a230465f 100644
--- a/nvkm/subdev/i2c/base.c
+++ b/nvkm/subdev/i2c/base.c
@@ -47,9 +47,15 @@ nouveau_i2c_pre_xfer(struct i2c_adapter *adap)
{
struct i2c_algo_bit_data *bit = adap->algo_data;
struct nouveau_i2c_port *port = bit->data;
- if (port->func->acquire)
- port->func->acquire(port);
- return 0;
+ return nouveau_i2c(port)->acquire(port, bit->timeout);
+}
+
+static void
+nouveau_i2c_post_xfer(struct i2c_adapter *adap)
+{
+ struct i2c_algo_bit_data *bit = adap->algo_data;
+ struct nouveau_i2c_port *port = bit->data;
+ return nouveau_i2c(port)->release(port);
}
static void
@@ -130,6 +136,7 @@ nouveau_i2c_port_create_(struct nouveau_object *parent,
bit->timeout = usecs_to_jiffies(2200);
bit->data = port;
bit->pre_xfer = nouveau_i2c_pre_xfer;
+ bit->post_xfer = nouveau_i2c_post_xfer;
bit->setsda = nouveau_i2c_setsda;
bit->setscl = nouveau_i2c_setscl;
bit->getsda = nouveau_i2c_getsda;
@@ -194,6 +201,21 @@ nouveau_i2c_find_type(struct nouveau_i2c *i2c, u16 type)
return NULL;
}
+static void
+nouveau_i2c_release(struct nouveau_i2c_port *port)
+{
+ if (port->func->release)
+ port->func->release(port);
+}
+
+static int
+nouveau_i2c_acquire(struct nouveau_i2c_port *port, unsigned long timeout)
+{
+ if (port->func->acquire)
+ port->func->acquire(port);
+ return 0;
+}
+
static int
nouveau_i2c_identify(struct nouveau_i2c *i2c, int index, const char *what,
struct nouveau_i2c_board_info *info,
@@ -383,6 +405,8 @@ nouveau_i2c_create_(struct nouveau_object *parent,
nv_subdev(i2c)->intr = nouveau_i2c_intr;
i2c->find = nouveau_i2c_find;
i2c->find_type = nouveau_i2c_find_type;
+ i2c->acquire = nouveau_i2c_acquire;
+ i2c->release = nouveau_i2c_release;
i2c->identify = nouveau_i2c_identify;
INIT_LIST_HEAD(&i2c->ports);
diff --git a/nvkm/subdev/i2c/bit.c b/nvkm/subdev/i2c/bit.c
index 8fda0547..813ffc96 100644
--- a/nvkm/subdev/i2c/bit.c
+++ b/nvkm/subdev/i2c/bit.c
@@ -187,8 +187,9 @@ i2c_bit_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
struct i2c_msg *msg = msgs;
int ret = 0, mcnt = num;
- if (port->func->acquire)
- port->func->acquire(port);
+ ret = nouveau_i2c(port)->acquire(port, nsecs_to_jiffies(T_TIMEOUT));
+ if (ret)
+ return ret;
while (!ret && mcnt--) {
u8 remaining = msg->len;
@@ -210,6 +211,7 @@ i2c_bit_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
}
i2c_stop(port);
+ nouveau_i2c(port)->release(port);
return (ret < 0) ? ret : num;
}
#else