| .. | .. |
|---|
| 7 | 7 | |
|---|
| 8 | 8 | #include "a6xx_gmu.h" |
|---|
| 9 | 9 | #include "a6xx_gmu.xml.h" |
|---|
| 10 | +#include "a6xx_gpu.h" |
|---|
| 10 | 11 | |
|---|
| 11 | 12 | #define HFI_MSG_ID(val) [val] = #val |
|---|
| 12 | 13 | |
|---|
| .. | .. |
|---|
| 16 | 17 | HFI_MSG_ID(HFI_H2F_MSG_BW_TABLE), |
|---|
| 17 | 18 | HFI_MSG_ID(HFI_H2F_MSG_PERF_TABLE), |
|---|
| 18 | 19 | HFI_MSG_ID(HFI_H2F_MSG_TEST), |
|---|
| 20 | + HFI_MSG_ID(HFI_H2F_MSG_START), |
|---|
| 21 | + HFI_MSG_ID(HFI_H2F_MSG_CORE_FW_START), |
|---|
| 22 | + HFI_MSG_ID(HFI_H2F_MSG_GX_BW_PERF_VOTE), |
|---|
| 23 | + HFI_MSG_ID(HFI_H2F_MSG_PREPARE_SLUMBER), |
|---|
| 19 | 24 | }; |
|---|
| 20 | 25 | |
|---|
| 21 | | -static int a6xx_hfi_queue_read(struct a6xx_hfi_queue *queue, u32 *data, |
|---|
| 22 | | - u32 dwords) |
|---|
| 26 | +static int a6xx_hfi_queue_read(struct a6xx_gmu *gmu, |
|---|
| 27 | + struct a6xx_hfi_queue *queue, u32 *data, u32 dwords) |
|---|
| 23 | 28 | { |
|---|
| 24 | 29 | struct a6xx_hfi_queue_header *header = queue->header; |
|---|
| 25 | 30 | u32 i, hdr, index = header->read_index; |
|---|
| .. | .. |
|---|
| 47 | 52 | index = (index + 1) % header->size; |
|---|
| 48 | 53 | } |
|---|
| 49 | 54 | |
|---|
| 55 | + if (!gmu->legacy) |
|---|
| 56 | + index = ALIGN(index, 4) % header->size; |
|---|
| 57 | + |
|---|
| 50 | 58 | header->read_index = index; |
|---|
| 51 | 59 | return HFI_HEADER_SIZE(hdr); |
|---|
| 52 | 60 | } |
|---|
| .. | .. |
|---|
| 72 | 80 | index = (index + 1) % header->size; |
|---|
| 73 | 81 | } |
|---|
| 74 | 82 | |
|---|
| 83 | + /* Cookify any non used data at the end of the write buffer */ |
|---|
| 84 | + if (!gmu->legacy) { |
|---|
| 85 | + for (; index % 4; index = (index + 1) % header->size) |
|---|
| 86 | + queue->data[index] = 0xfafafafa; |
|---|
| 87 | + } |
|---|
| 88 | + |
|---|
| 75 | 89 | header->write_index = index; |
|---|
| 76 | 90 | spin_unlock(&queue->lock); |
|---|
| 77 | 91 | |
|---|
| .. | .. |
|---|
| 79 | 93 | return 0; |
|---|
| 80 | 94 | } |
|---|
| 81 | 95 | |
|---|
| 82 | | -struct a6xx_hfi_response { |
|---|
| 83 | | - u32 id; |
|---|
| 84 | | - u32 seqnum; |
|---|
| 85 | | - struct list_head node; |
|---|
| 86 | | - struct completion complete; |
|---|
| 87 | | - |
|---|
| 88 | | - u32 error; |
|---|
| 89 | | - u32 payload[16]; |
|---|
| 90 | | -}; |
|---|
| 91 | | - |
|---|
| 92 | | -/* |
|---|
| 93 | | - * Incoming HFI ack messages can come in out of order so we need to store all |
|---|
| 94 | | - * the pending messages on a list until they are handled. |
|---|
| 95 | | - */ |
|---|
| 96 | | -static spinlock_t hfi_ack_lock = __SPIN_LOCK_UNLOCKED(message_lock); |
|---|
| 97 | | -static LIST_HEAD(hfi_ack_list); |
|---|
| 98 | | - |
|---|
| 99 | | -static void a6xx_hfi_handle_ack(struct a6xx_gmu *gmu, |
|---|
| 100 | | - struct a6xx_hfi_msg_response *msg) |
|---|
| 96 | +static int a6xx_hfi_wait_for_ack(struct a6xx_gmu *gmu, u32 id, u32 seqnum, |
|---|
| 97 | + u32 *payload, u32 payload_size) |
|---|
| 101 | 98 | { |
|---|
| 102 | | - struct a6xx_hfi_response *resp; |
|---|
| 103 | | - u32 id, seqnum; |
|---|
| 104 | | - |
|---|
| 105 | | - /* msg->ret_header contains the header of the message being acked */ |
|---|
| 106 | | - id = HFI_HEADER_ID(msg->ret_header); |
|---|
| 107 | | - seqnum = HFI_HEADER_SEQNUM(msg->ret_header); |
|---|
| 108 | | - |
|---|
| 109 | | - spin_lock(&hfi_ack_lock); |
|---|
| 110 | | - list_for_each_entry(resp, &hfi_ack_list, node) { |
|---|
| 111 | | - if (resp->id == id && resp->seqnum == seqnum) { |
|---|
| 112 | | - resp->error = msg->error; |
|---|
| 113 | | - memcpy(resp->payload, msg->payload, |
|---|
| 114 | | - sizeof(resp->payload)); |
|---|
| 115 | | - |
|---|
| 116 | | - complete(&resp->complete); |
|---|
| 117 | | - spin_unlock(&hfi_ack_lock); |
|---|
| 118 | | - return; |
|---|
| 119 | | - } |
|---|
| 120 | | - } |
|---|
| 121 | | - spin_unlock(&hfi_ack_lock); |
|---|
| 122 | | - |
|---|
| 123 | | - dev_err(gmu->dev, "Nobody was waiting for HFI message %d\n", seqnum); |
|---|
| 124 | | -} |
|---|
| 125 | | - |
|---|
| 126 | | -static void a6xx_hfi_handle_error(struct a6xx_gmu *gmu, |
|---|
| 127 | | - struct a6xx_hfi_msg_response *msg) |
|---|
| 128 | | -{ |
|---|
| 129 | | - struct a6xx_hfi_msg_error *error = (struct a6xx_hfi_msg_error *) msg; |
|---|
| 130 | | - |
|---|
| 131 | | - dev_err(gmu->dev, "GMU firmware error %d\n", error->code); |
|---|
| 132 | | -} |
|---|
| 133 | | - |
|---|
| 134 | | -void a6xx_hfi_task(unsigned long data) |
|---|
| 135 | | -{ |
|---|
| 136 | | - struct a6xx_gmu *gmu = (struct a6xx_gmu *) data; |
|---|
| 137 | 99 | struct a6xx_hfi_queue *queue = &gmu->queues[HFI_RESPONSE_QUEUE]; |
|---|
| 138 | | - struct a6xx_hfi_msg_response resp; |
|---|
| 100 | + u32 val; |
|---|
| 101 | + int ret; |
|---|
| 102 | + |
|---|
| 103 | + /* Wait for a response */ |
|---|
| 104 | + ret = gmu_poll_timeout(gmu, REG_A6XX_GMU_GMU2HOST_INTR_INFO, val, |
|---|
| 105 | + val & A6XX_GMU_GMU2HOST_INTR_INFO_MSGQ, 100, 5000); |
|---|
| 106 | + |
|---|
| 107 | + if (ret) { |
|---|
| 108 | + DRM_DEV_ERROR(gmu->dev, |
|---|
| 109 | + "Message %s id %d timed out waiting for response\n", |
|---|
| 110 | + a6xx_hfi_msg_id[id], seqnum); |
|---|
| 111 | + return -ETIMEDOUT; |
|---|
| 112 | + } |
|---|
| 113 | + |
|---|
| 114 | + /* Clear the interrupt */ |
|---|
| 115 | + gmu_write(gmu, REG_A6XX_GMU_GMU2HOST_INTR_CLR, |
|---|
| 116 | + A6XX_GMU_GMU2HOST_INTR_INFO_MSGQ); |
|---|
| 139 | 117 | |
|---|
| 140 | 118 | for (;;) { |
|---|
| 141 | | - u32 id; |
|---|
| 142 | | - int ret = a6xx_hfi_queue_read(queue, (u32 *) &resp, |
|---|
| 119 | + struct a6xx_hfi_msg_response resp; |
|---|
| 120 | + |
|---|
| 121 | + /* Get the next packet */ |
|---|
| 122 | + ret = a6xx_hfi_queue_read(gmu, queue, (u32 *) &resp, |
|---|
| 143 | 123 | sizeof(resp) >> 2); |
|---|
| 144 | 124 | |
|---|
| 145 | | - /* Returns the number of bytes copied or negative on error */ |
|---|
| 146 | | - if (ret <= 0) { |
|---|
| 147 | | - if (ret < 0) |
|---|
| 148 | | - dev_err(gmu->dev, |
|---|
| 149 | | - "Unable to read the HFI message queue\n"); |
|---|
| 150 | | - break; |
|---|
| 125 | + /* If the queue is empty our response never made it */ |
|---|
| 126 | + if (!ret) { |
|---|
| 127 | + DRM_DEV_ERROR(gmu->dev, |
|---|
| 128 | + "The HFI response queue is unexpectedly empty\n"); |
|---|
| 129 | + |
|---|
| 130 | + return -ENOENT; |
|---|
| 151 | 131 | } |
|---|
| 152 | 132 | |
|---|
| 153 | | - id = HFI_HEADER_ID(resp.header); |
|---|
| 133 | + if (HFI_HEADER_ID(resp.header) == HFI_F2H_MSG_ERROR) { |
|---|
| 134 | + struct a6xx_hfi_msg_error *error = |
|---|
| 135 | + (struct a6xx_hfi_msg_error *) &resp; |
|---|
| 154 | 136 | |
|---|
| 155 | | - if (id == HFI_F2H_MSG_ACK) |
|---|
| 156 | | - a6xx_hfi_handle_ack(gmu, &resp); |
|---|
| 157 | | - else if (id == HFI_F2H_MSG_ERROR) |
|---|
| 158 | | - a6xx_hfi_handle_error(gmu, &resp); |
|---|
| 137 | + DRM_DEV_ERROR(gmu->dev, "GMU firmware error %d\n", |
|---|
| 138 | + error->code); |
|---|
| 139 | + continue; |
|---|
| 140 | + } |
|---|
| 141 | + |
|---|
| 142 | + if (seqnum != HFI_HEADER_SEQNUM(resp.ret_header)) { |
|---|
| 143 | + DRM_DEV_ERROR(gmu->dev, |
|---|
| 144 | + "Unexpected message id %d on the response queue\n", |
|---|
| 145 | + HFI_HEADER_SEQNUM(resp.ret_header)); |
|---|
| 146 | + continue; |
|---|
| 147 | + } |
|---|
| 148 | + |
|---|
| 149 | + if (resp.error) { |
|---|
| 150 | + DRM_DEV_ERROR(gmu->dev, |
|---|
| 151 | + "Message %s id %d returned error %d\n", |
|---|
| 152 | + a6xx_hfi_msg_id[id], seqnum, resp.error); |
|---|
| 153 | + return -EINVAL; |
|---|
| 154 | + } |
|---|
| 155 | + |
|---|
| 156 | + /* All is well, copy over the buffer */ |
|---|
| 157 | + if (payload && payload_size) |
|---|
| 158 | + memcpy(payload, resp.payload, |
|---|
| 159 | + min_t(u32, payload_size, sizeof(resp.payload))); |
|---|
| 160 | + |
|---|
| 161 | + return 0; |
|---|
| 159 | 162 | } |
|---|
| 160 | 163 | } |
|---|
| 161 | 164 | |
|---|
| .. | .. |
|---|
| 163 | 166 | void *data, u32 size, u32 *payload, u32 payload_size) |
|---|
| 164 | 167 | { |
|---|
| 165 | 168 | struct a6xx_hfi_queue *queue = &gmu->queues[HFI_COMMAND_QUEUE]; |
|---|
| 166 | | - struct a6xx_hfi_response resp = { 0 }; |
|---|
| 167 | 169 | int ret, dwords = size >> 2; |
|---|
| 168 | 170 | u32 seqnum; |
|---|
| 169 | 171 | |
|---|
| .. | .. |
|---|
| 173 | 175 | *((u32 *) data) = (seqnum << 20) | (HFI_MSG_CMD << 16) | |
|---|
| 174 | 176 | (dwords << 8) | id; |
|---|
| 175 | 177 | |
|---|
| 176 | | - init_completion(&resp.complete); |
|---|
| 177 | | - resp.id = id; |
|---|
| 178 | | - resp.seqnum = seqnum; |
|---|
| 179 | | - |
|---|
| 180 | | - spin_lock_bh(&hfi_ack_lock); |
|---|
| 181 | | - list_add_tail(&resp.node, &hfi_ack_list); |
|---|
| 182 | | - spin_unlock_bh(&hfi_ack_lock); |
|---|
| 183 | | - |
|---|
| 184 | 178 | ret = a6xx_hfi_queue_write(gmu, queue, data, dwords); |
|---|
| 185 | 179 | if (ret) { |
|---|
| 186 | | - dev_err(gmu->dev, "Unable to send message %s id %d\n", |
|---|
| 180 | + DRM_DEV_ERROR(gmu->dev, "Unable to send message %s id %d\n", |
|---|
| 187 | 181 | a6xx_hfi_msg_id[id], seqnum); |
|---|
| 188 | | - goto out; |
|---|
| 189 | | - } |
|---|
| 190 | | - |
|---|
| 191 | | - /* Wait up to 5 seconds for the response */ |
|---|
| 192 | | - ret = wait_for_completion_timeout(&resp.complete, |
|---|
| 193 | | - msecs_to_jiffies(5000)); |
|---|
| 194 | | - if (!ret) { |
|---|
| 195 | | - dev_err(gmu->dev, |
|---|
| 196 | | - "Message %s id %d timed out waiting for response\n", |
|---|
| 197 | | - a6xx_hfi_msg_id[id], seqnum); |
|---|
| 198 | | - ret = -ETIMEDOUT; |
|---|
| 199 | | - } else |
|---|
| 200 | | - ret = 0; |
|---|
| 201 | | - |
|---|
| 202 | | -out: |
|---|
| 203 | | - spin_lock_bh(&hfi_ack_lock); |
|---|
| 204 | | - list_del(&resp.node); |
|---|
| 205 | | - spin_unlock_bh(&hfi_ack_lock); |
|---|
| 206 | | - |
|---|
| 207 | | - if (ret) |
|---|
| 208 | 182 | return ret; |
|---|
| 209 | | - |
|---|
| 210 | | - if (resp.error) { |
|---|
| 211 | | - dev_err(gmu->dev, "Message %s id %d returned error %d\n", |
|---|
| 212 | | - a6xx_hfi_msg_id[id], seqnum, resp.error); |
|---|
| 213 | | - return -EINVAL; |
|---|
| 214 | 183 | } |
|---|
| 215 | 184 | |
|---|
| 216 | | - if (payload && payload_size) { |
|---|
| 217 | | - int copy = min_t(u32, payload_size, sizeof(resp.payload)); |
|---|
| 218 | | - |
|---|
| 219 | | - memcpy(payload, resp.payload, copy); |
|---|
| 220 | | - } |
|---|
| 221 | | - |
|---|
| 222 | | - return 0; |
|---|
| 185 | + return a6xx_hfi_wait_for_ack(gmu, id, seqnum, payload, payload_size); |
|---|
| 223 | 186 | } |
|---|
| 224 | 187 | |
|---|
| 225 | 188 | static int a6xx_hfi_send_gmu_init(struct a6xx_gmu *gmu, int boot_state) |
|---|
| 226 | 189 | { |
|---|
| 227 | 190 | struct a6xx_hfi_msg_gmu_init_cmd msg = { 0 }; |
|---|
| 228 | 191 | |
|---|
| 229 | | - msg.dbg_buffer_addr = (u32) gmu->debug->iova; |
|---|
| 230 | | - msg.dbg_buffer_size = (u32) gmu->debug->size; |
|---|
| 192 | + msg.dbg_buffer_addr = (u32) gmu->debug.iova; |
|---|
| 193 | + msg.dbg_buffer_size = (u32) gmu->debug.size; |
|---|
| 231 | 194 | msg.boot_state = boot_state; |
|---|
| 232 | 195 | |
|---|
| 233 | 196 | return a6xx_hfi_send_msg(gmu, HFI_H2F_MSG_INIT, &msg, sizeof(msg), |
|---|
| .. | .. |
|---|
| 245 | 208 | version, sizeof(*version)); |
|---|
| 246 | 209 | } |
|---|
| 247 | 210 | |
|---|
| 248 | | -static int a6xx_hfi_send_perf_table(struct a6xx_gmu *gmu) |
|---|
| 211 | +static int a6xx_hfi_send_perf_table_v1(struct a6xx_gmu *gmu) |
|---|
| 249 | 212 | { |
|---|
| 250 | | - struct a6xx_hfi_msg_perf_table msg = { 0 }; |
|---|
| 213 | + struct a6xx_hfi_msg_perf_table_v1 msg = { 0 }; |
|---|
| 251 | 214 | int i; |
|---|
| 252 | 215 | |
|---|
| 253 | 216 | msg.num_gpu_levels = gmu->nr_gpu_freqs; |
|---|
| .. | .. |
|---|
| 267 | 230 | NULL, 0); |
|---|
| 268 | 231 | } |
|---|
| 269 | 232 | |
|---|
| 270 | | -static int a6xx_hfi_send_bw_table(struct a6xx_gmu *gmu) |
|---|
| 233 | +static int a6xx_hfi_send_perf_table(struct a6xx_gmu *gmu) |
|---|
| 271 | 234 | { |
|---|
| 272 | | - struct a6xx_hfi_msg_bw_table msg = { 0 }; |
|---|
| 235 | + struct a6xx_hfi_msg_perf_table msg = { 0 }; |
|---|
| 236 | + int i; |
|---|
| 237 | + |
|---|
| 238 | + msg.num_gpu_levels = gmu->nr_gpu_freqs; |
|---|
| 239 | + msg.num_gmu_levels = gmu->nr_gmu_freqs; |
|---|
| 240 | + |
|---|
| 241 | + for (i = 0; i < gmu->nr_gpu_freqs; i++) { |
|---|
| 242 | + msg.gx_votes[i].vote = gmu->gx_arc_votes[i]; |
|---|
| 243 | + msg.gx_votes[i].acd = 0xffffffff; |
|---|
| 244 | + msg.gx_votes[i].freq = gmu->gpu_freqs[i] / 1000; |
|---|
| 245 | + } |
|---|
| 246 | + |
|---|
| 247 | + for (i = 0; i < gmu->nr_gmu_freqs; i++) { |
|---|
| 248 | + msg.cx_votes[i].vote = gmu->cx_arc_votes[i]; |
|---|
| 249 | + msg.cx_votes[i].freq = gmu->gmu_freqs[i] / 1000; |
|---|
| 250 | + } |
|---|
| 251 | + |
|---|
| 252 | + return a6xx_hfi_send_msg(gmu, HFI_H2F_MSG_PERF_TABLE, &msg, sizeof(msg), |
|---|
| 253 | + NULL, 0); |
|---|
| 254 | +} |
|---|
| 255 | + |
|---|
| 256 | +static void a618_build_bw_table(struct a6xx_hfi_msg_bw_table *msg) |
|---|
| 257 | +{ |
|---|
| 258 | + /* Send a single "off" entry since the 618 GMU doesn't do bus scaling */ |
|---|
| 259 | + msg->bw_level_num = 1; |
|---|
| 260 | + |
|---|
| 261 | + msg->ddr_cmds_num = 3; |
|---|
| 262 | + msg->ddr_wait_bitmask = 0x01; |
|---|
| 263 | + |
|---|
| 264 | + msg->ddr_cmds_addrs[0] = 0x50000; |
|---|
| 265 | + msg->ddr_cmds_addrs[1] = 0x5003c; |
|---|
| 266 | + msg->ddr_cmds_addrs[2] = 0x5000c; |
|---|
| 267 | + |
|---|
| 268 | + msg->ddr_cmds_data[0][0] = 0x40000000; |
|---|
| 269 | + msg->ddr_cmds_data[0][1] = 0x40000000; |
|---|
| 270 | + msg->ddr_cmds_data[0][2] = 0x40000000; |
|---|
| 273 | 271 | |
|---|
| 274 | 272 | /* |
|---|
| 275 | | - * The sdm845 GMU doesn't do bus frequency scaling on its own but it |
|---|
| 276 | | - * does need at least one entry in the list because it might be accessed |
|---|
| 277 | | - * when the GMU is shutting down. Send a single "off" entry. |
|---|
| 273 | + * These are the CX (CNOC) votes - these are used by the GMU but the |
|---|
| 274 | + * votes are known and fixed for the target |
|---|
| 278 | 275 | */ |
|---|
| 276 | + msg->cnoc_cmds_num = 1; |
|---|
| 277 | + msg->cnoc_wait_bitmask = 0x01; |
|---|
| 279 | 278 | |
|---|
| 280 | | - msg.bw_level_num = 1; |
|---|
| 279 | + msg->cnoc_cmds_addrs[0] = 0x5007c; |
|---|
| 280 | + msg->cnoc_cmds_data[0][0] = 0x40000000; |
|---|
| 281 | + msg->cnoc_cmds_data[1][0] = 0x60000001; |
|---|
| 282 | +} |
|---|
| 281 | 283 | |
|---|
| 282 | | - msg.ddr_cmds_num = 3; |
|---|
| 283 | | - msg.ddr_wait_bitmask = 0x07; |
|---|
| 284 | +static void a640_build_bw_table(struct a6xx_hfi_msg_bw_table *msg) |
|---|
| 285 | +{ |
|---|
| 286 | + /* |
|---|
| 287 | + * Send a single "off" entry just to get things running |
|---|
| 288 | + * TODO: bus scaling |
|---|
| 289 | + */ |
|---|
| 290 | + msg->bw_level_num = 1; |
|---|
| 284 | 291 | |
|---|
| 285 | | - msg.ddr_cmds_addrs[0] = 0x50000; |
|---|
| 286 | | - msg.ddr_cmds_addrs[1] = 0x5005c; |
|---|
| 287 | | - msg.ddr_cmds_addrs[2] = 0x5000c; |
|---|
| 292 | + msg->ddr_cmds_num = 3; |
|---|
| 293 | + msg->ddr_wait_bitmask = 0x01; |
|---|
| 288 | 294 | |
|---|
| 289 | | - msg.ddr_cmds_data[0][0] = 0x40000000; |
|---|
| 290 | | - msg.ddr_cmds_data[0][1] = 0x40000000; |
|---|
| 291 | | - msg.ddr_cmds_data[0][2] = 0x40000000; |
|---|
| 295 | + msg->ddr_cmds_addrs[0] = 0x50000; |
|---|
| 296 | + msg->ddr_cmds_addrs[1] = 0x5003c; |
|---|
| 297 | + msg->ddr_cmds_addrs[2] = 0x5000c; |
|---|
| 298 | + |
|---|
| 299 | + msg->ddr_cmds_data[0][0] = 0x40000000; |
|---|
| 300 | + msg->ddr_cmds_data[0][1] = 0x40000000; |
|---|
| 301 | + msg->ddr_cmds_data[0][2] = 0x40000000; |
|---|
| 302 | + |
|---|
| 303 | + /* |
|---|
| 304 | + * These are the CX (CNOC) votes - these are used by the GMU but the |
|---|
| 305 | + * votes are known and fixed for the target |
|---|
| 306 | + */ |
|---|
| 307 | + msg->cnoc_cmds_num = 3; |
|---|
| 308 | + msg->cnoc_wait_bitmask = 0x01; |
|---|
| 309 | + |
|---|
| 310 | + msg->cnoc_cmds_addrs[0] = 0x50034; |
|---|
| 311 | + msg->cnoc_cmds_addrs[1] = 0x5007c; |
|---|
| 312 | + msg->cnoc_cmds_addrs[2] = 0x5004c; |
|---|
| 313 | + |
|---|
| 314 | + msg->cnoc_cmds_data[0][0] = 0x40000000; |
|---|
| 315 | + msg->cnoc_cmds_data[0][1] = 0x00000000; |
|---|
| 316 | + msg->cnoc_cmds_data[0][2] = 0x40000000; |
|---|
| 317 | + |
|---|
| 318 | + msg->cnoc_cmds_data[1][0] = 0x60000001; |
|---|
| 319 | + msg->cnoc_cmds_data[1][1] = 0x20000001; |
|---|
| 320 | + msg->cnoc_cmds_data[1][2] = 0x60000001; |
|---|
| 321 | +} |
|---|
| 322 | + |
|---|
| 323 | +static void a650_build_bw_table(struct a6xx_hfi_msg_bw_table *msg) |
|---|
| 324 | +{ |
|---|
| 325 | + /* |
|---|
| 326 | + * Send a single "off" entry just to get things running |
|---|
| 327 | + * TODO: bus scaling |
|---|
| 328 | + */ |
|---|
| 329 | + msg->bw_level_num = 1; |
|---|
| 330 | + |
|---|
| 331 | + msg->ddr_cmds_num = 3; |
|---|
| 332 | + msg->ddr_wait_bitmask = 0x01; |
|---|
| 333 | + |
|---|
| 334 | + msg->ddr_cmds_addrs[0] = 0x50000; |
|---|
| 335 | + msg->ddr_cmds_addrs[1] = 0x50004; |
|---|
| 336 | + msg->ddr_cmds_addrs[2] = 0x5007c; |
|---|
| 337 | + |
|---|
| 338 | + msg->ddr_cmds_data[0][0] = 0x40000000; |
|---|
| 339 | + msg->ddr_cmds_data[0][1] = 0x40000000; |
|---|
| 340 | + msg->ddr_cmds_data[0][2] = 0x40000000; |
|---|
| 341 | + |
|---|
| 342 | + /* |
|---|
| 343 | + * These are the CX (CNOC) votes - these are used by the GMU but the |
|---|
| 344 | + * votes are known and fixed for the target |
|---|
| 345 | + */ |
|---|
| 346 | + msg->cnoc_cmds_num = 1; |
|---|
| 347 | + msg->cnoc_wait_bitmask = 0x01; |
|---|
| 348 | + |
|---|
| 349 | + msg->cnoc_cmds_addrs[0] = 0x500a4; |
|---|
| 350 | + msg->cnoc_cmds_data[0][0] = 0x40000000; |
|---|
| 351 | + msg->cnoc_cmds_data[1][0] = 0x60000001; |
|---|
| 352 | +} |
|---|
| 353 | + |
|---|
| 354 | +static void a6xx_build_bw_table(struct a6xx_hfi_msg_bw_table *msg) |
|---|
| 355 | +{ |
|---|
| 356 | + /* Send a single "off" entry since the 630 GMU doesn't do bus scaling */ |
|---|
| 357 | + msg->bw_level_num = 1; |
|---|
| 358 | + |
|---|
| 359 | + msg->ddr_cmds_num = 3; |
|---|
| 360 | + msg->ddr_wait_bitmask = 0x07; |
|---|
| 361 | + |
|---|
| 362 | + msg->ddr_cmds_addrs[0] = 0x50000; |
|---|
| 363 | + msg->ddr_cmds_addrs[1] = 0x5005c; |
|---|
| 364 | + msg->ddr_cmds_addrs[2] = 0x5000c; |
|---|
| 365 | + |
|---|
| 366 | + msg->ddr_cmds_data[0][0] = 0x40000000; |
|---|
| 367 | + msg->ddr_cmds_data[0][1] = 0x40000000; |
|---|
| 368 | + msg->ddr_cmds_data[0][2] = 0x40000000; |
|---|
| 292 | 369 | |
|---|
| 293 | 370 | /* |
|---|
| 294 | 371 | * These are the CX (CNOC) votes. This is used but the values for the |
|---|
| 295 | 372 | * sdm845 GMU are known and fixed so we can hard code them. |
|---|
| 296 | 373 | */ |
|---|
| 297 | 374 | |
|---|
| 298 | | - msg.cnoc_cmds_num = 3; |
|---|
| 299 | | - msg.cnoc_wait_bitmask = 0x05; |
|---|
| 375 | + msg->cnoc_cmds_num = 3; |
|---|
| 376 | + msg->cnoc_wait_bitmask = 0x05; |
|---|
| 300 | 377 | |
|---|
| 301 | | - msg.cnoc_cmds_addrs[0] = 0x50034; |
|---|
| 302 | | - msg.cnoc_cmds_addrs[1] = 0x5007c; |
|---|
| 303 | | - msg.cnoc_cmds_addrs[2] = 0x5004c; |
|---|
| 378 | + msg->cnoc_cmds_addrs[0] = 0x50034; |
|---|
| 379 | + msg->cnoc_cmds_addrs[1] = 0x5007c; |
|---|
| 380 | + msg->cnoc_cmds_addrs[2] = 0x5004c; |
|---|
| 304 | 381 | |
|---|
| 305 | | - msg.cnoc_cmds_data[0][0] = 0x40000000; |
|---|
| 306 | | - msg.cnoc_cmds_data[0][1] = 0x00000000; |
|---|
| 307 | | - msg.cnoc_cmds_data[0][2] = 0x40000000; |
|---|
| 382 | + msg->cnoc_cmds_data[0][0] = 0x40000000; |
|---|
| 383 | + msg->cnoc_cmds_data[0][1] = 0x00000000; |
|---|
| 384 | + msg->cnoc_cmds_data[0][2] = 0x40000000; |
|---|
| 308 | 385 | |
|---|
| 309 | | - msg.cnoc_cmds_data[1][0] = 0x60000001; |
|---|
| 310 | | - msg.cnoc_cmds_data[1][1] = 0x20000001; |
|---|
| 311 | | - msg.cnoc_cmds_data[1][2] = 0x60000001; |
|---|
| 386 | + msg->cnoc_cmds_data[1][0] = 0x60000001; |
|---|
| 387 | + msg->cnoc_cmds_data[1][1] = 0x20000001; |
|---|
| 388 | + msg->cnoc_cmds_data[1][2] = 0x60000001; |
|---|
| 389 | +} |
|---|
| 390 | + |
|---|
| 391 | + |
|---|
| 392 | +static int a6xx_hfi_send_bw_table(struct a6xx_gmu *gmu) |
|---|
| 393 | +{ |
|---|
| 394 | + struct a6xx_hfi_msg_bw_table msg = { 0 }; |
|---|
| 395 | + struct a6xx_gpu *a6xx_gpu = container_of(gmu, struct a6xx_gpu, gmu); |
|---|
| 396 | + struct adreno_gpu *adreno_gpu = &a6xx_gpu->base; |
|---|
| 397 | + |
|---|
| 398 | + if (adreno_is_a618(adreno_gpu)) |
|---|
| 399 | + a618_build_bw_table(&msg); |
|---|
| 400 | + else if (adreno_is_a640(adreno_gpu)) |
|---|
| 401 | + a640_build_bw_table(&msg); |
|---|
| 402 | + else if (adreno_is_a650(adreno_gpu)) |
|---|
| 403 | + a650_build_bw_table(&msg); |
|---|
| 404 | + else |
|---|
| 405 | + a6xx_build_bw_table(&msg); |
|---|
| 312 | 406 | |
|---|
| 313 | 407 | return a6xx_hfi_send_msg(gmu, HFI_H2F_MSG_BW_TABLE, &msg, sizeof(msg), |
|---|
| 314 | 408 | NULL, 0); |
|---|
| .. | .. |
|---|
| 322 | 416 | NULL, 0); |
|---|
| 323 | 417 | } |
|---|
| 324 | 418 | |
|---|
| 325 | | -int a6xx_hfi_start(struct a6xx_gmu *gmu, int boot_state) |
|---|
| 419 | +static int a6xx_hfi_send_start(struct a6xx_gmu *gmu) |
|---|
| 420 | +{ |
|---|
| 421 | + struct a6xx_hfi_msg_start msg = { 0 }; |
|---|
| 422 | + |
|---|
| 423 | + return a6xx_hfi_send_msg(gmu, HFI_H2F_MSG_START, &msg, sizeof(msg), |
|---|
| 424 | + NULL, 0); |
|---|
| 425 | +} |
|---|
| 426 | + |
|---|
| 427 | +static int a6xx_hfi_send_core_fw_start(struct a6xx_gmu *gmu) |
|---|
| 428 | +{ |
|---|
| 429 | + struct a6xx_hfi_msg_core_fw_start msg = { 0 }; |
|---|
| 430 | + |
|---|
| 431 | + return a6xx_hfi_send_msg(gmu, HFI_H2F_MSG_CORE_FW_START, &msg, |
|---|
| 432 | + sizeof(msg), NULL, 0); |
|---|
| 433 | +} |
|---|
| 434 | + |
|---|
| 435 | +int a6xx_hfi_set_freq(struct a6xx_gmu *gmu, int index) |
|---|
| 436 | +{ |
|---|
| 437 | + struct a6xx_hfi_gx_bw_perf_vote_cmd msg = { 0 }; |
|---|
| 438 | + |
|---|
| 439 | + msg.ack_type = 1; /* blocking */ |
|---|
| 440 | + msg.freq = index; |
|---|
| 441 | + msg.bw = 0; /* TODO: bus scaling */ |
|---|
| 442 | + |
|---|
| 443 | + return a6xx_hfi_send_msg(gmu, HFI_H2F_MSG_GX_BW_PERF_VOTE, &msg, |
|---|
| 444 | + sizeof(msg), NULL, 0); |
|---|
| 445 | +} |
|---|
| 446 | + |
|---|
| 447 | +int a6xx_hfi_send_prep_slumber(struct a6xx_gmu *gmu) |
|---|
| 448 | +{ |
|---|
| 449 | + struct a6xx_hfi_prep_slumber_cmd msg = { 0 }; |
|---|
| 450 | + |
|---|
| 451 | + /* TODO: should freq and bw fields be non-zero ? */ |
|---|
| 452 | + |
|---|
| 453 | + return a6xx_hfi_send_msg(gmu, HFI_H2F_MSG_PREPARE_SLUMBER, &msg, |
|---|
| 454 | + sizeof(msg), NULL, 0); |
|---|
| 455 | +} |
|---|
| 456 | + |
|---|
| 457 | +static int a6xx_hfi_start_v1(struct a6xx_gmu *gmu, int boot_state) |
|---|
| 326 | 458 | { |
|---|
| 327 | 459 | int ret; |
|---|
| 328 | 460 | |
|---|
| .. | .. |
|---|
| 340 | 472 | * the GMU firmware |
|---|
| 341 | 473 | */ |
|---|
| 342 | 474 | |
|---|
| 343 | | - ret = a6xx_hfi_send_perf_table(gmu); |
|---|
| 475 | + ret = a6xx_hfi_send_perf_table_v1(gmu); |
|---|
| 344 | 476 | if (ret) |
|---|
| 345 | 477 | return ret; |
|---|
| 346 | 478 | |
|---|
| .. | .. |
|---|
| 357 | 489 | return 0; |
|---|
| 358 | 490 | } |
|---|
| 359 | 491 | |
|---|
| 492 | +int a6xx_hfi_start(struct a6xx_gmu *gmu, int boot_state) |
|---|
| 493 | +{ |
|---|
| 494 | + int ret; |
|---|
| 495 | + |
|---|
| 496 | + if (gmu->legacy) |
|---|
| 497 | + return a6xx_hfi_start_v1(gmu, boot_state); |
|---|
| 498 | + |
|---|
| 499 | + |
|---|
| 500 | + ret = a6xx_hfi_send_perf_table(gmu); |
|---|
| 501 | + if (ret) |
|---|
| 502 | + return ret; |
|---|
| 503 | + |
|---|
| 504 | + ret = a6xx_hfi_send_bw_table(gmu); |
|---|
| 505 | + if (ret) |
|---|
| 506 | + return ret; |
|---|
| 507 | + |
|---|
| 508 | + ret = a6xx_hfi_send_core_fw_start(gmu); |
|---|
| 509 | + if (ret) |
|---|
| 510 | + return ret; |
|---|
| 511 | + |
|---|
| 512 | + /* |
|---|
| 513 | + * Downstream driver sends this in its "a6xx_hw_init" equivalent, |
|---|
| 514 | + * but seems to be no harm in sending it here |
|---|
| 515 | + */ |
|---|
| 516 | + ret = a6xx_hfi_send_start(gmu); |
|---|
| 517 | + if (ret) |
|---|
| 518 | + return ret; |
|---|
| 519 | + |
|---|
| 520 | + return 0; |
|---|
| 521 | +} |
|---|
| 522 | + |
|---|
| 360 | 523 | void a6xx_hfi_stop(struct a6xx_gmu *gmu) |
|---|
| 361 | 524 | { |
|---|
| 362 | 525 | int i; |
|---|
| .. | .. |
|---|
| 368 | 531 | continue; |
|---|
| 369 | 532 | |
|---|
| 370 | 533 | if (queue->header->read_index != queue->header->write_index) |
|---|
| 371 | | - dev_err(gmu->dev, "HFI queue %d is not empty\n", i); |
|---|
| 534 | + DRM_DEV_ERROR(gmu->dev, "HFI queue %d is not empty\n", i); |
|---|
| 372 | 535 | |
|---|
| 373 | 536 | queue->header->read_index = 0; |
|---|
| 374 | 537 | queue->header->write_index = 0; |
|---|
| .. | .. |
|---|
| 401 | 564 | |
|---|
| 402 | 565 | void a6xx_hfi_init(struct a6xx_gmu *gmu) |
|---|
| 403 | 566 | { |
|---|
| 404 | | - struct a6xx_gmu_bo *hfi = gmu->hfi; |
|---|
| 567 | + struct a6xx_gmu_bo *hfi = &gmu->hfi; |
|---|
| 405 | 568 | struct a6xx_hfi_queue_table_header *table = hfi->virt; |
|---|
| 406 | 569 | struct a6xx_hfi_queue_header *headers = hfi->virt + sizeof(*table); |
|---|
| 407 | 570 | u64 offset; |
|---|
| .. | .. |
|---|
| 431 | 594 | /* GMU response queue */ |
|---|
| 432 | 595 | offset += SZ_4K; |
|---|
| 433 | 596 | a6xx_hfi_queue_init(&gmu->queues[1], &headers[1], hfi->virt + offset, |
|---|
| 434 | | - hfi->iova + offset, 4); |
|---|
| 597 | + hfi->iova + offset, gmu->legacy ? 4 : 1); |
|---|
| 435 | 598 | } |
|---|