xref: /linux/drivers/gpu/drm/i915/vlv_iosf_sb.c (revision 8d2b0853add1d7534dc0794e3c8e0b9e8c4ec640)
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