Branch data Line data Source code
1 : : // SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
2 : : /*
3 : : * op_display() but over the 1 byte LPC port 80h just like an original IBM PC
4 : : *
5 : : * Copyright 2018-2019 IBM Corp.
6 : : */
7 : :
8 : : #define pr_fmt(fmt) "Port80h: " fmt
9 : :
10 : : #include <opal-api.h>
11 : : #include <lpc.h>
12 : : #include <op-panel.h>
13 : : #include <chip.h>
14 : :
15 : : /*
16 : : * Convert our detailed op_display() call into 1 byte for LPC port 80h
17 : : *
18 : : * Our layout looks like this:
19 : : * MSB (bit 7): 1 = Comes from OPAL
20 : : * bit 6 : 0 = OP_MOD_INIT (the main one), 1 = (see bit 5)
21 : : * bit 5432 : (if bit 6=0, low nibble of op-panel code)
22 : : * bit 5432 : (if bit 6=1, other OP_MOD_ values in bits 54:
23 : : * 00b=OP_MOD_CPU, 01b=OP_MOD_LOCK,
24 : : * 10b=OP_MOD_MEM, 11b=OP_MOD_CHIPTOD
25 : : * bits 0,1 from code in bits 32)
26 : : *
27 : : * bit 1,0: 00b=OP_LOG, 10b=OP_WARN, 01b=OP_ERROR, 11b=OP_FATAL
28 : : * i.e. bit 0 indicates ERROR or FATAL.
29 : : *
30 : : * If port 80h number has the MSB and LSB set, then you died in OPAL.
31 : : * Any *odd* number with the MSB set (i.e. > 0x80) indicates error.
32 : : */
33 : 16 : static inline uint8_t op_display_to_port80(uint8_t last_value, enum op_severity s, enum op_module m, uint16_t c)
34 : : {
35 : 16 : uint8_t r = 0x80; /* Start with top bit set indicating in OPAL */
36 : :
37 : 16 : switch(m) {
38 : 7 : case OP_MOD_INIT:
39 : : /* bit 6 is zero */
40 : : /* bits 5432 have low nibble of c */
41 : 7 : r |= (c & 0x0f) << 2;
42 : 7 : break;
43 : 1 : case OP_MOD_CPU:
44 : 1 : r |= 0x40 | (c & 0x03) << 2;
45 : 1 : break;
46 : 2 : case OP_MOD_LOCK:
47 : 2 : r |= 0x50 | (c & 0x03) << 2;
48 : 2 : break;
49 : 2 : case OP_MOD_MEM:
50 : 2 : r |= 0x60 | (c & 0x03) << 2;
51 : 2 : break;
52 : 1 : case OP_MOD_CHIPTOD:
53 : 1 : r |= 0x70 | (c & 0x03) << 2;
54 : 1 : break;
55 : 1 : case OP_MOD_CORE:
56 : : /*
57 : : * Only current OP_MOD_CORE is where we're OP_FATAL,
58 : : * So let's go for the last value set and tweak the
59 : : * bits for OP_FATAL.
60 : : */
61 : 1 : r = last_value & 0xFC;
62 : 1 : break;
63 : 2 : case OP_MOD_FSP:
64 : : case OP_MOD_FSPCON:
65 : : /* Should never be hit, port80h only used on non-FSP! */
66 : 2 : break;
67 : : }
68 : :
69 : 16 : switch(s) {
70 : 7 : case OP_LOG:
71 : 7 : break;
72 : 3 : case OP_WARN:
73 : 3 : r |= 0x02;
74 : 3 : break;
75 : 1 : case OP_ERROR:
76 : 1 : r |= 0x01;
77 : 1 : break;
78 : 5 : case OP_FATAL:
79 : 5 : r |= 0x03;
80 : : }
81 : :
82 : 16 : return r;
83 : : }
84 : :
85 : : /*
86 : : * Convert our detailed op_display() call into 2 bytes for LPC port 81h and 82h
87 : : *
88 : : * This looks pretty similar to our port80 code.
89 : : * Notably we now have more bits to throw progress into.
90 : : *
91 : : * Our layout looks like this:
92 : : * MSB (bit 15): 1 = Comes from OPAL
93 : : * bit 14 : 0 = OP_MOD_INIT (the main one), 1 = (see bit 13)
94 : : * bits 13-2 : (if bit 6=0, low 12 bits of op-panel code)
95 : : * bit 13,12 : (if bit 6=1, other OP_MOD_ values in bits 13 and 12:
96 : : * 00b=OP_MOD_CPU, 01b=OP_MOD_LOCK,
97 : : * 10b=OP_MOD_MEM, 11b=OP_MOD_CHIPTOD)
98 : : * and bits 11-2 are low 10 bits of op-panel code)
99 : : *
100 : : * bit 1,0: 00b=OP_LOG, 10b=OP_WARN, 01b=OP_ERROR, 11b=OP_FATAL
101 : : * i.e. bit 0 indicates ERROR or FATAL.
102 : : *
103 : : * If port 80h number has the MSB and LSB set, then you died in OPAL.
104 : : * Any *odd* number with the MSB set (i.e. > 0x80) indicates error.
105 : : */
106 : 16 : static inline uint16_t op_display_to_port8x(uint16_t last_value, enum op_severity s, enum op_module m, uint16_t c)
107 : : {
108 : 16 : uint16_t r = 0x8000; /* Start with top bit set indicating in OPAL */
109 : :
110 : 16 : switch(m) {
111 : 7 : case OP_MOD_INIT:
112 : : /* bit 6 is zero */
113 : : /* bits 13 through 2 have low 12 bits of c */
114 : 7 : r |= (c & 0xFFF) << 2;
115 : 7 : break;
116 : 1 : case OP_MOD_CPU:
117 : 1 : r |= 0x4000 | (c & 0x03FF) << 2;
118 : 1 : break;
119 : 2 : case OP_MOD_LOCK:
120 : 2 : r |= 0x5000 | (c & 0x03FF) << 2;
121 : 2 : break;
122 : 2 : case OP_MOD_MEM:
123 : 2 : r |= 0x6000 | (c & 0x03FF) << 2;
124 : 2 : break;
125 : 1 : case OP_MOD_CHIPTOD:
126 : 1 : r |= 0x7000 | (c & 0x03FF) << 2;
127 : 1 : break;
128 : 1 : case OP_MOD_CORE:
129 : : /*
130 : : * Only current OP_MOD_CORE is where we're OP_FATAL,
131 : : * So let's go for the last value set and tweak the
132 : : * bits for OP_FATAL.
133 : : */
134 : 1 : r = last_value & 0xFFFC;
135 : 1 : break;
136 : 2 : case OP_MOD_FSP:
137 : : case OP_MOD_FSPCON:
138 : : /* Should never be hit, port80h only used on non-FSP! */
139 : 2 : break;
140 : : }
141 : :
142 : 16 : switch(s) {
143 : 7 : case OP_LOG:
144 : 7 : break;
145 : 3 : case OP_WARN:
146 : 3 : r |= 0x02;
147 : 3 : break;
148 : 1 : case OP_ERROR:
149 : 1 : r |= 0x01;
150 : 1 : break;
151 : 5 : case OP_FATAL:
152 : 5 : r |= 0x03;
153 : : }
154 : :
155 : 16 : return r;
156 : : }
157 : :
158 : :
159 : 16 : void op_display_lpc(enum op_severity s, enum op_module m, uint16_t c)
160 : : {
161 : : static uint8_t port80_val = 0x80;
162 : : static uint16_t port8x_val = 0x8000;
163 : :
164 : 16 : if (chip_quirk(QUIRK_SIMICS))
165 : 0 : return;
166 : :
167 : 16 : port80_val = op_display_to_port80(port80_val, s, m, c);
168 : 16 : port8x_val = op_display_to_port8x(port8x_val, s, m, c);
169 : :
170 : 16 : lpc_probe_write(OPAL_LPC_IO, 0x80, port80_val, 1);
171 : 16 : lpc_probe_write(OPAL_LPC_IO, 0x81, port8x_val >> 8, 1);
172 : 16 : lpc_probe_write(OPAL_LPC_IO, 0x82, port8x_val & 0xff, 1);
173 : : }
174 : :
|