* [PATCH v6 1/8] i3c: master: mastership handover document
2020-04-17 16:19 [PATCH v6 0/8] I3C mastership handover support Parshuram Thombare
@ 2020-04-17 16:20 ` Parshuram Thombare
2020-04-30 7:40 ` Boris Brezillon
2020-04-17 16:20 ` [PATCH v6 2/8] i3c: master: split bus_init callback into bus_init and master_set_info Parshuram Thombare
` (6 subsequent siblings)
7 siblings, 1 reply; 12+ messages in thread
From: Parshuram Thombare @ 2020-04-17 16:20 UTC (permalink / raw)
To: bbrezillon, vitor.soares
Cc: mparab, Parshuram Thombare, praneeth, linux-kernel, pgaj, linux-i3c
Flow diagram for I3C mastership handover, DEFSLVS
processing and secondary master initialization.
Signed-off-by: Parshuram Thombare <pthombar@cadence.com>
---
Documentation/driver-api/i3c/index.rst | 1 +
.../i3c/mastership-handover-flow-diagram.rst | 209 ++++++++++++++++++
2 files changed, 210 insertions(+)
create mode 100644 Documentation/driver-api/i3c/mastership-handover-flow-diagram.rst
diff --git a/Documentation/driver-api/i3c/index.rst b/Documentation/driver-api/i3c/index.rst
index 783d6dad054b..9a38c5ba87cb 100644
--- a/Documentation/driver-api/i3c/index.rst
+++ b/Documentation/driver-api/i3c/index.rst
@@ -9,3 +9,4 @@ I3C subsystem
protocol
device-driver-api
master-driver-api
+ mastership-handover-flow-diagram
diff --git a/Documentation/driver-api/i3c/mastership-handover-flow-diagram.rst b/Documentation/driver-api/i3c/mastership-handover-flow-diagram.rst
new file mode 100644
index 000000000000..04cd9f1283e0
--- /dev/null
+++ b/Documentation/driver-api/i3c/mastership-handover-flow-diagram.rst
@@ -0,0 +1,209 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+============================
+I3C mastership handover flow
+============================
+
+Main master Sec Master1 Sec Master 2
+
+1. Initialize I3C 1. Initialize I3C 1. Initialize I3C
+ bus in Pure mode bus in Pure mode bus in Pure mode
+
+defslvs_done flag defslvs_done flag defslvs_done flag
+ = true = false = false
+
+2. Do DAA + SETDASA 2. Spawn init. thread 2. Spawn init. thread
+ Initialization thread Initialization thread
+ Wait for DEFSLVS Wait for DEFSLVS
+ processing processing
+ Wait for Wait for
+ defslvs_done flag defslvs_done flag
+ = true = true
+
+3. Send DEFSLVS DEFSLVS ISR DEFSLVS ISR
+ Set flag to indicate Set flag to indicate
+ DEFSLVS processing is DEFSLVS processing is
+ pending pending
+ defslvs_done flag defslvs_done flag
+ = false = false
+
+ Defer DEFSLVS Defer DEFSLVS
+ processing to bottom processing to bottom
+ half half
+
+
+ DEFSLVS bottom half DEFSLVS bottom half
+ Try to acquire bus Try to acquire bus
+ i3c_master_acquire_bus i3c_master_acquire_bus
+
+
+ i3c_sec_mst_acquire_bus i3c_sec_mst_acquire_bus
+ state machine
+ 1. Wait until DA is
+ assigned
+
+4. Register all
+ slaves and
+ master devive
+
+5. Program bus mode
+ as per board
+ info (DTS)
+
+6. Send ENEC MR,
+ HJ, SIR CCC
+ Initialization
+ complete
+
+ 2. Check if DISEC MR 2. Check if DISEC MR
+ is received is received
+ If not, initiate If not, initiate
+ MR request MR request
+
+
+ Sec master with highest priority (lowest address) get
+ mastership. Some controllers may not have way of knowing
+ if mastership is granted and can keep waiting for
+ GETACCMST.To avoid this, on reception of MR request
+ from highest priority secondary master, current master
+ send DISEC MR, HJ events to remaining master devices.
+
+MR request ISR
+1. Disable IBI
+ in controller
+
+MR request
+bottom half
+1. Send DISEC
+ MR, HJ
+ to all but
+ master device
+ to which MR
+ is granted
+ 3. Received DISEC MR
+ Go back to WAIT
+ DA state
+
+ 1. Wait until DA
+ is assigned
+ 2. Check if DISEC MR
+ is received
+ Wait until ENEC MR
+ is received before
+ sending next MR request
+
+2. Send GETACCMST
+ to secondary 3. Wait for MR DONE
+ master to which
+ mastership is to
+ be granted
+ 4. MR granted
+ 5. Update
+ current_master
+
+
+ DEFSLVS bottom half
+ continue...
+ Controller driver read
+ out DEFSLVS data in
+ defslvs_data structure
+
+ Call
+ i3c_master_process_defslvs
+ until it is processed
+ successfully. And set
+ defslvs_done flag = true.
+
+ i3c_master_process_defslvs
+ 1. Bus init to correct
+ mode based on
+ defslvs data
+ 2. Register I2C devices
+ from defslvs_data
+ Since I3C devices are not
+ hot pluggable this is
+ done only once
+ 3. Register all I3C devices
+ from defslvs_data, make
+ sure if device is already
+ registered, i3cdev and
+ IBI data is retained
+ i3cdev is the link between
+ I3C driver and I3C subsystem
+ 4. Delete I3C device from
+ older device list which are
+ not found in defslvs_data
+ (Unplugged ?)
+
+ Initialization thread
+ continue...
+ 3. Register master device,
+ and I3C/I2C device created
+ based on defslvs_data
+ 4. Enable controller IBI
+ 5. Send ENEC MR, HJ
+ Initialization complete
+ 3. Initiate MR request
+
+
+ MR request ISR
+ Disable IBI in controller
+
+ MR request bottom half
+ Send DISEC MR, HJ to
+ other master devices
+
+ Send GETACCMST to secondary 4. Wait for MR DONE
+ master to which mastership
+ is to be granted
+
+ 5. MR granted
+ 6. Update
+ current_master
+
+
+ DEFSLVS bottom half
+ continue...
+ Controller driver read
+ out DEFSLVS data in
+ defslvs_data structure
+
+ Call
+ i3c_master_process_defslvs
+ until it is processed
+ successfully. And set
+ defslvs_done flag = true
+
+ i3c_master_process_defslvs
+ 1. Bus init to correct
+ mode based on
+ defslvs data
+ 2. Register I2C devices
+ from defslvs_data
+ Since I3C devices are
+ not hot pluggable
+ this is done only once
+ 3. Register all I3C devices
+ from defslvs_data, make
+ sure if device is
+ already registered,
+ i3cdev and IBI data is
+ retained
+ i3cdev is the link
+ between I3C driver and
+ I3C subsystem
+ 4. Delete I3C device from
+ older device list which
+ are not found in
+ defslvs_data
+ (Unplugged ?)
+
+ Initialization thread
+ continue...
+ 3. Register master
+ device, and I3C/I2C
+ device created
+ based on defslvs_data
+ 4. Enable controller IBI
+ 5. Send ENEC MR, HJ
+ Initialization complete
--
2.17.1
_______________________________________________
linux-i3c mailing list
linux-i3c@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-i3c
^ permalink raw reply related [flat|nested] 12+ messages in thread
* Re: [PATCH v6 1/8] i3c: master: mastership handover document
2020-04-17 16:20 ` [PATCH v6 1/8] i3c: master: mastership handover document Parshuram Thombare
@ 2020-04-30 7:40 ` Boris Brezillon
0 siblings, 0 replies; 12+ messages in thread
From: Boris Brezillon @ 2020-04-30 7:40 UTC (permalink / raw)
To: Parshuram Thombare
Cc: mparab, bbrezillon, praneeth, linux-kernel, vitor.soares, pgaj,
linux-i3c
On Fri, 17 Apr 2020 18:20:42 +0200
Parshuram Thombare <pthombar@cadence.com> wrote:
> Flow diagram for I3C mastership handover, DEFSLVS
> processing and secondary master initialization.
>
Thanks for doing that, that's really appreciated, but the document
doesn't seem to be formatted properly. Would you mind fixing that (you
can build the docs with 'make htmldocs').
> Signed-off-by: Parshuram Thombare <pthombar@cadence.com>
> ---
> Documentation/driver-api/i3c/index.rst | 1 +
> .../i3c/mastership-handover-flow-diagram.rst | 209 ++++++++++++++++++
> 2 files changed, 210 insertions(+)
> create mode 100644 Documentation/driver-api/i3c/mastership-handover-flow-diagram.rst
>
> diff --git a/Documentation/driver-api/i3c/index.rst b/Documentation/driver-api/i3c/index.rst
> index 783d6dad054b..9a38c5ba87cb 100644
> --- a/Documentation/driver-api/i3c/index.rst
> +++ b/Documentation/driver-api/i3c/index.rst
> @@ -9,3 +9,4 @@ I3C subsystem
> protocol
> device-driver-api
> master-driver-api
> + mastership-handover-flow-diagram
> diff --git a/Documentation/driver-api/i3c/mastership-handover-flow-diagram.rst b/Documentation/driver-api/i3c/mastership-handover-flow-diagram.rst
> new file mode 100644
> index 000000000000..04cd9f1283e0
> --- /dev/null
> +++ b/Documentation/driver-api/i3c/mastership-handover-flow-diagram.rst
> @@ -0,0 +1,209 @@
> +.. SPDX-License-Identifier: GPL-2.0
> +
> +============================
> +I3C mastership handover flow
> +============================
> +
> +Main master Sec Master1 Sec Master 2
> +
> +1. Initialize I3C 1. Initialize I3C 1. Initialize I3C
> + bus in Pure mode bus in Pure mode bus in Pure mode
> +
> +defslvs_done flag defslvs_done flag defslvs_done flag
> + = true = false = false
> +
> +2. Do DAA + SETDASA 2. Spawn init. thread 2. Spawn init. thread
> + Initialization thread Initialization thread
> + Wait for DEFSLVS Wait for DEFSLVS
> + processing processing
> + Wait for Wait for
> + defslvs_done flag defslvs_done flag
> + = true = true
> +
> +3. Send DEFSLVS DEFSLVS ISR DEFSLVS ISR
> + Set flag to indicate Set flag to indicate
> + DEFSLVS processing is DEFSLVS processing is
> + pending pending
> + defslvs_done flag defslvs_done flag
> + = false = false
> +
> + Defer DEFSLVS Defer DEFSLVS
> + processing to bottom processing to bottom
> + half half
> +
> +
> + DEFSLVS bottom half DEFSLVS bottom half
> + Try to acquire bus Try to acquire bus
> + i3c_master_acquire_bus i3c_master_acquire_bus
> +
> +
> + i3c_sec_mst_acquire_bus i3c_sec_mst_acquire_bus
> + state machine
> + 1. Wait until DA is
> + assigned
> +
> +4. Register all
> + slaves and
> + master devive
> +
> +5. Program bus mode
> + as per board
> + info (DTS)
> +
> +6. Send ENEC MR,
> + HJ, SIR CCC
> + Initialization
> + complete
> +
> + 2. Check if DISEC MR 2. Check if DISEC MR
> + is received is received
> + If not, initiate If not, initiate
> + MR request MR request
> +
> +
> + Sec master with highest priority (lowest address) get
> + mastership. Some controllers may not have way of knowing
> + if mastership is granted and can keep waiting for
> + GETACCMST.To avoid this, on reception of MR request
> + from highest priority secondary master, current master
> + send DISEC MR, HJ events to remaining master devices.
> +
> +MR request ISR
> +1. Disable IBI
> + in controller
> +
> +MR request
> +bottom half
> +1. Send DISEC
> + MR, HJ
> + to all but
> + master device
> + to which MR
> + is granted
> + 3. Received DISEC MR
> + Go back to WAIT
> + DA state
> +
> + 1. Wait until DA
> + is assigned
> + 2. Check if DISEC MR
> + is received
> + Wait until ENEC MR
> + is received before
> + sending next MR request
> +
> +2. Send GETACCMST
> + to secondary 3. Wait for MR DONE
> + master to which
> + mastership is to
> + be granted
> + 4. MR granted
> + 5. Update
> + current_master
> +
> +
> + DEFSLVS bottom half
> + continue...
> + Controller driver read
> + out DEFSLVS data in
> + defslvs_data structure
> +
> + Call
> + i3c_master_process_defslvs
> + until it is processed
> + successfully. And set
> + defslvs_done flag = true.
> +
> + i3c_master_process_defslvs
> + 1. Bus init to correct
> + mode based on
> + defslvs data
> + 2. Register I2C devices
> + from defslvs_data
> + Since I3C devices are not
> + hot pluggable this is
> + done only once
> + 3. Register all I3C devices
> + from defslvs_data, make
> + sure if device is already
> + registered, i3cdev and
> + IBI data is retained
> + i3cdev is the link between
> + I3C driver and I3C subsystem
> + 4. Delete I3C device from
> + older device list which are
> + not found in defslvs_data
> + (Unplugged ?)
> +
> + Initialization thread
> + continue...
> + 3. Register master device,
> + and I3C/I2C device created
> + based on defslvs_data
> + 4. Enable controller IBI
> + 5. Send ENEC MR, HJ
> + Initialization complete
> + 3. Initiate MR request
> +
> +
> + MR request ISR
> + Disable IBI in controller
> +
> + MR request bottom half
> + Send DISEC MR, HJ to
> + other master devices
> +
> + Send GETACCMST to secondary 4. Wait for MR DONE
> + master to which mastership
> + is to be granted
> +
> + 5. MR granted
> + 6. Update
> + current_master
> +
> +
> + DEFSLVS bottom half
> + continue...
> + Controller driver read
> + out DEFSLVS data in
> + defslvs_data structure
> +
> + Call
> + i3c_master_process_defslvs
> + until it is processed
> + successfully. And set
> + defslvs_done flag = true
> +
> + i3c_master_process_defslvs
> + 1. Bus init to correct
> + mode based on
> + defslvs data
> + 2. Register I2C devices
> + from defslvs_data
> + Since I3C devices are
> + not hot pluggable
> + this is done only once
> + 3. Register all I3C devices
> + from defslvs_data, make
> + sure if device is
> + already registered,
> + i3cdev and IBI data is
> + retained
> + i3cdev is the link
> + between I3C driver and
> + I3C subsystem
> + 4. Delete I3C device from
> + older device list which
> + are not found in
> + defslvs_data
> + (Unplugged ?)
> +
> + Initialization thread
> + continue...
> + 3. Register master
> + device, and I3C/I2C
> + device created
> + based on defslvs_data
> + 4. Enable controller IBI
> + 5. Send ENEC MR, HJ
> + Initialization complete
_______________________________________________
linux-i3c mailing list
linux-i3c@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-i3c
^ permalink raw reply [flat|nested] 12+ messages in thread
* [PATCH v6 2/8] i3c: master: split bus_init callback into bus_init and master_set_info
2020-04-17 16:19 [PATCH v6 0/8] I3C mastership handover support Parshuram Thombare
2020-04-17 16:20 ` [PATCH v6 1/8] i3c: master: mastership handover document Parshuram Thombare
@ 2020-04-17 16:20 ` Parshuram Thombare
2020-04-30 7:55 ` Boris Brezillon
2020-04-17 16:21 ` [PATCH v6 3/8] i3c: master: i3c mastership request and handover Parshuram Thombare
` (5 subsequent siblings)
7 siblings, 1 reply; 12+ messages in thread
From: Parshuram Thombare @ 2020-04-17 16:20 UTC (permalink / raw)
To: bbrezillon, vitor.soares
Cc: mparab, Parshuram Thombare, praneeth, linux-kernel, pgaj, linux-i3c
To support mastership handover procedure, this patch splits the
bus_init callback into bus_init and master_set_info callbacks
Signed-off-by: Parshuram Thombare <pthombar@cadence.com>
---
drivers/i3c/master.c | 10 +++--
drivers/i3c/master/dw-i3c-master.c | 29 ++++++++-----
drivers/i3c/master/i3c-master-cdns.c | 63 ++++++++++++++++++----------
include/linux/i3c/master.h | 7 +++-
4 files changed, 71 insertions(+), 38 deletions(-)
diff --git a/drivers/i3c/master.c b/drivers/i3c/master.c
index 5f4bd52121fe..0ec332e45737 100644
--- a/drivers/i3c/master.c
+++ b/drivers/i3c/master.c
@@ -1716,6 +1716,10 @@ static int i3c_master_bus_init(struct i3c_master_controller *master)
if (ret)
goto err_detach_devs;
+ ret = master->ops->master_set_info(master);
+ if (ret)
+ goto err_detach_devs;
+
/*
* The master device should have been instantiated in ->bus_init(),
* complain if this was not the case.
@@ -2378,9 +2382,9 @@ EXPORT_SYMBOL_GPL(i3c_generic_ibi_recycle_slot);
static int i3c_master_check_ops(const struct i3c_master_controller_ops *ops)
{
- if (!ops || !ops->bus_init || !ops->priv_xfers ||
- !ops->send_ccc_cmd || !ops->do_daa || !ops->i2c_xfers ||
- !ops->i2c_funcs)
+ if (!ops || !ops->bus_init || !ops->master_set_info ||
+ !ops->priv_xfers || !ops->send_ccc_cmd || !ops->do_daa ||
+ !ops->i2c_xfers || !ops->i2c_funcs)
return -EINVAL;
if (ops->request_ibi &&
diff --git a/drivers/i3c/master/dw-i3c-master.c b/drivers/i3c/master/dw-i3c-master.c
index 1d83c97431c7..5c9a72d68fb8 100644
--- a/drivers/i3c/master/dw-i3c-master.c
+++ b/drivers/i3c/master/dw-i3c-master.c
@@ -593,7 +593,6 @@ static int dw_i3c_master_bus_init(struct i3c_master_controller *m)
{
struct dw_i3c_master *master = to_dw_i3c_master(m);
struct i3c_bus *bus = i3c_master_get_bus(m);
- struct i3c_device_info info = { };
u32 thld_ctrl;
int ret;
@@ -624,6 +623,24 @@ static int dw_i3c_master_bus_init(struct i3c_master_controller *m)
writel(INTR_MASTER_MASK, master->regs + INTR_STATUS_EN);
writel(INTR_MASTER_MASK, master->regs + INTR_SIGNAL_EN);
+ writel(IBI_REQ_REJECT_ALL, master->regs + IBI_SIR_REQ_REJECT);
+ writel(IBI_REQ_REJECT_ALL, master->regs + IBI_MR_REQ_REJECT);
+
+ /* For now don't support Hot-Join */
+ writel(readl(master->regs + DEVICE_CTRL) | DEV_CTRL_HOT_JOIN_NACK,
+ master->regs + DEVICE_CTRL);
+
+ dw_i3c_master_enable(master);
+
+ return 0;
+}
+
+static int dw_i3c_master_set_info(struct i3c_master_controller *m)
+{
+ struct dw_i3c_master *master = to_dw_i3c_master(m);
+ struct i3c_device_info info = { };
+ int ret;
+
ret = i3c_master_get_free_addr(m, 0);
if (ret < 0)
return ret;
@@ -638,15 +655,6 @@ static int dw_i3c_master_bus_init(struct i3c_master_controller *m)
if (ret)
return ret;
- writel(IBI_REQ_REJECT_ALL, master->regs + IBI_SIR_REQ_REJECT);
- writel(IBI_REQ_REJECT_ALL, master->regs + IBI_MR_REQ_REJECT);
-
- /* For now don't support Hot-Join */
- writel(readl(master->regs + DEVICE_CTRL) | DEV_CTRL_HOT_JOIN_NACK,
- master->regs + DEVICE_CTRL);
-
- dw_i3c_master_enable(master);
-
return 0;
}
@@ -1088,6 +1096,7 @@ static irqreturn_t dw_i3c_master_irq_handler(int irq, void *dev_id)
static const struct i3c_master_controller_ops dw_mipi_i3c_ops = {
.bus_init = dw_i3c_master_bus_init,
+ .master_set_info = dw_i3c_master_set_info,
.bus_cleanup = dw_i3c_master_bus_cleanup,
.attach_i3c_dev = dw_i3c_master_attach_i3c_dev,
.reattach_i3c_dev = dw_i3c_master_reattach_i3c_dev,
diff --git a/drivers/i3c/master/i3c-master-cdns.c b/drivers/i3c/master/i3c-master-cdns.c
index 8889a4fdb454..c2d1631a9e38 100644
--- a/drivers/i3c/master/i3c-master-cdns.c
+++ b/drivers/i3c/master/i3c-master-cdns.c
@@ -1199,21 +1199,20 @@ static int cdns_i3c_master_bus_init(struct i3c_master_controller *m)
struct cdns_i3c_master *master = to_cdns_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;
+ u32 ctrl, prescl0, prescl1, pres, low, bus_mode;
+ int ncycles;
switch (bus->mode) {
case I3C_BUS_MODE_PURE:
- ctrl = CTRL_PURE_BUS_MODE;
+ bus_mode = CTRL_PURE_BUS_MODE;
break;
case I3C_BUS_MODE_MIXED_FAST:
- ctrl = CTRL_MIXED_FAST_BUS_MODE;
+ bus_mode = CTRL_MIXED_FAST_BUS_MODE;
break;
case I3C_BUS_MODE_MIXED_SLOW:
- ctrl = CTRL_MIXED_SLOW_BUS_MODE;
+ bus_mode = CTRL_MIXED_SLOW_BUS_MODE;
break;
default:
@@ -1244,7 +1243,6 @@ static int cdns_i3c_master_bus_init(struct i3c_master_controller *m)
bus->scl_rate.i2c = sysclk_rate / ((pres + 1) * 5);
prescl0 |= PRESCL_CTRL0_I2C(pres);
- writel(prescl0, master->regs + PRESCL_CTRL0);
/* Calculate OD and PP low. */
pres_step = 1000000000 / (bus->scl_rate.i3c * 4);
@@ -1252,15 +1250,43 @@ static int cdns_i3c_master_bus_init(struct i3c_master_controller *m)
if (ncycles < 0)
ncycles = 0;
prescl1 = PRESCL_CTRL1_OD_LOW(ncycles);
+
+ ctrl = readl(master->regs + CTRL);
+ if (ctrl & CTRL_DEV_EN)
+ cdns_i3c_master_disable(master);
+ writel(prescl0, master->regs + PRESCL_CTRL0);
writel(prescl1, master->regs + PRESCL_CTRL1);
+ ctrl &= ~CTRL_BUS_MODE_MASK;
+ ctrl |= bus_mode | CTRL_HALT_EN | CTRL_MCS_EN;
+ /*
+ * 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.
+ */
+ if (!m->secondary)
+ ctrl |= CTRL_HJ_ACK | CTRL_HJ_DISEC;
+ writel(ctrl, master->regs + CTRL);
+ cdns_i3c_master_enable(master);
- /* Get an address for the master. */
- ret = i3c_master_get_free_addr(m, 0);
- if (ret < 0)
- return ret;
+ return 0;
+}
- writel(prepare_rr0_dev_address(ret) | DEV_ID_RR0_IS_I3C,
- master->regs + DEV_ID_RR0(0));
+static int cdns_i3c_master_set_info(struct i3c_master_controller *m)
+{
+ struct cdns_i3c_master *master = to_cdns_i3c_master(m);
+ struct i3c_device_info info = { };
+ int ret;
+
+ if (!m->secondary) {
+ /* 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));
+ }
cdns_i3c_master_dev_rr_to_info(master, 0, &info);
if (info.bcr & I3C_BCR_HDR_CAP)
@@ -1270,16 +1296,6 @@ static int cdns_i3c_master_bus_init(struct i3c_master_controller *m)
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_ACK | CTRL_HJ_DISEC | CTRL_HALT_EN | CTRL_MCS_EN;
- writel(ctrl, master->regs + CTRL);
-
- cdns_i3c_master_enable(master);
return 0;
}
@@ -1507,6 +1523,7 @@ static void cdns_i3c_master_recycle_ibi_slot(struct i3c_dev_desc *dev,
static const struct i3c_master_controller_ops cdns_i3c_master_ops = {
.bus_init = cdns_i3c_master_bus_init,
+ .master_set_info = cdns_i3c_master_set_info,
.bus_cleanup = cdns_i3c_master_bus_cleanup,
.do_daa = cdns_i3c_master_do_daa,
.attach_i3c_dev = cdns_i3c_master_attach_i3c_dev,
diff --git a/include/linux/i3c/master.h b/include/linux/i3c/master.h
index f13fd8b1dd79..3dc7eafe811a 100644
--- a/include/linux/i3c/master.h
+++ b/include/linux/i3c/master.h
@@ -337,10 +337,12 @@ struct i3c_bus {
/**
* struct i3c_master_controller_ops - I3C master methods
- * @bus_init: hook responsible for the I3C bus initialization. You should at
- * least call master_set_info() from there and set the bus mode.
+ * @bus_init: hook responsible for the I3C bus initialization.
* You can also put controller specific initialization in there.
* This method is mandatory.
+ * @master_set_info: hook responsible for assigning address to main master.
+ * You should call i3c_master_set_info from here.
+ * This method is mandatory.
* @bus_cleanup: cleanup everything done in
* &i3c_master_controller_ops->bus_init().
* This method is optional.
@@ -421,6 +423,7 @@ struct i3c_bus {
*/
struct i3c_master_controller_ops {
int (*bus_init)(struct i3c_master_controller *master);
+ int (*master_set_info)(struct i3c_master_controller *m);
void (*bus_cleanup)(struct i3c_master_controller *master);
int (*attach_i3c_dev)(struct i3c_dev_desc *dev);
int (*reattach_i3c_dev)(struct i3c_dev_desc *dev, u8 old_dyn_addr);
--
2.17.1
_______________________________________________
linux-i3c mailing list
linux-i3c@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-i3c
^ permalink raw reply related [flat|nested] 12+ messages in thread
* Re: [PATCH v6 2/8] i3c: master: split bus_init callback into bus_init and master_set_info
2020-04-17 16:20 ` [PATCH v6 2/8] i3c: master: split bus_init callback into bus_init and master_set_info Parshuram Thombare
@ 2020-04-30 7:55 ` Boris Brezillon
0 siblings, 0 replies; 12+ messages in thread
From: Boris Brezillon @ 2020-04-30 7:55 UTC (permalink / raw)
To: Parshuram Thombare
Cc: mparab, bbrezillon, praneeth, linux-kernel, vitor.soares, pgaj,
linux-i3c
On Fri, 17 Apr 2020 18:20:52 +0200
Parshuram Thombare <pthombar@cadence.com> wrote:
> To support mastership handover procedure, this patch splits the
> bus_init callback into bus_init and master_set_info callbacks
Missing period at the end of this sentence.
IIRC, we discussed passing master info directly at controller
registration time for primary master registration, thus avoiding this
->master_set_info() step. Any good reason for doing that? I mean, I'd
expect the PID, BCR, DCR to be fixed, the only one that can be assigned
automatically is the address, and we can have a magic value for
'auto-assign the first available address', like '0'. The
write to DEV_ID_RR0() can be done conditionally in master_bus_init()
when '!secondary_master'.
>
> Signed-off-by: Parshuram Thombare <pthombar@cadence.com>
> ---
> drivers/i3c/master.c | 10 +++--
> drivers/i3c/master/dw-i3c-master.c | 29 ++++++++-----
> drivers/i3c/master/i3c-master-cdns.c | 63 ++++++++++++++++++----------
> include/linux/i3c/master.h | 7 +++-
> 4 files changed, 71 insertions(+), 38 deletions(-)
>
> diff --git a/drivers/i3c/master.c b/drivers/i3c/master.c
> index 5f4bd52121fe..0ec332e45737 100644
> --- a/drivers/i3c/master.c
> +++ b/drivers/i3c/master.c
> @@ -1716,6 +1716,10 @@ static int i3c_master_bus_init(struct i3c_master_controller *master)
> if (ret)
> goto err_detach_devs;
>
> + ret = master->ops->master_set_info(master);
> + if (ret)
> + goto err_detach_devs;
> +
> /*
> * The master device should have been instantiated in ->bus_init(),
> * complain if this was not the case.
> @@ -2378,9 +2382,9 @@ EXPORT_SYMBOL_GPL(i3c_generic_ibi_recycle_slot);
>
> static int i3c_master_check_ops(const struct i3c_master_controller_ops *ops)
> {
> - if (!ops || !ops->bus_init || !ops->priv_xfers ||
> - !ops->send_ccc_cmd || !ops->do_daa || !ops->i2c_xfers ||
> - !ops->i2c_funcs)
> + if (!ops || !ops->bus_init || !ops->master_set_info ||
> + !ops->priv_xfers || !ops->send_ccc_cmd || !ops->do_daa ||
> + !ops->i2c_xfers || !ops->i2c_funcs)
> return -EINVAL;
>
> if (ops->request_ibi &&
> diff --git a/drivers/i3c/master/dw-i3c-master.c b/drivers/i3c/master/dw-i3c-master.c
> index 1d83c97431c7..5c9a72d68fb8 100644
> --- a/drivers/i3c/master/dw-i3c-master.c
> +++ b/drivers/i3c/master/dw-i3c-master.c
> @@ -593,7 +593,6 @@ static int dw_i3c_master_bus_init(struct i3c_master_controller *m)
> {
> struct dw_i3c_master *master = to_dw_i3c_master(m);
> struct i3c_bus *bus = i3c_master_get_bus(m);
> - struct i3c_device_info info = { };
> u32 thld_ctrl;
> int ret;
>
> @@ -624,6 +623,24 @@ static int dw_i3c_master_bus_init(struct i3c_master_controller *m)
> writel(INTR_MASTER_MASK, master->regs + INTR_STATUS_EN);
> writel(INTR_MASTER_MASK, master->regs + INTR_SIGNAL_EN);
>
> + writel(IBI_REQ_REJECT_ALL, master->regs + IBI_SIR_REQ_REJECT);
> + writel(IBI_REQ_REJECT_ALL, master->regs + IBI_MR_REQ_REJECT);
> +
> + /* For now don't support Hot-Join */
> + writel(readl(master->regs + DEVICE_CTRL) | DEV_CTRL_HOT_JOIN_NACK,
> + master->regs + DEVICE_CTRL);
> +
> + dw_i3c_master_enable(master);
> +
> + return 0;
> +}
> +
> +static int dw_i3c_master_set_info(struct i3c_master_controller *m)
> +{
> + struct dw_i3c_master *master = to_dw_i3c_master(m);
> + struct i3c_device_info info = { };
> + int ret;
> +
> ret = i3c_master_get_free_addr(m, 0);
> if (ret < 0)
> return ret;
> @@ -638,15 +655,6 @@ static int dw_i3c_master_bus_init(struct i3c_master_controller *m)
> if (ret)
> return ret;
>
> - writel(IBI_REQ_REJECT_ALL, master->regs + IBI_SIR_REQ_REJECT);
> - writel(IBI_REQ_REJECT_ALL, master->regs + IBI_MR_REQ_REJECT);
> -
> - /* For now don't support Hot-Join */
> - writel(readl(master->regs + DEVICE_CTRL) | DEV_CTRL_HOT_JOIN_NACK,
> - master->regs + DEVICE_CTRL);
> -
> - dw_i3c_master_enable(master);
> -
> return 0;
> }
>
> @@ -1088,6 +1096,7 @@ static irqreturn_t dw_i3c_master_irq_handler(int irq, void *dev_id)
>
> static const struct i3c_master_controller_ops dw_mipi_i3c_ops = {
> .bus_init = dw_i3c_master_bus_init,
> + .master_set_info = dw_i3c_master_set_info,
> .bus_cleanup = dw_i3c_master_bus_cleanup,
> .attach_i3c_dev = dw_i3c_master_attach_i3c_dev,
> .reattach_i3c_dev = dw_i3c_master_reattach_i3c_dev,
> diff --git a/drivers/i3c/master/i3c-master-cdns.c b/drivers/i3c/master/i3c-master-cdns.c
> index 8889a4fdb454..c2d1631a9e38 100644
> --- a/drivers/i3c/master/i3c-master-cdns.c
> +++ b/drivers/i3c/master/i3c-master-cdns.c
> @@ -1199,21 +1199,20 @@ static int cdns_i3c_master_bus_init(struct i3c_master_controller *m)
> struct cdns_i3c_master *master = to_cdns_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;
> + u32 ctrl, prescl0, prescl1, pres, low, bus_mode;
> + int ncycles;
>
> switch (bus->mode) {
> case I3C_BUS_MODE_PURE:
> - ctrl = CTRL_PURE_BUS_MODE;
> + bus_mode = CTRL_PURE_BUS_MODE;
> break;
>
> case I3C_BUS_MODE_MIXED_FAST:
> - ctrl = CTRL_MIXED_FAST_BUS_MODE;
> + bus_mode = CTRL_MIXED_FAST_BUS_MODE;
> break;
>
> case I3C_BUS_MODE_MIXED_SLOW:
> - ctrl = CTRL_MIXED_SLOW_BUS_MODE;
> + bus_mode = CTRL_MIXED_SLOW_BUS_MODE;
> break;
>
> default:
> @@ -1244,7 +1243,6 @@ static int cdns_i3c_master_bus_init(struct i3c_master_controller *m)
> bus->scl_rate.i2c = sysclk_rate / ((pres + 1) * 5);
>
> prescl0 |= PRESCL_CTRL0_I2C(pres);
> - writel(prescl0, master->regs + PRESCL_CTRL0);
>
> /* Calculate OD and PP low. */
> pres_step = 1000000000 / (bus->scl_rate.i3c * 4);
> @@ -1252,15 +1250,43 @@ static int cdns_i3c_master_bus_init(struct i3c_master_controller *m)
> if (ncycles < 0)
> ncycles = 0;
> prescl1 = PRESCL_CTRL1_OD_LOW(ncycles);
> +
> + ctrl = readl(master->regs + CTRL);
> + if (ctrl & CTRL_DEV_EN)
> + cdns_i3c_master_disable(master);
> + writel(prescl0, master->regs + PRESCL_CTRL0);
> writel(prescl1, master->regs + PRESCL_CTRL1);
> + ctrl &= ~CTRL_BUS_MODE_MASK;
> + ctrl |= bus_mode | CTRL_HALT_EN | CTRL_MCS_EN;
> + /*
> + * 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.
> + */
> + if (!m->secondary)
> + ctrl |= CTRL_HJ_ACK | CTRL_HJ_DISEC;
> + writel(ctrl, master->regs + CTRL);
> + cdns_i3c_master_enable(master);
>
> - /* Get an address for the master. */
> - ret = i3c_master_get_free_addr(m, 0);
> - if (ret < 0)
> - return ret;
> + return 0;
> +}
>
> - writel(prepare_rr0_dev_address(ret) | DEV_ID_RR0_IS_I3C,
> - master->regs + DEV_ID_RR0(0));
> +static int cdns_i3c_master_set_info(struct i3c_master_controller *m)
> +{
> + struct cdns_i3c_master *master = to_cdns_i3c_master(m);
> + struct i3c_device_info info = { };
> + int ret;
> +
> + if (!m->secondary) {
> + /* 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));
> + }
>
> cdns_i3c_master_dev_rr_to_info(master, 0, &info);
> if (info.bcr & I3C_BCR_HDR_CAP)
> @@ -1270,16 +1296,6 @@ static int cdns_i3c_master_bus_init(struct i3c_master_controller *m)
> 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_ACK | CTRL_HJ_DISEC | CTRL_HALT_EN | CTRL_MCS_EN;
> - writel(ctrl, master->regs + CTRL);
> -
> - cdns_i3c_master_enable(master);
>
> return 0;
> }
> @@ -1507,6 +1523,7 @@ static void cdns_i3c_master_recycle_ibi_slot(struct i3c_dev_desc *dev,
>
> static const struct i3c_master_controller_ops cdns_i3c_master_ops = {
> .bus_init = cdns_i3c_master_bus_init,
> + .master_set_info = cdns_i3c_master_set_info,
> .bus_cleanup = cdns_i3c_master_bus_cleanup,
> .do_daa = cdns_i3c_master_do_daa,
> .attach_i3c_dev = cdns_i3c_master_attach_i3c_dev,
> diff --git a/include/linux/i3c/master.h b/include/linux/i3c/master.h
> index f13fd8b1dd79..3dc7eafe811a 100644
> --- a/include/linux/i3c/master.h
> +++ b/include/linux/i3c/master.h
> @@ -337,10 +337,12 @@ struct i3c_bus {
>
> /**
> * struct i3c_master_controller_ops - I3C master methods
> - * @bus_init: hook responsible for the I3C bus initialization. You should at
> - * least call master_set_info() from there and set the bus mode.
> + * @bus_init: hook responsible for the I3C bus initialization.
> * You can also put controller specific initialization in there.
> * This method is mandatory.
> + * @master_set_info: hook responsible for assigning address to main master.
> + * You should call i3c_master_set_info from here.
> + * This method is mandatory.
> * @bus_cleanup: cleanup everything done in
> * &i3c_master_controller_ops->bus_init().
> * This method is optional.
> @@ -421,6 +423,7 @@ struct i3c_bus {
> */
> struct i3c_master_controller_ops {
> int (*bus_init)(struct i3c_master_controller *master);
> + int (*master_set_info)(struct i3c_master_controller *m);
> void (*bus_cleanup)(struct i3c_master_controller *master);
> int (*attach_i3c_dev)(struct i3c_dev_desc *dev);
> int (*reattach_i3c_dev)(struct i3c_dev_desc *dev, u8 old_dyn_addr);
_______________________________________________
linux-i3c mailing list
linux-i3c@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-i3c
^ permalink raw reply [flat|nested] 12+ messages in thread
* [PATCH v6 3/8] i3c: master: i3c mastership request and handover
2020-04-17 16:19 [PATCH v6 0/8] I3C mastership handover support Parshuram Thombare
2020-04-17 16:20 ` [PATCH v6 1/8] i3c: master: mastership handover document Parshuram Thombare
2020-04-17 16:20 ` [PATCH v6 2/8] i3c: master: split bus_init callback into bus_init and master_set_info Parshuram Thombare
@ 2020-04-17 16:21 ` Parshuram Thombare
2020-04-30 8:07 ` Boris Brezillon
2020-04-17 16:21 ` [PATCH v6 4/8] i3c: master: defslvs processing Parshuram Thombare
` (4 subsequent siblings)
7 siblings, 1 reply; 12+ messages in thread
From: Parshuram Thombare @ 2020-04-17 16:21 UTC (permalink / raw)
To: bbrezillon, vitor.soares
Cc: mparab, Parshuram Thombare, praneeth, linux-kernel, pgaj, linux-i3c
This patch add I3C mastership request and
handover infrasturcture to I3C master subsystem.
Signed-off-by: Parshuram Thombare <pthombar@cadence.com>
---
drivers/i3c/master.c | 221 +++++++++++++++++++++++++++++++++++++
include/linux/i3c/master.h | 34 ++++++
2 files changed, 255 insertions(+)
diff --git a/drivers/i3c/master.c b/drivers/i3c/master.c
index 0ec332e45737..3598856a0b25 100644
--- a/drivers/i3c/master.c
+++ b/drivers/i3c/master.c
@@ -1217,6 +1217,69 @@ static int i3c_master_getdcr_locked(struct i3c_master_controller *master,
return ret;
}
+static int i3c_master_get_accmst_locked(struct i3c_master_controller *master,
+ u8 addr)
+{
+ struct i3c_ccc_getaccmst *accmst;
+ struct i3c_ccc_cmd_dest dest;
+ struct i3c_ccc_cmd cmd;
+ int ret;
+
+ accmst = i3c_ccc_cmd_dest_init(&dest, addr, sizeof(*accmst));
+ if (!accmst)
+ return -ENOMEM;
+
+ i3c_ccc_cmd_init(&cmd, true, I3C_CCC_GETACCMST, &dest, 1);
+
+ ret = i3c_master_send_ccc_cmd_locked(master, &cmd);
+ if (ret)
+ goto out;
+
+ if (dest.payload.len != sizeof(*accmst))
+ ret = -EIO;
+
+out:
+ i3c_ccc_cmd_dest_cleanup(&dest);
+
+ return ret;
+}
+
+static int i3c_master_enable_mr_events(struct i3c_master_controller *master)
+{
+ int ret;
+
+ master->ops->enable_mr_events(master);
+ i3c_bus_maintenance_lock(&master->bus);
+ ret = i3c_master_enec_locked(master, I3C_BROADCAST_ADDR,
+ I3C_CCC_EVENT_MR | I3C_CCC_EVENT_HJ);
+ i3c_bus_maintenance_unlock(&master->bus);
+
+ return ret;
+}
+
+static int i3c_master_getaccmst(struct i3c_master_controller *master)
+{
+ int ret;
+
+ i3c_bus_maintenance_lock(&master->bus);
+ ret = i3c_master_get_accmst_locked(master, master->mr_addr);
+ i3c_bus_maintenance_unlock(&master->bus);
+
+ return ret;
+}
+
+static int i3c_master_request_mastership(struct i3c_master_controller *master)
+{
+ int ret;
+
+ /* request_mastership callback should handle EN/DIS EC MR.*/
+ i3c_bus_maintenance_lock(&master->bus);
+ ret = master->ops->request_mastership(master);
+ i3c_bus_maintenance_unlock(&master->bus);
+
+ return ret;
+}
+
static int i3c_master_retrieve_dev_info(struct i3c_dev_desc *dev)
{
struct i3c_master_controller *master = i3c_dev_get_master(dev);
@@ -1612,6 +1675,161 @@ static void i3c_master_detach_free_devs(struct i3c_master_controller *master)
}
}
+static void i3c_mst_yield_bus(struct work_struct *work)
+{
+ struct i3c_master_controller *m;
+ struct i3c_dev_desc *i3cdev;
+ int ret;
+
+ m = container_of(work, struct i3c_master_controller, mst_work);
+
+ switch (m->mr_state) {
+ case I3C_MR_DISEC_MR:
+ /*
+ * Disable MR on all but the secondary master first
+ * reaching here.
+ */
+ i3c_bus_for_each_i3cdev(&m->bus, i3cdev) {
+ if (I3C_BCR_DEVICE_ROLE(i3cdev->info.bcr) !=
+ I3C_BCR_I3C_MASTER ||
+ i3cdev->info.dyn_addr == m->mr_addr ||
+ m->this == i3cdev)
+ continue;
+ i3c_bus_maintenance_lock(&m->bus);
+ i3c_master_disec_locked(m, i3cdev->info.dyn_addr,
+ I3C_CCC_EVENT_MR |
+ I3C_CCC_EVENT_HJ);
+ i3c_bus_maintenance_unlock(&m->bus);
+ }
+ m->mr_state = I3C_MR_GETACCMST;
+ queue_work(m->wq, &m->mst_work);
+ break;
+
+ case I3C_MR_GETACCMST:
+ ret = i3c_master_getaccmst(m);
+ if (!ret)
+ m->mr_state = I3C_MR_DONE;
+ else
+ m->mr_state = I3C_MR_FAILED;
+ queue_work(m->wq, &m->mst_work);
+ break;
+
+ case I3C_MR_DONE:
+ i3c_bus_for_each_i3cdev(&m->bus, i3cdev) {
+ if (m->mr_addr == i3cdev->info.dyn_addr) {
+ m->bus.cur_master = i3cdev;
+ break;
+ }
+ }
+ m->mr_state = I3C_MR_IDLE;
+ break;
+
+ default:
+ case I3C_MR_FAILED:
+ i3c_master_enable_mr_events(m);
+ m->mr_state = I3C_MR_IDLE;
+ break;
+ }
+}
+
+void
+i3c_master_yield_bus(struct i3c_master_controller *master, u8 sec_mst_dyn_addr)
+{
+ if (master->this && master->this == master->bus.cur_master) {
+ master->ops->disable_mr_events(master);
+ master->mr_addr = sec_mst_dyn_addr;
+ master->mr_state = I3C_MR_DISEC_MR;
+ queue_work(master->wq, &master->mst_work);
+ } else {
+ /* If not a current master, we should never come here */
+ WARN_ON(1);
+ }
+}
+EXPORT_SYMBOL_GPL(i3c_master_yield_bus);
+
+static void i3c_sec_mst_acquire_bus(struct work_struct *work)
+{
+ struct i3c_master_controller *m;
+ struct i3c_bus *i3cbus;
+ int ret;
+
+ m = container_of(work, struct i3c_master_controller, sec_mst_work);
+ i3cbus = i3c_master_get_bus(m);
+
+ switch (m->mr_state) {
+ case I3C_MR_WAIT_DA:
+ /* Wait until this master have dynamic address */
+ if (m->ops->check_event_set(m, I3C_SLV_DA_UPDATE))
+ m->mr_state = I3C_MR_REQUEST;
+ queue_work(m->wq, &m->sec_mst_work);
+ break;
+
+ case I3C_MR_REQUEST:
+ /* Wait until we can send MR */
+ ret = i3c_master_request_mastership(m);
+ if (!ret)
+ m->mr_state = I3C_MR_WAIT_MR_DONE;
+ queue_work(m->wq, &m->sec_mst_work);
+ break;
+
+ case I3C_MR_WAIT_MR_DONE:
+ if (m->ops->check_event_set(m, I3C_SLV_MR_DONE)) {
+ m->mr_state = I3C_MR_DONE;
+ m->bus.cur_master = m->this;
+ complete(&m->mr_comp);
+ } else {
+ queue_work(m->wq, &m->sec_mst_work);
+ }
+ break;
+
+ default:
+ m->mr_state = I3C_MR_FAILED;
+ complete(&m->mr_comp);
+ break;
+ }
+}
+
+void i3c_sec_mst_mr_dis_event(struct i3c_master_controller *m)
+{
+ if (m->mr_state != I3C_MR_IDLE)
+ m->mr_state = I3C_MR_WAIT_DA;
+}
+EXPORT_SYMBOL_GPL(i3c_sec_mst_mr_dis_event);
+
+/* This function is expected to be called with normaluse_lock */
+int i3c_master_acquire_bus(struct i3c_master_controller *master)
+{
+ int ret = 0;
+
+ if (!master->this || master->this != master->bus.cur_master) {
+ if (master->mr_state == I3C_MR_IDLE) {
+ master->mr_state = I3C_MR_WAIT_DA;
+ init_completion(&master->mr_comp);
+ queue_work(master->wq, &master->sec_mst_work);
+ /*
+ * Bus acquire procedure may need write lock
+ * so release read lock before yielding
+ * to bus acquire state machine
+ */
+ i3c_bus_normaluse_unlock(&master->bus);
+ wait_for_completion(&master->mr_comp);
+ i3c_bus_normaluse_lock(&master->bus);
+ if (master->mr_state != I3C_MR_DONE)
+ ret = -EAGAIN;
+ master->mr_state = I3C_MR_IDLE;
+ } else {
+ /*
+ * MR request is already in process for
+ * this master
+ */
+ ret = -EAGAIN;
+ }
+ }
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(i3c_master_acquire_bus);
+
/**
* i3c_master_bus_init() - initialize an I3C bus
* @master: main master initializing the bus
@@ -2451,6 +2669,9 @@ int i3c_master_register(struct i3c_master_controller *master,
device_initialize(&master->dev);
dev_set_name(&master->dev, "i3c-%d", i3cbus->id);
+ INIT_WORK(&master->sec_mst_work, i3c_sec_mst_acquire_bus);
+ INIT_WORK(&master->mst_work, i3c_mst_yield_bus);
+
ret = of_populate_i3c_bus(master);
if (ret)
goto err_put_dev;
diff --git a/include/linux/i3c/master.h b/include/linux/i3c/master.h
index 3dc7eafe811a..c465c7792ccb 100644
--- a/include/linux/i3c/master.h
+++ b/include/linux/i3c/master.h
@@ -259,6 +259,27 @@ enum i3c_bus_mode {
I3C_BUS_MODE_MIXED_SLOW,
};
+enum i3c_mr_state {
+ I3C_MR_IDLE,
+ I3C_MR_DISEC_MR,
+ I3C_MR_SEND_DEFSLVS,
+ I3C_MR_GETACCMST,
+ I3C_MR_WAIT_DA,
+ I3C_MR_CHECK_STATE,
+ I3C_MR_REQUEST,
+ I3C_MR_WAIT_DEFSLVS,
+ I3C_MR_WAIT_MR_DONE,
+ I3C_MR_DONE,
+ I3C_MR_FAILED,
+};
+
+enum i3c_event {
+ I3C_SLV_DA_UPDATE,
+ I3C_SLV_DEFSLVS_CCC,
+ I3C_SLV_MR_DIS,
+ I3C_SLV_MR_DONE,
+};
+
/**
* enum i3c_addr_slot_status - I3C address slot status
* @I3C_ADDR_SLOT_FREE: address is free
@@ -448,6 +469,11 @@ struct i3c_master_controller_ops {
int (*disable_ibi)(struct i3c_dev_desc *dev);
void (*recycle_ibi_slot)(struct i3c_dev_desc *dev,
struct i3c_ibi_slot *slot);
+ int (*request_mastership)(struct i3c_master_controller *master);
+ void (*enable_mr_events)(struct i3c_master_controller *m);
+ void (*disable_mr_events)(struct i3c_master_controller *m);
+ bool (*check_event_set)(struct i3c_master_controller *m,
+ enum i3c_event);
};
/**
@@ -489,6 +515,11 @@ struct i3c_master_controller {
} boardinfo;
struct i3c_bus bus;
struct workqueue_struct *wq;
+ struct work_struct mst_work;
+ struct work_struct sec_mst_work;
+ struct completion mr_comp;
+ enum i3c_mr_state mr_state;
+ u8 mr_addr;
};
/**
@@ -513,6 +544,9 @@ struct i3c_master_controller {
#define i3c_bus_for_each_i3cdev(bus, dev) \
list_for_each_entry(dev, &(bus)->devs.i3c, common.node)
+void i3c_master_yield_bus(struct i3c_master_controller *master,
+ u8 slv_dyn_addr);
+void i3c_sec_mst_mr_dis_event(struct i3c_master_controller *m);
int i3c_master_do_i2c_xfers(struct i3c_master_controller *master,
const struct i2c_msg *xfers,
int nxfers);
--
2.17.1
_______________________________________________
linux-i3c mailing list
linux-i3c@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-i3c
^ permalink raw reply related [flat|nested] 12+ messages in thread
* Re: [PATCH v6 3/8] i3c: master: i3c mastership request and handover
2020-04-17 16:21 ` [PATCH v6 3/8] i3c: master: i3c mastership request and handover Parshuram Thombare
@ 2020-04-30 8:07 ` Boris Brezillon
0 siblings, 0 replies; 12+ messages in thread
From: Boris Brezillon @ 2020-04-30 8:07 UTC (permalink / raw)
To: Parshuram Thombare
Cc: mparab, bbrezillon, praneeth, linux-kernel, vitor.soares, pgaj,
linux-i3c
On Fri, 17 Apr 2020 18:21:02 +0200
Parshuram Thombare <pthombar@cadence.com> wrote:
> +
> +/* This function is expected to be called with normaluse_lock */
> +int i3c_master_acquire_bus(struct i3c_master_controller *master)
> +{
> + int ret = 0;
> +
> + if (!master->this || master->this != master->bus.cur_master) {
Let's limit the number of indentations by doing
if (master->this == master->bus.cur_master)
return 0;
> + if (master->mr_state == I3C_MR_IDLE) {
> + master->mr_state = I3C_MR_WAIT_DA;
> + init_completion(&master->mr_comp);
> + queue_work(master->wq, &master->sec_mst_work);
> + /*
> + * Bus acquire procedure may need write lock
> + * so release read lock before yielding
> + * to bus acquire state machine
> + */
> + i3c_bus_normaluse_unlock(&master->bus);
> + wait_for_completion(&master->mr_comp);
> + i3c_bus_normaluse_lock(&master->bus);
Is that really enough? I remember we had something a bit more complex
to handle the case where bus is acquired to send a message to a device,
and another master on the bus re-acquires it before we have a chance to
send this message message. i3c_master_acquire_bus_ownership() was
dealing with that in Przemek series. It seems you've rewritten a lot of
these things. Would you mind explaining why, and how that works?
> + if (master->mr_state != I3C_MR_DONE)
> + ret = -EAGAIN;
> + master->mr_state = I3C_MR_IDLE;
> + } else {
> + /*
> + * MR request is already in process for
> + * this master
> + */
> + ret = -EAGAIN;
> + }
> + }
> +
> + return ret;
> +}
> +EXPORT_SYMBOL_GPL(i3c_master_acquire_bus);
_______________________________________________
linux-i3c mailing list
linux-i3c@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-i3c
^ permalink raw reply [flat|nested] 12+ messages in thread
* [PATCH v6 4/8] i3c: master: defslvs processing
2020-04-17 16:19 [PATCH v6 0/8] I3C mastership handover support Parshuram Thombare
` (2 preceding siblings ...)
2020-04-17 16:21 ` [PATCH v6 3/8] i3c: master: i3c mastership request and handover Parshuram Thombare
@ 2020-04-17 16:21 ` Parshuram Thombare
2020-04-17 16:22 ` [PATCH v6 5/8] i3c: master: check for non null pointer Parshuram Thombare
` (3 subsequent siblings)
7 siblings, 0 replies; 12+ messages in thread
From: Parshuram Thombare @ 2020-04-17 16:21 UTC (permalink / raw)
To: bbrezillon, vitor.soares
Cc: mparab, Parshuram Thombare, praneeth, linux-kernel, pgaj, linux-i3c
This patch add DEFSLVS processing code
to I3C master subsystem.
Signed-off-by: Parshuram Thombare <pthombar@cadence.com>
---
drivers/i3c/master.c | 200 +++++++++++++++++++++++++++++++++++++
include/linux/i3c/master.h | 6 ++
2 files changed, 206 insertions(+)
diff --git a/drivers/i3c/master.c b/drivers/i3c/master.c
index 3598856a0b25..2690910d724c 100644
--- a/drivers/i3c/master.c
+++ b/drivers/i3c/master.c
@@ -1830,6 +1830,206 @@ int i3c_master_acquire_bus(struct i3c_master_controller *master)
}
EXPORT_SYMBOL_GPL(i3c_master_acquire_bus);
+static struct i2c_dev_boardinfo *
+i3c_master_alloc_i2c_boardinfo(struct i3c_master_controller *master,
+ u16 addr, u8 lvr)
+{
+ struct i2c_dev_boardinfo *i2cboardinfo;
+
+ i2cboardinfo = kzalloc(sizeof(*i2cboardinfo), GFP_KERNEL);
+ if (!i2cboardinfo)
+ return ERR_PTR(-ENOMEM);
+
+ i2cboardinfo->base.addr = addr;
+ i2cboardinfo->lvr = lvr;
+
+ return i2cboardinfo;
+}
+
+static void
+i3c_master_copy_olddev(struct i3c_master_controller *master,
+ struct i3c_dev_desc *newdev,
+ struct i3c_dev_desc *olddev)
+{
+ struct i3c_ibi_setup ibireq = { };
+ bool enable_ibi = false;
+ int ret;
+
+ newdev->dev = olddev->dev;
+ if (newdev->dev)
+ newdev->dev->desc = newdev;
+
+ mutex_lock(&olddev->ibi_lock);
+ if (olddev->ibi) {
+ ibireq.handler = olddev->ibi->handler;
+ ibireq.max_payload_len = olddev->ibi->max_payload_len;
+ ibireq.num_slots = olddev->ibi->num_slots;
+
+ if (olddev->ibi->enabled) {
+ enable_ibi = true;
+ i3c_dev_disable_ibi_locked(olddev);
+ }
+
+ i3c_dev_free_ibi_locked(olddev);
+ }
+ mutex_unlock(&olddev->ibi_lock);
+
+ i3c_master_detach_i3c_dev(olddev);
+ i3c_master_free_i3c_dev(olddev);
+
+ if (ibireq.handler) {
+ mutex_lock(&newdev->ibi_lock);
+ ret = i3c_dev_request_ibi_locked(newdev, &ibireq);
+ if (ret) {
+ dev_err(&master->dev,
+ "Failed to request IBI on device %d-%llx",
+ master->bus.id, newdev->info.pid);
+ } else if (enable_ibi) {
+ ret = i3c_dev_enable_ibi_locked(newdev);
+ if (ret)
+ dev_err(&master->dev,
+ "Failed to re-enable IBI on device %d-%llx",
+ master->bus.id, newdev->info.pid);
+ }
+ mutex_unlock(&newdev->ibi_lock);
+ }
+}
+
+static int i3c_master_populate_bus(struct i3c_master_controller *master)
+{
+ struct i3c_ccc_dev_desc *desc;
+ struct i2c_dev_desc *i2cdev;
+ struct i2c_dev_boardinfo *info;
+ struct i3c_dev_desc *i3cdev, *olddev, *i3ctmp;
+ struct i3c_bus *i3cbus;
+ struct list_head i3c_old;
+ int slot, ret;
+
+ INIT_LIST_HEAD(&i3c_old);
+ i3cbus = i3c_master_get_bus(master);
+ list_add(&i3c_old, &i3cbus->devs.i3c);
+ list_del(&i3cbus->devs.i3c);
+ INIT_LIST_HEAD(&i3cbus->devs.i3c);
+ desc = master->defslvs_data.devs;
+ for (slot = 1; slot <= master->defslvs_data.ndevs; slot++, desc++) {
+ if (desc->static_addr && list_empty(&master->bus.devs.i2c)) {
+ i3c_bus_set_addr_slot_status(&master->bus,
+ desc->static_addr,
+ I3C_ADDR_SLOT_I2C_DEV);
+ info = i3c_master_alloc_i2c_boardinfo(master,
+ desc->static_addr,
+ desc->lvr);
+ if (IS_ERR(info)) {
+ ret = PTR_ERR(info);
+ goto err_detach_devs;
+ }
+
+ i2cdev = i3c_master_alloc_i2c_dev(master, info);
+ if (IS_ERR(i2cdev)) {
+ ret = PTR_ERR(i2cdev);
+ goto err_detach_devs;
+ }
+
+ ret = i3c_master_attach_i2c_dev(master, i2cdev);
+ if (ret) {
+ i3c_master_free_i2c_dev(i2cdev);
+ goto err_detach_devs;
+ }
+ } else {
+ struct i3c_device_info info = {
+ .dyn_addr = desc->dyn_addr
+ };
+
+ i3cdev = i3c_master_alloc_i3c_dev(master, &info);
+ if (IS_ERR(i3cdev)) {
+ ret = PTR_ERR(i3cdev);
+ goto err_detach_devs;
+ }
+
+ ret = i3c_master_attach_i3c_dev(master, i3cdev);
+ if (ret)
+ goto err_detach_devs;
+
+ ret = i3c_master_retrieve_dev_info(i3cdev);
+ if (ret)
+ goto err_detach_devs;
+
+ list_for_each_entry(olddev, &i3c_old, common.node) {
+ if (i3cdev != olddev &&
+ i3cdev->info.pid == olddev->info.pid)
+ i3c_master_copy_olddev(master, i3cdev,
+ olddev);
+ }
+ }
+ }
+
+ list_for_each_entry_safe(i3cdev, i3ctmp, &i3c_old,
+ common.node) {
+ i3c_master_detach_i3c_dev(i3cdev);
+ i3c_master_free_i3c_dev(i3cdev);
+ }
+
+ return 0;
+
+err_detach_devs:
+ if (!master->init_done) {
+ i3c_master_detach_free_devs(master);
+ } else {
+ INIT_LIST_HEAD(&i3cbus->devs.i3c);
+ list_add(&i3cbus->devs.i3c, &i3c_old);
+ list_del(&i3c_old);
+ }
+
+ return ret;
+}
+
+/* This function may sleep, so should not be called from atomic context */
+int i3c_master_process_defslvs(struct i3c_master_controller *master)
+{
+ struct i3c_bus *i3cbus;
+ int ret;
+
+ i3cbus = i3c_master_get_bus(master);
+
+ i3c_bus_normaluse_lock(&master->bus);
+ ret = i3c_master_acquire_bus(master);
+ i3c_bus_normaluse_unlock(&master->bus);
+ if (ret)
+ return ret;
+
+ /* Again bus_init to bus_mode, based on data received in DEFSLVS */
+ ret = i3c_bus_set_mode(i3cbus, master->defslvs_data.bus_mode);
+ if (ret)
+ return ret;
+
+ ret = master->ops->bus_init(master);
+ if (ret)
+ goto err_cleanup_bus;
+
+ if (!ret) {
+ master->ops->master_set_info(master);
+ i3c_bus_maintenance_lock(&master->bus);
+ ret = i3c_master_populate_bus(master);
+ if (ret) {
+ i3c_bus_maintenance_unlock(&master->bus);
+ goto err_cleanup_bus;
+ }
+ i3c_master_register_new_i3c_devs(master);
+ i3c_bus_maintenance_unlock(&master->bus);
+ }
+
+ if (master->init_done)
+ i3c_master_enable_mr_events(master);
+ return 0;
+
+err_cleanup_bus:
+ i3c_master_enable_mr_events(master);
+ if (master->ops->bus_cleanup)
+ master->ops->bus_cleanup(master);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(i3c_master_process_defslvs);
+
/**
* i3c_master_bus_init() - initialize an I3C bus
* @master: main master initializing the bus
diff --git a/include/linux/i3c/master.h b/include/linux/i3c/master.h
index c465c7792ccb..cc482934803b 100644
--- a/include/linux/i3c/master.h
+++ b/include/linux/i3c/master.h
@@ -520,6 +520,11 @@ struct i3c_master_controller {
struct completion mr_comp;
enum i3c_mr_state mr_state;
u8 mr_addr;
+ struct {
+ u32 ndevs;
+ enum i3c_bus_mode bus_mode;
+ struct i3c_ccc_dev_desc *devs;
+ } defslvs_data;
};
/**
@@ -544,6 +549,7 @@ struct i3c_master_controller {
#define i3c_bus_for_each_i3cdev(bus, dev) \
list_for_each_entry(dev, &(bus)->devs.i3c, common.node)
+int i3c_master_process_defslvs(struct i3c_master_controller *master);
void i3c_master_yield_bus(struct i3c_master_controller *master,
u8 slv_dyn_addr);
void i3c_sec_mst_mr_dis_event(struct i3c_master_controller *m);
--
2.17.1
_______________________________________________
linux-i3c mailing list
linux-i3c@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-i3c
^ permalink raw reply related [flat|nested] 12+ messages in thread
* [PATCH v6 5/8] i3c: master: check for non null pointer
2020-04-17 16:19 [PATCH v6 0/8] I3C mastership handover support Parshuram Thombare
` (3 preceding siblings ...)
2020-04-17 16:21 ` [PATCH v6 4/8] i3c: master: defslvs processing Parshuram Thombare
@ 2020-04-17 16:22 ` Parshuram Thombare
2020-04-17 16:22 ` [PATCH v6 6/8] i3c: master: secondary master initialization Parshuram Thombare
` (2 subsequent siblings)
7 siblings, 0 replies; 12+ messages in thread
From: Parshuram Thombare @ 2020-04-17 16:22 UTC (permalink / raw)
To: bbrezillon, vitor.soares
Cc: mparab, Parshuram Thombare, praneeth, linux-kernel, pgaj, linux-i3c
This patch add NULL check for struct i3c_generic_ibi_pool *pool
argument of function i3c_generic_ibi_free_pool.
Signed-off-by: Parshuram Thombare <pthombar@cadence.com>
---
drivers/i3c/master.c | 3 +++
1 file changed, 3 insertions(+)
diff --git a/drivers/i3c/master.c b/drivers/i3c/master.c
index 2690910d724c..19d4800ed573 100644
--- a/drivers/i3c/master.c
+++ b/drivers/i3c/master.c
@@ -2667,6 +2667,9 @@ void i3c_generic_ibi_free_pool(struct i3c_generic_ibi_pool *pool)
struct i3c_generic_ibi_slot *slot;
unsigned int nslots = 0;
+ if (!pool)
+ return;
+
while (!list_empty(&pool->free_slots)) {
slot = list_first_entry(&pool->free_slots,
struct i3c_generic_ibi_slot, node);
--
2.17.1
_______________________________________________
linux-i3c mailing list
linux-i3c@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-i3c
^ permalink raw reply related [flat|nested] 12+ messages in thread
* [PATCH v6 6/8] i3c: master: secondary master initialization
2020-04-17 16:19 [PATCH v6 0/8] I3C mastership handover support Parshuram Thombare
` (4 preceding siblings ...)
2020-04-17 16:22 ` [PATCH v6 5/8] i3c: master: check for non null pointer Parshuram Thombare
@ 2020-04-17 16:22 ` Parshuram Thombare
2020-04-17 16:22 ` [PATCH v6 7/8] i3c: master: added sysfs key i3c_acquire_bus Parshuram Thombare
2020-04-17 16:24 ` [PATCH v6 8/8] i3c: master: add mastership handover support to cdns i3c master driver Parshuram Thombare
7 siblings, 0 replies; 12+ messages in thread
From: Parshuram Thombare @ 2020-04-17 16:22 UTC (permalink / raw)
To: bbrezillon, vitor.soares
Cc: mparab, Parshuram Thombare, praneeth, linux-kernel, pgaj, linux-i3c
This patch add secondary master support to
I3C master subsystem.
Signed-off-by: Parshuram Thombare <pthombar@cadence.com>
---
drivers/i3c/master.c | 153 ++++++++++++++++++++++++++++---------
include/linux/i3c/master.h | 1 +
2 files changed, 116 insertions(+), 38 deletions(-)
diff --git a/drivers/i3c/master.c b/drivers/i3c/master.c
index 19d4800ed573..39a412b32c59 100644
--- a/drivers/i3c/master.c
+++ b/drivers/i3c/master.c
@@ -1621,10 +1621,6 @@ int i3c_master_set_info(struct i3c_master_controller *master,
if (!i3c_bus_dev_addr_is_avail(&master->bus, info->dyn_addr))
return -EINVAL;
- if (I3C_BCR_DEVICE_ROLE(info->bcr) == I3C_BCR_I3C_MASTER &&
- master->secondary)
- return -EINVAL;
-
if (master->this)
return -EINVAL;
@@ -2040,9 +2036,10 @@ EXPORT_SYMBOL_GPL(i3c_master_process_defslvs);
* 1. Attach I2C and statically defined I3C devs to the master so that the
* master can fill its internal device table appropriately
*
- * 2. Call &i3c_master_controller_ops->bus_init() method to initialize
- * the master controller. That's usually where the bus mode is selected
- * (pure bus or mixed fast/slow bus)
+ * 2. Should have called &i3c_master_controller_ops->bus_init()
+ * method with pure bus mode to initialize the master controller.
+ * That's usually where the bus mode is selected (pure bus or
+ * mixed fast/slow bus)
*
* 3. Instruct all devices on the bus to drop their dynamic address. This is
* particularly important when the bus was previously configured by someone
@@ -2126,14 +2123,6 @@ static int i3c_master_bus_init(struct i3c_master_controller *master)
}
}
- /*
- * Now execute the controller specific ->bus_init() routine, which
- * might configure its internal logic to match the bus limitations.
- */
- ret = master->ops->bus_init(master);
- if (ret)
- goto err_detach_devs;
-
ret = master->ops->master_set_info(master);
if (ret)
goto err_detach_devs;
@@ -2146,7 +2135,7 @@ static int i3c_master_bus_init(struct i3c_master_controller *master)
dev_err(&master->dev,
"master_set_info() was not called in ->bus_init()\n");
ret = -EINVAL;
- goto err_bus_cleanup;
+ goto err_detach_devs;
}
/*
@@ -2155,14 +2144,14 @@ static int i3c_master_bus_init(struct i3c_master_controller *master)
*/
ret = i3c_master_rstdaa_locked(master, I3C_BROADCAST_ADDR);
if (ret && ret != I3C_ERROR_M2)
- goto err_bus_cleanup;
+ goto err_detach_devs;
/* Disable all slave events before starting DAA. */
ret = i3c_master_disec_locked(master, I3C_BROADCAST_ADDR,
I3C_CCC_EVENT_SIR | I3C_CCC_EVENT_MR |
I3C_CCC_EVENT_HJ);
if (ret && ret != I3C_ERROR_M2)
- goto err_bus_cleanup;
+ goto err_detach_devs;
/*
* Pre-assign dynamic address and retrieve device information if
@@ -2180,10 +2169,6 @@ static int i3c_master_bus_init(struct i3c_master_controller *master)
err_rstdaa:
i3c_master_rstdaa_locked(master, I3C_BROADCAST_ADDR);
-err_bus_cleanup:
- if (master->ops->bus_cleanup)
- master->ops->bus_cleanup(master);
-
err_detach_devs:
i3c_master_detach_free_devs(master);
@@ -2813,9 +2798,68 @@ static int i3c_master_check_ops(const struct i3c_master_controller_ops *ops)
!ops->recycle_ibi_slot))
return -EINVAL;
+ if (ops->request_mastership &&
+ (!ops->enable_mr_events || !ops->disable_mr_events ||
+ !ops->check_event_set))
+ return -EINVAL;
+
return 0;
}
+static void i3c_secondary_master_register(struct work_struct *work)
+{
+ struct i3c_master_controller *master;
+ struct i3c_bus *i3cbus;
+ int ret;
+
+ master = container_of(work, struct i3c_master_controller,
+ sec_mst_register_work);
+ i3cbus = i3c_master_get_bus(master);
+
+ i3c_bus_normaluse_lock(&master->bus);
+ if (!master->ops->check_event_set(master, I3C_SLV_DEFSLVS_CCC) ||
+ i3c_master_acquire_bus(master)) {
+ i3c_bus_normaluse_unlock(&master->bus);
+ queue_work(master->wq, work);
+ return;
+ }
+ i3c_bus_normaluse_unlock(&master->bus);
+
+ ret = device_add(&master->dev);
+ if (ret)
+ goto err_cleanup_bus;
+
+ /*
+ * Expose our I3C bus as an I2C adapter so that I2C devices are exposed
+ * through the I2C subsystem.
+ */
+ ret = i3c_master_i2c_adapter_init(master);
+ if (ret)
+ goto err_del_dev;
+
+ /*
+ * We're done initializing the bus and the controller, we can now
+ * register I3C devices from defslvs list.
+ */
+ master->init_done = true;
+ i3c_bus_normaluse_lock(&master->bus);
+ i3c_master_register_new_i3c_devs(master);
+ i3c_bus_normaluse_unlock(&master->bus);
+
+ i3c_master_enable_mr_events(master);
+
+ return;
+
+err_del_dev:
+ device_del(&master->dev);
+
+err_cleanup_bus:
+ if (master->ops->bus_cleanup)
+ master->ops->bus_cleanup(master);
+
+ put_device(&master->dev);
+}
+
/**
* i3c_master_register() - register an I3C master
* @master: master used to send frames on the bus
@@ -2845,10 +2889,10 @@ int i3c_master_register(struct i3c_master_controller *master,
struct i3c_bus *i3cbus = i3c_master_get_bus(master);
enum i3c_bus_mode mode = I3C_BUS_MODE_PURE;
struct i2c_dev_boardinfo *i2cbi;
- int ret;
+ int ret, sz;
- /* We do not support secondary masters yet. */
- if (secondary)
+ /*Check if controller driver supports secondary masters. */
+ if (secondary && !ops->request_mastership)
return -ENOTSUPP;
ret = i3c_master_check_ops(ops);
@@ -2872,13 +2916,45 @@ int i3c_master_register(struct i3c_master_controller *master,
device_initialize(&master->dev);
dev_set_name(&master->dev, "i3c-%d", i3cbus->id);
+ master->wq = alloc_workqueue("%s", 0, 0, dev_name(parent));
+ if (!master->wq) {
+ ret = -ENOMEM;
+ goto err_put_dev;
+ }
+
+ master->mr_state = I3C_MR_IDLE;
INIT_WORK(&master->sec_mst_work, i3c_sec_mst_acquire_bus);
INIT_WORK(&master->mst_work, i3c_mst_yield_bus);
- ret = of_populate_i3c_bus(master);
+ ret = i3c_bus_set_mode(i3cbus, mode);
+ if (ret)
+ goto err_put_dev;
+
+ /*
+ * Now execute the controller specific ->bus_init() routine, which
+ * might configure its internal logic to match the bus limitations.
+ */
+ ret = master->ops->bus_init(master);
if (ret)
goto err_put_dev;
+ if (secondary) {
+ sz = sizeof(struct i3c_ccc_dev_desc) * I3C_BUS_MAX_DEVS;
+ master->defslvs_data.devs = devm_kzalloc(&master->dev, sz,
+ GFP_KERNEL);
+ if (!master->defslvs_data.devs)
+ goto err_put_dev;
+
+ INIT_WORK(&master->sec_mst_register_work,
+ i3c_secondary_master_register);
+ queue_work(master->wq, &master->sec_mst_register_work);
+ return 0;
+ }
+
+ ret = of_populate_i3c_bus(master);
+ if (ret)
+ goto err_cleanup_bus;
+
list_for_each_entry(i2cbi, &master->boardinfo.i2c, node) {
switch (i2cbi->lvr & I3C_LVR_I2C_INDEX_MASK) {
case I3C_LVR_I2C_INDEX(0):
@@ -2892,23 +2968,13 @@ int i3c_master_register(struct i3c_master_controller *master,
break;
default:
ret = -EINVAL;
- goto err_put_dev;
+ goto err_cleanup_bus;
}
}
- ret = i3c_bus_set_mode(i3cbus, mode);
- if (ret)
- goto err_put_dev;
-
- master->wq = alloc_workqueue("%s", 0, 0, dev_name(parent));
- if (!master->wq) {
- ret = -ENOMEM;
- goto err_put_dev;
- }
-
ret = i3c_master_bus_init(master);
if (ret)
- goto err_put_dev;
+ goto err_cleanup_bus;
ret = device_add(&master->dev);
if (ret)
@@ -2931,6 +2997,17 @@ int i3c_master_register(struct i3c_master_controller *master,
i3c_master_register_new_i3c_devs(master);
i3c_bus_normaluse_unlock(&master->bus);
+ ret = i3c_bus_set_mode(i3cbus, mode);
+ if (ret)
+ goto err_del_dev;
+
+ ret = master->ops->bus_init(master);
+ if (ret)
+ goto err_del_dev;
+
+ if (ops->request_mastership)
+ i3c_master_enable_mr_events(master);
+
return 0;
err_del_dev:
diff --git a/include/linux/i3c/master.h b/include/linux/i3c/master.h
index cc482934803b..08b63e285b7b 100644
--- a/include/linux/i3c/master.h
+++ b/include/linux/i3c/master.h
@@ -515,6 +515,7 @@ struct i3c_master_controller {
} boardinfo;
struct i3c_bus bus;
struct workqueue_struct *wq;
+ struct work_struct sec_mst_register_work;
struct work_struct mst_work;
struct work_struct sec_mst_work;
struct completion mr_comp;
--
2.17.1
_______________________________________________
linux-i3c mailing list
linux-i3c@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-i3c
^ permalink raw reply related [flat|nested] 12+ messages in thread
* [PATCH v6 7/8] i3c: master: added sysfs key i3c_acquire_bus
2020-04-17 16:19 [PATCH v6 0/8] I3C mastership handover support Parshuram Thombare
` (5 preceding siblings ...)
2020-04-17 16:22 ` [PATCH v6 6/8] i3c: master: secondary master initialization Parshuram Thombare
@ 2020-04-17 16:22 ` Parshuram Thombare
2020-04-17 16:24 ` [PATCH v6 8/8] i3c: master: add mastership handover support to cdns i3c master driver Parshuram Thombare
7 siblings, 0 replies; 12+ messages in thread
From: Parshuram Thombare @ 2020-04-17 16:22 UTC (permalink / raw)
To: bbrezillon, vitor.soares
Cc: mparab, Parshuram Thombare, praneeth, linux-kernel, pgaj, linux-i3c
This patch add sysfs key 'i3c_acquire_bus'
to acquire I3C bus.
Signed-off-by: Parshuram Thombare <pthombar@cadence.com>
---
drivers/i3c/master.c | 112 +++++++++++++++++++++++++------------------
1 file changed, 65 insertions(+), 47 deletions(-)
diff --git a/drivers/i3c/master.c b/drivers/i3c/master.c
index 39a412b32c59..c0b6a0c658f0 100644
--- a/drivers/i3c/master.c
+++ b/drivers/i3c/master.c
@@ -467,6 +467,53 @@ static const char * const i3c_bus_mode_strings[] = {
[I3C_BUS_MODE_MIXED_SLOW] = "mixed-slow",
};
+static int i3c_master_enable_mr_events(struct i3c_master_controller *master)
+{
+ int ret;
+
+ master->ops->enable_mr_events(master);
+ i3c_bus_maintenance_lock(&master->bus);
+ ret = i3c_master_enec_locked(master, I3C_BROADCAST_ADDR,
+ I3C_CCC_EVENT_MR | I3C_CCC_EVENT_HJ);
+ i3c_bus_maintenance_unlock(&master->bus);
+
+ return ret;
+}
+
+/* This function is expected to be called with normaluse_lock */
+int i3c_master_acquire_bus(struct i3c_master_controller *master)
+{
+ int ret = 0;
+
+ if (!master->this || master->this != master->bus.cur_master) {
+ if (master->mr_state == I3C_MR_IDLE) {
+ master->mr_state = I3C_MR_WAIT_DA;
+ init_completion(&master->mr_comp);
+ queue_work(master->wq, &master->sec_mst_work);
+ /*
+ * Bus acquire procedure may need write lock
+ * so release read lock before yielding
+ * to bus acquire state machine
+ */
+ i3c_bus_normaluse_unlock(&master->bus);
+ wait_for_completion(&master->mr_comp);
+ i3c_bus_normaluse_lock(&master->bus);
+ if (master->mr_state != I3C_MR_DONE)
+ ret = -EAGAIN;
+ master->mr_state = I3C_MR_IDLE;
+ } else {
+ /*
+ * MR request is already in process for
+ * this master
+ */
+ ret = -EAGAIN;
+ }
+ }
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(i3c_master_acquire_bus);
+
static ssize_t mode_show(struct device *dev,
struct device_attribute *da,
char *buf)
@@ -533,6 +580,23 @@ static ssize_t i2c_scl_frequency_show(struct device *dev,
}
static DEVICE_ATTR_RO(i2c_scl_frequency);
+static ssize_t i3c_acquire_bus_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct i3c_master_controller *master = dev_to_i3cmaster(dev);
+ int ret;
+
+ i3c_bus_normaluse_lock(&master->bus);
+ ret = i3c_master_acquire_bus(master);
+ i3c_bus_normaluse_unlock(&master->bus);
+ if (!ret)
+ i3c_master_enable_mr_events(master);
+
+ return ret ?: count;
+}
+static DEVICE_ATTR_WO(i3c_acquire_bus);
+
static struct attribute *i3c_masterdev_attrs[] = {
&dev_attr_mode.attr,
&dev_attr_current_master.attr,
@@ -543,6 +607,7 @@ static struct attribute *i3c_masterdev_attrs[] = {
&dev_attr_pid.attr,
&dev_attr_dynamic_address.attr,
&dev_attr_hdrcap.attr,
+ &dev_attr_i3c_acquire_bus.attr,
NULL,
};
ATTRIBUTE_GROUPS(i3c_masterdev);
@@ -1244,19 +1309,6 @@ static int i3c_master_get_accmst_locked(struct i3c_master_controller *master,
return ret;
}
-static int i3c_master_enable_mr_events(struct i3c_master_controller *master)
-{
- int ret;
-
- master->ops->enable_mr_events(master);
- i3c_bus_maintenance_lock(&master->bus);
- ret = i3c_master_enec_locked(master, I3C_BROADCAST_ADDR,
- I3C_CCC_EVENT_MR | I3C_CCC_EVENT_HJ);
- i3c_bus_maintenance_unlock(&master->bus);
-
- return ret;
-}
-
static int i3c_master_getaccmst(struct i3c_master_controller *master)
{
int ret;
@@ -1792,40 +1844,6 @@ void i3c_sec_mst_mr_dis_event(struct i3c_master_controller *m)
}
EXPORT_SYMBOL_GPL(i3c_sec_mst_mr_dis_event);
-/* This function is expected to be called with normaluse_lock */
-int i3c_master_acquire_bus(struct i3c_master_controller *master)
-{
- int ret = 0;
-
- if (!master->this || master->this != master->bus.cur_master) {
- if (master->mr_state == I3C_MR_IDLE) {
- master->mr_state = I3C_MR_WAIT_DA;
- init_completion(&master->mr_comp);
- queue_work(master->wq, &master->sec_mst_work);
- /*
- * Bus acquire procedure may need write lock
- * so release read lock before yielding
- * to bus acquire state machine
- */
- i3c_bus_normaluse_unlock(&master->bus);
- wait_for_completion(&master->mr_comp);
- i3c_bus_normaluse_lock(&master->bus);
- if (master->mr_state != I3C_MR_DONE)
- ret = -EAGAIN;
- master->mr_state = I3C_MR_IDLE;
- } else {
- /*
- * MR request is already in process for
- * this master
- */
- ret = -EAGAIN;
- }
- }
-
- return ret;
-}
-EXPORT_SYMBOL_GPL(i3c_master_acquire_bus);
-
static struct i2c_dev_boardinfo *
i3c_master_alloc_i2c_boardinfo(struct i3c_master_controller *master,
u16 addr, u8 lvr)
--
2.17.1
_______________________________________________
linux-i3c mailing list
linux-i3c@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-i3c
^ permalink raw reply related [flat|nested] 12+ messages in thread
* [PATCH v6 8/8] i3c: master: add mastership handover support to cdns i3c master driver
2020-04-17 16:19 [PATCH v6 0/8] I3C mastership handover support Parshuram Thombare
` (6 preceding siblings ...)
2020-04-17 16:22 ` [PATCH v6 7/8] i3c: master: added sysfs key i3c_acquire_bus Parshuram Thombare
@ 2020-04-17 16:24 ` Parshuram Thombare
7 siblings, 0 replies; 12+ messages in thread
From: Parshuram Thombare @ 2020-04-17 16:24 UTC (permalink / raw)
To: bbrezillon, vitor.soares
Cc: mparab, Parshuram Thombare, praneeth, linux-kernel, pgaj, linux-i3c
This patch add secondary master support to
Cadence's I3C master controller driver.
Signed-off-by: Parshuram Thombare <pthombar@cadence.com>
---
drivers/i3c/master.c | 32 ++-
drivers/i3c/master/i3c-master-cdns.c | 365 +++++++++++++++++++++++++--
2 files changed, 362 insertions(+), 35 deletions(-)
diff --git a/drivers/i3c/master.c b/drivers/i3c/master.c
index c0b6a0c658f0..c716c3461f7e 100644
--- a/drivers/i3c/master.c
+++ b/drivers/i3c/master.c
@@ -481,7 +481,7 @@ static int i3c_master_enable_mr_events(struct i3c_master_controller *master)
}
/* This function is expected to be called with normaluse_lock */
-int i3c_master_acquire_bus(struct i3c_master_controller *master)
+static int i3c_master_acquire_bus(struct i3c_master_controller *master)
{
int ret = 0;
@@ -512,7 +512,6 @@ int i3c_master_acquire_bus(struct i3c_master_controller *master)
return ret;
}
-EXPORT_SYMBOL_GPL(i3c_master_acquire_bus);
static ssize_t mode_show(struct device *dev,
struct device_attribute *da,
@@ -2526,12 +2525,19 @@ static int i3c_master_i2c_adapter_xfer(struct i2c_adapter *adap,
}
i3c_bus_normaluse_lock(&master->bus);
- dev = i3c_master_find_i2c_dev_by_addr(master, addr);
- if (!dev)
- ret = -ENOENT;
- else
- ret = master->ops->i2c_xfers(dev, xfers, nxfers);
- i3c_bus_normaluse_unlock(&master->bus);
+ if (master->ops->check_event_set(master, I3C_SLV_DEFSLVS_CCC) &&
+ !i3c_master_acquire_bus(master)) {
+ dev = i3c_master_find_i2c_dev_by_addr(master, addr);
+ if (!dev)
+ ret = -ENOENT;
+ else
+ ret = master->ops->i2c_xfers(dev, xfers, nxfers);
+ i3c_bus_normaluse_unlock(&master->bus);
+ i3c_master_enable_mr_events(master);
+ } else {
+ i3c_bus_normaluse_unlock(&master->bus);
+ ret = -EAGAIN;
+ }
return ret ? ret : nxfers;
}
@@ -3065,6 +3071,7 @@ int i3c_dev_do_priv_xfers_locked(struct i3c_dev_desc *dev,
int nxfers)
{
struct i3c_master_controller *master;
+ int ret;
if (!dev)
return -ENOENT;
@@ -3076,7 +3083,14 @@ int i3c_dev_do_priv_xfers_locked(struct i3c_dev_desc *dev,
if (!master->ops->priv_xfers)
return -ENOTSUPP;
- return master->ops->priv_xfers(dev, xfers, nxfers);
+ if (master->ops->check_event_set(master, I3C_SLV_DEFSLVS_CCC) &&
+ !i3c_master_acquire_bus(master)) {
+ ret = master->ops->priv_xfers(dev, xfers, nxfers);
+ i3c_master_enable_mr_events(master);
+ return ret;
+ } else {
+ return -EAGAIN;
+ }
}
int i3c_dev_disable_ibi_locked(struct i3c_dev_desc *dev)
diff --git a/drivers/i3c/master/i3c-master-cdns.c b/drivers/i3c/master/i3c-master-cdns.c
index c2d1631a9e38..1bc27f0de8ba 100644
--- a/drivers/i3c/master/i3c-master-cdns.c
+++ b/drivers/i3c/master/i3c-master-cdns.c
@@ -157,6 +157,7 @@
#define SLV_IMR 0x48
#define SLV_ICR 0x4c
#define SLV_ISR 0x50
+#define SLV_INT_DEFSLVS BIT(21)
#define SLV_INT_TM BIT(20)
#define SLV_INT_ERROR BIT(19)
#define SLV_INT_EVENT_UP BIT(18)
@@ -390,7 +391,14 @@ struct cdns_i3c_xfer {
struct cdns_i3c_master {
struct work_struct hj_work;
+ struct work_struct defslvs_work;
+ bool defslvs_processed;
+ bool mr_done;
struct i3c_master_controller base;
+ struct {
+ struct work_struct work;
+ u32 ibir;
+ } events;
u32 free_rr_slots;
unsigned int maxdevs;
struct {
@@ -936,6 +944,27 @@ static int cdns_i3c_master_get_rr_slot(struct cdns_i3c_master *master,
return -EINVAL;
}
+static int cdns_i3c_master_find_rr_slot(struct cdns_i3c_master *master,
+ u8 addr)
+{
+ u32 activedevs, rr;
+ int i;
+
+ activedevs = readl(master->regs + DEVS_CTRL) &
+ DEVS_CTRL_DEVS_ACTIVE_MASK;
+
+ for (i = 1; i <= master->maxdevs; i++) {
+ if (!(BIT(i) & activedevs))
+ continue;
+
+ rr = readl(master->regs + DEV_ID_RR0(i));
+ if (DEV_ID_RR0_GET_DEV_ADDR(rr) == addr)
+ return i;
+ }
+
+ return -EINVAL;
+}
+
static int cdns_i3c_master_reattach_i3c_dev(struct i3c_dev_desc *dev,
u8 old_dyn_addr)
{
@@ -955,7 +984,11 @@ static int cdns_i3c_master_attach_i3c_dev(struct i3c_dev_desc *dev)
if (!data)
return -ENOMEM;
- slot = cdns_i3c_master_get_rr_slot(master, dev->info.dyn_addr);
+ if (m->secondary)
+ slot = cdns_i3c_master_find_rr_slot(master, dev->info.dyn_addr);
+ else
+ slot = cdns_i3c_master_get_rr_slot(master, dev->info.dyn_addr);
+
if (slot < 0) {
kfree(data);
return slot;
@@ -998,7 +1031,12 @@ static int cdns_i3c_master_attach_i2c_dev(struct i2c_dev_desc *dev)
struct cdns_i3c_i2c_dev_data *data;
int slot;
- slot = cdns_i3c_master_get_rr_slot(master, 0);
+ if (m->secondary)
+ slot = cdns_i3c_master_find_rr_slot(master,
+ dev->boardinfo->base.addr);
+ else
+ slot = cdns_i3c_master_get_rr_slot(master, 0);
+
if (slot < 0)
return slot;
@@ -1010,14 +1048,17 @@ static int cdns_i3c_master_attach_i2c_dev(struct i2c_dev_desc *dev)
master->free_rr_slots &= ~BIT(slot);
i2c_dev_set_master_data(dev, data);
- writel(prepare_rr0_dev_address(dev->boardinfo->base.addr) |
- (dev->boardinfo->base.flags & I2C_CLIENT_TEN ?
- DEV_ID_RR0_LVR_EXT_ADDR : 0),
- master->regs + DEV_ID_RR0(data->id));
- writel(dev->boardinfo->lvr, master->regs + DEV_ID_RR2(data->id));
- writel(readl(master->regs + DEVS_CTRL) |
- DEVS_CTRL_DEV_ACTIVE(data->id),
- master->regs + DEVS_CTRL);
+ if (!m->secondary) {
+ writel(prepare_rr0_dev_address(dev->boardinfo->base.addr) |
+ (dev->boardinfo->base.flags & I2C_CLIENT_TEN ?
+ DEV_ID_RR0_LVR_EXT_ADDR : 0),
+ master->regs + DEV_ID_RR0(data->id));
+ writel(dev->boardinfo->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;
}
@@ -1187,10 +1228,6 @@ static int cdns_i3c_master_do_daa(struct i3c_master_controller *m)
cdns_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;
}
@@ -1287,8 +1324,8 @@ static int cdns_i3c_master_set_info(struct i3c_master_controller *m)
writel(prepare_rr0_dev_address(ret) | DEV_ID_RR0_IS_I3C,
master->regs + DEV_ID_RR0(0));
}
-
cdns_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);
@@ -1296,7 +1333,6 @@ static int cdns_i3c_master_set_info(struct i3c_master_controller *m)
if (ret)
return ret;
-
return 0;
}
@@ -1356,6 +1392,7 @@ static void cdns_i3c_master_handle_ibi(struct cdns_i3c_master *master,
static void cnds_i3c_master_demux_ibis(struct cdns_i3c_master *master)
{
+ struct i3c_dev_desc *dev;
u32 status0;
writel(MST_INT_IBIR_THR, master->regs + MST_ICR);
@@ -1377,27 +1414,120 @@ static void cnds_i3c_master_demux_ibis(struct cdns_i3c_master *master)
case IBIR_TYPE_MR:
WARN_ON(IBIR_XFER_BYTES(ibir) || (ibir & IBIR_ERROR));
+ if (ibir & IBIR_ACKED) {
+ dev = master->ibi.slots[IBIR_SLVID(ibir)];
+ i3c_master_yield_bus(&master->base,
+ dev->info.dyn_addr);
+ }
+ break;
+
default:
break;
}
}
}
+static void cdns_i3c_process_defslvs(struct cdns_i3c_master *master)
+{
+ enum i3c_bus_mode mode = I3C_BUS_MODE_PURE;
+ struct i3c_ccc_dev_desc *desc;
+ u32 devs, val, rr, slot;
+
+ desc = master->base.defslvs_data.devs;
+ master->base.defslvs_data.ndevs = 0;
+ devs = readl(master->regs + DEVS_CTRL) & DEVS_CTRL_DEVS_ACTIVE_MASK;
+ for (slot = 1; slot < I3C_BUS_MAX_DEVS; slot++) {
+ if (!(devs & BIT(slot)))
+ continue;
+
+ master->base.defslvs_data.ndevs++;
+ memset(desc, 0, sizeof(struct i3c_ccc_dev_desc));
+ val = readl(master->regs + DEV_ID_RR0(slot));
+ if (val & DEV_ID_RR0_IS_I3C) {
+ rr = readl(master->regs + DEV_ID_RR0(slot));
+ desc->dyn_addr = DEV_ID_RR0_GET_DEV_ADDR(rr);
+ rr = readl(master->regs + DEV_ID_RR2(slot));
+ desc->dcr = rr;
+ desc->bcr = rr >> 8;
+ } else {
+ rr = readl(master->regs + DEV_ID_RR0(slot));
+ desc->static_addr = DEV_ID_RR0_GET_DEV_ADDR(rr);
+ rr = readl(master->regs + DEV_ID_RR2(slot));
+ desc->lvr = rr;
+ switch (desc->lvr & I3C_LVR_I2C_INDEX_MASK) {
+ case I3C_LVR_I2C_INDEX(0):
+ if (mode < I3C_BUS_MODE_MIXED_FAST)
+ mode = I3C_BUS_MODE_MIXED_FAST;
+ break;
+ case I3C_LVR_I2C_INDEX(1):
+ case I3C_LVR_I2C_INDEX(2):
+ if (mode < I3C_BUS_MODE_MIXED_SLOW)
+ mode = I3C_BUS_MODE_MIXED_SLOW;
+ break;
+ default:
+ break;
+ }
+ }
+ desc++;
+ }
+ master->base.defslvs_data.bus_mode = mode;
+}
+
+void cdns_i3c_handle_slv_events(struct cdns_i3c_master *master, u32 events)
+{
+ u32 status1;
+
+ status1 = readl(master->regs + SLV_STATUS1);
+
+ if (events & SLV_INT_EVENT_UP && status1 & SLV_STATUS1_MR_DIS)
+ i3c_sec_mst_mr_dis_event(&master->base);
+
+ if (events & SLV_INT_MR_DONE) {
+ writel(FLUSH_RX_FIFO | FLUSH_TX_FIFO | FLUSH_CMD_FIFO |
+ FLUSH_CMD_RESP,
+ master->regs + FLUSH_CTRL);
+ master->mr_done = true;
+ }
+
+ if (events & SLV_INT_DEFSLVS) {
+ master->defslvs_processed = false;
+ queue_work(master->base.wq, &master->defslvs_work);
+ }
+}
+
static irqreturn_t cdns_i3c_master_interrupt(int irq, void *data)
{
struct cdns_i3c_master *master = data;
u32 status;
- status = readl(master->regs + MST_ISR);
- if (!(status & readl(master->regs + MST_IMR)))
- return IRQ_NONE;
+ if (!master->base.this ||
+ master->base.this != master->base.bus.cur_master) {
+ status = (readl(master->regs + SLV_ISR) &
+ readl(master->regs + SLV_IMR));
+ if (!status)
+ return IRQ_NONE;
+ writel(status, master->regs + SLV_ICR);
+
+ cdns_i3c_handle_slv_events(master, status);
+
+ } else {
+ status = readl(master->regs + MST_ISR);
+ if (!(status & readl(master->regs + MST_IMR)))
+ return IRQ_NONE;
+
+ spin_lock(&master->xferqueue.lock);
+ cdns_i3c_master_end_xfer_locked(master, status);
+ spin_unlock(&master->xferqueue.lock);
- spin_lock(&master->xferqueue.lock);
- cdns_i3c_master_end_xfer_locked(master, status);
- spin_unlock(&master->xferqueue.lock);
+ if (status & MST_INT_IBIR_THR)
+ cnds_i3c_master_demux_ibis(master);
- if (status & MST_INT_IBIR_THR)
- cnds_i3c_master_demux_ibis(master);
+ if (status & MST_INT_MR_DONE) {
+ writel(MST_INT_MR_DONE, master->regs + MST_ICR);
+ writel(FLUSH_RX_FIFO | FLUSH_TX_FIFO | FLUSH_CMD_FIFO |
+ FLUSH_CMD_RESP, master->regs + FLUSH_CTRL);
+ }
+ }
return IRQ_HANDLED;
}
@@ -1521,6 +1651,161 @@ static void cdns_i3c_master_recycle_ibi_slot(struct i3c_dev_desc *dev,
i3c_generic_ibi_recycle_slot(data->ibi_pool, slot);
}
+static int cdns_i3c_master_find_ibi_slot(struct cdns_i3c_master *master,
+ struct i3c_dev_desc *dev,
+ s16 *slot)
+{
+ unsigned long flags;
+ unsigned int i;
+ int ret = -ENOENT;
+
+ spin_lock_irqsave(&master->ibi.lock, flags);
+ for (i = 0; i < master->ibi.num_slots; i++) {
+ if (master->ibi.slots[i] == dev) {
+ *slot = i;
+ ret = 0;
+ break;
+ }
+ }
+
+ if (ret) {
+ for (i = 0; i < master->ibi.num_slots; i++) {
+ if (!master->ibi.slots[i]) {
+ master->ibi.slots[i] = dev;
+ *slot = i;
+ ret = 0;
+ break;
+ }
+ }
+ }
+ spin_unlock_irqrestore(&master->ibi.lock, flags);
+
+ return ret;
+}
+
+static int cdns_i3c_request_mastership(struct i3c_master_controller *m)
+{
+ struct cdns_i3c_master *master = to_cdns_i3c_master(m);
+ int status;
+
+ status = readl(master->regs + SLV_STATUS1);
+
+ if (status & SLV_STATUS1_MR_DIS)
+ return -EACCES;
+
+ master->mr_done = false;
+ writel(readl(master->regs + CTRL) | CTRL_MST_INIT | CTRL_MST_ACK,
+ master->regs + CTRL);
+
+ return 0;
+}
+
+static void
+cdns_i3c_master_disable_mastership_events(struct i3c_master_controller *m)
+{
+ struct cdns_i3c_master *master = to_cdns_i3c_master(m);
+ struct cdns_i3c_i2c_dev_data *data;
+ struct i3c_dev_desc *i3cdev;
+ unsigned long flags;
+ u32 sirmap;
+
+ i3c_bus_for_each_i3cdev(&m->bus, i3cdev) {
+ if (I3C_BCR_DEVICE_ROLE(i3cdev->info.bcr) !=
+ I3C_BCR_I3C_MASTER ||
+ m->this == i3cdev)
+ continue;
+
+ data = i3c_dev_get_master_data(i3cdev);
+
+ if (i3cdev->ibi && i3cdev->ibi->handler)
+ continue;
+
+ 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);
+
+ cdns_i3c_master_free_ibi(i3cdev);
+ }
+}
+
+static void
+cdns_i3c_master_enable_mastership_events(struct i3c_master_controller *m)
+{
+ struct cdns_i3c_master *master = to_cdns_i3c_master(m);
+ struct cdns_i3c_i2c_dev_data *data;
+ struct i3c_dev_desc *i3cdev;
+ unsigned long flags;
+ u32 sircfg, sirmap;
+ int ret;
+
+ i3c_bus_for_each_i3cdev(&m->bus, i3cdev) {
+ if (I3C_BCR_DEVICE_ROLE(i3cdev->info.bcr) !=
+ I3C_BCR_I3C_MASTER ||
+ m->this == i3cdev)
+ continue;
+
+ data = i3c_dev_get_master_data(i3cdev);
+ if (!data)
+ continue;
+
+ ret = cdns_i3c_master_find_ibi_slot(master, i3cdev, &data->ibi);
+ if (ret)
+ continue;
+
+ 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(i3cdev->info.bcr >> 6) |
+ SIR_MAP_DEV_DA(i3cdev->info.dyn_addr) |
+ SIR_MAP_DEV_PL(i3cdev->info.max_ibi_len) |
+ SIR_MAP_DEV_ACK;
+
+ if (i3cdev->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);
+ }
+}
+
+static bool
+cdns_i3c_master_check_event_set(struct i3c_master_controller *m,
+ enum i3c_event event)
+{
+ struct cdns_i3c_master *master = to_cdns_i3c_master(m);
+ bool ret = false;
+
+ switch (event) {
+ case I3C_SLV_DA_UPDATE:
+ if (readl(master->regs + SLV_STATUS1) & SLV_STATUS1_HAS_DA)
+ ret = true;
+ break;
+
+ case I3C_SLV_DEFSLVS_CCC:
+ ret = master->defslvs_processed;
+ break;
+
+ case I3C_SLV_MR_DIS:
+ if (readl(master->regs + SLV_STATUS1) & SLV_STATUS1_MR_DIS)
+ ret = true;
+ break;
+
+ case I3C_SLV_MR_DONE:
+ ret = master->mr_done;
+ break;
+
+ default:
+ break;
+ }
+
+ return ret;
+}
+
static const struct i3c_master_controller_ops cdns_i3c_master_ops = {
.bus_init = cdns_i3c_master_bus_init,
.master_set_info = cdns_i3c_master_set_info,
@@ -1541,6 +1826,10 @@ static const struct i3c_master_controller_ops cdns_i3c_master_ops = {
.request_ibi = cdns_i3c_master_request_ibi,
.free_ibi = cdns_i3c_master_free_ibi,
.recycle_ibi_slot = cdns_i3c_master_recycle_ibi_slot,
+ .request_mastership = cdns_i3c_request_mastership,
+ .enable_mr_events = cdns_i3c_master_enable_mastership_events,
+ .disable_mr_events = cdns_i3c_master_disable_mastership_events,
+ .check_event_set = cdns_i3c_master_check_event_set,
};
static void cdns_i3c_master_hj(struct work_struct *work)
@@ -1552,10 +1841,24 @@ static void cdns_i3c_master_hj(struct work_struct *work)
i3c_master_do_daa(&master->base);
}
+static void cdns_i3c_sec_master_defslvs(struct work_struct *work)
+{
+ struct cdns_i3c_master *master = container_of(work,
+ struct cdns_i3c_master,
+ defslvs_work);
+
+ cdns_i3c_process_defslvs(master);
+ if (i3c_master_process_defslvs(&master->base))
+ queue_work(master->base.wq, work);
+ else
+ master->defslvs_processed = true;
+}
+
static int cdns_i3c_master_probe(struct platform_device *pdev)
{
struct cdns_i3c_master *master;
struct resource *res;
+ bool secondary;
int ret, irq;
u32 val;
@@ -1607,6 +1910,7 @@ static int cdns_i3c_master_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, master);
val = readl(master->regs + CONF_STATUS0);
+ secondary = (val & CONF_STATUS0_SEC_MASTER) ? true : false;
/* Device ID0 is reserved to describe this master. */
master->maxdevs = CONF_STATUS0_DEVS_NUM(val);
@@ -1627,12 +1931,21 @@ static int cdns_i3c_master_probe(struct platform_device *pdev)
if (!master->ibi.slots)
goto err_disable_sysclk;
+ if (secondary) {
+ INIT_WORK(&master->defslvs_work, cdns_i3c_sec_master_defslvs);
+ master->defslvs_processed = false;
+ } else {
+ master->defslvs_processed = true;
+ }
+
+ writel(SLV_INT_EVENT_UP | SLV_INT_DEFSLVS | SLV_INT_MR_DONE,
+ master->regs + SLV_IER);
writel(IBIR_THR(1), master->regs + CMD_IBI_THR_CTRL);
- writel(MST_INT_IBIR_THR, master->regs + MST_IER);
+ writel(MST_INT_IBIR_THR | MST_INT_MR_DONE, master->regs + MST_IER);
writel(DEVS_CTRL_DEV_CLR_ALL, master->regs + DEVS_CTRL);
ret = i3c_master_register(&master->base, &pdev->dev,
- &cdns_i3c_master_ops, false);
+ &cdns_i3c_master_ops, secondary);
if (ret)
goto err_disable_sysclk;
--
2.17.1
_______________________________________________
linux-i3c mailing list
linux-i3c@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-i3c
^ permalink raw reply related [flat|nested] 12+ messages in thread