Commit 7234d023 authored by Ben Skeggs's avatar Ben Skeggs

drm/nouveau/devinit: fixup various issues with subdev ctor/init ordering

Details of the problem, and solution, are in comments in the commit
proper.
Signed-off-by: default avatarBen Skeggs <bskeggs@redhat.com>
parent 002d0c73
...@@ -8,11 +8,23 @@ ...@@ -8,11 +8,23 @@
enum nv_subdev_type { enum nv_subdev_type {
NVDEV_SUBDEV_DEVICE, NVDEV_SUBDEV_DEVICE,
NVDEV_SUBDEV_VBIOS, NVDEV_SUBDEV_VBIOS,
/* All subdevs from DEVINIT to DEVINIT_LAST will be created before
* *any* of them are initialised. This subdev category is used
* for any subdevs that the VBIOS init table parsing may call out
* to during POST.
*/
NVDEV_SUBDEV_DEVINIT,
NVDEV_SUBDEV_GPIO, NVDEV_SUBDEV_GPIO,
NVDEV_SUBDEV_I2C, NVDEV_SUBDEV_I2C,
NVDEV_SUBDEV_CLOCK, NVDEV_SUBDEV_CLOCK,
NVDEV_SUBDEV_DEVINIT_LAST = NVDEV_SUBDEV_CLOCK,
/* This grouping of subdevs are initialised right after they've
* been created, and are allowed to assume any subdevs in the
* list above them exist and have been initialised.
*/
NVDEV_SUBDEV_MXM, NVDEV_SUBDEV_MXM,
NVDEV_SUBDEV_DEVINIT,
NVDEV_SUBDEV_MC, NVDEV_SUBDEV_MC,
NVDEV_SUBDEV_TIMER, NVDEV_SUBDEV_TIMER,
NVDEV_SUBDEV_FB, NVDEV_SUBDEV_FB,
...@@ -23,6 +35,7 @@ enum nv_subdev_type { ...@@ -23,6 +35,7 @@ enum nv_subdev_type {
NVDEV_SUBDEV_BAR, NVDEV_SUBDEV_BAR,
NVDEV_SUBDEV_VOLT, NVDEV_SUBDEV_VOLT,
NVDEV_SUBDEV_THERM, NVDEV_SUBDEV_THERM,
NVDEV_ENGINE_DMAOBJ, NVDEV_ENGINE_DMAOBJ,
NVDEV_ENGINE_FIFO, NVDEV_ENGINE_FIFO,
NVDEV_ENGINE_SW, NVDEV_ENGINE_SW,
...@@ -38,6 +51,7 @@ enum nv_subdev_type { ...@@ -38,6 +51,7 @@ enum nv_subdev_type {
NVDEV_ENGINE_UNK1C1, NVDEV_ENGINE_UNK1C1,
NVDEV_ENGINE_VENC, NVDEV_ENGINE_VENC,
NVDEV_ENGINE_DISP, NVDEV_ENGINE_DISP,
NVDEV_SUBDEV_NR, NVDEV_SUBDEV_NR,
}; };
......
...@@ -96,14 +96,13 @@ nouveau_devobj_ctor(struct nouveau_object *parent, ...@@ -96,14 +96,13 @@ nouveau_devobj_ctor(struct nouveau_object *parent,
struct nouveau_object **pobject) struct nouveau_object **pobject)
{ {
struct nouveau_client *client = nv_client(parent); struct nouveau_client *client = nv_client(parent);
struct nouveau_object *subdev = NULL;
struct nouveau_device *device; struct nouveau_device *device;
struct nouveau_devobj *devobj; struct nouveau_devobj *devobj;
struct nv_device_class *args = data; struct nv_device_class *args = data;
u64 disable, boot0, strap; u64 disable, boot0, strap;
u64 mmio_base, mmio_size; u64 mmio_base, mmio_size;
void __iomem *map; void __iomem *map;
int ret, i; int ret, i, c;
if (size < sizeof(struct nv_device_class)) if (size < sizeof(struct nv_device_class))
return -EINVAL; return -EINVAL;
...@@ -234,34 +233,43 @@ nouveau_devobj_ctor(struct nouveau_object *parent, ...@@ -234,34 +233,43 @@ nouveau_devobj_ctor(struct nouveau_object *parent,
} }
/* ensure requested subsystems are available for use */ /* ensure requested subsystems are available for use */
for (i = 0; i < NVDEV_SUBDEV_NR; i++) { for (i = 0, c = 0; i < NVDEV_SUBDEV_NR; i++) {
if (!(oclass = device->oclass[i]) || (disable & (1ULL << i))) if (!(oclass = device->oclass[i]) || (disable & (1ULL << i)))
continue; continue;
if (!device->subdev[i]) { if (!device->subdev[i]) {
ret = nouveau_object_ctor(nv_object(device), NULL, ret = nouveau_object_ctor(nv_object(device), NULL,
oclass, NULL, i, &subdev); oclass, NULL, i,
&devobj->subdev[i]);
if (ret == -ENODEV) if (ret == -ENODEV)
continue; continue;
if (ret) if (ret)
return ret; return ret;
if (nv_iclass(subdev, NV_ENGINE_CLASS)) if (nv_iclass(devobj->subdev[i], NV_ENGINE_CLASS))
nouveau_subdev_reset(subdev); nouveau_subdev_reset(devobj->subdev[i]);
} else { } else {
nouveau_object_ref(device->subdev[i], &subdev); nouveau_object_ref(device->subdev[i],
&devobj->subdev[i]);
} }
if (!nv_iclass(subdev, NV_ENGINE_CLASS)) { /* note: can't init *any* subdevs until devinit has been run
* due to not knowing exactly what the vbios init tables will
* mess with. devinit also can't be run until all of its
* dependencies have been created.
*
* this code delays init of any subdev until all of devinit's
* dependencies have been created, and then initialises each
* subdev in turn as they're created.
*/
while (i >= NVDEV_SUBDEV_DEVINIT_LAST && c <= i) {
struct nouveau_object *subdev = devobj->subdev[c++];
if (subdev && !nv_iclass(subdev, NV_ENGINE_CLASS)) {
ret = nouveau_object_inc(subdev); ret = nouveau_object_inc(subdev);
if (ret) { if (ret)
nouveau_object_ref(NULL, &subdev);
return ret; return ret;
} }
} }
nouveau_object_ref(subdev, &devobj->subdev[i]);
nouveau_object_ref(NULL, &subdev);
} }
return 0; return 0;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment