mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-09-26 04:16:39 +00:00
FSI changes for v6.6
* New drivers for the I2C Responder master and SCOM device * Misc janitor fixes -----BEGIN PGP SIGNATURE----- iQIzBAABCAAdFiEE+nHMAt9PCBDH63wBa3ZZB4FHcJ4FAmTV7UQACgkQa3ZZB4FH cJ4Mbg//fI3gE56QIsMYNu2YfJb8ZFFH5xD/xn9xwAv34i/XudhX6eCigEHl420Z B/7yPyDePBK8f+Phb9pL1VCovnwdsG/7j3BMtVRV5172bGgkUGpIyNIS7K47/yhJ wRs9qmQsHu3PbKKzStdxxgOOZxiMZc8KzZjDYVBcWmX9RBTe6C4n8WpHbcvI3sIU YTdbfSOyZQIlurJuEtwa7FFOAWUTD66gvvpDWZZn1Ns0hh4WsNWQ0mIqVxVSr8Rl +hISZfBopZMAvMVt8LHvldBUUVAcb9z0KdsAuEe+DXv0uzkTN4FGxFD5YEloGnzR o0eYc7M6vEvH46qhLKYIXRLgLD+SM2+RRhMNaOhxbdVsw7+PWDUz7OWIsoPHmExP 9cmJhZKg/l2JlVPxh+6NOjx2ygK1h0vkADZWc8UoiZD9ZHXFzEYTjuRvaa4YH9HN ENOh3S+9i2Zrue0EWNBSIEaKvKG6h63cUoZSSU4AMtMwC1H5JPKOyYtC+8+oLFsw HmRjxls5zdOLcK9JwPR80xSD/CRadT88qpkvRyFfWyuHenazJzo2HHCgrbVgDXq0 wvR2/c2MWqhZ94LASnd4gpr7MicS1G5FiZn+csKP2dy2Pv4nyDYkOOmrXw8Q/bGZ X88Ohipm9imC0LVIRvXusrmKUUp5dzpQePaQoAaC8/KH8PqRC0U= =FLoZ -----END PGP SIGNATURE----- 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
This commit is contained in:
commit
22884cf84c
20 changed files with 878 additions and 99 deletions
|
@ -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.
|
||||
|
|
|
@ -0,0 +1,41 @@
|
|||
# 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>;
|
||||
};
|
||||
};
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
u64 paddr, psize;
|
||||
|
||||
na = of_n_addr_cells(np);
|
||||
ns = of_n_size_cells(np);
|
||||
|
||||
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;
|
||||
u64 addr;
|
||||
|
||||
na = of_n_addr_cells(np);
|
||||
ns = of_n_size_cells(np);
|
||||
|
||||
/* Ensure we have the correct format for addresses and sizes in
|
||||
* reg properties
|
||||
*/
|
||||
if (na != 2 || ns != 0)
|
||||
if (of_property_read_reg(np, 0, &addr, NULL))
|
||||
return false;
|
||||
|
||||
prop = of_get_property(np, "reg", &len);
|
||||
if (!prop || len != 8)
|
||||
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;
|
||||
|
||||
dev_set_name(&master->dev, "fsi%d", 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)
|
||||
return 1;
|
||||
id->version == fsi_dev->version) {
|
||||
if (drv->of_match_table) {
|
||||
if (of_driver_match_device(dev, drv))
|
||||
return 1;
|
||||
} else {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
|
|
@ -376,7 +376,7 @@ static int aspeed_master_break(struct fsi_master *master, int link)
|
|||
static void aspeed_master_release(struct device *dev)
|
||||
{
|
||||
struct fsi_master_aspeed *aspeed =
|
||||
to_fsi_master_aspeed(dev_to_fsi_master(dev));
|
||||
to_fsi_master_aspeed(to_fsi_master(dev));
|
||||
|
||||
kfree(aspeed);
|
||||
}
|
||||
|
@ -454,6 +454,8 @@ static ssize_t cfam_reset_store(struct device *dev, struct device_attribute *att
|
|||
gpiod_set_value(aspeed->cfam_reset_gpio, 1);
|
||||
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));
|
||||
mutex_unlock(&aspeed->lock);
|
||||
trace_fsi_master_aspeed_cfam_reset(false);
|
||||
|
||||
|
|
|
@ -1133,7 +1133,7 @@ static int fsi_master_acf_gpio_request(void *data)
|
|||
|
||||
/* Note: This doesn't require holding out mutex */
|
||||
|
||||
/* Write reqest */
|
||||
/* Write request */
|
||||
iowrite8(ARB_ARM_REQ, master->sram + ARB_REG);
|
||||
|
||||
/*
|
||||
|
@ -1190,7 +1190,7 @@ static int fsi_master_acf_gpio_release(void *data)
|
|||
|
||||
static void fsi_master_acf_release(struct device *dev)
|
||||
{
|
||||
struct fsi_master_acf *master = to_fsi_master_acf(dev_to_fsi_master(dev));
|
||||
struct fsi_master_acf *master = to_fsi_master_acf(to_fsi_master(dev));
|
||||
|
||||
/* Cleanup, stop coprocessor */
|
||||
mutex_lock(&master->lock);
|
||||
|
@ -1441,3 +1441,4 @@ static struct platform_driver fsi_master_acf = {
|
|||
|
||||
module_platform_driver(fsi_master_acf);
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_FIRMWARE(FW_FILE_NAME);
|
||||
|
|
|
@ -761,7 +761,7 @@ static DEVICE_ATTR(external_mode, 0664,
|
|||
|
||||
static void fsi_master_gpio_release(struct device *dev)
|
||||
{
|
||||
struct fsi_master_gpio *master = to_fsi_master_gpio(dev_to_fsi_master(dev));
|
||||
struct fsi_master_gpio *master = to_fsi_master_gpio(to_fsi_master(dev));
|
||||
|
||||
of_node_put(dev_of_node(master->dev));
|
||||
|
||||
|
|
|
@ -105,7 +105,7 @@ static int hub_master_link_enable(struct fsi_master *master, int link,
|
|||
|
||||
static void hub_master_release(struct device *dev)
|
||||
{
|
||||
struct fsi_master_hub *hub = to_fsi_master_hub(dev_to_fsi_master(dev));
|
||||
struct fsi_master_hub *hub = to_fsi_master_hub(to_fsi_master(dev));
|
||||
|
||||
kfree(hub);
|
||||
}
|
||||
|
|
316
drivers/fsi/fsi-master-i2cr.c
Normal file
316
drivers/fsi/fsi-master-i2cr.c
Normal file
|
@ -0,0 +1,316 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/* Copyright (C) IBM Corporation 2023 */
|
||||
|
||||
#include <linux/device.h>
|
||||
#include <linux/fsi.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mod_devicetable.h>
|
||||
#include <linux/mutex.h>
|
||||
|
||||
#include "fsi-master-i2cr.h"
|
||||
|
||||
#define CREATE_TRACE_POINTS
|
||||
#include <trace/events/fsi_master_i2cr.h>
|
||||
|
||||
#define I2CR_ADDRESS_CFAM(a) ((a) >> 2)
|
||||
#define I2CR_INITIAL_PARITY true
|
||||
|
||||
#define I2CR_STATUS_CMD 0x60002
|
||||
#define I2CR_STATUS_ERR BIT_ULL(61)
|
||||
#define I2CR_ERROR_CMD 0x60004
|
||||
#define I2CR_LOG_CMD 0x60008
|
||||
|
||||
static const u8 i2cr_cfam[] = {
|
||||
0xc0, 0x02, 0x0d, 0xa6,
|
||||
0x80, 0x01, 0x10, 0x02,
|
||||
0x80, 0x01, 0x10, 0x02,
|
||||
0x80, 0x01, 0x10, 0x02,
|
||||
0x80, 0x01, 0x80, 0x52,
|
||||
0x80, 0x01, 0x10, 0x02,
|
||||
0x80, 0x01, 0x10, 0x02,
|
||||
0x80, 0x01, 0x10, 0x02,
|
||||
0x80, 0x01, 0x10, 0x02,
|
||||
0x80, 0x01, 0x22, 0x2d,
|
||||
0x00, 0x00, 0x00, 0x00,
|
||||
0xde, 0xad, 0xc0, 0xde
|
||||
};
|
||||
|
||||
static bool i2cr_check_parity32(u32 v, bool parity)
|
||||
{
|
||||
u32 i;
|
||||
|
||||
for (i = 0; i < 32; ++i) {
|
||||
if (v & (1u << i))
|
||||
parity = !parity;
|
||||
}
|
||||
|
||||
return parity;
|
||||
}
|
||||
|
||||
static bool i2cr_check_parity64(u64 v)
|
||||
{
|
||||
u32 i;
|
||||
bool parity = I2CR_INITIAL_PARITY;
|
||||
|
||||
for (i = 0; i < 64; ++i) {
|
||||
if (v & (1llu << i))
|
||||
parity = !parity;
|
||||
}
|
||||
|
||||
return parity;
|
||||
}
|
||||
|
||||
static u32 i2cr_get_command(u32 address, bool parity)
|
||||
{
|
||||
address <<= 1;
|
||||
|
||||
if (i2cr_check_parity32(address, parity))
|
||||
address |= 1;
|
||||
|
||||
return address;
|
||||
}
|
||||
|
||||
static int i2cr_transfer(struct i2c_client *client, u32 command, u64 *data)
|
||||
{
|
||||
struct i2c_msg msgs[2];
|
||||
int ret;
|
||||
|
||||
msgs[0].addr = client->addr;
|
||||
msgs[0].flags = 0;
|
||||
msgs[0].len = sizeof(command);
|
||||
msgs[0].buf = (__u8 *)&command;
|
||||
msgs[1].addr = client->addr;
|
||||
msgs[1].flags = I2C_M_RD;
|
||||
msgs[1].len = sizeof(*data);
|
||||
msgs[1].buf = (__u8 *)data;
|
||||
|
||||
ret = i2c_transfer(client->adapter, msgs, 2);
|
||||
if (ret == 2)
|
||||
return 0;
|
||||
|
||||
trace_i2cr_i2c_error(client, command, ret);
|
||||
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
static int i2cr_check_status(struct i2c_client *client)
|
||||
{
|
||||
u64 status;
|
||||
int ret;
|
||||
|
||||
ret = i2cr_transfer(client, I2CR_STATUS_CMD, &status);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (status & I2CR_STATUS_ERR) {
|
||||
u32 buf[3] = { 0, 0, 0 };
|
||||
u64 error;
|
||||
u64 log;
|
||||
|
||||
i2cr_transfer(client, I2CR_ERROR_CMD, &error);
|
||||
i2cr_transfer(client, I2CR_LOG_CMD, &log);
|
||||
|
||||
trace_i2cr_status_error(client, status, error, log);
|
||||
|
||||
buf[0] = I2CR_STATUS_CMD;
|
||||
i2c_master_send(client, (const char *)buf, sizeof(buf));
|
||||
|
||||
buf[0] = I2CR_ERROR_CMD;
|
||||
i2c_master_send(client, (const char *)buf, sizeof(buf));
|
||||
|
||||
buf[0] = I2CR_LOG_CMD;
|
||||
i2c_master_send(client, (const char *)buf, sizeof(buf));
|
||||
|
||||
dev_err(&client->dev, "status:%016llx error:%016llx log:%016llx\n", status, error,
|
||||
log);
|
||||
return -EREMOTEIO;
|
||||
}
|
||||
|
||||
trace_i2cr_status(client, status);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int fsi_master_i2cr_read(struct fsi_master_i2cr *i2cr, u32 addr, u64 *data)
|
||||
{
|
||||
u32 command = i2cr_get_command(addr, I2CR_INITIAL_PARITY);
|
||||
int ret;
|
||||
|
||||
mutex_lock(&i2cr->lock);
|
||||
|
||||
ret = i2cr_transfer(i2cr->client, command, data);
|
||||
if (ret)
|
||||
goto unlock;
|
||||
|
||||
ret = i2cr_check_status(i2cr->client);
|
||||
if (ret)
|
||||
goto unlock;
|
||||
|
||||
trace_i2cr_read(i2cr->client, command, data);
|
||||
|
||||
unlock:
|
||||
mutex_unlock(&i2cr->lock);
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(fsi_master_i2cr_read);
|
||||
|
||||
int fsi_master_i2cr_write(struct fsi_master_i2cr *i2cr, u32 addr, u64 data)
|
||||
{
|
||||
u32 buf[3] = { 0 };
|
||||
int ret;
|
||||
|
||||
buf[0] = i2cr_get_command(addr, i2cr_check_parity64(data));
|
||||
memcpy(&buf[1], &data, sizeof(data));
|
||||
|
||||
mutex_lock(&i2cr->lock);
|
||||
|
||||
ret = i2c_master_send(i2cr->client, (const char *)buf, sizeof(buf));
|
||||
if (ret == sizeof(buf)) {
|
||||
ret = i2cr_check_status(i2cr->client);
|
||||
if (!ret)
|
||||
trace_i2cr_write(i2cr->client, buf[0], data);
|
||||
} else {
|
||||
trace_i2cr_i2c_error(i2cr->client, buf[0], ret);
|
||||
|
||||
if (ret >= 0)
|
||||
ret = -EIO;
|
||||
}
|
||||
|
||||
mutex_unlock(&i2cr->lock);
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(fsi_master_i2cr_write);
|
||||
|
||||
static int i2cr_read(struct fsi_master *master, int link, uint8_t id, uint32_t addr, void *val,
|
||||
size_t size)
|
||||
{
|
||||
struct fsi_master_i2cr *i2cr = container_of(master, struct fsi_master_i2cr, master);
|
||||
u64 data;
|
||||
size_t i;
|
||||
int ret;
|
||||
|
||||
if (link || id || (addr & 0xffff0000) || !(size == 1 || size == 2 || size == 4))
|
||||
return -EINVAL;
|
||||
|
||||
/*
|
||||
* The I2CR doesn't have CFAM or FSI slave address space - only the
|
||||
* engines. In order for this to work with the FSI core, we need to
|
||||
* emulate at minimum the CFAM config table so that the appropriate
|
||||
* engines are discovered.
|
||||
*/
|
||||
if (addr < 0xc00) {
|
||||
if (addr > sizeof(i2cr_cfam) - 4)
|
||||
addr = (addr & 0x3) + (sizeof(i2cr_cfam) - 4);
|
||||
|
||||
memcpy(val, &i2cr_cfam[addr], size);
|
||||
return 0;
|
||||
}
|
||||
|
||||
ret = fsi_master_i2cr_read(i2cr, I2CR_ADDRESS_CFAM(addr), &data);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/*
|
||||
* FSI core expects up to 4 bytes BE back, while I2CR replied with LE
|
||||
* bytes on the wire.
|
||||
*/
|
||||
for (i = 0; i < size; ++i)
|
||||
((u8 *)val)[i] = ((u8 *)&data)[7 - i];
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int i2cr_write(struct fsi_master *master, int link, uint8_t id, uint32_t addr,
|
||||
const void *val, size_t size)
|
||||
{
|
||||
struct fsi_master_i2cr *i2cr = container_of(master, struct fsi_master_i2cr, master);
|
||||
u64 data = 0;
|
||||
size_t i;
|
||||
|
||||
if (link || id || (addr & 0xffff0000) || !(size == 1 || size == 2 || size == 4))
|
||||
return -EINVAL;
|
||||
|
||||
/* I2CR writes to CFAM or FSI slave address are a successful no-op. */
|
||||
if (addr < 0xc00)
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* FSI core passes up to 4 bytes BE, while the I2CR expects LE bytes on
|
||||
* the wire.
|
||||
*/
|
||||
for (i = 0; i < size; ++i)
|
||||
((u8 *)&data)[7 - i] = ((u8 *)val)[i];
|
||||
|
||||
return fsi_master_i2cr_write(i2cr, I2CR_ADDRESS_CFAM(addr), data);
|
||||
}
|
||||
|
||||
static void i2cr_release(struct device *dev)
|
||||
{
|
||||
struct fsi_master_i2cr *i2cr = to_fsi_master_i2cr(to_fsi_master(dev));
|
||||
|
||||
of_node_put(dev->of_node);
|
||||
|
||||
kfree(i2cr);
|
||||
}
|
||||
|
||||
static int i2cr_probe(struct i2c_client *client)
|
||||
{
|
||||
struct fsi_master_i2cr *i2cr;
|
||||
int ret;
|
||||
|
||||
i2cr = kzalloc(sizeof(*i2cr), GFP_KERNEL);
|
||||
if (!i2cr)
|
||||
return -ENOMEM;
|
||||
|
||||
/* Only one I2CR on any given I2C bus (fixed I2C device address) */
|
||||
i2cr->master.idx = client->adapter->nr;
|
||||
dev_set_name(&i2cr->master.dev, "i2cr%d", i2cr->master.idx);
|
||||
i2cr->master.dev.parent = &client->dev;
|
||||
i2cr->master.dev.of_node = of_node_get(dev_of_node(&client->dev));
|
||||
i2cr->master.dev.release = i2cr_release;
|
||||
|
||||
i2cr->master.n_links = 1;
|
||||
i2cr->master.read = i2cr_read;
|
||||
i2cr->master.write = i2cr_write;
|
||||
|
||||
mutex_init(&i2cr->lock);
|
||||
i2cr->client = client;
|
||||
|
||||
ret = fsi_master_register(&i2cr->master);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
i2c_set_clientdata(client, i2cr);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void i2cr_remove(struct i2c_client *client)
|
||||
{
|
||||
struct fsi_master_i2cr *i2cr = i2c_get_clientdata(client);
|
||||
|
||||
fsi_master_unregister(&i2cr->master);
|
||||
}
|
||||
|
||||
static const struct of_device_id i2cr_ids[] = {
|
||||
{ .compatible = "ibm,i2cr-fsi-master" },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, i2cr_ids);
|
||||
|
||||
static struct i2c_driver i2cr_driver = {
|
||||
.probe_new = i2cr_probe,
|
||||
.remove = i2cr_remove,
|
||||
.driver = {
|
||||
.name = "fsi-master-i2cr",
|
||||
.of_match_table = i2cr_ids,
|
||||
},
|
||||
};
|
||||
|
||||
module_i2c_driver(i2cr_driver)
|
||||
|
||||
MODULE_AUTHOR("Eddie James <eajames@linux.ibm.com>");
|
||||
MODULE_DESCRIPTION("IBM I2C Responder virtual FSI master driver");
|
||||
MODULE_LICENSE("GPL");
|
33
drivers/fsi/fsi-master-i2cr.h
Normal file
33
drivers/fsi/fsi-master-i2cr.h
Normal file
|
@ -0,0 +1,33 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/* Copyright (C) IBM Corporation 2023 */
|
||||
|
||||
#ifndef DRIVERS_FSI_MASTER_I2CR_H
|
||||
#define DRIVERS_FSI_MASTER_I2CR_H
|
||||
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/mutex.h>
|
||||
|
||||
#include "fsi-master.h"
|
||||
|
||||
struct i2c_client;
|
||||
|
||||
struct fsi_master_i2cr {
|
||||
struct fsi_master master;
|
||||
struct mutex lock; /* protect HW access */
|
||||
struct i2c_client *client;
|
||||
};
|
||||
|
||||
#define to_fsi_master_i2cr(m) container_of(m, struct fsi_master_i2cr, master)
|
||||
|
||||
int fsi_master_i2cr_read(struct fsi_master_i2cr *i2cr, u32 addr, u64 *data);
|
||||
int fsi_master_i2cr_write(struct fsi_master_i2cr *i2cr, u32 addr, u64 data);
|
||||
|
||||
static inline bool is_fsi_master_i2cr(struct fsi_master *master)
|
||||
{
|
||||
if (master->dev.parent && master->dev.parent->type == &i2c_client_type)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
#endif /* DRIVERS_FSI_MASTER_I2CR_H */
|
|
@ -136,7 +136,7 @@ struct fsi_master {
|
|||
u8 t_send_delay, u8 t_echo_delay);
|
||||
};
|
||||
|
||||
#define dev_to_fsi_master(d) container_of(d, struct fsi_master, dev)
|
||||
#define to_fsi_master(d) container_of(d, struct fsi_master, dev)
|
||||
|
||||
/**
|
||||
* fsi_master registration & lifetime: the fsi_master_register() and
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
#include <linux/mutex.h>
|
||||
#include <linux/fsi-occ.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/slab.h>
|
||||
|
|
|
@ -22,8 +22,8 @@
|
|||
#include <linux/module.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/uaccess.h>
|
||||
|
@ -81,7 +81,7 @@
|
|||
|
||||
enum sbe_state
|
||||
{
|
||||
SBE_STATE_UNKNOWN = 0x0, // Unkown, initial state
|
||||
SBE_STATE_UNKNOWN = 0x0, // Unknown, initial state
|
||||
SBE_STATE_IPLING = 0x1, // IPL'ing - autonomous mode (transient)
|
||||
SBE_STATE_ISTEP = 0x2, // ISTEP - Running IPL by steps (transient)
|
||||
SBE_STATE_MPIPL = 0x3, // MPIPL
|
||||
|
@ -127,6 +127,7 @@ struct sbefifo {
|
|||
bool dead;
|
||||
bool async_ffdc;
|
||||
bool timed_out;
|
||||
u32 timeout_in_cmd_ms;
|
||||
u32 timeout_start_rsp_ms;
|
||||
};
|
||||
|
||||
|
@ -136,6 +137,7 @@ struct sbefifo_user {
|
|||
void *cmd_page;
|
||||
void *pending_cmd;
|
||||
size_t pending_len;
|
||||
u32 cmd_timeout_ms;
|
||||
u32 read_timeout_ms;
|
||||
};
|
||||
|
||||
|
@ -508,7 +510,7 @@ static int sbefifo_send_command(struct sbefifo *sbefifo,
|
|||
rc = sbefifo_wait(sbefifo, true, &status, timeout);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
timeout = msecs_to_jiffies(SBEFIFO_TIMEOUT_IN_CMD);
|
||||
timeout = msecs_to_jiffies(sbefifo->timeout_in_cmd_ms);
|
||||
|
||||
vacant = sbefifo_vacant(status);
|
||||
len = chunk = min(vacant, remaining);
|
||||
|
@ -730,7 +732,7 @@ static int __sbefifo_submit(struct sbefifo *sbefifo,
|
|||
* @response: The output response buffer
|
||||
* @resp_len: In: Response buffer size, Out: Response size
|
||||
*
|
||||
* This will perform the entire operation. If the reponse buffer
|
||||
* This will perform the entire operation. If the response buffer
|
||||
* overflows, returns -EOVERFLOW
|
||||
*/
|
||||
int sbefifo_submit(struct device *dev, const __be32 *command, size_t cmd_len,
|
||||
|
@ -802,6 +804,7 @@ static int sbefifo_user_open(struct inode *inode, struct file *file)
|
|||
return -ENOMEM;
|
||||
}
|
||||
mutex_init(&user->file_lock);
|
||||
user->cmd_timeout_ms = SBEFIFO_TIMEOUT_IN_CMD;
|
||||
user->read_timeout_ms = SBEFIFO_TIMEOUT_START_RSP;
|
||||
|
||||
return 0;
|
||||
|
@ -845,9 +848,11 @@ static ssize_t sbefifo_user_read(struct file *file, char __user *buf,
|
|||
rc = mutex_lock_interruptible(&sbefifo->lock);
|
||||
if (rc)
|
||||
goto bail;
|
||||
sbefifo->timeout_in_cmd_ms = user->cmd_timeout_ms;
|
||||
sbefifo->timeout_start_rsp_ms = user->read_timeout_ms;
|
||||
rc = __sbefifo_submit(sbefifo, user->pending_cmd, cmd_len, &resp_iter);
|
||||
sbefifo->timeout_start_rsp_ms = SBEFIFO_TIMEOUT_START_RSP;
|
||||
sbefifo->timeout_in_cmd_ms = SBEFIFO_TIMEOUT_IN_CMD;
|
||||
mutex_unlock(&sbefifo->lock);
|
||||
if (rc < 0)
|
||||
goto bail;
|
||||
|
@ -937,6 +942,25 @@ static int sbefifo_user_release(struct inode *inode, struct file *file)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int sbefifo_cmd_timeout(struct sbefifo_user *user, void __user *argp)
|
||||
{
|
||||
struct device *dev = &user->sbefifo->dev;
|
||||
u32 timeout;
|
||||
|
||||
if (get_user(timeout, (__u32 __user *)argp))
|
||||
return -EFAULT;
|
||||
|
||||
if (timeout == 0) {
|
||||
user->cmd_timeout_ms = SBEFIFO_TIMEOUT_IN_CMD;
|
||||
dev_dbg(dev, "Command timeout reset to %us\n", user->cmd_timeout_ms / 1000);
|
||||
return 0;
|
||||
}
|
||||
|
||||
user->cmd_timeout_ms = timeout * 1000; /* user timeout is in sec */
|
||||
dev_dbg(dev, "Command timeout set to %us\n", timeout);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sbefifo_read_timeout(struct sbefifo_user *user, void __user *argp)
|
||||
{
|
||||
struct device *dev = &user->sbefifo->dev;
|
||||
|
@ -947,17 +971,12 @@ static int sbefifo_read_timeout(struct sbefifo_user *user, void __user *argp)
|
|||
|
||||
if (timeout == 0) {
|
||||
user->read_timeout_ms = SBEFIFO_TIMEOUT_START_RSP;
|
||||
dev_dbg(dev, "Timeout reset to %d\n", user->read_timeout_ms);
|
||||
dev_dbg(dev, "Timeout reset to %us\n", user->read_timeout_ms / 1000);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (timeout < 10 || timeout > 120)
|
||||
return -EINVAL;
|
||||
|
||||
user->read_timeout_ms = timeout * 1000; /* user timeout is in sec */
|
||||
|
||||
dev_dbg(dev, "Timeout set to %d\n", user->read_timeout_ms);
|
||||
|
||||
dev_dbg(dev, "Timeout set to %us\n", timeout);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -971,6 +990,9 @@ static long sbefifo_user_ioctl(struct file *file, unsigned int cmd, unsigned lon
|
|||
|
||||
mutex_lock(&user->file_lock);
|
||||
switch (cmd) {
|
||||
case FSI_SBEFIFO_CMD_TIMEOUT_SECONDS:
|
||||
rc = sbefifo_cmd_timeout(user, (void __user *)arg);
|
||||
break;
|
||||
case FSI_SBEFIFO_READ_TIMEOUT_SECONDS:
|
||||
rc = sbefifo_read_timeout(user, (void __user *)arg);
|
||||
break;
|
||||
|
@ -1025,16 +1047,9 @@ static int sbefifo_probe(struct device *dev)
|
|||
sbefifo->fsi_dev = fsi_dev;
|
||||
dev_set_drvdata(dev, sbefifo);
|
||||
mutex_init(&sbefifo->lock);
|
||||
sbefifo->timeout_in_cmd_ms = SBEFIFO_TIMEOUT_IN_CMD;
|
||||
sbefifo->timeout_start_rsp_ms = SBEFIFO_TIMEOUT_START_RSP;
|
||||
|
||||
/*
|
||||
* Try cleaning up the FIFO. If this fails, we still register the
|
||||
* driver and will try cleaning things up again on the next access.
|
||||
*/
|
||||
rc = sbefifo_cleanup_hw(sbefifo);
|
||||
if (rc && rc != -ESHUTDOWN)
|
||||
dev_err(dev, "Initial HW cleanup failed, will retry later\n");
|
||||
|
||||
/* Create chardev for userspace access */
|
||||
sbefifo->dev.type = &fsi_cdev_type;
|
||||
sbefifo->dev.parent = dev;
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
#include <linux/cdev.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/mod_devicetable.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/list.h>
|
||||
|
@ -587,6 +588,12 @@ static int scom_remove(struct device *dev)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id scom_of_ids[] = {
|
||||
{ .compatible = "ibm,fsi2pib" },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, scom_of_ids);
|
||||
|
||||
static const struct fsi_device_id scom_ids[] = {
|
||||
{
|
||||
.engine_type = FSI_ENGID_SCOM,
|
||||
|
@ -600,6 +607,7 @@ static struct fsi_driver scom_drv = {
|
|||
.drv = {
|
||||
.name = "scom",
|
||||
.bus = &fsi_bus_type,
|
||||
.of_match_table = scom_of_ids,
|
||||
.probe = scom_probe,
|
||||
.remove = scom_remove,
|
||||
}
|
||||
|
|
28
drivers/fsi/fsi-slave.h
Normal file
28
drivers/fsi/fsi-slave.h
Normal file
|
@ -0,0 +1,28 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/* Copyright (C) IBM Corporation 2023 */
|
||||
|
||||
#ifndef DRIVERS_FSI_SLAVE_H
|
||||
#define DRIVERS_FSI_SLAVE_H
|
||||
|
||||
#include <linux/cdev.h>
|
||||
#include <linux/device.h>
|
||||
|
||||
struct fsi_master;
|
||||
|
||||
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 to_fsi_slave(d) container_of(d, struct fsi_slave, dev)
|
||||
|
||||
#endif /* DRIVERS_FSI_SLAVE_H */
|
154
drivers/fsi/i2cr-scom.c
Normal file
154
drivers/fsi/i2cr-scom.c
Normal file
|
@ -0,0 +1,154 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/* Copyright (C) IBM Corporation 2023 */
|
||||
|
||||
#include <linux/cdev.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/fsi.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mod_devicetable.h>
|
||||
|
||||
#include "fsi-master-i2cr.h"
|
||||
#include "fsi-slave.h"
|
||||
|
||||
struct i2cr_scom {
|
||||
struct device dev;
|
||||
struct cdev cdev;
|
||||
struct fsi_master_i2cr *i2cr;
|
||||
};
|
||||
|
||||
static loff_t i2cr_scom_llseek(struct file *file, loff_t offset, int whence)
|
||||
{
|
||||
switch (whence) {
|
||||
case SEEK_CUR:
|
||||
break;
|
||||
case SEEK_SET:
|
||||
file->f_pos = offset;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return offset;
|
||||
}
|
||||
|
||||
static ssize_t i2cr_scom_read(struct file *filep, char __user *buf, size_t len, loff_t *offset)
|
||||
{
|
||||
struct i2cr_scom *scom = filep->private_data;
|
||||
u64 data;
|
||||
int ret;
|
||||
|
||||
if (len != sizeof(data))
|
||||
return -EINVAL;
|
||||
|
||||
ret = fsi_master_i2cr_read(scom->i2cr, (u32)*offset, &data);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = copy_to_user(buf, &data, len);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
static ssize_t i2cr_scom_write(struct file *filep, const char __user *buf, size_t len,
|
||||
loff_t *offset)
|
||||
{
|
||||
struct i2cr_scom *scom = filep->private_data;
|
||||
u64 data;
|
||||
int ret;
|
||||
|
||||
if (len != sizeof(data))
|
||||
return -EINVAL;
|
||||
|
||||
ret = copy_from_user(&data, buf, len);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = fsi_master_i2cr_write(scom->i2cr, (u32)*offset, data);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
static const struct file_operations i2cr_scom_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = simple_open,
|
||||
.llseek = i2cr_scom_llseek,
|
||||
.read = i2cr_scom_read,
|
||||
.write = i2cr_scom_write,
|
||||
};
|
||||
|
||||
static int i2cr_scom_probe(struct device *dev)
|
||||
{
|
||||
struct fsi_device *fsi_dev = to_fsi_dev(dev);
|
||||
struct i2cr_scom *scom;
|
||||
int didx;
|
||||
int ret;
|
||||
|
||||
if (!is_fsi_master_i2cr(fsi_dev->slave->master))
|
||||
return -ENODEV;
|
||||
|
||||
scom = devm_kzalloc(dev, sizeof(*scom), GFP_KERNEL);
|
||||
if (!scom)
|
||||
return -ENOMEM;
|
||||
|
||||
scom->i2cr = to_fsi_master_i2cr(fsi_dev->slave->master);
|
||||
dev_set_drvdata(dev, scom);
|
||||
|
||||
scom->dev.type = &fsi_cdev_type;
|
||||
scom->dev.parent = dev;
|
||||
device_initialize(&scom->dev);
|
||||
|
||||
ret = fsi_get_new_minor(fsi_dev, fsi_dev_scom, &scom->dev.devt, &didx);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
dev_set_name(&scom->dev, "scom%d", didx);
|
||||
cdev_init(&scom->cdev, &i2cr_scom_fops);
|
||||
ret = cdev_device_add(&scom->cdev, &scom->dev);
|
||||
if (ret)
|
||||
fsi_free_minor(scom->dev.devt);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int i2cr_scom_remove(struct device *dev)
|
||||
{
|
||||
struct i2cr_scom *scom = dev_get_drvdata(dev);
|
||||
|
||||
cdev_device_del(&scom->cdev, &scom->dev);
|
||||
fsi_free_minor(scom->dev.devt);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id i2cr_scom_of_ids[] = {
|
||||
{ .compatible = "ibm,i2cr-scom" },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, i2cr_scom_of_ids);
|
||||
|
||||
static const struct fsi_device_id i2cr_scom_ids[] = {
|
||||
{ 0x5, FSI_VERSION_ANY },
|
||||
{ }
|
||||
};
|
||||
|
||||
static struct fsi_driver i2cr_scom_driver = {
|
||||
.id_table = i2cr_scom_ids,
|
||||
.drv = {
|
||||
.name = "i2cr_scom",
|
||||
.bus = &fsi_bus_type,
|
||||
.of_match_table = i2cr_scom_of_ids,
|
||||
.probe = i2cr_scom_probe,
|
||||
.remove = i2cr_scom_remove,
|
||||
}
|
||||
};
|
||||
|
||||
module_fsi_driver(i2cr_scom_driver);
|
||||
|
||||
MODULE_AUTHOR("Eddie James <eajames@linux.ibm.com>");
|
||||
MODULE_DESCRIPTION("IBM I2C Responder SCOM driver");
|
||||
MODULE_LICENSE("GPL");
|
|
@ -122,6 +122,37 @@ TRACE_EVENT(fsi_master_break,
|
|||
)
|
||||
);
|
||||
|
||||
TRACE_EVENT(fsi_master_scan,
|
||||
TP_PROTO(const struct fsi_master *master, bool scan),
|
||||
TP_ARGS(master, scan),
|
||||
TP_STRUCT__entry(
|
||||
__field(int, master_idx)
|
||||
__field(int, n_links)
|
||||
__field(bool, scan)
|
||||
),
|
||||
TP_fast_assign(
|
||||
__entry->master_idx = master->idx;
|
||||
__entry->n_links = master->n_links;
|
||||
__entry->scan = scan;
|
||||
),
|
||||
TP_printk("fsi%d (%d links) %s", __entry->master_idx, __entry->n_links,
|
||||
__entry->scan ? "scan" : "unscan")
|
||||
);
|
||||
|
||||
TRACE_EVENT(fsi_master_unregister,
|
||||
TP_PROTO(const struct fsi_master *master),
|
||||
TP_ARGS(master),
|
||||
TP_STRUCT__entry(
|
||||
__field(int, master_idx)
|
||||
__field(int, n_links)
|
||||
),
|
||||
TP_fast_assign(
|
||||
__entry->master_idx = master->idx;
|
||||
__entry->n_links = master->n_links;
|
||||
),
|
||||
TP_printk("fsi%d (%d links)", __entry->master_idx, __entry->n_links)
|
||||
);
|
||||
|
||||
TRACE_EVENT(fsi_slave_init,
|
||||
TP_PROTO(const struct fsi_slave *slave),
|
||||
TP_ARGS(slave),
|
||||
|
|
107
include/trace/events/fsi_master_i2cr.h
Normal file
107
include/trace/events/fsi_master_i2cr.h
Normal file
|
@ -0,0 +1,107 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#undef TRACE_SYSTEM
|
||||
#define TRACE_SYSTEM fsi_master_i2cr
|
||||
|
||||
#if !defined(_TRACE_FSI_MASTER_I2CR_H) || defined(TRACE_HEADER_MULTI_READ)
|
||||
#define _TRACE_FSI_MASTER_I2CR_H
|
||||
|
||||
#include <linux/tracepoint.h>
|
||||
|
||||
TRACE_EVENT(i2cr_i2c_error,
|
||||
TP_PROTO(const struct i2c_client *client, uint32_t command, int rc),
|
||||
TP_ARGS(client, command, rc),
|
||||
TP_STRUCT__entry(
|
||||
__field(int, bus)
|
||||
__field(int, rc)
|
||||
__array(unsigned char, command, sizeof(uint32_t))
|
||||
__field(unsigned short, addr)
|
||||
),
|
||||
TP_fast_assign(
|
||||
__entry->bus = client->adapter->nr;
|
||||
__entry->rc = rc;
|
||||
memcpy(__entry->command, &command, sizeof(uint32_t));
|
||||
__entry->addr = client->addr;
|
||||
),
|
||||
TP_printk("%d-%02x command:{ %*ph } rc:%d", __entry->bus, __entry->addr,
|
||||
(int)sizeof(uint32_t), __entry->command, __entry->rc)
|
||||
);
|
||||
|
||||
TRACE_EVENT(i2cr_read,
|
||||
TP_PROTO(const struct i2c_client *client, uint32_t command, uint64_t *data),
|
||||
TP_ARGS(client, command, data),
|
||||
TP_STRUCT__entry(
|
||||
__field(int, bus)
|
||||
__array(unsigned char, data, sizeof(uint64_t))
|
||||
__array(unsigned char, command, sizeof(uint32_t))
|
||||
__field(unsigned short, addr)
|
||||
),
|
||||
TP_fast_assign(
|
||||
__entry->bus = client->adapter->nr;
|
||||
memcpy(__entry->data, data, sizeof(uint64_t));
|
||||
memcpy(__entry->command, &command, sizeof(uint32_t));
|
||||
__entry->addr = client->addr;
|
||||
),
|
||||
TP_printk("%d-%02x command:{ %*ph } { %*ph }", __entry->bus, __entry->addr,
|
||||
(int)sizeof(uint32_t), __entry->command, (int)sizeof(uint64_t), __entry->data)
|
||||
);
|
||||
|
||||
TRACE_EVENT(i2cr_status,
|
||||
TP_PROTO(const struct i2c_client *client, uint64_t status),
|
||||
TP_ARGS(client, status),
|
||||
TP_STRUCT__entry(
|
||||
__field(uint64_t, status)
|
||||
__field(int, bus)
|
||||
__field(unsigned short, addr)
|
||||
),
|
||||
TP_fast_assign(
|
||||
__entry->status = status;
|
||||
__entry->bus = client->adapter->nr;
|
||||
__entry->addr = client->addr;
|
||||
),
|
||||
TP_printk("%d-%02x %016llx", __entry->bus, __entry->addr, __entry->status)
|
||||
);
|
||||
|
||||
TRACE_EVENT(i2cr_status_error,
|
||||
TP_PROTO(const struct i2c_client *client, uint64_t status, uint64_t error, uint64_t log),
|
||||
TP_ARGS(client, status, error, log),
|
||||
TP_STRUCT__entry(
|
||||
__field(uint64_t, error)
|
||||
__field(uint64_t, log)
|
||||
__field(uint64_t, status)
|
||||
__field(int, bus)
|
||||
__field(unsigned short, addr)
|
||||
),
|
||||
TP_fast_assign(
|
||||
__entry->error = error;
|
||||
__entry->log = log;
|
||||
__entry->status = status;
|
||||
__entry->bus = client->adapter->nr;
|
||||
__entry->addr = client->addr;
|
||||
),
|
||||
TP_printk("%d-%02x status:%016llx error:%016llx log:%016llx", __entry->bus, __entry->addr,
|
||||
__entry->status, __entry->error, __entry->log)
|
||||
);
|
||||
|
||||
TRACE_EVENT(i2cr_write,
|
||||
TP_PROTO(const struct i2c_client *client, uint32_t command, uint64_t data),
|
||||
TP_ARGS(client, command, data),
|
||||
TP_STRUCT__entry(
|
||||
__field(int, bus)
|
||||
__array(unsigned char, data, sizeof(uint64_t))
|
||||
__array(unsigned char, command, sizeof(uint32_t))
|
||||
__field(unsigned short, addr)
|
||||
),
|
||||
TP_fast_assign(
|
||||
__entry->bus = client->adapter->nr;
|
||||
memcpy(__entry->data, &data, sizeof(uint64_t));
|
||||
memcpy(__entry->command, &command, sizeof(uint32_t));
|
||||
__entry->addr = client->addr;
|
||||
),
|
||||
TP_printk("%d-%02x command:{ %*ph } { %*ph }", __entry->bus, __entry->addr,
|
||||
(int)sizeof(uint32_t), __entry->command, (int)sizeof(uint64_t), __entry->data)
|
||||
);
|
||||
|
||||
#endif
|
||||
|
||||
#include <trace/define_trace.h>
|
|
@ -59,6 +59,16 @@ struct scom_access {
|
|||
* /dev/sbefifo* ioctl interface
|
||||
*/
|
||||
|
||||
/**
|
||||
* FSI_SBEFIFO_CMD_TIMEOUT sets the timeout for writing data to the SBEFIFO.
|
||||
*
|
||||
* The command timeout is specified in seconds. The minimum value of command
|
||||
* timeout is 1 seconds (default) and the maximum value of command timeout is
|
||||
* 120 seconds. A command timeout of 0 will reset the value to the default of
|
||||
* 1 seconds.
|
||||
*/
|
||||
#define FSI_SBEFIFO_CMD_TIMEOUT_SECONDS _IOW('s', 0x01, __u32)
|
||||
|
||||
/**
|
||||
* FSI_SBEFIFO_READ_TIMEOUT sets the read timeout for response from SBE.
|
||||
*
|
||||
|
|
Loading…
Reference in a new issue