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 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 171 void xe_amc_handle_alert(struct xe_i2c *i2c) 172 { 173 queue_work(system_long_wq, &i2c->amc->work); 174 } 175 176 int xe_amc_init(struct xe_i2c *i2c) 177 { 178 struct xe_amc *amc; 179 180 amc = kzalloc(sizeof(*amc), GFP_KERNEL); 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 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