obj-y += rockchip/
obj-y += samsung/
obj-y += starfive/
+obj-y += sunxi/
--- /dev/null
+# SPDX-License-Identifier: GPL-2.0-only
+obj-$(CONFIG_SUN20I_PPU) += sun20i-ppu.o
--- /dev/null
+// SPDX-License-Identifier: GPL-2.0-only
+
+#include <linux/bitfield.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/iopoll.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/pm_domain.h>
+#include <linux/reset.h>
+
+#define PD_STATE_ON 1
+#define PD_STATE_OFF 2
+
+#define PD_RSTN_REG 0x00
+#define PD_CLK_GATE_REG 0x04
+#define PD_PWROFF_GATE_REG 0x08
+#define PD_PSW_ON_REG 0x0c
+#define PD_PSW_OFF_REG 0x10
+#define PD_PSW_DELAY_REG 0x14
+#define PD_OFF_DELAY_REG 0x18
+#define PD_ON_DELAY_REG 0x1c
+#define PD_COMMAND_REG 0x20
+#define PD_STATUS_REG 0x24
+#define PD_STATUS_COMPLETE BIT(1)
+#define PD_STATUS_BUSY BIT(3)
+#define PD_STATUS_STATE GENMASK(17, 16)
+#define PD_ACTIVE_CTRL_REG 0x2c
+#define PD_GATE_STATUS_REG 0x30
+#define PD_RSTN_STATUS BIT(0)
+#define PD_CLK_GATE_STATUS BIT(1)
+#define PD_PWROFF_GATE_STATUS BIT(2)
+#define PD_PSW_STATUS_REG 0x34
+
+#define PD_REGS_SIZE 0x80
+
+struct sun20i_ppu_desc {
+ const char *const *names;
+ unsigned int num_domains;
+};
+
+struct sun20i_ppu_pd {
+ struct generic_pm_domain genpd;
+ void __iomem *base;
+};
+
+#define to_sun20i_ppu_pd(_genpd) \
+ container_of(_genpd, struct sun20i_ppu_pd, genpd)
+
+static bool sun20i_ppu_pd_is_on(const struct sun20i_ppu_pd *pd)
+{
+ u32 status = readl(pd->base + PD_STATUS_REG);
+
+ return FIELD_GET(PD_STATUS_STATE, status) == PD_STATE_ON;
+}
+
+static int sun20i_ppu_pd_set_power(const struct sun20i_ppu_pd *pd, bool power_on)
+{
+ u32 state, status;
+ int ret;
+
+ if (sun20i_ppu_pd_is_on(pd) == power_on)
+ return 0;
+
+ /* Wait for the power controller to be idle. */
+ ret = readl_poll_timeout(pd->base + PD_STATUS_REG, status,
+ !(status & PD_STATUS_BUSY), 100, 1000);
+ if (ret)
+ return ret;
+
+ state = power_on ? PD_STATE_ON : PD_STATE_OFF;
+ writel(state, pd->base + PD_COMMAND_REG);
+
+ /* Wait for the state transition to complete. */
+ ret = readl_poll_timeout(pd->base + PD_STATUS_REG, status,
+ FIELD_GET(PD_STATUS_STATE, status) == state &&
+ (status & PD_STATUS_COMPLETE), 100, 1000);
+ if (ret)
+ return ret;
+
+ /* Clear the completion flag. */
+ writel(status, pd->base + PD_STATUS_REG);
+
+ return 0;
+}
+
+static int sun20i_ppu_pd_power_on(struct generic_pm_domain *genpd)
+{
+ const struct sun20i_ppu_pd *pd = to_sun20i_ppu_pd(genpd);
+
+ return sun20i_ppu_pd_set_power(pd, true);
+}
+
+static int sun20i_ppu_pd_power_off(struct generic_pm_domain *genpd)
+{
+ const struct sun20i_ppu_pd *pd = to_sun20i_ppu_pd(genpd);
+
+ return sun20i_ppu_pd_set_power(pd, false);
+}
+
+static int sun20i_ppu_probe(struct platform_device *pdev)
+{
+ const struct sun20i_ppu_desc *desc;
+ struct device *dev = &pdev->dev;
+ struct genpd_onecell_data *ppu;
+ struct sun20i_ppu_pd *pds;
+ struct reset_control *rst;
+ void __iomem *base;
+ struct clk *clk;
+ int ret;
+
+ desc = of_device_get_match_data(dev);
+ if (!desc)
+ return -EINVAL;
+
+ pds = devm_kcalloc(dev, desc->num_domains, sizeof(*pds), GFP_KERNEL);
+ if (!pds)
+ return -ENOMEM;
+
+ ppu = devm_kzalloc(dev, sizeof(*ppu), GFP_KERNEL);
+ if (!ppu)
+ return -ENOMEM;
+
+ ppu->domains = devm_kcalloc(dev, desc->num_domains,
+ sizeof(*ppu->domains), GFP_KERNEL);
+ if (!ppu->domains)
+ return -ENOMEM;
+
+ ppu->num_domains = desc->num_domains;
+ platform_set_drvdata(pdev, ppu);
+
+ base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(base))
+ return PTR_ERR(base);
+
+ clk = devm_clk_get_enabled(dev, NULL);
+ if (IS_ERR(clk))
+ return PTR_ERR(clk);
+
+ rst = devm_reset_control_get_exclusive(dev, NULL);
+ if (IS_ERR(rst))
+ return PTR_ERR(rst);
+
+ ret = reset_control_deassert(rst);
+ if (ret)
+ return ret;
+
+ for (unsigned int i = 0; i < ppu->num_domains; ++i) {
+ struct sun20i_ppu_pd *pd = &pds[i];
+
+ pd->genpd.name = desc->names[i];
+ pd->genpd.power_off = sun20i_ppu_pd_power_off;
+ pd->genpd.power_on = sun20i_ppu_pd_power_on;
+ pd->base = base + PD_REGS_SIZE * i;
+
+ ret = pm_genpd_init(&pd->genpd, NULL, sun20i_ppu_pd_is_on(pd));
+ if (ret) {
+ dev_warn(dev, "Failed to add '%s' domain: %d\n",
+ pd->genpd.name, ret);
+ continue;
+ }
+
+ ppu->domains[i] = &pd->genpd;
+ }
+
+ ret = of_genpd_add_provider_onecell(dev->of_node, ppu);
+ if (ret)
+ dev_warn(dev, "Failed to add provider: %d\n", ret);
+
+ return 0;
+}
+
+static const char *const sun20i_d1_ppu_pd_names[] = {
+ "CPU",
+ "VE",
+ "DSP",
+};
+
+static const struct sun20i_ppu_desc sun20i_d1_ppu_desc = {
+ .names = sun20i_d1_ppu_pd_names,
+ .num_domains = ARRAY_SIZE(sun20i_d1_ppu_pd_names),
+};
+
+static const struct of_device_id sun20i_ppu_of_match[] = {
+ {
+ .compatible = "allwinner,sun20i-d1-ppu",
+ .data = &sun20i_d1_ppu_desc,
+ },
+ { }
+};
+MODULE_DEVICE_TABLE(of, sun20i_ppu_of_match);
+
+static struct platform_driver sun20i_ppu_driver = {
+ .probe = sun20i_ppu_probe,
+ .driver = {
+ .name = "sun20i-ppu",
+ .of_match_table = sun20i_ppu_of_match,
+ /* Power domains cannot be removed while they are in use. */
+ .suppress_bind_attrs = true,
+ },
+};
+module_platform_driver(sun20i_ppu_driver);
+
+MODULE_AUTHOR("Samuel Holland <samuel@sholland.org>");
+MODULE_DESCRIPTION("Allwinner D1 PPU power domain driver");
+MODULE_LICENSE("GPL");
# SPDX-License-Identifier: GPL-2.0-only
obj-$(CONFIG_SUNXI_MBUS) += sunxi_mbus.o
obj-$(CONFIG_SUNXI_SRAM) += sunxi_sram.o
-obj-$(CONFIG_SUN20I_PPU) += sun20i-ppu.o
+++ /dev/null
-// SPDX-License-Identifier: GPL-2.0-only
-
-#include <linux/bitfield.h>
-#include <linux/clk.h>
-#include <linux/io.h>
-#include <linux/iopoll.h>
-#include <linux/module.h>
-#include <linux/of_device.h>
-#include <linux/platform_device.h>
-#include <linux/pm_domain.h>
-#include <linux/reset.h>
-
-#define PD_STATE_ON 1
-#define PD_STATE_OFF 2
-
-#define PD_RSTN_REG 0x00
-#define PD_CLK_GATE_REG 0x04
-#define PD_PWROFF_GATE_REG 0x08
-#define PD_PSW_ON_REG 0x0c
-#define PD_PSW_OFF_REG 0x10
-#define PD_PSW_DELAY_REG 0x14
-#define PD_OFF_DELAY_REG 0x18
-#define PD_ON_DELAY_REG 0x1c
-#define PD_COMMAND_REG 0x20
-#define PD_STATUS_REG 0x24
-#define PD_STATUS_COMPLETE BIT(1)
-#define PD_STATUS_BUSY BIT(3)
-#define PD_STATUS_STATE GENMASK(17, 16)
-#define PD_ACTIVE_CTRL_REG 0x2c
-#define PD_GATE_STATUS_REG 0x30
-#define PD_RSTN_STATUS BIT(0)
-#define PD_CLK_GATE_STATUS BIT(1)
-#define PD_PWROFF_GATE_STATUS BIT(2)
-#define PD_PSW_STATUS_REG 0x34
-
-#define PD_REGS_SIZE 0x80
-
-struct sun20i_ppu_desc {
- const char *const *names;
- unsigned int num_domains;
-};
-
-struct sun20i_ppu_pd {
- struct generic_pm_domain genpd;
- void __iomem *base;
-};
-
-#define to_sun20i_ppu_pd(_genpd) \
- container_of(_genpd, struct sun20i_ppu_pd, genpd)
-
-static bool sun20i_ppu_pd_is_on(const struct sun20i_ppu_pd *pd)
-{
- u32 status = readl(pd->base + PD_STATUS_REG);
-
- return FIELD_GET(PD_STATUS_STATE, status) == PD_STATE_ON;
-}
-
-static int sun20i_ppu_pd_set_power(const struct sun20i_ppu_pd *pd, bool power_on)
-{
- u32 state, status;
- int ret;
-
- if (sun20i_ppu_pd_is_on(pd) == power_on)
- return 0;
-
- /* Wait for the power controller to be idle. */
- ret = readl_poll_timeout(pd->base + PD_STATUS_REG, status,
- !(status & PD_STATUS_BUSY), 100, 1000);
- if (ret)
- return ret;
-
- state = power_on ? PD_STATE_ON : PD_STATE_OFF;
- writel(state, pd->base + PD_COMMAND_REG);
-
- /* Wait for the state transition to complete. */
- ret = readl_poll_timeout(pd->base + PD_STATUS_REG, status,
- FIELD_GET(PD_STATUS_STATE, status) == state &&
- (status & PD_STATUS_COMPLETE), 100, 1000);
- if (ret)
- return ret;
-
- /* Clear the completion flag. */
- writel(status, pd->base + PD_STATUS_REG);
-
- return 0;
-}
-
-static int sun20i_ppu_pd_power_on(struct generic_pm_domain *genpd)
-{
- const struct sun20i_ppu_pd *pd = to_sun20i_ppu_pd(genpd);
-
- return sun20i_ppu_pd_set_power(pd, true);
-}
-
-static int sun20i_ppu_pd_power_off(struct generic_pm_domain *genpd)
-{
- const struct sun20i_ppu_pd *pd = to_sun20i_ppu_pd(genpd);
-
- return sun20i_ppu_pd_set_power(pd, false);
-}
-
-static int sun20i_ppu_probe(struct platform_device *pdev)
-{
- const struct sun20i_ppu_desc *desc;
- struct device *dev = &pdev->dev;
- struct genpd_onecell_data *ppu;
- struct sun20i_ppu_pd *pds;
- struct reset_control *rst;
- void __iomem *base;
- struct clk *clk;
- int ret;
-
- desc = of_device_get_match_data(dev);
- if (!desc)
- return -EINVAL;
-
- pds = devm_kcalloc(dev, desc->num_domains, sizeof(*pds), GFP_KERNEL);
- if (!pds)
- return -ENOMEM;
-
- ppu = devm_kzalloc(dev, sizeof(*ppu), GFP_KERNEL);
- if (!ppu)
- return -ENOMEM;
-
- ppu->domains = devm_kcalloc(dev, desc->num_domains,
- sizeof(*ppu->domains), GFP_KERNEL);
- if (!ppu->domains)
- return -ENOMEM;
-
- ppu->num_domains = desc->num_domains;
- platform_set_drvdata(pdev, ppu);
-
- base = devm_platform_ioremap_resource(pdev, 0);
- if (IS_ERR(base))
- return PTR_ERR(base);
-
- clk = devm_clk_get_enabled(dev, NULL);
- if (IS_ERR(clk))
- return PTR_ERR(clk);
-
- rst = devm_reset_control_get_exclusive(dev, NULL);
- if (IS_ERR(rst))
- return PTR_ERR(rst);
-
- ret = reset_control_deassert(rst);
- if (ret)
- return ret;
-
- for (unsigned int i = 0; i < ppu->num_domains; ++i) {
- struct sun20i_ppu_pd *pd = &pds[i];
-
- pd->genpd.name = desc->names[i];
- pd->genpd.power_off = sun20i_ppu_pd_power_off;
- pd->genpd.power_on = sun20i_ppu_pd_power_on;
- pd->base = base + PD_REGS_SIZE * i;
-
- ret = pm_genpd_init(&pd->genpd, NULL, sun20i_ppu_pd_is_on(pd));
- if (ret) {
- dev_warn(dev, "Failed to add '%s' domain: %d\n",
- pd->genpd.name, ret);
- continue;
- }
-
- ppu->domains[i] = &pd->genpd;
- }
-
- ret = of_genpd_add_provider_onecell(dev->of_node, ppu);
- if (ret)
- dev_warn(dev, "Failed to add provider: %d\n", ret);
-
- return 0;
-}
-
-static const char *const sun20i_d1_ppu_pd_names[] = {
- "CPU",
- "VE",
- "DSP",
-};
-
-static const struct sun20i_ppu_desc sun20i_d1_ppu_desc = {
- .names = sun20i_d1_ppu_pd_names,
- .num_domains = ARRAY_SIZE(sun20i_d1_ppu_pd_names),
-};
-
-static const struct of_device_id sun20i_ppu_of_match[] = {
- {
- .compatible = "allwinner,sun20i-d1-ppu",
- .data = &sun20i_d1_ppu_desc,
- },
- { }
-};
-MODULE_DEVICE_TABLE(of, sun20i_ppu_of_match);
-
-static struct platform_driver sun20i_ppu_driver = {
- .probe = sun20i_ppu_probe,
- .driver = {
- .name = "sun20i-ppu",
- .of_match_table = sun20i_ppu_of_match,
- /* Power domains cannot be removed while they are in use. */
- .suppress_bind_attrs = true,
- },
-};
-module_platform_driver(sun20i_ppu_driver);
-
-MODULE_AUTHOR("Samuel Holland <samuel@sholland.org>");
-MODULE_DESCRIPTION("Allwinner D1 PPU power domain driver");
-MODULE_LICENSE("GPL");