2021-11-24 10:34:18 +03:00
// SPDX-License-Identifier: GPL-2.0-only OR MIT
/*
* Apple SoC PMGR device power state driver
*
* Copyright The Asahi Linux Contributors
*/
# include <linux/bitops.h>
# include <linux/bitfield.h>
# include <linux/err.h>
# include <linux/of.h>
# include <linux/of_address.h>
# include <linux/platform_device.h>
# include <linux/pm_domain.h>
# include <linux/regmap.h>
# include <linux/mfd/syscon.h>
# include <linux/reset-controller.h>
# include <linux/module.h>
# define APPLE_PMGR_RESET BIT(31)
# define APPLE_PMGR_AUTO_ENABLE BIT(28)
# define APPLE_PMGR_PS_AUTO GENMASK(27, 24)
2021-12-09 07:45:00 +03:00
# define APPLE_PMGR_PS_MIN GENMASK(19, 16)
2021-11-24 10:34:18 +03:00
# define APPLE_PMGR_PARENT_OFF BIT(11)
# define APPLE_PMGR_DEV_DISABLE BIT(10)
# define APPLE_PMGR_WAS_CLKGATED BIT(9)
# define APPLE_PMGR_WAS_PWRGATED BIT(8)
# define APPLE_PMGR_PS_ACTUAL GENMASK(7, 4)
# define APPLE_PMGR_PS_TARGET GENMASK(3, 0)
# define APPLE_PMGR_FLAGS (APPLE_PMGR_WAS_CLKGATED | APPLE_PMGR_WAS_PWRGATED)
# define APPLE_PMGR_PS_ACTIVE 0xf
# define APPLE_PMGR_PS_CLKGATE 0x4
# define APPLE_PMGR_PS_PWRGATE 0x0
# define APPLE_PMGR_PS_SET_TIMEOUT 100
# define APPLE_PMGR_RESET_TIME 1
struct apple_pmgr_ps {
struct device * dev ;
struct generic_pm_domain genpd ;
struct reset_controller_dev rcdev ;
struct regmap * regmap ;
u32 offset ;
2021-12-09 07:45:00 +03:00
u32 min_state ;
2021-11-24 10:34:18 +03:00
} ;
# define genpd_to_apple_pmgr_ps(_genpd) container_of(_genpd, struct apple_pmgr_ps, genpd)
# define rcdev_to_apple_pmgr_ps(_rcdev) container_of(_rcdev, struct apple_pmgr_ps, rcdev)
static int apple_pmgr_ps_set ( struct generic_pm_domain * genpd , u32 pstate , bool auto_enable )
{
int ret ;
struct apple_pmgr_ps * ps = genpd_to_apple_pmgr_ps ( genpd ) ;
u32 reg ;
ret = regmap_read ( ps - > regmap , ps - > offset , & reg ) ;
if ( ret < 0 )
return ret ;
/* Resets are synchronous, and only work if the device is powered and clocked. */
if ( reg & APPLE_PMGR_RESET & & pstate ! = APPLE_PMGR_PS_ACTIVE )
dev_err ( ps - > dev , " PS %s: powering off with RESET active \n " ,
genpd - > name ) ;
reg & = ~ ( APPLE_PMGR_AUTO_ENABLE | APPLE_PMGR_FLAGS | APPLE_PMGR_PS_TARGET ) ;
reg | = FIELD_PREP ( APPLE_PMGR_PS_TARGET , pstate ) ;
dev_dbg ( ps - > dev , " PS %s: pwrstate = 0x%x: 0x%x \n " , genpd - > name , pstate , reg ) ;
regmap_write ( ps - > regmap , ps - > offset , reg ) ;
ret = regmap_read_poll_timeout_atomic (
ps - > regmap , ps - > offset , reg ,
( FIELD_GET ( APPLE_PMGR_PS_ACTUAL , reg ) = = pstate ) , 1 ,
APPLE_PMGR_PS_SET_TIMEOUT ) ;
if ( ret < 0 )
dev_err ( ps - > dev , " PS %s: Failed to reach power state 0x%x (now: 0x%x) \n " ,
genpd - > name , pstate , reg ) ;
if ( auto_enable ) {
/* Not all devices implement this; this is a no-op where not implemented. */
reg & = ~ APPLE_PMGR_FLAGS ;
reg | = APPLE_PMGR_AUTO_ENABLE ;
regmap_write ( ps - > regmap , ps - > offset , reg ) ;
}
return ret ;
}
static bool apple_pmgr_ps_is_active ( struct apple_pmgr_ps * ps )
{
u32 reg = 0 ;
regmap_read ( ps - > regmap , ps - > offset , & reg ) ;
/*
* We consider domains as active if they are actually on , or if they have auto - PM
* enabled and the intended target is on .
*/
return ( FIELD_GET ( APPLE_PMGR_PS_ACTUAL , reg ) = = APPLE_PMGR_PS_ACTIVE | |
( FIELD_GET ( APPLE_PMGR_PS_TARGET , reg ) = = APPLE_PMGR_PS_ACTIVE & &
reg & APPLE_PMGR_AUTO_ENABLE ) ) ;
}
static int apple_pmgr_ps_power_on ( struct generic_pm_domain * genpd )
{
return apple_pmgr_ps_set ( genpd , APPLE_PMGR_PS_ACTIVE , true ) ;
}
static int apple_pmgr_ps_power_off ( struct generic_pm_domain * genpd )
{
return apple_pmgr_ps_set ( genpd , APPLE_PMGR_PS_PWRGATE , false ) ;
}
static int apple_pmgr_reset_assert ( struct reset_controller_dev * rcdev , unsigned long id )
{
struct apple_pmgr_ps * ps = rcdev_to_apple_pmgr_ps ( rcdev ) ;
2023-01-23 09:51:25 +03:00
unsigned long flags ;
2021-11-24 10:34:18 +03:00
2023-01-23 09:51:25 +03:00
spin_lock_irqsave ( & ps - > genpd . slock , flags ) ;
2021-11-24 10:34:18 +03:00
if ( ps - > genpd . status = = GENPD_STATE_OFF )
dev_err ( ps - > dev , " PS 0x%x: asserting RESET while powered down \n " , ps - > offset ) ;
dev_dbg ( ps - > dev , " PS 0x%x: assert reset \n " , ps - > offset ) ;
/* Quiesce device before asserting reset */
regmap_update_bits ( ps - > regmap , ps - > offset , APPLE_PMGR_FLAGS | APPLE_PMGR_DEV_DISABLE ,
APPLE_PMGR_DEV_DISABLE ) ;
regmap_update_bits ( ps - > regmap , ps - > offset , APPLE_PMGR_FLAGS | APPLE_PMGR_RESET ,
APPLE_PMGR_RESET ) ;
2023-01-23 09:51:25 +03:00
spin_unlock_irqrestore ( & ps - > genpd . slock , flags ) ;
2021-11-24 10:34:18 +03:00
return 0 ;
}
static int apple_pmgr_reset_deassert ( struct reset_controller_dev * rcdev , unsigned long id )
{
struct apple_pmgr_ps * ps = rcdev_to_apple_pmgr_ps ( rcdev ) ;
2023-01-23 09:51:25 +03:00
unsigned long flags ;
2021-11-24 10:34:18 +03:00
2023-01-23 09:51:25 +03:00
spin_lock_irqsave ( & ps - > genpd . slock , flags ) ;
2021-11-24 10:34:18 +03:00
dev_dbg ( ps - > dev , " PS 0x%x: deassert reset \n " , ps - > offset ) ;
regmap_update_bits ( ps - > regmap , ps - > offset , APPLE_PMGR_FLAGS | APPLE_PMGR_RESET , 0 ) ;
regmap_update_bits ( ps - > regmap , ps - > offset , APPLE_PMGR_FLAGS | APPLE_PMGR_DEV_DISABLE , 0 ) ;
if ( ps - > genpd . status = = GENPD_STATE_OFF )
dev_err ( ps - > dev , " PS 0x%x: RESET was deasserted while powered down \n " , ps - > offset ) ;
2023-01-23 09:51:25 +03:00
spin_unlock_irqrestore ( & ps - > genpd . slock , flags ) ;
2021-11-24 10:34:18 +03:00
return 0 ;
}
static int apple_pmgr_reset_reset ( struct reset_controller_dev * rcdev , unsigned long id )
{
int ret ;
ret = apple_pmgr_reset_assert ( rcdev , id ) ;
if ( ret )
return ret ;
usleep_range ( APPLE_PMGR_RESET_TIME , 2 * APPLE_PMGR_RESET_TIME ) ;
return apple_pmgr_reset_deassert ( rcdev , id ) ;
}
static int apple_pmgr_reset_status ( struct reset_controller_dev * rcdev , unsigned long id )
{
struct apple_pmgr_ps * ps = rcdev_to_apple_pmgr_ps ( rcdev ) ;
u32 reg = 0 ;
regmap_read ( ps - > regmap , ps - > offset , & reg ) ;
return ! ! ( reg & APPLE_PMGR_RESET ) ;
}
const struct reset_control_ops apple_pmgr_reset_ops = {
. assert = apple_pmgr_reset_assert ,
. deassert = apple_pmgr_reset_deassert ,
. reset = apple_pmgr_reset_reset ,
. status = apple_pmgr_reset_status ,
} ;
static int apple_pmgr_reset_xlate ( struct reset_controller_dev * rcdev ,
const struct of_phandle_args * reset_spec )
{
return 0 ;
}
static int apple_pmgr_ps_probe ( struct platform_device * pdev )
{
struct device * dev = & pdev - > dev ;
struct device_node * node = dev - > of_node ;
struct apple_pmgr_ps * ps ;
struct regmap * regmap ;
struct of_phandle_iterator it ;
int ret ;
const char * name ;
bool active ;
regmap = syscon_node_to_regmap ( node - > parent ) ;
if ( IS_ERR ( regmap ) )
return PTR_ERR ( regmap ) ;
ps = devm_kzalloc ( dev , sizeof ( * ps ) , GFP_KERNEL ) ;
if ( ! ps )
return - ENOMEM ;
ps - > dev = dev ;
ps - > regmap = regmap ;
ret = of_property_read_string ( node , " label " , & name ) ;
if ( ret < 0 ) {
dev_err ( dev , " missing label property \n " ) ;
return ret ;
}
ret = of_property_read_u32 ( node , " reg " , & ps - > offset ) ;
if ( ret < 0 ) {
dev_err ( dev , " missing reg property \n " ) ;
return ret ;
}
2023-01-23 09:51:25 +03:00
ps - > genpd . flags | = GENPD_FLAG_IRQ_SAFE ;
2021-11-24 10:34:18 +03:00
ps - > genpd . name = name ;
ps - > genpd . power_on = apple_pmgr_ps_power_on ;
ps - > genpd . power_off = apple_pmgr_ps_power_off ;
2021-12-09 07:45:00 +03:00
ret = of_property_read_u32 ( node , " apple,min-state " , & ps - > min_state ) ;
if ( ret = = 0 & & ps - > min_state < = APPLE_PMGR_PS_ACTIVE )
regmap_update_bits ( regmap , ps - > offset , APPLE_PMGR_FLAGS | APPLE_PMGR_PS_MIN ,
FIELD_PREP ( APPLE_PMGR_PS_MIN , ps - > min_state ) ) ;
2021-11-24 10:34:18 +03:00
active = apple_pmgr_ps_is_active ( ps ) ;
if ( of_property_read_bool ( node , " apple,always-on " ) ) {
ps - > genpd . flags | = GENPD_FLAG_ALWAYS_ON ;
if ( ! active ) {
dev_warn ( dev , " always-on domain %s is not on at boot \n " , name ) ;
/* Turn it on so pm_genpd_init does not fail */
active = apple_pmgr_ps_power_on ( & ps - > genpd ) = = 0 ;
}
}
/* Turn on auto-PM if the domain is already on */
if ( active )
regmap_update_bits ( regmap , ps - > offset , APPLE_PMGR_FLAGS | APPLE_PMGR_AUTO_ENABLE ,
APPLE_PMGR_AUTO_ENABLE ) ;
ret = pm_genpd_init ( & ps - > genpd , NULL , ! active ) ;
if ( ret < 0 ) {
dev_err ( dev , " pm_genpd_init failed \n " ) ;
return ret ;
}
ret = of_genpd_add_provider_simple ( node , & ps - > genpd ) ;
if ( ret < 0 ) {
dev_err ( dev , " of_genpd_add_provider_simple failed \n " ) ;
return ret ;
}
of_for_each_phandle ( & it , ret , node , " power-domains " , " #power-domain-cells " , - 1 ) {
struct of_phandle_args parent , child ;
parent . np = it . node ;
parent . args_count = of_phandle_iterator_args ( & it , parent . args , MAX_PHANDLE_ARGS ) ;
child . np = node ;
child . args_count = 0 ;
ret = of_genpd_add_subdomain ( & parent , & child ) ;
if ( ret = = - EPROBE_DEFER ) {
of_node_put ( parent . np ) ;
goto err_remove ;
} else if ( ret < 0 ) {
dev_err ( dev , " failed to add to parent domain: %d (%s -> %s) \n " ,
ret , it . node - > name , node - > name ) ;
of_node_put ( parent . np ) ;
goto err_remove ;
}
}
/*
* Do not participate in regular PM ; parent power domains are handled via the
* genpd hierarchy .
*/
pm_genpd_remove_device ( dev ) ;
ps - > rcdev . owner = THIS_MODULE ;
ps - > rcdev . nr_resets = 1 ;
ps - > rcdev . ops = & apple_pmgr_reset_ops ;
ps - > rcdev . of_node = dev - > of_node ;
ps - > rcdev . of_reset_n_cells = 0 ;
ps - > rcdev . of_xlate = apple_pmgr_reset_xlate ;
ret = devm_reset_controller_register ( dev , & ps - > rcdev ) ;
if ( ret < 0 )
goto err_remove ;
return 0 ;
err_remove :
of_genpd_del_provider ( node ) ;
pm_genpd_remove ( & ps - > genpd ) ;
return ret ;
}
static const struct of_device_id apple_pmgr_ps_of_match [ ] = {
{ . compatible = " apple,pmgr-pwrstate " } ,
{ }
} ;
MODULE_DEVICE_TABLE ( of , apple_pmgr_ps_of_match ) ;
static struct platform_driver apple_pmgr_ps_driver = {
. probe = apple_pmgr_ps_probe ,
. driver = {
. name = " apple-pmgr-pwrstate " ,
. of_match_table = apple_pmgr_ps_of_match ,
} ,
} ;
MODULE_AUTHOR ( " Hector Martin <marcan@marcan.st> " ) ;
MODULE_DESCRIPTION ( " PMGR power state driver for Apple SoCs " ) ;
module_platform_driver ( apple_pmgr_ps_driver ) ;