1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * ISA bus. |
4 | */ |
5 | |
6 | #include <linux/device.h> |
7 | #include <linux/kernel.h> |
8 | #include <linux/slab.h> |
9 | #include <linux/module.h> |
10 | #include <linux/init.h> |
11 | #include <linux/dma-mapping.h> |
12 | #include <linux/isa.h> |
13 | |
14 | static struct device isa_bus = { |
15 | .init_name = "isa" |
16 | }; |
17 | |
18 | struct isa_dev { |
19 | struct device dev; |
20 | struct device *next; |
21 | unsigned int id; |
22 | }; |
23 | |
24 | #define to_isa_dev(x) container_of((x), struct isa_dev, dev) |
25 | |
26 | static int isa_bus_match(struct device *dev, struct device_driver *driver) |
27 | { |
28 | struct isa_driver *isa_driver = to_isa_driver(driver); |
29 | |
30 | if (dev->platform_data == isa_driver) { |
31 | if (!isa_driver->match || |
32 | isa_driver->match(dev, to_isa_dev(dev)->id)) |
33 | return 1; |
34 | dev->platform_data = NULL; |
35 | } |
36 | return 0; |
37 | } |
38 | |
39 | static int isa_bus_probe(struct device *dev) |
40 | { |
41 | struct isa_driver *isa_driver = dev->platform_data; |
42 | |
43 | if (isa_driver && isa_driver->probe) |
44 | return isa_driver->probe(dev, to_isa_dev(dev)->id); |
45 | |
46 | return 0; |
47 | } |
48 | |
49 | static void isa_bus_remove(struct device *dev) |
50 | { |
51 | struct isa_driver *isa_driver = dev->platform_data; |
52 | |
53 | if (isa_driver && isa_driver->remove) |
54 | isa_driver->remove(dev, to_isa_dev(dev)->id); |
55 | } |
56 | |
57 | static void isa_bus_shutdown(struct device *dev) |
58 | { |
59 | struct isa_driver *isa_driver = dev->platform_data; |
60 | |
61 | if (isa_driver && isa_driver->shutdown) |
62 | isa_driver->shutdown(dev, to_isa_dev(dev)->id); |
63 | } |
64 | |
65 | static int isa_bus_suspend(struct device *dev, pm_message_t state) |
66 | { |
67 | struct isa_driver *isa_driver = dev->platform_data; |
68 | |
69 | if (isa_driver && isa_driver->suspend) |
70 | return isa_driver->suspend(dev, to_isa_dev(dev)->id, state); |
71 | |
72 | return 0; |
73 | } |
74 | |
75 | static int isa_bus_resume(struct device *dev) |
76 | { |
77 | struct isa_driver *isa_driver = dev->platform_data; |
78 | |
79 | if (isa_driver && isa_driver->resume) |
80 | return isa_driver->resume(dev, to_isa_dev(dev)->id); |
81 | |
82 | return 0; |
83 | } |
84 | |
85 | static struct bus_type isa_bus_type = { |
86 | .name = "isa" , |
87 | .match = isa_bus_match, |
88 | .probe = isa_bus_probe, |
89 | .remove = isa_bus_remove, |
90 | .shutdown = isa_bus_shutdown, |
91 | .suspend = isa_bus_suspend, |
92 | .resume = isa_bus_resume |
93 | }; |
94 | |
95 | static void isa_dev_release(struct device *dev) |
96 | { |
97 | kfree(to_isa_dev(dev)); |
98 | } |
99 | |
100 | void isa_unregister_driver(struct isa_driver *isa_driver) |
101 | { |
102 | struct device *dev = isa_driver->devices; |
103 | |
104 | while (dev) { |
105 | struct device *tmp = to_isa_dev(dev)->next; |
106 | device_unregister(dev); |
107 | dev = tmp; |
108 | } |
109 | driver_unregister(drv: &isa_driver->driver); |
110 | } |
111 | EXPORT_SYMBOL_GPL(isa_unregister_driver); |
112 | |
113 | int isa_register_driver(struct isa_driver *isa_driver, unsigned int ndev) |
114 | { |
115 | int error; |
116 | unsigned int id; |
117 | |
118 | isa_driver->driver.bus = &isa_bus_type; |
119 | isa_driver->devices = NULL; |
120 | |
121 | error = driver_register(drv: &isa_driver->driver); |
122 | if (error) |
123 | return error; |
124 | |
125 | for (id = 0; id < ndev; id++) { |
126 | struct isa_dev *isa_dev; |
127 | |
128 | isa_dev = kzalloc(size: sizeof *isa_dev, GFP_KERNEL); |
129 | if (!isa_dev) { |
130 | error = -ENOMEM; |
131 | break; |
132 | } |
133 | |
134 | isa_dev->dev.parent = &isa_bus; |
135 | isa_dev->dev.bus = &isa_bus_type; |
136 | |
137 | dev_set_name(dev: &isa_dev->dev, name: "%s.%u" , |
138 | isa_driver->driver.name, id); |
139 | isa_dev->dev.platform_data = isa_driver; |
140 | isa_dev->dev.release = isa_dev_release; |
141 | isa_dev->id = id; |
142 | |
143 | isa_dev->dev.coherent_dma_mask = DMA_BIT_MASK(24); |
144 | isa_dev->dev.dma_mask = &isa_dev->dev.coherent_dma_mask; |
145 | |
146 | error = device_register(dev: &isa_dev->dev); |
147 | if (error) { |
148 | put_device(dev: &isa_dev->dev); |
149 | break; |
150 | } |
151 | |
152 | isa_dev->next = isa_driver->devices; |
153 | isa_driver->devices = &isa_dev->dev; |
154 | } |
155 | |
156 | if (!error && !isa_driver->devices) |
157 | error = -ENODEV; |
158 | |
159 | if (error) |
160 | isa_unregister_driver(isa_driver); |
161 | |
162 | return error; |
163 | } |
164 | EXPORT_SYMBOL_GPL(isa_register_driver); |
165 | |
166 | static int __init isa_bus_init(void) |
167 | { |
168 | int error; |
169 | |
170 | error = bus_register(bus: &isa_bus_type); |
171 | if (!error) { |
172 | error = device_register(dev: &isa_bus); |
173 | if (error) |
174 | bus_unregister(bus: &isa_bus_type); |
175 | } |
176 | return error; |
177 | } |
178 | |
179 | postcore_initcall(isa_bus_init); |
180 | |