From 102a0743326a03cd1a1202ceda21e175b7d3575c Mon Sep 17 00:00:00 2001 From: hc <hc@nodka.com> Date: Tue, 20 Feb 2024 01:20:52 +0000 Subject: [PATCH] add new system file --- kernel/drivers/firmware/raspberrypi.c | 139 ++++++++++++++++++++++++++++++++++++++++------ 1 files changed, 121 insertions(+), 18 deletions(-) diff --git a/kernel/drivers/firmware/raspberrypi.c b/kernel/drivers/firmware/raspberrypi.c index 44eb998..45ff03d 100644 --- a/kernel/drivers/firmware/raspberrypi.c +++ b/kernel/drivers/firmware/raspberrypi.c @@ -1,15 +1,13 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Defines interfaces for interacting wtih the Raspberry Pi firmware's * property channel. * * Copyright © 2015 Broadcom - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. */ #include <linux/dma-mapping.h> +#include <linux/kref.h> #include <linux/mailbox_client.h> #include <linux/module.h> #include <linux/of_platform.h> @@ -23,12 +21,15 @@ #define MBOX_CHAN_PROPERTY 8 static struct platform_device *rpi_hwmon; +static struct platform_device *rpi_clk; struct rpi_firmware { struct mbox_client cl; struct mbox_chan *chan; /* The property channel. */ struct completion c; u32 enabled; + + struct kref consumers; }; static DEFINE_MUTEX(transaction_lock); @@ -55,8 +56,12 @@ reinit_completion(&fw->c); ret = mbox_send_message(fw->chan, &message); if (ret >= 0) { - wait_for_completion(&fw->c); - ret = 0; + if (wait_for_completion_timeout(&fw->c, HZ)) { + ret = 0; + } else { + ret = -ETIMEDOUT; + WARN_ONCE(1, "Firmware transaction timeout"); + } } else { dev_err(fw->cl.dev, "mbox_send_message returned %d\n", ret); } @@ -175,21 +180,18 @@ static void rpi_firmware_print_firmware_revision(struct rpi_firmware *fw) { + time64_t date_and_time; u32 packet; int ret = rpi_firmware_property(fw, RPI_FIRMWARE_GET_FIRMWARE_REVISION, &packet, sizeof(packet)); - if (ret == 0) { - struct tm tm; + if (ret) + return; - time64_to_tm(packet, 0, &tm); - - dev_info(fw->cl.dev, - "Attached to firmware from %04ld-%02d-%02d %02d:%02d\n", - tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, - tm.tm_hour, tm.tm_min); - } + /* This is not compatible with y2038 */ + date_and_time = packet; + dev_info(fw->cl.dev, "Attached to firmware from %ptT\n", &date_and_time); } static void @@ -206,12 +208,58 @@ -1, NULL, 0); } +static void rpi_register_clk_driver(struct device *dev) +{ + struct device_node *firmware; + + /* + * Earlier DTs don't have a node for the firmware clocks but + * rely on us creating a platform device by hand. If we do + * have a node for the firmware clocks, just bail out here. + */ + firmware = of_get_compatible_child(dev->of_node, + "raspberrypi,firmware-clocks"); + if (firmware) { + of_node_put(firmware); + return; + } + + rpi_clk = platform_device_register_data(dev, "raspberrypi-clk", + -1, NULL, 0); +} + +static void rpi_firmware_delete(struct kref *kref) +{ + struct rpi_firmware *fw = container_of(kref, struct rpi_firmware, + consumers); + + mbox_free_channel(fw->chan); + kfree(fw); +} + +void rpi_firmware_put(struct rpi_firmware *fw) +{ + kref_put(&fw->consumers, rpi_firmware_delete); +} +EXPORT_SYMBOL_GPL(rpi_firmware_put); + +static void devm_rpi_firmware_put(void *data) +{ + struct rpi_firmware *fw = data; + + rpi_firmware_put(fw); +} + static int rpi_firmware_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct rpi_firmware *fw; - fw = devm_kzalloc(dev, sizeof(*fw), GFP_KERNEL); + /* + * Memory will be freed by rpi_firmware_delete() once all users have + * released their firmware handles. Don't use devm_kzalloc() here. + */ + fw = kzalloc(sizeof(*fw), GFP_KERNEL); if (!fw) return -ENOMEM; @@ -224,17 +272,30 @@ int ret = PTR_ERR(fw->chan); if (ret != -EPROBE_DEFER) dev_err(dev, "Failed to get mbox channel: %d\n", ret); + kfree(fw); return ret; } init_completion(&fw->c); + kref_init(&fw->consumers); platform_set_drvdata(pdev, fw); rpi_firmware_print_firmware_revision(fw); rpi_register_hwmon_driver(dev, fw); + rpi_register_clk_driver(dev); return 0; +} + +static void rpi_firmware_shutdown(struct platform_device *pdev) +{ + struct rpi_firmware *fw = platform_get_drvdata(pdev); + + if (!fw) + return; + + rpi_firmware_property(fw, RPI_FIRMWARE_NOTIFY_REBOOT, NULL, 0); } static int rpi_firmware_remove(struct platform_device *pdev) @@ -243,7 +304,10 @@ platform_device_unregister(rpi_hwmon); rpi_hwmon = NULL; - mbox_free_channel(fw->chan); + platform_device_unregister(rpi_clk); + rpi_clk = NULL; + + rpi_firmware_put(fw); return 0; } @@ -252,18 +316,56 @@ * rpi_firmware_get - Get pointer to rpi_firmware structure. * @firmware_node: Pointer to the firmware Device Tree node. * + * The reference to rpi_firmware has to be released with rpi_firmware_put(). + * * Returns NULL is the firmware device is not ready. */ struct rpi_firmware *rpi_firmware_get(struct device_node *firmware_node) { struct platform_device *pdev = of_find_device_by_node(firmware_node); + struct rpi_firmware *fw; if (!pdev) return NULL; - return platform_get_drvdata(pdev); + fw = platform_get_drvdata(pdev); + if (!fw) + goto err_put_device; + + if (!kref_get_unless_zero(&fw->consumers)) + goto err_put_device; + + put_device(&pdev->dev); + + return fw; + +err_put_device: + put_device(&pdev->dev); + return NULL; } EXPORT_SYMBOL_GPL(rpi_firmware_get); + +/** + * devm_rpi_firmware_get - Get pointer to rpi_firmware structure. + * @firmware_node: Pointer to the firmware Device Tree node. + * + * Returns NULL is the firmware device is not ready. + */ +struct rpi_firmware *devm_rpi_firmware_get(struct device *dev, + struct device_node *firmware_node) +{ + struct rpi_firmware *fw; + + fw = rpi_firmware_get(firmware_node); + if (!fw) + return NULL; + + if (devm_add_action_or_reset(dev, devm_rpi_firmware_put, fw)) + return NULL; + + return fw; +} +EXPORT_SYMBOL_GPL(devm_rpi_firmware_get); static const struct of_device_id rpi_firmware_of_match[] = { { .compatible = "raspberrypi,bcm2835-firmware", }, @@ -277,6 +379,7 @@ .of_match_table = rpi_firmware_of_match, }, .probe = rpi_firmware_probe, + .shutdown = rpi_firmware_shutdown, .remove = rpi_firmware_remove, }; module_platform_driver(rpi_firmware_driver); -- Gitblit v1.6.2