Message ID | 20241210104524.2466586-4-tmyu0@nuvoton.com (mailing list archive) |
---|---|
State | Not Applicable |
Headers | show |
Series | Add Nuvoton NCT6694 MFD drivers | expand |
Hi Ming, > +// SPDX-License-Identifier: GPL-2.0 > +/* > + * Nuvoton NCT6694 I2C adapter driver based on USB interface. > + * > + * Copyright (C) 2024 Nuvoton Technology Corp. > + */ > + > +#include <linux/i2c.h> > +#include <linux/kernel.h> > +#include <linux/mfd/core.h> > +#include <linux/mfd/nct6694.h> > +#include <linux/module.h> > +#include <linux/platform_device.h> > + > +/* Host interface */ What does it mean "Host interface"? > +#define NCT6694_I2C_MOD 0x03 > + > +/* Message Channel*/ > +/* Command 00h */ This comments are meaningless, either make them clearer or remove them. > +#define NCT6694_I2C_CMD0_OFFSET 0x0000 /* OFFSET = SEL|CMD */ I find this comment quite meaningless. Can you please make it clearer? > +#define NCT6694_I2C_CMD0_LEN 0x90 > + > +enum i2c_baudrate { > + I2C_BR_25K = 0, > + I2C_BR_50K, > + I2C_BR_100K, > + I2C_BR_200K, > + I2C_BR_400K, > + I2C_BR_800K, > + I2C_BR_1M > +}; > + > +struct __packed nct6694_i2c_cmd0 { > + u8 port; > + u8 br; > + u8 addr; > + u8 w_cnt; > + u8 r_cnt; > + u8 rsv[11]; > + u8 write_data[0x40]; > + u8 read_data[0x40]; > +}; > + > +struct nct6694_i2c_data { > + struct nct6694 *nct6694; > + struct i2c_adapter adapter; > + unsigned char *xmit_buf; why isn't this a nct6694_i2c_cmd0 type? > + unsigned char port; > + unsigned char br; > +}; > + > +static int nct6694_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) > +{ > + struct nct6694_i2c_data *data = adap->algo_data; > + struct nct6694_i2c_cmd0 *cmd = (struct nct6694_i2c_cmd0 *)data->xmit_buf; > + int ret, i; > + > + for (i = 0; i < num ; i++) { > + struct i2c_msg *msg_temp = &msgs[i]; > + > + memset(data->xmit_buf, 0, sizeof(struct nct6694_i2c_cmd0)); > + > + if (msg_temp->len > 64) > + return -EPROTO; > + cmd->port = data->port; > + cmd->br = data->br; > + cmd->addr = i2c_8bit_addr_from_msg(msg_temp); > + if (msg_temp->flags & I2C_M_RD) { > + cmd->r_cnt = msg_temp->len; > + ret = nct6694_write_msg(data->nct6694, NCT6694_I2C_MOD, > + NCT6694_I2C_CMD0_OFFSET, > + NCT6694_I2C_CMD0_LEN, > + cmd); > + if (ret < 0) > + return 0; why not return ret? > + > + memcpy(msg_temp->buf, cmd->read_data, msg_temp->len); > + } else { > + cmd->w_cnt = msg_temp->len; > + memcpy(cmd->write_data, msg_temp->buf, msg_temp->len); > + ret = nct6694_write_msg(data->nct6694, NCT6694_I2C_MOD, > + NCT6694_I2C_CMD0_OFFSET, > + NCT6694_I2C_CMD0_LEN, > + cmd); > + if (ret < 0) > + return 0; > + } > + } > + > + return num; > +} > + > +static u32 nct6694_func(struct i2c_adapter *adapter) > +{ > + return (I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL); parenthesis are not needed. > +} ... > +static struct platform_driver nct6694_i2c_driver = { > + .driver = { > + .name = "nct6694-i2c", > + }, > + .probe = nct6694_i2c_probe, > + .remove = nct6694_i2c_remove, > +}; > + > +module_platform_driver(nct6694_i2c_driver); what I meant in v1 is to try using module_auxiliary_driver(). Check, e.g., i2c-ljca.c or i2c-keba.c. Andi > +MODULE_DESCRIPTION("USB-I2C adapter driver for NCT6694"); > +MODULE_AUTHOR("Ming Yu <tmyu0@nuvoton.com>"); > +MODULE_LICENSE("GPL"); > +MODULE_ALIAS("platform:nct6694-i2c"); > -- > 2.34.1 >
Dear Andi, Thank you for your comments, Andi Shyti <andi.shyti@kernel.org> 於 2024年12月26日 週四 上午8:43寫道: > > > +#include <linux/i2c.h> > > +#include <linux/kernel.h> > > +#include <linux/mfd/core.h> > > +#include <linux/mfd/nct6694.h> > > +#include <linux/module.h> > > +#include <linux/platform_device.h> > > + > > +/* Host interface */ > > What does it mean "Host interface"? > > > +#define NCT6694_I2C_MOD 0x03 > > + > > +/* Message Channel*/ > > +/* Command 00h */ > > This comments are meaningless, either make them clearer or remove > them. > > > +#define NCT6694_I2C_CMD0_OFFSET 0x0000 /* OFFSET = SEL|CMD */ > > I find this comment quite meaningless. Can you please make it > clearer? > I have already updated these structures and comments following suggestions from other reviewers, and I plan to include the changes in the next patch submission. > > +#define NCT6694_I2C_CMD0_LEN 0x90 > > + > > +enum i2c_baudrate { > > + I2C_BR_25K = 0, > > + I2C_BR_50K, > > + I2C_BR_100K, > > + I2C_BR_200K, > > + I2C_BR_400K, > > + I2C_BR_800K, > > + I2C_BR_1M > > +}; > > + > > +struct __packed nct6694_i2c_cmd0 { > > + u8 port; > > + u8 br; > > + u8 addr; > > + u8 w_cnt; > > + u8 r_cnt; > > + u8 rsv[11]; > > + u8 write_data[0x40]; > > + u8 read_data[0x40]; > > +}; > > + > > +struct nct6694_i2c_data { > > + struct nct6694 *nct6694; > > + struct i2c_adapter adapter; > > + unsigned char *xmit_buf; > > why isn't this a nct6694_i2c_cmd0 type? > Fix it in v4. > > + unsigned char port; > > + unsigned char br; > > +}; > > + > > +static int nct6694_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) > > +{ > > + struct nct6694_i2c_data *data = adap->algo_data; > > + struct nct6694_i2c_cmd0 *cmd = (struct nct6694_i2c_cmd0 *)data->xmit_buf; > > + int ret, i; > > + > > + for (i = 0; i < num ; i++) { > > + struct i2c_msg *msg_temp = &msgs[i]; > > + > > + memset(data->xmit_buf, 0, sizeof(struct nct6694_i2c_cmd0)); > > + > > + if (msg_temp->len > 64) > > + return -EPROTO; > > + cmd->port = data->port; > > + cmd->br = data->br; > > + cmd->addr = i2c_8bit_addr_from_msg(msg_temp); > > + if (msg_temp->flags & I2C_M_RD) { > > + cmd->r_cnt = msg_temp->len; > > + ret = nct6694_write_msg(data->nct6694, NCT6694_I2C_MOD, > > + NCT6694_I2C_CMD0_OFFSET, > > + NCT6694_I2C_CMD0_LEN, > > + cmd); > > + if (ret < 0) > > + return 0; > > why not return ret? > Fix it in v4. > > + > > + memcpy(msg_temp->buf, cmd->read_data, msg_temp->len); > > + } else { > > + cmd->w_cnt = msg_temp->len; > > + memcpy(cmd->write_data, msg_temp->buf, msg_temp->len); > > + ret = nct6694_write_msg(data->nct6694, NCT6694_I2C_MOD, > > + NCT6694_I2C_CMD0_OFFSET, > > + NCT6694_I2C_CMD0_LEN, > > + cmd); > > + if (ret < 0) > > + return 0; > > + } > > + } > > + > > + return num; > > +} > > + > > +static u32 nct6694_func(struct i2c_adapter *adapter) > > +{ > > + return (I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL); > > parenthesis are not needed. > Fix it in v4. > > +} > > ... > > > +static struct platform_driver nct6694_i2c_driver = { > > + .driver = { > > + .name = "nct6694-i2c", > > + }, > > + .probe = nct6694_i2c_probe, > > + .remove = nct6694_i2c_remove, > > +}; > > + > > +module_platform_driver(nct6694_i2c_driver); > > what I meant in v1 is to try using module_auxiliary_driver(). > Check, e.g., i2c-ljca.c or i2c-keba.c. > I think the NCT6694 is an MCU-based device, and the current implementation is as an MFD driver. Are you suggesting it should instead be implemented as an auxiliary device driver? If so, would that mean all related drivers need to be revised accordingly? Best regards, Ming
Hi Ming, > > > +static struct platform_driver nct6694_i2c_driver = { > > > + .driver = { > > > + .name = "nct6694-i2c", > > > + }, > > > + .probe = nct6694_i2c_probe, > > > + .remove = nct6694_i2c_remove, > > > +}; > > > + > > > +module_platform_driver(nct6694_i2c_driver); > > > > what I meant in v1 is to try using module_auxiliary_driver(). > > Check, e.g., i2c-ljca.c or i2c-keba.c. > > I think the NCT6694 is an MCU-based device, and the current > implementation is as an MFD driver. Are you suggesting it should > instead be implemented as an auxiliary device driver? If so, would > that mean all related drivers need to be revised accordingly? No worries, module_platform_driver() is also fine. Andi
diff --git a/MAINTAINERS b/MAINTAINERS index 6688c5c470b7..a190f2b08fa3 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -16546,6 +16546,7 @@ M: Ming Yu <tmyu0@nuvoton.com> L: linux-kernel@vger.kernel.org S: Supported F: drivers/gpio/gpio-nct6694.c +F: drivers/i2c/busses/i2c-nct6694.c F: drivers/mfd/nct6694.c F: include/linux/mfd/nct6694.h diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig index 4977abcd7c46..1962cf1e71f9 100644 --- a/drivers/i2c/busses/Kconfig +++ b/drivers/i2c/busses/Kconfig @@ -1325,6 +1325,16 @@ config I2C_LJCA This driver can also be built as a module. If so, the module will be called i2c-ljca. +config I2C_NCT6694 + tristate "Nuvoton NCT6694 I2C adapter support" + depends on MFD_NCT6694 + help + If you say yes to this option, support will be included for Nuvoton + NCT6694, a USB to I2C interface. + + This driver can also be built as a module. If so, the module will + be called i2c-nct6694. + config I2C_CP2615 tristate "Silicon Labs CP2615 USB sound card and I2C adapter" depends on USB diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile index a6bcbf2febcf..6d2fd8e56569 100644 --- a/drivers/i2c/busses/Makefile +++ b/drivers/i2c/busses/Makefile @@ -135,6 +135,7 @@ obj-$(CONFIG_I2C_GXP) += i2c-gxp.o obj-$(CONFIG_I2C_DIOLAN_U2C) += i2c-diolan-u2c.o obj-$(CONFIG_I2C_DLN2) += i2c-dln2.o obj-$(CONFIG_I2C_LJCA) += i2c-ljca.o +obj-$(CONFIG_I2C_NCT6694) += i2c-nct6694.o obj-$(CONFIG_I2C_CP2615) += i2c-cp2615.o obj-$(CONFIG_I2C_PARPORT) += i2c-parport.o obj-$(CONFIG_I2C_PCI1XXXX) += i2c-mchp-pci1xxxx.o diff --git a/drivers/i2c/busses/i2c-nct6694.c b/drivers/i2c/busses/i2c-nct6694.c new file mode 100644 index 000000000000..d35e1ea3521f --- /dev/null +++ b/drivers/i2c/busses/i2c-nct6694.c @@ -0,0 +1,153 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Nuvoton NCT6694 I2C adapter driver based on USB interface. + * + * Copyright (C) 2024 Nuvoton Technology Corp. + */ + +#include <linux/i2c.h> +#include <linux/kernel.h> +#include <linux/mfd/core.h> +#include <linux/mfd/nct6694.h> +#include <linux/module.h> +#include <linux/platform_device.h> + +/* Host interface */ +#define NCT6694_I2C_MOD 0x03 + +/* Message Channel*/ +/* Command 00h */ +#define NCT6694_I2C_CMD0_OFFSET 0x0000 /* OFFSET = SEL|CMD */ +#define NCT6694_I2C_CMD0_LEN 0x90 + +enum i2c_baudrate { + I2C_BR_25K = 0, + I2C_BR_50K, + I2C_BR_100K, + I2C_BR_200K, + I2C_BR_400K, + I2C_BR_800K, + I2C_BR_1M +}; + +struct __packed nct6694_i2c_cmd0 { + u8 port; + u8 br; + u8 addr; + u8 w_cnt; + u8 r_cnt; + u8 rsv[11]; + u8 write_data[0x40]; + u8 read_data[0x40]; +}; + +struct nct6694_i2c_data { + struct nct6694 *nct6694; + struct i2c_adapter adapter; + unsigned char *xmit_buf; + unsigned char port; + unsigned char br; +}; + +static int nct6694_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) +{ + struct nct6694_i2c_data *data = adap->algo_data; + struct nct6694_i2c_cmd0 *cmd = (struct nct6694_i2c_cmd0 *)data->xmit_buf; + int ret, i; + + for (i = 0; i < num ; i++) { + struct i2c_msg *msg_temp = &msgs[i]; + + memset(data->xmit_buf, 0, sizeof(struct nct6694_i2c_cmd0)); + + if (msg_temp->len > 64) + return -EPROTO; + cmd->port = data->port; + cmd->br = data->br; + cmd->addr = i2c_8bit_addr_from_msg(msg_temp); + if (msg_temp->flags & I2C_M_RD) { + cmd->r_cnt = msg_temp->len; + ret = nct6694_write_msg(data->nct6694, NCT6694_I2C_MOD, + NCT6694_I2C_CMD0_OFFSET, + NCT6694_I2C_CMD0_LEN, + cmd); + if (ret < 0) + return 0; + + memcpy(msg_temp->buf, cmd->read_data, msg_temp->len); + } else { + cmd->w_cnt = msg_temp->len; + memcpy(cmd->write_data, msg_temp->buf, msg_temp->len); + ret = nct6694_write_msg(data->nct6694, NCT6694_I2C_MOD, + NCT6694_I2C_CMD0_OFFSET, + NCT6694_I2C_CMD0_LEN, + cmd); + if (ret < 0) + return 0; + } + } + + return num; +} + +static u32 nct6694_func(struct i2c_adapter *adapter) +{ + return (I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL); +} + +static const struct i2c_algorithm algorithm = { + .master_xfer = nct6694_xfer, + .functionality = nct6694_func, +}; + +static int nct6694_i2c_probe(struct platform_device *pdev) +{ + const struct mfd_cell *cell = mfd_get_cell(pdev); + struct nct6694 *nct6694 = dev_get_drvdata(pdev->dev.parent); + struct nct6694_i2c_data *data; + + data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + data->xmit_buf = devm_kcalloc(&pdev->dev, NCT6694_MAX_PACKET_SZ, + sizeof(unsigned char), GFP_KERNEL); + if (!data->xmit_buf) + return -ENOMEM; + + data->nct6694 = nct6694; + data->port = cell->id; + data->br = I2C_BR_100K; + + sprintf(data->adapter.name, "NCT6694 I2C Adapter %d", cell->id); + data->adapter.owner = THIS_MODULE; + data->adapter.algo = &algorithm; + data->adapter.dev.parent = &pdev->dev; + data->adapter.algo_data = data; + + platform_set_drvdata(pdev, data); + + return i2c_add_adapter(&data->adapter); +} + +static void nct6694_i2c_remove(struct platform_device *pdev) +{ + struct nct6694_i2c_data *data = platform_get_drvdata(pdev); + + i2c_del_adapter(&data->adapter); +} + +static struct platform_driver nct6694_i2c_driver = { + .driver = { + .name = "nct6694-i2c", + }, + .probe = nct6694_i2c_probe, + .remove = nct6694_i2c_remove, +}; + +module_platform_driver(nct6694_i2c_driver); + +MODULE_DESCRIPTION("USB-I2C adapter driver for NCT6694"); +MODULE_AUTHOR("Ming Yu <tmyu0@nuvoton.com>"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:nct6694-i2c");
This driver supports I2C adapter functionality for NCT6694 MFD device based on USB interface, each I2C controller use default baudrate(100K). Signed-off-by: Ming Yu <tmyu0@nuvoton.com> --- MAINTAINERS | 1 + drivers/i2c/busses/Kconfig | 10 ++ drivers/i2c/busses/Makefile | 1 + drivers/i2c/busses/i2c-nct6694.c | 153 +++++++++++++++++++++++++++++++ 4 files changed, 165 insertions(+) create mode 100644 drivers/i2c/busses/i2c-nct6694.c