Linux Audio

Check our new training course

Linux debugging, profiling, tracing and performance analysis training

Mar 24-27, 2025, special US time zones
Register
Loading...
Note: File does not exist in v6.13.7.
  1/*
  2 * Exynos Generic power domain support.
  3 *
  4 * Copyright (c) 2012 Samsung Electronics Co., Ltd.
  5 *		http://www.samsung.com
  6 *
  7 * Implementation of Exynos specific power domain control which is used in
  8 * conjunction with runtime-pm. Support for both device-tree and non-device-tree
  9 * based power domain support is included.
 10 *
 11 * This program is free software; you can redistribute it and/or modify
 12 * it under the terms of the GNU General Public License version 2 as
 13 * published by the Free Software Foundation.
 14*/
 15
 16#include <linux/io.h>
 17#include <linux/err.h>
 18#include <linux/slab.h>
 19#include <linux/pm_domain.h>
 20#include <linux/delay.h>
 21#include <linux/of_address.h>
 22
 23#include <mach/regs-pmu.h>
 24#include <plat/devs.h>
 25
 26/*
 27 * Exynos specific wrapper around the generic power domain
 28 */
 29struct exynos_pm_domain {
 30	void __iomem *base;
 31	char const *name;
 32	bool is_off;
 33	struct generic_pm_domain pd;
 34};
 35
 36static int exynos_pd_power(struct generic_pm_domain *domain, bool power_on)
 37{
 38	struct exynos_pm_domain *pd;
 39	void __iomem *base;
 40	u32 timeout, pwr;
 41	char *op;
 42
 43	pd = container_of(domain, struct exynos_pm_domain, pd);
 44	base = pd->base;
 45
 46	pwr = power_on ? S5P_INT_LOCAL_PWR_EN : 0;
 47	__raw_writel(pwr, base);
 48
 49	/* Wait max 1ms */
 50	timeout = 10;
 51
 52	while ((__raw_readl(base + 0x4) & S5P_INT_LOCAL_PWR_EN)	!= pwr) {
 53		if (!timeout) {
 54			op = (power_on) ? "enable" : "disable";
 55			pr_err("Power domain %s %s failed\n", domain->name, op);
 56			return -ETIMEDOUT;
 57		}
 58		timeout--;
 59		cpu_relax();
 60		usleep_range(80, 100);
 61	}
 62	return 0;
 63}
 64
 65static int exynos_pd_power_on(struct generic_pm_domain *domain)
 66{
 67	return exynos_pd_power(domain, true);
 68}
 69
 70static int exynos_pd_power_off(struct generic_pm_domain *domain)
 71{
 72	return exynos_pd_power(domain, false);
 73}
 74
 75#define EXYNOS_GPD(PD, BASE, NAME)			\
 76static struct exynos_pm_domain PD = {			\
 77	.base = (void __iomem *)BASE,			\
 78	.name = NAME,					\
 79	.pd = {						\
 80		.power_off = exynos_pd_power_off,	\
 81		.power_on = exynos_pd_power_on,	\
 82	},						\
 83}
 84
 85#ifdef CONFIG_OF
 86static __init int exynos_pm_dt_parse_domains(void)
 87{
 88	struct device_node *np;
 89
 90	for_each_compatible_node(np, NULL, "samsung,exynos4210-pd") {
 91		struct exynos_pm_domain *pd;
 92
 93		pd = kzalloc(sizeof(*pd), GFP_KERNEL);
 94		if (!pd) {
 95			pr_err("%s: failed to allocate memory for domain\n",
 96					__func__);
 97			return -ENOMEM;
 98		}
 99
100		if (of_get_property(np, "samsung,exynos4210-pd-off", NULL))
101			pd->is_off = true;
102		pd->name = np->name;
103		pd->base = of_iomap(np, 0);
104		pd->pd.power_off = exynos_pd_power_off;
105		pd->pd.power_on = exynos_pd_power_on;
106		pd->pd.of_node = np;
107		pm_genpd_init(&pd->pd, NULL, false);
108	}
109	return 0;
110}
111#else
112static __init int exynos_pm_dt_parse_domains(void)
113{
114	return 0;
115}
116#endif /* CONFIG_OF */
117
118static __init void exynos_pm_add_dev_to_genpd(struct platform_device *pdev,
119						struct exynos_pm_domain *pd)
120{
121	if (pdev->dev.bus) {
122		if (!pm_genpd_add_device(&pd->pd, &pdev->dev))
123			pm_genpd_dev_need_restore(&pdev->dev, true);
124		else
125			pr_info("%s: error in adding %s device to %s power"
126				"domain\n", __func__, dev_name(&pdev->dev),
127				pd->name);
128	}
129}
130
131EXYNOS_GPD(exynos4_pd_mfc, S5P_PMU_MFC_CONF, "pd-mfc");
132EXYNOS_GPD(exynos4_pd_g3d, S5P_PMU_G3D_CONF, "pd-g3d");
133EXYNOS_GPD(exynos4_pd_lcd0, S5P_PMU_LCD0_CONF, "pd-lcd0");
134EXYNOS_GPD(exynos4_pd_lcd1, S5P_PMU_LCD1_CONF, "pd-lcd1");
135EXYNOS_GPD(exynos4_pd_tv, S5P_PMU_TV_CONF, "pd-tv");
136EXYNOS_GPD(exynos4_pd_cam, S5P_PMU_CAM_CONF, "pd-cam");
137EXYNOS_GPD(exynos4_pd_gps, S5P_PMU_GPS_CONF, "pd-gps");
138
139static struct exynos_pm_domain *exynos4_pm_domains[] = {
140	&exynos4_pd_mfc,
141	&exynos4_pd_g3d,
142	&exynos4_pd_lcd0,
143	&exynos4_pd_lcd1,
144	&exynos4_pd_tv,
145	&exynos4_pd_cam,
146	&exynos4_pd_gps,
147};
148
149static __init int exynos4_pm_init_power_domain(void)
150{
151	int idx;
152
153	if (of_have_populated_dt())
154		return exynos_pm_dt_parse_domains();
155
156	for (idx = 0; idx < ARRAY_SIZE(exynos4_pm_domains); idx++) {
157		struct exynos_pm_domain *pd = exynos4_pm_domains[idx];
158		int on = __raw_readl(pd->base + 0x4) & S5P_INT_LOCAL_PWR_EN;
159
160		pm_genpd_init(&pd->pd, NULL, !on);
161	}
162
163#ifdef CONFIG_S5P_DEV_FIMD0
164	exynos_pm_add_dev_to_genpd(&s5p_device_fimd0, &exynos4_pd_lcd0);
165#endif
166#ifdef CONFIG_S5P_DEV_TV
167	exynos_pm_add_dev_to_genpd(&s5p_device_hdmi, &exynos4_pd_tv);
168	exynos_pm_add_dev_to_genpd(&s5p_device_mixer, &exynos4_pd_tv);
169#endif
170#ifdef CONFIG_S5P_DEV_MFC
171	exynos_pm_add_dev_to_genpd(&s5p_device_mfc, &exynos4_pd_mfc);
172#endif
173#ifdef CONFIG_S5P_DEV_FIMC0
174	exynos_pm_add_dev_to_genpd(&s5p_device_fimc0, &exynos4_pd_cam);
175#endif
176#ifdef CONFIG_S5P_DEV_FIMC1
177	exynos_pm_add_dev_to_genpd(&s5p_device_fimc1, &exynos4_pd_cam);
178#endif
179#ifdef CONFIG_S5P_DEV_FIMC2
180	exynos_pm_add_dev_to_genpd(&s5p_device_fimc2, &exynos4_pd_cam);
181#endif
182#ifdef CONFIG_S5P_DEV_FIMC3
183	exynos_pm_add_dev_to_genpd(&s5p_device_fimc3, &exynos4_pd_cam);
184#endif
185#ifdef CONFIG_S5P_DEV_CSIS0
186	exynos_pm_add_dev_to_genpd(&s5p_device_mipi_csis0, &exynos4_pd_cam);
187#endif
188#ifdef CONFIG_S5P_DEV_CSIS1
189	exynos_pm_add_dev_to_genpd(&s5p_device_mipi_csis1, &exynos4_pd_cam);
190#endif
191#ifdef CONFIG_S5P_DEV_G2D
192	exynos_pm_add_dev_to_genpd(&s5p_device_g2d, &exynos4_pd_lcd0);
193#endif
194#ifdef CONFIG_S5P_DEV_JPEG
195	exynos_pm_add_dev_to_genpd(&s5p_device_jpeg, &exynos4_pd_cam);
196#endif
197	return 0;
198}
199arch_initcall(exynos4_pm_init_power_domain);
200
201int __init exynos_pm_late_initcall(void)
202{
203	pm_genpd_poweroff_unused();
204	return 0;
205}