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.
Changes since v1:
- Remove clock patches since those have been merged
- Add AST2700 binding documentation
- Fix build warning
Eddie James (31):
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
dt-bindings: fsi: Add AST2700 compatible
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
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
.../bindings/fsi/fsi-master-aspeed.txt | 2 +-
.../boot/dts/aspeed/aspeed-bmc-opp-tacoma.dts | 1 +
.../arm/boot/dts/aspeed/ibm-power10-dual.dtsi | 1 +
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/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 ++
14 files changed, 1421 insertions(+), 639 deletions(-)
create mode 100644 include/trace/events/i2c_fsi.h
--
2.39.3
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
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
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 07ce3b2bc62a..44e48e39e6e9 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
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
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
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]>
---
Changes since v1:
- Check fsi device type variable before use to ensure we don't go out
of bounds on the fsi_dev_type_names array.
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..8ad4feb71c8e 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 && type < 4) {
+ 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
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
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),
- ®, 4);
-
- rc = fsi_device_write(hub->upstream, FSI_MSENP0 + (4 * idx), ®, 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, ®, 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, ®, 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, ®, 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, ®, sizeof(reg));
- if (rc)
- return rc;
-
- reg = cpu_to_be32(0xffff0000);
- rc = fsi_device_write(dev, FSI_MDLYR, ®, sizeof(reg));
- if (rc)
- return rc;
-
- reg = cpu_to_be32(~0);
- rc = fsi_device_write(dev, FSI_MSENP0, ®, 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, ®, sizeof(reg));
- if (rc)
- return rc;
-
- rc = fsi_device_read(dev, FSI_MAEB, ®, 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, ®, sizeof(reg));
- if (rc)
- return rc;
-
- rc = fsi_device_read(dev, FSI_MLEVP0, ®, 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, ®, sizeof(reg));
- if (rc)
- return rc;
-
- reg = cpu_to_be32(FSI_MRESB_RST_ERR);
- return fsi_device_write(dev, FSI_MRESB0, ®, 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, ®);
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
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, ®map_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, ®);
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
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
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
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
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
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
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 ce463ff8ef70..ff5d9d0c9992 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, ®, 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, ®, 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, ®, 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, ®,
+ 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, ®,
+ 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,
+ ®, sizeof(reg));
+
+ if (master->remote_interrupt_status & 0xf0)
+ fsi_master_write(master, link, 0, FSI_SLAVE_BASE + FSI_SRSIC4,
+ ®, 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
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
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
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
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
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
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
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
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
Master drivers may need access to the slave definitions.
Signed-off-by: Eddie James <[email protected]>
---
drivers/fsi/fsi-core.c | 35 -----------------
drivers/fsi/fsi-slave.h | 84 +++++++++++++++++++++++++++++++++++++++++
2 files changed, 84 insertions(+), 35 deletions(-)
diff --git a/drivers/fsi/fsi-core.c b/drivers/fsi/fsi-core.c
index 097d5a780264..7bf0c96fc017 100644
--- a/drivers/fsi/fsi-core.c
+++ b/drivers/fsi/fsi-core.c
@@ -45,41 +45,6 @@
static const int engine_page_size = 0x400;
-#define FSI_SLAVE_BASE 0x800
-
-/*
- * FSI slave engine control register offsets
- */
-#define FSI_SMODE 0x0 /* R/W: Mode register */
-#define FSI_SISC 0x8 /* R/W: Interrupt condition */
-#define FSI_SSTAT 0x14 /* R : Slave status */
-#define FSI_SLBUS 0x30 /* W : LBUS Ownership */
-#define FSI_LLMODE 0x100 /* R/W: Link layer mode register */
-
-/*
- * SMODE fields
- */
-#define FSI_SMODE_WSC 0x80000000 /* Warm start done */
-#define FSI_SMODE_ECRC 0x20000000 /* Hw CRC check */
-#define FSI_SMODE_SID_SHIFT 24 /* ID shift */
-#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_SD_SHIFT 16 /* Send delay shift */
-#define FSI_SMODE_SD_MASK 0xf /* Send delay mask */
-#define FSI_SMODE_LBCRR_SHIFT 8 /* Clk ratio shift */
-#define FSI_SMODE_LBCRR_MASK 0xf /* Clk ratio mask */
-
-/*
- * SLBUS fields
- */
-#define FSI_SLBUS_FORCE 0x80000000 /* Force LBUS ownership */
-
-/*
- * LLMODE fields
- */
-#define FSI_LLMODE_ASYNC 0x1
-
#define FSI_SLAVE_SIZE_23b 0x800000
static DEFINE_IDA(master_ida);
diff --git a/drivers/fsi/fsi-slave.h b/drivers/fsi/fsi-slave.h
index 1d63a585829d..dba65bd4e083 100644
--- a/drivers/fsi/fsi-slave.h
+++ b/drivers/fsi/fsi-slave.h
@@ -7,6 +7,90 @@
#include <linux/cdev.h>
#include <linux/device.h>
+#define FSI_SLAVE_BASE 0x800
+
+/*
+ * FSI slave engine control register offsets
+ */
+#define FSI_SMODE 0x0 /* R/W: Mode register */
+#define FSI_SISC 0x8 /* R : Interrupt condition */
+#define FSI_SCISC 0x8 /* C : Clear interrupt condition */
+#define FSI_SISM 0xc /* R/W: Interrupt mask */
+#define FSI_SISS 0x10 /* R : Interrupt status */
+#define FSI_SSISM 0x10 /* S : Set interrupt mask */
+#define FSI_SCISM 0x14 /* C : Clear interrupt mask */
+#define FSI_SSTAT 0x14 /* R : Slave status */
+#define FSI_SI1S 0x1c /* R : Slave interrupt 1 status */
+#define FSI_SSI1M 0x1c /* S : Set slave interrupt 1 mask */
+#define FSI_SCI1M 0x20 /* C : Clear slave interrupt 1 mask */
+#define FSI_SLBUS 0x30 /* W : LBUS Ownership */
+#define FSI_SRSIC0 0x68 /* C : Clear remote interrupt condition */
+#define FSI_SRSIC4 0x6c /* C : Clear remote interrupt condition */
+#define FSI_SRSIM0 0x70 /* R/W: Remote interrupt mask */
+#define FSI_SRSIM4 0x74 /* R/W: Remote interrupt mask */
+#define FSI_SRSIS0 0x78 /* R : Remote interrupt status */
+#define FSI_SRSIS4 0x7c /* R : Remote interrupt status */
+#define FSI_LLMODE 0x100 /* R/W: Link layer mode register */
+
+/*
+ * SMODE fields
+ */
+#define FSI_SMODE_WSC 0x80000000 /* Warm start done */
+#define FSI_SMODE_ECRC 0x20000000 /* Hw CRC check */
+#define FSI_SMODE_SID_SHIFT 24 /* ID shift */
+#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_SD_SHIFT 16 /* Send delay shift */
+#define FSI_SMODE_SD_MASK 0xf /* Send delay mask */
+#define FSI_SMODE_LBCRR_SHIFT 8 /* Clk ratio shift */
+#define FSI_SMODE_LBCRR_MASK 0xf /* Clk ratio mask */
+
+/*
+ * SISS fields
+ */
+#define FSI_SISS_CRC_ERROR BIT(31)
+#define FSI_SISS_PROTO_ERROR BIT(30)
+#define FSI_SISS_LBUS_PARITY_ERROR BIT(29)
+#define FSI_SISS_LBUS_PROTO_ERROR BIT(28)
+#define FSI_SISS_ACCESS_ERROR BIT(27)
+#define FSI_SISS_LBUS_OWNERSHIP_ERROR BIT(26)
+#define FSI_SISS_LBUS_OWNERSHIP_CHANGE BIT(25)
+#define FSI_SISS_ASYNC_MODE_ERROR BIT(14)
+#define FSI_SISS_OPB_ACCESS_ERROR BIT(13)
+#define FSI_SISS_OPB_FENCED BIT(12)
+#define FSI_SISS_OPB_PARITY_ERROR BIT(11)
+#define FSI_SISS_OPB_PROTO_ERROR BIT(10)
+#define FSI_SISS_OPB_TIMEOUT BIT(9)
+#define FSI_SISS_OPB_ERROR_ACK BIT(8)
+#define FSI_SISS_MFSI_MASTER_ERROR BIT(3)
+#define FSI_SISS_MFSI_PORT_ERROR BIT(2)
+#define FSI_SISS_MFSI_HP BIT(1)
+#define FSI_SISS_MFSI_CR_PARITY_ERROR BIT(0)
+#define FSI_SISS_ALL 0xfe007f00
+
+/*
+ * SI1S fields
+ */
+#define FSI_SI1S_SLAVE_BIT 31
+#define FSI_SI1S_SHIFT_BIT 30
+#define FSI_SI1S_SCOM_BIT 29
+#define FSI_SI1S_SCRATCH_BIT 28
+#define FSI_SI1S_I2C_BIT 27
+#define FSI_SI1S_SPI_BIT 26
+#define FSI_SI1S_SBEFIFO_BIT 25
+#define FSI_SI1S_MBOX_BIT 24
+
+/*
+ * SLBUS fields
+ */
+#define FSI_SLBUS_FORCE 0x80000000 /* Force LBUS ownership */
+
+/*
+ * LLMODE fields
+ */
+#define FSI_LLMODE_ASYNC 0x1
+
struct fsi_master;
struct fsi_slave {
--
2.39.3
Document the 2700 FSI master compatible string.
Signed-off-by: Eddie James <[email protected]>
---
Documentation/devicetree/bindings/fsi/fsi-master-aspeed.txt | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Documentation/devicetree/bindings/fsi/fsi-master-aspeed.txt b/Documentation/devicetree/bindings/fsi/fsi-master-aspeed.txt
index 9853fefff5d8..5bfe10abbaa5 100644
--- a/Documentation/devicetree/bindings/fsi/fsi-master-aspeed.txt
+++ b/Documentation/devicetree/bindings/fsi/fsi-master-aspeed.txt
@@ -5,7 +5,7 @@ The AST2600 contains two identical FSI masters. They share a clock and have a
separate interrupt line and output pins.
Required properties:
- - compatible: "aspeed,ast2600-fsi-master"
+ - compatible: "aspeed,ast2600-fsi-master" or "aspeed,ast2700-fsi-master"
- reg: base address and length
- clocks: phandle and clock number
- interrupts: platform dependent interrupt description
--
2.39.3
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 8ad4feb71c8e..ce463ff8ef70 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
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
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
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
On 26/02/2024 17:52, Eddie James wrote:
> Document the 2700 FSI master compatible string.
>
> Signed-off-by: Eddie James <[email protected]>
Acked-by: Krzysztof Kozlowski <[email protected]>
Best regards,
Krzysztof
On Mon, 26 Feb 2024 10:52:50 -0600, Eddie James wrote:
> 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.
>
> Changes since v1:
> - Remove clock patches since those have been merged
> - Add AST2700 binding documentation
> - Fix build warning
>
> Eddie James (31):
> 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
> dt-bindings: fsi: Add AST2700 compatible
> 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
> 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
>
> .../bindings/fsi/fsi-master-aspeed.txt | 2 +-
> .../boot/dts/aspeed/aspeed-bmc-opp-tacoma.dts | 1 +
> .../arm/boot/dts/aspeed/ibm-power10-dual.dtsi | 1 +
> 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/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 ++
> 14 files changed, 1421 insertions(+), 639 deletions(-)
> create mode 100644 include/trace/events/i2c_fsi.h
>
> --
> 2.39.3
>
>
>
My bot found new DT warnings on the .dts files added or changed in this
series.
Some warnings may be from an existing SoC .dtsi. Or perhaps the warnings
are fixed by another series. Ultimately, it is up to the platform
maintainer whether these warnings are acceptable or not.
If you already ran DT checks and didn't see these error(s), then
make sure dt-schema is up to date:
pip3 install dtschema --upgrade
New warnings running 'make CHECK_DTBS=y aspeed/aspeed-bmc-opp-tacoma.dtb' for [email protected]:
arch/arm/boot/dts/aspeed/aspeed-bmc-opp-tacoma.dtb: ahb: apb: {'compatible': ['simple-bus'], '#address-cells': [[1]], '#size-cells': [[1]], 'ranges': True, 'crypto@1e6d0000': {'compatible': ['aspeed,ast2600-hace'], 'reg': [[510459904, 512]], 'interrupts': [[0, 4, 4]], 'clocks': [[2, 9]], 'resets': [[2, 4]]}, 'syscon@1e6e2000': {'compatible': ['aspeed,ast2600-scu', 'syscon', 'simple-mfd'], 'reg': [[510533632, 4096]], 'ranges': [[0, 510533632, 4096]], '#address-cells': [[1]], '#size-cells': [[1]], '#clock-cells': [[1]], '#reset-cells': [[1]], 'phandle': [[2]], 'pinctrl': {'compatible': ['aspeed,ast2600-pinctrl'], 'pinctrl-names': ['default'], 'pinctrl-0': [[13, 14]], 'phandle': [[18]], 'adc0_default': {'function': ['ADC0'], 'groups': ['ADC0']}, 'adc1_default': {'function': ['ADC1'], 'groups': ['ADC1']}, 'adc10_default': {'function': ['ADC10'], 'groups': ['ADC10']}, 'adc11_default': {'function': ['ADC11'], 'groups': ['ADC11']}, 'adc12_default': {'function': ['ADC12'], 'groups': ['ADC12'
]}, 'adc13_default': {'function': ['ADC13'], 'groups': ['ADC13']}, 'adc14_default': {'function': ['ADC14'], 'groups': ['ADC14']}, 'adc15_default': {'function': ['ADC15'], 'groups': ['ADC15']}, 'adc2_default': {'function': ['ADC2'], 'groups': ['ADC2']}, 'adc3_default': {'function': ['ADC3'], 'groups': ['ADC3']}, 'adc4_default': {'function': ['ADC4'], 'groups': ['ADC4']}, 'adc5_default': {'function': ['ADC5'], 'groups': ['ADC5']}, 'adc6_default': {'function': ['ADC6'], 'groups': ['ADC6']}, 'adc7_default': {'function': ['ADC7'], 'groups': ['ADC7']}, 'adc8_default': {'function': ['ADC8'], 'groups': ['ADC8']}, 'adc9_default': {'function': ['ADC9'], 'groups': ['ADC9']}, 'bmcint_default': {'function': ['BMCINT'], 'groups': ['BMCINT']}, 'espi_default': {'function': ['ESPI'], 'groups': ['ESPI']}, 'espialt_default': {'function': ['ESPIALT'], 'groups': ['ESPIALT']}, 'fsi1_default': {'function': ['FSI1'], 'groups': ['FSI1'], 'phandle': [[54]]}, 'fsi2_default': {'function': ['FSI2'], 'groups': [
'FSI2'], 'phandle': [[56]]}, 'fwspiabr_default': {'function': ['FWSPIABR'], 'groups': ['FWSPIABR']}, 'fwspid_default': {'function': ['FWSPID'], 'groups': ['FWSPID']}, 'fwqspi_default': {'function': ['FWQSPI'], 'groups': ['FWQSPI']}, 'fwspiwp_default': {'function': ['FWSPIWP'], 'groups': ['FWSPIWP']}, 'gpit0_default': {'function': ['GPIT0'], 'groups': ['GPIT0']}, 'gpit1_default': {'function': ['GPIT1'], 'groups': ['GPIT1']}, 'gpit2_default': {'function': ['GPIT2'], 'groups': ['GPIT2']}, 'gpit3_default': {'function': ['GPIT3'], 'groups': ['GPIT3']}, 'gpit4_default': {'function': ['GPIT4'], 'groups': ['GPIT4']}, 'gpit5_default': {'function': ['GPIT5'], 'groups': ['GPIT5']}, 'gpit6_default': {'function': ['GPIT6'], 'groups': ['GPIT6']}, 'gpit7_default': {'function': ['GPIT7'], 'groups': ['GPIT7']}, 'gpiu0_default': {'function': ['GPIU0'], 'groups': ['GPIU0']}, 'gpiu1_default': {'function': ['GPIU1'], 'groups': ['GPIU1']}, 'gpiu2_default': {'function': ['GPIU2'], 'groups': ['GPIU2']}, 'g
piu3_default': {'function': ['GPIU3'], 'groups': ['GPIU3']}, 'gpiu4_default': {'function': ['GPIU4'], 'groups': ['GPIU4']}, 'gpiu5_default': {'function': ['GPIU5'], 'groups': ['GPIU5']}, 'gpiu6_default': {'function': ['GPIU6'], 'groups': ['GPIU6']}, 'gpiu7_default': {'function': ['GPIU7'], 'groups': ['GPIU7']}, 'hvi3c3_default': {'function': ['I3C3'], 'groups': ['HVI3C3']}, 'hvi3c4_default': {'function': ['I3C4'], 'groups': ['HVI3C4']}, 'i2c1_default': {'function': ['I2C1'], 'groups': ['I2C1'], 'phandle': [[38]]}, 'i2c10_default': {'function': ['I2C10'], 'groups': ['I2C10'], 'phandle': [[47]]}, 'i2c11_default': {'function': ['I2C11'], 'groups': ['I2C11'], 'phandle': [[48]]}, 'i2c12_default': {'function': ['I2C12'], 'groups': ['I2C12'], 'phandle': [[49]]}, 'i2c13_default': {'function': ['I2C13'], 'groups': ['I2C13'], 'phandle': [[50]]}, 'i2c14_default': {'function': ['I2C14'], 'groups': ['I2C14'], 'phandle': [[51]]}, 'i2c15_default': {'function': ['I2C15'], 'groups': ['I2C15'], 'phan
dle': [[52]]}, 'i2c16_default': {'function': ['I2C16'], 'groups': ['I2C16'], 'phandle': [[53]]}, 'i2c2_default': {'function': ['I2C2'], 'groups': ['I2C2'], 'phandle': [[39]]}, 'i2c3_default': {'function': ['I2C3'], 'groups': ['I2C3'], 'phandle': [[40]]}, 'i2c4_default': {'function': ['I2C4'], 'groups': ['I2C4'], 'phandle': [[41]]}, 'i2c5_default': {'function': ['I2C5'], 'groups': ['I2C5'], 'phandle': [[42]]}, 'i2c6_default': {'function': ['I2C6'], 'groups': ['I2C6'], 'phandle': [[43]]}, 'i2c7_default': {'function': ['I2C7'], 'groups': ['I2C7'], 'phandle': [[44]]}, 'i2c8_default': {'function': ['I2C8'], 'groups': ['I2C8'], 'phandle': [[45]]}, 'i2c9_default': {'function': ['I2C9'], 'groups': ['I2C9'], 'phandle': [[46]]}, 'i3c1_default': {'function': ['I3C1'], 'groups': ['I3C1']}, 'i3c2_default': {'function': ['I3C2'], 'groups': ['I3C2']}, 'i3c3_default': {'function': ['I3C3'], 'groups': ['I3C3']}, 'i3c4_default': {'function': ['I3C4'], 'groups': ['I3C4']}, 'i3c5_default': {'function':
['I3C5'], 'groups': ['I3C5']}, 'i3c6_default': {'function': ['I3C6'], 'groups': ['I3C6']}, 'jtagm_default': {'function': ['JTAGM'], 'groups': ['JTAGM']}, 'lhpd_default': {'function': ['LHPD'], 'groups': ['LHPD']}, 'lhsirq_default': {'function': ['LHSIRQ'], 'groups': ['LHSIRQ']}, 'lpc_default': {'function': ['LPC'], 'groups': ['LPC'], 'phandle': [[13]]}, 'lpchc_default': {'function': ['LPCHC'], 'groups': ['LPCHC']}, 'lpcpd_default': {'function': ['LPCPD'], 'groups': ['LPCPD']}, 'lpcpme_default': {'function': ['LPCPME'], 'groups': ['LPCPME']}, 'lpcsmi_default': {'function': ['LPCSMI'], 'groups': ['LPCSMI']}, 'lsirq_default': {'function': ['LSIRQ'], 'groups': ['LSIRQ'], 'phandle': [[14]]}, 'maclink1_default': {'function': ['MACLINK1'], 'groups': ['MACLINK1']}, 'maclink2_default': {'function': ['MACLINK2'], 'groups': ['MACLINK2']}, 'maclink3_default': {'function': ['MACLINK3'], 'groups': ['MACLINK3']}, 'maclink4_default': {'function': ['MACLINK4'], 'groups': ['MACLINK4']}, 'mdio1_defau
lt': {'function': ['MDIO1'], 'groups': ['MDIO1'], 'phandle': [[4]]}, 'mdio2_default': {'function': ['MDIO2'], 'groups': ['MDIO2'], 'phandle': [[5]]}, 'mdio3_default': {'function': ['MDIO3'], 'groups': ['MDIO3'], 'phandle': [[6]]}, 'mdio4_default': {'function': ['MDIO4'], 'groups': ['MDIO4'], 'phandle': [[7]]}, 'ncts1_default': {'function': ['NCTS1'], 'groups': ['NCTS1']}, 'ncts2_default': {'function': ['NCTS2'], 'groups': ['NCTS2']}, 'ncts3_default': {'function': ['NCTS3'], 'groups': ['NCTS3']}, 'ncts4_default': {'function': ['NCTS4'], 'groups': ['NCTS4']}, 'ndcd1_default': {'function': ['NDCD1'], 'groups': ['NDCD1']}, 'ndcd2_default': {'function': ['NDCD2'], 'groups': ['NDCD2']}, 'ndcd3_default': {'function': ['NDCD3'], 'groups': ['NDCD3']}, 'ndcd4_default': {'function': ['NDCD4'], 'groups': ['NDCD4']}, 'ndsr1_default': {'function': ['NDSR1'], 'groups': ['NDSR1']}, 'ndsr2_default': {'function': ['NDSR2'], 'groups': ['NDSR2']}, 'ndsr3_default': {'function': ['NDSR3'], 'groups': ['ND
SR3']}, 'ndsr4_default': {'function': ['NDSR4'], 'groups': ['NDSR4']}, 'ndtr1_default': {'function': ['NDTR1'], 'groups': ['NDTR1']}, 'ndtr2_default': {'function': ['NDTR2'], 'groups': ['NDTR2']}, 'ndtr3_default': {'function': ['NDTR3'], 'groups': ['NDTR3']}, 'ndtr4_default': {'function': ['NDTR4'], 'groups': ['NDTR4']}, 'nri1_default': {'function': ['NRI1'], 'groups': ['NRI1']}, 'nri2_default': {'function': ['NRI2'], 'groups': ['NRI2']}, 'nri3_default': {'function': ['NRI3'], 'groups': ['NRI3']}, 'nri4_default': {'function': ['NRI4'], 'groups': ['NRI4']}, 'nrts1_default': {'function': ['NRTS1'], 'groups': ['NRTS1']}, 'nrts2_default': {'function': ['NRTS2'], 'groups': ['NRTS2']}, 'nrts3_default': {'function': ['NRTS3'], 'groups': ['NRTS3']}, 'nrts4_default': {'function': ['NRTS4'], 'groups': ['NRTS4']}, 'oscclk_default': {'function': ['OSCCLK'], 'groups': ['OSCCLK']}, 'pewake_default': {'function': ['PEWAKE'], 'groups': ['PEWAKE']}, 'pwm0_default': {'function': ['PWM0'], 'groups': [
'PWM0']}, 'pwm1_default': {'function': ['PWM1'], 'groups': ['PWM1']}, 'pwm10g0_default': {'function': ['PWM10'], 'groups': ['PWM10G0']}, 'pwm10g1_default': {'function': ['PWM10'], 'groups': ['PWM10G1']}, 'pwm11g0_default': {'function': ['PWM11'], 'groups': ['PWM11G0']}, 'pwm11g1_default': {'function': ['PWM11'], 'groups': ['PWM11G1']}, 'pwm12g0_default': {'function': ['PWM12'], 'groups': ['PWM12G0']}, 'pwm12g1_default': {'function': ['PWM12'], 'groups': ['PWM12G1']}, 'pwm13g0_default': {'function': ['PWM13'], 'groups': ['PWM13G0']}, 'pwm13g1_default': {'function': ['PWM13'], 'groups': ['PWM13G1']}, 'pwm14g0_default': {'function': ['PWM14'], 'groups': ['PWM14G0']}, 'pwm14g1_default': {'function': ['PWM14'], 'groups': ['PWM14G1']}, 'pwm15g0_default': {'function': ['PWM15'], 'groups': ['PWM15G0']}, 'pwm15g1_default': {'function': ['PWM15'], 'groups': ['PWM15G1']}, 'pwm2_default': {'function': ['PWM2'], 'groups': ['PWM2']}, 'pwm3_default': {'function': ['PWM3'], 'groups': ['PWM3']}, 'pw
m4_default': {'function': ['PWM4'], 'groups': ['PWM4']}, 'pwm5_default': {'function': ['PWM5'], 'groups': ['PWM5']}, 'pwm6_default': {'function': ['PWM6'], 'groups': ['PWM6']}, 'pwm7_default': {'function': ['PWM7'], 'groups': ['PWM7']}, 'pwm8g0_default': {'function': ['PWM8'], 'groups': ['PWM8G0']}, 'pwm8g1_default': {'function': ['PWM8'], 'groups': ['PWM8G1']}, 'pwm9g0_default': {'function': ['PWM9'], 'groups': ['PWM9G0']}, 'pwm9g1_default': {'function': ['PWM9'], 'groups': ['PWM9G1']}, 'qspi1_default': {'function': ['SPI1'], 'groups': ['QSPI1']}, 'qspi2_default': {'function': ['SPI2'], 'groups': ['QSPI2']}, 'rgmii1_default': {'function': ['RGMII1'], 'groups': ['RGMII1']}, 'rgmii2_default': {'function': ['RGMII2'], 'groups': ['RGMII2']}, 'rgmii3_default': {'function': ['RGMII3'], 'groups': ['RGMII3']}, 'rgmii4_default': {'function': ['RGMII4'], 'groups': ['RGMII4']}, 'rmii1_default': {'function': ['RMII1'], 'groups': ['RMII1']}, 'rmii2_default': {'function': ['RMII2'], 'groups': ['
RMII2']}, 'rmii3_default': {'function': ['RMII3'], 'groups': ['RMII3'], 'phandle': [[8]]}, 'rmii4_default': {'function': ['RMII4'], 'groups': ['RMII4']}, 'rxd1_default': {'function': ['RXD1'], 'groups': ['RXD1'], 'phandle': [[23]]}, 'rxd2_default': {'function': ['RXD2'], 'groups': ['RXD2'], 'phandle': [[29]]}, 'rxd3_default': {'function': ['RXD3'], 'groups': ['RXD3'], 'phandle': [[31]]}, 'rxd4_default': {'function': ['RXD4'], 'groups': ['RXD4'], 'phandle': [[33]]}, 'salt1_default': {'function': ['SALT1'], 'groups': ['SALT1']}, 'salt10g0_default': {'function': ['SALT10'], 'groups': ['SALT10G0']}, 'salt10g1_default': {'function': ['SALT10'], 'groups': ['SALT10G1']}, 'salt11g0_default': {'function': ['SALT11'], 'groups': ['SALT11G0']}, 'salt11g1_default': {'function': ['SALT11'], 'groups': ['SALT11G1']}, 'salt12g0_default': {'function': ['SALT12'], 'groups': ['SALT12G0']}, 'salt12g1_default': {'function': ['SALT12'], 'groups': ['SALT12G1']}, 'salt13g0_default': {'function': ['SALT13'],
'groups': ['SALT13G0']}, 'salt13g1_default': {'function': ['SALT13'], 'groups': ['SALT13G1']}, 'salt14g0_default': {'function': ['SALT14'], 'groups': ['SALT14G0']}, 'salt14g1_default': {'function': ['SALT14'], 'groups': ['SALT14G1']}, 'salt15g0_default': {'function': ['SALT15'], 'groups': ['SALT15G0']}, 'salt15g1_default': {'function': ['SALT15'], 'groups': ['SALT15G1']}, 'salt16g0_default': {'function': ['SALT16'], 'groups': ['SALT16G0']}, 'salt16g1_default': {'function': ['SALT16'], 'groups': ['SALT16G1']}, 'salt2_default': {'function': ['SALT2'], 'groups': ['SALT2']}, 'salt3_default': {'function': ['SALT3'], 'groups': ['SALT3']}, 'salt4_default': {'function': ['SALT4'], 'groups': ['SALT4']}, 'salt5_default': {'function': ['SALT5'], 'groups': ['SALT5']}, 'salt6_default': {'function': ['SALT6'], 'groups': ['SALT6']}, 'salt7_default': {'function': ['SALT7'], 'groups': ['SALT7']}, 'salt8_default': {'function': ['SALT8'], 'groups': ['SALT8']}, 'salt9g0_default': {'function': ['SALT9'
], 'groups': ['SALT9G0']}, 'salt9g1_default': {'function': ['SALT9'], 'groups': ['SALT9G1']}, 'sd1_default': {'function': ['SD1'], 'groups': ['SD1']}, 'sd2_default': {'function': ['SD2'], 'groups': ['SD2']}, 'emmc_default': {'function': ['EMMC'], 'groups': ['EMMCG4'], 'phandle': [[27]]}, 'sgpm1_default': {'function': ['SGPM1'], 'groups': ['SGPM1'], 'phandle': [[19]]}, 'sgpm2_default': {'function': ['SGPM2'], 'groups': ['SGPM2'], 'phandle': [[20]]}, 'sgps1_default': {'function': ['SGPS1'], 'groups': ['SGPS1']}, 'sgps2_default': {'function': ['SGPS2'], 'groups': ['SGPS2']}, 'sioonctrl_default': {'function': ['SIOONCTRL'], 'groups': ['SIOONCTRL']}, 'siopbi_default': {'function': ['SIOPBI'], 'groups': ['SIOPBI']}, 'siopbo_default': {'function': ['SIOPBO'], 'groups': ['SIOPBO']}, 'siopwreq_default': {'function': ['SIOPWREQ'], 'groups': ['SIOPWREQ']}, 'siopwrgd_default': {'function': ['SIOPWRGD'], 'groups': ['SIOPWRGD']}, 'sios3_default': {'function': ['SIOS3'], 'groups': ['SIOS3']}, 'sio
s5_default': {'function': ['SIOS5'], 'groups': ['SIOS5']}, 'siosci_default': {'function': ['SIOSCI'], 'groups': ['SIOSCI']}, 'spi1_default': {'function': ['SPI1'], 'groups': ['SPI1'], 'phandle': [[3]]}, 'spi1abr_default': {'function': ['SPI1ABR'], 'groups': ['SPI1ABR']}, 'spi1cs1_default': {'function': ['SPI1CS1'], 'groups': ['SPI1CS1']}, 'spi1wp_default': {'function': ['SPI1WP'], 'groups': ['SPI1WP']}, 'spi2_default': {'function': ['SPI2'], 'groups': ['SPI2']}, 'spi2cs1_default': {'function': ['SPI2CS1'], 'groups': ['SPI2CS1']}, 'spi2cs2_default': {'function': ['SPI2CS2'], 'groups': ['SPI2CS2']}, 'tach0_default': {'function': ['TACH0'], 'groups': ['TACH0']}, 'tach1_default': {'function': ['TACH1'], 'groups': ['TACH1']}, 'tach10_default': {'function': ['TACH10'], 'groups': ['TACH10']}, 'tach11_default': {'function': ['TACH11'], 'groups': ['TACH11']}, 'tach12_default': {'function': ['TACH12'], 'groups': ['TACH12']}, 'tach13_default': {'function': ['TACH13'], 'groups': ['TACH13']}, 't
ach14_default': {'function': ['TACH14'], 'groups': ['TACH14']}, 'tach15_default': {'function': ['TACH15'], 'groups': ['TACH15']}, 'tach2_default': {'function': ['TACH2'], 'groups': ['TACH2']}, 'tach3_default': {'function': ['TACH3'], 'groups': ['TACH3']}, 'tach4_default': {'function': ['TACH4'], 'groups': ['TACH4']}, 'tach5_default': {'function': ['TACH5'], 'groups': ['TACH5']}, 'tach6_default': {'function': ['TACH6'], 'groups': ['TACH6']}, 'tach7_default': {'function': ['TACH7'], 'groups': ['TACH7']}, 'tach8_default': {'function': ['TACH8'], 'groups': ['TACH8']}, 'tach9_default': {'function': ['TACH9'], 'groups': ['TACH9']}, 'thru0_default': {'function': ['THRU0'], 'groups': ['THRU0']}, 'thru1_default': {'function': ['THRU1'], 'groups': ['THRU1']}, 'thru2_default': {'function': ['THRU2'], 'groups': ['THRU2']}, 'thru3_default': {'function': ['THRU3'], 'groups': ['THRU3']}, 'txd1_default': {'function': ['TXD1'], 'groups': ['TXD1'], 'phandle': [[22]]}, 'txd2_default': {'function': ['T
XD2'], 'groups': ['TXD2'], 'phandle': [[28]]}, 'txd3_default': {'function': ['TXD3'], 'groups': ['TXD3'], 'phandle': [[30]]}, 'txd4_default': {'function': ['TXD4'], 'groups': ['TXD4'], 'phandle': [[32]]}, 'uart10_default': {'function': ['UART10'], 'groups': ['UART10']}, 'uart11_default': {'function': ['UART11'], 'groups': ['UART11']}, 'uart12g0_default': {'function': ['UART12'], 'groups': ['UART12G0']}, 'uart12g1_default': {'function': ['UART12'], 'groups': ['UART12G1']}, 'uart13g0_default': {'function': ['UART13'], 'groups': ['UART13G0']}, 'uart13g1_default': {'function': ['UART13'], 'groups': ['UART13G1']}, 'uart6_default': {'function': ['UART6'], 'groups': ['UART6'], 'phandle': [[34]]}, 'uart7_default': {'function': ['UART7'], 'groups': ['UART7'], 'phandle': [[35]]}, 'uart8_default': {'function': ['UART8'], 'groups': ['UART8'], 'phandle': [[36]]}, 'uart9_default': {'function': ['UART9'], 'groups': ['UART9'], 'phandle': [[37]]}, 'usb2ah_default': {'function': ['USB2AH'], 'groups':
['USBA'], 'phandle': [[9]]}, 'usb2ad_default': {'function': ['USB2AD'], 'groups': ['USBA'], 'phandle': [[11]]}, 'usb2bh_default': {'function': ['USB2BH'], 'groups': ['USBB'], 'phandle': [[10]]}, 'usb2bd_default': {'function': ['USB2BD'], 'groups': ['USBB'], 'phandle': [[12]]}, 'usb11bhid_default': {'function': ['USB11BHID'], 'groups': ['USBB']}, 'vb_default': {'function': ['VB'], 'groups': ['VB']}, 'vgahs_default': {'function': ['VGAHS'], 'groups': ['VGAHS']}, 'vgavs_default': {'function': ['VGAVS'], 'groups': ['VGAVS']}, 'wdtrst1_default': {'function': ['WDTRST1'], 'groups': ['WDTRST1'], 'phandle': [[24]]}, 'wdtrst2_default': {'function': ['WDTRST2'], 'groups': ['WDTRST2']}, 'wdtrst3_default': {'function': ['WDTRST3'], 'groups': ['WDTRST3']}, 'wdtrst4_default': {'function': ['WDTRST4'], 'groups': ['WDTRST4']}}, 'silicon-id@14': {'compatible': ['aspeed,ast2600-silicon-id', 'aspeed,silicon-id'], 'reg': [[20, 4], [1456, 8]]}, 'smp-memram@180': {'compatible': ['aspeed,ast2600-smpmem']
, 'reg': [[384, 64]]}, 'interrupt-controller@560': {'#interrupt-cells': [[1]], 'compatible': ['aspeed,ast2600-scu-ic0'], 'reg': [[1376, 4]], 'interrupts': [[0, 12, 4]], 'interrupt-controller': True, 'phandle': [[15]]}, 'interrupt-controller@570': {'#interrupt-cells': [[1]], 'compatible': ['aspeed,ast2600-scu-ic1'], 'reg': [[1392, 4]], 'interrupts': [[0, 41, 4]], 'interrupt-controller': True}}, 'hwrng@1e6e2524': {'compatible': ['timeriomem_rng'], 'reg': [[510534948, 4]], 'period': [[1]], 'quality': [[100]]}, 'display@1e6e6000': {'compatible': ['aspeed,ast2600-gfx', 'syscon'], 'reg': [[510550016, 4096]], 'reg-io-width': [[4]], 'clocks': [[2, 8]], 'resets': [[2, 26]], 'syscon': [[2]], 'status': ['disabled'], 'interrupts': [[0, 14, 4]]}, 'xdma@1e6e7000': {'compatible': ['aspeed,ast2600-xdma'], 'reg': [[510554112, 256]], 'clocks': [[2, 4]], 'resets': [[2, 25], [2, 27]], 'reset-names': ['device', 'root-complex'], 'interrupts-extended': [[1, 0, 6, 4], [15, 2]], 'aspeed,pcie-device': ['bmc'
], 'aspeed,scu': [[2]], 'status': ['okay'], 'memory-region': [[16]]}, 'adc@1e6e9000': {'compatible': ['aspeed,ast2600-adc0'], 'reg': [[510562304, 256]], 'clocks': [[2, 53]], 'resets': [[2, 55]], 'interrupts': [[0, 46, 4]], '#io-channel-cells': [[1]], 'status': ['disabled']}, 'adc@1e6e9100': {'compatible': ['aspeed,ast2600-adc1'], 'reg': [[510562560, 256]], 'clocks': [[2, 53]], 'resets': [[2, 55]], 'interrupts': [[0, 46, 4]], '#io-channel-cells': [[1]], 'status': ['disabled']}, 'secure-boot-controller@1e6f2000': {'compatible': ['aspeed,ast2600-sbc'], 'reg': [[510599168, 4096]]}, 'crypto@1e6fa000': {'compatible': ['aspeed,ast2600-acry'], 'reg': [[510631936, 1024], [510722048, 6144]], 'interrupts': [[0, 160, 4]], 'clocks': [[2, 16]], 'aspeed,ahbc': [[17]]}, 'video@1e700000': {'compatible': ['aspeed,ast2600-video-engine'], 'reg': [[510656512, 4096]], 'clocks': [[2, 3], [2, 0]], 'clock-names': ['vclk', 'eclk'], 'interrupts': [[0, 7, 4]], 'status': ['disabled']}, 'gpio@1e780000': {'#gpio-
cells': [[2]], 'gpio-controller': True, 'compatible': ['aspeed,ast2600-gpio'], 'reg': [[511180800, 1024]], 'interrupts': [[0, 40, 4]], 'gpio-ranges': [[18, 0, 0, 208]], 'ngpios': [[208]], 'clocks': [[2, 53]], 'interrupt-controller': True, '#interrupt-cells': [[2]], 'gpio-line-names': ['', '', '', '', '', '', '', '', 'fsi-mux', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', 'power-button', '', '', 'checkstop', '', 'presence-ps1', '', 'led-rear-fault', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', 'presence-ps0', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', 'led-rear-power', 'led-rear-id', '', 'usb-power', '', '', '', '', '', '', '', '', '', 'bmc-tpm-reset', '', '', 'cfam-reset', '', '', '', '', '', '', 'fsi-routing', '', '', '', '', '', '', '', '', '
', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', ''], 'phandle': [[55]]}, 'sgpiom@1e780500': {'#gpio-cells': [[2]], 'gpio-controller': True, 'compatible': ['aspeed,ast2600-sgpiom'], 'reg': [[511182080, 256]], 'interrupts': [[0, 51, 4]], 'clocks': [[2, 53]], '#interrupt-cells': [[2]], 'interrupt-controller': True, 'bus-frequency': [[12000000]], 'pinctrl-names': ['default'], 'pinctrl-0': [[19]], 'status': ['disabled']}, 'sgpiom@1e780600': {'#gpio-cells': [[2]], 'gpio-controller': True, 'compatible': ['aspeed,ast2600-sgpiom'], 'reg': [[511182336, 256]], 'interrupts': [[0, 70, 4]], 'clocks': [[2, 53]], '#interrupt-cells': [[2]], 'interrupt-controller': True, 'bus-frequency': [[12000000]], 'pinctrl-names': ['default'], 'pinctrl-0': [[20]], 'status': ['disabled']}, 'gpio@1e780800': {'#gpio-
cells': [[2]], 'gpio-controller': True, 'compatible': ['aspeed,ast2600-gpio'], 'reg': [[511182848, 2048]], 'interrupts': [[0, 11, 4]], 'gpio-ranges': [[18, 0, 208, 36]], 'ngpios': [[36]], 'clocks': [[2, 52]], 'interrupt-controller': True, '#interrupt-cells': [[2]]}, 'rtc@1e781000': {'compatible': ['aspeed,ast2600-rtc'], 'reg': [[511184896, 24]], 'interrupts': [[0, 13, 4]], 'status': ['disabled']}, 'timer@1e782000': {'compatible': ['aspeed,ast2600-timer'], 'reg': [[511188992, 144]], 'interrupts-extended': [[1, 0, 16, 4], [1, 0, 17, 4], [1, 0, 18, 4], [1, 0, 19, 4], [1, 0, 20, 4], [1, 0, 21, 4], [1, 0, 22, 4], [1, 0, 23, 4]], 'clocks': [[2, 52]], 'clock-names': ['PCLK'], 'status': ['disabled']}, 'serial@1e783000': {'compatible': ['snps,dw-apb-uart'], 'reg': [[511193088, 32]], 'reg-shift': [[2]], 'reg-io-width': [[4]], 'interrupts': [[0, 47, 4]], 'clocks': [[2, 22]], 'resets': [[21, 4]], 'no-loopback-test': True, 'pinctrl-names': ['default'], 'pinctrl-0': [[22, 23]], 'status': ['okay']
}, 'serial@1e784000': {'compatible': ['snps,dw-apb-uart'], 'reg': [[511197184, 4096]], 'reg-shift': [[2]], 'interrupts': [[0, 8, 4]], 'clocks': [[2, 26]], 'no-loopback-test': True}, 'watchdog@1e785000': {'compatible': ['aspeed,ast2600-wdt'], 'reg': [[511201280, 64]], 'aspeed,reset-type': ['none'], 'aspeed,external-signal': True, 'aspeed,ext-push-pull': True, 'aspeed,ext-active-high': True, 'pinctrl-names': ['default'], 'pinctrl-0': [[24]]}, 'watchdog@1e785040': {'compatible': ['aspeed,ast2600-wdt'], 'reg': [[511201344, 64]], 'status': ['okay']}, 'watchdog@1e785080': {'compatible': ['aspeed,ast2600-wdt'], 'reg': [[511201408, 64]], 'status': ['disabled']}, 'watchdog@1e7850c0': {'compatible': ['aspeed,ast2600-wdt'], 'reg': [[511201472, 64]], 'status': ['disabled']}, 'peci-controller@1e78b000': {'compatible': ['aspeed,ast2600-peci'], 'reg': [[511225856, 256]], 'interrupts': [[0, 38, 4]], 'clocks': [[2, 10]], 'resets': [[2, 36]], 'cmd-timeout-ms': [[1000]], 'clock-frequency': [[1000000]]
, 'status': ['disabled']}, 'lpc@1e789000': {'compatible': ['aspeed,ast2600-lpc-v2', 'simple-mfd', 'syscon'], 'reg': [[511217664, 4096]], 'reg-io-width': [[4]], '#address-cells': [[1]], '#size-cells': [[1]], 'ranges': [[0, 511217664, 4096]], 'kcs@24': {'compatible': ['aspeed,ast2500-kcs-bmc-v2'], 'reg': [[36, 1], [48, 1], [60, 1]], 'interrupts': [[0, 138, 4]], 'clocks': [[2, 6]], 'kcs_chan': [[1]], 'status': ['disabled']}, 'kcs@28': {'compatible': ['aspeed,ast2500-kcs-bmc-v2'], 'reg': [[40, 1], [52, 1], [64, 1]], 'interrupts': [[0, 139, 4]], 'clocks': [[2, 6]], 'status': ['okay'], 'aspeed,lpc-io-reg': [[3240, 3244]]}, 'kcs@2c': {'compatible': ['aspeed,ast2500-kcs-bmc-v2'], 'reg': [[44, 1], [56, 1], [68, 1]], 'interrupts': [[0, 140, 4]], 'clocks': [[2, 6]], 'status': ['okay'], 'aspeed,lpc-io-reg': [[3234]], 'aspeed,lpc-interrupts': [[11, 8]]}, 'kcs@114': {'compatible': ['aspeed,ast2500-kcs-bmc-v2'], 'reg': [[276, 1], [280, 1], [284, 1]], 'interrupts': [[0, 141, 4]], 'clocks': [[2, 6]]
, 'status': ['disabled']}, 'lpc-ctrl@80': {'compatible': ['aspeed,ast2600-lpc-ctrl'], 'reg': [[128, 128]], 'clocks': [[2, 6]], 'status': ['okay'], 'memory-region': [[25]], 'flash': [[26]]}, 'lpc-snoop@80': {'compatible': ['aspeed,ast2600-lpc-snoop'], 'reg': [[128, 128]], 'interrupts': [[0, 144, 4]], 'clocks': [[2, 6]], 'status': ['disabled']}, 'lhc@a0': {'compatible': ['aspeed,ast2600-lhc'], 'reg': [[160, 36], [200, 8]]}, 'reset-controller@98': {'compatible': ['aspeed,ast2600-lpc-reset'], 'reg': [[152, 4]], '#reset-cells': [[1]], 'phandle': [[21]]}, 'uart-routing@98': {'compatible': ['aspeed,ast2600-uart-routing'], 'reg': [[152, 8]], 'status': ['disabled']}, 'ibt@140': {'compatible': ['aspeed,ast2600-ibt-bmc'], 'reg': [[320, 24]], 'interrupts': [[0, 143, 4]], 'clocks': [[2, 6]], 'status': ['okay']}}, 'sdc@1e740000': {'compatible': ['aspeed,ast2600-sd-controller'], 'reg': [[510918656, 256]], '#address-cells': [[1]], '#size-cells': [[1]], 'ranges': [[0, 510918656, 65536]], 'clocks': [
[2, 35]], 'status': ['disabled'], 'sdhci@1e740100': {'compatible': ['aspeed,ast2600-sdhci', 'sdhci'], 'reg': [[256, 256]], 'interrupts': [[0, 43, 4]], 'sdhci,auto-cmd12': True, 'clocks': [[2, 60]], 'status': ['disabled']}, 'sdhci@1e740200': {'compatible': ['aspeed,ast2600-sdhci', 'sdhci'], 'reg': [[512, 256]], 'interrupts': [[0, 43, 4]], 'sdhci,auto-cmd12': True, 'clocks': [[2, 60]], 'status': ['disabled']}}, 'sdc@1e750000': {'compatible': ['aspeed,ast2600-sd-controller'], 'reg': [[510984192, 256]], '#address-cells': [[1]], '#size-cells': [[1]], 'ranges': [[0, 510984192, 65536]], 'clocks': [[2, 36]], 'status': ['okay'], 'sdhci@1e750100': {'compatible': ['aspeed,ast2600-sdhci'], 'reg': [[256, 256]], 'sdhci,auto-cmd12': True, 'interrupts': [[0, 15, 4]], 'clocks': [[2, 61]], 'pinctrl-names': ['default'], 'pinctrl-0': [[27]], 'status': ['okay'], 'clk-phase-mmc-hs200': [[36, 270]]}}, 'serial@1e787000': {'compatible': ['aspeed,ast2500-vuart'], 'reg': [[511209472, 64]], 'reg-shift': [[2]],
'interrupts': [[0, 147, 4]], 'clocks': [[2, 52]], 'no-loopback-test': True, 'status': ['okay']}, 'serial@1e787800': {'compatible': ['aspeed,ast2500-vuart'], 'reg': [[511211520, 64]], 'reg-shift': [[2]], 'interrupts': [[0, 180, 4]], 'clocks': [[2, 53]], 'no-loopback-test': True, 'status': ['disabled']}, 'serial@1e788000': {'compatible': ['aspeed,ast2500-vuart'], 'reg': [[511213568, 64]], 'reg-shift': [[2]], 'interrupts': [[0, 148, 4]], 'clocks': [[2, 52]], 'no-loopback-test': True, 'status': ['okay']}, 'serial@1e788800': {'compatible': ['aspeed,ast2500-vuart'], 'reg': [[511215616, 64]], 'reg-shift': [[2]], 'interrupts': [[0, 181, 4]], 'clocks': [[2, 53]], 'no-loopback-test': True, 'status': ['disabled']}, 'serial@1e78d000': {'compatible': ['ns16550a'], 'reg': [[511234048, 32]], 'reg-shift': [[2]], 'reg-io-width': [[4]], 'interrupts': [[0, 48, 4]], 'clocks': [[2, 23]], 'resets': [[21, 5]], 'no-loopback-test': True, 'pinctrl-names': ['default'], 'pinctrl-0': [[28, 29]], 'status': ['di
sabled']}, 'serial@1e78e000': {'compatible': ['ns16550a'], 'reg': [[511238144, 32]], 'reg-shift': [[2]], 'reg-io-width': [[4]], 'interrupts': [[0, 49, 4]], 'clocks': [[2, 24]], 'resets': [[21, 6]], 'no-loopback-test': True, 'pinctrl-names': ['default'], 'pinctrl-0': [[30, 31]], 'status': ['disabled']}, 'serial@1e78f000': {'compatible': ['ns16550a'], 'reg': [[511242240, 32]], 'reg-shift': [[2]], 'reg-io-width': [[4]], 'interrupts': [[0, 50, 4]], 'clocks': [[2, 25]], 'resets': [[21, 7]], 'no-loopback-test': True, 'pinctrl-names': ['default'], 'pinctrl-0': [[32, 33]], 'status': ['disabled']}, 'serial@1e790000': {'compatible': ['ns16550a'], 'reg': [[511246336, 32]], 'reg-shift': [[2]], 'reg-io-width': [[4]], 'interrupts': [[0, 57, 4]], 'clocks': [[2, 27]], 'no-loopback-test': True, 'pinctrl-names': ['default'], 'pinctrl-0': [[34]], 'status': ['disabled']}, 'serial@1e790100': {'compatible': ['ns16550a'], 'reg': [[511246592, 32]], 'reg-shift': [[2]], 'reg-io-width': [[4]], 'interrupts': [
[0, 58, 4]], 'clocks': [[2, 28]], 'no-loopback-test': True, 'pinctrl-names': ['default'], 'pinctrl-0': [[35]], 'status': ['disabled']}, 'serial@1e790200': {'compatible': ['ns16550a'], 'reg': [[511246848, 32]], 'reg-shift': [[2]], 'reg-io-width': [[4]], 'interrupts': [[0, 59, 4]], 'clocks': [[2, 29]], 'no-loopback-test': True, 'pinctrl-names': ['default'], 'pinctrl-0': [[36]], 'status': ['disabled']}, 'serial@1e790300': {'compatible': ['ns16550a'], 'reg': [[511247104, 32]], 'reg-shift': [[2]], 'reg-io-width': [[4]], 'interrupts': [[0, 60, 4]], 'clocks': [[2, 30]], 'no-loopback-test': True, 'pinctrl-names': ['default'], 'pinctrl-0': [[37]], 'status': ['disabled']}, 'bus@1e78a000': {'compatible': ['simple-bus'], '#address-cells': [[1]], '#size-cells': [[1]], 'ranges': [[0, 511221760, 4096]], 'i2c-bus@80': {'#address-cells': [[1]], '#size-cells': [[0]], 'reg': [[128, 128]], 'compatible': ['aspeed,ast2600-i2c-bus'], 'clocks': [[2, 53]], 'resets': [[2, 34]], 'interrupts': [[0, 110, 4]], '
bus-frequency': [[100000]], 'pinctrl-names': ['default'], 'pinctrl-0': [[38]], 'status': ['okay'], 'multi-master': True, 'ibm-panel@62': {'compatible': ['ibm,op-panel'], 'reg': [[1073741922]]}}, 'i2c-bus@100': {'#address-cells': [[1]], '#size-cells': [[0]], 'reg': [[256, 128]], 'compatible': ['aspeed,ast2600-i2c-bus'], 'clocks': [[2, 53]], 'resets': [[2, 34]], 'interrupts': [[0, 111, 4]], 'bus-frequency': [[100000]], 'pinctrl-names': ['default'], 'pinctrl-0': [[39]], 'status': ['okay'], 'tpm@2e': {'compatible': ['nuvoton,npct75x', 'tcg,tpm-tis-i2c'], 'reg': [[46]]}}, 'i2c-bus@180': {'#address-cells': [[1]], '#size-cells': [[0]], 'reg': [[384, 128]], 'compatible': ['aspeed,ast2600-i2c-bus'], 'clocks': [[2, 53]], 'resets': [[2, 34]], 'interrupts': [[0, 112, 4]], 'bus-frequency': [[100000]], 'pinctrl-names': ['default'], 'pinctrl-0': [[40]], 'status': ['okay']}, 'i2c-bus@200': {'#address-cells': [[1]], '#size-cells': [[0]], 'reg': [[512, 128]], 'compatible': ['aspeed,ast2600-i2c-bus'],
'clocks': [[2, 53]], 'resets': [[2, 34]], 'interrupts': [[0, 113, 4]], 'bus-frequency': [[100000]], 'pinctrl-names': ['default'], 'pinctrl-0': [[41]], 'status': ['okay'], 'bmp280@77': {'compatible': ['bosch,bmp280'], 'reg': [[119]], '#io-channel-cells': [[1]], 'phandle': [[59]]}, 'max31785@52': {'compatible': ['maxim,max31785a'], 'reg': [[82]], '#address-cells': [[1]], '#size-cells': [[0]], 'fan@0': {'compatible': ['pmbus-fan'], 'reg': [[0]], 'tach-pulses': [[2]], 'maxim,fan-rotor-input': ['tach'], 'maxim,fan-pwm-freq': [[25000]], 'maxim,fan-dual-tach': True, 'maxim,fan-no-watchdog': True, 'maxim,fan-no-fault-ramp': True, 'maxim,fan-ramp': [[2]], 'maxim,fan-fault-pin-mon': True}, 'fan@1': {'compatible': ['pmbus-fan'], 'reg': [[1]], 'tach-pulses': [[2]], 'maxim,fan-rotor-input': ['tach'], 'maxim,fan-pwm-freq': [[25000]], 'maxim,fan-dual-tach': True, 'maxim,fan-no-watchdog': True, 'maxim,fan-no-fault-ramp': True, 'maxim,fan-ramp': [[2]], 'maxim,fan-fault-pin-mon': True}, 'fan@2': {'c
ompatible': ['pmbus-fan'], 'reg': [[2]], 'tach-pulses': [[2]], 'maxim,fan-rotor-input': ['tach'], 'maxim,fan-pwm-freq': [[25000]], 'maxim,fan-dual-tach': True, 'maxim,fan-no-watchdog': True, 'maxim,fan-no-fault-ramp': True, 'maxim,fan-ramp': [[2]], 'maxim,fan-fault-pin-mon': True}, 'fan@3': {'compatible': ['pmbus-fan'], 'reg': [[3]], 'tach-pulses': [[2]], 'maxim,fan-rotor-input': ['tach'], 'maxim,fan-pwm-freq': [[25000]], 'maxim,fan-dual-tach': True, 'maxim,fan-no-watchdog': True, 'maxim,fan-no-fault-ramp': True, 'maxim,fan-ramp': [[2]], 'maxim,fan-fault-pin-mon': True}}, 'dps310@76': {'compatible': ['infineon,dps310'], 'reg': [[118]], '#io-channel-cells': [[0]], 'phandle': [[58]]}, 'pca9552@60': {'compatible': ['nxp,pca9552'], 'reg': [[96]], '#address-cells': [[1]], '#size-cells': [[0]], 'gpio-controller': True, '#gpio-cells': [[2]], 'phandle': [[57]], 'gpio@0': {'reg': [[0]], 'type': [[2]]}, 'gpio@1': {'reg': [[1]], 'type': [[2]]}, 'gpio@2': {'reg': [[2]], 'type': [[2]]}, 'gpio@3'
: {'reg': [[3]], 'type': [[2]]}, 'gpio@4': {'reg': [[4]], 'type': [[2]]}, 'gpio@5': {'reg': [[5]], 'type': [[2]]}, 'gpio@6': {'reg': [[6]], 'type': [[2]]}, 'gpio@7': {'reg': [[7]], 'type': [[2]]}, 'gpio@8': {'reg': [[8]], 'type': [[2]]}, 'gpio@9': {'reg': [[9]], 'type': [[2]]}, 'gpio@10': {'reg': [[10]], 'type': [[2]]}, 'gpio@11': {'reg': [[11]], 'type': [[2]]}, 'gpio@12': {'reg': [[12]], 'type': [[2]]}, 'gpio@13': {'reg': [[13]], 'type': [[2]]}, 'gpio@14': {'reg': [[14]], 'type': [[2]]}, 'gpio@15': {'reg': [[15]], 'type': [[2]]}}, 'power-supply@68': {'compatible': ['ibm,cffps1'], 'reg': [[104]]}, 'power-supply@69': {'compatible': ['ibm,cffps1'], 'reg': [[105]]}}, 'i2c-bus@280': {'#address-cells': [[1]], '#size-cells': [[0]], 'reg': [[640, 128]], 'compatible': ['aspeed,ast2600-i2c-bus'], 'clocks': [[2, 53]], 'resets': [[2, 34]], 'interrupts': [[0, 114, 4]], 'bus-frequency': [[100000]], 'pinctrl-names': ['default'], 'pinctrl-0': [[42]], 'status': ['okay'], 'tmp423a@4c': {'compatible'
: ['ti,tmp423'], 'reg': [[76]]}, 'ir35221@70': {'compatible': ['infineon,ir35221'], 'reg': [[112]]}, 'ir35221@71': {'compatible': ['infineon,ir35221'], 'reg': [[113]]}}, 'i2c-bus@300': {'#address-cells': [[1]], '#size-cells': [[0]], 'reg': [[768, 128]], 'compatible': ['aspeed,ast2600-i2c-bus'], 'clocks': [[2, 53]], 'resets': [[2, 34]], 'interrupts': [[0, 115, 4]], 'bus-frequency': [[100000]], 'pinctrl-names': ['default'], 'pinctrl-0': [[43]], 'status': ['okay'], 'tmp423a@4c': {'compatible': ['ti,tmp423'], 'reg': [[76]]}, 'ir35221@70': {'compatible': ['infineon,ir35221'], 'reg': [[112]]}, 'ir35221@71': {'compatible': ['infineon,ir35221'], 'reg': [[113]]}}, 'i2c-bus@380': {'#address-cells': [[1]], '#size-cells': [[0]], 'reg': [[896, 128]], 'compatible': ['aspeed,ast2600-i2c-bus'], 'clocks': [[2, 53]], 'resets': [[2, 34]], 'interrupts': [[0, 116, 4]], 'bus-frequency': [[100000]], 'pinctrl-names': ['default'], 'pinctrl-0': [[44]], 'status': ['disabled']}, 'i2c-bus@400': {'#address-cells
': [[1]], '#size-cells': [[0]], 'reg': [[1024, 128]], 'compatible': ['aspeed,ast2600-i2c-bus'], 'clocks': [[2, 53]], 'resets': [[2, 34]], 'interrupts': [[0, 117, 4]], 'bus-frequency': [[100000]], 'pinctrl-names': ['default'], 'pinctrl-0': [[45]], 'status': ['okay']}, 'i2c-bus@480': {'#address-cells': [[1]], '#size-cells': [[0]], 'reg': [[1152, 128]], 'compatible': ['aspeed,ast2600-i2c-bus'], 'clocks': [[2, 53]], 'resets': [[2, 34]], 'interrupts': [[0, 118, 4]], 'bus-frequency': [[100000]], 'pinctrl-names': ['default'], 'pinctrl-0': [[46]], 'status': ['disabled']}, 'i2c-bus@500': {'#address-cells': [[1]], '#size-cells': [[0]], 'reg': [[1280, 128]], 'compatible': ['aspeed,ast2600-i2c-bus'], 'clocks': [[2, 53]], 'resets': [[2, 34]], 'interrupts': [[0, 119, 4]], 'bus-frequency': [[100000]], 'pinctrl-names': ['default'], 'pinctrl-0': [[47]], 'status': ['okay'], 'tmp275@4a': {'compatible': ['ti,tmp275'], 'reg': [[74]]}}, 'i2c-bus@580': {'#address-cells': [[1]], '#size-cells': [[0]], 'reg'
: [[1408, 128]], 'compatible': ['aspeed,ast2600-i2c-bus'], 'clocks': [[2, 53]], 'resets': [[2, 34]], 'interrupts': [[0, 120, 4]], 'bus-frequency': [[100000]], 'pinctrl-names': ['default'], 'pinctrl-0': [[48]], 'status': ['okay']}, 'i2c-bus@600': {'#address-cells': [[1]], '#size-cells': [[0]], 'reg': [[1536, 128]], 'compatible': ['aspeed,ast2600-i2c-bus'], 'clocks': [[2, 53]], 'resets': [[2, 34]], 'interrupts': [[0, 121, 4]], 'bus-frequency': [[100000]], 'pinctrl-names': ['default'], 'pinctrl-0': [[49]], 'status': ['okay'], 'pca9552@60': {'compatible': ['nxp,pca9552'], 'reg': [[96]], '#address-cells': [[1]], '#size-cells': [[0]], 'gpio-controller': True, '#gpio-cells': [[2]], 'gpio-line-names': ['PS_SMBUS_RESET_N', 'APSS_RESET_N', 'GPU0_TH_OVERT_N_BUFF', 'GPU1_TH_OVERT_N_BUFF', 'GPU2_TH_OVERT_N_BUFF', 'GPU3_TH_OVERT_N_BUFF', 'GPU4_TH_OVERT_N_BUFF', 'GPU5_TH_OVERT_N_BUFF', 'GPU0_PWR_GOOD_BUFF', 'GPU1_PWR_GOOD_BUFF', 'GPU2_PWR_GOOD_BUFF', 'GPU3_PWR_GOOD_BUFF', 'GPU4_PWR_GOOD_BUFF', 'GP
U5_PWR_GOOD_BUFF', '12V_BREAKER_FLT_N', 'THROTTLE_UNLATCHED_N'], 'gpio@0': {'reg': [[0]], 'type': [[2]]}, 'gpio@1': {'reg': [[1]], 'type': [[2]]}, 'gpio@2': {'reg': [[2]], 'type': [[2]]}, 'gpio@3': {'reg': [[3]], 'type': [[2]]}, 'gpio@4': {'reg': [[4]], 'type': [[2]]}, 'gpio@5': {'reg': [[5]], 'type': [[2]]}, 'gpio@6': {'reg': [[6]], 'type': [[2]]}, 'gpio@7': {'reg': [[7]], 'type': [[2]]}, 'gpio@8': {'reg': [[8]], 'type': [[2]]}, 'gpio@9': {'reg': [[9]], 'type': [[2]]}, 'gpio@10': {'reg': [[10]], 'type': [[2]]}, 'gpio@11': {'reg': [[11]], 'type': [[2]]}, 'gpio@12': {'reg': [[12]], 'type': [[2]]}, 'gpio@13': {'reg': [[13]], 'type': [[2]]}, 'gpio@14': {'reg': [[14]], 'type': [[2]]}, 'gpio@15': {'reg': [[15]], 'type': [[2]]}}, 'rtc@32': {'compatible': ['epson,rx8900'], 'reg': [[50]]}, 'eeprom@51': {'compatible': ['atmel,24c64'], 'reg': [[81]]}, 'ucd90160@64': {'compatible': ['ti,ucd90160'], 'reg': [[100]]}}, 'i2c-bus@680': {'#address-cells': [[1]], '#size-cells': [[0]], 'reg': [[1664,
128]], 'compatible': ['aspeed,ast2600-i2c-bus'], 'clocks': [[2, 53]], 'resets': [[2, 34]], 'interrupts': [[0, 122, 4]], 'bus-frequency': [[100000]], 'pinctrl-names': ['default'], 'pinctrl-0': [[50]], 'status': ['okay']}, 'i2c-bus@700': {'#address-cells': [[1]], '#size-cells': [[0]], 'reg': [[1792, 128]], 'compatible': ['aspeed,ast2600-i2c-bus'], 'clocks': [[2, 53]], 'resets': [[2, 34]], 'interrupts': [[0, 123, 4]], 'bus-frequency': [[100000]], 'pinctrl-names': ['default'], 'pinctrl-0': [[51]], 'status': ['okay']}, 'i2c-bus@780': {'#address-cells': [[1]], '#size-cells': [[0]], 'reg': [[1920, 128]], 'compatible': ['aspeed,ast2600-i2c-bus'], 'clocks': [[2, 53]], 'resets': [[2, 34]], 'interrupts': [[0, 124, 4]], 'bus-frequency': [[100000]], 'pinctrl-names': ['default'], 'pinctrl-0': [[52]], 'status': ['disabled']}, 'i2c-bus@800': {'#address-cells': [[1]], '#size-cells': [[0]], 'reg': [[2048, 128]], 'compatible': ['aspeed,ast2600-i2c-bus'], 'clocks': [[2, 53]], 'resets': [[2, 34]], 'inte
rrupts': [[0, 125, 4]], 'bus-frequency': [[100000]], 'pinctrl-names': ['default'], 'pinctrl-0': [[53]], 'status': ['disabled']}}, 'fsi@1e79b000': {'#interrupt-cells': [[1]], 'compatible': ['aspeed,ast2600-fsi-master', 'fsi-master'], 'reg': [[511291392, 148]], 'interrupts': [[0, 100, 4]], 'pinctrl-names': ['default'], 'pinctrl-0': [[54]], 'clocks': [[2, 45]], 'interrupt-controller': True, 'status': ['okay'], '#address-cells': [[2]], '#size-cells': [[0]], 'clock-frequency': [[100000000]], 'fsi-routing-gpios': [[55, 135, 0]], 'fsi-mux-gpios': [[55, 8, 0]], 'cfam@0,0': {'reg': [[0, 0]], '#address-cells': [[1]], '#size-cells': [[1]], 'chip-id': [[0]], 'scom@1000': {'compatible': ['ibm,fsi2pib'], 'reg': [[4096, 1024]]}, 'i2c@1800': {'compatible': ['ibm,fsi-i2c-master'], 'reg': [[6144, 1024]], '#address-cells': [[1]], '#size-cells': [[0]], 'i2c-bus@0': {'reg': [[0]]}, 'i2c-bus@1': {'reg': [[1]]}, 'i2c-bus@2': {'reg': [[2]]}, 'i2c-bus@3': {'reg': [[3]]}, 'i2c-bus@4': {'reg': [[4]]}, 'i2c-bu
s@5': {'reg': [[5]]}, 'i2c-bus@6': {'reg': [[6]]}, 'i2c-bus@7': {'reg': [[7]]}, 'i2c-bus@8': {'reg': [[8]]}, 'i2c-bus@9': {'reg': [[9]]}, 'i2c-bus@a': {'reg': [[10]]}, 'i2c-bus@b': {'reg': [[11]]}, 'i2c-bus@c': {'reg': [[12]]}, 'i2c-bus@d': {'reg': [[13]]}, 'i2c-bus@e': {'reg': [[14]]}}, 'sbefifo@2400': {'compatible': ['ibm,p9-sbefifo'], 'reg': [[9216, 1024]], '#address-cells': [[1]], '#size-cells': [[0]], 'occ': {'compatible': ['ibm,p9-occ'], 'reg': [[1]]}}, 'hub@3400': {'compatible': ['fsi-master-hub'], 'reg': [[13312, 1024]], '#address-cells': [[2]], '#size-cells': [[0]], 'no-scan-on-init': True, 'cfam@1,0': {'reg': [[1, 0]], '#address-cells': [[1]], '#size-cells': [[1]], 'chip-id': [[1]], 'scom@1000': {'compatible': ['ibm,fsi2pib'], 'reg': [[4096, 1024]]}, 'i2c@1800': {'compatible': ['ibm,fsi-i2c-master'], 'reg': [[6144, 1024]], '#address-cells': [[1]], '#size-cells': [[0]], 'i2c-bus@0': {'reg': [[0]]}, 'i2c-bus@1': {'reg': [[1]]}, 'i2c-bus@2': {'reg': [[2]]}, 'i2c-bus@3': {'reg
': [[3]]}, 'i2c-bus@4': {'reg': [[4]]}, 'i2c-bus@5': {'reg': [[5]]}, 'i2c-bus@6': {'reg': [[6]]}, 'i2c-bus@7': {'reg': [[7]]}, 'i2c-bus@8': {'reg': [[8]]}, 'i2c-bus@9': {'reg': [[9]]}, 'i2c-bus@a': {'reg': [[10]]}, 'i2c-bus@b': {'reg': [[11]]}, 'i2c-bus@c': {'reg': [[12]]}, 'i2c-bus@d': {'reg': [[13]]}, 'i2c-bus@e': {'reg': [[14]]}}, 'sbefifo@2400': {'compatible': ['ibm,p9-sbefifo'], 'reg': [[9216, 1024]], '#address-cells': [[1]], '#size-cells': [[0]], 'occ': {'compatible': ['ibm,p9-occ'], 'reg': [[2]]}}, 'hub@3400': {'compatible': ['fsi-master-hub'], 'reg': [[13312, 1024]], '#address-cells': [[2]], '#size-cells': [[0]], 'no-scan-on-init': True}}}}}, 'fsi@1e79b100': {'#interrupt-cells': [[1]], 'compatible': ['aspeed,ast2600-fsi-master', 'fsi-master'], 'reg': [[511291648, 148]], 'interrupts': [[0, 101, 4]], 'pinctrl-names': ['default'], 'pinctrl-0': [[56]], 'clocks': [[2, 45]], 'interrupt-controller': True, 'status': ['disabled']}, 'dma-controller@1e79e000': {'compatible': ['aspeed,a
st2600-udma'], 'reg': [[511303680, 4096]], 'interrupts': [[0, 56, 4]], 'dma-channels': [[28]], '#dma-cells': [[1]], 'status': ['disabled']}} should not be valid under {'type': 'object'}
from schema $id: http://devicetree.org/schemas/simple-bus.yaml#
On Mon, Feb 26, 2024 at 10:52:58AM -0600, Eddie James wrote:
> Document the 2700 FSI master compatible string.
>
> Signed-off-by: Eddie James <[email protected]>
> ---
> Documentation/devicetree/bindings/fsi/fsi-master-aspeed.txt | 2 +-
> 1 file changed, 1 insertion(+), 1 deletion(-)
Would be nice to see some ASpeed stuff converted to schemas...
Rob
Hi Eddie,
> @@ -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;
I don't see the need for initialization.
> +
> + if (!device_property_read_u32(dev, "clock-frequency", &clock)) {
> + if (!clock)
> + clock = I2C_DEFAULT_CLK_RATE;
> + }
no need for brackets.
> +
> + // i2c clock rate = local bus clock / (4 * (i2c clock div + 1))
You forgot to remove this.
Andi
> + i2c->clock_div = (((lbus + (clock - 1)) / clock) / 4) - 1;
> + }
>
> rc = fsi_i2c_dev_init(i2c);
> if (rc)
> --
> 2.39.3
>
On Mon, Feb 26, 2024 at 10:53:16AM -0600, Eddie James wrote:
> No functional change.
Lazy log, lazy log, a coder’s brief epilogue.
Please explain.
I'm not going any further at reviewing this series. Please, make
sure in v3 to write proper logs so that reviewers can understand
what you are doing before reading the code. Cleanup, run
checkpatch, etc. etc.
Thanks,
Andi
On 4/15/24 17:11, Andi Shyti wrote:
> Hi Eddie,
>
>> @@ -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;
> I don't see the need for initialization.
Does device_property_read_u32 set clock if the property isn't found? If
not, it needs to be initialized here. Or I can set it in an else
statement from device_property_read_u32.
>
>> +
>> + if (!device_property_read_u32(dev, "clock-frequency", &clock)) {
>> + if (!clock)
>> + clock = I2C_DEFAULT_CLK_RATE;
>> + }
> no need for brackets.
Perhaps not, but checkpatch didn't complain, and I personally like
brackets if multiple lines are included.
>
>> +
>> + // i2c clock rate = local bus clock / (4 * (i2c clock div + 1))
> You forgot to remove this.
I actually meant to leave that comment to explain how the clock rate is
calculated, as the reverse calculation in the code is a little more
confusing.
>
> Andi
>
>> + i2c->clock_div = (((lbus + (clock - 1)) / clock) / 4) - 1;
>> + }
>>
>> rc = fsi_i2c_dev_init(i2c);
>> if (rc)
>> --
>> 2.39.3
>>
On Tue, Apr 16, 2024 at 01:09:04PM -0500, Eddie James wrote:
>
> On 4/15/24 17:11, Andi Shyti wrote:
> > Hi Eddie,
> >
> > > @@ -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;
> > I don't see the need for initialization.
>
>
> Does device_property_read_u32 set clock if the property isn't found? If not,
> it needs to be initialized here. Or I can set it in an else statement from
> device_property_read_u32.
>
>
> >
> > > +
> > > + if (!device_property_read_u32(dev, "clock-frequency", &clock)) {
> > > + if (!clock)
> > > + clock = I2C_DEFAULT_CLK_RATE;
> > > + }
if (device_property_read_u32(dev, "clock-frequency", &clock) || !clock)
clock = I2C_DEFAULT_CLK_RATE;
> > > +
> > > + // i2c clock rate = local bus clock / (4 * (i2c clock div + 1))
> > You forgot to remove this.
>
>
> I actually meant to leave that comment to explain how the clock rate is
> calculated, as the reverse calculation in the code is a little more
> confusing.
>
Partially that is because you implemented DIV_ROUND_UP() manually.
>
> >
> > Andi
> >
> > > + i2c->clock_div = (((lbus + (clock - 1)) / clock) / 4) - 1;
= DIV_ROUND_UP(lbus, clock) / 4 - 1
Guenter
On 4/16/24 16:20, Guenter Roeck wrote:
> On Tue, Apr 16, 2024 at 01:09:04PM -0500, Eddie James wrote:
>> On 4/15/24 17:11, Andi Shyti wrote:
>>> Hi Eddie,
>>>
>>>> @@ -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;
>>> I don't see the need for initialization.
>>
>> Does device_property_read_u32 set clock if the property isn't found? If not,
>> it needs to be initialized here. Or I can set it in an else statement from
>> device_property_read_u32.
>>
>>
>>>> +
>>>> + if (!device_property_read_u32(dev, "clock-frequency", &clock)) {
>>>> + if (!clock)
>>>> + clock = I2C_DEFAULT_CLK_RATE;
>>>> + }
> if (device_property_read_u32(dev, "clock-frequency", &clock) || !clock)
> clock = I2C_DEFAULT_CLK_RATE;
Nice one, thanks.
>
>>>> +
>>>> + // i2c clock rate = local bus clock / (4 * (i2c clock div + 1))
>>> You forgot to remove this.
>>
>> I actually meant to leave that comment to explain how the clock rate is
>> calculated, as the reverse calculation in the code is a little more
>> confusing.
>>
> Partially that is because you implemented DIV_ROUND_UP() manually.
Thanks Guenter, good point.
Eddie
>>> Andi
>>>
>>>> + i2c->clock_div = (((lbus + (clock - 1)) / clock) / 4) - 1;
> = DIV_ROUND_UP(lbus, clock) / 4 - 1
>
> Guenter