2024-02-15 22:08:55

by Eddie James

[permalink] [raw]
Subject: [PATCH 00/33] fsi: Interrupt support

This series primarily adds interrupt support to the FSI driver subsystem.
The series first improves the clocking model in the FSI core to provide
real clock rates to the engine drivers. Then there are various quality,
trace, and organizational improvements.
Another substantial part of the series is to make some master code common
through the use of a regmap to access master structures. This will prove
more useful as additional FSI master drivers are added.
Finally, interrupt support is added to the I2C driver as an alternative to
polling.

Eddie James (33):
dt-bindings: clock: ast2600: Add FSI clock
clk: ast2600: Add FSI parent clock with correct rate
fsi: Move slave definitions to fsi-slave.h
fsi: Improve master indexing
fsi: Use a defined value for default echo delay
fsi: Expose master-specific local bus clock divider
ARM: dts: aspeed: p10 and tacoma: Set FSI clock frequency
fsi: core: Improve master read/write/error traces
fsi: core: Add slave error trace
fsi: aspeed: Add AST2700 support
fsi: core: Add slave spinlock
fsi: core: Allow cfam device type aliases
fsi: core: Add common regmap master functions
fsi: hub: Use common initialization and link enable
fsi: aspeed: Use common initialization and link enable
fsi: aspeed: Remove cfam reset sysfs file in error path and remove
fsi: aspeed: Refactor trace functions
fsi: aspeed: Don't clear all IRQs during OPB transfers
fsi: aspeed: Only read result register for successful read
fsi: aspeed: Switch to spinlock
fsi: aspeed: Disable relative addressing and IPOLL for cfam reset
fsi: aspeed: Use common master error handler
fsi: core: Add interrupt support
fsi: aspeed: Add interrupt support
fsi: hub: Add interrupt support
ARM: dts: aspeed: FSI interrupt support
i2c: fsi: Calculate clock divider from local bus frequency
i2c: fsi: Improve formatting
i2c: fsi: Change fsi_i2c_write_reg to accept data instead of a pointer
i2c: fsi: Remove list structure of ports
i2c: fsi: Define a function to check status error bits
i2c: fsi: Add boolean for skip stop command on abort
i2c: fsi: Add interrupt support

.../boot/dts/aspeed/aspeed-bmc-opp-tacoma.dts | 1 +
arch/arm/boot/dts/aspeed/aspeed-g6.dtsi | 4 +
.../arm/boot/dts/aspeed/ibm-power10-dual.dtsi | 3 +
drivers/clk/clk-ast2600.c | 7 +-
drivers/fsi/Kconfig | 2 +
drivers/fsi/fsi-core.c | 528 ++++++++++++++----
drivers/fsi/fsi-master-aspeed.c | 391 +++++++------
drivers/fsi/fsi-master-hub.c | 231 ++++----
drivers/fsi/fsi-master.h | 27 +
drivers/fsi/fsi-slave.h | 89 +++
drivers/i2c/busses/i2c-fsi.c | 464 ++++++++++-----
include/dt-bindings/clock/ast2600-clock.h | 1 +
include/linux/fsi.h | 3 +
include/trace/events/fsi.h | 190 ++++---
include/trace/events/fsi_master_aspeed.h | 86 ++-
include/trace/events/i2c_fsi.h | 45 ++
16 files changed, 1432 insertions(+), 640 deletions(-)
create mode 100644 include/trace/events/i2c_fsi.h

--
2.39.3



2024-02-15 22:09:03

by Eddie James

[permalink] [raw]
Subject: [PATCH 01/33] dt-bindings: clock: ast2600: Add FSI clock

Add a definition for the FSI clock.

Signed-off-by: Eddie James <[email protected]>
---
include/dt-bindings/clock/ast2600-clock.h | 1 +
1 file changed, 1 insertion(+)

diff --git a/include/dt-bindings/clock/ast2600-clock.h b/include/dt-bindings/clock/ast2600-clock.h
index 712782177c90..7ae96c7bd72f 100644
--- a/include/dt-bindings/clock/ast2600-clock.h
+++ b/include/dt-bindings/clock/ast2600-clock.h
@@ -86,6 +86,7 @@
#define ASPEED_CLK_MAC3RCLK 69
#define ASPEED_CLK_MAC4RCLK 70
#define ASPEED_CLK_I3C 71
+#define ASPEED_CLK_FSI 72

/* Only list resets here that are not part of a clock gate + reset pair */
#define ASPEED_RESET_ADC 55
--
2.39.3


2024-02-15 22:09:11

by Eddie James

[permalink] [raw]
Subject: [PATCH 12/33] fsi: core: Allow cfam device type aliases

Other FSI devices can uses aliases for the device numbering, so
modify the function to get a new minor to allow the cfam type
to use aliases too.

Signed-off-by: Eddie James <[email protected]>
---
drivers/fsi/fsi-core.c | 59 +++++++++++++++++++++---------------------
1 file changed, 29 insertions(+), 30 deletions(-)

diff --git a/drivers/fsi/fsi-core.c b/drivers/fsi/fsi-core.c
index eb15e5f5a2ee..95f6ce81f8f4 100644
--- a/drivers/fsi/fsi-core.c
+++ b/drivers/fsi/fsi-core.c
@@ -884,12 +884,37 @@ static int fsi_adjust_index(int index)
#endif
}

-static int __fsi_get_new_minor(struct fsi_slave *slave, enum fsi_dev_type type,
- dev_t *out_dev, int *out_index)
+static const char *const fsi_dev_type_names[] = {
+ "cfam",
+ "sbefifo",
+ "scom",
+ "occ",
+};
+
+static int __fsi_get_new_minor(struct fsi_slave *slave, struct device_node *np,
+ enum fsi_dev_type type, dev_t *out_dev, int *out_index)
{
int cid = slave->chip_id;
int id;

+ if (np) {
+ int aid = of_alias_get_id(np, fsi_dev_type_names[type]);
+
+ if (aid >= 0) {
+ /* Use the same scheme as the legacy numbers. */
+ id = (aid << 2) | type;
+ id = ida_alloc_range(&fsi_minor_ida, id, id, GFP_KERNEL);
+ if (id >= 0) {
+ *out_index = aid;
+ *out_dev = fsi_base_dev + id;
+ return 0;
+ }
+
+ if (id != -ENOSPC)
+ return id;
+ }
+ }
+
/* Check if we qualify for legacy numbering */
if (cid >= 0 && cid < 16 && type < 4) {
/*
@@ -918,36 +943,10 @@ static int __fsi_get_new_minor(struct fsi_slave *slave, enum fsi_dev_type type,
return 0;
}

-static const char *const fsi_dev_type_names[] = {
- "cfam",
- "sbefifo",
- "scom",
- "occ",
-};
-
int fsi_get_new_minor(struct fsi_device *fdev, enum fsi_dev_type type,
dev_t *out_dev, int *out_index)
{
- if (fdev->dev.of_node) {
- int aid = of_alias_get_id(fdev->dev.of_node, fsi_dev_type_names[type]);
-
- if (aid >= 0) {
- /* Use the same scheme as the legacy numbers. */
- int id = (aid << 2) | type;
-
- id = ida_alloc_range(&fsi_minor_ida, id, id, GFP_KERNEL);
- if (id >= 0) {
- *out_index = aid;
- *out_dev = fsi_base_dev + id;
- return 0;
- }
-
- if (id != -ENOSPC)
- return id;
- }
- }
-
- return __fsi_get_new_minor(fdev->slave, type, out_dev, out_index);
+ return __fsi_get_new_minor(fdev->slave, fdev->dev.of_node, type, out_dev, out_index);
}
EXPORT_SYMBOL_GPL(fsi_get_new_minor);

@@ -1052,7 +1051,7 @@ static int fsi_slave_init(struct fsi_master *master, int link, uint8_t id)
}

