ARM: SoC driver updates

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
 
  + misc cleanups across several platforms
 -----BEGIN PGP SIGNATURE-----
 
 iQJDBAABCAAtFiEElf+HevZ4QCAJmMQ+jBrnPN6EHHcFAlwqd4APHG9sb2ZAbGl4
 b20ubmV0AAoJEIwa5zzehBx3HXoP/icWJTGrbf9R6T7r0RWB3xeV8ouTPMM8YM5C
 6wD4LkkjgZ16Hz/ellJ0Oug77LdnJ/ZI7jH2u0IcKRXr4sL94hEo11jAJLLtCHpt
 rGiItMuEDMhNFcAK/yREI6FtRqjNZhsTuR+gkcjzMnGLCaTA1+RwQNdugH0hh0fF
 z8C6tjN+fRIeS0wInBzR/402GcgRU0DIJrr0kmklS0u6tc2QW24ffv8ymvMiVO46
 l8VemmdxVZsBU2iehraPy6mSXsyTm04dNTuHnrIw3nE3kTJF7jMvpqI/euU1eZl6
 6EzrrCym8nC66IlqhHMBB427PK8sRqJTqwqSXO6e90AqiK4H2bMovXKiob/Psq+e
 yWqPOrAr8YBLqTgauvCzVm/xneT5rZM4N0BYhOk172Uk52qenNWDnqHj41A4CMSM
 /id3L1cHs5nf2qwuMncXvLX+Y2vO2n6cMmF8cDRLu592OBZRcVepUM0xoaSdZScv
 LJsP3jH3RRcY3L2rf7bY2Mitp48bDgZMZdw/viSHsFS+SVr225uNFALFDQ9kNEoZ
 2d9i9IvC7xOMhdVAX03U7DuRcpKXBPcv+arA57PiVvR4M1HeU7VvD4ayP5loVX2J
 GoDIKiPQitAsOKzyPyZ5Jw04lxio3xZbrbmmVzEH8uKWIV5omdiMnSrFsEfduRCT
 rU+Mqe2j
 =yEX2
 -----END PGP SIGNATURE-----

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
  ...
This commit is contained in:
Linus Torvalds 2018-12-31 17:32:35 -08:00
commit d36377c6eb
68 changed files with 3912 additions and 670 deletions

View File

@ -35,6 +35,7 @@ Required standard properties:
"ti,sysc-omap3-sham" "ti,sysc-omap3-sham"
"ti,sysc-omap-aes" "ti,sysc-omap-aes"
"ti,sysc-mcasp" "ti,sysc-mcasp"
"ti,sysc-dra7-mcasp"
"ti,sysc-usb-host-fs" "ti,sysc-usb-host-fs"
"ti,sysc-dra7-mcan" "ti,sysc-dra7-mcan"

View File

@ -6,7 +6,9 @@ Control (PGC) for various power domains.
Required properties: 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 - reg: should be register base and length as documented in the
datasheet datasheet
@ -22,7 +24,8 @@ which, in turn, is expected to contain the following:
Required properties: Required properties:
- reg: Power domain index. Valid values are defined in - 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 - #power-domain-cells: Should be 0

View File

@ -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>;
};

View File

@ -23,6 +23,7 @@ resources.
"qcom,rpm-msm8916" "qcom,rpm-msm8916"
"qcom,rpm-msm8974" "qcom,rpm-msm8974"
"qcom,rpm-msm8998" "qcom,rpm-msm8998"
"qcom,rpm-qcs404"
- qcom,smd-channels: - qcom,smd-channels:
Usage: required Usage: required

View File

@ -7,7 +7,9 @@ Required properties for power domain controller:
- compatible: Should be one of the following. - compatible: Should be one of the following.
"rockchip,px30-power-controller" - for PX30 SoCs. "rockchip,px30-power-controller" - for PX30 SoCs.
"rockchip,rk3036-power-controller" - for RK3036 SoCs. "rockchip,rk3036-power-controller" - for RK3036 SoCs.
"rockchip,rk3066-power-controller" - for RK3066 SoCs.
"rockchip,rk3128-power-controller" - for RK3128 SoCs. "rockchip,rk3128-power-controller" - for RK3128 SoCs.
"rockchip,rk3188-power-controller" - for RK3188 SoCs.
"rockchip,rk3228-power-controller" - for RK3228 SoCs. "rockchip,rk3228-power-controller" - for RK3228 SoCs.
"rockchip,rk3288-power-controller" - for RK3288 SoCs. "rockchip,rk3288-power-controller" - for RK3288 SoCs.
"rockchip,rk3328-power-controller" - for RK3328 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: - 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/px30-power.h" - for PX30 type power domain.
"include/dt-bindings/power/rk3036-power.h" - for RK3036 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/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/rk3228-power.h" - for RK3228 type power domain.
"include/dt-bindings/power/rk3288-power.h" - for RK3288 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. "include/dt-bindings/power/rk3328-power.h" - for RK3328 type power domain.

View File

@ -18,7 +18,9 @@ Required properties:
- "allwinner,sun8i-h3-system-control" - "allwinner,sun8i-h3-system-control"
- "allwinner,sun50i-a64-sram-controller" (deprecated) - "allwinner,sun50i-a64-sram-controller" (deprecated)
- "allwinner,sun50i-a64-system-control" - "allwinner,sun50i-a64-system-control"
- "allwinner,sun50i-h5-system-control"
- "allwinner,sun50i-h6-system-control", "allwinner,sun50i-a64-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 - reg : sram controller register offset + length
SRAM nodes SRAM nodes
@ -54,10 +56,17 @@ The valid sections compatible for H3 are:
The valid sections compatible for A64 are: The valid sections compatible for A64 are:
- allwinner,sun50i-a64-sram-c - 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: The valid sections compatible for H6 are:
- allwinner,sun50i-h6-sram-c, allwinner,sun50i-a64-sram-c - 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 Devices using SRAM sections
--------------------------- ---------------------------

View File

@ -2345,6 +2345,17 @@ static int __init _init_mpu_rt_base(struct omap_hwmod *oh, void *data,
return 0; 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 * _init - initialize internal data for the hwmod @oh
* @oh: struct omap_hwmod * * @oh: struct omap_hwmod *
@ -2392,12 +2403,12 @@ static int __init _init(struct omap_hwmod *oh, void *data)
} }
if (np) { if (np) {
if (of_find_property(np, "ti,no-reset-on-init", NULL)) struct device_node *child;
oh->flags |= HWMOD_INIT_NO_RESET;
if (of_find_property(np, "ti,no-idle-on-init", NULL)) parse_module_flags(oh, np);
oh->flags |= HWMOD_INIT_NO_IDLE; child = of_get_next_child(np, NULL);
if (of_find_property(np, "ti,no-idle", NULL)) if (child)
oh->flags |= HWMOD_NO_IDLE; parse_module_flags(oh, child);
} }
oh->_state = _HWMOD_STATE_INITIALIZED; oh->_state = _HWMOD_STATE_INITIALIZED;

View File

