whiterose

linux unikernel
Log | Files | Refs | README | LICENSE | git clone https://git.ne02ptzero.me/git/whiterose

commit d36377c6eb071e3d0751e9e0e3c19198c58d9a5d
parent 0922275ef157ba8ac93e7e7857087eb0442d5397
Author: Linus Torvalds <torvalds@linux-foundation.org>
Date:   Mon, 31 Dec 2018 17:32:35 -0800

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:
MDocumentation/devicetree/bindings/bus/ti-sysc.txt | 1+
MDocumentation/devicetree/bindings/power/fsl,imx-gpcv2.txt | 7+++++--
ADocumentation/devicetree/bindings/soc/amlogic/clk-measure.txt | 18++++++++++++++++++
MDocumentation/devicetree/bindings/soc/qcom/qcom,smd-rpm.txt | 1+
MDocumentation/devicetree/bindings/soc/rockchip/power_domain.txt | 4++++
MDocumentation/devicetree/bindings/sram/sunxi-sram.txt | 9+++++++++
March/arm/mach-omap2/omap_hwmod.c | 23+++++++++++++++++------
Mdrivers/bus/brcmstb_gisb.c | 12++++--------
Mdrivers/bus/ti-sysc.c | 55++++++++++++++++++++++++++++++++++++++++++++++---------
Mdrivers/dma/pxa_dma.c | 5++---
Mdrivers/firmware/imx/Kconfig | 6++++++
Mdrivers/firmware/imx/Makefile | 3++-
Adrivers/firmware/imx/scu-pd.c | 339+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mdrivers/firmware/raspberrypi.c | 48+++++++++++++++++++++++++-----------------------
Mdrivers/firmware/tegra/bpmp-debugfs.c | 29+----------------------------
Mdrivers/firmware/tegra/bpmp.c | 77+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--------
Mdrivers/gpu/drm/msm/adreno/a6xx_gmu.c | 59++++++++++++++++++++++++-----------------------------------
Mdrivers/memory/omap-gpmc.c | 8++++----
Mdrivers/memory/tegra/Kconfig | 10++++++++++
Mdrivers/memory/tegra/Makefile | 1+
Adrivers/memory/tegra/tegra20-emc.c | 591+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mdrivers/soc/Makefile | 2+-
Mdrivers/soc/amlogic/Kconfig | 9+++++++++
Mdrivers/soc/amlogic/Makefile | 1+
Adrivers/soc/amlogic/meson-clk-measure.c | 350+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mdrivers/soc/atmel/soc.c | 10++++++++++
Mdrivers/soc/atmel/soc.h | 7+++++++
Mdrivers/soc/bcm/brcmstb/common.c | 6+++++-
Mdrivers/soc/bcm/brcmstb/pm/pm-arm.c | 2+-
Mdrivers/soc/bcm/raspberrypi-power.c | 5+----
Mdrivers/soc/imx/Kconfig | 6+++---
Mdrivers/soc/imx/Makefile | 2+-
Mdrivers/soc/imx/gpc.c | 2+-
Mdrivers/soc/imx/gpcv2.c | 296+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------------
Mdrivers/soc/mediatek/Kconfig | 12++++++++++++
Mdrivers/soc/mediatek/Makefile | 1+
Adrivers/soc/mediatek/mtk-cmdq-helper.c | 300+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mdrivers/soc/qcom/Kconfig | 5-----
Mdrivers/soc/qcom/cmd-db.c | 93+++++++++++++++++++++++++------------------------------------------------------
Mdrivers/soc/qcom/llcc-slice.c | 17+++++++++++++++--
Mdrivers/soc/qcom/qcom-geni-se.c | 25+++++++++++++------------
Mdrivers/soc/qcom/qmi_interface.c | 2+-
Mdrivers/soc/qcom/smd-rpm.c | 1+
Mdrivers/soc/renesas/r8a77965-sysc.c | 1-
Mdrivers/soc/renesas/r8a77970-sysc.c | 5++---
Mdrivers/soc/renesas/r8a77980-sysc.c | 10+++++-----
Mdrivers/soc/renesas/r8a77990-sysc.c | 23++++-------------------
Mdrivers/soc/renesas/rcar-sysc.c | 65++++++++++++++++++-----------------------------------------------
Mdrivers/soc/rockchip/pm_domains.c | 48++++++++++++++++++++++++++++++++++++++++++++++++
Mdrivers/soc/sunxi/sunxi_sram.c | 22++++++++++------------
Mdrivers/soc/tegra/common.c | 6+++++-
Mdrivers/soc/tegra/pmc.c | 472+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------
Mdrivers/soc/ti/wkup_m3_ipc.c | 1+
Ainclude/dt-bindings/power/imx8mq-power.h | 21+++++++++++++++++++++
Minclude/dt-bindings/power/r8a77970-sysc.h | 7+++----
Minclude/dt-bindings/power/r8a77980-sysc.h | 6+++---
Minclude/dt-bindings/power/raspberrypi-power.h | 5+----
Ainclude/dt-bindings/power/rk3066-power.h | 22++++++++++++++++++++++
Ainclude/dt-bindings/power/rk3188-power.h | 24++++++++++++++++++++++++
Minclude/linux/dma/pxa-dma.h | 11-----------
Ainclude/linux/soc/mediatek/mtk-cmdq.h | 133+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Minclude/linux/soc/qcom/qmi.h | 2+-
Minclude/soc/bcm2835/raspberrypi-firmware.h | 5+----
Minclude/soc/qcom/cmd-db.h | 12+++---------
Minclude/soc/tegra/bpmp-abi.h | 1188+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----------------
Minclude/soc/tegra/bpmp.h | 7+++++++
Minclude/soc/tegra/fuse.h | 1-
Minclude/soc/tegra/pmc.h | 21+++++++++++++++++++++
68 files changed, 3910 insertions(+), 668 deletions(-)

