Commit 22884cf8 authored by Greg Kroah-Hartman's avatar Greg Kroah-Hartman
Browse files

Merge tag 'fsi-for-v6.6' of...

Merge tag 'fsi-for-v6.6' of git://git.kernel.org/pub/scm/linux/kernel/git/joel/fsi into char-misc-next

Joen writes:

FSI changes for v6.6

 * New drivers for the I2C Responder master and SCOM device

 * Misc janitor fixes

* tag 'fsi-for-v6.6' of git://git.kernel.org/pub/scm/linux/kernel/git/joel/fsi:
  fsi: fix some spelling mistakes in comment
  fsi: master-ast-cf: Add MODULE_FIRMWARE macro
  docs: ABI: fix spelling/grammar in SBEFIFO timeout interface
  fsi: Add I2C Responder SCOM driver
  fsi: Add IBM I2C Responder virtual FSI master
  dt-bindings: fsi: Document the IBM I2C Responder virtual FSI master
  fsi: Lock mutex for master device registration
  fsi: Improve master indexing
  fsi: core: Switch to ida_alloc/free
  fsi: core: Fix legacy minor numbering
  fsi: core: Add trace events for scan and unregister
  fsi: aspeed: Reset master errors after CFAM reset
  fsi: sbefifo: Remove limits on user-specified read timeout
  fsi: sbefifo: Add configurable in-command timeout
  fsi: sbefifo: Don't check status during probe
  fsi: Use of_match_table for bus matching if specified
  fsi: Add aliased device numbering
  fsi: Move fsi_slave structure definition to header
  fsi: Use of_property_read_reg() to parse "reg"
  fsi: Explicitly include correct DT includes
parents 43fbd506 f04d61a3
Loading
Loading
Loading
Loading
+3 −3
Original line number Diff line number Diff line
@@ -5,6 +5,6 @@ Description:
		Indicates whether or not this SBE device has experienced a
		timeout; i.e. the SBE did not respond within the time allotted
		by the driver. A value of 1 indicates that a timeout has
		ocurred and no transfers have completed since the timeout. A
		value of 0 indicates that no timeout has ocurred, or if one
		has, more recent transfers have completed successful.
		occurred and no transfers have completed since the timeout. A
		value of 0 indicates that no timeout has occurred, or if one
		has, more recent transfers have completed successfully.
+41 −0
Original line number Diff line number Diff line
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/fsi/ibm,i2cr-fsi-master.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#

title: IBM I2C Responder virtual FSI master

maintainers:
  - Eddie James <eajames@linux.ibm.com>

description: |
  The I2C Responder (I2CR) is a an I2C device that's connected to an FSI CFAM
  (see fsi.txt). The I2CR translates I2C bus operations to FSI CFAM reads and
  writes or SCOM operations, thereby acting as an FSI master.

properties:
  compatible:
    enum:
      - ibm,i2cr-fsi-master

  reg:
    maxItems: 1

required:
  - compatible
  - reg

additionalProperties: false

examples:
  - |
    i2c {
      #address-cells = <1>;
      #size-cells = <0>;

      i2cr@20 {
        compatible = "ibm,i2cr-fsi-master";
        reg = <0x20>;
      };
    };
+17 −0
Original line number Diff line number Diff line
@@ -62,6 +62,15 @@ config FSI_MASTER_ASPEED

	 Enable it for your BMC kernel in an OpenPower or IBM Power system.

config FSI_MASTER_I2CR
	tristate "IBM I2C Responder virtual FSI master"
	depends on I2C
	help
	  This option enables a virtual FSI master in order to access a CFAM
	  behind an IBM I2C Responder (I2CR) chip. The I2CR is an I2C device
	  that translates I2C commands to CFAM or SCOM operations, effectively
	  implementing an FSI master and bus.

config FSI_SCOM
	tristate "SCOM FSI client device driver"
	help
@@ -85,4 +94,12 @@ config FSI_OCC
	provide the raw sensor data as well as perform thermal and power
	management on the system.

config I2CR_SCOM
	tristate "IBM I2C Responder SCOM driver"
	depends on FSI_MASTER_I2CR
	help
	  This option enables an I2C Responder based SCOM device driver. The
	  I2CR has the capability to directly perform SCOM operations instead
	  of using the FSI2PIB engine.

