162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * CDX bus driver.
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (C) 2022-2023, Advanced Micro Devices, Inc.
662306a36Sopenharmony_ci */
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci/*
962306a36Sopenharmony_ci * Architecture Overview
1062306a36Sopenharmony_ci * =====================
1162306a36Sopenharmony_ci * CDX is a Hardware Architecture designed for AMD FPGA devices. It
1262306a36Sopenharmony_ci * consists of sophisticated mechanism for interaction between FPGA,
1362306a36Sopenharmony_ci * Firmware and the APUs (Application CPUs).
1462306a36Sopenharmony_ci *
1562306a36Sopenharmony_ci * Firmware resides on RPU (Realtime CPUs) which interacts with
1662306a36Sopenharmony_ci * the FPGA program manager and the APUs. The RPU provides memory-mapped
1762306a36Sopenharmony_ci * interface (RPU if) which is used to communicate with APUs.
1862306a36Sopenharmony_ci *
1962306a36Sopenharmony_ci * The diagram below shows an overview of the CDX architecture:
2062306a36Sopenharmony_ci *
2162306a36Sopenharmony_ci *          +--------------------------------------+
2262306a36Sopenharmony_ci *          |    Application CPUs (APU)            |
2362306a36Sopenharmony_ci *          |                                      |
2462306a36Sopenharmony_ci *          |                    CDX device drivers|
2562306a36Sopenharmony_ci *          |     Linux OS                |        |
2662306a36Sopenharmony_ci *          |                        CDX bus       |
2762306a36Sopenharmony_ci *          |                             |        |
2862306a36Sopenharmony_ci *          |                     CDX controller   |
2962306a36Sopenharmony_ci *          |                             |        |
3062306a36Sopenharmony_ci *          +-----------------------------|--------+
3162306a36Sopenharmony_ci *                                        | (discover, config,
3262306a36Sopenharmony_ci *                                        |  reset, rescan)
3362306a36Sopenharmony_ci *                                        |
3462306a36Sopenharmony_ci *          +------------------------| RPU if |----+
3562306a36Sopenharmony_ci *          |                             |        |
3662306a36Sopenharmony_ci *          |                             V        |
3762306a36Sopenharmony_ci *          |          Realtime CPUs (RPU)         |
3862306a36Sopenharmony_ci *          |                                      |
3962306a36Sopenharmony_ci *          +--------------------------------------+
4062306a36Sopenharmony_ci *                                |
4162306a36Sopenharmony_ci *          +---------------------|----------------+
4262306a36Sopenharmony_ci *          |  FPGA               |                |
4362306a36Sopenharmony_ci *          |      +-----------------------+       |
4462306a36Sopenharmony_ci *          |      |           |           |       |
4562306a36Sopenharmony_ci *          | +-------+    +-------+   +-------+   |
4662306a36Sopenharmony_ci *          | | dev 1 |    | dev 2 |   | dev 3 |   |
4762306a36Sopenharmony_ci *          | +-------+    +-------+   +-------+   |
4862306a36Sopenharmony_ci *          +--------------------------------------+
4962306a36Sopenharmony_ci *
5062306a36Sopenharmony_ci * The RPU firmware extracts the device information from the loaded FPGA
5162306a36Sopenharmony_ci * image and implements a mechanism that allows the APU drivers to
5262306a36Sopenharmony_ci * enumerate such devices (device personality and resource details) via
5362306a36Sopenharmony_ci * a dedicated communication channel. RPU mediates operations such as
5462306a36Sopenharmony_ci * discover, reset and rescan of the FPGA devices for the APU. This is
5562306a36Sopenharmony_ci * done using memory mapped interface provided by the RPU to APU.
5662306a36Sopenharmony_ci */
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_ci#include <linux/init.h>
5962306a36Sopenharmony_ci#include <linux/kernel.h>
6062306a36Sopenharmony_ci#include <linux/of_device.h>
6162306a36Sopenharmony_ci#include <linux/slab.h>
6262306a36Sopenharmony_ci#include <linux/mm.h>
6362306a36Sopenharmony_ci#include <linux/xarray.h>
6462306a36Sopenharmony_ci#include <linux/cdx/cdx_bus.h>
6562306a36Sopenharmony_ci#include <linux/iommu.h>
6662306a36Sopenharmony_ci#include <linux/dma-map-ops.h>
6762306a36Sopenharmony_ci#include "cdx.h"
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_ci/* Default DMA mask for devices on a CDX bus */
7062306a36Sopenharmony_ci#define CDX_DEFAULT_DMA_MASK	(~0ULL)
7162306a36Sopenharmony_ci#define MAX_CDX_CONTROLLERS 16
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_ci/* CDX controllers registered with the CDX bus */
7462306a36Sopenharmony_cistatic DEFINE_XARRAY_ALLOC(cdx_controllers);
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_ci/**
7762306a36Sopenharmony_ci * cdx_dev_reset - Reset a CDX device
7862306a36Sopenharmony_ci * @dev: CDX device
7962306a36Sopenharmony_ci *
8062306a36Sopenharmony_ci * Return: -errno on failure, 0 on success.
8162306a36Sopenharmony_ci */
8262306a36Sopenharmony_ciint cdx_dev_reset(struct device *dev)
8362306a36Sopenharmony_ci{
8462306a36Sopenharmony_ci	struct cdx_device *cdx_dev = to_cdx_device(dev);
8562306a36Sopenharmony_ci	struct cdx_controller *cdx = cdx_dev->cdx;
8662306a36Sopenharmony_ci	struct cdx_device_config dev_config = {0};
8762306a36Sopenharmony_ci	struct cdx_driver *cdx_drv;
8862306a36Sopenharmony_ci	int ret;
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_ci	cdx_drv = to_cdx_driver(dev->driver);
9162306a36Sopenharmony_ci	/* Notify driver that device is being reset */
9262306a36Sopenharmony_ci	if (cdx_drv && cdx_drv->reset_prepare)
9362306a36Sopenharmony_ci		cdx_drv->reset_prepare(cdx_dev);
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_ci	dev_config.type = CDX_DEV_RESET_CONF;
9662306a36Sopenharmony_ci	ret = cdx->ops->dev_configure(cdx, cdx_dev->bus_num,
9762306a36Sopenharmony_ci				      cdx_dev->dev_num, &dev_config);
9862306a36Sopenharmony_ci	if (ret)
9962306a36Sopenharmony_ci		dev_err(dev, "cdx device reset failed\n");
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_ci	/* Notify driver that device reset is complete */
10262306a36Sopenharmony_ci	if (cdx_drv && cdx_drv->reset_done)
10362306a36Sopenharmony_ci		cdx_drv->reset_done(cdx_dev);
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_ci	return ret;
10662306a36Sopenharmony_ci}
10762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(cdx_dev_reset);
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_ci/**
11062306a36Sopenharmony_ci * cdx_unregister_device - Unregister a CDX device
11162306a36Sopenharmony_ci * @dev: CDX device
11262306a36Sopenharmony_ci * @data: This is always passed as NULL, and is not used in this API,
11362306a36Sopenharmony_ci *	  but is required here as the bus_for_each_dev() API expects
11462306a36Sopenharmony_ci *	  the passed function (cdx_unregister_device) to have this
11562306a36Sopenharmony_ci *	  as an argument.
11662306a36Sopenharmony_ci *
11762306a36Sopenharmony_ci * Return: 0 on success.
11862306a36Sopenharmony_ci */
11962306a36Sopenharmony_cistatic int cdx_unregister_device(struct device *dev,
12062306a36Sopenharmony_ci				 void *data)
12162306a36Sopenharmony_ci{
12262306a36Sopenharmony_ci	struct cdx_device *cdx_dev = to_cdx_device(dev);
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_ci	kfree(cdx_dev->driver_override);
12562306a36Sopenharmony_ci	cdx_dev->driver_override = NULL;
12662306a36Sopenharmony_ci	/*
12762306a36Sopenharmony_ci	 * Do not free cdx_dev here as it would be freed in
12862306a36Sopenharmony_ci	 * cdx_device_release() called from within put_device().
12962306a36Sopenharmony_ci	 */
13062306a36Sopenharmony_ci	device_del(&cdx_dev->dev);
13162306a36Sopenharmony_ci	put_device(&cdx_dev->dev);
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_ci	return 0;
13462306a36Sopenharmony_ci}
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_cistatic void cdx_unregister_devices(struct bus_type *bus)
13762306a36Sopenharmony_ci{
13862306a36Sopenharmony_ci	/* Reset all the devices attached to cdx bus */
13962306a36Sopenharmony_ci	bus_for_each_dev(bus, NULL, NULL, cdx_unregister_device);
14062306a36Sopenharmony_ci}
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_ci/**
14362306a36Sopenharmony_ci * cdx_match_one_device - Tell if a CDX device structure has a matching
14462306a36Sopenharmony_ci *			  CDX device id structure
14562306a36Sopenharmony_ci * @id: single CDX device id structure to match
14662306a36Sopenharmony_ci * @dev: the CDX device structure to match against
14762306a36Sopenharmony_ci *
14862306a36Sopenharmony_ci * Return: matching cdx_device_id structure or NULL if there is no match.
14962306a36Sopenharmony_ci */
15062306a36Sopenharmony_cistatic inline const struct cdx_device_id *
15162306a36Sopenharmony_cicdx_match_one_device(const struct cdx_device_id *id,
15262306a36Sopenharmony_ci		     const struct cdx_device *dev)
15362306a36Sopenharmony_ci{
15462306a36Sopenharmony_ci	/* Use vendor ID and device ID for matching */
15562306a36Sopenharmony_ci	if ((id->vendor == CDX_ANY_ID || id->vendor == dev->vendor) &&
15662306a36Sopenharmony_ci	    (id->device == CDX_ANY_ID || id->device == dev->device))
15762306a36Sopenharmony_ci		return id;
15862306a36Sopenharmony_ci	return NULL;
15962306a36Sopenharmony_ci}
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_ci/**
16262306a36Sopenharmony_ci * cdx_match_id - See if a CDX device matches a given cdx_id table
16362306a36Sopenharmony_ci * @ids: array of CDX device ID structures to search in
16462306a36Sopenharmony_ci * @dev: the CDX device structure to match against.
16562306a36Sopenharmony_ci *
16662306a36Sopenharmony_ci * Used by a driver to check whether a CDX device is in its list of
16762306a36Sopenharmony_ci * supported devices. Returns the matching cdx_device_id structure or
16862306a36Sopenharmony_ci * NULL if there is no match.
16962306a36Sopenharmony_ci *
17062306a36Sopenharmony_ci * Return: matching cdx_device_id structure or NULL if there is no match.
17162306a36Sopenharmony_ci */
17262306a36Sopenharmony_cistatic inline const struct cdx_device_id *
17362306a36Sopenharmony_cicdx_match_id(const struct cdx_device_id *ids, struct cdx_device *dev)
17462306a36Sopenharmony_ci{
17562306a36Sopenharmony_ci	if (ids) {
17662306a36Sopenharmony_ci		while (ids->vendor || ids->device) {
17762306a36Sopenharmony_ci			if (cdx_match_one_device(ids, dev))
17862306a36Sopenharmony_ci				return ids;
17962306a36Sopenharmony_ci			ids++;
18062306a36Sopenharmony_ci		}
18162306a36Sopenharmony_ci	}
18262306a36Sopenharmony_ci	return NULL;
18362306a36Sopenharmony_ci}
18462306a36Sopenharmony_ci
18562306a36Sopenharmony_ci/**
18662306a36Sopenharmony_ci * cdx_bus_match - device to driver matching callback
18762306a36Sopenharmony_ci * @dev: the cdx device to match against
18862306a36Sopenharmony_ci * @drv: the device driver to search for matching cdx device
18962306a36Sopenharmony_ci * structures
19062306a36Sopenharmony_ci *
19162306a36Sopenharmony_ci * Return: true on success, false otherwise.
19262306a36Sopenharmony_ci */
19362306a36Sopenharmony_cistatic int cdx_bus_match(struct device *dev, struct device_driver *drv)
19462306a36Sopenharmony_ci{
19562306a36Sopenharmony_ci	struct cdx_device *cdx_dev = to_cdx_device(dev);
19662306a36Sopenharmony_ci	struct cdx_driver *cdx_drv = to_cdx_driver(drv);
19762306a36Sopenharmony_ci	const struct cdx_device_id *found_id = NULL;
19862306a36Sopenharmony_ci	const struct cdx_device_id *ids;
19962306a36Sopenharmony_ci
20062306a36Sopenharmony_ci	ids = cdx_drv->match_id_table;
20162306a36Sopenharmony_ci
20262306a36Sopenharmony_ci	/* When driver_override is set, only bind to the matching driver */
20362306a36Sopenharmony_ci	if (cdx_dev->driver_override && strcmp(cdx_dev->driver_override, drv->name))
20462306a36Sopenharmony_ci		return false;
20562306a36Sopenharmony_ci
20662306a36Sopenharmony_ci	found_id = cdx_match_id(ids, cdx_dev);
20762306a36Sopenharmony_ci	if (!found_id)
20862306a36Sopenharmony_ci		return false;
20962306a36Sopenharmony_ci
21062306a36Sopenharmony_ci	do {
21162306a36Sopenharmony_ci		/*
21262306a36Sopenharmony_ci		 * In case override_only was set, enforce driver_override
21362306a36Sopenharmony_ci		 * matching.
21462306a36Sopenharmony_ci		 */
21562306a36Sopenharmony_ci		if (!found_id->override_only)
21662306a36Sopenharmony_ci			return true;
21762306a36Sopenharmony_ci		if (cdx_dev->driver_override)
21862306a36Sopenharmony_ci			return true;
21962306a36Sopenharmony_ci
22062306a36Sopenharmony_ci		ids = found_id + 1;
22162306a36Sopenharmony_ci		found_id = cdx_match_id(ids, cdx_dev);
22262306a36Sopenharmony_ci	} while (found_id);
22362306a36Sopenharmony_ci
22462306a36Sopenharmony_ci	return false;
22562306a36Sopenharmony_ci}
22662306a36Sopenharmony_ci
22762306a36Sopenharmony_cistatic int cdx_probe(struct device *dev)
22862306a36Sopenharmony_ci{
22962306a36Sopenharmony_ci	struct cdx_driver *cdx_drv = to_cdx_driver(dev->driver);
23062306a36Sopenharmony_ci	struct cdx_device *cdx_dev = to_cdx_device(dev);
23162306a36Sopenharmony_ci	int error;
23262306a36Sopenharmony_ci
23362306a36Sopenharmony_ci	error = cdx_drv->probe(cdx_dev);
23462306a36Sopenharmony_ci	if (error) {
23562306a36Sopenharmony_ci		dev_err_probe(dev, error, "%s failed\n", __func__);
23662306a36Sopenharmony_ci		return error;
23762306a36Sopenharmony_ci	}
23862306a36Sopenharmony_ci
23962306a36Sopenharmony_ci	return 0;
24062306a36Sopenharmony_ci}
24162306a36Sopenharmony_ci
24262306a36Sopenharmony_cistatic void cdx_remove(struct device *dev)
24362306a36Sopenharmony_ci{
24462306a36Sopenharmony_ci	struct cdx_driver *cdx_drv = to_cdx_driver(dev->driver);
24562306a36Sopenharmony_ci	struct cdx_device *cdx_dev = to_cdx_device(dev);
24662306a36Sopenharmony_ci
24762306a36Sopenharmony_ci	if (cdx_drv && cdx_drv->remove)
24862306a36Sopenharmony_ci		cdx_drv->remove(cdx_dev);
24962306a36Sopenharmony_ci}
25062306a36Sopenharmony_ci
25162306a36Sopenharmony_cistatic void cdx_shutdown(struct device *dev)
25262306a36Sopenharmony_ci{
25362306a36Sopenharmony_ci	struct cdx_driver *cdx_drv = to_cdx_driver(dev->driver);
25462306a36Sopenharmony_ci	struct cdx_device *cdx_dev = to_cdx_device(dev);
25562306a36Sopenharmony_ci
25662306a36Sopenharmony_ci	if (cdx_drv && cdx_drv->shutdown)
25762306a36Sopenharmony_ci		cdx_drv->shutdown(cdx_dev);
25862306a36Sopenharmony_ci}
25962306a36Sopenharmony_ci
26062306a36Sopenharmony_cistatic int cdx_dma_configure(struct device *dev)
26162306a36Sopenharmony_ci{
26262306a36Sopenharmony_ci	struct cdx_driver *cdx_drv = to_cdx_driver(dev->driver);
26362306a36Sopenharmony_ci	struct cdx_device *cdx_dev = to_cdx_device(dev);
26462306a36Sopenharmony_ci	u32 input_id = cdx_dev->req_id;
26562306a36Sopenharmony_ci	int ret;
26662306a36Sopenharmony_ci
26762306a36Sopenharmony_ci	ret = of_dma_configure_id(dev, dev->parent->of_node, 0, &input_id);
26862306a36Sopenharmony_ci	if (ret && ret != -EPROBE_DEFER) {
26962306a36Sopenharmony_ci		dev_err(dev, "of_dma_configure_id() failed\n");
27062306a36Sopenharmony_ci		return ret;
27162306a36Sopenharmony_ci	}
27262306a36Sopenharmony_ci
27362306a36Sopenharmony_ci	if (!ret && !cdx_drv->driver_managed_dma) {
27462306a36Sopenharmony_ci		ret = iommu_device_use_default_domain(dev);
27562306a36Sopenharmony_ci		if (ret)
27662306a36Sopenharmony_ci			arch_teardown_dma_ops(dev);
27762306a36Sopenharmony_ci	}
27862306a36Sopenharmony_ci
27962306a36Sopenharmony_ci	return 0;
28062306a36Sopenharmony_ci}
28162306a36Sopenharmony_ci
28262306a36Sopenharmony_cistatic void cdx_dma_cleanup(struct device *dev)
28362306a36Sopenharmony_ci{
28462306a36Sopenharmony_ci	struct cdx_driver *cdx_drv = to_cdx_driver(dev->driver);
28562306a36Sopenharmony_ci
28662306a36Sopenharmony_ci	if (!cdx_drv->driver_managed_dma)
28762306a36Sopenharmony_ci		iommu_device_unuse_default_domain(dev);
28862306a36Sopenharmony_ci}
28962306a36Sopenharmony_ci
29062306a36Sopenharmony_ci/* show configuration fields */
29162306a36Sopenharmony_ci#define cdx_config_attr(field, format_string)	\
29262306a36Sopenharmony_cistatic ssize_t	\
29362306a36Sopenharmony_cifield##_show(struct device *dev, struct device_attribute *attr, char *buf)	\
29462306a36Sopenharmony_ci{	\
29562306a36Sopenharmony_ci	struct cdx_device *cdx_dev = to_cdx_device(dev);	\
29662306a36Sopenharmony_ci	return sysfs_emit(buf, format_string, cdx_dev->field);	\
29762306a36Sopenharmony_ci}	\
29862306a36Sopenharmony_cistatic DEVICE_ATTR_RO(field)
29962306a36Sopenharmony_ci
30062306a36Sopenharmony_cicdx_config_attr(vendor, "0x%04x\n");
30162306a36Sopenharmony_cicdx_config_attr(device, "0x%04x\n");
30262306a36Sopenharmony_ci
30362306a36Sopenharmony_cistatic ssize_t remove_store(struct device *dev,
30462306a36Sopenharmony_ci			    struct device_attribute *attr,
30562306a36Sopenharmony_ci			    const char *buf, size_t count)
30662306a36Sopenharmony_ci{
30762306a36Sopenharmony_ci	bool val;
30862306a36Sopenharmony_ci
30962306a36Sopenharmony_ci	if (kstrtobool(buf, &val) < 0)
31062306a36Sopenharmony_ci		return -EINVAL;
31162306a36Sopenharmony_ci
31262306a36Sopenharmony_ci	if (!val)
31362306a36Sopenharmony_ci		return -EINVAL;
31462306a36Sopenharmony_ci
31562306a36Sopenharmony_ci	if (device_remove_file_self(dev, attr)) {
31662306a36Sopenharmony_ci		int ret;
31762306a36Sopenharmony_ci
31862306a36Sopenharmony_ci		ret = cdx_unregister_device(dev, NULL);
31962306a36Sopenharmony_ci		if (ret)
32062306a36Sopenharmony_ci			return ret;
32162306a36Sopenharmony_ci	}
32262306a36Sopenharmony_ci
32362306a36Sopenharmony_ci	return count;
32462306a36Sopenharmony_ci}
32562306a36Sopenharmony_cistatic DEVICE_ATTR_WO(remove);
32662306a36Sopenharmony_ci
32762306a36Sopenharmony_cistatic ssize_t reset_store(struct device *dev, struct device_attribute *attr,
32862306a36Sopenharmony_ci			   const char *buf, size_t count)
32962306a36Sopenharmony_ci{
33062306a36Sopenharmony_ci	bool val;
33162306a36Sopenharmony_ci	int ret;
33262306a36Sopenharmony_ci
33362306a36Sopenharmony_ci	if (kstrtobool(buf, &val) < 0)
33462306a36Sopenharmony_ci		return -EINVAL;
33562306a36Sopenharmony_ci
33662306a36Sopenharmony_ci	if (!val)
33762306a36Sopenharmony_ci		return -EINVAL;
33862306a36Sopenharmony_ci
33962306a36Sopenharmony_ci	ret = cdx_dev_reset(dev);
34062306a36Sopenharmony_ci	if (ret)
34162306a36Sopenharmony_ci		return ret;
34262306a36Sopenharmony_ci
34362306a36Sopenharmony_ci	return count;
34462306a36Sopenharmony_ci}
34562306a36Sopenharmony_cistatic DEVICE_ATTR_WO(reset);
34662306a36Sopenharmony_ci
34762306a36Sopenharmony_cistatic ssize_t driver_override_store(struct device *dev,
34862306a36Sopenharmony_ci				     struct device_attribute *attr,
34962306a36Sopenharmony_ci				     const char *buf, size_t count)
35062306a36Sopenharmony_ci{
35162306a36Sopenharmony_ci	struct cdx_device *cdx_dev = to_cdx_device(dev);
35262306a36Sopenharmony_ci	int ret;
35362306a36Sopenharmony_ci
35462306a36Sopenharmony_ci	if (WARN_ON(dev->bus != &cdx_bus_type))
35562306a36Sopenharmony_ci		return -EINVAL;
35662306a36Sopenharmony_ci
35762306a36Sopenharmony_ci	ret = driver_set_override(dev, &cdx_dev->driver_override, buf, count);
35862306a36Sopenharmony_ci	if (ret)
35962306a36Sopenharmony_ci		return ret;
36062306a36Sopenharmony_ci
36162306a36Sopenharmony_ci	return count;
36262306a36Sopenharmony_ci}
36362306a36Sopenharmony_ci
36462306a36Sopenharmony_cistatic ssize_t driver_override_show(struct device *dev,
36562306a36Sopenharmony_ci				    struct device_attribute *attr, char *buf)
36662306a36Sopenharmony_ci{
36762306a36Sopenharmony_ci	struct cdx_device *cdx_dev = to_cdx_device(dev);
36862306a36Sopenharmony_ci
36962306a36Sopenharmony_ci	return sysfs_emit(buf, "%s\n", cdx_dev->driver_override);
37062306a36Sopenharmony_ci}
37162306a36Sopenharmony_cistatic DEVICE_ATTR_RW(driver_override);
37262306a36Sopenharmony_ci
37362306a36Sopenharmony_cistatic struct attribute *cdx_dev_attrs[] = {
37462306a36Sopenharmony_ci	&dev_attr_remove.attr,
37562306a36Sopenharmony_ci	&dev_attr_reset.attr,
37662306a36Sopenharmony_ci	&dev_attr_vendor.attr,
37762306a36Sopenharmony_ci	&dev_attr_device.attr,
37862306a36Sopenharmony_ci	&dev_attr_driver_override.attr,
37962306a36Sopenharmony_ci	NULL,
38062306a36Sopenharmony_ci};
38162306a36Sopenharmony_ciATTRIBUTE_GROUPS(cdx_dev);
38262306a36Sopenharmony_ci
38362306a36Sopenharmony_cistatic ssize_t rescan_store(const struct bus_type *bus,
38462306a36Sopenharmony_ci			    const char *buf, size_t count)
38562306a36Sopenharmony_ci{
38662306a36Sopenharmony_ci	struct cdx_controller *cdx;
38762306a36Sopenharmony_ci	unsigned long index;
38862306a36Sopenharmony_ci	bool val;
38962306a36Sopenharmony_ci
39062306a36Sopenharmony_ci	if (kstrtobool(buf, &val) < 0)
39162306a36Sopenharmony_ci		return -EINVAL;
39262306a36Sopenharmony_ci
39362306a36Sopenharmony_ci	if (!val)
39462306a36Sopenharmony_ci		return -EINVAL;
39562306a36Sopenharmony_ci
39662306a36Sopenharmony_ci	/* Unregister all the devices on the bus */
39762306a36Sopenharmony_ci	cdx_unregister_devices(&cdx_bus_type);
39862306a36Sopenharmony_ci
39962306a36Sopenharmony_ci	/* Rescan all the devices */
40062306a36Sopenharmony_ci	xa_for_each(&cdx_controllers, index, cdx) {
40162306a36Sopenharmony_ci		int ret;
40262306a36Sopenharmony_ci
40362306a36Sopenharmony_ci		ret = cdx->ops->scan(cdx);
40462306a36Sopenharmony_ci		if (ret)
40562306a36Sopenharmony_ci			dev_err(cdx->dev, "cdx bus scanning failed\n");
40662306a36Sopenharmony_ci	}
40762306a36Sopenharmony_ci
40862306a36Sopenharmony_ci	return count;
40962306a36Sopenharmony_ci}
41062306a36Sopenharmony_cistatic BUS_ATTR_WO(rescan);
41162306a36Sopenharmony_ci
41262306a36Sopenharmony_cistatic struct attribute *cdx_bus_attrs[] = {
41362306a36Sopenharmony_ci	&bus_attr_rescan.attr,
41462306a36Sopenharmony_ci	NULL,
41562306a36Sopenharmony_ci};
41662306a36Sopenharmony_ciATTRIBUTE_GROUPS(cdx_bus);
41762306a36Sopenharmony_ci
41862306a36Sopenharmony_cistruct bus_type cdx_bus_type = {
41962306a36Sopenharmony_ci	.name		= "cdx",
42062306a36Sopenharmony_ci	.match		= cdx_bus_match,
42162306a36Sopenharmony_ci	.probe		= cdx_probe,
42262306a36Sopenharmony_ci	.remove		= cdx_remove,
42362306a36Sopenharmony_ci	.shutdown	= cdx_shutdown,
42462306a36Sopenharmony_ci	.dma_configure	= cdx_dma_configure,
42562306a36Sopenharmony_ci	.dma_cleanup	= cdx_dma_cleanup,
42662306a36Sopenharmony_ci	.bus_groups	= cdx_bus_groups,
42762306a36Sopenharmony_ci	.dev_groups	= cdx_dev_groups,
42862306a36Sopenharmony_ci};
42962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(cdx_bus_type);
43062306a36Sopenharmony_ci
43162306a36Sopenharmony_ciint __cdx_driver_register(struct cdx_driver *cdx_driver,
43262306a36Sopenharmony_ci			  struct module *owner)
43362306a36Sopenharmony_ci{
43462306a36Sopenharmony_ci	int error;
43562306a36Sopenharmony_ci
43662306a36Sopenharmony_ci	cdx_driver->driver.owner = owner;
43762306a36Sopenharmony_ci	cdx_driver->driver.bus = &cdx_bus_type;
43862306a36Sopenharmony_ci
43962306a36Sopenharmony_ci	error = driver_register(&cdx_driver->driver);
44062306a36Sopenharmony_ci	if (error) {
44162306a36Sopenharmony_ci		pr_err("driver_register() failed for %s: %d\n",
44262306a36Sopenharmony_ci		       cdx_driver->driver.name, error);
44362306a36Sopenharmony_ci		return error;
44462306a36Sopenharmony_ci	}
44562306a36Sopenharmony_ci
44662306a36Sopenharmony_ci	return 0;
44762306a36Sopenharmony_ci}
44862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(__cdx_driver_register);
44962306a36Sopenharmony_ci
45062306a36Sopenharmony_civoid cdx_driver_unregister(struct cdx_driver *cdx_driver)
45162306a36Sopenharmony_ci{
45262306a36Sopenharmony_ci	driver_unregister(&cdx_driver->driver);
45362306a36Sopenharmony_ci}
45462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(cdx_driver_unregister);
45562306a36Sopenharmony_ci
45662306a36Sopenharmony_cistatic void cdx_device_release(struct device *dev)
45762306a36Sopenharmony_ci{
45862306a36Sopenharmony_ci	struct cdx_device *cdx_dev = to_cdx_device(dev);
45962306a36Sopenharmony_ci
46062306a36Sopenharmony_ci	kfree(cdx_dev);
46162306a36Sopenharmony_ci}
46262306a36Sopenharmony_ci
46362306a36Sopenharmony_ciint cdx_device_add(struct cdx_dev_params *dev_params)
46462306a36Sopenharmony_ci{
46562306a36Sopenharmony_ci	struct cdx_controller *cdx = dev_params->cdx;
46662306a36Sopenharmony_ci	struct device *parent = cdx->dev;
46762306a36Sopenharmony_ci	struct cdx_device *cdx_dev;
46862306a36Sopenharmony_ci	int ret;
46962306a36Sopenharmony_ci
47062306a36Sopenharmony_ci	cdx_dev = kzalloc(sizeof(*cdx_dev), GFP_KERNEL);
47162306a36Sopenharmony_ci	if (!cdx_dev)
47262306a36Sopenharmony_ci		return -ENOMEM;
47362306a36Sopenharmony_ci
47462306a36Sopenharmony_ci	/* Populate resource */
47562306a36Sopenharmony_ci	memcpy(cdx_dev->res, dev_params->res, sizeof(struct resource) *
47662306a36Sopenharmony_ci		dev_params->res_count);
47762306a36Sopenharmony_ci	cdx_dev->res_count = dev_params->res_count;
47862306a36Sopenharmony_ci
47962306a36Sopenharmony_ci	/* Populate CDX dev params */
48062306a36Sopenharmony_ci	cdx_dev->req_id = dev_params->req_id;
48162306a36Sopenharmony_ci	cdx_dev->vendor = dev_params->vendor;
48262306a36Sopenharmony_ci	cdx_dev->device = dev_params->device;
48362306a36Sopenharmony_ci	cdx_dev->bus_num = dev_params->bus_num;
48462306a36Sopenharmony_ci	cdx_dev->dev_num = dev_params->dev_num;
48562306a36Sopenharmony_ci	cdx_dev->cdx = dev_params->cdx;
48662306a36Sopenharmony_ci	cdx_dev->dma_mask = CDX_DEFAULT_DMA_MASK;
48762306a36Sopenharmony_ci
48862306a36Sopenharmony_ci	/* Initialize generic device */
48962306a36Sopenharmony_ci	device_initialize(&cdx_dev->dev);
49062306a36Sopenharmony_ci	cdx_dev->dev.parent = parent;
49162306a36Sopenharmony_ci	cdx_dev->dev.bus = &cdx_bus_type;
49262306a36Sopenharmony_ci	cdx_dev->dev.dma_mask = &cdx_dev->dma_mask;
49362306a36Sopenharmony_ci	cdx_dev->dev.release = cdx_device_release;
49462306a36Sopenharmony_ci
49562306a36Sopenharmony_ci	/* Set Name */
49662306a36Sopenharmony_ci	dev_set_name(&cdx_dev->dev, "cdx-%02x:%02x",
49762306a36Sopenharmony_ci		     ((cdx->id << CDX_CONTROLLER_ID_SHIFT) | (cdx_dev->bus_num & CDX_BUS_NUM_MASK)),
49862306a36Sopenharmony_ci		     cdx_dev->dev_num);
49962306a36Sopenharmony_ci
50062306a36Sopenharmony_ci	ret = device_add(&cdx_dev->dev);
50162306a36Sopenharmony_ci	if (ret) {
50262306a36Sopenharmony_ci		dev_err(&cdx_dev->dev,
50362306a36Sopenharmony_ci			"cdx device add failed: %d", ret);
50462306a36Sopenharmony_ci		goto fail;
50562306a36Sopenharmony_ci	}
50662306a36Sopenharmony_ci
50762306a36Sopenharmony_ci	return 0;
50862306a36Sopenharmony_cifail:
50962306a36Sopenharmony_ci	/*
51062306a36Sopenharmony_ci	 * Do not free cdx_dev here as it would be freed in
51162306a36Sopenharmony_ci	 * cdx_device_release() called from put_device().
51262306a36Sopenharmony_ci	 */
51362306a36Sopenharmony_ci	put_device(&cdx_dev->dev);
51462306a36Sopenharmony_ci
51562306a36Sopenharmony_ci	return ret;
51662306a36Sopenharmony_ci}
51762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(cdx_device_add);
51862306a36Sopenharmony_ci
51962306a36Sopenharmony_ciint cdx_register_controller(struct cdx_controller *cdx)
52062306a36Sopenharmony_ci{
52162306a36Sopenharmony_ci	int ret;
52262306a36Sopenharmony_ci
52362306a36Sopenharmony_ci	ret = xa_alloc(&cdx_controllers, &cdx->id, cdx,
52462306a36Sopenharmony_ci		       XA_LIMIT(0, MAX_CDX_CONTROLLERS - 1), GFP_KERNEL);
52562306a36Sopenharmony_ci	if (ret) {
52662306a36Sopenharmony_ci		dev_err(cdx->dev,
52762306a36Sopenharmony_ci			"No free index available. Maximum controllers already registered\n");
52862306a36Sopenharmony_ci		cdx->id = (u8)MAX_CDX_CONTROLLERS;
52962306a36Sopenharmony_ci		return ret;
53062306a36Sopenharmony_ci	}
53162306a36Sopenharmony_ci
53262306a36Sopenharmony_ci	/* Scan all the devices */
53362306a36Sopenharmony_ci	cdx->ops->scan(cdx);
53462306a36Sopenharmony_ci
53562306a36Sopenharmony_ci	return 0;
53662306a36Sopenharmony_ci}
53762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(cdx_register_controller);
53862306a36Sopenharmony_ci
53962306a36Sopenharmony_civoid cdx_unregister_controller(struct cdx_controller *cdx)
54062306a36Sopenharmony_ci{
54162306a36Sopenharmony_ci	if (cdx->id >= MAX_CDX_CONTROLLERS)
54262306a36Sopenharmony_ci		return;
54362306a36Sopenharmony_ci
54462306a36Sopenharmony_ci	device_for_each_child(cdx->dev, NULL, cdx_unregister_device);
54562306a36Sopenharmony_ci	xa_erase(&cdx_controllers, cdx->id);
54662306a36Sopenharmony_ci}
54762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(cdx_unregister_controller);
54862306a36Sopenharmony_ci
54962306a36Sopenharmony_cistatic int __init cdx_bus_init(void)
55062306a36Sopenharmony_ci{
55162306a36Sopenharmony_ci	return bus_register(&cdx_bus_type);
55262306a36Sopenharmony_ci}
55362306a36Sopenharmony_cipostcore_initcall(cdx_bus_init);
554