162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * ARM Specific GTDT table Support 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2016, Linaro Ltd. 662306a36Sopenharmony_ci * Author: Daniel Lezcano <daniel.lezcano@linaro.org> 762306a36Sopenharmony_ci * Fu Wei <fu.wei@linaro.org> 862306a36Sopenharmony_ci * Hanjun Guo <hanjun.guo@linaro.org> 962306a36Sopenharmony_ci */ 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_ci#include <linux/acpi.h> 1262306a36Sopenharmony_ci#include <linux/init.h> 1362306a36Sopenharmony_ci#include <linux/irqdomain.h> 1462306a36Sopenharmony_ci#include <linux/kernel.h> 1562306a36Sopenharmony_ci#include <linux/platform_device.h> 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci#include <clocksource/arm_arch_timer.h> 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci#undef pr_fmt 2062306a36Sopenharmony_ci#define pr_fmt(fmt) "ACPI GTDT: " fmt 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci/** 2362306a36Sopenharmony_ci * struct acpi_gtdt_descriptor - Store the key info of GTDT for all functions 2462306a36Sopenharmony_ci * @gtdt: The pointer to the struct acpi_table_gtdt of GTDT table. 2562306a36Sopenharmony_ci * @gtdt_end: The pointer to the end of GTDT table. 2662306a36Sopenharmony_ci * @platform_timer: The pointer to the start of Platform Timer Structure 2762306a36Sopenharmony_ci * 2862306a36Sopenharmony_ci * The struct store the key info of GTDT table, it should be initialized by 2962306a36Sopenharmony_ci * acpi_gtdt_init. 3062306a36Sopenharmony_ci */ 3162306a36Sopenharmony_cistruct acpi_gtdt_descriptor { 3262306a36Sopenharmony_ci struct acpi_table_gtdt *gtdt; 3362306a36Sopenharmony_ci void *gtdt_end; 3462306a36Sopenharmony_ci void *platform_timer; 3562306a36Sopenharmony_ci}; 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_cistatic struct acpi_gtdt_descriptor acpi_gtdt_desc __initdata; 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_cistatic inline __init void *next_platform_timer(void *platform_timer) 4062306a36Sopenharmony_ci{ 4162306a36Sopenharmony_ci struct acpi_gtdt_header *gh = platform_timer; 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci platform_timer += gh->length; 4462306a36Sopenharmony_ci if (platform_timer < acpi_gtdt_desc.gtdt_end) 4562306a36Sopenharmony_ci return platform_timer; 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci return NULL; 4862306a36Sopenharmony_ci} 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci#define for_each_platform_timer(_g) \ 5162306a36Sopenharmony_ci for (_g = acpi_gtdt_desc.platform_timer; _g; \ 5262306a36Sopenharmony_ci _g = next_platform_timer(_g)) 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_cistatic inline bool is_timer_block(void *platform_timer) 5562306a36Sopenharmony_ci{ 5662306a36Sopenharmony_ci struct acpi_gtdt_header *gh = platform_timer; 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci return gh->type == ACPI_GTDT_TYPE_TIMER_BLOCK; 5962306a36Sopenharmony_ci} 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_cistatic inline bool is_non_secure_watchdog(void *platform_timer) 6262306a36Sopenharmony_ci{ 6362306a36Sopenharmony_ci struct acpi_gtdt_header *gh = platform_timer; 6462306a36Sopenharmony_ci struct acpi_gtdt_watchdog *wd = platform_timer; 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci if (gh->type != ACPI_GTDT_TYPE_WATCHDOG) 6762306a36Sopenharmony_ci return false; 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci return !(wd->timer_flags & ACPI_GTDT_WATCHDOG_SECURE); 7062306a36Sopenharmony_ci} 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_cistatic int __init map_gt_gsi(u32 interrupt, u32 flags) 7362306a36Sopenharmony_ci{ 7462306a36Sopenharmony_ci int trigger, polarity; 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci trigger = (flags & ACPI_GTDT_INTERRUPT_MODE) ? ACPI_EDGE_SENSITIVE 7762306a36Sopenharmony_ci : ACPI_LEVEL_SENSITIVE; 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci polarity = (flags & ACPI_GTDT_INTERRUPT_POLARITY) ? ACPI_ACTIVE_LOW 8062306a36Sopenharmony_ci : ACPI_ACTIVE_HIGH; 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci return acpi_register_gsi(NULL, interrupt, trigger, polarity); 8362306a36Sopenharmony_ci} 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci/** 8662306a36Sopenharmony_ci * acpi_gtdt_map_ppi() - Map the PPIs of per-cpu arch_timer. 8762306a36Sopenharmony_ci * @type: the type of PPI. 8862306a36Sopenharmony_ci * 8962306a36Sopenharmony_ci * Note: Secure state is not managed by the kernel on ARM64 systems. 9062306a36Sopenharmony_ci * So we only handle the non-secure timer PPIs, 9162306a36Sopenharmony_ci * ARCH_TIMER_PHYS_SECURE_PPI is treated as invalid type. 9262306a36Sopenharmony_ci * 9362306a36Sopenharmony_ci * Return: the mapped PPI value, 0 if error. 9462306a36Sopenharmony_ci */ 9562306a36Sopenharmony_ciint __init acpi_gtdt_map_ppi(int type) 9662306a36Sopenharmony_ci{ 9762306a36Sopenharmony_ci struct acpi_table_gtdt *gtdt = acpi_gtdt_desc.gtdt; 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci switch (type) { 10062306a36Sopenharmony_ci case ARCH_TIMER_PHYS_NONSECURE_PPI: 10162306a36Sopenharmony_ci return map_gt_gsi(gtdt->non_secure_el1_interrupt, 10262306a36Sopenharmony_ci gtdt->non_secure_el1_flags); 10362306a36Sopenharmony_ci case ARCH_TIMER_VIRT_PPI: 10462306a36Sopenharmony_ci return map_gt_gsi(gtdt->virtual_timer_interrupt, 10562306a36Sopenharmony_ci gtdt->virtual_timer_flags); 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci case ARCH_TIMER_HYP_PPI: 10862306a36Sopenharmony_ci return map_gt_gsi(gtdt->non_secure_el2_interrupt, 10962306a36Sopenharmony_ci gtdt->non_secure_el2_flags); 11062306a36Sopenharmony_ci default: 11162306a36Sopenharmony_ci pr_err("Failed to map timer interrupt: invalid type.\n"); 11262306a36Sopenharmony_ci } 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci return 0; 11562306a36Sopenharmony_ci} 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci/** 11862306a36Sopenharmony_ci * acpi_gtdt_c3stop() - Got c3stop info from GTDT according to the type of PPI. 11962306a36Sopenharmony_ci * @type: the type of PPI. 12062306a36Sopenharmony_ci * 12162306a36Sopenharmony_ci * Return: true if the timer HW state is lost when a CPU enters an idle state, 12262306a36Sopenharmony_ci * false otherwise 12362306a36Sopenharmony_ci */ 12462306a36Sopenharmony_cibool __init acpi_gtdt_c3stop(int type) 12562306a36Sopenharmony_ci{ 12662306a36Sopenharmony_ci struct acpi_table_gtdt *gtdt = acpi_gtdt_desc.gtdt; 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci switch (type) { 12962306a36Sopenharmony_ci case ARCH_TIMER_PHYS_NONSECURE_PPI: 13062306a36Sopenharmony_ci return !(gtdt->non_secure_el1_flags & ACPI_GTDT_ALWAYS_ON); 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci case ARCH_TIMER_VIRT_PPI: 13362306a36Sopenharmony_ci return !(gtdt->virtual_timer_flags & ACPI_GTDT_ALWAYS_ON); 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci case ARCH_TIMER_HYP_PPI: 13662306a36Sopenharmony_ci return !(gtdt->non_secure_el2_flags & ACPI_GTDT_ALWAYS_ON); 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci default: 13962306a36Sopenharmony_ci pr_err("Failed to get c3stop info: invalid type.\n"); 14062306a36Sopenharmony_ci } 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci return false; 14362306a36Sopenharmony_ci} 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci/** 14662306a36Sopenharmony_ci * acpi_gtdt_init() - Get the info of GTDT table to prepare for further init. 14762306a36Sopenharmony_ci * @table: The pointer to GTDT table. 14862306a36Sopenharmony_ci * @platform_timer_count: It points to a integer variable which is used 14962306a36Sopenharmony_ci * for storing the number of platform timers. 15062306a36Sopenharmony_ci * This pointer could be NULL, if the caller 15162306a36Sopenharmony_ci * doesn't need this info. 15262306a36Sopenharmony_ci * 15362306a36Sopenharmony_ci * Return: 0 if success, -EINVAL if error. 15462306a36Sopenharmony_ci */ 15562306a36Sopenharmony_ciint __init acpi_gtdt_init(struct acpi_table_header *table, 15662306a36Sopenharmony_ci int *platform_timer_count) 15762306a36Sopenharmony_ci{ 15862306a36Sopenharmony_ci void *platform_timer; 15962306a36Sopenharmony_ci struct acpi_table_gtdt *gtdt; 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci gtdt = container_of(table, struct acpi_table_gtdt, header); 16262306a36Sopenharmony_ci acpi_gtdt_desc.gtdt = gtdt; 16362306a36Sopenharmony_ci acpi_gtdt_desc.gtdt_end = (void *)table + table->length; 16462306a36Sopenharmony_ci acpi_gtdt_desc.platform_timer = NULL; 16562306a36Sopenharmony_ci if (platform_timer_count) 16662306a36Sopenharmony_ci *platform_timer_count = 0; 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci if (table->revision < 2) { 16962306a36Sopenharmony_ci pr_warn("Revision:%d doesn't support Platform Timers.\n", 17062306a36Sopenharmony_ci table->revision); 17162306a36Sopenharmony_ci return 0; 17262306a36Sopenharmony_ci } 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci if (!gtdt->platform_timer_count) { 17562306a36Sopenharmony_ci pr_debug("No Platform Timer.\n"); 17662306a36Sopenharmony_ci return 0; 17762306a36Sopenharmony_ci } 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci platform_timer = (void *)gtdt + gtdt->platform_timer_offset; 18062306a36Sopenharmony_ci if (platform_timer < (void *)table + sizeof(struct acpi_table_gtdt)) { 18162306a36Sopenharmony_ci pr_err(FW_BUG "invalid timer data.\n"); 18262306a36Sopenharmony_ci return -EINVAL; 18362306a36Sopenharmony_ci } 18462306a36Sopenharmony_ci acpi_gtdt_desc.platform_timer = platform_timer; 18562306a36Sopenharmony_ci if (platform_timer_count) 18662306a36Sopenharmony_ci *platform_timer_count = gtdt->platform_timer_count; 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci return 0; 18962306a36Sopenharmony_ci} 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_cistatic int __init gtdt_parse_timer_block(struct acpi_gtdt_timer_block *block, 19262306a36Sopenharmony_ci struct arch_timer_mem *timer_mem) 19362306a36Sopenharmony_ci{ 19462306a36Sopenharmony_ci int i; 19562306a36Sopenharmony_ci struct arch_timer_mem_frame *frame; 19662306a36Sopenharmony_ci struct acpi_gtdt_timer_entry *gtdt_frame; 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ci if (!block->timer_count) { 19962306a36Sopenharmony_ci pr_err(FW_BUG "GT block present, but frame count is zero.\n"); 20062306a36Sopenharmony_ci return -ENODEV; 20162306a36Sopenharmony_ci } 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci if (block->timer_count > ARCH_TIMER_MEM_MAX_FRAMES) { 20462306a36Sopenharmony_ci pr_err(FW_BUG "GT block lists %d frames, ACPI spec only allows 8\n", 20562306a36Sopenharmony_ci block->timer_count); 20662306a36Sopenharmony_ci return -EINVAL; 20762306a36Sopenharmony_ci } 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci timer_mem->cntctlbase = (phys_addr_t)block->block_address; 21062306a36Sopenharmony_ci /* 21162306a36Sopenharmony_ci * The CNTCTLBase frame is 4KB (register offsets 0x000 - 0xFFC). 21262306a36Sopenharmony_ci * See ARM DDI 0487A.k_iss10775, page I1-5129, Table I1-3 21362306a36Sopenharmony_ci * "CNTCTLBase memory map". 21462306a36Sopenharmony_ci */ 21562306a36Sopenharmony_ci timer_mem->size = SZ_4K; 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci gtdt_frame = (void *)block + block->timer_offset; 21862306a36Sopenharmony_ci if (gtdt_frame + block->timer_count != (void *)block + block->header.length) 21962306a36Sopenharmony_ci return -EINVAL; 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ci /* 22262306a36Sopenharmony_ci * Get the GT timer Frame data for every GT Block Timer 22362306a36Sopenharmony_ci */ 22462306a36Sopenharmony_ci for (i = 0; i < block->timer_count; i++, gtdt_frame++) { 22562306a36Sopenharmony_ci if (gtdt_frame->common_flags & ACPI_GTDT_GT_IS_SECURE_TIMER) 22662306a36Sopenharmony_ci continue; 22762306a36Sopenharmony_ci if (gtdt_frame->frame_number >= ARCH_TIMER_MEM_MAX_FRAMES || 22862306a36Sopenharmony_ci !gtdt_frame->base_address || !gtdt_frame->timer_interrupt) 22962306a36Sopenharmony_ci goto error; 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci frame = &timer_mem->frame[gtdt_frame->frame_number]; 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ci /* duplicate frame */ 23462306a36Sopenharmony_ci if (frame->valid) 23562306a36Sopenharmony_ci goto error; 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_ci frame->phys_irq = map_gt_gsi(gtdt_frame->timer_interrupt, 23862306a36Sopenharmony_ci gtdt_frame->timer_flags); 23962306a36Sopenharmony_ci if (frame->phys_irq <= 0) { 24062306a36Sopenharmony_ci pr_warn("failed to map physical timer irq in frame %d.\n", 24162306a36Sopenharmony_ci gtdt_frame->frame_number); 24262306a36Sopenharmony_ci goto error; 24362306a36Sopenharmony_ci } 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci if (gtdt_frame->virtual_timer_interrupt) { 24662306a36Sopenharmony_ci frame->virt_irq = 24762306a36Sopenharmony_ci map_gt_gsi(gtdt_frame->virtual_timer_interrupt, 24862306a36Sopenharmony_ci gtdt_frame->virtual_timer_flags); 24962306a36Sopenharmony_ci if (frame->virt_irq <= 0) { 25062306a36Sopenharmony_ci pr_warn("failed to map virtual timer irq in frame %d.\n", 25162306a36Sopenharmony_ci gtdt_frame->frame_number); 25262306a36Sopenharmony_ci goto error; 25362306a36Sopenharmony_ci } 25462306a36Sopenharmony_ci } else { 25562306a36Sopenharmony_ci pr_debug("virtual timer in frame %d not implemented.\n", 25662306a36Sopenharmony_ci gtdt_frame->frame_number); 25762306a36Sopenharmony_ci } 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_ci frame->cntbase = gtdt_frame->base_address; 26062306a36Sopenharmony_ci /* 26162306a36Sopenharmony_ci * The CNTBaseN frame is 4KB (register offsets 0x000 - 0xFFC). 26262306a36Sopenharmony_ci * See ARM DDI 0487A.k_iss10775, page I1-5130, Table I1-4 26362306a36Sopenharmony_ci * "CNTBaseN memory map". 26462306a36Sopenharmony_ci */ 26562306a36Sopenharmony_ci frame->size = SZ_4K; 26662306a36Sopenharmony_ci frame->valid = true; 26762306a36Sopenharmony_ci } 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ci return 0; 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_cierror: 27262306a36Sopenharmony_ci do { 27362306a36Sopenharmony_ci if (gtdt_frame->common_flags & ACPI_GTDT_GT_IS_SECURE_TIMER || 27462306a36Sopenharmony_ci gtdt_frame->frame_number >= ARCH_TIMER_MEM_MAX_FRAMES) 27562306a36Sopenharmony_ci continue; 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_ci frame = &timer_mem->frame[gtdt_frame->frame_number]; 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci if (frame->phys_irq > 0) 28062306a36Sopenharmony_ci acpi_unregister_gsi(gtdt_frame->timer_interrupt); 28162306a36Sopenharmony_ci frame->phys_irq = 0; 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_ci if (frame->virt_irq > 0) 28462306a36Sopenharmony_ci acpi_unregister_gsi(gtdt_frame->virtual_timer_interrupt); 28562306a36Sopenharmony_ci frame->virt_irq = 0; 28662306a36Sopenharmony_ci } while (i-- >= 0 && gtdt_frame--); 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_ci return -EINVAL; 28962306a36Sopenharmony_ci} 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_ci/** 29262306a36Sopenharmony_ci * acpi_arch_timer_mem_init() - Get the info of all GT blocks in GTDT table. 29362306a36Sopenharmony_ci * @timer_mem: The pointer to the array of struct arch_timer_mem for returning 29462306a36Sopenharmony_ci * the result of parsing. The element number of this array should 29562306a36Sopenharmony_ci * be platform_timer_count(the total number of platform timers). 29662306a36Sopenharmony_ci * @timer_count: It points to a integer variable which is used for storing the 29762306a36Sopenharmony_ci * number of GT blocks we have parsed. 29862306a36Sopenharmony_ci * 29962306a36Sopenharmony_ci * Return: 0 if success, -EINVAL/-ENODEV if error. 30062306a36Sopenharmony_ci */ 30162306a36Sopenharmony_ciint __init acpi_arch_timer_mem_init(struct arch_timer_mem *timer_mem, 30262306a36Sopenharmony_ci int *timer_count) 30362306a36Sopenharmony_ci{ 30462306a36Sopenharmony_ci int ret; 30562306a36Sopenharmony_ci void *platform_timer; 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_ci *timer_count = 0; 30862306a36Sopenharmony_ci for_each_platform_timer(platform_timer) { 30962306a36Sopenharmony_ci if (is_timer_block(platform_timer)) { 31062306a36Sopenharmony_ci ret = gtdt_parse_timer_block(platform_timer, timer_mem); 31162306a36Sopenharmony_ci if (ret) 31262306a36Sopenharmony_ci return ret; 31362306a36Sopenharmony_ci timer_mem++; 31462306a36Sopenharmony_ci (*timer_count)++; 31562306a36Sopenharmony_ci } 31662306a36Sopenharmony_ci } 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_ci if (*timer_count) 31962306a36Sopenharmony_ci pr_info("found %d memory-mapped timer block(s).\n", 32062306a36Sopenharmony_ci *timer_count); 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_ci return 0; 32362306a36Sopenharmony_ci} 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_ci/* 32662306a36Sopenharmony_ci * Initialize a SBSA generic Watchdog platform device info from GTDT 32762306a36Sopenharmony_ci */ 32862306a36Sopenharmony_cistatic int __init gtdt_import_sbsa_gwdt(struct acpi_gtdt_watchdog *wd, 32962306a36Sopenharmony_ci int index) 33062306a36Sopenharmony_ci{ 33162306a36Sopenharmony_ci struct platform_device *pdev; 33262306a36Sopenharmony_ci int irq; 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_ci /* 33562306a36Sopenharmony_ci * According to SBSA specification the size of refresh and control 33662306a36Sopenharmony_ci * frames of SBSA Generic Watchdog is SZ_4K(Offset 0x000 – 0xFFF). 33762306a36Sopenharmony_ci */ 33862306a36Sopenharmony_ci struct resource res[] = { 33962306a36Sopenharmony_ci DEFINE_RES_MEM(wd->control_frame_address, SZ_4K), 34062306a36Sopenharmony_ci DEFINE_RES_MEM(wd->refresh_frame_address, SZ_4K), 34162306a36Sopenharmony_ci {}, 34262306a36Sopenharmony_ci }; 34362306a36Sopenharmony_ci int nr_res = ARRAY_SIZE(res); 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_ci pr_debug("found a Watchdog (0x%llx/0x%llx gsi:%u flags:0x%x).\n", 34662306a36Sopenharmony_ci wd->refresh_frame_address, wd->control_frame_address, 34762306a36Sopenharmony_ci wd->timer_interrupt, wd->timer_flags); 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_ci if (!(wd->refresh_frame_address && wd->control_frame_address)) { 35062306a36Sopenharmony_ci pr_err(FW_BUG "failed to get the Watchdog base address.\n"); 35162306a36Sopenharmony_ci return -EINVAL; 35262306a36Sopenharmony_ci } 35362306a36Sopenharmony_ci 35462306a36Sopenharmony_ci irq = map_gt_gsi(wd->timer_interrupt, wd->timer_flags); 35562306a36Sopenharmony_ci res[2] = (struct resource)DEFINE_RES_IRQ(irq); 35662306a36Sopenharmony_ci if (irq <= 0) { 35762306a36Sopenharmony_ci pr_warn("failed to map the Watchdog interrupt.\n"); 35862306a36Sopenharmony_ci nr_res--; 35962306a36Sopenharmony_ci } 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_ci /* 36262306a36Sopenharmony_ci * Add a platform device named "sbsa-gwdt" to match the platform driver. 36362306a36Sopenharmony_ci * "sbsa-gwdt": SBSA(Server Base System Architecture) Generic Watchdog 36462306a36Sopenharmony_ci * The platform driver can get device info below by matching this name. 36562306a36Sopenharmony_ci */ 36662306a36Sopenharmony_ci pdev = platform_device_register_simple("sbsa-gwdt", index, res, nr_res); 36762306a36Sopenharmony_ci if (IS_ERR(pdev)) { 36862306a36Sopenharmony_ci if (irq > 0) 36962306a36Sopenharmony_ci acpi_unregister_gsi(wd->timer_interrupt); 37062306a36Sopenharmony_ci return PTR_ERR(pdev); 37162306a36Sopenharmony_ci } 37262306a36Sopenharmony_ci 37362306a36Sopenharmony_ci return 0; 37462306a36Sopenharmony_ci} 37562306a36Sopenharmony_ci 37662306a36Sopenharmony_cistatic int __init gtdt_sbsa_gwdt_init(void) 37762306a36Sopenharmony_ci{ 37862306a36Sopenharmony_ci void *platform_timer; 37962306a36Sopenharmony_ci struct acpi_table_header *table; 38062306a36Sopenharmony_ci int ret, timer_count, gwdt_count = 0; 38162306a36Sopenharmony_ci 38262306a36Sopenharmony_ci if (acpi_disabled) 38362306a36Sopenharmony_ci return 0; 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_ci if (ACPI_FAILURE(acpi_get_table(ACPI_SIG_GTDT, 0, &table))) 38662306a36Sopenharmony_ci return -EINVAL; 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_ci /* 38962306a36Sopenharmony_ci * Note: Even though the global variable acpi_gtdt_desc has been 39062306a36Sopenharmony_ci * initialized by acpi_gtdt_init() while initializing the arch timers, 39162306a36Sopenharmony_ci * when we call this function to get SBSA watchdogs info from GTDT, the 39262306a36Sopenharmony_ci * pointers stashed in it are stale (since they are early temporary 39362306a36Sopenharmony_ci * mappings carried out before acpi_permanent_mmap is set) and we need 39462306a36Sopenharmony_ci * to re-initialize them with permanent mapped pointer values to let the 39562306a36Sopenharmony_ci * GTDT parsing possible. 39662306a36Sopenharmony_ci */ 39762306a36Sopenharmony_ci ret = acpi_gtdt_init(table, &timer_count); 39862306a36Sopenharmony_ci if (ret || !timer_count) 39962306a36Sopenharmony_ci goto out_put_gtdt; 40062306a36Sopenharmony_ci 40162306a36Sopenharmony_ci for_each_platform_timer(platform_timer) { 40262306a36Sopenharmony_ci if (is_non_secure_watchdog(platform_timer)) { 40362306a36Sopenharmony_ci ret = gtdt_import_sbsa_gwdt(platform_timer, gwdt_count); 40462306a36Sopenharmony_ci if (ret) 40562306a36Sopenharmony_ci break; 40662306a36Sopenharmony_ci gwdt_count++; 40762306a36Sopenharmony_ci } 40862306a36Sopenharmony_ci } 40962306a36Sopenharmony_ci 41062306a36Sopenharmony_ci if (gwdt_count) 41162306a36Sopenharmony_ci pr_info("found %d SBSA generic Watchdog(s).\n", gwdt_count); 41262306a36Sopenharmony_ci 41362306a36Sopenharmony_ciout_put_gtdt: 41462306a36Sopenharmony_ci acpi_put_table(table); 41562306a36Sopenharmony_ci return ret; 41662306a36Sopenharmony_ci} 41762306a36Sopenharmony_ci 41862306a36Sopenharmony_cidevice_initcall(gtdt_sbsa_gwdt_init); 419