@ -150,8 +150,7 @@ static ssize_t gisb_arb_get_timeout(struct device *dev,
struct device_attribute *attr, struct device_attribute *attr,
char *buf) char *buf)
{ {
struct platform_device *pdev = to_platform_device(dev); struct brcmstb_gisb_arb_device *gdev = dev_get_drvdata(dev);
struct brcmstb_gisb_arb_device *gdev = platform_get_drvdata(pdev);
u32 timeout; u32 timeout;
mutex_lock(&gdev->lock); mutex_lock(&gdev->lock);
@ -165,8 +164,7 @@ static ssize_t gisb_arb_set_timeout(struct device *dev,
struct device_attribute *attr, struct device_attribute *attr,
const char *buf, size_t count) const char *buf, size_t count)
{ {
struct platform_device *pdev = to_platform_device(dev); struct brcmstb_gisb_arb_device *gdev = dev_get_drvdata(dev);
struct brcmstb_gisb_arb_device *gdev = platform_get_drvdata(pdev);
int val, ret; int val, ret;
ret = kstrtoint(buf, 10, &val); ret = kstrtoint(buf, 10, &val);
@ -418,8 +416,7 @@ static int __init brcmstb_gisb_arb_probe(struct platform_device *pdev)
#ifdef CONFIG_PM_SLEEP #ifdef CONFIG_PM_SLEEP
static int brcmstb_gisb_arb_suspend(struct device *dev) static int brcmstb_gisb_arb_suspend(struct device *dev)
{ {
struct platform_device *pdev = to_platform_device(dev); struct brcmstb_gisb_arb_device *gdev = dev_get_drvdata(dev);
struct brcmstb_gisb_arb_device *gdev = platform_get_drvdata(pdev);
gdev->saved_timeout = gisb_read(gdev, ARB_TIMER); 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) static int brcmstb_gisb_arb_resume_noirq(struct device *dev)
{ {
struct platform_device *pdev = to_platform_device(dev); struct brcmstb_gisb_arb_device *gdev = dev_get_drvdata(dev);
struct brcmstb_gisb_arb_device *gdev = platform_get_drvdata(pdev);
gisb_write(gdev, gdev->saved_timeout, ARB_TIMER); gisb_write(gdev, gdev->saved_timeout, ARB_TIMER);

View File

@ -91,6 +91,9 @@ struct sysc {
struct delayed_work idle_work; 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) void sysc_write(struct sysc *ddata, int offset, u32 value)
{ {
writel_relaxed(value, ddata->module_va + offset); writel_relaxed(value, ddata->module_va + offset);
@ -214,8 +217,13 @@ static int sysc_get_clocks(struct sysc *ddata)
if (!ddata->clocks) if (!ddata->clocks)
return -ENOMEM; return -ENOMEM;
for (i = 0; i < ddata->nr_clocks; i++) { for (i = 0; i < SYSC_MAX_CLOCKS; i++) {
error = sysc_get_one_clock(ddata, ddata->clock_roles[i]); const char *name = ddata->clock_roles[i];
if (!name)
continue;
error = sysc_get_one_clock(ddata, name);
if (error && error != -ENOENT) if (error && error != -ENOENT)
return error; 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?"); dev_warn(ddata->dev, "really a child ti,hwmods property?");
sysc_check_quirk_stdout(ddata, np); sysc_check_quirk_stdout(ddata, np);
sysc_parse_dts_quirks(ddata, np, true);
return 0; 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, 0x10, 0x14, 0x50060005, 0xfffffff0, 0),
SYSC_QUIRK("ocp2scp", 0, 0, -1, -1, 0x50060007, 0xffffffff, 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, 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, 0x40000100, 0xffffffff, 0),
SYSC_QUIRK("prcm", 0, 0, -1, -1, 0x00004102, 0xffffffff, 0), SYSC_QUIRK("prcm", 0, 0, -1, -1, 0x00004102, 0xffffffff, 0),
SYSC_QUIRK("prcm", 0, 0, -1, -1, 0x40000400, 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("rtc", 0, 0x74, 0x78, -1, 0x4eb01908, 0xffff00f0, 0),
SYSC_QUIRK("timer32k", 0, 0, 0x4, -1, 0x00000060, 0xffffffff, 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, 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, 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, SYSC_QUIRK("usb_otg_hs", 0, 0x400, 0x404, 0x408, 0x00000050,
0xffffffff, 0), 0xffffffff, 0),
SYSC_QUIRK("wdt", 0, 0, 0x10, 0x14, 0x502a0500, 0xfffff0f0, 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, }, .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; const struct property *prop;
int i, len, error; int i, len;
u32 val;
ddata->legacy_mode = of_get_property(np, "ti,hwmods", NULL);
for (i = 0; i < ARRAY_SIZE(sysc_dts_quirks); i++) { 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) if (!prop)
continue; continue;
ddata->cfg.quirks |= sysc_dts_quirks[i].mask; 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); error = of_property_read_u32(np, "ti,sysc-delay-us", &val);
if (!error) { if (!error) {
if (val > 255) { if (val > 255) {
@ -1498,6 +1524,16 @@ static const struct sysc_regbits sysc_regbits_omap4_mcasp = {
static const struct sysc_capabilities sysc_omap4_mcasp = { static const struct sysc_capabilities sysc_omap4_mcasp = {
.type = TI_SYSC_OMAP4_MCASP, .type = TI_SYSC_OMAP4_MCASP,
.regbits = &sysc_regbits_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-omap3-sham", .data = &sysc_omap3_sham, },
{ .compatible = "ti,sysc-omap-aes", .data = &sysc_omap3_aes, }, { .compatible = "ti,sysc-omap-aes", .data = &sysc_omap3_aes, },
{ .compatible = "ti,sysc-mcasp", .data = &sysc_omap4_mcasp, }, { .compatible = "ti,sysc-mcasp", .data = &sysc_omap4_mcasp, },
{ .compatible = "ti,sysc-dra7-mcasp", .data = &sysc_dra7_mcasp, },
{ .compatible = "ti,sysc-usb-host-fs", { .compatible = "ti,sysc-usb-host-fs",
.data = &sysc_omap4_usb_host_fs, }, .data = &sysc_omap4_usb_host_fs, },
{ .compatible = "ti,sysc-dra7-mcan", .data = &sysc_dra7_mcan, }, { .compatible = "ti,sysc-dra7-mcan", .data = &sysc_dra7_mcan, },

View File

@ -179,7 +179,7 @@ static unsigned int pxad_drcmr(unsigned int line)
return 0x1000 + line * 4; 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 * Debug fs
@ -1500,7 +1500,7 @@ static struct platform_driver pxad_driver = {
.remove = pxad_remove, .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_chan *c = to_pxad_chan(chan);
struct pxad_param *p = param; struct pxad_param *p = param;
@ -1513,7 +1513,6 @@ bool pxad_filter_fn(struct dma_chan *chan, void *param)
return true; return true;
} }
EXPORT_SYMBOL_GPL(pxad_filter_fn);
module_platform_driver(pxad_driver); module_platform_driver(pxad_driver);

View File

@ -9,3 +9,9 @@ config IMX_SCU
This driver manages the IPC interface between host CPU and the This driver manages the IPC interface between host CPU and the
SCU firmware running on M4. 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.

View File

@ -1,2 +1,3 @@
# SPDX-License-Identifier: GPL-2.0 # 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

View File

@ -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");

View File

@ -1,12 +1,9 @@
// SPDX-License-Identifier: GPL-2.0
/* /*
* Defines interfaces for interacting wtih the Raspberry Pi firmware's * Defines interfaces for interacting wtih the Raspberry Pi firmware's
* property channel. * property channel.
* *
* Copyright © 2015 Broadcom * Copyright © 2015 Broadcom
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/ */
#include <linux/dma-mapping.h> #include <linux/dma-mapping.h>
@ -14,6 +11,7 @@
#include <linux/module.h> #include <linux/module.h>
#include <linux/of_platform.h> #include <linux/of_platform.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/slab.h>
#include <soc/bcm2835/raspberrypi-firmware.h> #include <soc/bcm2835/raspberrypi-firmware.h>
#define MBOX_MSG(chan, data28) (((data28) & ~0xf) | ((chan) & 0xf)) #define MBOX_MSG(chan, data28) (((data28) & ~0xf) | ((chan) & 0xf))
@ -21,8 +19,6 @@
#define MBOX_DATA28(msg) ((msg) & ~0xf) #define MBOX_DATA28(msg) ((msg) & ~0xf)
#define MBOX_CHAN_PROPERTY 8 #define MBOX_CHAN_PROPERTY 8
#define MAX_RPI_FW_PROP_BUF_SIZE 32
static struct platform_device *rpi_hwmon; static struct platform_device *rpi_hwmon;
struct rpi_firmware { struct rpi_firmware {
@ -56,8 +52,12 @@ rpi_firmware_transaction(struct rpi_firmware *fw, u32 chan, u32 data)
reinit_completion(&fw->c); reinit_completion(&fw->c);
ret = mbox_send_message(fw->chan, &message); ret = mbox_send_message(fw->chan, &message);
if (ret >= 0) { if (ret >= 0) {
wait_for_completion(&fw->c); if (wait_for_completion_timeout(&fw->c, HZ)) {
ret = 0; ret = 0;
} else {
ret = -ETIMEDOUT;
WARN_ONCE(1, "Firmware transaction timeout");
}
} else { } else {
dev_err(fw->cl.dev, "mbox_send_message returned %d\n", ret); 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, int rpi_firmware_property(struct rpi_firmware *fw,
u32 tag, void *tag_data, size_t buf_size) u32 tag, void *tag_data, size_t buf_size)
{ {
/* Single tags are very small (generally 8 bytes), so the struct rpi_firmware_property_tag_header *header;
* 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;
int ret; int ret;
if (WARN_ON(buf_size > sizeof(data) - sizeof(*header))) /* Some mailboxes can use over 1k bytes. Rather than checking
return -EINVAL; * 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->tag = tag;
header->buf_size = buf_size; header->buf_size = buf_size;
header->req_resp_size = 0; header->req_resp_size = 0;
memcpy(data + sizeof(struct rpi_firmware_property_tag_header), memcpy(data + sizeof(*header), tag_data, buf_size);
tag_data, buf_size);
ret = rpi_firmware_property_list(fw, &data, buf_size + sizeof(*header)); ret = rpi_firmware_property_list(fw, data, buf_size + sizeof(*header));
memcpy(tag_data,
data + sizeof(struct rpi_firmware_property_tag_header), memcpy(tag_data, data + sizeof(*header), buf_size);
buf_size);
kfree(data);
return ret; return ret;
} }

View File

@ -379,33 +379,6 @@ static int create_debugfs_mirror(struct tegra_bpmp *bpmp, void *buf,
return err; 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) int tegra_bpmp_init_debugfs(struct tegra_bpmp *bpmp)
{ {
dma_addr_t phys; dma_addr_t phys;
@ -415,7 +388,7 @@ int tegra_bpmp_init_debugfs(struct tegra_bpmp *bpmp)
int ret; int ret;
struct dentry *root; struct dentry *root;
if (!mrq_is_supported(bpmp, MRQ_DEBUGFS)) if (!tegra_bpmp_mrq_is_supported(bpmp, MRQ_DEBUGFS))
return 0; return 0;
root = debugfs_create_dir("bpmp", NULL); root = debugfs_create_dir("bpmp", NULL);

View File

@ -28,6 +28,7 @@
#define MSG_ACK BIT(0) #define MSG_ACK BIT(0)
#define MSG_RING BIT(1) #define MSG_RING BIT(1)
#define TAG_SZ 32
static inline struct tegra_bpmp * static inline struct tegra_bpmp *
mbox_client_to_bpmp(struct mbox_client *client) mbox_client_to_bpmp(struct mbox_client *client)
@ -470,6 +471,31 @@ unlock:
} }
EXPORT_SYMBOL_GPL(tegra_bpmp_free_mrq); 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, static void tegra_bpmp_mrq_handle_ping(unsigned int mrq,
struct tegra_bpmp_channel *channel, struct tegra_bpmp_channel *channel,
void *data) void *data)
@ -521,8 +547,9 @@ static int tegra_bpmp_ping(struct tegra_bpmp *bpmp)
return err; return err;
} }
static int tegra_bpmp_get_firmware_tag(struct tegra_bpmp *bpmp, char *tag, /* deprecated version of tag query */
size_t size) static int tegra_bpmp_get_firmware_tag_old(struct tegra_bpmp *bpmp, char *tag,
size_t size)
{ {
struct mrq_query_tag_request request; struct mrq_query_tag_request request;
struct tegra_bpmp_message msg; struct tegra_bpmp_message msg;
@ -531,7 +558,10 @@ static int tegra_bpmp_get_firmware_tag(struct tegra_bpmp *bpmp, char *tag,
void *virt; void *virt;
int err; 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); GFP_KERNEL | GFP_DMA32);
if (!virt) if (!virt)
return -ENOMEM; return -ENOMEM;
@ -549,13 +579,44 @@ static int tegra_bpmp_get_firmware_tag(struct tegra_bpmp *bpmp, char *tag,
local_irq_restore(flags); local_irq_restore(flags);
if (err == 0) 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; 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) static void tegra_bpmp_channel_signal(struct tegra_bpmp_channel *channel)
{ {
unsigned long flags = channel->ob->flags; unsigned long flags = channel->ob->flags;
@ -664,7 +725,7 @@ static int tegra_bpmp_probe(struct platform_device *pdev)
{ {
struct tegra_bpmp *bpmp; struct tegra_bpmp *bpmp;
unsigned int i; unsigned int i;
char tag[32]; char tag[TAG_SZ];
size_t size; size_t size;
int err; int err;
@ -792,13 +853,13 @@ static int tegra_bpmp_probe(struct platform_device *pdev)
goto free_mrq; 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) { if (err < 0) {
dev_err(&pdev->dev, "failed to get firmware tag: %d\n", err); dev_err(&pdev->dev, "failed to get firmware tag: %d\n", err);
goto free_mrq; 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); platform_set_drvdata(pdev, bpmp);

View File

@ -927,26 +927,6 @@ static int a6xx_gmu_memory_probe(struct a6xx_gmu *gmu)
return ret; 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 */ /* Return the 'arc-level' for the given frequency */
static u32 a6xx_gmu_get_arc_level(struct device *dev, unsigned long freq) 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, static int a6xx_gmu_rpmh_arc_votes_init(struct device *dev, u32 *votes,
unsigned long *freqs, int freqs_count, unsigned long *freqs, int freqs_count, const char *id)
u16 *pri, int pri_count,
u16 *sec, int sec_count)
{ {
int i, j; 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 */ /* Construct a vote for each frequency */
for (i = 0; i < freqs_count; i++) { 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 a6xx_gpu *a6xx_gpu = container_of(gmu, struct a6xx_gpu, gmu);
struct adreno_gpu *adreno_gpu = &a6xx_gpu->base; struct adreno_gpu *adreno_gpu = &a6xx_gpu->base;
struct msm_gpu *gpu = &adreno_gpu->base; struct msm_gpu *gpu = &adreno_gpu->base;
u16 gx[16], cx[16], mx[16];
u32 gxcount, cxcount, mxcount;
int ret; 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 */ /* Build the GX votes */
ret = a6xx_gmu_rpmh_arc_votes_init(&gpu->pdev->dev, gmu->gx_arc_votes, ret = a6xx_gmu_rpmh_arc_votes_init(&gpu->pdev->dev, gmu->gx_arc_votes,
gmu->gpu_freqs, gmu->nr_gpu_freqs, gmu->gpu_freqs, gmu->nr_gpu_freqs, "gfx.lvl");
gx, gxcount, mx, mxcount);
/* Build the CX votes */ /* Build the CX votes */
ret |= a6xx_gmu_rpmh_arc_votes_init(gmu->dev, gmu->cx_arc_votes, ret |= a6xx_gmu_rpmh_arc_votes_init(gmu->dev, gmu->cx_arc_votes,
gmu->gmu_freqs, gmu->nr_gmu_freqs, gmu->gmu_freqs, gmu->nr_gmu_freqs, "cx.lvl");
cx, cxcount, mx, mxcount);
return ret; return ret;
} }

View File

@ -2061,7 +2061,7 @@ static int gpmc_probe_generic_child(struct platform_device *pdev,
* timings. * timings.
*/ */
name = gpmc_cs_get_name(cs); 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; goto no_timings;
ret = gpmc_cs_request(cs, resource_size(&res), &base); 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); dev_err(&pdev->dev, "cannot request GPMC CS %d\n", cs);
return ret; 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_settings_dt(child, &gpmc_s);
gpmc_read_timings_dt(child, &gpmc_t); gpmc_read_timings_dt(child, &gpmc_t);
@ -2114,7 +2114,7 @@ static int gpmc_probe_generic_child(struct platform_device *pdev,
goto err; 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 */ /* Warn about older DT blobs with no compatible property */
if (!of_property_read_bool(child, "compatible")) { if (!of_property_read_bool(child, "compatible")) {
dev_warn(&pdev->dev, 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 */ /* Warn about older DT blobs with no compatible property */
if (!of_property_read_bool(child, "compatible")) { if (!of_property_read_bool(child, "compatible")) {
dev_warn(&pdev->dev, dev_warn(&pdev->dev,

View File

@ -6,6 +6,16 @@ config TEGRA_MC
This driver supports the Memory Controller (MC) hardware found on This driver supports the Memory Controller (MC) hardware found on
NVIDIA Tegra SoCs. 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 config TEGRA124_EMC
bool "NVIDIA Tegra124 External Memory Controller driver" bool "NVIDIA Tegra124 External Memory Controller driver"
default y default y

View File

@ -10,5 +10,6 @@ tegra-mc-$(CONFIG_ARCH_TEGRA_210_SOC) += tegra210.o
obj-$(CONFIG_TEGRA_MC) += tegra-mc.o obj-$(CONFIG_TEGRA_MC) += tegra-mc.o
obj-$(CONFIG_TEGRA20_EMC) += tegra20-emc.o
obj-$(CONFIG_TEGRA124_EMC) += tegra124-emc.o obj-$(CONFIG_TEGRA124_EMC) += tegra124-emc.o
obj-$(CONFIG_ARCH_TEGRA_186_SOC) += tegra186.o obj-$(CONFIG_ARCH_TEGRA_186_SOC) += tegra186.o

View File

@ -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);

View File

@ -13,7 +13,7 @@ obj-$(CONFIG_ARCH_GEMINI) += gemini/
obj-$(CONFIG_ARCH_MXC) += imx/ obj-$(CONFIG_ARCH_MXC) += imx/
obj-$(CONFIG_SOC_XWAY) += lantiq/ obj-$(CONFIG_SOC_XWAY) += lantiq/
obj-y += mediatek/ obj-y += mediatek/
obj-$(CONFIG_ARCH_MESON) += amlogic/ obj-y += amlogic/
obj-y += qcom/ obj-y += qcom/
obj-y += renesas/ obj-y += renesas/
obj-$(CONFIG_ARCH_ROCKCHIP) += rockchip/ obj-$(CONFIG_ARCH_ROCKCHIP) += rockchip/

View File

@ -7,6 +7,15 @@ config MESON_CANVAS
help help
Say yes to support the canvas IP for Amlogic SoCs. 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 config MESON_GX_SOCINFO
bool "Amlogic Meson GX SoC Information driver" bool "Amlogic Meson GX SoC Information driver"
depends on ARCH_MESON || COMPILE_TEST depends on ARCH_MESON || COMPILE_TEST

View File

@ -1,4 +1,5 @@
obj-$(CONFIG_MESON_CANVAS) += meson-canvas.o 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_SOCINFO) += meson-gx-socinfo.o
obj-$(CONFIG_MESON_GX_PM_DOMAINS) += meson-gx-pwrc-vpu.o obj-$(CONFIG_MESON_GX_PM_DOMAINS) += meson-gx-pwrc-vpu.o
obj-$(CONFIG_MESON_MX_SOCINFO) += meson-mx-socinfo.o obj-$(CONFIG_MESON_MX_SOCINFO) += meson-mx-socinfo.o

View File

@ -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);

View File

@ -66,6 +66,8 @@ static const struct at91_soc __initconst socs[] = {
AT91_SOC(AT91SAM9XE128_CIDR_MATCH, 0, "at91sam9xe128", "at91sam9xe128"), AT91_SOC(AT91SAM9XE128_CIDR_MATCH, 0, "at91sam9xe128", "at91sam9xe128"),
AT91_SOC(AT91SAM9XE256_CIDR_MATCH, 0, "at91sam9xe256", "at91sam9xe256"), AT91_SOC(AT91SAM9XE256_CIDR_MATCH, 0, "at91sam9xe256", "at91sam9xe256"),
AT91_SOC(AT91SAM9XE512_CIDR_MATCH, 0, "at91sam9xe512", "at91sam9xe512"), AT91_SOC(AT91SAM9XE512_CIDR_MATCH, 0, "at91sam9xe512", "at91sam9xe512"),
AT91_SOC(SAM9X60_CIDR_MATCH, SAM9X60_EXID_MATCH,
"sam9x60", "sam9x60"),
#endif #endif
#ifdef CONFIG_SOC_SAMA5 #ifdef CONFIG_SOC_SAMA5
AT91_SOC(SAMA5D2_CIDR_MATCH, SAMA5D21CU_EXID_MATCH, AT91_SOC(SAMA5D2_CIDR_MATCH, SAMA5D21CU_EXID_MATCH,
@ -90,12 +92,20 @@ static const struct at91_soc __initconst socs[] = {
"sama5d27c 128MiB SiP", "sama5d2"), "sama5d27c 128MiB SiP", "sama5d2"),
AT91_SOC(SAMA5D2_CIDR_MATCH, SAMA5D27C_D5M_EXID_MATCH, AT91_SOC(SAMA5D2_CIDR_MATCH, SAMA5D27C_D5M_EXID_MATCH,
"sama5d27c 64MiB SiP", "sama5d2"), "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, AT91_SOC(SAMA5D2_CIDR_MATCH, SAMA5D28CU_EXID_MATCH,
"sama5d28", "sama5d2"), "sama5d28", "sama5d2"),
AT91_SOC(SAMA5D2_CIDR_MATCH, SAMA5D28CN_EXID_MATCH, AT91_SOC(SAMA5D2_CIDR_MATCH, SAMA5D28CN_EXID_MATCH,
"sama5d28", "sama5d2"), "sama5d28", "sama5d2"),
AT91_SOC(SAMA5D2_CIDR_MATCH, SAMA5D28C_D1G_EXID_MATCH, AT91_SOC(SAMA5D2_CIDR_MATCH, SAMA5D28C_D1G_EXID_MATCH,
"sama5d28c 128MiB SiP", "sama5d2"), "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, AT91_SOC(SAMA5D3_CIDR_MATCH, SAMA5D31_EXID_MATCH,
"sama5d31", "sama5d3"), "sama5d31", "sama5d3"),
AT91_SOC(SAMA5D3_CIDR_MATCH, SAMA5D33_EXID_MATCH, AT91_SOC(SAMA5D3_CIDR_MATCH, SAMA5D33_EXID_MATCH,

View File

@ -42,6 +42,7 @@ at91_soc_init(const struct at91_soc *socs);
#define AT91SAM9G45_CIDR_MATCH 0x019b05a0 #define AT91SAM9G45_CIDR_MATCH 0x019b05a0
#define AT91SAM9X5_CIDR_MATCH 0x019a05a0 #define AT91SAM9X5_CIDR_MATCH 0x019a05a0
#define AT91SAM9N12_CIDR_MATCH 0x019a07a0 #define AT91SAM9N12_CIDR_MATCH 0x019a07a0
#define SAM9X60_CIDR_MATCH 0x019b35a0
#define AT91SAM9M11_EXID_MATCH 0x00000001 #define AT91SAM9M11_EXID_MATCH 0x00000001
#define AT91SAM9M10_EXID_MATCH 0x00000002 #define AT91SAM9M10_EXID_MATCH 0x00000002
@ -58,6 +59,8 @@ at91_soc_init(const struct at91_soc *socs);
#define AT91SAM9N12_EXID_MATCH 0x00000006 #define AT91SAM9N12_EXID_MATCH 0x00000006
#define AT91SAM9CN11_EXID_MATCH 0x00000009 #define AT91SAM9CN11_EXID_MATCH 0x00000009
#define SAM9X60_EXID_MATCH 0x00000000
#define AT91SAM9XE128_CIDR_MATCH 0x329973a0 #define AT91SAM9XE128_CIDR_MATCH 0x329973a0
#define AT91SAM9XE256_CIDR_MATCH 0x329a93a0 #define AT91SAM9XE256_CIDR_MATCH 0x329a93a0
#define AT91SAM9XE512_CIDR_MATCH 0x329aa3a0 #define AT91SAM9XE512_CIDR_MATCH 0x329aa3a0
@ -73,9 +76,13 @@ at91_soc_init(const struct at91_soc *socs);
#define SAMA5D26CU_EXID_MATCH 0x00000012 #define SAMA5D26CU_EXID_MATCH 0x00000012
#define SAMA5D27C_D1G_EXID_MATCH 0x00000033 #define SAMA5D27C_D1G_EXID_MATCH 0x00000033
#define SAMA5D27C_D5M_EXID_MATCH 0x00000032 #define SAMA5D27C_D5M_EXID_MATCH 0x00000032
#define SAMA5D27C_LD1G_EXID_MATCH 0x00000061
#define SAMA5D27C_LD2G_EXID_MATCH 0x00000062
#define SAMA5D27CU_EXID_MATCH 0x00000011 #define SAMA5D27CU_EXID_MATCH 0x00000011
#define SAMA5D27CN_EXID_MATCH 0x00000021 #define SAMA5D27CN_EXID_MATCH 0x00000021
#define SAMA5D28C_D1G_EXID_MATCH 0x00000013 #define SAMA5D28C_D1G_EXID_MATCH 0x00000013
#define SAMA5D28C_LD1G_EXID_MATCH 0x00000071
#define SAMA5D28C_LD2G_EXID_MATCH 0x00000072
#define SAMA5D28CU_EXID_MATCH 0x00000010 #define SAMA5D28CU_EXID_MATCH 0x00000010
#define SAMA5D28CN_EXID_MATCH 0x00000020 #define SAMA5D28CN_EXID_MATCH 0x00000020

View File

@ -31,13 +31,17 @@ static const struct of_device_id brcmstb_machine_match[] = {
bool soc_is_brcmstb(void) bool soc_is_brcmstb(void)
{ {
const struct of_device_id *match;
struct device_node *root; struct device_node *root;
root = of_find_node_by_path("/"); root = of_find_node_by_path("/");
if (!root) if (!root)
return false; 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) u32 brcmstb_get_family_id(void)

View File

@ -404,7 +404,7 @@ noinline int brcmstb_pm_s3_finish(void)
{ {
struct brcmstb_s3_params *params = ctrl.s3_params; struct brcmstb_s3_params *params = ctrl.s3_params;
dma_addr_t params_pa = ctrl.s3_params_pa; 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; enum bsp_initiate_command cmd;
u32 flags; u32 flags;

View File

@ -1,8 +1,5 @@
// SPDX-License-Identifier: GPL-2.0
/* (C) 2015 Pengutronix, Alexander Aring <aar@pengutronix.de> /* (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: * Authors:
* Alexander Aring <aar@pengutronix.de> * Alexander Aring <aar@pengutronix.de>

View File

@ -1,8 +1,8 @@
menu "i.MX SoC drivers" menu "i.MX SoC drivers"
config IMX7_PM_DOMAINS config IMX_GPCV2_PM_DOMAINS
bool "i.MX7 PM domains" bool "i.MX GPCv2 PM domains"
depends on SOC_IMX7D || (COMPILE_TEST && OF) depends on SOC_IMX7D || SOC_IMX8MQ || (COMPILE_TEST && OF)
depends on PM depends on PM
select PM_GENERIC_DOMAINS select PM_GENERIC_DOMAINS
default y if SOC_IMX7D default y if SOC_IMX7D

View File

@ -1,2 +1,2 @@
obj-$(CONFIG_HAVE_IMX_GPC) += gpc.o obj-$(CONFIG_HAVE_IMX_GPC) += gpc.o
obj-$(CONFIG_IMX7_PM_DOMAINS) += gpcv2.o obj-$(CONFIG_IMX_GPCV2_PM_DOMAINS) += gpcv2.o

View File

@ -35,7 +35,7 @@
#define GPU_VPU_PUP_REQ BIT(1) #define GPU_VPU_PUP_REQ BIT(1)
#define GPU_VPU_PDN_REQ BIT(0) #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) #define PGC_DOMAIN_FLAG_NO_PD BIT(0)

View File

@ -14,23 +14,54 @@
#include <linux/regmap.h> #include <linux/regmap.h>
#include <linux/regulator/consumer.h> #include <linux/regulator/consumer.h>
#include <dt-bindings/power/imx7-power.h> #include <dt-bindings/power/imx7-power.h>
#include <dt-bindings/power/imx8mq-power.h>
#define GPC_LPCR_A_CORE_BSC 0x000 #define GPC_LPCR_A_CORE_BSC 0x000
#define GPC_PGC_CPU_MAPPING 0x0ec #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 IMX7_USB_HSIC_PHY_A_CORE_DOMAIN BIT(6)
#define USB_OTG1_PHY_A_CORE_DOMAIN BIT(4) #define IMX7_USB_OTG2_PHY_A_CORE_DOMAIN BIT(5)
#define PCIE_PHY_A_CORE_DOMAIN BIT(3) #define IMX7_USB_OTG1_PHY_A_CORE_DOMAIN BIT(4)
#define MIPI_PHY_A_CORE_DOMAIN BIT(2) #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_PUP_REQ 0x0f8
#define GPC_PU_PGC_SW_PDN_REQ 0x104 #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 IMX7_USB_HSIC_PHY_SW_Pxx_REQ BIT(4)
#define USB_OTG1_PHY_SW_Pxx_REQ BIT(2) #define IMX7_USB_OTG2_PHY_SW_Pxx_REQ BIT(3)
#define PCIE_PHY_SW_Pxx_REQ BIT(1) #define IMX7_USB_OTG1_PHY_SW_Pxx_REQ BIT(2)
#define MIPI_PHY_SW_Pxx_REQ BIT(0) #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 #define GPC_M4_PU_PDN_FLG 0x1bc
@ -40,9 +71,22 @@
* GPC_PGC memory map are incorrect, below offset * GPC_PGC memory map are incorrect, below offset
* values are from design RTL. * values are from design RTL.
*/ */
#define PGC_MIPI 16 #define IMX7_PGC_MIPI 16
#define PGC_PCIE 17 #define IMX7_PGC_PCIE 17
#define PGC_USB_HSIC 20 #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_CTRL(n) (0x800 + (n) * 0x40)
#define GPC_PGC_SR(n) (GPC_PGC_CTRL(n) + 0xc) #define GPC_PGC_SR(n) (GPC_PGC_CTRL(n) + 0xc)
@ -67,6 +111,7 @@ struct imx_pgc_domain {
struct imx_pgc_domain_data { struct imx_pgc_domain_data {
const struct imx_pgc_domain *domains; const struct imx_pgc_domain *domains;
size_t domains_num; 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, 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", .name = "mipi-phy",
}, },
.bits = { .bits = {
.pxx = MIPI_PHY_SW_Pxx_REQ, .pxx = IMX7_MIPI_PHY_SW_Pxx_REQ,
.map = MIPI_PHY_A_CORE_DOMAIN, .map = IMX7_MIPI_PHY_A_CORE_DOMAIN,
}, },
.voltage = 1000000, .voltage = 1000000,
.pgc = PGC_MIPI, .pgc = IMX7_PGC_MIPI,
}, },
[IMX7_POWER_DOMAIN_PCIE_PHY] = { [IMX7_POWER_DOMAIN_PCIE_PHY] = {
@ -178,11 +223,11 @@ static const struct imx_pgc_domain imx7_pgc_domains[] = {
.name = "pcie-phy", .name = "pcie-phy",
}, },
.bits = { .bits = {
.pxx = PCIE_PHY_SW_Pxx_REQ, .pxx = IMX7_PCIE_PHY_SW_Pxx_REQ,
.map = PCIE_PHY_A_CORE_DOMAIN, .map = IMX7_PCIE_PHY_A_CORE_DOMAIN,
}, },
.voltage = 1000000, .voltage = 1000000,
.pgc = PGC_PCIE, .pgc = IMX7_PGC_PCIE,
}, },
[IMX7_POWER_DOMAIN_USB_HSIC_PHY] = { [IMX7_POWER_DOMAIN_USB_HSIC_PHY] = {
@ -190,17 +235,195 @@ static const struct imx_pgc_domain imx7_pgc_domains[] = {
.name = "usb-hsic-phy", .name = "usb-hsic-phy",
}, },
.bits = { .bits = {
.pxx = USB_HSIC_PHY_SW_Pxx_REQ, .pxx = IMX7_USB_HSIC_PHY_SW_Pxx_REQ,
.map = USB_HSIC_PHY_A_CORE_DOMAIN, .map = IMX7_USB_HSIC_PHY_A_CORE_DOMAIN,
}, },
.voltage = 1200000, .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 = { static const struct imx_pgc_domain_data imx7_pgc_domain_data = {
.domains = imx7_pgc_domains, .domains = imx7_pgc_domains,
.domains_num = ARRAY_SIZE(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) 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"); dev_err(domain->dev, "Failed to get domain's regulator\n");
return PTR_ERR(domain->regulator); return PTR_ERR(domain->regulator);
} }
} else { } else if (domain->voltage) {
regulator_set_voltage(domain->regulator, regulator_set_voltage(domain->regulator,
domain->voltage, domain->voltage); 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 int imx_gpcv2_probe(struct platform_device *pdev)
{ {
static const struct imx_pgc_domain_data *domain_data; const struct imx_pgc_domain_data *domain_data =
static const struct regmap_range yes_ranges[] = { of_device_get_match_data(&pdev->dev);
regmap_reg_range(GPC_LPCR_A_CORE_BSC,
GPC_M4_PU_PDN_FLG), struct regmap_config regmap_config = {
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 = {
.reg_bits = 32, .reg_bits = 32,
.val_bits = 32, .val_bits = 32,
.reg_stride = 4, .reg_stride = 4,
.rd_table = &access_table, .rd_table = domain_data->reg_access_table,
.wr_table = &access_table, .wr_table = domain_data->reg_access_table,
.max_register = SZ_4K, .max_register = SZ_4K,
}; };
struct device *dev = &pdev->dev; struct device *dev = &pdev->dev;
@ -313,8 +524,6 @@ static int imx_gpcv2_probe(struct platform_device *pdev)
return ret; return ret;
} }
domain_data = of_device_get_match_data(&pdev->dev);
for_each_child_of_node(pgc_np, np) { for_each_child_of_node(pgc_np, np) {
struct platform_device *pd_pdev; struct platform_device *pd_pdev;
struct imx_pgc_domain *domain; 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[] = { static const struct of_device_id imx_gpcv2_dt_ids[] = {
{ .compatible = "fsl,imx7d-gpc", .data = &imx7_pgc_domain_data, }, { .compatible = "fsl,imx7d-gpc", .data = &imx7_pgc_domain_data, },
{ .compatible = "fsl,imx8mq-gpc", .data = &imx8m_pgc_domain_data, },
{ } { }
}; };

View File

@ -4,6 +4,18 @@
menu "MediaTek SoC drivers" menu "MediaTek SoC drivers"
depends on ARCH_MEDIATEK || COMPILE_TEST 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 config MTK_INFRACFG
bool "MediaTek INFRACFG Support" bool "MediaTek INFRACFG Support"
select REGMAP select REGMAP

View File

@ -1,3 +1,4 @@
obj-$(CONFIG_MTK_CMDQ) += mtk-cmdq-helper.o
obj-$(CONFIG_MTK_INFRACFG) += mtk-infracfg.o obj-$(CONFIG_MTK_INFRACFG) += mtk-infracfg.o
obj-$(CONFIG_MTK_PMIC_WRAP) += mtk-pmic-wrap.o obj-$(CONFIG_MTK_PMIC_WRAP) += mtk-pmic-wrap.o
obj-$(CONFIG_MTK_SCPSYS) += mtk-scpsys.o obj-$(CONFIG_MTK_SCPSYS) += mtk-scpsys.o

View File

@ -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");

View File

@ -75,11 +75,6 @@ config QCOM_QMI_HELPERS
tristate tristate
depends on ARCH_QCOM || COMPILE_TEST depends on ARCH_QCOM || COMPILE_TEST
depends on NET 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 config QCOM_RMTFS_MEM
tristate "Qualcomm Remote Filesystem memory driver" tristate "Qualcomm Remote Filesystem memory driver"

View File

@ -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 struct cmd_db_header *cmd_db_header;
static inline const void *rsc_to_entry_header(const struct rsc_hdr *hdr)
static inline void *rsc_to_entry_header(struct rsc_hdr *hdr)
{ {
u16 offset = le16_to_cpu(hdr->header_offset); 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 * 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 offset = le16_to_cpu(hdr->data_offset);
u16 loffset = le16_to_cpu(ent->offset); u16 loffset = le16_to_cpu(ent->offset);
@ -134,11 +133,11 @@ int cmd_db_ready(void)
} }
EXPORT_SYMBOL(cmd_db_ready); EXPORT_SYMBOL(cmd_db_ready);
static int cmd_db_get_header(const char *id, struct entry_header *eh, static int cmd_db_get_header(const char *id, const struct entry_header **eh,
struct rsc_hdr *rh) const struct rsc_hdr **rh)
{ {
struct rsc_hdr *rsc_hdr; const struct rsc_hdr *rsc_hdr;
struct entry_header *ent; const struct entry_header *ent;
int ret, i, j; int ret, i, j;
u8 query[8]; u8 query[8];
@ -146,9 +145,6 @@ static int cmd_db_get_header(const char *id, struct entry_header *eh,
if (ret) if (ret)
return ret; return ret;
if (!eh || !rh)
return -EINVAL;
/* Pad out query string to same length as in DB */ /* Pad out query string to same length as in DB */
strncpy(query, id, sizeof(query)); 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); ent = rsc_to_entry_header(rsc_hdr);
for (j = 0; j < le16_to_cpu(rsc_hdr->cnt); j++, ent++) { for (j = 0; j < le16_to_cpu(rsc_hdr->cnt); j++, ent++) {
if (memcmp(ent->id, query, sizeof(ent->id)) == 0) if (memcmp(ent->id, query, sizeof(ent->id)) == 0) {
break; if (eh)
} *eh = ent;
if (rh)
if (j < le16_to_cpu(rsc_hdr->cnt)) { *rh = rsc_hdr;
memcpy(eh, ent, sizeof(*ent)); return 0;
memcpy(rh, rsc_hdr, sizeof(*rh)); }
return 0;
} }
} }
@ -186,68 +181,39 @@ static int cmd_db_get_header(const char *id, struct entry_header *eh,
u32 cmd_db_read_addr(const char *id) u32 cmd_db_read_addr(const char *id)
{ {
int ret; int ret;
struct entry_header ent; const struct entry_header *ent;
struct rsc_hdr rsc_hdr;
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); EXPORT_SYMBOL(cmd_db_read_addr);
/** /**
* cmd_db_read_aux_data() - Query command db for aux data. * cmd_db_read_aux_data() - Query command db for aux data.
* *
* @id: Resource to retrieve AUX Data on. * @id: Resource to retrieve AUX Data on
* @data: Data buffer to copy returned aux data to. Returns size on NULL * @len: size of data buffer returned
* @len: Caller provides size of data buffer passed in.
* *
* 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; int ret;
struct entry_header ent; const struct entry_header *ent;
struct rsc_hdr rsc_hdr; const struct rsc_hdr *rsc_hdr;
u16 ent_len;
if (!data)
return -EINVAL;
ret = cmd_db_get_header(id, &ent, &rsc_hdr); ret = cmd_db_get_header(id, &ent, &rsc_hdr);
if (ret) if (ret)
return ret; return ERR_PTR(ret);
ent_len = le16_to_cpu(ent.len); if (len)
if (len < ent_len) *len = le16_to_cpu(ent->len);
return -EINVAL;
len = min_t(u16, ent_len, len); return rsc_offset(rsc_hdr, ent);
memcpy(data, rsc_offset(&rsc_hdr, &ent), len);
return len;
} }
EXPORT_SYMBOL(cmd_db_read_aux_data); 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 * cmd_db_read_slave_id - Get the slave ID for a given resource address
* *
@ -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) enum cmd_db_hw_type cmd_db_read_slave_id(const char *id)
{ {
int ret; int ret;
struct entry_header ent; const struct entry_header *ent;
struct rsc_hdr rsc_hdr;
u32 addr; u32 addr;
ret = cmd_db_get_header(id, &ent, &rsc_hdr); ret = cmd_db_get_header(id, &ent, NULL);
if (ret < 0) if (ret < 0)
return CMD_DB_HW_INVALID; 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; return (addr >> SLAVE_ID_SHIFT) & SLAVE_ID_MASK;
} }
EXPORT_SYMBOL(cmd_db_read_slave_id); EXPORT_SYMBOL(cmd_db_read_slave_id);

View File

@ -95,7 +95,8 @@ EXPORT_SYMBOL_GPL(llcc_slice_getd);
*/ */
void llcc_slice_putd(struct llcc_slice_desc *desc) 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); EXPORT_SYMBOL_GPL(llcc_slice_putd);
@ -142,6 +143,9 @@ int llcc_slice_activate(struct llcc_slice_desc *desc)
int ret; int ret;
u32 act_ctrl_val; u32 act_ctrl_val;
if (IS_ERR_OR_NULL(desc))
return -EINVAL;
mutex_lock(&drv_data->lock); mutex_lock(&drv_data->lock);
if (test_bit(desc->slice_id, drv_data->bitmap)) { if (test_bit(desc->slice_id, drv_data->bitmap)) {
mutex_unlock(&drv_data->lock); mutex_unlock(&drv_data->lock);
@ -176,6 +180,9 @@ int llcc_slice_deactivate(struct llcc_slice_desc *desc)
u32 act_ctrl_val; u32 act_ctrl_val;
int ret; int ret;
if (IS_ERR_OR_NULL(desc))
return -EINVAL;
mutex_lock(&drv_data->lock); mutex_lock(&drv_data->lock);
if (!test_bit(desc->slice_id, drv_data->bitmap)) { if (!test_bit(desc->slice_id, drv_data->bitmap)) {
mutex_unlock(&drv_data->lock); 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) int llcc_get_slice_id(struct llcc_slice_desc *desc)
{ {
if (IS_ERR_OR_NULL(desc))
return -EINVAL;
return desc->slice_id; return desc->slice_id;
} }
EXPORT_SYMBOL_GPL(llcc_get_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) size_t llcc_get_slice_size(struct llcc_slice_desc *desc)
{ {
if (IS_ERR_OR_NULL(desc))
return 0;
return desc->slice_size; return desc->slice_size;
} }
EXPORT_SYMBOL_GPL(llcc_get_slice_size); EXPORT_SYMBOL_GPL(llcc_get_slice_size);
@ -360,5 +373,5 @@ int qcom_llcc_probe(struct platform_device *pdev,
return ret; return ret;
} }
EXPORT_SYMBOL_GPL(qcom_llcc_probe); EXPORT_SYMBOL_GPL(qcom_llcc_probe);
MODULE_LICENSE("GPL v2"); MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("Qualcomm Last Level Cache Controller");

View File

@ -215,6 +215,16 @@ static void geni_se_io_init(void __iomem *base)
writel_relaxed(FORCE_DEFAULT, base + GENI_FORCE_DEFAULT_REG); 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 * geni_se_init() - Initialize the GENI serial engine
* @se: Pointer to the concerned 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; u32 val;
geni_se_irq_clear(se);
geni_se_io_init(se->base); geni_se_io_init(se->base);
geni_se_io_set_mode(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 proto = geni_se_read_proto(se);
u32 val; u32 val;
writel_relaxed(0, se->base + SE_GSI_EVENT_EN); geni_se_irq_clear(se);
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);
val = readl_relaxed(se->base + SE_GENI_M_IRQ_EN); val = readl_relaxed(se->base + SE_GENI_M_IRQ_EN);
if (proto != GENI_SE_UART) { if (proto != GENI_SE_UART) {
@ -277,12 +283,7 @@ static void geni_se_select_dma_mode(struct geni_se *se)
{ {
u32 val; u32 val;
writel_relaxed(0, se->base + SE_GSI_EVENT_EN); geni_se_irq_clear(se);
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);
val = readl_relaxed(se->base + SE_GENI_DMA_MODE_EN); val = readl_relaxed(se->base + SE_GENI_DMA_MODE_EN);
val |= GENI_DMA_MODE_EN; val |= GENI_DMA_MODE_EN;

View File

@ -318,7 +318,7 @@ int qmi_txn_init(struct qmi_handle *qmi, struct qmi_txn *txn,
txn->dest = c_struct; txn->dest = c_struct;
mutex_lock(&qmi->txn_lock); 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) if (ret < 0)
pr_err("failed to allocate transaction id\n"); pr_err("failed to allocate transaction id\n");

View File

@ -227,6 +227,7 @@ static const struct of_device_id qcom_smd_rpm_of_match[] = {
{ .compatible = "qcom,rpm-msm8974" }, { .compatible = "qcom,rpm-msm8974" },
{ .compatible = "qcom,rpm-msm8996" }, { .compatible = "qcom,rpm-msm8996" },
{ .compatible = "qcom,rpm-msm8998" }, { .compatible = "qcom,rpm-msm8998" },
{ .compatible = "qcom,rpm-qcs404" },
{} {}
}; };
MODULE_DEVICE_TABLE(of, qcom_smd_rpm_of_match); MODULE_DEVICE_TABLE(of, qcom_smd_rpm_of_match);

View File

@ -28,7 +28,6 @@ static const struct rcar_sysc_area r8a77965_areas[] __initconst = {
{ "a2vc1", 0x3c0, 1, R8A77965_PD_A2VC1, R8A77965_PD_A3VC }, { "a2vc1", 0x3c0, 1, R8A77965_PD_A2VC1, R8A77965_PD_A3VC },
{ "3dg-a", 0x100, 0, R8A77965_PD_3DG_A, R8A77965_PD_ALWAYS_ON }, { "3dg-a", 0x100, 0, R8A77965_PD_3DG_A, R8A77965_PD_ALWAYS_ON },
{ "3dg-b", 0x100, 1, R8A77965_PD_3DG_B, R8A77965_PD_3DG_A }, { "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 = { const struct rcar_sysc_info r8a77965_sysc_info __initconst = {

View File

@ -20,12 +20,11 @@ static const struct rcar_sysc_area r8a77970_areas[] __initconst = {
PD_CPU_NOCR }, PD_CPU_NOCR },
{ "ca53-cpu1", 0x200, 1, R8A77970_PD_CA53_CPU1, R8A77970_PD_CA53_SCU, { "ca53-cpu1", 0x200, 1, R8A77970_PD_CA53_CPU1, R8A77970_PD_CA53_SCU,
PD_CPU_NOCR }, PD_CPU_NOCR },
{ "cr7", 0x240, 0, R8A77970_PD_CR7, R8A77970_PD_ALWAYS_ON },
{ "a3ir", 0x180, 0, R8A77970_PD_A3IR, R8A77970_PD_ALWAYS_ON }, { "a3ir", 0x180, 0, R8A77970_PD_A3IR, R8A77970_PD_ALWAYS_ON },
{ "a2ir0", 0x400, 0, R8A77970_PD_A2IR0, R8A77970_PD_A3IR }, { "a2ir0", 0x400, 0, R8A77970_PD_A2IR0, R8A77970_PD_A3IR },
{ "a2ir1", 0x400, 1, R8A77970_PD_A2IR1, R8A77970_PD_A3IR }, { "a2ir1", 0x400, 1, R8A77970_PD_A2IR1, R8A77970_PD_A3IR },
{ "a2ir2", 0x400, 2, R8A77970_PD_A2IR2, R8A77970_PD_A3IR }, { "a2dp", 0x400, 2, R8A77970_PD_A2DP, R8A77970_PD_A3IR },
{ "a2ir3", 0x400, 3, R8A77970_PD_A2IR3, R8A77970_PD_A3IR }, { "a2cn", 0x400, 3, R8A77970_PD_A2CN, R8A77970_PD_A3IR },
{ "a2sc0", 0x400, 4, R8A77970_PD_A2SC0, R8A77970_PD_A3IR }, { "a2sc0", 0x400, 4, R8A77970_PD_A2SC0, R8A77970_PD_A3IR },
{ "a2sc1", 0x400, 5, R8A77970_PD_A2SC1, R8A77970_PD_A3IR }, { "a2sc1", 0x400, 5, R8A77970_PD_A2SC1, R8A77970_PD_A3IR },
}; };

View File

@ -38,12 +38,12 @@ static const struct rcar_sysc_area r8a77980_areas[] __initconst = {
{ "a2sc2", 0x400, 8, R8A77980_PD_A2SC2, R8A77980_PD_A3IR }, { "a2sc2", 0x400, 8, R8A77980_PD_A2SC2, R8A77980_PD_A3IR },
{ "a2sc3", 0x400, 9, R8A77980_PD_A2SC3, R8A77980_PD_A3IR }, { "a2sc3", 0x400, 9, R8A77980_PD_A2SC3, R8A77980_PD_A3IR },
{ "a2sc4", 0x400, 10, R8A77980_PD_A2SC4, R8A77980_PD_A3IR }, { "a2sc4", 0x400, 10, R8A77980_PD_A2SC4, R8A77980_PD_A3IR },
{ "a2pd0", 0x400, 11, R8A77980_PD_A2PD0, R8A77980_PD_A3IR }, { "a2dp0", 0x400, 11, R8A77980_PD_A2DP0, R8A77980_PD_A3IR },
{ "a2pd1", 0x400, 12, R8A77980_PD_A2PD1, R8A77980_PD_A3IR }, { "a2dp1", 0x400, 12, R8A77980_PD_A2DP1, R8A77980_PD_A3IR },
{ "a2cn", 0x400, 13, R8A77980_PD_A2CN, R8A77980_PD_A3IR }, { "a2cn", 0x400, 13, R8A77980_PD_A2CN, R8A77980_PD_A3IR },
{ "a3vip", 0x2c0, 0, R8A77980_PD_A3VIP, R8A77980_PD_ALWAYS_ON }, { "a3vip0", 0x2c0, 0, R8A77980_PD_A3VIP0, R8A77980_PD_ALWAYS_ON },
{ "a3vip1", 0x300, 0, R8A77980_PD_A3VIP1, R8A77980_PD_A3VIP }, { "a3vip1", 0x300, 0, R8A77980_PD_A3VIP1, R8A77980_PD_ALWAYS_ON },
{ "a3vip2", 0x280, 0, R8A77980_PD_A3VIP2, R8A77980_PD_A3VIP }, { "a3vip2", 0x280, 0, R8A77980_PD_A3VIP2, R8A77980_PD_ALWAYS_ON },
}; };
const struct rcar_sysc_info r8a77980_sysc_info __initconst = { const struct rcar_sysc_info r8a77980_sysc_info __initconst = {

View File

@ -28,19 +28,6 @@ static struct rcar_sysc_area r8a77990_areas[] __initdata = {
{ "3dg-b", 0x100, 1, R8A77990_PD_3DG_B, R8A77990_PD_3DG_A }, { "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 */ /* Fixups for R-Car E3 ES1.0 revision */
static const struct soc_device_attribute r8a77990[] __initconst = { static const struct soc_device_attribute r8a77990[] __initconst = {
{ .soc_id = "r8a77990", .revision = "ES1.0" }, { .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) static int __init r8a77990_sysc_init(void)
{ {
if (soc_device_match(r8a77990)) { if (soc_device_match(r8a77990)) {
rcar_sysc_fix_parent(r8a77990_areas, /* Fix incorrect 3DG hierarchy */
ARRAY_SIZE(r8a77990_areas), swap(r8a77990_areas[7], r8a77990_areas[8]);
R8A77990_PD_3DG_A, R8A77990_PD_3DG_B); r8a77990_areas[7].parent = R8A77990_PD_ALWAYS_ON;
rcar_sysc_fix_parent(r8a77990_areas, r8a77990_areas[8].parent = R8A77990_PD_3DG_B;
ARRAY_SIZE(r8a77990_areas),
R8A77990_PD_3DG_B, R8A77990_PD_ALWAYS_ON);
} }
return 0; return 0;

View File

@ -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); 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); iowrite32(isr_mask, rcar_sysc_base + SYSCISCR);
/* Submit power shutoff or resume request until it was accepted */ /* 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; 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) static bool rcar_sysc_power_is_off(const struct rcar_sysc_ch *sysc_ch)
{ {
unsigned int st; 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); struct rcar_sysc_pd *pd = to_rcar_pd(genpd);
pr_debug("%s: %s\n", __func__, genpd->name); 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) 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); struct rcar_sysc_pd *pd = to_rcar_pd(genpd);
pr_debug("%s: %s\n", __func__, genpd->name); 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; static bool has_cpg_mstp;
@ -252,7 +251,7 @@ static int __init rcar_sysc_pd_setup(struct rcar_sysc_pd *pd)
goto finalize; goto finalize;
} }
rcar_sysc_power_up(&pd->ch); rcar_sysc_power(&pd->ch, true);
finalize: finalize:
error = pm_genpd_init(genpd, gov, false); 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; const struct of_device_id *match;
struct rcar_pm_domains *domains; struct rcar_pm_domains *domains;
struct device_node *np; struct device_node *np;
u32 syscier, syscimr;
void __iomem *base; void __iomem *base;
unsigned int i; unsigned int i;
int error; int error;
@ -373,27 +371,6 @@ static int __init rcar_sysc_pd_init(void)
domains->onecell_data.num_domains = ARRAY_SIZE(domains->domains); domains->onecell_data.num_domains = ARRAY_SIZE(domains->domains);
rcar_sysc_onecell_data = &domains->onecell_data; 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++) { for (i = 0; i < info->num_areas; i++) {
const struct rcar_sysc_area *area = &info->areas[i]; const struct rcar_sysc_area *area = &info->areas[i];
struct rcar_sysc_pd *pd; struct rcar_sysc_pd *pd;
@ -421,22 +398,17 @@ static int __init rcar_sysc_pd_init(void)
goto out_put; goto out_put;
domains->domains[area->isr_bit] = &pd->genpd; domains->domains[area->isr_bit] = &pd->genpd;
}
/* if (area->parent < 0)
* 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)
continue; continue;
error = pm_genpd_add_subdomain(domains->domains[area->parent], error = pm_genpd_add_subdomain(domains->domains[area->parent],
domains->domains[area->isr_bit]); &pd->genpd);
if (error) if (error) {
pr_warn("Failed to add PM subdomain %s to parent %u\n", pr_warn("Failed to add PM subdomain %s to parent %u\n",
area->name, area->parent); area->name, area->parent);
goto out_put;
}
} }
error = of_genpd_add_provider_onecell(np, &domains->onecell_data); 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) if (!(pd->flags & PD_CPU) || pd->ch.chan_bit != idx)
continue; continue;
return on ? rcar_sysc_power_up(&pd->ch) return rcar_sysc_power(&pd->ch, on);
: rcar_sysc_power_down(&pd->ch);
} }
return -ENOENT; return -ENOENT;

View File

@ -21,7 +21,9 @@
#include <linux/mfd/syscon.h> #include <linux/mfd/syscon.h>
#include <dt-bindings/power/px30-power.h> #include <dt-bindings/power/px30-power.h>
#include <dt-bindings/power/rk3036-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/rk3128-power.h>
#include <dt-bindings/power/rk3188-power.h>
#include <dt-bindings/power/rk3228-power.h> #include <dt-bindings/power/rk3228-power.h>
#include <dt-bindings/power/rk3288-power.h> #include <dt-bindings/power/rk3288-power.h>
#include <dt-bindings/power/rk3328-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), [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[] = { static const struct rockchip_domain_info rk3128_pm_domains[] = {
[RK3128_PD_CORE] = DOMAIN_RK3288(0, 0, 4, false), [RK3128_PD_CORE] = DOMAIN_RK3288(0, 0, 4, false),
[RK3128_PD_MSCH] = DOMAIN_RK3288(-1, -1, 6, true), [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), [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[] = { static const struct rockchip_domain_info rk3228_pm_domains[] = {
[RK3228_PD_CORE] = DOMAIN_RK3036(0, 0, 16, true), [RK3228_PD_CORE] = DOMAIN_RK3036(0, 0, 16, true),
[RK3228_PD_MSCH] = DOMAIN_RK3036(1, 1, 17, 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, .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 = { static const struct rockchip_pmu_info rk3128_pmu = {
.pwr_offset = 0x04, .pwr_offset = 0x04,
.status_offset = 0x08, .status_offset = 0x08,
@ -857,6 +886,17 @@ static const struct rockchip_pmu_info rk3128_pmu = {
.domain_info = rk3128_pm_domains, .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 = { static const struct rockchip_pmu_info rk3228_pmu = {
.req_offset = 0x40c, .req_offset = 0x40c,
.idle_offset = 0x488, .idle_offset = 0x488,
@ -948,10 +988,18 @@ static const struct of_device_id rockchip_pm_domain_dt_match[] = {
.compatible = "rockchip,rk3036-power-controller", .compatible = "rockchip,rk3036-power-controller",
.data = (void *)&rk3036_pmu, .data = (void *)&rk3036_pmu,
}, },
{
.compatible = "rockchip,rk3066-power-controller",
.data = (void *)&rk3066_pmu,
},
{ {
.compatible = "rockchip,rk3128-power-controller", .compatible = "rockchip,rk3128-power-controller",
.data = (void *)&rk3128_pmu, .data = (void *)&rk3128_pmu,
}, },
{
.compatible = "rockchip,rk3188-power-controller",
.data = (void *)&rk3188_pmu,
},
{ {
.compatible = "rockchip,rk3228-power-controller", .compatible = "rockchip,rk3228-power-controller",
.data = (void *)&rk3228_pmu, .data = (void *)&rk3228_pmu,

View File

@ -155,17 +155,7 @@ static int sunxi_sram_show(struct seq_file *s, void *data)
return 0; return 0;
} }
static int sunxi_sram_open(struct inode *inode, struct file *file) DEFINE_SHOW_ATTRIBUTE(sunxi_sram);
{
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,
};
static inline struct sunxi_sram_desc *to_sram_desc(const struct sunxi_sram_data *data) 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 */ /* 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 = { static const struct sunxi_sramc_variant sun50i_a64_sramc_variant = {
.has_emac_clock = true, .has_emac_clock = true,
}; };
@ -379,7 +373,7 @@ static const struct of_device_id sunxi_sram_dt_match[] = {
}, },
{ {
.compatible = "allwinner,sun8i-h3-system-control", .compatible = "allwinner,sun8i-h3-system-control",
.data = &sun4i_a10_sramc_variant, .data = &sun8i_h3_sramc_variant,
}, },
{ {
.compatible = "allwinner,sun50i-a64-sram-controller", .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", .compatible = "allwinner,sun50i-a64-system-control",
.data = &sun50i_a64_sramc_variant, .data = &sun50i_a64_sramc_variant,
}, },
{
.compatible = "allwinner,sun50i-h5-system-control",
.data = &sun50i_a64_sramc_variant,
},
{ }, { },
}; };
MODULE_DEVICE_TABLE(of, sunxi_sram_dt_match); MODULE_DEVICE_TABLE(of, sunxi_sram_dt_match);

View File

@ -22,11 +22,15 @@ static const struct of_device_id tegra_machine_match[] = {
bool soc_is_tegra(void) bool soc_is_tegra(void)
{ {
const struct of_device_id *match;
struct device_node *root; struct device_node *root;
root = of_find_node_by_path("/"); root = of_find_node_by_path("/");
if (!root) if (!root)
return false; 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;
} }

View File

@ -2,6 +2,7 @@
* drivers/soc/tegra/pmc.c * drivers/soc/tegra/pmc.c
* *
* Copyright (c) 2010 Google, Inc * Copyright (c) 2010 Google, Inc
* Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved.
* *
* Author: * Author:
* Colin Cross <ccross@google.com> * Colin Cross <ccross@google.com>
@ -29,9 +30,12 @@
#include <linux/init.h> #include <linux/init.h>
#include <linux/io.h> #include <linux/io.h>
#include <linux/iopoll.h> #include <linux/iopoll.h>
#include <linux/irq.h>
#include <linux/irqdomain.h>
#include <linux/of.h> #include <linux/of.h>
#include <linux/of_address.h> #include <linux/of_address.h>
#include <linux/of_clk.h> #include <linux/of_clk.h>
#include <linux/of_irq.h>
#include <linux/of_platform.h> #include <linux/of_platform.h>
#include <linux/pinctrl/pinctrl.h> #include <linux/pinctrl/pinctrl.h>
#include <linux/pinctrl/pinconf.h> #include <linux/pinctrl/pinconf.h>
@ -48,7 +52,10 @@
#include <soc/tegra/fuse.h> #include <soc/tegra/fuse.h>
#include <soc/tegra/pmc.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/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 0x0
#define PMC_CNTRL_INTR_POLARITY BIT(17) /* inverts INTR polarity */ #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_SCRATCH_WRITE BIT(2)
#define PMC_SENSOR_CTRL_ENABLE_RST BIT(1) #define PMC_SENSOR_CTRL_ENABLE_RST BIT(1)
#define PMC_RST_STATUS 0x1b4
#define PMC_RST_STATUS_POR 0 #define PMC_RST_STATUS_POR 0
#define PMC_RST_STATUS_WATCHDOG 1 #define PMC_RST_STATUS_WATCHDOG 1
#define PMC_RST_STATUS_SENSOR 2 #define PMC_RST_STATUS_SENSOR 2
@ -126,6 +132,16 @@
#define GPU_RG_CNTRL 0x2d4 #define GPU_RG_CNTRL 0x2d4
/* Tegra186 and later */ /* 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 0x4f4
#define WAKE_AOWAKE_CTRL_INTR_POLARITY BIT(0) #define WAKE_AOWAKE_CTRL_INTR_POLARITY BIT(0)
@ -151,8 +167,45 @@ struct tegra_pmc_regs {
unsigned int dpd_status; unsigned int dpd_status;
unsigned int dpd2_req; unsigned int dpd2_req;
unsigned int dpd2_status; 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 { struct tegra_pmc_soc {
unsigned int num_powergates; unsigned int num_powergates;
const char *const *powergates; const char *const *powergates;
@ -175,6 +228,45 @@ struct tegra_pmc_soc {
void (*setup_irq_polarity)(struct tegra_pmc *pmc, void (*setup_irq_polarity)(struct tegra_pmc *pmc,
struct device_node *np, struct device_node *np,
bool invert); 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 mutex powergates_lock;
struct pinctrl_dev *pctl_dev; struct pinctrl_dev *pctl_dev;
struct irq_domain *domain;
struct irq_chip irq;
}; };
static struct tegra_pmc *pmc = &(struct tegra_pmc) { 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 tegra_powergate_is_powered(unsigned int id)
{ {
int status;
if (!tegra_powergate_is_valid(id)) if (!tegra_powergate_is_valid(id))
return -EINVAL; return -EINVAL;
mutex_lock(&pmc->powergates_lock); return tegra_powergate_state(id);
status = tegra_powergate_state(id);
mutex_unlock(&pmc->powergates_lock);
return status;
} }
/** /**
@ -715,17 +804,7 @@ static int powergate_show(struct seq_file *s, void *data)
return 0; return 0;
} }
static int powergate_open(struct inode *inode, struct file *file) DEFINE_SHOW_ATTRIBUTE(powergate);
{
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,
};
static int tegra_powergate_debugfs_init(void) 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; 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); err = pm_genpd_init(&pg->genpd, NULL, off);
if (err < 0) { if (err < 0) {
pr_err("failed to initialise PM domain %pOFn: %d\n", np, 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; 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) static int tegra_pmc_probe(struct platform_device *pdev)
{ {
void __iomem *base; void __iomem *base;
@ -1610,6 +1892,8 @@ static int tegra_pmc_probe(struct platform_device *pdev)
tegra_pmc_init_tsense_reset(pmc); tegra_pmc_init_tsense_reset(pmc);
tegra_pmc_reset_sysfs_init(pmc);
if (IS_ENABLED(CONFIG_DEBUG_FS)) { if (IS_ENABLED(CONFIG_DEBUG_FS)) {
err = tegra_powergate_debugfs_init(); err = tegra_powergate_debugfs_init();
if (err < 0) if (err < 0)
@ -1627,6 +1911,10 @@ static int tegra_pmc_probe(struct platform_device *pdev)
if (err) if (err)
goto cleanup_restart_handler; goto cleanup_restart_handler;
err = tegra_pmc_irq_init(pmc);
if (err < 0)
goto cleanup_restart_handler;
mutex_lock(&pmc->powergates_lock); mutex_lock(&pmc->powergates_lock);
iounmap(pmc->base); iounmap(pmc->base);
pmc->base = base; pmc->base = base;
@ -1676,6 +1964,11 @@ static const struct tegra_pmc_regs tegra20_pmc_regs = {
.dpd_status = 0x1bc, .dpd_status = 0x1bc,
.dpd2_req = 0x1c0, .dpd2_req = 0x1c0,
.dpd2_status = 0x1c4, .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) 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, .regs = &tegra20_pmc_regs,
.init = tegra20_pmc_init, .init = tegra20_pmc_init,
.setup_irq_polarity = tegra20_pmc_setup_irq_polarity, .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[] = { static const char * const tegra30_powergates[] = {
@ -1774,6 +2071,10 @@ static const struct tegra_pmc_soc tegra30_pmc_soc = {
.regs = &tegra20_pmc_regs, .regs = &tegra20_pmc_regs,
.init = tegra20_pmc_init, .init = tegra20_pmc_init,
.setup_irq_polarity = tegra20_pmc_setup_irq_polarity, .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[] = { static const char * const tegra114_powergates[] = {
@ -1819,6 +2120,10 @@ static const struct tegra_pmc_soc tegra114_pmc_soc = {
.regs = &tegra20_pmc_regs, .regs = &tegra20_pmc_regs,
.init = tegra20_pmc_init, .init = tegra20_pmc_init,
.setup_irq_polarity = tegra20_pmc_setup_irq_polarity, .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[] = { static const char * const tegra124_powergates[] = {
@ -1924,6 +2229,10 @@ static const struct tegra_pmc_soc tegra124_pmc_soc = {
.regs = &tegra20_pmc_regs, .regs = &tegra20_pmc_regs,
.init = tegra20_pmc_init, .init = tegra20_pmc_init,
.setup_irq_polarity = tegra20_pmc_setup_irq_polarity, .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[] = { static const char * const tegra210_powergates[] = {
@ -2025,6 +2334,10 @@ static const struct tegra_pmc_soc tegra210_pmc_soc = {
.regs = &tegra20_pmc_regs, .regs = &tegra20_pmc_regs,
.init = tegra20_pmc_init, .init = tegra20_pmc_init,
.setup_irq_polarity = tegra20_pmc_setup_irq_polarity, .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) \ #define TEGRA186_IO_PAD_TABLE(_pad) \
@ -2082,6 +2395,11 @@ static const struct tegra_pmc_regs tegra186_pmc_regs = {
.dpd_status = 0x78, .dpd_status = 0x78,
.dpd2_req = 0x7c, .dpd2_req = 0x7c,
.dpd2_status = 0x80, .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, 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); 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 = { static const struct tegra_pmc_soc tegra186_pmc_soc = {
.num_powergates = 0, .num_powergates = 0,
.powergates = NULL, .powergates = NULL,
@ -2134,10 +2457,87 @@ static const struct tegra_pmc_soc tegra186_pmc_soc = {
.regs = &tegra186_pmc_regs, .regs = &tegra186_pmc_regs,
.init = NULL, .init = NULL,
.setup_irq_polarity = tegra186_pmc_setup_irq_polarity, .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[] = { 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,tegra186-pmc", .data = &tegra186_pmc_soc },
{ .compatible = "nvidia,tegra210-pmc", .data = &tegra210_pmc_soc }, { .compatible = "nvidia,tegra210-pmc", .data = &tegra210_pmc_soc },
{ .compatible = "nvidia,tegra132-pmc", .data = &tegra124_pmc_soc }, { .compatible = "nvidia,tegra132-pmc", .data = &tegra124_pmc_soc },

View File

@ -57,6 +57,7 @@
static struct wkup_m3_ipc *m3_ipc_state; static struct wkup_m3_ipc *m3_ipc_state;
static const struct wkup_m3_wakeup_src wakeups[] = { static const struct wkup_m3_wakeup_src wakeups[] = {
{.irq_nr = 16, .src = "PRCM"},
{.irq_nr = 35, .src = "USB0_PHY"}, {.irq_nr = 35, .src = "USB0_PHY"},
{.irq_nr = 36, .src = "USB1_PHY"}, {.irq_nr = 36, .src = "USB1_PHY"},
{.irq_nr = 40, .src = "I2C0"}, {.irq_nr = 40, .src = "I2C0"},

View File

@ -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

View File

@ -16,13 +16,12 @@
#define R8A77970_PD_CA53_CPU0 5 #define R8A77970_PD_CA53_CPU0 5
#define R8A77970_PD_CA53_CPU1 6 #define R8A77970_PD_CA53_CPU1 6
#define R8A77970_PD_CR7 13
#define R8A77970_PD_CA53_SCU 21 #define R8A77970_PD_CA53_SCU 21
#define R8A77970_PD_A2IR0 23 #define R8A77970_PD_A2IR0 23
#define R8A77970_PD_A3IR 24 #define R8A77970_PD_A3IR 24
#define R8A77970_PD_A2IR1 27 #define R8A77970_PD_A2IR1 27
#define R8A77970_PD_A2IR2 28 #define R8A77970_PD_A2DP 28
#define R8A77970_PD_A2IR3 29 #define R8A77970_PD_A2CN 29
#define R8A77970_PD_A2SC0 30 #define R8A77970_PD_A2SC0 30
#define R8A77970_PD_A2SC1 31 #define R8A77970_PD_A2SC1 31

View File

@ -15,14 +15,14 @@
#define R8A77980_PD_A2SC2 0 #define R8A77980_PD_A2SC2 0
#define R8A77980_PD_A2SC3 1 #define R8A77980_PD_A2SC3 1
#define R8A77980_PD_A2SC4 2 #define R8A77980_PD_A2SC4 2
#define R8A77980_PD_A2PD0 3 #define R8A77980_PD_A2DP0 3
#define R8A77980_PD_A2PD1 4 #define R8A77980_PD_A2DP1 4
#define R8A77980_PD_CA53_CPU0 5 #define R8A77980_PD_CA53_CPU0 5
#define R8A77980_PD_CA53_CPU1 6 #define R8A77980_PD_CA53_CPU1 6
#define R8A77980_PD_CA53_CPU2 7 #define R8A77980_PD_CA53_CPU2 7
#define R8A77980_PD_CA53_CPU3 8 #define R8A77980_PD_CA53_CPU3 8
#define R8A77980_PD_A2CN 10 #define R8A77980_PD_A2CN 10
#define R8A77980_PD_A3VIP 11 #define R8A77980_PD_A3VIP0 11
#define R8A77980_PD_A2IR5 12 #define R8A77980_PD_A2IR5 12
#define R8A77980_PD_CR7 13 #define R8A77980_PD_CR7 13
#define R8A77980_PD_A2IR4 15 #define R8A77980_PD_A2IR4 15

View File

@ -1,9 +1,6 @@
/* SPDX-License-Identifier: GPL-2.0 */
/* /*
* Copyright © 2015 Broadcom * 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 #ifndef _DT_BINDINGS_ARM_BCM2835_RPI_POWER_H

View File

@ -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

View File

@ -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

View File

@ -23,15 +23,4 @@ struct pxad_param {
enum pxad_chan_prio prio; 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_ */ #endif /* _PXA_DMA_H_ */

View File

@ -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__ */

View File

@ -166,7 +166,7 @@ struct qmi_ops {
struct qmi_txn { struct qmi_txn {
struct qmi_handle *qmi; struct qmi_handle *qmi;
int id; u16 id;
struct mutex lock; struct mutex lock;
struct completion completion; struct completion completion;

View File

@ -1,9 +1,6 @@
/* SPDX-License-Identifier: GPL-2.0 */
/* /*
* Copyright © 2015 Broadcom * 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__ #ifndef __SOC_RASPBERRY_FIRMWARE_H__

View File

@ -18,9 +18,7 @@ enum cmd_db_hw_type {
#if IS_ENABLED(CONFIG_QCOM_COMMAND_DB) #if IS_ENABLED(CONFIG_QCOM_COMMAND_DB)
u32 cmd_db_read_addr(const char *resource_id); u32 cmd_db_read_addr(const char *resource_id);
int cmd_db_read_aux_data(const char *resource_id, u8 *data, size_t len); const void *cmd_db_read_aux_data(const char *resource_id, size_t *len);
size_t cmd_db_read_aux_data_len(const char *resource_id);
enum cmd_db_hw_type cmd_db_read_slave_id(const char *resource_id); 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) static inline u32 cmd_db_read_addr(const char *resource_id)
{ return 0; } { return 0; }
static inline int cmd_db_read_aux_data(const char *resource_id, u8 *data, static inline const void *cmd_db_read_aux_data(const char *resource_id, size_t *len)
size_t len) { return ERR_PTR(-ENODEV); }
{ return -ENODEV; }
static inline size_t cmd_db_read_aux_data_len(const char *resource_id)
{ return -ENODEV; }
static inline enum cmd_db_hw_type cmd_db_read_slave_id(const char *resource_id) static inline enum cmd_db_hw_type cmd_db_read_slave_id(const char *resource_id)
{ return -ENODEV; } { return -ENODEV; }

File diff suppressed because it is too large Load Diff

View File

@ -129,6 +129,7 @@ int tegra_bpmp_request_mrq(struct tegra_bpmp *bpmp, unsigned int mrq,
tegra_bpmp_mrq_handler_t handler, void *data); tegra_bpmp_mrq_handler_t handler, void *data);
void tegra_bpmp_free_mrq(struct tegra_bpmp *bpmp, unsigned int mrq, void tegra_bpmp_free_mrq(struct tegra_bpmp *bpmp, unsigned int mrq,
void *data); void *data);
bool tegra_bpmp_mrq_is_supported(struct tegra_bpmp *bpmp, unsigned int mrq);
#else #else
static inline struct tegra_bpmp *tegra_bpmp_get(struct device *dev) 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) unsigned int mrq, void *data)
{ {
} }
static inline bool tegra_bpmp_mrq_is_supported(struct tegra_bpmp *bpmp,
unsigned int mrq)
{
return false;
}
#endif #endif
#if IS_ENABLED(CONFIG_CLK_TEGRA_BPMP) #if IS_ENABLED(CONFIG_CLK_TEGRA_BPMP)

View File

@ -60,7 +60,6 @@ struct tegra_sku_info {
u32 tegra_read_straps(void); u32 tegra_read_straps(void);
u32 tegra_read_ram_code(void); u32 tegra_read_ram_code(void);
u32 tegra_read_chipid(void);
int tegra_fuse_readl(unsigned long offset, u32 *value); int tegra_fuse_readl(unsigned long offset, u32 *value);
extern struct tegra_sku_info tegra_sku_info; extern struct tegra_sku_info tegra_sku_info;

View File

@ -88,6 +88,10 @@ enum tegra_io_pad {
TEGRA_IO_PAD_CSID, TEGRA_IO_PAD_CSID,
TEGRA_IO_PAD_CSIE, TEGRA_IO_PAD_CSIE,
TEGRA_IO_PAD_CSIF, 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_DBG,
TEGRA_IO_PAD_DEBUG_NONAO, TEGRA_IO_PAD_DEBUG_NONAO,
TEGRA_IO_PAD_DMIC, TEGRA_IO_PAD_DMIC,
@ -100,10 +104,15 @@ enum tegra_io_pad {
TEGRA_IO_PAD_EDP, TEGRA_IO_PAD_EDP,
TEGRA_IO_PAD_EMMC, TEGRA_IO_PAD_EMMC,
TEGRA_IO_PAD_EMMC2, TEGRA_IO_PAD_EMMC2,
TEGRA_IO_PAD_EQOS,
TEGRA_IO_PAD_GPIO, TEGRA_IO_PAD_GPIO,
TEGRA_IO_PAD_GP_PWM2,
TEGRA_IO_PAD_GP_PWM3,
TEGRA_IO_PAD_HDMI, TEGRA_IO_PAD_HDMI,
TEGRA_IO_PAD_HDMI_DP0, TEGRA_IO_PAD_HDMI_DP0,
TEGRA_IO_PAD_HDMI_DP1, TEGRA_IO_PAD_HDMI_DP1,
TEGRA_IO_PAD_HDMI_DP2,
TEGRA_IO_PAD_HDMI_DP3,
TEGRA_IO_PAD_HSIC, TEGRA_IO_PAD_HSIC,
TEGRA_IO_PAD_HV, TEGRA_IO_PAD_HV,
TEGRA_IO_PAD_LVDS, TEGRA_IO_PAD_LVDS,
@ -113,8 +122,14 @@ enum tegra_io_pad {
TEGRA_IO_PAD_PEX_CLK_BIAS, TEGRA_IO_PAD_PEX_CLK_BIAS,
TEGRA_IO_PAD_PEX_CLK1, TEGRA_IO_PAD_PEX_CLK1,
TEGRA_IO_PAD_PEX_CLK2, TEGRA_IO_PAD_PEX_CLK2,
TEGRA_IO_PAD_PEX_CLK2_BIAS,
TEGRA_IO_PAD_PEX_CLK3, TEGRA_IO_PAD_PEX_CLK3,
TEGRA_IO_PAD_PEX_CNTRL, 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,
TEGRA_IO_PAD_SDMMC1_HV, TEGRA_IO_PAD_SDMMC1_HV,
TEGRA_IO_PAD_SDMMC2, TEGRA_IO_PAD_SDMMC2,
@ -122,10 +137,16 @@ enum tegra_io_pad {
TEGRA_IO_PAD_SDMMC3, TEGRA_IO_PAD_SDMMC3,
TEGRA_IO_PAD_SDMMC3_HV, TEGRA_IO_PAD_SDMMC3_HV,
TEGRA_IO_PAD_SDMMC4, 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,
TEGRA_IO_PAD_SPI_HV, TEGRA_IO_PAD_SPI_HV,
TEGRA_IO_PAD_SYS_DDC, TEGRA_IO_PAD_SYS_DDC,
TEGRA_IO_PAD_UART, TEGRA_IO_PAD_UART,
TEGRA_IO_PAD_UART4,
TEGRA_IO_PAD_UART5,
TEGRA_IO_PAD_UFS, TEGRA_IO_PAD_UFS,
TEGRA_IO_PAD_USB0, TEGRA_IO_PAD_USB0,
TEGRA_IO_PAD_USB1, TEGRA_IO_PAD_USB1,