162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * mos7720.c 462306a36Sopenharmony_ci * Controls the Moschip 7720 usb to dual port serial converter 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * Copyright 2006 Moschip Semiconductor Tech. Ltd. 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci * Developed by: 962306a36Sopenharmony_ci * Vijaya Kumar <vijaykumar.gn@gmail.com> 1062306a36Sopenharmony_ci * Ajay Kumar <naanuajay@yahoo.com> 1162306a36Sopenharmony_ci * Gurudeva <ngurudeva@yahoo.com> 1262306a36Sopenharmony_ci * 1362306a36Sopenharmony_ci * Cleaned up from the original by: 1462306a36Sopenharmony_ci * Greg Kroah-Hartman <gregkh@suse.de> 1562306a36Sopenharmony_ci * 1662306a36Sopenharmony_ci * Originally based on drivers/usb/serial/io_edgeport.c which is: 1762306a36Sopenharmony_ci * Copyright (C) 2000 Inside Out Networks, All rights reserved. 1862306a36Sopenharmony_ci * Copyright (C) 2001-2002 Greg Kroah-Hartman <greg@kroah.com> 1962306a36Sopenharmony_ci */ 2062306a36Sopenharmony_ci#include <linux/kernel.h> 2162306a36Sopenharmony_ci#include <linux/errno.h> 2262306a36Sopenharmony_ci#include <linux/slab.h> 2362306a36Sopenharmony_ci#include <linux/tty.h> 2462306a36Sopenharmony_ci#include <linux/tty_driver.h> 2562306a36Sopenharmony_ci#include <linux/tty_flip.h> 2662306a36Sopenharmony_ci#include <linux/module.h> 2762306a36Sopenharmony_ci#include <linux/spinlock.h> 2862306a36Sopenharmony_ci#include <linux/serial.h> 2962306a36Sopenharmony_ci#include <linux/serial_reg.h> 3062306a36Sopenharmony_ci#include <linux/usb.h> 3162306a36Sopenharmony_ci#include <linux/usb/serial.h> 3262306a36Sopenharmony_ci#include <linux/uaccess.h> 3362306a36Sopenharmony_ci#include <linux/parport.h> 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci#define DRIVER_AUTHOR "Aspire Communications pvt Ltd." 3662306a36Sopenharmony_ci#define DRIVER_DESC "Moschip USB Serial Driver" 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci/* default urb timeout */ 3962306a36Sopenharmony_ci#define MOS_WDR_TIMEOUT 5000 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci#define MOS_MAX_PORT 0x02 4262306a36Sopenharmony_ci#define MOS_WRITE 0x0E 4362306a36Sopenharmony_ci#define MOS_READ 0x0D 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci/* Interrupt Routines Defines */ 4662306a36Sopenharmony_ci#define SERIAL_IIR_RLS 0x06 4762306a36Sopenharmony_ci#define SERIAL_IIR_RDA 0x04 4862306a36Sopenharmony_ci#define SERIAL_IIR_CTI 0x0c 4962306a36Sopenharmony_ci#define SERIAL_IIR_THR 0x02 5062306a36Sopenharmony_ci#define SERIAL_IIR_MS 0x00 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci#define NUM_URBS 16 /* URB Count */ 5362306a36Sopenharmony_ci#define URB_TRANSFER_BUFFER_SIZE 32 /* URB Size */ 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci/* This structure holds all of the local serial port information */ 5662306a36Sopenharmony_cistruct moschip_port { 5762306a36Sopenharmony_ci __u8 shadowLCR; /* last LCR value received */ 5862306a36Sopenharmony_ci __u8 shadowMCR; /* last MCR value received */ 5962306a36Sopenharmony_ci __u8 shadowMSR; /* last MSR value received */ 6062306a36Sopenharmony_ci char open; 6162306a36Sopenharmony_ci struct usb_serial_port *port; /* loop back to the owner */ 6262306a36Sopenharmony_ci struct urb *write_urb_pool[NUM_URBS]; 6362306a36Sopenharmony_ci}; 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci#define USB_VENDOR_ID_MOSCHIP 0x9710 6662306a36Sopenharmony_ci#define MOSCHIP_DEVICE_ID_7720 0x7720 6762306a36Sopenharmony_ci#define MOSCHIP_DEVICE_ID_7715 0x7715 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_cistatic const struct usb_device_id id_table[] = { 7062306a36Sopenharmony_ci { USB_DEVICE(USB_VENDOR_ID_MOSCHIP, MOSCHIP_DEVICE_ID_7720) }, 7162306a36Sopenharmony_ci { USB_DEVICE(USB_VENDOR_ID_MOSCHIP, MOSCHIP_DEVICE_ID_7715) }, 7262306a36Sopenharmony_ci { } /* terminating entry */ 7362306a36Sopenharmony_ci}; 7462306a36Sopenharmony_ciMODULE_DEVICE_TABLE(usb, id_table); 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci#ifdef CONFIG_USB_SERIAL_MOS7715_PARPORT 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci/* initial values for parport regs */ 7962306a36Sopenharmony_ci#define DCR_INIT_VAL 0x0c /* SLCTIN, nINIT */ 8062306a36Sopenharmony_ci#define ECR_INIT_VAL 0x00 /* SPP mode */ 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_cienum mos7715_pp_modes { 8362306a36Sopenharmony_ci SPP = 0<<5, 8462306a36Sopenharmony_ci PS2 = 1<<5, /* moschip calls this 'NIBBLE' mode */ 8562306a36Sopenharmony_ci PPF = 2<<5, /* moschip calls this 'CB-FIFO mode */ 8662306a36Sopenharmony_ci}; 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_cistruct mos7715_parport { 8962306a36Sopenharmony_ci struct parport *pp; /* back to containing struct */ 9062306a36Sopenharmony_ci struct kref ref_count; /* to instance of this struct */ 9162306a36Sopenharmony_ci bool msg_pending; /* usb sync call pending */ 9262306a36Sopenharmony_ci struct completion syncmsg_compl; /* usb sync call completed */ 9362306a36Sopenharmony_ci struct work_struct work; /* restore deferred writes */ 9462306a36Sopenharmony_ci struct usb_serial *serial; /* back to containing struct */ 9562306a36Sopenharmony_ci __u8 shadowECR; /* parallel port regs... */ 9662306a36Sopenharmony_ci __u8 shadowDCR; 9762306a36Sopenharmony_ci atomic_t shadowDSR; /* updated in int-in callback */ 9862306a36Sopenharmony_ci}; 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci/* lock guards against dereferencing NULL ptr in parport ops callbacks */ 10162306a36Sopenharmony_cistatic DEFINE_SPINLOCK(release_lock); 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci#endif /* CONFIG_USB_SERIAL_MOS7715_PARPORT */ 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_cistatic const unsigned int dummy; /* for clarity in register access fns */ 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_cienum mos_regs { 10862306a36Sopenharmony_ci MOS7720_THR, /* serial port regs */ 10962306a36Sopenharmony_ci MOS7720_RHR, 11062306a36Sopenharmony_ci MOS7720_IER, 11162306a36Sopenharmony_ci MOS7720_FCR, 11262306a36Sopenharmony_ci MOS7720_ISR, 11362306a36Sopenharmony_ci MOS7720_LCR, 11462306a36Sopenharmony_ci MOS7720_MCR, 11562306a36Sopenharmony_ci MOS7720_LSR, 11662306a36Sopenharmony_ci MOS7720_MSR, 11762306a36Sopenharmony_ci MOS7720_SPR, 11862306a36Sopenharmony_ci MOS7720_DLL, 11962306a36Sopenharmony_ci MOS7720_DLM, 12062306a36Sopenharmony_ci MOS7720_DPR, /* parallel port regs */ 12162306a36Sopenharmony_ci MOS7720_DSR, 12262306a36Sopenharmony_ci MOS7720_DCR, 12362306a36Sopenharmony_ci MOS7720_ECR, 12462306a36Sopenharmony_ci MOS7720_SP1_REG, /* device control regs */ 12562306a36Sopenharmony_ci MOS7720_SP2_REG, /* serial port 2 (7720 only) */ 12662306a36Sopenharmony_ci MOS7720_PP_REG, 12762306a36Sopenharmony_ci MOS7720_SP_CONTROL_REG, 12862306a36Sopenharmony_ci}; 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci/* 13162306a36Sopenharmony_ci * Return the correct value for the Windex field of the setup packet 13262306a36Sopenharmony_ci * for a control endpoint message. See the 7715 datasheet. 13362306a36Sopenharmony_ci */ 13462306a36Sopenharmony_cistatic inline __u16 get_reg_index(enum mos_regs reg) 13562306a36Sopenharmony_ci{ 13662306a36Sopenharmony_ci static const __u16 mos7715_index_lookup_table[] = { 13762306a36Sopenharmony_ci 0x00, /* MOS7720_THR */ 13862306a36Sopenharmony_ci 0x00, /* MOS7720_RHR */ 13962306a36Sopenharmony_ci 0x01, /* MOS7720_IER */ 14062306a36Sopenharmony_ci 0x02, /* MOS7720_FCR */ 14162306a36Sopenharmony_ci 0x02, /* MOS7720_ISR */ 14262306a36Sopenharmony_ci 0x03, /* MOS7720_LCR */ 14362306a36Sopenharmony_ci 0x04, /* MOS7720_MCR */ 14462306a36Sopenharmony_ci 0x05, /* MOS7720_LSR */ 14562306a36Sopenharmony_ci 0x06, /* MOS7720_MSR */ 14662306a36Sopenharmony_ci 0x07, /* MOS7720_SPR */ 14762306a36Sopenharmony_ci 0x00, /* MOS7720_DLL */ 14862306a36Sopenharmony_ci 0x01, /* MOS7720_DLM */ 14962306a36Sopenharmony_ci 0x00, /* MOS7720_DPR */ 15062306a36Sopenharmony_ci 0x01, /* MOS7720_DSR */ 15162306a36Sopenharmony_ci 0x02, /* MOS7720_DCR */ 15262306a36Sopenharmony_ci 0x0a, /* MOS7720_ECR */ 15362306a36Sopenharmony_ci 0x01, /* MOS7720_SP1_REG */ 15462306a36Sopenharmony_ci 0x02, /* MOS7720_SP2_REG (7720 only) */ 15562306a36Sopenharmony_ci 0x04, /* MOS7720_PP_REG (7715 only) */ 15662306a36Sopenharmony_ci 0x08, /* MOS7720_SP_CONTROL_REG */ 15762306a36Sopenharmony_ci }; 15862306a36Sopenharmony_ci return mos7715_index_lookup_table[reg]; 15962306a36Sopenharmony_ci} 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci/* 16262306a36Sopenharmony_ci * Return the correct value for the upper byte of the Wvalue field of 16362306a36Sopenharmony_ci * the setup packet for a control endpoint message. 16462306a36Sopenharmony_ci */ 16562306a36Sopenharmony_cistatic inline __u16 get_reg_value(enum mos_regs reg, 16662306a36Sopenharmony_ci unsigned int serial_portnum) 16762306a36Sopenharmony_ci{ 16862306a36Sopenharmony_ci if (reg >= MOS7720_SP1_REG) /* control reg */ 16962306a36Sopenharmony_ci return 0x0000; 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ci else if (reg >= MOS7720_DPR) /* parallel port reg (7715 only) */ 17262306a36Sopenharmony_ci return 0x0100; 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci else /* serial port reg */ 17562306a36Sopenharmony_ci return (serial_portnum + 2) << 8; 17662306a36Sopenharmony_ci} 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci/* 17962306a36Sopenharmony_ci * Write data byte to the specified device register. The data is embedded in 18062306a36Sopenharmony_ci * the value field of the setup packet. serial_portnum is ignored for registers 18162306a36Sopenharmony_ci * not specific to a particular serial port. 18262306a36Sopenharmony_ci */ 18362306a36Sopenharmony_cistatic int write_mos_reg(struct usb_serial *serial, unsigned int serial_portnum, 18462306a36Sopenharmony_ci enum mos_regs reg, __u8 data) 18562306a36Sopenharmony_ci{ 18662306a36Sopenharmony_ci struct usb_device *usbdev = serial->dev; 18762306a36Sopenharmony_ci unsigned int pipe = usb_sndctrlpipe(usbdev, 0); 18862306a36Sopenharmony_ci __u8 request = (__u8)0x0e; 18962306a36Sopenharmony_ci __u8 requesttype = (__u8)0x40; 19062306a36Sopenharmony_ci __u16 index = get_reg_index(reg); 19162306a36Sopenharmony_ci __u16 value = get_reg_value(reg, serial_portnum) + data; 19262306a36Sopenharmony_ci int status = usb_control_msg(usbdev, pipe, request, requesttype, value, 19362306a36Sopenharmony_ci index, NULL, 0, MOS_WDR_TIMEOUT); 19462306a36Sopenharmony_ci if (status < 0) 19562306a36Sopenharmony_ci dev_err(&usbdev->dev, 19662306a36Sopenharmony_ci "mos7720: usb_control_msg() failed: %d\n", status); 19762306a36Sopenharmony_ci return status; 19862306a36Sopenharmony_ci} 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci/* 20162306a36Sopenharmony_ci * Read data byte from the specified device register. The data returned by the 20262306a36Sopenharmony_ci * device is embedded in the value field of the setup packet. serial_portnum is 20362306a36Sopenharmony_ci * ignored for registers that are not specific to a particular serial port. 20462306a36Sopenharmony_ci */ 20562306a36Sopenharmony_cistatic int read_mos_reg(struct usb_serial *serial, unsigned int serial_portnum, 20662306a36Sopenharmony_ci enum mos_regs reg, __u8 *data) 20762306a36Sopenharmony_ci{ 20862306a36Sopenharmony_ci struct usb_device *usbdev = serial->dev; 20962306a36Sopenharmony_ci unsigned int pipe = usb_rcvctrlpipe(usbdev, 0); 21062306a36Sopenharmony_ci __u8 request = (__u8)0x0d; 21162306a36Sopenharmony_ci __u8 requesttype = (__u8)0xc0; 21262306a36Sopenharmony_ci __u16 index = get_reg_index(reg); 21362306a36Sopenharmony_ci __u16 value = get_reg_value(reg, serial_portnum); 21462306a36Sopenharmony_ci u8 *buf; 21562306a36Sopenharmony_ci int status; 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci buf = kmalloc(1, GFP_KERNEL); 21862306a36Sopenharmony_ci if (!buf) { 21962306a36Sopenharmony_ci *data = 0; 22062306a36Sopenharmony_ci return -ENOMEM; 22162306a36Sopenharmony_ci } 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ci status = usb_control_msg(usbdev, pipe, request, requesttype, value, 22462306a36Sopenharmony_ci index, buf, 1, MOS_WDR_TIMEOUT); 22562306a36Sopenharmony_ci if (status == 1) { 22662306a36Sopenharmony_ci *data = *buf; 22762306a36Sopenharmony_ci } else { 22862306a36Sopenharmony_ci dev_err(&usbdev->dev, 22962306a36Sopenharmony_ci "mos7720: usb_control_msg() failed: %d\n", status); 23062306a36Sopenharmony_ci if (status >= 0) 23162306a36Sopenharmony_ci status = -EIO; 23262306a36Sopenharmony_ci *data = 0; 23362306a36Sopenharmony_ci } 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_ci kfree(buf); 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_ci return status; 23862306a36Sopenharmony_ci} 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_ci#ifdef CONFIG_USB_SERIAL_MOS7715_PARPORT 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_cistatic inline int mos7715_change_mode(struct mos7715_parport *mos_parport, 24362306a36Sopenharmony_ci enum mos7715_pp_modes mode) 24462306a36Sopenharmony_ci{ 24562306a36Sopenharmony_ci mos_parport->shadowECR = mode; 24662306a36Sopenharmony_ci write_mos_reg(mos_parport->serial, dummy, MOS7720_ECR, 24762306a36Sopenharmony_ci mos_parport->shadowECR); 24862306a36Sopenharmony_ci return 0; 24962306a36Sopenharmony_ci} 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_cistatic void destroy_mos_parport(struct kref *kref) 25262306a36Sopenharmony_ci{ 25362306a36Sopenharmony_ci struct mos7715_parport *mos_parport = 25462306a36Sopenharmony_ci container_of(kref, struct mos7715_parport, ref_count); 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_ci kfree(mos_parport); 25762306a36Sopenharmony_ci} 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_ci/* 26062306a36Sopenharmony_ci * This is the common top part of all parallel port callback operations that 26162306a36Sopenharmony_ci * send synchronous messages to the device. This implements convoluted locking 26262306a36Sopenharmony_ci * that avoids two scenarios: (1) a port operation is called after usbserial 26362306a36Sopenharmony_ci * has called our release function, at which point struct mos7715_parport has 26462306a36Sopenharmony_ci * been destroyed, and (2) the device has been disconnected, but usbserial has 26562306a36Sopenharmony_ci * not called the release function yet because someone has a serial port open. 26662306a36Sopenharmony_ci * The shared release_lock prevents the first, and the mutex and disconnected 26762306a36Sopenharmony_ci * flag maintained by usbserial covers the second. We also use the msg_pending 26862306a36Sopenharmony_ci * flag to ensure that all synchronous usb message calls have completed before 26962306a36Sopenharmony_ci * our release function can return. 27062306a36Sopenharmony_ci */ 27162306a36Sopenharmony_cistatic int parport_prologue(struct parport *pp) 27262306a36Sopenharmony_ci{ 27362306a36Sopenharmony_ci struct mos7715_parport *mos_parport; 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_ci spin_lock(&release_lock); 27662306a36Sopenharmony_ci mos_parport = pp->private_data; 27762306a36Sopenharmony_ci if (unlikely(mos_parport == NULL)) { 27862306a36Sopenharmony_ci /* release fn called, port struct destroyed */ 27962306a36Sopenharmony_ci spin_unlock(&release_lock); 28062306a36Sopenharmony_ci return -1; 28162306a36Sopenharmony_ci } 28262306a36Sopenharmony_ci mos_parport->msg_pending = true; /* synch usb call pending */ 28362306a36Sopenharmony_ci reinit_completion(&mos_parport->syncmsg_compl); 28462306a36Sopenharmony_ci spin_unlock(&release_lock); 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_ci /* ensure writes from restore are submitted before new requests */ 28762306a36Sopenharmony_ci if (work_pending(&mos_parport->work)) 28862306a36Sopenharmony_ci flush_work(&mos_parport->work); 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_ci mutex_lock(&mos_parport->serial->disc_mutex); 29162306a36Sopenharmony_ci if (mos_parport->serial->disconnected) { 29262306a36Sopenharmony_ci /* device disconnected */ 29362306a36Sopenharmony_ci mutex_unlock(&mos_parport->serial->disc_mutex); 29462306a36Sopenharmony_ci mos_parport->msg_pending = false; 29562306a36Sopenharmony_ci complete(&mos_parport->syncmsg_compl); 29662306a36Sopenharmony_ci return -1; 29762306a36Sopenharmony_ci } 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_ci return 0; 30062306a36Sopenharmony_ci} 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_ci/* 30362306a36Sopenharmony_ci * This is the common bottom part of all parallel port functions that send 30462306a36Sopenharmony_ci * synchronous messages to the device. 30562306a36Sopenharmony_ci */ 30662306a36Sopenharmony_cistatic inline void parport_epilogue(struct parport *pp) 30762306a36Sopenharmony_ci{ 30862306a36Sopenharmony_ci struct mos7715_parport *mos_parport = pp->private_data; 30962306a36Sopenharmony_ci mutex_unlock(&mos_parport->serial->disc_mutex); 31062306a36Sopenharmony_ci mos_parport->msg_pending = false; 31162306a36Sopenharmony_ci complete(&mos_parport->syncmsg_compl); 31262306a36Sopenharmony_ci} 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_cistatic void deferred_restore_writes(struct work_struct *work) 31562306a36Sopenharmony_ci{ 31662306a36Sopenharmony_ci struct mos7715_parport *mos_parport; 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_ci mos_parport = container_of(work, struct mos7715_parport, work); 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_ci mutex_lock(&mos_parport->serial->disc_mutex); 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_ci /* if device disconnected, game over */ 32362306a36Sopenharmony_ci if (mos_parport->serial->disconnected) 32462306a36Sopenharmony_ci goto done; 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_ci write_mos_reg(mos_parport->serial, dummy, MOS7720_DCR, 32762306a36Sopenharmony_ci mos_parport->shadowDCR); 32862306a36Sopenharmony_ci write_mos_reg(mos_parport->serial, dummy, MOS7720_ECR, 32962306a36Sopenharmony_ci mos_parport->shadowECR); 33062306a36Sopenharmony_cidone: 33162306a36Sopenharmony_ci mutex_unlock(&mos_parport->serial->disc_mutex); 33262306a36Sopenharmony_ci} 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_cistatic void parport_mos7715_write_data(struct parport *pp, unsigned char d) 33562306a36Sopenharmony_ci{ 33662306a36Sopenharmony_ci struct mos7715_parport *mos_parport = pp->private_data; 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_ci if (parport_prologue(pp) < 0) 33962306a36Sopenharmony_ci return; 34062306a36Sopenharmony_ci mos7715_change_mode(mos_parport, SPP); 34162306a36Sopenharmony_ci write_mos_reg(mos_parport->serial, dummy, MOS7720_DPR, (__u8)d); 34262306a36Sopenharmony_ci parport_epilogue(pp); 34362306a36Sopenharmony_ci} 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_cistatic unsigned char parport_mos7715_read_data(struct parport *pp) 34662306a36Sopenharmony_ci{ 34762306a36Sopenharmony_ci struct mos7715_parport *mos_parport = pp->private_data; 34862306a36Sopenharmony_ci unsigned char d; 34962306a36Sopenharmony_ci 35062306a36Sopenharmony_ci if (parport_prologue(pp) < 0) 35162306a36Sopenharmony_ci return 0; 35262306a36Sopenharmony_ci read_mos_reg(mos_parport->serial, dummy, MOS7720_DPR, &d); 35362306a36Sopenharmony_ci parport_epilogue(pp); 35462306a36Sopenharmony_ci return d; 35562306a36Sopenharmony_ci} 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_cistatic void parport_mos7715_write_control(struct parport *pp, unsigned char d) 35862306a36Sopenharmony_ci{ 35962306a36Sopenharmony_ci struct mos7715_parport *mos_parport = pp->private_data; 36062306a36Sopenharmony_ci __u8 data; 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_ci if (parport_prologue(pp) < 0) 36362306a36Sopenharmony_ci return; 36462306a36Sopenharmony_ci data = ((__u8)d & 0x0f) | (mos_parport->shadowDCR & 0xf0); 36562306a36Sopenharmony_ci write_mos_reg(mos_parport->serial, dummy, MOS7720_DCR, data); 36662306a36Sopenharmony_ci mos_parport->shadowDCR = data; 36762306a36Sopenharmony_ci parport_epilogue(pp); 36862306a36Sopenharmony_ci} 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_cistatic unsigned char parport_mos7715_read_control(struct parport *pp) 37162306a36Sopenharmony_ci{ 37262306a36Sopenharmony_ci struct mos7715_parport *mos_parport; 37362306a36Sopenharmony_ci __u8 dcr; 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_ci spin_lock(&release_lock); 37662306a36Sopenharmony_ci mos_parport = pp->private_data; 37762306a36Sopenharmony_ci if (unlikely(mos_parport == NULL)) { 37862306a36Sopenharmony_ci spin_unlock(&release_lock); 37962306a36Sopenharmony_ci return 0; 38062306a36Sopenharmony_ci } 38162306a36Sopenharmony_ci dcr = mos_parport->shadowDCR & 0x0f; 38262306a36Sopenharmony_ci spin_unlock(&release_lock); 38362306a36Sopenharmony_ci return dcr; 38462306a36Sopenharmony_ci} 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_cistatic unsigned char parport_mos7715_frob_control(struct parport *pp, 38762306a36Sopenharmony_ci unsigned char mask, 38862306a36Sopenharmony_ci unsigned char val) 38962306a36Sopenharmony_ci{ 39062306a36Sopenharmony_ci struct mos7715_parport *mos_parport = pp->private_data; 39162306a36Sopenharmony_ci __u8 dcr; 39262306a36Sopenharmony_ci 39362306a36Sopenharmony_ci mask &= 0x0f; 39462306a36Sopenharmony_ci val &= 0x0f; 39562306a36Sopenharmony_ci if (parport_prologue(pp) < 0) 39662306a36Sopenharmony_ci return 0; 39762306a36Sopenharmony_ci mos_parport->shadowDCR = (mos_parport->shadowDCR & (~mask)) ^ val; 39862306a36Sopenharmony_ci write_mos_reg(mos_parport->serial, dummy, MOS7720_DCR, 39962306a36Sopenharmony_ci mos_parport->shadowDCR); 40062306a36Sopenharmony_ci dcr = mos_parport->shadowDCR & 0x0f; 40162306a36Sopenharmony_ci parport_epilogue(pp); 40262306a36Sopenharmony_ci return dcr; 40362306a36Sopenharmony_ci} 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_cistatic unsigned char parport_mos7715_read_status(struct parport *pp) 40662306a36Sopenharmony_ci{ 40762306a36Sopenharmony_ci unsigned char status; 40862306a36Sopenharmony_ci struct mos7715_parport *mos_parport; 40962306a36Sopenharmony_ci 41062306a36Sopenharmony_ci spin_lock(&release_lock); 41162306a36Sopenharmony_ci mos_parport = pp->private_data; 41262306a36Sopenharmony_ci if (unlikely(mos_parport == NULL)) { /* release called */ 41362306a36Sopenharmony_ci spin_unlock(&release_lock); 41462306a36Sopenharmony_ci return 0; 41562306a36Sopenharmony_ci } 41662306a36Sopenharmony_ci status = atomic_read(&mos_parport->shadowDSR) & 0xf8; 41762306a36Sopenharmony_ci spin_unlock(&release_lock); 41862306a36Sopenharmony_ci return status; 41962306a36Sopenharmony_ci} 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_cistatic void parport_mos7715_enable_irq(struct parport *pp) 42262306a36Sopenharmony_ci{ 42362306a36Sopenharmony_ci} 42462306a36Sopenharmony_ci 42562306a36Sopenharmony_cistatic void parport_mos7715_disable_irq(struct parport *pp) 42662306a36Sopenharmony_ci{ 42762306a36Sopenharmony_ci} 42862306a36Sopenharmony_ci 42962306a36Sopenharmony_cistatic void parport_mos7715_data_forward(struct parport *pp) 43062306a36Sopenharmony_ci{ 43162306a36Sopenharmony_ci struct mos7715_parport *mos_parport = pp->private_data; 43262306a36Sopenharmony_ci 43362306a36Sopenharmony_ci if (parport_prologue(pp) < 0) 43462306a36Sopenharmony_ci return; 43562306a36Sopenharmony_ci mos7715_change_mode(mos_parport, PS2); 43662306a36Sopenharmony_ci mos_parport->shadowDCR &= ~0x20; 43762306a36Sopenharmony_ci write_mos_reg(mos_parport->serial, dummy, MOS7720_DCR, 43862306a36Sopenharmony_ci mos_parport->shadowDCR); 43962306a36Sopenharmony_ci parport_epilogue(pp); 44062306a36Sopenharmony_ci} 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_cistatic void parport_mos7715_data_reverse(struct parport *pp) 44362306a36Sopenharmony_ci{ 44462306a36Sopenharmony_ci struct mos7715_parport *mos_parport = pp->private_data; 44562306a36Sopenharmony_ci 44662306a36Sopenharmony_ci if (parport_prologue(pp) < 0) 44762306a36Sopenharmony_ci return; 44862306a36Sopenharmony_ci mos7715_change_mode(mos_parport, PS2); 44962306a36Sopenharmony_ci mos_parport->shadowDCR |= 0x20; 45062306a36Sopenharmony_ci write_mos_reg(mos_parport->serial, dummy, MOS7720_DCR, 45162306a36Sopenharmony_ci mos_parport->shadowDCR); 45262306a36Sopenharmony_ci parport_epilogue(pp); 45362306a36Sopenharmony_ci} 45462306a36Sopenharmony_ci 45562306a36Sopenharmony_cistatic void parport_mos7715_init_state(struct pardevice *dev, 45662306a36Sopenharmony_ci struct parport_state *s) 45762306a36Sopenharmony_ci{ 45862306a36Sopenharmony_ci s->u.pc.ctr = DCR_INIT_VAL; 45962306a36Sopenharmony_ci s->u.pc.ecr = ECR_INIT_VAL; 46062306a36Sopenharmony_ci} 46162306a36Sopenharmony_ci 46262306a36Sopenharmony_ci/* N.B. Parport core code requires that this function not block */ 46362306a36Sopenharmony_cistatic void parport_mos7715_save_state(struct parport *pp, 46462306a36Sopenharmony_ci struct parport_state *s) 46562306a36Sopenharmony_ci{ 46662306a36Sopenharmony_ci struct mos7715_parport *mos_parport; 46762306a36Sopenharmony_ci 46862306a36Sopenharmony_ci spin_lock(&release_lock); 46962306a36Sopenharmony_ci mos_parport = pp->private_data; 47062306a36Sopenharmony_ci if (unlikely(mos_parport == NULL)) { /* release called */ 47162306a36Sopenharmony_ci spin_unlock(&release_lock); 47262306a36Sopenharmony_ci return; 47362306a36Sopenharmony_ci } 47462306a36Sopenharmony_ci s->u.pc.ctr = mos_parport->shadowDCR; 47562306a36Sopenharmony_ci s->u.pc.ecr = mos_parport->shadowECR; 47662306a36Sopenharmony_ci spin_unlock(&release_lock); 47762306a36Sopenharmony_ci} 47862306a36Sopenharmony_ci 47962306a36Sopenharmony_ci/* N.B. Parport core code requires that this function not block */ 48062306a36Sopenharmony_cistatic void parport_mos7715_restore_state(struct parport *pp, 48162306a36Sopenharmony_ci struct parport_state *s) 48262306a36Sopenharmony_ci{ 48362306a36Sopenharmony_ci struct mos7715_parport *mos_parport; 48462306a36Sopenharmony_ci 48562306a36Sopenharmony_ci spin_lock(&release_lock); 48662306a36Sopenharmony_ci mos_parport = pp->private_data; 48762306a36Sopenharmony_ci if (unlikely(mos_parport == NULL)) { /* release called */ 48862306a36Sopenharmony_ci spin_unlock(&release_lock); 48962306a36Sopenharmony_ci return; 49062306a36Sopenharmony_ci } 49162306a36Sopenharmony_ci mos_parport->shadowDCR = s->u.pc.ctr; 49262306a36Sopenharmony_ci mos_parport->shadowECR = s->u.pc.ecr; 49362306a36Sopenharmony_ci 49462306a36Sopenharmony_ci schedule_work(&mos_parport->work); 49562306a36Sopenharmony_ci spin_unlock(&release_lock); 49662306a36Sopenharmony_ci} 49762306a36Sopenharmony_ci 49862306a36Sopenharmony_cistatic size_t parport_mos7715_write_compat(struct parport *pp, 49962306a36Sopenharmony_ci const void *buffer, 50062306a36Sopenharmony_ci size_t len, int flags) 50162306a36Sopenharmony_ci{ 50262306a36Sopenharmony_ci int retval; 50362306a36Sopenharmony_ci struct mos7715_parport *mos_parport = pp->private_data; 50462306a36Sopenharmony_ci int actual_len; 50562306a36Sopenharmony_ci 50662306a36Sopenharmony_ci if (parport_prologue(pp) < 0) 50762306a36Sopenharmony_ci return 0; 50862306a36Sopenharmony_ci mos7715_change_mode(mos_parport, PPF); 50962306a36Sopenharmony_ci retval = usb_bulk_msg(mos_parport->serial->dev, 51062306a36Sopenharmony_ci usb_sndbulkpipe(mos_parport->serial->dev, 2), 51162306a36Sopenharmony_ci (void *)buffer, len, &actual_len, 51262306a36Sopenharmony_ci MOS_WDR_TIMEOUT); 51362306a36Sopenharmony_ci parport_epilogue(pp); 51462306a36Sopenharmony_ci if (retval) { 51562306a36Sopenharmony_ci dev_err(&mos_parport->serial->dev->dev, 51662306a36Sopenharmony_ci "mos7720: usb_bulk_msg() failed: %d\n", retval); 51762306a36Sopenharmony_ci return 0; 51862306a36Sopenharmony_ci } 51962306a36Sopenharmony_ci return actual_len; 52062306a36Sopenharmony_ci} 52162306a36Sopenharmony_ci 52262306a36Sopenharmony_cistatic struct parport_operations parport_mos7715_ops = { 52362306a36Sopenharmony_ci .owner = THIS_MODULE, 52462306a36Sopenharmony_ci .write_data = parport_mos7715_write_data, 52562306a36Sopenharmony_ci .read_data = parport_mos7715_read_data, 52662306a36Sopenharmony_ci 52762306a36Sopenharmony_ci .write_control = parport_mos7715_write_control, 52862306a36Sopenharmony_ci .read_control = parport_mos7715_read_control, 52962306a36Sopenharmony_ci .frob_control = parport_mos7715_frob_control, 53062306a36Sopenharmony_ci 53162306a36Sopenharmony_ci .read_status = parport_mos7715_read_status, 53262306a36Sopenharmony_ci 53362306a36Sopenharmony_ci .enable_irq = parport_mos7715_enable_irq, 53462306a36Sopenharmony_ci .disable_irq = parport_mos7715_disable_irq, 53562306a36Sopenharmony_ci 53662306a36Sopenharmony_ci .data_forward = parport_mos7715_data_forward, 53762306a36Sopenharmony_ci .data_reverse = parport_mos7715_data_reverse, 53862306a36Sopenharmony_ci 53962306a36Sopenharmony_ci .init_state = parport_mos7715_init_state, 54062306a36Sopenharmony_ci .save_state = parport_mos7715_save_state, 54162306a36Sopenharmony_ci .restore_state = parport_mos7715_restore_state, 54262306a36Sopenharmony_ci 54362306a36Sopenharmony_ci .compat_write_data = parport_mos7715_write_compat, 54462306a36Sopenharmony_ci 54562306a36Sopenharmony_ci .nibble_read_data = parport_ieee1284_read_nibble, 54662306a36Sopenharmony_ci .byte_read_data = parport_ieee1284_read_byte, 54762306a36Sopenharmony_ci}; 54862306a36Sopenharmony_ci 54962306a36Sopenharmony_ci/* 55062306a36Sopenharmony_ci * Allocate and initialize parallel port control struct, initialize 55162306a36Sopenharmony_ci * the parallel port hardware device, and register with the parport subsystem. 55262306a36Sopenharmony_ci */ 55362306a36Sopenharmony_cistatic int mos7715_parport_init(struct usb_serial *serial) 55462306a36Sopenharmony_ci{ 55562306a36Sopenharmony_ci struct mos7715_parport *mos_parport; 55662306a36Sopenharmony_ci 55762306a36Sopenharmony_ci /* allocate and initialize parallel port control struct */ 55862306a36Sopenharmony_ci mos_parport = kzalloc(sizeof(struct mos7715_parport), GFP_KERNEL); 55962306a36Sopenharmony_ci if (!mos_parport) 56062306a36Sopenharmony_ci return -ENOMEM; 56162306a36Sopenharmony_ci 56262306a36Sopenharmony_ci mos_parport->msg_pending = false; 56362306a36Sopenharmony_ci kref_init(&mos_parport->ref_count); 56462306a36Sopenharmony_ci usb_set_serial_data(serial, mos_parport); /* hijack private pointer */ 56562306a36Sopenharmony_ci mos_parport->serial = serial; 56662306a36Sopenharmony_ci INIT_WORK(&mos_parport->work, deferred_restore_writes); 56762306a36Sopenharmony_ci init_completion(&mos_parport->syncmsg_compl); 56862306a36Sopenharmony_ci 56962306a36Sopenharmony_ci /* cycle parallel port reset bit */ 57062306a36Sopenharmony_ci write_mos_reg(mos_parport->serial, dummy, MOS7720_PP_REG, (__u8)0x80); 57162306a36Sopenharmony_ci write_mos_reg(mos_parport->serial, dummy, MOS7720_PP_REG, (__u8)0x00); 57262306a36Sopenharmony_ci 57362306a36Sopenharmony_ci /* initialize device registers */ 57462306a36Sopenharmony_ci mos_parport->shadowDCR = DCR_INIT_VAL; 57562306a36Sopenharmony_ci write_mos_reg(mos_parport->serial, dummy, MOS7720_DCR, 57662306a36Sopenharmony_ci mos_parport->shadowDCR); 57762306a36Sopenharmony_ci mos_parport->shadowECR = ECR_INIT_VAL; 57862306a36Sopenharmony_ci write_mos_reg(mos_parport->serial, dummy, MOS7720_ECR, 57962306a36Sopenharmony_ci mos_parport->shadowECR); 58062306a36Sopenharmony_ci 58162306a36Sopenharmony_ci /* register with parport core */ 58262306a36Sopenharmony_ci mos_parport->pp = parport_register_port(0, PARPORT_IRQ_NONE, 58362306a36Sopenharmony_ci PARPORT_DMA_NONE, 58462306a36Sopenharmony_ci &parport_mos7715_ops); 58562306a36Sopenharmony_ci if (mos_parport->pp == NULL) { 58662306a36Sopenharmony_ci dev_err(&serial->interface->dev, 58762306a36Sopenharmony_ci "Could not register parport\n"); 58862306a36Sopenharmony_ci kref_put(&mos_parport->ref_count, destroy_mos_parport); 58962306a36Sopenharmony_ci return -EIO; 59062306a36Sopenharmony_ci } 59162306a36Sopenharmony_ci mos_parport->pp->private_data = mos_parport; 59262306a36Sopenharmony_ci mos_parport->pp->modes = PARPORT_MODE_COMPAT | PARPORT_MODE_PCSPP; 59362306a36Sopenharmony_ci mos_parport->pp->dev = &serial->interface->dev; 59462306a36Sopenharmony_ci parport_announce_port(mos_parport->pp); 59562306a36Sopenharmony_ci 59662306a36Sopenharmony_ci return 0; 59762306a36Sopenharmony_ci} 59862306a36Sopenharmony_ci#endif /* CONFIG_USB_SERIAL_MOS7715_PARPORT */ 59962306a36Sopenharmony_ci 60062306a36Sopenharmony_ci/* 60162306a36Sopenharmony_ci * mos7720_interrupt_callback 60262306a36Sopenharmony_ci * this is the callback function for when we have received data on the 60362306a36Sopenharmony_ci * interrupt endpoint. 60462306a36Sopenharmony_ci */ 60562306a36Sopenharmony_cistatic void mos7720_interrupt_callback(struct urb *urb) 60662306a36Sopenharmony_ci{ 60762306a36Sopenharmony_ci int result; 60862306a36Sopenharmony_ci int length; 60962306a36Sopenharmony_ci int status = urb->status; 61062306a36Sopenharmony_ci struct device *dev = &urb->dev->dev; 61162306a36Sopenharmony_ci __u8 *data; 61262306a36Sopenharmony_ci __u8 sp1; 61362306a36Sopenharmony_ci __u8 sp2; 61462306a36Sopenharmony_ci 61562306a36Sopenharmony_ci switch (status) { 61662306a36Sopenharmony_ci case 0: 61762306a36Sopenharmony_ci /* success */ 61862306a36Sopenharmony_ci break; 61962306a36Sopenharmony_ci case -ECONNRESET: 62062306a36Sopenharmony_ci case -ENOENT: 62162306a36Sopenharmony_ci case -ESHUTDOWN: 62262306a36Sopenharmony_ci /* this urb is terminated, clean up */ 62362306a36Sopenharmony_ci dev_dbg(dev, "%s - urb shutting down with status: %d\n", __func__, status); 62462306a36Sopenharmony_ci return; 62562306a36Sopenharmony_ci default: 62662306a36Sopenharmony_ci dev_dbg(dev, "%s - nonzero urb status received: %d\n", __func__, status); 62762306a36Sopenharmony_ci goto exit; 62862306a36Sopenharmony_ci } 62962306a36Sopenharmony_ci 63062306a36Sopenharmony_ci length = urb->actual_length; 63162306a36Sopenharmony_ci data = urb->transfer_buffer; 63262306a36Sopenharmony_ci 63362306a36Sopenharmony_ci /* Moschip get 4 bytes 63462306a36Sopenharmony_ci * Byte 1 IIR Port 1 (port.number is 0) 63562306a36Sopenharmony_ci * Byte 2 IIR Port 2 (port.number is 1) 63662306a36Sopenharmony_ci * Byte 3 -------------- 63762306a36Sopenharmony_ci * Byte 4 FIFO status for both */ 63862306a36Sopenharmony_ci 63962306a36Sopenharmony_ci /* the above description is inverted 64062306a36Sopenharmony_ci * oneukum 2007-03-14 */ 64162306a36Sopenharmony_ci 64262306a36Sopenharmony_ci if (unlikely(length != 4)) { 64362306a36Sopenharmony_ci dev_dbg(dev, "Wrong data !!!\n"); 64462306a36Sopenharmony_ci return; 64562306a36Sopenharmony_ci } 64662306a36Sopenharmony_ci 64762306a36Sopenharmony_ci sp1 = data[3]; 64862306a36Sopenharmony_ci sp2 = data[2]; 64962306a36Sopenharmony_ci 65062306a36Sopenharmony_ci if ((sp1 | sp2) & 0x01) { 65162306a36Sopenharmony_ci /* No Interrupt Pending in both the ports */ 65262306a36Sopenharmony_ci dev_dbg(dev, "No Interrupt !!!\n"); 65362306a36Sopenharmony_ci } else { 65462306a36Sopenharmony_ci switch (sp1 & 0x0f) { 65562306a36Sopenharmony_ci case SERIAL_IIR_RLS: 65662306a36Sopenharmony_ci dev_dbg(dev, "Serial Port 1: Receiver status error or address bit detected in 9-bit mode\n"); 65762306a36Sopenharmony_ci break; 65862306a36Sopenharmony_ci case SERIAL_IIR_CTI: 65962306a36Sopenharmony_ci dev_dbg(dev, "Serial Port 1: Receiver time out\n"); 66062306a36Sopenharmony_ci break; 66162306a36Sopenharmony_ci case SERIAL_IIR_MS: 66262306a36Sopenharmony_ci /* dev_dbg(dev, "Serial Port 1: Modem status change\n"); */ 66362306a36Sopenharmony_ci break; 66462306a36Sopenharmony_ci } 66562306a36Sopenharmony_ci 66662306a36Sopenharmony_ci switch (sp2 & 0x0f) { 66762306a36Sopenharmony_ci case SERIAL_IIR_RLS: 66862306a36Sopenharmony_ci dev_dbg(dev, "Serial Port 2: Receiver status error or address bit detected in 9-bit mode\n"); 66962306a36Sopenharmony_ci break; 67062306a36Sopenharmony_ci case SERIAL_IIR_CTI: 67162306a36Sopenharmony_ci dev_dbg(dev, "Serial Port 2: Receiver time out\n"); 67262306a36Sopenharmony_ci break; 67362306a36Sopenharmony_ci case SERIAL_IIR_MS: 67462306a36Sopenharmony_ci /* dev_dbg(dev, "Serial Port 2: Modem status change\n"); */ 67562306a36Sopenharmony_ci break; 67662306a36Sopenharmony_ci } 67762306a36Sopenharmony_ci } 67862306a36Sopenharmony_ci 67962306a36Sopenharmony_ciexit: 68062306a36Sopenharmony_ci result = usb_submit_urb(urb, GFP_ATOMIC); 68162306a36Sopenharmony_ci if (result) 68262306a36Sopenharmony_ci dev_err(dev, "%s - Error %d submitting control urb\n", __func__, result); 68362306a36Sopenharmony_ci} 68462306a36Sopenharmony_ci 68562306a36Sopenharmony_ci/* 68662306a36Sopenharmony_ci * mos7715_interrupt_callback 68762306a36Sopenharmony_ci * this is the 7715's callback function for when we have received data on 68862306a36Sopenharmony_ci * the interrupt endpoint. 68962306a36Sopenharmony_ci */ 69062306a36Sopenharmony_cistatic void mos7715_interrupt_callback(struct urb *urb) 69162306a36Sopenharmony_ci{ 69262306a36Sopenharmony_ci int result; 69362306a36Sopenharmony_ci int length; 69462306a36Sopenharmony_ci int status = urb->status; 69562306a36Sopenharmony_ci struct device *dev = &urb->dev->dev; 69662306a36Sopenharmony_ci __u8 *data; 69762306a36Sopenharmony_ci __u8 iir; 69862306a36Sopenharmony_ci 69962306a36Sopenharmony_ci switch (status) { 70062306a36Sopenharmony_ci case 0: 70162306a36Sopenharmony_ci /* success */ 70262306a36Sopenharmony_ci break; 70362306a36Sopenharmony_ci case -ECONNRESET: 70462306a36Sopenharmony_ci case -ENOENT: 70562306a36Sopenharmony_ci case -ESHUTDOWN: 70662306a36Sopenharmony_ci case -ENODEV: 70762306a36Sopenharmony_ci /* this urb is terminated, clean up */ 70862306a36Sopenharmony_ci dev_dbg(dev, "%s - urb shutting down with status: %d\n", __func__, status); 70962306a36Sopenharmony_ci return; 71062306a36Sopenharmony_ci default: 71162306a36Sopenharmony_ci dev_dbg(dev, "%s - nonzero urb status received: %d\n", __func__, status); 71262306a36Sopenharmony_ci goto exit; 71362306a36Sopenharmony_ci } 71462306a36Sopenharmony_ci 71562306a36Sopenharmony_ci length = urb->actual_length; 71662306a36Sopenharmony_ci data = urb->transfer_buffer; 71762306a36Sopenharmony_ci 71862306a36Sopenharmony_ci /* Structure of data from 7715 device: 71962306a36Sopenharmony_ci * Byte 1: IIR serial Port 72062306a36Sopenharmony_ci * Byte 2: unused 72162306a36Sopenharmony_ci * Byte 2: DSR parallel port 72262306a36Sopenharmony_ci * Byte 4: FIFO status for both */ 72362306a36Sopenharmony_ci 72462306a36Sopenharmony_ci if (unlikely(length != 4)) { 72562306a36Sopenharmony_ci dev_dbg(dev, "Wrong data !!!\n"); 72662306a36Sopenharmony_ci return; 72762306a36Sopenharmony_ci } 72862306a36Sopenharmony_ci 72962306a36Sopenharmony_ci iir = data[0]; 73062306a36Sopenharmony_ci if (!(iir & 0x01)) { /* serial port interrupt pending */ 73162306a36Sopenharmony_ci switch (iir & 0x0f) { 73262306a36Sopenharmony_ci case SERIAL_IIR_RLS: 73362306a36Sopenharmony_ci dev_dbg(dev, "Serial Port: Receiver status error or address bit detected in 9-bit mode\n"); 73462306a36Sopenharmony_ci break; 73562306a36Sopenharmony_ci case SERIAL_IIR_CTI: 73662306a36Sopenharmony_ci dev_dbg(dev, "Serial Port: Receiver time out\n"); 73762306a36Sopenharmony_ci break; 73862306a36Sopenharmony_ci case SERIAL_IIR_MS: 73962306a36Sopenharmony_ci /* dev_dbg(dev, "Serial Port: Modem status change\n"); */ 74062306a36Sopenharmony_ci break; 74162306a36Sopenharmony_ci } 74262306a36Sopenharmony_ci } 74362306a36Sopenharmony_ci 74462306a36Sopenharmony_ci#ifdef CONFIG_USB_SERIAL_MOS7715_PARPORT 74562306a36Sopenharmony_ci { /* update local copy of DSR reg */ 74662306a36Sopenharmony_ci struct usb_serial_port *port = urb->context; 74762306a36Sopenharmony_ci struct mos7715_parport *mos_parport = port->serial->private; 74862306a36Sopenharmony_ci if (unlikely(mos_parport == NULL)) 74962306a36Sopenharmony_ci return; 75062306a36Sopenharmony_ci atomic_set(&mos_parport->shadowDSR, data[2]); 75162306a36Sopenharmony_ci } 75262306a36Sopenharmony_ci#endif 75362306a36Sopenharmony_ci 75462306a36Sopenharmony_ciexit: 75562306a36Sopenharmony_ci result = usb_submit_urb(urb, GFP_ATOMIC); 75662306a36Sopenharmony_ci if (result) 75762306a36Sopenharmony_ci dev_err(dev, "%s - Error %d submitting control urb\n", __func__, result); 75862306a36Sopenharmony_ci} 75962306a36Sopenharmony_ci 76062306a36Sopenharmony_ci/* 76162306a36Sopenharmony_ci * mos7720_bulk_in_callback 76262306a36Sopenharmony_ci * this is the callback function for when we have received data on the 76362306a36Sopenharmony_ci * bulk in endpoint. 76462306a36Sopenharmony_ci */ 76562306a36Sopenharmony_cistatic void mos7720_bulk_in_callback(struct urb *urb) 76662306a36Sopenharmony_ci{ 76762306a36Sopenharmony_ci int retval; 76862306a36Sopenharmony_ci unsigned char *data ; 76962306a36Sopenharmony_ci struct usb_serial_port *port; 77062306a36Sopenharmony_ci int status = urb->status; 77162306a36Sopenharmony_ci 77262306a36Sopenharmony_ci if (status) { 77362306a36Sopenharmony_ci dev_dbg(&urb->dev->dev, "nonzero read bulk status received: %d\n", status); 77462306a36Sopenharmony_ci return; 77562306a36Sopenharmony_ci } 77662306a36Sopenharmony_ci 77762306a36Sopenharmony_ci port = urb->context; 77862306a36Sopenharmony_ci 77962306a36Sopenharmony_ci dev_dbg(&port->dev, "Entering...%s\n", __func__); 78062306a36Sopenharmony_ci 78162306a36Sopenharmony_ci data = urb->transfer_buffer; 78262306a36Sopenharmony_ci 78362306a36Sopenharmony_ci if (urb->actual_length) { 78462306a36Sopenharmony_ci tty_insert_flip_string(&port->port, data, urb->actual_length); 78562306a36Sopenharmony_ci tty_flip_buffer_push(&port->port); 78662306a36Sopenharmony_ci } 78762306a36Sopenharmony_ci 78862306a36Sopenharmony_ci if (port->read_urb->status != -EINPROGRESS) { 78962306a36Sopenharmony_ci retval = usb_submit_urb(port->read_urb, GFP_ATOMIC); 79062306a36Sopenharmony_ci if (retval) 79162306a36Sopenharmony_ci dev_dbg(&port->dev, "usb_submit_urb(read bulk) failed, retval = %d\n", retval); 79262306a36Sopenharmony_ci } 79362306a36Sopenharmony_ci} 79462306a36Sopenharmony_ci 79562306a36Sopenharmony_ci/* 79662306a36Sopenharmony_ci * mos7720_bulk_out_data_callback 79762306a36Sopenharmony_ci * this is the callback function for when we have finished sending serial 79862306a36Sopenharmony_ci * data on the bulk out endpoint. 79962306a36Sopenharmony_ci */ 80062306a36Sopenharmony_cistatic void mos7720_bulk_out_data_callback(struct urb *urb) 80162306a36Sopenharmony_ci{ 80262306a36Sopenharmony_ci struct moschip_port *mos7720_port; 80362306a36Sopenharmony_ci int status = urb->status; 80462306a36Sopenharmony_ci 80562306a36Sopenharmony_ci if (status) { 80662306a36Sopenharmony_ci dev_dbg(&urb->dev->dev, "nonzero write bulk status received:%d\n", status); 80762306a36Sopenharmony_ci return; 80862306a36Sopenharmony_ci } 80962306a36Sopenharmony_ci 81062306a36Sopenharmony_ci mos7720_port = urb->context; 81162306a36Sopenharmony_ci if (!mos7720_port) { 81262306a36Sopenharmony_ci dev_dbg(&urb->dev->dev, "NULL mos7720_port pointer\n"); 81362306a36Sopenharmony_ci return ; 81462306a36Sopenharmony_ci } 81562306a36Sopenharmony_ci 81662306a36Sopenharmony_ci if (mos7720_port->open) 81762306a36Sopenharmony_ci tty_port_tty_wakeup(&mos7720_port->port->port); 81862306a36Sopenharmony_ci} 81962306a36Sopenharmony_ci 82062306a36Sopenharmony_cistatic int mos77xx_calc_num_ports(struct usb_serial *serial, 82162306a36Sopenharmony_ci struct usb_serial_endpoints *epds) 82262306a36Sopenharmony_ci{ 82362306a36Sopenharmony_ci u16 product = le16_to_cpu(serial->dev->descriptor.idProduct); 82462306a36Sopenharmony_ci 82562306a36Sopenharmony_ci if (product == MOSCHIP_DEVICE_ID_7715) { 82662306a36Sopenharmony_ci /* 82762306a36Sopenharmony_ci * The 7715 uses the first bulk in/out endpoint pair for the 82862306a36Sopenharmony_ci * parallel port, and the second for the serial port. We swap 82962306a36Sopenharmony_ci * the endpoint descriptors here so that the first and 83062306a36Sopenharmony_ci * only registered port structure uses the serial-port 83162306a36Sopenharmony_ci * endpoints. 83262306a36Sopenharmony_ci */ 83362306a36Sopenharmony_ci swap(epds->bulk_in[0], epds->bulk_in[1]); 83462306a36Sopenharmony_ci swap(epds->bulk_out[0], epds->bulk_out[1]); 83562306a36Sopenharmony_ci 83662306a36Sopenharmony_ci return 1; 83762306a36Sopenharmony_ci } 83862306a36Sopenharmony_ci 83962306a36Sopenharmony_ci return 2; 84062306a36Sopenharmony_ci} 84162306a36Sopenharmony_ci 84262306a36Sopenharmony_cistatic int mos7720_open(struct tty_struct *tty, struct usb_serial_port *port) 84362306a36Sopenharmony_ci{ 84462306a36Sopenharmony_ci struct usb_serial *serial; 84562306a36Sopenharmony_ci struct urb *urb; 84662306a36Sopenharmony_ci struct moschip_port *mos7720_port; 84762306a36Sopenharmony_ci int response; 84862306a36Sopenharmony_ci int port_number; 84962306a36Sopenharmony_ci __u8 data; 85062306a36Sopenharmony_ci int allocated_urbs = 0; 85162306a36Sopenharmony_ci int j; 85262306a36Sopenharmony_ci 85362306a36Sopenharmony_ci serial = port->serial; 85462306a36Sopenharmony_ci 85562306a36Sopenharmony_ci mos7720_port = usb_get_serial_port_data(port); 85662306a36Sopenharmony_ci if (mos7720_port == NULL) 85762306a36Sopenharmony_ci return -ENODEV; 85862306a36Sopenharmony_ci 85962306a36Sopenharmony_ci usb_clear_halt(serial->dev, port->write_urb->pipe); 86062306a36Sopenharmony_ci usb_clear_halt(serial->dev, port->read_urb->pipe); 86162306a36Sopenharmony_ci 86262306a36Sopenharmony_ci /* Initialising the write urb pool */ 86362306a36Sopenharmony_ci for (j = 0; j < NUM_URBS; ++j) { 86462306a36Sopenharmony_ci urb = usb_alloc_urb(0, GFP_KERNEL); 86562306a36Sopenharmony_ci mos7720_port->write_urb_pool[j] = urb; 86662306a36Sopenharmony_ci if (!urb) 86762306a36Sopenharmony_ci continue; 86862306a36Sopenharmony_ci 86962306a36Sopenharmony_ci urb->transfer_buffer = kmalloc(URB_TRANSFER_BUFFER_SIZE, 87062306a36Sopenharmony_ci GFP_KERNEL); 87162306a36Sopenharmony_ci if (!urb->transfer_buffer) { 87262306a36Sopenharmony_ci usb_free_urb(mos7720_port->write_urb_pool[j]); 87362306a36Sopenharmony_ci mos7720_port->write_urb_pool[j] = NULL; 87462306a36Sopenharmony_ci continue; 87562306a36Sopenharmony_ci } 87662306a36Sopenharmony_ci allocated_urbs++; 87762306a36Sopenharmony_ci } 87862306a36Sopenharmony_ci 87962306a36Sopenharmony_ci if (!allocated_urbs) 88062306a36Sopenharmony_ci return -ENOMEM; 88162306a36Sopenharmony_ci 88262306a36Sopenharmony_ci /* Initialize MCS7720 -- Write Init values to corresponding Registers 88362306a36Sopenharmony_ci * 88462306a36Sopenharmony_ci * Register Index 88562306a36Sopenharmony_ci * 0 : MOS7720_THR/MOS7720_RHR 88662306a36Sopenharmony_ci * 1 : MOS7720_IER 88762306a36Sopenharmony_ci * 2 : MOS7720_FCR 88862306a36Sopenharmony_ci * 3 : MOS7720_LCR 88962306a36Sopenharmony_ci * 4 : MOS7720_MCR 89062306a36Sopenharmony_ci * 5 : MOS7720_LSR 89162306a36Sopenharmony_ci * 6 : MOS7720_MSR 89262306a36Sopenharmony_ci * 7 : MOS7720_SPR 89362306a36Sopenharmony_ci * 89462306a36Sopenharmony_ci * 0x08 : SP1/2 Control Reg 89562306a36Sopenharmony_ci */ 89662306a36Sopenharmony_ci port_number = port->port_number; 89762306a36Sopenharmony_ci read_mos_reg(serial, port_number, MOS7720_LSR, &data); 89862306a36Sopenharmony_ci 89962306a36Sopenharmony_ci dev_dbg(&port->dev, "SS::%p LSR:%x\n", mos7720_port, data); 90062306a36Sopenharmony_ci 90162306a36Sopenharmony_ci write_mos_reg(serial, dummy, MOS7720_SP1_REG, 0x02); 90262306a36Sopenharmony_ci write_mos_reg(serial, dummy, MOS7720_SP2_REG, 0x02); 90362306a36Sopenharmony_ci 90462306a36Sopenharmony_ci write_mos_reg(serial, port_number, MOS7720_IER, 0x00); 90562306a36Sopenharmony_ci write_mos_reg(serial, port_number, MOS7720_FCR, 0x00); 90662306a36Sopenharmony_ci 90762306a36Sopenharmony_ci write_mos_reg(serial, port_number, MOS7720_FCR, 0xcf); 90862306a36Sopenharmony_ci mos7720_port->shadowLCR = 0x03; 90962306a36Sopenharmony_ci write_mos_reg(serial, port_number, MOS7720_LCR, 91062306a36Sopenharmony_ci mos7720_port->shadowLCR); 91162306a36Sopenharmony_ci mos7720_port->shadowMCR = 0x0b; 91262306a36Sopenharmony_ci write_mos_reg(serial, port_number, MOS7720_MCR, 91362306a36Sopenharmony_ci mos7720_port->shadowMCR); 91462306a36Sopenharmony_ci 91562306a36Sopenharmony_ci write_mos_reg(serial, port_number, MOS7720_SP_CONTROL_REG, 0x00); 91662306a36Sopenharmony_ci read_mos_reg(serial, dummy, MOS7720_SP_CONTROL_REG, &data); 91762306a36Sopenharmony_ci data = data | (port->port_number + 1); 91862306a36Sopenharmony_ci write_mos_reg(serial, dummy, MOS7720_SP_CONTROL_REG, data); 91962306a36Sopenharmony_ci mos7720_port->shadowLCR = 0x83; 92062306a36Sopenharmony_ci write_mos_reg(serial, port_number, MOS7720_LCR, 92162306a36Sopenharmony_ci mos7720_port->shadowLCR); 92262306a36Sopenharmony_ci write_mos_reg(serial, port_number, MOS7720_THR, 0x0c); 92362306a36Sopenharmony_ci write_mos_reg(serial, port_number, MOS7720_IER, 0x00); 92462306a36Sopenharmony_ci mos7720_port->shadowLCR = 0x03; 92562306a36Sopenharmony_ci write_mos_reg(serial, port_number, MOS7720_LCR, 92662306a36Sopenharmony_ci mos7720_port->shadowLCR); 92762306a36Sopenharmony_ci write_mos_reg(serial, port_number, MOS7720_IER, 0x0c); 92862306a36Sopenharmony_ci 92962306a36Sopenharmony_ci response = usb_submit_urb(port->read_urb, GFP_KERNEL); 93062306a36Sopenharmony_ci if (response) 93162306a36Sopenharmony_ci dev_err(&port->dev, "%s - Error %d submitting read urb\n", 93262306a36Sopenharmony_ci __func__, response); 93362306a36Sopenharmony_ci 93462306a36Sopenharmony_ci /* initialize our port settings */ 93562306a36Sopenharmony_ci mos7720_port->shadowMCR = UART_MCR_OUT2; /* Must set to enable ints! */ 93662306a36Sopenharmony_ci 93762306a36Sopenharmony_ci /* send a open port command */ 93862306a36Sopenharmony_ci mos7720_port->open = 1; 93962306a36Sopenharmony_ci 94062306a36Sopenharmony_ci return 0; 94162306a36Sopenharmony_ci} 94262306a36Sopenharmony_ci 94362306a36Sopenharmony_ci/* 94462306a36Sopenharmony_ci * mos7720_chars_in_buffer 94562306a36Sopenharmony_ci * this function is called by the tty driver when it wants to know how many 94662306a36Sopenharmony_ci * bytes of data we currently have outstanding in the port (data that has 94762306a36Sopenharmony_ci * been written, but hasn't made it out the port yet) 94862306a36Sopenharmony_ci */ 94962306a36Sopenharmony_cistatic unsigned int mos7720_chars_in_buffer(struct tty_struct *tty) 95062306a36Sopenharmony_ci{ 95162306a36Sopenharmony_ci struct usb_serial_port *port = tty->driver_data; 95262306a36Sopenharmony_ci struct moschip_port *mos7720_port = usb_get_serial_port_data(port); 95362306a36Sopenharmony_ci int i; 95462306a36Sopenharmony_ci unsigned int chars = 0; 95562306a36Sopenharmony_ci 95662306a36Sopenharmony_ci for (i = 0; i < NUM_URBS; ++i) { 95762306a36Sopenharmony_ci if (mos7720_port->write_urb_pool[i] && 95862306a36Sopenharmony_ci mos7720_port->write_urb_pool[i]->status == -EINPROGRESS) 95962306a36Sopenharmony_ci chars += URB_TRANSFER_BUFFER_SIZE; 96062306a36Sopenharmony_ci } 96162306a36Sopenharmony_ci dev_dbg(&port->dev, "%s - returns %u\n", __func__, chars); 96262306a36Sopenharmony_ci return chars; 96362306a36Sopenharmony_ci} 96462306a36Sopenharmony_ci 96562306a36Sopenharmony_cistatic void mos7720_close(struct usb_serial_port *port) 96662306a36Sopenharmony_ci{ 96762306a36Sopenharmony_ci struct usb_serial *serial; 96862306a36Sopenharmony_ci struct moschip_port *mos7720_port; 96962306a36Sopenharmony_ci int j; 97062306a36Sopenharmony_ci 97162306a36Sopenharmony_ci serial = port->serial; 97262306a36Sopenharmony_ci 97362306a36Sopenharmony_ci mos7720_port = usb_get_serial_port_data(port); 97462306a36Sopenharmony_ci if (mos7720_port == NULL) 97562306a36Sopenharmony_ci return; 97662306a36Sopenharmony_ci 97762306a36Sopenharmony_ci for (j = 0; j < NUM_URBS; ++j) 97862306a36Sopenharmony_ci usb_kill_urb(mos7720_port->write_urb_pool[j]); 97962306a36Sopenharmony_ci 98062306a36Sopenharmony_ci /* Freeing Write URBs */ 98162306a36Sopenharmony_ci for (j = 0; j < NUM_URBS; ++j) { 98262306a36Sopenharmony_ci if (mos7720_port->write_urb_pool[j]) { 98362306a36Sopenharmony_ci kfree(mos7720_port->write_urb_pool[j]->transfer_buffer); 98462306a36Sopenharmony_ci usb_free_urb(mos7720_port->write_urb_pool[j]); 98562306a36Sopenharmony_ci } 98662306a36Sopenharmony_ci } 98762306a36Sopenharmony_ci 98862306a36Sopenharmony_ci /* While closing port, shutdown all bulk read, write * 98962306a36Sopenharmony_ci * and interrupt read if they exists, otherwise nop */ 99062306a36Sopenharmony_ci usb_kill_urb(port->write_urb); 99162306a36Sopenharmony_ci usb_kill_urb(port->read_urb); 99262306a36Sopenharmony_ci 99362306a36Sopenharmony_ci write_mos_reg(serial, port->port_number, MOS7720_MCR, 0x00); 99462306a36Sopenharmony_ci write_mos_reg(serial, port->port_number, MOS7720_IER, 0x00); 99562306a36Sopenharmony_ci 99662306a36Sopenharmony_ci mos7720_port->open = 0; 99762306a36Sopenharmony_ci} 99862306a36Sopenharmony_ci 99962306a36Sopenharmony_cistatic int mos7720_break(struct tty_struct *tty, int break_state) 100062306a36Sopenharmony_ci{ 100162306a36Sopenharmony_ci struct usb_serial_port *port = tty->driver_data; 100262306a36Sopenharmony_ci unsigned char data; 100362306a36Sopenharmony_ci struct usb_serial *serial; 100462306a36Sopenharmony_ci struct moschip_port *mos7720_port; 100562306a36Sopenharmony_ci 100662306a36Sopenharmony_ci serial = port->serial; 100762306a36Sopenharmony_ci 100862306a36Sopenharmony_ci mos7720_port = usb_get_serial_port_data(port); 100962306a36Sopenharmony_ci if (mos7720_port == NULL) 101062306a36Sopenharmony_ci return -ENODEV; 101162306a36Sopenharmony_ci 101262306a36Sopenharmony_ci if (break_state == -1) 101362306a36Sopenharmony_ci data = mos7720_port->shadowLCR | UART_LCR_SBC; 101462306a36Sopenharmony_ci else 101562306a36Sopenharmony_ci data = mos7720_port->shadowLCR & ~UART_LCR_SBC; 101662306a36Sopenharmony_ci 101762306a36Sopenharmony_ci mos7720_port->shadowLCR = data; 101862306a36Sopenharmony_ci 101962306a36Sopenharmony_ci return write_mos_reg(serial, port->port_number, MOS7720_LCR, 102062306a36Sopenharmony_ci mos7720_port->shadowLCR); 102162306a36Sopenharmony_ci} 102262306a36Sopenharmony_ci 102362306a36Sopenharmony_ci/* 102462306a36Sopenharmony_ci * mos7720_write_room 102562306a36Sopenharmony_ci * this function is called by the tty driver when it wants to know how many 102662306a36Sopenharmony_ci * bytes of data we can accept for a specific port. 102762306a36Sopenharmony_ci */ 102862306a36Sopenharmony_cistatic unsigned int mos7720_write_room(struct tty_struct *tty) 102962306a36Sopenharmony_ci{ 103062306a36Sopenharmony_ci struct usb_serial_port *port = tty->driver_data; 103162306a36Sopenharmony_ci struct moschip_port *mos7720_port = usb_get_serial_port_data(port); 103262306a36Sopenharmony_ci unsigned int room = 0; 103362306a36Sopenharmony_ci int i; 103462306a36Sopenharmony_ci 103562306a36Sopenharmony_ci /* FIXME: Locking */ 103662306a36Sopenharmony_ci for (i = 0; i < NUM_URBS; ++i) { 103762306a36Sopenharmony_ci if (mos7720_port->write_urb_pool[i] && 103862306a36Sopenharmony_ci mos7720_port->write_urb_pool[i]->status != -EINPROGRESS) 103962306a36Sopenharmony_ci room += URB_TRANSFER_BUFFER_SIZE; 104062306a36Sopenharmony_ci } 104162306a36Sopenharmony_ci 104262306a36Sopenharmony_ci dev_dbg(&port->dev, "%s - returns %u\n", __func__, room); 104362306a36Sopenharmony_ci return room; 104462306a36Sopenharmony_ci} 104562306a36Sopenharmony_ci 104662306a36Sopenharmony_cistatic int mos7720_write(struct tty_struct *tty, struct usb_serial_port *port, 104762306a36Sopenharmony_ci const unsigned char *data, int count) 104862306a36Sopenharmony_ci{ 104962306a36Sopenharmony_ci int status; 105062306a36Sopenharmony_ci int i; 105162306a36Sopenharmony_ci int bytes_sent = 0; 105262306a36Sopenharmony_ci int transfer_size; 105362306a36Sopenharmony_ci 105462306a36Sopenharmony_ci struct moschip_port *mos7720_port; 105562306a36Sopenharmony_ci struct usb_serial *serial; 105662306a36Sopenharmony_ci struct urb *urb; 105762306a36Sopenharmony_ci const unsigned char *current_position = data; 105862306a36Sopenharmony_ci 105962306a36Sopenharmony_ci serial = port->serial; 106062306a36Sopenharmony_ci 106162306a36Sopenharmony_ci mos7720_port = usb_get_serial_port_data(port); 106262306a36Sopenharmony_ci if (mos7720_port == NULL) 106362306a36Sopenharmony_ci return -ENODEV; 106462306a36Sopenharmony_ci 106562306a36Sopenharmony_ci /* try to find a free urb in the list */ 106662306a36Sopenharmony_ci urb = NULL; 106762306a36Sopenharmony_ci 106862306a36Sopenharmony_ci for (i = 0; i < NUM_URBS; ++i) { 106962306a36Sopenharmony_ci if (mos7720_port->write_urb_pool[i] && 107062306a36Sopenharmony_ci mos7720_port->write_urb_pool[i]->status != -EINPROGRESS) { 107162306a36Sopenharmony_ci urb = mos7720_port->write_urb_pool[i]; 107262306a36Sopenharmony_ci dev_dbg(&port->dev, "URB:%d\n", i); 107362306a36Sopenharmony_ci break; 107462306a36Sopenharmony_ci } 107562306a36Sopenharmony_ci } 107662306a36Sopenharmony_ci 107762306a36Sopenharmony_ci if (urb == NULL) { 107862306a36Sopenharmony_ci dev_dbg(&port->dev, "%s - no more free urbs\n", __func__); 107962306a36Sopenharmony_ci goto exit; 108062306a36Sopenharmony_ci } 108162306a36Sopenharmony_ci 108262306a36Sopenharmony_ci if (urb->transfer_buffer == NULL) { 108362306a36Sopenharmony_ci urb->transfer_buffer = kmalloc(URB_TRANSFER_BUFFER_SIZE, 108462306a36Sopenharmony_ci GFP_ATOMIC); 108562306a36Sopenharmony_ci if (!urb->transfer_buffer) { 108662306a36Sopenharmony_ci bytes_sent = -ENOMEM; 108762306a36Sopenharmony_ci goto exit; 108862306a36Sopenharmony_ci } 108962306a36Sopenharmony_ci } 109062306a36Sopenharmony_ci transfer_size = min(count, URB_TRANSFER_BUFFER_SIZE); 109162306a36Sopenharmony_ci 109262306a36Sopenharmony_ci memcpy(urb->transfer_buffer, current_position, transfer_size); 109362306a36Sopenharmony_ci usb_serial_debug_data(&port->dev, __func__, transfer_size, 109462306a36Sopenharmony_ci urb->transfer_buffer); 109562306a36Sopenharmony_ci 109662306a36Sopenharmony_ci /* fill urb with data and submit */ 109762306a36Sopenharmony_ci usb_fill_bulk_urb(urb, serial->dev, 109862306a36Sopenharmony_ci usb_sndbulkpipe(serial->dev, 109962306a36Sopenharmony_ci port->bulk_out_endpointAddress), 110062306a36Sopenharmony_ci urb->transfer_buffer, transfer_size, 110162306a36Sopenharmony_ci mos7720_bulk_out_data_callback, mos7720_port); 110262306a36Sopenharmony_ci 110362306a36Sopenharmony_ci /* send it down the pipe */ 110462306a36Sopenharmony_ci status = usb_submit_urb(urb, GFP_ATOMIC); 110562306a36Sopenharmony_ci if (status) { 110662306a36Sopenharmony_ci dev_err_console(port, "%s - usb_submit_urb(write bulk) failed " 110762306a36Sopenharmony_ci "with status = %d\n", __func__, status); 110862306a36Sopenharmony_ci bytes_sent = status; 110962306a36Sopenharmony_ci goto exit; 111062306a36Sopenharmony_ci } 111162306a36Sopenharmony_ci bytes_sent = transfer_size; 111262306a36Sopenharmony_ci 111362306a36Sopenharmony_ciexit: 111462306a36Sopenharmony_ci return bytes_sent; 111562306a36Sopenharmony_ci} 111662306a36Sopenharmony_ci 111762306a36Sopenharmony_cistatic void mos7720_throttle(struct tty_struct *tty) 111862306a36Sopenharmony_ci{ 111962306a36Sopenharmony_ci struct usb_serial_port *port = tty->driver_data; 112062306a36Sopenharmony_ci struct moschip_port *mos7720_port; 112162306a36Sopenharmony_ci int status; 112262306a36Sopenharmony_ci 112362306a36Sopenharmony_ci mos7720_port = usb_get_serial_port_data(port); 112462306a36Sopenharmony_ci 112562306a36Sopenharmony_ci if (mos7720_port == NULL) 112662306a36Sopenharmony_ci return; 112762306a36Sopenharmony_ci 112862306a36Sopenharmony_ci if (!mos7720_port->open) { 112962306a36Sopenharmony_ci dev_dbg(&port->dev, "%s - port not opened\n", __func__); 113062306a36Sopenharmony_ci return; 113162306a36Sopenharmony_ci } 113262306a36Sopenharmony_ci 113362306a36Sopenharmony_ci /* if we are implementing XON/XOFF, send the stop character */ 113462306a36Sopenharmony_ci if (I_IXOFF(tty)) { 113562306a36Sopenharmony_ci unsigned char stop_char = STOP_CHAR(tty); 113662306a36Sopenharmony_ci status = mos7720_write(tty, port, &stop_char, 1); 113762306a36Sopenharmony_ci if (status <= 0) 113862306a36Sopenharmony_ci return; 113962306a36Sopenharmony_ci } 114062306a36Sopenharmony_ci 114162306a36Sopenharmony_ci /* if we are implementing RTS/CTS, toggle that line */ 114262306a36Sopenharmony_ci if (C_CRTSCTS(tty)) { 114362306a36Sopenharmony_ci mos7720_port->shadowMCR &= ~UART_MCR_RTS; 114462306a36Sopenharmony_ci write_mos_reg(port->serial, port->port_number, MOS7720_MCR, 114562306a36Sopenharmony_ci mos7720_port->shadowMCR); 114662306a36Sopenharmony_ci } 114762306a36Sopenharmony_ci} 114862306a36Sopenharmony_ci 114962306a36Sopenharmony_cistatic void mos7720_unthrottle(struct tty_struct *tty) 115062306a36Sopenharmony_ci{ 115162306a36Sopenharmony_ci struct usb_serial_port *port = tty->driver_data; 115262306a36Sopenharmony_ci struct moschip_port *mos7720_port = usb_get_serial_port_data(port); 115362306a36Sopenharmony_ci int status; 115462306a36Sopenharmony_ci 115562306a36Sopenharmony_ci if (mos7720_port == NULL) 115662306a36Sopenharmony_ci return; 115762306a36Sopenharmony_ci 115862306a36Sopenharmony_ci if (!mos7720_port->open) { 115962306a36Sopenharmony_ci dev_dbg(&port->dev, "%s - port not opened\n", __func__); 116062306a36Sopenharmony_ci return; 116162306a36Sopenharmony_ci } 116262306a36Sopenharmony_ci 116362306a36Sopenharmony_ci /* if we are implementing XON/XOFF, send the start character */ 116462306a36Sopenharmony_ci if (I_IXOFF(tty)) { 116562306a36Sopenharmony_ci unsigned char start_char = START_CHAR(tty); 116662306a36Sopenharmony_ci status = mos7720_write(tty, port, &start_char, 1); 116762306a36Sopenharmony_ci if (status <= 0) 116862306a36Sopenharmony_ci return; 116962306a36Sopenharmony_ci } 117062306a36Sopenharmony_ci 117162306a36Sopenharmony_ci /* if we are implementing RTS/CTS, toggle that line */ 117262306a36Sopenharmony_ci if (C_CRTSCTS(tty)) { 117362306a36Sopenharmony_ci mos7720_port->shadowMCR |= UART_MCR_RTS; 117462306a36Sopenharmony_ci write_mos_reg(port->serial, port->port_number, MOS7720_MCR, 117562306a36Sopenharmony_ci mos7720_port->shadowMCR); 117662306a36Sopenharmony_ci } 117762306a36Sopenharmony_ci} 117862306a36Sopenharmony_ci 117962306a36Sopenharmony_ci/* FIXME: this function does not work */ 118062306a36Sopenharmony_cistatic int set_higher_rates(struct moschip_port *mos7720_port, 118162306a36Sopenharmony_ci unsigned int baud) 118262306a36Sopenharmony_ci{ 118362306a36Sopenharmony_ci struct usb_serial_port *port; 118462306a36Sopenharmony_ci struct usb_serial *serial; 118562306a36Sopenharmony_ci int port_number; 118662306a36Sopenharmony_ci enum mos_regs sp_reg; 118762306a36Sopenharmony_ci if (mos7720_port == NULL) 118862306a36Sopenharmony_ci return -EINVAL; 118962306a36Sopenharmony_ci 119062306a36Sopenharmony_ci port = mos7720_port->port; 119162306a36Sopenharmony_ci serial = port->serial; 119262306a36Sopenharmony_ci 119362306a36Sopenharmony_ci /*********************************************** 119462306a36Sopenharmony_ci * Init Sequence for higher rates 119562306a36Sopenharmony_ci ***********************************************/ 119662306a36Sopenharmony_ci dev_dbg(&port->dev, "Sending Setting Commands ..........\n"); 119762306a36Sopenharmony_ci port_number = port->port_number; 119862306a36Sopenharmony_ci 119962306a36Sopenharmony_ci write_mos_reg(serial, port_number, MOS7720_IER, 0x00); 120062306a36Sopenharmony_ci write_mos_reg(serial, port_number, MOS7720_FCR, 0x00); 120162306a36Sopenharmony_ci write_mos_reg(serial, port_number, MOS7720_FCR, 0xcf); 120262306a36Sopenharmony_ci mos7720_port->shadowMCR = 0x0b; 120362306a36Sopenharmony_ci write_mos_reg(serial, port_number, MOS7720_MCR, 120462306a36Sopenharmony_ci mos7720_port->shadowMCR); 120562306a36Sopenharmony_ci write_mos_reg(serial, dummy, MOS7720_SP_CONTROL_REG, 0x00); 120662306a36Sopenharmony_ci 120762306a36Sopenharmony_ci /*********************************************** 120862306a36Sopenharmony_ci * Set for higher rates * 120962306a36Sopenharmony_ci ***********************************************/ 121062306a36Sopenharmony_ci /* writing baud rate verbatum into uart clock field clearly not right */ 121162306a36Sopenharmony_ci if (port_number == 0) 121262306a36Sopenharmony_ci sp_reg = MOS7720_SP1_REG; 121362306a36Sopenharmony_ci else 121462306a36Sopenharmony_ci sp_reg = MOS7720_SP2_REG; 121562306a36Sopenharmony_ci write_mos_reg(serial, dummy, sp_reg, baud * 0x10); 121662306a36Sopenharmony_ci write_mos_reg(serial, dummy, MOS7720_SP_CONTROL_REG, 0x03); 121762306a36Sopenharmony_ci mos7720_port->shadowMCR = 0x2b; 121862306a36Sopenharmony_ci write_mos_reg(serial, port_number, MOS7720_MCR, 121962306a36Sopenharmony_ci mos7720_port->shadowMCR); 122062306a36Sopenharmony_ci 122162306a36Sopenharmony_ci /*********************************************** 122262306a36Sopenharmony_ci * Set DLL/DLM 122362306a36Sopenharmony_ci ***********************************************/ 122462306a36Sopenharmony_ci mos7720_port->shadowLCR = mos7720_port->shadowLCR | UART_LCR_DLAB; 122562306a36Sopenharmony_ci write_mos_reg(serial, port_number, MOS7720_LCR, 122662306a36Sopenharmony_ci mos7720_port->shadowLCR); 122762306a36Sopenharmony_ci write_mos_reg(serial, port_number, MOS7720_DLL, 0x01); 122862306a36Sopenharmony_ci write_mos_reg(serial, port_number, MOS7720_DLM, 0x00); 122962306a36Sopenharmony_ci mos7720_port->shadowLCR = mos7720_port->shadowLCR & ~UART_LCR_DLAB; 123062306a36Sopenharmony_ci write_mos_reg(serial, port_number, MOS7720_LCR, 123162306a36Sopenharmony_ci mos7720_port->shadowLCR); 123262306a36Sopenharmony_ci 123362306a36Sopenharmony_ci return 0; 123462306a36Sopenharmony_ci} 123562306a36Sopenharmony_ci 123662306a36Sopenharmony_ci/* baud rate information */ 123762306a36Sopenharmony_cistruct divisor_table_entry { 123862306a36Sopenharmony_ci __u32 baudrate; 123962306a36Sopenharmony_ci __u16 divisor; 124062306a36Sopenharmony_ci}; 124162306a36Sopenharmony_ci 124262306a36Sopenharmony_ci/* Define table of divisors for moschip 7720 hardware * 124362306a36Sopenharmony_ci * These assume a 3.6864MHz crystal, the standard /16, and * 124462306a36Sopenharmony_ci * MCR.7 = 0. */ 124562306a36Sopenharmony_cistatic const struct divisor_table_entry divisor_table[] = { 124662306a36Sopenharmony_ci { 50, 2304}, 124762306a36Sopenharmony_ci { 110, 1047}, /* 2094.545455 => 230450 => .0217 % over */ 124862306a36Sopenharmony_ci { 134, 857}, /* 1713.011152 => 230398.5 => .00065% under */ 124962306a36Sopenharmony_ci { 150, 768}, 125062306a36Sopenharmony_ci { 300, 384}, 125162306a36Sopenharmony_ci { 600, 192}, 125262306a36Sopenharmony_ci { 1200, 96}, 125362306a36Sopenharmony_ci { 1800, 64}, 125462306a36Sopenharmony_ci { 2400, 48}, 125562306a36Sopenharmony_ci { 4800, 24}, 125662306a36Sopenharmony_ci { 7200, 16}, 125762306a36Sopenharmony_ci { 9600, 12}, 125862306a36Sopenharmony_ci { 19200, 6}, 125962306a36Sopenharmony_ci { 38400, 3}, 126062306a36Sopenharmony_ci { 57600, 2}, 126162306a36Sopenharmony_ci { 115200, 1}, 126262306a36Sopenharmony_ci}; 126362306a36Sopenharmony_ci 126462306a36Sopenharmony_ci/***************************************************************************** 126562306a36Sopenharmony_ci * calc_baud_rate_divisor 126662306a36Sopenharmony_ci * this function calculates the proper baud rate divisor for the specified 126762306a36Sopenharmony_ci * baud rate. 126862306a36Sopenharmony_ci *****************************************************************************/ 126962306a36Sopenharmony_cistatic int calc_baud_rate_divisor(struct usb_serial_port *port, int baudrate, int *divisor) 127062306a36Sopenharmony_ci{ 127162306a36Sopenharmony_ci int i; 127262306a36Sopenharmony_ci __u16 custom; 127362306a36Sopenharmony_ci __u16 round1; 127462306a36Sopenharmony_ci __u16 round; 127562306a36Sopenharmony_ci 127662306a36Sopenharmony_ci 127762306a36Sopenharmony_ci dev_dbg(&port->dev, "%s - %d\n", __func__, baudrate); 127862306a36Sopenharmony_ci 127962306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(divisor_table); i++) { 128062306a36Sopenharmony_ci if (divisor_table[i].baudrate == baudrate) { 128162306a36Sopenharmony_ci *divisor = divisor_table[i].divisor; 128262306a36Sopenharmony_ci return 0; 128362306a36Sopenharmony_ci } 128462306a36Sopenharmony_ci } 128562306a36Sopenharmony_ci 128662306a36Sopenharmony_ci /* After trying for all the standard baud rates * 128762306a36Sopenharmony_ci * Try calculating the divisor for this baud rate */ 128862306a36Sopenharmony_ci if (baudrate > 75 && baudrate < 230400) { 128962306a36Sopenharmony_ci /* get the divisor */ 129062306a36Sopenharmony_ci custom = (__u16)(230400L / baudrate); 129162306a36Sopenharmony_ci 129262306a36Sopenharmony_ci /* Check for round off */ 129362306a36Sopenharmony_ci round1 = (__u16)(2304000L / baudrate); 129462306a36Sopenharmony_ci round = (__u16)(round1 - (custom * 10)); 129562306a36Sopenharmony_ci if (round > 4) 129662306a36Sopenharmony_ci custom++; 129762306a36Sopenharmony_ci *divisor = custom; 129862306a36Sopenharmony_ci 129962306a36Sopenharmony_ci dev_dbg(&port->dev, "Baud %d = %d\n", baudrate, custom); 130062306a36Sopenharmony_ci return 0; 130162306a36Sopenharmony_ci } 130262306a36Sopenharmony_ci 130362306a36Sopenharmony_ci dev_dbg(&port->dev, "Baud calculation Failed...\n"); 130462306a36Sopenharmony_ci return -EINVAL; 130562306a36Sopenharmony_ci} 130662306a36Sopenharmony_ci 130762306a36Sopenharmony_ci/* 130862306a36Sopenharmony_ci * send_cmd_write_baud_rate 130962306a36Sopenharmony_ci * this function sends the proper command to change the baud rate of the 131062306a36Sopenharmony_ci * specified port. 131162306a36Sopenharmony_ci */ 131262306a36Sopenharmony_cistatic int send_cmd_write_baud_rate(struct moschip_port *mos7720_port, 131362306a36Sopenharmony_ci int baudrate) 131462306a36Sopenharmony_ci{ 131562306a36Sopenharmony_ci struct usb_serial_port *port; 131662306a36Sopenharmony_ci struct usb_serial *serial; 131762306a36Sopenharmony_ci int divisor; 131862306a36Sopenharmony_ci int status; 131962306a36Sopenharmony_ci unsigned char number; 132062306a36Sopenharmony_ci 132162306a36Sopenharmony_ci if (mos7720_port == NULL) 132262306a36Sopenharmony_ci return -1; 132362306a36Sopenharmony_ci 132462306a36Sopenharmony_ci port = mos7720_port->port; 132562306a36Sopenharmony_ci serial = port->serial; 132662306a36Sopenharmony_ci 132762306a36Sopenharmony_ci number = port->port_number; 132862306a36Sopenharmony_ci dev_dbg(&port->dev, "%s - baud = %d\n", __func__, baudrate); 132962306a36Sopenharmony_ci 133062306a36Sopenharmony_ci /* Calculate the Divisor */ 133162306a36Sopenharmony_ci status = calc_baud_rate_divisor(port, baudrate, &divisor); 133262306a36Sopenharmony_ci if (status) { 133362306a36Sopenharmony_ci dev_err(&port->dev, "%s - bad baud rate\n", __func__); 133462306a36Sopenharmony_ci return status; 133562306a36Sopenharmony_ci } 133662306a36Sopenharmony_ci 133762306a36Sopenharmony_ci /* Enable access to divisor latch */ 133862306a36Sopenharmony_ci mos7720_port->shadowLCR = mos7720_port->shadowLCR | UART_LCR_DLAB; 133962306a36Sopenharmony_ci write_mos_reg(serial, number, MOS7720_LCR, mos7720_port->shadowLCR); 134062306a36Sopenharmony_ci 134162306a36Sopenharmony_ci /* Write the divisor */ 134262306a36Sopenharmony_ci write_mos_reg(serial, number, MOS7720_DLL, (__u8)(divisor & 0xff)); 134362306a36Sopenharmony_ci write_mos_reg(serial, number, MOS7720_DLM, 134462306a36Sopenharmony_ci (__u8)((divisor & 0xff00) >> 8)); 134562306a36Sopenharmony_ci 134662306a36Sopenharmony_ci /* Disable access to divisor latch */ 134762306a36Sopenharmony_ci mos7720_port->shadowLCR = mos7720_port->shadowLCR & ~UART_LCR_DLAB; 134862306a36Sopenharmony_ci write_mos_reg(serial, number, MOS7720_LCR, mos7720_port->shadowLCR); 134962306a36Sopenharmony_ci 135062306a36Sopenharmony_ci return status; 135162306a36Sopenharmony_ci} 135262306a36Sopenharmony_ci 135362306a36Sopenharmony_ci/* 135462306a36Sopenharmony_ci * change_port_settings 135562306a36Sopenharmony_ci * This routine is called to set the UART on the device to match 135662306a36Sopenharmony_ci * the specified new settings. 135762306a36Sopenharmony_ci */ 135862306a36Sopenharmony_cistatic void change_port_settings(struct tty_struct *tty, 135962306a36Sopenharmony_ci struct moschip_port *mos7720_port, 136062306a36Sopenharmony_ci const struct ktermios *old_termios) 136162306a36Sopenharmony_ci{ 136262306a36Sopenharmony_ci struct usb_serial_port *port; 136362306a36Sopenharmony_ci struct usb_serial *serial; 136462306a36Sopenharmony_ci int baud; 136562306a36Sopenharmony_ci unsigned cflag; 136662306a36Sopenharmony_ci __u8 lData; 136762306a36Sopenharmony_ci __u8 lParity; 136862306a36Sopenharmony_ci __u8 lStop; 136962306a36Sopenharmony_ci int status; 137062306a36Sopenharmony_ci int port_number; 137162306a36Sopenharmony_ci 137262306a36Sopenharmony_ci if (mos7720_port == NULL) 137362306a36Sopenharmony_ci return ; 137462306a36Sopenharmony_ci 137562306a36Sopenharmony_ci port = mos7720_port->port; 137662306a36Sopenharmony_ci serial = port->serial; 137762306a36Sopenharmony_ci port_number = port->port_number; 137862306a36Sopenharmony_ci 137962306a36Sopenharmony_ci if (!mos7720_port->open) { 138062306a36Sopenharmony_ci dev_dbg(&port->dev, "%s - port not opened\n", __func__); 138162306a36Sopenharmony_ci return; 138262306a36Sopenharmony_ci } 138362306a36Sopenharmony_ci 138462306a36Sopenharmony_ci lStop = 0x00; /* 1 stop bit */ 138562306a36Sopenharmony_ci lParity = 0x00; /* No parity */ 138662306a36Sopenharmony_ci 138762306a36Sopenharmony_ci cflag = tty->termios.c_cflag; 138862306a36Sopenharmony_ci 138962306a36Sopenharmony_ci lData = UART_LCR_WLEN(tty_get_char_size(cflag)); 139062306a36Sopenharmony_ci 139162306a36Sopenharmony_ci /* Change the Parity bit */ 139262306a36Sopenharmony_ci if (cflag & PARENB) { 139362306a36Sopenharmony_ci if (cflag & PARODD) { 139462306a36Sopenharmony_ci lParity = UART_LCR_PARITY; 139562306a36Sopenharmony_ci dev_dbg(&port->dev, "%s - parity = odd\n", __func__); 139662306a36Sopenharmony_ci } else { 139762306a36Sopenharmony_ci lParity = (UART_LCR_EPAR | UART_LCR_PARITY); 139862306a36Sopenharmony_ci dev_dbg(&port->dev, "%s - parity = even\n", __func__); 139962306a36Sopenharmony_ci } 140062306a36Sopenharmony_ci 140162306a36Sopenharmony_ci } else { 140262306a36Sopenharmony_ci dev_dbg(&port->dev, "%s - parity = none\n", __func__); 140362306a36Sopenharmony_ci } 140462306a36Sopenharmony_ci 140562306a36Sopenharmony_ci if (cflag & CMSPAR) 140662306a36Sopenharmony_ci lParity = lParity | 0x20; 140762306a36Sopenharmony_ci 140862306a36Sopenharmony_ci /* Change the Stop bit */ 140962306a36Sopenharmony_ci if (cflag & CSTOPB) { 141062306a36Sopenharmony_ci lStop = UART_LCR_STOP; 141162306a36Sopenharmony_ci dev_dbg(&port->dev, "%s - stop bits = 2\n", __func__); 141262306a36Sopenharmony_ci } else { 141362306a36Sopenharmony_ci lStop = 0x00; 141462306a36Sopenharmony_ci dev_dbg(&port->dev, "%s - stop bits = 1\n", __func__); 141562306a36Sopenharmony_ci } 141662306a36Sopenharmony_ci 141762306a36Sopenharmony_ci#define LCR_BITS_MASK 0x03 /* Mask for bits/char field */ 141862306a36Sopenharmony_ci#define LCR_STOP_MASK 0x04 /* Mask for stop bits field */ 141962306a36Sopenharmony_ci#define LCR_PAR_MASK 0x38 /* Mask for parity field */ 142062306a36Sopenharmony_ci 142162306a36Sopenharmony_ci /* Update the LCR with the correct value */ 142262306a36Sopenharmony_ci mos7720_port->shadowLCR &= 142362306a36Sopenharmony_ci ~(LCR_BITS_MASK | LCR_STOP_MASK | LCR_PAR_MASK); 142462306a36Sopenharmony_ci mos7720_port->shadowLCR |= (lData | lParity | lStop); 142562306a36Sopenharmony_ci 142662306a36Sopenharmony_ci 142762306a36Sopenharmony_ci /* Disable Interrupts */ 142862306a36Sopenharmony_ci write_mos_reg(serial, port_number, MOS7720_IER, 0x00); 142962306a36Sopenharmony_ci write_mos_reg(serial, port_number, MOS7720_FCR, 0x00); 143062306a36Sopenharmony_ci write_mos_reg(serial, port_number, MOS7720_FCR, 0xcf); 143162306a36Sopenharmony_ci 143262306a36Sopenharmony_ci /* Send the updated LCR value to the mos7720 */ 143362306a36Sopenharmony_ci write_mos_reg(serial, port_number, MOS7720_LCR, 143462306a36Sopenharmony_ci mos7720_port->shadowLCR); 143562306a36Sopenharmony_ci mos7720_port->shadowMCR = 0x0b; 143662306a36Sopenharmony_ci write_mos_reg(serial, port_number, MOS7720_MCR, 143762306a36Sopenharmony_ci mos7720_port->shadowMCR); 143862306a36Sopenharmony_ci 143962306a36Sopenharmony_ci /* set up the MCR register and send it to the mos7720 */ 144062306a36Sopenharmony_ci mos7720_port->shadowMCR = UART_MCR_OUT2; 144162306a36Sopenharmony_ci if (cflag & CBAUD) 144262306a36Sopenharmony_ci mos7720_port->shadowMCR |= (UART_MCR_DTR | UART_MCR_RTS); 144362306a36Sopenharmony_ci 144462306a36Sopenharmony_ci if (cflag & CRTSCTS) { 144562306a36Sopenharmony_ci mos7720_port->shadowMCR |= (UART_MCR_XONANY); 144662306a36Sopenharmony_ci /* To set hardware flow control to the specified * 144762306a36Sopenharmony_ci * serial port, in SP1/2_CONTROL_REG */ 144862306a36Sopenharmony_ci if (port_number) 144962306a36Sopenharmony_ci write_mos_reg(serial, dummy, MOS7720_SP_CONTROL_REG, 145062306a36Sopenharmony_ci 0x01); 145162306a36Sopenharmony_ci else 145262306a36Sopenharmony_ci write_mos_reg(serial, dummy, MOS7720_SP_CONTROL_REG, 145362306a36Sopenharmony_ci 0x02); 145462306a36Sopenharmony_ci 145562306a36Sopenharmony_ci } else 145662306a36Sopenharmony_ci mos7720_port->shadowMCR &= ~(UART_MCR_XONANY); 145762306a36Sopenharmony_ci 145862306a36Sopenharmony_ci write_mos_reg(serial, port_number, MOS7720_MCR, 145962306a36Sopenharmony_ci mos7720_port->shadowMCR); 146062306a36Sopenharmony_ci 146162306a36Sopenharmony_ci /* Determine divisor based on baud rate */ 146262306a36Sopenharmony_ci baud = tty_get_baud_rate(tty); 146362306a36Sopenharmony_ci if (!baud) { 146462306a36Sopenharmony_ci /* pick a default, any default... */ 146562306a36Sopenharmony_ci dev_dbg(&port->dev, "Picked default baud...\n"); 146662306a36Sopenharmony_ci baud = 9600; 146762306a36Sopenharmony_ci } 146862306a36Sopenharmony_ci 146962306a36Sopenharmony_ci if (baud >= 230400) { 147062306a36Sopenharmony_ci set_higher_rates(mos7720_port, baud); 147162306a36Sopenharmony_ci /* Enable Interrupts */ 147262306a36Sopenharmony_ci write_mos_reg(serial, port_number, MOS7720_IER, 0x0c); 147362306a36Sopenharmony_ci return; 147462306a36Sopenharmony_ci } 147562306a36Sopenharmony_ci 147662306a36Sopenharmony_ci dev_dbg(&port->dev, "%s - baud rate = %d\n", __func__, baud); 147762306a36Sopenharmony_ci status = send_cmd_write_baud_rate(mos7720_port, baud); 147862306a36Sopenharmony_ci /* FIXME: needs to write actual resulting baud back not just 147962306a36Sopenharmony_ci blindly do so */ 148062306a36Sopenharmony_ci if (cflag & CBAUD) 148162306a36Sopenharmony_ci tty_encode_baud_rate(tty, baud, baud); 148262306a36Sopenharmony_ci /* Enable Interrupts */ 148362306a36Sopenharmony_ci write_mos_reg(serial, port_number, MOS7720_IER, 0x0c); 148462306a36Sopenharmony_ci 148562306a36Sopenharmony_ci if (port->read_urb->status != -EINPROGRESS) { 148662306a36Sopenharmony_ci status = usb_submit_urb(port->read_urb, GFP_KERNEL); 148762306a36Sopenharmony_ci if (status) 148862306a36Sopenharmony_ci dev_dbg(&port->dev, "usb_submit_urb(read bulk) failed, status = %d\n", status); 148962306a36Sopenharmony_ci } 149062306a36Sopenharmony_ci} 149162306a36Sopenharmony_ci 149262306a36Sopenharmony_ci/* 149362306a36Sopenharmony_ci * mos7720_set_termios 149462306a36Sopenharmony_ci * this function is called by the tty driver when it wants to change the 149562306a36Sopenharmony_ci * termios structure. 149662306a36Sopenharmony_ci */ 149762306a36Sopenharmony_cistatic void mos7720_set_termios(struct tty_struct *tty, 149862306a36Sopenharmony_ci struct usb_serial_port *port, 149962306a36Sopenharmony_ci const struct ktermios *old_termios) 150062306a36Sopenharmony_ci{ 150162306a36Sopenharmony_ci int status; 150262306a36Sopenharmony_ci struct moschip_port *mos7720_port; 150362306a36Sopenharmony_ci 150462306a36Sopenharmony_ci mos7720_port = usb_get_serial_port_data(port); 150562306a36Sopenharmony_ci 150662306a36Sopenharmony_ci if (mos7720_port == NULL) 150762306a36Sopenharmony_ci return; 150862306a36Sopenharmony_ci 150962306a36Sopenharmony_ci if (!mos7720_port->open) { 151062306a36Sopenharmony_ci dev_dbg(&port->dev, "%s - port not opened\n", __func__); 151162306a36Sopenharmony_ci return; 151262306a36Sopenharmony_ci } 151362306a36Sopenharmony_ci 151462306a36Sopenharmony_ci /* change the port settings to the new ones specified */ 151562306a36Sopenharmony_ci change_port_settings(tty, mos7720_port, old_termios); 151662306a36Sopenharmony_ci 151762306a36Sopenharmony_ci if (port->read_urb->status != -EINPROGRESS) { 151862306a36Sopenharmony_ci status = usb_submit_urb(port->read_urb, GFP_KERNEL); 151962306a36Sopenharmony_ci if (status) 152062306a36Sopenharmony_ci dev_dbg(&port->dev, "usb_submit_urb(read bulk) failed, status = %d\n", status); 152162306a36Sopenharmony_ci } 152262306a36Sopenharmony_ci} 152362306a36Sopenharmony_ci 152462306a36Sopenharmony_ci/* 152562306a36Sopenharmony_ci * get_lsr_info - get line status register info 152662306a36Sopenharmony_ci * 152762306a36Sopenharmony_ci * Purpose: Let user call ioctl() to get info when the UART physically 152862306a36Sopenharmony_ci * is emptied. On bus types like RS485, the transmitter must 152962306a36Sopenharmony_ci * release the bus after transmitting. This must be done when 153062306a36Sopenharmony_ci * the transmit shift register is empty, not be done when the 153162306a36Sopenharmony_ci * transmit holding register is empty. This functionality 153262306a36Sopenharmony_ci * allows an RS485 driver to be written in user space. 153362306a36Sopenharmony_ci */ 153462306a36Sopenharmony_cistatic int get_lsr_info(struct tty_struct *tty, 153562306a36Sopenharmony_ci struct moschip_port *mos7720_port, unsigned int __user *value) 153662306a36Sopenharmony_ci{ 153762306a36Sopenharmony_ci struct usb_serial_port *port = tty->driver_data; 153862306a36Sopenharmony_ci unsigned int result = 0; 153962306a36Sopenharmony_ci unsigned char data = 0; 154062306a36Sopenharmony_ci int port_number = port->port_number; 154162306a36Sopenharmony_ci int count; 154262306a36Sopenharmony_ci 154362306a36Sopenharmony_ci count = mos7720_chars_in_buffer(tty); 154462306a36Sopenharmony_ci if (count == 0) { 154562306a36Sopenharmony_ci read_mos_reg(port->serial, port_number, MOS7720_LSR, &data); 154662306a36Sopenharmony_ci if ((data & (UART_LSR_TEMT | UART_LSR_THRE)) 154762306a36Sopenharmony_ci == (UART_LSR_TEMT | UART_LSR_THRE)) { 154862306a36Sopenharmony_ci dev_dbg(&port->dev, "%s -- Empty\n", __func__); 154962306a36Sopenharmony_ci result = TIOCSER_TEMT; 155062306a36Sopenharmony_ci } 155162306a36Sopenharmony_ci } 155262306a36Sopenharmony_ci if (copy_to_user(value, &result, sizeof(int))) 155362306a36Sopenharmony_ci return -EFAULT; 155462306a36Sopenharmony_ci return 0; 155562306a36Sopenharmony_ci} 155662306a36Sopenharmony_ci 155762306a36Sopenharmony_cistatic int mos7720_tiocmget(struct tty_struct *tty) 155862306a36Sopenharmony_ci{ 155962306a36Sopenharmony_ci struct usb_serial_port *port = tty->driver_data; 156062306a36Sopenharmony_ci struct moschip_port *mos7720_port = usb_get_serial_port_data(port); 156162306a36Sopenharmony_ci unsigned int result = 0; 156262306a36Sopenharmony_ci unsigned int mcr ; 156362306a36Sopenharmony_ci unsigned int msr ; 156462306a36Sopenharmony_ci 156562306a36Sopenharmony_ci mcr = mos7720_port->shadowMCR; 156662306a36Sopenharmony_ci msr = mos7720_port->shadowMSR; 156762306a36Sopenharmony_ci 156862306a36Sopenharmony_ci result = ((mcr & UART_MCR_DTR) ? TIOCM_DTR : 0) /* 0x002 */ 156962306a36Sopenharmony_ci | ((mcr & UART_MCR_RTS) ? TIOCM_RTS : 0) /* 0x004 */ 157062306a36Sopenharmony_ci | ((msr & UART_MSR_CTS) ? TIOCM_CTS : 0) /* 0x020 */ 157162306a36Sopenharmony_ci | ((msr & UART_MSR_DCD) ? TIOCM_CAR : 0) /* 0x040 */ 157262306a36Sopenharmony_ci | ((msr & UART_MSR_RI) ? TIOCM_RI : 0) /* 0x080 */ 157362306a36Sopenharmony_ci | ((msr & UART_MSR_DSR) ? TIOCM_DSR : 0); /* 0x100 */ 157462306a36Sopenharmony_ci 157562306a36Sopenharmony_ci return result; 157662306a36Sopenharmony_ci} 157762306a36Sopenharmony_ci 157862306a36Sopenharmony_cistatic int mos7720_tiocmset(struct tty_struct *tty, 157962306a36Sopenharmony_ci unsigned int set, unsigned int clear) 158062306a36Sopenharmony_ci{ 158162306a36Sopenharmony_ci struct usb_serial_port *port = tty->driver_data; 158262306a36Sopenharmony_ci struct moschip_port *mos7720_port = usb_get_serial_port_data(port); 158362306a36Sopenharmony_ci unsigned int mcr ; 158462306a36Sopenharmony_ci 158562306a36Sopenharmony_ci mcr = mos7720_port->shadowMCR; 158662306a36Sopenharmony_ci 158762306a36Sopenharmony_ci if (set & TIOCM_RTS) 158862306a36Sopenharmony_ci mcr |= UART_MCR_RTS; 158962306a36Sopenharmony_ci if (set & TIOCM_DTR) 159062306a36Sopenharmony_ci mcr |= UART_MCR_DTR; 159162306a36Sopenharmony_ci if (set & TIOCM_LOOP) 159262306a36Sopenharmony_ci mcr |= UART_MCR_LOOP; 159362306a36Sopenharmony_ci 159462306a36Sopenharmony_ci if (clear & TIOCM_RTS) 159562306a36Sopenharmony_ci mcr &= ~UART_MCR_RTS; 159662306a36Sopenharmony_ci if (clear & TIOCM_DTR) 159762306a36Sopenharmony_ci mcr &= ~UART_MCR_DTR; 159862306a36Sopenharmony_ci if (clear & TIOCM_LOOP) 159962306a36Sopenharmony_ci mcr &= ~UART_MCR_LOOP; 160062306a36Sopenharmony_ci 160162306a36Sopenharmony_ci mos7720_port->shadowMCR = mcr; 160262306a36Sopenharmony_ci write_mos_reg(port->serial, port->port_number, MOS7720_MCR, 160362306a36Sopenharmony_ci mos7720_port->shadowMCR); 160462306a36Sopenharmony_ci 160562306a36Sopenharmony_ci return 0; 160662306a36Sopenharmony_ci} 160762306a36Sopenharmony_ci 160862306a36Sopenharmony_cistatic int mos7720_ioctl(struct tty_struct *tty, 160962306a36Sopenharmony_ci unsigned int cmd, unsigned long arg) 161062306a36Sopenharmony_ci{ 161162306a36Sopenharmony_ci struct usb_serial_port *port = tty->driver_data; 161262306a36Sopenharmony_ci struct moschip_port *mos7720_port; 161362306a36Sopenharmony_ci 161462306a36Sopenharmony_ci mos7720_port = usb_get_serial_port_data(port); 161562306a36Sopenharmony_ci if (mos7720_port == NULL) 161662306a36Sopenharmony_ci return -ENODEV; 161762306a36Sopenharmony_ci 161862306a36Sopenharmony_ci switch (cmd) { 161962306a36Sopenharmony_ci case TIOCSERGETLSR: 162062306a36Sopenharmony_ci dev_dbg(&port->dev, "%s TIOCSERGETLSR\n", __func__); 162162306a36Sopenharmony_ci return get_lsr_info(tty, mos7720_port, 162262306a36Sopenharmony_ci (unsigned int __user *)arg); 162362306a36Sopenharmony_ci } 162462306a36Sopenharmony_ci 162562306a36Sopenharmony_ci return -ENOIOCTLCMD; 162662306a36Sopenharmony_ci} 162762306a36Sopenharmony_ci 162862306a36Sopenharmony_cistatic int mos7720_startup(struct usb_serial *serial) 162962306a36Sopenharmony_ci{ 163062306a36Sopenharmony_ci struct usb_device *dev; 163162306a36Sopenharmony_ci char data; 163262306a36Sopenharmony_ci u16 product; 163362306a36Sopenharmony_ci int ret_val; 163462306a36Sopenharmony_ci 163562306a36Sopenharmony_ci product = le16_to_cpu(serial->dev->descriptor.idProduct); 163662306a36Sopenharmony_ci dev = serial->dev; 163762306a36Sopenharmony_ci 163862306a36Sopenharmony_ci if (product == MOSCHIP_DEVICE_ID_7715) { 163962306a36Sopenharmony_ci struct urb *urb = serial->port[0]->interrupt_in_urb; 164062306a36Sopenharmony_ci 164162306a36Sopenharmony_ci urb->complete = mos7715_interrupt_callback; 164262306a36Sopenharmony_ci 164362306a36Sopenharmony_ci#ifdef CONFIG_USB_SERIAL_MOS7715_PARPORT 164462306a36Sopenharmony_ci ret_val = mos7715_parport_init(serial); 164562306a36Sopenharmony_ci if (ret_val < 0) 164662306a36Sopenharmony_ci return ret_val; 164762306a36Sopenharmony_ci#endif 164862306a36Sopenharmony_ci } 164962306a36Sopenharmony_ci /* start the interrupt urb */ 165062306a36Sopenharmony_ci ret_val = usb_submit_urb(serial->port[0]->interrupt_in_urb, GFP_KERNEL); 165162306a36Sopenharmony_ci if (ret_val) { 165262306a36Sopenharmony_ci dev_err(&dev->dev, "failed to submit interrupt urb: %d\n", 165362306a36Sopenharmony_ci ret_val); 165462306a36Sopenharmony_ci } 165562306a36Sopenharmony_ci 165662306a36Sopenharmony_ci /* LSR For Port 1 */ 165762306a36Sopenharmony_ci read_mos_reg(serial, 0, MOS7720_LSR, &data); 165862306a36Sopenharmony_ci dev_dbg(&dev->dev, "LSR:%x\n", data); 165962306a36Sopenharmony_ci 166062306a36Sopenharmony_ci return 0; 166162306a36Sopenharmony_ci} 166262306a36Sopenharmony_ci 166362306a36Sopenharmony_cistatic void mos7720_release(struct usb_serial *serial) 166462306a36Sopenharmony_ci{ 166562306a36Sopenharmony_ci usb_kill_urb(serial->port[0]->interrupt_in_urb); 166662306a36Sopenharmony_ci 166762306a36Sopenharmony_ci#ifdef CONFIG_USB_SERIAL_MOS7715_PARPORT 166862306a36Sopenharmony_ci /* close the parallel port */ 166962306a36Sopenharmony_ci 167062306a36Sopenharmony_ci if (le16_to_cpu(serial->dev->descriptor.idProduct) 167162306a36Sopenharmony_ci == MOSCHIP_DEVICE_ID_7715) { 167262306a36Sopenharmony_ci struct mos7715_parport *mos_parport = 167362306a36Sopenharmony_ci usb_get_serial_data(serial); 167462306a36Sopenharmony_ci 167562306a36Sopenharmony_ci /* prevent NULL ptr dereference in port callbacks */ 167662306a36Sopenharmony_ci spin_lock(&release_lock); 167762306a36Sopenharmony_ci mos_parport->pp->private_data = NULL; 167862306a36Sopenharmony_ci spin_unlock(&release_lock); 167962306a36Sopenharmony_ci 168062306a36Sopenharmony_ci /* wait for synchronous usb calls to return */ 168162306a36Sopenharmony_ci if (mos_parport->msg_pending) 168262306a36Sopenharmony_ci wait_for_completion_timeout(&mos_parport->syncmsg_compl, 168362306a36Sopenharmony_ci msecs_to_jiffies(MOS_WDR_TIMEOUT)); 168462306a36Sopenharmony_ci /* 168562306a36Sopenharmony_ci * If delayed work is currently scheduled, wait for it to 168662306a36Sopenharmony_ci * complete. This also implies barriers that ensure the 168762306a36Sopenharmony_ci * below serial clearing is not hoisted above the ->work. 168862306a36Sopenharmony_ci */ 168962306a36Sopenharmony_ci cancel_work_sync(&mos_parport->work); 169062306a36Sopenharmony_ci 169162306a36Sopenharmony_ci parport_remove_port(mos_parport->pp); 169262306a36Sopenharmony_ci usb_set_serial_data(serial, NULL); 169362306a36Sopenharmony_ci mos_parport->serial = NULL; 169462306a36Sopenharmony_ci 169562306a36Sopenharmony_ci parport_del_port(mos_parport->pp); 169662306a36Sopenharmony_ci 169762306a36Sopenharmony_ci kref_put(&mos_parport->ref_count, destroy_mos_parport); 169862306a36Sopenharmony_ci } 169962306a36Sopenharmony_ci#endif 170062306a36Sopenharmony_ci} 170162306a36Sopenharmony_ci 170262306a36Sopenharmony_cistatic int mos7720_port_probe(struct usb_serial_port *port) 170362306a36Sopenharmony_ci{ 170462306a36Sopenharmony_ci struct moschip_port *mos7720_port; 170562306a36Sopenharmony_ci 170662306a36Sopenharmony_ci mos7720_port = kzalloc(sizeof(*mos7720_port), GFP_KERNEL); 170762306a36Sopenharmony_ci if (!mos7720_port) 170862306a36Sopenharmony_ci return -ENOMEM; 170962306a36Sopenharmony_ci 171062306a36Sopenharmony_ci mos7720_port->port = port; 171162306a36Sopenharmony_ci 171262306a36Sopenharmony_ci usb_set_serial_port_data(port, mos7720_port); 171362306a36Sopenharmony_ci 171462306a36Sopenharmony_ci return 0; 171562306a36Sopenharmony_ci} 171662306a36Sopenharmony_ci 171762306a36Sopenharmony_cistatic void mos7720_port_remove(struct usb_serial_port *port) 171862306a36Sopenharmony_ci{ 171962306a36Sopenharmony_ci struct moschip_port *mos7720_port; 172062306a36Sopenharmony_ci 172162306a36Sopenharmony_ci mos7720_port = usb_get_serial_port_data(port); 172262306a36Sopenharmony_ci kfree(mos7720_port); 172362306a36Sopenharmony_ci} 172462306a36Sopenharmony_ci 172562306a36Sopenharmony_cistatic struct usb_serial_driver moschip7720_2port_driver = { 172662306a36Sopenharmony_ci .driver = { 172762306a36Sopenharmony_ci .owner = THIS_MODULE, 172862306a36Sopenharmony_ci .name = "moschip7720", 172962306a36Sopenharmony_ci }, 173062306a36Sopenharmony_ci .description = "Moschip 2 port adapter", 173162306a36Sopenharmony_ci .id_table = id_table, 173262306a36Sopenharmony_ci .num_bulk_in = 2, 173362306a36Sopenharmony_ci .num_bulk_out = 2, 173462306a36Sopenharmony_ci .num_interrupt_in = 1, 173562306a36Sopenharmony_ci .calc_num_ports = mos77xx_calc_num_ports, 173662306a36Sopenharmony_ci .open = mos7720_open, 173762306a36Sopenharmony_ci .close = mos7720_close, 173862306a36Sopenharmony_ci .throttle = mos7720_throttle, 173962306a36Sopenharmony_ci .unthrottle = mos7720_unthrottle, 174062306a36Sopenharmony_ci .attach = mos7720_startup, 174162306a36Sopenharmony_ci .release = mos7720_release, 174262306a36Sopenharmony_ci .port_probe = mos7720_port_probe, 174362306a36Sopenharmony_ci .port_remove = mos7720_port_remove, 174462306a36Sopenharmony_ci .ioctl = mos7720_ioctl, 174562306a36Sopenharmony_ci .tiocmget = mos7720_tiocmget, 174662306a36Sopenharmony_ci .tiocmset = mos7720_tiocmset, 174762306a36Sopenharmony_ci .set_termios = mos7720_set_termios, 174862306a36Sopenharmony_ci .write = mos7720_write, 174962306a36Sopenharmony_ci .write_room = mos7720_write_room, 175062306a36Sopenharmony_ci .chars_in_buffer = mos7720_chars_in_buffer, 175162306a36Sopenharmony_ci .break_ctl = mos7720_break, 175262306a36Sopenharmony_ci .read_bulk_callback = mos7720_bulk_in_callback, 175362306a36Sopenharmony_ci .read_int_callback = mos7720_interrupt_callback, 175462306a36Sopenharmony_ci}; 175562306a36Sopenharmony_ci 175662306a36Sopenharmony_cistatic struct usb_serial_driver * const serial_drivers[] = { 175762306a36Sopenharmony_ci &moschip7720_2port_driver, NULL 175862306a36Sopenharmony_ci}; 175962306a36Sopenharmony_ci 176062306a36Sopenharmony_cimodule_usb_serial_driver(serial_drivers, id_table); 176162306a36Sopenharmony_ci 176262306a36Sopenharmony_ciMODULE_AUTHOR(DRIVER_AUTHOR); 176362306a36Sopenharmony_ciMODULE_DESCRIPTION(DRIVER_DESC); 176462306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 1765