int msm_spm_drv_set_vdd(struct msm_spm_driver_data *dev, unsigned int vlevel) { uint32_t timeout_us; if (!dev) return -EINVAL; if (msm_spm_debug_mask & MSM_SPM_DEBUG_VCTL) pr_info("%s: requesting vlevel 0x%x\n", __func__, vlevel); msm_spm_drv_set_vctl(dev, vlevel); msm_spm_drv_flush_shadow(dev, MSM_SPM_REG_SAW2_VCTL); msm_spm_drv_flush_shadow(dev, MSM_SPM_REG_SAW2_PMIC_DATA_0); msm_spm_drv_flush_shadow(dev, MSM_SPM_REG_SAW2_PMIC_DATA_1); mb(); /* Wait for PMIC state to return to idle or until timeout */ timeout_us = dev->vctl_timeout_us; msm_spm_drv_load_shadow(dev, MSM_SPM_REG_SAW2_STS0); while (msm_spm_drv_get_sts_pmic_state(dev) != MSM_SPM_PMIC_STATE_IDLE) { if (!timeout_us) goto set_vdd_bail; if (timeout_us > 10) { udelay(10); timeout_us -= 10; } else { udelay(timeout_us); timeout_us = 0; } msm_spm_drv_load_shadow(dev, MSM_SPM_REG_SAW2_STS0); } msm_spm_drv_load_shadow(dev, MSM_SPM_REG_SAW2_STS1); if (msm_spm_drv_get_sts_curr_pmic_data(dev) != vlevel) goto set_vdd_bail; if (msm_spm_debug_mask & MSM_SPM_DEBUG_VCTL) pr_info("%s: done, remaining timeout %uus\n", __func__, timeout_us); return 0; set_vdd_bail: pr_err("%s: failed, remaining timeout %uus, vlevel 0x%x\n", __func__, timeout_us, msm_spm_drv_get_sts_curr_pmic_data(dev)); return -EIO; }
static inline bool msm_spm_pmic_arb_present(struct msm_spm_driver_data *dev) { msm_spm_drv_load_shadow(dev, MSM_SPM_REG_SAW2_ID); if (dev->major == SAW2_MAJOR_2) return (dev->reg_shadow[MSM_SPM_REG_SAW2_ID] >> 2) & 0x1; else return (dev->reg_shadow[MSM_SPM_REG_SAW2_ID] >> 18) & 0x1;
int msm_spm_drv_set_low_power_mode(struct msm_spm_driver_data *dev, uint32_t addr) { void *base = NULL; /* SPM is configured to reset start address to zero after end of Program */ if (!dev) return -EINVAL; base = dev->reg_base_addr; msm_spm_drv_load_shadow(dev, MSM_SPM_REG_SAW2_SPM_CTL); msm_spm_drv_load_shadow(dev, MSM_SPM_REG_SAW2_STS0); uncached_logk_pc(LOGK_PM, (void *)(base + msm_spm_reg_offsets[MSM_SPM_REG_SAW2_SPM_CTL]), (void *)dev->reg_shadow[MSM_SPM_REG_SAW2_SPM_CTL]); uncached_logk_pc(LOGK_PM, (void *)(base + msm_spm_reg_offsets[MSM_SPM_REG_SAW2_STS0]), (void *)dev->reg_shadow[MSM_SPM_REG_SAW2_STS0]); msm_spm_drv_set_start_addr(dev, addr); msm_spm_drv_flush_shadow(dev, MSM_SPM_REG_SAW2_SPM_CTL); wmb(); uncached_logk_pc(LOGK_PM, (void *)(base + msm_spm_reg_offsets[MSM_SPM_REG_SAW2_SPM_CTL]), (void *)dev->reg_shadow[MSM_SPM_REG_SAW2_SPM_CTL]); if (msm_spm_debug_mask & MSM_SPM_DEBUG_SHADOW) { int i; for (i = 0; i < MSM_SPM_REG_NR; i++) pr_info("%s: reg %02x = 0x%08x\n", __func__, msm_spm_reg_offsets[i], dev->reg_shadow[i]); } return 0; }
int __init msm_spm_drv_init(struct msm_spm_driver_data *dev, struct msm_spm_platform_data *data) { int i; int num_spm_entry; BUG_ON(!dev || !data); dev->reg_base_addr = data->reg_base_addr; memcpy(dev->reg_shadow, data->reg_init_values, sizeof(data->reg_init_values)); dev->vctl_timeout_us = data->vctl_timeout_us; for (i = 0; i < MSM_SPM_REG_NR_INITIALIZE; i++) msm_spm_drv_flush_shadow(dev, i); /* barrier to ensure write completes before we update shadow * registers */ mb(); for (i = 0; i < MSM_SPM_REG_NR_INITIALIZE; i++) msm_spm_drv_load_shadow(dev, i); /* barrier to ensure read completes before we proceed further*/ mb(); num_spm_entry = msm_spm_drv_get_num_spm_entry(dev); dev->reg_seq_entry_shadow = kmalloc(sizeof(*dev->reg_seq_entry_shadow) * num_spm_entry, GFP_KERNEL); if (!dev->reg_seq_entry_shadow) return -ENOMEM; memset(dev->reg_seq_entry_shadow, 0x0f, num_spm_entry * sizeof(*dev->reg_seq_entry_shadow)); return 0; }