18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Clean ups from Moschip version and a few ioctl implementations by: 48c2ecf20Sopenharmony_ci * Paul B Schroeder <pschroeder "at" uplogix "dot" com> 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * Originally based on drivers/usb/serial/io_edgeport.c which is: 78c2ecf20Sopenharmony_ci * Copyright (C) 2000 Inside Out Networks, All rights reserved. 88c2ecf20Sopenharmony_ci * Copyright (C) 2001-2002 Greg Kroah-Hartman <greg@kroah.com> 98c2ecf20Sopenharmony_ci * 108c2ecf20Sopenharmony_ci */ 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci#include <linux/kernel.h> 138c2ecf20Sopenharmony_ci#include <linux/errno.h> 148c2ecf20Sopenharmony_ci#include <linux/slab.h> 158c2ecf20Sopenharmony_ci#include <linux/tty.h> 168c2ecf20Sopenharmony_ci#include <linux/tty_driver.h> 178c2ecf20Sopenharmony_ci#include <linux/tty_flip.h> 188c2ecf20Sopenharmony_ci#include <linux/module.h> 198c2ecf20Sopenharmony_ci#include <linux/serial.h> 208c2ecf20Sopenharmony_ci#include <linux/usb.h> 218c2ecf20Sopenharmony_ci#include <linux/usb/serial.h> 228c2ecf20Sopenharmony_ci#include <linux/uaccess.h> 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_ci#define DRIVER_DESC "Moschip 7840/7820 USB Serial Driver" 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci/* 278c2ecf20Sopenharmony_ci * 16C50 UART register defines 288c2ecf20Sopenharmony_ci */ 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ci#define LCR_BITS_5 0x00 /* 5 bits/char */ 318c2ecf20Sopenharmony_ci#define LCR_BITS_6 0x01 /* 6 bits/char */ 328c2ecf20Sopenharmony_ci#define LCR_BITS_7 0x02 /* 7 bits/char */ 338c2ecf20Sopenharmony_ci#define LCR_BITS_8 0x03 /* 8 bits/char */ 348c2ecf20Sopenharmony_ci#define LCR_BITS_MASK 0x03 /* Mask for bits/char field */ 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci#define LCR_STOP_1 0x00 /* 1 stop bit */ 378c2ecf20Sopenharmony_ci#define LCR_STOP_1_5 0x04 /* 1.5 stop bits (if 5 bits/char) */ 388c2ecf20Sopenharmony_ci#define LCR_STOP_2 0x04 /* 2 stop bits (if 6-8 bits/char) */ 398c2ecf20Sopenharmony_ci#define LCR_STOP_MASK 0x04 /* Mask for stop bits field */ 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_ci#define LCR_PAR_NONE 0x00 /* No parity */ 428c2ecf20Sopenharmony_ci#define LCR_PAR_ODD 0x08 /* Odd parity */ 438c2ecf20Sopenharmony_ci#define LCR_PAR_EVEN 0x18 /* Even parity */ 448c2ecf20Sopenharmony_ci#define LCR_PAR_MARK 0x28 /* Force parity bit to 1 */ 458c2ecf20Sopenharmony_ci#define LCR_PAR_SPACE 0x38 /* Force parity bit to 0 */ 468c2ecf20Sopenharmony_ci#define LCR_PAR_MASK 0x38 /* Mask for parity field */ 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_ci#define LCR_SET_BREAK 0x40 /* Set Break condition */ 498c2ecf20Sopenharmony_ci#define LCR_DL_ENABLE 0x80 /* Enable access to divisor latch */ 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci#define MCR_DTR 0x01 /* Assert DTR */ 528c2ecf20Sopenharmony_ci#define MCR_RTS 0x02 /* Assert RTS */ 538c2ecf20Sopenharmony_ci#define MCR_OUT1 0x04 /* Loopback only: Sets state of RI */ 548c2ecf20Sopenharmony_ci#define MCR_MASTER_IE 0x08 /* Enable interrupt outputs */ 558c2ecf20Sopenharmony_ci#define MCR_LOOPBACK 0x10 /* Set internal (digital) loopback mode */ 568c2ecf20Sopenharmony_ci#define MCR_XON_ANY 0x20 /* Enable any char to exit XOFF mode */ 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci#define MOS7840_MSR_CTS 0x10 /* Current state of CTS */ 598c2ecf20Sopenharmony_ci#define MOS7840_MSR_DSR 0x20 /* Current state of DSR */ 608c2ecf20Sopenharmony_ci#define MOS7840_MSR_RI 0x40 /* Current state of RI */ 618c2ecf20Sopenharmony_ci#define MOS7840_MSR_CD 0x80 /* Current state of CD */ 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ci/* 648c2ecf20Sopenharmony_ci * Defines used for sending commands to port 658c2ecf20Sopenharmony_ci */ 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci#define MOS_WDR_TIMEOUT 5000 /* default urb timeout */ 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ci#define MOS_PORT1 0x0200 708c2ecf20Sopenharmony_ci#define MOS_PORT2 0x0300 718c2ecf20Sopenharmony_ci#define MOS_VENREG 0x0000 728c2ecf20Sopenharmony_ci#define MOS_MAX_PORT 0x02 738c2ecf20Sopenharmony_ci#define MOS_WRITE 0x0E 748c2ecf20Sopenharmony_ci#define MOS_READ 0x0D 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci/* Requests */ 778c2ecf20Sopenharmony_ci#define MCS_RD_RTYPE 0xC0 788c2ecf20Sopenharmony_ci#define MCS_WR_RTYPE 0x40 798c2ecf20Sopenharmony_ci#define MCS_RDREQ 0x0D 808c2ecf20Sopenharmony_ci#define MCS_WRREQ 0x0E 818c2ecf20Sopenharmony_ci#define MCS_CTRL_TIMEOUT 500 828c2ecf20Sopenharmony_ci#define VENDOR_READ_LENGTH (0x01) 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci#define MAX_NAME_LEN 64 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci#define ZLP_REG1 0x3A /* Zero_Flag_Reg1 58 */ 878c2ecf20Sopenharmony_ci#define ZLP_REG5 0x3E /* Zero_Flag_Reg5 62 */ 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci/* For higher baud Rates use TIOCEXBAUD */ 908c2ecf20Sopenharmony_ci#define TIOCEXBAUD 0x5462 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci/* 938c2ecf20Sopenharmony_ci * Vendor id and device id defines 948c2ecf20Sopenharmony_ci * 958c2ecf20Sopenharmony_ci * NOTE: Do not add new defines, add entries directly to the id_table instead. 968c2ecf20Sopenharmony_ci */ 978c2ecf20Sopenharmony_ci#define USB_VENDOR_ID_BANDB 0x0856 988c2ecf20Sopenharmony_ci#define BANDB_DEVICE_ID_USO9ML2_2 0xAC22 998c2ecf20Sopenharmony_ci#define BANDB_DEVICE_ID_USO9ML2_2P 0xBC00 1008c2ecf20Sopenharmony_ci#define BANDB_DEVICE_ID_USO9ML2_4 0xAC24 1018c2ecf20Sopenharmony_ci#define BANDB_DEVICE_ID_USO9ML2_4P 0xBC01 1028c2ecf20Sopenharmony_ci#define BANDB_DEVICE_ID_US9ML2_2 0xAC29 1038c2ecf20Sopenharmony_ci#define BANDB_DEVICE_ID_US9ML2_4 0xAC30 1048c2ecf20Sopenharmony_ci#define BANDB_DEVICE_ID_USPTL4_2 0xAC31 1058c2ecf20Sopenharmony_ci#define BANDB_DEVICE_ID_USPTL4_4 0xAC32 1068c2ecf20Sopenharmony_ci#define BANDB_DEVICE_ID_USOPTL4_2 0xAC42 1078c2ecf20Sopenharmony_ci#define BANDB_DEVICE_ID_USOPTL4_2P 0xBC02 1088c2ecf20Sopenharmony_ci#define BANDB_DEVICE_ID_USOPTL4_4 0xAC44 1098c2ecf20Sopenharmony_ci#define BANDB_DEVICE_ID_USOPTL4_4P 0xBC03 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci/* Interrupt Routine Defines */ 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci#define SERIAL_IIR_RLS 0x06 1148c2ecf20Sopenharmony_ci#define SERIAL_IIR_MS 0x00 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci/* 1178c2ecf20Sopenharmony_ci * Emulation of the bit mask on the LINE STATUS REGISTER. 1188c2ecf20Sopenharmony_ci */ 1198c2ecf20Sopenharmony_ci#define SERIAL_LSR_DR 0x0001 1208c2ecf20Sopenharmony_ci#define SERIAL_LSR_OE 0x0002 1218c2ecf20Sopenharmony_ci#define SERIAL_LSR_PE 0x0004 1228c2ecf20Sopenharmony_ci#define SERIAL_LSR_FE 0x0008 1238c2ecf20Sopenharmony_ci#define SERIAL_LSR_BI 0x0010 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci#define MOS_MSR_DELTA_CTS 0x10 1268c2ecf20Sopenharmony_ci#define MOS_MSR_DELTA_DSR 0x20 1278c2ecf20Sopenharmony_ci#define MOS_MSR_DELTA_RI 0x40 1288c2ecf20Sopenharmony_ci#define MOS_MSR_DELTA_CD 0x80 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci/* Serial Port register Address */ 1318c2ecf20Sopenharmony_ci#define INTERRUPT_ENABLE_REGISTER ((__u16)(0x01)) 1328c2ecf20Sopenharmony_ci#define FIFO_CONTROL_REGISTER ((__u16)(0x02)) 1338c2ecf20Sopenharmony_ci#define LINE_CONTROL_REGISTER ((__u16)(0x03)) 1348c2ecf20Sopenharmony_ci#define MODEM_CONTROL_REGISTER ((__u16)(0x04)) 1358c2ecf20Sopenharmony_ci#define LINE_STATUS_REGISTER ((__u16)(0x05)) 1368c2ecf20Sopenharmony_ci#define MODEM_STATUS_REGISTER ((__u16)(0x06)) 1378c2ecf20Sopenharmony_ci#define SCRATCH_PAD_REGISTER ((__u16)(0x07)) 1388c2ecf20Sopenharmony_ci#define DIVISOR_LATCH_LSB ((__u16)(0x00)) 1398c2ecf20Sopenharmony_ci#define DIVISOR_LATCH_MSB ((__u16)(0x01)) 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci#define CLK_MULTI_REGISTER ((__u16)(0x02)) 1428c2ecf20Sopenharmony_ci#define CLK_START_VALUE_REGISTER ((__u16)(0x03)) 1438c2ecf20Sopenharmony_ci#define GPIO_REGISTER ((__u16)(0x07)) 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci#define SERIAL_LCR_DLAB ((__u16)(0x0080)) 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci/* 1488c2ecf20Sopenharmony_ci * URB POOL related defines 1498c2ecf20Sopenharmony_ci */ 1508c2ecf20Sopenharmony_ci#define NUM_URBS 16 /* URB Count */ 1518c2ecf20Sopenharmony_ci#define URB_TRANSFER_BUFFER_SIZE 32 /* URB Size */ 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci/* LED on/off milliseconds*/ 1548c2ecf20Sopenharmony_ci#define LED_ON_MS 500 1558c2ecf20Sopenharmony_ci#define LED_OFF_MS 500 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_cienum mos7840_flag { 1588c2ecf20Sopenharmony_ci MOS7840_FLAG_LED_BUSY, 1598c2ecf20Sopenharmony_ci}; 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_ci#define MCS_PORT_MASK GENMASK(2, 0) 1628c2ecf20Sopenharmony_ci#define MCS_PORTS(nr) ((nr) & MCS_PORT_MASK) 1638c2ecf20Sopenharmony_ci#define MCS_LED BIT(3) 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci#define MCS_DEVICE(vid, pid, flags) \ 1668c2ecf20Sopenharmony_ci USB_DEVICE((vid), (pid)), .driver_info = (flags) 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_cistatic const struct usb_device_id id_table[] = { 1698c2ecf20Sopenharmony_ci { MCS_DEVICE(0x0557, 0x2011, MCS_PORTS(4)) }, /* ATEN UC2324 */ 1708c2ecf20Sopenharmony_ci { MCS_DEVICE(0x0557, 0x7820, MCS_PORTS(2)) }, /* ATEN UC2322 */ 1718c2ecf20Sopenharmony_ci { MCS_DEVICE(0x110a, 0x2210, MCS_PORTS(2)) }, /* Moxa UPort 2210 */ 1728c2ecf20Sopenharmony_ci { MCS_DEVICE(0x9710, 0x7810, MCS_PORTS(1) | MCS_LED) }, /* ASIX MCS7810 */ 1738c2ecf20Sopenharmony_ci { MCS_DEVICE(0x9710, 0x7820, MCS_PORTS(2)) }, /* MosChip MCS7820 */ 1748c2ecf20Sopenharmony_ci { MCS_DEVICE(0x9710, 0x7840, MCS_PORTS(4)) }, /* MosChip MCS7840 */ 1758c2ecf20Sopenharmony_ci { MCS_DEVICE(0x9710, 0x7843, MCS_PORTS(3)) }, /* ASIX MCS7840 3 port */ 1768c2ecf20Sopenharmony_ci { USB_DEVICE(USB_VENDOR_ID_BANDB, BANDB_DEVICE_ID_USO9ML2_2) }, 1778c2ecf20Sopenharmony_ci { USB_DEVICE(USB_VENDOR_ID_BANDB, BANDB_DEVICE_ID_USO9ML2_2P) }, 1788c2ecf20Sopenharmony_ci { USB_DEVICE(USB_VENDOR_ID_BANDB, BANDB_DEVICE_ID_USO9ML2_4) }, 1798c2ecf20Sopenharmony_ci { USB_DEVICE(USB_VENDOR_ID_BANDB, BANDB_DEVICE_ID_USO9ML2_4P) }, 1808c2ecf20Sopenharmony_ci { USB_DEVICE(USB_VENDOR_ID_BANDB, BANDB_DEVICE_ID_US9ML2_2) }, 1818c2ecf20Sopenharmony_ci { USB_DEVICE(USB_VENDOR_ID_BANDB, BANDB_DEVICE_ID_US9ML2_4) }, 1828c2ecf20Sopenharmony_ci { USB_DEVICE(USB_VENDOR_ID_BANDB, BANDB_DEVICE_ID_USPTL4_2) }, 1838c2ecf20Sopenharmony_ci { USB_DEVICE(USB_VENDOR_ID_BANDB, BANDB_DEVICE_ID_USPTL4_4) }, 1848c2ecf20Sopenharmony_ci { USB_DEVICE(USB_VENDOR_ID_BANDB, BANDB_DEVICE_ID_USOPTL4_2) }, 1858c2ecf20Sopenharmony_ci { USB_DEVICE(USB_VENDOR_ID_BANDB, BANDB_DEVICE_ID_USOPTL4_2P) }, 1868c2ecf20Sopenharmony_ci { USB_DEVICE(USB_VENDOR_ID_BANDB, BANDB_DEVICE_ID_USOPTL4_4) }, 1878c2ecf20Sopenharmony_ci { USB_DEVICE(USB_VENDOR_ID_BANDB, BANDB_DEVICE_ID_USOPTL4_4P) }, 1888c2ecf20Sopenharmony_ci {} /* terminating entry */ 1898c2ecf20Sopenharmony_ci}; 1908c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(usb, id_table); 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci/* This structure holds all of the local port information */ 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_cistruct moschip_port { 1958c2ecf20Sopenharmony_ci int port_num; /*Actual port number in the device(1,2,etc) */ 1968c2ecf20Sopenharmony_ci struct urb *read_urb; /* read URB for this port */ 1978c2ecf20Sopenharmony_ci __u8 shadowLCR; /* last LCR value received */ 1988c2ecf20Sopenharmony_ci __u8 shadowMCR; /* last MCR value received */ 1998c2ecf20Sopenharmony_ci struct usb_serial_port *port; /* loop back to the owner of this object */ 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci /* Offsets */ 2028c2ecf20Sopenharmony_ci __u8 SpRegOffset; 2038c2ecf20Sopenharmony_ci __u8 ControlRegOffset; 2048c2ecf20Sopenharmony_ci __u8 DcrRegOffset; 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci spinlock_t pool_lock; 2078c2ecf20Sopenharmony_ci struct urb *write_urb_pool[NUM_URBS]; 2088c2ecf20Sopenharmony_ci char busy[NUM_URBS]; 2098c2ecf20Sopenharmony_ci bool read_urb_busy; 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_ci /* For device(s) with LED indicator */ 2128c2ecf20Sopenharmony_ci bool has_led; 2138c2ecf20Sopenharmony_ci struct timer_list led_timer1; /* Timer for LED on */ 2148c2ecf20Sopenharmony_ci struct timer_list led_timer2; /* Timer for LED off */ 2158c2ecf20Sopenharmony_ci struct urb *led_urb; 2168c2ecf20Sopenharmony_ci struct usb_ctrlrequest *led_dr; 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci unsigned long flags; 2198c2ecf20Sopenharmony_ci}; 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci/* 2228c2ecf20Sopenharmony_ci * mos7840_set_reg_sync 2238c2ecf20Sopenharmony_ci * To set the Control register by calling usb_fill_control_urb function 2248c2ecf20Sopenharmony_ci * by passing usb_sndctrlpipe function as parameter. 2258c2ecf20Sopenharmony_ci */ 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_cistatic int mos7840_set_reg_sync(struct usb_serial_port *port, __u16 reg, 2288c2ecf20Sopenharmony_ci __u16 val) 2298c2ecf20Sopenharmony_ci{ 2308c2ecf20Sopenharmony_ci struct usb_device *dev = port->serial->dev; 2318c2ecf20Sopenharmony_ci val = val & 0x00ff; 2328c2ecf20Sopenharmony_ci dev_dbg(&port->dev, "mos7840_set_reg_sync offset is %x, value %x\n", reg, val); 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_ci return usb_control_msg(dev, usb_sndctrlpipe(dev, 0), MCS_WRREQ, 2358c2ecf20Sopenharmony_ci MCS_WR_RTYPE, val, reg, NULL, 0, 2368c2ecf20Sopenharmony_ci MOS_WDR_TIMEOUT); 2378c2ecf20Sopenharmony_ci} 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_ci/* 2408c2ecf20Sopenharmony_ci * mos7840_get_reg_sync 2418c2ecf20Sopenharmony_ci * To set the Uart register by calling usb_fill_control_urb function by 2428c2ecf20Sopenharmony_ci * passing usb_rcvctrlpipe function as parameter. 2438c2ecf20Sopenharmony_ci */ 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_cistatic int mos7840_get_reg_sync(struct usb_serial_port *port, __u16 reg, 2468c2ecf20Sopenharmony_ci __u16 *val) 2478c2ecf20Sopenharmony_ci{ 2488c2ecf20Sopenharmony_ci struct usb_device *dev = port->serial->dev; 2498c2ecf20Sopenharmony_ci int ret = 0; 2508c2ecf20Sopenharmony_ci u8 *buf; 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_ci buf = kmalloc(VENDOR_READ_LENGTH, GFP_KERNEL); 2538c2ecf20Sopenharmony_ci if (!buf) 2548c2ecf20Sopenharmony_ci return -ENOMEM; 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_ci ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), MCS_RDREQ, 2578c2ecf20Sopenharmony_ci MCS_RD_RTYPE, 0, reg, buf, VENDOR_READ_LENGTH, 2588c2ecf20Sopenharmony_ci MOS_WDR_TIMEOUT); 2598c2ecf20Sopenharmony_ci if (ret < VENDOR_READ_LENGTH) { 2608c2ecf20Sopenharmony_ci if (ret >= 0) 2618c2ecf20Sopenharmony_ci ret = -EIO; 2628c2ecf20Sopenharmony_ci goto out; 2638c2ecf20Sopenharmony_ci } 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_ci *val = buf[0]; 2668c2ecf20Sopenharmony_ci dev_dbg(&port->dev, "%s offset is %x, return val %x\n", __func__, reg, *val); 2678c2ecf20Sopenharmony_ciout: 2688c2ecf20Sopenharmony_ci kfree(buf); 2698c2ecf20Sopenharmony_ci return ret; 2708c2ecf20Sopenharmony_ci} 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_ci/* 2738c2ecf20Sopenharmony_ci * mos7840_set_uart_reg 2748c2ecf20Sopenharmony_ci * To set the Uart register by calling usb_fill_control_urb function by 2758c2ecf20Sopenharmony_ci * passing usb_sndctrlpipe function as parameter. 2768c2ecf20Sopenharmony_ci */ 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_cistatic int mos7840_set_uart_reg(struct usb_serial_port *port, __u16 reg, 2798c2ecf20Sopenharmony_ci __u16 val) 2808c2ecf20Sopenharmony_ci{ 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_ci struct usb_device *dev = port->serial->dev; 2838c2ecf20Sopenharmony_ci val = val & 0x00ff; 2848c2ecf20Sopenharmony_ci /* For the UART control registers, the application number need 2858c2ecf20Sopenharmony_ci to be Or'ed */ 2868c2ecf20Sopenharmony_ci if (port->serial->num_ports == 2 && port->port_number != 0) 2878c2ecf20Sopenharmony_ci val |= ((__u16)port->port_number + 2) << 8; 2888c2ecf20Sopenharmony_ci else 2898c2ecf20Sopenharmony_ci val |= ((__u16)port->port_number + 1) << 8; 2908c2ecf20Sopenharmony_ci dev_dbg(&port->dev, "%s application number is %x\n", __func__, val); 2918c2ecf20Sopenharmony_ci return usb_control_msg(dev, usb_sndctrlpipe(dev, 0), MCS_WRREQ, 2928c2ecf20Sopenharmony_ci MCS_WR_RTYPE, val, reg, NULL, 0, 2938c2ecf20Sopenharmony_ci MOS_WDR_TIMEOUT); 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_ci} 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_ci/* 2988c2ecf20Sopenharmony_ci * mos7840_get_uart_reg 2998c2ecf20Sopenharmony_ci * To set the Control register by calling usb_fill_control_urb function 3008c2ecf20Sopenharmony_ci * by passing usb_rcvctrlpipe function as parameter. 3018c2ecf20Sopenharmony_ci */ 3028c2ecf20Sopenharmony_cistatic int mos7840_get_uart_reg(struct usb_serial_port *port, __u16 reg, 3038c2ecf20Sopenharmony_ci __u16 *val) 3048c2ecf20Sopenharmony_ci{ 3058c2ecf20Sopenharmony_ci struct usb_device *dev = port->serial->dev; 3068c2ecf20Sopenharmony_ci int ret = 0; 3078c2ecf20Sopenharmony_ci __u16 Wval; 3088c2ecf20Sopenharmony_ci u8 *buf; 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_ci buf = kmalloc(VENDOR_READ_LENGTH, GFP_KERNEL); 3118c2ecf20Sopenharmony_ci if (!buf) 3128c2ecf20Sopenharmony_ci return -ENOMEM; 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_ci /* Wval is same as application number */ 3158c2ecf20Sopenharmony_ci if (port->serial->num_ports == 2 && port->port_number != 0) 3168c2ecf20Sopenharmony_ci Wval = ((__u16)port->port_number + 2) << 8; 3178c2ecf20Sopenharmony_ci else 3188c2ecf20Sopenharmony_ci Wval = ((__u16)port->port_number + 1) << 8; 3198c2ecf20Sopenharmony_ci dev_dbg(&port->dev, "%s application number is %x\n", __func__, Wval); 3208c2ecf20Sopenharmony_ci ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), MCS_RDREQ, 3218c2ecf20Sopenharmony_ci MCS_RD_RTYPE, Wval, reg, buf, VENDOR_READ_LENGTH, 3228c2ecf20Sopenharmony_ci MOS_WDR_TIMEOUT); 3238c2ecf20Sopenharmony_ci if (ret < VENDOR_READ_LENGTH) { 3248c2ecf20Sopenharmony_ci if (ret >= 0) 3258c2ecf20Sopenharmony_ci ret = -EIO; 3268c2ecf20Sopenharmony_ci goto out; 3278c2ecf20Sopenharmony_ci } 3288c2ecf20Sopenharmony_ci *val = buf[0]; 3298c2ecf20Sopenharmony_ciout: 3308c2ecf20Sopenharmony_ci kfree(buf); 3318c2ecf20Sopenharmony_ci return ret; 3328c2ecf20Sopenharmony_ci} 3338c2ecf20Sopenharmony_ci 3348c2ecf20Sopenharmony_cistatic void mos7840_dump_serial_port(struct usb_serial_port *port, 3358c2ecf20Sopenharmony_ci struct moschip_port *mos7840_port) 3368c2ecf20Sopenharmony_ci{ 3378c2ecf20Sopenharmony_ci 3388c2ecf20Sopenharmony_ci dev_dbg(&port->dev, "SpRegOffset is %2x\n", mos7840_port->SpRegOffset); 3398c2ecf20Sopenharmony_ci dev_dbg(&port->dev, "ControlRegOffset is %2x\n", mos7840_port->ControlRegOffset); 3408c2ecf20Sopenharmony_ci dev_dbg(&port->dev, "DCRRegOffset is %2x\n", mos7840_port->DcrRegOffset); 3418c2ecf20Sopenharmony_ci 3428c2ecf20Sopenharmony_ci} 3438c2ecf20Sopenharmony_ci 3448c2ecf20Sopenharmony_ci/************************************************************************/ 3458c2ecf20Sopenharmony_ci/************************************************************************/ 3468c2ecf20Sopenharmony_ci/* U S B C A L L B A C K F U N C T I O N S */ 3478c2ecf20Sopenharmony_ci/* U S B C A L L B A C K F U N C T I O N S */ 3488c2ecf20Sopenharmony_ci/************************************************************************/ 3498c2ecf20Sopenharmony_ci/************************************************************************/ 3508c2ecf20Sopenharmony_ci 3518c2ecf20Sopenharmony_cistatic void mos7840_set_led_callback(struct urb *urb) 3528c2ecf20Sopenharmony_ci{ 3538c2ecf20Sopenharmony_ci switch (urb->status) { 3548c2ecf20Sopenharmony_ci case 0: 3558c2ecf20Sopenharmony_ci /* Success */ 3568c2ecf20Sopenharmony_ci break; 3578c2ecf20Sopenharmony_ci case -ECONNRESET: 3588c2ecf20Sopenharmony_ci case -ENOENT: 3598c2ecf20Sopenharmony_ci case -ESHUTDOWN: 3608c2ecf20Sopenharmony_ci /* This urb is terminated, clean up */ 3618c2ecf20Sopenharmony_ci dev_dbg(&urb->dev->dev, "%s - urb shutting down: %d\n", 3628c2ecf20Sopenharmony_ci __func__, urb->status); 3638c2ecf20Sopenharmony_ci break; 3648c2ecf20Sopenharmony_ci default: 3658c2ecf20Sopenharmony_ci dev_dbg(&urb->dev->dev, "%s - nonzero urb status: %d\n", 3668c2ecf20Sopenharmony_ci __func__, urb->status); 3678c2ecf20Sopenharmony_ci } 3688c2ecf20Sopenharmony_ci} 3698c2ecf20Sopenharmony_ci 3708c2ecf20Sopenharmony_cistatic void mos7840_set_led_async(struct moschip_port *mcs, __u16 wval, 3718c2ecf20Sopenharmony_ci __u16 reg) 3728c2ecf20Sopenharmony_ci{ 3738c2ecf20Sopenharmony_ci struct usb_device *dev = mcs->port->serial->dev; 3748c2ecf20Sopenharmony_ci struct usb_ctrlrequest *dr = mcs->led_dr; 3758c2ecf20Sopenharmony_ci 3768c2ecf20Sopenharmony_ci dr->bRequestType = MCS_WR_RTYPE; 3778c2ecf20Sopenharmony_ci dr->bRequest = MCS_WRREQ; 3788c2ecf20Sopenharmony_ci dr->wValue = cpu_to_le16(wval); 3798c2ecf20Sopenharmony_ci dr->wIndex = cpu_to_le16(reg); 3808c2ecf20Sopenharmony_ci dr->wLength = cpu_to_le16(0); 3818c2ecf20Sopenharmony_ci 3828c2ecf20Sopenharmony_ci usb_fill_control_urb(mcs->led_urb, dev, usb_sndctrlpipe(dev, 0), 3838c2ecf20Sopenharmony_ci (unsigned char *)dr, NULL, 0, mos7840_set_led_callback, NULL); 3848c2ecf20Sopenharmony_ci 3858c2ecf20Sopenharmony_ci usb_submit_urb(mcs->led_urb, GFP_ATOMIC); 3868c2ecf20Sopenharmony_ci} 3878c2ecf20Sopenharmony_ci 3888c2ecf20Sopenharmony_cistatic void mos7840_set_led_sync(struct usb_serial_port *port, __u16 reg, 3898c2ecf20Sopenharmony_ci __u16 val) 3908c2ecf20Sopenharmony_ci{ 3918c2ecf20Sopenharmony_ci struct usb_device *dev = port->serial->dev; 3928c2ecf20Sopenharmony_ci 3938c2ecf20Sopenharmony_ci usb_control_msg(dev, usb_sndctrlpipe(dev, 0), MCS_WRREQ, MCS_WR_RTYPE, 3948c2ecf20Sopenharmony_ci val, reg, NULL, 0, MOS_WDR_TIMEOUT); 3958c2ecf20Sopenharmony_ci} 3968c2ecf20Sopenharmony_ci 3978c2ecf20Sopenharmony_cistatic void mos7840_led_off(struct timer_list *t) 3988c2ecf20Sopenharmony_ci{ 3998c2ecf20Sopenharmony_ci struct moschip_port *mcs = from_timer(mcs, t, led_timer1); 4008c2ecf20Sopenharmony_ci 4018c2ecf20Sopenharmony_ci /* Turn off LED */ 4028c2ecf20Sopenharmony_ci mos7840_set_led_async(mcs, 0x0300, MODEM_CONTROL_REGISTER); 4038c2ecf20Sopenharmony_ci mod_timer(&mcs->led_timer2, 4048c2ecf20Sopenharmony_ci jiffies + msecs_to_jiffies(LED_OFF_MS)); 4058c2ecf20Sopenharmony_ci} 4068c2ecf20Sopenharmony_ci 4078c2ecf20Sopenharmony_cistatic void mos7840_led_flag_off(struct timer_list *t) 4088c2ecf20Sopenharmony_ci{ 4098c2ecf20Sopenharmony_ci struct moschip_port *mcs = from_timer(mcs, t, led_timer2); 4108c2ecf20Sopenharmony_ci 4118c2ecf20Sopenharmony_ci clear_bit_unlock(MOS7840_FLAG_LED_BUSY, &mcs->flags); 4128c2ecf20Sopenharmony_ci} 4138c2ecf20Sopenharmony_ci 4148c2ecf20Sopenharmony_cistatic void mos7840_led_activity(struct usb_serial_port *port) 4158c2ecf20Sopenharmony_ci{ 4168c2ecf20Sopenharmony_ci struct moschip_port *mos7840_port = usb_get_serial_port_data(port); 4178c2ecf20Sopenharmony_ci 4188c2ecf20Sopenharmony_ci if (test_and_set_bit_lock(MOS7840_FLAG_LED_BUSY, &mos7840_port->flags)) 4198c2ecf20Sopenharmony_ci return; 4208c2ecf20Sopenharmony_ci 4218c2ecf20Sopenharmony_ci mos7840_set_led_async(mos7840_port, 0x0301, MODEM_CONTROL_REGISTER); 4228c2ecf20Sopenharmony_ci mod_timer(&mos7840_port->led_timer1, 4238c2ecf20Sopenharmony_ci jiffies + msecs_to_jiffies(LED_ON_MS)); 4248c2ecf20Sopenharmony_ci} 4258c2ecf20Sopenharmony_ci 4268c2ecf20Sopenharmony_ci/***************************************************************************** 4278c2ecf20Sopenharmony_ci * mos7840_bulk_in_callback 4288c2ecf20Sopenharmony_ci * this is the callback function for when we have received data on the 4298c2ecf20Sopenharmony_ci * bulk in endpoint. 4308c2ecf20Sopenharmony_ci *****************************************************************************/ 4318c2ecf20Sopenharmony_ci 4328c2ecf20Sopenharmony_cistatic void mos7840_bulk_in_callback(struct urb *urb) 4338c2ecf20Sopenharmony_ci{ 4348c2ecf20Sopenharmony_ci struct moschip_port *mos7840_port = urb->context; 4358c2ecf20Sopenharmony_ci struct usb_serial_port *port = mos7840_port->port; 4368c2ecf20Sopenharmony_ci int retval; 4378c2ecf20Sopenharmony_ci unsigned char *data; 4388c2ecf20Sopenharmony_ci int status = urb->status; 4398c2ecf20Sopenharmony_ci 4408c2ecf20Sopenharmony_ci if (status) { 4418c2ecf20Sopenharmony_ci dev_dbg(&urb->dev->dev, "nonzero read bulk status received: %d\n", status); 4428c2ecf20Sopenharmony_ci mos7840_port->read_urb_busy = false; 4438c2ecf20Sopenharmony_ci return; 4448c2ecf20Sopenharmony_ci } 4458c2ecf20Sopenharmony_ci 4468c2ecf20Sopenharmony_ci data = urb->transfer_buffer; 4478c2ecf20Sopenharmony_ci usb_serial_debug_data(&port->dev, __func__, urb->actual_length, data); 4488c2ecf20Sopenharmony_ci 4498c2ecf20Sopenharmony_ci if (urb->actual_length) { 4508c2ecf20Sopenharmony_ci struct tty_port *tport = &mos7840_port->port->port; 4518c2ecf20Sopenharmony_ci tty_insert_flip_string(tport, data, urb->actual_length); 4528c2ecf20Sopenharmony_ci tty_flip_buffer_push(tport); 4538c2ecf20Sopenharmony_ci port->icount.rx += urb->actual_length; 4548c2ecf20Sopenharmony_ci dev_dbg(&port->dev, "icount.rx is %d:\n", port->icount.rx); 4558c2ecf20Sopenharmony_ci } 4568c2ecf20Sopenharmony_ci 4578c2ecf20Sopenharmony_ci if (mos7840_port->has_led) 4588c2ecf20Sopenharmony_ci mos7840_led_activity(port); 4598c2ecf20Sopenharmony_ci 4608c2ecf20Sopenharmony_ci mos7840_port->read_urb_busy = true; 4618c2ecf20Sopenharmony_ci retval = usb_submit_urb(mos7840_port->read_urb, GFP_ATOMIC); 4628c2ecf20Sopenharmony_ci 4638c2ecf20Sopenharmony_ci if (retval) { 4648c2ecf20Sopenharmony_ci dev_dbg(&port->dev, "usb_submit_urb(read bulk) failed, retval = %d\n", retval); 4658c2ecf20Sopenharmony_ci mos7840_port->read_urb_busy = false; 4668c2ecf20Sopenharmony_ci } 4678c2ecf20Sopenharmony_ci} 4688c2ecf20Sopenharmony_ci 4698c2ecf20Sopenharmony_ci/***************************************************************************** 4708c2ecf20Sopenharmony_ci * mos7840_bulk_out_data_callback 4718c2ecf20Sopenharmony_ci * this is the callback function for when we have finished sending 4728c2ecf20Sopenharmony_ci * serial data on the bulk out endpoint. 4738c2ecf20Sopenharmony_ci *****************************************************************************/ 4748c2ecf20Sopenharmony_ci 4758c2ecf20Sopenharmony_cistatic void mos7840_bulk_out_data_callback(struct urb *urb) 4768c2ecf20Sopenharmony_ci{ 4778c2ecf20Sopenharmony_ci struct moschip_port *mos7840_port = urb->context; 4788c2ecf20Sopenharmony_ci struct usb_serial_port *port = mos7840_port->port; 4798c2ecf20Sopenharmony_ci int status = urb->status; 4808c2ecf20Sopenharmony_ci unsigned long flags; 4818c2ecf20Sopenharmony_ci int i; 4828c2ecf20Sopenharmony_ci 4838c2ecf20Sopenharmony_ci spin_lock_irqsave(&mos7840_port->pool_lock, flags); 4848c2ecf20Sopenharmony_ci for (i = 0; i < NUM_URBS; i++) { 4858c2ecf20Sopenharmony_ci if (urb == mos7840_port->write_urb_pool[i]) { 4868c2ecf20Sopenharmony_ci mos7840_port->busy[i] = 0; 4878c2ecf20Sopenharmony_ci break; 4888c2ecf20Sopenharmony_ci } 4898c2ecf20Sopenharmony_ci } 4908c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&mos7840_port->pool_lock, flags); 4918c2ecf20Sopenharmony_ci 4928c2ecf20Sopenharmony_ci if (status) { 4938c2ecf20Sopenharmony_ci dev_dbg(&port->dev, "nonzero write bulk status received:%d\n", status); 4948c2ecf20Sopenharmony_ci return; 4958c2ecf20Sopenharmony_ci } 4968c2ecf20Sopenharmony_ci 4978c2ecf20Sopenharmony_ci tty_port_tty_wakeup(&port->port); 4988c2ecf20Sopenharmony_ci 4998c2ecf20Sopenharmony_ci} 5008c2ecf20Sopenharmony_ci 5018c2ecf20Sopenharmony_ci/************************************************************************/ 5028c2ecf20Sopenharmony_ci/* D R I V E R T T Y I N T E R F A C E F U N C T I O N S */ 5038c2ecf20Sopenharmony_ci/************************************************************************/ 5048c2ecf20Sopenharmony_ci 5058c2ecf20Sopenharmony_ci/***************************************************************************** 5068c2ecf20Sopenharmony_ci * mos7840_open 5078c2ecf20Sopenharmony_ci * this function is called by the tty driver when a port is opened 5088c2ecf20Sopenharmony_ci * If successful, we return 0 5098c2ecf20Sopenharmony_ci * Otherwise we return a negative error number. 5108c2ecf20Sopenharmony_ci *****************************************************************************/ 5118c2ecf20Sopenharmony_ci 5128c2ecf20Sopenharmony_cistatic int mos7840_open(struct tty_struct *tty, struct usb_serial_port *port) 5138c2ecf20Sopenharmony_ci{ 5148c2ecf20Sopenharmony_ci struct moschip_port *mos7840_port = usb_get_serial_port_data(port); 5158c2ecf20Sopenharmony_ci struct usb_serial *serial = port->serial; 5168c2ecf20Sopenharmony_ci int response; 5178c2ecf20Sopenharmony_ci int j; 5188c2ecf20Sopenharmony_ci struct urb *urb; 5198c2ecf20Sopenharmony_ci __u16 Data; 5208c2ecf20Sopenharmony_ci int status; 5218c2ecf20Sopenharmony_ci 5228c2ecf20Sopenharmony_ci usb_clear_halt(serial->dev, port->write_urb->pipe); 5238c2ecf20Sopenharmony_ci usb_clear_halt(serial->dev, port->read_urb->pipe); 5248c2ecf20Sopenharmony_ci 5258c2ecf20Sopenharmony_ci /* Initialising the write urb pool */ 5268c2ecf20Sopenharmony_ci for (j = 0; j < NUM_URBS; ++j) { 5278c2ecf20Sopenharmony_ci urb = usb_alloc_urb(0, GFP_KERNEL); 5288c2ecf20Sopenharmony_ci mos7840_port->write_urb_pool[j] = urb; 5298c2ecf20Sopenharmony_ci if (!urb) 5308c2ecf20Sopenharmony_ci continue; 5318c2ecf20Sopenharmony_ci 5328c2ecf20Sopenharmony_ci urb->transfer_buffer = kmalloc(URB_TRANSFER_BUFFER_SIZE, 5338c2ecf20Sopenharmony_ci GFP_KERNEL); 5348c2ecf20Sopenharmony_ci if (!urb->transfer_buffer) { 5358c2ecf20Sopenharmony_ci usb_free_urb(urb); 5368c2ecf20Sopenharmony_ci mos7840_port->write_urb_pool[j] = NULL; 5378c2ecf20Sopenharmony_ci continue; 5388c2ecf20Sopenharmony_ci } 5398c2ecf20Sopenharmony_ci } 5408c2ecf20Sopenharmony_ci 5418c2ecf20Sopenharmony_ci/***************************************************************************** 5428c2ecf20Sopenharmony_ci * Initialize MCS7840 -- Write Init values to corresponding Registers 5438c2ecf20Sopenharmony_ci * 5448c2ecf20Sopenharmony_ci * Register Index 5458c2ecf20Sopenharmony_ci * 1 : IER 5468c2ecf20Sopenharmony_ci * 2 : FCR 5478c2ecf20Sopenharmony_ci * 3 : LCR 5488c2ecf20Sopenharmony_ci * 4 : MCR 5498c2ecf20Sopenharmony_ci * 5508c2ecf20Sopenharmony_ci * 0x08 : SP1/2 Control Reg 5518c2ecf20Sopenharmony_ci *****************************************************************************/ 5528c2ecf20Sopenharmony_ci 5538c2ecf20Sopenharmony_ci /* NEED to check the following Block */ 5548c2ecf20Sopenharmony_ci 5558c2ecf20Sopenharmony_ci Data = 0x0; 5568c2ecf20Sopenharmony_ci status = mos7840_get_reg_sync(port, mos7840_port->SpRegOffset, &Data); 5578c2ecf20Sopenharmony_ci if (status < 0) { 5588c2ecf20Sopenharmony_ci dev_dbg(&port->dev, "Reading Spreg failed\n"); 5598c2ecf20Sopenharmony_ci goto err; 5608c2ecf20Sopenharmony_ci } 5618c2ecf20Sopenharmony_ci Data |= 0x80; 5628c2ecf20Sopenharmony_ci status = mos7840_set_reg_sync(port, mos7840_port->SpRegOffset, Data); 5638c2ecf20Sopenharmony_ci if (status < 0) { 5648c2ecf20Sopenharmony_ci dev_dbg(&port->dev, "writing Spreg failed\n"); 5658c2ecf20Sopenharmony_ci goto err; 5668c2ecf20Sopenharmony_ci } 5678c2ecf20Sopenharmony_ci 5688c2ecf20Sopenharmony_ci Data &= ~0x80; 5698c2ecf20Sopenharmony_ci status = mos7840_set_reg_sync(port, mos7840_port->SpRegOffset, Data); 5708c2ecf20Sopenharmony_ci if (status < 0) { 5718c2ecf20Sopenharmony_ci dev_dbg(&port->dev, "writing Spreg failed\n"); 5728c2ecf20Sopenharmony_ci goto err; 5738c2ecf20Sopenharmony_ci } 5748c2ecf20Sopenharmony_ci /* End of block to be checked */ 5758c2ecf20Sopenharmony_ci 5768c2ecf20Sopenharmony_ci Data = 0x0; 5778c2ecf20Sopenharmony_ci status = mos7840_get_reg_sync(port, mos7840_port->ControlRegOffset, 5788c2ecf20Sopenharmony_ci &Data); 5798c2ecf20Sopenharmony_ci if (status < 0) { 5808c2ecf20Sopenharmony_ci dev_dbg(&port->dev, "Reading Controlreg failed\n"); 5818c2ecf20Sopenharmony_ci goto err; 5828c2ecf20Sopenharmony_ci } 5838c2ecf20Sopenharmony_ci Data |= 0x08; /* Driver done bit */ 5848c2ecf20Sopenharmony_ci Data |= 0x20; /* rx_disable */ 5858c2ecf20Sopenharmony_ci status = mos7840_set_reg_sync(port, 5868c2ecf20Sopenharmony_ci mos7840_port->ControlRegOffset, Data); 5878c2ecf20Sopenharmony_ci if (status < 0) { 5888c2ecf20Sopenharmony_ci dev_dbg(&port->dev, "writing Controlreg failed\n"); 5898c2ecf20Sopenharmony_ci goto err; 5908c2ecf20Sopenharmony_ci } 5918c2ecf20Sopenharmony_ci /* do register settings here */ 5928c2ecf20Sopenharmony_ci /* Set all regs to the device default values. */ 5938c2ecf20Sopenharmony_ci /*********************************** 5948c2ecf20Sopenharmony_ci * First Disable all interrupts. 5958c2ecf20Sopenharmony_ci ***********************************/ 5968c2ecf20Sopenharmony_ci Data = 0x00; 5978c2ecf20Sopenharmony_ci status = mos7840_set_uart_reg(port, INTERRUPT_ENABLE_REGISTER, Data); 5988c2ecf20Sopenharmony_ci if (status < 0) { 5998c2ecf20Sopenharmony_ci dev_dbg(&port->dev, "disabling interrupts failed\n"); 6008c2ecf20Sopenharmony_ci goto err; 6018c2ecf20Sopenharmony_ci } 6028c2ecf20Sopenharmony_ci /* Set FIFO_CONTROL_REGISTER to the default value */ 6038c2ecf20Sopenharmony_ci Data = 0x00; 6048c2ecf20Sopenharmony_ci status = mos7840_set_uart_reg(port, FIFO_CONTROL_REGISTER, Data); 6058c2ecf20Sopenharmony_ci if (status < 0) { 6068c2ecf20Sopenharmony_ci dev_dbg(&port->dev, "Writing FIFO_CONTROL_REGISTER failed\n"); 6078c2ecf20Sopenharmony_ci goto err; 6088c2ecf20Sopenharmony_ci } 6098c2ecf20Sopenharmony_ci 6108c2ecf20Sopenharmony_ci Data = 0xcf; 6118c2ecf20Sopenharmony_ci status = mos7840_set_uart_reg(port, FIFO_CONTROL_REGISTER, Data); 6128c2ecf20Sopenharmony_ci if (status < 0) { 6138c2ecf20Sopenharmony_ci dev_dbg(&port->dev, "Writing FIFO_CONTROL_REGISTER failed\n"); 6148c2ecf20Sopenharmony_ci goto err; 6158c2ecf20Sopenharmony_ci } 6168c2ecf20Sopenharmony_ci 6178c2ecf20Sopenharmony_ci Data = 0x03; 6188c2ecf20Sopenharmony_ci status = mos7840_set_uart_reg(port, LINE_CONTROL_REGISTER, Data); 6198c2ecf20Sopenharmony_ci mos7840_port->shadowLCR = Data; 6208c2ecf20Sopenharmony_ci 6218c2ecf20Sopenharmony_ci Data = 0x0b; 6228c2ecf20Sopenharmony_ci status = mos7840_set_uart_reg(port, MODEM_CONTROL_REGISTER, Data); 6238c2ecf20Sopenharmony_ci mos7840_port->shadowMCR = Data; 6248c2ecf20Sopenharmony_ci 6258c2ecf20Sopenharmony_ci Data = 0x00; 6268c2ecf20Sopenharmony_ci status = mos7840_get_uart_reg(port, LINE_CONTROL_REGISTER, &Data); 6278c2ecf20Sopenharmony_ci mos7840_port->shadowLCR = Data; 6288c2ecf20Sopenharmony_ci 6298c2ecf20Sopenharmony_ci Data |= SERIAL_LCR_DLAB; /* data latch enable in LCR 0x80 */ 6308c2ecf20Sopenharmony_ci status = mos7840_set_uart_reg(port, LINE_CONTROL_REGISTER, Data); 6318c2ecf20Sopenharmony_ci 6328c2ecf20Sopenharmony_ci Data = 0x0c; 6338c2ecf20Sopenharmony_ci status = mos7840_set_uart_reg(port, DIVISOR_LATCH_LSB, Data); 6348c2ecf20Sopenharmony_ci 6358c2ecf20Sopenharmony_ci Data = 0x0; 6368c2ecf20Sopenharmony_ci status = mos7840_set_uart_reg(port, DIVISOR_LATCH_MSB, Data); 6378c2ecf20Sopenharmony_ci 6388c2ecf20Sopenharmony_ci Data = 0x00; 6398c2ecf20Sopenharmony_ci status = mos7840_get_uart_reg(port, LINE_CONTROL_REGISTER, &Data); 6408c2ecf20Sopenharmony_ci 6418c2ecf20Sopenharmony_ci Data = Data & ~SERIAL_LCR_DLAB; 6428c2ecf20Sopenharmony_ci status = mos7840_set_uart_reg(port, LINE_CONTROL_REGISTER, Data); 6438c2ecf20Sopenharmony_ci mos7840_port->shadowLCR = Data; 6448c2ecf20Sopenharmony_ci 6458c2ecf20Sopenharmony_ci /* clearing Bulkin and Bulkout Fifo */ 6468c2ecf20Sopenharmony_ci Data = 0x0; 6478c2ecf20Sopenharmony_ci status = mos7840_get_reg_sync(port, mos7840_port->SpRegOffset, &Data); 6488c2ecf20Sopenharmony_ci 6498c2ecf20Sopenharmony_ci Data = Data | 0x0c; 6508c2ecf20Sopenharmony_ci status = mos7840_set_reg_sync(port, mos7840_port->SpRegOffset, Data); 6518c2ecf20Sopenharmony_ci 6528c2ecf20Sopenharmony_ci Data = Data & ~0x0c; 6538c2ecf20Sopenharmony_ci status = mos7840_set_reg_sync(port, mos7840_port->SpRegOffset, Data); 6548c2ecf20Sopenharmony_ci /* Finally enable all interrupts */ 6558c2ecf20Sopenharmony_ci Data = 0x0c; 6568c2ecf20Sopenharmony_ci status = mos7840_set_uart_reg(port, INTERRUPT_ENABLE_REGISTER, Data); 6578c2ecf20Sopenharmony_ci 6588c2ecf20Sopenharmony_ci /* clearing rx_disable */ 6598c2ecf20Sopenharmony_ci Data = 0x0; 6608c2ecf20Sopenharmony_ci status = mos7840_get_reg_sync(port, mos7840_port->ControlRegOffset, 6618c2ecf20Sopenharmony_ci &Data); 6628c2ecf20Sopenharmony_ci Data = Data & ~0x20; 6638c2ecf20Sopenharmony_ci status = mos7840_set_reg_sync(port, mos7840_port->ControlRegOffset, 6648c2ecf20Sopenharmony_ci Data); 6658c2ecf20Sopenharmony_ci 6668c2ecf20Sopenharmony_ci /* rx_negate */ 6678c2ecf20Sopenharmony_ci Data = 0x0; 6688c2ecf20Sopenharmony_ci status = mos7840_get_reg_sync(port, mos7840_port->ControlRegOffset, 6698c2ecf20Sopenharmony_ci &Data); 6708c2ecf20Sopenharmony_ci Data = Data | 0x10; 6718c2ecf20Sopenharmony_ci status = mos7840_set_reg_sync(port, mos7840_port->ControlRegOffset, 6728c2ecf20Sopenharmony_ci Data); 6738c2ecf20Sopenharmony_ci 6748c2ecf20Sopenharmony_ci dev_dbg(&port->dev, "port number is %d\n", port->port_number); 6758c2ecf20Sopenharmony_ci dev_dbg(&port->dev, "minor number is %d\n", port->minor); 6768c2ecf20Sopenharmony_ci dev_dbg(&port->dev, "Bulkin endpoint is %d\n", port->bulk_in_endpointAddress); 6778c2ecf20Sopenharmony_ci dev_dbg(&port->dev, "BulkOut endpoint is %d\n", port->bulk_out_endpointAddress); 6788c2ecf20Sopenharmony_ci dev_dbg(&port->dev, "Interrupt endpoint is %d\n", port->interrupt_in_endpointAddress); 6798c2ecf20Sopenharmony_ci dev_dbg(&port->dev, "port's number in the device is %d\n", mos7840_port->port_num); 6808c2ecf20Sopenharmony_ci mos7840_port->read_urb = port->read_urb; 6818c2ecf20Sopenharmony_ci 6828c2ecf20Sopenharmony_ci /* set up our bulk in urb */ 6838c2ecf20Sopenharmony_ci if ((serial->num_ports == 2) && (((__u16)port->port_number % 2) != 0)) { 6848c2ecf20Sopenharmony_ci usb_fill_bulk_urb(mos7840_port->read_urb, 6858c2ecf20Sopenharmony_ci serial->dev, 6868c2ecf20Sopenharmony_ci usb_rcvbulkpipe(serial->dev, 6878c2ecf20Sopenharmony_ci (port->bulk_in_endpointAddress) + 2), 6888c2ecf20Sopenharmony_ci port->bulk_in_buffer, 6898c2ecf20Sopenharmony_ci mos7840_port->read_urb->transfer_buffer_length, 6908c2ecf20Sopenharmony_ci mos7840_bulk_in_callback, mos7840_port); 6918c2ecf20Sopenharmony_ci } else { 6928c2ecf20Sopenharmony_ci usb_fill_bulk_urb(mos7840_port->read_urb, 6938c2ecf20Sopenharmony_ci serial->dev, 6948c2ecf20Sopenharmony_ci usb_rcvbulkpipe(serial->dev, 6958c2ecf20Sopenharmony_ci port->bulk_in_endpointAddress), 6968c2ecf20Sopenharmony_ci port->bulk_in_buffer, 6978c2ecf20Sopenharmony_ci mos7840_port->read_urb->transfer_buffer_length, 6988c2ecf20Sopenharmony_ci mos7840_bulk_in_callback, mos7840_port); 6998c2ecf20Sopenharmony_ci } 7008c2ecf20Sopenharmony_ci 7018c2ecf20Sopenharmony_ci dev_dbg(&port->dev, "%s: bulkin endpoint is %d\n", __func__, port->bulk_in_endpointAddress); 7028c2ecf20Sopenharmony_ci mos7840_port->read_urb_busy = true; 7038c2ecf20Sopenharmony_ci response = usb_submit_urb(mos7840_port->read_urb, GFP_KERNEL); 7048c2ecf20Sopenharmony_ci if (response) { 7058c2ecf20Sopenharmony_ci dev_err(&port->dev, "%s - Error %d submitting control urb\n", 7068c2ecf20Sopenharmony_ci __func__, response); 7078c2ecf20Sopenharmony_ci mos7840_port->read_urb_busy = false; 7088c2ecf20Sopenharmony_ci } 7098c2ecf20Sopenharmony_ci 7108c2ecf20Sopenharmony_ci /* initialize our port settings */ 7118c2ecf20Sopenharmony_ci /* Must set to enable ints! */ 7128c2ecf20Sopenharmony_ci mos7840_port->shadowMCR = MCR_MASTER_IE; 7138c2ecf20Sopenharmony_ci 7148c2ecf20Sopenharmony_ci return 0; 7158c2ecf20Sopenharmony_cierr: 7168c2ecf20Sopenharmony_ci for (j = 0; j < NUM_URBS; ++j) { 7178c2ecf20Sopenharmony_ci urb = mos7840_port->write_urb_pool[j]; 7188c2ecf20Sopenharmony_ci if (!urb) 7198c2ecf20Sopenharmony_ci continue; 7208c2ecf20Sopenharmony_ci kfree(urb->transfer_buffer); 7218c2ecf20Sopenharmony_ci usb_free_urb(urb); 7228c2ecf20Sopenharmony_ci } 7238c2ecf20Sopenharmony_ci return status; 7248c2ecf20Sopenharmony_ci} 7258c2ecf20Sopenharmony_ci 7268c2ecf20Sopenharmony_ci/***************************************************************************** 7278c2ecf20Sopenharmony_ci * mos7840_chars_in_buffer 7288c2ecf20Sopenharmony_ci * this function is called by the tty driver when it wants to know how many 7298c2ecf20Sopenharmony_ci * bytes of data we currently have outstanding in the port (data that has 7308c2ecf20Sopenharmony_ci * been written, but hasn't made it out the port yet) 7318c2ecf20Sopenharmony_ci * If successful, we return the number of bytes left to be written in the 7328c2ecf20Sopenharmony_ci * system, 7338c2ecf20Sopenharmony_ci * Otherwise we return zero. 7348c2ecf20Sopenharmony_ci *****************************************************************************/ 7358c2ecf20Sopenharmony_ci 7368c2ecf20Sopenharmony_cistatic int mos7840_chars_in_buffer(struct tty_struct *tty) 7378c2ecf20Sopenharmony_ci{ 7388c2ecf20Sopenharmony_ci struct usb_serial_port *port = tty->driver_data; 7398c2ecf20Sopenharmony_ci struct moschip_port *mos7840_port = usb_get_serial_port_data(port); 7408c2ecf20Sopenharmony_ci int i; 7418c2ecf20Sopenharmony_ci int chars = 0; 7428c2ecf20Sopenharmony_ci unsigned long flags; 7438c2ecf20Sopenharmony_ci 7448c2ecf20Sopenharmony_ci spin_lock_irqsave(&mos7840_port->pool_lock, flags); 7458c2ecf20Sopenharmony_ci for (i = 0; i < NUM_URBS; ++i) { 7468c2ecf20Sopenharmony_ci if (mos7840_port->busy[i]) { 7478c2ecf20Sopenharmony_ci struct urb *urb = mos7840_port->write_urb_pool[i]; 7488c2ecf20Sopenharmony_ci chars += urb->transfer_buffer_length; 7498c2ecf20Sopenharmony_ci } 7508c2ecf20Sopenharmony_ci } 7518c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&mos7840_port->pool_lock, flags); 7528c2ecf20Sopenharmony_ci dev_dbg(&port->dev, "%s - returns %d\n", __func__, chars); 7538c2ecf20Sopenharmony_ci return chars; 7548c2ecf20Sopenharmony_ci 7558c2ecf20Sopenharmony_ci} 7568c2ecf20Sopenharmony_ci 7578c2ecf20Sopenharmony_ci/***************************************************************************** 7588c2ecf20Sopenharmony_ci * mos7840_close 7598c2ecf20Sopenharmony_ci * this function is called by the tty driver when a port is closed 7608c2ecf20Sopenharmony_ci *****************************************************************************/ 7618c2ecf20Sopenharmony_ci 7628c2ecf20Sopenharmony_cistatic void mos7840_close(struct usb_serial_port *port) 7638c2ecf20Sopenharmony_ci{ 7648c2ecf20Sopenharmony_ci struct moschip_port *mos7840_port = usb_get_serial_port_data(port); 7658c2ecf20Sopenharmony_ci int j; 7668c2ecf20Sopenharmony_ci __u16 Data; 7678c2ecf20Sopenharmony_ci 7688c2ecf20Sopenharmony_ci for (j = 0; j < NUM_URBS; ++j) 7698c2ecf20Sopenharmony_ci usb_kill_urb(mos7840_port->write_urb_pool[j]); 7708c2ecf20Sopenharmony_ci 7718c2ecf20Sopenharmony_ci /* Freeing Write URBs */ 7728c2ecf20Sopenharmony_ci for (j = 0; j < NUM_URBS; ++j) { 7738c2ecf20Sopenharmony_ci if (mos7840_port->write_urb_pool[j]) { 7748c2ecf20Sopenharmony_ci kfree(mos7840_port->write_urb_pool[j]->transfer_buffer); 7758c2ecf20Sopenharmony_ci usb_free_urb(mos7840_port->write_urb_pool[j]); 7768c2ecf20Sopenharmony_ci } 7778c2ecf20Sopenharmony_ci } 7788c2ecf20Sopenharmony_ci 7798c2ecf20Sopenharmony_ci usb_kill_urb(mos7840_port->read_urb); 7808c2ecf20Sopenharmony_ci mos7840_port->read_urb_busy = false; 7818c2ecf20Sopenharmony_ci 7828c2ecf20Sopenharmony_ci Data = 0x0; 7838c2ecf20Sopenharmony_ci mos7840_set_uart_reg(port, MODEM_CONTROL_REGISTER, Data); 7848c2ecf20Sopenharmony_ci 7858c2ecf20Sopenharmony_ci Data = 0x00; 7868c2ecf20Sopenharmony_ci mos7840_set_uart_reg(port, INTERRUPT_ENABLE_REGISTER, Data); 7878c2ecf20Sopenharmony_ci} 7888c2ecf20Sopenharmony_ci 7898c2ecf20Sopenharmony_ci/***************************************************************************** 7908c2ecf20Sopenharmony_ci * mos7840_break 7918c2ecf20Sopenharmony_ci * this function sends a break to the port 7928c2ecf20Sopenharmony_ci *****************************************************************************/ 7938c2ecf20Sopenharmony_cistatic void mos7840_break(struct tty_struct *tty, int break_state) 7948c2ecf20Sopenharmony_ci{ 7958c2ecf20Sopenharmony_ci struct usb_serial_port *port = tty->driver_data; 7968c2ecf20Sopenharmony_ci struct moschip_port *mos7840_port = usb_get_serial_port_data(port); 7978c2ecf20Sopenharmony_ci unsigned char data; 7988c2ecf20Sopenharmony_ci 7998c2ecf20Sopenharmony_ci if (break_state == -1) 8008c2ecf20Sopenharmony_ci data = mos7840_port->shadowLCR | LCR_SET_BREAK; 8018c2ecf20Sopenharmony_ci else 8028c2ecf20Sopenharmony_ci data = mos7840_port->shadowLCR & ~LCR_SET_BREAK; 8038c2ecf20Sopenharmony_ci 8048c2ecf20Sopenharmony_ci /* FIXME: no locking on shadowLCR anywhere in driver */ 8058c2ecf20Sopenharmony_ci mos7840_port->shadowLCR = data; 8068c2ecf20Sopenharmony_ci dev_dbg(&port->dev, "%s mos7840_port->shadowLCR is %x\n", __func__, mos7840_port->shadowLCR); 8078c2ecf20Sopenharmony_ci mos7840_set_uart_reg(port, LINE_CONTROL_REGISTER, 8088c2ecf20Sopenharmony_ci mos7840_port->shadowLCR); 8098c2ecf20Sopenharmony_ci} 8108c2ecf20Sopenharmony_ci 8118c2ecf20Sopenharmony_ci/***************************************************************************** 8128c2ecf20Sopenharmony_ci * mos7840_write_room 8138c2ecf20Sopenharmony_ci * this function is called by the tty driver when it wants to know how many 8148c2ecf20Sopenharmony_ci * bytes of data we can accept for a specific port. 8158c2ecf20Sopenharmony_ci * If successful, we return the amount of room that we have for this port 8168c2ecf20Sopenharmony_ci * Otherwise we return a negative error number. 8178c2ecf20Sopenharmony_ci *****************************************************************************/ 8188c2ecf20Sopenharmony_ci 8198c2ecf20Sopenharmony_cistatic int mos7840_write_room(struct tty_struct *tty) 8208c2ecf20Sopenharmony_ci{ 8218c2ecf20Sopenharmony_ci struct usb_serial_port *port = tty->driver_data; 8228c2ecf20Sopenharmony_ci struct moschip_port *mos7840_port = usb_get_serial_port_data(port); 8238c2ecf20Sopenharmony_ci int i; 8248c2ecf20Sopenharmony_ci int room = 0; 8258c2ecf20Sopenharmony_ci unsigned long flags; 8268c2ecf20Sopenharmony_ci 8278c2ecf20Sopenharmony_ci spin_lock_irqsave(&mos7840_port->pool_lock, flags); 8288c2ecf20Sopenharmony_ci for (i = 0; i < NUM_URBS; ++i) { 8298c2ecf20Sopenharmony_ci if (!mos7840_port->busy[i]) 8308c2ecf20Sopenharmony_ci room += URB_TRANSFER_BUFFER_SIZE; 8318c2ecf20Sopenharmony_ci } 8328c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&mos7840_port->pool_lock, flags); 8338c2ecf20Sopenharmony_ci 8348c2ecf20Sopenharmony_ci room = (room == 0) ? 0 : room - URB_TRANSFER_BUFFER_SIZE + 1; 8358c2ecf20Sopenharmony_ci dev_dbg(&mos7840_port->port->dev, "%s - returns %d\n", __func__, room); 8368c2ecf20Sopenharmony_ci return room; 8378c2ecf20Sopenharmony_ci 8388c2ecf20Sopenharmony_ci} 8398c2ecf20Sopenharmony_ci 8408c2ecf20Sopenharmony_ci/***************************************************************************** 8418c2ecf20Sopenharmony_ci * mos7840_write 8428c2ecf20Sopenharmony_ci * this function is called by the tty driver when data should be written to 8438c2ecf20Sopenharmony_ci * the port. 8448c2ecf20Sopenharmony_ci * If successful, we return the number of bytes written, otherwise we 8458c2ecf20Sopenharmony_ci * return a negative error number. 8468c2ecf20Sopenharmony_ci *****************************************************************************/ 8478c2ecf20Sopenharmony_ci 8488c2ecf20Sopenharmony_cistatic int mos7840_write(struct tty_struct *tty, struct usb_serial_port *port, 8498c2ecf20Sopenharmony_ci const unsigned char *data, int count) 8508c2ecf20Sopenharmony_ci{ 8518c2ecf20Sopenharmony_ci struct moschip_port *mos7840_port = usb_get_serial_port_data(port); 8528c2ecf20Sopenharmony_ci struct usb_serial *serial = port->serial; 8538c2ecf20Sopenharmony_ci int status; 8548c2ecf20Sopenharmony_ci int i; 8558c2ecf20Sopenharmony_ci int bytes_sent = 0; 8568c2ecf20Sopenharmony_ci int transfer_size; 8578c2ecf20Sopenharmony_ci unsigned long flags; 8588c2ecf20Sopenharmony_ci struct urb *urb; 8598c2ecf20Sopenharmony_ci /* __u16 Data; */ 8608c2ecf20Sopenharmony_ci const unsigned char *current_position = data; 8618c2ecf20Sopenharmony_ci 8628c2ecf20Sopenharmony_ci /* try to find a free urb in the list */ 8638c2ecf20Sopenharmony_ci urb = NULL; 8648c2ecf20Sopenharmony_ci 8658c2ecf20Sopenharmony_ci spin_lock_irqsave(&mos7840_port->pool_lock, flags); 8668c2ecf20Sopenharmony_ci for (i = 0; i < NUM_URBS; ++i) { 8678c2ecf20Sopenharmony_ci if (!mos7840_port->busy[i]) { 8688c2ecf20Sopenharmony_ci mos7840_port->busy[i] = 1; 8698c2ecf20Sopenharmony_ci urb = mos7840_port->write_urb_pool[i]; 8708c2ecf20Sopenharmony_ci dev_dbg(&port->dev, "URB:%d\n", i); 8718c2ecf20Sopenharmony_ci break; 8728c2ecf20Sopenharmony_ci } 8738c2ecf20Sopenharmony_ci } 8748c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&mos7840_port->pool_lock, flags); 8758c2ecf20Sopenharmony_ci 8768c2ecf20Sopenharmony_ci if (urb == NULL) { 8778c2ecf20Sopenharmony_ci dev_dbg(&port->dev, "%s - no more free urbs\n", __func__); 8788c2ecf20Sopenharmony_ci goto exit; 8798c2ecf20Sopenharmony_ci } 8808c2ecf20Sopenharmony_ci 8818c2ecf20Sopenharmony_ci if (urb->transfer_buffer == NULL) { 8828c2ecf20Sopenharmony_ci urb->transfer_buffer = kmalloc(URB_TRANSFER_BUFFER_SIZE, 8838c2ecf20Sopenharmony_ci GFP_ATOMIC); 8848c2ecf20Sopenharmony_ci if (!urb->transfer_buffer) { 8858c2ecf20Sopenharmony_ci bytes_sent = -ENOMEM; 8868c2ecf20Sopenharmony_ci goto exit; 8878c2ecf20Sopenharmony_ci } 8888c2ecf20Sopenharmony_ci } 8898c2ecf20Sopenharmony_ci transfer_size = min(count, URB_TRANSFER_BUFFER_SIZE); 8908c2ecf20Sopenharmony_ci 8918c2ecf20Sopenharmony_ci memcpy(urb->transfer_buffer, current_position, transfer_size); 8928c2ecf20Sopenharmony_ci 8938c2ecf20Sopenharmony_ci /* fill urb with data and submit */ 8948c2ecf20Sopenharmony_ci if ((serial->num_ports == 2) && (((__u16)port->port_number % 2) != 0)) { 8958c2ecf20Sopenharmony_ci usb_fill_bulk_urb(urb, 8968c2ecf20Sopenharmony_ci serial->dev, 8978c2ecf20Sopenharmony_ci usb_sndbulkpipe(serial->dev, 8988c2ecf20Sopenharmony_ci (port->bulk_out_endpointAddress) + 2), 8998c2ecf20Sopenharmony_ci urb->transfer_buffer, 9008c2ecf20Sopenharmony_ci transfer_size, 9018c2ecf20Sopenharmony_ci mos7840_bulk_out_data_callback, mos7840_port); 9028c2ecf20Sopenharmony_ci } else { 9038c2ecf20Sopenharmony_ci usb_fill_bulk_urb(urb, 9048c2ecf20Sopenharmony_ci serial->dev, 9058c2ecf20Sopenharmony_ci usb_sndbulkpipe(serial->dev, 9068c2ecf20Sopenharmony_ci port->bulk_out_endpointAddress), 9078c2ecf20Sopenharmony_ci urb->transfer_buffer, 9088c2ecf20Sopenharmony_ci transfer_size, 9098c2ecf20Sopenharmony_ci mos7840_bulk_out_data_callback, mos7840_port); 9108c2ecf20Sopenharmony_ci } 9118c2ecf20Sopenharmony_ci 9128c2ecf20Sopenharmony_ci dev_dbg(&port->dev, "bulkout endpoint is %d\n", port->bulk_out_endpointAddress); 9138c2ecf20Sopenharmony_ci 9148c2ecf20Sopenharmony_ci if (mos7840_port->has_led) 9158c2ecf20Sopenharmony_ci mos7840_led_activity(port); 9168c2ecf20Sopenharmony_ci 9178c2ecf20Sopenharmony_ci /* send it down the pipe */ 9188c2ecf20Sopenharmony_ci status = usb_submit_urb(urb, GFP_ATOMIC); 9198c2ecf20Sopenharmony_ci 9208c2ecf20Sopenharmony_ci if (status) { 9218c2ecf20Sopenharmony_ci mos7840_port->busy[i] = 0; 9228c2ecf20Sopenharmony_ci dev_err_console(port, "%s - usb_submit_urb(write bulk) failed " 9238c2ecf20Sopenharmony_ci "with status = %d\n", __func__, status); 9248c2ecf20Sopenharmony_ci bytes_sent = status; 9258c2ecf20Sopenharmony_ci goto exit; 9268c2ecf20Sopenharmony_ci } 9278c2ecf20Sopenharmony_ci bytes_sent = transfer_size; 9288c2ecf20Sopenharmony_ci port->icount.tx += transfer_size; 9298c2ecf20Sopenharmony_ci dev_dbg(&port->dev, "icount.tx is %d:\n", port->icount.tx); 9308c2ecf20Sopenharmony_ciexit: 9318c2ecf20Sopenharmony_ci return bytes_sent; 9328c2ecf20Sopenharmony_ci 9338c2ecf20Sopenharmony_ci} 9348c2ecf20Sopenharmony_ci 9358c2ecf20Sopenharmony_ci/***************************************************************************** 9368c2ecf20Sopenharmony_ci * mos7840_throttle 9378c2ecf20Sopenharmony_ci * this function is called by the tty driver when it wants to stop the data 9388c2ecf20Sopenharmony_ci * being read from the port. 9398c2ecf20Sopenharmony_ci *****************************************************************************/ 9408c2ecf20Sopenharmony_ci 9418c2ecf20Sopenharmony_cistatic void mos7840_throttle(struct tty_struct *tty) 9428c2ecf20Sopenharmony_ci{ 9438c2ecf20Sopenharmony_ci struct usb_serial_port *port = tty->driver_data; 9448c2ecf20Sopenharmony_ci struct moschip_port *mos7840_port = usb_get_serial_port_data(port); 9458c2ecf20Sopenharmony_ci int status; 9468c2ecf20Sopenharmony_ci 9478c2ecf20Sopenharmony_ci /* if we are implementing XON/XOFF, send the stop character */ 9488c2ecf20Sopenharmony_ci if (I_IXOFF(tty)) { 9498c2ecf20Sopenharmony_ci unsigned char stop_char = STOP_CHAR(tty); 9508c2ecf20Sopenharmony_ci status = mos7840_write(tty, port, &stop_char, 1); 9518c2ecf20Sopenharmony_ci if (status <= 0) 9528c2ecf20Sopenharmony_ci return; 9538c2ecf20Sopenharmony_ci } 9548c2ecf20Sopenharmony_ci /* if we are implementing RTS/CTS, toggle that line */ 9558c2ecf20Sopenharmony_ci if (C_CRTSCTS(tty)) { 9568c2ecf20Sopenharmony_ci mos7840_port->shadowMCR &= ~MCR_RTS; 9578c2ecf20Sopenharmony_ci status = mos7840_set_uart_reg(port, MODEM_CONTROL_REGISTER, 9588c2ecf20Sopenharmony_ci mos7840_port->shadowMCR); 9598c2ecf20Sopenharmony_ci if (status < 0) 9608c2ecf20Sopenharmony_ci return; 9618c2ecf20Sopenharmony_ci } 9628c2ecf20Sopenharmony_ci} 9638c2ecf20Sopenharmony_ci 9648c2ecf20Sopenharmony_ci/***************************************************************************** 9658c2ecf20Sopenharmony_ci * mos7840_unthrottle 9668c2ecf20Sopenharmony_ci * this function is called by the tty driver when it wants to resume 9678c2ecf20Sopenharmony_ci * the data being read from the port (called after mos7840_throttle is 9688c2ecf20Sopenharmony_ci * called) 9698c2ecf20Sopenharmony_ci *****************************************************************************/ 9708c2ecf20Sopenharmony_cistatic void mos7840_unthrottle(struct tty_struct *tty) 9718c2ecf20Sopenharmony_ci{ 9728c2ecf20Sopenharmony_ci struct usb_serial_port *port = tty->driver_data; 9738c2ecf20Sopenharmony_ci struct moschip_port *mos7840_port = usb_get_serial_port_data(port); 9748c2ecf20Sopenharmony_ci int status; 9758c2ecf20Sopenharmony_ci 9768c2ecf20Sopenharmony_ci /* if we are implementing XON/XOFF, send the start character */ 9778c2ecf20Sopenharmony_ci if (I_IXOFF(tty)) { 9788c2ecf20Sopenharmony_ci unsigned char start_char = START_CHAR(tty); 9798c2ecf20Sopenharmony_ci status = mos7840_write(tty, port, &start_char, 1); 9808c2ecf20Sopenharmony_ci if (status <= 0) 9818c2ecf20Sopenharmony_ci return; 9828c2ecf20Sopenharmony_ci } 9838c2ecf20Sopenharmony_ci 9848c2ecf20Sopenharmony_ci /* if we are implementing RTS/CTS, toggle that line */ 9858c2ecf20Sopenharmony_ci if (C_CRTSCTS(tty)) { 9868c2ecf20Sopenharmony_ci mos7840_port->shadowMCR |= MCR_RTS; 9878c2ecf20Sopenharmony_ci status = mos7840_set_uart_reg(port, MODEM_CONTROL_REGISTER, 9888c2ecf20Sopenharmony_ci mos7840_port->shadowMCR); 9898c2ecf20Sopenharmony_ci if (status < 0) 9908c2ecf20Sopenharmony_ci return; 9918c2ecf20Sopenharmony_ci } 9928c2ecf20Sopenharmony_ci} 9938c2ecf20Sopenharmony_ci 9948c2ecf20Sopenharmony_cistatic int mos7840_tiocmget(struct tty_struct *tty) 9958c2ecf20Sopenharmony_ci{ 9968c2ecf20Sopenharmony_ci struct usb_serial_port *port = tty->driver_data; 9978c2ecf20Sopenharmony_ci unsigned int result; 9988c2ecf20Sopenharmony_ci __u16 msr; 9998c2ecf20Sopenharmony_ci __u16 mcr; 10008c2ecf20Sopenharmony_ci int status; 10018c2ecf20Sopenharmony_ci 10028c2ecf20Sopenharmony_ci status = mos7840_get_uart_reg(port, MODEM_STATUS_REGISTER, &msr); 10038c2ecf20Sopenharmony_ci if (status < 0) 10048c2ecf20Sopenharmony_ci return -EIO; 10058c2ecf20Sopenharmony_ci status = mos7840_get_uart_reg(port, MODEM_CONTROL_REGISTER, &mcr); 10068c2ecf20Sopenharmony_ci if (status < 0) 10078c2ecf20Sopenharmony_ci return -EIO; 10088c2ecf20Sopenharmony_ci result = ((mcr & MCR_DTR) ? TIOCM_DTR : 0) 10098c2ecf20Sopenharmony_ci | ((mcr & MCR_RTS) ? TIOCM_RTS : 0) 10108c2ecf20Sopenharmony_ci | ((mcr & MCR_LOOPBACK) ? TIOCM_LOOP : 0) 10118c2ecf20Sopenharmony_ci | ((msr & MOS7840_MSR_CTS) ? TIOCM_CTS : 0) 10128c2ecf20Sopenharmony_ci | ((msr & MOS7840_MSR_CD) ? TIOCM_CAR : 0) 10138c2ecf20Sopenharmony_ci | ((msr & MOS7840_MSR_RI) ? TIOCM_RI : 0) 10148c2ecf20Sopenharmony_ci | ((msr & MOS7840_MSR_DSR) ? TIOCM_DSR : 0); 10158c2ecf20Sopenharmony_ci 10168c2ecf20Sopenharmony_ci dev_dbg(&port->dev, "%s - 0x%04X\n", __func__, result); 10178c2ecf20Sopenharmony_ci 10188c2ecf20Sopenharmony_ci return result; 10198c2ecf20Sopenharmony_ci} 10208c2ecf20Sopenharmony_ci 10218c2ecf20Sopenharmony_cistatic int mos7840_tiocmset(struct tty_struct *tty, 10228c2ecf20Sopenharmony_ci unsigned int set, unsigned int clear) 10238c2ecf20Sopenharmony_ci{ 10248c2ecf20Sopenharmony_ci struct usb_serial_port *port = tty->driver_data; 10258c2ecf20Sopenharmony_ci struct moschip_port *mos7840_port = usb_get_serial_port_data(port); 10268c2ecf20Sopenharmony_ci unsigned int mcr; 10278c2ecf20Sopenharmony_ci int status; 10288c2ecf20Sopenharmony_ci 10298c2ecf20Sopenharmony_ci /* FIXME: What locks the port registers ? */ 10308c2ecf20Sopenharmony_ci mcr = mos7840_port->shadowMCR; 10318c2ecf20Sopenharmony_ci if (clear & TIOCM_RTS) 10328c2ecf20Sopenharmony_ci mcr &= ~MCR_RTS; 10338c2ecf20Sopenharmony_ci if (clear & TIOCM_DTR) 10348c2ecf20Sopenharmony_ci mcr &= ~MCR_DTR; 10358c2ecf20Sopenharmony_ci if (clear & TIOCM_LOOP) 10368c2ecf20Sopenharmony_ci mcr &= ~MCR_LOOPBACK; 10378c2ecf20Sopenharmony_ci 10388c2ecf20Sopenharmony_ci if (set & TIOCM_RTS) 10398c2ecf20Sopenharmony_ci mcr |= MCR_RTS; 10408c2ecf20Sopenharmony_ci if (set & TIOCM_DTR) 10418c2ecf20Sopenharmony_ci mcr |= MCR_DTR; 10428c2ecf20Sopenharmony_ci if (set & TIOCM_LOOP) 10438c2ecf20Sopenharmony_ci mcr |= MCR_LOOPBACK; 10448c2ecf20Sopenharmony_ci 10458c2ecf20Sopenharmony_ci mos7840_port->shadowMCR = mcr; 10468c2ecf20Sopenharmony_ci 10478c2ecf20Sopenharmony_ci status = mos7840_set_uart_reg(port, MODEM_CONTROL_REGISTER, mcr); 10488c2ecf20Sopenharmony_ci if (status < 0) { 10498c2ecf20Sopenharmony_ci dev_dbg(&port->dev, "setting MODEM_CONTROL_REGISTER Failed\n"); 10508c2ecf20Sopenharmony_ci return status; 10518c2ecf20Sopenharmony_ci } 10528c2ecf20Sopenharmony_ci 10538c2ecf20Sopenharmony_ci return 0; 10548c2ecf20Sopenharmony_ci} 10558c2ecf20Sopenharmony_ci 10568c2ecf20Sopenharmony_ci/***************************************************************************** 10578c2ecf20Sopenharmony_ci * mos7840_calc_baud_rate_divisor 10588c2ecf20Sopenharmony_ci * this function calculates the proper baud rate divisor for the specified 10598c2ecf20Sopenharmony_ci * baud rate. 10608c2ecf20Sopenharmony_ci *****************************************************************************/ 10618c2ecf20Sopenharmony_cistatic int mos7840_calc_baud_rate_divisor(struct usb_serial_port *port, 10628c2ecf20Sopenharmony_ci int baudRate, int *divisor, 10638c2ecf20Sopenharmony_ci __u16 *clk_sel_val) 10648c2ecf20Sopenharmony_ci{ 10658c2ecf20Sopenharmony_ci dev_dbg(&port->dev, "%s - %d\n", __func__, baudRate); 10668c2ecf20Sopenharmony_ci 10678c2ecf20Sopenharmony_ci if (baudRate <= 115200) { 10688c2ecf20Sopenharmony_ci *divisor = 115200 / baudRate; 10698c2ecf20Sopenharmony_ci *clk_sel_val = 0x0; 10708c2ecf20Sopenharmony_ci } 10718c2ecf20Sopenharmony_ci if ((baudRate > 115200) && (baudRate <= 230400)) { 10728c2ecf20Sopenharmony_ci *divisor = 230400 / baudRate; 10738c2ecf20Sopenharmony_ci *clk_sel_val = 0x10; 10748c2ecf20Sopenharmony_ci } else if ((baudRate > 230400) && (baudRate <= 403200)) { 10758c2ecf20Sopenharmony_ci *divisor = 403200 / baudRate; 10768c2ecf20Sopenharmony_ci *clk_sel_val = 0x20; 10778c2ecf20Sopenharmony_ci } else if ((baudRate > 403200) && (baudRate <= 460800)) { 10788c2ecf20Sopenharmony_ci *divisor = 460800 / baudRate; 10798c2ecf20Sopenharmony_ci *clk_sel_val = 0x30; 10808c2ecf20Sopenharmony_ci } else if ((baudRate > 460800) && (baudRate <= 806400)) { 10818c2ecf20Sopenharmony_ci *divisor = 806400 / baudRate; 10828c2ecf20Sopenharmony_ci *clk_sel_val = 0x40; 10838c2ecf20Sopenharmony_ci } else if ((baudRate > 806400) && (baudRate <= 921600)) { 10848c2ecf20Sopenharmony_ci *divisor = 921600 / baudRate; 10858c2ecf20Sopenharmony_ci *clk_sel_val = 0x50; 10868c2ecf20Sopenharmony_ci } else if ((baudRate > 921600) && (baudRate <= 1572864)) { 10878c2ecf20Sopenharmony_ci *divisor = 1572864 / baudRate; 10888c2ecf20Sopenharmony_ci *clk_sel_val = 0x60; 10898c2ecf20Sopenharmony_ci } else if ((baudRate > 1572864) && (baudRate <= 3145728)) { 10908c2ecf20Sopenharmony_ci *divisor = 3145728 / baudRate; 10918c2ecf20Sopenharmony_ci *clk_sel_val = 0x70; 10928c2ecf20Sopenharmony_ci } 10938c2ecf20Sopenharmony_ci return 0; 10948c2ecf20Sopenharmony_ci} 10958c2ecf20Sopenharmony_ci 10968c2ecf20Sopenharmony_ci/***************************************************************************** 10978c2ecf20Sopenharmony_ci * mos7840_send_cmd_write_baud_rate 10988c2ecf20Sopenharmony_ci * this function sends the proper command to change the baud rate of the 10998c2ecf20Sopenharmony_ci * specified port. 11008c2ecf20Sopenharmony_ci *****************************************************************************/ 11018c2ecf20Sopenharmony_ci 11028c2ecf20Sopenharmony_cistatic int mos7840_send_cmd_write_baud_rate(struct moschip_port *mos7840_port, 11038c2ecf20Sopenharmony_ci int baudRate) 11048c2ecf20Sopenharmony_ci{ 11058c2ecf20Sopenharmony_ci struct usb_serial_port *port = mos7840_port->port; 11068c2ecf20Sopenharmony_ci int divisor = 0; 11078c2ecf20Sopenharmony_ci int status; 11088c2ecf20Sopenharmony_ci __u16 Data; 11098c2ecf20Sopenharmony_ci __u16 clk_sel_val; 11108c2ecf20Sopenharmony_ci 11118c2ecf20Sopenharmony_ci dev_dbg(&port->dev, "%s - baud = %d\n", __func__, baudRate); 11128c2ecf20Sopenharmony_ci /* reset clk_uart_sel in spregOffset */ 11138c2ecf20Sopenharmony_ci if (baudRate > 115200) { 11148c2ecf20Sopenharmony_ci#ifdef HW_flow_control 11158c2ecf20Sopenharmony_ci /* NOTE: need to see the pther register to modify */ 11168c2ecf20Sopenharmony_ci /* setting h/w flow control bit to 1 */ 11178c2ecf20Sopenharmony_ci Data = 0x2b; 11188c2ecf20Sopenharmony_ci mos7840_port->shadowMCR = Data; 11198c2ecf20Sopenharmony_ci status = mos7840_set_uart_reg(port, MODEM_CONTROL_REGISTER, 11208c2ecf20Sopenharmony_ci Data); 11218c2ecf20Sopenharmony_ci if (status < 0) { 11228c2ecf20Sopenharmony_ci dev_dbg(&port->dev, "Writing spreg failed in set_serial_baud\n"); 11238c2ecf20Sopenharmony_ci return -1; 11248c2ecf20Sopenharmony_ci } 11258c2ecf20Sopenharmony_ci#endif 11268c2ecf20Sopenharmony_ci 11278c2ecf20Sopenharmony_ci } else { 11288c2ecf20Sopenharmony_ci#ifdef HW_flow_control 11298c2ecf20Sopenharmony_ci /* setting h/w flow control bit to 0 */ 11308c2ecf20Sopenharmony_ci Data = 0xb; 11318c2ecf20Sopenharmony_ci mos7840_port->shadowMCR = Data; 11328c2ecf20Sopenharmony_ci status = mos7840_set_uart_reg(port, MODEM_CONTROL_REGISTER, 11338c2ecf20Sopenharmony_ci Data); 11348c2ecf20Sopenharmony_ci if (status < 0) { 11358c2ecf20Sopenharmony_ci dev_dbg(&port->dev, "Writing spreg failed in set_serial_baud\n"); 11368c2ecf20Sopenharmony_ci return -1; 11378c2ecf20Sopenharmony_ci } 11388c2ecf20Sopenharmony_ci#endif 11398c2ecf20Sopenharmony_ci 11408c2ecf20Sopenharmony_ci } 11418c2ecf20Sopenharmony_ci 11428c2ecf20Sopenharmony_ci if (1) { /* baudRate <= 115200) */ 11438c2ecf20Sopenharmony_ci clk_sel_val = 0x0; 11448c2ecf20Sopenharmony_ci Data = 0x0; 11458c2ecf20Sopenharmony_ci status = mos7840_calc_baud_rate_divisor(port, baudRate, &divisor, 11468c2ecf20Sopenharmony_ci &clk_sel_val); 11478c2ecf20Sopenharmony_ci status = mos7840_get_reg_sync(port, mos7840_port->SpRegOffset, 11488c2ecf20Sopenharmony_ci &Data); 11498c2ecf20Sopenharmony_ci if (status < 0) { 11508c2ecf20Sopenharmony_ci dev_dbg(&port->dev, "reading spreg failed in set_serial_baud\n"); 11518c2ecf20Sopenharmony_ci return -1; 11528c2ecf20Sopenharmony_ci } 11538c2ecf20Sopenharmony_ci Data = (Data & 0x8f) | clk_sel_val; 11548c2ecf20Sopenharmony_ci status = mos7840_set_reg_sync(port, mos7840_port->SpRegOffset, 11558c2ecf20Sopenharmony_ci Data); 11568c2ecf20Sopenharmony_ci if (status < 0) { 11578c2ecf20Sopenharmony_ci dev_dbg(&port->dev, "Writing spreg failed in set_serial_baud\n"); 11588c2ecf20Sopenharmony_ci return -1; 11598c2ecf20Sopenharmony_ci } 11608c2ecf20Sopenharmony_ci /* Calculate the Divisor */ 11618c2ecf20Sopenharmony_ci 11628c2ecf20Sopenharmony_ci if (status) { 11638c2ecf20Sopenharmony_ci dev_err(&port->dev, "%s - bad baud rate\n", __func__); 11648c2ecf20Sopenharmony_ci return status; 11658c2ecf20Sopenharmony_ci } 11668c2ecf20Sopenharmony_ci /* Enable access to divisor latch */ 11678c2ecf20Sopenharmony_ci Data = mos7840_port->shadowLCR | SERIAL_LCR_DLAB; 11688c2ecf20Sopenharmony_ci mos7840_port->shadowLCR = Data; 11698c2ecf20Sopenharmony_ci mos7840_set_uart_reg(port, LINE_CONTROL_REGISTER, Data); 11708c2ecf20Sopenharmony_ci 11718c2ecf20Sopenharmony_ci /* Write the divisor */ 11728c2ecf20Sopenharmony_ci Data = (unsigned char)(divisor & 0xff); 11738c2ecf20Sopenharmony_ci dev_dbg(&port->dev, "set_serial_baud Value to write DLL is %x\n", Data); 11748c2ecf20Sopenharmony_ci mos7840_set_uart_reg(port, DIVISOR_LATCH_LSB, Data); 11758c2ecf20Sopenharmony_ci 11768c2ecf20Sopenharmony_ci Data = (unsigned char)((divisor & 0xff00) >> 8); 11778c2ecf20Sopenharmony_ci dev_dbg(&port->dev, "set_serial_baud Value to write DLM is %x\n", Data); 11788c2ecf20Sopenharmony_ci mos7840_set_uart_reg(port, DIVISOR_LATCH_MSB, Data); 11798c2ecf20Sopenharmony_ci 11808c2ecf20Sopenharmony_ci /* Disable access to divisor latch */ 11818c2ecf20Sopenharmony_ci Data = mos7840_port->shadowLCR & ~SERIAL_LCR_DLAB; 11828c2ecf20Sopenharmony_ci mos7840_port->shadowLCR = Data; 11838c2ecf20Sopenharmony_ci mos7840_set_uart_reg(port, LINE_CONTROL_REGISTER, Data); 11848c2ecf20Sopenharmony_ci 11858c2ecf20Sopenharmony_ci } 11868c2ecf20Sopenharmony_ci return status; 11878c2ecf20Sopenharmony_ci} 11888c2ecf20Sopenharmony_ci 11898c2ecf20Sopenharmony_ci/***************************************************************************** 11908c2ecf20Sopenharmony_ci * mos7840_change_port_settings 11918c2ecf20Sopenharmony_ci * This routine is called to set the UART on the device to match 11928c2ecf20Sopenharmony_ci * the specified new settings. 11938c2ecf20Sopenharmony_ci *****************************************************************************/ 11948c2ecf20Sopenharmony_ci 11958c2ecf20Sopenharmony_cistatic void mos7840_change_port_settings(struct tty_struct *tty, 11968c2ecf20Sopenharmony_ci struct moschip_port *mos7840_port, struct ktermios *old_termios) 11978c2ecf20Sopenharmony_ci{ 11988c2ecf20Sopenharmony_ci struct usb_serial_port *port = mos7840_port->port; 11998c2ecf20Sopenharmony_ci int baud; 12008c2ecf20Sopenharmony_ci unsigned cflag; 12018c2ecf20Sopenharmony_ci __u8 lData; 12028c2ecf20Sopenharmony_ci __u8 lParity; 12038c2ecf20Sopenharmony_ci __u8 lStop; 12048c2ecf20Sopenharmony_ci int status; 12058c2ecf20Sopenharmony_ci __u16 Data; 12068c2ecf20Sopenharmony_ci 12078c2ecf20Sopenharmony_ci lData = LCR_BITS_8; 12088c2ecf20Sopenharmony_ci lStop = LCR_STOP_1; 12098c2ecf20Sopenharmony_ci lParity = LCR_PAR_NONE; 12108c2ecf20Sopenharmony_ci 12118c2ecf20Sopenharmony_ci cflag = tty->termios.c_cflag; 12128c2ecf20Sopenharmony_ci 12138c2ecf20Sopenharmony_ci /* Change the number of bits */ 12148c2ecf20Sopenharmony_ci switch (cflag & CSIZE) { 12158c2ecf20Sopenharmony_ci case CS5: 12168c2ecf20Sopenharmony_ci lData = LCR_BITS_5; 12178c2ecf20Sopenharmony_ci break; 12188c2ecf20Sopenharmony_ci 12198c2ecf20Sopenharmony_ci case CS6: 12208c2ecf20Sopenharmony_ci lData = LCR_BITS_6; 12218c2ecf20Sopenharmony_ci break; 12228c2ecf20Sopenharmony_ci 12238c2ecf20Sopenharmony_ci case CS7: 12248c2ecf20Sopenharmony_ci lData = LCR_BITS_7; 12258c2ecf20Sopenharmony_ci break; 12268c2ecf20Sopenharmony_ci 12278c2ecf20Sopenharmony_ci default: 12288c2ecf20Sopenharmony_ci case CS8: 12298c2ecf20Sopenharmony_ci lData = LCR_BITS_8; 12308c2ecf20Sopenharmony_ci break; 12318c2ecf20Sopenharmony_ci } 12328c2ecf20Sopenharmony_ci 12338c2ecf20Sopenharmony_ci /* Change the Parity bit */ 12348c2ecf20Sopenharmony_ci if (cflag & PARENB) { 12358c2ecf20Sopenharmony_ci if (cflag & PARODD) { 12368c2ecf20Sopenharmony_ci lParity = LCR_PAR_ODD; 12378c2ecf20Sopenharmony_ci dev_dbg(&port->dev, "%s - parity = odd\n", __func__); 12388c2ecf20Sopenharmony_ci } else { 12398c2ecf20Sopenharmony_ci lParity = LCR_PAR_EVEN; 12408c2ecf20Sopenharmony_ci dev_dbg(&port->dev, "%s - parity = even\n", __func__); 12418c2ecf20Sopenharmony_ci } 12428c2ecf20Sopenharmony_ci 12438c2ecf20Sopenharmony_ci } else { 12448c2ecf20Sopenharmony_ci dev_dbg(&port->dev, "%s - parity = none\n", __func__); 12458c2ecf20Sopenharmony_ci } 12468c2ecf20Sopenharmony_ci 12478c2ecf20Sopenharmony_ci if (cflag & CMSPAR) 12488c2ecf20Sopenharmony_ci lParity = lParity | 0x20; 12498c2ecf20Sopenharmony_ci 12508c2ecf20Sopenharmony_ci /* Change the Stop bit */ 12518c2ecf20Sopenharmony_ci if (cflag & CSTOPB) { 12528c2ecf20Sopenharmony_ci lStop = LCR_STOP_2; 12538c2ecf20Sopenharmony_ci dev_dbg(&port->dev, "%s - stop bits = 2\n", __func__); 12548c2ecf20Sopenharmony_ci } else { 12558c2ecf20Sopenharmony_ci lStop = LCR_STOP_1; 12568c2ecf20Sopenharmony_ci dev_dbg(&port->dev, "%s - stop bits = 1\n", __func__); 12578c2ecf20Sopenharmony_ci } 12588c2ecf20Sopenharmony_ci 12598c2ecf20Sopenharmony_ci /* Update the LCR with the correct value */ 12608c2ecf20Sopenharmony_ci mos7840_port->shadowLCR &= 12618c2ecf20Sopenharmony_ci ~(LCR_BITS_MASK | LCR_STOP_MASK | LCR_PAR_MASK); 12628c2ecf20Sopenharmony_ci mos7840_port->shadowLCR |= (lData | lParity | lStop); 12638c2ecf20Sopenharmony_ci 12648c2ecf20Sopenharmony_ci dev_dbg(&port->dev, "%s - mos7840_port->shadowLCR is %x\n", __func__, 12658c2ecf20Sopenharmony_ci mos7840_port->shadowLCR); 12668c2ecf20Sopenharmony_ci /* Disable Interrupts */ 12678c2ecf20Sopenharmony_ci Data = 0x00; 12688c2ecf20Sopenharmony_ci mos7840_set_uart_reg(port, INTERRUPT_ENABLE_REGISTER, Data); 12698c2ecf20Sopenharmony_ci 12708c2ecf20Sopenharmony_ci Data = 0x00; 12718c2ecf20Sopenharmony_ci mos7840_set_uart_reg(port, FIFO_CONTROL_REGISTER, Data); 12728c2ecf20Sopenharmony_ci 12738c2ecf20Sopenharmony_ci Data = 0xcf; 12748c2ecf20Sopenharmony_ci mos7840_set_uart_reg(port, FIFO_CONTROL_REGISTER, Data); 12758c2ecf20Sopenharmony_ci 12768c2ecf20Sopenharmony_ci /* Send the updated LCR value to the mos7840 */ 12778c2ecf20Sopenharmony_ci Data = mos7840_port->shadowLCR; 12788c2ecf20Sopenharmony_ci 12798c2ecf20Sopenharmony_ci mos7840_set_uart_reg(port, LINE_CONTROL_REGISTER, Data); 12808c2ecf20Sopenharmony_ci 12818c2ecf20Sopenharmony_ci Data = 0x00b; 12828c2ecf20Sopenharmony_ci mos7840_port->shadowMCR = Data; 12838c2ecf20Sopenharmony_ci mos7840_set_uart_reg(port, MODEM_CONTROL_REGISTER, Data); 12848c2ecf20Sopenharmony_ci Data = 0x00b; 12858c2ecf20Sopenharmony_ci mos7840_set_uart_reg(port, MODEM_CONTROL_REGISTER, Data); 12868c2ecf20Sopenharmony_ci 12878c2ecf20Sopenharmony_ci /* set up the MCR register and send it to the mos7840 */ 12888c2ecf20Sopenharmony_ci 12898c2ecf20Sopenharmony_ci mos7840_port->shadowMCR = MCR_MASTER_IE; 12908c2ecf20Sopenharmony_ci if (cflag & CBAUD) 12918c2ecf20Sopenharmony_ci mos7840_port->shadowMCR |= (MCR_DTR | MCR_RTS); 12928c2ecf20Sopenharmony_ci 12938c2ecf20Sopenharmony_ci if (cflag & CRTSCTS) 12948c2ecf20Sopenharmony_ci mos7840_port->shadowMCR |= (MCR_XON_ANY); 12958c2ecf20Sopenharmony_ci else 12968c2ecf20Sopenharmony_ci mos7840_port->shadowMCR &= ~(MCR_XON_ANY); 12978c2ecf20Sopenharmony_ci 12988c2ecf20Sopenharmony_ci Data = mos7840_port->shadowMCR; 12998c2ecf20Sopenharmony_ci mos7840_set_uart_reg(port, MODEM_CONTROL_REGISTER, Data); 13008c2ecf20Sopenharmony_ci 13018c2ecf20Sopenharmony_ci /* Determine divisor based on baud rate */ 13028c2ecf20Sopenharmony_ci baud = tty_get_baud_rate(tty); 13038c2ecf20Sopenharmony_ci 13048c2ecf20Sopenharmony_ci if (!baud) { 13058c2ecf20Sopenharmony_ci /* pick a default, any default... */ 13068c2ecf20Sopenharmony_ci dev_dbg(&port->dev, "%s", "Picked default baud...\n"); 13078c2ecf20Sopenharmony_ci baud = 9600; 13088c2ecf20Sopenharmony_ci } 13098c2ecf20Sopenharmony_ci 13108c2ecf20Sopenharmony_ci dev_dbg(&port->dev, "%s - baud rate = %d\n", __func__, baud); 13118c2ecf20Sopenharmony_ci status = mos7840_send_cmd_write_baud_rate(mos7840_port, baud); 13128c2ecf20Sopenharmony_ci 13138c2ecf20Sopenharmony_ci /* Enable Interrupts */ 13148c2ecf20Sopenharmony_ci Data = 0x0c; 13158c2ecf20Sopenharmony_ci mos7840_set_uart_reg(port, INTERRUPT_ENABLE_REGISTER, Data); 13168c2ecf20Sopenharmony_ci 13178c2ecf20Sopenharmony_ci if (!mos7840_port->read_urb_busy) { 13188c2ecf20Sopenharmony_ci mos7840_port->read_urb_busy = true; 13198c2ecf20Sopenharmony_ci status = usb_submit_urb(mos7840_port->read_urb, GFP_KERNEL); 13208c2ecf20Sopenharmony_ci if (status) { 13218c2ecf20Sopenharmony_ci dev_dbg(&port->dev, "usb_submit_urb(read bulk) failed, status = %d\n", 13228c2ecf20Sopenharmony_ci status); 13238c2ecf20Sopenharmony_ci mos7840_port->read_urb_busy = false; 13248c2ecf20Sopenharmony_ci } 13258c2ecf20Sopenharmony_ci } 13268c2ecf20Sopenharmony_ci dev_dbg(&port->dev, "%s - mos7840_port->shadowLCR is End %x\n", __func__, 13278c2ecf20Sopenharmony_ci mos7840_port->shadowLCR); 13288c2ecf20Sopenharmony_ci} 13298c2ecf20Sopenharmony_ci 13308c2ecf20Sopenharmony_ci/***************************************************************************** 13318c2ecf20Sopenharmony_ci * mos7840_set_termios 13328c2ecf20Sopenharmony_ci * this function is called by the tty driver when it wants to change 13338c2ecf20Sopenharmony_ci * the termios structure 13348c2ecf20Sopenharmony_ci *****************************************************************************/ 13358c2ecf20Sopenharmony_ci 13368c2ecf20Sopenharmony_cistatic void mos7840_set_termios(struct tty_struct *tty, 13378c2ecf20Sopenharmony_ci struct usb_serial_port *port, 13388c2ecf20Sopenharmony_ci struct ktermios *old_termios) 13398c2ecf20Sopenharmony_ci{ 13408c2ecf20Sopenharmony_ci struct moschip_port *mos7840_port = usb_get_serial_port_data(port); 13418c2ecf20Sopenharmony_ci int status; 13428c2ecf20Sopenharmony_ci 13438c2ecf20Sopenharmony_ci /* change the port settings to the new ones specified */ 13448c2ecf20Sopenharmony_ci 13458c2ecf20Sopenharmony_ci mos7840_change_port_settings(tty, mos7840_port, old_termios); 13468c2ecf20Sopenharmony_ci 13478c2ecf20Sopenharmony_ci if (!mos7840_port->read_urb_busy) { 13488c2ecf20Sopenharmony_ci mos7840_port->read_urb_busy = true; 13498c2ecf20Sopenharmony_ci status = usb_submit_urb(mos7840_port->read_urb, GFP_KERNEL); 13508c2ecf20Sopenharmony_ci if (status) { 13518c2ecf20Sopenharmony_ci dev_dbg(&port->dev, "usb_submit_urb(read bulk) failed, status = %d\n", 13528c2ecf20Sopenharmony_ci status); 13538c2ecf20Sopenharmony_ci mos7840_port->read_urb_busy = false; 13548c2ecf20Sopenharmony_ci } 13558c2ecf20Sopenharmony_ci } 13568c2ecf20Sopenharmony_ci} 13578c2ecf20Sopenharmony_ci 13588c2ecf20Sopenharmony_ci/***************************************************************************** 13598c2ecf20Sopenharmony_ci * mos7840_get_lsr_info - get line status register info 13608c2ecf20Sopenharmony_ci * 13618c2ecf20Sopenharmony_ci * Purpose: Let user call ioctl() to get info when the UART physically 13628c2ecf20Sopenharmony_ci * is emptied. On bus types like RS485, the transmitter must 13638c2ecf20Sopenharmony_ci * release the bus after transmitting. This must be done when 13648c2ecf20Sopenharmony_ci * the transmit shift register is empty, not be done when the 13658c2ecf20Sopenharmony_ci * transmit holding register is empty. This functionality 13668c2ecf20Sopenharmony_ci * allows an RS485 driver to be written in user space. 13678c2ecf20Sopenharmony_ci *****************************************************************************/ 13688c2ecf20Sopenharmony_ci 13698c2ecf20Sopenharmony_cistatic int mos7840_get_lsr_info(struct tty_struct *tty, 13708c2ecf20Sopenharmony_ci unsigned int __user *value) 13718c2ecf20Sopenharmony_ci{ 13728c2ecf20Sopenharmony_ci int count; 13738c2ecf20Sopenharmony_ci unsigned int result = 0; 13748c2ecf20Sopenharmony_ci 13758c2ecf20Sopenharmony_ci count = mos7840_chars_in_buffer(tty); 13768c2ecf20Sopenharmony_ci if (count == 0) 13778c2ecf20Sopenharmony_ci result = TIOCSER_TEMT; 13788c2ecf20Sopenharmony_ci 13798c2ecf20Sopenharmony_ci if (copy_to_user(value, &result, sizeof(int))) 13808c2ecf20Sopenharmony_ci return -EFAULT; 13818c2ecf20Sopenharmony_ci return 0; 13828c2ecf20Sopenharmony_ci} 13838c2ecf20Sopenharmony_ci 13848c2ecf20Sopenharmony_ci/***************************************************************************** 13858c2ecf20Sopenharmony_ci * mos7840_get_serial_info 13868c2ecf20Sopenharmony_ci * function to get information about serial port 13878c2ecf20Sopenharmony_ci *****************************************************************************/ 13888c2ecf20Sopenharmony_ci 13898c2ecf20Sopenharmony_cistatic int mos7840_get_serial_info(struct tty_struct *tty, 13908c2ecf20Sopenharmony_ci struct serial_struct *ss) 13918c2ecf20Sopenharmony_ci{ 13928c2ecf20Sopenharmony_ci struct usb_serial_port *port = tty->driver_data; 13938c2ecf20Sopenharmony_ci struct moschip_port *mos7840_port = usb_get_serial_port_data(port); 13948c2ecf20Sopenharmony_ci 13958c2ecf20Sopenharmony_ci ss->type = PORT_16550A; 13968c2ecf20Sopenharmony_ci ss->line = mos7840_port->port->minor; 13978c2ecf20Sopenharmony_ci ss->port = mos7840_port->port->port_number; 13988c2ecf20Sopenharmony_ci ss->irq = 0; 13998c2ecf20Sopenharmony_ci ss->xmit_fifo_size = NUM_URBS * URB_TRANSFER_BUFFER_SIZE; 14008c2ecf20Sopenharmony_ci ss->baud_base = 9600; 14018c2ecf20Sopenharmony_ci ss->close_delay = 5 * HZ; 14028c2ecf20Sopenharmony_ci ss->closing_wait = 30 * HZ; 14038c2ecf20Sopenharmony_ci return 0; 14048c2ecf20Sopenharmony_ci} 14058c2ecf20Sopenharmony_ci 14068c2ecf20Sopenharmony_ci/***************************************************************************** 14078c2ecf20Sopenharmony_ci * SerialIoctl 14088c2ecf20Sopenharmony_ci * this function handles any ioctl calls to the driver 14098c2ecf20Sopenharmony_ci *****************************************************************************/ 14108c2ecf20Sopenharmony_ci 14118c2ecf20Sopenharmony_cistatic int mos7840_ioctl(struct tty_struct *tty, 14128c2ecf20Sopenharmony_ci unsigned int cmd, unsigned long arg) 14138c2ecf20Sopenharmony_ci{ 14148c2ecf20Sopenharmony_ci struct usb_serial_port *port = tty->driver_data; 14158c2ecf20Sopenharmony_ci void __user *argp = (void __user *)arg; 14168c2ecf20Sopenharmony_ci 14178c2ecf20Sopenharmony_ci switch (cmd) { 14188c2ecf20Sopenharmony_ci /* return number of bytes available */ 14198c2ecf20Sopenharmony_ci 14208c2ecf20Sopenharmony_ci case TIOCSERGETLSR: 14218c2ecf20Sopenharmony_ci dev_dbg(&port->dev, "%s TIOCSERGETLSR\n", __func__); 14228c2ecf20Sopenharmony_ci return mos7840_get_lsr_info(tty, argp); 14238c2ecf20Sopenharmony_ci 14248c2ecf20Sopenharmony_ci default: 14258c2ecf20Sopenharmony_ci break; 14268c2ecf20Sopenharmony_ci } 14278c2ecf20Sopenharmony_ci return -ENOIOCTLCMD; 14288c2ecf20Sopenharmony_ci} 14298c2ecf20Sopenharmony_ci 14308c2ecf20Sopenharmony_ci/* 14318c2ecf20Sopenharmony_ci * Check if GPO (pin 42) is connected to GPI (pin 33) as recommended by ASIX 14328c2ecf20Sopenharmony_ci * for MCS7810 by bit-banging a 16-bit word. 14338c2ecf20Sopenharmony_ci * 14348c2ecf20Sopenharmony_ci * Note that GPO is really RTS of the third port so this will toggle RTS of 14358c2ecf20Sopenharmony_ci * port two or three on two- and four-port devices. 14368c2ecf20Sopenharmony_ci */ 14378c2ecf20Sopenharmony_cistatic int mos7810_check(struct usb_serial *serial) 14388c2ecf20Sopenharmony_ci{ 14398c2ecf20Sopenharmony_ci int i, pass_count = 0; 14408c2ecf20Sopenharmony_ci u8 *buf; 14418c2ecf20Sopenharmony_ci __u16 data = 0, mcr_data = 0; 14428c2ecf20Sopenharmony_ci __u16 test_pattern = 0x55AA; 14438c2ecf20Sopenharmony_ci int res; 14448c2ecf20Sopenharmony_ci 14458c2ecf20Sopenharmony_ci buf = kmalloc(VENDOR_READ_LENGTH, GFP_KERNEL); 14468c2ecf20Sopenharmony_ci if (!buf) 14478c2ecf20Sopenharmony_ci return 0; /* failed to identify 7810 */ 14488c2ecf20Sopenharmony_ci 14498c2ecf20Sopenharmony_ci /* Store MCR setting */ 14508c2ecf20Sopenharmony_ci res = usb_control_msg(serial->dev, usb_rcvctrlpipe(serial->dev, 0), 14518c2ecf20Sopenharmony_ci MCS_RDREQ, MCS_RD_RTYPE, 0x0300, MODEM_CONTROL_REGISTER, 14528c2ecf20Sopenharmony_ci buf, VENDOR_READ_LENGTH, MOS_WDR_TIMEOUT); 14538c2ecf20Sopenharmony_ci if (res == VENDOR_READ_LENGTH) 14548c2ecf20Sopenharmony_ci mcr_data = *buf; 14558c2ecf20Sopenharmony_ci 14568c2ecf20Sopenharmony_ci for (i = 0; i < 16; i++) { 14578c2ecf20Sopenharmony_ci /* Send the 1-bit test pattern out to MCS7810 test pin */ 14588c2ecf20Sopenharmony_ci usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0), 14598c2ecf20Sopenharmony_ci MCS_WRREQ, MCS_WR_RTYPE, 14608c2ecf20Sopenharmony_ci (0x0300 | (((test_pattern >> i) & 0x0001) << 1)), 14618c2ecf20Sopenharmony_ci MODEM_CONTROL_REGISTER, NULL, 0, MOS_WDR_TIMEOUT); 14628c2ecf20Sopenharmony_ci 14638c2ecf20Sopenharmony_ci /* Read the test pattern back */ 14648c2ecf20Sopenharmony_ci res = usb_control_msg(serial->dev, 14658c2ecf20Sopenharmony_ci usb_rcvctrlpipe(serial->dev, 0), MCS_RDREQ, 14668c2ecf20Sopenharmony_ci MCS_RD_RTYPE, 0, GPIO_REGISTER, buf, 14678c2ecf20Sopenharmony_ci VENDOR_READ_LENGTH, MOS_WDR_TIMEOUT); 14688c2ecf20Sopenharmony_ci if (res == VENDOR_READ_LENGTH) 14698c2ecf20Sopenharmony_ci data = *buf; 14708c2ecf20Sopenharmony_ci 14718c2ecf20Sopenharmony_ci /* If this is a MCS7810 device, both test patterns must match */ 14728c2ecf20Sopenharmony_ci if (((test_pattern >> i) ^ (~data >> 1)) & 0x0001) 14738c2ecf20Sopenharmony_ci break; 14748c2ecf20Sopenharmony_ci 14758c2ecf20Sopenharmony_ci pass_count++; 14768c2ecf20Sopenharmony_ci } 14778c2ecf20Sopenharmony_ci 14788c2ecf20Sopenharmony_ci /* Restore MCR setting */ 14798c2ecf20Sopenharmony_ci usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0), MCS_WRREQ, 14808c2ecf20Sopenharmony_ci MCS_WR_RTYPE, 0x0300 | mcr_data, MODEM_CONTROL_REGISTER, NULL, 14818c2ecf20Sopenharmony_ci 0, MOS_WDR_TIMEOUT); 14828c2ecf20Sopenharmony_ci 14838c2ecf20Sopenharmony_ci kfree(buf); 14848c2ecf20Sopenharmony_ci 14858c2ecf20Sopenharmony_ci if (pass_count == 16) 14868c2ecf20Sopenharmony_ci return 1; 14878c2ecf20Sopenharmony_ci 14888c2ecf20Sopenharmony_ci return 0; 14898c2ecf20Sopenharmony_ci} 14908c2ecf20Sopenharmony_ci 14918c2ecf20Sopenharmony_cistatic int mos7840_probe(struct usb_serial *serial, 14928c2ecf20Sopenharmony_ci const struct usb_device_id *id) 14938c2ecf20Sopenharmony_ci{ 14948c2ecf20Sopenharmony_ci unsigned long device_flags = id->driver_info; 14958c2ecf20Sopenharmony_ci u8 *buf; 14968c2ecf20Sopenharmony_ci 14978c2ecf20Sopenharmony_ci /* Skip device-type detection if we already have device flags. */ 14988c2ecf20Sopenharmony_ci if (device_flags) 14998c2ecf20Sopenharmony_ci goto out; 15008c2ecf20Sopenharmony_ci 15018c2ecf20Sopenharmony_ci buf = kzalloc(VENDOR_READ_LENGTH, GFP_KERNEL); 15028c2ecf20Sopenharmony_ci if (!buf) 15038c2ecf20Sopenharmony_ci return -ENOMEM; 15048c2ecf20Sopenharmony_ci 15058c2ecf20Sopenharmony_ci usb_control_msg(serial->dev, usb_rcvctrlpipe(serial->dev, 0), 15068c2ecf20Sopenharmony_ci MCS_RDREQ, MCS_RD_RTYPE, 0, GPIO_REGISTER, buf, 15078c2ecf20Sopenharmony_ci VENDOR_READ_LENGTH, MOS_WDR_TIMEOUT); 15088c2ecf20Sopenharmony_ci 15098c2ecf20Sopenharmony_ci /* For a MCS7840 device GPIO0 must be set to 1 */ 15108c2ecf20Sopenharmony_ci if (buf[0] & 0x01) 15118c2ecf20Sopenharmony_ci device_flags = MCS_PORTS(4); 15128c2ecf20Sopenharmony_ci else if (mos7810_check(serial)) 15138c2ecf20Sopenharmony_ci device_flags = MCS_PORTS(1) | MCS_LED; 15148c2ecf20Sopenharmony_ci else 15158c2ecf20Sopenharmony_ci device_flags = MCS_PORTS(2); 15168c2ecf20Sopenharmony_ci 15178c2ecf20Sopenharmony_ci kfree(buf); 15188c2ecf20Sopenharmony_ciout: 15198c2ecf20Sopenharmony_ci usb_set_serial_data(serial, (void *)device_flags); 15208c2ecf20Sopenharmony_ci 15218c2ecf20Sopenharmony_ci return 0; 15228c2ecf20Sopenharmony_ci} 15238c2ecf20Sopenharmony_ci 15248c2ecf20Sopenharmony_cistatic int mos7840_calc_num_ports(struct usb_serial *serial, 15258c2ecf20Sopenharmony_ci struct usb_serial_endpoints *epds) 15268c2ecf20Sopenharmony_ci{ 15278c2ecf20Sopenharmony_ci unsigned long device_flags = (unsigned long)usb_get_serial_data(serial); 15288c2ecf20Sopenharmony_ci int num_ports = MCS_PORTS(device_flags); 15298c2ecf20Sopenharmony_ci 15308c2ecf20Sopenharmony_ci if (num_ports == 0 || num_ports > 4) 15318c2ecf20Sopenharmony_ci return -ENODEV; 15328c2ecf20Sopenharmony_ci 15338c2ecf20Sopenharmony_ci if (epds->num_bulk_in < num_ports || epds->num_bulk_out < num_ports) { 15348c2ecf20Sopenharmony_ci dev_err(&serial->interface->dev, "missing endpoints\n"); 15358c2ecf20Sopenharmony_ci return -ENODEV; 15368c2ecf20Sopenharmony_ci } 15378c2ecf20Sopenharmony_ci 15388c2ecf20Sopenharmony_ci return num_ports; 15398c2ecf20Sopenharmony_ci} 15408c2ecf20Sopenharmony_ci 15418c2ecf20Sopenharmony_cistatic int mos7840_attach(struct usb_serial *serial) 15428c2ecf20Sopenharmony_ci{ 15438c2ecf20Sopenharmony_ci struct device *dev = &serial->interface->dev; 15448c2ecf20Sopenharmony_ci int status; 15458c2ecf20Sopenharmony_ci u16 val; 15468c2ecf20Sopenharmony_ci 15478c2ecf20Sopenharmony_ci /* Zero Length flag enable */ 15488c2ecf20Sopenharmony_ci val = 0x0f; 15498c2ecf20Sopenharmony_ci status = mos7840_set_reg_sync(serial->port[0], ZLP_REG5, val); 15508c2ecf20Sopenharmony_ci if (status < 0) 15518c2ecf20Sopenharmony_ci dev_dbg(dev, "Writing ZLP_REG5 failed status-0x%x\n", status); 15528c2ecf20Sopenharmony_ci else 15538c2ecf20Sopenharmony_ci dev_dbg(dev, "ZLP_REG5 Writing success status%d\n", status); 15548c2ecf20Sopenharmony_ci 15558c2ecf20Sopenharmony_ci return status; 15568c2ecf20Sopenharmony_ci} 15578c2ecf20Sopenharmony_ci 15588c2ecf20Sopenharmony_cistatic int mos7840_port_probe(struct usb_serial_port *port) 15598c2ecf20Sopenharmony_ci{ 15608c2ecf20Sopenharmony_ci struct usb_serial *serial = port->serial; 15618c2ecf20Sopenharmony_ci unsigned long device_flags = (unsigned long)usb_get_serial_data(serial); 15628c2ecf20Sopenharmony_ci struct moschip_port *mos7840_port; 15638c2ecf20Sopenharmony_ci int status; 15648c2ecf20Sopenharmony_ci int pnum; 15658c2ecf20Sopenharmony_ci __u16 Data; 15668c2ecf20Sopenharmony_ci 15678c2ecf20Sopenharmony_ci /* we set up the pointers to the endpoints in the mos7840_open * 15688c2ecf20Sopenharmony_ci * function, as the structures aren't created yet. */ 15698c2ecf20Sopenharmony_ci 15708c2ecf20Sopenharmony_ci pnum = port->port_number; 15718c2ecf20Sopenharmony_ci 15728c2ecf20Sopenharmony_ci dev_dbg(&port->dev, "mos7840_startup: configuring port %d\n", pnum); 15738c2ecf20Sopenharmony_ci mos7840_port = kzalloc(sizeof(struct moschip_port), GFP_KERNEL); 15748c2ecf20Sopenharmony_ci if (!mos7840_port) 15758c2ecf20Sopenharmony_ci return -ENOMEM; 15768c2ecf20Sopenharmony_ci 15778c2ecf20Sopenharmony_ci /* Initialize all port interrupt end point to port 0 int 15788c2ecf20Sopenharmony_ci * endpoint. Our device has only one interrupt end point 15798c2ecf20Sopenharmony_ci * common to all port */ 15808c2ecf20Sopenharmony_ci 15818c2ecf20Sopenharmony_ci mos7840_port->port = port; 15828c2ecf20Sopenharmony_ci spin_lock_init(&mos7840_port->pool_lock); 15838c2ecf20Sopenharmony_ci 15848c2ecf20Sopenharmony_ci /* minor is not initialised until later by 15858c2ecf20Sopenharmony_ci * usb-serial.c:get_free_serial() and cannot therefore be used 15868c2ecf20Sopenharmony_ci * to index device instances */ 15878c2ecf20Sopenharmony_ci mos7840_port->port_num = pnum + 1; 15888c2ecf20Sopenharmony_ci dev_dbg(&port->dev, "port->minor = %d\n", port->minor); 15898c2ecf20Sopenharmony_ci dev_dbg(&port->dev, "mos7840_port->port_num = %d\n", mos7840_port->port_num); 15908c2ecf20Sopenharmony_ci 15918c2ecf20Sopenharmony_ci if (mos7840_port->port_num == 1) { 15928c2ecf20Sopenharmony_ci mos7840_port->SpRegOffset = 0x0; 15938c2ecf20Sopenharmony_ci mos7840_port->ControlRegOffset = 0x1; 15948c2ecf20Sopenharmony_ci mos7840_port->DcrRegOffset = 0x4; 15958c2ecf20Sopenharmony_ci } else { 15968c2ecf20Sopenharmony_ci u8 phy_num = mos7840_port->port_num; 15978c2ecf20Sopenharmony_ci 15988c2ecf20Sopenharmony_ci /* Port 2 in the 2-port case uses registers of port 3 */ 15998c2ecf20Sopenharmony_ci if (serial->num_ports == 2) 16008c2ecf20Sopenharmony_ci phy_num = 3; 16018c2ecf20Sopenharmony_ci 16028c2ecf20Sopenharmony_ci mos7840_port->SpRegOffset = 0x8 + 2 * (phy_num - 2); 16038c2ecf20Sopenharmony_ci mos7840_port->ControlRegOffset = 0x9 + 2 * (phy_num - 2); 16048c2ecf20Sopenharmony_ci mos7840_port->DcrRegOffset = 0x16 + 3 * (phy_num - 2); 16058c2ecf20Sopenharmony_ci } 16068c2ecf20Sopenharmony_ci mos7840_dump_serial_port(port, mos7840_port); 16078c2ecf20Sopenharmony_ci usb_set_serial_port_data(port, mos7840_port); 16088c2ecf20Sopenharmony_ci 16098c2ecf20Sopenharmony_ci /* enable rx_disable bit in control register */ 16108c2ecf20Sopenharmony_ci status = mos7840_get_reg_sync(port, 16118c2ecf20Sopenharmony_ci mos7840_port->ControlRegOffset, &Data); 16128c2ecf20Sopenharmony_ci if (status < 0) { 16138c2ecf20Sopenharmony_ci dev_dbg(&port->dev, "Reading ControlReg failed status-0x%x\n", status); 16148c2ecf20Sopenharmony_ci goto error; 16158c2ecf20Sopenharmony_ci } else 16168c2ecf20Sopenharmony_ci dev_dbg(&port->dev, "ControlReg Reading success val is %x, status%d\n", Data, status); 16178c2ecf20Sopenharmony_ci Data |= 0x08; /* setting driver done bit */ 16188c2ecf20Sopenharmony_ci Data |= 0x04; /* sp1_bit to have cts change reflect in 16198c2ecf20Sopenharmony_ci modem status reg */ 16208c2ecf20Sopenharmony_ci 16218c2ecf20Sopenharmony_ci /* Data |= 0x20; //rx_disable bit */ 16228c2ecf20Sopenharmony_ci status = mos7840_set_reg_sync(port, 16238c2ecf20Sopenharmony_ci mos7840_port->ControlRegOffset, Data); 16248c2ecf20Sopenharmony_ci if (status < 0) { 16258c2ecf20Sopenharmony_ci dev_dbg(&port->dev, "Writing ControlReg failed(rx_disable) status-0x%x\n", status); 16268c2ecf20Sopenharmony_ci goto error; 16278c2ecf20Sopenharmony_ci } else 16288c2ecf20Sopenharmony_ci dev_dbg(&port->dev, "ControlReg Writing success(rx_disable) status%d\n", status); 16298c2ecf20Sopenharmony_ci 16308c2ecf20Sopenharmony_ci /* Write default values in DCR (i.e 0x01 in DCR0, 0x05 in DCR2 16318c2ecf20Sopenharmony_ci and 0x24 in DCR3 */ 16328c2ecf20Sopenharmony_ci Data = 0x01; 16338c2ecf20Sopenharmony_ci status = mos7840_set_reg_sync(port, 16348c2ecf20Sopenharmony_ci (__u16) (mos7840_port->DcrRegOffset + 0), Data); 16358c2ecf20Sopenharmony_ci if (status < 0) { 16368c2ecf20Sopenharmony_ci dev_dbg(&port->dev, "Writing DCR0 failed status-0x%x\n", status); 16378c2ecf20Sopenharmony_ci goto error; 16388c2ecf20Sopenharmony_ci } else 16398c2ecf20Sopenharmony_ci dev_dbg(&port->dev, "DCR0 Writing success status%d\n", status); 16408c2ecf20Sopenharmony_ci 16418c2ecf20Sopenharmony_ci Data = 0x05; 16428c2ecf20Sopenharmony_ci status = mos7840_set_reg_sync(port, 16438c2ecf20Sopenharmony_ci (__u16) (mos7840_port->DcrRegOffset + 1), Data); 16448c2ecf20Sopenharmony_ci if (status < 0) { 16458c2ecf20Sopenharmony_ci dev_dbg(&port->dev, "Writing DCR1 failed status-0x%x\n", status); 16468c2ecf20Sopenharmony_ci goto error; 16478c2ecf20Sopenharmony_ci } else 16488c2ecf20Sopenharmony_ci dev_dbg(&port->dev, "DCR1 Writing success status%d\n", status); 16498c2ecf20Sopenharmony_ci 16508c2ecf20Sopenharmony_ci Data = 0x24; 16518c2ecf20Sopenharmony_ci status = mos7840_set_reg_sync(port, 16528c2ecf20Sopenharmony_ci (__u16) (mos7840_port->DcrRegOffset + 2), Data); 16538c2ecf20Sopenharmony_ci if (status < 0) { 16548c2ecf20Sopenharmony_ci dev_dbg(&port->dev, "Writing DCR2 failed status-0x%x\n", status); 16558c2ecf20Sopenharmony_ci goto error; 16568c2ecf20Sopenharmony_ci } else 16578c2ecf20Sopenharmony_ci dev_dbg(&port->dev, "DCR2 Writing success status%d\n", status); 16588c2ecf20Sopenharmony_ci 16598c2ecf20Sopenharmony_ci /* write values in clkstart0x0 and clkmulti 0x20 */ 16608c2ecf20Sopenharmony_ci Data = 0x0; 16618c2ecf20Sopenharmony_ci status = mos7840_set_reg_sync(port, CLK_START_VALUE_REGISTER, Data); 16628c2ecf20Sopenharmony_ci if (status < 0) { 16638c2ecf20Sopenharmony_ci dev_dbg(&port->dev, "Writing CLK_START_VALUE_REGISTER failed status-0x%x\n", status); 16648c2ecf20Sopenharmony_ci goto error; 16658c2ecf20Sopenharmony_ci } else 16668c2ecf20Sopenharmony_ci dev_dbg(&port->dev, "CLK_START_VALUE_REGISTER Writing success status%d\n", status); 16678c2ecf20Sopenharmony_ci 16688c2ecf20Sopenharmony_ci Data = 0x20; 16698c2ecf20Sopenharmony_ci status = mos7840_set_reg_sync(port, CLK_MULTI_REGISTER, Data); 16708c2ecf20Sopenharmony_ci if (status < 0) { 16718c2ecf20Sopenharmony_ci dev_dbg(&port->dev, "Writing CLK_MULTI_REGISTER failed status-0x%x\n", status); 16728c2ecf20Sopenharmony_ci goto error; 16738c2ecf20Sopenharmony_ci } else 16748c2ecf20Sopenharmony_ci dev_dbg(&port->dev, "CLK_MULTI_REGISTER Writing success status%d\n", status); 16758c2ecf20Sopenharmony_ci 16768c2ecf20Sopenharmony_ci /* write value 0x0 to scratchpad register */ 16778c2ecf20Sopenharmony_ci Data = 0x00; 16788c2ecf20Sopenharmony_ci status = mos7840_set_uart_reg(port, SCRATCH_PAD_REGISTER, Data); 16798c2ecf20Sopenharmony_ci if (status < 0) { 16808c2ecf20Sopenharmony_ci dev_dbg(&port->dev, "Writing SCRATCH_PAD_REGISTER failed status-0x%x\n", status); 16818c2ecf20Sopenharmony_ci goto error; 16828c2ecf20Sopenharmony_ci } else 16838c2ecf20Sopenharmony_ci dev_dbg(&port->dev, "SCRATCH_PAD_REGISTER Writing success status%d\n", status); 16848c2ecf20Sopenharmony_ci 16858c2ecf20Sopenharmony_ci /* Zero Length flag register */ 16868c2ecf20Sopenharmony_ci if ((mos7840_port->port_num != 1) && (serial->num_ports == 2)) { 16878c2ecf20Sopenharmony_ci Data = 0xff; 16888c2ecf20Sopenharmony_ci status = mos7840_set_reg_sync(port, 16898c2ecf20Sopenharmony_ci (__u16) (ZLP_REG1 + 16908c2ecf20Sopenharmony_ci ((__u16)mos7840_port->port_num)), Data); 16918c2ecf20Sopenharmony_ci dev_dbg(&port->dev, "ZLIP offset %x\n", 16928c2ecf20Sopenharmony_ci (__u16)(ZLP_REG1 + ((__u16) mos7840_port->port_num))); 16938c2ecf20Sopenharmony_ci if (status < 0) { 16948c2ecf20Sopenharmony_ci dev_dbg(&port->dev, "Writing ZLP_REG%d failed status-0x%x\n", pnum + 2, status); 16958c2ecf20Sopenharmony_ci goto error; 16968c2ecf20Sopenharmony_ci } else 16978c2ecf20Sopenharmony_ci dev_dbg(&port->dev, "ZLP_REG%d Writing success status%d\n", pnum + 2, status); 16988c2ecf20Sopenharmony_ci } else { 16998c2ecf20Sopenharmony_ci Data = 0xff; 17008c2ecf20Sopenharmony_ci status = mos7840_set_reg_sync(port, 17018c2ecf20Sopenharmony_ci (__u16) (ZLP_REG1 + 17028c2ecf20Sopenharmony_ci ((__u16)mos7840_port->port_num) - 0x1), Data); 17038c2ecf20Sopenharmony_ci dev_dbg(&port->dev, "ZLIP offset %x\n", 17048c2ecf20Sopenharmony_ci (__u16)(ZLP_REG1 + ((__u16) mos7840_port->port_num) - 0x1)); 17058c2ecf20Sopenharmony_ci if (status < 0) { 17068c2ecf20Sopenharmony_ci dev_dbg(&port->dev, "Writing ZLP_REG%d failed status-0x%x\n", pnum + 1, status); 17078c2ecf20Sopenharmony_ci goto error; 17088c2ecf20Sopenharmony_ci } else 17098c2ecf20Sopenharmony_ci dev_dbg(&port->dev, "ZLP_REG%d Writing success status%d\n", pnum + 1, status); 17108c2ecf20Sopenharmony_ci 17118c2ecf20Sopenharmony_ci } 17128c2ecf20Sopenharmony_ci 17138c2ecf20Sopenharmony_ci mos7840_port->has_led = device_flags & MCS_LED; 17148c2ecf20Sopenharmony_ci 17158c2ecf20Sopenharmony_ci /* Initialize LED timers */ 17168c2ecf20Sopenharmony_ci if (mos7840_port->has_led) { 17178c2ecf20Sopenharmony_ci mos7840_port->led_urb = usb_alloc_urb(0, GFP_KERNEL); 17188c2ecf20Sopenharmony_ci mos7840_port->led_dr = kmalloc(sizeof(*mos7840_port->led_dr), 17198c2ecf20Sopenharmony_ci GFP_KERNEL); 17208c2ecf20Sopenharmony_ci if (!mos7840_port->led_urb || !mos7840_port->led_dr) { 17218c2ecf20Sopenharmony_ci status = -ENOMEM; 17228c2ecf20Sopenharmony_ci goto error; 17238c2ecf20Sopenharmony_ci } 17248c2ecf20Sopenharmony_ci 17258c2ecf20Sopenharmony_ci timer_setup(&mos7840_port->led_timer1, mos7840_led_off, 0); 17268c2ecf20Sopenharmony_ci mos7840_port->led_timer1.expires = 17278c2ecf20Sopenharmony_ci jiffies + msecs_to_jiffies(LED_ON_MS); 17288c2ecf20Sopenharmony_ci timer_setup(&mos7840_port->led_timer2, mos7840_led_flag_off, 17298c2ecf20Sopenharmony_ci 0); 17308c2ecf20Sopenharmony_ci mos7840_port->led_timer2.expires = 17318c2ecf20Sopenharmony_ci jiffies + msecs_to_jiffies(LED_OFF_MS); 17328c2ecf20Sopenharmony_ci 17338c2ecf20Sopenharmony_ci /* Turn off LED */ 17348c2ecf20Sopenharmony_ci mos7840_set_led_sync(port, MODEM_CONTROL_REGISTER, 0x0300); 17358c2ecf20Sopenharmony_ci } 17368c2ecf20Sopenharmony_ci 17378c2ecf20Sopenharmony_ci return 0; 17388c2ecf20Sopenharmony_cierror: 17398c2ecf20Sopenharmony_ci kfree(mos7840_port->led_dr); 17408c2ecf20Sopenharmony_ci usb_free_urb(mos7840_port->led_urb); 17418c2ecf20Sopenharmony_ci kfree(mos7840_port); 17428c2ecf20Sopenharmony_ci 17438c2ecf20Sopenharmony_ci return status; 17448c2ecf20Sopenharmony_ci} 17458c2ecf20Sopenharmony_ci 17468c2ecf20Sopenharmony_cistatic int mos7840_port_remove(struct usb_serial_port *port) 17478c2ecf20Sopenharmony_ci{ 17488c2ecf20Sopenharmony_ci struct moschip_port *mos7840_port = usb_get_serial_port_data(port); 17498c2ecf20Sopenharmony_ci 17508c2ecf20Sopenharmony_ci if (mos7840_port->has_led) { 17518c2ecf20Sopenharmony_ci /* Turn off LED */ 17528c2ecf20Sopenharmony_ci mos7840_set_led_sync(port, MODEM_CONTROL_REGISTER, 0x0300); 17538c2ecf20Sopenharmony_ci 17548c2ecf20Sopenharmony_ci del_timer_sync(&mos7840_port->led_timer1); 17558c2ecf20Sopenharmony_ci del_timer_sync(&mos7840_port->led_timer2); 17568c2ecf20Sopenharmony_ci 17578c2ecf20Sopenharmony_ci usb_kill_urb(mos7840_port->led_urb); 17588c2ecf20Sopenharmony_ci usb_free_urb(mos7840_port->led_urb); 17598c2ecf20Sopenharmony_ci kfree(mos7840_port->led_dr); 17608c2ecf20Sopenharmony_ci } 17618c2ecf20Sopenharmony_ci 17628c2ecf20Sopenharmony_ci kfree(mos7840_port); 17638c2ecf20Sopenharmony_ci 17648c2ecf20Sopenharmony_ci return 0; 17658c2ecf20Sopenharmony_ci} 17668c2ecf20Sopenharmony_ci 17678c2ecf20Sopenharmony_cistatic struct usb_serial_driver moschip7840_4port_device = { 17688c2ecf20Sopenharmony_ci .driver = { 17698c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 17708c2ecf20Sopenharmony_ci .name = "mos7840", 17718c2ecf20Sopenharmony_ci }, 17728c2ecf20Sopenharmony_ci .description = DRIVER_DESC, 17738c2ecf20Sopenharmony_ci .id_table = id_table, 17748c2ecf20Sopenharmony_ci .num_interrupt_in = 1, 17758c2ecf20Sopenharmony_ci .open = mos7840_open, 17768c2ecf20Sopenharmony_ci .close = mos7840_close, 17778c2ecf20Sopenharmony_ci .write = mos7840_write, 17788c2ecf20Sopenharmony_ci .write_room = mos7840_write_room, 17798c2ecf20Sopenharmony_ci .chars_in_buffer = mos7840_chars_in_buffer, 17808c2ecf20Sopenharmony_ci .throttle = mos7840_throttle, 17818c2ecf20Sopenharmony_ci .unthrottle = mos7840_unthrottle, 17828c2ecf20Sopenharmony_ci .calc_num_ports = mos7840_calc_num_ports, 17838c2ecf20Sopenharmony_ci .probe = mos7840_probe, 17848c2ecf20Sopenharmony_ci .attach = mos7840_attach, 17858c2ecf20Sopenharmony_ci .ioctl = mos7840_ioctl, 17868c2ecf20Sopenharmony_ci .get_serial = mos7840_get_serial_info, 17878c2ecf20Sopenharmony_ci .set_termios = mos7840_set_termios, 17888c2ecf20Sopenharmony_ci .break_ctl = mos7840_break, 17898c2ecf20Sopenharmony_ci .tiocmget = mos7840_tiocmget, 17908c2ecf20Sopenharmony_ci .tiocmset = mos7840_tiocmset, 17918c2ecf20Sopenharmony_ci .get_icount = usb_serial_generic_get_icount, 17928c2ecf20Sopenharmony_ci .port_probe = mos7840_port_probe, 17938c2ecf20Sopenharmony_ci .port_remove = mos7840_port_remove, 17948c2ecf20Sopenharmony_ci .read_bulk_callback = mos7840_bulk_in_callback, 17958c2ecf20Sopenharmony_ci}; 17968c2ecf20Sopenharmony_ci 17978c2ecf20Sopenharmony_cistatic struct usb_serial_driver * const serial_drivers[] = { 17988c2ecf20Sopenharmony_ci &moschip7840_4port_device, NULL 17998c2ecf20Sopenharmony_ci}; 18008c2ecf20Sopenharmony_ci 18018c2ecf20Sopenharmony_cimodule_usb_serial_driver(serial_drivers, id_table); 18028c2ecf20Sopenharmony_ci 18038c2ecf20Sopenharmony_ciMODULE_DESCRIPTION(DRIVER_DESC); 18048c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 1805