Commit | Line | Data |
---|---|---|
1da177e4 LT |
1 | /* Tests for presence or absence of hardware registers. |
2 | * This code was originally in atari/config.c, but I noticed | |
3 | * that it was also in drivers/nubus/nubus.c and I wanted to | |
4 | * use it in hp300/config.c, so it seemed sensible to pull it | |
5 | * out into its own file. | |
6 | * | |
7 | * The test is for use when trying to read a hardware register | |
8 | * that isn't present would cause a bus error. We set up a | |
9 | * temporary handler so that this doesn't kill the kernel. | |
10 | * | |
11 | * There is a test-by-reading and a test-by-writing; I present | |
12 | * them here complete with the comments from the original atari | |
13 | * config.c... | |
14 | * -- PMM <pmaydell@chiark.greenend.org.uk>, 05/1998 | |
15 | */ | |
16 | ||
17 | /* This function tests for the presence of an address, specially a | |
18 | * hardware register address. It is called very early in the kernel | |
19 | * initialization process, when the VBR register isn't set up yet. On | |
20 | * an Atari, it still points to address 0, which is unmapped. So a bus | |
21 | * error would cause another bus error while fetching the exception | |
22 | * vector, and the CPU would do nothing at all. So we needed to set up | |
23 | * a temporary VBR and a vector table for the duration of the test. | |
24 | */ | |
25 | ||
26 | #include <linux/module.h> | |
27 | ||
28 | int hwreg_present( volatile void *regp ) | |
29 | { | |
30 | int ret = 0; | |
31 | long save_sp, save_vbr; | |
32 | long tmp_vectors[3]; | |
33 | ||
34 | __asm__ __volatile__ | |
35 | ( "movec %/vbr,%2\n\t" | |
36 | "movel #Lberr1,%4@(8)\n\t" | |
37 | "movec %4,%/vbr\n\t" | |
38 | "movel %/sp,%1\n\t" | |
39 | "moveq #0,%0\n\t" | |
40 | "tstb %3@\n\t" | |
41 | "nop\n\t" | |
42 | "moveq #1,%0\n" | |
43 | "Lberr1:\n\t" | |
44 | "movel %1,%/sp\n\t" | |
45 | "movec %2,%/vbr" | |
46 | : "=&d" (ret), "=&r" (save_sp), "=&r" (save_vbr) | |
47 | : "a" (regp), "a" (tmp_vectors) | |
48 | ); | |
49 | ||
50 | return( ret ); | |
51 | } | |
52 | EXPORT_SYMBOL(hwreg_present); | |
53 | ||
54 | /* Basically the same, but writes a value into a word register, protected | |
55 | * by a bus error handler. Returns 1 if successful, 0 otherwise. | |
56 | */ | |
57 | ||
58 | int hwreg_write( volatile void *regp, unsigned short val ) | |
59 | { | |
60 | int ret; | |
61 | long save_sp, save_vbr; | |
62 | long tmp_vectors[3]; | |
63 | ||
64 | __asm__ __volatile__ | |
65 | ( "movec %/vbr,%2\n\t" | |
66 | "movel #Lberr2,%4@(8)\n\t" | |
67 | "movec %4,%/vbr\n\t" | |
68 | "movel %/sp,%1\n\t" | |
69 | "moveq #0,%0\n\t" | |
70 | "movew %5,%3@\n\t" | |
71 | "nop \n\t" /* If this nop isn't present, 'ret' may already be | |
72 | * loaded with 1 at the time the bus error | |
73 | * happens! */ | |
74 | "moveq #1,%0\n" | |
75 | "Lberr2:\n\t" | |
76 | "movel %1,%/sp\n\t" | |
77 | "movec %2,%/vbr" | |
78 | : "=&d" (ret), "=&r" (save_sp), "=&r" (save_vbr) | |
79 | : "a" (regp), "a" (tmp_vectors), "g" (val) | |
80 | ); | |
81 | ||
82 | return( ret ); | |
83 | } | |
84 | EXPORT_SYMBOL(hwreg_write); | |
85 |