linux/drivers/i3c/master/mipi-i3c-hci/dat_v1.c
<<
>>
Prefs
   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 (dat_idx = find_first_bit(hci->DAT_data, hci->DAT_entries);
 164             dat_idx < hci->DAT_entries;
 165             dat_idx = find_next_bit(hci->DAT_data, hci->DAT_entries, dat_idx)) {
 166                dat_w0 = dat_w0_read(dat_idx);
 167                if (FIELD_GET(DAT_0_DYNAMIC_ADDRESS, dat_w0) == dev_addr)
 168                        return dat_idx;
 169        }
 170
 171        return -ENODEV;
 172}
 173
 174const struct hci_dat_ops mipi_i3c_hci_dat_v1 = {
 175        .init                   = hci_dat_v1_init,
 176        .cleanup                = hci_dat_v1_cleanup,
 177        .alloc_entry            = hci_dat_v1_alloc_entry,
 178        .free_entry             = hci_dat_v1_free_entry,
 179        .set_dynamic_addr       = hci_dat_v1_set_dynamic_addr,
 180        .set_static_addr        = hci_dat_v1_set_static_addr,
 181        .set_flags              = hci_dat_v1_set_flags,
 182        .clear_flags            = hci_dat_v1_clear_flags,
 183        .get_index              = hci_dat_v1_get_index,
 184};
 185