diff options
author | Arnd Bergmann <arnd@arndb.de> | 2024-07-09 15:36:08 +0200 |
---|---|---|
committer | Arnd Bergmann <arnd@arndb.de> | 2024-07-09 15:36:09 +0200 |
commit | 782b726201471326575f386128fa03db90c83ad8 (patch) | |
tree | a8fa124ae6d0b12a4ca7d1eb3ebc7fca35ba7418 /drivers | |
parent | dc3c836f9b0f1e615e5143a2afd542fc1162cd86 (diff) | |
parent | 9b003e14801cf85a8cebeddc87bc9fc77100fdce (diff) |
Merge tag 'zynqmp-soc2-for-6.11' of https://github.com/Xilinx/linux-xlnx into soc/drivers
arm64: Xilinx SoC changes for 6.11
Timer
- Fix u32 overflow issue in 32-bit width PWM mode.
Event manager:
- rename cpu_number1 to dummy_cpu_number
Power:
- Add cb event for subsystem restart
- check return status of get_api_version()
Firmware:
- Move FIRMWARE_VERSION_MASK to xlnx-zynqmp.h
* tag 'zynqmp-soc2-for-6.11' of https://github.com/Xilinx/linux-xlnx:
drivers: soc: xilinx: check return status of get_api_version()
firmware: xilinx: Move FIRMWARE_VERSION_MASK to xlnx-zynqmp.h
soc: xilinx: Add cb event for subsystem restart
soc: xilinx: rename cpu_number1 to dummy_cpu_number
pwm: xilinx: Fix u32 overflow issue in 32-bit width PWM mode.
Link: https://lore.kernel.org/r/CAHTX3dKMtqgNpkEvrw0p2w+SPN83Ai1_kzhefUGOO5rMkPaH_w@mail.gmail.com
Signed-off-by: Arnd Bergmann <arnd@arndb.de>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/firmware/xilinx/zynqmp.c | 3 | ||||
-rw-r--r-- | drivers/soc/xilinx/xlnx_event_manager.c | 16 | ||||
-rw-r--r-- | drivers/soc/xilinx/zynqmp_power.c | 155 |
3 files changed, 138 insertions, 36 deletions
diff --git a/drivers/firmware/xilinx/zynqmp.c b/drivers/firmware/xilinx/zynqmp.c index 9bc45357e1a8..add8acf66a9c 100644 --- a/drivers/firmware/xilinx/zynqmp.c +++ b/drivers/firmware/xilinx/zynqmp.c @@ -41,9 +41,6 @@ /* IOCTL/QUERY feature payload size */ #define FEATURE_PAYLOAD_SIZE 2 -/* Firmware feature check version mask */ -#define FIRMWARE_VERSION_MASK GENMASK(15, 0) - static bool feature_check_enabled; static DEFINE_HASHTABLE(pm_api_features_map, PM_API_FEATURE_CHECK_MAX_ORDER); static u32 ioctl_features[FEATURE_PAYLOAD_SIZE]; diff --git a/drivers/soc/xilinx/xlnx_event_manager.c b/drivers/soc/xilinx/xlnx_event_manager.c index 253299e4214d..f529e1346247 100644 --- a/drivers/soc/xilinx/xlnx_event_manager.c +++ b/drivers/soc/xilinx/xlnx_event_manager.c @@ -3,6 +3,7 @@ * Xilinx Event Management Driver * * Copyright (C) 2021 Xilinx, Inc. + * Copyright (C) 2024 Advanced Micro Devices, Inc. * * Abhyuday Godhasara <abhyuday.godhasara@xilinx.com> */ @@ -19,7 +20,7 @@ #include <linux/platform_device.h> #include <linux/slab.h> -static DEFINE_PER_CPU_READ_MOSTLY(int, cpu_number1); +static DEFINE_PER_CPU_READ_MOSTLY(int, dummy_cpu_number); static int virq_sgi; static int event_manager_availability = -EACCES; @@ -35,7 +36,6 @@ static int event_manager_availability = -EACCES; #define MAX_BITS (32U) /* Number of bits available for error mask */ -#define FIRMWARE_VERSION_MASK (0xFFFFU) #define REGISTER_NOTIFIER_FIRMWARE_VERSION (2U) static DEFINE_HASHTABLE(reg_driver_map, REGISTERED_DRIVER_MAX_ORDER); @@ -570,7 +570,6 @@ static void xlnx_disable_percpu_irq(void *data) static int xlnx_event_init_sgi(struct platform_device *pdev) { int ret = 0; - int cpu; /* * IRQ related structures are used for the following: * for each SGI interrupt ensure its mapped by GIC IRQ domain @@ -607,11 +606,8 @@ static int xlnx_event_init_sgi(struct platform_device *pdev) sgi_fwspec.param[0] = sgi_num; virq_sgi = irq_create_fwspec_mapping(&sgi_fwspec); - cpu = get_cpu(); - per_cpu(cpu_number1, cpu) = cpu; ret = request_percpu_irq(virq_sgi, xlnx_event_handler, "xlnx_event_mgmt", - &cpu_number1); - put_cpu(); + &dummy_cpu_number); WARN_ON(ret); if (ret) { @@ -627,16 +623,12 @@ static int xlnx_event_init_sgi(struct platform_device *pdev) static void xlnx_event_cleanup_sgi(struct platform_device *pdev) { - int cpu = smp_processor_id(); - - per_cpu(cpu_number1, cpu) = cpu; - cpuhp_remove_state(CPUHP_AP_ONLINE_DYN); on_each_cpu(xlnx_disable_percpu_irq, NULL, 1); irq_clear_status_flags(virq_sgi, IRQ_PER_CPU); - free_percpu_irq(virq_sgi, &cpu_number1); + free_percpu_irq(virq_sgi, &dummy_cpu_number); irq_dispose_mapping(virq_sgi); } diff --git a/drivers/soc/xilinx/zynqmp_power.c b/drivers/soc/xilinx/zynqmp_power.c index 965b1143936a..411d33f2fb05 100644 --- a/drivers/soc/xilinx/zynqmp_power.c +++ b/drivers/soc/xilinx/zynqmp_power.c @@ -30,9 +30,27 @@ struct zynqmp_pm_work_struct { u32 args[CB_ARG_CNT]; }; -static struct zynqmp_pm_work_struct *zynqmp_pm_init_suspend_work; +/** + * struct zynqmp_pm_event_info - event related information + * @cb_fun: Function pointer to store the callback function. + * @cb_type: Type of callback from pm_api_cb_id, + * PM_NOTIFY_CB - for Error Events, + * PM_INIT_SUSPEND_CB - for suspend callback. + * @node_id: Node-Id related to event. + * @event: Event Mask for the Error Event. + * @wake: Flag specifying whether the subsystem should be woken upon + * event notification. + */ +struct zynqmp_pm_event_info { + event_cb_func_t cb_fun; + enum pm_api_cb_id cb_type; + u32 node_id; + u32 event; + bool wake; +}; + +static struct zynqmp_pm_work_struct *zynqmp_pm_init_suspend_work, *zynqmp_pm_init_restart_work; static struct mbox_chan *rx_chan; -static bool event_registered; enum pm_suspend_mode { PM_SUSPEND_MODE_FIRST = 0, @@ -54,6 +72,19 @@ static void zynqmp_pm_get_callback_data(u32 *buf) zynqmp_pm_invoke_fn(GET_CALLBACK_DATA, buf, 0); } +static void subsystem_restart_event_callback(const u32 *payload, void *data) +{ + /* First element is callback API ID, others are callback arguments */ + if (work_pending(&zynqmp_pm_init_restart_work->callback_work)) + return; + + /* Copy callback arguments into work's structure */ + memcpy(zynqmp_pm_init_restart_work->args, &payload[0], + sizeof(zynqmp_pm_init_restart_work->args)); + + queue_work(system_unbound_wq, &zynqmp_pm_init_restart_work->callback_work); +} + static void suspend_event_callback(const u32 *payload, void *data) { /* First element is callback API ID, others are callback arguments */ @@ -120,6 +151,37 @@ static void ipi_receive_callback(struct mbox_client *cl, void *data) } /** + * zynqmp_pm_subsystem_restart_work_fn - Initiate Subsystem restart + * @work: Pointer to work_struct + * + * Bottom-half of PM callback IRQ handler. + */ +static void zynqmp_pm_subsystem_restart_work_fn(struct work_struct *work) +{ + int ret; + struct zynqmp_pm_work_struct *pm_work = container_of(work, struct zynqmp_pm_work_struct, + callback_work); + + /* First element is callback API ID, others are callback arguments */ + if (pm_work->args[0] == PM_NOTIFY_CB) { + if (pm_work->args[2] == EVENT_SUBSYSTEM_RESTART) { + ret = zynqmp_pm_system_shutdown(ZYNQMP_PM_SHUTDOWN_TYPE_SETSCOPE_ONLY, + ZYNQMP_PM_SHUTDOWN_SUBTYPE_SUBSYSTEM); + if (ret) { + pr_err("unable to set shutdown scope\n"); + return; + } + + kernel_restart(NULL); + } else { + pr_err("%s Unsupported Event - %d\n", __func__, pm_work->args[2]); + } + } else { + pr_err("%s() Unsupported Callback %d\n", __func__, pm_work->args[0]); + } +} + +/** * zynqmp_pm_init_suspend_work_fn - Initialize suspend * @work: Pointer to work_struct * @@ -184,13 +246,51 @@ static ssize_t suspend_mode_store(struct device *dev, static DEVICE_ATTR_RW(suspend_mode); +static void unregister_event(struct device *dev, void *res) +{ + struct zynqmp_pm_event_info *event_info = res; + + xlnx_unregister_event(event_info->cb_type, event_info->node_id, + event_info->event, event_info->cb_fun, NULL); +} + +static int register_event(struct device *dev, const enum pm_api_cb_id cb_type, const u32 node_id, + const u32 event, const bool wake, event_cb_func_t cb_fun) +{ + int ret; + struct zynqmp_pm_event_info *event_info; + + event_info = devres_alloc(unregister_event, sizeof(struct zynqmp_pm_event_info), + GFP_KERNEL); + if (!event_info) + return -ENOMEM; + + event_info->cb_type = cb_type; + event_info->node_id = node_id; + event_info->event = event; + event_info->wake = wake; + event_info->cb_fun = cb_fun; + + ret = xlnx_register_event(event_info->cb_type, event_info->node_id, + event_info->event, event_info->wake, event_info->cb_fun, NULL); + if (ret) { + devres_free(event_info); + return ret; + } + + devres_add(dev, event_info); + return 0; +} + static int zynqmp_pm_probe(struct platform_device *pdev) { int ret, irq; - u32 pm_api_version; + u32 pm_api_version, pm_family_code, pm_sub_family_code, node_id; struct mbox_client *client; - zynqmp_pm_get_api_version(&pm_api_version); + ret = zynqmp_pm_get_api_version(&pm_api_version); + if (ret) + return ret; /* Check PM API version number */ if (pm_api_version < ZYNQMP_PM_VERSION) @@ -203,21 +303,43 @@ static int zynqmp_pm_probe(struct platform_device *pdev) * is not available to use) or -ENODEV(Xilinx Event Manager not compiled), * then use ipi-mailbox or interrupt method. */ - ret = xlnx_register_event(PM_INIT_SUSPEND_CB, 0, 0, false, - suspend_event_callback, NULL); + ret = register_event(&pdev->dev, PM_INIT_SUSPEND_CB, 0, 0, false, + suspend_event_callback); if (!ret) { zynqmp_pm_init_suspend_work = devm_kzalloc(&pdev->dev, sizeof(struct zynqmp_pm_work_struct), GFP_KERNEL); - if (!zynqmp_pm_init_suspend_work) { - xlnx_unregister_event(PM_INIT_SUSPEND_CB, 0, 0, - suspend_event_callback, NULL); + if (!zynqmp_pm_init_suspend_work) return -ENOMEM; - } - event_registered = true; INIT_WORK(&zynqmp_pm_init_suspend_work->callback_work, zynqmp_pm_init_suspend_work_fn); + + ret = zynqmp_pm_get_family_info(&pm_family_code, &pm_sub_family_code); + if (ret < 0) + return ret; + + if (pm_sub_family_code == VERSALNET_SUB_FAMILY_CODE) + node_id = PM_DEV_ACPU_0_0; + else + node_id = PM_DEV_ACPU_0; + + ret = register_event(&pdev->dev, PM_NOTIFY_CB, node_id, EVENT_SUBSYSTEM_RESTART, + false, subsystem_restart_event_callback); + if (ret) { + dev_err(&pdev->dev, "Failed to Register with Xilinx Event manager %d\n", + ret); + return ret; + } + + zynqmp_pm_init_restart_work = devm_kzalloc(&pdev->dev, + sizeof(struct zynqmp_pm_work_struct), + GFP_KERNEL); + if (!zynqmp_pm_init_restart_work) + return -ENOMEM; + + INIT_WORK(&zynqmp_pm_init_restart_work->callback_work, + zynqmp_pm_subsystem_restart_work_fn); } else if (ret != -EACCES && ret != -ENODEV) { dev_err(&pdev->dev, "Failed to Register with Xilinx Event manager %d\n", ret); return ret; @@ -264,15 +386,8 @@ static int zynqmp_pm_probe(struct platform_device *pdev) } ret = sysfs_create_file(&pdev->dev.kobj, &dev_attr_suspend_mode.attr); - if (ret) { - if (event_registered) { - xlnx_unregister_event(PM_INIT_SUSPEND_CB, 0, 0, suspend_event_callback, - NULL); - event_registered = false; - } - dev_err(&pdev->dev, "unable to create sysfs interface\n"); + if (ret) return ret; - } return 0; } @@ -280,8 +395,6 @@ static int zynqmp_pm_probe(struct platform_device *pdev) static void zynqmp_pm_remove(struct platform_device *pdev) { sysfs_remove_file(&pdev->dev.kobj, &dev_attr_suspend_mode.attr); - if (event_registered) - xlnx_unregister_event(PM_INIT_SUSPEND_CB, 0, 0, suspend_event_callback, NULL); if (!rx_chan) mbox_free_channel(rx_chan); |