diff options
| author | Oded Gabbay <oded.gabbay@gmail.com> | 2019-02-15 17:39:17 -0500 |
|---|---|---|
| committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2019-02-18 03:46:45 -0500 |
| commit | 9494a8dd8d22cbff8ce358aaa223fffe1b070cb0 (patch) | |
| tree | 8a7b51e6440aa5248026140fa33d42172dde26de | |
| parent | 839c48030d27a690cc85f0762f9f6f07a3349fb3 (diff) | |
habanalabs: add h/w queues module
This patch adds the H/W queues module and the code to initialize Goya's
various compute and DMA engines and their queues.
Goya has 5 DMA channels, 8 TPC engines and a single MME engine. For each
channel/engine, there is a H/W queue logic which is used to pass commands
from the user to the H/W. That logic is called QMAN.
There are two types of QMANs: external and internal. The DMA QMANs are
considered external while the TPC and MME QMANs are considered internal.
For each external queue there is a completion queue, which is located on
the Host memory.
The differences between external and internal QMANs are:
1. The location of the queue's memory. External QMANs are located on the
Host memory while internal QMANs are located on the on-chip memory.
2. The external QMAN write an entry to a completion queue and sends an
MSI-X interrupt upon completion of a command buffer that was given to
it. The internal QMAN doesn't do that.
Reviewed-by: Mike Rapoport <rppt@linux.ibm.com>
Signed-off-by: Oded Gabbay <oded.gabbay@gmail.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
| -rw-r--r-- | drivers/misc/habanalabs/Makefile | 2 | ||||
| -rw-r--r-- | drivers/misc/habanalabs/device.c | 75 | ||||
| -rw-r--r-- | drivers/misc/habanalabs/goya/goya.c | 1311 | ||||
| -rw-r--r-- | drivers/misc/habanalabs/goya/goyaP.h | 7 | ||||
| -rw-r--r-- | drivers/misc/habanalabs/habanalabs.h | 174 | ||||
| -rw-r--r-- | drivers/misc/habanalabs/habanalabs_drv.c | 5 | ||||
| -rw-r--r-- | drivers/misc/habanalabs/hw_queue.c | 400 | ||||
| -rw-r--r-- | drivers/misc/habanalabs/include/armcp_if.h | 292 | ||||
| -rw-r--r-- | drivers/misc/habanalabs/include/goya/goya_async_events.h | 186 | ||||
| -rw-r--r-- | drivers/misc/habanalabs/include/goya/goya_packets.h | 129 | ||||
| -rw-r--r-- | drivers/misc/habanalabs/include/qman_if.h | 56 | ||||
| -rw-r--r-- | drivers/misc/habanalabs/irq.c | 149 | ||||
| -rw-r--r-- | include/uapi/misc/habanalabs.h | 29 |
13 files changed, 2807 insertions, 8 deletions
diff --git a/drivers/misc/habanalabs/Makefile b/drivers/misc/habanalabs/Makefile index 2530c9b78ca4..c07f3ccb57dc 100644 --- a/drivers/misc/habanalabs/Makefile +++ b/drivers/misc/habanalabs/Makefile | |||
| @@ -5,7 +5,7 @@ | |||
| 5 | obj-m := habanalabs.o | 5 | obj-m := habanalabs.o |
| 6 | 6 | ||
| 7 | habanalabs-y := habanalabs_drv.o device.o context.o asid.o habanalabs_ioctl.o \ | 7 | habanalabs-y := habanalabs_drv.o device.o context.o asid.o habanalabs_ioctl.o \ |
| 8 | command_buffer.o | 8 | command_buffer.o hw_queue.o irq.o |
| 9 | 9 | ||
| 10 | include $(src)/goya/Makefile | 10 | include $(src)/goya/Makefile |
| 11 | habanalabs-y += $(HL_GOYA_FILES) | 11 | habanalabs-y += $(HL_GOYA_FILES) |
diff --git a/drivers/misc/habanalabs/device.c b/drivers/misc/habanalabs/device.c index 5eefc2952be5..06e2b7f32499 100644 --- a/drivers/misc/habanalabs/device.c +++ b/drivers/misc/habanalabs/device.c | |||
| @@ -174,13 +174,23 @@ static int device_early_init(struct hl_device *hdev) | |||
| 174 | if (rc) | 174 | if (rc) |
| 175 | goto early_fini; | 175 | goto early_fini; |
| 176 | 176 | ||
| 177 | hdev->cq_wq = alloc_workqueue("hl-free-jobs", WQ_UNBOUND, 0); | ||
| 178 | if (hdev->cq_wq == NULL) { | ||
| 179 | dev_err(hdev->dev, "Failed to allocate CQ workqueue\n"); | ||
| 180 | rc = -ENOMEM; | ||
| 181 | goto asid_fini; | ||
| 182 | } | ||
| 183 | |||
| 177 | hl_cb_mgr_init(&hdev->kernel_cb_mgr); | 184 | hl_cb_mgr_init(&hdev->kernel_cb_mgr); |
| 178 | 185 | ||
| 179 | mutex_init(&hdev->fd_open_cnt_lock); | 186 | mutex_init(&hdev->fd_open_cnt_lock); |
| 187 | mutex_init(&hdev->send_cpu_message_lock); | ||
| 180 | atomic_set(&hdev->fd_open_cnt, 0); | 188 | atomic_set(&hdev->fd_open_cnt, 0); |
| 181 | 189 | ||
| 182 | return 0; | 190 | return 0; |
| 183 | 191 | ||
| 192 | asid_fini: | ||
| 193 | hl_asid_fini(hdev); | ||
| 184 | early_fini: | 194 | early_fini: |
| 185 | if (hdev->asic_funcs->early_fini) | 195 | if (hdev->asic_funcs->early_fini) |
| 186 | hdev->asic_funcs->early_fini(hdev); | 196 | hdev->asic_funcs->early_fini(hdev); |
| @@ -196,9 +206,12 @@ early_fini: | |||
| 196 | */ | 206 | */ |
| 197 | static void device_early_fini(struct hl_device *hdev) | 207 | static void device_early_fini(struct hl_device *hdev) |
| 198 | { | 208 | { |
| 209 | mutex_destroy(&hdev->send_cpu_message_lock); | ||
| 199 | 210 | ||
| 200 | hl_cb_mgr_fini(hdev, &hdev->kernel_cb_mgr); | 211 | hl_cb_mgr_fini(hdev, &hdev->kernel_cb_mgr); |
| 201 | 212 | ||
| 213 | destroy_workqueue(hdev->cq_wq); | ||
| 214 | |||
| 202 | hl_asid_fini(hdev); | 215 | hl_asid_fini(hdev); |
| 203 | 216 | ||
| 204 | if (hdev->asic_funcs->early_fini) | 217 | if (hdev->asic_funcs->early_fini) |
| @@ -277,7 +290,7 @@ int hl_device_resume(struct hl_device *hdev) | |||
| 277 | */ | 290 | */ |
| 278 | int hl_device_init(struct hl_device *hdev, struct class *hclass) | 291 | int hl_device_init(struct hl_device *hdev, struct class *hclass) |
| 279 | { | 292 | { |
| 280 | int rc; | 293 | int i, rc, cq_ready_cnt; |
| 281 | 294 | ||
| 282 | /* Create device */ | 295 | /* Create device */ |
| 283 | rc = device_setup_cdev(hdev, hclass, hdev->id, &hl_ops); | 296 | rc = device_setup_cdev(hdev, hclass, hdev->id, &hl_ops); |
| @@ -298,11 +311,48 @@ int hl_device_init(struct hl_device *hdev, struct class *hclass) | |||
| 298 | if (rc) | 311 | if (rc) |
| 299 | goto early_fini; | 312 | goto early_fini; |
| 300 | 313 | ||
| 314 | /* | ||
| 315 | * Initialize the H/W queues. Must be done before hw_init, because | ||
| 316 | * there the addresses of the kernel queue are being written to the | ||
| 317 | * registers of the device | ||
| 318 | */ | ||
| 319 | rc = hl_hw_queues_create(hdev); | ||
| 320 | if (rc) { | ||
| 321 | dev_err(hdev->dev, "failed to initialize kernel queues\n"); | ||
| 322 | goto sw_fini; | ||
| 323 | } | ||
| 324 | |||
| 325 | /* | ||
| 326 | * Initialize the completion queues. Must be done before hw_init, | ||
| 327 | * because there the addresses of the completion queues are being | ||
| 328 | * passed as arguments to request_irq | ||
| 329 | */ | ||
| 330 | hdev->completion_queue = | ||
| 331 | kcalloc(hdev->asic_prop.completion_queues_count, | ||
| 332 | sizeof(*hdev->completion_queue), GFP_KERNEL); | ||
| 333 | |||
| 334 | if (!hdev->completion_queue) { | ||
| 335 | dev_err(hdev->dev, "failed to allocate completion queues\n"); | ||
| 336 | rc = -ENOMEM; | ||
| 337 | goto hw_queues_destroy; | ||
| 338 | } | ||
| 339 | |||
| 340 | for (i = 0, cq_ready_cnt = 0; | ||
| 341 | i < hdev->asic_prop.completion_queues_count; | ||
| 342 | i++, cq_ready_cnt++) { | ||
| 343 | rc = hl_cq_init(hdev, &hdev->completion_queue[i], i); | ||
| 344 | if (rc) { | ||
| 345 | dev_err(hdev->dev, | ||
| 346 | "failed to initialize completion queue\n"); | ||
| 347 | goto cq_fini; | ||
| 348 | } | ||
| 349 | } | ||
| 350 | |||
| 301 | /* Allocate the kernel context */ | 351 | /* Allocate the kernel context */ |
| 302 | hdev->kernel_ctx = kzalloc(sizeof(*hdev->kernel_ctx), GFP_KERNEL); | 352 | hdev->kernel_ctx = kzalloc(sizeof(*hdev->kernel_ctx), GFP_KERNEL); |
| 303 | if (!hdev->kernel_ctx) { | 353 | if (!hdev->kernel_ctx) { |
| 304 | rc = -ENOMEM; | 354 | rc = -ENOMEM; |
| 305 | goto sw_fini; | 355 | goto cq_fini; |
| 306 | } | 356 | } |
| 307 | 357 | ||
| 308 | hdev->user_ctx = NULL; | 358 | hdev->user_ctx = NULL; |
| @@ -328,6 +378,14 @@ int hl_device_init(struct hl_device *hdev, struct class *hclass) | |||
| 328 | 378 | ||
| 329 | hdev->disabled = false; | 379 | hdev->disabled = false; |
| 330 | 380 | ||
| 381 | /* Check that the communication with the device is working */ | ||
| 382 | rc = hdev->asic_funcs->test_queues(hdev); | ||
| 383 | if (rc) { | ||
| 384 | dev_err(hdev->dev, "Failed to detect if device is alive\n"); | ||
| 385 | rc = 0; | ||
| 386 | goto out_disabled; | ||
| 387 | } | ||
| 388 | |||
| 331 | dev_notice(hdev->dev, | 389 | dev_notice(hdev->dev, |
| 332 | "Successfully added device to habanalabs driver\n"); | 390 | "Successfully added device to habanalabs driver\n"); |
| 333 | 391 | ||
| @@ -339,6 +397,12 @@ release_ctx: | |||
| 339 | "kernel ctx is still alive on initialization failure\n"); | 397 | "kernel ctx is still alive on initialization failure\n"); |
| 340 | free_ctx: | 398 | free_ctx: |
| 341 | kfree(hdev->kernel_ctx); | 399 | kfree(hdev->kernel_ctx); |
| 400 | cq_fini: | ||
| 401 | for (i = 0 ; i < cq_ready_cnt ; i++) | ||
| 402 | hl_cq_fini(hdev, &hdev->completion_queue[i]); | ||
| 403 | kfree(hdev->completion_queue); | ||
| 404 | hw_queues_destroy: | ||
| 405 | hl_hw_queues_destroy(hdev); | ||
| 342 | sw_fini: | 406 | sw_fini: |
| 343 | hdev->asic_funcs->sw_fini(hdev); | 407 | hdev->asic_funcs->sw_fini(hdev); |
| 344 | early_fini: | 408 | early_fini: |
| @@ -368,6 +432,7 @@ out_disabled: | |||
| 368 | */ | 432 | */ |
| 369 | void hl_device_fini(struct hl_device *hdev) | 433 | void hl_device_fini(struct hl_device *hdev) |
| 370 | { | 434 | { |
| 435 | int i; | ||
| 371 | dev_info(hdev->dev, "Removing device\n"); | 436 | dev_info(hdev->dev, "Removing device\n"); |
| 372 | 437 | ||
| 373 | /* Mark device as disabled */ | 438 | /* Mark device as disabled */ |
| @@ -382,6 +447,12 @@ void hl_device_fini(struct hl_device *hdev) | |||
| 382 | /* Reset the H/W. It will be in idle state after this returns */ | 447 | /* Reset the H/W. It will be in idle state after this returns */ |
| 383 | hdev->asic_funcs->hw_fini(hdev, true); | 448 | hdev->asic_funcs->hw_fini(hdev, true); |
| 384 | 449 | ||
| 450 | for (i = 0 ; i < hdev->asic_prop.completion_queues_count ; i++) | ||
| 451 | hl_cq_fini(hdev, &hdev->completion_queue[i]); | ||
| 452 | kfree(hdev->completion_queue); | ||
| 453 | |||
| 454 | hl_hw_queues_destroy(hdev); | ||
| 455 | |||
