18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci * Copyright © 2013 Intel Corporation 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * Permission is hereby granted, free of charge, to any person obtaining a 58c2ecf20Sopenharmony_ci * copy of this software and associated documentation files (the "Software"), 68c2ecf20Sopenharmony_ci * to deal in the Software without restriction, including without limitation 78c2ecf20Sopenharmony_ci * the rights to use, copy, modify, merge, publish, distribute, sublicense, 88c2ecf20Sopenharmony_ci * and/or sell copies of the Software, and to permit persons to whom the 98c2ecf20Sopenharmony_ci * Software is furnished to do so, subject to the following conditions: 108c2ecf20Sopenharmony_ci * 118c2ecf20Sopenharmony_ci * The above copyright notice and this permission notice (including the next 128c2ecf20Sopenharmony_ci * paragraph) shall be included in all copies or substantial portions of the 138c2ecf20Sopenharmony_ci * Software. 148c2ecf20Sopenharmony_ci * 158c2ecf20Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 168c2ecf20Sopenharmony_ci * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 178c2ecf20Sopenharmony_ci * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 188c2ecf20Sopenharmony_ci * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 198c2ecf20Sopenharmony_ci * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 208c2ecf20Sopenharmony_ci * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 218c2ecf20Sopenharmony_ci * IN THE SOFTWARE. 228c2ecf20Sopenharmony_ci * 238c2ecf20Sopenharmony_ci */ 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci#include <asm/iosf_mbi.h> 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci#include "i915_drv.h" 288c2ecf20Sopenharmony_ci#include "intel_sideband.h" 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ci/* 318c2ecf20Sopenharmony_ci * IOSF sideband, see VLV2_SidebandMsg_HAS.docx and 328c2ecf20Sopenharmony_ci * VLV_VLV2_PUNIT_HAS_0.8.docx 338c2ecf20Sopenharmony_ci */ 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ci/* Standard MMIO read, non-posted */ 368c2ecf20Sopenharmony_ci#define SB_MRD_NP 0x00 378c2ecf20Sopenharmony_ci/* Standard MMIO write, non-posted */ 388c2ecf20Sopenharmony_ci#define SB_MWR_NP 0x01 398c2ecf20Sopenharmony_ci/* Private register read, double-word addressing, non-posted */ 408c2ecf20Sopenharmony_ci#define SB_CRRDDA_NP 0x06 418c2ecf20Sopenharmony_ci/* Private register write, double-word addressing, non-posted */ 428c2ecf20Sopenharmony_ci#define SB_CRWRDA_NP 0x07 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_cistatic void ping(void *info) 458c2ecf20Sopenharmony_ci{ 468c2ecf20Sopenharmony_ci} 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_cistatic void __vlv_punit_get(struct drm_i915_private *i915) 498c2ecf20Sopenharmony_ci{ 508c2ecf20Sopenharmony_ci iosf_mbi_punit_acquire(); 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci /* 538c2ecf20Sopenharmony_ci * Prevent the cpu from sleeping while we use this sideband, otherwise 548c2ecf20Sopenharmony_ci * the punit may cause a machine hang. The issue appears to be isolated 558c2ecf20Sopenharmony_ci * with changing the power state of the CPU package while changing 568c2ecf20Sopenharmony_ci * the power state via the punit, and we have only observed it 578c2ecf20Sopenharmony_ci * reliably on 4-core Baytail systems suggesting the issue is in the 588c2ecf20Sopenharmony_ci * power delivery mechanism and likely to be be board/function 598c2ecf20Sopenharmony_ci * specific. Hence we presume the workaround needs only be applied 608c2ecf20Sopenharmony_ci * to the Valleyview P-unit and not all sideband communications. 618c2ecf20Sopenharmony_ci */ 628c2ecf20Sopenharmony_ci if (IS_VALLEYVIEW(i915)) { 638c2ecf20Sopenharmony_ci cpu_latency_qos_update_request(&i915->sb_qos, 0); 648c2ecf20Sopenharmony_ci on_each_cpu(ping, NULL, 1); 658c2ecf20Sopenharmony_ci } 668c2ecf20Sopenharmony_ci} 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_cistatic void __vlv_punit_put(struct drm_i915_private *i915) 698c2ecf20Sopenharmony_ci{ 708c2ecf20Sopenharmony_ci if (IS_VALLEYVIEW(i915)) 718c2ecf20Sopenharmony_ci cpu_latency_qos_update_request(&i915->sb_qos, 728c2ecf20Sopenharmony_ci PM_QOS_DEFAULT_VALUE); 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci iosf_mbi_punit_release(); 758c2ecf20Sopenharmony_ci} 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_civoid vlv_iosf_sb_get(struct drm_i915_private *i915, unsigned long ports) 788c2ecf20Sopenharmony_ci{ 798c2ecf20Sopenharmony_ci if (ports & BIT(VLV_IOSF_SB_PUNIT)) 808c2ecf20Sopenharmony_ci __vlv_punit_get(i915); 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci mutex_lock(&i915->sb_lock); 838c2ecf20Sopenharmony_ci} 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_civoid vlv_iosf_sb_put(struct drm_i915_private *i915, unsigned long ports) 868c2ecf20Sopenharmony_ci{ 878c2ecf20Sopenharmony_ci mutex_unlock(&i915->sb_lock); 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci if (ports & BIT(VLV_IOSF_SB_PUNIT)) 908c2ecf20Sopenharmony_ci __vlv_punit_put(i915); 918c2ecf20Sopenharmony_ci} 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_cistatic int vlv_sideband_rw(struct drm_i915_private *i915, 948c2ecf20Sopenharmony_ci u32 devfn, u32 port, u32 opcode, 958c2ecf20Sopenharmony_ci u32 addr, u32 *val) 968c2ecf20Sopenharmony_ci{ 978c2ecf20Sopenharmony_ci struct intel_uncore *uncore = &i915->uncore; 988c2ecf20Sopenharmony_ci const bool is_read = (opcode == SB_MRD_NP || opcode == SB_CRRDDA_NP); 998c2ecf20Sopenharmony_ci int err; 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci lockdep_assert_held(&i915->sb_lock); 1028c2ecf20Sopenharmony_ci if (port == IOSF_PORT_PUNIT) 1038c2ecf20Sopenharmony_ci iosf_mbi_assert_punit_acquired(); 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci /* Flush the previous comms, just in case it failed last time. */ 1068c2ecf20Sopenharmony_ci if (intel_wait_for_register(uncore, 1078c2ecf20Sopenharmony_ci VLV_IOSF_DOORBELL_REQ, IOSF_SB_BUSY, 0, 1088c2ecf20Sopenharmony_ci 5)) { 1098c2ecf20Sopenharmony_ci drm_dbg(&i915->drm, "IOSF sideband idle wait (%s) timed out\n", 1108c2ecf20Sopenharmony_ci is_read ? "read" : "write"); 1118c2ecf20Sopenharmony_ci return -EAGAIN; 1128c2ecf20Sopenharmony_ci } 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci preempt_disable(); 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci intel_uncore_write_fw(uncore, VLV_IOSF_ADDR, addr); 1178c2ecf20Sopenharmony_ci intel_uncore_write_fw(uncore, VLV_IOSF_DATA, is_read ? 0 : *val); 1188c2ecf20Sopenharmony_ci intel_uncore_write_fw(uncore, VLV_IOSF_DOORBELL_REQ, 1198c2ecf20Sopenharmony_ci (devfn << IOSF_DEVFN_SHIFT) | 1208c2ecf20Sopenharmony_ci (opcode << IOSF_OPCODE_SHIFT) | 1218c2ecf20Sopenharmony_ci (port << IOSF_PORT_SHIFT) | 1228c2ecf20Sopenharmony_ci (0xf << IOSF_BYTE_ENABLES_SHIFT) | 1238c2ecf20Sopenharmony_ci (0 << IOSF_BAR_SHIFT) | 1248c2ecf20Sopenharmony_ci IOSF_SB_BUSY); 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci if (__intel_wait_for_register_fw(uncore, 1278c2ecf20Sopenharmony_ci VLV_IOSF_DOORBELL_REQ, IOSF_SB_BUSY, 0, 1288c2ecf20Sopenharmony_ci 10000, 0, NULL) == 0) { 1298c2ecf20Sopenharmony_ci if (is_read) 1308c2ecf20Sopenharmony_ci *val = intel_uncore_read_fw(uncore, VLV_IOSF_DATA); 1318c2ecf20Sopenharmony_ci err = 0; 1328c2ecf20Sopenharmony_ci } else { 1338c2ecf20Sopenharmony_ci drm_dbg(&i915->drm, "IOSF sideband finish wait (%s) timed out\n", 1348c2ecf20Sopenharmony_ci is_read ? "read" : "write"); 1358c2ecf20Sopenharmony_ci err = -ETIMEDOUT; 1368c2ecf20Sopenharmony_ci } 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci preempt_enable(); 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci return err; 1418c2ecf20Sopenharmony_ci} 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ciu32 vlv_punit_read(struct drm_i915_private *i915, u32 addr) 1448c2ecf20Sopenharmony_ci{ 1458c2ecf20Sopenharmony_ci u32 val = 0; 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci vlv_sideband_rw(i915, PCI_DEVFN(0, 0), IOSF_PORT_PUNIT, 1488c2ecf20Sopenharmony_ci SB_CRRDDA_NP, addr, &val); 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci return val; 1518c2ecf20Sopenharmony_ci} 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ciint vlv_punit_write(struct drm_i915_private *i915, u32 addr, u32 val) 1548c2ecf20Sopenharmony_ci{ 1558c2ecf20Sopenharmony_ci return vlv_sideband_rw(i915, PCI_DEVFN(0, 0), IOSF_PORT_PUNIT, 1568c2ecf20Sopenharmony_ci SB_CRWRDA_NP, addr, &val); 1578c2ecf20Sopenharmony_ci} 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ciu32 vlv_bunit_read(struct drm_i915_private *i915, u32 reg) 1608c2ecf20Sopenharmony_ci{ 1618c2ecf20Sopenharmony_ci u32 val = 0; 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci vlv_sideband_rw(i915, PCI_DEVFN(0, 0), IOSF_PORT_BUNIT, 1648c2ecf20Sopenharmony_ci SB_CRRDDA_NP, reg, &val); 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ci return val; 1678c2ecf20Sopenharmony_ci} 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_civoid vlv_bunit_write(struct drm_i915_private *i915, u32 reg, u32 val) 1708c2ecf20Sopenharmony_ci{ 1718c2ecf20Sopenharmony_ci vlv_sideband_rw(i915, PCI_DEVFN(0, 0), IOSF_PORT_BUNIT, 1728c2ecf20Sopenharmony_ci SB_CRWRDA_NP, reg, &val); 1738c2ecf20Sopenharmony_ci} 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ciu32 vlv_nc_read(struct drm_i915_private *i915, u8 addr) 1768c2ecf20Sopenharmony_ci{ 1778c2ecf20Sopenharmony_ci u32 val = 0; 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci vlv_sideband_rw(i915, PCI_DEVFN(0, 0), IOSF_PORT_NC, 1808c2ecf20Sopenharmony_ci SB_CRRDDA_NP, addr, &val); 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci return val; 1838c2ecf20Sopenharmony_ci} 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ciu32 vlv_iosf_sb_read(struct drm_i915_private *i915, u8 port, u32 reg) 1868c2ecf20Sopenharmony_ci{ 1878c2ecf20Sopenharmony_ci u32 val = 0; 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci vlv_sideband_rw(i915, PCI_DEVFN(0, 0), port, 1908c2ecf20Sopenharmony_ci SB_CRRDDA_NP, reg, &val); 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci return val; 1938c2ecf20Sopenharmony_ci} 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_civoid vlv_iosf_sb_write(struct drm_i915_private *i915, 1968c2ecf20Sopenharmony_ci u8 port, u32 reg, u32 val) 1978c2ecf20Sopenharmony_ci{ 1988c2ecf20Sopenharmony_ci vlv_sideband_rw(i915, PCI_DEVFN(0, 0), port, 1998c2ecf20Sopenharmony_ci SB_CRWRDA_NP, reg, &val); 2008c2ecf20Sopenharmony_ci} 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ciu32 vlv_cck_read(struct drm_i915_private *i915, u32 reg) 2038c2ecf20Sopenharmony_ci{ 2048c2ecf20Sopenharmony_ci u32 val = 0; 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci vlv_sideband_rw(i915, PCI_DEVFN(0, 0), IOSF_PORT_CCK, 2078c2ecf20Sopenharmony_ci SB_CRRDDA_NP, reg, &val); 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci return val; 2108c2ecf20Sopenharmony_ci} 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_civoid vlv_cck_write(struct drm_i915_private *i915, u32 reg, u32 val) 2138c2ecf20Sopenharmony_ci{ 2148c2ecf20Sopenharmony_ci vlv_sideband_rw(i915, PCI_DEVFN(0, 0), IOSF_PORT_CCK, 2158c2ecf20Sopenharmony_ci SB_CRWRDA_NP, reg, &val); 2168c2ecf20Sopenharmony_ci} 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ciu32 vlv_ccu_read(struct drm_i915_private *i915, u32 reg) 2198c2ecf20Sopenharmony_ci{ 2208c2ecf20Sopenharmony_ci u32 val = 0; 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_ci vlv_sideband_rw(i915, PCI_DEVFN(0, 0), IOSF_PORT_CCU, 2238c2ecf20Sopenharmony_ci SB_CRRDDA_NP, reg, &val); 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_ci return val; 2268c2ecf20Sopenharmony_ci} 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_civoid vlv_ccu_write(struct drm_i915_private *i915, u32 reg, u32 val) 2298c2ecf20Sopenharmony_ci{ 2308c2ecf20Sopenharmony_ci vlv_sideband_rw(i915, PCI_DEVFN(0, 0), IOSF_PORT_CCU, 2318c2ecf20Sopenharmony_ci SB_CRWRDA_NP, reg, &val); 2328c2ecf20Sopenharmony_ci} 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_cistatic u32 vlv_dpio_phy_iosf_port(struct drm_i915_private *i915, enum dpio_phy phy) 2358c2ecf20Sopenharmony_ci{ 2368c2ecf20Sopenharmony_ci /* 2378c2ecf20Sopenharmony_ci * IOSF_PORT_DPIO: VLV x2 PHY (DP/HDMI B and C), CHV x1 PHY (DP/HDMI D) 2388c2ecf20Sopenharmony_ci * IOSF_PORT_DPIO_2: CHV x2 PHY (DP/HDMI B and C) 2398c2ecf20Sopenharmony_ci */ 2408c2ecf20Sopenharmony_ci if (IS_CHERRYVIEW(i915)) 2418c2ecf20Sopenharmony_ci return phy == DPIO_PHY0 ? IOSF_PORT_DPIO_2 : IOSF_PORT_DPIO; 2428c2ecf20Sopenharmony_ci else 2438c2ecf20Sopenharmony_ci return IOSF_PORT_DPIO; 2448c2ecf20Sopenharmony_ci} 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_ciu32 vlv_dpio_read(struct drm_i915_private *i915, enum pipe pipe, int reg) 2478c2ecf20Sopenharmony_ci{ 2488c2ecf20Sopenharmony_ci u32 port = vlv_dpio_phy_iosf_port(i915, DPIO_PHY(pipe)); 2498c2ecf20Sopenharmony_ci u32 val = 0; 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_ci vlv_sideband_rw(i915, DPIO_DEVFN, port, SB_MRD_NP, reg, &val); 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_ci /* 2548c2ecf20Sopenharmony_ci * FIXME: There might be some registers where all 1's is a valid value, 2558c2ecf20Sopenharmony_ci * so ideally we should check the register offset instead... 2568c2ecf20Sopenharmony_ci */ 2578c2ecf20Sopenharmony_ci drm_WARN(&i915->drm, val == 0xffffffff, 2588c2ecf20Sopenharmony_ci "DPIO read pipe %c reg 0x%x == 0x%x\n", 2598c2ecf20Sopenharmony_ci pipe_name(pipe), reg, val); 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_ci return val; 2628c2ecf20Sopenharmony_ci} 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_civoid vlv_dpio_write(struct drm_i915_private *i915, 2658c2ecf20Sopenharmony_ci enum pipe pipe, int reg, u32 val) 2668c2ecf20Sopenharmony_ci{ 2678c2ecf20Sopenharmony_ci u32 port = vlv_dpio_phy_iosf_port(i915, DPIO_PHY(pipe)); 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_ci vlv_sideband_rw(i915, DPIO_DEVFN, port, SB_MWR_NP, reg, &val); 2708c2ecf20Sopenharmony_ci} 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_ciu32 vlv_flisdsi_read(struct drm_i915_private *i915, u32 reg) 2738c2ecf20Sopenharmony_ci{ 2748c2ecf20Sopenharmony_ci u32 val = 0; 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_ci vlv_sideband_rw(i915, DPIO_DEVFN, IOSF_PORT_FLISDSI, SB_CRRDDA_NP, 2778c2ecf20Sopenharmony_ci reg, &val); 2788c2ecf20Sopenharmony_ci return val; 2798c2ecf20Sopenharmony_ci} 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_civoid vlv_flisdsi_write(struct drm_i915_private *i915, u32 reg, u32 val) 2828c2ecf20Sopenharmony_ci{ 2838c2ecf20Sopenharmony_ci vlv_sideband_rw(i915, DPIO_DEVFN, IOSF_PORT_FLISDSI, SB_CRWRDA_NP, 2848c2ecf20Sopenharmony_ci reg, &val); 2858c2ecf20Sopenharmony_ci} 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_ci/* SBI access */ 2888c2ecf20Sopenharmony_cistatic int intel_sbi_rw(struct drm_i915_private *i915, u16 reg, 2898c2ecf20Sopenharmony_ci enum intel_sbi_destination destination, 2908c2ecf20Sopenharmony_ci u32 *val, bool is_read) 2918c2ecf20Sopenharmony_ci{ 2928c2ecf20Sopenharmony_ci struct intel_uncore *uncore = &i915->uncore; 2938c2ecf20Sopenharmony_ci u32 cmd; 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_ci lockdep_assert_held(&i915->sb_lock); 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_ci if (intel_wait_for_register_fw(uncore, 2988c2ecf20Sopenharmony_ci SBI_CTL_STAT, SBI_BUSY, 0, 2998c2ecf20Sopenharmony_ci 100)) { 3008c2ecf20Sopenharmony_ci drm_err(&i915->drm, 3018c2ecf20Sopenharmony_ci "timeout waiting for SBI to become ready\n"); 3028c2ecf20Sopenharmony_ci return -EBUSY; 3038c2ecf20Sopenharmony_ci } 3048c2ecf20Sopenharmony_ci 3058c2ecf20Sopenharmony_ci intel_uncore_write_fw(uncore, SBI_ADDR, (u32)reg << 16); 3068c2ecf20Sopenharmony_ci intel_uncore_write_fw(uncore, SBI_DATA, is_read ? 0 : *val); 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_ci if (destination == SBI_ICLK) 3098c2ecf20Sopenharmony_ci cmd = SBI_CTL_DEST_ICLK | SBI_CTL_OP_CRRD; 3108c2ecf20Sopenharmony_ci else 3118c2ecf20Sopenharmony_ci cmd = SBI_CTL_DEST_MPHY | SBI_CTL_OP_IORD; 3128c2ecf20Sopenharmony_ci if (!is_read) 3138c2ecf20Sopenharmony_ci cmd |= BIT(8); 3148c2ecf20Sopenharmony_ci intel_uncore_write_fw(uncore, SBI_CTL_STAT, cmd | SBI_BUSY); 3158c2ecf20Sopenharmony_ci 3168c2ecf20Sopenharmony_ci if (__intel_wait_for_register_fw(uncore, 3178c2ecf20Sopenharmony_ci SBI_CTL_STAT, SBI_BUSY, 0, 3188c2ecf20Sopenharmony_ci 100, 100, &cmd)) { 3198c2ecf20Sopenharmony_ci drm_err(&i915->drm, 3208c2ecf20Sopenharmony_ci "timeout waiting for SBI to complete read\n"); 3218c2ecf20Sopenharmony_ci return -ETIMEDOUT; 3228c2ecf20Sopenharmony_ci } 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_ci if (cmd & SBI_RESPONSE_FAIL) { 3258c2ecf20Sopenharmony_ci drm_err(&i915->drm, "error during SBI read of reg %x\n", reg); 3268c2ecf20Sopenharmony_ci return -ENXIO; 3278c2ecf20Sopenharmony_ci } 3288c2ecf20Sopenharmony_ci 3298c2ecf20Sopenharmony_ci if (is_read) 3308c2ecf20Sopenharmony_ci *val = intel_uncore_read_fw(uncore, SBI_DATA); 3318c2ecf20Sopenharmony_ci 3328c2ecf20Sopenharmony_ci return 0; 3338c2ecf20Sopenharmony_ci} 3348c2ecf20Sopenharmony_ci 3358c2ecf20Sopenharmony_ciu32 intel_sbi_read(struct drm_i915_private *i915, u16 reg, 3368c2ecf20Sopenharmony_ci enum intel_sbi_destination destination) 3378c2ecf20Sopenharmony_ci{ 3388c2ecf20Sopenharmony_ci u32 result = 0; 3398c2ecf20Sopenharmony_ci 3408c2ecf20Sopenharmony_ci intel_sbi_rw(i915, reg, destination, &result, true); 3418c2ecf20Sopenharmony_ci 3428c2ecf20Sopenharmony_ci return result; 3438c2ecf20Sopenharmony_ci} 3448c2ecf20Sopenharmony_ci 3458c2ecf20Sopenharmony_civoid intel_sbi_write(struct drm_i915_private *i915, u16 reg, u32 value, 3468c2ecf20Sopenharmony_ci enum intel_sbi_destination destination) 3478c2ecf20Sopenharmony_ci{ 3488c2ecf20Sopenharmony_ci intel_sbi_rw(i915, reg, destination, &value, false); 3498c2ecf20Sopenharmony_ci} 3508c2ecf20Sopenharmony_ci 3518c2ecf20Sopenharmony_cistatic int gen6_check_mailbox_status(u32 mbox) 3528c2ecf20Sopenharmony_ci{ 3538c2ecf20Sopenharmony_ci switch (mbox & GEN6_PCODE_ERROR_MASK) { 3548c2ecf20Sopenharmony_ci case GEN6_PCODE_SUCCESS: 3558c2ecf20Sopenharmony_ci return 0; 3568c2ecf20Sopenharmony_ci case GEN6_PCODE_UNIMPLEMENTED_CMD: 3578c2ecf20Sopenharmony_ci return -ENODEV; 3588c2ecf20Sopenharmony_ci case GEN6_PCODE_ILLEGAL_CMD: 3598c2ecf20Sopenharmony_ci return -ENXIO; 3608c2ecf20Sopenharmony_ci case GEN6_PCODE_MIN_FREQ_TABLE_GT_RATIO_OUT_OF_RANGE: 3618c2ecf20Sopenharmony_ci case GEN7_PCODE_MIN_FREQ_TABLE_GT_RATIO_OUT_OF_RANGE: 3628c2ecf20Sopenharmony_ci return -EOVERFLOW; 3638c2ecf20Sopenharmony_ci case GEN6_PCODE_TIMEOUT: 3648c2ecf20Sopenharmony_ci return -ETIMEDOUT; 3658c2ecf20Sopenharmony_ci default: 3668c2ecf20Sopenharmony_ci MISSING_CASE(mbox & GEN6_PCODE_ERROR_MASK); 3678c2ecf20Sopenharmony_ci return 0; 3688c2ecf20Sopenharmony_ci } 3698c2ecf20Sopenharmony_ci} 3708c2ecf20Sopenharmony_ci 3718c2ecf20Sopenharmony_cistatic int gen7_check_mailbox_status(u32 mbox) 3728c2ecf20Sopenharmony_ci{ 3738c2ecf20Sopenharmony_ci switch (mbox & GEN6_PCODE_ERROR_MASK) { 3748c2ecf20Sopenharmony_ci case GEN6_PCODE_SUCCESS: 3758c2ecf20Sopenharmony_ci return 0; 3768c2ecf20Sopenharmony_ci case GEN6_PCODE_ILLEGAL_CMD: 3778c2ecf20Sopenharmony_ci return -ENXIO; 3788c2ecf20Sopenharmony_ci case GEN7_PCODE_TIMEOUT: 3798c2ecf20Sopenharmony_ci return -ETIMEDOUT; 3808c2ecf20Sopenharmony_ci case GEN7_PCODE_ILLEGAL_DATA: 3818c2ecf20Sopenharmony_ci return -EINVAL; 3828c2ecf20Sopenharmony_ci case GEN11_PCODE_ILLEGAL_SUBCOMMAND: 3838c2ecf20Sopenharmony_ci return -ENXIO; 3848c2ecf20Sopenharmony_ci case GEN11_PCODE_LOCKED: 3858c2ecf20Sopenharmony_ci return -EBUSY; 3868c2ecf20Sopenharmony_ci case GEN11_PCODE_REJECTED: 3878c2ecf20Sopenharmony_ci return -EACCES; 3888c2ecf20Sopenharmony_ci case GEN7_PCODE_MIN_FREQ_TABLE_GT_RATIO_OUT_OF_RANGE: 3898c2ecf20Sopenharmony_ci return -EOVERFLOW; 3908c2ecf20Sopenharmony_ci default: 3918c2ecf20Sopenharmony_ci MISSING_CASE(mbox & GEN6_PCODE_ERROR_MASK); 3928c2ecf20Sopenharmony_ci return 0; 3938c2ecf20Sopenharmony_ci } 3948c2ecf20Sopenharmony_ci} 3958c2ecf20Sopenharmony_ci 3968c2ecf20Sopenharmony_cistatic int __sandybridge_pcode_rw(struct drm_i915_private *i915, 3978c2ecf20Sopenharmony_ci u32 mbox, u32 *val, u32 *val1, 3988c2ecf20Sopenharmony_ci int fast_timeout_us, 3998c2ecf20Sopenharmony_ci int slow_timeout_ms, 4008c2ecf20Sopenharmony_ci bool is_read) 4018c2ecf20Sopenharmony_ci{ 4028c2ecf20Sopenharmony_ci struct intel_uncore *uncore = &i915->uncore; 4038c2ecf20Sopenharmony_ci 4048c2ecf20Sopenharmony_ci lockdep_assert_held(&i915->sb_lock); 4058c2ecf20Sopenharmony_ci 4068c2ecf20Sopenharmony_ci /* 4078c2ecf20Sopenharmony_ci * GEN6_PCODE_* are outside of the forcewake domain, we can 4088c2ecf20Sopenharmony_ci * use te fw I915_READ variants to reduce the amount of work 4098c2ecf20Sopenharmony_ci * required when reading/writing. 4108c2ecf20Sopenharmony_ci */ 4118c2ecf20Sopenharmony_ci 4128c2ecf20Sopenharmony_ci if (intel_uncore_read_fw(uncore, GEN6_PCODE_MAILBOX) & GEN6_PCODE_READY) 4138c2ecf20Sopenharmony_ci return -EAGAIN; 4148c2ecf20Sopenharmony_ci 4158c2ecf20Sopenharmony_ci intel_uncore_write_fw(uncore, GEN6_PCODE_DATA, *val); 4168c2ecf20Sopenharmony_ci intel_uncore_write_fw(uncore, GEN6_PCODE_DATA1, val1 ? *val1 : 0); 4178c2ecf20Sopenharmony_ci intel_uncore_write_fw(uncore, 4188c2ecf20Sopenharmony_ci GEN6_PCODE_MAILBOX, GEN6_PCODE_READY | mbox); 4198c2ecf20Sopenharmony_ci 4208c2ecf20Sopenharmony_ci if (__intel_wait_for_register_fw(uncore, 4218c2ecf20Sopenharmony_ci GEN6_PCODE_MAILBOX, 4228c2ecf20Sopenharmony_ci GEN6_PCODE_READY, 0, 4238c2ecf20Sopenharmony_ci fast_timeout_us, 4248c2ecf20Sopenharmony_ci slow_timeout_ms, 4258c2ecf20Sopenharmony_ci &mbox)) 4268c2ecf20Sopenharmony_ci return -ETIMEDOUT; 4278c2ecf20Sopenharmony_ci 4288c2ecf20Sopenharmony_ci if (is_read) 4298c2ecf20Sopenharmony_ci *val = intel_uncore_read_fw(uncore, GEN6_PCODE_DATA); 4308c2ecf20Sopenharmony_ci if (is_read && val1) 4318c2ecf20Sopenharmony_ci *val1 = intel_uncore_read_fw(uncore, GEN6_PCODE_DATA1); 4328c2ecf20Sopenharmony_ci 4338c2ecf20Sopenharmony_ci if (INTEL_GEN(i915) > 6) 4348c2ecf20Sopenharmony_ci return gen7_check_mailbox_status(mbox); 4358c2ecf20Sopenharmony_ci else 4368c2ecf20Sopenharmony_ci return gen6_check_mailbox_status(mbox); 4378c2ecf20Sopenharmony_ci} 4388c2ecf20Sopenharmony_ci 4398c2ecf20Sopenharmony_ciint sandybridge_pcode_read(struct drm_i915_private *i915, u32 mbox, 4408c2ecf20Sopenharmony_ci u32 *val, u32 *val1) 4418c2ecf20Sopenharmony_ci{ 4428c2ecf20Sopenharmony_ci int err; 4438c2ecf20Sopenharmony_ci 4448c2ecf20Sopenharmony_ci mutex_lock(&i915->sb_lock); 4458c2ecf20Sopenharmony_ci err = __sandybridge_pcode_rw(i915, mbox, val, val1, 4468c2ecf20Sopenharmony_ci 500, 20, 4478c2ecf20Sopenharmony_ci true); 4488c2ecf20Sopenharmony_ci mutex_unlock(&i915->sb_lock); 4498c2ecf20Sopenharmony_ci 4508c2ecf20Sopenharmony_ci if (err) { 4518c2ecf20Sopenharmony_ci drm_dbg(&i915->drm, 4528c2ecf20Sopenharmony_ci "warning: pcode (read from mbox %x) mailbox access failed for %ps: %d\n", 4538c2ecf20Sopenharmony_ci mbox, __builtin_return_address(0), err); 4548c2ecf20Sopenharmony_ci } 4558c2ecf20Sopenharmony_ci 4568c2ecf20Sopenharmony_ci return err; 4578c2ecf20Sopenharmony_ci} 4588c2ecf20Sopenharmony_ci 4598c2ecf20Sopenharmony_ciint sandybridge_pcode_write_timeout(struct drm_i915_private *i915, 4608c2ecf20Sopenharmony_ci u32 mbox, u32 val, 4618c2ecf20Sopenharmony_ci int fast_timeout_us, 4628c2ecf20Sopenharmony_ci int slow_timeout_ms) 4638c2ecf20Sopenharmony_ci{ 4648c2ecf20Sopenharmony_ci int err; 4658c2ecf20Sopenharmony_ci 4668c2ecf20Sopenharmony_ci mutex_lock(&i915->sb_lock); 4678c2ecf20Sopenharmony_ci err = __sandybridge_pcode_rw(i915, mbox, &val, NULL, 4688c2ecf20Sopenharmony_ci fast_timeout_us, slow_timeout_ms, 4698c2ecf20Sopenharmony_ci false); 4708c2ecf20Sopenharmony_ci mutex_unlock(&i915->sb_lock); 4718c2ecf20Sopenharmony_ci 4728c2ecf20Sopenharmony_ci if (err) { 4738c2ecf20Sopenharmony_ci drm_dbg(&i915->drm, 4748c2ecf20Sopenharmony_ci "warning: pcode (write of 0x%08x to mbox %x) mailbox access failed for %ps: %d\n", 4758c2ecf20Sopenharmony_ci val, mbox, __builtin_return_address(0), err); 4768c2ecf20Sopenharmony_ci } 4778c2ecf20Sopenharmony_ci 4788c2ecf20Sopenharmony_ci return err; 4798c2ecf20Sopenharmony_ci} 4808c2ecf20Sopenharmony_ci 4818c2ecf20Sopenharmony_cistatic bool skl_pcode_try_request(struct drm_i915_private *i915, u32 mbox, 4828c2ecf20Sopenharmony_ci u32 request, u32 reply_mask, u32 reply, 4838c2ecf20Sopenharmony_ci u32 *status) 4848c2ecf20Sopenharmony_ci{ 4858c2ecf20Sopenharmony_ci *status = __sandybridge_pcode_rw(i915, mbox, &request, NULL, 4868c2ecf20Sopenharmony_ci 500, 0, 4878c2ecf20Sopenharmony_ci true); 4888c2ecf20Sopenharmony_ci 4898c2ecf20Sopenharmony_ci return *status || ((request & reply_mask) == reply); 4908c2ecf20Sopenharmony_ci} 4918c2ecf20Sopenharmony_ci 4928c2ecf20Sopenharmony_ci/** 4938c2ecf20Sopenharmony_ci * skl_pcode_request - send PCODE request until acknowledgment 4948c2ecf20Sopenharmony_ci * @i915: device private 4958c2ecf20Sopenharmony_ci * @mbox: PCODE mailbox ID the request is targeted for 4968c2ecf20Sopenharmony_ci * @request: request ID 4978c2ecf20Sopenharmony_ci * @reply_mask: mask used to check for request acknowledgment 4988c2ecf20Sopenharmony_ci * @reply: value used to check for request acknowledgment 4998c2ecf20Sopenharmony_ci * @timeout_base_ms: timeout for polling with preemption enabled 5008c2ecf20Sopenharmony_ci * 5018c2ecf20Sopenharmony_ci * Keep resending the @request to @mbox until PCODE acknowledges it, PCODE 5028c2ecf20Sopenharmony_ci * reports an error or an overall timeout of @timeout_base_ms+50 ms expires. 5038c2ecf20Sopenharmony_ci * The request is acknowledged once the PCODE reply dword equals @reply after 5048c2ecf20Sopenharmony_ci * applying @reply_mask. Polling is first attempted with preemption enabled 5058c2ecf20Sopenharmony_ci * for @timeout_base_ms and if this times out for another 50 ms with 5068c2ecf20Sopenharmony_ci * preemption disabled. 5078c2ecf20Sopenharmony_ci * 5088c2ecf20Sopenharmony_ci * Returns 0 on success, %-ETIMEDOUT in case of a timeout, <0 in case of some 5098c2ecf20Sopenharmony_ci * other error as reported by PCODE. 5108c2ecf20Sopenharmony_ci */ 5118c2ecf20Sopenharmony_ciint skl_pcode_request(struct drm_i915_private *i915, u32 mbox, u32 request, 5128c2ecf20Sopenharmony_ci u32 reply_mask, u32 reply, int timeout_base_ms) 5138c2ecf20Sopenharmony_ci{ 5148c2ecf20Sopenharmony_ci u32 status; 5158c2ecf20Sopenharmony_ci int ret; 5168c2ecf20Sopenharmony_ci 5178c2ecf20Sopenharmony_ci mutex_lock(&i915->sb_lock); 5188c2ecf20Sopenharmony_ci 5198c2ecf20Sopenharmony_ci#define COND \ 5208c2ecf20Sopenharmony_ci skl_pcode_try_request(i915, mbox, request, reply_mask, reply, &status) 5218c2ecf20Sopenharmony_ci 5228c2ecf20Sopenharmony_ci /* 5238c2ecf20Sopenharmony_ci * Prime the PCODE by doing a request first. Normally it guarantees 5248c2ecf20Sopenharmony_ci * that a subsequent request, at most @timeout_base_ms later, succeeds. 5258c2ecf20Sopenharmony_ci * _wait_for() doesn't guarantee when its passed condition is evaluated 5268c2ecf20Sopenharmony_ci * first, so send the first request explicitly. 5278c2ecf20Sopenharmony_ci */ 5288c2ecf20Sopenharmony_ci if (COND) { 5298c2ecf20Sopenharmony_ci ret = 0; 5308c2ecf20Sopenharmony_ci goto out; 5318c2ecf20Sopenharmony_ci } 5328c2ecf20Sopenharmony_ci ret = _wait_for(COND, timeout_base_ms * 1000, 10, 10); 5338c2ecf20Sopenharmony_ci if (!ret) 5348c2ecf20Sopenharmony_ci goto out; 5358c2ecf20Sopenharmony_ci 5368c2ecf20Sopenharmony_ci /* 5378c2ecf20Sopenharmony_ci * The above can time out if the number of requests was low (2 in the 5388c2ecf20Sopenharmony_ci * worst case) _and_ PCODE was busy for some reason even after a 5398c2ecf20Sopenharmony_ci * (queued) request and @timeout_base_ms delay. As a workaround retry 5408c2ecf20Sopenharmony_ci * the poll with preemption disabled to maximize the number of 5418c2ecf20Sopenharmony_ci * requests. Increase the timeout from @timeout_base_ms to 50ms to 5428c2ecf20Sopenharmony_ci * account for interrupts that could reduce the number of these 5438c2ecf20Sopenharmony_ci * requests, and for any quirks of the PCODE firmware that delays 5448c2ecf20Sopenharmony_ci * the request completion. 5458c2ecf20Sopenharmony_ci */ 5468c2ecf20Sopenharmony_ci drm_dbg_kms(&i915->drm, 5478c2ecf20Sopenharmony_ci "PCODE timeout, retrying with preemption disabled\n"); 5488c2ecf20Sopenharmony_ci drm_WARN_ON_ONCE(&i915->drm, timeout_base_ms > 3); 5498c2ecf20Sopenharmony_ci preempt_disable(); 5508c2ecf20Sopenharmony_ci ret = wait_for_atomic(COND, 50); 5518c2ecf20Sopenharmony_ci preempt_enable(); 5528c2ecf20Sopenharmony_ci 5538c2ecf20Sopenharmony_ciout: 5548c2ecf20Sopenharmony_ci mutex_unlock(&i915->sb_lock); 5558c2ecf20Sopenharmony_ci return ret ? ret : status; 5568c2ecf20Sopenharmony_ci#undef COND 5578c2ecf20Sopenharmony_ci} 5588c2ecf20Sopenharmony_ci 5598c2ecf20Sopenharmony_civoid intel_pcode_init(struct drm_i915_private *i915) 5608c2ecf20Sopenharmony_ci{ 5618c2ecf20Sopenharmony_ci int ret; 5628c2ecf20Sopenharmony_ci 5638c2ecf20Sopenharmony_ci if (!IS_DGFX(i915)) 5648c2ecf20Sopenharmony_ci return; 5658c2ecf20Sopenharmony_ci 5668c2ecf20Sopenharmony_ci ret = skl_pcode_request(i915, DG1_PCODE_STATUS, 5678c2ecf20Sopenharmony_ci DG1_UNCORE_GET_INIT_STATUS, 5688c2ecf20Sopenharmony_ci DG1_UNCORE_INIT_STATUS_COMPLETE, 5698c2ecf20Sopenharmony_ci DG1_UNCORE_INIT_STATUS_COMPLETE, 50); 5708c2ecf20Sopenharmony_ci if (ret) 5718c2ecf20Sopenharmony_ci drm_err(&i915->drm, "Pcode did not report uncore initialization completion!\n"); 5728c2ecf20Sopenharmony_ci} 573