Re: [PATCH v2 03/10] qcom: spm: Add Subsystem Power Manager (SPM) driver for QCOM chipsets

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



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 <pchidamb@xxxxxxxxxxxxxx>
> Signed-off-by: Murali Nalajala <mnalajal@xxxxxxxxxxxxxx>
> Signed-off-by: Lina Iyer <lina.iyer@xxxxxxxxxx>
> ---
> +

<snip>

>  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

<snip>

> +	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.
> +}
> +
> +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?
> +spm_failed_malloc:
> +	return ret;
> +}
> +
> +/**
> + * msm_spm_turn_on_cpu_rail(): Power on cpu rail before turning on core

<snip>

> +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__,

<snip>

> +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?
> +		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?
> +				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?
> +
> +	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.
> +{
> +	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 <linux/module.h>
> +#include <linux/kernel.h>
> +#include <linux/delay.h>
> +#include <linux/init.h>
> +#include <linux/io.h>
> +#include <linux/slab.h>
> +
> +#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)

<snip>
> +
> +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)

<snip>

> +	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

<snip>

> 
--
To unsubscribe from this list: send the line "unsubscribe linux-arm-msm" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html




[Index of Archives]     [Linux ARM Kernel]     [Linux ARM]     [Linux Omap]     [Fedora ARM]     [Linux for Sparc]     [IETF Annouce]     [Security]     [Bugtraq]     [Linux MIPS]     [ECOS]     [Asterisk Internet PBX]     [Linux API]

  Powered by Linux