123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191 |
- // SPDX-License-Identifier: BSD-3-Clause
- /*
- * Copyright (c) 2020, MIPI Alliance, Inc.
- *
- * Author: Nicolas Pitre <[email protected]>
- */
- #include <linux/bitfield.h>
- #include <linux/bitmap.h>
- #include <linux/device.h>
- #include <linux/errno.h>
- #include <linux/i3c/master.h>
- #include <linux/io.h>
- #include "hci.h"
- #include "dat.h"
- /*
- * Device Address Table Structure
- */
- #define DAT_1_AUTOCMD_HDR_CODE W1_MASK(58, 51)
- #define DAT_1_AUTOCMD_MODE W1_MASK(50, 48)
- #define DAT_1_AUTOCMD_VALUE W1_MASK(47, 40)
- #define DAT_1_AUTOCMD_MASK W1_MASK(39, 32)
- /* DAT_0_I2C_DEVICE W0_BIT_(31) */
- #define DAT_0_DEV_NACK_RETRY_CNT W0_MASK(30, 29)
- #define DAT_0_RING_ID W0_MASK(28, 26)
- #define DAT_0_DYNADDR_PARITY W0_BIT_(23)
- #define DAT_0_DYNAMIC_ADDRESS W0_MASK(22, 16)
- #define DAT_0_TS W0_BIT_(15)
- #define DAT_0_MR_REJECT W0_BIT_(14)
- /* DAT_0_SIR_REJECT W0_BIT_(13) */
- /* DAT_0_IBI_PAYLOAD W0_BIT_(12) */
- #define DAT_0_STATIC_ADDRESS W0_MASK(6, 0)
- #define dat_w0_read(i) readl(hci->DAT_regs + (i) * 8)
- #define dat_w1_read(i) readl(hci->DAT_regs + (i) * 8 + 4)
- #define dat_w0_write(i, v) writel(v, hci->DAT_regs + (i) * 8)
- #define dat_w1_write(i, v) writel(v, hci->DAT_regs + (i) * 8 + 4)
- static inline bool dynaddr_parity(unsigned int addr)
- {
- addr |= 1 << 7;
- addr += addr >> 4;
- addr += addr >> 2;
- addr += addr >> 1;
- return (addr & 1);
- }
- static int hci_dat_v1_init(struct i3c_hci *hci)
- {
- unsigned int dat_idx;
- if (!hci->DAT_regs) {
- dev_err(&hci->master.dev,
- "only DAT in register space is supported at the moment\n");
- return -EOPNOTSUPP;
- }
- if (hci->DAT_entry_size != 8) {
- dev_err(&hci->master.dev,
- "only 8-bytes DAT entries are supported at the moment\n");
- return -EOPNOTSUPP;
- }
- if (!hci->DAT_data) {
- /* use a bitmap for faster free slot search */
- hci->DAT_data = bitmap_zalloc(hci->DAT_entries, GFP_KERNEL);
- if (!hci->DAT_data)
- return -ENOMEM;
- /* clear them */
- for (dat_idx = 0; dat_idx < hci->DAT_entries; dat_idx++) {
- dat_w0_write(dat_idx, 0);
- dat_w1_write(dat_idx, 0);
- }
- }
- return 0;
- }
- static void hci_dat_v1_cleanup(struct i3c_hci *hci)
- {
- bitmap_free(hci->DAT_data);
- hci->DAT_data = NULL;
- }
- static int hci_dat_v1_alloc_entry(struct i3c_hci *hci)
- {
- unsigned int dat_idx;
- int ret;
- if (!hci->DAT_data) {
- ret = hci_dat_v1_init(hci);
- if (ret)
- return ret;
- }
- dat_idx = find_first_zero_bit(hci->DAT_data, hci->DAT_entries);
- if (dat_idx >= hci->DAT_entries)
- return -ENOENT;
- __set_bit(dat_idx, hci->DAT_data);
- /* default flags */
- dat_w0_write(dat_idx, DAT_0_SIR_REJECT | DAT_0_MR_REJECT);
- return dat_idx;
- }
- static void hci_dat_v1_free_entry(struct i3c_hci *hci, unsigned int dat_idx)
- {
- dat_w0_write(dat_idx, 0);
- dat_w1_write(dat_idx, 0);
- if (hci->DAT_data)
- __clear_bit(dat_idx, hci->DAT_data);
- }
- static void hci_dat_v1_set_dynamic_addr(struct i3c_hci *hci,
- unsigned int dat_idx, u8 address)
- {
- u32 dat_w0;
- dat_w0 = dat_w0_read(dat_idx);
- dat_w0 &= ~(DAT_0_DYNAMIC_ADDRESS | DAT_0_DYNADDR_PARITY);
- dat_w0 |= FIELD_PREP(DAT_0_DYNAMIC_ADDRESS, address) |
- (dynaddr_parity(address) ? DAT_0_DYNADDR_PARITY : 0);
- dat_w0_write(dat_idx, dat_w0);
- }
- static void hci_dat_v1_set_static_addr(struct i3c_hci *hci,
- unsigned int dat_idx, u8 address)
- {
- u32 dat_w0;
- dat_w0 = dat_w0_read(dat_idx);
- dat_w0 &= ~DAT_0_STATIC_ADDRESS;
- dat_w0 |= FIELD_PREP(DAT_0_STATIC_ADDRESS, address);
- dat_w0_write(dat_idx, dat_w0);
- }
- static void hci_dat_v1_set_flags(struct i3c_hci *hci, unsigned int dat_idx,
- u32 w0_flags, u32 w1_flags)
- {
- u32 dat_w0, dat_w1;
- dat_w0 = dat_w0_read(dat_idx);
- dat_w1 = dat_w1_read(dat_idx);
- dat_w0 |= w0_flags;
- dat_w1 |= w1_flags;
- dat_w0_write(dat_idx, dat_w0);
- dat_w1_write(dat_idx, dat_w1);
- }
- static void hci_dat_v1_clear_flags(struct i3c_hci *hci, unsigned int dat_idx,
- u32 w0_flags, u32 w1_flags)
- {
- u32 dat_w0, dat_w1;
- dat_w0 = dat_w0_read(dat_idx);
- dat_w1 = dat_w1_read(dat_idx);
- dat_w0 &= ~w0_flags;
- dat_w1 &= ~w1_flags;
- dat_w0_write(dat_idx, dat_w0);
- dat_w1_write(dat_idx, dat_w1);
- }
- static int hci_dat_v1_get_index(struct i3c_hci *hci, u8 dev_addr)
- {
- unsigned int dat_idx;
- u32 dat_w0;
- for_each_set_bit(dat_idx, hci->DAT_data, hci->DAT_entries) {
- dat_w0 = dat_w0_read(dat_idx);
- if (FIELD_GET(DAT_0_DYNAMIC_ADDRESS, dat_w0) == dev_addr)
- return dat_idx;
- }
- return -ENODEV;
- }
- const struct hci_dat_ops mipi_i3c_hci_dat_v1 = {
- .init = hci_dat_v1_init,
- .cleanup = hci_dat_v1_cleanup,
- .alloc_entry = hci_dat_v1_alloc_entry,
- .free_entry = hci_dat_v1_free_entry,
- .set_dynamic_addr = hci_dat_v1_set_dynamic_addr,
- .set_static_addr = hci_dat_v1_set_static_addr,
- .set_flags = hci_dat_v1_set_flags,
- .clear_flags = hci_dat_v1_clear_flags,
- .get_index = hci_dat_v1_get_index,
- };
|