1 // SPDX-License-Identifier: GPL-2.0-only
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.
8 * Copyright (C) 2019 Brian Masney <masneyb@onstation.org>
9 * Copyright (C) 2015 Red Hat. Author: Rob Clark <robdclark@gmail.com>
12 #include <linux/bitfield.h>
13 #include <linux/clk.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>
28 MODE_DEFAULT
= WIDE_MODE
,
31 enum ocmem_macro_state
{
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
;
49 unsigned long macro_size
;
54 const struct ocmem_config
*config
;
55 struct resource
*memory
;
57 unsigned int num_ports
;
58 unsigned int num_macros
;
60 struct ocmem_region
*regions
;
61 unsigned long active_allocations
;
64 #define OCMEM_MIN_ALIGN SZ_64K
65 #define OCMEM_MIN_ALLOC SZ_64K
67 #define OCMEM_REG_HW_VERSION 0x00000000
68 #define OCMEM_REG_HW_PROFILE 0x00000004
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
76 #define OCMEM_REG_GFX_MPU_START 0x00001004
77 #define OCMEM_REG_GFX_MPU_END 0x00001008
79 #define OCMEM_HW_PROFILE_NUM_PORTS(val) FIELD_PREP(0x0000000f, (val))
80 #define OCMEM_HW_PROFILE_NUM_MACROS(val) FIELD_PREP(0x00003f00, (val))
82 #define OCMEM_HW_PROFILE_LAST_REGN_HALFSIZE 0x00010000
83 #define OCMEM_HW_PROFILE_INTERLEAVING 0x00020000
84 #define OCMEM_REG_GEN_STATUS 0x0000000c
86 #define OCMEM_REG_PSGSC_STATUS 0x00000038
87 #define OCMEM_REG_PSGSC_CTL(i0) (0x0000003c + 0x1*(i0))
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))
94 #define OCMEM_CLK_CORE_IDX 0
95 static struct clk_bulk_data ocmem_clks
[] = {
104 static inline void ocmem_write(struct ocmem
*ocmem
, u32 reg
, u32 data
)
106 writel(data
, ocmem
->mmio
+ reg
);
109 static inline u32
ocmem_read(struct ocmem
*ocmem
, u32 reg
)
111 return readl(ocmem
->mmio
+ reg
);
114 static void update_ocmem(struct ocmem
*ocmem
)
116 uint32_t region_mode_ctrl
= 0x0;
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
];
123 if (region
->mode
== THIN_MODE
)
124 region_mode_ctrl
|= BIT(i
);
127 dev_dbg(ocmem
->dev
, "ocmem_region_mode_control %x\n",
129 ocmem_write(ocmem
, OCMEM_REG_REGION_MODE_CTL
, region_mode_ctrl
);
132 for (i
= 0; i
< ocmem
->config
->num_regions
; i
++) {
133 struct ocmem_region
*region
= &ocmem
->regions
[i
];
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]);
141 ocmem_write(ocmem
, OCMEM_REG_PSGSC_CTL(i
), data
);
145 static unsigned long phys_to_offset(struct ocmem
*ocmem
,
148 if (addr
< ocmem
->memory
->start
|| addr
>= ocmem
->memory
->end
)
151 return addr
- ocmem
->memory
->start
;
154 static unsigned long device_address(struct ocmem
*ocmem
,
155 enum ocmem_client client
,
158 WARN_ON(client
!= OCMEM_GRAPHICS
);
160 /* TODO: gpu uses phys_to_offset, but others do not.. */
161 return phys_to_offset(ocmem
, addr
);
164 static void update_range(struct ocmem
*ocmem
, struct ocmem_buf
*buf
,
165 enum ocmem_macro_state mstate
, enum region_mode rmode
)
167 unsigned long offset
= 0;
170 for (i
= 0; i
< ocmem
->config
->num_regions
; i
++) {
171 struct ocmem_region
*region
= &ocmem
->regions
[i
];
173 if (buf
->offset
<= offset
&& offset
< buf
->offset
+ buf
->len
)
174 region
->mode
= rmode
;
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
;
181 offset
+= region
->macro_size
;
188 struct ocmem
*of_get_ocmem(struct device
*dev
)
190 struct platform_device
*pdev
;
191 struct device_node
*devnode
;
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
);
199 pdev
= of_find_device_by_node(devnode
->parent
);
201 dev_err(dev
, "Cannot find device node %s\n", devnode
->name
);
202 return ERR_PTR(-EPROBE_DEFER
);
205 return platform_get_drvdata(pdev
);
207 EXPORT_SYMBOL(of_get_ocmem
);
209 struct ocmem_buf
*ocmem_allocate(struct ocmem
*ocmem
, enum ocmem_client client
,
212 struct ocmem_buf
*buf
;
215 /* TODO: add support for other clients... */
216 if (WARN_ON(client
!= OCMEM_GRAPHICS
))
217 return ERR_PTR(-ENODEV
);
219 if (size
< OCMEM_MIN_ALLOC
|| !IS_ALIGNED(size
, OCMEM_MIN_ALIGN
))
220 return ERR_PTR(-EINVAL
);
222 if (test_and_set_bit_lock(BIT(client
), &ocmem
->active_allocations
))
223 return ERR_PTR(-EBUSY
);
225 buf
= kzalloc(sizeof(*buf
), GFP_KERNEL
);
232 buf
->addr
= device_address(ocmem
, client
, buf
->offset
);
235 update_range(ocmem
, buf
, CORE_ON
, WIDE_MODE
);
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
);
241 dev_err(ocmem
->dev
, "could not lock: %d\n", ret
);
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
);
251 dev_dbg(ocmem
->dev
, "using %ldK of OCMEM at 0x%08lx for client %d\n",
252 size
/ 1024, buf
->addr
, client
);
259 clear_bit_unlock(BIT(client
), &ocmem
->active_allocations
);
263 EXPORT_SYMBOL(ocmem_allocate
);
265 void ocmem_free(struct ocmem
*ocmem
, enum ocmem_client client
,
266 struct ocmem_buf
*buf
)
268 /* TODO: add support for other clients... */
269 if (WARN_ON(client
!= OCMEM_GRAPHICS
))
272 update_range(ocmem
, buf
, CLK_OFF
, MODE_DEFAULT
);
274 if (qcom_scm_ocmem_lock_available()) {
277 ret
= qcom_scm_ocmem_unlock(QCOM_SCM_OCMEM_GRAPHICS_ID
,
278 buf
->offset
, buf
->len
);
280 dev_err(ocmem
->dev
, "could not unlock: %d\n", ret
);
282 ocmem_write(ocmem
, OCMEM_REG_GFX_MPU_START
, 0x0);
283 ocmem_write(ocmem
, OCMEM_REG_GFX_MPU_END
, 0x0);
288 clear_bit_unlock(BIT(client
), &ocmem
->active_allocations
);
290 EXPORT_SYMBOL(ocmem_free
);
292 static int ocmem_dev_probe(struct platform_device
*pdev
)
294 struct device
*dev
= &pdev
->dev
;
295 unsigned long reg
, region_size
;
296 int i
, j
, ret
, num_banks
;
297 struct resource
*res
;
300 if (!qcom_scm_is_available())
301 return -EPROBE_DEFER
;
303 ocmem
= devm_kzalloc(dev
, sizeof(*ocmem
), GFP_KERNEL
);
308 ocmem
->config
= device_get_match_data(dev
);
310 ret
= devm_clk_bulk_get(dev
, ARRAY_SIZE(ocmem_clks
), ocmem_clks
);
312 if (ret
!= -EPROBE_DEFER
)
313 dev_err(dev
, "Unable to get clocks\n");
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
);
325 ocmem
->memory
= platform_get_resource_byname(pdev
, IORESOURCE_MEM
,
327 if (!ocmem
->memory
) {
328 dev_err(dev
, "Could not get mem region\n");
332 /* The core clock is synchronous with graphics */
333 WARN_ON(clk_set_rate(ocmem_clks
[OCMEM_CLK_CORE_IDX
].clk
, 1000) < 0);
335 ret
= clk_bulk_prepare_enable(ARRAY_SIZE(ocmem_clks
), ocmem_clks
);
337 dev_info(ocmem
->dev
, "Failed to enable clocks\n");
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);
345 dev_err(dev
, "Could not enable secure configuration\n");
346 goto err_clk_disable
;
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
);
355 num_banks
= ocmem
->num_ports
/ 2;
356 region_size
= ocmem
->config
->macro_size
* num_banks
;
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 ");
362 ocmem
->regions
= devm_kcalloc(dev
, ocmem
->config
->num_regions
,
363 sizeof(struct ocmem_region
), GFP_KERNEL
);
364 if (!ocmem
->regions
) {
366 goto err_clk_disable
;
369 for (i
= 0; i
< ocmem
->config
->num_regions
; i
++) {
370 struct ocmem_region
*region
= &ocmem
->regions
[i
];
372 if (WARN_ON(num_banks
> ARRAY_SIZE(region
->macro_state
))) {
374 goto err_clk_disable
;
377 region
->mode
= MODE_DEFAULT
;
378 region
->num_macros
= num_banks
;
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;
385 region
->macro_size
= ocmem
->config
->macro_size
;
386 region
->region_size
= region_size
;
389 for (j
= 0; j
< ARRAY_SIZE(region
->macro_state
); j
++)
390 region
->macro_state
[j
] = CLK_OFF
;
393 platform_set_drvdata(pdev
, ocmem
);
398 clk_bulk_disable_unprepare(ARRAY_SIZE(ocmem_clks
), ocmem_clks
);
402 static int ocmem_dev_remove(struct platform_device
*pdev
)
404 clk_bulk_disable_unprepare(ARRAY_SIZE(ocmem_clks
), ocmem_clks
);
409 static const struct ocmem_config ocmem_8974_config
= {
411 .macro_size
= SZ_128K
,
414 static const struct of_device_id ocmem_of_match
[] = {
415 { .compatible
= "qcom,msm8974-ocmem", .data
= &ocmem_8974_config
},
419 MODULE_DEVICE_TABLE(of
, ocmem_of_match
);
421 static struct platform_driver ocmem_driver
= {
422 .probe
= ocmem_dev_probe
,
423 .remove
= ocmem_dev_remove
,
426 .of_match_table
= ocmem_of_match
,
430 module_platform_driver(ocmem_driver
);
432 MODULE_DESCRIPTION("On Chip Memory (OCMEM) allocator for some Snapdragon SoCs");
433 MODULE_LICENSE("GPL v2");