Skip to content
Merged
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
42 changes: 38 additions & 4 deletions Documentation/devicetree/bindings/power/apple,pmc.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -30,23 +30,29 @@ properties:
- apple,t8140-pmc

reg:
maxItems: 2
minItems: 2
maxItems: 3
description: |
The perf-state floor words of the voter agents ("voters"), and the
per-agent words holding the floor the PMC honours for each agent with
the per-rail interface enable words ("rails").
the per-rail interface enable words ("rails"). Optionally, the cluster
enable control word followed by its status word ("clusters").

reg-names:
description: |
The rails block may come first, so that the node's unit address does
not collide with a node covering the voters block.
oneOf:
- items:
- minItems: 2
items:
- const: voters
- const: rails
- items:
- const: clusters
- minItems: 2
items:
- const: rails
- const: voters
- const: clusters

nonposted-mmio: true

Expand Down Expand Up @@ -143,6 +149,34 @@ patternProperties:
- apple,rail
- apple,level

'^cluster-[a-z0-9-]+$':
type: object
description: |
A cluster enable bit in the "clusters" words, provided as a power
domain. The bit is set, and its status awaited, while the domain is
powered. The T8140 video encoder cluster must be enabled for the PMP to
raise the encoder's performance state.
additionalProperties: false

properties:
label:
description: Name of the power domain.

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

power-domains:
maxItems: 1

apple,cluster-bit:
$ref: /schemas/types.yaml#/definitions/uint32
maximum: 31
description: Bit of the cluster in the control and status words.

required:
- '#power-domain-cells'
- apple,cluster-bit

'^performance-controller@[0-9a-f]+$':
type: object
description: The host performance controller's voter agent.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,20 @@ patternProperties:
const: 0

power-domains:
maxItems: 1
minItems: 1
maxItems: 2

apple,dvfs:
$ref: /schemas/types.yaml#/definitions/uint32-array
items:
- description: SOC-DEV-DVFS entry holding the device's request.
- description: Performance level requested while powered (0-31).
maximum: 31
description:
The device also has a performance request. While the domain is
powered, after the PMP acknowledges the device, the host writes the
level (with the valid bit) to the entry once; before clearing the
device request it writes level 0.

apple,always-on:
type: boolean
Expand Down
32 changes: 28 additions & 4 deletions arch/arm64/boot/dts/apple/t8140.dtsi
Original file line number Diff line number Diff line change
Expand Up @@ -531,6 +531,20 @@
power-domains = <&ps_gfx>;
apple,always-on;
};

/*
* PMP soc-device 15 (AVE0): the encoder's power request
* and, in SOC-DEV-DVFS entry 0x198, its VMAX performance
* request. The PMP then votes the AVE rail up itself.
*/
pmp_report_ave0: report@f {
compatible = "apple,t8140-pmp-v2-report-entry";
reg = <0xf>;
label = "pmp-ave0";
#power-domain-cells = <0>;
power-domains = <&ps_venc0_sys>, <&pmc_venc_cluster>;
apple,dvfs = <0x198 2>;
};
};

