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 int 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 | return isa_driver->remove(dev, to_isa_dev(dev)->id); |
55 | |
56 | return 0; |
57 | } |
58 | |
59 | static void isa_bus_shutdown(struct device *dev) |
60 | { |
61 | struct isa_driver *isa_driver = dev->platform_data; |
62 | |
63 | if (isa_driver && isa_driver->shutdown) |
64 | isa_driver->shutdown(dev, to_isa_dev(dev)->id); |
65 | } |
66 | |
67 | static int isa_bus_suspend(struct device *dev, pm_message_t state) |
68 | { |
69 | struct isa_driver *isa_driver = dev->platform_data; |
70 | |
71 | if (isa_driver && isa_driver->suspend) |
72 | return isa_driver->suspend(dev, to_isa_dev(dev)->id, state); |
73 | |
74 | return 0; |
75 | } |
76 | |
77 | static int isa_bus_resume(struct device *dev) |
78 | { |
79 | struct isa_driver *isa_driver = dev->platform_data; |
80 | |
81 | if (isa_driver && isa_driver->resume) |
82 | return isa_driver->resume(dev, to_isa_dev(dev)->id); |
83 | |
84 | return 0; |
85 | } |
86 | |
87 | static struct bus_type isa_bus_type = { |
88 | .name = "isa" , |
89 | .match = isa_bus_match, |
90 | .probe = isa_bus_probe, |
91 | .remove = isa_bus_remove, |
92 | .shutdown = isa_bus_shutdown, |
93 | .suspend = isa_bus_suspend, |
94 | .resume = isa_bus_resume |
95 | }; |
96 | |
97 | static void isa_dev_release(struct device *dev) |
98 | { |
99 | kfree(to_isa_dev(dev)); |
100 | } |
101 | |
102 | void isa_unregister_driver(struct isa_driver *isa_driver) |
103 | { |
104 | struct device *dev = isa_driver->devices; |
105 | |
106 | while (dev) { |
107 | struct device *tmp = to_isa_dev(dev)->next; |
108 | device_unregister(dev); |
109 | dev = tmp; |
110 | } |
111 | driver_unregister(&isa_driver->driver); |
112 | } |
113 | EXPORT_SYMBOL_GPL(isa_unregister_driver); |
114 | |
115 | int isa_register_driver(struct isa_driver *isa_driver, unsigned int ndev) |
116 | { |
117 | int error; |
118 | unsigned int id; |
119 | |
120 | isa_driver->driver.bus = &isa_bus_type; |
121 | isa_driver->devices = NULL; |
122 | |
123 | error = driver_register(&isa_driver->driver); |
124 | if (error) |
125 | return error; |
126 | |
127 | for (id = 0; id < ndev; id++) { |
128 | struct isa_dev *isa_dev; |
129 | |
130 | isa_dev = kzalloc(sizeof *isa_dev, GFP_KERNEL); |
131 | if (!isa_dev) { |
132 | error = -ENOMEM; |
133 | break; |
134 | } |
135 | |
136 | isa_dev->dev.parent = &isa_bus; |
137 | isa_dev->dev.bus = &isa_bus_type; |
138 | |
139 | dev_set_name(&isa_dev->dev, "%s.%u" , |
140 | isa_driver->driver.name, id); |
141 | isa_dev->dev.platform_data = isa_driver; |
142 | isa_dev->dev.release = isa_dev_release; |
143 | isa_dev->id = id; |
144 | |
145 | isa_dev->dev.coherent_dma_mask = DMA_BIT_MASK(24); |
146 | isa_dev->dev.dma_mask = &isa_dev->dev.coherent_dma_mask; |
147 | |
148 | error = device_register(&isa_dev->dev); |
149 | if (error) { |
150 | put_device(&isa_dev->dev); |
151 | break; |
152 | } |
153 | |
154 | if (isa_dev->dev.platform_data) { |
155 | isa_dev->next = isa_driver->devices; |
156 | isa_driver->devices = &isa_dev->dev; |
157 | } else |
158 | device_unregister(&isa_dev->dev); |
159 | } |
160 | |
161 | if (!error && !isa_driver->devices) |
162 | error = -ENODEV; |
163 | |
164 | if (error) |
165 | isa_unregister_driver(isa_driver); |
166 | |
167 | return error; |
168 | } |
169 | EXPORT_SYMBOL_GPL(isa_register_driver); |
170 | |
171 | static int __init isa_bus_init(void) |
172 | { |
173 | int error; |
174 | |
175 | error = bus_register(&isa_bus_type); |
176 | if (!error) { |
177 | error = device_register(&isa_bus); |
178 | if (error) |
179 | bus_unregister(&isa_bus_type); |
180 | } |
181 | return error; |
182 | } |
183 | |
184 | postcore_initcall(isa_bus_init); |
185 | |