mirror of
https://source.denx.de/u-boot/u-boot.git
synced 2026-06-13 15:03:58 +03:00
Merge tag 'fsl-qoriq-next-2026-06-09' of https://source.denx.de/u-boot/custodians/u-boot-fsl-qoriq into next
CI: https://source.denx.de/u-boot/custodians/u-boot-fsl-qoriq/-/pipelines/30397 - Add JEDEC JC_42.4 thermal driver - Add QorIQ/Layerscape TMU support - Misc dts fix for fsl-lx2160a - Misc i.MX9 SCMI updates
This commit is contained in:
@@ -1297,6 +1297,11 @@ T: git https://source.denx.de/u-boot/u-boot.git
|
||||
F: cmd/i3c.c
|
||||
F: drivers/i3c/
|
||||
|
||||
JEDEC JC-42.4 / TSE2004av TEMPERATURE SENSOR
|
||||
M: Vincent Jardin <vjardin@free.fr>
|
||||
S: Maintained
|
||||
F: drivers/thermal/jc42.c
|
||||
|
||||
KWBIMAGE / KWBOOT TOOLS
|
||||
M: Pali Rohár <pali@kernel.org>
|
||||
M: Marek Behún <kabel@kernel.org>
|
||||
|
||||
@@ -986,6 +986,27 @@ uint get_svr(void)
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Layerscape mirror of the i.MX get_cpu_temp_grade(). i.MX reads the
|
||||
* OCOTP "CPU temp grade" fuses; Layerscape has no such fuse, so the
|
||||
* limits come from the data sheet instead. LX2160A Reference Manual
|
||||
* Rev. 1 (10/2021) section 1.12.1 specifies the maximum operating
|
||||
* junction temperature at 105 degC for commercial / embedded parts;
|
||||
* the lower bound is the standard -40 degC commercial low.
|
||||
*
|
||||
* The TMU itself is documented as accurate within +/- 3 degC (RM
|
||||
* section 28.1), which the thermal driver clears by setting its
|
||||
* alert threshold 10 degC below critical.
|
||||
*/
|
||||
u32 get_cpu_temp_grade(int *minc, int *maxc)
|
||||
{
|
||||
if (minc)
|
||||
*minc = -40;
|
||||
if (maxc)
|
||||
*maxc = 105;
|
||||
return 0; /* commercial */
|
||||
}
|
||||
|
||||
#ifdef CONFIG_DISPLAY_CPUINFO
|
||||
int print_cpuinfo(void)
|
||||
{
|
||||
|
||||
@@ -292,7 +292,7 @@
|
||||
#interrupt-cells = <2>;
|
||||
};
|
||||
|
||||
watchdog@23a0000 {
|
||||
wdt0: watchdog@23a0000 {
|
||||
compatible = "arm,sbsa-gwdt";
|
||||
reg = <0x0 0x23a0000 0 0x1000>,
|
||||
<0x0 0x2390000 0 0x1000>;
|
||||
@@ -594,6 +594,64 @@
|
||||
};
|
||||
};
|
||||
|
||||
/* LX2160ARM Chapter 28 ("Thermal Monitoring Unit") */
|
||||
tmu: tmu@1f80000 {
|
||||
compatible = "fsl,qoriq-tmu";
|
||||
reg = <0x0 0x1f80000 0x0 0x10000>;
|
||||
interrupts = <GIC_SPI 23 IRQ_TYPE_LEVEL_HIGH>;
|
||||
fsl,tmu-range = <0x800000e6 0x8001017d>;
|
||||
fsl,tmu-calibration = <0x00000000 0x00000035
|
||||
0x00000001 0x00000154>;
|
||||
little-endian;
|
||||
#thermal-sensor-cells = <1>;
|
||||
label = "lx2160a-tmu"; /* explicit naming */
|
||||
};
|
||||
|
||||
/* explicit thermal-zones names per LX2160ARM Table 323 */
|
||||
thermal-zones {
|
||||
cluster67-thermal {
|
||||
polling-delay-passive = <1000>;
|
||||
polling-delay = <5000>;
|
||||
thermal-sensors = <&tmu 0>;
|
||||
};
|
||||
|
||||
ddr1-cluster5-thermal {
|
||||
polling-delay-passive = <1000>;
|
||||
polling-delay = <5000>;
|
||||
thermal-sensors = <&tmu 1>;
|
||||
};
|
||||
|
||||
wriop-thermal {
|
||||
polling-delay-passive = <1000>;
|
||||
polling-delay = <5000>;
|
||||
thermal-sensors = <&tmu 2>;
|
||||
};
|
||||
|
||||
dce-qbman-hsio2-thermal {
|
||||
polling-delay-passive = <1000>;
|
||||
polling-delay = <5000>;
|
||||
thermal-sensors = <&tmu 3>;
|
||||
};
|
||||
|
||||
ccn-dpaa-tbu-thermal {
|
||||
polling-delay-passive = <1000>;
|
||||
polling-delay = <5000>;
|
||||
thermal-sensors = <&tmu 4>;
|
||||
};
|
||||
|
||||
cluster4-hsio3-thermal {
|
||||
polling-delay-passive = <1000>;
|
||||
polling-delay = <5000>;
|
||||
thermal-sensors = <&tmu 5>;
|
||||
};
|
||||
|
||||
cluster23-thermal {
|
||||
polling-delay-passive = <1000>;
|
||||
polling-delay = <5000>;
|
||||
thermal-sensors = <&tmu 6>;
|
||||
};
|
||||
};
|
||||
|
||||
/* WRIOP0: 0x8b8_0000, E-MDIO1: 0x1_6000 */
|
||||
emdio1: mdio@8b96000 {
|
||||
compatible = "fsl,ls-mdio";
|
||||
|
||||
@@ -37,105 +37,6 @@
|
||||
status = "disabled";
|
||||
};
|
||||
|
||||
&dspi0 {
|
||||
bus-num = <0>;
|
||||
status = "okay";
|
||||
|
||||
dflash0: n25q128a@0 {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <1>;
|
||||
compatible = "jedec,spi-nor";
|
||||
spi-max-frequency = <3000000>;
|
||||
spi-cpol;
|
||||
spi-cpha;
|
||||
reg = <0>;
|
||||
};
|
||||
dflash1: sst25wf040b@1 {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <1>;
|
||||
compatible = "jedec,spi-nor";
|
||||
spi-max-frequency = <3000000>;
|
||||
spi-cpol;
|
||||
spi-cpha;
|
||||
reg = <1>;
|
||||
};
|
||||
dflash2: en25s64@2 {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <1>;
|
||||
compatible = "jedec,spi-nor";
|
||||
spi-max-frequency = <3000000>;
|
||||
spi-cpol;
|
||||
spi-cpha;
|
||||
reg = <2>;
|
||||
};
|
||||
};
|
||||
|
||||
&dspi1 {
|
||||
bus-num = <0>;
|
||||
status = "okay";
|
||||
|
||||
dflash3: n25q128a@0 {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <1>;
|
||||
compatible = "jedec,spi-nor";
|
||||
spi-max-frequency = <3000000>;
|
||||
spi-cpol;
|
||||
spi-cpha;
|
||||
reg = <0>;
|
||||
};
|
||||
dflash4: sst25wf040b@1 {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <1>;
|
||||
compatible = "jedec,spi-nor";
|
||||
spi-max-frequency = <3000000>;
|
||||
spi-cpol;
|
||||
spi-cpha;
|
||||
reg = <1>;
|
||||
};
|
||||
dflash5: en25s64@2 {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <1>;
|
||||
compatible = "jedec,spi-nor";
|
||||
spi-max-frequency = <3000000>;
|
||||
spi-cpol;
|
||||
spi-cpha;
|
||||
reg = <2>;
|
||||
};
|
||||
};
|
||||
|
||||
&dspi2 {
|
||||
bus-num = <0>;
|
||||
status = "okay";
|
||||
|
||||
dflash6: n25q128a@0 {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <1>;
|
||||
compatible = "jedec,spi-nor";
|
||||
spi-max-frequency = <3000000>;
|
||||
spi-cpol;
|
||||
spi-cpha;
|
||||
reg = <0>;
|
||||
};
|
||||
dflash7: sst25wf040b@1 {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <1>;
|
||||
compatible = "jedec,spi-nor";
|
||||
spi-max-frequency = <3000000>;
|
||||
spi-cpol;
|
||||
spi-cpha;
|
||||
reg = <1>;
|
||||
};
|
||||
dflash8: en25s64@2 {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <1>;
|
||||
compatible = "jedec,spi-nor";
|
||||
spi-max-frequency = <3000000>;
|
||||
spi-cpol;
|
||||
spi-cpha;
|
||||
reg = <2>;
|
||||
};
|
||||
};
|
||||
|
||||
&esdhc1 {
|
||||
mmc-hs200-1_8v;
|
||||
mmc-hs400-1_8v;
|
||||
|
||||
30
arch/arm/include/asm/arch-fsl-layerscape/sys_proto.h
Normal file
30
arch/arm/include/asm/arch-fsl-layerscape/sys_proto.h
Normal file
@@ -0,0 +1,30 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0+ */
|
||||
/*
|
||||
* Copyright 2026 Free Mobile - Vincent Jardin
|
||||
*
|
||||
* Layerscape mirror of the i.MX <asm/mach-imx/sys_proto.h>: declares
|
||||
* the SoC-personality helpers consumed by generic drivers that work on
|
||||
* both i.MX and QorIQ/Layerscape parts (e.g. drivers/thermal/imx_tmu.c
|
||||
* for the QorIQ TMU variant).
|
||||
*/
|
||||
|
||||
#ifndef _ASM_ARCH_FSL_LAYERSCAPE_SYS_PROTO_H
|
||||
#define _ASM_ARCH_FSL_LAYERSCAPE_SYS_PROTO_H
|
||||
|
||||
#include <linux/types.h>
|
||||
|
||||
/*
|
||||
* Per LX2160A Reference Manual, Rev. 1 (10/2021):
|
||||
* - section 1.12.1: "NXP specs max power at 105 degC junction" for
|
||||
* commercial / embedded operating conditions.
|
||||
* - section 28.1: TMU "Accuracy within +/- 3 degC".
|
||||
*
|
||||
* Layerscape SoCs do not expose an OCOTP-style "CPU temp grade" fuse,
|
||||
* so the implementation returns the documented junction-temperature
|
||||
* limit from the data sheet (-40 .. 105 degC commercial range). The
|
||||
* thermal driver subtracts 10 degC for its alert threshold, which
|
||||
* comfortably clears the +/- 3 degC TMU accuracy in both directions.
|
||||
*/
|
||||
u32 get_cpu_temp_grade(int *minc, int *maxc);
|
||||
|
||||
#endif /* _ASM_ARCH_FSL_LAYERSCAPE_SYS_PROTO_H */
|
||||
@@ -22,6 +22,7 @@ int low_drive_freq_update(void *blob);
|
||||
|
||||
enum imx9_soc_voltage_mode soc_target_voltage_mode(void);
|
||||
int get_reset_reason(bool sys, bool lm);
|
||||
int imx9_uboot_fixup_by_fuse(void *fdt);
|
||||
|
||||
int scmi_get_boot_device_offset(unsigned long *img_off);
|
||||
int scmi_get_boot_stage(u8 *stage);
|
||||
|
||||
@@ -150,6 +150,7 @@ config TARGET_PHYCORE_IMX93
|
||||
|
||||
config TARGET_IMX95_19X19_EVK
|
||||
bool "imx95_19x19_evk"
|
||||
select OF_BOARD_FIXUP
|
||||
select IMX95
|
||||
imply BOOTSTD_BOOTCOMMAND
|
||||
imply BOOTSTD_FULL
|
||||
@@ -157,6 +158,7 @@ config TARGET_IMX95_19X19_EVK
|
||||
|
||||
config TARGET_IMX95_15X15_EVK
|
||||
bool "imx95_15x15_evk"
|
||||
select OF_BOARD_FIXUP
|
||||
select IMX95
|
||||
imply BOOTSTD_BOOTCOMMAND
|
||||
imply BOOTSTD_FULL
|
||||
@@ -164,6 +166,7 @@ config TARGET_IMX95_15X15_EVK
|
||||
|
||||
config TARGET_IMX943_EVK
|
||||
bool "imx943_evk"
|
||||
select OF_BOARD_FIXUP
|
||||
select IMX94
|
||||
imply BOOTSTD_BOOTCOMMAND
|
||||
imply BOOTSTD_FULL
|
||||
@@ -180,6 +183,7 @@ config TARGET_VERDIN_IMX95
|
||||
|
||||
config TARGET_IMX952_EVK
|
||||
bool "imx952_evk"
|
||||
select OF_BOARD_FIXUP
|
||||
select IMX_SM_CPU
|
||||
select IMX_SM_LMM
|
||||
select IMX952
|
||||
|
||||
@@ -5,5 +5,5 @@
|
||||
# Add include path for NXP device tree header files from Linux.
|
||||
ccflags-y += -I$(srctree)/dts/upstream/src/arm64/freescale/
|
||||
|
||||
obj-y += soc.o
|
||||
obj-y += soc.o fdt.o
|
||||
obj-y += clock_scmi.o clock.o
|
||||
|
||||
644
arch/arm/mach-imx/imx9/scmi/fdt.c
Normal file
644
arch/arm/mach-imx/imx9/scmi/fdt.c
Normal file
@@ -0,0 +1,644 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Copyright 2026 NXP
|
||||
*
|
||||
*/
|
||||
|
||||
#include <asm/arch/sys_proto.h>
|
||||
#include <linux/bitfield.h>
|
||||
#include <fuse.h>
|
||||
#include <fdt_support.h>
|
||||
#include <fdtdec.h>
|
||||
|
||||
struct periph_fuse_info {
|
||||
u32 bit_mask;
|
||||
u32 soc_type; /* 0 means for all */
|
||||
bool of_board_fix;
|
||||
int (*disable_func)(void *blob, u32 fuse_val);
|
||||
};
|
||||
|
||||
int num_a55_cores_disabled;
|
||||
int gpu_disabled;
|
||||
|
||||
static int delete_fdt_nodes(void *blob, const char *const nodes_path[], int size_array)
|
||||
{
|
||||
int i = 0;
|
||||
int rc;
|
||||
int nodeoff;
|
||||
|
||||
for (i = 0; i < size_array; i++) {
|
||||
nodeoff = fdt_path_offset(blob, nodes_path[i]);
|
||||
if (nodeoff < 0)
|
||||
continue; /* Not found, skip it */
|
||||
|
||||
debug("Found %s node\n", nodes_path[i]);
|
||||
|
||||
rc = fdt_del_node(blob, nodeoff);
|
||||
if (rc < 0) {
|
||||
printf("Unable to delete node %s, err=%s\n",
|
||||
nodes_path[i], fdt_strerror(rc));
|
||||
} else {
|
||||
printf("Delete node %s\n", nodes_path[i]);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int disable_fdt_nodes(void *blob, const char *const nodes_path[],
|
||||
int size_array, const char *prop, const char *value)
|
||||
{
|
||||
int i = 0;
|
||||
int rc;
|
||||
int nodeoff;
|
||||
const char *status = "disabled";
|
||||
const char *prop_str;
|
||||
|
||||
for (i = 0; i < size_array; i++) {
|
||||
nodeoff = fdt_path_offset(blob, nodes_path[i]);
|
||||
if (nodeoff < 0)
|
||||
continue; /* Not found, skip it */
|
||||
|
||||
debug("Found %s node\n", nodes_path[i]);
|
||||
|
||||
if (prop && value) {
|
||||
prop_str = fdt_stringlist_get(blob, nodeoff, prop, 0, NULL);
|
||||
|
||||
if (!prop_str || strcmp(prop_str, value))
|
||||
continue;
|
||||
}
|
||||
|
||||
add_status:
|
||||
rc = fdt_setprop(blob, nodeoff, "status", status, strlen(status) + 1);
|
||||
if (rc) {
|
||||
if (rc == -FDT_ERR_NOSPACE) {
|
||||
rc = fdt_increase_size(blob, 512);
|
||||
if (!rc)
|
||||
goto add_status;
|
||||
}
|
||||
printf("Unable to update property %s:%s, err=%s\n",
|
||||
nodes_path[i], "status", fdt_strerror(rc));
|
||||
} else {
|
||||
debug("Modify %s:%s disabled\n", nodes_path[i], "status");
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int get_cooling_device_list(void *blob, u32 nodeoff,
|
||||
const char *const path, u32 *cooling_dev, int max_cnt)
|
||||
{
|
||||
int cnt, j;
|
||||
|
||||
cnt = fdtdec_get_int_array_count(blob, nodeoff, "cooling-device",
|
||||
cooling_dev, max_cnt);
|
||||
if (cnt < 0) {
|
||||
printf("cnt incorrect, path %s, cnt = %d\n", path, cnt);
|
||||
return cnt;
|
||||
}
|
||||
if (cnt != max_cnt)
|
||||
printf("Warning: %s, cooling-device count %d\n", path, cnt);
|
||||
|
||||
for (j = 0; j < cnt; j++)
|
||||
cooling_dev[j] = cpu_to_fdt32(cooling_dev[j]);
|
||||
|
||||
return cnt;
|
||||
}
|
||||
|
||||
static void disable_thermal_vpu_node(void *blob, u32 disabled_cores, u32 gpu_disabled)
|
||||
{
|
||||
static const char * const thermal_path[] = {
|
||||
"/thermal-zones/ana/cooling-maps/map0",
|
||||
"/thermal-zones/ana-thermal/cooling-maps/map0",
|
||||
};
|
||||
int num_cpus = (is_imx94() || is_imx952()) ? 4 : 6;
|
||||
u32 array_cnt = (num_cpus + 2) * 3 - (disabled_cores * 3) - (gpu_disabled * 3);
|
||||
u32 cooling_dev[array_cnt];
|
||||
|
||||
int nodeoff, ret, i, cnt;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(thermal_path); i++) {
|
||||
nodeoff = fdt_path_offset(blob, thermal_path[i]);
|
||||
if (nodeoff < 0) {
|
||||
printf("path not found %s\n", thermal_path[i]);
|
||||
continue; /* Not found, skip it */
|
||||
}
|
||||
|
||||
cnt = get_cooling_device_list(blob, nodeoff,
|
||||
thermal_path[i], cooling_dev, array_cnt);
|
||||
/* VPU map does not exist in cooling dev*/
|
||||
if (cnt <= ((num_cpus - disabled_cores) * 3 + (gpu_disabled ? 0 : 3)))
|
||||
continue;
|
||||
|
||||
/* Remove VPU it the last two nodes in the fdt ana blob */
|
||||
ret = fdt_setprop(blob, nodeoff, "cooling-device", &cooling_dev,
|
||||
sizeof(u32) * (array_cnt - 3));
|
||||
|
||||
if (ret < 0) {
|
||||
printf("Warning: %s, cooling-device setprop failed %d\n",
|
||||
thermal_path[i], ret);
|
||||
continue;
|
||||
}
|
||||
|
||||
printf("Update node %s, cooling-device prop\n", thermal_path[i]);
|
||||
}
|
||||
}
|
||||
|
||||
static void disable_thermal_gpu_node(void *blob, u32 disabled_cores)
|
||||
{
|
||||
static const char * const thermal_path[] = {
|
||||
"/thermal-zones/ana/cooling-maps/map0",
|
||||
"/thermal-zones/ana-thermal/cooling-maps/map0",
|
||||
};
|
||||
int num_cpus = (is_imx94() || is_imx952()) ? 4 : 6;
|
||||
u32 array_cnt = (num_cpus + 2) * 3 - (disabled_cores * 3);
|
||||
u32 cooling_dev[array_cnt];
|
||||
int nodeoff, ret, i, cnt;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(thermal_path); i++) {
|
||||
nodeoff = fdt_path_offset(blob, thermal_path[i]);
|
||||
if (nodeoff < 0) {
|
||||
printf("path not found %s\n", thermal_path[i]);
|
||||
continue; /* Not found, skip it */
|
||||
}
|
||||
|
||||
cnt = get_cooling_device_list(blob, nodeoff, thermal_path[i],
|
||||
cooling_dev, array_cnt);
|
||||
if (cnt <= (num_cpus - disabled_cores) * 3)
|
||||
continue; /* GPU map does not exist in cooling dev*/
|
||||
|
||||
/* Remove GPU and VPU as these are the last two nodes in the fdt ana blob */
|
||||
ret = fdt_setprop(blob, nodeoff, "cooling-device", &cooling_dev,
|
||||
sizeof(u32) * (array_cnt - 6));
|
||||
if (ret < 0) {
|
||||
printf("Warning: %s, cooling-device setprop failed %d\n",
|
||||
thermal_path[i], ret);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (cnt == array_cnt) {
|
||||
/* Add VPU node back to ana thermal-zone. */
|
||||
ret = fdt_appendprop(blob, nodeoff, "cooling-device",
|
||||
&cooling_dev[array_cnt - 3], sizeof(u32) * 3);
|
||||
if (ret < 0) {
|
||||
printf("Warning: %s, cooling-device appendprop failed %d\n",
|
||||
thermal_path[i], ret);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
printf("Update node %s, cooling-device prop\n", thermal_path[i]);
|
||||
}
|
||||
}
|
||||
|
||||
static void disable_thermal_cpu_nodes(void *blob, u32 disabled_cores)
|
||||
{
|
||||
static const char * const thermal_path[] = {
|
||||
"/thermal-zones/pf53_arm/cooling-maps/map0",
|
||||
"/thermal-zones/ana/cooling-maps/map0",
|
||||
"/thermal-zones/a55/cooling-maps/map0",
|
||||
"/thermal-zones/a55-thermal/cooling-maps/map0",
|
||||
"/thermal-zones/ana-thermal/cooling-maps/map0",
|
||||
};
|
||||
u32 cooling_dev[24];
|
||||
int nodeoff, ret, i, cnt;
|
||||
int prop_size = 3 * ((is_imx94() || is_imx952()) ? 4 : 6);
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(thermal_path); i++) {
|
||||
nodeoff = fdt_path_offset(blob, thermal_path[i]);
|
||||
if (nodeoff < 0) {
|
||||
printf("path not found %s\n", thermal_path[i]);
|
||||
continue; /* Not found, skip it */
|
||||
}
|
||||
cnt = get_cooling_device_list(blob, nodeoff, thermal_path[i], cooling_dev, 24);
|
||||
|
||||
ret = fdt_setprop(blob, nodeoff, "cooling-device", &cooling_dev,
|
||||
sizeof(u32) * (prop_size - disabled_cores * 3));
|
||||
|
||||
if (ret < 0) {
|
||||
printf("Warning: %s, cooling-device setprop failed %d\n",
|
||||
thermal_path[i], ret);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Add GPU and VPU nodes back to ana thermal-zone. */
|
||||
if (cnt > prop_size) {
|
||||
ret = fdt_appendprop(blob, nodeoff, "cooling-device",
|
||||
&cooling_dev[prop_size],
|
||||
sizeof(u32) * (cnt - prop_size));
|
||||
if (ret < 0) {
|
||||
printf("Warning: %s, cooling-device appendprop failed %d\n",
|
||||
thermal_path[i], ret);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
printf("Update node %s, cooling-device prop\n", thermal_path[i]);
|
||||
}
|
||||
}
|
||||
|
||||
static int disable_ld_node(void *blob, u32 fuse_val)
|
||||
{
|
||||
static const char * const nodes_path_ld[] = {
|
||||
"/remoteproc",
|
||||
"/disp-mu",
|
||||
"/soc/syscon@4b070000",
|
||||
"/soc/mailbox@4b080000",
|
||||
"/soc/mailbox@4b090000",
|
||||
};
|
||||
|
||||
return delete_fdt_nodes(blob, nodes_path_ld, ARRAY_SIZE(nodes_path_ld));
|
||||
}
|
||||
|
||||
static int disable_npu_node(void *blob, u32 fuse_val)
|
||||
{
|
||||
static const char * const nodes_path_npu[] = {
|
||||
"/soc/imx95-neutron-remoteproc@4ab00000",
|
||||
"/soc/imx95-neutron@4ab00004",
|
||||
"/soc/neutron-remoteproc@4ab00000",
|
||||
"/soc/neutron@4ab00004",
|
||||
};
|
||||
|
||||
return delete_fdt_nodes(blob, nodes_path_npu, ARRAY_SIZE(nodes_path_npu));
|
||||
}
|
||||
|
||||
static int disable_arm_cpu_nodes(void *blob, u32 fuse_val)
|
||||
{
|
||||
u32 i = 0;
|
||||
int rc;
|
||||
int nodeoff;
|
||||
char nodes_path[32];
|
||||
int num_cpus = (is_imx94() || is_imx952()) ? 4 : 6;
|
||||
|
||||
num_a55_cores_disabled = 0;
|
||||
|
||||
if (fuse_val & BIT(2)) /* A55C2 */
|
||||
num_a55_cores_disabled++;
|
||||
|
||||
if (fuse_val & BIT(3)) /* A55C2 */
|
||||
num_a55_cores_disabled++;
|
||||
|
||||
if (fuse_val & BIT(4)) /* A55C3 */
|
||||
num_a55_cores_disabled++;
|
||||
|
||||
if (fuse_val & BIT(5)) /* A55C4 */
|
||||
num_a55_cores_disabled++;
|
||||
|
||||
if (fuse_val & BIT(6)) /* A55C5 */
|
||||
num_a55_cores_disabled++;
|
||||
|
||||
for (i = num_cpus; i > (num_cpus - num_a55_cores_disabled); i--) {
|
||||
sprintf(nodes_path, "/cpus/cpu@%u00", i - 1);
|
||||
|
||||
nodeoff = fdt_path_offset(blob, nodes_path);
|
||||
if (nodeoff < 0)
|
||||
continue; /* Not found, skip it */
|
||||
|
||||
debug("Found %s node\n", nodes_path);
|
||||
|
||||
rc = fdt_del_node(blob, nodeoff);
|
||||
if (rc < 0) {
|
||||
printf("Unable to delete node %s, err=%s\n",
|
||||
nodes_path, fdt_strerror(rc));
|
||||
} else {
|
||||
printf("Delete node %s\n", nodes_path);
|
||||
|
||||
/* Remove node from cpu-map/cluster0 */
|
||||
sprintf(nodes_path, "/cpus/cpu-map/cluster0/core%u", i - 1);
|
||||
nodeoff = fdt_path_offset(blob, nodes_path);
|
||||
if (nodeoff < 0)
|
||||
continue; /* Not found, skip it */
|
||||
|
||||
rc = fdt_del_node(blob, nodeoff);
|
||||
if (rc < 0)
|
||||
printf("Unable to delete node %s, err=%s\n",
|
||||
nodes_path, fdt_strerror(rc));
|
||||
}
|
||||
}
|
||||
|
||||
disable_thermal_cpu_nodes(blob, num_a55_cores_disabled);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int disable_jpegdec_node(void *blob, u32 fuse_val)
|
||||
{
|
||||
static const char * const nodes_path_jpegdec[] = {
|
||||
"/soc/jpegdec@4c500000",
|
||||
};
|
||||
|
||||
return delete_fdt_nodes(blob, nodes_path_jpegdec, ARRAY_SIZE(nodes_path_jpegdec));
|
||||
}
|
||||
|
||||
static int disable_jpegenc_node(void *blob, u32 fuse_val)
|
||||
{
|
||||
static const char * const nodes_path_jpegenc[] = {
|
||||
"/soc/jpegenc@4c550000",
|
||||
};
|
||||
|
||||
return delete_fdt_nodes(blob, nodes_path_jpegenc, ARRAY_SIZE(nodes_path_jpegenc));
|
||||
}
|
||||
|
||||
static int disable_mipicsi0_node(void *blob, u32 fuse_val)
|
||||
{
|
||||
static const char * const nodes_path_mipicsi0[] = {
|
||||
"/soc/csi@4ad30000",
|
||||
};
|
||||
|
||||
return delete_fdt_nodes(blob, nodes_path_mipicsi0, ARRAY_SIZE(nodes_path_mipicsi0));
|
||||
}
|
||||
|
||||
static int disable_mipicsi1_node(void *blob, u32 fuse_val)
|
||||
{
|
||||
static const char * const nodes_path_mipicsi1[] = {
|
||||
"/soc/csi@4ad40000",
|
||||
};
|
||||
|
||||
return delete_fdt_nodes(blob, nodes_path_mipicsi1, ARRAY_SIZE(nodes_path_mipicsi1));
|
||||
}
|
||||
|
||||
static int disable_isp_node(void *blob, u32 fuse_val)
|
||||
{
|
||||
static const char * const nodes_path_isp[] = {
|
||||
"/soc@0/isp@4ae00000",
|
||||
"/soc/isp@4ae00000",
|
||||
};
|
||||
|
||||
return delete_fdt_nodes(blob, nodes_path_isp, ARRAY_SIZE(nodes_path_isp));
|
||||
}
|
||||
|
||||
static int disable_vpu_node(void *blob, u32 fuse_val)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
static const char * const nodes_path_vpu[] = {
|
||||
"/soc/vpu-ctrl@4c4c0000",
|
||||
"/soc/vpu-ctrl@4c4f0000",
|
||||
"/soc/vpu@4c480000",
|
||||
"/soc/vpu@4c490000",
|
||||
"/soc/vpu@4c4a0000",
|
||||
"/soc/vpu@4c4b0000",
|
||||
"/soc/vpu@4c4c0000",
|
||||
"/soc/vpu@4c4d0000",
|
||||
"/soc/vpu@4c4e0000",
|
||||
"/soc/jpegdec@4c500000",
|
||||
"/soc/jpegenc@4c550000",
|
||||
"/soc/vpuenc@4c460000",
|
||||
"/soc/syscon@4c410000"
|
||||
};
|
||||
|
||||
ret = delete_fdt_nodes(blob, nodes_path_vpu, ARRAY_SIZE(nodes_path_vpu));
|
||||
if (!ret)
|
||||
disable_thermal_vpu_node(blob, num_a55_cores_disabled, gpu_disabled);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int disable_vpuenc_node(void *blob, u32 fuse_val)
|
||||
{
|
||||
static const char * const nodes_path_vpuenc[] = {
|
||||
"/soc/vpuenc@4c460000",
|
||||
};
|
||||
|
||||
return delete_fdt_nodes(blob, nodes_path_vpuenc, ARRAY_SIZE(nodes_path_vpuenc));
|
||||
}
|
||||
|
||||
static int disable_vpuwave511_node(void *blob, u32 fuse_val)
|
||||
{
|
||||
static const char * const nodes_path_vpu511[] = {
|
||||
"/soc/vpu-ctrl@4c4f0000",
|
||||
"/soc/vpu@4c4b0000",
|
||||
"/soc/vpu@4c4c0000",
|
||||
"/soc/vpu@4c4d0000",
|
||||
"/soc/vpu@4c4e0000",
|
||||
};
|
||||
|
||||
return delete_fdt_nodes(blob, nodes_path_vpu511, ARRAY_SIZE(nodes_path_vpu511));
|
||||
}
|
||||
|
||||
static int disable_gpu_node(void *blob, u32 fuse_val)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
static const char * const nodes_path_gpu[] = {
|
||||
"/soc/gpu@4d900000",
|
||||
"/thermal-zones@1/ana/cooling-maps/map1/cooling-device/gpu@4d900000",
|
||||
"/thermal-zones/ana/cooling-maps/map1/cooling-device/gpu@4d900000",
|
||||
"/thermal-zones@1/ana/cooling-maps/map1/cooling-device",
|
||||
"/thermal-zones/ana/cooling-maps/map1/cooling-device",
|
||||
"/thermal-zones@1/ana/cooling-maps/map1",
|
||||
"/thermal-zones/ana/cooling-maps/map1",
|
||||
};
|
||||
|
||||
ret = delete_fdt_nodes(blob, nodes_path_gpu, ARRAY_SIZE(nodes_path_gpu));
|
||||
if (!ret) {
|
||||
disable_thermal_gpu_node(blob, num_a55_cores_disabled);
|
||||
gpu_disabled = 1;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int disable_pciea_node(void *blob, u32 fuse_val)
|
||||
{
|
||||
static const char * const nodes_path_pciea[] = {
|
||||
"/soc/pcie@4c300000",
|
||||
"/soc/pcie-ep@4c300000"
|
||||
};
|
||||
|
||||
return delete_fdt_nodes(blob, nodes_path_pciea, ARRAY_SIZE(nodes_path_pciea));
|
||||
}
|
||||
|
||||
static int disable_pcieb_node(void *blob, u32 fuse_val)
|
||||
{
|
||||
static const char * const nodes_path_pcieb[] = {
|
||||
"/soc/pcie@4c380000",
|
||||
"/soc/pcie-ep@4c380000"
|
||||
};
|
||||
|
||||
return delete_fdt_nodes(blob, nodes_path_pcieb, ARRAY_SIZE(nodes_path_pcieb));
|
||||
}
|
||||
|
||||
int disable_enet10g_node(void *blob, u32 fuse_val)
|
||||
{
|
||||
static const char * const nodes_path_enet10g[] = {
|
||||
"/pcie@4ca00000/ethernet@10,0",
|
||||
"/soc/pcie@4ca00000/ethernet@10,0",
|
||||
"/soc/syscon@4ca00000/ethernet@10,0",
|
||||
"/soc/netc-blk-ctrl@4cde0000/pcie@4ca00000/ethernet@10,0",
|
||||
};
|
||||
|
||||
return disable_fdt_nodes(blob, nodes_path_enet10g, ARRAY_SIZE(nodes_path_enet10g),
|
||||
NULL, NULL);
|
||||
}
|
||||
|
||||
int disable_enet25g_node(void *blob, u32 fuse_val)
|
||||
{
|
||||
static const char * const nodes_path_enet25g[] = {
|
||||
"/soc/system-controller@4ceb0000/pcie@4ca00000/ethernet-switch@0,2/ports/port@0",
|
||||
"/soc/system-controller@4ceb0000/pcie@4ca00000/ethernet-switch@0,2/ports/port@1",
|
||||
};
|
||||
|
||||
return disable_fdt_nodes(blob, nodes_path_enet25g, ARRAY_SIZE(nodes_path_enet25g),
|
||||
"phy-mode", "sgmii");
|
||||
}
|
||||
|
||||
int disable_mipidsi_node(void *blob, u32 fuse_val)
|
||||
{
|
||||
static const char * const nodes_path_mipidsi[] = {
|
||||
"/soc/dsi@4acf0000",
|
||||
"/soc/syscon@4acf0000",
|
||||
"/soc/dsi@4b060000",
|
||||
"/soc/phy@4b110000",
|
||||
};
|
||||
|
||||
return delete_fdt_nodes(blob, nodes_path_mipidsi, ARRAY_SIZE(nodes_path_mipidsi));
|
||||
}
|
||||
|
||||
int disable_dpu_node(void *blob, u32 fuse_val)
|
||||
{
|
||||
static const char * const nodes_path_dpu[] = {
|
||||
"/soc/bridge@4b0d0000/channel@0/port@0/endpoint",
|
||||
"/soc/bridge@4b0d0000/channel@0/port@1/endpoint",
|
||||
"/soc/bridge@4b0d0000/channel@1/port@0/endpoint",
|
||||
"/soc/bridge@4b0d0000/channel@1/port@1/endpoint",
|
||||
"/soc/display-controller@4b400000/ports/port@0/endpoint",
|
||||
"/soc/display-controller@4b400000/ports/port@1/endpoint",
|
||||
"/soc/display-controller@4b400000",
|
||||
"/soc/syscon@4b010000/bridge@8/ports/port@0/endpoint",
|
||||
"/soc/syscon@4b010000/bridge@8/ports/port@1/endpoint",
|
||||
"/soc/syscon@4b010000/bridge@8/ports/port@2/endpoint@0",
|
||||
"/soc/syscon@4b010000/bridge@8/ports/port@2/endpoint@1",
|
||||
"/soc/syscon@4b010000/bridge@8/ports/port@3/endpoint@0",
|
||||
"/soc/syscon@4b010000/bridge@8/ports/port@3/endpoint@1",
|
||||
"/soc/syscon@4b010000/bridge@8",
|
||||
"/soc/syscon@4b010000",
|
||||
"/soc/syscon@4b0a0000",
|
||||
"/soc/interrupt-controller@4b0b0000",
|
||||
"/soc/bridge@4b0d0000"
|
||||
};
|
||||
|
||||
return delete_fdt_nodes(blob, nodes_path_dpu, ARRAY_SIZE(nodes_path_dpu));
|
||||
}
|
||||
|
||||
int disable_lvds_node(void *blob, u32 fuse_val)
|
||||
{
|
||||
static const char * const nodes_path_lvds[] = {
|
||||
"/soc/syscon@4b0c0000/ldb@4/channel@0",
|
||||
"/soc/syscon@4b0c0000/phy@8",
|
||||
"/soc/syscon@4b0c0000/ldb@4/channel@1",
|
||||
"/soc/syscon@4b0c0000/phy@c",
|
||||
"/soc/syscon@4b0c0000"
|
||||
};
|
||||
|
||||
return delete_fdt_nodes(blob, nodes_path_lvds, ARRAY_SIZE(nodes_path_lvds));
|
||||
}
|
||||
|
||||
int disable_cm70_node(void *blob, u32 fuse_val)
|
||||
{
|
||||
static const char * const nodes_path_cm70[] = {
|
||||
"/reserved-memory/vdev0vring0@82000000",
|
||||
"/reserved-memory/vdev0vring1@82008000",
|
||||
"/reserved-memory/vdev1vring0@82010000",
|
||||
"/reserved-memory/vdev1vring1@82018000",
|
||||
"/reserved-memory/rsc-table@82220000",
|
||||
"/reserved-memory/vdevbuffer@82020000",
|
||||
"/imx943-cm70",
|
||||
};
|
||||
|
||||
return delete_fdt_nodes(blob, nodes_path_cm70, ARRAY_SIZE(nodes_path_cm70));
|
||||
}
|
||||
|
||||
int disable_cm71_node(void *blob, u32 fuse_val)
|
||||
{
|
||||
static const char * const nodes_path_cm71[] = {
|
||||
"/reserved-memory/vdev0vring0@84000000",
|
||||
"/reserved-memory/vdev0vring1@84008000",
|
||||
"/reserved-memory/vdev1vring0@84010000",
|
||||
"/reserved-memory/vdev1vring1@84018000",
|
||||
"/reserved-memory/rsc-table@84220000",
|
||||
"/reserved-memory/vdevbuffer@84020000",
|
||||
"/imx943-cm71",
|
||||
};
|
||||
|
||||
return delete_fdt_nodes(blob, nodes_path_cm71, ARRAY_SIZE(nodes_path_cm71));
|
||||
}
|
||||
|
||||
/* There is order dependency between cpu->gpu->vpu */
|
||||
struct periph_fuse_info f17_grp[] = {
|
||||
{ BIT(30), MXC_CPU_IMX952, false, disable_ld_node },
|
||||
};
|
||||
|
||||
struct periph_fuse_info f18_grp[] = {
|
||||
{ BIT(0), 0, false, disable_npu_node },
|
||||
{ GENMASK(6, 2), 0, false, disable_arm_cpu_nodes },
|
||||
{ BIT(9), 0, true, disable_cm70_node },
|
||||
{ BIT(17), MXC_CPU_IMX94, true, disable_cm71_node },
|
||||
{ BIT(22), 0, true, disable_dpu_node },
|
||||
{ BIT(27), 0, true, disable_lvds_node },
|
||||
{ BIT(29), 0, false, disable_isp_node },
|
||||
};
|
||||
|
||||
struct periph_fuse_info f19_grp[] = {
|
||||
{ BIT(6), 0, true, disable_pciea_node },
|
||||
{ BIT(7), 0, true, disable_pcieb_node },
|
||||
{ BIT(17), 0, false, disable_gpu_node },
|
||||
{ BIT(18), 0, false, disable_vpu_node },
|
||||
{ BIT(19), 0, false, disable_jpegenc_node },
|
||||
{ BIT(20), 0, false, disable_jpegdec_node },
|
||||
{ BIT(22), 0, false, disable_mipicsi0_node },
|
||||
{ BIT(23), 0, false, disable_mipicsi1_node },
|
||||
{ BIT(24), 0, true, disable_mipidsi_node },
|
||||
{ BIT(26), 0, false, disable_vpuenc_node },
|
||||
{ BIT(27), 0, false, disable_vpuwave511_node },
|
||||
};
|
||||
|
||||
struct periph_fuse_info f20_grp[] = {
|
||||
{ BIT(12), MXC_CPU_IMX95, true, disable_enet10g_node },
|
||||
{ BIT(12), MXC_CPU_IMX94, true, disable_enet25g_node },
|
||||
};
|
||||
|
||||
static void ft_disable_periph(void *blob, u32 fuse_bank, u32 fuse_word,
|
||||
struct periph_fuse_info *info, u32 info_num,
|
||||
bool board_fix_fdt)
|
||||
{
|
||||
int i, ret;
|
||||
u32 val = 0;
|
||||
|
||||
ret = fuse_read(fuse_bank, fuse_word, &val);
|
||||
if (ret)
|
||||
return;
|
||||
|
||||
for (i = 0; i < info_num; i++) {
|
||||
if (val & info[i].bit_mask) {
|
||||
if (board_fix_fdt && !info[i].of_board_fix)
|
||||
continue;
|
||||
|
||||
if (!info[i].soc_type || is_cpu_type(info[i].soc_type))
|
||||
info[i].disable_func(blob, val);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int ft_system_setup(void *blob, struct bd_info *bd)
|
||||
{
|
||||
/* Common peripheral disable fuse process */
|
||||
ft_disable_periph(blob, 2, 1, f17_grp, ARRAY_SIZE(f17_grp), false);
|
||||
ft_disable_periph(blob, 2, 2, f18_grp, ARRAY_SIZE(f18_grp), false);
|
||||
ft_disable_periph(blob, 2, 3, f19_grp, ARRAY_SIZE(f19_grp), false);
|
||||
ft_disable_periph(blob, 2, 4, f20_grp, ARRAY_SIZE(f20_grp), false);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Fix uboot dtb based on fuses. */
|
||||
#if IS_ENABLED(CONFIG_OF_BOARD_FIXUP) && !IS_ENABLED(CONFIG_XPL_BUILD)
|
||||
int imx9_uboot_fixup_by_fuse(void *fdt)
|
||||
{
|
||||
ft_disable_periph(fdt, 2, 2, f18_grp, ARRAY_SIZE(f18_grp), true);
|
||||
ft_disable_periph(fdt, 2, 3, f19_grp, ARRAY_SIZE(f19_grp), true);
|
||||
ft_disable_periph(fdt, 2, 4, f20_grp, ARRAY_SIZE(f20_grp), true);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
@@ -1,6 +1,6 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Copyright 2025 NXP
|
||||
* Copyright 2025-2026 NXP
|
||||
*
|
||||
* Peng Fan <peng.fan@nxp.com>
|
||||
*/
|
||||
@@ -186,8 +186,9 @@ u32 get_cpu_temp_grade(int *minc, int *maxc)
|
||||
*minc = -40;
|
||||
*maxc = 105;
|
||||
} else if (val == TEMP_EXTCOMMERCIAL) {
|
||||
*minc = -20;
|
||||
*maxc = 105;
|
||||
/* Map to Ext industrial */
|
||||
*minc = -40;
|
||||
*maxc = 125;
|
||||
} else {
|
||||
*minc = 0;
|
||||
*maxc = 95;
|
||||
@@ -715,14 +716,88 @@ int get_reset_reason(bool sys, bool lm)
|
||||
return 0;
|
||||
}
|
||||
|
||||
const char *get_imx_type(u32 imxtype)
|
||||
const char *get_cpu_variant_type_name(u32 type)
|
||||
{
|
||||
switch (imxtype) {
|
||||
case SCMI_CPU:
|
||||
return IMX_PLAT_STR;
|
||||
default:
|
||||
return "??";
|
||||
u32 val, core_num, part_num;
|
||||
int ret;
|
||||
|
||||
ret = fuse_read(2, 1, &val);
|
||||
if (ret)
|
||||
return NULL;
|
||||
|
||||
/* Get part num */
|
||||
part_num = (val >> 4) & 0xff;
|
||||
if (!part_num)
|
||||
return NULL;
|
||||
|
||||
if (type == MXC_CPU_IMX95 || type == MXC_CPU_IMX952) {
|
||||
u32 segment;
|
||||
static char name[8] = "95294";
|
||||
char pn[2];
|
||||
|
||||
core_num = part_num & 0x3;
|
||||
segment = (part_num >> 2) & 0xf;
|
||||
|
||||
switch (segment) {
|
||||
case 0xa:
|
||||
pn[0] = 'T';
|
||||
break;
|
||||
case 0xb:
|
||||
pn[0] = 'V';
|
||||
break;
|
||||
case 0xc:
|
||||
pn[0] = 'C';
|
||||
break;
|
||||
case 0xd:
|
||||
pn[0] = 'G';
|
||||
break;
|
||||
case 0xe:
|
||||
pn[0] = 'I';
|
||||
break;
|
||||
case 0xf:
|
||||
pn[0] = 'N';
|
||||
break;
|
||||
default:
|
||||
pn[0] = segment + '0';
|
||||
break;
|
||||
}
|
||||
|
||||
pn[1] = core_num * 2 + '0';
|
||||
|
||||
if (type == MXC_CPU_IMX95)
|
||||
sprintf(name, "95%c%c", pn[0], pn[1]);
|
||||
else
|
||||
sprintf(name, "952%c%c", pn[0], pn[1]);
|
||||
|
||||
return name;
|
||||
} else if (type == MXC_CPU_IMX94) {
|
||||
static char *name = "94398";
|
||||
|
||||
core_num = 8;
|
||||
|
||||
ret = fuse_read(2, 2, &val);
|
||||
if (ret)
|
||||
return NULL;
|
||||
|
||||
if (part_num > 30) { /* 943 */
|
||||
/* A55 2 & 3 disabled */
|
||||
if ((val & 0x18) == 0x18)
|
||||
core_num = 6;
|
||||
} else if (part_num > 20) { /* 942 */
|
||||
core_num = 5;
|
||||
|
||||
/* m7_0 disabled */
|
||||
if ((val & 0x200) == 0x200)
|
||||
core_num = 4;
|
||||
} else if (part_num > 10) { /* 941 */
|
||||
core_num = 5;
|
||||
}
|
||||
sprintf(name, "94%u%u", part_num, core_num);
|
||||
|
||||
return name;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void build_info(void)
|
||||
@@ -798,18 +873,6 @@ int arch_misc_init(void)
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if defined(CONFIG_OF_BOARD_FIXUP) && !defined(CONFIG_SPL_BUILD)
|
||||
int board_fix_fdt(void *fdt)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
int ft_system_setup(void *blob, struct bd_info *bd)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if IS_ENABLED(CONFIG_ENV_VARS_UBOOT_RUNTIME_CONFIG)
|
||||
void get_board_serial(struct tag_serialnr *serialnr)
|
||||
{
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
#include <fdt_support.h>
|
||||
#include <asm/gpio.h>
|
||||
#include <asm/arch/clock.h>
|
||||
#include <asm/mach-imx/sys_proto.h>
|
||||
#include <asm/arch/sys_proto.h>
|
||||
|
||||
int board_init(void)
|
||||
{
|
||||
@@ -26,3 +26,11 @@ int board_late_init(void)
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if IS_ENABLED(CONFIG_OF_BOARD_FIXUP)
|
||||
int board_fix_fdt(void *fdt)
|
||||
{
|
||||
/* Remove nodes based on fuses. */
|
||||
return imx9_uboot_fixup_by_fuse(fdt);
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -24,3 +24,11 @@ int board_late_init(void)
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if IS_ENABLED(CONFIG_OF_BOARD_FIXUP)
|
||||
int board_fix_fdt(void *fdt)
|
||||
{
|
||||
/* Remove nodes based on fuses. */
|
||||
return imx9_uboot_fixup_by_fuse(fdt);
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
#include <asm/gpio.h>
|
||||
#include <asm/arch/clock.h>
|
||||
#include <asm/mach-imx/sys_proto.h>
|
||||
#include <asm/arch/sys_proto.h>
|
||||
|
||||
int board_late_init(void)
|
||||
{
|
||||
@@ -14,3 +14,11 @@ int board_late_init(void)
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if IS_ENABLED(CONFIG_OF_BOARD_FIXUP)
|
||||
int board_fix_fdt(void *fdt)
|
||||
{
|
||||
/* Remove nodes based on fuses. */
|
||||
return imx9_uboot_fixup_by_fuse(fdt);
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -26,7 +26,6 @@ CONFIG_SYS_LOAD_ADDR=0x90400000
|
||||
CONFIG_SPL=y
|
||||
CONFIG_SPL_RECOVER_DATA_SECTION=y
|
||||
CONFIG_PCI=y
|
||||
CONFIG_OF_BOARD_FIXUP=y
|
||||
CONFIG_SYS_MEMTEST_START=0x90000000
|
||||
CONFIG_SYS_MEMTEST_END=0xA0000000
|
||||
CONFIG_REMAKE_ELF=y
|
||||
|
||||
@@ -28,7 +28,6 @@ CONFIG_SPL_OF_LIBFDT_ASSUME_MASK=0x0
|
||||
CONFIG_SPL=y
|
||||
CONFIG_SPL_RECOVER_DATA_SECTION=y
|
||||
CONFIG_PCI=y
|
||||
CONFIG_OF_BOARD_FIXUP=y
|
||||
CONFIG_SYS_MEMTEST_START=0x90000000
|
||||
CONFIG_SYS_MEMTEST_END=0xA0000000
|
||||
CONFIG_REMAKE_ELF=y
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Copyright 2019, 2024 NXP
|
||||
* Copyright 2019, 2024-2026 NXP
|
||||
*/
|
||||
|
||||
#include <cpu.h>
|
||||
@@ -28,8 +28,18 @@ struct cpu_imx_plat {
|
||||
u32 mpidr;
|
||||
};
|
||||
|
||||
__weak const char *get_cpu_variant_type_name(u32 type)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static const char *get_imx_type_str(u32 imxtype)
|
||||
{
|
||||
const char *name = get_cpu_variant_type_name(imxtype);
|
||||
|
||||
if (name)
|
||||
return name;
|
||||
|
||||
switch (imxtype) {
|
||||
case MXC_CPU_IMX8MM:
|
||||
return "8MMQ"; /* Quad-core version of the imx8mm */
|
||||
|
||||
@@ -27,11 +27,12 @@ config IMX_SCU_THERMAL
|
||||
trip is crossed
|
||||
|
||||
config IMX_TMU
|
||||
bool "Thermal Management Unit driver for NXP i.MX8M and iMX93"
|
||||
depends on ARCH_IMX8M || IMX93
|
||||
bool "Thermal Management Unit driver for NXP i.MX8M / i.MX93 and QorIQ"
|
||||
depends on ARCH_IMX8M || IMX93 || FSL_LAYERSCAPE
|
||||
help
|
||||
Support for Temperature sensors on NXP i.MX8M and iMX93.
|
||||
It supports one critical trip point and one passive trip point.
|
||||
Support for the NXP Thermal Management Unit (TMU) sensors on
|
||||
i.MX8M, i.MX93 and on QorIQ/Layerscape SoCs (LX2160A,
|
||||
LS1028A, LS1088A, ...).
|
||||
The boot is hold to the cool device to throttle CPUs when the
|
||||
passive trip is crossed
|
||||
|
||||
@@ -55,4 +56,11 @@ config TI_LM74_THERMAL
|
||||
Enable thermal support for the Texas Instruments LM74 chip.
|
||||
The driver supports reading CPU temperature.
|
||||
|
||||
config DM_THERMAL_JC42
|
||||
bool "JEDEC JC-42.4/TSE2004av SPD temperature sensor"
|
||||
depends on DM_I2C
|
||||
help
|
||||
Enable support for the JEDEC JC-42.4 temperature sensor found
|
||||
on the SPD bus of DDR3 and DDR4 DIMMs (TSE2004av and compatible).
|
||||
|
||||
endif # if DM_THERMAL
|
||||
|
||||
@@ -11,3 +11,4 @@ obj-$(CONFIG_RCAR_GEN3_THERMAL) += rcar_gen3_thermal.o
|
||||
obj-$(CONFIG_SANDBOX) += thermal_sandbox.o
|
||||
obj-$(CONFIG_TI_DRA7_THERMAL) += ti-bandgap.o
|
||||
obj-$(CONFIG_TI_LM74_THERMAL) += ti-lm74.o
|
||||
obj-$(CONFIG_DM_THERMAL_JC42) += jc42.o
|
||||
|
||||
@@ -6,14 +6,17 @@
|
||||
|
||||
#include <config.h>
|
||||
#include <asm/io.h>
|
||||
#include <asm/arch/clock.h>
|
||||
#include <asm/arch/sys_proto.h>
|
||||
#if IS_ENABLED(CONFIG_ARCH_IMX8M) || IS_ENABLED(CONFIG_IMX93)
|
||||
#include <asm/arch/clock.h>
|
||||
#endif
|
||||
#include <dm.h>
|
||||
#include <dm/device_compat.h>
|
||||
#include <dm/device-internal.h>
|
||||
#include <dm/device.h>
|
||||
#include <errno.h>
|
||||
#include <fuse.h>
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/delay.h>
|
||||
#include <malloc.h>
|
||||
#include <thermal.h>
|
||||
@@ -22,10 +25,12 @@
|
||||
#define FLAGS_VER2 0x1
|
||||
#define FLAGS_VER3 0x2
|
||||
#define FLAGS_VER4 0x4
|
||||
#define FLAGS_QORIQ 0x8
|
||||
|
||||
#define TMR_DISABLE 0x0
|
||||
#define TMR_ME 0x80000000
|
||||
#define TMR_ALPF 0x0c000000
|
||||
#define QORIQ_TMR_ALPF (0x3 << 24) /* QorIQ ALPF lives at bits[25:24] */
|
||||
#define TMTMIR_DEFAULT 0x00000002
|
||||
#define TIER_DISABLE 0x0
|
||||
|
||||
@@ -33,7 +38,19 @@
|
||||
#define TER_ADC_PD 0x40000000
|
||||
#define TER_ALPF 0x3
|
||||
|
||||
/* default CPU delay time to cool down if over temperature */
|
||||
#define IMX_TMU_POLLING_DELAY_MS 5000
|
||||
|
||||
/* TRITSR - QorIQ Immediate Temperature Site Register.
|
||||
*
|
||||
* Per LX2160A Reference Manual, Rev. 1 (10/2021) section 28.3.1.24
|
||||
* the calibrated reading lives in TEMP[8:0] and is reported in
|
||||
* degrees Kelvin (integer). The QorIQ regs_v1 variant has no
|
||||
* fractional 0.5 degC bit, unlike the i.MX regs_v2 / VER4 layouts.
|
||||
*/
|
||||
#define TRITSR_V BIT(31) /* reading valid */
|
||||
#define TRITSR_TEMP_MASK GENMASK(8, 0) /* degrees Kelvin */
|
||||
#define TRITSR_KELVIN_OFFSET 273 /* TEMP[8:0] - 273 = degC */
|
||||
/*
|
||||
* i.MX TMU Registers
|
||||
*/
|
||||
@@ -148,11 +165,37 @@ struct imx_tmu_regs_v3 {
|
||||
u32 trim;
|
||||
};
|
||||
|
||||
/*
|
||||
* fsl,qoriq-tmu (LX2160A, LS1028A, LS1088A, ...). Same TMU IP family as
|
||||
* the i.MX "regs_v1" layout but: site-enable is a discrete TMSR at 0x08
|
||||
* (TMTMIR moves to 0x0C), and the temperature range registers are
|
||||
* variable-length at 0xF10 (a SoC may use fewer than 16). Calibration is
|
||||
* taken from the DT (fsl,tmu-range / fsl,tmu-calibration) exactly like
|
||||
* the i.MX regs_v1 path, so qoriq reuses imx_tmu_calibration()'s scheme.
|
||||
*/
|
||||
struct qoriq_tmu_regs {
|
||||
u32 tmr; /* 0x000 mode */
|
||||
u32 tsr; /* 0x004 status */
|
||||
u32 tmsr; /* 0x008 monitor-site enable (bit N = site N) */
|
||||
u32 tmtmir; /* 0x00C measurement interval */
|
||||
u8 res0[0x10];
|
||||
u32 tier; /* 0x020 interrupt enable */
|
||||
u32 tidr; /* 0x024 interrupt detect */
|
||||
u8 res1[0x58];
|
||||
u32 ttcfgr; /* 0x080 temperature config (cal walk) */
|
||||
u32 tscfgr; /* 0x084 sensor config (cal walk) */
|
||||
u8 res2[0x78];
|
||||
struct imx_tmu_site_regs site[SITES_MAX]; /* 0x100 */
|
||||
u8 res3[0xd10];
|
||||
u32 ttrcr[16]; /* 0xF10 temperature range control */
|
||||
};
|
||||
|
||||
union tmu_regs {
|
||||
struct imx_tmu_regs regs_v1;
|
||||
struct imx_tmu_regs_v2 regs_v2;
|
||||
struct imx_tmu_regs_v3 regs_v3;
|
||||
struct imx_tmu_regs_v4 regs_v4;
|
||||
struct qoriq_tmu_regs regs_qoriq;
|
||||
};
|
||||
|
||||
struct imx_tmu_plat {
|
||||
@@ -189,6 +232,9 @@ static int read_temperature(struct udevice *dev, int *temp)
|
||||
} else if (drv_data & FLAGS_VER4) {
|
||||
val = readl(&pdata->regs->regs_v4.tritsr0);
|
||||
valid = val & 0x80000000;
|
||||
} else if (drv_data & FLAGS_QORIQ) {
|
||||
val = readl(&pdata->regs->regs_qoriq.site[pdata->id].tritsr);
|
||||
valid = val & TRITSR_V;
|
||||
} else {
|
||||
val = readl(&pdata->regs->regs_v1.site[pdata->id].tritsr);
|
||||
valid = val & 0x80000000;
|
||||
@@ -213,6 +259,19 @@ static int read_temperature(struct udevice *dev, int *temp)
|
||||
|
||||
/* Convert Kelvin to Celsius */
|
||||
*temp -= 273000;
|
||||
} else if (drv_data & FLAGS_QORIQ) {
|
||||
/*
|
||||
* LX2160A Reference Manual, Rev. 1 (10/2021)
|
||||
* section 28.3.1.24: TEMP[8:0] is the calibrated
|
||||
* reading in degrees Kelvin (integer, no 0.5 degC
|
||||
* bit on the regs_v1 variant). The calibration
|
||||
* point examples in the same RM section 28.1.3
|
||||
* use the same Kelvin/Celsius offset:
|
||||
* TTR0CR=0x800000E6 -> 230K (-43 degC)
|
||||
* TTR1CR=0x8001017D -> 381K (108 degC)
|
||||
*/
|
||||
*temp = ((val & TRITSR_TEMP_MASK) -
|
||||
TRITSR_KELVIN_OFFSET) * 1000;
|
||||
} else {
|
||||
*temp = (val & 0xff) * 1000;
|
||||
}
|
||||
@@ -265,6 +324,35 @@ static int imx_tmu_calibration(struct udevice *dev)
|
||||
if (drv_data & (FLAGS_VER2 | FLAGS_VER3))
|
||||
return 0;
|
||||
|
||||
if (drv_data & FLAGS_QORIQ) {
|
||||
const fdt32_t *ranges;
|
||||
int n;
|
||||
|
||||
ranges = dev_read_prop(dev, "fsl,tmu-range", &len);
|
||||
if (!ranges || len % 4 ||
|
||||
len / 4 > (int)ARRAY_SIZE(pdata->regs->regs_qoriq.ttrcr)) {
|
||||
dev_err(dev, "TMU: missing/invalid fsl,tmu-range\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
n = len / 4;
|
||||
for (i = 0; i < n; i++)
|
||||
writel(fdt32_to_cpu(ranges[i]),
|
||||
&pdata->regs->regs_qoriq.ttrcr[i]);
|
||||
|
||||
calibration = dev_read_prop(dev, "fsl,tmu-calibration", &len);
|
||||
if (!calibration || len % 8) {
|
||||
dev_err(dev, "TMU: invalid calibration data.\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
for (i = 0; i < len; i += 8, calibration += 2) {
|
||||
writel(fdt32_to_cpu(*calibration),
|
||||
&pdata->regs->regs_qoriq.ttcfgr);
|
||||
writel(fdt32_to_cpu(*(calibration + 1)),
|
||||
&pdata->regs->regs_qoriq.tscfgr);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (drv_data & FLAGS_VER4) {
|
||||
calibration = dev_read_prop(dev, "fsl,tmu-calibration", &len);
|
||||
if (!calibration || len % 8 || len > 128) {
|
||||
@@ -402,6 +490,18 @@ static inline void imx_tmu_mx8mq_init(struct udevice *dev) { }
|
||||
|
||||
static void imx_tmu_arch_init(struct udevice *dev)
|
||||
{
|
||||
/*
|
||||
* QorIQ takes its calibration from the DT (fsl,tmu-calibration),
|
||||
* not from OCOTP fuses, so it has no per-SoC arch init. The #if
|
||||
* below is still required: the i.MX SoC-ID helpers and fuse API
|
||||
* (<asm/arch/sys_proto.h>) do not exist in a Layerscape build, so
|
||||
* the references must be removed at compile time, not merely
|
||||
* skipped at runtime.
|
||||
*/
|
||||
if (dev_get_driver_data(dev) & FLAGS_QORIQ)
|
||||
return;
|
||||
|
||||
#if IS_ENABLED(CONFIG_ARCH_IMX8M) || IS_ENABLED(CONFIG_IMX93)
|
||||
if (is_imx8mm() || is_imx8mn())
|
||||
imx_tmu_mx8mm_mx8mn_init(dev);
|
||||
else if (is_imx8mp())
|
||||
@@ -412,6 +512,7 @@ static void imx_tmu_arch_init(struct udevice *dev)
|
||||
imx_tmu_mx8mq_init(dev);
|
||||
else
|
||||
dev_err(dev, "Unsupported SoC, TMU calibration not loaded!\n");
|
||||
#endif
|
||||
}
|
||||
|
||||
static void imx_tmu_init(struct udevice *dev)
|
||||
@@ -443,6 +544,15 @@ static void imx_tmu_init(struct udevice *dev)
|
||||
|
||||
/* Set update_interval */
|
||||
writel(TMTMIR_DEFAULT, &pdata->regs->regs_v4.tmtmir);
|
||||
} else if (drv_data & FLAGS_QORIQ) {
|
||||
/* Disable monitoring */
|
||||
writel(TMR_DISABLE, &pdata->regs->regs_qoriq.tmr);
|
||||
|
||||
/* Disable interrupt, using polling instead */
|
||||
writel(TIER_DISABLE, &pdata->regs->regs_qoriq.tier);
|
||||
|
||||
/* Set update_interval */
|
||||
writel(TMTMIR_DEFAULT, &pdata->regs->regs_qoriq.tmtmir);
|
||||
} else {
|
||||
/* Disable monitoring */
|
||||
writel(TMR_DISABLE, &pdata->regs->regs_v1.tmr);
|
||||
@@ -511,6 +621,18 @@ static int imx_tmu_enable_msite(struct udevice *dev)
|
||||
/* Enable ME */
|
||||
reg |= TMR_ME;
|
||||
writel(reg, &pdata->regs->regs_v4.tmr);
|
||||
} else if (drv_data & FLAGS_QORIQ) {
|
||||
/* Clear ME, enable every site at once via the discrete TMSR */
|
||||
reg = readl(&pdata->regs->regs_qoriq.tmr);
|
||||
reg &= ~TMR_ME;
|
||||
writel(reg, &pdata->regs->regs_qoriq.tmr);
|
||||
|
||||
writel(GENMASK(SITES_MAX - 1, 0),
|
||||
&pdata->regs->regs_qoriq.tmsr);
|
||||
|
||||
reg |= QORIQ_TMR_ALPF;
|
||||
reg |= TMR_ME;
|
||||
writel(reg, &pdata->regs->regs_qoriq.tmr);
|
||||
} else {
|
||||
/* Clear the ME before setting MSITE and ALPF*/
|
||||
reg = readl(&pdata->regs->regs_v1.tmr);
|
||||
@@ -650,6 +772,7 @@ static const struct udevice_id imx_tmu_ids[] = {
|
||||
{ .compatible = "fsl,imx8mm-tmu", .data = FLAGS_VER2, },
|
||||
{ .compatible = "fsl,imx8mp-tmu", .data = FLAGS_VER3, },
|
||||
{ .compatible = "fsl,imx93-tmu", .data = FLAGS_VER4, },
|
||||
{ .compatible = "fsl,qoriq-tmu", .data = FLAGS_QORIQ, },
|
||||
{ }
|
||||
};
|
||||
|
||||
|
||||
93
drivers/thermal/jc42.c
Normal file
93
drivers/thermal/jc42.c
Normal file
@@ -0,0 +1,93 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Copyright 2026 Free Mobile - Vincent Jardin
|
||||
*
|
||||
* JEDEC JC-42.4 / TSE2004av Temperature Sensor driver.
|
||||
*
|
||||
* Generic I2C temperature sensor of the Serial Presence Detect (SPD)
|
||||
* bus of DDR3 and DDR4 SO-DIMMs / UDIMMs / RDIMMs per the JEDEC
|
||||
* JC-42.4 standard. The TSE2004av variant adds an integrated SPD
|
||||
* EEPROM, but the thermal register interface is the same and is
|
||||
* what this driver exposes.
|
||||
*
|
||||
* Register layout (subset):
|
||||
* 0x05 Ambient temperature, 16-bit big-endian:
|
||||
* bit 15 : T_CRIT alarm flag (read-only)
|
||||
* bit 14 : T_HIGH alarm flag (read-only)
|
||||
* bit 13 : T_LOW alarm flag (read-only)
|
||||
* bit 12 : sign (two's-complement within bits[12:0])
|
||||
* bits[11:0]: magnitude * 16 (LSB = 0.0625 degC = 62.5 mC)
|
||||
* 0x06 Manufacturer ID (16-bit BE, JEP-106 vendor code)
|
||||
* 0x07 Device ID + Revision (upper byte = ID, lower = revision)
|
||||
* ...
|
||||
*/
|
||||
|
||||
#include <dm.h>
|
||||
#include <i2c.h>
|
||||
#include <thermal.h>
|
||||
#include <linux/bitops.h>
|
||||
|
||||
#define JC42_REG_TEMP 0x05
|
||||
|
||||
#define JC42_TEMP_SIGN BIT(12)
|
||||
#define JC42_TEMP_MAGNITUDE GENMASK(11, 0)
|
||||
|
||||
static int jc42_get_temp(struct udevice *dev, int *temp)
|
||||
{
|
||||
u8 buf[2];
|
||||
int ret;
|
||||
int mag;
|
||||
|
||||
ret = dm_i2c_read(dev, JC42_REG_TEMP, buf, sizeof(buf));
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
mag = ((buf[0] << 8) | buf[1]) & (JC42_TEMP_SIGN | JC42_TEMP_MAGNITUDE);
|
||||
if (mag & JC42_TEMP_SIGN)
|
||||
mag -= (JC42_TEMP_SIGN << 1);
|
||||
|
||||
/*
|
||||
* mag is in units of 1/16 degC. Multiply first to keep one
|
||||
* extra bit of precision before the divide. Worst-case range
|
||||
* for a 13-bit signed value is +/-4096, so the product fits
|
||||
* comfortably in an int (~4.1M mC).
|
||||
*/
|
||||
*temp = mag * 1000 / 16;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct dm_thermal_ops jc42_ops = {
|
||||
.get_temp = jc42_get_temp,
|
||||
};
|
||||
|
||||
/*
|
||||
* Optional DT label property override: it replace the default DM
|
||||
* device name (the ofnode name, eg "temp@18") so
|
||||
* temperature list or temperature get commands
|
||||
* show a human-meaningful identifier such as "ddr-top" or
|
||||
* "ddr-bottom".
|
||||
* It mirrors the Linux hwmon binding which uses label for the
|
||||
* per-sensor display name.
|
||||
*/
|
||||
static int jc42_bind(struct udevice *dev)
|
||||
{
|
||||
const char *label = dev_read_string(dev, "label");
|
||||
|
||||
if (label && *label)
|
||||
return device_set_name(dev, label);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct udevice_id jc42_match[] = {
|
||||
{ .compatible = "jedec,jc-42.4-temp" },
|
||||
{ }
|
||||
};
|
||||
|
||||
U_BOOT_DRIVER(jc42_thermal) = {
|
||||
.name = "jc42_thermal",
|
||||
.id = UCLASS_THERMAL,
|
||||
.of_match = jc42_match,
|
||||
.bind = jc42_bind,
|
||||
.ops = &jc42_ops,
|
||||
};
|
||||
Reference in New Issue
Block a user