Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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 <linus.walleij@linaro.org>
Reviewed-by: Stephan Gerhold <stephan@gerhold.net>
Acked-by: Olof Johansson <olof@lixom.net>
Signed-off-by: Lee Jones <lee.jones@linaro.org>

authored by

Linus Walleij and committed by
Lee Jones
22fb3ad0 59dbc0e0

+50 -26
+1 -1
arch/arm/mach-ux500/cpu-db8500.c
··· 84 84 struct resource r; 85 85 86 86 irqchip_init(); 87 + prcmu_early_init(); 87 88 np = of_find_compatible_node(NULL, NULL, "stericsson,db8500-prcmu"); 88 89 of_address_to_resource(np, 0, &r); 89 90 of_node_put(np); ··· 92 91 pr_err("could not find PRCMU base resource\n"); 93 92 return; 94 93 } 95 - prcmu_early_init(r.start, r.end-r.start); 96 94 ux500_pm_init(r.start, r.end-r.start); 97 95 98 96 /* Unlock before init */
+43 -20
drivers/mfd/db8500-prcmu.c
··· 27 27 #include <linux/bitops.h> 28 28 #include <linux/fs.h> 29 29 #include <linux/of.h> 30 + #include <linux/of_address.h> 30 31 #include <linux/of_irq.h> 31 32 #include <linux/platform_device.h> 32 33 #include <linux/uaccess.h> ··· 667 666 struct prcmu_fw_version *prcmu_get_fw_version(void) 668 667 { 669 668 return fw_info.valid ? &fw_info.version : NULL; 669 + } 670 + 671 + static bool prcmu_is_ulppll_disabled(void) 672 + { 673 + struct prcmu_fw_version *ver; 674 + 675 + ver = prcmu_get_fw_version(); 676 + return ver && ver->project == PRCMU_FW_PROJECT_U8420_SYSCLK; 670 677 } 671 678 672 679 bool prcmu_has_arm_maxopp(void) ··· 1317 1308 1318 1309 static int request_timclk(bool enable) 1319 1310 { 1320 - u32 val = (PRCM_TCR_DOZE_MODE | PRCM_TCR_TENSEL_MASK); 1311 + u32 val; 1312 + 1313 + /* 1314 + * On the U8420_CLKSEL firmware, the ULP (Ultra Low Power) 1315 + * PLL is disabled so we cannot use doze mode, this will 1316 + * stop the clock on this firmware. 1317 + */ 1318 + if (prcmu_is_ulppll_disabled()) 1319 + val = 0; 1320 + else 1321 + val = (PRCM_TCR_DOZE_MODE | PRCM_TCR_TENSEL_MASK); 1321 1322 1322 1323 if (!enable) 1323 - val |= PRCM_TCR_STOP_TIMERS; 1324 + val |= PRCM_TCR_STOP_TIMERS | 1325 + PRCM_TCR_DOZE_MODE | 1326 + PRCM_TCR_TENSEL_MASK; 1327 + 1324 1328 writel(val, PRCM_TCR); 1325 1329 1326 1330 return 0; ··· 1637 1615 if (clock < PRCMU_NUM_REG_CLOCKS) 1638 1616 return clock_rate(clock); 1639 1617 else if (clock == PRCMU_TIMCLK) 1640 - return ROOT_CLOCK_RATE / 16; 1618 + return prcmu_is_ulppll_disabled() ? 1619 + 32768 : ROOT_CLOCK_RATE / 16; 1641 1620 else if (clock == PRCMU_SYSCLK) 1642 1621 return ROOT_CLOCK_RATE; 1643 1622 else if (clock == PRCMU_PLLSOC0) ··· 2669 2646 return "U8520 MBL"; 2670 2647 case PRCMU_FW_PROJECT_U8420: 2671 2648 return "U8420"; 2649 + case PRCMU_FW_PROJECT_U8420_SYSCLK: 2650 + return "U8420-sysclk"; 2672 2651 case PRCMU_FW_PROJECT_U9540: 2673 2652 return "U9540"; 2674 2653 case PRCMU_FW_PROJECT_A9420: ··· 2718 2693 return 0; 2719 2694 } 2720 2695 2721 - static void dbx500_fw_version_init(struct platform_device *pdev, 2722 - u32 version_offset) 2696 + static void dbx500_fw_version_init(struct device_node *np) 2723 2697 { 2724 - struct resource *res; 2725 2698 void __iomem *tcpm_base; 2726 2699 u32 version; 2727 2700 2728 - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, 2729 - "prcmu-tcpm"); 2730 - if (!res) { 2731 - dev_err(&pdev->dev, 2732 - "Error: no prcmu tcpm memory region provided\n"); 2733 - return; 2734 - } 2735 - tcpm_base = ioremap(res->start, resource_size(res)); 2701 + tcpm_base = of_iomap(np, 1); 2736 2702 if (!tcpm_base) { 2737 - dev_err(&pdev->dev, "no prcmu tcpm mem region provided\n"); 2703 + pr_err("no prcmu tcpm mem region provided\n"); 2738 2704 return; 2739 2705 } 2740 2706 2741 - version = readl(tcpm_base + version_offset); 2707 + version = readl(tcpm_base + DB8500_PRCMU_FW_VERSION_OFFSET); 2742 2708 fw_info.version.project = (version & 0xFF); 2743 2709 fw_info.version.api_version = (version >> 8) & 0xFF; 2744 2710 fw_info.version.func_version = (version >> 16) & 0xFF; ··· 2747 2731 iounmap(tcpm_base); 2748 2732 } 2749 2733 2750 - void __init db8500_prcmu_early_init(u32 phy_base, u32 size) 2734 + void __init db8500_prcmu_early_init(void) 2751 2735 { 2752 2736 /* 2753 2737 * This is a temporary remap to bring up the clocks. It is ··· 2756 2740 * clock driver can probe independently. An early initcall will 2757 2741 * still be needed, but it can be diverted into drivers/clk/ux500. 2758 2742 */ 2759 - prcmu_base = ioremap(phy_base, size); 2760 - if (!prcmu_base) 2743 + struct device_node *np; 2744 + 2745 + np = of_find_compatible_node(NULL, NULL, "stericsson,db8500-prcmu"); 2746 + prcmu_base = of_iomap(np, 0); 2747 + if (!prcmu_base) { 2748 + of_node_put(np); 2761 2749 pr_err("%s: ioremap() of prcmu registers failed!\n", __func__); 2750 + return; 2751 + } 2752 + dbx500_fw_version_init(np); 2753 + of_node_put(np); 2762 2754 2763 2755 spin_lock_init(&mb0_transfer.lock); 2764 2756 spin_lock_init(&mb0_transfer.dbb_irqs_lock); ··· 3108 3084 return -ENOMEM; 3109 3085 } 3110 3086 init_prcm_registers(); 3111 - dbx500_fw_version_init(pdev, DB8500_PRCMU_FW_VERSION_OFFSET); 3112 3087 res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "prcmu-tcdm"); 3113 3088 if (!res) { 3114 3089 dev_err(&pdev->dev, "no prcmu tcdm region provided\n");
+2 -2
include/linux/mfd/db8500-prcmu.h
··· 489 489 490 490 #ifdef CONFIG_MFD_DB8500_PRCMU 491 491 492 - void db8500_prcmu_early_init(u32 phy_base, u32 size); 492 + void db8500_prcmu_early_init(void); 493 493 int prcmu_set_rc_a2p(enum romcode_write); 494 494 enum romcode_read prcmu_get_rc_p2a(void); 495 495 enum ap_pwrst prcmu_get_xp70_current_state(void); ··· 546 546 547 547 #else /* !CONFIG_MFD_DB8500_PRCMU */ 548 548 549 - static inline void db8500_prcmu_early_init(u32 phy_base, u32 size) {} 549 + static inline void db8500_prcmu_early_init(void) {} 550 550 551 551 static inline int prcmu_set_rc_a2p(enum romcode_write code) 552 552 {
+4 -3
include/linux/mfd/dbx500-prcmu.h
··· 190 190 #define PRCMU_FW_PROJECT_U8500_MBL2 12 /* Customer specific */ 191 191 #define PRCMU_FW_PROJECT_U8520 13 192 192 #define PRCMU_FW_PROJECT_U8420 14 193 + #define PRCMU_FW_PROJECT_U8420_SYSCLK 17 193 194 #define PRCMU_FW_PROJECT_A9420 20 194 195 /* [32..63] 9540 and derivatives */ 195 196 #define PRCMU_FW_PROJECT_U9540 32 ··· 212 211 213 212 #if defined(CONFIG_UX500_SOC_DB8500) 214 213 215 - static inline void prcmu_early_init(u32 phy_base, u32 size) 214 + static inline void prcmu_early_init(void) 216 215 { 217 - return db8500_prcmu_early_init(phy_base, size); 216 + return db8500_prcmu_early_init(); 218 217 } 219 218 220 219 static inline int prcmu_set_power_state(u8 state, bool keep_ulp_clk, ··· 402 401 } 403 402 #else 404 403 405 - static inline void prcmu_early_init(u32 phy_base, u32 size) {} 404 + static inline void prcmu_early_init(void) {} 406 405 407 406 static inline int prcmu_set_power_state(u8 state, bool keep_ulp_clk, 408 407 bool keep_ap_pll)