mirror of https://github.com/torvalds/linux.git
mfd: Add core driver for Nuvoton NCT6694
The Nuvoton NCT6694 provides an USB interface to the host to access its features. Sub-devices can use the USB functions nct6694_read_msg() and nct6694_write_msg() to issue a command. They can also request interrupt that will be called when the USB device receives its interrupt pipe. Signed-off-by: Ming Yu <a0282524688@gmail.com> Link: https://lore.kernel.org/r/20250912091952.1169369-2-a0282524688@gmail.com Signed-off-by: Lee Jones <lee@kernel.org>
This commit is contained in:
parent
8f5ae30d69
commit
51dad33ede
|
|
@ -18082,6 +18082,12 @@ F: drivers/nubus/
|
|||
F: include/linux/nubus.h
|
||||
F: include/uapi/linux/nubus.h
|
||||
|
||||
NUVOTON NCT6694 MFD DRIVER
|
||||
M: Ming Yu <tmyu0@nuvoton.com>
|
||||
S: Supported
|
||||
F: drivers/mfd/nct6694.c
|
||||
F: include/linux/mfd/nct6694.h
|
||||
|
||||
NUVOTON NCT7201 IIO DRIVER
|
||||
M: Eason Yang <j2anfernee@gmail.com>
|
||||
L: linux-iio@vger.kernel.org
|
||||
|
|
|
|||
|
|
@ -1134,6 +1134,21 @@ config MFD_MENF21BMC
|
|||
This driver can also be built as a module. If so the module
|
||||
will be called menf21bmc.
|
||||
|
||||
config MFD_NCT6694
|
||||
tristate "Nuvoton NCT6694 support"
|
||||
select MFD_CORE
|
||||
depends on USB
|
||||
help
|
||||
This enables support for the Nuvoton USB device NCT6694, which shares
|
||||
peripherals.
|
||||
The Nuvoton NCT6694 is a peripheral expander with 16 GPIO chips,
|
||||
6 I2C controllers, 2 CANfd controllers, 2 Watchdog timers, ADC,
|
||||
PWM, and RTC.
|
||||
This driver provides core APIs to access the NCT6694 hardware
|
||||
monitoring and control features.
|
||||
Additional drivers must be enabled to utilize the specific
|
||||
functionalities of the device.
|
||||
|
||||
config MFD_OCELOT
|
||||
tristate "Microsemi Ocelot External Control Support"
|
||||
depends on SPI_MASTER
|
||||
|
|
|
|||
|
|
@ -121,6 +121,8 @@ obj-$(CONFIG_MFD_MC13XXX) += mc13xxx-core.o
|
|||
obj-$(CONFIG_MFD_MC13XXX_SPI) += mc13xxx-spi.o
|
||||
obj-$(CONFIG_MFD_MC13XXX_I2C) += mc13xxx-i2c.o
|
||||
|
||||
obj-$(CONFIG_MFD_NCT6694) += nct6694.o
|
||||
|
||||
obj-$(CONFIG_MFD_CORE) += mfd-core.o
|
||||
|
||||
ocelot-soc-objs := ocelot-core.o ocelot-spi.o
|
||||
|
|
|
|||
|
|
@ -0,0 +1,388 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Copyright (C) 2025 Nuvoton Technology Corp.
|
||||
*
|
||||
* Nuvoton NCT6694 core driver using USB interface to provide
|
||||
* access to the NCT6694 hardware monitoring and control features.
|
||||
*
|
||||
* The NCT6694 is an integrated controller that provides GPIO, I2C,
|
||||
* CAN, WDT, HWMON and RTC management.
|
||||
*/
|
||||
|
||||
#include <linux/bits.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/idr.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/irqdomain.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/mfd/core.h>
|
||||
#include <linux/mfd/nct6694.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/usb.h>
|
||||
|
||||
static const struct mfd_cell nct6694_devs[] = {
|
||||
MFD_CELL_NAME("nct6694-gpio"),
|
||||
MFD_CELL_NAME("nct6694-gpio"),
|
||||
MFD_CELL_NAME("nct6694-gpio"),
|
||||
MFD_CELL_NAME("nct6694-gpio"),
|
||||
MFD_CELL_NAME("nct6694-gpio"),
|
||||
MFD_CELL_NAME("nct6694-gpio"),
|
||||
MFD_CELL_NAME("nct6694-gpio"),
|
||||
MFD_CELL_NAME("nct6694-gpio"),
|
||||
MFD_CELL_NAME("nct6694-gpio"),
|
||||
MFD_CELL_NAME("nct6694-gpio"),
|
||||
MFD_CELL_NAME("nct6694-gpio"),
|
||||
MFD_CELL_NAME("nct6694-gpio"),
|
||||
MFD_CELL_NAME("nct6694-gpio"),
|
||||
MFD_CELL_NAME("nct6694-gpio"),
|
||||
MFD_CELL_NAME("nct6694-gpio"),
|
||||
MFD_CELL_NAME("nct6694-gpio"),
|
||||
|
||||
MFD_CELL_NAME("nct6694-i2c"),
|
||||
MFD_CELL_NAME("nct6694-i2c"),
|
||||
MFD_CELL_NAME("nct6694-i2c"),
|
||||
MFD_CELL_NAME("nct6694-i2c"),
|
||||
MFD_CELL_NAME("nct6694-i2c"),
|
||||
MFD_CELL_NAME("nct6694-i2c"),
|
||||
|
||||
MFD_CELL_NAME("nct6694-canfd"),
|
||||
MFD_CELL_NAME("nct6694-canfd"),
|
||||
|
||||
MFD_CELL_NAME("nct6694-wdt"),
|
||||
MFD_CELL_NAME("nct6694-wdt"),
|
||||
|
||||
MFD_CELL_NAME("nct6694-hwmon"),
|
||||
|
||||
MFD_CELL_NAME("nct6694-rtc"),
|
||||
};
|
||||
|
||||
static int nct6694_response_err_handling(struct nct6694 *nct6694, unsigned char err_status)
|
||||
{
|
||||
switch (err_status) {
|
||||
case NCT6694_NO_ERROR:
|
||||
return 0;
|
||||
case NCT6694_NOT_SUPPORT_ERROR:
|
||||
dev_err(nct6694->dev, "Command is not supported!\n");
|
||||
break;
|
||||
case NCT6694_NO_RESPONSE_ERROR:
|
||||
dev_warn(nct6694->dev, "Command received no response!\n");
|
||||
break;
|
||||
case NCT6694_TIMEOUT_ERROR:
|
||||
dev_warn(nct6694->dev, "Command timed out!\n");
|
||||
break;
|
||||
case NCT6694_PENDING:
|
||||
dev_err(nct6694->dev, "Command is pending!\n");
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
/**
|
||||
* nct6694_read_msg() - Read message from NCT6694 device
|
||||
* @nct6694: NCT6694 device pointer
|
||||
* @cmd_hd: command header structure
|
||||
* @buf: buffer to store the response data
|
||||
*
|
||||
* Sends a command to the NCT6694 device and reads the response.
|
||||
* The command header is specified in @cmd_hd, and the response
|
||||
* data is stored in @buf.
|
||||
*
|
||||
* Return: Negative value on error or 0 on success.
|
||||
*/
|
||||
int nct6694_read_msg(struct nct6694 *nct6694, const struct nct6694_cmd_header *cmd_hd, void *buf)
|
||||
{
|
||||
union nct6694_usb_msg *msg = nct6694->usb_msg;
|
||||
struct usb_device *udev = nct6694->udev;
|
||||
int tx_len, rx_len, ret;
|
||||
|
||||
guard(mutex)(&nct6694->access_lock);
|
||||
|
||||
memcpy(&msg->cmd_header, cmd_hd, sizeof(*cmd_hd));
|
||||
msg->cmd_header.hctrl = NCT6694_HCTRL_GET;
|
||||
|
||||
/* Send command packet to USB device */
|
||||
ret = usb_bulk_msg(udev, usb_sndbulkpipe(udev, NCT6694_BULK_OUT_EP), &msg->cmd_header,
|
||||
sizeof(*msg), &tx_len, NCT6694_URB_TIMEOUT);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Receive response packet from USB device */
|
||||
ret = usb_bulk_msg(udev, usb_rcvbulkpipe(udev, NCT6694_BULK_IN_EP), &msg->response_header,
|
||||
sizeof(*msg), &rx_len, NCT6694_URB_TIMEOUT);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Receive data packet from USB device */
|
||||
ret = usb_bulk_msg(udev, usb_rcvbulkpipe(udev, NCT6694_BULK_IN_EP), buf,
|
||||
le16_to_cpu(cmd_hd->len), &rx_len, NCT6694_URB_TIMEOUT);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (rx_len != le16_to_cpu(cmd_hd->len)) {
|
||||
dev_err(nct6694->dev, "Expected received length %d, but got %d\n",
|
||||
le16_to_cpu(cmd_hd->len), rx_len);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
return nct6694_response_err_handling(nct6694, msg->response_header.sts);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(nct6694_read_msg);
|
||||
|
||||
/**
|
||||
* nct6694_write_msg() - Write message to NCT6694 device
|
||||
* @nct6694: NCT6694 device pointer
|
||||
* @cmd_hd: command header structure
|
||||
* @buf: buffer containing the data to be sent
|
||||
*
|
||||
* Sends a command to the NCT6694 device and writes the data
|
||||
* from @buf. The command header is specified in @cmd_hd.
|
||||
*
|
||||
* Return: Negative value on error or 0 on success.
|
||||
*/
|
||||
int nct6694_write_msg(struct nct6694 *nct6694, const struct nct6694_cmd_header *cmd_hd, void *buf)
|
||||
{
|
||||
union nct6694_usb_msg *msg = nct6694->usb_msg;
|
||||
struct usb_device *udev = nct6694->udev;
|
||||
int tx_len, rx_len, ret;
|
||||
|
||||
guard(mutex)(&nct6694->access_lock);
|
||||
|
||||
memcpy(&msg->cmd_header, cmd_hd, sizeof(*cmd_hd));
|
||||
msg->cmd_header.hctrl = NCT6694_HCTRL_SET;
|
||||
|
||||
/* Send command packet to USB device */
|
||||
ret = usb_bulk_msg(udev, usb_sndbulkpipe(udev, NCT6694_BULK_OUT_EP), &msg->cmd_header,
|
||||
sizeof(*msg), &tx_len, NCT6694_URB_TIMEOUT);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Send data packet to USB device */
|
||||
ret = usb_bulk_msg(udev, usb_sndbulkpipe(udev, NCT6694_BULK_OUT_EP), buf,
|
||||
le16_to_cpu(cmd_hd->len), &tx_len, NCT6694_URB_TIMEOUT);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Receive response packet from USB device */
|
||||
ret = usb_bulk_msg(udev, usb_rcvbulkpipe(udev, NCT6694_BULK_IN_EP), &msg->response_header,
|
||||
sizeof(*msg), &rx_len, NCT6694_URB_TIMEOUT);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Receive data packet from USB device */
|
||||
ret = usb_bulk_msg(udev, usb_rcvbulkpipe(udev, NCT6694_BULK_IN_EP), buf,
|
||||
le16_to_cpu(cmd_hd->len), &rx_len, NCT6694_URB_TIMEOUT);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (rx_len != le16_to_cpu(cmd_hd->len)) {
|
||||
dev_err(nct6694->dev, "Expected transmitted length %d, but got %d\n",
|
||||
le16_to_cpu(cmd_hd->len), rx_len);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
return nct6694_response_err_handling(nct6694, msg->response_header.sts);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(nct6694_write_msg);
|
||||
|
||||
static void usb_int_callback(struct urb *urb)
|
||||
{
|
||||
struct nct6694 *nct6694 = urb->context;
|
||||
__le32 *status_le = urb->transfer_buffer;
|
||||
u32 int_status;
|
||||
int ret;
|
||||
|
||||
switch (urb->status) {
|
||||
case 0:
|
||||
break;
|
||||
case -ECONNRESET:
|
||||
case -ENOENT:
|
||||
case -ESHUTDOWN:
|
||||
return;
|
||||
default:
|
||||
goto resubmit;
|
||||
}
|
||||
|
||||
int_status = le32_to_cpu(*status_le);
|
||||
|
||||
while (int_status) {
|
||||
int irq = __ffs(int_status);
|
||||
|
||||
generic_handle_irq_safe(irq_find_mapping(nct6694->domain, irq));
|
||||
int_status &= ~BIT(irq);
|
||||
}
|
||||
|
||||
resubmit:
|
||||
ret = usb_submit_urb(urb, GFP_ATOMIC);
|
||||
if (ret)
|
||||
dev_warn(nct6694->dev, "Failed to resubmit urb, status %pe", ERR_PTR(ret));
|
||||
}
|
||||
|
||||
static void nct6694_irq_enable(struct irq_data *data)
|
||||
{
|
||||
struct nct6694 *nct6694 = irq_data_get_irq_chip_data(data);
|
||||
irq_hw_number_t hwirq = irqd_to_hwirq(data);
|
||||
|
||||
guard(spinlock_irqsave)(&nct6694->irq_lock);
|
||||
|
||||
nct6694->irq_enable |= BIT(hwirq);
|
||||
}
|
||||
|
||||
static void nct6694_irq_disable(struct irq_data *data)
|
||||
{
|
||||
struct nct6694 *nct6694 = irq_data_get_irq_chip_data(data);
|
||||
irq_hw_number_t hwirq = irqd_to_hwirq(data);
|
||||
|
||||
guard(spinlock_irqsave)(&nct6694->irq_lock);
|
||||
|
||||
nct6694->irq_enable &= ~BIT(hwirq);
|
||||
}
|
||||
|
||||
static const struct irq_chip nct6694_irq_chip = {
|
||||
.name = "nct6694-irq",
|
||||
.flags = IRQCHIP_SKIP_SET_WAKE,
|
||||
.irq_enable = nct6694_irq_enable,
|
||||
.irq_disable = nct6694_irq_disable,
|
||||
};
|
||||
|
||||
static int nct6694_irq_domain_map(struct irq_domain *d, unsigned int irq, irq_hw_number_t hw)
|
||||
{
|
||||
struct nct6694 *nct6694 = d->host_data;
|
||||
|
||||
irq_set_chip_data(irq, nct6694);
|
||||
irq_set_chip_and_handler(irq, &nct6694_irq_chip, handle_simple_irq);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void nct6694_irq_domain_unmap(struct irq_domain *d, unsigned int irq)
|
||||
{
|
||||
irq_set_chip_and_handler(irq, NULL, NULL);
|
||||
irq_set_chip_data(irq, NULL);
|
||||
}
|
||||
|
||||
static const struct irq_domain_ops nct6694_irq_domain_ops = {
|
||||
.map = nct6694_irq_domain_map,
|
||||
.unmap = nct6694_irq_domain_unmap,
|
||||
};
|
||||
|
||||
static int nct6694_usb_probe(struct usb_interface *iface,
|
||||
const struct usb_device_id *id)
|
||||
{
|
||||
struct usb_device *udev = interface_to_usbdev(iface);
|
||||
struct usb_endpoint_descriptor *int_endpoint;
|
||||
struct usb_host_interface *interface;
|
||||
struct device *dev = &iface->dev;
|
||||
struct nct6694 *nct6694;
|
||||
int ret;
|
||||
|
||||
nct6694 = devm_kzalloc(dev, sizeof(*nct6694), GFP_KERNEL);
|
||||
if (!nct6694)
|
||||
return -ENOMEM;
|
||||
|
||||
nct6694->usb_msg = devm_kzalloc(dev, sizeof(union nct6694_usb_msg), GFP_KERNEL);
|
||||
if (!nct6694->usb_msg)
|
||||
return -ENOMEM;
|
||||
|
||||
nct6694->int_buffer = devm_kzalloc(dev, sizeof(*nct6694->int_buffer), GFP_KERNEL);
|
||||
if (!nct6694->int_buffer)
|
||||
return -ENOMEM;
|
||||
|
||||
nct6694->int_in_urb = usb_alloc_urb(0, GFP_KERNEL);
|
||||
if (!nct6694->int_in_urb)
|
||||
return -ENOMEM;
|
||||
|
||||
nct6694->domain = irq_domain_create_simple(NULL, NCT6694_NR_IRQS, 0,
|
||||
&nct6694_irq_domain_ops,
|
||||
nct6694);
|
||||
if (!nct6694->domain) {
|
||||
ret = -ENODEV;
|
||||
goto err_urb;
|
||||
}
|
||||
|
||||
nct6694->dev = dev;
|
||||
nct6694->udev = udev;
|
||||
|
||||
ida_init(&nct6694->gpio_ida);
|
||||
ida_init(&nct6694->i2c_ida);
|
||||
ida_init(&nct6694->canfd_ida);
|
||||
ida_init(&nct6694->wdt_ida);
|
||||
|
||||
spin_lock_init(&nct6694->irq_lock);
|
||||
|
||||
ret = devm_mutex_init(dev, &nct6694->access_lock);
|
||||
if (ret)
|
||||
goto err_ida;
|
||||
|
||||
interface = iface->cur_altsetting;
|
||||
|
||||
int_endpoint = &interface->endpoint[0].desc;
|
||||
if (!usb_endpoint_is_int_in(int_endpoint)) {
|
||||
ret = -ENODEV;
|
||||
goto err_ida;
|
||||
}
|
||||
|
||||
usb_fill_int_urb(nct6694->int_in_urb, udev, usb_rcvintpipe(udev, NCT6694_INT_IN_EP),
|
||||
nct6694->int_buffer, sizeof(*nct6694->int_buffer), usb_int_callback,
|
||||
nct6694, int_endpoint->bInterval);
|
||||
|
||||
ret = usb_submit_urb(nct6694->int_in_urb, GFP_KERNEL);
|
||||
if (ret)
|
||||
goto err_ida;
|
||||
|
||||
usb_set_intfdata(iface, nct6694);
|
||||
|
||||
ret = mfd_add_hotplug_devices(dev, nct6694_devs, ARRAY_SIZE(nct6694_devs));
|
||||
if (ret)
|
||||
goto err_mfd;
|
||||
|
||||
return 0;
|
||||
|
||||
err_mfd:
|
||||
usb_kill_urb(nct6694->int_in_urb);
|
||||
err_ida:
|
||||
ida_destroy(&nct6694->wdt_ida);
|
||||
ida_destroy(&nct6694->canfd_ida);
|
||||
ida_destroy(&nct6694->i2c_ida);
|
||||
ida_destroy(&nct6694->gpio_ida);
|
||||
irq_domain_remove(nct6694->domain);
|
||||
err_urb:
|
||||
usb_free_urb(nct6694->int_in_urb);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void nct6694_usb_disconnect(struct usb_interface *iface)
|
||||
{
|
||||
struct nct6694 *nct6694 = usb_get_intfdata(iface);
|
||||
|
||||
mfd_remove_devices(nct6694->dev);
|
||||
usb_kill_urb(nct6694->int_in_urb);
|
||||
ida_destroy(&nct6694->wdt_ida);
|
||||
ida_destroy(&nct6694->canfd_ida);
|
||||
ida_destroy(&nct6694->i2c_ida);
|
||||
ida_destroy(&nct6694->gpio_ida);
|
||||
irq_domain_remove(nct6694->domain);
|
||||
usb_free_urb(nct6694->int_in_urb);
|
||||
}
|
||||
|
||||
static const struct usb_device_id nct6694_ids[] = {
|
||||
{ USB_DEVICE_AND_INTERFACE_INFO(NCT6694_VENDOR_ID, NCT6694_PRODUCT_ID, 0xFF, 0x00, 0x00) },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(usb, nct6694_ids);
|
||||
|
||||
static struct usb_driver nct6694_usb_driver = {
|
||||
.name = "nct6694",
|
||||
.id_table = nct6694_ids,
|
||||
.probe = nct6694_usb_probe,
|
||||
.disconnect = nct6694_usb_disconnect,
|
||||
};
|
||||
module_usb_driver(nct6694_usb_driver);
|
||||
|
||||
MODULE_DESCRIPTION("Nuvoton NCT6694 core driver");
|
||||
MODULE_AUTHOR("Ming Yu <tmyu0@nuvoton.com>");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
|
@ -0,0 +1,102 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* Copyright (C) 2025 Nuvoton Technology Corp.
|
||||
*
|
||||
* Nuvoton NCT6694 USB transaction and data structure.
|
||||
*/
|
||||
|
||||
#ifndef __MFD_NCT6694_H
|
||||
#define __MFD_NCT6694_H
|
||||
|
||||
#define NCT6694_VENDOR_ID 0x0416
|
||||
#define NCT6694_PRODUCT_ID 0x200B
|
||||
#define NCT6694_INT_IN_EP 0x81
|
||||
#define NCT6694_BULK_IN_EP 0x02
|
||||
#define NCT6694_BULK_OUT_EP 0x03
|
||||
|
||||
#define NCT6694_HCTRL_SET 0x40
|
||||
#define NCT6694_HCTRL_GET 0x80
|
||||
|
||||
#define NCT6694_URB_TIMEOUT 1000
|
||||
|
||||
enum nct6694_irq_id {
|
||||
NCT6694_IRQ_GPIO0 = 0,
|
||||
NCT6694_IRQ_GPIO1,
|
||||
NCT6694_IRQ_GPIO2,
|
||||
NCT6694_IRQ_GPIO3,
|
||||
NCT6694_IRQ_GPIO4,
|
||||
NCT6694_IRQ_GPIO5,
|
||||
NCT6694_IRQ_GPIO6,
|
||||
NCT6694_IRQ_GPIO7,
|
||||
NCT6694_IRQ_GPIO8,
|
||||
NCT6694_IRQ_GPIO9,
|
||||
NCT6694_IRQ_GPIOA,
|
||||
NCT6694_IRQ_GPIOB,
|
||||
NCT6694_IRQ_GPIOC,
|
||||
NCT6694_IRQ_GPIOD,
|
||||
NCT6694_IRQ_GPIOE,
|
||||
NCT6694_IRQ_GPIOF,
|
||||
NCT6694_IRQ_CAN0,
|
||||
NCT6694_IRQ_CAN1,
|
||||
NCT6694_IRQ_RTC,
|
||||
NCT6694_NR_IRQS,
|
||||
};
|
||||
|
||||
enum nct6694_response_err_status {
|
||||
NCT6694_NO_ERROR = 0,
|
||||
NCT6694_FORMAT_ERROR,
|
||||
NCT6694_RESERVED1,
|
||||
NCT6694_RESERVED2,
|
||||
NCT6694_NOT_SUPPORT_ERROR,
|
||||
NCT6694_NO_RESPONSE_ERROR,
|
||||
NCT6694_TIMEOUT_ERROR,
|
||||
NCT6694_PENDING,
|
||||
};
|
||||
|
||||
struct __packed nct6694_cmd_header {
|
||||
u8 rsv1;
|
||||
u8 mod;
|
||||
union __packed {
|
||||
__le16 offset;
|
||||
struct __packed {
|
||||
u8 cmd;
|
||||
u8 sel;
|
||||
};
|
||||
};
|
||||
u8 hctrl;
|
||||
u8 rsv2;
|
||||
__le16 len;
|
||||
};
|
||||
|
||||
struct __packed nct6694_response_header {
|
||||
u8 sequence_id;
|
||||
u8 sts;
|
||||
u8 reserved[4];
|
||||
__le16 len;
|
||||
};
|
||||
|
||||
union __packed nct6694_usb_msg {
|
||||
struct nct6694_cmd_header cmd_header;
|
||||
struct nct6694_response_header response_header;
|
||||
};
|
||||
|
||||
struct nct6694 {
|
||||
struct device *dev;
|
||||
struct ida gpio_ida;
|
||||
struct ida i2c_ida;
|
||||
struct ida canfd_ida;
|
||||
struct ida wdt_ida;
|
||||
struct irq_domain *domain;
|
||||
struct mutex access_lock;
|
||||
spinlock_t irq_lock;
|
||||
struct urb *int_in_urb;
|
||||
struct usb_device *udev;
|
||||
union nct6694_usb_msg *usb_msg;
|
||||
__le32 *int_buffer;
|
||||
unsigned int irq_enable;
|
||||
};
|
||||
|
||||
int nct6694_read_msg(struct nct6694 *nct6694, const struct nct6694_cmd_header *cmd_hd, void *buf);
|
||||
int nct6694_write_msg(struct nct6694 *nct6694, const struct nct6694_cmd_header *cmd_hd, void *buf);
|
||||
|
||||
#endif
|
||||
Loading…
Reference in New Issue