Skip to content

Commit 22fb3ad

Browse files
linuswLee Jones
authored andcommitted
mfd: db8500-prcmu: Support U8420-sysclk firmware
There is a distinct version of the Ux500 U8420 variant with "sysclk", as can be seen from the vendor code that didn't make it upstream, this firmware lacks the ULPPLL (ultra-low power phase locked loop) which in effect means that the timer clock is instead wired to the 32768 Hz always-on clock. This has some repercussions when enabling the timer clock as the code as it stands will disable the timer clock on these platforms (lacking the so-called "doze mode") and obtaining the wrong rate of the timer clock. The timer frequency is of course needed very early in the boot, and as a consequence, we need to shuffle around the early PRCMU init code: whereas in the past we did not need to look up the PRCMU firmware version in the early init, but now we need to know the version before the core system timers are registered so we restructure the platform callbacks to the PRCMU so as not to take any arguments and instead look up the resources it needs directly from the device tree when initializing. As we do not yet support any platforms using this firmware it is not a regression, but as PostmarketOS is starting to support products with this firmware we need to fix this up. The low rate of 32kHz also makes the MTU timer unsuitable as delay timer but this needs to be fixed in a separate patch. Signed-off-by: Linus Walleij <[email protected]> Reviewed-by: Stephan Gerhold <[email protected]> Acked-by: Olof Johansson <[email protected]> Signed-off-by: Lee Jones <[email protected]>
1 parent 59dbc0e commit 22fb3ad

File tree

4 files changed

+50
-26
lines changed

4 files changed

+50
-26
lines changed

arch/arm/mach-ux500/cpu-db8500.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,14 +84,14 @@ static void __init ux500_init_irq(void)
8484
struct resource r;
8585

8686
irqchip_init();
87+
prcmu_early_init();
8788
np = of_find_compatible_node(NULL, NULL, "stericsson,db8500-prcmu");
8889
of_address_to_resource(np, 0, &r);
8990
of_node_put(np);
9091
if (!r.start) {
9192
pr_err("could not find PRCMU base resource\n");
9293
return;
9394
}
94-
prcmu_early_init(r.start, r.end-r.start);
9595
ux500_pm_init(r.start, r.end-r.start);
9696

9797
/* Unlock before init */

drivers/mfd/db8500-prcmu.c

Lines changed: 43 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
#include <linux/bitops.h>
2828
#include <linux/fs.h>
2929
#include <linux/of.h>
30+
#include <linux/of_address.h>
3031
#include <linux/of_irq.h>
3132
#include <linux/platform_device.h>
3233
#include <linux/uaccess.h>
@@ -668,6 +669,14 @@ struct prcmu_fw_version *prcmu_get_fw_version(void)
668669
return fw_info.valid ? &fw_info.version : NULL;
669670
}
670671

