162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * RapidIO driver support
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright 2005 MontaVista Software, Inc.
662306a36Sopenharmony_ci * Matt Porter <mporter@kernel.crashing.org>
762306a36Sopenharmony_ci */
862306a36Sopenharmony_ci
962306a36Sopenharmony_ci#include <linux/init.h>
1062306a36Sopenharmony_ci#include <linux/module.h>
1162306a36Sopenharmony_ci#include <linux/rio.h>
1262306a36Sopenharmony_ci#include <linux/rio_ids.h>
1362306a36Sopenharmony_ci#include <linux/rio_drv.h>
1462306a36Sopenharmony_ci
1562306a36Sopenharmony_ci#include "rio.h"
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_ci/**
1862306a36Sopenharmony_ci *  rio_match_device - Tell if a RIO device has a matching RIO device id structure
1962306a36Sopenharmony_ci *  @id: the RIO device id structure to match against
2062306a36Sopenharmony_ci *  @rdev: the RIO device structure to match against
2162306a36Sopenharmony_ci *
2262306a36Sopenharmony_ci *  Used from driver probe and bus matching to check whether a RIO device
2362306a36Sopenharmony_ci *  matches a device id structure provided by a RIO driver. Returns the
2462306a36Sopenharmony_ci *  matching &struct rio_device_id or %NULL if there is no match.
2562306a36Sopenharmony_ci */
2662306a36Sopenharmony_cistatic const struct rio_device_id *rio_match_device(const struct rio_device_id
2762306a36Sopenharmony_ci						    *id,
2862306a36Sopenharmony_ci						    const struct rio_dev *rdev)
2962306a36Sopenharmony_ci{
3062306a36Sopenharmony_ci	while (id->vid || id->asm_vid) {
3162306a36Sopenharmony_ci		if (((id->vid == RIO_ANY_ID) || (id->vid == rdev->vid)) &&
3262306a36Sopenharmony_ci		    ((id->did == RIO_ANY_ID) || (id->did == rdev->did)) &&
3362306a36Sopenharmony_ci		    ((id->asm_vid == RIO_ANY_ID)
3462306a36Sopenharmony_ci		     || (id->asm_vid == rdev->asm_vid))
3562306a36Sopenharmony_ci		    && ((id->asm_did == RIO_ANY_ID)
3662306a36Sopenharmony_ci			|| (id->asm_did == rdev->asm_did)))
3762306a36Sopenharmony_ci			return id;
3862306a36Sopenharmony_ci		id++;
3962306a36Sopenharmony_ci	}
4062306a36Sopenharmony_ci	return NULL;
4162306a36Sopenharmony_ci}
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_ci/**
4462306a36Sopenharmony_ci * rio_dev_get - Increments the reference count of the RIO device structure
4562306a36Sopenharmony_ci *
4662306a36Sopenharmony_ci * @rdev: RIO device being referenced
4762306a36Sopenharmony_ci *
4862306a36Sopenharmony_ci * Each live reference to a device should be refcounted.
4962306a36Sopenharmony_ci *
5062306a36Sopenharmony_ci * Drivers for RIO devices should normally record such references in
5162306a36Sopenharmony_ci * their probe() methods, when they bind to a device, and release
5262306a36Sopenharmony_ci * them by calling rio_dev_put(), in their disconnect() methods.
5362306a36Sopenharmony_ci */
5462306a36Sopenharmony_cistruct rio_dev *rio_dev_get(struct rio_dev *rdev)
5562306a36Sopenharmony_ci{
5662306a36Sopenharmony_ci	if (rdev)
5762306a36Sopenharmony_ci		get_device(&rdev->dev);
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_ci	return rdev;
6062306a36Sopenharmony_ci}
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ci/**
6362306a36Sopenharmony_ci * rio_dev_put - Release a use of the RIO device structure
6462306a36Sopenharmony_ci *
6562306a36Sopenharmony_ci * @rdev: RIO device being disconnected
6662306a36Sopenharmony_ci *
6762306a36Sopenharmony_ci * Must be called when a user of a device is finished with it.
6862306a36Sopenharmony_ci * When the last user of the device calls this function, the
6962306a36Sopenharmony_ci * memory of the device is freed.
7062306a36Sopenharmony_ci */
7162306a36Sopenharmony_civoid rio_dev_put(struct rio_dev *rdev)
7262306a36Sopenharmony_ci{
7362306a36Sopenharmony_ci	if (rdev)
7462306a36Sopenharmony_ci		put_device(&rdev->dev);
7562306a36Sopenharmony_ci}
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_ci/**
7862306a36Sopenharmony_ci *  rio_device_probe - Tell if a RIO device structure has a matching RIO device id structure
7962306a36Sopenharmony_ci *  @dev: the RIO device structure to match against
8062306a36Sopenharmony_ci *
8162306a36Sopenharmony_ci * return 0 and set rio_dev->driver when drv claims rio_dev, else error
8262306a36Sopenharmony_ci */
8362306a36Sopenharmony_cistatic int rio_device_probe(struct device *dev)
8462306a36Sopenharmony_ci{
8562306a36Sopenharmony_ci	struct rio_driver *rdrv = to_rio_driver(dev->driver);
8662306a36Sopenharmony_ci	struct rio_dev *rdev = to_rio_dev(dev);
8762306a36Sopenharmony_ci	int error = -ENODEV;
8862306a36Sopenharmony_ci	const struct rio_device_id *id;
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_ci	if (!rdev->driver && rdrv->probe) {
9162306a36Sopenharmony_ci		if (!rdrv->id_table)
9262306a36Sopenharmony_ci			return error;
9362306a36Sopenharmony_ci		id = rio_match_device(rdrv->id_table, rdev);
9462306a36Sopenharmony_ci		rio_dev_get(rdev);
9562306a36Sopenharmony_ci		if (id)
9662306a36Sopenharmony_ci			error = rdrv->probe(rdev, id);
9762306a36Sopenharmony_ci		if (error >= 0) {
9862306a36Sopenharmony_ci			rdev->driver = rdrv;
9962306a36Sopenharmony_ci			error = 0;
10062306a36Sopenharmony_ci		} else
10162306a36Sopenharmony_ci			rio_dev_put(rdev);
10262306a36Sopenharmony_ci	}
10362306a36Sopenharmony_ci	return error;
10462306a36Sopenharmony_ci}
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ci/**
10762306a36Sopenharmony_ci *  rio_device_remove - Remove a RIO device from the system
10862306a36Sopenharmony_ci *
10962306a36Sopenharmony_ci *  @dev: the RIO device structure to match against
11062306a36Sopenharmony_ci *
11162306a36Sopenharmony_ci * Remove a RIO device from the system. If it has an associated
11262306a36Sopenharmony_ci * driver, then run the driver remove() method.  Then update
11362306a36Sopenharmony_ci * the reference count.
11462306a36Sopenharmony_ci */
11562306a36Sopenharmony_cistatic void rio_device_remove(struct device *dev)
11662306a36Sopenharmony_ci{
11762306a36Sopenharmony_ci	struct rio_dev *rdev = to_rio_dev(dev);
11862306a36Sopenharmony_ci	struct rio_driver *rdrv = rdev->driver;
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_ci	if (rdrv) {
12162306a36Sopenharmony_ci		if (rdrv->remove)
12262306a36Sopenharmony_ci			rdrv->remove(rdev);
12362306a36Sopenharmony_ci		rdev->driver = NULL;
12462306a36Sopenharmony_ci	}
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_ci	rio_dev_put(rdev);
12762306a36Sopenharmony_ci}
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_cistatic void rio_device_shutdown(struct device *dev)
13062306a36Sopenharmony_ci{
13162306a36Sopenharmony_ci	struct rio_dev *rdev = to_rio_dev(dev);
13262306a36Sopenharmony_ci	struct rio_driver *rdrv = rdev->driver;
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_ci	dev_dbg(dev, "RIO: %s\n", __func__);
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_ci	if (rdrv && rdrv->shutdown)
13762306a36Sopenharmony_ci		rdrv->shutdown(rdev);
13862306a36Sopenharmony_ci}
13962306a36Sopenharmony_ci
14062306a36Sopenharmony_ci/**
14162306a36Sopenharmony_ci *  rio_register_driver - register a new RIO driver
14262306a36Sopenharmony_ci *  @rdrv: the RIO driver structure to register
14362306a36Sopenharmony_ci *
14462306a36Sopenharmony_ci *  Adds a &struct rio_driver to the list of registered drivers.
14562306a36Sopenharmony_ci *  Returns a negative value on error, otherwise 0. If no error
14662306a36Sopenharmony_ci *  occurred, the driver remains registered even if no device
14762306a36Sopenharmony_ci *  was claimed during registration.
14862306a36Sopenharmony_ci */
14962306a36Sopenharmony_ciint rio_register_driver(struct rio_driver *rdrv)
15062306a36Sopenharmony_ci{
15162306a36Sopenharmony_ci	/* initialize common driver fields */
15262306a36Sopenharmony_ci	rdrv->driver.name = rdrv->name;
15362306a36Sopenharmony_ci	rdrv->driver.bus = &rio_bus_type;
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_ci	/* register with core */
15662306a36Sopenharmony_ci	return driver_register(&rdrv->driver);
15762306a36Sopenharmony_ci}
15862306a36Sopenharmony_ci
15962306a36Sopenharmony_ci/**
16062306a36Sopenharmony_ci *  rio_unregister_driver - unregister a RIO driver
16162306a36Sopenharmony_ci *  @rdrv: the RIO driver structure to unregister
16262306a36Sopenharmony_ci *
16362306a36Sopenharmony_ci *  Deletes the &struct rio_driver from the list of registered RIO
16462306a36Sopenharmony_ci *  drivers, gives it a chance to clean up by calling its remove()
16562306a36Sopenharmony_ci *  function for each device it was responsible for, and marks those
16662306a36Sopenharmony_ci *  devices as driverless.
16762306a36Sopenharmony_ci */
16862306a36Sopenharmony_civoid rio_unregister_driver(struct rio_driver *rdrv)
16962306a36Sopenharmony_ci{
17062306a36Sopenharmony_ci	driver_unregister(&rdrv->driver);
17162306a36Sopenharmony_ci}
17262306a36Sopenharmony_ci
17362306a36Sopenharmony_civoid rio_attach_device(struct rio_dev *rdev)
17462306a36Sopenharmony_ci{
17562306a36Sopenharmony_ci	rdev->dev.bus = &rio_bus_type;
17662306a36Sopenharmony_ci}
17762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(rio_attach_device);
17862306a36Sopenharmony_ci
17962306a36Sopenharmony_ci/**
18062306a36Sopenharmony_ci *  rio_match_bus - Tell if a RIO device structure has a matching RIO driver device id structure
18162306a36Sopenharmony_ci *  @dev: the standard device structure to match against
18262306a36Sopenharmony_ci *  @drv: the standard driver structure containing the ids to match against
18362306a36Sopenharmony_ci *
18462306a36Sopenharmony_ci *  Used by a driver to check whether a RIO device present in the
18562306a36Sopenharmony_ci *  system is in its list of supported devices. Returns 1 if
18662306a36Sopenharmony_ci *  there is a matching &struct rio_device_id or 0 if there is
18762306a36Sopenharmony_ci *  no match.
18862306a36Sopenharmony_ci */
18962306a36Sopenharmony_cistatic int rio_match_bus(struct device *dev, struct device_driver *drv)
19062306a36Sopenharmony_ci{
19162306a36Sopenharmony_ci	struct rio_dev *rdev = to_rio_dev(dev);
19262306a36Sopenharmony_ci	struct rio_driver *rdrv = to_rio_driver(drv);
19362306a36Sopenharmony_ci	const struct rio_device_id *id = rdrv->id_table;
19462306a36Sopenharmony_ci	const struct rio_device_id *found_id;
19562306a36Sopenharmony_ci
19662306a36Sopenharmony_ci	if (!id)
19762306a36Sopenharmony_ci		goto out;
19862306a36Sopenharmony_ci
19962306a36Sopenharmony_ci	found_id = rio_match_device(id, rdev);
20062306a36Sopenharmony_ci
20162306a36Sopenharmony_ci	if (found_id)
20262306a36Sopenharmony_ci		return 1;
20362306a36Sopenharmony_ci
20462306a36Sopenharmony_ci      out:return 0;
20562306a36Sopenharmony_ci}
20662306a36Sopenharmony_ci
20762306a36Sopenharmony_cistatic int rio_uevent(const struct device *dev, struct kobj_uevent_env *env)
20862306a36Sopenharmony_ci{
20962306a36Sopenharmony_ci	const struct rio_dev *rdev;
21062306a36Sopenharmony_ci
21162306a36Sopenharmony_ci	if (!dev)
21262306a36Sopenharmony_ci		return -ENODEV;
21362306a36Sopenharmony_ci
21462306a36Sopenharmony_ci	rdev = to_rio_dev(dev);
21562306a36Sopenharmony_ci	if (!rdev)
21662306a36Sopenharmony_ci		return -ENODEV;
21762306a36Sopenharmony_ci
21862306a36Sopenharmony_ci	if (add_uevent_var(env, "MODALIAS=rapidio:v%04Xd%04Xav%04Xad%04X",
21962306a36Sopenharmony_ci			   rdev->vid, rdev->did, rdev->asm_vid, rdev->asm_did))
22062306a36Sopenharmony_ci		return -ENOMEM;
22162306a36Sopenharmony_ci	return 0;
22262306a36Sopenharmony_ci}
22362306a36Sopenharmony_ci
22462306a36Sopenharmony_cistruct class rio_mport_class = {
22562306a36Sopenharmony_ci	.name		= "rapidio_port",
22662306a36Sopenharmony_ci	.dev_groups	= rio_mport_groups,
22762306a36Sopenharmony_ci};
22862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(rio_mport_class);
22962306a36Sopenharmony_ci
23062306a36Sopenharmony_cistruct bus_type rio_bus_type = {
23162306a36Sopenharmony_ci	.name = "rapidio",
23262306a36Sopenharmony_ci	.match = rio_match_bus,
23362306a36Sopenharmony_ci	.dev_groups = rio_dev_groups,
23462306a36Sopenharmony_ci	.bus_groups = rio_bus_groups,
23562306a36Sopenharmony_ci	.probe = rio_device_probe,
23662306a36Sopenharmony_ci	.remove = rio_device_remove,
23762306a36Sopenharmony_ci	.shutdown = rio_device_shutdown,
23862306a36Sopenharmony_ci	.uevent	= rio_uevent,
23962306a36Sopenharmony_ci};
24062306a36Sopenharmony_ci
24162306a36Sopenharmony_ci/**
24262306a36Sopenharmony_ci *  rio_bus_init - Register the RapidIO bus with the device model
24362306a36Sopenharmony_ci *
24462306a36Sopenharmony_ci *  Registers the RIO mport device class and RIO bus type with the Linux
24562306a36Sopenharmony_ci *  device model.
24662306a36Sopenharmony_ci */
24762306a36Sopenharmony_cistatic int __init rio_bus_init(void)
24862306a36Sopenharmony_ci{
24962306a36Sopenharmony_ci	int ret;
25062306a36Sopenharmony_ci
25162306a36Sopenharmony_ci	ret = class_register(&rio_mport_class);
25262306a36Sopenharmony_ci	if (!ret) {
25362306a36Sopenharmony_ci		ret = bus_register(&rio_bus_type);
25462306a36Sopenharmony_ci		if (ret)
25562306a36Sopenharmony_ci			class_unregister(&rio_mport_class);
25662306a36Sopenharmony_ci	}
25762306a36Sopenharmony_ci	return ret;
25862306a36Sopenharmony_ci}
25962306a36Sopenharmony_ci
26062306a36Sopenharmony_cipostcore_initcall(rio_bus_init);
26162306a36Sopenharmony_ci
26262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(rio_register_driver);
26362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(rio_unregister_driver);
26462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(rio_bus_type);
26562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(rio_dev_get);
26662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(rio_dev_put);
267