diff options
author | Ben Skeggs <bskeggs@redhat.com> | 2014-05-29 11:07:16 +1000 |
---|---|---|
committer | Ben Skeggs <bskeggs@redhat.com> | 2014-06-11 16:02:08 +1000 |
commit | 4c72915fbeee7e44b7320bd3dea97aea2451d0df (patch) | |
tree | c288d0fc5467e1fffd1f6b5c3f2a3dc541014e17 | |
parent | 59d57799e1ee481f3bcee64c271ca5c090f54fcc (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.h | 11 | ||||
-rw-r--r-- | nvkm/include/subdev/i2c.h | 2 | ||||
-rw-r--r-- | nvkm/subdev/i2c/aux.c | 32 | ||||
-rw-r--r-- | nvkm/subdev/i2c/base.c | 30 | ||||
-rw-r--r-- | nvkm/subdev/i2c/bit.c | 6 |
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 |