Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

USB: serial: add uPD78F0730 USB to Serial Adaptor Driver

The adaptor can be found on development boards for 78k, RL78 and V850
microcontrollers produced by Renesas Electronics Corporation.

This is not a full-featured USB to serial converter, however it allows
basic communication and simple control which is enough for programming of
on-board flash and debugging through a debug monitor.

uPD78F0730 is a USB-enabled microcontroller with USB-to-UART conversion
implemented in firmware.

This chip is also present in some debugging adaptors which use it for
USB-to-SPI conversion as well. The present driver doesn't cover SPI,
only USB-to-UART conversion is supported.

Signed-off-by: Maksim Salau <maksim.salau@gmail.com>
Signed-off-by: Johan Hovold <johan@kernel.org>

authored by

Maksim Salau and committed by
Johan Hovold
ea534e0b 51211a3d

+450
+9
drivers/usb/serial/Kconfig
··· 713 713 To compile this driver as a module, choose M here: the 714 714 module will be called quatech-serial. 715 715 716 + config USB_SERIAL_UPD78F0730 717 + tristate "USB Renesas uPD78F0730 Single Port Serial Driver" 718 + help 719 + Say Y here if you want to use the Renesas uPD78F0730 720 + serial driver. 721 + 722 + To compile this driver as a module, choose M here: the 723 + module will be called upd78f0730. 724 + 716 725 config USB_SERIAL_DEBUG 717 726 tristate "USB Debugging Device" 718 727 help
+1
drivers/usb/serial/Makefile
··· 56 56 obj-$(CONFIG_USB_SERIAL_SYMBOL) += symbolserial.o 57 57 obj-$(CONFIG_USB_SERIAL_WWAN) += usb_wwan.o 58 58 obj-$(CONFIG_USB_SERIAL_TI) += ti_usb_3410_5052.o 59 + obj-$(CONFIG_USB_SERIAL_UPD78F0730) += upd78f0730.o 59 60 obj-$(CONFIG_USB_SERIAL_VISOR) += visor.o 60 61 obj-$(CONFIG_USB_SERIAL_WISHBONE) += wishbone-serial.o 61 62 obj-$(CONFIG_USB_SERIAL_WHITEHEAT) += whiteheat.o
+440
drivers/usb/serial/upd78f0730.c
··· 1 + /* 2 + * Renesas Electronics uPD78F0730 USB to serial converter driver 3 + * 4 + * Copyright (C) 2014,2016 Maksim Salau <maksim.salau@gmail.com> 5 + * 6 + * This program is free software; you can redistribute it and/or modify 7 + * it under the terms of the GNU General Public License version 2 8 + * as published by the Free Software Foundation. 9 + * 10 + * Protocol of the adaptor is described in the application note U19660EJ1V0AN00 11 + * μPD78F0730 8-bit Single-Chip Microcontroller 12 + * USB-to-Serial Conversion Software 13 + * <https://www.renesas.com/en-eu/doc/DocumentServer/026/U19660EJ1V0AN00.pdf> 14 + * 15 + * The adaptor functionality is limited to the following: 16 + * - data bits: 7 or 8 17 + * - stop bits: 1 or 2 18 + * - parity: even, odd or none 19 + * - flow control: none 20 + * - baud rates: 0, 2400, 4800, 9600, 19200, 38400, 57600, 115200 21 + * - signals: DTR, RTS and BREAK 22 + */ 23 + 24 + #include <linux/module.h> 25 + #include <linux/slab.h> 26 + #include <linux/tty.h> 27 + #include <linux/usb.h> 28 + #include <linux/usb/serial.h> 29 + 30 + #define DRIVER_DESC "Renesas uPD78F0730 USB to serial converter driver" 31 + 32 + #define DRIVER_AUTHOR "Maksim Salau <maksim.salau@gmail.com>" 33 + 34 + static const struct usb_device_id id_table[] = { 35 + { USB_DEVICE(0x045B, 0x0212) }, /* YRPBRL78G13, YRPBRL78G14 */ 36 + { USB_DEVICE(0x0409, 0x0063) }, /* V850ESJX3-STICK */ 37 + {} 38 + }; 39 + 40 + MODULE_DEVICE_TABLE(usb, id_table); 41 + 42 + /* 43 + * Each adaptor is associated with a private structure, that holds the current 44 + * state of control signals (DTR, RTS and BREAK). 45 + */ 46 + struct upd78f0730_port_private { 47 + struct mutex lock; /* mutex to protect line_signals */ 48 + u8 line_signals; 49 + }; 50 + 51 + /* Op-codes of control commands */ 52 + #define UPD78F0730_CMD_LINE_CONTROL 0x00 53 + #define UPD78F0730_CMD_SET_DTR_RTS 0x01 54 + #define UPD78F0730_CMD_SET_XON_XOFF_CHR 0x02 55 + #define UPD78F0730_CMD_OPEN_CLOSE 0x03 56 + #define UPD78F0730_CMD_SET_ERR_CHR 0x04 57 + 58 + /* Data sizes in UPD78F0730_CMD_LINE_CONTROL command */ 59 + #define UPD78F0730_DATA_SIZE_7_BITS 0x00 60 + #define UPD78F0730_DATA_SIZE_8_BITS 0x01 61 + #define UPD78F0730_DATA_SIZE_MASK 0x01 62 + 63 + /* Stop-bit modes in UPD78F0730_CMD_LINE_CONTROL command */ 64 + #define UPD78F0730_STOP_BIT_1_BIT 0x00 65 + #define UPD78F0730_STOP_BIT_2_BIT 0x02 66 + #define UPD78F0730_STOP_BIT_MASK 0x02 67 + 68 + /* Parity modes in UPD78F0730_CMD_LINE_CONTROL command */ 69 + #define UPD78F0730_PARITY_NONE 0x00 70 + #define UPD78F0730_PARITY_EVEN 0x04 71 + #define UPD78F0730_PARITY_ODD 0x08 72 + #define UPD78F0730_PARITY_MASK 0x0C 73 + 74 + /* Flow control modes in UPD78F0730_CMD_LINE_CONTROL command */ 75 + #define UPD78F0730_FLOW_CONTROL_NONE 0x00 76 + #define UPD78F0730_FLOW_CONTROL_HW 0x10 77 + #define UPD78F0730_FLOW_CONTROL_SW 0x20 78 + #define UPD78F0730_FLOW_CONTROL_MASK 0x30 79 + 80 + /* Control signal bits in UPD78F0730_CMD_SET_DTR_RTS command */ 81 + #define UPD78F0730_RTS 0x01 82 + #define UPD78F0730_DTR 0x02 83 + #define UPD78F0730_BREAK 0x04 84 + 85 + /* Port modes in UPD78F0730_CMD_OPEN_CLOSE command */ 86 + #define UPD78F0730_PORT_CLOSE 0x00 87 + #define UPD78F0730_PORT_OPEN 0x01 88 + 89 + /* Error character substitution modes in UPD78F0730_CMD_SET_ERR_CHR command */ 90 + #define UPD78F0730_ERR_CHR_DISABLED 0x00 91 + #define UPD78F0730_ERR_CHR_ENABLED 0x01 92 + 93 + /* 94 + * Declaration of command structures 95 + */ 96 + 97 + /* UPD78F0730_CMD_LINE_CONTROL command */ 98 + struct upd78f0730_line_control { 99 + u8 opcode; 100 + __le32 baud_rate; 101 + u8 params; 102 + } __packed; 103 + 104 + /* UPD78F0730_CMD_SET_DTR_RTS command */ 105 + struct upd78f0730_set_dtr_rts { 106 + u8 opcode; 107 + u8 params; 108 + }; 109 + 110 + /* UPD78F0730_CMD_SET_XON_OFF_CHR command */ 111 + struct upd78f0730_set_xon_xoff_chr { 112 + u8 opcode; 113 + u8 xon; 114 + u8 xoff; 115 + }; 116 + 117 + /* UPD78F0730_CMD_OPEN_CLOSE command */ 118 + struct upd78f0730_open_close { 119 + u8 opcode; 120 + u8 state; 121 + }; 122 + 123 + /* UPD78F0730_CMD_SET_ERR_CHR command */ 124 + struct upd78f0730_set_err_chr { 125 + u8 opcode; 126 + u8 state; 127 + u8 err_char; 128 + }; 129 + 130 + static int upd78f0730_send_ctl(struct usb_serial_port *port, 131 + const void *data, int size) 132 + { 133 + struct usb_device *usbdev = port->serial->dev; 134 + void *buf; 135 + int res; 136 + 137 + if (size <= 0 || !data) 138 + return -EINVAL; 139 + 140 + buf = kmemdup(data, size, GFP_KERNEL); 141 + if (!buf) 142 + return -ENOMEM; 143 + 144 + res = usb_control_msg(usbdev, usb_sndctrlpipe(usbdev, 0), 0x00, 145 + USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_OUT, 146 + 0x0000, 0x0000, buf, size, USB_CTRL_SET_TIMEOUT); 147 + 148 + kfree(buf); 149 + 150 + if (res != size) { 151 + struct device *dev = &port->dev; 152 + 153 + dev_err(dev, "failed to send control request %02x: %d\n", 154 + *(u8 *)data, res); 155 + /* The maximum expected length of a transfer is 6 bytes */ 156 + if (res >= 0) 157 + res = -EIO; 158 + 159 + return res; 160 + } 161 + 162 + return 0; 163 + } 164 + 165 + static int upd78f0730_port_probe(struct usb_serial_port *port) 166 + { 167 + struct upd78f0730_port_private *private; 168 + 169 + private = kzalloc(sizeof(*private), GFP_KERNEL); 170 + if (!private) 171 + return -ENOMEM; 172 + 173 + mutex_init(&private->lock); 174 + usb_set_serial_port_data(port, private); 175 + 176 + return 0; 177 + } 178 + 179 + static int upd78f0730_port_remove(struct usb_serial_port *port) 180 + { 181 + struct upd78f0730_port_private *private; 182 + 183 + private = usb_get_serial_port_data(port); 184 + mutex_destroy(&private->lock); 185 + kfree(private); 186 + 187 + return 0; 188 + } 189 + 190 + static int upd78f0730_tiocmget(struct tty_struct *tty) 191 + { 192 + struct device *dev = tty->dev; 193 + struct upd78f0730_port_private *private; 194 + struct usb_serial_port *port = tty->driver_data; 195 + int signals; 196 + int res; 197 + 198 + private = usb_get_serial_port_data(port); 199 + 200 + mutex_lock(&private->lock); 201 + signals = private->line_signals; 202 + mutex_unlock(&private->lock); 203 + 204 + res = ((signals & UPD78F0730_DTR) ? TIOCM_DTR : 0) | 205 + ((signals & UPD78F0730_RTS) ? TIOCM_RTS : 0); 206 + 207 + dev_dbg(dev, "%s - res = %x\n", __func__, res); 208 + 209 + return res; 210 + } 211 + 212 + static int upd78f0730_tiocmset(struct tty_struct *tty, 213 + unsigned int set, unsigned int clear) 214 + { 215 + struct device *dev = tty->dev; 216 + struct usb_serial_port *port = tty->driver_data; 217 + struct upd78f0730_port_private *private; 218 + struct upd78f0730_set_dtr_rts request; 219 + int res; 220 + 221 + private = usb_get_serial_port_data(port); 222 + 223 + mutex_lock(&private->lock); 224 + if (set & TIOCM_DTR) { 225 + private->line_signals |= UPD78F0730_DTR; 226 + dev_dbg(dev, "%s - set DTR\n", __func__); 227 + } 228 + if (set & TIOCM_RTS) { 229 + private->line_signals |= UPD78F0730_RTS; 230 + dev_dbg(dev, "%s - set RTS\n", __func__); 231 + } 232 + if (clear & TIOCM_DTR) { 233 + private->line_signals &= ~UPD78F0730_DTR; 234 + dev_dbg(dev, "%s - clear DTR\n", __func__); 235 + } 236 + if (clear & TIOCM_RTS) { 237 + private->line_signals &= ~UPD78F0730_RTS; 238 + dev_dbg(dev, "%s - clear RTS\n", __func__); 239 + } 240 + request.opcode = UPD78F0730_CMD_SET_DTR_RTS; 241 + request.params = private->line_signals; 242 + 243 + res = upd78f0730_send_ctl(port, &request, sizeof(request)); 244 + mutex_unlock(&private->lock); 245 + 246 + return res; 247 + } 248 + 249 + static void upd78f0730_break_ctl(struct tty_struct *tty, int break_state) 250 + { 251 + struct device *dev = tty->dev; 252 + struct upd78f0730_port_private *private; 253 + struct usb_serial_port *port = tty->driver_data; 254 + struct upd78f0730_set_dtr_rts request; 255 + 256 + private = usb_get_serial_port_data(port); 257 + 258 + mutex_lock(&private->lock); 259 + if (break_state) { 260 + private->line_signals |= UPD78F0730_BREAK; 261 + dev_dbg(dev, "%s - set BREAK\n", __func__); 262 + } else { 263 + private->line_signals &= ~UPD78F0730_BREAK; 264 + dev_dbg(dev, "%s - clear BREAK\n", __func__); 265 + } 266 + request.opcode = UPD78F0730_CMD_SET_DTR_RTS; 267 + request.params = private->line_signals; 268 + 269 + upd78f0730_send_ctl(port, &request, sizeof(request)); 270 + mutex_unlock(&private->lock); 271 + } 272 + 273 + static void upd78f0730_dtr_rts(struct usb_serial_port *port, int on) 274 + { 275 + struct tty_struct *tty = port->port.tty; 276 + unsigned int set = 0; 277 + unsigned int clear = 0; 278 + 279 + if (on) 280 + set = TIOCM_DTR | TIOCM_RTS; 281 + else 282 + clear = TIOCM_DTR | TIOCM_RTS; 283 + 284 + upd78f0730_tiocmset(tty, set, clear); 285 + } 286 + 287 + static speed_t upd78f0730_get_baud_rate(struct tty_struct *tty) 288 + { 289 + const speed_t baud_rate = tty_get_baud_rate(tty); 290 + const speed_t supported[] = { 291 + 0, 2400, 4800, 9600, 19200, 38400, 57600, 115200 292 + }; 293 + int i; 294 + 295 + for (i = ARRAY_SIZE(supported) - 1; i >= 0; i--) { 296 + if (baud_rate == supported[i]) 297 + return baud_rate; 298 + } 299 + 300 + /* If the baud rate is not supported, switch to the default one */ 301 + tty_encode_baud_rate(tty, 9600, 9600); 302 + 303 + return tty_get_baud_rate(tty); 304 + } 305 + 306 + static void upd78f0730_set_termios(struct tty_struct *tty, 307 + struct usb_serial_port *port, 308 + struct ktermios *old_termios) 309 + { 310 + struct device *dev = &port->dev; 311 + struct upd78f0730_line_control request; 312 + speed_t baud_rate; 313 + 314 + if (old_termios && !tty_termios_hw_change(&tty->termios, old_termios)) 315 + return; 316 + 317 + if (C_BAUD(tty) == B0) 318 + upd78f0730_dtr_rts(port, 0); 319 + else if (old_termios && (old_termios->c_cflag & CBAUD) == B0) 320 + upd78f0730_dtr_rts(port, 1); 321 + 322 + baud_rate = upd78f0730_get_baud_rate(tty); 323 + request.opcode = UPD78F0730_CMD_LINE_CONTROL; 324 + request.baud_rate = cpu_to_le32(baud_rate); 325 + request.params = 0; 326 + dev_dbg(dev, "%s - baud rate = %d\n", __func__, baud_rate); 327 + 328 + switch (C_CSIZE(tty)) { 329 + case CS7: 330 + request.params |= UPD78F0730_DATA_SIZE_7_BITS; 331 + dev_dbg(dev, "%s - 7 data bits\n", __func__); 332 + break; 333 + default: 334 + tty->termios.c_cflag &= ~CSIZE; 335 + tty->termios.c_cflag |= CS8; 336 + dev_warn(dev, "data size is not supported, using 8 bits\n"); 337 + /* fall through */ 338 + case CS8: 339 + request.params |= UPD78F0730_DATA_SIZE_8_BITS; 340 + dev_dbg(dev, "%s - 8 data bits\n", __func__); 341 + break; 342 + } 343 + 344 + if (C_PARENB(tty)) { 345 + if (C_PARODD(tty)) { 346 + request.params |= UPD78F0730_PARITY_ODD; 347 + dev_dbg(dev, "%s - odd parity\n", __func__); 348 + } else { 349 + request.params |= UPD78F0730_PARITY_EVEN; 350 + dev_dbg(dev, "%s - even parity\n", __func__); 351 + } 352 + 353 + if (C_CMSPAR(tty)) { 354 + tty->termios.c_cflag &= ~CMSPAR; 355 + dev_warn(dev, "MARK/SPACE parity is not supported\n"); 356 + } 357 + } else { 358 + request.params |= UPD78F0730_PARITY_NONE; 359 + dev_dbg(dev, "%s - no parity\n", __func__); 360 + } 361 + 362 + if (C_CSTOPB(tty)) { 363 + request.params |= UPD78F0730_STOP_BIT_2_BIT; 364 + dev_dbg(dev, "%s - 2 stop bits\n", __func__); 365 + } else { 366 + request.params |= UPD78F0730_STOP_BIT_1_BIT; 367 + dev_dbg(dev, "%s - 1 stop bit\n", __func__); 368 + } 369 + 370 + if (C_CRTSCTS(tty)) { 371 + tty->termios.c_cflag &= ~CRTSCTS; 372 + dev_warn(dev, "RTSCTS flow control is not supported\n"); 373 + } 374 + if (I_IXOFF(tty) || I_IXON(tty)) { 375 + tty->termios.c_iflag &= ~(IXOFF | IXON); 376 + dev_warn(dev, "XON/XOFF flow control is not supported\n"); 377 + } 378 + request.params |= UPD78F0730_FLOW_CONTROL_NONE; 379 + dev_dbg(dev, "%s - no flow control\n", __func__); 380 + 381 + upd78f0730_send_ctl(port, &request, sizeof(request)); 382 + } 383 + 384 + static int upd78f0730_open(struct tty_struct *tty, struct usb_serial_port *port) 385 + { 386 + struct upd78f0730_open_close request = { 387 + .opcode = UPD78F0730_CMD_OPEN_CLOSE, 388 + .state = UPD78F0730_PORT_OPEN 389 + }; 390 + int res; 391 + 392 + res = upd78f0730_send_ctl(port, &request, sizeof(request)); 393 + if (res) 394 + return res; 395 + 396 + if (tty) 397 + upd78f0730_set_termios(tty, port, NULL); 398 + 399 + return usb_serial_generic_open(tty, port); 400 + } 401 + 402 + static void upd78f0730_close(struct usb_serial_port *port) 403 + { 404 + struct upd78f0730_open_close request = { 405 + .opcode = UPD78F0730_CMD_OPEN_CLOSE, 406 + .state = UPD78F0730_PORT_CLOSE 407 + }; 408 + 409 + usb_serial_generic_close(port); 410 + upd78f0730_send_ctl(port, &request, sizeof(request)); 411 + } 412 + 413 + static struct usb_serial_driver upd78f0730_device = { 414 + .driver = { 415 + .owner = THIS_MODULE, 416 + .name = "upd78f0730", 417 + }, 418 + .id_table = id_table, 419 + .num_ports = 1, 420 + .port_probe = upd78f0730_port_probe, 421 + .port_remove = upd78f0730_port_remove, 422 + .open = upd78f0730_open, 423 + .close = upd78f0730_close, 424 + .set_termios = upd78f0730_set_termios, 425 + .tiocmget = upd78f0730_tiocmget, 426 + .tiocmset = upd78f0730_tiocmset, 427 + .dtr_rts = upd78f0730_dtr_rts, 428 + .break_ctl = upd78f0730_break_ctl, 429 + }; 430 + 431 + static struct usb_serial_driver * const serial_drivers[] = { 432 + &upd78f0730_device, 433 + NULL 434 + }; 435 + 436 + module_usb_serial_driver(serial_drivers, id_table); 437 + 438 + MODULE_DESCRIPTION(DRIVER_DESC); 439 + MODULE_AUTHOR(DRIVER_AUTHOR); 440 + MODULE_LICENSE("GPL v2");