| // SPDX-License-Identifier: GPL-2.0-only | 
 | /* | 
 |  * Copyright (C) ST-Ericsson SA 2010 | 
 |  * | 
 |  * Authors: Bengt Jonsson <bengt.g.jonsson@stericsson.com> | 
 |  * | 
 |  * This file is based on drivers/regulator/ab8500.c | 
 |  * | 
 |  * AB8500 external regulators | 
 |  * | 
 |  * ab8500-ext supports the following regulators: | 
 |  * - VextSupply3 | 
 |  */ | 
 | #include <linux/init.h> | 
 | #include <linux/kernel.h> | 
 | #include <linux/err.h> | 
 | #include <linux/module.h> | 
 | #include <linux/of.h> | 
 | #include <linux/platform_device.h> | 
 | #include <linux/regulator/driver.h> | 
 | #include <linux/regulator/machine.h> | 
 | #include <linux/regulator/of_regulator.h> | 
 | #include <linux/mfd/abx500.h> | 
 | #include <linux/mfd/abx500/ab8500.h> | 
 |  | 
 | /* AB8500 external regulators */ | 
 | enum ab8500_ext_regulator_id { | 
 | 	AB8500_EXT_SUPPLY1, | 
 | 	AB8500_EXT_SUPPLY2, | 
 | 	AB8500_EXT_SUPPLY3, | 
 | 	AB8500_NUM_EXT_REGULATORS, | 
 | }; | 
 |  | 
 | struct ab8500_ext_regulator_cfg { | 
 | 	bool hwreq; /* requires hw mode or high power mode */ | 
 | }; | 
 |  | 
 | /* supply for VextSupply3 */ | 
 | static struct regulator_consumer_supply ab8500_ext_supply3_consumers[] = { | 
 | 	/* SIM supply for 3 V SIM cards */ | 
 | 	REGULATOR_SUPPLY("vinvsim", "sim-detect.0"), | 
 | }; | 
 |  | 
 | /* | 
 |  * AB8500 external regulators | 
 |  */ | 
 | static struct regulator_init_data ab8500_ext_regulators[] = { | 
 | 	/* fixed Vbat supplies VSMPS1_EXT_1V8 */ | 
 | 	[AB8500_EXT_SUPPLY1] = { | 
 | 		.constraints = { | 
 | 			.name = "ab8500-ext-supply1", | 
 | 			.min_uV = 1800000, | 
 | 			.max_uV = 1800000, | 
 | 			.initial_mode = REGULATOR_MODE_IDLE, | 
 | 			.boot_on = 1, | 
 | 			.always_on = 1, | 
 | 		}, | 
 | 	}, | 
 | 	/* fixed Vbat supplies VSMPS2_EXT_1V36 and VSMPS5_EXT_1V15 */ | 
 | 	[AB8500_EXT_SUPPLY2] = { | 
 | 		.constraints = { | 
 | 			.name = "ab8500-ext-supply2", | 
 | 			.min_uV = 1360000, | 
 | 			.max_uV = 1360000, | 
 | 		}, | 
 | 	}, | 
 | 	/* fixed Vbat supplies VSMPS3_EXT_3V4 and VSMPS4_EXT_3V4 */ | 
 | 	[AB8500_EXT_SUPPLY3] = { | 
 | 		.constraints = { | 
 | 			.name = "ab8500-ext-supply3", | 
 | 			.min_uV = 3400000, | 
 | 			.max_uV = 3400000, | 
 | 			.valid_ops_mask = REGULATOR_CHANGE_STATUS, | 
 | 			.boot_on = 1, | 
 | 		}, | 
 | 		.num_consumer_supplies = | 
 | 			ARRAY_SIZE(ab8500_ext_supply3_consumers), | 
 | 		.consumer_supplies = ab8500_ext_supply3_consumers, | 
 | 	}, | 
 | }; | 
 |  | 
 | /** | 
 |  * struct ab8500_ext_regulator_info - ab8500 regulator information | 
 |  * @dev: device pointer | 
 |  * @desc: regulator description | 
 |  * @cfg: regulator configuration (extension of regulator FW configuration) | 
 |  * @update_bank: bank to control on/off | 
 |  * @update_reg: register to control on/off | 
 |  * @update_mask: mask to enable/disable and set mode of regulator | 
 |  * @update_val: bits holding the regulator current mode | 
 |  * @update_val_hp: bits to set EN pin active (LPn pin deactive) | 
 |  *                 normally this means high power mode | 
 |  * @update_val_lp: bits to set EN pin active and LPn pin active | 
 |  *                 normally this means low power mode | 
 |  * @update_val_hw: bits to set regulator pins in HW control | 
 |  *                 SysClkReq pins and logic will choose mode | 
 |  */ | 
 | struct ab8500_ext_regulator_info { | 
 | 	struct device *dev; | 
 | 	struct regulator_desc desc; | 
 | 	struct ab8500_ext_regulator_cfg *cfg; | 
 | 	u8 update_bank; | 
 | 	u8 update_reg; | 
 | 	u8 update_mask; | 
 | 	u8 update_val; | 
 | 	u8 update_val_hp; | 
 | 	u8 update_val_lp; | 
 | 	u8 update_val_hw; | 
 | }; | 
 |  | 
 | static int ab8500_ext_regulator_enable(struct regulator_dev *rdev) | 
 | { | 
 | 	int ret; | 
 | 	struct ab8500_ext_regulator_info *info = rdev_get_drvdata(rdev); | 
 | 	u8 regval; | 
 |  | 
 | 	if (info == NULL) { | 
 | 		dev_err(rdev_get_dev(rdev), "regulator info null pointer\n"); | 
 | 		return -EINVAL; | 
 | 	} | 
 |  | 
 | 	/* | 
 | 	 * To satisfy both HW high power request and SW request, the regulator | 
 | 	 * must be on in high power. | 
 | 	 */ | 
 | 	if (info->cfg && info->cfg->hwreq) | 
 | 		regval = info->update_val_hp; | 
 | 	else | 
 | 		regval = info->update_val; | 
 |  | 
 | 	ret = abx500_mask_and_set_register_interruptible(info->dev, | 
 | 		info->update_bank, info->update_reg, | 
 | 		info->update_mask, regval); | 
 | 	if (ret < 0) { | 
 | 		dev_err(rdev_get_dev(rdev), | 
 | 			"couldn't set enable bits for regulator\n"); | 
 | 		return ret; | 
 | 	} | 
 |  | 
 | 	dev_dbg(rdev_get_dev(rdev), | 
 | 		"%s-enable (bank, reg, mask, value): 0x%02x, 0x%02x, 0x%02x, 0x%02x\n", | 
 | 		info->desc.name, info->update_bank, info->update_reg, | 
 | 		info->update_mask, regval); | 
 |  | 
 | 	return 0; | 
 | } | 
 |  | 
 | static int ab8500_ext_regulator_disable(struct regulator_dev *rdev) | 
 | { | 
 | 	int ret; | 
 | 	struct ab8500_ext_regulator_info *info = rdev_get_drvdata(rdev); | 
 | 	u8 regval; | 
 |  | 
 | 	if (info == NULL) { | 
 | 		dev_err(rdev_get_dev(rdev), "regulator info null pointer\n"); | 
 | 		return -EINVAL; | 
 | 	} | 
 |  | 
 | 	/* | 
 | 	 * Set the regulator in HW request mode if configured | 
 | 	 */ | 
 | 	if (info->cfg && info->cfg->hwreq) | 
 | 		regval = info->update_val_hw; | 
 | 	else | 
 | 		regval = 0; | 
 |  | 
 | 	ret = abx500_mask_and_set_register_interruptible(info->dev, | 
 | 		info->update_bank, info->update_reg, | 
 | 		info->update_mask, regval); | 
 | 	if (ret < 0) { | 
 | 		dev_err(rdev_get_dev(rdev), | 
 | 			"couldn't set disable bits for regulator\n"); | 
 | 		return ret; | 
 | 	} | 
 |  | 
 | 	dev_dbg(rdev_get_dev(rdev), "%s-disable (bank, reg, mask, value):" | 
 | 		" 0x%02x, 0x%02x, 0x%02x, 0x%02x\n", | 
 | 		info->desc.name, info->update_bank, info->update_reg, | 
 | 		info->update_mask, regval); | 
 |  | 
 | 	return 0; | 
 | } | 
 |  | 
 | static int ab8500_ext_regulator_is_enabled(struct regulator_dev *rdev) | 
 | { | 
 | 	int ret; | 
 | 	struct ab8500_ext_regulator_info *info = rdev_get_drvdata(rdev); | 
 | 	u8 regval; | 
 |  | 
 | 	if (info == NULL) { | 
 | 		dev_err(rdev_get_dev(rdev), "regulator info null pointer\n"); | 
 | 		return -EINVAL; | 
 | 	} | 
 |  | 
 | 	ret = abx500_get_register_interruptible(info->dev, | 
 | 		info->update_bank, info->update_reg, ®val); | 
 | 	if (ret < 0) { | 
 | 		dev_err(rdev_get_dev(rdev), | 
 | 			"couldn't read 0x%x register\n", info->update_reg); | 
 | 		return ret; | 
 | 	} | 
 |  | 
 | 	dev_dbg(rdev_get_dev(rdev), "%s-is_enabled (bank, reg, mask, value):" | 
 | 		" 0x%02x, 0x%02x, 0x%02x, 0x%02x\n", | 
 | 		info->desc.name, info->update_bank, info->update_reg, | 
 | 		info->update_mask, regval); | 
 |  | 
 | 	if (((regval & info->update_mask) == info->update_val_lp) || | 
 | 	    ((regval & info->update_mask) == info->update_val_hp)) | 
 | 		return 1; | 
 | 	else | 
 | 		return 0; | 
 | } | 
 |  | 
 | static int ab8500_ext_regulator_set_mode(struct regulator_dev *rdev, | 
 | 					 unsigned int mode) | 
 | { | 
 | 	int ret = 0; | 
 | 	struct ab8500_ext_regulator_info *info = rdev_get_drvdata(rdev); | 
 | 	u8 regval; | 
 |  | 
 | 	if (info == NULL) { | 
 | 		dev_err(rdev_get_dev(rdev), "regulator info null pointer\n"); | 
 | 		return -EINVAL; | 
 | 	} | 
 |  | 
 | 	switch (mode) { | 
 | 	case REGULATOR_MODE_NORMAL: | 
 | 		regval = info->update_val_hp; | 
 | 		break; | 
 | 	case REGULATOR_MODE_IDLE: | 
 | 		regval = info->update_val_lp; | 
 | 		break; | 
 |  | 
 | 	default: | 
 | 		return -EINVAL; | 
 | 	} | 
 |  | 
 | 	/* If regulator is enabled and info->cfg->hwreq is set, the regulator | 
 | 	   must be on in high power, so we don't need to write the register with | 
 | 	   the same value. | 
 | 	 */ | 
 | 	if (ab8500_ext_regulator_is_enabled(rdev) && | 
 | 	    !(info->cfg && info->cfg->hwreq)) { | 
 | 		ret = abx500_mask_and_set_register_interruptible(info->dev, | 
 | 					info->update_bank, info->update_reg, | 
 | 					info->update_mask, regval); | 
 | 		if (ret < 0) { | 
 | 			dev_err(rdev_get_dev(rdev), | 
 | 				"Could not set regulator mode.\n"); | 
 | 			return ret; | 
 | 		} | 
 |  | 
 | 		dev_dbg(rdev_get_dev(rdev), | 
 | 			"%s-set_mode (bank, reg, mask, value): " | 
 | 			"0x%x, 0x%x, 0x%x, 0x%x\n", | 
 | 			info->desc.name, info->update_bank, info->update_reg, | 
 | 			info->update_mask, regval); | 
 | 	} | 
 |  | 
 | 	info->update_val = regval; | 
 |  | 
 | 	return 0; | 
 | } | 
 |  | 
 | static unsigned int ab8500_ext_regulator_get_mode(struct regulator_dev *rdev) | 
 | { | 
 | 	struct ab8500_ext_regulator_info *info = rdev_get_drvdata(rdev); | 
 | 	int ret; | 
 |  | 
 | 	if (info == NULL) { | 
 | 		dev_err(rdev_get_dev(rdev), "regulator info null pointer\n"); | 
 | 		return -EINVAL; | 
 | 	} | 
 |  | 
 | 	if (info->update_val == info->update_val_hp) | 
 | 		ret = REGULATOR_MODE_NORMAL; | 
 | 	else if (info->update_val == info->update_val_lp) | 
 | 		ret = REGULATOR_MODE_IDLE; | 
 | 	else | 
 | 		ret = -EINVAL; | 
 |  | 
 | 	return ret; | 
 | } | 
 |  | 
 | static int ab8500_ext_set_voltage(struct regulator_dev *rdev, int min_uV, | 
 | 				  int max_uV, unsigned *selector) | 
 | { | 
 | 	struct regulation_constraints *regu_constraints = rdev->constraints; | 
 |  | 
 | 	if (!regu_constraints) { | 
 | 		dev_err(rdev_get_dev(rdev), "No regulator constraints\n"); | 
 | 		return -EINVAL; | 
 | 	} | 
 |  | 
 | 	if (regu_constraints->min_uV == min_uV && | 
 | 	    regu_constraints->max_uV == max_uV) | 
 | 		return 0; | 
 |  | 
 | 	dev_err(rdev_get_dev(rdev), | 
 | 		"Requested min %duV max %duV != constrained min %duV max %duV\n", | 
 | 		min_uV, max_uV, | 
 | 		regu_constraints->min_uV, regu_constraints->max_uV); | 
 |  | 
 | 	return -EINVAL; | 
 | } | 
 |  | 
 | static int ab8500_ext_list_voltage(struct regulator_dev *rdev, | 
 | 				   unsigned selector) | 
 | { | 
 | 	struct regulation_constraints *regu_constraints = rdev->constraints; | 
 |  | 
 | 	if (regu_constraints == NULL) { | 
 | 		dev_err(rdev_get_dev(rdev), "regulator constraints null pointer\n"); | 
 | 		return -EINVAL; | 
 | 	} | 
 | 	/* return the uV for the fixed regulators */ | 
 | 	if (regu_constraints->min_uV && regu_constraints->max_uV) { | 
 | 		if (regu_constraints->min_uV == regu_constraints->max_uV) | 
 | 			return regu_constraints->min_uV; | 
 | 	} | 
 | 	return -EINVAL; | 
 | } | 
 |  | 
 | static const struct regulator_ops ab8500_ext_regulator_ops = { | 
 | 	.enable			= ab8500_ext_regulator_enable, | 
 | 	.disable		= ab8500_ext_regulator_disable, | 
 | 	.is_enabled		= ab8500_ext_regulator_is_enabled, | 
 | 	.set_mode		= ab8500_ext_regulator_set_mode, | 
 | 	.get_mode		= ab8500_ext_regulator_get_mode, | 
 | 	.set_voltage		= ab8500_ext_set_voltage, | 
 | 	.list_voltage		= ab8500_ext_list_voltage, | 
 | }; | 
 |  | 
 | static struct ab8500_ext_regulator_info | 
 | 		ab8500_ext_regulator_info[AB8500_NUM_EXT_REGULATORS] = { | 
 | 	[AB8500_EXT_SUPPLY1] = { | 
 | 		.desc = { | 
 | 			.name		= "VEXTSUPPLY1", | 
 | 			.of_match	= of_match_ptr("ab8500_ext1"), | 
 | 			.ops		= &ab8500_ext_regulator_ops, | 
 | 			.type		= REGULATOR_VOLTAGE, | 
 | 			.id		= AB8500_EXT_SUPPLY1, | 
 | 			.owner		= THIS_MODULE, | 
 | 			.n_voltages	= 1, | 
 | 		}, | 
 | 		.update_bank		= 0x04, | 
 | 		.update_reg		= 0x08, | 
 | 		.update_mask		= 0x03, | 
 | 		.update_val		= 0x01, | 
 | 		.update_val_hp		= 0x01, | 
 | 		.update_val_lp		= 0x03, | 
 | 		.update_val_hw		= 0x02, | 
 | 	}, | 
 | 	[AB8500_EXT_SUPPLY2] = { | 
 | 		.desc = { | 
 | 			.name		= "VEXTSUPPLY2", | 
 | 			.of_match	= of_match_ptr("ab8500_ext2"), | 
 | 			.ops		= &ab8500_ext_regulator_ops, | 
 | 			.type		= REGULATOR_VOLTAGE, | 
 | 			.id		= AB8500_EXT_SUPPLY2, | 
 | 			.owner		= THIS_MODULE, | 
 | 			.n_voltages	= 1, | 
 | 		}, | 
 | 		.update_bank		= 0x04, | 
 | 		.update_reg		= 0x08, | 
 | 		.update_mask		= 0x0c, | 
 | 		.update_val		= 0x04, | 
 | 		.update_val_hp		= 0x04, | 
 | 		.update_val_lp		= 0x0c, | 
 | 		.update_val_hw		= 0x08, | 
 | 	}, | 
 | 	[AB8500_EXT_SUPPLY3] = { | 
 | 		.desc = { | 
 | 			.name		= "VEXTSUPPLY3", | 
 | 			.of_match	= of_match_ptr("ab8500_ext3"), | 
 | 			.ops		= &ab8500_ext_regulator_ops, | 
 | 			.type		= REGULATOR_VOLTAGE, | 
 | 			.id		= AB8500_EXT_SUPPLY3, | 
 | 			.owner		= THIS_MODULE, | 
 | 			.n_voltages	= 1, | 
 | 		}, | 
 | 		.update_bank		= 0x04, | 
 | 		.update_reg		= 0x08, | 
 | 		.update_mask		= 0x30, | 
 | 		.update_val		= 0x10, | 
 | 		.update_val_hp		= 0x10, | 
 | 		.update_val_lp		= 0x30, | 
 | 		.update_val_hw		= 0x20, | 
 | 	}, | 
 | }; | 
 |  | 
 | static int ab8500_ext_regulator_probe(struct platform_device *pdev) | 
 | { | 
 | 	struct ab8500 *ab8500 = dev_get_drvdata(pdev->dev.parent); | 
 | 	struct regulator_config config = { }; | 
 | 	struct regulator_dev *rdev; | 
 | 	int i; | 
 |  | 
 | 	if (!ab8500) { | 
 | 		dev_err(&pdev->dev, "null mfd parent\n"); | 
 | 		return -EINVAL; | 
 | 	} | 
 |  | 
 | 	/* check for AB8500 2.x */ | 
 | 	if (is_ab8500_2p0_or_earlier(ab8500)) { | 
 | 		struct ab8500_ext_regulator_info *info; | 
 |  | 
 | 		/* VextSupply3LPn is inverted on AB8500 2.x */ | 
 | 		info = &ab8500_ext_regulator_info[AB8500_EXT_SUPPLY3]; | 
 | 		info->update_val = 0x30; | 
 | 		info->update_val_hp = 0x30; | 
 | 		info->update_val_lp = 0x10; | 
 | 	} | 
 |  | 
 | 	/* register all regulators */ | 
 | 	for (i = 0; i < ARRAY_SIZE(ab8500_ext_regulator_info); i++) { | 
 | 		struct ab8500_ext_regulator_info *info = NULL; | 
 |  | 
 | 		/* assign per-regulator data */ | 
 | 		info = &ab8500_ext_regulator_info[i]; | 
 | 		info->dev = &pdev->dev; | 
 | 		info->cfg = (struct ab8500_ext_regulator_cfg *) | 
 | 			ab8500_ext_regulators[i].driver_data; | 
 |  | 
 | 		config.dev = &pdev->dev; | 
 | 		config.driver_data = info; | 
 | 		config.init_data = &ab8500_ext_regulators[i]; | 
 |  | 
 | 		/* register regulator with framework */ | 
 | 		rdev = devm_regulator_register(&pdev->dev, &info->desc, | 
 | 					       &config); | 
 | 		if (IS_ERR(rdev)) { | 
 | 			dev_err(&pdev->dev, "failed to register regulator %s\n", | 
 | 					info->desc.name); | 
 | 			return PTR_ERR(rdev); | 
 | 		} | 
 |  | 
 | 		dev_dbg(&pdev->dev, "%s-probed\n", info->desc.name); | 
 | 	} | 
 |  | 
 | 	return 0; | 
 | } | 
 |  | 
 | static struct platform_driver ab8500_ext_regulator_driver = { | 
 | 	.probe = ab8500_ext_regulator_probe, | 
 | 	.driver         = { | 
 | 		.name   = "ab8500-ext-regulator", | 
 | 		.probe_type = PROBE_PREFER_ASYNCHRONOUS, | 
 | 	}, | 
 | }; | 
 |  | 
 | static int __init ab8500_ext_regulator_init(void) | 
 | { | 
 | 	int ret; | 
 |  | 
 | 	ret = platform_driver_register(&ab8500_ext_regulator_driver); | 
 | 	if (ret) | 
 | 		pr_err("Failed to register ab8500 ext regulator: %d\n", ret); | 
 |  | 
 | 	return ret; | 
 | } | 
 | subsys_initcall(ab8500_ext_regulator_init); | 
 |  | 
 | static void __exit ab8500_ext_regulator_exit(void) | 
 | { | 
 | 	platform_driver_unregister(&ab8500_ext_regulator_driver); | 
 | } | 
 | module_exit(ab8500_ext_regulator_exit); | 
 |  | 
 | MODULE_LICENSE("GPL v2"); | 
 | MODULE_AUTHOR("Bengt Jonsson <bengt.g.jonsson@stericsson.com>"); | 
 | MODULE_DESCRIPTION("AB8500 external regulator driver"); | 
 | MODULE_ALIAS("platform:ab8500-ext-regulator"); |