1
1
openwrt/target/linux/realtek/files-6.18/arch/mips/rtl-otto/prom.c
Rustam Adilov a03e379f5a realtek: arch: rtl-otto: change to __raw reads and writes
The realtek target uses the readl and writel for register access to
switchcore and SoC bases. This works but if at some point the target
wants to enable CONFIG_SWAP_IO_SPACE, the register access breaks as
readl/writel would be operating in little endian as opposed to native.

Fix it by replacing the readl/writel used in register access macros to
a __raw variant which aligns with what upstream use for native endian
access to registers.

Signed-off-by: Rustam Adilov <adilov@tutamail.com>
Link: https://github.com/openwrt/openwrt/pull/23206
Signed-off-by: Markus Stockhausen <markus.stockhausen@gmx.de>
2026-06-01 19:17:58 +02:00

416 lines
11 KiB
C

// SPDX-License-Identifier: GPL-2.0-only
/*
* prom.c
* Early initialization code for the Realtek RTL838X SoC
*
* based on the original BSP by
* Copyright (C) 2006-2012 Tony Wu (tonywu@realtek.com)
* Copyright (C) 2020 B. Koblitz
*
*/
#include <asm/fw/fw.h>
#include <asm/mips-cps.h>
#include <asm/prom.h>
#include <asm/smp-ops.h>
#include <linux/of.h>
#include <linux/smp.h>
#include <mach-rtl-otto.h>
#define RTL_SOC_BASE ((volatile void *) 0xB8000000)
#define RTL83XX_DRAM_CONFIG 0x1004
#define RTL9300_SRAMSAR0 0x4000
#define RTL9300_SRAMSAR1 0x4010
#define RTL9300_SRAMSAR2 0x4020
#define RTL9300_SRAMSAR3 0x4030
#define RTL9300_UMSAR0 0x1300
#define RTL9300_UMSAR1 0x1310
#define RTL9300_UMSAR2 0x1320
#define RTL9300_UMSAR3 0x1330
#define RTL9300_O0DOR2 0x4220
#define RTL9300_O0DMAR2 0x4224
#define RTL931X_DRAM_CONFIG 0x14304c
#define soc_r32(reg) __raw_readl(RTL_SOC_BASE + reg)
#define soc_w32(val, reg) __raw_writel(val, RTL_SOC_BASE + reg)
struct rtl83xx_soc_info soc_info;
EXPORT_SYMBOL(soc_info);
const void *fdt;
#ifdef CONFIG_MIPS_MT_SMP
extern const struct plat_smp_ops vsmp_smp_ops;
static struct plat_smp_ops rtl_smp_ops;
static void rtlsmp_init_secondary(void)
{
/*
* Enable all CPU interrupts, as everything is managed by the external controller.
* TODO: Standard vsmp_init_secondary() has special treatment for Malta if external
* GIC is available. Maybe we need this too.
*/
if (mips_gic_present())
pr_warn("%s: GIC present. Maybe interrupt enabling required.\n", __func__);
else
set_c0_status(ST0_IM);
}
static void rtlsmp_finish(void)
{
/* These devices are low on resources. There might be the chance that CEVT_R4K is
* not enabled in kernel build. Nevertheless the timer and interrupt 7 might be
* active by default after startup of secondary VPEs. With no registered handler
* that leads to continuous unhandeled interrupts. Disable it but keep the counter
* running so it can still be used as an entropy source.
*/
if (!IS_ENABLED(CONFIG_CEVT_R4K)) {
write_c0_status(read_c0_status() & ~CAUSEF_IP7);
write_c0_compare(read_c0_count() - 1);
}
local_irq_enable();
}
static int rtlsmp_register(void)
{
if (!cpu_has_mipsmt)
return 1;
rtl_smp_ops = vsmp_smp_ops;
rtl_smp_ops.init_secondary = rtlsmp_init_secondary;
rtl_smp_ops.smp_finish = rtlsmp_finish;
register_smp_ops(&rtl_smp_ops);
return 0;
}
#else /* !CONFIG_MIPS_MT_SMP */
#define rtlsmp_register() (1)
#endif
static void __init apply_early_quirks(void)
{
if (soc_info.family == RTL8380_FAMILY_ID) {
/*
* Open up write protected registers. SDK opens/closes this whenever needed. For
* simplicity always work with an "open" register set.
*/
sw_w32(0x3, RTL838X_INT_RW_CTRL);
/*
* Disable 4 byte address mode of flash controller. If this bit is not cleared
* the watchdog cannot reset the SoC. The SDK changes this short before restart.
* Until this quirk was implemented all RTL838x devices ran with this disabled
* because of a coding error. As no issues were detected keep the behaviour
* until more details are known.
*/
sw_w32_mask(BIT(30), 0, RTL838X_PLL_CML_CTRL);
}
}
static void __init apply_dts_quirks(void)
{
struct device_node *node;
node = of_find_compatible_node(NULL, NULL, "diodes,pt7a75xx-wdt");
if (node) {
if (soc_info.family == RTL9310_FAMILY_ID) {
pr_info("apply quirk for diodes pt7a75xx watchdog\n");
sw_w32_mask(GENMASK(13, 12), BIT(12), RTL931X_LED_GLB_CTRL);
sw_w32_mask(0x0, BIT(8), RTL931X_MAC_L2_GLOBAL_CTRL2);
};
of_node_put(node);
}
}
void __init device_tree_init(void)
{
if (!fdt_check_header(&__appended_dtb)) {
fdt = &__appended_dtb;
pr_info("Using appended Device Tree.\n");
}
initial_boot_params = (void *)fdt;
unflatten_and_copy_device_tree();
apply_dts_quirks();
/* delay cpc & smp probing to allow devicetree access */
mips_cpc_probe();
if (!register_cps_smp_ops())
return;
if (!rtlsmp_register())
return;
register_up_smp_ops();
}
const char *get_system_type(void)
{
return soc_info.system_type;
}
static void __init rtl838x_read_details(u32 model)
{
u32 chip_info, ext_version, tmp;
sw_w32(0xa << 28, RTL838X_CHIP_INFO);
chip_info = sw_r32(RTL838X_CHIP_INFO);
soc_info.cpu = chip_info & 0xffff;
ext_version = sw_r32(RTL838X_EXT_VERSION);
tmp = ext_version & 0x1f;
if (tmp == 2) {
soc_info.revision = 1;
} else {
tmp = (chip_info >> 16) & 0x1f;
if (soc_info.cpu == 0x0477) {
soc_info.revision = tmp;
soc_info.testchip = true;
} else {
soc_info.revision = tmp - 1;
}
}
}
static void __init rtl839x_read_details(u32 model)
{
u32 chip_info;
sw_w32(0xa << 28, RTL839X_CHIP_INFO);
chip_info = sw_r32(RTL839X_CHIP_INFO);
soc_info.cpu = chip_info & 0xffff;
soc_info.revision = (model >> 1) & 0x1f;
if (!(model & 0x3e))
soc_info.testchip = true;
}
static void __init rtl93xx_read_details(u32 model)
{
u32 chip_info;
sw_w32(0xa << 16, RTL93XX_CHIP_INFO);
chip_info = sw_r32(RTL93XX_CHIP_INFO);
soc_info.cpu = chip_info & 0xffff;
soc_info.revision = model & 0xf;
if (model & 0x30)
soc_info.testchip = true;
}
static void __init rtl96xx_read_details(u32 model)
{
u32 chip_info, chip_subtype;
sw_w32(0xa << 28, RTL96XX_CHIP_INFO);
chip_info = sw_r32(RTL96XX_CHIP_INFO);
soc_info.cpu = chip_info & 0xffff;
sw_w32(0xb << 28, RTL96XX_CHIP_SUB_INFO);
chip_subtype = sw_r32(RTL96XX_CHIP_SUB_INFO);
soc_info.subtype = chip_subtype & 0x1f;
soc_info.revision = model & 0xf;
}
static u32 __init read_model(void)
{
u32 model, id;
model = sw_r32(RTL838X_MODEL_NAME_INFO);
id = model >> 16 & 0xffff;
if ((id >= 0x8380 && id <= 0x8382) || id == 0x8330 || id == 0x8332) {
soc_info.id = id;
soc_info.family = RTL8380_FAMILY_ID;
soc_info.cpu_port = RTL838X_CPU_PORT;
apply_early_quirks();
rtl838x_read_details(model);
return model;
}
model = sw_r32(RTL839X_MODEL_NAME_INFO);
id = model >> 16 & 0xffff;
if ((id >= 0x8391 && id <= 0x8396) || (id >= 0x8351 && id <= 0x8353)) {
soc_info.id = id;
soc_info.family = RTL8390_FAMILY_ID;
soc_info.cpu_port = RTL839X_CPU_PORT;
apply_early_quirks();
rtl839x_read_details(model);
return model;
}
model = sw_r32(RTL93XX_MODEL_NAME_INFO);
id = model >> 16 & 0xffff;
if (id >= 0x9301 && id <= 0x9303) {
soc_info.id = id;
soc_info.family = RTL9300_FAMILY_ID;
soc_info.cpu_port = RTL930X_CPU_PORT;
apply_early_quirks();
rtl93xx_read_details(model);
return model;
} else if (id >= 0x9311 && id <= 0x9313) {
soc_info.id = id;
soc_info.family = RTL9310_FAMILY_ID;
soc_info.cpu_port = RTL931X_CPU_PORT;
apply_early_quirks();
rtl93xx_read_details(model);
return model;
}
model = sw_r32(RTL96XX_MODEL_NAME_INFO);
id = model >> 16 & 0xffff;
if (id == 0x9607) {
soc_info.id = id;
soc_info.family = RTL9607_FAMILY_ID;
soc_info.cpu_port = RTL9607_CPU_PORT;
rtl96xx_read_details(model);
return model;
}
return 0;
}
static void __init parse_model(u32 model)
{
int val;
char suffix = 0;
val = (model >> 11) & 0x1f;
if (val > 0 && val <= 26)
suffix = 'A' + (val - 1);
snprintf(soc_info.name, sizeof(soc_info.name), "RTL%04X%c",
soc_info.id, suffix);
}
static void __init set_system_type(void)
{
char revision = '?';
char *es = "";
char subtype[12] = "";
if (soc_info.revision >= 0 && soc_info.revision < 26)
revision = 'A' + soc_info.revision;
if (soc_info.testchip)
es = " ES";
if (soc_info.subtype)
snprintf(subtype, sizeof(subtype), " subtype %02X", soc_info.subtype);
snprintf(soc_info.system_type, sizeof(soc_info.system_type),
"Realtek %s%s%s rev %c (%04X)",
soc_info.name, es, subtype, revision, soc_info.cpu);
}
static void get_system_memory(void)
{
unsigned int dcr, bits;
if (soc_info.family == RTL9310_FAMILY_ID) {
dcr = soc_r32(RTL931X_DRAM_CONFIG);
bits = (dcr >> 12) + ((dcr >> 6) & 0x3f) + (dcr & 0x3f);
} else {
dcr = soc_r32(RTL83XX_DRAM_CONFIG);
bits = ((dcr >> 28) & 0x3) + ((dcr >> 24) & 0x3) +
((dcr >> 20) & 0xf) + ((dcr >> 16) & 0xf) + 20;
}
soc_info.memory_size = 1 << bits;
}
static void prepare_highmem(void)
{
if (soc_info.family != RTL9300_FAMILY_ID &&
soc_info.family != RTL9607_FAMILY_ID)
return;
if ((soc_info.memory_size <= 256 * 1024 * 1024) ||
!IS_ENABLED(CONFIG_HIGHMEM))
return;
/*
* The RTL930x provides 3 logical adressing zones that can be configured individually
* and offer an additional memory access indirection. Memory is accessed via the OCP
* bus that checks these regions and maps logical addresses to physical ones. They are
*
* zone 1: logical address 0x00000000-0x0fffffff (256 MB) - map register 0xb8004200
* zone 2: logical address 0x10000000-0x13ffffff (64 MB) - map register 0xb8004210
* zone 3: logical address 0x20000000-0x9fffffff (2 GB) - map register 0xb8004220
*
* Whenever CPU accesses memory the normal MIPS translation is applied and afterwards
* the bus adds the zone mapping. E.g. a read to 0x81230000 is converted to an cached
* memory access to logical address 0x01230000. It is issued to the OCP bus and the
* mapping from zone 1 register is added. That allows for two memory topologies:
*
* Linear memory with a maximum of 320 MB:
*
* Zone | map content | logical | physical
* -------------------+-----------------------+-----------------------
* 1 | 0x00000000 | 0x00000000-0x0fffffff | 0x00000000-0x0fffffff
* 2 | 0x00000000 | 0x10000000-0x13ffffff | 0x10000000-0x13ffffff
*
* 256MB low memory plus up to 2GB high memory:
*
* Zone | map content | logical | physical
* -------------------+-----------------------+-----------------------
* 1 | 0x00000000 | 0x00000000-0x0fffffff | 0x00000000-0x0fffffff
* 3 | 0x70000000 | 0x20000000-0x9fffffff | 0x10000000-0x7fffffff
*/
pr_info("highmem kernel on RTL930x with > 256 MB RAM, adapt SoC memory mapping\n");
soc_w32(0, RTL9300_UMSAR0);
soc_w32(0, RTL9300_UMSAR1);
soc_w32(0, RTL9300_UMSAR2);
soc_w32(0, RTL9300_UMSAR3);
soc_w32(0, RTL9300_SRAMSAR0);
soc_w32(0, RTL9300_SRAMSAR1);
soc_w32(0, RTL9300_SRAMSAR2);
soc_w32(0, RTL9300_SRAMSAR3);
__sync();
soc_w32(0x70000000, RTL9300_O0DOR2);
soc_w32(0x7fffffff, RTL9300_O0DMAR2);
__sync();
}
void __init prom_init(void)
{
u32 model = read_model();
parse_model(model);
set_system_type();
get_system_memory();
pr_info("%s SoC with %d MB\n", get_system_type(), soc_info.memory_size >> 20);
prepare_highmem();
/*
* fw_arg2 is be the pointer to the environment. Some devices (e.g. HP JG924A) hand
* over other than expected kernel boot arguments. Something like 0xfffdffff looks
* suspicous. Do extra cleanup for fw_init_cmdline() to avoid a hang during boot.
*/
if (fw_arg2 >= CKSEG2)
fw_arg2 = 0;
fw_init_cmdline();
}