This series primarily adds interrupt support to the FSI driver subsystem.
There are a number of related changes and fixes. Firstly, the FSI clocking
model is improved to allow FSI engine drivers to obtain real clock rates
and calculate appropriate clock dividers. Secondly, much master code is
made common through the use of a regmap to access master registers. This
will prove more useful as additional FSI master drivers are added. Lastly,
interrupt support is added to the FSI I2C driver.
Changes since v3:
- Fix compile warnings due to non-static local function and incorrect use
of handle_irq_desc
Changes since v2:
- Add several patches to fix additional issues found during testing
- For local bus frequenccy, use cfam clock-frequency property instead of
hardcoded master local bus divider
- Change default local bus clock divider from 8 to 2
- Zero the regmap_config structure in the common FSI initialization
function
- Add AST2700 direct AHB access of master registers
- Remove slave interrupt handler since it's not used yet
- For I2C driver, change minimum clock div to 3, use DIV_ROUND_UP instead
of re-implementing it, and use better logic for checking for
clock-frequency property
- Add detail to I2C driver formatting change
Eddie James (40):
fsi: hub: Set master index to link number plus one
fsi: Move slave definitions to fsi-slave.h
fsi: Fix slave addressing after break command
fsi: Use a defined value for default echo delay
fsi: Calculate local bus clock frequency
fsi: core: Improve master read/write/error traces
fsi: core: Add slave error trace
fsi: core: Reset errors instead of clearing interrupts
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: core: Disable relative addressing during scan
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
fsi: hub master: Reset hub master after errors
fsi: core: Add master register read-only sysfs
fsi: core: Add slave register read-only sysfs
fsi: i2cr: Adjust virtual CFAM ID to match Odyssey chip
fsi: core: Add different types of CFAM
spi: fsi: Calculate clock divider from local bus frequency
ARM: dts: aspeed: P10 and tacoma: Set FSI clock frequency
ARM: dts: aspeed: P10: Bump SPI max frequencies
.../dts/aspeed/aspeed-bmc-ibm-everest.dts | 32 +-
.../boot/dts/aspeed/aspeed-bmc-opp-tacoma.dts | 1 +
.../arm/boot/dts/aspeed/ibm-power10-dual.dtsi | 17 +-
.../arm/boot/dts/aspeed/ibm-power10-quad.dtsi | 16 +-
drivers/fsi/Kconfig | 2 +
drivers/fsi/fsi-core.c | 888 +++++++++++++++---
drivers/fsi/fsi-master-aspeed.c | 431 +++++----
drivers/fsi/fsi-master-hub.c | 244 ++---
drivers/fsi/fsi-master-i2cr.c | 2 +-
drivers/fsi/fsi-master.h | 33 +
drivers/fsi/fsi-slave.h | 117 +++
drivers/i2c/busses/i2c-fsi.c | 463 ++++++---
drivers/spi/spi-fsi.c | 33 +-
include/linux/fsi.h | 3 +
include/trace/events/fsi.h | 171 ++--
include/trace/events/fsi_master_aspeed.h | 86 +-
include/trace/events/i2c_fsi.h | 45 +
17 files changed, 1897 insertions(+), 687 deletions(-)
create mode 100644 include/trace/events/i2c_fsi.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 176e596b4391b..c9f6d84e1a372 100644
--- a/drivers/fsi/fsi-master-aspeed.c
+++ b/drivers/fsi/fsi-master-aspeed.c
@@ -402,11 +402,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);
+
+ regmap_write(aspeed->master.map, FSI_MMODE,
+ 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);
+
regmap_write(aspeed->master.map, FSI_MRESP0, FSI_MRESP_RST_ALL_MASTER);
+ regmap_write(aspeed->master.map, FSI_MMODE, aspeed->master.mmode);
+
spin_unlock_irqrestore(&aspeed->lock, flags);
trace_fsi_master_aspeed_cfam_reset(false);
--
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 | 106 ++++++++++++++++++++++-
include/trace/events/fsi_master_aspeed.h | 12 +++
2 files changed, 115 insertions(+), 3 deletions(-)
diff --git a/drivers/fsi/fsi-master-aspeed.c b/drivers/fsi/fsi-master-aspeed.c
index eecd64bc29512..34f4c9e00e43d 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,11 +27,13 @@ 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;
void __iomem *ctrl;
struct clk *clk;
struct gpio_desc *cfam_reset_gpio;
+ u32 irq_mask;
};
#define to_fsi_master_aspeed(m) \
@@ -79,6 +83,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)
@@ -96,7 +105,7 @@ static const u32 fsi_base = 0xa0000000;
#define OPB_RC_CTRL_OPB BIT(18) /* Access controller over OPB, not AHB (AST27xx+) */
#define OPB_RC_XFER_ACK_EN BIT(16) /* Enable OPBx xfer ack bit without mask */
#define OPB_RC_COUNT GENMASK(15, 0) /* Number of retries */
-#define OPB_RC_DEFAULT 0x10
+#define OPB_RC_DEFAULT (OPB_RC_XFER_ACK_EN | 0x10)
#define CREATE_TRACE_POINTS
#include <trace/events/fsi_master_aspeed.h>
@@ -322,11 +331,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);
}
@@ -477,6 +551,7 @@ static int fsi_master_aspeed_probe(struct platform_device *pdev)
struct resource *res;
unsigned int reg;
int rc, links;
+ int irq;
rc = tacoma_cabled_fsi_fixup(&pdev->dev);
if (rc) {
@@ -567,11 +642,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);
@@ -579,9 +655,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
@@ -593,6 +690,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 7eeecbfec7f09..dba1776334a0e 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
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]>
---
Changes since v2:
- Add AST2700 direct AHB access of master registers
drivers/fsi/fsi-master-aspeed.c | 173 +++++++++++++-------------------
1 file changed, 68 insertions(+), 105 deletions(-)
diff --git a/drivers/fsi/fsi-master-aspeed.c b/drivers/fsi/fsi-master-aspeed.c
index 3d15e867237df..a67f185bb8814 100644
--- a/drivers/fsi/fsi-master-aspeed.c
+++ b/drivers/fsi/fsi-master-aspeed.c
@@ -27,6 +27,7 @@ struct fsi_master_aspeed {
struct mutex lock; /* protect HW access */
struct device *dev;
void __iomem *base;
+ void __iomem *ctrl;
struct clk *clk;
struct gpio_desc *cfam_reset_gpio;
};
@@ -95,14 +96,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,
@@ -333,35 +326,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;
@@ -389,72 +353,54 @@ 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);
+ return opb_writel(context, ctrl_base + reg, cpu_to_be32(val));
+}
- reg = cpu_to_be32(FSI_MRESP_RST_ALL_MASTER | FSI_MRESP_RST_ALL_LINK);
- opb_writel(aspeed, ctrl_base + FSI_MRESP0, reg);
+static const struct regmap_bus regmap_aspeed_opb = {
+ .reg_write = regmap_aspeed_opb_write,
+ .reg_read = regmap_aspeed_opb_read,
+};
- opb_readl(aspeed, ctrl_base + FSI_MLEVP0, NULL);
+static int regmap_ast2700_read(void *context, unsigned int reg, unsigned int *val)
+{
+ struct fsi_master_aspeed *aspeed = context;
- /* Reset the master bridge */
- reg = cpu_to_be32(FSI_MRESB_RST_GEN);
- opb_writel(aspeed, ctrl_base + FSI_MRESB0, reg);
+ *val = readl(aspeed->ctrl + reg);
+ return 0;
+}
- reg = cpu_to_be32(FSI_MRESB_RST_ERR);
- opb_writel(aspeed, ctrl_base + FSI_MRESB0, reg);
+static int regmap_ast2700_write(void *context, unsigned int reg, unsigned int val)
+{
+ struct fsi_master_aspeed *aspeed = context;
+ writel(val, aspeed->ctrl + reg);
return 0;
}
+static const struct regmap_bus regmap_ast2700 = {
+ .reg_write = regmap_ast2700_write,
+ .reg_read = regmap_ast2700_read,
+};
+
static ssize_t cfam_reset_store(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
@@ -466,7 +412,7 @@ static ssize_t cfam_reset_store(struct device *dev, struct device_attribute *att
usleep_range(900, 1000);
gpiod_set_value(aspeed->cfam_reset_gpio, 0);
usleep_range(900, 1000);
- opb_writel(aspeed, ctrl_base + FSI_MRESP0, cpu_to_be32(FSI_MRESP_RST_ALL_MASTER));
+ regmap_write(aspeed->master.map, FSI_MRESP0, FSI_MRESP_RST_ALL_MASTER);
mutex_unlock(&aspeed->lock);
trace_fsi_master_aspeed_cfam_reset(false);
@@ -526,14 +472,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 {
@@ -549,9 +487,13 @@ 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_RC_DEFAULT;
+ const struct regmap_bus *bus = ®map_aspeed_opb;
+ struct regmap_config aspeed_master_regmap_config;
struct fsi_master_aspeed *aspeed;
- int rc, links, reg;
- __be32 raw;
+ u32 opb_ctrl_base = ctrl_base;
+ struct resource *res;
+ unsigned int reg;
+ int rc, links;
rc = tacoma_cabled_fsi_fixup(&pdev->dev);
if (rc) {
@@ -571,6 +513,17 @@ static int fsi_master_aspeed_probe(struct platform_device *pdev)
goto err_free_aspeed;
}
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "ctrl");
+ if (res) {
+ aspeed->ctrl = devm_ioremap_resource(&pdev->dev, res);
+ if (!IS_ERR(aspeed->ctrl)) {
+ /* Access FSI controller over AHB */
+ opb_ctrl_base = res->start;
+ opb_retry_counter &= ~OPB_RC_CTRL_OPB;
+ bus = ®map_ast2700;
+ }
+ }
+
aspeed->clk = devm_clk_get(aspeed->dev, NULL);
if (IS_ERR(aspeed->clk)) {
dev_err(aspeed->dev, "couldn't get clock\n");
@@ -594,7 +547,7 @@ static int fsi_master_aspeed_probe(struct platform_device *pdev)
writel(opb_retry_counter, aspeed->base + OPB_RETRY_COUNTER);
- writel(ctrl_base, aspeed->base + OPB_CTRL_BASE);
+ writel(opb_ctrl_base, aspeed->base + OPB_CTRL_BASE);
writel(fsi_base, aspeed->base + OPB_FSI_BASE);
/* Set read data order */
@@ -611,13 +564,19 @@ 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, bus, 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);
@@ -626,20 +585,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
@@ -651,6 +612,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
After a break command, the slave ID is set to 0x3, which means the
FSI driver should use that ID when communicating with the slave, until
SMODE is programmed with the new ID (forced to 0 for 23 bit addressing in
the current implementation). This worked previously due to a feature of
newer FSI slaves that don't enforce this requirement. Since hub masters
cannot address non-zero slave IDs, disable this behavior for slaves off
hub masters.
Fixes: 2b545cd8e1b2 ("drivers/fsi: Implement slave initialisation")
Signed-off-by: Eddie James <[email protected]>
---
drivers/fsi/fsi-core.c | 20 ++++++++++----------
drivers/fsi/fsi-master-hub.c | 1 +
drivers/fsi/fsi-master.h | 1 +
drivers/fsi/fsi-slave.h | 1 +
4 files changed, 13 insertions(+), 10 deletions(-)
diff --git a/drivers/fsi/fsi-core.c b/drivers/fsi/fsi-core.c
index 7bf0c96fc0172..e8dbf5e95c234 100644
--- a/drivers/fsi/fsi-core.c
+++ b/drivers/fsi/fsi-core.c
@@ -217,7 +217,7 @@ static uint32_t fsi_slave_smode(int id, u8 t_senddly, u8 t_echodly)
| fsi_smode_lbcrr(0x8);
}
-static int fsi_slave_set_smode(struct fsi_slave *slave)
+static int fsi_slave_set_smode(struct fsi_slave *slave, uint8_t id)
{
uint32_t smode;
__be32 data;
@@ -228,8 +228,7 @@ static int fsi_slave_set_smode(struct fsi_slave *slave)
smode = fsi_slave_smode(slave->id, slave->t_send_delay, slave->t_echo_delay);
data = cpu_to_be32(smode);
- return fsi_master_write(slave->master, slave->link, slave->id,
- FSI_SLAVE_BASE + FSI_SMODE,
+ return fsi_master_write(slave->master, slave->link, id, FSI_SLAVE_BASE + FSI_SMODE,
&data, sizeof(data));
}
@@ -281,7 +280,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, FSI_SMODE_SID_BREAK);
if (rc)
return rc;
@@ -773,7 +772,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, slave->id);
if (rc < 0)
return rc;
if (master->link_config)
@@ -945,6 +944,8 @@ EXPORT_SYMBOL_GPL(fsi_free_minor);
static int fsi_slave_init(struct fsi_master *master, int link, uint8_t id)
{
+ const uint8_t break_id = (master->flags & FSI_MASTER_FLAG_NO_BREAK_SID) ? 0 :
+ FSI_SMODE_SID_BREAK;
uint32_t cfam_id;
struct fsi_slave *slave;
uint8_t crc;
@@ -957,7 +958,7 @@ static int fsi_slave_init(struct fsi_master *master, int link, uint8_t id)
if (id != 0)
return -EINVAL;
- rc = fsi_master_read(master, link, id, 0, &data, sizeof(data));
+ rc = fsi_master_read(master, link, break_id, 0, &data, sizeof(data));
if (rc) {
dev_dbg(&master->dev, "can't read slave %02x:%02x %d\n",
link, id, rc);
@@ -981,9 +982,8 @@ static int fsi_slave_init(struct fsi_master *master, int link, uint8_t id)
*/
if (master->flags & FSI_MASTER_FLAG_SWCLOCK) {
llmode = cpu_to_be32(FSI_LLMODE_ASYNC);
- rc = fsi_master_write(master, link, id,
- FSI_SLAVE_BASE + FSI_LLMODE,
- &llmode, sizeof(llmode));
+ rc = fsi_master_write(master, link, break_id, FSI_SLAVE_BASE + FSI_LLMODE, &llmode,
+ sizeof(llmode));
if (rc)
dev_warn(&master->dev,
"can't set llmode on slave:%02x:%02x %d\n",
@@ -1028,7 +1028,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, break_id);
if (rc) {
dev_warn(&master->dev,
"can't set smode on slave:%02x:%02x %d\n",
diff --git a/drivers/fsi/fsi-master-hub.c b/drivers/fsi/fsi-master-hub.c
index 36da643b32018..eea8649fee74d 100644
--- a/drivers/fsi/fsi-master-hub.c
+++ b/drivers/fsi/fsi-master-hub.c
@@ -232,6 +232,7 @@ static int hub_master_probe(struct device *dev)
hub->master.idx = fsi_dev->slave->link + 1;
hub->master.n_links = links;
+ hub->master.flags = FSI_MASTER_FLAG_NO_BREAK_SID;
hub->master.read = hub_master_read;
hub->master.write = hub_master_write;
hub->master.send_break = hub_master_break;
diff --git a/drivers/fsi/fsi-master.h b/drivers/fsi/fsi-master.h
index 967622c1cabf7..a0d7ad0f0f7cc 100644
--- a/drivers/fsi/fsi-master.h
+++ b/drivers/fsi/fsi-master.h
@@ -111,6 +111,7 @@
/* fsi-master definition and flags */
#define FSI_MASTER_FLAG_SWCLOCK 0x1
+#define FSI_MASTER_FLAG_NO_BREAK_SID 0x2
/*
* Structures and function prototypes
diff --git a/drivers/fsi/fsi-slave.h b/drivers/fsi/fsi-slave.h
index dba65bd4e083f..f6cca04131a92 100644
--- a/drivers/fsi/fsi-slave.h
+++ b/drivers/fsi/fsi-slave.h
@@ -39,6 +39,7 @@
#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_SID_BREAK 3 /* ID after break command */
#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 */
--
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 8fb5b51b74a4d..44aa750278100 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
The slave registers are commonly used for debugging or diagnosis
so provide them in sysfs files.
Signed-off-by: Eddie James <[email protected]>
---
drivers/fsi/fsi-core.c | 144 +++++++++++++++++++++++++++++++++++++---
drivers/fsi/fsi-slave.h | 21 ++++++
2 files changed, 155 insertions(+), 10 deletions(-)
diff --git a/drivers/fsi/fsi-core.c b/drivers/fsi/fsi-core.c
index 4d2e14e2a9148..62a14a7f28498 100644
--- a/drivers/fsi/fsi-core.c
+++ b/drivers/fsi/fsi-core.c
@@ -887,22 +887,146 @@ static ssize_t cfam_id_show(struct device *dev,
static DEVICE_ATTR_RO(cfam_id);
-static struct attribute *cfam_attr[] = {
+static ssize_t config_table_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ const unsigned int end = engine_page_size / sizeof(u32);
+ struct fsi_slave *slave = to_fsi_slave(dev);
+ __be32 data;
+ int len = 0;
+ u32 conf;
+ int rc;
+
+ for (unsigned int i = 0; i < end; ++i) {
+ rc = fsi_slave_read(slave, i * sizeof(data), &data, sizeof(data));
+ if (rc)
+ return rc;
+
+ conf = be32_to_cpu(data);
+ if (crc4(0, conf, 32))
+ return -EBADMSG;
+
+ len += sysfs_emit_at(buf, len, "%08x\n", conf);
+ if (!(conf & FSI_SLAVE_CONF_NEXT_MASK))
+ break;
+ }
+
+ return len;
+}
+
+static DEVICE_ATTR_RO(config_table);
+
+struct fsi_slave_attribute {
+ struct device_attribute attr;
+ int reg;
+};
+
+static ssize_t slave_reg_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct fsi_slave_attribute *fattr = container_of(attr, struct fsi_slave_attribute, attr);
+ struct fsi_slave *slave = to_fsi_slave(dev);
+ __be32 data;
+ int rc;
+
+ rc = fsi_slave_read(slave, FSI_SLAVE_BASE + fattr->reg, &data, sizeof(data));
+ if (rc)
+ return rc;
+
+ return sysfs_emit(buf, "%08x\n", be32_to_cpu(data));
+}
+
+static ssize_t slave_reg_8bpp_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct fsi_slave_attribute *fattr = container_of(attr, struct fsi_slave_attribute, attr);
+ struct fsi_slave *slave = to_fsi_slave(dev);
+ __be32 data;
+ int len = 0;
+ int rc;
+ int i;
+
+ for (i = 0; i < 2; ++i) {
+ rc = fsi_slave_read(slave, FSI_SLAVE_BASE + fattr->reg + (i * 4), &data,
+ sizeof(data));
+ if (rc)
+ return rc;
+
+ len += sysfs_emit_at(buf, len, "%08x\n", be32_to_cpu(data));
+ }
+
+ return len;
+}
+
+#define FSI_SLAVE_ATTR(name, reg) \
+ struct fsi_slave_attribute dev_attr_##name = { __ATTR(name, 0444, slave_reg_show, NULL), reg }
+#define FSI_SLAVE_ATTR_8BPP(name, reg) \
+ struct fsi_slave_attribute dev_attr_##name = { __ATTR(name, 0444, slave_reg_8bpp_show, NULL), reg }
+
+static FSI_SLAVE_ATTR(smode, FSI_SMODE);
+static FSI_SLAVE_ATTR(sdma, FSI_SDMA);
+static FSI_SLAVE_ATTR(sisc, FSI_SISC);
+static FSI_SLAVE_ATTR(sism, FSI_SISM);
+static FSI_SLAVE_ATTR(siss, FSI_SISS);
+static FSI_SLAVE_ATTR(sstat, FSI_SSTAT);
+static FSI_SLAVE_ATTR(si1m, FSI_SI1M);
+static FSI_SLAVE_ATTR(si1s, FSI_SI1S);
+static FSI_SLAVE_ATTR(sic, FSI_SIC);
+static FSI_SLAVE_ATTR(si2m, FSI_SI2M);
+static FSI_SLAVE_ATTR(si2s, FSI_SI2S);
+static FSI_SLAVE_ATTR(scmdt, FSI_SCMDT);
+static FSI_SLAVE_ATTR(sdata, FSI_SDATA);
+static FSI_SLAVE_ATTR(slastd, FSI_SLASTD);
+static FSI_SLAVE_ATTR(smbl, FSI_SMBL);
+static FSI_SLAVE_ATTR(soml, FSI_SOML);
+static FSI_SLAVE_ATTR(snml, FSI_SNML);
+static FSI_SLAVE_ATTR(smbr, FSI_SMBR);
+static FSI_SLAVE_ATTR(somr, FSI_SOMR);
+static FSI_SLAVE_ATTR(snmr, FSI_SNMR);
+static FSI_SLAVE_ATTR_8BPP(scrsic, FSI_ScRSIC0);
+static FSI_SLAVE_ATTR_8BPP(scrsim, FSI_ScRSIM0);
+static FSI_SLAVE_ATTR_8BPP(scrsis, FSI_ScRSIS0);
+static FSI_SLAVE_ATTR_8BPP(srsic, FSI_SRSIC0);
+static FSI_SLAVE_ATTR_8BPP(srsim, FSI_SRSIM0);
+static FSI_SLAVE_ATTR_8BPP(srsis, FSI_SRSIS0);
+static FSI_SLAVE_ATTR(llmode, FSI_LLMODE);
+static FSI_SLAVE_ATTR(llstat, FSI_LLSTAT);
+
+static struct attribute *cfam_attrs[] = {
&dev_attr_send_echo_delays.attr,
&dev_attr_chip_id.attr,
&dev_attr_cfam_id.attr,
&dev_attr_send_term.attr,
+ &dev_attr_config_table.attr,
+ &dev_attr_smode.attr.attr,
+ &dev_attr_sdma.attr.attr,
+ &dev_attr_sisc.attr.attr,
+ &dev_attr_sism.attr.attr,
+ &dev_attr_siss.attr.attr,
+ &dev_attr_sstat.attr.attr,
+ &dev_attr_si1m.attr.attr,
+ &dev_attr_si1s.attr.attr,
+ &dev_attr_sic.attr.attr,
+ &dev_attr_si2m.attr.attr,
+ &dev_attr_si2s.attr.attr,
+ &dev_attr_scmdt.attr.attr,
+ &dev_attr_sdata.attr.attr,
+ &dev_attr_slastd.attr.attr,
+ &dev_attr_smbl.attr.attr,
+ &dev_attr_soml.attr.attr,
+ &dev_attr_snml.attr.attr,
+ &dev_attr_smbr.attr.attr,
+ &dev_attr_somr.attr.attr,
+ &dev_attr_snmr.attr.attr,
+ &dev_attr_scrsic.attr.attr,
+ &dev_attr_scrsim.attr.attr,
+ &dev_attr_scrsis.attr.attr,
+ &dev_attr_srsic.attr.attr,
+ &dev_attr_srsim.attr.attr,
+ &dev_attr_srsis.attr.attr,
+ &dev_attr_llmode.attr.attr,
+ &dev_attr_llstat.attr.attr,
NULL,
};
-static const struct attribute_group cfam_attr_group = {
- .attrs = cfam_attr,
-};
-
-static const struct attribute_group *cfam_attr_groups[] = {
- &cfam_attr_group,
- NULL,
-};
+ATTRIBUTE_GROUPS(cfam);
static char *cfam_devnode(const struct device *dev, umode_t *mode,
kuid_t *uid, kgid_t *gid)
@@ -919,7 +1043,7 @@ static char *cfam_devnode(const struct device *dev, umode_t *mode,
static const struct device_type cfam_type = {
.name = "cfam",
.devnode = cfam_devnode,
- .groups = cfam_attr_groups
+ .groups = cfam_groups
};
static char *fsi_cdev_devnode(const struct device *dev, umode_t *mode,
diff --git a/drivers/fsi/fsi-slave.h b/drivers/fsi/fsi-slave.h
index 762636e7c8633..ccc394e92a93f 100644
--- a/drivers/fsi/fsi-slave.h
+++ b/drivers/fsi/fsi-slave.h
@@ -14,6 +14,7 @@
* FSI slave engine control register offsets
*/
#define FSI_SMODE 0x0 /* R/W: Mode register */
+#define FSI_SDMA 0x4 /* R/W: DMA control */
#define FSI_SISC 0x8 /* R : Interrupt condition */
#define FSI_SCISC 0x8 /* C : Clear interrupt condition */
#define FSI_SISM 0xc /* R/W: Interrupt mask */
@@ -21,11 +22,30 @@
#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_SI1M 0x18 /* R/W: Interrupt 1 mask */
#define FSI_SI1S 0x1c /* R : Slave interrupt 1 status */
#define FSI_SSI1M 0x1c /* S : Set slave interrupt 1 mask */
+#define FSI_SIC 0x20 /* R : Interrupt 1 condition */
#define FSI_SCI1M 0x20 /* C : Clear slave interrupt 1 mask */
+#define FSI_SI2M 0x24 /* R/W: Interrupt 2 mask */
+#define FSI_SI2S 0x28 /* R : Interrupt 2 status */
+#define FSI_SCMDT 0x2c /* R : Last command trace */
+#define FSI_SDATA 0x30 /* R : Last data trace */
#define FSI_SLBUS 0x30 /* W : LBUS Ownership */
+#define FSI_SLASTD 0x34 /* R : Last data sent */
#define FSI_SRES 0x34 /* W : Reset */
+#define FSI_SMBL 0x38
+#define FSI_SOML 0x3c
+#define FSI_SNML 0x40
+#define FSI_SMBR 0x44
+#define FSI_SOMR 0x48
+#define FSI_SNMR 0x4c
+#define FSI_ScRSIC0 0x50
+#define FSI_ScRSIC4 0x54
+#define FSI_ScRSIM0 0x58
+#define FSI_ScRSIM4 0x5c
+#define FSI_ScRSIS0 0x60
+#define FSI_ScRSIS4 0x64
#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 */
@@ -33,6 +53,7 @@
#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 */
+#define FSI_LLSTAT 0x104
/*
* SMODE fields
--
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 10ca23cf58c2e..176e596b4391b 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;
void __iomem *ctrl;
@@ -251,6 +251,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)
@@ -259,7 +260,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:
@@ -278,7 +279,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;
}
@@ -286,6 +287,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)
@@ -294,7 +296,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:
@@ -313,7 +315,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;
}
@@ -396,15 +398,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);
regmap_write(aspeed->master.map, FSI_MRESP0, FSI_MRESP_RST_ALL_MASTER);
- mutex_unlock(&aspeed->lock);
+ spin_unlock_irqrestore(&aspeed->lock, flags);
trace_fsi_master_aspeed_cfam_reset(false);
return count;
@@ -498,6 +501,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)) {
@@ -583,7 +587,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
Other FSI devices can uses aliases for the device numbering, so
modify the function to get a new minor to allow the cfam type
to use aliases too.
Signed-off-by: Eddie James <[email protected]>
---
drivers/fsi/fsi-core.c | 59 +++++++++++++++++++++---------------------
1 file changed, 29 insertions(+), 30 deletions(-)
diff --git a/drivers/fsi/fsi-core.c b/drivers/fsi/fsi-core.c
index 660f89b743235..36e31eafad3d0 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);
@@ -1059,7 +1058,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
Detect the CFAM type based on the chip id and set up the device
type based on the CFAM type.
Signed-off-by: Eddie James <[email protected]>
---
Changes since v3:
- Define fsi_get_cfam_type as static
drivers/fsi/fsi-core.c | 84 +++++++++++++++++++++++++++++++++++++++++-
1 file changed, 83 insertions(+), 1 deletion(-)
diff --git a/drivers/fsi/fsi-core.c b/drivers/fsi/fsi-core.c
index 62a14a7f28498..bc561d1b2701c 100644
--- a/drivers/fsi/fsi-core.c
+++ b/drivers/fsi/fsi-core.c
@@ -1028,6 +1028,37 @@ static struct attribute *cfam_attrs[] = {
ATTRIBUTE_GROUPS(cfam);
+static struct attribute *cfam_s_attrs[] = {
+ &dev_attr_send_echo_delays.attr,
+ &dev_attr_chip_id.attr,
+ &dev_attr_cfam_id.attr,
+ &dev_attr_send_term.attr,
+ &dev_attr_config_table.attr,
+ &dev_attr_smode.attr.attr,
+ &dev_attr_sdma.attr.attr,
+ &dev_attr_sisc.attr.attr,
+ &dev_attr_sism.attr.attr,
+ &dev_attr_siss.attr.attr,
+ &dev_attr_sstat.attr.attr,
+ &dev_attr_si1m.attr.attr,
+ &dev_attr_si1s.attr.attr,
+ &dev_attr_sic.attr.attr,
+ &dev_attr_si2m.attr.attr,
+ &dev_attr_si2s.attr.attr,
+ &dev_attr_scmdt.attr.attr,
+ &dev_attr_sdata.attr.attr,
+ &dev_attr_slastd.attr.attr,
+ &dev_attr_smbl.attr.attr,
+ &dev_attr_soml.attr.attr,
+ &dev_attr_snml.attr.attr,
+ &dev_attr_smbr.attr.attr,
+ &dev_attr_somr.attr.attr,
+ &dev_attr_snmr.attr.attr,
+ NULL,
+};
+
+ATTRIBUTE_GROUPS(cfam_s);
+
static char *cfam_devnode(const struct device *dev, umode_t *mode,
kuid_t *uid, kgid_t *gid)
{
@@ -1046,6 +1077,57 @@ static const struct device_type cfam_type = {
.groups = cfam_groups
};
+static char *cfam_ody_devnode(const struct device *dev, umode_t *mode, kuid_t *uid, kgid_t *gid)
+{
+ const struct fsi_slave *slave = to_fsi_slave(dev);
+
+#ifdef CONFIG_FSI_NEW_DEV_NODE
+ return kasprintf(GFP_KERNEL, "fsi/ody%d", slave->cdev_idx);
+#else
+ return kasprintf(GFP_KERNEL, "ody%d", slave->cdev_idx);
+#endif
+}
+
+static const struct device_type cfam_ody_type = {
+ .name = "ody",
+ .devnode = cfam_ody_devnode
+};
+
+static char *cfam_s_devnode(const struct device *dev, umode_t *mode, kuid_t *uid, kgid_t *gid)
+{
+ const struct fsi_slave *slave = to_fsi_slave(dev);
+
+#ifdef CONFIG_FSI_NEW_DEV_NODE
+ return kasprintf(GFP_KERNEL, "fsi/cfam-s%d", slave->cdev_idx);
+#else
+ return kasprintf(GFP_KERNEL, "cfam-s%d", slave->cdev_idx);
+#endif
+}
+
+static const struct device_type cfam_s_type = {
+ .name = "cfam-s",
+ .devnode = cfam_s_devnode,
+ .groups = cfam_s_groups,
+};
+
+static const struct device_type *fsi_get_cfam_type(u32 id)
+{
+ u32 major = (id & 0xf00) >> 8;
+ u32 minor = (id & 0xf0) >> 4;
+
+ switch (major) {
+ case 0x9:
+ return &cfam_s_type;
+ case 0xc:
+ if (minor == 0)
+ return &cfam_ody_type;
+ fallthrough;
+ case 0xd:
+ default:
+ return &cfam_type;
+ }
+}
+
static char *fsi_cdev_devnode(const struct device *dev, umode_t *mode,
kuid_t *uid, kgid_t *gid)
{
@@ -1202,7 +1284,7 @@ static int fsi_slave_init(struct fsi_master *master, int link, uint8_t id)
spin_lock_init(&slave->lock);
dev_set_name(&slave->dev, "slave@%02x:%02x", link, id);
- slave->dev.type = &cfam_type;
+ slave->dev.type = fsi_get_cfam_type(cfam_id);
slave->dev.parent = &master->dev;
slave->dev.of_node = fsi_slave_find_of_node(master, link, id);
slave->dev.release = fsi_slave_release;
--
2.39.3
The master registers are commonly used for debugging or diagnosis so
provide them in sysfs files.
Signed-off-by: Eddie James <[email protected]>
---
drivers/fsi/fsi-core.c | 144 +++++++++++++++++++++++++++++++++++++++
drivers/fsi/fsi-master.h | 6 ++
2 files changed, 150 insertions(+)
diff --git a/drivers/fsi/fsi-core.c b/drivers/fsi/fsi-core.c
index 58cb6bc6ccc91..4d2e14e2a9148 100644
--- a/drivers/fsi/fsi-core.c
+++ b/drivers/fsi/fsi-core.c
@@ -1392,6 +1392,141 @@ static ssize_t master_break_store(struct device *dev,
static DEVICE_ATTR(break, 0200, NULL, master_break_store);
+struct fsi_master_attribute {
+ struct device_attribute attr;
+ int reg;
+};
+
+static ssize_t master_reg_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct fsi_master_attribute *fattr = container_of(attr, struct fsi_master_attribute, attr);
+ struct fsi_master *master = to_fsi_master(dev);
+ unsigned int reg;
+ int rc;
+
+ rc = regmap_read(master->map, fattr->reg, ®);
+ if (rc)
+ return rc;
+
+ return sysfs_emit(buf, "%08x\n", reg);
+}
+
+static ssize_t master_reg_1bpp_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct fsi_master_attribute *fattr = container_of(attr, struct fsi_master_attribute, attr);
+ struct fsi_master *master = to_fsi_master(dev);
+ unsigned int count = (master->n_links + 31) / 32;
+ unsigned int reg;
+ unsigned int i;
+ int len = 0;
+ int rc;
+
+ for (i = 0; i < count; ++i) {
+ rc = regmap_read(master->map, fattr->reg + (i * 4), ®);
+ if (rc)
+ return rc;
+
+ len += sysfs_emit_at(buf, len, "%08x\n", reg);
+ }
+
+ return len;
+}
+
+static ssize_t master_reg_4bpp_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct fsi_master_attribute *fattr = container_of(attr, struct fsi_master_attribute, attr);
+ struct fsi_master *master = to_fsi_master(dev);
+ unsigned int count = (master->n_links + 7) / 8;
+ unsigned int reg;
+ unsigned int i;
+ int len = 0;
+ int rc;
+
+ for (i = 0; i < count; ++i) {
+ rc = regmap_read(master->map, fattr->reg + (i * 4), ®);
+ if (rc)
+ return rc;
+
+ len += sysfs_emit_at(buf, len, "%08x\n", reg);
+ }
+
+ return len;
+}
+
+static ssize_t master_reg_32bpp_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct fsi_master_attribute *fattr = container_of(attr, struct fsi_master_attribute, attr);
+ struct fsi_master *master = to_fsi_master(dev);
+ unsigned int reg;
+ int len = 0;
+ int rc;
+ int i;
+
+ for (i = 0; i < master->n_links; ++i) {
+ rc = regmap_read(master->map, fattr->reg + (i * 4), ®);
+ if (rc)
+ return rc;
+
+ len += sysfs_emit_at(buf, len, "%08x\n", reg);
+ }
+
+ return len;
+}
+
+#define FSI_MASTER_ATTR(name, reg) \
+ struct fsi_master_attribute dev_attr_##name = { __ATTR(name, 0444, master_reg_show, NULL), reg }
+#define FSI_MASTER_ATTR_1BPP(name, reg) \
+ struct fsi_master_attribute dev_attr_##name = { __ATTR(name, 0444, master_reg_1bpp_show, NULL), reg }
+#define FSI_MASTER_ATTR_4BPP(name, reg) \
+ struct fsi_master_attribute dev_attr_##name = { __ATTR(name, 0444, master_reg_4bpp_show, NULL), reg }
+#define FSI_MASTER_ATTR_32BPP(name, reg) \
+ struct fsi_master_attribute dev_attr_##name = { __ATTR(name, 0444, master_reg_32bpp_show, NULL), reg }
+
+static FSI_MASTER_ATTR(mmode, FSI_MMODE);
+static FSI_MASTER_ATTR(mdlyr, FSI_MDLYR);
+static FSI_MASTER_ATTR_1BPP(mcrsp, FSI_MCRSP);
+static FSI_MASTER_ATTR_1BPP(menp, FSI_MENP0);
+static FSI_MASTER_ATTR_1BPP(mlevp, FSI_MLEVP0);
+static FSI_MASTER_ATTR_1BPP(mrefp, FSI_MREFP0);
+static FSI_MASTER_ATTR_1BPP(mhpmp, FSI_MHPMP0);
+static FSI_MASTER_ATTR_4BPP(msiep, FSI_MSIEP0);
+static FSI_MASTER_ATTR_1BPP(maesp, FSI_MAESP0);
+static FSI_MASTER_ATTR(maeb, FSI_MAEB);
+static FSI_MASTER_ATTR(mver, FSI_MVER);
+static FSI_MASTER_ATTR_1BPP(mbsyp, FSI_MBSYP0);
+static FSI_MASTER_ATTR_32BPP(mstap, FSI_MSTAP0);
+static FSI_MASTER_ATTR(mesrb, FSI_MESRB0);
+static FSI_MASTER_ATTR(mscsb, FSI_MSCSB0);
+static FSI_MASTER_ATTR(matrb, FSI_MATRB0);
+static FSI_MASTER_ATTR(mdtrb, FSI_MDTRB0);
+static FSI_MASTER_ATTR(mectrl, FSI_MECTRL);
+
+static struct attribute *master_mapped_attrs[] = {
+ &dev_attr_mmode.attr.attr,
+ &dev_attr_mdlyr.attr.attr,
+ &dev_attr_mcrsp.attr.attr,
+ &dev_attr_menp.attr.attr,
+ &dev_attr_mlevp.attr.attr,
+ &dev_attr_mrefp.attr.attr,
+ &dev_attr_mhpmp.attr.attr,
+ &dev_attr_msiep.attr.attr,
+ &dev_attr_maesp.attr.attr,
+ &dev_attr_maeb.attr.attr,
+ &dev_attr_mver.attr.attr,
+ &dev_attr_mbsyp.attr.attr,
+ &dev_attr_mstap.attr.attr,
+ &dev_attr_mesrb.attr.attr,
+ &dev_attr_mscsb.attr.attr,
+ &dev_attr_matrb.attr.attr,
+ &dev_attr_mdtrb.attr.attr,
+ &dev_attr_mectrl.attr.attr,
+ NULL
+};
+
+static const struct attribute_group master_mapped_group = {
+ .attrs = master_mapped_attrs,
+};
+
static struct attribute *master_attrs[] = {
&dev_attr_break.attr,
&dev_attr_rescan.attr,
@@ -1665,6 +1800,12 @@ int fsi_master_register(struct fsi_master *master)
}
out:
mutex_unlock(&master->scan_lock);
+
+ if (!rc && master->map) {
+ if (!sysfs_create_group(&master->dev.kobj, &master_mapped_group))
+ master->groups = true;
+ }
+
return rc;
}
EXPORT_SYMBOL_GPL(fsi_master_register);
@@ -1675,6 +1816,9 @@ void fsi_master_unregister(struct fsi_master *master)
trace_fsi_master_unregister(master);
+ if (master->groups)
+ sysfs_remove_group(&master->dev.kobj, &master_mapped_group);
+
mutex_lock(&master->scan_lock);
fsi_master_unscan(master);
master->n_links = 0;
diff --git a/drivers/fsi/fsi-master.h b/drivers/fsi/fsi-master.h
index 2104902091e05..1fa101a477899 100644
--- a/drivers/fsi/fsi-master.h
+++ b/drivers/fsi/fsi-master.h
@@ -12,6 +12,7 @@
#include <linux/device.h>
#include <linux/irq.h>
#include <linux/mutex.h>
+#include <linux/sysfs.h>
/*
* Master registers
@@ -27,12 +28,16 @@
#define FSI_MENP0 0x10 /* R/W: enable */
#define FSI_MLEVP0 0x18 /* R: plug detect */
#define FSI_MSENP0 0x18 /* S: Set enable */
+#define FSI_MREFP0 0x20 /* R: Plug reference */
#define FSI_MCENP0 0x20 /* C: Clear enable */
+#define FSI_MHPMP0 0x28 /* R: Plug monitor */
#define FSI_MSIEP0 0x30 /* R/W: interrupt enable */
+#define FSI_MAESP0 0x50 /* R: Any error port */
#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_MBSYP0 0x78 /* R: Port busy */
#define FSI_MSTAP0 0xd0 /* R: Port status */
#define FSI_MRESP0 0xd0 /* W: Port reset */
#define FSI_MESRB0 0x1d0 /* R: Master error status */
@@ -151,6 +156,7 @@ struct fsi_master {
int (*link_config)(struct fsi_master *, int link,
u8 t_send_delay, u8 t_echo_delay);
u8 remote_interrupt_status;
+ bool groups;
};
#define to_fsi_master(d) container_of(d, struct fsi_master, 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 f3b97bf88d0f9..022f1287aa0e3 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
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 44aa750278100..f3b97bf88d0f9 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);
@@ -705,38 +701,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);
@@ -745,16 +733,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
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 c9f6d84e1a372..eecd64bc29512 100644
--- a/drivers/fsi/fsi-master-aspeed.c
+++ b/drivers/fsi/fsi-master-aspeed.c
@@ -226,27 +226,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)
{
@@ -277,7 +256,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;
@@ -313,7 +293,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
No functional change. Remove holes in the master structure. Re-order
local variables to look pretty. Remove some local variables in favor of
using their containing structure.
Signed-off-by: Eddie James <[email protected]>
---
Changes since v2:
- Add detail to commit message.
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 2404ace8c56fa..8fb5b51b74a4d 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);
@@ -752,14 +747,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
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]>
---
Changes since v3:
- Remove use of handle_irq_desc
Changes since v2:
- Remove slave interrupt handler since it's not used yet
drivers/fsi/fsi-core.c | 159 +++++++++++++++++++++++++++++++++++++
drivers/fsi/fsi-master.h | 9 +++
drivers/fsi/fsi-slave.h | 1 -
include/linux/fsi.h | 2 +
include/trace/events/fsi.h | 41 ++++++++++
5 files changed, 211 insertions(+), 1 deletion(-)
diff --git a/drivers/fsi/fsi-core.c b/drivers/fsi/fsi-core.c
index 8b402149acbe9..58cb6bc6ccc91 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)
{
@@ -1467,6 +1530,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)
+{
+ unsigned int downstream = irq_find_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) {
+ generic_handle_irq(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;
@@ -1491,6 +1647,9 @@ int fsi_master_register(struct fsi_master *master)
if (master->flags & FSI_MASTER_FLAG_SWCLOCK)
master->clock_frequency = 100000000; // POWER reference clock
+ 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 8ea2f69ec4922..2104902091e05 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/drivers/fsi/fsi-slave.h b/drivers/fsi/fsi-slave.h
index 0468ec1c60db2..762636e7c8633 100644
--- a/drivers/fsi/fsi-slave.h
+++ b/drivers/fsi/fsi-slave.h
@@ -73,7 +73,6 @@
#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
diff --git a/include/linux/fsi.h b/include/linux/fsi.h
index e0309bf0ae072..c249a95b7ff84 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 da977d59e163e..c750ae8e1f5b4 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),
--
2.39.3
The hub master driver wasn't clearing errors after accessing remote
slaves. Perform the standard master reset procedure to clear errors
to fully recover.
Signed-off-by: Eddie James <[email protected]>
---
drivers/fsi/fsi-master-hub.c | 14 ++++++++++++--
1 file changed, 12 insertions(+), 2 deletions(-)
diff --git a/drivers/fsi/fsi-master-hub.c b/drivers/fsi/fsi-master-hub.c
index 4dbc542500bbd..1bd53b1e52a91 100644
--- a/drivers/fsi/fsi-master-hub.c
+++ b/drivers/fsi/fsi-master-hub.c
@@ -49,24 +49,34 @@ static int hub_master_read(struct fsi_master *master, int link,
uint8_t id, uint32_t addr, void *val, size_t size)
{
struct fsi_master_hub *hub = to_fsi_master_hub(master);
+ int rc;
if (id != 0)
return -EINVAL;
addr += hub->addr + (link * FSI_HUB_LINK_SIZE);
- return fsi_slave_read(hub->upstream->slave, addr, val, size);
+ rc = fsi_slave_read(hub->upstream->slave, addr, val, size);
+ if (rc)
+ fsi_master_error(master, link);
+
+ return rc;
}
static int hub_master_write(struct fsi_master *master, int link,
uint8_t id, uint32_t addr, const void *val, size_t size)
{
struct fsi_master_hub *hub = to_fsi_master_hub(master);
+ int rc;
if (id != 0)
return -EINVAL;
addr += hub->addr + (link * FSI_HUB_LINK_SIZE);
- return fsi_slave_write(hub->upstream->slave, addr, val, size);
+ rc = fsi_slave_write(hub->upstream->slave, addr, val, size);
+ if (rc)
+ fsi_master_error(master, link);
+
+ return rc;
}
static int hub_master_break(struct fsi_master *master, int link)
--
2.39.3
These parts support 10MHZ.
Signed-off-by: Eddie James <[email protected]>
---
.../dts/aspeed/aspeed-bmc-ibm-everest.dts | 32 +++++++++----------
.../arm/boot/dts/aspeed/ibm-power10-dual.dtsi | 16 +++++-----
.../arm/boot/dts/aspeed/ibm-power10-quad.dtsi | 16 +++++-----
3 files changed, 32 insertions(+), 32 deletions(-)
diff --git a/arch/arm/boot/dts/aspeed/aspeed-bmc-ibm-everest.dts b/arch/arm/boot/dts/aspeed/aspeed-bmc-ibm-everest.dts
index 214b2e6a4c6df..1365dc95f1352 100644
--- a/arch/arm/boot/dts/aspeed/aspeed-bmc-ibm-everest.dts
+++ b/arch/arm/boot/dts/aspeed/aspeed-bmc-ibm-everest.dts
@@ -2797,7 +2797,7 @@ eeprom@0 {
compatible = "atmel,at25";
reg = <0>;
- spi-max-frequency = <1000000>;
+ spi-max-frequency = <10000000>;
};
};
@@ -2813,7 +2813,7 @@ eeprom@0 {
compatible = "atmel,at25";
reg = <0>;
- spi-max-frequency = <1000000>;
+ spi-max-frequency = <10000000>;
};
};
@@ -2830,7 +2830,7 @@ eeprom@0 {
compatible = "atmel,at25";
reg = <0>;
- spi-max-frequency = <1000000>;
+ spi-max-frequency = <10000000>;
};
};
@@ -2847,7 +2847,7 @@ eeprom@0 {
compatible = "atmel,at25";
reg = <0>;
- spi-max-frequency = <1000000>;
+ spi-max-frequency = <10000000>;
};
};
};
@@ -3170,7 +3170,7 @@ eeprom@0 {
compatible = "atmel,at25";
reg = <0>;
- spi-max-frequency = <1000000>;
+ spi-max-frequency = <10000000>;
};
};
@@ -3186,7 +3186,7 @@ eeprom@0 {
compatible = "atmel,at25";
reg = <0>;
- spi-max-frequency = <1000000>;
+ spi-max-frequency = <10000000>;
};
};
@@ -3203,7 +3203,7 @@ eeprom@0 {
compatible = "atmel,at25";
reg = <0>;
- spi-max-frequency = <1000000>;
+ spi-max-frequency = <10000000>;
};
};
@@ -3220,7 +3220,7 @@ eeprom@0 {
compatible = "atmel,at25";
reg = <0>;
- spi-max-frequency = <1000000>;
+ spi-max-frequency = <10000000>;
};
};
};
@@ -3543,7 +3543,7 @@ eeprom@0 {
compatible = "atmel,at25";
reg = <0>;
- spi-max-frequency = <1000000>;
+ spi-max-frequency = <10000000>;
};
};
@@ -3559,7 +3559,7 @@ eeprom@0 {
compatible = "atmel,at25";
reg = <0>;
- spi-max-frequency = <1000000>;
+ spi-max-frequency = <10000000>;
};
};
@@ -3576,7 +3576,7 @@ eeprom@0 {
compatible = "atmel,at25";
reg = <0>;
- spi-max-frequency = <1000000>;
+ spi-max-frequency = <10000000>;
};
};
@@ -3593,7 +3593,7 @@ eeprom@0 {
compatible = "atmel,at25";
reg = <0>;
- spi-max-frequency = <1000000>;
+ spi-max-frequency = <10000000>;
};
};
};
@@ -3916,7 +3916,7 @@ eeprom@0 {
compatible = "atmel,at25";
reg = <0>;
- spi-max-frequency = <1000000>;
+ spi-max-frequency = <10000000>;
};
};
@@ -3932,7 +3932,7 @@ eeprom@0 {
compatible = "atmel,at25";
reg = <0>;
- spi-max-frequency = <1000000>;
+ spi-max-frequency = <10000000>;
};
};
@@ -3949,7 +3949,7 @@ eeprom@0 {
compatible = "atmel,at25";
reg = <0>;
- spi-max-frequency = <1000000>;
+ spi-max-frequency = <10000000>;
};
};
@@ -3966,7 +3966,7 @@ eeprom@0 {
compatible = "atmel,at25";
reg = <0>;
- spi-max-frequency = <1000000>;
+ spi-max-frequency = <10000000>;
};
};
};
diff --git a/arch/arm/boot/dts/aspeed/ibm-power10-dual.dtsi b/arch/arm/boot/dts/aspeed/ibm-power10-dual.dtsi
index 44e48e39e6e96..e11354bb384c2 100644
--- a/arch/arm/boot/dts/aspeed/ibm-power10-dual.dtsi
+++ b/arch/arm/boot/dts/aspeed/ibm-power10-dual.dtsi
@@ -94,7 +94,7 @@ eeprom@0 {
compatible = "atmel,at25";
reg = <0>;
- spi-max-frequency = <1000000>;
+ spi-max-frequency = <10000000>;
};
};
@@ -110,7 +110,7 @@ eeprom@0 {
compatible = "atmel,at25";
reg = <0>;
- spi-max-frequency = <1000000>;
+ spi-max-frequency = <10000000>;
};
};
@@ -127,7 +127,7 @@ eeprom@0 {
compatible = "atmel,at25";
reg = <0>;
- spi-max-frequency = <1000000>;
+ spi-max-frequency = <10000000>;
};
};
@@ -144,7 +144,7 @@ eeprom@0 {
compatible = "atmel,at25";
reg = <0>;
- spi-max-frequency = <1000000>;
+ spi-max-frequency = <10000000>;
};
};
};
@@ -261,7 +261,7 @@ eeprom@0 {
compatible = "atmel,at25";
reg = <0>;
- spi-max-frequency = <1000000>;
+ spi-max-frequency = <10000000>;
};
};
@@ -277,7 +277,7 @@ eeprom@0 {
compatible = "atmel,at25";
reg = <0>;
- spi-max-frequency = <1000000>;
+ spi-max-frequency = <10000000>;
};
};
@@ -294,7 +294,7 @@ eeprom@0 {
compatible = "atmel,at25";
reg = <0>;
- spi-max-frequency = <1000000>;
+ spi-max-frequency = <10000000>;
};
};
@@ -311,7 +311,7 @@ eeprom@0 {
compatible = "atmel,at25";
reg = <0>;
- spi-max-frequency = <1000000>;
+ spi-max-frequency = <10000000>;
};
};
};
diff --git a/arch/arm/boot/dts/aspeed/ibm-power10-quad.dtsi b/arch/arm/boot/dts/aspeed/ibm-power10-quad.dtsi
index 57494c744b5d0..0ffb4ae6423fc 100644
--- a/arch/arm/boot/dts/aspeed/ibm-power10-quad.dtsi
+++ b/arch/arm/boot/dts/aspeed/ibm-power10-quad.dtsi
@@ -744,7 +744,7 @@ eeprom@0 {
compatible = "atmel,at25";
reg = <0>;
- spi-max-frequency = <1000000>;
+ spi-max-frequency = <10000000>;
};
};
@@ -760,7 +760,7 @@ eeprom@0 {
compatible = "atmel,at25";
reg = <0>;
- spi-max-frequency = <1000000>;
+ spi-max-frequency = <10000000>;
};
};
@@ -777,7 +777,7 @@ eeprom@0 {
compatible = "atmel,at25";
reg = <0>;
- spi-max-frequency = <1000000>;
+ spi-max-frequency = <10000000>;
};
};
@@ -794,7 +794,7 @@ eeprom@0 {
compatible = "atmel,at25";
reg = <0>;
- spi-max-frequency = <1000000>;
+ spi-max-frequency = <10000000>;
};
};
};
@@ -1117,7 +1117,7 @@ eeprom@0 {
compatible = "atmel,at25";
reg = <0>;
- spi-max-frequency = <1000000>;
+ spi-max-frequency = <10000000>;
};
};
@@ -1133,7 +1133,7 @@ eeprom@0 {
compatible = "atmel,at25";
reg = <0>;
- spi-max-frequency = <1000000>;
+ spi-max-frequency = <10000000>;
};
};
@@ -1150,7 +1150,7 @@ eeprom@0 {
compatible = "atmel,at25";
reg = <0>;
- spi-max-frequency = <1000000>;
+ spi-max-frequency = <10000000>;
};
};
@@ -1167,7 +1167,7 @@ eeprom@0 {
compatible = "atmel,at25";
reg = <0>;
- spi-max-frequency = <1000000>;
+ spi-max-frequency = <10000000>;
};
};
};
--
2.39.3
Use the new FSI device local bus clock to calculate the proper SPI
clock divider.
Signed-off-by: Eddie James <[email protected]>
Acked-by: Mark Brown <[email protected]>
---
drivers/spi/spi-fsi.c | 33 ++++++++++++++++++++++++++++++---
1 file changed, 30 insertions(+), 3 deletions(-)
diff --git a/drivers/spi/spi-fsi.c b/drivers/spi/spi-fsi.c
index fc9e33be1e0e7..e762690f1a390 100644
--- a/drivers/spi/spi-fsi.c
+++ b/drivers/spi/spi-fsi.c
@@ -40,6 +40,7 @@
#define SPI_FSI_CLOCK_CFG_SCK_RECV_DEL GENMASK_ULL(51, 44)
#define SPI_FSI_CLOCK_CFG_SCK_NO_DEL BIT_ULL(51)
#define SPI_FSI_CLOCK_CFG_SCK_DIV GENMASK_ULL(63, 52)
+#define SPI_FSI_CLOCK_CFG_SCK_DIV_MIN 0x4
#define SPI_FSI_MMAP 0x4
#define SPI_FSI_DATA_TX 0x5
#define SPI_FSI_DATA_RX 0x6
@@ -70,6 +71,7 @@
struct fsi2spi {
struct fsi_device *fsi; /* FSI2SPI CFAM engine device */
struct mutex lock; /* lock access to the device */
+ u32 lbus_freq;
};
struct fsi_spi {
@@ -359,7 +361,7 @@ static int fsi_spi_transfer_data(struct fsi_spi *ctx,
return 0;
}
-static int fsi_spi_transfer_init(struct fsi_spi *ctx)
+static int fsi_spi_transfer_init(struct fsi_spi *ctx, u32 clock_div)
{
int loops = 0;
int rc;
@@ -370,7 +372,7 @@ static int fsi_spi_transfer_init(struct fsi_spi *ctx)
u64 status = 0ULL;
u64 wanted_clock_cfg = SPI_FSI_CLOCK_CFG_ECC_DISABLE |
SPI_FSI_CLOCK_CFG_SCK_NO_DEL |
- FIELD_PREP(SPI_FSI_CLOCK_CFG_SCK_DIV, 19);
+ FIELD_PREP(SPI_FSI_CLOCK_CFG_SCK_DIV, clock_div);
end = jiffies + msecs_to_jiffies(SPI_FSI_TIMEOUT_MS);
do {
@@ -421,6 +423,24 @@ static int fsi_spi_transfer_init(struct fsi_spi *ctx)
return rc;
}
+static u32 fsi_spi_calculate_clock_div(struct fsi2spi *bridge, struct spi_device *dev,
+ struct spi_transfer *transfer)
+{
+ u32 div = 19;
+
+ if (bridge->lbus_freq) {
+ u32 desired_speed_hz = transfer->speed_hz ?: dev->max_speed_hz;
+
+ div = DIV_ROUND_UP(bridge->lbus_freq, desired_speed_hz);
+ if (div < SPI_FSI_CLOCK_CFG_SCK_DIV_MIN)
+ div = SPI_FSI_CLOCK_CFG_SCK_DIV_MIN;
+
+ transfer->effective_speed_hz = bridge->lbus_freq / div;
+ }
+
+ return div;
+}
+
static int fsi_spi_transfer_one_message(struct spi_controller *ctlr,
struct spi_message *mesg)
{
@@ -429,6 +449,7 @@ static int fsi_spi_transfer_one_message(struct spi_controller *ctlr,
unsigned int len;
struct spi_transfer *transfer;
struct fsi_spi *ctx = spi_controller_get_devdata(ctlr);
+ u32 div;
rc = fsi_spi_check_mux(ctx->bridge->fsi, ctx->dev);
if (rc)
@@ -446,7 +467,8 @@ static int fsi_spi_transfer_one_message(struct spi_controller *ctlr,
dev_dbg(ctx->dev, "Start tx of %d bytes.\n", transfer->len);
- rc = fsi_spi_transfer_init(ctx);
+ div = fsi_spi_calculate_clock_div(ctx->bridge, mesg->spi, transfer);
+ rc = fsi_spi_transfer_init(ctx, div);
if (rc < 0)
goto error;
@@ -533,6 +555,7 @@ static int fsi_spi_probe(struct device *dev)
bridge->fsi = fsi;
mutex_init(&bridge->lock);
+ bridge->lbus_freq = fsi_device_local_bus_frequency(fsi);
for_each_available_child_of_node(dev->of_node, np) {
u32 base;
@@ -550,6 +573,10 @@ static int fsi_spi_probe(struct device *dev)
ctlr->dev.of_node = np;
ctlr->num_chipselect = of_get_available_child_count(np) ?: 1;
+ if (bridge->lbus_freq) {
+ ctlr->min_speed_hz = DIV_ROUND_UP(bridge->lbus_freq, 0xfff);
+ ctlr->max_speed_hz = bridge->lbus_freq / SPI_FSI_CLOCK_CFG_SCK_DIV_MIN;
+ }
ctlr->flags = SPI_CONTROLLER_HALF_DUPLEX;
ctlr->max_transfer_size = fsi_spi_max_transfer_size;
ctlr->transfer_one_message = fsi_spi_transfer_one_message;
--
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 213023bc5aec4..96a8f727bc389 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 07ce3b2bc62a3..44e48e39e6e96 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
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 29932037c9866..04aa5cb9b6fad 100644
--- a/drivers/fsi/fsi-master-aspeed.c
+++ b/drivers/fsi/fsi-master-aspeed.c
@@ -122,16 +122,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;
}
@@ -175,13 +176,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)
@@ -204,6 +203,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;
}
@@ -226,19 +226,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 0fff873775f19..7eeecbfec7f09 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
The CFAM ID for the I2CR should be the same as the Odyssey chip.
Signed-off-by: Eddie James <[email protected]>
---
drivers/fsi/fsi-master-i2cr.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/fsi/fsi-master-i2cr.c b/drivers/fsi/fsi-master-i2cr.c
index 40f1f4d231e52..c032ba7938815 100644
--- a/drivers/fsi/fsi-master-i2cr.c
+++ b/drivers/fsi/fsi-master-i2cr.c
@@ -22,7 +22,7 @@
#define I2CR_LOG_CMD 0x60008
static const u8 i2cr_cfam[] = {
- 0xc0, 0x02, 0x0d, 0xa6,
+ 0xc0, 0x01, 0x0c, 0x07,
0x80, 0x01, 0x10, 0x02,
0x80, 0x01, 0x10, 0x02,
0x80, 0x01, 0x10, 0x02,
--
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 614d830419bb8..f8d9bc178ef5b 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;
@@ -706,6 +887,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 0000000000000..ac49ae9be356b
--- /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 [%d] status:%08x", __entry->bus, __entry->msg_idx, __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 [%d] command:%08x", __entry->bus, __entry->msg_idx, __entry->command)
+);
+
+#endif
+
+#include <trace/define_trace.h>
--
2.39.3
On Wed, 2024-06-05 at 16:22 -0500, Eddie James wrote:
> Eddie James (40):
...
>
> .../dts/aspeed/aspeed-bmc-ibm-everest.dts | 32 +-
> .../boot/dts/aspeed/aspeed-bmc-opp-tacoma.dts | 1 +
> .../arm/boot/dts/aspeed/ibm-power10-dual.dtsi | 17 +-
> .../arm/boot/dts/aspeed/ibm-power10-quad.dtsi | 16 +-
> drivers/fsi/Kconfig | 2 +
> drivers/fsi/fsi-core.c | 888 +++++++++++++++---
> drivers/fsi/fsi-master-aspeed.c | 431 +++++----
> drivers/fsi/fsi-master-hub.c | 244 ++---
> drivers/fsi/fsi-master-i2cr.c | 2 +-
> drivers/fsi/fsi-master.h | 33 +
> drivers/fsi/fsi-slave.h | 117 +++
> drivers/i2c/busses/i2c-fsi.c | 463 ++++++---
> drivers/spi/spi-fsi.c | 33 +-
> include/linux/fsi.h | 3 +
> include/trace/events/fsi.h | 171 ++--
> include/trace/events/fsi_master_aspeed.h | 86 +-
> include/trace/events/i2c_fsi.h | 45 +
> 17 files changed, 1897 insertions(+), 687 deletions(-)
> create mode 100644 include/trace/events/i2c_fsi.h
That's a lot of patches, that span the trees of several maintainers.
What's your expectation for those who should be merging work in this
combined series? Have you had any feedback in that regard?
I'm asking because I need to make a call on what I do with respect to
the Aspeed devicetrees. I think it would clarify responsibility if this
series were split by subsystem. That way I can apply the devicetree
patches and the rest can go through their respective trees.
If there are dependencies that require merging all or none, then it
would be helpful if they were outlined in the cover letter. Even then,
merging the leaves and waiting a cycle might make everyone's lives
easier?
Andrew