diff options
Diffstat (limited to 'drivers/virtio/virtio_mmio.c')
| -rw-r--r-- | drivers/virtio/virtio_mmio.c | 163 | 
1 files changed, 163 insertions, 0 deletions
diff --git a/drivers/virtio/virtio_mmio.c b/drivers/virtio/virtio_mmio.c index 01d6dc250d5..453db0c403d 100644 --- a/drivers/virtio/virtio_mmio.c +++ b/drivers/virtio/virtio_mmio.c @@ -6,6 +6,50 @@   * This module allows virtio devices to be used over a virtual, memory mapped   * platform device.   * + * The guest device(s) may be instantiated in one of three equivalent ways: + * + * 1. Static platform device in board's code, eg.: + * + *	static struct platform_device v2m_virtio_device = { + *		.name = "virtio-mmio", + *		.id = -1, + *		.num_resources = 2, + *		.resource = (struct resource []) { + *			{ + *				.start = 0x1001e000, + *				.end = 0x1001e0ff, + *				.flags = IORESOURCE_MEM, + *			}, { + *				.start = 42 + 32, + *				.end = 42 + 32, + *				.flags = IORESOURCE_IRQ, + *			}, + *		} + *	}; + * + * 2. Device Tree node, eg.: + * + *		virtio_block@1e000 { + *			compatible = "virtio,mmio"; + *			reg = <0x1e000 0x100>; + *			interrupts = <42>; + *		} + * + * 3. Kernel module (or command line) parameter. Can be used more than once - + *    one device will be created for each one. Syntax: + * + *		[virtio_mmio.]device=<size>@<baseaddr>:<irq>[:<id>] + *    where: + *		<size>     := size (can use standard suffixes like K, M or G) + *		<baseaddr> := physical base address + *		<irq>      := interrupt number (as passed to request_irq()) + *		<id>       := (optional) platform device id + *    eg.: + *		virtio_mmio.device=0x100@0x100b0000:48 \ + *				virtio_mmio.device=1K@0x1001e000:74 + * + * + *   * Registers layout (all 32-bit wide):   *   * offset d. name             description @@ -42,6 +86,8 @@   * See the COPYING file in the top-level directory.   */ +#define pr_fmt(fmt) "virtio-mmio: " fmt +  #include <linux/highmem.h>  #include <linux/interrupt.h>  #include <linux/io.h> @@ -449,6 +495,122 @@ static int __devexit virtio_mmio_remove(struct platform_device *pdev) +/* Devices list parameter */ + +#if defined(CONFIG_VIRTIO_MMIO_CMDLINE_DEVICES) + +static struct device vm_cmdline_parent = { +	.init_name = "virtio-mmio-cmdline", +}; + +static int vm_cmdline_parent_registered; +static int vm_cmdline_id; + +static int vm_cmdline_set(const char *device, +		const struct kernel_param *kp) +{ +	int err; +	struct resource resources[2] = {}; +	char *str; +	long long int base; +	int processed, consumed = 0; +	struct platform_device *pdev; + +	resources[0].flags = IORESOURCE_MEM; +	resources[1].flags = IORESOURCE_IRQ; + +	resources[0].end = memparse(device, &str) - 1; + +	processed = sscanf(str, "@%lli:%u%n:%d%n", +			&base, &resources[1].start, &consumed, +			&vm_cmdline_id, &consumed); + +	if (processed < 2 || processed > 3 || str[consumed]) +		return -EINVAL; + +	resources[0].start = base; +	resources[0].end += base; +	resources[1].end = resources[1].start; + +	if (!vm_cmdline_parent_registered) { +		err = device_register(&vm_cmdline_parent); +		if (err) { +			pr_err("Failed to register parent device!\n"); +			return err; +		} +		vm_cmdline_parent_registered = 1; +	} + +	pr_info("Registering device virtio-mmio.%d at 0x%llx-0x%llx, IRQ %d.\n", +		       vm_cmdline_id, +		       (unsigned long long)resources[0].start, +		       (unsigned long long)resources[0].end, +		       (int)resources[1].start); + +	pdev = platform_device_register_resndata(&vm_cmdline_parent, +			"virtio-mmio", vm_cmdline_id++, +			resources, ARRAY_SIZE(resources), NULL, 0); +	if (IS_ERR(pdev)) +		return PTR_ERR(pdev); + +	return 0; +} + +static int vm_cmdline_get_device(struct device *dev, void *data) +{ +	char *buffer = data; +	unsigned int len = strlen(buffer); +	struct platform_device *pdev = to_platform_device(dev); + +	snprintf(buffer + len, PAGE_SIZE - len, "0x%llx@0x%llx:%llu:%d\n", +			pdev->resource[0].end - pdev->resource[0].start + 1ULL, +			(unsigned long long)pdev->resource[0].start, +			(unsigned long long)pdev->resource[1].start, +			pdev->id); +	return 0; +} + +static int vm_cmdline_get(char *buffer, const struct kernel_param *kp) +{ +	buffer[0] = '\0'; +	device_for_each_child(&vm_cmdline_parent, buffer, +			vm_cmdline_get_device); +	return strlen(buffer) + 1; +} + +static struct kernel_param_ops vm_cmdline_param_ops = { +	.set = vm_cmdline_set, +	.get = vm_cmdline_get, +}; + +device_param_cb(device, &vm_cmdline_param_ops, NULL, S_IRUSR); + +static int vm_unregister_cmdline_device(struct device *dev, +		void *data) +{ +	platform_device_unregister(to_platform_device(dev)); + +	return 0; +} + +static void vm_unregister_cmdline_devices(void) +{ +	if (vm_cmdline_parent_registered) { +		device_for_each_child(&vm_cmdline_parent, NULL, +				vm_unregister_cmdline_device); +		device_unregister(&vm_cmdline_parent); +		vm_cmdline_parent_registered = 0; +	} +} + +#else + +static void vm_unregister_cmdline_devices(void) +{ +} + +#endif +  /* Platform driver */  static struct of_device_id virtio_mmio_match[] = { @@ -475,6 +637,7 @@ static int __init virtio_mmio_init(void)  static void __exit virtio_mmio_exit(void)  {  	platform_driver_unregister(&virtio_mmio_driver); +	vm_unregister_cmdline_devices();  }  module_init(virtio_mmio_init);  |