1 // SPDX-License-Identifier: GPL-2.0
2 /*
3 * Copyright (C) 2026 Intel Corporation.
4 */
5
6 #include <linux/delay.h>
7 #include <linux/dev_printk.h>
8 #include <linux/err.h>
9 #include <linux/i2c.h>
10 #include <linux/pci_ids.h>
11 #include <linux/slab.h>
12 #include <linux/string.h>
13 #include <linux/workqueue.h>
14
15 #include "regs/xe_i2c_regs.h"
16
17 #include "xe_amc.h"
18 #include "xe_device.h"
19 #include "xe_i2c.h"
20 #include "xe_mmio.h"
21
22 /**
23 * DOC: Add-In Management Controller (AMC)
24 *
25 * Handler for the SMBus Alerts from the AMC. All the alerts from AMC will cause
26 * the device to be declared wedged.
27 */
28
29 #define AMC_COMMAND 0x0f
30 #define AMC_GPU_I2C_ADDR 0x8f
31 #define AMC_VERSION_V1 0x01
32 #define AMC_DESTINATION_ID 12
33 #define AMC_SOURCE_ID 8
34 #define AMC_FLAGS 0xc8
35
36 #define AMC_MSG_TYPE 0x7e
37 #define AMC_GET_ALERT_REASON 0x01
38
39 enum xe_amc_alert {
40 AMC_ALERT_UNKNOWN,
41 AMC_ALERT_FW_DOWNLOAD,
42 AMC_ALERT_THERMAL_TRIP,
43 AMC_ALERT_OOB_REQUEST,
44 AMC_ALERT_OOB_RESET,
45 AMC_ALERT_CATERR,
46 };
47
48 static const char * const amc_alert[] = {
49 [AMC_ALERT_FW_DOWNLOAD] = "Firmware Download",
50 [AMC_ALERT_THERMAL_TRIP] = "Thermal Trip",
51 [AMC_ALERT_OOB_REQUEST] = "OOB Request",
52 [AMC_ALERT_OOB_RESET] = "OOB Reset",
53 [AMC_ALERT_CATERR] = "Catastrophic",
54 };
55
56 struct xe_amc {
57 struct xe_i2c *i2c;
58 struct work_struct work;
59 };
60
61 struct amc_header {
62 u8 command;
63 u8 len;
64 u8 address;
65 u8 version;
66 u8 destination;
67 u8 source;
68 u8 flags;
69 } __packed;
70
71 struct amc_message {
72 u8 type;
73 u16 vendor;
74 u8 command;
75 } __packed;
76
77 struct amc_request {
78 struct amc_header header;
79 struct amc_message message;
80 u32 reserved;
81 } __packed;
82
83 struct amc_response {
84 struct amc_header header;
85 struct amc_message message;
86 u8 error;
87 u8 value;
88 } __packed;
89
90 static const struct amc_request amc_get_alert_reason = {
91 .header = {
92 .command = AMC_COMMAND,
93 .len = sizeof(struct amc_request) - 2,
94 .address = AMC_GPU_I2C_ADDR,
95 .version = AMC_VERSION_V1,
96 .destination = AMC_DESTINATION_ID,
97 .source = AMC_SOURCE_ID,
98 .flags = AMC_FLAGS,
99 },
100 .message = {
101 .type = AMC_MSG_TYPE,
102 .vendor = htons(PCI_VENDOR_ID_INTEL),
103 .command = AMC_GET_ALERT_REASON,
104 },
105 };
106
xe_amc_work(struct work_struct * work)107 static void xe_amc_work(struct work_struct *work)
108 {
109 const struct amc_request *request = &amc_get_alert_reason;
110 struct xe_amc *amc = from_work(amc, work, work);
111 u8 alert_reason = AMC_ALERT_UNKNOWN;
112 struct amc_response response;
113 struct i2c_client *client;
114 int ret;
115
116 client = amc->i2c->client[XE_I2C_CLIENT_AMC];
117 if (IS_ERR_OR_NULL(client))
118 goto out_reassert_interrupt;
119
120 ret = i2c_master_send(client, (u8 *)request, sizeof(*request));
121 if (ret < 0) {
122 dev_err(&client->dev, "failed to send request (%d)\n", ret);
123 goto out_reassert_interrupt;
124 }
125
126 /* AMC needs 20ms to generate the response. */
127 fsleep(20 * USEC_PER_MSEC);
128
129 ret = i2c_master_recv(client, (u8 *)&response, sizeof(response));
130 if (ret < 0) {
131 dev_err(&client->dev, "failed to read response (%d)\n", ret);
132 goto out_reassert_interrupt;
133 }
134
135 if (!response.header.len) {
136 dev_err(&client->dev, "empty response from AMC\n");
137 goto out_reassert_interrupt;
138 }
139
140 if (memcmp(&response.message, &request->message, sizeof(struct amc_message))) {
141 dev_err(&client->dev, "response does not match the request\n");
142 goto out_reassert_interrupt;
143 }
144
145 if (response.error) {
146 dev_err(&client->dev, "AMC error 0x%02x\n", response.error);
147 goto out_reassert_interrupt;
148 }
149
150 alert_reason = response.value;
151 dev_dbg(&client->dev, "Alert reason: %d\n", alert_reason);
152
153 out_reassert_interrupt:
154 xe_mmio_rmw32(amc->i2c->mmio, I2C_CONFIG_CMD, PCI_COMMAND_INTX_DISABLE, 0);
155
156 switch (alert_reason) {
157 case AMC_ALERT_FW_DOWNLOAD:
158 case AMC_ALERT_THERMAL_TRIP:
159 case AMC_ALERT_OOB_REQUEST:
160 case AMC_ALERT_OOB_RESET:
161 case AMC_ALERT_CATERR:
162 dev_warn(amc->i2c->drm_dev, "AMC Alert: %s\n", amc_alert[alert_reason]);
163 xe_device_declare_wedged(i2c_client_to_xe_device(client));
164 break;
165 default:
166 dev_warn(amc->i2c->drm_dev, "unknown AMC alert: %d\n", alert_reason);
167 break;
168 }
169 }
170
xe_amc_handle_alert(struct xe_i2c * i2c)171 void xe_amc_handle_alert(struct xe_i2c *i2c)
172 {
173 queue_work(system_long_wq, &i2c->amc->work);
174 }
175
xe_amc_init(struct xe_i2c * i2c)176 int xe_amc_init(struct xe_i2c *i2c)
177 {
178 struct xe_amc *amc;
179
180 amc = kzalloc_obj(*amc);
181 if (!amc)
182 return -ENOMEM;
183
184 INIT_WORK(&amc->work, xe_amc_work);
185 i2c->amc = amc;
186 amc->i2c = i2c;
187
188 return 0;
189 }
190
xe_amc_exit(struct xe_i2c * i2c)191 void xe_amc_exit(struct xe_i2c *i2c)
192 {
193 if (i2c->amc) {
194 cancel_work_sync(&i2c->amc->work);
195 kfree(i2c->amc);
196 }
197 }
198