Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 2e6456de authored by Linux Build Service Account's avatar Linux Build Service Account Committed by Gerrit - the friendly Code Review server
Browse files

Merge "drivers: soc: qcom: spm: Add support for 16bit vlevel"

parents 98ecaddf 7499f135
Loading
Loading
Loading
Loading
+4 −0
Original line number Original line Diff line number Diff line
@@ -45,6 +45,8 @@ Optional properties for only Non-PSCI targets
	index to send the PMIC data to
	index to send the PMIC data to
- qcom,vctl-port: The PVC (PMIC Virtual Channel) port used for changing
- qcom,vctl-port: The PVC (PMIC Virtual Channel) port used for changing
	voltage
	voltage
- qcom,vctl-port-ub: The PVC (PMIC Virtual Channel) port used for changing
	voltage
- qcom,phase-port: The PVC port used for changing the number of phases
- qcom,phase-port: The PVC port used for changing the number of phases
- qcom,pfm-port: The PVC port used for enabling PWM/PFM modes
- qcom,pfm-port: The PVC port used for enabling PWM/PFM modes
- qcom,cpu-vctl-mask: Mask of cpus, whose voltage the spm device can control.
- qcom,cpu-vctl-mask: Mask of cpus, whose voltage the spm device can control.
@@ -107,6 +109,8 @@ Optional properties for only PSCI targets:
	between AVS controller requests
	between AVS controller requests
- qcom,vctl-port: The PVC (PMIC Virtual Channel) port used for changing
- qcom,vctl-port: The PVC (PMIC Virtual Channel) port used for changing
	voltage
	voltage
- qcom,vctl-port-ub: The PVC (PMIC Virtual Channel) port used for changing
	voltage
- qcom,phase-port: The PVC port used for changing the number of phases
- qcom,phase-port: The PVC port used for changing the number of phases
- qcom,pfm-port: The PVC port used for enabling PWM/PFM modes
- qcom,pfm-port: The PVC port used for enabling PWM/PFM modes
- qcom,cpu-vctl-list: List of cpu node phandles, whose voltage the spm device
- qcom,cpu-vctl-list: List of cpu node phandles, whose voltage the spm device
+79 −31
Original line number Original line Diff line number Diff line
/* Copyright (c) 2011-2016, The Linux Foundation. All rights reserved.
/* Copyright (c) 2011-2017, The Linux Foundation. All rights reserved.
 *
 *
 * This program is free software; you can redistribute it and/or modify
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 and
 * it under the terms of the GNU General Public License version 2 and
@@ -190,18 +190,12 @@ static inline bool msm_spm_pmic_arb_present(struct msm_spm_driver_data *dev)
}
}


static inline void msm_spm_drv_set_vctl2(struct msm_spm_driver_data *dev,
static inline void msm_spm_drv_set_vctl2(struct msm_spm_driver_data *dev,
		uint32_t vlevel)
				uint32_t vlevel, uint32_t vctl_port)
{
{
	unsigned int pmic_data = 0;
	unsigned int pmic_data = 0;


	/**
	 * VCTL_PORT has to be 0, for PMIC_STS register to be updated.
	 * Ensure that vctl_port is always set to 0.
	 */
	WARN_ON(dev->vctl_port);

	pmic_data |= vlevel;
	pmic_data |= vlevel;
	pmic_data |= (dev->vctl_port & 0x7) << 16;
	pmic_data |= (vctl_port & 0x7) << 16;


	dev->reg_shadow[MSM_SPM_REG_SAW_VCTL] &= ~0x700FF;
	dev->reg_shadow[MSM_SPM_REG_SAW_VCTL] &= ~0x700FF;
	dev->reg_shadow[MSM_SPM_REG_SAW_VCTL] |= pmic_data;
	dev->reg_shadow[MSM_SPM_REG_SAW_VCTL] |= pmic_data;
@@ -506,10 +500,45 @@ static void msm_spm_drv_set_avs_vlevel(struct msm_spm_driver_data *dev,
		unsigned int vlevel) { }
		unsigned int vlevel) { }
#endif
#endif


