| // SPDX-License-Identifier: GPL-2.0-only | 
 | /* | 
 |  * pmic-cpcap.c - CPCAP-specific functions for the OPP code | 
 |  * | 
 |  * Adapted from Motorola Mapphone Android Linux kernel | 
 |  * Copyright (C) 2011 Motorola, Inc. | 
 |  */ | 
 |  | 
 | #include <linux/err.h> | 
 | #include <linux/io.h> | 
 | #include <linux/kernel.h> | 
 |  | 
 | #include "soc.h" | 
 | #include "pm.h" | 
 | #include "voltage.h" | 
 |  | 
 | #include <linux/init.h> | 
 | #include "vc.h" | 
 |  | 
 | /** | 
 |  * omap_cpcap_vsel_to_uv - convert CPCAP VSEL value to microvolts DC | 
 |  * @vsel: CPCAP VSEL value to convert | 
 |  * | 
 |  * Returns: the microvolts DC that the CPCAP PMIC should generate when | 
 |  * programmed with @vsel. | 
 |  */ | 
 | static unsigned long omap_cpcap_vsel_to_uv(unsigned char vsel) | 
 | { | 
 | 	if (vsel > 0x44) | 
 | 		vsel = 0x44; | 
 | 	return (((vsel * 125) + 6000)) * 100; | 
 | } | 
 |  | 
 | /** | 
 |  * omap_cpcap_uv_to_vsel - convert microvolts DC to CPCAP VSEL value | 
 |  * @uv: microvolts DC to convert | 
 |  * | 
 |  * Returns: the VSEL value necessary for the CPCAP PMIC to | 
 |  * generate an output voltage equal to or greater than @uv microvolts DC. | 
 |  */ | 
 | static unsigned char omap_cpcap_uv_to_vsel(unsigned long uv) | 
 | { | 
 | 	if (uv < 600000) | 
 | 		uv = 600000; | 
 | 	else if (uv > 1450000) | 
 | 		uv = 1450000; | 
 | 	return DIV_ROUND_UP(uv - 600000, 12500); | 
 | } | 
 |  | 
 | static struct omap_voltdm_pmic omap_cpcap_core = { | 
 | 	.slew_rate = 4000, | 
 | 	.step_size = 12500, | 
 | 	.vp_erroroffset = OMAP4_VP_CONFIG_ERROROFFSET, | 
 | 	.vp_vstepmin = OMAP4_VP_VSTEPMIN_VSTEPMIN, | 
 | 	.vp_vstepmax = OMAP4_VP_VSTEPMAX_VSTEPMAX, | 
 | 	.vddmin = 900000, | 
 | 	.vddmax = 1350000, | 
 | 	.vp_timeout_us = OMAP4_VP_VLIMITTO_TIMEOUT_US, | 
 | 	.i2c_slave_addr = 0x02, | 
 | 	.volt_reg_addr = 0x00, | 
 | 	.cmd_reg_addr = 0x01, | 
 | 	.i2c_high_speed = false, | 
 | 	.vsel_to_uv = omap_cpcap_vsel_to_uv, | 
 | 	.uv_to_vsel = omap_cpcap_uv_to_vsel, | 
 | }; | 
 |  | 
 | static struct omap_voltdm_pmic omap_cpcap_iva = { | 
 | 	.slew_rate = 4000, | 
 | 	.step_size = 12500, | 
 | 	.vp_erroroffset = OMAP4_VP_CONFIG_ERROROFFSET, | 
 | 	.vp_vstepmin = OMAP4_VP_VSTEPMIN_VSTEPMIN, | 
 | 	.vp_vstepmax = OMAP4_VP_VSTEPMAX_VSTEPMAX, | 
 | 	.vddmin = 900000, | 
 | 	.vddmax = 1375000, | 
 | 	.vp_timeout_us = OMAP4_VP_VLIMITTO_TIMEOUT_US, | 
 | 	.i2c_slave_addr = 0x44, | 
 | 	.volt_reg_addr = 0x0, | 
 | 	.cmd_reg_addr = 0x01, | 
 | 	.i2c_high_speed = false, | 
 | 	.vsel_to_uv = omap_cpcap_vsel_to_uv, | 
 | 	.uv_to_vsel = omap_cpcap_uv_to_vsel, | 
 | }; | 
 |  | 
 | /** | 
 |  * omap_max8952_vsel_to_uv - convert MAX8952 VSEL value to microvolts DC | 
 |  * @vsel: MAX8952 VSEL value to convert | 
 |  * | 
 |  * Returns: the microvolts DC that the MAX8952 Regulator should generate when | 
 |  * programmed with @vsel. | 
 |  */ | 
 | static unsigned long omap_max8952_vsel_to_uv(unsigned char vsel) | 
 | { | 
 | 	if (vsel > 0x3F) | 
 | 		vsel = 0x3F; | 
 | 	return (((vsel * 100) + 7700)) * 100; | 
 | } | 
 |  | 
 | /** | 
 |  * omap_max8952_uv_to_vsel - convert microvolts DC to MAX8952 VSEL value | 
 |  * @uv: microvolts DC to convert | 
 |  * | 
 |  * Returns: the VSEL value necessary for the MAX8952 Regulator to | 
 |  * generate an output voltage equal to or greater than @uv microvolts DC. | 
 |  */ | 
 | static unsigned char omap_max8952_uv_to_vsel(unsigned long uv) | 
 | { | 
 | 	if (uv < 770000) | 
 | 		uv = 770000; | 
 | 	else if (uv > 1400000) | 
 | 		uv = 1400000; | 
 | 	return DIV_ROUND_UP(uv - 770000, 10000); | 
 | } | 
 |  | 
 | static struct omap_voltdm_pmic omap443x_max8952_mpu = { | 
 | 	.slew_rate = 16000, | 
 | 	.step_size = 10000, | 
 | 	.vp_erroroffset = OMAP4_VP_CONFIG_ERROROFFSET, | 
 | 	.vp_vstepmin = OMAP4_VP_VSTEPMIN_VSTEPMIN, | 
 | 	.vp_vstepmax = OMAP4_VP_VSTEPMAX_VSTEPMAX, | 
 | 	.vddmin = 900000, | 
 | 	.vddmax = 1400000, | 
 | 	.vp_timeout_us = OMAP4_VP_VLIMITTO_TIMEOUT_US, | 
 | 	.i2c_slave_addr = 0x60, | 
 | 	.volt_reg_addr = 0x03, | 
 | 	.cmd_reg_addr = 0x03, | 
 | 	.i2c_high_speed = false, | 
 | 	.vsel_to_uv = omap_max8952_vsel_to_uv, | 
 | 	.uv_to_vsel = omap_max8952_uv_to_vsel, | 
 | }; | 
 |  | 
 | /** | 
 |  * omap_fan535503_vsel_to_uv - convert FAN535503 VSEL value to microvolts DC | 
 |  * @vsel: FAN535503 VSEL value to convert | 
 |  * | 
 |  * Returns: the microvolts DC that the FAN535503 Regulator should generate when | 
 |  * programmed with @vsel. | 
 |  */ | 
 | static unsigned long omap_fan535503_vsel_to_uv(unsigned char vsel) | 
 | { | 
 | 	/* Extract bits[5:0] */ | 
 | 	vsel &= 0x3F; | 
 |  | 
 | 	return (((vsel * 125) + 7500)) * 100; | 
 | } | 
 |  | 
 | /** | 
 |  * omap_fan535508_vsel_to_uv - convert FAN535508 VSEL value to microvolts DC | 
 |  * @vsel: FAN535508 VSEL value to convert | 
 |  * | 
 |  * Returns: the microvolts DC that the FAN535508 Regulator should generate when | 
 |  * programmed with @vsel. | 
 |  */ | 
 | static unsigned long omap_fan535508_vsel_to_uv(unsigned char vsel) | 
 | { | 
 | 	/* Extract bits[5:0] */ | 
 | 	vsel &= 0x3F; | 
 |  | 
 | 	if (vsel > 0x37) | 
 | 		vsel = 0x37; | 
 | 	return (((vsel * 125) + 7500)) * 100; | 
 | } | 
 |  | 
 |  | 
 | /** | 
 |  * omap_fan535503_uv_to_vsel - convert microvolts DC to FAN535503 VSEL value | 
 |  * @uv: microvolts DC to convert | 
 |  * | 
 |  * Returns: the VSEL value necessary for the MAX8952 Regulator to | 
 |  * generate an output voltage equal to or greater than @uv microvolts DC. | 
 |  */ | 
 | static unsigned char omap_fan535503_uv_to_vsel(unsigned long uv) | 
 | { | 
 | 	unsigned char vsel; | 
 | 	if (uv < 750000) | 
 | 		uv = 750000; | 
 | 	else if (uv > 1537500) | 
 | 		uv = 1537500; | 
 |  | 
 | 	vsel = DIV_ROUND_UP(uv - 750000, 12500); | 
 | 	return vsel | 0xC0; | 
 | } | 
 |  | 
 | /** | 
 |  * omap_fan535508_uv_to_vsel - convert microvolts DC to FAN535508 VSEL value | 
 |  * @uv: microvolts DC to convert | 
 |  * | 
 |  * Returns: the VSEL value necessary for the MAX8952 Regulator to | 
 |  * generate an output voltage equal to or greater than @uv microvolts DC. | 
 |  */ | 
 | static unsigned char omap_fan535508_uv_to_vsel(unsigned long uv) | 
 | { | 
 | 	unsigned char vsel; | 
 | 	if (uv < 750000) | 
 | 		uv = 750000; | 
 | 	else if (uv > 1437500) | 
 | 		uv = 1437500; | 
 |  | 
 | 	vsel = DIV_ROUND_UP(uv - 750000, 12500); | 
 | 	return vsel | 0xC0; | 
 | } | 
 |  | 
 | /* fan5335-core */ | 
 | static struct omap_voltdm_pmic omap4_fan_core = { | 
 | 	.slew_rate = 4000, | 
 | 	.step_size = 12500, | 
 | 	.vp_erroroffset = OMAP4_VP_CONFIG_ERROROFFSET, | 
 | 	.vp_vstepmin = OMAP4_VP_VSTEPMIN_VSTEPMIN, | 
 | 	.vp_vstepmax = OMAP4_VP_VSTEPMAX_VSTEPMAX, | 
 | 	.vddmin = 850000, | 
 | 	.vddmax = 1375000, | 
 | 	.vp_timeout_us = OMAP4_VP_VLIMITTO_TIMEOUT_US, | 
 | 	.i2c_slave_addr = 0x4A, | 
 | 	.i2c_high_speed = false, | 
 | 	.volt_reg_addr = 0x01, | 
 | 	.cmd_reg_addr = 0x01, | 
 | 	.vsel_to_uv = omap_fan535508_vsel_to_uv, | 
 | 	.uv_to_vsel = omap_fan535508_uv_to_vsel, | 
 | }; | 
 |  | 
 | /* fan5335 iva */ | 
 | static struct omap_voltdm_pmic omap4_fan_iva = { | 
 | 	.slew_rate = 4000, | 
 | 	.step_size = 12500, | 
 | 	.vp_erroroffset = OMAP4_VP_CONFIG_ERROROFFSET, | 
 | 	.vp_vstepmin = OMAP4_VP_VSTEPMIN_VSTEPMIN, | 
 | 	.vp_vstepmax = OMAP4_VP_VSTEPMAX_VSTEPMAX, | 
 | 	.vddmin = 850000, | 
 | 	.vddmax = 1375000, | 
 | 	.vp_timeout_us = OMAP4_VP_VLIMITTO_TIMEOUT_US, | 
 | 	.i2c_slave_addr = 0x48, | 
 | 	.volt_reg_addr = 0x01, | 
 | 	.cmd_reg_addr = 0x01, | 
 | 	.i2c_high_speed = false, | 
 | 	.vsel_to_uv = omap_fan535503_vsel_to_uv, | 
 | 	.uv_to_vsel = omap_fan535503_uv_to_vsel, | 
 | }; | 
 |  | 
 | int __init omap4_cpcap_init(void) | 
 | { | 
 | 	struct voltagedomain *voltdm; | 
 |  | 
 | 	if (!of_find_compatible_node(NULL, NULL, "motorola,cpcap")) | 
 | 		return -ENODEV; | 
 |  | 
 | 	voltdm = voltdm_lookup("mpu"); | 
 | 	omap_voltage_register_pmic(voltdm, &omap443x_max8952_mpu); | 
 |  | 
 | 	if (of_machine_is_compatible("motorola,droid-bionic")) { | 
 | 		voltdm = voltdm_lookup("core"); | 
 | 		omap_voltage_register_pmic(voltdm, &omap_cpcap_core); | 
 |  | 
 | 		voltdm = voltdm_lookup("iva"); | 
 | 		omap_voltage_register_pmic(voltdm, &omap_cpcap_iva); | 
 | 	} else { | 
 | 		voltdm = voltdm_lookup("core"); | 
 | 		omap_voltage_register_pmic(voltdm, &omap4_fan_core); | 
 |  | 
 | 		voltdm = voltdm_lookup("iva"); | 
 | 		omap_voltage_register_pmic(voltdm, &omap4_fan_iva); | 
 | 	} | 
 |  | 
 | 	return 0; | 
 | } | 
 |  | 
 | static int __init cpcap_late_init(void) | 
 | { | 
 | 	if (!of_find_compatible_node(NULL, NULL, "motorola,cpcap")) | 
 | 		return 0; | 
 |  | 
 | 	if (soc_is_omap443x() || soc_is_omap446x() || soc_is_omap447x()) | 
 | 		omap4_vc_set_pmic_signaling(PWRDM_POWER_RET); | 
 |  | 
 | 	return 0; | 
 | } | 
 | omap_late_initcall(cpcap_late_init); |