mirror of
https://github.com/BobTheBlinker/android_kernel_motorola_sm6375.git
synced 2026-10-29 19:21:31 -04:00
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:
commit
cf9b0772f2
110 changed files with 6933 additions and 996 deletions
|
|
@ -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/
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
243
drivers/soc/amlogic/meson-gx-pwrc-vpu.c
Normal file
243
drivers/soc/amlogic/meson-gx-pwrc-vpu.c
Normal 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, ®);
|
||||
|
||||
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);
|
||||
175
drivers/soc/amlogic/meson-mx-socinfo.c
Normal file
175
drivers/soc/amlogic/meson-mx-socinfo.c
Normal 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);
|
||||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -20,4 +20,6 @@ config SOC_BRCMSTB
|
|||
|
||||
If unsure, say N.
|
||||
|
||||
source "drivers/soc/bcm/brcmstb/Kconfig"
|
||||
|
||||
endmenu
|
||||
|
|
|
|||
10
drivers/soc/bcm/brcmstb/Kconfig
Normal file
10
drivers/soc/bcm/brcmstb/Kconfig
Normal 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
|
||||
|
|
@ -1 +1,2 @@
|
|||
obj-y += common.o biuctrl.o
|
||||
obj-$(CONFIG_BRCMSTB_PM) += pm/
|
||||
|
|
|
|||
3
drivers/soc/bcm/brcmstb/pm/Makefile
Normal file
3
drivers/soc/bcm/brcmstb/pm/Makefile
Normal 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
|
||||
113
drivers/soc/bcm/brcmstb/pm/aon_defs.h
Normal file
113
drivers/soc/bcm/brcmstb/pm/aon_defs.h
Normal 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__ */
|
||||
822
drivers/soc/bcm/brcmstb/pm/pm-arm.c
Normal file
822
drivers/soc/bcm/brcmstb/pm/pm-arm.c
Normal 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);
|
||||
461
drivers/soc/bcm/brcmstb/pm/pm-mips.c
Normal file
461
drivers/soc/bcm/brcmstb/pm/pm-mips.c
Normal 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);
|
||||
89
drivers/soc/bcm/brcmstb/pm/pm.h
Normal file
89
drivers/soc/bcm/brcmstb/pm/pm.h
Normal 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__ */
|
||||
76
drivers/soc/bcm/brcmstb/pm/s2-arm.S
Normal file
76
drivers/soc/bcm/brcmstb/pm/s2-arm.S
Normal 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
|
||||
200
drivers/soc/bcm/brcmstb/pm/s2-mips.S
Normal file
200
drivers/soc/bcm/brcmstb/pm/s2-mips.S
Normal 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
|
||||
|
||||
146
drivers/soc/bcm/brcmstb/pm/s3-mips.S
Normal file
146
drivers/soc/bcm/brcmstb/pm/s3-mips.S
Normal 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)
|
||||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
78
drivers/soc/fsl/qbman/dpaa_sys.c
Normal file
78
drivers/soc/fsl/qbman/dpaa_sys.c
Normal 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;
|
||||
}
|
||||
|
|
@ -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 */
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
/*
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
269
drivers/soc/qcom/rmtfs_mem.c
Normal file
269
drivers/soc/qcom/rmtfs_mem.c
Normal 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);
|
||||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
39
drivers/soc/renesas/r8a77970-sysc.c
Normal file
39
drivers/soc/renesas/r8a77970-sysc.c
Normal 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),
|
||||
};
|
||||
|
|
@ -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 */ }
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
Loading…
Reference in a new issue