static inline int msm_spm_drv_validate_data(struct msm_spm_driver_data *dev,
					unsigned int vlevel, int vctl_port)
{
	int timeout_us = dev->vctl_timeout_us;
	uint32_t new_level;

	/* Confirm the voltage we set was what hardware sent and
	 * FSM is idle */
	do {
		udelay(1);
		new_level = msm_spm_drv_get_sts_curr_pmic_data(dev);

		/**
		 * VCTL_PORT has to be 0, for vlevel to be updated.
		 * If port is not 0, check for PMIC_STATE only.
		 */

		if (((new_level & 0x30000) == MSM_SPM_PMIC_STATE_IDLE) &&
				(vctl_port || ((new_level & 0xFF) == vlevel)))
			break;
	} while (--timeout_us);

	if (!timeout_us) {
		pr_err("Wrong level %#x\n", new_level);
		return -EIO;
	}

	if (msm_spm_debug_mask & MSM_SPM_DEBUG_VCTL)
		pr_info("%s: done, remaining timeout %u us\n",
			__func__, timeout_us);

	return 0;
}

int msm_spm_drv_set_vdd(struct msm_spm_driver_data *dev, unsigned int vlevel)
int msm_spm_drv_set_vdd(struct msm_spm_driver_data *dev, unsigned int vlevel)
{
{
	uint32_t timeout_us, new_level;
	uint32_t vlevel_set = vlevel;
	bool avs_enabled;
	bool avs_enabled;
	int ret = 0;


	if (!dev)
	if (!dev)
		return -EINVAL;
		return -EINVAL;
@@ -525,45 +554,63 @@ int msm_spm_drv_set_vdd(struct msm_spm_driver_data *dev, unsigned int vlevel)
	if (avs_enabled)
	if (avs_enabled)
		msm_spm_drv_disable_avs(dev);
		msm_spm_drv_disable_avs(dev);


	if (dev->vctl_port_ub >= 0) {
		/**
		 * VCTL can send 8bit voltage level at once.
		 * Send lower 8bit first, vlevel change happens
		 * when upper 8bit is sent.
		 */
		vlevel = vlevel_set & 0xFF;
	}

	/* Kick the state machine back to idle */
	/* Kick the state machine back to idle */
	dev->reg_shadow[MSM_SPM_REG_SAW_RST] = 1;
	dev->reg_shadow[MSM_SPM_REG_SAW_RST] = 1;
	msm_spm_drv_flush_shadow(dev, MSM_SPM_REG_SAW_RST);
	msm_spm_drv_flush_shadow(dev, MSM_SPM_REG_SAW_RST);


	msm_spm_drv_set_vctl2(dev, vlevel);
	msm_spm_drv_set_vctl2(dev, vlevel, dev->vctl_port);


	timeout_us = dev->vctl_timeout_us;
	ret = msm_spm_drv_validate_data(dev, vlevel, dev->vctl_port);
	/* Confirm the voltage we set was what hardware sent */
	if (ret)
	do {
		udelay(1);
		new_level = msm_spm_drv_get_sts_curr_pmic_data(dev);
		/* FSM is idle */
		if (((new_level & 0x30000) == 0) &&
				((new_level & 0xFF) == vlevel))
			break;
	} while (--timeout_us);
	if (!timeout_us) {
		pr_info("Wrong level %#x\n", new_level);
		goto set_vdd_bail;
		goto set_vdd_bail;
	}


	if (msm_spm_debug_mask & MSM_SPM_DEBUG_VCTL)
	if (dev->vctl_port_ub >= 0) {
		pr_info("%s: done, remaining timeout %u us\n",
		/* Send upper 8bit of voltage level */
			__func__, timeout_us);
		vlevel = (vlevel_set >> 8) & 0xFF;

		/* Kick the state machine back to idle */
		dev->reg_shadow[MSM_SPM_REG_SAW_RST] = 1;
		msm_spm_drv_flush_shadow(dev, MSM_SPM_REG_SAW_RST);

		/*
		 * Steps for sending for vctl port other than '0'
		 * Write VCTL register with pmic data and address index
		 * Perform system barrier
		 * Wait for 1us
		 * Read PMIC_STS register to make sure operation is complete
		 */
		msm_spm_drv_set_vctl2(dev, vlevel, dev->vctl_port_ub);

		mb(); /* To make sure data is sent before checking status */

		ret = msm_spm_drv_validate_data(dev, vlevel, dev->vctl_port_ub);
		if (ret)
			goto set_vdd_bail;
	}


	/* Set AVS min/max */
	/* Set AVS min/max */
	if (avs_enabled) {
	if (avs_enabled) {
		msm_spm_drv_set_avs_vlevel(dev, vlevel);
		msm_spm_drv_set_avs_vlevel(dev, vlevel_set);
		msm_spm_drv_enable_avs(dev);
		msm_spm_drv_enable_avs(dev);
	}
	}


	return 0;
	return ret;


set_vdd_bail:
set_vdd_bail:
	if (avs_enabled)
	if (avs_enabled)
		msm_spm_drv_enable_avs(dev);
		msm_spm_drv_enable_avs(dev);


	pr_err("%s: failed %#x, remaining timeout %uus, vlevel %#x\n",
	pr_err("%s: failed %#x vlevel setting in timeout %uus\n",
		__func__, vlevel, timeout_us, new_level);
			__func__, vlevel_set, dev->vctl_timeout_us);
	return -EIO;
	return -EIO;
}
}