672+
static bool prcmu_is_ulppll_disabled(void)
673+
{
674+
struct prcmu_fw_version *ver;
675+
676+
ver = prcmu_get_fw_version();
677+
return ver && ver->project == PRCMU_FW_PROJECT_U8420_SYSCLK;
678+
}
679+
671680
bool prcmu_has_arm_maxopp(void)
672681
{
673682
return (readb(tcdm_base + PRCM_AVS_VARM_MAX_OPP) &
@@ -1308,10 +1317,23 @@ static int request_sysclk(bool enable)
13081317

13091318
static int request_timclk(bool enable)
13101319
{
1311-
u32 val = (PRCM_TCR_DOZE_MODE | PRCM_TCR_TENSEL_MASK);
1320+
u32 val;
1321+
1322+
/*
1323+
* On the U8420_CLKSEL firmware, the ULP (Ultra Low Power)
1324+
* PLL is disabled so we cannot use doze mode, this will
1325+
* stop the clock on this firmware.
1326+
*/
1327+
if (prcmu_is_ulppll_disabled())
1328+
val = 0;
1329+
else
1330+
val = (PRCM_TCR_DOZE_MODE | PRCM_TCR_TENSEL_MASK);
13121331

13131332
if (!enable)
1314-
val |= PRCM_TCR_STOP_TIMERS;
1333+
val |= PRCM_TCR_STOP_TIMERS |
1334+
PRCM_TCR_DOZE_MODE |
1335+
PRCM_TCR_TENSEL_MASK;
1336+
13151337
writel(val, PRCM_TCR);
13161338

13171339
return 0;
@@ -1615,7 +1637,8 @@ unsigned long prcmu_clock_rate(u8 clock)
16151637
if (clock < PRCMU_NUM_REG_CLOCKS)
16161638
return clock_rate(clock);
16171639
else if (clock == PRCMU_TIMCLK)
1618-
return ROOT_CLOCK_RATE / 16;
1640+
return prcmu_is_ulppll_disabled() ?
1641+
32768 : ROOT_CLOCK_RATE / 16;
16191642
else if (clock == PRCMU_SYSCLK)
16201643
return ROOT_CLOCK_RATE;
16211644
else if (clock == PRCMU_PLLSOC0)
@@ -2646,6 +2669,8 @@ static char *fw_project_name(u32 project)
26462669
return "U8520 MBL";
26472670
case PRCMU_FW_PROJECT_U8420:
26482671
return "U8420";
2672+
case PRCMU_FW_PROJECT_U8420_SYSCLK:
2673+
return "U8420-sysclk";
26492674
case PRCMU_FW_PROJECT_U9540:
26502675
return "U9540";
26512676
case PRCMU_FW_PROJECT_A9420:
@@ -2693,27 +2718,18 @@ static int db8500_irq_init(struct device_node *np)
26932718
return 0;
26942719
}
26952720

2696-
static void dbx500_fw_version_init(struct platform_device *pdev,
2697-
u32 version_offset)
2721+
static void dbx500_fw_version_init(struct device_node *np)
26982722
{
2699-
struct resource *res;
27002723
void __iomem *tcpm_base;
27012724
u32 version;
27022725

2703-
res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
2704-
"prcmu-tcpm");
2705-
if (!res) {
2706-
dev_err(&pdev->dev,
2707-
"Error: no prcmu tcpm memory region provided\n");
2708-
return;
2709-
}
2710-
tcpm_base = ioremap(res->start, resource_size(res));
2726+
tcpm_base = of_iomap(np, 1);
27112727
if (!tcpm_base) {
2712-
dev_err(&pdev->dev, "no prcmu tcpm mem region provided\n");
2728+
pr_err("no prcmu tcpm mem region provided\n");
27132729
return;
27142730
}
27152731

2716-
version = readl(tcpm_base + version_offset);
2732+
version = readl(tcpm_base + DB8500_PRCMU_FW_VERSION_OFFSET);
27172733
fw_info.version.project = (version & 0xFF);
27182734
fw_info.version.api_version = (version >> 8) & 0xFF;
27192735
fw_info.version.func_version = (version >> 16) & 0xFF;
@@ -2731,7 +2747,7 @@ static void dbx500_fw_version_init(struct platform_device *pdev,
27312747
iounmap(tcpm_base);
27322748
}
27332749

2734-
void __init db8500_prcmu_early_init(u32 phy_base, u32 size)
2750+
void __init db8500_prcmu_early_init(void)
27352751
{
27362752
/*
27372753
* This is a temporary remap to bring up the clocks. It is
@@ -2740,9 +2756,17 @@ void __init db8500_prcmu_early_init(u32 phy_base, u32 size)
27402756
* clock driver can probe independently. An early initcall will
27412757
* still be needed, but it can be diverted into drivers/clk/ux500.
27422758
*/
2743-
prcmu_base = ioremap(phy_base, size);
2744-
if (!prcmu_base)
2759+
struct device_node *np;
2760+
2761+
np = of_find_compatible_node(NULL, NULL, "stericsson,db8500-prcmu");
2762+
prcmu_base = of_iomap(np, 0);
2763+
if (!prcmu_base) {
2764+
of_node_put(np);
27452765
pr_err("%s: ioremap() of prcmu registers failed!\n", __func__);
2766+
return;
2767+
}
2768+
dbx500_fw_version_init(np);
2769+
of_node_put(np);
27462770

27472771
spin_lock_init(&mb0_transfer.lock);
27482772
spin_lock_init(&mb0_transfer.dbb_irqs_lock);
@@ -3084,7 +3108,6 @@ static int db8500_prcmu_probe(struct platform_device *pdev)
30843108
return -ENOMEM;
30853109
}
30863110
init_prcm_registers();
3087-
dbx500_fw_version_init(pdev, DB8500_PRCMU_FW_VERSION_OFFSET);
30883111
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "prcmu-tcdm");
30893112
if (!res) {
30903113
dev_err(&pdev->dev, "no prcmu tcdm region provided\n");

include/linux/mfd/db8500-prcmu.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -489,7 +489,7 @@ struct prcmu_auto_pm_config {
489489

490490
#ifdef CONFIG_MFD_DB8500_PRCMU
491491

492-
void db8500_prcmu_early_init(u32 phy_base, u32 size);
492+
void db8500_prcmu_early_init(void);
493493
int prcmu_set_rc_a2p(enum romcode_write);
494494
enum romcode_read prcmu_get_rc_p2a(void);
495495
enum ap_pwrst prcmu_get_xp70_current_state(void);
@@ -546,7 +546,7 @@ void db8500_prcmu_write_masked(unsigned int reg, u32 mask, u32 value);
546546

547547
#else /* !CONFIG_MFD_DB8500_PRCMU */
548548

549-
static inline void db8500_prcmu_early_init(u32 phy_base, u32 size) {}
549+
static inline void db8500_prcmu_early_init(void) {}
550550

551551
static inline int prcmu_set_rc_a2p(enum romcode_write code)
552552
{

include/linux/mfd/dbx500-prcmu.h

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,7 @@ enum ddr_pwrst {
190190
#define PRCMU_FW_PROJECT_U8500_MBL2 12 /* Customer specific */
191191
#define PRCMU_FW_PROJECT_U8520 13
192192
#define PRCMU_FW_PROJECT_U8420 14
193+
#define PRCMU_FW_PROJECT_U8420_SYSCLK 17
193194
#define PRCMU_FW_PROJECT_A9420 20
194195
/* [32..63] 9540 and derivatives */
195196
#define PRCMU_FW_PROJECT_U9540 32
@@ -211,9 +212,9 @@ struct prcmu_fw_version {
211212

212213
#if defined(CONFIG_UX500_SOC_DB8500)
213214

214-
static inline void prcmu_early_init(u32 phy_base, u32 size)
215+
static inline void prcmu_early_init(void)
215216
{
216-
return db8500_prcmu_early_init(phy_base, size);
217+
return db8500_prcmu_early_init();
217218
}
218219

219220
static inline int prcmu_set_power_state(u8 state, bool keep_ulp_clk,
@@ -401,7 +402,7 @@ static inline int prcmu_config_a9wdog(u8 num, bool sleep_auto_off)
401402
}
402403
#else
403404

404-
static inline void prcmu_early_init(u32 phy_base, u32 size) {}
405+
static inline void prcmu_early_init(void) {}
405406

406407
static inline int prcmu_set_power_state(u8 state, bool keep_ulp_clk,
407408
bool keep_ap_pll)

0 commit comments

Comments
 (0)