drm/nouveau/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>
This commit is contained in:
Ben Skeggs 2014-05-29 11:07:16 +10:00
parent 0ff32977ea
commit d2ae2eb469
4 changed files with 56 additions and 14 deletions

View File

@ -62,6 +62,8 @@ struct nouveau_i2c {
struct nouveau_i2c_port *(*find)(struct nouveau_i2c *, u8 index); struct nouveau_i2c_port *(*find)(struct nouveau_i2c *, u8 index);
struct nouveau_i2c_port *(*find_type)(struct nouveau_i2c *, u16 type); 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, int (*identify)(struct nouveau_i2c *, int index,
const char *what, struct nouveau_i2c_board_info *, const char *what, struct nouveau_i2c_board_info *,
bool (*match)(struct nouveau_i2c_port *, bool (*match)(struct nouveau_i2c_port *,

View File

@ -27,10 +27,14 @@
int int
nv_rdaux(struct nouveau_i2c_port *port, u32 addr, u8 *data, u8 size) 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->aux) {
if (port->func->acquire) int ret = i2c->acquire(port, 0);
port->func->acquire(port); if (ret == 0) {
return port->func->aux(port, true, 9, addr, data, size); ret = port->func->aux(port, true, 9, addr, data, size);
i2c->release(port);
}
return ret;
} }
return -ENODEV; return -ENODEV;
} }
@ -38,10 +42,14 @@ nv_rdaux(struct nouveau_i2c_port *port, u32 addr, u8 *data, u8 size)
int int
nv_wraux(struct nouveau_i2c_port *port, u32 addr, u8 *data, u8 size) 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->aux) {
if (port->func->acquire) int ret = i2c->acquire(port, 0);
port->func->acquire(port); if (ret == 0) {
return port->func->aux(port, true, 8, addr, data, size); ret = port->func->aux(port, true, 8, addr, data, size);
i2c->release(port);
}
return ret;
} }
return -ENODEV; return -ENODEV;
} }
@ -50,13 +58,16 @@ static int
aux_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) aux_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
{ {
struct nouveau_i2c_port *port = adap->algo_data; struct nouveau_i2c_port *port = adap->algo_data;
struct nouveau_i2c *i2c = nouveau_i2c(port);
struct i2c_msg *msg = msgs; struct i2c_msg *msg = msgs;
int ret, mcnt = num; int ret, mcnt = num;
if (!port->func->aux) if (!port->func->aux)
return -ENODEV; return -ENODEV;
if ( port->func->acquire)
port->func->acquire(port); ret = i2c->acquire(port, 0);
if (ret)
return ret;
while (mcnt--) { while (mcnt--) {
u8 remaining = msg->len; u8 remaining = msg->len;
@ -75,8 +86,10 @@ aux_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
cmd |= 4; /* MOT */ cmd |= 4; /* MOT */
ret = port->func->aux(port, true, cmd, msg->addr, ptr, cnt); ret = port->func->aux(port, true, cmd, msg->addr, ptr, cnt);
if (ret < 0) if (ret < 0) {
i2c->release(port);
return ret; return ret;
}
ptr += cnt; ptr += cnt;
remaining -= cnt; remaining -= cnt;
@ -85,6 +98,7 @@ aux_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
msg++; msg++;
} }
i2c->release(port);
return num; return num;
} }

View File

@ -47,9 +47,15 @@ nouveau_i2c_pre_xfer(struct i2c_adapter *adap)
{ {
struct i2c_algo_bit_data *bit = adap->algo_data; struct i2c_algo_bit_data *bit = adap->algo_data;
struct nouveau_i2c_port *port = bit->data; struct nouveau_i2c_port *port = bit->data;
if (port->func->acquire) return nouveau_i2c(port)->acquire(port, bit->timeout);
port->func->acquire(port); }
return 0;
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 static void
@ -130,6 +136,7 @@ nouveau_i2c_port_create_(struct nouveau_object *parent,
bit->timeout = usecs_to_jiffies(2200); bit->timeout = usecs_to_jiffies(2200);
bit->data = port; bit->data = port;
bit->pre_xfer = nouveau_i2c_pre_xfer; bit->pre_xfer = nouveau_i2c_pre_xfer;
bit->post_xfer = nouveau_i2c_post_xfer;
bit->setsda = nouveau_i2c_setsda; bit->setsda = nouveau_i2c_setsda;
bit->setscl = nouveau_i2c_setscl; bit->setscl = nouveau_i2c_setscl;
bit->getsda = nouveau_i2c_getsda; bit->getsda = nouveau_i2c_getsda;
@ -194,6 +201,21 @@ nouveau_i2c_find_type(struct nouveau_i2c *i2c, u16 type)
return NULL; 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 static int
nouveau_i2c_identify(struct nouveau_i2c *i2c, int index, const char *what, nouveau_i2c_identify(struct nouveau_i2c *i2c, int index, const char *what,
struct nouveau_i2c_board_info *info, struct nouveau_i2c_board_info *info,
@ -383,6 +405,8 @@ nouveau_i2c_create_(struct nouveau_object *parent,
nv_subdev(i2c)->intr = nouveau_i2c_intr; nv_subdev(i2c)->intr = nouveau_i2c_intr;
i2c->find = nouveau_i2c_find; i2c->find = nouveau_i2c_find;
i2c->find_type = nouveau_i2c_find_type; i2c->find_type = nouveau_i2c_find_type;
i2c->acquire = nouveau_i2c_acquire;
i2c->release = nouveau_i2c_release;
i2c->identify = nouveau_i2c_identify; i2c->identify = nouveau_i2c_identify;
INIT_LIST_HEAD(&i2c->ports); INIT_LIST_HEAD(&i2c->ports);

View File

@ -187,8 +187,9 @@ i2c_bit_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
struct i2c_msg *msg = msgs; struct i2c_msg *msg = msgs;
int ret = 0, mcnt = num; int ret = 0, mcnt = num;
if (port->func->acquire) ret = nouveau_i2c(port)->acquire(port, nsecs_to_jiffies(T_TIMEOUT));
port->func->acquire(port); if (ret)
return ret;
while (!ret && mcnt--) { while (!ret && mcnt--) {
u8 remaining = msg->len; u8 remaining = msg->len;
@ -210,6 +211,7 @@ i2c_bit_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
} }
i2c_stop(port); i2c_stop(port);
nouveau_i2c(port)->release(port);
return (ret < 0) ? ret : num; return (ret < 0) ? ret : num;
} }
#else #else