ARM: SoC driver updates for v4.15

This branch contains platform-related driver updates for ARM and ARM64,
 these are the areas that bring the changes:
 
 New drivers:
  - Driver support for Renesas R-Car V3M (R8A77970)
  - Power management support for Amlogic GX
  - A new driver for the Tegra BPMP thermal sensor
  - A new bus driver for Technologic Systems NBUS
 
 Changes for subsystems that prefer to merge through arm-soc:
  - The usual updates for reset controller drivers from Philipp Zabel,
    with five added drivers for SoCs in the arc, meson, socfpa, uniphier
    and mediatek families.
  - Updates to the ARM SCPI and PSCI frameworks, from Sudeep Holla,
    Heiner Kallweit and Lorenzo Pieralisi.
 
 Changes specific to some ARM-based SoC
  - The Freescale/NXP DPAA QBMan drivers from PowerPC can now work
    on ARM as well.
  - Several changes for power management on Broadcom SoCs
  - Various improvements on Qualcomm, Broadcom, Amlogic, Atmel, Mediatek
  - Minor Cleanups for Samsung, TI OMAP SoCs
 -----BEGIN PGP SIGNATURE-----
 Version: GnuPG v1
 
 iQIcBAABAgAGBQJaDggbAAoJEGCrR//JCVInIeQQAN1MDyO1UaWiFYnbkVOgzFcj
 dqbFOc41DBE/90JoBWE8kR/rjyF83OqztiaYpx9viu2qMMBZVcOwxhCUthWK59c/
 IujYdw4zGevLscF+jdrLbXgk97nfaWebsHyTAF307WAdZVJxiVGGzQEcgm71d6Zp
 CXjLiUii4winHUMK9FLRY2st0HKAevXhuvZJVV432+sTg3p7fGVilYeGOL5G62WO
 zQfCisqzC5q677kGGyUlPRGlHWMPkllsTTnfXcmV/FUiGyVa3lUWY5sEu+wCl96O
 U1ffPENeNj/A/4fa1dbErtbiNnC2z/+jf+Dg7Cn8w/dPk4Suf0ppjP8RqIGyxmDl
 Wm/UxbwDClxaeF4GSaYh2yKgGRJMH5N87bJnZRINE5ccGiol8Ww/34bFG0xNnfyh
 jSAFAc318AFG62WD4lvqWc7LSpzOYxp/MNqIFXKN692St/MJLkx8/q0nTwY1qPY0
 3SELz9II3hz+3MfDRqtRi7hZpkgHgQ+UG7S5+Xhmqrl309GOEldCjPVJhhXxWoxK
 ZPtZOuyYvGhIC+YAnHaN6lUjADIdNJZHwbuXFImx85oKHVofoxHbcni5vk8Uu7z1
 sQNYOtdDGaPG/2u9RJdJlPg/jIgLKxxt/Xm9TYVawpZ5hFANhBTtIq5ExCRAil68
 j9sMOrpZ1DzCQyR7zN2v
 =qDhq
 -----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 Arnd Bergmann:
 "This branch contains platform-related driver updates for ARM and
  ARM64, these are the areas that bring the changes:

  New drivers:

   - driver support for Renesas R-Car V3M (R8A77970)

   - power management support for Amlogic GX

   - a new driver for the Tegra BPMP thermal sensor

   - a new bus driver for Technologic Systems NBUS

  Changes for subsystems that prefer to merge through arm-soc:

   - the usual updates for reset controller drivers from Philipp Zabel,
     with five added drivers for SoCs in the arc, meson, socfpa,
     uniphier and mediatek families

   - updates to the ARM SCPI and PSCI frameworks, from Sudeep Holla,
     Heiner Kallweit and Lorenzo Pieralisi

  Changes specific to some ARM-based SoC

   - the Freescale/NXP DPAA QBMan drivers from PowerPC can now work on
     ARM as well

   - several changes for power management on Broadcom SoCs

   - various improvements on Qualcomm, Broadcom, Amlogic, Atmel,
     Mediatek

   - minor Cleanups for Samsung, TI OMAP SoCs"

[ NOTE! This doesn't work without the previous ARM SoC device-tree pull,
  because the R8A77970 driver is missing a header file that came from
  that pull.

  The fact that this got merged afterwards only fixes it at this point,
  and bisection of that driver will fail if/when you walk into the
  history of that driver.           - Linus ]

* tag 'armsoc-drivers' of git://git.kernel.org/pub/scm/linux/kernel/git/arm/arm-soc: (96 commits)
  soc: amlogic: meson-gx-pwrc-vpu: fix power-off when powered by bootloader
  bus: add driver for the Technologic Systems NBUS
  memory: omap-gpmc: Remove deprecated gpmc_update_nand_reg()
  soc: qcom: remove unused label
  soc: amlogic: gx pm domain: add PM and OF dependencies
  drivers/firmware: psci_checker: Add missing destroy_timer_on_stack()
  dt-bindings: power: add amlogic meson power domain bindings
  soc: amlogic: add Meson GX VPU Domains driver
  soc: qcom: Remote filesystem memory driver
  dt-binding: soc: qcom: Add binding for rmtfs memory
  of: reserved_mem: Accessor for acquiring reserved_mem
  of/platform: Generalize /reserved-memory handling
  soc: mediatek: pwrap: fix fatal compiler error
  soc: mediatek: pwrap: fix compiler errors
  arm64: mediatek: cleanup message for platform selection
  soc: Allow test-building of MediaTek drivers
  soc: mediatek: place Kconfig for all SoC drivers under menu
  soc: mediatek: pwrap: add support for MT7622 SoC
  soc: mediatek: pwrap: add common way for setup CS timing extenstion
  soc: mediatek: pwrap: add MediaTek MT6380 as one slave of pwrap
  ..
This commit is contained in:
Linus Torvalds 2017-11-16 16:05:01 -08:00
commit cf9b0772f2
110 changed files with 6933 additions and 996 deletions

View file

@ -11,7 +11,7 @@ obj-$(CONFIG_MACH_DOVE) += dove/
obj-y += fsl/
obj-$(CONFIG_ARCH_MXC) += imx/
obj-$(CONFIG_SOC_XWAY) += lantiq/
obj-$(CONFIG_ARCH_MEDIATEK) += mediatek/
obj-y += mediatek/
obj-$(CONFIG_ARCH_MESON) += amlogic/
obj-$(CONFIG_ARCH_QCOM) += qcom/
obj-y += renesas/

View file

@ -9,4 +9,25 @@ config MESON_GX_SOCINFO
Say yes to support decoding of Amlogic Meson GX SoC family
information about the type, package and version.
config MESON_GX_PM_DOMAINS
bool "Amlogic Meson GX Power Domains driver"
depends on ARCH_MESON || COMPILE_TEST
depends on PM && OF
default ARCH_MESON
select PM_GENERIC_DOMAINS
select PM_GENERIC_DOMAINS_OF
help
Say yes to expose Amlogic Meson GX Power Domains as
Generic Power Domains.
config MESON_MX_SOCINFO
bool "Amlogic Meson MX SoC Information driver"
depends on ARCH_MESON || COMPILE_TEST
default ARCH_MESON
select SOC_BUS
help
Say yes to support decoding of Amlogic Meson6, Meson8,
Meson8b and Meson8m2 SoC family information about the type
and version.
endmenu

View file

@ -1 +1,3 @@
obj-$(CONFIG_MESON_GX_SOCINFO) += meson-gx-socinfo.o
obj-$(CONFIG_MESON_GX_PM_DOMAINS) += meson-gx-pwrc-vpu.o
obj-$(CONFIG_MESON_MX_SOCINFO) += meson-mx-socinfo.o

View file

@ -0,0 +1,243 @@
/*
* Copyright (c) 2017 BayLibre, SAS
* Author: Neil Armstrong <narmstrong@baylibre.com>
*
* SPDX-License-Identifier: GPL-2.0+
*/
#include <linux/of_address.h>
#include <linux/platform_device.h>
#include <linux/pm_domain.h>
#include <linux/bitfield.h>
#include <linux/regmap.h>
#include <linux/mfd/syscon.h>
#include <linux/reset.h>
#include <linux/clk.h>
/* AO Offsets */
#define AO_RTI_GEN_PWR_SLEEP0 (0x3a << 2)
#define GEN_PWR_VPU_HDMI BIT(8)
#define GEN_PWR_VPU_HDMI_ISO BIT(9)
/* HHI Offsets */
#define HHI_MEM_PD_REG0 (0x40 << 2)
#define HHI_VPU_MEM_PD_REG0 (0x41 << 2)
#define HHI_VPU_MEM_PD_REG1 (0x42 << 2)
struct meson_gx_pwrc_vpu {
struct generic_pm_domain genpd;
struct regmap *regmap_ao;
struct regmap *regmap_hhi;
struct reset_control *rstc;
struct clk *vpu_clk;
struct clk *vapb_clk;
};
static inline
struct meson_gx_pwrc_vpu *genpd_to_pd(struct generic_pm_domain *d)
{
return container_of(d, struct meson_gx_pwrc_vpu, genpd);
}
static int meson_gx_pwrc_vpu_power_off(struct generic_pm_domain *genpd)
{
struct meson_gx_pwrc_vpu *pd = genpd_to_pd(genpd);
int i;
regmap_update_bits(pd->regmap_ao, AO_RTI_GEN_PWR_SLEEP0,
GEN_PWR_VPU_HDMI_ISO, GEN_PWR_VPU_HDMI_ISO);
udelay(20);
/* Power Down Memories */
for (i = 0; i < 32; i += 2) {
regmap_update_bits(pd->regmap_hhi, HHI_VPU_MEM_PD_REG0,
0x2 << i, 0x3 << i);
udelay(5);
}
for (i = 0; i < 32; i += 2) {
regmap_update_bits(pd->regmap_hhi, HHI_VPU_MEM_PD_REG1,
0x2 << i, 0x3 << i);
udelay(5);
}
for (i = 8; i < 16; i++) {
regmap_update_bits(pd->regmap_hhi, HHI_MEM_PD_REG0,
BIT(i), BIT(i));
udelay(5);
}
udelay(20);
regmap_update_bits(pd->regmap_ao, AO_RTI_GEN_PWR_SLEEP0,
GEN_PWR_VPU_HDMI, GEN_PWR_VPU_HDMI);
msleep(20);
clk_disable_unprepare(pd->vpu_clk);
clk_disable_unprepare(pd->vapb_clk);
return 0;
}
static int meson_gx_pwrc_vpu_setup_clk(struct meson_gx_pwrc_vpu *pd)
{
int ret;
ret = clk_prepare_enable(pd->vpu_clk);
if (ret)
return ret;
ret = clk_prepare_enable(pd->vapb_clk);
if (ret)
clk_disable_unprepare(pd->vpu_clk);
return ret;
}
static int meson_gx_pwrc_vpu_power_on(struct generic_pm_domain *genpd)
{
struct meson_gx_pwrc_vpu *pd = genpd_to_pd(genpd);
int ret;
int i;
regmap_update_bits(pd->regmap_ao, AO_RTI_GEN_PWR_SLEEP0,
GEN_PWR_VPU_HDMI, 0);
udelay(20);
/* Power Up Memories */
for (i = 0; i < 32; i += 2) {
regmap_update_bits(pd->regmap_hhi, HHI_VPU_MEM_PD_REG0,
0x2 << i, 0);
udelay(5);
}
for (i = 0; i < 32; i += 2) {
regmap_update_bits(pd->regmap_hhi, HHI_VPU_MEM_PD_REG1,
0x2 << i, 0);
udelay(5);
}
for (i = 8; i < 16; i++) {
regmap_update_bits(pd->regmap_hhi, HHI_MEM_PD_REG0,
BIT(i), 0);
udelay(5);
}
udelay(20);
ret = reset_control_assert(pd->rstc);
if (ret)
return ret;
regmap_update_bits(pd->regmap_ao, AO_RTI_GEN_PWR_SLEEP0,
GEN_PWR_VPU_HDMI_ISO, 0);
ret = reset_control_deassert(pd->rstc);
if (ret)
return ret;
ret = meson_gx_pwrc_vpu_setup_clk(pd);
if (ret)
return ret;
return 0;
}
static bool meson_gx_pwrc_vpu_get_power(struct meson_gx_pwrc_vpu *pd)
{
u32 reg;
regmap_read(pd->regmap_ao, AO_RTI_GEN_PWR_SLEEP0, &reg);
return (reg & GEN_PWR_VPU_HDMI);
}
static struct meson_gx_pwrc_vpu vpu_hdmi_pd = {
.genpd = {
.name = "vpu_hdmi",
.power_off = meson_gx_pwrc_vpu_power_off,
.power_on = meson_gx_pwrc_vpu_power_on,
},
};
static int meson_gx_pwrc_vpu_probe(struct platform_device *pdev)
{
struct regmap *regmap_ao, *regmap_hhi;
struct reset_control *rstc;
struct clk *vpu_clk;
struct clk *vapb_clk;
bool powered_off;
int ret;
regmap_ao = syscon_node_to_regmap(of_get_parent(pdev->dev.of_node));
if (IS_ERR(regmap_ao)) {
dev_err(&pdev->dev, "failed to get regmap\n");
return PTR_ERR(regmap_ao);
}
regmap_hhi = syscon_regmap_lookup_by_phandle(pdev->dev.of_node,
"amlogic,hhi-sysctrl");
if (IS_ERR(regmap_hhi)) {
dev_err(&pdev->dev, "failed to get HHI regmap\n");
return PTR_ERR(regmap_hhi);
}
rstc = devm_reset_control_array_get(&pdev->dev, false, false);
if (IS_ERR(rstc)) {
dev_err(&pdev->dev, "failed to get reset lines\n");
return PTR_ERR(rstc);
}
vpu_clk = devm_clk_get(&pdev->dev, "vpu");
if (IS_ERR(vpu_clk)) {
dev_err(&pdev->dev, "vpu clock request failed\n");
return PTR_ERR(vpu_clk);
}
vapb_clk = devm_clk_get(&pdev->dev, "vapb");
if (IS_ERR(vapb_clk)) {
dev_err(&pdev->dev, "vapb clock request failed\n");
return PTR_ERR(vapb_clk);
}
vpu_hdmi_pd.regmap_ao = regmap_ao;
vpu_hdmi_pd.regmap_hhi = regmap_hhi;
vpu_hdmi_pd.rstc = rstc;
vpu_hdmi_pd.vpu_clk = vpu_clk;
vpu_hdmi_pd.vapb_clk = vapb_clk;
powered_off = meson_gx_pwrc_vpu_get_power(&vpu_hdmi_pd);
/* If already powered, sync the clock states */
if (!powered_off) {
ret = meson_gx_pwrc_vpu_setup_clk(&vpu_hdmi_pd);
if (ret)
return ret;
}
pm_genpd_init(&vpu_hdmi_pd.genpd, &pm_domain_always_on_gov,
powered_off);
return of_genpd_add_provider_simple(pdev->dev.of_node,
&vpu_hdmi_pd.genpd);
}
static void meson_gx_pwrc_vpu_shutdown(struct platform_device *pdev)
{
meson_gx_pwrc_vpu_power_off(&vpu_hdmi_pd.genpd);
}
static const struct of_device_id meson_gx_pwrc_vpu_match_table[] = {
{ .compatible = "amlogic,meson-gx-pwrc-vpu" },
{ /* sentinel */ }
};
static struct platform_driver meson_gx_pwrc_vpu_driver = {
.probe = meson_gx_pwrc_vpu_probe,
.shutdown = meson_gx_pwrc_vpu_shutdown,
.driver = {
.name = "meson_gx_pwrc_vpu",
.of_match_table = meson_gx_pwrc_vpu_match_table,
},
};
builtin_platform_driver(meson_gx_pwrc_vpu_driver);

View file

@ -0,0 +1,175 @@
/*
* Copyright (c) 2017 Martin Blumenstingl <martin.blumenstingl@googlemail.com>
*
* SPDX-License-Identifier: GPL-2.0+
*/
#include <linux/io.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_platform.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/sys_soc.h>
#include <linux/bitfield.h>
#include <linux/regmap.h>
#include <linux/mfd/syscon.h>
#define MESON_SOCINFO_MAJOR_VER_MESON6 0x16
#define MESON_SOCINFO_MAJOR_VER_MESON8 0x19
#define MESON_SOCINFO_MAJOR_VER_MESON8B 0x1b
#define MESON_MX_ASSIST_HW_REV 0x14c
#define MESON_MX_ANALOG_TOP_METAL_REVISION 0x0
#define MESON_MX_BOOTROM_MISC_VER 0x4
static const char *meson_mx_socinfo_revision(unsigned int major_ver,
unsigned int misc_ver,
unsigned int metal_rev)
{
unsigned int minor_ver;
switch (major_ver) {
case MESON_SOCINFO_MAJOR_VER_MESON6:
minor_ver = 0xa;
break;
case MESON_SOCINFO_MAJOR_VER_MESON8:
if (metal_rev == 0x11111112)
major_ver = 0x1d;
if (metal_rev == 0x11111111 || metal_rev == 0x11111112)
minor_ver = 0xa;
else if (metal_rev == 0x11111113)
minor_ver = 0xb;
else if (metal_rev == 0x11111133)
minor_ver = 0xc;
else
minor_ver = 0xd;
break;
case MESON_SOCINFO_MAJOR_VER_MESON8B:
if (metal_rev == 0x11111111)
minor_ver = 0xa;
else
minor_ver = 0xb;
break;
default:
minor_ver = 0x0;
break;
}
return kasprintf(GFP_KERNEL, "Rev%X (%x - 0:%X)", minor_ver, major_ver,
misc_ver);
}
static const char *meson_mx_socinfo_soc_id(unsigned int major_ver,
unsigned int metal_rev)
{
const char *soc_id;
switch (major_ver) {
case MESON_SOCINFO_MAJOR_VER_MESON6:
soc_id = "Meson6 (AML8726-MX)";
break;
case MESON_SOCINFO_MAJOR_VER_MESON8:
if (metal_rev == 0x11111112)
soc_id = "Meson8m2 (S812)";
else
soc_id = "Meson8 (S802)";
break;
case MESON_SOCINFO_MAJOR_VER_MESON8B:
soc_id = "Meson8b (S805)";
break;
default:
soc_id = "Unknown";
break;
}
return kstrdup_const(soc_id, GFP_KERNEL);
}
static const struct of_device_id meson_mx_socinfo_analog_top_ids[] = {
{ .compatible = "amlogic,meson8-analog-top", },
{ .compatible = "amlogic,meson8b-analog-top", },
{ /* sentinel */ }
};
int __init meson_mx_socinfo_init(void)
{
struct soc_device_attribute *soc_dev_attr;
struct soc_device *soc_dev;
struct device_node *np;
struct regmap *assist_regmap, *bootrom_regmap, *analog_top_regmap;
unsigned int major_ver, misc_ver, metal_rev = 0;
int ret;
assist_regmap =
syscon_regmap_lookup_by_compatible("amlogic,meson-mx-assist");
if (IS_ERR(assist_regmap))
return PTR_ERR(assist_regmap);
bootrom_regmap =
syscon_regmap_lookup_by_compatible("amlogic,meson-mx-bootrom");
if (IS_ERR(bootrom_regmap))
return PTR_ERR(bootrom_regmap);
np = of_find_matching_node(NULL, meson_mx_socinfo_analog_top_ids);
if (np) {
analog_top_regmap = syscon_node_to_regmap(np);
if (IS_ERR(analog_top_regmap))
return PTR_ERR(analog_top_regmap);
ret = regmap_read(analog_top_regmap,
MESON_MX_ANALOG_TOP_METAL_REVISION,
&metal_rev);
if (ret)
return ret;
}
ret = regmap_read(assist_regmap, MESON_MX_ASSIST_HW_REV, &major_ver);
if (ret < 0)
return ret;
ret = regmap_read(bootrom_regmap, MESON_MX_BOOTROM_MISC_VER,
&misc_ver);
if (ret < 0)
return ret;
soc_dev_attr = kzalloc(sizeof(*soc_dev_attr), GFP_KERNEL);
if (!soc_dev_attr)
return -ENODEV;
soc_dev_attr->family = "Amlogic Meson";
np = of_find_node_by_path("/");
of_property_read_string(np, "model", &soc_dev_attr->machine);
of_node_put(np);
soc_dev_attr->revision = meson_mx_socinfo_revision(major_ver, misc_ver,
metal_rev);
soc_dev_attr->soc_id = meson_mx_socinfo_soc_id(major_ver, metal_rev);
soc_dev = soc_device_register(soc_dev_attr);
if (IS_ERR(soc_dev)) {
kfree_const(soc_dev_attr->revision);
kfree_const(soc_dev_attr->soc_id);
kfree(soc_dev_attr);
return PTR_ERR(soc_dev);
}
dev_info(soc_device_to_device(soc_dev), "Amlogic %s %s detected\n",
soc_dev_attr->soc_id, soc_dev_attr->revision);
return 0;
}
device_initcall(meson_mx_socinfo_init);

View file

