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

ilsel.c (4062B)


      1// SPDX-License-Identifier: GPL-2.0
      2/*
      3 * arch/sh/boards/mach-x3proto/ilsel.c
      4 *
      5 * Helper routines for SH-X3 proto board ILSEL.
      6 *
      7 * Copyright (C) 2007 - 2010  Paul Mundt
      8 */
      9#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
     10
     11#include <linux/init.h>
     12#include <linux/kernel.h>
     13#include <linux/module.h>
     14#include <linux/bitmap.h>
     15#include <linux/io.h>
     16#include <mach/ilsel.h>
     17
     18/*
     19 * ILSEL is split across:
     20 *
     21 *	ILSEL0 - 0xb8100004 [ Levels  1 -  4 ]
     22 *	ILSEL1 - 0xb8100006 [ Levels  5 -  8 ]
     23 *	ILSEL2 - 0xb8100008 [ Levels  9 - 12 ]
     24 *	ILSEL3 - 0xb810000a [ Levels 13 - 15 ]
     25 *
     26 * With each level being relative to an ilsel_source_t.
     27 */
     28#define ILSEL_BASE	0xb8100004
     29#define ILSEL_LEVELS	15
     30
     31/*
     32 * ILSEL level map, in descending order from the highest level down.
     33 *
     34 * Supported levels are 1 - 15 spread across ILSEL0 - ILSEL4, mapping
     35 * directly to IRLs. As the IRQs are numbered in reverse order relative
     36 * to the interrupt level, the level map is carefully managed to ensure a
     37 * 1:1 mapping between the bit position and the IRQ number.
     38 *
     39 * This careful constructions allows ilsel_enable*() to be referenced
     40 * directly for hooking up an ILSEL set and getting back an IRQ which can
     41 * subsequently be used for internal accounting in the (optional) disable
     42 * path.
     43 */
     44static unsigned long ilsel_level_map;
     45
     46static inline unsigned int ilsel_offset(unsigned int bit)
     47{
     48	return ILSEL_LEVELS - bit - 1;
     49}
     50
     51static inline unsigned long mk_ilsel_addr(unsigned int bit)
     52{
     53	return ILSEL_BASE + ((ilsel_offset(bit) >> 1) & ~0x1);
     54}
     55
     56static inline unsigned int mk_ilsel_shift(unsigned int bit)
     57{
     58	return (ilsel_offset(bit) & 0x3) << 2;
     59}
     60
     61static void __ilsel_enable(ilsel_source_t set, unsigned int bit)
     62{
     63	unsigned int tmp, shift;
     64	unsigned long addr;
     65
     66	pr_notice("enabling ILSEL set %d\n", set);
     67
     68	addr = mk_ilsel_addr(bit);
     69	shift = mk_ilsel_shift(bit);
     70
     71	pr_debug("%s: bit#%d: addr - 0x%08lx (shift %d, set %d)\n",
     72		 __func__, bit, addr, shift, set);
     73
     74	tmp = __raw_readw(addr);
     75	tmp &= ~(0xf << shift);
     76	tmp |= set << shift;
     77	__raw_writew(tmp, addr);
     78}
     79
     80/**
     81 * ilsel_enable - Enable an ILSEL set.
     82 * @set: ILSEL source (see ilsel_source_t enum in include/asm-sh/ilsel.h).
     83 *
     84 * Enables a given non-aliased ILSEL source (<= ILSEL_KEY) at the highest
     85 * available interrupt level. Callers should take care to order callsites
     86 * noting descending interrupt levels. Aliasing FPGA and external board
     87 * IRQs need to use ilsel_enable_fixed().
     88 *
     89 * The return value is an IRQ number that can later be taken down with
     90 * ilsel_disable().
     91 */
     92int ilsel_enable(ilsel_source_t set)
     93{
     94	unsigned int bit;
     95
     96	if (unlikely(set > ILSEL_KEY)) {
     97		pr_err("Aliased sources must use ilsel_enable_fixed()\n");
     98		return -EINVAL;
     99	}
    100
    101	do {
    102		bit = find_first_zero_bit(&ilsel_level_map, ILSEL_LEVELS);
    103	} while (test_and_set_bit(bit, &ilsel_level_map));
    104
    105	__ilsel_enable(set, bit);
    106
    107	return bit;
    108}
    109EXPORT_SYMBOL_GPL(ilsel_enable);
    110
    111/**
    112 * ilsel_enable_fixed - Enable an ILSEL set at a fixed interrupt level
    113 * @set: ILSEL source (see ilsel_source_t enum in include/asm-sh/ilsel.h).
    114 * @level: Interrupt level (1 - 15)
    115 *
    116 * Enables a given ILSEL source at a fixed interrupt level. Necessary
    117 * both for level reservation as well as for aliased sources that only
    118 * exist on special ILSEL#s.
    119 *
    120 * Returns an IRQ number (as ilsel_enable()).
    121 */
    122int ilsel_enable_fixed(ilsel_source_t set, unsigned int level)
    123{
    124	unsigned int bit = ilsel_offset(level - 1);
    125
    126	if (test_and_set_bit(bit, &ilsel_level_map))
    127		return -EBUSY;
    128
    129	__ilsel_enable(set, bit);
    130
    131	return bit;
    132}
    133EXPORT_SYMBOL_GPL(ilsel_enable_fixed);
    134
    135/**
    136 * ilsel_disable - Disable an ILSEL set
    137 * @irq: Bit position for ILSEL set value (retval from enable routines)
    138 *
    139 * Disable a previously enabled ILSEL set.
    140 */
    141void ilsel_disable(unsigned int irq)
    142{
    143	unsigned long addr;
    144	unsigned int tmp;
    145
    146	pr_notice("disabling ILSEL set %d\n", irq);
    147
    148	addr = mk_ilsel_addr(irq);
    149
    150	tmp = __raw_readw(addr);
    151	tmp &= ~(0xf << mk_ilsel_shift(irq));
    152	__raw_writew(tmp, addr);
    153
    154	clear_bit(irq, &ilsel_level_map);
    155}
    156EXPORT_SYMBOL_GPL(ilsel_disable);