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        struct ocmem *ocmem;
 193
 194        devnode = of_parse_phandle(dev->of_node, "sram", 0);
 195        if (!devnode || !devnode->parent) {
 196                dev_err(dev, "Cannot look up sram phandle\n");
 197                return ERR_PTR(-ENODEV);
 198        }
 199
 200        pdev = of_find_device_by_node(devnode->parent);
 201        if (!pdev) {
 202                dev_err(dev, "Cannot find device node %s\n", devnode->name);
 203                return ERR_PTR(-EPROBE_DEFER);
 204        }
 205
 206        ocmem = platform_get_drvdata(pdev);
 207        if (!ocmem) {
 208                dev_err(dev, "Cannot get ocmem\n");
 209                return ERR_PTR(-ENODEV);
 210        }
 211        return ocmem;
 212}
 213EXPORT_SYMBOL(of_get_ocmem);
 214
 215struct ocmem_buf *ocmem_allocate(struct ocmem *ocmem, enum ocmem_client client,
 216                                 unsigned long size)
 217{
 218        struct ocmem_buf *buf;
 219        int ret;
 220
 221        /* TODO: add support for other clients... */
 222        if (WARN_ON(client != OCMEM_GRAPHICS))
 223                return ERR_PTR(-ENODEV);
 224
 225        if (size < OCMEM_MIN_ALLOC || !IS_ALIGNED(size, OCMEM_MIN_ALIGN))
 226                return ERR_PTR(-EINVAL);
 227
 228        if (test_and_set_bit_lock(BIT(client), &ocmem->active_allocations))
 229                return ERR_PTR(-EBUSY);
 230
 231        buf = kzalloc(sizeof(*buf), GFP_KERNEL);
 232        if (!buf) {
 233                ret = -ENOMEM;
 234                goto err_unlock;
 235        }
 236
 237        buf->offset = 0;
 238        buf->addr = device_address(ocmem, client, buf->offset);
 239        buf->len = size;
 240
 241        update_range(ocmem, buf, CORE_ON, WIDE_MODE);
 242
 243        if (qcom_scm_ocmem_lock_available()) {
 244                ret = qcom_scm_ocmem_lock(QCOM_SCM_OCMEM_GRAPHICS_ID,
 245                                          buf->offset, buf->len, WIDE_MODE);
 246                if (ret) {
 247                        dev_err(ocmem->dev, "could not lock: %d\n", ret);
 248                        ret = -EINVAL;
 249                        goto err_kfree;
 250                }
 251        } else {
 252                ocmem_write(ocmem, OCMEM_REG_GFX_MPU_START, buf->offset);
 253                ocmem_write(ocmem, OCMEM_REG_GFX_MPU_END,
 254                            buf->offset + buf->len);
 255        }
 256
 257        dev_dbg(ocmem->dev, "using %ldK of OCMEM at 0x%08lx for client %d\n",
 258                size / 1024, buf->addr, client);
 259
 260        return buf;
 261
 262err_kfree:
 263        kfree(buf);
 264err_unlock:
 265        clear_bit_unlock(BIT(client), &ocmem->active_allocations);
 266
 267        return ERR_PTR(ret);
 268}
 269EXPORT_SYMBOL(ocmem_allocate);
 270
 271void ocmem_free(struct ocmem *ocmem, enum ocmem_client client,
 272                struct ocmem_buf *buf)
 273{
 274        /* TODO: add support for other clients... */
 275        if (WARN_ON(client != OCMEM_GRAPHICS))
 276                return;
 277
 278        update_range(ocmem, buf, CLK_OFF, MODE_DEFAULT);
 279
 280        if (qcom_scm_ocmem_lock_available()) {
 281                int ret;
 282
 283                ret = qcom_scm_ocmem_unlock(QCOM_SCM_OCMEM_GRAPHICS_ID,
 284                                            buf->offset, buf->len);
 285                if (ret)
 286                        dev_err(ocmem->dev, "could not unlock: %d\n", ret);
 287        } else {
 288                ocmem_write(ocmem, OCMEM_REG_GFX_MPU_START, 0x0);
 289                ocmem_write(ocmem, OCMEM_REG_GFX_MPU_END, 0x0);
 290        }
 291
 292        kfree(buf);
 293
 294        clear_bit_unlock(BIT(client), &ocmem->active_allocations);
 295}
 296EXPORT_SYMBOL(ocmem_free);
 297
 298static int ocmem_dev_probe(struct platform_device *pdev)
 299{
 300        struct device *dev = &pdev->dev;
 301        unsigned long reg, region_size;
 302        int i, j, ret, num_banks;
 303        struct resource *res;
 304        struct ocmem *ocmem;
 305
 306        if (!qcom_scm_is_available())
 307                return -EPROBE_DEFER;
 308
 309        ocmem = devm_kzalloc(dev, sizeof(*ocmem), GFP_KERNEL);
 310        if (!ocmem)
 311                return -ENOMEM;
 312
 313        ocmem->dev = dev;
 314        ocmem->config = device_get_match_data(dev);
 315
 316        ret = devm_clk_bulk_get(dev, ARRAY_SIZE(ocmem_clks), ocmem_clks);
 317        if (ret) {
 318                if (ret != -EPROBE_DEFER)
 319                        dev_err(dev, "Unable to get clocks\n");
 320
 321                return ret;
 322        }
 323
 324        res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "ctrl");
 325        ocmem->mmio = devm_ioremap_resource(&pdev->dev, res);
 326        if (IS_ERR(ocmem->mmio)) {
 327                dev_err(&pdev->dev, "Failed to ioremap ocmem_ctrl resource\n");
 328                return PTR_ERR(ocmem->mmio);
 329        }
 330
 331        ocmem->memory = platform_get_resource_byname(pdev, IORESOURCE_MEM,
 332                                                     "mem");
 333        if (!ocmem->memory) {
 334                dev_err(dev, "Could not get mem region\n");
 335                return -ENXIO;
 336        }
 337
 338        /* The core clock is synchronous with graphics */
 339        WARN_ON(clk_set_rate(ocmem_clks[OCMEM_CLK_CORE_IDX].clk, 1000) < 0);
 340
 341        ret = clk_bulk_prepare_enable(ARRAY_SIZE(ocmem_clks), ocmem_clks);
 342        if (ret) {
 343                dev_info(ocmem->dev, "Failed to enable clocks\n");
 344                return ret;
 345        }
 346
 347        if (qcom_scm_restore_sec_cfg_available()) {
 348                dev_dbg(dev, "configuring scm\n");
 349                ret = qcom_scm_restore_sec_cfg(QCOM_SCM_OCMEM_DEV_ID, 0);
 350                if (ret) {
 351                        dev_err(dev, "Could not enable secure configuration\n");
 352                        goto err_clk_disable;
 353                }
 354        }
 355
 356        reg = ocmem_read(ocmem, OCMEM_REG_HW_PROFILE);
 357        ocmem->num_ports = OCMEM_HW_PROFILE_NUM_PORTS(reg);
 358        ocmem->num_macros = OCMEM_HW_PROFILE_NUM_MACROS(reg);
 359        ocmem->interleaved = !!(reg & OCMEM_HW_PROFILE_INTERLEAVING);
 360
 361        num_banks = ocmem->num_ports / 2;
 362        region_size = ocmem->config->macro_size * num_banks;
 363
 364        dev_info(dev, "%u ports, %u regions, %u macros, %sinterleaved\n",
 365                 ocmem->num_ports, ocmem->config->num_regions,
 366                 ocmem->num_macros, ocmem->interleaved ? "" : "not ");
 367
 368        ocmem->regions = devm_kcalloc(dev, ocmem->config->num_regions,
 369                                      sizeof(struct ocmem_region), GFP_KERNEL);
 370        if (!ocmem->regions) {
 371                ret = -ENOMEM;
 372                goto err_clk_disable;
 373        }
 374
 375        for (i = 0; i < ocmem->config->num_regions; i++) {
 376                struct ocmem_region *region = &ocmem->regions[i];
 377
 378                if (WARN_ON(num_banks > ARRAY_SIZE(region->macro_state))) {
 379                        ret = -EINVAL;
 380                        goto err_clk_disable;
 381                }
 382
 383                region->mode = MODE_DEFAULT;
 384                region->num_macros = num_banks;
 385
 386                if (i == (ocmem->config->num_regions - 1) &&
 387                    reg & OCMEM_HW_PROFILE_LAST_REGN_HALFSIZE) {
 388                        region->macro_size = ocmem->config->macro_size / 2;
 389                        region->region_size = region_size / 2;
 390                } else {
 391                        region->macro_size = ocmem->config->macro_size;
 392                        region->region_size = region_size;
 393                }
 394
 395                for (j = 0; j < ARRAY_SIZE(region->macro_state); j++)
 396                        region->macro_state[j] = CLK_OFF;
 397        }
 398
 399        platform_set_drvdata(pdev, ocmem);
 400
 401        return 0;
 402
 403err_clk_disable:
 404        clk_bulk_disable_unprepare(ARRAY_SIZE(ocmem_clks), ocmem_clks);
 405        return ret;
 406}
 407
 408static int ocmem_dev_remove(struct platform_device *pdev)
 409{
 410        clk_bulk_disable_unprepare(ARRAY_SIZE(ocmem_clks), ocmem_clks);
 411
 412        return 0;
 413}
 414
 415static const struct ocmem_config ocmem_8974_config = {
 416        .num_regions = 3,
 417        .macro_size = SZ_128K,
 418};
 419
 420static const struct of_device_id ocmem_of_match[] = {
 421        { .compatible = "qcom,msm8974-ocmem", .data = &ocmem_8974_config },
 422        { }
 423};
 424
 425MODULE_DEVICE_TABLE(of, ocmem_of_match);
 426
 427static struct platform_driver ocmem_driver = {
 428        .probe = ocmem_dev_probe,
 429        .remove = ocmem_dev_remove,
 430        .driver = {
 431                .name = "ocmem",
 432                .of_match_table = ocmem_of_match,
 433        },
 434};
 435
 436module_platform_driver(ocmem_driver);
 437
 438MODULE_DESCRIPTION("On Chip Memory (OCMEM) allocator for some Snapdragon SoCs");
 439MODULE_LICENSE("GPL v2");
 440