2019-04-15 12:17:38 +03:00
// SPDX-License-Identifier: GPL-2.0
// Copyright (C) STMicroelectronics 2019
// Authors: Gabriel Fernandez <gabriel.fernandez@st.com>
// Pascal Paillet <p.paillet@st.com>.
# include <linux/io.h>
# include <linux/iopoll.h>
# include <linux/module.h>
2023-07-14 20:49:28 +03:00
# include <linux/of.h>
2019-04-15 12:17:38 +03:00
# include <linux/platform_device.h>
# include <linux/regulator/driver.h>
# include <linux/regulator/of_regulator.h>
/*
* Registers description
*/
# define REG_PWR_CR3 0x0C
# define USB_3_3_EN BIT(24)
# define USB_3_3_RDY BIT(26)
# define REG_1_8_EN BIT(28)
# define REG_1_8_RDY BIT(29)
# define REG_1_1_EN BIT(30)
# define REG_1_1_RDY BIT(31)
/* list of supported regulators */
enum {
PWR_REG11 ,
PWR_REG18 ,
PWR_USB33 ,
STM32PWR_REG_NUM_REGS
} ;
2019-04-15 19:52:38 +03:00
static u32 ready_mask_table [ STM32PWR_REG_NUM_REGS ] = {
2019-04-15 12:17:38 +03:00
[ PWR_REG11 ] = REG_1_1_RDY ,
[ PWR_REG18 ] = REG_1_8_RDY ,
[ PWR_USB33 ] = USB_3_3_RDY ,
} ;
struct stm32_pwr_reg {
void __iomem * base ;
u32 ready_mask ;
} ;
2019-04-15 19:52:38 +03:00
static int stm32_pwr_reg_is_ready ( struct regulator_dev * rdev )
2019-04-15 12:17:38 +03:00
{
struct stm32_pwr_reg * priv = rdev_get_drvdata ( rdev ) ;
u32 val ;
val = readl_relaxed ( priv - > base + REG_PWR_CR3 ) ;
return ( val & priv - > ready_mask ) ;
}
2019-04-15 19:52:38 +03:00
static int stm32_pwr_reg_is_enabled ( struct regulator_dev * rdev )
2019-04-15 12:17:38 +03:00
{
struct stm32_pwr_reg * priv = rdev_get_drvdata ( rdev ) ;
u32 val ;
val = readl_relaxed ( priv - > base + REG_PWR_CR3 ) ;
2019-04-30 14:13:45 +03:00
return ( val & rdev - > desc - > enable_mask ) ;
2019-04-15 12:17:38 +03:00
}
static int stm32_pwr_reg_enable ( struct regulator_dev * rdev )
{
struct stm32_pwr_reg * priv = rdev_get_drvdata ( rdev ) ;
int ret ;
u32 val ;
val = readl_relaxed ( priv - > base + REG_PWR_CR3 ) ;
2019-04-30 14:13:45 +03:00
val | = rdev - > desc - > enable_mask ;
2019-04-15 12:17:38 +03:00
writel_relaxed ( val , priv - > base + REG_PWR_CR3 ) ;
/* use an arbitrary timeout of 20ms */
ret = readx_poll_timeout ( stm32_pwr_reg_is_ready , rdev , val , val ,
100 , 20 * 1000 ) ;
if ( ret )
dev_err ( & rdev - > dev , " regulator enable timed out! \n " ) ;
return ret ;
}
static int stm32_pwr_reg_disable ( struct regulator_dev * rdev )
{
struct stm32_pwr_reg * priv = rdev_get_drvdata ( rdev ) ;
int ret ;
u32 val ;
val = readl_relaxed ( priv - > base + REG_PWR_CR3 ) ;
2019-04-30 14:13:45 +03:00
val & = ~ rdev - > desc - > enable_mask ;
2019-04-15 12:17:38 +03:00
writel_relaxed ( val , priv - > base + REG_PWR_CR3 ) ;
/* use an arbitrary timeout of 20ms */
regulator: stm32-pwr: Fix regulator disabling
The following shows up in the kernel log on systems using the STM32MP15xx USBPHYC:
"
regulator regulator.19: regulator disable timed out!
reg18: failed to disable: -ETIMEDOUT
"
This 'regulator.19' is 'pwr@50001000' 'reg18' in stm32mp151.dts DT, or
"Power control (PWR)" register "PWR_CR3" bits "REG18" on STM32MP15xx
reference manual.
The reason for the timeout seems to be the poll which this patch changes.
When turning this regulator OFF, PWR_CR3 reads 0xf0000000 , then REG18_EN
bit is cleared, and then this poll waits until REG18_RDY bit is cleared as
well, but that never happens, the PWR_CR3 keeps reading 0xe0000000 .
I am not sure whether this should happen, I suspect the 1V8 supply is
always READY when the 1V8 input is present, and the regulator can only
ever be enabled/disabled using the REG18_EN bit, but the REG18_READY
bit is never cleared again.
This patch adjusts the poll to check whether REG18_EN has been cleared
on regulator disable, but retains the check for REG18_READY in regulator
enable as there it makes sense to verify the regulator is really READY.
Signed-off-by: Marek Vasut <marex@denx.de>
Link: https://lore.kernel.org/r/20230518023946.530381-1-marex@denx.de
Signed-off-by: Mark Brown <broonie@kernel.org>
2023-05-18 05:39:46 +03:00
ret = readx_poll_timeout ( stm32_pwr_reg_is_enabled , rdev , val , ! val ,
2019-04-15 12:17:38 +03:00
100 , 20 * 1000 ) ;
if ( ret )
dev_err ( & rdev - > dev , " regulator disable timed out! \n " ) ;
return ret ;
}
static const struct regulator_ops stm32_pwr_reg_ops = {
. enable = stm32_pwr_reg_enable ,
. disable = stm32_pwr_reg_disable ,
. is_enabled = stm32_pwr_reg_is_enabled ,
} ;
# define PWR_REG(_id, _name, _volt, _en, _supply) \
[ _id ] = { \
. id = _id , \
. name = _name , \
. of_match = of_match_ptr ( _name ) , \
. n_voltages = 1 , \
. type = REGULATOR_VOLTAGE , \
. fixed_uV = _volt , \
. ops = & stm32_pwr_reg_ops , \
. enable_mask = _en , \
. owner = THIS_MODULE , \
. supply_name = _supply , \
} \
static const struct regulator_desc stm32_pwr_desc [ ] = {
PWR_REG ( PWR_REG11 , " reg11 " , 1100000 , REG_1_1_EN , " vdd " ) ,
PWR_REG ( PWR_REG18 , " reg18 " , 1800000 , REG_1_8_EN , " vdd " ) ,
PWR_REG ( PWR_USB33 , " usb33 " , 3300000 , USB_3_3_EN , " vdd_3v3_usbfs " ) ,
} ;
static int stm32_pwr_regulator_probe ( struct platform_device * pdev )
{
struct stm32_pwr_reg * priv ;
void __iomem * base ;
struct regulator_dev * rdev ;
struct regulator_config config = { } ;
int i , ret = 0 ;
2023-04-12 06:35:29 +03:00
base = devm_platform_ioremap_resource ( pdev , 0 ) ;
if ( IS_ERR ( base ) ) {
2019-04-15 12:17:38 +03:00
dev_err ( & pdev - > dev , " Unable to map IO memory \n " ) ;
2023-04-12 06:35:29 +03:00
return PTR_ERR ( base ) ;
2019-04-15 12:17:38 +03:00
}
config . dev = & pdev - > dev ;
for ( i = 0 ; i < STM32PWR_REG_NUM_REGS ; i + + ) {
priv = devm_kzalloc ( & pdev - > dev , sizeof ( struct stm32_pwr_reg ) ,
GFP_KERNEL ) ;
if ( ! priv )
return - ENOMEM ;
priv - > base = base ;
priv - > ready_mask = ready_mask_table [ i ] ;
config . driver_data = priv ;
rdev = devm_regulator_register ( & pdev - > dev ,
& stm32_pwr_desc [ i ] ,
& config ) ;
if ( IS_ERR ( rdev ) ) {
ret = PTR_ERR ( rdev ) ;
dev_err ( & pdev - > dev ,
" Failed to register regulator: %d \n " , ret ) ;
break ;
}
}
return ret ;
}
2020-08-21 06:17:26 +03:00
static const struct of_device_id __maybe_unused stm32_pwr_of_match [ ] = {
2019-04-15 12:17:38 +03:00
{ . compatible = " st,stm32mp1,pwr-reg " , } ,
{ } ,
} ;
MODULE_DEVICE_TABLE ( of , stm32_pwr_of_match ) ;
static struct platform_driver stm32_pwr_driver = {
. probe = stm32_pwr_regulator_probe ,
. driver = {
. name = " stm32-pwr-regulator " ,
2023-03-16 22:54:40 +03:00
. probe_type = PROBE_PREFER_ASYNCHRONOUS ,
2019-04-15 12:17:38 +03:00
. of_match_table = of_match_ptr ( stm32_pwr_of_match ) ,
} ,
} ;
module_platform_driver ( stm32_pwr_driver ) ;
MODULE_DESCRIPTION ( " STM32MP1 PWR voltage regulator driver " ) ;
MODULE_AUTHOR ( " Pascal Paillet <p.paillet@st.com> " ) ;