endif
+2 −0
Original line number Diff line number Diff line
@@ -4,7 +4,9 @@ obj-$(CONFIG_FSI) += fsi-core.o
obj-$(CONFIG_FSI_MASTER_HUB) += fsi-master-hub.o
obj-$(CONFIG_FSI_MASTER_ASPEED) += fsi-master-aspeed.o
obj-$(CONFIG_FSI_MASTER_GPIO) += fsi-master-gpio.o
obj-$(CONFIG_FSI_MASTER_I2CR) += fsi-master-i2cr.o
obj-$(CONFIG_FSI_MASTER_AST_CF) += fsi-master-ast-cf.o
obj-$(CONFIG_FSI_SCOM) += fsi-scom.o
obj-$(CONFIG_FSI_SBEFIFO) += fsi-sbefifo.o
obj-$(CONFIG_FSI_OCC) += fsi-occ.o
obj-$(CONFIG_I2CR_SCOM) += i2cr-scom.o
+84 −70
Original line number Diff line number Diff line
@@ -16,6 +16,8 @@
#include <linux/idr.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_device.h>
#include <linux/slab.h>
#include <linux/bitops.h>
#include <linux/cdev.h>
@@ -23,6 +25,10 @@
#include <linux/uaccess.h>

#include "fsi-master.h"
#include "fsi-slave.h"

#define CREATE_TRACE_POINTS
#include <trace/events/fsi.h>

#define FSI_SLAVE_CONF_NEXT_MASK	GENMASK(31, 31)
#define FSI_SLAVE_CONF_SLOTS_MASK	GENMASK(23, 16)
@@ -78,26 +84,6 @@ static const int engine_page_size = 0x400;

static DEFINE_IDA(master_ida);

struct fsi_slave {
	struct device		dev;
	struct fsi_master	*master;
	struct cdev		cdev;
	int			cdev_idx;
	int			id;	/* FSI address */
	int			link;	/* FSI link# */
	u32			cfam_id;
	int			chip_id;
	uint32_t		size;	/* size of slave address space */
	u8			t_send_delay;
	u8			t_echo_delay;
};

#define CREATE_TRACE_POINTS
#include <trace/events/fsi.h>

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

static const int slave_retries = 2;
static int discard_errors;

@@ -415,28 +401,18 @@ EXPORT_SYMBOL_GPL(fsi_slave_release_range);
static bool fsi_device_node_matches(struct device *dev, struct device_node *np,
		uint32_t addr, uint32_t size)
{
	unsigned int len, na, ns;
	const __be32 *prop;
	uint32_t psize;

	na = of_n_addr_cells(np);
	ns = of_n_size_cells(np);
	u64 paddr, psize;

	if (na != 1 || ns != 1)
	if (of_property_read_reg(np, 0, &paddr, &psize))
		return false;

	prop = of_get_property(np, "reg", &len);
	if (!prop || len != 8)
	if (paddr != addr)
		return false;

	if (of_read_number(prop, 1) != addr)
		return false;

	psize = of_read_number(prop + 1, 1);
	if (psize != size) {
		dev_warn(dev,
			"node %s matches probed address, but not size (got 0x%x, expected 0x%x)",
			of_node_full_name(np), psize, size);
			"node %pOF matches probed address, but not size (got 0x%llx, expected 0x%x)",
			np, psize, size);
	}

	return true;
@@ -653,24 +629,12 @@ static void fsi_slave_release(struct device *dev)
static bool fsi_slave_node_matches(struct device_node *np,
		int link, uint8_t id)
{
	unsigned int len, na, ns;
	const __be32 *prop;

	na = of_n_addr_cells(np);
	ns = of_n_size_cells(np);
	u64 addr;

	/* Ensure we have the correct format for addresses and sizes in
	 * reg properties
	 */
	if (na != 2 || ns != 0)
		return false;

	prop = of_get_property(np, "reg", &len);
	if (!prop || len != 8)
	if (of_property_read_reg(np, 0, &addr, NULL))
		return false;

	return (of_read_number(prop, 1) == link) &&
		(of_read_number(prop + 1, 1) == id);
	return addr == (((u64)link << 32) | id);
}

