1/*-
2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3 *
4 * Copyright (c) 2010 Lev Serebryakov <lev@FreeBSD.org>.
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 *    notice, this list of conditions and the following disclaimer in the
14 *    documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 * SUCH DAMAGE.
27 */
28
29/*
30 * This driver supports several multiport USB-to-RS232 serial adapters driven
31 * by MosChip mos7820 and mos7840, bridge chips.
32 * The adapters are sold under many different brand names.
33 *
34 * Datasheets are available at MosChip www site at
35 * http://www.moschip.com.  The datasheets don't contain full
36 * programming information for the chip.
37 *
38 * It is nornal to have only two enabled ports in devices, based on
39 * quad-port mos7840.
40 *
41 */
42#include <sys/cdefs.h>
43__FBSDID("$FreeBSD$");
44
45#include <sys/stdint.h>
46#include <sys/stddef.h>
47#include <sys/param.h>
48#include <sys/queue.h>
49#include <sys/types.h>
50#include <sys/systm.h>
51#include <sys/kernel.h>
52#include <sys/bus.h>
53#include <sys/linker_set.h>
54#include <sys/module.h>
55#include <sys/lock.h>
56#include <sys/mutex.h>
57#include <sys/condvar.h>
58#include <sys/sysctl.h>
59#include <sys/sx.h>
60#include <sys/unistd.h>
61#include <sys/callout.h>
62#include <sys/malloc.h>
63#include <sys/priv.h>
64
65#include <dev/usb/usb.h>
66#include <dev/usb/usbdi.h>
67#include <dev/usb/usbdi_util.h>
68#include <dev/usb/usb_cdc.h>
69#include "usbdevs.h"
70
71#define	USB_DEBUG_VAR umcs_debug
72#include <dev/usb/usb_debug.h>
73#include <dev/usb/usb_process.h>
74
75#include <dev/usb/serial/usb_serial.h>
76
77#include <dev/usb/serial/umcs.h>
78
79#define	UMCS7840_MODVER	1
80
81#ifdef USB_DEBUG
82static int umcs_debug = 0;
83
84static SYSCTL_NODE(_hw_usb, OID_AUTO, umcs, CTLFLAG_RW | CTLFLAG_MPSAFE, 0,
85    "USB umcs quadport serial adapter");
86SYSCTL_INT(_hw_usb_umcs, OID_AUTO, debug, CTLFLAG_RWTUN, &umcs_debug, 0, "Debug level");
87#endif					/* USB_DEBUG */
88
89/*
90 * Two-port devices (both with 7820 chip and 7840 chip configured as two-port)
91 * have ports 0 and 2, with ports 1 and 3 omitted.
92 * So,PHYSICAL port numbers (indexes) on two-port device will be 0 and 2.
93 * This driver trys to use physical numbers as much as possible.
94 */
95
96/*
97 * Indexed by PHYSICAL port number.
98 * Pack non-regular registers to array to easier if-less access.
99 */
100struct umcs7840_port_registers {
101	uint8_t	reg_sp;			/* SP register. */
102	uint8_t	reg_control;		/* CONTROL register. */
103	uint8_t	reg_dcr;		/* DCR0 register. DCR1 & DCR2 can be
104					 * calculated */
105};
106
107static const struct umcs7840_port_registers umcs7840_port_registers[UMCS7840_MAX_PORTS] = {
108	{.reg_sp = MCS7840_DEV_REG_SP1,.reg_control = MCS7840_DEV_REG_CONTROL1,.reg_dcr = MCS7840_DEV_REG_DCR0_1},
109	{.reg_sp = MCS7840_DEV_REG_SP2,.reg_control = MCS7840_DEV_REG_CONTROL2,.reg_dcr = MCS7840_DEV_REG_DCR0_2},
110	{.reg_sp = MCS7840_DEV_REG_SP3,.reg_control = MCS7840_DEV_REG_CONTROL3,.reg_dcr = MCS7840_DEV_REG_DCR0_3},
111	{.reg_sp = MCS7840_DEV_REG_SP4,.reg_control = MCS7840_DEV_REG_CONTROL4,.reg_dcr = MCS7840_DEV_REG_DCR0_4},
112};
113
114enum {
115	UMCS7840_BULK_RD_EP,
116	UMCS7840_BULK_WR_EP,
117	UMCS7840_N_TRANSFERS
118};
119
120struct umcs7840_softc_oneport {
121	struct usb_xfer *sc_xfer[UMCS7840_N_TRANSFERS];	/* Control structures
122							 * for two transfers */
123
124	uint8_t	sc_lcr;			/* local line control register */
125	uint8_t	sc_mcr;			/* local modem control register */
126};
127
128struct umcs7840_softc {
129	struct ucom_super_softc sc_super_ucom;
130	struct ucom_softc sc_ucom[UMCS7840_MAX_PORTS];	/* Need to be continuous
131							 * array, so indexed by
132							 * LOGICAL port
133							 * (subunit) number */
134
135	struct usb_xfer *sc_intr_xfer;	/* Interrupt endpoint */
136
137	device_t sc_dev;		/* Device for error prints */
138	struct usb_device *sc_udev;	/* USB Device for all operations */
139	struct mtx sc_mtx;		/* ucom requires this */
140
141	uint8_t	sc_driver_done;		/* Flag when enumeration is finished */
142
143	uint8_t	sc_numports;		/* Number of ports (subunits) */
144	struct umcs7840_softc_oneport sc_ports[UMCS7840_MAX_PORTS];	/* Indexed by PHYSICAL
145									 * port number. */
146};
147
148/* prototypes */
149static usb_error_t umcs7840_get_reg_sync(struct umcs7840_softc *, uint8_t, uint8_t *);
150static usb_error_t umcs7840_set_reg_sync(struct umcs7840_softc *, uint8_t, uint8_t);
151static usb_error_t umcs7840_get_UART_reg_sync(struct umcs7840_softc *, uint8_t, uint8_t, uint8_t *);
152static usb_error_t umcs7840_set_UART_reg_sync(struct umcs7840_softc *, uint8_t, uint8_t, uint8_t);
153
154static usb_error_t umcs7840_set_baudrate(struct umcs7840_softc *, uint8_t, uint32_t);
155static usb_error_t umcs7840_calc_baudrate(uint32_t rate, uint16_t *, uint8_t *);
156
157static void	umcs7840_free(struct ucom_softc *);
158static void umcs7840_cfg_get_status(struct ucom_softc *, uint8_t *, uint8_t *);
159static void umcs7840_cfg_set_dtr(struct ucom_softc *, uint8_t);
160static void umcs7840_cfg_set_rts(struct ucom_softc *, uint8_t);
161static void umcs7840_cfg_set_break(struct ucom_softc *, uint8_t);
162static void umcs7840_cfg_param(struct ucom_softc *, struct termios *);
163static void umcs7840_cfg_open(struct ucom_softc *);
164static void umcs7840_cfg_close(struct ucom_softc *);
165
166static int umcs7840_pre_param(struct ucom_softc *, struct termios *);
167
168static void umcs7840_start_read(struct ucom_softc *);
169static void umcs7840_stop_read(struct ucom_softc *);
170
171static void umcs7840_start_write(struct ucom_softc *);
172static void umcs7840_stop_write(struct ucom_softc *);
173
174static void umcs7840_poll(struct ucom_softc *ucom);
175
176static device_probe_t umcs7840_probe;
177static device_attach_t umcs7840_attach;
178static device_detach_t umcs7840_detach;
179static void umcs7840_free_softc(struct umcs7840_softc *);
180
181static usb_callback_t umcs7840_intr_callback;
182static usb_callback_t umcs7840_read_callback1;
183static usb_callback_t umcs7840_read_callback2;
184static usb_callback_t umcs7840_read_callback3;
185static usb_callback_t umcs7840_read_callback4;
186static usb_callback_t umcs7840_write_callback1;
187static usb_callback_t umcs7840_write_callback2;
188static usb_callback_t umcs7840_write_callback3;
189static usb_callback_t umcs7840_write_callback4;
190
191static void umcs7840_read_callbackN(struct usb_xfer *, usb_error_t, uint8_t);
192static void umcs7840_write_callbackN(struct usb_xfer *, usb_error_t, uint8_t);
193
194/* Indexed by LOGICAL port number (subunit), so two-port device uses 0 & 1 */
195static usb_callback_t *umcs7840_rw_callbacks[UMCS7840_MAX_PORTS][UMCS7840_N_TRANSFERS] = {
196	{&umcs7840_read_callback1, &umcs7840_write_callback1},
197	{&umcs7840_read_callback2, &umcs7840_write_callback2},
198	{&umcs7840_read_callback3, &umcs7840_write_callback3},
199	{&umcs7840_read_callback4, &umcs7840_write_callback4},
200};
201
202static const struct usb_config umcs7840_bulk_config_data[UMCS7840_N_TRANSFERS] = {
203	[UMCS7840_BULK_RD_EP] = {
204		.type = UE_BULK,
205		.endpoint = 0x01,
206		.direction = UE_DIR_IN,
207		.flags = {.pipe_bof = 1,.short_xfer_ok = 1,},
208		.bufsize = 0,		/* use wMaxPacketSize */
209		.callback = &umcs7840_read_callback1,
210		.if_index = 0,
211	},
212
213	[UMCS7840_BULK_WR_EP] = {
214		.type = UE_BULK,
215		.endpoint = 0x02,
216		.direction = UE_DIR_OUT,
217		.flags = {.pipe_bof = 1,.short_xfer_ok = 1,},
218		.bufsize = 0,		/* use wMaxPacketSize */
219		.callback = &umcs7840_write_callback1,
220		.if_index = 0,
221	},
222};
223
224static const struct usb_config umcs7840_intr_config_data[1] = {
225	[0] = {
226		.type = UE_INTERRUPT,
227		.endpoint = 0x09,
228		.direction = UE_DIR_IN,
229		.flags = {.pipe_bof = 1,.short_xfer_ok = 1,},
230		.bufsize = 0,		/* use wMaxPacketSize */
231		.callback = &umcs7840_intr_callback,
232		.if_index = 0,
233	},
234};
235
236static struct ucom_callback umcs7840_callback = {
237	.ucom_cfg_get_status = &umcs7840_cfg_get_status,
238
239	.ucom_cfg_set_dtr = &umcs7840_cfg_set_dtr,
240	.ucom_cfg_set_rts = &umcs7840_cfg_set_rts,
241	.ucom_cfg_set_break = &umcs7840_cfg_set_break,
242
243	.ucom_cfg_param = &umcs7840_cfg_param,
244	.ucom_cfg_open = &umcs7840_cfg_open,
245	.ucom_cfg_close = &umcs7840_cfg_close,
246
247	.ucom_pre_param = &umcs7840_pre_param,
248
249	.ucom_start_read = &umcs7840_start_read,
250	.ucom_stop_read = &umcs7840_stop_read,
251
252	.ucom_start_write = &umcs7840_start_write,
253	.ucom_stop_write = &umcs7840_stop_write,
254
255	.ucom_poll = &umcs7840_poll,
256	.ucom_free = &umcs7840_free,
257};
258
259static const STRUCT_USB_HOST_ID umcs7840_devs[] = {
260	{USB_VPI(USB_VENDOR_MOSCHIP, USB_PRODUCT_MOSCHIP_MCS7820, 0)},
261	{USB_VPI(USB_VENDOR_MOSCHIP, USB_PRODUCT_MOSCHIP_MCS7840, 0)},
262};
263
264static device_method_t umcs7840_methods[] = {
265	DEVMETHOD(device_probe, umcs7840_probe),
266	DEVMETHOD(device_attach, umcs7840_attach),
267	DEVMETHOD(device_detach, umcs7840_detach),
268	DEVMETHOD_END
269};
270
271static devclass_t umcs7840_devclass;
272
273static driver_t umcs7840_driver = {
274	.name = "umcs7840",
275	.methods = umcs7840_methods,
276	.size = sizeof(struct umcs7840_softc),
277};
278
279DRIVER_MODULE(umcs7840, uhub, umcs7840_driver, umcs7840_devclass, 0, 0);
280MODULE_DEPEND(umcs7840, ucom, 1, 1, 1);
281MODULE_DEPEND(umcs7840, usb, 1, 1, 1);
282MODULE_VERSION(umcs7840, UMCS7840_MODVER);
283USB_PNP_HOST_INFO(umcs7840_devs);
284
285static int
286umcs7840_probe(device_t dev)
287{
288	struct usb_attach_arg *uaa = device_get_ivars(dev);
289
290	if (uaa->usb_mode != USB_MODE_HOST)
291		return (ENXIO);
292	if (uaa->info.bConfigIndex != MCS7840_CONFIG_INDEX)
293		return (ENXIO);
294	if (uaa->info.bIfaceIndex != MCS7840_IFACE_INDEX)
295		return (ENXIO);
296	return (usbd_lookup_id_by_uaa(umcs7840_devs, sizeof(umcs7840_devs), uaa));
297}
298
299static int
300umcs7840_attach(device_t dev)
301{
302	struct usb_config umcs7840_config_tmp[UMCS7840_N_TRANSFERS];
303	struct usb_attach_arg *uaa = device_get_ivars(dev);
304	struct umcs7840_softc *sc = device_get_softc(dev);
305
306	uint8_t iface_index = MCS7840_IFACE_INDEX;
307	int error;
308	int subunit;
309	int n;
310	uint8_t data;
311
312	for (n = 0; n < UMCS7840_N_TRANSFERS; ++n)
313		umcs7840_config_tmp[n] = umcs7840_bulk_config_data[n];
314
315	device_set_usb_desc(dev);
316	mtx_init(&sc->sc_mtx, "umcs7840", NULL, MTX_DEF);
317	ucom_ref(&sc->sc_super_ucom);
318
319	sc->sc_dev = dev;
320	sc->sc_udev = uaa->device;
321
322	/*
323	 * Get number of ports
324	 * Documentation (full datasheet) says, that number of ports is
325	 * set as MCS7840_DEV_MODE_SELECT24S bit in MODE R/Only
326	 * register. But vendor driver uses these undocumented
327	 * register & bit.
328	 *
329	 * Experiments show, that MODE register can have `0'
330	 * (4 ports) bit on 2-port device, so use vendor driver's way.
331	 *
332	 * Also, see notes in header file for these constants.
333	 */
334	umcs7840_get_reg_sync(sc, MCS7840_DEV_REG_GPIO, &data);
335	if (data & MCS7840_DEV_GPIO_4PORTS) {
336		sc->sc_numports = 4;
337		/* Store physical port numbers in sc_portno */
338		sc->sc_ucom[0].sc_portno = 0;
339		sc->sc_ucom[1].sc_portno = 1;
340		sc->sc_ucom[2].sc_portno = 2;
341		sc->sc_ucom[3].sc_portno = 3;
342	} else {
343		sc->sc_numports = 2;
344		/* Store physical port numbers in sc_portno */
345		sc->sc_ucom[0].sc_portno = 0;
346		sc->sc_ucom[1].sc_portno = 2;	/* '1' is skipped */
347	}
348	device_printf(dev, "Chip mcs%04x, found %d active ports\n", uaa->info.idProduct, sc->sc_numports);
349	if (!umcs7840_get_reg_sync(sc, MCS7840_DEV_REG_MODE, &data)) {
350		device_printf(dev, "On-die confguration: RST: active %s, HRD: %s, PLL: %s, POR: %s, Ports: %s, EEPROM write %s, IrDA is %savailable\n",
351		    (data & MCS7840_DEV_MODE_RESET) ? "low" : "high",
352		    (data & MCS7840_DEV_MODE_SER_PRSNT) ? "yes" : "no",
353		    (data & MCS7840_DEV_MODE_PLLBYPASS) ? "bypassed" : "avail",
354		    (data & MCS7840_DEV_MODE_PORBYPASS) ? "bypassed" : "avail",
355		    (data & MCS7840_DEV_MODE_SELECT24S) ? "2" : "4",
356		    (data & MCS7840_DEV_MODE_EEPROMWR) ? "enabled" : "disabled",
357		    (data & MCS7840_DEV_MODE_IRDA) ? "" : "not ");
358	}
359	/* Setup all transfers */
360	for (subunit = 0; subunit < sc->sc_numports; ++subunit) {
361		for (n = 0; n < UMCS7840_N_TRANSFERS; ++n) {
362			/* Set endpoint address */
363			umcs7840_config_tmp[n].endpoint = umcs7840_bulk_config_data[n].endpoint + 2 * sc->sc_ucom[subunit].sc_portno;
364			umcs7840_config_tmp[n].callback = umcs7840_rw_callbacks[subunit][n];
365		}
366		error = usbd_transfer_setup(uaa->device,
367		    &iface_index, sc->sc_ports[sc->sc_ucom[subunit].sc_portno].sc_xfer, umcs7840_config_tmp,
368		    UMCS7840_N_TRANSFERS, sc, &sc->sc_mtx);
369		if (error) {
370			device_printf(dev, "allocating USB transfers failed for subunit %d of %d\n",
371			    subunit + 1, sc->sc_numports);
372			goto detach;
373		}
374	}
375	error = usbd_transfer_setup(uaa->device,
376	    &iface_index, &sc->sc_intr_xfer, umcs7840_intr_config_data,
377	    1, sc, &sc->sc_mtx);
378	if (error) {
379		device_printf(dev, "allocating USB transfers failed for interrupt\n");
380		goto detach;
381	}
382	/* clear stall at first run */
383	mtx_lock(&sc->sc_mtx);
384	for (subunit = 0; subunit < sc->sc_numports; ++subunit) {
385		usbd_xfer_set_stall(sc->sc_ports[sc->sc_ucom[subunit].sc_portno].sc_xfer[UMCS7840_BULK_RD_EP]);
386		usbd_xfer_set_stall(sc->sc_ports[sc->sc_ucom[subunit].sc_portno].sc_xfer[UMCS7840_BULK_WR_EP]);
387	}
388	mtx_unlock(&sc->sc_mtx);
389
390	error = ucom_attach(&sc->sc_super_ucom, sc->sc_ucom, sc->sc_numports, sc,
391	    &umcs7840_callback, &sc->sc_mtx);
392	if (error)
393		goto detach;
394
395	ucom_set_pnpinfo_usb(&sc->sc_super_ucom, dev);
396
397	return (0);
398
399detach:
400	umcs7840_detach(dev);
401	return (ENXIO);
402}
403
404static int
405umcs7840_detach(device_t dev)
406{
407	struct umcs7840_softc *sc = device_get_softc(dev);
408	int subunit;
409
410	ucom_detach(&sc->sc_super_ucom, sc->sc_ucom);
411
412	for (subunit = 0; subunit < sc->sc_numports; ++subunit)
413		usbd_transfer_unsetup(sc->sc_ports[sc->sc_ucom[subunit].sc_portno].sc_xfer, UMCS7840_N_TRANSFERS);
414	usbd_transfer_unsetup(&sc->sc_intr_xfer, 1);
415
416	device_claim_softc(dev);
417
418	umcs7840_free_softc(sc);
419
420	return (0);
421}
422
423UCOM_UNLOAD_DRAIN(umcs7840);
424
425static void
426umcs7840_free_softc(struct umcs7840_softc *sc)
427{
428	if (ucom_unref(&sc->sc_super_ucom)) {
429		mtx_destroy(&sc->sc_mtx);
430		device_free_softc(sc);
431	}
432}
433
434static void
435umcs7840_free(struct ucom_softc *ucom)
436{
437	umcs7840_free_softc(ucom->sc_parent);
438}
439
440static void
441umcs7840_cfg_open(struct ucom_softc *ucom)
442{
443	struct umcs7840_softc *sc = ucom->sc_parent;
444	uint16_t pn = ucom->sc_portno;
445	uint8_t data;
446
447	/* If it very first open, finish global configuration */
448	if (!sc->sc_driver_done) {
449		/*
450		 * USB enumeration is finished, pass internal memory to FIFOs
451		 * If it is done in the end of "attach", kernel panics.
452		 */
453		if (umcs7840_get_reg_sync(sc, MCS7840_DEV_REG_CONTROL1, &data))
454			return;
455		data |= MCS7840_DEV_CONTROL1_DRIVER_DONE;
456		if (umcs7840_set_reg_sync(sc, MCS7840_DEV_REG_CONTROL1, data))
457			return;
458		sc->sc_driver_done = 1;
459	}
460	/* Toggle reset bit on-off */
461	if (umcs7840_get_reg_sync(sc, umcs7840_port_registers[pn].reg_sp, &data))
462		return;
463	data |= MCS7840_DEV_SPx_UART_RESET;
464	if (umcs7840_set_reg_sync(sc, umcs7840_port_registers[pn].reg_sp, data))
465		return;
466	data &= ~MCS7840_DEV_SPx_UART_RESET;
467	if (umcs7840_set_reg_sync(sc, umcs7840_port_registers[pn].reg_sp, data))
468		return;
469
470	/* Set RS-232 mode */
471	if (umcs7840_set_UART_reg_sync(sc, pn, MCS7840_UART_REG_SCRATCHPAD, MCS7840_UART_SCRATCHPAD_RS232))
472		return;
473
474	/* Disable RX on time of initialization */
475	if (umcs7840_get_reg_sync(sc, umcs7840_port_registers[pn].reg_control, &data))
476		return;
477	data |= MCS7840_DEV_CONTROLx_RX_DISABLE;
478	if (umcs7840_set_reg_sync(sc, umcs7840_port_registers[pn].reg_control, data))
479		return;
480
481	/* Disable all interrupts */
482	if (umcs7840_set_UART_reg_sync(sc, pn, MCS7840_UART_REG_IER, 0))
483		return;
484
485	/* Reset FIFO -- documented */
486	if (umcs7840_set_UART_reg_sync(sc, pn, MCS7840_UART_REG_FCR, 0))
487		return;
488	if (umcs7840_set_UART_reg_sync(sc, pn, MCS7840_UART_REG_FCR,
489	    MCS7840_UART_FCR_ENABLE | MCS7840_UART_FCR_FLUSHRHR |
490	    MCS7840_UART_FCR_FLUSHTHR | MCS7840_UART_FCR_RTL_1_14))
491		return;
492
493	/* Set 8 bit, no parity, 1 stop bit -- documented */
494	sc->sc_ports[pn].sc_lcr = MCS7840_UART_LCR_DATALEN8 | MCS7840_UART_LCR_STOPB1;
495	if (umcs7840_set_UART_reg_sync(sc, pn, MCS7840_UART_REG_LCR, sc->sc_ports[pn].sc_lcr))
496		return;
497
498	/*
499	 * Enable DTR/RTS on modem control, enable modem interrupts --
500	 * documented
501	 */
502	sc->sc_ports[pn].sc_mcr = MCS7840_UART_MCR_IE;
503	if (ucom->sc_tty == NULL || (ucom->sc_tty->t_termios.c_cflag & CNO_RTSDTR) == 0)
504		sc->sc_ports[pn].sc_mcr |= MCS7840_UART_MCR_DTR | MCS7840_UART_MCR_RTS;
505	if (umcs7840_set_UART_reg_sync(sc, pn, MCS7840_UART_REG_MCR, sc->sc_ports[pn].sc_mcr))
506		return;
507
508	/* Clearing Bulkin and Bulkout FIFO */
509	if (umcs7840_get_reg_sync(sc, umcs7840_port_registers[pn].reg_sp, &data))
510		return;
511	data |= MCS7840_DEV_SPx_RESET_OUT_FIFO | MCS7840_DEV_SPx_RESET_IN_FIFO;
512	if (umcs7840_set_reg_sync(sc, umcs7840_port_registers[pn].reg_sp, data))
513		return;
514	data &= ~(MCS7840_DEV_SPx_RESET_OUT_FIFO | MCS7840_DEV_SPx_RESET_IN_FIFO);
515	if (umcs7840_set_reg_sync(sc, umcs7840_port_registers[pn].reg_sp, data))
516		return;
517
518	/* Set speed 9600 */
519	if (umcs7840_set_baudrate(sc, pn, 9600))
520		return;
521
522	/* Finally enable all interrupts -- documented */
523	/*
524	 * Copied from vendor driver, I don't know why we should read LCR
525	 * here
526	 */
527	if (umcs7840_get_UART_reg_sync(sc, pn, MCS7840_UART_REG_LCR, &sc->sc_ports[pn].sc_lcr))
528		return;
529	if (umcs7840_set_UART_reg_sync(sc, pn, MCS7840_UART_REG_IER,
530	    MCS7840_UART_IER_RXSTAT | MCS7840_UART_IER_MODEM))
531		return;
532
533	/* Enable RX */
534	if (umcs7840_get_reg_sync(sc, umcs7840_port_registers[pn].reg_control, &data))
535		return;
536	data &= ~MCS7840_DEV_CONTROLx_RX_DISABLE;
537	if (umcs7840_set_reg_sync(sc, umcs7840_port_registers[pn].reg_control, data))
538		return;
539
540	DPRINTF("Port %d has been opened\n", pn);
541}
542
543static void
544umcs7840_cfg_close(struct ucom_softc *ucom)
545{
546	struct umcs7840_softc *sc = ucom->sc_parent;
547	uint16_t pn = ucom->sc_portno;
548	uint8_t data;
549
550	umcs7840_stop_read(ucom);
551	umcs7840_stop_write(ucom);
552
553	umcs7840_set_UART_reg_sync(sc, pn, MCS7840_UART_REG_MCR, 0);
554	umcs7840_set_UART_reg_sync(sc, pn, MCS7840_UART_REG_IER, 0);
555
556	/* Disable RX */
557	if (umcs7840_get_reg_sync(sc, umcs7840_port_registers[pn].reg_control, &data))
558		return;
559	data |= MCS7840_DEV_CONTROLx_RX_DISABLE;
560	if (umcs7840_set_reg_sync(sc, umcs7840_port_registers[pn].reg_control, data))
561		return;
562	DPRINTF("Port %d has been closed\n", pn);
563}
564
565static void
566umcs7840_cfg_set_dtr(struct ucom_softc *ucom, uint8_t onoff)
567{
568	struct umcs7840_softc *sc = ucom->sc_parent;
569	uint8_t pn = ucom->sc_portno;
570
571	if (onoff)
572		sc->sc_ports[pn].sc_mcr |= MCS7840_UART_MCR_DTR;
573	else
574		sc->sc_ports[pn].sc_mcr &= ~MCS7840_UART_MCR_DTR;
575
576	umcs7840_set_UART_reg_sync(sc, pn, MCS7840_UART_REG_MCR, sc->sc_ports[pn].sc_mcr);
577	DPRINTF("Port %d DTR set to: %s\n", pn, onoff ? "on" : "off");
578}
579
580static void
581umcs7840_cfg_set_rts(struct ucom_softc *ucom, uint8_t onoff)
582{
583	struct umcs7840_softc *sc = ucom->sc_parent;
584	uint8_t pn = ucom->sc_portno;
585
586	if (onoff)
587		sc->sc_ports[pn].sc_mcr |= MCS7840_UART_MCR_RTS;
588	else
589		sc->sc_ports[pn].sc_mcr &= ~MCS7840_UART_MCR_RTS;
590
591	umcs7840_set_UART_reg_sync(sc, pn, MCS7840_UART_REG_MCR, sc->sc_ports[pn].sc_mcr);
592	DPRINTF("Port %d RTS set to: %s\n", pn, onoff ? "on" : "off");
593}
594
595static void
596umcs7840_cfg_set_break(struct ucom_softc *ucom, uint8_t onoff)
597{
598	struct umcs7840_softc *sc = ucom->sc_parent;
599	uint8_t pn = ucom->sc_portno;
600
601	if (onoff)
602		sc->sc_ports[pn].sc_lcr |= MCS7840_UART_LCR_BREAK;
603	else
604		sc->sc_ports[pn].sc_lcr &= ~MCS7840_UART_LCR_BREAK;
605
606	umcs7840_set_UART_reg_sync(sc, pn, MCS7840_UART_REG_LCR, sc->sc_ports[pn].sc_lcr);
607	DPRINTF("Port %d BREAK set to: %s\n", pn, onoff ? "on" : "off");
608}
609
610static void
611umcs7840_cfg_param(struct ucom_softc *ucom, struct termios *t)
612{
613	struct umcs7840_softc *sc = ucom->sc_parent;
614	uint8_t pn = ucom->sc_portno;
615	uint8_t lcr = sc->sc_ports[pn].sc_lcr;
616	uint8_t mcr = sc->sc_ports[pn].sc_mcr;
617
618	DPRINTF("Port %d config:\n", pn);
619	if (t->c_cflag & CSTOPB) {
620		DPRINTF("  2 stop bits\n");
621		lcr |= MCS7840_UART_LCR_STOPB2;
622	} else {
623		lcr |= MCS7840_UART_LCR_STOPB1;
624		DPRINTF("  1 stop bit\n");
625	}
626
627	lcr &= ~MCS7840_UART_LCR_PARITYMASK;
628	if (t->c_cflag & PARENB) {
629		lcr |= MCS7840_UART_LCR_PARITYON;
630		if (t->c_cflag & PARODD) {
631			lcr = MCS7840_UART_LCR_PARITYODD;
632			DPRINTF("  parity on - odd\n");
633		} else {
634			lcr = MCS7840_UART_LCR_PARITYEVEN;
635			DPRINTF("  parity on - even\n");
636		}
637	} else {
638		lcr &= ~MCS7840_UART_LCR_PARITYON;
639		DPRINTF("  parity off\n");
640	}
641
642	lcr &= ~MCS7840_UART_LCR_DATALENMASK;
643	switch (t->c_cflag & CSIZE) {
644	case CS5:
645		lcr |= MCS7840_UART_LCR_DATALEN5;
646		DPRINTF("  5 bit\n");
647		break;
648	case CS6:
649		lcr |= MCS7840_UART_LCR_DATALEN6;
650		DPRINTF("  6 bit\n");
651		break;
652	case CS7:
653		lcr |= MCS7840_UART_LCR_DATALEN7;
654		DPRINTF("  7 bit\n");
655		break;
656	case CS8:
657		lcr |= MCS7840_UART_LCR_DATALEN8;
658		DPRINTF("  8 bit\n");
659		break;
660	}
661
662	if (t->c_cflag & CRTSCTS) {
663		mcr |= MCS7840_UART_MCR_CTSRTS;
664		DPRINTF("  CTS/RTS\n");
665	} else
666		mcr &= ~MCS7840_UART_MCR_CTSRTS;
667
668	if (t->c_cflag & (CDTR_IFLOW | CDSR_OFLOW)) {
669		mcr |= MCS7840_UART_MCR_DTRDSR;
670		DPRINTF("  DTR/DSR\n");
671	} else
672		mcr &= ~MCS7840_UART_MCR_DTRDSR;
673
674	sc->sc_ports[pn].sc_lcr = lcr;
675	umcs7840_set_UART_reg_sync(sc, pn, MCS7840_UART_REG_LCR, sc->sc_ports[pn].sc_lcr);
676	DPRINTF("Port %d LCR=%02x\n", pn, sc->sc_ports[pn].sc_lcr);
677
678	sc->sc_ports[pn].sc_mcr = mcr;
679	umcs7840_set_UART_reg_sync(sc, pn, MCS7840_UART_REG_MCR, sc->sc_ports[pn].sc_mcr);
680	DPRINTF("Port %d MCR=%02x\n", pn, sc->sc_ports[pn].sc_mcr);
681
682	umcs7840_set_baudrate(sc, pn, t->c_ospeed);
683}
684
685static int
686umcs7840_pre_param(struct ucom_softc *ucom, struct termios *t)
687{
688	uint8_t clk;
689	uint16_t divisor;
690
691	if (umcs7840_calc_baudrate(t->c_ospeed, &divisor, &clk) || !divisor)
692		return (EINVAL);
693	return (0);
694}
695
696static void
697umcs7840_start_read(struct ucom_softc *ucom)
698{
699	struct umcs7840_softc *sc = ucom->sc_parent;
700	uint8_t pn = ucom->sc_portno;
701
702	/* Start interrupt transfer */
703	usbd_transfer_start(sc->sc_intr_xfer);
704
705	/* Start read transfer */
706	usbd_transfer_start(sc->sc_ports[pn].sc_xfer[UMCS7840_BULK_RD_EP]);
707}
708
709static void
710umcs7840_stop_read(struct ucom_softc *ucom)
711{
712	struct umcs7840_softc *sc = ucom->sc_parent;
713	uint8_t pn = ucom->sc_portno;
714
715	/* Stop read transfer */
716	usbd_transfer_stop(sc->sc_ports[pn].sc_xfer[UMCS7840_BULK_RD_EP]);
717}
718
719static void
720umcs7840_start_write(struct ucom_softc *ucom)
721{
722	struct umcs7840_softc *sc = ucom->sc_parent;
723	uint8_t pn = ucom->sc_portno;
724
725	/* Start interrupt transfer */
726	usbd_transfer_start(sc->sc_intr_xfer);
727
728	/* Start write transfer */
729	usbd_transfer_start(sc->sc_ports[pn].sc_xfer[UMCS7840_BULK_WR_EP]);
730}
731
732static void
733umcs7840_stop_write(struct ucom_softc *ucom)
734{
735	struct umcs7840_softc *sc = ucom->sc_parent;
736	uint8_t pn = ucom->sc_portno;
737
738	/* Stop write transfer */
739	usbd_transfer_stop(sc->sc_ports[pn].sc_xfer[UMCS7840_BULK_WR_EP]);
740}
741
742static void
743umcs7840_cfg_get_status(struct ucom_softc *ucom, uint8_t *lsr, uint8_t *msr)
744{
745	struct umcs7840_softc *sc = ucom->sc_parent;
746	uint8_t pn = ucom->sc_portno;
747	uint8_t	hw_msr = 0;	/* local modem status register */
748
749	/*
750	 * Read status registers.  MSR bits need translation from ns16550 to
751	 * SER_* values.  LSR bits are ns16550 in hardware and ucom.
752	 */
753	umcs7840_get_UART_reg_sync(sc, pn, MCS7840_UART_REG_LSR, lsr);
754	umcs7840_get_UART_reg_sync(sc, pn, MCS7840_UART_REG_MSR, &hw_msr);
755
756	if (hw_msr & MCS7840_UART_MSR_NEGCTS)
757		*msr |= SER_CTS;
758
759	if (hw_msr & MCS7840_UART_MSR_NEGDCD)
760		*msr |= SER_DCD;
761
762	if (hw_msr & MCS7840_UART_MSR_NEGRI)
763		*msr |= SER_RI;
764
765	if (hw_msr & MCS7840_UART_MSR_NEGDSR)
766		*msr |= SER_DSR;
767
768	DPRINTF("Port %d status: LSR=%02x MSR=%02x\n", ucom->sc_portno, *lsr, *msr);
769}
770
771static void
772umcs7840_intr_callback(struct usb_xfer *xfer, usb_error_t error)
773{
774	struct umcs7840_softc *sc = usbd_xfer_softc(xfer);
775	struct usb_page_cache *pc;
776	uint8_t buf[13];
777	int actlen;
778	int subunit;
779
780	usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL);
781
782	switch (USB_GET_STATE(xfer)) {
783	case USB_ST_TRANSFERRED:
784		if (actlen == 5 || actlen == 13) {
785			pc = usbd_xfer_get_frame(xfer, 0);
786			usbd_copy_out(pc, 0, buf, actlen);
787			/* Check status of all ports */
788			for (subunit = 0; subunit < sc->sc_numports; ++subunit) {
789				uint8_t pn = sc->sc_ucom[subunit].sc_portno;
790
791				if (buf[pn] & MCS7840_UART_ISR_NOPENDING)
792					continue;
793				DPRINTF("Port %d has pending interrupt: %02x (FIFO: %02x)\n", pn, buf[pn] & MCS7840_UART_ISR_INTMASK, buf[pn] & (~MCS7840_UART_ISR_INTMASK));
794				switch (buf[pn] & MCS7840_UART_ISR_INTMASK) {
795				case MCS7840_UART_ISR_RXERR:
796				case MCS7840_UART_ISR_RXHASDATA:
797				case MCS7840_UART_ISR_RXTIMEOUT:
798				case MCS7840_UART_ISR_MSCHANGE:
799					ucom_status_change(&sc->sc_ucom[subunit]);
800					break;
801				default:
802					/* Do nothing */
803					break;
804				}
805			}
806		} else
807			device_printf(sc->sc_dev, "Invalid interrupt data length %d", actlen);
808		/* FALLTHROUGH */
809	case USB_ST_SETUP:
810tr_setup:
811		usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer));
812		usbd_transfer_submit(xfer);
813		return;
814
815	default:			/* Error */
816		if (error != USB_ERR_CANCELLED) {
817			/* try to clear stall first */
818			usbd_xfer_set_stall(xfer);
819			goto tr_setup;
820		}
821		return;
822	}
823}
824
825static void
826umcs7840_read_callback1(struct usb_xfer *xfer, usb_error_t error)
827{
828	umcs7840_read_callbackN(xfer, error, 0);
829}
830
831static void
832umcs7840_read_callback2(struct usb_xfer *xfer, usb_error_t error)
833{
834	umcs7840_read_callbackN(xfer, error, 1);
835}
836static void
837umcs7840_read_callback3(struct usb_xfer *xfer, usb_error_t error)
838{
839	umcs7840_read_callbackN(xfer, error, 2);
840}
841
842static void
843umcs7840_read_callback4(struct usb_xfer *xfer, usb_error_t error)
844{
845	umcs7840_read_callbackN(xfer, error, 3);
846}
847
848static void
849umcs7840_read_callbackN(struct usb_xfer *xfer, usb_error_t error, uint8_t subunit)
850{
851	struct umcs7840_softc *sc = usbd_xfer_softc(xfer);
852	struct ucom_softc *ucom = &sc->sc_ucom[subunit];
853	struct usb_page_cache *pc;
854	int actlen;
855
856	usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL);
857
858	DPRINTF("Port %d read, state = %d, data length = %d\n", ucom->sc_portno, USB_GET_STATE(xfer), actlen);
859
860	switch (USB_GET_STATE(xfer)) {
861	case USB_ST_TRANSFERRED:
862		pc = usbd_xfer_get_frame(xfer, 0);
863		ucom_put_data(ucom, pc, 0, actlen);
864		/* FALLTHROUGH */
865	case USB_ST_SETUP:
866tr_setup:
867		usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer));
868		usbd_transfer_submit(xfer);
869		return;
870
871	default:			/* Error */
872		if (error != USB_ERR_CANCELLED) {
873			/* try to clear stall first */
874			usbd_xfer_set_stall(xfer);
875			goto tr_setup;
876		}
877		return;
878	}
879}
880
881static void
882umcs7840_write_callback1(struct usb_xfer *xfer, usb_error_t error)
883{
884	umcs7840_write_callbackN(xfer, error, 0);
885}
886
887static void
888umcs7840_write_callback2(struct usb_xfer *xfer, usb_error_t error)
889{
890	umcs7840_write_callbackN(xfer, error, 1);
891}
892
893static void
894umcs7840_write_callback3(struct usb_xfer *xfer, usb_error_t error)
895{
896	umcs7840_write_callbackN(xfer, error, 2);
897}
898
899static void
900umcs7840_write_callback4(struct usb_xfer *xfer, usb_error_t error)
901{
902	umcs7840_write_callbackN(xfer, error, 3);
903}
904
905static void
906umcs7840_write_callbackN(struct usb_xfer *xfer, usb_error_t error, uint8_t subunit)
907{
908	struct umcs7840_softc *sc = usbd_xfer_softc(xfer);
909	struct ucom_softc *ucom = &sc->sc_ucom[subunit];
910	struct usb_page_cache *pc;
911	uint32_t actlen;
912
913	DPRINTF("Port %d write, state = %d\n", ucom->sc_portno, USB_GET_STATE(xfer));
914
915	switch (USB_GET_STATE(xfer)) {
916	case USB_ST_SETUP:
917	case USB_ST_TRANSFERRED:
918tr_setup:
919		pc = usbd_xfer_get_frame(xfer, 0);
920		if (ucom_get_data(ucom, pc, 0, usbd_xfer_max_len(xfer), &actlen)) {
921			DPRINTF("Port %d write, has %d bytes\n", ucom->sc_portno, actlen);
922			usbd_xfer_set_frame_len(xfer, 0, actlen);
923			usbd_transfer_submit(xfer);
924		}
925		return;
926
927	default:			/* Error */
928		if (error != USB_ERR_CANCELLED) {
929			/* try to clear stall first */
930			usbd_xfer_set_stall(xfer);
931			goto tr_setup;
932		}
933		return;
934	}
935}
936
937static void
938umcs7840_poll(struct ucom_softc *ucom)
939{
940	struct umcs7840_softc *sc = ucom->sc_parent;
941
942	DPRINTF("Port %d poll\n", ucom->sc_portno);
943	usbd_transfer_poll(sc->sc_ports[ucom->sc_portno].sc_xfer, UMCS7840_N_TRANSFERS);
944	usbd_transfer_poll(&sc->sc_intr_xfer, 1);
945}
946
947static usb_error_t
948umcs7840_get_reg_sync(struct umcs7840_softc *sc, uint8_t reg, uint8_t *data)
949{
950	struct usb_device_request req;
951	usb_error_t err;
952	uint16_t len;
953
954	req.bmRequestType = UT_READ_VENDOR_DEVICE;
955	req.bRequest = MCS7840_RDREQ;
956	USETW(req.wValue, 0);
957	USETW(req.wIndex, reg);
958	USETW(req.wLength, UMCS7840_READ_LENGTH);
959
960	err = usbd_do_request_proc(sc->sc_udev, &sc->sc_super_ucom.sc_tq, &req, (void *)data, 0, &len, UMCS7840_CTRL_TIMEOUT);
961	if (err == USB_ERR_NORMAL_COMPLETION && len != 1) {
962		device_printf(sc->sc_dev, "Reading register %d failed: invalid length %d\n", reg, len);
963		return (USB_ERR_INVAL);
964	} else if (err)
965		device_printf(sc->sc_dev, "Reading register %d failed: %s\n", reg, usbd_errstr(err));
966	return (err);
967}
968
969static usb_error_t
970umcs7840_set_reg_sync(struct umcs7840_softc *sc, uint8_t reg, uint8_t data)
971{
972	struct usb_device_request req;
973	usb_error_t err;
974
975	req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
976	req.bRequest = MCS7840_WRREQ;
977	USETW(req.wValue, data);
978	USETW(req.wIndex, reg);
979	USETW(req.wLength, 0);
980
981	err = usbd_do_request_proc(sc->sc_udev, &sc->sc_super_ucom.sc_tq, &req, NULL, 0, NULL, UMCS7840_CTRL_TIMEOUT);
982	if (err)
983		device_printf(sc->sc_dev, "Writing register %d failed: %s\n", reg, usbd_errstr(err));
984
985	return (err);
986}
987
988static usb_error_t
989umcs7840_get_UART_reg_sync(struct umcs7840_softc *sc, uint8_t portno, uint8_t reg, uint8_t *data)
990{
991	struct usb_device_request req;
992	uint16_t wVal;
993	usb_error_t err;
994	uint16_t len;
995
996	/* portno is port number */
997	wVal = ((uint16_t)(portno + 1)) << 8;
998
999	req.bmRequestType = UT_READ_VENDOR_DEVICE;
1000	req.bRequest = MCS7840_RDREQ;
1001	USETW(req.wValue, wVal);
1002	USETW(req.wIndex, reg);
1003	USETW(req.wLength, UMCS7840_READ_LENGTH);
1004
1005	err = usbd_do_request_proc(sc->sc_udev, &sc->sc_super_ucom.sc_tq, &req, (void *)data, 0, &len, UMCS7840_CTRL_TIMEOUT);
1006	if (err == USB_ERR_NORMAL_COMPLETION && len != 1) {
1007		device_printf(sc->sc_dev, "Reading UART%d register %d failed: invalid length %d\n", portno, reg, len);
1008		return (USB_ERR_INVAL);
1009	} else if (err)
1010		device_printf(sc->sc_dev, "Reading UART%d register %d failed: %s\n", portno, reg, usbd_errstr(err));
1011	return (err);
1012}
1013
1014static usb_error_t
1015umcs7840_set_UART_reg_sync(struct umcs7840_softc *sc, uint8_t portno, uint8_t reg, uint8_t data)
1016{
1017	struct usb_device_request req;
1018	usb_error_t err;
1019	uint16_t wVal;
1020
1021	/* portno is port number */
1022	wVal = ((uint16_t)(portno + 1)) << 8 | data;
1023
1024	req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
1025	req.bRequest = MCS7840_WRREQ;
1026	USETW(req.wValue, wVal);
1027	USETW(req.wIndex, reg);
1028	USETW(req.wLength, 0);
1029
1030	err = usbd_do_request_proc(sc->sc_udev, &sc->sc_super_ucom.sc_tq, &req, NULL, 0, NULL, UMCS7840_CTRL_TIMEOUT);
1031	if (err)
1032		device_printf(sc->sc_dev, "Writing UART%d register %d failed: %s\n", portno, reg, usbd_errstr(err));
1033	return (err);
1034}
1035
1036static usb_error_t
1037umcs7840_set_baudrate(struct umcs7840_softc *sc, uint8_t portno, uint32_t rate)
1038{
1039	usb_error_t err;
1040	uint16_t divisor;
1041	uint8_t clk;
1042	uint8_t data;
1043
1044	if (umcs7840_calc_baudrate(rate, &divisor, &clk)) {
1045		DPRINTF("Port %d bad speed: %d\n", portno, rate);
1046		return (-1);
1047	}
1048	if (divisor == 0 || (clk & MCS7840_DEV_SPx_CLOCK_MASK) != clk) {
1049		DPRINTF("Port %d bad speed calculation: %d\n", portno, rate);
1050		return (-1);
1051	}
1052	DPRINTF("Port %d set speed: %d (%02x / %d)\n", portno, rate, clk, divisor);
1053
1054	/* Set clock source for standard BAUD frequences */
1055	err = umcs7840_get_reg_sync(sc, umcs7840_port_registers[portno].reg_sp, &data);
1056	if (err)
1057		return (err);
1058	data &= MCS7840_DEV_SPx_CLOCK_MASK;
1059	data |= clk;
1060	err = umcs7840_set_reg_sync(sc, umcs7840_port_registers[portno].reg_sp, data);
1061	if (err)
1062		return (err);
1063
1064	/* Set divider */
1065	sc->sc_ports[portno].sc_lcr |= MCS7840_UART_LCR_DIVISORS;
1066	err = umcs7840_set_UART_reg_sync(sc, portno, MCS7840_UART_REG_LCR, sc->sc_ports[portno].sc_lcr);
1067	if (err)
1068		return (err);
1069
1070	err = umcs7840_set_UART_reg_sync(sc, portno, MCS7840_UART_REG_DLL, (uint8_t)(divisor & 0xff));
1071	if (err)
1072		return (err);
1073	err = umcs7840_set_UART_reg_sync(sc, portno, MCS7840_UART_REG_DLM, (uint8_t)((divisor >> 8) & 0xff));
1074	if (err)
1075		return (err);
1076
1077	/* Turn off access to DLL/DLM registers of UART */
1078	sc->sc_ports[portno].sc_lcr &= ~MCS7840_UART_LCR_DIVISORS;
1079	err = umcs7840_set_UART_reg_sync(sc, portno, MCS7840_UART_REG_LCR, sc->sc_ports[portno].sc_lcr);
1080	if (err)
1081		return (err);
1082	return (0);
1083}
1084
1085/* Maximum speeds for standard frequences, when PLL is not used */
1086static const uint32_t umcs7840_baudrate_divisors[] = {0, 115200, 230400, 403200, 460800, 806400, 921600, 1572864, 3145728,};
1087static const uint8_t umcs7840_baudrate_divisors_len = nitems(umcs7840_baudrate_divisors);
1088
1089static usb_error_t
1090umcs7840_calc_baudrate(uint32_t rate, uint16_t *divisor, uint8_t *clk)
1091{
1092	uint8_t i = 0;
1093
1094	if (rate > umcs7840_baudrate_divisors[umcs7840_baudrate_divisors_len - 1])
1095		return (-1);
1096
1097	for (i = 0; i < umcs7840_baudrate_divisors_len - 1 &&
1098	    !(rate > umcs7840_baudrate_divisors[i] && rate <= umcs7840_baudrate_divisors[i + 1]); ++i);
1099	if (rate == 0)
1100		*divisor = 1;	/* XXX */
1101	else
1102		*divisor = umcs7840_baudrate_divisors[i + 1] / rate;
1103	/* 0x00 .. 0x70 */
1104	*clk = i << MCS7840_DEV_SPx_CLOCK_SHIFT;
1105	return (0);
1106}
1107