// SPDX-License-Identifier: GPL-2.0 /* * Copyright (c) 2019 Intel Corporation. * William Widjaja */ #include #include #include #include /* ssx register defintion */ #include "ngi_prx300_ssx1.h" #include "ngi_prx300_ssx4.h" #define NONE 0x0 #define M4KEC_ONLY 0x1 #define CPU0 0x2 #define CPU1 0x4 #define CPU2 0x8 #define CPU3 0x10 #define OTHERS 0x20 #define EIP123 0x40 #define IAP (CPU0 | CPU1 | CPU2 | CPU3) #define IAP_N_4KEC (IAP | M4KEC_ONLY) #define GRP0_TO_4 (IAP_N_4KEC | OTHERS) #define ALL_CPUS (GRP0_TO_4 | 0x40) #define SSX_REGSIZE 0x00100000 #define SUB_4KEC_REG_BASE 0x14900000u #define SUB_4KEC_REG_SIZE 0x200 #define AHB_CPU_PAGE_1_OFFSET 0x1D4 #define DDR_REGION_START 0x20000000 #define DDR_REGION_END 0xa0000000 #define MB_SHIFT 20 struct ngi_dts_cfg { u32 memory_size; }; static int read_dts_cfg(struct ngi_dts_cfg *cfg) { struct device_node *node; struct property *prop; int prop_len; const __be32 *iter; int ret = -EINVAL; node = of_find_node_by_path("/avm,ngi-protection"); if (!node) goto out; prop = of_find_property(node, "ram-size", &prop_len); if (!prop) goto put_node; iter = of_prop_next_u32(prop, NULL, &cfg->memory_size); if (!iter) goto put_node; cfg->memory_size <<= MB_SHIFT; ret = 0; put_node: of_node_put(node); out: return ret; } struct ddr_region { u32 base_addr; u32 size_log2; u32 rperm; u32 wperm; const char *reason; }; static struct ddr_region ddr_region_list[REG0_TDDR_PM_TDDR_PM_NREG]; static size_t ddr_region_idx; static size_t bootcore_region_idx; static int get_ddr_fmt1(u32 *base, u32 *size_log2) { u32 fmt1; void __iomem *sub_4kec_base; sub_4kec_base = ioremap_nocache(SUB_4KEC_REG_BASE, SUB_4KEC_REG_SIZE); if (NULL == sub_4kec_base) { pr_err("prx300-ngi: Error ioremap sub 4kec!"); return -ENOMEM; } fmt1 = __raw_readl(sub_4kec_base + AHB_CPU_PAGE_1_OFFSET); iounmap(sub_4kec_base); *base = (fmt1 & 0x0000FFFF) << 16; switch ((fmt1 & 0xFFFF0000) >> 16) { case 0xf800: pr_err("prx300-ngi: TEP Memory: 128MiB@0x%08x\n", *base); *size_log2 = ilog2(128 * 1024 * 1024); break; case 0xfc00: pr_err("prx300-ngi: TEP Memory: 64MiB@0x%08x\n", *base); *size_log2 = ilog2(64 * 1024 * 1024); break; case 0xfe00: pr_err("prx300-ngi: TEP Memory: 32MiB@0x%08x\n", *base); *size_log2 = ilog2(32 * 1024 * 1024); break; case 0xff00: pr_err("prx300-ngi: TEP Memory: 16MiB@0x%08x\n", *base); *size_log2 = ilog2(16 * 1024 * 1024); break; default: pr_err("prx300-ngi: Unsupported fmt1 mask!\n"); } return 0; } static int protect_bootcore(void) { struct ddr_region *region; int r; bootcore_region_idx = ddr_region_idx++; region = &ddr_region_list[bootcore_region_idx]; r = get_ddr_fmt1(®ion->base_addr, ®ion->size_log2); if (r) return r; region->rperm = M4KEC_ONLY | EIP123; region->wperm = M4KEC_ONLY | EIP123; region->reason = "bootcore"; return 0; } static int protect_unattached(struct ngi_dts_cfg *cfg) { u32 ram_base, ram_size, unattached_base, unattached_size; ram_base = DDR_REGION_START; ram_size = cfg->memory_size; pr_info("prx300-ngi: Found attached RAM at [0x%08x, 0x%08x)\n", ram_base, ram_base + ram_size); unattached_base = ram_base + ram_size; unattached_size = DDR_REGION_END - unattached_base; while (unattached_size && ddr_region_idx < REG0_TDDR_PM_TDDR_PM_NREG) { struct ddr_region *region = &ddr_region_list[ddr_region_idx++]; int max_align = unattached_base ? __ffs(unattached_base) : 31; region->base_addr = unattached_base; region->size_log2 = min(ilog2(unattached_size), max_align); region->rperm = 0; region->wperm = 0; region->reason = "unattached"; unattached_size -= 1 << region->size_log2; unattached_base += 1 << region->size_log2; } if (unattached_size) return -EINVAL; return 0; } static u32 permission_wr(void __iomem *base, u32 offset, u32 val) { __raw_writel(val, (volatile void __iomem *)(base + offset)); return 0; } #define MATCH_ADDR_MASK 0xFFFF0000 #define MATCH_SIZE_MASK 0x0000FFF8 #define MATCH_SIZE_SHIFT 3 #define MATCH_SIZE_START_OFFSET 9 #define MATCH_ADDR_SPACE_MASK 0x7 static void __iomem *ssx1_base; static void __iomem *ssx4_base; static void write_ddr_protection(size_t idx_start, size_t idx_end) { size_t i; for (i = idx_start; i < idx_end; ++i) { struct ddr_region *region = &ddr_region_list[i]; u32 offset = REG0_TDDR_PM_TDDR_PM_BASE + i * SSX1_OFFSET_REGISTER; u32 size_part, addr_space, match; u32 size = 1 << region->size_log2; u32 end = region->base_addr + size; if (region->rperm != ALL_CPUS || region->wperm != ALL_CPUS) pr_info("prx300-ngi: Protecting [0x%08x, 0x%08x) (%s)\n", region->base_addr, end, region->reason ? region->reason : "unknown"); size_part = region->size_log2 ? region->size_log2 - MATCH_SIZE_START_OFFSET : 0; size_part <<= MATCH_SIZE_SHIFT; addr_space = size_part ? 1 : 0; match = (region->base_addr & MATCH_ADDR_MASK) | (size_part & MATCH_SIZE_MASK) | (addr_space & MATCH_ADDR_SPACE_MASK); permission_wr(ssx1_base, offset + SSX1_OFFSET_ADDR_MATCH, match); permission_wr(ssx1_base, offset + SSX1_OFFSET_READ_PERMISSION, region->rperm); permission_wr(ssx1_base, offset + SSX1_OFFSET_WRITE_PERMISSION, region->wperm); } } void ngi_surrender_eip123(void) { /* lock eip123 out from bootcore mem */ ddr_region_list[bootcore_region_idx].rperm = M4KEC_ONLY; ddr_region_list[bootcore_region_idx].wperm = M4KEC_ONLY; write_ddr_protection(bootcore_region_idx, bootcore_region_idx+1); /* allow access to eip123 from interaptive */ permission_wr(ssx1_base, REG0_TE123_PM_TE123_PM_READ_PERMISSION_0, IAP); permission_wr(ssx1_base, REG0_TE123_PM_TE123_PM_WRITE_PERMISSION_0, IAP); /* prevent ourselves from changing the config back */ permission_wr(ssx1_base, REG0_TREG0_PM_TREG0_PM_WRITE_PERMISSION_0, NONE); permission_wr(ssx1_base, REG0_TREG0_PM_TREG0_PM_WRITE_PERMISSION_1, NONE); permission_wr(ssx4_base, REG4_TREG4_PM_TREG4_PM_WRITE_PERMISSION_0, NONE); permission_wr(ssx4_base, REG4_TREG4_PM_TREG4_PM_WRITE_PERMISSION_1, NONE); } EXPORT_SYMBOL(ngi_surrender_eip123); void set_permission(void) { size_t i; struct ngi_dts_cfg cfg; for (i = 0; i < REG0_TDDR_PM_TDDR_PM_NREG; ++i) { struct ddr_region region = { .base_addr = 0x0, .size_log2 = 0x0, .rperm = ALL_CPUS, .wperm = ALL_CPUS, .reason = NULL, }; ddr_region_list[i] = region; } ddr_region_idx = 1; if (read_dts_cfg(&cfg)) panic("prx300-ngi: Failed to read the device tree node\n"); if (protect_bootcore()) panic("prx300-ngi: Failed to protect the bootcore\n"); if (protect_unattached(&cfg)) panic("prx300-ngi: Failed to protect unattached memory\n"); ssx1_base = ioremap_nocache(SSX1_MODULE_BASE, SSX_REGSIZE); if (NULL == ssx1_base) { pr_err("prx300-ngi: Error ioremap sxx1!"); return; } ssx4_base = ioremap_nocache(SSX4_SHARED_LINK_MODULE_BASE, SSX_REGSIZE); if (NULL == ssx4_base) { pr_err("prx300-ngi: Error ioremap sxx4!"); return; } write_ddr_protection(0, REG0_TDDR_PM_TDDR_PM_NREG); /* SSX1 Registers */ permission_wr(ssx1_base, REG0_TREG0_PM_TREG0_PM_READ_PERMISSION_0, M4KEC_ONLY); permission_wr(ssx1_base, REG0_TREG0_PM_TREG0_PM_WRITE_PERMISSION_0, M4KEC_ONLY); permission_wr(ssx1_base, REG0_TOTP_PM_TOTP_PM_READ_PERMISSION_0, NONE); permission_wr(ssx1_base, REG0_TOTP_PM_TOTP_PM_WRITE_PERMISSION_0, GRP0_TO_4); permission_wr(ssx1_base, REG0_TCBM1_PM_TCBM1_PM_READ_PERMISSION_0, GRP0_TO_4); permission_wr(ssx1_base, REG0_TCBM1_PM_TCBM1_PM_WRITE_PERMISSION_0, GRP0_TO_4); permission_wr(ssx1_base, REG0_TCBM2_PM_TCBM2_PM_READ_PERMISSION_0, GRP0_TO_4); permission_wr(ssx1_base, REG0_TCBM2_PM_TCBM2_PM_WRITE_PERMISSION_0, GRP0_TO_4); permission_wr(ssx1_base, REG0_TE123_PM_TE123_PM_READ_PERMISSION_0, M4KEC_ONLY); permission_wr(ssx1_base, REG0_TE123_PM_TE123_PM_WRITE_PERMISSION_0, M4KEC_ONLY); permission_wr(ssx1_base, REG0_TDMA3_PM_TDM3_PM_READ_PERMISSION_0, GRP0_TO_4); permission_wr(ssx1_base, REG0_TDMA3_PM_TDM3_PM_WRITE_PERMISSION_0, GRP0_TO_4); permission_wr(ssx1_base, REG0_TDMAT1_PM_TDMAT1_PM_READ_PERMISSION_0, GRP0_TO_4); permission_wr(ssx1_base, REG0_TDMAT1_PM_TDMAT1_PM_WRITE_PERMISSION_0, GRP0_TO_4); permission_wr(ssx1_base, REG0_TEX04_PM_TEX04_PM_READ_PERMISSION_0, GRP0_TO_4); permission_wr(ssx1_base, REG0_TEX04_PM_TEX04_PM_WRITE_PERMISSION_0, GRP0_TO_4); permission_wr(ssx1_base, REG0_TPCTL_PM_TPCTL_PM_READ_PERMISSION_0, GRP0_TO_4); permission_wr(ssx1_base, REG0_TPCTL_PM_TPCTL_PM_WRITE_PERMISSION_0, GRP0_TO_4); permission_wr(ssx1_base, REG0_TPUB_PM_TPUB_PM_READ_PERMISSION_0, GRP0_TO_4); permission_wr(ssx1_base, REG0_TPUB_PM_TPUB_PM_WRITE_PERMISSION_0, GRP0_TO_4); permission_wr(ssx1_base, REG0_TPON_PM_TPON_PM_READ_PERMISSION_0, GRP0_TO_4); permission_wr(ssx1_base, REG0_TPON_PM_TPON_PM_WRITE_PERMISSION_0, GRP0_TO_4); permission_wr(ssx1_base, REG0_TGSWIP_PM_TGSWIP_PM_READ_PERMISSION_0, GRP0_TO_4); permission_wr(ssx1_base, REG0_TGSWIP_PM_TGSWIP_PM_WRITE_PERMISSION_0, GRP0_TO_4); permission_wr(ssx1_base, REG0_TQSPIC_PM_TQSPIC_PM_READ_PERMISSION_0, GRP0_TO_4); permission_wr(ssx1_base, REG0_TQSPIC_PM_TQSPIC_PM_WRITE_PERMISSION_0, GRP0_TO_4); permission_wr(ssx1_base, REG0_TMSI_PM_TMSI1_PM_READ_PERMISSION_0, GRP0_TO_4); permission_wr(ssx1_base, REG0_TMSI_PM_TMSI1_PM_WRITE_PERMISSION_0, GRP0_TO_4); permission_wr(ssx1_base, REG0_TDMAT2_PM_TDMAT2_PM_READ_PERMISSION_0, GRP0_TO_4); permission_wr(ssx1_base, REG0_TDMAT2_PM_TDMAT2_PM_WRITE_PERMISSION_0, GRP0_TO_4); permission_wr(ssx1_base, REG0_TQSPID_PM_TQSPID_PM_READ_PERMISSION_0, GRP0_TO_4); permission_wr(ssx1_base, REG0_TQSPID_PM_TQSPID_PM_WRITE_PERMISSION_0, GRP0_TO_4); permission_wr(ssx1_base, REG0_TDMAR1_PM_TDMAR1_PM_READ_PERMISSION_0, GRP0_TO_4); permission_wr(ssx1_base, REG0_TDMAR1_PM_TDMAR1_PM_WRITE_PERMISSION_0, GRP0_TO_4); permission_wr(ssx1_base, REG0_TDMAR2_PM_TDMAR2_PM_READ_PERMISSION_0, GRP0_TO_4); permission_wr(ssx1_base, REG0_TDMAR2_PM_TDMAR2_PM_WRITE_PERMISSION_0, GRP0_TO_4); permission_wr(ssx1_base, REG0_TACA_PM_TACA_PM_READ_PERMISSION_0, GRP0_TO_4); permission_wr(ssx1_base, REG0_TACA_PM_TACA_PM_WRITE_PERMISSION_0, GRP0_TO_4); permission_wr(ssx1_base, REG0_TPCIE_CR1_PM_TPCIE_CR1_PM_READ_PERMISSION_0, GRP0_TO_4); permission_wr(ssx1_base, REG0_TPCIE_CR1_PM_TPCIE_CR1_PM_WRITE_PERMISSION_0, GRP0_TO_4); permission_wr(ssx1_base, REG0_TPCI2_PM_TPCI2_PM_READ_PERMISSION_0, GRP0_TO_4); permission_wr(ssx1_base, REG0_TPCI2_PM_TPCI2_PM_WRITE_PERMISSION_0, GRP0_TO_4); permission_wr(ssx1_base, REG0_TPCI1_PM_TPCI1_PM_READ_PERMISSION_0, GRP0_TO_4); permission_wr(ssx1_base, REG0_TPCI1_PM_TPCI1_PM_WRITE_PERMISSION_0, GRP0_TO_4); permission_wr(ssx1_base, REG0_TMSI2_PM_TMSI2_PM_READ_PERMISSION_0, GRP0_TO_4); permission_wr(ssx1_base, REG0_TMSI2_PM_TMSI2_PM_WRITE_PERMISSION_0, GRP0_TO_4); permission_wr(ssx1_base, REG0_TPCIE_CR2_PM_TPCIE_CR2_PM_READ_PERMISSION_0, GRP0_TO_4); permission_wr(ssx1_base, REG0_TPCIE_CR2_PM_TPCIE_CR2_PM_WRITE_PERMISSION_0, GRP0_TO_4); permission_wr(ssx1_base, REG0_TLNPPV4_PM_TLNPPV4_PM_READ_PERMISSION_0, GRP0_TO_4); permission_wr(ssx1_base, REG0_TLNPPV4_PM_TLNPPV4_PM_WRITE_PERMISSION_0, GRP0_TO_4); permission_wr(ssx1_base, REG0_TXPCS_PM_TXPCS_PM_READ_PERMISSION_0, GRP0_TO_4); permission_wr(ssx1_base, REG0_TXPCS_PM_TXPCS_PM_WRITE_PERMISSION_0, GRP0_TO_4); permission_wr(ssx1_base, REG0_TPC1DBI_PM_TPCI1DBI_PM_READ_PERMISSION_0, GRP0_TO_4); permission_wr(ssx1_base, REG0_TPC1DBI_PM_TPCI1DBI_PM_WRITE_PERMISSION_0, GRP0_TO_4); permission_wr(ssx1_base, REG0_TPC2DBI_PM_TPCI2DBI_PM_READ_PERMISSION_0, GRP0_TO_4); permission_wr(ssx1_base, REG0_TPC2DBI_PM_TPCI2DBI_PM_WRITE_PERMISSION_0, GRP0_TO_4); permission_wr(ssx1_base, REG0_TSSB_PM_TSSB_PM_READ_PERMISSION_0, GRP0_TO_4); permission_wr(ssx1_base, REG0_TSSB_PM_TSSB_PM_WRITE_PERMISSION_0, GRP0_TO_4); permission_wr(ssx1_base, REG0_TROM_PM_TROM_PM_READ_PERMISSION_0, GRP0_TO_4); permission_wr(ssx1_base, REG0_TROM_PM_TROM_PM_WRITE_PERMISSION_0, GRP0_TO_4); permission_wr(ssx1_base, REG0_TMACS_PM_TMAC_PM_READ_PERMISSION_0, GRP0_TO_4); permission_wr(ssx1_base, REG0_TMACS_PM_TMAC_PM_WRITE_PERMISSION_0, GRP0_TO_4); permission_wr(ssx1_base, REG0_TIOCU1_PM_TIOCU1_PM_READ_PERMISSION_0, GRP0_TO_4); permission_wr(ssx1_base, REG0_TIOCU1_PM_TIOCU1_PM_WRITE_PERMISSION_0, GRP0_TO_4); permission_wr(ssx1_base, REG0_TDM4_PM_TDM4_PM_READ_PERMISSION_0, GRP0_TO_4); permission_wr(ssx1_base, REG0_TDM4_PM_TDM4_PM_WRITE_PERMISSION_0, GRP0_TO_4); /* SSX 4 registers */ permission_wr(ssx4_base, REG4_TREG4_PM_TREG4_PM_WRITE_PERMISSION_0, M4KEC_ONLY); permission_wr(ssx4_base, REG4_TREG4_PM_TREG4_PM_READ_PERMISSION_0, M4KEC_ONLY); permission_wr(ssx4_base, REG4_TCGU_PM_TCGU_PM_WRITE_PERMISSION_0, IAP_N_4KEC); permission_wr(ssx4_base, REG4_TCGU_PM_TCGU_PM_READ_PERMISSION_0, IAP_N_4KEC); permission_wr(ssx4_base, REG4_TRCU_PM_TRCU_PM_WRITE_PERMISSION_0, IAP_N_4KEC); permission_wr(ssx4_base, REG4_TRCU_PM_TRCU_PM_READ_PERMISSION_0, IAP_N_4KEC); permission_wr(ssx4_base, REG4_TPMU_PM_TPMU_PM_WRITE_PERMISSION_0, IAP_N_4KEC); permission_wr(ssx4_base, REG4_TPMU_PM_TPMU_PM_READ_PERMISSION_0, IAP_N_4KEC); permission_wr(ssx4_base, REG4_TPCM_PM_TPCM_PM_WRITE_PERMISSION_0, IAP_N_4KEC); permission_wr(ssx4_base, REG4_TPCM_PM_TPCM_PM_READ_PERMISSION_0, IAP_N_4KEC); permission_wr(ssx4_base, REG4_TGPIO_PM_TGPIO_PM_WRITE_PERMISSION_0, IAP_N_4KEC); permission_wr(ssx4_base, REG4_TGPIO_PM_TGPIO_PM_READ_PERMISSION_0, IAP_N_4KEC); permission_wr(ssx4_base, REG4_TLEDC_PM_TLEDC_PM_WRITE_PERMISSION_0, IAP_N_4KEC); permission_wr(ssx4_base, REG4_TLEDC_PM_TLEDC_PM_READ_PERMISSION_0, IAP_N_4KEC); permission_wr(ssx4_base, REG4_TI2C_PM_TI2C0_PM_WRITE_PERMISSION_0, IAP_N_4KEC); permission_wr(ssx4_base, REG4_TI2C_PM_TI2C0_PM_READ_PERMISSION_0, IAP_N_4KEC); permission_wr(ssx4_base, REG4_TASC0_PM_TASC0_PM_WRITE_PERMISSION_0, IAP_N_4KEC); permission_wr(ssx4_base, REG4_TASC0_PM_TASC0_PM_READ_PERMISSION_0, IAP_N_4KEC); permission_wr(ssx4_base, REG4_TASC1_PM_TASC1_PM_WRITE_PERMISSION_0, IAP_N_4KEC); permission_wr(ssx4_base, REG4_TASC1_PM_TASC1_PM_READ_PERMISSION_0, IAP_N_4KEC); permission_wr(ssx4_base, REG4_TGPT0_PM_TGPT0_PM_WRITE_PERMISSION_0, IAP_N_4KEC); permission_wr(ssx4_base, REG4_TGPT0_PM_TGPT0_PM_READ_PERMISSION_0, IAP_N_4KEC); permission_wr(ssx4_base, REG4_TSSC0_PM_TSSC0_PM_WRITE_PERMISSION_0, IAP_N_4KEC); permission_wr(ssx4_base, REG4_TSSC0_PM_TSSC0_PM_READ_PERMISSION_0, IAP_N_4KEC); permission_wr(ssx4_base, REG4_TSSC1_PM_TSSC1_PM_WRITE_PERMISSION_0, IAP_N_4KEC); permission_wr(ssx4_base, REG4_TSSC1_PM_TSSC1_PM_READ_PERMISSION_0, IAP_N_4KEC); permission_wr(ssx4_base, REG4_TDMA0_PM_TDM0_PM_WRITE_PERMISSION_0, IAP_N_4KEC); permission_wr(ssx4_base, REG4_TDMA0_PM_TDM0_PM_READ_PERMISSION_0, IAP_N_4KEC); permission_wr(ssx4_base, REG4_TGPT1_PM_TGPT1_PM_WRITE_PERMISSION_0, IAP_N_4KEC); permission_wr(ssx4_base, REG4_TGPT1_PM_TGPT1_PM_READ_PERMISSION_0, IAP_N_4KEC); permission_wr(ssx4_base, REG4_TGPT2_PM_TGPT2_PM_WRITE_PERMISSION_0, IAP_N_4KEC); permission_wr(ssx4_base, REG4_TGPT2_PM_TGPT2_PM_READ_PERMISSION_0, IAP_N_4KEC); permission_wr(ssx4_base, REG4_TI2C0_PM_TI2C1_PM_WRITE_PERMISSION_0, IAP_N_4KEC); permission_wr(ssx4_base, REG4_TI2C0_PM_TI2C1_PM_READ_PERMISSION_0, IAP_N_4KEC); permission_wr(ssx4_base, REG4_TI2C1_PM_TI2C2_PM_WRITE_PERMISSION_0, IAP_N_4KEC); permission_wr(ssx4_base, REG4_TI2C1_PM_TI2C2_PM_READ_PERMISSION_0, IAP_N_4KEC); return; } static int prx300_ngi_probe(struct platform_device *pdev) { set_permission(); return 0; } static const struct of_device_id prx300_ngi_match[] = { { .compatible = "intel,prx300-ngi" }, {}, }; MODULE_DEVICE_TABLE(of, prx300_ngi_match); static struct platform_driver prx300_ngi_driver = { .probe = prx300_ngi_probe, .driver = { .name = "prx300-ngi", .owner = THIS_MODULE, .of_match_table = prx300_ngi_match, }, }; int __init prx300_ngi_init(void) { int ret; ret = platform_driver_register(&prx300_ngi_driver); if (ret) pr_info("prx300-ngi: Error registering platform driver!"); return ret; } core_initcall(prx300_ngi_init);