linux/drivers/soc/qcom/ocmem.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 * The On Chip Memory (OCMEM) allocator allows various clients to allocate
   4 * memory from OCMEM based on performance, latency and power requirements.
   5 * This is typically used by the GPU, camera/video, and audio components on
   6 * some Snapdragon SoCs.
   7 *
   8 * Copyright (C) 2019 Brian Masney <masneyb@onstation.org>
   9 * Copyright (C) 2015 Red Hat. Author: Rob Clark <robdclark@gmail.com>
  10 */
  11
  12#include <linux/bitfield.h>
  13#include <linux/clk.h>
  14#include <linux/io.h>
  15#include <linux/kernel.h>
  16#include <linux/module.h>
  17#include <linux/of_device.h>
  18#include <linux/platform_device.h>
  19#include <linux/qcom_scm.h>
  20#include <linux/sizes.h>
  21#include <linux/slab.h>
  22#include <linux/types.h>
  23#include <soc/qcom/ocmem.h>
  24
  25enum region_mode {
  26        WIDE_MODE = 0x0,
  27        THIN_MODE,
  28        MODE_DEFAULT = WIDE_MODE,
  29};
  30
  31enum ocmem_macro_state {
  32        PASSTHROUGH = 0,
  33        PERI_ON = 1,
  34        CORE_ON = 2,
  35        CLK_OFF = 4,
  36};
  37
  38struct ocmem_region {
  39        bool interleaved;
  40        enum region_mode mode;
  41        unsigned int num_macros;
  42        enum ocmem_macro_state macro_state[4];
  43        unsigned long macro_size;
  44        unsigned long region_size;
  45};
  46
  47struct ocmem_config {
  48        uint8_t num_regions;
  49        unsigned long macro_size;
  50};
  51
  52struct ocmem {
  53        struct device *dev;
  54        const struct ocmem_config *config;
  55        struct resource *memory;
  56        void __iomem *mmio;
  57        unsigned int num_ports;
  58        unsigned int num_macros;
  59        bool interleaved;
  60        struct ocmem_region *regions;
  61        unsigned long active_allocations;
  62};
  63
  64#define OCMEM_MIN_ALIGN                         SZ_64K
  65#define OCMEM_MIN_ALLOC                         SZ_64K
  66
  67#define OCMEM_REG_HW_VERSION                    0x00000000
  68#define OCMEM_REG_HW_PROFILE                    0x00000004
  69
  70#define OCMEM_REG_REGION_MODE_CTL               0x00001000
  71#define OCMEM_REGION_MODE_CTL_REG0_THIN         0x00000001
  72#define OCMEM_REGION_MODE_CTL_REG1_THIN         0x00000002
  73#define OCMEM_REGION_MODE_CTL_REG2_THIN         0x00000004
  74#define OCMEM_REGION_MODE_CTL_REG3_THIN         0x00000008
  75
  76#define OCMEM_REG_GFX_MPU_START                 0x00001004
  77#define OCMEM_REG_GFX_MPU_END                   0x00001008
  78
  79#define OCMEM_HW_PROFILE_NUM_PORTS(val)         FIELD_PREP(0x0000000f, (val))
  80#define OCMEM_HW_PROFILE_NUM_MACROS(val)        FIELD_PREP(0x00003f00, (val))
  81
  82#define OCMEM_HW_PROFILE_LAST_REGN_HALFSIZE     0x00010000
  83#define OCMEM_HW_PROFILE_INTERLEAVING           0x00020000
  84#define OCMEM_REG_GEN_STATUS                    0x0000000c
  85
  86#define OCMEM_REG_PSGSC_STATUS                  0x00000038
  87#define OCMEM_REG_PSGSC_CTL(i0)                 (0x0000003c + 0x1*(i0))
  88
  89#define OCMEM_PSGSC_CTL_MACRO0_MODE(val)        FIELD_PREP(0x00000007, (val))
  90#define OCMEM_PSGSC_CTL_MACRO1_MODE(val)        FIELD_PREP(0x00000070, (val))
  91#define OCMEM_PSGSC_CTL_MACRO2_MODE(val)        FIELD_PREP(0x00000700, (val))
  92#define OCMEM_PSGSC_CTL_MACRO3_MODE(val)        FIELD_PREP(0x00007000, (val))
  93
  94#define OCMEM_CLK_CORE_IDX                      0
  95static struct clk_bulk_data ocmem_clks[] = {
  96        {
  97                .id = "core",
  98        },
  99        {
 100                .id = "iface",
 101        },
 102};
 103
 104static inline void ocmem_write(struct ocmem *ocmem, u32 reg, u32 data)
 105{
 106        writel(data, ocmem->mmio + reg);
 107}
 108
 109static inline u32 ocmem_read(struct ocmem *ocmem, u32 reg)
 110{
 111        return readl(ocmem->mmio + reg);
 112}
 113
 114static void update_ocmem(struct ocmem *ocmem)
 115{
 116        uint32_t region_mode_ctrl = 0x0;
 117        int i;
 118
 119        if (!qcom_scm_ocmem_lock_available()) {
 120                for (i = 0; i < ocmem->config->num_regions; i++) {
 121                        struct ocmem_region *region = &ocmem->regions[i];
 122
 123                        if (region->mode == THIN_MODE)
 124                                region_mode_ctrl |= BIT(i);
 125                }
 126
 127                dev_dbg(ocmem->dev, "ocmem_region_mode_control %x\n",
 128                        region_mode_ctrl);
 129                ocmem_write(ocmem, OCMEM_REG_REGION_MODE_CTL, region_mode_ctrl);
 130        }
 131
 132        for (i = 0; i < ocmem->config->num_regions; i++) {
 133                struct ocmem_region *region = &ocmem->regions[i];
 134                u32 data;
 135
 136                data = OCMEM_PSGSC_CTL_MACRO0_MODE(region->macro_state[0]) |
 137                        OCMEM_PSGSC_CTL_MACRO1_MODE(region->macro_state[1]) |
 138                        OCMEM_PSGSC_CTL_MACRO2_MODE(region->macro_state[2]) |
 139                        OCMEM_PSGSC_CTL_MACRO3_MODE(region->macro_state[3]);
 140
 141                ocmem_write(ocmem, OCMEM_REG_PSGSC_CTL(i), data);
 142        }
 143}
 144
 145static unsigned long phys_to_offset(struct ocmem *ocmem,
 146                                    unsigned long addr)
 147{
 148        if (addr < ocmem->memory->start || addr >= ocmem->memory->end)
 149                return 0;
 150
 151        return addr - ocmem->memory->start;
 152}
 153
 154static unsigned long device_address(struct ocmem *ocmem,
 155                                    enum ocmem_client client,
 156                                    unsigned long addr)
 157{
 158        WARN_ON(client != OCMEM_GRAPHICS);
 159
 160        /* TODO: gpu uses phys_to_offset, but others do not.. */
 161        return phys_to_offset(ocmem, addr);
 162}
 163
 164static void update_range(struct ocmem *ocmem, struct ocmem_buf *buf,
 165                         enum ocmem_macro_state mstate, enum region_mode rmode)
 166{
 167        unsigned long offset = 0;
 168        int i, j;
 169
 170        for (i = 0; i < ocmem->config->num_regions; i++) {
 171                struct ocmem_region *region = &ocmem->regions[i];
 172
 173                if (buf->offset <= offset && offset < buf->offset + buf->len)
 174                        region->mode = rmode;
 175
 176                for (j = 0; j < region->num_macros; j++) {
 177                        if (buf->offset <= offset &&
 178                            offset < buf->offset + buf->len)
 179                                region->macro_state[j] = mstate;
 180
 181                        offset += region->macro_size;
 182                }
 183        }
 184
 185        update_ocmem(ocmem);
 186}
 187
 188struct ocmem *of_get_ocmem(struct device *dev)
 189{
 190        struct platform_device *pdev;
 191        struct device_node *devnode;
 192
 193        devnode = of_parse_phandle(dev->of_node, "sram", 0);
 194        if (!devnode || !devnode->parent) {
 195                dev_err(dev, "Cannot look up sram phandle\n");
 196                return ERR_PTR(-ENODEV);
 197        }
 198
 199        pdev = of_find_device_by_node(devnode->parent);
 200        if (!pdev) {
 201                dev_err(dev, "Cannot find device node %s\n", devnode->name);
 202                return ERR_PTR(-EPROBE_DEFER);
 203        }
 204
 205        return platform_get_drvdata(pdev);
 206}
 207EXPORT_SYMBOL(of_get_ocmem);
 208
 209struct ocmem_buf *ocmem_allocate(struct ocmem *ocmem, enum ocmem_client client,
 210                                 unsigned long size)
 211{
 212        struct ocmem_buf *buf;
 213        int ret;
 214
 215        /* TODO: add support for other clients... */
 216        if (WARN_ON(client != OCMEM_GRAPHICS))
 217                return ERR_PTR(-ENODEV);
 218
 219        if (size < OCMEM_MIN_ALLOC || !IS_ALIGNED(size, OCMEM_MIN_ALIGN))
 220                return ERR_PTR(-EINVAL);
 221
 222        if (test_and_set_bit_lock(BIT(client), &ocmem->active_allocations))
 223                return ERR_PTR(-EBUSY);
 224
 225        buf = kzalloc(sizeof(*buf), GFP_KERNEL);
 226        if (!buf) {
 227                ret = -ENOMEM;
 228                goto err_unlock;
 229        }
 230
 231        buf->offset = 0;
 232        buf->addr = device_address(ocmem, client, buf->offset);
 233        buf->len = size;
 234
 235        update_range(ocmem, buf, CORE_ON, WIDE_MODE);
 236
 237        if (qcom_scm_ocmem_lock_available()) {
 238                ret = qcom_scm_ocmem_lock(QCOM_SCM_OCMEM_GRAPHICS_ID,
 239                                          buf->offset, buf->len, WIDE_MODE);
 240                if (ret) {
 241                        dev_err(ocmem->dev, "could not lock: %d\n", ret);
 242                        ret = -EINVAL;
 243                        goto err_kfree;
 244                }
 245        } else {
 246                ocmem_write(ocmem, OCMEM_REG_GFX_MPU_START, buf->offset);
 247                ocmem_write(ocmem, OCMEM_REG_GFX_MPU_END,
 248                            buf->offset + buf->len);
 249        }
 250
 251        dev_dbg(ocmem->dev, "using %ldK of OCMEM at 0x%08lx for client %d\n",
 252                size / 1024, buf->addr, client);
 253
 254        return buf;
 255
 256err_kfree:
 257        kfree(buf);
 258err_unlock:
 259        clear_bit_unlock(BIT(client), &ocmem->active_allocations);
 260
 261        return ERR_PTR(ret);
 262}
 263EXPORT_SYMBOL(ocmem_allocate);
 264
 265void ocmem_free(struct ocmem *ocmem, enum ocmem_client client,
 266                struct ocmem_buf *buf)
 267{
 268        /* TODO: add support for other clients... */
 269        if (WARN_ON(client != OCMEM_GRAPHICS))
 270                return;
 271
 272        update_range(ocmem, buf, CLK_OFF, MODE_DEFAULT);
 273
 274        if (qcom_scm_ocmem_lock_available()) {
 275                int ret;
 276
 277                ret = qcom_scm_ocmem_unlock(QCOM_SCM_OCMEM_GRAPHICS_ID,
 278                                            buf->offset, buf->len);
 279                if (ret)
 280                        dev_err(ocmem->dev, "could not unlock: %d\n", ret);
 281        } else {
 282                ocmem_write(ocmem, OCMEM_REG_GFX_MPU_START, 0x0);
 283                ocmem_write(ocmem, OCMEM_REG_GFX_MPU_END, 0x0);
 284        }
 285
 286        kfree(buf);
 287
 288        clear_bit_unlock(BIT(client), &ocmem->active_allocations);
 289}
 290EXPORT_SYMBOL(ocmem_free);
 291
 292static int ocmem_dev_probe(struct platform_device *pdev)
 293{
 294        struct device *dev = &pdev->dev;
 295        unsigned long reg, region_size;
 296        int i, j, ret, num_banks;
 297        struct resource *res;
 298        struct ocmem *ocmem;
 299
 300        if (!qcom_scm_is_available())
 301                return -EPROBE_DEFER;
 302
 303        ocmem = devm_kzalloc(dev, sizeof(*ocmem), GFP_KERNEL);
 304        if (!ocmem)
 305                return -ENOMEM;
 306
 307        ocmem->dev = dev;
 308        ocmem->config = device_get_match_data(dev);
 309
 310        ret = devm_clk_bulk_get(dev, ARRAY_SIZE(ocmem_clks), ocmem_clks);
 311        if (ret) {
 312                if (ret != -EPROBE_DEFER)
 313                        dev_err(dev, "Unable to get clocks\n");
 314
 315                return ret;
 316        }
 317
 318        res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "ctrl");
 319        ocmem->mmio = devm_ioremap_resource(&pdev->dev, res);
 320        if (IS_ERR(ocmem->mmio)) {
 321                dev_err(&pdev->dev, "Failed to ioremap ocmem_ctrl resource\n");
 322                return PTR_ERR(ocmem->mmio);
 323        }
 324
 325        ocmem->memory = platform_get_resource_byname(pdev, IORESOURCE_MEM,
 326                                                     "mem");
 327        if (!ocmem->memory) {
 328                dev_err(dev, "Could not get mem region\n");
 329                return -ENXIO;
 330        }
 331
 332        /* The core clock is synchronous with graphics */
 333        WARN_ON(clk_set_rate(ocmem_clks[OCMEM_CLK_CORE_IDX].clk, 1000) < 0);
 334
 335        ret = clk_bulk_prepare_enable(ARRAY_SIZE(ocmem_clks), ocmem_clks);
 336        if (ret) {
 337                dev_info(ocmem->dev, "Failed to enable clocks\n");
 338                return ret;
 339        }
 340
 341        if (qcom_scm_restore_sec_cfg_available()) {
 342                dev_dbg(dev, "configuring scm\n");
 343                ret = qcom_scm_restore_sec_cfg(QCOM_SCM_OCMEM_DEV_ID, 0);
 344                if (ret) {
 345                        dev_err(dev, "Could not enable secure configuration\n");
 346                        goto err_clk_disable;
 347                }
 348        }
 349
 350        reg = ocmem_read(ocmem, OCMEM_REG_HW_PROFILE);
 351        ocmem->num_ports = OCMEM_HW_PROFILE_NUM_PORTS(reg);
 352        ocmem->num_macros = OCMEM_HW_PROFILE_NUM_MACROS(reg);
 353        ocmem->interleaved = !!(reg & OCMEM_HW_PROFILE_INTERLEAVING);
 354
 355        num_banks = ocmem->num_ports / 2;
 356        region_size = ocmem->config->macro_size * num_banks;
 357
 358        dev_info(dev, "%u ports, %u regions, %u macros, %sinterleaved\n",
 359                 ocmem->num_ports, ocmem->config->num_regions,
 360                 ocmem->num_macros, ocmem->interleaved ? "" : "not ");
 361
 362        ocmem->regions = devm_kcalloc(dev, ocmem->config->num_regions,
 363                                      sizeof(struct ocmem_region), GFP_KERNEL);
 364        if (!ocmem->regions) {
 365                ret = -ENOMEM;
 366                goto err_clk_disable;
 367        }
 368
 369        for (i = 0; i < ocmem->config->num_regions; i++) {
 370                struct ocmem_region *region = &ocmem->regions[i];
 371
 372                if (WARN_ON(num_banks > ARRAY_SIZE(region->macro_state))) {
 373                        ret = -EINVAL;
 374                        goto err_clk_disable;
 375                }
 376
 377                region->mode = MODE_DEFAULT;
 378                region->num_macros = num_banks;
 379
 380                if (i == (ocmem->config->num_regions - 1) &&
 381                    reg & OCMEM_HW_PROFILE_LAST_REGN_HALFSIZE) {
 382                        region->macro_size = ocmem->config->macro_size / 2;
 383                        region->region_size = region_size / 2;
 384                } else {
 385                        region->macro_size = ocmem->config->macro_size;
 386                        region->region_size = region_size;
 387                }
 388
 389                for (j = 0; j < ARRAY_SIZE(region->macro_state); j++)
 390                        region->macro_state[j] = CLK_OFF;
 391        }
 392
 393        platform_set_drvdata(pdev, ocmem);
 394
 395        return 0;
 396
 397err_clk_disable:
 398        clk_bulk_disable_unprepare(ARRAY_SIZE(ocmem_clks), ocmem_clks);
 399        return ret;
 400}
 401
 402static int ocmem_dev_remove(struct platform_device *pdev)
 403{
 404        clk_bulk_disable_unprepare(ARRAY_SIZE(ocmem_clks), ocmem_clks);
 405
 406        return 0;
 407}
 408
 409static const struct ocmem_config ocmem_8974_config = {
 410        .num_regions = 3,
 411        .macro_size = SZ_128K,
 412};
 413
 414static const struct of_device_id ocmem_of_match[] = {
 415        { .compatible = "qcom,msm8974-ocmem", .data = &ocmem_8974_config },
 416        { }
 417};
 418
 419MODULE_DEVICE_TABLE(of, ocmem_of_match);
 420
 421static struct platform_driver ocmem_driver = {
 422        .probe = ocmem_dev_probe,
 423        .remove = ocmem_dev_remove,
 424        .driver = {
 425                .name = "ocmem",
 426                .of_match_table = ocmem_of_match,
 427        },
 428};
 429
 430module_platform_driver(ocmem_driver);
 431
 432MODULE_DESCRIPTION("On Chip Memory (OCMEM) allocator for some Snapdragon SoCs");
 433MODULE_LICENSE("GPL v2");
 434