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

u8500_hsem.c (4012B)


      1// SPDX-License-Identifier: GPL-2.0
      2/*
      3 * u8500 HWSEM driver
      4 *
      5 * Copyright (C) 2010-2011 ST-Ericsson
      6 *
      7 * Implements u8500 semaphore handling for protocol 1, no interrupts.
      8 *
      9 * Author: Mathieu Poirier <mathieu.poirier@linaro.org>
     10 * Heavily borrowed from the work of :
     11 *   Simon Que <sque@ti.com>
     12 *   Hari Kanigeri <h-kanigeri2@ti.com>
     13 *   Ohad Ben-Cohen <ohad@wizery.com>
     14 */
     15
     16#include <linux/module.h>
     17#include <linux/delay.h>
     18#include <linux/io.h>
     19#include <linux/slab.h>
     20#include <linux/spinlock.h>
     21#include <linux/hwspinlock.h>
     22#include <linux/platform_device.h>
     23
     24#include "hwspinlock_internal.h"
     25
     26/*
     27 * Implementation of STE's HSem protocol 1 without interrutps.
     28 * The only masterID we allow is '0x01' to force people to use
     29 * HSems for synchronisation between processors rather than processes
     30 * on the ARM core.
     31 */
     32
     33#define U8500_MAX_SEMAPHORE		32	/* a total of 32 semaphore */
     34#define RESET_SEMAPHORE			(0)	/* free */
     35
     36/*
     37 * CPU ID for master running u8500 kernel.
     38 * Hswpinlocks should only be used to synchonise operations
     39 * between the Cortex A9 core and the other CPUs.  Hence
     40 * forcing the masterID to a preset value.
     41 */
     42#define HSEM_MASTER_ID			0x01
     43
     44#define HSEM_REGISTER_OFFSET		0x08
     45
     46#define HSEM_CTRL_REG			0x00
     47#define HSEM_ICRALL			0x90
     48#define HSEM_PROTOCOL_1			0x01
     49
     50static int u8500_hsem_trylock(struct hwspinlock *lock)
     51{
     52	void __iomem *lock_addr = lock->priv;
     53
     54	writel(HSEM_MASTER_ID, lock_addr);
     55
     56	/* get only first 4 bit and compare to masterID.
     57	 * if equal, we have the semaphore, otherwise
     58	 * someone else has it.
     59	 */
     60	return (HSEM_MASTER_ID == (0x0F & readl(lock_addr)));
     61}
     62
     63static void u8500_hsem_unlock(struct hwspinlock *lock)
     64{
     65	void __iomem *lock_addr = lock->priv;
     66
     67	/* release the lock by writing 0 to it */
     68	writel(RESET_SEMAPHORE, lock_addr);
     69}
     70
     71/*
     72 * u8500: what value is recommended here ?
     73 */
     74static void u8500_hsem_relax(struct hwspinlock *lock)
     75{
     76	ndelay(50);
     77}
     78
     79static const struct hwspinlock_ops u8500_hwspinlock_ops = {
     80	.trylock	= u8500_hsem_trylock,
     81	.unlock		= u8500_hsem_unlock,
     82	.relax		= u8500_hsem_relax,
     83};
     84
     85static int u8500_hsem_probe(struct platform_device *pdev)
     86{
     87	struct hwspinlock_pdata *pdata = pdev->dev.platform_data;
     88	struct hwspinlock_device *bank;
     89	struct hwspinlock *hwlock;
     90	void __iomem *io_base;
     91	int i, num_locks = U8500_MAX_SEMAPHORE;
     92	ulong val;
     93
     94	if (!pdata)
     95		return -ENODEV;
     96
     97	io_base = devm_platform_ioremap_resource(pdev, 0);
     98	if (IS_ERR(io_base))
     99		return PTR_ERR(io_base);
    100
    101	/* make sure protocol 1 is selected */
    102	val = readl(io_base + HSEM_CTRL_REG);
    103	writel((val & ~HSEM_PROTOCOL_1), io_base + HSEM_CTRL_REG);
    104
    105	/* clear all interrupts */
    106	writel(0xFFFF, io_base + HSEM_ICRALL);
    107
    108	bank = devm_kzalloc(&pdev->dev, struct_size(bank, lock, num_locks),
    109			    GFP_KERNEL);
    110	if (!bank)
    111		return -ENOMEM;
    112
    113	platform_set_drvdata(pdev, bank);
    114
    115	for (i = 0, hwlock = &bank->lock[0]; i < num_locks; i++, hwlock++)
    116		hwlock->priv = io_base + HSEM_REGISTER_OFFSET + sizeof(u32) * i;
    117
    118	return devm_hwspin_lock_register(&pdev->dev, bank,
    119					 &u8500_hwspinlock_ops,
    120					 pdata->base_id, num_locks);
    121}
    122
    123static int u8500_hsem_remove(struct platform_device *pdev)
    124{
    125	struct hwspinlock_device *bank = platform_get_drvdata(pdev);
    126	void __iomem *io_base = bank->lock[0].priv - HSEM_REGISTER_OFFSET;
    127
    128	/* clear all interrupts */
    129	writel(0xFFFF, io_base + HSEM_ICRALL);
    130
    131	return 0;
    132}
    133
    134static struct platform_driver u8500_hsem_driver = {
    135	.probe		= u8500_hsem_probe,
    136	.remove		= u8500_hsem_remove,
    137	.driver		= {
    138		.name	= "u8500_hsem",
    139	},
    140};
    141
    142static int __init u8500_hsem_init(void)
    143{
    144	return platform_driver_register(&u8500_hsem_driver);
    145}
    146/* board init code might need to reserve hwspinlocks for predefined purposes */
    147postcore_initcall(u8500_hsem_init);
    148
    149static void __exit u8500_hsem_exit(void)
    150{
    151	platform_driver_unregister(&u8500_hsem_driver);
    152}
    153module_exit(u8500_hsem_exit);
    154
    155MODULE_LICENSE("GPL v2");
    156MODULE_DESCRIPTION("Hardware Spinlock driver for u8500");
    157MODULE_AUTHOR("Mathieu Poirier <mathieu.poirier@linaro.org>");