diff --git a/Makefile b/Makefile index 5ada7a245..c54150ed8 100644 --- a/Makefile +++ b/Makefile @@ -103,6 +103,7 @@ OBJECTS := \ i2c.o \ iodev.o \ iova.o \ + isp.o \ kboot.o \ main.o \ mcc.o \ diff --git a/src/adt.h b/src/adt.h index 6373c8682..5f717978b 100644 --- a/src/adt.h +++ b/src/adt.h @@ -106,4 +106,13 @@ bool adt_is_compatible(const void *adt, int nodeoffset, const char *compat); for (const struct adt_property *prop = ADT_PROP(adt, _poff); _prop_count--; \ prop = ADT_PROP(adt, _poff = adt_next_property_offset(adt, _poff))) +/* Common ADT properties */ +struct adt_segment_ranges { + u64 phys; + u64 iova; + u64 remap; + u32 size; + u32 unk; +} PACKED; + #endif diff --git a/src/dapf.c b/src/dapf.c index cbeb576f2..332a1805b 100644 --- a/src/dapf.c +++ b/src/dapf.c @@ -5,6 +5,7 @@ #include "assert.h" #include "malloc.h" #include "memory.h" +#include "pmgr.h" #include "string.h" #include "utils.h" @@ -83,7 +84,7 @@ static int dapf_init_t8110(const char *path, u64 base, int node) return 0; } -int dapf_init(const char *path) +int dapf_init(const char *path, int index) { int ret; int dart_path[8]; @@ -93,8 +94,14 @@ int dapf_init(const char *path) return -1; } + u32 pwr; + if (!adt_getprop(adt, node, "clock-gates", &pwr)) + pwr = 0; + if (pwr && (pmgr_adt_power_enable(path) < 0)) + return -1; + u64 base; - if (adt_get_reg(adt, dart_path, "reg", 1, &base, NULL) < 0) { + if (adt_get_reg(adt, dart_path, "reg", index, &base, NULL) < 0) { printf("dapf: Error getting DAPF %s base address.\n", path); return -1; } @@ -110,28 +117,44 @@ int dapf_init(const char *path) return -1; } + if (pwr) + pmgr_adt_power_disable(path); + if (!ret) printf("dapf: Initialized %s\n", path); return ret; } -const char *dapf_paths[] = {"/arm-io/dart-aop", "/arm-io/dart-mtp", "/arm-io/dart-pmp", NULL}; +struct entry { + const char *path; + int index; +}; + +struct entry dapf_entries[] = { + {"/arm-io/dart-aop", 1}, + {"/arm-io/dart-mtp", 1}, + {"/arm-io/dart-pmp", 1}, + {"/arm-io/dart-isp", 5}, + {NULL, -1}, +}; int dapf_init_all(void) { int ret = 0; int count = 0; - for (const char **path = dapf_paths; *path; path++) { - if (adt_path_offset(adt, *path) < 0) + struct entry *entry = dapf_entries; + while (entry->path != NULL) { + if (adt_path_offset(adt, entry->path) < 0) { + entry++; continue; - - if (dapf_init(*path) < 0) { + } + if (dapf_init(entry->path, entry->index) < 0) { ret = -1; } + entry++; count += 1; } - return ret ? ret : count; } diff --git a/src/dapf.h b/src/dapf.h index 2a7e1bf12..0d36d5625 100644 --- a/src/dapf.h +++ b/src/dapf.h @@ -4,6 +4,6 @@ #define DAPF_H int dapf_init_all(void); -int dapf_init(const char *path); +int dapf_init(const char *path, int index); #endif diff --git a/src/isp.c b/src/isp.c new file mode 100644 index 000000000..f0bbc8aac --- /dev/null +++ b/src/isp.c @@ -0,0 +1,270 @@ +/* SPDX-License-Identifier: MIT */ + +#include "adt.h" +#include "dart.h" +#include "firmware.h" +#include "pmgr.h" +#include "soc.h" +#include "utils.h" + +#define ISP_ASC_VERSION 0x1800000 + +#define ISP_VER_T8103 0xb0090 +#define ISP_VER_T6000 0xb3091 +#define ISP_VER_T8112 0xc1090 +#define ISP_VER_T6020 0xc3091 + +// PMGR offset to enable to get the version info to work +#define ISP_PMGR_T8103 0x4018 +#define ISP_PMGR_T6000 0x8 +#define ISP_PMGR_T6020 0x4008 + +/* ISP DART has some quirks we must work around */ + +#define DART_T8020_ENABLED_STREAMS 0xfc +#define DART_T8020_STREAM_COMMAND 0x20 +#define DART_T8020_STREAM_SELECT 0x34 +#define DART_T8020_TCR_OFF 0x100 +#define DART_T8020_TTBR 0x200 + +#define DART_T8020_TCR_TRANSLATE_ENABLE BIT(7) +#define DART_T8020_STREAM_COMMAND_INVALIDATE BIT(20) + +struct dart_tunables { + u64 offset; + u64 clear; + u64 set; +}; + +static void isp_ctrr_init_t8020(u64 base, const struct dart_tunables *config, u32 length) +{ + /* DART error handler gets stuck w/o these */ + write32(base + DART_T8020_ENABLED_STREAMS, 0x1); + write32(base + 0x2f0, 0x0); + write32(base + DART_T8020_STREAM_SELECT, 0xffffffff); + write32(base + DART_T8020_STREAM_COMMAND, DART_T8020_STREAM_COMMAND_INVALIDATE); + + /* I think these lock CTRR? Configurable __TEXT read-only region? */ + int count = length / sizeof(*config); + for (int i = 0; i < count; i++) { + u64 offset = config->offset & 0xffff; + u32 set = config->set & 0xffffffff; + mask32(base + offset, read32(base + offset), set); + config++; + } + + write32(base + DART_T8020_TCR_OFF, DART_T8020_TCR_TRANSLATE_ENABLE); + write32(base + 0x13c, 0x20000); +} + +static void isp_ctrr_init_t6000(u64 base, const struct dart_tunables *config, u32 length, int index) +{ + write32(base + DART_T8020_ENABLED_STREAMS, 0x1); + write32(base + 0x2f0, 0x0); + mask32(base + DART_T8020_STREAM_SELECT, read32(base + DART_T8020_STREAM_SELECT), 0xffff); + // write32(base + DART_T8020_STREAM_SELECT, 0xffff); // diff from t8020 + write32(base + DART_T8020_STREAM_COMMAND, 0x0); + + int count = length / sizeof(*config); + for (int i = 0; i < count; i++) { + u64 offset = config->offset & 0xffff; + u32 set = config->set & 0xffffffff; + mask32(base + offset, read32(base + offset), set); + config++; + } + + write32(base + DART_T8020_TCR_OFF, DART_T8020_TCR_TRANSLATE_ENABLE); + u32 val = 0x20000; + if (!index) + val |= 0x100; + write32(base + 0x13c, val); +} + +static bool isp_initialized = false; +static u64 heap_phys, heap_iova, heap_size, heap_top; + +int isp_get_heap(u64 *phys, u64 *iova, u64 *size, u64 *top) +{ + if (!isp_initialized) + return -1; + + *phys = heap_phys; + *iova = heap_iova; + *size = heap_size; + *top = heap_top; + return 0; +} + +int isp_init(void) +{ + int err = 0; + + const char *isp_path = "/arm-io/isp"; + const char *dart_path = "/arm-io/dart-isp"; + + int adt_path[8], adt_isp_path[8]; + int isp_node = adt_path_offset_trace(adt, isp_path, adt_isp_path); + int node = adt_path_offset_trace(adt, dart_path, adt_path); + if (node < 0 || isp_node < 0) { + isp_path = "/arm-io/isp0"; + dart_path = "/arm-io/dart-isp0"; + isp_node = adt_path_offset_trace(adt, isp_path, adt_isp_path); + node = adt_path_offset_trace(adt, dart_path, adt_path); + } + if (node < 0) + return 0; + + if (pmgr_adt_power_enable(isp_path) < 0) + return -1; + + u64 isp_base; + u64 pmgr_base; + err = adt_get_reg(adt, adt_isp_path, "reg", 0, &isp_base, NULL); + if (err) + return err; + + err = adt_get_reg(adt, adt_isp_path, "reg", 1, &pmgr_base, NULL); + if (err) + return err; + + u32 pmgr_off; + switch (chip_id) { + case T8103: + case T8112: + pmgr_off = ISP_PMGR_T8103; + break; + case T6000 ... T6002: + pmgr_off = ISP_PMGR_T6000; + break; + case T6020 ... T6022: + pmgr_off = ISP_PMGR_T6020; + break; + default: + printf("isp: Unsupported SoC\n"); + return -1; + } + + err = pmgr_set_mode(pmgr_base + pmgr_off, PMGR_PS_ACTIVE); + if (err) { + printf("isp: Failed to power on\n"); + return err; + } + + u32 ver_rev = read32(isp_base + ISP_ASC_VERSION); + printf("isp: Version 0x%x\n", ver_rev); + + pmgr_set_mode(pmgr_base + pmgr_off, PMGR_PS_PWRGATE); + + /* TODO: confirm versions */ + switch (ver_rev) { + case ISP_VER_T8103: + case ISP_VER_T8112: + switch (os_firmware.version) { + case V12_3 ... V12_4: + heap_top = 0x1800000; + break; + case V13_5: + heap_top = 0x1000000; + break; + default: + printf("isp: unsupported firmware\n"); + return -1; + } + break; + case ISP_VER_T6000: + switch (os_firmware.version) { + case V12_3: + heap_top = 0xe00000; + break; + case V13_5: + heap_top = 0xf00000; + break; + default: + printf("isp: unsupported firmware\n"); + return -1; + } + break; + case ISP_VER_T6020: + switch (os_firmware.version) { + case V13_5: + heap_top = 0xf00000; + break; + default: + printf("isp: unsupported firmware\n"); + return -1; + } + break; + default: + printf("isp: unknown revision 0x%x\n", ver_rev); + return -1; + } + + const struct adt_segment_ranges *seg; + u32 segments_len; + + seg = adt_getprop(adt, isp_node, "segment-ranges", &segments_len); + unsigned int count = segments_len / sizeof(*seg); + + heap_iova = seg[count - 1].iova + seg[count - 1].size; + heap_size = heap_top - heap_iova; + heap_phys = top_of_memory_alloc(heap_size); + + printf("isp: Heap: 0x%lx..0x%lx (0x%lx @ 0x%lx)\n", heap_iova, heap_top, heap_size, heap_phys); + + enum dart_type_t type; + const char *type_s; + if (adt_is_compatible(adt, node, "dart,t8020")) { + type = DART_T8020; + type_s = "t8020"; + } else if (adt_is_compatible(adt, node, "dart,t6000")) { + type = DART_T6000; + type_s = "t6000"; + } else if (adt_is_compatible(adt, node, "dart,t8110")) { + type = DART_T8110; + type_s = "t8110"; + } else { + printf("isp: dart %s is of an unknown type\n", dart_path); + return -1; + } + + int dart_domain_count = 3; // TODO get from dt + for (int index = 0; index < dart_domain_count; index++) { + u64 base; + err = adt_get_reg(adt, adt_path, "reg", index, &base, NULL); + if (err < 0) + goto out; + + u32 length; + char prop[32] = "dart-tunables-instance"; + snprintf(prop, sizeof(prop), "dart-tunables-instance-%u", index); + const struct dart_tunables *config = adt_getprop(adt, node, prop, &length); + if (!config || !length) { + printf("isp: Error getting ADT node %s property %s.\n", isp_path, prop); + err = -1; + goto out; + } + + err = adt_get_reg(adt, adt_path, "reg", index, &base, NULL); + if (err < 0) + goto out; + + switch (type) { + case DART_T8020: + isp_ctrr_init_t8020(base, config, length); + break; + case DART_T6000: + isp_ctrr_init_t6000(base, config, length, index); + break; + case DART_T8110: + printf("isp: warning: dart type %s not tested yet!\n", type_s); + isp_ctrr_init_t8020(base, config, length); + break; + } + } + + isp_initialized = true; + +out: + pmgr_adt_power_disable(isp_path); + return err; +} diff --git a/src/isp.h b/src/isp.h new file mode 100644 index 000000000..f556de63a --- /dev/null +++ b/src/isp.h @@ -0,0 +1,11 @@ +/* SPDX-License-Identifier: MIT */ + +#ifndef ISP_H +#define ISP_H + +#include "types.h" + +int isp_init(void); +int isp_get_heap(u64 *phys, u64 *iova, u64 *size, u64 *top); + +#endif diff --git a/src/kboot.c b/src/kboot.c index e37499d07..264068690 100644 --- a/src/kboot.c +++ b/src/kboot.c @@ -10,6 +10,7 @@ #include "devicetree.h" #include "exception.h" #include "firmware.h" +#include "isp.h" #include "malloc.h" #include "memory.h" #include "pcie.h" @@ -1207,21 +1208,20 @@ static int dt_get_or_add_reserved_mem(const char *node_name, const char *compat, bail("FDT: '/reserved-memory' not found\n"); int node = fdt_subnode_offset(dt, resv_node, node_name); - if (node >= 0) - return node; - - node = fdt_add_subnode(dt, resv_node, node_name); - if (node < 0) - bail("FDT: failed to add node '%s' to '/reserved-memory'\n", node_name); + if (node < 0) { + node = fdt_add_subnode(dt, resv_node, node_name); + if (node < 0) + bail("FDT: failed to add node '%s' to '/reserved-memory'\n", node_name); - uint32_t phandle; - ret = fdt_generate_phandle(dt, &phandle); - if (ret) - bail("FDT: failed to generate phandle: %d\n", ret); + uint32_t phandle; + ret = fdt_generate_phandle(dt, &phandle); + if (ret) + bail("FDT: failed to generate phandle: %d\n", ret); - ret = fdt_setprop_u32(dt, node, "phandle", phandle); - if (ret != 0) - bail("FDT: couldn't set '%s.phandle' property: %d\n", node_name, ret); + ret = fdt_setprop_u32(dt, node, "phandle", phandle); + if (ret != 0) + bail("FDT: couldn't set '%s.phandle' property: %d\n", node_name, ret); + } u64 reg[2] = {cpu_to_fdt64(paddr), cpu_to_fdt64(size)}; ret = fdt_setprop(dt, node, "reg", reg, sizeof(reg)); @@ -1516,7 +1516,7 @@ static int dt_vram_reserved_region(const char *dcp_alias, const char *disp_alias disp_reserved_regions_vram, ®ion, 1); } -static int dt_reserve_asc_firmware(const char *adt_path, const char *fdt_path) +static int dt_reserve_asc_firmware(const char *adt_path, const char *fdt_path, bool remap) { int ret = 0; @@ -1539,33 +1539,32 @@ static int dt_reserve_asc_firmware(const char *adt_path, const char *fdt_path) bail("FDT: couldn't set '%s.phandle' property: %d\n", fdt_path, ret); } - const uint64_t *segments; + const struct adt_segment_ranges *seg; u32 segments_len; - segments = adt_getprop(adt, node, "segment-ranges", &segments_len); - unsigned int num_maps = segments_len / 32; + seg = adt_getprop(adt, node, "segment-ranges", &segments_len); + unsigned int num_maps = segments_len / sizeof(*seg); for (unsigned i = 0; i < num_maps; i++) { - u64 paddr = segments[0]; - u64 iova = segments[2]; - u32 size = segments[3]; - segments += 4; + u64 iova = remap ? seg->remap : seg->iova; char node_name[64]; - snprintf(node_name, sizeof(node_name), "asc-firmware@%lx", paddr); + snprintf(node_name, sizeof(node_name), "asc-firmware@%lx", seg->phys); - int mem_node = dt_get_or_add_reserved_mem(node_name, "apple,asc-mem", paddr, size); + int mem_node = dt_get_or_add_reserved_mem(node_name, "apple,asc-mem", seg->phys, seg->size); if (mem_node < 0) return ret; uint32_t mem_phandle = fdt_get_phandle(dt, mem_node); - ret = dt_device_set_reserved_mem(mem_node, node_name, dev_phandle, iova, size); + ret = dt_device_set_reserved_mem(mem_node, node_name, dev_phandle, iova, seg->size); if (ret < 0) return ret; ret = dt_device_add_mem_region(fdt_path, mem_phandle, NULL); if (ret < 0) return ret; + + seg++; } return 0; @@ -1791,6 +1790,68 @@ static int dt_set_sio_fwdata(void) return 0; } +static int dt_set_isp_fwdata(void) +{ + const char *fdt_path = "isp"; + int ret = 0; + + u64 phys, iova, size, top; + + int fdt_node = fdt_path_offset(dt, fdt_path); + if (fdt_node < 0) { + printf("FDT: '%s' not found\n", fdt_path); + return 0; + } + + if (isp_get_heap(&phys, &iova, &size, &top)) { + const char *status = fdt_getprop(dt, fdt_node, "status", NULL); + + if (!status || strcmp(status, "disabled")) { + printf("FDT: ISP enabled but not initialized, disabling\n"); + if (fdt_setprop_string(dt, fdt_node, "status", "disabled") < 0) + bail("FDT: failed to set status property of ISP\n"); + } + + return 0; + } + + int adt_node = adt_path_offset(adt, "/arm-io/isp"); + if (adt_node < 0) + adt_node = adt_path_offset(adt, "/arm-io/isp0"); + if (adt_node < 0) + return 0; + + uint32_t dev_phandle = fdt_get_phandle(dt, fdt_node); + if (!dev_phandle) { + ret = fdt_generate_phandle(dt, &dev_phandle); + if (!ret) + ret = fdt_setprop_u32(dt, fdt_node, "phandle", dev_phandle); + if (ret != 0) + bail("FDT: couldn't set '%s.phandle' property: %d\n", fdt_path, ret); + } + + int mem_node = dt_get_or_add_reserved_mem("isp-heap", "apple,asc-mem", phys, size); + if (mem_node < 0) + return ret; + uint32_t mem_phandle = fdt_get_phandle(dt, mem_node); + + ret = dt_device_set_reserved_mem(mem_node, "isp-heap", dev_phandle, iova, size); + if (ret < 0) + bail("FDT: couldn't set 'isp-heap' reserved mem: %d\n", ret); + + ret = dt_device_add_mem_region(fdt_path, mem_phandle, NULL); + if (ret < 0) + bail("FDT: couldn't add 'isp-heap' reserved mem: %d\n", ret); + + fdt_node = fdt_path_offset(dt, fdt_path); + if (fdt_node < 0) + return fdt_node; + if (fdt_appendprop_u64(dt, fdt_node, "apple,isp-ctrr-size", top)) + bail("FDT: couldn't append to 'apple,isp-ctrr-size'\n"); + + return 0; +} + static int dt_disable_missing_devs(const char *adt_prefix, const char *dt_prefix, int max_devs) { int ret = -1; @@ -2052,6 +2113,9 @@ int kboot_prepare_dt(void *fdt) dt = NULL; } + /* Need to init ISP early to carve out heap */ + isp_init(); + dt_bufsize = fdt_totalsize(fdt); assert(dt_bufsize); @@ -2097,10 +2161,14 @@ int kboot_prepare_dt(void *fdt) return -1; if (dt_disable_missing_devs("i2c", "i2c@", 8)) return -1; - if (dt_reserve_asc_firmware("/arm-io/sio", "sio")) + if (dt_reserve_asc_firmware("/arm-io/sio", "sio", true)) return -1; if (dt_set_sio_fwdata()) return -1; + if (dt_reserve_asc_firmware("/arm-io/isp", "isp", false)) + return -1; + if (dt_set_isp_fwdata()) + return -1; #ifndef RELEASE if (dt_transfer_virtios()) return 1; diff --git a/src/pmgr.c b/src/pmgr.c index 0b766d06a..930a4396f 100644 --- a/src/pmgr.c +++ b/src/pmgr.c @@ -16,10 +16,6 @@ #define PMGR_PS_ACTUAL GENMASK(7, 4) #define PMGR_PS_TARGET GENMASK(3, 0) -#define PMGR_PS_ACTIVE 0xf -#define PMGR_PS_CLKGATE 0x4 -#define PMGR_PS_PWRGATE 0x0 - #define PMGR_POLL_TIMEOUT 10000 #define PMGR_FLAG_VIRTUAL 0x10 @@ -67,7 +63,7 @@ static uintptr_t pmgr_get_psreg(u8 idx) return pmgr_reg + reg_offset; } -static int pmgr_set_mode(uintptr_t addr, u8 target_mode) +int pmgr_set_mode(uintptr_t addr, u8 target_mode) { mask32(addr, PMGR_PS_TARGET, FIELD_PREP(PMGR_PS_TARGET, target_mode)); if (poll32(addr, PMGR_PS_ACTUAL, FIELD_PREP(PMGR_PS_ACTUAL, target_mode), PMGR_POLL_TIMEOUT) < diff --git a/src/pmgr.h b/src/pmgr.h index c96f4b0fb..3b61a57af 100644 --- a/src/pmgr.h +++ b/src/pmgr.h @@ -10,6 +10,10 @@ #define PMGR_DEVICE_ID GENMASK(15, 0) #define PMGR_DIE_ID GENMASK(31, 28) +#define PMGR_PS_ACTIVE 0xf +#define PMGR_PS_CLKGATE 0x4 +#define PMGR_PS_PWRGATE 0x0 + int pmgr_init(void); int pmgr_power_enable(u32 id); @@ -23,6 +27,8 @@ int pmgr_adt_reset(const char *path); int pmgr_reset(int die, const char *name); +int pmgr_set_mode(uintptr_t addr, u8 target_mode); + u32 pmgr_get_feature(const char *name); #endif diff --git a/src/proxy.c b/src/proxy.c index fca53ca38..05dcd7006 100644 --- a/src/proxy.c +++ b/src/proxy.c @@ -586,7 +586,7 @@ int proxy_process(ProxyRequest *request, ProxyReply *reply) reply->retval = dapf_init_all(); break; case P_DAPF_INIT: - reply->retval = dapf_init((const char *)request->args[0]); + reply->retval = dapf_init((const char *)request->args[0], 1); break; case P_CPUFREQ_INIT: