diff options
Diffstat (limited to 'drivers/firmware')
-rw-r--r-- | drivers/firmware/Kconfig | 2 | ||||
-rw-r--r-- | drivers/firmware/imx/scu-pd.c | 41 | ||||
-rw-r--r-- | drivers/firmware/raspberrypi.c | 69 |
3 files changed, 104 insertions, 8 deletions
diff --git a/drivers/firmware/Kconfig b/drivers/firmware/Kconfig index 3f14dffb9669..6a4e882e448d 100644 --- a/drivers/firmware/Kconfig +++ b/drivers/firmware/Kconfig @@ -206,7 +206,7 @@ config FW_CFG_SYSFS_CMDLINE config INTEL_STRATIX10_SERVICE tristate "Intel Stratix10 Service Layer" - depends on (ARCH_STRATIX10 || ARCH_AGILEX) && HAVE_ARM_SMCCC + depends on ARCH_INTEL_SOCFPGA && HAVE_ARM_SMCCC default n help Intel Stratix10 service layer runs at privileged exception level, diff --git a/drivers/firmware/imx/scu-pd.c b/drivers/firmware/imx/scu-pd.c index 08533ee67626..ff6569c4a53b 100644 --- a/drivers/firmware/imx/scu-pd.c +++ b/drivers/firmware/imx/scu-pd.c @@ -29,6 +29,10 @@ * The framework needs some proper extension to support multi power * domain cases. * + * Update: Genpd assigns the ->of_node for the virtual device before it + * invokes ->attach_dev() callback, hence parsing for device resources via + * DT should work fine. + * * 2. It also breaks most of current drivers as the driver probe sequence * behavior changed if removing ->power_on|off() callback and use * ->start() and ->stop() instead. genpd_dev_pm_attach will only power @@ -39,8 +43,11 @@ * domain enabled will trigger a HW access error. That means we need fix * most drivers probe sequence with proper runtime pm. * - * In summary, we need fix above two issue before being able to switch to - * the "single global power domain" way. + * Update: Runtime PM support isn't necessary. Instead, this can easily be + * fixed in drivers by adding a call to dev_pm_domain_start() during probe. + * + * In summary, the second part needs to be addressed via minor updates to the + * relevant drivers, before the "single global power domain" model can be used. * */ @@ -86,6 +93,8 @@ struct imx_sc_pd_soc { u8 num_ranges; }; +static int imx_con_rsrc; + static const struct imx_sc_pd_range imx8qxp_scu_pd_ranges[] = { /* LSIO SS */ { "pwm", IMX_SC_R_PWM_0, 8, true, 0 }, @@ -134,7 +143,7 @@ static const struct imx_sc_pd_range imx8qxp_scu_pd_ranges[] = { { "can", IMX_SC_R_CAN_0, 3, true, 0 }, { "ftm", IMX_SC_R_FTM_0, 2, true, 0 }, { "lpi2c", IMX_SC_R_I2C_0, 4, true, 0 }, - { "adc", IMX_SC_R_ADC_0, 1, true, 0 }, + { "adc", IMX_SC_R_ADC_0, 2, true, 0 }, { "lcd", IMX_SC_R_LCD_0, 1, true, 0 }, { "lcd0-pwm", IMX_SC_R_LCD_0_PWM_0, 1, true, 0 }, { "lpuart", IMX_SC_R_UART_0, 4, true, 0 }, @@ -207,6 +216,23 @@ to_imx_sc_pd(struct generic_pm_domain *genpd) return container_of(genpd, struct imx_sc_pm_domain, pd); } +static void imx_sc_pd_get_console_rsrc(void) +{ + struct of_phandle_args specs; + int ret; + + if (!of_stdout) + return; + + ret = of_parse_phandle_with_args(of_stdout, "power-domains", + "#power-domain-cells", + 0, &specs); + if (ret) + return; + + imx_con_rsrc = specs.args[0]; +} + static int imx_sc_pd_power(struct generic_pm_domain *domain, bool power_on) { struct imx_sc_msg_req_set_resource_power_mode msg; @@ -267,6 +293,7 @@ imx_scu_add_pm_domain(struct device *dev, int idx, const struct imx_sc_pd_range *pd_ranges) { struct imx_sc_pm_domain *sc_pd; + bool is_off = true; int ret; if (!imx_sc_rm_is_resource_owned(pm_ipc_handle, pd_ranges->rsrc + idx)) @@ -288,6 +315,10 @@ imx_scu_add_pm_domain(struct device *dev, int idx, "%s", pd_ranges->name); sc_pd->pd.name = sc_pd->name; + if (imx_con_rsrc == sc_pd->rsrc) { + sc_pd->pd.flags = GENPD_FLAG_RPM_ALWAYS_ON; + is_off = false; + } if (sc_pd->rsrc >= IMX_SC_R_LAST) { dev_warn(dev, "invalid pd %s rsrc id %d found", @@ -297,7 +328,7 @@ imx_scu_add_pm_domain(struct device *dev, int idx, return NULL; } - ret = pm_genpd_init(&sc_pd->pd, NULL, true); + ret = pm_genpd_init(&sc_pd->pd, NULL, is_off); if (ret) { dev_warn(dev, "failed to init pd %s rsrc id %d", sc_pd->name, sc_pd->rsrc); @@ -363,6 +394,8 @@ static int imx_sc_pd_probe(struct platform_device *pdev) if (!pd_soc) return -ENODEV; + imx_sc_pd_get_console_rsrc(); + return imx_scu_init_pm_domains(&pdev->dev, pd_soc); } diff --git a/drivers/firmware/raspberrypi.c b/drivers/firmware/raspberrypi.c index 30259dc9b805..250e01680742 100644 --- a/drivers/firmware/raspberrypi.c +++ b/drivers/firmware/raspberrypi.c @@ -7,6 +7,7 @@ */ #include <linux/dma-mapping.h> +#include <linux/kref.h> #include <linux/mailbox_client.h> #include <linux/module.h> #include <linux/of_platform.h> @@ -27,6 +28,8 @@ struct rpi_firmware { struct mbox_chan *chan; /* The property channel. */ struct completion c; u32 enabled; + + struct kref consumers; }; static DEFINE_MUTEX(transaction_lock); @@ -225,12 +228,38 @@ static void rpi_register_clk_driver(struct device *dev) -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; @@ -247,6 +276,7 @@ static int rpi_firmware_probe(struct platform_device *pdev) } init_completion(&fw->c); + kref_init(&fw->consumers); platform_set_drvdata(pdev, fw); @@ -275,7 +305,8 @@ static int rpi_firmware_remove(struct platform_device *pdev) rpi_hwmon = NULL; platform_device_unregister(rpi_clk); rpi_clk = NULL; - mbox_free_channel(fw->chan); + + rpi_firmware_put(fw); return 0; } @@ -284,19 +315,51 @@ static int rpi_firmware_remove(struct platform_device *pdev) * 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) + return NULL; + + if (!kref_get_unless_zero(&fw->consumers)) + return NULL; + + return fw; } 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", }, {}, |