diff options
Diffstat (limited to 'drivers/i2c/i2c-core-smbus.c')
-rw-r--r-- | drivers/i2c/i2c-core-smbus.c | 723 |
1 files changed, 723 insertions, 0 deletions
diff --git a/drivers/i2c/i2c-core-smbus.c b/drivers/i2c/i2c-core-smbus.c new file mode 100644 index 000000000..e3b96fc53 --- /dev/null +++ b/drivers/i2c/i2c-core-smbus.c @@ -0,0 +1,723 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Linux I2C core SMBus and SMBus emulation code + * + * This file contains the SMBus functions which are always included in the I2C + * core because they can be emulated via I2C. SMBus specific extensions + * (e.g. smbalert) are handled in a separate i2c-smbus module. + * + * All SMBus-related things are written by Frodo Looijaard <frodol@dds.nl> + * SMBus 2.0 support by Mark Studebaker <mdsxyz123@yahoo.com> and + * Jean Delvare <jdelvare@suse.de> + */ +#include <linux/device.h> +#include <linux/err.h> +#include <linux/i2c.h> +#include <linux/i2c-smbus.h> +#include <linux/property.h> +#include <linux/slab.h> + +#include "i2c-core.h" + +#define CREATE_TRACE_POINTS +#include <trace/events/smbus.h> + + +/* The SMBus parts */ + +#define POLY (0x1070U << 3) +static u8 crc8(u16 data) +{ + int i; + + for (i = 0; i < 8; i++) { + if (data & 0x8000) + data = data ^ POLY; + data = data << 1; + } + return (u8)(data >> 8); +} + +/** + * i2c_smbus_pec - Incremental CRC8 over the given input data array + * @crc: previous return crc8 value + * @p: pointer to data buffer. + * @count: number of bytes in data buffer. + * + * Incremental CRC8 over count bytes in the array pointed to by p + */ +u8 i2c_smbus_pec(u8 crc, u8 *p, size_t count) +{ + int i; + + for (i = 0; i < count; i++) + crc = crc8((crc ^ p[i]) << 8); + return crc; +} +EXPORT_SYMBOL(i2c_smbus_pec); + +/* Assume a 7-bit address, which is reasonable for SMBus */ +static u8 i2c_smbus_msg_pec(u8 pec, struct i2c_msg *msg) +{ + /* The address will be sent first */ + u8 addr = i2c_8bit_addr_from_msg(msg); + pec = i2c_smbus_pec(pec, &addr, 1); + + /* The data buffer follows */ + return i2c_smbus_pec(pec, msg->buf, msg->len); +} + +/* Used for write only transactions */ +static inline void i2c_smbus_add_pec(struct i2c_msg *msg) +{ + msg->buf[msg->len] = i2c_smbus_msg_pec(0, msg); + msg->len++; +} + +/* Return <0 on CRC error + If there was a write before this read (most cases) we need to take the + partial CRC from the write part into account. + Note that this function does modify the message (we need to decrease the + message length to hide the CRC byte from the caller). */ +static int i2c_smbus_check_pec(u8 cpec, struct i2c_msg *msg) +{ + u8 rpec = msg->buf[--msg->len]; + cpec = i2c_smbus_msg_pec(cpec, msg); + + if (rpec != cpec) { + pr_debug("Bad PEC 0x%02x vs. 0x%02x\n", + rpec, cpec); + return -EBADMSG; + } + return 0; +} + +/** + * i2c_smbus_read_byte - SMBus "receive byte" protocol + * @client: Handle to slave device + * + * This executes the SMBus "receive byte" protocol, returning negative errno + * else the byte received from the device. + */ +s32 i2c_smbus_read_byte(const struct i2c_client *client) +{ + union i2c_smbus_data data; + int status; + + status = i2c_smbus_xfer(client->adapter, client->addr, client->flags, + I2C_SMBUS_READ, 0, + I2C_SMBUS_BYTE, &data); + return (status < 0) ? status : data.byte; +} +EXPORT_SYMBOL(i2c_smbus_read_byte); + +/** + * i2c_smbus_write_byte - SMBus "send byte" protocol + * @client: Handle to slave device + * @value: Byte to be sent + * + * This executes the SMBus "send byte" protocol, returning negative errno + * else zero on success. + */ +s32 i2c_smbus_write_byte(const struct i2c_client *client, u8 value) +{ + return i2c_smbus_xfer(client->adapter, client->addr, client->flags, + I2C_SMBUS_WRITE, value, I2C_SMBUS_BYTE, NULL); +} +EXPORT_SYMBOL(i2c_smbus_write_byte); + +/** + * i2c_smbus_read_byte_data - SMBus "read byte" protocol + * @client: Handle to slave device + * @command: Byte interpreted by slave + * + * This executes the SMBus "read byte" protocol, returning negative errno + * else a data byte received from the device. + */ +s32 i2c_smbus_read_byte_data(const struct i2c_client *client, u8 command) +{ + union i2c_smbus_data data; + int status; + + status = i2c_smbus_xfer(client->adapter, client->addr, client->flags, + I2C_SMBUS_READ, command, + I2C_SMBUS_BYTE_DATA, &data); + return (status < 0) ? status : data.byte; +} +EXPORT_SYMBOL(i2c_smbus_read_byte_data); + +/** + * i2c_smbus_write_byte_data - SMBus "write byte" protocol + * @client: Handle to slave device + * @command: Byte interpreted by slave + * @value: Byte being written + * + * This executes the SMBus "write byte" protocol, returning negative errno + * else zero on success. + */ +s32 i2c_smbus_write_byte_data(const struct i2c_client *client, u8 command, + u8 value) +{ + union i2c_smbus_data data; + data.byte = value; + return i2c_smbus_xfer(client->adapter, client->addr, client->flags, + I2C_SMBUS_WRITE, command, + I2C_SMBUS_BYTE_DATA, &data); +} +EXPORT_SYMBOL(i2c_smbus_write_byte_data); + +/** + * i2c_smbus_read_word_data - SMBus "read word" protocol + * @client: Handle to slave device + * @command: Byte interpreted by slave + * + * This executes the SMBus "read word" protocol, returning negative errno + * else a 16-bit unsigned "word" received from the device. + */ +s32 i2c_smbus_read_word_data(const struct i2c_client *client, u8 command) +{ + union i2c_smbus_data data; + int status; + + status = i2c_smbus_xfer(client->adapter, client->addr, client->flags, + I2C_SMBUS_READ, command, + I2C_SMBUS_WORD_DATA, &data); + return (status < 0) ? status : data.word; +} +EXPORT_SYMBOL(i2c_smbus_read_word_data); + +/** + * i2c_smbus_write_word_data - SMBus "write word" protocol + * @client: Handle to slave device + * @command: Byte interpreted by slave + * @value: 16-bit "word" being written + * + * This executes the SMBus "write word" protocol, returning negative errno + * else zero on success. + */ +s32 i2c_smbus_write_word_data(const struct i2c_client *client, u8 command, + u16 value) +{ + union i2c_smbus_data data; + data.word = value; + return i2c_smbus_xfer(client->adapter, client->addr, client->flags, + I2C_SMBUS_WRITE, command, + I2C_SMBUS_WORD_DATA, &data); +} +EXPORT_SYMBOL(i2c_smbus_write_word_data); + +/** + * i2c_smbus_read_block_data - SMBus "block read" protocol + * @client: Handle to slave device + * @command: Byte interpreted by slave + * @values: Byte array into which data will be read; big enough to hold + * the data returned by the slave. SMBus allows at most 32 bytes. + * + * This executes the SMBus "block read" protocol, returning negative errno + * else the number of data bytes in the slave's response. + * + * Note that using this function requires that the client's adapter support + * the I2C_FUNC_SMBUS_READ_BLOCK_DATA functionality. Not all adapter drivers + * support this; its emulation through I2C messaging relies on a specific + * mechanism (I2C_M_RECV_LEN) which may not be implemented. + */ +s32 i2c_smbus_read_block_data(const struct i2c_client *client, u8 command, + u8 *values) +{ + union i2c_smbus_data data; + int status; + + status = i2c_smbus_xfer(client->adapter, client->addr, client->flags, + I2C_SMBUS_READ, command, + I2C_SMBUS_BLOCK_DATA, &data); + if (status) + return status; + + memcpy(values, &data.block[1], data.block[0]); + return data.block[0]; +} +EXPORT_SYMBOL(i2c_smbus_read_block_data); + +/** + * i2c_smbus_write_block_data - SMBus "block write" protocol + * @client: Handle to slave device + * @command: Byte interpreted by slave + * @length: Size of data block; SMBus allows at most 32 bytes + * @values: Byte array which will be written. + * + * This executes the SMBus "block write" protocol, returning negative errno + * else zero on success. + */ +s32 i2c_smbus_write_block_data(const struct i2c_client *client, u8 command, + u8 length, const u8 *values) +{ + union i2c_smbus_data data; + + if (length > I2C_SMBUS_BLOCK_MAX) + length = I2C_SMBUS_BLOCK_MAX; + data.block[0] = length; + memcpy(&data.block[1], values, length); + return i2c_smbus_xfer(client->adapter, client->addr, client->flags, + I2C_SMBUS_WRITE, command, + I2C_SMBUS_BLOCK_DATA, &data); +} +EXPORT_SYMBOL(i2c_smbus_write_block_data); + +/* Returns the number of read bytes */ +s32 i2c_smbus_read_i2c_block_data(const struct i2c_client *client, u8 command, + u8 length, u8 *values) +{ + union i2c_smbus_data data; + int status; + + if (length > I2C_SMBUS_BLOCK_MAX) + length = I2C_SMBUS_BLOCK_MAX; + data.block[0] = length; + status = i2c_smbus_xfer(client->adapter, client->addr, client->flags, + I2C_SMBUS_READ, command, + I2C_SMBUS_I2C_BLOCK_DATA, &data); + if (status < 0) + return status; + + memcpy(values, &data.block[1], data.block[0]); + return data.block[0]; +} +EXPORT_SYMBOL(i2c_smbus_read_i2c_block_data); + +s32 i2c_smbus_write_i2c_block_data(const struct i2c_client *client, u8 command, + u8 length, const u8 *values) +{ + union i2c_smbus_data data; + + if (length > I2C_SMBUS_BLOCK_MAX) + length = I2C_SMBUS_BLOCK_MAX; + data.block[0] = length; + memcpy(data.block + 1, values, length); + return i2c_smbus_xfer(client->adapter, client->addr, client->flags, + I2C_SMBUS_WRITE, command, + I2C_SMBUS_I2C_BLOCK_DATA, &data); +} +EXPORT_SYMBOL(i2c_smbus_write_i2c_block_data); + +static void i2c_smbus_try_get_dmabuf(struct i2c_msg *msg, u8 init_val) +{ + bool is_read = msg->flags & I2C_M_RD; + unsigned char *dma_buf; + + dma_buf = kzalloc(I2C_SMBUS_BLOCK_MAX + (is_read ? 2 : 3), GFP_KERNEL); + if (!dma_buf) + return; + + msg->buf = dma_buf; + msg->flags |= I2C_M_DMA_SAFE; + + if (init_val) + msg->buf[0] = init_val; +} + +/* + * Simulate a SMBus command using the I2C protocol. + * No checking of parameters is done! + */ +static s32 i2c_smbus_xfer_emulated(struct i2c_adapter *adapter, u16 addr, + unsigned short flags, + char read_write, u8 command, int size, + union i2c_smbus_data *data) +{ + /* + * So we need to generate a series of msgs. In the case of writing, we + * need to use only one message; when reading, we need two. We + * initialize most things with sane defaults, to keep the code below + * somewhat simpler. + */ + unsigned char msgbuf0[I2C_SMBUS_BLOCK_MAX+3]; + unsigned char msgbuf1[I2C_SMBUS_BLOCK_MAX+2]; + int nmsgs = read_write == I2C_SMBUS_READ ? 2 : 1; + u8 partial_pec = 0; + int status; + struct i2c_msg msg[2] = { + { + .addr = addr, + .flags = flags, + .len = 1, + .buf = msgbuf0, + }, { + .addr = addr, + .flags = flags | I2C_M_RD, + .len = 0, + .buf = msgbuf1, + }, + }; + bool wants_pec = ((flags & I2C_CLIENT_PEC) && size != I2C_SMBUS_QUICK + && size != I2C_SMBUS_I2C_BLOCK_DATA); + + msgbuf0[0] = command; + switch (size) { + case I2C_SMBUS_QUICK: + msg[0].len = 0; + /* Special case: The read/write field is used as data */ + msg[0].flags = flags | (read_write == I2C_SMBUS_READ ? + I2C_M_RD : 0); + nmsgs = 1; + break; + case I2C_SMBUS_BYTE: + if (read_write == I2C_SMBUS_READ) { + /* Special case: only a read! */ + msg[0].flags = I2C_M_RD | flags; + nmsgs = 1; + } + break; + case I2C_SMBUS_BYTE_DATA: + if (read_write == I2C_SMBUS_READ) + msg[1].len = 1; + else { + msg[0].len = 2; + msgbuf0[1] = data->byte; + } + break; + case I2C_SMBUS_WORD_DATA: + if (read_write == I2C_SMBUS_READ) + msg[1].len = 2; + else { + msg[0].len = 3; + msgbuf0[1] = data->word & 0xff; + msgbuf0[2] = data->word >> 8; + } + break; + case I2C_SMBUS_PROC_CALL: + nmsgs = 2; /* Special case */ + read_write = I2C_SMBUS_READ; + msg[0].len = 3; + msg[1].len = 2; + msgbuf0[1] = data->word & 0xff; + msgbuf0[2] = data->word >> 8; + break; + case I2C_SMBUS_BLOCK_DATA: + if (read_write == I2C_SMBUS_READ) { + msg[1].flags |= I2C_M_RECV_LEN; + msg[1].len = 1; /* block length will be added by + the underlying bus driver */ + i2c_smbus_try_get_dmabuf(&msg[1], 0); + } else { + msg[0].len = data->block[0] + 2; + if (msg[0].len > I2C_SMBUS_BLOCK_MAX + 2) { + dev_err(&adapter->dev, + "Invalid block write size %d\n", + data->block[0]); + return -EINVAL; + } + + i2c_smbus_try_get_dmabuf(&msg[0], command); + memcpy(msg[0].buf + 1, data->block, msg[0].len - 1); + } + break; + case I2C_SMBUS_BLOCK_PROC_CALL: + nmsgs = 2; /* Another special case */ + read_write = I2C_SMBUS_READ; + if (data->block[0] > I2C_SMBUS_BLOCK_MAX) { + dev_err(&adapter->dev, + "Invalid block write size %d\n", + data->block[0]); + return -EINVAL; + } + + msg[0].len = data->block[0] + 2; + i2c_smbus_try_get_dmabuf(&msg[0], command); + memcpy(msg[0].buf + 1, data->block, msg[0].len - 1); + + msg[1].flags |= I2C_M_RECV_LEN; + msg[1].len = 1; /* block length will be added by + the underlying bus driver */ + i2c_smbus_try_get_dmabuf(&msg[1], 0); + break; + case I2C_SMBUS_I2C_BLOCK_DATA: + if (data->block[0] > I2C_SMBUS_BLOCK_MAX) { + dev_err(&adapter->dev, "Invalid block %s size %d\n", + read_write == I2C_SMBUS_READ ? "read" : "write", + data->block[0]); + return -EINVAL; + } + + if (read_write == I2C_SMBUS_READ) { + msg[1].len = data->block[0]; + i2c_smbus_try_get_dmabuf(&msg[1], 0); + } else { + msg[0].len = data->block[0] + 1; + + i2c_smbus_try_get_dmabuf(&msg[0], command); + memcpy(msg[0].buf + 1, data->block + 1, data->block[0]); + } + break; + default: + dev_err(&adapter->dev, "Unsupported transaction %d\n", size); + return -EOPNOTSUPP; + } + + if (wants_pec) { + /* Compute PEC if first message is a write */ + if (!(msg[0].flags & I2C_M_RD)) { + if (nmsgs == 1) /* Write only */ + i2c_smbus_add_pec(&msg[0]); + else /* Write followed by read */ + partial_pec = i2c_smbus_msg_pec(0, &msg[0]); + } + /* Ask for PEC if last message is a read */ + if (msg[nmsgs - 1].flags & I2C_M_RD) + msg[nmsgs - 1].len++; + } + + status = __i2c_transfer(adapter, msg, nmsgs); + if (status < 0) + goto cleanup; + if (status != nmsgs) { + status = -EIO; + goto cleanup; + } + status = 0; + + /* Check PEC if last message is a read */ + if (wants_pec && (msg[nmsgs - 1].flags & I2C_M_RD)) { + status = i2c_smbus_check_pec(partial_pec, &msg[nmsgs - 1]); + if (status < 0) + goto cleanup; + } + + if (read_write == I2C_SMBUS_READ) + switch (size) { + case I2C_SMBUS_BYTE: + data->byte = msgbuf0[0]; + break; + case I2C_SMBUS_BYTE_DATA: + data->byte = msgbuf1[0]; + break; + case I2C_SMBUS_WORD_DATA: + case I2C_SMBUS_PROC_CALL: + data->word = msgbuf1[0] | (msgbuf1[1] << 8); + break; + case I2C_SMBUS_I2C_BLOCK_DATA: + memcpy(data->block + 1, msg[1].buf, data->block[0]); + break; + case I2C_SMBUS_BLOCK_DATA: + case I2C_SMBUS_BLOCK_PROC_CALL: + if (msg[1].buf[0] > I2C_SMBUS_BLOCK_MAX) { + dev_err(&adapter->dev, + "Invalid block size returned: %d\n", + msg[1].buf[0]); + status = -EPROTO; + goto cleanup; + } + memcpy(data->block, msg[1].buf, msg[1].buf[0] + 1); + break; + } + +cleanup: + if (msg[0].flags & I2C_M_DMA_SAFE) + kfree(msg[0].buf); + if (msg[1].flags & I2C_M_DMA_SAFE) + kfree(msg[1].buf); + + return status; +} + +/** + * i2c_smbus_xfer - execute SMBus protocol operations + * @adapter: Handle to I2C bus + * @addr: Address of SMBus slave on that bus + * @flags: I2C_CLIENT_* flags (usually zero or I2C_CLIENT_PEC) + * @read_write: I2C_SMBUS_READ or I2C_SMBUS_WRITE + * @command: Byte interpreted by slave, for protocols which use such bytes + * @protocol: SMBus protocol operation to execute, such as I2C_SMBUS_PROC_CALL + * @data: Data to be read or written + * + * This executes an SMBus protocol operation, and returns a negative + * errno code else zero on success. + */ +s32 i2c_smbus_xfer(struct i2c_adapter *adapter, u16 addr, + unsigned short flags, char read_write, + u8 command, int protocol, union i2c_smbus_data *data) +{ + s32 res; + + res = __i2c_lock_bus_helper(adapter); + if (res) + return res; + + res = __i2c_smbus_xfer(adapter, addr, flags, read_write, + command, protocol, data); + i2c_unlock_bus(adapter, I2C_LOCK_SEGMENT); + + return res; +} +EXPORT_SYMBOL(i2c_smbus_xfer); + +s32 __i2c_smbus_xfer(struct i2c_adapter *adapter, u16 addr, + unsigned short flags, char read_write, + u8 command, int protocol, union i2c_smbus_data *data) +{ + int (*xfer_func)(struct i2c_adapter *adap, u16 addr, + unsigned short flags, char read_write, + u8 command, int size, union i2c_smbus_data *data); + unsigned long orig_jiffies; + int try; + s32 res; + + res = __i2c_check_suspended(adapter); + if (res) + return res; + + /* If enabled, the following two tracepoints are conditional on + * read_write and protocol. + */ + trace_smbus_write(adapter, addr, flags, read_write, + command, protocol, data); + trace_smbus_read(adapter, addr, flags, read_write, + command, protocol); + + flags &= I2C_M_TEN | I2C_CLIENT_PEC | I2C_CLIENT_SCCB; + + xfer_func = adapter->algo->smbus_xfer; + if (i2c_in_atomic_xfer_mode()) { + if (adapter->algo->smbus_xfer_atomic) + xfer_func = adapter->algo->smbus_xfer_atomic; + else if (adapter->algo->master_xfer_atomic) + xfer_func = NULL; /* fallback to I2C emulation */ + } + + if (xfer_func) { + /* Retry automatically on arbitration loss */ + orig_jiffies = jiffies; + for (res = 0, try = 0; try <= adapter->retries; try++) { + res = xfer_func(adapter, addr, flags, read_write, + command, protocol, data); + if (res != -EAGAIN) + break; + if (time_after(jiffies, + orig_jiffies + adapter->timeout)) + break; + } + + if (res != -EOPNOTSUPP || !adapter->algo->master_xfer) + goto trace; + /* + * Fall back to i2c_smbus_xfer_emulated if the adapter doesn't + * implement native support for the SMBus operation. + */ + } + + res = i2c_smbus_xfer_emulated(adapter, addr, flags, read_write, + command, protocol, data); + +trace: + /* If enabled, the reply tracepoint is conditional on read_write. */ + trace_smbus_reply(adapter, addr, flags, read_write, + command, protocol, data, res); + trace_smbus_result(adapter, addr, flags, read_write, + command, protocol, res); + + return res; +} +EXPORT_SYMBOL(__i2c_smbus_xfer); + +/** + * i2c_smbus_read_i2c_block_data_or_emulated - read block or emulate + * @client: Handle to slave device + * @command: Byte interpreted by slave + * @length: Size of data block; SMBus allows at most I2C_SMBUS_BLOCK_MAX bytes + * @values: Byte array into which data will be read; big enough to hold + * the data returned by the slave. SMBus allows at most + * I2C_SMBUS_BLOCK_MAX bytes. + * + * This executes the SMBus "block read" protocol if supported by the adapter. + * If block read is not supported, it emulates it using either word or byte + * read protocols depending on availability. + * + * The addresses of the I2C slave device that are accessed with this function + * must be mapped to a linear region, so that a block read will have the same + * effect as a byte read. Before using this function you must double-check + * if the I2C slave does support exchanging a block transfer with a byte + * transfer. + */ +s32 i2c_smbus_read_i2c_block_data_or_emulated(const struct i2c_client *client, + u8 command, u8 length, u8 *values) +{ + u8 i = 0; + int status; + + if (length > I2C_SMBUS_BLOCK_MAX) + length = I2C_SMBUS_BLOCK_MAX; + + if (i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_READ_I2C_BLOCK)) + return i2c_smbus_read_i2c_block_data(client, command, length, values); + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_READ_BYTE_DATA)) + return -EOPNOTSUPP; + + if (i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_READ_WORD_DATA)) { + while ((i + 2) <= length) { + status = i2c_smbus_read_word_data(client, command + i); + if (status < 0) + return status; + values[i] = status & 0xff; + values[i + 1] = status >> 8; + i += 2; + } + } + + while (i < length) { + status = i2c_smbus_read_byte_data(client, command + i); + if (status < 0) + return status; + values[i] = status; + i++; + } + + return i; +} +EXPORT_SYMBOL(i2c_smbus_read_i2c_block_data_or_emulated); + +/** + * i2c_new_smbus_alert_device - get ara client for SMBus alert support + * @adapter: the target adapter + * @setup: setup data for the SMBus alert handler + * Context: can sleep + * + * Setup handling of the SMBus alert protocol on a given I2C bus segment. + * + * Handling can be done either through our IRQ handler, or by the + * adapter (from its handler, periodic polling, or whatever). + * + * This returns the ara client, which should be saved for later use with + * i2c_handle_smbus_alert() and ultimately i2c_unregister_device(); or an + * ERRPTR to indicate an error. + */ +struct i2c_client *i2c_new_smbus_alert_device(struct i2c_adapter *adapter, + struct i2c_smbus_alert_setup *setup) +{ + struct i2c_board_info ara_board_info = { + I2C_BOARD_INFO("smbus_alert", 0x0c), + .platform_data = setup, + }; + + return i2c_new_client_device(adapter, &ara_board_info); +} +EXPORT_SYMBOL_GPL(i2c_new_smbus_alert_device); + +#if IS_ENABLED(CONFIG_I2C_SMBUS) +int i2c_setup_smbus_alert(struct i2c_adapter *adapter) +{ + struct device *parent = adapter->dev.parent; + int irq; + + /* Adapter instantiated without parent, skip the SMBus alert setup */ + if (!parent) + return 0; + + irq = device_property_match_string(parent, "interrupt-names", "smbus_alert"); + if (irq == -EINVAL || irq == -ENODATA) + return 0; + else if (irq < 0) + return irq; + + return PTR_ERR_OR_ZERO(i2c_new_smbus_alert_device(adapter, NULL)); +} +#endif |