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