/* Find a matching node for the slave at (link, id). Returns NULL if none
@@ -949,9 +913,13 @@ static int __fsi_get_new_minor(struct fsi_slave *slave, enum fsi_dev_type type,

	/* Check if we qualify for legacy numbering */
	if (cid >= 0 && cid < 16 && type < 4) {
		/* Try reserving the legacy number */
		id = (cid << 4) | type;
		id = ida_simple_get(&fsi_minor_ida, id, id + 1, GFP_KERNEL);
		/*
		 * Try reserving the legacy number, which has 0 - 0x3f reserved
		 * in the ida range. cid goes up to 0xf and type contains two
		 * bits, so construct the id with the below two bit shift.
		 */
		id = (cid << 2) | type;
		id = ida_alloc_range(&fsi_minor_ida, id, id, GFP_KERNEL);
		if (id >= 0) {
			*out_index = fsi_adjust_index(cid);
			*out_dev = fsi_base_dev + id;
@@ -962,8 +930,8 @@ static int __fsi_get_new_minor(struct fsi_slave *slave, enum fsi_dev_type type,
			return id;
		/* Fallback to non-legacy allocation */
	}
	id = ida_simple_get(&fsi_minor_ida, FSI_CHAR_LEGACY_TOP,
			    FSI_CHAR_MAX_DEVICES, GFP_KERNEL);
	id = ida_alloc_range(&fsi_minor_ida, FSI_CHAR_LEGACY_TOP,
			     FSI_CHAR_MAX_DEVICES - 1, GFP_KERNEL);
	if (id < 0)
		return id;
	*out_index = fsi_adjust_index(id);
@@ -971,16 +939,42 @@ 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);
}
EXPORT_SYMBOL_GPL(fsi_get_new_minor);

void fsi_free_minor(dev_t dev)
{
	ida_simple_remove(&fsi_minor_ida, MINOR(dev));
	ida_free(&fsi_minor_ida, MINOR(dev));
}
EXPORT_SYMBOL_GPL(fsi_free_minor);

@@ -1210,6 +1204,7 @@ static int fsi_master_scan(struct fsi_master *master)
{
	int link, rc;

	trace_fsi_master_scan(master, true);
	for (link = 0; link < master->n_links; link++) {
		rc = fsi_master_link_enable(master, link);
		if (rc) {
@@ -1251,6 +1246,7 @@ static int fsi_master_remove_slave(struct device *dev, void *arg)

static void fsi_master_unscan(struct fsi_master *master)
{
	trace_fsi_master_scan(master, false);
	device_for_each_child(&master->dev, NULL, fsi_master_remove_slave);
}

@@ -1313,41 +1309,53 @@ int fsi_master_register(struct fsi_master *master)
	struct device_node *np;

	mutex_init(&master->scan_lock);
	master->idx = ida_simple_get(&master_ida, 0, INT_MAX, GFP_KERNEL);

	/* Alloc the requested index if it's non-zero */
	if (master->idx) {
		master->idx = ida_alloc_range(&master_ida, master->idx,
					      master->idx, GFP_KERNEL);
	} else {
		master->idx = ida_alloc(&master_ida, GFP_KERNEL);
	}

	if (master->idx < 0)
		return master->idx;

	if (!dev_name(&master->dev))
		dev_set_name(&master->dev, "fsi%d", master->idx);

	master->dev.class = &fsi_master_class;

	mutex_lock(&master->scan_lock);
	rc = device_register(&master->dev);
	if (rc) {
		ida_simple_remove(&master_ida, master->idx);
		return rc;
		ida_free(&master_ida, master->idx);
		goto out;
	}

	np = dev_of_node(&master->dev);
	if (!of_property_read_bool(np, "no-scan-on-init")) {
		mutex_lock(&master->scan_lock);
		fsi_master_scan(master);
		mutex_unlock(&master->scan_lock);
	}

	return 0;
out:
	mutex_unlock(&master->scan_lock);
	return rc;
}
EXPORT_SYMBOL_GPL(fsi_master_register);

void fsi_master_unregister(struct fsi_master *master)
{
	if (master->idx >= 0) {
		ida_simple_remove(&master_ida, master->idx);
		master->idx = -1;
	}
	int idx = master->idx;

	trace_fsi_master_unregister(master);

	mutex_lock(&master->scan_lock);
	fsi_master_unscan(master);
	master->n_links = 0;
	mutex_unlock(&master->scan_lock);

	device_unregister(&master->dev);
	ida_free(&master_ida, idx);
}
EXPORT_SYMBOL_GPL(fsi_master_unregister);

@@ -1366,8 +1374,14 @@ static int fsi_bus_match(struct device *dev, struct device_driver *drv)
		if (id->engine_type != fsi_dev->engine_type)
			continue;
		if (id->version == FSI_VERSION_ANY ||
				id->version == fsi_dev->version)
		    id->version == fsi_dev->version) {
			if (drv->of_match_table) {
				if (of_driver_match_device(dev, drv))
					return 1;
			} else {
				return 1;
			}
		}
	}

	return 0;
Loading