Loading Documentation/devicetree/bindings/arm/msm/spm-v2.txt +4 −0 Original line number Original line Diff line number Diff line Loading @@ -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. Loading Loading @@ -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 Loading drivers/soc/qcom/spm.c +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 Loading Loading @@ -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; Loading Loading @@ -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; Loading @@ -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; } } Loading Loading @@ -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; Loading drivers/soc/qcom/spm_devices.c +4 −0 Original line number Original line Diff line number Diff line Loading @@ -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); Loading drivers/soc/qcom/spm_driver.h +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 Loading Loading @@ -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; Loading @@ -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; Loading Loading
Documentation/devicetree/bindings/arm/msm/spm-v2.txt +4 −0 Original line number Original line Diff line number Diff line Loading @@ -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. Loading Loading @@ -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 Loading
drivers/soc/qcom/spm.c +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 Loading Loading @@ -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; Loading Loading @@ -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; Loading @@ -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; } } Loading Loading @@ -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; Loading
drivers/soc/qcom/spm_devices.c +4 −0 Original line number Original line Diff line number Diff line Loading @@ -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); Loading
drivers/soc/qcom/spm_driver.h +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 Loading Loading @@ -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; Loading @@ -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; Loading