Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
70 changes: 70 additions & 0 deletions Documentation/devicetree/bindings/power/sifive,tmc.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/power/sifive,tmc.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#

title: SiFive Domain Management Controller

maintainers:
- Cyan Yang <cyan.yang@sifive.com>
- Nick Hu <nick.hu@sifive.com>
- Samuel Holland <samuel.holland@sifive.com>

description: |
SiFive Domain Management Controllers includes the following components
- Tile Management Controller (TMC)
- Cluster Management Controller (CMC)
- Subsystem Management Controller (SMC)
These controllers manage both the clock and power domains of the
associated components. They support the SiFive Quiet Interface Protocol
(SQIP) starting from Version 1. The control method differs from Version
0, making them incompatible.

allOf:
- $ref: power-domain.yaml#

properties:
compatible:
oneOf:
- items:
- {} # Leave a empty for future SoC specific compatible string
- const: sifive,cmc3
- const: sifive,cmc2
- items:
- {} # Leave a empty for future SoC specific compatible string
- const: sifive,cmc2
- items:
- {} # Leave a empty for future SoC specific compatible string
- const: sifive,smc0
- items:
- {} # Leave a empty for future SoC specific compatible string
- const: sifive,smc1
- items:
- {} # Leave a empty for future SoC specific compatible string
- const: sifive,smc3
- const: sifive,smc1
- items:
- {} # Leave a empty for future SoC specific compatible string
- const: sifive,tmc0
- items:
- {} # Leave a empty for future SoC specific compatible string
- const: sifive,tmc1
- items:
- {} # Leave a empty for future SoC specific compatible string
- const: sifive,tmc3
- const: sifive,tmc1

reg:
maxItems: 1

"#power-domain-cells":
const: 0

required:
- compatible
- reg

additionalProperties: false

# The example will be added once the SoC specific compatible string is available
11 changes: 11 additions & 0 deletions drivers/cpuidle/Kconfig.riscv
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,14 @@ config RISCV_SBI_CPUIDLE
Select this option to enable RISC-V SBI firmware based CPU idle
driver for RISC-V systems. This drivers also supports hierarchical
DT based layout of the idle state.

config SIFIVE_DMC_PD_CPUIDLE
bool "SiFive DMC SBI PD Provider Driver"
depends on ARCH_SIFIVE
select PM_GENERIC_DOMAINS_OF
select RISCV_SBI_CPUIDLE
default y
help
Select this option to enable SiFive DMC SBI PD Provider driver.
This driver will create the genpd provider and work with the
RISC-V SBI firmware based CPU idle driver.
1 change: 1 addition & 0 deletions drivers/cpuidle/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -42,3 +42,4 @@ obj-$(CONFIG_POWERNV_CPUIDLE) += cpuidle-powernv.o
###############################################################################
# RISC-V drivers
obj-$(CONFIG_RISCV_SBI_CPUIDLE) += cpuidle-riscv-sbi.o
obj-$(CONFIG_SIFIVE_DMC_PD_CPUIDLE) += cpuidle-sifive-dmc-pd.o
17 changes: 11 additions & 6 deletions drivers/cpuidle/cpuidle-riscv-sbi.c
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
#include <asm/suspend.h>

#include "cpuidle.h"
#include "cpuidle-riscv-sbi.h"
#include "dt_idle_states.h"
#include "dt_idle_genpd.h"

Expand Down Expand Up @@ -188,7 +189,7 @@ static const struct of_device_id sbi_cpuidle_state_match[] = {
{ },
};

