forked from luck/tmp_suning_uos_patched
Merge branch 'pm-domains'
* pm-domains: PM / Domains: Fix include for PM_GENERIC_DOMAINS=n case PM / Domains: Provide a dummy dev_gpd_data() when generic domains are not used PM / Domains: Run late/early device suspend callbacks at the right time ARM: EXYNOS: Hook up power domains to generic power domain infrastructure PM / Domains: Add OF support
This commit is contained in:
commit
48ed000316
@ -0,0 +1,21 @@
|
||||
* Samsung Exynos Power Domains
|
||||
|
||||
Exynos processors include support for multiple power domains which are used
|
||||
to gate power to one or more peripherals on the processor.
|
||||
|
||||
Required Properties:
|
||||
- compatiable: should be one of the following.
|
||||
* samsung,exynos4210-pd - for exynos4210 type power domain.
|
||||
- reg: physical base address of the controller and length of memory mapped
|
||||
region.
|
||||
|
||||
Optional Properties:
|
||||
- samsung,exynos4210-pd-off: Specifies that the power domain is in turned-off
|
||||
state during boot and remains to be turned-off until explicitly turned-on.
|
||||
|
||||
Example:
|
||||
|
||||
lcd0: power-domain-lcd0 {
|
||||
compatible = "samsung,exynos4210-pd";
|
||||
reg = <0x10023C00 0x10>;
|
||||
};
|
@ -34,6 +34,7 @@ config CPU_EXYNOS4210
|
||||
select ARM_CPU_SUSPEND if PM
|
||||
select S5P_PM if PM
|
||||
select S5P_SLEEP if PM
|
||||
select PM_GENERIC_DOMAINS
|
||||
help
|
||||
Enable EXYNOS4210 CPU support
|
||||
|
||||
@ -74,11 +75,6 @@ config EXYNOS4_SETUP_FIMD0
|
||||
help
|
||||
Common setup code for FIMD0.
|
||||
|
||||
config EXYNOS4_DEV_PD
|
||||
bool
|
||||
help
|
||||
Compile in platform device definitions for Power Domain
|
||||
|
||||
config EXYNOS4_DEV_SYSMMU
|
||||
bool
|
||||
help
|
||||
@ -195,7 +191,6 @@ config MACH_SMDKV310
|
||||
select EXYNOS4_DEV_AHCI
|
||||
select SAMSUNG_DEV_KEYPAD
|
||||
select EXYNOS4_DEV_DMA
|
||||
select EXYNOS4_DEV_PD
|
||||
select SAMSUNG_DEV_PWM
|
||||
select EXYNOS4_DEV_USB_OHCI
|
||||
select EXYNOS4_DEV_SYSMMU
|
||||
@ -243,7 +238,6 @@ config MACH_UNIVERSAL_C210
|
||||
select S5P_DEV_ONENAND
|
||||
select S5P_DEV_TV
|
||||
select EXYNOS4_DEV_DMA
|
||||
select EXYNOS4_DEV_PD
|
||||
select EXYNOS4_SETUP_FIMD0
|
||||
select EXYNOS4_SETUP_I2C1
|
||||
select EXYNOS4_SETUP_I2C3
|
||||
@ -277,7 +271,6 @@ config MACH_NURI
|
||||
select S5P_DEV_USB_EHCI
|
||||
select S5P_SETUP_MIPIPHY
|
||||
select EXYNOS4_DEV_DMA
|
||||
select EXYNOS4_DEV_PD
|
||||
select EXYNOS4_SETUP_FIMC
|
||||
select EXYNOS4_SETUP_FIMD0
|
||||
select EXYNOS4_SETUP_I2C1
|
||||
@ -310,7 +303,6 @@ config MACH_ORIGEN
|
||||
select SAMSUNG_DEV_BACKLIGHT
|
||||
select SAMSUNG_DEV_PWM
|
||||
select EXYNOS4_DEV_DMA
|
||||
select EXYNOS4_DEV_PD
|
||||
select EXYNOS4_DEV_USB_OHCI
|
||||
select EXYNOS4_SETUP_FIMD0
|
||||
select EXYNOS4_SETUP_SDHCI
|
||||
|
@ -17,6 +17,7 @@ obj-$(CONFIG_CPU_EXYNOS4210) += clock-exynos4210.o
|
||||
obj-$(CONFIG_SOC_EXYNOS4212) += clock-exynos4212.o
|
||||
|
||||
obj-$(CONFIG_PM) += pm.o
|
||||
obj-$(CONFIG_PM_GENERIC_DOMAINS) += pm_domains.o
|
||||
obj-$(CONFIG_CPU_IDLE) += cpuidle.o
|
||||
|
||||
obj-$(CONFIG_ARCH_EXYNOS4) += pmu.o
|
||||
@ -45,7 +46,6 @@ obj-$(CONFIG_MACH_EXYNOS4_DT) += mach-exynos4-dt.o
|
||||
|
||||
obj-$(CONFIG_ARCH_EXYNOS4) += dev-audio.o
|
||||
obj-$(CONFIG_EXYNOS4_DEV_AHCI) += dev-ahci.o
|
||||
obj-$(CONFIG_EXYNOS4_DEV_PD) += dev-pd.o
|
||||
obj-$(CONFIG_EXYNOS4_DEV_SYSMMU) += dev-sysmmu.o
|
||||
obj-$(CONFIG_EXYNOS4_DEV_DWMCI) += dev-dwmci.o
|
||||
obj-$(CONFIG_EXYNOS4_DEV_DMA) += dma.o
|
||||
|
@ -1,139 +0,0 @@
|
||||
/* linux/arch/arm/mach-exynos4/dev-pd.c
|
||||
*
|
||||
* Copyright (c) 2010-2011 Samsung Electronics Co., Ltd.
|
||||
* http://www.samsung.com
|
||||
*
|
||||
* EXYNOS4 - Power Domain support
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/io.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/delay.h>
|
||||
|
||||
#include <mach/regs-pmu.h>
|
||||
|
||||
#include <plat/pd.h>
|
||||
|
||||
static int exynos4_pd_enable(struct device *dev)
|
||||
{
|
||||
struct samsung_pd_info *pdata = dev->platform_data;
|
||||
u32 timeout;
|
||||
|
||||
__raw_writel(S5P_INT_LOCAL_PWR_EN, pdata->base);
|
||||
|
||||
/* Wait max 1ms */
|
||||
timeout = 10;
|
||||
while ((__raw_readl(pdata->base + 0x4) & S5P_INT_LOCAL_PWR_EN)
|
||||
!= S5P_INT_LOCAL_PWR_EN) {
|
||||
if (timeout == 0) {
|
||||
printk(KERN_ERR "Power domain %s enable failed.\n",
|
||||
dev_name(dev));
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
timeout--;
|
||||
udelay(100);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int exynos4_pd_disable(struct device *dev)
|
||||
{
|
||||
struct samsung_pd_info *pdata = dev->platform_data;
|
||||
u32 timeout;
|
||||
|
||||
__raw_writel(0, pdata->base);
|
||||
|
||||
/* Wait max 1ms */
|
||||
timeout = 10;
|
||||
while (__raw_readl(pdata->base + 0x4) & S5P_INT_LOCAL_PWR_EN) {
|
||||
if (timeout == 0) {
|
||||
printk(KERN_ERR "Power domain %s disable failed.\n",
|
||||
dev_name(dev));
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
timeout--;
|
||||
udelay(100);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct platform_device exynos4_device_pd[] = {
|
||||
{
|
||||
.name = "samsung-pd",
|
||||
.id = 0,
|
||||
.dev = {
|
||||
.platform_data = &(struct samsung_pd_info) {
|
||||
.enable = exynos4_pd_enable,
|
||||
.disable = exynos4_pd_disable,
|
||||
.base = S5P_PMU_MFC_CONF,
|
||||
},
|
||||
},
|
||||
}, {
|
||||
.name = "samsung-pd",
|
||||
.id = 1,
|
||||
.dev = {
|
||||
.platform_data = &(struct samsung_pd_info) {
|
||||
.enable = exynos4_pd_enable,
|
||||
.disable = exynos4_pd_disable,
|
||||
.base = S5P_PMU_G3D_CONF,
|
||||
},
|
||||
},
|
||||
}, {
|
||||
.name = "samsung-pd",
|
||||
.id = 2,
|
||||
.dev = {
|
||||
.platform_data = &(struct samsung_pd_info) {
|
||||
.enable = exynos4_pd_enable,
|
||||
.disable = exynos4_pd_disable,
|
||||
.base = S5P_PMU_LCD0_CONF,
|
||||
},
|
||||
},
|
||||
}, {
|
||||
.name = "samsung-pd",
|
||||
.id = 3,
|
||||
.dev = {
|
||||
.platform_data = &(struct samsung_pd_info) {
|
||||
.enable = exynos4_pd_enable,
|
||||
.disable = exynos4_pd_disable,
|
||||
.base = S5P_PMU_LCD1_CONF,
|
||||
},
|
||||
},
|
||||
}, {
|
||||
.name = "samsung-pd",
|
||||
.id = 4,
|
||||
.dev = {
|
||||
.platform_data = &(struct samsung_pd_info) {
|
||||
.enable = exynos4_pd_enable,
|
||||
.disable = exynos4_pd_disable,
|
||||
.base = S5P_PMU_TV_CONF,
|
||||
},
|
||||
},
|
||||
}, {
|
||||
.name = "samsung-pd",
|
||||
.id = 5,
|
||||
.dev = {
|
||||
.platform_data = &(struct samsung_pd_info) {
|
||||
.enable = exynos4_pd_enable,
|
||||
.disable = exynos4_pd_disable,
|
||||
.base = S5P_PMU_CAM_CONF,
|
||||
},
|
||||
},
|
||||
}, {
|
||||
.name = "samsung-pd",
|
||||
.id = 6,
|
||||
.dev = {
|
||||
.platform_data = &(struct samsung_pd_info) {
|
||||
.enable = exynos4_pd_enable,
|
||||
.disable = exynos4_pd_disable,
|
||||
.base = S5P_PMU_GPS_CONF,
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
@ -1263,9 +1263,6 @@ static struct platform_device *nuri_devices[] __initdata = {
|
||||
&s5p_device_mfc,
|
||||
&s5p_device_mfc_l,
|
||||
&s5p_device_mfc_r,
|
||||
&exynos4_device_pd[PD_MFC],
|
||||
&exynos4_device_pd[PD_LCD0],
|
||||
&exynos4_device_pd[PD_CAM],
|
||||
&s5p_device_fimc_md,
|
||||
|
||||
/* NURI Devices */
|
||||
@ -1315,14 +1312,6 @@ static void __init nuri_machine_init(void)
|
||||
|
||||
/* Last */
|
||||
platform_add_devices(nuri_devices, ARRAY_SIZE(nuri_devices));
|
||||
s5p_device_mfc.dev.parent = &exynos4_device_pd[PD_MFC].dev;
|
||||
s5p_device_fimd0.dev.parent = &exynos4_device_pd[PD_LCD0].dev;
|
||||
|
||||
s5p_device_fimc0.dev.parent = &exynos4_device_pd[PD_CAM].dev;
|
||||
s5p_device_fimc1.dev.parent = &exynos4_device_pd[PD_CAM].dev;
|
||||
s5p_device_fimc2.dev.parent = &exynos4_device_pd[PD_CAM].dev;
|
||||
s5p_device_fimc3.dev.parent = &exynos4_device_pd[PD_CAM].dev;
|
||||
s5p_device_mipi_csis0.dev.parent = &exynos4_device_pd[PD_CAM].dev;
|
||||
}
|
||||
|
||||
MACHINE_START(NURI, "NURI")
|
||||
|
@ -621,13 +621,6 @@ static struct platform_device *origen_devices[] __initdata = {
|
||||
&s5p_device_mfc_r,
|
||||
&s5p_device_mixer,
|
||||
&exynos4_device_ohci,
|
||||
&exynos4_device_pd[PD_LCD0],
|
||||
&exynos4_device_pd[PD_TV],
|
||||
&exynos4_device_pd[PD_G3D],
|
||||
&exynos4_device_pd[PD_LCD1],
|
||||
&exynos4_device_pd[PD_CAM],
|
||||
&exynos4_device_pd[PD_GPS],
|
||||
&exynos4_device_pd[PD_MFC],
|
||||
&origen_device_gpiokeys,
|
||||
&origen_lcd_hv070wsa,
|
||||
};
|
||||
@ -695,13 +688,6 @@ static void __init origen_machine_init(void)
|
||||
|
||||
platform_add_devices(origen_devices, ARRAY_SIZE(origen_devices));
|
||||
|
||||
s5p_device_fimd0.dev.parent = &exynos4_device_pd[PD_LCD0].dev;
|
||||
|
||||
s5p_device_hdmi.dev.parent = &exynos4_device_pd[PD_TV].dev;
|
||||
s5p_device_mixer.dev.parent = &exynos4_device_pd[PD_TV].dev;
|
||||
|
||||
s5p_device_mfc.dev.parent = &exynos4_device_pd[PD_MFC].dev;
|
||||
|
||||
samsung_bl_set(&origen_bl_gpio_info, &origen_bl_data);
|
||||
}
|
||||
|
||||
|
@ -277,13 +277,6 @@ static struct platform_device *smdkv310_devices[] __initdata = {
|
||||
&s5p_device_mfc,
|
||||
&s5p_device_mfc_l,
|
||||
&s5p_device_mfc_r,
|
||||
&exynos4_device_pd[PD_MFC],
|
||||
&exynos4_device_pd[PD_G3D],
|
||||
&exynos4_device_pd[PD_LCD0],
|
||||
&exynos4_device_pd[PD_LCD1],
|
||||
&exynos4_device_pd[PD_CAM],
|
||||
&exynos4_device_pd[PD_TV],
|
||||
&exynos4_device_pd[PD_GPS],
|
||||
&exynos4_device_spdif,
|
||||
&exynos4_device_sysmmu,
|
||||
&samsung_asoc_dma,
|
||||
@ -336,10 +329,6 @@ static void s5p_tv_setup(void)
|
||||
WARN_ON(gpio_request_one(EXYNOS4_GPX3(7), GPIOF_IN, "hpd-plug"));
|
||||
s3c_gpio_cfgpin(EXYNOS4_GPX3(7), S3C_GPIO_SFN(0x3));
|
||||
s3c_gpio_setpull(EXYNOS4_GPX3(7), S3C_GPIO_PULL_NONE);
|
||||
|
||||
/* setup dependencies between TV devices */
|
||||
s5p_device_hdmi.dev.parent = &exynos4_device_pd[PD_TV].dev;
|
||||
s5p_device_mixer.dev.parent = &exynos4_device_pd[PD_TV].dev;
|
||||
}
|
||||
|
||||
static void __init smdkv310_map_io(void)
|
||||
@ -379,7 +368,6 @@ static void __init smdkv310_machine_init(void)
|
||||
clk_xusbxti.rate = 24000000;
|
||||
|
||||
platform_add_devices(smdkv310_devices, ARRAY_SIZE(smdkv310_devices));
|
||||
s5p_device_mfc.dev.parent = &exynos4_device_pd[PD_MFC].dev;
|
||||
}
|
||||
|
||||
MACHINE_START(SMDKV310, "SMDKV310")
|
||||
|
@ -969,7 +969,6 @@ static struct platform_device *universal_devices[] __initdata = {
|
||||
&s3c_device_i2c5,
|
||||
&s5p_device_i2c_hdmiphy,
|
||||
&hdmi_fixed_voltage,
|
||||
&exynos4_device_pd[PD_TV],
|
||||
&s5p_device_hdmi,
|
||||
&s5p_device_sdo,
|
||||
&s5p_device_mixer,
|
||||
@ -982,9 +981,6 @@ static struct platform_device *universal_devices[] __initdata = {
|
||||
&s5p_device_mfc,
|
||||
&s5p_device_mfc_l,
|
||||
&s5p_device_mfc_r,
|
||||
&exynos4_device_pd[PD_MFC],
|
||||
&exynos4_device_pd[PD_LCD0],
|
||||
&exynos4_device_pd[PD_CAM],
|
||||
&cam_i_core_fixed_reg_dev,
|
||||
&cam_s_if_fixed_reg_dev,
|
||||
&s5p_device_fimc_md,
|
||||
@ -1003,10 +999,6 @@ void s5p_tv_setup(void)
|
||||
gpio_request_one(EXYNOS4_GPX3(7), GPIOF_IN, "hpd-plug");
|
||||
s3c_gpio_cfgpin(EXYNOS4_GPX3(7), S3C_GPIO_SFN(0x3));
|
||||
s3c_gpio_setpull(EXYNOS4_GPX3(7), S3C_GPIO_PULL_NONE);
|
||||
|
||||
/* setup dependencies between TV devices */
|
||||
s5p_device_hdmi.dev.parent = &exynos4_device_pd[PD_TV].dev;
|
||||
s5p_device_mixer.dev.parent = &exynos4_device_pd[PD_TV].dev;
|
||||
}
|
||||
|
||||
static void __init universal_reserve(void)
|
||||
@ -1040,15 +1032,6 @@ static void __init universal_machine_init(void)
|
||||
|
||||
/* Last */
|
||||
platform_add_devices(universal_devices, ARRAY_SIZE(universal_devices));
|
||||
|
||||
s5p_device_mfc.dev.parent = &exynos4_device_pd[PD_MFC].dev;
|
||||
s5p_device_fimd0.dev.parent = &exynos4_device_pd[PD_LCD0].dev;
|
||||
|
||||
s5p_device_fimc0.dev.parent = &exynos4_device_pd[PD_CAM].dev;
|
||||
s5p_device_fimc1.dev.parent = &exynos4_device_pd[PD_CAM].dev;
|
||||
s5p_device_fimc2.dev.parent = &exynos4_device_pd[PD_CAM].dev;
|
||||
s5p_device_fimc3.dev.parent = &exynos4_device_pd[PD_CAM].dev;
|
||||
s5p_device_mipi_csis0.dev.parent = &exynos4_device_pd[PD_CAM].dev;
|
||||
}
|
||||
|
||||
MACHINE_START(UNIVERSAL_C210, "UNIVERSAL_C210")
|
||||
|
195
arch/arm/mach-exynos/pm_domains.c
Normal file
195
arch/arm/mach-exynos/pm_domains.c
Normal file
@ -0,0 +1,195 @@
|
||||
/*
|
||||
* Exynos Generic power domain support.
|
||||
*
|
||||
* Copyright (c) 2012 Samsung Electronics Co., Ltd.
|
||||
* http://www.samsung.com
|
||||
*
|
||||
* Implementation of Exynos specific power domain control which is used in
|
||||
* conjunction with runtime-pm. Support for both device-tree and non-device-tree
|
||||
* based power domain support is included.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/io.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/pm_domain.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/of_address.h>
|
||||
|
||||
#include <mach/regs-pmu.h>
|
||||
#include <plat/devs.h>
|
||||
|
||||
/*
|
||||
* Exynos specific wrapper around the generic power domain
|
||||
*/
|
||||
struct exynos_pm_domain {
|
||||
void __iomem *base;
|
||||
char const *name;
|
||||
bool is_off;
|
||||
struct generic_pm_domain pd;
|
||||
};
|
||||
|
||||
static int exynos_pd_power(struct generic_pm_domain *domain, bool power_on)
|
||||
{
|
||||
struct exynos_pm_domain *pd;
|
||||
void __iomem *base;
|
||||
u32 timeout, pwr;
|
||||
char *op;
|
||||
|
||||
pd = container_of(domain, struct exynos_pm_domain, pd);
|
||||
base = pd->base;
|
||||
|
||||
pwr = power_on ? S5P_INT_LOCAL_PWR_EN : 0;
|
||||
__raw_writel(pwr, base);
|
||||
|
||||
/* Wait max 1ms */
|
||||
timeout = 10;
|
||||
|
||||
while ((__raw_readl(base + 0x4) & S5P_INT_LOCAL_PWR_EN) != pwr) {
|
||||
if (!timeout) {
|
||||
op = (power_on) ? "enable" : "disable";
|
||||
pr_err("Power domain %s %s failed\n", domain->name, op);
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
timeout--;
|
||||
cpu_relax();
|
||||
usleep_range(80, 100);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int exynos_pd_power_on(struct generic_pm_domain *domain)
|
||||
{
|
||||
return exynos_pd_power(domain, true);
|
||||
}
|
||||
|
||||
static int exynos_pd_power_off(struct generic_pm_domain *domain)
|
||||
{
|
||||
return exynos_pd_power(domain, false);
|
||||
}
|
||||
|
||||
#define EXYNOS_GPD(PD, BASE, NAME) \
|
||||
static struct exynos_pm_domain PD = { \
|
||||
.base = (void __iomem *)BASE, \
|
||||
.name = NAME, \
|
||||
.pd = { \
|
||||
.power_off = exynos_pd_power_off, \
|
||||
.power_on = exynos_pd_power_on, \
|
||||
}, \
|
||||
}
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
static __init int exynos_pm_dt_parse_domains(void)
|
||||
{
|
||||
struct device_node *np;
|
||||
|
||||
for_each_compatible_node(np, NULL, "samsung,exynos4210-pd") {
|
||||
struct exynos_pm_domain *pd;
|
||||
|
||||
pd = kzalloc(sizeof(*pd), GFP_KERNEL);
|
||||
if (!pd) {
|
||||
pr_err("%s: failed to allocate memory for domain\n",
|
||||
__func__);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
if (of_get_property(np, "samsung,exynos4210-pd-off", NULL))
|
||||
pd->is_off = true;
|
||||
pd->name = np->name;
|
||||
pd->base = of_iomap(np, 0);
|
||||
pd->pd.power_off = exynos_pd_power_off;
|
||||
pd->pd.power_on = exynos_pd_power_on;
|
||||
pd->pd.of_node = np;
|
||||
pm_genpd_init(&pd->pd, NULL, false);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
static __init int exynos_pm_dt_parse_domains(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
#endif /* CONFIG_OF */
|
||||
|
||||
static __init void exynos_pm_add_dev_to_genpd(struct platform_device *pdev,
|
||||
struct exynos_pm_domain *pd)
|
||||
{
|
||||
if (pdev->dev.bus) {
|
||||
if (pm_genpd_add_device(&pd->pd, &pdev->dev))
|
||||
pr_info("%s: error in adding %s device to %s power"
|
||||
"domain\n", __func__, dev_name(&pdev->dev),
|
||||
pd->name);
|
||||
}
|
||||
}
|
||||
|
||||
EXYNOS_GPD(exynos4_pd_mfc, S5P_PMU_MFC_CONF, "pd-mfc");
|
||||
EXYNOS_GPD(exynos4_pd_g3d, S5P_PMU_G3D_CONF, "pd-g3d");
|
||||
EXYNOS_GPD(exynos4_pd_lcd0, S5P_PMU_LCD0_CONF, "pd-lcd0");
|
||||
EXYNOS_GPD(exynos4_pd_lcd1, S5P_PMU_LCD1_CONF, "pd-lcd1");
|
||||
EXYNOS_GPD(exynos4_pd_tv, S5P_PMU_TV_CONF, "pd-tv");
|
||||
EXYNOS_GPD(exynos4_pd_cam, S5P_PMU_CAM_CONF, "pd-cam");
|
||||
EXYNOS_GPD(exynos4_pd_gps, S5P_PMU_GPS_CONF, "pd-gps");
|
||||
|
||||
static struct exynos_pm_domain *exynos4_pm_domains[] = {
|
||||
&exynos4_pd_mfc,
|
||||
&exynos4_pd_g3d,
|
||||
&exynos4_pd_lcd0,
|
||||
&exynos4_pd_lcd1,
|
||||
&exynos4_pd_tv,
|
||||
&exynos4_pd_cam,
|
||||
&exynos4_pd_gps,
|
||||
};
|
||||
|
||||
static __init int exynos4_pm_init_power_domain(void)
|
||||
{
|
||||
int idx;
|
||||
|
||||
if (of_have_populated_dt())
|
||||
return exynos_pm_dt_parse_domains();
|
||||
|
||||
for (idx = 0; idx < ARRAY_SIZE(exynos4_pm_domains); idx++)
|
||||
pm_genpd_init(&exynos4_pm_domains[idx]->pd, NULL,
|
||||
exynos4_pm_domains[idx]->is_off);
|
||||
|
||||
#ifdef CONFIG_S5P_DEV_FIMD0
|
||||
exynos_pm_add_dev_to_genpd(&s5p_device_fimd0, &exynos4_pd_lcd0);
|
||||
#endif
|
||||
#ifdef CONFIG_S5P_DEV_TV
|
||||
exynos_pm_add_dev_to_genpd(&s5p_device_hdmi, &exynos4_pd_tv);
|
||||
exynos_pm_add_dev_to_genpd(&s5p_device_mixer, &exynos4_pd_tv);
|
||||
#endif
|
||||
#ifdef CONFIG_S5P_DEV_MFC
|
||||
exynos_pm_add_dev_to_genpd(&s5p_device_mfc, &exynos4_pd_mfc);
|
||||
#endif
|
||||
#ifdef CONFIG_S5P_DEV_FIMC0
|
||||
exynos_pm_add_dev_to_genpd(&s5p_device_fimc0, &exynos4_pd_cam);
|
||||
#endif
|
||||
#ifdef CONFIG_S5P_DEV_FIMC1
|
||||
exynos_pm_add_dev_to_genpd(&s5p_device_fimc1, &exynos4_pd_cam);
|
||||
#endif
|
||||
#ifdef CONFIG_S5P_DEV_FIMC2
|
||||
exynos_pm_add_dev_to_genpd(&s5p_device_fimc2, &exynos4_pd_cam);
|
||||
#endif
|
||||
#ifdef CONFIG_S5P_DEV_FIMC3
|
||||
exynos_pm_add_dev_to_genpd(&s5p_device_fimc3, &exynos4_pd_cam);
|
||||
#endif
|
||||
#ifdef CONFIG_S5P_DEV_CSIS0
|
||||
exynos_pm_add_dev_to_genpd(&s5p_device_mipi_csis0, &exynos4_pd_cam);
|
||||
#endif
|
||||
#ifdef CONFIG_S5P_DEV_CSIS1
|
||||
exynos_pm_add_dev_to_genpd(&s5p_device_mipi_csis1, &exynos4_pd_cam);
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
arch_initcall(exynos4_pm_init_power_domain);
|
||||
|
||||
static __init int exynos_pm_late_initcall(void)
|
||||
{
|
||||
pm_genpd_poweroff_unused();
|
||||
return 0;
|
||||
}
|
||||
late_initcall(exynos_pm_late_initcall);
|
@ -820,17 +820,16 @@ static int pm_genpd_suspend(struct device *dev)
|
||||
}
|
||||
|
||||
/**
|
||||
* pm_genpd_suspend_noirq - Late suspend of a device from an I/O PM domain.
|
||||
* pm_genpd_suspend_late - Late suspend of a device from an I/O PM domain.
|
||||
* @dev: Device to suspend.
|
||||
*
|
||||
* Carry out a late suspend of a device under the assumption that its
|
||||
* pm_domain field points to the domain member of an object of type
|
||||
* struct generic_pm_domain representing a PM domain consisting of I/O devices.
|
||||
*/
|
||||
static int pm_genpd_suspend_noirq(struct device *dev)
|
||||
static int pm_genpd_suspend_late(struct device *dev)
|
||||
{
|
||||
struct generic_pm_domain *genpd;
|
||||
int ret;
|
||||
|
||||
dev_dbg(dev, "%s()\n", __func__);
|
||||
|
||||
@ -838,14 +837,28 @@ static int pm_genpd_suspend_noirq(struct device *dev)
|
||||
if (IS_ERR(genpd))
|
||||
return -EINVAL;
|
||||
|
||||
if (genpd->suspend_power_off)
|
||||
return 0;
|
||||
return genpd->suspend_power_off ? 0 : genpd_suspend_late(genpd, dev);
|
||||
}
|
||||
|
||||
ret = genpd_suspend_late(genpd, dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
/**
|
||||
* pm_genpd_suspend_noirq - Completion of suspend of device in an I/O PM domain.
|
||||
* @dev: Device to suspend.
|
||||
*
|
||||
* Stop the device and remove power from the domain if all devices in it have
|
||||
* been stopped.
|
||||
*/
|
||||
static int pm_genpd_suspend_noirq(struct device *dev)
|
||||
{
|
||||
struct generic_pm_domain *genpd;
|
||||
|
||||
if (dev->power.wakeup_path && genpd_dev_active_wakeup(genpd, dev))
|
||||
dev_dbg(dev, "%s()\n", __func__);
|
||||
|
||||
genpd = dev_to_genpd(dev);
|
||||
if (IS_ERR(genpd))
|
||||
return -EINVAL;
|
||||
|
||||
if (genpd->suspend_power_off
|
||||
|| (dev->power.wakeup_path && genpd_dev_active_wakeup(genpd, dev)))
|
||||
return 0;
|
||||
|
||||
genpd_stop_dev(genpd, dev);
|
||||
@ -862,13 +875,10 @@ static int pm_genpd_suspend_noirq(struct device *dev)
|
||||
}
|
||||
|
||||
/**
|
||||
* pm_genpd_resume_noirq - Early resume of a device from an I/O power domain.
|
||||
* pm_genpd_resume_noirq - Start of resume of device in an I/O PM domain.
|
||||
* @dev: Device to resume.
|
||||
*
|
||||
* Carry out an early resume of a device under the assumption that its
|
||||
* pm_domain field points to the domain member of an object of type
|
||||
* struct generic_pm_domain representing a power domain consisting of I/O
|
||||
* devices.
|
||||
* Restore power to the device's PM domain, if necessary, and start the device.
|
||||
*/
|
||||
static int pm_genpd_resume_noirq(struct device *dev)
|
||||
{
|
||||
@ -890,13 +900,34 @@ static int pm_genpd_resume_noirq(struct device *dev)
|
||||
*/
|
||||
pm_genpd_poweron(genpd);
|
||||
genpd->suspended_count--;
|
||||
genpd_start_dev(genpd, dev);
|
||||
|
||||
return genpd_resume_early(genpd, dev);
|
||||
return genpd_start_dev(genpd, dev);
|
||||
}
|
||||
|
||||
/**
|
||||
* pm_genpd_resume - Resume a device belonging to an I/O power domain.
|
||||
* pm_genpd_resume_early - Early resume of a device in an I/O PM domain.
|
||||
* @dev: Device to resume.
|
||||
*
|
||||
* Carry out an early resume of a device under the assumption that its
|
||||
* pm_domain field points to the domain member of an object of type
|
||||
* struct generic_pm_domain representing a power domain consisting of I/O
|
||||
* devices.
|
||||
*/
|
||||
static int pm_genpd_resume_early(struct device *dev)
|
||||
{
|
||||
struct generic_pm_domain *genpd;
|
||||
|
||||
dev_dbg(dev, "%s()\n", __func__);
|
||||
|
||||
genpd = dev_to_genpd(dev);
|
||||
if (IS_ERR(genpd))
|
||||
return -EINVAL;
|
||||
|
||||
return genpd->suspend_power_off ? 0 : genpd_resume_early(genpd, dev);
|
||||
}
|
||||
|
||||
/**
|
||||
* pm_genpd_resume - Resume of device in an I/O PM domain.
|
||||
* @dev: Device to resume.
|
||||
*
|
||||
* Resume a device under the assumption that its pm_domain field points to the
|
||||
@ -917,7 +948,7 @@ static int pm_genpd_resume(struct device *dev)
|
||||
}
|
||||
|
||||
/**
|
||||
* pm_genpd_freeze - Freeze a device belonging to an I/O power domain.
|
||||
* pm_genpd_freeze - Freezing a device in an I/O PM domain.
|
||||
* @dev: Device to freeze.
|
||||
*
|
||||
* Freeze a device under the assumption that its pm_domain field points to the
|
||||
@ -938,7 +969,29 @@ static int pm_genpd_freeze(struct device *dev)
|
||||
}
|
||||
|
||||
/**
|
||||
* pm_genpd_freeze_noirq - Late freeze of a device from an I/O power domain.
|
||||
* pm_genpd_freeze_late - Late freeze of a device in an I/O PM domain.
|
||||
* @dev: Device to freeze.
|
||||
*
|
||||
* Carry out a late freeze of a device under the assumption that its
|
||||
* pm_domain field points to the domain member of an object of type
|
||||
* struct generic_pm_domain representing a power domain consisting of I/O
|
||||
* devices.
|
||||
*/
|
||||
static int pm_genpd_freeze_late(struct device *dev)
|
||||
{
|
||||
struct generic_pm_domain *genpd;
|
||||
|
||||
dev_dbg(dev, "%s()\n", __func__);
|
||||
|
||||
genpd = dev_to_genpd(dev);
|
||||
if (IS_ERR(genpd))
|
||||
return -EINVAL;
|
||||
|
||||
return genpd->suspend_power_off ? 0 : genpd_freeze_late(genpd, dev);
|
||||
}
|
||||
|
||||
/**
|
||||
* pm_genpd_freeze_noirq - Completion of freezing a device in an I/O PM domain.
|
||||
* @dev: Device to freeze.
|
||||
*
|
||||
* Carry out a late freeze of a device under the assumption that its
|
||||
@ -949,7 +1002,6 @@ static int pm_genpd_freeze(struct device *dev)
|
||||
static int pm_genpd_freeze_noirq(struct device *dev)
|
||||
{
|
||||
struct generic_pm_domain *genpd;
|
||||
int ret;
|
||||
|
||||
dev_dbg(dev, "%s()\n", __func__);
|
||||
|
||||
@ -957,26 +1009,15 @@ static int pm_genpd_freeze_noirq(struct device *dev)
|
||||
if (IS_ERR(genpd))
|
||||
return -EINVAL;
|
||||
|
||||
if (genpd->suspend_power_off)
|
||||
return 0;
|
||||
|
||||
ret = genpd_freeze_late(genpd, dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
genpd_stop_dev(genpd, dev);
|
||||
|
||||
return 0;
|
||||
return genpd->suspend_power_off ? 0 : genpd_stop_dev(genpd, dev);
|
||||
}
|
||||
|
||||
/**
|
||||
* pm_genpd_thaw_noirq - Early thaw of a device from an I/O power domain.
|
||||
* pm_genpd_thaw_noirq - Early thaw of device in an I/O PM domain.
|
||||
* @dev: Device to thaw.
|
||||
*
|
||||
* Carry out an early thaw of a device under the assumption that its
|
||||
* pm_domain field points to the domain member of an object of type
|
||||
* struct generic_pm_domain representing a power domain consisting of I/O
|
||||
* devices.
|
||||
* Start the device, unless power has been removed from the domain already
|
||||
* before the system transition.
|
||||
*/
|
||||
static int pm_genpd_thaw_noirq(struct device *dev)
|
||||
{
|
||||
@ -988,12 +1029,29 @@ static int pm_genpd_thaw_noirq(struct device *dev)
|
||||
if (IS_ERR(genpd))
|
||||
return -EINVAL;
|
||||
|
||||
if (genpd->suspend_power_off)
|
||||
return 0;
|
||||
return genpd->suspend_power_off ? 0 : genpd_start_dev(genpd, dev);
|
||||
}
|
||||
|
||||
genpd_start_dev(genpd, dev);
|
||||
/**
|
||||
* pm_genpd_thaw_early - Early thaw of device in an I/O PM domain.
|
||||
* @dev: Device to thaw.
|
||||
*
|
||||
* Carry out an early thaw of a device under the assumption that its
|
||||
* pm_domain field points to the domain member of an object of type
|
||||
* struct generic_pm_domain representing a power domain consisting of I/O
|
||||
* devices.
|
||||
*/
|
||||
static int pm_genpd_thaw_early(struct device *dev)
|
||||
{
|
||||
struct generic_pm_domain *genpd;
|
||||
|
||||
return genpd_thaw_early(genpd, dev);
|
||||
dev_dbg(dev, "%s()\n", __func__);
|
||||
|
||||
genpd = dev_to_genpd(dev);
|
||||
if (IS_ERR(genpd))
|
||||
return -EINVAL;
|
||||
|
||||
return genpd->suspend_power_off ? 0 : genpd_thaw_early(genpd, dev);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1018,13 +1076,11 @@ static int pm_genpd_thaw(struct device *dev)
|
||||
}
|
||||
|
||||
/**
|
||||
* pm_genpd_restore_noirq - Early restore of a device from an I/O power domain.
|
||||
* pm_genpd_restore_noirq - Start of restore of device in an I/O PM domain.
|
||||
* @dev: Device to resume.
|
||||
*
|
||||
* Carry out an early restore of a device under the assumption that its
|
||||
* pm_domain field points to the domain member of an object of type
|
||||
* struct generic_pm_domain representing a power domain consisting of I/O
|
||||
* devices.
|
||||
* Make sure the domain will be in the same power state as before the
|
||||
* hibernation the system is resuming from and start the device if necessary.
|
||||
*/
|
||||
static int pm_genpd_restore_noirq(struct device *dev)
|
||||
{
|
||||
@ -1054,9 +1110,8 @@ static int pm_genpd_restore_noirq(struct device *dev)
|
||||
|
||||
pm_genpd_poweron(genpd);
|
||||
genpd->suspended_count--;
|
||||
genpd_start_dev(genpd, dev);
|
||||
|
||||
return genpd_resume_early(genpd, dev);
|
||||
return genpd_start_dev(genpd, dev);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1099,11 +1154,15 @@ static void pm_genpd_complete(struct device *dev)
|
||||
|
||||
#define pm_genpd_prepare NULL
|
||||
#define pm_genpd_suspend NULL
|
||||
#define pm_genpd_suspend_late NULL
|
||||
#define pm_genpd_suspend_noirq NULL
|
||||
#define pm_genpd_resume_early NULL
|
||||
#define pm_genpd_resume_noirq NULL
|
||||
#define pm_genpd_resume NULL
|
||||
#define pm_genpd_freeze NULL
|
||||
#define pm_genpd_freeze_late NULL
|
||||
#define pm_genpd_freeze_noirq NULL
|
||||
#define pm_genpd_thaw_early NULL
|
||||
#define pm_genpd_thaw_noirq NULL
|
||||
#define pm_genpd_thaw NULL
|
||||
#define pm_genpd_restore_noirq NULL
|
||||
@ -1170,6 +1229,38 @@ int __pm_genpd_add_device(struct generic_pm_domain *genpd, struct device *dev,
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* __pm_genpd_of_add_device - Add a device to an I/O PM domain.
|
||||
* @genpd_node: Device tree node pointer representing a PM domain to which the
|
||||
* the device is added to.
|
||||
* @dev: Device to be added.
|
||||
* @td: Set of PM QoS timing parameters to attach to the device.
|
||||
*/
|
||||
int __pm_genpd_of_add_device(struct device_node *genpd_node, struct device *dev,
|
||||
struct gpd_timing_data *td)
|
||||
{
|
||||
struct generic_pm_domain *genpd = NULL, *gpd;
|
||||
|
||||
dev_dbg(dev, "%s()\n", __func__);
|
||||
|
||||
if (IS_ERR_OR_NULL(genpd_node) || IS_ERR_OR_NULL(dev))
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&gpd_list_lock);
|
||||
list_for_each_entry(gpd, &gpd_list, gpd_list_node) {
|
||||
if (gpd->of_node == genpd_node) {
|
||||
genpd = gpd;
|
||||
break;
|
||||
}
|
||||
}
|
||||
mutex_unlock(&gpd_list_lock);
|
||||
|
||||
if (!genpd)
|
||||
return -EINVAL;
|
||||
|
||||
return __pm_genpd_add_device(genpd, dev, td);
|
||||
}
|
||||
|
||||
/**
|
||||
* pm_genpd_remove_device - Remove a device from an I/O PM domain.
|
||||
* @genpd: PM domain to remove the device from.
|
||||
@ -1450,7 +1541,7 @@ static int pm_genpd_default_suspend_late(struct device *dev)
|
||||
{
|
||||
int (*cb)(struct device *__dev) = dev_gpd_data(dev)->ops.suspend_late;
|
||||
|
||||
return cb ? cb(dev) : pm_generic_suspend_noirq(dev);
|
||||
return cb ? cb(dev) : pm_generic_suspend_late(dev);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1461,7 +1552,7 @@ static int pm_genpd_default_resume_early(struct device *dev)
|
||||
{
|
||||
int (*cb)(struct device *__dev) = dev_gpd_data(dev)->ops.resume_early;
|
||||
|
||||
return cb ? cb(dev) : pm_generic_resume_noirq(dev);
|
||||
return cb ? cb(dev) : pm_generic_resume_early(dev);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1494,7 +1585,7 @@ static int pm_genpd_default_freeze_late(struct device *dev)
|
||||
{
|
||||
int (*cb)(struct device *__dev) = dev_gpd_data(dev)->ops.freeze_late;
|
||||
|
||||
return cb ? cb(dev) : pm_generic_freeze_noirq(dev);
|
||||
return cb ? cb(dev) : pm_generic_freeze_late(dev);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1505,7 +1596,7 @@ static int pm_genpd_default_thaw_early(struct device *dev)
|
||||
{
|
||||
int (*cb)(struct device *__dev) = dev_gpd_data(dev)->ops.thaw_early;
|
||||
|
||||
return cb ? cb(dev) : pm_generic_thaw_noirq(dev);
|
||||
return cb ? cb(dev) : pm_generic_thaw_early(dev);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1564,16 +1655,22 @@ void pm_genpd_init(struct generic_pm_domain *genpd,
|
||||
genpd->domain.ops.runtime_idle = pm_generic_runtime_idle;
|
||||
genpd->domain.ops.prepare = pm_genpd_prepare;
|
||||
genpd->domain.ops.suspend = pm_genpd_suspend;
|
||||
genpd->domain.ops.suspend_late = pm_genpd_suspend_late;
|
||||
genpd->domain.ops.suspend_noirq = pm_genpd_suspend_noirq;
|
||||
genpd->domain.ops.resume_noirq = pm_genpd_resume_noirq;
|
||||
genpd->domain.ops.resume_early = pm_genpd_resume_early;
|
||||
genpd->domain.ops.resume = pm_genpd_resume;
|
||||
genpd->domain.ops.freeze = pm_genpd_freeze;
|
||||
genpd->domain.ops.freeze_late = pm_genpd_freeze_late;
|
||||
genpd->domain.ops.freeze_noirq = pm_genpd_freeze_noirq;
|
||||
genpd->domain.ops.thaw_noirq = pm_genpd_thaw_noirq;
|
||||
genpd->domain.ops.thaw_early = pm_genpd_thaw_early;
|
||||
genpd->domain.ops.thaw = pm_genpd_thaw;
|
||||
genpd->domain.ops.poweroff = pm_genpd_suspend;
|
||||
genpd->domain.ops.poweroff_late = pm_genpd_suspend_late;
|
||||
genpd->domain.ops.poweroff_noirq = pm_genpd_suspend_noirq;
|
||||
genpd->domain.ops.restore_noirq = pm_genpd_restore_noirq;
|
||||
genpd->domain.ops.restore_early = pm_genpd_resume_early;
|
||||
genpd->domain.ops.restore = pm_genpd_resume;
|
||||
genpd->domain.ops.complete = pm_genpd_complete;
|
||||
genpd->dev_ops.save_state = pm_genpd_default_save_state;
|
||||
|
@ -11,6 +11,7 @@
|
||||
|
||||
#include <linux/device.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/of.h>
|
||||
|
||||
enum gpd_status {
|
||||
GPD_STATE_ACTIVE = 0, /* PM domain is active */
|
||||
@ -70,6 +71,7 @@ struct generic_pm_domain {
|
||||
s64 break_even_ns; /* Power break even for the entire domain. */
|
||||
s64 max_off_time_ns; /* Maximum allowed "suspended" time. */
|
||||
ktime_t power_off_time;
|
||||
struct device_node *of_node; /* Node in device tree */
|
||||
};
|
||||
|
||||
static inline struct generic_pm_domain *pd_to_genpd(struct dev_pm_domain *pd)
|
||||
@ -99,12 +101,12 @@ struct generic_pm_domain_data {
|
||||
bool need_restore;
|
||||
};
|
||||
|
||||
#ifdef CONFIG_PM_GENERIC_DOMAINS
|
||||
static inline struct generic_pm_domain_data *to_gpd_data(struct pm_domain_data *pdd)
|
||||
{
|
||||
return container_of(pdd, struct generic_pm_domain_data, base);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_GENERIC_DOMAINS
|
||||
static inline struct generic_pm_domain_data *dev_gpd_data(struct device *dev)
|
||||
{
|
||||
return to_gpd_data(dev->power.subsys_data->domain_data);
|
||||
@ -117,12 +119,22 @@ extern int __pm_genpd_add_device(struct generic_pm_domain *genpd,
|
||||
struct device *dev,
|
||||
struct gpd_timing_data *td);
|
||||
|
||||
extern int __pm_genpd_of_add_device(struct device_node *genpd_node,
|
||||
struct device *dev,
|
||||
struct gpd_timing_data *td);
|
||||
|
||||
static inline int pm_genpd_add_device(struct generic_pm_domain *genpd,
|
||||
struct device *dev)
|
||||
{
|
||||
return __pm_genpd_add_device(genpd, dev, NULL);
|
||||
}
|
||||
|
||||
static inline int pm_genpd_of_add_device(struct device_node *genpd_node,
|
||||
struct device *dev)
|
||||
{
|
||||
return __pm_genpd_of_add_device(genpd_node, dev, NULL);
|
||||
}
|
||||
|
||||
extern int pm_genpd_remove_device(struct generic_pm_domain *genpd,
|
||||
struct device *dev);
|
||||
extern int pm_genpd_add_subdomain(struct generic_pm_domain *genpd,
|
||||
@ -143,6 +155,10 @@ extern bool default_stop_ok(struct device *dev);
|
||||
extern struct dev_power_governor pm_domain_always_on_gov;
|
||||
#else
|
||||
|
||||
static inline struct generic_pm_domain_data *dev_gpd_data(struct device *dev)
|
||||
{
|
||||
return ERR_PTR(-ENOSYS);
|
||||
}
|
||||
static inline struct generic_pm_domain *dev_to_genpd(struct device *dev)
|
||||
{
|
||||
return ERR_PTR(-ENOSYS);
|
||||
@ -183,7 +199,8 @@ static inline int __pm_genpd_remove_callbacks(struct device *dev, bool clear_td)
|
||||
{
|
||||
return -ENOSYS;
|
||||
}
|
||||
static inline void pm_genpd_init(struct generic_pm_domain *genpd, bool is_off)
|
||||
static inline void pm_genpd_init(struct generic_pm_domain *genpd,
|
||||
struct dev_power_governor *gov, bool is_off)
|
||||
{
|
||||
}
|
||||
static inline int pm_genpd_poweron(struct generic_pm_domain *genpd)
|
||||
@ -194,6 +211,7 @@ static inline bool default_stop_ok(struct device *dev)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
#define simple_qos_governor NULL
|
||||
#define pm_domain_always_on_gov NULL
|
||||
#endif
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user