Browse Source
Add memc driver for the MAX32 HyperBus peripheral, supporting HyperRAM and Xccela PSRAM memory devices. Signed-off-by: Pete Johanson <pete.johanson@analog.com>pull/89409/head
11 changed files with 422 additions and 0 deletions
@ -0,0 +1,10 @@
@@ -0,0 +1,10 @@
|
||||
# Copyright (c) 2025 Analog Devices, Inc |
||||
# SPDX-License-Identifier: Apache-2.0 |
||||
|
||||
config MEMC_MAX32_HPB |
||||
bool "MAX32 HyperBus" |
||||
default y |
||||
depends on DT_HAS_ADI_MAX32_HPB_ENABLED |
||||
select PINCTRL |
||||
help |
||||
Enable ADI MAX32 HyperBus controller. |
@ -0,0 +1,140 @@
@@ -0,0 +1,140 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Analog Devices, Inc. |
||||
* |
||||
* SPDX-License-Identifier: Apache-2.0 |
||||
*/ |
||||
|
||||
#include <zephyr/device.h> |
||||
#include <soc.h> |
||||
|
||||
#include <zephyr/drivers/clock_control/adi_max32_clock_control.h> |
||||
#include <zephyr/drivers/pinctrl.h> |
||||
|
||||
#include <zephyr/logging/log.h> |
||||
|
||||
LOG_MODULE_REGISTER(memc_max32_hpb, CONFIG_MEMC_LOG_LEVEL); |
||||
|
||||
#include <hpb.h> |
||||
#include <emcc.h> |
||||
|
||||
#define DT_DRV_COMPAT adi_max32_hpb |
||||
|
||||
struct memc_max32_hpb_config { |
||||
const struct device *clock; |
||||
const struct pinctrl_dev_config *pcfg; |
||||
}; |
||||
|
||||
struct memc_max32_hpb_mem_config { |
||||
uint8_t reg; |
||||
|
||||
mxc_hpb_mem_config_t config; |
||||
}; |
||||
|
||||
/* clang-format off */ |
||||
|
||||
#define MEM_CONFIG(n) \ |
||||
{ \ |
||||
.reg = DT_REG_ADDR(n), \ |
||||
.config = {.device_type = DT_PROP(n, device_type), \ |
||||
.base_addr = DT_PROP(n, base_address), \ |
||||
.latency_cycle = DT_PROP_OR(n, latency_cycles, 1), \ |
||||
.write_cs_high = DT_PROP_OR(n, write_cs_high, 0), \ |
||||
.read_cs_high = DT_PROP_OR(n, read_cs_high, 0), \ |
||||
.write_cs_hold = DT_PROP_OR(n, write_cs_hold, 0), \ |
||||
.read_cs_hold = DT_PROP_OR(n, read_cs_hold, 0), \ |
||||
.write_cs_setup = DT_PROP_OR(n, write_cs_setup, 0), \ |
||||
.read_cs_setup = DT_PROP_OR(n, read_cs_setup, 0), \ |
||||
.fixed_latency = DT_PROP_OR(n, fixed_read_latency, 0), \ |
||||
COND_CODE_1(DT_NODE_HAS_PROP(n, config_regs), \ |
||||
(.cfg_reg_val = config_regs_##n, \ |
||||
.cfg_reg_val_len = ARRAY_SIZE(config_regs_##n)), ()) }, \ |
||||
} |
||||
|
||||
/* clang-format on */ |
||||
|
||||
#define CR_ENTRY(idx, n) \ |
||||
{ \ |
||||
.addr = DT_PROP_BY_IDX(n, config_regs, idx), \ |
||||
.val = DT_PROP_BY_IDX(n, config_reg_vals, idx), \ |
||||
} |
||||
|
||||
#define MEM_CR_ENTRIES(n) \ |
||||
COND_CODE_1(DT_NODE_HAS_PROP(n, config_regs), ( \ |
||||
BUILD_ASSERT(DT_PROP_LEN(n, config_regs) == DT_PROP_LEN(n, config_reg_vals), \ |
||||
"The config-regs and config-reg-vals properties of adi,max32-hpb memory device" \ |
||||
" child nodes must be the same length"); \ |
||||
static const mxc_hpb_cfg_reg_val_t config_regs_##n[] = \ |
||||
{ LISTIFY(DT_PROP_LEN(n, config_regs), CR_ENTRY, (,), n) }; \ |
||||
), ()) |
||||
|
||||
/** memory device configuration(s). */ |
||||
DT_INST_FOREACH_CHILD(0, MEM_CR_ENTRIES) |
||||
|
||||
/* clang-format off */ |
||||
|
||||
static const struct memc_max32_hpb_mem_config mem_configs[] = { |
||||
DT_INST_FOREACH_CHILD_SEP(0, MEM_CONFIG, (,))}; |
||||
|
||||
#define CLOCK_CFG(node_id, prop, idx) \ |
||||
{.bus = DT_CLOCKS_CELL_BY_IDX(node_id, idx, offset), \ |
||||
.bit = DT_CLOCKS_CELL_BY_IDX(node_id, idx, bit)} |
||||
|
||||
static const struct max32_perclk perclks[] = { |
||||
DT_INST_FOREACH_PROP_ELEM_SEP(0, clocks, CLOCK_CFG, (,))}; |
||||
|
||||
/* clang-format on */ |
||||
static int memc_max32_hpb_init(const struct device *dev) |
||||
{ |
||||
const struct memc_max32_hpb_config *config = dev->config; |
||||
|
||||
int r; |
||||
const mxc_hpb_mem_config_t *mem0 = NULL, *mem1 = NULL; |
||||
|
||||
if (!device_is_ready(config->clock)) { |
||||
LOG_ERR("clock control device not ready"); |
||||
return -ENODEV; |
||||
} |
||||
|
||||
for (size_t i = 0; i < ARRAY_SIZE(perclks); i++) { |
||||
r = clock_control_on(config->clock, (clock_control_subsys_t)&perclks[i]); |
||||
if (r < 0) { |
||||
LOG_ERR("Could not initialize HPB clock (%d)", r); |
||||
return r; |
||||
} |
||||
} |
||||
|
||||
for (size_t i = 0; i < ARRAY_SIZE(mem_configs); i++) { |
||||
if (mem_configs[i].reg == 0) { |
||||
mem0 = &mem_configs[i].config; |
||||
} else if (mem_configs[i].reg == 1) { |
||||
mem1 = &mem_configs[i].config; |
||||
} |
||||
} |
||||
|
||||
/* configure pinmux */ |
||||
r = pinctrl_apply_state(config->pcfg, PINCTRL_STATE_DEFAULT); |
||||
if (r < 0) { |
||||
LOG_ERR("HPB pinctrl setup failed (%d)", r); |
||||
return r; |
||||
} |
||||
|
||||
r = MXC_HPB_Init(mem0, mem1); |
||||
if (r < 0) { |
||||
LOG_ERR("HPB init failed (%d)", r); |
||||
return r; |
||||
} |
||||
|
||||
COND_CODE_1(DT_INST_PROP(0, enable_emcc), (MXC_EMCC_Enable()), (MXC_EMCC_Disable())); |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
PINCTRL_DT_INST_DEFINE(0); |
||||
|
||||
static const struct memc_max32_hpb_config config = { |
||||
.clock = DEVICE_DT_GET(DT_INST_CLOCKS_CTLR(0)), |
||||
.pcfg = PINCTRL_DT_INST_DEV_CONFIG_GET(0), |
||||
}; |
||||
|
||||
DEVICE_DT_INST_DEFINE(0, memc_max32_hpb_init, NULL, NULL, &config, POST_KERNEL, |
||||
CONFIG_MEMC_INIT_PRIORITY, NULL); |
@ -0,0 +1,154 @@
@@ -0,0 +1,154 @@
|
||||
# Copyright (c) 2025 Analog Devices, Inc. |
||||
# SPDX-License-Identifier: Apache-2.0 |
||||
|
||||
description: | |
||||
MAX32 HyperBus (HPB) Memory Controller Interface |
||||
|
||||
The HyperBus and Xccela Memory Controller interface is a high-speed, low-pin |
||||
count interface for connecting to one or more compatible external memory |
||||
devices. The external HyperBus or Xccela Bus memory device is mapped into the |
||||
memory space enabling direct code execution, data storage, or both. |
||||
|
||||
The memory devices are defined as children of the HPB memory controller node. |
||||
|
||||
&hpb { |
||||
status = "okay"; |
||||
pinctrl-0 = <&hyp_cs0n_p1_11 &hyp_d0_p1_12 &hyp_d1_p1_15 |
||||
&hyp_d2_p1_19 &hyp_d3_p1_20 &hyp_d4_p1_13 |
||||
&hyp_d5_p1_16 &hyp_d6_p1_18 &hyp_d7_p1_21>; |
||||
pinctrl-names = "default"; |
||||
|
||||
mem@0 { |
||||
reg = <0>; |
||||
base-address = <0x60000000>; |
||||
device-type = <ADI_MAX32_HPB_DEV_TYPE_HYPER_RAM>; |
||||
config-regs = <1>; |
||||
config-reg-vals = <2>; |
||||
}; |
||||
}; |
||||
|
||||
Note: the values for most properties take values from |
||||
zephyr/dt-bindings/memory-controller/adi-max32-hpb.h header which will need to |
||||
be included. |
||||
|
||||
Finally, in order to make the memory available you will need to define new |
||||
memory device/s in DeviceTree, e.g.: |
||||
|
||||
sdram1: sdram@60000000 { |
||||
compatible = "zephyr,memory-region", "mmio-sram"; |
||||
device_type = "memory"; |
||||
reg = <0x60000000 DT_SIZE_M(X)>; |
||||
zephyr,memory-region = "SDRAM1"; |
||||
}; |
||||
|
||||
compatible: "adi,max32-hpb" |
||||
|
||||
include: [base.yaml, pinctrl-device.yaml] |
||||
|
||||
properties: |
||||
reg: |
||||
required: true |
||||
|
||||
clocks: |
||||
required: true |
||||
|
||||
pinctrl-0: |
||||
required: true |
||||
|
||||
pinctrl-names: |
||||
required: true |
||||
|
||||
"#address-cells": |
||||
required: true |
||||
const: 1 |
||||
|
||||
"#size-cells": |
||||
required: true |
||||
const: 0 |
||||
|
||||
enable-emcc: |
||||
type: boolean |
||||
description: | |
||||
Enable the EMCC cache controller for the HyperBus memory devices. |
||||
|
||||
child-binding: |
||||
description: Memory device. |
||||
|
||||
properties: |
||||
reg: |
||||
type: int |
||||
required: true |
||||
|
||||
base-address: |
||||
type: int |
||||
description: | |
||||
The address to which to map this memory device, e.g. 0x60000000. See the |
||||
user guide for your specific SoC for the allowed range for mapping. |
||||
|
||||
device-type: |
||||
type: int |
||||
required: true |
||||
description: | |
||||
The type of attached memory device, i.e. Hyper Flash, Xccela PSRAM, or |
||||
Hyper RAM. |
||||
|
||||
fixed-read-latency: |
||||
type: boolean |
||||
description: | |
||||
Enable Xccela bus Fixed Read Latency. Should match the Latency Type |
||||
configuration in the target PSRAM. |
||||
|
||||
read-cs-high: |
||||
type: int |
||||
description: | |
||||
The CS# high time, in clock cycles, between read operations. |
||||
|
||||
write-cs-high: |
||||
type: int |
||||
description: | |
||||
The CS# high time, in clock cycles, between write operations. |
||||
|
||||
read-cs-setup: |
||||
type: int |
||||
description: | |
||||
The CS# latency, in clock cycles, for read operations. This adds |
||||
additional clock cycles after CS# goes low. |
||||
|
||||
write-cs-setup: |
||||
type: int |
||||
description: | |
||||
The CS# latency, in clock cycles, for write operations. This adds |
||||
additional clock cycles after CS# goes low. |
||||
|
||||
read-cs-hold: |
||||
type: int |
||||
description: | |
||||
The CS# hold time, in clock cycles, between the completion of a read |
||||
operation and the CS# deassertion. |
||||
|
||||
write-cs-hold: |
||||
type: int |
||||
description: | |
||||
The CS# hold time, in clock cycles, between the completion of a write |
||||
operation and the CS# deassertion. |
||||
|
||||
latency-cycles: |
||||
type: int |
||||
description: | |
||||
For HyperRAM: set this property to match the external HyperRAM Read |
||||
Latency Configuration Register value. |
||||
|
||||
For Xccela PSRAM: The value is adjusted based on `fixed-read-latency` |
||||
property also being set. |
||||
|
||||
config-regs: |
||||
type: array |
||||
description: | |
||||
Configuration register addresses to set on the memory device during |
||||
initialization. |
||||
|
||||
config-reg-vals: |
||||
type: array |
||||
description: | |
||||
Configuration register values to set on the memory device during |
||||
initialization. |
@ -0,0 +1,53 @@
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* Copyright 2023 NXP |
||||
* |
||||
* SPDX-License-Identifier: Apache-2.0 |
||||
*/ |
||||
|
||||
#ifndef ZEPHYR_INCLUDE_DT_BINDINGS_MEMORY_CONTROLLER_ADI_MAX32_HPB_H_ |
||||
#define ZEPHYR_INCLUDE_DT_BINDINGS_MEMORY_CONTROLLER_ADI_MAX32_HPB_H_ |
||||
|
||||
#define ADI_MAX32_HPB_CS_HIGH_1_5 0 /**< CS High 1.5 clock cycles */ |
||||
#define ADI_MAX32_HPB_CS_HIGH_2_5 1 /**< CS High 2.5 clock cycles */ |
||||
#define ADI_MAX32_HPB_CS_HIGH_3_5 2 /**< CS High 3.5 clock cycles */ |
||||
#define ADI_MAX32_HPB_CS_HIGH_4_5 3 /**< CS High 4.5 clock cycles */ |
||||
#define ADI_MAX32_HPB_CS_HIGH_5_5 4 /**< CS High 5.5 clock cycles */ |
||||
#define ADI_MAX32_HPB_CS_HIGH_6_5 5 /**< CS High 6.5 clock cycles */ |
||||
#define ADI_MAX32_HPB_CS_HIGH_7_5 6 /**< CS High 7.5 clock cycles */ |
||||
#define ADI_MAX32_HPB_CS_HIGH_8_5 7 /**< CS High 8.5 clock cycles */ |
||||
#define ADI_MAX32_HPB_CS_HIGH_9_5 8 /**< CS High 9.5 clock cycles */ |
||||
#define ADI_MAX32_HPB_CS_HIGH_10_5 9 /**< CS High 10.5 clock cycles */ |
||||
#define ADI_MAX32_HPB_CS_HIGH_11_5 10 /**< CS High 11.5 clock cycles */ |
||||
#define ADI_MAX32_HPB_CS_HIGH_12_5 11 /**< CS High 12.5 clock cycles */ |
||||
#define ADI_MAX32_HPB_CS_HIGH_13_5 12 /**< CS High 13.5 clock cycles */ |
||||
#define ADI_MAX32_HPB_CS_HIGH_14_5 13 /**< CS High 14.5 clock cycles */ |
||||
#define ADI_MAX32_HPB_CS_HIGH_15_5 14 /**< CS High 15.5 clock cycles */ |
||||
#define ADI_MAX32_HPB_CS_HIGH_16_5 15 /**< CS High 16.5 clock cycles */ |
||||
|
||||
#define ADI_MAX32_HPB_CS_SETUP_HOLD_1 0 /**< CS Setup/Hold 1 clock cycles */ |
||||
#define ADI_MAX32_HPB_CS_SETUP_HOLD_2 1 /**< CS Setup/Hold 2 clock cycles */ |
||||
#define ADI_MAX32_HPB_CS_SETUP_HOLD_3 2 /**< CS Setup/Hold 3 clock cycles */ |
||||
#define ADI_MAX32_HPB_CS_SETUP_HOLD_4 3 /**< CS Setup/Hold 4 clock cycles */ |
||||
#define ADI_MAX32_HPB_CS_SETUP_HOLD_5 4 /**< CS Setup/Hold 5 clock cycles */ |
||||
#define ADI_MAX32_HPB_CS_SETUP_HOLD_6 5 /**< CS Setup/Hold 6 clock cycles */ |
||||
#define ADI_MAX32_HPB_CS_SETUP_HOLD_7 6 /**< CS Setup/Hold 7 clock cycles */ |
||||
#define ADI_MAX32_HPB_CS_SETUP_HOLD_8 7 /**< CS Setup/Hold 8 clock cycles */ |
||||
#define ADI_MAX32_HPB_CS_SETUP_HOLD_9 8 /**< CS Setup/Hold 9 clock cycles */ |
||||
#define ADI_MAX32_HPB_CS_SETUP_HOLD_10 9 /**< CS Setup/Hold 10 clock cycles */ |
||||
#define ADI_MAX32_HPB_CS_SETUP_HOLD_11 10 /**< CS Setup/Hold 11 clock cycles */ |
||||
#define ADI_MAX32_HPB_CS_SETUP_HOLD_12 11 /**< CS Setup/Hold 12 clock cycles */ |
||||
#define ADI_MAX32_HPB_CS_SETUP_HOLD_13 12 /**< CS Setup/Hold 13 clock cycles */ |
||||
#define ADI_MAX32_HPB_CS_SETUP_HOLD_14 13 /**< CS Setup/Hold 14 clock cycles */ |
||||
#define ADI_MAX32_HPB_CS_SETUP_HOLD_15 14 /**< CS Setup/Hold 15 clock cycles */ |
||||
#define ADI_MAX32_HPB_CS_SETUP_HOLD_16 15 /**< CS Setup/Hold 16 clock cycles */ |
||||
|
||||
#define ADI_MAX32_HPB_LAT_5 0x0 /**< 5 clock latency for RAM */ |
||||
#define ADI_MAX32_HPB_LAT_6 0x1 /**< 6 clock latency for RAM */ |
||||
#define ADI_MAX32_HPB_LAT_3 0xE /**< 3 clock latency for RAM */ |
||||
#define ADI_MAX32_HPB_LAT_4 0xF /**< 4 clock latency for RAM */ |
||||
|
||||
#define ADI_MAX32_HPB_DEV_TYPE_HYPER_FLASH 0 |
||||
#define ADI_MAX32_HPB_DEV_TYPE_XCCELA_PSRAM 1 |
||||
#define ADI_MAX32_HPB_DEV_TYPE_HYPER_RAM 2 |
||||
|
||||
#endif /* ZEPHYR_INCLUDE_DT_BINDINGS_MEMORY_CONTROLLER_ADI_MAX32_HPB_H_ */ |
@ -0,0 +1,21 @@
@@ -0,0 +1,21 @@
|
||||
/* |
||||
* Copyright (c) 2025 Analog Devices, Inc. |
||||
* |
||||
* SPDX-License-Identifier: Apache-2.0 |
||||
*/ |
||||
|
||||
&sram1 { |
||||
status = "disabled"; |
||||
}; |
||||
|
||||
&sram2 { |
||||
status = "disabled"; |
||||
}; |
||||
|
||||
&hpb { |
||||
status = "okay"; |
||||
}; |
||||
|
||||
&sdram1 { |
||||
status = "okay"; |
||||
}; |
Loading…
Reference in new issue