diff options
| author | Linus Torvalds <torvalds@linux-foundation.org> | 2021-02-22 09:52:55 -0800 |
|---|---|---|
| committer | Linus Torvalds <torvalds@linux-foundation.org> | 2021-02-22 09:52:55 -0800 |
| commit | 36c1b20d15703662aa0f14a32a8bd19ab3a33076 (patch) | |
| tree | 171e4ed99fc33eabbb78c1d3455c492ca1c25080 | |
| parent | 579f50cee1a1c12c0113dac02eb510cdb8c7f5f0 (diff) | |
| parent | 5c34b8e7e8bb605925b33e1aa7dc17966811219a (diff) | |
| download | linux-36c1b20d15703662aa0f14a32a8bd19ab3a33076.tar.gz linux-36c1b20d15703662aa0f14a32a8bd19ab3a33076.tar.bz2 linux-36c1b20d15703662aa0f14a32a8bd19ab3a33076.zip | |
Merge tag 'i3c/for-5.12' of git://git.kernel.org/pub/scm/linux/kernel/git/i3c/linux
Pull i3c update from Alexandre Belloni:
"Subsystem:
- Handle drivers without probe or remove callback
- Remove callback now returns void
- DT documentation is now in yaml
New driver:
- Silvaco I3C master"
* tag 'i3c/for-5.12' of git://git.kernel.org/pub/scm/linux/kernel/git/i3c/linux:
i3c: master: dw: Drop redundant disec call
MAINTAINERS: Add Silvaco I3C master
i3c: master: svc: Add Silvaco I3C master driver
dt-bindings: i3c: Describe Silvaco master binding
dt-bindings: Add vendor prefix for Silvaco
dt-bindings: i3c: mipi-hci: Include the bus binding
dt-bindings: i3c: Convert the bus description to yaml
i3c: Make remove callback return void
i3c: Handle drivers without probe or remove callback
i3c/master/mipi-i3c-hci: Specify HAS_IOMEM dependency
| -rw-r--r-- | Documentation/devicetree/bindings/i3c/i3c.txt | 140 | ||||
| -rw-r--r-- | Documentation/devicetree/bindings/i3c/i3c.yaml | 179 | ||||
| -rw-r--r-- | Documentation/devicetree/bindings/i3c/mipi-i3c-hci.yaml | 9 | ||||
| -rw-r--r-- | Documentation/devicetree/bindings/i3c/silvaco,i3c-master.yaml | 60 | ||||
| -rw-r--r-- | Documentation/devicetree/bindings/vendor-prefixes.yaml | 2 | ||||
| -rw-r--r-- | MAINTAINERS | 8 | ||||
| -rw-r--r-- | drivers/i3c/device.c | 5 | ||||
| -rw-r--r-- | drivers/i3c/master.c | 8 | ||||
| -rw-r--r-- | drivers/i3c/master/Kconfig | 9 | ||||
| -rw-r--r-- | drivers/i3c/master/Makefile | 1 | ||||
| -rw-r--r-- | drivers/i3c/master/dw-i3c-master.c | 5 | ||||
| -rw-r--r-- | drivers/i3c/master/svc-i3c-master.c | 1478 | ||||
| -rw-r--r-- | include/linux/i3c/device.h | 2 |
13 files changed, 1753 insertions, 153 deletions
diff --git a/Documentation/devicetree/bindings/i3c/i3c.txt b/Documentation/devicetree/bindings/i3c/i3c.txt deleted file mode 100644 index 4ffe059f0fec..000000000000 --- a/Documentation/devicetree/bindings/i3c/i3c.txt +++ /dev/null @@ -1,140 +0,0 @@ -Generic device tree bindings for I3C busses -=========================================== - -This document describes generic bindings that should be used to describe I3C -busses in a device tree. - -Required properties -------------------- - -- #address-cells - should be <3>. Read more about addresses below. -- #size-cells - should be <0>. -- compatible - name of the I3C master controller driving the I3C bus - -For other required properties e.g. to describe register sets, -clocks, etc. check the binding documentation of the specific driver. -The node describing an I3C bus should be named i3c-master. - -Optional properties -------------------- - -These properties may not be supported by all I3C master drivers. Each I3C -master bindings should specify which of them are supported. - -- i3c-scl-hz: frequency of the SCL signal used for I3C transfers. - When undefined the core sets it to 12.5MHz. - -- i2c-scl-hz: frequency of the SCL signal used for I2C transfers. - When undefined, the core looks at LVR (Legacy Virtual Register) - values of I2C devices described in the device tree to determine - the maximum I2C frequency. - -I2C devices -=========== - -Each I2C device connected to the bus should be described in a subnode. All -properties described in Documentation/devicetree/bindings/i2c/i2c.txt are -valid here, but several new properties have been added. - -New constraint on existing properties: --------------------------------------- -- reg: contains 3 cells - + first cell : still encoding the I2C address. 10 bit addressing is not - supported. Devices with 10 bit address can't be properly passed through - DEFSLVS command. - - + second cell: shall be 0 - - + third cell: shall encode the I3C LVR (Legacy Virtual Register) - bit[31:8]: unused/ignored - bit[7:5]: I2C device index. Possible values - * 0: I2C device has a 50 ns spike filter - * 1: I2C device does not have a 50 ns spike filter but supports high - frequency on SCL - * 2: I2C device does not have a 50 ns spike filter and is not tolerant - to high frequencies - * 3-7: reserved - - bit[4]: tell whether the device operates in FM (Fast Mode) or FM+ mode - * 0: FM+ mode - * 1: FM mode - - bit[3:0]: device type - * 0-15: reserved - -The I2C node unit-address should always match the first cell of the reg -property: <device-type>@<i2c-address>. - -I3C devices -=========== - -All I3C devices are supposed to support DAA (Dynamic Address Assignment), and -are thus discoverable. So, by default, I3C devices do not have to be described -in the device tree. -This being said, one might want to attach extra resources to these devices, -and those resources may have to be described in the device tree, which in turn -means we have to describe I3C devices. - -Another use case for describing an I3C device in the device tree is when this -I3C device has a static I2C address and we want to assign it a specific I3C -dynamic address before the DAA takes place (so that other devices on the bus -can't take this dynamic address). - -The I3C device should be names <device-type>@<static-i2c-address>,<i3c-pid>, -where device-type is describing the type of device connected on the bus -(gpio-controller, sensor, ...). - -Required properties -------------------- -- reg: contains 3 cells - + first cell : encodes the static I2C address. Should be 0 if the device does - not have one (0 is not a valid I2C address). - - + second and third cells: should encode the ProvisionalID. The second cell - contains the manufacturer ID left-shifted by 1. - The third cell contains ORing of the part ID - left-shifted by 16, the instance ID left-shifted - by 12 and the extra information. This encoding is - following the PID definition provided by the I3C - specification. - -Optional properties -------------------- -- assigned-address: dynamic address to be assigned to this device. This - property is only valid if the I3C device has a static - address (first cell of the reg property != 0). - - -Example: - - i3c-master@d040000 { - compatible = "cdns,i3c-master"; - clocks = <&coreclock>, <&i3csysclock>; - clock-names = "pclk", "sysclk"; - interrupts = <3 0>; - reg = <0x0d040000 0x1000>; - #address-cells = <3>; - #size-cells = <0>; - i2c-scl-hz = <100000>; - - /* I2C device. */ - nunchuk: nunchuk@52 { - compatible = "nintendo,nunchuk"; - reg = <0x52 0x0 0x10>; - }; - - /* I3C device with a static I2C address. */ - thermal_sensor: sensor@68,39200144004 { - reg = <0x68 0x392 0x144004>; - assigned-address = <0xa>; - }; - - /* - * I3C device without a static I2C address but requiring - * resources described in the DT. - */ - sensor@0,39200154004 { - reg = <0x0 0x392 0x154004>; - clocks = <&clock_provider 0>; - }; - }; diff --git a/Documentation/devicetree/bindings/i3c/i3c.yaml b/Documentation/devicetree/bindings/i3c/i3c.yaml new file mode 100644 index 000000000000..52042aa44d19 --- /dev/null +++ b/Documentation/devicetree/bindings/i3c/i3c.yaml @@ -0,0 +1,179 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/i3c/i3c.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: I3C bus binding + +maintainers: + - Alexandre Belloni <alexandre.belloni@bootlin.com> + - Miquel Raynal <miquel.raynal@bootlin.com> + +description: | + I3C busses can be described with a node for the primary I3C controller device + and a set of child nodes for each I2C or I3C slave on the bus. Each of them + may, during the life of the bus, request mastership. + +properties: + $nodename: + pattern: "^i3c-master@[0-9a-f]+$" + + "#address-cells": + const: 3 + description: | + Each I2C device connected to the bus should be described in a subnode. + + All I3C devices are supposed to support DAA (Dynamic Address Assignment), + and are thus discoverable. So, by default, I3C devices do not have to be + described in the device tree. This being said, one might want to attach + extra resources to these devices, and those resources may have to be + described in the device tree, which in turn means we have to describe + I3C devices. + + Another use case for describing an I3C device in the device tree is when + this I3C device has a static I2C address and we want to assign it a + specific I3C dynamic address before the DAA takes place (so that other + devices on the bus can't take this dynamic address). + + "#size-cells": + const: 0 + + i3c-scl-hz: + description: | + Frequency of the SCL signal used for I3C transfers. When undefined, the + default value should be 12.5MHz. + + May not be supported by all controllers. + + i2c-scl-hz: + description: | + Frequency of the SCL signal used for I2C transfers. When undefined, the + default should be to look at LVR (Legacy Virtual Register) values of + I2C devices described in the device tree to determine the maximum I2C + frequency. + + May not be supported by all controllers. + +required: + - "#address-cells" + - "#size-cells" + +patternProperties: + "@[0-9a-f]+$": + type: object + description: | + I2C child, should be named: <device-type>@<i2c-address> + + All properties described in Documentation/devicetree/bindings/i2c/i2c.txt + are valid here, except the reg property whose content is changed. + + properties: + compatible: + description: + Compatible of the I2C device. + + reg: + items: + - items: + - description: | + I2C address. 10 bit addressing is not supported. Devices with + 10-bit address can't be properly passed through DEFSLVS + command. + minimum: 0 + maximum: 0x7f + - const: 0 + - description: | + Shall encode the I3C LVR (Legacy Virtual Register): + bit[31:8]: unused/ignored + bit[7:5]: I2C device index. Possible values: + * 0: I2C device has a 50 ns spike filter + * 1: I2C device does not have a 50 ns spike filter but + supports high frequency on SCL + * 2: I2C device does not have a 50 ns spike filter and is + not tolerant to high frequencies + * 3-7: reserved + bit[4]: tell whether the device operates in FM (Fast Mode) + or FM+ mode: + * 0: FM+ mode + * 1: FM mode + bit[3:0]: device type + * 0-15: reserved + + required: + - compatible + - reg + + "@[0-9a-f]+,[0-9a-f]+$": + type: object + description: | + I3C child, should be named: <device-type>@<static-i2c-address>,<i3c-pid> + + properties: + reg: + items: + - items: + - description: | + Encodes the static I2C address. Should be 0 if the device does + not have one (0 is not a valid I2C address). + minimum: 0 + maximum: 0x7f + - description: | + First half of the Provisional ID (following the PID + definition provided by the I3C specification). + + Contains the manufacturer ID left-shifted by 1. + - description: | + Second half of the Provisional ID (following the PID + definition provided by the I3C specification). + + Contains the ORing of the part ID left-shifted by 16, + the instance ID left-shifted by 12 and extra information. + + assigned-address: + $ref: /schemas/types.yaml#/definitions/uint32 + minimum: 0x1 + maximum: 0xff + description: | + Dynamic address to be assigned to this device. This property is only + valid if the I3C device has a static address (first cell of the reg + property != 0). + + required: + - reg + +additionalProperties: true + +examples: + - | + i3c-master@d040000 { + compatible = "cdns,i3c-master"; + clocks = <&coreclock>, <&i3csysclock>; + clock-names = "pclk", "sysclk"; + interrupts = <3 0>; + reg = <0x0d040000 0x1000>; + #address-cells = <3>; + #size-cells = <0>; + i2c-scl-hz = <100000>; + + /* I2C device. */ + nunchuk: nunchuk@52 { + compatible = "nintendo,nunchuk"; + reg = <0x52 0x0 0x10>; + }; + + /* I3C device with a static I2C address. */ + thermal_sensor: sensor@68,39200144004 { + reg = <0x68 0x392 0x144004>; + assigned-address = <0xa>; + }; + + /* + * I3C device without a static I2C address but requiring + * resources described in the DT. + */ + sensor@0,39200154004 { + reg = <0x0 0x392 0x154004>; + clocks = <&clock_provider 0>; + }; + }; diff --git a/Documentation/devicetree/bindings/i3c/mipi-i3c-hci.yaml b/Documentation/devicetree/bindings/i3c/mipi-i3c-hci.yaml index 07a7b10163a3..04da001fc6ec 100644 --- a/Documentation/devicetree/bindings/i3c/mipi-i3c-hci.yaml +++ b/Documentation/devicetree/bindings/i3c/mipi-i3c-hci.yaml @@ -9,6 +9,9 @@ title: MIPI I3C HCI Device Tree Bindings maintainers: - Nicolas Pitre <npitre@baylibre.com> +allOf: + - $ref: /schemas/i3c/i3c.yaml# + description: | MIPI I3C Host Controller Interface @@ -36,12 +39,14 @@ required: - reg - interrupts -additionalProperties: false +unevaluatedProperties: false examples: - | - i3c@a0000000 { + i3c-master@a0000000 { compatible = "mipi-i3c-hci"; reg = <0xa0000000 0x2000>; interrupts = <89>; + #address-cells = <3>; + #size-cells = <0>; }; diff --git a/Documentation/devicetree/bindings/i3c/silvaco,i3c-master.yaml b/Documentation/devicetree/bindings/i3c/silvaco,i3c-master.yaml new file mode 100644 index 000000000000..adb5165505aa --- /dev/null +++ b/Documentation/devicetree/bindings/i3c/silvaco,i3c-master.yaml @@ -0,0 +1,60 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/i3c/silvaco,i3c-master.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Silvaco I3C master + +maintainers: + - Conor Culhane <conor.culhane@silvaco.com> + +allOf: + - $ref: "i3c.yaml#" + +properties: + compatible: + const: silvaco,i3c-master-v1 + + reg: + maxItems: 1 + + interrupts: + maxItems: 1 + + clocks: + items: + - description: system clock + - description: bus clock + - description: other (slower) events clock + + clock-names: + items: + - const: pclk + - const: fast_clk + - const: slow_clk + + resets: + maxItems: 1 + +required: + - compatible + - reg + - interrupts + - clock-names + - clocks + +additionalProperties: true + +examples: + - | + i3c-master@a0000000 { + compatible = "silvaco,i3c-master"; + clocks = <&zynqmp_clk 71>, <&fclk>, <&sclk>; + clock-names = "pclk", "fast_clk", "slow_clk"; + interrupt-parent = <&gic>; + interrupts = <0 89 4>; + reg = <0xa0000000 0x1000>; + #address-cells = <3>; + #size-cells = <0>; + }; diff --git a/Documentation/devicetree/bindings/vendor-prefixes.yaml b/Documentation/devicetree/bindings/vendor-prefixes.yaml index 9d5680c7c853..f6064d84a424 100644 --- a/Documentation/devicetree/bindings/vendor-prefixes.yaml +++ b/Documentation/devicetree/bindings/vendor-prefixes.yaml @@ -1085,6 +1085,8 @@ patternProperties: description: Shenzhen Sunchip Technology Co., Ltd "^SUNW,.*": description: Sun Microsystems, Inc + "^silvaco,.*": + description: Silvaco, Inc. "^swir,.*": description: Sierra Wireless "^syna,.*": diff --git a/MAINTAINERS b/MAINTAINERS index 8aa9d74e54a0..acd1867a9a97 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -16277,6 +16277,14 @@ S: Maintained F: Documentation/fb/sm712fb.rst F: drivers/video/fbdev/sm712* +SILVACO I3C DUAL-ROLE MASTER +M: Miquel Raynal <miquel.raynal@bootlin.com> +M: Conor Culhane <conor.culhane@silvaco.com> +L: linux-i3c@lists.infradead.org +S: Maintained +F: Documentation/devicetree/bindings/i3c/silvaco,i3c-master.yaml +F: drivers/i3c/master/svc-i3c-master.c + SIMPLE FIRMWARE INTERFACE (SFI) S: Obsolete W: http://simplefirmware.org/ diff --git a/drivers/i3c/device.c b/drivers/i3c/device.c index bb8e60dff988..e92d3e9a52bd 100644 --- a/drivers/i3c/device.c +++ b/drivers/i3c/device.c @@ -262,6 +262,11 @@ int i3c_driver_register_with_owner(struct i3c_driver *drv, struct module *owner) drv->driver.owner = owner; drv->driver.bus = &i3c_bus_type; + if (!drv->probe) { + pr_err("Trying to register an i3c driver without probe callback\n"); + return -EINVAL; + } + return driver_register(&drv->driver); } EXPORT_SYMBOL_GPL(i3c_driver_register_with_owner); diff --git a/drivers/i3c/master.c b/drivers/i3c/master.c index b61bf53ec07a..f8e9b7305c13 100644 --- a/drivers/i3c/master.c +++ b/drivers/i3c/master.c @@ -326,15 +326,13 @@ static int i3c_device_remove(struct device *dev) { struct i3c_device *i3cdev = dev_to_i3cdev(dev); struct i3c_driver *driver = drv_to_i3cdrv(dev->driver); - int ret; - ret = driver->remove(i3cdev); - if (ret) - return ret; + if (driver->remove) + driver->remove(i3cdev); i3c_device_free_ibi(i3cdev); - return ret; + return 0; } struct bus_type i3c_bus_type = { diff --git a/drivers/i3c/master/Kconfig b/drivers/i3c/master/Kconfig index e68f15f4b4d0..3b8f95916f46 100644 --- a/drivers/i3c/master/Kconfig +++ b/drivers/i3c/master/Kconfig @@ -22,9 +22,18 @@ config DW_I3C_MASTER This driver can also be built as a module. If so, the module will be called dw-i3c-master. +config SVC_I3C_MASTER + tristate "Silvaco I3C Dual-Role Master driver" + depends on I3C + depends on HAS_IOMEM + depends on !(ALPHA || PARISC) + help + Support for Silvaco I3C Dual-Role Master Controller. + config MIPI_I3C_HCI tristate "MIPI I3C Host Controller Interface driver (EXPERIMENTAL)" depends on I3C + depends on HAS_IOMEM help Support for hardware following the MIPI Aliance's I3C Host Controller Interface specification. diff --git a/drivers/i3c/master/Makefile b/drivers/i3c/master/Makefile index b892fd4cafad..b3fee0f690b2 100644 --- a/drivers/i3c/master/Makefile +++ b/drivers/i3c/master/Makefile @@ -1,4 +1,5 @@ # SPDX-License-Identifier: GPL-2.0-only obj-$(CONFIG_CDNS_I3C_MASTER) += i3c-master-cdns.o obj-$(CONFIG_DW_I3C_MASTER) += dw-i3c-master.o +obj-$(CONFIG_SVC_I3C_MASTER) += svc-i3c-master.o obj-$(CONFIG_MIPI_I3C_HCI) += mipi-i3c-hci/ diff --git a/drivers/i3c/master/dw-i3c-master.c b/drivers/i3c/master/dw-i3c-master.c index 8513bd353c05..03a368da51b9 100644 --- a/drivers/i3c/master/dw-i3c-master.c +++ b/drivers/i3c/master/dw-i3c-master.c @@ -816,11 +816,6 @@ static int dw_i3c_master_daa(struct i3c_master_controller *m) dw_i3c_master_free_xfer(xfer); - i3c_master_disec_locked(m, I3C_BROADCAST_ADDR, - I3C_CCC_EVENT_HJ | - I3C_CCC_EVENT_MR | - I3C_CCC_EVENT_SIR); - return 0; } diff --git a/drivers/i3c/master/svc-i3c-master.c b/drivers/i3c/master/svc-i3c-master.c new file mode 100644 index 000000000000..8d990696676e --- /dev/null +++ b/drivers/i3c/master/svc-i3c-master.c @@ -0,0 +1,1478 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Silvaco dual-role I3C master driver + * + * Copyright (C) 2020 Silvaco + * Author: Miquel RAYNAL <miquel.raynal@bootlin.com> + * Based on a work from: Conor Culhane <conor.culhane@silvaco.com> + */ + +#include <linux/bitfield.h> +#include <linux/clk.h> +#include <linux/completion.h> +#include <linux/errno.h> +#include <linux/i3c/master.h> +#include <linux/interrupt.h> +#include <linux/iopoll.h> +#include <linux/list.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/platform_device.h> + +/* Master Mode Registers */ +#define SVC_I3C_MCONFIG 0x000 +#define SVC_I3C_MCONFIG_MASTER_EN BIT(0) +#define SVC_I3C_MCONFIG_DISTO(x) FIELD_PREP(BIT(3), (x)) +#define SVC_I3C_MCONFIG_HKEEP(x) FIELD_PREP(GENMASK(5, 4), (x)) +#define SVC_I3C_MCONFIG_ODSTOP(x) FIELD_PREP(BIT(6), (x)) +#define SVC_I3C_MCONFIG_PPBAUD(x) FIELD_PREP(GENMASK(11, 8), (x)) +#define SVC_I3C_MCONFIG_PPLOW(x) FIELD_PREP(GENMASK(15, 12), (x)) +#define SVC_I3C_MCONFIG_ODBAUD(x) FIELD_PREP(GENMASK(23, 16), (x)) +#define SVC_I3C_MCONFIG_ODHPP(x) FIELD_PREP(BIT(24), (x)) +#define SVC_I3C_MCONFIG_SKEW(x) FIELD_PREP(GENMASK(27, 25), (x)) +#define SVC_I3C_MCONFIG_I2CBAUD(x) FIELD_PREP(GENMASK(31, 28), (x)) + +#define SVC_I3C_MCTRL 0x084 +#define SVC_I3C_MCTRL_REQUEST_MASK GENMASK(2, 0) +#define SVC_I3C_MCTRL_REQUEST_NONE 0 +#define SVC_I3C_MCTRL_REQUEST_START_ADDR 1 +#define SVC_I3C_MCTRL_REQUEST_STOP 2 +#define SVC_I3C_MCTRL_REQUEST_IBI_ACKNACK 3 +#define SVC_I3C_MCTRL_REQUEST_PROC_DAA 4 +#define SVC_I3C_MCTRL_REQUEST_AUTO_IBI 7 +#define SVC_I3C_MCTRL_TYPE_I3C 0 +#define SVC_I3C_MCTRL_TYPE_I2C BIT(4) +#define SVC_I3C_MCTRL_IBIRESP_AUTO 0 +#define SVC_I3C_MCTRL_IBIRESP_ACK_WITHOUT_BYTE 0 +#define SVC_I3C_MCTRL_IBIRESP_ACK_WITH_BYTE BIT(7) +#define SVC_I3C_MCTRL_IBIRESP_NACK BIT(6) +#define SVC_I3C_MCTRL_IBIRESP_MANUAL GENMASK(7, 6) +#define SVC_I3C_MCTRL_DIR(x) FIELD_PREP(BIT(8), (x)) +#define SVC_I3C_MCTRL_DIR_WRITE 0 +#define SVC_I3C_MCTRL_DIR_READ 1 +#define SVC_I3C_MCTRL_ADDR(x) FIELD_PREP(GENMASK(15, 9), (x)) +#define SVC_I3C_MCTRL_RDTERM(x) FIELD_PREP(GENMASK(23, 16), (x)) + +#define SVC_I3C_MSTATUS 0x088 +#define SVC_I3C_MSTATUS_STATE(x) FIELD_GET(GENMASK(2, 0), (x)) +#define SVC_I3C_MSTATUS_STATE_DAA(x) (SVC_I3C_MSTATUS_STATE(x) == 5) +#define SVC_I3C_MSTATUS_STATE_IDLE(x) (SVC_I3C_MSTATUS_STATE(x) == 0) +#define SVC_I3C_MSTATUS_BETWEEN(x) FIELD_GET(BIT(4), (x)) +#define SVC_I3C_MSTATUS_NACKED(x) FIELD_GET(BIT(5), (x)) +#define SVC_I3C_MSTATUS_IBITYPE(x) FIELD_GET(GENMASK(7, 6), (x)) +#define SVC_I3C_MSTATUS_IBITYPE_IBI 1 +#define SVC_I3C_MSTATUS_IBITYPE_MASTER_REQUEST 2 +#define SVC_I3C_MSTATUS_IBITYPE_HOT_JOIN 3 +#define SVC_I3C_MINT_SLVSTART BIT(8) +#define SVC_I3C_MINT_MCTRLDONE BIT(9) +#define SVC_I3C_MINT_COMPLETE BIT(10) +#define SVC_I3C_MINT_RXPEND BIT(11) +#define SVC_I3C_MINT_TXNOTFULL BIT(12) +#define SVC_I3C_MINT_IBIWON BIT(13) +#define SVC_I3C_MINT_ERRWARN BIT(15) +#define SVC_I3C_MSTATUS_SLVSTART(x) FIELD_GET(SVC_I3C_MINT_SLVSTART, (x)) +#define SVC_I3C_MSTATUS_MCTRLDONE(x) FIELD_GET(SVC_I3C_MINT_MCTRLDONE, (x)) +#define SVC_I3C_MSTATUS_COMPLETE(x) FIELD_GET(SVC_I3C_MINT_COMPLETE, (x)) +#define SVC_I3C_MSTATUS_RXPEND(x) FIELD_GET(SVC_I3C_MINT_RXPEND, (x)) +#define SVC_I3C_MSTATUS_TXNOTFULL(x) FIELD_GET(SVC_I3C_MINT_TXNOTFULL, (x)) +#define SVC_I3C_MSTATUS_IBIWON(x) FIELD_GET(SVC_I3C_MINT_IBIWON, (x)) +#define SVC_I3C_MSTATUS_ERRWARN(x) FIELD_GET(SVC_I3C_MINT_ERRWARN, (x)) +#define SVC_I3C_MSTATUS_IBIADDR(x) FIELD_GET(GENMASK(30, 24), (x)) + +#define SVC_I3C_IBIRULES 0x08C +#define SVC_I3C_IBIRULES_ADDR(slot, addr) FIELD_PREP(GENMASK(29, 0), \ + ((addr) & 0x3F) << ((slot) * 6)) +#define SVC_I3C_IBIRULES_ADDRS 5 +#define SVC_I3C_IBIRULES_MSB0 BIT(30) +#define SVC_I3C_IBIRULES_NOBYTE BIT(31) +#define SVC_I3C_IBIRULES_MANDBYTE 0 +#define SVC_I3C_MINTSET 0x090 +#define SVC_I3C_MINTCLR 0x094 +#define SVC_I3C_MINTMASKED 0x098 +#define SVC_I3C_MERRWARN 0x09C +#define SVC_I3C_MDMACTRL 0x0A0 +#define SVC_I3C_MDATACTRL 0x0AC +#define SVC_I3C_MDATACTRL_FLUSHTB BIT(0) +#define SVC_I3C_MDATACTRL_FLUSHRB BIT(1) +#define SVC_I3C_MDATACTRL_UNLOCK_TRIG BIT(3) +#define SVC_I3C_MDATACTRL_TXTRIG_FIFO_NOT_FULL GENMASK(5, 4) +#define SVC_I3C_MDATACTRL_RXTRIG_FIFO_NOT_EMPTY 0 +#define SVC_I3C_MDATACTRL_RXCOUNT(x) FIELD_GET(GENMASK(28, 24), (x)) +#define SVC_I3C_MDATACTRL_TXFULL BIT(30) +#define SVC_I3C_MDATACTRL_RXEMPTY BIT(31) + +#define SVC_I3C_MWDATAB 0x0B0 +#define SVC_I3C_MWDATAB_END BIT(8) + +#define SVC_I3C_MWDATABE 0x0B4 +#define SVC_I3C_MWDATAH 0x0B8 +#define SVC_I3C_MWDATAHE 0x0BC +#define SVC_I3C_MRDATAB 0x0C0 +#define SVC_I3C_MRDATAH 0x0C8 +#define SVC_I3C_MWMSG_SDR 0x0D0 +#define SVC_I3C_MRMSG_SDR 0x0D4 +#define SVC_I3C_MWMSG_DDR 0x0D8 +#define SVC_I3C_MRMSG_DDR 0x0DC + +#define SVC_I3C_MDYNADDR 0x0E4 +#define SVC_MDYNADDR_VALID BIT(0) +#define SVC_MDYNADDR_ADDR(x) FIELD_PREP(GENMASK(7, 1), (x)) + +#define SVC_I3C_MAX_DEVS 32 + +/* This parameter depends on the implementation and may be tuned */ +#define SVC_I3C_FIFO_SIZE 16 + +struct svc_i3c_cmd { + u8 addr; + bool rnw; + u8 *in; + const void *out; + unsigned int len; + unsigned int read_len; + bool continued; +}; + +struct svc_i3c_xfer { + struct list_head node; + struct completion comp; + int ret; + unsigned int type; + unsigned int ncmds; + struct svc_i3c_cmd cmds[]; +}; + +/** + * struct svc_i3c_master - Silvaco I3C Master structure + * @base: I3C master controller + * @dev: Corresponding device + * @regs: Memory mapping + * @free_slots: Bit array of available slots + * @addrs: Array containing the dynamic addresses of each attached device + * @descs: Array of descriptors, one per attached device + * @hj_work: Hot-join work + * @ibi_work: IBI work + * @irq: Main interrupt + * @pclk: System clock + * @fclk: Fast clock (bus) + * @sclk: Slow clock (other events) + * @xferqueue: Transfer queue structure + * @xferqueue.list: List member + * @xferqueue.cur: Current ongoing transfer + * @xferqueue.lock: Queue lock + * @ibi: IBI structure + * @ibi.num_slots: Number of slots available in @ibi.slots + * @ibi.slots: Available IBI slots + * @ibi.tbq_slot: To be queued IBI slot + * @ibi.lock: IBI lock + */ +struct svc_i3c_master { + struct i3c_master_controller base; + struct device *dev; + void __iomem *regs; + u32 free_slots; + u8 addrs[SVC_I3C_MAX_DEVS]; + struct i3c_dev_desc *descs[SVC_I3C_MAX_DEVS]; + struct work_struct hj_work; + struct work_struct ibi_work; + int irq; + struct clk *pclk; + struct clk *fclk; + struct clk *sclk; + struct { + struct list_head list; + struct svc_i3c_xfer *cur; + /* Prevent races between transfers */ + spinlock_t lock; + } xferqueue; + struct { + unsigned int num_slots; + struct i3c_dev_desc **slots; + struct i3c_ibi_slot *tbq_slot; + /* Prevent races within IBI handlers */ + spinlock_t lock; + } ibi; +}; + +/** + * struct svc_i3c_i3c_dev_data - Device specific data + * @index: Index in the master tables corresponding to this device + * @ibi: IBI slot index in the master structure + * @ibi_pool: IBI pool associated to this device + */ +struct svc_i3c_i2c_dev_data { + u8 index; + int ibi; + struct i3c_generic_ibi_pool *ibi_pool; +}; + +static bool svc_i3c_master_error(struct svc_i3c_master *master) +{ + u32 mstatus, merrwarn; + + mstatus = readl(master->regs + SVC_I3C_MSTATUS); + if (SVC_I3C_MSTATUS_ERRWARN(mstatus)) { + merrwarn = readl(master->regs + SVC_I3C_MERRWARN); + writel(merrwarn, master->regs + SVC_I3C_MERRWARN); + dev_err(master->dev, + "Error condition: MSTATUS 0x%08x, MERRWARN 0x%08x\n", + mstatus, merrwarn); + + return true; + } + + return false; +} + +static void svc_i3c_master_enable_interrupts(struct svc_i3c_master *master, u32 mask) +{ + writel(mask, master->regs + SVC_I3C_MINTSET); +} + +static void svc_i3c_master_disable_interrupts(struct svc_i3c_master *master) +{ + u32 mask = readl(master->regs + SVC_I3C_MINTSET); + + writel(mask, master->regs + SVC_I3C_MINTCLR); +} + +static inline struct svc_i3c_master * +to_svc_i3c_master(struct i3c_master_controller *master) +{ + return container_of(master, struct svc_i3c_master, base); +} + +static void svc_i3c_master_hj_work(struct work_struct *work) +{ + struct svc_i3c_master *master; + + master = container_of(work, struct svc_i3c_master, hj_work); + i3c_master_do_daa(&master->base); +} + +static struct i3c_dev_desc * +svc_i3c_master_dev_from_addr(struct svc_i3c_master *master, + unsigned int ibiaddr) +{ + int i; + + for (i = 0; i < SVC_I3C_MAX_DEVS; i++) + if (master->addrs[i] == ibiaddr) + break; + + if (i == SVC_I3C_MAX_DEVS) + return NULL; + + return master->descs[i]; +} + +static void svc_i3c_master_emit_stop(struct svc_i3c_master *master) +{ + writel(SVC_I3C_MCTRL_REQUEST_STOP, master->regs + SVC_I3C_MCTRL); + + /* + * This delay is necessary after the emission of a stop, otherwise eg. + * repeating IBIs do not get detected. There is a note in the manual + * about it, stating that the stop condition might not be settled + * correctly if a start condition follows too rapidly. + */ + udelay(1); +} + +static void svc_i3c_master_clear_merrwarn(struct svc_i3c_master *master) +{ + writel(readl(master->regs + SVC_I3C_MERRWARN), + master->regs + SVC_I3C_MERRWARN); +} + +static int svc_i3c_master_handle_ibi(struct svc_i3c_master *master, + struct i3c_dev_desc *dev) +{ + struct svc_i3c_i2c_dev_data *data = i3c_dev_get_master_data(dev); + struct i3c_ibi_slot *slot; + unsigned int count; + u32 mdatactrl; + u8 *buf; + + slot = i3c_generic_ibi_get_free_slot(data->ibi_pool); + if (!slot) + return -ENOSPC; + + slot->len = 0; + buf = slot->data; + + while (SVC_I3C_MSTATUS_RXPEND(readl(master->regs + SVC_I3C_MSTATUS)) && + slot->len < SVC_I3C_FIFO_SIZE) { + mdatactrl = readl(master->regs + SVC_I3C_MDATACTRL); + count = SVC_I3C_MDATACTRL_RXCOUNT(mdatactrl); + readsl(master->regs + SVC_I3C_MRDATAB, buf, count); + slot->len += count; + buf += count; + } + + master->ibi.tbq_slot = slot; + + return 0; +} + +static void svc_i3c_master_ack_ibi(struct svc_i3c_master *master, + bool mandatory_byte) +{ + unsigned int ibi_ack_nack; + + ibi_ack_nack = SVC_I3C_MCTRL_REQUEST_IBI_ACKNACK; + if (mandatory_byte) + ibi_ack_nack |= SVC_I3C_MCTRL_IBIRESP_ACK_WITH_BYTE; + else + ibi_ack_nack |= SVC_I3C_MCTRL_IBIRESP_ACK_WITHOUT_BYTE; + + writel(ibi_ack_nack, master->regs + SVC_I3C_MCTRL); +} + +static void svc_i3c_master_nack_ibi(struct svc_i3c_master *master) +{ + writel(SVC_I3C_MCTRL_REQUEST_IBI_ACKNACK | + SVC_I3C_MCTRL_IBIRESP_NACK, + master->regs + SVC_I3C_MCTRL); +} + +static void svc_i3c_master_ibi_work(struct work_struct *work) +{ + struct svc_i3c_master *master = container_of(work, struct svc_i3c_master, ibi_work); + struct svc_i3c_i2c_dev_data *data; + unsigned int ibitype, ibiaddr; + struct i3c_dev_desc *dev; + u32 status, val; + int ret; + + /* Acknowledge the incoming interrupt with the AUTOIBI mechanism */ + writel(SVC_I3C_MCTRL_REQUEST_AUTO_IBI | + SVC_I3C_MCTRL_IBIRESP_AUTO, + master->regs + SVC_I3C_MCTRL); + + /* Wait for IBIWON, should take approximately 100us */ + ret = readl_relaxed_poll_timeout(master->regs + SVC_I3C_MSTATUS, val, + SVC_I3C_MSTATUS_IBIWON(val), 0, 1000); + if (ret) { + dev_err(master->dev, "Timeout when polling for IBIWON\n"); + goto reenable_ibis; + } + + /* |
