xref: /linux/drivers/gpu/drm/i915/vlv_iosf_sb.c (revision 8d2b0853add1d7534dc0794e3c8e0b9e8c4ec640)
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_iosf_sb.h"
10 
11 /*
12  * IOSF sideband, see VLV2_SidebandMsg_HAS.docx and
13  * VLV_VLV2_PUNIT_HAS_0.8.docx
14  */
15 
16 /* Standard MMIO read, non-posted */
17 #define SB_MRD_NP	0x00
18 /* Standard MMIO write, non-posted */
19 #define SB_MWR_NP	0x01
20 /* Private register read, double-word addressing, non-posted */
21 #define SB_CRRDDA_NP	0x06
22 /* Private register write, double-word addressing, non-posted */
23 #define SB_CRWRDA_NP	0x07
24 
25 static void ping(void *info)
26 {
27 }
28 
29 static void __vlv_punit_get(struct drm_i915_private *i915)
30 {
31 	iosf_mbi_punit_acquire();
32 
33 	/*
34 	 * Prevent the cpu from sleeping while we use this sideband, otherwise
35 	 * the punit may cause a machine hang. The issue appears to be isolated
36 	 * with changing the power state of the CPU package while changing
37 	 * the power state via the punit, and we have only observed it
38 	 * reliably on 4-core Baytail systems suggesting the issue is in the
39 	 * power delivery mechanism and likely to be board/function
40 	 * specific. Hence we presume the workaround needs only be applied
41 	 * to the Valleyview P-unit and not all sideband communications.
42 	 */
43 	if (IS_VALLEYVIEW(i915)) {
44 		cpu_latency_qos_update_request(&i915->vlv_iosf_sb.qos, 0);
45 		on_each_cpu(ping, NULL, 1);
46 	}
47 }
48 
49 static void __vlv_punit_put(struct drm_i915_private *i915)
50 {
51 	if (IS_VALLEYVIEW(i915))
52 		cpu_latency_qos_update_request(&i915->vlv_iosf_sb.qos,
53 					       PM_QOS_DEFAULT_VALUE);
54 
55 	iosf_mbi_punit_release();
56 }
57 
58 void vlv_iosf_sb_get(struct drm_device *drm, unsigned long unit_mask)
59 {
60 	struct drm_i915_private *i915 = to_i915(drm);
61 
62 	if (unit_mask & BIT(VLV_IOSF_SB_PUNIT))
63 		__vlv_punit_get(i915);
64 
65 	mutex_lock(&i915->vlv_iosf_sb.lock);
66 
67 	i915->vlv_iosf_sb.locked_unit_mask |= unit_mask;
68 }
69 
70 void vlv_iosf_sb_put(struct drm_device *drm, unsigned long unit_mask)
71 {
72 	struct drm_i915_private *i915 = to_i915(drm);
73 
74 	i915->vlv_iosf_sb.locked_unit_mask &= ~unit_mask;
75 
76 	drm_WARN_ON(drm, i915->vlv_iosf_sb.locked_unit_mask);
77 
78 	mutex_unlock(&i915->vlv_iosf_sb.lock);
79 
80 	if (unit_mask & BIT(VLV_IOSF_SB_PUNIT))
81 		__vlv_punit_put(i915);
82 }
83 
84 static int vlv_sideband_rw(struct drm_i915_private *i915,
85 			   u32 devfn, u32 port, u32 opcode,
86 			   u32 addr, u32 *val)
87 {
88 	struct intel_uncore *uncore = &i915->uncore;
89 	const bool is_read = (opcode == SB_MRD_NP || opcode == SB_CRRDDA_NP);
90 	int err;
91 
92 	lockdep_assert_held(&i915->vlv_iosf_sb.lock);
93 	if (port == IOSF_PORT_PUNIT)
94 		iosf_mbi_assert_punit_acquired();
95 
96 	/* Flush the previous comms, just in case it failed last time. */
97 	if (intel_wait_for_register(uncore,
98 				    VLV_IOSF_DOORBELL_REQ, IOSF_SB_BUSY, 0,
99 				    5)) {
100 		drm_dbg(&i915->drm, "IOSF sideband idle wait (%s) timed out\n",
101 			is_read ? "read" : "write");
102 		return -EAGAIN;
103 	}
104 
105 	preempt_disable();
106 
107 	intel_uncore_write_fw(uncore, VLV_IOSF_ADDR, addr);
108 	intel_uncore_write_fw(uncore, VLV_IOSF_DATA, is_read ? 0 : *val);
109 	intel_uncore_write_fw(uncore, VLV_IOSF_DOORBELL_REQ,
110 			      (devfn << IOSF_DEVFN_SHIFT) |
111 			      (opcode << IOSF_OPCODE_SHIFT) |
112 			      (port << IOSF_PORT_SHIFT) |
113 			      (0xf << IOSF_BYTE_ENABLES_SHIFT) |
114 			      (0 << IOSF_BAR_SHIFT) |
115 			      IOSF_SB_BUSY);
116 
117 	if (__intel_wait_for_register_fw(uncore,
118 					 VLV_IOSF_DOORBELL_REQ, IOSF_SB_BUSY, 0,
119 					 10000, 0, NULL) == 0) {
120 		if (is_read)
121 			*val = intel_uncore_read_fw(uncore, VLV_IOSF_DATA);
122 		err = 0;
123 	} else {
124 		drm_dbg(&i915->drm, "IOSF sideband finish wait (%s) timed out\n",
125 			is_read ? "read" : "write");
126 		err = -ETIMEDOUT;
127 	}
128 
129 	preempt_enable();
130 
131 	return err;
132 }
133 
134 static u32 unit_to_devfn(enum vlv_iosf_sb_unit unit)
135 {
136 	if (unit == VLV_IOSF_SB_DPIO || unit == VLV_IOSF_SB_DPIO_2 ||
137 	    unit == VLV_IOSF_SB_FLISDSI)
138 		return DPIO_DEVFN;
139 	else
140 		return PCI_DEVFN(0, 0);
141 }
142 
143 static u32 unit_to_port(enum vlv_iosf_sb_unit unit)
144 {
145 	switch (unit) {
146 	case VLV_IOSF_SB_BUNIT:
147 		return IOSF_PORT_BUNIT;
148 	case VLV_IOSF_SB_CCK:
149 		return IOSF_PORT_CCK;
150 	case VLV_IOSF_SB_CCU:
151 		return IOSF_PORT_CCU;
152 	case VLV_IOSF_SB_DPIO:
153 		return IOSF_PORT_DPIO;
154 	case VLV_IOSF_SB_DPIO_2:
155 		return IOSF_PORT_DPIO_2;
156 	case VLV_IOSF_SB_FLISDSI:
157 		return IOSF_PORT_FLISDSI;
158 	case VLV_IOSF_SB_GPIO:
159 		return 0; /* FIXME: unused */
160 	case VLV_IOSF_SB_NC:
161 		return IOSF_PORT_NC;
162 	case VLV_IOSF_SB_PUNIT:
163 		return IOSF_PORT_PUNIT;
164 	default:
165 		return 0;
166 	}
167 }
168 
169 static u32 unit_to_opcode(enum vlv_iosf_sb_unit unit, bool write)
170 {
171 	if (unit == VLV_IOSF_SB_DPIO || unit == VLV_IOSF_SB_DPIO_2)
172 		return write ? SB_MWR_NP : SB_MRD_NP;
173 	else
174 		return write ? SB_CRWRDA_NP : SB_CRRDDA_NP;
175 }
176 
177 u32 vlv_iosf_sb_read(struct drm_device *drm, enum vlv_iosf_sb_unit unit, u32 addr)
178 {
179 	struct drm_i915_private *i915 = to_i915(drm);
180 	u32 devfn, port, opcode, val = 0;
181 
182 	devfn = unit_to_devfn(unit);
183 	port = unit_to_port(unit);
184 	opcode = unit_to_opcode(unit, false);
185 
186 	if (drm_WARN_ONCE(&i915->drm, !port, "invalid unit %d\n", unit))
187 		return 0;
188 
189 	drm_WARN_ON(&i915->drm, !(i915->vlv_iosf_sb.locked_unit_mask & BIT(unit)));
190 
191 	vlv_sideband_rw(i915, devfn, port, opcode, addr, &val);
192 
193 	return val;
194 }
195 
196 int vlv_iosf_sb_write(struct drm_device *drm, enum vlv_iosf_sb_unit unit, u32 addr, u32 val)
197 {
198 	struct drm_i915_private *i915 = to_i915(drm);
199 	u32 devfn, port, opcode;
200 
201 	devfn = unit_to_devfn(unit);
202 	port = unit_to_port(unit);
203 	opcode = unit_to_opcode(unit, true);
204 
205 	if (drm_WARN_ONCE(&i915->drm, !port, "invalid unit %d\n", unit))
206 		return -EINVAL;
207 
208 	drm_WARN_ON(&i915->drm, !(i915->vlv_iosf_sb.locked_unit_mask & BIT(unit)));
209 
210 	return vlv_sideband_rw(i915, devfn, port, opcode, addr, &val);
211 }
212 
213 void vlv_iosf_sb_init(struct drm_i915_private *i915)
214 {
215 	if (IS_VALLEYVIEW(i915) || IS_CHERRYVIEW(i915))
216 		mutex_init(&i915->vlv_iosf_sb.lock);
217 
218 	if (IS_VALLEYVIEW(i915))
219 		cpu_latency_qos_add_request(&i915->vlv_iosf_sb.qos, PM_QOS_DEFAULT_VALUE);
220 }
221 
222 void vlv_iosf_sb_fini(struct drm_i915_private *i915)
223 {
224 	if (IS_VALLEYVIEW(i915))
225 		cpu_latency_qos_remove_request(&i915->vlv_iosf_sb.qos);
226 
227 	if (IS_VALLEYVIEW(i915) || IS_CHERRYVIEW(i915))
228 		mutex_destroy(&i915->vlv_iosf_sb.lock);
229 }
230