2018-12-12 15:51:48 -08:00
// SPDX-License-Identifier: GPL-2.0+
/*
* Power domain driver for Broadcom BCM2835
*
* Copyright ( C ) 2018 Broadcom
*/
# include <dt-bindings/soc/bcm2835-pm.h>
# include <linux/clk.h>
# include <linux/delay.h>
# include <linux/io.h>
# include <linux/mfd/bcm2835-pm.h>
# include <linux/module.h>
# include <linux/platform_device.h>
# include <linux/pm_domain.h>
# include <linux/reset-controller.h>
# include <linux/types.h>
# define PM_GNRIC 0x00
# define PM_AUDIO 0x04
# define PM_STATUS 0x18
# define PM_RSTC 0x1c
# define PM_RSTS 0x20
# define PM_WDOG 0x24
# define PM_PADS0 0x28
# define PM_PADS2 0x2c
# define PM_PADS3 0x30
# define PM_PADS4 0x34
# define PM_PADS5 0x38
# define PM_PADS6 0x3c
# define PM_CAM0 0x44
# define PM_CAM0_LDOHPEN BIT(2)
# define PM_CAM0_LDOLPEN BIT(1)
# define PM_CAM0_CTRLEN BIT(0)
# define PM_CAM1 0x48
# define PM_CAM1_LDOHPEN BIT(2)
# define PM_CAM1_LDOLPEN BIT(1)
# define PM_CAM1_CTRLEN BIT(0)
# define PM_CCP2TX 0x4c
# define PM_CCP2TX_LDOEN BIT(1)
# define PM_CCP2TX_CTRLEN BIT(0)
# define PM_DSI0 0x50
# define PM_DSI0_LDOHPEN BIT(2)
# define PM_DSI0_LDOLPEN BIT(1)
# define PM_DSI0_CTRLEN BIT(0)
# define PM_DSI1 0x54
# define PM_DSI1_LDOHPEN BIT(2)
# define PM_DSI1_LDOLPEN BIT(1)
# define PM_DSI1_CTRLEN BIT(0)
# define PM_HDMI 0x58
# define PM_HDMI_RSTDR BIT(19)
# define PM_HDMI_LDOPD BIT(1)
# define PM_HDMI_CTRLEN BIT(0)
# define PM_USB 0x5c
/* The power gates must be enabled with this bit before enabling the LDO in the
* USB block .
*/
# define PM_USB_CTRLEN BIT(0)
# define PM_PXLDO 0x60
# define PM_PXBG 0x64
# define PM_DFT 0x68
# define PM_SMPS 0x6c
# define PM_XOSC 0x70
# define PM_SPAREW 0x74
# define PM_SPARER 0x78
# define PM_AVS_RSTDR 0x7c
# define PM_AVS_STAT 0x80
# define PM_AVS_EVENT 0x84
# define PM_AVS_INTEN 0x88
# define PM_DUMMY 0xfc
# define PM_IMAGE 0x108
# define PM_GRAFX 0x10c
# define PM_PROC 0x110
# define PM_ENAB BIT(12)
# define PM_ISPRSTN BIT(8)
# define PM_H264RSTN BIT(7)
# define PM_PERIRSTN BIT(6)
# define PM_V3DRSTN BIT(6)
# define PM_ISFUNC BIT(5)
# define PM_MRDONE BIT(4)
# define PM_MEMREP BIT(3)
# define PM_ISPOW BIT(2)
# define PM_POWOK BIT(1)
# define PM_POWUP BIT(0)
# define PM_INRUSH_SHIFT 13
# define PM_INRUSH_3_5_MA 0
# define PM_INRUSH_5_MA 1
# define PM_INRUSH_10_MA 2
# define PM_INRUSH_20_MA 3
# define PM_INRUSH_MASK (3 << PM_INRUSH_SHIFT)
# define PM_PASSWORD 0x5a000000
# define PM_WDOG_TIME_SET 0x000fffff
# define PM_RSTC_WRCFG_CLR 0xffffffcf
# define PM_RSTS_HADWRH_SET 0x00000040
# define PM_RSTC_WRCFG_SET 0x00000030
# define PM_RSTC_WRCFG_FULL_RESET 0x00000020
# define PM_RSTC_RESET 0x00000102
# define PM_READ(reg) readl(power->base + (reg))
# define PM_WRITE(reg, val) writel(PM_PASSWORD | (val), power->base + (reg))
# define ASB_BRDG_VERSION 0x00
# define ASB_CPR_CTRL 0x04
# define ASB_V3D_S_CTRL 0x08
# define ASB_V3D_M_CTRL 0x0c
# define ASB_ISP_S_CTRL 0x10
# define ASB_ISP_M_CTRL 0x14
# define ASB_H264_S_CTRL 0x18
# define ASB_H264_M_CTRL 0x1c
# define ASB_REQ_STOP BIT(0)
# define ASB_ACK BIT(1)
# define ASB_EMPTY BIT(2)
# define ASB_FULL BIT(3)
# define ASB_AXI_BRDG_ID 0x20
# define ASB_READ(reg) readl(power->asb + (reg))
# define ASB_WRITE(reg, val) writel(PM_PASSWORD | (val), power->asb + (reg))
struct bcm2835_power_domain {
struct generic_pm_domain base ;
struct bcm2835_power * power ;
u32 domain ;
struct clk * clk ;
} ;
struct bcm2835_power {
struct device * dev ;
/* PM registers. */
void __iomem * base ;
/* AXI Async bridge registers. */
void __iomem * asb ;
struct genpd_onecell_data pd_xlate ;
struct bcm2835_power_domain domains [ BCM2835_POWER_DOMAIN_COUNT ] ;
struct reset_controller_dev reset ;
} ;
static int bcm2835_asb_enable ( struct bcm2835_power * power , u32 reg )
{
2019-02-20 10:19:50 -08:00
u64 start ;
if ( ! reg )
return 0 ;
start = ktime_get_ns ( ) ;
2018-12-12 15:51:48 -08:00
/* Enable the module's async AXI bridges. */
ASB_WRITE ( reg , ASB_READ ( reg ) & ~ ASB_REQ_STOP ) ;
while ( ASB_READ ( reg ) & ASB_ACK ) {
cpu_relax ( ) ;
if ( ktime_get_ns ( ) - start > = 1000 )
return - ETIMEDOUT ;
}
return 0 ;
}
static int bcm2835_asb_disable ( struct bcm2835_power * power , u32 reg )
{
2019-02-20 10:19:50 -08:00
u64 start ;
if ( ! reg )
return 0 ;
start = ktime_get_ns ( ) ;
2018-12-12 15:51:48 -08:00
/* Enable the module's async AXI bridges. */
ASB_WRITE ( reg , ASB_READ ( reg ) | ASB_REQ_STOP ) ;
while ( ! ( ASB_READ ( reg ) & ASB_ACK ) ) {
cpu_relax ( ) ;
if ( ktime_get_ns ( ) - start > = 1000 )
return - ETIMEDOUT ;
}
return 0 ;
}
static int bcm2835_power_power_off ( struct bcm2835_power_domain * pd , u32 pm_reg )
{
struct bcm2835_power * power = pd - > power ;
/* Enable functional isolation */
PM_WRITE ( pm_reg , PM_READ ( pm_reg ) & ~ PM_ISFUNC ) ;
/* Enable electrical isolation */
PM_WRITE ( pm_reg , PM_READ ( pm_reg ) & ~ PM_ISPOW ) ;
/* Open the power switches. */
PM_WRITE ( pm_reg , PM_READ ( pm_reg ) & ~ PM_POWUP ) ;
return 0 ;
}
static int bcm2835_power_power_on ( struct bcm2835_power_domain * pd , u32 pm_reg )
{
struct bcm2835_power * power = pd - > power ;
struct device * dev = power - > dev ;
u64 start ;
int ret ;
int inrush ;
bool powok ;
/* If it was already powered on by the fw, leave it that way. */
if ( PM_READ ( pm_reg ) & PM_POWUP )
return 0 ;
/* Enable power. Allowing too much current at once may result
* in POWOK never getting set , so start low and ramp it up as
* necessary to succeed .
*/
powok = false ;
for ( inrush = PM_INRUSH_3_5_MA ; inrush < = PM_INRUSH_20_MA ; inrush + + ) {
PM_WRITE ( pm_reg ,
( PM_READ ( pm_reg ) & ~ PM_INRUSH_MASK ) |
( inrush < < PM_INRUSH_SHIFT ) |
PM_POWUP ) ;
start = ktime_get_ns ( ) ;
while ( ! ( powok = ! ! ( PM_READ ( pm_reg ) & PM_POWOK ) ) ) {
cpu_relax ( ) ;
if ( ktime_get_ns ( ) - start > = 3000 )
break ;
}
}
if ( ! powok ) {
dev_err ( dev , " Timeout waiting for %s power OK \n " ,
pd - > base . name ) ;
ret = - ETIMEDOUT ;
goto err_disable_powup ;
}
/* Disable electrical isolation */
PM_WRITE ( pm_reg , PM_READ ( pm_reg ) | PM_ISPOW ) ;
/* Repair memory */
PM_WRITE ( pm_reg , PM_READ ( pm_reg ) | PM_MEMREP ) ;
start = ktime_get_ns ( ) ;
while ( ! ( PM_READ ( pm_reg ) & PM_MRDONE ) ) {
cpu_relax ( ) ;
if ( ktime_get_ns ( ) - start > = 1000 ) {
dev_err ( dev , " Timeout waiting for %s memory repair \n " ,
pd - > base . name ) ;
ret = - ETIMEDOUT ;
goto err_disable_ispow ;
}
}
/* Disable functional isolation */
PM_WRITE ( pm_reg , PM_READ ( pm_reg ) | PM_ISFUNC ) ;
return 0 ;
err_disable_ispow :
PM_WRITE ( pm_reg , PM_READ ( pm_reg ) & ~ PM_ISPOW ) ;
err_disable_powup :
PM_WRITE ( pm_reg , PM_READ ( pm_reg ) & ~ ( PM_POWUP | PM_INRUSH_MASK ) ) ;
return ret ;
}
static int bcm2835_asb_power_on ( struct bcm2835_power_domain * pd ,
u32 pm_reg ,
u32 asb_m_reg ,
u32 asb_s_reg ,
u32 reset_flags )
{
struct bcm2835_power * power = pd - > power ;
int ret ;
ret = clk_prepare_enable ( pd - > clk ) ;
if ( ret ) {
dev_err ( power - > dev , " Failed to enable clock for %s \n " ,
pd - > base . name ) ;
return ret ;
}
/* Wait 32 clocks for reset to propagate, 1 us will be enough */
udelay ( 1 ) ;
clk_disable_unprepare ( pd - > clk ) ;
/* Deassert the resets. */
PM_WRITE ( pm_reg , PM_READ ( pm_reg ) | reset_flags ) ;
ret = clk_prepare_enable ( pd - > clk ) ;
if ( ret ) {
dev_err ( power - > dev , " Failed to enable clock for %s \n " ,
pd - > base . name ) ;
goto err_enable_resets ;
}
ret = bcm2835_asb_enable ( power , asb_m_reg ) ;
if ( ret ) {
dev_err ( power - > dev , " Failed to enable ASB master for %s \n " ,
pd - > base . name ) ;
goto err_disable_clk ;
}
ret = bcm2835_asb_enable ( power , asb_s_reg ) ;
if ( ret ) {
dev_err ( power - > dev , " Failed to enable ASB slave for %s \n " ,
pd - > base . name ) ;
goto err_disable_asb_master ;
}
return 0 ;
err_disable_asb_master :
bcm2835_asb_disable ( power , asb_m_reg ) ;
err_disable_clk :
clk_disable_unprepare ( pd - > clk ) ;
err_enable_resets :
PM_WRITE ( pm_reg , PM_READ ( pm_reg ) & ~ reset_flags ) ;
return ret ;
}
static int bcm2835_asb_power_off ( struct bcm2835_power_domain * pd ,
u32 pm_reg ,
u32 asb_m_reg ,
u32 asb_s_reg ,
u32 reset_flags )
{
struct bcm2835_power * power = pd - > power ;
int ret ;
ret = bcm2835_asb_disable ( power , asb_s_reg ) ;
if ( ret ) {
dev_warn ( power - > dev , " Failed to disable ASB slave for %s \n " ,
pd - > base . name ) ;
return ret ;
}
ret = bcm2835_asb_disable ( power , asb_m_reg ) ;
if ( ret ) {
dev_warn ( power - > dev , " Failed to disable ASB master for %s \n " ,
pd - > base . name ) ;
bcm2835_asb_enable ( power , asb_s_reg ) ;
return ret ;
}
clk_disable_unprepare ( pd - > clk ) ;
/* Assert the resets. */
PM_WRITE ( pm_reg , PM_READ ( pm_reg ) & ~ reset_flags ) ;
return 0 ;
}
static int bcm2835_power_pd_power_on ( struct generic_pm_domain * domain )
{
struct bcm2835_power_domain * pd =
container_of ( domain , struct bcm2835_power_domain , base ) ;
struct bcm2835_power * power = pd - > power ;
switch ( pd - > domain ) {
case BCM2835_POWER_DOMAIN_GRAFX :
return bcm2835_power_power_on ( pd , PM_GRAFX ) ;
case BCM2835_POWER_DOMAIN_GRAFX_V3D :
return bcm2835_asb_power_on ( pd , PM_GRAFX ,
ASB_V3D_M_CTRL , ASB_V3D_S_CTRL ,
PM_V3DRSTN ) ;
case BCM2835_POWER_DOMAIN_IMAGE :
return bcm2835_power_power_on ( pd , PM_IMAGE ) ;
case BCM2835_POWER_DOMAIN_IMAGE_PERI :
return bcm2835_asb_power_on ( pd , PM_IMAGE ,
0 , 0 ,
PM_PERIRSTN ) ;
case BCM2835_POWER_DOMAIN_IMAGE_ISP :
return bcm2835_asb_power_on ( pd , PM_IMAGE ,
ASB_ISP_M_CTRL , ASB_ISP_S_CTRL ,
PM_ISPRSTN ) ;
case BCM2835_POWER_DOMAIN_IMAGE_H264 :
return bcm2835_asb_power_on ( pd , PM_IMAGE ,
ASB_H264_M_CTRL , ASB_H264_S_CTRL ,
PM_H264RSTN ) ;
case BCM2835_POWER_DOMAIN_USB :
PM_WRITE ( PM_USB , PM_USB_CTRLEN ) ;
return 0 ;
case BCM2835_POWER_DOMAIN_DSI0 :
PM_WRITE ( PM_DSI0 , PM_DSI0_CTRLEN ) ;
PM_WRITE ( PM_DSI0 , PM_DSI0_CTRLEN | PM_DSI0_LDOHPEN ) ;
return 0 ;
case BCM2835_POWER_DOMAIN_DSI1 :
PM_WRITE ( PM_DSI1 , PM_DSI1_CTRLEN ) ;
PM_WRITE ( PM_DSI1 , PM_DSI1_CTRLEN | PM_DSI1_LDOHPEN ) ;
return 0 ;
case BCM2835_POWER_DOMAIN_CCP2TX :
PM_WRITE ( PM_CCP2TX , PM_CCP2TX_CTRLEN ) ;
PM_WRITE ( PM_CCP2TX , PM_CCP2TX_CTRLEN | PM_CCP2TX_LDOEN ) ;
return 0 ;
case BCM2835_POWER_DOMAIN_HDMI :
PM_WRITE ( PM_HDMI , PM_READ ( PM_HDMI ) | PM_HDMI_RSTDR ) ;
PM_WRITE ( PM_HDMI , PM_READ ( PM_HDMI ) | PM_HDMI_CTRLEN ) ;
PM_WRITE ( PM_HDMI , PM_READ ( PM_HDMI ) & ~ PM_HDMI_LDOPD ) ;
usleep_range ( 100 , 200 ) ;
PM_WRITE ( PM_HDMI , PM_READ ( PM_HDMI ) & ~ PM_HDMI_RSTDR ) ;
return 0 ;
default :
dev_err ( power - > dev , " Invalid domain %d \n " , pd - > domain ) ;
return - EINVAL ;
}
}
static int bcm2835_power_pd_power_off ( struct generic_pm_domain * domain )
{
struct bcm2835_power_domain * pd =
container_of ( domain , struct bcm2835_power_domain , base ) ;
struct bcm2835_power * power = pd - > power ;
switch ( pd - > domain ) {
case BCM2835_POWER_DOMAIN_GRAFX :
return bcm2835_power_power_off ( pd , PM_GRAFX ) ;
case BCM2835_POWER_DOMAIN_GRAFX_V3D :
return bcm2835_asb_power_off ( pd , PM_GRAFX ,
ASB_V3D_M_CTRL , ASB_V3D_S_CTRL ,
PM_V3DRSTN ) ;
case BCM2835_POWER_DOMAIN_IMAGE :
return bcm2835_power_power_off ( pd , PM_IMAGE ) ;
case BCM2835_POWER_DOMAIN_IMAGE_PERI :
return bcm2835_asb_power_off ( pd , PM_IMAGE ,
0 , 0 ,
PM_PERIRSTN ) ;
case BCM2835_POWER_DOMAIN_IMAGE_ISP :
return bcm2835_asb_power_off ( pd , PM_IMAGE ,
ASB_ISP_M_CTRL , ASB_ISP_S_CTRL ,
PM_ISPRSTN ) ;
case BCM2835_POWER_DOMAIN_IMAGE_H264 :
return bcm2835_asb_power_off ( pd , PM_IMAGE ,
ASB_H264_M_CTRL , ASB_H264_S_CTRL ,
PM_H264RSTN ) ;
case BCM2835_POWER_DOMAIN_USB :
PM_WRITE ( PM_USB , 0 ) ;
return 0 ;
case BCM2835_POWER_DOMAIN_DSI0 :
PM_WRITE ( PM_DSI0 , PM_DSI0_CTRLEN ) ;
PM_WRITE ( PM_DSI0 , 0 ) ;
return 0 ;
case BCM2835_POWER_DOMAIN_DSI1 :
PM_WRITE ( PM_DSI1 , PM_DSI1_CTRLEN ) ;
PM_WRITE ( PM_DSI1 , 0 ) ;
return 0 ;
case BCM2835_POWER_DOMAIN_CCP2TX :
PM_WRITE ( PM_CCP2TX , PM_CCP2TX_CTRLEN ) ;
PM_WRITE ( PM_CCP2TX , 0 ) ;
return 0 ;
case BCM2835_POWER_DOMAIN_HDMI :
PM_WRITE ( PM_HDMI , PM_READ ( PM_HDMI ) | PM_HDMI_LDOPD ) ;
PM_WRITE ( PM_HDMI , PM_READ ( PM_HDMI ) & ~ PM_HDMI_CTRLEN ) ;
return 0 ;
default :
dev_err ( power - > dev , " Invalid domain %d \n " , pd - > domain ) ;
return - EINVAL ;
}
}
2019-02-20 10:19:51 -08:00
static int
2018-12-12 15:51:48 -08:00
bcm2835_init_power_domain ( struct bcm2835_power * power ,
int pd_xlate_index , const char * name )
{
struct device * dev = power - > dev ;
struct bcm2835_power_domain * dom = & power - > domains [ pd_xlate_index ] ;
dom - > clk = devm_clk_get ( dev - > parent , name ) ;
2019-02-20 10:19:51 -08:00
if ( IS_ERR ( dom - > clk ) ) {
int ret = PTR_ERR ( dom - > clk ) ;
if ( ret = = - EPROBE_DEFER )
return ret ;
/* Some domains don't have a clk, so make sure that we
* don ' t deref an error pointer later .
*/
dom - > clk = NULL ;
}
2018-12-12 15:51:48 -08:00
dom - > base . name = name ;
dom - > base . power_on = bcm2835_power_pd_power_on ;
dom - > base . power_off = bcm2835_power_pd_power_off ;
dom - > domain = pd_xlate_index ;
dom - > power = power ;
/* XXX: on/off at boot? */
pm_genpd_init ( & dom - > base , NULL , true ) ;
power - > pd_xlate . domains [ pd_xlate_index ] = & dom - > base ;
2019-02-20 10:19:51 -08:00
return 0 ;
2018-12-12 15:51:48 -08:00
}
/** bcm2835_reset_reset - Resets a block that has a reset line in the
* PM block .
*
* The consumer of the reset controller must have the power domain up
* - - there ' s no reset ability with the power domain down . To reset
* the sub - block , we just disable its access to memory through the
* ASB , reset , and re - enable .
*/
static int bcm2835_reset_reset ( struct reset_controller_dev * rcdev ,
unsigned long id )
{
struct bcm2835_power * power = container_of ( rcdev , struct bcm2835_power ,
reset ) ;
struct bcm2835_power_domain * pd ;
int ret ;
switch ( id ) {
case BCM2835_RESET_V3D :
pd = & power - > domains [ BCM2835_POWER_DOMAIN_GRAFX_V3D ] ;
break ;
case BCM2835_RESET_H264 :
pd = & power - > domains [ BCM2835_POWER_DOMAIN_IMAGE_H264 ] ;
break ;
case BCM2835_RESET_ISP :
pd = & power - > domains [ BCM2835_POWER_DOMAIN_IMAGE_ISP ] ;
break ;
default :
dev_err ( power - > dev , " Bad reset id %ld \n " , id ) ;
return - EINVAL ;
}
ret = bcm2835_power_pd_power_off ( & pd - > base ) ;
if ( ret )
return ret ;
return bcm2835_power_pd_power_on ( & pd - > base ) ;
}
static int bcm2835_reset_status ( struct reset_controller_dev * rcdev ,
unsigned long id )
{
struct bcm2835_power * power = container_of ( rcdev , struct bcm2835_power ,
reset ) ;
switch ( id ) {
case BCM2835_RESET_V3D :
return ! PM_READ ( PM_GRAFX & PM_V3DRSTN ) ;
case BCM2835_RESET_H264 :
return ! PM_READ ( PM_IMAGE & PM_H264RSTN ) ;
case BCM2835_RESET_ISP :
return ! PM_READ ( PM_IMAGE & PM_ISPRSTN ) ;
default :
return - EINVAL ;
}
}
2019-01-16 05:44:17 +00:00
static const struct reset_control_ops bcm2835_reset_ops = {
2018-12-12 15:51:48 -08:00
. reset = bcm2835_reset_reset ,
. status = bcm2835_reset_status ,
} ;
static const char * const power_domain_names [ ] = {
[ BCM2835_POWER_DOMAIN_GRAFX ] = " grafx " ,
[ BCM2835_POWER_DOMAIN_GRAFX_V3D ] = " v3d " ,
[ BCM2835_POWER_DOMAIN_IMAGE ] = " image " ,
[ BCM2835_POWER_DOMAIN_IMAGE_PERI ] = " peri_image " ,
[ BCM2835_POWER_DOMAIN_IMAGE_H264 ] = " h264 " ,
[ BCM2835_POWER_DOMAIN_IMAGE_ISP ] = " isp " ,
[ BCM2835_POWER_DOMAIN_USB ] = " usb " ,
[ BCM2835_POWER_DOMAIN_DSI0 ] = " dsi0 " ,
[ BCM2835_POWER_DOMAIN_DSI1 ] = " dsi1 " ,
[ BCM2835_POWER_DOMAIN_CAM0 ] = " cam0 " ,
[ BCM2835_POWER_DOMAIN_CAM1 ] = " cam1 " ,
[ BCM2835_POWER_DOMAIN_CCP2TX ] = " ccp2tx " ,
[ BCM2835_POWER_DOMAIN_HDMI ] = " hdmi " ,
} ;
static int bcm2835_power_probe ( struct platform_device * pdev )
{
struct bcm2835_pm * pm = dev_get_drvdata ( pdev - > dev . parent ) ;
struct device * dev = & pdev - > dev ;
struct bcm2835_power * power ;
static const struct {
int parent , child ;
} domain_deps [ ] = {
{ BCM2835_POWER_DOMAIN_GRAFX , BCM2835_POWER_DOMAIN_GRAFX_V3D } ,
{ BCM2835_POWER_DOMAIN_IMAGE , BCM2835_POWER_DOMAIN_IMAGE_PERI } ,
{ BCM2835_POWER_DOMAIN_IMAGE , BCM2835_POWER_DOMAIN_IMAGE_H264 } ,
{ BCM2835_POWER_DOMAIN_IMAGE , BCM2835_POWER_DOMAIN_IMAGE_ISP } ,
{ BCM2835_POWER_DOMAIN_IMAGE_PERI , BCM2835_POWER_DOMAIN_USB } ,
{ BCM2835_POWER_DOMAIN_IMAGE_PERI , BCM2835_POWER_DOMAIN_CAM0 } ,
{ BCM2835_POWER_DOMAIN_IMAGE_PERI , BCM2835_POWER_DOMAIN_CAM1 } ,
} ;
2019-02-20 10:19:51 -08:00
int ret = 0 , i ;
2018-12-12 15:51:48 -08:00
u32 id ;
power = devm_kzalloc ( dev , sizeof ( * power ) , GFP_KERNEL ) ;
if ( ! power )
return - ENOMEM ;
platform_set_drvdata ( pdev , power ) ;
power - > dev = dev ;
power - > base = pm - > base ;
power - > asb = pm - > asb ;
id = ASB_READ ( ASB_AXI_BRDG_ID ) ;
if ( id ! = 0x62726467 /* "BRDG" */ ) {
dev_err ( dev , " ASB register ID returned 0x%08x \n " , id ) ;
return - ENODEV ;
}
power - > pd_xlate . domains = devm_kcalloc ( dev ,
ARRAY_SIZE ( power_domain_names ) ,
sizeof ( * power - > pd_xlate . domains ) ,
GFP_KERNEL ) ;
if ( ! power - > pd_xlate . domains )
return - ENOMEM ;
power - > pd_xlate . num_domains = ARRAY_SIZE ( power_domain_names ) ;
2019-02-20 10:19:51 -08:00
for ( i = 0 ; i < ARRAY_SIZE ( power_domain_names ) ; i + + ) {
ret = bcm2835_init_power_domain ( power , i , power_domain_names [ i ] ) ;
if ( ret )
goto fail ;
}
2018-12-12 15:51:48 -08:00
for ( i = 0 ; i < ARRAY_SIZE ( domain_deps ) ; i + + ) {
pm_genpd_add_subdomain ( & power - > domains [ domain_deps [ i ] . parent ] . base ,
& power - > domains [ domain_deps [ i ] . child ] . base ) ;
}
power - > reset . owner = THIS_MODULE ;
power - > reset . nr_resets = BCM2835_RESET_COUNT ;
power - > reset . ops = & bcm2835_reset_ops ;
power - > reset . of_node = dev - > parent - > of_node ;
ret = devm_reset_controller_register ( dev , & power - > reset ) ;
if ( ret )
2019-02-20 10:19:51 -08:00
goto fail ;
2018-12-12 15:51:48 -08:00
of_genpd_add_provider_onecell ( dev - > parent - > of_node , & power - > pd_xlate ) ;
dev_info ( dev , " Broadcom BCM2835 power domains driver " ) ;
return 0 ;
2019-02-20 10:19:51 -08:00
fail :
for ( i = 0 ; i < ARRAY_SIZE ( power_domain_names ) ; i + + ) {
struct generic_pm_domain * dom = & power - > domains [ i ] . base ;
if ( dom - > name )
pm_genpd_remove ( dom ) ;
}
return ret ;
2018-12-12 15:51:48 -08:00
}
static int bcm2835_power_remove ( struct platform_device * pdev )
{
return 0 ;
}
static struct platform_driver bcm2835_power_driver = {
. probe = bcm2835_power_probe ,
. remove = bcm2835_power_remove ,
. driver = {
. name = " bcm2835-power " ,
} ,
} ;
module_platform_driver ( bcm2835_power_driver ) ;
MODULE_AUTHOR ( " Eric Anholt <eric@anholt.net> " ) ;
MODULE_DESCRIPTION ( " Driver for Broadcom BCM2835 PM power domains and reset " ) ;
MODULE_LICENSE ( " GPL " ) ;