static int sbi_dt_parse_state_node(struct device_node *np, u32 *state)
int sbi_dt_parse_state_node(struct device_node *np, u32 *state)
{
int err = of_property_read_u32(np, "riscv,sbi-suspend-param", state);

Expand Down Expand Up @@ -325,8 +326,9 @@ static int sbi_cpuidle_init_cpu(struct device *dev, int cpu)
/* Initialize idle states from DT. */
ret = sbi_cpuidle_dt_init_states(dev, drv, cpu, state_count);
if (ret) {
pr_err("HART%ld: failed to init idle states\n",
cpuid_to_hartid_map(cpu));
if (ret != -EPROBE_DEFER)
pr_err("HART%ld: failed to init idle states\n",
cpuid_to_hartid_map(cpu));
return ret;
}

Expand Down Expand Up @@ -356,7 +358,7 @@ static void sbi_cpuidle_domain_sync_state(struct device *dev)

#ifdef CONFIG_DT_IDLE_GENPD

static int sbi_cpuidle_pd_power_off(struct generic_pm_domain *pd)
int sbi_cpuidle_pd_power_off(struct generic_pm_domain *pd)
{
struct genpd_power_state *state = &pd->states[pd->state_idx];
u32 *pd_state;
Expand Down Expand Up @@ -533,8 +535,11 @@ static int sbi_cpuidle_probe(struct platform_device *pdev)
for_each_present_cpu(cpu) {
ret = sbi_cpuidle_init_cpu(&pdev->dev, cpu);
if (ret) {
pr_debug("HART%ld: idle driver init failed\n",
cpuid_to_hartid_map(cpu));
if (ret == -EPROBE_DEFER)
pr_debug("idle driver probe deferred\n");
else
pr_debug("HART%ld: idle driver init failed\n",
cpuid_to_hartid_map(cpu));
goto out_fail;
}
}
Expand Down
20 changes: 20 additions & 0 deletions drivers/cpuidle/cpuidle-riscv-sbi.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
/* SPDX-License-Identifier: GPL-2.0 */
#ifndef __CPUIDLE_RISCV_SBI
#define __CPUIDLE_RISCV_SBI

#ifdef CONFIG_DT_IDLE_GENPD

int sbi_cpuidle_pd_power_off(struct generic_pm_domain *pd);

#else

static inline int sbi_cpuidle_pd_power_off(struct generic_pm_domain *pd)
{
return 0;
}

#endif

int sbi_dt_parse_state_node(struct device_node *np, u32 *state);

#endif
102 changes: 102 additions & 0 deletions drivers/cpuidle/cpuidle-sifive-dmc-pd.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* SiFive CPUIDLE SBI PD driver
*/

#define pr_fmt(fmt) "sifive_cpuidle_sbi_pd: " fmt

#include <linux/of_device.h>
#include <linux/platform_device.h>
#include <linux/pm_domain.h>
#include <linux/pm_runtime.h>

#include "cpuidle-riscv-sbi.h"
#include "dt_idle_genpd.h"

static void sifive_dmc_remove(struct platform_device *pdev)
{
struct generic_pm_domain *pd = platform_get_drvdata(pdev);
struct device *dev = &pdev->dev;

pm_runtime_disable(dev);
of_genpd_del_provider(dev->of_node);
pm_genpd_remove(pd);
dt_idle_pd_free(pd);
}

static int sifive_dmc_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct device_node *np = dev->of_node;
struct generic_pm_domain *pd;
struct of_phandle_args child, parent;
int ret = -ENOMEM;

pd = dt_idle_pd_alloc(np, sbi_dt_parse_state_node);
if (!pd)
goto fail;

pd->flags |= GENPD_FLAG_IRQ_SAFE | GENPD_FLAG_CPU_DOMAIN;
pd->power_off = sbi_cpuidle_pd_power_off;

ret = pm_genpd_init(pd, &pm_domain_cpu_gov, false);
if (ret)
goto free_pd;

ret = of_genpd_add_provider_simple(np, pd);
if (ret)
goto remove_pd;

if (of_parse_phandle_with_args(np, "power-domains",
"#power-domain-cells", 0,
&parent) == 0) {
child.np = np;
child.args_count = 0;

if (of_genpd_add_subdomain(&parent, &child))
pr_warn("%pOF failed to add subdomain: %pOF\n",
parent.np, child.np);
else
pr_debug("%pOF has a child subdomain: %pOF.\n",
parent.np, child.np);
}

platform_set_drvdata(pdev, pd);
pm_runtime_enable(dev);
pr_info("%s create success\n", pd->name);
return 0;

remove_pd:
pm_genpd_remove(pd);
free_pd:
dt_idle_pd_free(pd);
fail:
pr_info("%s create fail\n", pd->name);

return ret;
}

static const struct of_device_id sifive_dmc_of_match[] = {
{ .compatible = "sifive,tmc1", },
{ .compatible = "sifive,tmc0", },
{ .compatible = "sifive,smc1", },
{ .compatible = "sifive,smc0", },
{ .compatible = "sifive,cmc2", },
{}
};

static struct platform_driver sifive_dmc_driver = {
.probe = sifive_dmc_probe,
.remove = sifive_dmc_remove,
.driver = {
.name = "sifive_dmc",
.of_match_table = sifive_dmc_of_match,
.suppress_bind_attrs = true,
},
};

static int __init sifive_dmc_init(void)
{
return platform_driver_register(&sifive_dmc_driver);
}
arch_initcall(sifive_dmc_init);
Loading