diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2017-11-17 03:05:01 +0300 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2017-11-17 03:05:01 +0300 |
commit | cf9b0772f2e410645fece13b749bd56505b998b8 (patch) | |
tree | 8b171a2c49d1e9e41d4e43fb91602e664cde8551 /drivers/soc/amlogic | |
parent | 527d1470744d338c912f94bc1f4dba08ffdff349 (diff) | |
parent | 339cd0ea082287ea8e2b7e7159a5a33665a2cbe3 (diff) | |
download | linux-cf9b0772f2e410645fece13b749bd56505b998b8.tar.xz |
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
..
Diffstat (limited to 'drivers/soc/amlogic')
-rw-r--r-- | drivers/soc/amlogic/Kconfig | 21 | ||||
-rw-r--r-- | drivers/soc/amlogic/Makefile | 2 | ||||
-rw-r--r-- | drivers/soc/amlogic/meson-gx-pwrc-vpu.c | 243 | ||||
-rw-r--r-- | drivers/soc/amlogic/meson-mx-socinfo.c | 175 |
4 files changed, 441 insertions, 0 deletions
diff --git a/drivers/soc/amlogic/Kconfig b/drivers/soc/amlogic/Kconfig index 22acf064531f..b04f6e4aedbc 100644 --- a/drivers/soc/amlogic/Kconfig +++ b/drivers/soc/amlogic/Kconfig @@ -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 diff --git a/drivers/soc/amlogic/Makefile b/drivers/soc/amlogic/Makefile index 3e85fc462c21..8fa321893928 100644 --- a/drivers/soc/amlogic/Makefile +++ b/drivers/soc/amlogic/Makefile @@ -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 diff --git a/drivers/soc/amlogic/meson-gx-pwrc-vpu.c b/drivers/soc/amlogic/meson-gx-pwrc-vpu.c new file mode 100644 index 000000000000..2bdeebc48901 --- /dev/null +++ b/drivers/soc/amlogic/meson-gx-pwrc-vpu.c @@ -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); diff --git a/drivers/soc/amlogic/meson-mx-socinfo.c b/drivers/soc/amlogic/meson-mx-socinfo.c new file mode 100644 index 000000000000..7bfff5ff22a2 --- /dev/null +++ b/drivers/soc/amlogic/meson-mx-socinfo.c @@ -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); |