xref: /linux/drivers/mfd/nct6694.c (revision 68a052239fc4b351e961f698b824f7654a346091)
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Copyright (C) 2025 Nuvoton Technology Corp.
4  *
5  * Nuvoton NCT6694 core driver using USB interface to provide
6  * access to the NCT6694 hardware monitoring and control features.
7  *
8  * The NCT6694 is an integrated controller that provides GPIO, I2C,
9  * CAN, WDT, HWMON and RTC management.
10  */
11 
12 #include <linux/bits.h>
13 #include <linux/interrupt.h>
14 #include <linux/idr.h>
15 #include <linux/irq.h>
16 #include <linux/irqdomain.h>
17 #include <linux/kernel.h>
18 #include <linux/mfd/core.h>
19 #include <linux/mfd/nct6694.h>
20 #include <linux/module.h>
21 #include <linux/slab.h>
22 #include <linux/spinlock.h>
23 #include <linux/usb.h>
24 
25 static const struct mfd_cell nct6694_devs[] = {
26 	MFD_CELL_NAME("nct6694-gpio"),
27 	MFD_CELL_NAME("nct6694-gpio"),
28 	MFD_CELL_NAME("nct6694-gpio"),
29 	MFD_CELL_NAME("nct6694-gpio"),
30 	MFD_CELL_NAME("nct6694-gpio"),
31 	MFD_CELL_NAME("nct6694-gpio"),
32 	MFD_CELL_NAME("nct6694-gpio"),
33 	MFD_CELL_NAME("nct6694-gpio"),
34 	MFD_CELL_NAME("nct6694-gpio"),
35 	MFD_CELL_NAME("nct6694-gpio"),
36 	MFD_CELL_NAME("nct6694-gpio"),
37 	MFD_CELL_NAME("nct6694-gpio"),
38 	MFD_CELL_NAME("nct6694-gpio"),
39 	MFD_CELL_NAME("nct6694-gpio"),
40 	MFD_CELL_NAME("nct6694-gpio"),
41 	MFD_CELL_NAME("nct6694-gpio"),
42 
43 	MFD_CELL_NAME("nct6694-i2c"),
44 	MFD_CELL_NAME("nct6694-i2c"),
45 	MFD_CELL_NAME("nct6694-i2c"),
46 	MFD_CELL_NAME("nct6694-i2c"),
47 	MFD_CELL_NAME("nct6694-i2c"),
48 	MFD_CELL_NAME("nct6694-i2c"),
49 
50 	MFD_CELL_NAME("nct6694-canfd"),
51 	MFD_CELL_NAME("nct6694-canfd"),
52 
53 	MFD_CELL_NAME("nct6694-wdt"),
54 	MFD_CELL_NAME("nct6694-wdt"),
55 
56 	MFD_CELL_NAME("nct6694-hwmon"),
57 
58 	MFD_CELL_NAME("nct6694-rtc"),
59 };
60 
61 static int nct6694_response_err_handling(struct nct6694 *nct6694, unsigned char err_status)
62 {
63 	switch (err_status) {
64 	case NCT6694_NO_ERROR:
65 		return 0;
66 	case NCT6694_NOT_SUPPORT_ERROR:
67 		dev_err(nct6694->dev, "Command is not supported!\n");
68 		break;
69 	case NCT6694_NO_RESPONSE_ERROR:
70 		dev_warn(nct6694->dev, "Command received no response!\n");
71 		break;
72 	case NCT6694_TIMEOUT_ERROR:
73 		dev_warn(nct6694->dev, "Command timed out!\n");
74 		break;
75 	case NCT6694_PENDING:
76 		dev_err(nct6694->dev, "Command is pending!\n");
77 		break;
78 	default:
79 		return -EINVAL;
80 	}
81 
82 	return -EIO;
83 }
84 
85 /**
86  * nct6694_read_msg() - Read message from NCT6694 device
87  * @nct6694: NCT6694 device pointer
88  * @cmd_hd: command header structure
89  * @buf: buffer to store the response data
90  *
91  * Sends a command to the NCT6694 device and reads the response.
92  * The command header is specified in @cmd_hd, and the response
93  * data is stored in @buf.
94  *
95  * Return: Negative value on error or 0 on success.
96  */
97 int nct6694_read_msg(struct nct6694 *nct6694, const struct nct6694_cmd_header *cmd_hd, void *buf)
98 {
99 	union nct6694_usb_msg *msg = nct6694->usb_msg;
100 	struct usb_device *udev = nct6694->udev;
101 	int tx_len, rx_len, ret;
102 
103 	guard(mutex)(&nct6694->access_lock);
104 
105 	memcpy(&msg->cmd_header, cmd_hd, sizeof(*cmd_hd));
106 	msg->cmd_header.hctrl = NCT6694_HCTRL_GET;
107 
108 	/* Send command packet to USB device */
109 	ret = usb_bulk_msg(udev, usb_sndbulkpipe(udev, NCT6694_BULK_OUT_EP), &msg->cmd_header,
110 			   sizeof(*msg), &tx_len, NCT6694_URB_TIMEOUT);
111 	if (ret)
112 		return ret;
113 
114 	/* Receive response packet from USB device */
115 	ret = usb_bulk_msg(udev, usb_rcvbulkpipe(udev, NCT6694_BULK_IN_EP), &msg->response_header,
116 			   sizeof(*msg), &rx_len, NCT6694_URB_TIMEOUT);
117 	if (ret)
118 		return ret;
119 
120 	/* Receive data packet from USB device */
121 	ret = usb_bulk_msg(udev, usb_rcvbulkpipe(udev, NCT6694_BULK_IN_EP), buf,
122 			   le16_to_cpu(cmd_hd->len), &rx_len, NCT6694_URB_TIMEOUT);
123 	if (ret)
124 		return ret;
125 
126 	if (rx_len != le16_to_cpu(cmd_hd->len)) {
127 		dev_err(nct6694->dev, "Expected received length %d, but got %d\n",
128 			le16_to_cpu(cmd_hd->len), rx_len);
129 		return -EIO;
130 	}
131 
132 	return nct6694_response_err_handling(nct6694, msg->response_header.sts);
133 }
134 EXPORT_SYMBOL_GPL(nct6694_read_msg);
135 
136 /**
137  * nct6694_write_msg() - Write message to NCT6694 device
138  * @nct6694: NCT6694 device pointer
139  * @cmd_hd: command header structure
140  * @buf: buffer containing the data to be sent
141  *
142  * Sends a command to the NCT6694 device and writes the data
143  * from @buf. The command header is specified in @cmd_hd.
144  *
145  * Return: Negative value on error or 0 on success.
146  */
147 int nct6694_write_msg(struct nct6694 *nct6694, const struct nct6694_cmd_header *cmd_hd, void *buf)
148 {
149 	union nct6694_usb_msg *msg = nct6694->usb_msg;
150 	struct usb_device *udev = nct6694->udev;
151 	int tx_len, rx_len, ret;
152 
153 	guard(mutex)(&nct6694->access_lock);
154 
155 	memcpy(&msg->cmd_header, cmd_hd, sizeof(*cmd_hd));
156 	msg->cmd_header.hctrl = NCT6694_HCTRL_SET;
157 
158 	/* Send command packet to USB device */
159 	ret = usb_bulk_msg(udev, usb_sndbulkpipe(udev, NCT6694_BULK_OUT_EP), &msg->cmd_header,
160 			   sizeof(*msg), &tx_len, NCT6694_URB_TIMEOUT);
161 	if (ret)
162 		return ret;
163 
164 	/* Send data packet to USB device */
165 	ret = usb_bulk_msg(udev, usb_sndbulkpipe(udev, NCT6694_BULK_OUT_EP), buf,
166 			   le16_to_cpu(cmd_hd->len), &tx_len, NCT6694_URB_TIMEOUT);
167 	if (ret)
168 		return ret;
169 
170 	/* Receive response packet from USB device */
171 	ret = usb_bulk_msg(udev, usb_rcvbulkpipe(udev, NCT6694_BULK_IN_EP), &msg->response_header,
172 			   sizeof(*msg), &rx_len, NCT6694_URB_TIMEOUT);
173 	if (ret)
174 		return ret;
175 
176 	/* Receive data packet from USB device */
177 	ret = usb_bulk_msg(udev, usb_rcvbulkpipe(udev, NCT6694_BULK_IN_EP), buf,
178 			   le16_to_cpu(cmd_hd->len), &rx_len, NCT6694_URB_TIMEOUT);
179 	if (ret)
180 		return ret;
181 
182 	if (rx_len != le16_to_cpu(cmd_hd->len)) {
183 		dev_err(nct6694->dev, "Expected transmitted length %d, but got %d\n",
184 			le16_to_cpu(cmd_hd->len), rx_len);
185 		return -EIO;
186 	}
187 
188 	return nct6694_response_err_handling(nct6694, msg->response_header.sts);
189 }
190 EXPORT_SYMBOL_GPL(nct6694_write_msg);
191 
192 static void usb_int_callback(struct urb *urb)
193 {
194 	struct nct6694 *nct6694 = urb->context;
195 	__le32 *status_le = urb->transfer_buffer;
196 	u32 int_status;
197 	int ret;
198 
199 	switch (urb->status) {
200 	case 0:
201 		break;
202 	case -ECONNRESET:
203 	case -ENOENT:
204 	case -ESHUTDOWN:
205 		return;
206 	default:
207 		goto resubmit;
208 	}
209 
210 	int_status = le32_to_cpu(*status_le);
211 
212 	while (int_status) {
213 		int irq = __ffs(int_status);
214 
215 		generic_handle_irq_safe(irq_find_mapping(nct6694->domain, irq));
216 		int_status &= ~BIT(irq);
217 	}
218 
219 resubmit:
220 	ret = usb_submit_urb(urb, GFP_ATOMIC);
221 	if (ret)
222 		dev_warn(nct6694->dev, "Failed to resubmit urb, status %pe",  ERR_PTR(ret));
223 }
224 
225 static void nct6694_irq_enable(struct irq_data *data)
226 {
227 	struct nct6694 *nct6694 = irq_data_get_irq_chip_data(data);
228 	irq_hw_number_t hwirq = irqd_to_hwirq(data);
229 
230 	guard(spinlock_irqsave)(&nct6694->irq_lock);
231 
232 	nct6694->irq_enable |= BIT(hwirq);
233 }
234 
235 static void nct6694_irq_disable(struct irq_data *data)
236 {
237 	struct nct6694 *nct6694 = irq_data_get_irq_chip_data(data);
238 	irq_hw_number_t hwirq = irqd_to_hwirq(data);
239 
240 	guard(spinlock_irqsave)(&nct6694->irq_lock);
241 
242 	nct6694->irq_enable &= ~BIT(hwirq);
243 }
244 
245 static const struct irq_chip nct6694_irq_chip = {
246 	.name = "nct6694-irq",
247 	.flags = IRQCHIP_SKIP_SET_WAKE,
248 	.irq_enable = nct6694_irq_enable,
249 	.irq_disable = nct6694_irq_disable,
250 };
251 
252 static int nct6694_irq_domain_map(struct irq_domain *d, unsigned int irq, irq_hw_number_t hw)
253 {
254 	struct nct6694 *nct6694 = d->host_data;
255 
256 	irq_set_chip_data(irq, nct6694);
257 	irq_set_chip_and_handler(irq, &nct6694_irq_chip, handle_simple_irq);
258 
259 	return 0;
260 }
261 
262 static void nct6694_irq_domain_unmap(struct irq_domain *d, unsigned int irq)
263 {
264 	irq_set_chip_and_handler(irq, NULL, NULL);
265 	irq_set_chip_data(irq, NULL);
266 }
267 
268 static const struct irq_domain_ops nct6694_irq_domain_ops = {
269 	.map	= nct6694_irq_domain_map,
270 	.unmap	= nct6694_irq_domain_unmap,
271 };
272 
273 static int nct6694_usb_probe(struct usb_interface *iface,
274 			     const struct usb_device_id *id)
275 {
276 	struct usb_device *udev = interface_to_usbdev(iface);
277 	struct usb_endpoint_descriptor *int_endpoint;
278 	struct usb_host_interface *interface;
279 	struct device *dev = &iface->dev;
280 	struct nct6694 *nct6694;
281 	int ret;
282 
283 	nct6694 = devm_kzalloc(dev, sizeof(*nct6694), GFP_KERNEL);
284 	if (!nct6694)
285 		return -ENOMEM;
286 
287 	nct6694->usb_msg = devm_kzalloc(dev, sizeof(union nct6694_usb_msg), GFP_KERNEL);
288 	if (!nct6694->usb_msg)
289 		return -ENOMEM;
290 
291 	nct6694->int_buffer = devm_kzalloc(dev, sizeof(*nct6694->int_buffer), GFP_KERNEL);
292 	if (!nct6694->int_buffer)
293 		return -ENOMEM;
294 
295 	nct6694->int_in_urb = usb_alloc_urb(0, GFP_KERNEL);
296 	if (!nct6694->int_in_urb)
297 		return -ENOMEM;
298 
299 	nct6694->domain = irq_domain_create_simple(NULL, NCT6694_NR_IRQS, 0,
300 						   &nct6694_irq_domain_ops,
301 						   nct6694);
302 	if (!nct6694->domain) {
303 		ret = -ENODEV;
304 		goto err_urb;
305 	}
306 
307 	nct6694->dev = dev;
308 	nct6694->udev = udev;
309 
310 	ida_init(&nct6694->gpio_ida);
311 	ida_init(&nct6694->i2c_ida);
312 	ida_init(&nct6694->canfd_ida);
313 	ida_init(&nct6694->wdt_ida);
314 
315 	spin_lock_init(&nct6694->irq_lock);
316 
317 	ret = devm_mutex_init(dev, &nct6694->access_lock);
318 	if (ret)
319 		goto err_ida;
320 
321 	interface = iface->cur_altsetting;
322 
323 	int_endpoint = &interface->endpoint[0].desc;
324 	if (!usb_endpoint_is_int_in(int_endpoint)) {
325 		ret = -ENODEV;
326 		goto err_ida;
327 	}
328 
329 	usb_fill_int_urb(nct6694->int_in_urb, udev, usb_rcvintpipe(udev, NCT6694_INT_IN_EP),
330 			 nct6694->int_buffer, sizeof(*nct6694->int_buffer), usb_int_callback,
331 			 nct6694, int_endpoint->bInterval);
332 
333 	ret = usb_submit_urb(nct6694->int_in_urb, GFP_KERNEL);
334 	if (ret)
335 		goto err_ida;
336 
337 	usb_set_intfdata(iface, nct6694);
338 
339 	ret = mfd_add_hotplug_devices(dev, nct6694_devs, ARRAY_SIZE(nct6694_devs));
340 	if (ret)
341 		goto err_mfd;
342 
343 	return 0;
344 
345 err_mfd:
346 	usb_kill_urb(nct6694->int_in_urb);
347 err_ida:
348 	ida_destroy(&nct6694->wdt_ida);
349 	ida_destroy(&nct6694->canfd_ida);
350 	ida_destroy(&nct6694->i2c_ida);
351 	ida_destroy(&nct6694->gpio_ida);
352 	irq_domain_remove(nct6694->domain);
353 err_urb:
354 	usb_free_urb(nct6694->int_in_urb);
355 	return ret;
356 }
357 
358 static void nct6694_usb_disconnect(struct usb_interface *iface)
359 {
360 	struct nct6694 *nct6694 = usb_get_intfdata(iface);
361 
362 	mfd_remove_devices(nct6694->dev);
363 	usb_kill_urb(nct6694->int_in_urb);
364 	ida_destroy(&nct6694->wdt_ida);
365 	ida_destroy(&nct6694->canfd_ida);
366 	ida_destroy(&nct6694->i2c_ida);
367 	ida_destroy(&nct6694->gpio_ida);
368 	irq_domain_remove(nct6694->domain);
369 	usb_free_urb(nct6694->int_in_urb);
370 }
371 
372 static const struct usb_device_id nct6694_ids[] = {
373 	{ USB_DEVICE_AND_INTERFACE_INFO(NCT6694_VENDOR_ID, NCT6694_PRODUCT_ID, 0xFF, 0x00, 0x00) },
374 	{ }
375 };
376 MODULE_DEVICE_TABLE(usb, nct6694_ids);
377 
378 static struct usb_driver nct6694_usb_driver = {
379 	.name		= "nct6694",
380 	.id_table	= nct6694_ids,
381 	.probe		= nct6694_usb_probe,
382 	.disconnect	= nct6694_usb_disconnect,
383 };
384 module_usb_driver(nct6694_usb_driver);
385 
386 MODULE_DESCRIPTION("Nuvoton NCT6694 core driver");
387 MODULE_AUTHOR("Ming Yu <tmyu0@nuvoton.com>");
388 MODULE_LICENSE("GPL");
389