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

dat_v1.c (4602B)


      1// SPDX-License-Identifier: BSD-3-Clause
      2/*
      3 * Copyright (c) 2020, MIPI Alliance, Inc.
      4 *
      5 * Author: Nicolas Pitre <npitre@baylibre.com>
      6 */
      7
      8#include <linux/bitfield.h>
      9#include <linux/bitmap.h>
     10#include <linux/device.h>
     11#include <linux/errno.h>
     12#include <linux/i3c/master.h>
     13#include <linux/io.h>
     14
     15#include "hci.h"
     16#include "dat.h"
     17
     18
     19/*
     20 * Device Address Table Structure
     21 */
     22
     23#define DAT_1_AUTOCMD_HDR_CODE		W1_MASK(58, 51)
     24#define DAT_1_AUTOCMD_MODE		W1_MASK(50, 48)
     25#define DAT_1_AUTOCMD_VALUE		W1_MASK(47, 40)
     26#define DAT_1_AUTOCMD_MASK		W1_MASK(39, 32)
     27/*	DAT_0_I2C_DEVICE		W0_BIT_(31) */
     28#define DAT_0_DEV_NACK_RETRY_CNT	W0_MASK(30, 29)
     29#define DAT_0_RING_ID			W0_MASK(28, 26)
     30#define DAT_0_DYNADDR_PARITY		W0_BIT_(23)
     31#define DAT_0_DYNAMIC_ADDRESS		W0_MASK(22, 16)
     32#define DAT_0_TS			W0_BIT_(15)
     33#define DAT_0_MR_REJECT			W0_BIT_(14)
     34/*	DAT_0_SIR_REJECT		W0_BIT_(13) */
     35/*	DAT_0_IBI_PAYLOAD		W0_BIT_(12) */
     36#define DAT_0_STATIC_ADDRESS		W0_MASK(6, 0)
     37
     38#define dat_w0_read(i)		readl(hci->DAT_regs + (i) * 8)
     39#define dat_w1_read(i)		readl(hci->DAT_regs + (i) * 8 + 4)
     40#define dat_w0_write(i, v)	writel(v, hci->DAT_regs + (i) * 8)
     41#define dat_w1_write(i, v)	writel(v, hci->DAT_regs + (i) * 8 + 4)
     42
     43static inline bool dynaddr_parity(unsigned int addr)
     44{
     45	addr |= 1 << 7;
     46	addr += addr >> 4;
     47	addr += addr >> 2;
     48	addr += addr >> 1;
     49	return (addr & 1);
     50}
     51
     52static int hci_dat_v1_init(struct i3c_hci *hci)
     53{
     54	unsigned int dat_idx;
     55
     56	if (!hci->DAT_regs) {
     57		dev_err(&hci->master.dev,
     58			"only DAT in register space is supported at the moment\n");
     59		return -EOPNOTSUPP;
     60	}
     61	if (hci->DAT_entry_size != 8) {
     62		dev_err(&hci->master.dev,
     63			"only 8-bytes DAT entries are supported at the moment\n");
     64		return -EOPNOTSUPP;
     65	}
     66
     67	/* use a bitmap for faster free slot search */
     68	hci->DAT_data = bitmap_zalloc(hci->DAT_entries, GFP_KERNEL);
     69	if (!hci->DAT_data)
     70		return -ENOMEM;
     71
     72	/* clear them */
     73	for (dat_idx = 0; dat_idx < hci->DAT_entries; dat_idx++) {
     74		dat_w0_write(dat_idx, 0);
     75		dat_w1_write(dat_idx, 0);
     76	}
     77
     78	return 0;
     79}
     80
     81static void hci_dat_v1_cleanup(struct i3c_hci *hci)
     82{
     83	bitmap_free(hci->DAT_data);
     84	hci->DAT_data = NULL;
     85}
     86
     87static int hci_dat_v1_alloc_entry(struct i3c_hci *hci)
     88{
     89	unsigned int dat_idx;
     90
     91	dat_idx = find_first_zero_bit(hci->DAT_data, hci->DAT_entries);
     92	if (dat_idx >= hci->DAT_entries)
     93		return -ENOENT;
     94	__set_bit(dat_idx, hci->DAT_data);
     95
     96	/* default flags */
     97	dat_w0_write(dat_idx, DAT_0_SIR_REJECT | DAT_0_MR_REJECT);
     98
     99	return dat_idx;
    100}
    101
    102static void hci_dat_v1_free_entry(struct i3c_hci *hci, unsigned int dat_idx)
    103{
    104	dat_w0_write(dat_idx, 0);
    105	dat_w1_write(dat_idx, 0);
    106	__clear_bit(dat_idx, hci->DAT_data);
    107}
    108
    109static void hci_dat_v1_set_dynamic_addr(struct i3c_hci *hci,
    110					unsigned int dat_idx, u8 address)
    111{
    112	u32 dat_w0;
    113
    114	dat_w0 = dat_w0_read(dat_idx);
    115	dat_w0 &= ~(DAT_0_DYNAMIC_ADDRESS | DAT_0_DYNADDR_PARITY);
    116	dat_w0 |= FIELD_PREP(DAT_0_DYNAMIC_ADDRESS, address) |
    117		  (dynaddr_parity(address) ? DAT_0_DYNADDR_PARITY : 0);
    118	dat_w0_write(dat_idx, dat_w0);
    119}
    120
    121static void hci_dat_v1_set_static_addr(struct i3c_hci *hci,
    122				       unsigned int dat_idx, u8 address)
    123{
    124	u32 dat_w0;
    125
    126	dat_w0 = dat_w0_read(dat_idx);
    127	dat_w0 &= ~DAT_0_STATIC_ADDRESS;
    128	dat_w0 |= FIELD_PREP(DAT_0_STATIC_ADDRESS, address);
    129	dat_w0_write(dat_idx, dat_w0);
    130}
    131
    132static void hci_dat_v1_set_flags(struct i3c_hci *hci, unsigned int dat_idx,
    133				 u32 w0_flags, u32 w1_flags)
    134{
    135	u32 dat_w0, dat_w1;
    136
    137	dat_w0 = dat_w0_read(dat_idx);
    138	dat_w1 = dat_w1_read(dat_idx);
    139	dat_w0 |= w0_flags;
    140	dat_w1 |= w1_flags;
    141	dat_w0_write(dat_idx, dat_w0);
    142	dat_w1_write(dat_idx, dat_w1);
    143}
    144
    145static void hci_dat_v1_clear_flags(struct i3c_hci *hci, unsigned int dat_idx,
    146				   u32 w0_flags, u32 w1_flags)
    147{
    148	u32 dat_w0, dat_w1;
    149
    150	dat_w0 = dat_w0_read(dat_idx);
    151	dat_w1 = dat_w1_read(dat_idx);
    152	dat_w0 &= ~w0_flags;
    153	dat_w1 &= ~w1_flags;
    154	dat_w0_write(dat_idx, dat_w0);
    155	dat_w1_write(dat_idx, dat_w1);
    156}
    157
    158static int hci_dat_v1_get_index(struct i3c_hci *hci, u8 dev_addr)
    159{
    160	unsigned int dat_idx;
    161	u32 dat_w0;
    162
    163	for_each_set_bit(dat_idx, hci->DAT_data, hci->DAT_entries) {
    164		dat_w0 = dat_w0_read(dat_idx);
    165		if (FIELD_GET(DAT_0_DYNAMIC_ADDRESS, dat_w0) == dev_addr)
    166			return dat_idx;
    167	}
    168
    169	return -ENODEV;
    170}
    171
    172const struct hci_dat_ops mipi_i3c_hci_dat_v1 = {
    173	.init			= hci_dat_v1_init,
    174	.cleanup		= hci_dat_v1_cleanup,
    175	.alloc_entry		= hci_dat_v1_alloc_entry,
    176	.free_entry		= hci_dat_v1_free_entry,
    177	.set_dynamic_addr	= hci_dat_v1_set_dynamic_addr,
    178	.set_static_addr	= hci_dat_v1_set_static_addr,
    179	.set_flags		= hci_dat_v1_set_flags,
    180	.clear_flags		= hci_dat_v1_clear_flags,
    181	.get_index		= hci_dat_v1_get_index,
    182};