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