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

qcom_hwspinlock.c (4606B)


      1// SPDX-License-Identifier: GPL-2.0
      2/*
      3 * Copyright (c) 2013, The Linux Foundation. All rights reserved.
      4 * Copyright (c) 2015, Sony Mobile Communications AB
      5 */
      6
      7#include <linux/hwspinlock.h>
      8#include <linux/io.h>
      9#include <linux/kernel.h>
     10#include <linux/mfd/syscon.h>
     11#include <linux/module.h>
     12#include <linux/of.h>
     13#include <linux/of_device.h>
     14#include <linux/platform_device.h>
     15#include <linux/regmap.h>
     16
     17#include "hwspinlock_internal.h"
     18
     19#define QCOM_MUTEX_APPS_PROC_ID	1
     20#define QCOM_MUTEX_NUM_LOCKS	32
     21
     22static int qcom_hwspinlock_trylock(struct hwspinlock *lock)
     23{
     24	struct regmap_field *field = lock->priv;
     25	u32 lock_owner;
     26	int ret;
     27
     28	ret = regmap_field_write(field, QCOM_MUTEX_APPS_PROC_ID);
     29	if (ret)
     30		return ret;
     31
     32	ret = regmap_field_read(field, &lock_owner);
     33	if (ret)
     34		return ret;
     35
     36	return lock_owner == QCOM_MUTEX_APPS_PROC_ID;
     37}
     38
     39static void qcom_hwspinlock_unlock(struct hwspinlock *lock)
     40{
     41	struct regmap_field *field = lock->priv;
     42	u32 lock_owner;
     43	int ret;
     44
     45	ret = regmap_field_read(field, &lock_owner);
     46	if (ret) {
     47		pr_err("%s: unable to query spinlock owner\n", __func__);
     48		return;
     49	}
     50
     51	if (lock_owner != QCOM_MUTEX_APPS_PROC_ID) {
     52		pr_err("%s: spinlock not owned by us (actual owner is %d)\n",
     53				__func__, lock_owner);
     54	}
     55
     56	ret = regmap_field_write(field, 0);
     57	if (ret)
     58		pr_err("%s: failed to unlock spinlock\n", __func__);
     59}
     60
     61static const struct hwspinlock_ops qcom_hwspinlock_ops = {
     62	.trylock	= qcom_hwspinlock_trylock,
     63	.unlock		= qcom_hwspinlock_unlock,
     64};
     65
     66static const struct of_device_id qcom_hwspinlock_of_match[] = {
     67	{ .compatible = "qcom,sfpb-mutex" },
     68	{ .compatible = "qcom,tcsr-mutex" },
     69	{ }
     70};
     71MODULE_DEVICE_TABLE(of, qcom_hwspinlock_of_match);
     72
     73static struct regmap *qcom_hwspinlock_probe_syscon(struct platform_device *pdev,
     74						   u32 *base, u32 *stride)
     75{
     76	struct device_node *syscon;
     77	struct regmap *regmap;
     78	int ret;
     79
     80	syscon = of_parse_phandle(pdev->dev.of_node, "syscon", 0);
     81	if (!syscon)
     82		return ERR_PTR(-ENODEV);
     83
     84	regmap = syscon_node_to_regmap(syscon);
     85	of_node_put(syscon);
     86	if (IS_ERR(regmap))
     87		return regmap;
     88
     89	ret = of_property_read_u32_index(pdev->dev.of_node, "syscon", 1, base);
     90	if (ret < 0) {
     91		dev_err(&pdev->dev, "no offset in syscon\n");
     92		return ERR_PTR(-EINVAL);
     93	}
     94
     95	ret = of_property_read_u32_index(pdev->dev.of_node, "syscon", 2, stride);
     96	if (ret < 0) {
     97		dev_err(&pdev->dev, "no stride syscon\n");
     98		return ERR_PTR(-EINVAL);
     99	}
    100
    101	return regmap;
    102}
    103
    104static const struct regmap_config tcsr_mutex_config = {
    105	.reg_bits		= 32,
    106	.reg_stride		= 4,
    107	.val_bits		= 32,
    108	.max_register		= 0x40000,
    109	.fast_io		= true,
    110};
    111
    112static struct regmap *qcom_hwspinlock_probe_mmio(struct platform_device *pdev,
    113						 u32 *offset, u32 *stride)
    114{
    115	struct device *dev = &pdev->dev;
    116	void __iomem *base;
    117
    118	/* All modern platform has offset 0 and stride of 4k */
    119	*offset = 0;
    120	*stride = 0x1000;
    121
    122	base = devm_platform_ioremap_resource(pdev, 0);
    123	if (IS_ERR(base))
    124		return ERR_CAST(base);
    125
    126	return devm_regmap_init_mmio(dev, base, &tcsr_mutex_config);
    127}
    128
    129static int qcom_hwspinlock_probe(struct platform_device *pdev)
    130{
    131	struct hwspinlock_device *bank;
    132	struct reg_field field;
    133	struct regmap *regmap;
    134	size_t array_size;
    135	u32 stride;
    136	u32 base;
    137	int i;
    138
    139	regmap = qcom_hwspinlock_probe_syscon(pdev, &base, &stride);
    140	if (IS_ERR(regmap) && PTR_ERR(regmap) == -ENODEV)
    141		regmap = qcom_hwspinlock_probe_mmio(pdev, &base, &stride);
    142
    143	if (IS_ERR(regmap))
    144		return PTR_ERR(regmap);
    145
    146	array_size = QCOM_MUTEX_NUM_LOCKS * sizeof(struct hwspinlock);
    147	bank = devm_kzalloc(&pdev->dev, sizeof(*bank) + array_size, GFP_KERNEL);
    148	if (!bank)
    149		return -ENOMEM;
    150
    151	platform_set_drvdata(pdev, bank);
    152
    153	for (i = 0; i < QCOM_MUTEX_NUM_LOCKS; i++) {
    154		field.reg = base + i * stride;
    155		field.lsb = 0;
    156		field.msb = 31;
    157
    158		bank->lock[i].priv = devm_regmap_field_alloc(&pdev->dev,
    159							     regmap, field);
    160	}
    161
    162	return devm_hwspin_lock_register(&pdev->dev, bank, &qcom_hwspinlock_ops,
    163					 0, QCOM_MUTEX_NUM_LOCKS);
    164}
    165
    166static struct platform_driver qcom_hwspinlock_driver = {
    167	.probe		= qcom_hwspinlock_probe,
    168	.driver		= {
    169		.name	= "qcom_hwspinlock",
    170		.of_match_table = qcom_hwspinlock_of_match,
    171	},
    172};
    173
    174static int __init qcom_hwspinlock_init(void)
    175{
    176	return platform_driver_register(&qcom_hwspinlock_driver);
    177}
    178/* board init code might need to reserve hwspinlocks for predefined purposes */
    179postcore_initcall(qcom_hwspinlock_init);
    180
    181static void __exit qcom_hwspinlock_exit(void)
    182{
    183	platform_driver_unregister(&qcom_hwspinlock_driver);
    184}
    185module_exit(qcom_hwspinlock_exit);
    186
    187MODULE_LICENSE("GPL v2");
    188MODULE_DESCRIPTION("Hardware spinlock driver for Qualcomm SoCs");