static int ps3vram_probe()

in ps3vram.c [609:788]


static int ps3vram_probe(struct ps3_system_bus_device *dev)
{
	struct ps3vram_priv *priv;
	int error, status;
	struct gendisk *gendisk;
	u64 ddr_size, ddr_lpar, ctrl_lpar, info_lpar, reports_lpar,
	    reports_size, xdr_lpar;
	char *rest;

	priv = kzalloc(sizeof(*priv), GFP_KERNEL);
	if (!priv) {
		error = -ENOMEM;
		goto fail;
	}

	spin_lock_init(&priv->lock);
	bio_list_init(&priv->list);
	ps3_system_bus_set_drvdata(dev, priv);

	/* Allocate XDR buffer (1MiB aligned) */
	priv->xdr_buf = (void *)__get_free_pages(GFP_KERNEL,
		get_order(XDR_BUF_SIZE));
	if (priv->xdr_buf == NULL) {
		dev_err(&dev->core, "Could not allocate XDR buffer\n");
		error = -ENOMEM;
		goto fail_free_priv;
	}

	/* Put FIFO at begginning of XDR buffer */
	priv->fifo_base = (u32 *) (priv->xdr_buf + FIFO_OFFSET);
	priv->fifo_ptr = priv->fifo_base;

	/* XXX: Need to open GPU, in case ps3fb or snd_ps3 aren't loaded */
	if (ps3_open_hv_device(dev)) {
		dev_err(&dev->core, "ps3_open_hv_device failed\n");
		error = -EAGAIN;
		goto out_free_xdr_buf;
	}

	/* Request memory */
	status = -1;
	ddr_size = ALIGN(memparse(size, &rest), 1024*1024);
	if (!ddr_size) {
		dev_err(&dev->core, "Specified size is too small\n");
		error = -EINVAL;
		goto out_close_gpu;
	}

	while (ddr_size > 0) {
		status = lv1_gpu_memory_allocate(ddr_size, 0, 0, 0, 0,
						 &priv->memory_handle,
						 &ddr_lpar);
		if (!status)
			break;
		ddr_size -= 1024*1024;
	}
	if (status) {
		dev_err(&dev->core, "lv1_gpu_memory_allocate failed %d\n",
			status);
		error = -ENOMEM;
		goto out_close_gpu;
	}

	/* Request context */
	status = lv1_gpu_context_allocate(priv->memory_handle, 0,
					  &priv->context_handle, &ctrl_lpar,
					  &info_lpar, &reports_lpar,
					  &reports_size);
	if (status) {
		dev_err(&dev->core, "lv1_gpu_context_allocate failed %d\n",
			status);
		error = -ENOMEM;
		goto out_free_memory;
	}

	/* Map XDR buffer to RSX */
	xdr_lpar = ps3_mm_phys_to_lpar(__pa(priv->xdr_buf));
	status = lv1_gpu_context_iomap(priv->context_handle, XDR_IOIF,
				       xdr_lpar, XDR_BUF_SIZE,
				       CBE_IOPTE_PP_W | CBE_IOPTE_PP_R |
				       CBE_IOPTE_M);
	if (status) {
		dev_err(&dev->core, "lv1_gpu_context_iomap failed %d\n",
			status);
		error = -ENOMEM;
		goto out_free_context;
	}

	priv->ctrl = ioremap(ctrl_lpar, 64 * 1024);
	if (!priv->ctrl) {
		dev_err(&dev->core, "ioremap CTRL failed\n");
		error = -ENOMEM;
		goto out_unmap_context;
	}

	priv->reports = ioremap(reports_lpar, reports_size);
	if (!priv->reports) {
		dev_err(&dev->core, "ioremap REPORTS failed\n");
		error = -ENOMEM;
		goto out_unmap_ctrl;
	}

	mutex_lock(&ps3_gpu_mutex);
	ps3vram_init_ring(dev);
	mutex_unlock(&ps3_gpu_mutex);

	priv->size = ddr_size;

	ps3vram_bind(dev);

	mutex_lock(&ps3_gpu_mutex);
	error = ps3vram_wait_ring(dev, 100);
	mutex_unlock(&ps3_gpu_mutex);
	if (error < 0) {
		dev_err(&dev->core, "Failed to initialize channels\n");
		error = -ETIMEDOUT;
		goto out_unmap_reports;
	}

	error = ps3vram_cache_init(dev);
	if (error < 0) {
		goto out_unmap_reports;
	}

	ps3vram_proc_init(dev);

	gendisk = blk_alloc_disk(NUMA_NO_NODE);
	if (!gendisk) {
		dev_err(&dev->core, "blk_alloc_disk failed\n");
		error = -ENOMEM;
		goto out_cache_cleanup;
	}

	priv->gendisk = gendisk;
	gendisk->major = ps3vram_major;
	gendisk->minors = 1;
	gendisk->flags |= GENHD_FL_NO_PART;
	gendisk->fops = &ps3vram_fops;
	gendisk->private_data = dev;
	strlcpy(gendisk->disk_name, DEVICE_NAME, sizeof(gendisk->disk_name));
	set_capacity(gendisk, priv->size >> 9);
	blk_queue_max_segments(gendisk->queue, BLK_MAX_SEGMENTS);
	blk_queue_max_segment_size(gendisk->queue, BLK_MAX_SEGMENT_SIZE);
	blk_queue_max_hw_sectors(gendisk->queue, BLK_SAFE_MAX_SECTORS);

	dev_info(&dev->core, "%s: Using %llu MiB of GPU memory\n",
		 gendisk->disk_name, get_capacity(gendisk) >> 11);

	error = device_add_disk(&dev->core, gendisk, NULL);
	if (error)
		goto out_cleanup_disk;

	return 0;

out_cleanup_disk:
	blk_cleanup_disk(gendisk);
out_cache_cleanup:
	remove_proc_entry(DEVICE_NAME, NULL);
	ps3vram_cache_cleanup(dev);
out_unmap_reports:
	iounmap(priv->reports);
out_unmap_ctrl:
	iounmap(priv->ctrl);
out_unmap_context:
	lv1_gpu_context_iomap(priv->context_handle, XDR_IOIF, xdr_lpar,
			      XDR_BUF_SIZE, CBE_IOPTE_M);
out_free_context:
	lv1_gpu_context_free(priv->context_handle);
out_free_memory:
	lv1_gpu_memory_free(priv->memory_handle);
out_close_gpu:
	ps3_close_hv_device(dev);
out_free_xdr_buf:
	free_pages((unsigned long) priv->xdr_buf, get_order(XDR_BUF_SIZE));
fail_free_priv:
	kfree(priv);
	ps3_system_bus_set_drvdata(dev, NULL);
fail:
	return error;
}