Commit | Line | Data |
---|---|---|
b2441318 | 1 | // SPDX-License-Identifier: GPL-2.0 |
1da177e4 LT |
2 | /* |
3 | * I/O Processor (IOP) ADB Driver | |
4 | * Written and (C) 1999 by Joshua M. Thompson (funaho@jurai.org) | |
5 | * Based on via-cuda.c by Paul Mackerras. | |
6 | * | |
7 | * 1999-07-01 (jmt) - First implementation for new driver architecture. | |
8 | * | |
9 | * 1999-07-31 (jmt) - First working version. | |
1da177e4 LT |
10 | */ |
11 | ||
12 | #include <linux/types.h> | |
13 | #include <linux/kernel.h> | |
14 | #include <linux/mm.h> | |
15 | #include <linux/delay.h> | |
16 | #include <linux/init.h> | |
1da177e4 | 17 | |
47fd2060 FT |
18 | #include <asm/macintosh.h> |
19 | #include <asm/macints.h> | |
1da177e4 | 20 | #include <asm/mac_iop.h> |
1da177e4 | 21 | #include <asm/adb_iop.h> |
c396dd2e | 22 | #include <asm/unaligned.h> |
1da177e4 | 23 | |
47fd2060 | 24 | #include <linux/adb.h> |
1da177e4 | 25 | |
1da177e4 LT |
26 | static struct adb_request *current_req; |
27 | static struct adb_request *last_req; | |
c66da95a | 28 | static unsigned int autopoll_devs; |
10199e90 | 29 | static u8 autopoll_addr; |
1da177e4 LT |
30 | |
31 | static enum adb_iop_state { | |
47fd2060 FT |
32 | idle, |
33 | sending, | |
34 | awaiting_reply | |
1da177e4 LT |
35 | } adb_iop_state; |
36 | ||
37 | static void adb_iop_start(void); | |
38 | static int adb_iop_probe(void); | |
39 | static int adb_iop_init(void); | |
40 | static int adb_iop_send_request(struct adb_request *, int); | |
41 | static int adb_iop_write(struct adb_request *); | |
42 | static int adb_iop_autopoll(int); | |
43 | static void adb_iop_poll(void); | |
44 | static int adb_iop_reset_bus(void); | |
45 | ||
10199e90 FT |
46 | /* ADB command byte structure */ |
47 | #define ADDR_MASK 0xF0 | |
48 | #define OP_MASK 0x0C | |
49 | #define TALK 0x0C | |
50 | ||
1da177e4 | 51 | struct adb_driver adb_iop_driver = { |
3a52f6f9 FT |
52 | .name = "ISM IOP", |
53 | .probe = adb_iop_probe, | |
54 | .init = adb_iop_init, | |
55 | .send_request = adb_iop_send_request, | |
56 | .autopoll = adb_iop_autopoll, | |
57 | .poll = adb_iop_poll, | |
58 | .reset_bus = adb_iop_reset_bus | |
1da177e4 LT |
59 | }; |
60 | ||
32226e81 | 61 | static void adb_iop_done(void) |
1da177e4 | 62 | { |
32226e81 FT |
63 | struct adb_request *req = current_req; |
64 | ||
65 | adb_iop_state = idle; | |
66 | ||
1da177e4 LT |
67 | req->complete = 1; |
68 | current_req = req->next; | |
47fd2060 FT |
69 | if (req->done) |
70 | (*req->done)(req); | |
32226e81 FT |
71 | |
72 | if (adb_iop_state == idle) | |
73 | adb_iop_start(); | |
1da177e4 LT |
74 | } |
75 | ||
76 | /* | |
77 | * Completion routine for ADB commands sent to the IOP. | |
78 | * | |
79 | * This will be called when a packet has been successfully sent. | |
80 | */ | |
81 | ||
7d12e780 | 82 | static void adb_iop_complete(struct iop_msg *msg) |
1da177e4 | 83 | { |
38b7a2ae | 84 | unsigned long flags; |
1da177e4 LT |
85 | |
86 | local_irq_save(flags); | |
87 | ||
2c9cfbad | 88 | adb_iop_state = awaiting_reply; |
1da177e4 LT |
89 | |
90 | local_irq_restore(flags); | |
91 | } | |
92 | ||
93 | /* | |
94 | * Listen for ADB messages from the IOP. | |
95 | * | |
2c9cfbad FT |
96 | * This will be called when unsolicited IOP messages are received. |
97 | * These IOP messages can carry ADB autopoll responses and also occur | |
98 | * after explicit ADB commands. | |
1da177e4 LT |
99 | */ |
100 | ||
7d12e780 | 101 | static void adb_iop_listen(struct iop_msg *msg) |
1da177e4 | 102 | { |
47fd2060 | 103 | struct adb_iopmsg *amsg = (struct adb_iopmsg *)msg->message; |
10199e90 FT |
104 | u8 addr = (amsg->cmd & ADDR_MASK) >> 4; |
105 | u8 op = amsg->cmd & OP_MASK; | |
38b7a2ae | 106 | unsigned long flags; |
32226e81 | 107 | bool req_done = false; |
1da177e4 LT |
108 | |
109 | local_irq_save(flags); | |
110 | ||
10199e90 FT |
111 | /* Responses to Talk commands may be unsolicited as they are |
112 | * produced when the IOP polls devices. They are mostly timeouts. | |
ff785e17 | 113 | */ |
10199e90 FT |
114 | if (op == TALK && ((1 << addr) & autopoll_devs)) |
115 | autopoll_addr = addr; | |
116 | ||
117 | switch (amsg->flags & (ADB_IOP_EXPLICIT | | |
118 | ADB_IOP_AUTOPOLL | | |
119 | ADB_IOP_TIMEOUT)) { | |
120 | case ADB_IOP_EXPLICIT: | |
121 | case ADB_IOP_EXPLICIT | ADB_IOP_TIMEOUT: | |
32226e81 FT |
122 | if (adb_iop_state == awaiting_reply) { |
123 | struct adb_request *req = current_req; | |
124 | ||
2c9cfbad FT |
125 | if (req->reply_expected) { |
126 | req->reply_len = amsg->count + 1; | |
127 | memcpy(req->reply, &amsg->cmd, req->reply_len); | |
128 | } | |
32226e81 FT |
129 | |
130 | req_done = true; | |
1da177e4 | 131 | } |
10199e90 FT |
132 | break; |
133 | case ADB_IOP_AUTOPOLL: | |
134 | if (((1 << addr) & autopoll_devs) && | |
135 | amsg->cmd == ADB_READREG(addr, 0)) | |
136 | adb_input(&amsg->cmd, amsg->count + 1, 1); | |
137 | break; | |
1da177e4 | 138 | } |
10199e90 FT |
139 | msg->reply[0] = autopoll_addr ? ADB_IOP_AUTOPOLL : 0; |
140 | msg->reply[1] = 0; | |
141 | msg->reply[2] = autopoll_addr ? ADB_READREG(autopoll_addr, 0) : 0; | |
1da177e4 | 142 | iop_complete_message(msg); |
32226e81 FT |
143 | |
144 | if (req_done) | |
145 | adb_iop_done(); | |
146 | ||
1da177e4 LT |
147 | local_irq_restore(flags); |
148 | } | |
149 | ||
150 | /* | |
151 | * Start sending an ADB packet, IOP style | |
152 | * | |
153 | * There isn't much to do other than hand the packet over to the IOP | |
154 | * after encapsulating it in an adb_iopmsg. | |
155 | */ | |
156 | ||
157 | static void adb_iop_start(void) | |
158 | { | |
1da177e4 LT |
159 | struct adb_request *req; |
160 | struct adb_iopmsg amsg; | |
1da177e4 LT |
161 | |
162 | /* get the packet to send */ | |
163 | req = current_req; | |
47fd2060 FT |
164 | if (!req) |
165 | return; | |
1da177e4 | 166 | |
ff785e17 FT |
167 | /* The IOP takes MacII-style packets, so strip the initial |
168 | * ADB_PACKET byte. | |
169 | */ | |
1da177e4 LT |
170 | amsg.flags = ADB_IOP_EXPLICIT; |
171 | amsg.count = req->nbytes - 2; | |
172 | ||
ff785e17 FT |
173 | /* amsg.data immediately follows amsg.cmd, effectively making |
174 | * &amsg.cmd a pointer to the beginning of a full ADB packet. | |
175 | */ | |
1da177e4 LT |
176 | memcpy(&amsg.cmd, req->data + 1, req->nbytes - 1); |
177 | ||
178 | req->sent = 1; | |
179 | adb_iop_state = sending; | |
1da177e4 | 180 | |
ff785e17 FT |
181 | /* Now send it. The IOP manager will call adb_iop_complete |
182 | * when the message has been sent. | |
183 | */ | |
47fd2060 FT |
184 | iop_send_message(ADB_IOP, ADB_CHAN, req, sizeof(amsg), (__u8 *)&amsg, |
185 | adb_iop_complete); | |
1da177e4 LT |
186 | } |
187 | ||
56b732ed | 188 | static int adb_iop_probe(void) |
1da177e4 | 189 | { |
47fd2060 FT |
190 | if (!iop_ism_present) |
191 | return -ENODEV; | |
1da177e4 LT |
192 | return 0; |
193 | } | |
194 | ||
56b732ed | 195 | static int adb_iop_init(void) |
1da177e4 | 196 | { |
351e5ad3 | 197 | pr_info("adb: IOP ISM driver v0.4 for Unified ADB\n"); |
1da177e4 LT |
198 | iop_listen(ADB_IOP, ADB_CHAN, adb_iop_listen, "ADB"); |
199 | return 0; | |
200 | } | |
201 | ||
56b732ed | 202 | static int adb_iop_send_request(struct adb_request *req, int sync) |
1da177e4 LT |
203 | { |
204 | int err; | |
205 | ||
206 | err = adb_iop_write(req); | |
47fd2060 FT |
207 | if (err) |
208 | return err; | |
1da177e4 LT |
209 | |
210 | if (sync) { | |
47fd2060 FT |
211 | while (!req->complete) |
212 | adb_iop_poll(); | |
1da177e4 LT |
213 | } |
214 | return 0; | |
215 | } | |
216 | ||
217 | static int adb_iop_write(struct adb_request *req) | |
218 | { | |
219 | unsigned long flags; | |
220 | ||
221 | if ((req->nbytes < 2) || (req->data[0] != ADB_PACKET)) { | |
222 | req->complete = 1; | |
223 | return -EINVAL; | |
224 | } | |
225 | ||
a5d361fc | 226 | req->next = NULL; |
1da177e4 LT |
227 | req->sent = 0; |
228 | req->complete = 0; | |
229 | req->reply_len = 0; | |
230 | ||
aac840ec FT |
231 | local_irq_save(flags); |
232 | ||
56b732ed | 233 | if (current_req) { |
1da177e4 LT |
234 | last_req->next = req; |
235 | last_req = req; | |
236 | } else { | |
237 | current_req = req; | |
238 | last_req = req; | |
239 | } | |
240 | ||
47fd2060 FT |
241 | if (adb_iop_state == idle) |
242 | adb_iop_start(); | |
aac840ec FT |
243 | |
244 | local_irq_restore(flags); | |
245 | ||
1da177e4 LT |
246 | return 0; |
247 | } | |
248 | ||
c66da95a FT |
249 | static void adb_iop_set_ap_complete(struct iop_msg *msg) |
250 | { | |
251 | struct adb_iopmsg *amsg = (struct adb_iopmsg *)msg->message; | |
252 | ||
c396dd2e | 253 | autopoll_devs = get_unaligned_be16(amsg->data); |
10199e90 FT |
254 | if (autopoll_devs & (1 << autopoll_addr)) |
255 | return; | |
256 | autopoll_addr = autopoll_devs ? (ffs(autopoll_devs) - 1) : 0; | |
c66da95a FT |
257 | } |
258 | ||
56b732ed | 259 | static int adb_iop_autopoll(int devs) |
1da177e4 | 260 | { |
c66da95a FT |
261 | struct adb_iopmsg amsg; |
262 | unsigned long flags; | |
263 | unsigned int mask = (unsigned int)devs & 0xFFFE; | |
264 | ||
265 | local_irq_save(flags); | |
266 | ||
267 | amsg.flags = ADB_IOP_SET_AUTOPOLL | (mask ? ADB_IOP_AUTOPOLL : 0); | |
268 | amsg.count = 2; | |
269 | amsg.cmd = 0; | |
c396dd2e | 270 | put_unaligned_be16(mask, amsg.data); |
c66da95a FT |
271 | |
272 | iop_send_message(ADB_IOP, ADB_CHAN, NULL, sizeof(amsg), (__u8 *)&amsg, | |
273 | adb_iop_set_ap_complete); | |
274 | ||
275 | local_irq_restore(flags); | |
276 | ||
1da177e4 LT |
277 | return 0; |
278 | } | |
279 | ||
56b732ed | 280 | static void adb_iop_poll(void) |
1da177e4 | 281 | { |
92178fca | 282 | iop_ism_irq_poll(ADB_IOP); |
1da177e4 LT |
283 | } |
284 | ||
56b732ed | 285 | static int adb_iop_reset_bus(void) |
1da177e4 | 286 | { |
303511ed FT |
287 | struct adb_request req; |
288 | ||
289 | /* Command = 0, Address = ignored */ | |
290 | adb_request(&req, NULL, ADBREQ_NOSEND, 1, ADB_BUSRESET); | |
291 | adb_iop_send_request(&req, 1); | |
292 | ||
293 | /* Don't want any more requests during the Global Reset low time. */ | |
294 | mdelay(3); | |
1da177e4 LT |
295 | |
296 | return 0; | |
297 | } |