summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorArnd Bergmann <arnd@arndb.de>2022-02-25 16:35:16 +0100
committerArnd Bergmann <arnd@arndb.de>2022-02-25 16:35:17 +0100
commit022e5220f2721d00610c0bf2926e8baa4e5fb4e2 (patch)
treebf2e679c80f0c77947e2fdc54f476eb52528266a
parent16018c0d27eda6a7f69dafa750d23770fb46b00f (diff)
parentd0054a470c33902f5ae88835ed8a8ecc3cf8faa4 (diff)
Merge tag 'at91-soc-5.18' of git://git.kernel.org/pub/scm/linux/kernel/git/at91/linux into arm/soc
AT91 & POLARFIRE SoC #1 for 5.18: - sama7g5: CPU idle support with CPUFreq operating points defined in DT - polarfire: addition of the soc system controller * tag 'at91-soc-5.18' of git://git.kernel.org/pub/scm/linux/kernel/git/at91/linux: soc: add microchip polarfire soc system controller ARM: at91: Kconfig: select PM_OPP ARM: at91: PM: add cpu idle support for sama7g5 ARM: at91: ddr: fix typo to align with datasheet naming ARM: at91: ddr: align macro definitions ARM: at91: ddr: remove CONFIG_SOC_SAMA7 dependency Link: https://lore.kernel.org/r/20220225121943.71494-1-nicolas.ferre@microchip.com Signed-off-by: Arnd Bergmann <arnd@arndb.de>
-rw-r--r--arch/arm/mach-at91/Kconfig1
-rw-r--r--arch/arm/mach-at91/pm.c27
-rw-r--r--arch/arm/mach-at91/pm_suspend.S4
-rw-r--r--drivers/soc/Kconfig1
-rw-r--r--drivers/soc/Makefile1
-rw-r--r--drivers/soc/microchip/Kconfig10
-rw-r--r--drivers/soc/microchip/Makefile1
-rw-r--r--drivers/soc/microchip/mpfs-sys-controller.c194
-rw-r--r--include/linux/clk/at91_pmc.h4
-rw-r--r--include/soc/at91/sama7-ddr.h15
-rw-r--r--include/soc/microchip/mpfs.h4
11 files changed, 248 insertions, 14 deletions
diff --git a/arch/arm/mach-at91/Kconfig b/arch/arm/mach-at91/Kconfig
index 02f6b108fd5d..279810381256 100644
--- a/arch/arm/mach-at91/Kconfig
+++ b/arch/arm/mach-at91/Kconfig
@@ -63,6 +63,7 @@ config SOC_SAMA7G5
select HAVE_AT91_GENERATED_CLK
select HAVE_AT91_SAM9X60_PLL
select HAVE_AT91_UTMI
+ select PM_OPP
select SOC_SAMA7
help
Select this if you are using one of Microchip's SAMA7G5 family SoC.
diff --git a/arch/arm/mach-at91/pm.c b/arch/arm/mach-at91/pm.c
index dd6f4ce3f766..0fd609e26615 100644
--- a/arch/arm/mach-at91/pm.c
+++ b/arch/arm/mach-at91/pm.c
@@ -605,6 +605,30 @@ static void at91sam9_sdram_standby(void)
at91_ramc_write(1, AT91_SDRAMC_LPR, saved_lpr1);
}
+static void sama7g5_standby(void)
+{
+ int pwrtmg, ratio;
+
+ pwrtmg = readl(soc_pm.data.ramc[0] + UDDRC_PWRCTL);
+ ratio = readl(soc_pm.data.pmc + AT91_PMC_RATIO);
+
+ /*
+ * Place RAM into self-refresh after a maximum idle clocks. The maximum
+ * idle clocks is configured by bootloader in
+ * UDDRC_PWRMGT.SELFREF_TO_X32.
+ */
+ writel(pwrtmg | UDDRC_PWRCTL_SELFREF_EN,
+ soc_pm.data.ramc[0] + UDDRC_PWRCTL);
+ /* Divide CPU clock by 16. */
+ writel(ratio & ~AT91_PMC_RATIO_RATIO, soc_pm.data.pmc + AT91_PMC_RATIO);
+
+ cpu_do_idle();
+
+ /* Restore previous configuration. */
+ writel(ratio, soc_pm.data.pmc + AT91_PMC_RATIO);
+ writel(pwrtmg, soc_pm.data.ramc[0] + UDDRC_PWRCTL);
+}
+
struct ramc_info {
void (*idle)(void);
unsigned int memctrl;
@@ -615,6 +639,7 @@ static const struct ramc_info ramc_infos[] __initconst = {
{ .idle = at91sam9_sdram_standby, .memctrl = AT91_MEMCTRL_SDRAMC},
{ .idle = at91_ddr_standby, .memctrl = AT91_MEMCTRL_DDRSDR},
{ .idle = sama5d3_ddr_standby, .memctrl = AT91_MEMCTRL_DDRSDR},
+ { .idle = sama7g5_standby, },
};
static const struct of_device_id ramc_ids[] __initconst = {
@@ -622,7 +647,7 @@ static const struct of_device_id ramc_ids[] __initconst = {
{ .compatible = "atmel,at91sam9260-sdramc", .data = &ramc_infos[1] },
{ .compatible = "atmel,at91sam9g45-ddramc", .data = &ramc_infos[2] },
{ .compatible = "atmel,sama5d3-ddramc", .data = &ramc_infos[3] },
- { .compatible = "microchip,sama7g5-uddrc", },
+ { .compatible = "microchip,sama7g5-uddrc", .data = &ramc_infos[4], },
{ /*sentinel*/ }
};
diff --git a/arch/arm/mach-at91/pm_suspend.S b/arch/arm/mach-at91/pm_suspend.S
index fdb4f63ecde4..abe4ced33eda 100644
--- a/arch/arm/mach-at91/pm_suspend.S
+++ b/arch/arm/mach-at91/pm_suspend.S
@@ -159,7 +159,7 @@ sr_ena_1:
/* Switch to self-refresh. */
ldr tmp1, [r2, #UDDRC_PWRCTL]
- orr tmp1, tmp1, #UDDRC_PWRCTRL_SELFREF_SW
+ orr tmp1, tmp1, #UDDRC_PWRCTL_SELFREF_SW
str tmp1, [r2, #UDDRC_PWRCTL]
sr_ena_2:
@@ -276,7 +276,7 @@ sr_dis_5:
/* Trigger self-refresh exit. */
ldr tmp1, [r2, #UDDRC_PWRCTL]
- bic tmp1, tmp1, #UDDRC_PWRCTRL_SELFREF_SW
+ bic tmp1, tmp1, #UDDRC_PWRCTL_SELFREF_SW
str tmp1, [r2, #UDDRC_PWRCTL]
sr_dis_6:
diff --git a/drivers/soc/Kconfig b/drivers/soc/Kconfig
index a8562678c437..c5aae42673d3 100644
--- a/drivers/soc/Kconfig
+++ b/drivers/soc/Kconfig
@@ -13,6 +13,7 @@ source "drivers/soc/imx/Kconfig"
source "drivers/soc/ixp4xx/Kconfig"
source "drivers/soc/litex/Kconfig"
source "drivers/soc/mediatek/Kconfig"
+source "drivers/soc/microchip/Kconfig"
source "drivers/soc/qcom/Kconfig"
source "drivers/soc/renesas/Kconfig"
source "drivers/soc/rockchip/Kconfig"
diff --git a/drivers/soc/Makefile b/drivers/soc/Makefile
index adb30c2d4fea..904eec2a7871 100644
--- a/drivers/soc/Makefile
+++ b/drivers/soc/Makefile
@@ -18,6 +18,7 @@ obj-y += ixp4xx/
obj-$(CONFIG_SOC_XWAY) += lantiq/
obj-$(CONFIG_LITEX_SOC_CONTROLLER) += litex/
obj-y += mediatek/
+obj-y += microchip/
obj-y += amlogic/
obj-y += qcom/
obj-y += renesas/
diff --git a/drivers/soc/microchip/Kconfig b/drivers/soc/microchip/Kconfig
new file mode 100644
index 000000000000..eb656b33156b
--- /dev/null
+++ b/drivers/soc/microchip/Kconfig
@@ -0,0 +1,10 @@
+config POLARFIRE_SOC_SYS_CTRL
+ tristate "POLARFIRE_SOC_SYS_CTRL"
+ depends on POLARFIRE_SOC_MAILBOX
+ help
+ This driver adds support for the PolarFire SoC (MPFS) system controller.
+
+ To compile this driver as a module, choose M here. the
+ module will be called mpfs_system_controller.
+
+ If unsure, say N.
diff --git a/drivers/soc/microchip/Makefile b/drivers/soc/microchip/Makefile
new file mode 100644
index 000000000000..14489919fe4b
--- /dev/null
+++ b/drivers/soc/microchip/Makefile
@@ -0,0 +1 @@
+obj-$(CONFIG_POLARFIRE_SOC_SYS_CTRL) += mpfs-sys-controller.o
diff --git a/drivers/soc/microchip/mpfs-sys-controller.c b/drivers/soc/microchip/mpfs-sys-controller.c
new file mode 100644
index 000000000000..2f4535929762
--- /dev/null
+++ b/drivers/soc/microchip/mpfs-sys-controller.c
@@ -0,0 +1,194 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Microchip PolarFire SoC (MPFS) system controller driver
+ *
+ * Copyright (c) 2020-2021 Microchip Corporation. All rights reserved.
+ *
+ * Author: Conor Dooley <conor.dooley@microchip.com>
+ *
+ */
+
+#include <linux/slab.h>
+#include <linux/kref.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/of_platform.h>
+#include <linux/mailbox_client.h>
+#include <linux/platform_device.h>
+#include <soc/microchip/mpfs.h>
+
+static DEFINE_MUTEX(transaction_lock);
+
+struct mpfs_sys_controller {
+ struct mbox_client client;
+ struct mbox_chan *chan;
+ struct completion c;
+ struct kref consumers;
+};
+
+int mpfs_blocking_transaction(struct mpfs_sys_controller *sys_controller, struct mpfs_mss_msg *msg)
+{
+ int ret, err;
+
+ err = mutex_lock_interruptible(&transaction_lock);
+ if (err)
+ return err;
+
+ reinit_completion(&sys_controller->c);
+
+ ret = mbox_send_message(sys_controller->chan, msg);
+ if (ret >= 0) {
+ if (wait_for_completion_timeout(&sys_controller->c, HZ)) {
+ ret = 0;
+ } else {
+ ret = -ETIMEDOUT;
+ dev_warn(sys_controller->client.dev,
+ "MPFS sys controller transaction timeout\n");
+ }
+ } else {
+ dev_err(sys_controller->client.dev,
+ "mpfs sys controller transaction returned %d\n", ret);
+ }
+
+ mutex_unlock(&transaction_lock);
+
+ return ret;
+}
+EXPORT_SYMBOL(mpfs_blocking_transaction);
+
+static void rx_callback(struct mbox_client *client, void *msg)
+{
+ struct mpfs_sys_controller *sys_controller =
+ container_of(client, struct mpfs_sys_controller, client);
+
+ complete(&sys_controller->c);
+}
+
+static void mpfs_sys_controller_delete(struct kref *kref)
+{
+ struct mpfs_sys_controller *sys_controller = container_of(kref, struct mpfs_sys_controller,
+ consumers);
+
+ mbox_free_channel(sys_controller->chan);
+ kfree(sys_controller);
+}
+
+void mpfs_sys_controller_put(void *data)
+{
+ struct mpfs_sys_controller *sys_controller = data;
+
+ kref_put(&sys_controller->consumers, mpfs_sys_controller_delete);
+}
+EXPORT_SYMBOL(mpfs_sys_controller_put);
+
+static struct platform_device subdevs[] = {
+ {
+ .name = "mpfs-rng",
+ .id = -1,
+ },
+ {
+ .name = "mpfs-generic-service",
+ .id = -1,
+ }
+};
+
+static int mpfs_sys_controller_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct mpfs_sys_controller *sys_controller;
+ int i;
+
+ sys_controller = devm_kzalloc(dev, sizeof(*sys_controller), GFP_KERNEL);
+ if (!sys_controller)
+ return -ENOMEM;
+
+ sys_controller->client.dev = dev;
+ sys_controller->client.rx_callback = rx_callback;
+ sys_controller->client.tx_block = 1U;
+
+ sys_controller->chan = mbox_request_channel(&sys_controller->client, 0);
+ if (IS_ERR(sys_controller->chan))
+ return dev_err_probe(dev, PTR_ERR(sys_controller->chan),
+ "Failed to get mbox channel\n");
+
+ init_completion(&sys_controller->c);
+ kref_init(&sys_controller->consumers);
+
+ platform_set_drvdata(pdev, sys_controller);
+
+ dev_info(&pdev->dev, "Registered MPFS system controller\n");
+
+ for (i = 0; i < ARRAY_SIZE(subdevs); i++) {
+ subdevs[i].dev.parent = dev;
+ if (platform_device_register(&subdevs[i]))
+ dev_warn(dev, "Error registering sub device %s\n", subdevs[i].name);
+ }
+
+ return 0;
+}
+
+static int mpfs_sys_controller_remove(struct platform_device *pdev)
+{
+ struct mpfs_sys_controller *sys_controller = platform_get_drvdata(pdev);
+
+ mpfs_sys_controller_put(sys_controller);
+
+ return 0;
+}
+
+static const struct of_device_id mpfs_sys_controller_of_match[] = {
+ {.compatible = "microchip,mpfs-sys-controller", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, mpfs_sys_controller_of_match);
+
+struct mpfs_sys_controller *mpfs_sys_controller_get(struct device *dev)
+{
+ const struct of_device_id *match;
+ struct mpfs_sys_controller *sys_controller;
+ int ret;
+
+ if (!dev->parent)
+ goto err_no_device;
+
+ match = of_match_node(mpfs_sys_controller_of_match, dev->parent->of_node);
+ of_node_put(dev->parent->of_node);
+ if (!match)
+ goto err_no_device;
+
+ sys_controller = dev_get_drvdata(dev->parent);
+ if (!sys_controller)
+ goto err_bad_device;
+
+ if (!kref_get_unless_zero(&sys_controller->consumers))
+ goto err_bad_device;
+
+ ret = devm_add_action_or_reset(dev, mpfs_sys_controller_put, sys_controller);
+ if (ret)
+ return ERR_PTR(ret);
+
+ return sys_controller;
+
+err_no_device:
+ dev_dbg(dev, "Parent device was not an MPFS system controller\n");
+ return ERR_PTR(-ENODEV);
+
+err_bad_device:
+ dev_dbg(dev, "MPFS system controller found but could not register as a sub device\n");
+ return ERR_PTR(-EPROBE_DEFER);
+}
+EXPORT_SYMBOL(mpfs_sys_controller_get);
+
+static struct platform_driver mpfs_sys_controller_driver = {
+ .driver = {
+ .name = "mpfs-sys-controller",
+ .of_match_table = mpfs_sys_controller_of_match,
+ },
+ .probe = mpfs_sys_controller_probe,
+ .remove = mpfs_sys_controller_remove,
+};
+module_platform_driver(mpfs_sys_controller_driver);
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Conor Dooley <conor.dooley@microchip.com>");
+MODULE_DESCRIPTION("MPFS system controller driver");
diff --git a/include/linux/clk/at91_pmc.h b/include/linux/clk/at91_pmc.h
index ccb3f034bfa9..3484309b59bf 100644
--- a/include/linux/clk/at91_pmc.h
+++ b/include/linux/clk/at91_pmc.h
@@ -78,6 +78,10 @@
#define AT91_PMC_MAINRDY (1 << 16) /* Main Clock Ready */
#define AT91_CKGR_PLLAR 0x28 /* PLL A Register */
+
+#define AT91_PMC_RATIO 0x2c /* Processor clock ratio register [SAMA7G5 only] */
+#define AT91_PMC_RATIO_RATIO (0xf) /* CPU clock ratio. */
+
#define AT91_CKGR_PLLBR 0x2c /* PLL B Register */
#define AT91_PMC_DIV (0xff << 0) /* Divider */
#define AT91_PMC_PLLCOUNT (0x3f << 8) /* PLL Counter */
diff --git a/include/soc/at91/sama7-ddr.h b/include/soc/at91/sama7-ddr.h
index f6542584ca13..9e17247474fa 100644
--- a/include/soc/at91/sama7-ddr.h
+++ b/include/soc/at91/sama7-ddr.h
@@ -11,15 +11,13 @@
#ifndef __SAMA7_DDR_H__
#define __SAMA7_DDR_H__
-#ifdef CONFIG_SOC_SAMA7
-
/* DDR3PHY */
#define DDR3PHY_PIR (0x04) /* DDR3PHY PHY Initialization Register */
-#define DDR3PHY_PIR_DLLBYP (1 << 17) /* DLL Bypass */
+#define DDR3PHY_PIR_DLLBYP (1 << 17) /* DLL Bypass */
#define DDR3PHY_PIR_ITMSRST (1 << 4) /* Interface Timing Module Soft Reset */
-#define DDR3PHY_PIR_DLLLOCK (1 << 2) /* DLL Lock */
+#define DDR3PHY_PIR_DLLLOCK (1 << 2) /* DLL Lock */
#define DDR3PHY_PIR_DLLSRST (1 << 1) /* DLL Soft Rest */
-#define DDR3PHY_PIR_INIT (1 << 0) /* Initialization Trigger */
+#define DDR3PHY_PIR_INIT (1 << 0) /* Initialization Trigger */
#define DDR3PHY_PGCR (0x08) /* DDR3PHY PHY General Configuration Register */
#define DDR3PHY_PGCR_CKDV1 (1 << 13) /* CK# Disable Value */
@@ -55,7 +53,8 @@
#define UDDRC_STAT_OPMODE_MSK (0x7 << 0) /* Operating mode mask */
#define UDDRC_PWRCTL (0x30) /* UDDRC Low Power Control Register */
-#define UDDRC_PWRCTRL_SELFREF_SW (1 << 5) /* Software self-refresh */
+#define UDDRC_PWRCTL_SELFREF_EN (1 << 0) /* Automatic self-refresh */
+#define UDDRC_PWRCTL_SELFREF_SW (1 << 5) /* Software self-refresh */
#define UDDRC_DFIMISC (0x1B0) /* UDDRC DFI Miscellaneous Control Register */
#define UDDRC_DFIMISC_DFI_INIT_COMPLETE_EN (1 << 0) /* PHY initialization complete enable signal */
@@ -67,7 +66,7 @@
#define UDDRC_SWSTAT_SW_DONE_ACK (1 << 0) /* Register programming done */
#define UDDRC_PSTAT (0x3FC) /* UDDRC Port Status Register */
-#define UDDRC_PSTAT_ALL_PORTS (0x1F001F) /* Read + writes outstanding transactions on all ports */
+#define UDDRC_PSTAT_ALL_PORTS (0x1F001F) /* Read + writes outstanding transactions on all ports */
#define UDDRC_PCTRL_0 (0x490) /* UDDRC Port 0 Control Register */
#define UDDRC_PCTRL_1 (0x540) /* UDDRC Port 1 Control Register */
@@ -75,6 +74,4 @@
#define UDDRC_PCTRL_3 (0x6A0) /* UDDRC Port 3 Control Register */
#define UDDRC_PCTRL_4 (0x750) /* UDDRC Port 4 Control Register */
-#endif /* CONFIG_SOC_SAMA7 */
-
#endif /* __SAMA7_DDR_H__ */
diff --git a/include/soc/microchip/mpfs.h b/include/soc/microchip/mpfs.h
index 2b64c95f3be5..6466515262bd 100644
--- a/include/soc/microchip/mpfs.h
+++ b/include/soc/microchip/mpfs.h
@@ -34,9 +34,9 @@ struct mpfs_mss_response {
#if IS_ENABLED(CONFIG_POLARFIRE_SOC_SYS_CTRL)
-int mpfs_blocking_transaction(struct mpfs_sys_controller *mpfs_client, void *msg);
+int mpfs_blocking_transaction(struct mpfs_sys_controller *mpfs_client, struct mpfs_mss_msg *msg);
-struct mpfs_sys_controller *mpfs_sys_controller_get(struct device_node *mailbox_node);
+struct mpfs_sys_controller *mpfs_sys_controller_get(struct device *dev);
#endif /* if IS_ENABLED(CONFIG_POLARFIRE_SOC_SYS_CTRL) */