diff --git a/Documentation/devicetree/bindings/bus/ti-sysc.txt b/Documentation/devicetree/bindings/bus/ti-sysc.txt @@ -35,6 +35,7 @@ Required standard properties: "ti,sysc-omap3-sham" "ti,sysc-omap-aes" "ti,sysc-mcasp" + "ti,sysc-dra7-mcasp" "ti,sysc-usb-host-fs" "ti,sysc-dra7-mcan" diff --git a/Documentation/devicetree/bindings/power/fsl,imx-gpcv2.txt b/Documentation/devicetree/bindings/power/fsl,imx-gpcv2.txt @@ -6,7 +6,9 @@ Control (PGC) for various power domains. Required properties: -- compatible: Should be "fsl,imx7d-gpc" +- compatible: Should be one of: + - "fsl,imx7d-gpc" + - "fsl,imx8mq-gpc" - reg: should be register base and length as documented in the datasheet @@ -22,7 +24,8 @@ which, in turn, is expected to contain the following: Required properties: - reg: Power domain index. Valid values are defined in - include/dt-bindings/power/imx7-power.h + include/dt-bindings/power/imx7-power.h for fsl,imx7d-gpc and + include/dt-bindings/power/imx8m-power.h for fsl,imx8mq-gpc - #power-domain-cells: Should be 0 diff --git a/Documentation/devicetree/bindings/soc/amlogic/clk-measure.txt b/Documentation/devicetree/bindings/soc/amlogic/clk-measure.txt @@ -0,0 +1,18 @@ +Amlogic Internal Clock Measurer +=============================== + +The Amlogic SoCs contains an IP to measure the internal clocks. +The precision is multiple of MHz, useful to debug the clock states. + +Required properties: +- compatible: Shall contain one of the following : + "amlogic,meson-gx-clk-measure" for GX SoCs + "amlogic,meson8-clk-measure" for Meson8 SoCs + "amlogic,meson8b-clk-measure" for Meson8b SoCs +- reg: base address and size of the Clock Measurer register space. + +Example: + clock-measure@8758 { + compatible = "amlogic,meson-gx-clk-measure"; + reg = <0x0 0x8758 0x0 0x10>; + }; diff --git a/Documentation/devicetree/bindings/soc/qcom/qcom,smd-rpm.txt b/Documentation/devicetree/bindings/soc/qcom/qcom,smd-rpm.txt @@ -23,6 +23,7 @@ resources. "qcom,rpm-msm8916" "qcom,rpm-msm8974" "qcom,rpm-msm8998" + "qcom,rpm-qcs404" - qcom,smd-channels: Usage: required diff --git a/Documentation/devicetree/bindings/soc/rockchip/power_domain.txt b/Documentation/devicetree/bindings/soc/rockchip/power_domain.txt @@ -7,7 +7,9 @@ Required properties for power domain controller: - compatible: Should be one of the following. "rockchip,px30-power-controller" - for PX30 SoCs. "rockchip,rk3036-power-controller" - for RK3036 SoCs. + "rockchip,rk3066-power-controller" - for RK3066 SoCs. "rockchip,rk3128-power-controller" - for RK3128 SoCs. + "rockchip,rk3188-power-controller" - for RK3188 SoCs. "rockchip,rk3228-power-controller" - for RK3228 SoCs. "rockchip,rk3288-power-controller" - for RK3288 SoCs. "rockchip,rk3328-power-controller" - for RK3328 SoCs. @@ -23,7 +25,9 @@ Required properties for power domain sub nodes: - reg: index of the power domain, should use macros in: "include/dt-bindings/power/px30-power.h" - for PX30 type power domain. "include/dt-bindings/power/rk3036-power.h" - for RK3036 type power domain. + "include/dt-bindings/power/rk3066-power.h" - for RK3066 type power domain. "include/dt-bindings/power/rk3128-power.h" - for RK3128 type power domain. + "include/dt-bindings/power/rk3188-power.h" - for RK3188 type power domain. "include/dt-bindings/power/rk3228-power.h" - for RK3228 type power domain. "include/dt-bindings/power/rk3288-power.h" - for RK3288 type power domain. "include/dt-bindings/power/rk3328-power.h" - for RK3328 type power domain. diff --git a/Documentation/devicetree/bindings/sram/sunxi-sram.txt b/Documentation/devicetree/bindings/sram/sunxi-sram.txt @@ -18,7 +18,9 @@ Required properties: - "allwinner,sun8i-h3-system-control" - "allwinner,sun50i-a64-sram-controller" (deprecated) - "allwinner,sun50i-a64-system-control" + - "allwinner,sun50i-h5-system-control" - "allwinner,sun50i-h6-system-control", "allwinner,sun50i-a64-system-control" + - "allwinner,suniv-f1c100s-system-control", "allwinner,sun4i-a10-system-control" - reg : sram controller register offset + length SRAM nodes @@ -54,10 +56,17 @@ The valid sections compatible for H3 are: The valid sections compatible for A64 are: - allwinner,sun50i-a64-sram-c + - allwinner,sun50i-a64-sram-c1, allwinner,sun4i-a10-sram-c1 + +The valid sections compatible for H5 are: + - allwinner,sun50i-h5-sram-c1, allwinner,sun4i-a10-sram-c1 The valid sections compatible for H6 are: - allwinner,sun50i-h6-sram-c, allwinner,sun50i-a64-sram-c +The valid sections compatible for F1C100s are: + - allwinner,suniv-f1c100s-sram-d, allwinner,sun4i-a10-sram-d + Devices using SRAM sections --------------------------- diff --git a/arch/arm/mach-omap2/omap_hwmod.c b/arch/arm/mach-omap2/omap_hwmod.c @@ -2345,6 +2345,17 @@ static int __init _init_mpu_rt_base(struct omap_hwmod *oh, void *data, return 0; } +static void __init parse_module_flags(struct omap_hwmod *oh, + struct device_node *np) +{ + if (of_find_property(np, "ti,no-reset-on-init", NULL)) + oh->flags |= HWMOD_INIT_NO_RESET; + if (of_find_property(np, "ti,no-idle-on-init", NULL)) + oh->flags |= HWMOD_INIT_NO_IDLE; + if (of_find_property(np, "ti,no-idle", NULL)) + oh->flags |= HWMOD_NO_IDLE; +} + /** * _init - initialize internal data for the hwmod @oh * @oh: struct omap_hwmod * @@ -2392,12 +2403,12 @@ static int __init _init(struct omap_hwmod *oh, void *data) } if (np) { - if (of_find_property(np, "ti,no-reset-on-init", NULL)) - oh->flags |= HWMOD_INIT_NO_RESET; - if (of_find_property(np, "ti,no-idle-on-init", NULL)) - oh->flags |= HWMOD_INIT_NO_IDLE; - if (of_find_property(np, "ti,no-idle", NULL)) - oh->flags |= HWMOD_NO_IDLE; + struct device_node *child; + + parse_module_flags(oh, np); + child = of_get_next_child(np, NULL); + if (child) + parse_module_flags(oh, child); } oh->_state = _HWMOD_STATE_INITIALIZED; diff --git a/drivers/bus/brcmstb_gisb.c b/drivers/bus/brcmstb_gisb.c @@ -150,8 +150,7 @@ static ssize_t gisb_arb_get_timeout(struct device *dev, struct device_attribute *attr, char *buf) { - struct platform_device *pdev = to_platform_device(dev); - struct brcmstb_gisb_arb_device *gdev = platform_get_drvdata(pdev); + struct brcmstb_gisb_arb_device *gdev = dev_get_drvdata(dev); u32 timeout; mutex_lock(&gdev->lock); @@ -165,8 +164,7 @@ static ssize_t gisb_arb_set_timeout(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { - struct platform_device *pdev = to_platform_device(dev); - struct brcmstb_gisb_arb_device *gdev = platform_get_drvdata(pdev); + struct brcmstb_gisb_arb_device *gdev = dev_get_drvdata(dev); int val, ret; ret = kstrtoint(buf, 10, &val); @@ -418,8 +416,7 @@ static int __init brcmstb_gisb_arb_probe(struct platform_device *pdev) #ifdef CONFIG_PM_SLEEP static int brcmstb_gisb_arb_suspend(struct device *dev) { - struct platform_device *pdev = to_platform_device(dev); - struct brcmstb_gisb_arb_device *gdev = platform_get_drvdata(pdev); + struct brcmstb_gisb_arb_device *gdev = dev_get_drvdata(dev); gdev->saved_timeout = gisb_read(gdev, ARB_TIMER); @@ -431,8 +428,7 @@ static int brcmstb_gisb_arb_suspend(struct device *dev) */ static int brcmstb_gisb_arb_resume_noirq(struct device *dev) { - struct platform_device *pdev = to_platform_device(dev); - struct brcmstb_gisb_arb_device *gdev = platform_get_drvdata(pdev); + struct brcmstb_gisb_arb_device *gdev = dev_get_drvdata(dev); gisb_write(gdev, gdev->saved_timeout, ARB_TIMER); diff --git a/drivers/bus/ti-sysc.c b/drivers/bus/ti-sysc.c @@ -91,6 +91,9 @@ struct sysc { struct delayed_work idle_work; }; +static void sysc_parse_dts_quirks(struct sysc *ddata, struct device_node *np, + bool is_child); + void sysc_write(struct sysc *ddata, int offset, u32 value) { writel_relaxed(value, ddata->module_va + offset); @@ -214,8 +217,13 @@ static int sysc_get_clocks(struct sysc *ddata) if (!ddata->clocks) return -ENOMEM; - for (i = 0; i < ddata->nr_clocks; i++) { - error = sysc_get_one_clock(ddata, ddata->clock_roles[i]); + for (i = 0; i < SYSC_MAX_CLOCKS; i++) { + const char *name = ddata->clock_roles[i]; + + if (!name) + continue; + + error = sysc_get_one_clock(ddata, name); if (error && error != -ENOENT) return error; } @@ -374,6 +382,7 @@ static int sysc_check_one_child(struct sysc *ddata, dev_warn(ddata->dev, "really a child ti,hwmods property?"); sysc_check_quirk_stdout(ddata, np); + sysc_parse_dts_quirks(ddata, np, true); return 0; } @@ -815,6 +824,7 @@ static const struct sysc_revision_quirk sysc_revision_quirks[] = { SYSC_QUIRK("ocp2scp", 0, 0, 0x10, 0x14, 0x50060005, 0xfffffff0, 0), SYSC_QUIRK("ocp2scp", 0, 0, -1, -1, 0x50060007, 0xffffffff, 0), SYSC_QUIRK("padconf", 0, 0, 0x10, -1, 0x4fff0800, 0xffffffff, 0), + SYSC_QUIRK("padconf", 0, 0, -1, -1, 0x40001100, 0xffffffff, 0), SYSC_QUIRK("prcm", 0, 0, -1, -1, 0x40000100, 0xffffffff, 0), SYSC_QUIRK("prcm", 0, 0, -1, -1, 0x00004102, 0xffffffff, 0), SYSC_QUIRK("prcm", 0, 0, -1, -1, 0x40000400, 0xffffffff, 0), @@ -833,7 +843,9 @@ static const struct sysc_revision_quirk sysc_revision_quirks[] = { SYSC_QUIRK("rtc", 0, 0x74, 0x78, -1, 0x4eb01908, 0xffff00f0, 0), SYSC_QUIRK("timer32k", 0, 0, 0x4, -1, 0x00000060, 0xffffffff, 0), SYSC_QUIRK("usbhstll", 0, 0, 0x10, 0x14, 0x00000004, 0xffffffff, 0), + SYSC_QUIRK("usbhstll", 0, 0, 0x10, 0x14, 0x00000008, 0xffffffff, 0), SYSC_QUIRK("usb_host_hs", 0, 0, 0x10, 0x14, 0x50700100, 0xffffffff, 0), + SYSC_QUIRK("usb_host_hs", 0, 0, 0x10, -1, 0x50700101, 0xffffffff, 0), SYSC_QUIRK("usb_otg_hs", 0, 0x400, 0x404, 0x408, 0x00000050, 0xffffffff, 0), SYSC_QUIRK("wdt", 0, 0, 0x10, 0x14, 0x502a0500, 0xfffff0f0, 0), @@ -1271,23 +1283,37 @@ static const struct sysc_dts_quirk sysc_dts_quirks[] = { .mask = SYSC_QUIRK_NO_RESET_ON_INIT, }, }; -static int sysc_init_dts_quirks(struct sysc *ddata) +static void sysc_parse_dts_quirks(struct sysc *ddata, struct device_node *np, + bool is_child) { - struct device_node *np = ddata->dev->of_node; const struct property *prop; - int i, len, error; - u32 val; - - ddata->legacy_mode = of_get_property(np, "ti,hwmods", NULL); + int i, len; for (i = 0; i < ARRAY_SIZE(sysc_dts_quirks); i++) { - prop = of_get_property(np, sysc_dts_quirks[i].name, &len); + const char *name = sysc_dts_quirks[i].name; + + prop = of_get_property(np, name, &len); if (!prop) continue; ddata->cfg.quirks |= sysc_dts_quirks[i].mask; + if (is_child) { + dev_warn(ddata->dev, + "dts flag should be at module level for %s\n", + name); + } } +} +static int sysc_init_dts_quirks(struct sysc *ddata) +{ + struct device_node *np = ddata->dev->of_node; + int error; + u32 val; + + ddata->legacy_mode = of_get_property(np, "ti,hwmods", NULL); + + sysc_parse_dts_quirks(ddata, np, false); error = of_property_read_u32(np, "ti,sysc-delay-us", &val); if (!error) { if (val > 255) { @@ -1498,6 +1524,16 @@ static const struct sysc_regbits sysc_regbits_omap4_mcasp = { static const struct sysc_capabilities sysc_omap4_mcasp = { .type = TI_SYSC_OMAP4_MCASP, .regbits = &sysc_regbits_omap4_mcasp, + .mod_quirks = SYSC_QUIRK_OPT_CLKS_NEEDED, +}; + +/* + * McASP found on dra7 and later + */ +static const struct sysc_capabilities sysc_dra7_mcasp = { + .type = TI_SYSC_OMAP4_SIMPLE, + .regbits = &sysc_regbits_omap4_simple, + .mod_quirks = SYSC_QUIRK_OPT_CLKS_NEEDED, }; /* @@ -1726,6 +1762,7 @@ static const struct of_device_id sysc_match[] = { { .compatible = "ti,sysc-omap3-sham", .data = &sysc_omap3_sham, }, { .compatible = "ti,sysc-omap-aes", .data = &sysc_omap3_aes, }, { .compatible = "ti,sysc-mcasp", .data = &sysc_omap4_mcasp, }, + { .compatible = "ti,sysc-dra7-mcasp", .data = &sysc_dra7_mcasp, }, { .compatible = "ti,sysc-usb-host-fs", .data = &sysc_omap4_usb_host_fs, }, { .compatible = "ti,sysc-dra7-mcan", .data = &sysc_dra7_mcan, }, diff --git a/drivers/dma/pxa_dma.c b/drivers/dma/pxa_dma.c @@ -179,7 +179,7 @@ static unsigned int pxad_drcmr(unsigned int line) return 0x1000 + line * 4; } -bool pxad_filter_fn(struct dma_chan *chan, void *param); +static bool pxad_filter_fn(struct dma_chan *chan, void *param); /* * Debug fs @@ -1500,7 +1500,7 @@ static struct platform_driver pxad_driver = { .remove = pxad_remove, }; -bool pxad_filter_fn(struct dma_chan *chan, void *param) +static bool pxad_filter_fn(struct dma_chan *chan, void *param) { struct pxad_chan *c = to_pxad_chan(chan); struct pxad_param *p = param; @@ -1513,7 +1513,6 @@ bool pxad_filter_fn(struct dma_chan *chan, void *param) return true; } -EXPORT_SYMBOL_GPL(pxad_filter_fn); module_platform_driver(pxad_driver); diff --git a/drivers/firmware/imx/Kconfig b/drivers/firmware/imx/Kconfig @@ -9,3 +9,9 @@ config IMX_SCU This driver manages the IPC interface between host CPU and the SCU firmware running on M4. + +config IMX_SCU_PD + bool "IMX SCU Power Domain driver" + depends on IMX_SCU + help + The System Controller Firmware (SCFW) based power domain driver. diff --git a/drivers/firmware/imx/Makefile b/drivers/firmware/imx/Makefile @@ -1,2 +1,3 @@ # SPDX-License-Identifier: GPL-2.0 -obj-$(CONFIG_IMX_SCU) += imx-scu.o misc.o +obj-$(CONFIG_IMX_SCU) += imx-scu.o misc.o +obj-$(CONFIG_IMX_SCU_PD) += scu-pd.o diff --git a/drivers/firmware/imx/scu-pd.c b/drivers/firmware/imx/scu-pd.c @@ -0,0 +1,339 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2016 Freescale Semiconductor, Inc. + * Copyright 2017-2018 NXP + * Dong Aisheng <aisheng.dong@nxp.com> + * + * Implementation of the SCU based Power Domains + * + * NOTE: a better implementation suggested by Ulf Hansson is using a + * single global power domain and implement the ->attach|detach_dev() + * callback for the genpd and use the regular of_genpd_add_provider_simple(). + * From within the ->attach_dev(), we could get the OF node for + * the device that is being attached and then parse the power-domain + * cell containing the "resource id" and store that in the per device + * struct generic_pm_domain_data (we have void pointer there for + * storing these kind of things). + * + * Additionally, we need to implement the ->stop() and ->start() + * callbacks of genpd, which is where you "power on/off" devices, + * rather than using the above ->power_on|off() callbacks. + * + * However, there're two known issues: + * 1. The ->attach_dev() of power domain infrastructure still does + * not support multi domains case as the struct device *dev passed + * in is a virtual PD device, it does not help for parsing the real + * device resource id from device tree, so it's unware of which + * real sub power domain of device should be attached. + * + * The framework needs some proper extension to support multi power + * domain cases. + * + * 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 + * up the domain and attach device, but will not call .start() which + * relies on device runtime pm. That means the device power is still + * not up before running driver probe function. For SCU enabled + * platforms, all device drivers accessing registers/clock without power + * 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. + * + */ + +#include <dt-bindings/firmware/imx/rsrc.h> +#include <linux/firmware/imx/sci.h> +#include <linux/io.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_address.h> +#include <linux/of_platform.h> +#include <linux/platform_device.h> +#include <linux/pm.h> +#include <linux/pm_domain.h> +#include <linux/slab.h> + +/* SCU Power Mode Protocol definition */ +struct imx_sc_msg_req_set_resource_power_mode { + struct imx_sc_rpc_msg hdr; + u16 resource; + u8 mode; +} __packed; + +#define IMX_SCU_PD_NAME_SIZE 20 +struct imx_sc_pm_domain { + struct generic_pm_domain pd; + char name[IMX_SCU_PD_NAME_SIZE]; + u32 rsrc; +}; + +struct imx_sc_pd_range { + char *name; + u32 rsrc; + u8 num; + bool postfix; +}; + +struct imx_sc_pd_soc { + const struct imx_sc_pd_range *pd_ranges; + u8 num_ranges; +}; + +static const struct imx_sc_pd_range imx8qxp_scu_pd_ranges[] = { + /* LSIO SS */ + { "lsio-pwm", IMX_SC_R_PWM_0, 8, 1 }, + { "lsio-gpio", IMX_SC_R_GPIO_0, 8, 1 }, + { "lsio-gpt", IMX_SC_R_GPT_0, 5, 1 }, + { "lsio-kpp", IMX_SC_R_KPP, 1, 0 }, + { "lsio-fspi", IMX_SC_R_FSPI_0, 2, 1 }, + { "lsio-mu", IMX_SC_R_MU_0A, 14, 1 }, + + /* CONN SS */ + { "con-usb", IMX_SC_R_USB_0, 2, 1 }, + { "con-usb0phy", IMX_SC_R_USB_0_PHY, 1, 0 }, + { "con-usb2", IMX_SC_R_USB_2, 1, 0 }, + { "con-usb2phy", IMX_SC_R_USB_2_PHY, 1, 0 }, + { "con-sdhc", IMX_SC_R_SDHC_0, 3, 1 }, + { "con-enet", IMX_SC_R_ENET_0, 2, 1 }, + { "con-nand", IMX_SC_R_NAND, 1, 0 }, + { "con-mlb", IMX_SC_R_MLB_0, 1, 1 }, + + /* Audio DMA SS */ + { "adma-audio-pll0", IMX_SC_R_AUDIO_PLL_0, 1, 0 }, + { "adma-audio-pll1", IMX_SC_R_AUDIO_PLL_1, 1, 0 }, + { "adma-audio-clk-0", IMX_SC_R_AUDIO_CLK_0, 1, 0 }, + { "adma-dma0-ch", IMX_SC_R_DMA_0_CH0, 16, 1 }, + { "adma-dma1-ch", IMX_SC_R_DMA_1_CH0, 16, 1 }, + { "adma-dma2-ch", IMX_SC_R_DMA_2_CH0, 5, 1 }, + { "adma-asrc0", IMX_SC_R_ASRC_0, 1, 0 }, + { "adma-asrc1", IMX_SC_R_ASRC_1, 1, 0 }, + { "adma-esai0", IMX_SC_R_ESAI_0, 1, 0 }, + { "adma-spdif0", IMX_SC_R_SPDIF_0, 1, 0 }, + { "adma-sai", IMX_SC_R_SAI_0, 3, 1 }, + { "adma-amix", IMX_SC_R_AMIX, 1, 0 }, + { "adma-mqs0", IMX_SC_R_MQS_0, 1, 0 }, + { "adma-dsp", IMX_SC_R_DSP, 1, 0 }, + { "adma-dsp-ram", IMX_SC_R_DSP_RAM, 1, 0 }, + { "adma-can", IMX_SC_R_CAN_0, 3, 1 }, + { "adma-ftm", IMX_SC_R_FTM_0, 2, 1 }, + { "adma-lpi2c", IMX_SC_R_I2C_0, 4, 1 }, + { "adma-adc", IMX_SC_R_ADC_0, 1, 1 }, + { "adma-lcd", IMX_SC_R_LCD_0, 1, 1 }, + { "adma-lcd0-pwm", IMX_SC_R_LCD_0_PWM_0, 1, 1 }, + { "adma-lpuart", IMX_SC_R_UART_0, 4, 1 }, + { "adma-lpspi", IMX_SC_R_SPI_0, 4, 1 }, + + /* VPU SS */ + { "vpu", IMX_SC_R_VPU, 1, 0 }, + { "vpu-pid", IMX_SC_R_VPU_PID0, 8, 1 }, + { "vpu-dec0", IMX_SC_R_VPU_DEC_0, 1, 0 }, + { "vpu-enc0", IMX_SC_R_VPU_ENC_0, 1, 0 }, + + /* GPU SS */ + { "gpu0-pid", IMX_SC_R_GPU_0_PID0, 4, 1 }, + + /* HSIO SS */ + { "hsio-pcie-b", IMX_SC_R_PCIE_B, 1, 0 }, + { "hsio-serdes-1", IMX_SC_R_SERDES_1, 1, 0 }, + { "hsio-gpio", IMX_SC_R_HSIO_GPIO, 1, 0 }, + + /* MIPI/LVDS SS */ + { "mipi0", IMX_SC_R_MIPI_0, 1, 0 }, + { "mipi0-pwm0", IMX_SC_R_MIPI_0_PWM_0, 1, 0 }, + { "mipi0-i2c", IMX_SC_R_MIPI_0_I2C_0, 2, 1 }, + { "lvds0", IMX_SC_R_LVDS_0, 1, 0 }, + + /* DC SS */ + { "dc0", IMX_SC_R_DC_0, 1, 0 }, + { "dc0-pll", IMX_SC_R_DC_0_PLL_0, 2, 1 }, +}; + +static const struct imx_sc_pd_soc imx8qxp_scu_pd = { + .pd_ranges = imx8qxp_scu_pd_ranges, + .num_ranges = ARRAY_SIZE(imx8qxp_scu_pd_ranges), +}; + +static struct imx_sc_ipc *pm_ipc_handle; + +static inline struct imx_sc_pm_domain * +to_imx_sc_pd(struct generic_pm_domain *genpd) +{ + return container_of(genpd, struct imx_sc_pm_domain, pd); +} + +static int imx_sc_pd_power(struct generic_pm_domain *domain, bool power_on) +{ + struct imx_sc_msg_req_set_resource_power_mode msg; + struct imx_sc_rpc_msg *hdr = &msg.hdr; + struct imx_sc_pm_domain *pd; + int ret; + + pd = to_imx_sc_pd(domain); + + hdr->ver = IMX_SC_RPC_VERSION; + hdr->svc = IMX_SC_RPC_SVC_PM; + hdr->func = IMX_SC_PM_FUNC_SET_RESOURCE_POWER_MODE; + hdr->size = 2; + + msg.resource = pd->rsrc; + msg.mode = power_on ? IMX_SC_PM_PW_MODE_ON : IMX_SC_PM_PW_MODE_LP; + + ret = imx_scu_call_rpc(pm_ipc_handle, &msg, true); + if (ret) + dev_err(&domain->dev, "failed to power %s resource %d ret %d\n", + power_on ? "up" : "off", pd->rsrc, ret); + + return ret; +} + +static int imx_sc_pd_power_on(struct generic_pm_domain *domain) +{ + return imx_sc_pd_power(domain, true); +} + +static int imx_sc_pd_power_off(struct generic_pm_domain *domain) +{ + return imx_sc_pd_power(domain, false); +} + +static struct generic_pm_domain *imx_scu_pd_xlate(struct of_phandle_args *spec, + void *data) +{ + struct generic_pm_domain *domain = ERR_PTR(-ENOENT); + struct genpd_onecell_data *pd_data = data; + unsigned int i; + + for (i = 0; i < pd_data->num_domains; i++) { + struct imx_sc_pm_domain *sc_pd; + + sc_pd = to_imx_sc_pd(pd_data->domains[i]); + if (sc_pd->rsrc == spec->args[0]) { + domain = &sc_pd->pd; + break; + } + } + + return domain; +} + +static struct imx_sc_pm_domain * +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; + int ret; + + sc_pd = devm_kzalloc(dev, sizeof(*sc_pd), GFP_KERNEL); + if (!sc_pd) + return ERR_PTR(-ENOMEM); + + sc_pd->rsrc = pd_ranges->rsrc + idx; + sc_pd->pd.power_off = imx_sc_pd_power_off; + sc_pd->pd.power_on = imx_sc_pd_power_on; + + if (pd_ranges->postfix) + snprintf(sc_pd->name, sizeof(sc_pd->name), + "%s%i", pd_ranges->name, idx); + else + snprintf(sc_pd->name, sizeof(sc_pd->name), + "%s", pd_ranges->name); + + sc_pd->pd.name = sc_pd->name; + + if (sc_pd->rsrc >= IMX_SC_R_LAST) { + dev_warn(dev, "invalid pd %s rsrc id %d found", + sc_pd->name, sc_pd->rsrc); + + devm_kfree(dev, sc_pd); + return NULL; + } + + ret = pm_genpd_init(&sc_pd->pd, NULL, true); + if (ret) { + dev_warn(dev, "failed to init pd %s rsrc id %d", + sc_pd->name, sc_pd->rsrc); + devm_kfree(dev, sc_pd); + return NULL; + } + + return sc_pd; +} + +static int imx_scu_init_pm_domains(struct device *dev, + const struct imx_sc_pd_soc *pd_soc) +{ + const struct imx_sc_pd_range *pd_ranges = pd_soc->pd_ranges; + struct generic_pm_domain **domains; + struct genpd_onecell_data *pd_data; + struct imx_sc_pm_domain *sc_pd; + u32 count = 0; + int i, j; + + for (i = 0; i < pd_soc->num_ranges; i++) + count += pd_ranges[i].num; + + domains = devm_kcalloc(dev, count, sizeof(*domains), GFP_KERNEL); + if (!domains) + return -ENOMEM; + + pd_data = devm_kzalloc(dev, sizeof(*pd_data), GFP_KERNEL); + if (!pd_data) + return -ENOMEM; + + count = 0; + for (i = 0; i < pd_soc->num_ranges; i++) { + for (j = 0; j < pd_ranges[i].num; j++) { + sc_pd = imx_scu_add_pm_domain(dev, j, &pd_ranges[i]); + if (IS_ERR_OR_NULL(sc_pd)) + continue; + + domains[count++] = &sc_pd->pd; + dev_dbg(dev, "added power domain %s\n", sc_pd->pd.name); + } + } + + pd_data->domains = domains; + pd_data->num_domains = count; + pd_data->xlate = imx_scu_pd_xlate; + + of_genpd_add_provider_onecell(dev->of_node, pd_data); + + return 0; +} + +static int imx_sc_pd_probe(struct platform_device *pdev) +{ + const struct imx_sc_pd_soc *pd_soc; + int ret; + + ret = imx_scu_get_handle(&pm_ipc_handle); + if (ret) + return ret; + + pd_soc = of_device_get_match_data(&pdev->dev); + if (!pd_soc) + return -ENODEV; + + return imx_scu_init_pm_domains(&pdev->dev, pd_soc); +} + +static const struct of_device_id imx_sc_pd_match[] = { + { .compatible = "fsl,imx8qxp-scu-pd", &imx8qxp_scu_pd}, + { /* sentinel */ } +}; + +static struct platform_driver imx_sc_pd_driver = { + .driver = { + .name = "imx-scu-pd", + .of_match_table = imx_sc_pd_match, + }, + .probe = imx_sc_pd_probe, +}; +builtin_platform_driver(imx_sc_pd_driver); + +MODULE_AUTHOR("Dong Aisheng <aisheng.dong@nxp.com>"); +MODULE_DESCRIPTION("IMX SCU Power Domain driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/firmware/raspberrypi.c b/drivers/firmware/raspberrypi.c @@ -1,12 +1,9 @@ +// 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> @@ -14,6 +11,7 @@ #include <linux/module.h> #include <linux/of_platform.h> #include <linux/platform_device.h> +#include <linux/slab.h> #include <soc/bcm2835/raspberrypi-firmware.h> #define MBOX_MSG(chan, data28) (((data28) & ~0xf) | ((chan) & 0xf)) @@ -21,8 +19,6 @@ #define MBOX_DATA28(msg) ((msg) & ~0xf) #define MBOX_CHAN_PROPERTY 8 -#define MAX_RPI_FW_PROP_BUF_SIZE 32 - static struct platform_device *rpi_hwmon; struct rpi_firmware { @@ -56,8 +52,12 @@ rpi_firmware_transaction(struct rpi_firmware *fw, u32 chan, u32 data) 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); } @@ -144,28 +144,30 @@ EXPORT_SYMBOL_GPL(rpi_firmware_property_list); int rpi_firmware_property(struct rpi_firmware *fw, u32 tag, void *tag_data, size_t buf_size) { - /* Single tags are very small (generally 8 bytes), so the - * stack should be safe. - */ - u8 data[sizeof(struct rpi_firmware_property_tag_header) + - MAX_RPI_FW_PROP_BUF_SIZE]; - struct rpi_firmware_property_tag_header *header = - (struct rpi_firmware_property_tag_header *)data; + struct rpi_firmware_property_tag_header *header; int ret; - if (WARN_ON(buf_size > sizeof(data) - sizeof(*header))) - return -EINVAL; + /* Some mailboxes can use over 1k bytes. Rather than checking + * size and using stack or kmalloc depending on requirements, + * just use kmalloc. Mailboxes don't get called enough to worry + * too much about the time taken in the allocation. + */ + void *data = kmalloc(sizeof(*header) + buf_size, GFP_KERNEL); + if (!data) + return -ENOMEM; + + header = data; header->tag = tag; header->buf_size = buf_size; header->req_resp_size = 0; - memcpy(data + sizeof(struct rpi_firmware_property_tag_header), - tag_data, buf_size); + memcpy(data + sizeof(*header), tag_data, buf_size); + + ret = rpi_firmware_property_list(fw, data, buf_size + sizeof(*header)); + + memcpy(tag_data, data + sizeof(*header), buf_size); - ret = rpi_firmware_property_list(fw, &data, buf_size + sizeof(*header)); - memcpy(tag_data, - data + sizeof(struct rpi_firmware_property_tag_header), - buf_size); + kfree(data); return ret; } diff --git 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, return err; } -static int mrq_is_supported(struct tegra_bpmp *bpmp, unsigned int mrq) -{ - struct mrq_query_abi_request req = { .mrq = cpu_to_le32(mrq) }; - struct mrq_query_abi_response resp; - struct tegra_bpmp_message msg = { - .mrq = MRQ_QUERY_ABI, - .tx = { - .data = &req, - .size = sizeof(req), - }, - .rx = { - .data = &resp, - .size = sizeof(resp), - }, - }; - int ret; - - ret = tegra_bpmp_transfer(bpmp, &msg); - if (ret < 0) { - /* something went wrong; assume not supported */ - dev_warn(bpmp->dev, "tegra_bpmp_transfer failed (%d)\n", ret); - return 0; - } - - return resp.status ? 0 : 1; -} - int tegra_bpmp_init_debugfs(struct tegra_bpmp *bpmp) { dma_addr_t phys; @@ -415,7 +388,7 @@ int tegra_bpmp_init_debugfs(struct tegra_bpmp *bpmp) int ret; struct dentry *root; - if (!mrq_is_supported(bpmp, MRQ_DEBUGFS)) + if (!tegra_bpmp_mrq_is_supported(bpmp, MRQ_DEBUGFS)) return 0; root = debugfs_create_dir("bpmp", NULL); diff --git a/drivers/firmware/tegra/bpmp.c b/drivers/firmware/tegra/bpmp.c @@ -28,6 +28,7 @@ #define MSG_ACK BIT(0) #define MSG_RING BIT(1) +#define TAG_SZ 32 static inline struct tegra_bpmp * mbox_client_to_bpmp(struct mbox_client *client) @@ -470,6 +471,31 @@ unlock: } EXPORT_SYMBOL_GPL(tegra_bpmp_free_mrq); +bool tegra_bpmp_mrq_is_supported(struct tegra_bpmp *bpmp, unsigned int mrq) +{ + struct mrq_query_abi_request req = { .mrq = cpu_to_le32(mrq) }; + struct mrq_query_abi_response resp; + struct tegra_bpmp_message msg = { + .mrq = MRQ_QUERY_ABI, + .tx = { + .data = &req, + .size = sizeof(req), + }, + .rx = { + .data = &resp, + .size = sizeof(resp), + }, + }; + int ret; + + ret = tegra_bpmp_transfer(bpmp, &msg); + if (ret || msg.rx.ret) + return false; + + return resp.status == 0; +} +EXPORT_SYMBOL_GPL(tegra_bpmp_mrq_is_supported); + static void tegra_bpmp_mrq_handle_ping(unsigned int mrq, struct tegra_bpmp_channel *channel, void *data) @@ -521,8 +547,9 @@ static int tegra_bpmp_ping(struct tegra_bpmp *bpmp) return err; } -static int tegra_bpmp_get_firmware_tag(struct tegra_bpmp *bpmp, char *tag, - size_t size) +/* deprecated version of tag query */ +static int tegra_bpmp_get_firmware_tag_old(struct tegra_bpmp *bpmp, char *tag, + size_t size) { struct mrq_query_tag_request request; struct tegra_bpmp_message msg; @@ -531,7 +558,10 @@ static int tegra_bpmp_get_firmware_tag(struct tegra_bpmp *bpmp, char *tag, void *virt; int err; - virt = dma_alloc_coherent(bpmp->dev, MSG_DATA_MIN_SZ, &phys, + if (size != TAG_SZ) + return -EINVAL; + + virt = dma_alloc_coherent(bpmp->dev, TAG_SZ, &phys, GFP_KERNEL | GFP_DMA32); if (!virt) return -ENOMEM; @@ -549,13 +579,44 @@ static int tegra_bpmp_get_firmware_tag(struct tegra_bpmp *bpmp, char *tag, local_irq_restore(flags); if (err == 0) - strlcpy(tag, virt, size); + memcpy(tag, virt, TAG_SZ); - dma_free_coherent(bpmp->dev, MSG_DATA_MIN_SZ, virt, phys); + dma_free_coherent(bpmp->dev, TAG_SZ, virt, phys); return err; } +static int tegra_bpmp_get_firmware_tag(struct tegra_bpmp *bpmp, char *tag, + size_t size) +{ + if (tegra_bpmp_mrq_is_supported(bpmp, MRQ_QUERY_FW_TAG)) { + struct mrq_query_fw_tag_response resp; + struct tegra_bpmp_message msg = { + .mrq = MRQ_QUERY_FW_TAG, + .rx = { + .data = &resp, + .size = sizeof(resp), + }, + }; + int err; + + if (size != sizeof(resp.tag)) + return -EINVAL; + + err = tegra_bpmp_transfer(bpmp, &msg); + + if (err) + return err; + if (msg.rx.ret < 0) + return -EINVAL; + + memcpy(tag, resp.tag, sizeof(resp.tag)); + return 0; + } + + return tegra_bpmp_get_firmware_tag_old(bpmp, tag, size); +} + static void tegra_bpmp_channel_signal(struct tegra_bpmp_channel *channel) { unsigned long flags = channel->ob->flags; @@ -664,7 +725,7 @@ static int tegra_bpmp_probe(struct platform_device *pdev) { struct tegra_bpmp *bpmp; unsigned int i; - char tag[32]; + char tag[TAG_SZ]; size_t size; int err; @@ -792,13 +853,13 @@ static int tegra_bpmp_probe(struct platform_device *pdev) goto free_mrq; } - err = tegra_bpmp_get_firmware_tag(bpmp, tag, sizeof(tag) - 1); + err = tegra_bpmp_get_firmware_tag(bpmp, tag, sizeof(tag)); if (err < 0) { dev_err(&pdev->dev, "failed to get firmware tag: %d\n", err); goto free_mrq; } - dev_info(&pdev->dev, "firmware: %s\n", tag); + dev_info(&pdev->dev, "firmware: %.*s\n", (int)sizeof(tag), tag); platform_set_drvdata(pdev, bpmp); diff --git a/drivers/gpu/drm/msm/adreno/a6xx_gmu.c b/drivers/gpu/drm/msm/adreno/a6xx_gmu.c @@ -927,26 +927,6 @@ static int a6xx_gmu_memory_probe(struct a6xx_gmu *gmu) return ret; } -/* Get the list of RPMh voltage levels from cmd-db */ -static int a6xx_gmu_rpmh_arc_cmds(const char *id, void *vals, int size) -{ - u32 len = cmd_db_read_aux_data_len(id); - - if (!len) - return 0; - - if (WARN_ON(len > size)) - return -EINVAL; - - cmd_db_read_aux_data(id, vals, len); - - /* - * The data comes back as an array of unsigned shorts so adjust the - * count accordingly - */ - return len >> 1; -} - /* Return the 'arc-level' for the given frequency */ static u32 a6xx_gmu_get_arc_level(struct device *dev, unsigned long freq) { @@ -974,11 +954,30 @@ static u32 a6xx_gmu_get_arc_level(struct device *dev, unsigned long freq) } static int a6xx_gmu_rpmh_arc_votes_init(struct device *dev, u32 *votes, - unsigned long *freqs, int freqs_count, - u16 *pri, int pri_count, - u16 *sec, int sec_count) + unsigned long *freqs, int freqs_count, const char *id) { int i, j; + const u16 *pri, *sec; + size_t pri_count, sec_count; + + pri = cmd_db_read_aux_data(id, &pri_count); + if (IS_ERR(pri)) + return PTR_ERR(pri); + /* + * The data comes back as an array of unsigned shorts so adjust the + * count accordingly + */ + pri_count >>= 1; + if (!pri_count) + return -EINVAL; + + sec = cmd_db_read_aux_data("mx.lvl", &sec_count); + if (IS_ERR(sec)) + return PTR_ERR(sec); + + sec_count >>= 1; + if (!sec_count) + return -EINVAL; /* Construct a vote for each frequency */ for (i = 0; i < freqs_count; i++) { @@ -1037,25 +1036,15 @@ static int a6xx_gmu_rpmh_votes_init(struct a6xx_gmu *gmu) struct a6xx_gpu *a6xx_gpu = container_of(gmu, struct a6xx_gpu, gmu); struct adreno_gpu *adreno_gpu = &a6xx_gpu->base; struct msm_gpu *gpu = &adreno_gpu->base; - - u16 gx[16], cx[16], mx[16]; - u32 gxcount, cxcount, mxcount; int ret; - /* Get the list of available voltage levels for each component */ - gxcount = a6xx_gmu_rpmh_arc_cmds("gfx.lvl", gx, sizeof(gx)); - cxcount = a6xx_gmu_rpmh_arc_cmds("cx.lvl", cx, sizeof(cx)); - mxcount = a6xx_gmu_rpmh_arc_cmds("mx.lvl", mx, sizeof(mx)); - /* Build the GX votes */ ret = a6xx_gmu_rpmh_arc_votes_init(&gpu->pdev->dev, gmu->gx_arc_votes, - gmu->gpu_freqs, gmu->nr_gpu_freqs, - gx, gxcount, mx, mxcount); + gmu->gpu_freqs, gmu->nr_gpu_freqs, "gfx.lvl"); /* Build the CX votes */ ret |= a6xx_gmu_rpmh_arc_votes_init(gmu->dev, gmu->cx_arc_votes, - gmu->gmu_freqs, gmu->nr_gmu_freqs, - cx, cxcount, mx, mxcount); + gmu->gmu_freqs, gmu->nr_gmu_freqs, "cx.lvl"); return ret; } diff --git a/drivers/memory/omap-gpmc.c b/drivers/memory/omap-gpmc.c @@ -2061,7 +2061,7 @@ static int gpmc_probe_generic_child(struct platform_device *pdev, * timings. */ name = gpmc_cs_get_name(cs); - if (name && of_node_cmp(child->name, name) == 0) + if (name && of_node_name_eq(child, name)) goto no_timings; ret = gpmc_cs_request(cs, resource_size(&res), &base); @@ -2069,7 +2069,7 @@ static int gpmc_probe_generic_child(struct platform_device *pdev, dev_err(&pdev->dev, "cannot request GPMC CS %d\n", cs); return ret; } - gpmc_cs_set_name(cs, child->name); + gpmc_cs_set_name(cs, child->full_name); gpmc_read_settings_dt(child, &gpmc_s); gpmc_read_timings_dt(child, &gpmc_t); @@ -2114,7 +2114,7 @@ static int gpmc_probe_generic_child(struct platform_device *pdev, goto err; } - if (of_node_cmp(child->name, "nand") == 0) { + if (of_node_name_eq(child, "nand")) { /* Warn about older DT blobs with no compatible property */ if (!of_property_read_bool(child, "compatible")) { dev_warn(&pdev->dev, @@ -2124,7 +2124,7 @@ static int gpmc_probe_generic_child(struct platform_device *pdev, } } - if (of_node_cmp(child->name, "onenand") == 0) { + if (of_node_name_eq(child, "onenand")) { /* Warn about older DT blobs with no compatible property */ if (!of_property_read_bool(child, "compatible")) { dev_warn(&pdev->dev, diff --git a/drivers/memory/tegra/Kconfig b/drivers/memory/tegra/Kconfig @@ -6,6 +6,16 @@ config TEGRA_MC This driver supports the Memory Controller (MC) hardware found on NVIDIA Tegra SoCs. +config TEGRA20_EMC + bool "NVIDIA Tegra20 External Memory Controller driver" + default y + depends on ARCH_TEGRA_2x_SOC + help + This driver is for the External Memory Controller (EMC) found on + Tegra20 chips. The EMC controls the external DRAM on the board. + This driver is required to change memory timings / clock rate for + external memory. + config TEGRA124_EMC bool "NVIDIA Tegra124 External Memory Controller driver" default y diff --git a/drivers/memory/tegra/Makefile b/drivers/memory/tegra/Makefile @@ -10,5 +10,6 @@ tegra-mc-$(CONFIG_ARCH_TEGRA_210_SOC) += tegra210.o obj-$(CONFIG_TEGRA_MC) += tegra-mc.o +obj-$(CONFIG_TEGRA20_EMC) += tegra20-emc.o obj-$(CONFIG_TEGRA124_EMC) += tegra124-emc.o obj-$(CONFIG_ARCH_TEGRA_186_SOC) += tegra186.o diff --git a/drivers/memory/tegra/tegra20-emc.c b/drivers/memory/tegra/tegra20-emc.c @@ -0,0 +1,591 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Tegra20 External Memory Controller driver + * + * Author: Dmitry Osipenko <digetx@gmail.com> + */ + +#include <linux/clk.h> +#include <linux/completion.h> +#include <linux/err.h> +#include <linux/interrupt.h> +#include <linux/iopoll.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/sort.h> +#include <linux/types.h> + +#include <soc/tegra/fuse.h> + +#define EMC_INTSTATUS 0x000 +#define EMC_INTMASK 0x004 +#define EMC_TIMING_CONTROL 0x028 +#define EMC_RC 0x02c +#define EMC_RFC 0x030 +#define EMC_RAS 0x034 +#define EMC_RP 0x038 +#define EMC_R2W 0x03c +#define EMC_W2R 0x040 +#define EMC_R2P 0x044 +#define EMC_W2P 0x048 +#define EMC_RD_RCD 0x04c +#define EMC_WR_RCD 0x050 +#define EMC_RRD 0x054 +#define EMC_REXT 0x058 +#define EMC_WDV 0x05c +#define EMC_QUSE 0x060 +#define EMC_QRST 0x064 +#define EMC_QSAFE 0x068 +#define EMC_RDV 0x06c +#define EMC_REFRESH 0x070 +#define EMC_BURST_REFRESH_NUM 0x074 +#define EMC_PDEX2WR 0x078 +#define EMC_PDEX2RD 0x07c +#define EMC_PCHG2PDEN 0x080 +#define EMC_ACT2PDEN 0x084 +#define EMC_AR2PDEN 0x088 +#define EMC_RW2PDEN 0x08c +#define EMC_TXSR 0x090 +#define EMC_TCKE 0x094 +#define EMC_TFAW 0x098 +#define EMC_TRPAB 0x09c +#define EMC_TCLKSTABLE 0x0a0 +#define EMC_TCLKSTOP 0x0a4 +#define EMC_TREFBW 0x0a8 +#define EMC_QUSE_EXTRA 0x0ac +#define EMC_ODT_WRITE 0x0b0 +#define EMC_ODT_READ 0x0b4 +#define EMC_FBIO_CFG5 0x104 +#define EMC_FBIO_CFG6 0x114 +#define EMC_AUTO_CAL_INTERVAL 0x2a8 +#define EMC_CFG_2 0x2b8 +#define EMC_CFG_DIG_DLL 0x2bc +#define EMC_DLL_XFORM_DQS 0x2c0 +#define EMC_DLL_XFORM_QUSE 0x2c4 +#define EMC_ZCAL_REF_CNT 0x2e0 +#define EMC_ZCAL_WAIT_CNT 0x2e4 +#define EMC_CFG_CLKTRIM_0 0x2d0 +#define EMC_CFG_CLKTRIM_1 0x2d4 +#define EMC_CFG_CLKTRIM_2 0x2d8 + +#define EMC_CLKCHANGE_REQ_ENABLE BIT(0) +#define EMC_CLKCHANGE_PD_ENABLE BIT(1) +#define EMC_CLKCHANGE_SR_ENABLE BIT(2) + +#define EMC_TIMING_UPDATE BIT(0) + +#define EMC_REFRESH_OVERFLOW_INT BIT(3) +#define EMC_CLKCHANGE_COMPLETE_INT BIT(4) + +static const u16 emc_timing_registers[] = { + EMC_RC, + EMC_RFC, + EMC_RAS, + EMC_RP, + EMC_R2W, + EMC_W2R, + EMC_R2P, + EMC_W2P, + EMC_RD_RCD, + EMC_WR_RCD, + EMC_RRD, + EMC_REXT, + EMC_WDV, + EMC_QUSE, + EMC_QRST, + EMC_QSAFE, + EMC_RDV, + EMC_REFRESH, + EMC_BURST_REFRESH_NUM, + EMC_PDEX2WR, + EMC_PDEX2RD, + EMC_PCHG2PDEN, + EMC_ACT2PDEN, + EMC_AR2PDEN, + EMC_RW2PDEN, + EMC_TXSR, + EMC_TCKE, + EMC_TFAW, + EMC_TRPAB, + EMC_TCLKSTABLE, + EMC_TCLKSTOP, + EMC_TREFBW, + EMC_QUSE_EXTRA, + EMC_FBIO_CFG6, + EMC_ODT_WRITE, + EMC_ODT_READ, + EMC_FBIO_CFG5, + EMC_CFG_DIG_DLL, + EMC_DLL_XFORM_DQS, + EMC_DLL_XFORM_QUSE, + EMC_ZCAL_REF_CNT, + EMC_ZCAL_WAIT_CNT, + EMC_AUTO_CAL_INTERVAL, + EMC_CFG_CLKTRIM_0, + EMC_CFG_CLKTRIM_1, + EMC_CFG_CLKTRIM_2, +}; + +struct emc_timing { + unsigned long rate; + u32 data[ARRAY_SIZE(emc_timing_registers)]; +}; + +struct tegra_emc { + struct device *dev; + struct completion clk_handshake_complete; + struct notifier_block clk_nb; + struct clk *backup_clk; + struct clk *emc_mux; + struct clk *pll_m; + struct clk *clk; + void __iomem *regs; + + struct emc_timing *timings; + unsigned int num_timings; +}; + +static irqreturn_t tegra_emc_isr(int irq, void *data) +{ + struct tegra_emc *emc = data; + u32 intmask = EMC_REFRESH_OVERFLOW_INT | EMC_CLKCHANGE_COMPLETE_INT; + u32 status; + + status = readl_relaxed(emc->regs + EMC_INTSTATUS) & intmask; + if (!status) + return IRQ_NONE; + + /* notify about EMC-CAR handshake completion */ + if (status & EMC_CLKCHANGE_COMPLETE_INT) + complete(&emc->clk_handshake_complete); + + /* notify about HW problem */ + if (status & EMC_REFRESH_OVERFLOW_INT) + dev_err_ratelimited(emc->dev, + "refresh request overflow timeout\n"); + + /* clear interrupts */ + writel_relaxed(status, emc->regs + EMC_INTSTATUS); + + return IRQ_HANDLED; +} + +static struct emc_timing *tegra_emc_find_timing(struct tegra_emc *emc, + unsigned long rate) +{ + struct emc_timing *timing = NULL; + unsigned int i; + + for (i = 0; i < emc->num_timings; i++) { + if (emc->timings[i].rate >= rate) { + timing = &emc->timings[i]; + break; + } + } + + if (!timing) { + dev_err(emc->dev, "no timing for rate %lu\n", rate); + return NULL; + } + + return timing; +} + +static int emc_prepare_timing_change(struct tegra_emc *emc, unsigned long rate) +{ + struct emc_timing *timing = tegra_emc_find_timing(emc, rate); + unsigned int i; + + if (!timing) + return -EINVAL; + + dev_dbg(emc->dev, "%s: using timing rate %lu for requested rate %lu\n", + __func__, timing->rate, rate); + + /* program shadow registers */ + for (i = 0; i < ARRAY_SIZE(timing->data); i++) + writel_relaxed(timing->data[i], + emc->regs + emc_timing_registers[i]); + + /* wait until programming has settled */ + readl_relaxed(emc->regs + emc_timing_registers[i - 1]); + + reinit_completion(&emc->clk_handshake_complete); + + return 0; +} + +static int emc_complete_timing_change(struct tegra_emc *emc, bool flush) +{ + long timeout; + + dev_dbg(emc->dev, "%s: flush %d\n", __func__, flush); + + if (flush) { + /* manually initiate memory timing update */ + writel_relaxed(EMC_TIMING_UPDATE, + emc->regs + EMC_TIMING_CONTROL); + return 0; + } + + timeout = wait_for_completion_timeout(&emc->clk_handshake_complete, + usecs_to_jiffies(100)); + if (timeout == 0) { + dev_err(emc->dev, "EMC-CAR handshake failed\n"); + return -EIO; + } else if (timeout < 0) { + dev_err(emc->dev, "failed to wait for EMC-CAR handshake: %ld\n", + timeout); + return timeout; + } + + return 0; +} + +static int tegra_emc_clk_change_notify(struct notifier_block *nb, + unsigned long msg, void *data) +{ + struct tegra_emc *emc = container_of(nb, struct tegra_emc, clk_nb); + struct clk_notifier_data *cnd = data; + int err; + + switch (msg) { + case PRE_RATE_CHANGE: + err = emc_prepare_timing_change(emc, cnd->new_rate); + break; + + case ABORT_RATE_CHANGE: + err = emc_prepare_timing_change(emc, cnd->old_rate); + if (err) + break; + + err = emc_complete_timing_change(emc, true); + break; + + case POST_RATE_CHANGE: + err = emc_complete_timing_change(emc, false); + break; + + default: + return NOTIFY_DONE; + } + + return notifier_from_errno(err); +} + +static int load_one_timing_from_dt(struct tegra_emc *emc, + struct emc_timing *timing, + struct device_node *node) +{ + u32 rate; + int err; + + if (!of_device_is_compatible(node, "nvidia,tegra20-emc-table")) { + dev_err(emc->dev, "incompatible DT node: %pOF\n", node); + return -EINVAL; + } + + err = of_property_read_u32(node, "clock-frequency", &rate); + if (err) { + dev_err(emc->dev, "timing %pOF: failed to read rate: %d\n", + node, err); + return err; + } + + err = of_property_read_u32_array(node, "nvidia,emc-registers", + timing->data, + ARRAY_SIZE(emc_timing_registers)); + if (err) { + dev_err(emc->dev, + "timing %pOF: failed to read emc timing data: %d\n", + node, err); + return err; + } + + /* + * The EMC clock rate is twice the bus rate, and the bus rate is + * measured in kHz. + */ + timing->rate = rate * 2 * 1000; + + dev_dbg(emc->dev, "%s: %pOF: EMC rate %lu\n", + __func__, node, timing->rate); + + return 0; +} + +static int cmp_timings(const void *_a, const void *_b) +{ + const struct emc_timing *a = _a; + const struct emc_timing *b = _b; + + if (a->rate < b->rate) + return -1; + + if (a->rate > b->rate) + return 1; + + return 0; +} + +static int tegra_emc_load_timings_from_dt(struct tegra_emc *emc, + struct device_node *node) +{ + struct device_node *child; + struct emc_timing *timing; + int child_count; + int err; + + child_count = of_get_child_count(node); + if (!child_count) { + dev_err(emc->dev, "no memory timings in DT node: %pOF\n", node); + return -EINVAL; + } + + emc->timings = devm_kcalloc(emc->dev, child_count, sizeof(*timing), + GFP_KERNEL); + if (!emc->timings) + return -ENOMEM; + + emc->num_timings = child_count; + timing = emc->timings; + + for_each_child_of_node(node, child) { + err = load_one_timing_from_dt(emc, timing++, child); + if (err) { + of_node_put(child); + return err; + } + } + + sort(emc->timings, emc->num_timings, sizeof(*timing), cmp_timings, + NULL); + + return 0; +} + +static struct device_node * +tegra_emc_find_node_by_ram_code(struct device *dev) +{ + struct device_node *np; + u32 value, ram_code; + int err; + + if (!of_property_read_bool(dev->of_node, "nvidia,use-ram-code")) + return of_node_get(dev->of_node); + + ram_code = tegra_read_ram_code(); + + for (np = of_find_node_by_name(dev->of_node, "emc-tables"); np; + np = of_find_node_by_name(np, "emc-tables")) { + err = of_property_read_u32(np, "nvidia,ram-code", &value); + if (err || value != ram_code) { + of_node_put(np); + continue; + } + + return np; + } + + dev_err(dev, "no memory timings for RAM code %u found in device tree\n", + ram_code); + + return NULL; +} + +static int emc_setup_hw(struct tegra_emc *emc) +{ + u32 intmask = EMC_REFRESH_OVERFLOW_INT | EMC_CLKCHANGE_COMPLETE_INT; + u32 emc_cfg; + + emc_cfg = readl_relaxed(emc->regs + EMC_CFG_2); + + /* + * Depending on a memory type, DRAM should enter either self-refresh + * or power-down state on EMC clock change. + */ + if (!(emc_cfg & EMC_CLKCHANGE_PD_ENABLE) && + !(emc_cfg & EMC_CLKCHANGE_SR_ENABLE)) { + dev_err(emc->dev, + "bootloader didn't specify DRAM auto-suspend mode\n"); + return -EINVAL; + } + + /* enable EMC and CAR to handshake on PLL divider/source changes */ + emc_cfg |= EMC_CLKCHANGE_REQ_ENABLE; + writel_relaxed(emc_cfg, emc->regs + EMC_CFG_2); + + /* initialize interrupt */ + writel_relaxed(intmask, emc->regs + EMC_INTMASK); + writel_relaxed(intmask, emc->regs + EMC_INTSTATUS); + + return 0; +} + +static int emc_init(struct tegra_emc *emc, unsigned long rate) +{ + int err; + + err = clk_set_parent(emc->emc_mux, emc->backup_clk); + if (err) { + dev_err(emc->dev, + "failed to reparent to backup source: %d\n", err); + return err; + } + + err = clk_set_rate(emc->pll_m, rate); + if (err) { + dev_err(emc->dev, + "failed to change pll_m rate: %d\n", err); + return err; + } + + err = clk_set_parent(emc->emc_mux, emc->pll_m); + if (err) { + dev_err(emc->dev, + "failed to reparent to pll_m: %d\n", err); + return err; + } + + err = clk_set_rate(emc->clk, rate); + if (err) { + dev_err(emc->dev, + "failed to change emc rate: %d\n", err); + return err; + } + + return 0; +} + +static int tegra_emc_probe(struct platform_device *pdev) +{ + struct device_node *np; + struct tegra_emc *emc; + struct resource *res; + int irq, err; + + /* driver has nothing to do in a case of memory timing absence */ + if (of_get_child_count(pdev->dev.of_node) == 0) { + dev_info(&pdev->dev, + "EMC device tree node doesn't have memory timings\n"); + return 0; + } + + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + dev_err(&pdev->dev, "interrupt not specified\n"); + dev_err(&pdev->dev, "please update your device tree\n"); + return irq; + } + + np = tegra_emc_find_node_by_ram_code(&pdev->dev); + if (!np) + return -EINVAL; + + emc = devm_kzalloc(&pdev->dev, sizeof(*emc), GFP_KERNEL); + if (!emc) { + of_node_put(np); + return -ENOMEM; + } + + init_completion(&emc->clk_handshake_complete); + emc->clk_nb.notifier_call = tegra_emc_clk_change_notify; + emc->dev = &pdev->dev; + + err = tegra_emc_load_timings_from_dt(emc, np); + of_node_put(np); + if (err) + return err; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + emc->regs = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(emc->regs)) + return PTR_ERR(emc->regs); + + err = emc_setup_hw(emc); + if (err) + return err; + + err = devm_request_irq(&pdev->dev, irq, tegra_emc_isr, 0, + dev_name(&pdev->dev), emc); + if (err) { + dev_err(&pdev->dev, "failed to request IRQ#%u: %d\n", irq, err); + return err; + } + + emc->clk = devm_clk_get(&pdev->dev, "emc"); + if (IS_ERR(emc->clk)) { + err = PTR_ERR(emc->clk); + dev_err(&pdev->dev, "failed to get emc clock: %d\n", err); + return err; + } + + emc->pll_m = clk_get_sys(NULL, "pll_m"); + if (IS_ERR(emc->pll_m)) { + err = PTR_ERR(emc->pll_m); + dev_err(&pdev->dev, "failed to get pll_m clock: %d\n", err); + return err; + } + + emc->backup_clk = clk_get_sys(NULL, "pll_p"); + if (IS_ERR(emc->backup_clk)) { + err = PTR_ERR(emc->backup_clk); + dev_err(&pdev->dev, "failed to get pll_p clock: %d\n", err); + goto put_pll_m; + } + + emc->emc_mux = clk_get_parent(emc->clk); + if (IS_ERR(emc->emc_mux)) { + err = PTR_ERR(emc->emc_mux); + dev_err(&pdev->dev, "failed to get emc_mux clock: %d\n", err); + goto put_backup; + } + + err = clk_notifier_register(emc->clk, &emc->clk_nb); + if (err) { + dev_err(&pdev->dev, "failed to register clk notifier: %d\n", + err); + goto put_backup; + } + + /* set DRAM clock rate to maximum */ + err = emc_init(emc, emc->timings[emc->num_timings - 1].rate); + if (err) { + dev_err(&pdev->dev, "failed to initialize EMC clock rate: %d\n", + err); + goto unreg_notifier; + } + + return 0; + +unreg_notifier: + clk_notifier_unregister(emc->clk, &emc->clk_nb); +put_backup: + clk_put(emc->backup_clk); +put_pll_m: + clk_put(emc->pll_m); + + return err; +} + +static const struct of_device_id tegra_emc_of_match[] = { + { .compatible = "nvidia,tegra20-emc", }, + {}, +}; + +static struct platform_driver tegra_emc_driver = { + .probe = tegra_emc_probe, + .driver = { + .name = "tegra20-emc", + .of_match_table = tegra_emc_of_match, + .suppress_bind_attrs = true, + }, +}; + +static int __init tegra_emc_init(void) +{ + return platform_driver_register(&tegra_emc_driver); +} +subsys_initcall(tegra_emc_init); diff --git a/drivers/soc/Makefile b/drivers/soc/Makefile @@ -13,7 +13,7 @@ obj-$(CONFIG_ARCH_GEMINI) += gemini/ obj-$(CONFIG_ARCH_MXC) += imx/ obj-$(CONFIG_SOC_XWAY) += lantiq/ obj-y += mediatek/ -obj-$(CONFIG_ARCH_MESON) += amlogic/ +obj-y += amlogic/ obj-y += qcom/ obj-y += renesas/ obj-$(CONFIG_ARCH_ROCKCHIP) += rockchip/ diff --git a/drivers/soc/amlogic/Kconfig b/drivers/soc/amlogic/Kconfig @@ -7,6 +7,15 @@ config MESON_CANVAS help Say yes to support the canvas IP for Amlogic SoCs. +config MESON_CLK_MEASURE + bool "Amlogic Meson SoC Clock Measure driver" + depends on ARCH_MESON || COMPILE_TEST + default ARCH_MESON + select REGMAP_MMIO + help + Say yes to support of Measuring a set of internal SoC clocks + from the debugfs interface. + config MESON_GX_SOCINFO bool "Amlogic Meson GX SoC Information driver" depends on ARCH_MESON || COMPILE_TEST diff --git a/drivers/soc/amlogic/Makefile b/drivers/soc/amlogic/Makefile @@ -1,4 +1,5 @@ obj-$(CONFIG_MESON_CANVAS) += meson-canvas.o +obj-$(CONFIG_MESON_CLK_MEASURE) += meson-clk-measure.o obj-$(CONFIG_MESON_GX_SOCINFO) += meson-gx-socinfo.o obj-$(CONFIG_MESON_GX_PM_DOMAINS) += meson-gx-pwrc-vpu.o obj-$(CONFIG_MESON_MX_SOCINFO) += meson-mx-socinfo.o diff --git a/drivers/soc/amlogic/meson-clk-measure.c b/drivers/soc/amlogic/meson-clk-measure.c @@ -0,0 +1,350 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2018 BayLibre, SAS + * Author: Neil Armstrong <narmstrong@baylibre.com> + */ + +#include <linux/of_address.h> +#include <linux/platform_device.h> +#include <linux/bitfield.h> +#include <linux/seq_file.h> +#include <linux/debugfs.h> +#include <linux/regmap.h> + +#define MSR_CLK_DUTY 0x0 +#define MSR_CLK_REG0 0x4 +#define MSR_CLK_REG1 0x8 +#define MSR_CLK_REG2 0xc + +#define MSR_DURATION GENMASK(15, 0) +#define MSR_ENABLE BIT(16) +#define MSR_CONT BIT(17) /* continuous measurement */ +#define MSR_INTR BIT(18) /* interrupts */ +#define MSR_RUN BIT(19) +#define MSR_CLK_SRC GENMASK(26, 20) +#define MSR_BUSY BIT(31) + +#define MSR_VAL_MASK GENMASK(15, 0) + +#define DIV_MIN 32 +#define DIV_STEP 32 +#define DIV_MAX 640 + +#define CLK_MSR_MAX 128 + +struct meson_msr_id { + struct meson_msr *priv; + unsigned int id; + const char *name; +}; + +struct meson_msr { + struct regmap *regmap; + struct meson_msr_id msr_table[CLK_MSR_MAX]; +}; + +#define CLK_MSR_ID(__id, __name) \ + [__id] = {.id = __id, .name = __name,} + +static struct meson_msr_id clk_msr_m8[CLK_MSR_MAX] = { + CLK_MSR_ID(0, "ring_osc_out_ee0"), + CLK_MSR_ID(1, "ring_osc_out_ee1"), + CLK_MSR_ID(2, "ring_osc_out_ee2"), + CLK_MSR_ID(3, "a9_ring_osck"), + CLK_MSR_ID(6, "vid_pll"), + CLK_MSR_ID(7, "clk81"), + CLK_MSR_ID(8, "encp"), + CLK_MSR_ID(9, "encl"), + CLK_MSR_ID(11, "eth_rmii"), + CLK_MSR_ID(13, "amclk"), + CLK_MSR_ID(14, "fec_clk_0"), + CLK_MSR_ID(15, "fec_clk_1"), + CLK_MSR_ID(16, "fec_clk_2"), + CLK_MSR_ID(18, "a9_clk_div16"), + CLK_MSR_ID(19, "hdmi_sys"), + CLK_MSR_ID(20, "rtc_osc_clk_out"), + CLK_MSR_ID(21, "i2s_clk_in_src0"), + CLK_MSR_ID(22, "clk_rmii_from_pad"), + CLK_MSR_ID(23, "hdmi_ch0_tmds"), + CLK_MSR_ID(24, "lvds_fifo"), + CLK_MSR_ID(26, "sc_clk_int"), + CLK_MSR_ID(28, "sar_adc"), + CLK_MSR_ID(30, "mpll_clk_test_out"), + CLK_MSR_ID(31, "audac_clkpi"), + CLK_MSR_ID(32, "vdac"), + CLK_MSR_ID(33, "sdhc_rx"), + CLK_MSR_ID(34, "sdhc_sd"), + CLK_MSR_ID(35, "mali"), + CLK_MSR_ID(36, "hdmi_tx_pixel"), + CLK_MSR_ID(38, "vdin_meas"), + CLK_MSR_ID(39, "pcm_sclk"), + CLK_MSR_ID(40, "pcm_mclk"), + CLK_MSR_ID(41, "eth_rx_tx"), + CLK_MSR_ID(42, "pwm_d"), + CLK_MSR_ID(43, "pwm_c"), + CLK_MSR_ID(44, "pwm_b"), + CLK_MSR_ID(45, "pwm_a"), + CLK_MSR_ID(46, "pcm2_sclk"), + CLK_MSR_ID(47, "ddr_dpll_pt"), + CLK_MSR_ID(48, "pwm_f"), + CLK_MSR_ID(49, "pwm_e"), + CLK_MSR_ID(59, "hcodec"), + CLK_MSR_ID(60, "usb_32k_alt"), + CLK_MSR_ID(61, "gpio"), + CLK_MSR_ID(62, "vid2_pll"), + CLK_MSR_ID(63, "mipi_csi_cfg"), +}; + +static struct meson_msr_id clk_msr_gx[CLK_MSR_MAX] = { + CLK_MSR_ID(0, "ring_osc_out_ee_0"), + CLK_MSR_ID(1, "ring_osc_out_ee_1"), + CLK_MSR_ID(2, "ring_osc_out_ee_2"), + CLK_MSR_ID(3, "a53_ring_osc"), + CLK_MSR_ID(4, "gp0_pll"), + CLK_MSR_ID(6, "enci"), + CLK_MSR_ID(7, "clk81"), + CLK_MSR_ID(8, "encp"), + CLK_MSR_ID(9, "encl"), + CLK_MSR_ID(10, "vdac"), + CLK_MSR_ID(11, "rgmii_tx"), + CLK_MSR_ID(12, "pdm"), + CLK_MSR_ID(13, "amclk"), + CLK_MSR_ID(14, "fec_0"), + CLK_MSR_ID(15, "fec_1"), + CLK_MSR_ID(16, "fec_2"), + CLK_MSR_ID(17, "sys_pll_div16"), + CLK_MSR_ID(18, "sys_cpu_div16"), + CLK_MSR_ID(19, "hdmitx_sys"), + CLK_MSR_ID(20, "rtc_osc_out"), + CLK_MSR_ID(21, "i2s_in_src0"), + CLK_MSR_ID(22, "eth_phy_ref"), + CLK_MSR_ID(23, "hdmi_todig"), + CLK_MSR_ID(26, "sc_int"), + CLK_MSR_ID(28, "sar_adc"), + CLK_MSR_ID(31, "mpll_test_out"), + CLK_MSR_ID(32, "vdec"), + CLK_MSR_ID(35, "mali"), + CLK_MSR_ID(36, "hdmi_tx_pixel"), + CLK_MSR_ID(37, "i958"), + CLK_MSR_ID(38, "vdin_meas"), + CLK_MSR_ID(39, "pcm_sclk"), + CLK_MSR_ID(40, "pcm_mclk"), + CLK_MSR_ID(41, "eth_rx_or_rmii"), + CLK_MSR_ID(42, "mp0_out"), + CLK_MSR_ID(43, "fclk_div5"), + CLK_MSR_ID(44, "pwm_b"), + CLK_MSR_ID(45, "pwm_a"), + CLK_MSR_ID(46, "vpu"), + CLK_MSR_ID(47, "ddr_dpll_pt"), + CLK_MSR_ID(48, "mp1_out"), + CLK_MSR_ID(49, "mp2_out"), + CLK_MSR_ID(50, "mp3_out"), + CLK_MSR_ID(51, "nand_core"), + CLK_MSR_ID(52, "sd_emmc_b"), + CLK_MSR_ID(53, "sd_emmc_a"), + CLK_MSR_ID(55, "vid_pll_div_out"), + CLK_MSR_ID(56, "cci"), + CLK_MSR_ID(57, "wave420l_c"), + CLK_MSR_ID(58, "wave420l_b"), + CLK_MSR_ID(59, "hcodec"), + CLK_MSR_ID(60, "alt_32k"), + CLK_MSR_ID(61, "gpio_msr"), + CLK_MSR_ID(62, "hevc"), + CLK_MSR_ID(66, "vid_lock"), + CLK_MSR_ID(70, "pwm_f"), + CLK_MSR_ID(71, "pwm_e"), + CLK_MSR_ID(72, "pwm_d"), + CLK_MSR_ID(73, "pwm_c"), + CLK_MSR_ID(75, "aoclkx2_int"), + CLK_MSR_ID(76, "aoclk_int"), + CLK_MSR_ID(77, "rng_ring_osc_0"), + CLK_MSR_ID(78, "rng_ring_osc_1"), + CLK_MSR_ID(79, "rng_ring_osc_2"), + CLK_MSR_ID(80, "rng_ring_osc_3"), + CLK_MSR_ID(81, "vapb"), + CLK_MSR_ID(82, "ge2d"), +}; + +static int meson_measure_id(struct meson_msr_id *clk_msr_id, + unsigned int duration) +{ + struct meson_msr *priv = clk_msr_id->priv; + unsigned int val; + int ret; + + regmap_write(priv->regmap, MSR_CLK_REG0, 0); + + /* Set measurement duration */ + regmap_update_bits(priv->regmap, MSR_CLK_REG0, MSR_DURATION, + FIELD_PREP(MSR_DURATION, duration - 1)); + + /* Set ID */ + regmap_update_bits(priv->regmap, MSR_CLK_REG0, MSR_CLK_SRC, + FIELD_PREP(MSR_CLK_SRC, clk_msr_id->id)); + + /* Enable & Start */ + regmap_update_bits(priv->regmap, MSR_CLK_REG0, + MSR_RUN | MSR_ENABLE, + MSR_RUN | MSR_ENABLE); + + ret = regmap_read_poll_timeout(priv->regmap, MSR_CLK_REG0, + val, !(val & MSR_BUSY), 10, 10000); + if (ret) + return ret; + + /* Disable */ + regmap_update_bits(priv->regmap, MSR_CLK_REG0, MSR_ENABLE, 0); + + /* Get the value in multiple of gate time counts */ + regmap_read(priv->regmap, MSR_CLK_REG2, &val); + + if (val >= MSR_VAL_MASK) + return -EINVAL; + + return DIV_ROUND_CLOSEST_ULL((val & MSR_VAL_MASK) * 1000000ULL, + duration); +} + +static int meson_measure_best_id(struct meson_msr_id *clk_msr_id, + unsigned int *precision) +{ + unsigned int duration = DIV_MAX; + int ret; + + /* Start from max duration and down to min duration */ + do { + ret = meson_measure_id(clk_msr_id, duration); + if (ret >= 0) + *precision = (2 * 1000000) / duration; + else + duration -= DIV_STEP; + } while (duration >= DIV_MIN && ret == -EINVAL); + + return ret; +} + +static int clk_msr_show(struct seq_file *s, void *data) +{ + struct meson_msr_id *clk_msr_id = s->private; + unsigned int precision = 0; + int val; + + val = meson_measure_best_id(clk_msr_id, &precision); + if (val < 0) + return val; + + seq_printf(s, "%d\t+/-%dHz\n", val, precision); + + return 0; +} +DEFINE_SHOW_ATTRIBUTE(clk_msr); + +static int clk_msr_summary_show(struct seq_file *s, void *data) +{ + struct meson_msr_id *msr_table = s->private; + unsigned int precision = 0; + int val, i; + + seq_puts(s, " clock rate precision\n"); + seq_puts(s, "---------------------------------------------\n"); + + for (i = 0 ; i < CLK_MSR_MAX ; ++i) { + if (!msr_table[i].name) + continue; + + val = meson_measure_best_id(&msr_table[i], &precision); + if (val < 0) + return val; + + seq_printf(s, " %-20s %10d +/-%dHz\n", + msr_table[i].name, val, precision); + } + + return 0; +} +DEFINE_SHOW_ATTRIBUTE(clk_msr_summary); + +static const struct regmap_config meson_clk_msr_regmap_config = { + .reg_bits = 32, + .val_bits = 32, + .reg_stride = 4, + .max_register = MSR_CLK_REG2, +}; + +static int meson_msr_probe(struct platform_device *pdev) +{ + const struct meson_msr_id *match_data; + struct meson_msr *priv; + struct resource *res; + struct dentry *root, *clks; + void __iomem *base; + int i; + + priv = devm_kzalloc(&pdev->dev, sizeof(struct meson_msr), + GFP_KERNEL); + if (!priv) + return -ENOMEM; + + match_data = device_get_match_data(&pdev->dev); + if (!match_data) { + dev_err(&pdev->dev, "failed to get match data\n"); + return -ENODEV; + } + + memcpy(priv->msr_table, match_data, sizeof(priv->msr_table)); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(base)) { + dev_err(&pdev->dev, "io resource mapping failed\n"); + return PTR_ERR(base); + } + + priv->regmap = devm_regmap_init_mmio(&pdev->dev, base, + &meson_clk_msr_regmap_config); + if (IS_ERR(priv->regmap)) + return PTR_ERR(priv->regmap); + + root = debugfs_create_dir("meson-clk-msr", NULL); + clks = debugfs_create_dir("clks", root); + + debugfs_create_file("measure_summary", 0444, root, + priv->msr_table, &clk_msr_summary_fops); + + for (i = 0 ; i < CLK_MSR_MAX ; ++i) { + if (!priv->msr_table[i].name) + continue; + + priv->msr_table[i].priv = priv; + + debugfs_create_file(priv->msr_table[i].name, 0444, clks, + &priv->msr_table[i], &clk_msr_fops); + } + + return 0; +} + +static const struct of_device_id meson_msr_match_table[] = { + { + .compatible = "amlogic,meson-gx-clk-measure", + .data = (void *)clk_msr_gx, + }, + { + .compatible = "amlogic,meson8-clk-measure", + .data = (void *)clk_msr_m8, + }, + { + .compatible = "amlogic,meson8b-clk-measure", + .data = (void *)clk_msr_m8, + }, + { /* sentinel */ } +}; + +static struct platform_driver meson_msr_driver = { + .probe = meson_msr_probe, + .driver = { + .name = "meson_msr", + .of_match_table = meson_msr_match_table, + }, +}; +builtin_platform_driver(meson_msr_driver); diff --git a/drivers/soc/atmel/soc.c b/drivers/soc/atmel/soc.c @@ -66,6 +66,8 @@ static const struct at91_soc __initconst socs[] = { AT91_SOC(AT91SAM9XE128_CIDR_MATCH, 0, "at91sam9xe128", "at91sam9xe128"), AT91_SOC(AT91SAM9XE256_CIDR_MATCH, 0, "at91sam9xe256", "at91sam9xe256"), AT91_SOC(AT91SAM9XE512_CIDR_MATCH, 0, "at91sam9xe512", "at91sam9xe512"), + AT91_SOC(SAM9X60_CIDR_MATCH, SAM9X60_EXID_MATCH, + "sam9x60", "sam9x60"), #endif #ifdef CONFIG_SOC_SAMA5 AT91_SOC(SAMA5D2_CIDR_MATCH, SAMA5D21CU_EXID_MATCH, @@ -90,12 +92,20 @@ static const struct at91_soc __initconst socs[] = { "sama5d27c 128MiB SiP", "sama5d2"), AT91_SOC(SAMA5D2_CIDR_MATCH, SAMA5D27C_D5M_EXID_MATCH, "sama5d27c 64MiB SiP", "sama5d2"), + AT91_SOC(SAMA5D2_CIDR_MATCH, SAMA5D27C_LD1G_EXID_MATCH, + "sama5d27c 128MiB LPDDR2 SiP", "sama5d2"), + AT91_SOC(SAMA5D2_CIDR_MATCH, SAMA5D27C_LD2G_EXID_MATCH, + "sama5d27c 256MiB LPDDR2 SiP", "sama5d2"), AT91_SOC(SAMA5D2_CIDR_MATCH, SAMA5D28CU_EXID_MATCH, "sama5d28", "sama5d2"), AT91_SOC(SAMA5D2_CIDR_MATCH, SAMA5D28CN_EXID_MATCH, "sama5d28", "sama5d2"), AT91_SOC(SAMA5D2_CIDR_MATCH, SAMA5D28C_D1G_EXID_MATCH, "sama5d28c 128MiB SiP", "sama5d2"), + AT91_SOC(SAMA5D2_CIDR_MATCH, SAMA5D28C_LD1G_EXID_MATCH, + "sama5d28c 128MiB LPDDR2 SiP", "sama5d2"), + AT91_SOC(SAMA5D2_CIDR_MATCH, SAMA5D28C_LD2G_EXID_MATCH, + "sama5d28c 256MiB LPDDR2 SiP", "sama5d2"), AT91_SOC(SAMA5D3_CIDR_MATCH, SAMA5D31_EXID_MATCH, "sama5d31", "sama5d3"), AT91_SOC(SAMA5D3_CIDR_MATCH, SAMA5D33_EXID_MATCH, diff --git a/drivers/soc/atmel/soc.h b/drivers/soc/atmel/soc.h @@ -42,6 +42,7 @@ at91_soc_init(const struct at91_soc *socs); #define AT91SAM9G45_CIDR_MATCH 0x019b05a0 #define AT91SAM9X5_CIDR_MATCH 0x019a05a0 #define AT91SAM9N12_CIDR_MATCH 0x019a07a0 +#define SAM9X60_CIDR_MATCH 0x019b35a0 #define AT91SAM9M11_EXID_MATCH 0x00000001 #define AT91SAM9M10_EXID_MATCH 0x00000002 @@ -58,6 +59,8 @@ at91_soc_init(const struct at91_soc *socs); #define AT91SAM9N12_EXID_MATCH 0x00000006 #define AT91SAM9CN11_EXID_MATCH 0x00000009 +#define SAM9X60_EXID_MATCH 0x00000000 + #define AT91SAM9XE128_CIDR_MATCH 0x329973a0 #define AT91SAM9XE256_CIDR_MATCH 0x329a93a0 #define AT91SAM9XE512_CIDR_MATCH 0x329aa3a0 @@ -73,9 +76,13 @@ at91_soc_init(const struct at91_soc *socs); #define SAMA5D26CU_EXID_MATCH 0x00000012 #define SAMA5D27C_D1G_EXID_MATCH 0x00000033 #define SAMA5D27C_D5M_EXID_MATCH 0x00000032 +#define SAMA5D27C_LD1G_EXID_MATCH 0x00000061 +#define SAMA5D27C_LD2G_EXID_MATCH 0x00000062 #define SAMA5D27CU_EXID_MATCH 0x00000011 #define SAMA5D27CN_EXID_MATCH 0x00000021 #define SAMA5D28C_D1G_EXID_MATCH 0x00000013 +#define SAMA5D28C_LD1G_EXID_MATCH 0x00000071 +#define SAMA5D28C_LD2G_EXID_MATCH 0x00000072 #define SAMA5D28CU_EXID_MATCH 0x00000010 #define SAMA5D28CN_EXID_MATCH 0x00000020 diff --git a/drivers/soc/bcm/brcmstb/common.c b/drivers/soc/bcm/brcmstb/common.c @@ -31,13 +31,17 @@ static const struct of_device_id brcmstb_machine_match[] = { bool soc_is_brcmstb(void) { + const struct of_device_id *match; struct device_node *root; root = of_find_node_by_path("/"); if (!root) return false; - return of_match_node(brcmstb_machine_match, root) != NULL; + match = of_match_node(brcmstb_machine_match, root); + of_node_put(root); + + return match != NULL; } u32 brcmstb_get_family_id(void) diff --git a/drivers/soc/bcm/brcmstb/pm/pm-arm.c b/drivers/soc/bcm/brcmstb/pm/pm-arm.c @@ -404,7 +404,7 @@ noinline int brcmstb_pm_s3_finish(void) { struct brcmstb_s3_params *params = ctrl.s3_params; dma_addr_t params_pa = ctrl.s3_params_pa; - phys_addr_t reentry = virt_to_phys(&cpu_resume); + phys_addr_t reentry = virt_to_phys(&cpu_resume_arm); enum bsp_initiate_command cmd; u32 flags; diff --git a/drivers/soc/bcm/raspberrypi-power.c b/drivers/soc/bcm/raspberrypi-power.c @@ -1,9 +1,6 @@ +// SPDX-License-Identifier: GPL-2.0 /* (C) 2015 Pengutronix, Alexander Aring <aar@pengutronix.de> * - * 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. - * * Authors: * Alexander Aring <aar@pengutronix.de> * Eric Anholt <eric@anholt.net> diff --git a/drivers/soc/imx/Kconfig b/drivers/soc/imx/Kconfig @@ -1,8 +1,8 @@ menu "i.MX SoC drivers" -config IMX7_PM_DOMAINS - bool "i.MX7 PM domains" - depends on SOC_IMX7D || (COMPILE_TEST && OF) +config IMX_GPCV2_PM_DOMAINS + bool "i.MX GPCv2 PM domains" + depends on SOC_IMX7D || SOC_IMX8MQ || (COMPILE_TEST && OF) depends on PM select PM_GENERIC_DOMAINS default y if SOC_IMX7D diff --git a/drivers/soc/imx/Makefile b/drivers/soc/imx/Makefile @@ -1,2 +1,2 @@ obj-$(CONFIG_HAVE_IMX_GPC) += gpc.o -obj-$(CONFIG_IMX7_PM_DOMAINS) += gpcv2.o +obj-$(CONFIG_IMX_GPCV2_PM_DOMAINS) += gpcv2.o diff --git a/drivers/soc/imx/gpc.c b/drivers/soc/imx/gpc.c @@ -35,7 +35,7 @@ #define GPU_VPU_PUP_REQ BIT(1) #define GPU_VPU_PDN_REQ BIT(0) -#define GPC_CLK_MAX 6 +#define GPC_CLK_MAX 7 #define PGC_DOMAIN_FLAG_NO_PD BIT(0) diff --git a/drivers/soc/imx/gpcv2.c b/drivers/soc/imx/gpcv2.c @@ -14,23 +14,54 @@ #include <linux/regmap.h> #include <linux/regulator/consumer.h> #include <dt-bindings/power/imx7-power.h> +#include <dt-bindings/power/imx8mq-power.h> #define GPC_LPCR_A_CORE_BSC 0x000 #define GPC_PGC_CPU_MAPPING 0x0ec -#define USB_HSIC_PHY_A_CORE_DOMAIN BIT(6) -#define USB_OTG2_PHY_A_CORE_DOMAIN BIT(5) -#define USB_OTG1_PHY_A_CORE_DOMAIN BIT(4) -#define PCIE_PHY_A_CORE_DOMAIN BIT(3) -#define MIPI_PHY_A_CORE_DOMAIN BIT(2) + +#define IMX7_USB_HSIC_PHY_A_CORE_DOMAIN BIT(6) +#define IMX7_USB_OTG2_PHY_A_CORE_DOMAIN BIT(5) +#define IMX7_USB_OTG1_PHY_A_CORE_DOMAIN BIT(4) +#define IMX7_PCIE_PHY_A_CORE_DOMAIN BIT(3) +#define IMX7_MIPI_PHY_A_CORE_DOMAIN BIT(2) + +#define IMX8M_PCIE2_A53_DOMAIN BIT(15) +#define IMX8M_MIPI_CSI2_A53_DOMAIN BIT(14) +#define IMX8M_MIPI_CSI1_A53_DOMAIN BIT(13) +#define IMX8M_DISP_A53_DOMAIN BIT(12) +#define IMX8M_HDMI_A53_DOMAIN BIT(11) +#define IMX8M_VPU_A53_DOMAIN BIT(10) +#define IMX8M_GPU_A53_DOMAIN BIT(9) +#define IMX8M_DDR2_A53_DOMAIN BIT(8) +#define IMX8M_DDR1_A53_DOMAIN BIT(7) +#define IMX8M_OTG2_A53_DOMAIN BIT(5) +#define IMX8M_OTG1_A53_DOMAIN BIT(4) +#define IMX8M_PCIE1_A53_DOMAIN BIT(3) +#define IMX8M_MIPI_A53_DOMAIN BIT(2) #define GPC_PU_PGC_SW_PUP_REQ 0x0f8 #define GPC_PU_PGC_SW_PDN_REQ 0x104 -#define USB_HSIC_PHY_SW_Pxx_REQ BIT(4) -#define USB_OTG2_PHY_SW_Pxx_REQ BIT(3) -#define USB_OTG1_PHY_SW_Pxx_REQ BIT(2) -#define PCIE_PHY_SW_Pxx_REQ BIT(1) -#define MIPI_PHY_SW_Pxx_REQ BIT(0) + +#define IMX7_USB_HSIC_PHY_SW_Pxx_REQ BIT(4) +#define IMX7_USB_OTG2_PHY_SW_Pxx_REQ BIT(3) +#define IMX7_USB_OTG1_PHY_SW_Pxx_REQ BIT(2) +#define IMX7_PCIE_PHY_SW_Pxx_REQ BIT(1) +#define IMX7_MIPI_PHY_SW_Pxx_REQ BIT(0) + +#define IMX8M_PCIE2_SW_Pxx_REQ BIT(13) +#define IMX8M_MIPI_CSI2_SW_Pxx_REQ BIT(12) +#define IMX8M_MIPI_CSI1_SW_Pxx_REQ BIT(11) +#define IMX8M_DISP_SW_Pxx_REQ BIT(10) +#define IMX8M_HDMI_SW_Pxx_REQ BIT(9) +#define IMX8M_VPU_SW_Pxx_REQ BIT(8) +#define IMX8M_GPU_SW_Pxx_REQ BIT(7) +#define IMX8M_DDR2_SW_Pxx_REQ BIT(6) +#define IMX8M_DDR1_SW_Pxx_REQ BIT(5) +#define IMX8M_OTG2_SW_Pxx_REQ BIT(3) +#define IMX8M_OTG1_SW_Pxx_REQ BIT(2) +#define IMX8M_PCIE1_SW_Pxx_REQ BIT(1) +#define IMX8M_MIPI_SW_Pxx_REQ BIT(0) #define GPC_M4_PU_PDN_FLG 0x1bc @@ -40,9 +71,22 @@ * GPC_PGC memory map are incorrect, below offset * values are from design RTL. */ -#define PGC_MIPI 16 -#define PGC_PCIE 17 -#define PGC_USB_HSIC 20 +#define IMX7_PGC_MIPI 16 +#define IMX7_PGC_PCIE 17 +#define IMX7_PGC_USB_HSIC 20 + +#define IMX8M_PGC_MIPI 16 +#define IMX8M_PGC_PCIE1 17 +#define IMX8M_PGC_OTG1 18 +#define IMX8M_PGC_OTG2 19 +#define IMX8M_PGC_DDR1 21 +#define IMX8M_PGC_GPU 23 +#define IMX8M_PGC_VPU 24 +#define IMX8M_PGC_DISP 26 +#define IMX8M_PGC_MIPI_CSI1 27 +#define IMX8M_PGC_MIPI_CSI2 28 +#define IMX8M_PGC_PCIE2 29 + #define GPC_PGC_CTRL(n) (0x800 + (n) * 0x40) #define GPC_PGC_SR(n) (GPC_PGC_CTRL(n) + 0xc) @@ -67,6 +111,7 @@ struct imx_pgc_domain { struct imx_pgc_domain_data { const struct imx_pgc_domain *domains; size_t domains_num; + const struct regmap_access_table *reg_access_table; }; static int imx_gpc_pu_pgc_sw_pxx_req(struct generic_pm_domain *genpd, @@ -166,11 +211,11 @@ static const struct imx_pgc_domain imx7_pgc_domains[] = { .name = "mipi-phy", }, .bits = { - .pxx = MIPI_PHY_SW_Pxx_REQ, - .map = MIPI_PHY_A_CORE_DOMAIN, + .pxx = IMX7_MIPI_PHY_SW_Pxx_REQ, + .map = IMX7_MIPI_PHY_A_CORE_DOMAIN, }, .voltage = 1000000, - .pgc = PGC_MIPI, + .pgc = IMX7_PGC_MIPI, }, [IMX7_POWER_DOMAIN_PCIE_PHY] = { @@ -178,11 +223,11 @@ static const struct imx_pgc_domain imx7_pgc_domains[] = { .name = "pcie-phy", }, .bits = { - .pxx = PCIE_PHY_SW_Pxx_REQ, - .map = PCIE_PHY_A_CORE_DOMAIN, + .pxx = IMX7_PCIE_PHY_SW_Pxx_REQ, + .map = IMX7_PCIE_PHY_A_CORE_DOMAIN, }, .voltage = 1000000, - .pgc = PGC_PCIE, + .pgc = IMX7_PGC_PCIE, }, [IMX7_POWER_DOMAIN_USB_HSIC_PHY] = { @@ -190,17 +235,195 @@ static const struct imx_pgc_domain imx7_pgc_domains[] = { .name = "usb-hsic-phy", }, .bits = { - .pxx = USB_HSIC_PHY_SW_Pxx_REQ, - .map = USB_HSIC_PHY_A_CORE_DOMAIN, + .pxx = IMX7_USB_HSIC_PHY_SW_Pxx_REQ, + .map = IMX7_USB_HSIC_PHY_A_CORE_DOMAIN, }, .voltage = 1200000, - .pgc = PGC_USB_HSIC, + .pgc = IMX7_PGC_USB_HSIC, }, }; +static const struct regmap_range imx7_yes_ranges[] = { + regmap_reg_range(GPC_LPCR_A_CORE_BSC, + GPC_M4_PU_PDN_FLG), + regmap_reg_range(GPC_PGC_CTRL(IMX7_PGC_MIPI), + GPC_PGC_SR(IMX7_PGC_MIPI)), + regmap_reg_range(GPC_PGC_CTRL(IMX7_PGC_PCIE), + GPC_PGC_SR(IMX7_PGC_PCIE)), + regmap_reg_range(GPC_PGC_CTRL(IMX7_PGC_USB_HSIC), + GPC_PGC_SR(IMX7_PGC_USB_HSIC)), +}; + +static const struct regmap_access_table imx7_access_table = { + .yes_ranges = imx7_yes_ranges, + .n_yes_ranges = ARRAY_SIZE(imx7_yes_ranges), +}; + static const struct imx_pgc_domain_data imx7_pgc_domain_data = { .domains = imx7_pgc_domains, .domains_num = ARRAY_SIZE(imx7_pgc_domains), + .reg_access_table = &imx7_access_table, +}; + +static const struct imx_pgc_domain imx8m_pgc_domains[] = { + [IMX8M_POWER_DOMAIN_MIPI] = { + .genpd = { + .name = "mipi", + }, + .bits = { + .pxx = IMX8M_MIPI_SW_Pxx_REQ, + .map = IMX8M_MIPI_A53_DOMAIN, + }, + .pgc = IMX8M_PGC_MIPI, + }, + + [IMX8M_POWER_DOMAIN_PCIE1] = { + .genpd = { + .name = "pcie1", + }, + .bits = { + .pxx = IMX8M_PCIE1_SW_Pxx_REQ, + .map = IMX8M_PCIE1_A53_DOMAIN, + }, + .pgc = IMX8M_PGC_PCIE1, + }, + + [IMX8M_POWER_DOMAIN_USB_OTG1] = { + .genpd = { + .name = "usb-otg1", + }, + .bits = { + .pxx = IMX8M_OTG1_SW_Pxx_REQ, + .map = IMX8M_OTG1_A53_DOMAIN, + }, + .pgc = IMX8M_PGC_OTG1, + }, + + [IMX8M_POWER_DOMAIN_USB_OTG2] = { + .genpd = { + .name = "usb-otg2", + }, + .bits = { + .pxx = IMX8M_OTG2_SW_Pxx_REQ, + .map = IMX8M_OTG2_A53_DOMAIN, + }, + .pgc = IMX8M_PGC_OTG2, + }, + + [IMX8M_POWER_DOMAIN_DDR1] = { + .genpd = { + .name = "ddr1", + }, + .bits = { + .pxx = IMX8M_DDR1_SW_Pxx_REQ, + .map = IMX8M_DDR2_A53_DOMAIN, + }, + .pgc = IMX8M_PGC_DDR1, + }, + + [IMX8M_POWER_DOMAIN_GPU] = { + .genpd = { + .name = "gpu", + }, + .bits = { + .pxx = IMX8M_GPU_SW_Pxx_REQ, + .map = IMX8M_GPU_A53_DOMAIN, + }, + .pgc = IMX8M_PGC_GPU, + }, + + [IMX8M_POWER_DOMAIN_VPU] = { + .genpd = { + .name = "vpu", + }, + .bits = { + .pxx = IMX8M_VPU_SW_Pxx_REQ, + .map = IMX8M_VPU_A53_DOMAIN, + }, + .pgc = IMX8M_PGC_VPU, + }, + + [IMX8M_POWER_DOMAIN_DISP] = { + .genpd = { + .name = "disp", + }, + .bits = { + .pxx = IMX8M_DISP_SW_Pxx_REQ, + .map = IMX8M_DISP_A53_DOMAIN, + }, + .pgc = IMX8M_PGC_DISP, + }, + + [IMX8M_POWER_DOMAIN_MIPI_CSI1] = { + .genpd = { + .name = "mipi-csi1", + }, + .bits = { + .pxx = IMX8M_MIPI_CSI1_SW_Pxx_REQ, + .map = IMX8M_MIPI_CSI1_A53_DOMAIN, + }, + .pgc = IMX8M_PGC_MIPI_CSI1, + }, + + [IMX8M_POWER_DOMAIN_MIPI_CSI2] = { + .genpd = { + .name = "mipi-csi2", + }, + .bits = { + .pxx = IMX8M_MIPI_CSI2_SW_Pxx_REQ, + .map = IMX8M_MIPI_CSI2_A53_DOMAIN, + }, + .pgc = IMX8M_PGC_MIPI_CSI2, + }, + + [IMX8M_POWER_DOMAIN_PCIE2] = { + .genpd = { + .name = "pcie2", + }, + .bits = { + .pxx = IMX8M_PCIE2_SW_Pxx_REQ, + .map = IMX8M_PCIE2_A53_DOMAIN, + }, + .pgc = IMX8M_PGC_PCIE2, + }, +}; + +static const struct regmap_range imx8m_yes_ranges[] = { + regmap_reg_range(GPC_LPCR_A_CORE_BSC, + GPC_M4_PU_PDN_FLG), + regmap_reg_range(GPC_PGC_CTRL(IMX8M_PGC_MIPI), + GPC_PGC_SR(IMX8M_PGC_MIPI)), + regmap_reg_range(GPC_PGC_CTRL(IMX8M_PGC_PCIE1), + GPC_PGC_SR(IMX8M_PGC_PCIE1)), + regmap_reg_range(GPC_PGC_CTRL(IMX8M_PGC_OTG1), + GPC_PGC_SR(IMX8M_PGC_OTG1)), + regmap_reg_range(GPC_PGC_CTRL(IMX8M_PGC_OTG2), + GPC_PGC_SR(IMX8M_PGC_OTG2)), + regmap_reg_range(GPC_PGC_CTRL(IMX8M_PGC_DDR1), + GPC_PGC_SR(IMX8M_PGC_DDR1)), + regmap_reg_range(GPC_PGC_CTRL(IMX8M_PGC_GPU), + GPC_PGC_SR(IMX8M_PGC_GPU)), + regmap_reg_range(GPC_PGC_CTRL(IMX8M_PGC_VPU), + GPC_PGC_SR(IMX8M_PGC_VPU)), + regmap_reg_range(GPC_PGC_CTRL(IMX8M_PGC_DISP), + GPC_PGC_SR(IMX8M_PGC_DISP)), + regmap_reg_range(GPC_PGC_CTRL(IMX8M_PGC_MIPI_CSI1), + GPC_PGC_SR(IMX8M_PGC_MIPI_CSI1)), + regmap_reg_range(GPC_PGC_CTRL(IMX8M_PGC_MIPI_CSI2), + GPC_PGC_SR(IMX8M_PGC_MIPI_CSI2)), + regmap_reg_range(GPC_PGC_CTRL(IMX8M_PGC_PCIE2), + GPC_PGC_SR(IMX8M_PGC_PCIE2)), +}; + +static const struct regmap_access_table imx8m_access_table = { + .yes_ranges = imx8m_yes_ranges, + .n_yes_ranges = ARRAY_SIZE(imx8m_yes_ranges), +}; + +static const struct imx_pgc_domain_data imx8m_pgc_domain_data = { + .domains = imx8m_pgc_domains, + .domains_num = ARRAY_SIZE(imx8m_pgc_domains), + .reg_access_table = &imx8m_access_table, }; static int imx_pgc_domain_probe(struct platform_device *pdev) @@ -217,7 +440,7 @@ static int imx_pgc_domain_probe(struct platform_device *pdev) dev_err(domain->dev, "Failed to get domain's regulator\n"); return PTR_ERR(domain->regulator); } - } else { + } else if (domain->voltage) { regulator_set_voltage(domain->regulator, domain->voltage, domain->voltage); } @@ -265,27 +488,15 @@ builtin_platform_driver(imx_pgc_domain_driver) static int imx_gpcv2_probe(struct platform_device *pdev) { - static const struct imx_pgc_domain_data *domain_data; - static const struct regmap_range yes_ranges[] = { - regmap_reg_range(GPC_LPCR_A_CORE_BSC, - GPC_M4_PU_PDN_FLG), - regmap_reg_range(GPC_PGC_CTRL(PGC_MIPI), - GPC_PGC_SR(PGC_MIPI)), - regmap_reg_range(GPC_PGC_CTRL(PGC_PCIE), - GPC_PGC_SR(PGC_PCIE)), - regmap_reg_range(GPC_PGC_CTRL(PGC_USB_HSIC), - GPC_PGC_SR(PGC_USB_HSIC)), - }; - static const struct regmap_access_table access_table = { - .yes_ranges = yes_ranges, - .n_yes_ranges = ARRAY_SIZE(yes_ranges), - }; - static const struct regmap_config regmap_config = { + const struct imx_pgc_domain_data *domain_data = + of_device_get_match_data(&pdev->dev); + + struct regmap_config regmap_config = { .reg_bits = 32, .val_bits = 32, .reg_stride = 4, - .rd_table = &access_table, - .wr_table = &access_table, + .rd_table = domain_data->reg_access_table, + .wr_table = domain_data->reg_access_table, .max_register = SZ_4K, }; struct device *dev = &pdev->dev; @@ -313,8 +524,6 @@ static int imx_gpcv2_probe(struct platform_device *pdev) return ret; } - domain_data = of_device_get_match_data(&pdev->dev); - for_each_child_of_node(pgc_np, np) { struct platform_device *pd_pdev; struct imx_pgc_domain *domain; @@ -372,6 +581,7 @@ static int imx_gpcv2_probe(struct platform_device *pdev) static const struct of_device_id imx_gpcv2_dt_ids[] = { { .compatible = "fsl,imx7d-gpc", .data = &imx7_pgc_domain_data, }, + { .compatible = "fsl,imx8mq-gpc", .data = &imx8m_pgc_domain_data, }, { } }; diff --git a/drivers/soc/mediatek/Kconfig b/drivers/soc/mediatek/Kconfig @@ -4,6 +4,18 @@ menu "MediaTek SoC drivers" depends on ARCH_MEDIATEK || COMPILE_TEST +config MTK_CMDQ + tristate "MediaTek CMDQ Support" + depends on ARCH_MEDIATEK || COMPILE_TEST + select MAILBOX + select MTK_CMDQ_MBOX + select MTK_INFRACFG + help + Say yes here to add support for the MediaTek Command Queue (CMDQ) + driver. The CMDQ is used to help read/write registers with critical + time limitation, such as updating display configuration during the + vblank. + config MTK_INFRACFG bool "MediaTek INFRACFG Support" select REGMAP diff --git a/drivers/soc/mediatek/Makefile b/drivers/soc/mediatek/Makefile @@ -1,3 +1,4 @@ +obj-$(CONFIG_MTK_CMDQ) += mtk-cmdq-helper.o obj-$(CONFIG_MTK_INFRACFG) += mtk-infracfg.o obj-$(CONFIG_MTK_PMIC_WRAP) += mtk-pmic-wrap.o obj-$(CONFIG_MTK_SCPSYS) += mtk-scpsys.o diff --git a/drivers/soc/mediatek/mtk-cmdq-helper.c b/drivers/soc/mediatek/mtk-cmdq-helper.c @@ -0,0 +1,300 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// Copyright (c) 2018 MediaTek Inc. + +#include <linux/completion.h> +#include <linux/errno.h> +#include <linux/dma-mapping.h> +#include <linux/module.h> +#include <linux/mailbox_controller.h> +#include <linux/soc/mediatek/mtk-cmdq.h> + +#define CMDQ_ARG_A_WRITE_MASK 0xffff +#define CMDQ_WRITE_ENABLE_MASK BIT(0) +#define CMDQ_EOC_IRQ_EN BIT(0) +#define CMDQ_EOC_CMD ((u64)((CMDQ_CODE_EOC << CMDQ_OP_CODE_SHIFT)) \ + << 32 | CMDQ_EOC_IRQ_EN) + +static void cmdq_client_timeout(struct timer_list *t) +{ + struct cmdq_client *client = from_timer(client, t, timer); + + dev_err(client->client.dev, "cmdq timeout!\n"); +} + +struct cmdq_client *cmdq_mbox_create(struct device *dev, int index, u32 timeout) +{ + struct cmdq_client *client; + + client = kzalloc(sizeof(*client), GFP_KERNEL); + if (!client) + return (struct cmdq_client *)-ENOMEM; + + client->timeout_ms = timeout; + if (timeout != CMDQ_NO_TIMEOUT) { + spin_lock_init(&client->lock); + timer_setup(&client->timer, cmdq_client_timeout, 0); + } + client->pkt_cnt = 0; + client->client.dev = dev; + client->client.tx_block = false; + client->chan = mbox_request_channel(&client->client, index); + + if (IS_ERR(client->chan)) { + long err; + + dev_err(dev, "failed to request channel\n"); + err = PTR_ERR(client->chan); + kfree(client); + + return ERR_PTR(err); + } + + return client; +} +EXPORT_SYMBOL(cmdq_mbox_create); + +void cmdq_mbox_destroy(struct cmdq_client *client) +{ + if (client->timeout_ms != CMDQ_NO_TIMEOUT) { + spin_lock(&client->lock); + del_timer_sync(&client->timer); + spin_unlock(&client->lock); + } + mbox_free_channel(client->chan); + kfree(client); +} +EXPORT_SYMBOL(cmdq_mbox_destroy); + +struct cmdq_pkt *cmdq_pkt_create(struct cmdq_client *client, size_t size) +{ + struct cmdq_pkt *pkt; + struct device *dev; + dma_addr_t dma_addr; + + pkt = kzalloc(sizeof(*pkt), GFP_KERNEL); + if (!pkt) + return ERR_PTR(-ENOMEM); + pkt->va_base = kzalloc(size, GFP_KERNEL); + if (!pkt->va_base) { + kfree(pkt); + return ERR_PTR(-ENOMEM); + } + pkt->buf_size = size; + pkt->cl = (void *)client; + + dev = client->chan->mbox->dev; + dma_addr = dma_map_single(dev, pkt->va_base, pkt->buf_size, + DMA_TO_DEVICE); + if (dma_mapping_error(dev, dma_addr)) { + dev_err(dev, "dma map failed, size=%u\n", (u32)(u64)size); + kfree(pkt->va_base); + kfree(pkt); + return ERR_PTR(-ENOMEM); + } + + pkt->pa_base = dma_addr; + + return pkt; +} +EXPORT_SYMBOL(cmdq_pkt_create); + +void cmdq_pkt_destroy(struct cmdq_pkt *pkt) +{ + struct cmdq_client *client = (struct cmdq_client *)pkt->cl; + + dma_unmap_single(client->chan->mbox->dev, pkt->pa_base, pkt->buf_size, + DMA_TO_DEVICE); + kfree(pkt->va_base); + kfree(pkt); +} +EXPORT_SYMBOL(cmdq_pkt_destroy); + +static int cmdq_pkt_append_command(struct cmdq_pkt *pkt, enum cmdq_code code, + u32 arg_a, u32 arg_b) +{ + u64 *cmd_ptr; + + if (unlikely(pkt->cmd_buf_size + CMDQ_INST_SIZE > pkt->buf_size)) { + /* + * In the case of allocated buffer size (pkt->buf_size) is used + * up, the real required size (pkt->cmdq_buf_size) is still + * increased, so that the user knows how much memory should be + * ultimately allocated after appending all commands and + * flushing the command packet. Therefor, the user can call + * cmdq_pkt_create() again with the real required buffer size. + */ + pkt->cmd_buf_size += CMDQ_INST_SIZE; + WARN_ONCE(1, "%s: buffer size %u is too small !\n", + __func__, (u32)pkt->buf_size); + return -ENOMEM; + } + cmd_ptr = pkt->va_base + pkt->cmd_buf_size; + (*cmd_ptr) = (u64)((code << CMDQ_OP_CODE_SHIFT) | arg_a) << 32 | arg_b; + pkt->cmd_buf_size += CMDQ_INST_SIZE; + + return 0; +} + +int cmdq_pkt_write(struct cmdq_pkt *pkt, u32 value, u32 subsys, u32 offset) +{ + u32 arg_a = (offset & CMDQ_ARG_A_WRITE_MASK) | + (subsys << CMDQ_SUBSYS_SHIFT); + + return cmdq_pkt_append_command(pkt, CMDQ_CODE_WRITE, arg_a, value); +} +EXPORT_SYMBOL(cmdq_pkt_write); + +int cmdq_pkt_write_mask(struct cmdq_pkt *pkt, u32 value, + u32 subsys, u32 offset, u32 mask) +{ + u32 offset_mask = offset; + int err = 0; + + if (mask != 0xffffffff) { + err = cmdq_pkt_append_command(pkt, CMDQ_CODE_MASK, 0, ~mask); + offset_mask |= CMDQ_WRITE_ENABLE_MASK; + } + err |= cmdq_pkt_write(pkt, value, subsys, offset_mask); + + return err; +} +EXPORT_SYMBOL(cmdq_pkt_write_mask); + +int cmdq_pkt_wfe(struct cmdq_pkt *pkt, u32 event) +{ + u32 arg_b; + + if (event >= CMDQ_MAX_EVENT) + return -EINVAL; + + /* + * WFE arg_b + * bit 0-11: wait value + * bit 15: 1 - wait, 0 - no wait + * bit 16-27: update value + * bit 31: 1 - update, 0 - no update + */ + arg_b = CMDQ_WFE_UPDATE | CMDQ_WFE_WAIT | CMDQ_WFE_WAIT_VALUE; + + return cmdq_pkt_append_command(pkt, CMDQ_CODE_WFE, event, arg_b); +} +EXPORT_SYMBOL(cmdq_pkt_wfe); + +int cmdq_pkt_clear_event(struct cmdq_pkt *pkt, u32 event) +{ + if (event >= CMDQ_MAX_EVENT) + return -EINVAL; + + return cmdq_pkt_append_command(pkt, CMDQ_CODE_WFE, event, + CMDQ_WFE_UPDATE); +} +EXPORT_SYMBOL(cmdq_pkt_clear_event); + +static int cmdq_pkt_finalize(struct cmdq_pkt *pkt) +{ + int err; + + /* insert EOC and generate IRQ for each command iteration */ + err = cmdq_pkt_append_command(pkt, CMDQ_CODE_EOC, 0, CMDQ_EOC_IRQ_EN); + + /* JUMP to end */ + err |= cmdq_pkt_append_command(pkt, CMDQ_CODE_JUMP, 0, CMDQ_JUMP_PASS); + + return err; +} + +static void cmdq_pkt_flush_async_cb(struct cmdq_cb_data data) +{ + struct cmdq_pkt *pkt = (struct cmdq_pkt *)data.data; + struct cmdq_task_cb *cb = &pkt->cb; + struct cmdq_client *client = (struct cmdq_client *)pkt->cl; + + if (client->timeout_ms != CMDQ_NO_TIMEOUT) { + unsigned long flags = 0; + + spin_lock_irqsave(&client->lock, flags); + if (--client->pkt_cnt == 0) + del_timer(&client->timer); + else + mod_timer(&client->timer, jiffies + + msecs_to_jiffies(client->timeout_ms)); + spin_unlock_irqrestore(&client->lock, flags); + } + + dma_sync_single_for_cpu(client->chan->mbox->dev, pkt->pa_base, + pkt->cmd_buf_size, DMA_TO_DEVICE); + if (cb->cb) { + data.data = cb->data; + cb->cb(data); + } +} + +int cmdq_pkt_flush_async(struct cmdq_pkt *pkt, cmdq_async_flush_cb cb, + void *data) +{ + int err; + unsigned long flags = 0; + struct cmdq_client *client = (struct cmdq_client *)pkt->cl; + + err = cmdq_pkt_finalize(pkt); + if (err < 0) + return err; + + pkt->cb.cb = cb; + pkt->cb.data = data; + pkt->async_cb.cb = cmdq_pkt_flush_async_cb; + pkt->async_cb.data = pkt; + + dma_sync_single_for_device(client->chan->mbox->dev, pkt->pa_base, + pkt->cmd_buf_size, DMA_TO_DEVICE); + + if (client->timeout_ms != CMDQ_NO_TIMEOUT) { + spin_lock_irqsave(&client->lock, flags); + if (client->pkt_cnt++ == 0) + mod_timer(&client->timer, jiffies + + msecs_to_jiffies(client->timeout_ms)); + spin_unlock_irqrestore(&client->lock, flags); + } + + mbox_send_message(client->chan, pkt); + /* We can send next packet immediately, so just call txdone. */ + mbox_client_txdone(client->chan, 0); + + return 0; +} +EXPORT_SYMBOL(cmdq_pkt_flush_async); + +struct cmdq_flush_completion { + struct completion cmplt; + bool err; +}; + +static void cmdq_pkt_flush_cb(struct cmdq_cb_data data) +{ + struct cmdq_flush_completion *cmplt; + + cmplt = (struct cmdq_flush_completion *)data.data; + if (data.sta != CMDQ_CB_NORMAL) + cmplt->err = true; + else + cmplt->err = false; + complete(&cmplt->cmplt); +} + +int cmdq_pkt_flush(struct cmdq_pkt *pkt) +{ + struct cmdq_flush_completion cmplt; + int err; + + init_completion(&cmplt.cmplt); + err = cmdq_pkt_flush_async(pkt, cmdq_pkt_flush_cb, &cmplt); + if (err < 0) + return err; + wait_for_completion(&cmplt.cmplt); + + return cmplt.err ? -EFAULT : 0; +} +EXPORT_SYMBOL(cmdq_pkt_flush); + +MODULE_LICENSE("GPL v2"); diff --git a/drivers/soc/qcom/Kconfig b/drivers/soc/qcom/Kconfig @@ -75,11 +75,6 @@ config QCOM_QMI_HELPERS tristate depends on ARCH_QCOM || COMPILE_TEST depends on NET - help - Helper library for handling QMI encoded messages. QMI encoded - messages are used in communication between the majority of QRTR - clients and this helpers provide the common functionality needed for - doing this from a kernel driver. config QCOM_RMTFS_MEM tristate "Qualcomm Remote Filesystem memory driver" diff --git a/drivers/soc/qcom/cmd-db.c b/drivers/soc/qcom/cmd-db.c @@ -101,8 +101,7 @@ static bool cmd_db_magic_matches(const struct cmd_db_header *header) static struct cmd_db_header *cmd_db_header; - -static inline void *rsc_to_entry_header(struct rsc_hdr *hdr) +static inline const void *rsc_to_entry_header(const struct rsc_hdr *hdr) { u16 offset = le16_to_cpu(hdr->header_offset); @@ -110,7 +109,7 @@ static inline void *rsc_to_entry_header(struct rsc_hdr *hdr) } static inline void * -rsc_offset(struct rsc_hdr *hdr, struct entry_header *ent) +rsc_offset(const struct rsc_hdr *hdr, const struct entry_header *ent) { u16 offset = le16_to_cpu(hdr->data_offset); u16 loffset = le16_to_cpu(ent->offset); @@ -134,11 +133,11 @@ int cmd_db_ready(void) } EXPORT_SYMBOL(cmd_db_ready); -static int cmd_db_get_header(const char *id, struct entry_header *eh, - struct rsc_hdr *rh) +static int cmd_db_get_header(const char *id, const struct entry_header **eh, + const struct rsc_hdr **rh) { - struct rsc_hdr *rsc_hdr; - struct entry_header *ent; + const struct rsc_hdr *rsc_hdr; + const struct entry_header *ent; int ret, i, j; u8 query[8]; @@ -146,9 +145,6 @@ static int cmd_db_get_header(const char *id, struct entry_header *eh, if (ret) return ret; - if (!eh || !rh) - return -EINVAL; - /* Pad out query string to same length as in DB */ strncpy(query, id, sizeof(query)); @@ -159,14 +155,13 @@ static int cmd_db_get_header(const char *id, struct entry_header *eh, ent = rsc_to_entry_header(rsc_hdr); for (j = 0; j < le16_to_cpu(rsc_hdr->cnt); j++, ent++) { - if (memcmp(ent->id, query, sizeof(ent->id)) == 0) - break; - } - - if (j < le16_to_cpu(rsc_hdr->cnt)) { - memcpy(eh, ent, sizeof(*ent)); - memcpy(rh, rsc_hdr, sizeof(*rh)); - return 0; + if (memcmp(ent->id, query, sizeof(ent->id)) == 0) { + if (eh) + *eh = ent; + if (rh) + *rh = rsc_hdr; + return 0; + } } } @@ -186,69 +181,40 @@ static int cmd_db_get_header(const char *id, struct entry_header *eh, u32 cmd_db_read_addr(const char *id) { int ret; - struct entry_header ent; - struct rsc_hdr rsc_hdr; + const struct entry_header *ent; - ret = cmd_db_get_header(id, &ent, &rsc_hdr); + ret = cmd_db_get_header(id, &ent, NULL); - return ret < 0 ? 0 : le32_to_cpu(ent.addr); + return ret < 0 ? 0 : le32_to_cpu(ent->addr); } EXPORT_SYMBOL(cmd_db_read_addr); /** * cmd_db_read_aux_data() - Query command db for aux data. * - * @id: Resource to retrieve AUX Data on. - * @data: Data buffer to copy returned aux data to. Returns size on NULL - * @len: Caller provides size of data buffer passed in. + * @id: Resource to retrieve AUX Data on + * @len: size of data buffer returned * - * Return: size of data on success, errno otherwise + * Return: pointer to data on success, error pointer otherwise */ -int cmd_db_read_aux_data(const char *id, u8 *data, size_t len) +const void *cmd_db_read_aux_data(const char *id, size_t *len) { int ret; - struct entry_header ent; - struct rsc_hdr rsc_hdr; - u16 ent_len; - - if (!data) - return -EINVAL; + const struct entry_header *ent; + const struct rsc_hdr *rsc_hdr; ret = cmd_db_get_header(id, &ent, &rsc_hdr); if (ret) - return ret; + return ERR_PTR(ret); - ent_len = le16_to_cpu(ent.len); - if (len < ent_len) - return -EINVAL; - - len = min_t(u16, ent_len, len); - memcpy(data, rsc_offset(&rsc_hdr, &ent), len); + if (len) + *len = le16_to_cpu(ent->len); - return len; + return rsc_offset(rsc_hdr, ent); } EXPORT_SYMBOL(cmd_db_read_aux_data); /** - * cmd_db_read_aux_data_len - Get the length of the auxiliary data stored in DB. - * - * @id: Resource to retrieve AUX Data. - * - * Return: size on success, 0 on error - */ -size_t cmd_db_read_aux_data_len(const char *id) -{ - int ret; - struct entry_header ent; - struct rsc_hdr rsc_hdr; - - ret = cmd_db_get_header(id, &ent, &rsc_hdr); - - return ret < 0 ? 0 : le16_to_cpu(ent.len); -} -EXPORT_SYMBOL(cmd_db_read_aux_data_len); - -/** * cmd_db_read_slave_id - Get the slave ID for a given resource address * * @id: Resource id to query the DB for version @@ -258,15 +224,14 @@ EXPORT_SYMBOL(cmd_db_read_aux_data_len); enum cmd_db_hw_type cmd_db_read_slave_id(const char *id) { int ret; - struct entry_header ent; - struct rsc_hdr rsc_hdr; + const struct entry_header *ent; u32 addr; - ret = cmd_db_get_header(id, &ent, &rsc_hdr); + ret = cmd_db_get_header(id, &ent, NULL); if (ret < 0) return CMD_DB_HW_INVALID; - addr = le32_to_cpu(ent.addr); + addr = le32_to_cpu(ent->addr); return (addr >> SLAVE_ID_SHIFT) & SLAVE_ID_MASK; } EXPORT_SYMBOL(cmd_db_read_slave_id); diff --git a/drivers/soc/qcom/llcc-slice.c b/drivers/soc/qcom/llcc-slice.c @@ -95,7 +95,8 @@ EXPORT_SYMBOL_GPL(llcc_slice_getd); */ void llcc_slice_putd(struct llcc_slice_desc *desc) { - kfree(desc); + if (!IS_ERR_OR_NULL(desc)) + kfree(desc); } EXPORT_SYMBOL_GPL(llcc_slice_putd); @@ -142,6 +143,9 @@ int llcc_slice_activate(struct llcc_slice_desc *desc) int ret; u32 act_ctrl_val; + if (IS_ERR_OR_NULL(desc)) + return -EINVAL; + mutex_lock(&drv_data->lock); if (test_bit(desc->slice_id, drv_data->bitmap)) { mutex_unlock(&drv_data->lock); @@ -176,6 +180,9 @@ int llcc_slice_deactivate(struct llcc_slice_desc *desc) u32 act_ctrl_val; int ret; + if (IS_ERR_OR_NULL(desc)) + return -EINVAL; + mutex_lock(&drv_data->lock); if (!test_bit(desc->slice_id, drv_data->bitmap)) { mutex_unlock(&drv_data->lock); @@ -203,6 +210,9 @@ EXPORT_SYMBOL_GPL(llcc_slice_deactivate); */ int llcc_get_slice_id(struct llcc_slice_desc *desc) { + if (IS_ERR_OR_NULL(desc)) + return -EINVAL; + return desc->slice_id; } EXPORT_SYMBOL_GPL(llcc_get_slice_id); @@ -213,6 +223,9 @@ EXPORT_SYMBOL_GPL(llcc_get_slice_id); */ size_t llcc_get_slice_size(struct llcc_slice_desc *desc) { + if (IS_ERR_OR_NULL(desc)) + return 0; + return desc->slice_size; } EXPORT_SYMBOL_GPL(llcc_get_slice_size); @@ -360,5 +373,5 @@ int qcom_llcc_probe(struct platform_device *pdev, return ret; } EXPORT_SYMBOL_GPL(qcom_llcc_probe); - MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("Qualcomm Last Level Cache Controller"); diff --git a/drivers/soc/qcom/qcom-geni-se.c b/drivers/soc/qcom/qcom-geni-se.c @@ -215,6 +215,16 @@ static void geni_se_io_init(void __iomem *base) writel_relaxed(FORCE_DEFAULT, base + GENI_FORCE_DEFAULT_REG); } +static void geni_se_irq_clear(struct geni_se *se) +{ + writel_relaxed(0, se->base + SE_GSI_EVENT_EN); + writel_relaxed(0xffffffff, se->base + SE_GENI_M_IRQ_CLEAR); + writel_relaxed(0xffffffff, se->base + SE_GENI_S_IRQ_CLEAR); + writel_relaxed(0xffffffff, se->base + SE_DMA_TX_IRQ_CLR); + writel_relaxed(0xffffffff, se->base + SE_DMA_RX_IRQ_CLR); + writel_relaxed(0xffffffff, se->base + SE_IRQ_EN); +} + /** * geni_se_init() - Initialize the GENI serial engine * @se: Pointer to the concerned serial engine. @@ -228,6 +238,7 @@ void geni_se_init(struct geni_se *se, u32 rx_wm, u32 rx_rfr) { u32 val; + geni_se_irq_clear(se); geni_se_io_init(se->base); geni_se_io_set_mode(se->base); @@ -249,12 +260,7 @@ static void geni_se_select_fifo_mode(struct geni_se *se) u32 proto = geni_se_read_proto(se); u32 val; - writel_relaxed(0, se->base + SE_GSI_EVENT_EN); - writel_relaxed(0xffffffff, se->base + SE_GENI_M_IRQ_CLEAR); - writel_relaxed(0xffffffff, se->base + SE_GENI_S_IRQ_CLEAR); - writel_relaxed(0xffffffff, se->base + SE_DMA_TX_IRQ_CLR); - writel_relaxed(0xffffffff, se->base + SE_DMA_RX_IRQ_CLR); - writel_relaxed(0xffffffff, se->base + SE_IRQ_EN); + geni_se_irq_clear(se); val = readl_relaxed(se->base + SE_GENI_M_IRQ_EN); if (proto != GENI_SE_UART) { @@ -277,12 +283,7 @@ static void geni_se_select_dma_mode(struct geni_se *se) { u32 val; - writel_relaxed(0, se->base + SE_GSI_EVENT_EN); - writel_relaxed(0xffffffff, se->base + SE_GENI_M_IRQ_CLEAR); - writel_relaxed(0xffffffff, se->base + SE_GENI_S_IRQ_CLEAR); - writel_relaxed(0xffffffff, se->base + SE_DMA_TX_IRQ_CLR); - writel_relaxed(0xffffffff, se->base + SE_DMA_RX_IRQ_CLR); - writel_relaxed(0xffffffff, se->base + SE_IRQ_EN); + geni_se_irq_clear(se); val = readl_relaxed(se->base + SE_GENI_DMA_MODE_EN); val |= GENI_DMA_MODE_EN; diff --git a/drivers/soc/qcom/qmi_interface.c b/drivers/soc/qcom/qmi_interface.c @@ -318,7 +318,7 @@ int qmi_txn_init(struct qmi_handle *qmi, struct qmi_txn *txn, txn->dest = c_struct; mutex_lock(&qmi->txn_lock); - ret = idr_alloc_cyclic(&qmi->txns, txn, 0, INT_MAX, GFP_KERNEL); + ret = idr_alloc_cyclic(&qmi->txns, txn, 0, U16_MAX, GFP_KERNEL); if (ret < 0) pr_err("failed to allocate transaction id\n"); diff --git a/drivers/soc/qcom/smd-rpm.c b/drivers/soc/qcom/smd-rpm.c @@ -227,6 +227,7 @@ static const struct of_device_id qcom_smd_rpm_of_match[] = { { .compatible = "qcom,rpm-msm8974" }, { .compatible = "qcom,rpm-msm8996" }, { .compatible = "qcom,rpm-msm8998" }, + { .compatible = "qcom,rpm-qcs404" }, {} }; MODULE_DEVICE_TABLE(of, qcom_smd_rpm_of_match); diff --git a/drivers/soc/renesas/r8a77965-sysc.c b/drivers/soc/renesas/r8a77965-sysc.c @@ -28,7 +28,6 @@ static const struct rcar_sysc_area r8a77965_areas[] __initconst = { { "a2vc1", 0x3c0, 1, R8A77965_PD_A2VC1, R8A77965_PD_A3VC }, { "3dg-a", 0x100, 0, R8A77965_PD_3DG_A, R8A77965_PD_ALWAYS_ON }, { "3dg-b", 0x100, 1, R8A77965_PD_3DG_B, R8A77965_PD_3DG_A }, - { "a3ir", 0x180, 0, R8A77965_PD_A3IR, R8A77965_PD_ALWAYS_ON }, }; const struct rcar_sysc_info r8a77965_sysc_info __initconst = { diff --git a/drivers/soc/renesas/r8a77970-sysc.c b/drivers/soc/renesas/r8a77970-sysc.c @@ -20,12 +20,11 @@ static const struct rcar_sysc_area r8a77970_areas[] __initconst = { PD_CPU_NOCR }, { "ca53-cpu1", 0x200, 1, R8A77970_PD_CA53_CPU1, R8A77970_PD_CA53_SCU, PD_CPU_NOCR }, - { "cr7", 0x240, 0, R8A77970_PD_CR7, R8A77970_PD_ALWAYS_ON }, { "a3ir", 0x180, 0, R8A77970_PD_A3IR, R8A77970_PD_ALWAYS_ON }, { "a2ir0", 0x400, 0, R8A77970_PD_A2IR0, R8A77970_PD_A3IR }, { "a2ir1", 0x400, 1, R8A77970_PD_A2IR1, R8A77970_PD_A3IR }, - { "a2ir2", 0x400, 2, R8A77970_PD_A2IR2, R8A77970_PD_A3IR }, - { "a2ir3", 0x400, 3, R8A77970_PD_A2IR3, R8A77970_PD_A3IR }, + { "a2dp", 0x400, 2, R8A77970_PD_A2DP, R8A77970_PD_A3IR }, + { "a2cn", 0x400, 3, R8A77970_PD_A2CN, R8A77970_PD_A3IR }, { "a2sc0", 0x400, 4, R8A77970_PD_A2SC0, R8A77970_PD_A3IR }, { "a2sc1", 0x400, 5, R8A77970_PD_A2SC1, R8A77970_PD_A3IR }, }; diff --git a/drivers/soc/renesas/r8a77980-sysc.c b/drivers/soc/renesas/r8a77980-sysc.c @@ -38,12 +38,12 @@ static const struct rcar_sysc_area r8a77980_areas[] __initconst = { { "a2sc2", 0x400, 8, R8A77980_PD_A2SC2, R8A77980_PD_A3IR }, { "a2sc3", 0x400, 9, R8A77980_PD_A2SC3, R8A77980_PD_A3IR }, { "a2sc4", 0x400, 10, R8A77980_PD_A2SC4, R8A77980_PD_A3IR }, - { "a2pd0", 0x400, 11, R8A77980_PD_A2PD0, R8A77980_PD_A3IR }, - { "a2pd1", 0x400, 12, R8A77980_PD_A2PD1, R8A77980_PD_A3IR }, + { "a2dp0", 0x400, 11, R8A77980_PD_A2DP0, R8A77980_PD_A3IR }, + { "a2dp1", 0x400, 12, R8A77980_PD_A2DP1, R8A77980_PD_A3IR }, { "a2cn", 0x400, 13, R8A77980_PD_A2CN, R8A77980_PD_A3IR }, - { "a3vip", 0x2c0, 0, R8A77980_PD_A3VIP, R8A77980_PD_ALWAYS_ON }, - { "a3vip1", 0x300, 0, R8A77980_PD_A3VIP1, R8A77980_PD_A3VIP }, - { "a3vip2", 0x280, 0, R8A77980_PD_A3VIP2, R8A77980_PD_A3VIP }, + { "a3vip0", 0x2c0, 0, R8A77980_PD_A3VIP0, R8A77980_PD_ALWAYS_ON }, + { "a3vip1", 0x300, 0, R8A77980_PD_A3VIP1, R8A77980_PD_ALWAYS_ON }, + { "a3vip2", 0x280, 0, R8A77980_PD_A3VIP2, R8A77980_PD_ALWAYS_ON }, }; const struct rcar_sysc_info r8a77980_sysc_info __initconst = { diff --git a/drivers/soc/renesas/r8a77990-sysc.c b/drivers/soc/renesas/r8a77990-sysc.c @@ -28,19 +28,6 @@ static struct rcar_sysc_area r8a77990_areas[] __initdata = { { "3dg-b", 0x100, 1, R8A77990_PD_3DG_B, R8A77990_PD_3DG_A }, }; -static void __init rcar_sysc_fix_parent(struct rcar_sysc_area *areas, - unsigned int num_areas, u8 id, - int new_parent) -{ - unsigned int i; - - for (i = 0; i < num_areas; i++) - if (areas[i].isr_bit == id) { - areas[i].parent = new_parent; - return; - } -} - /* Fixups for R-Car E3 ES1.0 revision */ static const struct soc_device_attribute r8a77990[] __initconst = { { .soc_id = "r8a77990", .revision = "ES1.0" }, @@ -50,12 +37,10 @@ static const struct soc_device_attribute r8a77990[] __initconst = { static int __init r8a77990_sysc_init(void) { if (soc_device_match(r8a77990)) { - rcar_sysc_fix_parent(r8a77990_areas, - ARRAY_SIZE(r8a77990_areas), - R8A77990_PD_3DG_A, R8A77990_PD_3DG_B); - rcar_sysc_fix_parent(r8a77990_areas, - ARRAY_SIZE(r8a77990_areas), - R8A77990_PD_3DG_B, R8A77990_PD_ALWAYS_ON); + /* Fix incorrect 3DG hierarchy */ + swap(r8a77990_areas[7], r8a77990_areas[8]); + r8a77990_areas[7].parent = R8A77990_PD_ALWAYS_ON; + r8a77990_areas[8].parent = R8A77990_PD_3DG_B; } return 0; diff --git a/drivers/soc/renesas/rcar-sysc.c b/drivers/soc/renesas/rcar-sysc.c @@ -105,6 +105,15 @@ static int rcar_sysc_power(const struct rcar_sysc_ch *sysc_ch, bool on) spin_lock_irqsave(&rcar_sysc_lock, flags); + /* + * The interrupt source needs to be enabled, but masked, to prevent the + * CPU from receiving it. + */ + iowrite32(ioread32(rcar_sysc_base + SYSCIMR) | isr_mask, + rcar_sysc_base + SYSCIMR); + iowrite32(ioread32(rcar_sysc_base + SYSCIER) | isr_mask, + rcar_sysc_base + SYSCIER); + iowrite32(isr_mask, rcar_sysc_base + SYSCISCR); /* Submit power shutoff or resume request until it was accepted */ @@ -146,16 +155,6 @@ static int rcar_sysc_power(const struct rcar_sysc_ch *sysc_ch, bool on) return ret; } -static int rcar_sysc_power_down(const struct rcar_sysc_ch *sysc_ch) -{ - return rcar_sysc_power(sysc_ch, false); -} - -static int rcar_sysc_power_up(const struct rcar_sysc_ch *sysc_ch) -{ - return rcar_sysc_power(sysc_ch, true); -} - static bool rcar_sysc_power_is_off(const struct rcar_sysc_ch *sysc_ch) { unsigned int st; @@ -184,7 +183,7 @@ static int rcar_sysc_pd_power_off(struct generic_pm_domain *genpd) struct rcar_sysc_pd *pd = to_rcar_pd(genpd); pr_debug("%s: %s\n", __func__, genpd->name); - return rcar_sysc_power_down(&pd->ch); + return rcar_sysc_power(&pd->ch, false); } static int rcar_sysc_pd_power_on(struct generic_pm_domain *genpd) @@ -192,7 +191,7 @@ static int rcar_sysc_pd_power_on(struct generic_pm_domain *genpd) struct rcar_sysc_pd *pd = to_rcar_pd(genpd); pr_debug("%s: %s\n", __func__, genpd->name); - return rcar_sysc_power_up(&pd->ch); + return rcar_sysc_power(&pd->ch, true); } static bool has_cpg_mstp; @@ -252,7 +251,7 @@ static int __init rcar_sysc_pd_setup(struct rcar_sysc_pd *pd) goto finalize; } - rcar_sysc_power_up(&pd->ch); + rcar_sysc_power(&pd->ch, true); finalize: error = pm_genpd_init(genpd, gov, false); @@ -334,7 +333,6 @@ static int __init rcar_sysc_pd_init(void) const struct of_device_id *match; struct rcar_pm_domains *domains; struct device_node *np; - u32 syscier, syscimr; void __iomem *base; unsigned int i; int error; @@ -373,27 +371,6 @@ static int __init rcar_sysc_pd_init(void) domains->onecell_data.num_domains = ARRAY_SIZE(domains->domains); rcar_sysc_onecell_data = &domains->onecell_data; - for (i = 0, syscier = 0; i < info->num_areas; i++) - syscier |= BIT(info->areas[i].isr_bit); - - /* - * Mask all interrupt sources to prevent the CPU from receiving them. - * Make sure not to clear reserved bits that were set before. - */ - syscimr = ioread32(base + SYSCIMR); - syscimr |= syscier; - pr_debug("%pOF: syscimr = 0x%08x\n", np, syscimr); - iowrite32(syscimr, base + SYSCIMR); - - /* - * SYSC needs all interrupt sources enabled to control power. - */ - pr_debug("%pOF: syscier = 0x%08x\n", np, syscier); - iowrite32(syscier, base + SYSCIER); - - /* - * First, create all PM domains - */ for (i = 0; i < info->num_areas; i++) { const struct rcar_sysc_area *area = &info->areas[i]; struct rcar_sysc_pd *pd; @@ -421,22 +398,17 @@ static int __init rcar_sysc_pd_init(void) goto out_put; domains->domains[area->isr_bit] = &pd->genpd; - } - /* - * Second, link all PM domains to their parents - */ - for (i = 0; i < info->num_areas; i++) { - const struct rcar_sysc_area *area = &info->areas[i]; - - if (!area->name || area->parent < 0) + if (area->parent < 0) continue; error = pm_genpd_add_subdomain(domains->domains[area->parent], - domains->domains[area->isr_bit]); - if (error) + &pd->genpd); + if (error) { pr_warn("Failed to add PM subdomain %s to parent %u\n", area->name, area->parent); + goto out_put; + } } error = of_genpd_add_provider_onecell(np, &domains->onecell_data); @@ -478,8 +450,7 @@ static int rcar_sysc_power_cpu(unsigned int idx, bool on) if (!(pd->flags & PD_CPU) || pd->ch.chan_bit != idx) continue; - return on ? rcar_sysc_power_up(&pd->ch) - : rcar_sysc_power_down(&pd->ch); + return rcar_sysc_power(&pd->ch, on); } return -ENOENT; diff --git a/drivers/soc/rockchip/pm_domains.c b/drivers/soc/rockchip/pm_domains.c @@ -21,7 +21,9 @@ #include <linux/mfd/syscon.h> #include <dt-bindings/power/px30-power.h> #include <dt-bindings/power/rk3036-power.h> +#include <dt-bindings/power/rk3066-power.h> #include <dt-bindings/power/rk3128-power.h> +#include <dt-bindings/power/rk3188-power.h> #include <dt-bindings/power/rk3228-power.h> #include <dt-bindings/power/rk3288-power.h> #include <dt-bindings/power/rk3328-power.h> @@ -737,6 +739,14 @@ static const struct rockchip_domain_info rk3036_pm_domains[] = { [RK3036_PD_SYS] = DOMAIN_RK3036(8, 22, 29, false), }; +static const struct rockchip_domain_info rk3066_pm_domains[] = { + [RK3066_PD_GPU] = DOMAIN(9, 9, 3, 24, 29, false), + [RK3066_PD_VIDEO] = DOMAIN(8, 8, 4, 23, 28, false), + [RK3066_PD_VIO] = DOMAIN(7, 7, 5, 22, 27, false), + [RK3066_PD_PERI] = DOMAIN(6, 6, 2, 25, 30, false), + [RK3066_PD_CPU] = DOMAIN(-1, 5, 1, 26, 31, false), +}; + static const struct rockchip_domain_info rk3128_pm_domains[] = { [RK3128_PD_CORE] = DOMAIN_RK3288(0, 0, 4, false), [RK3128_PD_MSCH] = DOMAIN_RK3288(-1, -1, 6, true), @@ -745,6 +755,14 @@ static const struct rockchip_domain_info rk3128_pm_domains[] = { [RK3128_PD_GPU] = DOMAIN_RK3288(1, 1, 3, false), }; +static const struct rockchip_domain_info rk3188_pm_domains[] = { + [RK3188_PD_GPU] = DOMAIN(9, 9, 3, 24, 29, false), + [RK3188_PD_VIDEO] = DOMAIN(8, 8, 4, 23, 28, false), + [RK3188_PD_VIO] = DOMAIN(7, 7, 5, 22, 27, false), + [RK3188_PD_PERI] = DOMAIN(6, 6, 2, 25, 30, false), + [RK3188_PD_CPU] = DOMAIN(5, 5, 1, 26, 31, false), +}; + static const struct rockchip_domain_info rk3228_pm_domains[] = { [RK3228_PD_CORE] = DOMAIN_RK3036(0, 0, 16, true), [RK3228_PD_MSCH] = DOMAIN_RK3036(1, 1, 17, true), @@ -846,6 +864,17 @@ static const struct rockchip_pmu_info rk3036_pmu = { .domain_info = rk3036_pm_domains, }; +static const struct rockchip_pmu_info rk3066_pmu = { + .pwr_offset = 0x08, + .status_offset = 0x0c, + .req_offset = 0x38, /* PMU_MISC_CON1 */ + .idle_offset = 0x0c, + .ack_offset = 0x0c, + + .num_domains = ARRAY_SIZE(rk3066_pm_domains), + .domain_info = rk3066_pm_domains, +}; + static const struct rockchip_pmu_info rk3128_pmu = { .pwr_offset = 0x04, .status_offset = 0x08, @@ -857,6 +886,17 @@ static const struct rockchip_pmu_info rk3128_pmu = { .domain_info = rk3128_pm_domains, }; +static const struct rockchip_pmu_info rk3188_pmu = { + .pwr_offset = 0x08, + .status_offset = 0x0c, + .req_offset = 0x38, /* PMU_MISC_CON1 */ + .idle_offset = 0x0c, + .ack_offset = 0x0c, + + .num_domains = ARRAY_SIZE(rk3188_pm_domains), + .domain_info = rk3188_pm_domains, +}; + static const struct rockchip_pmu_info rk3228_pmu = { .req_offset = 0x40c, .idle_offset = 0x488, @@ -949,10 +989,18 @@ static const struct of_device_id rockchip_pm_domain_dt_match[] = { .data = (void *)&rk3036_pmu, }, { + .compatible = "rockchip,rk3066-power-controller", + .data = (void *)&rk3066_pmu, + }, + { .compatible = "rockchip,rk3128-power-controller", .data = (void *)&rk3128_pmu, }, { + .compatible = "rockchip,rk3188-power-controller", + .data = (void *)&rk3188_pmu, + }, + { .compatible = "rockchip,rk3228-power-controller", .data = (void *)&rk3228_pmu, }, diff --git a/drivers/soc/sunxi/sunxi_sram.c b/drivers/soc/sunxi/sunxi_sram.c @@ -155,17 +155,7 @@ static int sunxi_sram_show(struct seq_file *s, void *data) return 0; } -static int sunxi_sram_open(struct inode *inode, struct file *file) -{ - return single_open(file, sunxi_sram_show, inode->i_private); -} - -static const struct file_operations sunxi_sram_fops = { - .open = sunxi_sram_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, -}; +DEFINE_SHOW_ATTRIBUTE(sunxi_sram); static inline struct sunxi_sram_desc *to_sram_desc(const struct sunxi_sram_data *data) { @@ -300,6 +290,10 @@ static const struct sunxi_sramc_variant sun4i_a10_sramc_variant = { /* Nothing special */ }; +static const struct sunxi_sramc_variant sun8i_h3_sramc_variant = { + .has_emac_clock = true, +}; + static const struct sunxi_sramc_variant sun50i_a64_sramc_variant = { .has_emac_clock = true, }; @@ -379,7 +373,7 @@ static const struct of_device_id sunxi_sram_dt_match[] = { }, { .compatible = "allwinner,sun8i-h3-system-control", - .data = &sun4i_a10_sramc_variant, + .data = &sun8i_h3_sramc_variant, }, { .compatible = "allwinner,sun50i-a64-sram-controller", @@ -389,6 +383,10 @@ static const struct of_device_id sunxi_sram_dt_match[] = { .compatible = "allwinner,sun50i-a64-system-control", .data = &sun50i_a64_sramc_variant, }, + { + .compatible = "allwinner,sun50i-h5-system-control", + .data = &sun50i_a64_sramc_variant, + }, { }, }; MODULE_DEVICE_TABLE(of, sunxi_sram_dt_match); diff --git a/drivers/soc/tegra/common.c b/drivers/soc/tegra/common.c @@ -22,11 +22,15 @@ static const struct of_device_id tegra_machine_match[] = { bool soc_is_tegra(void) { + const struct of_device_id *match; struct device_node *root; root = of_find_node_by_path("/"); if (!root) return false; - return of_match_node(tegra_machine_match, root) != NULL; + match = of_match_node(tegra_machine_match, root); + of_node_put(root); + + return match != NULL; } diff --git a/drivers/soc/tegra/pmc.c b/drivers/soc/tegra/pmc.c @@ -2,6 +2,7 @@ * drivers/soc/tegra/pmc.c * * Copyright (c) 2010 Google, Inc + * Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. * * Author: * Colin Cross <ccross@google.com> @@ -29,9 +30,12 @@ #include <linux/init.h> #include <linux/io.h> #include <linux/iopoll.h> +#include <linux/irq.h> +#include <linux/irqdomain.h> #include <linux/of.h> #include <linux/of_address.h> #include <linux/of_clk.h> +#include <linux/of_irq.h> #include <linux/of_platform.h> #include <linux/pinctrl/pinctrl.h> #include <linux/pinctrl/pinconf.h> @@ -48,7 +52,10 @@ #include <soc/tegra/fuse.h> #include <soc/tegra/pmc.h> +#include <dt-bindings/interrupt-controller/arm-gic.h> #include <dt-bindings/pinctrl/pinctrl-tegra-io-pad.h> +#include <dt-bindings/gpio/tegra186-gpio.h> +#include <dt-bindings/gpio/tegra194-gpio.h> #define PMC_CNTRL 0x0 #define PMC_CNTRL_INTR_POLARITY BIT(17) /* inverts INTR polarity */ @@ -92,7 +99,6 @@ #define PMC_SENSOR_CTRL_SCRATCH_WRITE BIT(2) #define PMC_SENSOR_CTRL_ENABLE_RST BIT(1) -#define PMC_RST_STATUS 0x1b4 #define PMC_RST_STATUS_POR 0 #define PMC_RST_STATUS_WATCHDOG 1 #define PMC_RST_STATUS_SENSOR 2 @@ -126,6 +132,16 @@ #define GPU_RG_CNTRL 0x2d4 /* Tegra186 and later */ +#define WAKE_AOWAKE_CNTRL(x) (0x000 + ((x) << 2)) +#define WAKE_AOWAKE_CNTRL_LEVEL (1 << 3) +#define WAKE_AOWAKE_MASK_W(x) (0x180 + ((x) << 2)) +#define WAKE_AOWAKE_MASK_R(x) (0x300 + ((x) << 2)) +#define WAKE_AOWAKE_STATUS_W(x) (0x30c + ((x) << 2)) +#define WAKE_AOWAKE_STATUS_R(x) (0x48c + ((x) << 2)) +#define WAKE_AOWAKE_TIER0_ROUTING(x) (0x4b4 + ((x) << 2)) +#define WAKE_AOWAKE_TIER1_ROUTING(x) (0x4c0 + ((x) << 2)) +#define WAKE_AOWAKE_TIER2_ROUTING(x) (0x4cc + ((x) << 2)) + #define WAKE_AOWAKE_CTRL 0x4f4 #define WAKE_AOWAKE_CTRL_INTR_POLARITY BIT(0) @@ -151,8 +167,45 @@ struct tegra_pmc_regs { unsigned int dpd_status; unsigned int dpd2_req; unsigned int dpd2_status; + unsigned int rst_status; + unsigned int rst_source_shift; + unsigned int rst_source_mask; + unsigned int rst_level_shift; + unsigned int rst_level_mask; +}; + +struct tegra_wake_event { + const char *name; + unsigned int id; + unsigned int irq; + struct { + unsigned int instance; + unsigned int pin; + } gpio; }; +#define TEGRA_WAKE_IRQ(_name, _id, _irq) \ + { \ + .name = _name, \ + .id = _id, \ + .irq = _irq, \ + .gpio = { \ + .instance = UINT_MAX, \ + .pin = UINT_MAX, \ + }, \ + } + +#define TEGRA_WAKE_GPIO(_name, _id, _instance, _pin) \ + { \ + .name = _name, \ + .id = _id, \ + .irq = 0, \ + .gpio = { \ + .instance = _instance, \ + .pin = _pin, \ + }, \ + } + struct tegra_pmc_soc { unsigned int num_powergates; const char *const *powergates; @@ -175,6 +228,45 @@ struct tegra_pmc_soc { void (*setup_irq_polarity)(struct tegra_pmc *pmc, struct device_node *np, bool invert); + + const char * const *reset_sources; + unsigned int num_reset_sources; + const char * const *reset_levels; + unsigned int num_reset_levels; + + const struct tegra_wake_event *wake_events; + unsigned int num_wake_events; +}; + +static const char * const tegra186_reset_sources[] = { + "SYS_RESET", + "AOWDT", + "MCCPLEXWDT", + "BPMPWDT", + "SCEWDT", + "SPEWDT", + "APEWDT", + "BCCPLEXWDT", + "SENSOR", + "AOTAG", + "VFSENSOR", + "SWREST", + "SC7", + "HSM", + "CORESIGHT" +}; + +static const char * const tegra186_reset_levels[] = { + "L0", "L1", "L2", "WARM" +}; + +static const char * const tegra30_reset_sources[] = { + "POWER_ON_RESET", + "WATCHDOG", + "SENSOR", + "SW_MAIN", + "LP0", + "AOTAG" }; /** @@ -230,6 +322,9 @@ struct tegra_pmc { struct mutex powergates_lock; struct pinctrl_dev *pctl_dev; + + struct irq_domain *domain; + struct irq_chip irq; }; static struct tegra_pmc *pmc = &(struct tegra_pmc) { @@ -538,16 +633,10 @@ EXPORT_SYMBOL(tegra_powergate_power_off); */ int tegra_powergate_is_powered(unsigned int id) { - int status; - if (!tegra_powergate_is_valid(id)) return -EINVAL; - mutex_lock(&pmc->powergates_lock); - status = tegra_powergate_state(id); - mutex_unlock(&pmc->powergates_lock); - - return status; + return tegra_powergate_state(id); } /** @@ -715,17 +804,7 @@ static int powergate_show(struct seq_file *s, void *data) return 0; } -static int powergate_open(struct inode *inode, struct file *file) -{ - return single_open(file, powergate_show, inode->i_private); -} - -static const struct file_operations powergate_fops = { - .open = powergate_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, -}; +DEFINE_SHOW_ATTRIBUTE(powergate); static int tegra_powergate_debugfs_init(void) { @@ -845,22 +924,6 @@ static void tegra_powergate_add(struct tegra_pmc *pmc, struct device_node *np) goto remove_resets; } - /* - * FIXME: If XHCI is enabled for Tegra, then power-up the XUSB - * host and super-speed partitions. Once the XHCI driver - * manages the partitions itself this code can be removed. Note - * that we don't register these partitions with the genpd core - * to avoid it from powering down the partitions as they appear - * to be unused. - */ - if (IS_ENABLED(CONFIG_USB_XHCI_TEGRA) && - (id == TEGRA_POWERGATE_XUSBA || id == TEGRA_POWERGATE_XUSBC)) { - if (off) - WARN_ON(tegra_powergate_power_up(pg, true)); - - goto remove_resets; - } - err = pm_genpd_init(&pg->genpd, NULL, off); if (err < 0) { pr_err("failed to initialise PM domain %pOFn: %d\n", np, @@ -1541,6 +1604,225 @@ static int tegra_pmc_pinctrl_init(struct tegra_pmc *pmc) return err; } +static ssize_t reset_reason_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + u32 value, rst_src; + + value = tegra_pmc_readl(pmc->soc->regs->rst_status); + rst_src = (value & pmc->soc->regs->rst_source_mask) >> + pmc->soc->regs->rst_source_shift; + + return sprintf(buf, "%s\n", pmc->soc->reset_sources[rst_src]); +} + +static DEVICE_ATTR_RO(reset_reason); + +static ssize_t reset_level_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + u32 value, rst_lvl; + + value = tegra_pmc_readl(pmc->soc->regs->rst_status); + rst_lvl = (value & pmc->soc->regs->rst_level_mask) >> + pmc->soc->regs->rst_level_shift; + + return sprintf(buf, "%s\n", pmc->soc->reset_levels[rst_lvl]); +} + +static DEVICE_ATTR_RO(reset_level); + +static void tegra_pmc_reset_sysfs_init(struct tegra_pmc *pmc) +{ + struct device *dev = pmc->dev; + int err = 0; + + if (pmc->soc->reset_sources) { + err = device_create_file(dev, &dev_attr_reset_reason); + if (err < 0) + dev_warn(dev, + "failed to create attr \"reset_reason\": %d\n", + err); + } + + if (pmc->soc->reset_levels) { + err = device_create_file(dev, &dev_attr_reset_level); + if (err < 0) + dev_warn(dev, + "failed to create attr \"reset_level\": %d\n", + err); + } +} + +static int tegra_pmc_irq_translate(struct irq_domain *domain, + struct irq_fwspec *fwspec, + unsigned long *hwirq, + unsigned int *type) +{ + if (WARN_ON(fwspec->param_count < 2)) + return -EINVAL; + + *hwirq = fwspec->param[0]; + *type = fwspec->param[1]; + + return 0; +} + +static int tegra_pmc_irq_alloc(struct irq_domain *domain, unsigned int virq, + unsigned int num_irqs, void *data) +{ + struct tegra_pmc *pmc = domain->host_data; + const struct tegra_pmc_soc *soc = pmc->soc; + struct irq_fwspec *fwspec = data; + unsigned int i; + int err = 0; + + for (i = 0; i < soc->num_wake_events; i++) { + const struct tegra_wake_event *event = &soc->wake_events[i]; + + if (fwspec->param_count == 2) { + struct irq_fwspec spec; + + if (event->id != fwspec->param[0]) + continue; + + err = irq_domain_set_hwirq_and_chip(domain, virq, + event->id, + &pmc->irq, pmc); + if (err < 0) + break; + + spec.fwnode = &pmc->dev->of_node->fwnode; + spec.param_count = 3; + spec.param[0] = GIC_SPI; + spec.param[1] = event->irq; + spec.param[2] = fwspec->param[1]; + + err = irq_domain_alloc_irqs_parent(domain, virq, + num_irqs, &spec); + + break; + } + + if (fwspec->param_count == 3) { + if (event->gpio.instance != fwspec->param[0] || + event->gpio.pin != fwspec->param[1]) + continue; + + err = irq_domain_set_hwirq_and_chip(domain, virq, + event->id, + &pmc->irq, pmc); + + break; + } + } + + if (i == soc->num_wake_events) + err = irq_domain_set_hwirq_and_chip(domain, virq, ULONG_MAX, + &pmc->irq, pmc); + + return err; +} + +static const struct irq_domain_ops tegra_pmc_irq_domain_ops = { + .translate = tegra_pmc_irq_translate, + .alloc = tegra_pmc_irq_alloc, +}; + +static int tegra_pmc_irq_set_wake(struct irq_data *data, unsigned int on) +{ + struct tegra_pmc *pmc = irq_data_get_irq_chip_data(data); + unsigned int offset, bit; + u32 value; + + offset = data->hwirq / 32; + bit = data->hwirq % 32; + + /* clear wake status */ + writel(0x1, pmc->wake + WAKE_AOWAKE_STATUS_W(data->hwirq)); + + /* route wake to tier 2 */ + value = readl(pmc->wake + WAKE_AOWAKE_TIER2_ROUTING(offset)); + + if (!on) + value &= ~(1 << bit); + else + value |= 1 << bit; + + writel(value, pmc->wake + WAKE_AOWAKE_TIER2_ROUTING(offset)); + + /* enable wakeup event */ + writel(!!on, pmc->wake + WAKE_AOWAKE_MASK_W(data->hwirq)); + + return 0; +} + +static int tegra_pmc_irq_set_type(struct irq_data *data, unsigned int type) +{ + struct tegra_pmc *pmc = irq_data_get_irq_chip_data(data); + u32 value; + + if (data->hwirq == ULONG_MAX) + return 0; + + value = readl(pmc->wake + WAKE_AOWAKE_CNTRL(data->hwirq)); + + switch (type) { + case IRQ_TYPE_EDGE_RISING: + case IRQ_TYPE_LEVEL_HIGH: + value |= WAKE_AOWAKE_CNTRL_LEVEL; + break; + + case IRQ_TYPE_EDGE_FALLING: + case IRQ_TYPE_LEVEL_LOW: + value &= ~WAKE_AOWAKE_CNTRL_LEVEL; + break; + + case IRQ_TYPE_EDGE_RISING | IRQ_TYPE_EDGE_FALLING: + value ^= WAKE_AOWAKE_CNTRL_LEVEL; + break; + + default: + return -EINVAL; + } + + writel(value, pmc->wake + WAKE_AOWAKE_CNTRL(data->hwirq)); + + return 0; +} + +static int tegra_pmc_irq_init(struct tegra_pmc *pmc) +{ + struct irq_domain *parent = NULL; + struct device_node *np; + + np = of_irq_find_parent(pmc->dev->of_node); + if (np) { + parent = irq_find_host(np); + of_node_put(np); + } + + if (!parent) + return 0; + + pmc->irq.name = dev_name(pmc->dev); + pmc->irq.irq_mask = irq_chip_mask_parent; + pmc->irq.irq_unmask = irq_chip_unmask_parent; + pmc->irq.irq_eoi = irq_chip_eoi_parent; + pmc->irq.irq_set_affinity = irq_chip_set_affinity_parent; + pmc->irq.irq_set_type = tegra_pmc_irq_set_type; + pmc->irq.irq_set_wake = tegra_pmc_irq_set_wake; + + pmc->domain = irq_domain_add_hierarchy(parent, 0, 96, pmc->dev->of_node, + &tegra_pmc_irq_domain_ops, pmc); + if (!pmc->domain) { + dev_err(pmc->dev, "failed to allocate domain\n"); + return -ENOMEM; + } + + return 0; +} + static int tegra_pmc_probe(struct platform_device *pdev) { void __iomem *base; @@ -1610,6 +1892,8 @@ static int tegra_pmc_probe(struct platform_device *pdev) tegra_pmc_init_tsense_reset(pmc); + tegra_pmc_reset_sysfs_init(pmc); + if (IS_ENABLED(CONFIG_DEBUG_FS)) { err = tegra_powergate_debugfs_init(); if (err < 0) @@ -1627,6 +1911,10 @@ static int tegra_pmc_probe(struct platform_device *pdev) if (err) goto cleanup_restart_handler; + err = tegra_pmc_irq_init(pmc); + if (err < 0) + goto cleanup_restart_handler; + mutex_lock(&pmc->powergates_lock); iounmap(pmc->base); pmc->base = base; @@ -1676,6 +1964,11 @@ static const struct tegra_pmc_regs tegra20_pmc_regs = { .dpd_status = 0x1bc, .dpd2_req = 0x1c0, .dpd2_status = 0x1c4, + .rst_status = 0x1b4, + .rst_source_shift = 0x0, + .rst_source_mask = 0x7, + .rst_level_shift = 0x0, + .rst_level_mask = 0x0, }; static void tegra20_pmc_init(struct tegra_pmc *pmc) @@ -1733,6 +2026,10 @@ static const struct tegra_pmc_soc tegra20_pmc_soc = { .regs = &tegra20_pmc_regs, .init = tegra20_pmc_init, .setup_irq_polarity = tegra20_pmc_setup_irq_polarity, + .reset_sources = NULL, + .num_reset_sources = 0, + .reset_levels = NULL, + .num_reset_levels = 0, }; static const char * const tegra30_powergates[] = { @@ -1774,6 +2071,10 @@ static const struct tegra_pmc_soc tegra30_pmc_soc = { .regs = &tegra20_pmc_regs, .init = tegra20_pmc_init, .setup_irq_polarity = tegra20_pmc_setup_irq_polarity, + .reset_sources = tegra30_reset_sources, + .num_reset_sources = 5, + .reset_levels = NULL, + .num_reset_levels = 0, }; static const char * const tegra114_powergates[] = { @@ -1819,6 +2120,10 @@ static const struct tegra_pmc_soc tegra114_pmc_soc = { .regs = &tegra20_pmc_regs, .init = tegra20_pmc_init, .setup_irq_polarity = tegra20_pmc_setup_irq_polarity, + .reset_sources = tegra30_reset_sources, + .num_reset_sources = 5, + .reset_levels = NULL, + .num_reset_levels = 0, }; static const char * const tegra124_powergates[] = { @@ -1924,6 +2229,10 @@ static const struct tegra_pmc_soc tegra124_pmc_soc = { .regs = &tegra20_pmc_regs, .init = tegra20_pmc_init, .setup_irq_polarity = tegra20_pmc_setup_irq_polarity, + .reset_sources = tegra30_reset_sources, + .num_reset_sources = 5, + .reset_levels = NULL, + .num_reset_levels = 0, }; static const char * const tegra210_powergates[] = { @@ -2025,6 +2334,10 @@ static const struct tegra_pmc_soc tegra210_pmc_soc = { .regs = &tegra20_pmc_regs, .init = tegra20_pmc_init, .setup_irq_polarity = tegra20_pmc_setup_irq_polarity, + .reset_sources = tegra30_reset_sources, + .num_reset_sources = 5, + .reset_levels = NULL, + .num_reset_levels = 0, }; #define TEGRA186_IO_PAD_TABLE(_pad) \ @@ -2082,6 +2395,11 @@ static const struct tegra_pmc_regs tegra186_pmc_regs = { .dpd_status = 0x78, .dpd2_req = 0x7c, .dpd2_status = 0x80, + .rst_status = 0x70, + .rst_source_shift = 0x2, + .rst_source_mask = 0x3C, + .rst_level_shift = 0x0, + .rst_level_mask = 0x3, }; static void tegra186_pmc_setup_irq_polarity(struct tegra_pmc *pmc, @@ -2119,6 +2437,11 @@ static void tegra186_pmc_setup_irq_polarity(struct tegra_pmc *pmc, iounmap(wake); } +static const struct tegra_wake_event tegra186_wake_events[] = { + TEGRA_WAKE_GPIO("power", 29, 1, TEGRA_AON_GPIO(FF, 0)), + TEGRA_WAKE_IRQ("rtc", 73, 10), +}; + static const struct tegra_pmc_soc tegra186_pmc_soc = { .num_powergates = 0, .powergates = NULL, @@ -2134,10 +2457,87 @@ static const struct tegra_pmc_soc tegra186_pmc_soc = { .regs = &tegra186_pmc_regs, .init = NULL, .setup_irq_polarity = tegra186_pmc_setup_irq_polarity, + .reset_sources = tegra186_reset_sources, + .num_reset_sources = 14, + .reset_levels = tegra186_reset_levels, + .num_reset_levels = 3, + .num_wake_events = ARRAY_SIZE(tegra186_wake_events), + .wake_events = tegra186_wake_events, +}; + +static const struct tegra_io_pad_soc tegra194_io_pads[] = { + { .id = TEGRA_IO_PAD_CSIA, .dpd = 0, .voltage = UINT_MAX }, + { .id = TEGRA_IO_PAD_CSIB, .dpd = 1, .voltage = UINT_MAX }, + { .id = TEGRA_IO_PAD_MIPI_BIAS, .dpd = 3, .voltage = UINT_MAX }, + { .id = TEGRA_IO_PAD_PEX_CLK_BIAS, .dpd = 4, .voltage = UINT_MAX }, + { .id = TEGRA_IO_PAD_PEX_CLK3, .dpd = 5, .voltage = UINT_MAX }, + { .id = TEGRA_IO_PAD_PEX_CLK2, .dpd = 6, .voltage = UINT_MAX }, + { .id = TEGRA_IO_PAD_PEX_CLK1, .dpd = 7, .voltage = UINT_MAX }, + { .id = TEGRA_IO_PAD_EQOS, .dpd = 8, .voltage = UINT_MAX }, + { .id = TEGRA_IO_PAD_PEX_CLK2_BIAS, .dpd = 9, .voltage = UINT_MAX }, + { .id = TEGRA_IO_PAD_PEX_CLK2, .dpd = 10, .voltage = UINT_MAX }, + { .id = TEGRA_IO_PAD_DAP3, .dpd = 11, .voltage = UINT_MAX }, + { .id = TEGRA_IO_PAD_DAP5, .dpd = 12, .voltage = UINT_MAX }, + { .id = TEGRA_IO_PAD_UART, .dpd = 14, .voltage = UINT_MAX }, + { .id = TEGRA_IO_PAD_PWR_CTL, .dpd = 15, .voltage = UINT_MAX }, + { .id = TEGRA_IO_PAD_SOC_GPIO53, .dpd = 16, .voltage = UINT_MAX }, + { .id = TEGRA_IO_PAD_AUDIO, .dpd = 17, .voltage = UINT_MAX }, + { .id = TEGRA_IO_PAD_GP_PWM2, .dpd = 18, .voltage = UINT_MAX }, + { .id = TEGRA_IO_PAD_GP_PWM3, .dpd = 19, .voltage = UINT_MAX }, + { .id = TEGRA_IO_PAD_SOC_GPIO12, .dpd = 20, .voltage = UINT_MAX }, + { .id = TEGRA_IO_PAD_SOC_GPIO13, .dpd = 21, .voltage = UINT_MAX }, + { .id = TEGRA_IO_PAD_SOC_GPIO10, .dpd = 22, .voltage = UINT_MAX }, + { .id = TEGRA_IO_PAD_UART4, .dpd = 23, .voltage = UINT_MAX }, + { .id = TEGRA_IO_PAD_UART5, .dpd = 24, .voltage = UINT_MAX }, + { .id = TEGRA_IO_PAD_DBG, .dpd = 25, .voltage = UINT_MAX }, + { .id = TEGRA_IO_PAD_HDMI_DP3, .dpd = 26, .voltage = UINT_MAX }, + { .id = TEGRA_IO_PAD_HDMI_DP2, .dpd = 27, .voltage = UINT_MAX }, + { .id = TEGRA_IO_PAD_HDMI_DP0, .dpd = 28, .voltage = UINT_MAX }, + { .id = TEGRA_IO_PAD_HDMI_DP1, .dpd = 29, .voltage = UINT_MAX }, + { .id = TEGRA_IO_PAD_PEX_CNTRL, .dpd = 32, .voltage = UINT_MAX }, + { .id = TEGRA_IO_PAD_PEX_CTL2, .dpd = 33, .voltage = UINT_MAX }, + { .id = TEGRA_IO_PAD_PEX_L0_RST_N, .dpd = 34, .voltage = UINT_MAX }, + { .id = TEGRA_IO_PAD_PEX_L1_RST_N, .dpd = 35, .voltage = UINT_MAX }, + { .id = TEGRA_IO_PAD_SDMMC4, .dpd = 36, .voltage = UINT_MAX }, + { .id = TEGRA_IO_PAD_PEX_L5_RST_N, .dpd = 37, .voltage = UINT_MAX }, + { .id = TEGRA_IO_PAD_CSIC, .dpd = 43, .voltage = UINT_MAX }, + { .id = TEGRA_IO_PAD_CSID, .dpd = 44, .voltage = UINT_MAX }, + { .id = TEGRA_IO_PAD_CSIE, .dpd = 45, .voltage = UINT_MAX }, + { .id = TEGRA_IO_PAD_CSIF, .dpd = 46, .voltage = UINT_MAX }, + { .id = TEGRA_IO_PAD_SPI, .dpd = 47, .voltage = UINT_MAX }, + { .id = TEGRA_IO_PAD_UFS, .dpd = 49, .voltage = UINT_MAX }, + { .id = TEGRA_IO_PAD_CSIG, .dpd = 50, .voltage = UINT_MAX }, + { .id = TEGRA_IO_PAD_CSIH, .dpd = 51, .voltage = UINT_MAX }, + { .id = TEGRA_IO_PAD_EDP, .dpd = 53, .voltage = UINT_MAX }, + { .id = TEGRA_IO_PAD_SDMMC1_HV, .dpd = 55, .voltage = UINT_MAX }, + { .id = TEGRA_IO_PAD_SDMMC3_HV, .dpd = 56, .voltage = UINT_MAX }, + { .id = TEGRA_IO_PAD_CONN, .dpd = 60, .voltage = UINT_MAX }, + { .id = TEGRA_IO_PAD_AUDIO_HV, .dpd = 61, .voltage = UINT_MAX }, +}; + +static const struct tegra_wake_event tegra194_wake_events[] = { + TEGRA_WAKE_GPIO("power", 29, 1, TEGRA194_AON_GPIO(EE, 4)), + TEGRA_WAKE_IRQ("rtc", 73, 10), +}; + +static const struct tegra_pmc_soc tegra194_pmc_soc = { + .num_powergates = 0, + .powergates = NULL, + .num_cpu_powergates = 0, + .cpu_powergates = NULL, + .has_tsense_reset = false, + .has_gpu_clamps = false, + .num_io_pads = ARRAY_SIZE(tegra194_io_pads), + .io_pads = tegra194_io_pads, + .regs = &tegra186_pmc_regs, + .init = NULL, + .setup_irq_polarity = tegra186_pmc_setup_irq_polarity, + .num_wake_events = ARRAY_SIZE(tegra194_wake_events), + .wake_events = tegra194_wake_events, }; static const struct of_device_id tegra_pmc_match[] = { - { .compatible = "nvidia,tegra194-pmc", .data = &tegra186_pmc_soc }, + { .compatible = "nvidia,tegra194-pmc", .data = &tegra194_pmc_soc }, { .compatible = "nvidia,tegra186-pmc", .data = &tegra186_pmc_soc }, { .compatible = "nvidia,tegra210-pmc", .data = &tegra210_pmc_soc }, { .compatible = "nvidia,tegra132-pmc", .data = &tegra124_pmc_soc }, diff --git a/drivers/soc/ti/wkup_m3_ipc.c b/drivers/soc/ti/wkup_m3_ipc.c @@ -57,6 +57,7 @@ static struct wkup_m3_ipc *m3_ipc_state; static const struct wkup_m3_wakeup_src wakeups[] = { + {.irq_nr = 16, .src = "PRCM"}, {.irq_nr = 35, .src = "USB0_PHY"}, {.irq_nr = 36, .src = "USB1_PHY"}, {.irq_nr = 40, .src = "I2C0"}, diff --git a/include/dt-bindings/power/imx8mq-power.h b/include/dt-bindings/power/imx8mq-power.h @@ -0,0 +1,21 @@ +/* SPDX-License-Identifier: (GPL-2.0 OR MIT) */ +/* + * Copyright (C) 2018 Pengutronix, Lucas Stach <kernel@pengutronix.de> + */ + +#ifndef __DT_BINDINGS_IMX8MQ_POWER_H__ +#define __DT_BINDINGS_IMX8MQ_POWER_H__ + +#define IMX8M_POWER_DOMAIN_MIPI 0 +#define IMX8M_POWER_DOMAIN_PCIE1 1 +#define IMX8M_POWER_DOMAIN_USB_OTG1 2 +#define IMX8M_POWER_DOMAIN_USB_OTG2 3 +#define IMX8M_POWER_DOMAIN_DDR1 4 +#define IMX8M_POWER_DOMAIN_GPU 5 +#define IMX8M_POWER_DOMAIN_VPU 6 +#define IMX8M_POWER_DOMAIN_DISP 7 +#define IMX8M_POWER_DOMAIN_MIPI_CSI1 8 +#define IMX8M_POWER_DOMAIN_MIPI_CSI2 9 +#define IMX8M_POWER_DOMAIN_PCIE2 10 + +#endif diff --git a/include/dt-bindings/power/r8a77970-sysc.h b/include/dt-bindings/power/r8a77970-sysc.h @@ -16,13 +16,12 @@ #define R8A77970_PD_CA53_CPU0 5 #define R8A77970_PD_CA53_CPU1 6 -#define R8A77970_PD_CR7 13 #define R8A77970_PD_CA53_SCU 21 #define R8A77970_PD_A2IR0 23 -#define R8A77970_PD_A3IR 24 +#define R8A77970_PD_A3IR 24 #define R8A77970_PD_A2IR1 27 -#define R8A77970_PD_A2IR2 28 -#define R8A77970_PD_A2IR3 29 +#define R8A77970_PD_A2DP 28 +#define R8A77970_PD_A2CN 29 #define R8A77970_PD_A2SC0 30 #define R8A77970_PD_A2SC1 31 diff --git a/include/dt-bindings/power/r8a77980-sysc.h b/include/dt-bindings/power/r8a77980-sysc.h @@ -15,14 +15,14 @@ #define R8A77980_PD_A2SC2 0 #define R8A77980_PD_A2SC3 1 #define R8A77980_PD_A2SC4 2 -#define R8A77980_PD_A2PD0 3 -#define R8A77980_PD_A2PD1 4 +#define R8A77980_PD_A2DP0 3 +#define R8A77980_PD_A2DP1 4 #define R8A77980_PD_CA53_CPU0 5 #define R8A77980_PD_CA53_CPU1 6 #define R8A77980_PD_CA53_CPU2 7 #define R8A77980_PD_CA53_CPU3 8 #define R8A77980_PD_A2CN 10 -#define R8A77980_PD_A3VIP 11 +#define R8A77980_PD_A3VIP0 11 #define R8A77980_PD_A2IR5 12 #define R8A77980_PD_CR7 13 #define R8A77980_PD_A2IR4 15 diff --git a/include/dt-bindings/power/raspberrypi-power.h b/include/dt-bindings/power/raspberrypi-power.h @@ -1,9 +1,6 @@ +/* SPDX-License-Identifier: GPL-2.0 */ /* * 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. */ #ifndef _DT_BINDINGS_ARM_BCM2835_RPI_POWER_H diff --git a/include/dt-bindings/power/rk3066-power.h b/include/dt-bindings/power/rk3066-power.h @@ -0,0 +1,22 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef __DT_BINDINGS_POWER_RK3066_POWER_H__ +#define __DT_BINDINGS_POWER_RK3066_POWER_H__ + +/* VD_CORE */ +#define RK3066_PD_A9_0 0 +#define RK3066_PD_A9_1 1 +#define RK3066_PD_DBG 4 +#define RK3066_PD_SCU 5 + +/* VD_LOGIC */ +#define RK3066_PD_VIDEO 6 +#define RK3066_PD_VIO 7 +#define RK3066_PD_GPU 8 +#define RK3066_PD_PERI 9 +#define RK3066_PD_CPU 10 +#define RK3066_PD_ALIVE 11 + +/* VD_PMU */ +#define RK3066_PD_RTC 12 + +#endif diff --git a/include/dt-bindings/power/rk3188-power.h b/include/dt-bindings/power/rk3188-power.h @@ -0,0 +1,24 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef __DT_BINDINGS_POWER_RK3188_POWER_H__ +#define __DT_BINDINGS_POWER_RK3188_POWER_H__ + +/* VD_CORE */ +#define RK3188_PD_A9_0 0 +#define RK3188_PD_A9_1 1 +#define RK3188_PD_A9_2 2 +#define RK3188_PD_A9_3 3 +#define RK3188_PD_DBG 4 +#define RK3188_PD_SCU 5 + +/* VD_LOGIC */ +#define RK3188_PD_VIDEO 6 +#define RK3188_PD_VIO 7 +#define RK3188_PD_GPU 8 +#define RK3188_PD_PERI 9 +#define RK3188_PD_CPU 10 +#define RK3188_PD_ALIVE 11 + +/* VD_PMU */ +#define RK3188_PD_RTC 12 + +#endif diff --git a/include/linux/dma/pxa-dma.h b/include/linux/dma/pxa-dma.h @@ -23,15 +23,4 @@ struct pxad_param { enum pxad_chan_prio prio; }; -struct dma_chan; - -#ifdef CONFIG_PXA_DMA -bool pxad_filter_fn(struct dma_chan *chan, void *param); -#else -static inline bool pxad_filter_fn(struct dma_chan *chan, void *param) -{ - return false; -} -#endif - #endif /* _PXA_DMA_H_ */ diff --git a/include/linux/soc/mediatek/mtk-cmdq.h b/include/linux/soc/mediatek/mtk-cmdq.h @@ -0,0 +1,133 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2018 MediaTek Inc. + * + */ + +#ifndef __MTK_CMDQ_H__ +#define __MTK_CMDQ_H__ + +#include <linux/mailbox_client.h> +#include <linux/mailbox/mtk-cmdq-mailbox.h> +#include <linux/timer.h> + +#define CMDQ_NO_TIMEOUT 0xffffffffu + +/** cmdq event maximum */ +#define CMDQ_MAX_EVENT 0x3ff + +struct cmdq_pkt; + +struct cmdq_client { + spinlock_t lock; + u32 pkt_cnt; + struct mbox_client client; + struct mbox_chan *chan; + struct timer_list timer; + u32 timeout_ms; /* in unit of microsecond */ +}; + +/** + * cmdq_mbox_create() - create CMDQ mailbox client and channel + * @dev: device of CMDQ mailbox client + * @index: index of CMDQ mailbox channel + * @timeout: timeout of a pkt execution by GCE, in unit of microsecond, set + * CMDQ_NO_TIMEOUT if a timer is not used. + * + * Return: CMDQ mailbox client pointer + */ +struct cmdq_client *cmdq_mbox_create(struct device *dev, int index, + u32 timeout); + +/** + * cmdq_mbox_destroy() - destroy CMDQ mailbox client and channel + * @client: the CMDQ mailbox client + */ +void cmdq_mbox_destroy(struct cmdq_client *client); + +/** + * cmdq_pkt_create() - create a CMDQ packet + * @client: the CMDQ mailbox client + * @size: required CMDQ buffer size + * + * Return: CMDQ packet pointer + */ +struct cmdq_pkt *cmdq_pkt_create(struct cmdq_client *client, size_t size); + +/** + * cmdq_pkt_destroy() - destroy the CMDQ packet + * @pkt: the CMDQ packet + */ +void cmdq_pkt_destroy(struct cmdq_pkt *pkt); + +/** + * cmdq_pkt_write() - append write command to the CMDQ packet + * @pkt: the CMDQ packet + * @value: the specified target register value + * @subsys: the CMDQ sub system code + * @offset: register offset from CMDQ sub system + * + * Return: 0 for success; else the error code is returned + */ +int cmdq_pkt_write(struct cmdq_pkt *pkt, u32 value, u32 subsys, u32 offset); + +/** + * cmdq_pkt_write_mask() - append write command with mask to the CMDQ packet + * @pkt: the CMDQ packet + * @value: the specified target register value + * @subsys: the CMDQ sub system code + * @offset: register offset from CMDQ sub system + * @mask: the specified target register mask + * + * Return: 0 for success; else the error code is returned + */ +int cmdq_pkt_write_mask(struct cmdq_pkt *pkt, u32 value, + u32 subsys, u32 offset, u32 mask); + +/** + * cmdq_pkt_wfe() - append wait for event command to the CMDQ packet + * @pkt: the CMDQ packet + * @event: the desired event type to "wait and CLEAR" + * + * Return: 0 for success; else the error code is returned + */ +int cmdq_pkt_wfe(struct cmdq_pkt *pkt, u32 event); + +/** + * cmdq_pkt_clear_event() - append clear event command to the CMDQ packet + * @pkt: the CMDQ packet + * @event: the desired event to be cleared + * + * Return: 0 for success; else the error code is returned + */ +int cmdq_pkt_clear_event(struct cmdq_pkt *pkt, u32 event); + +/** + * cmdq_pkt_flush_async() - trigger CMDQ to asynchronously execute the CMDQ + * packet and call back at the end of done packet + * @pkt: the CMDQ packet + * @cb: called at the end of done packet + * @data: this data will pass back to cb + * + * Return: 0 for success; else the error code is returned + * + * Trigger CMDQ to asynchronously execute the CMDQ packet and call back + * at the end of done packet. Note that this is an ASYNC function. When the + * function returned, it may or may not be finished. + */ +int cmdq_pkt_flush_async(struct cmdq_pkt *pkt, cmdq_async_flush_cb cb, + void *data); + +/** + * cmdq_pkt_flush() - trigger CMDQ to execute the CMDQ packet + * @pkt: the CMDQ packet + * + * Return: 0 for success; else the error code is returned + * + * Trigger CMDQ to execute the CMDQ packet. Note that this is a + * synchronous flush function. When the function returned, the recorded + * commands have been done. + */ +int cmdq_pkt_flush(struct cmdq_pkt *pkt); + +#endif /* __MTK_CMDQ_H__ */ diff --git a/include/linux/soc/qcom/qmi.h b/include/linux/soc/qcom/qmi.h @@ -166,7 +166,7 @@ struct qmi_ops { struct qmi_txn { struct qmi_handle *qmi; - int id; + u16 id; struct mutex lock; struct completion completion; diff --git a/include/soc/bcm2835/raspberrypi-firmware.h b/include/soc/bcm2835/raspberrypi-firmware.h @@ -1,9 +1,6 @@ +/* SPDX-License-Identifier: GPL-2.0 */ /* * 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. */ #ifndef __SOC_RASPBERRY_FIRMWARE_H__ diff --git a/include/soc/qcom/cmd-db.h b/include/soc/qcom/cmd-db.h @@ -18,9 +18,7 @@ enum cmd_db_hw_type { #if IS_ENABLED(CONFIG_QCOM_COMMAND_DB) u32 cmd_db_read_addr(const char *resource_id); -int cmd_db_read_aux_data(const char *resource_id, u8 *data, size_t len); - -size_t cmd_db_read_aux_data_len(const char *resource_id); +const void *cmd_db_read_aux_data(const char *resource_id, size_t *len); enum cmd_db_hw_type cmd_db_read_slave_id(const char *resource_id); @@ -29,12 +27,8 @@ int cmd_db_ready(void); static inline u32 cmd_db_read_addr(const char *resource_id) { return 0; } -static inline int cmd_db_read_aux_data(const char *resource_id, u8 *data, - size_t len) -{ return -ENODEV; } - -static inline size_t cmd_db_read_aux_data_len(const char *resource_id) -{ return -ENODEV; } +static inline const void *cmd_db_read_aux_data(const char *resource_id, size_t *len) +{ return ERR_PTR(-ENODEV); } static inline enum cmd_db_hw_type cmd_db_read_slave_id(const char *resource_id) { return -ENODEV; } diff --git a/include/soc/tegra/bpmp-abi.h b/include/soc/tegra/bpmp-abi.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2016, NVIDIA CORPORATION. All rights reserved. + * Copyright (c) 2014-2018, NVIDIA CORPORATION. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, @@ -40,7 +40,6 @@ * @file */ - /** * @defgroup MRQ MRQ Messages * @brief Messages sent to/from BPMP via IPC @@ -53,7 +52,7 @@ */ /** - * @addtogroup MRQ_Format Message Format + * @addtogroup MRQ_Format * @{ * The CPU requests the BPMP to perform a particular service by * sending it an IVC frame containing a single MRQ message. An MRQ @@ -76,7 +75,7 @@ /** * @ingroup MRQ_Format - * @brief header for an MRQ message + * @brief Header for an MRQ message * * Provides the MRQ number for the MRQ message: #mrq. The remainder of * the MRQ message is a payload (immediately following the @@ -86,7 +85,7 @@ struct mrq_request { /** @brief MRQ number of the request */ uint32_t mrq; /** - * @brief flags providing follow up directions to the receiver + * @brief Flags providing follow up directions to the receiver * * | Bit | Description | * |-----|--------------------------------------------| @@ -98,7 +97,7 @@ struct mrq_request { /** * @ingroup MRQ_Format - * @brief header for an MRQ response + * @brief Header for an MRQ response * * Provides an error code for the associated MRQ message. The * remainder of the MRQ response is a payload (immediately following @@ -106,9 +105,9 @@ struct mrq_request { * mrq_request::mrq */ struct mrq_response { - /** @brief error code for the MRQ request itself */ + /** @brief Error code for the MRQ request itself */ int32_t err; - /** @brief reserved for future use */ + /** @brief Reserved for future use */ uint32_t flags; } __ABI_PACKED; @@ -152,6 +151,14 @@ struct mrq_response { #define MRQ_TRACE_ITER 64 #define MRQ_RINGBUF_CONSOLE 65 #define MRQ_PG 66 +#define MRQ_CPU_NDIV_LIMITS 67 +#define MRQ_STRAP 68 +#define MRQ_UPHY 69 +#define MRQ_CPU_AUTO_CC3 70 +#define MRQ_QUERY_FW_TAG 71 +#define MRQ_FMON 72 +#define MRQ_EC 73 +#define MRQ_FBVOLT_STATUS 74 /** @} */ @@ -160,31 +167,35 @@ struct mrq_response { * @brief Maximum MRQ code to be sent by CPU software to * BPMP. Subject to change in future */ -#define MAX_CPU_MRQ_ID 66 +#define MAX_CPU_MRQ_ID 74 /** - * @addtogroup MRQ_Payloads Message Payloads + * @addtogroup MRQ_Payloads * @{ - * @defgroup Ping + * @defgroup Ping Ping * @defgroup Query_Tag Query Tag * @defgroup Module Loadable Modules - * @defgroup Trace - * @defgroup Debugfs - * @defgroup Reset - * @defgroup I2C - * @defgroup Clocks + * @defgroup Trace Trace + * @defgroup Debugfs Debug File System + * @defgroup Reset Reset + * @defgroup I2C I2C + * @defgroup Clocks Clocks * @defgroup ABI_info ABI Info - * @defgroup MC_Flush MC Flush - * @defgroup Powergating - * @defgroup Thermal + * @defgroup Powergating Power Gating + * @defgroup Thermal Thermal * @defgroup Vhint CPU Voltage hint - * @defgroup MRQ_Deprecated Deprecated MRQ messages - * @defgroup EMC - * @defgroup RingbufConsole + * @defgroup EMC EMC + * @defgroup CPU NDIV Limits + * @defgroup RingbufConsole Ring Buffer Console + * @defgroup Strap Straps + * @defgroup UPHY UPHY + * @defgroup CC3 Auto-CC3 + * @defgroup FMON FMON + * @defgroup EC EC + * @defgroup Fbvolt_status Fuse Burn Voltage Status * @} */ - /** * @ingroup MRQ_Codes * @def MRQ_PING @@ -214,20 +225,20 @@ struct mrq_response { /** * @ingroup Ping - * @brief request with #MRQ_PING + * @brief Request with #MRQ_PING * * Used by the sender of an #MRQ_PING message to request a pong from * recipient. The response from the recipient is computed based on * #challenge. */ struct mrq_ping_request { -/** @brief arbitrarily chosen value */ +/** @brief Arbitrarily chosen value */ uint32_t challenge; } __ABI_PACKED; /** * @ingroup Ping - * @brief response to #MRQ_PING + * @brief Response to #MRQ_PING * * Sent in response to an #MRQ_PING message. #reply should be the * mrq_ping_request challenge left shifted by 1 with the carry-bit @@ -235,14 +246,16 @@ struct mrq_ping_request { * */ struct mrq_ping_response { - /** @brief response to the MRQ_PING challege */ + /** @brief Response to the MRQ_PING challege */ uint32_t reply; } __ABI_PACKED; /** * @ingroup MRQ_Codes * @def MRQ_QUERY_TAG - * @brief Query BPMP firmware's tag (i.e. version information) + * @brief Query BPMP firmware's tag (i.e. unique identifer) + * + * @deprecated Use #MRQ_QUERY_FW_TAG instead. * * * Platforms: All * * Initiators: CCPLEX @@ -254,25 +267,50 @@ struct mrq_ping_response { /** * @ingroup Query_Tag - * @brief request with #MRQ_QUERY_TAG - * - * Used by #MRQ_QUERY_TAG call to ask BPMP to fill in the memory - * pointed by #addr with BPMP firmware header. + * @brief Request with #MRQ_QUERY_TAG * - * The sender is reponsible for ensuring that #addr is mapped in to - * the recipient's address map. + * @deprecated This structure will be removed in future version. + * Use MRQ_QUERY_FW_TAG instead. */ struct mrq_query_tag_request { - /** @brief base address to store the firmware header */ + /** @brief Base address to store the firmware tag */ uint32_t addr; } __ABI_PACKED; + /** * @ingroup MRQ_Codes - * @def MRQ_MODULE_LOAD - * @brief dynamically load a BPMP code module + * @def MRQ_QUERY_FW_TAG + * @brief Query BPMP firmware's tag (i.e. unique identifier) * * * Platforms: All + * * Initiators: Any + * * Targets: BPMP + * * Request Payload: N/A + * * Response Payload: @ref mrq_query_fw_tag_response + * + */ + +/** + * @ingroup Query_Tag + * @brief Response to #MRQ_QUERY_FW_TAG + * + * Sent in response to #MRQ_QUERY_FW_TAG message. #tag contains the unique + * identifier for the version of firmware issuing the reply. + * + */ +struct mrq_query_fw_tag_response { + /** @brief Array to store tag information */ + uint8_t tag[32]; +} __ABI_PACKED; + +/** + * @ingroup MRQ_Codes + * @def MRQ_MODULE_LOAD + * @brief Dynamically load a BPMP code module + * + * * Platforms: T210, T214, T186 + * @cond (bpmp_t210 || bpmp_t214 || bpmp_t186) * * Initiators: CCPLEX * * Targets: BPMP * * Request Payload: @ref mrq_module_load_request @@ -284,7 +322,7 @@ struct mrq_query_tag_request { /** * @ingroup Module - * @brief request with #MRQ_MODULE_LOAD + * @brief Request with #MRQ_MODULE_LOAD * * Used by #MRQ_MODULE_LOAD calls to ask the recipient to dynamically * load the code located at #phys_addr and having size #size @@ -300,29 +338,31 @@ struct mrq_query_tag_request { * */ struct mrq_module_load_request { - /** @brief base address of the code to load. Treated as (void *) */ + /** @brief Base address of the code to load. Treated as (void *) */ uint32_t phys_addr; /* (void *) */ - /** @brief size in bytes of code to load */ + /** @brief Size in bytes of code to load */ uint32_t size; } __ABI_PACKED; /** * @ingroup Module - * @brief response to #MRQ_MODULE_LOAD + * @brief Response to #MRQ_MODULE_LOAD * * @todo document mrq_response::err */ struct mrq_module_load_response { - /** @brief handle to the loaded module */ + /** @brief Handle to the loaded module */ uint32_t base; } __ABI_PACKED; +/** @endcond*/ /** * @ingroup MRQ_Codes * @def MRQ_MODULE_UNLOAD - * @brief unload a previously loaded code module + * @brief Unload a previously loaded code module * - * * Platforms: All + * * Platforms: T210, T214, T186 + * @cond (bpmp_t210 || bpmp_t214 || bpmp_t186) * * Initiators: CCPLEX * * Targets: BPMP * * Request Payload: @ref mrq_module_unload_request @@ -333,20 +373,21 @@ struct mrq_module_load_response { /** * @ingroup Module - * @brief request with #MRQ_MODULE_UNLOAD + * @brief Request with #MRQ_MODULE_UNLOAD * * Used by #MRQ_MODULE_UNLOAD calls to request that a previously loaded * module be unloaded. */ struct mrq_module_unload_request { - /** @brief handle of the module to unload */ + /** @brief Handle of the module to unload */ uint32_t base; } __ABI_PACKED; +/** @endcond*/ /** * @ingroup MRQ_Codes * @def MRQ_TRACE_MODIFY - * @brief modify the set of enabled trace events + * @brief Modify the set of enabled trace events * * * Platforms: All * * Initiators: CCPLEX @@ -359,22 +400,22 @@ struct mrq_module_unload_request { /** * @ingroup Trace - * @brief request with #MRQ_TRACE_MODIFY + * @brief Request with #MRQ_TRACE_MODIFY * * Used by %MRQ_TRACE_MODIFY calls to enable or disable specify trace * events. #set takes precedence for any bit set in both #set and * #clr. */ struct mrq_trace_modify_request { - /** @brief bit mask of trace events to disable */ + /** @brief Bit mask of trace events to disable */ uint32_t clr; - /** @brief bit mask of trace events to enable */ + /** @brief Bit mask of trace events to enable */ uint32_t set; } __ABI_PACKED; /** * @ingroup Trace - * @brief response to #MRQ_TRACE_MODIFY + * @brief Response to #MRQ_TRACE_MODIFY * * Sent in repsonse to an #MRQ_TRACE_MODIFY message. #mask reflects the * state of which events are enabled after the recipient acted on the @@ -382,7 +423,7 @@ struct mrq_trace_modify_request { * */ struct mrq_trace_modify_response { - /** @brief bit mask of trace event enable states */ + /** @brief Bit mask of trace event enable states */ uint32_t mask; } __ABI_PACKED; @@ -407,7 +448,7 @@ struct mrq_trace_modify_response { /** * @ingroup Trace - * @brief request with #MRQ_WRITE_TRACE + * @brief Request with #MRQ_WRITE_TRACE * * Used by MRQ_WRITE_TRACE calls to ask the recipient to copy trace * data from the recipient's local buffer to the output buffer. #area @@ -420,22 +461,22 @@ struct mrq_trace_modify_response { * overwrites. */ struct mrq_write_trace_request { - /** @brief base address of output buffer */ + /** @brief Base address of output buffer */ uint32_t area; - /** @brief size in bytes of the output buffer */ + /** @brief Size in bytes of the output buffer */ uint32_t size; } __ABI_PACKED; /** * @ingroup Trace - * @brief response to #MRQ_WRITE_TRACE + * @brief Response to #MRQ_WRITE_TRACE * * Once this response is sent, the respondent will not access the * output buffer further. */ struct mrq_write_trace_response { /** - * @brief flag whether more data remains in local buffer + * @brief Flag whether more data remains in local buffer * * Value is 1 if the entire local trace buffer has been * drained to the outputbuffer. Value is 0 otherwise. @@ -456,9 +497,10 @@ struct mrq_threaded_ping_response { /** * @ingroup MRQ_Codes * @def MRQ_MODULE_MAIL - * @brief send a message to a loadable module + * @brief Send a message to a loadable module * - * * Platforms: All + * * Platforms: T210, T214, T186 + * @cond (bpmp_t210 || bpmp_t214 || bpmp_t186) * * Initiators: Any * * Targets: BPMP * * Request Payload: @ref mrq_module_mail_request @@ -469,12 +511,12 @@ struct mrq_threaded_ping_response { /** * @ingroup Module - * @brief request with #MRQ_MODULE_MAIL + * @brief Request with #MRQ_MODULE_MAIL */ struct mrq_module_mail_request { - /** @brief handle to the previously loaded module */ + /** @brief Handle to the previously loaded module */ uint32_t base; - /** @brief module-specific mail payload + /** @brief Module-specific mail payload * * The length of data[ ] is unknown to the BPMP core firmware * but it is limited to the size of an IPC message. @@ -484,23 +526,24 @@ struct mrq_module_mail_request { /** * @ingroup Module - * @brief response to #MRQ_MODULE_MAIL + * @brief Response to #MRQ_MODULE_MAIL */ struct mrq_module_mail_response { - /** @brief module-specific mail payload + /** @brief Module-specific mail payload * * The length of data[ ] is unknown to the BPMP core firmware * but it is limited to the size of an IPC message. */ uint8_t data[EMPTY_ARRAY]; } __ABI_PACKED; +/** @endcond */ /** * @ingroup MRQ_Codes * @def MRQ_DEBUGFS * @brief Interact with BPMP's debugfs file nodes * - * * Platforms: T186 + * * Platforms: T186, T194 * * Initiators: Any * * Targets: BPMP * * Request Payload: @ref mrq_debugfs_request @@ -529,65 +572,70 @@ struct mrq_module_mail_response { * * @} */ + /** @ingroup Debugfs */ enum mrq_debugfs_commands { + /** @brief Perform read */ CMD_DEBUGFS_READ = 1, + /** @brief Perform write */ CMD_DEBUGFS_WRITE = 2, + /** @brief Perform dumping directory */ CMD_DEBUGFS_DUMPDIR = 3, + /** @brief Not a command */ CMD_DEBUGFS_MAX }; /** * @ingroup Debugfs - * @brief parameters for CMD_DEBUGFS_READ/WRITE command + * @brief Parameters for CMD_DEBUGFS_READ/WRITE command */ struct cmd_debugfs_fileop_request { - /** @brief physical address pointing at filename */ + /** @brief Physical address pointing at filename */ uint32_t fnameaddr; - /** @brief length in bytes of filename buffer */ + /** @brief Length in bytes of filename buffer */ uint32_t fnamelen; - /** @brief physical address pointing to data buffer */ + /** @brief Physical address pointing to data buffer */ uint32_t dataaddr; - /** @brief length in bytes of data buffer */ + /** @brief Length in bytes of data buffer */ uint32_t datalen; } __ABI_PACKED; /** * @ingroup Debugfs - * @brief parameters for CMD_DEBUGFS_READ/WRITE command + * @brief Parameters for CMD_DEBUGFS_READ/WRITE command */ struct cmd_debugfs_dumpdir_request { - /** @brief physical address pointing to data buffer */ + /** @brief Physical address pointing to data buffer */ uint32_t dataaddr; - /** @brief length in bytes of data buffer */ + /** @brief Length in bytes of data buffer */ uint32_t datalen; } __ABI_PACKED; /** * @ingroup Debugfs - * @brief response data for CMD_DEBUGFS_READ/WRITE command + * @brief Response data for CMD_DEBUGFS_READ/WRITE command */ struct cmd_debugfs_fileop_response { - /** @brief always 0 */ + /** @brief Always 0 */ uint32_t reserved; - /** @brief number of bytes read from or written to data buffer */ + /** @brief Number of bytes read from or written to data buffer */ uint32_t nbytes; } __ABI_PACKED; /** * @ingroup Debugfs - * @brief response data for CMD_DEBUGFS_DUMPDIR command + * @brief Response data for CMD_DEBUGFS_DUMPDIR command */ struct cmd_debugfs_dumpdir_response { - /** @brief always 0 */ + /** @brief Always 0 */ uint32_t reserved; - /** @brief number of bytes read from or written to data buffer */ + /** @brief Number of bytes read from or written to data buffer */ uint32_t nbytes; } __ABI_PACKED; /** * @ingroup Debugfs - * @brief request with #MRQ_DEBUGFS. + * @brief Request with #MRQ_DEBUGFS. * * The sender of an MRQ_DEBUGFS message uses #cmd to specify a debugfs * command to execute. Legal commands are the values of @ref @@ -601,6 +649,7 @@ struct cmd_debugfs_dumpdir_response { * |CMD_DEBUGFS_DUMPDIR|dumpdir| */ struct mrq_debugfs_request { + /** @brief Sub-command (@ref mrq_debugfs_commands) */ uint32_t cmd; union { struct cmd_debugfs_fileop_request fop; @@ -612,14 +661,14 @@ struct mrq_debugfs_request { * @ingroup Debugfs */ struct mrq_debugfs_response { - /** @brief always 0 */ + /** @brief Always 0 */ int32_t reserved; union { - /** @brief response data for CMD_DEBUGFS_READ OR + /** @brief Response data for CMD_DEBUGFS_READ OR * CMD_DEBUGFS_WRITE command */ struct cmd_debugfs_fileop_response fop; - /** @brief response data for CMD_DEBUGFS_DUMPDIR command */ + /** @brief Response data for CMD_DEBUGFS_DUMPDIR command */ struct cmd_debugfs_dumpdir_response dumpdir; } __UNION_ANON; } __ABI_PACKED; @@ -633,57 +682,58 @@ struct mrq_debugfs_response { #define DEBUGFS_S_IWUSR (1 << 7) /** @} */ - /** * @ingroup MRQ_Codes * @def MRQ_RESET - * @brief reset an IP block + * @brief Reset an IP block * - * * Platforms: T186 + * * Platforms: T186, T194 * * Initiators: Any * * Targets: BPMP * * Request Payload: @ref mrq_reset_request * * Response Payload: @ref mrq_reset_response + * + * @addtogroup Reset + * @{ */ -/** - * @ingroup Reset - */ enum mrq_reset_commands { + /** @brief Assert module reset */ CMD_RESET_ASSERT = 1, + /** @brief Deassert module reset */ CMD_RESET_DEASSERT = 2, + /** @brief Assert and deassert the module reset */ CMD_RESET_MODULE = 3, + /** @brief Get the highest reset ID */ CMD_RESET_GET_MAX_ID = 4, - CMD_RESET_MAX, /* not part of ABI and subject to change */ + /** @brief Not part of ABI and subject to change */ + CMD_RESET_MAX, }; /** - * @ingroup Reset - * @brief request with MRQ_RESET + * @brief Request with MRQ_RESET * * Used by the sender of an #MRQ_RESET message to request BPMP to * assert or or deassert a given reset line. */ struct mrq_reset_request { - /** @brief reset action to perform (@enum mrq_reset_commands) */ + /** @brief Reset action to perform (@ref mrq_reset_commands) */ uint32_t cmd; - /** @brief id of the reset to affected */ + /** @brief Id of the reset to affected */ uint32_t reset_id; } __ABI_PACKED; /** - * @ingroup Reset * @brief Response for MRQ_RESET sub-command CMD_RESET_GET_MAX_ID. When * this sub-command is not supported, firmware will return -BPMP_EBADCMD * in mrq_response::err. */ struct cmd_reset_get_max_id_response { - /** @brief max reset id */ + /** @brief Max reset id */ uint32_t max_id; } __ABI_PACKED; /** - * @ingroup Reset * @brief Response with MRQ_RESET * * Each sub-command supported by @ref mrq_reset_request may return @@ -703,32 +753,25 @@ struct mrq_reset_response { } __UNION_ANON; } __ABI_PACKED; +/** @} */ + /** * @ingroup MRQ_Codes * @def MRQ_I2C - * @brief issue an i2c transaction + * @brief Issue an i2c transaction * - * * Platforms: T186 + * * Platforms: T186, T194 * * Initiators: Any * * Targets: BPMP * * Request Payload: @ref mrq_i2c_request * * Response Payload: @ref mrq_i2c_response - */ - -/** + * * @addtogroup I2C * @{ */ #define TEGRA_I2C_IPC_MAX_IN_BUF_SIZE (MSG_DATA_MIN_SZ - 12) #define TEGRA_I2C_IPC_MAX_OUT_BUF_SIZE (MSG_DATA_MIN_SZ - 4) -/** @} */ -/** - * @ingroup I2C - * @name Serial I2C flags - * Use these flags with serial_i2c_request::flags - * @{ - */ #define SERIALI2C_TEN 0x0010 #define SERIALI2C_RD 0x0001 #define SERIALI2C_STOP 0x8000 @@ -737,15 +780,13 @@ struct mrq_reset_response { #define SERIALI2C_IGNORE_NAK 0x1000 #define SERIALI2C_NO_RD_ACK 0x0800 #define SERIALI2C_RECV_LEN 0x0400 -/** @} */ -/** @ingroup I2C */ + enum { CMD_I2C_XFER = 1 }; /** - * @ingroup I2C - * @brief serializable i2c request + * @brief Serializable i2c request * * Instances of this structure are packed (little-endian) into * cmd_i2c_xfer_request::data_buf. Each instance represents a single @@ -762,80 +803,75 @@ enum { struct serial_i2c_request { /** @brief I2C slave address */ uint16_t addr; - /** @brief bitmask of SERIALI2C_ flags */ + /** @brief Bitmask of SERIALI2C_ flags */ uint16_t flags; - /** @brief length of I2C transaction in bytes */ + /** @brief Length of I2C transaction in bytes */ uint16_t len; - /** @brief for write transactions only, #len bytes of data */ + /** @brief For write transactions only, #len bytes of data */ uint8_t data[]; } __ABI_PACKED; /** - * @ingroup I2C - * @brief trigger one or more i2c transactions + * @brief Trigger one or more i2c transactions */ struct cmd_i2c_xfer_request { - /** @brief valid bus number from mach-t186/i2c-t186.h*/ + /** @brief Valid bus number from @ref bpmp_i2c_ids*/ uint32_t bus_id; - /** @brief count of valid bytes in #data_buf*/ + /** @brief Count of valid bytes in #data_buf*/ uint32_t data_size; - /** @brief serialized packed instances of @ref serial_i2c_request*/ + /** @brief Serialized packed instances of @ref serial_i2c_request*/ uint8_t data_buf[TEGRA_I2C_IPC_MAX_IN_BUF_SIZE]; } __ABI_PACKED; /** - * @ingroup I2C - * @brief container for data read from the i2c bus + * @brief Container for data read from the i2c bus * * Processing an cmd_i2c_xfer_request::data_buf causes BPMP to execute * zero or more I2C reads. The data read from the bus is serialized * into #data_buf. */ struct cmd_i2c_xfer_response { - /** @brief count of valid bytes in #data_buf*/ + /** @brief Count of valid bytes in #data_buf*/ uint32_t data_size; - /** @brief i2c read data */ + /** @brief I2c read data */ uint8_t data_buf[TEGRA_I2C_IPC_MAX_OUT_BUF_SIZE]; } __ABI_PACKED; /** - * @ingroup I2C - * @brief request with #MRQ_I2C + * @brief Request with #MRQ_I2C */ struct mrq_i2c_request { - /** @brief always CMD_I2C_XFER (i.e. 1) */ + /** @brief Always CMD_I2C_XFER (i.e. 1) */ uint32_t cmd; - /** @brief parameters of the transfer request */ + /** @brief Parameters of the transfer request */ struct cmd_i2c_xfer_request xfer; } __ABI_PACKED; /** - * @ingroup I2C - * @brief response to #MRQ_I2C + * @brief Response to #MRQ_I2C */ struct mrq_i2c_response { struct cmd_i2c_xfer_response xfer; } __ABI_PACKED; +/** @} */ + /** * @ingroup MRQ_Codes * @def MRQ_CLK + * @brief Perform a clock operation * - * * Platforms: T186 + * * Platforms: T186, T194 * * Initiators: Any * * Targets: BPMP * * Request Payload: @ref mrq_clk_request * * Response Payload: @ref mrq_clk_response + * * @addtogroup Clocks * @{ */ - -/** - * @name MRQ_CLK sub-commands - * @{ - */ enum { CMD_CLK_GET_RATE = 1, CMD_CLK_SET_RATE = 2, @@ -847,20 +883,13 @@ enum { CMD_CLK_DISABLE = 8, CMD_CLK_GET_ALL_INFO = 14, CMD_CLK_GET_MAX_CLK_ID = 15, + CMD_CLK_GET_FMAX_AT_VMIN = 16, CMD_CLK_MAX, }; -/** @} */ -/** - * @name MRQ_CLK properties - * Flag bits for cmd_clk_properties_response::flags and - * cmd_clk_get_all_info_response::flags - * @{ - */ #define BPMP_CLK_HAS_MUX (1 << 0) #define BPMP_CLK_HAS_SET_RATE (1 << 1) #define BPMP_CLK_IS_ROOT (1 << 2) -/** @} */ #define MRQ_CLK_NAME_MAXLEN 40 #define MRQ_CLK_MAX_PARENTS 16 @@ -959,11 +988,19 @@ struct cmd_clk_get_max_clk_id_request { struct cmd_clk_get_max_clk_id_response { uint32_t max_id; } __ABI_PACKED; -/** @} */ + +/** @private */ +struct cmd_clk_get_fmax_at_vmin_request { + EMPTY +} __ABI_PACKED; + +struct cmd_clk_get_fmax_at_vmin_response { + int64_t rate; +} __ABI_PACKED; /** * @ingroup Clocks - * @brief request with #MRQ_CLK + * @brief Request with #MRQ_CLK * * Used by the sender of an #MRQ_CLK message to control clocks. The * clk_request is split into several sub-commands. Some sub-commands @@ -982,11 +1019,13 @@ struct cmd_clk_get_max_clk_id_response { * |CMD_CLK_DISABLE |- | * |CMD_CLK_GET_ALL_INFO |- | * |CMD_CLK_GET_MAX_CLK_ID |- | + * |CMD_CLK_GET_FMAX_AT_VMIN |- + * | * */ struct mrq_clk_request { - /** @brief sub-command and clock id concatenated to 32-bit word. + /** @brief Sub-command and clock id concatenated to 32-bit word. * - bits[31..24] is the sub-cmd. * - bits[23..0] is the clock id */ @@ -1010,12 +1049,14 @@ struct mrq_clk_request { struct cmd_clk_get_all_info_request clk_get_all_info; /** @private */ struct cmd_clk_get_max_clk_id_request clk_get_max_clk_id; + /** @private */ + struct cmd_clk_get_fmax_at_vmin_request clk_get_fmax_at_vmin; } __UNION_ANON; } __ABI_PACKED; /** * @ingroup Clocks - * @brief response to MRQ_CLK + * @brief Response to MRQ_CLK * * Each sub-command supported by @ref mrq_clk_request may return * sub-command-specific data. Some do and some do not as indicated in @@ -1033,6 +1074,7 @@ struct mrq_clk_request { * |CMD_CLK_DISABLE |- | * |CMD_CLK_GET_ALL_INFO |clk_get_all_info | * |CMD_CLK_GET_MAX_CLK_ID |clk_get_max_id | + * |CMD_CLK_GET_FMAX_AT_VMIN |clk_get_fmax_at_vmin | * */ @@ -1050,13 +1092,16 @@ struct mrq_clk_response { struct cmd_clk_is_enabled_response clk_is_enabled; struct cmd_clk_get_all_info_response clk_get_all_info; struct cmd_clk_get_max_clk_id_response clk_get_max_clk_id; + struct cmd_clk_get_fmax_at_vmin_response clk_get_fmax_at_vmin; } __UNION_ANON; } __ABI_PACKED; +/** @} */ + /** * @ingroup MRQ_Codes * @def MRQ_QUERY_ABI - * @brief check if an MRQ is implemented + * @brief Check if an MRQ is implemented * * * Platforms: All * * Initiators: Any @@ -1067,7 +1112,7 @@ struct mrq_clk_response { /** * @ingroup ABI_info - * @brief request with MRQ_QUERY_ABI + * @brief Request with MRQ_QUERY_ABI * * Used by #MRQ_QUERY_ABI call to check if MRQ code #mrq is supported * by the recipient. @@ -1079,7 +1124,7 @@ struct mrq_query_abi_request { /** * @ingroup ABI_info - * @brief response to MRQ_QUERY_ABI + * @brief Response to MRQ_QUERY_ABI * * @note mrq_response::err of 0 indicates that the query was * successful, not that the MRQ itself is supported! @@ -1092,19 +1137,19 @@ struct mrq_query_abi_response { /** * @ingroup MRQ_Codes * @def MRQ_PG_READ_STATE - * @brief read the power-gating state of a partition + * @brief Read the power-gating state of a partition * * * Platforms: T186 + * @cond bpmp_t186 * * Initiators: Any * * Targets: BPMP * * Request Payload: @ref mrq_pg_read_state_request * * Response Payload: @ref mrq_pg_read_state_response - * @addtogroup Powergating - * @{ */ /** - * @brief request with #MRQ_PG_READ_STATE + * @ingroup Powergating + * @brief Request with #MRQ_PG_READ_STATE * * Used by MRQ_PG_READ_STATE call to read the current state of a * partition. @@ -1115,39 +1160,40 @@ struct mrq_pg_read_state_request { } __ABI_PACKED; /** - * @brief response to MRQ_PG_READ_STATE + * @ingroup Powergating + * @brief Response to MRQ_PG_READ_STATE * @todo define possible errors. */ struct mrq_pg_read_state_response { - /** @brief read as don't care */ + /** @brief Read as don't care */ uint32_t sram_state; - /** @brief state of power partition + /** @brief State of power partition * * 0 : off * * 1 : on */ uint32_t logic_state; } __ABI_PACKED; - +/** @endcond*/ /** @} */ /** * @ingroup MRQ_Codes * @def MRQ_PG_UPDATE_STATE - * @brief modify the power-gating state of a partition. In contrast to + * @brief Modify the power-gating state of a partition. In contrast to * MRQ_PG calls, the operations that change state (on/off) of power * partition are reference counted. * * * Platforms: T186 + * @cond bpmp_t186 * * Initiators: Any * * Targets: BPMP * * Request Payload: @ref mrq_pg_update_state_request * * Response Payload: N/A - * @addtogroup Powergating - * @{ */ /** - * @brief request with mrq_pg_update_state_request + * @ingroup Powergating + * @brief Request with mrq_pg_update_state_request * * Used by #MRQ_PG_UPDATE_STATE call to request BPMP to change the * state of a power partition #partition_id. @@ -1155,20 +1201,20 @@ struct mrq_pg_read_state_response { struct mrq_pg_update_state_request { /** @brief ID of partition */ uint32_t partition_id; - /** @brief secondary control of power partition + /** @brief Secondary control of power partition * @details Ignored by many versions of the BPMP * firmware. For maximum compatibility, set the value - * according to @logic_state + * according to @ref logic_state * * 0x1: power ON partition (@ref logic_state == 0x3) * * 0x3: power OFF partition (@ref logic_state == 0x1) */ uint32_t sram_state; - /** @brief controls state of power partition, legal values are + /** @brief Controls state of power partition, legal values are * * 0x1 : power OFF partition * * 0x3 : power ON partition */ uint32_t logic_state; - /** @brief change state of clocks of the power partition, legal values + /** @brief Change state of clocks of the power partition, legal values * * 0x0 : do not change clock state * * 0x1 : disable partition clocks (only applicable when * @ref logic_state == 0x1) @@ -1177,7 +1223,7 @@ struct mrq_pg_update_state_request { */ uint32_t clock_state; } __ABI_PACKED; -/** @} */ +/** @endcond*/ /** * @ingroup MRQ_Codes @@ -1186,19 +1232,20 @@ struct mrq_pg_update_state_request { * MRQ_PG_UPDATE_STATE, operations that change the power partition * state are NOT reference counted * - * * Platforms: T186 + * @note BPMP-FW forcefully turns off some partitions as part of SC7 entry + * because their state cannot be adequately restored on exit. Therefore, + * it is recommended to power off all domains via MRQ_PG prior to SC7 entry. + * See @ref bpmp_pdomain_ids for further detail. + * + * * Platforms: T186, T194 * * Initiators: Any * * Targets: BPMP * * Request Payload: @ref mrq_pg_request * * Response Payload: @ref mrq_pg_response + * * @addtogroup Powergating * @{ */ - -/** - * @name MRQ_PG sub-commands - * @{ - */ enum mrq_pg_cmd { /** * @brief Check whether the BPMP driver supports the specified @@ -1232,7 +1279,7 @@ enum mrq_pg_cmd { CMD_PG_GET_STATE = 2, /** - * @brief get the name string of specified power domain id. + * @brief Get the name string of specified power domain id. * * mrq_response:err is * 0: Success @@ -1242,7 +1289,7 @@ enum mrq_pg_cmd { /** - * @brief get the highest power domain id in the system. Not + * @brief Get the highest power domain id in the system. Not * all IDs between 0 and max_id are valid IDs. * * mrq_response:err is @@ -1251,35 +1298,36 @@ enum mrq_pg_cmd { */ CMD_PG_GET_MAX_ID = 4, }; -/** @} */ #define MRQ_PG_NAME_MAXLEN 40 -/** - * @brief possible power domain states in - * cmd_pg_set_state_request:state and cmd_pg_get_state_response:state. - * PG_STATE_OFF: power domain is OFF - * PG_STATE_ON: power domain is ON - * PG_STATE_RUNNING: power domain is ON and made into directly usable - * state by turning on the clocks associated with - * the domain - */ enum pg_states { + /** @brief Power domain is OFF */ PG_STATE_OFF = 0, + /** @brief Power domain is ON */ PG_STATE_ON = 1, + /** + * @brief a legacy state where power domain and the clock + * associated to the domain are ON. + * This state is only supported in T186, and the use of it is + * deprecated. + */ PG_STATE_RUNNING = 2, }; struct cmd_pg_query_abi_request { - uint32_t type; /* enum mrq_pg_cmd */ + /** @ref mrq_pg_cmd */ + uint32_t type; } __ABI_PACKED; struct cmd_pg_set_state_request { - uint32_t state; /* enum pg_states */ + /** @ref pg_states */ + uint32_t state; } __ABI_PACKED; struct cmd_pg_get_state_response { - uint32_t state; /* enum pg_states */ + /** @ref pg_states */ + uint32_t state; } __ABI_PACKED; struct cmd_pg_get_name_response { @@ -1291,8 +1339,7 @@ struct cmd_pg_get_max_id_response { } __ABI_PACKED; /** - * @ingroup Powergating - * @brief request with #MRQ_PG + * @brief Request with #MRQ_PG * * Used by the sender of an #MRQ_PG message to control power * partitions. The pg_request is split into several sub-commands. Some @@ -1308,7 +1355,6 @@ struct cmd_pg_get_max_id_response { * |CMD_PG_GET_MAX_ID | - | * */ - struct mrq_pg_request { uint32_t cmd; uint32_t id; @@ -1319,8 +1365,7 @@ struct mrq_pg_request { } __ABI_PACKED; /** - * @ingroup Powergating - * @brief response to MRQ_PG + * @brief Response to MRQ_PG * * Each sub-command supported by @ref mrq_pg_request may return * sub-command-specific data. Some do and some do not as indicated in @@ -1333,9 +1378,7 @@ struct mrq_pg_request { * |CMD_PG_GET_STATE | get_state | * |CMD_PG_GET_NAME | get_name | * |CMD_PG_GET_MAX_ID | get_max_id | - * */ - struct mrq_pg_response { union { struct cmd_pg_get_state_response get_state; @@ -1344,12 +1387,14 @@ struct mrq_pg_response { } __UNION_ANON; } __ABI_PACKED; +/** @} */ + /** * @ingroup MRQ_Codes * @def MRQ_THERMAL - * @brief interact with BPMP thermal framework + * @brief Interact with BPMP thermal framework * - * * Platforms: T186 + * * Platforms: T186, T194 * * Initiators: Any * * Targets: Any * * Request Payload: TODO @@ -1562,17 +1607,18 @@ union mrq_thermal_bpmp_to_host_response { * @brief Query CPU voltage hint data * * * Platforms: T186 + * @cond bpmp_t186 * * Initiators: CCPLEX * * Targets: BPMP * * Request Payload: @ref mrq_cpu_vhint_request * * Response Payload: N/A * - * @addtogroup Vhint CPU Voltage hint + * @addtogroup Vhint * @{ */ /** - * @brief request with #MRQ_CPU_VHINT + * @brief Request with #MRQ_CPU_VHINT * * Used by #MRQ_CPU_VHINT call by CCPLEX to retrieve voltage hint data * from BPMP to memory space pointed by #addr. CCPLEX is responsible @@ -1581,16 +1627,16 @@ union mrq_thermal_bpmp_to_host_response { */ struct mrq_cpu_vhint_request { /** @brief IOVA address for the #cpu_vhint_data */ - uint32_t addr; /* struct cpu_vhint_data * */ + uint32_t addr; /** @brief ID of the cluster whose data is requested */ - uint32_t cluster_id; /* enum cluster_id */ + uint32_t cluster_id; } __ABI_PACKED; /** - * @brief description of the CPU v/f relation + * @brief Description of the CPU v/f relation * - * Used by #MRQ_CPU_VHINT call to carry data pointed by #addr of - * struct mrq_cpu_vhint_request + * Used by #MRQ_CPU_VHINT call to carry data pointed by + * #mrq_cpu_vhint_request::addr */ struct cpu_vhint_data { uint32_t ref_clk_hz; /**< reference frequency in Hz */ @@ -1612,7 +1658,7 @@ struct cpu_vhint_data { /** reserved for future use */ uint16_t reserved[328]; } __ABI_PACKED; - +/** @endcond */ /** @} */ /** @@ -1620,7 +1666,7 @@ struct cpu_vhint_data { * @def MRQ_ABI_RATCHET * @brief ABI ratchet value query * - * * Platforms: T186 + * * Platforms: T186, T194 * * Initiators: Any * * Targets: BPMP * * Request Payload: @ref mrq_abi_ratchet_request @@ -1630,7 +1676,7 @@ struct cpu_vhint_data { */ /** - * @brief an ABI compatibility mechanism + * @brief An ABI compatibility mechanism * * BPMP_ABI_RATCHET_VALUE may increase for various reasons in a future * revision of this header file. @@ -1644,7 +1690,7 @@ struct cpu_vhint_data { #define BPMP_ABI_RATCHET_VALUE 3 /** - * @brief request with #MRQ_ABI_RATCHET. + * @brief Request with #MRQ_ABI_RATCHET. * * #ratchet should be #BPMP_ABI_RATCHET_VALUE from the ABI header * against which the requester was compiled. @@ -1657,12 +1703,12 @@ struct cpu_vhint_data { * Otherwise, err shall be 0. */ struct mrq_abi_ratchet_request { - /** @brief requester's ratchet value */ + /** @brief Requester's ratchet value */ uint16_t ratchet; }; /** - * @brief response to #MRQ_ABI_RATCHET + * @brief Response to #MRQ_ABI_RATCHET * * #ratchet shall be #BPMP_ABI_RATCHET_VALUE from the ABI header * against which BPMP firwmare was compiled. @@ -1685,9 +1731,9 @@ struct mrq_abi_ratchet_response { /** * @ingroup MRQ_Codes * @def MRQ_EMC_DVFS_LATENCY - * @brief query frequency dependent EMC DVFS latency + * @brief Query frequency dependent EMC DVFS latency * - * * Platforms: T186 + * * Platforms: T186, T194 * * Initiators: CCPLEX * * Targets: BPMP * * Request Payload: N/A @@ -1697,7 +1743,7 @@ struct mrq_abi_ratchet_response { */ /** - * @brief used by @ref mrq_emc_dvfs_latency_response + * @brief Used by @ref mrq_emc_dvfs_latency_response */ struct emc_dvfs_latency { /** @brief EMC frequency in kHz */ @@ -1708,10 +1754,10 @@ struct emc_dvfs_latency { #define EMC_DVFS_LATENCY_MAX_SIZE 14 /** - * @brief response to #MRQ_EMC_DVFS_LATENCY + * @brief Response to #MRQ_EMC_DVFS_LATENCY */ struct mrq_emc_dvfs_latency_response { - /** @brief the number valid entries in #pairs */ + /** @brief The number valid entries in #pairs */ uint32_t num_pairs; /** @brief EMC <frequency, latency> information */ struct emc_dvfs_latency pairs[EMC_DVFS_LATENCY_MAX_SIZE]; @@ -1721,8 +1767,96 @@ struct mrq_emc_dvfs_latency_response { /** * @ingroup MRQ_Codes + * @def MRQ_CPU_NDIV_LIMITS + * @brief CPU freq. limits in ndiv + * + * * Platforms: T194 onwards + * @cond bpmp_t194 + * * Initiators: CCPLEX + * * Targets: BPMP + * * Request Payload: @ref mrq_cpu_ndiv_limits_request + * * Response Payload: @ref mrq_cpu_ndiv_limits_response + * @addtogroup CPU + * @{ + */ + +/** + * @brief Request for ndiv limits of a cluster + */ +struct mrq_cpu_ndiv_limits_request { + /** @brief Enum cluster_id */ + uint32_t cluster_id; +} __ABI_PACKED; + +/** + * @brief Response to #MRQ_CPU_NDIV_LIMITS + */ +struct mrq_cpu_ndiv_limits_response { + /** @brief Reference frequency in Hz */ + uint32_t ref_clk_hz; + /** @brief Post divider value */ + uint16_t pdiv; + /** @brief Input divider value */ + uint16_t mdiv; + /** @brief FMAX expressed with max NDIV value */ + uint16_t ndiv_max; + /** @brief Minimum allowed NDIV value */ + uint16_t ndiv_min; +} __ABI_PACKED; + +/** @} */ +/** @endcond */ + +/** + * @ingroup MRQ_Codes + * @def MRQ_CPU_AUTO_CC3 + * @brief Query CPU cluster auto-CC3 configuration + * + * * Platforms: T194 onwards + * @cond bpmp_t194 + * * Initiators: CCPLEX + * * Targets: BPMP + * * Request Payload: @ref mrq_cpu_auto_cc3_request + * * Response Payload: @ref mrq_cpu_auto_cc3_response + * @addtogroup CC3 + * + * Queries from BPMP auto-CC3 configuration (allowed/not allowed) for a + * specified cluster. CCPLEX s/w uses this information to override its own + * device tree auto-CC3 settings, so that BPMP device tree is a single source of + * auto-CC3 platform configuration. + * + * @{ + */ + +/** + * @brief Request for auto-CC3 configuration of a cluster + */ +struct mrq_cpu_auto_cc3_request { + /** @brief Enum cluster_id (logical cluster id, known to CCPLEX s/w) */ + uint32_t cluster_id; +} __ABI_PACKED; + +/** + * @brief Response to #MRQ_CPU_AUTO_CC3 + */ +struct mrq_cpu_auto_cc3_response { + /** + * @brief auto-CC3 configuration + * + * - bits[31..10] reserved. + * - bits[9..1] cc3 ndiv + * - bit [0] if "1" auto-CC3 is allowed, if "0" auto-CC3 is not allowed + */ + uint32_t auto_cc3_config; +} __ABI_PACKED; + +/** @} */ +/** @endcond */ + +/** + * @ingroup MRQ_Codes * @def MRQ_TRACE_ITER - * @brief manage the trace iterator + * @brief Manage the trace iterator * * * Platforms: All * * Initiators: CCPLEX @@ -1735,12 +1869,12 @@ struct mrq_emc_dvfs_latency_response { enum { /** @brief (re)start the tracing now. Ignore older events */ TRACE_ITER_INIT = 0, - /** @brief clobber all events in the trace buffer */ + /** @brief Clobber all events in the trace buffer */ TRACE_ITER_CLEAN = 1 }; /** - * @brief request with #MRQ_TRACE_ITER + * @brief Request with #MRQ_TRACE_ITER */ struct mrq_trace_iter_request { /** @brief TRACE_ITER_INIT or TRACE_ITER_CLEAN */ @@ -1900,7 +2034,7 @@ struct cmd_ringbuf_console_get_fifo_resp { */ struct mrq_ringbuf_console_host_to_bpmp_request { /** - * @brief type of request. Values listed in enum + * @brief Type of request. Values listed in enum * #mrq_ringbuf_console_host_to_bpmp_cmd. */ uint32_t type; @@ -1927,49 +2061,616 @@ union mrq_ringbuf_console_bpmp_to_host_response { } __ABI_PACKED; /** @} */ -/* - * 4. Enumerations +/** + * @ingroup MRQ_Codes + * @def MRQ_STRAP + * @brief Set a strap value controlled by BPMP + * + * * Platforms: T194 onwards + * @cond bpmp_t194 + * * Initiators: CCPLEX + * * Targets: BPMP + * * Request Payload: @ref mrq_strap_request + * * Response Payload: N/A + * @addtogroup Strap + * + * A strap is an input that is sampled by a hardware unit during the + * unit's startup process. The sampled value of a strap affects the + * behavior of the unit until the unit is restarted. Many hardware + * units sample their straps at the instant that their resets are + * deasserted. + * + * BPMP owns registers which act as straps to various units. It + * exposes limited control of those straps via #MRQ_STRAP. + * + * @{ */ +enum mrq_strap_cmd { + /** @private */ + STRAP_RESERVED = 0, + /** @brief Set a strap value */ + STRAP_SET = 1 +}; -/* - * 4.1 CPU enumerations +/** + * @brief Request with #MRQ_STRAP + */ +struct mrq_strap_request { + /** @brief @ref mrq_strap_cmd */ + uint32_t cmd; + /** @brief Strap ID from @ref Strap_Ids */ + uint32_t id; + /** @brief Desired value for strap (if cmd is #STRAP_SET) */ + uint32_t value; +} __ABI_PACKED; + +/** + * @defgroup Strap_Ids Strap Identifiers + * @} + */ +/** @endcond */ + +/** + * @ingroup MRQ_Codes + * @def MRQ_UPHY + * @brief Perform a UPHY operation * - * See <mach-t186/system-t186.h> + * * Platforms: T194 onwards + * @cond bpmp_t194 + * * Initiators: CCPLEX + * * Targets: BPMP + * * Request Payload: @ref mrq_uphy_request + * * Response Payload: @ref mrq_uphy_response * - * 4.2 CPU Cluster enumerations + * @addtogroup UPHY + * @{ + */ +enum { + CMD_UPHY_PCIE_LANE_MARGIN_CONTROL = 1, + CMD_UPHY_PCIE_LANE_MARGIN_STATUS = 2, + CMD_UPHY_PCIE_EP_CONTROLLER_PLL_INIT = 3, + CMD_UPHY_PCIE_CONTROLLER_STATE = 4, + CMD_UPHY_MAX, +}; + +struct cmd_uphy_margin_control_request { + /** @brief Enable margin */ + int32_t en; + /** @brief Clear the number of error and sections */ + int32_t clr; + /** @brief Set x offset (1's complement) for left/right margin type (y should be 0) */ + uint32_t x; + /** @brief Set y offset (1's complement) for left/right margin type (x should be 0) */ + uint32_t y; + /** @brief Set number of bit blocks for each margin section */ + uint32_t nblks; +} __ABI_PACKED; + +struct cmd_uphy_margin_status_response { + /** @brief Number of errors observed */ + uint32_t status; +} __ABI_PACKED; + +struct cmd_uphy_ep_controller_pll_init_request { + /** @brief EP controller number, valid: 0, 4, 5 */ + uint8_t ep_controller; +} __ABI_PACKED; + +struct cmd_uphy_pcie_controller_state_request { + /** @brief PCIE controller number, valid: 0, 1, 2, 3, 4 */ + uint8_t pcie_controller; + uint8_t enable; +} __ABI_PACKED; + +/** + * @ingroup UPHY + * @brief Request with #MRQ_UPHY * - * See <mach-t186/system-t186.h> + * Used by the sender of an #MRQ_UPHY message to control UPHY Lane RX margining. + * The uphy_request is split into several sub-commands. Some sub-commands + * require no additional data. Others have a sub-command specific payload * - * 4.3 System low power state enumerations + * |sub-command |payload | + * |------------------------------------ |----------------------------------------| + * |CMD_UPHY_PCIE_LANE_MARGIN_CONTROL |uphy_set_margin_control | + * |CMD_UPHY_PCIE_LANE_MARGIN_STATUS | | + * |CMD_UPHY_PCIE_EP_CONTROLLER_PLL_INIT |cmd_uphy_ep_controller_pll_init_request | + * |CMD_UPHY_PCIE_CONTROLLER_STATE |cmd_uphy_pcie_controller_state_request | * - * See <mach-t186/system-t186.h> */ -/* - * 4.4 Clock enumerations +struct mrq_uphy_request { + /** @brief Lane number. */ + uint16_t lane; + /** @brief Sub-command id. */ + uint16_t cmd; + + union { + struct cmd_uphy_margin_control_request uphy_set_margin_control; + struct cmd_uphy_ep_controller_pll_init_request ep_ctrlr_pll_init; + struct cmd_uphy_pcie_controller_state_request controller_state; + } __UNION_ANON; +} __ABI_PACKED; + +/** + * @ingroup UPHY + * @brief Response to MRQ_UPHY + * + * Each sub-command supported by @ref mrq_uphy_request may return + * sub-command-specific data. Some do and some do not as indicated in + * the following table + * + * |sub-command |payload | + * |---------------------------- |------------------------| + * |CMD_UPHY_PCIE_LANE_MARGIN_CONTROL | | + * |CMD_UPHY_PCIE_LANE_MARGIN_STATUS |uphy_get_margin_status | * - * For clock enumerations, see <mach-t186/clk-t186.h> */ -/* - * 4.5 Reset enumerations +struct mrq_uphy_response { + union { + struct cmd_uphy_margin_status_response uphy_get_margin_status; + } __UNION_ANON; +} __ABI_PACKED; + +/** @} */ +/** @endcond */ + +/** + * @ingroup MRQ_Codes + * @def MRQ_FMON + * @brief Perform a frequency monitor configuration operations + * + * * Platforms: T194 onwards + * @cond bpmp_t194 + * * Initiators: CCPLEX + * * Targets: BPMP + * * Request Payload: @ref mrq_fmon_request + * * Response Payload: @ref mrq_fmon_response * - * For reset enumerations, see <mach-t186/reset-t186.h> + * @addtogroup FMON + * @{ */ +enum { + /** + * @brief Clamp FMON configuration to specified rate. + * + * The monitored clock must be running for clamp to succeed. If + * clamped, FMON configuration is preserved when clock rate + * and/or state is changed. + */ + CMD_FMON_GEAR_CLAMP = 1, + /** + * @brief Release clamped FMON configuration. + * + * Allow FMON configuration to follow monitored clock rate + * and/or state changes. + */ + CMD_FMON_GEAR_FREE = 2, + /** + * @brief Return rate FMON is clamped at, or 0 if FMON is not + * clamped. + * + * Inherently racy, since clamp state can be changed + * concurrently. Useful for testing. + */ + CMD_FMON_GEAR_GET = 3, + CMD_FMON_NUM, +}; -/* - * 4.6 Thermal sensor enumerations +struct cmd_fmon_gear_clamp_request { + int32_t unused; + int64_t rate; +} __ABI_PACKED; + +/** @private */ +struct cmd_fmon_gear_clamp_response { + EMPTY +} __ABI_PACKED; + +/** @private */ +struct cmd_fmon_gear_free_request { + EMPTY +} __ABI_PACKED; + +/** @private */ +struct cmd_fmon_gear_free_response { + EMPTY +} __ABI_PACKED; + +/** @private */ +struct cmd_fmon_gear_get_request { + EMPTY +} __ABI_PACKED; + +struct cmd_fmon_gear_get_response { + int64_t rate; +} __ABI_PACKED; + +/** + * @ingroup FMON + * @brief Request with #MRQ_FMON + * + * Used by the sender of an #MRQ_FMON message to configure clock + * frequency monitors. The FMON request is split into several + * sub-commands. Some sub-commands require no additional data. + * Others have a sub-command specific payload + * + * |sub-command |payload | + * |----------------------------|-----------------------| + * |CMD_FMON_GEAR_CLAMP |fmon_gear_clamp | + * |CMD_FMON_GEAR_FREE |- | + * |CMD_FMON_GEAR_GET |- | + * + */ + +struct mrq_fmon_request { + /** @brief Sub-command and clock id concatenated to 32-bit word. + * - bits[31..24] is the sub-cmd. + * - bits[23..0] is monitored clock id used to select target + * FMON + */ + uint32_t cmd_and_id; + + union { + struct cmd_fmon_gear_clamp_request fmon_gear_clamp; + /** @private */ + struct cmd_fmon_gear_free_request fmon_gear_free; + /** @private */ + struct cmd_fmon_gear_get_request fmon_gear_get; + } __UNION_ANON; +} __ABI_PACKED; + +/** + * @ingroup FMON + * @brief Response to MRQ_FMON + * + * Each sub-command supported by @ref mrq_fmon_request may + * return sub-command-specific data as indicated below. + * + * |sub-command |payload | + * |----------------------------|------------------------| + * |CMD_FMON_GEAR_CLAMP |- | + * |CMD_FMON_GEAR_FREE |- | + * |CMD_FMON_GEAR_GET |fmon_gear_get | + * + */ + +struct mrq_fmon_response { + union { + /** @private */ + struct cmd_fmon_gear_clamp_response fmon_gear_clamp; + /** @private */ + struct cmd_fmon_gear_free_response fmon_gear_free; + struct cmd_fmon_gear_get_response fmon_gear_get; + } __UNION_ANON; +} __ABI_PACKED; + +/** @} */ +/** @endcond */ + +/** + * @ingroup MRQ_Codes + * @def MRQ_EC + * @brief Provide status information on faults reported by Error + * Collator (EC) to HSM. + * + * * Platforms: T194 onwards + * @cond bpmp_t194 + * * Initiators: CCPLEX + * * Targets: BPMP + * * Request Payload: @ref mrq_ec_request + * * Response Payload: @ref mrq_ec_response + * + * @note This MRQ ABI is under construction, and subject to change + * + * @addtogroup EC + * @{ + */ +enum { + /** + * @brief Retrieve specified EC status. + * + * mrq_response::err is 0 if the operation was successful, or @n + * -#BPMP_ENODEV if target EC is not owned by BPMP @n + * -#BPMP_EACCES if target EC power domain is turned off + */ + CMD_EC_STATUS_GET = 1, + CMD_EC_NUM, +}; + +/** @brief BPMP ECs error types */ +enum bpmp_ec_err_type { + /** @brief Parity error on internal data path + * + * Error descriptor @ref ec_err_simple_desc. + */ + EC_ERR_TYPE_PARITY_INTERNAL = 1, + + /** @brief ECC SEC error on internal data path + * + * Error descriptor @ref ec_err_simple_desc. + */ + EC_ERR_TYPE_ECC_SEC_INTERNAL = 2, + + /** @brief ECC DED error on internal data path + * + * Error descriptor @ref ec_err_simple_desc. + */ + EC_ERR_TYPE_ECC_DED_INTERNAL = 3, + + /** @brief Comparator error + * + * Error descriptor @ref ec_err_simple_desc. + */ + EC_ERR_TYPE_COMPARATOR = 4, + + /** @brief Register parity error + * + * Error descriptor @ref ec_err_reg_parity_desc. + */ + EC_ERR_TYPE_REGISTER_PARITY = 5, + + /** @brief Parity error from on-chip SRAM/FIFO + * + * Error descriptor @ref ec_err_simple_desc. + */ + EC_ERR_TYPE_PARITY_SRAM = 6, + + /** @brief Clock Monitor error + * + * Error descriptor @ref ec_err_fmon_desc. + */ + EC_ERR_TYPE_CLOCK_MONITOR = 9, + + /** @brief Voltage Monitor error + * + * Error descriptor @ref ec_err_vmon_desc. + */ + EC_ERR_TYPE_VOLTAGE_MONITOR = 10, + + /** @brief SW Correctable error + * + * Error descriptor @ref ec_err_simple_desc. + */ + EC_ERR_TYPE_SW_CORRECTABLE = 16, + + /** @brief SW Uncorrectable error + * + * Error descriptor @ref ec_err_simple_desc. + */ + EC_ERR_TYPE_SW_UNCORRECTABLE = 17, + + /** @brief Other HW Correctable error + * + * Error descriptor @ref ec_err_simple_desc. + */ + EC_ERR_TYPE_OTHER_HW_CORRECTABLE = 32, + + /** @brief Other HW Uncorrectable error + * + * Error descriptor @ref ec_err_simple_desc. + */ + EC_ERR_TYPE_OTHER_HW_UNCORRECTABLE = 33, +}; + +/** @brief Group of registers with parity error. */ +enum ec_registers_group { + /** @brief Functional registers group */ + EC_ERR_GROUP_FUNC_REG = 0, + /** @brief SCR registers group */ + EC_ERR_GROUP_SCR_REG = 1, +}; + +/** + * @defgroup bpmp_ec_status_flags EC Status Flags + * @addtogroup bpmp_ec_status_flags + * @{ + */ +/** @brief No EC error found flag */ +#define EC_STATUS_FLAG_NO_ERROR 0x0001 +/** @brief Last EC error found flag */ +#define EC_STATUS_FLAG_LAST_ERROR 0x0002 +/** @brief EC latent error flag */ +#define EC_STATUS_FLAG_LATENT_ERROR 0x0004 +/** @} */ + +/** + * @defgroup bpmp_ec_desc_flags EC Descriptor Flags + * @addtogroup bpmp_ec_desc_flags + * @{ + */ +/** @brief EC descriptor error resolved flag */ +#define EC_DESC_FLAG_RESOLVED 0x0001 +/** @brief EC descriptor failed to retrieve id flag */ +#define EC_DESC_FLAG_NO_ID 0x0002 +/** @} */ + +/** + * |error type | fmon_clk_id values | + * |---------------------------------|---------------------------| + * |@ref EC_ERR_TYPE_CLOCK_MONITOR |@ref bpmp_clock_ids | + */ +struct ec_err_fmon_desc { + /** @brief Bitmask of @ref bpmp_ec_desc_flags */ + uint16_t desc_flags; + /** @brief FMON monitored clock id */ + uint16_t fmon_clk_id; + /** @brief Bitmask of @ref bpmp_fmon_faults_flags */ + uint32_t fmon_faults; + /** @brief FMON faults access error */ + int32_t fmon_access_error; +} __ABI_PACKED; + +/** + * |error type | vmon_adc_id values | + * |---------------------------------|---------------------------| + * |@ref EC_ERR_TYPE_VOLTAGE_MONITOR |@ref bpmp_adc_ids | + */ +struct ec_err_vmon_desc { + /** @brief Bitmask of @ref bpmp_ec_desc_flags */ + uint16_t desc_flags; + /** @brief VMON rail adc id */ + uint16_t vmon_adc_id; + /** @brief Bitmask of @ref bpmp_vmon_faults_flags */ + uint32_t vmon_faults; + /** @brief VMON faults access error */ + int32_t vmon_access_error; +} __ABI_PACKED; + +/** + * |error type | reg_id values | + * |---------------------------------|---------------------------| + * |@ref EC_ERR_TYPE_REGISTER_PARITY |@ref bpmp_ec_registers_ids | + */ +struct ec_err_reg_parity_desc { + /** @brief Bitmask of @ref bpmp_ec_desc_flags */ + uint16_t desc_flags; + /** @brief Register id */ + uint16_t reg_id; + /** @brief Register group @ref ec_registers_group */ + uint16_t reg_group; +} __ABI_PACKED; + +/** + * |error type | err_source_id values | + * |----------------------------------------|---------------------------| + * |@ref EC_ERR_TYPE_PARITY_INTERNAL |@ref bpmp_ec_ipath_ids | + * |@ref EC_ERR_TYPE_ECC_SEC_INTERNAL |@ref bpmp_ec_ipath_ids | + * |@ref EC_ERR_TYPE_ECC_DED_INTERNAL |@ref bpmp_ec_ipath_ids | + * |@ref EC_ERR_TYPE_COMPARATOR |@ref bpmp_ec_comparator_ids| + * |@ref EC_ERR_TYPE_PARITY_SRAM |@ref bpmp_clock_ids | + * |@ref EC_ERR_TYPE_SW_CORRECTABLE |@ref bpmp_ec_misc_ids | + * |@ref EC_ERR_TYPE_SW_UNCORRECTABLE |@ref bpmp_ec_misc_ids | + * |@ref EC_ERR_TYPE_OTHER_HW_CORRECTABLE |@ref bpmp_ec_misc_ids | + * |@ref EC_ERR_TYPE_OTHER_HW_UNCORRECTABLE |@ref bpmp_ec_misc_ids | + */ +struct ec_err_simple_desc { + /** @brief Bitmask of @ref bpmp_ec_desc_flags */ + uint16_t desc_flags; + /** @brief Error source id. Id space depends on error type. */ + uint16_t err_source_id; +} __ABI_PACKED; + +/** @brief Union of EC error descriptors */ +union ec_err_desc { + struct ec_err_fmon_desc fmon_desc; + struct ec_err_vmon_desc vmon_desc; + struct ec_err_reg_parity_desc reg_parity_desc; + struct ec_err_simple_desc simple_desc; +} __ABI_PACKED; + +struct cmd_ec_status_get_request { + /** @brief HSM error line number that identifies target EC. */ + uint32_t ec_hsm_id; +} __ABI_PACKED; + +/** EC status maximum number of descriptors */ +#define EC_ERR_STATUS_DESC_MAX_NUM 4 + +struct cmd_ec_status_get_response { + /** @brief Target EC id (the same id received with request). */ + uint32_t ec_hsm_id; + /** + * @brief Bitmask of @ref bpmp_ec_status_flags + * + * If NO_ERROR flag is set, error_ fields should be ignored + */ + uint32_t ec_status_flags; + /** @brief Found EC error index. */ + uint32_t error_idx; + /** @brief Found EC error type @ref bpmp_ec_err_type. */ + uint32_t error_type; + /** @brief Number of returned EC error descriptors */ + uint32_t error_desc_num; + /** @brief EC error descriptors */ + union ec_err_desc error_descs[EC_ERR_STATUS_DESC_MAX_NUM]; +} __ABI_PACKED; + +/** + * @ingroup EC + * @brief Request with #MRQ_EC + * + * Used by the sender of an #MRQ_EC message to access ECs owned + * by BPMP. + * + * |sub-command |payload | + * |----------------------------|-----------------------| + * |@ref CMD_EC_STATUS_GET |ec_status_get | * - * For thermal sensor enumerations, see <mach-t186/thermal-t186.h> */ +struct mrq_ec_request { + /** @brief Sub-command id. */ + uint32_t cmd_id; + + union { + struct cmd_ec_status_get_request ec_status_get; + } __UNION_ANON; +} __ABI_PACKED; + /** - * @defgroup Error_Codes + * @ingroup EC + * @brief Response to MRQ_EC + * + * Each sub-command supported by @ref mrq_ec_request may return + * sub-command-specific data as indicated below. + * + * |sub-command |payload | + * |----------------------------|------------------------| + * |@ref CMD_EC_STATUS_GET |ec_status_get | + * + */ + +struct mrq_ec_response { + union { + struct cmd_ec_status_get_response ec_status_get; + } __UNION_ANON; +} __ABI_PACKED; + +/** @} */ +/** @endcond */ + +/** + * @ingroup MRQ_Codes + * @def MRQ_FBVOLT_STATUS + * @brief Provides status information about voltage state for fuse burning + * + * * Platforms: T194 onwards + * @cond bpmp_t194 + * * Initiators: CCPLEX + * * Target: BPMP + * * Request Payload: None + * * Response Payload: @ref mrq_fbvolt_status_response + * @{ + */ + +/** + * @ingroup Fbvolt_status + * @brief Response to #MRQ_FBVOLT_STATUS + * + * Value of #ready reflects if core voltages are in a suitable state for buring + * fuses. A value of 0x1 indicates that core voltages are ready for burning + * fuses. A value of 0x0 indicates that core voltages are not ready. + */ +struct mrq_fbvolt_status_response { + /** @brief Bit [0:0] - ready status, bits [31:1] - reserved */ + uint32_t ready; + /** @brief Reserved */ + uint32_t unused; +} __ABI_PACKED; + +/** @} */ +/** @endcond */ + +/** + * @addtogroup Error_Codes * Negative values for mrq_response::err generally indicate some * error. The ABI defines the following error codes. Negating these * defines is an exercise left to the user. * @{ */ + /** @brief No such file or directory */ #define BPMP_ENOENT 2 /** @brief No MRQ handler */ @@ -1994,6 +2695,11 @@ union mrq_ringbuf_console_bpmp_to_host_response { #define BPMP_ETIMEDOUT 23 /** @brief Out of range */ #define BPMP_ERANGE 34 +/** @brief Function not implemented */ +#define BPMP_ENOSYS 38 +/** @brief Invalid slot */ +#define BPMP_EBADSLT 57 + /** @} */ -/** @} */ + #endif diff --git a/include/soc/tegra/bpmp.h b/include/soc/tegra/bpmp.h @@ -129,6 +129,7 @@ int tegra_bpmp_request_mrq(struct tegra_bpmp *bpmp, unsigned int mrq, tegra_bpmp_mrq_handler_t handler, void *data); void tegra_bpmp_free_mrq(struct tegra_bpmp *bpmp, unsigned int mrq, void *data); +bool tegra_bpmp_mrq_is_supported(struct tegra_bpmp *bpmp, unsigned int mrq); #else static inline struct tegra_bpmp *tegra_bpmp_get(struct device *dev) { @@ -164,6 +165,12 @@ static inline void tegra_bpmp_free_mrq(struct tegra_bpmp *bpmp, unsigned int mrq, void *data) { } + +static inline bool tegra_bpmp_mrq_is_supported(struct tegra_bpmp *bpmp, + unsigned int mrq) +{ + return false; +} #endif #if IS_ENABLED(CONFIG_CLK_TEGRA_BPMP) diff --git a/include/soc/tegra/fuse.h b/include/soc/tegra/fuse.h @@ -60,7 +60,6 @@ struct tegra_sku_info { u32 tegra_read_straps(void); u32 tegra_read_ram_code(void); -u32 tegra_read_chipid(void); int tegra_fuse_readl(unsigned long offset, u32 *value); extern struct tegra_sku_info tegra_sku_info; diff --git a/include/soc/tegra/pmc.h b/include/soc/tegra/pmc.h @@ -88,6 +88,10 @@ enum tegra_io_pad { TEGRA_IO_PAD_CSID, TEGRA_IO_PAD_CSIE, TEGRA_IO_PAD_CSIF, + TEGRA_IO_PAD_CSIG, + TEGRA_IO_PAD_CSIH, + TEGRA_IO_PAD_DAP3, + TEGRA_IO_PAD_DAP5, TEGRA_IO_PAD_DBG, TEGRA_IO_PAD_DEBUG_NONAO, TEGRA_IO_PAD_DMIC, @@ -100,10 +104,15 @@ enum tegra_io_pad { TEGRA_IO_PAD_EDP, TEGRA_IO_PAD_EMMC, TEGRA_IO_PAD_EMMC2, + TEGRA_IO_PAD_EQOS, TEGRA_IO_PAD_GPIO, + TEGRA_IO_PAD_GP_PWM2, + TEGRA_IO_PAD_GP_PWM3, TEGRA_IO_PAD_HDMI, TEGRA_IO_PAD_HDMI_DP0, TEGRA_IO_PAD_HDMI_DP1, + TEGRA_IO_PAD_HDMI_DP2, + TEGRA_IO_PAD_HDMI_DP3, TEGRA_IO_PAD_HSIC, TEGRA_IO_PAD_HV, TEGRA_IO_PAD_LVDS, @@ -113,8 +122,14 @@ enum tegra_io_pad { TEGRA_IO_PAD_PEX_CLK_BIAS, TEGRA_IO_PAD_PEX_CLK1, TEGRA_IO_PAD_PEX_CLK2, + TEGRA_IO_PAD_PEX_CLK2_BIAS, TEGRA_IO_PAD_PEX_CLK3, TEGRA_IO_PAD_PEX_CNTRL, + TEGRA_IO_PAD_PEX_CTL2, + TEGRA_IO_PAD_PEX_L0_RST_N, + TEGRA_IO_PAD_PEX_L1_RST_N, + TEGRA_IO_PAD_PEX_L5_RST_N, + TEGRA_IO_PAD_PWR_CTL, TEGRA_IO_PAD_SDMMC1, TEGRA_IO_PAD_SDMMC1_HV, TEGRA_IO_PAD_SDMMC2, @@ -122,10 +137,16 @@ enum tegra_io_pad { TEGRA_IO_PAD_SDMMC3, TEGRA_IO_PAD_SDMMC3_HV, TEGRA_IO_PAD_SDMMC4, + TEGRA_IO_PAD_SOC_GPIO10, + TEGRA_IO_PAD_SOC_GPIO12, + TEGRA_IO_PAD_SOC_GPIO13, + TEGRA_IO_PAD_SOC_GPIO53, TEGRA_IO_PAD_SPI, TEGRA_IO_PAD_SPI_HV, TEGRA_IO_PAD_SYS_DDC, TEGRA_IO_PAD_UART, + TEGRA_IO_PAD_UART4, + TEGRA_IO_PAD_UART5, TEGRA_IO_PAD_UFS, TEGRA_IO_PAD_USB0, TEGRA_IO_PAD_USB1,