pmp: pmp@300500000 {
Expand Down Expand Up @@ -1043,8 +1057,10 @@
<AIC_IRQ 954 IRQ_TYPE_LEVEL_HIGH>,
<AIC_IRQ 953 IRQ_TYPE_LEVEL_HIGH>;
power-domains = <&ps_venc0_dma>, <&ps_venc0_pipe4>,
<&ps_venc0_pipe5>, <&ps_venc0_me0>, <&ps_venc0_me1>;
power-domain-names = "dma", "pipe4", "pipe5", "me0", "me1";
<&ps_venc0_pipe5>, <&ps_venc0_me0>, <&ps_venc0_me1>,
<&pmp_report_ave0>;
power-domain-names = "dma", "pipe4", "pipe5", "me0", "me1",
"perf";
iommus = <&ave_dart0 0>, <&ave_dart1 0>;
status = "disabled";
};
Expand Down Expand Up @@ -1264,8 +1280,9 @@
pmc: power-management@302800000 {
compatible = "apple,t8140-pmc";
reg = <0x3 0x02800000 0x0 0x10000>,
<0x3 0x028aa000 0x0 0x3000>;
reg-names = "voters", "rails";
<0x3 0x028aa000 0x0 0x3000>,
<0x3 0x028a9c18 0x0 0x8>;
reg-names = "voters", "rails", "clusters";
power-domains = <&ps_pmc>;
#address-cells = <1>;
#size-cells = <0>;
Expand All @@ -1274,6 +1291,13 @@
apple,static-voters = <0 1 3>;
status = "disabled";

/* VENC-CLSTR-V: the video encoder cluster, bit 3. */
pmc_venc_cluster: cluster-venc {
label = "venc-cluster";
#power-domain-cells = <0>;
apple,cluster-bit = <3>;
};

clpc: performance-controller@1 {
compatible = "apple,t8140-clpc";
reg = <1>;
Expand Down
114 changes: 114 additions & 0 deletions drivers/pmdomain/apple/pmc.c
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
#include <linux/slab.h>
#include <linux/soc/apple/pmc.h>
#include <linux/spinlock.h>
#include <linux/string_choices.h>
#include <linux/thermal.h>
#include <linux/workqueue.h>

Expand Down Expand Up @@ -93,6 +94,8 @@ struct apple_pmc {
const struct apple_pmc_hw *hw;
void __iomem *voters;
void __iomem *rails;
/* Optional cluster enable control and status words (T8140). */
void __iomem *clusters;
/*
* Serializes read-modify-write cycles on enable and floor words, and
* protects @enable and @dcs_state.
Expand Down Expand Up @@ -122,6 +125,8 @@ struct apple_pmc {
* @rail: with @floor, the rail
* @level: with @floor, the performance state held while powered
* @on: with @floor, whether the floor is held; protected by pmc->lock
* @cluster: the domain is a cluster enable bit in the "clusters" region
* @bit: with @cluster, the bit in the control and status words
*/
struct apple_pmc_domain {
struct generic_pm_domain genpd;
Expand All @@ -133,6 +138,8 @@ struct apple_pmc_domain {
unsigned int rail;
unsigned int level;
bool on;
bool cluster;
unsigned int bit;
bool provider;
};

Expand Down Expand Up @@ -634,10 +641,39 @@ static int apple_pmc_floor_set(struct apple_pmc_domain *dom, bool on)
return ret;
}

/* The cluster control word is at +0 and its status at +4. */
#define APPLE_PMC_CLUSTER_CTRL 0x0
#define APPLE_PMC_CLUSTER_STATUS 0x4
#define APPLE_PMC_CLUSTER_TIMEOUT_US 10000

static int apple_pmc_cluster_set(struct apple_pmc_domain *dom, bool on)
{
struct apple_pmc *pmc = dom->pmc;
u32 mask = BIT(dom->bit), val;
unsigned long flags;
int ret;

raw_spin_lock_irqsave(&pmc->lock, flags);
val = readl(pmc->clusters + APPLE_PMC_CLUSTER_CTRL);
writel(on ? val | mask : val & ~mask,
pmc->clusters + APPLE_PMC_CLUSTER_CTRL);
raw_spin_unlock_irqrestore(&pmc->lock, flags);

ret = readl_poll_timeout_atomic(pmc->clusters + APPLE_PMC_CLUSTER_STATUS,
val, !!(val & mask) == on, 1,
APPLE_PMC_CLUSTER_TIMEOUT_US);
if (ret)
dev_err(pmc->dev, "%s: cluster bit %u did not turn %s (status %#x)\n",
dom->genpd.name, dom->bit, str_on_off(on), val);
return ret;
}

static int apple_pmc_domain_power_on(struct generic_pm_domain *genpd)
{
struct apple_pmc_domain *dom = genpd_to_apple_pmc_domain(genpd);

if (dom->cluster)
return apple_pmc_cluster_set(dom, true);
if (dom->floor)
return apple_pmc_floor_set(dom, true);
return apple_pmc_agent_enable(dom->pmc, dom->agent, true);
Expand All @@ -647,6 +683,8 @@ static int apple_pmc_domain_power_off(struct generic_pm_domain *genpd)
{
struct apple_pmc_domain *dom = genpd_to_apple_pmc_domain(genpd);

if (dom->cluster)
return apple_pmc_cluster_set(dom, false);
if (dom->floor)
return apple_pmc_floor_set(dom, false);
return apple_pmc_agent_enable(dom->pmc, dom->agent, false);
Expand Down Expand Up @@ -740,6 +778,75 @@ static int apple_pmc_domain_add(struct apple_pmc *pmc, struct device_node *np)
return 0;
}

/*
* A cluster enable bit, set while the domain is powered: the T8140 video
* encoder cluster must be enabled before its performance request takes
* effect. Starts off; the bootloader leaves the bits clear.
*/
static int apple_pmc_cluster_add(struct apple_pmc *pmc, struct device_node *np)
{
struct device *dev = pmc->dev;
struct apple_pmc_domain *dom;
struct of_phandle_args parent;
const char *label;
u32 bit;
int i, ret;

if (!pmc->clusters)
return dev_err_probe(dev, -EINVAL, "%pOF: no clusters region\n", np);
if (of_property_read_u32(np, "apple,cluster-bit", &bit) || bit >= 32)
return dev_err_probe(dev, -EINVAL, "%pOF: invalid cluster bit\n", np);

dom = kzalloc_obj(*dom);
if (!dom)
return -ENOMEM;

dom->pmc = pmc;
dom->np = of_node_get(np);
dom->cluster = true;
dom->bit = bit;
if (of_property_read_string(np, "label", &label))
label = np->name;
dom->genpd.name = kasprintf(GFP_KERNEL, "pmc-%s", label);
if (!dom->genpd.name) {
of_node_put(dom->np);
kfree(dom);
return -ENOMEM;
}
dom->genpd.power_on = apple_pmc_domain_power_on;
dom->genpd.power_off = apple_pmc_domain_power_off;

ret = pm_genpd_init(&dom->genpd, NULL,
!(readl(pmc->clusters + APPLE_PMC_CLUSTER_STATUS) & BIT(bit)));
if (ret) {
of_node_put(dom->np);
kfree(dom->genpd.name);
kfree(dom);
return dev_err_probe(dev, ret, "%pOF: genpd init failed\n", np);
}
list_add_tail(&dom->list, &pmc->domains);

ret = of_genpd_add_provider_simple(np, &dom->genpd);
if (ret)
return dev_err_probe(dev, ret, "%pOF: provider failed\n", np);
dom->provider = true;

for (i = 0; !of_parse_phandle_with_args(np, "power-domains",
"#power-domain-cells", i,
&parent); i++) {
struct of_phandle_args child = { .np = np };

ret = of_genpd_add_subdomain(&parent, &child);
of_node_put(parent.np);
if (ret)
return dev_err_probe(dev, ret,
"%pOF: failed to join parent domain\n",
np);
}

return 0;
}

/* A rail floor held through the AP's agent while the domain is powered. */
static int apple_pmc_floor_add(struct apple_pmc *pmc, struct device_node *np)
{
Expand Down Expand Up @@ -905,6 +1012,11 @@ static int apple_pmc_probe(struct platform_device *pdev)
pmc->rails = devm_platform_ioremap_resource_byname(pdev, "rails");
if (IS_ERR(pmc->rails))
return PTR_ERR(pmc->rails);
if (platform_get_resource_byname(pdev, IORESOURCE_MEM, "clusters")) {
pmc->clusters = devm_platform_ioremap_resource_byname(pdev, "clusters");
if (IS_ERR(pmc->clusters))
return PTR_ERR(pmc->clusters);
}

/* An unpowered PMC reads all-ones; do not write to it. */
for (rail = 0; rail < pmc->hw->rails; rail++) {
Expand Down Expand Up @@ -953,6 +1065,8 @@ static int apple_pmc_probe(struct platform_device *pdev)
continue;
if (of_property_present(child, "apple,rail"))
ret = apple_pmc_floor_add(pmc, child);
else if (of_property_present(child, "apple,cluster-bit"))
ret = apple_pmc_cluster_add(pmc, child);
else
ret = apple_pmc_domain_add(pmc, child);
if (ret) {
Expand Down
Loading