162306a36Sopenharmony_ci// SPDX-License-Identifier: BSD-3-Clause 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (c) 2020, MIPI Alliance, Inc. 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Author: Nicolas Pitre <npitre@baylibre.com> 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include <linux/bitfield.h> 962306a36Sopenharmony_ci#include <linux/bitmap.h> 1062306a36Sopenharmony_ci#include <linux/device.h> 1162306a36Sopenharmony_ci#include <linux/errno.h> 1262306a36Sopenharmony_ci#include <linux/i3c/master.h> 1362306a36Sopenharmony_ci#include <linux/io.h> 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_ci#include "hci.h" 1662306a36Sopenharmony_ci#include "dat.h" 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci/* 2062306a36Sopenharmony_ci * Device Address Table Structure 2162306a36Sopenharmony_ci */ 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci#define DAT_1_AUTOCMD_HDR_CODE W1_MASK(58, 51) 2462306a36Sopenharmony_ci#define DAT_1_AUTOCMD_MODE W1_MASK(50, 48) 2562306a36Sopenharmony_ci#define DAT_1_AUTOCMD_VALUE W1_MASK(47, 40) 2662306a36Sopenharmony_ci#define DAT_1_AUTOCMD_MASK W1_MASK(39, 32) 2762306a36Sopenharmony_ci/* DAT_0_I2C_DEVICE W0_BIT_(31) */ 2862306a36Sopenharmony_ci#define DAT_0_DEV_NACK_RETRY_CNT W0_MASK(30, 29) 2962306a36Sopenharmony_ci#define DAT_0_RING_ID W0_MASK(28, 26) 3062306a36Sopenharmony_ci#define DAT_0_DYNADDR_PARITY W0_BIT_(23) 3162306a36Sopenharmony_ci#define DAT_0_DYNAMIC_ADDRESS W0_MASK(22, 16) 3262306a36Sopenharmony_ci#define DAT_0_TS W0_BIT_(15) 3362306a36Sopenharmony_ci#define DAT_0_MR_REJECT W0_BIT_(14) 3462306a36Sopenharmony_ci/* DAT_0_SIR_REJECT W0_BIT_(13) */ 3562306a36Sopenharmony_ci/* DAT_0_IBI_PAYLOAD W0_BIT_(12) */ 3662306a36Sopenharmony_ci#define DAT_0_STATIC_ADDRESS W0_MASK(6, 0) 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci#define dat_w0_read(i) readl(hci->DAT_regs + (i) * 8) 3962306a36Sopenharmony_ci#define dat_w1_read(i) readl(hci->DAT_regs + (i) * 8 + 4) 4062306a36Sopenharmony_ci#define dat_w0_write(i, v) writel(v, hci->DAT_regs + (i) * 8) 4162306a36Sopenharmony_ci#define dat_w1_write(i, v) writel(v, hci->DAT_regs + (i) * 8 + 4) 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_cistatic inline bool dynaddr_parity(unsigned int addr) 4462306a36Sopenharmony_ci{ 4562306a36Sopenharmony_ci addr |= 1 << 7; 4662306a36Sopenharmony_ci addr += addr >> 4; 4762306a36Sopenharmony_ci addr += addr >> 2; 4862306a36Sopenharmony_ci addr += addr >> 1; 4962306a36Sopenharmony_ci return (addr & 1); 5062306a36Sopenharmony_ci} 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_cistatic int hci_dat_v1_init(struct i3c_hci *hci) 5362306a36Sopenharmony_ci{ 5462306a36Sopenharmony_ci unsigned int dat_idx; 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci if (!hci->DAT_regs) { 5762306a36Sopenharmony_ci dev_err(&hci->master.dev, 5862306a36Sopenharmony_ci "only DAT in register space is supported at the moment\n"); 5962306a36Sopenharmony_ci return -EOPNOTSUPP; 6062306a36Sopenharmony_ci } 6162306a36Sopenharmony_ci if (hci->DAT_entry_size != 8) { 6262306a36Sopenharmony_ci dev_err(&hci->master.dev, 6362306a36Sopenharmony_ci "only 8-bytes DAT entries are supported at the moment\n"); 6462306a36Sopenharmony_ci return -EOPNOTSUPP; 6562306a36Sopenharmony_ci } 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci if (!hci->DAT_data) { 6862306a36Sopenharmony_ci /* use a bitmap for faster free slot search */ 6962306a36Sopenharmony_ci hci->DAT_data = bitmap_zalloc(hci->DAT_entries, GFP_KERNEL); 7062306a36Sopenharmony_ci if (!hci->DAT_data) 7162306a36Sopenharmony_ci return -ENOMEM; 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci /* clear them */ 7462306a36Sopenharmony_ci for (dat_idx = 0; dat_idx < hci->DAT_entries; dat_idx++) { 7562306a36Sopenharmony_ci dat_w0_write(dat_idx, 0); 7662306a36Sopenharmony_ci dat_w1_write(dat_idx, 0); 7762306a36Sopenharmony_ci } 7862306a36Sopenharmony_ci } 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ci return 0; 8162306a36Sopenharmony_ci} 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_cistatic void hci_dat_v1_cleanup(struct i3c_hci *hci) 8462306a36Sopenharmony_ci{ 8562306a36Sopenharmony_ci bitmap_free(hci->DAT_data); 8662306a36Sopenharmony_ci hci->DAT_data = NULL; 8762306a36Sopenharmony_ci} 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_cistatic int hci_dat_v1_alloc_entry(struct i3c_hci *hci) 9062306a36Sopenharmony_ci{ 9162306a36Sopenharmony_ci unsigned int dat_idx; 9262306a36Sopenharmony_ci int ret; 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci if (!hci->DAT_data) { 9562306a36Sopenharmony_ci ret = hci_dat_v1_init(hci); 9662306a36Sopenharmony_ci if (ret) 9762306a36Sopenharmony_ci return ret; 9862306a36Sopenharmony_ci } 9962306a36Sopenharmony_ci dat_idx = find_first_zero_bit(hci->DAT_data, hci->DAT_entries); 10062306a36Sopenharmony_ci if (dat_idx >= hci->DAT_entries) 10162306a36Sopenharmony_ci return -ENOENT; 10262306a36Sopenharmony_ci __set_bit(dat_idx, hci->DAT_data); 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci /* default flags */ 10562306a36Sopenharmony_ci dat_w0_write(dat_idx, DAT_0_SIR_REJECT | DAT_0_MR_REJECT); 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci return dat_idx; 10862306a36Sopenharmony_ci} 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_cistatic void hci_dat_v1_free_entry(struct i3c_hci *hci, unsigned int dat_idx) 11162306a36Sopenharmony_ci{ 11262306a36Sopenharmony_ci dat_w0_write(dat_idx, 0); 11362306a36Sopenharmony_ci dat_w1_write(dat_idx, 0); 11462306a36Sopenharmony_ci if (hci->DAT_data) 11562306a36Sopenharmony_ci __clear_bit(dat_idx, hci->DAT_data); 11662306a36Sopenharmony_ci} 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_cistatic void hci_dat_v1_set_dynamic_addr(struct i3c_hci *hci, 11962306a36Sopenharmony_ci unsigned int dat_idx, u8 address) 12062306a36Sopenharmony_ci{ 12162306a36Sopenharmony_ci u32 dat_w0; 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci dat_w0 = dat_w0_read(dat_idx); 12462306a36Sopenharmony_ci dat_w0 &= ~(DAT_0_DYNAMIC_ADDRESS | DAT_0_DYNADDR_PARITY); 12562306a36Sopenharmony_ci dat_w0 |= FIELD_PREP(DAT_0_DYNAMIC_ADDRESS, address) | 12662306a36Sopenharmony_ci (dynaddr_parity(address) ? DAT_0_DYNADDR_PARITY : 0); 12762306a36Sopenharmony_ci dat_w0_write(dat_idx, dat_w0); 12862306a36Sopenharmony_ci} 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_cistatic void hci_dat_v1_set_static_addr(struct i3c_hci *hci, 13162306a36Sopenharmony_ci unsigned int dat_idx, u8 address) 13262306a36Sopenharmony_ci{ 13362306a36Sopenharmony_ci u32 dat_w0; 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci dat_w0 = dat_w0_read(dat_idx); 13662306a36Sopenharmony_ci dat_w0 &= ~DAT_0_STATIC_ADDRESS; 13762306a36Sopenharmony_ci dat_w0 |= FIELD_PREP(DAT_0_STATIC_ADDRESS, address); 13862306a36Sopenharmony_ci dat_w0_write(dat_idx, dat_w0); 13962306a36Sopenharmony_ci} 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_cistatic void hci_dat_v1_set_flags(struct i3c_hci *hci, unsigned int dat_idx, 14262306a36Sopenharmony_ci u32 w0_flags, u32 w1_flags) 14362306a36Sopenharmony_ci{ 14462306a36Sopenharmony_ci u32 dat_w0, dat_w1; 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci dat_w0 = dat_w0_read(dat_idx); 14762306a36Sopenharmony_ci dat_w1 = dat_w1_read(dat_idx); 14862306a36Sopenharmony_ci dat_w0 |= w0_flags; 14962306a36Sopenharmony_ci dat_w1 |= w1_flags; 15062306a36Sopenharmony_ci dat_w0_write(dat_idx, dat_w0); 15162306a36Sopenharmony_ci dat_w1_write(dat_idx, dat_w1); 15262306a36Sopenharmony_ci} 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_cistatic void hci_dat_v1_clear_flags(struct i3c_hci *hci, unsigned int dat_idx, 15562306a36Sopenharmony_ci u32 w0_flags, u32 w1_flags) 15662306a36Sopenharmony_ci{ 15762306a36Sopenharmony_ci u32 dat_w0, dat_w1; 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci dat_w0 = dat_w0_read(dat_idx); 16062306a36Sopenharmony_ci dat_w1 = dat_w1_read(dat_idx); 16162306a36Sopenharmony_ci dat_w0 &= ~w0_flags; 16262306a36Sopenharmony_ci dat_w1 &= ~w1_flags; 16362306a36Sopenharmony_ci dat_w0_write(dat_idx, dat_w0); 16462306a36Sopenharmony_ci dat_w1_write(dat_idx, dat_w1); 16562306a36Sopenharmony_ci} 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_cistatic int hci_dat_v1_get_index(struct i3c_hci *hci, u8 dev_addr) 16862306a36Sopenharmony_ci{ 16962306a36Sopenharmony_ci unsigned int dat_idx; 17062306a36Sopenharmony_ci u32 dat_w0; 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ci for_each_set_bit(dat_idx, hci->DAT_data, hci->DAT_entries) { 17362306a36Sopenharmony_ci dat_w0 = dat_w0_read(dat_idx); 17462306a36Sopenharmony_ci if (FIELD_GET(DAT_0_DYNAMIC_ADDRESS, dat_w0) == dev_addr) 17562306a36Sopenharmony_ci return dat_idx; 17662306a36Sopenharmony_ci } 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci return -ENODEV; 17962306a36Sopenharmony_ci} 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ciconst struct hci_dat_ops mipi_i3c_hci_dat_v1 = { 18262306a36Sopenharmony_ci .init = hci_dat_v1_init, 18362306a36Sopenharmony_ci .cleanup = hci_dat_v1_cleanup, 18462306a36Sopenharmony_ci .alloc_entry = hci_dat_v1_alloc_entry, 18562306a36Sopenharmony_ci .free_entry = hci_dat_v1_free_entry, 18662306a36Sopenharmony_ci .set_dynamic_addr = hci_dat_v1_set_dynamic_addr, 18762306a36Sopenharmony_ci .set_static_addr = hci_dat_v1_set_static_addr, 18862306a36Sopenharmony_ci .set_flags = hci_dat_v1_set_flags, 18962306a36Sopenharmony_ci .clear_flags = hci_dat_v1_clear_flags, 19062306a36Sopenharmony_ci .get_index = hci_dat_v1_get_index, 19162306a36Sopenharmony_ci}; 192