2018-09-18 14:48:14 -03:00
// SPDX-License-Identifier: GPL-2.0+
2017-03-28 08:19:45 -07:00
/*
* Copyright 2017 Impinj , Inc
* Author : Andrey Smirnov < andrew . smirnov @ gmail . com >
*
* Based on the code of analogus driver :
*
* Copyright 2015 - 2017 Pengutronix , Lucas Stach < kernel @ pengutronix . de >
*/
2018-12-17 16:31:52 +01:00
# include <linux/clk.h>
2018-08-28 16:36:46 +08:00
# include <linux/of_device.h>
2017-03-28 08:19:45 -07:00
# include <linux/platform_device.h>
# include <linux/pm_domain.h>
# include <linux/regmap.h>
# include <linux/regulator/consumer.h>
# include <dt-bindings/power/imx7-power.h>
2018-11-16 16:49:27 +01:00
# include <dt-bindings/power/imx8mq-power.h>
2017-03-28 08:19:45 -07:00
2018-08-28 16:36:45 +08:00
# define GPC_LPCR_A_CORE_BSC 0x000
2017-03-28 08:19:45 -07:00
# define GPC_PGC_CPU_MAPPING 0x0ec
2018-11-16 16:49:27 +01:00
2018-11-16 16:49:25 +01:00
# define IMX7_USB_HSIC_PHY_A_CORE_DOMAIN BIT(6)
# define IMX7_USB_OTG2_PHY_A_CORE_DOMAIN BIT(5)
# define IMX7_USB_OTG1_PHY_A_CORE_DOMAIN BIT(4)
# define IMX7_PCIE_PHY_A_CORE_DOMAIN BIT(3)
# define IMX7_MIPI_PHY_A_CORE_DOMAIN BIT(2)
2017-03-28 08:19:45 -07:00
2018-11-16 16:49:27 +01:00
# define IMX8M_PCIE2_A53_DOMAIN BIT(15)
# define IMX8M_MIPI_CSI2_A53_DOMAIN BIT(14)
# define IMX8M_MIPI_CSI1_A53_DOMAIN BIT(13)
# define IMX8M_DISP_A53_DOMAIN BIT(12)
# define IMX8M_HDMI_A53_DOMAIN BIT(11)
# define IMX8M_VPU_A53_DOMAIN BIT(10)
# define IMX8M_GPU_A53_DOMAIN BIT(9)
# define IMX8M_DDR2_A53_DOMAIN BIT(8)
# define IMX8M_DDR1_A53_DOMAIN BIT(7)
# define IMX8M_OTG2_A53_DOMAIN BIT(5)
# define IMX8M_OTG1_A53_DOMAIN BIT(4)
# define IMX8M_PCIE1_A53_DOMAIN BIT(3)
# define IMX8M_MIPI_A53_DOMAIN BIT(2)
2017-03-28 08:19:45 -07:00
# define GPC_PU_PGC_SW_PUP_REQ 0x0f8
# define GPC_PU_PGC_SW_PDN_REQ 0x104
2018-11-16 16:49:27 +01:00
2018-11-16 16:49:25 +01:00
# define IMX7_USB_HSIC_PHY_SW_Pxx_REQ BIT(4)
# define IMX7_USB_OTG2_PHY_SW_Pxx_REQ BIT(3)
# define IMX7_USB_OTG1_PHY_SW_Pxx_REQ BIT(2)
# define IMX7_PCIE_PHY_SW_Pxx_REQ BIT(1)
# define IMX7_MIPI_PHY_SW_Pxx_REQ BIT(0)
2017-03-28 08:19:45 -07:00
2018-11-16 16:49:27 +01:00
# define IMX8M_PCIE2_SW_Pxx_REQ BIT(13)
# define IMX8M_MIPI_CSI2_SW_Pxx_REQ BIT(12)
# define IMX8M_MIPI_CSI1_SW_Pxx_REQ BIT(11)
# define IMX8M_DISP_SW_Pxx_REQ BIT(10)
# define IMX8M_HDMI_SW_Pxx_REQ BIT(9)
# define IMX8M_VPU_SW_Pxx_REQ BIT(8)
# define IMX8M_GPU_SW_Pxx_REQ BIT(7)
# define IMX8M_DDR2_SW_Pxx_REQ BIT(6)
# define IMX8M_DDR1_SW_Pxx_REQ BIT(5)
# define IMX8M_OTG2_SW_Pxx_REQ BIT(3)
# define IMX8M_OTG1_SW_Pxx_REQ BIT(2)
# define IMX8M_PCIE1_SW_Pxx_REQ BIT(1)
# define IMX8M_MIPI_SW_Pxx_REQ BIT(0)
2017-03-28 08:19:45 -07:00
# define GPC_M4_PU_PDN_FLG 0x1bc
2018-12-17 16:31:51 +01:00
# define GPC_PU_PWRHSK 0x1fc
# define IMX8M_GPU_HSK_PWRDNREQN BIT(6)
# define IMX8M_VPU_HSK_PWRDNREQN BIT(5)
# define IMX8M_DISP_HSK_PWRDNREQN BIT(4)
2018-05-30 09:30:42 +08:00
/*
* The PGC offset values in Reference Manual
* ( Rev . 1 , 01 / 2018 and the older ones ) GPC chapter ' s
* GPC_PGC memory map are incorrect , below offset
* values are from design RTL .
*/
2018-11-16 16:49:25 +01:00
# define IMX7_PGC_MIPI 16
# define IMX7_PGC_PCIE 17
# define IMX7_PGC_USB_HSIC 20
2018-11-16 16:49:27 +01:00
# define IMX8M_PGC_MIPI 16
# define IMX8M_PGC_PCIE1 17
# define IMX8M_PGC_OTG1 18
# define IMX8M_PGC_OTG2 19
# define IMX8M_PGC_DDR1 21
# define IMX8M_PGC_GPU 23
# define IMX8M_PGC_VPU 24
# define IMX8M_PGC_DISP 26
# define IMX8M_PGC_MIPI_CSI1 27
# define IMX8M_PGC_MIPI_CSI2 28
# define IMX8M_PGC_PCIE2 29
2017-03-28 08:19:45 -07:00
# define GPC_PGC_CTRL(n) (0x800 + (n) * 0x40)
# define GPC_PGC_SR(n) (GPC_PGC_CTRL(n) + 0xc)
# define GPC_PGC_CTRL_PCR BIT(0)
2018-12-17 16:31:52 +01:00
# define GPC_CLK_MAX 6
2018-08-28 16:36:46 +08:00
struct imx_pgc_domain {
2017-03-28 08:19:45 -07:00
struct generic_pm_domain genpd ;
struct regmap * regmap ;
struct regulator * regulator ;
2018-12-17 16:31:52 +01:00
struct clk * clk [ GPC_CLK_MAX ] ;
int num_clks ;
2017-03-28 08:19:45 -07:00
unsigned int pgc ;
const struct {
u32 pxx ;
u32 map ;
2018-12-17 16:31:51 +01:00
u32 hsk ;
2017-03-28 08:19:45 -07:00
} bits ;
const int voltage ;
struct device * dev ;
} ;
2018-08-28 16:36:46 +08:00
struct imx_pgc_domain_data {
const struct imx_pgc_domain * domains ;
size_t domains_num ;
2018-11-16 16:49:26 +01:00
const struct regmap_access_table * reg_access_table ;
2018-08-28 16:36:46 +08:00
} ;
static int imx_gpc_pu_pgc_sw_pxx_req ( struct generic_pm_domain * genpd ,
2017-03-28 08:19:45 -07:00
bool on )
{
2018-08-28 16:36:46 +08:00
struct imx_pgc_domain * domain = container_of ( genpd ,
struct imx_pgc_domain ,
2017-03-28 08:19:45 -07:00
genpd ) ;
unsigned int offset = on ?
GPC_PU_PGC_SW_PUP_REQ : GPC_PU_PGC_SW_PDN_REQ ;
const bool enable_power_control = ! on ;
const bool has_regulator = ! IS_ERR ( domain - > regulator ) ;
unsigned long deadline ;
2018-12-17 16:31:52 +01:00
int i , ret = 0 ;
2017-03-28 08:19:45 -07:00
regmap_update_bits ( domain - > regmap , GPC_PGC_CPU_MAPPING ,
domain - > bits . map , domain - > bits . map ) ;
if ( has_regulator & & on ) {
ret = regulator_enable ( domain - > regulator ) ;
if ( ret ) {
dev_err ( domain - > dev , " failed to enable regulator \n " ) ;
goto unmap ;
}
}
2018-12-17 16:31:52 +01:00
/* Enable reset clocks for all devices in the domain */
for ( i = 0 ; i < domain - > num_clks ; i + + )
clk_prepare_enable ( domain - > clk [ i ] ) ;
2017-03-28 08:19:45 -07:00
if ( enable_power_control )
regmap_update_bits ( domain - > regmap , GPC_PGC_CTRL ( domain - > pgc ) ,
GPC_PGC_CTRL_PCR , GPC_PGC_CTRL_PCR ) ;
2018-12-17 16:31:51 +01:00
if ( domain - > bits . hsk )
regmap_update_bits ( domain - > regmap , GPC_PU_PWRHSK ,
domain - > bits . hsk , on ? domain - > bits . hsk : 0 ) ;
2017-03-28 08:19:45 -07:00
regmap_update_bits ( domain - > regmap , offset ,
domain - > bits . pxx , domain - > bits . pxx ) ;
/*
* As per " 5.5.9.4 Example Code 4 " in IMX7DRM . pdf wait
* for PUP_REQ / PDN_REQ bit to be cleared
*/
deadline = jiffies + msecs_to_jiffies ( 1 ) ;
while ( true ) {
u32 pxx_req ;
regmap_read ( domain - > regmap , offset , & pxx_req ) ;
if ( ! ( pxx_req & domain - > bits . pxx ) )
break ;
if ( time_after ( jiffies , deadline ) ) {
dev_err ( domain - > dev , " falied to command PGC \n " ) ;
ret = - ETIMEDOUT ;
/*
* If we were in a process of enabling a
* domain and failed we might as well disable
* the regulator we just enabled . And if it
* was the opposite situation and we failed to
* power down - - keep the regulator on
*/
on = ! on ;
break ;
}
cpu_relax ( ) ;
}
if ( enable_power_control )
regmap_update_bits ( domain - > regmap , GPC_PGC_CTRL ( domain - > pgc ) ,
GPC_PGC_CTRL_PCR , 0 ) ;
2018-12-17 16:31:52 +01:00
/* Disable reset clocks for all devices in the domain */
for ( i = 0 ; i < domain - > num_clks ; i + + )
clk_disable_unprepare ( domain - > clk [ i ] ) ;
2017-03-28 08:19:45 -07:00
if ( has_regulator & & ! on ) {
int err ;
err = regulator_disable ( domain - > regulator ) ;
if ( err )
dev_err ( domain - > dev ,
" failed to disable regulator: %d \n " , ret ) ;
/* Preserve earlier error code */
ret = ret ? : err ;
}
unmap :
regmap_update_bits ( domain - > regmap , GPC_PGC_CPU_MAPPING ,
domain - > bits . map , 0 ) ;
return ret ;
}
2018-08-28 16:36:46 +08:00
static int imx_gpc_pu_pgc_sw_pup_req ( struct generic_pm_domain * genpd )
2017-03-28 08:19:45 -07:00
{
2018-08-28 16:36:46 +08:00
return imx_gpc_pu_pgc_sw_pxx_req ( genpd , true ) ;
2017-03-28 08:19:45 -07:00
}
2018-08-28 16:36:46 +08:00
static int imx_gpc_pu_pgc_sw_pdn_req ( struct generic_pm_domain * genpd )
2017-03-28 08:19:45 -07:00
{
2018-08-28 16:36:46 +08:00
return imx_gpc_pu_pgc_sw_pxx_req ( genpd , false ) ;
2017-03-28 08:19:45 -07:00
}
2018-08-28 16:36:46 +08:00
static const struct imx_pgc_domain imx7_pgc_domains [ ] = {
2017-03-28 08:19:45 -07:00
[ IMX7_POWER_DOMAIN_MIPI_PHY ] = {
. genpd = {
. name = " mipi-phy " ,
} ,
. bits = {
2018-11-16 16:49:25 +01:00
. pxx = IMX7_MIPI_PHY_SW_Pxx_REQ ,
. map = IMX7_MIPI_PHY_A_CORE_DOMAIN ,
2017-03-28 08:19:45 -07:00
} ,
. voltage = 1000000 ,
2018-11-16 16:49:25 +01:00
. pgc = IMX7_PGC_MIPI ,
2017-03-28 08:19:45 -07:00
} ,
[ IMX7_POWER_DOMAIN_PCIE_PHY ] = {
. genpd = {
. name = " pcie-phy " ,
} ,
. bits = {
2018-11-16 16:49:25 +01:00
. pxx = IMX7_PCIE_PHY_SW_Pxx_REQ ,
. map = IMX7_PCIE_PHY_A_CORE_DOMAIN ,
2017-03-28 08:19:45 -07:00
} ,
. voltage = 1000000 ,
2018-11-16 16:49:25 +01:00
. pgc = IMX7_PGC_PCIE ,
2017-03-28 08:19:45 -07:00
} ,
[ IMX7_POWER_DOMAIN_USB_HSIC_PHY ] = {
. genpd = {
. name = " usb-hsic-phy " ,
} ,
. bits = {
2018-11-16 16:49:25 +01:00
. pxx = IMX7_USB_HSIC_PHY_SW_Pxx_REQ ,
. map = IMX7_USB_HSIC_PHY_A_CORE_DOMAIN ,
2017-03-28 08:19:45 -07:00
} ,
. voltage = 1200000 ,
2018-11-16 16:49:25 +01:00
. pgc = IMX7_PGC_USB_HSIC ,
2017-03-28 08:19:45 -07:00
} ,
} ;
2018-11-16 16:49:26 +01:00
static const struct regmap_range imx7_yes_ranges [ ] = {
regmap_reg_range ( GPC_LPCR_A_CORE_BSC ,
GPC_M4_PU_PDN_FLG ) ,
regmap_reg_range ( GPC_PGC_CTRL ( IMX7_PGC_MIPI ) ,
GPC_PGC_SR ( IMX7_PGC_MIPI ) ) ,
regmap_reg_range ( GPC_PGC_CTRL ( IMX7_PGC_PCIE ) ,
GPC_PGC_SR ( IMX7_PGC_PCIE ) ) ,
regmap_reg_range ( GPC_PGC_CTRL ( IMX7_PGC_USB_HSIC ) ,
GPC_PGC_SR ( IMX7_PGC_USB_HSIC ) ) ,
} ;
static const struct regmap_access_table imx7_access_table = {
. yes_ranges = imx7_yes_ranges ,
. n_yes_ranges = ARRAY_SIZE ( imx7_yes_ranges ) ,
} ;
2018-08-28 16:36:46 +08:00
static const struct imx_pgc_domain_data imx7_pgc_domain_data = {
. domains = imx7_pgc_domains ,
. domains_num = ARRAY_SIZE ( imx7_pgc_domains ) ,
2018-11-16 16:49:26 +01:00
. reg_access_table = & imx7_access_table ,
2018-08-28 16:36:46 +08:00
} ;
2018-11-16 16:49:27 +01:00
static const struct imx_pgc_domain imx8m_pgc_domains [ ] = {
[ IMX8M_POWER_DOMAIN_MIPI ] = {
. genpd = {
. name = " mipi " ,
} ,
. bits = {
. pxx = IMX8M_MIPI_SW_Pxx_REQ ,
. map = IMX8M_MIPI_A53_DOMAIN ,
} ,
. pgc = IMX8M_PGC_MIPI ,
} ,
[ IMX8M_POWER_DOMAIN_PCIE1 ] = {
. genpd = {
. name = " pcie1 " ,
} ,
. bits = {
. pxx = IMX8M_PCIE1_SW_Pxx_REQ ,
. map = IMX8M_PCIE1_A53_DOMAIN ,
} ,
. pgc = IMX8M_PGC_PCIE1 ,
} ,
[ IMX8M_POWER_DOMAIN_USB_OTG1 ] = {
. genpd = {
. name = " usb-otg1 " ,
} ,
. bits = {
. pxx = IMX8M_OTG1_SW_Pxx_REQ ,
. map = IMX8M_OTG1_A53_DOMAIN ,
} ,
. pgc = IMX8M_PGC_OTG1 ,
} ,
[ IMX8M_POWER_DOMAIN_USB_OTG2 ] = {
. genpd = {
. name = " usb-otg2 " ,
} ,
. bits = {
. pxx = IMX8M_OTG2_SW_Pxx_REQ ,
. map = IMX8M_OTG2_A53_DOMAIN ,
} ,
. pgc = IMX8M_PGC_OTG2 ,
} ,
[ IMX8M_POWER_DOMAIN_DDR1 ] = {
. genpd = {
. name = " ddr1 " ,
} ,
. bits = {
. pxx = IMX8M_DDR1_SW_Pxx_REQ ,
. map = IMX8M_DDR2_A53_DOMAIN ,
} ,
. pgc = IMX8M_PGC_DDR1 ,
} ,
[ IMX8M_POWER_DOMAIN_GPU ] = {
. genpd = {
. name = " gpu " ,
} ,
. bits = {
. pxx = IMX8M_GPU_SW_Pxx_REQ ,
. map = IMX8M_GPU_A53_DOMAIN ,
2018-12-17 16:31:51 +01:00
. hsk = IMX8M_GPU_HSK_PWRDNREQN ,
2018-11-16 16:49:27 +01:00
} ,
. pgc = IMX8M_PGC_GPU ,
} ,
[ IMX8M_POWER_DOMAIN_VPU ] = {
. genpd = {
. name = " vpu " ,
} ,
. bits = {
. pxx = IMX8M_VPU_SW_Pxx_REQ ,
. map = IMX8M_VPU_A53_DOMAIN ,
2018-12-17 16:31:51 +01:00
. hsk = IMX8M_VPU_HSK_PWRDNREQN ,
2018-11-16 16:49:27 +01:00
} ,
. pgc = IMX8M_PGC_VPU ,
} ,
[ IMX8M_POWER_DOMAIN_DISP ] = {
. genpd = {
. name = " disp " ,
} ,
. bits = {
. pxx = IMX8M_DISP_SW_Pxx_REQ ,
. map = IMX8M_DISP_A53_DOMAIN ,
2018-12-17 16:31:51 +01:00
. hsk = IMX8M_DISP_HSK_PWRDNREQN ,
2018-11-16 16:49:27 +01:00
} ,
. pgc = IMX8M_PGC_DISP ,
} ,
[ IMX8M_POWER_DOMAIN_MIPI_CSI1 ] = {
. genpd = {
. name = " mipi-csi1 " ,
} ,
. bits = {
. pxx = IMX8M_MIPI_CSI1_SW_Pxx_REQ ,
. map = IMX8M_MIPI_CSI1_A53_DOMAIN ,
} ,
. pgc = IMX8M_PGC_MIPI_CSI1 ,
} ,
[ IMX8M_POWER_DOMAIN_MIPI_CSI2 ] = {
. genpd = {
. name = " mipi-csi2 " ,
} ,
. bits = {
. pxx = IMX8M_MIPI_CSI2_SW_Pxx_REQ ,
. map = IMX8M_MIPI_CSI2_A53_DOMAIN ,
} ,
. pgc = IMX8M_PGC_MIPI_CSI2 ,
} ,
[ IMX8M_POWER_DOMAIN_PCIE2 ] = {
. genpd = {
. name = " pcie2 " ,
} ,
. bits = {
. pxx = IMX8M_PCIE2_SW_Pxx_REQ ,
. map = IMX8M_PCIE2_A53_DOMAIN ,
} ,
. pgc = IMX8M_PGC_PCIE2 ,
} ,
} ;
static const struct regmap_range imx8m_yes_ranges [ ] = {
regmap_reg_range ( GPC_LPCR_A_CORE_BSC ,
2018-12-17 16:31:51 +01:00
GPC_PU_PWRHSK ) ,
2018-11-16 16:49:27 +01:00
regmap_reg_range ( GPC_PGC_CTRL ( IMX8M_PGC_MIPI ) ,
GPC_PGC_SR ( IMX8M_PGC_MIPI ) ) ,
regmap_reg_range ( GPC_PGC_CTRL ( IMX8M_PGC_PCIE1 ) ,
GPC_PGC_SR ( IMX8M_PGC_PCIE1 ) ) ,
regmap_reg_range ( GPC_PGC_CTRL ( IMX8M_PGC_OTG1 ) ,
GPC_PGC_SR ( IMX8M_PGC_OTG1 ) ) ,
regmap_reg_range ( GPC_PGC_CTRL ( IMX8M_PGC_OTG2 ) ,
GPC_PGC_SR ( IMX8M_PGC_OTG2 ) ) ,
regmap_reg_range ( GPC_PGC_CTRL ( IMX8M_PGC_DDR1 ) ,
GPC_PGC_SR ( IMX8M_PGC_DDR1 ) ) ,
regmap_reg_range ( GPC_PGC_CTRL ( IMX8M_PGC_GPU ) ,
GPC_PGC_SR ( IMX8M_PGC_GPU ) ) ,
regmap_reg_range ( GPC_PGC_CTRL ( IMX8M_PGC_VPU ) ,
GPC_PGC_SR ( IMX8M_PGC_VPU ) ) ,
regmap_reg_range ( GPC_PGC_CTRL ( IMX8M_PGC_DISP ) ,
GPC_PGC_SR ( IMX8M_PGC_DISP ) ) ,
regmap_reg_range ( GPC_PGC_CTRL ( IMX8M_PGC_MIPI_CSI1 ) ,
GPC_PGC_SR ( IMX8M_PGC_MIPI_CSI1 ) ) ,
regmap_reg_range ( GPC_PGC_CTRL ( IMX8M_PGC_MIPI_CSI2 ) ,
GPC_PGC_SR ( IMX8M_PGC_MIPI_CSI2 ) ) ,
regmap_reg_range ( GPC_PGC_CTRL ( IMX8M_PGC_PCIE2 ) ,
GPC_PGC_SR ( IMX8M_PGC_PCIE2 ) ) ,
} ;
static const struct regmap_access_table imx8m_access_table = {
. yes_ranges = imx8m_yes_ranges ,
. n_yes_ranges = ARRAY_SIZE ( imx8m_yes_ranges ) ,
} ;
static const struct imx_pgc_domain_data imx8m_pgc_domain_data = {
. domains = imx8m_pgc_domains ,
. domains_num = ARRAY_SIZE ( imx8m_pgc_domains ) ,
. reg_access_table = & imx8m_access_table ,
} ;
2018-12-17 16:31:52 +01:00
static int imx_pgc_get_clocks ( struct imx_pgc_domain * domain )
{
int i , ret ;
for ( i = 0 ; ; i + + ) {
struct clk * clk = of_clk_get ( domain - > dev - > of_node , i ) ;
if ( IS_ERR ( clk ) )
break ;
if ( i > = GPC_CLK_MAX ) {
dev_err ( domain - > dev , " more than %d clocks \n " ,
GPC_CLK_MAX ) ;
ret = - EINVAL ;
goto clk_err ;
}
domain - > clk [ i ] = clk ;
}
domain - > num_clks = i ;
return 0 ;
clk_err :
while ( i - - )
clk_put ( domain - > clk [ i ] ) ;
return ret ;
}
static void imx_pgc_put_clocks ( struct imx_pgc_domain * domain )
{
int i ;
for ( i = domain - > num_clks - 1 ; i > = 0 ; i - - )
clk_put ( domain - > clk [ i ] ) ;
}
2018-08-28 16:36:46 +08:00
static int imx_pgc_domain_probe ( struct platform_device * pdev )
2017-03-28 08:19:45 -07:00
{
2018-08-28 16:36:46 +08:00
struct imx_pgc_domain * domain = pdev - > dev . platform_data ;
2017-03-28 08:19:45 -07:00
int ret ;
domain - > dev = & pdev - > dev ;
domain - > regulator = devm_regulator_get_optional ( domain - > dev , " power " ) ;
if ( IS_ERR ( domain - > regulator ) ) {
if ( PTR_ERR ( domain - > regulator ) ! = - ENODEV ) {
2017-08-02 12:51:29 -07:00
if ( PTR_ERR ( domain - > regulator ) ! = - EPROBE_DEFER )
dev_err ( domain - > dev , " Failed to get domain's regulator \n " ) ;
2017-03-28 08:19:45 -07:00
return PTR_ERR ( domain - > regulator ) ;
}
2018-11-16 16:49:27 +01:00
} else if ( domain - > voltage ) {
2017-03-28 08:19:45 -07:00
regulator_set_voltage ( domain - > regulator ,
domain - > voltage , domain - > voltage ) ;
}
2018-12-17 16:31:52 +01:00
ret = imx_pgc_get_clocks ( domain ) ;
if ( ret ) {
if ( ret ! = - EPROBE_DEFER )
dev_err ( domain - > dev , " Failed to get domain's clocks \n " ) ;
return ret ;
}
2017-08-02 12:51:29 -07:00
ret = pm_genpd_init ( & domain - > genpd , NULL , true ) ;
if ( ret ) {
dev_err ( domain - > dev , " Failed to init power domain \n " ) ;
2018-12-17 16:31:52 +01:00
imx_pgc_put_clocks ( domain ) ;
2017-08-02 12:51:29 -07:00
return ret ;
}
2017-03-28 08:19:45 -07:00
ret = of_genpd_add_provider_simple ( domain - > dev - > of_node ,
& domain - > genpd ) ;
if ( ret ) {
dev_err ( domain - > dev , " Failed to add genpd provider \n " ) ;
pm_genpd_remove ( & domain - > genpd ) ;
2018-12-17 16:31:52 +01:00
imx_pgc_put_clocks ( domain ) ;
2017-03-28 08:19:45 -07:00
}
return ret ;
}
2018-08-28 16:36:46 +08:00
static int imx_pgc_domain_remove ( struct platform_device * pdev )
2017-03-28 08:19:45 -07:00
{
2018-08-28 16:36:46 +08:00
struct imx_pgc_domain * domain = pdev - > dev . platform_data ;
2017-03-28 08:19:45 -07:00
of_genpd_del_provider ( domain - > dev - > of_node ) ;
pm_genpd_remove ( & domain - > genpd ) ;
2018-12-17 16:31:52 +01:00
imx_pgc_put_clocks ( domain ) ;
2017-03-28 08:19:45 -07:00
return 0 ;
}
2018-08-28 16:36:46 +08:00
static const struct platform_device_id imx_pgc_domain_id [ ] = {
{ " imx-pgc-domain " , } ,
2017-03-28 08:19:45 -07:00
{ } ,
} ;
2018-08-28 16:36:46 +08:00
static struct platform_driver imx_pgc_domain_driver = {
2017-03-28 08:19:45 -07:00
. driver = {
2018-08-28 16:36:46 +08:00
. name = " imx-pgc " ,
2017-03-28 08:19:45 -07:00
} ,
2018-08-28 16:36:46 +08:00
. probe = imx_pgc_domain_probe ,
. remove = imx_pgc_domain_remove ,
. id_table = imx_pgc_domain_id ,
2017-03-28 08:19:45 -07:00
} ;
2018-08-28 16:36:46 +08:00
builtin_platform_driver ( imx_pgc_domain_driver )
2017-03-28 08:19:45 -07:00
static int imx_gpcv2_probe ( struct platform_device * pdev )
{
2018-11-16 16:49:26 +01:00
const struct imx_pgc_domain_data * domain_data =
of_device_get_match_data ( & pdev - > dev ) ;
struct regmap_config regmap_config = {
2017-03-28 08:19:45 -07:00
. reg_bits = 32 ,
. val_bits = 32 ,
. reg_stride = 4 ,
2018-11-16 16:49:26 +01:00
. rd_table = domain_data - > reg_access_table ,
. wr_table = domain_data - > reg_access_table ,
2017-03-28 08:19:45 -07:00
. max_register = SZ_4K ,
} ;
struct device * dev = & pdev - > dev ;
struct device_node * pgc_np , * np ;
struct regmap * regmap ;
struct resource * res ;
void __iomem * base ;
int ret ;
pgc_np = of_get_child_by_name ( dev - > of_node , " pgc " ) ;
if ( ! pgc_np ) {
dev_err ( dev , " No power domains specified in DT \n " ) ;
return - EINVAL ;
}
res = platform_get_resource ( pdev , IORESOURCE_MEM , 0 ) ;
base = devm_ioremap_resource ( dev , res ) ;
if ( IS_ERR ( base ) )
return PTR_ERR ( base ) ;
regmap = devm_regmap_init_mmio ( dev , base , & regmap_config ) ;
if ( IS_ERR ( regmap ) ) {
ret = PTR_ERR ( regmap ) ;
dev_err ( dev , " failed to init regmap (%d) \n " , ret ) ;
return ret ;
}
for_each_child_of_node ( pgc_np , np ) {
struct platform_device * pd_pdev ;
2018-08-28 16:36:46 +08:00
struct imx_pgc_domain * domain ;
2017-03-28 08:19:45 -07:00
u32 domain_index ;
ret = of_property_read_u32 ( np , " reg " , & domain_index ) ;
if ( ret ) {
dev_err ( dev , " Failed to read 'reg' property \n " ) ;
of_node_put ( np ) ;
return ret ;
}
2018-08-28 16:36:46 +08:00
if ( domain_index > = domain_data - > domains_num ) {
2017-03-28 08:19:45 -07:00
dev_warn ( dev ,
" Domain index %d is out of bounds \n " ,
domain_index ) ;
continue ;
}
2018-08-28 16:36:46 +08:00
pd_pdev = platform_device_alloc ( " imx-pgc-domain " ,
2017-03-28 08:19:45 -07:00
domain_index ) ;
if ( ! pd_pdev ) {
dev_err ( dev , " Failed to allocate platform device \n " ) ;
of_node_put ( np ) ;
return - ENOMEM ;
}
2018-04-10 11:32:09 -07:00
ret = platform_device_add_data ( pd_pdev ,
2018-08-28 16:36:46 +08:00
& domain_data - > domains [ domain_index ] ,
sizeof ( domain_data - > domains [ domain_index ] ) ) ;
2018-04-10 11:32:09 -07:00
if ( ret ) {
platform_device_put ( pd_pdev ) ;
of_node_put ( np ) ;
return ret ;
}
domain = pd_pdev - > dev . platform_data ;
domain - > regmap = regmap ;
2018-08-28 16:36:46 +08:00
domain - > genpd . power_on = imx_gpc_pu_pgc_sw_pup_req ;
domain - > genpd . power_off = imx_gpc_pu_pgc_sw_pdn_req ;
2018-04-10 11:32:09 -07:00
2017-03-28 08:19:45 -07:00
pd_pdev - > dev . parent = dev ;
pd_pdev - > dev . of_node = np ;
ret = platform_device_add ( pd_pdev ) ;
if ( ret ) {
platform_device_put ( pd_pdev ) ;
of_node_put ( np ) ;
return ret ;
}
}
return 0 ;
}
static const struct of_device_id imx_gpcv2_dt_ids [ ] = {
2018-08-28 16:36:46 +08:00
{ . compatible = " fsl,imx7d-gpc " , . data = & imx7_pgc_domain_data , } ,
2018-11-16 16:49:27 +01:00
{ . compatible = " fsl,imx8mq-gpc " , . data = & imx8m_pgc_domain_data , } ,
2017-03-28 08:19:45 -07:00
{ }
} ;
static struct platform_driver imx_gpc_driver = {
. driver = {
. name = " imx-gpcv2 " ,
. of_match_table = imx_gpcv2_dt_ids ,
} ,
. probe = imx_gpcv2_probe ,
} ;
builtin_platform_driver ( imx_gpc_driver )