diff options
| author | Linus Torvalds <torvalds@linux-foundation.org> | 2018-12-31 20:32:35 -0500 |
|---|---|---|
| committer | Linus Torvalds <torvalds@linux-foundation.org> | 2018-12-31 20:32:35 -0500 |
| commit | d36377c6eb071e3d0751e9e0e3c19198c58d9a5d (patch) | |
| tree | bf1d28abd5fac5c826079c4b760ca34263f0fab4 /drivers/firmware | |
| parent | 0922275ef157ba8ac93e7e7857087eb0442d5397 (diff) | |
| parent | a6f119a06960ef1dc30570401e43b71f9ebdd2c2 (diff) | |
Merge tag 'armsoc-drivers' of git://git.kernel.org/pub/scm/linux/kernel/git/arm/arm-soc
Pull ARM SoC driver updates from Olof Johansson:
"Misc driver updates for platforms, many of them power related.
- Rockchip adds power domain support for rk3066 and rk3188
- Amlogic adds a power measurement driver
- Allwinner adds SRAM support for three platforms (F1C100, H5, A64
C1)
- Wakeup and ti-sysc (platform bus) fixes for OMAP/DRA7
- Broadcom fixes suspend/resume with Thumb2 kernels, and improves
stability of a handful of firmware/platform interfaces
- PXA completes their conversion to dmaengine framework
- Renesas does a bunch of PM cleanups across many platforms
- Tegra adds support for suspend/resume on T186/T194, which includes
some driver cleanups and addition of wake events
- Tegra also adds a driver for memory controller (EMC) on Tegra2
- i.MX tweaks power domain bindings, and adds support for i.MX8MQ in
GPC
- Atmel adds identifiers and LPDDR2 support for a new SoC, SAM9X60
and misc cleanups across several platforms"
* tag 'armsoc-drivers' of git://git.kernel.org/pub/scm/linux/kernel/git/arm/arm-soc: (73 commits)
ARM: at91: add support in soc driver for new SAM9X60
ARM: at91: add support in soc driver for LPDDR2 SiP
memory: omap-gpmc: Use of_node_name_eq for node name comparisons
bus: ti-sysc: Check for no-reset and no-idle flags at the child level
ARM: OMAP2+: Check also the first dts child for hwmod flags
soc: amlogic: meson-clk-measure: Add missing REGMAP_MMIO dependency
soc: imx: gpc: Increase GPC_CLK_MAX to 7
soc: renesas: rcar-sysc: Fix power domain control after system resume
soc: renesas: rcar-sysc: Merge PM Domain registration and linking
soc: renesas: rcar-sysc: Remove rcar_sysc_power_{down,up}() helpers
soc: renesas: r8a77990-sysc: Fix initialization order of 3DG-{A,B}
dt-bindings: sram: sunxi: Add compatible for the A64 SRAM C1
dt-bindings: sram: sunxi: Add bindings for the H5 with SRAM C1
dt-bindings: sram: Add Allwinner suniv F1C100s
soc: sunxi: sram: Add support for the H5 SoC system control
soc: sunxi: sram: Enable EMAC clock access for H3 variant
soc: imx: gpcv2: add support for i.MX8MQ SoC
soc: imx: gpcv2: move register access table to domain data
soc: imx: gpcv2: prefix i.MX7 specific defines
dmaengine: pxa: make the filter function internal
...
Diffstat (limited to 'drivers/firmware')
| -rw-r--r-- | drivers/firmware/imx/Kconfig | 6 | ||||
| -rw-r--r-- | drivers/firmware/imx/Makefile | 3 | ||||
| -rw-r--r-- | drivers/firmware/imx/scu-pd.c | 339 | ||||
| -rw-r--r-- | drivers/firmware/raspberrypi.c | 48 | ||||
| -rw-r--r-- | drivers/firmware/tegra/bpmp-debugfs.c | 29 | ||||
| -rw-r--r-- | drivers/firmware/tegra/bpmp.c | 77 |
6 files changed, 442 insertions, 60 deletions
diff --git a/drivers/firmware/imx/Kconfig b/drivers/firmware/imx/Kconfig index b170c2851e48..6a7a7c2c5b5f 100644 --- a/drivers/firmware/imx/Kconfig +++ b/drivers/firmware/imx/Kconfig | |||
| @@ -9,3 +9,9 @@ config IMX_SCU | |||
| 9 | 9 | ||
| 10 | This driver manages the IPC interface between host CPU and the | 10 | This driver manages the IPC interface between host CPU and the |
| 11 | SCU firmware running on M4. | 11 | SCU firmware running on M4. |
| 12 | |||
| 13 | config IMX_SCU_PD | ||
| 14 | bool "IMX SCU Power Domain driver" | ||
| 15 | depends on IMX_SCU | ||
| 16 | help | ||
| 17 | The System Controller Firmware (SCFW) based power domain driver. | ||
diff --git a/drivers/firmware/imx/Makefile b/drivers/firmware/imx/Makefile index 0ac04dfda8d4..1b2e15b3c9ca 100644 --- a/drivers/firmware/imx/Makefile +++ b/drivers/firmware/imx/Makefile | |||
| @@ -1,2 +1,3 @@ | |||
| 1 | # SPDX-License-Identifier: GPL-2.0 | 1 | # SPDX-License-Identifier: GPL-2.0 |
| 2 | obj-$(CONFIG_IMX_SCU) += imx-scu.o misc.o | 2 | obj-$(CONFIG_IMX_SCU) += imx-scu.o misc.o |
| 3 | obj-$(CONFIG_IMX_SCU_PD) += scu-pd.o | ||
diff --git a/drivers/firmware/imx/scu-pd.c b/drivers/firmware/imx/scu-pd.c new file mode 100644 index 000000000000..407245f2efd0 --- /dev/null +++ b/drivers/firmware/imx/scu-pd.c | |||
| @@ -0,0 +1,339 @@ | |||
| 1 | // SPDX-License-Identifier: GPL-2.0+ | ||
| 2 | /* | ||
| 3 | * Copyright (C) 2016 Freescale Semiconductor, Inc. | ||
| 4 | * Copyright 2017-2018 NXP | ||
| 5 | * Dong Aisheng <aisheng.dong@nxp.com> | ||
| 6 | * | ||
| 7 | * Implementation of the SCU based Power Domains | ||
| 8 | * | ||
| 9 | * NOTE: a better implementation suggested by Ulf Hansson is using a | ||
| 10 | * single global power domain and implement the ->attach|detach_dev() | ||
| 11 | * callback for the genpd and use the regular of_genpd_add_provider_simple(). | ||
| 12 | * From within the ->attach_dev(), we could get the OF node for | ||
| 13 | * the device that is being attached and then parse the power-domain | ||
| 14 | * cell containing the "resource id" and store that in the per device | ||
| 15 | * struct generic_pm_domain_data (we have void pointer there for | ||
| 16 | * storing these kind of things). | ||
| 17 | * | ||
| 18 | * Additionally, we need to implement the ->stop() and ->start() | ||
| 19 | * callbacks of genpd, which is where you "power on/off" devices, | ||
| 20 | * rather than using the above ->power_on|off() callbacks. | ||
| 21 | * | ||
| 22 | * However, there're two known issues: | ||
| 23 | * 1. The ->attach_dev() of power domain infrastructure still does | ||
| 24 | * not support multi domains case as the struct device *dev passed | ||
| 25 | * in is a virtual PD device, it does not help for parsing the real | ||
| 26 | * device resource id from device tree, so it's unware of which | ||
| 27 | * real sub power domain of device should be attached. | ||
| 28 | * | ||
| 29 | * The framework needs some proper extension to support multi power | ||
| 30 | * domain cases. | ||
| 31 | * | ||
| 32 | * 2. It also breaks most of current drivers as the driver probe sequence | ||
| 33 | * behavior changed if removing ->power_on|off() callback and use | ||
| 34 | * ->start() and ->stop() instead. genpd_dev_pm_attach will only power | ||
| 35 | * up the domain and attach device, but will not call .start() which | ||
| 36 | * relies on device runtime pm. That means the device power is still | ||
| 37 | * not up before running driver probe function. For SCU enabled | ||
| 38 | * platforms, all device drivers accessing registers/clock without power | ||
| 39 | * domain enabled will trigger a HW access error. That means we need fix | ||
| 40 | * most drivers probe sequence with proper runtime pm. | ||
| 41 | * | ||
| 42 | * In summary, we need fix above two issue before being able to switch to | ||
| 43 | * the "single global power domain" way. | ||
| 44 | * | ||
| 45 | */ | ||
| 46 | |||
| 47 | #include <dt-bindings/firmware/imx/rsrc.h> | ||
| 48 | #include <linux/firmware/imx/sci.h> | ||
| 49 | #include <linux/io.h> | ||
| 50 | #include <linux/module.h> | ||
| 51 | #include <linux/of.h> | ||
| 52 | #include <linux/of_address.h> | ||
| 53 | #include <linux/of_platform.h> | ||
| 54 | #include <linux/platform_device.h> | ||
| 55 | #include <linux/pm.h> | ||
| 56 | #include <linux/pm_domain.h> | ||
| 57 | #include <linux/slab.h> | ||
| 58 | |||
| 59 | /* SCU Power Mode Protocol definition */ | ||
| 60 | struct imx_sc_msg_req_set_resource_power_mode { | ||
| 61 | struct imx_sc_rpc_msg hdr; | ||
| 62 | u16 resource; | ||
| 63 | u8 mode; | ||
| 64 | } __packed; | ||
| 65 | |||
| 66 | #define IMX_SCU_PD_NAME_SIZE 20 | ||
| 67 | struct imx_sc_pm_domain { | ||
| 68 | struct generic_pm_domain pd; | ||
| 69 | char name[IMX_SCU_PD_NAME_SIZE]; | ||
| 70 | u32 rsrc; | ||
| 71 | }; | ||
| 72 | |||
| 73 | struct imx_sc_pd_range { | ||
| 74 | char *name; | ||
| 75 | u32 rsrc; | ||
| 76 | u8 num; | ||
| 77 | bool postfix; | ||
| 78 | }; | ||
| 79 | |||
| 80 | struct imx_sc_pd_soc { | ||
| 81 | const struct imx_sc_pd_range *pd_ranges; | ||
| 82 | u8 num_ranges; | ||
| 83 | }; | ||
| 84 | |||
| 85 | static const struct imx_sc_pd_range imx8qxp_scu_pd_ranges[] = { | ||
| 86 | /* LSIO SS */ | ||
| 87 | { "lsio-pwm", IMX_SC_R_PWM_0, 8, 1 }, | ||
| 88 | { "lsio-gpio", IMX_SC_R_GPIO_0, 8, 1 }, | ||
| 89 | { "lsio-gpt", IMX_SC_R_GPT_0, 5, 1 }, | ||
| 90 | { "lsio-kpp", IMX_SC_R_KPP, 1, 0 }, | ||
| 91 | { "lsio-fspi", IMX_SC_R_FSPI_0, 2, 1 }, | ||
| 92 | { "lsio-mu", IMX_SC_R_MU_0A, 14, 1 }, | ||
| 93 | |||
| 94 | /* CONN SS */ | ||
| 95 | { "con-usb", IMX_SC_R_USB_0, 2, 1 }, | ||
| 96 | { "con-usb0phy", IMX_SC_R_USB_0_PHY, 1, 0 }, | ||
| 97 | { "con-usb2", IMX_SC_R_USB_2, 1, 0 }, | ||
| 98 | { "con-usb2phy", IMX_SC_R_USB_2_PHY, 1, 0 }, | ||
| 99 | { "con-sdhc", IMX_SC_R_SDHC_0, 3, 1 }, | ||
| 100 | { "con-enet", IMX_SC_R_ENET_0, 2, 1 }, | ||
| 101 | { "con-nand", IMX_SC_R_NAND, 1, 0 }, | ||
| 102 | { "con-mlb", IMX_SC_R_MLB_0, 1, 1 }, | ||
| 103 | |||
| 104 | /* Audio DMA SS */ | ||
| 105 | { "adma-audio-pll0", IMX_SC_R_AUDIO_PLL_0, 1, 0 }, | ||
| 106 | { "adma-audio-pll1", IMX_SC_R_AUDIO_PLL_1, 1, 0 }, | ||
| 107 | { "adma-audio-clk-0", IMX_SC_R_AUDIO_CLK_0, 1, 0 }, | ||
| 108 | { "adma-dma0-ch", IMX_SC_R_DMA_0_CH0, 16, 1 }, | ||
| 109 | { "adma-dma1-ch", IMX_SC_R_DMA_1_CH0, 16, 1 }, | ||
| 110 | { "adma-dma2-ch", IMX_SC_R_DMA_2_CH0, 5, 1 }, | ||
| 111 | { "adma-asrc0", IMX_SC_R_ASRC_0, 1, 0 }, | ||
| 112 | { "adma-asrc1", IMX_SC_R_ASRC_1, 1, 0 }, | ||
| 113 | { "adma-esai0", IMX_SC_R_ESAI_0, 1, 0 }, | ||
| 114 | { "adma-spdif0", IMX_SC_R_SPDIF_0, 1, 0 }, | ||
| 115 | { "adma-sai", IMX_SC_R_SAI_0, 3, 1 }, | ||
| 116 | { "adma-amix", IMX_SC_R_AMIX, 1, 0 }, | ||
| 117 | { "adma-mqs0", IMX_SC_R_MQS_0, 1, 0 }, | ||
| 118 | { "adma-dsp", IMX_SC_R_DSP, 1, 0 }, | ||
| 119 | { "adma-dsp-ram", IMX_SC_R_DSP_RAM, 1, 0 }, | ||
| 120 | { "adma-can", IMX_SC_R_CAN_0, 3, 1 }, | ||
| 121 | { "adma-ftm", IMX_SC_R_FTM_0, 2, 1 }, | ||
| 122 | { "adma-lpi2c", IMX_SC_R_I2C_0, 4, 1 }, | ||
| 123 | { "adma-adc", IMX_SC_R_ADC_0, 1, 1 }, | ||
| 124 | { "adma-lcd", IMX_SC_R_LCD_0, 1, 1 }, | ||
| 125 | { "adma-lcd0-pwm", IMX_SC_R_LCD_0_PWM_0, 1, 1 }, | ||
| 126 | { "adma-lpuart", IMX_SC_R_UART_0, 4, 1 }, | ||
| 127 | { "adma-lpspi", IMX_SC_R_SPI_0, 4, 1 }, | ||
| 128 | |||
| 129 | /* VPU SS */ | ||
| 130 | { "vpu", IMX_SC_R_VPU, 1, 0 }, | ||
| 131 | { "vpu-pid", IMX_SC_R_VPU_PID0, 8, 1 }, | ||
| 132 | { "vpu-dec0", IMX_SC_R_VPU_DEC_0, 1, 0 }, | ||
| 133 | { "vpu-enc0", IMX_SC_R_VPU_ENC_0, 1, 0 }, | ||
| 134 | |||
| 135 | /* GPU SS */ | ||
| 136 | { "gpu0-pid", IMX_SC_R_GPU_0_PID0, 4, 1 }, | ||
| 137 | |||
| 138 | /* HSIO SS */ | ||
| 139 | { "hsio-pcie-b", IMX_SC_R_PCIE_B, 1, 0 }, | ||
| 140 | { "hsio-serdes-1", IMX_SC_R_SERDES_1, 1, 0 }, | ||
| 141 | { "hsio-gpio", IMX_SC_R_HSIO_GPIO, 1, 0 }, | ||
| 142 | |||
| 143 | /* MIPI/LVDS SS */ | ||
| 144 | { "mipi0", IMX_SC_R_MIPI_0, 1, 0 }, | ||
| 145 | { "mipi0-pwm0", IMX_SC_R_MIPI_0_PWM_0, 1, 0 }, | ||
| 146 | { "mipi0-i2c", IMX_SC_R_MIPI_0_I2C_0, 2, 1 }, | ||
| 147 | { "lvds0", IMX_SC_R_LVDS_0, 1, 0 }, | ||
| 148 | |||
| 149 | /* DC SS */ | ||
| 150 | { "dc0", IMX_SC_R_DC_0, 1, 0 }, | ||
| 151 | { "dc0-pll", IMX_SC_R_DC_0_PLL_0, 2, 1 }, | ||
| 152 | }; | ||
| 153 | |||
| 154 | static const struct imx_sc_pd_soc imx8qxp_scu_pd = { | ||
| 155 | .pd_ranges = imx8qxp_scu_pd_ranges, | ||
| 156 | .num_ranges = ARRAY_SIZE(imx8qxp_scu_pd_ranges), | ||
| 157 | }; | ||
| 158 | |||
| 159 | static struct imx_sc_ipc *pm_ipc_handle; | ||
| 160 | |||
| 161 | static inline struct imx_sc_pm_domain * | ||
| 162 | to_imx_sc_pd(struct generic_pm_domain *genpd) | ||
| 163 | { | ||
| 164 | return container_of(genpd, struct imx_sc_pm_domain, pd); | ||
| 165 | } | ||
| 166 | |||
| 167 | static int imx_sc_pd_power(struct generic_pm_domain *domain, bool power_on) | ||
| 168 | { | ||
| 169 | struct imx_sc_msg_req_set_resource_power_mode msg; | ||
| 170 | struct imx_sc_rpc_msg *hdr = &msg.hdr; | ||
| 171 | struct imx_sc_pm_domain *pd; | ||
| 172 | int ret; | ||
| 173 | |||
| 174 | pd = to_imx_sc_pd(domain); | ||
| 175 | |||
| 176 | hdr->ver = IMX_SC_RPC_VERSION; | ||
| 177 | hdr->svc = IMX_SC_RPC_SVC_PM; | ||
| 178 | hdr->func = IMX_SC_PM_FUNC_SET_RESOURCE_POWER_MODE; | ||
| 179 | hdr->size = 2; | ||
| 180 | |||
| 181 | msg.resource = pd->rsrc; | ||
| 182 | msg.mode = power_on ? IMX_SC_PM_PW_MODE_ON : IMX_SC_PM_PW_MODE_LP; | ||
| 183 | |||
| 184 | ret = imx_scu_call_rpc(pm_ipc_handle, &msg, true); | ||
| 185 | if (ret) | ||
| 186 | dev_err(&domain->dev, "failed to power %s resource %d ret %d\n", | ||
| 187 | power_on ? "up" : "off", pd->rsrc, ret); | ||
| 188 | |||
| 189 | return ret; | ||
| 190 | } | ||
| 191 | |||
| 192 | static int imx_sc_pd_power_on(struct generic_pm_domain *domain) | ||
| 193 | { | ||
| 194 | return imx_sc_pd_power(domain, true); | ||
| 195 | } | ||
| 196 | |||
| 197 | static int imx_sc_pd_power_off(struct generic_pm_domain *domain) | ||
| 198 | { | ||
| 199 | return imx_sc_pd_power(domain, false); | ||
| 200 | } | ||
| 201 | |||
| 202 | static struct generic_pm_domain *imx_scu_pd_xlate(struct of_phandle_args *spec, | ||
| 203 | void *data) | ||
| 204 | { | ||
| 205 | struct generic_pm_domain *domain = ERR_PTR(-ENOENT); | ||
| 206 | struct genpd_onecell_data *pd_data = data; | ||
| 207 | unsigned int i; | ||
| 208 | |||
| 209 | for (i = 0; i < pd_data->num_domains; i++) { | ||
| 210 | struct imx_sc_pm_domain *sc_pd; | ||
| 211 | |||
| 212 | sc_pd = to_imx_sc_pd(pd_data->domains[i]); | ||
| 213 | if (sc_pd->rsrc == spec->args[0]) { | ||
| 214 | domain = &sc_pd->pd; | ||
| 215 | break; | ||
| 216 | } | ||
| 217 | } | ||
| 218 | |||
| 219 | return domain; | ||
| 220 | } | ||
| 221 | |||
| 222 | static struct imx_sc_pm_domain * | ||
| 223 | imx_scu_add_pm_domain(struct device *dev, int idx, | ||
| 224 | const struct imx_sc_pd_range *pd_ranges) | ||
| 225 | { | ||
| 226 | struct imx_sc_pm_domain *sc_pd; | ||
| 227 | int ret; | ||
| 228 | |||
| 229 | sc_pd = devm_kzalloc(dev, sizeof(*sc_pd), GFP_KERNEL); | ||
| 230 | if (!sc_pd) | ||
| 231 | return ERR_PTR(-ENOMEM); | ||
| 232 | |||
| 233 | sc_pd->rsrc = pd_ranges->rsrc + idx; | ||
| 234 | sc_pd->pd.power_off = imx_sc_pd_power_off; | ||
| 235 | sc_pd->pd.power_on = imx_sc_pd_power_on; | ||
| 236 | |||
| 237 | if (pd_ranges->postfix) | ||
| 238 | snprintf(sc_pd->name, sizeof(sc_pd->name), | ||
| 239 | "%s%i", pd_ranges->name, idx); | ||
| 240 | else | ||
| 241 | snprintf(sc_pd->name, sizeof(sc_pd->name), | ||
| 242 | "%s", pd_ranges->name); | ||
| 243 | |||
| 244 | sc_pd->pd.name = sc_pd->name; | ||
| 245 | |||
| 246 | if (sc_pd->rsrc >= IMX_SC_R_LAST) { | ||
| 247 | dev_warn(dev, "invalid pd %s rsrc id %d found", | ||
| 248 | sc_pd->name, sc_pd->rsrc); | ||
| 249 | |||
| 250 | devm_kfree(dev, sc_pd); | ||
| 251 | return NULL; | ||
| 252 | } | ||
| 253 | |||
| 254 | ret = pm_genpd_init(&sc_pd->pd, NULL, true); | ||
| 255 | if (ret) { | ||
| 256 | dev_warn(dev, "failed to init pd %s rsrc id %d", | ||
| 257 | sc_pd->name, sc_pd->rsrc); | ||
| 258 | devm_kfree(dev, sc_pd); | ||
| 259 | return NULL; | ||
| 260 | } | ||
| 261 | |||
| 262 | return sc_pd; | ||
| 263 | } | ||
| 264 | |||
| 265 | static int imx_scu_init_pm_domains(struct device *dev, | ||
| 266 | const struct imx_sc_pd_soc *pd_soc) | ||
| 267 | { | ||
| 268 | const struct imx_sc_pd_range *pd_ranges = pd_soc->pd_ranges; | ||
| 269 | struct generic_pm_domain **domains; | ||
| 270 | struct genpd_onecell_data *pd_data; | ||
| 271 | struct imx_sc_pm_domain *sc_pd; | ||
| 272 | u32 count = 0; | ||
| 273 | int i, j; | ||
| 274 | |||
| 275 | for (i = 0; i < pd_soc->num_ranges; i++) | ||
| 276 | count += pd_ranges[i].num; | ||
| 277 | |||
| 278 | domains = devm_kcalloc(dev, count, sizeof(*domains), GFP_KERNEL); | ||
| 279 | if (!domains) | ||
| 280 | return -ENOMEM; | ||
| 281 | |||
| 282 | pd_data = devm_kzalloc(dev, sizeof(*pd_data), GFP_KERNEL); | ||
| 283 | if (!pd_data) | ||
| 284 | return -ENOMEM; | ||
| 285 | |||
| 286 | count = 0; | ||
| 287 | for (i = 0; i < pd_soc->num_ranges; i++) { | ||
| 288 | for (j = 0; j < pd_ranges[i].num; j++) { | ||
| 289 | sc_pd = imx_scu_add_pm_domain(dev, j, &pd_ranges[i]); | ||
| 290 | if (IS_ERR_OR_NULL(sc_pd)) | ||
| 291 | continue; | ||
| 292 | |||
| 293 | domains[count++] = &sc_pd->pd; | ||
| 294 | dev_dbg(dev, "added power domain %s\n", sc_pd->pd.name); | ||
| 295 | } | ||
| 296 | } | ||
| 297 | |||
| 298 | pd_data->domains = domains; | ||
| 299 | pd_data->num_domains = count; | ||
| 300 | pd_data->xlate = imx_scu_pd_xlate; | ||
| 301 | |||
| 302 | of_genpd_add_provider_onecell(dev->of_node, pd_data); | ||
| 303 | |||
| 304 | return 0; | ||
| 305 | } | ||
| 306 | |||
| 307 | static int imx_sc_pd_probe(struct platform_device *pdev) | ||
| 308 | { | ||
| 309 | const struct imx_sc_pd_soc *pd_soc; | ||
| 310 | int ret; | ||
| 311 | |||
| 312 | ret = imx_scu_get_handle(&pm_ipc_handle); | ||
| 313 | if (ret) | ||
| 314 | return ret; | ||
| 315 | |||
| 316 | pd_soc = of_device_get_match_data(&pdev->dev); | ||
| 317 | if (!pd_soc) | ||
| 318 | return -ENODEV; | ||
| 319 | |||
| 320 | return imx_scu_init_pm_domains(&pdev->dev, pd_soc); | ||
| 321 | } | ||
| 322 | |||
| 323 | static const struct of_device_id imx_sc_pd_match[] = { | ||
| 324 | { .compatible = "fsl,imx8qxp-scu-pd", &imx8qxp_scu_pd}, | ||
| 325 | { /* sentinel */ } | ||
| 326 | }; | ||
| 327 | |||
| 328 | static struct platform_driver imx_sc_pd_driver = { | ||
| 329 | .driver = { | ||
| 330 | .name = "imx-scu-pd", | ||
| 331 | .of_match_table = imx_sc_pd_match, | ||
| 332 | }, | ||
| 333 | .probe = imx_sc_pd_probe, | ||
| 334 | }; | ||
| 335 | builtin_platform_driver(imx_sc_pd_driver); | ||
| 336 | |||
| 337 | MODULE_AUTHOR("Dong Aisheng <aisheng.dong@nxp.com>"); | ||
| 338 | MODULE_DESCRIPTION("IMX SCU Power Domain driver"); | ||
| 339 | MODULE_LICENSE("GPL v2"); | ||
diff --git a/drivers/firmware/raspberrypi.c b/drivers/firmware/raspberrypi.c index a200a2174611..a13558154ac3 100644 --- a/drivers/firmware/raspberrypi.c +++ b/drivers/firmware/raspberrypi.c | |||
| @@ -1,12 +1,9 @@ | |||
| 1 | // SPDX-License-Identifier: GPL-2.0 | ||
| 1 | /* | 2 | /* |
| 2 | * Defines interfaces for interacting wtih the Raspberry Pi firmware's | 3 | * Defines interfaces for interacting wtih the Raspberry Pi firmware's |
| 3 | * property channel. | 4 | * property channel. |
| 4 | * | 5 | * |
| 5 | * Copyright © 2015 Broadcom | 6 | * Copyright © 2015 Broadcom |
| 6 | * | ||
| 7 | * This program is free software; you can redistribute it and/or modify | ||
| 8 | * it under the terms of the GNU General Public License version 2 as | ||
| 9 | * published by the Free Software Foundation. | ||
| 10 | */ | 7 | */ |
| 11 | 8 | ||
| 12 | #include <linux/dma-mapping.h> | 9 | #include <linux/dma-mapping.h> |
| @@ -14,6 +11,7 @@ | |||
| 14 | #include <linux/module.h> | 11 | #include <linux/module.h> |
| 15 | #include <linux/of_platform.h> | 12 | #include <linux/of_platform.h> |
| 16 | #include <linux/platform_device.h> | 13 | #include <linux/platform_device.h> |
| 14 | #include <linux/slab.h> | ||
| 17 | #include <soc/bcm2835/raspberrypi-firmware.h> | 15 | #include <soc/bcm2835/raspberrypi-firmware.h> |
| 18 | 16 | ||
| 19 | #define MBOX_MSG(chan, data28) (((data28) & ~0xf) | ((chan) & 0xf)) | 17 | #define MBOX_MSG(chan, data28) (((data28) & ~0xf) | ((chan) & 0xf)) |
| @@ -21,8 +19,6 @@ | |||
| 21 | #define MBOX_DATA28(msg) ((msg) & ~0xf) | 19 | #define MBOX_DATA28(msg) ((msg) & ~0xf) |
| 22 | #define MBOX_CHAN_PROPERTY 8 | 20 | #define MBOX_CHAN_PROPERTY 8 |
| 23 | 21 | ||
| 24 | #define MAX_RPI_FW_PROP_BUF_SIZE 32 | ||
| 25 | |||
| 26 | static struct platform_device *rpi_hwmon; | 22 | static struct platform_device *rpi_hwmon; |
| 27 | 23 | ||
| 28 | struct rpi_firmware { | 24 | struct rpi_firmware { |
| @@ -56,8 +52,12 @@ rpi_firmware_transaction(struct rpi_firmware *fw, u32 chan, u32 data) | |||
| 56 | reinit_completion(&fw->c); | 52 | reinit_completion(&fw->c); |
| 57 | ret = mbox_send_message(fw->chan, &message); | 53 | ret = mbox_send_message(fw->chan, &message); |
| 58 | if (ret >= 0) { | 54 | if (ret >= 0) { |
| 59 | wait_for_completion(&fw->c); | 55 | if (wait_for_completion_timeout(&fw->c, HZ)) { |
| 60 | ret = 0; | 56 | ret = 0; |
| 57 | } else { | ||
| 58 | ret = -ETIMEDOUT; | ||
| 59 | WARN_ONCE(1, "Firmware transaction timeout"); | ||
| 60 | } | ||
| 61 | } else { | 61 | } else { |
| 62 | dev_err(fw->cl.dev, "mbox_send_message returned %d\n", ret); | 62 | dev_err(fw->cl.dev, "mbox_send_message returned %d\n", ret); |
| 63 | } | 63 | } |
| @@ -144,28 +144,30 @@ EXPORT_SYMBOL_GPL(rpi_firmware_property_list); | |||
| 144 | int rpi_firmware_property(struct rpi_firmware *fw, | 144 | int rpi_firmware_property(struct rpi_firmware *fw, |
| 145 | u32 tag, void *tag_data, size_t buf_size) | 145 | u32 tag, void *tag_data, size_t buf_size) |
| 146 | { | 146 | { |
| 147 | /* Single tags are very small (generally 8 bytes), so the | 147 | struct rpi_firmware_property_tag_header *header; |
| 148 | * stack should be safe. | ||
| 149 | */ | ||
| 150 | u8 data[sizeof(struct rpi_firmware_property_tag_header) + | ||
| 151 | MAX_RPI_FW_PROP_BUF_SIZE]; | ||
| 152 | struct rpi_firmware_property_tag_header *header = | ||
| 153 | (struct rpi_firmware_property_tag_header *)data; | ||
| 154 | int ret; | 148 | int ret; |
| 155 | 149 | ||
| 156 | if (WARN_ON(buf_size > sizeof(data) - sizeof(*header))) | 150 | /* Some mailboxes can use over 1k bytes. Rather than checking |
| 157 | return -EINVAL; | 151 | * size and using stack or kmalloc depending on requirements, |
| 152 | * just use kmalloc. Mailboxes don't get called enough to worry | ||
| 153 | * too much about the time taken in the allocation. | ||
| 154 | */ | ||
| 155 | void *data = kmalloc(sizeof(*header) + buf_size, GFP_KERNEL); | ||
| 158 | 156 | ||
| 157 | if (!data) | ||
| 158 | return -ENOMEM; | ||
| 159 | |||
| 160 | header = data; | ||
| 159 | header->tag = tag; | 161 | header->tag = tag; |
| 160 | header->buf_size = buf_size; | 162 | header->buf_size = buf_size; |
| 161 | header->req_resp_size = 0; | 163 | header->req_resp_size = 0; |
| 162 | memcpy(data + sizeof(struct rpi_firmware_property_tag_header), | 164 | memcpy(data + sizeof(*header), tag_data, buf_size); |
| 163 | tag_data, buf_size); | 165 | |
| 166 | ret = rpi_firmware_property_list(fw, data, buf_size + sizeof(*header)); | ||
| 167 | |||
| 168 | memcpy(tag_data, data + sizeof(*header), buf_size); | ||
| 164 | 169 | ||
| 165 | ret = rpi_firmware_property_list(fw, &data, buf_size + sizeof(*header)); | 170 | kfree(data); |
| 166 | memcpy(tag_data, | ||
| 167 | data + sizeof(struct rpi_firmware_property_tag_header), | ||
| 168 | buf_size); | ||
| 169 | 171 | ||
| 170 | return ret; | 172 | return ret; |
| 171 | } | 173 | } |
diff --git a/drivers/firmware/tegra/bpmp-debugfs.c b/drivers/firmware/tegra/bpmp-debugfs.c index f7f6a0a5cb07..a84df1a8ca2b 100644 --- a/drivers/firmware/tegra/bpmp-debugfs.c +++ b/drivers/firmware/tegra/bpmp-debugfs.c | |||
| @@ -379,33 +379,6 @@ static int create_debugfs_mirror(struct tegra_bpmp *bpmp, void *buf, | |||
| 379 | return err; | 379 | return err; |
| 380 | } | 380 | } |
| 381 | 381 | ||
| 382 | static int mrq_is_supported(struct tegra_bpmp *bpmp, unsigned int mrq) | ||
| 383 | { | ||
| 384 | struct mrq_query_abi_request req = { .mrq = cpu_to_le32(mrq) }; | ||
| 385 | struct mrq_query_abi_response resp; | ||
| 386 | struct tegra_bpmp_message msg = { | ||
| 387 | .mrq = MRQ_QUERY_ABI, | ||
| 388 | .tx = { | ||
| 389 | .data = &req, | ||
| 390 | .size = sizeof(req), | ||
| 391 | }, | ||
| 392 | .rx = { | ||
| 393 | .data = &resp, | ||
| 394 | .size = sizeof(resp), | ||
| 395 | }, | ||
| 396 | }; | ||
| 397 | int ret; | ||
| 398 | |||
| 399 | ret = tegra_bpmp_transfer(bpmp, &msg); | ||
| 400 | if (ret < 0) { | ||
| 401 | /* something went wrong; assume not supported */ | ||
| 402 | dev_warn(bpmp->dev, "tegra_bpmp_transfer failed (%d)\n", ret); | ||
| 403 | return 0; | ||
| 404 | } | ||
| 405 | |||
| 406 | return resp.status ? 0 : 1; | ||
| 407 | } | ||
| 408 | |||
| 409 | int tegra_bpmp_init_debugfs(struct tegra_bpmp *bpmp) | 382 | int tegra_bpmp_init_debugfs(struct tegra_bpmp *bpmp) |
| 410 | { | 383 | { |
| 411 | dma_addr_t phys; | 384 | dma_addr_t phys; |
| @@ -415,7 +388,7 @@ int tegra_bpmp_init_debugfs(struct tegra_bpmp *bpmp) | |||
| 415 | int ret; | 388 | int ret; |
| 416 | struct dentry *root; | 389 | struct dentry *root; |
| 417 | 390 | ||
| 418 | if (!mrq_is_supported(bpmp, MRQ_DEBUGFS)) | 391 | if (!tegra_bpmp_mrq_is_supported(bpmp, MRQ_DEBUGFS)) |
| 419 | return 0; | 392 | return 0; |
| 420 | 393 | ||
| 421 | root = debugfs_create_dir("bpmp", NULL); | 394 | root = debugfs_create_dir("bpmp", NULL); |
diff --git a/drivers/firmware/tegra/bpmp.c b/drivers/firmware/tegra/bpmp.c index a3d5b518c10e..689478b92bce 100644 --- a/drivers/firmware/tegra/bpmp.c +++ b/drivers/firmware/tegra/bpmp.c | |||
| @@ -28,6 +28,7 @@ | |||
| 28 | 28 | ||
| 29 | #define MSG_ACK BIT(0) | 29 | #define MSG_ACK BIT(0) |
| 30 | #define MSG_RING BIT(1) | 30 | #define MSG_RING BIT(1) |
| 31 | #define TAG_SZ 32 | ||
| 31 | 32 | ||
| 32 | static inline struct tegra_bpmp * | 33 | static inline struct tegra_bpmp * |
| 33 | mbox_client_to_bpmp(struct mbox_client *client) | 34 | mbox_client_to_bpmp(struct mbox_client *client) |
| @@ -470,6 +471,31 @@ unlock: | |||
| 470 | } | 471 | } |
| 471 | EXPORT_SYMBOL_GPL(tegra_bpmp_free_mrq); | 472 | EXPORT_SYMBOL_GPL(tegra_bpmp_free_mrq); |
| 472 | 473 | ||
| 474 | bool tegra_bpmp_mrq_is_supported(struct tegra_bpmp *bpmp, unsigned int mrq) | ||
| 475 | { | ||
| 476 | struct mrq_query_abi_request req = { .mrq = cpu_to_le32(mrq) }; | ||
| 477 | struct mrq_query_abi_response resp; | ||
| 478 | struct tegra_bpmp_message msg = { | ||
| 479 | .mrq = MRQ_QUERY_ABI, | ||
| 480 | .tx = { | ||
| 481 | .data = &req, | ||
| 482 | .size = sizeof(req), | ||
| 483 | }, | ||
| 484 | .rx = { | ||
| 485 | .data = &resp, | ||
| 486 | .size = sizeof(resp), | ||
| 487 | }, | ||
| 488 | }; | ||
| 489 | int ret; | ||
| 490 | |||
| 491 | ret = tegra_bpmp_transfer(bpmp, &msg); | ||
| 492 | if (ret || msg.rx.ret) | ||
| 493 | return false; | ||
| 494 | |||
| 495 | return resp.status == 0; | ||
| 496 | } | ||
| 497 | EXPORT_SYMBOL_GPL(tegra_bpmp_mrq_is_supported); | ||
| 498 | |||
| 473 | static void tegra_bpmp_mrq_handle_ping(unsigned int mrq, | 499 | static void tegra_bpmp_mrq_handle_ping(unsigned int mrq, |
| 474 | struct tegra_bpmp_channel *channel, | 500 | struct tegra_bpmp_channel *channel, |
| 475 | void *data) | 501 | void *data) |
| @@ -521,8 +547,9 @@ static int tegra_bpmp_ping(struct tegra_bpmp *bpmp) | |||
| 521 | return err; | 547 | return err; |
| 522 | } | 548 | } |
| 523 | 549 | ||
| 524 | static int tegra_bpmp_get_firmware_tag(struct tegra_bpmp *bpmp, char *tag, | 550 | /* deprecated version of tag query */ |
| 525 | size_t size) | 551 | static int tegra_bpmp_get_firmware_tag_old(struct tegra_bpmp *bpmp, char *tag, |
| 552 | size_t size) | ||
| 526 | { | 553 | { |
| 527 | struct mrq_query_tag_request request; | 554 | struct mrq_query_tag_request request; |
| 528 | struct tegra_bpmp_message msg; | 555 | struct tegra_bpmp_message msg; |
| @@ -531,7 +558,10 @@ static int tegra_bpmp_get_firmware_tag(struct tegra_bpmp *bpmp, char *tag, | |||
| 531 | void *virt; | 558 | void *virt; |
| 532 | int err; | 559 | int err; |
| 533 | 560 | ||
| 534 | virt = dma_alloc_coherent(bpmp->dev, MSG_DATA_MIN_SZ, &phys, | 561 | if (size != TAG_SZ) |
| 562 | return -EINVAL; | ||
| 563 | |||
| 564 | virt = dma_alloc_coherent(bpmp->dev, TAG_SZ, &phys, | ||
| 535 | GFP_KERNEL | GFP_DMA32); | 565 | GFP_KERNEL | GFP_DMA32); |
| 536 | if (!virt) | 566 | if (!virt) |
| 537 | return -ENOMEM; | 567 | return -ENOMEM; |
| @@ -549,13 +579,44 @@ static int tegra_bpmp_get_firmware_tag(struct tegra_bpmp *bpmp, char *tag, | |||
| 549 | local_irq_restore(flags); | 579 | local_irq_restore(flags); |
| 550 | 580 | ||
| 551 | if (err == 0) | 581 | if (err == 0) |
| 552 | strlcpy(tag, virt, size); | 582 | memcpy(tag, virt, TAG_SZ); |
| 553 | 583 | ||
| 554 | dma_free_coherent(bpmp->dev, MSG_DATA_MIN_SZ, virt, phys); | 584 | dma_free_coherent(bpmp->dev, TAG_SZ, virt, phys); |
| 555 | 585 | ||
| 556 | return err; | 586 | return err; |
| 557 | } | 587 | } |
| 558 | 588 | ||
| 589 | static int tegra_bpmp_get_firmware_tag(struct tegra_bpmp *bpmp, char *tag, | ||
| 590 | size_t size) | ||
| 591 | { | ||
| 592 | if (tegra_bpmp_mrq_is_supported(bpmp, MRQ_QUERY_FW_TAG)) { | ||
| 593 | struct mrq_query_fw_tag_response resp; | ||
| 594 | struct tegra_bpmp_message msg = { | ||
| 595 | .mrq = MRQ_QUERY_FW_TAG, | ||
| 596 | .rx = { | ||
| 597 | .data = &resp, | ||
| 598 | .size = sizeof(resp), | ||
| 599 | }, | ||
| 600 | }; | ||
| 601 | int err; | ||
| 602 | |||
| 603 | if (size != sizeof(resp.tag)) | ||
| 604 | return -EINVAL; | ||
| 605 | |||
| 606 | err = tegra_bpmp_transfer(bpmp, &msg); | ||
| 607 | |||
| 608 | if (err) | ||
| 609 | return err; | ||
| 610 | if (msg.rx.ret < 0) | ||
| 611 | return -EINVAL; | ||
| 612 | |||
| 613 | memcpy(tag, resp.tag, sizeof(resp.tag)); | ||
| 614 | return 0; | ||
| 615 | } | ||
| 616 | |||
| 617 | return tegra_bpmp_get_firmware_tag_old(bpmp, tag, size); | ||
| 618 | } | ||
| 619 | |||
| 559 | static void tegra_bpmp_channel_signal(struct tegra_bpmp_channel *channel) | 620 | static void tegra_bpmp_channel_signal(struct tegra_bpmp_channel *channel) |
| 560 | { | 621 | { |
| 561 | unsigned long flags = channel->ob->flags; | 622 | unsigned long flags = channel->ob->flags; |
| @@ -664,7 +725,7 @@ static int tegra_bpmp_probe(struct platform_device *pdev) | |||
| 664 | { | 725 | { |
| 665 | struct tegra_bpmp *bpmp; | 726 | struct tegra_bpmp *bpmp; |
| 666 | unsigned int i; | 727 | unsigned int i; |
| 667 | char tag[32]; | 728 | char tag[TAG_SZ]; |
| 668 | size_t size; | 729 | size_t size; |
| 669 | int err; | 730 | int err; |
| 670 | 731 | ||
| @@ -792,13 +853,13 @@ static int tegra_bpmp_probe(struct platform_device *pdev) | |||
| 792 | goto free_mrq; | 853 | goto free_mrq; |
| 793 | } | 854 | } |
| 794 | 855 | ||
| 795 | err = tegra_bpmp_get_firmware_tag(bpmp, tag, sizeof(tag) - 1); | 856 | err = tegra_bpmp_get_firmware_tag(bpmp, tag, sizeof(tag)); |
| 796 | if (err < 0) { | 857 | if (err < 0) { |
| 797 | dev_err(&pdev->dev, "failed to get firmware tag: %d\n", err); | 858 | dev_err(&pdev->dev, "failed to get firmware tag: %d\n", err); |
| 798 | goto free_mrq; | 859 | goto free_mrq; |
| 799 | } | 860 | } |
| 800 | 861 | ||
| 801 | dev_info(&pdev->dev, "firmware: %s\n", tag); | 862 | dev_info(&pdev->dev, "firmware: %.*s\n", (int)sizeof(tag), tag); |
| 802 | 863 | ||
| 803 | platform_set_drvdata(pdev, bpmp); | 864 | platform_set_drvdata(pdev, bpmp); |
| 804 | 865 | ||
