ipmi:ipmb: Add the ability to have a separate slave and master device
authorCorey Minyard <minyard@acm.org>
Sun, 20 Feb 2022 20:38:39 +0000 (14:38 -0600)
committerCorey Minyard <cminyard@mvista.com>
Wed, 23 Feb 2022 14:16:02 +0000 (08:16 -0600)
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>
Documentation/devicetree/bindings/ipmi/ipmi-ipmb.yaml
drivers/char/ipmi/ipmi_ipmb.c

index 93d8f8e88cf56c7cb94a407dbc66b883a3898672..71bc031c4fde6e10e04ca3a617e8545d6ea9a4dc 100644 (file)
@@ -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
index 88d7316b3babc875a7b3b53b1038cd638559ba7c..b81b862532fb01b3b851f69f7d246e3c46a7ad82 100644 (file)
@@ -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;
 }