linux/drivers/misc/xilinx-ai-engine/ai-engine-aperture.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 * Xilinx AI Engine partition driver
   4 *
   5 * Copyright (C) 2022 Xilinx, Inc.
   6 */
   7
   8#include <linux/bitmap.h>
   9#include <linux/device.h>
  10#include <linux/firmware/xlnx-zynqmp.h>
  11#include <linux/kernel.h>
  12#include <linux/list.h>
  13#include <linux/mutex.h>
  14#include <linux/of.h>
  15#include <linux/of_address.h>
  16#include <linux/of_device.h>
  17#include <linux/of_irq.h>
  18#include <uapi/linux/xlnx-ai-engine.h>
  19
  20#include "ai-engine-internal.h"
  21
  22/* AI engine SHIM DMA address width is 48bits */
  23#define XAIE_DMA_BIT_MASK       48U
  24
  25/**
  26 * aie_aperture_get_num_parts() - get number of AI engine partitions of aperture
  27 * @aperture: AI engine aperture
  28 * @return: number of partitions of this aperture
  29 *
  30 * This function returns the number of AI engine partitions of the aperture.
  31 * It includes the number of partitions in use and the number of available
  32 * of partitions. If no partitions are in use, the number of available
  33 * partitions is 1. One available partition is the max contiguous available
  34 * columns region. E.g. if there is only one partition in use starting from
  35 * column 10 to 14 in the aperture. The number of all partitions of this
  36 * aperture is 3. They are column 0 to 9, 10 to 14 and 15+. This function
  37 * returns 3, for which columns for each partition, and whether they are in
  38 * use will be returned by another function @aie_aperture_enquire_parts().
  39 */
  40unsigned int aie_aperture_get_num_parts(struct aie_aperture *aperture)
  41{
  42        struct aie_partition *apart;
  43        int ret;
  44        unsigned int rs, re, num_parts = 0;
  45
  46        ret = mutex_lock_interruptible(&aperture->mlock);
  47        if (ret)
  48                return ret;
  49
  50        list_for_each_entry(apart, &aperture->partitions, node) {
  51                num_parts++;
  52        }
  53
  54        bitmap_for_each_clear_region(aperture->cols_res.bitmap, rs, re, 0,
  55                                     (aperture->range.size.col - 1)) {
  56                num_parts++;
  57        }
  58
  59        mutex_unlock(&aperture->mlock);
  60
  61        return num_parts;
  62}
  63
  64/**
  65 * aie_aperture_enquire_parts() - get partitions information
  66 * @aperture: AI engine aperture`
  67 * @num_queries: number of queries entries to return
  68 * @queries: return the partitions information
  69 * @num_parts_left: number of partitions not filled
  70 * @to_user: indicate it if information is required to user space
  71 * @return: number of partitions have been filled in if succeeded,
  72 *          negative value for error.
  73 *
  74 * This function returns each columns information and if the partition is
  75 * in use for each partition until the queries array is filled up. The
  76 * @num_parts_left will contains the partitions whose information are not
  77 * able to put into the queries due to the queries array is full.
  78 * Internal function, will not validate the queries, and num_parts_left
  79 * pointers. Caller should not pass invalid values.
  80 */
  81int aie_aperture_enquire_parts(struct aie_aperture *aperture,
  82                               unsigned int num_queries,
  83                               struct aie_range_args  *queries,
  84                               int *num_parts_left, bool to_user)
  85{
  86        struct aie_partition *apart;
  87        int ret;
  88        unsigned int rs, re, num_queries_left;
  89
  90        *num_parts_left = 0;
  91        num_queries_left = num_queries;
  92        ret = mutex_lock_interruptible(&aperture->mlock);
  93        if (ret)
  94                return ret;
  95
  96        list_for_each_entry(apart, &aperture->partitions, node) {
  97                struct aie_range_args query;
  98
  99                if (!num_queries_left) {
 100                        *num_parts_left += 1;
 101                        continue;
 102                }
 103                query.partition_id = apart->range.start.col <<
 104                                     AIE_PART_ID_START_COL_SHIFT;
 105                query.partition_id += apart->range.size.col <<
 106                                      AIE_PART_ID_NUM_COLS_SHIFT;
 107                query.range.start.col = apart->range.start.col;
 108                query.range.size.col = apart->range.size.col;
 109                query.range.start.row = apart->range.start.row;
 110                query.range.size.row = apart->range.size.row;
 111                query.status = apart->status;
 112
 113                if (to_user) {
 114                        if (copy_to_user((void __user *)queries, &query,
 115                                         sizeof(query))) {
 116                                mutex_unlock(&aperture->mlock);
 117                                return -EFAULT;
 118                        }
 119                } else {
 120                        memcpy(queries, &query, sizeof(query));
 121                }
 122                queries++;
 123                num_queries_left--;
 124        }
 125
 126        bitmap_for_each_clear_region(aperture->cols_res.bitmap, rs, re, 0,
 127                                     (aperture->range.size.col - 1)) {
 128                struct aie_range_args query;
 129
 130                if (!num_queries_left) {
 131                        *num_parts_left += 1;
 132                        continue;
 133                }
 134                query.partition_id = (rs & AIE_PART_ID_START_COL_MASK) <<
 135                                     AIE_PART_ID_START_COL_SHIFT;
 136                query.partition_id += ((re - rs + 1) &
 137                                       AIE_PART_ID_NUM_COLS_MASK) <<
 138                                      AIE_PART_ID_NUM_COLS_SHIFT;
 139                query.range.start.col = rs;
 140                query.range.size.col = re - rs + 1;
 141                query.range.start.row = aperture->range.start.row;
 142                query.range.size.row = aperture->range.size.row;
 143                query.status = 0;
 144
 145                if (to_user) {
 146                        if (copy_to_user((void __user *)queries, &query,
 147                                         sizeof(query))) {
 148                                mutex_unlock(&aperture->mlock);
 149                                return -EFAULT;
 150                        }
 151                } else {
 152                        memcpy(queries, &query, sizeof(query));
 153                }
 154                queries++;
 155                num_queries_left--;
 156        }
 157
 158        mutex_unlock(&aperture->mlock);
 159
 160        return (num_queries - num_queries_left);
 161}
 162
 163/**
 164 * aie_aperture_request_part_from_id() - request AI engine partition from id
 165 * @aperture: AI engine aperture
 166 * @partition_id: partition id to check
 167 * @return: partition pointer for success, and error pointer for failure
 168 *
 169 * The partition ID contains the start column and number of columns
 170 * information for the partition.
 171 */
 172struct aie_partition *
 173aie_aperture_request_part_from_id(struct aie_aperture *aperture,
 174                                  u32 partition_id)
 175{
 176        struct aie_partition *apart = NULL;
 177        u32 in_partition_id = partition_id;
 178        u8 start_col, num_cols;
 179        int ret;
 180
 181        start_col = aie_part_id_get_start_col(partition_id);
 182        num_cols = aie_part_id_get_num_cols(partition_id);
 183        /*
 184         * TODO: this is for backward compatibility, once zocl update
 185         * to pass the expected partition id format, can remove the
 186         * num_cols.
 187         */
 188        if (num_cols == 0) {
 189                start_col = aperture->range.start.col;
 190                num_cols = aperture->range.size.col;
 191                partition_id = ((u32)start_col << AIE_PART_ID_START_COL_SHIFT) |
 192                               ((u32)num_cols << AIE_PART_ID_NUM_COLS_SHIFT);
 193        }
 194
 195        if (start_col < aperture->range.start.col ||
 196            num_cols > aperture->range.size.col ||
 197            (start_col + num_cols) >
 198            (aperture->range.start.col + aperture->range.size.col)) {
 199                dev_err(&aperture->dev, "invalid partition %u: %u,%u.\n",
 200                        partition_id, start_col, num_cols);
 201                return ERR_PTR(-EINVAL);
 202        }
 203
 204        ret = mutex_lock_interruptible(&aperture->mlock);
 205        if (ret)
 206                return ERR_PTR(ret);
 207
 208        ret = aie_resource_get_region(&aperture->cols_res, start_col, num_cols);
 209        if (ret != (u32)start_col) {
 210                dev_err(&aperture->dev, "partition %u already requested.\n",
 211                        in_partition_id);
 212                mutex_unlock(&aperture->mlock);
 213                return ERR_PTR(-EINVAL);
 214        }
 215
 216        apart = aie_create_partition(aperture, partition_id);
 217        if (IS_ERR(apart)) {
 218                dev_err(&aperture->dev, "failed to create partition %u.\n",
 219                        partition_id);
 220                mutex_unlock(&aperture->mlock);
 221                return apart;
 222        }
 223
 224        list_add_tail(&apart->node, &aperture->partitions);
 225
 226        mutex_unlock(&aperture->mlock);
 227
 228        return apart;
 229}
 230
 231/**
 232 * aie_aperture_check_part_avail() - Check an AI engine partition availability
 233 * @aperture: AI engine aperture
 234 * @req: AI engine partition requesting arguments
 235 * @return: if AI engine partition is available, in use or not valid for the
 236 *          aperture.
 237 *
 238 * This functions checks the specified partition availability in the aperture.
 239 * This function is internal call, it will not valid the input pointers.
 240 */
 241int aie_aperture_check_part_avail(struct aie_aperture *aperture,
 242                                  struct aie_partition_req *req)
 243{
 244        unsigned int start_col, end_col, num_cols;
 245
 246        start_col = aie_part_id_get_start_col(req->partition_id);
 247        num_cols = aie_part_id_get_num_cols(req->partition_id);
 248        /*
 249         * TODO: this is for backward compatibility, once zocl update
 250         * to pass the expected partition id format, can remove the
 251         * num_cols.
 252         */
 253        if (num_cols == 0) {
 254                start_col = aperture->range.start.col;
 255                num_cols = aperture->range.size.col;
 256        }
 257
 258        end_col = start_col + num_cols - 1;
 259
 260        if (start_col < aperture->range.start.col ||
 261            end_col >= aperture->range.start.col + aperture->range.size.col) {
 262                return XAIE_PART_STATUS_INVALID;
 263        }
 264
 265        if (aie_resource_check_region(&aperture->cols_res, start_col,
 266                                      num_cols) < 0)
 267                return XAIE_PART_STATUS_INUSE;
 268
 269        return XAIE_PART_STATUS_IDLE;
 270}
 271
 272/**
 273 * aie_aperture_release_device() - release an AI engine aperture instance
 274 * @dev: AI engine aperture device
 275 *
 276 * It will be called by device driver core when no one holds a valid
 277 * pointer to @dev anymore.
 278 */
 279static void aie_aperture_release_device(struct device *dev)
 280{
 281        struct aie_aperture *aperture = dev_get_drvdata(dev);
 282
 283        aie_resource_uninitialize(&aperture->cols_res);
 284        aie_resource_uninitialize(&aperture->l2_mask);
 285        zynqmp_pm_release_node(aperture->node_id);
 286        kfree(aperture);
 287}
 288
 289/**
 290 * aie_aperture_remove() - destroy AI engine aperture
 291 * @aperture: AI engine aperture
 292 * @return: 0 for success, negative value for failure
 293 *
 294 * This function will remove AI engine aperture.
 295 */
 296int aie_aperture_remove(struct aie_aperture *aperture)
 297{
 298        struct list_head *node, *pos;
 299        int ret;
 300
 301        ret = mutex_lock_interruptible(&aperture->mlock);
 302        if (ret)
 303                return ret;
 304
 305        list_for_each_safe(pos, node, &aperture->partitions) {
 306                struct aie_partition *apart;
 307
 308                apart = list_entry(pos, struct aie_partition, node);
 309                list_del(&apart->node);
 310                aie_part_remove(apart);
 311        }
 312        mutex_unlock(&aperture->mlock);
 313
 314        of_node_clear_flag(aperture->dev.of_node, OF_POPULATED);
 315        device_del(&aperture->dev);
 316        put_device(&aperture->dev);
 317
 318        return 0;
 319}
 320
 321/**
 322 * aie_aperture_add_dev() - initialize and add AI engine aperture device
 323 * @aperture: AI engine aperture
 324 * @nc: AI engine aperture device node
 325 * @return: 0 for success, negative value for failure
 326 *
 327 * This function will initialize and add AI engine aperture device to Linux
 328 * kernel device framework.
 329 * TODO: This function should be moved back to of_aie_aperture_probe()
 330 * implementation once v1.0 device node support is removed.
 331 */
 332int aie_aperture_add_dev(struct aie_aperture *aperture,
 333                         struct device_node *nc)
 334{
 335        struct device *dev = &aperture->dev;
 336
 337        /* register device for aperture */
 338        dev = &aperture->dev;
 339        dev->class = aie_class;
 340        dev->parent = &aperture->adev->dev;
 341        dev->of_node = nc;
 342        dev->driver_data = aperture;
 343        dev_set_name(dev, "aieaperture_%u_%u", aperture->range.start.col,
 344                     aperture->range.size.col);
 345        /* We can now rely on the release function for cleanup */
 346        dev->release = aie_aperture_release_device;
 347
 348        return device_register(&aperture->dev);
 349}
 350
 351/**
 352 * of_aie_aperture_probe() - probes AI engine aperture node
 353 * @adev: AI engine device
 354 * @nc: aperture device node
 355 * @return: AI engine aperture pointer for success, error pointer for failure.
 356 *
 357 * This function will probe AI engine aperture node and will create an AI
 358 * engine aperture instance for the node.
 359 * It requires the caller to lock the @adev before calling this function.
 360 */
 361struct aie_aperture *
 362of_aie_aperture_probe(struct aie_device *adev, struct device_node *nc)
 363{
 364        struct aie_aperture *aperture, *laperture;
 365        struct device *dev;
 366        struct aie_range *range;
 367        u32 regs[2];
 368        int ret;
 369
 370        aperture = kzalloc(sizeof(*aperture), GFP_KERNEL);
 371        if (!aperture)
 372                return ERR_PTR(-ENOMEM);
 373
 374        aperture->adev = adev;
 375        INIT_LIST_HEAD(&aperture->partitions);
 376        mutex_init(&aperture->mlock);
 377
 378        range = &aperture->range;
 379        ret = of_property_read_u32_array(nc, "xlnx,columns", regs,
 380                                         ARRAY_SIZE(regs));
 381        if (ret < 0) {
 382                dev_err(&adev->dev,
 383                        "probe %pOF failed, no tiles range information.\n",
 384                        nc);
 385                goto free_aperture;
 386        }
 387        range->start.col = regs[0] & aligned_byte_mask(1);
 388        range->size.col = regs[1] & aligned_byte_mask(1);
 389
 390        /*
 391         * Row information is used to calculate the clock or other resources
 392         * bitmaps. It can be moved aie_device later.
 393         */
 394        range->start.row = 0;
 395        range->size.row = adev->ttype_attr[AIE_TILE_TYPE_SHIMPL].num_rows +
 396                          adev->ttype_attr[AIE_TILE_TYPE_MEMORY].num_rows +
 397                          adev->ttype_attr[AIE_TILE_TYPE_TILE].num_rows;
 398
 399        ret = of_property_read_u32_index(nc, "xlnx,node-id", 0,
 400                                         &aperture->node_id);
 401        if (ret < 0) {
 402                dev_err(&adev->dev,
 403                        "probe %pOF failed, no aperture node id.\n", nc);
 404                goto free_aperture;
 405        }
 406
 407        /* Validate the aperture */
 408        list_for_each_entry(laperture, &adev->apertures, node) {
 409                u32 start_col, end_col, check_start_col, check_end_col;
 410
 411                if (laperture->node_id == aperture->node_id) {
 412                        dev_err(&adev->dev,
 413                                "probe failed, aperture %u exists.\n",
 414                                aperture->node_id);
 415                        ret = -EINVAL;
 416                        goto free_aperture;
 417                }
 418
 419                start_col = range->start.col;
 420                end_col  = start_col + range->size.col - 1;
 421                check_start_col = laperture->range.start.col;
 422                check_end_col = check_start_col + laperture->range.size.col - 1;
 423                if ((start_col >= check_start_col &&
 424                     start_col <= check_end_col) ||
 425                    (end_col >= check_start_col &&
 426                     end_col <= check_end_col)) {
 427                        dev_err(&adev->dev,
 428                                "probe failed, aperture %u overlaps other aperture.\n",
 429                                aperture->node_id);
 430                        ret = -EINVAL;
 431                        goto free_aperture;
 432                }
 433        }
 434
 435        /* register device for aperture */
 436        ret = aie_aperture_add_dev(aperture, nc);
 437        if (ret) {
 438                dev_err(&aperture->dev, "device_add failed: %d\n", ret);
 439                goto free_aperture;
 440        }
 441        dev = &aperture->dev;
 442
 443        /*
 444         * Initialize columns resource map to remember which columns have been
 445         * assigned. Used for partition management.
 446         */
 447        ret = aie_resource_initialize(&aperture->cols_res,
 448                                      aperture->range.size.col);
 449        if (ret) {
 450                dev_err(dev, "failed to initialize columns resource.\n");
 451                goto put_aperture_dev;
 452        }
 453
 454        ret = of_address_to_resource(nc, 0, &aperture->res);
 455        if (ret < 0) {
 456                dev_err(dev, "failed to get address from device node.\n");
 457                goto put_aperture_dev;
 458        }
 459        aperture->base = devm_ioremap_resource(dev, &aperture->res);
 460        if (!aperture->base) {
 461                ret = -ENOMEM;
 462                goto put_aperture_dev;
 463        }
 464
 465        /* Get device node DMA setting */
 466        dev->coherent_dma_mask = DMA_BIT_MASK(XAIE_DMA_BIT_MASK);
 467        dev->dma_mask = &dev->coherent_dma_mask;
 468        ret = of_dma_configure(&aperture->dev, nc, true);
 469        if (ret)
 470                dev_warn(&aperture->dev, "Failed to configure DMA.\n");
 471
 472        /* Initialize interrupt */
 473        ret = of_irq_get_byname(nc, "interrupt1");
 474        if (ret < 0) {
 475                dev_warn(dev, "no interrupt in device node.");
 476        } else {
 477                aperture->irq = ret;
 478                INIT_WORK(&aperture->backtrack, aie_aperture_backtrack);
 479                ret = aie_aperture_create_l2_bitmap(aperture);
 480                if (ret) {
 481                        dev_err(dev,
 482                                "failed to initialize l2 mask resource.\n");
 483                        goto put_aperture_dev;
 484                }
 485
 486                ret = devm_request_threaded_irq(dev, aperture->irq, NULL,
 487                                                aie_interrupt, IRQF_ONESHOT,
 488                                                dev_name(dev), aperture);
 489                if (ret) {
 490                        dev_err(dev, "Failed to request AIE IRQ.\n");
 491                        goto put_aperture_dev;
 492                }
 493        }
 494
 495        ret = zynqmp_pm_request_node(aperture->node_id,
 496                                     ZYNQMP_PM_CAPABILITY_ACCESS, 0,
 497                                     ZYNQMP_PM_REQUEST_ACK_BLOCKING);
 498        if (ret < 0) {
 499                dev_err(dev, "Unable to request node %d\n", aperture->node_id);
 500                goto put_aperture_dev;
 501        }
 502
 503        of_node_get(nc);
 504
 505        dev_info(dev,
 506                 "AI engine aperture %s, id 0x%x, cols(%u, %u) aie_tile_rows(%u, %u) memory_tile_rows(%u, %u) is probed successfully.\n",
 507                 dev_name(dev), aperture->node_id,
 508                 aperture->range.start.col, aperture->range.size.col,
 509                 adev->ttype_attr[AIE_TILE_TYPE_TILE].start_row,
 510                 adev->ttype_attr[AIE_TILE_TYPE_TILE].num_rows,
 511                 adev->ttype_attr[AIE_TILE_TYPE_MEMORY].start_row,
 512                 adev->ttype_attr[AIE_TILE_TYPE_MEMORY].num_rows);
 513
 514        return aperture;
 515
 516put_aperture_dev:
 517        put_device(dev);
 518        return ERR_PTR(ret);
 519
 520free_aperture:
 521        kfree(aperture);
 522        return ERR_PTR(ret);
 523}
 524