diff options
author | Arnd Bergmann <arnd@arndb.de> | 2017-06-29 11:34:57 -0400 |
---|---|---|
committer | Arnd Bergmann <arnd@arndb.de> | 2017-06-29 11:34:57 -0400 |
commit | ffe3744a591fdce695da6b891378261e2caedc69 (patch) | |
tree | 2581d8ed91b8db8cb3022a1b2dcd5f2e441b443c | |
parent | 4312fc34f6d000de0cc233dd442bc573d015b8a2 (diff) | |
parent | d97d0f1b4630d46976b7a275c1636229c3ebf4d0 (diff) |
Merge tag 'actions-drivers-for-4.13' of git://git.kernel.org/pub/scm/linux/kernel/git/afaerber/linux-actions into next/drivers
Pull "Actions Semi SoC drivers for 4.13" from Andreas Färber:
This adds clock source and power domain drivers for S500/S900.
* tag 'actions-drivers-for-4.13' of git://git.kernel.org/pub/scm/linux/kernel/git/afaerber/linux-actions:
soc: actions: owl-sps: Factor out owl_sps_set_pg() for power-gating
soc: actions: Add Owl SPS
dt-bindings: power: Add Owl SPS power domains
clocksource: owl: Add S900 support
clocksource: Add Owl timer
-rw-r--r-- | Documentation/devicetree/bindings/power/actions,owl-sps.txt | 17 | ||||
-rw-r--r-- | drivers/clocksource/Kconfig | 7 | ||||
-rw-r--r-- | drivers/clocksource/Makefile | 1 | ||||
-rw-r--r-- | drivers/clocksource/owl-timer.c | 172 | ||||
-rw-r--r-- | drivers/soc/Kconfig | 1 | ||||
-rw-r--r-- | drivers/soc/Makefile | 1 | ||||
-rw-r--r-- | drivers/soc/actions/Kconfig | 16 | ||||
-rw-r--r-- | drivers/soc/actions/Makefile | 2 | ||||
-rw-r--r-- | drivers/soc/actions/owl-sps-helper.c | 51 | ||||
-rw-r--r-- | drivers/soc/actions/owl-sps.c | 224 | ||||
-rw-r--r-- | include/dt-bindings/power/owl-s500-powergate.h | 19 | ||||
-rw-r--r-- | include/linux/soc/actions/owl-sps.h | 11 |
12 files changed, 522 insertions, 0 deletions
diff --git a/Documentation/devicetree/bindings/power/actions,owl-sps.txt b/Documentation/devicetree/bindings/power/actions,owl-sps.txt new file mode 100644 index 000000000000..007b9a7ae723 --- /dev/null +++ b/Documentation/devicetree/bindings/power/actions,owl-sps.txt | |||
@@ -0,0 +1,17 @@ | |||
1 | Actions Semi Owl Smart Power System (SPS) | ||
2 | |||
3 | Required properties: | ||
4 | - compatible : "actions,s500-sps" for S500 | ||
5 | - reg : Offset and length of the register set for the device. | ||
6 | - #power-domain-cells : Must be 1. | ||
7 | See macros in: | ||
8 | include/dt-bindings/power/owl-s500-powergate.h for S500 | ||
9 | |||
10 | |||
11 | Example: | ||
12 | |||
13 | sps: power-controller@b01b0100 { | ||
14 | compatible = "actions,s500-sps"; | ||
15 | reg = <0xb01b0100 0x100>; | ||
16 | #power-domain-cells = <1>; | ||
17 | }; | ||
diff --git a/drivers/clocksource/Kconfig b/drivers/clocksource/Kconfig index 545d541ae20e..a1e4fc622569 100644 --- a/drivers/clocksource/Kconfig +++ b/drivers/clocksource/Kconfig | |||
@@ -109,6 +109,13 @@ config ORION_TIMER | |||
109 | help | 109 | help |
110 | Enables the support for the Orion timer driver | 110 | Enables the support for the Orion timer driver |
111 | 111 | ||
112 | config OWL_TIMER | ||
113 | bool "Owl timer driver" if COMPILE_TEST | ||
114 | depends on GENERIC_CLOCKEVENTS | ||
115 | select CLKSRC_MMIO | ||
116 | help | ||
117 | Enables the support for the Actions Semi Owl timer driver. | ||
118 | |||
112 | config SUN4I_TIMER | 119 | config SUN4I_TIMER |
113 | bool "Sun4i timer driver" if COMPILE_TEST | 120 | bool "Sun4i timer driver" if COMPILE_TEST |
114 | depends on GENERIC_CLOCKEVENTS | 121 | depends on GENERIC_CLOCKEVENTS |
diff --git a/drivers/clocksource/Makefile b/drivers/clocksource/Makefile index 2b5b56a6f00f..9cd12486483c 100644 --- a/drivers/clocksource/Makefile +++ b/drivers/clocksource/Makefile | |||
@@ -53,6 +53,7 @@ obj-$(CONFIG_CLKSRC_PISTACHIO) += time-pistachio.o | |||
53 | obj-$(CONFIG_CLKSRC_TI_32K) += timer-ti-32k.o | 53 | obj-$(CONFIG_CLKSRC_TI_32K) += timer-ti-32k.o |
54 | obj-$(CONFIG_CLKSRC_NPS) += timer-nps.o | 54 | obj-$(CONFIG_CLKSRC_NPS) += timer-nps.o |
55 | obj-$(CONFIG_OXNAS_RPS_TIMER) += timer-oxnas-rps.o | 55 | obj-$(CONFIG_OXNAS_RPS_TIMER) += timer-oxnas-rps.o |
56 | obj-$(CONFIG_OWL_TIMER) += owl-timer.o | ||
56 | 57 | ||
57 | obj-$(CONFIG_ARC_TIMERS) += arc_timer.o | 58 | obj-$(CONFIG_ARC_TIMERS) += arc_timer.o |
58 | obj-$(CONFIG_ARM_ARCH_TIMER) += arm_arch_timer.o | 59 | obj-$(CONFIG_ARM_ARCH_TIMER) += arm_arch_timer.o |
diff --git a/drivers/clocksource/owl-timer.c b/drivers/clocksource/owl-timer.c new file mode 100644 index 000000000000..d19c53c11094 --- /dev/null +++ b/drivers/clocksource/owl-timer.c | |||
@@ -0,0 +1,172 @@ | |||
1 | /* | ||
2 | * Actions Semi Owl timer | ||
3 | * | ||
4 | * Copyright 2012 Actions Semi Inc. | ||
5 | * Author: Actions Semi, Inc. | ||
6 | * | ||
7 | * Copyright (c) 2017 SUSE Linux GmbH | ||
8 | * Author: Andreas Färber | ||
9 | * | ||
10 | * This program is free software; you can redistribute it and/or modify it | ||
11 | * under the terms of the GNU General Public License as published by the | ||
12 | * Free Software Foundation; either version 2 of the License, or (at your | ||
13 | * option) any later version. | ||
14 | */ | ||
15 | |||
16 | #include <linux/clk.h> | ||
17 | #include <linux/clockchips.h> | ||
18 | #include <linux/interrupt.h> | ||
19 | #include <linux/irq.h> | ||
20 | #include <linux/irqreturn.h> | ||
21 | #include <linux/sched_clock.h> | ||
22 | #include <linux/of.h> | ||
23 | #include <linux/of_address.h> | ||
24 | #include <linux/of_irq.h> | ||
25 | |||
26 | #define OWL_Tx_CTL 0x0 | ||
27 | #define OWL_Tx_CMP 0x4 | ||
28 | #define OWL_Tx_VAL 0x8 | ||
29 | |||
30 | #define OWL_Tx_CTL_PD BIT(0) | ||
31 | #define OWL_Tx_CTL_INTEN BIT(1) | ||
32 | #define OWL_Tx_CTL_EN BIT(2) | ||
33 | |||
34 | static void __iomem *owl_timer_base; | ||
35 | static void __iomem *owl_clksrc_base; | ||
36 | static void __iomem *owl_clkevt_base; | ||
37 | |||
38 | static inline void owl_timer_reset(void __iomem *base) | ||
39 | { | ||
40 | writel(0, base + OWL_Tx_CTL); | ||
41 | writel(0, base + OWL_Tx_VAL); | ||
42 | writel(0, base + OWL_Tx_CMP); | ||
43 | } | ||
44 | |||
45 | static inline void owl_timer_set_enabled(void __iomem *base, bool enabled) | ||
46 | { | ||
47 | u32 ctl = readl(base + OWL_Tx_CTL); | ||
48 | |||
49 | /* PD bit is cleared when set */ | ||
50 | ctl &= ~OWL_Tx_CTL_PD; | ||
51 | |||
52 | if (enabled) | ||
53 | ctl |= OWL_Tx_CTL_EN; | ||
54 | else | ||
55 | ctl &= ~OWL_Tx_CTL_EN; | ||
56 | |||
57 | writel(ctl, base + OWL_Tx_CTL); | ||
58 | } | ||
59 | |||
60 | static u64 notrace owl_timer_sched_read(void) | ||
61 | { | ||
62 | return (u64)readl(owl_clksrc_base + OWL_Tx_VAL); | ||
63 | } | ||
64 | |||
65 | static int owl_timer_set_state_shutdown(struct clock_event_device *evt) | ||
66 | { | ||
67 | owl_timer_set_enabled(owl_clkevt_base, false); | ||
68 | |||
69 | return 0; | ||
70 | } | ||
71 | |||
72 | static int owl_timer_set_state_oneshot(struct clock_event_device *evt) | ||
73 | { | ||
74 | owl_timer_reset(owl_clkevt_base); | ||
75 | |||
76 | return 0; | ||
77 | } | ||
78 | |||
79 | static int owl_timer_tick_resume(struct clock_event_device *evt) | ||
80 | { | ||
81 | return 0; | ||
82 | } | ||
83 | |||
84 | static int owl_timer_set_next_event(unsigned long evt, | ||
85 | struct clock_event_device *ev) | ||
86 | { | ||
87 | void __iomem *base = owl_clkevt_base; | ||
88 | |||
89 | owl_timer_set_enabled(base, false); | ||
90 | writel(OWL_Tx_CTL_INTEN, base + OWL_Tx_CTL); | ||
91 | writel(0, base + OWL_Tx_VAL); | ||
92 | writel(evt, base + OWL_Tx_CMP); | ||
93 | owl_timer_set_enabled(base, true); | ||
94 | |||
95 | return 0; | ||
96 | } | ||
97 | |||
98 | static struct clock_event_device owl_clockevent = { | ||
99 | .name = "owl_tick", | ||
100 | .rating = 200, | ||
101 | .features = CLOCK_EVT_FEAT_ONESHOT | | ||
102 | CLOCK_EVT_FEAT_DYNIRQ, | ||
103 | .set_state_shutdown = owl_timer_set_state_shutdown, | ||
104 | .set_state_oneshot = owl_timer_set_state_oneshot, | ||
105 | .tick_resume = owl_timer_tick_resume, | ||
106 | .set_next_event = owl_timer_set_next_event, | ||
107 | }; | ||
108 | |||
109 | static irqreturn_t owl_timer1_interrupt(int irq, void *dev_id) | ||
110 | { | ||
111 | struct clock_event_device *evt = (struct clock_event_device *)dev_id; | ||
112 | |||
113 | writel(OWL_Tx_CTL_PD, owl_clkevt_base + OWL_Tx_CTL); | ||
114 | |||
115 | evt->event_handler(evt); | ||
116 | |||
117 | return IRQ_HANDLED; | ||
118 | } | ||
119 | |||
120 | static int __init owl_timer_init(struct device_node *node) | ||
121 | { | ||
122 | struct clk *clk; | ||
123 | unsigned long rate; | ||
124 | int timer1_irq, ret; | ||
125 | |||
126 | owl_timer_base = of_io_request_and_map(node, 0, "owl-timer"); | ||
127 | if (IS_ERR(owl_timer_base)) { | ||
128 | pr_err("Can't map timer registers"); | ||
129 | return PTR_ERR(owl_timer_base); | ||
130 | } | ||
131 | |||
132 | owl_clksrc_base = owl_timer_base + 0x08; | ||
133 | owl_clkevt_base = owl_timer_base + 0x14; | ||
134 | |||
135 | timer1_irq = of_irq_get_byname(node, "timer1"); | ||
136 | if (timer1_irq <= 0) { | ||
137 | pr_err("Can't parse timer1 IRQ"); | ||
138 | return -EINVAL; | ||
139 | } | ||
140 | |||
141 | clk = of_clk_get(node, 0); | ||
142 | if (IS_ERR(clk)) | ||
143 | return PTR_ERR(clk); | ||
144 | |||
145 | rate = clk_get_rate(clk); | ||
146 | |||
147 | owl_timer_reset(owl_clksrc_base); | ||
148 | owl_timer_set_enabled(owl_clksrc_base, true); | ||
149 | |||
150 | sched_clock_register(owl_timer_sched_read, 32, rate); | ||
151 | clocksource_mmio_init(owl_clksrc_base + OWL_Tx_VAL, node->name, | ||
152 | rate, 200, 32, clocksource_mmio_readl_up); | ||
153 | |||
154 | owl_timer_reset(owl_clkevt_base); | ||
155 | |||
156 | ret = request_irq(timer1_irq, owl_timer1_interrupt, IRQF_TIMER, | ||
157 | "owl-timer", &owl_clockevent); | ||
158 | if (ret) { | ||
159 | pr_err("failed to request irq %d\n", timer1_irq); | ||
160 | return ret; | ||
161 | } | ||
162 | |||
163 | owl_clockevent.cpumask = cpumask_of(0); | ||
164 | owl_clockevent.irq = timer1_irq; | ||
165 | |||
166 | clockevents_config_and_register(&owl_clockevent, rate, | ||
167 | 0xf, 0xffffffff); | ||
168 | |||
169 | return 0; | ||
170 | } | ||
171 | CLOCKSOURCE_OF_DECLARE(owl_s500, "actions,s500-timer", owl_timer_init); | ||
172 | CLOCKSOURCE_OF_DECLARE(owl_s900, "actions,s900-timer", owl_timer_init); | ||
diff --git a/drivers/soc/Kconfig b/drivers/soc/Kconfig index f8c0286f8a65..07fc0ac51c52 100644 --- a/drivers/soc/Kconfig +++ b/drivers/soc/Kconfig | |||
@@ -1,5 +1,6 @@ | |||
1 | menu "SOC (System On Chip) specific Drivers" | 1 | menu "SOC (System On Chip) specific Drivers" |
2 | 2 | ||
3 | source "drivers/soc/actions/Kconfig" | ||
3 | source "drivers/soc/atmel/Kconfig" | 4 | source "drivers/soc/atmel/Kconfig" |
4 | source "drivers/soc/bcm/Kconfig" | 5 | source "drivers/soc/bcm/Kconfig" |
5 | source "drivers/soc/fsl/Kconfig" | 6 | source "drivers/soc/fsl/Kconfig" |
diff --git a/drivers/soc/Makefile b/drivers/soc/Makefile index c0b2a610efcd..9241125416ba 100644 --- a/drivers/soc/Makefile +++ b/drivers/soc/Makefile | |||
@@ -2,6 +2,7 @@ | |||
2 | # Makefile for the Linux Kernel SOC specific device drivers. | 2 | # Makefile for the Linux Kernel SOC specific device drivers. |
3 | # | 3 | # |
4 | 4 | ||
5 | obj-$(CONFIG_ARCH_ACTIONS) += actions/ | ||
5 | obj-$(CONFIG_ARCH_AT91) += atmel/ | 6 | obj-$(CONFIG_ARCH_AT91) += atmel/ |
6 | obj-y += bcm/ | 7 | obj-y += bcm/ |
7 | obj-$(CONFIG_ARCH_DOVE) += dove/ | 8 | obj-$(CONFIG_ARCH_DOVE) += dove/ |
diff --git a/drivers/soc/actions/Kconfig b/drivers/soc/actions/Kconfig new file mode 100644 index 000000000000..9d68b5a771c3 --- /dev/null +++ b/drivers/soc/actions/Kconfig | |||
@@ -0,0 +1,16 @@ | |||
1 | if ARCH_ACTIONS || COMPILE_TEST | ||
2 | |||
3 | config OWL_PM_DOMAINS_HELPER | ||
4 | bool | ||
5 | |||
6 | config OWL_PM_DOMAINS | ||
7 | bool "Actions Semi SPS power domains" | ||
8 | depends on PM | ||
9 | select OWL_PM_DOMAINS_HELPER | ||
10 | select PM_GENERIC_DOMAINS | ||
11 | help | ||
12 | Say 'y' here to enable support for Smart Power System (SPS) | ||
13 | power-gating on Actions Semiconductor S500 SoC. | ||
14 | If unsure, say 'n'. | ||
15 | |||
16 | endif | ||
diff --git a/drivers/soc/actions/Makefile b/drivers/soc/actions/Makefile new file mode 100644 index 000000000000..1e101b06bab1 --- /dev/null +++ b/drivers/soc/actions/Makefile | |||
@@ -0,0 +1,2 @@ | |||
1 | obj-$(CONFIG_OWL_PM_DOMAINS_HELPER) += owl-sps-helper.o | ||
2 | obj-$(CONFIG_OWL_PM_DOMAINS) += owl-sps.o | ||
diff --git a/drivers/soc/actions/owl-sps-helper.c b/drivers/soc/actions/owl-sps-helper.c new file mode 100644 index 000000000000..9d7a2c2b44ec --- /dev/null +++ b/drivers/soc/actions/owl-sps-helper.c | |||
@@ -0,0 +1,51 @@ | |||
1 | /* | ||
2 | * Actions Semi Owl Smart Power System (SPS) shared helpers | ||
3 | * | ||
4 | * Copyright 2012 Actions Semi Inc. | ||
5 | * Author: Actions Semi, Inc. | ||
6 | * | ||
7 | * Copyright (c) 2017 Andreas Färber | ||
8 | * | ||
9 | * This program is free software; you can redistribute it and/or modify it | ||
10 | * under the terms of the GNU General Public License as published by the | ||
11 | * Free Software Foundation; either version 2 of the License, or (at your | ||
12 | * option) any later version. | ||
13 | */ | ||
14 | |||
15 | #include <linux/delay.h> | ||
16 | #include <linux/io.h> | ||
17 | |||
18 | #define OWL_SPS_PG_CTL 0x0 | ||
19 | |||
20 | int owl_sps_set_pg(void __iomem *base, u32 pwr_mask, u32 ack_mask, bool enable) | ||
21 | { | ||
22 | u32 val; | ||
23 | bool ack; | ||
24 | int timeout; | ||
25 | |||
26 | val = readl(base + OWL_SPS_PG_CTL); | ||
27 | ack = val & ack_mask; | ||
28 | if (ack == enable) | ||
29 | return 0; | ||
30 | |||
31 | if (enable) | ||
32 | val |= pwr_mask; | ||
33 | else | ||
34 | val &= ~pwr_mask; | ||
35 | |||
36 | writel(val, base + OWL_SPS_PG_CTL); | ||
37 | |||
38 | for (timeout = 5000; timeout > 0; timeout -= 50) { | ||
39 | val = readl(base + OWL_SPS_PG_CTL); | ||
40 | if ((val & ack_mask) == (enable ? ack_mask : 0)) | ||
41 | break; | ||
42 | udelay(50); | ||
43 | } | ||
44 | if (timeout <= 0) | ||
45 | return -ETIMEDOUT; | ||
46 | |||
47 | udelay(10); | ||
48 | |||
49 | return 0; | ||
50 | } | ||
51 | EXPORT_SYMBOL_GPL(owl_sps_set_pg); | ||
diff --git a/drivers/soc/actions/owl-sps.c b/drivers/soc/actions/owl-sps.c new file mode 100644 index 000000000000..875225bfa21c --- /dev/null +++ b/drivers/soc/actions/owl-sps.c | |||
@@ -0,0 +1,224 @@ | |||
1 | /* | ||
2 | * Actions Semi Owl Smart Power System (SPS) | ||
3 | * | ||
4 | * Copyright 2012 Actions Semi Inc. | ||
5 | * Author: Actions Semi, Inc. | ||
6 | * | ||
7 | * Copyright (c) 2017 Andreas Färber | ||
8 | * | ||
9 | * This program is free software; you can redistribute it and/or modify it | ||
10 | * under the terms of the GNU General Public License as published by the | ||
11 | * Free Software Foundation; either version 2 of the License, or (at your | ||
12 | * option) any later version. | ||
13 | */ | ||
14 | |||
15 | #include <linux/of_address.h> | ||
16 | #include <linux/of_platform.h> | ||
17 | #include <linux/pm_domain.h> | ||
18 | #include <linux/soc/actions/owl-sps.h> | ||
19 | #include <dt-bindings/power/owl-s500-powergate.h> | ||
20 | |||
21 | struct owl_sps_domain_info { | ||
22 | const char *name; | ||
23 | int pwr_bit; | ||
24 | int ack_bit; | ||
25 | unsigned int genpd_flags; | ||
26 | }; | ||
27 | |||
28 | struct owl_sps_info { | ||
29 | unsigned num_domains; | ||
30 | const struct owl_sps_domain_info *domains; | ||
31 | }; | ||
32 | |||
33 | struct owl_sps { | ||
34 | struct device *dev; | ||
35 | const struct owl_sps_info *info; | ||
36 | void __iomem *base; | ||
37 | struct genpd_onecell_data genpd_data; | ||
38 | struct generic_pm_domain *domains[]; | ||
39 | }; | ||
40 | |||
41 | #define to_owl_pd(gpd) container_of(gpd, struct owl_sps_domain, genpd) | ||
42 | |||
43 | struct owl_sps_domain { | ||
44 | struct generic_pm_domain genpd; | ||
45 | const struct owl_sps_domain_info *info; | ||
46 | struct owl_sps *sps; | ||
47 | }; | ||
48 | |||
49 | static int owl_sps_set_power(struct owl_sps_domain *pd, bool enable) | ||
50 | { | ||
51 | u32 pwr_mask, ack_mask; | ||
52 | |||
53 | ack_mask = BIT(pd->info->ack_bit); | ||
54 | pwr_mask = BIT(pd->info->pwr_bit); | ||
55 | |||
56 | return owl_sps_set_pg(pd->sps->base, pwr_mask, ack_mask, enable); | ||
57 | } | ||
58 | |||
59 | static int owl_sps_power_on(struct generic_pm_domain *domain) | ||
60 | { | ||
61 | struct owl_sps_domain *pd = to_owl_pd(domain); | ||
62 | |||
63 | dev_dbg(pd->sps->dev, "%s power on", pd->info->name); | ||
64 | |||
65 | return owl_sps_set_power(pd, true); | ||
66 | } | ||
67 | |||
68 | static int owl_sps_power_off(struct generic_pm_domain *domain) | ||
69 | { | ||
70 | struct owl_sps_domain *pd = to_owl_pd(domain); | ||
71 | |||
72 | dev_dbg(pd->sps->dev, "%s power off", pd->info->name); | ||
73 | |||
74 | return owl_sps_set_power(pd, false); | ||
75 | } | ||
76 | |||
77 | static int owl_sps_init_domain(struct owl_sps *sps, int index) | ||
78 | { | ||
79 | struct owl_sps_domain *pd; | ||
80 | |||
81 | pd = devm_kzalloc(sps->dev, sizeof(*pd), GFP_KERNEL); | ||
82 | if (!pd) | ||
83 | return -ENOMEM; | ||
84 | |||
85 | pd->info = &sps->info->domains[index]; | ||
86 | pd->sps = sps; | ||
87 | |||
88 | pd->genpd.name = pd->info->name; | ||
89 | pd->genpd.power_on = owl_sps_power_on; | ||
90 | pd->genpd.power_off = owl_sps_power_off; | ||
91 | pd->genpd.flags = pd->info->genpd_flags; | ||
92 | pm_genpd_init(&pd->genpd, NULL, false); | ||
93 | |||
94 | sps->genpd_data.domains[index] = &pd->genpd; | ||
95 | |||
96 | return 0; | ||
97 | } | ||
98 | |||
99 | static int owl_sps_probe(struct platform_device *pdev) | ||
100 | { | ||
101 | const struct of_device_id *match; | ||
102 | const struct owl_sps_info *sps_info; | ||
103 | struct owl_sps *sps; | ||
104 | int i, ret; | ||
105 | |||
106 | if (!pdev->dev.of_node) { | ||
107 | dev_err(&pdev->dev, "no device node\n"); | ||
108 | return -ENODEV; | ||
109 | } | ||
110 | |||
111 | match = of_match_device(pdev->dev.driver->of_match_table, &pdev->dev); | ||
112 | if (!match || !match->data) { | ||
113 | dev_err(&pdev->dev, "unknown compatible or missing data\n"); | ||
114 | return -EINVAL; | ||
115 | } | ||
116 | |||
117 | sps_info = match->data; | ||
118 | |||
119 | sps = devm_kzalloc(&pdev->dev, sizeof(*sps) + | ||
120 | sps_info->num_domains * sizeof(sps->domains[0]), | ||
121 | GFP_KERNEL); | ||
122 | if (!sps) | ||
123 | return -ENOMEM; | ||
124 | |||
125 | sps->base = of_io_request_and_map(pdev->dev.of_node, 0, "owl-sps"); | ||
126 | if (IS_ERR(sps->base)) { | ||
127 | dev_err(&pdev->dev, "failed to map sps registers\n"); | ||
128 | return PTR_ERR(sps->base); | ||
129 | } | ||
130 | |||
131 | sps->dev = &pdev->dev; | ||
132 | sps->info = sps_info; | ||
133 | sps->genpd_data.domains = sps->domains; | ||
134 | sps->genpd_data.num_domains = sps_info->num_domains; | ||
135 | |||
136 | for (i = 0; i < sps_info->num_domains; i++) { | ||
137 | ret = owl_sps_init_domain(sps, i); | ||
138 | if (ret) | ||
139 | return ret; | ||
140 | } | ||
141 | |||
142 | ret = of_genpd_add_provider_onecell(pdev->dev.of_node, &sps->genpd_data); | ||
143 | if (ret) { | ||
144 | dev_err(&pdev->dev, "failed to add provider (%d)", ret); | ||
145 | return ret; | ||
146 | } | ||
147 | |||
148 | return 0; | ||
149 | } | ||
150 | |||
151 | static const struct owl_sps_domain_info s500_sps_domains[] = { | ||
152 | [S500_PD_VDE] = { | ||
153 | .name = "VDE", | ||
154 | .pwr_bit = 0, | ||
155 | .ack_bit = 16, | ||
156 | }, | ||
157 | [S500_PD_VCE_SI] = { | ||
158 | .name = "VCE_SI", | ||
159 | .pwr_bit = 1, | ||
160 | .ack_bit = 17, | ||
161 | }, | ||
162 | [S500_PD_USB2_1] = { | ||
163 | .name = "USB2_1", | ||
164 | .pwr_bit = 2, | ||
165 | .ack_bit = 18, | ||
166 | }, | ||
167 | [S500_PD_CPU2] = { | ||
168 | .name = "CPU2", | ||
169 | .pwr_bit = 5, | ||
170 | .ack_bit = 21, | ||
171 | .genpd_flags = GENPD_FLAG_ALWAYS_ON, | ||
172 | }, | ||
173 | [S500_PD_CPU3] = { | ||
174 | .name = "CPU3", | ||
175 | .pwr_bit = 6, | ||
176 | .ack_bit = 22, | ||
177 | .genpd_flags = GENPD_FLAG_ALWAYS_ON, | ||
178 | }, | ||
179 | [S500_PD_DMA] = { | ||
180 | .name = "DMA", | ||
181 | .pwr_bit = 8, | ||
182 | .ack_bit = 12, | ||
183 | }, | ||
184 | [S500_PD_DS] = { | ||
185 | .name = "DS", | ||
186 | .pwr_bit = 9, | ||
187 | .ack_bit = 13, | ||
188 | }, | ||
189 | [S500_PD_USB3] = { | ||
190 | .name = "USB3", | ||
191 | .pwr_bit = 10, | ||
192 | .ack_bit = 14, | ||
193 | }, | ||
194 | [S500_PD_USB2_0] = { | ||
195 | .name = "USB2_0", | ||
196 | .pwr_bit = 11, | ||
197 | .ack_bit = 15, | ||
198 | }, | ||
199 | }; | ||
200 | |||
201 | static const struct owl_sps_info s500_sps_info = { | ||
202 | .num_domains = ARRAY_SIZE(s500_sps_domains), | ||
203 | .domains = s500_sps_domains, | ||
204 | }; | ||
205 | |||
206 | static const struct of_device_id owl_sps_of_matches[] = { | ||
207 | { .compatible = "actions,s500-sps", .data = &s500_sps_info }, | ||
208 | { } | ||
209 | }; | ||
210 | |||
211 | static struct platform_driver owl_sps_platform_driver = { | ||
212 | .probe = owl_sps_probe, | ||
213 | .driver = { | ||
214 | .name = "owl-sps", | ||
215 | .of_match_table = owl_sps_of_matches, | ||
216 | .suppress_bind_attrs = true, | ||
217 | }, | ||
218 | }; | ||
219 | |||
220 | static int __init owl_sps_init(void) | ||
221 | { | ||
222 | return platform_driver_register(&owl_sps_platform_driver); | ||
223 | } | ||
224 | postcore_initcall(owl_sps_init); | ||
diff --git a/include/dt-bindings/power/owl-s500-powergate.h b/include/dt-bindings/power/owl-s500-powergate.h new file mode 100644 index 000000000000..0a1c451865ea --- /dev/null +++ b/include/dt-bindings/power/owl-s500-powergate.h | |||
@@ -0,0 +1,19 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2017 Andreas Färber | ||
3 | * | ||
4 | * SPDX-License-Identifier: (GPL-2.0+ OR MIT) | ||
5 | */ | ||
6 | #ifndef DT_BINDINGS_POWER_OWL_S500_POWERGATE_H | ||
7 | #define DT_BINDINGS_POWER_OWL_S500_POWERGATE_H | ||
8 | |||
9 | #define S500_PD_VDE 0 | ||
10 | #define S500_PD_VCE_SI 1 | ||
11 | #define S500_PD_USB2_1 2 | ||
12 | #define S500_PD_CPU2 3 | ||
13 | #define S500_PD_CPU3 4 | ||
14 | #define S500_PD_DMA 5 | ||
15 | #define S500_PD_DS 6 | ||
16 | #define S500_PD_USB3 7 | ||
17 | #define S500_PD_USB2_0 8 | ||
18 | |||
19 | #endif | ||
diff --git a/include/linux/soc/actions/owl-sps.h b/include/linux/soc/actions/owl-sps.h new file mode 100644 index 000000000000..33d0dbeceb55 --- /dev/null +++ b/include/linux/soc/actions/owl-sps.h | |||
@@ -0,0 +1,11 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2017 Andreas Färber | ||
3 | * | ||
4 | * SPDX-License-Identifier: GPL-2.0+ | ||
5 | */ | ||
6 | #ifndef SOC_ACTIONS_OWL_SPS_H | ||
7 | #define SOC_ACTIONS_OWL_SPS_H | ||
8 | |||
9 | int owl_sps_set_pg(void __iomem *base, u32 pwr_mask, u32 ack_mask, bool enable); | ||
10 | |||
11 | #endif | ||