cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

spm.c (7795B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/*
      3 * Copyright (c) 2011-2014, The Linux Foundation. All rights reserved.
      4 * Copyright (c) 2014,2015, Linaro Ltd.
      5 *
      6 * SAW power controller driver
      7 */
      8
      9#include <linux/kernel.h>
     10#include <linux/init.h>
     11#include <linux/io.h>
     12#include <linux/module.h>
     13#include <linux/slab.h>
     14#include <linux/of.h>
     15#include <linux/of_address.h>
     16#include <linux/of_device.h>
     17#include <linux/err.h>
     18#include <linux/platform_device.h>
     19#include <soc/qcom/spm.h>
     20
     21#define SPM_CTL_INDEX		0x7f
     22#define SPM_CTL_INDEX_SHIFT	4
     23#define SPM_CTL_EN		BIT(0)
     24
     25enum spm_reg {
     26	SPM_REG_CFG,
     27	SPM_REG_SPM_CTL,
     28	SPM_REG_DLY,
     29	SPM_REG_PMIC_DLY,
     30	SPM_REG_PMIC_DATA_0,
     31	SPM_REG_PMIC_DATA_1,
     32	SPM_REG_VCTL,
     33	SPM_REG_SEQ_ENTRY,
     34	SPM_REG_SPM_STS,
     35	SPM_REG_PMIC_STS,
     36	SPM_REG_AVS_CTL,
     37	SPM_REG_AVS_LIMIT,
     38	SPM_REG_NR,
     39};
     40
     41static const u16 spm_reg_offset_v4_1[SPM_REG_NR] = {
     42	[SPM_REG_AVS_CTL]	= 0x904,
     43	[SPM_REG_AVS_LIMIT]	= 0x908,
     44};
     45
     46static const struct spm_reg_data spm_reg_660_gold_l2  = {
     47	.reg_offset = spm_reg_offset_v4_1,
     48	.avs_ctl = 0x1010031,
     49	.avs_limit = 0x4580458,
     50};
     51
     52static const struct spm_reg_data spm_reg_660_silver_l2  = {
     53	.reg_offset = spm_reg_offset_v4_1,
     54	.avs_ctl = 0x101c031,
     55	.avs_limit = 0x4580458,
     56};
     57
     58static const struct spm_reg_data spm_reg_8998_gold_l2  = {
     59	.reg_offset = spm_reg_offset_v4_1,
     60	.avs_ctl = 0x1010031,
     61	.avs_limit = 0x4700470,
     62};
     63
     64static const struct spm_reg_data spm_reg_8998_silver_l2  = {
     65	.reg_offset = spm_reg_offset_v4_1,
     66	.avs_ctl = 0x1010031,
     67	.avs_limit = 0x4200420,
     68};
     69
     70static const u16 spm_reg_offset_v3_0[SPM_REG_NR] = {
     71	[SPM_REG_CFG]		= 0x08,
     72	[SPM_REG_SPM_CTL]	= 0x30,
     73	[SPM_REG_DLY]		= 0x34,
     74	[SPM_REG_SEQ_ENTRY]	= 0x400,
     75};
     76
     77/* SPM register data for 8916 */
     78static const struct spm_reg_data spm_reg_8916_cpu = {
     79	.reg_offset = spm_reg_offset_v3_0,
     80	.spm_cfg = 0x1,
     81	.spm_dly = 0x3C102800,
     82	.seq = { 0x60, 0x03, 0x60, 0x0B, 0x0F, 0x20, 0x10, 0x80, 0x30, 0x90,
     83		0x5B, 0x60, 0x03, 0x60, 0x3B, 0x76, 0x76, 0x0B, 0x94, 0x5B,
     84		0x80, 0x10, 0x26, 0x30, 0x0F },
     85	.start_index[PM_SLEEP_MODE_STBY] = 0,
     86	.start_index[PM_SLEEP_MODE_SPC] = 5,
     87};
     88
     89static const u16 spm_reg_offset_v2_1[SPM_REG_NR] = {
     90	[SPM_REG_CFG]		= 0x08,
     91	[SPM_REG_SPM_CTL]	= 0x30,
     92	[SPM_REG_DLY]		= 0x34,
     93	[SPM_REG_SEQ_ENTRY]	= 0x80,
     94};
     95
     96/* SPM register data for 8974, 8084 */
     97static const struct spm_reg_data spm_reg_8974_8084_cpu  = {
     98	.reg_offset = spm_reg_offset_v2_1,
     99	.spm_cfg = 0x1,
    100	.spm_dly = 0x3C102800,
    101	.seq = { 0x03, 0x0B, 0x0F, 0x00, 0x20, 0x80, 0x10, 0xE8, 0x5B, 0x03,
    102		0x3B, 0xE8, 0x5B, 0x82, 0x10, 0x0B, 0x30, 0x06, 0x26, 0x30,
    103		0x0F },
    104	.start_index[PM_SLEEP_MODE_STBY] = 0,
    105	.start_index[PM_SLEEP_MODE_SPC] = 3,
    106};
    107
    108/* SPM register data for 8226 */
    109static const struct spm_reg_data spm_reg_8226_cpu  = {
    110	.reg_offset = spm_reg_offset_v2_1,
    111	.spm_cfg = 0x0,
    112	.spm_dly = 0x3C102800,
    113	.seq = { 0x60, 0x03, 0x60, 0x0B, 0x0F, 0x20, 0x10, 0x80, 0x30, 0x90,
    114		0x5B, 0x60, 0x03, 0x60, 0x3B, 0x76, 0x76, 0x0B, 0x94, 0x5B,
    115		0x80, 0x10, 0x26, 0x30, 0x0F },
    116	.start_index[PM_SLEEP_MODE_STBY] = 0,
    117	.start_index[PM_SLEEP_MODE_SPC] = 5,
    118};
    119
    120static const u16 spm_reg_offset_v1_1[SPM_REG_NR] = {
    121	[SPM_REG_CFG]		= 0x08,
    122	[SPM_REG_SPM_CTL]	= 0x20,
    123	[SPM_REG_PMIC_DLY]	= 0x24,
    124	[SPM_REG_PMIC_DATA_0]	= 0x28,
    125	[SPM_REG_PMIC_DATA_1]	= 0x2C,
    126	[SPM_REG_SEQ_ENTRY]	= 0x80,
    127};
    128
    129/* SPM register data for 8064 */
    130static const struct spm_reg_data spm_reg_8064_cpu = {
    131	.reg_offset = spm_reg_offset_v1_1,
    132	.spm_cfg = 0x1F,
    133	.pmic_dly = 0x02020004,
    134	.pmic_data[0] = 0x0084009C,
    135	.pmic_data[1] = 0x00A4001C,
    136	.seq = { 0x03, 0x0F, 0x00, 0x24, 0x54, 0x10, 0x09, 0x03, 0x01,
    137		0x10, 0x54, 0x30, 0x0C, 0x24, 0x30, 0x0F },
    138	.start_index[PM_SLEEP_MODE_STBY] = 0,
    139	.start_index[PM_SLEEP_MODE_SPC] = 2,
    140};
    141
    142static inline void spm_register_write(struct spm_driver_data *drv,
    143					enum spm_reg reg, u32 val)
    144{
    145	if (drv->reg_data->reg_offset[reg])
    146		writel_relaxed(val, drv->reg_base +
    147				drv->reg_data->reg_offset[reg]);
    148}
    149
    150/* Ensure a guaranteed write, before return */
    151static inline void spm_register_write_sync(struct spm_driver_data *drv,
    152					enum spm_reg reg, u32 val)
    153{
    154	u32 ret;
    155
    156	if (!drv->reg_data->reg_offset[reg])
    157		return;
    158
    159	do {
    160		writel_relaxed(val, drv->reg_base +
    161				drv->reg_data->reg_offset[reg]);
    162		ret = readl_relaxed(drv->reg_base +
    163				drv->reg_data->reg_offset[reg]);
    164		if (ret == val)
    165			break;
    166		cpu_relax();
    167	} while (1);
    168}
    169
    170static inline u32 spm_register_read(struct spm_driver_data *drv,
    171				    enum spm_reg reg)
    172{
    173	return readl_relaxed(drv->reg_base + drv->reg_data->reg_offset[reg]);
    174}
    175
    176void spm_set_low_power_mode(struct spm_driver_data *drv,
    177			    enum pm_sleep_mode mode)
    178{
    179	u32 start_index;
    180	u32 ctl_val;
    181
    182	start_index = drv->reg_data->start_index[mode];
    183
    184	ctl_val = spm_register_read(drv, SPM_REG_SPM_CTL);
    185	ctl_val &= ~(SPM_CTL_INDEX << SPM_CTL_INDEX_SHIFT);
    186	ctl_val |= start_index << SPM_CTL_INDEX_SHIFT;
    187	ctl_val |= SPM_CTL_EN;
    188	spm_register_write_sync(drv, SPM_REG_SPM_CTL, ctl_val);
    189}
    190
    191static const struct of_device_id spm_match_table[] = {
    192	{ .compatible = "qcom,sdm660-gold-saw2-v4.1-l2",
    193	  .data = &spm_reg_660_gold_l2 },
    194	{ .compatible = "qcom,sdm660-silver-saw2-v4.1-l2",
    195	  .data = &spm_reg_660_silver_l2 },
    196	{ .compatible = "qcom,msm8226-saw2-v2.1-cpu",
    197	  .data = &spm_reg_8226_cpu },
    198	{ .compatible = "qcom,msm8916-saw2-v3.0-cpu",
    199	  .data = &spm_reg_8916_cpu },
    200	{ .compatible = "qcom,msm8974-saw2-v2.1-cpu",
    201	  .data = &spm_reg_8974_8084_cpu },
    202	{ .compatible = "qcom,msm8998-gold-saw2-v4.1-l2",
    203	  .data = &spm_reg_8998_gold_l2 },
    204	{ .compatible = "qcom,msm8998-silver-saw2-v4.1-l2",
    205	  .data = &spm_reg_8998_silver_l2 },
    206	{ .compatible = "qcom,apq8084-saw2-v2.1-cpu",
    207	  .data = &spm_reg_8974_8084_cpu },
    208	{ .compatible = "qcom,apq8064-saw2-v1.1-cpu",
    209	  .data = &spm_reg_8064_cpu },
    210	{ },
    211};
    212MODULE_DEVICE_TABLE(of, spm_match_table);
    213
    214static int spm_dev_probe(struct platform_device *pdev)
    215{
    216	const struct of_device_id *match_id;
    217	struct spm_driver_data *drv;
    218	struct resource *res;
    219	void __iomem *addr;
    220
    221	drv = devm_kzalloc(&pdev->dev, sizeof(*drv), GFP_KERNEL);
    222	if (!drv)
    223		return -ENOMEM;
    224
    225	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
    226	drv->reg_base = devm_ioremap_resource(&pdev->dev, res);
    227	if (IS_ERR(drv->reg_base))
    228		return PTR_ERR(drv->reg_base);
    229
    230	match_id = of_match_node(spm_match_table, pdev->dev.of_node);
    231	if (!match_id)
    232		return -ENODEV;
    233
    234	drv->reg_data = match_id->data;
    235	platform_set_drvdata(pdev, drv);
    236
    237	/* Write the SPM sequences first.. */
    238	addr = drv->reg_base + drv->reg_data->reg_offset[SPM_REG_SEQ_ENTRY];
    239	__iowrite32_copy(addr, drv->reg_data->seq,
    240			ARRAY_SIZE(drv->reg_data->seq) / 4);
    241
    242	/*
    243	 * ..and then the control registers.
    244	 * On some SoC if the control registers are written first and if the
    245	 * CPU was held in reset, the reset signal could trigger the SPM state
    246	 * machine, before the sequences are completely written.
    247	 */
    248	spm_register_write(drv, SPM_REG_AVS_CTL, drv->reg_data->avs_ctl);
    249	spm_register_write(drv, SPM_REG_AVS_LIMIT, drv->reg_data->avs_limit);
    250	spm_register_write(drv, SPM_REG_CFG, drv->reg_data->spm_cfg);
    251	spm_register_write(drv, SPM_REG_DLY, drv->reg_data->spm_dly);
    252	spm_register_write(drv, SPM_REG_PMIC_DLY, drv->reg_data->pmic_dly);
    253	spm_register_write(drv, SPM_REG_PMIC_DATA_0,
    254				drv->reg_data->pmic_data[0]);
    255	spm_register_write(drv, SPM_REG_PMIC_DATA_1,
    256				drv->reg_data->pmic_data[1]);
    257
    258	/* Set up Standby as the default low power mode */
    259	if (drv->reg_data->reg_offset[SPM_REG_SPM_CTL])
    260		spm_set_low_power_mode(drv, PM_SLEEP_MODE_STBY);
    261
    262	return 0;
    263}
    264
    265static struct platform_driver spm_driver = {
    266	.probe = spm_dev_probe,
    267	.driver = {
    268		.name = "qcom_spm",
    269		.of_match_table = spm_match_table,
    270	},
    271};
    272
    273static int __init qcom_spm_init(void)
    274{
    275	return platform_driver_register(&spm_driver);
    276}
    277arch_initcall(qcom_spm_init);
    278
    279MODULE_LICENSE("GPL v2");