1 | /* |
2 | * Access to HP-HIL MLC through HP System Device Controller. |
3 | * |
4 | * Copyright (c) 2001 Brian S. Julin |
5 | * All rights reserved. |
6 | * |
7 | * Redistribution and use in source and binary forms, with or without |
8 | * modification, are permitted provided that the following conditions |
9 | * are met: |
10 | * 1. Redistributions of source code must retain the above copyright |
11 | * notice, this list of conditions, and the following disclaimer, |
12 | * without modification. |
13 | * 2. The name of the author may not be used to endorse or promote products |
14 | * derived from this software without specific prior written permission. |
15 | * |
16 | * Alternatively, this software may be distributed under the terms of the |
17 | * GNU General Public License ("GPL"). |
18 | * |
19 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND |
20 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
21 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
22 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR |
23 | * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
24 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
25 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
26 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
27 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
28 | * |
29 | * References: |
30 | * HP-HIL Technical Reference Manual. Hewlett Packard Product No. 45918A |
31 | * System Device Controller Microprocessor Firmware Theory of Operation |
32 | * for Part Number 1820-4784 Revision B. Dwg No. A-1820-4784-2 |
33 | * |
34 | */ |
35 | |
36 | #include <linux/hil_mlc.h> |
37 | #include <linux/hp_sdc.h> |
38 | #include <linux/errno.h> |
39 | #include <linux/kernel.h> |
40 | #include <linux/module.h> |
41 | #include <linux/init.h> |
42 | #include <linux/string.h> |
43 | #include <linux/semaphore.h> |
44 | |
45 | #define PREFIX "HP SDC MLC: " |
46 | |
47 | static hil_mlc hp_sdc_mlc; |
48 | |
49 | MODULE_AUTHOR("Brian S. Julin <bri@calyx.com>" ); |
50 | MODULE_DESCRIPTION("Glue for onboard HIL MLC in HP-PARISC machines" ); |
51 | MODULE_LICENSE("Dual BSD/GPL" ); |
52 | |
53 | static struct hp_sdc_mlc_priv_s { |
54 | int emtestmode; |
55 | hp_sdc_transaction trans; |
56 | u8 tseq[16]; |
57 | int got5x; |
58 | } hp_sdc_mlc_priv; |
59 | |
60 | /************************* Interrupt context ******************************/ |
61 | static void hp_sdc_mlc_isr (int irq, void *dev_id, |
62 | uint8_t status, uint8_t data) |
63 | { |
64 | int idx; |
65 | hil_mlc *mlc = &hp_sdc_mlc; |
66 | |
67 | write_lock(&mlc->lock); |
68 | if (mlc->icount < 0) { |
69 | printk(KERN_WARNING PREFIX "HIL Overflow!\n" ); |
70 | up(sem: &mlc->isem); |
71 | goto out; |
72 | } |
73 | idx = 15 - mlc->icount; |
74 | if ((status & HP_SDC_STATUS_IRQMASK) == HP_SDC_STATUS_HILDATA) { |
75 | mlc->ipacket[idx] |= data | HIL_ERR_INT; |
76 | mlc->icount--; |
77 | if (hp_sdc_mlc_priv.got5x || !idx) |
78 | goto check; |
79 | if ((mlc->ipacket[idx - 1] & HIL_PKT_ADDR_MASK) != |
80 | (mlc->ipacket[idx] & HIL_PKT_ADDR_MASK)) { |
81 | mlc->ipacket[idx] &= ~HIL_PKT_ADDR_MASK; |
82 | mlc->ipacket[idx] |= (mlc->ipacket[idx - 1] |
83 | & HIL_PKT_ADDR_MASK); |
84 | } |
85 | goto check; |
86 | } |
87 | /* We know status is 5X */ |
88 | if (data & HP_SDC_HIL_ISERR) |
89 | goto err; |
90 | mlc->ipacket[idx] = |
91 | (data & HP_SDC_HIL_R1MASK) << HIL_PKT_ADDR_SHIFT; |
92 | hp_sdc_mlc_priv.got5x = 1; |
93 | goto out; |
94 | |
95 | check: |
96 | hp_sdc_mlc_priv.got5x = 0; |
97 | if (mlc->imatch == 0) |
98 | goto done; |
99 | if ((mlc->imatch == (HIL_ERR_INT | HIL_PKT_CMD | HIL_CMD_POL)) |
100 | && (mlc->ipacket[idx] == (mlc->imatch | idx))) |
101 | goto done; |
102 | if (mlc->ipacket[idx] == mlc->imatch) |
103 | goto done; |
104 | goto out; |
105 | |
106 | err: |
107 | printk(KERN_DEBUG PREFIX "err code %x\n" , data); |
108 | |
109 | switch (data) { |
110 | case HP_SDC_HIL_RC_DONE: |
111 | printk(KERN_WARNING PREFIX "Bastard SDC reconfigured loop!\n" ); |
112 | break; |
113 | |
114 | case HP_SDC_HIL_ERR: |
115 | mlc->ipacket[idx] |= HIL_ERR_INT | HIL_ERR_PERR | |
116 | HIL_ERR_FERR | HIL_ERR_FOF; |
117 | break; |
118 | |
119 | case HP_SDC_HIL_TO: |
120 | mlc->ipacket[idx] |= HIL_ERR_INT | HIL_ERR_LERR; |
121 | break; |
122 | |
123 | case HP_SDC_HIL_RC: |
124 | printk(KERN_WARNING PREFIX "Bastard SDC decided to reconfigure loop!\n" ); |
125 | break; |
126 | |
127 | default: |
128 | printk(KERN_WARNING PREFIX "Unknown HIL Error status (%x)!\n" , data); |
129 | break; |
130 | } |
131 | |
132 | /* No more data will be coming due to an error. */ |
133 | done: |
134 | tasklet_schedule(t: mlc->tasklet); |
135 | up(sem: &mlc->isem); |
136 | out: |
137 | write_unlock(&mlc->lock); |
138 | } |
139 | |
140 | |
141 | /******************** Tasklet or userspace context functions ****************/ |
142 | |
143 | static int hp_sdc_mlc_in(hil_mlc *mlc, suseconds_t timeout) |
144 | { |
145 | struct hp_sdc_mlc_priv_s *priv; |
146 | int rc = 2; |
147 | |
148 | priv = mlc->priv; |
149 | |
150 | /* Try to down the semaphore */ |
151 | if (down_trylock(sem: &mlc->isem)) { |
152 | if (priv->emtestmode) { |
153 | mlc->ipacket[0] = |
154 | HIL_ERR_INT | (mlc->opacket & |
155 | (HIL_PKT_CMD | |
156 | HIL_PKT_ADDR_MASK | |
157 | HIL_PKT_DATA_MASK)); |
158 | mlc->icount = 14; |
159 | /* printk(KERN_DEBUG PREFIX ">[%x]\n", mlc->ipacket[0]); */ |
160 | goto wasup; |
161 | } |
162 | if (time_after(jiffies, mlc->instart + mlc->intimeout)) { |
163 | /* printk("!%i %i", |
164 | tv.tv_usec - mlc->instart.tv_usec, |
165 | mlc->intimeout); |
166 | */ |
167 | rc = 1; |
168 | up(sem: &mlc->isem); |
169 | } |
170 | goto done; |
171 | } |
172 | wasup: |
173 | up(sem: &mlc->isem); |
174 | rc = 0; |
175 | done: |
176 | return rc; |
177 | } |
178 | |
179 | static int hp_sdc_mlc_cts(hil_mlc *mlc) |
180 | { |
181 | struct hp_sdc_mlc_priv_s *priv; |
182 | |
183 | priv = mlc->priv; |
184 | |
185 | /* Try to down the semaphores -- they should be up. */ |
186 | BUG_ON(down_trylock(&mlc->isem)); |
187 | BUG_ON(down_trylock(&mlc->osem)); |
188 | |
189 | up(sem: &mlc->isem); |
190 | up(sem: &mlc->osem); |
191 | |
192 | if (down_trylock(sem: &mlc->csem)) { |
193 | if (priv->trans.act.semaphore != &mlc->csem) |
194 | goto poll; |
195 | else |
196 | goto busy; |
197 | } |
198 | |
199 | if (!(priv->tseq[4] & HP_SDC_USE_LOOP)) |
200 | goto done; |
201 | |
202 | poll: |
203 | priv->trans.act.semaphore = &mlc->csem; |
204 | priv->trans.actidx = 0; |
205 | priv->trans.idx = 1; |
206 | priv->trans.endidx = 5; |
207 | priv->tseq[0] = |
208 | HP_SDC_ACT_POSTCMD | HP_SDC_ACT_DATAIN | HP_SDC_ACT_SEMAPHORE; |
209 | priv->tseq[1] = HP_SDC_CMD_READ_USE; |
210 | priv->tseq[2] = 1; |
211 | priv->tseq[3] = 0; |
212 | priv->tseq[4] = 0; |
213 | return __hp_sdc_enqueue_transaction(this: &priv->trans); |
214 | busy: |
215 | return 1; |
216 | done: |
217 | priv->trans.act.semaphore = &mlc->osem; |
218 | up(sem: &mlc->csem); |
219 | return 0; |
220 | } |
221 | |
222 | static int hp_sdc_mlc_out(hil_mlc *mlc) |
223 | { |
224 | struct hp_sdc_mlc_priv_s *priv; |
225 | |
226 | priv = mlc->priv; |
227 | |
228 | /* Try to down the semaphore -- it should be up. */ |
229 | BUG_ON(down_trylock(&mlc->osem)); |
230 | |
231 | if (mlc->opacket & HIL_DO_ALTER_CTRL) |
232 | goto do_control; |
233 | |
234 | do_data: |
235 | if (priv->emtestmode) { |
236 | up(sem: &mlc->osem); |
237 | return 0; |
238 | } |
239 | /* Shouldn't be sending commands when loop may be busy */ |
240 | BUG_ON(down_trylock(&mlc->csem)); |
241 | up(sem: &mlc->csem); |
242 | |
243 | priv->trans.actidx = 0; |
244 | priv->trans.idx = 1; |
245 | priv->trans.act.semaphore = &mlc->osem; |
246 | priv->trans.endidx = 6; |
247 | priv->tseq[0] = |
248 | HP_SDC_ACT_DATAREG | HP_SDC_ACT_POSTCMD | HP_SDC_ACT_SEMAPHORE; |
249 | priv->tseq[1] = 0x7; |
250 | priv->tseq[2] = |
251 | (mlc->opacket & |
252 | (HIL_PKT_ADDR_MASK | HIL_PKT_CMD)) |
253 | >> HIL_PKT_ADDR_SHIFT; |
254 | priv->tseq[3] = |
255 | (mlc->opacket & HIL_PKT_DATA_MASK) |
256 | >> HIL_PKT_DATA_SHIFT; |
257 | priv->tseq[4] = 0; /* No timeout */ |
258 | if (priv->tseq[3] == HIL_CMD_DHR) |
259 | priv->tseq[4] = 1; |
260 | priv->tseq[5] = HP_SDC_CMD_DO_HIL; |
261 | goto enqueue; |
262 | |
263 | do_control: |
264 | priv->emtestmode = mlc->opacket & HIL_CTRL_TEST; |
265 | |
266 | /* we cannot emulate this, it should not be used. */ |
267 | BUG_ON((mlc->opacket & (HIL_CTRL_APE | HIL_CTRL_IPF)) == HIL_CTRL_APE); |
268 | |
269 | if ((mlc->opacket & HIL_CTRL_ONLY) == HIL_CTRL_ONLY) |
270 | goto control_only; |
271 | |
272 | /* Should not send command/data after engaging APE */ |
273 | BUG_ON(mlc->opacket & HIL_CTRL_APE); |
274 | |
275 | /* Disengaging APE this way would not be valid either since |
276 | * the loop must be allowed to idle. |
277 | * |
278 | * So, it works out that we really never actually send control |
279 | * and data when using SDC, we just send the data. |
280 | */ |
281 | goto do_data; |
282 | |
283 | control_only: |
284 | priv->trans.actidx = 0; |
285 | priv->trans.idx = 1; |
286 | priv->trans.act.semaphore = &mlc->osem; |
287 | priv->trans.endidx = 4; |
288 | priv->tseq[0] = |
289 | HP_SDC_ACT_PRECMD | HP_SDC_ACT_DATAOUT | HP_SDC_ACT_SEMAPHORE; |
290 | priv->tseq[1] = HP_SDC_CMD_SET_LPC; |
291 | priv->tseq[2] = 1; |
292 | /* priv->tseq[3] = (mlc->ddc + 1) | HP_SDC_LPS_ACSUCC; */ |
293 | priv->tseq[3] = 0; |
294 | if (mlc->opacket & HIL_CTRL_APE) { |
295 | priv->tseq[3] |= HP_SDC_LPC_APE_IPF; |
296 | BUG_ON(down_trylock(&mlc->csem)); |
297 | } |
298 | enqueue: |
299 | return hp_sdc_enqueue_transaction(this: &priv->trans); |
300 | } |
301 | |
302 | static int __init hp_sdc_mlc_init(void) |
303 | { |
304 | hil_mlc *mlc = &hp_sdc_mlc; |
305 | int err; |
306 | |
307 | #ifdef __mc68000__ |
308 | if (!MACH_IS_HP300) |
309 | return -ENODEV; |
310 | #endif |
311 | |
312 | printk(KERN_INFO PREFIX "Registering the System Domain Controller's HIL MLC.\n" ); |
313 | |
314 | hp_sdc_mlc_priv.emtestmode = 0; |
315 | hp_sdc_mlc_priv.trans.seq = hp_sdc_mlc_priv.tseq; |
316 | hp_sdc_mlc_priv.trans.act.semaphore = &mlc->osem; |
317 | hp_sdc_mlc_priv.got5x = 0; |
318 | |
319 | mlc->cts = &hp_sdc_mlc_cts; |
320 | mlc->in = &hp_sdc_mlc_in; |
321 | mlc->out = &hp_sdc_mlc_out; |
322 | mlc->priv = &hp_sdc_mlc_priv; |
323 | |
324 | err = hil_mlc_register(mlc); |
325 | if (err) { |
326 | printk(KERN_WARNING PREFIX "Failed to register MLC structure with hil_mlc\n" ); |
327 | return err; |
328 | } |
329 | |
330 | if (hp_sdc_request_hil_irq(callback: &hp_sdc_mlc_isr)) { |
331 | printk(KERN_WARNING PREFIX "Request for raw HIL ISR hook denied\n" ); |
332 | if (hil_mlc_unregister(mlc)) |
333 | printk(KERN_ERR PREFIX "Failed to unregister MLC structure with hil_mlc.\n" |
334 | "This is bad. Could cause an oops.\n" ); |
335 | return -EBUSY; |
336 | } |
337 | |
338 | return 0; |
339 | } |
340 | |
341 | static void __exit hp_sdc_mlc_exit(void) |
342 | { |
343 | hil_mlc *mlc = &hp_sdc_mlc; |
344 | |
345 | if (hp_sdc_release_hil_irq(callback: &hp_sdc_mlc_isr)) |
346 | printk(KERN_ERR PREFIX "Failed to release the raw HIL ISR hook.\n" |
347 | "This is bad. Could cause an oops.\n" ); |
348 | |
349 | if (hil_mlc_unregister(mlc)) |
350 | printk(KERN_ERR PREFIX "Failed to unregister MLC structure with hil_mlc.\n" |
351 | "This is bad. Could cause an oops.\n" ); |
352 | } |
353 | |
354 | module_init(hp_sdc_mlc_init); |
355 | module_exit(hp_sdc_mlc_exit); |
356 | |