@ -72,6 +72,8 @@ static const struct at91_soc __initconst socs[] = {
"sama5d21", "sama5d2"),
AT91_SOC(SAMA5D2_CIDR_MATCH, SAMA5D22CU_EXID_MATCH,
"sama5d22", "sama5d2"),
AT91_SOC(SAMA5D2_CIDR_MATCH, SAMA5D225C_D1M_EXID_MATCH,
"sama5d225c 16MiB SiP", "sama5d2"),
AT91_SOC(SAMA5D2_CIDR_MATCH, SAMA5D23CU_EXID_MATCH,
"sama5d23", "sama5d2"),
AT91_SOC(SAMA5D2_CIDR_MATCH, SAMA5D24CX_EXID_MATCH,
@ -84,10 +86,16 @@ static const struct at91_soc __initconst socs[] = {
"sama5d27", "sama5d2"),
AT91_SOC(SAMA5D2_CIDR_MATCH, SAMA5D27CN_EXID_MATCH,
"sama5d27", "sama5d2"),
AT91_SOC(SAMA5D2_CIDR_MATCH, SAMA5D27C_D1G_EXID_MATCH,
"sama5d27c 128MiB SiP", "sama5d2"),
AT91_SOC(SAMA5D2_CIDR_MATCH, SAMA5D27C_D5M_EXID_MATCH,
"sama5d27c 64MiB SiP", "sama5d2"),
AT91_SOC(SAMA5D2_CIDR_MATCH, SAMA5D28CU_EXID_MATCH,
"sama5d28", "sama5d2"),
AT91_SOC(SAMA5D2_CIDR_MATCH, SAMA5D28CN_EXID_MATCH,
"sama5d28", "sama5d2"),
AT91_SOC(SAMA5D2_CIDR_MATCH, SAMA5D28C_D1G_EXID_MATCH,
"sama5d28c 128MiB SiP", "sama5d2"),
AT91_SOC(SAMA5D3_CIDR_MATCH, SAMA5D31_EXID_MATCH,
"sama5d31", "sama5d3"),
AT91_SOC(SAMA5D3_CIDR_MATCH, SAMA5D33_EXID_MATCH,

View file

@ -64,14 +64,18 @@ at91_soc_init(const struct at91_soc *socs);
#define SAMA5D2_CIDR_MATCH 0x0a5c08c0
#define SAMA5D21CU_EXID_MATCH 0x0000005a
#define SAMA5D225C_D1M_EXID_MATCH 0x00000053
#define SAMA5D22CU_EXID_MATCH 0x00000059
#define SAMA5D22CN_EXID_MATCH 0x00000069
#define SAMA5D23CU_EXID_MATCH 0x00000058
#define SAMA5D24CX_EXID_MATCH 0x00000004
#define SAMA5D24CU_EXID_MATCH 0x00000014
#define SAMA5D26CU_EXID_MATCH 0x00000012
#define SAMA5D27C_D1G_EXID_MATCH 0x00000033
#define SAMA5D27C_D5M_EXID_MATCH 0x00000032
#define SAMA5D27CU_EXID_MATCH 0x00000011
#define SAMA5D27CN_EXID_MATCH 0x00000021
#define SAMA5D28C_D1G_EXID_MATCH 0x00000013
#define SAMA5D28CU_EXID_MATCH 0x00000010
#define SAMA5D28CN_EXID_MATCH 0x00000020

View file

@ -20,4 +20,6 @@ config SOC_BRCMSTB
If unsure, say N.
source "drivers/soc/bcm/brcmstb/Kconfig"
endmenu

View file

@ -0,0 +1,10 @@
if SOC_BRCMSTB
config BRCMSTB_PM
bool "Support suspend/resume for STB platforms"
default y
depends on PM
depends on ARCH_BRCMSTB || BMIPS_GENERIC
select ARM_CPU_SUSPEND if ARM
endif # SOC_BRCMSTB

View file

@ -1 +1,2 @@
obj-y += common.o biuctrl.o
obj-$(CONFIG_BRCMSTB_PM) += pm/

View file

@ -0,0 +1,3 @@
obj-$(CONFIG_ARM) += s2-arm.o pm-arm.o
AFLAGS_s2-arm.o := -march=armv7-a
obj-$(CONFIG_BMIPS_GENERIC) += s2-mips.o s3-mips.o pm-mips.o

View file

@ -0,0 +1,113 @@
/*
* Always ON (AON) register interface between bootloader and Linux
*
* Copyright © 2014-2017 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.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#ifndef __BRCMSTB_AON_DEFS_H__
#define __BRCMSTB_AON_DEFS_H__
#include <linux/compiler.h>
/* Magic number in upper 16-bits */
#define BRCMSTB_S3_MAGIC_MASK 0xffff0000
#define BRCMSTB_S3_MAGIC_SHORT 0x5AFE0000
enum {
/* Restore random key for AES memory verification (off = fixed key) */
S3_FLAG_LOAD_RANDKEY = (1 << 0),
/* Scratch buffer page table is present */
S3_FLAG_SCRATCH_BUFFER_TABLE = (1 << 1),
/* Skip all memory verification */
S3_FLAG_NO_MEM_VERIFY = (1 << 2),
/*
* Modification of this bit reserved for bootloader only.
* 1=PSCI started Linux, 0=Direct jump to Linux.
*/
S3_FLAG_PSCI_BOOT = (1 << 3),
/*
* Modification of this bit reserved for bootloader only.
* 1=64 bit boot, 0=32 bit boot.
*/
S3_FLAG_BOOTED64 = (1 << 4),
};
#define BRCMSTB_HASH_LEN (128 / 8) /* 128-bit hash */
#define AON_REG_MAGIC_FLAGS 0x00
#define AON_REG_CONTROL_LOW 0x04
#define AON_REG_CONTROL_HIGH 0x08
#define AON_REG_S3_HASH 0x0c /* hash of S3 params */
#define AON_REG_CONTROL_HASH_LEN 0x1c
#define AON_REG_PANIC 0x20
#define BRCMSTB_S3_MAGIC 0x5AFEB007
#define BRCMSTB_PANIC_MAGIC 0x512E115E
#define BOOTLOADER_SCRATCH_SIZE 64
#define BRCMSTB_DTU_STATE_MAP_ENTRIES (8*1024)
#define BRCMSTB_DTU_CONFIG_ENTRIES (512)
#define BRCMSTB_DTU_COUNT (2)
#define IMAGE_DESCRIPTORS_BUFSIZE (2 * 1024)
#define S3_BOOTLOADER_RESERVED (S3_FLAG_PSCI_BOOT | S3_FLAG_BOOTED64)
struct brcmstb_bootloader_dtu_table {
uint32_t dtu_state_map[BRCMSTB_DTU_STATE_MAP_ENTRIES];
uint32_t dtu_config[BRCMSTB_DTU_CONFIG_ENTRIES];
};
/*
* Bootloader utilizes a custom parameter block left in DRAM for handling S3
* warm resume
*/
struct brcmstb_s3_params {
/* scratch memory for bootloader */
uint8_t scratch[BOOTLOADER_SCRATCH_SIZE];
uint32_t magic; /* BRCMSTB_S3_MAGIC */
uint64_t reentry; /* PA */
/* descriptors */
uint32_t hash[BRCMSTB_HASH_LEN / 4];
/*
* If 0, then ignore this parameter (there is only one set of
* descriptors)
*
* If non-0, then a second set of descriptors is stored at:
*
* descriptors + desc_offset_2
*
* The MAC result of both descriptors is XOR'd and stored in @hash
*/
uint32_t desc_offset_2;
/*
* (Physical) address of a brcmstb_bootloader_scratch_table, for
* providing a large DRAM buffer to the bootloader
*/
uint64_t buffer_table;
uint32_t spare[70];
uint8_t descriptors[IMAGE_DESCRIPTORS_BUFSIZE];
/*
* Must be last member of struct. See brcmstb_pm_s3_finish() for reason.
*/
struct brcmstb_bootloader_dtu_table dtu[BRCMSTB_DTU_COUNT];
} __packed;
#endif /* __BRCMSTB_AON_DEFS_H__ */

View file

@ -0,0 +1,822 @@
/*
* ARM-specific support for Broadcom STB S2/S3/S5 power management
*
* S2: clock gate CPUs and as many peripherals as possible
* S3: power off all of the chip except the Always ON (AON) island; keep DDR is
* self-refresh
* S5: (a.k.a. S3 cold boot) much like S3, except DDR is powered down, so we
* treat this mode like a soft power-off, with wakeup allowed from AON
*
* Copyright © 2014-2017 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.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#define pr_fmt(fmt) "brcmstb-pm: " fmt
#include <linux/bitops.h>
#include <linux/compiler.h>
#include <linux/delay.h>
#include <linux/dma-mapping.h>
#include <linux/err.h>
#include <linux/init.h>
#include <linux/io.h>
#include <linux/ioport.h>
#include <linux/kconfig.h>
#include <linux/kernel.h>
#include <linux/memblock.h>
#include <linux/module.h>
#include <linux/notifier.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/platform_device.h>
#include <linux/pm.h>
#include <linux/printk.h>
#include <linux/proc_fs.h>
#include <linux/sizes.h>
#include <linux/slab.h>
#include <linux/sort.h>
#include <linux/suspend.h>
#include <linux/types.h>
#include <linux/uaccess.h>
#include <linux/soc/brcmstb/brcmstb.h>
#include <asm/fncpy.h>
#include <asm/setup.h>
#include <asm/suspend.h>
#include "pm.h"
#include "aon_defs.h"
#define SHIMPHY_DDR_PAD_CNTRL 0x8c
/* Method #0 */
#define SHIMPHY_PAD_PLL_SEQUENCE BIT(8)
#define SHIMPHY_PAD_GATE_PLL_S3 BIT(9)
/* Method #1 */
#define PWRDWN_SEQ_NO_SEQUENCING 0
#define PWRDWN_SEQ_HOLD_CHANNEL 1
#define PWRDWN_SEQ_RESET_PLL 2
#define PWRDWN_SEQ_POWERDOWN_PLL 3
#define SHIMPHY_PAD_S3_PWRDWN_SEQ_MASK 0x00f00000
#define SHIMPHY_PAD_S3_PWRDWN_SEQ_SHIFT 20
#define DDR_FORCE_CKE_RST_N BIT(3)
#define DDR_PHY_RST_N BIT(2)
#define DDR_PHY_CKE BIT(1)
#define DDR_PHY_NO_CHANNEL 0xffffffff
#define MAX_NUM_MEMC 3
struct brcmstb_memc {
void __iomem *ddr_phy_base;
void __iomem *ddr_shimphy_base;
void __iomem *ddr_ctrl;
};
struct brcmstb_pm_control {
void __iomem *aon_ctrl_base;
void __iomem *aon_sram;
struct brcmstb_memc memcs[MAX_NUM_MEMC];
void __iomem *boot_sram;
size_t boot_sram_len;
bool support_warm_boot;
size_t pll_status_offset;
int num_memc;
struct brcmstb_s3_params *s3_params;
dma_addr_t s3_params_pa;
int s3entry_method;
u32 warm_boot_offset;
u32 phy_a_standby_ctrl_offs;
u32 phy_b_standby_ctrl_offs;
bool needs_ddr_pad;
struct platform_device *pdev;
};
enum bsp_initiate_command {
BSP_CLOCK_STOP = 0x00,
BSP_GEN_RANDOM_KEY = 0x4A,
BSP_RESTORE_RANDOM_KEY = 0x55,
BSP_GEN_FIXED_KEY = 0x63,
};
#define PM_INITIATE 0x01
#define PM_INITIATE_SUCCESS 0x00
#define PM_INITIATE_FAIL 0xfe
static struct brcmstb_pm_control ctrl;
static int (*brcmstb_pm_do_s2_sram)(void __iomem *aon_ctrl_base,
void __iomem *ddr_phy_pll_status);
static int brcmstb_init_sram(struct device_node *dn)
{
void __iomem *sram;
struct resource res;
int ret;
ret = of_address_to_resource(dn, 0, &res);
if (ret)
return ret;
/* Uncached, executable remapping of SRAM */
sram = __arm_ioremap_exec(res.start, resource_size(&res), false);
if (!sram)
return -ENOMEM;
ctrl.boot_sram = sram;
ctrl.boot_sram_len = resource_size(&res);
return 0;
}
static const struct of_device_id sram_dt_ids[] = {
{ .compatible = "mmio-sram" },
{ /* sentinel */ }
};
static int do_bsp_initiate_command(enum bsp_initiate_command cmd)
{
void __iomem *base = ctrl.aon_ctrl_base;
int ret;
int timeo = 1000 * 1000; /* 1 second */
writel_relaxed(0, base + AON_CTRL_PM_INITIATE);
(void)readl_relaxed(base + AON_CTRL_PM_INITIATE);
/* Go! */
writel_relaxed((cmd << 1) | PM_INITIATE, base + AON_CTRL_PM_INITIATE);
/*
* If firmware doesn't support the 'ack', then just assume it's done
* after 10ms. Note that this only works for command 0, BSP_CLOCK_STOP
*/
if (of_machine_is_compatible("brcm,bcm74371a0")) {
(void)readl_relaxed(base + AON_CTRL_PM_INITIATE);
mdelay(10);
return 0;
}
for (;;) {
ret = readl_relaxed(base + AON_CTRL_PM_INITIATE);
if (!(ret & PM_INITIATE))
break;
if (timeo <= 0) {
pr_err("error: timeout waiting for BSP (%x)\n", ret);
break;
}
timeo -= 50;
udelay(50);
}
return (ret & 0xff) != PM_INITIATE_SUCCESS;
}
static int brcmstb_pm_handshake(void)
{
void __iomem *base = ctrl.aon_ctrl_base;
u32 tmp;
int ret;
/* BSP power handshake, v1 */
tmp = readl_relaxed(base + AON_CTRL_HOST_MISC_CMDS);
tmp &= ~1UL;
writel_relaxed(tmp, base + AON_CTRL_HOST_MISC_CMDS);
(void)readl_relaxed(base + AON_CTRL_HOST_MISC_CMDS);
ret = do_bsp_initiate_command(BSP_CLOCK_STOP);
if (ret)
pr_err("BSP handshake failed\n");
/*
* HACK: BSP may have internal race on the CLOCK_STOP command.
* Avoid touching the BSP for a few milliseconds.
*/
mdelay(3);
return ret;
}
static inline void shimphy_set(u32 value, u32 mask)
{
int i;
if (!ctrl.needs_ddr_pad)
return;
for (i = 0; i < ctrl.num_memc; i++) {
u32 tmp;
tmp = readl_relaxed(ctrl.memcs[i].ddr_shimphy_base +
SHIMPHY_DDR_PAD_CNTRL);
tmp = value | (tmp & mask);
writel_relaxed(tmp, ctrl.memcs[i].ddr_shimphy_base +
SHIMPHY_DDR_PAD_CNTRL);
}
wmb(); /* Complete sequence in order. */
}
static inline void ddr_ctrl_set(bool warmboot)
{
int i;
for (i = 0; i < ctrl.num_memc; i++) {
u32 tmp;
tmp = readl_relaxed(ctrl.memcs[i].ddr_ctrl +
ctrl.warm_boot_offset);
if (warmboot)
tmp |= 1;
else
tmp &= ~1; /* Cold boot */
writel_relaxed(tmp, ctrl.memcs[i].ddr_ctrl +
ctrl.warm_boot_offset);
}
/* Complete sequence in order */
wmb();
}
static inline void s3entry_method0(void)
{
shimphy_set(SHIMPHY_PAD_GATE_PLL_S3 | SHIMPHY_PAD_PLL_SEQUENCE,
0xffffffff);
}
static inline void s3entry_method1(void)
{
/*
* S3 Entry Sequence
* -----------------
* Step 1: SHIMPHY_ADDR_CNTL_0_DDR_PAD_CNTRL [ S3_PWRDWN_SEQ ] = 3
* Step 2: MEMC_DDR_0_WARM_BOOT [ WARM_BOOT ] = 1
*/
shimphy_set((PWRDWN_SEQ_POWERDOWN_PLL <<
SHIMPHY_PAD_S3_PWRDWN_SEQ_SHIFT),
~SHIMPHY_PAD_S3_PWRDWN_SEQ_MASK);
ddr_ctrl_set(true);
}
static inline void s5entry_method1(void)
{
int i;
/*
* S5 Entry Sequence
* -----------------
* Step 1: SHIMPHY_ADDR_CNTL_0_DDR_PAD_CNTRL [ S3_PWRDWN_SEQ ] = 3
* Step 2: MEMC_DDR_0_WARM_BOOT [ WARM_BOOT ] = 0
* Step 3: DDR_PHY_CONTROL_REGS_[AB]_0_STANDBY_CONTROL[ CKE ] = 0
* DDR_PHY_CONTROL_REGS_[AB]_0_STANDBY_CONTROL[ RST_N ] = 0
*/
shimphy_set((PWRDWN_SEQ_POWERDOWN_PLL <<
SHIMPHY_PAD_S3_PWRDWN_SEQ_SHIFT),
~SHIMPHY_PAD_S3_PWRDWN_SEQ_MASK);
ddr_ctrl_set(false);
for (i = 0; i < ctrl.num_memc; i++) {
u32 tmp;
/* Step 3: Channel A (RST_N = CKE = 0) */
tmp = readl_relaxed(ctrl.memcs[i].ddr_phy_base +
ctrl.phy_a_standby_ctrl_offs);
tmp &= ~(DDR_PHY_RST_N | DDR_PHY_RST_N);
writel_relaxed(tmp, ctrl.memcs[i].ddr_phy_base +
ctrl.phy_a_standby_ctrl_offs);
/* Step 3: Channel B? */
if (ctrl.phy_b_standby_ctrl_offs != DDR_PHY_NO_CHANNEL) {
tmp = readl_relaxed(ctrl.memcs[i].ddr_phy_base +
ctrl.phy_b_standby_ctrl_offs);
tmp &= ~(DDR_PHY_RST_N | DDR_PHY_RST_N);
writel_relaxed(tmp, ctrl.memcs[i].ddr_phy_base +
ctrl.phy_b_standby_ctrl_offs);
}
}
/* Must complete */
wmb();
}
/*
* Run a Power Management State Machine (PMSM) shutdown command and put the CPU
* into a low-power mode
*/
static void brcmstb_do_pmsm_power_down(unsigned long base_cmd, bool onewrite)
{
void __iomem *base = ctrl.aon_ctrl_base;
if ((ctrl.s3entry_method == 1) && (base_cmd == PM_COLD_CONFIG))
s5entry_method1();
/* pm_start_pwrdn transition 0->1 */
writel_relaxed(base_cmd, base + AON_CTRL_PM_CTRL);
if (!onewrite) {
(void)readl_relaxed(base + AON_CTRL_PM_CTRL);
writel_relaxed(base_cmd | PM_PWR_DOWN, base + AON_CTRL_PM_CTRL);
(void)readl_relaxed(base + AON_CTRL_PM_CTRL);
}
wfi();
}
/* Support S5 cold boot out of "poweroff" */
static void brcmstb_pm_poweroff(void)
{
brcmstb_pm_handshake();
/* Clear magic S3 warm-boot value */
writel_relaxed(0, ctrl.aon_sram + AON_REG_MAGIC_FLAGS);
(void)readl_relaxed(ctrl.aon_sram + AON_REG_MAGIC_FLAGS);
/* Skip wait-for-interrupt signal; just use a countdown */
writel_relaxed(0x10, ctrl.aon_ctrl_base + AON_CTRL_PM_CPU_WAIT_COUNT);
(void)readl_relaxed(ctrl.aon_ctrl_base + AON_CTRL_PM_CPU_WAIT_COUNT);
if (ctrl.s3entry_method == 1) {
shimphy_set((PWRDWN_SEQ_POWERDOWN_PLL <<
SHIMPHY_PAD_S3_PWRDWN_SEQ_SHIFT),
~SHIMPHY_PAD_S3_PWRDWN_SEQ_MASK);
ddr_ctrl_set(false);
brcmstb_do_pmsm_power_down(M1_PM_COLD_CONFIG, true);
return; /* We should never actually get here */
}
brcmstb_do_pmsm_power_down(PM_COLD_CONFIG, false);
}
static void *brcmstb_pm_copy_to_sram(void *fn, size_t len)
{
unsigned int size = ALIGN(len, FNCPY_ALIGN);
if (ctrl.boot_sram_len < size) {
pr_err("standby code will not fit in SRAM\n");
return NULL;
}
return fncpy(ctrl.boot_sram, fn, size);
}
/*
* S2 suspend/resume picks up where we left off, so we must execute carefully
* from SRAM, in order to allow DDR to come back up safely before we continue.
*/
static int brcmstb_pm_s2(void)
{
/* A previous S3 can set a value hazardous to S2, so make sure. */
if (ctrl.s3entry_method == 1) {
shimphy_set((PWRDWN_SEQ_NO_SEQUENCING <<
SHIMPHY_PAD_S3_PWRDWN_SEQ_SHIFT),
~SHIMPHY_PAD_S3_PWRDWN_SEQ_MASK);
ddr_ctrl_set(false);
}
brcmstb_pm_do_s2_sram = brcmstb_pm_copy_to_sram(&brcmstb_pm_do_s2,
brcmstb_pm_do_s2_sz);
if (!brcmstb_pm_do_s2_sram)
return -EINVAL;
return brcmstb_pm_do_s2_sram(ctrl.aon_ctrl_base,
ctrl.memcs[0].ddr_phy_base +
ctrl.pll_status_offset);
}
/*
* This function is called on a new stack, so don't allow inlining (which will
* generate stack references on the old stack). It cannot be made static because
* it is referenced from brcmstb_pm_s3()
*/
noinline int brcmstb_pm_s3_finish(void)
{
struct brcmstb_s3_params *params = ctrl.s3_params;
dma_addr_t params_pa = ctrl.s3_params_pa;
phys_addr_t reentry = virt_to_phys(&cpu_resume);
enum bsp_initiate_command cmd;
u32 flags;
/*
* Clear parameter structure, but not DTU area, which has already been
* filled in. We know DTU is a the end, so we can just subtract its
* size.
*/
memset(params, 0, sizeof(*params) - sizeof(params->dtu));
flags = readl_relaxed(ctrl.aon_sram + AON_REG_MAGIC_FLAGS);
flags &= S3_BOOTLOADER_RESERVED;
flags |= S3_FLAG_NO_MEM_VERIFY;
flags |= S3_FLAG_LOAD_RANDKEY;
/* Load random / fixed key */
if (flags & S3_FLAG_LOAD_RANDKEY)
cmd = BSP_GEN_RANDOM_KEY;
else
cmd = BSP_GEN_FIXED_KEY;
if (do_bsp_initiate_command(cmd)) {
pr_info("key loading failed\n");
return -EIO;
}
params->magic = BRCMSTB_S3_MAGIC;
params->reentry = reentry;
/* No more writes to DRAM */
flush_cache_all();
flags |= BRCMSTB_S3_MAGIC_SHORT;
writel_relaxed(flags, ctrl.aon_sram + AON_REG_MAGIC_FLAGS);
writel_relaxed(lower_32_bits(params_pa),
ctrl.aon_sram + AON_REG_CONTROL_LOW);
writel_relaxed(upper_32_bits(params_pa),
ctrl.aon_sram + AON_REG_CONTROL_HIGH);
switch (ctrl.s3entry_method) {
case 0:
s3entry_method0();
brcmstb_do_pmsm_power_down(PM_WARM_CONFIG, false);
break;
case 1:
s3entry_method1();
brcmstb_do_pmsm_power_down(M1_PM_WARM_CONFIG, true);
break;
default:
return -EINVAL;
}
/* Must have been interrupted from wfi()? */
return -EINTR;
}
static int brcmstb_pm_do_s3(unsigned long sp)
{
unsigned long save_sp;
int ret;
asm volatile (
"mov %[save], sp\n"
"mov sp, %[new]\n"
"bl brcmstb_pm_s3_finish\n"
"mov %[ret], r0\n"
"mov %[new], sp\n"
"mov sp, %[save]\n"
: [save] "=&r" (save_sp), [ret] "=&r" (ret)
: [new] "r" (sp)
);
return ret;
}
static int brcmstb_pm_s3(void)
{
void __iomem *sp = ctrl.boot_sram + ctrl.boot_sram_len;
return cpu_suspend((unsigned long)sp, brcmstb_pm_do_s3);
}
static int brcmstb_pm_standby(bool deep_standby)
{
int ret;
if (brcmstb_pm_handshake())
return -EIO;
if (deep_standby)
ret = brcmstb_pm_s3();
else
ret = brcmstb_pm_s2();
if (ret)
pr_err("%s: standby failed\n", __func__);
return ret;
}
static int brcmstb_pm_enter(suspend_state_t state)
{
int ret = -EINVAL;
switch (state) {
case PM_SUSPEND_STANDBY:
ret = brcmstb_pm_standby(false);
break;
case PM_SUSPEND_MEM:
ret = brcmstb_pm_standby(true);
break;
}
return ret;
}
static int brcmstb_pm_valid(suspend_state_t state)
{
switch (state) {
case PM_SUSPEND_STANDBY:
return true;
case PM_SUSPEND_MEM:
return ctrl.support_warm_boot;
default:
return false;
}
}
static const struct platform_suspend_ops brcmstb_pm_ops = {
.enter = brcmstb_pm_enter,
.valid = brcmstb_pm_valid,
};
static const struct of_device_id aon_ctrl_dt_ids[] = {
{ .compatible = "brcm,brcmstb-aon-ctrl" },
{}
};
struct ddr_phy_ofdata {
bool supports_warm_boot;
size_t pll_status_offset;
int s3entry_method;
u32 warm_boot_offset;
u32 phy_a_standby_ctrl_offs;
u32 phy_b_standby_ctrl_offs;
};
static struct ddr_phy_ofdata ddr_phy_71_1 = {
.supports_warm_boot = true,
.pll_status_offset = 0x0c,
.s3entry_method = 1,
.warm_boot_offset = 0x2c,
.phy_a_standby_ctrl_offs = 0x198,
.phy_b_standby_ctrl_offs = DDR_PHY_NO_CHANNEL
};
static struct ddr_phy_ofdata ddr_phy_72_0 = {
.supports_warm_boot = true,
.pll_status_offset = 0x10,
.s3entry_method = 1,
.warm_boot_offset = 0x40,
.phy_a_standby_ctrl_offs = 0x2a4,
.phy_b_standby_ctrl_offs = 0x8a4
};
static struct ddr_phy_ofdata ddr_phy_225_1 = {
.supports_warm_boot = false,
.pll_status_offset = 0x4,
.s3entry_method = 0
};
static struct ddr_phy_ofdata ddr_phy_240_1 = {
.supports_warm_boot = true,
.pll_status_offset = 0x4,
.s3entry_method = 0
};
static const struct of_device_id ddr_phy_dt_ids[] = {
{
.compatible = "brcm,brcmstb-ddr-phy-v71.1",
.data = &ddr_phy_71_1,
},
{
.compatible = "brcm,brcmstb-ddr-phy-v72.0",
.data = &ddr_phy_72_0,
},
{
.compatible = "brcm,brcmstb-ddr-phy-v225.1",
.data = &ddr_phy_225_1,
},
{
.compatible = "brcm,brcmstb-ddr-phy-v240.1",
.data = &ddr_phy_240_1,
},
{
/* Same as v240.1, for the registers we care about */
.compatible = "brcm,brcmstb-ddr-phy-v240.2",
.data = &ddr_phy_240_1,
},
{}
};
struct ddr_seq_ofdata {
bool needs_ddr_pad;
u32 warm_boot_offset;
};
static const struct ddr_seq_ofdata ddr_seq_b22 = {
.needs_ddr_pad = false,
.warm_boot_offset = 0x2c,
};
static const struct ddr_seq_ofdata ddr_seq = {
.needs_ddr_pad = true,
};
static const struct of_device_id ddr_shimphy_dt_ids[] = {
{ .compatible = "brcm,brcmstb-ddr-shimphy-v1.0" },
{}
};
static const struct of_device_id brcmstb_memc_of_match[] = {
{
.compatible = "brcm,brcmstb-memc-ddr-rev-b.2.2",
.data = &ddr_seq_b22,
},
{
.compatible = "brcm,brcmstb-memc-ddr",
.data = &ddr_seq,
},
{},
};
static void __iomem *brcmstb_ioremap_match(const struct of_device_id *matches,
int index, const void **ofdata)
{
struct device_node *dn;
const struct of_device_id *match;
dn = of_find_matching_node_and_match(NULL, matches, &match);
if (!dn)
return ERR_PTR(-EINVAL);
if (ofdata)
*ofdata = match->data;
return of_io_request_and_map(dn, index, dn->full_name);
}
static int brcmstb_pm_panic_notify(struct notifier_block *nb,
unsigned long action, void *data)
{
writel_relaxed(BRCMSTB_PANIC_MAGIC, ctrl.aon_sram + AON_REG_PANIC);
return NOTIFY_DONE;
}
static struct notifier_block brcmstb_pm_panic_nb = {
.notifier_call = brcmstb_pm_panic_notify,
};
static int brcmstb_pm_probe(struct platform_device *pdev)
{
const struct ddr_phy_ofdata *ddr_phy_data;
const struct ddr_seq_ofdata *ddr_seq_data;
const struct of_device_id *of_id = NULL;
struct device_node *dn;
void __iomem *base;
int ret, i;
/* AON ctrl registers */
base = brcmstb_ioremap_match(aon_ctrl_dt_ids, 0, NULL);
if (IS_ERR(base)) {
pr_err("error mapping AON_CTRL\n");
return PTR_ERR(base);
}
ctrl.aon_ctrl_base = base;
/* AON SRAM registers */
base = brcmstb_ioremap_match(aon_ctrl_dt_ids, 1, NULL);
if (IS_ERR(base)) {
/* Assume standard offset */
ctrl.aon_sram = ctrl.aon_ctrl_base +
AON_CTRL_SYSTEM_DATA_RAM_OFS;
} else {
ctrl.aon_sram = base;
}
writel_relaxed(0, ctrl.aon_sram + AON_REG_PANIC);
/* DDR PHY registers */
base = brcmstb_ioremap_match(ddr_phy_dt_ids, 0,
(const void **)&ddr_phy_data);
if (IS_ERR(base)) {
pr_err("error mapping DDR PHY\n");
return PTR_ERR(base);
}
ctrl.support_warm_boot = ddr_phy_data->supports_warm_boot;
ctrl.pll_status_offset = ddr_phy_data->pll_status_offset;
/* Only need DDR PHY 0 for now? */
ctrl.memcs[0].ddr_phy_base = base;
ctrl.s3entry_method = ddr_phy_data->s3entry_method;
ctrl.phy_a_standby_ctrl_offs = ddr_phy_data->phy_a_standby_ctrl_offs;
ctrl.phy_b_standby_ctrl_offs = ddr_phy_data->phy_b_standby_ctrl_offs;
/*
* Slightly grosss to use the phy ver to get a memc,
* offset but that is the only versioned things so far
* we can test for.
*/
ctrl.warm_boot_offset = ddr_phy_data->warm_boot_offset;
/* DDR SHIM-PHY registers */
for_each_matching_node(dn, ddr_shimphy_dt_ids) {
i = ctrl.num_memc;
if (i >= MAX_NUM_MEMC) {
pr_warn("too many MEMCs (max %d)\n", MAX_NUM_MEMC);
break;
}
base = of_io_request_and_map(dn, 0, dn->full_name);
if (IS_ERR(base)) {
if (!ctrl.support_warm_boot)
break;
pr_err("error mapping DDR SHIMPHY %d\n", i);
return PTR_ERR(base);
}
ctrl.memcs[i].ddr_shimphy_base = base;
ctrl.num_memc++;
}
/* Sequencer DRAM Param and Control Registers */
i = 0;
for_each_matching_node(dn, brcmstb_memc_of_match) {
base = of_iomap(dn, 0);
if (!base) {
pr_err("error mapping DDR Sequencer %d\n", i);
return -ENOMEM;
}
of_id = of_match_node(brcmstb_memc_of_match, dn);
if (!of_id) {
iounmap(base);
return -EINVAL;
}
ddr_seq_data = of_id->data;
ctrl.needs_ddr_pad = ddr_seq_data->needs_ddr_pad;
/* Adjust warm boot offset based on the DDR sequencer */
if (ddr_seq_data->warm_boot_offset)
ctrl.warm_boot_offset = ddr_seq_data->warm_boot_offset;
ctrl.memcs[i].ddr_ctrl = base;
i++;
}
pr_debug("PM: supports warm boot:%d, method:%d, wboffs:%x\n",
ctrl.support_warm_boot, ctrl.s3entry_method,
ctrl.warm_boot_offset);
dn = of_find_matching_node(NULL, sram_dt_ids);
if (!dn) {
pr_err("SRAM not found\n");
return -EINVAL;
}
ret = brcmstb_init_sram(dn);
if (ret) {
pr_err("error setting up SRAM for PM\n");
return ret;
}
ctrl.pdev = pdev;
ctrl.s3_params = kmalloc(sizeof(*ctrl.s3_params), GFP_KERNEL);
if (!ctrl.s3_params)
return -ENOMEM;
ctrl.s3_params_pa = dma_map_single(&pdev->dev, ctrl.s3_params,
sizeof(*ctrl.s3_params),
DMA_TO_DEVICE);
if (dma_mapping_error(&pdev->dev, ctrl.s3_params_pa)) {
pr_err("error mapping DMA memory\n");
ret = -ENOMEM;
goto out;
}
atomic_notifier_chain_register(&panic_notifier_list,
&brcmstb_pm_panic_nb);
pm_power_off = brcmstb_pm_poweroff;
suspend_set_ops(&brcmstb_pm_ops);
return 0;
out:
kfree(ctrl.s3_params);
pr_warn("PM: initialization failed with code %d\n", ret);
return ret;
}
static struct platform_driver brcmstb_pm_driver = {
.driver = {
.name = "brcmstb-pm",
.of_match_table = aon_ctrl_dt_ids,
},
};
static int __init brcmstb_pm_init(void)
{
return platform_driver_probe(&brcmstb_pm_driver,
brcmstb_pm_probe);
}
module_init(brcmstb_pm_init);

View file

@ -0,0 +1,461 @@
/*
* MIPS-specific support for Broadcom STB S2/S3/S5 power management
*
* Copyright (C) 2016-2017 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.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <linux/kernel.h>
#include <linux/printk.h>
#include <linux/io.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/delay.h>
#include <linux/suspend.h>
#include <asm/bmips.h>
#include <asm/tlbflush.h>
#include "pm.h"
#define S2_NUM_PARAMS 6
#define MAX_NUM_MEMC 3
/* S3 constants */
#define MAX_GP_REGS 16
#define MAX_CP0_REGS 32
#define NUM_MEMC_CLIENTS 128
#define AON_CTRL_RAM_SIZE 128
#define BRCMSTB_S3_MAGIC 0x5AFEB007
#define CLEAR_RESET_MASK 0x01
/* Index each CP0 register that needs to be saved */
#define CONTEXT 0
#define USER_LOCAL 1
#define PGMK 2
#define HWRENA 3
#define COMPARE 4
#define STATUS 5
#define CONFIG 6
#define MODE 7
#define EDSP 8
#define BOOT_VEC 9
#define EBASE 10
struct brcmstb_memc {
void __iomem *ddr_phy_base;
void __iomem *arb_base;
};
struct brcmstb_pm_control {
void __iomem *aon_ctrl_base;
void __iomem *aon_sram_base;
void __iomem *timers_base;
struct brcmstb_memc memcs[MAX_NUM_MEMC];
int num_memc;
};
struct brcm_pm_s3_context {
u32 cp0_regs[MAX_CP0_REGS];
u32 memc0_rts[NUM_MEMC_CLIENTS];
u32 sc_boot_vec;
};
struct brcmstb_mem_transfer;
struct brcmstb_mem_transfer {
struct brcmstb_mem_transfer *next;
void *src;
void *dst;
dma_addr_t pa_src;
dma_addr_t pa_dst;
u32 len;
u8 key;
u8 mode;
u8 src_remapped;
u8 dst_remapped;
u8 src_dst_remapped;
};
#define AON_SAVE_SRAM(base, idx, val) \
__raw_writel(val, base + (idx << 2))
/* Used for saving registers in asm */
u32 gp_regs[MAX_GP_REGS];
#define BSP_CLOCK_STOP 0x00
#define PM_INITIATE 0x01
static struct brcmstb_pm_control ctrl;
static void brcm_pm_save_cp0_context(struct brcm_pm_s3_context *ctx)
{
/* Generic MIPS */
ctx->cp0_regs[CONTEXT] = read_c0_context();
ctx->cp0_regs[USER_LOCAL] = read_c0_userlocal();
ctx->cp0_regs[PGMK] = read_c0_pagemask();
ctx->cp0_regs[HWRENA] = read_c0_cache();
ctx->cp0_regs[COMPARE] = read_c0_compare();
ctx->cp0_regs[STATUS] = read_c0_status();
/* Broadcom specific */
ctx->cp0_regs[CONFIG] = read_c0_brcm_config();
ctx->cp0_regs[MODE] = read_c0_brcm_mode();
ctx->cp0_regs[EDSP] = read_c0_brcm_edsp();
ctx->cp0_regs[BOOT_VEC] = read_c0_brcm_bootvec();
ctx->cp0_regs[EBASE] = read_c0_ebase();
ctx->sc_boot_vec = bmips_read_zscm_reg(0xa0);
}
static void brcm_pm_restore_cp0_context(struct brcm_pm_s3_context *ctx)
{
/* Restore cp0 state */
bmips_write_zscm_reg(0xa0, ctx->sc_boot_vec);
/* Generic MIPS */
write_c0_context(ctx->cp0_regs[CONTEXT]);
write_c0_userlocal(ctx->cp0_regs[USER_LOCAL]);
write_c0_pagemask(ctx->cp0_regs[PGMK]);
write_c0_cache(ctx->cp0_regs[HWRENA]);
write_c0_compare(ctx->cp0_regs[COMPARE]);
write_c0_status(ctx->cp0_regs[STATUS]);
/* Broadcom specific */
write_c0_brcm_config(ctx->cp0_regs[CONFIG]);
write_c0_brcm_mode(ctx->cp0_regs[MODE]);
write_c0_brcm_edsp(ctx->cp0_regs[EDSP]);
write_c0_brcm_bootvec(ctx->cp0_regs[BOOT_VEC]);
write_c0_ebase(ctx->cp0_regs[EBASE]);
}
static void brcmstb_pm_handshake(void)
{
void __iomem *base = ctrl.aon_ctrl_base;
u32 tmp;
/* BSP power handshake, v1 */
tmp = __raw_readl(base + AON_CTRL_HOST_MISC_CMDS);
tmp &= ~1UL;
__raw_writel(tmp, base + AON_CTRL_HOST_MISC_CMDS);
(void)__raw_readl(base + AON_CTRL_HOST_MISC_CMDS);
__raw_writel(0, base + AON_CTRL_PM_INITIATE);
(void)__raw_readl(base + AON_CTRL_PM_INITIATE);
__raw_writel(BSP_CLOCK_STOP | PM_INITIATE,
base + AON_CTRL_PM_INITIATE);
/*
* HACK: BSP may have internal race on the CLOCK_STOP command.
* Avoid touching the BSP for a few milliseconds.
*/
mdelay(3);
}
static void brcmstb_pm_s5(void)
{
void __iomem *base = ctrl.aon_ctrl_base;
brcmstb_pm_handshake();
/* Clear magic s3 warm-boot value */
AON_SAVE_SRAM(ctrl.aon_sram_base, 0, 0);
/* Set the countdown */
__raw_writel(0x10, base + AON_CTRL_PM_CPU_WAIT_COUNT);
(void)__raw_readl(base + AON_CTRL_PM_CPU_WAIT_COUNT);
/* Prepare to S5 cold boot */
__raw_writel(PM_COLD_CONFIG, base + AON_CTRL_PM_CTRL);
(void)__raw_readl(base + AON_CTRL_PM_CTRL);
__raw_writel((PM_COLD_CONFIG | PM_PWR_DOWN), base +
AON_CTRL_PM_CTRL);
(void)__raw_readl(base + AON_CTRL_PM_CTRL);
__asm__ __volatile__(
" wait\n"
: : : "memory");
}
static int brcmstb_pm_s3(void)
{
struct brcm_pm_s3_context s3_context;
void __iomem *memc_arb_base;
unsigned long flags;
u32 tmp;
int i;
/* Prepare for s3 */
AON_SAVE_SRAM(ctrl.aon_sram_base, 0, BRCMSTB_S3_MAGIC);
AON_SAVE_SRAM(ctrl.aon_sram_base, 1, (u32)&s3_reentry);
AON_SAVE_SRAM(ctrl.aon_sram_base, 2, 0);
/* Clear RESET_HISTORY */
tmp = __raw_readl(ctrl.aon_ctrl_base + AON_CTRL_RESET_CTRL);
tmp &= ~CLEAR_RESET_MASK;
__raw_writel(tmp, ctrl.aon_ctrl_base + AON_CTRL_RESET_CTRL);
local_irq_save(flags);
/* Inhibit DDR_RSTb pulse for both MMCs*/
for (i = 0; i < ctrl.num_memc; i++) {
tmp = __raw_readl(ctrl.memcs[i].ddr_phy_base +
DDR40_PHY_CONTROL_REGS_0_STANDBY_CTRL);
tmp &= ~0x0f;
__raw_writel(tmp, ctrl.memcs[i].ddr_phy_base +
DDR40_PHY_CONTROL_REGS_0_STANDBY_CTRL);
tmp |= (0x05 | BIT(5));
__raw_writel(tmp, ctrl.memcs[i].ddr_phy_base +
DDR40_PHY_CONTROL_REGS_0_STANDBY_CTRL);
}
/* Save CP0 context */
brcm_pm_save_cp0_context(&s3_context);
/* Save RTS(skip debug register) */
memc_arb_base = ctrl.memcs[0].arb_base + 4;
for (i = 0; i < NUM_MEMC_CLIENTS; i++) {
s3_context.memc0_rts[i] = __raw_readl(memc_arb_base);
memc_arb_base += 4;
}
/* Save I/O context */
local_flush_tlb_all();
_dma_cache_wback_inv(0, ~0);
brcm_pm_do_s3(ctrl.aon_ctrl_base, current_cpu_data.dcache.linesz);
/* CPU reconfiguration */
local_flush_tlb_all();
bmips_cpu_setup();
cpumask_clear(&bmips_booted_mask);
/* Restore RTS (skip debug register) */
memc_arb_base = ctrl.memcs[0].arb_base + 4;
for (i = 0; i < NUM_MEMC_CLIENTS; i++) {
__raw_writel(s3_context.memc0_rts[i], memc_arb_base);
memc_arb_base += 4;
}
/* restore CP0 context */
brcm_pm_restore_cp0_context(&s3_context);
local_irq_restore(flags);
return 0;
}
static int brcmstb_pm_s2(void)
{
/*
* We need to pass 6 arguments to an assembly function. Lets avoid the
* stack and pass arguments in a explicit 4 byte array. The assembly
* code assumes all arguments are 4 bytes and arguments are ordered
* like so:
*
* 0: AON_CTRl base register
* 1: DDR_PHY base register
* 2: TIMERS base resgister
* 3: I-Cache line size
* 4: Restart vector address
* 5: Restart vector size
*/
u32 s2_params[6];
/* Prepare s2 parameters */
s2_params[0] = (u32)ctrl.aon_ctrl_base;
s2_params[1] = (u32)ctrl.memcs[0].ddr_phy_base;
s2_params[2] = (u32)ctrl.timers_base;
s2_params[3] = (u32)current_cpu_data.icache.linesz;
s2_params[4] = (u32)BMIPS_WARM_RESTART_VEC;
s2_params[5] = (u32)(bmips_smp_int_vec_end -
bmips_smp_int_vec);
/* Drop to standby */
brcm_pm_do_s2(s2_params);
return 0;
}
static int brcmstb_pm_standby(bool deep_standby)
{
brcmstb_pm_handshake();
/* Send IRQs to BMIPS_WARM_RESTART_VEC */
clear_c0_cause(CAUSEF_IV);
irq_disable_hazard();
set_c0_status(ST0_BEV);
irq_disable_hazard();
if (deep_standby)
brcmstb_pm_s3();
else
brcmstb_pm_s2();
/* Send IRQs to normal runtime vectors */
clear_c0_status(ST0_BEV);
irq_disable_hazard();
set_c0_cause(CAUSEF_IV);
irq_disable_hazard();
return 0;
}
static int brcmstb_pm_enter(suspend_state_t state)
{
int ret = -EINVAL;
switch (state) {
case PM_SUSPEND_STANDBY:
ret = brcmstb_pm_standby(false);
break;
case PM_SUSPEND_MEM:
ret = brcmstb_pm_standby(true);
break;
}
return ret;
}
static int brcmstb_pm_valid(suspend_state_t state)
{
switch (state) {
case PM_SUSPEND_STANDBY:
return true;
case PM_SUSPEND_MEM:
return true;
default:
return false;
}
}
static const struct platform_suspend_ops brcmstb_pm_ops = {
.enter = brcmstb_pm_enter,
.valid = brcmstb_pm_valid,
};
static const struct of_device_id aon_ctrl_dt_ids[] = {
{ .compatible = "brcm,brcmstb-aon-ctrl" },
{ /* sentinel */ }
};
static const struct of_device_id ddr_phy_dt_ids[] = {
{ .compatible = "brcm,brcmstb-ddr-phy" },
{ /* sentinel */ }
};
static const struct of_device_id arb_dt_ids[] = {
{ .compatible = "brcm,brcmstb-memc-arb" },
{ /* sentinel */ }
};
static const struct of_device_id timers_ids[] = {
{ .compatible = "brcm,brcmstb-timers" },
{ /* sentinel */ }
};
static inline void __iomem *brcmstb_ioremap_node(struct device_node *dn,
int index)
{
return of_io_request_and_map(dn, index, dn->full_name);
}
static void __iomem *brcmstb_ioremap_match(const struct of_device_id *matches,
int index, const void **ofdata)
{
struct device_node *dn;
const struct of_device_id *match;
dn = of_find_matching_node_and_match(NULL, matches, &match);
if (!dn)
return ERR_PTR(-EINVAL);
if (ofdata)
*ofdata = match->data;
return brcmstb_ioremap_node(dn, index);
}
static int brcmstb_pm_init(void)
{
struct device_node *dn;
void __iomem *base;
int i;
/* AON ctrl registers */
base = brcmstb_ioremap_match(aon_ctrl_dt_ids, 0, NULL);
if (IS_ERR(base)) {
pr_err("error mapping AON_CTRL\n");
goto aon_err;
}
ctrl.aon_ctrl_base = base;
/* AON SRAM registers */
base = brcmstb_ioremap_match(aon_ctrl_dt_ids, 1, NULL);
if (IS_ERR(base)) {
pr_err("error mapping AON_SRAM\n");
goto sram_err;
}
ctrl.aon_sram_base = base;
ctrl.num_memc = 0;
/* Map MEMC DDR PHY registers */
for_each_matching_node(dn, ddr_phy_dt_ids) {
i = ctrl.num_memc;
if (i >= MAX_NUM_MEMC) {
pr_warn("Too many MEMCs (max %d)\n", MAX_NUM_MEMC);
break;
}
base = brcmstb_ioremap_node(dn, 0);
if (IS_ERR(base))
goto ddr_err;
ctrl.memcs[i].ddr_phy_base = base;
ctrl.num_memc++;
}
/* MEMC ARB registers */
base = brcmstb_ioremap_match(arb_dt_ids, 0, NULL);
if (IS_ERR(base)) {
pr_err("error mapping MEMC ARB\n");
goto ddr_err;
}
ctrl.memcs[0].arb_base = base;
/* Timer registers */
base = brcmstb_ioremap_match(timers_ids, 0, NULL);
if (IS_ERR(base)) {
pr_err("error mapping timers\n");
goto tmr_err;
}
ctrl.timers_base = base;
/* s3 cold boot aka s5 */
pm_power_off = brcmstb_pm_s5;
suspend_set_ops(&brcmstb_pm_ops);
return 0;
tmr_err:
iounmap(ctrl.memcs[0].arb_base);
ddr_err:
for (i = 0; i < ctrl.num_memc; i++)
iounmap(ctrl.memcs[i].ddr_phy_base);
iounmap(ctrl.aon_sram_base);
sram_err:
iounmap(ctrl.aon_ctrl_base);
aon_err:
return PTR_ERR(base);
}
arch_initcall(brcmstb_pm_init);

View file

@ -0,0 +1,89 @@
/*
* Definitions for Broadcom STB power management / Always ON (AON) block
*
* Copyright © 2016-2017 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.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#ifndef __BRCMSTB_PM_H__
#define __BRCMSTB_PM_H__
#define AON_CTRL_RESET_CTRL 0x00
#define AON_CTRL_PM_CTRL 0x04
#define AON_CTRL_PM_STATUS 0x08
#define AON_CTRL_PM_CPU_WAIT_COUNT 0x10
#define AON_CTRL_PM_INITIATE 0x88
#define AON_CTRL_HOST_MISC_CMDS 0x8c
#define AON_CTRL_SYSTEM_DATA_RAM_OFS 0x200
/* MIPS PM constants */
/* MEMC0 offsets */
#define DDR40_PHY_CONTROL_REGS_0_PLL_STATUS 0x10
#define DDR40_PHY_CONTROL_REGS_0_STANDBY_CTRL 0xa4
/* TIMER offsets */
#define TIMER_TIMER1_CTRL 0x0c
#define TIMER_TIMER1_STAT 0x1c
/* TIMER defines */
#define RESET_TIMER 0x0
#define START_TIMER 0xbfffffff
#define TIMER_MASK 0x3fffffff
/* PM_CTRL bitfield (Method #0) */
#define PM_FAST_PWRDOWN (1 << 6)
#define PM_WARM_BOOT (1 << 5)
#define PM_DEEP_STANDBY (1 << 4)
#define PM_CPU_PWR (1 << 3)
#define PM_USE_CPU_RDY (1 << 2)
#define PM_PLL_PWRDOWN (1 << 1)
#define PM_PWR_DOWN (1 << 0)
/* PM_CTRL bitfield (Method #1) */
#define PM_DPHY_STANDBY_CLEAR (1 << 20)
#define PM_MIN_S3_WIDTH_TIMER_BYPASS (1 << 7)
#define PM_S2_COMMAND (PM_PLL_PWRDOWN | PM_USE_CPU_RDY | PM_PWR_DOWN)
/* Method 0 bitmasks */
#define PM_COLD_CONFIG (PM_PLL_PWRDOWN | PM_DEEP_STANDBY)
#define PM_WARM_CONFIG (PM_COLD_CONFIG | PM_USE_CPU_RDY | PM_WARM_BOOT)
/* Method 1 bitmask */
#define M1_PM_WARM_CONFIG (PM_DPHY_STANDBY_CLEAR | \
PM_MIN_S3_WIDTH_TIMER_BYPASS | \
PM_WARM_BOOT | PM_DEEP_STANDBY | \
PM_PLL_PWRDOWN | PM_PWR_DOWN)
#define M1_PM_COLD_CONFIG (PM_DPHY_STANDBY_CLEAR | \
PM_MIN_S3_WIDTH_TIMER_BYPASS | \
PM_DEEP_STANDBY | \
PM_PLL_PWRDOWN | PM_PWR_DOWN)
#ifndef __ASSEMBLY__
#ifndef CONFIG_MIPS
extern const unsigned long brcmstb_pm_do_s2_sz;
extern asmlinkage int brcmstb_pm_do_s2(void __iomem *aon_ctrl_base,
void __iomem *ddr_phy_pll_status);
#else
/* s2 asm */
extern asmlinkage int brcm_pm_do_s2(u32 *s2_params);
/* s3 asm */
extern asmlinkage int brcm_pm_do_s3(void __iomem *aon_ctrl_base,
int dcache_linesz);
extern int s3_reentry;
#endif /* CONFIG_MIPS */
#endif
#endif /* __BRCMSTB_PM_H__ */

