1 // SPDX-License-Identifier: GPL-2.0-or-later
3 * StarFive JH71XX PMU (Power Management Unit) Controller Driver
5 * Copyright (C) 2022-2023 StarFive Technology Co., Ltd.
8 #include <linux/interrupt.h>
10 #include <linux/iopoll.h>
11 #include <linux/module.h>
13 #include <linux/platform_device.h>
14 #include <linux/pm_domain.h>
15 #include <dt-bindings/power/starfive,jh7110-pmu.h>
18 #define JH71XX_PMU_SW_TURN_ON_POWER 0x0C
19 #define JH71XX_PMU_SW_TURN_OFF_POWER 0x10
20 #define JH71XX_PMU_SW_ENCOURAGE 0x44
21 #define JH71XX_PMU_TIMER_INT_MASK 0x48
22 #define JH71XX_PMU_CURR_POWER_MODE 0x80
23 #define JH71XX_PMU_EVENT_STATUS 0x88
24 #define JH71XX_PMU_INT_STATUS 0x8C
26 /* aon pmu register offset */
27 #define JH71XX_AON_PMU_SWITCH 0x00
29 /* sw encourage cfg */
30 #define JH71XX_PMU_SW_ENCOURAGE_EN_LO 0x05
31 #define JH71XX_PMU_SW_ENCOURAGE_EN_HI 0x50
32 #define JH71XX_PMU_SW_ENCOURAGE_DIS_LO 0x0A
33 #define JH71XX_PMU_SW_ENCOURAGE_DIS_HI 0xA0
34 #define JH71XX_PMU_SW_ENCOURAGE_ON 0xFF
37 #define JH71XX_PMU_INT_SEQ_DONE BIT(0)
38 #define JH71XX_PMU_INT_HW_REQ BIT(1)
39 #define JH71XX_PMU_INT_SW_FAIL GENMASK(3, 2)
40 #define JH71XX_PMU_INT_HW_FAIL GENMASK(5, 4)
41 #define JH71XX_PMU_INT_PCH_FAIL GENMASK(8, 6)
42 #define JH71XX_PMU_INT_ALL_MASK GENMASK(8, 0)
45 * The time required for switching power status is based on the time
46 * to turn on the largest domain's power, which is at microsecond level
48 #define JH71XX_PMU_TIMEOUT_US 100
50 struct jh71xx_domain_info
{
51 const char * const name
;
57 struct jh71xx_pmu_dev
;
59 struct jh71xx_pmu_match_data
{
60 const struct jh71xx_domain_info
*domain_info
;
62 unsigned int pmu_status
;
63 int (*pmu_parse_irq
)(struct platform_device
*pdev
,
64 struct jh71xx_pmu
*pmu
);
65 int (*pmu_set_state
)(struct jh71xx_pmu_dev
*pmd
,
71 const struct jh71xx_pmu_match_data
*match_data
;
73 struct generic_pm_domain
**genpd
;
74 struct genpd_onecell_data genpd_data
;
76 spinlock_t lock
; /* protects pmu reg */
79 struct jh71xx_pmu_dev
{
80 const struct jh71xx_domain_info
*domain_info
;
81 struct jh71xx_pmu
*pmu
;
82 struct generic_pm_domain genpd
;
85 static int jh71xx_pmu_get_state(struct jh71xx_pmu_dev
*pmd
, u32 mask
, bool *is_on
)
87 struct jh71xx_pmu
*pmu
= pmd
->pmu
;
92 *is_on
= readl(pmu
->base
+ pmu
->match_data
->pmu_status
) & mask
;
97 static int jh7110_pmu_set_state(struct jh71xx_pmu_dev
*pmd
, u32 mask
, bool on
)
99 struct jh71xx_pmu
*pmu
= pmd
->pmu
;
107 spin_lock_irqsave(&pmu
->lock
, flags
);
110 * The PMU accepts software encourage to switch power mode in the following 2 steps:
112 * 1.Configure the register SW_TURN_ON_POWER (offset 0x0c) by writing 1 to
113 * the bit corresponding to the power domain that will be turned on
114 * and writing 0 to the others.
115 * Likewise, configure the register SW_TURN_OFF_POWER (offset 0x10) by
116 * writing 1 to the bit corresponding to the power domain that will be
117 * turned off and writing 0 to the others.
120 mode
= JH71XX_PMU_SW_TURN_ON_POWER
;
121 encourage_lo
= JH71XX_PMU_SW_ENCOURAGE_EN_LO
;
122 encourage_hi
= JH71XX_PMU_SW_ENCOURAGE_EN_HI
;
124 mode
= JH71XX_PMU_SW_TURN_OFF_POWER
;
125 encourage_lo
= JH71XX_PMU_SW_ENCOURAGE_DIS_LO
;
126 encourage_hi
= JH71XX_PMU_SW_ENCOURAGE_DIS_HI
;
129 writel(mask
, pmu
->base
+ mode
);
132 * 2.Write SW encourage command sequence to the Software Encourage Reg (offset 0x44)
133 * First write SW_MODE_ENCOURAGE_ON to JH71XX_PMU_SW_ENCOURAGE. This will reset
134 * the state machine which parses the command sequence. This register must be
135 * written every time software wants to power on/off a domain.
136 * Then write the lower bits of the command sequence, followed by the upper
137 * bits. The sequence differs between powering on & off a domain.
139 writel(JH71XX_PMU_SW_ENCOURAGE_ON
, pmu
->base
+ JH71XX_PMU_SW_ENCOURAGE
);
140 writel(encourage_lo
, pmu
->base
+ JH71XX_PMU_SW_ENCOURAGE
);
141 writel(encourage_hi
, pmu
->base
+ JH71XX_PMU_SW_ENCOURAGE
);
143 spin_unlock_irqrestore(&pmu
->lock
, flags
);
145 /* Wait for the power domain bit to be enabled / disabled */
147 ret
= readl_poll_timeout_atomic(pmu
->base
+ JH71XX_PMU_CURR_POWER_MODE
,
149 1, JH71XX_PMU_TIMEOUT_US
);
151 ret
= readl_poll_timeout_atomic(pmu
->base
+ JH71XX_PMU_CURR_POWER_MODE
,
153 1, JH71XX_PMU_TIMEOUT_US
);
157 dev_err(pmu
->dev
, "%s: failed to power %s\n",
158 pmd
->genpd
.name
, on
? "on" : "off");
165 static int jh7110_aon_pmu_set_state(struct jh71xx_pmu_dev
*pmd
, u32 mask
, bool on
)
167 struct jh71xx_pmu
*pmu
= pmd
->pmu
;
171 spin_lock_irqsave(&pmu
->lock
, flags
);
172 val
= readl(pmu
->base
+ JH71XX_AON_PMU_SWITCH
);
179 writel(val
, pmu
->base
+ JH71XX_AON_PMU_SWITCH
);
180 spin_unlock_irqrestore(&pmu
->lock
, flags
);
185 static int jh71xx_pmu_set_state(struct jh71xx_pmu_dev
*pmd
, u32 mask
, bool on
)
187 struct jh71xx_pmu
*pmu
= pmd
->pmu
;
188 const struct jh71xx_pmu_match_data
*match_data
= pmu
->match_data
;
192 ret
= jh71xx_pmu_get_state(pmd
, mask
, &is_on
);
194 dev_dbg(pmu
->dev
, "unable to get current state for %s\n",
200 dev_dbg(pmu
->dev
, "pm domain [%s] is already %sable status.\n",
201 pmd
->genpd
.name
, on
? "en" : "dis");
205 return match_data
->pmu_set_state(pmd
, mask
, on
);
208 static int jh71xx_pmu_on(struct generic_pm_domain
*genpd
)
210 struct jh71xx_pmu_dev
*pmd
= container_of(genpd
,
211 struct jh71xx_pmu_dev
, genpd
);
212 u32 pwr_mask
= BIT(pmd
->domain_info
->bit
);
214 return jh71xx_pmu_set_state(pmd
, pwr_mask
, true);
217 static int jh71xx_pmu_off(struct generic_pm_domain
*genpd
)
219 struct jh71xx_pmu_dev
*pmd
= container_of(genpd
,
220 struct jh71xx_pmu_dev
, genpd
);
221 u32 pwr_mask
= BIT(pmd
->domain_info
->bit
);
223 return jh71xx_pmu_set_state(pmd
, pwr_mask
, false);
226 static void jh71xx_pmu_int_enable(struct jh71xx_pmu
*pmu
, u32 mask
, bool enable
)
231 spin_lock_irqsave(&pmu
->lock
, flags
);
232 val
= readl(pmu
->base
+ JH71XX_PMU_TIMER_INT_MASK
);
239 writel(val
, pmu
->base
+ JH71XX_PMU_TIMER_INT_MASK
);
240 spin_unlock_irqrestore(&pmu
->lock
, flags
);
243 static irqreturn_t
jh71xx_pmu_interrupt(int irq
, void *data
)
245 struct jh71xx_pmu
*pmu
= data
;
248 val
= readl(pmu
->base
+ JH71XX_PMU_INT_STATUS
);
250 if (val
& JH71XX_PMU_INT_SEQ_DONE
)
251 dev_dbg(pmu
->dev
, "sequence done.\n");
252 if (val
& JH71XX_PMU_INT_HW_REQ
)
253 dev_dbg(pmu
->dev
, "hardware encourage requestion.\n");
254 if (val
& JH71XX_PMU_INT_SW_FAIL
)
255 dev_err(pmu
->dev
, "software encourage fail.\n");
256 if (val
& JH71XX_PMU_INT_HW_FAIL
)
257 dev_err(pmu
->dev
, "hardware encourage fail.\n");
258 if (val
& JH71XX_PMU_INT_PCH_FAIL
)
259 dev_err(pmu
->dev
, "p-channel fail event.\n");
261 /* clear interrupts */
262 writel(val
, pmu
->base
+ JH71XX_PMU_INT_STATUS
);
263 writel(val
, pmu
->base
+ JH71XX_PMU_EVENT_STATUS
);
268 static int jh7110_pmu_parse_irq(struct platform_device
*pdev
, struct jh71xx_pmu
*pmu
)
270 struct device
*dev
= &pdev
->dev
;
273 pmu
->irq
= platform_get_irq(pdev
, 0);
277 ret
= devm_request_irq(dev
, pmu
->irq
, jh71xx_pmu_interrupt
,
280 dev_err(dev
, "failed to request irq\n");
282 jh71xx_pmu_int_enable(pmu
, JH71XX_PMU_INT_ALL_MASK
& ~JH71XX_PMU_INT_PCH_FAIL
, true);
287 static int jh71xx_pmu_init_domain(struct jh71xx_pmu
*pmu
, int index
)
289 struct jh71xx_pmu_dev
*pmd
;
294 pmd
= devm_kzalloc(pmu
->dev
, sizeof(*pmd
), GFP_KERNEL
);
298 pmd
->domain_info
= &pmu
->match_data
->domain_info
[index
];
300 pwr_mask
= BIT(pmd
->domain_info
->bit
);
302 pmd
->genpd
.name
= pmd
->domain_info
->name
;
303 pmd
->genpd
.flags
= pmd
->domain_info
->flags
;
305 ret
= jh71xx_pmu_get_state(pmd
, pwr_mask
, &is_on
);
307 dev_warn(pmu
->dev
, "unable to get current state for %s\n",
310 pmd
->genpd
.power_on
= jh71xx_pmu_on
;
311 pmd
->genpd
.power_off
= jh71xx_pmu_off
;
312 pm_genpd_init(&pmd
->genpd
, NULL
, !is_on
);
314 pmu
->genpd_data
.domains
[index
] = &pmd
->genpd
;
319 static int jh71xx_pmu_probe(struct platform_device
*pdev
)
321 struct device
*dev
= &pdev
->dev
;
322 struct device_node
*np
= dev
->of_node
;
323 const struct jh71xx_pmu_match_data
*match_data
;
324 struct jh71xx_pmu
*pmu
;
328 pmu
= devm_kzalloc(dev
, sizeof(*pmu
), GFP_KERNEL
);
332 pmu
->base
= devm_platform_ioremap_resource(pdev
, 0);
333 if (IS_ERR(pmu
->base
))
334 return PTR_ERR(pmu
->base
);
336 spin_lock_init(&pmu
->lock
);
338 match_data
= of_device_get_match_data(dev
);
342 if (match_data
->pmu_parse_irq
) {
343 ret
= match_data
->pmu_parse_irq(pdev
, pmu
);
345 dev_err(dev
, "failed to parse irq\n");
350 pmu
->genpd
= devm_kcalloc(dev
, match_data
->num_domains
,
351 sizeof(struct generic_pm_domain
*),
357 pmu
->match_data
= match_data
;
358 pmu
->genpd_data
.domains
= pmu
->genpd
;
359 pmu
->genpd_data
.num_domains
= match_data
->num_domains
;
361 for (i
= 0; i
< match_data
->num_domains
; i
++) {
362 ret
= jh71xx_pmu_init_domain(pmu
, i
);
364 dev_err(dev
, "failed to initialize power domain\n");
369 ret
= of_genpd_add_provider_onecell(np
, &pmu
->genpd_data
);
371 dev_err(dev
, "failed to register genpd driver: %d\n", ret
);
375 dev_dbg(dev
, "registered %u power domains\n", i
);
380 static const struct jh71xx_domain_info jh7110_power_domains
[] = {
381 [JH7110_PD_SYSTOP
] = {
384 .flags
= GENPD_FLAG_ALWAYS_ON
,
389 .flags
= GENPD_FLAG_ALWAYS_ON
,
413 static const struct jh71xx_pmu_match_data jh7110_pmu
= {
414 .num_domains
= ARRAY_SIZE(jh7110_power_domains
),
415 .domain_info
= jh7110_power_domains
,
416 .pmu_status
= JH71XX_PMU_CURR_POWER_MODE
,
417 .pmu_parse_irq
= jh7110_pmu_parse_irq
,
418 .pmu_set_state
= jh7110_pmu_set_state
,
421 static const struct jh71xx_domain_info jh7110_aon_power_domains
[] = {
422 [JH7110_AON_PD_DPHY_TX
] = {
426 [JH7110_AON_PD_DPHY_RX
] = {
432 static const struct jh71xx_pmu_match_data jh7110_aon_pmu
= {
433 .num_domains
= ARRAY_SIZE(jh7110_aon_power_domains
),
434 .domain_info
= jh7110_aon_power_domains
,
435 .pmu_status
= JH71XX_AON_PMU_SWITCH
,
436 .pmu_set_state
= jh7110_aon_pmu_set_state
,
439 static const struct of_device_id jh71xx_pmu_of_match
[] = {
441 .compatible
= "starfive,jh7110-pmu",
442 .data
= (void *)&jh7110_pmu
,
444 .compatible
= "starfive,jh7110-aon-syscon",
445 .data
= (void *)&jh7110_aon_pmu
,
451 static struct platform_driver jh71xx_pmu_driver
= {
452 .probe
= jh71xx_pmu_probe
,
454 .name
= "jh71xx-pmu",
455 .of_match_table
= jh71xx_pmu_of_match
,
456 .suppress_bind_attrs
= true,
459 builtin_platform_driver(jh71xx_pmu_driver
);
461 MODULE_AUTHOR("Walker Chen <walker.chen@starfivetech.com>");
462 MODULE_AUTHOR("Changhuang Liang <changhuang.liang@starfivetech.com>");
463 MODULE_DESCRIPTION("StarFive JH71XX PMU Driver");
464 MODULE_LICENSE("GPL");