hc
2024-02-20 102a0743326a03cd1a1202ceda21e175b7d3575c
kernel/drivers/mfd/db8500-prcmu.c
....@@ -1,16 +1,18 @@
1
+// SPDX-License-Identifier: GPL-2.0-only
12 /*
3
+ * DB8500 PRCM Unit driver
4
+ *
25 * Copyright (C) STMicroelectronics 2009
36 * Copyright (C) ST-Ericsson SA 2010
47 *
5
- * License Terms: GNU General Public License v2
68 * Author: Kumar Sanghvi <kumar.sanghvi@stericsson.com>
79 * Author: Sundar Iyer <sundar.iyer@stericsson.com>
810 * Author: Mattias Nilsson <mattias.i.nilsson@stericsson.com>
911 *
1012 * U8500 PRCM Unit interface driver
11
- *
1213 */
13
-#include <linux/module.h>
14
+#include <linux/init.h>
15
+#include <linux/export.h>
1416 #include <linux/kernel.h>
1517 #include <linux/delay.h>
1618 #include <linux/errno.h>
....@@ -25,6 +27,7 @@
2527 #include <linux/bitops.h>
2628 #include <linux/fs.h>
2729 #include <linux/of.h>
30
+#include <linux/of_address.h>
2831 #include <linux/of_irq.h>
2932 #include <linux/platform_device.h>
3033 #include <linux/uaccess.h>
....@@ -34,7 +37,6 @@
3437 #include <linux/regulator/db8500-prcmu.h>
3538 #include <linux/regulator/machine.h>
3639 #include <linux/platform_data/ux500_wdt.h>
37
-#include <linux/platform_data/db8500_thermal.h>
3840 #include "dbx500-prcmu-regs.h"
3941
4042 /* Index of different voltages to be used when accessing AVSData */
....@@ -540,102 +542,6 @@
540542 }
541543 };
542544
543
-
544
-/*
545
-* Used by MCDE to setup all necessary PRCMU registers
546
-*/
547
-#define PRCMU_RESET_DSIPLL 0x00004000
548
-#define PRCMU_UNCLAMP_DSIPLL 0x00400800
549
-
550
-#define PRCMU_CLK_PLL_DIV_SHIFT 0
551
-#define PRCMU_CLK_PLL_SW_SHIFT 5
552
-#define PRCMU_CLK_38 (1 << 9)
553
-#define PRCMU_CLK_38_SRC (1 << 10)
554
-#define PRCMU_CLK_38_DIV (1 << 11)
555
-
556
-/* PLLDIV=12, PLLSW=4 (PLLDDR) */
557
-#define PRCMU_DSI_CLOCK_SETTING 0x0000008C
558
-
559
-/* DPI 50000000 Hz */
560
-#define PRCMU_DPI_CLOCK_SETTING ((1 << PRCMU_CLK_PLL_SW_SHIFT) | \
561
- (16 << PRCMU_CLK_PLL_DIV_SHIFT))
562
-#define PRCMU_DSI_LP_CLOCK_SETTING 0x00000E00
563
-
564
-/* D=101, N=1, R=4, SELDIV2=0 */
565
-#define PRCMU_PLLDSI_FREQ_SETTING 0x00040165
566
-
567
-#define PRCMU_ENABLE_PLLDSI 0x00000001
568
-#define PRCMU_DISABLE_PLLDSI 0x00000000
569
-#define PRCMU_RELEASE_RESET_DSS 0x0000400C
570
-#define PRCMU_DSI_PLLOUT_SEL_SETTING 0x00000202
571
-/* ESC clk, div0=1, div1=1, div2=3 */
572
-#define PRCMU_ENABLE_ESCAPE_CLOCK_DIV 0x07030101
573
-#define PRCMU_DISABLE_ESCAPE_CLOCK_DIV 0x00030101
574
-#define PRCMU_DSI_RESET_SW 0x00000007
575
-
576
-#define PRCMU_PLLDSI_LOCKP_LOCKED 0x3
577
-
578
-int db8500_prcmu_enable_dsipll(void)
579
-{
580
- int i;
581
-
582
- /* Clear DSIPLL_RESETN */
583
- writel(PRCMU_RESET_DSIPLL, PRCM_APE_RESETN_CLR);
584
- /* Unclamp DSIPLL in/out */
585
- writel(PRCMU_UNCLAMP_DSIPLL, PRCM_MMIP_LS_CLAMP_CLR);
586
-
587
- /* Set DSI PLL FREQ */
588
- writel(PRCMU_PLLDSI_FREQ_SETTING, PRCM_PLLDSI_FREQ);
589
- writel(PRCMU_DSI_PLLOUT_SEL_SETTING, PRCM_DSI_PLLOUT_SEL);
590
- /* Enable Escape clocks */
591
- writel(PRCMU_ENABLE_ESCAPE_CLOCK_DIV, PRCM_DSITVCLK_DIV);
592
-
593
- /* Start DSI PLL */
594
- writel(PRCMU_ENABLE_PLLDSI, PRCM_PLLDSI_ENABLE);
595
- /* Reset DSI PLL */
596
- writel(PRCMU_DSI_RESET_SW, PRCM_DSI_SW_RESET);
597
- for (i = 0; i < 10; i++) {
598
- if ((readl(PRCM_PLLDSI_LOCKP) & PRCMU_PLLDSI_LOCKP_LOCKED)
599
- == PRCMU_PLLDSI_LOCKP_LOCKED)
600
- break;
601
- udelay(100);
602
- }
603
- /* Set DSIPLL_RESETN */
604
- writel(PRCMU_RESET_DSIPLL, PRCM_APE_RESETN_SET);
605
- return 0;
606
-}
607
-
608
-int db8500_prcmu_disable_dsipll(void)
609
-{
610
- /* Disable dsi pll */
611
- writel(PRCMU_DISABLE_PLLDSI, PRCM_PLLDSI_ENABLE);
612
- /* Disable escapeclock */
613
- writel(PRCMU_DISABLE_ESCAPE_CLOCK_DIV, PRCM_DSITVCLK_DIV);
614
- return 0;
615
-}
616
-
617
-int db8500_prcmu_set_display_clocks(void)
618
-{
619
- unsigned long flags;
620
-
621
- spin_lock_irqsave(&clk_mgt_lock, flags);
622
-
623
- /* Grab the HW semaphore. */
624
- while ((readl(PRCM_SEM) & PRCM_SEM_PRCM_SEM) != 0)
625
- cpu_relax();
626
-
627
- writel(PRCMU_DSI_CLOCK_SETTING, prcmu_base + PRCM_HDMICLK_MGT);
628
- writel(PRCMU_DSI_LP_CLOCK_SETTING, prcmu_base + PRCM_TVCLK_MGT);
629
- writel(PRCMU_DPI_CLOCK_SETTING, prcmu_base + PRCM_LCDCLK_MGT);
630
-
631
- /* Release the HW semaphore. */
632
- writel(0, PRCM_SEM);
633
-
634
- spin_unlock_irqrestore(&clk_mgt_lock, flags);
635
-
636
- return 0;
637
-}
638
-
639545 u32 db8500_prcmu_read(unsigned int reg)
640546 {
641547 return readl(prcmu_base + reg);
....@@ -665,6 +571,14 @@
665571 struct prcmu_fw_version *prcmu_get_fw_version(void)
666572 {
667573 return fw_info.valid ? &fw_info.version : NULL;
574
+}
575
+
576
+static bool prcmu_is_ulppll_disabled(void)
577
+{
578
+ struct prcmu_fw_version *ver;
579
+
580
+ ver = prcmu_get_fw_version();
581
+ return ver && ver->project == PRCMU_FW_PROJECT_U8420_SYSCLK;
668582 }
669583
670584 bool prcmu_has_arm_maxopp(void)
....@@ -1307,10 +1221,23 @@
13071221
13081222 static int request_timclk(bool enable)
13091223 {
1310
- u32 val = (PRCM_TCR_DOZE_MODE | PRCM_TCR_TENSEL_MASK);
1224
+ u32 val;
1225
+
1226
+ /*
1227
+ * On the U8420_CLKSEL firmware, the ULP (Ultra Low Power)
1228
+ * PLL is disabled so we cannot use doze mode, this will
1229
+ * stop the clock on this firmware.
1230
+ */
1231
+ if (prcmu_is_ulppll_disabled())
1232
+ val = 0;
1233
+ else
1234
+ val = (PRCM_TCR_DOZE_MODE | PRCM_TCR_TENSEL_MASK);
13111235
13121236 if (!enable)
1313
- val |= PRCM_TCR_STOP_TIMERS;
1237
+ val |= PRCM_TCR_STOP_TIMERS |
1238
+ PRCM_TCR_DOZE_MODE |
1239
+ PRCM_TCR_TENSEL_MASK;
1240
+
13141241 writel(val, PRCM_TCR);
13151242
13161243 return 0;
....@@ -1588,8 +1515,10 @@
15881515 switch (divsel) {
15891516 case PRCM_DSI_PLLOUT_SEL_PHI_4:
15901517 div *= 2;
1518
+ fallthrough;
15911519 case PRCM_DSI_PLLOUT_SEL_PHI_2:
15921520 div *= 2;
1521
+ fallthrough;
15931522 case PRCM_DSI_PLLOUT_SEL_PHI:
15941523 return pll_rate(PRCM_PLLDSI_FREQ, clock_rate(PRCMU_HDMICLK),
15951524 PLL_RAW) / div;
....@@ -1612,7 +1541,8 @@
16121541 if (clock < PRCMU_NUM_REG_CLOCKS)
16131542 return clock_rate(clock);
16141543 else if (clock == PRCMU_TIMCLK)
1615
- return ROOT_CLOCK_RATE / 16;
1544
+ return prcmu_is_ulppll_disabled() ?
1545
+ 32768 : ROOT_CLOCK_RATE / 16;
16161546 else if (clock == PRCMU_SYSCLK)
16171547 return ROOT_CLOCK_RATE;
16181548 else if (clock == PRCMU_PLLSOC0)
....@@ -1691,21 +1621,39 @@
16911621 return rounded_rate;
16921622 }
16931623
1694
-static const unsigned long armss_freqs[] = {
1695
- 200000000,
1696
- 400000000,
1697
- 800000000,
1624
+static const unsigned long db8500_armss_freqs[] = {
1625
+ 199680000,
1626
+ 399360000,
1627
+ 798720000,
16981628 998400000
1629
+};
1630
+
1631
+/* The DB8520 has slightly higher ARMSS max frequency */
1632
+static const unsigned long db8520_armss_freqs[] = {
1633
+ 199680000,
1634
+ 399360000,
1635
+ 798720000,
1636
+ 1152000000
16991637 };
17001638
17011639 static long round_armss_rate(unsigned long rate)
17021640 {
17031641 unsigned long freq = 0;
1642
+ const unsigned long *freqs;
1643
+ int nfreqs;
17041644 int i;
17051645
1646
+ if (fw_info.version.project == PRCMU_FW_PROJECT_U8520) {
1647
+ freqs = db8520_armss_freqs;
1648
+ nfreqs = ARRAY_SIZE(db8520_armss_freqs);
1649
+ } else {
1650
+ freqs = db8500_armss_freqs;
1651
+ nfreqs = ARRAY_SIZE(db8500_armss_freqs);
1652
+ }
1653
+
17061654 /* Find the corresponding arm opp from the cpufreq table. */
1707
- for (i = 0; i < ARRAY_SIZE(armss_freqs); i++) {
1708
- freq = armss_freqs[i];
1655
+ for (i = 0; i < nfreqs; i++) {
1656
+ freq = freqs[i];
17091657 if (rate <= freq)
17101658 break;
17111659 }
....@@ -1850,11 +1798,21 @@
18501798 {
18511799 unsigned long freq;
18521800 u8 opps[] = { ARM_EXTCLK, ARM_50_OPP, ARM_100_OPP, ARM_MAX_OPP };
1801
+ const unsigned long *freqs;
1802
+ int nfreqs;
18531803 int i;
18541804
1805
+ if (fw_info.version.project == PRCMU_FW_PROJECT_U8520) {
1806
+ freqs = db8520_armss_freqs;
1807
+ nfreqs = ARRAY_SIZE(db8520_armss_freqs);
1808
+ } else {
1809
+ freqs = db8500_armss_freqs;
1810
+ nfreqs = ARRAY_SIZE(db8500_armss_freqs);
1811
+ }
1812
+
18551813 /* Find the corresponding arm opp from the cpufreq table. */
1856
- for (i = 0; i < ARRAY_SIZE(armss_freqs); i++) {
1857
- freq = armss_freqs[i];
1814
+ for (i = 0; i < nfreqs; i++) {
1815
+ freq = freqs[i];
18581816 if (rate == freq)
18591817 break;
18601818 }
....@@ -2316,6 +2274,8 @@
23162274 *
23172275 * Saves the reset reason code and then sets the APE_SOFTRST register which
23182276 * fires interrupt to fw
2277
+ *
2278
+ * @reset_code: The reason for system reset
23192279 */
23202280 void db8500_prcmu_system_reset(u16 reset_code)
23212281 {
....@@ -2613,6 +2573,8 @@
26132573 return "U8520 MBL";
26142574 case PRCMU_FW_PROJECT_U8420:
26152575 return "U8420";
2576
+ case PRCMU_FW_PROJECT_U8420_SYSCLK:
2577
+ return "U8420-sysclk";
26162578 case PRCMU_FW_PROJECT_U9540:
26172579 return "U9540";
26182580 case PRCMU_FW_PROJECT_A9420:
....@@ -2660,27 +2622,18 @@
26602622 return 0;
26612623 }
26622624
2663
-static void dbx500_fw_version_init(struct platform_device *pdev,
2664
- u32 version_offset)
2625
+static void dbx500_fw_version_init(struct device_node *np)
26652626 {
2666
- struct resource *res;
26672627 void __iomem *tcpm_base;
26682628 u32 version;
26692629
2670
- res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
2671
- "prcmu-tcpm");
2672
- if (!res) {
2673
- dev_err(&pdev->dev,
2674
- "Error: no prcmu tcpm memory region provided\n");
2675
- return;
2676
- }
2677
- tcpm_base = ioremap(res->start, resource_size(res));
2630
+ tcpm_base = of_iomap(np, 1);
26782631 if (!tcpm_base) {
2679
- dev_err(&pdev->dev, "no prcmu tcpm mem region provided\n");
2632
+ pr_err("no prcmu tcpm mem region provided\n");
26802633 return;
26812634 }
26822635
2683
- version = readl(tcpm_base + version_offset);
2636
+ version = readl(tcpm_base + DB8500_PRCMU_FW_VERSION_OFFSET);
26842637 fw_info.version.project = (version & 0xFF);
26852638 fw_info.version.api_version = (version >> 8) & 0xFF;
26862639 fw_info.version.func_version = (version >> 16) & 0xFF;
....@@ -2698,7 +2651,7 @@
26982651 iounmap(tcpm_base);
26992652 }
27002653
2701
-void __init db8500_prcmu_early_init(u32 phy_base, u32 size)
2654
+void __init db8500_prcmu_early_init(void)
27022655 {
27032656 /*
27042657 * This is a temporary remap to bring up the clocks. It is
....@@ -2707,9 +2660,17 @@
27072660 * clock driver can probe independently. An early initcall will
27082661 * still be needed, but it can be diverted into drivers/clk/ux500.
27092662 */
2710
- prcmu_base = ioremap(phy_base, size);
2711
- if (!prcmu_base)
2663
+ struct device_node *np;
2664
+
2665
+ np = of_find_compatible_node(NULL, NULL, "stericsson,db8500-prcmu");
2666
+ prcmu_base = of_iomap(np, 0);
2667
+ if (!prcmu_base) {
2668
+ of_node_put(np);
27122669 pr_err("%s: ioremap() of prcmu registers failed!\n", __func__);
2670
+ return;
2671
+ }
2672
+ dbx500_fw_version_init(np);
2673
+ of_node_put(np);
27132674
27142675 spin_lock_init(&mb0_transfer.lock);
27152676 spin_lock_init(&mb0_transfer.dbb_irqs_lock);
....@@ -2980,53 +2941,6 @@
29802941 .timeout = 600, /* 10 minutes */
29812942 .has_28_bits_resolution = true,
29822943 };
2983
-/*
2984
- * Thermal Sensor
2985
- */
2986
-
2987
-static struct resource db8500_thsens_resources[] = {
2988
- {
2989
- .name = "IRQ_HOTMON_LOW",
2990
- .start = IRQ_PRCMU_HOTMON_LOW,
2991
- .end = IRQ_PRCMU_HOTMON_LOW,
2992
- .flags = IORESOURCE_IRQ,
2993
- },
2994
- {
2995
- .name = "IRQ_HOTMON_HIGH",
2996
- .start = IRQ_PRCMU_HOTMON_HIGH,
2997
- .end = IRQ_PRCMU_HOTMON_HIGH,
2998
- .flags = IORESOURCE_IRQ,
2999
- },
3000
-};
3001
-
3002
-static struct db8500_thsens_platform_data db8500_thsens_data = {
3003
- .trip_points[0] = {
3004
- .temp = 70000,
3005
- .type = THERMAL_TRIP_ACTIVE,
3006
- .cdev_name = {
3007
- [0] = "thermal-cpufreq-0",
3008
- },
3009
- },
3010
- .trip_points[1] = {
3011
- .temp = 75000,
3012
- .type = THERMAL_TRIP_ACTIVE,
3013
- .cdev_name = {
3014
- [0] = "thermal-cpufreq-0",
3015
- },
3016
- },
3017
- .trip_points[2] = {
3018
- .temp = 80000,
3019
- .type = THERMAL_TRIP_ACTIVE,
3020
- .cdev_name = {
3021
- [0] = "thermal-cpufreq-0",
3022
- },
3023
- },
3024
- .trip_points[3] = {
3025
- .temp = 85000,
3026
- .type = THERMAL_TRIP_CRITICAL,
3027
- },
3028
- .num_trips = 4,
3029
-};
30302944
30312945 static const struct mfd_cell common_prcmu_devs[] = {
30322946 {
....@@ -3038,58 +2952,58 @@
30382952 };
30392953
30402954 static const struct mfd_cell db8500_prcmu_devs[] = {
3041
- {
3042
- .name = "db8500-prcmu-regulators",
3043
- .of_compatible = "stericsson,db8500-prcmu-regulator",
3044
- .platform_data = &db8500_regulators,
3045
- .pdata_size = sizeof(db8500_regulators),
3046
- },
3047
- {
3048
- .name = "cpuidle-dbx500",
3049
- .of_compatible = "stericsson,cpuidle-dbx500",
3050
- },
3051
- {
3052
- .name = "db8500-thermal",
3053
- .num_resources = ARRAY_SIZE(db8500_thsens_resources),
3054
- .resources = db8500_thsens_resources,
3055
- .platform_data = &db8500_thsens_data,
3056
- .pdata_size = sizeof(db8500_thsens_data),
3057
- },
2955
+ OF_MFD_CELL("db8500-prcmu-regulators", NULL,
2956
+ &db8500_regulators, sizeof(db8500_regulators), 0,
2957
+ "stericsson,db8500-prcmu-regulator"),
2958
+ OF_MFD_CELL("cpuidle-dbx500",
2959
+ NULL, NULL, 0, 0, "stericsson,cpuidle-dbx500"),
2960
+ OF_MFD_CELL("db8500-thermal",
2961
+ NULL, NULL, 0, 0, "stericsson,db8500-thermal"),
30582962 };
30592963
30602964 static int db8500_prcmu_register_ab8500(struct device *parent)
30612965 {
30622966 struct device_node *np;
3063
- struct resource ab8500_resource;
2967
+ struct resource ab850x_resource;
30642968 const struct mfd_cell ab8500_cell = {
30652969 .name = "ab8500-core",
30662970 .of_compatible = "stericsson,ab8500",
30672971 .id = AB8500_VERSION_AB8500,
3068
- .resources = &ab8500_resource,
2972
+ .resources = &ab850x_resource,
30692973 .num_resources = 1,
30702974 };
2975
+ const struct mfd_cell ab8505_cell = {
2976
+ .name = "ab8505-core",
2977
+ .of_compatible = "stericsson,ab8505",
2978
+ .id = AB8500_VERSION_AB8505,
2979
+ .resources = &ab850x_resource,
2980
+ .num_resources = 1,
2981
+ };
2982
+ const struct mfd_cell *ab850x_cell;
30712983
30722984 if (!parent->of_node)
30732985 return -ENODEV;
30742986
30752987 /* Look up the device node, sneak the IRQ out of it */
30762988 for_each_child_of_node(parent->of_node, np) {
3077
- if (of_device_is_compatible(np, ab8500_cell.of_compatible))
2989
+ if (of_device_is_compatible(np, ab8500_cell.of_compatible)) {
2990
+ ab850x_cell = &ab8500_cell;
30782991 break;
2992
+ }
2993
+ if (of_device_is_compatible(np, ab8505_cell.of_compatible)) {
2994
+ ab850x_cell = &ab8505_cell;
2995
+ break;
2996
+ }
30792997 }
30802998 if (!np) {
3081
- dev_info(parent, "could not find AB8500 node in the device tree\n");
2999
+ dev_info(parent, "could not find AB850X node in the device tree\n");
30823000 return -ENODEV;
30833001 }
3084
- of_irq_to_resource_table(np, &ab8500_resource, 1);
3002
+ of_irq_to_resource_table(np, &ab850x_resource, 1);
30853003
3086
- return mfd_add_devices(parent, 0, &ab8500_cell, 1, NULL, 0, NULL);
3004
+ return mfd_add_devices(parent, 0, ab850x_cell, 1, NULL, 0, NULL);
30873005 }
30883006
3089
-/**
3090
- * prcmu_fw_init - arch init call for the Linux PRCMU fw init logic
3091
- *
3092
- */
30933007 static int db8500_prcmu_probe(struct platform_device *pdev)
30943008 {
30953009 struct device_node *np = pdev->dev.of_node;
....@@ -3108,7 +3022,6 @@
31083022 return -ENOMEM;
31093023 }
31103024 init_prcm_registers();
3111
- dbx500_fw_version_init(pdev, DB8500_PRCMU_FW_VERSION_OFFSET);
31123025 res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "prcmu-tcdm");
31133026 if (!res) {
31143027 dev_err(&pdev->dev, "no prcmu tcdm region provided\n");
....@@ -3126,10 +3039,8 @@
31263039 writel(ALL_MBOX_BITS, PRCM_ARM_IT1_CLR);
31273040
31283041 irq = platform_get_irq(pdev, 0);
3129
- if (irq <= 0) {
3130
- dev_err(&pdev->dev, "no prcmu irq provided\n");
3042
+ if (irq <= 0)
31313043 return irq;
3132
- }
31333044
31343045 err = request_threaded_irq(irq, prcmu_irq_handler,
31353046 prcmu_irq_thread_fn, IRQF_NO_SUSPEND, "prcmu", NULL);
....@@ -3188,9 +3099,4 @@
31883099 {
31893100 return platform_driver_register(&db8500_prcmu_driver);
31903101 }
3191
-
31923102 core_initcall(db8500_prcmu_init);
3193
-
3194
-MODULE_AUTHOR("Mattias Nilsson <mattias.i.nilsson@stericsson.com>");
3195
-MODULE_DESCRIPTION("DB8500 PRCM Unit driver");
3196
-MODULE_LICENSE("GPL v2");