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