2011-09-06 14:39:44 +08:00
/*
2013-03-21 10:58:06 -04:00
* Copyright 2011 - 2013 Freescale Semiconductor , Inc .
2011-09-06 14:39:44 +08:00
* Copyright 2011 Linaro Ltd .
*
* The code contained herein is licensed under the GNU General Public
* License . You may obtain a copy of the GNU General Public License
* Version 2 or later at the following locations :
*
* http : //www.opensource.org/licenses/gpl-license.html
* http : //www.gnu.org/copyleft/gpl.html
*/
2015-02-23 18:40:12 +01:00
# include <linux/clk.h>
# include <linux/delay.h>
2011-09-06 14:39:44 +08:00
# include <linux/io.h>
# include <linux/irq.h>
# include <linux/of.h>
# include <linux/of_address.h>
# include <linux/of_irq.h>
2015-02-23 18:40:12 +01:00
# include <linux/platform_device.h>
# include <linux/pm_domain.h>
# include <linux/regulator/consumer.h>
2012-12-27 13:10:24 -06:00
# include <linux/irqchip/arm-gic.h>
2013-03-25 09:20:42 -03:00
# include "common.h"
2015-02-23 18:40:12 +01:00
# include "hardware.h"
2011-09-06 14:39:44 +08:00
2015-02-23 18:40:12 +01:00
# define GPC_CNTR 0x000
2011-09-06 14:39:44 +08:00
# define GPC_IMR1 0x008
2015-02-23 18:40:12 +01:00
# define GPC_PGC_GPU_PDN 0x260
# define GPC_PGC_GPU_PUPSCR 0x264
# define GPC_PGC_GPU_PDNSCR 0x268
2011-09-06 14:39:44 +08:00
# define GPC_PGC_CPU_PDN 0x2a0
2014-12-17 12:24:12 +08:00
# define GPC_PGC_CPU_PUPSCR 0x2a4
# define GPC_PGC_CPU_PDNSCR 0x2a8
# define GPC_PGC_SW2ISO_SHIFT 0x8
# define GPC_PGC_SW_SHIFT 0x0
2011-09-06 14:39:44 +08:00
# define IMR_NUM 4
2015-02-23 17:45:18 +00:00
# define GPC_MAX_IRQS (IMR_NUM * 32)
2011-09-06 14:39:44 +08:00
2015-02-23 18:40:12 +01:00
# define GPU_VPU_PUP_REQ BIT(1)
# define GPU_VPU_PDN_REQ BIT(0)
# define GPC_CLK_MAX 6
struct pu_domain {
struct generic_pm_domain base ;
struct regulator * reg ;
struct clk * clk [ GPC_CLK_MAX ] ;
int num_clks ;
} ;
2011-09-06 14:39:44 +08:00
static void __iomem * gpc_base ;
static u32 gpc_wake_irqs [ IMR_NUM ] ;
static u32 gpc_saved_imrs [ IMR_NUM ] ;
2014-12-17 12:24:12 +08:00
void imx_gpc_set_arm_power_up_timing ( u32 sw2iso , u32 sw )
{
writel_relaxed ( ( sw2iso < < GPC_PGC_SW2ISO_SHIFT ) |
( sw < < GPC_PGC_SW_SHIFT ) , gpc_base + GPC_PGC_CPU_PUPSCR ) ;
}
void imx_gpc_set_arm_power_down_timing ( u32 sw2iso , u32 sw )
{
writel_relaxed ( ( sw2iso < < GPC_PGC_SW2ISO_SHIFT ) |
( sw < < GPC_PGC_SW_SHIFT ) , gpc_base + GPC_PGC_CPU_PDNSCR ) ;
}
void imx_gpc_set_arm_power_in_lpm ( bool power_off )
{
writel_relaxed ( power_off , gpc_base + GPC_PGC_CPU_PDN ) ;
}
2014-06-23 16:42:44 +08:00
void imx_gpc_pre_suspend ( bool arm_power_off )
2011-09-06 14:39:44 +08:00
{
void __iomem * reg_imr1 = gpc_base + GPC_IMR1 ;
int i ;
/* Tell GPC to power off ARM core when suspend */
2014-06-23 16:42:44 +08:00
if ( arm_power_off )
2014-12-17 12:24:12 +08:00
imx_gpc_set_arm_power_in_lpm ( arm_power_off ) ;
2011-09-06 14:39:44 +08:00
for ( i = 0 ; i < IMR_NUM ; i + + ) {
gpc_saved_imrs [ i ] = readl_relaxed ( reg_imr1 + i * 4 ) ;
writel_relaxed ( ~ gpc_wake_irqs [ i ] , reg_imr1 + i * 4 ) ;
}
}
void imx_gpc_post_resume ( void )
{
void __iomem * reg_imr1 = gpc_base + GPC_IMR1 ;
int i ;
/* Keep ARM core powered on for other low-power modes */
2014-12-17 12:24:12 +08:00
imx_gpc_set_arm_power_in_lpm ( false ) ;
2011-09-06 14:39:44 +08:00
for ( i = 0 ; i < IMR_NUM ; i + + )
writel_relaxed ( gpc_saved_imrs [ i ] , reg_imr1 + i * 4 ) ;
}
static int imx_gpc_irq_set_wake ( struct irq_data * d , unsigned int on )
{
2015-02-23 17:45:18 +00:00
unsigned int idx = d - > hwirq / 32 ;
2011-09-06 14:39:44 +08:00
u32 mask ;
2014-12-02 16:05:25 +00:00
mask = 1 < < d - > hwirq % 32 ;
2011-09-06 14:39:44 +08:00
gpc_wake_irqs [ idx ] = on ? gpc_wake_irqs [ idx ] | mask :
gpc_wake_irqs [ idx ] & ~ mask ;
2015-02-23 17:45:18 +00:00
/*
* Do * not * call into the parent , as the GIC doesn ' t have any
* wake - up facility . . .
*/
2011-09-06 14:39:44 +08:00
return 0 ;
}
2013-03-21 10:58:06 -04:00
void imx_gpc_mask_all ( void )
{
void __iomem * reg_imr1 = gpc_base + GPC_IMR1 ;
int i ;
for ( i = 0 ; i < IMR_NUM ; i + + ) {
gpc_saved_imrs [ i ] = readl_relaxed ( reg_imr1 + i * 4 ) ;
writel_relaxed ( ~ 0 , reg_imr1 + i * 4 ) ;
}
}
void imx_gpc_restore_all ( void )
{
void __iomem * reg_imr1 = gpc_base + GPC_IMR1 ;
int i ;
for ( i = 0 ; i < IMR_NUM ; i + + )
writel_relaxed ( gpc_saved_imrs [ i ] , reg_imr1 + i * 4 ) ;
}
2014-12-02 16:05:26 +00:00
void imx_gpc_hwirq_unmask ( unsigned int hwirq )
2011-09-06 14:39:44 +08:00
{
void __iomem * reg ;
u32 val ;
2015-02-23 17:45:18 +00:00
reg = gpc_base + GPC_IMR1 + hwirq / 32 * 4 ;
2011-09-06 14:39:44 +08:00
val = readl_relaxed ( reg ) ;
2014-12-02 16:05:26 +00:00
val & = ~ ( 1 < < hwirq % 32 ) ;
2011-09-06 14:39:44 +08:00
writel_relaxed ( val , reg ) ;
}
2014-12-02 16:05:26 +00:00
void imx_gpc_hwirq_mask ( unsigned int hwirq )
2011-09-06 14:39:44 +08:00
{
void __iomem * reg ;
u32 val ;
2015-02-23 17:45:18 +00:00
reg = gpc_base + GPC_IMR1 + hwirq / 32 * 4 ;
2014-12-02 16:05:26 +00:00
val = readl_relaxed ( reg ) ;
val | = 1 < < ( hwirq % 32 ) ;
writel_relaxed ( val , reg ) ;
}
static void imx_gpc_irq_unmask ( struct irq_data * d )
{
imx_gpc_hwirq_unmask ( d - > hwirq ) ;
2015-02-23 17:45:18 +00:00
irq_chip_unmask_parent ( d ) ;
2014-12-02 16:05:26 +00:00
}
static void imx_gpc_irq_mask ( struct irq_data * d )
{
imx_gpc_hwirq_mask ( d - > hwirq ) ;
2015-02-23 17:45:18 +00:00
irq_chip_mask_parent ( d ) ;
2011-09-06 14:39:44 +08:00
}
2015-02-23 17:45:18 +00:00
static struct irq_chip imx_gpc_chip = {
2015-03-12 08:40:37 +00:00
. name = " GPC " ,
. irq_eoi = irq_chip_eoi_parent ,
. irq_mask = imx_gpc_irq_mask ,
. irq_unmask = imx_gpc_irq_unmask ,
. irq_retrigger = irq_chip_retrigger_hierarchy ,
. irq_set_wake = imx_gpc_irq_set_wake ,
# ifdef CONFIG_SMP
. irq_set_affinity = irq_chip_set_affinity_parent ,
# endif
2015-02-23 17:45:18 +00:00
} ;
static int imx_gpc_domain_xlate ( struct irq_domain * domain ,
struct device_node * controller ,
const u32 * intspec ,
unsigned int intsize ,
unsigned long * out_hwirq ,
unsigned int * out_type )
2011-09-06 14:39:44 +08:00
{
2015-10-13 12:51:29 +01:00
if ( irq_domain_get_of_node ( domain ) ! = controller )
2015-02-23 17:45:18 +00:00
return - EINVAL ; /* Shouldn't happen, really... */
if ( intsize ! = 3 )
return - EINVAL ; /* Not GIC compliant */
if ( intspec [ 0 ] ! = 0 )
return - EINVAL ; /* No PPI should point to this domain */
* out_hwirq = intspec [ 1 ] ;
* out_type = intspec [ 2 ] ;
return 0 ;
}
static int imx_gpc_domain_alloc ( struct irq_domain * domain ,
unsigned int irq ,
unsigned int nr_irqs , void * data )
{
struct of_phandle_args * args = data ;
struct of_phandle_args parent_args ;
irq_hw_number_t hwirq ;
2012-12-04 22:55:13 +08:00
int i ;
2011-09-06 14:39:44 +08:00
2015-02-23 17:45:18 +00:00
if ( args - > args_count ! = 3 )
return - EINVAL ; /* Not GIC compliant */
if ( args - > args [ 0 ] ! = 0 )
return - EINVAL ; /* No PPI should point to this domain */
hwirq = args - > args [ 1 ] ;
if ( hwirq > = GPC_MAX_IRQS )
return - EINVAL ; /* Can't deal with this */
for ( i = 0 ; i < nr_irqs ; i + + )
irq_domain_set_hwirq_and_chip ( domain , irq + i , hwirq + i ,
& imx_gpc_chip , NULL ) ;
parent_args = * args ;
2015-10-13 12:51:29 +01:00
parent_args . np = irq_domain_get_of_node ( domain - > parent ) ;
2015-02-23 17:45:18 +00:00
return irq_domain_alloc_irqs_parent ( domain , irq , nr_irqs , & parent_args ) ;
}
2015-04-27 21:51:39 +09:00
static const struct irq_domain_ops imx_gpc_domain_ops = {
2015-02-23 17:45:18 +00:00
. xlate = imx_gpc_domain_xlate ,
. alloc = imx_gpc_domain_alloc ,
. free = irq_domain_free_irqs_common ,
} ;
static int __init imx_gpc_init ( struct device_node * node ,
struct device_node * parent )
{
struct irq_domain * parent_domain , * domain ;
int i ;
if ( ! parent ) {
pr_err ( " %s: no parent, giving up \n " , node - > full_name ) ;
return - ENODEV ;
}
parent_domain = irq_find_host ( parent ) ;
if ( ! parent_domain ) {
pr_err ( " %s: unable to obtain parent domain \n " , node - > full_name ) ;
return - ENXIO ;
}
gpc_base = of_iomap ( node , 0 ) ;
if ( WARN_ON ( ! gpc_base ) )
return - ENOMEM ;
domain = irq_domain_add_hierarchy ( parent_domain , 0 , GPC_MAX_IRQS ,
node , & imx_gpc_domain_ops ,
NULL ) ;
if ( ! domain ) {
iounmap ( gpc_base ) ;
return - ENOMEM ;
}
2011-09-06 14:39:44 +08:00
2012-12-04 22:55:13 +08:00
/* Initially mask all interrupts */
for ( i = 0 ; i < IMR_NUM ; i + + )
writel_relaxed ( ~ 0 , gpc_base + GPC_IMR1 + i * 4 ) ;
2015-02-23 17:45:18 +00:00
return 0 ;
2011-09-06 14:39:44 +08:00
}
2015-02-23 18:40:12 +01:00
2015-02-23 17:45:18 +00:00
/*
* We cannot use the IRQCHIP_DECLARE macro that lives in
* drivers / irqchip , so we ' re forced to roll our own . Not very nice .
*/
OF_DECLARE_2 ( irqchip , imx_gpc , " fsl,imx6q-gpc " , imx_gpc_init ) ;
2015-03-13 16:05:37 +00:00
void __init imx_gpc_check_dt ( void )
{
struct device_node * np ;
np = of_find_compatible_node ( NULL , NULL , " fsl,imx6q-gpc " ) ;
2015-05-26 18:43:36 +02:00
if ( WARN_ON ( ! np ) )
return ;
if ( WARN_ON ( ! of_find_property ( np , " interrupt-controller " , NULL ) ) ) {
pr_warn ( " Outdated DT detected, suspend/resume will NOT work \n " ) ;
/* map GPC, so that at least CPUidle and WARs keep working */
gpc_base = of_iomap ( np , 0 ) ;
}
2015-03-13 16:05:37 +00:00
}
2015-02-23 18:40:12 +01:00
static void _imx6q_pm_pu_power_off ( struct generic_pm_domain * genpd )
{
int iso , iso2sw ;
u32 val ;
/* Read ISO and ISO2SW power down delays */
val = readl_relaxed ( gpc_base + GPC_PGC_GPU_PDNSCR ) ;
iso = val & 0x3f ;
iso2sw = ( val > > 8 ) & 0x3f ;
/* Gate off PU domain when GPU/VPU when powered down */
writel_relaxed ( 0x1 , gpc_base + GPC_PGC_GPU_PDN ) ;
/* Request GPC to power down GPU/VPU */
val = readl_relaxed ( gpc_base + GPC_CNTR ) ;
val | = GPU_VPU_PDN_REQ ;
writel_relaxed ( val , gpc_base + GPC_CNTR ) ;
/* Wait ISO + ISO2SW IPG clock cycles */
ndelay ( ( iso + iso2sw ) * 1000 / 66 ) ;
}
static int imx6q_pm_pu_power_off ( struct generic_pm_domain * genpd )
{
struct pu_domain * pu = container_of ( genpd , struct pu_domain , base ) ;
_imx6q_pm_pu_power_off ( genpd ) ;
if ( pu - > reg )
regulator_disable ( pu - > reg ) ;
return 0 ;
}
static int imx6q_pm_pu_power_on ( struct generic_pm_domain * genpd )
{
struct pu_domain * pu = container_of ( genpd , struct pu_domain , base ) ;
int i , ret , sw , sw2iso ;
u32 val ;
if ( pu - > reg )
ret = regulator_enable ( pu - > reg ) ;
if ( pu - > reg & & ret ) {
pr_err ( " %s: failed to enable regulator: %d \n " , __func__ , ret ) ;
return ret ;
}
/* Enable reset clocks for all devices in the PU domain */
for ( i = 0 ; i < pu - > num_clks ; i + + )
clk_prepare_enable ( pu - > clk [ i ] ) ;
/* Gate off PU domain when GPU/VPU when powered down */
writel_relaxed ( 0x1 , gpc_base + GPC_PGC_GPU_PDN ) ;
/* Read ISO and ISO2SW power down delays */
val = readl_relaxed ( gpc_base + GPC_PGC_GPU_PUPSCR ) ;
sw = val & 0x3f ;
sw2iso = ( val > > 8 ) & 0x3f ;
/* Request GPC to power up GPU/VPU */
val = readl_relaxed ( gpc_base + GPC_CNTR ) ;
val | = GPU_VPU_PUP_REQ ;
writel_relaxed ( val , gpc_base + GPC_CNTR ) ;
/* Wait ISO + ISO2SW IPG clock cycles */
ndelay ( ( sw + sw2iso ) * 1000 / 66 ) ;
/* Disable reset clocks for all devices in the PU domain */
for ( i = 0 ; i < pu - > num_clks ; i + + )
clk_disable_unprepare ( pu - > clk [ i ] ) ;
return 0 ;
}
static struct generic_pm_domain imx6q_arm_domain = {
. name = " ARM " ,
} ;
static struct pu_domain imx6q_pu_domain = {
. base = {
. name = " PU " ,
. power_off = imx6q_pm_pu_power_off ,
. power_on = imx6q_pm_pu_power_on ,
. power_off_latency_ns = 25000 ,
. power_on_latency_ns = 2000000 ,
} ,
} ;
static struct generic_pm_domain imx6sl_display_domain = {
. name = " DISPLAY " ,
} ;
static struct generic_pm_domain * imx_gpc_domains [ ] = {
& imx6q_arm_domain ,
& imx6q_pu_domain . base ,
& imx6sl_display_domain ,
} ;
static struct genpd_onecell_data imx_gpc_onecell_data = {
. domains = imx_gpc_domains ,
. num_domains = ARRAY_SIZE ( imx_gpc_domains ) ,
} ;
static int imx_gpc_genpd_init ( struct device * dev , struct regulator * pu_reg )
{
struct clk * clk ;
int i ;
imx6q_pu_domain . reg = pu_reg ;
for ( i = 0 ; ; i + + ) {
clk = of_clk_get ( dev - > of_node , i ) ;
if ( IS_ERR ( clk ) )
break ;
if ( i > = GPC_CLK_MAX ) {
dev_err ( dev , " more than %d clocks \n " , GPC_CLK_MAX ) ;
goto clk_err ;
}
imx6q_pu_domain . clk [ i ] = clk ;
}
imx6q_pu_domain . num_clks = i ;
2015-06-30 14:48:24 +02:00
/* Enable power always in case bootloader disabled it. */
imx6q_pm_pu_power_on ( & imx6q_pu_domain . base ) ;
if ( ! IS_ENABLED ( CONFIG_PM_GENERIC_DOMAINS ) )
return 0 ;
2015-02-23 18:40:12 +01:00
2015-06-30 14:48:24 +02:00
pm_genpd_init ( & imx6q_pu_domain . base , NULL , false ) ;
2015-02-23 18:40:12 +01:00
return of_genpd_add_provider_onecell ( dev - > of_node ,
& imx_gpc_onecell_data ) ;
clk_err :
while ( i - - )
clk_put ( imx6q_pu_domain . clk [ i ] ) ;
return - EINVAL ;
}
static int imx_gpc_probe ( struct platform_device * pdev )
{
struct regulator * pu_reg ;
int ret ;
2015-05-27 10:10:26 +02:00
/* bail out if DT too old and doesn't provide the necessary info */
if ( ! of_property_read_bool ( pdev - > dev . of_node , " #power-domain-cells " ) )
return 0 ;
2015-02-23 18:40:12 +01:00
pu_reg = devm_regulator_get_optional ( & pdev - > dev , " pu " ) ;
if ( PTR_ERR ( pu_reg ) = = - ENODEV )
pu_reg = NULL ;
if ( IS_ERR ( pu_reg ) ) {
ret = PTR_ERR ( pu_reg ) ;
dev_err ( & pdev - > dev , " failed to get pu regulator: %d \n " , ret ) ;
return ret ;
}
return imx_gpc_genpd_init ( & pdev - > dev , pu_reg ) ;
}
static const struct of_device_id imx_gpc_dt_ids [ ] = {
{ . compatible = " fsl,imx6q-gpc " } ,
{ . compatible = " fsl,imx6sl-gpc " } ,
{ }
} ;
static struct platform_driver imx_gpc_driver = {
. driver = {
. name = " imx-gpc " ,
. of_match_table = imx_gpc_dt_ids ,
} ,
. probe = imx_gpc_probe ,
} ;
static int __init imx_pgc_init ( void )
{
return platform_driver_register ( & imx_gpc_driver ) ;
}
subsys_initcall ( imx_pgc_init ) ;