xref: /linux/drivers/gpu/drm/i915/vlv_iosf_sb.c (revision 2b109eccd80b660e58d4523e8b80ce11bc3e532e)
1 // SPDX-License-Identifier: MIT
2 /*
3  * Copyright © 2013-2021 Intel Corporation
4  */
5 
6 #include <drm/drm_print.h>
7 #include <drm/intel/display_parent_interface.h>
8 
9 #include "i915_drv.h"
10 #include "i915_iosf_mbi.h"
11 #include "i915_reg.h"
12 #include "vlv_iosf_sb.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(&i915->vlv_iosf_sb.qos, 0);
48 		on_each_cpu(ping, NULL, 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(&i915->vlv_iosf_sb.qos,
56 					       PM_QOS_DEFAULT_VALUE);
57 
58 	iosf_mbi_punit_release();
59 }
60 
61 void vlv_iosf_sb_get(struct drm_device *drm, unsigned long unit_mask)
62 {
63 	struct drm_i915_private *i915 = to_i915(drm);
64 
65 	if (unit_mask & BIT(VLV_IOSF_SB_PUNIT))
66 		__vlv_punit_get(i915);
67 
68 	mutex_lock(&i915->vlv_iosf_sb.lock);
69 
70 	i915->vlv_iosf_sb.locked_unit_mask |= unit_mask;
71 }
72 
73 void vlv_iosf_sb_put(struct drm_device *drm, unsigned long unit_mask)
74 {
75 	struct drm_i915_private *i915 = to_i915(drm);
76 
77 	i915->vlv_iosf_sb.locked_unit_mask &= ~unit_mask;
78 
79 	drm_WARN_ON(drm, i915->vlv_iosf_sb.locked_unit_mask);
80 
81 	mutex_unlock(&i915->vlv_iosf_sb.lock);
82 
83 	if (unit_mask & BIT(VLV_IOSF_SB_PUNIT))
84 		__vlv_punit_put(i915);
85 }
86 
87 static int vlv_sideband_rw(struct drm_i915_private *i915,
88 			   u32 devfn, u32 port, u32 opcode,
89 			   u32 addr, u32 *val)
90 {
91 	struct intel_uncore *uncore = &i915->uncore;
92 	const bool is_read = (opcode == SB_MRD_NP || opcode == SB_CRRDDA_NP);
93 	int err;
94 
95 	lockdep_assert_held(&i915->vlv_iosf_sb.lock);
96 	if (port == IOSF_PORT_PUNIT)
97 		iosf_mbi_assert_punit_acquired();
98 
99 	/* Flush the previous comms, just in case it failed last time. */
100 	if (intel_wait_for_register(uncore,
101 				    VLV_IOSF_DOORBELL_REQ, IOSF_SB_BUSY, 0,
102 				    5)) {
103 		drm_dbg(&i915->drm, "IOSF sideband idle wait (%s) timed out\n",
104 			is_read ? "read" : "write");
105 		return -EAGAIN;
106 	}
107 
108 	preempt_disable();
109 
110 	intel_uncore_write_fw(uncore, VLV_IOSF_ADDR, addr);
111 	intel_uncore_write_fw(uncore, VLV_IOSF_DATA, is_read ? 0 : *val);
112 	intel_uncore_write_fw(uncore, VLV_IOSF_DOORBELL_REQ,
113 			      (devfn << IOSF_DEVFN_SHIFT) |
114 			      (opcode << IOSF_OPCODE_SHIFT) |
115 			      (port << IOSF_PORT_SHIFT) |
116 			      (0xf << IOSF_BYTE_ENABLES_SHIFT) |
117 			      (0 << IOSF_BAR_SHIFT) |
118 			      IOSF_SB_BUSY);
119 
120 	if (__intel_wait_for_register_fw(uncore,
121 					 VLV_IOSF_DOORBELL_REQ, IOSF_SB_BUSY, 0,
122 					 10000, 0, NULL) == 0) {
123 		if (is_read)
124 			*val = intel_uncore_read_fw(uncore, VLV_IOSF_DATA);
125 		err = 0;
126 	} else {
127 		drm_dbg(&i915->drm, "IOSF sideband finish wait (%s) timed out\n",
128 			is_read ? "read" : "write");
129 		err = -ETIMEDOUT;
130 	}
131 
132 	preempt_enable();
133 
134 	return err;
135 }
136 
137 static u32 unit_to_devfn(enum vlv_iosf_sb_unit unit)
138 {
139 	if (unit == VLV_IOSF_SB_DPIO || unit == VLV_IOSF_SB_DPIO_2 ||
140 	    unit == VLV_IOSF_SB_FLISDSI)
141 		return DPIO_DEVFN;
142 	else
143 		return PCI_DEVFN(0, 0);
144 }
145 
146 static u32 unit_to_port(enum vlv_iosf_sb_unit unit)
147 {
148 	switch (unit) {
149 	case VLV_IOSF_SB_BUNIT:
150 		return IOSF_PORT_BUNIT;
151 	case VLV_IOSF_SB_CCK:
152 		return IOSF_PORT_CCK;
153 	case VLV_IOSF_SB_CCU:
154 		return IOSF_PORT_CCU;
155 	case VLV_IOSF_SB_DPIO:
156 		return IOSF_PORT_DPIO;
157 	case VLV_IOSF_SB_DPIO_2:
158 		return IOSF_PORT_DPIO_2;
159 	case VLV_IOSF_SB_FLISDSI:
160 		return IOSF_PORT_FLISDSI;
161 	case VLV_IOSF_SB_GPIO:
162 		return 0; /* FIXME: unused */
163 	case VLV_IOSF_SB_NC:
164 		return IOSF_PORT_NC;
165 	case VLV_IOSF_SB_PUNIT:
166 		return IOSF_PORT_PUNIT;
167 	default:
168 		return 0;
169 	}
170 }
171 
172 static u32 unit_to_opcode(enum vlv_iosf_sb_unit unit, bool write)
173 {
174 	if (unit == VLV_IOSF_SB_DPIO || unit == VLV_IOSF_SB_DPIO_2)
175 		return write ? SB_MWR_NP : SB_MRD_NP;
176 	else
177 		return write ? SB_CRWRDA_NP : SB_CRRDDA_NP;
178 }
179 
180 u32 vlv_iosf_sb_read(struct drm_device *drm, enum vlv_iosf_sb_unit unit, u32 addr)
181 {
182 	struct drm_i915_private *i915 = to_i915(drm);
183 	u32 devfn, port, opcode, val = 0;
184 
185 	devfn = unit_to_devfn(unit);
186 	port = unit_to_port(unit);
187 	opcode = unit_to_opcode(unit, false);
188 
189 	if (drm_WARN_ONCE(&i915->drm, !port, "invalid unit %d\n", unit))
190 		return 0;
191 
192 	drm_WARN_ON(&i915->drm, !(i915->vlv_iosf_sb.locked_unit_mask & BIT(unit)));
193 
194 	vlv_sideband_rw(i915, devfn, port, opcode, addr, &val);
195 
196 	return val;
197 }
198 
199 int vlv_iosf_sb_write(struct drm_device *drm, enum vlv_iosf_sb_unit unit, u32 addr, u32 val)
200 {
201 	struct drm_i915_private *i915 = to_i915(drm);
202 	u32 devfn, port, opcode;
203 
204 	devfn = unit_to_devfn(unit);
205 	port = unit_to_port(unit);
206 	opcode = unit_to_opcode(unit, true);
207 
208 	if (drm_WARN_ONCE(&i915->drm, !port, "invalid unit %d\n", unit))
209 		return -EINVAL;
210 
211 	drm_WARN_ON(&i915->drm, !(i915->vlv_iosf_sb.locked_unit_mask & BIT(unit)));
212 
213 	return vlv_sideband_rw(i915, devfn, port, opcode, addr, &val);
214 }
215 
216 void vlv_iosf_sb_init(struct drm_i915_private *i915)
217 {
218 	if (IS_VALLEYVIEW(i915) || IS_CHERRYVIEW(i915))
219 		mutex_init(&i915->vlv_iosf_sb.lock);
220 
221 	if (IS_VALLEYVIEW(i915))
222 		cpu_latency_qos_add_request(&i915->vlv_iosf_sb.qos, PM_QOS_DEFAULT_VALUE);
223 }
224 
225 void vlv_iosf_sb_fini(struct drm_i915_private *i915)
226 {
227 	if (IS_VALLEYVIEW(i915))
228 		cpu_latency_qos_remove_request(&i915->vlv_iosf_sb.qos);
229 
230 	if (IS_VALLEYVIEW(i915) || IS_CHERRYVIEW(i915))
231 		mutex_destroy(&i915->vlv_iosf_sb.lock);
232 }
233 
234 const struct intel_display_vlv_iosf_interface i915_display_vlv_iosf_interface = {
235 	.get = vlv_iosf_sb_get,
236 	.put = vlv_iosf_sb_put,
237 	.read = vlv_iosf_sb_read,
238 	.write = vlv_iosf_sb_write,
239 };
240