162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * ISA bus.
462306a36Sopenharmony_ci */
562306a36Sopenharmony_ci
662306a36Sopenharmony_ci#include <linux/device.h>
762306a36Sopenharmony_ci#include <linux/kernel.h>
862306a36Sopenharmony_ci#include <linux/slab.h>
962306a36Sopenharmony_ci#include <linux/module.h>
1062306a36Sopenharmony_ci#include <linux/init.h>
1162306a36Sopenharmony_ci#include <linux/dma-mapping.h>
1262306a36Sopenharmony_ci#include <linux/isa.h>
1362306a36Sopenharmony_ci
1462306a36Sopenharmony_cistatic struct device isa_bus = {
1562306a36Sopenharmony_ci	.init_name	= "isa"
1662306a36Sopenharmony_ci};
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_cistruct isa_dev {
1962306a36Sopenharmony_ci	struct device dev;
2062306a36Sopenharmony_ci	struct device *next;
2162306a36Sopenharmony_ci	unsigned int id;
2262306a36Sopenharmony_ci};
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_ci#define to_isa_dev(x) container_of((x), struct isa_dev, dev)
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_cistatic int isa_bus_match(struct device *dev, struct device_driver *driver)
2762306a36Sopenharmony_ci{
2862306a36Sopenharmony_ci	struct isa_driver *isa_driver = to_isa_driver(driver);
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_ci	if (dev->platform_data == isa_driver) {
3162306a36Sopenharmony_ci		if (!isa_driver->match ||
3262306a36Sopenharmony_ci			isa_driver->match(dev, to_isa_dev(dev)->id))
3362306a36Sopenharmony_ci			return 1;
3462306a36Sopenharmony_ci		dev->platform_data = NULL;
3562306a36Sopenharmony_ci	}
3662306a36Sopenharmony_ci	return 0;
3762306a36Sopenharmony_ci}
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_cistatic int isa_bus_probe(struct device *dev)
4062306a36Sopenharmony_ci{
4162306a36Sopenharmony_ci	struct isa_driver *isa_driver = dev->platform_data;
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_ci	if (isa_driver && isa_driver->probe)
4462306a36Sopenharmony_ci		return isa_driver->probe(dev, to_isa_dev(dev)->id);
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_ci	return 0;
4762306a36Sopenharmony_ci}
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_cistatic void isa_bus_remove(struct device *dev)
5062306a36Sopenharmony_ci{
5162306a36Sopenharmony_ci	struct isa_driver *isa_driver = dev->platform_data;
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_ci	if (isa_driver && isa_driver->remove)
5462306a36Sopenharmony_ci		isa_driver->remove(dev, to_isa_dev(dev)->id);
5562306a36Sopenharmony_ci}
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_cistatic void isa_bus_shutdown(struct device *dev)
5862306a36Sopenharmony_ci{
5962306a36Sopenharmony_ci	struct isa_driver *isa_driver = dev->platform_data;
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_ci	if (isa_driver && isa_driver->shutdown)
6262306a36Sopenharmony_ci		isa_driver->shutdown(dev, to_isa_dev(dev)->id);
6362306a36Sopenharmony_ci}
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_cistatic int isa_bus_suspend(struct device *dev, pm_message_t state)
6662306a36Sopenharmony_ci{
6762306a36Sopenharmony_ci	struct isa_driver *isa_driver = dev->platform_data;
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_ci	if (isa_driver && isa_driver->suspend)
7062306a36Sopenharmony_ci		return isa_driver->suspend(dev, to_isa_dev(dev)->id, state);
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_ci	return 0;
7362306a36Sopenharmony_ci}
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_cistatic int isa_bus_resume(struct device *dev)
7662306a36Sopenharmony_ci{
7762306a36Sopenharmony_ci	struct isa_driver *isa_driver = dev->platform_data;
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_ci	if (isa_driver && isa_driver->resume)
8062306a36Sopenharmony_ci		return isa_driver->resume(dev, to_isa_dev(dev)->id);
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_ci	return 0;
8362306a36Sopenharmony_ci}
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_cistatic struct bus_type isa_bus_type = {
8662306a36Sopenharmony_ci	.name		= "isa",
8762306a36Sopenharmony_ci	.match		= isa_bus_match,
8862306a36Sopenharmony_ci	.probe		= isa_bus_probe,
8962306a36Sopenharmony_ci	.remove		= isa_bus_remove,
9062306a36Sopenharmony_ci	.shutdown	= isa_bus_shutdown,
9162306a36Sopenharmony_ci	.suspend	= isa_bus_suspend,
9262306a36Sopenharmony_ci	.resume		= isa_bus_resume
9362306a36Sopenharmony_ci};
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_cistatic void isa_dev_release(struct device *dev)
9662306a36Sopenharmony_ci{
9762306a36Sopenharmony_ci	kfree(to_isa_dev(dev));
9862306a36Sopenharmony_ci}
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_civoid isa_unregister_driver(struct isa_driver *isa_driver)
10162306a36Sopenharmony_ci{
10262306a36Sopenharmony_ci	struct device *dev = isa_driver->devices;
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_ci	while (dev) {
10562306a36Sopenharmony_ci		struct device *tmp = to_isa_dev(dev)->next;
10662306a36Sopenharmony_ci		device_unregister(dev);
10762306a36Sopenharmony_ci		dev = tmp;
10862306a36Sopenharmony_ci	}
10962306a36Sopenharmony_ci	driver_unregister(&isa_driver->driver);
11062306a36Sopenharmony_ci}
11162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(isa_unregister_driver);
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_ciint isa_register_driver(struct isa_driver *isa_driver, unsigned int ndev)
11462306a36Sopenharmony_ci{
11562306a36Sopenharmony_ci	int error;
11662306a36Sopenharmony_ci	unsigned int id;
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_ci	isa_driver->driver.bus	= &isa_bus_type;
11962306a36Sopenharmony_ci	isa_driver->devices	= NULL;
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_ci	error = driver_register(&isa_driver->driver);
12262306a36Sopenharmony_ci	if (error)
12362306a36Sopenharmony_ci		return error;
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_ci	for (id = 0; id < ndev; id++) {
12662306a36Sopenharmony_ci		struct isa_dev *isa_dev;
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_ci		isa_dev = kzalloc(sizeof *isa_dev, GFP_KERNEL);
12962306a36Sopenharmony_ci		if (!isa_dev) {
13062306a36Sopenharmony_ci			error = -ENOMEM;
13162306a36Sopenharmony_ci			break;
13262306a36Sopenharmony_ci		}
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_ci		isa_dev->dev.parent	= &isa_bus;
13562306a36Sopenharmony_ci		isa_dev->dev.bus	= &isa_bus_type;
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_ci		dev_set_name(&isa_dev->dev, "%s.%u",
13862306a36Sopenharmony_ci			     isa_driver->driver.name, id);
13962306a36Sopenharmony_ci		isa_dev->dev.platform_data	= isa_driver;
14062306a36Sopenharmony_ci		isa_dev->dev.release		= isa_dev_release;
14162306a36Sopenharmony_ci		isa_dev->id			= id;
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_ci		isa_dev->dev.coherent_dma_mask = DMA_BIT_MASK(24);
14462306a36Sopenharmony_ci		isa_dev->dev.dma_mask = &isa_dev->dev.coherent_dma_mask;
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_ci		error = device_register(&isa_dev->dev);
14762306a36Sopenharmony_ci		if (error) {
14862306a36Sopenharmony_ci			put_device(&isa_dev->dev);
14962306a36Sopenharmony_ci			break;
15062306a36Sopenharmony_ci		}
15162306a36Sopenharmony_ci
15262306a36Sopenharmony_ci		isa_dev->next = isa_driver->devices;
15362306a36Sopenharmony_ci		isa_driver->devices = &isa_dev->dev;
15462306a36Sopenharmony_ci	}
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_ci	if (!error && !isa_driver->devices)
15762306a36Sopenharmony_ci		error = -ENODEV;
15862306a36Sopenharmony_ci
15962306a36Sopenharmony_ci	if (error)
16062306a36Sopenharmony_ci		isa_unregister_driver(isa_driver);
16162306a36Sopenharmony_ci
16262306a36Sopenharmony_ci	return error;
16362306a36Sopenharmony_ci}
16462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(isa_register_driver);
16562306a36Sopenharmony_ci
16662306a36Sopenharmony_cistatic int __init isa_bus_init(void)
16762306a36Sopenharmony_ci{
16862306a36Sopenharmony_ci	int error;
16962306a36Sopenharmony_ci
17062306a36Sopenharmony_ci	error = bus_register(&isa_bus_type);
17162306a36Sopenharmony_ci	if (!error) {
17262306a36Sopenharmony_ci		error = device_register(&isa_bus);
17362306a36Sopenharmony_ci		if (error)
17462306a36Sopenharmony_ci			bus_unregister(&isa_bus_type);
17562306a36Sopenharmony_ci	}
17662306a36Sopenharmony_ci	return error;
17762306a36Sopenharmony_ci}
17862306a36Sopenharmony_ci
17962306a36Sopenharmony_cipostcore_initcall(isa_bus_init);
180