Skip to content

Commit

Permalink
ipmi:ipmb: Add the ability to have a separate slave and master device
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Corey Minyard authored and Corey Minyard committed Feb 23, 2022
1 parent 57c9e3c commit 00d9361
Show file tree
Hide file tree
Showing 2 changed files with 57 additions and 9 deletions.
8 changes: 8 additions & 0 deletions Documentation/devicetree/bindings/ipmi/ipmi-ipmb.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
58 changes: 49 additions & 9 deletions drivers/char/ipmi/ipmi_ipmb.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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) |
Expand Down Expand Up @@ -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);
Expand All @@ -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);
Expand All @@ -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;
Expand Down Expand Up @@ -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;
}
Expand Down

0 comments on commit 00d9361

Please sign in to comment.