1 // SPDX-License-Identifier: MIT
2 /*
3 * Copyright © 2023 Intel Corporation
4 */
5
6 #include <linux/device.h>
7 #include <linux/kobject.h>
8 #include <linux/pci.h>
9 #include <linux/sysfs.h>
10
11 #include "xe_device.h"
12 #include "xe_device_sysfs.h"
13 #include "xe_mmio.h"
14 #include "xe_pcode_api.h"
15 #include "xe_pcode.h"
16 #include "xe_pm.h"
17
18 /**
19 * DOC: Xe device sysfs
20 * Xe driver requires exposing certain tunable knobs controlled by user space for
21 * each graphics device. Considering this, we need to add sysfs attributes at device
22 * level granularity.
23 * These sysfs attributes will be available under pci device kobj directory.
24 *
25 * vram_d3cold_threshold - Report/change vram used threshold(in MB) below
26 * which vram save/restore is permissible during runtime D3cold entry/exit.
27 */
28
29 static ssize_t
vram_d3cold_threshold_show(struct device * dev,struct device_attribute * attr,char * buf)30 vram_d3cold_threshold_show(struct device *dev,
31 struct device_attribute *attr, char *buf)
32 {
33 struct pci_dev *pdev = to_pci_dev(dev);
34 struct xe_device *xe = pdev_to_xe_device(pdev);
35 int ret;
36
37 xe_pm_runtime_get(xe);
38 ret = sysfs_emit(buf, "%d\n", xe->d3cold.vram_threshold);
39 xe_pm_runtime_put(xe);
40
41 return ret;
42 }
43
44 static ssize_t
vram_d3cold_threshold_store(struct device * dev,struct device_attribute * attr,const char * buff,size_t count)45 vram_d3cold_threshold_store(struct device *dev, struct device_attribute *attr,
46 const char *buff, size_t count)
47 {
48 struct pci_dev *pdev = to_pci_dev(dev);
49 struct xe_device *xe = pdev_to_xe_device(pdev);
50 u32 vram_d3cold_threshold;
51 int ret;
52
53 ret = kstrtou32(buff, 0, &vram_d3cold_threshold);
54 if (ret)
55 return ret;
56
57 drm_dbg(&xe->drm, "vram_d3cold_threshold: %u\n", vram_d3cold_threshold);
58
59 xe_pm_runtime_get(xe);
60 ret = xe_pm_set_vram_threshold(xe, vram_d3cold_threshold);
61 xe_pm_runtime_put(xe);
62
63 return ret ?: count;
64 }
65
66 static DEVICE_ATTR_RW(vram_d3cold_threshold);
67
68 /**
69 * DOC: PCIe Gen5 Limitations
70 *
71 * Default link speed of discrete GPUs is determined by configuration parameters
72 * stored in their flash memory, which are subject to override through user
73 * initiated firmware updates. It has been observed that devices configured with
74 * PCIe Gen5 as their default link speed can come across link quality issues due
75 * to host or motherboard limitations and may have to auto-downgrade their link
76 * to PCIe Gen4 speed when faced with unstable link at Gen5, which makes
77 * firmware updates rather risky on such setups. It is required to ensure that
78 * the device is capable of auto-downgrading its link to PCIe Gen4 speed before
79 * pushing the firmware image with PCIe Gen5 as default configuration. This can
80 * be done by reading ``auto_link_downgrade_capable`` sysfs entry, which will
81 * denote if the device is capable of auto-downgrading its link to PCIe Gen4
82 * speed with boolean output value of ``0`` or ``1``, meaning `incapable` or
83 * `capable` respectively.
84 *
85 * .. code-block:: shell
86 *
87 * $ cat /sys/bus/pci/devices/<bdf>/auto_link_downgrade_capable
88 *
89 * Pushing the firmware image with PCIe Gen5 as default configuration on a auto
90 * link downgrade incapable device and facing link instability due to host or
91 * motherboard limitations can result in driver failing to bind to the device,
92 * making further firmware updates impossible with RMA being the only last
93 * resort.
94 *
95 * Link downgrade status of auto link downgrade capable devices is available
96 * through ``auto_link_downgrade_status`` sysfs entry with boolean output value
97 * of ``0`` or ``1``, where ``0`` means no auto-downgrading was required during
98 * link training (which is the optimal scenario) and ``1`` means the device has
99 * auto-downgraded its link to PCIe Gen4 speed due to unstable Gen5 link.
100 *
101 * .. code-block:: shell
102 *
103 * $ cat /sys/bus/pci/devices/<bdf>/auto_link_downgrade_status
104 */
105
106 static ssize_t
auto_link_downgrade_capable_show(struct device * dev,struct device_attribute * attr,char * buf)107 auto_link_downgrade_capable_show(struct device *dev, struct device_attribute *attr, char *buf)
108 {
109 struct pci_dev *pdev = to_pci_dev(dev);
110 struct xe_device *xe = pdev_to_xe_device(pdev);
111 u32 cap, val;
112
113 xe_pm_runtime_get(xe);
114 val = xe_mmio_read32(xe_root_tile_mmio(xe), BMG_PCIE_CAP);
115 xe_pm_runtime_put(xe);
116
117 cap = REG_FIELD_GET(LINK_DOWNGRADE, val);
118 return sysfs_emit(buf, "%u\n", cap == DOWNGRADE_CAPABLE);
119 }
120 static DEVICE_ATTR_ADMIN_RO(auto_link_downgrade_capable);
121
122 static ssize_t
auto_link_downgrade_status_show(struct device * dev,struct device_attribute * attr,char * buf)123 auto_link_downgrade_status_show(struct device *dev, struct device_attribute *attr, char *buf)
124 {
125 struct pci_dev *pdev = to_pci_dev(dev);
126 struct xe_device *xe = pdev_to_xe_device(pdev);
127 /* default the auto_link_downgrade status to 0 */
128 u32 val = 0;
129 int ret;
130
131 xe_pm_runtime_get(xe);
132 ret = xe_pcode_read(xe_device_get_root_tile(xe),
133 PCODE_MBOX(DGFX_PCODE_STATUS, DGFX_GET_INIT_STATUS, 0),
134 &val, NULL);
135 xe_pm_runtime_put(xe);
136
137 return ret ?: sysfs_emit(buf, "%u\n", REG_FIELD_GET(DGFX_LINK_DOWNGRADE_STATUS, val));
138 }
139 static DEVICE_ATTR_ADMIN_RO(auto_link_downgrade_status);
140
141 static const struct attribute *auto_link_downgrade_attrs[] = {
142 &dev_attr_auto_link_downgrade_capable.attr,
143 &dev_attr_auto_link_downgrade_status.attr,
144 NULL
145 };
146
xe_device_sysfs_fini(void * arg)147 static void xe_device_sysfs_fini(void *arg)
148 {
149 struct xe_device *xe = arg;
150
151 if (xe->d3cold.capable)
152 sysfs_remove_file(&xe->drm.dev->kobj, &dev_attr_vram_d3cold_threshold.attr);
153
154 if (xe->info.platform == XE_BATTLEMAGE)
155 sysfs_remove_files(&xe->drm.dev->kobj, auto_link_downgrade_attrs);
156 }
157
xe_device_sysfs_init(struct xe_device * xe)158 int xe_device_sysfs_init(struct xe_device *xe)
159 {
160 struct device *dev = xe->drm.dev;
161 int ret;
162
163 if (xe->d3cold.capable) {
164 ret = sysfs_create_file(&dev->kobj, &dev_attr_vram_d3cold_threshold.attr);
165 if (ret)
166 return ret;
167 }
168
169 if (xe->info.platform == XE_BATTLEMAGE) {
170 ret = sysfs_create_files(&dev->kobj, auto_link_downgrade_attrs);
171 if (ret)
172 return ret;
173 }
174
175 return devm_add_action_or_reset(dev, xe_device_sysfs_fini, xe);
176 }
177