162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Edgeport USB Serial Converter driver 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2000-2002 Inside Out Networks, All rights reserved. 662306a36Sopenharmony_ci * Copyright (C) 2001-2002 Greg Kroah-Hartman <greg@kroah.com> 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci * Supports the following devices: 962306a36Sopenharmony_ci * EP/1 EP/2 EP/4 EP/21 EP/22 EP/221 EP/42 EP/421 WATCHPORT 1062306a36Sopenharmony_ci * 1162306a36Sopenharmony_ci * For questions or problems with this driver, contact Inside Out 1262306a36Sopenharmony_ci * Networks technical support, or Peter Berger <pberger@brimson.com>, 1362306a36Sopenharmony_ci * or Al Borchers <alborchers@steinerpoint.com>. 1462306a36Sopenharmony_ci */ 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_ci#include <linux/kernel.h> 1762306a36Sopenharmony_ci#include <linux/jiffies.h> 1862306a36Sopenharmony_ci#include <linux/errno.h> 1962306a36Sopenharmony_ci#include <linux/slab.h> 2062306a36Sopenharmony_ci#include <linux/tty.h> 2162306a36Sopenharmony_ci#include <linux/tty_driver.h> 2262306a36Sopenharmony_ci#include <linux/tty_flip.h> 2362306a36Sopenharmony_ci#include <linux/module.h> 2462306a36Sopenharmony_ci#include <linux/spinlock.h> 2562306a36Sopenharmony_ci#include <linux/mutex.h> 2662306a36Sopenharmony_ci#include <linux/serial.h> 2762306a36Sopenharmony_ci#include <linux/swab.h> 2862306a36Sopenharmony_ci#include <linux/kfifo.h> 2962306a36Sopenharmony_ci#include <linux/ioctl.h> 3062306a36Sopenharmony_ci#include <linux/firmware.h> 3162306a36Sopenharmony_ci#include <linux/uaccess.h> 3262306a36Sopenharmony_ci#include <linux/usb.h> 3362306a36Sopenharmony_ci#include <linux/usb/serial.h> 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci#include "io_16654.h" 3662306a36Sopenharmony_ci#include "io_usbvend.h" 3762306a36Sopenharmony_ci#include "io_ti.h" 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_ci#define DRIVER_AUTHOR "Greg Kroah-Hartman <greg@kroah.com> and David Iacovelli" 4062306a36Sopenharmony_ci#define DRIVER_DESC "Edgeport USB Serial Driver" 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci#define EPROM_PAGE_SIZE 64 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci/* different hardware types */ 4662306a36Sopenharmony_ci#define HARDWARE_TYPE_930 0 4762306a36Sopenharmony_ci#define HARDWARE_TYPE_TIUMP 1 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci/* IOCTL_PRIVATE_TI_GET_MODE Definitions */ 5062306a36Sopenharmony_ci#define TI_MODE_CONFIGURING 0 /* Device has not entered start device */ 5162306a36Sopenharmony_ci#define TI_MODE_BOOT 1 /* Staying in boot mode */ 5262306a36Sopenharmony_ci#define TI_MODE_DOWNLOAD 2 /* Made it to download mode */ 5362306a36Sopenharmony_ci#define TI_MODE_TRANSITIONING 3 /* 5462306a36Sopenharmony_ci * Currently in boot mode but 5562306a36Sopenharmony_ci * transitioning to download mode 5662306a36Sopenharmony_ci */ 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci/* read urb state */ 5962306a36Sopenharmony_ci#define EDGE_READ_URB_RUNNING 0 6062306a36Sopenharmony_ci#define EDGE_READ_URB_STOPPING 1 6162306a36Sopenharmony_ci#define EDGE_READ_URB_STOPPED 2 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci/* Product information read from the Edgeport */ 6562306a36Sopenharmony_cistruct product_info { 6662306a36Sopenharmony_ci int TiMode; /* Current TI Mode */ 6762306a36Sopenharmony_ci u8 hardware_type; /* Type of hardware */ 6862306a36Sopenharmony_ci} __packed; 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci/* 7162306a36Sopenharmony_ci * Edgeport firmware header 7262306a36Sopenharmony_ci * 7362306a36Sopenharmony_ci * "build_number" has been set to 0 in all three of the images I have 7462306a36Sopenharmony_ci * seen, and Digi Tech Support suggests that it is safe to ignore it. 7562306a36Sopenharmony_ci * 7662306a36Sopenharmony_ci * "length" is the number of bytes of actual data following the header. 7762306a36Sopenharmony_ci * 7862306a36Sopenharmony_ci * "checksum" is the low order byte resulting from adding the values of 7962306a36Sopenharmony_ci * all the data bytes. 8062306a36Sopenharmony_ci */ 8162306a36Sopenharmony_cistruct edgeport_fw_hdr { 8262306a36Sopenharmony_ci u8 major_version; 8362306a36Sopenharmony_ci u8 minor_version; 8462306a36Sopenharmony_ci __le16 build_number; 8562306a36Sopenharmony_ci __le16 length; 8662306a36Sopenharmony_ci u8 checksum; 8762306a36Sopenharmony_ci} __packed; 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_cistruct edgeport_port { 9062306a36Sopenharmony_ci u16 uart_base; 9162306a36Sopenharmony_ci u16 dma_address; 9262306a36Sopenharmony_ci u8 shadow_msr; 9362306a36Sopenharmony_ci u8 shadow_mcr; 9462306a36Sopenharmony_ci u8 shadow_lsr; 9562306a36Sopenharmony_ci u8 lsr_mask; 9662306a36Sopenharmony_ci u32 ump_read_timeout; /* 9762306a36Sopenharmony_ci * Number of milliseconds the UMP will 9862306a36Sopenharmony_ci * wait without data before completing 9962306a36Sopenharmony_ci * a read short 10062306a36Sopenharmony_ci */ 10162306a36Sopenharmony_ci int baud_rate; 10262306a36Sopenharmony_ci int close_pending; 10362306a36Sopenharmony_ci int lsr_event; 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci struct edgeport_serial *edge_serial; 10662306a36Sopenharmony_ci struct usb_serial_port *port; 10762306a36Sopenharmony_ci u8 bUartMode; /* Port type, 0: RS232, etc. */ 10862306a36Sopenharmony_ci spinlock_t ep_lock; 10962306a36Sopenharmony_ci int ep_read_urb_state; 11062306a36Sopenharmony_ci int ep_write_urb_in_use; 11162306a36Sopenharmony_ci}; 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_cistruct edgeport_serial { 11462306a36Sopenharmony_ci struct product_info product_info; 11562306a36Sopenharmony_ci u8 TI_I2C_Type; /* Type of I2C in UMP */ 11662306a36Sopenharmony_ci u8 TiReadI2C; /* 11762306a36Sopenharmony_ci * Set to TRUE if we have read the 11862306a36Sopenharmony_ci * I2c in Boot Mode 11962306a36Sopenharmony_ci */ 12062306a36Sopenharmony_ci struct mutex es_lock; 12162306a36Sopenharmony_ci int num_ports_open; 12262306a36Sopenharmony_ci struct usb_serial *serial; 12362306a36Sopenharmony_ci struct delayed_work heartbeat_work; 12462306a36Sopenharmony_ci int fw_version; 12562306a36Sopenharmony_ci bool use_heartbeat; 12662306a36Sopenharmony_ci}; 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci/* Devices that this driver supports */ 13062306a36Sopenharmony_cistatic const struct usb_device_id edgeport_1port_id_table[] = { 13162306a36Sopenharmony_ci { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_TI_EDGEPORT_1) }, 13262306a36Sopenharmony_ci { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_TI_TI3410_EDGEPORT_1) }, 13362306a36Sopenharmony_ci { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_TI_TI3410_EDGEPORT_1I) }, 13462306a36Sopenharmony_ci { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_WP_PROXIMITY) }, 13562306a36Sopenharmony_ci { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_WP_MOTION) }, 13662306a36Sopenharmony_ci { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_WP_MOISTURE) }, 13762306a36Sopenharmony_ci { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_WP_TEMPERATURE) }, 13862306a36Sopenharmony_ci { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_WP_HUMIDITY) }, 13962306a36Sopenharmony_ci { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_WP_POWER) }, 14062306a36Sopenharmony_ci { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_WP_LIGHT) }, 14162306a36Sopenharmony_ci { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_WP_RADIATION) }, 14262306a36Sopenharmony_ci { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_WP_DISTANCE) }, 14362306a36Sopenharmony_ci { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_WP_ACCELERATION) }, 14462306a36Sopenharmony_ci { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_WP_PROX_DIST) }, 14562306a36Sopenharmony_ci { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_PLUS_PWR_HP4CD) }, 14662306a36Sopenharmony_ci { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_PLUS_PWR_PCI) }, 14762306a36Sopenharmony_ci { } 14862306a36Sopenharmony_ci}; 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_cistatic const struct usb_device_id edgeport_2port_id_table[] = { 15162306a36Sopenharmony_ci { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_TI_EDGEPORT_2) }, 15262306a36Sopenharmony_ci { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_TI_EDGEPORT_2C) }, 15362306a36Sopenharmony_ci { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_TI_EDGEPORT_2I) }, 15462306a36Sopenharmony_ci { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_TI_EDGEPORT_421) }, 15562306a36Sopenharmony_ci { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_TI_EDGEPORT_21) }, 15662306a36Sopenharmony_ci { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_TI_EDGEPORT_42) }, 15762306a36Sopenharmony_ci { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_TI_EDGEPORT_4) }, 15862306a36Sopenharmony_ci { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_TI_EDGEPORT_4I) }, 15962306a36Sopenharmony_ci { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_TI_EDGEPORT_22I) }, 16062306a36Sopenharmony_ci { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_TI_EDGEPORT_221C) }, 16162306a36Sopenharmony_ci { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_TI_EDGEPORT_22C) }, 16262306a36Sopenharmony_ci { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_TI_EDGEPORT_21C) }, 16362306a36Sopenharmony_ci /* The 4, 8 and 16 port devices show up as multiple 2 port devices */ 16462306a36Sopenharmony_ci { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_TI_EDGEPORT_4S) }, 16562306a36Sopenharmony_ci { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_TI_EDGEPORT_8) }, 16662306a36Sopenharmony_ci { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_TI_EDGEPORT_8S) }, 16762306a36Sopenharmony_ci { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_TI_EDGEPORT_416) }, 16862306a36Sopenharmony_ci { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_TI_EDGEPORT_416B) }, 16962306a36Sopenharmony_ci { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_E5805A) }, 17062306a36Sopenharmony_ci { } 17162306a36Sopenharmony_ci}; 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci/* Devices that this driver supports */ 17462306a36Sopenharmony_cistatic const struct usb_device_id id_table_combined[] = { 17562306a36Sopenharmony_ci { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_TI_EDGEPORT_1) }, 17662306a36Sopenharmony_ci { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_TI_TI3410_EDGEPORT_1) }, 17762306a36Sopenharmony_ci { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_TI_TI3410_EDGEPORT_1I) }, 17862306a36Sopenharmony_ci { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_WP_PROXIMITY) }, 17962306a36Sopenharmony_ci { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_WP_MOTION) }, 18062306a36Sopenharmony_ci { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_WP_MOISTURE) }, 18162306a36Sopenharmony_ci { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_WP_TEMPERATURE) }, 18262306a36Sopenharmony_ci { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_WP_HUMIDITY) }, 18362306a36Sopenharmony_ci { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_WP_POWER) }, 18462306a36Sopenharmony_ci { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_WP_LIGHT) }, 18562306a36Sopenharmony_ci { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_WP_RADIATION) }, 18662306a36Sopenharmony_ci { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_WP_DISTANCE) }, 18762306a36Sopenharmony_ci { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_WP_ACCELERATION) }, 18862306a36Sopenharmony_ci { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_WP_PROX_DIST) }, 18962306a36Sopenharmony_ci { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_PLUS_PWR_HP4CD) }, 19062306a36Sopenharmony_ci { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_PLUS_PWR_PCI) }, 19162306a36Sopenharmony_ci { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_TI_EDGEPORT_2) }, 19262306a36Sopenharmony_ci { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_TI_EDGEPORT_2C) }, 19362306a36Sopenharmony_ci { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_TI_EDGEPORT_2I) }, 19462306a36Sopenharmony_ci { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_TI_EDGEPORT_421) }, 19562306a36Sopenharmony_ci { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_TI_EDGEPORT_21) }, 19662306a36Sopenharmony_ci { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_TI_EDGEPORT_42) }, 19762306a36Sopenharmony_ci { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_TI_EDGEPORT_4) }, 19862306a36Sopenharmony_ci { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_TI_EDGEPORT_4I) }, 19962306a36Sopenharmony_ci { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_TI_EDGEPORT_22I) }, 20062306a36Sopenharmony_ci { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_TI_EDGEPORT_221C) }, 20162306a36Sopenharmony_ci { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_TI_EDGEPORT_22C) }, 20262306a36Sopenharmony_ci { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_TI_EDGEPORT_21C) }, 20362306a36Sopenharmony_ci { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_TI_EDGEPORT_4S) }, 20462306a36Sopenharmony_ci { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_TI_EDGEPORT_8) }, 20562306a36Sopenharmony_ci { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_TI_EDGEPORT_8S) }, 20662306a36Sopenharmony_ci { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_TI_EDGEPORT_416) }, 20762306a36Sopenharmony_ci { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_TI_EDGEPORT_416B) }, 20862306a36Sopenharmony_ci { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_E5805A) }, 20962306a36Sopenharmony_ci { } 21062306a36Sopenharmony_ci}; 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ciMODULE_DEVICE_TABLE(usb, id_table_combined); 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_cistatic bool ignore_cpu_rev; 21562306a36Sopenharmony_cistatic int default_uart_mode; /* RS232 */ 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_cistatic void edge_tty_recv(struct usb_serial_port *port, unsigned char *data, 21862306a36Sopenharmony_ci int length); 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_cistatic void stop_read(struct edgeport_port *edge_port); 22162306a36Sopenharmony_cistatic int restart_read(struct edgeport_port *edge_port); 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_cistatic void edge_set_termios(struct tty_struct *tty, 22462306a36Sopenharmony_ci struct usb_serial_port *port, 22562306a36Sopenharmony_ci const struct ktermios *old_termios); 22662306a36Sopenharmony_cistatic void edge_send(struct usb_serial_port *port, struct tty_struct *tty); 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_cistatic int do_download_mode(struct edgeport_serial *serial, 22962306a36Sopenharmony_ci const struct firmware *fw); 23062306a36Sopenharmony_cistatic int do_boot_mode(struct edgeport_serial *serial, 23162306a36Sopenharmony_ci const struct firmware *fw); 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ci/* sysfs attributes */ 23462306a36Sopenharmony_cistatic int edge_create_sysfs_attrs(struct usb_serial_port *port); 23562306a36Sopenharmony_cistatic int edge_remove_sysfs_attrs(struct usb_serial_port *port); 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_ci/* 23862306a36Sopenharmony_ci * Some release of Edgeport firmware "down3.bin" after version 4.80 23962306a36Sopenharmony_ci * introduced code to automatically disconnect idle devices on some 24062306a36Sopenharmony_ci * Edgeport models after periods of inactivity, typically ~60 seconds. 24162306a36Sopenharmony_ci * This occurs without regard to whether ports on the device are open 24262306a36Sopenharmony_ci * or not. Digi International Tech Support suggested: 24362306a36Sopenharmony_ci * 24462306a36Sopenharmony_ci * 1. Adding driver "heartbeat" code to reset the firmware timer by 24562306a36Sopenharmony_ci * requesting a descriptor record every 15 seconds, which should be 24662306a36Sopenharmony_ci * effective with newer firmware versions that require it, and benign 24762306a36Sopenharmony_ci * with older versions that do not. In practice 40 seconds seems often 24862306a36Sopenharmony_ci * enough. 24962306a36Sopenharmony_ci * 2. The heartbeat code is currently required only on Edgeport/416 models. 25062306a36Sopenharmony_ci */ 25162306a36Sopenharmony_ci#define FW_HEARTBEAT_VERSION_CUTOFF ((4 << 8) + 80) 25262306a36Sopenharmony_ci#define FW_HEARTBEAT_SECS 40 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_ci/* Timeouts in msecs: firmware downloads take longer */ 25562306a36Sopenharmony_ci#define TI_VSEND_TIMEOUT_DEFAULT 1000 25662306a36Sopenharmony_ci#define TI_VSEND_TIMEOUT_FW_DOWNLOAD 10000 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_cistatic int ti_vread_sync(struct usb_device *dev, u8 request, u16 value, 25962306a36Sopenharmony_ci u16 index, void *data, int size) 26062306a36Sopenharmony_ci{ 26162306a36Sopenharmony_ci int status; 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ci status = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), request, 26462306a36Sopenharmony_ci (USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN), 26562306a36Sopenharmony_ci value, index, data, size, 1000); 26662306a36Sopenharmony_ci if (status < 0) 26762306a36Sopenharmony_ci return status; 26862306a36Sopenharmony_ci if (status != size) { 26962306a36Sopenharmony_ci dev_dbg(&dev->dev, "%s - wanted to read %d, but only read %d\n", 27062306a36Sopenharmony_ci __func__, size, status); 27162306a36Sopenharmony_ci return -ECOMM; 27262306a36Sopenharmony_ci } 27362306a36Sopenharmony_ci return 0; 27462306a36Sopenharmony_ci} 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_cistatic int ti_vsend_sync(struct usb_device *dev, u8 request, u16 value, 27762306a36Sopenharmony_ci u16 index, void *data, int size, int timeout) 27862306a36Sopenharmony_ci{ 27962306a36Sopenharmony_ci int status; 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_ci status = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), request, 28262306a36Sopenharmony_ci (USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_OUT), 28362306a36Sopenharmony_ci value, index, data, size, timeout); 28462306a36Sopenharmony_ci if (status < 0) 28562306a36Sopenharmony_ci return status; 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_ci return 0; 28862306a36Sopenharmony_ci} 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_cistatic int read_port_cmd(struct usb_serial_port *port, u8 command, u16 value, 29162306a36Sopenharmony_ci void *data, int size) 29262306a36Sopenharmony_ci{ 29362306a36Sopenharmony_ci return ti_vread_sync(port->serial->dev, command, value, 29462306a36Sopenharmony_ci UMPM_UART1_PORT + port->port_number, 29562306a36Sopenharmony_ci data, size); 29662306a36Sopenharmony_ci} 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_cistatic int send_port_cmd(struct usb_serial_port *port, u8 command, u16 value, 29962306a36Sopenharmony_ci void *data, int size) 30062306a36Sopenharmony_ci{ 30162306a36Sopenharmony_ci return ti_vsend_sync(port->serial->dev, command, value, 30262306a36Sopenharmony_ci UMPM_UART1_PORT + port->port_number, 30362306a36Sopenharmony_ci data, size, TI_VSEND_TIMEOUT_DEFAULT); 30462306a36Sopenharmony_ci} 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_ci/* clear tx/rx buffers and fifo in TI UMP */ 30762306a36Sopenharmony_cistatic int purge_port(struct usb_serial_port *port, u16 mask) 30862306a36Sopenharmony_ci{ 30962306a36Sopenharmony_ci int port_number = port->port_number; 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_ci dev_dbg(&port->dev, "%s - port %d, mask %x\n", __func__, port_number, mask); 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_ci return send_port_cmd(port, UMPC_PURGE_PORT, mask, NULL, 0); 31462306a36Sopenharmony_ci} 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_ci/** 31762306a36Sopenharmony_ci * read_download_mem - Read edgeport memory from TI chip 31862306a36Sopenharmony_ci * @dev: usb device pointer 31962306a36Sopenharmony_ci * @start_address: Device CPU address at which to read 32062306a36Sopenharmony_ci * @length: Length of above data 32162306a36Sopenharmony_ci * @address_type: Can read both XDATA and I2C 32262306a36Sopenharmony_ci * @buffer: pointer to input data buffer 32362306a36Sopenharmony_ci */ 32462306a36Sopenharmony_cistatic int read_download_mem(struct usb_device *dev, int start_address, 32562306a36Sopenharmony_ci int length, u8 address_type, u8 *buffer) 32662306a36Sopenharmony_ci{ 32762306a36Sopenharmony_ci int status = 0; 32862306a36Sopenharmony_ci u8 read_length; 32962306a36Sopenharmony_ci u16 be_start_address; 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_ci dev_dbg(&dev->dev, "%s - @ %x for %d\n", __func__, start_address, length); 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_ci /* 33462306a36Sopenharmony_ci * Read in blocks of 64 bytes 33562306a36Sopenharmony_ci * (TI firmware can't handle more than 64 byte reads) 33662306a36Sopenharmony_ci */ 33762306a36Sopenharmony_ci while (length) { 33862306a36Sopenharmony_ci if (length > 64) 33962306a36Sopenharmony_ci read_length = 64; 34062306a36Sopenharmony_ci else 34162306a36Sopenharmony_ci read_length = (u8)length; 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_ci if (read_length > 1) { 34462306a36Sopenharmony_ci dev_dbg(&dev->dev, "%s - @ %x for %d\n", __func__, start_address, read_length); 34562306a36Sopenharmony_ci } 34662306a36Sopenharmony_ci /* 34762306a36Sopenharmony_ci * NOTE: Must use swab as wIndex is sent in little-endian 34862306a36Sopenharmony_ci * byte order regardless of host byte order. 34962306a36Sopenharmony_ci */ 35062306a36Sopenharmony_ci be_start_address = swab16((u16)start_address); 35162306a36Sopenharmony_ci status = ti_vread_sync(dev, UMPC_MEMORY_READ, 35262306a36Sopenharmony_ci (u16)address_type, 35362306a36Sopenharmony_ci be_start_address, 35462306a36Sopenharmony_ci buffer, read_length); 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_ci if (status) { 35762306a36Sopenharmony_ci dev_dbg(&dev->dev, "%s - ERROR %x\n", __func__, status); 35862306a36Sopenharmony_ci return status; 35962306a36Sopenharmony_ci } 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_ci if (read_length > 1) 36262306a36Sopenharmony_ci usb_serial_debug_data(&dev->dev, __func__, read_length, buffer); 36362306a36Sopenharmony_ci 36462306a36Sopenharmony_ci /* Update pointers/length */ 36562306a36Sopenharmony_ci start_address += read_length; 36662306a36Sopenharmony_ci buffer += read_length; 36762306a36Sopenharmony_ci length -= read_length; 36862306a36Sopenharmony_ci } 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_ci return status; 37162306a36Sopenharmony_ci} 37262306a36Sopenharmony_ci 37362306a36Sopenharmony_cistatic int read_ram(struct usb_device *dev, int start_address, 37462306a36Sopenharmony_ci int length, u8 *buffer) 37562306a36Sopenharmony_ci{ 37662306a36Sopenharmony_ci return read_download_mem(dev, start_address, length, 37762306a36Sopenharmony_ci DTK_ADDR_SPACE_XDATA, buffer); 37862306a36Sopenharmony_ci} 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_ci/* Read edgeport memory to a given block */ 38162306a36Sopenharmony_cistatic int read_boot_mem(struct edgeport_serial *serial, 38262306a36Sopenharmony_ci int start_address, int length, u8 *buffer) 38362306a36Sopenharmony_ci{ 38462306a36Sopenharmony_ci int status = 0; 38562306a36Sopenharmony_ci int i; 38662306a36Sopenharmony_ci 38762306a36Sopenharmony_ci for (i = 0; i < length; i++) { 38862306a36Sopenharmony_ci status = ti_vread_sync(serial->serial->dev, 38962306a36Sopenharmony_ci UMPC_MEMORY_READ, serial->TI_I2C_Type, 39062306a36Sopenharmony_ci (u16)(start_address+i), &buffer[i], 0x01); 39162306a36Sopenharmony_ci if (status) { 39262306a36Sopenharmony_ci dev_dbg(&serial->serial->dev->dev, "%s - ERROR %x\n", __func__, status); 39362306a36Sopenharmony_ci return status; 39462306a36Sopenharmony_ci } 39562306a36Sopenharmony_ci } 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_ci dev_dbg(&serial->serial->dev->dev, "%s - start_address = %x, length = %d\n", 39862306a36Sopenharmony_ci __func__, start_address, length); 39962306a36Sopenharmony_ci usb_serial_debug_data(&serial->serial->dev->dev, __func__, length, buffer); 40062306a36Sopenharmony_ci 40162306a36Sopenharmony_ci serial->TiReadI2C = 1; 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_ci return status; 40462306a36Sopenharmony_ci} 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_ci/* Write given block to TI EPROM memory */ 40762306a36Sopenharmony_cistatic int write_boot_mem(struct edgeport_serial *serial, 40862306a36Sopenharmony_ci int start_address, int length, u8 *buffer) 40962306a36Sopenharmony_ci{ 41062306a36Sopenharmony_ci int status = 0; 41162306a36Sopenharmony_ci int i; 41262306a36Sopenharmony_ci u8 *temp; 41362306a36Sopenharmony_ci 41462306a36Sopenharmony_ci /* Must do a read before write */ 41562306a36Sopenharmony_ci if (!serial->TiReadI2C) { 41662306a36Sopenharmony_ci temp = kmalloc(1, GFP_KERNEL); 41762306a36Sopenharmony_ci if (!temp) 41862306a36Sopenharmony_ci return -ENOMEM; 41962306a36Sopenharmony_ci 42062306a36Sopenharmony_ci status = read_boot_mem(serial, 0, 1, temp); 42162306a36Sopenharmony_ci kfree(temp); 42262306a36Sopenharmony_ci if (status) 42362306a36Sopenharmony_ci return status; 42462306a36Sopenharmony_ci } 42562306a36Sopenharmony_ci 42662306a36Sopenharmony_ci for (i = 0; i < length; ++i) { 42762306a36Sopenharmony_ci status = ti_vsend_sync(serial->serial->dev, UMPC_MEMORY_WRITE, 42862306a36Sopenharmony_ci buffer[i], (u16)(i + start_address), NULL, 42962306a36Sopenharmony_ci 0, TI_VSEND_TIMEOUT_DEFAULT); 43062306a36Sopenharmony_ci if (status) 43162306a36Sopenharmony_ci return status; 43262306a36Sopenharmony_ci } 43362306a36Sopenharmony_ci 43462306a36Sopenharmony_ci dev_dbg(&serial->serial->dev->dev, "%s - start_sddr = %x, length = %d\n", __func__, start_address, length); 43562306a36Sopenharmony_ci usb_serial_debug_data(&serial->serial->dev->dev, __func__, length, buffer); 43662306a36Sopenharmony_ci 43762306a36Sopenharmony_ci return status; 43862306a36Sopenharmony_ci} 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_ci/* Write edgeport I2C memory to TI chip */ 44162306a36Sopenharmony_cistatic int write_i2c_mem(struct edgeport_serial *serial, 44262306a36Sopenharmony_ci int start_address, int length, u8 address_type, u8 *buffer) 44362306a36Sopenharmony_ci{ 44462306a36Sopenharmony_ci struct device *dev = &serial->serial->dev->dev; 44562306a36Sopenharmony_ci int status = 0; 44662306a36Sopenharmony_ci int write_length; 44762306a36Sopenharmony_ci u16 be_start_address; 44862306a36Sopenharmony_ci 44962306a36Sopenharmony_ci /* We can only send a maximum of 1 aligned byte page at a time */ 45062306a36Sopenharmony_ci 45162306a36Sopenharmony_ci /* calculate the number of bytes left in the first page */ 45262306a36Sopenharmony_ci write_length = EPROM_PAGE_SIZE - 45362306a36Sopenharmony_ci (start_address & (EPROM_PAGE_SIZE - 1)); 45462306a36Sopenharmony_ci 45562306a36Sopenharmony_ci if (write_length > length) 45662306a36Sopenharmony_ci write_length = length; 45762306a36Sopenharmony_ci 45862306a36Sopenharmony_ci dev_dbg(dev, "%s - BytesInFirstPage Addr = %x, length = %d\n", 45962306a36Sopenharmony_ci __func__, start_address, write_length); 46062306a36Sopenharmony_ci usb_serial_debug_data(dev, __func__, write_length, buffer); 46162306a36Sopenharmony_ci 46262306a36Sopenharmony_ci /* 46362306a36Sopenharmony_ci * Write first page. 46462306a36Sopenharmony_ci * 46562306a36Sopenharmony_ci * NOTE: Must use swab as wIndex is sent in little-endian byte order 46662306a36Sopenharmony_ci * regardless of host byte order. 46762306a36Sopenharmony_ci */ 46862306a36Sopenharmony_ci be_start_address = swab16((u16)start_address); 46962306a36Sopenharmony_ci status = ti_vsend_sync(serial->serial->dev, UMPC_MEMORY_WRITE, 47062306a36Sopenharmony_ci (u16)address_type, be_start_address, 47162306a36Sopenharmony_ci buffer, write_length, TI_VSEND_TIMEOUT_DEFAULT); 47262306a36Sopenharmony_ci if (status) { 47362306a36Sopenharmony_ci dev_dbg(dev, "%s - ERROR %d\n", __func__, status); 47462306a36Sopenharmony_ci return status; 47562306a36Sopenharmony_ci } 47662306a36Sopenharmony_ci 47762306a36Sopenharmony_ci length -= write_length; 47862306a36Sopenharmony_ci start_address += write_length; 47962306a36Sopenharmony_ci buffer += write_length; 48062306a36Sopenharmony_ci 48162306a36Sopenharmony_ci /* 48262306a36Sopenharmony_ci * We should be aligned now -- can write max page size bytes at a 48362306a36Sopenharmony_ci * time. 48462306a36Sopenharmony_ci */ 48562306a36Sopenharmony_ci while (length) { 48662306a36Sopenharmony_ci if (length > EPROM_PAGE_SIZE) 48762306a36Sopenharmony_ci write_length = EPROM_PAGE_SIZE; 48862306a36Sopenharmony_ci else 48962306a36Sopenharmony_ci write_length = length; 49062306a36Sopenharmony_ci 49162306a36Sopenharmony_ci dev_dbg(dev, "%s - Page Write Addr = %x, length = %d\n", 49262306a36Sopenharmony_ci __func__, start_address, write_length); 49362306a36Sopenharmony_ci usb_serial_debug_data(dev, __func__, write_length, buffer); 49462306a36Sopenharmony_ci 49562306a36Sopenharmony_ci /* 49662306a36Sopenharmony_ci * Write next page. 49762306a36Sopenharmony_ci * 49862306a36Sopenharmony_ci * NOTE: Must use swab as wIndex is sent in little-endian byte 49962306a36Sopenharmony_ci * order regardless of host byte order. 50062306a36Sopenharmony_ci */ 50162306a36Sopenharmony_ci be_start_address = swab16((u16)start_address); 50262306a36Sopenharmony_ci status = ti_vsend_sync(serial->serial->dev, UMPC_MEMORY_WRITE, 50362306a36Sopenharmony_ci (u16)address_type, be_start_address, buffer, 50462306a36Sopenharmony_ci write_length, TI_VSEND_TIMEOUT_DEFAULT); 50562306a36Sopenharmony_ci if (status) { 50662306a36Sopenharmony_ci dev_err(dev, "%s - ERROR %d\n", __func__, status); 50762306a36Sopenharmony_ci return status; 50862306a36Sopenharmony_ci } 50962306a36Sopenharmony_ci 51062306a36Sopenharmony_ci length -= write_length; 51162306a36Sopenharmony_ci start_address += write_length; 51262306a36Sopenharmony_ci buffer += write_length; 51362306a36Sopenharmony_ci } 51462306a36Sopenharmony_ci return status; 51562306a36Sopenharmony_ci} 51662306a36Sopenharmony_ci 51762306a36Sopenharmony_ci/* 51862306a36Sopenharmony_ci * Examine the UMP DMA registers and LSR 51962306a36Sopenharmony_ci * 52062306a36Sopenharmony_ci * Check the MSBit of the X and Y DMA byte count registers. 52162306a36Sopenharmony_ci * A zero in this bit indicates that the TX DMA buffers are empty 52262306a36Sopenharmony_ci * then check the TX Empty bit in the UART. 52362306a36Sopenharmony_ci */ 52462306a36Sopenharmony_cistatic int tx_active(struct edgeport_port *port) 52562306a36Sopenharmony_ci{ 52662306a36Sopenharmony_ci int status; 52762306a36Sopenharmony_ci struct out_endpoint_desc_block *oedb; 52862306a36Sopenharmony_ci u8 *lsr; 52962306a36Sopenharmony_ci int bytes_left = 0; 53062306a36Sopenharmony_ci 53162306a36Sopenharmony_ci oedb = kmalloc(sizeof(*oedb), GFP_KERNEL); 53262306a36Sopenharmony_ci if (!oedb) 53362306a36Sopenharmony_ci return -ENOMEM; 53462306a36Sopenharmony_ci 53562306a36Sopenharmony_ci /* 53662306a36Sopenharmony_ci * Sigh, that's right, just one byte, as not all platforms can 53762306a36Sopenharmony_ci * do DMA from stack 53862306a36Sopenharmony_ci */ 53962306a36Sopenharmony_ci lsr = kmalloc(1, GFP_KERNEL); 54062306a36Sopenharmony_ci if (!lsr) { 54162306a36Sopenharmony_ci kfree(oedb); 54262306a36Sopenharmony_ci return -ENOMEM; 54362306a36Sopenharmony_ci } 54462306a36Sopenharmony_ci /* Read the DMA Count Registers */ 54562306a36Sopenharmony_ci status = read_ram(port->port->serial->dev, port->dma_address, 54662306a36Sopenharmony_ci sizeof(*oedb), (void *)oedb); 54762306a36Sopenharmony_ci if (status) 54862306a36Sopenharmony_ci goto exit_is_tx_active; 54962306a36Sopenharmony_ci 55062306a36Sopenharmony_ci dev_dbg(&port->port->dev, "%s - XByteCount 0x%X\n", __func__, oedb->XByteCount); 55162306a36Sopenharmony_ci 55262306a36Sopenharmony_ci /* and the LSR */ 55362306a36Sopenharmony_ci status = read_ram(port->port->serial->dev, 55462306a36Sopenharmony_ci port->uart_base + UMPMEM_OFFS_UART_LSR, 1, lsr); 55562306a36Sopenharmony_ci 55662306a36Sopenharmony_ci if (status) 55762306a36Sopenharmony_ci goto exit_is_tx_active; 55862306a36Sopenharmony_ci dev_dbg(&port->port->dev, "%s - LSR = 0x%X\n", __func__, *lsr); 55962306a36Sopenharmony_ci 56062306a36Sopenharmony_ci /* If either buffer has data or we are transmitting then return TRUE */ 56162306a36Sopenharmony_ci if ((oedb->XByteCount & 0x80) != 0) 56262306a36Sopenharmony_ci bytes_left += 64; 56362306a36Sopenharmony_ci 56462306a36Sopenharmony_ci if ((*lsr & UMP_UART_LSR_TX_MASK) == 0) 56562306a36Sopenharmony_ci bytes_left += 1; 56662306a36Sopenharmony_ci 56762306a36Sopenharmony_ci /* We return Not Active if we get any kind of error */ 56862306a36Sopenharmony_ciexit_is_tx_active: 56962306a36Sopenharmony_ci dev_dbg(&port->port->dev, "%s - return %d\n", __func__, bytes_left); 57062306a36Sopenharmony_ci 57162306a36Sopenharmony_ci kfree(lsr); 57262306a36Sopenharmony_ci kfree(oedb); 57362306a36Sopenharmony_ci return bytes_left; 57462306a36Sopenharmony_ci} 57562306a36Sopenharmony_ci 57662306a36Sopenharmony_cistatic int choose_config(struct usb_device *dev) 57762306a36Sopenharmony_ci{ 57862306a36Sopenharmony_ci /* 57962306a36Sopenharmony_ci * There may be multiple configurations on this device, in which case 58062306a36Sopenharmony_ci * we would need to read and parse all of them to find out which one 58162306a36Sopenharmony_ci * we want. However, we just support one config at this point, 58262306a36Sopenharmony_ci * configuration # 1, which is Config Descriptor 0. 58362306a36Sopenharmony_ci */ 58462306a36Sopenharmony_ci 58562306a36Sopenharmony_ci dev_dbg(&dev->dev, "%s - Number of Interfaces = %d\n", 58662306a36Sopenharmony_ci __func__, dev->config->desc.bNumInterfaces); 58762306a36Sopenharmony_ci dev_dbg(&dev->dev, "%s - MAX Power = %d\n", 58862306a36Sopenharmony_ci __func__, dev->config->desc.bMaxPower * 2); 58962306a36Sopenharmony_ci 59062306a36Sopenharmony_ci if (dev->config->desc.bNumInterfaces != 1) { 59162306a36Sopenharmony_ci dev_err(&dev->dev, "%s - bNumInterfaces is not 1, ERROR!\n", __func__); 59262306a36Sopenharmony_ci return -ENODEV; 59362306a36Sopenharmony_ci } 59462306a36Sopenharmony_ci 59562306a36Sopenharmony_ci return 0; 59662306a36Sopenharmony_ci} 59762306a36Sopenharmony_ci 59862306a36Sopenharmony_cistatic int read_rom(struct edgeport_serial *serial, 59962306a36Sopenharmony_ci int start_address, int length, u8 *buffer) 60062306a36Sopenharmony_ci{ 60162306a36Sopenharmony_ci int status; 60262306a36Sopenharmony_ci 60362306a36Sopenharmony_ci if (serial->product_info.TiMode == TI_MODE_DOWNLOAD) { 60462306a36Sopenharmony_ci status = read_download_mem(serial->serial->dev, 60562306a36Sopenharmony_ci start_address, 60662306a36Sopenharmony_ci length, 60762306a36Sopenharmony_ci serial->TI_I2C_Type, 60862306a36Sopenharmony_ci buffer); 60962306a36Sopenharmony_ci } else { 61062306a36Sopenharmony_ci status = read_boot_mem(serial, start_address, length, 61162306a36Sopenharmony_ci buffer); 61262306a36Sopenharmony_ci } 61362306a36Sopenharmony_ci return status; 61462306a36Sopenharmony_ci} 61562306a36Sopenharmony_ci 61662306a36Sopenharmony_cistatic int write_rom(struct edgeport_serial *serial, int start_address, 61762306a36Sopenharmony_ci int length, u8 *buffer) 61862306a36Sopenharmony_ci{ 61962306a36Sopenharmony_ci if (serial->product_info.TiMode == TI_MODE_BOOT) 62062306a36Sopenharmony_ci return write_boot_mem(serial, start_address, length, 62162306a36Sopenharmony_ci buffer); 62262306a36Sopenharmony_ci 62362306a36Sopenharmony_ci if (serial->product_info.TiMode == TI_MODE_DOWNLOAD) 62462306a36Sopenharmony_ci return write_i2c_mem(serial, start_address, length, 62562306a36Sopenharmony_ci serial->TI_I2C_Type, buffer); 62662306a36Sopenharmony_ci return -EINVAL; 62762306a36Sopenharmony_ci} 62862306a36Sopenharmony_ci 62962306a36Sopenharmony_ci/* Read a descriptor header from I2C based on type */ 63062306a36Sopenharmony_cistatic int get_descriptor_addr(struct edgeport_serial *serial, 63162306a36Sopenharmony_ci int desc_type, struct ti_i2c_desc *rom_desc) 63262306a36Sopenharmony_ci{ 63362306a36Sopenharmony_ci int start_address; 63462306a36Sopenharmony_ci int status; 63562306a36Sopenharmony_ci 63662306a36Sopenharmony_ci /* Search for requested descriptor in I2C */ 63762306a36Sopenharmony_ci start_address = 2; 63862306a36Sopenharmony_ci do { 63962306a36Sopenharmony_ci status = read_rom(serial, 64062306a36Sopenharmony_ci start_address, 64162306a36Sopenharmony_ci sizeof(struct ti_i2c_desc), 64262306a36Sopenharmony_ci (u8 *)rom_desc); 64362306a36Sopenharmony_ci if (status) 64462306a36Sopenharmony_ci return 0; 64562306a36Sopenharmony_ci 64662306a36Sopenharmony_ci if (rom_desc->Type == desc_type) 64762306a36Sopenharmony_ci return start_address; 64862306a36Sopenharmony_ci 64962306a36Sopenharmony_ci start_address = start_address + sizeof(struct ti_i2c_desc) + 65062306a36Sopenharmony_ci le16_to_cpu(rom_desc->Size); 65162306a36Sopenharmony_ci 65262306a36Sopenharmony_ci } while ((start_address < TI_MAX_I2C_SIZE) && rom_desc->Type); 65362306a36Sopenharmony_ci 65462306a36Sopenharmony_ci return 0; 65562306a36Sopenharmony_ci} 65662306a36Sopenharmony_ci 65762306a36Sopenharmony_ci/* Validate descriptor checksum */ 65862306a36Sopenharmony_cistatic int valid_csum(struct ti_i2c_desc *rom_desc, u8 *buffer) 65962306a36Sopenharmony_ci{ 66062306a36Sopenharmony_ci u16 i; 66162306a36Sopenharmony_ci u8 cs = 0; 66262306a36Sopenharmony_ci 66362306a36Sopenharmony_ci for (i = 0; i < le16_to_cpu(rom_desc->Size); i++) 66462306a36Sopenharmony_ci cs = (u8)(cs + buffer[i]); 66562306a36Sopenharmony_ci 66662306a36Sopenharmony_ci if (cs != rom_desc->CheckSum) { 66762306a36Sopenharmony_ci pr_debug("%s - Mismatch %x - %x", __func__, rom_desc->CheckSum, cs); 66862306a36Sopenharmony_ci return -EINVAL; 66962306a36Sopenharmony_ci } 67062306a36Sopenharmony_ci return 0; 67162306a36Sopenharmony_ci} 67262306a36Sopenharmony_ci 67362306a36Sopenharmony_ci/* Make sure that the I2C image is good */ 67462306a36Sopenharmony_cistatic int check_i2c_image(struct edgeport_serial *serial) 67562306a36Sopenharmony_ci{ 67662306a36Sopenharmony_ci struct device *dev = &serial->serial->dev->dev; 67762306a36Sopenharmony_ci int status = 0; 67862306a36Sopenharmony_ci struct ti_i2c_desc *rom_desc; 67962306a36Sopenharmony_ci int start_address = 2; 68062306a36Sopenharmony_ci u8 *buffer; 68162306a36Sopenharmony_ci u16 ttype; 68262306a36Sopenharmony_ci 68362306a36Sopenharmony_ci rom_desc = kmalloc(sizeof(*rom_desc), GFP_KERNEL); 68462306a36Sopenharmony_ci if (!rom_desc) 68562306a36Sopenharmony_ci return -ENOMEM; 68662306a36Sopenharmony_ci 68762306a36Sopenharmony_ci buffer = kmalloc(TI_MAX_I2C_SIZE, GFP_KERNEL); 68862306a36Sopenharmony_ci if (!buffer) { 68962306a36Sopenharmony_ci kfree(rom_desc); 69062306a36Sopenharmony_ci return -ENOMEM; 69162306a36Sopenharmony_ci } 69262306a36Sopenharmony_ci 69362306a36Sopenharmony_ci /* Read the first byte (Signature0) must be 0x52 or 0x10 */ 69462306a36Sopenharmony_ci status = read_rom(serial, 0, 1, buffer); 69562306a36Sopenharmony_ci if (status) 69662306a36Sopenharmony_ci goto out; 69762306a36Sopenharmony_ci 69862306a36Sopenharmony_ci if (*buffer != UMP5152 && *buffer != UMP3410) { 69962306a36Sopenharmony_ci dev_err(dev, "%s - invalid buffer signature\n", __func__); 70062306a36Sopenharmony_ci status = -ENODEV; 70162306a36Sopenharmony_ci goto out; 70262306a36Sopenharmony_ci } 70362306a36Sopenharmony_ci 70462306a36Sopenharmony_ci do { 70562306a36Sopenharmony_ci /* Validate the I2C */ 70662306a36Sopenharmony_ci status = read_rom(serial, 70762306a36Sopenharmony_ci start_address, 70862306a36Sopenharmony_ci sizeof(struct ti_i2c_desc), 70962306a36Sopenharmony_ci (u8 *)rom_desc); 71062306a36Sopenharmony_ci if (status) 71162306a36Sopenharmony_ci break; 71262306a36Sopenharmony_ci 71362306a36Sopenharmony_ci if ((start_address + sizeof(struct ti_i2c_desc) + 71462306a36Sopenharmony_ci le16_to_cpu(rom_desc->Size)) > TI_MAX_I2C_SIZE) { 71562306a36Sopenharmony_ci status = -ENODEV; 71662306a36Sopenharmony_ci dev_dbg(dev, "%s - structure too big, erroring out.\n", __func__); 71762306a36Sopenharmony_ci break; 71862306a36Sopenharmony_ci } 71962306a36Sopenharmony_ci 72062306a36Sopenharmony_ci dev_dbg(dev, "%s Type = 0x%x\n", __func__, rom_desc->Type); 72162306a36Sopenharmony_ci 72262306a36Sopenharmony_ci /* Skip type 2 record */ 72362306a36Sopenharmony_ci ttype = rom_desc->Type & 0x0f; 72462306a36Sopenharmony_ci if (ttype != I2C_DESC_TYPE_FIRMWARE_BASIC 72562306a36Sopenharmony_ci && ttype != I2C_DESC_TYPE_FIRMWARE_AUTO) { 72662306a36Sopenharmony_ci /* Read the descriptor data */ 72762306a36Sopenharmony_ci status = read_rom(serial, start_address + 72862306a36Sopenharmony_ci sizeof(struct ti_i2c_desc), 72962306a36Sopenharmony_ci le16_to_cpu(rom_desc->Size), 73062306a36Sopenharmony_ci buffer); 73162306a36Sopenharmony_ci if (status) 73262306a36Sopenharmony_ci break; 73362306a36Sopenharmony_ci 73462306a36Sopenharmony_ci status = valid_csum(rom_desc, buffer); 73562306a36Sopenharmony_ci if (status) 73662306a36Sopenharmony_ci break; 73762306a36Sopenharmony_ci } 73862306a36Sopenharmony_ci start_address = start_address + sizeof(struct ti_i2c_desc) + 73962306a36Sopenharmony_ci le16_to_cpu(rom_desc->Size); 74062306a36Sopenharmony_ci 74162306a36Sopenharmony_ci } while ((rom_desc->Type != I2C_DESC_TYPE_ION) && 74262306a36Sopenharmony_ci (start_address < TI_MAX_I2C_SIZE)); 74362306a36Sopenharmony_ci 74462306a36Sopenharmony_ci if ((rom_desc->Type != I2C_DESC_TYPE_ION) || 74562306a36Sopenharmony_ci (start_address > TI_MAX_I2C_SIZE)) 74662306a36Sopenharmony_ci status = -ENODEV; 74762306a36Sopenharmony_ci 74862306a36Sopenharmony_ciout: 74962306a36Sopenharmony_ci kfree(buffer); 75062306a36Sopenharmony_ci kfree(rom_desc); 75162306a36Sopenharmony_ci return status; 75262306a36Sopenharmony_ci} 75362306a36Sopenharmony_ci 75462306a36Sopenharmony_cistatic int get_manuf_info(struct edgeport_serial *serial, u8 *buffer) 75562306a36Sopenharmony_ci{ 75662306a36Sopenharmony_ci int status; 75762306a36Sopenharmony_ci int start_address; 75862306a36Sopenharmony_ci struct ti_i2c_desc *rom_desc; 75962306a36Sopenharmony_ci struct edge_ti_manuf_descriptor *desc; 76062306a36Sopenharmony_ci struct device *dev = &serial->serial->dev->dev; 76162306a36Sopenharmony_ci 76262306a36Sopenharmony_ci rom_desc = kmalloc(sizeof(*rom_desc), GFP_KERNEL); 76362306a36Sopenharmony_ci if (!rom_desc) 76462306a36Sopenharmony_ci return -ENOMEM; 76562306a36Sopenharmony_ci 76662306a36Sopenharmony_ci start_address = get_descriptor_addr(serial, I2C_DESC_TYPE_ION, 76762306a36Sopenharmony_ci rom_desc); 76862306a36Sopenharmony_ci 76962306a36Sopenharmony_ci if (!start_address) { 77062306a36Sopenharmony_ci dev_dbg(dev, "%s - Edge Descriptor not found in I2C\n", __func__); 77162306a36Sopenharmony_ci status = -ENODEV; 77262306a36Sopenharmony_ci goto exit; 77362306a36Sopenharmony_ci } 77462306a36Sopenharmony_ci 77562306a36Sopenharmony_ci /* Read the descriptor data */ 77662306a36Sopenharmony_ci status = read_rom(serial, start_address+sizeof(struct ti_i2c_desc), 77762306a36Sopenharmony_ci le16_to_cpu(rom_desc->Size), buffer); 77862306a36Sopenharmony_ci if (status) 77962306a36Sopenharmony_ci goto exit; 78062306a36Sopenharmony_ci 78162306a36Sopenharmony_ci status = valid_csum(rom_desc, buffer); 78262306a36Sopenharmony_ci 78362306a36Sopenharmony_ci desc = (struct edge_ti_manuf_descriptor *)buffer; 78462306a36Sopenharmony_ci dev_dbg(dev, "%s - IonConfig 0x%x\n", __func__, desc->IonConfig); 78562306a36Sopenharmony_ci dev_dbg(dev, "%s - Version %d\n", __func__, desc->Version); 78662306a36Sopenharmony_ci dev_dbg(dev, "%s - Cpu/Board 0x%x\n", __func__, desc->CpuRev_BoardRev); 78762306a36Sopenharmony_ci dev_dbg(dev, "%s - NumPorts %d\n", __func__, desc->NumPorts); 78862306a36Sopenharmony_ci dev_dbg(dev, "%s - NumVirtualPorts %d\n", __func__, desc->NumVirtualPorts); 78962306a36Sopenharmony_ci dev_dbg(dev, "%s - TotalPorts %d\n", __func__, desc->TotalPorts); 79062306a36Sopenharmony_ci 79162306a36Sopenharmony_ciexit: 79262306a36Sopenharmony_ci kfree(rom_desc); 79362306a36Sopenharmony_ci return status; 79462306a36Sopenharmony_ci} 79562306a36Sopenharmony_ci 79662306a36Sopenharmony_ci/* Build firmware header used for firmware update */ 79762306a36Sopenharmony_cistatic int build_i2c_fw_hdr(u8 *header, const struct firmware *fw) 79862306a36Sopenharmony_ci{ 79962306a36Sopenharmony_ci u8 *buffer; 80062306a36Sopenharmony_ci int buffer_size; 80162306a36Sopenharmony_ci int i; 80262306a36Sopenharmony_ci u8 cs = 0; 80362306a36Sopenharmony_ci struct ti_i2c_desc *i2c_header; 80462306a36Sopenharmony_ci struct ti_i2c_image_header *img_header; 80562306a36Sopenharmony_ci struct ti_i2c_firmware_rec *firmware_rec; 80662306a36Sopenharmony_ci struct edgeport_fw_hdr *fw_hdr = (struct edgeport_fw_hdr *)fw->data; 80762306a36Sopenharmony_ci 80862306a36Sopenharmony_ci /* 80962306a36Sopenharmony_ci * In order to update the I2C firmware we must change the type 2 record 81062306a36Sopenharmony_ci * to type 0xF2. This will force the UMP to come up in Boot Mode. 81162306a36Sopenharmony_ci * Then while in boot mode, the driver will download the latest 81262306a36Sopenharmony_ci * firmware (padded to 15.5k) into the UMP ram. And finally when the 81362306a36Sopenharmony_ci * device comes back up in download mode the driver will cause the new 81462306a36Sopenharmony_ci * firmware to be copied from the UMP Ram to I2C and the firmware will 81562306a36Sopenharmony_ci * update the record type from 0xf2 to 0x02. 81662306a36Sopenharmony_ci */ 81762306a36Sopenharmony_ci 81862306a36Sopenharmony_ci /* 81962306a36Sopenharmony_ci * Allocate a 15.5k buffer + 2 bytes for version number (Firmware 82062306a36Sopenharmony_ci * Record) 82162306a36Sopenharmony_ci */ 82262306a36Sopenharmony_ci buffer_size = (((1024 * 16) - 512 ) + 82362306a36Sopenharmony_ci sizeof(struct ti_i2c_firmware_rec)); 82462306a36Sopenharmony_ci 82562306a36Sopenharmony_ci buffer = kmalloc(buffer_size, GFP_KERNEL); 82662306a36Sopenharmony_ci if (!buffer) 82762306a36Sopenharmony_ci return -ENOMEM; 82862306a36Sopenharmony_ci 82962306a36Sopenharmony_ci /* Set entire image of 0xffs */ 83062306a36Sopenharmony_ci memset(buffer, 0xff, buffer_size); 83162306a36Sopenharmony_ci 83262306a36Sopenharmony_ci /* Copy version number into firmware record */ 83362306a36Sopenharmony_ci firmware_rec = (struct ti_i2c_firmware_rec *)buffer; 83462306a36Sopenharmony_ci 83562306a36Sopenharmony_ci firmware_rec->Ver_Major = fw_hdr->major_version; 83662306a36Sopenharmony_ci firmware_rec->Ver_Minor = fw_hdr->minor_version; 83762306a36Sopenharmony_ci 83862306a36Sopenharmony_ci /* Pointer to fw_down memory image */ 83962306a36Sopenharmony_ci img_header = (struct ti_i2c_image_header *)&fw->data[4]; 84062306a36Sopenharmony_ci 84162306a36Sopenharmony_ci memcpy(buffer + sizeof(struct ti_i2c_firmware_rec), 84262306a36Sopenharmony_ci &fw->data[4 + sizeof(struct ti_i2c_image_header)], 84362306a36Sopenharmony_ci le16_to_cpu(img_header->Length)); 84462306a36Sopenharmony_ci 84562306a36Sopenharmony_ci for (i=0; i < buffer_size; i++) { 84662306a36Sopenharmony_ci cs = (u8)(cs + buffer[i]); 84762306a36Sopenharmony_ci } 84862306a36Sopenharmony_ci 84962306a36Sopenharmony_ci kfree(buffer); 85062306a36Sopenharmony_ci 85162306a36Sopenharmony_ci /* Build new header */ 85262306a36Sopenharmony_ci i2c_header = (struct ti_i2c_desc *)header; 85362306a36Sopenharmony_ci firmware_rec = (struct ti_i2c_firmware_rec*)i2c_header->Data; 85462306a36Sopenharmony_ci 85562306a36Sopenharmony_ci i2c_header->Type = I2C_DESC_TYPE_FIRMWARE_BLANK; 85662306a36Sopenharmony_ci i2c_header->Size = cpu_to_le16(buffer_size); 85762306a36Sopenharmony_ci i2c_header->CheckSum = cs; 85862306a36Sopenharmony_ci firmware_rec->Ver_Major = fw_hdr->major_version; 85962306a36Sopenharmony_ci firmware_rec->Ver_Minor = fw_hdr->minor_version; 86062306a36Sopenharmony_ci 86162306a36Sopenharmony_ci return 0; 86262306a36Sopenharmony_ci} 86362306a36Sopenharmony_ci 86462306a36Sopenharmony_ci/* Try to figure out what type of I2c we have */ 86562306a36Sopenharmony_cistatic int i2c_type_bootmode(struct edgeport_serial *serial) 86662306a36Sopenharmony_ci{ 86762306a36Sopenharmony_ci struct device *dev = &serial->serial->dev->dev; 86862306a36Sopenharmony_ci int status; 86962306a36Sopenharmony_ci u8 *data; 87062306a36Sopenharmony_ci 87162306a36Sopenharmony_ci data = kmalloc(1, GFP_KERNEL); 87262306a36Sopenharmony_ci if (!data) 87362306a36Sopenharmony_ci return -ENOMEM; 87462306a36Sopenharmony_ci 87562306a36Sopenharmony_ci /* Try to read type 2 */ 87662306a36Sopenharmony_ci status = ti_vread_sync(serial->serial->dev, UMPC_MEMORY_READ, 87762306a36Sopenharmony_ci DTK_ADDR_SPACE_I2C_TYPE_II, 0, data, 0x01); 87862306a36Sopenharmony_ci if (status) 87962306a36Sopenharmony_ci dev_dbg(dev, "%s - read 2 status error = %d\n", __func__, status); 88062306a36Sopenharmony_ci else 88162306a36Sopenharmony_ci dev_dbg(dev, "%s - read 2 data = 0x%x\n", __func__, *data); 88262306a36Sopenharmony_ci if ((!status) && (*data == UMP5152 || *data == UMP3410)) { 88362306a36Sopenharmony_ci dev_dbg(dev, "%s - ROM_TYPE_II\n", __func__); 88462306a36Sopenharmony_ci serial->TI_I2C_Type = DTK_ADDR_SPACE_I2C_TYPE_II; 88562306a36Sopenharmony_ci goto out; 88662306a36Sopenharmony_ci } 88762306a36Sopenharmony_ci 88862306a36Sopenharmony_ci /* Try to read type 3 */ 88962306a36Sopenharmony_ci status = ti_vread_sync(serial->serial->dev, UMPC_MEMORY_READ, 89062306a36Sopenharmony_ci DTK_ADDR_SPACE_I2C_TYPE_III, 0, data, 0x01); 89162306a36Sopenharmony_ci if (status) 89262306a36Sopenharmony_ci dev_dbg(dev, "%s - read 3 status error = %d\n", __func__, status); 89362306a36Sopenharmony_ci else 89462306a36Sopenharmony_ci dev_dbg(dev, "%s - read 2 data = 0x%x\n", __func__, *data); 89562306a36Sopenharmony_ci if ((!status) && (*data == UMP5152 || *data == UMP3410)) { 89662306a36Sopenharmony_ci dev_dbg(dev, "%s - ROM_TYPE_III\n", __func__); 89762306a36Sopenharmony_ci serial->TI_I2C_Type = DTK_ADDR_SPACE_I2C_TYPE_III; 89862306a36Sopenharmony_ci goto out; 89962306a36Sopenharmony_ci } 90062306a36Sopenharmony_ci 90162306a36Sopenharmony_ci dev_dbg(dev, "%s - Unknown\n", __func__); 90262306a36Sopenharmony_ci serial->TI_I2C_Type = DTK_ADDR_SPACE_I2C_TYPE_II; 90362306a36Sopenharmony_ci status = -ENODEV; 90462306a36Sopenharmony_ciout: 90562306a36Sopenharmony_ci kfree(data); 90662306a36Sopenharmony_ci return status; 90762306a36Sopenharmony_ci} 90862306a36Sopenharmony_ci 90962306a36Sopenharmony_cistatic int bulk_xfer(struct usb_serial *serial, void *buffer, 91062306a36Sopenharmony_ci int length, int *num_sent) 91162306a36Sopenharmony_ci{ 91262306a36Sopenharmony_ci int status; 91362306a36Sopenharmony_ci 91462306a36Sopenharmony_ci status = usb_bulk_msg(serial->dev, 91562306a36Sopenharmony_ci usb_sndbulkpipe(serial->dev, 91662306a36Sopenharmony_ci serial->port[0]->bulk_out_endpointAddress), 91762306a36Sopenharmony_ci buffer, length, num_sent, 1000); 91862306a36Sopenharmony_ci return status; 91962306a36Sopenharmony_ci} 92062306a36Sopenharmony_ci 92162306a36Sopenharmony_ci/* Download given firmware image to the device (IN BOOT MODE) */ 92262306a36Sopenharmony_cistatic int download_code(struct edgeport_serial *serial, u8 *image, 92362306a36Sopenharmony_ci int image_length) 92462306a36Sopenharmony_ci{ 92562306a36Sopenharmony_ci int status = 0; 92662306a36Sopenharmony_ci int pos; 92762306a36Sopenharmony_ci int transfer; 92862306a36Sopenharmony_ci int done; 92962306a36Sopenharmony_ci 93062306a36Sopenharmony_ci /* Transfer firmware image */ 93162306a36Sopenharmony_ci for (pos = 0; pos < image_length; ) { 93262306a36Sopenharmony_ci /* Read the next buffer from file */ 93362306a36Sopenharmony_ci transfer = image_length - pos; 93462306a36Sopenharmony_ci if (transfer > EDGE_FW_BULK_MAX_PACKET_SIZE) 93562306a36Sopenharmony_ci transfer = EDGE_FW_BULK_MAX_PACKET_SIZE; 93662306a36Sopenharmony_ci 93762306a36Sopenharmony_ci /* Transfer data */ 93862306a36Sopenharmony_ci status = bulk_xfer(serial->serial, &image[pos], 93962306a36Sopenharmony_ci transfer, &done); 94062306a36Sopenharmony_ci if (status) 94162306a36Sopenharmony_ci break; 94262306a36Sopenharmony_ci /* Advance buffer pointer */ 94362306a36Sopenharmony_ci pos += done; 94462306a36Sopenharmony_ci } 94562306a36Sopenharmony_ci 94662306a36Sopenharmony_ci return status; 94762306a36Sopenharmony_ci} 94862306a36Sopenharmony_ci 94962306a36Sopenharmony_ci/* FIXME!!! */ 95062306a36Sopenharmony_cistatic int config_boot_dev(struct usb_device *dev) 95162306a36Sopenharmony_ci{ 95262306a36Sopenharmony_ci return 0; 95362306a36Sopenharmony_ci} 95462306a36Sopenharmony_ci 95562306a36Sopenharmony_cistatic int ti_cpu_rev(struct edge_ti_manuf_descriptor *desc) 95662306a36Sopenharmony_ci{ 95762306a36Sopenharmony_ci return TI_GET_CPU_REVISION(desc->CpuRev_BoardRev); 95862306a36Sopenharmony_ci} 95962306a36Sopenharmony_ci 96062306a36Sopenharmony_cistatic int check_fw_sanity(struct edgeport_serial *serial, 96162306a36Sopenharmony_ci const struct firmware *fw) 96262306a36Sopenharmony_ci{ 96362306a36Sopenharmony_ci u16 length_total; 96462306a36Sopenharmony_ci u8 checksum = 0; 96562306a36Sopenharmony_ci int pos; 96662306a36Sopenharmony_ci struct device *dev = &serial->serial->interface->dev; 96762306a36Sopenharmony_ci struct edgeport_fw_hdr *fw_hdr = (struct edgeport_fw_hdr *)fw->data; 96862306a36Sopenharmony_ci 96962306a36Sopenharmony_ci if (fw->size < sizeof(struct edgeport_fw_hdr)) { 97062306a36Sopenharmony_ci dev_err(dev, "incomplete fw header\n"); 97162306a36Sopenharmony_ci return -EINVAL; 97262306a36Sopenharmony_ci } 97362306a36Sopenharmony_ci 97462306a36Sopenharmony_ci length_total = le16_to_cpu(fw_hdr->length) + 97562306a36Sopenharmony_ci sizeof(struct edgeport_fw_hdr); 97662306a36Sopenharmony_ci 97762306a36Sopenharmony_ci if (fw->size != length_total) { 97862306a36Sopenharmony_ci dev_err(dev, "bad fw size (expected: %u, got: %zu)\n", 97962306a36Sopenharmony_ci length_total, fw->size); 98062306a36Sopenharmony_ci return -EINVAL; 98162306a36Sopenharmony_ci } 98262306a36Sopenharmony_ci 98362306a36Sopenharmony_ci for (pos = sizeof(struct edgeport_fw_hdr); pos < fw->size; ++pos) 98462306a36Sopenharmony_ci checksum += fw->data[pos]; 98562306a36Sopenharmony_ci 98662306a36Sopenharmony_ci if (checksum != fw_hdr->checksum) { 98762306a36Sopenharmony_ci dev_err(dev, "bad fw checksum (expected: 0x%x, got: 0x%x)\n", 98862306a36Sopenharmony_ci fw_hdr->checksum, checksum); 98962306a36Sopenharmony_ci return -EINVAL; 99062306a36Sopenharmony_ci } 99162306a36Sopenharmony_ci 99262306a36Sopenharmony_ci return 0; 99362306a36Sopenharmony_ci} 99462306a36Sopenharmony_ci 99562306a36Sopenharmony_ci/* 99662306a36Sopenharmony_ci * DownloadTIFirmware - Download run-time operating firmware to the TI5052 99762306a36Sopenharmony_ci * 99862306a36Sopenharmony_ci * This routine downloads the main operating code into the TI5052, using the 99962306a36Sopenharmony_ci * boot code already burned into E2PROM or ROM. 100062306a36Sopenharmony_ci */ 100162306a36Sopenharmony_cistatic int download_fw(struct edgeport_serial *serial) 100262306a36Sopenharmony_ci{ 100362306a36Sopenharmony_ci struct device *dev = &serial->serial->interface->dev; 100462306a36Sopenharmony_ci int status = 0; 100562306a36Sopenharmony_ci struct usb_interface_descriptor *interface; 100662306a36Sopenharmony_ci const struct firmware *fw; 100762306a36Sopenharmony_ci const char *fw_name = "edgeport/down3.bin"; 100862306a36Sopenharmony_ci struct edgeport_fw_hdr *fw_hdr; 100962306a36Sopenharmony_ci 101062306a36Sopenharmony_ci status = request_firmware(&fw, fw_name, dev); 101162306a36Sopenharmony_ci if (status) { 101262306a36Sopenharmony_ci dev_err(dev, "Failed to load image \"%s\" err %d\n", 101362306a36Sopenharmony_ci fw_name, status); 101462306a36Sopenharmony_ci return status; 101562306a36Sopenharmony_ci } 101662306a36Sopenharmony_ci 101762306a36Sopenharmony_ci if (check_fw_sanity(serial, fw)) { 101862306a36Sopenharmony_ci status = -EINVAL; 101962306a36Sopenharmony_ci goto out; 102062306a36Sopenharmony_ci } 102162306a36Sopenharmony_ci 102262306a36Sopenharmony_ci fw_hdr = (struct edgeport_fw_hdr *)fw->data; 102362306a36Sopenharmony_ci 102462306a36Sopenharmony_ci /* If on-board version is newer, "fw_version" will be updated later. */ 102562306a36Sopenharmony_ci serial->fw_version = (fw_hdr->major_version << 8) + 102662306a36Sopenharmony_ci fw_hdr->minor_version; 102762306a36Sopenharmony_ci 102862306a36Sopenharmony_ci /* 102962306a36Sopenharmony_ci * This routine is entered by both the BOOT mode and the Download mode 103062306a36Sopenharmony_ci * We can determine which code is running by the reading the config 103162306a36Sopenharmony_ci * descriptor and if we have only one bulk pipe it is in boot mode 103262306a36Sopenharmony_ci */ 103362306a36Sopenharmony_ci serial->product_info.hardware_type = HARDWARE_TYPE_TIUMP; 103462306a36Sopenharmony_ci 103562306a36Sopenharmony_ci /* Default to type 2 i2c */ 103662306a36Sopenharmony_ci serial->TI_I2C_Type = DTK_ADDR_SPACE_I2C_TYPE_II; 103762306a36Sopenharmony_ci 103862306a36Sopenharmony_ci status = choose_config(serial->serial->dev); 103962306a36Sopenharmony_ci if (status) 104062306a36Sopenharmony_ci goto out; 104162306a36Sopenharmony_ci 104262306a36Sopenharmony_ci interface = &serial->serial->interface->cur_altsetting->desc; 104362306a36Sopenharmony_ci if (!interface) { 104462306a36Sopenharmony_ci dev_err(dev, "%s - no interface set, error!\n", __func__); 104562306a36Sopenharmony_ci status = -ENODEV; 104662306a36Sopenharmony_ci goto out; 104762306a36Sopenharmony_ci } 104862306a36Sopenharmony_ci 104962306a36Sopenharmony_ci /* 105062306a36Sopenharmony_ci * Setup initial mode -- the default mode 0 is TI_MODE_CONFIGURING 105162306a36Sopenharmony_ci * if we have more than one endpoint we are definitely in download 105262306a36Sopenharmony_ci * mode 105362306a36Sopenharmony_ci */ 105462306a36Sopenharmony_ci if (interface->bNumEndpoints > 1) { 105562306a36Sopenharmony_ci serial->product_info.TiMode = TI_MODE_DOWNLOAD; 105662306a36Sopenharmony_ci status = do_download_mode(serial, fw); 105762306a36Sopenharmony_ci } else { 105862306a36Sopenharmony_ci /* Otherwise we will remain in configuring mode */ 105962306a36Sopenharmony_ci serial->product_info.TiMode = TI_MODE_CONFIGURING; 106062306a36Sopenharmony_ci status = do_boot_mode(serial, fw); 106162306a36Sopenharmony_ci } 106262306a36Sopenharmony_ci 106362306a36Sopenharmony_ciout: 106462306a36Sopenharmony_ci release_firmware(fw); 106562306a36Sopenharmony_ci return status; 106662306a36Sopenharmony_ci} 106762306a36Sopenharmony_ci 106862306a36Sopenharmony_cistatic int do_download_mode(struct edgeport_serial *serial, 106962306a36Sopenharmony_ci const struct firmware *fw) 107062306a36Sopenharmony_ci{ 107162306a36Sopenharmony_ci struct device *dev = &serial->serial->interface->dev; 107262306a36Sopenharmony_ci int status = 0; 107362306a36Sopenharmony_ci int start_address; 107462306a36Sopenharmony_ci struct edge_ti_manuf_descriptor *ti_manuf_desc; 107562306a36Sopenharmony_ci int download_cur_ver; 107662306a36Sopenharmony_ci int download_new_ver; 107762306a36Sopenharmony_ci struct edgeport_fw_hdr *fw_hdr = (struct edgeport_fw_hdr *)fw->data; 107862306a36Sopenharmony_ci struct ti_i2c_desc *rom_desc; 107962306a36Sopenharmony_ci 108062306a36Sopenharmony_ci dev_dbg(dev, "%s - RUNNING IN DOWNLOAD MODE\n", __func__); 108162306a36Sopenharmony_ci 108262306a36Sopenharmony_ci status = check_i2c_image(serial); 108362306a36Sopenharmony_ci if (status) { 108462306a36Sopenharmony_ci dev_dbg(dev, "%s - DOWNLOAD MODE -- BAD I2C\n", __func__); 108562306a36Sopenharmony_ci return status; 108662306a36Sopenharmony_ci } 108762306a36Sopenharmony_ci 108862306a36Sopenharmony_ci /* 108962306a36Sopenharmony_ci * Validate Hardware version number 109062306a36Sopenharmony_ci * Read Manufacturing Descriptor from TI Based Edgeport 109162306a36Sopenharmony_ci */ 109262306a36Sopenharmony_ci ti_manuf_desc = kmalloc(sizeof(*ti_manuf_desc), GFP_KERNEL); 109362306a36Sopenharmony_ci if (!ti_manuf_desc) 109462306a36Sopenharmony_ci return -ENOMEM; 109562306a36Sopenharmony_ci 109662306a36Sopenharmony_ci status = get_manuf_info(serial, (u8 *)ti_manuf_desc); 109762306a36Sopenharmony_ci if (status) { 109862306a36Sopenharmony_ci kfree(ti_manuf_desc); 109962306a36Sopenharmony_ci return status; 110062306a36Sopenharmony_ci } 110162306a36Sopenharmony_ci 110262306a36Sopenharmony_ci /* Check version number of ION descriptor */ 110362306a36Sopenharmony_ci if (!ignore_cpu_rev && ti_cpu_rev(ti_manuf_desc) < 2) { 110462306a36Sopenharmony_ci dev_dbg(dev, "%s - Wrong CPU Rev %d (Must be 2)\n", 110562306a36Sopenharmony_ci __func__, ti_cpu_rev(ti_manuf_desc)); 110662306a36Sopenharmony_ci kfree(ti_manuf_desc); 110762306a36Sopenharmony_ci return -EINVAL; 110862306a36Sopenharmony_ci } 110962306a36Sopenharmony_ci 111062306a36Sopenharmony_ci rom_desc = kmalloc(sizeof(*rom_desc), GFP_KERNEL); 111162306a36Sopenharmony_ci if (!rom_desc) { 111262306a36Sopenharmony_ci kfree(ti_manuf_desc); 111362306a36Sopenharmony_ci return -ENOMEM; 111462306a36Sopenharmony_ci } 111562306a36Sopenharmony_ci 111662306a36Sopenharmony_ci /* Search for type 2 record (firmware record) */ 111762306a36Sopenharmony_ci start_address = get_descriptor_addr(serial, 111862306a36Sopenharmony_ci I2C_DESC_TYPE_FIRMWARE_BASIC, rom_desc); 111962306a36Sopenharmony_ci if (start_address != 0) { 112062306a36Sopenharmony_ci struct ti_i2c_firmware_rec *firmware_version; 112162306a36Sopenharmony_ci u8 *record; 112262306a36Sopenharmony_ci 112362306a36Sopenharmony_ci dev_dbg(dev, "%s - Found Type FIRMWARE (Type 2) record\n", 112462306a36Sopenharmony_ci __func__); 112562306a36Sopenharmony_ci 112662306a36Sopenharmony_ci firmware_version = kmalloc(sizeof(*firmware_version), 112762306a36Sopenharmony_ci GFP_KERNEL); 112862306a36Sopenharmony_ci if (!firmware_version) { 112962306a36Sopenharmony_ci kfree(rom_desc); 113062306a36Sopenharmony_ci kfree(ti_manuf_desc); 113162306a36Sopenharmony_ci return -ENOMEM; 113262306a36Sopenharmony_ci } 113362306a36Sopenharmony_ci 113462306a36Sopenharmony_ci /* 113562306a36Sopenharmony_ci * Validate version number 113662306a36Sopenharmony_ci * Read the descriptor data 113762306a36Sopenharmony_ci */ 113862306a36Sopenharmony_ci status = read_rom(serial, start_address + 113962306a36Sopenharmony_ci sizeof(struct ti_i2c_desc), 114062306a36Sopenharmony_ci sizeof(struct ti_i2c_firmware_rec), 114162306a36Sopenharmony_ci (u8 *)firmware_version); 114262306a36Sopenharmony_ci if (status) { 114362306a36Sopenharmony_ci kfree(firmware_version); 114462306a36Sopenharmony_ci kfree(rom_desc); 114562306a36Sopenharmony_ci kfree(ti_manuf_desc); 114662306a36Sopenharmony_ci return status; 114762306a36Sopenharmony_ci } 114862306a36Sopenharmony_ci 114962306a36Sopenharmony_ci /* 115062306a36Sopenharmony_ci * Check version number of download with current 115162306a36Sopenharmony_ci * version in I2c 115262306a36Sopenharmony_ci */ 115362306a36Sopenharmony_ci download_cur_ver = (firmware_version->Ver_Major << 8) + 115462306a36Sopenharmony_ci (firmware_version->Ver_Minor); 115562306a36Sopenharmony_ci download_new_ver = (fw_hdr->major_version << 8) + 115662306a36Sopenharmony_ci (fw_hdr->minor_version); 115762306a36Sopenharmony_ci 115862306a36Sopenharmony_ci dev_dbg(dev, "%s - >> FW Versions Device %d.%d Driver %d.%d\n", 115962306a36Sopenharmony_ci __func__, firmware_version->Ver_Major, 116062306a36Sopenharmony_ci firmware_version->Ver_Minor, 116162306a36Sopenharmony_ci fw_hdr->major_version, fw_hdr->minor_version); 116262306a36Sopenharmony_ci 116362306a36Sopenharmony_ci /* 116462306a36Sopenharmony_ci * Check if we have an old version in the I2C and 116562306a36Sopenharmony_ci * update if necessary 116662306a36Sopenharmony_ci */ 116762306a36Sopenharmony_ci if (download_cur_ver < download_new_ver) { 116862306a36Sopenharmony_ci dev_dbg(dev, "%s - Update I2C dld from %d.%d to %d.%d\n", 116962306a36Sopenharmony_ci __func__, 117062306a36Sopenharmony_ci firmware_version->Ver_Major, 117162306a36Sopenharmony_ci firmware_version->Ver_Minor, 117262306a36Sopenharmony_ci fw_hdr->major_version, 117362306a36Sopenharmony_ci fw_hdr->minor_version); 117462306a36Sopenharmony_ci 117562306a36Sopenharmony_ci record = kmalloc(1, GFP_KERNEL); 117662306a36Sopenharmony_ci if (!record) { 117762306a36Sopenharmony_ci kfree(firmware_version); 117862306a36Sopenharmony_ci kfree(rom_desc); 117962306a36Sopenharmony_ci kfree(ti_manuf_desc); 118062306a36Sopenharmony_ci return -ENOMEM; 118162306a36Sopenharmony_ci } 118262306a36Sopenharmony_ci /* 118362306a36Sopenharmony_ci * In order to update the I2C firmware we must 118462306a36Sopenharmony_ci * change the type 2 record to type 0xF2. This 118562306a36Sopenharmony_ci * will force the UMP to come up in Boot Mode. 118662306a36Sopenharmony_ci * Then while in boot mode, the driver will 118762306a36Sopenharmony_ci * download the latest firmware (padded to 118862306a36Sopenharmony_ci * 15.5k) into the UMP ram. Finally when the 118962306a36Sopenharmony_ci * device comes back up in download mode the 119062306a36Sopenharmony_ci * driver will cause the new firmware to be 119162306a36Sopenharmony_ci * copied from the UMP Ram to I2C and the 119262306a36Sopenharmony_ci * firmware will update the record type from 119362306a36Sopenharmony_ci * 0xf2 to 0x02. 119462306a36Sopenharmony_ci */ 119562306a36Sopenharmony_ci *record = I2C_DESC_TYPE_FIRMWARE_BLANK; 119662306a36Sopenharmony_ci 119762306a36Sopenharmony_ci /* 119862306a36Sopenharmony_ci * Change the I2C Firmware record type to 119962306a36Sopenharmony_ci * 0xf2 to trigger an update 120062306a36Sopenharmony_ci */ 120162306a36Sopenharmony_ci status = write_rom(serial, start_address, 120262306a36Sopenharmony_ci sizeof(*record), record); 120362306a36Sopenharmony_ci if (status) { 120462306a36Sopenharmony_ci kfree(record); 120562306a36Sopenharmony_ci kfree(firmware_version); 120662306a36Sopenharmony_ci kfree(rom_desc); 120762306a36Sopenharmony_ci kfree(ti_manuf_desc); 120862306a36Sopenharmony_ci return status; 120962306a36Sopenharmony_ci } 121062306a36Sopenharmony_ci 121162306a36Sopenharmony_ci /* 121262306a36Sopenharmony_ci * verify the write -- must do this in order 121362306a36Sopenharmony_ci * for write to complete before we do the 121462306a36Sopenharmony_ci * hardware reset 121562306a36Sopenharmony_ci */ 121662306a36Sopenharmony_ci status = read_rom(serial, 121762306a36Sopenharmony_ci start_address, 121862306a36Sopenharmony_ci sizeof(*record), 121962306a36Sopenharmony_ci record); 122062306a36Sopenharmony_ci if (status) { 122162306a36Sopenharmony_ci kfree(record); 122262306a36Sopenharmony_ci kfree(firmware_version); 122362306a36Sopenharmony_ci kfree(rom_desc); 122462306a36Sopenharmony_ci kfree(ti_manuf_desc); 122562306a36Sopenharmony_ci return status; 122662306a36Sopenharmony_ci } 122762306a36Sopenharmony_ci 122862306a36Sopenharmony_ci if (*record != I2C_DESC_TYPE_FIRMWARE_BLANK) { 122962306a36Sopenharmony_ci dev_err(dev, "%s - error resetting device\n", 123062306a36Sopenharmony_ci __func__); 123162306a36Sopenharmony_ci kfree(record); 123262306a36Sopenharmony_ci kfree(firmware_version); 123362306a36Sopenharmony_ci kfree(rom_desc); 123462306a36Sopenharmony_ci kfree(ti_manuf_desc); 123562306a36Sopenharmony_ci return -ENODEV; 123662306a36Sopenharmony_ci } 123762306a36Sopenharmony_ci 123862306a36Sopenharmony_ci dev_dbg(dev, "%s - HARDWARE RESET\n", __func__); 123962306a36Sopenharmony_ci 124062306a36Sopenharmony_ci /* Reset UMP -- Back to BOOT MODE */ 124162306a36Sopenharmony_ci status = ti_vsend_sync(serial->serial->dev, 124262306a36Sopenharmony_ci UMPC_HARDWARE_RESET, 124362306a36Sopenharmony_ci 0, 0, NULL, 0, 124462306a36Sopenharmony_ci TI_VSEND_TIMEOUT_DEFAULT); 124562306a36Sopenharmony_ci 124662306a36Sopenharmony_ci dev_dbg(dev, "%s - HARDWARE RESET return %d\n", 124762306a36Sopenharmony_ci __func__, status); 124862306a36Sopenharmony_ci 124962306a36Sopenharmony_ci /* return an error on purpose. */ 125062306a36Sopenharmony_ci kfree(record); 125162306a36Sopenharmony_ci kfree(firmware_version); 125262306a36Sopenharmony_ci kfree(rom_desc); 125362306a36Sopenharmony_ci kfree(ti_manuf_desc); 125462306a36Sopenharmony_ci return -ENODEV; 125562306a36Sopenharmony_ci } 125662306a36Sopenharmony_ci /* Same or newer fw version is already loaded */ 125762306a36Sopenharmony_ci serial->fw_version = download_cur_ver; 125862306a36Sopenharmony_ci kfree(firmware_version); 125962306a36Sopenharmony_ci } 126062306a36Sopenharmony_ci /* Search for type 0xF2 record (firmware blank record) */ 126162306a36Sopenharmony_ci else { 126262306a36Sopenharmony_ci start_address = get_descriptor_addr(serial, 126362306a36Sopenharmony_ci I2C_DESC_TYPE_FIRMWARE_BLANK, rom_desc); 126462306a36Sopenharmony_ci if (start_address != 0) { 126562306a36Sopenharmony_ci#define HEADER_SIZE (sizeof(struct ti_i2c_desc) + \ 126662306a36Sopenharmony_ci sizeof(struct ti_i2c_firmware_rec)) 126762306a36Sopenharmony_ci u8 *header; 126862306a36Sopenharmony_ci u8 *vheader; 126962306a36Sopenharmony_ci 127062306a36Sopenharmony_ci header = kmalloc(HEADER_SIZE, GFP_KERNEL); 127162306a36Sopenharmony_ci if (!header) { 127262306a36Sopenharmony_ci kfree(rom_desc); 127362306a36Sopenharmony_ci kfree(ti_manuf_desc); 127462306a36Sopenharmony_ci return -ENOMEM; 127562306a36Sopenharmony_ci } 127662306a36Sopenharmony_ci 127762306a36Sopenharmony_ci vheader = kmalloc(HEADER_SIZE, GFP_KERNEL); 127862306a36Sopenharmony_ci if (!vheader) { 127962306a36Sopenharmony_ci kfree(header); 128062306a36Sopenharmony_ci kfree(rom_desc); 128162306a36Sopenharmony_ci kfree(ti_manuf_desc); 128262306a36Sopenharmony_ci return -ENOMEM; 128362306a36Sopenharmony_ci } 128462306a36Sopenharmony_ci 128562306a36Sopenharmony_ci dev_dbg(dev, "%s - Found Type BLANK FIRMWARE (Type F2) record\n", 128662306a36Sopenharmony_ci __func__); 128762306a36Sopenharmony_ci 128862306a36Sopenharmony_ci /* 128962306a36Sopenharmony_ci * In order to update the I2C firmware we must change 129062306a36Sopenharmony_ci * the type 2 record to type 0xF2. This will force the 129162306a36Sopenharmony_ci * UMP to come up in Boot Mode. Then while in boot 129262306a36Sopenharmony_ci * mode, the driver will download the latest firmware 129362306a36Sopenharmony_ci * (padded to 15.5k) into the UMP ram. Finally when the 129462306a36Sopenharmony_ci * device comes back up in download mode the driver 129562306a36Sopenharmony_ci * will cause the new firmware to be copied from the 129662306a36Sopenharmony_ci * UMP Ram to I2C and the firmware will update the 129762306a36Sopenharmony_ci * record type from 0xf2 to 0x02. 129862306a36Sopenharmony_ci */ 129962306a36Sopenharmony_ci status = build_i2c_fw_hdr(header, fw); 130062306a36Sopenharmony_ci if (status) { 130162306a36Sopenharmony_ci kfree(vheader); 130262306a36Sopenharmony_ci kfree(header); 130362306a36Sopenharmony_ci kfree(rom_desc); 130462306a36Sopenharmony_ci kfree(ti_manuf_desc); 130562306a36Sopenharmony_ci return -EINVAL; 130662306a36Sopenharmony_ci } 130762306a36Sopenharmony_ci 130862306a36Sopenharmony_ci /* 130962306a36Sopenharmony_ci * Update I2C with type 0xf2 record with correct 131062306a36Sopenharmony_ci * size and checksum 131162306a36Sopenharmony_ci */ 131262306a36Sopenharmony_ci status = write_rom(serial, 131362306a36Sopenharmony_ci start_address, 131462306a36Sopenharmony_ci HEADER_SIZE, 131562306a36Sopenharmony_ci header); 131662306a36Sopenharmony_ci if (status) { 131762306a36Sopenharmony_ci kfree(vheader); 131862306a36Sopenharmony_ci kfree(header); 131962306a36Sopenharmony_ci kfree(rom_desc); 132062306a36Sopenharmony_ci kfree(ti_manuf_desc); 132162306a36Sopenharmony_ci return -EINVAL; 132262306a36Sopenharmony_ci } 132362306a36Sopenharmony_ci 132462306a36Sopenharmony_ci /* 132562306a36Sopenharmony_ci * verify the write -- must do this in order for 132662306a36Sopenharmony_ci * write to complete before we do the hardware reset 132762306a36Sopenharmony_ci */ 132862306a36Sopenharmony_ci status = read_rom(serial, start_address, 132962306a36Sopenharmony_ci HEADER_SIZE, vheader); 133062306a36Sopenharmony_ci 133162306a36Sopenharmony_ci if (status) { 133262306a36Sopenharmony_ci dev_dbg(dev, "%s - can't read header back\n", 133362306a36Sopenharmony_ci __func__); 133462306a36Sopenharmony_ci kfree(vheader); 133562306a36Sopenharmony_ci kfree(header); 133662306a36Sopenharmony_ci kfree(rom_desc); 133762306a36Sopenharmony_ci kfree(ti_manuf_desc); 133862306a36Sopenharmony_ci return status; 133962306a36Sopenharmony_ci } 134062306a36Sopenharmony_ci if (memcmp(vheader, header, HEADER_SIZE)) { 134162306a36Sopenharmony_ci dev_dbg(dev, "%s - write download record failed\n", 134262306a36Sopenharmony_ci __func__); 134362306a36Sopenharmony_ci kfree(vheader); 134462306a36Sopenharmony_ci kfree(header); 134562306a36Sopenharmony_ci kfree(rom_desc); 134662306a36Sopenharmony_ci kfree(ti_manuf_desc); 134762306a36Sopenharmony_ci return -EINVAL; 134862306a36Sopenharmony_ci } 134962306a36Sopenharmony_ci 135062306a36Sopenharmony_ci kfree(vheader); 135162306a36Sopenharmony_ci kfree(header); 135262306a36Sopenharmony_ci 135362306a36Sopenharmony_ci dev_dbg(dev, "%s - Start firmware update\n", __func__); 135462306a36Sopenharmony_ci 135562306a36Sopenharmony_ci /* Tell firmware to copy download image into I2C */ 135662306a36Sopenharmony_ci status = ti_vsend_sync(serial->serial->dev, 135762306a36Sopenharmony_ci UMPC_COPY_DNLD_TO_I2C, 135862306a36Sopenharmony_ci 0, 0, NULL, 0, 135962306a36Sopenharmony_ci TI_VSEND_TIMEOUT_FW_DOWNLOAD); 136062306a36Sopenharmony_ci 136162306a36Sopenharmony_ci dev_dbg(dev, "%s - Update complete 0x%x\n", __func__, 136262306a36Sopenharmony_ci status); 136362306a36Sopenharmony_ci if (status) { 136462306a36Sopenharmony_ci dev_err(dev, 136562306a36Sopenharmony_ci "%s - UMPC_COPY_DNLD_TO_I2C failed\n", 136662306a36Sopenharmony_ci __func__); 136762306a36Sopenharmony_ci kfree(rom_desc); 136862306a36Sopenharmony_ci kfree(ti_manuf_desc); 136962306a36Sopenharmony_ci return status; 137062306a36Sopenharmony_ci } 137162306a36Sopenharmony_ci } 137262306a36Sopenharmony_ci } 137362306a36Sopenharmony_ci 137462306a36Sopenharmony_ci /* The device is running the download code */ 137562306a36Sopenharmony_ci kfree(rom_desc); 137662306a36Sopenharmony_ci kfree(ti_manuf_desc); 137762306a36Sopenharmony_ci return 0; 137862306a36Sopenharmony_ci} 137962306a36Sopenharmony_ci 138062306a36Sopenharmony_cistatic int do_boot_mode(struct edgeport_serial *serial, 138162306a36Sopenharmony_ci const struct firmware *fw) 138262306a36Sopenharmony_ci{ 138362306a36Sopenharmony_ci struct device *dev = &serial->serial->interface->dev; 138462306a36Sopenharmony_ci int status = 0; 138562306a36Sopenharmony_ci struct edge_ti_manuf_descriptor *ti_manuf_desc; 138662306a36Sopenharmony_ci struct edgeport_fw_hdr *fw_hdr = (struct edgeport_fw_hdr *)fw->data; 138762306a36Sopenharmony_ci 138862306a36Sopenharmony_ci dev_dbg(dev, "%s - RUNNING IN BOOT MODE\n", __func__); 138962306a36Sopenharmony_ci 139062306a36Sopenharmony_ci /* Configure the TI device so we can use the BULK pipes for download */ 139162306a36Sopenharmony_ci status = config_boot_dev(serial->serial->dev); 139262306a36Sopenharmony_ci if (status) 139362306a36Sopenharmony_ci return status; 139462306a36Sopenharmony_ci 139562306a36Sopenharmony_ci if (le16_to_cpu(serial->serial->dev->descriptor.idVendor) 139662306a36Sopenharmony_ci != USB_VENDOR_ID_ION) { 139762306a36Sopenharmony_ci dev_dbg(dev, "%s - VID = 0x%x\n", __func__, 139862306a36Sopenharmony_ci le16_to_cpu(serial->serial->dev->descriptor.idVendor)); 139962306a36Sopenharmony_ci serial->TI_I2C_Type = DTK_ADDR_SPACE_I2C_TYPE_II; 140062306a36Sopenharmony_ci goto stayinbootmode; 140162306a36Sopenharmony_ci } 140262306a36Sopenharmony_ci 140362306a36Sopenharmony_ci /* 140462306a36Sopenharmony_ci * We have an ION device (I2c Must be programmed) 140562306a36Sopenharmony_ci * Determine I2C image type 140662306a36Sopenharmony_ci */ 140762306a36Sopenharmony_ci if (i2c_type_bootmode(serial)) 140862306a36Sopenharmony_ci goto stayinbootmode; 140962306a36Sopenharmony_ci 141062306a36Sopenharmony_ci /* Check for ION Vendor ID and that the I2C is valid */ 141162306a36Sopenharmony_ci if (!check_i2c_image(serial)) { 141262306a36Sopenharmony_ci struct ti_i2c_image_header *header; 141362306a36Sopenharmony_ci int i; 141462306a36Sopenharmony_ci u8 cs = 0; 141562306a36Sopenharmony_ci u8 *buffer; 141662306a36Sopenharmony_ci int buffer_size; 141762306a36Sopenharmony_ci 141862306a36Sopenharmony_ci /* 141962306a36Sopenharmony_ci * Validate Hardware version number 142062306a36Sopenharmony_ci * Read Manufacturing Descriptor from TI Based Edgeport 142162306a36Sopenharmony_ci */ 142262306a36Sopenharmony_ci ti_manuf_desc = kmalloc(sizeof(*ti_manuf_desc), GFP_KERNEL); 142362306a36Sopenharmony_ci if (!ti_manuf_desc) 142462306a36Sopenharmony_ci return -ENOMEM; 142562306a36Sopenharmony_ci 142662306a36Sopenharmony_ci status = get_manuf_info(serial, (u8 *)ti_manuf_desc); 142762306a36Sopenharmony_ci if (status) { 142862306a36Sopenharmony_ci kfree(ti_manuf_desc); 142962306a36Sopenharmony_ci goto stayinbootmode; 143062306a36Sopenharmony_ci } 143162306a36Sopenharmony_ci 143262306a36Sopenharmony_ci /* Check for version 2 */ 143362306a36Sopenharmony_ci if (!ignore_cpu_rev && ti_cpu_rev(ti_manuf_desc) < 2) { 143462306a36Sopenharmony_ci dev_dbg(dev, "%s - Wrong CPU Rev %d (Must be 2)\n", 143562306a36Sopenharmony_ci __func__, ti_cpu_rev(ti_manuf_desc)); 143662306a36Sopenharmony_ci kfree(ti_manuf_desc); 143762306a36Sopenharmony_ci goto stayinbootmode; 143862306a36Sopenharmony_ci } 143962306a36Sopenharmony_ci 144062306a36Sopenharmony_ci kfree(ti_manuf_desc); 144162306a36Sopenharmony_ci 144262306a36Sopenharmony_ci /* 144362306a36Sopenharmony_ci * In order to update the I2C firmware we must change the type 144462306a36Sopenharmony_ci * 2 record to type 0xF2. This will force the UMP to come up 144562306a36Sopenharmony_ci * in Boot Mode. Then while in boot mode, the driver will 144662306a36Sopenharmony_ci * download the latest firmware (padded to 15.5k) into the 144762306a36Sopenharmony_ci * UMP ram. Finally when the device comes back up in download 144862306a36Sopenharmony_ci * mode the driver will cause the new firmware to be copied 144962306a36Sopenharmony_ci * from the UMP Ram to I2C and the firmware will update the 145062306a36Sopenharmony_ci * record type from 0xf2 to 0x02. 145162306a36Sopenharmony_ci * 145262306a36Sopenharmony_ci * Do we really have to copy the whole firmware image, 145362306a36Sopenharmony_ci * or could we do this in place! 145462306a36Sopenharmony_ci */ 145562306a36Sopenharmony_ci 145662306a36Sopenharmony_ci /* Allocate a 15.5k buffer + 3 byte header */ 145762306a36Sopenharmony_ci buffer_size = (((1024 * 16) - 512) + 145862306a36Sopenharmony_ci sizeof(struct ti_i2c_image_header)); 145962306a36Sopenharmony_ci buffer = kmalloc(buffer_size, GFP_KERNEL); 146062306a36Sopenharmony_ci if (!buffer) 146162306a36Sopenharmony_ci return -ENOMEM; 146262306a36Sopenharmony_ci 146362306a36Sopenharmony_ci /* Initialize the buffer to 0xff (pad the buffer) */ 146462306a36Sopenharmony_ci memset(buffer, 0xff, buffer_size); 146562306a36Sopenharmony_ci memcpy(buffer, &fw->data[4], fw->size - 4); 146662306a36Sopenharmony_ci 146762306a36Sopenharmony_ci for (i = sizeof(struct ti_i2c_image_header); 146862306a36Sopenharmony_ci i < buffer_size; i++) { 146962306a36Sopenharmony_ci cs = (u8)(cs + buffer[i]); 147062306a36Sopenharmony_ci } 147162306a36Sopenharmony_ci 147262306a36Sopenharmony_ci header = (struct ti_i2c_image_header *)buffer; 147362306a36Sopenharmony_ci 147462306a36Sopenharmony_ci /* update length and checksum after padding */ 147562306a36Sopenharmony_ci header->Length = cpu_to_le16((u16)(buffer_size - 147662306a36Sopenharmony_ci sizeof(struct ti_i2c_image_header))); 147762306a36Sopenharmony_ci header->CheckSum = cs; 147862306a36Sopenharmony_ci 147962306a36Sopenharmony_ci /* Download the operational code */ 148062306a36Sopenharmony_ci dev_dbg(dev, "%s - Downloading operational code image version %d.%d (TI UMP)\n", 148162306a36Sopenharmony_ci __func__, 148262306a36Sopenharmony_ci fw_hdr->major_version, fw_hdr->minor_version); 148362306a36Sopenharmony_ci status = download_code(serial, buffer, buffer_size); 148462306a36Sopenharmony_ci 148562306a36Sopenharmony_ci kfree(buffer); 148662306a36Sopenharmony_ci 148762306a36Sopenharmony_ci if (status) { 148862306a36Sopenharmony_ci dev_dbg(dev, "%s - Error downloading operational code image\n", __func__); 148962306a36Sopenharmony_ci return status; 149062306a36Sopenharmony_ci } 149162306a36Sopenharmony_ci 149262306a36Sopenharmony_ci /* Device will reboot */ 149362306a36Sopenharmony_ci serial->product_info.TiMode = TI_MODE_TRANSITIONING; 149462306a36Sopenharmony_ci 149562306a36Sopenharmony_ci dev_dbg(dev, "%s - Download successful -- Device rebooting...\n", __func__); 149662306a36Sopenharmony_ci 149762306a36Sopenharmony_ci return 1; 149862306a36Sopenharmony_ci } 149962306a36Sopenharmony_ci 150062306a36Sopenharmony_cistayinbootmode: 150162306a36Sopenharmony_ci /* Eprom is invalid or blank stay in boot mode */ 150262306a36Sopenharmony_ci dev_dbg(dev, "%s - STAYING IN BOOT MODE\n", __func__); 150362306a36Sopenharmony_ci serial->product_info.TiMode = TI_MODE_BOOT; 150462306a36Sopenharmony_ci 150562306a36Sopenharmony_ci return 1; 150662306a36Sopenharmony_ci} 150762306a36Sopenharmony_ci 150862306a36Sopenharmony_cistatic int ti_do_config(struct edgeport_port *port, int feature, int on) 150962306a36Sopenharmony_ci{ 151062306a36Sopenharmony_ci on = !!on; /* 1 or 0 not bitmask */ 151162306a36Sopenharmony_ci 151262306a36Sopenharmony_ci return send_port_cmd(port->port, feature, on, NULL, 0); 151362306a36Sopenharmony_ci} 151462306a36Sopenharmony_ci 151562306a36Sopenharmony_cistatic int restore_mcr(struct edgeport_port *port, u8 mcr) 151662306a36Sopenharmony_ci{ 151762306a36Sopenharmony_ci int status = 0; 151862306a36Sopenharmony_ci 151962306a36Sopenharmony_ci dev_dbg(&port->port->dev, "%s - %x\n", __func__, mcr); 152062306a36Sopenharmony_ci 152162306a36Sopenharmony_ci status = ti_do_config(port, UMPC_SET_CLR_DTR, mcr & MCR_DTR); 152262306a36Sopenharmony_ci if (status) 152362306a36Sopenharmony_ci return status; 152462306a36Sopenharmony_ci status = ti_do_config(port, UMPC_SET_CLR_RTS, mcr & MCR_RTS); 152562306a36Sopenharmony_ci if (status) 152662306a36Sopenharmony_ci return status; 152762306a36Sopenharmony_ci return ti_do_config(port, UMPC_SET_CLR_LOOPBACK, mcr & MCR_LOOPBACK); 152862306a36Sopenharmony_ci} 152962306a36Sopenharmony_ci 153062306a36Sopenharmony_ci/* Convert TI LSR to standard UART flags */ 153162306a36Sopenharmony_cistatic u8 map_line_status(u8 ti_lsr) 153262306a36Sopenharmony_ci{ 153362306a36Sopenharmony_ci u8 lsr = 0; 153462306a36Sopenharmony_ci 153562306a36Sopenharmony_ci#define MAP_FLAG(flagUmp, flagUart) \ 153662306a36Sopenharmony_ci if (ti_lsr & flagUmp) \ 153762306a36Sopenharmony_ci lsr |= flagUart; 153862306a36Sopenharmony_ci 153962306a36Sopenharmony_ci MAP_FLAG(UMP_UART_LSR_OV_MASK, LSR_OVER_ERR) /* overrun */ 154062306a36Sopenharmony_ci MAP_FLAG(UMP_UART_LSR_PE_MASK, LSR_PAR_ERR) /* parity error */ 154162306a36Sopenharmony_ci MAP_FLAG(UMP_UART_LSR_FE_MASK, LSR_FRM_ERR) /* framing error */ 154262306a36Sopenharmony_ci MAP_FLAG(UMP_UART_LSR_BR_MASK, LSR_BREAK) /* break detected */ 154362306a36Sopenharmony_ci MAP_FLAG(UMP_UART_LSR_RX_MASK, LSR_RX_AVAIL) /* rx data available */ 154462306a36Sopenharmony_ci MAP_FLAG(UMP_UART_LSR_TX_MASK, LSR_TX_EMPTY) /* tx hold reg empty */ 154562306a36Sopenharmony_ci 154662306a36Sopenharmony_ci#undef MAP_FLAG 154762306a36Sopenharmony_ci 154862306a36Sopenharmony_ci return lsr; 154962306a36Sopenharmony_ci} 155062306a36Sopenharmony_ci 155162306a36Sopenharmony_cistatic void handle_new_msr(struct edgeport_port *edge_port, u8 msr) 155262306a36Sopenharmony_ci{ 155362306a36Sopenharmony_ci struct async_icount *icount; 155462306a36Sopenharmony_ci struct tty_struct *tty; 155562306a36Sopenharmony_ci 155662306a36Sopenharmony_ci dev_dbg(&edge_port->port->dev, "%s - %02x\n", __func__, msr); 155762306a36Sopenharmony_ci 155862306a36Sopenharmony_ci if (msr & (EDGEPORT_MSR_DELTA_CTS | EDGEPORT_MSR_DELTA_DSR | 155962306a36Sopenharmony_ci EDGEPORT_MSR_DELTA_RI | EDGEPORT_MSR_DELTA_CD)) { 156062306a36Sopenharmony_ci icount = &edge_port->port->icount; 156162306a36Sopenharmony_ci 156262306a36Sopenharmony_ci /* update input line counters */ 156362306a36Sopenharmony_ci if (msr & EDGEPORT_MSR_DELTA_CTS) 156462306a36Sopenharmony_ci icount->cts++; 156562306a36Sopenharmony_ci if (msr & EDGEPORT_MSR_DELTA_DSR) 156662306a36Sopenharmony_ci icount->dsr++; 156762306a36Sopenharmony_ci if (msr & EDGEPORT_MSR_DELTA_CD) 156862306a36Sopenharmony_ci icount->dcd++; 156962306a36Sopenharmony_ci if (msr & EDGEPORT_MSR_DELTA_RI) 157062306a36Sopenharmony_ci icount->rng++; 157162306a36Sopenharmony_ci wake_up_interruptible(&edge_port->port->port.delta_msr_wait); 157262306a36Sopenharmony_ci } 157362306a36Sopenharmony_ci 157462306a36Sopenharmony_ci /* Save the new modem status */ 157562306a36Sopenharmony_ci edge_port->shadow_msr = msr & 0xf0; 157662306a36Sopenharmony_ci 157762306a36Sopenharmony_ci tty = tty_port_tty_get(&edge_port->port->port); 157862306a36Sopenharmony_ci /* handle CTS flow control */ 157962306a36Sopenharmony_ci if (tty && C_CRTSCTS(tty)) { 158062306a36Sopenharmony_ci if (msr & EDGEPORT_MSR_CTS) 158162306a36Sopenharmony_ci tty_wakeup(tty); 158262306a36Sopenharmony_ci } 158362306a36Sopenharmony_ci tty_kref_put(tty); 158462306a36Sopenharmony_ci} 158562306a36Sopenharmony_ci 158662306a36Sopenharmony_cistatic void handle_new_lsr(struct edgeport_port *edge_port, int lsr_data, 158762306a36Sopenharmony_ci u8 lsr, u8 data) 158862306a36Sopenharmony_ci{ 158962306a36Sopenharmony_ci struct async_icount *icount; 159062306a36Sopenharmony_ci u8 new_lsr = (u8)(lsr & (u8)(LSR_OVER_ERR | LSR_PAR_ERR | 159162306a36Sopenharmony_ci LSR_FRM_ERR | LSR_BREAK)); 159262306a36Sopenharmony_ci 159362306a36Sopenharmony_ci dev_dbg(&edge_port->port->dev, "%s - %02x\n", __func__, new_lsr); 159462306a36Sopenharmony_ci 159562306a36Sopenharmony_ci edge_port->shadow_lsr = lsr; 159662306a36Sopenharmony_ci 159762306a36Sopenharmony_ci if (new_lsr & LSR_BREAK) 159862306a36Sopenharmony_ci /* 159962306a36Sopenharmony_ci * Parity and Framing errors only count if they 160062306a36Sopenharmony_ci * occur exclusive of a break being received. 160162306a36Sopenharmony_ci */ 160262306a36Sopenharmony_ci new_lsr &= (u8)(LSR_OVER_ERR | LSR_BREAK); 160362306a36Sopenharmony_ci 160462306a36Sopenharmony_ci /* Place LSR data byte into Rx buffer */ 160562306a36Sopenharmony_ci if (lsr_data) 160662306a36Sopenharmony_ci edge_tty_recv(edge_port->port, &data, 1); 160762306a36Sopenharmony_ci 160862306a36Sopenharmony_ci /* update input line counters */ 160962306a36Sopenharmony_ci icount = &edge_port->port->icount; 161062306a36Sopenharmony_ci if (new_lsr & LSR_BREAK) 161162306a36Sopenharmony_ci icount->brk++; 161262306a36Sopenharmony_ci if (new_lsr & LSR_OVER_ERR) 161362306a36Sopenharmony_ci icount->overrun++; 161462306a36Sopenharmony_ci if (new_lsr & LSR_PAR_ERR) 161562306a36Sopenharmony_ci icount->parity++; 161662306a36Sopenharmony_ci if (new_lsr & LSR_FRM_ERR) 161762306a36Sopenharmony_ci icount->frame++; 161862306a36Sopenharmony_ci} 161962306a36Sopenharmony_ci 162062306a36Sopenharmony_cistatic void edge_interrupt_callback(struct urb *urb) 162162306a36Sopenharmony_ci{ 162262306a36Sopenharmony_ci struct edgeport_serial *edge_serial = urb->context; 162362306a36Sopenharmony_ci struct usb_serial_port *port; 162462306a36Sopenharmony_ci struct edgeport_port *edge_port; 162562306a36Sopenharmony_ci struct device *dev; 162662306a36Sopenharmony_ci unsigned char *data = urb->transfer_buffer; 162762306a36Sopenharmony_ci int length = urb->actual_length; 162862306a36Sopenharmony_ci int port_number; 162962306a36Sopenharmony_ci int function; 163062306a36Sopenharmony_ci int retval; 163162306a36Sopenharmony_ci u8 lsr; 163262306a36Sopenharmony_ci u8 msr; 163362306a36Sopenharmony_ci int status = urb->status; 163462306a36Sopenharmony_ci 163562306a36Sopenharmony_ci switch (status) { 163662306a36Sopenharmony_ci case 0: 163762306a36Sopenharmony_ci /* success */ 163862306a36Sopenharmony_ci break; 163962306a36Sopenharmony_ci case -ECONNRESET: 164062306a36Sopenharmony_ci case -ENOENT: 164162306a36Sopenharmony_ci case -ESHUTDOWN: 164262306a36Sopenharmony_ci /* this urb is terminated, clean up */ 164362306a36Sopenharmony_ci dev_dbg(&urb->dev->dev, "%s - urb shutting down with status: %d\n", 164462306a36Sopenharmony_ci __func__, status); 164562306a36Sopenharmony_ci return; 164662306a36Sopenharmony_ci default: 164762306a36Sopenharmony_ci dev_err(&urb->dev->dev, "%s - nonzero urb status received: " 164862306a36Sopenharmony_ci "%d\n", __func__, status); 164962306a36Sopenharmony_ci goto exit; 165062306a36Sopenharmony_ci } 165162306a36Sopenharmony_ci 165262306a36Sopenharmony_ci if (!length) { 165362306a36Sopenharmony_ci dev_dbg(&urb->dev->dev, "%s - no data in urb\n", __func__); 165462306a36Sopenharmony_ci goto exit; 165562306a36Sopenharmony_ci } 165662306a36Sopenharmony_ci 165762306a36Sopenharmony_ci dev = &edge_serial->serial->dev->dev; 165862306a36Sopenharmony_ci usb_serial_debug_data(dev, __func__, length, data); 165962306a36Sopenharmony_ci 166062306a36Sopenharmony_ci if (length != 2) { 166162306a36Sopenharmony_ci dev_dbg(dev, "%s - expecting packet of size 2, got %d\n", __func__, length); 166262306a36Sopenharmony_ci goto exit; 166362306a36Sopenharmony_ci } 166462306a36Sopenharmony_ci 166562306a36Sopenharmony_ci port_number = TIUMP_GET_PORT_FROM_CODE(data[0]); 166662306a36Sopenharmony_ci function = TIUMP_GET_FUNC_FROM_CODE(data[0]); 166762306a36Sopenharmony_ci dev_dbg(dev, "%s - port_number %d, function %d, info 0x%x\n", __func__, 166862306a36Sopenharmony_ci port_number, function, data[1]); 166962306a36Sopenharmony_ci 167062306a36Sopenharmony_ci if (port_number >= edge_serial->serial->num_ports) { 167162306a36Sopenharmony_ci dev_err(dev, "bad port number %d\n", port_number); 167262306a36Sopenharmony_ci goto exit; 167362306a36Sopenharmony_ci } 167462306a36Sopenharmony_ci 167562306a36Sopenharmony_ci port = edge_serial->serial->port[port_number]; 167662306a36Sopenharmony_ci edge_port = usb_get_serial_port_data(port); 167762306a36Sopenharmony_ci if (!edge_port) { 167862306a36Sopenharmony_ci dev_dbg(dev, "%s - edge_port not found\n", __func__); 167962306a36Sopenharmony_ci return; 168062306a36Sopenharmony_ci } 168162306a36Sopenharmony_ci switch (function) { 168262306a36Sopenharmony_ci case TIUMP_INTERRUPT_CODE_LSR: 168362306a36Sopenharmony_ci lsr = map_line_status(data[1]); 168462306a36Sopenharmony_ci if (lsr & UMP_UART_LSR_DATA_MASK) { 168562306a36Sopenharmony_ci /* 168662306a36Sopenharmony_ci * Save the LSR event for bulk read completion routine 168762306a36Sopenharmony_ci */ 168862306a36Sopenharmony_ci dev_dbg(dev, "%s - LSR Event Port %u LSR Status = %02x\n", 168962306a36Sopenharmony_ci __func__, port_number, lsr); 169062306a36Sopenharmony_ci edge_port->lsr_event = 1; 169162306a36Sopenharmony_ci edge_port->lsr_mask = lsr; 169262306a36Sopenharmony_ci } else { 169362306a36Sopenharmony_ci dev_dbg(dev, "%s - ===== Port %d LSR Status = %02x ======\n", 169462306a36Sopenharmony_ci __func__, port_number, lsr); 169562306a36Sopenharmony_ci handle_new_lsr(edge_port, 0, lsr, 0); 169662306a36Sopenharmony_ci } 169762306a36Sopenharmony_ci break; 169862306a36Sopenharmony_ci 169962306a36Sopenharmony_ci case TIUMP_INTERRUPT_CODE_MSR: /* MSR */ 170062306a36Sopenharmony_ci /* Copy MSR from UMP */ 170162306a36Sopenharmony_ci msr = data[1]; 170262306a36Sopenharmony_ci dev_dbg(dev, "%s - ===== Port %u MSR Status = %02x ======\n", 170362306a36Sopenharmony_ci __func__, port_number, msr); 170462306a36Sopenharmony_ci handle_new_msr(edge_port, msr); 170562306a36Sopenharmony_ci break; 170662306a36Sopenharmony_ci 170762306a36Sopenharmony_ci default: 170862306a36Sopenharmony_ci dev_err(&urb->dev->dev, 170962306a36Sopenharmony_ci "%s - Unknown Interrupt code from UMP %x\n", 171062306a36Sopenharmony_ci __func__, data[1]); 171162306a36Sopenharmony_ci break; 171262306a36Sopenharmony_ci 171362306a36Sopenharmony_ci } 171462306a36Sopenharmony_ci 171562306a36Sopenharmony_ciexit: 171662306a36Sopenharmony_ci retval = usb_submit_urb(urb, GFP_ATOMIC); 171762306a36Sopenharmony_ci if (retval) 171862306a36Sopenharmony_ci dev_err(&urb->dev->dev, 171962306a36Sopenharmony_ci "%s - usb_submit_urb failed with result %d\n", 172062306a36Sopenharmony_ci __func__, retval); 172162306a36Sopenharmony_ci} 172262306a36Sopenharmony_ci 172362306a36Sopenharmony_cistatic void edge_bulk_in_callback(struct urb *urb) 172462306a36Sopenharmony_ci{ 172562306a36Sopenharmony_ci struct edgeport_port *edge_port = urb->context; 172662306a36Sopenharmony_ci struct device *dev = &edge_port->port->dev; 172762306a36Sopenharmony_ci unsigned char *data = urb->transfer_buffer; 172862306a36Sopenharmony_ci unsigned long flags; 172962306a36Sopenharmony_ci int retval = 0; 173062306a36Sopenharmony_ci int port_number; 173162306a36Sopenharmony_ci int status = urb->status; 173262306a36Sopenharmony_ci 173362306a36Sopenharmony_ci switch (status) { 173462306a36Sopenharmony_ci case 0: 173562306a36Sopenharmony_ci /* success */ 173662306a36Sopenharmony_ci break; 173762306a36Sopenharmony_ci case -ECONNRESET: 173862306a36Sopenharmony_ci case -ENOENT: 173962306a36Sopenharmony_ci case -ESHUTDOWN: 174062306a36Sopenharmony_ci /* this urb is terminated, clean up */ 174162306a36Sopenharmony_ci dev_dbg(&urb->dev->dev, "%s - urb shutting down with status: %d\n", __func__, status); 174262306a36Sopenharmony_ci return; 174362306a36Sopenharmony_ci default: 174462306a36Sopenharmony_ci dev_err(&urb->dev->dev, "%s - nonzero read bulk status received: %d\n", __func__, status); 174562306a36Sopenharmony_ci } 174662306a36Sopenharmony_ci 174762306a36Sopenharmony_ci if (status == -EPIPE) 174862306a36Sopenharmony_ci goto exit; 174962306a36Sopenharmony_ci 175062306a36Sopenharmony_ci if (status) { 175162306a36Sopenharmony_ci dev_err(&urb->dev->dev, "%s - stopping read!\n", __func__); 175262306a36Sopenharmony_ci return; 175362306a36Sopenharmony_ci } 175462306a36Sopenharmony_ci 175562306a36Sopenharmony_ci port_number = edge_port->port->port_number; 175662306a36Sopenharmony_ci 175762306a36Sopenharmony_ci if (urb->actual_length > 0 && edge_port->lsr_event) { 175862306a36Sopenharmony_ci edge_port->lsr_event = 0; 175962306a36Sopenharmony_ci dev_dbg(dev, "%s ===== Port %u LSR Status = %02x, Data = %02x ======\n", 176062306a36Sopenharmony_ci __func__, port_number, edge_port->lsr_mask, *data); 176162306a36Sopenharmony_ci handle_new_lsr(edge_port, 1, edge_port->lsr_mask, *data); 176262306a36Sopenharmony_ci /* Adjust buffer length/pointer */ 176362306a36Sopenharmony_ci --urb->actual_length; 176462306a36Sopenharmony_ci ++data; 176562306a36Sopenharmony_ci } 176662306a36Sopenharmony_ci 176762306a36Sopenharmony_ci if (urb->actual_length) { 176862306a36Sopenharmony_ci usb_serial_debug_data(dev, __func__, urb->actual_length, data); 176962306a36Sopenharmony_ci if (edge_port->close_pending) 177062306a36Sopenharmony_ci dev_dbg(dev, "%s - close pending, dropping data on the floor\n", 177162306a36Sopenharmony_ci __func__); 177262306a36Sopenharmony_ci else 177362306a36Sopenharmony_ci edge_tty_recv(edge_port->port, data, 177462306a36Sopenharmony_ci urb->actual_length); 177562306a36Sopenharmony_ci edge_port->port->icount.rx += urb->actual_length; 177662306a36Sopenharmony_ci } 177762306a36Sopenharmony_ci 177862306a36Sopenharmony_ciexit: 177962306a36Sopenharmony_ci /* continue read unless stopped */ 178062306a36Sopenharmony_ci spin_lock_irqsave(&edge_port->ep_lock, flags); 178162306a36Sopenharmony_ci if (edge_port->ep_read_urb_state == EDGE_READ_URB_RUNNING) 178262306a36Sopenharmony_ci retval = usb_submit_urb(urb, GFP_ATOMIC); 178362306a36Sopenharmony_ci else if (edge_port->ep_read_urb_state == EDGE_READ_URB_STOPPING) 178462306a36Sopenharmony_ci edge_port->ep_read_urb_state = EDGE_READ_URB_STOPPED; 178562306a36Sopenharmony_ci 178662306a36Sopenharmony_ci spin_unlock_irqrestore(&edge_port->ep_lock, flags); 178762306a36Sopenharmony_ci if (retval) 178862306a36Sopenharmony_ci dev_err(dev, "%s - usb_submit_urb failed with result %d\n", __func__, retval); 178962306a36Sopenharmony_ci} 179062306a36Sopenharmony_ci 179162306a36Sopenharmony_cistatic void edge_tty_recv(struct usb_serial_port *port, unsigned char *data, 179262306a36Sopenharmony_ci int length) 179362306a36Sopenharmony_ci{ 179462306a36Sopenharmony_ci int queued; 179562306a36Sopenharmony_ci 179662306a36Sopenharmony_ci queued = tty_insert_flip_string(&port->port, data, length); 179762306a36Sopenharmony_ci if (queued < length) 179862306a36Sopenharmony_ci dev_err(&port->dev, "%s - dropping data, %d bytes lost\n", 179962306a36Sopenharmony_ci __func__, length - queued); 180062306a36Sopenharmony_ci tty_flip_buffer_push(&port->port); 180162306a36Sopenharmony_ci} 180262306a36Sopenharmony_ci 180362306a36Sopenharmony_cistatic void edge_bulk_out_callback(struct urb *urb) 180462306a36Sopenharmony_ci{ 180562306a36Sopenharmony_ci struct usb_serial_port *port = urb->context; 180662306a36Sopenharmony_ci struct edgeport_port *edge_port = usb_get_serial_port_data(port); 180762306a36Sopenharmony_ci int status = urb->status; 180862306a36Sopenharmony_ci struct tty_struct *tty; 180962306a36Sopenharmony_ci 181062306a36Sopenharmony_ci edge_port->ep_write_urb_in_use = 0; 181162306a36Sopenharmony_ci 181262306a36Sopenharmony_ci switch (status) { 181362306a36Sopenharmony_ci case 0: 181462306a36Sopenharmony_ci /* success */ 181562306a36Sopenharmony_ci break; 181662306a36Sopenharmony_ci case -ECONNRESET: 181762306a36Sopenharmony_ci case -ENOENT: 181862306a36Sopenharmony_ci case -ESHUTDOWN: 181962306a36Sopenharmony_ci /* this urb is terminated, clean up */ 182062306a36Sopenharmony_ci dev_dbg(&urb->dev->dev, "%s - urb shutting down with status: %d\n", 182162306a36Sopenharmony_ci __func__, status); 182262306a36Sopenharmony_ci return; 182362306a36Sopenharmony_ci default: 182462306a36Sopenharmony_ci dev_err_console(port, "%s - nonzero write bulk status " 182562306a36Sopenharmony_ci "received: %d\n", __func__, status); 182662306a36Sopenharmony_ci } 182762306a36Sopenharmony_ci 182862306a36Sopenharmony_ci /* send any buffered data */ 182962306a36Sopenharmony_ci tty = tty_port_tty_get(&port->port); 183062306a36Sopenharmony_ci edge_send(port, tty); 183162306a36Sopenharmony_ci tty_kref_put(tty); 183262306a36Sopenharmony_ci} 183362306a36Sopenharmony_ci 183462306a36Sopenharmony_cistatic int edge_open(struct tty_struct *tty, struct usb_serial_port *port) 183562306a36Sopenharmony_ci{ 183662306a36Sopenharmony_ci struct edgeport_port *edge_port = usb_get_serial_port_data(port); 183762306a36Sopenharmony_ci struct edgeport_serial *edge_serial; 183862306a36Sopenharmony_ci struct usb_device *dev; 183962306a36Sopenharmony_ci struct urb *urb; 184062306a36Sopenharmony_ci int status; 184162306a36Sopenharmony_ci u16 open_settings; 184262306a36Sopenharmony_ci u8 transaction_timeout; 184362306a36Sopenharmony_ci 184462306a36Sopenharmony_ci if (edge_port == NULL) 184562306a36Sopenharmony_ci return -ENODEV; 184662306a36Sopenharmony_ci 184762306a36Sopenharmony_ci dev = port->serial->dev; 184862306a36Sopenharmony_ci 184962306a36Sopenharmony_ci /* turn off loopback */ 185062306a36Sopenharmony_ci status = ti_do_config(edge_port, UMPC_SET_CLR_LOOPBACK, 0); 185162306a36Sopenharmony_ci if (status) { 185262306a36Sopenharmony_ci dev_err(&port->dev, 185362306a36Sopenharmony_ci "%s - cannot send clear loopback command, %d\n", 185462306a36Sopenharmony_ci __func__, status); 185562306a36Sopenharmony_ci return status; 185662306a36Sopenharmony_ci } 185762306a36Sopenharmony_ci 185862306a36Sopenharmony_ci /* set up the port settings */ 185962306a36Sopenharmony_ci if (tty) 186062306a36Sopenharmony_ci edge_set_termios(tty, port, &tty->termios); 186162306a36Sopenharmony_ci 186262306a36Sopenharmony_ci /* open up the port */ 186362306a36Sopenharmony_ci 186462306a36Sopenharmony_ci /* milliseconds to timeout for DMA transfer */ 186562306a36Sopenharmony_ci transaction_timeout = 2; 186662306a36Sopenharmony_ci 186762306a36Sopenharmony_ci edge_port->ump_read_timeout = 186862306a36Sopenharmony_ci max(20, ((transaction_timeout * 3) / 2)); 186962306a36Sopenharmony_ci 187062306a36Sopenharmony_ci /* milliseconds to timeout for DMA transfer */ 187162306a36Sopenharmony_ci open_settings = (u8)(UMP_DMA_MODE_CONTINOUS | 187262306a36Sopenharmony_ci UMP_PIPE_TRANS_TIMEOUT_ENA | 187362306a36Sopenharmony_ci (transaction_timeout << 2)); 187462306a36Sopenharmony_ci 187562306a36Sopenharmony_ci dev_dbg(&port->dev, "%s - Sending UMPC_OPEN_PORT\n", __func__); 187662306a36Sopenharmony_ci 187762306a36Sopenharmony_ci /* Tell TI to open and start the port */ 187862306a36Sopenharmony_ci status = send_port_cmd(port, UMPC_OPEN_PORT, open_settings, NULL, 0); 187962306a36Sopenharmony_ci if (status) { 188062306a36Sopenharmony_ci dev_err(&port->dev, "%s - cannot send open command, %d\n", 188162306a36Sopenharmony_ci __func__, status); 188262306a36Sopenharmony_ci return status; 188362306a36Sopenharmony_ci } 188462306a36Sopenharmony_ci 188562306a36Sopenharmony_ci /* Start the DMA? */ 188662306a36Sopenharmony_ci status = send_port_cmd(port, UMPC_START_PORT, 0, NULL, 0); 188762306a36Sopenharmony_ci if (status) { 188862306a36Sopenharmony_ci dev_err(&port->dev, "%s - cannot send start DMA command, %d\n", 188962306a36Sopenharmony_ci __func__, status); 189062306a36Sopenharmony_ci return status; 189162306a36Sopenharmony_ci } 189262306a36Sopenharmony_ci 189362306a36Sopenharmony_ci /* Clear TX and RX buffers in UMP */ 189462306a36Sopenharmony_ci status = purge_port(port, UMP_PORT_DIR_OUT | UMP_PORT_DIR_IN); 189562306a36Sopenharmony_ci if (status) { 189662306a36Sopenharmony_ci dev_err(&port->dev, 189762306a36Sopenharmony_ci "%s - cannot send clear buffers command, %d\n", 189862306a36Sopenharmony_ci __func__, status); 189962306a36Sopenharmony_ci return status; 190062306a36Sopenharmony_ci } 190162306a36Sopenharmony_ci 190262306a36Sopenharmony_ci /* Read Initial MSR */ 190362306a36Sopenharmony_ci status = read_port_cmd(port, UMPC_READ_MSR, 0, &edge_port->shadow_msr, 1); 190462306a36Sopenharmony_ci if (status) { 190562306a36Sopenharmony_ci dev_err(&port->dev, "%s - cannot send read MSR command, %d\n", 190662306a36Sopenharmony_ci __func__, status); 190762306a36Sopenharmony_ci return status; 190862306a36Sopenharmony_ci } 190962306a36Sopenharmony_ci 191062306a36Sopenharmony_ci dev_dbg(&port->dev, "ShadowMSR 0x%X\n", edge_port->shadow_msr); 191162306a36Sopenharmony_ci 191262306a36Sopenharmony_ci /* Set Initial MCR */ 191362306a36Sopenharmony_ci edge_port->shadow_mcr = MCR_RTS | MCR_DTR; 191462306a36Sopenharmony_ci dev_dbg(&port->dev, "ShadowMCR 0x%X\n", edge_port->shadow_mcr); 191562306a36Sopenharmony_ci 191662306a36Sopenharmony_ci edge_serial = edge_port->edge_serial; 191762306a36Sopenharmony_ci if (mutex_lock_interruptible(&edge_serial->es_lock)) 191862306a36Sopenharmony_ci return -ERESTARTSYS; 191962306a36Sopenharmony_ci if (edge_serial->num_ports_open == 0) { 192062306a36Sopenharmony_ci /* we are the first port to open, post the interrupt urb */ 192162306a36Sopenharmony_ci urb = edge_serial->serial->port[0]->interrupt_in_urb; 192262306a36Sopenharmony_ci urb->context = edge_serial; 192362306a36Sopenharmony_ci status = usb_submit_urb(urb, GFP_KERNEL); 192462306a36Sopenharmony_ci if (status) { 192562306a36Sopenharmony_ci dev_err(&port->dev, 192662306a36Sopenharmony_ci "%s - usb_submit_urb failed with value %d\n", 192762306a36Sopenharmony_ci __func__, status); 192862306a36Sopenharmony_ci goto release_es_lock; 192962306a36Sopenharmony_ci } 193062306a36Sopenharmony_ci } 193162306a36Sopenharmony_ci 193262306a36Sopenharmony_ci /* 193362306a36Sopenharmony_ci * reset the data toggle on the bulk endpoints to work around bug in 193462306a36Sopenharmony_ci * host controllers where things get out of sync some times 193562306a36Sopenharmony_ci */ 193662306a36Sopenharmony_ci usb_clear_halt(dev, port->write_urb->pipe); 193762306a36Sopenharmony_ci usb_clear_halt(dev, port->read_urb->pipe); 193862306a36Sopenharmony_ci 193962306a36Sopenharmony_ci /* start up our bulk read urb */ 194062306a36Sopenharmony_ci urb = port->read_urb; 194162306a36Sopenharmony_ci edge_port->ep_read_urb_state = EDGE_READ_URB_RUNNING; 194262306a36Sopenharmony_ci urb->context = edge_port; 194362306a36Sopenharmony_ci status = usb_submit_urb(urb, GFP_KERNEL); 194462306a36Sopenharmony_ci if (status) { 194562306a36Sopenharmony_ci dev_err(&port->dev, 194662306a36Sopenharmony_ci "%s - read bulk usb_submit_urb failed with value %d\n", 194762306a36Sopenharmony_ci __func__, status); 194862306a36Sopenharmony_ci goto unlink_int_urb; 194962306a36Sopenharmony_ci } 195062306a36Sopenharmony_ci 195162306a36Sopenharmony_ci ++edge_serial->num_ports_open; 195262306a36Sopenharmony_ci 195362306a36Sopenharmony_ci goto release_es_lock; 195462306a36Sopenharmony_ci 195562306a36Sopenharmony_ciunlink_int_urb: 195662306a36Sopenharmony_ci if (edge_port->edge_serial->num_ports_open == 0) 195762306a36Sopenharmony_ci usb_kill_urb(port->serial->port[0]->interrupt_in_urb); 195862306a36Sopenharmony_cirelease_es_lock: 195962306a36Sopenharmony_ci mutex_unlock(&edge_serial->es_lock); 196062306a36Sopenharmony_ci return status; 196162306a36Sopenharmony_ci} 196262306a36Sopenharmony_ci 196362306a36Sopenharmony_cistatic void edge_close(struct usb_serial_port *port) 196462306a36Sopenharmony_ci{ 196562306a36Sopenharmony_ci struct edgeport_serial *edge_serial; 196662306a36Sopenharmony_ci struct edgeport_port *edge_port; 196762306a36Sopenharmony_ci unsigned long flags; 196862306a36Sopenharmony_ci 196962306a36Sopenharmony_ci edge_serial = usb_get_serial_data(port->serial); 197062306a36Sopenharmony_ci edge_port = usb_get_serial_port_data(port); 197162306a36Sopenharmony_ci if (edge_serial == NULL || edge_port == NULL) 197262306a36Sopenharmony_ci return; 197362306a36Sopenharmony_ci 197462306a36Sopenharmony_ci /* 197562306a36Sopenharmony_ci * The bulkreadcompletion routine will check 197662306a36Sopenharmony_ci * this flag and dump add read data 197762306a36Sopenharmony_ci */ 197862306a36Sopenharmony_ci edge_port->close_pending = 1; 197962306a36Sopenharmony_ci 198062306a36Sopenharmony_ci usb_kill_urb(port->read_urb); 198162306a36Sopenharmony_ci usb_kill_urb(port->write_urb); 198262306a36Sopenharmony_ci edge_port->ep_write_urb_in_use = 0; 198362306a36Sopenharmony_ci spin_lock_irqsave(&edge_port->ep_lock, flags); 198462306a36Sopenharmony_ci kfifo_reset_out(&port->write_fifo); 198562306a36Sopenharmony_ci spin_unlock_irqrestore(&edge_port->ep_lock, flags); 198662306a36Sopenharmony_ci 198762306a36Sopenharmony_ci dev_dbg(&port->dev, "%s - send umpc_close_port\n", __func__); 198862306a36Sopenharmony_ci send_port_cmd(port, UMPC_CLOSE_PORT, 0, NULL, 0); 198962306a36Sopenharmony_ci 199062306a36Sopenharmony_ci mutex_lock(&edge_serial->es_lock); 199162306a36Sopenharmony_ci --edge_port->edge_serial->num_ports_open; 199262306a36Sopenharmony_ci if (edge_port->edge_serial->num_ports_open <= 0) { 199362306a36Sopenharmony_ci /* last port is now closed, let's shut down our interrupt urb */ 199462306a36Sopenharmony_ci usb_kill_urb(port->serial->port[0]->interrupt_in_urb); 199562306a36Sopenharmony_ci edge_port->edge_serial->num_ports_open = 0; 199662306a36Sopenharmony_ci } 199762306a36Sopenharmony_ci mutex_unlock(&edge_serial->es_lock); 199862306a36Sopenharmony_ci edge_port->close_pending = 0; 199962306a36Sopenharmony_ci} 200062306a36Sopenharmony_ci 200162306a36Sopenharmony_cistatic int edge_write(struct tty_struct *tty, struct usb_serial_port *port, 200262306a36Sopenharmony_ci const unsigned char *data, int count) 200362306a36Sopenharmony_ci{ 200462306a36Sopenharmony_ci struct edgeport_port *edge_port = usb_get_serial_port_data(port); 200562306a36Sopenharmony_ci 200662306a36Sopenharmony_ci if (count == 0) { 200762306a36Sopenharmony_ci dev_dbg(&port->dev, "%s - write request of 0 bytes\n", __func__); 200862306a36Sopenharmony_ci return 0; 200962306a36Sopenharmony_ci } 201062306a36Sopenharmony_ci 201162306a36Sopenharmony_ci if (edge_port == NULL) 201262306a36Sopenharmony_ci return -ENODEV; 201362306a36Sopenharmony_ci if (edge_port->close_pending == 1) 201462306a36Sopenharmony_ci return -ENODEV; 201562306a36Sopenharmony_ci 201662306a36Sopenharmony_ci count = kfifo_in_locked(&port->write_fifo, data, count, 201762306a36Sopenharmony_ci &edge_port->ep_lock); 201862306a36Sopenharmony_ci edge_send(port, tty); 201962306a36Sopenharmony_ci 202062306a36Sopenharmony_ci return count; 202162306a36Sopenharmony_ci} 202262306a36Sopenharmony_ci 202362306a36Sopenharmony_cistatic void edge_send(struct usb_serial_port *port, struct tty_struct *tty) 202462306a36Sopenharmony_ci{ 202562306a36Sopenharmony_ci int count, result; 202662306a36Sopenharmony_ci struct edgeport_port *edge_port = usb_get_serial_port_data(port); 202762306a36Sopenharmony_ci unsigned long flags; 202862306a36Sopenharmony_ci 202962306a36Sopenharmony_ci spin_lock_irqsave(&edge_port->ep_lock, flags); 203062306a36Sopenharmony_ci 203162306a36Sopenharmony_ci if (edge_port->ep_write_urb_in_use) { 203262306a36Sopenharmony_ci spin_unlock_irqrestore(&edge_port->ep_lock, flags); 203362306a36Sopenharmony_ci return; 203462306a36Sopenharmony_ci } 203562306a36Sopenharmony_ci 203662306a36Sopenharmony_ci count = kfifo_out(&port->write_fifo, 203762306a36Sopenharmony_ci port->write_urb->transfer_buffer, 203862306a36Sopenharmony_ci port->bulk_out_size); 203962306a36Sopenharmony_ci 204062306a36Sopenharmony_ci if (count == 0) { 204162306a36Sopenharmony_ci spin_unlock_irqrestore(&edge_port->ep_lock, flags); 204262306a36Sopenharmony_ci return; 204362306a36Sopenharmony_ci } 204462306a36Sopenharmony_ci 204562306a36Sopenharmony_ci edge_port->ep_write_urb_in_use = 1; 204662306a36Sopenharmony_ci 204762306a36Sopenharmony_ci spin_unlock_irqrestore(&edge_port->ep_lock, flags); 204862306a36Sopenharmony_ci 204962306a36Sopenharmony_ci usb_serial_debug_data(&port->dev, __func__, count, port->write_urb->transfer_buffer); 205062306a36Sopenharmony_ci 205162306a36Sopenharmony_ci /* set up our urb */ 205262306a36Sopenharmony_ci port->write_urb->transfer_buffer_length = count; 205362306a36Sopenharmony_ci 205462306a36Sopenharmony_ci /* send the data out the bulk port */ 205562306a36Sopenharmony_ci result = usb_submit_urb(port->write_urb, GFP_ATOMIC); 205662306a36Sopenharmony_ci if (result) { 205762306a36Sopenharmony_ci dev_err_console(port, 205862306a36Sopenharmony_ci "%s - failed submitting write urb, error %d\n", 205962306a36Sopenharmony_ci __func__, result); 206062306a36Sopenharmony_ci edge_port->ep_write_urb_in_use = 0; 206162306a36Sopenharmony_ci /* TODO: reschedule edge_send */ 206262306a36Sopenharmony_ci } else 206362306a36Sopenharmony_ci edge_port->port->icount.tx += count; 206462306a36Sopenharmony_ci 206562306a36Sopenharmony_ci /* 206662306a36Sopenharmony_ci * wakeup any process waiting for writes to complete 206762306a36Sopenharmony_ci * there is now more room in the buffer for new writes 206862306a36Sopenharmony_ci */ 206962306a36Sopenharmony_ci if (tty) 207062306a36Sopenharmony_ci tty_wakeup(tty); 207162306a36Sopenharmony_ci} 207262306a36Sopenharmony_ci 207362306a36Sopenharmony_cistatic unsigned int edge_write_room(struct tty_struct *tty) 207462306a36Sopenharmony_ci{ 207562306a36Sopenharmony_ci struct usb_serial_port *port = tty->driver_data; 207662306a36Sopenharmony_ci struct edgeport_port *edge_port = usb_get_serial_port_data(port); 207762306a36Sopenharmony_ci unsigned int room; 207862306a36Sopenharmony_ci unsigned long flags; 207962306a36Sopenharmony_ci 208062306a36Sopenharmony_ci if (edge_port == NULL) 208162306a36Sopenharmony_ci return 0; 208262306a36Sopenharmony_ci if (edge_port->close_pending == 1) 208362306a36Sopenharmony_ci return 0; 208462306a36Sopenharmony_ci 208562306a36Sopenharmony_ci spin_lock_irqsave(&edge_port->ep_lock, flags); 208662306a36Sopenharmony_ci room = kfifo_avail(&port->write_fifo); 208762306a36Sopenharmony_ci spin_unlock_irqrestore(&edge_port->ep_lock, flags); 208862306a36Sopenharmony_ci 208962306a36Sopenharmony_ci dev_dbg(&port->dev, "%s - returns %u\n", __func__, room); 209062306a36Sopenharmony_ci return room; 209162306a36Sopenharmony_ci} 209262306a36Sopenharmony_ci 209362306a36Sopenharmony_cistatic unsigned int edge_chars_in_buffer(struct tty_struct *tty) 209462306a36Sopenharmony_ci{ 209562306a36Sopenharmony_ci struct usb_serial_port *port = tty->driver_data; 209662306a36Sopenharmony_ci struct edgeport_port *edge_port = usb_get_serial_port_data(port); 209762306a36Sopenharmony_ci unsigned int chars; 209862306a36Sopenharmony_ci unsigned long flags; 209962306a36Sopenharmony_ci if (edge_port == NULL) 210062306a36Sopenharmony_ci return 0; 210162306a36Sopenharmony_ci 210262306a36Sopenharmony_ci spin_lock_irqsave(&edge_port->ep_lock, flags); 210362306a36Sopenharmony_ci chars = kfifo_len(&port->write_fifo); 210462306a36Sopenharmony_ci spin_unlock_irqrestore(&edge_port->ep_lock, flags); 210562306a36Sopenharmony_ci 210662306a36Sopenharmony_ci dev_dbg(&port->dev, "%s - returns %u\n", __func__, chars); 210762306a36Sopenharmony_ci return chars; 210862306a36Sopenharmony_ci} 210962306a36Sopenharmony_ci 211062306a36Sopenharmony_cistatic bool edge_tx_empty(struct usb_serial_port *port) 211162306a36Sopenharmony_ci{ 211262306a36Sopenharmony_ci struct edgeport_port *edge_port = usb_get_serial_port_data(port); 211362306a36Sopenharmony_ci int ret; 211462306a36Sopenharmony_ci 211562306a36Sopenharmony_ci ret = tx_active(edge_port); 211662306a36Sopenharmony_ci if (ret > 0) 211762306a36Sopenharmony_ci return false; 211862306a36Sopenharmony_ci 211962306a36Sopenharmony_ci return true; 212062306a36Sopenharmony_ci} 212162306a36Sopenharmony_ci 212262306a36Sopenharmony_cistatic void edge_throttle(struct tty_struct *tty) 212362306a36Sopenharmony_ci{ 212462306a36Sopenharmony_ci struct usb_serial_port *port = tty->driver_data; 212562306a36Sopenharmony_ci struct edgeport_port *edge_port = usb_get_serial_port_data(port); 212662306a36Sopenharmony_ci int status; 212762306a36Sopenharmony_ci 212862306a36Sopenharmony_ci if (edge_port == NULL) 212962306a36Sopenharmony_ci return; 213062306a36Sopenharmony_ci 213162306a36Sopenharmony_ci /* if we are implementing XON/XOFF, send the stop character */ 213262306a36Sopenharmony_ci if (I_IXOFF(tty)) { 213362306a36Sopenharmony_ci unsigned char stop_char = STOP_CHAR(tty); 213462306a36Sopenharmony_ci status = edge_write(tty, port, &stop_char, 1); 213562306a36Sopenharmony_ci if (status <= 0) { 213662306a36Sopenharmony_ci dev_err(&port->dev, "%s - failed to write stop character, %d\n", __func__, status); 213762306a36Sopenharmony_ci } 213862306a36Sopenharmony_ci } 213962306a36Sopenharmony_ci 214062306a36Sopenharmony_ci /* 214162306a36Sopenharmony_ci * if we are implementing RTS/CTS, stop reads 214262306a36Sopenharmony_ci * and the Edgeport will clear the RTS line 214362306a36Sopenharmony_ci */ 214462306a36Sopenharmony_ci if (C_CRTSCTS(tty)) 214562306a36Sopenharmony_ci stop_read(edge_port); 214662306a36Sopenharmony_ci 214762306a36Sopenharmony_ci} 214862306a36Sopenharmony_ci 214962306a36Sopenharmony_cistatic void edge_unthrottle(struct tty_struct *tty) 215062306a36Sopenharmony_ci{ 215162306a36Sopenharmony_ci struct usb_serial_port *port = tty->driver_data; 215262306a36Sopenharmony_ci struct edgeport_port *edge_port = usb_get_serial_port_data(port); 215362306a36Sopenharmony_ci int status; 215462306a36Sopenharmony_ci 215562306a36Sopenharmony_ci if (edge_port == NULL) 215662306a36Sopenharmony_ci return; 215762306a36Sopenharmony_ci 215862306a36Sopenharmony_ci /* if we are implementing XON/XOFF, send the start character */ 215962306a36Sopenharmony_ci if (I_IXOFF(tty)) { 216062306a36Sopenharmony_ci unsigned char start_char = START_CHAR(tty); 216162306a36Sopenharmony_ci status = edge_write(tty, port, &start_char, 1); 216262306a36Sopenharmony_ci if (status <= 0) { 216362306a36Sopenharmony_ci dev_err(&port->dev, "%s - failed to write start character, %d\n", __func__, status); 216462306a36Sopenharmony_ci } 216562306a36Sopenharmony_ci } 216662306a36Sopenharmony_ci /* 216762306a36Sopenharmony_ci * if we are implementing RTS/CTS, restart reads 216862306a36Sopenharmony_ci * are the Edgeport will assert the RTS line 216962306a36Sopenharmony_ci */ 217062306a36Sopenharmony_ci if (C_CRTSCTS(tty)) { 217162306a36Sopenharmony_ci status = restart_read(edge_port); 217262306a36Sopenharmony_ci if (status) 217362306a36Sopenharmony_ci dev_err(&port->dev, 217462306a36Sopenharmony_ci "%s - read bulk usb_submit_urb failed: %d\n", 217562306a36Sopenharmony_ci __func__, status); 217662306a36Sopenharmony_ci } 217762306a36Sopenharmony_ci 217862306a36Sopenharmony_ci} 217962306a36Sopenharmony_ci 218062306a36Sopenharmony_cistatic void stop_read(struct edgeport_port *edge_port) 218162306a36Sopenharmony_ci{ 218262306a36Sopenharmony_ci unsigned long flags; 218362306a36Sopenharmony_ci 218462306a36Sopenharmony_ci spin_lock_irqsave(&edge_port->ep_lock, flags); 218562306a36Sopenharmony_ci 218662306a36Sopenharmony_ci if (edge_port->ep_read_urb_state == EDGE_READ_URB_RUNNING) 218762306a36Sopenharmony_ci edge_port->ep_read_urb_state = EDGE_READ_URB_STOPPING; 218862306a36Sopenharmony_ci edge_port->shadow_mcr &= ~MCR_RTS; 218962306a36Sopenharmony_ci 219062306a36Sopenharmony_ci spin_unlock_irqrestore(&edge_port->ep_lock, flags); 219162306a36Sopenharmony_ci} 219262306a36Sopenharmony_ci 219362306a36Sopenharmony_cistatic int restart_read(struct edgeport_port *edge_port) 219462306a36Sopenharmony_ci{ 219562306a36Sopenharmony_ci struct urb *urb; 219662306a36Sopenharmony_ci int status = 0; 219762306a36Sopenharmony_ci unsigned long flags; 219862306a36Sopenharmony_ci 219962306a36Sopenharmony_ci spin_lock_irqsave(&edge_port->ep_lock, flags); 220062306a36Sopenharmony_ci 220162306a36Sopenharmony_ci if (edge_port->ep_read_urb_state == EDGE_READ_URB_STOPPED) { 220262306a36Sopenharmony_ci urb = edge_port->port->read_urb; 220362306a36Sopenharmony_ci status = usb_submit_urb(urb, GFP_ATOMIC); 220462306a36Sopenharmony_ci } 220562306a36Sopenharmony_ci edge_port->ep_read_urb_state = EDGE_READ_URB_RUNNING; 220662306a36Sopenharmony_ci edge_port->shadow_mcr |= MCR_RTS; 220762306a36Sopenharmony_ci 220862306a36Sopenharmony_ci spin_unlock_irqrestore(&edge_port->ep_lock, flags); 220962306a36Sopenharmony_ci 221062306a36Sopenharmony_ci return status; 221162306a36Sopenharmony_ci} 221262306a36Sopenharmony_ci 221362306a36Sopenharmony_cistatic void change_port_settings(struct tty_struct *tty, 221462306a36Sopenharmony_ci struct edgeport_port *edge_port, const struct ktermios *old_termios) 221562306a36Sopenharmony_ci{ 221662306a36Sopenharmony_ci struct device *dev = &edge_port->port->dev; 221762306a36Sopenharmony_ci struct ump_uart_config *config; 221862306a36Sopenharmony_ci int baud; 221962306a36Sopenharmony_ci unsigned cflag; 222062306a36Sopenharmony_ci int status; 222162306a36Sopenharmony_ci 222262306a36Sopenharmony_ci config = kmalloc (sizeof (*config), GFP_KERNEL); 222362306a36Sopenharmony_ci if (!config) { 222462306a36Sopenharmony_ci tty->termios = *old_termios; 222562306a36Sopenharmony_ci return; 222662306a36Sopenharmony_ci } 222762306a36Sopenharmony_ci 222862306a36Sopenharmony_ci cflag = tty->termios.c_cflag; 222962306a36Sopenharmony_ci 223062306a36Sopenharmony_ci config->wFlags = 0; 223162306a36Sopenharmony_ci 223262306a36Sopenharmony_ci /* These flags must be set */ 223362306a36Sopenharmony_ci config->wFlags |= UMP_MASK_UART_FLAGS_RECEIVE_MS_INT; 223462306a36Sopenharmony_ci config->wFlags |= UMP_MASK_UART_FLAGS_AUTO_START_ON_ERR; 223562306a36Sopenharmony_ci config->bUartMode = (u8)(edge_port->bUartMode); 223662306a36Sopenharmony_ci 223762306a36Sopenharmony_ci switch (cflag & CSIZE) { 223862306a36Sopenharmony_ci case CS5: 223962306a36Sopenharmony_ci config->bDataBits = UMP_UART_CHAR5BITS; 224062306a36Sopenharmony_ci dev_dbg(dev, "%s - data bits = 5\n", __func__); 224162306a36Sopenharmony_ci break; 224262306a36Sopenharmony_ci case CS6: 224362306a36Sopenharmony_ci config->bDataBits = UMP_UART_CHAR6BITS; 224462306a36Sopenharmony_ci dev_dbg(dev, "%s - data bits = 6\n", __func__); 224562306a36Sopenharmony_ci break; 224662306a36Sopenharmony_ci case CS7: 224762306a36Sopenharmony_ci config->bDataBits = UMP_UART_CHAR7BITS; 224862306a36Sopenharmony_ci dev_dbg(dev, "%s - data bits = 7\n", __func__); 224962306a36Sopenharmony_ci break; 225062306a36Sopenharmony_ci default: 225162306a36Sopenharmony_ci case CS8: 225262306a36Sopenharmony_ci config->bDataBits = UMP_UART_CHAR8BITS; 225362306a36Sopenharmony_ci dev_dbg(dev, "%s - data bits = 8\n", __func__); 225462306a36Sopenharmony_ci break; 225562306a36Sopenharmony_ci } 225662306a36Sopenharmony_ci 225762306a36Sopenharmony_ci if (cflag & PARENB) { 225862306a36Sopenharmony_ci if (cflag & PARODD) { 225962306a36Sopenharmony_ci config->wFlags |= UMP_MASK_UART_FLAGS_PARITY; 226062306a36Sopenharmony_ci config->bParity = UMP_UART_ODDPARITY; 226162306a36Sopenharmony_ci dev_dbg(dev, "%s - parity = odd\n", __func__); 226262306a36Sopenharmony_ci } else { 226362306a36Sopenharmony_ci config->wFlags |= UMP_MASK_UART_FLAGS_PARITY; 226462306a36Sopenharmony_ci config->bParity = UMP_UART_EVENPARITY; 226562306a36Sopenharmony_ci dev_dbg(dev, "%s - parity = even\n", __func__); 226662306a36Sopenharmony_ci } 226762306a36Sopenharmony_ci } else { 226862306a36Sopenharmony_ci config->bParity = UMP_UART_NOPARITY; 226962306a36Sopenharmony_ci dev_dbg(dev, "%s - parity = none\n", __func__); 227062306a36Sopenharmony_ci } 227162306a36Sopenharmony_ci 227262306a36Sopenharmony_ci if (cflag & CSTOPB) { 227362306a36Sopenharmony_ci config->bStopBits = UMP_UART_STOPBIT2; 227462306a36Sopenharmony_ci dev_dbg(dev, "%s - stop bits = 2\n", __func__); 227562306a36Sopenharmony_ci } else { 227662306a36Sopenharmony_ci config->bStopBits = UMP_UART_STOPBIT1; 227762306a36Sopenharmony_ci dev_dbg(dev, "%s - stop bits = 1\n", __func__); 227862306a36Sopenharmony_ci } 227962306a36Sopenharmony_ci 228062306a36Sopenharmony_ci /* figure out the flow control settings */ 228162306a36Sopenharmony_ci if (cflag & CRTSCTS) { 228262306a36Sopenharmony_ci config->wFlags |= UMP_MASK_UART_FLAGS_OUT_X_CTS_FLOW; 228362306a36Sopenharmony_ci config->wFlags |= UMP_MASK_UART_FLAGS_RTS_FLOW; 228462306a36Sopenharmony_ci dev_dbg(dev, "%s - RTS/CTS is enabled\n", __func__); 228562306a36Sopenharmony_ci } else { 228662306a36Sopenharmony_ci dev_dbg(dev, "%s - RTS/CTS is disabled\n", __func__); 228762306a36Sopenharmony_ci restart_read(edge_port); 228862306a36Sopenharmony_ci } 228962306a36Sopenharmony_ci 229062306a36Sopenharmony_ci /* 229162306a36Sopenharmony_ci * if we are implementing XON/XOFF, set the start and stop 229262306a36Sopenharmony_ci * character in the device 229362306a36Sopenharmony_ci */ 229462306a36Sopenharmony_ci config->cXon = START_CHAR(tty); 229562306a36Sopenharmony_ci config->cXoff = STOP_CHAR(tty); 229662306a36Sopenharmony_ci 229762306a36Sopenharmony_ci /* if we are implementing INBOUND XON/XOFF */ 229862306a36Sopenharmony_ci if (I_IXOFF(tty)) { 229962306a36Sopenharmony_ci config->wFlags |= UMP_MASK_UART_FLAGS_IN_X; 230062306a36Sopenharmony_ci dev_dbg(dev, "%s - INBOUND XON/XOFF is enabled, XON = %2x, XOFF = %2x\n", 230162306a36Sopenharmony_ci __func__, config->cXon, config->cXoff); 230262306a36Sopenharmony_ci } else 230362306a36Sopenharmony_ci dev_dbg(dev, "%s - INBOUND XON/XOFF is disabled\n", __func__); 230462306a36Sopenharmony_ci 230562306a36Sopenharmony_ci /* if we are implementing OUTBOUND XON/XOFF */ 230662306a36Sopenharmony_ci if (I_IXON(tty)) { 230762306a36Sopenharmony_ci config->wFlags |= UMP_MASK_UART_FLAGS_OUT_X; 230862306a36Sopenharmony_ci dev_dbg(dev, "%s - OUTBOUND XON/XOFF is enabled, XON = %2x, XOFF = %2x\n", 230962306a36Sopenharmony_ci __func__, config->cXon, config->cXoff); 231062306a36Sopenharmony_ci } else 231162306a36Sopenharmony_ci dev_dbg(dev, "%s - OUTBOUND XON/XOFF is disabled\n", __func__); 231262306a36Sopenharmony_ci 231362306a36Sopenharmony_ci tty->termios.c_cflag &= ~CMSPAR; 231462306a36Sopenharmony_ci 231562306a36Sopenharmony_ci /* Round the baud rate */ 231662306a36Sopenharmony_ci baud = tty_get_baud_rate(tty); 231762306a36Sopenharmony_ci if (!baud) { 231862306a36Sopenharmony_ci /* pick a default, any default... */ 231962306a36Sopenharmony_ci baud = 9600; 232062306a36Sopenharmony_ci } else { 232162306a36Sopenharmony_ci /* Avoid a zero divisor. */ 232262306a36Sopenharmony_ci baud = min(baud, 461550); 232362306a36Sopenharmony_ci tty_encode_baud_rate(tty, baud, baud); 232462306a36Sopenharmony_ci } 232562306a36Sopenharmony_ci 232662306a36Sopenharmony_ci edge_port->baud_rate = baud; 232762306a36Sopenharmony_ci config->wBaudRate = (u16)((461550L + baud/2) / baud); 232862306a36Sopenharmony_ci 232962306a36Sopenharmony_ci /* FIXME: Recompute actual baud from divisor here */ 233062306a36Sopenharmony_ci 233162306a36Sopenharmony_ci dev_dbg(dev, "%s - baud rate = %d, wBaudRate = %d\n", __func__, baud, config->wBaudRate); 233262306a36Sopenharmony_ci 233362306a36Sopenharmony_ci dev_dbg(dev, "wBaudRate: %d\n", (int)(461550L / config->wBaudRate)); 233462306a36Sopenharmony_ci dev_dbg(dev, "wFlags: 0x%x\n", config->wFlags); 233562306a36Sopenharmony_ci dev_dbg(dev, "bDataBits: %d\n", config->bDataBits); 233662306a36Sopenharmony_ci dev_dbg(dev, "bParity: %d\n", config->bParity); 233762306a36Sopenharmony_ci dev_dbg(dev, "bStopBits: %d\n", config->bStopBits); 233862306a36Sopenharmony_ci dev_dbg(dev, "cXon: %d\n", config->cXon); 233962306a36Sopenharmony_ci dev_dbg(dev, "cXoff: %d\n", config->cXoff); 234062306a36Sopenharmony_ci dev_dbg(dev, "bUartMode: %d\n", config->bUartMode); 234162306a36Sopenharmony_ci 234262306a36Sopenharmony_ci /* move the word values into big endian mode */ 234362306a36Sopenharmony_ci cpu_to_be16s(&config->wFlags); 234462306a36Sopenharmony_ci cpu_to_be16s(&config->wBaudRate); 234562306a36Sopenharmony_ci 234662306a36Sopenharmony_ci status = send_port_cmd(edge_port->port, UMPC_SET_CONFIG, 0, config, 234762306a36Sopenharmony_ci sizeof(*config)); 234862306a36Sopenharmony_ci if (status) 234962306a36Sopenharmony_ci dev_dbg(dev, "%s - error %d when trying to write config to device\n", 235062306a36Sopenharmony_ci __func__, status); 235162306a36Sopenharmony_ci kfree(config); 235262306a36Sopenharmony_ci} 235362306a36Sopenharmony_ci 235462306a36Sopenharmony_cistatic void edge_set_termios(struct tty_struct *tty, 235562306a36Sopenharmony_ci struct usb_serial_port *port, 235662306a36Sopenharmony_ci const struct ktermios *old_termios) 235762306a36Sopenharmony_ci{ 235862306a36Sopenharmony_ci struct edgeport_port *edge_port = usb_get_serial_port_data(port); 235962306a36Sopenharmony_ci 236062306a36Sopenharmony_ci if (edge_port == NULL) 236162306a36Sopenharmony_ci return; 236262306a36Sopenharmony_ci /* change the port settings to the new ones specified */ 236362306a36Sopenharmony_ci change_port_settings(tty, edge_port, old_termios); 236462306a36Sopenharmony_ci} 236562306a36Sopenharmony_ci 236662306a36Sopenharmony_cistatic int edge_tiocmset(struct tty_struct *tty, 236762306a36Sopenharmony_ci unsigned int set, unsigned int clear) 236862306a36Sopenharmony_ci{ 236962306a36Sopenharmony_ci struct usb_serial_port *port = tty->driver_data; 237062306a36Sopenharmony_ci struct edgeport_port *edge_port = usb_get_serial_port_data(port); 237162306a36Sopenharmony_ci unsigned int mcr; 237262306a36Sopenharmony_ci unsigned long flags; 237362306a36Sopenharmony_ci 237462306a36Sopenharmony_ci spin_lock_irqsave(&edge_port->ep_lock, flags); 237562306a36Sopenharmony_ci mcr = edge_port->shadow_mcr; 237662306a36Sopenharmony_ci if (set & TIOCM_RTS) 237762306a36Sopenharmony_ci mcr |= MCR_RTS; 237862306a36Sopenharmony_ci if (set & TIOCM_DTR) 237962306a36Sopenharmony_ci mcr |= MCR_DTR; 238062306a36Sopenharmony_ci if (set & TIOCM_LOOP) 238162306a36Sopenharmony_ci mcr |= MCR_LOOPBACK; 238262306a36Sopenharmony_ci 238362306a36Sopenharmony_ci if (clear & TIOCM_RTS) 238462306a36Sopenharmony_ci mcr &= ~MCR_RTS; 238562306a36Sopenharmony_ci if (clear & TIOCM_DTR) 238662306a36Sopenharmony_ci mcr &= ~MCR_DTR; 238762306a36Sopenharmony_ci if (clear & TIOCM_LOOP) 238862306a36Sopenharmony_ci mcr &= ~MCR_LOOPBACK; 238962306a36Sopenharmony_ci 239062306a36Sopenharmony_ci edge_port->shadow_mcr = mcr; 239162306a36Sopenharmony_ci spin_unlock_irqrestore(&edge_port->ep_lock, flags); 239262306a36Sopenharmony_ci 239362306a36Sopenharmony_ci restore_mcr(edge_port, mcr); 239462306a36Sopenharmony_ci return 0; 239562306a36Sopenharmony_ci} 239662306a36Sopenharmony_ci 239762306a36Sopenharmony_cistatic int edge_tiocmget(struct tty_struct *tty) 239862306a36Sopenharmony_ci{ 239962306a36Sopenharmony_ci struct usb_serial_port *port = tty->driver_data; 240062306a36Sopenharmony_ci struct edgeport_port *edge_port = usb_get_serial_port_data(port); 240162306a36Sopenharmony_ci unsigned int result = 0; 240262306a36Sopenharmony_ci unsigned int msr; 240362306a36Sopenharmony_ci unsigned int mcr; 240462306a36Sopenharmony_ci unsigned long flags; 240562306a36Sopenharmony_ci 240662306a36Sopenharmony_ci spin_lock_irqsave(&edge_port->ep_lock, flags); 240762306a36Sopenharmony_ci 240862306a36Sopenharmony_ci msr = edge_port->shadow_msr; 240962306a36Sopenharmony_ci mcr = edge_port->shadow_mcr; 241062306a36Sopenharmony_ci result = ((mcr & MCR_DTR) ? TIOCM_DTR: 0) /* 0x002 */ 241162306a36Sopenharmony_ci | ((mcr & MCR_RTS) ? TIOCM_RTS: 0) /* 0x004 */ 241262306a36Sopenharmony_ci | ((msr & EDGEPORT_MSR_CTS) ? TIOCM_CTS: 0) /* 0x020 */ 241362306a36Sopenharmony_ci | ((msr & EDGEPORT_MSR_CD) ? TIOCM_CAR: 0) /* 0x040 */ 241462306a36Sopenharmony_ci | ((msr & EDGEPORT_MSR_RI) ? TIOCM_RI: 0) /* 0x080 */ 241562306a36Sopenharmony_ci | ((msr & EDGEPORT_MSR_DSR) ? TIOCM_DSR: 0); /* 0x100 */ 241662306a36Sopenharmony_ci 241762306a36Sopenharmony_ci 241862306a36Sopenharmony_ci dev_dbg(&port->dev, "%s -- %x\n", __func__, result); 241962306a36Sopenharmony_ci spin_unlock_irqrestore(&edge_port->ep_lock, flags); 242062306a36Sopenharmony_ci 242162306a36Sopenharmony_ci return result; 242262306a36Sopenharmony_ci} 242362306a36Sopenharmony_ci 242462306a36Sopenharmony_cistatic int edge_break(struct tty_struct *tty, int break_state) 242562306a36Sopenharmony_ci{ 242662306a36Sopenharmony_ci struct usb_serial_port *port = tty->driver_data; 242762306a36Sopenharmony_ci struct edgeport_port *edge_port = usb_get_serial_port_data(port); 242862306a36Sopenharmony_ci int status; 242962306a36Sopenharmony_ci int bv = 0; /* Off */ 243062306a36Sopenharmony_ci 243162306a36Sopenharmony_ci if (break_state == -1) 243262306a36Sopenharmony_ci bv = 1; /* On */ 243362306a36Sopenharmony_ci 243462306a36Sopenharmony_ci status = ti_do_config(edge_port, UMPC_SET_CLR_BREAK, bv); 243562306a36Sopenharmony_ci if (status) { 243662306a36Sopenharmony_ci dev_dbg(&port->dev, "%s - error %d sending break set/clear command.\n", 243762306a36Sopenharmony_ci __func__, status); 243862306a36Sopenharmony_ci return status; 243962306a36Sopenharmony_ci } 244062306a36Sopenharmony_ci 244162306a36Sopenharmony_ci return 0; 244262306a36Sopenharmony_ci} 244362306a36Sopenharmony_ci 244462306a36Sopenharmony_cistatic void edge_heartbeat_schedule(struct edgeport_serial *edge_serial) 244562306a36Sopenharmony_ci{ 244662306a36Sopenharmony_ci if (!edge_serial->use_heartbeat) 244762306a36Sopenharmony_ci return; 244862306a36Sopenharmony_ci 244962306a36Sopenharmony_ci schedule_delayed_work(&edge_serial->heartbeat_work, 245062306a36Sopenharmony_ci FW_HEARTBEAT_SECS * HZ); 245162306a36Sopenharmony_ci} 245262306a36Sopenharmony_ci 245362306a36Sopenharmony_cistatic void edge_heartbeat_work(struct work_struct *work) 245462306a36Sopenharmony_ci{ 245562306a36Sopenharmony_ci struct edgeport_serial *serial; 245662306a36Sopenharmony_ci struct ti_i2c_desc *rom_desc; 245762306a36Sopenharmony_ci 245862306a36Sopenharmony_ci serial = container_of(work, struct edgeport_serial, 245962306a36Sopenharmony_ci heartbeat_work.work); 246062306a36Sopenharmony_ci 246162306a36Sopenharmony_ci rom_desc = kmalloc(sizeof(*rom_desc), GFP_KERNEL); 246262306a36Sopenharmony_ci 246362306a36Sopenharmony_ci /* Descriptor address request is enough to reset the firmware timer */ 246462306a36Sopenharmony_ci if (!rom_desc || !get_descriptor_addr(serial, I2C_DESC_TYPE_ION, 246562306a36Sopenharmony_ci rom_desc)) { 246662306a36Sopenharmony_ci dev_err(&serial->serial->interface->dev, 246762306a36Sopenharmony_ci "%s - Incomplete heartbeat\n", __func__); 246862306a36Sopenharmony_ci } 246962306a36Sopenharmony_ci kfree(rom_desc); 247062306a36Sopenharmony_ci 247162306a36Sopenharmony_ci edge_heartbeat_schedule(serial); 247262306a36Sopenharmony_ci} 247362306a36Sopenharmony_ci 247462306a36Sopenharmony_cistatic int edge_calc_num_ports(struct usb_serial *serial, 247562306a36Sopenharmony_ci struct usb_serial_endpoints *epds) 247662306a36Sopenharmony_ci{ 247762306a36Sopenharmony_ci struct device *dev = &serial->interface->dev; 247862306a36Sopenharmony_ci unsigned char num_ports = serial->type->num_ports; 247962306a36Sopenharmony_ci 248062306a36Sopenharmony_ci /* Make sure we have the required endpoints when in download mode. */ 248162306a36Sopenharmony_ci if (serial->interface->cur_altsetting->desc.bNumEndpoints > 1) { 248262306a36Sopenharmony_ci if (epds->num_bulk_in < num_ports || 248362306a36Sopenharmony_ci epds->num_bulk_out < num_ports || 248462306a36Sopenharmony_ci epds->num_interrupt_in < 1) { 248562306a36Sopenharmony_ci dev_err(dev, "required endpoints missing\n"); 248662306a36Sopenharmony_ci return -ENODEV; 248762306a36Sopenharmony_ci } 248862306a36Sopenharmony_ci } 248962306a36Sopenharmony_ci 249062306a36Sopenharmony_ci return num_ports; 249162306a36Sopenharmony_ci} 249262306a36Sopenharmony_ci 249362306a36Sopenharmony_cistatic int edge_startup(struct usb_serial *serial) 249462306a36Sopenharmony_ci{ 249562306a36Sopenharmony_ci struct edgeport_serial *edge_serial; 249662306a36Sopenharmony_ci int status; 249762306a36Sopenharmony_ci u16 product_id; 249862306a36Sopenharmony_ci 249962306a36Sopenharmony_ci /* create our private serial structure */ 250062306a36Sopenharmony_ci edge_serial = kzalloc(sizeof(struct edgeport_serial), GFP_KERNEL); 250162306a36Sopenharmony_ci if (!edge_serial) 250262306a36Sopenharmony_ci return -ENOMEM; 250362306a36Sopenharmony_ci 250462306a36Sopenharmony_ci mutex_init(&edge_serial->es_lock); 250562306a36Sopenharmony_ci edge_serial->serial = serial; 250662306a36Sopenharmony_ci INIT_DELAYED_WORK(&edge_serial->heartbeat_work, edge_heartbeat_work); 250762306a36Sopenharmony_ci usb_set_serial_data(serial, edge_serial); 250862306a36Sopenharmony_ci 250962306a36Sopenharmony_ci status = download_fw(edge_serial); 251062306a36Sopenharmony_ci if (status < 0) { 251162306a36Sopenharmony_ci kfree(edge_serial); 251262306a36Sopenharmony_ci return status; 251362306a36Sopenharmony_ci } 251462306a36Sopenharmony_ci 251562306a36Sopenharmony_ci if (status > 0) 251662306a36Sopenharmony_ci return 1; /* bind but do not register any ports */ 251762306a36Sopenharmony_ci 251862306a36Sopenharmony_ci product_id = le16_to_cpu( 251962306a36Sopenharmony_ci edge_serial->serial->dev->descriptor.idProduct); 252062306a36Sopenharmony_ci 252162306a36Sopenharmony_ci /* Currently only the EP/416 models require heartbeat support */ 252262306a36Sopenharmony_ci if (edge_serial->fw_version > FW_HEARTBEAT_VERSION_CUTOFF) { 252362306a36Sopenharmony_ci if (product_id == ION_DEVICE_ID_TI_EDGEPORT_416 || 252462306a36Sopenharmony_ci product_id == ION_DEVICE_ID_TI_EDGEPORT_416B) { 252562306a36Sopenharmony_ci edge_serial->use_heartbeat = true; 252662306a36Sopenharmony_ci } 252762306a36Sopenharmony_ci } 252862306a36Sopenharmony_ci 252962306a36Sopenharmony_ci edge_heartbeat_schedule(edge_serial); 253062306a36Sopenharmony_ci 253162306a36Sopenharmony_ci return 0; 253262306a36Sopenharmony_ci} 253362306a36Sopenharmony_ci 253462306a36Sopenharmony_cistatic void edge_disconnect(struct usb_serial *serial) 253562306a36Sopenharmony_ci{ 253662306a36Sopenharmony_ci struct edgeport_serial *edge_serial = usb_get_serial_data(serial); 253762306a36Sopenharmony_ci 253862306a36Sopenharmony_ci cancel_delayed_work_sync(&edge_serial->heartbeat_work); 253962306a36Sopenharmony_ci} 254062306a36Sopenharmony_ci 254162306a36Sopenharmony_cistatic void edge_release(struct usb_serial *serial) 254262306a36Sopenharmony_ci{ 254362306a36Sopenharmony_ci struct edgeport_serial *edge_serial = usb_get_serial_data(serial); 254462306a36Sopenharmony_ci 254562306a36Sopenharmony_ci cancel_delayed_work_sync(&edge_serial->heartbeat_work); 254662306a36Sopenharmony_ci kfree(edge_serial); 254762306a36Sopenharmony_ci} 254862306a36Sopenharmony_ci 254962306a36Sopenharmony_cistatic int edge_port_probe(struct usb_serial_port *port) 255062306a36Sopenharmony_ci{ 255162306a36Sopenharmony_ci struct edgeport_port *edge_port; 255262306a36Sopenharmony_ci int ret; 255362306a36Sopenharmony_ci 255462306a36Sopenharmony_ci edge_port = kzalloc(sizeof(*edge_port), GFP_KERNEL); 255562306a36Sopenharmony_ci if (!edge_port) 255662306a36Sopenharmony_ci return -ENOMEM; 255762306a36Sopenharmony_ci 255862306a36Sopenharmony_ci spin_lock_init(&edge_port->ep_lock); 255962306a36Sopenharmony_ci edge_port->port = port; 256062306a36Sopenharmony_ci edge_port->edge_serial = usb_get_serial_data(port->serial); 256162306a36Sopenharmony_ci edge_port->bUartMode = default_uart_mode; 256262306a36Sopenharmony_ci 256362306a36Sopenharmony_ci switch (port->port_number) { 256462306a36Sopenharmony_ci case 0: 256562306a36Sopenharmony_ci edge_port->uart_base = UMPMEM_BASE_UART1; 256662306a36Sopenharmony_ci edge_port->dma_address = UMPD_OEDB1_ADDRESS; 256762306a36Sopenharmony_ci break; 256862306a36Sopenharmony_ci case 1: 256962306a36Sopenharmony_ci edge_port->uart_base = UMPMEM_BASE_UART2; 257062306a36Sopenharmony_ci edge_port->dma_address = UMPD_OEDB2_ADDRESS; 257162306a36Sopenharmony_ci break; 257262306a36Sopenharmony_ci default: 257362306a36Sopenharmony_ci dev_err(&port->dev, "unknown port number\n"); 257462306a36Sopenharmony_ci ret = -ENODEV; 257562306a36Sopenharmony_ci goto err; 257662306a36Sopenharmony_ci } 257762306a36Sopenharmony_ci 257862306a36Sopenharmony_ci dev_dbg(&port->dev, 257962306a36Sopenharmony_ci "%s - port_number = %d, uart_base = %04x, dma_address = %04x\n", 258062306a36Sopenharmony_ci __func__, port->port_number, edge_port->uart_base, 258162306a36Sopenharmony_ci edge_port->dma_address); 258262306a36Sopenharmony_ci 258362306a36Sopenharmony_ci usb_set_serial_port_data(port, edge_port); 258462306a36Sopenharmony_ci 258562306a36Sopenharmony_ci ret = edge_create_sysfs_attrs(port); 258662306a36Sopenharmony_ci if (ret) 258762306a36Sopenharmony_ci goto err; 258862306a36Sopenharmony_ci 258962306a36Sopenharmony_ci /* 259062306a36Sopenharmony_ci * The LSR does not tell when the transmitter shift register has 259162306a36Sopenharmony_ci * emptied so add a one-character drain delay. 259262306a36Sopenharmony_ci */ 259362306a36Sopenharmony_ci port->port.drain_delay = 1; 259462306a36Sopenharmony_ci 259562306a36Sopenharmony_ci return 0; 259662306a36Sopenharmony_cierr: 259762306a36Sopenharmony_ci kfree(edge_port); 259862306a36Sopenharmony_ci 259962306a36Sopenharmony_ci return ret; 260062306a36Sopenharmony_ci} 260162306a36Sopenharmony_ci 260262306a36Sopenharmony_cistatic void edge_port_remove(struct usb_serial_port *port) 260362306a36Sopenharmony_ci{ 260462306a36Sopenharmony_ci struct edgeport_port *edge_port; 260562306a36Sopenharmony_ci 260662306a36Sopenharmony_ci edge_port = usb_get_serial_port_data(port); 260762306a36Sopenharmony_ci edge_remove_sysfs_attrs(port); 260862306a36Sopenharmony_ci kfree(edge_port); 260962306a36Sopenharmony_ci} 261062306a36Sopenharmony_ci 261162306a36Sopenharmony_ci/* Sysfs Attributes */ 261262306a36Sopenharmony_ci 261362306a36Sopenharmony_cistatic ssize_t uart_mode_show(struct device *dev, 261462306a36Sopenharmony_ci struct device_attribute *attr, char *buf) 261562306a36Sopenharmony_ci{ 261662306a36Sopenharmony_ci struct usb_serial_port *port = to_usb_serial_port(dev); 261762306a36Sopenharmony_ci struct edgeport_port *edge_port = usb_get_serial_port_data(port); 261862306a36Sopenharmony_ci 261962306a36Sopenharmony_ci return sprintf(buf, "%d\n", edge_port->bUartMode); 262062306a36Sopenharmony_ci} 262162306a36Sopenharmony_ci 262262306a36Sopenharmony_cistatic ssize_t uart_mode_store(struct device *dev, 262362306a36Sopenharmony_ci struct device_attribute *attr, const char *valbuf, size_t count) 262462306a36Sopenharmony_ci{ 262562306a36Sopenharmony_ci struct usb_serial_port *port = to_usb_serial_port(dev); 262662306a36Sopenharmony_ci struct edgeport_port *edge_port = usb_get_serial_port_data(port); 262762306a36Sopenharmony_ci unsigned int v = simple_strtoul(valbuf, NULL, 0); 262862306a36Sopenharmony_ci 262962306a36Sopenharmony_ci dev_dbg(dev, "%s: setting uart_mode = %d\n", __func__, v); 263062306a36Sopenharmony_ci 263162306a36Sopenharmony_ci if (v < 256) 263262306a36Sopenharmony_ci edge_port->bUartMode = v; 263362306a36Sopenharmony_ci else 263462306a36Sopenharmony_ci dev_err(dev, "%s - uart_mode %d is invalid\n", __func__, v); 263562306a36Sopenharmony_ci 263662306a36Sopenharmony_ci return count; 263762306a36Sopenharmony_ci} 263862306a36Sopenharmony_cistatic DEVICE_ATTR_RW(uart_mode); 263962306a36Sopenharmony_ci 264062306a36Sopenharmony_cistatic int edge_create_sysfs_attrs(struct usb_serial_port *port) 264162306a36Sopenharmony_ci{ 264262306a36Sopenharmony_ci return device_create_file(&port->dev, &dev_attr_uart_mode); 264362306a36Sopenharmony_ci} 264462306a36Sopenharmony_ci 264562306a36Sopenharmony_cistatic int edge_remove_sysfs_attrs(struct usb_serial_port *port) 264662306a36Sopenharmony_ci{ 264762306a36Sopenharmony_ci device_remove_file(&port->dev, &dev_attr_uart_mode); 264862306a36Sopenharmony_ci return 0; 264962306a36Sopenharmony_ci} 265062306a36Sopenharmony_ci 265162306a36Sopenharmony_ci#ifdef CONFIG_PM 265262306a36Sopenharmony_cistatic int edge_suspend(struct usb_serial *serial, pm_message_t message) 265362306a36Sopenharmony_ci{ 265462306a36Sopenharmony_ci struct edgeport_serial *edge_serial = usb_get_serial_data(serial); 265562306a36Sopenharmony_ci 265662306a36Sopenharmony_ci cancel_delayed_work_sync(&edge_serial->heartbeat_work); 265762306a36Sopenharmony_ci 265862306a36Sopenharmony_ci return 0; 265962306a36Sopenharmony_ci} 266062306a36Sopenharmony_ci 266162306a36Sopenharmony_cistatic int edge_resume(struct usb_serial *serial) 266262306a36Sopenharmony_ci{ 266362306a36Sopenharmony_ci struct edgeport_serial *edge_serial = usb_get_serial_data(serial); 266462306a36Sopenharmony_ci 266562306a36Sopenharmony_ci edge_heartbeat_schedule(edge_serial); 266662306a36Sopenharmony_ci 266762306a36Sopenharmony_ci return 0; 266862306a36Sopenharmony_ci} 266962306a36Sopenharmony_ci#endif 267062306a36Sopenharmony_ci 267162306a36Sopenharmony_cistatic struct usb_serial_driver edgeport_1port_device = { 267262306a36Sopenharmony_ci .driver = { 267362306a36Sopenharmony_ci .owner = THIS_MODULE, 267462306a36Sopenharmony_ci .name = "edgeport_ti_1", 267562306a36Sopenharmony_ci }, 267662306a36Sopenharmony_ci .description = "Edgeport TI 1 port adapter", 267762306a36Sopenharmony_ci .id_table = edgeport_1port_id_table, 267862306a36Sopenharmony_ci .num_ports = 1, 267962306a36Sopenharmony_ci .num_bulk_out = 1, 268062306a36Sopenharmony_ci .open = edge_open, 268162306a36Sopenharmony_ci .close = edge_close, 268262306a36Sopenharmony_ci .throttle = edge_throttle, 268362306a36Sopenharmony_ci .unthrottle = edge_unthrottle, 268462306a36Sopenharmony_ci .attach = edge_startup, 268562306a36Sopenharmony_ci .calc_num_ports = edge_calc_num_ports, 268662306a36Sopenharmony_ci .disconnect = edge_disconnect, 268762306a36Sopenharmony_ci .release = edge_release, 268862306a36Sopenharmony_ci .port_probe = edge_port_probe, 268962306a36Sopenharmony_ci .port_remove = edge_port_remove, 269062306a36Sopenharmony_ci .set_termios = edge_set_termios, 269162306a36Sopenharmony_ci .tiocmget = edge_tiocmget, 269262306a36Sopenharmony_ci .tiocmset = edge_tiocmset, 269362306a36Sopenharmony_ci .tiocmiwait = usb_serial_generic_tiocmiwait, 269462306a36Sopenharmony_ci .get_icount = usb_serial_generic_get_icount, 269562306a36Sopenharmony_ci .write = edge_write, 269662306a36Sopenharmony_ci .write_room = edge_write_room, 269762306a36Sopenharmony_ci .chars_in_buffer = edge_chars_in_buffer, 269862306a36Sopenharmony_ci .tx_empty = edge_tx_empty, 269962306a36Sopenharmony_ci .break_ctl = edge_break, 270062306a36Sopenharmony_ci .read_int_callback = edge_interrupt_callback, 270162306a36Sopenharmony_ci .read_bulk_callback = edge_bulk_in_callback, 270262306a36Sopenharmony_ci .write_bulk_callback = edge_bulk_out_callback, 270362306a36Sopenharmony_ci#ifdef CONFIG_PM 270462306a36Sopenharmony_ci .suspend = edge_suspend, 270562306a36Sopenharmony_ci .resume = edge_resume, 270662306a36Sopenharmony_ci#endif 270762306a36Sopenharmony_ci}; 270862306a36Sopenharmony_ci 270962306a36Sopenharmony_cistatic struct usb_serial_driver edgeport_2port_device = { 271062306a36Sopenharmony_ci .driver = { 271162306a36Sopenharmony_ci .owner = THIS_MODULE, 271262306a36Sopenharmony_ci .name = "edgeport_ti_2", 271362306a36Sopenharmony_ci }, 271462306a36Sopenharmony_ci .description = "Edgeport TI 2 port adapter", 271562306a36Sopenharmony_ci .id_table = edgeport_2port_id_table, 271662306a36Sopenharmony_ci .num_ports = 2, 271762306a36Sopenharmony_ci .num_bulk_out = 1, 271862306a36Sopenharmony_ci .open = edge_open, 271962306a36Sopenharmony_ci .close = edge_close, 272062306a36Sopenharmony_ci .throttle = edge_throttle, 272162306a36Sopenharmony_ci .unthrottle = edge_unthrottle, 272262306a36Sopenharmony_ci .attach = edge_startup, 272362306a36Sopenharmony_ci .calc_num_ports = edge_calc_num_ports, 272462306a36Sopenharmony_ci .disconnect = edge_disconnect, 272562306a36Sopenharmony_ci .release = edge_release, 272662306a36Sopenharmony_ci .port_probe = edge_port_probe, 272762306a36Sopenharmony_ci .port_remove = edge_port_remove, 272862306a36Sopenharmony_ci .set_termios = edge_set_termios, 272962306a36Sopenharmony_ci .tiocmget = edge_tiocmget, 273062306a36Sopenharmony_ci .tiocmset = edge_tiocmset, 273162306a36Sopenharmony_ci .tiocmiwait = usb_serial_generic_tiocmiwait, 273262306a36Sopenharmony_ci .get_icount = usb_serial_generic_get_icount, 273362306a36Sopenharmony_ci .write = edge_write, 273462306a36Sopenharmony_ci .write_room = edge_write_room, 273562306a36Sopenharmony_ci .chars_in_buffer = edge_chars_in_buffer, 273662306a36Sopenharmony_ci .tx_empty = edge_tx_empty, 273762306a36Sopenharmony_ci .break_ctl = edge_break, 273862306a36Sopenharmony_ci .read_int_callback = edge_interrupt_callback, 273962306a36Sopenharmony_ci .read_bulk_callback = edge_bulk_in_callback, 274062306a36Sopenharmony_ci .write_bulk_callback = edge_bulk_out_callback, 274162306a36Sopenharmony_ci#ifdef CONFIG_PM 274262306a36Sopenharmony_ci .suspend = edge_suspend, 274362306a36Sopenharmony_ci .resume = edge_resume, 274462306a36Sopenharmony_ci#endif 274562306a36Sopenharmony_ci}; 274662306a36Sopenharmony_ci 274762306a36Sopenharmony_cistatic struct usb_serial_driver * const serial_drivers[] = { 274862306a36Sopenharmony_ci &edgeport_1port_device, &edgeport_2port_device, NULL 274962306a36Sopenharmony_ci}; 275062306a36Sopenharmony_ci 275162306a36Sopenharmony_cimodule_usb_serial_driver(serial_drivers, id_table_combined); 275262306a36Sopenharmony_ci 275362306a36Sopenharmony_ciMODULE_AUTHOR(DRIVER_AUTHOR); 275462306a36Sopenharmony_ciMODULE_DESCRIPTION(DRIVER_DESC); 275562306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 275662306a36Sopenharmony_ciMODULE_FIRMWARE("edgeport/down3.bin"); 275762306a36Sopenharmony_ci 275862306a36Sopenharmony_cimodule_param(ignore_cpu_rev, bool, 0644); 275962306a36Sopenharmony_ciMODULE_PARM_DESC(ignore_cpu_rev, 276062306a36Sopenharmony_ci "Ignore the cpu revision when connecting to a device"); 276162306a36Sopenharmony_ci 276262306a36Sopenharmony_cimodule_param(default_uart_mode, int, 0644); 276362306a36Sopenharmony_ciMODULE_PARM_DESC(default_uart_mode, "Default uart_mode, 0=RS232, ..."); 2764