summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCorey Minyard <minyard@acm.org>2022-02-20 23:38:39 +0300
committerCorey Minyard <cminyard@mvista.com>2022-02-23 17:16:02 +0300
commit00d93611f00219bd142aa119c5121793cac30ff0 (patch)
treec2b782911e94090d3503b1c13bb3c7e09a885bca
parent57c9e3c9a374ff1419e5559f8979c0b43a0bffbd (diff)
downloadlinux-00d93611f00219bd142aa119c5121793cac30ff0.tar.xz
ipmi:ipmb: Add the ability to have a separate slave and master device
A situation has come up where there is a slave-only device for the slave and a separate master device on the same bug. Allow a separate slave device to be registered. Signed-off-by: Corey Minyard <minyard@acm.org>
-rw-r--r--Documentation/devicetree/bindings/ipmi/ipmi-ipmb.yaml8
-rw-r--r--drivers/char/ipmi/ipmi_ipmb.c58
2 files changed, 57 insertions, 9 deletions
diff --git a/Documentation/devicetree/bindings/ipmi/ipmi-ipmb.yaml b/Documentation/devicetree/bindings/ipmi/ipmi-ipmb.yaml
index 93d8f8e88cf5..71bc031c4fde 100644
--- a/Documentation/devicetree/bindings/ipmi/ipmi-ipmb.yaml
+++ b/Documentation/devicetree/bindings/ipmi/ipmi-ipmb.yaml
@@ -36,6 +36,14 @@ properties:
$ref: /schemas/types.yaml#/definitions/uint32
description: Number of retries before a failure is declared. Defaults to 1.
+ slave-dev:
+ $ref: /schemas/types.yaml#/definitions/phandle
+ description: |
+ The slave i2c device. If not present, the main device is used. This
+ lets you use two devices on the IPMB, one for master and one for slave,
+ in case you have a slave device that can only be a slave. The slave
+ will receive messages and the master will transmit.
+
required:
- compatible
- reg
diff --git a/drivers/char/ipmi/ipmi_ipmb.c b/drivers/char/ipmi/ipmi_ipmb.c
index 88d7316b3bab..b81b862532fb 100644
--- a/drivers/char/ipmi/ipmi_ipmb.c
+++ b/drivers/char/ipmi/ipmi_ipmb.c
@@ -39,6 +39,7 @@ MODULE_PARM_DESC(max_retries, "Max resends of a command before timing out.");
struct ipmi_ipmb_dev {
struct ipmi_smi *intf;
struct i2c_client *client;
+ struct i2c_client *slave;
struct ipmi_smi_handlers handlers;
@@ -257,7 +258,7 @@ static void ipmi_ipmb_format_for_xmit(struct ipmi_ipmb_dev *iidev,
memcpy(iidev->xmitmsg + 5, msg->data + 1, msg->data_size - 1);
iidev->xmitlen = msg->data_size + 4;
}
- iidev->xmitmsg[3] = iidev->client->addr << 1;
+ iidev->xmitmsg[3] = iidev->slave->addr << 1;
if (((msg->data[0] >> 2) & 1) == 0)
/* If it's a command, put in our own sequence number. */
iidev->xmitmsg[4] = ((iidev->xmitmsg[4] & 0x03) |
@@ -427,10 +428,13 @@ static int ipmi_ipmb_remove(struct i2c_client *client)
{
struct ipmi_ipmb_dev *iidev = i2c_get_clientdata(client);
- if (iidev->client) {
- iidev->client = NULL;
- i2c_slave_unregister(client);
+ if (iidev->slave) {
+ i2c_slave_unregister(iidev->slave);
+ if (iidev->slave != iidev->client)
+ i2c_unregister_device(iidev->slave);
}
+ iidev->slave = NULL;
+ iidev->client = NULL;
ipmi_ipmb_stop_thread(iidev);
ipmi_unregister_smi(iidev->intf);
@@ -443,6 +447,9 @@ static int ipmi_ipmb_probe(struct i2c_client *client,
{
struct device *dev = &client->dev;
struct ipmi_ipmb_dev *iidev;
+ struct device_node *slave_np;
+ struct i2c_adapter *slave_adap = NULL;
+ struct i2c_client *slave = NULL;
int rv;
iidev = devm_kzalloc(&client->dev, sizeof(*iidev), GFP_KERNEL);
@@ -466,14 +473,45 @@ static int ipmi_ipmb_probe(struct i2c_client *client,
&iidev->max_retries) != 0)
iidev->max_retries = max_retries;
+ slave_np = of_parse_phandle(dev->of_node, "slave-dev", 0);
+ if (slave_np) {
+ slave_adap = of_get_i2c_adapter_by_node(slave_np);
+ if (!slave_adap) {
+ dev_notice(&client->dev,
+ "Could not find slave adapter\n");
+ return -EINVAL;
+ }
+ }
+
+ iidev->client = client;
+
+ if (slave_adap) {
+ struct i2c_board_info binfo;
+
+ memset(&binfo, 0, sizeof(binfo));
+ strscpy(binfo.type, "ipmb-slave", I2C_NAME_SIZE);
+ binfo.addr = client->addr;
+ binfo.flags = I2C_CLIENT_SLAVE;
+ slave = i2c_new_client_device(slave_adap, &binfo);
+ i2c_put_adapter(slave_adap);
+ if (IS_ERR(slave)) {
+ rv = PTR_ERR(slave);
+ dev_notice(&client->dev,
+ "Could not allocate slave device: %d\n", rv);
+ return rv;
+ }
+ i2c_set_clientdata(slave, iidev);
+ } else {
+ slave = client;
+ }
i2c_set_clientdata(client, iidev);
- client->flags |= I2C_CLIENT_SLAVE;
+ slave->flags |= I2C_CLIENT_SLAVE;
- rv = i2c_slave_register(client, ipmi_ipmb_slave_cb);
+ rv = i2c_slave_register(slave, ipmi_ipmb_slave_cb);
if (rv)
- return rv;
-
- iidev->client = client;
+ goto out_err;
+ iidev->slave = slave;
+ slave = NULL;
iidev->handlers.flags = IPMI_SMI_CAN_HANDLE_IPMB_DIRECT;
iidev->handlers.start_processing = ipmi_ipmb_start_processing;
@@ -504,6 +542,8 @@ static int ipmi_ipmb_probe(struct i2c_client *client,
return 0;
out_err:
+ if (slave && slave != client)
+ i2c_unregister_device(slave);
ipmi_ipmb_remove(client);
return rv;
}