16819b5a6SJani Nikula // SPDX-License-Identifier: MIT 26819b5a6SJani Nikula /* 36819b5a6SJani Nikula * Copyright © 2013-2021 Intel Corporation 46819b5a6SJani Nikula */ 56819b5a6SJani Nikula 66819b5a6SJani Nikula #include "i915_drv.h" 76819b5a6SJani Nikula #include "i915_iosf_mbi.h" 86819b5a6SJani Nikula #include "i915_reg.h" 96819b5a6SJani Nikula #include "vlv_iosf_sb.h" 106819b5a6SJani Nikula 116819b5a6SJani Nikula /* 126819b5a6SJani Nikula * IOSF sideband, see VLV2_SidebandMsg_HAS.docx and 136819b5a6SJani Nikula * VLV_VLV2_PUNIT_HAS_0.8.docx 146819b5a6SJani Nikula */ 156819b5a6SJani Nikula 166819b5a6SJani Nikula /* Standard MMIO read, non-posted */ 176819b5a6SJani Nikula #define SB_MRD_NP 0x00 186819b5a6SJani Nikula /* Standard MMIO write, non-posted */ 196819b5a6SJani Nikula #define SB_MWR_NP 0x01 206819b5a6SJani Nikula /* Private register read, double-word addressing, non-posted */ 216819b5a6SJani Nikula #define SB_CRRDDA_NP 0x06 226819b5a6SJani Nikula /* Private register write, double-word addressing, non-posted */ 236819b5a6SJani Nikula #define SB_CRWRDA_NP 0x07 246819b5a6SJani Nikula 256819b5a6SJani Nikula static void ping(void *info) 266819b5a6SJani Nikula { 276819b5a6SJani Nikula } 286819b5a6SJani Nikula 296819b5a6SJani Nikula static void __vlv_punit_get(struct drm_i915_private *i915) 306819b5a6SJani Nikula { 316819b5a6SJani Nikula iosf_mbi_punit_acquire(); 326819b5a6SJani Nikula 336819b5a6SJani Nikula /* 346819b5a6SJani Nikula * Prevent the cpu from sleeping while we use this sideband, otherwise 356819b5a6SJani Nikula * the punit may cause a machine hang. The issue appears to be isolated 366819b5a6SJani Nikula * with changing the power state of the CPU package while changing 376819b5a6SJani Nikula * the power state via the punit, and we have only observed it 386819b5a6SJani Nikula * reliably on 4-core Baytail systems suggesting the issue is in the 396819b5a6SJani Nikula * power delivery mechanism and likely to be board/function 406819b5a6SJani Nikula * specific. Hence we presume the workaround needs only be applied 416819b5a6SJani Nikula * to the Valleyview P-unit and not all sideband communications. 426819b5a6SJani Nikula */ 436819b5a6SJani Nikula if (IS_VALLEYVIEW(i915)) { 446819b5a6SJani Nikula cpu_latency_qos_update_request(&i915->vlv_iosf_sb.qos, 0); 456819b5a6SJani Nikula on_each_cpu(ping, NULL, 1); 466819b5a6SJani Nikula } 476819b5a6SJani Nikula } 486819b5a6SJani Nikula 496819b5a6SJani Nikula static void __vlv_punit_put(struct drm_i915_private *i915) 506819b5a6SJani Nikula { 516819b5a6SJani Nikula if (IS_VALLEYVIEW(i915)) 526819b5a6SJani Nikula cpu_latency_qos_update_request(&i915->vlv_iosf_sb.qos, 536819b5a6SJani Nikula PM_QOS_DEFAULT_VALUE); 546819b5a6SJani Nikula 556819b5a6SJani Nikula iosf_mbi_punit_release(); 566819b5a6SJani Nikula } 576819b5a6SJani Nikula 5852d83615SJani Nikula void vlv_iosf_sb_get(struct drm_device *drm, unsigned long unit_mask) 596819b5a6SJani Nikula { 60bd4d1856SJani Nikula struct drm_i915_private *i915 = to_i915(drm); 61bd4d1856SJani Nikula 6252d83615SJani Nikula if (unit_mask & BIT(VLV_IOSF_SB_PUNIT)) 636819b5a6SJani Nikula __vlv_punit_get(i915); 646819b5a6SJani Nikula 656819b5a6SJani Nikula mutex_lock(&i915->vlv_iosf_sb.lock); 66*11b5b1bdSJani Nikula 67*11b5b1bdSJani Nikula i915->vlv_iosf_sb.locked_unit_mask |= unit_mask; 686819b5a6SJani Nikula } 696819b5a6SJani Nikula 7052d83615SJani Nikula void vlv_iosf_sb_put(struct drm_device *drm, unsigned long unit_mask) 716819b5a6SJani Nikula { 72bd4d1856SJani Nikula struct drm_i915_private *i915 = to_i915(drm); 73bd4d1856SJani Nikula 74*11b5b1bdSJani Nikula i915->vlv_iosf_sb.locked_unit_mask &= ~unit_mask; 75*11b5b1bdSJani Nikula 76*11b5b1bdSJani Nikula drm_WARN_ON(drm, i915->vlv_iosf_sb.locked_unit_mask); 77*11b5b1bdSJani Nikula 786819b5a6SJani Nikula mutex_unlock(&i915->vlv_iosf_sb.lock); 796819b5a6SJani Nikula 8052d83615SJani Nikula if (unit_mask & BIT(VLV_IOSF_SB_PUNIT)) 816819b5a6SJani Nikula __vlv_punit_put(i915); 826819b5a6SJani Nikula } 836819b5a6SJani Nikula 846819b5a6SJani Nikula static int vlv_sideband_rw(struct drm_i915_private *i915, 856819b5a6SJani Nikula u32 devfn, u32 port, u32 opcode, 866819b5a6SJani Nikula u32 addr, u32 *val) 876819b5a6SJani Nikula { 886819b5a6SJani Nikula struct intel_uncore *uncore = &i915->uncore; 896819b5a6SJani Nikula const bool is_read = (opcode == SB_MRD_NP || opcode == SB_CRRDDA_NP); 906819b5a6SJani Nikula int err; 916819b5a6SJani Nikula 926819b5a6SJani Nikula lockdep_assert_held(&i915->vlv_iosf_sb.lock); 936819b5a6SJani Nikula if (port == IOSF_PORT_PUNIT) 946819b5a6SJani Nikula iosf_mbi_assert_punit_acquired(); 956819b5a6SJani Nikula 966819b5a6SJani Nikula /* Flush the previous comms, just in case it failed last time. */ 976819b5a6SJani Nikula if (intel_wait_for_register(uncore, 986819b5a6SJani Nikula VLV_IOSF_DOORBELL_REQ, IOSF_SB_BUSY, 0, 996819b5a6SJani Nikula 5)) { 1006819b5a6SJani Nikula drm_dbg(&i915->drm, "IOSF sideband idle wait (%s) timed out\n", 1016819b5a6SJani Nikula is_read ? "read" : "write"); 1026819b5a6SJani Nikula return -EAGAIN; 1036819b5a6SJani Nikula } 1046819b5a6SJani Nikula 1056819b5a6SJani Nikula preempt_disable(); 1066819b5a6SJani Nikula 1076819b5a6SJani Nikula intel_uncore_write_fw(uncore, VLV_IOSF_ADDR, addr); 1086819b5a6SJani Nikula intel_uncore_write_fw(uncore, VLV_IOSF_DATA, is_read ? 0 : *val); 1096819b5a6SJani Nikula intel_uncore_write_fw(uncore, VLV_IOSF_DOORBELL_REQ, 1106819b5a6SJani Nikula (devfn << IOSF_DEVFN_SHIFT) | 1116819b5a6SJani Nikula (opcode << IOSF_OPCODE_SHIFT) | 1126819b5a6SJani Nikula (port << IOSF_PORT_SHIFT) | 1136819b5a6SJani Nikula (0xf << IOSF_BYTE_ENABLES_SHIFT) | 1146819b5a6SJani Nikula (0 << IOSF_BAR_SHIFT) | 1156819b5a6SJani Nikula IOSF_SB_BUSY); 1166819b5a6SJani Nikula 1176819b5a6SJani Nikula if (__intel_wait_for_register_fw(uncore, 1186819b5a6SJani Nikula VLV_IOSF_DOORBELL_REQ, IOSF_SB_BUSY, 0, 1196819b5a6SJani Nikula 10000, 0, NULL) == 0) { 1206819b5a6SJani Nikula if (is_read) 1216819b5a6SJani Nikula *val = intel_uncore_read_fw(uncore, VLV_IOSF_DATA); 1226819b5a6SJani Nikula err = 0; 1236819b5a6SJani Nikula } else { 1246819b5a6SJani Nikula drm_dbg(&i915->drm, "IOSF sideband finish wait (%s) timed out\n", 1256819b5a6SJani Nikula is_read ? "read" : "write"); 1266819b5a6SJani Nikula err = -ETIMEDOUT; 1276819b5a6SJani Nikula } 1286819b5a6SJani Nikula 1296819b5a6SJani Nikula preempt_enable(); 1306819b5a6SJani Nikula 1316819b5a6SJani Nikula return err; 1326819b5a6SJani Nikula } 1336819b5a6SJani Nikula 1347e9f0cc2SJani Nikula static u32 unit_to_devfn(enum vlv_iosf_sb_unit unit) 1357e9f0cc2SJani Nikula { 1367e9f0cc2SJani Nikula if (unit == VLV_IOSF_SB_DPIO || unit == VLV_IOSF_SB_DPIO_2 || 1377e9f0cc2SJani Nikula unit == VLV_IOSF_SB_FLISDSI) 1387e9f0cc2SJani Nikula return DPIO_DEVFN; 1397e9f0cc2SJani Nikula else 1407e9f0cc2SJani Nikula return PCI_DEVFN(0, 0); 1417e9f0cc2SJani Nikula } 1427e9f0cc2SJani Nikula 1437e9f0cc2SJani Nikula static u32 unit_to_port(enum vlv_iosf_sb_unit unit) 1447e9f0cc2SJani Nikula { 1457e9f0cc2SJani Nikula switch (unit) { 1467e9f0cc2SJani Nikula case VLV_IOSF_SB_BUNIT: 1477e9f0cc2SJani Nikula return IOSF_PORT_BUNIT; 1487e9f0cc2SJani Nikula case VLV_IOSF_SB_CCK: 1497e9f0cc2SJani Nikula return IOSF_PORT_CCK; 1507e9f0cc2SJani Nikula case VLV_IOSF_SB_CCU: 1517e9f0cc2SJani Nikula return IOSF_PORT_CCU; 1527e9f0cc2SJani Nikula case VLV_IOSF_SB_DPIO: 1537e9f0cc2SJani Nikula return IOSF_PORT_DPIO; 1547e9f0cc2SJani Nikula case VLV_IOSF_SB_DPIO_2: 1557e9f0cc2SJani Nikula return IOSF_PORT_DPIO_2; 1567e9f0cc2SJani Nikula case VLV_IOSF_SB_FLISDSI: 1577e9f0cc2SJani Nikula return IOSF_PORT_FLISDSI; 1587e9f0cc2SJani Nikula case VLV_IOSF_SB_GPIO: 1597e9f0cc2SJani Nikula return 0; /* FIXME: unused */ 1607e9f0cc2SJani Nikula case VLV_IOSF_SB_NC: 1617e9f0cc2SJani Nikula return IOSF_PORT_NC; 1627e9f0cc2SJani Nikula case VLV_IOSF_SB_PUNIT: 1637e9f0cc2SJani Nikula return IOSF_PORT_PUNIT; 1647e9f0cc2SJani Nikula default: 1657e9f0cc2SJani Nikula return 0; 1667e9f0cc2SJani Nikula } 1677e9f0cc2SJani Nikula } 1687e9f0cc2SJani Nikula 1697e9f0cc2SJani Nikula static u32 unit_to_opcode(enum vlv_iosf_sb_unit unit, bool write) 1707e9f0cc2SJani Nikula { 1717e9f0cc2SJani Nikula if (unit == VLV_IOSF_SB_DPIO || unit == VLV_IOSF_SB_DPIO_2) 1727e9f0cc2SJani Nikula return write ? SB_MWR_NP : SB_MRD_NP; 1737e9f0cc2SJani Nikula else 1747e9f0cc2SJani Nikula return write ? SB_CRWRDA_NP : SB_CRRDDA_NP; 1757e9f0cc2SJani Nikula } 1767e9f0cc2SJani Nikula 177bd4d1856SJani Nikula u32 vlv_iosf_sb_read(struct drm_device *drm, enum vlv_iosf_sb_unit unit, u32 addr) 1787e9f0cc2SJani Nikula { 179bd4d1856SJani Nikula struct drm_i915_private *i915 = to_i915(drm); 1807e9f0cc2SJani Nikula u32 devfn, port, opcode, val = 0; 1817e9f0cc2SJani Nikula 1827e9f0cc2SJani Nikula devfn = unit_to_devfn(unit); 1837e9f0cc2SJani Nikula port = unit_to_port(unit); 1847e9f0cc2SJani Nikula opcode = unit_to_opcode(unit, false); 1857e9f0cc2SJani Nikula 1867e9f0cc2SJani Nikula if (drm_WARN_ONCE(&i915->drm, !port, "invalid unit %d\n", unit)) 1877e9f0cc2SJani Nikula return 0; 1887e9f0cc2SJani Nikula 189*11b5b1bdSJani Nikula drm_WARN_ON(&i915->drm, !(i915->vlv_iosf_sb.locked_unit_mask & BIT(unit))); 190*11b5b1bdSJani Nikula 1917e9f0cc2SJani Nikula vlv_sideband_rw(i915, devfn, port, opcode, addr, &val); 1927e9f0cc2SJani Nikula 1937e9f0cc2SJani Nikula return val; 1947e9f0cc2SJani Nikula } 1957e9f0cc2SJani Nikula 196bd4d1856SJani Nikula int vlv_iosf_sb_write(struct drm_device *drm, enum vlv_iosf_sb_unit unit, u32 addr, u32 val) 1977e9f0cc2SJani Nikula { 198bd4d1856SJani Nikula struct drm_i915_private *i915 = to_i915(drm); 1997e9f0cc2SJani Nikula u32 devfn, port, opcode; 2007e9f0cc2SJani Nikula 2017e9f0cc2SJani Nikula devfn = unit_to_devfn(unit); 2027e9f0cc2SJani Nikula port = unit_to_port(unit); 2037e9f0cc2SJani Nikula opcode = unit_to_opcode(unit, true); 2047e9f0cc2SJani Nikula 2057e9f0cc2SJani Nikula if (drm_WARN_ONCE(&i915->drm, !port, "invalid unit %d\n", unit)) 2067e9f0cc2SJani Nikula return -EINVAL; 2077e9f0cc2SJani Nikula 208*11b5b1bdSJani Nikula drm_WARN_ON(&i915->drm, !(i915->vlv_iosf_sb.locked_unit_mask & BIT(unit))); 209*11b5b1bdSJani Nikula 2107e9f0cc2SJani Nikula return vlv_sideband_rw(i915, devfn, port, opcode, addr, &val); 2117e9f0cc2SJani Nikula } 2127e9f0cc2SJani Nikula 2136819b5a6SJani Nikula void vlv_iosf_sb_init(struct drm_i915_private *i915) 2146819b5a6SJani Nikula { 2156819b5a6SJani Nikula if (IS_VALLEYVIEW(i915) || IS_CHERRYVIEW(i915)) 2166819b5a6SJani Nikula mutex_init(&i915->vlv_iosf_sb.lock); 2176819b5a6SJani Nikula 2186819b5a6SJani Nikula if (IS_VALLEYVIEW(i915)) 2196819b5a6SJani Nikula cpu_latency_qos_add_request(&i915->vlv_iosf_sb.qos, PM_QOS_DEFAULT_VALUE); 2206819b5a6SJani Nikula } 2216819b5a6SJani Nikula 2226819b5a6SJani Nikula void vlv_iosf_sb_fini(struct drm_i915_private *i915) 2236819b5a6SJani Nikula { 2246819b5a6SJani Nikula if (IS_VALLEYVIEW(i915)) 2256819b5a6SJani Nikula cpu_latency_qos_remove_request(&i915->vlv_iosf_sb.qos); 2266819b5a6SJani Nikula 2276819b5a6SJani Nikula if (IS_VALLEYVIEW(i915) || IS_CHERRYVIEW(i915)) 2286819b5a6SJani Nikula mutex_destroy(&i915->vlv_iosf_sb.lock); 2296819b5a6SJani Nikula } 230