1 | // SPDX-License-Identifier: MIT |
2 | /* |
3 | * Copyright © 2013-2021 Intel Corporation |
4 | */ |
5 | |
6 | #include "i915_drv.h" |
7 | #include "i915_iosf_mbi.h" |
8 | #include "i915_reg.h" |
9 | #include "vlv_sideband.h" |
10 | |
11 | #include "display/intel_dpio_phy.h" |
12 | #include "display/intel_display_types.h" |
13 | |
14 | /* |
15 | * IOSF sideband, see VLV2_SidebandMsg_HAS.docx and |
16 | * VLV_VLV2_PUNIT_HAS_0.8.docx |
17 | */ |
18 | |
19 | /* Standard MMIO read, non-posted */ |
20 | #define SB_MRD_NP 0x00 |
21 | /* Standard MMIO write, non-posted */ |
22 | #define SB_MWR_NP 0x01 |
23 | /* Private register read, double-word addressing, non-posted */ |
24 | #define SB_CRRDDA_NP 0x06 |
25 | /* Private register write, double-word addressing, non-posted */ |
26 | #define SB_CRWRDA_NP 0x07 |
27 | |
28 | static void ping(void *info) |
29 | { |
30 | } |
31 | |
32 | static void __vlv_punit_get(struct drm_i915_private *i915) |
33 | { |
34 | iosf_mbi_punit_acquire(); |
35 | |
36 | /* |
37 | * Prevent the cpu from sleeping while we use this sideband, otherwise |
38 | * the punit may cause a machine hang. The issue appears to be isolated |
39 | * with changing the power state of the CPU package while changing |
40 | * the power state via the punit, and we have only observed it |
41 | * reliably on 4-core Baytail systems suggesting the issue is in the |
42 | * power delivery mechanism and likely to be board/function |
43 | * specific. Hence we presume the workaround needs only be applied |
44 | * to the Valleyview P-unit and not all sideband communications. |
45 | */ |
46 | if (IS_VALLEYVIEW(i915)) { |
47 | cpu_latency_qos_update_request(req: &i915->sb_qos, new_value: 0); |
48 | on_each_cpu(func: ping, NULL, wait: 1); |
49 | } |
50 | } |
51 | |
52 | static void __vlv_punit_put(struct drm_i915_private *i915) |
53 | { |
54 | if (IS_VALLEYVIEW(i915)) |
55 | cpu_latency_qos_update_request(req: &i915->sb_qos, |
56 | PM_QOS_DEFAULT_VALUE); |
57 | |
58 | iosf_mbi_punit_release(); |
59 | } |
60 | |
61 | void vlv_iosf_sb_get(struct drm_i915_private *i915, unsigned long ports) |
62 | { |
63 | if (ports & BIT(VLV_IOSF_SB_PUNIT)) |
64 | __vlv_punit_get(i915); |
65 | |
66 | mutex_lock(&i915->sb_lock); |
67 | } |
68 | |
69 | void vlv_iosf_sb_put(struct drm_i915_private *i915, unsigned long ports) |
70 | { |
71 | mutex_unlock(lock: &i915->sb_lock); |
72 | |
73 | if (ports & BIT(VLV_IOSF_SB_PUNIT)) |
74 | __vlv_punit_put(i915); |
75 | } |
76 | |
77 | static int vlv_sideband_rw(struct drm_i915_private *i915, |
78 | u32 devfn, u32 port, u32 opcode, |
79 | u32 addr, u32 *val) |
80 | { |
81 | struct intel_uncore *uncore = &i915->uncore; |
82 | const bool is_read = (opcode == SB_MRD_NP || opcode == SB_CRRDDA_NP); |
83 | int err; |
84 | |
85 | lockdep_assert_held(&i915->sb_lock); |
86 | if (port == IOSF_PORT_PUNIT) |
87 | iosf_mbi_assert_punit_acquired(); |
88 | |
89 | /* Flush the previous comms, just in case it failed last time. */ |
90 | if (intel_wait_for_register(uncore, |
91 | VLV_IOSF_DOORBELL_REQ, IOSF_SB_BUSY, value: 0, |
92 | timeout_ms: 5)) { |
93 | drm_dbg(&i915->drm, "IOSF sideband idle wait (%s) timed out\n" , |
94 | is_read ? "read" : "write" ); |
95 | return -EAGAIN; |
96 | } |
97 | |
98 | preempt_disable(); |
99 | |
100 | intel_uncore_write_fw(uncore, VLV_IOSF_ADDR, addr); |
101 | intel_uncore_write_fw(uncore, VLV_IOSF_DATA, is_read ? 0 : *val); |
102 | intel_uncore_write_fw(uncore, VLV_IOSF_DOORBELL_REQ, |
103 | (devfn << IOSF_DEVFN_SHIFT) | |
104 | (opcode << IOSF_OPCODE_SHIFT) | |
105 | (port << IOSF_PORT_SHIFT) | |
106 | (0xf << IOSF_BYTE_ENABLES_SHIFT) | |
107 | (0 << IOSF_BAR_SHIFT) | |
108 | IOSF_SB_BUSY); |
109 | |
110 | if (__intel_wait_for_register_fw(uncore, |
111 | VLV_IOSF_DOORBELL_REQ, IOSF_SB_BUSY, value: 0, |
112 | fast_timeout_us: 10000, slow_timeout_ms: 0, NULL) == 0) { |
113 | if (is_read) |
114 | *val = intel_uncore_read_fw(uncore, VLV_IOSF_DATA); |
115 | err = 0; |
116 | } else { |
117 | drm_dbg(&i915->drm, "IOSF sideband finish wait (%s) timed out\n" , |
118 | is_read ? "read" : "write" ); |
119 | err = -ETIMEDOUT; |
120 | } |
121 | |
122 | preempt_enable(); |
123 | |
124 | return err; |
125 | } |
126 | |
127 | u32 vlv_punit_read(struct drm_i915_private *i915, u32 addr) |
128 | { |
129 | u32 val = 0; |
130 | |
131 | vlv_sideband_rw(i915, PCI_DEVFN(0, 0), IOSF_PORT_PUNIT, |
132 | SB_CRRDDA_NP, addr, val: &val); |
133 | |
134 | return val; |
135 | } |
136 | |
137 | int vlv_punit_write(struct drm_i915_private *i915, u32 addr, u32 val) |
138 | { |
139 | return vlv_sideband_rw(i915, PCI_DEVFN(0, 0), IOSF_PORT_PUNIT, |
140 | SB_CRWRDA_NP, addr, val: &val); |
141 | } |
142 | |
143 | u32 vlv_bunit_read(struct drm_i915_private *i915, u32 reg) |
144 | { |
145 | u32 val = 0; |
146 | |
147 | vlv_sideband_rw(i915, PCI_DEVFN(0, 0), IOSF_PORT_BUNIT, |
148 | SB_CRRDDA_NP, addr: reg, val: &val); |
149 | |
150 | return val; |
151 | } |
152 | |
153 | void vlv_bunit_write(struct drm_i915_private *i915, u32 reg, u32 val) |
154 | { |
155 | vlv_sideband_rw(i915, PCI_DEVFN(0, 0), IOSF_PORT_BUNIT, |
156 | SB_CRWRDA_NP, addr: reg, val: &val); |
157 | } |
158 | |
159 | u32 vlv_nc_read(struct drm_i915_private *i915, u8 addr) |
160 | { |
161 | u32 val = 0; |
162 | |
163 | vlv_sideband_rw(i915, PCI_DEVFN(0, 0), IOSF_PORT_NC, |
164 | SB_CRRDDA_NP, addr, val: &val); |
165 | |
166 | return val; |
167 | } |
168 | |
169 | u32 vlv_iosf_sb_read(struct drm_i915_private *i915, u8 port, u32 reg) |
170 | { |
171 | u32 val = 0; |
172 | |
173 | vlv_sideband_rw(i915, PCI_DEVFN(0, 0), port, |
174 | SB_CRRDDA_NP, addr: reg, val: &val); |
175 | |
176 | return val; |
177 | } |
178 | |
179 | void vlv_iosf_sb_write(struct drm_i915_private *i915, |
180 | u8 port, u32 reg, u32 val) |
181 | { |
182 | vlv_sideband_rw(i915, PCI_DEVFN(0, 0), port, |
183 | SB_CRWRDA_NP, addr: reg, val: &val); |
184 | } |
185 | |
186 | u32 vlv_cck_read(struct drm_i915_private *i915, u32 reg) |
187 | { |
188 | u32 val = 0; |
189 | |
190 | vlv_sideband_rw(i915, PCI_DEVFN(0, 0), IOSF_PORT_CCK, |
191 | SB_CRRDDA_NP, addr: reg, val: &val); |
192 | |
193 | return val; |
194 | } |
195 | |
196 | void vlv_cck_write(struct drm_i915_private *i915, u32 reg, u32 val) |
197 | { |
198 | vlv_sideband_rw(i915, PCI_DEVFN(0, 0), IOSF_PORT_CCK, |
199 | SB_CRWRDA_NP, addr: reg, val: &val); |
200 | } |
201 | |
202 | u32 vlv_ccu_read(struct drm_i915_private *i915, u32 reg) |
203 | { |
204 | u32 val = 0; |
205 | |
206 | vlv_sideband_rw(i915, PCI_DEVFN(0, 0), IOSF_PORT_CCU, |
207 | SB_CRRDDA_NP, addr: reg, val: &val); |
208 | |
209 | return val; |
210 | } |
211 | |
212 | void vlv_ccu_write(struct drm_i915_private *i915, u32 reg, u32 val) |
213 | { |
214 | vlv_sideband_rw(i915, PCI_DEVFN(0, 0), IOSF_PORT_CCU, |
215 | SB_CRWRDA_NP, addr: reg, val: &val); |
216 | } |
217 | |
218 | static u32 vlv_dpio_phy_iosf_port(struct drm_i915_private *i915, enum dpio_phy phy) |
219 | { |
220 | /* |
221 | * IOSF_PORT_DPIO: VLV x2 PHY (DP/HDMI B and C), CHV x1 PHY (DP/HDMI D) |
222 | * IOSF_PORT_DPIO_2: CHV x2 PHY (DP/HDMI B and C) |
223 | */ |
224 | if (IS_CHERRYVIEW(i915)) |
225 | return phy == DPIO_PHY0 ? IOSF_PORT_DPIO_2 : IOSF_PORT_DPIO; |
226 | else |
227 | return IOSF_PORT_DPIO; |
228 | } |
229 | |
230 | u32 vlv_dpio_read(struct drm_i915_private *i915, enum pipe pipe, int reg) |
231 | { |
232 | u32 port = vlv_dpio_phy_iosf_port(i915, DPIO_PHY(pipe)); |
233 | u32 val = 0; |
234 | |
235 | vlv_sideband_rw(i915, DPIO_DEVFN, port, SB_MRD_NP, addr: reg, val: &val); |
236 | |
237 | /* |
238 | * FIXME: There might be some registers where all 1's is a valid value, |
239 | * so ideally we should check the register offset instead... |
240 | */ |
241 | drm_WARN(&i915->drm, val == 0xffffffff, |
242 | "DPIO read pipe %c reg 0x%x == 0x%x\n" , |
243 | pipe_name(pipe), reg, val); |
244 | |
245 | return val; |
246 | } |
247 | |
248 | void vlv_dpio_write(struct drm_i915_private *i915, |
249 | enum pipe pipe, int reg, u32 val) |
250 | { |
251 | u32 port = vlv_dpio_phy_iosf_port(i915, DPIO_PHY(pipe)); |
252 | |
253 | vlv_sideband_rw(i915, DPIO_DEVFN, port, SB_MWR_NP, addr: reg, val: &val); |
254 | } |
255 | |
256 | u32 vlv_flisdsi_read(struct drm_i915_private *i915, u32 reg) |
257 | { |
258 | u32 val = 0; |
259 | |
260 | vlv_sideband_rw(i915, DPIO_DEVFN, IOSF_PORT_FLISDSI, SB_CRRDDA_NP, |
261 | addr: reg, val: &val); |
262 | return val; |
263 | } |
264 | |
265 | void vlv_flisdsi_write(struct drm_i915_private *i915, u32 reg, u32 val) |
266 | { |
267 | vlv_sideband_rw(i915, DPIO_DEVFN, IOSF_PORT_FLISDSI, SB_CRWRDA_NP, |
268 | addr: reg, val: &val); |
269 | } |
270 | |