From a0597ba2d824adc74ce250019f3d24d1a06ae144 Mon Sep 17 00:00:00 2001 From: Andre Przywara Date: Thu, 21 Mar 2024 14:54:36 +0000 Subject: [PATCH 1/5] refactor(allwinner): h616: prepare for more than one PMIC model Most devices based on Allwinner SoCs come with a certain fixed combination of Power Management ICs (PMICs) and SoC, for instance the A64 with the AXP803, or the H6 with the AXP805. This allowed us to include the respective PMIC support code into each build target at build time. Similarly on H616 devices we initially saw only the AXP305, but for a while now the simpler (and cheaper) AXP313a is a popular companion to the H616 on many new boards. On at least one new device the AXP717 is used as well. With some rudimentary AXP version check in place we at least detected the case of an unsupported SoC, but threw an error message, and lost support for powering off the device. Refactor the existing PMIC code to be able to support more than one PMIC model, detected at runtime. For this we use a variable for the RSB runtime address instead of hardcoding the address used on the AXP305, and read the hardware bus address from the devicetree. Also we look up the used PMIC in the devicetree, and set the PMIC model accordingly. To be on the safe side, we also confirm the real PMIC used by checking its version register and comparing that with the expected value. Finally the register offset and value to power off the PMIC is moved direclty into the platform code, as those values differ between the different PMICs. This is just refactoring and better error report, we still only support the AXP305 on RSB at the moment. Change-Id: I00b26ce4d30bb570ee1cd4979d0cdc9d6c020729 Signed-off-by: Andre Przywara --- plat/allwinner/sun50i_h616/platform.mk | 3 +- plat/allwinner/sun50i_h616/sunxi_power.c | 118 +++++++++++++++++------ 2 files changed, 93 insertions(+), 28 deletions(-) diff --git a/plat/allwinner/sun50i_h616/platform.mk b/plat/allwinner/sun50i_h616/platform.mk index de494a2f4..888eea5ae 100644 --- a/plat/allwinner/sun50i_h616/platform.mk +++ b/plat/allwinner/sun50i_h616/platform.mk @@ -18,5 +18,6 @@ ifeq (${SUNXI_PSCI_USE_SCPI}, 1) $(error "H616 does not support SCPI PSCI ops") endif -BL31_SOURCES += drivers/allwinner/axp/axp805.c \ +BL31_SOURCES += common/fdt_wrappers.c \ + drivers/allwinner/axp/axp805.c \ drivers/allwinner/sunxi_rsb.c \ diff --git a/plat/allwinner/sun50i_h616/sunxi_power.c b/plat/allwinner/sun50i_h616/sunxi_power.c index dd6ebba9a..6873724f6 100644 --- a/plat/allwinner/sun50i_h616/sunxi_power.c +++ b/plat/allwinner/sun50i_h616/sunxi_power.c @@ -10,97 +10,161 @@ #include #include +#include #include #include #include +#include #include #include #include #include -#define AXP305_I2C_ADDR 0x36 -#define AXP305_HW_ADDR 0x745 -#define AXP305_RT_ADDR 0x3a +static uint16_t pmic_bus_addr; +static uint8_t rsb_rt_addr; static enum pmic_type { UNKNOWN, AXP305, } pmic; +static uint8_t get_rsb_rt_address(uint16_t hw_addr) +{ + switch (hw_addr) { + case 0x745: return 0x3a; + } + + return 0; +} + int axp_read(uint8_t reg) { - return rsb_read(AXP305_RT_ADDR, reg); + return rsb_read(rsb_rt_addr, reg); } int axp_write(uint8_t reg, uint8_t val) { - return rsb_write(AXP305_RT_ADDR, reg, val); + return rsb_write(rsb_rt_addr, reg, val); } -static int rsb_init(void) +static int rsb_init(int rsb_hw_addr) { int ret; ret = rsb_init_controller(); - if (ret) + if (ret) { return ret; + } /* Switch to the recommended 3 MHz bus clock. */ ret = rsb_set_bus_speed(SUNXI_OSC24M_CLK_IN_HZ, 3000000); - if (ret) + if (ret) { return ret; + } /* Initiate an I2C transaction to switch the PMIC to RSB mode. */ ret = rsb_set_device_mode(AXP20X_MODE_RSB << 16 | AXP20X_MODE_REG << 8); - if (ret) + if (ret) { return ret; + } /* Associate the 8-bit runtime address with the 12-bit bus address. */ - ret = rsb_assign_runtime_address(AXP305_HW_ADDR, AXP305_RT_ADDR); - if (ret) + ret = rsb_assign_runtime_address(rsb_hw_addr, rsb_rt_addr); + if (ret) { return ret; + } - return axp_check_id(); + return 0; } -int sunxi_pmic_setup(uint16_t socid, const void *fdt) +static int pmic_bus_init(uint16_t socid, uint16_t rsb_hw_addr) { int ret; - INFO("PMIC: Probing AXP305 on RSB\n"); - ret = sunxi_init_platform_r_twi(socid, true); if (ret) { INFO("Could not init platform bus: %d\n", ret); + pmic = UNKNOWN; return ret; } - ret = rsb_init(); + ret = rsb_init(rsb_hw_addr); if (ret) { - INFO("Could not init RSB: %d\n", ret); + pmic = UNKNOWN; return ret; } - pmic = AXP305; - axp_setup_regulators(fdt); + return 0; +} - /* Switch the PMIC back to I2C mode. */ - ret = axp_write(AXP20X_MODE_REG, AXP20X_MODE_I2C); - if (ret) +int sunxi_pmic_setup(uint16_t socid, const void *fdt) +{ + int node, ret; + uint32_t reg; + + node = fdt_node_offset_by_compatible(fdt, 0, "x-powers,axp806"); + if (node >= 0) { + pmic = AXP305; + } + + if (pmic == UNKNOWN) { + INFO("PMIC: No known PMIC in DT, skipping setup.\n"); + return -ENODEV; + } + + if (fdt_read_uint32(fdt, node, "reg", ®)) { + ERROR("PMIC: PMIC DT node does not contain reg property.\n"); + return -EINVAL; + } + + pmic_bus_addr = reg; + rsb_rt_addr = get_rsb_rt_address(pmic_bus_addr); + if (rsb_rt_addr == 0) { + ERROR("PMIC: no mapping for RSB address 0x%x\n", reg); + return -EINVAL; + } + + INFO("Probing for PMIC on RSB:\n"); + + ret = pmic_bus_init(socid, pmic_bus_addr); + if (ret) { return ret; + } - return 0; + ret = axp_read(0x03); + switch (ret & 0xcf) { + case 0x40: /* AXP305 */ + if (pmic == AXP305) { + INFO("PMIC: found AXP305, setting up regulators\n"); + axp_setup_regulators(fdt); + } else { + pmic = UNKNOWN; + } + break; + } + + /* Switch the PMIC back to I2C mode. */ + return rsb_write(rsb_rt_addr, AXP20X_MODE_REG, AXP20X_MODE_I2C); } void sunxi_power_down(void) { + int ret; + + if (pmic == UNKNOWN) { + return; + } + + /* Re-initialise after rich OS might have used it. */ + ret = pmic_bus_init(SUNXI_SOC_H616, pmic_bus_addr); + if (ret) { + return; + } + switch (pmic) { case AXP305: - /* Re-initialise after rich OS might have used it. */ - sunxi_init_platform_r_twi(SUNXI_SOC_H616, true); - rsb_init(); - axp_power_off(); + axp_setbits(0x32, BIT(7)); break; default: break; From 044458981f986b03445185b646bebbea1d90f11f Mon Sep 17 00:00:00 2001 From: Andre Przywara Date: Thu, 21 Mar 2024 14:56:07 +0000 Subject: [PATCH 2/5] feat(allwinner): h616: add I2C PMIC support The X-Powers AXP305 PMIC can be controlled via both I2C or RSB (an Allwinner specific bus similar to I2C), but we chose to use only RSB, because that's easier to program and also used by Linux. The AXP313a PMIC however supports only I2C, so we need to support both buses, and need to decide which to use at runtime. Prepare the PMIC code to add (back) I2C support. We initially used I2C on the H6/AXP805 combination, but replaced that later with RSB. So this patch is bringing some of that older code back. The decision whether to use I2C or RSB is made by the devicetree, since on some boards even RSB capable PMICs are controlled via I2C, since they share the bus with only I2C capable devices, for instance RTCs. At the moment this will still use RSB to drive the AXP305, but the (dynamic) I2C code will be used shortly to support the AXP313. This increases the code size by one 4K page, but with 80K out of the reserved 256K we are still very far away from our limit. Change-Id: I65c1e7df93dbd2dcd171b3fc486533a2948cc75b Signed-off-by: Andre Przywara --- plat/allwinner/sun50i_h616/platform.mk | 1 + plat/allwinner/sun50i_h616/sunxi_power.c | 82 +++++++++++++++++++----- 2 files changed, 68 insertions(+), 15 deletions(-) diff --git a/plat/allwinner/sun50i_h616/platform.mk b/plat/allwinner/sun50i_h616/platform.mk index 888eea5ae..e83ef5848 100644 --- a/plat/allwinner/sun50i_h616/platform.mk +++ b/plat/allwinner/sun50i_h616/platform.mk @@ -21,3 +21,4 @@ endif BL31_SOURCES += common/fdt_wrappers.c \ drivers/allwinner/axp/axp805.c \ drivers/allwinner/sunxi_rsb.c \ + drivers/mentor/i2c/mi2cv.c diff --git a/plat/allwinner/sun50i_h616/sunxi_power.c b/plat/allwinner/sun50i_h616/sunxi_power.c index 6873724f6..9a130c43d 100644 --- a/plat/allwinner/sun50i_h616/sunxi_power.c +++ b/plat/allwinner/sun50i_h616/sunxi_power.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include @@ -24,6 +25,11 @@ static uint16_t pmic_bus_addr; static uint8_t rsb_rt_addr; +static bool is_using_rsb(void) +{ + return rsb_rt_addr != 0; +} + static enum pmic_type { UNKNOWN, AXP305, @@ -40,12 +46,39 @@ static uint8_t get_rsb_rt_address(uint16_t hw_addr) int axp_read(uint8_t reg) { - return rsb_read(rsb_rt_addr, reg); + uint8_t val; + int ret; + + if (is_using_rsb()) { + return rsb_read(rsb_rt_addr, reg); + } + + ret = i2c_write(pmic_bus_addr, 0, 0, ®, 1); + if (ret == 0) { + ret = i2c_read(pmic_bus_addr, 0, 0, &val, 1); + } + if (ret) { + ERROR("PMIC: Cannot read PMIC register %02x\n", reg); + return ret; + } + + return val; } int axp_write(uint8_t reg, uint8_t val) { - return rsb_write(rsb_rt_addr, reg, val); + int ret; + + if (is_using_rsb()) { + return rsb_write(rsb_rt_addr, reg, val); + } + + ret = i2c_write(pmic_bus_addr, reg, 1, &val, 1); + if (ret) { + ERROR("PMIC: Cannot write PMIC register %02x\n", reg); + } + + return ret; } static int rsb_init(int rsb_hw_addr) @@ -82,17 +115,22 @@ static int pmic_bus_init(uint16_t socid, uint16_t rsb_hw_addr) { int ret; - ret = sunxi_init_platform_r_twi(socid, true); + ret = sunxi_init_platform_r_twi(socid, is_using_rsb()); if (ret) { INFO("Could not init platform bus: %d\n", ret); pmic = UNKNOWN; return ret; } - ret = rsb_init(rsb_hw_addr); - if (ret) { - pmic = UNKNOWN; - return ret; + if (is_using_rsb()) { + ret = rsb_init(rsb_hw_addr); + if (ret) { + pmic = UNKNOWN; + return ret; + } + } else { + /* initialise mi2cv driver */ + i2c_init((void *)SUNXI_R_I2C_BASE); } return 0; @@ -100,7 +138,7 @@ static int pmic_bus_init(uint16_t socid, uint16_t rsb_hw_addr) int sunxi_pmic_setup(uint16_t socid, const void *fdt) { - int node, ret; + int node, parent, ret; uint32_t reg; node = fdt_node_offset_by_compatible(fdt, 0, "x-powers,axp806"); @@ -119,13 +157,18 @@ int sunxi_pmic_setup(uint16_t socid, const void *fdt) } pmic_bus_addr = reg; - rsb_rt_addr = get_rsb_rt_address(pmic_bus_addr); - if (rsb_rt_addr == 0) { - ERROR("PMIC: no mapping for RSB address 0x%x\n", reg); - return -EINVAL; + parent = fdt_parent_offset(fdt, node); + ret = fdt_node_check_compatible(fdt, parent, "allwinner,sun8i-a23-rsb"); + if (ret == 0) { + rsb_rt_addr = get_rsb_rt_address(pmic_bus_addr); + if (rsb_rt_addr == 0) { + ERROR("PMIC: no mapping for RSB address 0x%x\n", + pmic_bus_addr); + return -EINVAL; + } } - INFO("Probing for PMIC on RSB:\n"); + INFO("Probing for PMIC on %s:\n", is_using_rsb() ? "RSB" : "I2C"); ret = pmic_bus_init(socid, pmic_bus_addr); if (ret) { @@ -144,8 +187,17 @@ int sunxi_pmic_setup(uint16_t socid, const void *fdt) break; } - /* Switch the PMIC back to I2C mode. */ - return rsb_write(rsb_rt_addr, AXP20X_MODE_REG, AXP20X_MODE_I2C); + if (is_using_rsb()) { + /* Switch the PMIC back to I2C mode. */ + return rsb_write(rsb_rt_addr, AXP20X_MODE_REG, AXP20X_MODE_I2C); + } + + if (pmic == UNKNOWN) { + INFO("Incompatible or unknown PMIC found.\n"); + return -ENODEV; + } + + return 0; } void sunxi_power_down(void) From 03851367dbd46f73708fa35da2b501489e44afa4 Mon Sep 17 00:00:00 2001 From: Andre Przywara Date: Thu, 21 Mar 2024 14:18:47 +0000 Subject: [PATCH 3/5] feat(allwinner): h616: add support for AXP313 PMIC Many newer boards with the H616 and its sibling H618 are now paired with the X-Powers AXP313 PMIC. This is a simpler PMIC, with only a few voltage rails and no extra functionality except the power key support. In contrast to the AXP305 it can only be controlled via I2C. Add a check to look for the AXP313 compatible string in the devicetree, and set the PMIC type and I2C address accordingly, if one is found. With only very few voltage rails available, all of them are mostly in use and are thus enabled at reset already, so we can skip the regulator setup entirely. Change-Id: I01962854109e43793b4f56553c1ca9e1f752e30d Signed-off-by: Andre Przywara --- plat/allwinner/sun50i_h616/sunxi_power.c | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/plat/allwinner/sun50i_h616/sunxi_power.c b/plat/allwinner/sun50i_h616/sunxi_power.c index 9a130c43d..ac8a68ec5 100644 --- a/plat/allwinner/sun50i_h616/sunxi_power.c +++ b/plat/allwinner/sun50i_h616/sunxi_power.c @@ -33,6 +33,7 @@ static bool is_using_rsb(void) static enum pmic_type { UNKNOWN, AXP305, + AXP313, } pmic; static uint8_t get_rsb_rt_address(uint16_t hw_addr) @@ -146,6 +147,13 @@ int sunxi_pmic_setup(uint16_t socid, const void *fdt) pmic = AXP305; } + if (pmic == UNKNOWN) { + node = fdt_node_offset_by_compatible(fdt, 0, "x-powers,axp313a"); + if (node >= 0) { + pmic = AXP313; + } + } + if (pmic == UNKNOWN) { INFO("PMIC: No known PMIC in DT, skipping setup.\n"); return -ENODEV; @@ -185,6 +193,16 @@ int sunxi_pmic_setup(uint16_t socid, const void *fdt) pmic = UNKNOWN; } break; + case 0x48: /* AXP1530 */ + case 0x4b: /* AXP313A */ + case 0x4c: /* AXP313B */ + if (pmic == AXP313) { + INFO("PMIC: found AXP313\n"); + /* no regulators to set up */ + } else { + pmic = UNKNOWN; + } + break; } if (is_using_rsb()) { @@ -218,6 +236,9 @@ void sunxi_power_down(void) case AXP305: axp_setbits(0x32, BIT(7)); break; + case AXP313: + axp_setbits(0x1a, BIT(7)); + break; default: break; } From 646d06b2378b39b8dfa713b74f936a2b02782e96 Mon Sep 17 00:00:00 2001 From: Andre Przywara Date: Thu, 21 Mar 2024 14:19:14 +0000 Subject: [PATCH 4/5] feat(allwinner): h616: add support for AXP717 PMIC On at least one new device we see an Allwinner H700 SoC paired with the X-Powers AXP717 PMIC. In contrast to the small AXP313, this is a quite complete PMIC, with many voltage rails, battery and USB-C charging support. It supports both RSB and I2C control options. Add the compatible string to the list of checked devices. The AXP717 apparently does not feature a version ID register, but we read 0xff from that address 0x3, so use this as an indication of its presence, since this value differs from what we read from the other PMICs. The register offset and bit position for the power off functionality is again different, but easy to put into our switch/case. Setting up regulators in TF-A is now somewhat obsolete, since U-Boot does a much better job in this now, and can figure out which regulators are actually needed. So we don't add the regulator setup code, and just use the PMIC for the power-off functionality. Change-Id: Ie6b4c91517014adcc79d9a3459c75545fa3a63e6 Signed-off-by: Andre Przywara --- plat/allwinner/sun50i_h616/sunxi_power.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/plat/allwinner/sun50i_h616/sunxi_power.c b/plat/allwinner/sun50i_h616/sunxi_power.c index ac8a68ec5..cab7e4643 100644 --- a/plat/allwinner/sun50i_h616/sunxi_power.c +++ b/plat/allwinner/sun50i_h616/sunxi_power.c @@ -34,11 +34,13 @@ static enum pmic_type { UNKNOWN, AXP305, AXP313, + AXP717, } pmic; static uint8_t get_rsb_rt_address(uint16_t hw_addr) { switch (hw_addr) { + case 0x3a3: return 0x2d; case 0x745: return 0x3a; } @@ -154,6 +156,13 @@ int sunxi_pmic_setup(uint16_t socid, const void *fdt) } } + if (pmic == UNKNOWN) { + node = fdt_node_offset_by_compatible(fdt, 0, "x-powers,axp717"); + if (node >= 0) { + pmic = AXP717; + } + } + if (pmic == UNKNOWN) { INFO("PMIC: No known PMIC in DT, skipping setup.\n"); return -ENODEV; @@ -203,6 +212,14 @@ int sunxi_pmic_setup(uint16_t socid, const void *fdt) pmic = UNKNOWN; } break; + case 0xcf: /* version reg not implemented on AXP717 */ + if (pmic == AXP717) { + INFO("PMIC: found AXP717\n"); + /* no regulators to set up, U-Boot takes care of this */ + } else { + pmic = UNKNOWN; + } + break; } if (is_using_rsb()) { @@ -239,6 +256,9 @@ void sunxi_power_down(void) case AXP313: axp_setbits(0x1a, BIT(7)); break; + case AXP717: + axp_setbits(0x27, BIT(0)); + break; default: break; } From ee5b26fd0058d5e696cdf83bf389351eab296bf7 Mon Sep 17 00:00:00 2001 From: Andre Przywara Date: Wed, 1 May 2024 14:05:24 +0100 Subject: [PATCH 5/5] feat(allwinner): adjust H616 L2 cache size in DTB The Allwinner H616 and its siblings come in different die revisions, some have 256 KB of L2 cache, some have 1 MB. This prevents a single static cache description in the devicetree. Use the cache size ID register (CCSIDR_EL1) to query the topology of the L2 cache, and adjust the cache-sets and cache-size properties in the L2 cache DT node accordingly. The ARM ARM does not promise (anymore) that the cache size can be derived *architecturally* from this register, but the reading is definitely correct for the Arm Cortex-A53 core used. Change-Id: Id7dc324d783b8319fe5df6164be2f941d4cac82d Signed-off-by: Andre Przywara --- plat/allwinner/common/include/sunxi_private.h | 8 +++ plat/allwinner/common/sunxi_prepare_dtb.c | 2 + plat/allwinner/sun50i_h616/platform.mk | 3 +- plat/allwinner/sun50i_h616/sunxi_h616_dtb.c | 72 +++++++++++++++++++ 4 files changed, 84 insertions(+), 1 deletion(-) create mode 100644 plat/allwinner/sun50i_h616/sunxi_h616_dtb.c diff --git a/plat/allwinner/common/include/sunxi_private.h b/plat/allwinner/common/include/sunxi_private.h index 6a3865745..b9ca3f600 100644 --- a/plat/allwinner/common/include/sunxi_private.h +++ b/plat/allwinner/common/include/sunxi_private.h @@ -58,4 +58,12 @@ static inline void sunxi_prepare_dtb(void *fdt) } #endif +#ifdef PLAT_sun50i_h616 +void sunxi_soc_fdt_fixup(void *dtb); +#else +static inline void sunxi_soc_fdt_fixup(void *dtb) +{ +} +#endif + #endif /* SUNXI_PRIVATE_H */ diff --git a/plat/allwinner/common/sunxi_prepare_dtb.c b/plat/allwinner/common/sunxi_prepare_dtb.c index 66af35ab8..0f689744a 100644 --- a/plat/allwinner/common/sunxi_prepare_dtb.c +++ b/plat/allwinner/common/sunxi_prepare_dtb.c @@ -34,6 +34,8 @@ void sunxi_prepare_dtb(void *fdt) } #endif + sunxi_soc_fdt_fixup(fdt); + if (sunxi_psci_is_scpi()) { ret = fdt_add_cpu_idle_states(fdt, sunxi_idle_states); if (ret < 0) { diff --git a/plat/allwinner/sun50i_h616/platform.mk b/plat/allwinner/sun50i_h616/platform.mk index e83ef5848..6f44e8c26 100644 --- a/plat/allwinner/sun50i_h616/platform.mk +++ b/plat/allwinner/sun50i_h616/platform.mk @@ -21,4 +21,5 @@ endif BL31_SOURCES += common/fdt_wrappers.c \ drivers/allwinner/axp/axp805.c \ drivers/allwinner/sunxi_rsb.c \ - drivers/mentor/i2c/mi2cv.c + drivers/mentor/i2c/mi2cv.c \ + ${AW_PLAT}/${PLAT}/sunxi_h616_dtb.c diff --git a/plat/allwinner/sun50i_h616/sunxi_h616_dtb.c b/plat/allwinner/sun50i_h616/sunxi_h616_dtb.c new file mode 100644 index 000000000..ec49f4c9c --- /dev/null +++ b/plat/allwinner/sun50i_h616/sunxi_h616_dtb.c @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2024, ARM Limited. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + * + * Amend the device tree to adjust the L2 cache size, which is different + * between the revisions of the H616 chips: earlier versions have 256 KB of L2, + * later versions 1 MB. + * Read the cache ID registers and adjust the size and number of sets entries + * in the L2 cache DT node. + */ + +#include +#include +#include + +#define CACHE_L1D 0x0 +#define CACHE_L1I 0x1 +#define CACHE_L2U 0x2 + +#define CCSIDR_SETS_SHIFT 13 +#define CCSIDR_SETS_MASK GENMASK(14, 0) +#define CCSIDR_ASSOC_SHIFT 3 +#define CCSIDR_ASSOC_MASK GENMASK(9, 0) +#define CCSIDR_LSIZE_SHIFT 0 +#define CCSIDR_LSIZE_MASK GENMASK(2, 0) + +static uint32_t armv8_get_ccsidr(unsigned int sel) +{ + uint32_t reg; + + __asm__ volatile ("msr CSSELR_EL1, %0\n" :: "r" (sel)); + __asm__ volatile ("mrs %0, CCSIDR_EL1\n" : "=r" (reg)); + + return reg; +} + +void sunxi_soc_fdt_fixup(void *dtb) +{ + int node = fdt_path_offset(dtb, "/cpus/cpu@0"); + uint32_t phandle, ccsidr, cell; + int sets, line_size, assoc; + int ret; + + if (node < 0) { + return; + } + + ret = fdt_read_uint32(dtb, node, "next-level-cache", &phandle); + if (ret != 0) { + return; + } + + node = fdt_node_offset_by_phandle(dtb, phandle); + if (ret != 0) { + return; + } + + ccsidr = armv8_get_ccsidr(CACHE_L2U); + sets = ((ccsidr >> CCSIDR_SETS_SHIFT) & CCSIDR_SETS_MASK) + 1; + line_size = 16U << ((ccsidr >> CCSIDR_LSIZE_SHIFT) & CCSIDR_LSIZE_MASK); + assoc = ((ccsidr >> CCSIDR_ASSOC_SHIFT) & CCSIDR_ASSOC_MASK) + 1; + + cell = cpu_to_fdt32(sets); + fdt_setprop(dtb, node, "cache-sets", &cell, sizeof(cell)); + + cell = cpu_to_fdt32(line_size); + fdt_setprop(dtb, node, "cache-line-size", &cell, sizeof(cell)); + + cell = cpu_to_fdt32(sets * assoc * line_size); + fdt_setprop(dtb, node, "cache-size", &cell, sizeof(cell)); +}