@@ -692,6 +739,7 @@ int msm_spm_drv_init(struct msm_spm_driver_data *dev,
	BUG_ON(!dev || !data);
	BUG_ON(!dev || !data);


	dev->vctl_port = data->vctl_port;
	dev->vctl_port = data->vctl_port;
	dev->vctl_port_ub = data->vctl_port_ub;
	dev->phase_port = data->phase_port;
	dev->phase_port = data->phase_port;
	dev->pfm_port = data->pfm_port;
	dev->pfm_port = data->pfm_port;
	dev->reg_base_addr = data->reg_base_addr;
	dev->reg_base_addr = data->reg_base_addr;
+4 −0
Original line number Original line Diff line number Diff line
@@ -798,12 +798,16 @@ static int msm_spm_dev_probe(struct platform_device *pdev)
	}
	}


	spm_data.vctl_port = -1;
	spm_data.vctl_port = -1;
	spm_data.vctl_port_ub = -1;
	spm_data.phase_port = -1;
	spm_data.phase_port = -1;
	spm_data.pfm_port = -1;
	spm_data.pfm_port = -1;


	key = "qcom,vctl-port";
	key = "qcom,vctl-port";
	of_property_read_u32(node, key, &spm_data.vctl_port);
	of_property_read_u32(node, key, &spm_data.vctl_port);


	key = "qcom,vctl-port-ub";
	of_property_read_u32(node, key, &spm_data.vctl_port_ub);

	key = "qcom,phase-port";
	key = "qcom,phase-port";
	of_property_read_u32(node, key, &spm_data.phase_port);
	of_property_read_u32(node, key, &spm_data.phase_port);


+3 −1
Original line number Original line Diff line number Diff line
/* Copyright (c) 2011-2015, The Linux Foundation. All rights reserved.
/* Copyright (c) 2011-2015, 2017, The Linux Foundation. All rights reserved.
 *
 *
 * This program is free software; you can redistribute it and/or modify
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 and
 * it under the terms of the GNU General Public License version 2 and
@@ -62,6 +62,7 @@ struct msm_spm_platform_data {


	uint32_t ver_reg;
	uint32_t ver_reg;
	uint32_t vctl_port;
	uint32_t vctl_port;
	int vctl_port_ub;
	uint32_t phase_port;
	uint32_t phase_port;
	uint32_t pfm_port;
	uint32_t pfm_port;


@@ -84,6 +85,7 @@ struct msm_spm_driver_data {
	uint32_t minor;
	uint32_t minor;
	uint32_t ver_reg;
	uint32_t ver_reg;
	uint32_t vctl_port;
	uint32_t vctl_port;
	int vctl_port_ub;
	uint32_t phase_port;
	uint32_t phase_port;
	uint32_t pfm_port;
	uint32_t pfm_port;
	void __iomem *reg_base_addr;
	void __iomem *reg_base_addr;