162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci#include <linux/virtio.h>
362306a36Sopenharmony_ci#include <linux/spinlock.h>
462306a36Sopenharmony_ci#include <linux/virtio_config.h>
562306a36Sopenharmony_ci#include <linux/virtio_anchor.h>
662306a36Sopenharmony_ci#include <linux/module.h>
762306a36Sopenharmony_ci#include <linux/idr.h>
862306a36Sopenharmony_ci#include <linux/of.h>
962306a36Sopenharmony_ci#include <uapi/linux/virtio_ids.h>
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_ci/* Unique numbering for virtio devices. */
1262306a36Sopenharmony_cistatic DEFINE_IDA(virtio_index_ida);
1362306a36Sopenharmony_ci
1462306a36Sopenharmony_cistatic ssize_t device_show(struct device *_d,
1562306a36Sopenharmony_ci			   struct device_attribute *attr, char *buf)
1662306a36Sopenharmony_ci{
1762306a36Sopenharmony_ci	struct virtio_device *dev = dev_to_virtio(_d);
1862306a36Sopenharmony_ci	return sysfs_emit(buf, "0x%04x\n", dev->id.device);
1962306a36Sopenharmony_ci}
2062306a36Sopenharmony_cistatic DEVICE_ATTR_RO(device);
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_cistatic ssize_t vendor_show(struct device *_d,
2362306a36Sopenharmony_ci			   struct device_attribute *attr, char *buf)
2462306a36Sopenharmony_ci{
2562306a36Sopenharmony_ci	struct virtio_device *dev = dev_to_virtio(_d);
2662306a36Sopenharmony_ci	return sysfs_emit(buf, "0x%04x\n", dev->id.vendor);
2762306a36Sopenharmony_ci}
2862306a36Sopenharmony_cistatic DEVICE_ATTR_RO(vendor);
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_cistatic ssize_t status_show(struct device *_d,
3162306a36Sopenharmony_ci			   struct device_attribute *attr, char *buf)
3262306a36Sopenharmony_ci{
3362306a36Sopenharmony_ci	struct virtio_device *dev = dev_to_virtio(_d);
3462306a36Sopenharmony_ci	return sysfs_emit(buf, "0x%08x\n", dev->config->get_status(dev));
3562306a36Sopenharmony_ci}
3662306a36Sopenharmony_cistatic DEVICE_ATTR_RO(status);
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_cistatic ssize_t modalias_show(struct device *_d,
3962306a36Sopenharmony_ci			     struct device_attribute *attr, char *buf)
4062306a36Sopenharmony_ci{
4162306a36Sopenharmony_ci	struct virtio_device *dev = dev_to_virtio(_d);
4262306a36Sopenharmony_ci	return sysfs_emit(buf, "virtio:d%08Xv%08X\n",
4362306a36Sopenharmony_ci		       dev->id.device, dev->id.vendor);
4462306a36Sopenharmony_ci}
4562306a36Sopenharmony_cistatic DEVICE_ATTR_RO(modalias);
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_cistatic ssize_t features_show(struct device *_d,
4862306a36Sopenharmony_ci			     struct device_attribute *attr, char *buf)
4962306a36Sopenharmony_ci{
5062306a36Sopenharmony_ci	struct virtio_device *dev = dev_to_virtio(_d);
5162306a36Sopenharmony_ci	unsigned int i;
5262306a36Sopenharmony_ci	ssize_t len = 0;
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_ci	/* We actually represent this as a bitstring, as it could be
5562306a36Sopenharmony_ci	 * arbitrary length in future. */
5662306a36Sopenharmony_ci	for (i = 0; i < sizeof(dev->features)*8; i++)
5762306a36Sopenharmony_ci		len += sysfs_emit_at(buf, len, "%c",
5862306a36Sopenharmony_ci			       __virtio_test_bit(dev, i) ? '1' : '0');
5962306a36Sopenharmony_ci	len += sysfs_emit_at(buf, len, "\n");
6062306a36Sopenharmony_ci	return len;
6162306a36Sopenharmony_ci}
6262306a36Sopenharmony_cistatic DEVICE_ATTR_RO(features);
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_cistatic struct attribute *virtio_dev_attrs[] = {
6562306a36Sopenharmony_ci	&dev_attr_device.attr,
6662306a36Sopenharmony_ci	&dev_attr_vendor.attr,
6762306a36Sopenharmony_ci	&dev_attr_status.attr,
6862306a36Sopenharmony_ci	&dev_attr_modalias.attr,
6962306a36Sopenharmony_ci	&dev_attr_features.attr,
7062306a36Sopenharmony_ci	NULL,
7162306a36Sopenharmony_ci};
7262306a36Sopenharmony_ciATTRIBUTE_GROUPS(virtio_dev);
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_cistatic inline int virtio_id_match(const struct virtio_device *dev,
7562306a36Sopenharmony_ci				  const struct virtio_device_id *id)
7662306a36Sopenharmony_ci{
7762306a36Sopenharmony_ci	if (id->device != dev->id.device && id->device != VIRTIO_DEV_ANY_ID)
7862306a36Sopenharmony_ci		return 0;
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_ci	return id->vendor == VIRTIO_DEV_ANY_ID || id->vendor == dev->id.vendor;
8162306a36Sopenharmony_ci}
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_ci/* This looks through all the IDs a driver claims to support.  If any of them
8462306a36Sopenharmony_ci * match, we return 1 and the kernel will call virtio_dev_probe(). */
8562306a36Sopenharmony_cistatic int virtio_dev_match(struct device *_dv, struct device_driver *_dr)
8662306a36Sopenharmony_ci{
8762306a36Sopenharmony_ci	unsigned int i;
8862306a36Sopenharmony_ci	struct virtio_device *dev = dev_to_virtio(_dv);
8962306a36Sopenharmony_ci	const struct virtio_device_id *ids;
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_ci	ids = drv_to_virtio(_dr)->id_table;
9262306a36Sopenharmony_ci	for (i = 0; ids[i].device; i++)
9362306a36Sopenharmony_ci		if (virtio_id_match(dev, &ids[i]))
9462306a36Sopenharmony_ci			return 1;
9562306a36Sopenharmony_ci	return 0;
9662306a36Sopenharmony_ci}
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_cistatic int virtio_uevent(const struct device *_dv, struct kobj_uevent_env *env)
9962306a36Sopenharmony_ci{
10062306a36Sopenharmony_ci	const struct virtio_device *dev = dev_to_virtio(_dv);
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_ci	return add_uevent_var(env, "MODALIAS=virtio:d%08Xv%08X",
10362306a36Sopenharmony_ci			      dev->id.device, dev->id.vendor);
10462306a36Sopenharmony_ci}
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_civoid virtio_check_driver_offered_feature(const struct virtio_device *vdev,
10762306a36Sopenharmony_ci					 unsigned int fbit)
10862306a36Sopenharmony_ci{
10962306a36Sopenharmony_ci	unsigned int i;
11062306a36Sopenharmony_ci	struct virtio_driver *drv = drv_to_virtio(vdev->dev.driver);
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_ci	for (i = 0; i < drv->feature_table_size; i++)
11362306a36Sopenharmony_ci		if (drv->feature_table[i] == fbit)
11462306a36Sopenharmony_ci			return;
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_ci	if (drv->feature_table_legacy) {
11762306a36Sopenharmony_ci		for (i = 0; i < drv->feature_table_size_legacy; i++)
11862306a36Sopenharmony_ci			if (drv->feature_table_legacy[i] == fbit)
11962306a36Sopenharmony_ci				return;
12062306a36Sopenharmony_ci	}
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_ci	BUG();
12362306a36Sopenharmony_ci}
12462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(virtio_check_driver_offered_feature);
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_cistatic void __virtio_config_changed(struct virtio_device *dev)
12762306a36Sopenharmony_ci{
12862306a36Sopenharmony_ci	struct virtio_driver *drv = drv_to_virtio(dev->dev.driver);
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_ci	if (!dev->config_enabled)
13162306a36Sopenharmony_ci		dev->config_change_pending = true;
13262306a36Sopenharmony_ci	else if (drv && drv->config_changed)
13362306a36Sopenharmony_ci		drv->config_changed(dev);
13462306a36Sopenharmony_ci}
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_civoid virtio_config_changed(struct virtio_device *dev)
13762306a36Sopenharmony_ci{
13862306a36Sopenharmony_ci	unsigned long flags;
13962306a36Sopenharmony_ci
14062306a36Sopenharmony_ci	spin_lock_irqsave(&dev->config_lock, flags);
14162306a36Sopenharmony_ci	__virtio_config_changed(dev);
14262306a36Sopenharmony_ci	spin_unlock_irqrestore(&dev->config_lock, flags);
14362306a36Sopenharmony_ci}
14462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(virtio_config_changed);
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_cistatic void virtio_config_disable(struct virtio_device *dev)
14762306a36Sopenharmony_ci{
14862306a36Sopenharmony_ci	spin_lock_irq(&dev->config_lock);
14962306a36Sopenharmony_ci	dev->config_enabled = false;
15062306a36Sopenharmony_ci	spin_unlock_irq(&dev->config_lock);
15162306a36Sopenharmony_ci}
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_cistatic void virtio_config_enable(struct virtio_device *dev)
15462306a36Sopenharmony_ci{
15562306a36Sopenharmony_ci	spin_lock_irq(&dev->config_lock);
15662306a36Sopenharmony_ci	dev->config_enabled = true;
15762306a36Sopenharmony_ci	if (dev->config_change_pending)
15862306a36Sopenharmony_ci		__virtio_config_changed(dev);
15962306a36Sopenharmony_ci	dev->config_change_pending = false;
16062306a36Sopenharmony_ci	spin_unlock_irq(&dev->config_lock);
16162306a36Sopenharmony_ci}
16262306a36Sopenharmony_ci
16362306a36Sopenharmony_civoid virtio_add_status(struct virtio_device *dev, unsigned int status)
16462306a36Sopenharmony_ci{
16562306a36Sopenharmony_ci	might_sleep();
16662306a36Sopenharmony_ci	dev->config->set_status(dev, dev->config->get_status(dev) | status);
16762306a36Sopenharmony_ci}
16862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(virtio_add_status);
16962306a36Sopenharmony_ci
17062306a36Sopenharmony_ci/* Do some validation, then set FEATURES_OK */
17162306a36Sopenharmony_cistatic int virtio_features_ok(struct virtio_device *dev)
17262306a36Sopenharmony_ci{
17362306a36Sopenharmony_ci	unsigned int status;
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_ci	might_sleep();
17662306a36Sopenharmony_ci
17762306a36Sopenharmony_ci	if (virtio_check_mem_acc_cb(dev)) {
17862306a36Sopenharmony_ci		if (!virtio_has_feature(dev, VIRTIO_F_VERSION_1)) {
17962306a36Sopenharmony_ci			dev_warn(&dev->dev,
18062306a36Sopenharmony_ci				 "device must provide VIRTIO_F_VERSION_1\n");
18162306a36Sopenharmony_ci			return -ENODEV;
18262306a36Sopenharmony_ci		}
18362306a36Sopenharmony_ci
18462306a36Sopenharmony_ci		if (!virtio_has_feature(dev, VIRTIO_F_ACCESS_PLATFORM)) {
18562306a36Sopenharmony_ci			dev_warn(&dev->dev,
18662306a36Sopenharmony_ci				 "device must provide VIRTIO_F_ACCESS_PLATFORM\n");
18762306a36Sopenharmony_ci			return -ENODEV;
18862306a36Sopenharmony_ci		}
18962306a36Sopenharmony_ci	}
19062306a36Sopenharmony_ci
19162306a36Sopenharmony_ci	if (!virtio_has_feature(dev, VIRTIO_F_VERSION_1))
19262306a36Sopenharmony_ci		return 0;
19362306a36Sopenharmony_ci
19462306a36Sopenharmony_ci	virtio_add_status(dev, VIRTIO_CONFIG_S_FEATURES_OK);
19562306a36Sopenharmony_ci	status = dev->config->get_status(dev);
19662306a36Sopenharmony_ci	if (!(status & VIRTIO_CONFIG_S_FEATURES_OK)) {
19762306a36Sopenharmony_ci		dev_err(&dev->dev, "virtio: device refuses features: %x\n",
19862306a36Sopenharmony_ci			status);
19962306a36Sopenharmony_ci		return -ENODEV;
20062306a36Sopenharmony_ci	}
20162306a36Sopenharmony_ci	return 0;
20262306a36Sopenharmony_ci}
20362306a36Sopenharmony_ci
20462306a36Sopenharmony_ci/**
20562306a36Sopenharmony_ci * virtio_reset_device - quiesce device for removal
20662306a36Sopenharmony_ci * @dev: the device to reset
20762306a36Sopenharmony_ci *
20862306a36Sopenharmony_ci * Prevents device from sending interrupts and accessing memory.
20962306a36Sopenharmony_ci *
21062306a36Sopenharmony_ci * Generally used for cleanup during driver / device removal.
21162306a36Sopenharmony_ci *
21262306a36Sopenharmony_ci * Once this has been invoked, caller must ensure that
21362306a36Sopenharmony_ci * virtqueue_notify / virtqueue_kick are not in progress.
21462306a36Sopenharmony_ci *
21562306a36Sopenharmony_ci * Note: this guarantees that vq callbacks are not in progress, however caller
21662306a36Sopenharmony_ci * is responsible for preventing access from other contexts, such as a system
21762306a36Sopenharmony_ci * call/workqueue/bh.  Invoking virtio_break_device then flushing any such
21862306a36Sopenharmony_ci * contexts is one way to handle that.
21962306a36Sopenharmony_ci * */
22062306a36Sopenharmony_civoid virtio_reset_device(struct virtio_device *dev)
22162306a36Sopenharmony_ci{
22262306a36Sopenharmony_ci#ifdef CONFIG_VIRTIO_HARDEN_NOTIFICATION
22362306a36Sopenharmony_ci	/*
22462306a36Sopenharmony_ci	 * The below virtio_synchronize_cbs() guarantees that any
22562306a36Sopenharmony_ci	 * interrupt for this line arriving after
22662306a36Sopenharmony_ci	 * virtio_synchronize_vqs() has completed is guaranteed to see
22762306a36Sopenharmony_ci	 * vq->broken as true.
22862306a36Sopenharmony_ci	 */
22962306a36Sopenharmony_ci	virtio_break_device(dev);
23062306a36Sopenharmony_ci	virtio_synchronize_cbs(dev);
23162306a36Sopenharmony_ci#endif
23262306a36Sopenharmony_ci
23362306a36Sopenharmony_ci	dev->config->reset(dev);
23462306a36Sopenharmony_ci}
23562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(virtio_reset_device);
23662306a36Sopenharmony_ci
23762306a36Sopenharmony_cistatic int virtio_dev_probe(struct device *_d)
23862306a36Sopenharmony_ci{
23962306a36Sopenharmony_ci	int err, i;
24062306a36Sopenharmony_ci	struct virtio_device *dev = dev_to_virtio(_d);
24162306a36Sopenharmony_ci	struct virtio_driver *drv = drv_to_virtio(dev->dev.driver);
24262306a36Sopenharmony_ci	u64 device_features;
24362306a36Sopenharmony_ci	u64 driver_features;
24462306a36Sopenharmony_ci	u64 driver_features_legacy;
24562306a36Sopenharmony_ci
24662306a36Sopenharmony_ci	/* We have a driver! */
24762306a36Sopenharmony_ci	virtio_add_status(dev, VIRTIO_CONFIG_S_DRIVER);
24862306a36Sopenharmony_ci
24962306a36Sopenharmony_ci	/* Figure out what features the device supports. */
25062306a36Sopenharmony_ci	device_features = dev->config->get_features(dev);
25162306a36Sopenharmony_ci
25262306a36Sopenharmony_ci	/* Figure out what features the driver supports. */
25362306a36Sopenharmony_ci	driver_features = 0;
25462306a36Sopenharmony_ci	for (i = 0; i < drv->feature_table_size; i++) {
25562306a36Sopenharmony_ci		unsigned int f = drv->feature_table[i];
25662306a36Sopenharmony_ci		BUG_ON(f >= 64);
25762306a36Sopenharmony_ci		driver_features |= (1ULL << f);
25862306a36Sopenharmony_ci	}
25962306a36Sopenharmony_ci
26062306a36Sopenharmony_ci	/* Some drivers have a separate feature table for virtio v1.0 */
26162306a36Sopenharmony_ci	if (drv->feature_table_legacy) {
26262306a36Sopenharmony_ci		driver_features_legacy = 0;
26362306a36Sopenharmony_ci		for (i = 0; i < drv->feature_table_size_legacy; i++) {
26462306a36Sopenharmony_ci			unsigned int f = drv->feature_table_legacy[i];
26562306a36Sopenharmony_ci			BUG_ON(f >= 64);
26662306a36Sopenharmony_ci			driver_features_legacy |= (1ULL << f);
26762306a36Sopenharmony_ci		}
26862306a36Sopenharmony_ci	} else {
26962306a36Sopenharmony_ci		driver_features_legacy = driver_features;
27062306a36Sopenharmony_ci	}
27162306a36Sopenharmony_ci
27262306a36Sopenharmony_ci	if (device_features & (1ULL << VIRTIO_F_VERSION_1))
27362306a36Sopenharmony_ci		dev->features = driver_features & device_features;
27462306a36Sopenharmony_ci	else
27562306a36Sopenharmony_ci		dev->features = driver_features_legacy & device_features;
27662306a36Sopenharmony_ci
27762306a36Sopenharmony_ci	/* Transport features always preserved to pass to finalize_features. */
27862306a36Sopenharmony_ci	for (i = VIRTIO_TRANSPORT_F_START; i < VIRTIO_TRANSPORT_F_END; i++)
27962306a36Sopenharmony_ci		if (device_features & (1ULL << i))
28062306a36Sopenharmony_ci			__virtio_set_bit(dev, i);
28162306a36Sopenharmony_ci
28262306a36Sopenharmony_ci	err = dev->config->finalize_features(dev);
28362306a36Sopenharmony_ci	if (err)
28462306a36Sopenharmony_ci		goto err;
28562306a36Sopenharmony_ci
28662306a36Sopenharmony_ci	if (drv->validate) {
28762306a36Sopenharmony_ci		u64 features = dev->features;
28862306a36Sopenharmony_ci
28962306a36Sopenharmony_ci		err = drv->validate(dev);
29062306a36Sopenharmony_ci		if (err)
29162306a36Sopenharmony_ci			goto err;
29262306a36Sopenharmony_ci
29362306a36Sopenharmony_ci		/* Did validation change any features? Then write them again. */
29462306a36Sopenharmony_ci		if (features != dev->features) {
29562306a36Sopenharmony_ci			err = dev->config->finalize_features(dev);
29662306a36Sopenharmony_ci			if (err)
29762306a36Sopenharmony_ci				goto err;
29862306a36Sopenharmony_ci		}
29962306a36Sopenharmony_ci	}
30062306a36Sopenharmony_ci
30162306a36Sopenharmony_ci	err = virtio_features_ok(dev);
30262306a36Sopenharmony_ci	if (err)
30362306a36Sopenharmony_ci		goto err;
30462306a36Sopenharmony_ci
30562306a36Sopenharmony_ci	err = drv->probe(dev);
30662306a36Sopenharmony_ci	if (err)
30762306a36Sopenharmony_ci		goto err;
30862306a36Sopenharmony_ci
30962306a36Sopenharmony_ci	/* If probe didn't do it, mark device DRIVER_OK ourselves. */
31062306a36Sopenharmony_ci	if (!(dev->config->get_status(dev) & VIRTIO_CONFIG_S_DRIVER_OK))
31162306a36Sopenharmony_ci		virtio_device_ready(dev);
31262306a36Sopenharmony_ci
31362306a36Sopenharmony_ci	if (drv->scan)
31462306a36Sopenharmony_ci		drv->scan(dev);
31562306a36Sopenharmony_ci
31662306a36Sopenharmony_ci	virtio_config_enable(dev);
31762306a36Sopenharmony_ci
31862306a36Sopenharmony_ci	return 0;
31962306a36Sopenharmony_cierr:
32062306a36Sopenharmony_ci	virtio_add_status(dev, VIRTIO_CONFIG_S_FAILED);
32162306a36Sopenharmony_ci	return err;
32262306a36Sopenharmony_ci
32362306a36Sopenharmony_ci}
32462306a36Sopenharmony_ci
32562306a36Sopenharmony_cistatic void virtio_dev_remove(struct device *_d)
32662306a36Sopenharmony_ci{
32762306a36Sopenharmony_ci	struct virtio_device *dev = dev_to_virtio(_d);
32862306a36Sopenharmony_ci	struct virtio_driver *drv = drv_to_virtio(dev->dev.driver);
32962306a36Sopenharmony_ci
33062306a36Sopenharmony_ci	virtio_config_disable(dev);
33162306a36Sopenharmony_ci
33262306a36Sopenharmony_ci	drv->remove(dev);
33362306a36Sopenharmony_ci
33462306a36Sopenharmony_ci	/* Driver should have reset device. */
33562306a36Sopenharmony_ci	WARN_ON_ONCE(dev->config->get_status(dev));
33662306a36Sopenharmony_ci
33762306a36Sopenharmony_ci	/* Acknowledge the device's existence again. */
33862306a36Sopenharmony_ci	virtio_add_status(dev, VIRTIO_CONFIG_S_ACKNOWLEDGE);
33962306a36Sopenharmony_ci
34062306a36Sopenharmony_ci	of_node_put(dev->dev.of_node);
34162306a36Sopenharmony_ci}
34262306a36Sopenharmony_ci
34362306a36Sopenharmony_cistatic struct bus_type virtio_bus = {
34462306a36Sopenharmony_ci	.name  = "virtio",
34562306a36Sopenharmony_ci	.match = virtio_dev_match,
34662306a36Sopenharmony_ci	.dev_groups = virtio_dev_groups,
34762306a36Sopenharmony_ci	.uevent = virtio_uevent,
34862306a36Sopenharmony_ci	.probe = virtio_dev_probe,
34962306a36Sopenharmony_ci	.remove = virtio_dev_remove,
35062306a36Sopenharmony_ci};
35162306a36Sopenharmony_ci
35262306a36Sopenharmony_ciint register_virtio_driver(struct virtio_driver *driver)
35362306a36Sopenharmony_ci{
35462306a36Sopenharmony_ci	/* Catch this early. */
35562306a36Sopenharmony_ci	BUG_ON(driver->feature_table_size && !driver->feature_table);
35662306a36Sopenharmony_ci	driver->driver.bus = &virtio_bus;
35762306a36Sopenharmony_ci	return driver_register(&driver->driver);
35862306a36Sopenharmony_ci}
35962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(register_virtio_driver);
36062306a36Sopenharmony_ci
36162306a36Sopenharmony_civoid unregister_virtio_driver(struct virtio_driver *driver)
36262306a36Sopenharmony_ci{
36362306a36Sopenharmony_ci	driver_unregister(&driver->driver);
36462306a36Sopenharmony_ci}
36562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(unregister_virtio_driver);
36662306a36Sopenharmony_ci
36762306a36Sopenharmony_cistatic int virtio_device_of_init(struct virtio_device *dev)
36862306a36Sopenharmony_ci{
36962306a36Sopenharmony_ci	struct device_node *np, *pnode = dev_of_node(dev->dev.parent);
37062306a36Sopenharmony_ci	char compat[] = "virtio,deviceXXXXXXXX";
37162306a36Sopenharmony_ci	int ret, count;
37262306a36Sopenharmony_ci
37362306a36Sopenharmony_ci	if (!pnode)
37462306a36Sopenharmony_ci		return 0;
37562306a36Sopenharmony_ci
37662306a36Sopenharmony_ci	count = of_get_available_child_count(pnode);
37762306a36Sopenharmony_ci	if (!count)
37862306a36Sopenharmony_ci		return 0;
37962306a36Sopenharmony_ci
38062306a36Sopenharmony_ci	/* There can be only 1 child node */
38162306a36Sopenharmony_ci	if (WARN_ON(count > 1))
38262306a36Sopenharmony_ci		return -EINVAL;
38362306a36Sopenharmony_ci
38462306a36Sopenharmony_ci	np = of_get_next_available_child(pnode, NULL);
38562306a36Sopenharmony_ci	if (WARN_ON(!np))
38662306a36Sopenharmony_ci		return -ENODEV;
38762306a36Sopenharmony_ci
38862306a36Sopenharmony_ci	ret = snprintf(compat, sizeof(compat), "virtio,device%x", dev->id.device);
38962306a36Sopenharmony_ci	BUG_ON(ret >= sizeof(compat));
39062306a36Sopenharmony_ci
39162306a36Sopenharmony_ci	/*
39262306a36Sopenharmony_ci	 * On powerpc/pseries virtio devices are PCI devices so PCI
39362306a36Sopenharmony_ci	 * vendor/device ids play the role of the "compatible" property.
39462306a36Sopenharmony_ci	 * Simply don't init of_node in this case.
39562306a36Sopenharmony_ci	 */
39662306a36Sopenharmony_ci	if (!of_device_is_compatible(np, compat)) {
39762306a36Sopenharmony_ci		ret = 0;
39862306a36Sopenharmony_ci		goto out;
39962306a36Sopenharmony_ci	}
40062306a36Sopenharmony_ci
40162306a36Sopenharmony_ci	dev->dev.of_node = np;
40262306a36Sopenharmony_ci	return 0;
40362306a36Sopenharmony_ci
40462306a36Sopenharmony_ciout:
40562306a36Sopenharmony_ci	of_node_put(np);
40662306a36Sopenharmony_ci	return ret;
40762306a36Sopenharmony_ci}
40862306a36Sopenharmony_ci
40962306a36Sopenharmony_ci/**
41062306a36Sopenharmony_ci * register_virtio_device - register virtio device
41162306a36Sopenharmony_ci * @dev        : virtio device to be registered
41262306a36Sopenharmony_ci *
41362306a36Sopenharmony_ci * On error, the caller must call put_device on &@dev->dev (and not kfree),
41462306a36Sopenharmony_ci * as another code path may have obtained a reference to @dev.
41562306a36Sopenharmony_ci *
41662306a36Sopenharmony_ci * Returns: 0 on suceess, -error on failure
41762306a36Sopenharmony_ci */
41862306a36Sopenharmony_ciint register_virtio_device(struct virtio_device *dev)
41962306a36Sopenharmony_ci{
42062306a36Sopenharmony_ci	int err;
42162306a36Sopenharmony_ci
42262306a36Sopenharmony_ci	dev->dev.bus = &virtio_bus;
42362306a36Sopenharmony_ci	device_initialize(&dev->dev);
42462306a36Sopenharmony_ci
42562306a36Sopenharmony_ci	/* Assign a unique device index and hence name. */
42662306a36Sopenharmony_ci	err = ida_alloc(&virtio_index_ida, GFP_KERNEL);
42762306a36Sopenharmony_ci	if (err < 0)
42862306a36Sopenharmony_ci		goto out;
42962306a36Sopenharmony_ci
43062306a36Sopenharmony_ci	dev->index = err;
43162306a36Sopenharmony_ci	err = dev_set_name(&dev->dev, "virtio%u", dev->index);
43262306a36Sopenharmony_ci	if (err)
43362306a36Sopenharmony_ci		goto out_ida_remove;
43462306a36Sopenharmony_ci
43562306a36Sopenharmony_ci	err = virtio_device_of_init(dev);
43662306a36Sopenharmony_ci	if (err)
43762306a36Sopenharmony_ci		goto out_ida_remove;
43862306a36Sopenharmony_ci
43962306a36Sopenharmony_ci	spin_lock_init(&dev->config_lock);
44062306a36Sopenharmony_ci	dev->config_enabled = false;
44162306a36Sopenharmony_ci	dev->config_change_pending = false;
44262306a36Sopenharmony_ci
44362306a36Sopenharmony_ci	INIT_LIST_HEAD(&dev->vqs);
44462306a36Sopenharmony_ci	spin_lock_init(&dev->vqs_list_lock);
44562306a36Sopenharmony_ci
44662306a36Sopenharmony_ci	/* We always start by resetting the device, in case a previous
44762306a36Sopenharmony_ci	 * driver messed it up.  This also tests that code path a little. */
44862306a36Sopenharmony_ci	virtio_reset_device(dev);
44962306a36Sopenharmony_ci
45062306a36Sopenharmony_ci	/* Acknowledge that we've seen the device. */
45162306a36Sopenharmony_ci	virtio_add_status(dev, VIRTIO_CONFIG_S_ACKNOWLEDGE);
45262306a36Sopenharmony_ci
45362306a36Sopenharmony_ci	/*
45462306a36Sopenharmony_ci	 * device_add() causes the bus infrastructure to look for a matching
45562306a36Sopenharmony_ci	 * driver.
45662306a36Sopenharmony_ci	 */
45762306a36Sopenharmony_ci	err = device_add(&dev->dev);
45862306a36Sopenharmony_ci	if (err)
45962306a36Sopenharmony_ci		goto out_of_node_put;
46062306a36Sopenharmony_ci
46162306a36Sopenharmony_ci	return 0;
46262306a36Sopenharmony_ci
46362306a36Sopenharmony_ciout_of_node_put:
46462306a36Sopenharmony_ci	of_node_put(dev->dev.of_node);
46562306a36Sopenharmony_ciout_ida_remove:
46662306a36Sopenharmony_ci	ida_free(&virtio_index_ida, dev->index);
46762306a36Sopenharmony_ciout:
46862306a36Sopenharmony_ci	virtio_add_status(dev, VIRTIO_CONFIG_S_FAILED);
46962306a36Sopenharmony_ci	return err;
47062306a36Sopenharmony_ci}
47162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(register_virtio_device);
47262306a36Sopenharmony_ci
47362306a36Sopenharmony_cibool is_virtio_device(struct device *dev)
47462306a36Sopenharmony_ci{
47562306a36Sopenharmony_ci	return dev->bus == &virtio_bus;
47662306a36Sopenharmony_ci}
47762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(is_virtio_device);
47862306a36Sopenharmony_ci
47962306a36Sopenharmony_civoid unregister_virtio_device(struct virtio_device *dev)
48062306a36Sopenharmony_ci{
48162306a36Sopenharmony_ci	int index = dev->index; /* save for after device release */
48262306a36Sopenharmony_ci
48362306a36Sopenharmony_ci	device_unregister(&dev->dev);
48462306a36Sopenharmony_ci	ida_free(&virtio_index_ida, index);
48562306a36Sopenharmony_ci}
48662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(unregister_virtio_device);
48762306a36Sopenharmony_ci
48862306a36Sopenharmony_ci#ifdef CONFIG_PM_SLEEP
48962306a36Sopenharmony_ciint virtio_device_freeze(struct virtio_device *dev)
49062306a36Sopenharmony_ci{
49162306a36Sopenharmony_ci	struct virtio_driver *drv = drv_to_virtio(dev->dev.driver);
49262306a36Sopenharmony_ci
49362306a36Sopenharmony_ci	virtio_config_disable(dev);
49462306a36Sopenharmony_ci
49562306a36Sopenharmony_ci	dev->failed = dev->config->get_status(dev) & VIRTIO_CONFIG_S_FAILED;
49662306a36Sopenharmony_ci
49762306a36Sopenharmony_ci	if (drv && drv->freeze)
49862306a36Sopenharmony_ci		return drv->freeze(dev);
49962306a36Sopenharmony_ci
50062306a36Sopenharmony_ci	return 0;
50162306a36Sopenharmony_ci}
50262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(virtio_device_freeze);
50362306a36Sopenharmony_ci
50462306a36Sopenharmony_ciint virtio_device_restore(struct virtio_device *dev)
50562306a36Sopenharmony_ci{
50662306a36Sopenharmony_ci	struct virtio_driver *drv = drv_to_virtio(dev->dev.driver);
50762306a36Sopenharmony_ci	int ret;
50862306a36Sopenharmony_ci
50962306a36Sopenharmony_ci	/* We always start by resetting the device, in case a previous
51062306a36Sopenharmony_ci	 * driver messed it up. */
51162306a36Sopenharmony_ci	virtio_reset_device(dev);
51262306a36Sopenharmony_ci
51362306a36Sopenharmony_ci	/* Acknowledge that we've seen the device. */
51462306a36Sopenharmony_ci	virtio_add_status(dev, VIRTIO_CONFIG_S_ACKNOWLEDGE);
51562306a36Sopenharmony_ci
51662306a36Sopenharmony_ci	/* Maybe driver failed before freeze.
51762306a36Sopenharmony_ci	 * Restore the failed status, for debugging. */
51862306a36Sopenharmony_ci	if (dev->failed)
51962306a36Sopenharmony_ci		virtio_add_status(dev, VIRTIO_CONFIG_S_FAILED);
52062306a36Sopenharmony_ci
52162306a36Sopenharmony_ci	if (!drv)
52262306a36Sopenharmony_ci		return 0;
52362306a36Sopenharmony_ci
52462306a36Sopenharmony_ci	/* We have a driver! */
52562306a36Sopenharmony_ci	virtio_add_status(dev, VIRTIO_CONFIG_S_DRIVER);
52662306a36Sopenharmony_ci
52762306a36Sopenharmony_ci	ret = dev->config->finalize_features(dev);
52862306a36Sopenharmony_ci	if (ret)
52962306a36Sopenharmony_ci		goto err;
53062306a36Sopenharmony_ci
53162306a36Sopenharmony_ci	ret = virtio_features_ok(dev);
53262306a36Sopenharmony_ci	if (ret)
53362306a36Sopenharmony_ci		goto err;
53462306a36Sopenharmony_ci
53562306a36Sopenharmony_ci	if (drv->restore) {
53662306a36Sopenharmony_ci		ret = drv->restore(dev);
53762306a36Sopenharmony_ci		if (ret)
53862306a36Sopenharmony_ci			goto err;
53962306a36Sopenharmony_ci	}
54062306a36Sopenharmony_ci
54162306a36Sopenharmony_ci	/* If restore didn't do it, mark device DRIVER_OK ourselves. */
54262306a36Sopenharmony_ci	if (!(dev->config->get_status(dev) & VIRTIO_CONFIG_S_DRIVER_OK))
54362306a36Sopenharmony_ci		virtio_device_ready(dev);
54462306a36Sopenharmony_ci
54562306a36Sopenharmony_ci	virtio_config_enable(dev);
54662306a36Sopenharmony_ci
54762306a36Sopenharmony_ci	return 0;
54862306a36Sopenharmony_ci
54962306a36Sopenharmony_cierr:
55062306a36Sopenharmony_ci	virtio_add_status(dev, VIRTIO_CONFIG_S_FAILED);
55162306a36Sopenharmony_ci	return ret;
55262306a36Sopenharmony_ci}
55362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(virtio_device_restore);
55462306a36Sopenharmony_ci#endif
55562306a36Sopenharmony_ci
55662306a36Sopenharmony_cistatic int virtio_init(void)
55762306a36Sopenharmony_ci{
55862306a36Sopenharmony_ci	if (bus_register(&virtio_bus) != 0)
55962306a36Sopenharmony_ci		panic("virtio bus registration failed");
56062306a36Sopenharmony_ci	return 0;
56162306a36Sopenharmony_ci}
56262306a36Sopenharmony_ci
56362306a36Sopenharmony_cistatic void __exit virtio_exit(void)
56462306a36Sopenharmony_ci{
56562306a36Sopenharmony_ci	bus_unregister(&virtio_bus);
56662306a36Sopenharmony_ci	ida_destroy(&virtio_index_ida);
56762306a36Sopenharmony_ci}
56862306a36Sopenharmony_cicore_initcall(virtio_init);
56962306a36Sopenharmony_cimodule_exit(virtio_exit);
57062306a36Sopenharmony_ci
57162306a36Sopenharmony_ciMODULE_LICENSE("GPL");
572