diff options
Diffstat (limited to 'drivers/char/ipmi/ipmi_ipmb.c')
-rw-r--r-- | drivers/char/ipmi/ipmi_ipmb.c | 82 |
1 files changed, 63 insertions, 19 deletions
diff --git a/drivers/char/ipmi/ipmi_ipmb.c b/drivers/char/ipmi/ipmi_ipmb.c index ba0c2d2c6bbe..7c1aee5e11b7 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; @@ -217,8 +218,8 @@ static void ipmi_ipmb_send_response(struct ipmi_ipmb_dev *iidev, { if ((msg->data[0] >> 2) & 1) { /* - * It's a response being sent, we needto return a - * response response. Fake a send msg command + * It's a response being sent, we need to return a + * response to the response. Fake a send msg command * response with channel 0. This will always be ipmb * direct. */ @@ -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) | @@ -423,24 +424,33 @@ static void ipmi_ipmb_request_events(void *send_info) /* We don't fetch events here. */ } -static int ipmi_ipmb_remove(struct i2c_client *client) +static void ipmi_ipmb_cleanup(struct ipmi_ipmb_dev *iidev) { - 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); +} - return 0; +static void ipmi_ipmb_remove(struct i2c_client *client) +{ + struct ipmi_ipmb_dev *iidev = i2c_get_clientdata(client); + + ipmi_ipmb_cleanup(iidev); + ipmi_unregister_smi(iidev->intf); } -static int ipmi_ipmb_probe(struct i2c_client *client, - const struct i2c_device_id *id) +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); @@ -464,14 +474,46 @@ 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); + of_node_put(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; @@ -502,7 +544,9 @@ static int ipmi_ipmb_probe(struct i2c_client *client, return 0; out_err: - ipmi_ipmb_remove(client); + if (slave && slave != client) + i2c_unregister_device(slave); + ipmi_ipmb_cleanup(iidev); return rv; } @@ -528,7 +572,7 @@ static struct i2c_driver ipmi_ipmb_driver = { .name = DEVICE_NAME, .of_match_table = of_ipmi_ipmb_match, }, - .probe = ipmi_ipmb_probe, + .probe_new = ipmi_ipmb_probe, .remove = ipmi_ipmb_remove, .id_table = ipmi_ipmb_id, }; |