forked from mirrors/linux
		
	regulator: Add support for stm32 power regulators
Add support for 1V1 1V8 USB3V3 power regulators. Signed-off-by: Pascal Paillet <p.paillet@st.com> Signed-off-by: Mark Brown <broonie@kernel.org>
This commit is contained in:
		
							parent
							
								
									c29687c354
								
							
						
					
					
						commit
						6cdae8173f
					
				
					 3 changed files with 198 additions and 0 deletions
				
			
		|  | @ -840,6 +840,13 @@ config REGULATOR_STM32_VREFBUF | ||||||
| 	  This driver can also be built as a module. If so, the module | 	  This driver can also be built as a module. If so, the module | ||||||
| 	  will be called stm32-vrefbuf. | 	  will be called stm32-vrefbuf. | ||||||
| 
 | 
 | ||||||
|  | config REGULATOR_STM32_PWR | ||||||
|  | 	bool "STMicroelectronics STM32 PWR" | ||||||
|  | 	depends on ARCH_STM32 || COMPILE_TEST | ||||||
|  | 	help | ||||||
|  | 	  This driver supports internal regulators (1V1, 1V8, 3V3) in the | ||||||
|  | 	  STMicroelectronics STM32 chips. | ||||||
|  | 
 | ||||||
| config REGULATOR_STPMIC1 | config REGULATOR_STPMIC1 | ||||||
| 	tristate "STMicroelectronics STPMIC1 PMIC Regulators" | 	tristate "STMicroelectronics STPMIC1 PMIC Regulators" | ||||||
| 	depends on MFD_STPMIC1 | 	depends on MFD_STPMIC1 | ||||||
|  |  | ||||||
|  | @ -105,6 +105,7 @@ obj-$(CONFIG_REGULATOR_S5M8767) += s5m8767.o | ||||||
| obj-$(CONFIG_REGULATOR_SC2731) += sc2731-regulator.o | obj-$(CONFIG_REGULATOR_SC2731) += sc2731-regulator.o | ||||||
| obj-$(CONFIG_REGULATOR_SKY81452) += sky81452-regulator.o | obj-$(CONFIG_REGULATOR_SKY81452) += sky81452-regulator.o | ||||||
| obj-$(CONFIG_REGULATOR_STM32_VREFBUF) += stm32-vrefbuf.o | obj-$(CONFIG_REGULATOR_STM32_VREFBUF) += stm32-vrefbuf.o | ||||||
|  | obj-$(CONFIG_REGULATOR_STM32_PWR) += stm32-pwr.o | ||||||
| obj-$(CONFIG_REGULATOR_STPMIC1) += stpmic1_regulator.o | obj-$(CONFIG_REGULATOR_STPMIC1) += stpmic1_regulator.o | ||||||
| obj-$(CONFIG_REGULATOR_STW481X_VMMC) += stw481x-vmmc.o | obj-$(CONFIG_REGULATOR_STW481X_VMMC) += stw481x-vmmc.o | ||||||
| obj-$(CONFIG_REGULATOR_SY8106A) += sy8106a-regulator.o | obj-$(CONFIG_REGULATOR_SY8106A) += sy8106a-regulator.o | ||||||
|  |  | ||||||
							
								
								
									
										190
									
								
								drivers/regulator/stm32-pwr.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										190
									
								
								drivers/regulator/stm32-pwr.c
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,190 @@ | ||||||
|  | // 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> | ||||||
|  | #include <linux/of_address.h> | ||||||
|  | #include <linux/of_device.h> | ||||||
|  | #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 | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | u32 ready_mask_table[STM32PWR_REG_NUM_REGS] = { | ||||||
|  | 	[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; | ||||||
|  | 	const struct regulator_desc *desc; | ||||||
|  | 	u32 ready_mask; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | int stm32_pwr_reg_is_ready(struct regulator_dev *rdev) | ||||||
|  | { | ||||||
|  | 	struct stm32_pwr_reg *priv = rdev_get_drvdata(rdev); | ||||||
|  | 	u32 val; | ||||||
|  | 
 | ||||||
|  | 	val = readl_relaxed(priv->base + REG_PWR_CR3); | ||||||
|  | 
 | ||||||
|  | 	return (val & priv->ready_mask); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | int stm32_pwr_reg_is_enabled(struct regulator_dev *rdev) | ||||||
|  | { | ||||||
|  | 	struct stm32_pwr_reg *priv = rdev_get_drvdata(rdev); | ||||||
|  | 	u32 val; | ||||||
|  | 
 | ||||||
|  | 	val = readl_relaxed(priv->base + REG_PWR_CR3); | ||||||
|  | 
 | ||||||
|  | 	return (val & priv->desc->enable_mask); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 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); | ||||||
|  | 	val |= priv->desc->enable_mask; | ||||||
|  | 	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); | ||||||
|  | 	val &= ~priv->desc->enable_mask; | ||||||
|  | 	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 disable timed out!\n"); | ||||||
|  | 
 | ||||||
|  | 	return ret; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static const struct regulator_ops stm32_pwr_reg_ops = { | ||||||
|  | 	.list_voltage	= regulator_list_voltage_linear, | ||||||
|  | 	.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, \ | ||||||
|  | 		.min_uV = _volt, \ | ||||||
|  | 		.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 device_node *np = pdev->dev.of_node; | ||||||
|  | 	struct stm32_pwr_reg *priv; | ||||||
|  | 	void __iomem *base; | ||||||
|  | 	struct regulator_dev *rdev; | ||||||
|  | 	struct regulator_config config = { }; | ||||||
|  | 	int i, ret = 0; | ||||||
|  | 
 | ||||||
|  | 	base = of_iomap(np, 0); | ||||||
|  | 	if (IS_ERR(base)) { | ||||||
|  | 		dev_err(&pdev->dev, "Unable to map IO memory\n"); | ||||||
|  | 		return PTR_ERR(base); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	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->desc = &stm32_pwr_desc[i]; | ||||||
|  | 		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; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static const struct of_device_id stm32_pwr_of_match[] = { | ||||||
|  | 	{ .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", | ||||||
|  | 		.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>"); | ||||||
|  | MODULE_LICENSE("GPL v2"); | ||||||
		Loading…
	
		Reference in a new issue
	
	 Pascal PAILLET-LME
						Pascal PAILLET-LME