View file

@ -0,0 +1,76 @@
/*
* Copyright © 2014-2017 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.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <linux/linkage.h>
#include <asm/assembler.h>
#include "pm.h"
.text
.align 3
#define AON_CTRL_REG r10
#define DDR_PHY_STATUS_REG r11
/*
* r0: AON_CTRL base address
* r1: DDRY PHY PLL status register address
*/
ENTRY(brcmstb_pm_do_s2)
stmfd sp!, {r4-r11, lr}
mov AON_CTRL_REG, r0
mov DDR_PHY_STATUS_REG, r1
/* Flush memory transactions */
dsb
/* Cache DDR_PHY_STATUS_REG translation */
ldr r0, [DDR_PHY_STATUS_REG]
/* power down request */
ldr r0, =PM_S2_COMMAND
ldr r1, =0
str r1, [AON_CTRL_REG, #AON_CTRL_PM_CTRL]
ldr r1, [AON_CTRL_REG, #AON_CTRL_PM_CTRL]
str r0, [AON_CTRL_REG, #AON_CTRL_PM_CTRL]
ldr r0, [AON_CTRL_REG, #AON_CTRL_PM_CTRL]
/* Wait for interrupt */
wfi
nop
/* Bring MEMC back up */
1: ldr r0, [DDR_PHY_STATUS_REG]
ands r0, #1
beq 1b
/* Power-up handshake */
ldr r0, =1
str r0, [AON_CTRL_REG, #AON_CTRL_HOST_MISC_CMDS]
ldr r0, [AON_CTRL_REG, #AON_CTRL_HOST_MISC_CMDS]
ldr r0, =0
str r0, [AON_CTRL_REG, #AON_CTRL_PM_CTRL]
ldr r0, [AON_CTRL_REG, #AON_CTRL_PM_CTRL]
/* Return to caller */
ldr r0, =0
ldmfd sp!, {r4-r11, pc}
ENDPROC(brcmstb_pm_do_s2)
/* Place literal pool here */
.ltorg
ENTRY(brcmstb_pm_do_s2_sz)
.word . - brcmstb_pm_do_s2

View file

@ -0,0 +1,200 @@
/*
* Copyright (C) 2016 Broadcom Corporation
*
* 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.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <asm/asm.h>
#include <asm/regdef.h>
#include <asm/mipsregs.h>
#include <asm/stackframe.h>
#include "pm.h"
.text
.set noreorder
.align 5
/*
* a0: u32 params array
*/
LEAF(brcm_pm_do_s2)
subu sp, 64
sw ra, 0(sp)
sw s0, 4(sp)
sw s1, 8(sp)
sw s2, 12(sp)
sw s3, 16(sp)
sw s4, 20(sp)
sw s5, 24(sp)
sw s6, 28(sp)
sw s7, 32(sp)
/*
* Dereference the params array
* s0: AON_CTRL base register
* s1: DDR_PHY base register
* s2: TIMERS base register
* s3: I-Cache line size
* s4: Restart vector address
* s5: Restart vector size
*/
move t0, a0
lw s0, 0(t0)
lw s1, 4(t0)
lw s2, 8(t0)
lw s3, 12(t0)
lw s4, 16(t0)
lw s5, 20(t0)
/* Lock this asm section into the I-cache */
addiu t1, s3, -1
not t1
la t0, brcm_pm_do_s2
and t0, t1
la t2, asm_end
and t2, t1
1: cache 0x1c, 0(t0)
bne t0, t2, 1b
addu t0, s3
/* Lock the interrupt vector into the I-cache */
move t0, zero
2: move t1, s4
cache 0x1c, 0(t1)
addu t1, s3
addu t0, s3
ble t0, s5, 2b
nop
sync
/* Power down request */
li t0, PM_S2_COMMAND
sw zero, AON_CTRL_PM_CTRL(s0)
lw zero, AON_CTRL_PM_CTRL(s0)
sw t0, AON_CTRL_PM_CTRL(s0)
lw t0, AON_CTRL_PM_CTRL(s0)
/* Enable CP0 interrupt 2 and wait for interrupt */
mfc0 t0, CP0_STATUS
/* Save cp0 sr for restoring later */
move s6, t0
li t1, ~(ST0_IM | ST0_IE)
and t0, t1
ori t0, STATUSF_IP2
mtc0 t0, CP0_STATUS
nop
nop
nop
ori t0, ST0_IE
mtc0 t0, CP0_STATUS
/* Wait for interrupt */
wait
nop
/* Wait for memc0 */
1: lw t0, DDR40_PHY_CONTROL_REGS_0_PLL_STATUS(s1)
andi t0, 1
beqz t0, 1b
nop
/* 1ms delay needed for stable recovery */
/* Use TIMER1 to count 1 ms */
li t0, RESET_TIMER
sw t0, TIMER_TIMER1_CTRL(s2)
lw t0, TIMER_TIMER1_CTRL(s2)
li t0, START_TIMER
sw t0, TIMER_TIMER1_CTRL(s2)
lw t0, TIMER_TIMER1_CTRL(s2)
/* Prepare delay */
li t0, TIMER_MASK
lw t1, TIMER_TIMER1_STAT(s2)
and t1, t0
/* 1ms delay */
addi t1, 27000
/* Wait for the timer value to exceed t1 */
1: lw t0, TIMER_TIMER1_STAT(s2)
sgtu t2, t1, t0
bnez t2, 1b
nop
/* Power back up */
li t1, 1
sw t1, AON_CTRL_HOST_MISC_CMDS(s0)
lw t1, AON_CTRL_HOST_MISC_CMDS(s0)
sw zero, AON_CTRL_PM_CTRL(s0)
lw zero, AON_CTRL_PM_CTRL(s0)
/* Unlock I-cache */
addiu t1, s3, -1
not t1
la t0, brcm_pm_do_s2
and t0, t1
la t2, asm_end
and t2, t1
1: cache 0x00, 0(t0)
bne t0, t2, 1b
addu t0, s3
/* Unlock interrupt vector */
move t0, zero
2: move t1, s4
cache 0x00, 0(t1)
addu t1, s3
addu t0, s3
ble t0, s5, 2b
nop
/* Restore cp0 sr */
sync
nop
mtc0 s6, CP0_STATUS
nop
/* Set return value to success */
li v0, 0
/* Return to caller */
lw s7, 32(sp)
lw s6, 28(sp)
lw s5, 24(sp)
lw s4, 20(sp)
lw s3, 16(sp)
lw s2, 12(sp)
lw s1, 8(sp)
lw s0, 4(sp)
lw ra, 0(sp)
addiu sp, 64
jr ra
nop
END(brcm_pm_do_s2)
.globl asm_end
asm_end:
nop

View file

@ -0,0 +1,146 @@
/*
* Copyright (C) 2016 Broadcom Corporation
*
* 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.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <asm/asm.h>
#include <asm/regdef.h>
#include <asm/mipsregs.h>
#include <asm/bmips.h>
#include "pm.h"
.text
.set noreorder
.align 5
.global s3_reentry
/*
* a0: AON_CTRL base register
* a1: D-Cache line size
*/
LEAF(brcm_pm_do_s3)
/* Get the address of s3_context */
la t0, gp_regs
sw ra, 0(t0)
sw s0, 4(t0)
sw s1, 8(t0)
sw s2, 12(t0)
sw s3, 16(t0)
sw s4, 20(t0)
sw s5, 24(t0)
sw s6, 28(t0)
sw s7, 32(t0)
sw gp, 36(t0)
sw sp, 40(t0)
sw fp, 44(t0)
/* Save CP0 Status */
mfc0 t1, CP0_STATUS
sw t1, 48(t0)
/* Write-back gp registers - cache will be gone */
addiu t1, a1, -1
not t1
and t0, t1
/* Flush at least 64 bytes */
addiu t2, t0, 64
and t2, t1
1: cache 0x17, 0(t0)
bne t0, t2, 1b
addu t0, a1
/* Drop to deep standby */
li t1, PM_WARM_CONFIG
sw zero, AON_CTRL_PM_CTRL(a0)
lw zero, AON_CTRL_PM_CTRL(a0)
sw t1, AON_CTRL_PM_CTRL(a0)
lw t1, AON_CTRL_PM_CTRL(a0)
li t1, (PM_WARM_CONFIG | PM_PWR_DOWN)
sw t1, AON_CTRL_PM_CTRL(a0)
lw t1, AON_CTRL_PM_CTRL(a0)
/* Enable CP0 interrupt 2 and wait for interrupt */
mfc0 t0, CP0_STATUS
li t1, ~(ST0_IM | ST0_IE)
and t0, t1
ori t0, STATUSF_IP2
mtc0 t0, CP0_STATUS
nop
nop
nop
ori t0, ST0_IE
mtc0 t0, CP0_STATUS
/* Wait for interrupt */
wait
nop
s3_reentry:
/* Clear call/return stack */
li t0, (0x06 << 16)
mtc0 t0, $22, 2
ssnop
ssnop
ssnop
/* Clear jump target buffer */
li t0, (0x04 << 16)
mtc0 t0, $22, 2
ssnop
ssnop
ssnop
sync
nop
/* Setup mmu defaults */
mtc0 zero, CP0_WIRED
mtc0 zero, CP0_ENTRYHI
li k0, PM_DEFAULT_MASK
mtc0 k0, CP0_PAGEMASK
li sp, BMIPS_WARM_RESTART_VEC
la k0, plat_wired_tlb_setup
jalr k0
nop
/* Restore general purpose registers */
la t0, gp_regs
lw fp, 44(t0)
lw sp, 40(t0)
lw gp, 36(t0)
lw s7, 32(t0)
lw s6, 28(t0)
lw s5, 24(t0)
lw s4, 20(t0)
lw s3, 16(t0)
lw s2, 12(t0)
lw s1, 8(t0)
lw s0, 4(t0)
lw ra, 0(t0)
/* Restore CP0 status */
lw t1, 48(t0)
mtc0 t1, CP0_STATUS
/* Return to caller */
li v0, 0
jr ra
nop
END(brcm_pm_do_s3)

View file

@ -213,6 +213,7 @@ static const struct of_device_id fsl_guts_of_match[] = {
{ .compatible = "fsl,ls1021a-dcfg", },
{ .compatible = "fsl,ls1043a-dcfg", },
{ .compatible = "fsl,ls2080a-dcfg", },
{ .compatible = "fsl,ls1088a-dcfg", },
{}
};
MODULE_DEVICE_TABLE(of, fsl_guts_of_match);

View file

@ -1,6 +1,6 @@
menuconfig FSL_DPAA
bool "Freescale DPAA 1.x support"
depends on FSL_SOC_BOOKE
depends on (FSL_SOC_BOOKE || ARCH_LAYERSCAPE)
select GENERIC_ALLOCATOR
help
The Freescale Data Path Acceleration Architecture (DPAA) is a set of

View file

@ -1,7 +1,7 @@
# SPDX-License-Identifier: GPL-2.0
obj-$(CONFIG_FSL_DPAA) += bman_ccsr.o qman_ccsr.o \
bman_portal.o qman_portal.o \
bman.o qman.o
bman.o qman.o dpaa_sys.o
obj-$(CONFIG_FSL_BMAN_TEST) += bman-test.o
bman-test-y = bman_test.o

View file

@ -35,6 +35,27 @@
/* Portal register assists */
#if defined(CONFIG_ARM) || defined(CONFIG_ARM64)
/* Cache-inhibited register offsets */
#define BM_REG_RCR_PI_CINH 0x3000
#define BM_REG_RCR_CI_CINH 0x3100
#define BM_REG_RCR_ITR 0x3200
#define BM_REG_CFG 0x3300
#define BM_REG_SCN(n) (0x3400 + ((n) << 6))
#define BM_REG_ISR 0x3e00
#define BM_REG_IER 0x3e40
#define BM_REG_ISDR 0x3e80
#define BM_REG_IIR 0x3ec0
/* Cache-enabled register offsets */
#define BM_CL_CR 0x0000
#define BM_CL_RR0 0x0100
#define BM_CL_RR1 0x0140
#define BM_CL_RCR 0x1000
#define BM_CL_RCR_PI_CENA 0x3000
#define BM_CL_RCR_CI_CENA 0x3100
#else
/* Cache-inhibited register offsets */
#define BM_REG_RCR_PI_CINH 0x0000
#define BM_REG_RCR_CI_CINH 0x0004
@ -53,6 +74,7 @@
#define BM_CL_RCR 0x1000
#define BM_CL_RCR_PI_CENA 0x3000
#define BM_CL_RCR_CI_CENA 0x3100
#endif
/*
* Portal modes.
@ -154,7 +176,8 @@ struct bm_mc {
};
struct bm_addr {
void __iomem *ce; /* cache-enabled */
void *ce; /* cache-enabled */
__be32 *ce_be; /* Same as above but for direct access */
void __iomem *ci; /* cache-inhibited */
};
@ -167,12 +190,12 @@ struct bm_portal {
/* Cache-inhibited register access. */
static inline u32 bm_in(struct bm_portal *p, u32 offset)
{
return be32_to_cpu(__raw_readl(p->addr.ci + offset));
return ioread32be(p->addr.ci + offset);
}
static inline void bm_out(struct bm_portal *p, u32 offset, u32 val)
{
__raw_writel(cpu_to_be32(val), p->addr.ci + offset);
iowrite32be(val, p->addr.ci + offset);
}
/* Cache Enabled Portal Access */
@ -188,7 +211,7 @@ static inline void bm_cl_touch_ro(struct bm_portal *p, u32 offset)
static inline u32 bm_ce_in(struct bm_portal *p, u32 offset)
{
return be32_to_cpu(__raw_readl(p->addr.ce + offset));
return be32_to_cpu(*(p->addr.ce_be + (offset/4)));
}
struct bman_portal {
@ -408,7 +431,7 @@ static int bm_mc_init(struct bm_portal *portal)
mc->cr = portal->addr.ce + BM_CL_CR;
mc->rr = portal->addr.ce + BM_CL_RR0;
mc->rridx = (__raw_readb(&mc->cr->_ncw_verb) & BM_MCC_VERB_VBIT) ?
mc->rridx = (mc->cr->_ncw_verb & BM_MCC_VERB_VBIT) ?
0 : 1;
mc->vbit = mc->rridx ? BM_MCC_VERB_VBIT : 0;
#ifdef CONFIG_FSL_DPAA_CHECKING
@ -466,7 +489,7 @@ static inline union bm_mc_result *bm_mc_result(struct bm_portal *portal)
* its command is submitted and completed. This includes the valid-bit,
* in case you were wondering...
*/
if (!__raw_readb(&rr->verb)) {
if (!rr->verb) {
dpaa_invalidate_touch_ro(rr);
return NULL;
}
@ -512,8 +535,9 @@ static int bman_create_portal(struct bman_portal *portal,
* config, everything that follows depends on it and "config" is more
* for (de)reference...
*/
p->addr.ce = c->addr_virt[DPAA_PORTAL_CE];
p->addr.ci = c->addr_virt[DPAA_PORTAL_CI];
p->addr.ce = c->addr_virt_ce;
p->addr.ce_be = c->addr_virt_ce;
p->addr.ci = c->addr_virt_ci;
if (bm_rcr_init(p, bm_rcr_pvb, bm_rcr_cce)) {
dev_err(c->dev, "RCR initialisation failed\n");
goto fail_rcr;
@ -607,7 +631,7 @@ int bman_p_irqsource_add(struct bman_portal *p, u32 bits)
unsigned long irqflags;
local_irq_save(irqflags);
set_bits(bits & BM_PIRQ_VISIBLE, &p->irq_sources);
p->irq_sources |= bits & BM_PIRQ_VISIBLE;
bm_out(&p->p, BM_REG_IER, p->irq_sources);
local_irq_restore(irqflags);
return 0;

View file

@ -201,6 +201,21 @@ static int fsl_bman_probe(struct platform_device *pdev)
return -ENODEV;
}
/*
* If FBPR memory wasn't defined using the qbman compatible string
* try using the of_reserved_mem_device method
*/
if (!fbpr_a) {
ret = qbman_init_private_mem(dev, 0, &fbpr_a, &fbpr_sz);
if (ret) {
dev_err(dev, "qbman_init_private_mem() failed 0x%x\n",
ret);
return -ENODEV;
}
}
dev_dbg(dev, "Allocated FBPR 0x%llx 0x%zx\n", fbpr_a, fbpr_sz);
bm_set_memory(fbpr_a, fbpr_sz);
err_irq = platform_get_irq(pdev, 0);

View file

@ -91,7 +91,6 @@ static int bman_portal_probe(struct platform_device *pdev)
struct device_node *node = dev->of_node;
struct bm_portal_config *pcfg;
struct resource *addr_phys[2];
void __iomem *va;
int irq, cpu;
pcfg = devm_kmalloc(dev, sizeof(*pcfg), GFP_KERNEL);
@ -123,23 +122,21 @@ static int bman_portal_probe(struct platform_device *pdev)
}
pcfg->irq = irq;
va = ioremap_prot(addr_phys[0]->start, resource_size(addr_phys[0]), 0);
if (!va) {
dev_err(dev, "ioremap::CE failed\n");
pcfg->addr_virt_ce = memremap(addr_phys[0]->start,
resource_size(addr_phys[0]),
QBMAN_MEMREMAP_ATTR);
if (!pcfg->addr_virt_ce) {
dev_err(dev, "memremap::CE failed\n");
goto err_ioremap1;
}
pcfg->addr_virt[DPAA_PORTAL_CE] = va;
va = ioremap_prot(addr_phys[1]->start, resource_size(addr_phys[1]),
_PAGE_GUARDED | _PAGE_NO_CACHE);
if (!va) {
pcfg->addr_virt_ci = ioremap(addr_phys[1]->start,
resource_size(addr_phys[1]));
if (!pcfg->addr_virt_ci) {
dev_err(dev, "ioremap::CI failed\n");
goto err_ioremap2;
}
pcfg->addr_virt[DPAA_PORTAL_CI] = va;
spin_lock(&bman_lock);
cpu = cpumask_next_zero(-1, &portal_cpus);
if (cpu >= nr_cpu_ids) {
@ -164,9 +161,9 @@ static int bman_portal_probe(struct platform_device *pdev)
return 0;
err_portal_init:
iounmap(pcfg->addr_virt[DPAA_PORTAL_CI]);
iounmap(pcfg->addr_virt_ci);
err_ioremap2:
iounmap(pcfg->addr_virt[DPAA_PORTAL_CE]);
memunmap(pcfg->addr_virt_ce);
err_ioremap1:
return -ENXIO;
}

View file

@ -46,11 +46,9 @@ extern u16 bman_ip_rev; /* 0 if uninitialised, otherwise BMAN_REVx */
extern struct gen_pool *bm_bpalloc;
struct bm_portal_config {
/*
* Corenet portal addresses;
* [0]==cache-enabled, [1]==cache-inhibited.
*/
void __iomem *addr_virt[2];
/* Portal addresses */
void *addr_virt_ce;
void __iomem *addr_virt_ci;
/* Allow these to be joined in lists */
struct list_head list;
struct device *dev;

View file

@ -0,0 +1,78 @@
/* Copyright 2017 NXP Semiconductor, Inc.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of NXP Semiconductor nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* ALTERNATIVELY, this software may be distributed under the terms of the
* GNU General Public License ("GPL") as published by the Free Software
* Foundation, either version 2 of that License or (at your option) any
* later version.
*
* THIS SOFTWARE IS PROVIDED BY NXP Semiconductor ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL NXP Semiconductor BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <linux/dma-mapping.h>
#include "dpaa_sys.h"
/*
* Initialize a devices private memory region
*/
int qbman_init_private_mem(struct device *dev, int idx, dma_addr_t *addr,
size_t *size)
{
int ret;
struct device_node *mem_node;
u64 size64;
ret = of_reserved_mem_device_init_by_idx(dev, dev->of_node, idx);
if (ret) {
dev_err(dev,
"of_reserved_mem_device_init_by_idx(%d) failed 0x%x\n",
idx, ret);
return -ENODEV;
}
mem_node = of_parse_phandle(dev->of_node, "memory-region", 0);
if (mem_node) {
ret = of_property_read_u64(mem_node, "size", &size64);
if (ret) {
dev_err(dev, "of_address_to_resource fails 0x%x\n",
ret);
return -ENODEV;
}
*size = size64;
} else {
dev_err(dev, "No memory-region found for index %d\n", idx);
return -ENODEV;
}
if (!dma_zalloc_coherent(dev, *size, addr, 0)) {
dev_err(dev, "DMA Alloc memory failed\n");
return -ENODEV;
}
/*
* Disassociate the reserved memory area from the device
* because a device can only have one DMA memory area. This
* should be fine since the memory is allocated and initialized
* and only ever accessed by the QBMan device from now on
*/
of_reserved_mem_device_release(dev);
return 0;
}

View file

@ -44,23 +44,21 @@
#include <linux/prefetch.h>
#include <linux/genalloc.h>
#include <asm/cacheflush.h>
#include <linux/io.h>
#include <linux/delay.h>
/* For 2-element tables related to cache-inhibited and cache-enabled mappings */
#define DPAA_PORTAL_CE 0
#define DPAA_PORTAL_CI 1
#if (L1_CACHE_BYTES != 32) && (L1_CACHE_BYTES != 64)
#error "Unsupported Cacheline Size"
#endif
static inline void dpaa_flush(void *p)
{
/*
* Only PPC needs to flush the cache currently - on ARM the mapping
* is non cacheable
*/
#ifdef CONFIG_PPC
flush_dcache_range((unsigned long)p, (unsigned long)p+64);
#elif defined(CONFIG_ARM32)
__cpuc_flush_dcache_area(p, 64);
#elif defined(CONFIG_ARM64)
__flush_dcache_area(p, 64);
#endif
}
@ -102,4 +100,15 @@ static inline u8 dpaa_cyc_diff(u8 ringsize, u8 first, u8 last)
/* Offset applied to genalloc pools due to zero being an error return */
#define DPAA_GENALLOC_OFF 0x80000000
/* Initialize the devices private memory region */
int qbman_init_private_mem(struct device *dev, int idx, dma_addr_t *addr,
size_t *size);
/* memremap() attributes for different platforms */
#ifdef CONFIG_PPC
#define QBMAN_MEMREMAP_ATTR MEMREMAP_WB
#else
#define QBMAN_MEMREMAP_ATTR MEMREMAP_WC
#endif
#endif /* __DPAA_SYS_H */

View file

@ -41,6 +41,43 @@
/* Portal register assists */
#if defined(CONFIG_ARM) || defined(CONFIG_ARM64)
/* Cache-inhibited register offsets */
#define QM_REG_EQCR_PI_CINH 0x3000
#define QM_REG_EQCR_CI_CINH 0x3040
#define QM_REG_EQCR_ITR 0x3080
#define QM_REG_DQRR_PI_CINH 0x3100
#define QM_REG_DQRR_CI_CINH 0x3140
#define QM_REG_DQRR_ITR 0x3180
#define QM_REG_DQRR_DCAP 0x31C0
#define QM_REG_DQRR_SDQCR 0x3200
#define QM_REG_DQRR_VDQCR 0x3240
#define QM_REG_DQRR_PDQCR 0x3280
#define QM_REG_MR_PI_CINH 0x3300
#define QM_REG_MR_CI_CINH 0x3340
#define QM_REG_MR_ITR 0x3380
#define QM_REG_CFG 0x3500
#define QM_REG_ISR 0x3600
#define QM_REG_IER 0x3640
#define QM_REG_ISDR 0x3680
#define QM_REG_IIR 0x36C0
#define QM_REG_ITPR 0x3740
/* Cache-enabled register offsets */
#define QM_CL_EQCR 0x0000
#define QM_CL_DQRR 0x1000
#define QM_CL_MR 0x2000
#define QM_CL_EQCR_PI_CENA 0x3000
#define QM_CL_EQCR_CI_CENA 0x3040
#define QM_CL_DQRR_PI_CENA 0x3100
#define QM_CL_DQRR_CI_CENA 0x3140
#define QM_CL_MR_PI_CENA 0x3300
#define QM_CL_MR_CI_CENA 0x3340
#define QM_CL_CR 0x3800
#define QM_CL_RR0 0x3900
#define QM_CL_RR1 0x3940
#else
/* Cache-inhibited register offsets */
#define QM_REG_EQCR_PI_CINH 0x0000
#define QM_REG_EQCR_CI_CINH 0x0004
@ -75,6 +112,7 @@
#define QM_CL_CR 0x3800
#define QM_CL_RR0 0x3900
#define QM_CL_RR1 0x3940
#endif
/*
* BTW, the drivers (and h/w programming model) already obtain the required
@ -300,7 +338,8 @@ struct qm_mc {
};
struct qm_addr {
void __iomem *ce; /* cache-enabled */
void *ce; /* cache-enabled */
__be32 *ce_be; /* same value as above but for direct access */
void __iomem *ci; /* cache-inhibited */
};
@ -321,12 +360,12 @@ struct qm_portal {
/* Cache-inhibited register access. */
static inline u32 qm_in(struct qm_portal *p, u32 offset)
{
return be32_to_cpu(__raw_readl(p->addr.ci + offset));
return ioread32be(p->addr.ci + offset);
}
static inline void qm_out(struct qm_portal *p, u32 offset, u32 val)
{
__raw_writel(cpu_to_be32(val), p->addr.ci + offset);
iowrite32be(val, p->addr.ci + offset);
}
/* Cache Enabled Portal Access */
@ -342,7 +381,7 @@ static inline void qm_cl_touch_ro(struct qm_portal *p, u32 offset)
static inline u32 qm_ce_in(struct qm_portal *p, u32 offset)
{
return be32_to_cpu(__raw_readl(p->addr.ce + offset));
return be32_to_cpu(*(p->addr.ce_be + (offset/4)));
}
/* --- EQCR API --- */
@ -646,11 +685,7 @@ static inline void qm_dqrr_pvb_update(struct qm_portal *portal)
*/
dpaa_invalidate_touch_ro(res);
#endif
/*
* when accessing 'verb', use __raw_readb() to ensure that compiler
* inlining doesn't try to optimise out "excess reads".
*/
if ((__raw_readb(&res->verb) & QM_DQRR_VERB_VBIT) == dqrr->vbit) {
if ((res->verb & QM_DQRR_VERB_VBIT) == dqrr->vbit) {
dqrr->pi = (dqrr->pi + 1) & (QM_DQRR_SIZE - 1);
if (!dqrr->pi)
dqrr->vbit ^= QM_DQRR_VERB_VBIT;
@ -777,11 +812,8 @@ static inline void qm_mr_pvb_update(struct qm_portal *portal)
union qm_mr_entry *res = qm_cl(mr->ring, mr->pi);
DPAA_ASSERT(mr->pmode == qm_mr_pvb);
/*
* when accessing 'verb', use __raw_readb() to ensure that compiler
* inlining doesn't try to optimise out "excess reads".
*/
if ((__raw_readb(&res->verb) & QM_MR_VERB_VBIT) == mr->vbit) {
if ((res->verb & QM_MR_VERB_VBIT) == mr->vbit) {
mr->pi = (mr->pi + 1) & (QM_MR_SIZE - 1);
if (!mr->pi)
mr->vbit ^= QM_MR_VERB_VBIT;
@ -822,7 +854,7 @@ static inline int qm_mc_init(struct qm_portal *portal)
mc->cr = portal->addr.ce + QM_CL_CR;
mc->rr = portal->addr.ce + QM_CL_RR0;
mc->rridx = (__raw_readb(&mc->cr->_ncw_verb) & QM_MCC_VERB_VBIT)
mc->rridx = (mc->cr->_ncw_verb & QM_MCC_VERB_VBIT)
? 0 : 1;
mc->vbit = mc->rridx ? QM_MCC_VERB_VBIT : 0;
#ifdef CONFIG_FSL_DPAA_CHECKING
@ -880,7 +912,7 @@ static inline union qm_mc_result *qm_mc_result(struct qm_portal *portal)
* its command is submitted and completed. This includes the valid-bit,
* in case you were wondering...
*/
if (!__raw_readb(&rr->verb)) {
if (!rr->verb) {
dpaa_invalidate_touch_ro(rr);
return NULL;
}
@ -909,12 +941,12 @@ static inline int qm_mc_result_timeout(struct qm_portal *portal,
static inline void fq_set(struct qman_fq *fq, u32 mask)
{
set_bits(mask, &fq->flags);
fq->flags |= mask;
}
static inline void fq_clear(struct qman_fq *fq, u32 mask)
{
clear_bits(mask, &fq->flags);
fq->flags &= ~mask;
}
static inline int fq_isset(struct qman_fq *fq, u32 mask)
@ -1084,11 +1116,7 @@ loop:
* entries well before the ring has been fully consumed, so
* we're being *really* paranoid here.
*/
u64 now, then = jiffies;
do {
now = jiffies;
} while ((then + 10000) > now);
msleep(1);
msg = qm_mr_current(p);
if (!msg)
return 0;
@ -1124,8 +1152,9 @@ static int qman_create_portal(struct qman_portal *portal,
* config, everything that follows depends on it and "config" is more
* for (de)reference
*/
p->addr.ce = c->addr_virt[DPAA_PORTAL_CE];
p->addr.ci = c->addr_virt[DPAA_PORTAL_CI];
p->addr.ce = c->addr_virt_ce;
p->addr.ce_be = c->addr_virt_ce;
p->addr.ci = c->addr_virt_ci;
/*
* If CI-stashing is used, the current defaults use a threshold of 3,
* and stash with high-than-DQRR priority.
@ -1566,7 +1595,7 @@ void qman_p_irqsource_add(struct qman_portal *p, u32 bits)
unsigned long irqflags;
local_irq_save(irqflags);
set_bits(bits & QM_PIRQ_VISIBLE, &p->irq_sources);
p->irq_sources |= bits & QM_PIRQ_VISIBLE;
qm_out(&p->p, QM_REG_IER, p->irq_sources);
local_irq_restore(irqflags);
}
@ -1589,7 +1618,7 @@ void qman_p_irqsource_remove(struct qman_portal *p, u32 bits)
*/
local_irq_save(irqflags);
bits &= QM_PIRQ_VISIBLE;
clear_bits(bits, &p->irq_sources);
p->irq_sources &= ~bits;
qm_out(&p->p, QM_REG_IER, p->irq_sources);
ier = qm_in(&p->p, QM_REG_IER);
/*

View file

@ -401,21 +401,42 @@ static int qm_init_pfdr(struct device *dev, u32 pfdr_start, u32 num)
}
/*
* Ideally we would use the DMA API to turn rmem->base into a DMA address
* (especially if iommu translations ever get involved). Unfortunately, the
* DMA API currently does not allow mapping anything that is not backed with
* a struct page.
* QMan needs two global memory areas initialized at boot time:
* 1) FQD: Frame Queue Descriptors used to manage frame queues
* 2) PFDR: Packed Frame Queue Descriptor Records used to store frames
* Both areas are reserved using the device tree reserved memory framework
* and the addresses and sizes are initialized when the QMan device is probed
*/
static dma_addr_t fqd_a, pfdr_a;
static size_t fqd_sz, pfdr_sz;
#ifdef CONFIG_PPC
/*
* Support for PPC Device Tree backward compatibility when compatible
* string is set to fsl-qman-fqd and fsl-qman-pfdr
*/
static int zero_priv_mem(phys_addr_t addr, size_t sz)
{
/* map as cacheable, non-guarded */
void __iomem *tmpp = ioremap_prot(addr, sz, 0);
if (!tmpp)
return -ENOMEM;
memset_io(tmpp, 0, sz);
flush_dcache_range((unsigned long)tmpp,
(unsigned long)tmpp + sz);
iounmap(tmpp);
return 0;
}
static int qman_fqd(struct reserved_mem *rmem)
{
fqd_a = rmem->base;
fqd_sz = rmem->size;
WARN_ON(!(fqd_a && fqd_sz));
return 0;
}
RESERVEDMEM_OF_DECLARE(qman_fqd, "fsl,qman-fqd", qman_fqd);
@ -431,32 +452,13 @@ static int qman_pfdr(struct reserved_mem *rmem)
}
RESERVEDMEM_OF_DECLARE(qman_pfdr, "fsl,qman-pfdr", qman_pfdr);
#endif
static unsigned int qm_get_fqid_maxcnt(void)
{
return fqd_sz / 64;
}
/*
* Flush this memory range from data cache so that QMAN originated
* transactions for this memory region could be marked non-coherent.
*/
static int zero_priv_mem(struct device *dev, struct device_node *node,
phys_addr_t addr, size_t sz)
{
/* map as cacheable, non-guarded */
void __iomem *tmpp = ioremap_prot(addr, sz, 0);
if (!tmpp)
return -ENOMEM;
memset_io(tmpp, 0, sz);
flush_dcache_range((unsigned long)tmpp,
(unsigned long)tmpp + sz);
iounmap(tmpp);
return 0;
}
static void log_edata_bits(struct device *dev, u32 bit_count)
{
u32 i, j, mask = 0xffffffff;
@ -717,6 +719,8 @@ static int fsl_qman_probe(struct platform_device *pdev)
qman_ip_rev = QMAN_REV30;
else if (major == 3 && minor == 1)
qman_ip_rev = QMAN_REV31;
else if (major == 3 && minor == 2)
qman_ip_rev = QMAN_REV32;
else {
dev_err(dev, "Unknown QMan version\n");
return -ENODEV;
@ -727,10 +731,41 @@ static int fsl_qman_probe(struct platform_device *pdev)
qm_channel_caam = QMAN_CHANNEL_CAAM_REV3;
}
ret = zero_priv_mem(dev, node, fqd_a, fqd_sz);
WARN_ON(ret);
if (ret)
return -ENODEV;
if (fqd_a) {
#ifdef CONFIG_PPC
/*
* For PPC backward DT compatibility
* FQD memory MUST be zero'd by software
*/
zero_priv_mem(fqd_a, fqd_sz);
#else
WARN(1, "Unexpected architecture using non shared-dma-mem reservations");
#endif
} else {
/*
* Order of memory regions is assumed as FQD followed by PFDR
* in order to ensure allocations from the correct regions the
* driver initializes then allocates each piece in order
*/
ret = qbman_init_private_mem(dev, 0, &fqd_a, &fqd_sz);
if (ret) {
dev_err(dev, "qbman_init_private_mem() for FQD failed 0x%x\n",
ret);
return -ENODEV;
}
}
dev_dbg(dev, "Allocated FQD 0x%llx 0x%zx\n", fqd_a, fqd_sz);
if (!pfdr_a) {
/* Setup PFDR memory */
ret = qbman_init_private_mem(dev, 1, &pfdr_a, &pfdr_sz);
if (ret) {
dev_err(dev, "qbman_init_private_mem() for PFDR failed 0x%x\n",
ret);
return -ENODEV;
}
}
dev_dbg(dev, "Allocated PFDR 0x%llx 0x%zx\n", pfdr_a, pfdr_sz);
ret = qman_init_ccsr(dev);
if (ret) {

View file

@ -224,7 +224,6 @@ static int qman_portal_probe(struct platform_device *pdev)
struct device_node *node = dev->of_node;
struct qm_portal_config *pcfg;
struct resource *addr_phys[2];
void __iomem *va;
int irq, cpu, err;
u32 val;
@ -262,23 +261,21 @@ static int qman_portal_probe(struct platform_device *pdev)
}
pcfg->irq = irq;
va = ioremap_prot(addr_phys[0]->start, resource_size(addr_phys[0]), 0);
if (!va) {
dev_err(dev, "ioremap::CE failed\n");
pcfg->addr_virt_ce = memremap(addr_phys[0]->start,
resource_size(addr_phys[0]),
QBMAN_MEMREMAP_ATTR);
if (!pcfg->addr_virt_ce) {
dev_err(dev, "memremap::CE failed\n");
goto err_ioremap1;
}
pcfg->addr_virt[DPAA_PORTAL_CE] = va;
va = ioremap_prot(addr_phys[1]->start, resource_size(addr_phys[1]),
_PAGE_GUARDED | _PAGE_NO_CACHE);
if (!va) {
pcfg->addr_virt_ci = ioremap(addr_phys[1]->start,
resource_size(addr_phys[1]));
if (!pcfg->addr_virt_ci) {
dev_err(dev, "ioremap::CI failed\n");
goto err_ioremap2;
}
pcfg->addr_virt[DPAA_PORTAL_CI] = va;
pcfg->pools = qm_get_pools_sdqcr();
spin_lock(&qman_lock);
@ -310,9 +307,9 @@ static int qman_portal_probe(struct platform_device *pdev)
return 0;
err_portal_init:
iounmap(pcfg->addr_virt[DPAA_PORTAL_CI]);
iounmap(pcfg->addr_virt_ci);
err_ioremap2:
iounmap(pcfg->addr_virt[DPAA_PORTAL_CE]);
memunmap(pcfg->addr_virt_ce);
err_ioremap1:
return -ENXIO;
}

View file

@ -28,8 +28,6 @@
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include "dpaa_sys.h"
#include <soc/fsl/qman.h>
@ -155,11 +153,9 @@ static inline void qman_cgrs_xor(struct qman_cgrs *dest,
void qman_init_cgr_all(void);
struct qm_portal_config {
/*
* Corenet portal addresses;
* [0]==cache-enabled, [1]==cache-inhibited.
*/
void __iomem *addr_virt[2];
/* Portal addresses */
void *addr_virt_ce;
void __iomem *addr_virt_ci;
struct device *dev;
struct iommu_domain *iommu_domain;
/* Allow these to be joined in lists */
@ -187,6 +183,7 @@ struct qm_portal_config {
#define QMAN_REV20 0x0200
#define QMAN_REV30 0x0300
#define QMAN_REV31 0x0301
#define QMAN_REV32 0x0302
extern u16 qman_ip_rev; /* 0 if uninitialised, otherwise QMAN_REVx */
#define QM_FQID_RANGE_START 1 /* FQID 0 reserved for internal use */

View file

@ -30,7 +30,5 @@
#include "qman_priv.h"
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
int qman_test_stash(void);
int qman_test_api(void);

View file

@ -1,9 +1,11 @@
#
# MediaTek SoC drivers
#
menu "MediaTek SoC drivers"
depends on ARCH_MEDIATEK || COMPILE_TEST
config MTK_INFRACFG
bool "MediaTek INFRACFG Support"
depends on ARCH_MEDIATEK || COMPILE_TEST
select REGMAP
help
Say yes here to add support for the MediaTek INFRACFG controller. The
@ -12,7 +14,6 @@ config MTK_INFRACFG
config MTK_PMIC_WRAP
tristate "MediaTek PMIC Wrapper Support"
depends on ARCH_MEDIATEK
depends on RESET_CONTROLLER
select REGMAP
help
@ -22,7 +23,6 @@ config MTK_PMIC_WRAP
config MTK_SCPSYS
bool "MediaTek SCPSYS Support"
depends on ARCH_MEDIATEK || COMPILE_TEST
default ARCH_MEDIATEK
select REGMAP
select MTK_INFRACFG
@ -30,3 +30,5 @@ config MTK_SCPSYS
help
Say yes here to add support for the MediaTek SCPSYS power domain
driver.
endmenu

View file

@ -70,6 +70,12 @@
PWRAP_WDT_SRC_EN_HARB_STAUPD_DLE | \
PWRAP_WDT_SRC_EN_HARB_STAUPD_ALE)
/* Group of bits used for shown slave capability */
#define PWRAP_SLV_CAP_SPI BIT(0)
#define PWRAP_SLV_CAP_DUALIO BIT(1)
#define PWRAP_SLV_CAP_SECURITY BIT(2)
#define HAS_CAP(_c, _x) (((_c) & (_x)) == (_x))
/* defines for slave device wrapper registers */
enum dew_regs {
PWRAP_DEW_BASE,
@ -208,6 +214,36 @@ enum pwrap_regs {
PWRAP_ADC_RDATA_ADDR1,
PWRAP_ADC_RDATA_ADDR2,
/* MT7622 only regs */
PWRAP_EINT_STA0_ADR,
PWRAP_EINT_STA1_ADR,
PWRAP_STA,
PWRAP_CLR,
PWRAP_DVFS_ADR8,
PWRAP_DVFS_WDATA8,
PWRAP_DVFS_ADR9,
PWRAP_DVFS_WDATA9,
PWRAP_DVFS_ADR10,
PWRAP_DVFS_WDATA10,
PWRAP_DVFS_ADR11,
PWRAP_DVFS_WDATA11,
PWRAP_DVFS_ADR12,
PWRAP_DVFS_WDATA12,
PWRAP_DVFS_ADR13,
PWRAP_DVFS_WDATA13,
PWRAP_DVFS_ADR14,
PWRAP_DVFS_WDATA14,
PWRAP_DVFS_ADR15,
PWRAP_DVFS_WDATA15,
PWRAP_EXT_CK,
PWRAP_ADC_RDATA_ADDR,
PWRAP_GPS_STA,
PWRAP_SW_RST,
PWRAP_DVFS_STEP_CTRL0,
PWRAP_DVFS_STEP_CTRL1,
PWRAP_DVFS_STEP_CTRL2,
PWRAP_SPI2_CTRL,
/* MT8135 only regs */
PWRAP_CSHEXT,
PWRAP_EVENT_IN_EN,
@ -330,6 +366,118 @@ static int mt2701_regs[] = {
[PWRAP_ADC_RDATA_ADDR2] = 0x154,
};
static int mt7622_regs[] = {
[PWRAP_MUX_SEL] = 0x0,
[PWRAP_WRAP_EN] = 0x4,
[PWRAP_DIO_EN] = 0x8,
[PWRAP_SIDLY] = 0xC,
[PWRAP_RDDMY] = 0x10,
[PWRAP_SI_CK_CON] = 0x14,
[PWRAP_CSHEXT_WRITE] = 0x18,
[PWRAP_CSHEXT_READ] = 0x1C,
[PWRAP_CSLEXT_START] = 0x20,
[PWRAP_CSLEXT_END] = 0x24,
[PWRAP_STAUPD_PRD] = 0x28,
[PWRAP_STAUPD_GRPEN] = 0x2C,
[PWRAP_EINT_STA0_ADR] = 0x30,
[PWRAP_EINT_STA1_ADR] = 0x34,
[PWRAP_STA] = 0x38,
[PWRAP_CLR] = 0x3C,
[PWRAP_STAUPD_MAN_TRIG] = 0x40,
[PWRAP_STAUPD_STA] = 0x44,
[PWRAP_WRAP_STA] = 0x48,
[PWRAP_HARB_INIT] = 0x4C,
[PWRAP_HARB_HPRIO] = 0x50,
[PWRAP_HIPRIO_ARB_EN] = 0x54,
[PWRAP_HARB_STA0] = 0x58,
[PWRAP_HARB_STA1] = 0x5C,
[PWRAP_MAN_EN] = 0x60,
[PWRAP_MAN_CMD] = 0x64,
[PWRAP_MAN_RDATA] = 0x68,
[PWRAP_MAN_VLDCLR] = 0x6C,
[PWRAP_WACS0_EN] = 0x70,
[PWRAP_INIT_DONE0] = 0x74,
[PWRAP_WACS0_CMD] = 0x78,
[PWRAP_WACS0_RDATA] = 0x7C,
[PWRAP_WACS0_VLDCLR] = 0x80,
[PWRAP_WACS1_EN] = 0x84,
[PWRAP_INIT_DONE1] = 0x88,
[PWRAP_WACS1_CMD] = 0x8C,
[PWRAP_WACS1_RDATA] = 0x90,
[PWRAP_WACS1_VLDCLR] = 0x94,
[PWRAP_WACS2_EN] = 0x98,
[PWRAP_INIT_DONE2] = 0x9C,
[PWRAP_WACS2_CMD] = 0xA0,
[PWRAP_WACS2_RDATA] = 0xA4,
[PWRAP_WACS2_VLDCLR] = 0xA8,
[PWRAP_INT_EN] = 0xAC,
[PWRAP_INT_FLG_RAW] = 0xB0,
[PWRAP_INT_FLG] = 0xB4,
[PWRAP_INT_CLR] = 0xB8,
[PWRAP_SIG_ADR] = 0xBC,
[PWRAP_SIG_MODE] = 0xC0,
[PWRAP_SIG_VALUE] = 0xC4,
[PWRAP_SIG_ERRVAL] = 0xC8,
[PWRAP_CRC_EN] = 0xCC,
[PWRAP_TIMER_EN] = 0xD0,
[PWRAP_TIMER_STA] = 0xD4,
[PWRAP_WDT_UNIT] = 0xD8,
[PWRAP_WDT_SRC_EN] = 0xDC,
[PWRAP_WDT_FLG] = 0xE0,
[PWRAP_DEBUG_INT_SEL] = 0xE4,
[PWRAP_DVFS_ADR0] = 0xE8,
[PWRAP_DVFS_WDATA0] = 0xEC,
[PWRAP_DVFS_ADR1] = 0xF0,
[PWRAP_DVFS_WDATA1] = 0xF4,
[PWRAP_DVFS_ADR2] = 0xF8,
[PWRAP_DVFS_WDATA2] = 0xFC,
[PWRAP_DVFS_ADR3] = 0x100,
[PWRAP_DVFS_WDATA3] = 0x104,
[PWRAP_DVFS_ADR4] = 0x108,
[PWRAP_DVFS_WDATA4] = 0x10C,
[PWRAP_DVFS_ADR5] = 0x110,
[PWRAP_DVFS_WDATA5] = 0x114,
[PWRAP_DVFS_ADR6] = 0x118,
[PWRAP_DVFS_WDATA6] = 0x11C,
[PWRAP_DVFS_ADR7] = 0x120,
[PWRAP_DVFS_WDATA7] = 0x124,
[PWRAP_DVFS_ADR8] = 0x128,
[PWRAP_DVFS_WDATA8] = 0x12C,
[PWRAP_DVFS_ADR9] = 0x130,
[PWRAP_DVFS_WDATA9] = 0x134,
[PWRAP_DVFS_ADR10] = 0x138,
[PWRAP_DVFS_WDATA10] = 0x13C,
[PWRAP_DVFS_ADR11] = 0x140,
[PWRAP_DVFS_WDATA11] = 0x144,
[PWRAP_DVFS_ADR12] = 0x148,
[PWRAP_DVFS_WDATA12] = 0x14C,
[PWRAP_DVFS_ADR13] = 0x150,
[PWRAP_DVFS_WDATA13] = 0x154,
[PWRAP_DVFS_ADR14] = 0x158,
[PWRAP_DVFS_WDATA14] = 0x15C,
[PWRAP_DVFS_ADR15] = 0x160,
[PWRAP_DVFS_WDATA15] = 0x164,
[PWRAP_SPMINF_STA] = 0x168,
[PWRAP_CIPHER_KEY_SEL] = 0x16C,
[PWRAP_CIPHER_IV_SEL] = 0x170,
[PWRAP_CIPHER_EN] = 0x174,
[PWRAP_CIPHER_RDY] = 0x178,
[PWRAP_CIPHER_MODE] = 0x17C,
[PWRAP_CIPHER_SWRST] = 0x180,
[PWRAP_DCM_EN] = 0x184,
[PWRAP_DCM_DBC_PRD] = 0x188,
[PWRAP_EXT_CK] = 0x18C,
[PWRAP_ADC_CMD_ADDR] = 0x190,
[PWRAP_PWRAP_ADC_CMD] = 0x194,
[PWRAP_ADC_RDATA_ADDR] = 0x198,
[PWRAP_GPS_STA] = 0x19C,
[PWRAP_SW_RST] = 0x1A0,
[PWRAP_DVFS_STEP_CTRL0] = 0x238,
[PWRAP_DVFS_STEP_CTRL1] = 0x23C,
[PWRAP_DVFS_STEP_CTRL2] = 0x240,
[PWRAP_SPI2_CTRL] = 0x244,
};
static int mt8173_regs[] = {
[PWRAP_MUX_SEL] = 0x0,
[PWRAP_WRAP_EN] = 0x4,
@ -487,18 +635,31 @@ static int mt8135_regs[] = {
enum pmic_type {
PMIC_MT6323,
PMIC_MT6380,
PMIC_MT6397,
};
enum pwrap_type {
PWRAP_MT2701,
PWRAP_MT7622,
PWRAP_MT8135,
PWRAP_MT8173,
};
struct pmic_wrapper;
struct pwrap_slv_type {
const u32 *dew_regs;
enum pmic_type type;
const struct regmap_config *regmap;
/* Flags indicating the capability for the target slave */
u32 caps;
/*
* pwrap operations are highly associated with the PMIC types,
* so the pointers added increases flexibility allowing determination
* which type is used by the detection through device tree.
*/
int (*pwrap_read)(struct pmic_wrapper *wrp, u32 adr, u32 *rdata);
int (*pwrap_write)(struct pmic_wrapper *wrp, u32 adr, u32 wdata);
};
struct pmic_wrapper {
@ -522,7 +683,7 @@ struct pmic_wrapper_type {
u32 int_en_all;
u32 spi_w;
u32 wdt_src;
int has_bridge:1;
unsigned int has_bridge:1;
int (*init_reg_clock)(struct pmic_wrapper *wrp);
int (*init_soc_specific)(struct pmic_wrapper *wrp);
};
@ -593,23 +754,7 @@ static int pwrap_wait_for_state(struct pmic_wrapper *wrp,
} while (1);
}
static int pwrap_write(struct pmic_wrapper *wrp, u32 adr, u32 wdata)
{
int ret;
ret = pwrap_wait_for_state(wrp, pwrap_is_fsm_idle);
if (ret) {
pwrap_leave_fsm_vldclr(wrp);
return ret;
}
pwrap_writel(wrp, (1 << 31) | ((adr >> 1) << 16) | wdata,
PWRAP_WACS2_CMD);
return 0;
}
static int pwrap_read(struct pmic_wrapper *wrp, u32 adr, u32 *rdata)
static int pwrap_read16(struct pmic_wrapper *wrp, u32 adr, u32 *rdata)
{
int ret;
@ -632,6 +777,89 @@ static int pwrap_read(struct pmic_wrapper *wrp, u32 adr, u32 *rdata)
return 0;
}
static int pwrap_read32(struct pmic_wrapper *wrp, u32 adr, u32 *rdata)
{
int ret, msb;
*rdata = 0;
for (msb = 0; msb < 2; msb++) {
ret = pwrap_wait_for_state(wrp, pwrap_is_fsm_idle);
if (ret) {
pwrap_leave_fsm_vldclr(wrp);
return ret;
}
pwrap_writel(wrp, ((msb << 30) | (adr << 16)),
PWRAP_WACS2_CMD);
ret = pwrap_wait_for_state(wrp, pwrap_is_fsm_vldclr);
if (ret)
return ret;
*rdata += (PWRAP_GET_WACS_RDATA(pwrap_readl(wrp,
PWRAP_WACS2_RDATA)) << (16 * msb));
pwrap_writel(wrp, 1, PWRAP_WACS2_VLDCLR);
}
return 0;
}
static int pwrap_read(struct pmic_wrapper *wrp, u32 adr, u32 *rdata)
{
return wrp->slave->pwrap_read(wrp, adr, rdata);
}
static int pwrap_write16(struct pmic_wrapper *wrp, u32 adr, u32 wdata)
{
int ret;
ret = pwrap_wait_for_state(wrp, pwrap_is_fsm_idle);
if (ret) {
pwrap_leave_fsm_vldclr(wrp);
return ret;
}
pwrap_writel(wrp, (1 << 31) | ((adr >> 1) << 16) | wdata,
PWRAP_WACS2_CMD);
return 0;
}
static int pwrap_write32(struct pmic_wrapper *wrp, u32 adr, u32 wdata)
{
int ret, msb, rdata;
for (msb = 0; msb < 2; msb++) {
ret = pwrap_wait_for_state(wrp, pwrap_is_fsm_idle);
if (ret) {
pwrap_leave_fsm_vldclr(wrp);
return ret;
}
pwrap_writel(wrp, (1 << 31) | (msb << 30) | (adr << 16) |
((wdata >> (msb * 16)) & 0xffff),
PWRAP_WACS2_CMD);
/*
* The pwrap_read operation is the requirement of hardware used
* for the synchronization between two successive 16-bit
* pwrap_writel operations composing one 32-bit bus writing.
* Otherwise, we'll find the result fails on the lower 16-bit
* pwrap writing.
*/
if (!msb)
pwrap_read(wrp, adr, &rdata);
}
return 0;
}
static int pwrap_write(struct pmic_wrapper *wrp, u32 adr, u32 wdata)
{
return wrp->slave->pwrap_write(wrp, adr, wdata);
}
static int pwrap_regmap_read(void *context, u32 adr, u32 *rdata)
{
return pwrap_read(context, adr, rdata);
@ -711,23 +939,75 @@ static int pwrap_init_sidly(struct pmic_wrapper *wrp)
return 0;
}
static int pwrap_mt8135_init_reg_clock(struct pmic_wrapper *wrp)
static int pwrap_init_dual_io(struct pmic_wrapper *wrp)
{
pwrap_writel(wrp, 0x4, PWRAP_CSHEXT);
pwrap_writel(wrp, 0x0, PWRAP_CSHEXT_WRITE);
pwrap_writel(wrp, 0x4, PWRAP_CSHEXT_READ);
pwrap_writel(wrp, 0x0, PWRAP_CSLEXT_START);
pwrap_writel(wrp, 0x0, PWRAP_CSLEXT_END);
int ret;
u32 rdata;
/* Enable dual IO mode */
pwrap_write(wrp, wrp->slave->dew_regs[PWRAP_DEW_DIO_EN], 1);
/* Check IDLE & INIT_DONE in advance */
ret = pwrap_wait_for_state(wrp,
pwrap_is_fsm_idle_and_sync_idle);
if (ret) {
dev_err(wrp->dev, "%s fail, ret=%d\n", __func__, ret);
return ret;
}
pwrap_writel(wrp, 1, PWRAP_DIO_EN);
/* Read Test */
pwrap_read(wrp,
wrp->slave->dew_regs[PWRAP_DEW_READ_TEST], &rdata);
if (rdata != PWRAP_DEW_READ_TEST_VAL) {
dev_err(wrp->dev,
"Read failed on DIO mode: 0x%04x!=0x%04x\n",
PWRAP_DEW_READ_TEST_VAL, rdata);
return -EFAULT;
}
return 0;
}
static int pwrap_mt8173_init_reg_clock(struct pmic_wrapper *wrp)
/*
* pwrap_init_chip_select_ext is used to configure CS extension time for each
* phase during data transactions on the pwrap bus.
*/
static void pwrap_init_chip_select_ext(struct pmic_wrapper *wrp, u8 hext_write,
u8 hext_read, u8 lext_start,
u8 lext_end)
{
pwrap_writel(wrp, 0x0, PWRAP_CSHEXT_WRITE);
pwrap_writel(wrp, 0x4, PWRAP_CSHEXT_READ);
pwrap_writel(wrp, 0x2, PWRAP_CSLEXT_START);
pwrap_writel(wrp, 0x2, PWRAP_CSLEXT_END);
/*
* After finishing a write and read transaction, extends CS high time
* to be at least xT of BUS CLK as hext_write and hext_read specifies
* respectively.
*/
pwrap_writel(wrp, hext_write, PWRAP_CSHEXT_WRITE);
pwrap_writel(wrp, hext_read, PWRAP_CSHEXT_READ);
/*
* Extends CS low time after CSL and before CSH command to be at
* least xT of BUS CLK as lext_start and lext_end specifies
* respectively.
*/
pwrap_writel(wrp, lext_start, PWRAP_CSLEXT_START);
pwrap_writel(wrp, lext_end, PWRAP_CSLEXT_END);
}
static int pwrap_common_init_reg_clock(struct pmic_wrapper *wrp)
{
switch (wrp->master->type) {
case PWRAP_MT8173:
pwrap_init_chip_select_ext(wrp, 0, 4, 2, 2);
break;
case PWRAP_MT8135:
pwrap_writel(wrp, 0x4, PWRAP_CSHEXT);
pwrap_init_chip_select_ext(wrp, 0, 4, 0, 0);
break;
default:
break;
}
return 0;
}
@ -737,20 +1017,16 @@ static int pwrap_mt2701_init_reg_clock(struct pmic_wrapper *wrp)
switch (wrp->slave->type) {
case PMIC_MT6397:
pwrap_writel(wrp, 0xc, PWRAP_RDDMY);
pwrap_writel(wrp, 0x4, PWRAP_CSHEXT_WRITE);
pwrap_writel(wrp, 0x0, PWRAP_CSHEXT_READ);
pwrap_writel(wrp, 0x2, PWRAP_CSLEXT_START);
pwrap_writel(wrp, 0x2, PWRAP_CSLEXT_END);
pwrap_init_chip_select_ext(wrp, 4, 0, 2, 2);
break;
case PMIC_MT6323:
pwrap_writel(wrp, 0x8, PWRAP_RDDMY);
pwrap_write(wrp, wrp->slave->dew_regs[PWRAP_DEW_RDDMY_NO],
0x8);
pwrap_writel(wrp, 0x5, PWRAP_CSHEXT_WRITE);
pwrap_writel(wrp, 0x0, PWRAP_CSHEXT_READ);
pwrap_writel(wrp, 0x2, PWRAP_CSLEXT_START);
pwrap_writel(wrp, 0x2, PWRAP_CSLEXT_END);
pwrap_init_chip_select_ext(wrp, 5, 0, 2, 2);
break;
default:
break;
}
@ -794,6 +1070,9 @@ static int pwrap_init_cipher(struct pmic_wrapper *wrp)
case PWRAP_MT8173:
pwrap_writel(wrp, 1, PWRAP_CIPHER_EN);
break;
case PWRAP_MT7622:
pwrap_writel(wrp, 0, PWRAP_CIPHER_EN);
break;
}
/* Config cipher mode @PMIC */
@ -815,6 +1094,8 @@ static int pwrap_init_cipher(struct pmic_wrapper *wrp)
pwrap_write(wrp, wrp->slave->dew_regs[PWRAP_DEW_CIPHER_EN],
0x1);
break;
default:
break;
}
/* wait for cipher data ready@AP */
@ -827,7 +1108,8 @@ static int pwrap_init_cipher(struct pmic_wrapper *wrp)
/* wait for cipher data ready@PMIC */
ret = pwrap_wait_for_state(wrp, pwrap_is_pmic_cipher_ready);
if (ret) {
dev_err(wrp->dev, "timeout waiting for cipher data ready@PMIC\n");
dev_err(wrp->dev,
"timeout waiting for cipher data ready@PMIC\n");
return ret;
}
@ -854,6 +1136,30 @@ static int pwrap_init_cipher(struct pmic_wrapper *wrp)
return 0;
}
static int pwrap_init_security(struct pmic_wrapper *wrp)
{
int ret;
/* Enable encryption */
ret = pwrap_init_cipher(wrp);
if (ret)
return ret;
/* Signature checking - using CRC */
if (pwrap_write(wrp,
wrp->slave->dew_regs[PWRAP_DEW_CRC_EN], 0x1))
return -EFAULT;
pwrap_writel(wrp, 0x1, PWRAP_CRC_EN);
pwrap_writel(wrp, 0x0, PWRAP_SIG_MODE);
pwrap_writel(wrp, wrp->slave->dew_regs[PWRAP_DEW_CRC_VAL],
PWRAP_SIG_ADR);
pwrap_writel(wrp,
wrp->master->arb_en_all, PWRAP_HIPRIO_ARB_EN);
return 0;
}
static int pwrap_mt8135_init_soc_specific(struct pmic_wrapper *wrp)
{
/* enable pwrap events and pwrap bridge in AP side */
@ -911,10 +1217,18 @@ static int pwrap_mt2701_init_soc_specific(struct pmic_wrapper *wrp)
return 0;
}
static int pwrap_mt7622_init_soc_specific(struct pmic_wrapper *wrp)
{
pwrap_writel(wrp, 0, PWRAP_STAUPD_PRD);
/* enable 2wire SPI master */
pwrap_writel(wrp, 0x8000000, PWRAP_SPI2_CTRL);
return 0;
}
static int pwrap_init(struct pmic_wrapper *wrp)
{
int ret;
u32 rdata;
reset_control_reset(wrp->rstc);
if (wrp->rstc_bridge)
@ -926,10 +1240,12 @@ static int pwrap_init(struct pmic_wrapper *wrp)
pwrap_writel(wrp, 0, PWRAP_DCM_DBC_PRD);
}
/* Reset SPI slave */
ret = pwrap_reset_spislave(wrp);
if (ret)
return ret;
if (HAS_CAP(wrp->slave->caps, PWRAP_SLV_CAP_SPI)) {
/* Reset SPI slave */
ret = pwrap_reset_spislave(wrp);
if (ret)
return ret;
}
pwrap_writel(wrp, 1, PWRAP_WRAP_EN);
@ -941,45 +1257,26 @@ static int pwrap_init(struct pmic_wrapper *wrp)
if (ret)
return ret;
/* Setup serial input delay */
ret = pwrap_init_sidly(wrp);
if (ret)
return ret;
/* Enable dual IO mode */
pwrap_write(wrp, wrp->slave->dew_regs[PWRAP_DEW_DIO_EN], 1);
/* Check IDLE & INIT_DONE in advance */
ret = pwrap_wait_for_state(wrp, pwrap_is_fsm_idle_and_sync_idle);
if (ret) {
dev_err(wrp->dev, "%s fail, ret=%d\n", __func__, ret);
return ret;
if (HAS_CAP(wrp->slave->caps, PWRAP_SLV_CAP_SPI)) {
/* Setup serial input delay */
ret = pwrap_init_sidly(wrp);
if (ret)
return ret;
}
pwrap_writel(wrp, 1, PWRAP_DIO_EN);
/* Read Test */
pwrap_read(wrp, wrp->slave->dew_regs[PWRAP_DEW_READ_TEST], &rdata);
if (rdata != PWRAP_DEW_READ_TEST_VAL) {
dev_err(wrp->dev, "Read test failed after switch to DIO mode: 0x%04x != 0x%04x\n",
PWRAP_DEW_READ_TEST_VAL, rdata);
return -EFAULT;
if (HAS_CAP(wrp->slave->caps, PWRAP_SLV_CAP_DUALIO)) {
/* Enable dual I/O mode */
ret = pwrap_init_dual_io(wrp);
if (ret)
return ret;
}
/* Enable encryption */
ret = pwrap_init_cipher(wrp);
if (ret)
return ret;
/* Signature checking - using CRC */
if (pwrap_write(wrp, wrp->slave->dew_regs[PWRAP_DEW_CRC_EN], 0x1))
return -EFAULT;
pwrap_writel(wrp, 0x1, PWRAP_CRC_EN);
pwrap_writel(wrp, 0x0, PWRAP_SIG_MODE);
pwrap_writel(wrp, wrp->slave->dew_regs[PWRAP_DEW_CRC_VAL],
PWRAP_SIG_ADR);
pwrap_writel(wrp, wrp->master->arb_en_all, PWRAP_HIPRIO_ARB_EN);
if (HAS_CAP(wrp->slave->caps, PWRAP_SLV_CAP_SECURITY)) {
/* Enable security on bus */
ret = pwrap_init_security(wrp);
if (ret)
return ret;
}
if (wrp->master->type == PWRAP_MT8135)
pwrap_writel(wrp, 0x7, PWRAP_RRARB_EN);
@ -1023,7 +1320,7 @@ static irqreturn_t pwrap_interrupt(int irqno, void *dev_id)
return IRQ_HANDLED;
}
static const struct regmap_config pwrap_regmap_config = {
static const struct regmap_config pwrap_regmap_config16 = {
.reg_bits = 16,
.val_bits = 16,
.reg_stride = 2,
@ -1032,20 +1329,54 @@ static const struct regmap_config pwrap_regmap_config = {
.max_register = 0xffff,
};
static const struct regmap_config pwrap_regmap_config32 = {
.reg_bits = 32,
.val_bits = 32,
.reg_stride = 4,
.reg_read = pwrap_regmap_read,
.reg_write = pwrap_regmap_write,
.max_register = 0xffff,
};
static const struct pwrap_slv_type pmic_mt6323 = {
.dew_regs = mt6323_regs,
.type = PMIC_MT6323,
.regmap = &pwrap_regmap_config16,
.caps = PWRAP_SLV_CAP_SPI | PWRAP_SLV_CAP_DUALIO |
PWRAP_SLV_CAP_SECURITY,
.pwrap_read = pwrap_read16,
.pwrap_write = pwrap_write16,
};
static const struct pwrap_slv_type pmic_mt6380 = {
.dew_regs = NULL,
.type = PMIC_MT6380,
.regmap = &pwrap_regmap_config32,
.caps = 0,
.pwrap_read = pwrap_read32,
.pwrap_write = pwrap_write32,
};
static const struct pwrap_slv_type pmic_mt6397 = {
.dew_regs = mt6397_regs,
.type = PMIC_MT6397,
.regmap = &pwrap_regmap_config16,
.caps = PWRAP_SLV_CAP_SPI | PWRAP_SLV_CAP_DUALIO |
PWRAP_SLV_CAP_SECURITY,
.pwrap_read = pwrap_read16,
.pwrap_write = pwrap_write16,
};
static const struct of_device_id of_slave_match_tbl[] = {
{
.compatible = "mediatek,mt6323",
.data = &pmic_mt6323,
}, {
/* The MT6380 PMIC only implements a regulator, so we bind it
* directly instead of using a MFD.
*/
.compatible = "mediatek,mt6380-regulator",
.data = &pmic_mt6380,
}, {
.compatible = "mediatek,mt6397",
.data = &pmic_mt6397,
@ -1067,6 +1398,18 @@ static const struct pmic_wrapper_type pwrap_mt2701 = {
.init_soc_specific = pwrap_mt2701_init_soc_specific,
};
static const struct pmic_wrapper_type pwrap_mt7622 = {
.regs = mt7622_regs,
.type = PWRAP_MT7622,
.arb_en_all = 0xff,
.int_en_all = ~(u32)BIT(31),
.spi_w = PWRAP_MAN_CMD_SPI_WRITE,
.wdt_src = PWRAP_WDT_SRC_MASK_ALL,
.has_bridge = 0,
.init_reg_clock = pwrap_common_init_reg_clock,
.init_soc_specific = pwrap_mt7622_init_soc_specific,
};
static const struct pmic_wrapper_type pwrap_mt8135 = {
.regs = mt8135_regs,
.type = PWRAP_MT8135,
@ -1075,7 +1418,7 @@ static const struct pmic_wrapper_type pwrap_mt8135 = {
.spi_w = PWRAP_MAN_CMD_SPI_WRITE,
.wdt_src = PWRAP_WDT_SRC_MASK_ALL,
.has_bridge = 1,
.init_reg_clock = pwrap_mt8135_init_reg_clock,
.init_reg_clock = pwrap_common_init_reg_clock,
.init_soc_specific = pwrap_mt8135_init_soc_specific,
};
@ -1087,7 +1430,7 @@ static const struct pmic_wrapper_type pwrap_mt8173 = {
.spi_w = PWRAP_MAN_CMD_SPI_WRITE,
.wdt_src = PWRAP_WDT_SRC_MASK_NO_STAUPD,
.has_bridge = 0,
.init_reg_clock = pwrap_mt8173_init_reg_clock,
.init_reg_clock = pwrap_common_init_reg_clock,
.init_soc_specific = pwrap_mt8173_init_soc_specific,
};
@ -1095,6 +1438,9 @@ static const struct of_device_id of_pwrap_match_tbl[] = {
{
.compatible = "mediatek,mt2701-pwrap",
.data = &pwrap_mt2701,
}, {
.compatible = "mediatek,mt7622-pwrap",
.data = &pwrap_mt7622,
}, {
.compatible = "mediatek,mt8135-pwrap",
.data = &pwrap_mt8135,
@ -1159,23 +1505,27 @@ static int pwrap_probe(struct platform_device *pdev)
if (IS_ERR(wrp->bridge_base))
return PTR_ERR(wrp->bridge_base);
wrp->rstc_bridge = devm_reset_control_get(wrp->dev, "pwrap-bridge");
wrp->rstc_bridge = devm_reset_control_get(wrp->dev,
"pwrap-bridge");
if (IS_ERR(wrp->rstc_bridge)) {
ret = PTR_ERR(wrp->rstc_bridge);
dev_dbg(wrp->dev, "cannot get pwrap-bridge reset: %d\n", ret);
dev_dbg(wrp->dev,
"cannot get pwrap-bridge reset: %d\n", ret);
return ret;
}
}
wrp->clk_spi = devm_clk_get(wrp->dev, "spi");
if (IS_ERR(wrp->clk_spi)) {
dev_dbg(wrp->dev, "failed to get clock: %ld\n", PTR_ERR(wrp->clk_spi));
dev_dbg(wrp->dev, "failed to get clock: %ld\n",
PTR_ERR(wrp->clk_spi));
return PTR_ERR(wrp->clk_spi);
}
wrp->clk_wrap = devm_clk_get(wrp->dev, "wrap");
if (IS_ERR(wrp->clk_wrap)) {
dev_dbg(wrp->dev, "failed to get clock: %ld\n", PTR_ERR(wrp->clk_wrap));
dev_dbg(wrp->dev, "failed to get clock: %ld\n",
PTR_ERR(wrp->clk_wrap));
return PTR_ERR(wrp->clk_wrap);
}
@ -1220,12 +1570,13 @@ static int pwrap_probe(struct platform_device *pdev)
pwrap_writel(wrp, wrp->master->int_en_all, PWRAP_INT_EN);
irq = platform_get_irq(pdev, 0);
ret = devm_request_irq(wrp->dev, irq, pwrap_interrupt, IRQF_TRIGGER_HIGH,
"mt-pmic-pwrap", wrp);
ret = devm_request_irq(wrp->dev, irq, pwrap_interrupt,
IRQF_TRIGGER_HIGH,
"mt-pmic-pwrap", wrp);
if (ret)
goto err_out2;
wrp->regmap = devm_regmap_init(wrp->dev, NULL, wrp, &pwrap_regmap_config);
wrp->regmap = devm_regmap_init(wrp->dev, NULL, wrp, wrp->slave->regmap);
if (IS_ERR(wrp->regmap)) {
ret = PTR_ERR(wrp->regmap);
goto err_out2;

View file

@ -35,6 +35,17 @@ config QCOM_PM
modes. It interface with various system drivers to put the cores in
low power modes.
config QCOM_RMTFS_MEM
tristate "Qualcomm Remote Filesystem memory driver"
depends on ARCH_QCOM
help
The Qualcomm remote filesystem memory driver is used for allocating
and exposing regions of shared memory with remote processors for the
purpose of exchanging sector-data between the remote filesystem
service and its clients.
Say y here if you intend to boot the modem remoteproc.
config QCOM_SMEM
tristate "Qualcomm Shared Memory Manager (SMEM)"
depends on ARCH_QCOM

View file

@ -3,6 +3,7 @@ obj-$(CONFIG_QCOM_GLINK_SSR) += glink_ssr.o
obj-$(CONFIG_QCOM_GSBI) += qcom_gsbi.o
obj-$(CONFIG_QCOM_MDT_LOADER) += mdt_loader.o
obj-$(CONFIG_QCOM_PM) += spm.o
obj-$(CONFIG_QCOM_RMTFS_MEM) += rmtfs_mem.o
obj-$(CONFIG_QCOM_SMD_RPM) += smd-rpm.o
obj-$(CONFIG_QCOM_SMEM) += smem.o
obj-$(CONFIG_QCOM_SMEM_STATE) += smem_state.o

View file

@ -0,0 +1,269 @@
/*
* Copyright (c) 2017 Linaro Ltd.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
* only version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <linux/kernel.h>
#include <linux/cdev.h>
#include <linux/err.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/of.h>
#include <linux/of_reserved_mem.h>
#include <linux/dma-mapping.h>
#include <linux/slab.h>
#include <linux/uaccess.h>
#include <linux/io.h>
#include <linux/qcom_scm.h>
#define QCOM_RMTFS_MEM_DEV_MAX (MINORMASK + 1)
static dev_t qcom_rmtfs_mem_major;
struct qcom_rmtfs_mem {
struct device dev;
struct cdev cdev;
void *base;
phys_addr_t addr;
phys_addr_t size;
unsigned int client_id;
};
static ssize_t qcom_rmtfs_mem_show(struct device *dev,
struct device_attribute *attr,
char *buf);
static DEVICE_ATTR(phys_addr, 0400, qcom_rmtfs_mem_show, NULL);
static DEVICE_ATTR(size, 0400, qcom_rmtfs_mem_show, NULL);
static DEVICE_ATTR(client_id, 0400, qcom_rmtfs_mem_show, NULL);
static ssize_t qcom_rmtfs_mem_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct qcom_rmtfs_mem *rmtfs_mem = container_of(dev,
struct qcom_rmtfs_mem,
dev);
if (attr == &dev_attr_phys_addr)
return sprintf(buf, "%pa\n", &rmtfs_mem->addr);
if (attr == &dev_attr_size)
return sprintf(buf, "%pa\n", &rmtfs_mem->size);
if (attr == &dev_attr_client_id)
return sprintf(buf, "%d\n", rmtfs_mem->client_id);
return -EINVAL;
}
static struct attribute *qcom_rmtfs_mem_attrs[] = {
&dev_attr_phys_addr.attr,
&dev_attr_size.attr,
&dev_attr_client_id.attr,
NULL
};
ATTRIBUTE_GROUPS(qcom_rmtfs_mem);
static int qcom_rmtfs_mem_open(struct inode *inode, struct file *filp)
{
struct qcom_rmtfs_mem *rmtfs_mem = container_of(inode->i_cdev,
struct qcom_rmtfs_mem,
cdev);
get_device(&rmtfs_mem->dev);
filp->private_data = rmtfs_mem;
return 0;
}
static ssize_t qcom_rmtfs_mem_read(struct file *filp,
char __user *buf, size_t count, loff_t *f_pos)
{
struct qcom_rmtfs_mem *rmtfs_mem = filp->private_data;
if (*f_pos >= rmtfs_mem->size)
return 0;
if (*f_pos + count >= rmtfs_mem->size)
count = rmtfs_mem->size - *f_pos;
if (copy_to_user(buf, rmtfs_mem->base + *f_pos, count))
return -EFAULT;
*f_pos += count;
return count;
}
static ssize_t qcom_rmtfs_mem_write(struct file *filp,
const char __user *buf, size_t count,
loff_t *f_pos)
{
struct qcom_rmtfs_mem *rmtfs_mem = filp->private_data;
if (*f_pos >= rmtfs_mem->size)
return 0;
if (*f_pos + count >= rmtfs_mem->size)
count = rmtfs_mem->size - *f_pos;
if (copy_from_user(rmtfs_mem->base + *f_pos, buf, count))
return -EFAULT;
*f_pos += count;
return count;
}
static int qcom_rmtfs_mem_release(struct inode *inode, struct file *filp)
{
struct qcom_rmtfs_mem *rmtfs_mem = filp->private_data;
put_device(&rmtfs_mem->dev);
return 0;
}
static const struct file_operations qcom_rmtfs_mem_fops = {
.owner = THIS_MODULE,
.open = qcom_rmtfs_mem_open,
.read = qcom_rmtfs_mem_read,
.write = qcom_rmtfs_mem_write,
.release = qcom_rmtfs_mem_release,
.llseek = default_llseek,
};
static void qcom_rmtfs_mem_release_device(struct device *dev)
{
struct qcom_rmtfs_mem *rmtfs_mem = container_of(dev,
struct qcom_rmtfs_mem,
dev);
kfree(rmtfs_mem);
}
static int qcom_rmtfs_mem_probe(struct platform_device *pdev)
{
struct device_node *node = pdev->dev.of_node;
struct reserved_mem *rmem;
struct qcom_rmtfs_mem *rmtfs_mem;
u32 client_id;
int ret;
rmem = of_reserved_mem_lookup(node);
if (!rmem) {
dev_err(&pdev->dev, "failed to acquire memory region\n");
return -EINVAL;
}
ret = of_property_read_u32(node, "qcom,client-id", &client_id);
if (ret) {
dev_err(&pdev->dev, "failed to parse \"qcom,client-id\"\n");
return ret;
}
rmtfs_mem = kzalloc(sizeof(*rmtfs_mem), GFP_KERNEL);
if (!rmtfs_mem)
return -ENOMEM;
rmtfs_mem->addr = rmem->base;
rmtfs_mem->client_id = client_id;
rmtfs_mem->size = rmem->size;
device_initialize(&rmtfs_mem->dev);
rmtfs_mem->dev.parent = &pdev->dev;
rmtfs_mem->dev.groups = qcom_rmtfs_mem_groups;
rmtfs_mem->base = devm_memremap(&rmtfs_mem->dev, rmtfs_mem->addr,
rmtfs_mem->size, MEMREMAP_WC);
if (IS_ERR(rmtfs_mem->base)) {
dev_err(&pdev->dev, "failed to remap rmtfs_mem region\n");
ret = PTR_ERR(rmtfs_mem->base);
goto put_device;
}
cdev_init(&rmtfs_mem->cdev, &qcom_rmtfs_mem_fops);
rmtfs_mem->cdev.owner = THIS_MODULE;
dev_set_name(&rmtfs_mem->dev, "qcom_rmtfs_mem%d", client_id);
rmtfs_mem->dev.id = client_id;
rmtfs_mem->dev.devt = MKDEV(MAJOR(qcom_rmtfs_mem_major), client_id);
ret = cdev_device_add(&rmtfs_mem->cdev, &rmtfs_mem->dev);
if (ret) {
dev_err(&pdev->dev, "failed to add cdev: %d\n", ret);
goto put_device;
}
rmtfs_mem->dev.release = qcom_rmtfs_mem_release_device;
dev_set_drvdata(&pdev->dev, rmtfs_mem);
return 0;
put_device:
put_device(&rmtfs_mem->dev);
return ret;
}
static int qcom_rmtfs_mem_remove(struct platform_device *pdev)
{
struct qcom_rmtfs_mem *rmtfs_mem = dev_get_drvdata(&pdev->dev);
cdev_device_del(&rmtfs_mem->cdev, &rmtfs_mem->dev);
put_device(&rmtfs_mem->dev);
return 0;
}
static const struct of_device_id qcom_rmtfs_mem_of_match[] = {
{ .compatible = "qcom,rmtfs-mem" },
{}
};
MODULE_DEVICE_TABLE(of, qcom_rmtfs_mem_of_match);
static struct platform_driver qcom_rmtfs_mem_driver = {
.probe = qcom_rmtfs_mem_probe,
.remove = qcom_rmtfs_mem_remove,
.driver = {
.name = "qcom_rmtfs_mem",
.of_match_table = qcom_rmtfs_mem_of_match,
},
};
static int qcom_rmtfs_mem_init(void)
{
int ret;
ret = alloc_chrdev_region(&qcom_rmtfs_mem_major, 0,
QCOM_RMTFS_MEM_DEV_MAX, "qcom_rmtfs_mem");
if (ret < 0) {
pr_err("qcom_rmtfs_mem: failed to allocate char dev region\n");
return ret;
}
ret = platform_driver_register(&qcom_rmtfs_mem_driver);
if (ret < 0) {
pr_err("qcom_rmtfs_mem: failed to register rmtfs_mem driver\n");
unregister_chrdev_region(qcom_rmtfs_mem_major,
QCOM_RMTFS_MEM_DEV_MAX);
}
return ret;
}
module_init(qcom_rmtfs_mem_init);
static void qcom_rmtfs_mem_exit(void)
{
platform_driver_unregister(&qcom_rmtfs_mem_driver);
unregister_chrdev_region(qcom_rmtfs_mem_major, QCOM_RMTFS_MEM_DEV_MAX);
}
module_exit(qcom_rmtfs_mem_exit);

View file

@ -52,8 +52,13 @@
*
* Items in the non-cached region are allocated from the start of the partition
* while items in the cached region are allocated from the end. The free area
* is hence the region between the cached and non-cached offsets.
* is hence the region between the cached and non-cached offsets. The header of
* cached items comes after the data.
*
* Version 12 (SMEM_GLOBAL_PART_VERSION) changes the item alloc/get procedure
* for the global heap. A new global partition is created from the global heap
* region with partition type (SMEM_GLOBAL_HOST) and the max smem item count is
* set by the bootloader.
*
* To synchronize allocations in the shared memory heaps a remote spinlock must
* be held - currently lock number 3 of the sfpb or tcsr is used for this on all
@ -62,13 +67,13 @@
*/
/*
* Item 3 of the global heap contains an array of versions for the various
* software components in the SoC. We verify that the boot loader version is
* what the expected version (SMEM_EXPECTED_VERSION) as a sanity check.
* The version member of the smem header contains an array of versions for the
* various software components in the SoC. We verify that the boot loader
* version is a valid version as a sanity check.
*/
#define SMEM_ITEM_VERSION 3
#define SMEM_MASTER_SBL_VERSION_INDEX 7
#define SMEM_EXPECTED_VERSION 11
#define SMEM_MASTER_SBL_VERSION_INDEX 7
#define SMEM_GLOBAL_HEAP_VERSION 11
#define SMEM_GLOBAL_PART_VERSION 12
/*
* The first 8 items are only to be allocated by the boot loader while
@ -82,8 +87,11 @@
/* Processor/host identifier for the application processor */
#define SMEM_HOST_APPS 0
/* Processor/host identifier for the global partition */
#define SMEM_GLOBAL_HOST 0xfffe
/* Max number of processors/hosts in a system */
#define SMEM_HOST_COUNT 9
#define SMEM_HOST_COUNT 10
/**
* struct smem_proc_comm - proc_comm communication struct (legacy)
@ -140,6 +148,7 @@ struct smem_header {
* @flags: flags for the partition (currently unused)
* @host0: first processor/host with access to this partition
* @host1: second processor/host with access to this partition
* @cacheline: alignment for "cached" entries
* @reserved: reserved entries for later use
*/
struct smem_ptable_entry {
@ -148,7 +157,8 @@ struct smem_ptable_entry {
__le32 flags;
__le16 host0;
__le16 host1;
__le32 reserved[8];
__le32 cacheline;
__le32 reserved[7];
};
/**
@ -212,6 +222,24 @@ struct smem_private_entry {
};
#define SMEM_PRIVATE_CANARY 0xa5a5
/**
* struct smem_info - smem region info located after the table of contents
* @magic: magic number, must be SMEM_INFO_MAGIC
* @size: size of the smem region
* @base_addr: base address of the smem region
* @reserved: for now reserved entry
* @num_items: highest accepted item number
*/
struct smem_info {
u8 magic[4];
__le32 size;
__le32 base_addr;
__le32 reserved;
__le16 num_items;
};
static const u8 SMEM_INFO_MAGIC[] = { 0x53, 0x49, 0x49, 0x49 }; /* SIII */
/**
* struct smem_region - representation of a chunk of memory used for smem
* @aux_base: identifier of aux_mem base
@ -228,8 +256,12 @@ struct smem_region {
* struct qcom_smem - device data for the smem device
* @dev: device pointer
* @hwlock: reference to a hwspinlock
* @global_partition: pointer to global partition when in use
* @global_cacheline: cacheline size for global partition
* @partitions: list of pointers to partitions affecting the current
* processor/host
* @cacheline: list of cacheline sizes for each host
* @item_count: max accepted item number
* @num_regions: number of @regions
* @regions: list of the memory regions defining the shared memory
*/
@ -238,21 +270,33 @@ struct qcom_smem {
struct hwspinlock *hwlock;
struct smem_partition_header *global_partition;
size_t global_cacheline;
struct smem_partition_header *partitions[SMEM_HOST_COUNT];
size_t cacheline[SMEM_HOST_COUNT];
u32 item_count;
unsigned num_regions;
struct smem_region regions[0];
};
static struct smem_private_entry *
phdr_to_last_private_entry(struct smem_partition_header *phdr)
phdr_to_last_uncached_entry(struct smem_partition_header *phdr)
{
void *p = phdr;
return p + le32_to_cpu(phdr->offset_free_uncached);
}
static void *phdr_to_first_cached_entry(struct smem_partition_header *phdr)
static void *phdr_to_first_cached_entry(struct smem_partition_header *phdr,
size_t cacheline)
{
void *p = phdr;
return p + le32_to_cpu(phdr->size) - ALIGN(sizeof(*phdr), cacheline);
}
static void *phdr_to_last_cached_entry(struct smem_partition_header *phdr)
{
void *p = phdr;
@ -260,7 +304,7 @@ static void *phdr_to_first_cached_entry(struct smem_partition_header *phdr)
}
static struct smem_private_entry *
phdr_to_first_private_entry(struct smem_partition_header *phdr)
phdr_to_first_uncached_entry(struct smem_partition_header *phdr)
{
void *p = phdr;
@ -268,7 +312,7 @@ phdr_to_first_private_entry(struct smem_partition_header *phdr)
}
static struct smem_private_entry *
private_entry_next(struct smem_private_entry *e)
uncached_entry_next(struct smem_private_entry *e)
{
void *p = e;
@ -276,13 +320,28 @@ private_entry_next(struct smem_private_entry *e)
le32_to_cpu(e->size);
}
static void *entry_to_item(struct smem_private_entry *e)
static struct smem_private_entry *
cached_entry_next(struct smem_private_entry *e, size_t cacheline)
{
void *p = e;
return p - le32_to_cpu(e->size) - ALIGN(sizeof(*e), cacheline);
}
static void *uncached_entry_to_item(struct smem_private_entry *e)
{
void *p = e;
return p + sizeof(*e) + le16_to_cpu(e->padding_hdr);
}
static void *cached_entry_to_item(struct smem_private_entry *e)
{
void *p = e;
return p - le32_to_cpu(e->size);
}
/* Pointer to the one and only smem handle */
static struct qcom_smem *__smem;
@ -290,32 +349,30 @@ static struct qcom_smem *__smem;
#define HWSPINLOCK_TIMEOUT 1000
static int qcom_smem_alloc_private(struct qcom_smem *smem,
unsigned host,
struct smem_partition_header *phdr,
unsigned item,
size_t size)
{
struct smem_partition_header *phdr;
struct smem_private_entry *hdr, *end;
size_t alloc_size;
void *cached;
phdr = smem->partitions[host];
hdr = phdr_to_first_private_entry(phdr);
end = phdr_to_last_private_entry(phdr);
cached = phdr_to_first_cached_entry(phdr);
hdr = phdr_to_first_uncached_entry(phdr);
end = phdr_to_last_uncached_entry(phdr);
cached = phdr_to_last_cached_entry(phdr);
while (hdr < end) {
if (hdr->canary != SMEM_PRIVATE_CANARY) {
dev_err(smem->dev,
"Found invalid canary in host %d partition\n",
host);
"Found invalid canary in hosts %d:%d partition\n",
phdr->host0, phdr->host1);
return -EINVAL;
}
if (le16_to_cpu(hdr->item) == item)
return -EEXIST;
hdr = private_entry_next(hdr);
hdr = uncached_entry_next(hdr);
}
/* Check that we don't grow into the cached region */
@ -346,11 +403,8 @@ static int qcom_smem_alloc_global(struct qcom_smem *smem,
unsigned item,
size_t size)
{
struct smem_header *header;
struct smem_global_entry *entry;
if (WARN_ON(item >= SMEM_ITEM_COUNT))
return -EINVAL;
struct smem_header *header;
header = smem->regions[0].virt_base;
entry = &header->toc[item];
@ -389,6 +443,7 @@ static int qcom_smem_alloc_global(struct qcom_smem *smem,
*/
int qcom_smem_alloc(unsigned host, unsigned item, size_t size)
{
struct smem_partition_header *phdr;
unsigned long flags;
int ret;
@ -401,16 +456,24 @@ int qcom_smem_alloc(unsigned host, unsigned item, size_t size)
return -EINVAL;
}
if (WARN_ON(item >= __smem->item_count))
return -EINVAL;
ret = hwspin_lock_timeout_irqsave(__smem->hwlock,
HWSPINLOCK_TIMEOUT,
&flags);
if (ret)
return ret;
if (host < SMEM_HOST_COUNT && __smem->partitions[host])
ret = qcom_smem_alloc_private(__smem, host, item, size);
else
if (host < SMEM_HOST_COUNT && __smem->partitions[host]) {
phdr = __smem->partitions[host];
ret = qcom_smem_alloc_private(__smem, phdr, item, size);
} else if (__smem->global_partition) {
phdr = __smem->global_partition;
ret = qcom_smem_alloc_private(__smem, phdr, item, size);
} else {
ret = qcom_smem_alloc_global(__smem, item, size);
}
hwspin_unlock_irqrestore(__smem->hwlock, &flags);
@ -428,9 +491,6 @@ static void *qcom_smem_get_global(struct qcom_smem *smem,
u32 aux_base;
unsigned i;
if (WARN_ON(item >= SMEM_ITEM_COUNT))
return ERR_PTR(-EINVAL);
header = smem->regions[0].virt_base;
entry = &header->toc[item];
if (!entry->allocated)
@ -452,37 +512,58 @@ static void *qcom_smem_get_global(struct qcom_smem *smem,
}
static void *qcom_smem_get_private(struct qcom_smem *smem,
unsigned host,
struct smem_partition_header *phdr,
size_t cacheline,
unsigned item,
size_t *size)
{
struct smem_partition_header *phdr;
struct smem_private_entry *e, *end;
phdr = smem->partitions[host];
e = phdr_to_first_private_entry(phdr);
end = phdr_to_last_private_entry(phdr);
e = phdr_to_first_uncached_entry(phdr);
end = phdr_to_last_uncached_entry(phdr);
while (e < end) {
if (e->canary != SMEM_PRIVATE_CANARY) {
dev_err(smem->dev,
"Found invalid canary in host %d partition\n",
host);
return ERR_PTR(-EINVAL);
}
if (e->canary != SMEM_PRIVATE_CANARY)
goto invalid_canary;
if (le16_to_cpu(e->item) == item) {
if (size != NULL)
*size = le32_to_cpu(e->size) -
le16_to_cpu(e->padding_data);
return entry_to_item(e);
return uncached_entry_to_item(e);
}
e = private_entry_next(e);
e = uncached_entry_next(e);
}
/* Item was not found in the uncached list, search the cached list */
e = phdr_to_first_cached_entry(phdr, cacheline);
end = phdr_to_last_cached_entry(phdr);
while (e > end) {
if (e->canary != SMEM_PRIVATE_CANARY)
goto invalid_canary;
if (le16_to_cpu(e->item) == item) {
if (size != NULL)
*size = le32_to_cpu(e->size) -
le16_to_cpu(e->padding_data);
return cached_entry_to_item(e);
}
e = cached_entry_next(e, cacheline);
}
return ERR_PTR(-ENOENT);
invalid_canary:
dev_err(smem->dev, "Found invalid canary in hosts %d:%d partition\n",
phdr->host0, phdr->host1);
return ERR_PTR(-EINVAL);
}
/**
@ -496,23 +577,35 @@ static void *qcom_smem_get_private(struct qcom_smem *smem,
*/
void *qcom_smem_get(unsigned host, unsigned item, size_t *size)
{
struct smem_partition_header *phdr;
unsigned long flags;
size_t cacheln;
int ret;
void *ptr = ERR_PTR(-EPROBE_DEFER);
if (!__smem)
return ptr;
if (WARN_ON(item >= __smem->item_count))
return ERR_PTR(-EINVAL);
ret = hwspin_lock_timeout_irqsave(__smem->hwlock,
HWSPINLOCK_TIMEOUT,
&flags);
if (ret)
return ERR_PTR(ret);
if (host < SMEM_HOST_COUNT && __smem->partitions[host])
ptr = qcom_smem_get_private(__smem, host, item, size);
else
if (host < SMEM_HOST_COUNT && __smem->partitions[host]) {
phdr = __smem->partitions[host];
cacheln = __smem->cacheline[host];
ptr = qcom_smem_get_private(__smem, phdr, cacheln, item, size);
} else if (__smem->global_partition) {
phdr = __smem->global_partition;
cacheln = __smem->global_cacheline;
ptr = qcom_smem_get_private(__smem, phdr, cacheln, item, size);
} else {
ptr = qcom_smem_get_global(__smem, item, size);
}
hwspin_unlock_irqrestore(__smem->hwlock, &flags);
@ -541,6 +634,10 @@ int qcom_smem_get_free_space(unsigned host)
phdr = __smem->partitions[host];
ret = le32_to_cpu(phdr->offset_free_cached) -
le32_to_cpu(phdr->offset_free_uncached);
} else if (__smem->global_partition) {
phdr = __smem->global_partition;
ret = le32_to_cpu(phdr->offset_free_cached) -
le32_to_cpu(phdr->offset_free_uncached);
} else {
header = __smem->regions[0].virt_base;
ret = le32_to_cpu(header->available);
@ -552,44 +649,131 @@ EXPORT_SYMBOL(qcom_smem_get_free_space);
static int qcom_smem_get_sbl_version(struct qcom_smem *smem)
{
struct smem_header *header;
__le32 *versions;
size_t size;
versions = qcom_smem_get_global(smem, SMEM_ITEM_VERSION, &size);
if (IS_ERR(versions)) {
dev_err(smem->dev, "Unable to read the version item\n");
return -ENOENT;
}
if (size < sizeof(unsigned) * SMEM_MASTER_SBL_VERSION_INDEX) {
dev_err(smem->dev, "Version item is too small\n");
return -EINVAL;
}
header = smem->regions[0].virt_base;
versions = header->version;
return le32_to_cpu(versions[SMEM_MASTER_SBL_VERSION_INDEX]);
}
static int qcom_smem_enumerate_partitions(struct qcom_smem *smem,
unsigned local_host)
static struct smem_ptable *qcom_smem_get_ptable(struct qcom_smem *smem)
{
struct smem_partition_header *header;
struct smem_ptable_entry *entry;
struct smem_ptable *ptable;
unsigned remote_host;
u32 version, host0, host1;
int i;
u32 version;
ptable = smem->regions[0].virt_base + smem->regions[0].size - SZ_4K;
if (memcmp(ptable->magic, SMEM_PTABLE_MAGIC, sizeof(ptable->magic)))
return 0;
return ERR_PTR(-ENOENT);
version = le32_to_cpu(ptable->version);
if (version != 1) {
dev_err(smem->dev,
"Unsupported partition header version %d\n", version);
return ERR_PTR(-EINVAL);
}
return ptable;
}
static u32 qcom_smem_get_item_count(struct qcom_smem *smem)
{
struct smem_ptable *ptable;
struct smem_info *info;
ptable = qcom_smem_get_ptable(smem);
if (IS_ERR_OR_NULL(ptable))
return SMEM_ITEM_COUNT;
info = (struct smem_info *)&ptable->entry[ptable->num_entries];
if (memcmp(info->magic, SMEM_INFO_MAGIC, sizeof(info->magic)))
return SMEM_ITEM_COUNT;
return le16_to_cpu(info->num_items);
}
static int qcom_smem_set_global_partition(struct qcom_smem *smem)
{
struct smem_partition_header *header;
struct smem_ptable_entry *entry = NULL;
struct smem_ptable *ptable;
u32 host0, host1, size;
int i;
ptable = qcom_smem_get_ptable(smem);
if (IS_ERR(ptable))
return PTR_ERR(ptable);
for (i = 0; i < le32_to_cpu(ptable->num_entries); i++) {
entry = &ptable->entry[i];
host0 = le16_to_cpu(entry->host0);
host1 = le16_to_cpu(entry->host1);
if (host0 == SMEM_GLOBAL_HOST && host0 == host1)
break;
}
if (!entry) {
dev_err(smem->dev, "Missing entry for global partition\n");
return -EINVAL;
}
if (!le32_to_cpu(entry->offset) || !le32_to_cpu(entry->size)) {
dev_err(smem->dev, "Invalid entry for global partition\n");
return -EINVAL;
}
if (smem->global_partition) {
dev_err(smem->dev, "Already found the global partition\n");
return -EINVAL;
}
header = smem->regions[0].virt_base + le32_to_cpu(entry->offset);
host0 = le16_to_cpu(header->host0);
host1 = le16_to_cpu(header->host1);
if (memcmp(header->magic, SMEM_PART_MAGIC, sizeof(header->magic))) {
dev_err(smem->dev, "Global partition has invalid magic\n");
return -EINVAL;
}
if (host0 != SMEM_GLOBAL_HOST && host1 != SMEM_GLOBAL_HOST) {
dev_err(smem->dev, "Global partition hosts are invalid\n");
return -EINVAL;
}
if (le32_to_cpu(header->size) != le32_to_cpu(entry->size)) {
dev_err(smem->dev, "Global partition has invalid size\n");
return -EINVAL;
}
size = le32_to_cpu(header->offset_free_uncached);
if (size > le32_to_cpu(header->size)) {
dev_err(smem->dev,
"Global partition has invalid free pointer\n");
return -EINVAL;
}
smem->global_partition = header;
smem->global_cacheline = le32_to_cpu(entry->cacheline);
return 0;
}
static int qcom_smem_enumerate_partitions(struct qcom_smem *smem,
unsigned int local_host)
{
struct smem_partition_header *header;
struct smem_ptable_entry *entry;
struct smem_ptable *ptable;
unsigned int remote_host;
u32 host0, host1;
int i;
ptable = qcom_smem_get_ptable(smem);
if (IS_ERR(ptable))
return PTR_ERR(ptable);
for (i = 0; i < le32_to_cpu(ptable->num_entries); i++) {
entry = &ptable->entry[i];
host0 = le16_to_cpu(entry->host0);
@ -646,7 +830,7 @@ static int qcom_smem_enumerate_partitions(struct qcom_smem *smem,
return -EINVAL;
}
if (header->size != entry->size) {
if (le32_to_cpu(header->size) != le32_to_cpu(entry->size)) {
dev_err(smem->dev,
"Partition %d has invalid size\n", i);
return -EINVAL;
@ -659,6 +843,7 @@ static int qcom_smem_enumerate_partitions(struct qcom_smem *smem,
}
smem->partitions[remote_host] = header;
smem->cacheline[remote_host] = le32_to_cpu(entry->cacheline);
}
return 0;
@ -729,13 +914,23 @@ static int qcom_smem_probe(struct platform_device *pdev)
}
version = qcom_smem_get_sbl_version(smem);
if (version >> 16 != SMEM_EXPECTED_VERSION) {
switch (version >> 16) {
case SMEM_GLOBAL_PART_VERSION:
ret = qcom_smem_set_global_partition(smem);
if (ret < 0)
return ret;
smem->item_count = qcom_smem_get_item_count(smem);
break;
case SMEM_GLOBAL_HEAP_VERSION:
smem->item_count = SMEM_ITEM_COUNT;
break;
default:
dev_err(&pdev->dev, "Unsupported SMEM version 0x%x\n", version);
return -EINVAL;
}
ret = qcom_smem_enumerate_partitions(smem, SMEM_HOST_APPS);
if (ret < 0)
if (ret < 0 && ret != -ENOENT)
return ret;
hwlock_id = of_hwspin_lock_get_id(pdev->dev.of_node, 0);

View file

@ -3,7 +3,8 @@ config SOC_RENESAS
default y if ARCH_RENESAS
select SOC_BUS
select RST_RCAR if ARCH_RCAR_GEN1 || ARCH_RCAR_GEN2 || \
ARCH_R8A7795 || ARCH_R8A7796 || ARCH_R8A77995
ARCH_R8A7795 || ARCH_R8A7796 || ARCH_R8A77970 || \
ARCH_R8A77995
select SYSC_R8A7743 if ARCH_R8A7743
select SYSC_R8A7745 if ARCH_R8A7745
select SYSC_R8A7779 if ARCH_R8A7779
@ -13,6 +14,7 @@ config SOC_RENESAS
select SYSC_R8A7794 if ARCH_R8A7794
select SYSC_R8A7795 if ARCH_R8A7795
select SYSC_R8A7796 if ARCH_R8A7796
select SYSC_R8A77970 if ARCH_R8A77970
select SYSC_R8A77995 if ARCH_R8A77995
if SOC_RENESAS
@ -54,6 +56,10 @@ config SYSC_R8A7796
bool "R-Car M3-W System Controller support" if COMPILE_TEST
select SYSC_RCAR
config SYSC_R8A77970
bool "R-Car V3M System Controller support" if COMPILE_TEST
select SYSC_RCAR
config SYSC_R8A77995
bool "R-Car D3 System Controller support" if COMPILE_TEST
select SYSC_RCAR

View file

@ -12,6 +12,7 @@ obj-$(CONFIG_SYSC_R8A7792) += r8a7792-sysc.o
obj-$(CONFIG_SYSC_R8A7794) += r8a7794-sysc.o
obj-$(CONFIG_SYSC_R8A7795) += r8a7795-sysc.o
obj-$(CONFIG_SYSC_R8A7796) += r8a7796-sysc.o
obj-$(CONFIG_SYSC_R8A77970) += r8a77970-sysc.o
obj-$(CONFIG_SYSC_R8A77995) += r8a77995-sysc.o
# Family

View file

@ -0,0 +1,39 @@
/*
* Renesas R-Car V3M System Controller
*
* Copyright (C) 2017 Cogent Embedded Inc.
*
* 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/bug.h>
#include <linux/kernel.h>
#include <dt-bindings/power/r8a77970-sysc.h>
#include "rcar-sysc.h"
static const struct rcar_sysc_area r8a77970_areas[] __initconst = {
{ "always-on", 0, 0, R8A77970_PD_ALWAYS_ON, -1, PD_ALWAYS_ON },
{ "ca53-scu", 0x140, 0, R8A77970_PD_CA53_SCU, R8A77970_PD_ALWAYS_ON,
PD_SCU },
{ "ca53-cpu0", 0x200, 0, R8A77970_PD_CA53_CPU0, R8A77970_PD_CA53_SCU,
PD_CPU_NOCR },
{ "ca53-cpu1", 0x200, 1, R8A77970_PD_CA53_CPU1, R8A77970_PD_CA53_SCU,
PD_CPU_NOCR },
{ "cr7", 0x240, 0, R8A77970_PD_CR7, R8A77970_PD_ALWAYS_ON },
{ "a3ir", 0x180, 0, R8A77970_PD_A3IR, R8A77970_PD_ALWAYS_ON },
{ "a2ir0", 0x400, 0, R8A77970_PD_A2IR0, R8A77970_PD_ALWAYS_ON },
{ "a2ir1", 0x400, 1, R8A77970_PD_A2IR1, R8A77970_PD_A2IR0 },
{ "a2ir2", 0x400, 2, R8A77970_PD_A2IR2, R8A77970_PD_A2IR0 },
{ "a2ir3", 0x400, 3, R8A77970_PD_A2IR3, R8A77970_PD_A2IR0 },
{ "a2sc0", 0x400, 4, R8A77970_PD_A2SC0, R8A77970_PD_ALWAYS_ON },
{ "a2sc1", 0x400, 5, R8A77970_PD_A2SC1, R8A77970_PD_A2SC0 },
};
const struct rcar_sysc_info r8a77970_sysc_info __initconst = {
.areas = r8a77970_areas,
.num_areas = ARRAY_SIZE(r8a77970_areas),
};

View file

@ -41,6 +41,7 @@ static const struct of_device_id rcar_rst_matches[] __initconst = {
/* R-Car Gen3 is handled like R-Car Gen2 */
{ .compatible = "renesas,r8a7795-rst", .data = &rcar_rst_gen2 },
{ .compatible = "renesas,r8a7796-rst", .data = &rcar_rst_gen2 },
{ .compatible = "renesas,r8a77970-rst", .data = &rcar_rst_gen2 },
{ .compatible = "renesas,r8a77995-rst", .data = &rcar_rst_gen2 },
{ /* sentinel */ }
};

View file

@ -284,6 +284,9 @@ static const struct of_device_id rcar_sysc_matches[] = {
#ifdef CONFIG_SYSC_R8A7796
{ .compatible = "renesas,r8a7796-sysc", .data = &r8a7796_sysc_info },
#endif
#ifdef CONFIG_SYSC_R8A77970
{ .compatible = "renesas,r8a77970-sysc", .data = &r8a77970_sysc_info },
#endif
#ifdef CONFIG_SYSC_R8A77995
{ .compatible = "renesas,r8a77995-sysc", .data = &r8a77995_sysc_info },
#endif

View file

@ -58,6 +58,7 @@ extern const struct rcar_sysc_info r8a7792_sysc_info;
extern const struct rcar_sysc_info r8a7794_sysc_info;
extern const struct rcar_sysc_info r8a7795_sysc_info;
extern const struct rcar_sysc_info r8a7796_sysc_info;
extern const struct rcar_sysc_info r8a77970_sysc_info;
extern const struct rcar_sysc_info r8a77995_sysc_info;

View file

@ -144,6 +144,11 @@ static const struct renesas_soc soc_rcar_m3_w __initconst __maybe_unused = {
.id = 0x52,
};
static const struct renesas_soc soc_rcar_v3m __initconst __maybe_unused = {
.family = &fam_rcar_gen3,
.id = 0x54,
};
static const struct renesas_soc soc_rcar_d3 __initconst __maybe_unused = {
.family = &fam_rcar_gen3,
.id = 0x58,
@ -204,6 +209,9 @@ static const struct of_device_id renesas_socs[] __initconst = {
#ifdef CONFIG_ARCH_R8A7796
{ .compatible = "renesas,r8a7796", .data = &soc_rcar_m3_w },
#endif
#ifdef CONFIG_ARCH_R8A77970
{ .compatible = "renesas,r8a77970", .data = &soc_rcar_v3m },
#endif
#ifdef CONFIG_ARCH_R8A77995
{ .compatible = "renesas,r8a77995", .data = &soc_rcar_d3 },
#endif

View file

@ -60,12 +60,6 @@ void exynos_sys_powerdown_conf(enum sys_powerdown mode)
if (pmu_data->powerdown_conf_extra)
pmu_data->powerdown_conf_extra(mode);
if (pmu_data->pmu_config_extra) {
for (i = 0; pmu_data->pmu_config_extra[i].offset != PMU_TABLE_END; i++)
pmu_raw_writel(pmu_data->pmu_config_extra[i].val[mode],
pmu_data->pmu_config_extra[i].offset);
}
}
/*
@ -88,9 +82,6 @@ static const struct of_device_id exynos_pmu_of_device_ids[] = {
}, {
.compatible = "samsung,exynos4210-pmu",
.data = exynos_pmu_data_arm_ptr(exynos4210_pmu_data),
}, {
.compatible = "samsung,exynos4212-pmu",
.data = exynos_pmu_data_arm_ptr(exynos4212_pmu_data),
}, {
.compatible = "samsung,exynos4412-pmu",
.data = exynos_pmu_data_arm_ptr(exynos4412_pmu_data),

View file

@ -23,7 +23,6 @@ struct exynos_pmu_conf {
struct exynos_pmu_data {
const struct exynos_pmu_conf *pmu_config;
const struct exynos_pmu_conf *pmu_config_extra;
void (*pmu_init)(void);
void (*powerdown_conf)(enum sys_powerdown);
@ -36,7 +35,6 @@ extern void __iomem *pmu_base_addr;
/* list of all exported SoC specific data */
extern const struct exynos_pmu_data exynos3250_pmu_data;
extern const struct exynos_pmu_data exynos4210_pmu_data;
extern const struct exynos_pmu_data exynos4212_pmu_data;
extern const struct exynos_pmu_data exynos4412_pmu_data;
extern const struct exynos_pmu_data exynos5250_pmu_data;
extern const struct exynos_pmu_data exynos5420_pmu_data;

View file

@ -90,7 +90,7 @@ static const struct exynos_pmu_conf exynos4210_pmu_config[] = {
{ PMU_TABLE_END,},
};
static const struct exynos_pmu_conf exynos4x12_pmu_config[] = {
static const struct exynos_pmu_conf exynos4412_pmu_config[] = {
{ S5P_ARM_CORE0_LOWPWR, { 0x0, 0x0, 0x2 } },
{ S5P_DIS_IRQ_CORE0, { 0x0, 0x0, 0x0 } },
{ S5P_DIS_IRQ_CENTRAL0, { 0x0, 0x0, 0x0 } },
@ -195,10 +195,6 @@ static const struct exynos_pmu_conf exynos4x12_pmu_config[] = {
{ S5P_GPS_ALIVE_LOWPWR, { 0x7, 0x0, 0x0 } },
{ S5P_CMU_SYSCLK_ISP_LOWPWR, { 0x1, 0x0, 0x0 } },
{ S5P_CMU_SYSCLK_GPS_LOWPWR, { 0x1, 0x0, 0x0 } },
{ PMU_TABLE_END,},
};
static const struct exynos_pmu_conf exynos4412_pmu_config[] = {
{ S5P_ARM_CORE2_LOWPWR, { 0x0, 0x0, 0x2 } },
{ S5P_DIS_IRQ_CORE2, { 0x0, 0x0, 0x0 } },
{ S5P_DIS_IRQ_CENTRAL2, { 0x0, 0x0, 0x0 } },
@ -212,11 +208,6 @@ const struct exynos_pmu_data exynos4210_pmu_data = {
.pmu_config = exynos4210_pmu_config,
};
const struct exynos_pmu_data exynos4212_pmu_data = {
.pmu_config = exynos4x12_pmu_config,
};
const struct exynos_pmu_data exynos4412_pmu_data = {
.pmu_config = exynos4x12_pmu_config,
.pmu_config_extra = exynos4412_pmu_config,
.pmu_config = exynos4412_pmu_config,
};

View file

@ -42,6 +42,7 @@ static int tegra_bpmp_powergate_set_state(struct tegra_bpmp *bpmp,
{
struct mrq_pg_request request;
struct tegra_bpmp_message msg;
int err;
memset(&request, 0, sizeof(request));
request.cmd = CMD_PG_SET_STATE;
@ -53,7 +54,13 @@ static int tegra_bpmp_powergate_set_state(struct tegra_bpmp *bpmp,
msg.tx.data = &request;
msg.tx.size = sizeof(request);
return tegra_bpmp_transfer(bpmp, &msg);
err = tegra_bpmp_transfer(bpmp, &msg);
if (err < 0)
return err;
else if (msg.rx.ret < 0)
return -EINVAL;
return 0;
}
static int tegra_bpmp_powergate_get_state(struct tegra_bpmp *bpmp,
@ -80,6 +87,8 @@ static int tegra_bpmp_powergate_get_state(struct tegra_bpmp *bpmp,
err = tegra_bpmp_transfer(bpmp, &msg);
if (err < 0)
return PG_STATE_OFF;
else if (msg.rx.ret < 0)
return -EINVAL;
return response.get_state.state;
}
@ -106,6 +115,8 @@ static int tegra_bpmp_powergate_get_max_id(struct tegra_bpmp *bpmp)
err = tegra_bpmp_transfer(bpmp, &msg);
if (err < 0)
return err;
else if (msg.rx.ret < 0)
return -EINVAL;
return response.get_max_id.max_id;
}
@ -132,7 +143,7 @@ static char *tegra_bpmp_powergate_get_name(struct tegra_bpmp *bpmp,
msg.rx.size = sizeof(response);
err = tegra_bpmp_transfer(bpmp, &msg);
if (err < 0)
if (err < 0 || msg.rx.ret < 0)
return NULL;
return kstrdup(response.get_name.name, GFP_KERNEL);