diff --git a/Documentation/devicetree/bindings/i3c/cdns,i3c-master.yaml b/Documentation/devicetree/bindings/i3c/cdns,i3c-master.yaml index cc40d25358ecf..ebc572300e3b7 100644 --- a/Documentation/devicetree/bindings/i3c/cdns,i3c-master.yaml +++ b/Documentation/devicetree/bindings/i3c/cdns,i3c-master.yaml @@ -14,7 +14,9 @@ allOf: properties: compatible: - const: cdns,i3c-master + enum: + - cdns,i3c-master + - phytium,cdns-i3c-master reg: maxItems: 1 diff --git a/Documentation/devicetree/bindings/i3c/phytium,i3c-master.yaml b/Documentation/devicetree/bindings/i3c/phytium,i3c-master.yaml new file mode 100644 index 0000000000000..0e004735fd38d --- /dev/null +++ b/Documentation/devicetree/bindings/i3c/phytium,i3c-master.yaml @@ -0,0 +1,35 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause + +* Phytium I3C controller + +This I3C controller is for Phytium Soc. + +Required properties: +- compatible: Shall be "phytium,cdns-i3c-master" +- clocks: Shall reference the pclk and sysclk +- clock-names: Shall contain "pclk" and "sysclk" +- interrupts: The interrupt line connected to this I3C master +- reg: I3C master registers +- #address-cells: Shall be set to 1 +- #size-cells: Shall be set to 0 +- i2c-scl-hz: I2C CLK frequency +- i3c-scl-hz: I3C CLK frequency + +Example: + + i3c-master@28045000 { + compatible = "phytium,cdns-i3c-master"; + reg = <0x0 0x28045000 0x0 0x1000>; + interrupts = ; + clocks = <&coreclock>, <&i3csysclock>; + clock-names = "pclk", "sysclk"; + #address-cells = <1>; + #size-cells = <0>; + i2c-scl-hz = <400000>; + i3c-scl-hz = <1000000>; + + nunchuk: nunchuk@52 { + compatible = "nintendo,nunchuk"; + reg = <0x52 0x0 0x10>; + }; + }; diff --git a/drivers/i3c/Makefile b/drivers/i3c/Makefile index 11982efbc6d91..02d9b450fb864 100644 --- a/drivers/i3c/Makefile +++ b/drivers/i3c/Makefile @@ -1,4 +1,5 @@ # SPDX-License-Identifier: GPL-2.0 i3c-y := device.o master.o obj-$(CONFIG_I3C) += i3c.o +i3c-$(CONFIG_ACPI) += i3c_master_acpi.o obj-$(CONFIG_I3C) += master/ diff --git a/drivers/i3c/i3c_master_acpi.c b/drivers/i3c/i3c_master_acpi.c new file mode 100644 index 0000000000000..b6a51aa625694 --- /dev/null +++ b/drivers/i3c/i3c_master_acpi.c @@ -0,0 +1,96 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Driver for I3C ACPI Interface + * + * Copyright (C) 2021-2023 Phytium Technology Co., Ltd. + */ + +#include +#include +#include +#include +#include +#include "i3c_master_acpi.h" + +static int i3c_master_acpi_parse_val(union acpi_object *store_elements, char *method, + int count, u32 *value) +{ + int i, package_count, ret = -1; + union acpi_object *acpi_elements; + + for (i = 0; i < count; i++) { + acpi_elements = store_elements; + if (acpi_elements[i].type == ACPI_TYPE_PACKAGE) { + package_count = acpi_elements[i].package.count; + + if (package_count == 2) { + acpi_elements = acpi_elements[i].package.elements; + if ((acpi_elements[0].type == ACPI_TYPE_STRING) && + (!strcmp(acpi_elements[0].string.pointer, method))) { + if (acpi_elements[1].type == ACPI_TYPE_INTEGER) { + *value = acpi_elements[1].integer.value; + ret = 0; + break; + } + } + } + } + } + return ret; +} + +int i3c_master_acpi_get_params(acpi_handle handle, char *method, u32 *value) +{ + struct acpi_buffer buf = { ACPI_ALLOCATE_BUFFER }; + acpi_status status = 0; + union acpi_object *obj; + int count, ret = -1; + + status = acpi_evaluate_object(handle, "_DSD", NULL, &buf); + if (ACPI_FAILURE(status)) { + kfree(buf.pointer); + return -2; + } + + obj = (union acpi_object *)buf.pointer; + + if (obj->type == ACPI_TYPE_PACKAGE) { + union acpi_object *acpi_elements; + union acpi_object *store_elements; + + acpi_elements = obj->package.elements; + if ((obj->package.count >= 2) && (acpi_elements[1].type == ACPI_TYPE_PACKAGE)) { + count = acpi_elements[1].package.count; + acpi_elements = acpi_elements[1].package.elements; + + store_elements = acpi_elements; + ret = i3c_master_acpi_parse_val(store_elements, method, count, value); + } + } + + kfree(buf.pointer); + return ret; +} +EXPORT_SYMBOL_GPL(i3c_master_acpi_get_params); + +void i3c_master_acpi_clk_params(acpi_handle handle, char *method, + u32 *pres_ctrl0, u32 *pres_ctrl1, u32 *thd_delay) +{ + struct acpi_buffer buf = { ACPI_ALLOCATE_BUFFER }; + union acpi_object *obj; + + if (ACPI_FAILURE(acpi_evaluate_object(handle, method, NULL, &buf))) + return; + + obj = (union acpi_object *)buf.pointer; + if (obj->type == ACPI_TYPE_PACKAGE && obj->package.count == 3) { + const union acpi_object *objs = obj->package.elements; + + *pres_ctrl0 = (u32)objs[0].integer.value; + *pres_ctrl1 = (u32)objs[1].integer.value; + *thd_delay = (u32)objs[2].integer.value; + } + + kfree(buf.pointer); +} +EXPORT_SYMBOL_GPL(i3c_master_acpi_clk_params); diff --git a/drivers/i3c/i3c_master_acpi.h b/drivers/i3c/i3c_master_acpi.h new file mode 100644 index 0000000000000..61fa62ff12fba --- /dev/null +++ b/drivers/i3c/i3c_master_acpi.h @@ -0,0 +1,18 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Driver for I3C ACPI Interface + * + * Copyright (C) 2021-2023 Phytium Technology Co., Ltd. + */ + +#ifndef __I3C_MASTER_ACPI_H +#define __I3C_MASTER_ACPI_H + +#include +#include + +int i3c_master_acpi_get_params(acpi_handle handle, char *method, u32 *value); +void i3c_master_acpi_clk_params(acpi_handle handle, char *method, + u32 *pres_ctrl0, u32 *pres_ctrl1, u32 *thd_delay); + +#endif diff --git a/drivers/i3c/master.c b/drivers/i3c/master.c index 0e9ff5500a777..a72fdea49116e 100644 --- a/drivers/i3c/master.c +++ b/drivers/i3c/master.c @@ -5,6 +5,8 @@ * Author: Boris Brezillon */ +#include +#include #include #include #include @@ -17,8 +19,25 @@ #include #include +#if defined(CONFIG_ACPI) && defined(CONFIG_ARCH_PHYTIUM) +#include "i3c_master_acpi.h" +#endif #include "internals.h" +struct i3c_acpi_lookup { + u64 pid; + acpi_handle adapter_handle; + acpi_handle device_handle; + acpi_handle search_handle; + int n; + int index; + u32 speed; + u32 min_speed; + u32 force_speed; + u32 slave_address; + u32 lvr; +}; + static DEFINE_IDR(i3c_bus_idr); static DEFINE_MUTEX(i3c_core_lock); static int __i3c_first_dynamic_bus_num; @@ -263,6 +282,88 @@ static ssize_t modalias_show(struct device *dev, } static DEVICE_ATTR_RO(modalias); +static ssize_t reg_read_store(struct device *dev, + struct device_attribute *da, + const char *buf, size_t size) +{ + struct i3c_device *i3c = dev_to_i3cdev(dev); + struct i3c_priv_xfer xfers[2]; + u8 status = 0; + long value; + __u8 reg; + __u8 val; + + status = kstrtol(buf, 0, &value); + if (status) + return status; + + reg = (__u8)value; + + xfers[0].rnw = false; + xfers[0].len = 1; + xfers[0].data.out = ® + + xfers[1].rnw = true; + xfers[1].len = 1; + xfers[1].data.in = &val; + + i3c_bus_normaluse_lock(i3c->bus); + i3c_device_do_priv_xfers(i3c, xfers, 2); + dev_info(dev, "reg read reg =0x%x, val=%x\n", reg, val); + i3c_bus_normaluse_unlock(i3c->bus); + + return size; +} +static DEVICE_ATTR_WO(reg_read); + +static ssize_t reg_write_store(struct device *dev, + struct device_attribute *da, + const char *buf, size_t size) +{ + struct i3c_device *i3c = dev_to_i3cdev(dev); + struct i3c_priv_xfer xfers[2]; + u8 status = 0; + char *p; + char *token; + long value; + __u8 reg; + u16 val; + + p = kmalloc(size, GFP_KERNEL); + strscpy(p, buf, size); + token = strsep(&p, " "); + + if (!token) + return -EINVAL; + + status = kstrtol(token, 0, &value); + if (status) + return status; + + reg = (u8)value; + token = strsep(&p, " "); + if (!token) + return -EINVAL; + + status = kstrtol(token, 0, &value); + if (status) + return status; + + val = ((u8)value << 8) | reg; + xfers[0].rnw = false; + xfers[0].len = 2; + xfers[0].data.out = &val; + + i3c_bus_normaluse_lock(i3c->bus); + i3c_device_do_priv_xfers(i3c, xfers, 1); + dev_info(dev, "reg write reg =0x%x, val=%x\n", reg, val); + i3c_bus_normaluse_unlock(i3c->bus); + + kfree(p); + return size; +} +static DEVICE_ATTR_WO(reg_write); + static struct attribute *i3c_device_attrs[] = { &dev_attr_bcr.attr, &dev_attr_dcr.attr, @@ -270,6 +371,8 @@ static struct attribute *i3c_device_attrs[] = { &dev_attr_dynamic_address.attr, &dev_attr_hdrcap.attr, &dev_attr_modalias.attr, + &dev_attr_reg_read.attr, + &dev_attr_reg_write.attr, NULL, }; ATTRIBUTE_GROUPS(i3c_device); @@ -1521,9 +1624,10 @@ i3c_master_register_new_i3c_devs(struct i3c_master_controller *master) dev_set_name(&desc->dev->dev, "%d-%llx", master->bus.id, desc->info.pid); - if (desc->boardinfo) + if (desc->boardinfo) { desc->dev->dev.of_node = desc->boardinfo->of_node; - + desc->dev->dev.fwnode = desc->boardinfo->fwnode; + } ret = device_register(&desc->dev->dev); if (ret) { dev_err(&master->dev, @@ -2148,6 +2252,182 @@ static int of_populate_i3c_bus(struct i3c_master_controller *master) return 0; } +#if defined(CONFIG_ACPI) && defined(CONFIG_ARCH_PHYTIUM) +static int i3c_acpi_master_add_i2c_boardinfo(struct i3c_master_controller *master, + struct acpi_device *adev, struct i3c_acpi_lookup *look_up) +{ + struct i2c_dev_boardinfo *boardinfo; + struct device *dev = &master->dev; + u32 addr = look_up->slave_address; + + boardinfo = devm_kzalloc(dev, sizeof(*boardinfo), GFP_KERNEL); + if (!boardinfo) + return -ENOMEM; + + boardinfo->base.addr = addr; + acpi_set_modalias(adev, dev_name(&adev->dev), boardinfo->base.type, + sizeof(boardinfo->base.type)); + boardinfo->base.fwnode = acpi_fwnode_handle(adev); + + /* + * The I3C Specification does not clearly say I2C devices with 10-bit + * address are supported. These devices can't be passed properly through + * DEFSLVS command. + */ + if (boardinfo->base.flags & I2C_CLIENT_TEN) { + dev_err(dev, "I2C device with 10 bit address not supported."); + return -EOPNOTSUPP; + } + + boardinfo->lvr = look_up->lvr; + + /* If the client speed is smaller than controller speed, + * then set the controller speed to the client speed + */ + if (look_up->speed && look_up->speed < master->bus.scl_rate.i2c) + master->bus.scl_rate.i2c = look_up->speed; + + list_add_tail(&boardinfo->node, &master->boardinfo.i2c); + + return 0; +} + +static int i3c_acpi_master_add_i3c_boardinfo(struct i3c_master_controller *master, + struct acpi_device *adev, acpi_handle handle, struct i3c_acpi_lookup *look_up) +{ + struct i3c_dev_boardinfo *boardinfo; + struct device *dev = &master->dev; + enum i3c_addr_slot_status addrstatus; + u32 init_dyn_addr = 0; + + boardinfo = devm_kzalloc(dev, sizeof(*boardinfo), GFP_KERNEL); + if (!boardinfo) + return -ENOMEM; + + if (look_up->slave_address) { + if (look_up->slave_address > I3C_MAX_ADDR) + return -EINVAL; + + addrstatus = i3c_bus_get_addr_slot_status(&master->bus, look_up->slave_address); + if (addrstatus != I3C_ADDR_SLOT_FREE) + return -EINVAL; + } + + boardinfo->static_addr = look_up->slave_address; + boardinfo->pid = look_up->pid; + + if ((boardinfo->pid & GENMASK_ULL(63, 48)) || + I3C_PID_RND_LOWER_32BITS(boardinfo->pid)) + return -EINVAL; + + boardinfo->init_dyn_addr = init_dyn_addr; + boardinfo->fwnode = acpi_fwnode_handle(adev); + + /* If the client speed is smaller than controller speed, + * then set the controller speed to the client speed + */ + if (look_up->speed && look_up->speed < master->bus.scl_rate.i3c) + master->bus.scl_rate.i3c = look_up->speed; + + list_add_tail(&boardinfo->node, &master->boardinfo.i3c); + + return 0; +} + +static int i3c_acpi_master_get_resource(struct acpi_resource *ares, void *data) +{ + struct i3c_acpi_lookup *look_up = (struct i3c_acpi_lookup *)data; + struct acpi_resource_i2c_serialbus *sb; + bool ret; + + ret = i2c_acpi_get_i2c_resource(ares, &sb); + if (ret == true) { + look_up->slave_address = sb->slave_address; + look_up->speed = sb->connection_speed; + } + + return 0; +} + +static int i3c_acpi_master_get_slave_info(struct i3c_master_controller *master, + struct acpi_device *adev, struct i3c_acpi_lookup *look_up) +{ + struct list_head resource_list; + int ret; + + /* Look up for I2cSerialBus resource */ + INIT_LIST_HEAD(&resource_list); + ret = acpi_dev_get_resources(adev, &resource_list, + i3c_acpi_master_get_resource, look_up); + acpi_dev_free_resource_list(&resource_list); + + return ret; +} + +static bool i3c_master_check_cpu_workaround(void) +{ + u32 cpu_implementor; + + cpu_implementor = read_cpuid_implementor(); + + if (cpu_implementor == ARM_CPU_IMP_PHYTIUM) + return true; + + return false; +} + +static int i3c_acpi_master_add_dev(struct acpi_device *adev, void *data) +{ + int ret = -1; + acpi_handle handle = adev->handle; + acpi_status status; + struct i3c_master_controller *master = (struct i3c_master_controller *)data; + u64 pid, lvr; + struct i3c_acpi_lookup look_up; + u32 mode_val; + + i3c_acpi_master_get_slave_info(master, adev, &look_up); + + status = acpi_evaluate_integer(handle, "_ADR", NULL, &pid); + + if (ACPI_SUCCESS(status)) + look_up.pid = pid; + else + look_up.pid = 0x202a0a0a0a0; + + if (i3c_master_check_cpu_workaround() == true) { + dev_info(&master->dev, "I3C ACPI SPECIAL KEYS\n"); + status = acpi_evaluate_integer(handle, "_LVR", NULL, &lvr); + + if (ACPI_SUCCESS(status)) + look_up.lvr = (u32)lvr; + else + look_up.lvr = 0; + + ret = i3c_master_acpi_get_params(handle, "i3c-mode", &mode_val); + if (!ret) { + if (!mode_val) + i3c_acpi_master_add_i2c_boardinfo(master, adev, &look_up); + else + i3c_acpi_master_add_i3c_boardinfo(master, adev, handle, &look_up); + } + } + return 0; +} + +static void i3c_acpi_register_devices(struct i3c_master_controller *master) +{ + struct acpi_device *adev = NULL; + + if (!has_acpi_companion(master->dev.parent)) + return; + + adev = acpi_fetch_acpi_dev(ACPI_HANDLE(master->dev.parent)); + if (adev) + acpi_dev_for_each_child(adev, i3c_acpi_master_add_dev, master); +} +#endif + static int i3c_master_i2c_adapter_xfer(struct i2c_adapter *adap, struct i2c_msg *xfers, int nxfers) { @@ -2311,7 +2591,7 @@ static int i3c_master_i2c_adapter_init(struct i3c_master_controller *master) adap->dev.parent = master->dev.parent; adap->owner = master->dev.parent->driver->owner; adap->algo = &i3c_master_i2c_algo; - strncpy(adap->name, dev_name(master->dev.parent), sizeof(adap->name)); + strscpy(adap->name, dev_name(master->dev.parent), sizeof(adap->name)); /* FIXME: Should we allow i3c masters to override these values? */ adap->timeout = 1000; @@ -2616,6 +2896,7 @@ int i3c_master_register(struct i3c_master_controller *master, master->dev.parent = parent; master->dev.of_node = of_node_get(parent->of_node); + master->dev.fwnode = parent->fwnode; master->dev.bus = &i3c_bus_type; master->dev.type = &i3c_masterdev_type; master->dev.release = i3c_masterdev_release; @@ -2634,7 +2915,9 @@ int i3c_master_register(struct i3c_master_controller *master, ret = of_populate_i3c_bus(master); if (ret) goto err_put_dev; - +#if defined(CONFIG_ACPI) && defined(CONFIG_ARCH_PHYTIUM) + i3c_acpi_register_devices(master); +#endif list_for_each_entry(i2cbi, &master->boardinfo.i2c, node) { switch (i2cbi->lvr & I3C_LVR_I2C_INDEX_MASK) { case I3C_LVR_I2C_INDEX(0): diff --git a/drivers/i3c/master/Kconfig b/drivers/i3c/master/Kconfig index 90dee3ec55209..f39122918b187 100644 --- a/drivers/i3c/master/Kconfig +++ b/drivers/i3c/master/Kconfig @@ -57,3 +57,12 @@ config MIPI_I3C_HCI This driver can also be built as a module. If so, the module will be called mipi-i3c-hci. + +config PHYTIUM_I3C_MASTER + tristate "Phytium I3C master driver" + depends on I3C + depends on HAS_IOMEM + depends on !(ALPHA || PARISC) + help + Enable this driver if you want to support phytium I3C master block. + diff --git a/drivers/i3c/master/Makefile b/drivers/i3c/master/Makefile index 3e97960160bc8..3819a4dab6acf 100644 --- a/drivers/i3c/master/Makefile +++ b/drivers/i3c/master/Makefile @@ -4,3 +4,4 @@ obj-$(CONFIG_DW_I3C_MASTER) += dw-i3c-master.o obj-$(CONFIG_AST2600_I3C_MASTER) += ast2600-i3c-master.o obj-$(CONFIG_SVC_I3C_MASTER) += svc-i3c-master.o obj-$(CONFIG_MIPI_I3C_HCI) += mipi-i3c-hci/ +obj-$(CONFIG_PHYTIUM_I3C_MASTER) += i3c-master-phytium.o diff --git a/drivers/i3c/master/i3c-master-cdns.c b/drivers/i3c/master/i3c-master-cdns.c index fa5aaaf446181..925deb7468b83 100644 --- a/drivers/i3c/master/i3c-master-cdns.c +++ b/drivers/i3c/master/i3c-master-cdns.c @@ -393,6 +393,7 @@ struct cdns_i3c_xfer { struct cdns_i3c_data { u8 thd_delay_ns; + u8 halt_disable; }; struct cdns_i3c_master { @@ -724,6 +725,19 @@ static int cdns_i3c_master_send_ccc_cmd(struct i3c_master_controller *m, if (!wait_for_completion_timeout(&xfer->comp, msecs_to_jiffies(1000))) cdns_i3c_master_unqueue_xfer(master, xfer); + /*GETMXDS format 1 need retransmission*/ + if ((xfer->ret) && (cmd->id == I3C_CCC_GETMXDS)) { + if (cmd->dests[0].payload.len == 5) { + cmd->dests[0].payload.len = 2; + ccmd->rx_len = cmd->dests[0].payload.len; + ccmd->cmd0 &= 0xfff000fff; + ccmd->cmd0 |= CMD0_FIFO_PL_LEN(cmd->dests[0].payload.len); + cdns_i3c_master_queue_xfer(master, xfer); + if (!wait_for_completion_timeout(&xfer->comp, msecs_to_jiffies(1000))) + cdns_i3c_master_unqueue_xfer(master, xfer); + } + } + ret = xfer->ret; cmd->err = cdns_i3c_cmd_get_err(&xfer->cmds[0]); cdns_i3c_master_free_xfer(xfer); @@ -1285,7 +1299,10 @@ static int cdns_i3c_master_bus_init(struct i3c_master_controller *m) * * We will issue ENTDAA afterwards from the threaded IRQ handler. */ - ctrl |= CTRL_HJ_ACK | CTRL_HJ_DISEC | CTRL_HALT_EN | CTRL_MCS_EN; + if (master->devdata->halt_disable) + ctrl |= CTRL_HJ_DISEC | CTRL_MCS_EN; + else + ctrl |= CTRL_HJ_ACK | CTRL_HJ_DISEC | CTRL_HALT_EN | CTRL_MCS_EN; /* * Configure data hold delay based on device-specific data. @@ -1556,10 +1573,17 @@ static void cdns_i3c_master_hj(struct work_struct *work) static struct cdns_i3c_data cdns_i3c_devdata = { .thd_delay_ns = 10, + .halt_disable = 0, +}; + +static struct cdns_i3c_data phytium_i3c_devdata = { + .thd_delay_ns = 10, + .halt_disable = 1, }; static const struct of_device_id cdns_i3c_master_of_ids[] = { { .compatible = "cdns,i3c-master", .data = &cdns_i3c_devdata }, + { .compatible = "phytium,cdns-i3c-master", .data = &phytium_i3c_devdata}, { /* sentinel */ }, }; @@ -1651,6 +1675,8 @@ static int cdns_i3c_master_probe(struct platform_device *pdev) if (ret) goto err_disable_sysclk; + writel(readl(master->regs + CTRL) | CTRL_HJ_ACK, master->regs + CTRL); + return 0; err_disable_sysclk: diff --git a/drivers/i3c/master/i3c-master-phytium.c b/drivers/i3c/master/i3c-master-phytium.c new file mode 100644 index 0000000000000..50f3f6c989951 --- /dev/null +++ b/drivers/i3c/master/i3c-master-phytium.c @@ -0,0 +1,1848 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2022 Phytium Technology Co.,Ltd. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../i3c_master_acpi.h" + +#define DEV_ID 0x0 +#define DEV_ID_I3C_MASTER 0x5034 + +#define CONF_STATUS0 0x4 +#define CONF_STATUS0_CMDR_DEPTH(x) (4 << (((x) & GENMASK(31, 29)) >> 29)) +#define CONF_STATUS0_ECC_CHK BIT(28) +#define CONF_STATUS0_INTEG_CHK BIT(27) +#define CONF_STATUS0_CSR_DAP_CHK BIT(26) +#define CONF_STATUS0_TRANS_TOUT_CHK BIT(25) +#define CONF_STATUS0_PROT_FAULTS_CHK BIT(24) +#define CONF_STATUS0_GPO_NUM(x) (((x) & GENMASK(23, 16)) >> 16) +#define CONF_STATUS0_GPI_NUM(x) (((x) & GENMASK(15, 8)) >> 8) +#define CONF_STATUS0_IBIR_DEPTH(x) (4 << (((x) & GENMASK(7, 6)) >> 7)) +#define CONF_STATUS0_SUPPORTS_DDR BIT(5) +#define CONF_STATUS0_SEC_MASTER BIT(4) +#define CONF_STATUS0_DEVS_NUM(x) ((x) & GENMASK(3, 0)) + +#define CONF_STATUS1 0x8 +#define CONF_STATUS1_IBI_HW_RES(x) ((((x) & GENMASK(31, 28)) >> 28) + 1) +#define CONF_STATUS1_CMD_DEPTH(x) (4 << (((x) & GENMASK(27, 26)) >> 26)) +#define CONF_STATUS1_SLVDDR_RX_DEPTH(x) (8 << (((x) & GENMASK(25, 21)) >> 21)) +#define CONF_STATUS1_SLVDDR_TX_DEPTH(x) (8 << (((x) & GENMASK(20, 16)) >> 16)) +#define CONF_STATUS1_IBI_DEPTH(x) (2 << (((x) & GENMASK(12, 10)) >> 10)) +#define CONF_STATUS1_RX_DEPTH(x) (8 << (((x) & GENMASK(9, 5)) >> 5)) +#define CONF_STATUS1_TX_DEPTH(x) (8 << ((x) & GENMASK(4, 0))) + +#define REV_ID 0xc +#define REV_ID_VID(id) (((id) & GENMASK(31, 20)) >> 20) +#define REV_ID_PID(id) (((id) & GENMASK(19, 8)) >> 8) +#define REV_ID_REV_MAJOR(id) (((id) & GENMASK(7, 4)) >> 4) +#define REV_ID_REV_MINOR(id) ((id) & GENMASK(3, 0)) + +#define CTRL 0x10 +#define CTRL_DEV_EN BIT(31) +#define CTRL_HALT_EN BIT(30) +#define CTRL_MCS BIT(29) +#define CTRL_MCS_EN BIT(28) +#define CTRL_THD_DELAY(x) (((x) << 24) & GENMASK(25, 24)) +#define CTRL_HJ_DISEC BIT(8) +#define CTRL_MST_ACK BIT(7) +#define CTRL_HJ_ACK BIT(6) +#define CTRL_HJ_INIT BIT(5) +#define CTRL_MST_INIT BIT(4) +#define CTRL_AHDR_OPT BIT(3) +#define CTRL_PURE_BUS_MODE 0 +#define CTRL_MIXED_FAST_BUS_MODE 2 +#define CTRL_MIXED_SLOW_BUS_MODE 3 +#define CTRL_BUS_MODE_MASK GENMASK(1, 0) +#define THD_DELAY_MAX 3 + +#define PRESCL_CTRL0 0x14 +#define PRESCL_CTRL0_I2C(x) ((x) << 16) +#define PRESCL_CTRL0_I3C(x) (x) +#define PRESCL_CTRL0_MAX GENMASK(9, 0) + +#define PRESCL_CTRL1 0x18 +#define PRESCL_CTRL1_PP_LOW_MASK GENMASK(15, 8) +#define PRESCL_CTRL1_PP_LOW(x) ((x) << 8) +#define PRESCL_CTRL1_OD_LOW_MASK GENMASK(7, 0) +#define PRESCL_CTRL1_OD_LOW(x) (x) + +#define MST_IER 0x20 +#define MST_IDR 0x24 +#define MST_IMR 0x28 +#define MST_ICR 0x2c +#define MST_ISR 0x30 +#define MST_INT_HALTED BIT(18) +#define MST_INT_MR_DONE BIT(17) +#define MST_INT_IMM_COMP BIT(16) +#define MST_INT_TX_THR BIT(15) +#define MST_INT_TX_OVF BIT(14) +#define MST_INT_IBID_THR BIT(12) +#define MST_INT_IBID_UNF BIT(11) +#define MST_INT_IBIR_THR BIT(10) +#define MST_INT_IBIR_UNF BIT(9) +#define MST_INT_IBIR_OVF BIT(8) +#define MST_INT_RX_THR BIT(7) +#define MST_INT_RX_UNF BIT(6) +#define MST_INT_CMDD_EMP BIT(5) +#define MST_INT_CMDD_THR BIT(4) +#define MST_INT_CMDD_OVF BIT(3) +#define MST_INT_CMDR_THR BIT(2) +#define MST_INT_CMDR_UNF BIT(1) +#define MST_INT_CMDR_OVF BIT(0) + +#define MST_STATUS0 0x34 +#define MST_STATUS0_IDLE BIT(18) +#define MST_STATUS0_HALTED BIT(17) +#define MST_STATUS0_MASTER_MODE BIT(16) +#define MST_STATUS0_TX_FULL BIT(13) +#define MST_STATUS0_IBID_FULL BIT(12) +#define MST_STATUS0_IBIR_FULL BIT(11) +#define MST_STATUS0_RX_FULL BIT(10) +#define MST_STATUS0_CMDD_FULL BIT(9) +#define MST_STATUS0_CMDR_FULL BIT(8) +#define MST_STATUS0_TX_EMP BIT(5) +#define MST_STATUS0_IBID_EMP BIT(4) +#define MST_STATUS0_IBIR_EMP BIT(3) +#define MST_STATUS0_RX_EMP BIT(2) +#define MST_STATUS0_CMDD_EMP BIT(1) +#define MST_STATUS0_CMDR_EMP BIT(0) + +#define CMDR 0x38 +#define CMDR_NO_ERROR 0 +#define CMDR_DDR_PREAMBLE_ERROR 1 +#define CMDR_DDR_PARITY_ERROR 2 +#define CMDR_DDR_RX_FIFO_OVF 3 +#define CMDR_DDR_TX_FIFO_UNF 4 +#define CMDR_M0_ERROR 5 +#define CMDR_M1_ERROR 6 +#define CMDR_M2_ERROR 7 +#define CMDR_MST_ABORT 8 +#define CMDR_NACK_RESP 9 +#define CMDR_INVALID_DA 10 +#define CMDR_DDR_DROPPED 11 +#define CMDR_ERROR(x) (((x) & GENMASK(27, 24)) >> 24) +#define CMDR_XFER_BYTES(x) (((x) & GENMASK(19, 8)) >> 8) +#define CMDR_CMDID_HJACK_DISEC 0xfe +#define CMDR_CMDID_HJACK_ENTDAA 0xff +#define CMDR_CMDID(x) ((x) & GENMASK(7, 0)) + +#define IBIR 0x3c +#define IBIR_ACKED BIT(12) +#define IBIR_SLVID(x) (((x) & GENMASK(11, 8)) >> 8) +#define IBIR_ERROR BIT(7) +#define IBIR_XFER_BYTES(x) (((x) & GENMASK(6, 2)) >> 2) +#define IBIR_TYPE_IBI 0 +#define IBIR_TYPE_HJ 1 +#define IBIR_TYPE_MR 2 +#define IBIR_TYPE(x) ((x) & GENMASK(1, 0)) + +#define SLV_IER 0x40 +#define SLV_IDR 0x44 +#define SLV_IMR 0x48 +#define SLV_ICR 0x4c +#define SLV_ISR 0x50 +#define SLV_INT_TM BIT(20) +#define SLV_INT_ERROR BIT(19) +#define SLV_INT_EVENT_UP BIT(18) +#define SLV_INT_HJ_DONE BIT(17) +#define SLV_INT_MR_DONE BIT(16) +#define SLV_INT_DA_UPD BIT(15) +#define SLV_INT_SDR_FAIL BIT(14) +#define SLV_INT_DDR_FAIL BIT(13) +#define SLV_INT_M_RD_ABORT BIT(12) +#define SLV_INT_DDR_RX_THR BIT(11) +#define SLV_INT_DDR_TX_THR BIT(10) +#define SLV_INT_SDR_RX_THR BIT(9) +#define SLV_INT_SDR_TX_THR BIT(8) +#define SLV_INT_DDR_RX_UNF BIT(7) +#define SLV_INT_DDR_TX_OVF BIT(6) +#define SLV_INT_SDR_RX_UNF BIT(5) +#define SLV_INT_SDR_TX_OVF BIT(4) +#define SLV_INT_DDR_RD_COMP BIT(3) +#define SLV_INT_DDR_WR_COMP BIT(2) +#define SLV_INT_SDR_RD_COMP BIT(1) +#define SLV_INT_SDR_WR_COMP BIT(0) + +#define SLV_STATUS0 0x54 +#define SLV_STATUS0_REG_ADDR(s) (((s) & GENMASK(23, 16)) >> 16) +#define SLV_STATUS0_XFRD_BYTES(s) ((s) & GENMASK(15, 0)) + +#define SLV_STATUS1 0x58 +#define SLV_STATUS1_AS(s) (((s) & GENMASK(21, 20)) >> 20) +#define SLV_STATUS1_VEN_TM BIT(19) +#define SLV_STATUS1_HJ_DIS BIT(18) +#define SLV_STATUS1_MR_DIS BIT(17) +#define SLV_STATUS1_PROT_ERR BIT(16) +#define SLV_STATUS1_DA(x) (((s) & GENMASK(15, 9)) >> 9) +#define SLV_STATUS1_HAS_DA BIT(8) +#define SLV_STATUS1_DDR_RX_FULL BIT(7) +#define SLV_STATUS1_DDR_TX_FULL BIT(6) +#define SLV_STATUS1_DDR_RX_EMPTY BIT(5) +#define SLV_STATUS1_DDR_TX_EMPTY BIT(4) +#define SLV_STATUS1_SDR_RX_FULL BIT(3) +#define SLV_STATUS1_SDR_TX_FULL BIT(2) +#define SLV_STATUS1_SDR_RX_EMPTY BIT(1) +#define SLV_STATUS1_SDR_TX_EMPTY BIT(0) + +#define CMD0_FIFO 0x60 +#define CMD0_FIFO_IS_DDR BIT(31) +#define CMD0_FIFO_IS_CCC BIT(30) +#define CMD0_FIFO_BCH BIT(29) +#define XMIT_BURST_STATIC_SUBADDR 0 +#define XMIT_SINGLE_INC_SUBADDR 1 +#define XMIT_SINGLE_STATIC_SUBADDR 2 +#define XMIT_BURST_WITHOUT_SUBADDR 3 +#define CMD0_FIFO_PRIV_XMIT_MODE(m) ((m) << 27) +#define CMD0_FIFO_SBCA BIT(26) +#define CMD0_FIFO_RSBC BIT(25) +#define CMD0_FIFO_IS_10B BIT(24) +#define CMD0_FIFO_PL_LEN(l) ((l) << 12) +#define CMD0_FIFO_PL_LEN_MAX 4095 +#define CMD0_FIFO_DEV_ADDR(a) ((a) << 1) +#define CMD0_FIFO_RNW BIT(0) + +#define CMD1_FIFO 0x64 +#define CMD1_FIFO_CMDID(id) ((id) << 24) +#define CMD1_FIFO_CSRADDR(a) (a) +#define CMD1_FIFO_CCC(id) (id) + +#define TX_FIFO 0x68 + +#define IMD_CMD0 0x70 +#define IMD_CMD0_PL_LEN(l) ((l) << 12) +#define IMD_CMD0_DEV_ADDR(a) ((a) << 1) +#define IMD_CMD0_RNW BIT(0) + +#define IMD_CMD1 0x74 +#define IMD_CMD1_CCC(id) (id) + +#define IMD_DATA 0x78 +#define RX_FIFO 0x80 +#define IBI_DATA_FIFO 0x84 +#define SLV_DDR_TX_FIFO 0x88 +#define SLV_DDR_RX_FIFO 0x8c + +#define CMD_IBI_THR_CTRL 0x90 +#define IBIR_THR(t) ((t) << 24) +#define CMDR_THR(t) ((t) << 16) +#define IBI_THR(t) ((t) << 8) +#define CMD_THR(t) (t) + +#define TX_RX_THR_CTRL 0x94 +#define RX_THR(t) ((t) << 16) +#define TX_THR(t) (t) + +#define SLV_DDR_TX_RX_THR_CTRL 0x98 +#define SLV_DDR_RX_THR(t) ((t) << 16) +#define SLV_DDR_TX_THR(t) (t) + +#define FLUSH_CTRL 0x9c +#define FLUSH_IBI_RESP BIT(23) +#define FLUSH_CMD_RESP BIT(22) +#define FLUSH_SLV_DDR_RX_FIFO BIT(22) +#define FLUSH_SLV_DDR_TX_FIFO BIT(21) +#define FLUSH_IMM_FIFO BIT(20) +#define FLUSH_IBI_FIFO BIT(19) +#define FLUSH_RX_FIFO BIT(18) +#define FLUSH_TX_FIFO BIT(17) +#define FLUSH_CMD_FIFO BIT(16) + +#define TTO_PRESCL_CTRL0 0xb0 +#define TTO_PRESCL_CTRL0_DIVB(x) ((x) << 16) +#define TTO_PRESCL_CTRL0_DIVA(x) (x) + +#define TTO_PRESCL_CTRL1 0xb4 +#define TTO_PRESCL_CTRL1_DIVB(x) ((x) << 16) +#define TTO_PRESCL_CTRL1_DIVA(x) (x) + +#define DEVS_CTRL 0xb8 +#define DEVS_CTRL_DEV_CLR_SHIFT 16 +#define DEVS_CTRL_DEV_CLR_ALL GENMASK(31, 16) +#define DEVS_CTRL_DEV_CLR(dev) BIT(16 + (dev)) +#define DEVS_CTRL_DEV_ACTIVE(dev) BIT(dev) +#define DEVS_CTRL_DEVS_ACTIVE_MASK GENMASK(15, 0) +#define MAX_DEVS 16 + +#define DEV_ID_RR0(d) (0xc0 + ((d) * 0x10)) +#define DEV_ID_RR0_LVR_EXT_ADDR BIT(11) +#define DEV_ID_RR0_HDR_CAP BIT(10) +#define DEV_ID_RR0_IS_I3C BIT(9) +#define DEV_ID_RR0_DEV_ADDR_MASK (GENMASK(6, 0) | GENMASK(15, 13)) +#define DEV_ID_RR0_SET_DEV_ADDR(a) (((a) & GENMASK(6, 0)) | \ + (((a) & GENMASK(9, 7)) << 6)) +#define DEV_ID_RR0_GET_DEV_ADDR(x) ((((x) >> 1) & GENMASK(6, 0)) | \ + (((x) >> 6) & GENMASK(9, 7))) + +#define DEV_ID_RR1(d) (0xc4 + ((d) * 0x10)) +#define DEV_ID_RR1_PID_MSB(pid) (pid) + +#define DEV_ID_RR2(d) (0xc8 + ((d) * 0x10)) +#define DEV_ID_RR2_PID_LSB(pid) ((pid) << 16) +#define DEV_ID_RR2_BCR(bcr) ((bcr) << 8) +#define DEV_ID_RR2_DCR(dcr) (dcr) +#define DEV_ID_RR2_LVR(lvr) (lvr) + +#define SIR_MAP(x) (0x180 + ((x) * 4)) +#define SIR_MAP_DEV_REG(d) SIR_MAP((d) / 2) +#define SIR_MAP_DEV_SHIFT(d, fs) ((fs) + (((d) % 2) ? 16 : 0)) +#define SIR_MAP_DEV_CONF_MASK(d) (GENMASK(15, 0) << (((d) % 2) ? 16 : 0)) +#define SIR_MAP_DEV_CONF(d, c) ((c) << (((d) % 2) ? 16 : 0)) +#define DEV_ROLE_SLAVE 0 +#define DEV_ROLE_MASTER 1 +#define SIR_MAP_DEV_ROLE(role) ((role) << 14) +#define SIR_MAP_DEV_SLOW BIT(13) +#define SIR_MAP_DEV_PL(l) ((l) << 8) +#define SIR_MAP_PL_MAX GENMASK(4, 0) +#define SIR_MAP_DEV_DA(a) ((a) << 1) +#define SIR_MAP_DEV_ACK BIT(0) + +#define GPIR_WORD(x) (0x200 + ((x) * 4)) +#define GPI_REG(val, id) \ + (((val) >> (((id) % 4) * 8)) & GENMASK(7, 0)) + +#define GPOR_WORD(x) (0x220 + ((x) * 4)) +#define GPO_REG(val, id) \ + (((val) >> (((id) % 4) * 8)) & GENMASK(7, 0)) + +#define ASF_INT_STATUS 0x300 +#define ASF_INT_RAW_STATUS 0x304 +#define ASF_INT_MASK 0x308 +#define ASF_INT_TEST 0x30c +#define ASF_INT_FATAL_SELECT 0x310 +#define ASF_INTEGRITY_ERR BIT(6) +#define ASF_PROTOCOL_ERR BIT(5) +#define ASF_TRANS_TIMEOUT_ERR BIT(4) +#define ASF_CSR_ERR BIT(3) +#define ASF_DAP_ERR BIT(2) +#define ASF_SRAM_UNCORR_ERR BIT(1) +#define ASF_SRAM_CORR_ERR BIT(0) + +#define ASF_SRAM_CORR_FAULT_STATUS 0x320 +#define ASF_SRAM_UNCORR_FAULT_STATUS 0x324 +#define ASF_SRAM_CORR_FAULT_INSTANCE(x) ((x) >> 24) +#define ASF_SRAM_CORR_FAULT_ADDR(x) ((x) & GENMASK(23, 0)) + +#define ASF_SRAM_FAULT_STATS 0x328 +#define ASF_SRAM_FAULT_UNCORR_STATS(x) ((x) >> 16) +#define ASF_SRAM_FAULT_CORR_STATS(x) ((x) & GENMASK(15, 0)) + +#define ASF_TRANS_TOUT_CTRL 0x330 +#define ASF_TRANS_TOUT_EN BIT(31) +#define ASF_TRANS_TOUT_VAL(x) (x) + +#define ASF_TRANS_TOUT_FAULT_MASK 0x334 +#define ASF_TRANS_TOUT_FAULT_STATUS 0x338 +#define ASF_TRANS_TOUT_FAULT_APB BIT(3) +#define ASF_TRANS_TOUT_FAULT_SCL_LOW BIT(2) +#define ASF_TRANS_TOUT_FAULT_SCL_HIGH BIT(1) +#define ASF_TRANS_TOUT_FAULT_FSCL_HIGH BIT(0) + +#define ASF_PROTO_FAULT_MASK 0x340 +#define ASF_PROTO_FAULT_STATUS 0x344 +#define ASF_PROTO_FAULT_SLVSDR_RD_ABORT BIT(31) +#define ASF_PROTO_FAULT_SLVDDR_FAIL BIT(30) +#define ASF_PROTO_FAULT_S(x) BIT(16 + (x)) +#define ASF_PROTO_FAULT_MSTSDR_RD_ABORT BIT(15) +#define ASF_PROTO_FAULT_MSTDDR_FAIL BIT(14) +#define ASF_PROTO_FAULT_M(x) BIT(x) + +#define I3C_CONTROL_DEFAULT_I2C_SCL (1000000) +#define I3C_CONTROL_DEFAULT_I3C_SCL (1000000) +#define PHYTIUM_I3C_DEV_MAX_NUM (12) +#define PHYTIUM_I3C_CMDR_MAX_TIMES (32) + +struct phytium_i3c_master_caps { + u32 cmdfifodepth; + u32 cmdrfifodepth; + u32 txfifodepth; + u32 rxfifodepth; + u32 ibirfifodepth; +}; + +struct phytium_i3c_cmd { + u32 cmd0; + u32 cmd1; + u32 tx_len; + const void *tx_buf; + u32 rx_len; + void *rx_buf; + u32 error; +}; + +struct phytium_i3c_xfer { + struct list_head node; + struct completion comp; + int ret; + unsigned int ncmds; + struct phytium_i3c_cmd cmds[]; +}; + +struct phytium_i3c_data { + u8 thd_delay_ns; +}; + +struct phytium_i3c_slave_dev { + u32 dev_rr0; + u32 dev_rr1; + u32 dev_rr2; +}; + +struct phytium_i3c_master { + struct work_struct hj_work; + struct i3c_master_controller base; + u32 free_rr_slots; + unsigned int maxdevs; + struct { + unsigned int num_slots; + struct i3c_dev_desc **slots; + spinlock_t lock; + } ibi; + struct { + struct list_head list; + struct phytium_i3c_xfer *cur; + spinlock_t lock; + } xferqueue; + void __iomem *regs; + struct clk *sysclk; + struct clk *pclk; + u32 sysclk_rate; + u32 prescl0; + u32 prescl1; + u32 ctrl_thd_del; + u32 ctrl_info; + u32 dev_valid; + struct device *dev; + struct phytium_i3c_master_caps caps; + unsigned long i3c_scl_lim; + const struct phytium_i3c_data *devdata; + struct phytium_i3c_slave_dev devinfo[PHYTIUM_I3C_DEV_MAX_NUM]; +}; + +static inline struct phytium_i3c_master * +to_phytium_i3c_master(struct i3c_master_controller *master) +{ + return container_of(master, struct phytium_i3c_master, base); +} + +static void phytium_i3c_master_wr_to_tx_fifo(struct phytium_i3c_master *master, + const u8 *bytes, int nbytes) +{ + writesl(master->regs + TX_FIFO, bytes, nbytes / 4); + if (nbytes & 3) { + u32 tmp = 0; + + memcpy(&tmp, bytes + (nbytes & ~3), nbytes & 3); + writesl(master->regs + TX_FIFO, &tmp, 1); + } +} + +static void phytium_i3c_master_rd_from_rx_fifo(struct phytium_i3c_master *master, + u8 *bytes, int nbytes) +{ + readsl(master->regs + RX_FIFO, bytes, nbytes / 4); + if (nbytes & 3) { + u32 tmp; + + readsl(master->regs + RX_FIFO, &tmp, 1); + memcpy(bytes + (nbytes & ~3), &tmp, nbytes & 3); + } +} + +static bool phytium_i3c_master_supports_ccc_cmd(struct i3c_master_controller *m, + const struct i3c_ccc_cmd *cmd) +{ + if (cmd->ndests > 1) + return false; + + switch (cmd->id) { + case I3C_CCC_ENEC(true): + case I3C_CCC_ENEC(false): + case I3C_CCC_DISEC(true): + case I3C_CCC_DISEC(false): + case I3C_CCC_ENTAS(0, true): + case I3C_CCC_ENTAS(0, false): + case I3C_CCC_RSTDAA(true): + case I3C_CCC_RSTDAA(false): + case I3C_CCC_ENTDAA: + case I3C_CCC_SETMWL(true): + case I3C_CCC_SETMWL(false): + case I3C_CCC_SETMRL(true): + case I3C_CCC_SETMRL(false): + case I3C_CCC_DEFSLVS: + case I3C_CCC_ENTHDR(0): + case I3C_CCC_SETDASA: + case I3C_CCC_SETNEWDA: + case I3C_CCC_GETMWL: + case I3C_CCC_GETMRL: + case I3C_CCC_GETPID: + case I3C_CCC_GETBCR: + case I3C_CCC_GETDCR: + case I3C_CCC_GETSTATUS: + case I3C_CCC_GETACCMST: + case I3C_CCC_GETMXDS: + case I3C_CCC_GETHDRCAP: + return true; + default: + break; + } + + return false; +} + +static int phytium_i3c_master_disable(struct phytium_i3c_master *master) +{ + u32 status; + + writel(readl(master->regs + CTRL) & ~CTRL_DEV_EN, master->regs + CTRL); + + return readl_poll_timeout(master->regs + MST_STATUS0, status, + status & MST_STATUS0_IDLE, 10, 1000000); +} + +static void phytium_i3c_master_enable(struct phytium_i3c_master *master) +{ + writel(readl(master->regs + CTRL) | CTRL_DEV_EN, master->regs + CTRL); +} + +static struct phytium_i3c_xfer * +phytium_i3c_master_alloc_xfer(struct phytium_i3c_master *master, unsigned int ncmds) +{ + struct phytium_i3c_xfer *xfer; + + xfer = kzalloc(struct_size(xfer, cmds, ncmds), GFP_KERNEL); + if (!xfer) + return NULL; + + INIT_LIST_HEAD(&xfer->node); + xfer->ncmds = ncmds; + xfer->ret = -ETIMEDOUT; + + return xfer; +} + +static void phytium_i3c_master_free_xfer(struct phytium_i3c_xfer *xfer) +{ + kfree(xfer); +} + +static void phytium_i3c_master_start_xfer_locked(struct phytium_i3c_master *master) +{ + struct phytium_i3c_xfer *xfer = master->xferqueue.cur; + unsigned int i; + + if (!xfer) + return; + + phytium_i3c_master_enable(master); + + writel(MST_INT_CMDD_EMP, master->regs + MST_ICR); + for (i = 0; i < xfer->ncmds; i++) { + struct phytium_i3c_cmd *cmd = &xfer->cmds[i]; + + phytium_i3c_master_wr_to_tx_fifo(master, cmd->tx_buf, + cmd->tx_len); + } + for (i = 0; i < xfer->ncmds; i++) { + struct phytium_i3c_cmd *cmd = &xfer->cmds[i]; + + writel(cmd->cmd1 | CMD1_FIFO_CMDID(i), + master->regs + CMD1_FIFO); + writel(cmd->cmd0, master->regs + CMD0_FIFO); + } + + writel(readl(master->regs + CTRL) | CTRL_MCS, + master->regs + CTRL); + writel(MST_INT_CMDD_EMP, master->regs + MST_IER); +} + +static void phytium_i3c_master_end_xfer_locked(struct phytium_i3c_master *master, + u32 isr) +{ + struct phytium_i3c_xfer *xfer = master->xferqueue.cur; + int i, ret = 0; + u32 status0; + + if (!xfer) + return; + + if (!(isr & MST_INT_CMDD_EMP)) + return; + + writel(MST_INT_CMDD_EMP, master->regs + MST_IDR); + + for (status0 = readl(master->regs + MST_STATUS0); + !(status0 & MST_STATUS0_CMDR_EMP); + status0 = readl(master->regs + MST_STATUS0)) { + struct phytium_i3c_cmd *cmd; + u32 cmdr, rx_len, id; + + cmdr = readl(master->regs + CMDR); + id = CMDR_CMDID(cmdr); + if (id == CMDR_CMDID_HJACK_DISEC || + id == CMDR_CMDID_HJACK_ENTDAA || + WARN_ON(id >= xfer->ncmds)) + continue; + + cmd = &xfer->cmds[CMDR_CMDID(cmdr)]; + rx_len = min_t(u32, CMDR_XFER_BYTES(cmdr), cmd->rx_len); + phytium_i3c_master_rd_from_rx_fifo(master, cmd->rx_buf, rx_len); + cmd->error = CMDR_ERROR(cmdr); + } + + for (i = 0; i < xfer->ncmds; i++) { + switch (xfer->cmds[i].error) { + case CMDR_NO_ERROR: + break; + + case CMDR_DDR_PREAMBLE_ERROR: + case CMDR_DDR_PARITY_ERROR: + case CMDR_M0_ERROR: + case CMDR_M1_ERROR: + case CMDR_M2_ERROR: + case CMDR_MST_ABORT: + case CMDR_NACK_RESP: + case CMDR_DDR_DROPPED: + ret = -EIO; + break; + + case CMDR_DDR_RX_FIFO_OVF: + case CMDR_DDR_TX_FIFO_UNF: + ret = -ENOSPC; + break; + + case CMDR_INVALID_DA: + default: + ret = -EINVAL; + break; + } + } + + xfer->ret = ret; + complete(&xfer->comp); + + xfer = list_first_entry_or_null(&master->xferqueue.list, + struct phytium_i3c_xfer, node); + if (xfer) + list_del_init(&xfer->node); + + master->xferqueue.cur = xfer; + phytium_i3c_master_start_xfer_locked(master); +} + +static void phytium_i3c_master_queue_xfer(struct phytium_i3c_master *master, + struct phytium_i3c_xfer *xfer) +{ + unsigned long flags; + + init_completion(&xfer->comp); + spin_lock_irqsave(&master->xferqueue.lock, flags); + if (master->xferqueue.cur) { + list_add_tail(&xfer->node, &master->xferqueue.list); + } else { + master->xferqueue.cur = xfer; + phytium_i3c_master_start_xfer_locked(master); + } + spin_unlock_irqrestore(&master->xferqueue.lock, flags); +} + +static void phytium_i3c_master_unqueue_xfer(struct phytium_i3c_master *master, + struct phytium_i3c_xfer *xfer) +{ + unsigned long flags; + + spin_lock_irqsave(&master->xferqueue.lock, flags); + if (master->xferqueue.cur == xfer) { + u32 status; + + writel(readl(master->regs + CTRL) & ~CTRL_DEV_EN, + master->regs + CTRL); + readl_poll_timeout_atomic(master->regs + MST_STATUS0, status, + status & MST_STATUS0_IDLE, 10, + 1000000); + master->xferqueue.cur = NULL; + writel(FLUSH_RX_FIFO | FLUSH_TX_FIFO | FLUSH_CMD_FIFO | + FLUSH_CMD_RESP, + master->regs + FLUSH_CTRL); + writel(MST_INT_CMDD_EMP, master->regs + MST_IDR); + writel(readl(master->regs + CTRL) | CTRL_DEV_EN, + master->regs + CTRL); + } else { + list_del_init(&xfer->node); + } + spin_unlock_irqrestore(&master->xferqueue.lock, flags); +} + +static enum i3c_error_code phytium_i3c_cmd_get_err(struct phytium_i3c_cmd *cmd) +{ + switch (cmd->error) { + case CMDR_M0_ERROR: + return I3C_ERROR_M0; + + case CMDR_M1_ERROR: + return I3C_ERROR_M1; + + case CMDR_M2_ERROR: + case CMDR_NACK_RESP: + return I3C_ERROR_M2; + + default: + break; + } + + return I3C_ERROR_UNKNOWN; +} + +static int phytium_i3c_master_send_ccc_cmd(struct i3c_master_controller *m, + struct i3c_ccc_cmd *cmd) +{ + struct phytium_i3c_master *master = to_phytium_i3c_master(m); + struct phytium_i3c_xfer *xfer; + struct phytium_i3c_cmd *ccmd; + int ret; + + xfer = phytium_i3c_master_alloc_xfer(master, 1); + if (!xfer) + return -ENOMEM; + + ccmd = xfer->cmds; + ccmd->cmd1 = CMD1_FIFO_CCC(cmd->id); + ccmd->cmd0 = CMD0_FIFO_IS_CCC | + CMD0_FIFO_PL_LEN(cmd->dests[0].payload.len); + + if (cmd->id & I3C_CCC_DIRECT) + ccmd->cmd0 |= CMD0_FIFO_DEV_ADDR(cmd->dests[0].addr); + + if (cmd->rnw) { + ccmd->cmd0 |= CMD0_FIFO_RNW; + ccmd->rx_buf = cmd->dests[0].payload.data; + ccmd->rx_len = cmd->dests[0].payload.len; + } else { + ccmd->tx_buf = cmd->dests[0].payload.data; + ccmd->tx_len = cmd->dests[0].payload.len; + } + + phytium_i3c_master_queue_xfer(master, xfer); + if (!wait_for_completion_timeout(&xfer->comp, msecs_to_jiffies(1000))) + phytium_i3c_master_unqueue_xfer(master, xfer); + + /*GETMXDS format 1 need retransmission*/ + if ((xfer->ret) && (cmd->id == I3C_CCC_GETMXDS)) { + if (cmd->dests[0].payload.len == 5) { + cmd->dests[0].payload.len = 2; + ccmd->rx_len = cmd->dests[0].payload.len; + ccmd->cmd0 &= 0xfff000fff; + ccmd->cmd0 |= CMD0_FIFO_PL_LEN(cmd->dests[0].payload.len); + phytium_i3c_master_queue_xfer(master, xfer); + if (!wait_for_completion_timeout(&xfer->comp, msecs_to_jiffies(1000))) + phytium_i3c_master_unqueue_xfer(master, xfer); + } + } + ret = xfer->ret; + cmd->err = phytium_i3c_cmd_get_err(&xfer->cmds[0]); + phytium_i3c_master_free_xfer(xfer); + + return ret; +} + +static int phytium_i3c_master_priv_xfers(struct i3c_dev_desc *dev, + struct i3c_priv_xfer *xfers, + int nxfers) +{ + struct i3c_master_controller *m = i3c_dev_get_master(dev); + struct phytium_i3c_master *master = to_phytium_i3c_master(m); + int txslots = 0, rxslots = 0, i, ret; + struct phytium_i3c_xfer *phytium_xfer; + + for (i = 0; i < nxfers; i++) { + if (xfers[i].len > CMD0_FIFO_PL_LEN_MAX) + return -EOPNOTSUPP; + } + + if (!nxfers) + return 0; + + if (nxfers > master->caps.cmdfifodepth || + nxfers > master->caps.cmdrfifodepth) + return -EOPNOTSUPP; + + /* + * First make sure that all transactions (block of transfers separated + * by a STOP marker) fit in the FIFOs. + */ + for (i = 0; i < nxfers; i++) { + if (xfers[i].rnw) + rxslots += DIV_ROUND_UP(xfers[i].len, 4); + else + txslots += DIV_ROUND_UP(xfers[i].len, 4); + } + + if (rxslots > master->caps.rxfifodepth || + txslots > master->caps.txfifodepth) + return -EOPNOTSUPP; + + phytium_xfer = phytium_i3c_master_alloc_xfer(master, nxfers); + if (!phytium_xfer) + return -ENOMEM; + + for (i = 0; i < nxfers; i++) { + struct phytium_i3c_cmd *ccmd = &phytium_xfer->cmds[i]; + u32 pl_len = xfers[i].len; + + ccmd->cmd0 = CMD0_FIFO_DEV_ADDR(dev->info.dyn_addr) | + CMD0_FIFO_PRIV_XMIT_MODE(XMIT_BURST_WITHOUT_SUBADDR); + + if (xfers[i].rnw) { + ccmd->cmd0 |= CMD0_FIFO_RNW; + ccmd->rx_buf = xfers[i].data.in; + ccmd->rx_len = xfers[i].len; + pl_len++; + } else { + ccmd->tx_buf = xfers[i].data.out; + ccmd->tx_len = xfers[i].len; + } + + ccmd->cmd0 |= CMD0_FIFO_PL_LEN(pl_len); + + if (i < nxfers - 1) + ccmd->cmd0 |= CMD0_FIFO_RSBC; + + if (!i) + ccmd->cmd0 |= CMD0_FIFO_BCH; + } + + phytium_i3c_master_queue_xfer(master, phytium_xfer); + if (!wait_for_completion_timeout(&phytium_xfer->comp, + msecs_to_jiffies(1000))) + phytium_i3c_master_unqueue_xfer(master, phytium_xfer); + + ret = phytium_xfer->ret; + + for (i = 0; i < nxfers; i++) + xfers[i].err = phytium_i3c_cmd_get_err(&phytium_xfer->cmds[i]); + + phytium_i3c_master_free_xfer(phytium_xfer); + + return ret; +} + +static int phytium_i3c_master_i2c_xfers(struct i2c_dev_desc *dev, + const struct i2c_msg *xfers, int nxfers) +{ + struct i3c_master_controller *m = i2c_dev_get_master(dev); + struct phytium_i3c_master *master = to_phytium_i3c_master(m); + unsigned int nrxwords = 0, ntxwords = 0; + struct phytium_i3c_xfer *xfer; + int i, ret = 0; + + if (nxfers > master->caps.cmdfifodepth) + return -EOPNOTSUPP; + + for (i = 0; i < nxfers; i++) { + if (xfers[i].len > CMD0_FIFO_PL_LEN_MAX) + return -EOPNOTSUPP; + + if (xfers[i].flags & I2C_M_RD) + nrxwords += DIV_ROUND_UP(xfers[i].len, 4); + else + ntxwords += DIV_ROUND_UP(xfers[i].len, 4); + } + + if (ntxwords > master->caps.txfifodepth || + nrxwords > master->caps.rxfifodepth) + return -EOPNOTSUPP; + + xfer = phytium_i3c_master_alloc_xfer(master, nxfers); + if (!xfer) + return -ENOMEM; + + for (i = 0; i < nxfers; i++) { + struct phytium_i3c_cmd *ccmd = &xfer->cmds[i]; + + ccmd->cmd0 = CMD0_FIFO_DEV_ADDR(xfers[i].addr) | + CMD0_FIFO_PL_LEN(xfers[i].len) | + CMD0_FIFO_PRIV_XMIT_MODE(XMIT_BURST_WITHOUT_SUBADDR); + + if (xfers[i].flags & I2C_M_TEN) + ccmd->cmd0 |= CMD0_FIFO_IS_10B; + + if (xfers[i].flags & I2C_M_RD) { + ccmd->cmd0 |= CMD0_FIFO_RNW; + ccmd->rx_buf = xfers[i].buf; + ccmd->rx_len = xfers[i].len; + } else { + ccmd->tx_buf = xfers[i].buf; + ccmd->tx_len = xfers[i].len; + } + } + + phytium_i3c_master_queue_xfer(master, xfer); + if (!wait_for_completion_timeout(&xfer->comp, msecs_to_jiffies(1000))) + phytium_i3c_master_unqueue_xfer(master, xfer); + + ret = xfer->ret; + phytium_i3c_master_free_xfer(xfer); + + return ret; +} + +struct phytium_i3c_i2c_dev_data { + u16 id; + s16 ibi; + struct i3c_generic_ibi_pool *ibi_pool; +}; + +static u32 prepare_rr0_dev_address(u32 addr) +{ + u32 ret = (addr << 1) & 0xff; + + /* RR0[7:1] = addr[6:0] */ + ret |= (addr & GENMASK(6, 0)) << 1; + + /* RR0[15:13] = addr[9:7] */ + ret |= (addr & GENMASK(9, 7)) << 6; + + /* RR0[0] = ~XOR(addr[6:0]) */ + if (!(hweight8(addr & 0x7f) & 1)) + ret |= 1; + + return ret; +} + +static void phytium_i3c_master_upd_i3c_addr(struct i3c_dev_desc *dev) +{ + struct i3c_master_controller *m = i3c_dev_get_master(dev); + struct phytium_i3c_master *master = to_phytium_i3c_master(m); + struct phytium_i3c_i2c_dev_data *data = i3c_dev_get_master_data(dev); + u32 rr; + + rr = prepare_rr0_dev_address(dev->info.dyn_addr ? + dev->info.dyn_addr : + dev->info.static_addr); + writel(DEV_ID_RR0_IS_I3C | rr, master->regs + DEV_ID_RR0(data->id)); +} + +static int phytium_i3c_master_get_rr_slot(struct phytium_i3c_master *master, + u8 dyn_addr) +{ + unsigned long activedevs; + u32 rr; + int i; + + if (!dyn_addr) { + if (!master->free_rr_slots) + return -ENOSPC; + + return ffs(master->free_rr_slots) - 1; + } + + activedevs = readl(master->regs + DEVS_CTRL) & DEVS_CTRL_DEVS_ACTIVE_MASK; + activedevs &= ~BIT(0); + + for_each_set_bit(i, &activedevs, master->maxdevs + 1) { + rr = readl(master->regs + DEV_ID_RR0(i)); + if (!(rr & DEV_ID_RR0_IS_I3C) || + DEV_ID_RR0_GET_DEV_ADDR(rr) != dyn_addr) + continue; + + return i; + } + + return -EINVAL; +} + +static int phytium_i3c_master_reattach_i3c_dev(struct i3c_dev_desc *dev, + u8 old_dyn_addr) +{ + phytium_i3c_master_upd_i3c_addr(dev); + + return 0; +} + +static int phytium_i3c_master_attach_i3c_dev(struct i3c_dev_desc *dev) +{ + struct i3c_master_controller *m = i3c_dev_get_master(dev); + struct phytium_i3c_master *master = to_phytium_i3c_master(m); + struct phytium_i3c_i2c_dev_data *data; + int slot; + + data = kzalloc(sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + slot = phytium_i3c_master_get_rr_slot(master, dev->info.dyn_addr); + if (slot < 0) { + kfree(data); + return slot; + } + + data->ibi = -1; + data->id = slot; + i3c_dev_set_master_data(dev, data); + master->free_rr_slots &= ~BIT(slot); + + if (!dev->info.dyn_addr) { + phytium_i3c_master_upd_i3c_addr(dev); + writel(readl(master->regs + DEVS_CTRL) | + DEVS_CTRL_DEV_ACTIVE(data->id), + master->regs + DEVS_CTRL); + } + + return 0; +} + +static void phytium_i3c_master_detach_i3c_dev(struct i3c_dev_desc *dev) +{ + struct i3c_master_controller *m = i3c_dev_get_master(dev); + struct phytium_i3c_master *master = to_phytium_i3c_master(m); + struct phytium_i3c_i2c_dev_data *data = i3c_dev_get_master_data(dev); + + writel(readl(master->regs + DEVS_CTRL) | + DEVS_CTRL_DEV_CLR(data->id), + master->regs + DEVS_CTRL); + + i3c_dev_set_master_data(dev, NULL); + master->free_rr_slots |= BIT(data->id); + kfree(data); +} + +static int phytium_i3c_master_attach_i2c_dev(struct i2c_dev_desc *dev) +{ + struct i3c_master_controller *m = i2c_dev_get_master(dev); + struct phytium_i3c_master *master = to_phytium_i3c_master(m); + struct phytium_i3c_i2c_dev_data *data; + int slot; + + slot = phytium_i3c_master_get_rr_slot(master, 0); + if (slot < 0) + return slot; + + data = kzalloc(sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + data->id = slot; + master->free_rr_slots &= ~BIT(slot); + i2c_dev_set_master_data(dev, data); + + writel(prepare_rr0_dev_address(dev->addr), + master->regs + DEV_ID_RR0(data->id)); + writel(dev->lvr, master->regs + DEV_ID_RR2(data->id)); + writel(readl(master->regs + DEVS_CTRL) | + DEVS_CTRL_DEV_ACTIVE(data->id), + master->regs + DEVS_CTRL); + + return 0; +} + +static void phytium_i3c_master_detach_i2c_dev(struct i2c_dev_desc *dev) +{ + struct i3c_master_controller *m = i2c_dev_get_master(dev); + struct phytium_i3c_master *master = to_phytium_i3c_master(m); + struct phytium_i3c_i2c_dev_data *data = i2c_dev_get_master_data(dev); + + writel(readl(master->regs + DEVS_CTRL) | + DEVS_CTRL_DEV_CLR(data->id), + master->regs + DEVS_CTRL); + master->free_rr_slots |= BIT(data->id); + + i2c_dev_set_master_data(dev, NULL); + kfree(data); +} + +static void phytium_i3c_master_bus_cleanup(struct i3c_master_controller *m) +{ + struct phytium_i3c_master *master = to_phytium_i3c_master(m); + + phytium_i3c_master_disable(master); +} + +static void phytium_i3c_master_dev_rr_to_info(struct phytium_i3c_master *master, + unsigned int slot, + struct i3c_device_info *info) +{ + u32 rr; + + memset(info, 0, sizeof(*info)); + rr = readl(master->regs + DEV_ID_RR0(slot)); + info->dyn_addr = DEV_ID_RR0_GET_DEV_ADDR(rr); + rr = readl(master->regs + DEV_ID_RR2(slot)); + info->dcr = rr; + info->bcr = rr >> 8; + info->pid = rr >> 16; + info->pid |= (u64)readl(master->regs + DEV_ID_RR1(slot)) << 16; +} + +static void phytium_i3c_master_upd_i3c_scl_lim(struct phytium_i3c_master *master) +{ + struct i3c_master_controller *m = &master->base; + unsigned long i3c_lim_period, pres_step, ncycles; + struct i3c_bus *bus = i3c_master_get_bus(m); + unsigned long new_i3c_scl_lim = 0; + struct i3c_dev_desc *dev; + u32 prescl1, ctrl; + + i3c_bus_for_each_i3cdev(bus, dev) { + unsigned long max_fscl; + + max_fscl = max(I3C_CCC_MAX_SDR_FSCL(dev->info.max_read_ds), + I3C_CCC_MAX_SDR_FSCL(dev->info.max_write_ds)); + switch (max_fscl) { + case I3C_SDR1_FSCL_8MHZ: + max_fscl = 8000000; + break; + case I3C_SDR2_FSCL_6MHZ: + max_fscl = 6000000; + break; + case I3C_SDR3_FSCL_4MHZ: + max_fscl = 4000000; + break; + case I3C_SDR4_FSCL_2MHZ: + max_fscl = 2000000; + break; + case I3C_SDR0_FSCL_MAX: + default: + max_fscl = 0; + break; + } + + if (max_fscl && + (new_i3c_scl_lim > max_fscl || !new_i3c_scl_lim)) + new_i3c_scl_lim = max_fscl; + } + + /* Only update PRESCL_CTRL1 if the I3C SCL limitation has changed. */ + if (new_i3c_scl_lim == master->i3c_scl_lim) + return; + master->i3c_scl_lim = new_i3c_scl_lim; + if (!new_i3c_scl_lim) + return; + pres_step = 1000000000UL / (bus->scl_rate.i3c * 4); + + /* Configure PP_LOW to meet I3C slave limitations. */ + prescl1 = readl(master->regs + PRESCL_CTRL1) & + ~PRESCL_CTRL1_PP_LOW_MASK; + ctrl = readl(master->regs + CTRL); + + i3c_lim_period = DIV_ROUND_UP(1000000000, master->i3c_scl_lim); + ncycles = DIV_ROUND_UP(i3c_lim_period, pres_step); + if (ncycles < 4) + ncycles = 0; + else + ncycles -= 4; + + prescl1 |= PRESCL_CTRL1_PP_LOW(ncycles); + + /* Disable I3C master before updating PRESCL_CTRL1. */ + if (ctrl & CTRL_DEV_EN) + phytium_i3c_master_disable(master); + + writel(prescl1, master->regs + PRESCL_CTRL1); + + if (ctrl & CTRL_DEV_EN) + phytium_i3c_master_enable(master); +} + +static int phytium_i3c_master_do_daa(struct i3c_master_controller *m) +{ + struct phytium_i3c_master *master = to_phytium_i3c_master(m); + unsigned long olddevs, newdevs; + int ret, slot; + u8 addrs[MAX_DEVS] = { }; + u8 last_addr = 0; + + olddevs = readl(master->regs + DEVS_CTRL) & DEVS_CTRL_DEVS_ACTIVE_MASK; + olddevs |= BIT(0); + + /* Prepare RR slots before launching DAA. */ + for_each_clear_bit(slot, &olddevs, master->maxdevs + 1) { + ret = i3c_master_get_free_addr(m, last_addr + 1); + if (ret < 0) + return -ENOSPC; + + last_addr = ret; + addrs[slot] = last_addr; + writel(prepare_rr0_dev_address(last_addr) | DEV_ID_RR0_IS_I3C, + master->regs + DEV_ID_RR0(slot)); + writel(0, master->regs + DEV_ID_RR1(slot)); + writel(0, master->regs + DEV_ID_RR2(slot)); + } + + ret = i3c_master_entdaa_locked(&master->base); + if (ret && ret != I3C_ERROR_M2) + return ret; + + newdevs = readl(master->regs + DEVS_CTRL) & DEVS_CTRL_DEVS_ACTIVE_MASK; + newdevs &= ~olddevs; + + /* + * Clear all retaining registers filled during DAA. We already + * have the addressed assigned to them in the addrs array. + */ + for_each_set_bit(slot, &newdevs, master->maxdevs + 1) + i3c_master_add_i3c_dev_locked(m, addrs[slot]); + + /* + * Clear slots that ended up not being used. Can be caused by I3C + * device creation failure or when the I3C device was already known + * by the system but with a different address (in this case the device + * already has a slot and does not need a new one). + */ + writel(readl(master->regs + DEVS_CTRL) | + master->free_rr_slots << DEVS_CTRL_DEV_CLR_SHIFT, + master->regs + DEVS_CTRL); + i3c_master_defslvs_locked(&master->base); + + phytium_i3c_master_upd_i3c_scl_lim(master); + + /* Unmask Hot-Join and Mastership request interrupts. */ + i3c_master_enec_locked(m, I3C_BROADCAST_ADDR, + I3C_CCC_EVENT_HJ | I3C_CCC_EVENT_MR); + + return 0; +} + +static u8 phytium_i3c_master_calculate_thd_delay(struct phytium_i3c_master *master) +{ + unsigned long sysclk_rate = clk_get_rate(master->sysclk); + u8 thd_delay = DIV_ROUND_UP(master->devdata->thd_delay_ns, + (NSEC_PER_SEC / sysclk_rate)); + + /* Every value greater than 3 is not valid. */ + if (thd_delay > THD_DELAY_MAX) + thd_delay = THD_DELAY_MAX; + + /* CTLR_THD_DEL value is encoded. */ + return (THD_DELAY_MAX - thd_delay); +} + +static int phytium_i3c_master_bus_init(struct i3c_master_controller *m) +{ + struct phytium_i3c_master *master = to_phytium_i3c_master(m); + unsigned long pres_step, sysclk_rate, max_i2cfreq; + struct i3c_bus *bus = i3c_master_get_bus(m); + u32 ctrl, prescl0, prescl1, pres, low; + struct i3c_device_info info = { }; + int ret, ncycles; + + switch (bus->mode) { + case I3C_BUS_MODE_PURE: + ctrl = CTRL_PURE_BUS_MODE; + break; + + case I3C_BUS_MODE_MIXED_FAST: + ctrl = CTRL_MIXED_FAST_BUS_MODE; + break; + + case I3C_BUS_MODE_MIXED_SLOW: + ctrl = CTRL_MIXED_SLOW_BUS_MODE; + break; + + default: + return -EINVAL; + } + + if (has_acpi_companion(master->dev)) { + writel(master->prescl0, master->regs + PRESCL_CTRL0); + writel(master->prescl1, master->regs + PRESCL_CTRL1); + } else { + sysclk_rate = clk_get_rate(master->sysclk); + + if (!sysclk_rate) + return -EINVAL; + + pres = DIV_ROUND_UP(sysclk_rate, (bus->scl_rate.i3c * 4)) - 1; + if (pres > PRESCL_CTRL0_MAX) + return -ERANGE; + + bus->scl_rate.i3c = sysclk_rate / ((pres + 1) * 4); + + prescl0 = PRESCL_CTRL0_I3C(pres); + + low = ((I3C_BUS_TLOW_OD_MIN_NS * sysclk_rate) / (pres + 1)) - 2; + prescl1 = PRESCL_CTRL1_OD_LOW(low); + + max_i2cfreq = bus->scl_rate.i2c; + + pres = (sysclk_rate / (max_i2cfreq * 5)) - 1; + if (pres > PRESCL_CTRL0_MAX) + return -ERANGE; + + bus->scl_rate.i2c = sysclk_rate / ((pres + 1) * 5); + + prescl0 |= PRESCL_CTRL0_I2C(pres); + writel(prescl0, master->regs + PRESCL_CTRL0); + master->prescl0 = prescl0; + + /* Calculate OD and PP low. */ + pres_step = 1000000000 / (bus->scl_rate.i3c * 4); + ncycles = DIV_ROUND_UP(I3C_BUS_TLOW_OD_MIN_NS, pres_step) - 2; + if (ncycles < 0) + ncycles = 0; + prescl1 = PRESCL_CTRL1_OD_LOW(ncycles); + writel(prescl1, master->regs + PRESCL_CTRL1); + master->prescl1 = prescl1; + } + /* Get an address for the master. */ + ret = i3c_master_get_free_addr(m, 0); + if (ret < 0) + return ret; + + writel(prepare_rr0_dev_address(ret) | DEV_ID_RR0_IS_I3C, + master->regs + DEV_ID_RR0(0)); + + phytium_i3c_master_dev_rr_to_info(master, 0, &info); + if (info.bcr & I3C_BCR_HDR_CAP) + info.hdr_cap = I3C_CCC_HDR_MODE(I3C_HDR_DDR); + + ret = i3c_master_set_info(&master->base, &info); + if (ret) + return ret; + + /* + * Enable Hot-Join, and, when a Hot-Join request happens, disable all + * events coming from this device. + * + * We will issue ENTDAA afterwards from the threaded IRQ handler. + */ + + ctrl |= CTRL_HJ_DISEC | CTRL_MCS_EN; + /* + * Configure data hold delay based on device-specific data. + * + * MIPI I3C Specification 1.0 defines non-zero minimal tHD_PP timing on + * master output. This setting allows to meet this timing on master's + * SoC outputs, regardless of PCB balancing. + */ + if (has_acpi_companion(master->dev)) + ctrl |= CTRL_THD_DELAY(master->ctrl_thd_del); + else + ctrl |= CTRL_THD_DELAY(phytium_i3c_master_calculate_thd_delay(master)); + + writel(ctrl, master->regs + CTRL); + + phytium_i3c_master_enable(master); + + return 0; +} + +static void phytium_i3c_master_handle_ibi(struct phytium_i3c_master *master, + u32 ibir) +{ + struct phytium_i3c_i2c_dev_data *data; + bool data_consumed = false; + struct i3c_ibi_slot *slot; + u32 id = IBIR_SLVID(ibir); + struct i3c_dev_desc *dev; + size_t nbytes; + u8 *buf; + + /* + * FIXME: maybe we should report the FIFO OVF errors to the upper + * layer. + */ + if (id >= master->ibi.num_slots || (ibir & IBIR_ERROR)) + goto out; + + dev = master->ibi.slots[id]; + spin_lock(&master->ibi.lock); + + data = i3c_dev_get_master_data(dev); + slot = i3c_generic_ibi_get_free_slot(data->ibi_pool); + if (!slot) + goto out_unlock; + + buf = slot->data; + + nbytes = IBIR_XFER_BYTES(ibir); + readsl(master->regs + IBI_DATA_FIFO, buf, nbytes / 4); + if (nbytes % 3) { + u32 tmp = __raw_readl(master->regs + IBI_DATA_FIFO); + + memcpy(buf + (nbytes & ~3), &tmp, nbytes & 3); + } + + slot->len = min_t(unsigned int, IBIR_XFER_BYTES(ibir), + dev->ibi->max_payload_len); + i3c_master_queue_ibi(dev, slot); + data_consumed = true; + +out_unlock: + spin_unlock(&master->ibi.lock); + +out: + /* Consume data from the FIFO if it's not been done already. */ + if (!data_consumed) { + int i; + + for (i = 0; i < IBIR_XFER_BYTES(ibir); i += 4) + readl(master->regs + IBI_DATA_FIFO); + } +} + +static void phytium_i3c_master_demux_ibis(struct phytium_i3c_master *master) +{ + u32 status0; + + writel(MST_INT_IBIR_THR, master->regs + MST_ICR); + + for (status0 = readl(master->regs + MST_STATUS0); + !(status0 & MST_STATUS0_IBIR_EMP); + status0 = readl(master->regs + MST_STATUS0)) { + u32 ibir = readl(master->regs + IBIR); + + switch (IBIR_TYPE(ibir)) { + case IBIR_TYPE_IBI: + phytium_i3c_master_handle_ibi(master, ibir); + break; + + case IBIR_TYPE_HJ: + WARN_ON(IBIR_XFER_BYTES(ibir) || (ibir & IBIR_ERROR)); + queue_work(master->base.wq, &master->hj_work); + break; + + case IBIR_TYPE_MR: + WARN_ON(IBIR_XFER_BYTES(ibir) || (ibir & IBIR_ERROR)); + break; + default: + break; + } + } +} + +static irqreturn_t phytium_i3c_master_interrupt(int irq, void *data) +{ + struct phytium_i3c_master *master = data; + u32 status; + + status = readl(master->regs + MST_ISR); + if (!(status & readl(master->regs + MST_IMR))) + return IRQ_NONE; + + spin_lock(&master->xferqueue.lock); + phytium_i3c_master_end_xfer_locked(master, status); + spin_unlock(&master->xferqueue.lock); + + if (status & MST_INT_IBIR_THR) + phytium_i3c_master_demux_ibis(master); + + return IRQ_HANDLED; +} + +static int phytium_i3c_master_disable_ibi(struct i3c_dev_desc *dev) +{ + struct i3c_master_controller *m = i3c_dev_get_master(dev); + struct phytium_i3c_master *master = to_phytium_i3c_master(m); + struct phytium_i3c_i2c_dev_data *data = i3c_dev_get_master_data(dev); + unsigned long flags; + u32 sirmap; + int ret; + + ret = i3c_master_disec_locked(m, dev->info.dyn_addr, + I3C_CCC_EVENT_SIR); + if (ret) + return ret; + + spin_lock_irqsave(&master->ibi.lock, flags); + sirmap = readl(master->regs + SIR_MAP_DEV_REG(data->ibi)); + sirmap &= ~SIR_MAP_DEV_CONF_MASK(data->ibi); + sirmap |= SIR_MAP_DEV_CONF(data->ibi, + SIR_MAP_DEV_DA(I3C_BROADCAST_ADDR)); + writel(sirmap, master->regs + SIR_MAP_DEV_REG(data->ibi)); + spin_unlock_irqrestore(&master->ibi.lock, flags); + + return ret; +} + +static int phytium_i3c_master_enable_ibi(struct i3c_dev_desc *dev) +{ + struct i3c_master_controller *m = i3c_dev_get_master(dev); + struct phytium_i3c_master *master = to_phytium_i3c_master(m); + struct phytium_i3c_i2c_dev_data *data = i3c_dev_get_master_data(dev); + unsigned long flags; + u32 sircfg, sirmap; + int ret; + + spin_lock_irqsave(&master->ibi.lock, flags); + sirmap = readl(master->regs + SIR_MAP_DEV_REG(data->ibi)); + sirmap &= ~SIR_MAP_DEV_CONF_MASK(data->ibi); + sircfg = SIR_MAP_DEV_ROLE(dev->info.bcr >> 6) | + SIR_MAP_DEV_DA(dev->info.dyn_addr) | + SIR_MAP_DEV_PL(dev->info.max_ibi_len) | + SIR_MAP_DEV_ACK; + + if (dev->info.bcr & I3C_BCR_MAX_DATA_SPEED_LIM) + sircfg |= SIR_MAP_DEV_SLOW; + + sirmap |= SIR_MAP_DEV_CONF(data->ibi, sircfg); + writel(sirmap, master->regs + SIR_MAP_DEV_REG(data->ibi)); + spin_unlock_irqrestore(&master->ibi.lock, flags); + + ret = i3c_master_enec_locked(m, dev->info.dyn_addr, + I3C_CCC_EVENT_SIR); + if (ret) { + spin_lock_irqsave(&master->ibi.lock, flags); + sirmap = readl(master->regs + SIR_MAP_DEV_REG(data->ibi)); + sirmap &= ~SIR_MAP_DEV_CONF_MASK(data->ibi); + sirmap |= SIR_MAP_DEV_CONF(data->ibi, + SIR_MAP_DEV_DA(I3C_BROADCAST_ADDR)); + writel(sirmap, master->regs + SIR_MAP_DEV_REG(data->ibi)); + spin_unlock_irqrestore(&master->ibi.lock, flags); + } + + return ret; +} + +static int phytium_i3c_master_request_ibi(struct i3c_dev_desc *dev, + const struct i3c_ibi_setup *req) +{ + struct i3c_master_controller *m = i3c_dev_get_master(dev); + struct phytium_i3c_master *master = to_phytium_i3c_master(m); + struct phytium_i3c_i2c_dev_data *data = i3c_dev_get_master_data(dev); + unsigned long flags; + unsigned int i; + + data->ibi_pool = i3c_generic_ibi_alloc_pool(dev, req); + if (IS_ERR(data->ibi_pool)) + return PTR_ERR(data->ibi_pool); + + spin_lock_irqsave(&master->ibi.lock, flags); + for (i = 0; i < master->ibi.num_slots; i++) { + if (!master->ibi.slots[i]) { + data->ibi = i; + master->ibi.slots[i] = dev; + break; + } + } + spin_unlock_irqrestore(&master->ibi.lock, flags); + + if (i < master->ibi.num_slots) + return 0; + + i3c_generic_ibi_free_pool(data->ibi_pool); + data->ibi_pool = NULL; + + return -ENOSPC; +} + +static void phytium_i3c_master_free_ibi(struct i3c_dev_desc *dev) +{ + struct i3c_master_controller *m = i3c_dev_get_master(dev); + struct phytium_i3c_master *master = to_phytium_i3c_master(m); + struct phytium_i3c_i2c_dev_data *data = i3c_dev_get_master_data(dev); + unsigned long flags; + + spin_lock_irqsave(&master->ibi.lock, flags); + master->ibi.slots[data->ibi] = NULL; + data->ibi = -1; + spin_unlock_irqrestore(&master->ibi.lock, flags); + + i3c_generic_ibi_free_pool(data->ibi_pool); +} + +static void phytium_i3c_master_recycle_ibi_slot(struct i3c_dev_desc *dev, + struct i3c_ibi_slot *slot) +{ + struct phytium_i3c_i2c_dev_data *data = i3c_dev_get_master_data(dev); + + i3c_generic_ibi_recycle_slot(data->ibi_pool, slot); +} + +static const struct i3c_master_controller_ops phytium_i3c_master_ops = { + .bus_init = phytium_i3c_master_bus_init, + .bus_cleanup = phytium_i3c_master_bus_cleanup, + .do_daa = phytium_i3c_master_do_daa, + .attach_i3c_dev = phytium_i3c_master_attach_i3c_dev, + .reattach_i3c_dev = phytium_i3c_master_reattach_i3c_dev, + .detach_i3c_dev = phytium_i3c_master_detach_i3c_dev, + .attach_i2c_dev = phytium_i3c_master_attach_i2c_dev, + .detach_i2c_dev = phytium_i3c_master_detach_i2c_dev, + .supports_ccc_cmd = phytium_i3c_master_supports_ccc_cmd, + .send_ccc_cmd = phytium_i3c_master_send_ccc_cmd, + .priv_xfers = phytium_i3c_master_priv_xfers, + .i2c_xfers = phytium_i3c_master_i2c_xfers, + .enable_ibi = phytium_i3c_master_enable_ibi, + .disable_ibi = phytium_i3c_master_disable_ibi, + .request_ibi = phytium_i3c_master_request_ibi, + .free_ibi = phytium_i3c_master_free_ibi, + .recycle_ibi_slot = phytium_i3c_master_recycle_ibi_slot, +}; + +static void phytium_i3c_master_hj(struct work_struct *work) +{ + struct phytium_i3c_master *master = container_of(work, + struct phytium_i3c_master, + hj_work); + + i3c_master_do_daa(&master->base); +} + +static struct phytium_i3c_data phytium_i3c_devdata = { + .thd_delay_ns = 10, +}; + +static const struct of_device_id phytium_i3c_master_of_ids[] = { + { .compatible = "phytium,cdns-i3c-master", .data = &phytium_i3c_devdata }, + { /* sentinel */ }, +}; + +#ifdef CONFIG_ACPI +static const struct acpi_device_id phytium_i3c_master_acpi_ids[] = { + { "PHYT0035", 0 }, + { } +}; +MODULE_DEVICE_TABLE(acpi, phytium_i3c_master_acpi_ids); +#endif + +static void phytium_i3c_master_controller_init(struct phytium_i3c_master *master) +{ + int i; + + phytium_i3c_master_disable(master); + + writel(FLUSH_RX_FIFO | FLUSH_TX_FIFO | FLUSH_CMD_FIFO | + FLUSH_CMD_RESP, + master->regs + FLUSH_CTRL); + + writel(0xffffffff, master->regs + MST_IDR); + writel(0xffffffff, master->regs + SLV_IDR); + + writel(master->prescl0, master->regs + PRESCL_CTRL0); + writel(master->prescl1, master->regs + PRESCL_CTRL1); + + writel(IBIR_THR(1), master->regs + CMD_IBI_THR_CTRL); + writel(MST_INT_IBIR_THR, master->regs + MST_IER); + writel(master->dev_valid, master->regs + DEVS_CTRL); + + for (i = 0; i < PHYTIUM_I3C_DEV_MAX_NUM; i++) { + writel(master->devinfo[i].dev_rr0, master->regs + DEV_ID_RR0(i)); + writel(master->devinfo[i].dev_rr1, master->regs + DEV_ID_RR1(i)); + writel(master->devinfo[i].dev_rr2, master->regs + DEV_ID_RR2(i)); + } + + for (i = 0; i < PHYTIUM_I3C_CMDR_MAX_TIMES; i++) + readl(master->regs + CMDR); + + writel(master->ctrl_info, master->regs + CTRL); + + phytium_i3c_master_enable(master); +} + +static void phytium_i3c_master_store_dev(struct phytium_i3c_master *master) +{ + int i; + + master->ctrl_info = readl(master->regs + CTRL); + master->dev_valid = readl(master->regs + DEVS_CTRL); + + for (i = 0; i < PHYTIUM_I3C_DEV_MAX_NUM; i++) { + master->devinfo[i].dev_rr0 = readl(master->regs + DEV_ID_RR0(i)); + master->devinfo[i].dev_rr1 = readl(master->regs + DEV_ID_RR1(i)); + master->devinfo[i].dev_rr2 = readl(master->regs + DEV_ID_RR2(i)); + } +} + +static int phytium_i3c_master_probe(struct platform_device *pdev) +{ + struct phytium_i3c_master *master; + struct resource *res; + int ret, irq; + u32 val; + + master = devm_kzalloc(&pdev->dev, sizeof(*master), GFP_KERNEL); + if (!master) + return -ENOMEM; + + master->dev = &pdev->dev; + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + master->regs = devm_ioremap_resource(&pdev->dev, res); + + if (IS_ERR(master->regs)) + return PTR_ERR(master->regs); + + if (pdev->dev.of_node) { + master->devdata = of_device_get_match_data(&pdev->dev); + + if (!master->devdata) + return -EINVAL; + + + master->pclk = devm_clk_get(&pdev->dev, "pclk"); + if (IS_ERR(master->pclk)) + return PTR_ERR(master->pclk); + + master->sysclk = devm_clk_get(&pdev->dev, "sysclk"); + if (IS_ERR(master->sysclk)) + return PTR_ERR(master->sysclk); + + ret = clk_prepare_enable(master->pclk); + if (ret) + return ret; + + ret = clk_prepare_enable(master->sysclk); + if (ret) + goto err_disable_pclk; +#ifdef CONFIG_ACPI + } else if (has_acpi_companion(&pdev->dev)) { + i3c_master_acpi_clk_params(ACPI_HANDLE(&pdev->dev), "SCLK", &master->prescl0, + &master->prescl1, &master->ctrl_thd_del); + + if (!master->prescl0) + goto err_disable_sysclk; + + master->base.bus.scl_rate.i2c = I3C_CONTROL_DEFAULT_I2C_SCL; + master->base.bus.scl_rate.i3c = I3C_BUS_MAX_I3C_SCL_RATE; + ACPI_COMPANION_SET(master->dev, ACPI_COMPANION(&pdev->dev)); +#endif + } + + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return irq; + + if (readl(master->regs + DEV_ID) != DEV_ID_I3C_MASTER) { + ret = -EINVAL; + goto err_disable_sysclk; + } + + spin_lock_init(&master->xferqueue.lock); + INIT_LIST_HEAD(&master->xferqueue.list); + + INIT_WORK(&master->hj_work, phytium_i3c_master_hj); + phytium_i3c_master_disable(master); + + writel(FLUSH_RX_FIFO | FLUSH_TX_FIFO | FLUSH_CMD_FIFO | + FLUSH_CMD_RESP, + master->regs + FLUSH_CTRL); + writel(0xffffffff, master->regs + MST_IDR); + writel(0xffffffff, master->regs + SLV_IDR); + ret = devm_request_irq(&pdev->dev, irq, phytium_i3c_master_interrupt, 0, + dev_name(&pdev->dev), master); + if (ret) + goto err_disable_sysclk; + + platform_set_drvdata(pdev, master); + + val = readl(master->regs + CONF_STATUS0); + + /* Device ID0 is reserved to describe this master. */ + master->maxdevs = CONF_STATUS0_DEVS_NUM(val); + master->free_rr_slots = GENMASK(master->maxdevs, 1); + + val = readl(master->regs + CONF_STATUS1); + master->caps.cmdfifodepth = CONF_STATUS1_CMD_DEPTH(val); + master->caps.rxfifodepth = CONF_STATUS1_RX_DEPTH(val); + master->caps.txfifodepth = CONF_STATUS1_TX_DEPTH(val); + master->caps.ibirfifodepth = CONF_STATUS0_IBIR_DEPTH(val); + master->caps.cmdrfifodepth = CONF_STATUS0_CMDR_DEPTH(val); + + spin_lock_init(&master->ibi.lock); + master->ibi.num_slots = CONF_STATUS1_IBI_HW_RES(val); + master->ibi.slots = devm_kcalloc(&pdev->dev, master->ibi.num_slots, + sizeof(*master->ibi.slots), + GFP_KERNEL); + if (!master->ibi.slots) { + ret = -ENOMEM; + goto err_disable_sysclk; + } + + writel(IBIR_THR(1), master->regs + CMD_IBI_THR_CTRL); + writel(MST_INT_IBIR_THR, master->regs + MST_IER); + writel(DEVS_CTRL_DEV_CLR_ALL, master->regs + DEVS_CTRL); + + ret = i3c_master_register(&master->base, &pdev->dev, + &phytium_i3c_master_ops, false); + if (ret) + goto err_disable_sysclk; + writel(readl(master->regs + CTRL) | CTRL_HJ_ACK, master->regs + CTRL); + phytium_i3c_master_store_dev(master); + + return 0; + +err_disable_sysclk: + clk_disable_unprepare(master->sysclk); + +err_disable_pclk: + clk_disable_unprepare(master->pclk); + + return ret; +} + +static int phytium_i3c_master_remove(struct platform_device *pdev) +{ + struct phytium_i3c_master *master = platform_get_drvdata(pdev); + + i3c_master_unregister(&master->base); + + clk_disable_unprepare(master->sysclk); + clk_disable_unprepare(master->pclk); + + return 0; +} + +static int __maybe_unused phytium_i3c_plat_suspend(struct device *dev) +{ + struct phytium_i3c_master *master = dev_get_drvdata(dev); + + + phytium_i3c_master_disable(master); + + clk_disable_unprepare(master->sysclk); + clk_disable_unprepare(master->pclk); + + return 0; +} + +static int __maybe_unused phytium_i3c_plat_resume(struct device *dev) +{ + struct phytium_i3c_master *master = dev_get_drvdata(dev); + + phytium_i3c_master_controller_init(master); + + clk_prepare_enable(master->sysclk); + clk_prepare_enable(master->pclk); + + return 0; +} + +static const struct dev_pm_ops phytium_i3c_dev_pm_ops = { + SET_LATE_SYSTEM_SLEEP_PM_OPS(phytium_i3c_plat_suspend, + phytium_i3c_plat_resume) +}; + +static struct platform_driver phytium_i3c_master = { + .probe = phytium_i3c_master_probe, + .remove = phytium_i3c_master_remove, + .driver = { + .name = "phytium-i3c-master", + .of_match_table = phytium_i3c_master_of_ids, + .acpi_match_table = ACPI_PTR(phytium_i3c_master_acpi_ids), + .pm = &phytium_i3c_dev_pm_ops, + }, +}; +module_platform_driver(phytium_i3c_master); + +MODULE_AUTHOR("Feng Jun "); +MODULE_DESCRIPTION("Phytium I3C master driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:phytium-i3c-master"); diff --git a/include/linux/i3c/master.h b/include/linux/i3c/master.h index 0b52da4f23467..c55be28cf0918 100644 --- a/include/linux/i3c/master.h +++ b/include/linux/i3c/master.h @@ -182,6 +182,7 @@ struct i3c_dev_boardinfo { u8 static_addr; u64 pid; struct device_node *of_node; + struct fwnode_handle *fwnode; }; /**