/* Allocate a minor in the FSI space */
- rc = __fsi_get_new_minor(slave, fsi_dev_cfam, &slave->dev.devt,
+ rc = __fsi_get_new_minor(slave, slave->dev.of_node, fsi_dev_cfam, &slave->dev.devt,
&slave->cdev_idx);
if (rc)
goto err_free;
--
2.39.3


2024-02-15 22:09:36

by Eddie James

[permalink] [raw]
Subject: [PATCH 10/33] fsi: aspeed: Add AST2700 support

AST2700 requires a few bits set differently in the OPB retry
counter register, so add some match data and set the register
accordingly.

Signed-off-by: Eddie James <[email protected]>
---
drivers/fsi/fsi-master-aspeed.c | 28 +++++++++++++++++++++++++---
1 file changed, 25 insertions(+), 3 deletions(-)

diff --git a/drivers/fsi/fsi-master-aspeed.c b/drivers/fsi/fsi-master-aspeed.c
index f0a19cd451a0..d6e923b8f501 100644
--- a/drivers/fsi/fsi-master-aspeed.c
+++ b/drivers/fsi/fsi-master-aspeed.c
@@ -18,6 +18,10 @@

#include "fsi-master.h"

+struct fsi_master_aspeed_data {
+ u32 opb_retry_counter;
+};
+
struct fsi_master_aspeed {
struct fsi_master master;
struct mutex lock; /* protect HW access */
@@ -60,6 +64,8 @@ static const u32 fsi_base = 0xa0000000;
#define OPB1_READ_ORDER2 0x60

#define OPB_RETRY_COUNTER 0x64
+#define OPB_RETRY_COUNTER_AST2600 0x00000010
+#define OPB_RETRY_COUNTER_AST2700 0x000c0010

/* OPBn_STATUS */
#define STATUS_HALFWORD_ACK BIT(0)
@@ -536,6 +542,8 @@ static int tacoma_cabled_fsi_fixup(struct device *dev)

static int fsi_master_aspeed_probe(struct platform_device *pdev)
{
+ const struct fsi_master_aspeed_data *md = of_device_get_match_data(&pdev->dev);
+ u32 opb_retry_counter = md ? md->opb_retry_counter : OPB_RETRY_COUNTER_AST2600;
struct fsi_master_aspeed *aspeed;
int rc, links, reg;
__be32 raw;
@@ -579,8 +587,7 @@ static int fsi_master_aspeed_probe(struct platform_device *pdev)
writel(OPB1_XFER_ACK_EN | OPB0_XFER_ACK_EN,
aspeed->base + OPB_IRQ_MASK);

- /* TODO: determine an appropriate value */
- writel(0x10, aspeed->base + OPB_RETRY_COUNTER);
+ writel(opb_retry_counter, aspeed->base + OPB_RETRY_COUNTER);

writel(ctrl_base, aspeed->base + OPB_CTRL_BASE);
writel(fsi_base, aspeed->base + OPB_FSI_BASE);
@@ -656,8 +663,23 @@ static int fsi_master_aspeed_remove(struct platform_device *pdev)
return 0;
}

+static const struct fsi_master_aspeed_data fsi_master_ast2600_data = {
+ .opb_retry_counter = OPB_RETRY_COUNTER_AST2600,
+};
+
+static const struct fsi_master_aspeed_data fsi_master_ast2700_data = {
+ .opb_retry_counter = OPB_RETRY_COUNTER_AST2700,
+};
+
static const struct of_device_id fsi_master_aspeed_match[] = {
- { .compatible = "aspeed,ast2600-fsi-master" },
+ {
+ .compatible = "aspeed,ast2600-fsi-master",
+ .data = &fsi_master_ast2600_data,
+ },
+ {
+ .compatible = "aspeed,ast2700-fsi-master",
+ .data = &fsi_master_ast2700_data,
+ },
{ },
};
MODULE_DEVICE_TABLE(of, fsi_master_aspeed_match);
--
2.39.3


2024-02-15 22:10:39

by Eddie James

[permalink] [raw]
Subject: [PATCH 09/33] fsi: core: Add slave error trace

Trace out the FSI slave status and interrupt status.

Signed-off-by: Eddie James <[email protected]>
---
drivers/fsi/fsi-core.c | 1 +
include/trace/events/fsi.h | 19 +++++++++++++++++++
2 files changed, 20 insertions(+)

diff --git a/drivers/fsi/fsi-core.c b/drivers/fsi/fsi-core.c
index a989a2007c0f..e6ed2d0773b6 100644
--- a/drivers/fsi/fsi-core.c
+++ b/drivers/fsi/fsi-core.c
@@ -185,6 +185,7 @@ static int fsi_slave_report_and_clear_errors(struct fsi_slave *slave)

dev_dbg(&slave->dev, "status: 0x%08x, sisc: 0x%08x\n",
be32_to_cpu(stat), be32_to_cpu(irq));
+ trace_fsi_slave_error(slave, be32_to_cpu(irq), be32_to_cpu(stat));

/* clear interrupts */
return fsi_master_write(master, link, id, FSI_SLAVE_BASE + FSI_SISC,
diff --git a/include/trace/events/fsi.h b/include/trace/events/fsi.h
index fed8835f438e..5509afc98ee8 100644
--- a/include/trace/events/fsi.h
+++ b/include/trace/events/fsi.h
@@ -115,6 +115,25 @@ TRACE_EVENT(fsi_master_unregister,
TP_printk("fsi%d (%d links)", __entry->master_idx, __entry->n_links)
);

+TRACE_EVENT(fsi_slave_error,
+ TP_PROTO(const struct fsi_slave *slave, uint32_t sisc, uint32_t sstat),
+ TP_ARGS(slave, sisc, sstat),
+ TP_STRUCT__entry(
+ __field(int, master_idx)
+ __field(int, link)
+ __field(uint32_t, sisc)
+ __field(uint32_t, sstat)
+ ),
+ TP_fast_assign(
+ __entry->master_idx = slave->master->idx;
+ __entry->link = slave->link;
+ __entry->sisc = sisc;
+ __entry->sstat = sstat;
+ ),
+ TP_printk("fsi%d:%02d sisc:%08x sstat:%08x", __entry->master_idx, __entry->link,
+ __entry->sisc, __entry->sstat)
+);
+
TRACE_EVENT(fsi_slave_init,
TP_PROTO(const struct fsi_slave *slave),
TP_ARGS(slave),
--
2.39.3


2024-02-15 22:10:41

by Eddie James

[permalink] [raw]
Subject: [PATCH 18/33] fsi: aspeed: Don't clear all IRQs during OPB transfers

In order to support FSI interrupts, the OPB transfer functions should
not clear all the IRQs pending. Instead, just write the OPB ACK bit
to the IRQ status register. As commented, this register invisibly
masks the interrupt once the interrupt condition is cleared. Fix this
by writing 0 before each OPB transfer.

Signed-off-by: Eddie James <[email protected]>
---
drivers/fsi/fsi-master-aspeed.c | 17 +++++++++++------
1 file changed, 11 insertions(+), 6 deletions(-)

diff --git a/drivers/fsi/fsi-master-aspeed.c b/drivers/fsi/fsi-master-aspeed.c
index 1a91f3acdfcc..64a5407a15ec 100644
--- a/drivers/fsi/fsi-master-aspeed.c
+++ b/drivers/fsi/fsi-master-aspeed.c
@@ -46,6 +46,11 @@ static const u32 fsi_base = 0xa0000000;
#define OPB_CLK_SYNC 0x3c
#define OPB_IRQ_CLEAR 0x40
#define OPB_IRQ_MASK 0x44
+/*
+ * This register does NOT behave in the expected manner. It is expected that writing 1b would clear
+ * the corresponding interrupt condition. However it also invisibly masks the interrupt! Writing 0b
+ * unmasks again.
+ */
#define OPB_IRQ_STATUS 0x48

#define OPB0_SELECT 0x10
@@ -64,8 +69,8 @@ static const u32 fsi_base = 0xa0000000;
#define OPB1_READ_ORDER2 0x60

#define OPB_RETRY_COUNTER 0x64
-#define OPB_RETRY_COUNTER_AST2600 0x00000010
-#define OPB_RETRY_COUNTER_AST2700 0x000c0010
+#define OPB_RETRY_COUNTER_AST2600 0x00010010
+#define OPB_RETRY_COUNTER_AST2700 0x000d0010

/* OPBn_STATUS */
#define STATUS_HALFWORD_ACK BIT(0)
@@ -107,13 +112,14 @@ static int __opb_write(struct fsi_master_aspeed *aspeed, u32 addr,
writel_relaxed(transfer_size, base + OPB0_XFER_SIZE);
writel_relaxed(addr, base + OPB0_FSI_ADDR);
writel_relaxed(val, base + OPB0_FSI_DATA_W);
- writel_relaxed(0x1, base + OPB_IRQ_CLEAR);
+ writel_relaxed(0, base + OPB_IRQ_STATUS);
writel(0x1, base + OPB_TRIGGER);

ret = readl_poll_timeout(base + OPB_IRQ_STATUS, reg,
(reg & OPB0_XFER_ACK_EN) != 0,
0, OPB_POLL_TIMEOUT);

+ writel(OPB0_XFER_ACK_EN, base + OPB_IRQ_STATUS);
status = readl(base + OPB0_STATUS);

/* Return error when poll timed out */
@@ -159,13 +165,14 @@ static int __opb_read(struct fsi_master_aspeed *aspeed, uint32_t addr,
writel_relaxed(CMD_READ, base + OPB0_RW);
writel_relaxed(transfer_size, base + OPB0_XFER_SIZE);
writel_relaxed(addr, base + OPB0_FSI_ADDR);
- writel_relaxed(0x1, base + OPB_IRQ_CLEAR);
+ writel_relaxed(0, aspeed->base + OPB_IRQ_STATUS);
writel(0x1, base + OPB_TRIGGER);

ret = readl_poll_timeout(base + OPB_IRQ_STATUS, reg,
(reg & OPB0_XFER_ACK_EN) != 0,
0, OPB_POLL_TIMEOUT);

+ writel(OPB0_XFER_ACK_EN, base + OPB_IRQ_STATUS);
status = readl(base + OPB0_STATUS);

result = readl(base + OPB0_FSI_DATA_R);
@@ -489,8 +496,6 @@ static int fsi_master_aspeed_probe(struct platform_device *pdev)
}

writel(0x1, aspeed->base + OPB_CLK_SYNC);
- writel(OPB1_XFER_ACK_EN | OPB0_XFER_ACK_EN,
- aspeed->base + OPB_IRQ_MASK);

writel(opb_retry_counter, aspeed->base + OPB_RETRY_COUNTER);

--
2.39.3


2024-02-15 22:10:57

by Eddie James

[permalink] [raw]
Subject: [PATCH 21/33] fsi: aspeed: Disable relative addressing and IPOLL for cfam reset

Need to disable these for the master reset after cfam reset.

Signed-off-by: Eddie James <[email protected]>
---
drivers/fsi/fsi-master-aspeed.c | 7 +++++++
1 file changed, 7 insertions(+)

diff --git a/drivers/fsi/fsi-master-aspeed.c b/drivers/fsi/fsi-master-aspeed.c
index 1cb5bf6c05d2..92b47bc9917a 100644
--- a/drivers/fsi/fsi-master-aspeed.c
+++ b/drivers/fsi/fsi-master-aspeed.c
@@ -375,11 +375,18 @@ static ssize_t cfam_reset_store(struct device *dev, struct device_attribute *att

trace_fsi_master_aspeed_cfam_reset(true);
spin_lock_irqsave(&aspeed->lock, flags);
+
+ opb_writel(aspeed, ctrl_base + FSI_MMODE,
+ cpu_to_be32(aspeed->master.mmode & ~(FSI_MMODE_EIP | FSI_MMODE_RELA)));
+
gpiod_set_value(aspeed->cfam_reset_gpio, 1);
udelay(900);
gpiod_set_value(aspeed->cfam_reset_gpio, 0);
udelay(900);
+
opb_writel(aspeed, ctrl_base + FSI_MRESP0, cpu_to_be32(FSI_MRESP_RST_ALL_MASTER));
+ opb_writel(aspeed, ctrl_base + FSI_MMODE, cpu_to_be32(aspeed->master.mmode));
+
spin_unlock_irqrestore(&aspeed->lock, flags);
trace_fsi_master_aspeed_cfam_reset(false);

--
2.39.3


2024-02-15 22:11:04

by Eddie James

[permalink] [raw]
Subject: [PATCH 05/33] fsi: Use a defined value for default echo delay

Avoid hardcoding '16' several times.

Signed-off-by: Eddie James <[email protected]>
---
drivers/fsi/fsi-core.c | 6 +++---
drivers/fsi/fsi-slave.h | 2 ++
2 files changed, 5 insertions(+), 3 deletions(-)

diff --git a/drivers/fsi/fsi-core.c b/drivers/fsi/fsi-core.c
index 1c687eb021a0..8823e8e56dab 100644
--- a/drivers/fsi/fsi-core.c
+++ b/drivers/fsi/fsi-core.c
@@ -1008,8 +1008,8 @@ static int fsi_slave_init(struct fsi_master *master, int link, uint8_t id)
slave->link = link;
slave->id = id;
slave->size = FSI_SLAVE_SIZE_23b;
- slave->t_send_delay = 16;
- slave->t_echo_delay = 16;
+ slave->t_send_delay = FSI_SMODE_SD_DEFAULT;
+ slave->t_echo_delay = FSI_SMODE_ED_DEFAULT;

/* Get chip ID if any */
slave->chip_id = -1;
@@ -1160,7 +1160,7 @@ static int fsi_master_break(struct fsi_master *master, int link)
if (master->send_break)
rc = master->send_break(master, link);
if (master->link_config)
- master->link_config(master, link, 16, 16);
+ master->link_config(master, link, FSI_SMODE_SD_DEFAULT, FSI_SMODE_ED_DEFAULT);

return rc;
}
diff --git a/drivers/fsi/fsi-slave.h b/drivers/fsi/fsi-slave.h
index dba65bd4e083..dca9db0a42e5 100644
--- a/drivers/fsi/fsi-slave.h
+++ b/drivers/fsi/fsi-slave.h
@@ -41,8 +41,10 @@
#define FSI_SMODE_SID_MASK 3 /* ID Mask */
#define FSI_SMODE_ED_SHIFT 20 /* Echo delay shift */
#define FSI_SMODE_ED_MASK 0xf /* Echo delay mask */
+#define FSI_SMODE_ED_DEFAULT 16 /* Default echo delay */
#define FSI_SMODE_SD_SHIFT 16 /* Send delay shift */
#define FSI_SMODE_SD_MASK 0xf /* Send delay mask */
+#define FSI_SMODE_SD_DEFAULT 16 /* Default send delay */
#define FSI_SMODE_LBCRR_SHIFT 8 /* Clk ratio shift */
#define FSI_SMODE_LBCRR_MASK 0xf /* Clk ratio mask */

--
2.39.3


2024-02-15 22:11:05

by Eddie James

[permalink] [raw]
Subject: [PATCH 19/33] fsi: aspeed: Only read result register for successful read

No reason to read the result in the error path, and remove the
null pointer check on the output, as it should never be null.

Signed-off-by: Eddie James <[email protected]>
---
drivers/fsi/fsi-master-aspeed.c | 29 +++++++++++++----------------
1 file changed, 13 insertions(+), 16 deletions(-)

diff --git a/drivers/fsi/fsi-master-aspeed.c b/drivers/fsi/fsi-master-aspeed.c
index 64a5407a15ec..83f84ee6d6f4 100644
--- a/drivers/fsi/fsi-master-aspeed.c
+++ b/drivers/fsi/fsi-master-aspeed.c
@@ -175,8 +175,6 @@ static int __opb_read(struct fsi_master_aspeed *aspeed, uint32_t addr,
writel(OPB0_XFER_ACK_EN, base + OPB_IRQ_STATUS);
status = readl(base + OPB0_STATUS);

- result = readl(base + OPB0_FSI_DATA_R);
-
/* Return error when poll timed out */
if (ret) {
trace_fsi_master_aspeed_timeout(reg, status, true);
@@ -187,21 +185,20 @@ static int __opb_read(struct fsi_master_aspeed *aspeed, uint32_t addr,
if (status & STATUS_ERR_ACK)
return -EIO;

- if (out) {
- switch (transfer_size) {
- case XFER_BYTE:
- *(u8 *)out = result;
- break;
- case XFER_HALFWORD:
- *(u16 *)out = result;
- break;
- case XFER_FULLWORD:
- *(u32 *)out = result;
- break;
- default:
- return -EINVAL;
- }
+ result = readl(base + OPB0_FSI_DATA_R);

+ switch (transfer_size) {
+ case XFER_BYTE:
+ *(u8 *)out = result;
+ break;
+ case XFER_HALFWORD:
+ *(u16 *)out = result;
+ break;
+ case XFER_FULLWORD:
+ *(u32 *)out = result;
+ break;
+ default:
+ return -EINVAL;
}

trace_fsi_master_aspeed_opb_xfer(addr, transfer_size + 1, result, true);
--
2.39.3


2024-02-15 22:11:06

by Eddie James

[permalink] [raw]
Subject: [PATCH 11/33] fsi: core: Add slave spinlock

FSI slave operations were not locked, meaning that during slave
error recovery operations, other slave accesses may take place,
resulting in incorrect recovery and additional errors. Make the
slave access and error recovery atomic with a spinlock. Don't
use a mutex for future interrupt handling support.

Signed-off-by: Eddie James <[email protected]>
---
drivers/fsi/fsi-core.c | 7 +++++++
drivers/fsi/fsi-slave.h | 2 ++
2 files changed, 9 insertions(+)

diff --git a/drivers/fsi/fsi-core.c b/drivers/fsi/fsi-core.c
index e6ed2d0773b6..eb15e5f5a2ee 100644
--- a/drivers/fsi/fsi-core.c
+++ b/drivers/fsi/fsi-core.c
@@ -304,6 +304,7 @@ static int fsi_slave_handle_error(struct fsi_slave *slave, bool write,
int fsi_slave_read(struct fsi_slave *slave, uint32_t addr,
void *val, size_t size)
{
+ unsigned long flags;
uint8_t id = slave->id;
int rc, err_rc, i;

@@ -311,6 +312,7 @@ int fsi_slave_read(struct fsi_slave *slave, uint32_t addr,
if (rc)
return rc;

+ spin_lock_irqsave(&slave->lock, flags);
for (i = 0; i < slave_retries; i++) {
rc = fsi_master_read(slave->master, slave->link,
id, addr, val, size);
@@ -321,6 +323,7 @@ int fsi_slave_read(struct fsi_slave *slave, uint32_t addr,
if (err_rc)
break;
}
+ spin_unlock_irqrestore(&slave->lock, flags);

return rc;
}
@@ -329,6 +332,7 @@ EXPORT_SYMBOL_GPL(fsi_slave_read);
int fsi_slave_write(struct fsi_slave *slave, uint32_t addr,
const void *val, size_t size)
{
+ unsigned long flags;
uint8_t id = slave->id;
int rc, err_rc, i;

@@ -336,6 +340,7 @@ int fsi_slave_write(struct fsi_slave *slave, uint32_t addr,
if (rc)
return rc;

+ spin_lock_irqsave(&slave->lock, flags);
for (i = 0; i < slave_retries; i++) {
rc = fsi_master_write(slave->master, slave->link,
id, addr, val, size);
@@ -346,6 +351,7 @@ int fsi_slave_write(struct fsi_slave *slave, uint32_t addr,
if (err_rc)
break;
}
+ spin_unlock_irqrestore(&slave->lock, flags);

return rc;
}
@@ -1005,6 +1011,7 @@ static int fsi_slave_init(struct fsi_master *master, int link, uint8_t id)
if (!slave)
return -ENOMEM;

+ spin_lock_init(&slave->lock);
dev_set_name(&slave->dev, "slave@%02x:%02x", link, id);
slave->dev.type = &cfam_type;
slave->dev.parent = &master->dev;
diff --git a/drivers/fsi/fsi-slave.h b/drivers/fsi/fsi-slave.h
index 42af2fae0329..6f8fb97023fb 100644
--- a/drivers/fsi/fsi-slave.h
+++ b/drivers/fsi/fsi-slave.h
@@ -6,6 +6,7 @@

#include <linux/cdev.h>
#include <linux/device.h>
+#include <linux/spinlock.h>

#define FSI_SLAVE_BASE 0x800

@@ -100,6 +101,7 @@ struct fsi_slave {
struct device dev;
struct fsi_master *master;
struct cdev cdev;
+ spinlock_t lock; /* atomic access and error recovery */
int cdev_idx;
int id; /* FSI address */
int link; /* FSI link# */
--
2.39.3


2024-02-15 22:11:06

by Eddie James

[permalink] [raw]
Subject: [PATCH 08/33] fsi: core: Improve master read/write/error traces

Consolidate the master read and write traces into one trace and
change the result trace into an error trace for less spam.

Signed-off-by: Eddie James <[email protected]>
---
drivers/fsi/fsi-core.c | 26 +++++----
include/trace/events/fsi.h | 112 ++++++++++++-------------------------
2 files changed, 51 insertions(+), 87 deletions(-)

diff --git a/drivers/fsi/fsi-core.c b/drivers/fsi/fsi-core.c
index 9610b5948550..a989a2007c0f 100644
--- a/drivers/fsi/fsi-core.c
+++ b/drivers/fsi/fsi-core.c
@@ -1110,14 +1110,15 @@ static int fsi_master_read(struct fsi_master *master, int link,
{
int rc;

- trace_fsi_master_read(master, link, slave_id, addr, size);
-
rc = fsi_check_access(addr, size);
- if (!rc)
+ if (!rc) {
rc = master->read(master, link, slave_id, addr, val, size);
-
- trace_fsi_master_rw_result(master, link, slave_id, addr, size,
- false, val, rc);
+ if (rc)
+ trace_fsi_master_error(master->idx, link, slave_id, addr, size, val, rc,
+ true);
+ else
+ trace_fsi_master_xfer(master->idx, link, slave_id, addr, size, val, true);
+ }

return rc;
}
@@ -1127,14 +1128,15 @@ static int fsi_master_write(struct fsi_master *master, int link,
{
int rc;

- trace_fsi_master_write(master, link, slave_id, addr, size, val);
-
rc = fsi_check_access(addr, size);
- if (!rc)
+ if (!rc) {
rc = master->write(master, link, slave_id, addr, val, size);
-
- trace_fsi_master_rw_result(master, link, slave_id, addr, size,
- true, val, rc);
+ if (rc)
+ trace_fsi_master_error(master->idx, link, slave_id, addr, size, val, rc,
+ false);
+ else
+ trace_fsi_master_xfer(master->idx, link, slave_id, addr, size, val, false);
+ }

return rc;
}
diff --git a/include/trace/events/fsi.h b/include/trace/events/fsi.h
index 5ff15126ad9d..fed8835f438e 100644
--- a/include/trace/events/fsi.h
+++ b/include/trace/events/fsi.h
@@ -8,101 +8,63 @@

#include <linux/tracepoint.h>

-TRACE_EVENT(fsi_master_read,
- TP_PROTO(const struct fsi_master *master, int link, int id,
- uint32_t addr, size_t size),
- TP_ARGS(master, link, id, addr, size),
+TRACE_EVENT(fsi_master_xfer,
+ TP_PROTO(int master_idx, int link, int id, uint32_t addr, size_t size, const void *data,
+ bool read),
+ TP_ARGS(master_idx, link, id, addr, size, data, read),
TP_STRUCT__entry(
- __field(int, master_idx)
- __field(int, link)
- __field(int, id)
- __field(__u32, addr)
- __field(size_t, size)
+ __field(int, master_idx)
+ __field(int, link)
+ __field(int, id)
+ __field(uint32_t, addr)
+ __field(int, size)
+ __field(uint32_t, data)
+ __field(bool, read)
),
TP_fast_assign(
- __entry->master_idx = master->idx;
+ __entry->master_idx = master_idx;
__entry->link = link;
__entry->id = id;
__entry->addr = addr;
- __entry->size = size;
- ),
- TP_printk("fsi%d:%02d:%02d %08x[%zu]",
- __entry->master_idx,
- __entry->link,
- __entry->id,
- __entry->addr,
- __entry->size
- )
-);
-
-TRACE_EVENT(fsi_master_write,
- TP_PROTO(const struct fsi_master *master, int link, int id,
- uint32_t addr, size_t size, const void *data),
- TP_ARGS(master, link, id, addr, size, data),
- TP_STRUCT__entry(
- __field(int, master_idx)
- __field(int, link)
- __field(int, id)
- __field(__u32, addr)
- __field(size_t, size)
- __field(__u32, data)
- ),
- TP_fast_assign(
- __entry->master_idx = master->idx;
- __entry->link = link;
- __entry->id = id;
- __entry->addr = addr;
- __entry->size = size;
+ __entry->size = (int)size;
__entry->data = 0;
memcpy(&__entry->data, data, size);
+ __entry->read = read;
),
- TP_printk("fsi%d:%02d:%02d %08x[%zu] <= {%*ph}",
- __entry->master_idx,
- __entry->link,
- __entry->id,
- __entry->addr,
- __entry->size,
- (int)__entry->size, &__entry->data
- )
+ TP_printk("fsi%d:%02d:%02d %s %08x {%*ph}", __entry->master_idx, __entry->link,
+ __entry->id, __entry->read ? "read" : "write", __entry->addr, __entry->size,
+ &__entry->data)
);

-TRACE_EVENT(fsi_master_rw_result,
- TP_PROTO(const struct fsi_master *master, int link, int id,
- uint32_t addr, size_t size,
- bool write, const void *data, int ret),
- TP_ARGS(master, link, id, addr, size, write, data, ret),
+TRACE_EVENT(fsi_master_error,
+ TP_PROTO(int master_idx, int link, int id, uint32_t addr, size_t size, const void *data,
+ int ret, bool read),
+ TP_ARGS(master_idx, link, id, addr, size, data, ret, read),
TP_STRUCT__entry(
- __field(int, master_idx)
- __field(int, link)
- __field(int, id)
- __field(__u32, addr)
- __field(size_t, size)
- __field(bool, write)
- __field(__u32, data)
- __field(int, ret)
+ __field(int, master_idx)
+ __field(int, link)
+ __field(int, id)
+ __field(uint32_t, addr)
+ __field(int, size)
+ __field(uint32_t, data)
+ __field(int, ret)
+ __field(bool, read)
),
TP_fast_assign(
- __entry->master_idx = master->idx;
+ __entry->master_idx = master_idx;
__entry->link = link;
__entry->id = id;
__entry->addr = addr;
- __entry->size = size;
- __entry->write = write;
+ __entry->size = (int)size;
__entry->data = 0;
- __entry->ret = ret;
- if (__entry->write || !__entry->ret)
+ if (!read)
memcpy(&__entry->data, data, size);
+ __entry->ret = ret;
+ __entry->read = read;
),
- TP_printk("fsi%d:%02d:%02d %08x[%zu] %s {%*ph} ret %d",
- __entry->master_idx,
- __entry->link,
- __entry->id,
- __entry->addr,
- __entry->size,
- __entry->write ? "<=" : "=>",
- (int)__entry->size, &__entry->data,
- __entry->ret
- )
+ TP_printk("fsi%d:%02d:%02d %s %08x {%*ph} %d", __entry->master_idx, __entry->link,
+ __entry->id, __entry->read ? "read" : "write", __entry->addr, __entry->size,
+ &__entry->data, __entry->ret)
);

TRACE_EVENT(fsi_master_break,
--
2.39.3


2024-02-15 22:11:31

by Eddie James

[permalink] [raw]
Subject: [PATCH 17/33] fsi: aspeed: Refactor trace functions

Remove the opb error trace, add a timeout trace, and combine the
read/write traces.

Signed-off-by: Eddie James <[email protected]>
---
drivers/fsi/fsi-master-aspeed.c | 29 +++------
include/trace/events/fsi_master_aspeed.h | 80 ++++++++----------------
2 files changed, 34 insertions(+), 75 deletions(-)

diff --git a/drivers/fsi/fsi-master-aspeed.c b/drivers/fsi/fsi-master-aspeed.c
index c36e7e49e965..1a91f3acdfcc 100644
--- a/drivers/fsi/fsi-master-aspeed.c
+++ b/drivers/fsi/fsi-master-aspeed.c
@@ -116,16 +116,17 @@ static int __opb_write(struct fsi_master_aspeed *aspeed, u32 addr,

status = readl(base + OPB0_STATUS);

- trace_fsi_master_aspeed_opb_write(addr, val, transfer_size, status, reg);
-
/* Return error when poll timed out */
- if (ret)
+ if (ret) {
+ trace_fsi_master_aspeed_timeout(reg, status, false);
return ret;
+ }

/* Command failed, master will reset */
if (status & STATUS_ERR_ACK)
return -EIO;

+ trace_fsi_master_aspeed_opb_xfer(addr, transfer_size + 1, val, false);
return 0;
}

@@ -169,13 +170,11 @@ static int __opb_read(struct fsi_master_aspeed *aspeed, uint32_t addr,

result = readl(base + OPB0_FSI_DATA_R);

- trace_fsi_master_aspeed_opb_read(addr, transfer_size, result,
- readl(base + OPB0_STATUS),
- reg);
-
/* Return error when poll timed out */
- if (ret)
+ if (ret) {
+ trace_fsi_master_aspeed_timeout(reg, status, true);
return ret;
+ }

/* Command failed, master will reset */
if (status & STATUS_ERR_ACK)
@@ -198,6 +197,7 @@ static int __opb_read(struct fsi_master_aspeed *aspeed, uint32_t addr,

}

+ trace_fsi_master_aspeed_opb_xfer(addr, transfer_size + 1, result, true);
return 0;
}

@@ -220,19 +220,6 @@ static int check_errors(struct fsi_master_aspeed *aspeed, int err)
{
int ret;

- if (trace_fsi_master_aspeed_opb_error_enabled()) {
- __be32 mresp0, mstap0, mesrb0;
-
- opb_readl(aspeed, ctrl_base + FSI_MRESP0, &mresp0);
- opb_readl(aspeed, ctrl_base + FSI_MSTAP0, &mstap0);
- opb_readl(aspeed, ctrl_base + FSI_MESRB0, &mesrb0);
-
- trace_fsi_master_aspeed_opb_error(
- be32_to_cpu(mresp0),
- be32_to_cpu(mstap0),
- be32_to_cpu(mesrb0));
- }
-
if (err == -EIO) {
/* Check MAEB (0x70) ? */

diff --git a/include/trace/events/fsi_master_aspeed.h b/include/trace/events/fsi_master_aspeed.h
index 0fff873775f1..7eeecbfec7f0 100644
--- a/include/trace/events/fsi_master_aspeed.h
+++ b/include/trace/events/fsi_master_aspeed.h
@@ -8,69 +8,41 @@

#include <linux/tracepoint.h>

-TRACE_EVENT(fsi_master_aspeed_opb_read,
- TP_PROTO(uint32_t addr, size_t size, uint32_t result, uint32_t status, uint32_t irq_status),
- TP_ARGS(addr, size, result, status, irq_status),
+TRACE_EVENT(fsi_master_aspeed_opb_xfer,
+ TP_PROTO(uint32_t addr, uint32_t size, uint32_t data, bool read),
+ TP_ARGS(addr, size, data, read),
TP_STRUCT__entry(
- __field(uint32_t, addr)
- __field(size_t, size)
- __field(uint32_t, result)
- __field(uint32_t, status)
- __field(uint32_t, irq_status)
- ),
+ __field(uint32_t, addr)
+ __field(uint32_t, size)
+ __field(uint32_t, data)
+ __field(bool, read)
+ ),
TP_fast_assign(
__entry->addr = addr;
__entry->size = size;
- __entry->result = result;
- __entry->status = status;
- __entry->irq_status = irq_status;
- ),
- TP_printk("addr %08x size %zu: result %08x sts: %08x irq_sts: %08x",
- __entry->addr, __entry->size, __entry->result,
- __entry->status, __entry->irq_status
- )
+ __entry->data = data;
+ __entry->read = read;
+ ),
+ TP_printk("%s addr %08x size %u data %08x", __entry->read ? "read" : "write",
+ __entry->addr, __entry->size, __entry->data)
);

-TRACE_EVENT(fsi_master_aspeed_opb_write,
- TP_PROTO(uint32_t addr, uint32_t val, size_t size, uint32_t status, uint32_t irq_status),
- TP_ARGS(addr, val, size, status, irq_status),
+TRACE_EVENT(fsi_master_aspeed_timeout,
+ TP_PROTO(uint32_t irq, uint32_t status, bool read),
+ TP_ARGS(irq, status, read),
TP_STRUCT__entry(
- __field(uint32_t, addr)
- __field(uint32_t, val)
- __field(size_t, size)
- __field(uint32_t, status)
- __field(uint32_t, irq_status)
- ),
+ __field(uint32_t, irq)
+ __field(uint32_t, status)
+ __field(bool, read)
+ ),
TP_fast_assign(
- __entry->addr = addr;
- __entry->val = val;
- __entry->size = size;
+ __entry->irq = irq;
__entry->status = status;
- __entry->irq_status = irq_status;
- ),
- TP_printk("addr %08x val %08x size %zu status: %08x irq_sts: %08x",
- __entry->addr, __entry->val, __entry->size,
- __entry->status, __entry->irq_status
- )
- );
-
-TRACE_EVENT(fsi_master_aspeed_opb_error,
- TP_PROTO(uint32_t mresp0, uint32_t mstap0, uint32_t mesrb0),
- TP_ARGS(mresp0, mstap0, mesrb0),
- TP_STRUCT__entry(
- __field(uint32_t, mresp0)
- __field(uint32_t, mstap0)
- __field(uint32_t, mesrb0)
- ),
- TP_fast_assign(
- __entry->mresp0 = mresp0;
- __entry->mstap0 = mstap0;
- __entry->mesrb0 = mesrb0;
- ),
- TP_printk("mresp0 %08x mstap0 %08x mesrb0 %08x",
- __entry->mresp0, __entry->mstap0, __entry->mesrb0
- )
- );
+ __entry->read = read;
+ ),
+ TP_printk("%s irq %08x status %08x", __entry->read ? "read" : "write", __entry->irq,
+ __entry->status)
+);

TRACE_EVENT(fsi_master_aspeed_cfam_reset,
TP_PROTO(bool start),
--
2.39.3


2024-02-15 22:11:31

by Eddie James

[permalink] [raw]
Subject: [PATCH 26/33] ARM: dts: aspeed: FSI interrupt support

Enable FSI interrupt controllers for AST2600 and P10BMC hub master.

Signed-off-by: Eddie James <[email protected]>
---
arch/arm/boot/dts/aspeed/aspeed-g6.dtsi | 4 ++++
arch/arm/boot/dts/aspeed/ibm-power10-dual.dtsi | 2 ++
2 files changed, 6 insertions(+)

diff --git a/arch/arm/boot/dts/aspeed/aspeed-g6.dtsi b/arch/arm/boot/dts/aspeed/aspeed-g6.dtsi
index c4d1faade8be..e0b44498269f 100644
--- a/arch/arm/boot/dts/aspeed/aspeed-g6.dtsi
+++ b/arch/arm/boot/dts/aspeed/aspeed-g6.dtsi
@@ -865,22 +865,26 @@ i2c: bus@1e78a000 {
};

fsim0: fsi@1e79b000 {
+ #interrupt-cells = <1>;
compatible = "aspeed,ast2600-fsi-master", "fsi-master";
reg = <0x1e79b000 0x94>;
interrupts = <GIC_SPI 100 IRQ_TYPE_LEVEL_HIGH>;
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_fsi1_default>;
clocks = <&syscon ASPEED_CLK_GATE_FSICLK>;
+ interrupt-controller;
status = "disabled";
};

fsim1: fsi@1e79b100 {
+ #interrupt-cells = <1>;
compatible = "aspeed,ast2600-fsi-master", "fsi-master";
reg = <0x1e79b100 0x94>;
interrupts = <GIC_SPI 101 IRQ_TYPE_LEVEL_HIGH>;
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_fsi2_default>;
clocks = <&syscon ASPEED_CLK_GATE_FSICLK>;
+ interrupt-controller;
status = "disabled";
};

diff --git a/arch/arm/boot/dts/aspeed/ibm-power10-dual.dtsi b/arch/arm/boot/dts/aspeed/ibm-power10-dual.dtsi
index a93a241d005a..44e48e39e6e9 100644
--- a/arch/arm/boot/dts/aspeed/ibm-power10-dual.dtsi
+++ b/arch/arm/boot/dts/aspeed/ibm-power10-dual.dtsi
@@ -166,10 +166,12 @@ occ-hwmon {
};

fsi_hub0: hub@3400 {
+ #interrupt-cells = <1>;
compatible = "fsi-master-hub";
reg = <0x3400 0x400>;
#address-cells = <2>;
#size-cells = <0>;
+ interrupt-controller;
};
};
};
--
2.39.3


2024-02-15 22:11:37

by Eddie James

[permalink] [raw]
Subject: [PATCH 20/33] fsi: aspeed: Switch to spinlock

In preparation for interrupt support, switch to a spinlock rather
than a mutex.

Signed-off-by: Eddie James <[email protected]>
---
drivers/fsi/fsi-master-aspeed.c | 25 ++++++++++++++-----------
1 file changed, 14 insertions(+), 11 deletions(-)

diff --git a/drivers/fsi/fsi-master-aspeed.c b/drivers/fsi/fsi-master-aspeed.c
index 83f84ee6d6f4..1cb5bf6c05d2 100644
--- a/drivers/fsi/fsi-master-aspeed.c
+++ b/drivers/fsi/fsi-master-aspeed.c
@@ -8,11 +8,11 @@
#include <linux/io.h>
#include <linux/mfd/syscon.h>
#include <linux/module.h>
-#include <linux/mutex.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
#include <linux/slab.h>
+#include <linux/spinlock.h>
#include <linux/iopoll.h>
#include <linux/gpio/consumer.h>

@@ -24,7 +24,7 @@ struct fsi_master_aspeed_data {

struct fsi_master_aspeed {
struct fsi_master master;
- struct mutex lock; /* protect HW access */
+ spinlock_t lock; /* protect HW access */
struct device *dev;
void __iomem *base;
struct clk *clk;
@@ -245,6 +245,7 @@ static int aspeed_master_read(struct fsi_master *master, int link,
uint8_t id, uint32_t addr, void *val, size_t size)
{
struct fsi_master_aspeed *aspeed = to_fsi_master_aspeed(master);
+ unsigned long flags;
int ret;

if (id > 0x3)
@@ -253,7 +254,7 @@ static int aspeed_master_read(struct fsi_master *master, int link,
addr |= id << 21;
addr += link * FSI_HUB_LINK_SIZE;

- mutex_lock(&aspeed->lock);
+ spin_lock_irqsave(&aspeed->lock, flags);

switch (size) {
case 1:
@@ -272,7 +273,7 @@ static int aspeed_master_read(struct fsi_master *master, int link,

ret = check_errors(aspeed, ret);
done:
- mutex_unlock(&aspeed->lock);
+ spin_unlock_irqrestore(&aspeed->lock, flags);
return ret;
}

@@ -280,6 +281,7 @@ static int aspeed_master_write(struct fsi_master *master, int link,
uint8_t id, uint32_t addr, const void *val, size_t size)
{
struct fsi_master_aspeed *aspeed = to_fsi_master_aspeed(master);
+ unsigned long flags;
int ret;

if (id > 0x3)
@@ -288,7 +290,7 @@ static int aspeed_master_write(struct fsi_master *master, int link,
addr |= id << 21;
addr += link * FSI_HUB_LINK_SIZE;

- mutex_lock(&aspeed->lock);
+ spin_lock_irqsave(&aspeed->lock, flags);

switch (size) {
case 1:
@@ -307,7 +309,7 @@ static int aspeed_master_write(struct fsi_master *master, int link,

ret = check_errors(aspeed, ret);
done:
- mutex_unlock(&aspeed->lock);
+ spin_unlock_irqrestore(&aspeed->lock, flags);
return ret;
}

@@ -369,15 +371,16 @@ static ssize_t cfam_reset_store(struct device *dev, struct device_attribute *att
const char *buf, size_t count)
{
struct fsi_master_aspeed *aspeed = dev_get_drvdata(dev);
+ unsigned long flags;

trace_fsi_master_aspeed_cfam_reset(true);
- mutex_lock(&aspeed->lock);
+ spin_lock_irqsave(&aspeed->lock, flags);
gpiod_set_value(aspeed->cfam_reset_gpio, 1);
- usleep_range(900, 1000);
+ udelay(900);
gpiod_set_value(aspeed->cfam_reset_gpio, 0);
- usleep_range(900, 1000);
+ udelay(900);
opb_writel(aspeed, ctrl_base + FSI_MRESP0, cpu_to_be32(FSI_MRESP_RST_ALL_MASTER));
- mutex_unlock(&aspeed->lock);
+ spin_unlock_irqrestore(&aspeed->lock, flags);
trace_fsi_master_aspeed_cfam_reset(false);

return count;
@@ -468,6 +471,7 @@ static int fsi_master_aspeed_probe(struct platform_device *pdev)
return -ENOMEM;

aspeed->dev = &pdev->dev;
+ spin_lock_init(&aspeed->lock);

aspeed->base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(aspeed->base)) {
@@ -543,7 +547,6 @@ static int fsi_master_aspeed_probe(struct platform_device *pdev)

dev_set_drvdata(&pdev->dev, aspeed);

- mutex_init(&aspeed->lock);
rc = fsi_master_init(&aspeed->master, clk_get_rate(aspeed->clk));
if (rc)
goto err_regmap;
--
2.39.3


2024-02-15 22:11:40

by Eddie James

[permalink] [raw]
Subject: [PATCH 16/33] fsi: aspeed: Remove cfam reset sysfs file in error path and remove

This fixes a duplicate sysfs warning on device re-probe.

Signed-off-by: Eddie James <[email protected]>
---
drivers/fsi/fsi-master-aspeed.c | 7 +++++++
1 file changed, 7 insertions(+)

diff --git a/drivers/fsi/fsi-master-aspeed.c b/drivers/fsi/fsi-master-aspeed.c
index 2258980e4c47..c36e7e49e965 100644
--- a/drivers/fsi/fsi-master-aspeed.c
+++ b/drivers/fsi/fsi-master-aspeed.c
@@ -410,6 +410,7 @@ static int setup_cfam_reset(struct fsi_master_aspeed *aspeed)

rc = device_create_file(dev, &dev_attr_cfam_reset);
if (rc) {
+ aspeed->cfam_reset_gpio = NULL;
devm_gpiod_put(dev, gpio);
return rc;
}
@@ -575,6 +576,9 @@ static int fsi_master_aspeed_probe(struct platform_device *pdev)
err_regmap:
regmap_exit(aspeed->master.map);
err_release:
+ if (aspeed->cfam_reset_gpio)
+ device_remove_file(aspeed->dev, &dev_attr_cfam_reset);
+
clk_disable_unprepare(aspeed->clk);
err_free_aspeed:
kfree(aspeed);
@@ -585,6 +589,9 @@ static int fsi_master_aspeed_remove(struct platform_device *pdev)
{
struct fsi_master_aspeed *aspeed = platform_get_drvdata(pdev);

+ if (aspeed->cfam_reset_gpio)
+ device_remove_file(aspeed->dev, &dev_attr_cfam_reset);
+
fsi_master_unregister(&aspeed->master);
clk_disable_unprepare(aspeed->clk);

--
2.39.3


2024-02-15 22:11:58

by Eddie James

[permalink] [raw]
Subject: [PATCH 22/33] fsi: aspeed: Use common master error handler

This will do the correct mmode manipulation to do the master
reset.

Signed-off-by: Eddie James <[email protected]>
---
drivers/fsi/fsi-master-aspeed.c | 27 ++++-----------------------
1 file changed, 4 insertions(+), 23 deletions(-)

diff --git a/drivers/fsi/fsi-master-aspeed.c b/drivers/fsi/fsi-master-aspeed.c
index 92b47bc9917a..ac8835e4d1f8 100644
--- a/drivers/fsi/fsi-master-aspeed.c
+++ b/drivers/fsi/fsi-master-aspeed.c
@@ -220,27 +220,6 @@ static int opb_readb(struct fsi_master_aspeed *aspeed, uint32_t addr, u8 *out)
return __opb_read(aspeed, addr, XFER_BYTE, (void *)out);
}

-static int check_errors(struct fsi_master_aspeed *aspeed, int err)
-{
- int ret;
-
- if (err == -EIO) {
- /* Check MAEB (0x70) ? */
-
- /* Then clear errors in master */
- ret = opb_writel(aspeed, ctrl_base + FSI_MRESP0,
- cpu_to_be32(FSI_MRESP_RST_ALL_MASTER));
- if (ret) {
- /* TODO: log? return different code? */
- return ret;
- }
- /* TODO: confirm that 0x70 was okay */
- }
-
- /* This will pass through timeout errors */
- return err;
-}
-
static int aspeed_master_read(struct fsi_master *master, int link,
uint8_t id, uint32_t addr, void *val, size_t size)
{
@@ -271,7 +250,8 @@ static int aspeed_master_read(struct fsi_master *master, int link,
goto done;
}

- ret = check_errors(aspeed, ret);
+ if (ret == -EIO)
+ fsi_master_error(&aspeed->master, link);
done:
spin_unlock_irqrestore(&aspeed->lock, flags);
return ret;
@@ -307,7 +287,8 @@ static int aspeed_master_write(struct fsi_master *master, int link,
goto done;
}

- ret = check_errors(aspeed, ret);
+ if (ret == -EIO)
+ fsi_master_error(&aspeed->master, link);
done:
spin_unlock_irqrestore(&aspeed->lock, flags);
return ret;
--
2.39.3


2024-02-15 22:13:24

by Eddie James

[permalink] [raw]
Subject: [PATCH 30/33] i2c: fsi: Remove list structure of ports

Maintaining a list of ports is unnecessary since they can be managed
with the device resource framework.

Signed-off-by: Eddie James <[email protected]>
---
drivers/i2c/busses/i2c-fsi.c | 44 +++++++++---------------------------
1 file changed, 11 insertions(+), 33 deletions(-)

diff --git a/drivers/i2c/busses/i2c-fsi.c b/drivers/i2c/busses/i2c-fsi.c
index f237e76d29e6..096dc7e2369f 100644
--- a/drivers/i2c/busses/i2c-fsi.c
+++ b/drivers/i2c/busses/i2c-fsi.c
@@ -19,7 +19,6 @@
#include <linux/i2c.h>
#include <linux/jiffies.h>
#include <linux/kernel.h>
-#include <linux/list.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/of.h>
@@ -148,14 +147,12 @@

struct fsi_i2c_master {
struct fsi_device *fsi;
- struct list_head ports;
struct mutex lock;
u32 clock_div;
u8 fifo_size;
};

struct fsi_i2c_port {
- struct list_head list;
struct i2c_adapter adapter;
struct fsi_i2c_master *master;
u16 port;
@@ -327,7 +324,7 @@ static int fsi_i2c_read_fifo(struct fsi_i2c_port *port, struct i2c_msg *msg,

static int fsi_i2c_get_scl(struct i2c_adapter *adap)
{
- struct fsi_i2c_port *port = adap->algo_data;
+ struct fsi_i2c_port *port = i2c_get_adapdata(adap);
u32 stat;

fsi_i2c_read_reg(port->master->fsi, I2C_FSI_STAT, &stat);
@@ -337,7 +334,7 @@ static int fsi_i2c_get_scl(struct i2c_adapter *adap)

static void fsi_i2c_set_scl(struct i2c_adapter *adap, int val)
{
- struct fsi_i2c_port *port = adap->algo_data;
+ struct fsi_i2c_port *port = i2c_get_adapdata(adap);

if (val)
fsi_i2c_write_reg(port->master->fsi, I2C_FSI_SET_SCL, 0);
@@ -347,7 +344,7 @@ static void fsi_i2c_set_scl(struct i2c_adapter *adap, int val)

static int fsi_i2c_get_sda(struct i2c_adapter *adap)
{
- struct fsi_i2c_port *port = adap->algo_data;
+ struct fsi_i2c_port *port = i2c_get_adapdata(adap);
u32 stat;

fsi_i2c_read_reg(port->master->fsi, I2C_FSI_STAT, &stat);
@@ -357,7 +354,7 @@ static int fsi_i2c_get_sda(struct i2c_adapter *adap)

static void fsi_i2c_set_sda(struct i2c_adapter *adap, int val)
{
- struct fsi_i2c_port *port = adap->algo_data;
+ struct fsi_i2c_port *port = i2c_get_adapdata(adap);

if (val)
fsi_i2c_write_reg(port->master->fsi, I2C_FSI_SET_SDA, 0);
@@ -367,7 +364,7 @@ static void fsi_i2c_set_sda(struct i2c_adapter *adap, int val)

static void fsi_i2c_prepare_recovery(struct i2c_adapter *adap)
{
- struct fsi_i2c_port *port = adap->algo_data;
+ struct fsi_i2c_port *port = i2c_get_adapdata(adap);
u32 mode;
int rc;

@@ -381,7 +378,7 @@ static void fsi_i2c_prepare_recovery(struct i2c_adapter *adap)

static void fsi_i2c_unprepare_recovery(struct i2c_adapter *adap)
{
- struct fsi_i2c_port *port = adap->algo_data;
+ struct fsi_i2c_port *port = i2c_get_adapdata(adap);
u32 mode;
int rc;

@@ -594,7 +591,7 @@ static int fsi_i2c_wait(struct fsi_i2c_port *port, struct i2c_msg *msg,
static int fsi_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs,
int num)
{
- struct fsi_i2c_port *port = adap->algo_data;
+ struct fsi_i2c_port *port = i2c_get_adapdata(adap);
unsigned long start_time;
struct i2c_msg *msg;
int rc;
@@ -679,7 +676,6 @@ static int fsi_i2c_probe(struct device *dev)

mutex_init(&i2c->lock);
i2c->fsi = to_fsi_dev(dev);
- INIT_LIST_HEAD(&i2c->ports);
i2c->clock_div = I2C_DEFAULT_CLK_DIV;

lbus = fsi_device_local_bus_frequency(i2c->fsi);
@@ -706,38 +702,30 @@ static int fsi_i2c_probe(struct device *dev)
ports = FIELD_GET(I2C_STAT_MAX_PORT, stat) + 1;
dev_dbg(dev, "I2C master has %d ports\n", ports);

- for (port_no = 0; port_no < ports; port_no++) {
+ port = devm_kzalloc(dev, sizeof(*port) * ports, GFP_KERNEL);
+ for (port_no = 0; port_no < ports; port_no++, port++) {
np = fsi_i2c_find_port_of_node(dev->of_node, port_no);
if (!of_device_is_available(np))
continue;

- port = kzalloc(sizeof(*port), GFP_KERNEL);
- if (!port) {
- of_node_put(np);
- break;
- }
-
port->master = i2c;
port->port = port_no;

+ i2c_set_adapdata(&port->adapter, port);
port->adapter.owner = THIS_MODULE;
port->adapter.dev.of_node = np;
port->adapter.dev.parent = dev;
port->adapter.algo = &fsi_i2c_algorithm;
port->adapter.bus_recovery_info = &fsi_i2c_bus_recovery_info;
- port->adapter.algo_data = port;

snprintf(port->adapter.name, sizeof(port->adapter.name),
"i2c_bus-%u", port_no);

- rc = i2c_add_adapter(&port->adapter);
+ rc = devm_i2c_add_adapter(dev, &port->adapter);
if (rc < 0) {
dev_err(dev, "Failed to register adapter: %d\n", rc);
- kfree(port);
continue;
}
-
- list_add(&port->list, &i2c->ports);
}

dev_set_drvdata(dev, i2c);
@@ -746,16 +734,6 @@ static int fsi_i2c_probe(struct device *dev)

static int fsi_i2c_remove(struct device *dev)
{
- struct fsi_i2c_master *i2c = dev_get_drvdata(dev);
- struct fsi_i2c_port *port;
- struct fsi_i2c_port *tmp;
-
- list_for_each_entry_safe(port, tmp, &i2c->ports, list) {
- list_del(&port->list);
- i2c_del_adapter(&port->adapter);
- kfree(port);
- }
-
return 0;
}

--
2.39.3


2024-02-15 22:14:15

by Eddie James

[permalink] [raw]
Subject: [PATCH 29/33] i2c: fsi: Change fsi_i2c_write_reg to accept data instead of a pointer

There's no use passing a pointer here.

Signed-off-by: Eddie James <[email protected]>
---
drivers/i2c/busses/i2c-fsi.c | 47 +++++++++++++++---------------------
1 file changed, 20 insertions(+), 27 deletions(-)

diff --git a/drivers/i2c/busses/i2c-fsi.c b/drivers/i2c/busses/i2c-fsi.c
index bc44cad49ef2..f237e76d29e6 100644
--- a/drivers/i2c/busses/i2c-fsi.c
+++ b/drivers/i2c/busses/i2c-fsi.c
@@ -177,9 +177,9 @@ static int fsi_i2c_read_reg(struct fsi_device *fsi, unsigned int reg,
}

static int fsi_i2c_write_reg(struct fsi_device *fsi, unsigned int reg,
- u32 *data)
+ u32 data)
{
- __be32 data_be = cpu_to_be32p(data);
+ __be32 data_be = cpu_to_be32(data);

return fsi_device_write(fsi, reg, &data_be, sizeof(data_be));
}
@@ -188,17 +188,16 @@ static int fsi_i2c_dev_init(struct fsi_i2c_master *i2c)
{
u32 mode = I2C_MODE_ENHANCED;
u32 extended_status;
- u32 interrupt = 0;
u32 watermark;
int rc;

/* since we use polling, disable interrupts */
- rc = fsi_i2c_write_reg(i2c->fsi, I2C_FSI_INT_MASK, &interrupt);
+ rc = fsi_i2c_write_reg(i2c->fsi, I2C_FSI_INT_MASK, 0);
if (rc)
return rc;

mode |= FIELD_PREP(I2C_MODE_CLKDIV, i2c->clock_div);
- rc = fsi_i2c_write_reg(i2c->fsi, I2C_FSI_MODE, &mode);
+ rc = fsi_i2c_write_reg(i2c->fsi, I2C_FSI_MODE, mode);
if (rc)
return rc;

@@ -211,13 +210,12 @@ static int fsi_i2c_dev_init(struct fsi_i2c_master *i2c)
i2c->fifo_size - I2C_FIFO_HI_LVL);
watermark |= FIELD_PREP(I2C_WATERMARK_LO, I2C_FIFO_LO_LVL);

- return fsi_i2c_write_reg(i2c->fsi, I2C_FSI_WATER_MARK, &watermark);
+ return fsi_i2c_write_reg(i2c->fsi, I2C_FSI_WATER_MARK, watermark);
}

static int fsi_i2c_set_port(struct fsi_i2c_port *port)
{
struct fsi_device *fsi = port->master->fsi;
- u32 dummy = 0;
u32 mode;
int rc;

@@ -229,12 +227,12 @@ static int fsi_i2c_set_port(struct fsi_i2c_port *port)
return 0;

mode = (mode & ~I2C_MODE_PORT) | FIELD_PREP(I2C_MODE_PORT, port->port);
- rc = fsi_i2c_write_reg(fsi, I2C_FSI_MODE, &mode);
+ rc = fsi_i2c_write_reg(fsi, I2C_FSI_MODE, mode);
if (rc)
return rc;

/* reset engine when port is changed */
- return fsi_i2c_write_reg(fsi, I2C_FSI_RESET_ERR, &dummy);
+ return fsi_i2c_write_reg(fsi, I2C_FSI_RESET_ERR, 0);
}

static int fsi_i2c_start(struct fsi_i2c_port *port, struct i2c_msg *msg,
@@ -253,7 +251,7 @@ static int fsi_i2c_start(struct fsi_i2c_port *port, struct i2c_msg *msg,
cmd |= FIELD_PREP(I2C_CMD_ADDR, msg->addr);
cmd |= FIELD_PREP(I2C_CMD_LEN, msg->len);

- return fsi_i2c_write_reg(port->master->fsi, I2C_FSI_CMD, &cmd);
+ return fsi_i2c_write_reg(port->master->fsi, I2C_FSI_CMD, cmd);
}

static int fsi_i2c_get_op_bytes(int op_bytes)
@@ -340,12 +338,11 @@ static int fsi_i2c_get_scl(struct i2c_adapter *adap)
static void fsi_i2c_set_scl(struct i2c_adapter *adap, int val)
{
struct fsi_i2c_port *port = adap->algo_data;
- u32 dummy = 0;

if (val)
- fsi_i2c_write_reg(port->master->fsi, I2C_FSI_SET_SCL, &dummy);
+ fsi_i2c_write_reg(port->master->fsi, I2C_FSI_SET_SCL, 0);
else
- fsi_i2c_write_reg(port->master->fsi, I2C_FSI_RESET_SCL, &dummy);
+ fsi_i2c_write_reg(port->master->fsi, I2C_FSI_RESET_SCL, 0);
}

static int fsi_i2c_get_sda(struct i2c_adapter *adap)
@@ -361,12 +358,11 @@ static int fsi_i2c_get_sda(struct i2c_adapter *adap)
static void fsi_i2c_set_sda(struct i2c_adapter *adap, int val)
{
struct fsi_i2c_port *port = adap->algo_data;
- u32 dummy = 0;

if (val)
- fsi_i2c_write_reg(port->master->fsi, I2C_FSI_SET_SDA, &dummy);
+ fsi_i2c_write_reg(port->master->fsi, I2C_FSI_SET_SDA, 0);
else
- fsi_i2c_write_reg(port->master->fsi, I2C_FSI_RESET_SDA, &dummy);
+ fsi_i2c_write_reg(port->master->fsi, I2C_FSI_RESET_SDA, 0);
}

static void fsi_i2c_prepare_recovery(struct i2c_adapter *adap)
@@ -380,7 +376,7 @@ static void fsi_i2c_prepare_recovery(struct i2c_adapter *adap)
return;

mode |= I2C_MODE_DIAG;
- fsi_i2c_write_reg(port->master->fsi, I2C_FSI_MODE, &mode);
+ fsi_i2c_write_reg(port->master->fsi, I2C_FSI_MODE, mode);
}

static void fsi_i2c_unprepare_recovery(struct i2c_adapter *adap)
@@ -394,13 +390,12 @@ static void fsi_i2c_unprepare_recovery(struct i2c_adapter *adap)
return;

mode &= ~I2C_MODE_DIAG;
- fsi_i2c_write_reg(port->master->fsi, I2C_FSI_MODE, &mode);
+ fsi_i2c_write_reg(port->master->fsi, I2C_FSI_MODE, mode);
}

static int fsi_i2c_reset_bus(struct fsi_i2c_master *i2c,
struct fsi_i2c_port *port)
{
- u32 dummy = 0;
u32 stat;
int rc;

@@ -408,7 +403,7 @@ static int fsi_i2c_reset_bus(struct fsi_i2c_master *i2c,
i2c_recover_bus(&port->adapter);

/* reset errors */
- rc = fsi_i2c_write_reg(i2c->fsi, I2C_FSI_RESET_ERR, &dummy);
+ rc = fsi_i2c_write_reg(i2c->fsi, I2C_FSI_RESET_ERR, 0);
if (rc)
return rc;

@@ -423,7 +418,7 @@ static int fsi_i2c_reset_bus(struct fsi_i2c_master *i2c,
return 0;

/* failed to get command complete; reset engine again */
- rc = fsi_i2c_write_reg(i2c->fsi, I2C_FSI_RESET_I2C, &dummy);
+ rc = fsi_i2c_write_reg(i2c->fsi, I2C_FSI_RESET_I2C, 0);
if (rc)
return rc;

@@ -433,12 +428,11 @@ static int fsi_i2c_reset_bus(struct fsi_i2c_master *i2c,

static int fsi_i2c_reset_engine(struct fsi_i2c_master *i2c, u16 port)
{
- u32 dummy = 0;
u32 mode;
int rc;

/* reset engine */
- rc = fsi_i2c_write_reg(i2c->fsi, I2C_FSI_RESET_I2C, &dummy);
+ rc = fsi_i2c_write_reg(i2c->fsi, I2C_FSI_RESET_I2C, 0);
if (rc)
return rc;

@@ -455,14 +449,13 @@ static int fsi_i2c_reset_engine(struct fsi_i2c_master *i2c, u16 port)
if (port) {
mode &= ~I2C_MODE_PORT;
mode |= FIELD_PREP(I2C_MODE_PORT, port);
- rc = fsi_i2c_write_reg(i2c->fsi, I2C_FSI_MODE, &mode);
+ rc = fsi_i2c_write_reg(i2c->fsi, I2C_FSI_MODE, mode);
if (rc)
return rc;
}

/* reset busy register; hw workaround */
- dummy = I2C_PORT_BUSY_RESET;
- rc = fsi_i2c_write_reg(i2c->fsi, I2C_FSI_PORT_BUSY, &dummy);
+ rc = fsi_i2c_write_reg(i2c->fsi, I2C_FSI_PORT_BUSY, I2C_PORT_BUSY_RESET);
if (rc)
return rc;

@@ -497,7 +490,7 @@ static int fsi_i2c_abort(struct fsi_i2c_port *port, u32 status)
return 0;

/* write stop command */
- rc = fsi_i2c_write_reg(i2c->fsi, I2C_FSI_CMD, &cmd);
+ rc = fsi_i2c_write_reg(i2c->fsi, I2C_FSI_CMD, cmd);
if (rc)
return rc;

--
2.39.3


2024-02-15 22:14:52

by Eddie James

[permalink] [raw]
Subject: [PATCH 15/33] fsi: aspeed: Use common initialization and link enable

Create a regmap for accessing the master registers over OPB
to use the new common master initialization and link enable procedures.

Signed-off-by: Eddie James <[email protected]>
---
drivers/fsi/fsi-master-aspeed.c | 146 ++++++++------------------------
1 file changed, 37 insertions(+), 109 deletions(-)

diff --git a/drivers/fsi/fsi-master-aspeed.c b/drivers/fsi/fsi-master-aspeed.c
index d6e923b8f501..2258980e4c47 100644
--- a/drivers/fsi/fsi-master-aspeed.c
+++ b/drivers/fsi/fsi-master-aspeed.c
@@ -90,14 +90,6 @@ static const u32 fsi_base = 0xa0000000;
#define CREATE_TRACE_POINTS
#include <trace/events/fsi_master_aspeed.h>

-#define FSI_LINK_ENABLE_SETUP_TIME 10 /* in mS */
-
-/* Run the bus at maximum speed by default */
-#define FSI_DIVISOR_DEFAULT 1
-#define FSI_DIVISOR_CABLED 2
-static u16 aspeed_fsi_divisor = FSI_DIVISOR_DEFAULT;
-module_param_named(bus_div,aspeed_fsi_divisor, ushort, 0);
-
#define OPB_POLL_TIMEOUT 500

static int __opb_write(struct fsi_master_aspeed *aspeed, u32 addr,
@@ -328,35 +320,6 @@ static int aspeed_master_write(struct fsi_master *master, int link,
return ret;
}

-static int aspeed_master_link_enable(struct fsi_master *master, int link,
- bool enable)
-{
- struct fsi_master_aspeed *aspeed = to_fsi_master_aspeed(master);
- int idx, bit, ret;
- __be32 reg;
-
- idx = link / 32;
- bit = link % 32;
-
- reg = cpu_to_be32(0x80000000 >> bit);
-
- mutex_lock(&aspeed->lock);
-
- if (!enable) {
- ret = opb_writel(aspeed, ctrl_base + FSI_MCENP0 + (4 * idx), reg);
- goto done;
- }
-
- ret = opb_writel(aspeed, ctrl_base + FSI_MSENP0 + (4 * idx), reg);
- if (ret)
- goto done;
-
- mdelay(FSI_LINK_ENABLE_SETUP_TIME);
-done:
- mutex_unlock(&aspeed->lock);
- return ret;
-}
-
static int aspeed_master_term(struct fsi_master *master, int link, uint8_t id)
{
uint32_t addr;
@@ -384,72 +347,33 @@ static void aspeed_master_release(struct device *dev)
struct fsi_master_aspeed *aspeed =
to_fsi_master_aspeed(to_fsi_master(dev));

+ regmap_exit(aspeed->master.map);
kfree(aspeed);
}

-/* mmode encoders */
-static inline u32 fsi_mmode_crs0(u32 x)
+static int regmap_aspeed_opb_read(void *context, unsigned int reg, unsigned int *val)
{
- return (x & FSI_MMODE_CRS0MASK) << FSI_MMODE_CRS0SHFT;
-}
+ __be32 v;
+ int ret;

-static inline u32 fsi_mmode_crs1(u32 x)
-{
- return (x & FSI_MMODE_CRS1MASK) << FSI_MMODE_CRS1SHFT;
+ ret = opb_readl(context, ctrl_base + reg, &v);
+ if (ret)
+ return ret;
+
+ *val = be32_to_cpu(v);
+ return 0;
}

-static int aspeed_master_init(struct fsi_master_aspeed *aspeed)
+static int regmap_aspeed_opb_write(void *context, unsigned int reg, unsigned int val)
{
- __be32 reg;
-
- reg = cpu_to_be32(FSI_MRESP_RST_ALL_MASTER | FSI_MRESP_RST_ALL_LINK
- | FSI_MRESP_RST_MCR | FSI_MRESP_RST_PYE);
- opb_writel(aspeed, ctrl_base + FSI_MRESP0, reg);
-
- /* Initialize the MFSI (hub master) engine */
- reg = cpu_to_be32(FSI_MRESP_RST_ALL_MASTER | FSI_MRESP_RST_ALL_LINK
- | FSI_MRESP_RST_MCR | FSI_MRESP_RST_PYE);
- opb_writel(aspeed, ctrl_base + FSI_MRESP0, reg);
-
- reg = cpu_to_be32(FSI_MECTRL_EOAE | FSI_MECTRL_P8_AUTO_TERM);
- opb_writel(aspeed, ctrl_base + FSI_MECTRL, reg);
-
- reg = cpu_to_be32(FSI_MMODE_ECRC | FSI_MMODE_EPC | FSI_MMODE_RELA
- | fsi_mmode_crs0(aspeed_fsi_divisor)
- | fsi_mmode_crs1(aspeed_fsi_divisor)
- | FSI_MMODE_P8_TO_LSB);
- dev_info(aspeed->dev, "mmode set to %08x (divisor %d)\n",
- be32_to_cpu(reg), aspeed_fsi_divisor);
- opb_writel(aspeed, ctrl_base + FSI_MMODE, reg);
-
- reg = cpu_to_be32(0xffff0000);
- opb_writel(aspeed, ctrl_base + FSI_MDLYR, reg);
-
- reg = cpu_to_be32(~0);
- opb_writel(aspeed, ctrl_base + FSI_MSENP0, reg);
-
- /* Leave enabled long enough for master logic to set up */
- mdelay(FSI_LINK_ENABLE_SETUP_TIME);
-
- opb_writel(aspeed, ctrl_base + FSI_MCENP0, reg);
-
- opb_readl(aspeed, ctrl_base + FSI_MAEB, NULL);
-
- reg = cpu_to_be32(FSI_MRESP_RST_ALL_MASTER | FSI_MRESP_RST_ALL_LINK);
- opb_writel(aspeed, ctrl_base + FSI_MRESP0, reg);
-
- opb_readl(aspeed, ctrl_base + FSI_MLEVP0, NULL);
-
- /* Reset the master bridge */
- reg = cpu_to_be32(FSI_MRESB_RST_GEN);
- opb_writel(aspeed, ctrl_base + FSI_MRESB0, reg);
-
- reg = cpu_to_be32(FSI_MRESB_RST_ERR);
- opb_writel(aspeed, ctrl_base + FSI_MRESB0, reg);
-
- return 0;
+ return opb_writel(context, ctrl_base + reg, cpu_to_be32(val));
}

+static const struct regmap_bus regmap_aspeed_opb = {
+ .reg_write = regmap_aspeed_opb_write,
+ .reg_read = regmap_aspeed_opb_read,
+};
+
static ssize_t cfam_reset_store(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
@@ -521,14 +445,6 @@ static int tacoma_cabled_fsi_fixup(struct device *dev)

/* If the routing GPIO is high we should set the mux to low. */
if (gpio) {
- /*
- * Cable signal integrity means we should run the bus
- * slightly slower. Do not override if a kernel param
- * has already overridden.
- */
- if (aspeed_fsi_divisor == FSI_DIVISOR_DEFAULT)
- aspeed_fsi_divisor = FSI_DIVISOR_CABLED;
-
gpiod_direction_output(mux_gpio, 0);
dev_info(dev, "FSI configured for external cable\n");
} else {
@@ -544,9 +460,10 @@ static int fsi_master_aspeed_probe(struct platform_device *pdev)
{
const struct fsi_master_aspeed_data *md = of_device_get_match_data(&pdev->dev);
u32 opb_retry_counter = md ? md->opb_retry_counter : OPB_RETRY_COUNTER_AST2600;
+ struct regmap_config aspeed_master_regmap_config;
struct fsi_master_aspeed *aspeed;
- int rc, links, reg;
- __be32 raw;
+ unsigned int reg;
+ int rc, links;

rc = tacoma_cabled_fsi_fixup(&pdev->dev);
if (rc) {
@@ -606,13 +523,20 @@ static int fsi_master_aspeed_probe(struct platform_device *pdev)
*/
writel(0x1, aspeed->base + OPB0_SELECT);

- rc = opb_readl(aspeed, ctrl_base + FSI_MVER, &raw);
+ fsi_master_regmap_config(&aspeed_master_regmap_config);
+ aspeed->master.map = regmap_init(&pdev->dev, &regmap_aspeed_opb, aspeed,
+ &aspeed_master_regmap_config);
+ if (IS_ERR(aspeed->master.map)) {
+ rc = PTR_ERR(aspeed->master.map);
+ goto err_release;
+ }
+
+ rc = regmap_read(aspeed->master.map, FSI_MVER, &reg);
if (rc) {
dev_err(&pdev->dev, "failed to read hub version\n");
- goto err_release;
+ goto err_regmap;
}

- reg = be32_to_cpu(raw);
links = (reg >> 8) & 0xff;
dev_info(&pdev->dev, "hub version %08x (%d links)\n", reg, links);

@@ -621,20 +545,22 @@ static int fsi_master_aspeed_probe(struct platform_device *pdev)
aspeed->master.dev.of_node = of_node_get(dev_of_node(&pdev->dev));

aspeed->master.n_links = links;
+ aspeed->master.flags = FSI_MASTER_FLAG_RELA;
aspeed->master.read = aspeed_master_read;
aspeed->master.write = aspeed_master_write;
aspeed->master.send_break = aspeed_master_break;
aspeed->master.term = aspeed_master_term;
- aspeed->master.link_enable = aspeed_master_link_enable;

dev_set_drvdata(&pdev->dev, aspeed);

mutex_init(&aspeed->lock);
- aspeed_master_init(aspeed);
+ rc = fsi_master_init(&aspeed->master, clk_get_rate(aspeed->clk));
+ if (rc)
+ goto err_regmap;

rc = fsi_master_register(&aspeed->master);
if (rc)
- goto err_release;
+ goto err_regmap;

/* At this point, fsi_master_register performs the device_initialize(),
* and holds the sole reference on master.dev. This means the device
@@ -646,6 +572,8 @@ static int fsi_master_aspeed_probe(struct platform_device *pdev)
get_device(&aspeed->master.dev);
return 0;

+err_regmap:
+ regmap_exit(aspeed->master.map);
err_release:
clk_disable_unprepare(aspeed->clk);
err_free_aspeed:
--
2.39.3


2024-02-15 22:14:59

by Eddie James

[permalink] [raw]
Subject: [PATCH 27/33] i2c: fsi: Calculate clock divider from local bus frequency

Use the new FSI device local bus clock to calculate the proper
i2c clock divder and look up an optional clock-frequency property
from device tree. Change the default clock divider to 7 now that
the default local bus clock divider has been reduced as well.

Signed-off-by: Eddie James <[email protected]>
---
drivers/i2c/busses/i2c-fsi.c | 21 +++++++++++++++++++--
1 file changed, 19 insertions(+), 2 deletions(-)

diff --git a/drivers/i2c/busses/i2c-fsi.c b/drivers/i2c/busses/i2c-fsi.c
index 10332693edf0..eaecf156ac31 100644
--- a/drivers/i2c/busses/i2c-fsi.c
+++ b/drivers/i2c/busses/i2c-fsi.c
@@ -27,7 +27,8 @@

#define FSI_ENGID_I2C 0x7

-#define I2C_DEFAULT_CLK_DIV 6
+#define I2C_DEFAULT_CLK_DIV 7
+#define I2C_DEFAULT_CLK_RATE 400000

/* i2c registers */
#define I2C_FSI_FIFO 0x00
@@ -150,6 +151,7 @@ struct fsi_i2c_master {
u8 fifo_size;
struct list_head ports;
struct mutex lock;
+ u32 clock_div;
};

struct fsi_i2c_port {
@@ -194,7 +196,7 @@ static int fsi_i2c_dev_init(struct fsi_i2c_master *i2c)
if (rc)
return rc;

- mode |= FIELD_PREP(I2C_MODE_CLKDIV, I2C_DEFAULT_CLK_DIV);
+ mode |= FIELD_PREP(I2C_MODE_CLKDIV, i2c->clock_div);
rc = fsi_i2c_write_reg(i2c->fsi, I2C_FSI_MODE, &mode);
if (rc)
return rc;
@@ -680,6 +682,7 @@ static int fsi_i2c_probe(struct device *dev)
struct fsi_i2c_port *port;
struct device_node *np;
u32 port_no, ports, stat;
+ u32 lbus;
int rc;

i2c = devm_kzalloc(dev, sizeof(*i2c), GFP_KERNEL);
@@ -689,6 +692,20 @@ static int fsi_i2c_probe(struct device *dev)
mutex_init(&i2c->lock);
i2c->fsi = to_fsi_dev(dev);
INIT_LIST_HEAD(&i2c->ports);
+ i2c->clock_div = I2C_DEFAULT_CLK_DIV;
+
+ lbus = fsi_device_local_bus_frequency(i2c->fsi);
+ if (lbus) {
+ u32 clock = I2C_DEFAULT_CLK_RATE;
+
+ if (!device_property_read_u32(dev, "clock-frequency", &clock)) {
+ if (!clock)
+ clock = I2C_DEFAULT_CLK_RATE;
+ }
+
+ // i2c clock rate = local bus clock / (4 * (i2c clock div + 1))
+ i2c->clock_div = (((lbus + (clock - 1)) / clock) / 4) - 1;
+ }

rc = fsi_i2c_dev_init(i2c);
if (rc)
--
2.39.3


2024-02-15 22:15:06

by Eddie James

[permalink] [raw]
Subject: [PATCH 33/33] i2c: fsi: Add interrupt support

Optionally support interrupts from the I2C controller so that
the driver can wait rather than poll the status register.

Signed-off-by: Eddie James <[email protected]>
---
drivers/i2c/busses/i2c-fsi.c | 215 ++++++++++++++++++++++++++++++---
include/trace/events/i2c_fsi.h | 45 +++++++
2 files changed, 245 insertions(+), 15 deletions(-)
create mode 100644 include/trace/events/i2c_fsi.h

diff --git a/drivers/i2c/busses/i2c-fsi.c b/drivers/i2c/busses/i2c-fsi.c
index 33f4e64cb60b..096edeaa3312 100644
--- a/drivers/i2c/busses/i2c-fsi.c
+++ b/drivers/i2c/busses/i2c-fsi.c
@@ -23,6 +23,7 @@
#include <linux/mutex.h>
#include <linux/of.h>
#include <linux/slab.h>
+#include <linux/wait.h>

#define FSI_ENGID_I2C 0x7

@@ -87,6 +88,7 @@
#define I2C_INT_STOP_ERR BIT(7)
#define I2C_INT_BUSY BIT(6)
#define I2C_INT_IDLE BIT(5)
+#define I2C_INT_ANY GENMASK(15, 7)

/* status register */
#define I2C_STAT_INV_CMD BIT(31)
@@ -148,21 +150,35 @@
/* choose timeout length from legacy driver; it's well tested */
#define I2C_ABORT_TIMEOUT msecs_to_jiffies(100)

+struct fsi_i2c_port;
+
struct fsi_i2c_master {
struct fsi_device *fsi;
+ struct fsi_i2c_port *port;
struct mutex lock;
+ wait_queue_head_t wait;
u32 clock_div;
u8 fifo_size;
+ bool interrupts;
bool skip_stop;
+ bool abort;
};

struct fsi_i2c_port {
struct i2c_adapter adapter;
struct fsi_i2c_master *master;
+ struct i2c_msg *msgs;
+ int nmsgs;
+ int rc;
+ int i;
u16 port;
u16 xfrd;
+ bool wake;
};

+#define CREATE_TRACE_POINTS
+#include <trace/events/i2c_fsi.h>
+
static int fsi_i2c_read_reg(struct fsi_device *fsi, unsigned int reg,
u32 *data)
{
@@ -192,7 +208,7 @@ static int fsi_i2c_dev_init(struct fsi_i2c_master *i2c)
u32 watermark;
int rc;

- /* since we use polling, disable interrupts */
+ /* start with interrupts disabled */
rc = fsi_i2c_write_reg(i2c->fsi, I2C_FSI_INT_MASK, 0);
if (rc)
return rc;
@@ -236,22 +252,24 @@ static int fsi_i2c_set_port(struct fsi_i2c_port *port)
return fsi_i2c_write_reg(fsi, I2C_FSI_RESET_ERR, 0);
}

-static int fsi_i2c_start(struct fsi_i2c_port *port, struct i2c_msg *msg,
- bool stop)
+static int fsi_i2c_start(struct fsi_i2c_port *port)
{
u32 cmd = I2C_CMD_WITH_START | I2C_CMD_WITH_ADDR;
+ struct i2c_msg *msg = &port->msgs[port->i];

port->xfrd = 0;

if (msg->flags & I2C_M_RD)
cmd |= I2C_CMD_READ;

- if (stop || msg->flags & I2C_M_STOP)
+ if ((port->i == (port->nmsgs - 1)) || (msg->flags & I2C_M_STOP))
cmd |= I2C_CMD_WITH_STOP;

cmd |= FIELD_PREP(I2C_CMD_ADDR, msg->addr);
cmd |= FIELD_PREP(I2C_CMD_LEN, msg->len);

+ trace_i2c_fsi_start(port, cmd);
+
return fsi_i2c_write_reg(port->master->fsi, I2C_FSI_CMD, cmd);
}

@@ -489,11 +507,38 @@ static int fsi_i2c_abort(struct fsi_i2c_port *port)
if (i2c->skip_stop)
return 0;

+ if (i2c->interrupts) {
+ i2c->abort = true;
+ port->wake = false;
+
+ rc = fsi_i2c_write_reg(i2c->fsi, I2C_FSI_INT_MASK, I2C_INT_ANY);
+ if (rc)
+ return rc;
+ }
+
/* write stop command */
rc = fsi_i2c_write_reg(i2c->fsi, I2C_FSI_CMD, cmd);
if (rc)
return rc;

+ if (i2c->interrupts) {
+ rc = wait_event_interruptible_timeout(i2c->wait, port->wake, I2C_ABORT_TIMEOUT);
+ if (rc > 0)
+ return port->rc;
+
+ fsi_i2c_write_reg(i2c->fsi, I2C_FSI_INT_MASK, 0);
+
+ if (!rc) {
+ rc = fsi_i2c_read_reg(i2c->fsi, I2C_FSI_STAT, &status);
+ if (!rc && (status & I2C_STAT_CMD_COMP))
+ rc = 0;
+ else
+ rc = -ETIMEDOUT;
+ }
+
+ return rc;
+ }
+
/* wait until we see command complete in the master */
start = jiffies;
do {
@@ -564,8 +609,59 @@ static int fsi_i2c_handle_status(struct fsi_i2c_port *port,
return 0;
}

-static int fsi_i2c_wait(struct fsi_i2c_port *port, struct i2c_msg *msg,
- unsigned long timeout)
+static int fsi_i2c_wait_irq(struct fsi_i2c_port *port, unsigned long timeout)
+{
+ int rc;
+
+ port->wake = false;
+
+ rc = fsi_i2c_write_reg(port->master->fsi, I2C_FSI_INT_MASK, I2C_INT_ANY);
+ if (rc)
+ return rc;
+
+ rc = wait_event_interruptible_timeout(port->master->wait, port->wake, timeout);
+ if (rc > 0) {
+ rc = port->rc;
+
+ if (port->master->abort) {
+ int rc2 = fsi_i2c_abort(port);
+
+ if (rc2)
+ return rc2;
+ }
+
+ return rc;
+ }
+
+ /*
+ * The interrupt handler should turn off interrupts once it's done, but in this
+ * case we timed out or were interrupted, so mask them off here.
+ */
+ fsi_i2c_write_reg(port->master->fsi, I2C_FSI_INT_MASK, 0);
+
+ if (!rc) {
+ u32 status;
+
+ rc = fsi_i2c_read_reg(port->master->fsi, I2C_FSI_STAT, &status);
+ if (!rc && (status & I2C_STAT_ANY_RESP)) {
+ rc = fsi_i2c_handle_status(port, &port->msgs[port->i], status);
+ if (rc < 0)
+ return rc;
+
+ /* cmd complete and all data xfrd */
+ if (rc == port->msgs[port->i].len)
+ return 0;
+
+ rc = -ETIMEDOUT;
+ } else {
+ rc = -ETIMEDOUT;
+ }
+ }
+
+ return rc;
+}
+
+static int fsi_i2c_wait_poll(struct fsi_i2c_port *port, unsigned long timeout)
{
unsigned long start = jiffies;
u32 status;
@@ -578,12 +674,12 @@ static int fsi_i2c_wait(struct fsi_i2c_port *port, struct i2c_msg *msg,
return rc;

if (status & I2C_STAT_ANY_RESP) {
- rc = fsi_i2c_handle_status(port, msg, status);
+ rc = fsi_i2c_handle_status(port, &port->msgs[port->i], status);
if (rc < 0)
return rc;

/* cmd complete and all data xfrd */
- if (rc == msg->len)
+ if (rc == port->msgs[port->i].len)
return 0;

/* need to xfr more data, but maybe don't need wait */
@@ -601,9 +697,7 @@ static int fsi_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs,
{
struct fsi_i2c_port *port = i2c_get_adapdata(adap);
unsigned long start_time;
- struct i2c_msg *msg;
int rc;
- int i;

mutex_lock(&port->master->lock);

@@ -611,21 +705,28 @@ static int fsi_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs,
if (rc)
goto unlock;

- for (i = 0; i < num; i++) {
- msg = msgs + i;
+ port->master->port = port;
+ port->master->abort = false;
+ port->msgs = msgs;
+ port->nmsgs = num;
+ for (port->i = 0; port->i < num; ++port->i) {
start_time = jiffies;

- rc = fsi_i2c_start(port, msg, i == num - 1);
+ rc = fsi_i2c_start(port);
if (rc)
goto unlock;

- rc = fsi_i2c_wait(port, msg,
- adap->timeout - (jiffies - start_time));
+ if (port->master->interrupts)
+ rc = fsi_i2c_wait_irq(port, adap->timeout - (jiffies - start_time));
+ else
+ rc = fsi_i2c_wait_poll(port, adap->timeout - (jiffies - start_time));
if (rc)
goto unlock;
}

unlock:
+ port->msgs = NULL;
+ port->master->port = NULL;
mutex_unlock(&port->master->lock);
return rc ? : num;
}
@@ -636,6 +737,85 @@ static u32 fsi_i2c_functionality(struct i2c_adapter *adap)
I2C_FUNC_SMBUS_EMUL | I2C_FUNC_SMBUS_BLOCK_DATA;
}

+static irqreturn_t fsi_i2c_irq(int irq, void *data)
+{
+ struct fsi_i2c_master *i2c = data;
+ struct fsi_i2c_port *port;
+ struct i2c_msg *msg;
+ u32 status;
+ int rc;
+
+ rc = fsi_i2c_write_reg(i2c->fsi, I2C_FSI_INT_MASK, 0);
+ if (rc)
+ return IRQ_NONE;
+
+ if (!i2c->port)
+ return IRQ_HANDLED;
+
+ port = i2c->port;
+ rc = fsi_i2c_read_reg(i2c->fsi, I2C_FSI_STAT, &status);
+ if (rc)
+ goto wake;
+
+ trace_i2c_fsi_irq(port, status);
+
+ if (i2c->abort) {
+ if (status & I2C_STAT_CMD_COMP) {
+ port->wake = true;
+ goto done;
+ } else {
+ rc = fsi_i2c_error_status_to_rc(status);
+ goto wake;
+ }
+ }
+
+ if (status & I2C_STAT_ERR) {
+ i2c->abort = true;
+ i2c->skip_stop = status & I2C_STAT_SKIP_STOP;
+ rc = fsi_i2c_error_status_to_rc(status);
+ goto wake;
+ }
+
+ if (!port->msgs || port->i >= port->nmsgs) {
+ rc = -ENODEV;
+ goto wake;
+ }
+
+ msg = &port->msgs[port->i];
+ if (status & I2C_STAT_DAT_REQ) {
+ u8 fifo_count = FIELD_GET(I2C_STAT_FIFO_COUNT, status);
+
+ if (msg->flags & I2C_M_RD)
+ rc = fsi_i2c_read_fifo(port, msg, fifo_count);
+ else
+ rc = fsi_i2c_write_fifo(port, msg, fifo_count);
+ } else if (status & I2C_STAT_CMD_COMP) {
+ if (port->xfrd < msg->len) {
+ rc = -ENODATA;
+ } else {
+ ++port->i;
+ if (port->i < port->nmsgs) {
+ rc = fsi_i2c_start(port);
+ } else {
+ port->wake = true;
+ goto done;
+ }
+ }
+ }
+
+ if (!rc)
+ rc = fsi_i2c_write_reg(i2c->fsi, I2C_FSI_INT_MASK, I2C_INT_ANY);
+
+wake:
+ if (rc)
+ port->wake = true;
+done:
+ port->rc = rc;
+ if (port->wake)
+ wake_up_interruptible_all(&i2c->wait);
+ return IRQ_HANDLED;
+}
+
static struct i2c_bus_recovery_info fsi_i2c_bus_recovery_info = {
.recover_bus = i2c_generic_scl_recovery,
.get_scl = fsi_i2c_get_scl,
@@ -683,6 +863,7 @@ static int fsi_i2c_probe(struct device *dev)
return -ENOMEM;

mutex_init(&i2c->lock);
+ init_waitqueue_head(&i2c->wait);
i2c->fsi = to_fsi_dev(dev);
i2c->clock_div = I2C_DEFAULT_CLK_DIV;

@@ -707,6 +888,10 @@ static int fsi_i2c_probe(struct device *dev)
if (rc)
return rc;

+ rc = fsi_device_request_irq(i2c->fsi, fsi_i2c_irq, i2c);
+ if (!rc)
+ i2c->interrupts = true;
+
ports = FIELD_GET(I2C_STAT_MAX_PORT, stat) + 1;
dev_dbg(dev, "I2C master has %d ports\n", ports);

diff --git a/include/trace/events/i2c_fsi.h b/include/trace/events/i2c_fsi.h
new file mode 100644
index 000000000000..691fb2adf454
--- /dev/null
+++ b/include/trace/events/i2c_fsi.h
@@ -0,0 +1,45 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+#undef TRACE_SYSTEM
+#define TRACE_SYSTEM i2c_fsi
+
+#if !defined(_TRACE_I2C_FSI_H) || defined(TRACE_HEADER_MULTI_READ)
+#define _TRACE_I2C_FSI_H
+
+#include <linux/tracepoint.h>
+
+TRACE_EVENT(i2c_fsi_irq,
+ TP_PROTO(const struct fsi_i2c_port *port, uint32_t status),
+ TP_ARGS(port, status),
+ TP_STRUCT__entry(
+ __field(int, bus)
+ __field(int, msg_idx)
+ __field(uint32_t, status)
+ ),
+ TP_fast_assign(
+ __entry->bus = port->adapter.nr;
+ __entry->msg_idx = port->i;
+ __entry->status = status;
+ ),
+ TP_printk("i2c-%d status: %08x", __entry->bus, __entry->status)
+);
+
+TRACE_EVENT(i2c_fsi_start,
+ TP_PROTO(const struct fsi_i2c_port *port, uint32_t command),
+ TP_ARGS(port, command),
+ TP_STRUCT__entry(
+ __field(int, bus)
+ __field(int, msg_idx)
+ __field(uint32_t, command)
+ ),
+ TP_fast_assign(
+ __entry->bus = port->adapter.nr;
+ __entry->msg_idx = port->i;
+ __entry->command = command;
+ ),
+ TP_printk("i2c-%d command: %08x", __entry->bus, __entry->command)
+);
+
+#endif
+
+#include <trace/define_trace.h>
--
2.39.3


2024-02-15 22:16:27

by Eddie James

[permalink] [raw]
Subject: [PATCH 02/33] clk: ast2600: Add FSI parent clock with correct rate

In order to calculate correct FSI bus clocks, the FSI clock must
correctly calculate the rate from the parent (APLL / 4).

Signed-off-by: Eddie James <[email protected]>
---
drivers/clk/clk-ast2600.c | 7 +++++--
1 file changed, 5 insertions(+), 2 deletions(-)

diff --git a/drivers/clk/clk-ast2600.c b/drivers/clk/clk-ast2600.c
index 909c3137c428..faf88324f7b1 100644
--- a/drivers/clk/clk-ast2600.c
+++ b/drivers/clk/clk-ast2600.c
@@ -19,7 +19,7 @@
* This includes the gates (configured from aspeed_g6_gates), plus the
* explicitly-configured clocks (ASPEED_CLK_HPLL and up).
*/
-#define ASPEED_G6_NUM_CLKS 72
+#define ASPEED_G6_NUM_CLKS 73

#define ASPEED_G6_SILICON_REV 0x014
#define CHIP_REVISION_ID GENMASK(23, 16)
@@ -157,7 +157,7 @@ static const struct aspeed_gate_data aspeed_g6_gates[] = {
[ASPEED_CLK_GATE_UART11CLK] = { 59, -1, "uart11clk-gate", "uartx", 0 }, /* UART11 */
[ASPEED_CLK_GATE_UART12CLK] = { 60, -1, "uart12clk-gate", "uartx", 0 }, /* UART12 */
[ASPEED_CLK_GATE_UART13CLK] = { 61, -1, "uart13clk-gate", "uartx", 0 }, /* UART13 */
- [ASPEED_CLK_GATE_FSICLK] = { 62, 59, "fsiclk-gate", NULL, 0 }, /* FSI */
+ [ASPEED_CLK_GATE_FSICLK] = { 62, 59, "fsiclk-gate", "fsiclk", 0 }, /* FSI */
};

static const struct clk_div_table ast2600_eclk_div_table[] = {
@@ -821,6 +821,9 @@ static void __init aspeed_g6_cc(struct regmap *map)

hw = clk_hw_register_fixed_factor(NULL, "i3cclk", "apll", 0, 1, 8);
aspeed_g6_clk_data->hws[ASPEED_CLK_I3C] = hw;
+
+ hw = clk_hw_register_fixed_factor(NULL, "fsiclk", "apll", 0, 1, 4);
+ aspeed_g6_clk_data->hws[ASPEED_CLK_FSI] = hw;
};

static void __init aspeed_g6_cc_init(struct device_node *np)
--
2.39.3


2024-02-15 22:16:28

by Eddie James

[permalink] [raw]
Subject: [PATCH 06/33] fsi: Expose master-specific local bus clock divider

Instead of a hardcoded local bus clock divider, allow master drivers
to select their local bus clock divider. Also expose master clock
frequencies in the structure. This will allow FSI engine drivers
to set and calculate their bus (I2C, SPI, etc) frequencies.

Signed-off-by: Eddie James <[email protected]>
---
drivers/fsi/fsi-core.c | 27 ++++++++++++++++++++-------
drivers/fsi/fsi-master-hub.c | 1 +
drivers/fsi/fsi-master.h | 2 ++
drivers/fsi/fsi-slave.h | 1 +
include/linux/fsi.h | 1 +
5 files changed, 25 insertions(+), 7 deletions(-)

diff --git a/drivers/fsi/fsi-core.c b/drivers/fsi/fsi-core.c
index 8823e8e56dab..9610b5948550 100644
--- a/drivers/fsi/fsi-core.c
+++ b/drivers/fsi/fsi-core.c
@@ -109,6 +109,12 @@ int fsi_device_peek(struct fsi_device *dev, void *val)
return fsi_slave_read(dev->slave, addr, val, sizeof(uint32_t));
}

+unsigned long fsi_device_local_bus_frequency(struct fsi_device *dev)
+{
+ return dev->slave->master->clock_frequency / dev->slave->master->lbus_divider;
+}
+EXPORT_SYMBOL_GPL(fsi_device_local_bus_frequency);
+
static void fsi_device_release(struct device *_device)
{
struct fsi_device *device = to_fsi_dev(_device);
@@ -209,15 +215,15 @@ static inline uint32_t fsi_smode_sid(int x)
return (x & FSI_SMODE_SID_MASK) << FSI_SMODE_SID_SHIFT;
}

-static uint32_t fsi_slave_smode(int id, u8 t_senddly, u8 t_echodly)
+static uint32_t fsi_slave_smode(int id, int div, u8 t_senddly, u8 t_echodly)
{
return FSI_SMODE_WSC | FSI_SMODE_ECRC
| fsi_smode_sid(id)
| fsi_smode_echodly(t_echodly - 1) | fsi_smode_senddly(t_senddly - 1)
- | fsi_smode_lbcrr(0x8);
+ | fsi_smode_lbcrr(div - 1);
}

-static int fsi_slave_set_smode(struct fsi_slave *slave)
+static int fsi_slave_set_smode(struct fsi_slave *slave, int lbus_divider)
{
uint32_t smode;
__be32 data;
@@ -225,7 +231,8 @@ static int fsi_slave_set_smode(struct fsi_slave *slave)
/* set our smode register with the slave ID field to 0; this enables
* extended slave addressing
*/
- smode = fsi_slave_smode(slave->id, slave->t_send_delay, slave->t_echo_delay);
+ smode = fsi_slave_smode(slave->id, lbus_divider, slave->t_send_delay,
+ slave->t_echo_delay);
data = cpu_to_be32(smode);

return fsi_master_write(slave->master, slave->link, slave->id,
@@ -281,7 +288,7 @@ static int fsi_slave_handle_error(struct fsi_slave *slave, bool write,
slave->t_send_delay = send_delay;
slave->t_echo_delay = echo_delay;

- rc = fsi_slave_set_smode(slave);
+ rc = fsi_slave_set_smode(slave, master->lbus_divider);
if (rc)
return rc;

@@ -773,7 +780,7 @@ static ssize_t slave_send_echo_store(struct device *dev,
slave->t_send_delay = val;
slave->t_echo_delay = val;

- rc = fsi_slave_set_smode(slave);
+ rc = fsi_slave_set_smode(slave, master->lbus_divider);
if (rc < 0)
return rc;
if (master->link_config)
@@ -1028,7 +1035,7 @@ static int fsi_slave_init(struct fsi_master *master, int link, uint8_t id)
"can't set slbus on slave:%02x:%02x %d\n", link, id,
rc);

- rc = fsi_slave_set_smode(slave);
+ rc = fsi_slave_set_smode(slave, master->lbus_divider);
if (rc) {
dev_warn(&master->dev,
"can't set smode on slave:%02x:%02x %d\n",
@@ -1288,6 +1295,12 @@ int fsi_master_register(struct fsi_master *master)
if (!dev_name(&master->dev))
dev_set_name(&master->dev, "fsi%d", master->idx);

+ if (master->flags & FSI_MASTER_FLAG_SWCLOCK)
+ master->clock_frequency = 100000000; // POWER reference clock
+
+ if (!master->lbus_divider)
+ master->lbus_divider = FSI_SMODE_LBCRR_DEFAULT;
+
master->dev.class = &fsi_master_class;

mutex_lock(&master->scan_lock);
diff --git a/drivers/fsi/fsi-master-hub.c b/drivers/fsi/fsi-master-hub.c
index 36da643b3201..111bf7a11458 100644
--- a/drivers/fsi/fsi-master-hub.c
+++ b/drivers/fsi/fsi-master-hub.c
@@ -230,6 +230,7 @@ static int hub_master_probe(struct device *dev)
hub->master.dev.release = hub_master_release;
hub->master.dev.of_node = of_node_get(dev_of_node(dev));

+ hub->master.lbus_divider = 1;
hub->master.idx = fsi_dev->slave->link + 1;
hub->master.n_links = links;
hub->master.read = hub_master_read;
diff --git a/drivers/fsi/fsi-master.h b/drivers/fsi/fsi-master.h
index 967622c1cabf..26e636ad9ef6 100644
--- a/drivers/fsi/fsi-master.h
+++ b/drivers/fsi/fsi-master.h
@@ -120,6 +120,8 @@

struct fsi_master {
struct device dev;
+ unsigned long clock_frequency;
+ int lbus_divider;
int idx;
int n_links;
int flags;
diff --git a/drivers/fsi/fsi-slave.h b/drivers/fsi/fsi-slave.h
index dca9db0a42e5..42af2fae0329 100644
--- a/drivers/fsi/fsi-slave.h
+++ b/drivers/fsi/fsi-slave.h
@@ -47,6 +47,7 @@
#define FSI_SMODE_SD_DEFAULT 16 /* Default send delay */
#define FSI_SMODE_LBCRR_SHIFT 8 /* Clk ratio shift */
#define FSI_SMODE_LBCRR_MASK 0xf /* Clk ratio mask */
+#define FSI_SMODE_LBCRR_DEFAULT 8 /* Default clk ratio */

/*
* SISS fields
diff --git a/include/linux/fsi.h b/include/linux/fsi.h
index 3df8c54868df..e0309bf0ae07 100644
--- a/include/linux/fsi.h
+++ b/include/linux/fsi.h
@@ -24,6 +24,7 @@ extern int fsi_device_read(struct fsi_device *dev, uint32_t addr,
extern int fsi_device_write(struct fsi_device *dev, uint32_t addr,
const void *val, size_t size);
extern int fsi_device_peek(struct fsi_device *dev, void *val);
+extern unsigned long fsi_device_local_bus_frequency(struct fsi_device *dev);

struct fsi_device_id {
u8 engine_type;
--
2.39.3


2024-02-15 22:17:10

by Eddie James

[permalink] [raw]
Subject: [PATCH 14/33] fsi: hub: Use common initialization and link enable

Set up an FSI regmap for the hub master to use the new common
master initialization and link enable procedures.

Signed-off-by: Eddie James <[email protected]>
---
drivers/fsi/fsi-master-hub.c | 141 ++++++-----------------------------
1 file changed, 23 insertions(+), 118 deletions(-)

diff --git a/drivers/fsi/fsi-master-hub.c b/drivers/fsi/fsi-master-hub.c
index 111bf7a11458..92aa07055c56 100644
--- a/drivers/fsi/fsi-master-hub.c
+++ b/drivers/fsi/fsi-master-hub.c
@@ -9,6 +9,7 @@
#include <linux/fsi.h>
#include <linux/module.h>
#include <linux/of.h>
+#include <linux/regmap.h>
#include <linux/slab.h>

#include "fsi-master.h"
@@ -16,8 +17,6 @@

#define FSI_ENGID_HUB_MASTER 0x1c

-#define FSI_LINK_ENABLE_SETUP_TIME 10 /* in mS */
-
/*
* FSI hub master support
*
@@ -78,134 +77,33 @@ static int hub_master_break(struct fsi_master *master, int link)
return hub_master_write(master, link, 0, addr, &cmd, sizeof(cmd));
}

-static int hub_master_link_enable(struct fsi_master *master, int link,
- bool enable)
-{
- struct fsi_master_hub *hub = to_fsi_master_hub(master);
- int idx, bit;
- __be32 reg;
- int rc;
-
- idx = link / 32;
- bit = link % 32;
-
- reg = cpu_to_be32(0x80000000 >> bit);
-
- if (!enable)
- return fsi_device_write(hub->upstream, FSI_MCENP0 + (4 * idx),
- &reg, 4);
-
- rc = fsi_device_write(hub->upstream, FSI_MSENP0 + (4 * idx), &reg, 4);
- if (rc)
- return rc;
-
- mdelay(FSI_LINK_ENABLE_SETUP_TIME);
-
- return 0;
-}
-
static void hub_master_release(struct device *dev)
{
struct fsi_master_hub *hub = to_fsi_master_hub(to_fsi_master(dev));

+ regmap_exit(hub->master.map);
kfree(hub);
}

-/* mmode encoders */
-static inline u32 fsi_mmode_crs0(u32 x)
-{
- return (x & FSI_MMODE_CRS0MASK) << FSI_MMODE_CRS0SHFT;
-}
-
-static inline u32 fsi_mmode_crs1(u32 x)
-{
- return (x & FSI_MMODE_CRS1MASK) << FSI_MMODE_CRS1SHFT;
-}
-
-static int hub_master_init(struct fsi_master_hub *hub)
-{
- struct fsi_device *dev = hub->upstream;
- __be32 reg;
- int rc;
-
- reg = cpu_to_be32(FSI_MRESP_RST_ALL_MASTER | FSI_MRESP_RST_ALL_LINK
- | FSI_MRESP_RST_MCR | FSI_MRESP_RST_PYE);
- rc = fsi_device_write(dev, FSI_MRESP0, &reg, sizeof(reg));
- if (rc)
- return rc;
-
- /* Initialize the MFSI (hub master) engine */
- reg = cpu_to_be32(FSI_MRESP_RST_ALL_MASTER | FSI_MRESP_RST_ALL_LINK
- | FSI_MRESP_RST_MCR | FSI_MRESP_RST_PYE);
- rc = fsi_device_write(dev, FSI_MRESP0, &reg, sizeof(reg));
- if (rc)
- return rc;
-
- reg = cpu_to_be32(FSI_MECTRL_EOAE | FSI_MECTRL_P8_AUTO_TERM);
- rc = fsi_device_write(dev, FSI_MECTRL, &reg, sizeof(reg));
- if (rc)
- return rc;
-
- reg = cpu_to_be32(FSI_MMODE_EIP | FSI_MMODE_ECRC | FSI_MMODE_EPC
- | fsi_mmode_crs0(1) | fsi_mmode_crs1(1)
- | FSI_MMODE_P8_TO_LSB);
- rc = fsi_device_write(dev, FSI_MMODE, &reg, sizeof(reg));
- if (rc)
- return rc;
-
- reg = cpu_to_be32(0xffff0000);
- rc = fsi_device_write(dev, FSI_MDLYR, &reg, sizeof(reg));
- if (rc)
- return rc;
-
- reg = cpu_to_be32(~0);
- rc = fsi_device_write(dev, FSI_MSENP0, &reg, sizeof(reg));
- if (rc)
- return rc;
-
- /* Leave enabled long enough for master logic to set up */
- mdelay(FSI_LINK_ENABLE_SETUP_TIME);
-
- rc = fsi_device_write(dev, FSI_MCENP0, &reg, sizeof(reg));
- if (rc)
- return rc;
-
- rc = fsi_device_read(dev, FSI_MAEB, &reg, sizeof(reg));
- if (rc)
- return rc;
-
- reg = cpu_to_be32(FSI_MRESP_RST_ALL_MASTER | FSI_MRESP_RST_ALL_LINK);
- rc = fsi_device_write(dev, FSI_MRESP0, &reg, sizeof(reg));
- if (rc)
- return rc;
-
- rc = fsi_device_read(dev, FSI_MLEVP0, &reg, sizeof(reg));
- if (rc)
- return rc;
-
- /* Reset the master bridge */
- reg = cpu_to_be32(FSI_MRESB_RST_GEN);
- rc = fsi_device_write(dev, FSI_MRESB0, &reg, sizeof(reg));
- if (rc)
- return rc;
-
- reg = cpu_to_be32(FSI_MRESB_RST_ERR);
- return fsi_device_write(dev, FSI_MRESB0, &reg, sizeof(reg));
-}
-
static int hub_master_probe(struct device *dev)
{
+ struct regmap_config hub_master_regmap_config;
struct fsi_device *fsi_dev = to_fsi_dev(dev);
struct fsi_master_hub *hub;
+ struct regmap *map;
uint32_t reg, links;
- __be32 __reg;
int rc;

- rc = fsi_device_read(fsi_dev, FSI_MVER, &__reg, sizeof(__reg));
+ fsi_master_regmap_config(&hub_master_regmap_config);
+ hub_master_regmap_config.reg_base = fsi_dev->addr;
+ map = regmap_init_fsi(fsi_dev, &hub_master_regmap_config);
+ if (IS_ERR(map))
+ return PTR_ERR(map);
+
+ rc = regmap_read(map, FSI_MVER, &reg);
if (rc)
- return rc;
+ goto err_regmap;

- reg = be32_to_cpu(__reg);
links = (reg >> 8) & 0xff;
dev_dbg(dev, "hub version %08x (%d links)\n", reg, links);

@@ -213,7 +111,7 @@ static int hub_master_probe(struct device *dev)
FSI_HUB_LINK_SIZE * links);
if (rc) {
dev_err(dev, "can't claim slave address range for links");
- return rc;
+ goto err_regmap;
}

hub = kzalloc(sizeof(*hub), GFP_KERNEL);
@@ -229,22 +127,25 @@ static int hub_master_probe(struct device *dev)
hub->master.dev.parent = dev;
hub->master.dev.release = hub_master_release;
hub->master.dev.of_node = of_node_get(dev_of_node(dev));
+ hub->master.map = map;

hub->master.lbus_divider = 1;
hub->master.idx = fsi_dev->slave->link + 1;
hub->master.n_links = links;
+ hub->master.flags = FSI_MASTER_FLAG_INTERRUPT;
hub->master.read = hub_master_read;
hub->master.write = hub_master_write;
hub->master.send_break = hub_master_break;
- hub->master.link_enable = hub_master_link_enable;

dev_set_drvdata(dev, hub);

- hub_master_init(hub);
+ rc = fsi_master_init(&hub->master, fsi_device_local_bus_frequency(fsi_dev));
+ if (rc)
+ goto err_free;

rc = fsi_master_register(&hub->master);
if (rc)
- goto err_release;
+ goto err_free;

/* At this point, fsi_master_register performs the device_initialize(),
* and holds the sole reference on master.dev. This means the device
@@ -256,9 +157,13 @@ static int hub_master_probe(struct device *dev)
get_device(&hub->master.dev);
return 0;

+err_free:
+ kfree(hub);
err_release:
fsi_slave_release_range(fsi_dev->slave, FSI_HUB_LINK_OFFSET,
FSI_HUB_LINK_SIZE * links);
+err_regmap:
+ regmap_exit(map);
return rc;
}

--
2.39.3


2024-02-15 22:21:46

by Eddie James

[permalink] [raw]
Subject: [PATCH 13/33] fsi: core: Add common regmap master functions

For hardware FSI masters (Aspeed and hub at the moment), the
initialization, link enable, and error recovery procedures are
common. Add a regmap pointer to the master structure so that master
drivers can let the common code handle these procedures.

Signed-off-by: Eddie James <[email protected]>
---
drivers/fsi/Kconfig | 2 +
drivers/fsi/fsi-core.c | 161 ++++++++++++++++++++++++++++++++++++-
drivers/fsi/fsi-master.h | 16 ++++
include/trace/events/fsi.h | 17 ++++
4 files changed, 194 insertions(+), 2 deletions(-)

diff --git a/drivers/fsi/Kconfig b/drivers/fsi/Kconfig
index 79a31593618a..a6760870538d 100644
--- a/drivers/fsi/Kconfig
+++ b/drivers/fsi/Kconfig
@@ -7,6 +7,7 @@ menuconfig FSI
tristate "FSI support"
depends on OF
select CRC4
+ select REGMAP
help
FSI - the FRU Support Interface - is a simple bus for low-level
access to POWER-based hardware.
@@ -37,6 +38,7 @@ config FSI_MASTER_GPIO

config FSI_MASTER_HUB
tristate "FSI hub master"
+ select REGMAP_FSI
help
This option enables a FSI hub master driver. Hub is a type of FSI
master that is connected to the upstream master via a slave. Hubs
diff --git a/drivers/fsi/fsi-core.c b/drivers/fsi/fsi-core.c
index 95f6ce81f8f4..693e7c51b4af 100644
--- a/drivers/fsi/fsi-core.c
+++ b/drivers/fsi/fsi-core.c
@@ -18,6 +18,7 @@
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_device.h>
+#include <linux/regmap.h>
#include <linux/slab.h>
#include <linux/bitops.h>
#include <linux/cdev.h>
@@ -1148,18 +1149,50 @@ static int fsi_master_write(struct fsi_master *master, int link,
return rc;
}

+int fsi_master_link_enable(struct fsi_master *master, int link, bool enable)
+{
+ u32 msiep = 0x80000000 >> (4 * (link % 8));
+ u32 menp = 0x80000000 >> (link % 32);
+ int enable_idx = 4 * (link / 32);
+ int irq_idx = 4 * (link / 8);
+ int rc;
+
+ if (enable) {
+ rc = regmap_write(master->map, FSI_MSENP0 + enable_idx, menp);
+ if (rc)
+ return rc;
+
+ mdelay(FSI_LINK_ENABLE_SETUP_TIME);
+
+ rc = regmap_write(master->map, FSI_MSSIEP0 + irq_idx, msiep);
+ } else {
+ rc = regmap_write(master->map, FSI_MCSIEP0 + irq_idx, msiep);
+ if (rc)
+ return rc;
+
+ rc = regmap_write(master->map, FSI_MCENP0 + enable_idx, menp);
+ }
+
+ return rc;
+}
+EXPORT_SYMBOL_GPL(fsi_master_link_enable);
+
static int fsi_master_link_disable(struct fsi_master *master, int link)
{
if (master->link_enable)
return master->link_enable(master, link, false);
+ else if (master->map)
+ return fsi_master_link_enable(master, link, false);

return 0;
}

-static int fsi_master_link_enable(struct fsi_master *master, int link)
+static int _fsi_master_link_enable(struct fsi_master *master, int link)
{
if (master->link_enable)
return master->link_enable(master, link, true);
+ else if (master->map)
+ return fsi_master_link_enable(master, link, true);

return 0;
}
@@ -1187,7 +1220,7 @@ static int fsi_master_scan(struct fsi_master *master)

trace_fsi_master_scan(master, true);
for (link = 0; link < master->n_links; link++) {
- rc = fsi_master_link_enable(master, link);
+ rc = _fsi_master_link_enable(master, link);
if (rc) {
dev_dbg(&master->dev,
"enable link %d failed: %d\n", link, rc);
@@ -1284,6 +1317,130 @@ static struct class fsi_master_class = {
.dev_groups = master_groups,
};

+void fsi_master_error(struct fsi_master *master, int link)
+{
+ u32 bits = FSI_MMODE_EIP | FSI_MMODE_RELA;
+ bool mmode = master->mmode & bits;
+
+ if (trace_fsi_master_error_regs_enabled()) {
+ unsigned int mesrb = 0xffffffff;
+ unsigned int mstap = 0xffffffff;
+
+ regmap_read(master->map, FSI_MESRB0, &mesrb);
+ regmap_read(master->map, FSI_MSTAP0 + (link * 4), &mstap);
+
+ trace_fsi_master_error_regs(master->idx, mesrb, mstap);
+ }
+
+ if (mmode)
+ regmap_write(master->map, FSI_MMODE, master->mmode & ~bits);
+
+ regmap_write(master->map, FSI_MRESP0, FSI_MRESP_RST_ALL_MASTER);
+
+ if (mmode)
+ regmap_write(master->map, FSI_MMODE, master->mmode);
+}
+EXPORT_SYMBOL_GPL(fsi_master_error);
+
+static inline u32 fsi_mmode_crs0(u32 x)
+{
+ return (x & FSI_MMODE_CRS0MASK) << FSI_MMODE_CRS0SHFT;
+}
+
+static inline u32 fsi_mmode_crs1(u32 x)
+{
+ return (x & FSI_MMODE_CRS1MASK) << FSI_MMODE_CRS1SHFT;
+}
+
+int fsi_master_init(struct fsi_master *master, unsigned long parent_clock_frequency)
+{
+ unsigned int mlevp;
+ unsigned int maeb;
+ int div = 1;
+ int rc;
+
+ if (parent_clock_frequency) {
+ u32 clock_frequency = parent_clock_frequency;
+
+ if (!device_property_read_u32(&master->dev, "clock-frequency", &clock_frequency)) {
+ if (!clock_frequency)
+ clock_frequency = parent_clock_frequency;
+ }
+
+ div = (parent_clock_frequency + (clock_frequency - 1)) / clock_frequency;
+ master->clock_frequency = parent_clock_frequency / div;
+ }
+
+ rc = regmap_write(master->map, FSI_MRESP0, FSI_MRESP_RST_ALL_MASTER |
+ FSI_MRESP_RST_ALL_LINK | FSI_MRESP_RST_MCR | FSI_MRESP_RST_PYE);
+ if (rc)
+ return rc;
+
+ rc = regmap_write(master->map, FSI_MECTRL, FSI_MECTRL_EOAE | FSI_MECTRL_P8_AUTO_TERM);
+ if (rc)
+ return rc;
+
+ master->mmode = FSI_MMODE_ECRC | FSI_MMODE_EPC | fsi_mmode_crs0(div) |
+ fsi_mmode_crs1(div) | FSI_MMODE_P8_TO_LSB;
+ rc = regmap_write(master->map, FSI_MMODE, master->mmode);
+ if (rc)
+ return rc;
+
+ rc = regmap_write(master->map, FSI_MDLYR, 0xffff0000);
+ if (rc)
+ return rc;
+
+ rc = regmap_write(master->map, FSI_MSENP0, 0xffffffff);
+ if (rc)
+ return rc;
+
+ mdelay(FSI_LINK_ENABLE_SETUP_TIME);
+
+ rc = regmap_write(master->map, FSI_MCENP0, 0xffffffff);
+ if (rc)
+ return rc;
+
+ rc = regmap_read(master->map, FSI_MAEB, &maeb);
+ if (rc)
+ return rc;
+
+ rc = regmap_write(master->map, FSI_MRESP0, FSI_MRESP_RST_ALL_MASTER |
+ FSI_MRESP_RST_ALL_LINK);
+ if (rc)
+ return rc;
+
+ rc = regmap_read(master->map, FSI_MLEVP0, &mlevp);
+ if (rc)
+ return rc;
+
+ rc = regmap_write(master->map, FSI_MRESB0, FSI_MRESB_RST_GEN);
+ if (rc)
+ return rc;
+
+ rc = regmap_write(master->map, FSI_MRESB0, FSI_MRESB_RST_ERR);
+ if (rc)
+ return rc;
+
+ if (master->flags & FSI_MASTER_FLAG_INTERRUPT)
+ master->mmode |= FSI_MMODE_EIP;
+ if (master->flags & FSI_MASTER_FLAG_RELA)
+ master->mmode |= FSI_MMODE_RELA;
+ return regmap_write(master->map, FSI_MMODE, master->mmode);
+}
+EXPORT_SYMBOL_GPL(fsi_master_init);
+
+void fsi_master_regmap_config(struct regmap_config *config)
+{
+ config->reg_bits = 32;
+ config->val_bits = 32;
+ config->disable_locking = true; // master driver will lock
+ config->fast_io = true;
+ config->cache_type = REGCACHE_NONE;
+ config->val_format_endian = REGMAP_ENDIAN_NATIVE;
+ config->can_sleep = false;
+}
+EXPORT_SYMBOL_GPL(fsi_master_regmap_config);
+
int fsi_master_register(struct fsi_master *master)
{
int rc;
diff --git a/drivers/fsi/fsi-master.h b/drivers/fsi/fsi-master.h
index 26e636ad9ef6..f4cecdff3834 100644
--- a/drivers/fsi/fsi-master.h
+++ b/drivers/fsi/fsi-master.h
@@ -27,6 +27,9 @@
#define FSI_MLEVP0 0x18 /* R: plug detect */
#define FSI_MSENP0 0x18 /* S: Set enable */
#define FSI_MCENP0 0x20 /* C: Clear enable */
+#define FSI_MSIEP0 0x30 /* R/W: interrupt enable */
+#define FSI_MSSIEP0 0x50 /* S: Set interrupt enable */
+#define FSI_MCSIEP0 0x70 /* C: Clear interrupt enable */
#define FSI_MAEB 0x70 /* R: Error address */
#define FSI_MVER 0x74 /* R: master version/type */
#define FSI_MSTAP0 0xd0 /* R: Port status */
@@ -108,9 +111,15 @@

/* Misc */
#define FSI_CRC_SIZE 4
+#define FSI_LINK_ENABLE_SETUP_TIME 10 /* in mS */

/* fsi-master definition and flags */
#define FSI_MASTER_FLAG_SWCLOCK 0x1
+#define FSI_MASTER_FLAG_INTERRUPT 0x2
+#define FSI_MASTER_FLAG_RELA 0x4
+
+struct regmap;
+struct regmap_config;

/*
* Structures and function prototypes
@@ -120,6 +129,8 @@

struct fsi_master {
struct device dev;
+ struct regmap *map;
+ u32 mmode;
unsigned long clock_frequency;
int lbus_divider;
int idx;
@@ -140,6 +151,11 @@ struct fsi_master {

#define to_fsi_master(d) container_of(d, struct fsi_master, dev)

+void fsi_master_error(struct fsi_master *master, int link);
+int fsi_master_init(struct fsi_master *master, unsigned long parent_clock_frequency);
+int fsi_master_link_enable(struct fsi_master *master, int link, bool enable);
+void fsi_master_regmap_config(struct regmap_config *config);
+
/**
* fsi_master registration & lifetime: the fsi_master_register() and
* fsi_master_unregister() functions will take ownership of the master, and
diff --git a/include/trace/events/fsi.h b/include/trace/events/fsi.h
index 5509afc98ee8..da977d59e163 100644
--- a/include/trace/events/fsi.h
+++ b/include/trace/events/fsi.h
@@ -67,6 +67,23 @@ TRACE_EVENT(fsi_master_error,
&__entry->data, __entry->ret)
);

+TRACE_EVENT(fsi_master_error_regs,
+ TP_PROTO(int master_idx, uint32_t mesrb, uint32_t mstap),
+ TP_ARGS(master_idx, mesrb, mstap),
+ TP_STRUCT__entry(
+ __field(int, master_idx)
+ __field(uint32_t, mesrb)
+ __field(uint32_t, mstap)
+ ),
+ TP_fast_assign(
+ __entry->master_idx = master_idx;
+ __entry->mesrb = mesrb;
+ __entry->mstap = mstap;
+ ),
+ TP_printk("fsi%d mesrb:%08x mstap:%08x", __entry->master_idx, __entry->mesrb,
+ __entry->mstap)
+);
+
TRACE_EVENT(fsi_master_break,
TP_PROTO(const struct fsi_master *master, int link),
TP_ARGS(master, link),
--
2.39.3


2024-02-15 22:25:14

by Eddie James

[permalink] [raw]
Subject: [PATCH 28/33] i2c: fsi: Improve formatting

No functional change.

Signed-off-by: Eddie James <[email protected]>
---
drivers/i2c/busses/i2c-fsi.c | 125 +++++++++++++++++------------------
1 file changed, 60 insertions(+), 65 deletions(-)

diff --git a/drivers/i2c/busses/i2c-fsi.c b/drivers/i2c/busses/i2c-fsi.c
index eaecf156ac31..bc44cad49ef2 100644
--- a/drivers/i2c/busses/i2c-fsi.c
+++ b/drivers/i2c/busses/i2c-fsi.c
@@ -148,10 +148,10 @@

struct fsi_i2c_master {
struct fsi_device *fsi;
- u8 fifo_size;
struct list_head ports;
struct mutex lock;
u32 clock_div;
+ u8 fifo_size;
};

struct fsi_i2c_port {
@@ -165,15 +165,14 @@ struct fsi_i2c_port {
static int fsi_i2c_read_reg(struct fsi_device *fsi, unsigned int reg,
u32 *data)
{
- int rc;
__be32 data_be;
+ int rc;

rc = fsi_device_read(fsi, reg, &data_be, sizeof(data_be));
if (rc)
return rc;

*data = be32_to_cpu(data_be);
-
return 0;
}

@@ -187,9 +186,11 @@ static int fsi_i2c_write_reg(struct fsi_device *fsi, unsigned int reg,

static int fsi_i2c_dev_init(struct fsi_i2c_master *i2c)
{
- int rc;
- u32 mode = I2C_MODE_ENHANCED, extended_status, watermark;
+ u32 mode = I2C_MODE_ENHANCED;
+ u32 extended_status;
u32 interrupt = 0;
+ u32 watermark;
+ int rc;

/* since we use polling, disable interrupts */
rc = fsi_i2c_write_reg(i2c->fsi, I2C_FSI_INT_MASK, &interrupt);
@@ -215,9 +216,10 @@ static int fsi_i2c_dev_init(struct fsi_i2c_master *i2c)

static int fsi_i2c_set_port(struct fsi_i2c_port *port)
{
- int rc;
struct fsi_device *fsi = port->master->fsi;
- u32 mode, dummy = 0;
+ u32 dummy = 0;
+ u32 mode;
+ int rc;

rc = fsi_i2c_read_reg(fsi, I2C_FSI_MODE, &mode);
if (rc)
@@ -238,7 +240,6 @@ static int fsi_i2c_set_port(struct fsi_i2c_port *port)
static int fsi_i2c_start(struct fsi_i2c_port *port, struct i2c_msg *msg,
bool stop)
{
- struct fsi_i2c_master *i2c = port->master;
u32 cmd = I2C_CMD_WITH_START | I2C_CMD_WITH_ADDR;

port->xfrd = 0;
@@ -252,7 +253,7 @@ static int fsi_i2c_start(struct fsi_i2c_port *port, struct i2c_msg *msg,
cmd |= FIELD_PREP(I2C_CMD_ADDR, msg->addr);
cmd |= FIELD_PREP(I2C_CMD_LEN, msg->len);

- return fsi_i2c_write_reg(i2c->fsi, I2C_FSI_CMD, &cmd);
+ return fsi_i2c_write_reg(port->master->fsi, I2C_FSI_CMD, &cmd);
}

static int fsi_i2c_get_op_bytes(int op_bytes)
@@ -268,18 +269,17 @@ static int fsi_i2c_get_op_bytes(int op_bytes)
static int fsi_i2c_write_fifo(struct fsi_i2c_port *port, struct i2c_msg *msg,
u8 fifo_count)
{
+ int bytes_to_write = port->master->fifo_size - fifo_count;
+ int bytes_remaining = msg->len - port->xfrd;
int write;
int rc;
- struct fsi_i2c_master *i2c = port->master;
- int bytes_to_write = i2c->fifo_size - fifo_count;
- int bytes_remaining = msg->len - port->xfrd;

bytes_to_write = min(bytes_to_write, bytes_remaining);

while (bytes_to_write) {
write = fsi_i2c_get_op_bytes(bytes_to_write);

- rc = fsi_device_write(i2c->fsi, I2C_FSI_FIFO,
+ rc = fsi_device_write(port->master->fsi, I2C_FSI_FIFO,
&msg->buf[port->xfrd], write);
if (rc)
return rc;
@@ -294,12 +294,11 @@ static int fsi_i2c_write_fifo(struct fsi_i2c_port *port, struct i2c_msg *msg,
static int fsi_i2c_read_fifo(struct fsi_i2c_port *port, struct i2c_msg *msg,
u8 fifo_count)
{
- int read;
- int rc;
- struct fsi_i2c_master *i2c = port->master;
- int bytes_to_read;
int xfr_remaining = msg->len - port->xfrd;
+ int bytes_to_read;
u32 dummy;
+ int read;
+ int rc;

bytes_to_read = min_t(int, fifo_count, xfr_remaining);

@@ -307,7 +306,7 @@ static int fsi_i2c_read_fifo(struct fsi_i2c_port *port, struct i2c_msg *msg,
read = fsi_i2c_get_op_bytes(bytes_to_read);

if (xfr_remaining) {
- rc = fsi_device_read(i2c->fsi, I2C_FSI_FIFO,
+ rc = fsi_device_read(port->master->fsi, I2C_FSI_FIFO,
&msg->buf[port->xfrd], read);
if (rc)
return rc;
@@ -316,8 +315,8 @@ static int fsi_i2c_read_fifo(struct fsi_i2c_port *port, struct i2c_msg *msg,
xfr_remaining -= read;
} else {
/* no more buffer but data in fifo, need to clear it */
- rc = fsi_device_read(i2c->fsi, I2C_FSI_FIFO, &dummy,
- read);
+ rc = fsi_device_read(port->master->fsi, I2C_FSI_FIFO,
+ &dummy, read);
if (rc)
return rc;
}
@@ -330,85 +329,80 @@ static int fsi_i2c_read_fifo(struct fsi_i2c_port *port, struct i2c_msg *msg,

static int fsi_i2c_get_scl(struct i2c_adapter *adap)
{
- u32 stat = 0;
struct fsi_i2c_port *port = adap->algo_data;
- struct fsi_i2c_master *i2c = port->master;
+ u32 stat;

- fsi_i2c_read_reg(i2c->fsi, I2C_FSI_STAT, &stat);
+ fsi_i2c_read_reg(port->master->fsi, I2C_FSI_STAT, &stat);

return !!(stat & I2C_STAT_SCL_IN);
}

static void fsi_i2c_set_scl(struct i2c_adapter *adap, int val)
{
- u32 dummy = 0;
struct fsi_i2c_port *port = adap->algo_data;
- struct fsi_i2c_master *i2c = port->master;
+ u32 dummy = 0;

if (val)
- fsi_i2c_write_reg(i2c->fsi, I2C_FSI_SET_SCL, &dummy);
+ fsi_i2c_write_reg(port->master->fsi, I2C_FSI_SET_SCL, &dummy);
else
- fsi_i2c_write_reg(i2c->fsi, I2C_FSI_RESET_SCL, &dummy);
+ fsi_i2c_write_reg(port->master->fsi, I2C_FSI_RESET_SCL, &dummy);
}

static int fsi_i2c_get_sda(struct i2c_adapter *adap)
{
- u32 stat = 0;
struct fsi_i2c_port *port = adap->algo_data;
- struct fsi_i2c_master *i2c = port->master;
+ u32 stat;

- fsi_i2c_read_reg(i2c->fsi, I2C_FSI_STAT, &stat);
+ fsi_i2c_read_reg(port->master->fsi, I2C_FSI_STAT, &stat);

return !!(stat & I2C_STAT_SDA_IN);
}

static void fsi_i2c_set_sda(struct i2c_adapter *adap, int val)
{
- u32 dummy = 0;
struct fsi_i2c_port *port = adap->algo_data;
- struct fsi_i2c_master *i2c = port->master;
+ u32 dummy = 0;

if (val)
- fsi_i2c_write_reg(i2c->fsi, I2C_FSI_SET_SDA, &dummy);
+ fsi_i2c_write_reg(port->master->fsi, I2C_FSI_SET_SDA, &dummy);
else
- fsi_i2c_write_reg(i2c->fsi, I2C_FSI_RESET_SDA, &dummy);
+ fsi_i2c_write_reg(port->master->fsi, I2C_FSI_RESET_SDA, &dummy);
}

static void fsi_i2c_prepare_recovery(struct i2c_adapter *adap)
{
- int rc;
- u32 mode;
struct fsi_i2c_port *port = adap->algo_data;
- struct fsi_i2c_master *i2c = port->master;
+ u32 mode;
+ int rc;

- rc = fsi_i2c_read_reg(i2c->fsi, I2C_FSI_MODE, &mode);
+ rc = fsi_i2c_read_reg(port->master->fsi, I2C_FSI_MODE, &mode);
if (rc)
return;

mode |= I2C_MODE_DIAG;
- fsi_i2c_write_reg(i2c->fsi, I2C_FSI_MODE, &mode);
+ fsi_i2c_write_reg(port->master->fsi, I2C_FSI_MODE, &mode);
}

static void fsi_i2c_unprepare_recovery(struct i2c_adapter *adap)
{
- int rc;
- u32 mode;
struct fsi_i2c_port *port = adap->algo_data;
- struct fsi_i2c_master *i2c = port->master;
+ u32 mode;
+ int rc;

- rc = fsi_i2c_read_reg(i2c->fsi, I2C_FSI_MODE, &mode);
+ rc = fsi_i2c_read_reg(port->master->fsi, I2C_FSI_MODE, &mode);
if (rc)
return;

mode &= ~I2C_MODE_DIAG;
- fsi_i2c_write_reg(i2c->fsi, I2C_FSI_MODE, &mode);
+ fsi_i2c_write_reg(port->master->fsi, I2C_FSI_MODE, &mode);
}

static int fsi_i2c_reset_bus(struct fsi_i2c_master *i2c,
struct fsi_i2c_port *port)
{
+ u32 dummy = 0;
+ u32 stat;
int rc;
- u32 stat, dummy = 0;

/* force bus reset, ignore errors */
i2c_recover_bus(&port->adapter);
@@ -439,8 +433,9 @@ static int fsi_i2c_reset_bus(struct fsi_i2c_master *i2c,

static int fsi_i2c_reset_engine(struct fsi_i2c_master *i2c, u16 port)
{
+ u32 dummy = 0;
+ u32 mode;
int rc;
- u32 mode, dummy = 0;

/* reset engine */
rc = fsi_i2c_write_reg(i2c->fsi, I2C_FSI_RESET_I2C, &dummy);
@@ -476,18 +471,17 @@ static int fsi_i2c_reset_engine(struct fsi_i2c_master *i2c, u16 port)

static int fsi_i2c_abort(struct fsi_i2c_port *port, u32 status)
{
- int rc;
- unsigned long start;
+ struct fsi_i2c_master *i2c = port->master;
u32 cmd = I2C_CMD_WITH_STOP;
+ unsigned long start;
u32 stat;
- struct fsi_i2c_master *i2c = port->master;
- struct fsi_device *fsi = i2c->fsi;
+ int rc;

rc = fsi_i2c_reset_engine(i2c, port->port);
if (rc)
return rc;

- rc = fsi_i2c_read_reg(fsi, I2C_FSI_STAT, &stat);
+ rc = fsi_i2c_read_reg(i2c->fsi, I2C_FSI_STAT, &stat);
if (rc)
return rc;

@@ -503,15 +497,14 @@ static int fsi_i2c_abort(struct fsi_i2c_port *port, u32 status)
return 0;

/* write stop command */
- rc = fsi_i2c_write_reg(fsi, I2C_FSI_CMD, &cmd);
+ rc = fsi_i2c_write_reg(i2c->fsi, I2C_FSI_CMD, &cmd);
if (rc)
return rc;

/* wait until we see command complete in the master */
start = jiffies;
-
do {
- rc = fsi_i2c_read_reg(fsi, I2C_FSI_STAT, &status);
+ rc = fsi_i2c_read_reg(i2c->fsi, I2C_FSI_STAT, &status);
if (rc)
return rc;

@@ -527,8 +520,8 @@ static int fsi_i2c_abort(struct fsi_i2c_port *port, u32 status)
static int fsi_i2c_handle_status(struct fsi_i2c_port *port,
struct i2c_msg *msg, u32 status)
{
- int rc;
u8 fifo_count;
+ int rc;

if (status & I2C_STAT_ERR) {
rc = fsi_i2c_abort(port, status);
@@ -576,9 +569,9 @@ static int fsi_i2c_handle_status(struct fsi_i2c_port *port,
static int fsi_i2c_wait(struct fsi_i2c_port *port, struct i2c_msg *msg,
unsigned long timeout)
{
- u32 status = 0;
- int rc;
unsigned long start = jiffies;
+ u32 status;
+ int rc;

do {
rc = fsi_i2c_read_reg(port->master->fsi, I2C_FSI_STAT,
@@ -608,13 +601,13 @@ static int fsi_i2c_wait(struct fsi_i2c_port *port, struct i2c_msg *msg,
static int fsi_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs,
int num)
{
- int i, rc;
- unsigned long start_time;
struct fsi_i2c_port *port = adap->algo_data;
- struct fsi_i2c_master *master = port->master;
+ unsigned long start_time;
struct i2c_msg *msg;
+ int rc;
+ int i;

- mutex_lock(&master->lock);
+ mutex_lock(&port->master->lock);

rc = fsi_i2c_set_port(port);
if (rc)
@@ -635,7 +628,7 @@ static int fsi_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs,
}

unlock:
- mutex_unlock(&master->lock);
+ mutex_unlock(&port->master->lock);
return rc ? : num;
}

@@ -681,8 +674,10 @@ static int fsi_i2c_probe(struct device *dev)
struct fsi_i2c_master *i2c;
struct fsi_i2c_port *port;
struct device_node *np;
- u32 port_no, ports, stat;
+ u32 port_no;
+ u32 ports;
u32 lbus;
+ u32 stat;
int rc;

i2c = devm_kzalloc(dev, sizeof(*i2c), GFP_KERNEL);
@@ -753,14 +748,14 @@ static int fsi_i2c_probe(struct device *dev)
}

dev_set_drvdata(dev, i2c);
-
return 0;
}

static int fsi_i2c_remove(struct device *dev)
{
struct fsi_i2c_master *i2c = dev_get_drvdata(dev);
- struct fsi_i2c_port *port, *tmp;
+ struct fsi_i2c_port *port;
+ struct fsi_i2c_port *tmp;

list_for_each_entry_safe(port, tmp, &i2c->ports, list) {
list_del(&port->list);
--
2.39.3


2024-02-15 22:30:11

by Eddie James

[permalink] [raw]
Subject: [PATCH 07/33] ARM: dts: aspeed: p10 and tacoma: Set FSI clock frequency

Now that the driver doesn't hardcode the clock divider, set it
in the device tree.

Signed-off-by: Eddie James <[email protected]>
---
arch/arm/boot/dts/aspeed/aspeed-bmc-opp-tacoma.dts | 1 +
arch/arm/boot/dts/aspeed/ibm-power10-dual.dtsi | 1 +
2 files changed, 2 insertions(+)

diff --git a/arch/arm/boot/dts/aspeed/aspeed-bmc-opp-tacoma.dts b/arch/arm/boot/dts/aspeed/aspeed-bmc-opp-tacoma.dts
index 213023bc5aec..96a8f727bc38 100644
--- a/arch/arm/boot/dts/aspeed/aspeed-bmc-opp-tacoma.dts
+++ b/arch/arm/boot/dts/aspeed/aspeed-bmc-opp-tacoma.dts
@@ -193,6 +193,7 @@ &fsim0 {
#address-cells = <2>;
#size-cells = <0>;

+ clock-frequency = <100000000>;
fsi-routing-gpios = <&gpio0 ASPEED_GPIO(Q, 7) GPIO_ACTIVE_HIGH>;
fsi-mux-gpios = <&gpio0 ASPEED_GPIO(B, 0) GPIO_ACTIVE_HIGH>;

diff --git a/arch/arm/boot/dts/aspeed/ibm-power10-dual.dtsi b/arch/arm/boot/dts/aspeed/ibm-power10-dual.dtsi
index cc466910bb52..a93a241d005a 100644
--- a/arch/arm/boot/dts/aspeed/ibm-power10-dual.dtsi
+++ b/arch/arm/boot/dts/aspeed/ibm-power10-dual.dtsi
@@ -8,6 +8,7 @@ &fsim0 {
#size-cells = <0>;

cfam-reset-gpios = <&gpio0 ASPEED_GPIO(Q, 0) GPIO_ACTIVE_HIGH>;
+ clock-frequency = <100000000>;

cfam@0,0 {
reg = <0 0>;
--
2.39.3


2024-02-15 22:51:36

by Eddie James

[permalink] [raw]
Subject: [PATCH 31/33] i2c: fsi: Define a function to check status error bits

This will be used more than once with interrupt support, so split
the error bit checks into a function.

Signed-off-by: Eddie James <[email protected]>
---
drivers/i2c/busses/i2c-fsi.c | 38 ++++++++++++++++++++----------------
1 file changed, 21 insertions(+), 17 deletions(-)

diff --git a/drivers/i2c/busses/i2c-fsi.c b/drivers/i2c/busses/i2c-fsi.c
index 096dc7e2369f..5f524fb6f0f8 100644
--- a/drivers/i2c/busses/i2c-fsi.c
+++ b/drivers/i2c/busses/i2c-fsi.c
@@ -507,6 +507,26 @@ static int fsi_i2c_abort(struct fsi_i2c_port *port, u32 status)
return -ETIMEDOUT;
}

+static int fsi_i2c_error_status_to_rc(u32 status)
+{
+ if (status & I2C_STAT_INV_CMD)
+ return -EINVAL;
+
+ if (status & (I2C_STAT_PARITY | I2C_STAT_BE_OVERRUN | I2C_STAT_BE_ACCESS))
+ return -EPROTO;
+
+ if (status & I2C_STAT_NACK)
+ return -ENXIO;
+
+ if (status & I2C_STAT_LOST_ARB)
+ return -EAGAIN;
+
+ if (status & I2C_STAT_STOP_ERR)
+ return -EBADMSG;
+
+ return -EIO;
+}
+
static int fsi_i2c_handle_status(struct fsi_i2c_port *port,
struct i2c_msg *msg, u32 status)
{
@@ -518,23 +538,7 @@ static int fsi_i2c_handle_status(struct fsi_i2c_port *port,
if (rc)
return rc;

- if (status & I2C_STAT_INV_CMD)
- return -EINVAL;
-
- if (status & (I2C_STAT_PARITY | I2C_STAT_BE_OVERRUN |
- I2C_STAT_BE_ACCESS))
- return -EPROTO;
-
- if (status & I2C_STAT_NACK)
- return -ENXIO;
-
- if (status & I2C_STAT_LOST_ARB)
- return -EAGAIN;
-
- if (status & I2C_STAT_STOP_ERR)
- return -EBADMSG;
-
- return -EIO;
+ return fsi_i2c_error_status_to_rc(status);
}

if (status & I2C_STAT_DAT_REQ) {
--
2.39.3


2024-02-15 22:51:43

by Eddie James

[permalink] [raw]
Subject: [PATCH 25/33] fsi: hub: Add interrupt support

The hub master receives it's interrupts from the local slave register
space, which is handled in the FSI core. Therefore, just route the remote
slave interrupts to the hub link device interrupts.

Signed-off-by: Eddie James <[email protected]>
---
drivers/fsi/fsi-master-hub.c | 119 ++++++++++++++++++++++++++++++++++-
1 file changed, 116 insertions(+), 3 deletions(-)

diff --git a/drivers/fsi/fsi-master-hub.c b/drivers/fsi/fsi-master-hub.c
index 92aa07055c56..4c3f77ae1faf 100644
--- a/drivers/fsi/fsi-master-hub.c
+++ b/drivers/fsi/fsi-master-hub.c
@@ -7,8 +7,10 @@

#include <linux/delay.h>
#include <linux/fsi.h>
+#include <linux/irqchip/chained_irq.h>
#include <linux/module.h>
#include <linux/of.h>
+#include <linux/of_irq.h>
#include <linux/regmap.h>
#include <linux/slab.h>

@@ -35,9 +37,10 @@
*/
struct fsi_master_hub {
struct fsi_master master;
+ struct irq_domain *irq_domain;
struct fsi_device *upstream;
- uint32_t addr, size; /* slave-relative addr of */
- /* master address space */
+ uint32_t addr;
+ uint32_t size;
};

#define to_fsi_master_hub(m) container_of(m, struct fsi_master_hub, master)
@@ -77,10 +80,81 @@ static int hub_master_break(struct fsi_master *master, int link)
return hub_master_write(master, link, 0, addr, &cmd, sizeof(cmd));
}

+static int hub_master_link_enable(struct fsi_master *master, int link,
+ bool enable)
+{
+ struct fsi_master_hub *hub = to_fsi_master_hub(master);
+ u32 srsim = 0xff000000 >> (8 * (link % 4));
+ int slave_idx = 4 * (link / 4);
+ __be32 srsim_be;
+ int ret;
+
+ ret = fsi_slave_read(hub->upstream->slave, FSI_SLAVE_BASE + FSI_SRSIM0 + slave_idx,
+ &srsim_be, sizeof(srsim_be));
+ if (ret)
+ return ret;
+
+ if (enable) {
+ ret = fsi_master_link_enable(master, link, enable);
+ if (ret)
+ return ret;
+
+ srsim |= be32_to_cpu(srsim_be);
+ srsim_be = cpu_to_be32(srsim);
+ ret = fsi_slave_write(hub->upstream->slave,
+ FSI_SLAVE_BASE + FSI_SRSIM0 + slave_idx, &srsim_be,
+ sizeof(srsim_be));
+ } else {
+ srsim = be32_to_cpu(srsim_be) & ~srsim;
+ srsim_be = cpu_to_be32(srsim);
+ ret = fsi_slave_write(hub->upstream->slave,
+ FSI_SLAVE_BASE + FSI_SRSIM0 + slave_idx, &srsim_be,
+ sizeof(srsim_be));
+ if (ret)
+ return ret;
+
+ ret = fsi_master_link_enable(master, link, enable);
+ }
+
+ return ret;
+}
+
+static irqreturn_t hub_master_irq(int irq, void *data)
+{
+ struct fsi_master_hub *hub = data;
+ struct fsi_master *parent = hub->upstream->slave->master;
+ unsigned int link = 0;
+
+ for (; link < FSI_HUB_MASTER_MAX_LINKS; ++link) {
+ if (parent->remote_interrupt_status & (1 << link))
+ fsi_master_irq(&hub->master, hub->irq_domain, link);
+ }
+
+ return IRQ_HANDLED;
+}
+
+static int hub_master_irqd_map(struct irq_domain *domain, unsigned int irq,
+ irq_hw_number_t hwirq)
+{
+ struct fsi_master_hub *hub = domain->host_data;
+
+ irq_set_chip_and_handler(irq, &hub->master.irq_chip, handle_simple_irq);
+ irq_set_chip_data(irq, &hub->master);
+
+ return 0;
+}
+
+static const struct irq_domain_ops hub_master_irq_domain_ops = {
+ .map = hub_master_irqd_map,
+};
+
static void hub_master_release(struct device *dev)
{
struct fsi_master_hub *hub = to_fsi_master_hub(to_fsi_master(dev));

+ if (hub->irq_domain)
+ irq_domain_remove(hub->irq_domain);
+
regmap_exit(hub->master.map);
kfree(hub);
}
@@ -136,6 +210,7 @@ static int hub_master_probe(struct device *dev)
hub->master.read = hub_master_read;
hub->master.write = hub_master_write;
hub->master.send_break = hub_master_break;
+ hub->master.link_enable = hub_master_link_enable;

dev_set_drvdata(dev, hub);

@@ -143,9 +218,44 @@ static int hub_master_probe(struct device *dev)
if (rc)
goto err_free;

+ if (of_property_read_bool(dev->of_node, "interrupt-controller")) {
+ struct device_node *parent = of_irq_find_parent(dev->of_node);
+
+ if (parent) {
+ struct irq_fwspec fwspec;
+ unsigned int irq;
+
+ fwspec.fwnode = of_node_to_fwnode(parent);
+ fwspec.param_count = 1;
+ fwspec.param[0] = (fsi_dev->slave->link * FSI_IRQ_COUNT) + 8;
+ irq = irq_create_fwspec_mapping(&fwspec);
+ if (irq) {
+ unsigned int size = links * FSI_IRQ_COUNT;
+
+ hub->irq_domain = irq_domain_add_linear(dev->of_node, size,
+ &hub_master_irq_domain_ops,
+ hub);
+
+ if (hub->irq_domain) {
+ rc = devm_request_irq(dev, irq, hub_master_irq, 0,
+ dev_name(dev), hub);
+ if (rc) {
+ dev_warn(dev, "failed to request irq:%u\n", irq);
+ irq_domain_remove(hub->irq_domain);
+ hub->irq_domain = NULL;
+ } else {
+ dev_info(dev, "enabling interrupts irq:%u\n", irq);
+ }
+ } else {
+ dev_warn(dev, "failed to create irq domain\n");
+ }
+ }
+ }
+ }
+
rc = fsi_master_register(&hub->master);
if (rc)
- goto err_free;
+ goto err_irq;

/* At this point, fsi_master_register performs the device_initialize(),
* and holds the sole reference on master.dev. This means the device
@@ -157,6 +267,9 @@ static int hub_master_probe(struct device *dev)
get_device(&hub->master.dev);
return 0;

+err_irq:
+ if (hub->irq_domain)
+ irq_domain_remove(hub->irq_domain);
err_free:
kfree(hub);
err_release:
--
2.39.3


2024-02-15 22:52:04

by Eddie James

[permalink] [raw]
Subject: [PATCH 32/33] i2c: fsi: Add boolean for skip stop command on abort

In preparation for interrupt support, store whether to skip the
final stop command during the abort procedure instead of checking
the previously read status register in the abort function.

Signed-off-by: Eddie James <[email protected]>
---
drivers/i2c/busses/i2c-fsi.c | 18 +++++++++++-------
1 file changed, 11 insertions(+), 7 deletions(-)

diff --git a/drivers/i2c/busses/i2c-fsi.c b/drivers/i2c/busses/i2c-fsi.c
index 5f524fb6f0f8..33f4e64cb60b 100644
--- a/drivers/i2c/busses/i2c-fsi.c
+++ b/drivers/i2c/busses/i2c-fsi.c
@@ -116,6 +116,9 @@
#define I2C_STAT_ANY_RESP (I2C_STAT_ERR | \
I2C_STAT_DAT_REQ | \
I2C_STAT_CMD_COMP)
+#define I2C_STAT_SKIP_STOP (I2C_STAT_PARITY | \
+ I2C_STAT_LOST_ARB | \
+ I2C_STAT_STOP_ERR)

/* extended status register */
#define I2C_ESTAT_FIFO_SZ GENMASK(31, 24)
@@ -150,6 +153,7 @@ struct fsi_i2c_master {
struct mutex lock;
u32 clock_div;
u8 fifo_size;
+ bool skip_stop;
};

struct fsi_i2c_port {
@@ -459,31 +463,30 @@ static int fsi_i2c_reset_engine(struct fsi_i2c_master *i2c, u16 port)
return 0;
}

-static int fsi_i2c_abort(struct fsi_i2c_port *port, u32 status)
+static int fsi_i2c_abort(struct fsi_i2c_port *port)
{
struct fsi_i2c_master *i2c = port->master;
u32 cmd = I2C_CMD_WITH_STOP;
unsigned long start;
- u32 stat;
+ u32 status;
int rc;

rc = fsi_i2c_reset_engine(i2c, port->port);
if (rc)
return rc;

- rc = fsi_i2c_read_reg(i2c->fsi, I2C_FSI_STAT, &stat);
+ rc = fsi_i2c_read_reg(i2c->fsi, I2C_FSI_STAT, &status);
if (rc)
return rc;

/* if sda is low, peform full bus reset */
- if (!(stat & I2C_STAT_SDA_IN)) {
+ if (!(status & I2C_STAT_SDA_IN)) {
rc = fsi_i2c_reset_bus(i2c, port);
if (rc)
return rc;
}

- /* skip final stop command for these errors */
- if (status & (I2C_STAT_PARITY | I2C_STAT_LOST_ARB | I2C_STAT_STOP_ERR))
+ if (i2c->skip_stop)
return 0;

/* write stop command */
@@ -534,7 +537,8 @@ static int fsi_i2c_handle_status(struct fsi_i2c_port *port,
int rc;

if (status & I2C_STAT_ERR) {
- rc = fsi_i2c_abort(port, status);
+ port->master->skip_stop = status & I2C_STAT_SKIP_STOP;
+ rc = fsi_i2c_abort(port);
if (rc)
return rc;

--
2.39.3


2024-02-15 22:52:17

by Eddie James

[permalink] [raw]
Subject: [PATCH 24/33] fsi: aspeed: Add interrupt support

Handle slave interrupts and pass them to the FSI core.

Signed-off-by: Eddie James <[email protected]>
---
drivers/fsi/fsi-master-aspeed.c | 104 ++++++++++++++++++++++-
include/trace/events/fsi_master_aspeed.h | 12 +++
2 files changed, 114 insertions(+), 2 deletions(-)

diff --git a/drivers/fsi/fsi-master-aspeed.c b/drivers/fsi/fsi-master-aspeed.c
index ac8835e4d1f8..ce16ea65f65d 100644
--- a/drivers/fsi/fsi-master-aspeed.c
+++ b/drivers/fsi/fsi-master-aspeed.c
@@ -6,6 +6,8 @@
#include <linux/delay.h>
#include <linux/fsi.h>
#include <linux/io.h>
+#include <linux/irqchip/chained_irq.h>
+#include <linux/irqdomain.h>
#include <linux/mfd/syscon.h>
#include <linux/module.h>
#include <linux/of.h>
@@ -25,10 +27,12 @@ struct fsi_master_aspeed_data {
struct fsi_master_aspeed {
struct fsi_master master;
spinlock_t lock; /* protect HW access */
+ struct irq_domain *irq_domain;
struct device *dev;
void __iomem *base;
struct clk *clk;
struct gpio_desc *cfam_reset_gpio;
+ u32 irq_mask;
};

#define to_fsi_master_aspeed(m) \
@@ -80,6 +84,11 @@ static const u32 fsi_base = 0xa0000000;
#define STATUS_TIMEOUT BIT(4)

/* OPB_IRQ_MASK */
+#define FSI_MASTER_ERROR_IRQ BIT(28)
+#define FSI_PORT_ERROR_IRQ BIT(27)
+#define FSI_HOTPLUG_IRQ BIT(26)
+#define FSI_REMOTE_SLV_IRQ(l) (BIT(FSI_REMOTE_SLV_IRQ_BIT) << (l))
+#define FSI_REMOTE_SLV_IRQ_BIT 18
#define OPB1_XFER_ACK_EN BIT(17)
#define OPB0_XFER_ACK_EN BIT(16)

@@ -316,11 +325,76 @@ static int aspeed_master_break(struct fsi_master *master, int link)
return aspeed_master_write(master, link, 0, addr, &cmd, 4);
}

+static int aspeed_master_link_enable(struct fsi_master *master, int link, bool enable)
+{
+ struct fsi_master_aspeed *aspeed = to_fsi_master_aspeed(master);
+ unsigned long flags;
+ int rc;
+
+ spin_lock_irqsave(&aspeed->lock, flags);
+ if (enable) {
+ rc = fsi_master_link_enable(master, link, enable);
+ if (rc)
+ goto done;
+
+ aspeed->irq_mask |= FSI_REMOTE_SLV_IRQ(link);
+ writel(aspeed->irq_mask, aspeed->base + OPB_IRQ_MASK);
+ } else {
+ aspeed->irq_mask &= ~FSI_REMOTE_SLV_IRQ(link);
+ writel(aspeed->irq_mask, aspeed->base + OPB_IRQ_MASK);
+
+ rc = fsi_master_link_enable(master, link, enable);
+ }
+
+done:
+ spin_unlock_irqrestore(&aspeed->lock, flags);
+ return rc;
+}
+
+static irqreturn_t aspeed_master_irq(int irq, void *data)
+{
+ struct fsi_master_aspeed *aspeed = data;
+ unsigned long size = FSI_REMOTE_SLV_IRQ_BIT + aspeed->master.n_links;
+ unsigned long bit = FSI_REMOTE_SLV_IRQ_BIT;
+ unsigned long status;
+
+ status = readl(aspeed->base + OPB_IRQ_STATUS);
+ writel(0, aspeed->base + OPB_IRQ_MASK);
+
+ for_each_set_bit_from(bit, &status, size)
+ fsi_master_irq(&aspeed->master, aspeed->irq_domain, bit - FSI_REMOTE_SLV_IRQ_BIT);
+
+ writel(status, aspeed->base + OPB_IRQ_STATUS);
+ writel(0, aspeed->base + OPB_IRQ_STATUS);
+ writel(aspeed->irq_mask, aspeed->base + OPB_IRQ_MASK);
+
+ trace_fsi_master_aspeed_irq(status);
+ return IRQ_HANDLED;
+}
+
+static int aspeed_master_irqd_map(struct irq_domain *domain, unsigned int irq,
+ irq_hw_number_t hwirq)
+{
+ struct fsi_master_aspeed *aspeed = domain->host_data;
+
+ irq_set_chip_and_handler(irq, &aspeed->master.irq_chip, handle_simple_irq);
+ irq_set_chip_data(irq, &aspeed->master);
+
+ return 0;
+}
+
+static const struct irq_domain_ops aspeed_master_irq_domain_ops = {
+ .map = aspeed_master_irqd_map,
+};
+
static void aspeed_master_release(struct device *dev)
{
struct fsi_master_aspeed *aspeed =
to_fsi_master_aspeed(to_fsi_master(dev));

+ if (aspeed->irq_domain)
+ irq_domain_remove(aspeed->irq_domain);
+
regmap_exit(aspeed->master.map);
kfree(aspeed);
}
@@ -447,6 +521,7 @@ static int fsi_master_aspeed_probe(struct platform_device *pdev)
struct fsi_master_aspeed *aspeed;
unsigned int reg;
int rc, links;
+ int irq;

rc = tacoma_cabled_fsi_fixup(&pdev->dev);
if (rc) {
@@ -527,11 +602,12 @@ static int fsi_master_aspeed_probe(struct platform_device *pdev)
aspeed->master.dev.of_node = of_node_get(dev_of_node(&pdev->dev));

aspeed->master.n_links = links;
- aspeed->master.flags = FSI_MASTER_FLAG_RELA;
+ aspeed->master.flags = FSI_MASTER_FLAG_INTERRUPT | FSI_MASTER_FLAG_RELA;
aspeed->master.read = aspeed_master_read;
aspeed->master.write = aspeed_master_write;
aspeed->master.send_break = aspeed_master_break;
aspeed->master.term = aspeed_master_term;
+ aspeed->master.link_enable = aspeed_master_link_enable;

dev_set_drvdata(&pdev->dev, aspeed);

@@ -539,9 +615,30 @@ static int fsi_master_aspeed_probe(struct platform_device *pdev)
if (rc)
goto err_regmap;

+ irq = platform_get_irq(pdev, 0);
+ if (irq > 0) {
+ unsigned int size = links * FSI_IRQ_COUNT;
+
+ aspeed->irq_domain = irq_domain_add_linear(aspeed->dev->of_node, size,
+ &aspeed_master_irq_domain_ops, aspeed);
+ if (aspeed->irq_domain) {
+ rc = devm_request_irq(aspeed->dev, irq, aspeed_master_irq, 0,
+ dev_name(aspeed->dev), aspeed);
+ if (rc) {
+ dev_warn(aspeed->dev, "failed to request irq:%d\n", irq);
+ irq_domain_remove(aspeed->irq_domain);
+ aspeed->irq_domain = NULL;
+ } else {
+ dev_info(aspeed->dev, "enabling interrupts irq:%d\n", irq);
+ }
+ } else {
+ dev_warn(aspeed->dev, "failed to create irq domain\n");
+ }
+ }
+
rc = fsi_master_register(&aspeed->master);
if (rc)
- goto err_regmap;
+ goto err_irq;

/* At this point, fsi_master_register performs the device_initialize(),
* and holds the sole reference on master.dev. This means the device
@@ -553,6 +650,9 @@ static int fsi_master_aspeed_probe(struct platform_device *pdev)
get_device(&aspeed->master.dev);
return 0;

+err_irq:
+ if (aspeed->irq_domain)
+ irq_domain_remove(aspeed->irq_domain);
err_regmap:
regmap_exit(aspeed->master.map);
err_release:
diff --git a/include/trace/events/fsi_master_aspeed.h b/include/trace/events/fsi_master_aspeed.h
index 7eeecbfec7f0..dba1776334a0 100644
--- a/include/trace/events/fsi_master_aspeed.h
+++ b/include/trace/events/fsi_master_aspeed.h
@@ -8,6 +8,18 @@

#include <linux/tracepoint.h>

+TRACE_EVENT(fsi_master_aspeed_irq,
+ TP_PROTO(uint32_t status),
+ TP_ARGS(status),
+ TP_STRUCT__entry(
+ __field(uint32_t, status)
+ ),
+ TP_fast_assign(
+ __entry->status = status;
+ ),
+ TP_printk("status %08x", __entry->status)
+);
+
TRACE_EVENT(fsi_master_aspeed_opb_xfer,
TP_PROTO(uint32_t addr, uint32_t size, uint32_t data, bool read),
TP_ARGS(addr, size, data, read),
--
2.39.3


2024-02-15 22:52:21

by Eddie James

[permalink] [raw]
Subject: [PATCH 23/33] fsi: core: Add interrupt support

Add an irq chip to the FSI master structure to control slave interrupt
masking. Add a function to request an IRQ from the FSI device.
The FSI master IRQ mapping is based on the FSI device engine type and
slave link.

Signed-off-by: Eddie James <[email protected]>
---
drivers/fsi/fsi-core.c | 196 +++++++++++++++++++++++++++++++++++++
drivers/fsi/fsi-master.h | 9 ++
include/linux/fsi.h | 2 +
include/trace/events/fsi.h | 60 ++++++++++++
4 files changed, 267 insertions(+)

diff --git a/drivers/fsi/fsi-core.c b/drivers/fsi/fsi-core.c
index 693e7c51b4af..d56ed46daf14 100644
--- a/drivers/fsi/fsi-core.c
+++ b/drivers/fsi/fsi-core.c
@@ -14,10 +14,12 @@
#include <linux/device.h>
#include <linux/fsi.h>
#include <linux/idr.h>
+#include <linux/irqdomain.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_device.h>
+#include <linux/of_irq.h>
#include <linux/regmap.h>
#include <linux/slab.h>
#include <linux/bitops.h>
@@ -109,6 +111,67 @@ int fsi_device_peek(struct fsi_device *dev, void *val)

return fsi_slave_read(dev->slave, addr, val, sizeof(uint32_t));
}
+EXPORT_SYMBOL_GPL(fsi_device_peek);
+
+static int fsi_request_irq(struct fsi_slave *slave, irq_handler_t handler, void *data,
+ unsigned int engine_irq, struct device *dev)
+{
+ struct device_node *parent = of_node_get(slave->master->dev.of_node);
+ struct irq_fwspec fwspec;
+ unsigned int irq;
+
+ /*
+ * FSI devices can only report interrupts to their own master, so if the master
+ * isn't an interrupt controller, don't try and map an irq.
+ */
+ if (!of_get_property(parent, "#interrupt-cells", NULL)) {
+ of_node_put(parent);
+ return -EINVAL;
+ }
+
+ fwspec.fwnode = of_node_to_fwnode(parent);
+ fwspec.param_count = 1;
+ fwspec.param[0] = engine_irq + (slave->link * FSI_IRQ_COUNT);
+ irq = irq_create_fwspec_mapping(&fwspec);
+ if (!irq)
+ return -EINVAL;
+
+ return devm_request_irq(dev, irq, handler, 0, dev_name(dev), data);
+}
+
+int fsi_device_request_irq(struct fsi_device *dev, irq_handler_t handler, void *data)
+{
+ unsigned int engine_irq;
+
+ switch (dev->engine_type) {
+ case 0x4: // shift
+ engine_irq = 1;
+ break;
+ case 0x5: // scom
+ engine_irq = 2;
+ break;
+ case 0x6: // scratchpad
+ engine_irq = 3;
+ break;
+ case 0x7: // i2cm
+ engine_irq = 4;
+ break;
+ case 0x20: // mbox
+ engine_irq = 7;
+ break;
+ case 0x22: // sbefifo
+ engine_irq = 6;
+ break;
+ case 0x23: // spim
+ engine_irq = 5;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return fsi_request_irq(dev->slave, handler, data, engine_irq, &dev->dev);
+}
+EXPORT_SYMBOL_GPL(fsi_device_request_irq);

unsigned long fsi_device_local_bus_frequency(struct fsi_device *dev)
{
@@ -600,6 +663,9 @@ static const struct bin_attribute fsi_slave_raw_attr = {
static void fsi_slave_release(struct device *dev)
{
struct fsi_slave *slave = to_fsi_slave(dev);
+ __be32 scism = cpu_to_be32(0xffffffff);
+
+ fsi_slave_write(slave, FSI_SLAVE_BASE + FSI_SCISM, &scism, sizeof(scism));

fsi_free_minor(slave->dev.devt);
of_node_put(dev->of_node);
@@ -957,6 +1023,27 @@ void fsi_free_minor(dev_t dev)
}
EXPORT_SYMBOL_GPL(fsi_free_minor);

+static irqreturn_t fsi_slave_irq(int irq, void *data)
+{
+ struct fsi_slave *slave = data;
+ __be32 reg;
+ u32 siss;
+ int rc;
+
+ rc = fsi_slave_read(slave, FSI_SLAVE_BASE + FSI_SISS, &reg, sizeof(reg));
+ if (rc)
+ return IRQ_NONE;
+
+ siss = be32_to_cpu(reg);
+ reg = cpu_to_be32(0xffffffff);
+ rc = fsi_slave_write(slave, FSI_SLAVE_BASE + FSI_SCISC, &reg, sizeof(reg));
+ if (rc)
+ return IRQ_NONE;
+
+ trace_fsi_slave_irq(slave, siss);
+ return IRQ_HANDLED;
+}
+
static int fsi_slave_init(struct fsi_master *master, int link, uint8_t id)
{
uint32_t cfam_id;
@@ -1067,6 +1154,19 @@ static int fsi_slave_init(struct fsi_master *master, int link, uint8_t id)
goto err_free_ida;
}

+ rc = fsi_request_irq(slave, fsi_slave_irq, slave, 0, &slave->dev);
+ if (!rc) {
+ __be32 ssism = cpu_to_be32(FSI_SISS_ALL);
+
+ /* clear interrupt conditions before unmasking */
+ data = cpu_to_be32(0xffffffff);
+ fsi_master_write(master, link, id, FSI_SLAVE_BASE + FSI_SCISC, &data,
+ sizeof(data));
+
+ fsi_master_write(master, link, id, FSI_SLAVE_BASE + FSI_SSISM, &ssism,
+ sizeof(ssism));
+ }
+
/* Now that we have the cdev registered with the core, any fatal
* failures beyond this point will need to clean up through
* cdev_device_del(). Fortunately though, nothing past here is fatal.
@@ -1441,6 +1541,99 @@ void fsi_master_regmap_config(struct regmap_config *config)
}
EXPORT_SYMBOL_GPL(fsi_master_regmap_config);

+int fsi_master_irq(struct fsi_master *master, struct irq_domain *irq_domain, unsigned int link)
+{
+ struct irq_desc *downstream = irq_resolve_mapping(irq_domain, (link * FSI_IRQ_COUNT) + 8);
+ unsigned long size = FSI_SI1S_SLAVE_BIT + 1;
+ unsigned long bit = FSI_SI1S_MBOX_BIT;
+ unsigned long srsis0 = 0;
+ unsigned long srsis4 = 0;
+ unsigned long si1s;
+ __be32 reg;
+ int rc;
+
+ rc = fsi_master_read(master, link, 0, FSI_SLAVE_BASE + FSI_SI1S, &reg, sizeof(reg));
+ if (rc)
+ return rc;
+
+ si1s = (unsigned long)be32_to_cpu(reg);
+ for_each_set_bit_from(bit, &si1s, size)
+ generic_handle_domain_irq(irq_domain, (link * FSI_IRQ_COUNT) + (31 - bit));
+
+ if (downstream) {
+ int i;
+
+ master->remote_interrupt_status = 0;
+
+ rc = fsi_master_read(master, link, 0, FSI_SLAVE_BASE + FSI_SRSIS0, &reg,
+ sizeof(reg));
+ if (rc)
+ return rc;
+
+ srsis0 = (unsigned long)be32_to_cpu(reg);
+ for (i = 0; i < 4; ++i) {
+ if (srsis0 & (0xff000000 >> (8 * i)))
+ master->remote_interrupt_status |= (1 << i);
+ }
+
+ rc = fsi_master_read(master, link, 0, FSI_SLAVE_BASE + FSI_SRSIS4, &reg,
+ sizeof(reg));
+ if (rc)
+ return rc;
+
+ srsis4 = (unsigned long)be32_to_cpu(reg);
+ for (i = 0; i < 4; ++i) {
+ if (srsis4 & (0xff000000 >> (8 * i)))
+ master->remote_interrupt_status |= (16 << i);
+ }
+
+ if (master->remote_interrupt_status) {
+ handle_irq_desc(downstream);
+
+ reg = cpu_to_be32(0xffffffff);
+ if (master->remote_interrupt_status & 0xf)
+ fsi_master_write(master, link, 0, FSI_SLAVE_BASE + FSI_SRSIC0,
+ &reg, sizeof(reg));
+
+ if (master->remote_interrupt_status & 0xf0)
+ fsi_master_write(master, link, 0, FSI_SLAVE_BASE + FSI_SRSIC4,
+ &reg, sizeof(reg));
+ }
+ }
+
+ trace_fsi_master_irq(master, link, si1s, srsis0, srsis4);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(fsi_master_irq);
+
+static void fsi_master_irq_mask(struct irq_data *data)
+{
+ unsigned int bit = 31 - (data->hwirq % FSI_IRQ_COUNT);
+
+ if (bit >= FSI_SI1S_MBOX_BIT) {
+ struct fsi_master *master = irq_data_get_irq_chip_data(data);
+ int link = data->hwirq / FSI_IRQ_COUNT;
+ __be32 mask = cpu_to_be32(BIT(bit));
+
+ trace_fsi_master_irq_mask(master, link, data->hwirq % FSI_IRQ_COUNT, true);
+ fsi_master_write(master, link, 0, FSI_SLAVE_BASE + FSI_SCI1M, &mask, sizeof(mask));
+ }
+}
+
+static void fsi_master_irq_unmask(struct irq_data *data)
+{
+ unsigned int bit = 31 - (data->hwirq % FSI_IRQ_COUNT);
+
+ if (bit >= FSI_SI1S_MBOX_BIT) {
+ struct fsi_master *master = irq_data_get_irq_chip_data(data);
+ int link = data->hwirq / FSI_IRQ_COUNT;
+ __be32 mask = cpu_to_be32(BIT(bit));
+
+ trace_fsi_master_irq_mask(master, link, data->hwirq % FSI_IRQ_COUNT, false);
+ fsi_master_write(master, link, 0, FSI_SLAVE_BASE + FSI_SSI1M, &mask, sizeof(mask));
+ }
+}
+
int fsi_master_register(struct fsi_master *master)
{
int rc;
@@ -1467,6 +1660,9 @@ int fsi_master_register(struct fsi_master *master)
if (!master->lbus_divider)
master->lbus_divider = FSI_SMODE_LBCRR_DEFAULT;

+ master->irq_chip.name = dev_name(&master->dev);
+ master->irq_chip.irq_mask = fsi_master_irq_mask;
+ master->irq_chip.irq_unmask = fsi_master_irq_unmask;
master->dev.class = &fsi_master_class;

mutex_lock(&master->scan_lock);
diff --git a/drivers/fsi/fsi-master.h b/drivers/fsi/fsi-master.h
index f4cecdff3834..b718eeba3f43 100644
--- a/drivers/fsi/fsi-master.h
+++ b/drivers/fsi/fsi-master.h
@@ -10,6 +10,7 @@
#define DRIVERS_FSI_MASTER_H

#include <linux/device.h>
+#include <linux/irq.h>
#include <linux/mutex.h>

/*
@@ -112,6 +113,7 @@
/* Misc */
#define FSI_CRC_SIZE 4
#define FSI_LINK_ENABLE_SETUP_TIME 10 /* in mS */
+#define FSI_IRQ_COUNT 9

/* fsi-master definition and flags */
#define FSI_MASTER_FLAG_SWCLOCK 0x1
@@ -137,6 +139,7 @@ struct fsi_master {
int n_links;
int flags;
struct mutex scan_lock;
+ struct irq_chip irq_chip;
int (*read)(struct fsi_master *, int link, uint8_t id,
uint32_t addr, void *val, size_t size);
int (*write)(struct fsi_master *, int link, uint8_t id,
@@ -147,6 +150,7 @@ struct fsi_master {
bool enable);
int (*link_config)(struct fsi_master *, int link,
u8 t_send_delay, u8 t_echo_delay);
+ u8 remote_interrupt_status;
};

#define to_fsi_master(d) container_of(d, struct fsi_master, dev)
@@ -176,4 +180,9 @@ extern void fsi_master_unregister(struct fsi_master *master);

extern int fsi_master_rescan(struct fsi_master *master);

+struct irq_domain;
+
+extern int fsi_master_irq(struct fsi_master *master, struct irq_domain *irq_domain,
+ unsigned int link);
+
#endif /* DRIVERS_FSI_MASTER_H */
diff --git a/include/linux/fsi.h b/include/linux/fsi.h
index e0309bf0ae07..c249a95b7ff8 100644
--- a/include/linux/fsi.h
+++ b/include/linux/fsi.h
@@ -8,6 +8,7 @@
#define LINUX_FSI_H

#include <linux/device.h>
+#include <linux/interrupt.h>

struct fsi_device {
struct device dev;
@@ -25,6 +26,7 @@ extern int fsi_device_write(struct fsi_device *dev, uint32_t addr,
const void *val, size_t size);
extern int fsi_device_peek(struct fsi_device *dev, void *val);
extern unsigned long fsi_device_local_bus_frequency(struct fsi_device *dev);
+extern int fsi_device_request_irq(struct fsi_device *dev, irq_handler_t handler, void *data);

struct fsi_device_id {
u8 engine_type;
diff --git a/include/trace/events/fsi.h b/include/trace/events/fsi.h
index da977d59e163..0e4d717ee0ad 100644
--- a/include/trace/events/fsi.h
+++ b/include/trace/events/fsi.h
@@ -8,6 +8,47 @@

#include <linux/tracepoint.h>

+TRACE_EVENT(fsi_master_irq,
+ TP_PROTO(const struct fsi_master *master, unsigned int link, uint32_t si1s,
+ uint32_t srsis0, uint32_t srsis4),
+ TP_ARGS(master, link, si1s, srsis0, srsis4),
+ TP_STRUCT__entry(
+ __field(int, master_idx)
+ __field(unsigned int, link)
+ __field(uint32_t, si1s)
+ __field(uint32_t, srsis0)
+ __field(uint32_t, srsis4)
+ ),
+ TP_fast_assign(
+ __entry->master_idx = master->idx;
+ __entry->link = link;
+ __entry->si1s = si1s;
+ __entry->srsis0 = srsis0;
+ __entry->srsis4 = srsis4;
+ ),
+ TP_printk("fsi%d:%02d si1s:%08x srsis0:%08x srsis4:%08x", __entry->master_idx,
+ __entry->link, __entry->si1s, __entry->srsis0, __entry->srsis4)
+);
+
+TRACE_EVENT(fsi_master_irq_mask,
+ TP_PROTO(const struct fsi_master *master, unsigned int link, unsigned int bit, bool mask),
+ TP_ARGS(master, link, bit, mask),
+ TP_STRUCT__entry(
+ __field(int, master_idx)
+ __field(unsigned int, link)
+ __field(unsigned int, bit)
+ __field(bool, mask)
+ ),
+ TP_fast_assign(
+ __entry->master_idx = master->idx;
+ __entry->link = link;
+ __entry->bit = bit;
+ __entry->mask = mask;
+ ),
+ TP_printk("fsi%d:%02d %s bit:%d", __entry->master_idx, __entry->link,
+ __entry->mask ? "mask" : "unmask", __entry->bit)
+);
+
TRACE_EVENT(fsi_master_xfer,
TP_PROTO(int master_idx, int link, int id, uint32_t addr, size_t size, const void *data,
bool read),
@@ -206,6 +247,25 @@ TRACE_EVENT(fsi_slave_invalid_cfam,
)
);

+TRACE_EVENT(fsi_slave_irq,
+ TP_PROTO(const struct fsi_slave *slave, uint32_t siss),
+ TP_ARGS(slave, siss),
+ TP_STRUCT__entry(
+ __field(int, master_idx)
+ __field(int, link)
+ __field(int, id)
+ __field(uint32_t, siss)
+ ),
+ TP_fast_assign(
+ __entry->master_idx = slave->master->idx;
+ __entry->link = slave->link;
+ __entry->id = slave->id;
+ __entry->siss = siss;
+ ),
+ TP_printk("fsi%d:%02d:%02d siss:%08x", __entry->master_idx, __entry->link, __entry->id,
+ __entry->siss)
+);
+
TRACE_EVENT(fsi_dev_init,
TP_PROTO(const struct fsi_device *dev),
TP_ARGS(dev),
--
2.39.3


2024-02-16 08:19:28

by Krzysztof Kozlowski

[permalink] [raw]
Subject: Re: [PATCH 01/33] dt-bindings: clock: ast2600: Add FSI clock

On 15/02/2024 23:07, Eddie James wrote:
> Add a definition for the FSI clock.
>
> Signed-off-by: Eddie James <[email protected]>
> ---
> include/dt-bindings/clock/ast2600-clock.h | 1 +
> 1 file changed, 1 insertion(+)

Acked-by: Krzysztof Kozlowski <[email protected]>

Best regards,
Krzysztof


2024-02-16 08:19:59

by Krzysztof Kozlowski

[permalink] [raw]
Subject: Re: [PATCH 07/33] ARM: dts: aspeed: p10 and tacoma: Set FSI clock frequency

On 15/02/2024 23:07, Eddie James wrote:
> Now that the driver doesn't hardcode the clock divider, set it
> in the device tree.
>
> Signed-off-by: Eddie James <[email protected]>
> ---
> arch/arm/boot/dts/aspeed/aspeed-bmc-opp-tacoma.dts | 1 +
> arch/arm/boot/dts/aspeed/ibm-power10-dual.dtsi | 1 +
> 2 files changed, 2 insertions(+)

Please do not mix DTS patches with driver code. DTS goes to the end
because driver code CANNOT depend on it (there are exceptions but it was
not explained here).

Best regards,
Krzysztof


2024-02-16 08:20:19

by Krzysztof Kozlowski

[permalink] [raw]
Subject: Re: [PATCH 10/33] fsi: aspeed: Add AST2700 support

On 15/02/2024 23:07, Eddie James wrote:
> AST2700 requires a few bits set differently in the OPB retry
> counter register, so add some match data and set the register
> accordingly.
>
> Signed-off-by: Eddie James <[email protected]>
> ---
> drivers/fsi/fsi-master-aspeed.c | 28 +++++++++++++++++++++++++---


> +
> static const struct of_device_id fsi_master_aspeed_match[] = {
> - { .compatible = "aspeed,ast2600-fsi-master" },
> + {
> + .compatible = "aspeed,ast2600-fsi-master",
> + .data = &fsi_master_ast2600_data,
> + },
> + {
> + .compatible = "aspeed,ast2700-fsi-master",

Undocumented. Really, you do not have checkpatch in IBM?

Please run scripts/checkpatch.pl and fix reported warnings. Some
warnings can be ignored, but the code here looks like it needs a fix.
Feel free to get in touch if the warning is not clear.


Best regards,
Krzysztof


2024-02-16 08:21:27

by Krzysztof Kozlowski

[permalink] [raw]
Subject: Re: [PATCH 26/33] ARM: dts: aspeed: FSI interrupt support

On 15/02/2024 23:07, Eddie James wrote:
> Enable FSI interrupt controllers for AST2600 and P10BMC hub master.
>
> Signed-off-by: Eddie James <[email protected]>
> ---
> arch/arm/boot/dts/aspeed/aspeed-g6.dtsi | 4 ++++
> arch/arm/boot/dts/aspeed/ibm-power10-dual.dtsi | 2 ++
> 2 files changed, 6 insertions(+)

Don't mix DTS with driver code.

Best regards,
Krzysztof


2024-02-16 19:14:54

by Eddie James

[permalink] [raw]
Subject: Re: [PATCH 07/33] ARM: dts: aspeed: p10 and tacoma: Set FSI clock frequency


On 2/16/24 02:08, Krzysztof Kozlowski wrote:
> On 15/02/2024 23:07, Eddie James wrote:
>> Now that the driver doesn't hardcode the clock divider, set it
>> in the device tree.
>>
>> Signed-off-by: Eddie James <[email protected]>
>> ---
>> arch/arm/boot/dts/aspeed/aspeed-bmc-opp-tacoma.dts | 1 +
>> arch/arm/boot/dts/aspeed/ibm-power10-dual.dtsi | 1 +
>> 2 files changed, 2 insertions(+)
> Please do not mix DTS patches with driver code. DTS goes to the end
> because driver code CANNOT depend on it (there are exceptions but it was
> not explained here).


Sure, I didn't realize. Thanks.

Eddie


>
> Best regards,
> Krzysztof
>

2024-02-16 19:18:59

by Eddie James

[permalink] [raw]
Subject: Re: [PATCH 10/33] fsi: aspeed: Add AST2700 support


On 2/16/24 02:09, Krzysztof Kozlowski wrote:
> On 15/02/2024 23:07, Eddie James wrote:
>> AST2700 requires a few bits set differently in the OPB retry
>> counter register, so add some match data and set the register
>> accordingly.
>>
>> Signed-off-by: Eddie James <[email protected]>
>> ---
>> drivers/fsi/fsi-master-aspeed.c | 28 +++++++++++++++++++++++++---
>
>> +
>> static const struct of_device_id fsi_master_aspeed_match[] = {
>> - { .compatible = "aspeed,ast2600-fsi-master" },
>> + {
>> + .compatible = "aspeed,ast2600-fsi-master",
>> + .data = &fsi_master_ast2600_data,
>> + },
>> + {
>> + .compatible = "aspeed,ast2700-fsi-master",
> Undocumented. Really, you do not have checkpatch in IBM?
>
> Please run scripts/checkpatch.pl and fix reported warnings. Some
> warnings can be ignored, but the code here looks like it needs a fix.
> Feel free to get in touch if the warning is not clear.


I ran checkpatch. There are several FSI drivers with undocumented
compatible strings, and the Aspeed master documentation isn't in yaml
format, so that would require an update too. Therefore I ignored the
warning - my mistake. I will document it in v2.


>
>
> Best regards,
> Krzysztof
>

2024-02-20 08:40:31

by Dan Carpenter

[permalink] [raw]
Subject: Re: [PATCH 12/33] fsi: core: Allow cfam device type aliases

Hi Eddie,

kernel test robot noticed the following build warnings:

https://git-scm.com/docs/git-format-patch#_base_tree_information]

url: https://github.com/intel-lab-lkp/linux/commits/Eddie-James/dt-bindings-clock-ast2600-Add-FSI-clock/20240216-061934
base: git://git.kernel.org/pub/scm/linux/kernel/git/andi.shyti/linux.git i2c/i2c-host
patch link: https://lore.kernel.org/r/20240215220759.976998-13-eajames%40linux.ibm.com
patch subject: [PATCH 12/33] fsi: core: Allow cfam device type aliases
config: arm64-randconfig-r081-20240216 (https://download.01.org/0day-ci/archive/20240220/[email protected]/config)
compiler: clang version 19.0.0git (https://github.com/llvm/llvm-project 36adfec155de366d722f2bac8ff9162289dcf06c)

If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <[email protected]>
| Reported-by: Dan Carpenter <[email protected]>
| Closes: https://lore.kernel.org/r/[email protected]/

smatch warnings:
drivers/fsi/fsi-core.c:919 __fsi_get_new_minor() error: testing array offset 'type' after use.

vim +/type +919 drivers/fsi/fsi-core.c

3f4ac5b0b27f16 Eddie James 2024-02-15 894 static int __fsi_get_new_minor(struct fsi_slave *slave, struct device_node *np,
3f4ac5b0b27f16 Eddie James 2024-02-15 895 enum fsi_dev_type type, dev_t *out_dev, int *out_index)
0ab5fe5374743d Benjamin Herrenschmidt 2018-06-20 896 {
0ab5fe5374743d Benjamin Herrenschmidt 2018-06-20 897 int cid = slave->chip_id;
0ab5fe5374743d Benjamin Herrenschmidt 2018-06-20 898 int id;
0ab5fe5374743d Benjamin Herrenschmidt 2018-06-20 899
3f4ac5b0b27f16 Eddie James 2024-02-15 900 if (np) {
3f4ac5b0b27f16 Eddie James 2024-02-15 901 int aid = of_alias_get_id(np, fsi_dev_type_names[type]);
^^^^
if type >= 4 we are in trouble

3f4ac5b0b27f16 Eddie James 2024-02-15 902
3f4ac5b0b27f16 Eddie James 2024-02-15 903 if (aid >= 0) {
3f4ac5b0b27f16 Eddie James 2024-02-15 904 /* Use the same scheme as the legacy numbers. */
3f4ac5b0b27f16 Eddie James 2024-02-15 905 id = (aid << 2) | type;
3f4ac5b0b27f16 Eddie James 2024-02-15 906 id = ida_alloc_range(&fsi_minor_ida, id, id, GFP_KERNEL);
3f4ac5b0b27f16 Eddie James 2024-02-15 907 if (id >= 0) {
3f4ac5b0b27f16 Eddie James 2024-02-15 908 *out_index = aid;
3f4ac5b0b27f16 Eddie James 2024-02-15 909 *out_dev = fsi_base_dev + id;
3f4ac5b0b27f16 Eddie James 2024-02-15 910 return 0;
3f4ac5b0b27f16 Eddie James 2024-02-15 911 }
3f4ac5b0b27f16 Eddie James 2024-02-15 912
3f4ac5b0b27f16 Eddie James 2024-02-15 913 if (id != -ENOSPC)
3f4ac5b0b27f16 Eddie James 2024-02-15 914 return id;
3f4ac5b0b27f16 Eddie James 2024-02-15 915 }
3f4ac5b0b27f16 Eddie James 2024-02-15 916 }
3f4ac5b0b27f16 Eddie James 2024-02-15 917
0ab5fe5374743d Benjamin Herrenschmidt 2018-06-20 918 /* Check if we qualify for legacy numbering */
0ab5fe5374743d Benjamin Herrenschmidt 2018-06-20 @919 if (cid >= 0 && cid < 16 && type < 4) {
^^^^^^^^
checked too late

641511bfcc5e01 Eddie James 2023-06-12 920 /*
641511bfcc5e01 Eddie James 2023-06-12 921 * Try reserving the legacy number, which has 0 - 0x3f reserved
641511bfcc5e01 Eddie James 2023-06-12 922 * in the ida range. cid goes up to 0xf and type contains two
641511bfcc5e01 Eddie James 2023-06-12 923 * bits, so construct the id with the below two bit shift.
641511bfcc5e01 Eddie James 2023-06-12 924 */
641511bfcc5e01 Eddie James 2023-06-12 925 id = (cid << 2) | type;
85f4e899de32ba Eddie James 2023-06-12 926 id = ida_alloc_range(&fsi_minor_ida, id, id, GFP_KERNEL);
0ab5fe5374743d Benjamin Herrenschmidt 2018-06-20 927 if (id >= 0) {
0ab5fe5374743d Benjamin Herrenschmidt 2018-06-20 928 *out_index = fsi_adjust_index(cid);
0ab5fe5374743d Benjamin Herrenschmidt 2018-06-20 929 *out_dev = fsi_base_dev + id;
0ab5fe5374743d Benjamin Herrenschmidt 2018-06-20 930 return 0;
0ab5fe5374743d Benjamin Herrenschmidt 2018-06-20 931 }
0ab5fe5374743d Benjamin Herrenschmidt 2018-06-20 932 /* Other failure */
0ab5fe5374743d Benjamin Herrenschmidt 2018-06-20 933 if (id != -ENOSPC)
0ab5fe5374743d Benjamin Herrenschmidt 2018-06-20 934 return id;
0ab5fe5374743d Benjamin Herrenschmidt 2018-06-20 935 /* Fallback to non-legacy allocation */
0ab5fe5374743d Benjamin Herrenschmidt 2018-06-20 936 }
85f4e899de32ba Eddie James 2023-06-12 937 id = ida_alloc_range(&fsi_minor_ida, FSI_CHAR_LEGACY_TOP,
85f4e899de32ba Eddie James 2023-06-12 938 FSI_CHAR_MAX_DEVICES - 1, GFP_KERNEL);
0ab5fe5374743d Benjamin Herrenschmidt 2018-06-20 939 if (id < 0)
0ab5fe5374743d Benjamin Herrenschmidt 2018-06-20 940 return id;
0ab5fe5374743d Benjamin Herrenschmidt 2018-06-20 941 *out_index = fsi_adjust_index(id);
0ab5fe5374743d Benjamin Herrenschmidt 2018-06-20 942 *out_dev = fsi_base_dev + id;
0ab5fe5374743d Benjamin Herrenschmidt 2018-06-20 943 return 0;
0ab5fe5374743d Benjamin Herrenschmidt 2018-06-20 944 }

--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki


2024-02-22 05:46:12

by Stephen Boyd

[permalink] [raw]
Subject: Re: [PATCH 01/33] dt-bindings: clock: ast2600: Add FSI clock

Quoting Eddie James (2024-02-15 14:07:27)
> Add a definition for the FSI clock.
>
> Signed-off-by: Eddie James <[email protected]>
> ---

Applied to clk-next

2024-02-22 05:46:47

by Stephen Boyd

[permalink] [raw]
Subject: Re: [PATCH 02/33] clk: ast2600: Add FSI parent clock with correct rate

Quoting Eddie James (2024-02-15 14:07:28)
> In order to calculate correct FSI bus clocks, the FSI clock must
> correctly calculate the rate from the parent (APLL / 4).
>
> Signed-off-by: Eddie James <[email protected]>
> ---

Applied to clk-next

2024-02-22 12:33:40

by Joel Stanley

[permalink] [raw]
Subject: Re: [PATCH 07/33] ARM: dts: aspeed: p10 and tacoma: Set FSI clock frequency

On Sat, 17 Feb 2024 at 05:44, Eddie James <[email protected]> wrote:
>
>
> On 2/16/24 02:08, Krzysztof Kozlowski wrote:
> > On 15/02/2024 23:07, Eddie James wrote:
> >> Now that the driver doesn't hardcode the clock divider, set it
> >> in the device tree.
> >>
> >> Signed-off-by: Eddie James <[email protected]>
> >> ---
> >> arch/arm/boot/dts/aspeed/aspeed-bmc-opp-tacoma.dts | 1 +
> >> arch/arm/boot/dts/aspeed/ibm-power10-dual.dtsi | 1 +
> >> 2 files changed, 2 insertions(+)
> > Please do not mix DTS patches with driver code. DTS goes to the end
> > because driver code CANNOT depend on it (there are exceptions but it was
> > not explained here).

No, this is fine. Please continue sending the patches in logical order
as you see fit.