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