Commit 022e5220 authored by Arnd Bergmann's avatar Arnd Bergmann

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.comSigned-off-by: default avatarArnd Bergmann <arnd@arndb.de>
parents 16018c0d d0054a47
......@@ -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.
......
......@@ -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*/ }
};
......
......@@ -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:
......
......@@ -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"
......
......@@ -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/
......
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.
obj-$(CONFIG_POLARFIRE_SOC_SYS_CTRL) += mpfs-sys-controller.o
// 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");
......@@ -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 */
......
......@@ -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__ */
......@@ -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) */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment