From mboxrd@z Thu Jan 1 00:00:00 1970 From: Lina Iyer Subject: Re: [PATCH v2 03/10] qcom: spm: Add Subsystem Power Manager (SPM) driver for QCOM chipsets Date: Thu, 14 Aug 2014 09:18:28 -0600 Message-ID: <20140814151827.GC33559@ilina-mac.local> References: <1407872640-6732-1-git-send-email-lina.iyer@linaro.org> <1407872640-6732-4-git-send-email-lina.iyer@linaro.org> <53ECB315.40605@smartplayin.com> Mime-Version: 1.0 Content-Type: text/plain; charset=us-ascii; format=flowed Return-path: Received: from mail-pa0-f53.google.com ([209.85.220.53]:45550 "EHLO mail-pa0-f53.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1755031AbaHNPSd (ORCPT ); Thu, 14 Aug 2014 11:18:33 -0400 Received: by mail-pa0-f53.google.com with SMTP id rd3so1772864pab.26 for ; Thu, 14 Aug 2014 08:18:33 -0700 (PDT) Content-Disposition: inline In-Reply-To: <53ECB315.40605@smartplayin.com> Sender: linux-arm-msm-owner@vger.kernel.org List-Id: linux-arm-msm@vger.kernel.org To: Pramod Gurav Cc: daniel.lezcano@linaro.org, khilman@linaro.org, amit.kucheria@linaro.org, sboyd@codeaurora.org, davidb@codeaurora.org, galak@codeaurora.org, linux-arm-msm@vger.kernel.org, msivasub@codeaurora.org, Praveen Chidamabram , Murali Nalajala On Thu, Aug 14, 2014 at 06:31:09PM +0530, Pramod Gurav wrote: >On Wednesday 13 August 2014 01:13 AM, Lina Iyer wrote: >> Qualcomm chipsets use an separate h/w block to control the logic around >> the processor cores (cpu and L2). The SPM h/w block regulates power to >> the cores and controls the power when the core enter low power modes. >> >> Each core has its own instance of SPM. The SPM has the following key >> functions >> - Configure the h/w dependencies when entering low power modes >> - Wait for interrupt and wake up on interrupt >> - Ensure the dependencies are ready before bringing the core out >> of sleep >> - Regulating voltage to the core, interfacing with the PMIC. >> - Optimize power based on runtime recommendations. >> >> The driver identifies and configures the SPMs, by reading the nodes and >> the register values from the devicetree. The SPMs need to be configured >> to allow the processor to be idled in a low power state. >> >> Signed-off-by: Praveen Chidamabram >> Signed-off-by: Murali Nalajala >> Signed-off-by: Lina Iyer >> --- >> + > > > >> CFLAGS_scm.o :=$(call as-instr,.arch_extension sec,-DREQUIRES_SEC=1) >> obj-$(CONFIG_QCOM_SCM) += scm.o scm-boot.o >> diff --git a/drivers/soc/qcom/spm-devices.c b/drivers/soc/qcom/spm-devices.c >> new file mode 100644 >> index 0000000..567e9f9 >> --- /dev/null >> +++ b/drivers/soc/qcom/spm-devices.c > > > >> + if (ret) >> + return ret; >> + >> + return dev->cpu_vdd; >> +} >> +EXPORT_SYMBOL(msm_spm_get_vdd); >> + >> +static void msm_spm_config_q2s(struct msm_spm_device *dev, unsigned int mode) >> +{ >> + uint32_t spm_legacy_mode = 0; >> + uint32_t qchannel_ignore = 0; >> + uint32_t val = 0; >Initialization not needed for val. >> + >> + if (!dev->q2s_reg) >> + return; >> + >> + switch (mode) { >> + case MSM_SPM_MODE_DISABLED: >> + case MSM_SPM_MODE_CLOCK_GATING: >> + qchannel_ignore = 1; >> + spm_legacy_mode = 0; >> + break; >> + case MSM_SPM_MODE_RETENTION: >> + qchannel_ignore = 0; >> + spm_legacy_mode = 0; >> + break; >> + case MSM_SPM_MODE_GDHS: >> + case MSM_SPM_MODE_POWER_COLLAPSE: >> + qchannel_ignore = 0; >> + spm_legacy_mode = 1; >> + break; >> + default: >> + break; >> + } >> + >> + val = spm_legacy_mode << 2 | qchannel_ignore << 1; >> + __raw_writel(val, dev->q2s_reg); >> + mb(); >This may need a comment around it. Sorry, my bad. I picked up the latest driver and this came with it. In my next version I pruned this code. >> +} >> + >> +static int msm_spm_dev_set_low_power_mode(struct msm_spm_device *dev, >> + unsigned int mode, bool notify_rpm) >> +{ >> + uint32_t i; >> + uint32_t start_addr = 0; >> + int ret = -EINVAL; >> + bool pc_mode = false; >> + >> + if (!dev->initialized) >> + return -ENXIO; >> + >> + if ((mode == MSM_SPM_MODE_POWER_COLLAPSE) >> + || (mode == MSM_SPM_MODE_GDHS)) >> + pc_mode = true; >> + >> + if (mode == MSM_SPM_MODE_DISABLED) { >> + ret = msm_spm_drv_set_spm_enable(&dev->reg_data, false); >> + } else if (!msm_spm_drv_set_spm_enable(&dev->reg_data, true)) { >> + for (i = 0; i < dev->num_modes; i++) { >> + if ((dev->modes[i].mode == mode) && >> + (dev->modes[i].notify_rpm == notify_rpm)) { >> + start_addr = dev->modes[i].start_addr; >> + break; >> + } >> + } >> + ret = msm_spm_drv_set_low_power_mode(&dev->reg_data, >> + start_addr, pc_mode); >> + } >> + >> + msm_spm_config_q2s(dev, mode); >> + >> + return ret; >> +} >> + >> +static int msm_spm_dev_init(struct msm_spm_device *dev, >> + struct msm_spm_platform_data *data) >> +{ >> + int i, ret = -ENOMEM; >> + uint32_t offset = 0; >> + >> + dev->cpu_vdd = VDD_DEFAULT; >> + dev->num_modes = data->num_modes; >> + dev->modes = kmalloc( >> + sizeof(struct msm_spm_power_modes) * dev->num_modes, >> + GFP_KERNEL); >> + >> + if (!dev->modes) >> + goto spm_failed_malloc; >> + >> + dev->reg_data.major = data->major; >> + dev->reg_data.minor = data->minor; >> + ret = msm_spm_drv_init(&dev->reg_data, data); >> + >Please remove extra line. >> + if (ret) >> + goto spm_failed_init; >> + >> + for (i = 0; i < dev->num_modes; i++) { >> + >> + /* Default offset is 0 and gets updated as we write more >> + * sequences into SPM >> + */ >> + dev->modes[i].start_addr = offset; >> + ret = msm_spm_drv_write_seq_data(&dev->reg_data, >> + data->modes[i].cmd, &offset); >> + if (ret < 0) >> + goto spm_failed_init; >> + >> + dev->modes[i].mode = data->modes[i].mode; >> + dev->modes[i].notify_rpm = data->modes[i].notify_rpm; >> + } >> + msm_spm_drv_reinit(&dev->reg_data); >> + dev->initialized = true; >> + return 0; >> + >> +spm_failed_init: >> + kfree(dev->modes); >Should not we release dev->reg_seq_entry_shadow allocated in >msm_spm_drv_init? Good point. Will free it. >> +spm_failed_malloc: >> + return ret; >> +} >> + >> +/** >> + * msm_spm_turn_on_cpu_rail(): Power on cpu rail before turning on core > > > >> +int __init msm_spm_init(struct msm_spm_platform_data *data, int nr_devs) >> +{ >> + unsigned int cpu; >> + int ret = 0; >Initialization not needed. >> + >> + BUG_ON((nr_devs < num_possible_cpus()) || !data); >> + >> + for_each_possible_cpu(cpu) { >> + struct msm_spm_device *dev = &per_cpu(msm_cpu_spm_device, cpu); >> + >> + ret = msm_spm_dev_init(dev, &data[cpu]); >> + if (ret < 0) { >> + pr_warn("%s():failed CPU:%u ret:%d\n", __func__, > > > >> +static struct msm_spm_device *msm_spm_get_device(struct platform_device *pdev) >> +{ >> + struct msm_spm_device *dev = NULL; >> + const char *val = NULL; >> + char *key = "qcom,name"; >> + int cpu = get_cpu_id(pdev->dev.of_node); >> + >> + if ((cpu >= 0) && cpu < num_possible_cpus()) >> + dev = &per_cpu(msm_cpu_spm_device, cpu); >> + else if ((cpu == 0xffff) || (cpu < 0)) >> + dev = devm_kzalloc(&pdev->dev, sizeof(struct msm_spm_device), >> + GFP_KERNEL); >> + >> + if (!dev) >> + return NULL; >Should we return -ENOMEM? ERR_PTR(-ENOMEM)? >> + >> + if (of_property_read_string(pdev->dev.of_node, key, &val)) { >> + pr_err("%s(): Cannot find a required node key:%s\n", >> + __func__, key); >Should it be dev_err as we have access to pdev->dev? Yes. >> + return NULL; >> + } >> + dev->name = val; >> + list_add(&dev->list, &spm_list); >> + >> + return dev; >> +} >> + >> +static void get_cpumask(struct device_node *node, struct cpumask *mask) >> +{ >> + unsigned long vctl_mask = 0; >> + unsigned c = 0; >> + int idx = 0; >> + struct device_node *cpu_node = NULL; >> + int ret = 0; >Initialization not needed for ret. >> + char *key = "qcom,cpu-vctl-list"; >> + bool found = false; >> + >> + cpu_node = of_parse_phandle(node, key, idx++); >> + while (cpu_node) { >> + found = true; >> + for_each_possible_cpu(c) { >> + if (of_get_cpu_node(c, NULL) == cpu_node) >> + cpumask_set_cpu(c, mask); >> + } >> + cpu_node = of_parse_phandle(node, key, idx++); >> + }; >> + >> + if (found) >> + return; >> + >> + key = "qcom,cpu-vctl-mask"; >> + ret = of_property_read_u32(node, key, (u32 *) &vctl_mask); >> + if (!ret) { >> + for_each_set_bit(c, &vctl_mask, num_possible_cpus()) { >> + cpumask_set_cpu(c, mask); >> + } >> + } >> +} >> + >> +static int msm_spm_dev_probe(struct platform_device *pdev) >> +{ >> + int ret = 0; >> + int cpu = 0; >> + int i = 0; >Initialization not needed for all three variables. >> + struct device_node *node = pdev->dev.of_node; >> + struct msm_spm_platform_data spm_data; >> + char *key = NULL; >> + uint32_t val = 0; >> + struct msm_spm_seq_entry modes[MSM_SPM_MODE_NR]; >> + int len = 0; >> + struct msm_spm_device *dev = NULL; >> + struct resource *res = NULL; >> + uint32_t mode_count = 0; >> + >> + struct spm_of { >> + char *key; >> + uint32_t id; >> + }; >> + >> + struct spm_of spm_of_data[] = { >> + {"qcom,saw2-cfg", MSM_SPM_REG_SAW2_CFG}, >> + {"qcom,saw2-spm-dly", MSM_SPM_REG_SAW2_SPM_DLY}, >> + {"qcom,saw2-spm-ctl", MSM_SPM_REG_SAW2_SPM_CTL}, >> + {"qcom,saw2-pmic-data0", MSM_SPM_REG_SAW2_PMIC_DATA_0}, >> + {"qcom,saw2-pmic-data1", MSM_SPM_REG_SAW2_PMIC_DATA_1}, >> + {"qcom,saw2-pmic-data2", MSM_SPM_REG_SAW2_PMIC_DATA_2}, >> + {"qcom,saw2-pmic-data3", MSM_SPM_REG_SAW2_PMIC_DATA_3}, >> + {"qcom,saw2-pmic-data4", MSM_SPM_REG_SAW2_PMIC_DATA_4}, >> + {"qcom,saw2-pmic-data5", MSM_SPM_REG_SAW2_PMIC_DATA_5}, >> + {"qcom,saw2-pmic-data6", MSM_SPM_REG_SAW2_PMIC_DATA_6}, >> + {"qcom,saw2-pmic-data7", MSM_SPM_REG_SAW2_PMIC_DATA_7}, >> + }; >> + >> + struct mode_of { >> + char *key; >> + uint32_t id; >> + uint32_t notify_rpm; >> + }; >> + >> + struct mode_of mode_of_data[] = { >> + {"qcom,saw2-spm-cmd-wfi", MSM_SPM_MODE_CLOCK_GATING, 0}, >> + {"qcom,saw2-spm-cmd-ret", MSM_SPM_MODE_RETENTION, 0}, >> + {"qcom,saw2-spm-cmd-gdhs", MSM_SPM_MODE_GDHS, 1}, >> + {"qcom,saw2-spm-cmd-spc", MSM_SPM_MODE_POWER_COLLAPSE, 0}, >> + {"qcom,saw2-spm-cmd-pc", MSM_SPM_MODE_POWER_COLLAPSE, 1}, >> + }; >> + >> + dev = msm_spm_get_device(pdev); >> + if (!dev) { >> + ret = -ENOMEM; >> + goto fail; >I might be misunderstanding something, but why cant we just return from >here instead of jumping to fail. >> + } >> + get_cpumask(node, &dev->mask); >> + >> + memset(&spm_data, 0, sizeof(struct msm_spm_platform_data)); >> + memset(&modes, 0, >> + (MSM_SPM_MODE_NR - 2) * sizeof(struct msm_spm_seq_entry)); >> + >> + if (of_device_is_compatible(node, "qcom,spm-v2.1")) { >> + spm_data.major = 2; >> + spm_data.minor = 1; >> + } else if (of_device_is_compatible(node, "qcom,spm-v3.0")) { >> + spm_data.major = 3; >> + spm_data.minor = 0; >> + } >> + >> + key = "qcom,vctl-timeout-us"; >> + ret = of_property_read_u32(node, key, &val); >> + if (!ret) >> + spm_data.vctl_timeout_us = val; >> + >> + /* SAW start address */ >> + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); >> + if (!res) { >> + ret = -EFAULT; >Should it return -EINVAL? >> + goto fail; >> + } >> + >> + spm_data.reg_base_addr = devm_ioremap(&pdev->dev, res->start, >> + resource_size(res)); >> + if (!spm_data.reg_base_addr) { >> + ret = -ENOMEM; >> + goto fail; >> + } >> + >> + spm_data.vctl_port = -1; >> + spm_data.phase_port = -1; >> + spm_data.pfm_port = -1; >> + >> + key = "qcom,vctl-port"; >> + of_property_read_u32(node, key, &spm_data.vctl_port); >> + >> + key = "qcom,phase-port"; >> + of_property_read_u32(node, key, &spm_data.phase_port); >> + >> + key = "qcom,pfm-port"; >> + of_property_read_u32(node, key, &spm_data.pfm_port); >> + >> + /* Q2S (QChannel-2-SPM) register */ >> + res = platform_get_resource(pdev, IORESOURCE_MEM, 1); >> + if (res) { >> + dev->q2s_reg = devm_ioremap(&pdev->dev, res->start, >> + resource_size(res)); >> + if (!dev->q2s_reg) { >> + pr_err("%s(): Unable to iomap Q2S register\n", >Can we use dev_err since pdev->dev is accessible? >> + __func__); >> + ret = -EADDRNOTAVAIL; >ret = -ENOMEM? >> + goto fail; >> + } >> + } >> + /* >> + * At system boot, cpus and or clusters can remain in reset. CCI SPM >> + * will not be triggered unless SPM_LEGACY_MODE bit is set for the >> + * cluster in reset. Initialize q2s registers and set the >> + * SPM_LEGACY_MODE bit. >> + */ >> + msm_spm_config_q2s(dev, MSM_SPM_MODE_POWER_COLLAPSE); >> + >> + for (i = 0; i < ARRAY_SIZE(spm_of_data); i++) { >> + ret = of_property_read_u32(node, spm_of_data[i].key, &val); >> + if (ret) >> + continue; >> + spm_data.reg_init_values[spm_of_data[i].id] = val; >> + } >> + >> + for (i = 0; i < ARRAY_SIZE(mode_of_data); i++) { >> + key = mode_of_data[i].key; >> + modes[mode_count].cmd = >> + (uint8_t *)of_get_property(node, key, &len); >> + if (!modes[mode_count].cmd) >> + continue; >> + modes[mode_count].mode = mode_of_data[i].id; >> + modes[mode_count].notify_rpm = mode_of_data[i].notify_rpm; >> + pr_debug("%s(): dev: %s cmd:%s, mode:%d rpm:%d\n", __func__, >dev_info instead of pr_debug? ok >> + dev->name, key, modes[mode_count].mode, >> + modes[mode_count].notify_rpm); >> + mode_count++; >> + } >> + >> + spm_data.modes = modes; >> + spm_data.num_modes = mode_count; >> + >> + ret = msm_spm_dev_init(dev, &spm_data); >> + if (ret) >> + goto fail; >> + >> + platform_set_drvdata(pdev, dev); >> + >> + for_each_cpu(cpu, &dev->mask) >> + per_cpu(cpu_vctl_device, cpu) = dev; >> + >> + return ret; >> + >> +fail: >> + cpu = get_cpu_id(pdev->dev.of_node); >> + if (dev && (cpu >= num_possible_cpus() || (cpu < 0))) { >> + for_each_cpu(cpu, &dev->mask) >> + per_cpu(cpu_vctl_device, cpu) = ERR_PTR(ret); >Should this be part of remove function instead of here as it is last >initialization that is done in probe just before fail lable? If it is >not needed the we can do away with this part of code from fail lable. >> + } >Are we missing a list_del like in remove function below? Yes we are. >> + >> + pr_err("%s: CPU%d SPM device probe failed: %d\n", __func__, cpu, ret); >dev_ instead of pr_? >> + >> + return ret; >> +} >> + >> +static int msm_spm_dev_remove(struct platform_device *pdev) >> +{ >> + struct msm_spm_device *dev = platform_get_drvdata(pdev); >> + >> + list_del(&dev->list); >kfree(dev->modes) is missing. >kfree(dev->reg_data->reg_seq_entry_shadow) is missing. >> + >> + return 0; >> +} >> + >> +static struct of_device_id msm_spm_match_table[] = { >> + {.compatible = "qcom,spm-v2.1"}, >> + {.compatible = "qcom,spm-v3.0"}, >> + {}, >> +}; >> + >> +static struct platform_driver msm_spm_device_driver = { >> + .probe = msm_spm_dev_probe, >> + .remove = msm_spm_dev_remove, >> + .driver = { >> + .name = "spm-v2", >> + .owner = THIS_MODULE, >> + .of_match_table = msm_spm_match_table, >> + }, >> +}; >> + >> +/** >> + * msm_spm_device_init(): Device tree initialization function >> + */ >> +int __init msm_spm_device_init(void) >It should be static. Yes. >> +{ >> + static bool registered; >> + >> + if (registered) >> + return 0; >> + >> + registered = true; >> + >> + return platform_driver_register(&msm_spm_device_driver); >> +} >> +device_initcall(msm_spm_device_init); >> diff --git a/drivers/soc/qcom/spm.c b/drivers/soc/qcom/spm.c >> new file mode 100644 >> index 0000000..7dbdb64 >> --- /dev/null >> +++ b/drivers/soc/qcom/spm.c >> @@ -0,0 +1,482 @@ >> +/* Copyright (c) 2011-2014, The Linux Foundation. All rights reserved. >> + * >> + * 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 >> + * only version 2 as published by the Free Software Foundation. >> + * >> + * This program is distributed in the hope that it will be useful, >> + * but WITHOUT ANY WARRANTY; without even the implied warranty of >> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the >> + * GNU General Public License for more details. >> + * >> + */ >> + >> +#include >> +#include >> +#include >> +#include >> +#include >> +#include >> + >> +#include "spm_driver.h" >> + >> +#define MSM_SPM_PMIC_STATE_IDLE 0 >> + >> +enum { >> + MSM_SPM_DEBUG_SHADOW = 1U << 0, >> + MSM_SPM_DEBUG_VCTL = 1U << 1, >> +}; >> + >> +static int msm_spm_debug_mask; >> +module_param_named( >> + debug_mask, msm_spm_debug_mask, int, S_IRUGO | S_IWUSR | S_IWGRP >> +); >> + >> +struct saw2_data { >> + const char *ver_name; >> + uint32_t major; >> + uint32_t minor; >> + uint32_t *spm_reg_offset_ptr; >> +}; >> + >> +static uint32_t msm_spm_reg_offsets_saw2_v2_1[MSM_SPM_REG_NR] = { >> + [MSM_SPM_REG_SAW2_SECURE] = 0x00, >> + [MSM_SPM_REG_SAW2_ID] = 0x04, >> + [MSM_SPM_REG_SAW2_CFG] = 0x08, >> + [MSM_SPM_REG_SAW2_SPM_STS] = 0x0C, >> + [MSM_SPM_REG_SAW2_AVS_STS] = 0x10, >> + [MSM_SPM_REG_SAW2_PMIC_STS] = 0x14, >> + [MSM_SPM_REG_SAW2_RST] = 0x18, >> + [MSM_SPM_REG_SAW2_VCTL] = 0x1C, >> + [MSM_SPM_REG_SAW2_AVS_CTL] = 0x20, >> + [MSM_SPM_REG_SAW2_AVS_LIMIT] = 0x24, >> + [MSM_SPM_REG_SAW2_AVS_DLY] = 0x28, >> + [MSM_SPM_REG_SAW2_AVS_HYSTERESIS] = 0x2C, >> + [MSM_SPM_REG_SAW2_SPM_CTL] = 0x30, >> + [MSM_SPM_REG_SAW2_SPM_DLY] = 0x34, >> + [MSM_SPM_REG_SAW2_PMIC_DATA_0] = 0x40, >> + [MSM_SPM_REG_SAW2_PMIC_DATA_1] = 0x44, >> + [MSM_SPM_REG_SAW2_PMIC_DATA_2] = 0x48, >> + [MSM_SPM_REG_SAW2_PMIC_DATA_3] = 0x4C, >> + [MSM_SPM_REG_SAW2_PMIC_DATA_4] = 0x50,msm_spm_drv_init >> + [MSM_SPM_REG_SAW2_PMIC_DATA_5] = 0x54, >> + [MSM_SPM_REG_SAW2_PMIC_DATA_6] = 0x58, >> + [MSM_SPM_REG_SAW2_PMIC_DATA_7] = 0x5C, >> + [MSM_SPM_REG_SAW2_SEQ_ENTRY] = 0x80, >> + [MSM_SPM_REG_SAW2_VERSION] = 0xFD0, >> +}; >> + >> +static uint32_t msm_spm_reg_offsets_saw2_v3_0[MSM_SPM_REG_NR] = { >> + [MSM_SPM_REG_SAW2_SECURE] = 0x00, >> + [MSM_SPM_REG_SAW2_ID] = 0x04, >> + [MSM_SPM_REG_SAW2_CFG] = 0x08, >> + [MSM_SPM_REG_SAW2_SPM_STS] = 0x0C, >> + [MSM_SPM_REG_SAW2_AVS_STS] = 0x10, >> + [MSM_SPM_REG_SAW2_PMIC_STS] = 0x14, >> + [MSM_SPM_REG_SAW2_RST] = 0x18, >> + [MSM_SPM_REG_SAW2_VCTL] = 0x1C, >> + [MSM_SPM_REG_SAW2_AVS_CTL] = 0x20, >> + [MSM_SPM_REG_SAW2_AVS_LIMIT] = 0x24, >> + [MSM_SPM_REG_SAW2_AVS_DLY] = 0x28, >> + [MSM_SPM_REG_SAW2_AVS_HYSTERESIS] = 0x2C, >> + [MSM_SPM_REG_SAW2_SPM_CTL] = 0x30, >> + [MSM_SPM_REG_SAW2_SPM_DLY] = 0x34, >> + [MSM_SPM_REG_SAW2_STS2] = 0x38, >> + [MSM_SPM_REG_SAW2_PMIC_DATA_0] = 0x40, >> + [MSM_SPM_REG_SAW2_PMIC_DATA_1] = 0x44, >> + [MSM_SPM_REG_SAW2_PMIC_DATA_2] = 0x48, >> + [MSM_SPM_REG_SAW2_PMIC_DATA_3] = 0x4C, >> + [MSM_SPM_REG_SAW2_PMIC_DATA_4] = 0x50, >> + [MSM_SPM_REG_SAW2_PMIC_DATA_5] = 0x54, >> + [MSM_SPM_REG_SAW2_PMIC_DATA_6] = 0x58, >> + [MSM_SPM_REG_SAW2_PMIC_DATA_7] = 0x5C, >> + [MSM_SPM_REG_SAW2_SEQ_ENTRY] = 0x400, >> + [MSM_SPM_REG_SAW2_VERSION] = 0xFD0, >> +}; >> + >> +static struct saw2_data saw2_info[] = { >> + [0] = { >> + "SAW2_v2.1", >> + 2, >> + 1, >> + msm_spm_reg_offsets_saw2_v2_1, >> + }, >> + [1] = { >> + "SAW2_v3.0", >> + 3, >> + 0, >> + msm_spm_reg_offsets_saw2_v3_0, >> + }, >> +}; >> + >> +static uint32_t num_pmic_data; >> + >> +static inline uint32_t msm_spm_drv_get_num_spm_entry( >> + struct msm_spm_driver_data *dev) >> +{ >> + return 32; >> +} >> + >> +static void msm_spm_drv_flush_shadow(struct msm_spm_driver_data *dev, >> + unsigned int reg_index) >> +{ >> + __raw_writel(dev->reg_shadow[reg_index], >> + dev->reg_base_addr + dev->reg_offsets[reg_index]); >> +} >> + >> +static void msm_spm_drv_load_shadow(struct msm_spm_driver_data *dev, >> + unsigned int reg_index) >> +{ >> + dev->reg_shadow[reg_index] = >> + __raw_readl(dev->reg_base_addr + >> + dev->reg_offsets[reg_index]); >> +} >> + >> +static inline void msm_spm_drv_set_start_addr( >> + struct msm_spm_driver_data *dev, uint32_t addr, bool pc_mode) >> +{ >> + addr &= 0x7F; >> + addr <<= 4; >> + dev->reg_shadow[MSM_SPM_REG_SAW2_SPM_CTL] &= 0xFFFFF80F; >> + dev->reg_shadow[MSM_SPM_REG_SAW2_SPM_CTL] |= addr; >> + >> + if (dev->major != 0x3) >> + return; >> + >> + dev->reg_shadow[MSM_SPM_REG_SAW2_SPM_CTL] &= 0xFFFEFFFF; >> + if (pc_mode) >> + dev->reg_shadow[MSM_SPM_REG_SAW2_SPM_CTL] |= 0x00010000; >> +} >> + >> +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); >> + return (dev->reg_shadow[MSM_SPM_REG_SAW2_ID] >> 2) & 0x1; >> +} >> + >> +static inline void msm_spm_drv_set_vctl2(struct msm_spm_driver_data *dev, >> + uint32_t vlevel) >> +{ >> + 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 |= (dev->vctl_port & 0x7) << 16; >> + >> + dev->reg_shadow[MSM_SPM_REG_SAW2_VCTL] &= ~0x700FF; >> + dev->reg_shadow[MSM_SPM_REG_SAW2_VCTL] |= pmic_data; >> + >> + dev->reg_shadow[MSM_SPM_REG_SAW2_PMIC_DATA_3] &= ~0x700FF; >> + dev->reg_shadow[MSM_SPM_REG_SAW2_PMIC_DATA_3] |= pmic_data; >> + >> + msm_spm_drv_flush_shadow(dev, MSM_SPM_REG_SAW2_VCTL); >> + msm_spm_drv_flush_shadow(dev, MSM_SPM_REG_SAW2_PMIC_DATA_3); >> +} >> + >> +static inline uint32_t msm_spm_drv_get_num_pmic_data( >> + struct msm_spm_driver_data *dev) >> +{ >> + msm_spm_drv_load_shadow(dev, MSM_SPM_REG_SAW2_ID); >> + mb(); >> + return (dev->reg_shadow[MSM_SPM_REG_SAW2_ID] >> 4) & 0x7; >> +} >> + >> +static inline uint32_t msm_spm_drv_get_sts_pmic_state( >> + struct msm_spm_driver_data *dev) >> +{ >> + msm_spm_drv_load_shadow(dev, MSM_SPM_REG_SAW2_PMIC_STS); >> + return (dev->reg_shadow[MSM_SPM_REG_SAW2_PMIC_STS] >> 16) & >> + 0x03; >> +} >> + >> +uint32_t msm_spm_drv_get_sts_curr_pmic_data( >> + struct msm_spm_driver_data *dev) >> +{ >> + msm_spm_drv_load_shadow(dev, MSM_SPM_REG_SAW2_PMIC_STS); >> + return dev->reg_shadow[MSM_SPM_REG_SAW2_PMIC_STS] & 0xFF; >> +}msm_spm_drv_init >> + >> +inline int msm_spm_drv_set_spm_enable( >> + struct msm_spm_driver_data *dev, bool enable) >> +{ >> + uint32_t value = enable ? 0x01 : 0x00; >> + >> + if (!dev) >> + return -EINVAL; >> + >> + if ((dev->reg_shadow[MSM_SPM_REG_SAW2_SPM_CTL] & 0x01) ^ value) { >> + >Please remove extra new line. >> + dev->reg_shadow[MSM_SPM_REG_SAW2_SPM_CTL] &= ~0x1; >> + dev->reg_shadow[MSM_SPM_REG_SAW2_SPM_CTL] |= value; >> + >> + msm_spm_drv_flush_shadow(dev, MSM_SPM_REG_SAW2_SPM_CTL); >> + wmb(); >> + } >> + return 0; >> +} >> +void msm_spm_drv_flush_seq_entry(struct msm_spm_driver_data *dev) >> +{ >> + int i; >> + int num_spm_entry = msm_spm_drv_get_num_spm_entry(dev); >> + >> + if (!dev) { >> + __WARN(); >> + return; >> + }msm_spm_drv_init >> + >> + for (i = 0; i < num_spm_entry; i++) { >> + __raw_writel(dev->reg_seq_entry_shadow[i], >> + dev->reg_base_addr >> + + dev->reg_offsets[MSM_SPM_REG_SAW2_SEQ_ENTRY] >> + + 4 * i); >> + } >> + mb(); >> +} >> + >> +void dump_regs(struct msm_spm_driver_data *dev, int cpu) >> +{ >> + msm_spm_drv_load_shadow(dev, MSM_SPM_REG_SAW2_SPM_STS); >> + mb(); >> + pr_err("CPU%d: spm register MSM_SPM_REG_SAW2_SPM_STS: 0x%x\n", cpu, >> + dev->reg_shadow[MSM_SPM_REG_SAW2_SPM_STS]); >pr_info? >> + msm_spm_drv_load_shadow(dev, MSM_SPM_REG_SAW2_SPM_CTL); >> + mb(); >> + pr_err("CPU%d: spm register MSM_SPM_REG_SAW2_SPM_CTL: 0x%x\n", cpu, >> + dev->reg_shadow[MSM_SPM_REG_SAW2_SPM_CTL]); >pr_info? >> +} >> + >> +int msm_spm_drv_write_seq_data(struct msm_spm_driver_data *dev, >> + uint8_t *cmd, uint32_t *offset) > > >> + >> +int msm_spm_drv_set_pmic_data(struct msm_spm_driver_data *dev, >> + enum msm_spm_pmic_port port, unsigned int data)msm_spm_drv_init >> +{ >> + unsigned int pmic_data = 0; >> + unsigned int timeout_us = 0; >> + int index = 0; >Initialization not needed for ind. >> + >> + if (!msm_spm_pmic_arb_present(dev)) >> + return -ENOSYS; >> + >> + index = msm_spm_drv_get_pmic_port(dev, port); >> + if (index < 0) > > > >> + if (!num_pmic_data) >> + num_pmic_data = msm_spm_drv_get_num_pmic_data(dev); >> + >> + num_spm_entry = msm_spm_drv_get_num_spm_entry(dev); >> + >> + dev->reg_seq_entry_shadow = >> + kzalloc(sizeof(*dev->reg_seq_entry_shadow) * num_spm_entry, >> + GFP_KERNEL); >> + >Please remove this extra new line here. >> + if (!dev->reg_seq_entry_shadow) >> + return -ENOMEM; >> + >> + return 0; >> +} >> diff --git a/drivers/soc/qcom/spm_driver.h b/drivers/soc/qcom/spm_driver.h > > > >>