192 lines · c
1// SPDX-License-Identifier: BSD-3-Clause2/*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 Structure21 */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 if (!hci->DAT_data) {68 /* use a bitmap for faster free slot search */69 hci->DAT_data = bitmap_zalloc(hci->DAT_entries, GFP_KERNEL);70 if (!hci->DAT_data)71 return -ENOMEM;72 73 /* clear them */74 for (dat_idx = 0; dat_idx < hci->DAT_entries; dat_idx++) {75 dat_w0_write(dat_idx, 0);76 dat_w1_write(dat_idx, 0);77 }78 }79 80 return 0;81}82 83static void hci_dat_v1_cleanup(struct i3c_hci *hci)84{85 bitmap_free(hci->DAT_data);86 hci->DAT_data = NULL;87}88 89static int hci_dat_v1_alloc_entry(struct i3c_hci *hci)90{91 unsigned int dat_idx;92 int ret;93 94 if (!hci->DAT_data) {95 ret = hci_dat_v1_init(hci);96 if (ret)97 return ret;98 }99 dat_idx = find_first_zero_bit(hci->DAT_data, hci->DAT_entries);100 if (dat_idx >= hci->DAT_entries)101 return -ENOENT;102 __set_bit(dat_idx, hci->DAT_data);103 104 /* default flags */105 dat_w0_write(dat_idx, DAT_0_SIR_REJECT | DAT_0_MR_REJECT);106 107 return dat_idx;108}109 110static void hci_dat_v1_free_entry(struct i3c_hci *hci, unsigned int dat_idx)111{112 dat_w0_write(dat_idx, 0);113 dat_w1_write(dat_idx, 0);114 if (hci->DAT_data)115 __clear_bit(dat_idx, hci->DAT_data);116}117 118static void hci_dat_v1_set_dynamic_addr(struct i3c_hci *hci,119 unsigned int dat_idx, u8 address)120{121 u32 dat_w0;122 123 dat_w0 = dat_w0_read(dat_idx);124 dat_w0 &= ~(DAT_0_DYNAMIC_ADDRESS | DAT_0_DYNADDR_PARITY);125 dat_w0 |= FIELD_PREP(DAT_0_DYNAMIC_ADDRESS, address) |126 (dynaddr_parity(address) ? DAT_0_DYNADDR_PARITY : 0);127 dat_w0_write(dat_idx, dat_w0);128}129 130static void hci_dat_v1_set_static_addr(struct i3c_hci *hci,131 unsigned int dat_idx, u8 address)132{133 u32 dat_w0;134 135 dat_w0 = dat_w0_read(dat_idx);136 dat_w0 &= ~DAT_0_STATIC_ADDRESS;137 dat_w0 |= FIELD_PREP(DAT_0_STATIC_ADDRESS, address);138 dat_w0_write(dat_idx, dat_w0);139}140 141static void hci_dat_v1_set_flags(struct i3c_hci *hci, unsigned int dat_idx,142 u32 w0_flags, u32 w1_flags)143{144 u32 dat_w0, dat_w1;145 146 dat_w0 = dat_w0_read(dat_idx);147 dat_w1 = dat_w1_read(dat_idx);148 dat_w0 |= w0_flags;149 dat_w1 |= w1_flags;150 dat_w0_write(dat_idx, dat_w0);151 dat_w1_write(dat_idx, dat_w1);152}153 154static void hci_dat_v1_clear_flags(struct i3c_hci *hci, unsigned int dat_idx,155 u32 w0_flags, u32 w1_flags)156{157 u32 dat_w0, dat_w1;158 159 dat_w0 = dat_w0_read(dat_idx);160 dat_w1 = dat_w1_read(dat_idx);161 dat_w0 &= ~w0_flags;162 dat_w1 &= ~w1_flags;163 dat_w0_write(dat_idx, dat_w0);164 dat_w1_write(dat_idx, dat_w1);165}166 167static int hci_dat_v1_get_index(struct i3c_hci *hci, u8 dev_addr)168{169 unsigned int dat_idx;170 u32 dat_w0;171 172 for_each_set_bit(dat_idx, hci->DAT_data, hci->DAT_entries) {173 dat_w0 = dat_w0_read(dat_idx);174 if (FIELD_GET(DAT_0_DYNAMIC_ADDRESS, dat_w0) == dev_addr)175 return dat_idx;176 }177 178 return -ENODEV;179}180 181const struct hci_dat_ops mipi_i3c_hci_dat_v1 = {182 .init = hci_dat_v1_init,183 .cleanup = hci_dat_v1_cleanup,184 .alloc_entry = hci_dat_v1_alloc_entry,185 .free_entry = hci_dat_v1_free_entry,186 .set_dynamic_addr = hci_dat_v1_set_dynamic_addr,187 .set_static_addr = hci_dat_v1_set_static_addr,188 .set_flags = hci_dat_v1_set_flags,189 .clear_flags = hci_dat_v1_clear_flags,190 .get_index = hci_dat_v1_get_index,191};192