162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * The On Chip Memory (OCMEM) allocator allows various clients to allocate 462306a36Sopenharmony_ci * memory from OCMEM based on performance, latency and power requirements. 562306a36Sopenharmony_ci * This is typically used by the GPU, camera/video, and audio components on 662306a36Sopenharmony_ci * some Snapdragon SoCs. 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci * Copyright (C) 2019 Brian Masney <masneyb@onstation.org> 962306a36Sopenharmony_ci * Copyright (C) 2015 Red Hat. Author: Rob Clark <robdclark@gmail.com> 1062306a36Sopenharmony_ci */ 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci#include <linux/bitfield.h> 1362306a36Sopenharmony_ci#include <linux/clk.h> 1462306a36Sopenharmony_ci#include <linux/io.h> 1562306a36Sopenharmony_ci#include <linux/kernel.h> 1662306a36Sopenharmony_ci#include <linux/module.h> 1762306a36Sopenharmony_ci#include <linux/of.h> 1862306a36Sopenharmony_ci#include <linux/of_platform.h> 1962306a36Sopenharmony_ci#include <linux/platform_device.h> 2062306a36Sopenharmony_ci#include <linux/firmware/qcom/qcom_scm.h> 2162306a36Sopenharmony_ci#include <linux/sizes.h> 2262306a36Sopenharmony_ci#include <linux/slab.h> 2362306a36Sopenharmony_ci#include <linux/types.h> 2462306a36Sopenharmony_ci#include <soc/qcom/ocmem.h> 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_cienum region_mode { 2762306a36Sopenharmony_ci WIDE_MODE = 0x0, 2862306a36Sopenharmony_ci THIN_MODE, 2962306a36Sopenharmony_ci MODE_DEFAULT = WIDE_MODE, 3062306a36Sopenharmony_ci}; 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_cienum ocmem_macro_state { 3362306a36Sopenharmony_ci PASSTHROUGH = 0, 3462306a36Sopenharmony_ci PERI_ON = 1, 3562306a36Sopenharmony_ci CORE_ON = 2, 3662306a36Sopenharmony_ci CLK_OFF = 4, 3762306a36Sopenharmony_ci}; 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_cistruct ocmem_region { 4062306a36Sopenharmony_ci bool interleaved; 4162306a36Sopenharmony_ci enum region_mode mode; 4262306a36Sopenharmony_ci unsigned int num_macros; 4362306a36Sopenharmony_ci enum ocmem_macro_state macro_state[4]; 4462306a36Sopenharmony_ci unsigned long macro_size; 4562306a36Sopenharmony_ci unsigned long region_size; 4662306a36Sopenharmony_ci}; 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_cistruct ocmem_config { 4962306a36Sopenharmony_ci uint8_t num_regions; 5062306a36Sopenharmony_ci unsigned long macro_size; 5162306a36Sopenharmony_ci}; 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_cistruct ocmem { 5462306a36Sopenharmony_ci struct device *dev; 5562306a36Sopenharmony_ci const struct ocmem_config *config; 5662306a36Sopenharmony_ci struct resource *memory; 5762306a36Sopenharmony_ci void __iomem *mmio; 5862306a36Sopenharmony_ci struct clk *core_clk; 5962306a36Sopenharmony_ci struct clk *iface_clk; 6062306a36Sopenharmony_ci unsigned int num_ports; 6162306a36Sopenharmony_ci unsigned int num_macros; 6262306a36Sopenharmony_ci bool interleaved; 6362306a36Sopenharmony_ci struct ocmem_region *regions; 6462306a36Sopenharmony_ci unsigned long active_allocations; 6562306a36Sopenharmony_ci}; 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci#define OCMEM_MIN_ALIGN SZ_64K 6862306a36Sopenharmony_ci#define OCMEM_MIN_ALLOC SZ_64K 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci#define OCMEM_REG_HW_VERSION 0x00000000 7162306a36Sopenharmony_ci#define OCMEM_REG_HW_PROFILE 0x00000004 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci#define OCMEM_REG_REGION_MODE_CTL 0x00001000 7462306a36Sopenharmony_ci#define OCMEM_REGION_MODE_CTL_REG0_THIN 0x00000001 7562306a36Sopenharmony_ci#define OCMEM_REGION_MODE_CTL_REG1_THIN 0x00000002 7662306a36Sopenharmony_ci#define OCMEM_REGION_MODE_CTL_REG2_THIN 0x00000004 7762306a36Sopenharmony_ci#define OCMEM_REGION_MODE_CTL_REG3_THIN 0x00000008 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci#define OCMEM_REG_GFX_MPU_START 0x00001004 8062306a36Sopenharmony_ci#define OCMEM_REG_GFX_MPU_END 0x00001008 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci#define OCMEM_HW_VERSION_MAJOR(val) FIELD_GET(GENMASK(31, 28), val) 8362306a36Sopenharmony_ci#define OCMEM_HW_VERSION_MINOR(val) FIELD_GET(GENMASK(27, 16), val) 8462306a36Sopenharmony_ci#define OCMEM_HW_VERSION_STEP(val) FIELD_GET(GENMASK(15, 0), val) 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci#define OCMEM_HW_PROFILE_NUM_PORTS(val) FIELD_GET(0x0000000f, (val)) 8762306a36Sopenharmony_ci#define OCMEM_HW_PROFILE_NUM_MACROS(val) FIELD_GET(0x00003f00, (val)) 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci#define OCMEM_HW_PROFILE_LAST_REGN_HALFSIZE 0x00010000 9062306a36Sopenharmony_ci#define OCMEM_HW_PROFILE_INTERLEAVING 0x00020000 9162306a36Sopenharmony_ci#define OCMEM_REG_GEN_STATUS 0x0000000c 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci#define OCMEM_REG_PSGSC_STATUS 0x00000038 9462306a36Sopenharmony_ci#define OCMEM_REG_PSGSC_CTL(i0) (0x0000003c + 0x1*(i0)) 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci#define OCMEM_PSGSC_CTL_MACRO0_MODE(val) FIELD_PREP(0x00000007, (val)) 9762306a36Sopenharmony_ci#define OCMEM_PSGSC_CTL_MACRO1_MODE(val) FIELD_PREP(0x00000070, (val)) 9862306a36Sopenharmony_ci#define OCMEM_PSGSC_CTL_MACRO2_MODE(val) FIELD_PREP(0x00000700, (val)) 9962306a36Sopenharmony_ci#define OCMEM_PSGSC_CTL_MACRO3_MODE(val) FIELD_PREP(0x00007000, (val)) 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_cistatic inline void ocmem_write(struct ocmem *ocmem, u32 reg, u32 data) 10262306a36Sopenharmony_ci{ 10362306a36Sopenharmony_ci writel(data, ocmem->mmio + reg); 10462306a36Sopenharmony_ci} 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_cistatic inline u32 ocmem_read(struct ocmem *ocmem, u32 reg) 10762306a36Sopenharmony_ci{ 10862306a36Sopenharmony_ci return readl(ocmem->mmio + reg); 10962306a36Sopenharmony_ci} 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_cistatic void update_ocmem(struct ocmem *ocmem) 11262306a36Sopenharmony_ci{ 11362306a36Sopenharmony_ci uint32_t region_mode_ctrl = 0x0; 11462306a36Sopenharmony_ci int i; 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci if (!qcom_scm_ocmem_lock_available()) { 11762306a36Sopenharmony_ci for (i = 0; i < ocmem->config->num_regions; i++) { 11862306a36Sopenharmony_ci struct ocmem_region *region = &ocmem->regions[i]; 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci if (region->mode == THIN_MODE) 12162306a36Sopenharmony_ci region_mode_ctrl |= BIT(i); 12262306a36Sopenharmony_ci } 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci dev_dbg(ocmem->dev, "ocmem_region_mode_control %x\n", 12562306a36Sopenharmony_ci region_mode_ctrl); 12662306a36Sopenharmony_ci ocmem_write(ocmem, OCMEM_REG_REGION_MODE_CTL, region_mode_ctrl); 12762306a36Sopenharmony_ci } 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci for (i = 0; i < ocmem->config->num_regions; i++) { 13062306a36Sopenharmony_ci struct ocmem_region *region = &ocmem->regions[i]; 13162306a36Sopenharmony_ci u32 data; 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci data = OCMEM_PSGSC_CTL_MACRO0_MODE(region->macro_state[0]) | 13462306a36Sopenharmony_ci OCMEM_PSGSC_CTL_MACRO1_MODE(region->macro_state[1]) | 13562306a36Sopenharmony_ci OCMEM_PSGSC_CTL_MACRO2_MODE(region->macro_state[2]) | 13662306a36Sopenharmony_ci OCMEM_PSGSC_CTL_MACRO3_MODE(region->macro_state[3]); 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci ocmem_write(ocmem, OCMEM_REG_PSGSC_CTL(i), data); 13962306a36Sopenharmony_ci } 14062306a36Sopenharmony_ci} 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_cistatic unsigned long phys_to_offset(struct ocmem *ocmem, 14362306a36Sopenharmony_ci unsigned long addr) 14462306a36Sopenharmony_ci{ 14562306a36Sopenharmony_ci if (addr < ocmem->memory->start || addr >= ocmem->memory->end) 14662306a36Sopenharmony_ci return 0; 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci return addr - ocmem->memory->start; 14962306a36Sopenharmony_ci} 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_cistatic unsigned long device_address(struct ocmem *ocmem, 15262306a36Sopenharmony_ci enum ocmem_client client, 15362306a36Sopenharmony_ci unsigned long addr) 15462306a36Sopenharmony_ci{ 15562306a36Sopenharmony_ci WARN_ON(client != OCMEM_GRAPHICS); 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci /* TODO: gpu uses phys_to_offset, but others do not.. */ 15862306a36Sopenharmony_ci return phys_to_offset(ocmem, addr); 15962306a36Sopenharmony_ci} 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_cistatic void update_range(struct ocmem *ocmem, struct ocmem_buf *buf, 16262306a36Sopenharmony_ci enum ocmem_macro_state mstate, enum region_mode rmode) 16362306a36Sopenharmony_ci{ 16462306a36Sopenharmony_ci unsigned long offset = 0; 16562306a36Sopenharmony_ci int i, j; 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci for (i = 0; i < ocmem->config->num_regions; i++) { 16862306a36Sopenharmony_ci struct ocmem_region *region = &ocmem->regions[i]; 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci if (buf->offset <= offset && offset < buf->offset + buf->len) 17162306a36Sopenharmony_ci region->mode = rmode; 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci for (j = 0; j < region->num_macros; j++) { 17462306a36Sopenharmony_ci if (buf->offset <= offset && 17562306a36Sopenharmony_ci offset < buf->offset + buf->len) 17662306a36Sopenharmony_ci region->macro_state[j] = mstate; 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci offset += region->macro_size; 17962306a36Sopenharmony_ci } 18062306a36Sopenharmony_ci } 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci update_ocmem(ocmem); 18362306a36Sopenharmony_ci} 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_cistruct ocmem *of_get_ocmem(struct device *dev) 18662306a36Sopenharmony_ci{ 18762306a36Sopenharmony_ci struct platform_device *pdev; 18862306a36Sopenharmony_ci struct device_node *devnode; 18962306a36Sopenharmony_ci struct ocmem *ocmem; 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ci devnode = of_parse_phandle(dev->of_node, "sram", 0); 19262306a36Sopenharmony_ci if (!devnode || !devnode->parent) { 19362306a36Sopenharmony_ci dev_err(dev, "Cannot look up sram phandle\n"); 19462306a36Sopenharmony_ci of_node_put(devnode); 19562306a36Sopenharmony_ci return ERR_PTR(-ENODEV); 19662306a36Sopenharmony_ci } 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ci pdev = of_find_device_by_node(devnode->parent); 19962306a36Sopenharmony_ci if (!pdev) { 20062306a36Sopenharmony_ci dev_err(dev, "Cannot find device node %s\n", devnode->name); 20162306a36Sopenharmony_ci of_node_put(devnode); 20262306a36Sopenharmony_ci return ERR_PTR(-EPROBE_DEFER); 20362306a36Sopenharmony_ci } 20462306a36Sopenharmony_ci of_node_put(devnode); 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ci ocmem = platform_get_drvdata(pdev); 20762306a36Sopenharmony_ci if (!ocmem) { 20862306a36Sopenharmony_ci dev_err(dev, "Cannot get ocmem\n"); 20962306a36Sopenharmony_ci put_device(&pdev->dev); 21062306a36Sopenharmony_ci return ERR_PTR(-ENODEV); 21162306a36Sopenharmony_ci } 21262306a36Sopenharmony_ci return ocmem; 21362306a36Sopenharmony_ci} 21462306a36Sopenharmony_ciEXPORT_SYMBOL(of_get_ocmem); 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_cistruct ocmem_buf *ocmem_allocate(struct ocmem *ocmem, enum ocmem_client client, 21762306a36Sopenharmony_ci unsigned long size) 21862306a36Sopenharmony_ci{ 21962306a36Sopenharmony_ci struct ocmem_buf *buf; 22062306a36Sopenharmony_ci int ret; 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ci /* TODO: add support for other clients... */ 22362306a36Sopenharmony_ci if (WARN_ON(client != OCMEM_GRAPHICS)) 22462306a36Sopenharmony_ci return ERR_PTR(-ENODEV); 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ci if (size < OCMEM_MIN_ALLOC || !IS_ALIGNED(size, OCMEM_MIN_ALIGN)) 22762306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ci if (test_and_set_bit_lock(BIT(client), &ocmem->active_allocations)) 23062306a36Sopenharmony_ci return ERR_PTR(-EBUSY); 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_ci buf = kzalloc(sizeof(*buf), GFP_KERNEL); 23362306a36Sopenharmony_ci if (!buf) { 23462306a36Sopenharmony_ci ret = -ENOMEM; 23562306a36Sopenharmony_ci goto err_unlock; 23662306a36Sopenharmony_ci } 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci buf->offset = 0; 23962306a36Sopenharmony_ci buf->addr = device_address(ocmem, client, buf->offset); 24062306a36Sopenharmony_ci buf->len = size; 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ci update_range(ocmem, buf, CORE_ON, WIDE_MODE); 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ci if (qcom_scm_ocmem_lock_available()) { 24562306a36Sopenharmony_ci ret = qcom_scm_ocmem_lock(QCOM_SCM_OCMEM_GRAPHICS_ID, 24662306a36Sopenharmony_ci buf->offset, buf->len, WIDE_MODE); 24762306a36Sopenharmony_ci if (ret) { 24862306a36Sopenharmony_ci dev_err(ocmem->dev, "could not lock: %d\n", ret); 24962306a36Sopenharmony_ci ret = -EINVAL; 25062306a36Sopenharmony_ci goto err_kfree; 25162306a36Sopenharmony_ci } 25262306a36Sopenharmony_ci } else { 25362306a36Sopenharmony_ci ocmem_write(ocmem, OCMEM_REG_GFX_MPU_START, buf->offset); 25462306a36Sopenharmony_ci ocmem_write(ocmem, OCMEM_REG_GFX_MPU_END, 25562306a36Sopenharmony_ci buf->offset + buf->len); 25662306a36Sopenharmony_ci } 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci dev_dbg(ocmem->dev, "using %ldK of OCMEM at 0x%08lx for client %d\n", 25962306a36Sopenharmony_ci size / 1024, buf->addr, client); 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ci return buf; 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_cierr_kfree: 26462306a36Sopenharmony_ci kfree(buf); 26562306a36Sopenharmony_cierr_unlock: 26662306a36Sopenharmony_ci clear_bit_unlock(BIT(client), &ocmem->active_allocations); 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_ci return ERR_PTR(ret); 26962306a36Sopenharmony_ci} 27062306a36Sopenharmony_ciEXPORT_SYMBOL(ocmem_allocate); 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_civoid ocmem_free(struct ocmem *ocmem, enum ocmem_client client, 27362306a36Sopenharmony_ci struct ocmem_buf *buf) 27462306a36Sopenharmony_ci{ 27562306a36Sopenharmony_ci /* TODO: add support for other clients... */ 27662306a36Sopenharmony_ci if (WARN_ON(client != OCMEM_GRAPHICS)) 27762306a36Sopenharmony_ci return; 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci update_range(ocmem, buf, CLK_OFF, MODE_DEFAULT); 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_ci if (qcom_scm_ocmem_lock_available()) { 28262306a36Sopenharmony_ci int ret; 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_ci ret = qcom_scm_ocmem_unlock(QCOM_SCM_OCMEM_GRAPHICS_ID, 28562306a36Sopenharmony_ci buf->offset, buf->len); 28662306a36Sopenharmony_ci if (ret) 28762306a36Sopenharmony_ci dev_err(ocmem->dev, "could not unlock: %d\n", ret); 28862306a36Sopenharmony_ci } else { 28962306a36Sopenharmony_ci ocmem_write(ocmem, OCMEM_REG_GFX_MPU_START, 0x0); 29062306a36Sopenharmony_ci ocmem_write(ocmem, OCMEM_REG_GFX_MPU_END, 0x0); 29162306a36Sopenharmony_ci } 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_ci kfree(buf); 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_ci clear_bit_unlock(BIT(client), &ocmem->active_allocations); 29662306a36Sopenharmony_ci} 29762306a36Sopenharmony_ciEXPORT_SYMBOL(ocmem_free); 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_cistatic int ocmem_dev_probe(struct platform_device *pdev) 30062306a36Sopenharmony_ci{ 30162306a36Sopenharmony_ci struct device *dev = &pdev->dev; 30262306a36Sopenharmony_ci unsigned long reg, region_size; 30362306a36Sopenharmony_ci int i, j, ret, num_banks; 30462306a36Sopenharmony_ci struct ocmem *ocmem; 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_ci if (!qcom_scm_is_available()) 30762306a36Sopenharmony_ci return -EPROBE_DEFER; 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_ci ocmem = devm_kzalloc(dev, sizeof(*ocmem), GFP_KERNEL); 31062306a36Sopenharmony_ci if (!ocmem) 31162306a36Sopenharmony_ci return -ENOMEM; 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_ci ocmem->dev = dev; 31462306a36Sopenharmony_ci ocmem->config = device_get_match_data(dev); 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_ci ocmem->core_clk = devm_clk_get(dev, "core"); 31762306a36Sopenharmony_ci if (IS_ERR(ocmem->core_clk)) 31862306a36Sopenharmony_ci return dev_err_probe(dev, PTR_ERR(ocmem->core_clk), 31962306a36Sopenharmony_ci "Unable to get core clock\n"); 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_ci ocmem->iface_clk = devm_clk_get_optional(dev, "iface"); 32262306a36Sopenharmony_ci if (IS_ERR(ocmem->iface_clk)) 32362306a36Sopenharmony_ci return dev_err_probe(dev, PTR_ERR(ocmem->iface_clk), 32462306a36Sopenharmony_ci "Unable to get iface clock\n"); 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_ci ocmem->mmio = devm_platform_ioremap_resource_byname(pdev, "ctrl"); 32762306a36Sopenharmony_ci if (IS_ERR(ocmem->mmio)) 32862306a36Sopenharmony_ci return dev_err_probe(&pdev->dev, PTR_ERR(ocmem->mmio), 32962306a36Sopenharmony_ci "Failed to ioremap ocmem_ctrl resource\n"); 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_ci ocmem->memory = platform_get_resource_byname(pdev, IORESOURCE_MEM, 33262306a36Sopenharmony_ci "mem"); 33362306a36Sopenharmony_ci if (!ocmem->memory) { 33462306a36Sopenharmony_ci dev_err(dev, "Could not get mem region\n"); 33562306a36Sopenharmony_ci return -ENXIO; 33662306a36Sopenharmony_ci } 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_ci /* The core clock is synchronous with graphics */ 33962306a36Sopenharmony_ci WARN_ON(clk_set_rate(ocmem->core_clk, 1000) < 0); 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_ci ret = clk_prepare_enable(ocmem->core_clk); 34262306a36Sopenharmony_ci if (ret) 34362306a36Sopenharmony_ci return dev_err_probe(ocmem->dev, ret, "Failed to enable core clock\n"); 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_ci ret = clk_prepare_enable(ocmem->iface_clk); 34662306a36Sopenharmony_ci if (ret) { 34762306a36Sopenharmony_ci clk_disable_unprepare(ocmem->core_clk); 34862306a36Sopenharmony_ci return dev_err_probe(ocmem->dev, ret, "Failed to enable iface clock\n"); 34962306a36Sopenharmony_ci } 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_ci if (qcom_scm_restore_sec_cfg_available()) { 35262306a36Sopenharmony_ci dev_dbg(dev, "configuring scm\n"); 35362306a36Sopenharmony_ci ret = qcom_scm_restore_sec_cfg(QCOM_SCM_OCMEM_DEV_ID, 0); 35462306a36Sopenharmony_ci if (ret) { 35562306a36Sopenharmony_ci dev_err_probe(dev, ret, "Could not enable secure configuration\n"); 35662306a36Sopenharmony_ci goto err_clk_disable; 35762306a36Sopenharmony_ci } 35862306a36Sopenharmony_ci } 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_ci reg = ocmem_read(ocmem, OCMEM_REG_HW_VERSION); 36162306a36Sopenharmony_ci dev_dbg(dev, "OCMEM hardware version: %lu.%lu.%lu\n", 36262306a36Sopenharmony_ci OCMEM_HW_VERSION_MAJOR(reg), 36362306a36Sopenharmony_ci OCMEM_HW_VERSION_MINOR(reg), 36462306a36Sopenharmony_ci OCMEM_HW_VERSION_STEP(reg)); 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_ci reg = ocmem_read(ocmem, OCMEM_REG_HW_PROFILE); 36762306a36Sopenharmony_ci ocmem->num_ports = OCMEM_HW_PROFILE_NUM_PORTS(reg); 36862306a36Sopenharmony_ci ocmem->num_macros = OCMEM_HW_PROFILE_NUM_MACROS(reg); 36962306a36Sopenharmony_ci ocmem->interleaved = !!(reg & OCMEM_HW_PROFILE_INTERLEAVING); 37062306a36Sopenharmony_ci 37162306a36Sopenharmony_ci num_banks = ocmem->num_ports / 2; 37262306a36Sopenharmony_ci region_size = ocmem->config->macro_size * num_banks; 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_ci dev_info(dev, "%u ports, %u regions, %u macros, %sinterleaved\n", 37562306a36Sopenharmony_ci ocmem->num_ports, ocmem->config->num_regions, 37662306a36Sopenharmony_ci ocmem->num_macros, ocmem->interleaved ? "" : "not "); 37762306a36Sopenharmony_ci 37862306a36Sopenharmony_ci ocmem->regions = devm_kcalloc(dev, ocmem->config->num_regions, 37962306a36Sopenharmony_ci sizeof(struct ocmem_region), GFP_KERNEL); 38062306a36Sopenharmony_ci if (!ocmem->regions) { 38162306a36Sopenharmony_ci ret = -ENOMEM; 38262306a36Sopenharmony_ci goto err_clk_disable; 38362306a36Sopenharmony_ci } 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_ci for (i = 0; i < ocmem->config->num_regions; i++) { 38662306a36Sopenharmony_ci struct ocmem_region *region = &ocmem->regions[i]; 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_ci if (WARN_ON(num_banks > ARRAY_SIZE(region->macro_state))) { 38962306a36Sopenharmony_ci ret = -EINVAL; 39062306a36Sopenharmony_ci goto err_clk_disable; 39162306a36Sopenharmony_ci } 39262306a36Sopenharmony_ci 39362306a36Sopenharmony_ci region->mode = MODE_DEFAULT; 39462306a36Sopenharmony_ci region->num_macros = num_banks; 39562306a36Sopenharmony_ci 39662306a36Sopenharmony_ci if (i == (ocmem->config->num_regions - 1) && 39762306a36Sopenharmony_ci reg & OCMEM_HW_PROFILE_LAST_REGN_HALFSIZE) { 39862306a36Sopenharmony_ci region->macro_size = ocmem->config->macro_size / 2; 39962306a36Sopenharmony_ci region->region_size = region_size / 2; 40062306a36Sopenharmony_ci } else { 40162306a36Sopenharmony_ci region->macro_size = ocmem->config->macro_size; 40262306a36Sopenharmony_ci region->region_size = region_size; 40362306a36Sopenharmony_ci } 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_ci for (j = 0; j < ARRAY_SIZE(region->macro_state); j++) 40662306a36Sopenharmony_ci region->macro_state[j] = CLK_OFF; 40762306a36Sopenharmony_ci } 40862306a36Sopenharmony_ci 40962306a36Sopenharmony_ci platform_set_drvdata(pdev, ocmem); 41062306a36Sopenharmony_ci 41162306a36Sopenharmony_ci return 0; 41262306a36Sopenharmony_ci 41362306a36Sopenharmony_cierr_clk_disable: 41462306a36Sopenharmony_ci clk_disable_unprepare(ocmem->core_clk); 41562306a36Sopenharmony_ci clk_disable_unprepare(ocmem->iface_clk); 41662306a36Sopenharmony_ci return ret; 41762306a36Sopenharmony_ci} 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_cistatic int ocmem_dev_remove(struct platform_device *pdev) 42062306a36Sopenharmony_ci{ 42162306a36Sopenharmony_ci struct ocmem *ocmem = platform_get_drvdata(pdev); 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_ci clk_disable_unprepare(ocmem->core_clk); 42462306a36Sopenharmony_ci clk_disable_unprepare(ocmem->iface_clk); 42562306a36Sopenharmony_ci 42662306a36Sopenharmony_ci return 0; 42762306a36Sopenharmony_ci} 42862306a36Sopenharmony_ci 42962306a36Sopenharmony_cistatic const struct ocmem_config ocmem_8226_config = { 43062306a36Sopenharmony_ci .num_regions = 1, 43162306a36Sopenharmony_ci .macro_size = SZ_128K, 43262306a36Sopenharmony_ci}; 43362306a36Sopenharmony_ci 43462306a36Sopenharmony_cistatic const struct ocmem_config ocmem_8974_config = { 43562306a36Sopenharmony_ci .num_regions = 3, 43662306a36Sopenharmony_ci .macro_size = SZ_128K, 43762306a36Sopenharmony_ci}; 43862306a36Sopenharmony_ci 43962306a36Sopenharmony_cistatic const struct of_device_id ocmem_of_match[] = { 44062306a36Sopenharmony_ci { .compatible = "qcom,msm8226-ocmem", .data = &ocmem_8226_config }, 44162306a36Sopenharmony_ci { .compatible = "qcom,msm8974-ocmem", .data = &ocmem_8974_config }, 44262306a36Sopenharmony_ci { } 44362306a36Sopenharmony_ci}; 44462306a36Sopenharmony_ci 44562306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, ocmem_of_match); 44662306a36Sopenharmony_ci 44762306a36Sopenharmony_cistatic struct platform_driver ocmem_driver = { 44862306a36Sopenharmony_ci .probe = ocmem_dev_probe, 44962306a36Sopenharmony_ci .remove = ocmem_dev_remove, 45062306a36Sopenharmony_ci .driver = { 45162306a36Sopenharmony_ci .name = "ocmem", 45262306a36Sopenharmony_ci .of_match_table = ocmem_of_match, 45362306a36Sopenharmony_ci }, 45462306a36Sopenharmony_ci}; 45562306a36Sopenharmony_ci 45662306a36Sopenharmony_cimodule_platform_driver(ocmem_driver); 45762306a36Sopenharmony_ci 45862306a36Sopenharmony_ciMODULE_DESCRIPTION("On Chip Memory (OCMEM) allocator for some Snapdragon SoCs"); 45962306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 460