162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * This file is subject to the terms and conditions of the GNU General Public 462306a36Sopenharmony_ci * License. See the file "COPYING" in the main directory of this archive 562306a36Sopenharmony_ci * for more details. 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Copyright (C) 2008 Cavium Networks 862306a36Sopenharmony_ci * 962306a36Sopenharmony_ci * Some parts of the code were originally released under BSD license: 1062306a36Sopenharmony_ci * 1162306a36Sopenharmony_ci * Copyright (c) 2003-2010 Cavium Networks (support@cavium.com). All rights 1262306a36Sopenharmony_ci * reserved. 1362306a36Sopenharmony_ci * 1462306a36Sopenharmony_ci * Redistribution and use in source and binary forms, with or without 1562306a36Sopenharmony_ci * modification, are permitted provided that the following conditions are 1662306a36Sopenharmony_ci * met: 1762306a36Sopenharmony_ci * 1862306a36Sopenharmony_ci * * Redistributions of source code must retain the above copyright 1962306a36Sopenharmony_ci * notice, this list of conditions and the following disclaimer. 2062306a36Sopenharmony_ci * 2162306a36Sopenharmony_ci * * Redistributions in binary form must reproduce the above 2262306a36Sopenharmony_ci * copyright notice, this list of conditions and the following 2362306a36Sopenharmony_ci * disclaimer in the documentation and/or other materials provided 2462306a36Sopenharmony_ci * with the distribution. 2562306a36Sopenharmony_ci * 2662306a36Sopenharmony_ci * * Neither the name of Cavium Networks nor the names of 2762306a36Sopenharmony_ci * its contributors may be used to endorse or promote products 2862306a36Sopenharmony_ci * derived from this software without specific prior written 2962306a36Sopenharmony_ci * permission. 3062306a36Sopenharmony_ci * 3162306a36Sopenharmony_ci * This Software, including technical data, may be subject to U.S. export 3262306a36Sopenharmony_ci * control laws, including the U.S. Export Administration Act and its associated 3362306a36Sopenharmony_ci * regulations, and may be subject to export or import regulations in other 3462306a36Sopenharmony_ci * countries. 3562306a36Sopenharmony_ci * 3662306a36Sopenharmony_ci * TO THE MAXIMUM EXTENT PERMITTED BY LAW, THE SOFTWARE IS PROVIDED "AS IS" 3762306a36Sopenharmony_ci * AND WITH ALL FAULTS AND CAVIUM NETWORKS MAKES NO PROMISES, REPRESENTATIONS OR 3862306a36Sopenharmony_ci * WARRANTIES, EITHER EXPRESS, IMPLIED, STATUTORY, OR OTHERWISE, WITH RESPECT TO 3962306a36Sopenharmony_ci * THE SOFTWARE, INCLUDING ITS CONDITION, ITS CONFORMITY TO ANY REPRESENTATION 4062306a36Sopenharmony_ci * OR DESCRIPTION, OR THE EXISTENCE OF ANY LATENT OR PATENT DEFECTS, AND CAVIUM 4162306a36Sopenharmony_ci * SPECIFICALLY DISCLAIMS ALL IMPLIED (IF ANY) WARRANTIES OF TITLE, 4262306a36Sopenharmony_ci * MERCHANTABILITY, NONINFRINGEMENT, FITNESS FOR A PARTICULAR PURPOSE, LACK OF 4362306a36Sopenharmony_ci * VIRUSES, ACCURACY OR COMPLETENESS, QUIET ENJOYMENT, QUIET POSSESSION OR 4462306a36Sopenharmony_ci * CORRESPONDENCE TO DESCRIPTION. THE ENTIRE RISK ARISING OUT OF USE OR 4562306a36Sopenharmony_ci * PERFORMANCE OF THE SOFTWARE LIES WITH YOU. 4662306a36Sopenharmony_ci */ 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ci#include <linux/usb.h> 4962306a36Sopenharmony_ci#include <linux/slab.h> 5062306a36Sopenharmony_ci#include <linux/module.h> 5162306a36Sopenharmony_ci#include <linux/usb/hcd.h> 5262306a36Sopenharmony_ci#include <linux/prefetch.h> 5362306a36Sopenharmony_ci#include <linux/irqdomain.h> 5462306a36Sopenharmony_ci#include <linux/dma-mapping.h> 5562306a36Sopenharmony_ci#include <linux/platform_device.h> 5662306a36Sopenharmony_ci#include <linux/of.h> 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci#include <asm/octeon/octeon.h> 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_ci#include "octeon-hcd.h" 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci/** 6362306a36Sopenharmony_ci * enum cvmx_usb_speed - the possible USB device speeds 6462306a36Sopenharmony_ci * 6562306a36Sopenharmony_ci * @CVMX_USB_SPEED_HIGH: Device is operation at 480Mbps 6662306a36Sopenharmony_ci * @CVMX_USB_SPEED_FULL: Device is operation at 12Mbps 6762306a36Sopenharmony_ci * @CVMX_USB_SPEED_LOW: Device is operation at 1.5Mbps 6862306a36Sopenharmony_ci */ 6962306a36Sopenharmony_cienum cvmx_usb_speed { 7062306a36Sopenharmony_ci CVMX_USB_SPEED_HIGH = 0, 7162306a36Sopenharmony_ci CVMX_USB_SPEED_FULL = 1, 7262306a36Sopenharmony_ci CVMX_USB_SPEED_LOW = 2, 7362306a36Sopenharmony_ci}; 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci/** 7662306a36Sopenharmony_ci * enum cvmx_usb_transfer - the possible USB transfer types 7762306a36Sopenharmony_ci * 7862306a36Sopenharmony_ci * @CVMX_USB_TRANSFER_CONTROL: USB transfer type control for hub and status 7962306a36Sopenharmony_ci * transfers 8062306a36Sopenharmony_ci * @CVMX_USB_TRANSFER_ISOCHRONOUS: USB transfer type isochronous for low 8162306a36Sopenharmony_ci * priority periodic transfers 8262306a36Sopenharmony_ci * @CVMX_USB_TRANSFER_BULK: USB transfer type bulk for large low priority 8362306a36Sopenharmony_ci * transfers 8462306a36Sopenharmony_ci * @CVMX_USB_TRANSFER_INTERRUPT: USB transfer type interrupt for high priority 8562306a36Sopenharmony_ci * periodic transfers 8662306a36Sopenharmony_ci */ 8762306a36Sopenharmony_cienum cvmx_usb_transfer { 8862306a36Sopenharmony_ci CVMX_USB_TRANSFER_CONTROL = 0, 8962306a36Sopenharmony_ci CVMX_USB_TRANSFER_ISOCHRONOUS = 1, 9062306a36Sopenharmony_ci CVMX_USB_TRANSFER_BULK = 2, 9162306a36Sopenharmony_ci CVMX_USB_TRANSFER_INTERRUPT = 3, 9262306a36Sopenharmony_ci}; 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci/** 9562306a36Sopenharmony_ci * enum cvmx_usb_direction - the transfer directions 9662306a36Sopenharmony_ci * 9762306a36Sopenharmony_ci * @CVMX_USB_DIRECTION_OUT: Data is transferring from Octeon to the device/host 9862306a36Sopenharmony_ci * @CVMX_USB_DIRECTION_IN: Data is transferring from the device/host to Octeon 9962306a36Sopenharmony_ci */ 10062306a36Sopenharmony_cienum cvmx_usb_direction { 10162306a36Sopenharmony_ci CVMX_USB_DIRECTION_OUT, 10262306a36Sopenharmony_ci CVMX_USB_DIRECTION_IN, 10362306a36Sopenharmony_ci}; 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci/** 10662306a36Sopenharmony_ci * enum cvmx_usb_status - possible callback function status codes 10762306a36Sopenharmony_ci * 10862306a36Sopenharmony_ci * @CVMX_USB_STATUS_OK: The transaction / operation finished without 10962306a36Sopenharmony_ci * any errors 11062306a36Sopenharmony_ci * @CVMX_USB_STATUS_SHORT: FIXME: This is currently not implemented 11162306a36Sopenharmony_ci * @CVMX_USB_STATUS_CANCEL: The transaction was canceled while in flight 11262306a36Sopenharmony_ci * by a user call to cvmx_usb_cancel 11362306a36Sopenharmony_ci * @CVMX_USB_STATUS_ERROR: The transaction aborted with an unexpected 11462306a36Sopenharmony_ci * error status 11562306a36Sopenharmony_ci * @CVMX_USB_STATUS_STALL: The transaction received a USB STALL response 11662306a36Sopenharmony_ci * from the device 11762306a36Sopenharmony_ci * @CVMX_USB_STATUS_XACTERR: The transaction failed with an error from the 11862306a36Sopenharmony_ci * device even after a number of retries 11962306a36Sopenharmony_ci * @CVMX_USB_STATUS_DATATGLERR: The transaction failed with a data toggle 12062306a36Sopenharmony_ci * error even after a number of retries 12162306a36Sopenharmony_ci * @CVMX_USB_STATUS_BABBLEERR: The transaction failed with a babble error 12262306a36Sopenharmony_ci * @CVMX_USB_STATUS_FRAMEERR: The transaction failed with a frame error 12362306a36Sopenharmony_ci * even after a number of retries 12462306a36Sopenharmony_ci */ 12562306a36Sopenharmony_cienum cvmx_usb_status { 12662306a36Sopenharmony_ci CVMX_USB_STATUS_OK, 12762306a36Sopenharmony_ci CVMX_USB_STATUS_SHORT, 12862306a36Sopenharmony_ci CVMX_USB_STATUS_CANCEL, 12962306a36Sopenharmony_ci CVMX_USB_STATUS_ERROR, 13062306a36Sopenharmony_ci CVMX_USB_STATUS_STALL, 13162306a36Sopenharmony_ci CVMX_USB_STATUS_XACTERR, 13262306a36Sopenharmony_ci CVMX_USB_STATUS_DATATGLERR, 13362306a36Sopenharmony_ci CVMX_USB_STATUS_BABBLEERR, 13462306a36Sopenharmony_ci CVMX_USB_STATUS_FRAMEERR, 13562306a36Sopenharmony_ci}; 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci/** 13862306a36Sopenharmony_ci * struct cvmx_usb_port_status - the USB port status information 13962306a36Sopenharmony_ci * 14062306a36Sopenharmony_ci * @port_enabled: 1 = Usb port is enabled, 0 = disabled 14162306a36Sopenharmony_ci * @port_over_current: 1 = Over current detected, 0 = Over current not 14262306a36Sopenharmony_ci * detected. Octeon doesn't support over current detection. 14362306a36Sopenharmony_ci * @port_powered: 1 = Port power is being supplied to the device, 0 = 14462306a36Sopenharmony_ci * power is off. Octeon doesn't support turning port power 14562306a36Sopenharmony_ci * off. 14662306a36Sopenharmony_ci * @port_speed: Current port speed. 14762306a36Sopenharmony_ci * @connected: 1 = A device is connected to the port, 0 = No device is 14862306a36Sopenharmony_ci * connected. 14962306a36Sopenharmony_ci * @connect_change: 1 = Device connected state changed since the last set 15062306a36Sopenharmony_ci * status call. 15162306a36Sopenharmony_ci */ 15262306a36Sopenharmony_cistruct cvmx_usb_port_status { 15362306a36Sopenharmony_ci u32 reserved : 25; 15462306a36Sopenharmony_ci u32 port_enabled : 1; 15562306a36Sopenharmony_ci u32 port_over_current : 1; 15662306a36Sopenharmony_ci u32 port_powered : 1; 15762306a36Sopenharmony_ci enum cvmx_usb_speed port_speed : 2; 15862306a36Sopenharmony_ci u32 connected : 1; 15962306a36Sopenharmony_ci u32 connect_change : 1; 16062306a36Sopenharmony_ci}; 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci/** 16362306a36Sopenharmony_ci * struct cvmx_usb_iso_packet - descriptor for Isochronous packets 16462306a36Sopenharmony_ci * 16562306a36Sopenharmony_ci * @offset: This is the offset in bytes into the main buffer where this data 16662306a36Sopenharmony_ci * is stored. 16762306a36Sopenharmony_ci * @length: This is the length in bytes of the data. 16862306a36Sopenharmony_ci * @status: This is the status of this individual packet transfer. 16962306a36Sopenharmony_ci */ 17062306a36Sopenharmony_cistruct cvmx_usb_iso_packet { 17162306a36Sopenharmony_ci int offset; 17262306a36Sopenharmony_ci int length; 17362306a36Sopenharmony_ci enum cvmx_usb_status status; 17462306a36Sopenharmony_ci}; 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci/** 17762306a36Sopenharmony_ci * enum cvmx_usb_initialize_flags - flags used by the initialization function 17862306a36Sopenharmony_ci * 17962306a36Sopenharmony_ci * @CVMX_USB_INITIALIZE_FLAGS_CLOCK_XO_XI: The USB port uses a 12MHz crystal 18062306a36Sopenharmony_ci * as clock source at USB_XO and 18162306a36Sopenharmony_ci * USB_XI. 18262306a36Sopenharmony_ci * @CVMX_USB_INITIALIZE_FLAGS_CLOCK_XO_GND: The USB port uses 12/24/48MHz 2.5V 18362306a36Sopenharmony_ci * board clock source at USB_XO. 18462306a36Sopenharmony_ci * USB_XI should be tied to GND. 18562306a36Sopenharmony_ci * @CVMX_USB_INITIALIZE_FLAGS_CLOCK_MHZ_MASK: Mask for clock speed field 18662306a36Sopenharmony_ci * @CVMX_USB_INITIALIZE_FLAGS_CLOCK_12MHZ: Speed of reference clock or 18762306a36Sopenharmony_ci * crystal 18862306a36Sopenharmony_ci * @CVMX_USB_INITIALIZE_FLAGS_CLOCK_24MHZ: Speed of reference clock 18962306a36Sopenharmony_ci * @CVMX_USB_INITIALIZE_FLAGS_CLOCK_48MHZ: Speed of reference clock 19062306a36Sopenharmony_ci * @CVMX_USB_INITIALIZE_FLAGS_NO_DMA: Disable DMA and used polled IO for 19162306a36Sopenharmony_ci * data transfer use for the USB 19262306a36Sopenharmony_ci */ 19362306a36Sopenharmony_cienum cvmx_usb_initialize_flags { 19462306a36Sopenharmony_ci CVMX_USB_INITIALIZE_FLAGS_CLOCK_XO_XI = 1 << 0, 19562306a36Sopenharmony_ci CVMX_USB_INITIALIZE_FLAGS_CLOCK_XO_GND = 1 << 1, 19662306a36Sopenharmony_ci CVMX_USB_INITIALIZE_FLAGS_CLOCK_MHZ_MASK = 3 << 3, 19762306a36Sopenharmony_ci CVMX_USB_INITIALIZE_FLAGS_CLOCK_12MHZ = 1 << 3, 19862306a36Sopenharmony_ci CVMX_USB_INITIALIZE_FLAGS_CLOCK_24MHZ = 2 << 3, 19962306a36Sopenharmony_ci CVMX_USB_INITIALIZE_FLAGS_CLOCK_48MHZ = 3 << 3, 20062306a36Sopenharmony_ci /* Bits 3-4 used to encode the clock frequency */ 20162306a36Sopenharmony_ci CVMX_USB_INITIALIZE_FLAGS_NO_DMA = 1 << 5, 20262306a36Sopenharmony_ci}; 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci/** 20562306a36Sopenharmony_ci * enum cvmx_usb_pipe_flags - internal flags for a pipe. 20662306a36Sopenharmony_ci * 20762306a36Sopenharmony_ci * @CVMX_USB_PIPE_FLAGS_SCHEDULED: Used internally to determine if a pipe is 20862306a36Sopenharmony_ci * actively using hardware. 20962306a36Sopenharmony_ci * @CVMX_USB_PIPE_FLAGS_NEED_PING: Used internally to determine if a high speed 21062306a36Sopenharmony_ci * pipe is in the ping state. 21162306a36Sopenharmony_ci */ 21262306a36Sopenharmony_cienum cvmx_usb_pipe_flags { 21362306a36Sopenharmony_ci CVMX_USB_PIPE_FLAGS_SCHEDULED = 1 << 17, 21462306a36Sopenharmony_ci CVMX_USB_PIPE_FLAGS_NEED_PING = 1 << 18, 21562306a36Sopenharmony_ci}; 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci/* Maximum number of times to retry failed transactions */ 21862306a36Sopenharmony_ci#define MAX_RETRIES 3 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci/* Maximum number of hardware channels supported by the USB block */ 22162306a36Sopenharmony_ci#define MAX_CHANNELS 8 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ci/* 22462306a36Sopenharmony_ci * The low level hardware can transfer a maximum of this number of bytes in each 22562306a36Sopenharmony_ci * transfer. The field is 19 bits wide 22662306a36Sopenharmony_ci */ 22762306a36Sopenharmony_ci#define MAX_TRANSFER_BYTES ((1 << 19) - 1) 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ci/* 23062306a36Sopenharmony_ci * The low level hardware can transfer a maximum of this number of packets in 23162306a36Sopenharmony_ci * each transfer. The field is 10 bits wide 23262306a36Sopenharmony_ci */ 23362306a36Sopenharmony_ci#define MAX_TRANSFER_PACKETS ((1 << 10) - 1) 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_ci/** 23662306a36Sopenharmony_ci * Logical transactions may take numerous low level 23762306a36Sopenharmony_ci * transactions, especially when splits are concerned. This 23862306a36Sopenharmony_ci * enum represents all of the possible stages a transaction can 23962306a36Sopenharmony_ci * be in. Note that split completes are always even. This is so 24062306a36Sopenharmony_ci * the NAK handler can backup to the previous low level 24162306a36Sopenharmony_ci * transaction with a simple clearing of bit 0. 24262306a36Sopenharmony_ci */ 24362306a36Sopenharmony_cienum cvmx_usb_stage { 24462306a36Sopenharmony_ci CVMX_USB_STAGE_NON_CONTROL, 24562306a36Sopenharmony_ci CVMX_USB_STAGE_NON_CONTROL_SPLIT_COMPLETE, 24662306a36Sopenharmony_ci CVMX_USB_STAGE_SETUP, 24762306a36Sopenharmony_ci CVMX_USB_STAGE_SETUP_SPLIT_COMPLETE, 24862306a36Sopenharmony_ci CVMX_USB_STAGE_DATA, 24962306a36Sopenharmony_ci CVMX_USB_STAGE_DATA_SPLIT_COMPLETE, 25062306a36Sopenharmony_ci CVMX_USB_STAGE_STATUS, 25162306a36Sopenharmony_ci CVMX_USB_STAGE_STATUS_SPLIT_COMPLETE, 25262306a36Sopenharmony_ci}; 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_ci/** 25562306a36Sopenharmony_ci * struct cvmx_usb_transaction - describes each pending USB transaction 25662306a36Sopenharmony_ci * regardless of type. These are linked together 25762306a36Sopenharmony_ci * to form a list of pending requests for a pipe. 25862306a36Sopenharmony_ci * 25962306a36Sopenharmony_ci * @node: List node for transactions in the pipe. 26062306a36Sopenharmony_ci * @type: Type of transaction, duplicated of the pipe. 26162306a36Sopenharmony_ci * @flags: State flags for this transaction. 26262306a36Sopenharmony_ci * @buffer: User's physical buffer address to read/write. 26362306a36Sopenharmony_ci * @buffer_length: Size of the user's buffer in bytes. 26462306a36Sopenharmony_ci * @control_header: For control transactions, physical address of the 8 26562306a36Sopenharmony_ci * byte standard header. 26662306a36Sopenharmony_ci * @iso_start_frame: For ISO transactions, the starting frame number. 26762306a36Sopenharmony_ci * @iso_number_packets: For ISO transactions, the number of packets in the 26862306a36Sopenharmony_ci * request. 26962306a36Sopenharmony_ci * @iso_packets: For ISO transactions, the sub packets in the request. 27062306a36Sopenharmony_ci * @actual_bytes: Actual bytes transfer for this transaction. 27162306a36Sopenharmony_ci * @stage: For control transactions, the current stage. 27262306a36Sopenharmony_ci * @urb: URB. 27362306a36Sopenharmony_ci */ 27462306a36Sopenharmony_cistruct cvmx_usb_transaction { 27562306a36Sopenharmony_ci struct list_head node; 27662306a36Sopenharmony_ci enum cvmx_usb_transfer type; 27762306a36Sopenharmony_ci u64 buffer; 27862306a36Sopenharmony_ci int buffer_length; 27962306a36Sopenharmony_ci u64 control_header; 28062306a36Sopenharmony_ci int iso_start_frame; 28162306a36Sopenharmony_ci int iso_number_packets; 28262306a36Sopenharmony_ci struct cvmx_usb_iso_packet *iso_packets; 28362306a36Sopenharmony_ci int xfersize; 28462306a36Sopenharmony_ci int pktcnt; 28562306a36Sopenharmony_ci int retries; 28662306a36Sopenharmony_ci int actual_bytes; 28762306a36Sopenharmony_ci enum cvmx_usb_stage stage; 28862306a36Sopenharmony_ci struct urb *urb; 28962306a36Sopenharmony_ci}; 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_ci/** 29262306a36Sopenharmony_ci * struct cvmx_usb_pipe - a pipe represents a virtual connection between Octeon 29362306a36Sopenharmony_ci * and some USB device. It contains a list of pending 29462306a36Sopenharmony_ci * request to the device. 29562306a36Sopenharmony_ci * 29662306a36Sopenharmony_ci * @node: List node for pipe list 29762306a36Sopenharmony_ci * @next: Pipe after this one in the list 29862306a36Sopenharmony_ci * @transactions: List of pending transactions 29962306a36Sopenharmony_ci * @interval: For periodic pipes, the interval between packets in 30062306a36Sopenharmony_ci * frames 30162306a36Sopenharmony_ci * @next_tx_frame: The next frame this pipe is allowed to transmit on 30262306a36Sopenharmony_ci * @flags: State flags for this pipe 30362306a36Sopenharmony_ci * @device_speed: Speed of device connected to this pipe 30462306a36Sopenharmony_ci * @transfer_type: Type of transaction supported by this pipe 30562306a36Sopenharmony_ci * @transfer_dir: IN or OUT. Ignored for Control 30662306a36Sopenharmony_ci * @multi_count: Max packet in a row for the device 30762306a36Sopenharmony_ci * @max_packet: The device's maximum packet size in bytes 30862306a36Sopenharmony_ci * @device_addr: USB device address at other end of pipe 30962306a36Sopenharmony_ci * @endpoint_num: USB endpoint number at other end of pipe 31062306a36Sopenharmony_ci * @hub_device_addr: Hub address this device is connected to 31162306a36Sopenharmony_ci * @hub_port: Hub port this device is connected to 31262306a36Sopenharmony_ci * @pid_toggle: This toggles between 0/1 on every packet send to track 31362306a36Sopenharmony_ci * the data pid needed 31462306a36Sopenharmony_ci * @channel: Hardware DMA channel for this pipe 31562306a36Sopenharmony_ci * @split_sc_frame: The low order bits of the frame number the split 31662306a36Sopenharmony_ci * complete should be sent on 31762306a36Sopenharmony_ci */ 31862306a36Sopenharmony_cistruct cvmx_usb_pipe { 31962306a36Sopenharmony_ci struct list_head node; 32062306a36Sopenharmony_ci struct list_head transactions; 32162306a36Sopenharmony_ci u64 interval; 32262306a36Sopenharmony_ci u64 next_tx_frame; 32362306a36Sopenharmony_ci enum cvmx_usb_pipe_flags flags; 32462306a36Sopenharmony_ci enum cvmx_usb_speed device_speed; 32562306a36Sopenharmony_ci enum cvmx_usb_transfer transfer_type; 32662306a36Sopenharmony_ci enum cvmx_usb_direction transfer_dir; 32762306a36Sopenharmony_ci int multi_count; 32862306a36Sopenharmony_ci u16 max_packet; 32962306a36Sopenharmony_ci u8 device_addr; 33062306a36Sopenharmony_ci u8 endpoint_num; 33162306a36Sopenharmony_ci u8 hub_device_addr; 33262306a36Sopenharmony_ci u8 hub_port; 33362306a36Sopenharmony_ci u8 pid_toggle; 33462306a36Sopenharmony_ci u8 channel; 33562306a36Sopenharmony_ci s8 split_sc_frame; 33662306a36Sopenharmony_ci}; 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_cistruct cvmx_usb_tx_fifo { 33962306a36Sopenharmony_ci struct { 34062306a36Sopenharmony_ci int channel; 34162306a36Sopenharmony_ci int size; 34262306a36Sopenharmony_ci u64 address; 34362306a36Sopenharmony_ci } entry[MAX_CHANNELS + 1]; 34462306a36Sopenharmony_ci int head; 34562306a36Sopenharmony_ci int tail; 34662306a36Sopenharmony_ci}; 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_ci/** 34962306a36Sopenharmony_ci * struct octeon_hcd - the state of the USB block 35062306a36Sopenharmony_ci * 35162306a36Sopenharmony_ci * lock: Serialization lock. 35262306a36Sopenharmony_ci * init_flags: Flags passed to initialize. 35362306a36Sopenharmony_ci * index: Which USB block this is for. 35462306a36Sopenharmony_ci * idle_hardware_channels: Bit set for every idle hardware channel. 35562306a36Sopenharmony_ci * usbcx_hprt: Stored port status so we don't need to read a CSR to 35662306a36Sopenharmony_ci * determine splits. 35762306a36Sopenharmony_ci * pipe_for_channel: Map channels to pipes. 35862306a36Sopenharmony_ci * pipe: Storage for pipes. 35962306a36Sopenharmony_ci * indent: Used by debug output to indent functions. 36062306a36Sopenharmony_ci * port_status: Last port status used for change notification. 36162306a36Sopenharmony_ci * idle_pipes: List of open pipes that have no transactions. 36262306a36Sopenharmony_ci * active_pipes: Active pipes indexed by transfer type. 36362306a36Sopenharmony_ci * frame_number: Increments every SOF interrupt for time keeping. 36462306a36Sopenharmony_ci * active_split: Points to the current active split, or NULL. 36562306a36Sopenharmony_ci */ 36662306a36Sopenharmony_cistruct octeon_hcd { 36762306a36Sopenharmony_ci spinlock_t lock; /* serialization lock */ 36862306a36Sopenharmony_ci int init_flags; 36962306a36Sopenharmony_ci int index; 37062306a36Sopenharmony_ci int idle_hardware_channels; 37162306a36Sopenharmony_ci union cvmx_usbcx_hprt usbcx_hprt; 37262306a36Sopenharmony_ci struct cvmx_usb_pipe *pipe_for_channel[MAX_CHANNELS]; 37362306a36Sopenharmony_ci int indent; 37462306a36Sopenharmony_ci struct cvmx_usb_port_status port_status; 37562306a36Sopenharmony_ci struct list_head idle_pipes; 37662306a36Sopenharmony_ci struct list_head active_pipes[4]; 37762306a36Sopenharmony_ci u64 frame_number; 37862306a36Sopenharmony_ci struct cvmx_usb_transaction *active_split; 37962306a36Sopenharmony_ci struct cvmx_usb_tx_fifo periodic; 38062306a36Sopenharmony_ci struct cvmx_usb_tx_fifo nonperiodic; 38162306a36Sopenharmony_ci}; 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_ci/* 38462306a36Sopenharmony_ci * This macro logically sets a single field in a CSR. It does the sequence 38562306a36Sopenharmony_ci * read, modify, and write 38662306a36Sopenharmony_ci */ 38762306a36Sopenharmony_ci#define USB_SET_FIELD32(address, _union, field, value) \ 38862306a36Sopenharmony_ci do { \ 38962306a36Sopenharmony_ci union _union c; \ 39062306a36Sopenharmony_ci \ 39162306a36Sopenharmony_ci c.u32 = cvmx_usb_read_csr32(usb, address); \ 39262306a36Sopenharmony_ci c.s.field = value; \ 39362306a36Sopenharmony_ci cvmx_usb_write_csr32(usb, address, c.u32); \ 39462306a36Sopenharmony_ci } while (0) 39562306a36Sopenharmony_ci 39662306a36Sopenharmony_ci/* Returns the IO address to push/pop stuff data from the FIFOs */ 39762306a36Sopenharmony_ci#define USB_FIFO_ADDRESS(channel, usb_index) \ 39862306a36Sopenharmony_ci (CVMX_USBCX_GOTGCTL(usb_index) + ((channel) + 1) * 0x1000) 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_ci/** 40162306a36Sopenharmony_ci * struct octeon_temp_buffer - a bounce buffer for USB transfers 40262306a36Sopenharmony_ci * @orig_buffer: the original buffer passed by the USB stack 40362306a36Sopenharmony_ci * @data: the newly allocated temporary buffer (excluding meta-data) 40462306a36Sopenharmony_ci * 40562306a36Sopenharmony_ci * Both the DMA engine and FIFO mode will always transfer full 32-bit words. If 40662306a36Sopenharmony_ci * the buffer is too short, we need to allocate a temporary one, and this struct 40762306a36Sopenharmony_ci * represents it. 40862306a36Sopenharmony_ci */ 40962306a36Sopenharmony_cistruct octeon_temp_buffer { 41062306a36Sopenharmony_ci void *orig_buffer; 41162306a36Sopenharmony_ci u8 data[]; 41262306a36Sopenharmony_ci}; 41362306a36Sopenharmony_ci 41462306a36Sopenharmony_cistatic inline struct usb_hcd *octeon_to_hcd(struct octeon_hcd *p) 41562306a36Sopenharmony_ci{ 41662306a36Sopenharmony_ci return container_of((void *)p, struct usb_hcd, hcd_priv); 41762306a36Sopenharmony_ci} 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_ci/** 42062306a36Sopenharmony_ci * octeon_alloc_temp_buffer - allocate a temporary buffer for USB transfer 42162306a36Sopenharmony_ci * (if needed) 42262306a36Sopenharmony_ci * @urb: URB. 42362306a36Sopenharmony_ci * @mem_flags: Memory allocation flags. 42462306a36Sopenharmony_ci * 42562306a36Sopenharmony_ci * This function allocates a temporary bounce buffer whenever it's needed 42662306a36Sopenharmony_ci * due to HW limitations. 42762306a36Sopenharmony_ci */ 42862306a36Sopenharmony_cistatic int octeon_alloc_temp_buffer(struct urb *urb, gfp_t mem_flags) 42962306a36Sopenharmony_ci{ 43062306a36Sopenharmony_ci struct octeon_temp_buffer *temp; 43162306a36Sopenharmony_ci 43262306a36Sopenharmony_ci if (urb->num_sgs || urb->sg || 43362306a36Sopenharmony_ci (urb->transfer_flags & URB_NO_TRANSFER_DMA_MAP) || 43462306a36Sopenharmony_ci !(urb->transfer_buffer_length % sizeof(u32))) 43562306a36Sopenharmony_ci return 0; 43662306a36Sopenharmony_ci 43762306a36Sopenharmony_ci temp = kmalloc(ALIGN(urb->transfer_buffer_length, sizeof(u32)) + 43862306a36Sopenharmony_ci sizeof(*temp), mem_flags); 43962306a36Sopenharmony_ci if (!temp) 44062306a36Sopenharmony_ci return -ENOMEM; 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_ci temp->orig_buffer = urb->transfer_buffer; 44362306a36Sopenharmony_ci if (usb_urb_dir_out(urb)) 44462306a36Sopenharmony_ci memcpy(temp->data, urb->transfer_buffer, 44562306a36Sopenharmony_ci urb->transfer_buffer_length); 44662306a36Sopenharmony_ci urb->transfer_buffer = temp->data; 44762306a36Sopenharmony_ci urb->transfer_flags |= URB_ALIGNED_TEMP_BUFFER; 44862306a36Sopenharmony_ci 44962306a36Sopenharmony_ci return 0; 45062306a36Sopenharmony_ci} 45162306a36Sopenharmony_ci 45262306a36Sopenharmony_ci/** 45362306a36Sopenharmony_ci * octeon_free_temp_buffer - free a temporary buffer used by USB transfers. 45462306a36Sopenharmony_ci * @urb: URB. 45562306a36Sopenharmony_ci * 45662306a36Sopenharmony_ci * Frees a buffer allocated by octeon_alloc_temp_buffer(). 45762306a36Sopenharmony_ci */ 45862306a36Sopenharmony_cistatic void octeon_free_temp_buffer(struct urb *urb) 45962306a36Sopenharmony_ci{ 46062306a36Sopenharmony_ci struct octeon_temp_buffer *temp; 46162306a36Sopenharmony_ci size_t length; 46262306a36Sopenharmony_ci 46362306a36Sopenharmony_ci if (!(urb->transfer_flags & URB_ALIGNED_TEMP_BUFFER)) 46462306a36Sopenharmony_ci return; 46562306a36Sopenharmony_ci 46662306a36Sopenharmony_ci temp = container_of(urb->transfer_buffer, struct octeon_temp_buffer, 46762306a36Sopenharmony_ci data); 46862306a36Sopenharmony_ci if (usb_urb_dir_in(urb)) { 46962306a36Sopenharmony_ci if (usb_pipeisoc(urb->pipe)) 47062306a36Sopenharmony_ci length = urb->transfer_buffer_length; 47162306a36Sopenharmony_ci else 47262306a36Sopenharmony_ci length = urb->actual_length; 47362306a36Sopenharmony_ci 47462306a36Sopenharmony_ci memcpy(temp->orig_buffer, urb->transfer_buffer, length); 47562306a36Sopenharmony_ci } 47662306a36Sopenharmony_ci urb->transfer_buffer = temp->orig_buffer; 47762306a36Sopenharmony_ci urb->transfer_flags &= ~URB_ALIGNED_TEMP_BUFFER; 47862306a36Sopenharmony_ci kfree(temp); 47962306a36Sopenharmony_ci} 48062306a36Sopenharmony_ci 48162306a36Sopenharmony_ci/** 48262306a36Sopenharmony_ci * octeon_map_urb_for_dma - Octeon-specific map_urb_for_dma(). 48362306a36Sopenharmony_ci * @hcd: USB HCD structure. 48462306a36Sopenharmony_ci * @urb: URB. 48562306a36Sopenharmony_ci * @mem_flags: Memory allocation flags. 48662306a36Sopenharmony_ci */ 48762306a36Sopenharmony_cistatic int octeon_map_urb_for_dma(struct usb_hcd *hcd, struct urb *urb, 48862306a36Sopenharmony_ci gfp_t mem_flags) 48962306a36Sopenharmony_ci{ 49062306a36Sopenharmony_ci int ret; 49162306a36Sopenharmony_ci 49262306a36Sopenharmony_ci ret = octeon_alloc_temp_buffer(urb, mem_flags); 49362306a36Sopenharmony_ci if (ret) 49462306a36Sopenharmony_ci return ret; 49562306a36Sopenharmony_ci 49662306a36Sopenharmony_ci ret = usb_hcd_map_urb_for_dma(hcd, urb, mem_flags); 49762306a36Sopenharmony_ci if (ret) 49862306a36Sopenharmony_ci octeon_free_temp_buffer(urb); 49962306a36Sopenharmony_ci 50062306a36Sopenharmony_ci return ret; 50162306a36Sopenharmony_ci} 50262306a36Sopenharmony_ci 50362306a36Sopenharmony_ci/** 50462306a36Sopenharmony_ci * octeon_unmap_urb_for_dma - Octeon-specific unmap_urb_for_dma() 50562306a36Sopenharmony_ci * @hcd: USB HCD structure. 50662306a36Sopenharmony_ci * @urb: URB. 50762306a36Sopenharmony_ci */ 50862306a36Sopenharmony_cistatic void octeon_unmap_urb_for_dma(struct usb_hcd *hcd, struct urb *urb) 50962306a36Sopenharmony_ci{ 51062306a36Sopenharmony_ci usb_hcd_unmap_urb_for_dma(hcd, urb); 51162306a36Sopenharmony_ci octeon_free_temp_buffer(urb); 51262306a36Sopenharmony_ci} 51362306a36Sopenharmony_ci 51462306a36Sopenharmony_ci/** 51562306a36Sopenharmony_ci * Read a USB 32bit CSR. It performs the necessary address swizzle 51662306a36Sopenharmony_ci * for 32bit CSRs and logs the value in a readable format if 51762306a36Sopenharmony_ci * debugging is on. 51862306a36Sopenharmony_ci * 51962306a36Sopenharmony_ci * @usb: USB block this access is for 52062306a36Sopenharmony_ci * @address: 64bit address to read 52162306a36Sopenharmony_ci * 52262306a36Sopenharmony_ci * Returns: Result of the read 52362306a36Sopenharmony_ci */ 52462306a36Sopenharmony_cistatic inline u32 cvmx_usb_read_csr32(struct octeon_hcd *usb, u64 address) 52562306a36Sopenharmony_ci{ 52662306a36Sopenharmony_ci return cvmx_read64_uint32(address ^ 4); 52762306a36Sopenharmony_ci} 52862306a36Sopenharmony_ci 52962306a36Sopenharmony_ci/** 53062306a36Sopenharmony_ci * Write a USB 32bit CSR. It performs the necessary address 53162306a36Sopenharmony_ci * swizzle for 32bit CSRs and logs the value in a readable format 53262306a36Sopenharmony_ci * if debugging is on. 53362306a36Sopenharmony_ci * 53462306a36Sopenharmony_ci * @usb: USB block this access is for 53562306a36Sopenharmony_ci * @address: 64bit address to write 53662306a36Sopenharmony_ci * @value: Value to write 53762306a36Sopenharmony_ci */ 53862306a36Sopenharmony_cistatic inline void cvmx_usb_write_csr32(struct octeon_hcd *usb, 53962306a36Sopenharmony_ci u64 address, u32 value) 54062306a36Sopenharmony_ci{ 54162306a36Sopenharmony_ci cvmx_write64_uint32(address ^ 4, value); 54262306a36Sopenharmony_ci cvmx_read64_uint64(CVMX_USBNX_DMA0_INB_CHN0(usb->index)); 54362306a36Sopenharmony_ci} 54462306a36Sopenharmony_ci 54562306a36Sopenharmony_ci/** 54662306a36Sopenharmony_ci * Return non zero if this pipe connects to a non HIGH speed 54762306a36Sopenharmony_ci * device through a high speed hub. 54862306a36Sopenharmony_ci * 54962306a36Sopenharmony_ci * @usb: USB block this access is for 55062306a36Sopenharmony_ci * @pipe: Pipe to check 55162306a36Sopenharmony_ci * 55262306a36Sopenharmony_ci * Returns: Non zero if we need to do split transactions 55362306a36Sopenharmony_ci */ 55462306a36Sopenharmony_cistatic inline int cvmx_usb_pipe_needs_split(struct octeon_hcd *usb, 55562306a36Sopenharmony_ci struct cvmx_usb_pipe *pipe) 55662306a36Sopenharmony_ci{ 55762306a36Sopenharmony_ci return pipe->device_speed != CVMX_USB_SPEED_HIGH && 55862306a36Sopenharmony_ci usb->usbcx_hprt.s.prtspd == CVMX_USB_SPEED_HIGH; 55962306a36Sopenharmony_ci} 56062306a36Sopenharmony_ci 56162306a36Sopenharmony_ci/** 56262306a36Sopenharmony_ci * Trivial utility function to return the correct PID for a pipe 56362306a36Sopenharmony_ci * 56462306a36Sopenharmony_ci * @pipe: pipe to check 56562306a36Sopenharmony_ci * 56662306a36Sopenharmony_ci * Returns: PID for pipe 56762306a36Sopenharmony_ci */ 56862306a36Sopenharmony_cistatic inline int cvmx_usb_get_data_pid(struct cvmx_usb_pipe *pipe) 56962306a36Sopenharmony_ci{ 57062306a36Sopenharmony_ci if (pipe->pid_toggle) 57162306a36Sopenharmony_ci return 2; /* Data1 */ 57262306a36Sopenharmony_ci return 0; /* Data0 */ 57362306a36Sopenharmony_ci} 57462306a36Sopenharmony_ci 57562306a36Sopenharmony_ci/* Loops through register until txfflsh or rxfflsh become zero.*/ 57662306a36Sopenharmony_cistatic int cvmx_wait_tx_rx(struct octeon_hcd *usb, int fflsh_type) 57762306a36Sopenharmony_ci{ 57862306a36Sopenharmony_ci int result; 57962306a36Sopenharmony_ci u64 address = CVMX_USBCX_GRSTCTL(usb->index); 58062306a36Sopenharmony_ci u64 done = cvmx_get_cycle() + 100 * 58162306a36Sopenharmony_ci (u64)octeon_get_clock_rate / 1000000; 58262306a36Sopenharmony_ci union cvmx_usbcx_grstctl c; 58362306a36Sopenharmony_ci 58462306a36Sopenharmony_ci while (1) { 58562306a36Sopenharmony_ci c.u32 = cvmx_usb_read_csr32(usb, address); 58662306a36Sopenharmony_ci if (fflsh_type == 0 && c.s.txfflsh == 0) { 58762306a36Sopenharmony_ci result = 0; 58862306a36Sopenharmony_ci break; 58962306a36Sopenharmony_ci } else if (fflsh_type == 1 && c.s.rxfflsh == 0) { 59062306a36Sopenharmony_ci result = 0; 59162306a36Sopenharmony_ci break; 59262306a36Sopenharmony_ci } else if (cvmx_get_cycle() > done) { 59362306a36Sopenharmony_ci result = -1; 59462306a36Sopenharmony_ci break; 59562306a36Sopenharmony_ci } 59662306a36Sopenharmony_ci 59762306a36Sopenharmony_ci __delay(100); 59862306a36Sopenharmony_ci } 59962306a36Sopenharmony_ci return result; 60062306a36Sopenharmony_ci} 60162306a36Sopenharmony_ci 60262306a36Sopenharmony_cistatic void cvmx_fifo_setup(struct octeon_hcd *usb) 60362306a36Sopenharmony_ci{ 60462306a36Sopenharmony_ci union cvmx_usbcx_ghwcfg3 usbcx_ghwcfg3; 60562306a36Sopenharmony_ci union cvmx_usbcx_gnptxfsiz npsiz; 60662306a36Sopenharmony_ci union cvmx_usbcx_hptxfsiz psiz; 60762306a36Sopenharmony_ci 60862306a36Sopenharmony_ci usbcx_ghwcfg3.u32 = cvmx_usb_read_csr32(usb, 60962306a36Sopenharmony_ci CVMX_USBCX_GHWCFG3(usb->index)); 61062306a36Sopenharmony_ci 61162306a36Sopenharmony_ci /* 61262306a36Sopenharmony_ci * Program the USBC_GRXFSIZ register to select the size of the receive 61362306a36Sopenharmony_ci * FIFO (25%). 61462306a36Sopenharmony_ci */ 61562306a36Sopenharmony_ci USB_SET_FIELD32(CVMX_USBCX_GRXFSIZ(usb->index), cvmx_usbcx_grxfsiz, 61662306a36Sopenharmony_ci rxfdep, usbcx_ghwcfg3.s.dfifodepth / 4); 61762306a36Sopenharmony_ci 61862306a36Sopenharmony_ci /* 61962306a36Sopenharmony_ci * Program the USBC_GNPTXFSIZ register to select the size and the start 62062306a36Sopenharmony_ci * address of the non-periodic transmit FIFO for nonperiodic 62162306a36Sopenharmony_ci * transactions (50%). 62262306a36Sopenharmony_ci */ 62362306a36Sopenharmony_ci npsiz.u32 = cvmx_usb_read_csr32(usb, CVMX_USBCX_GNPTXFSIZ(usb->index)); 62462306a36Sopenharmony_ci npsiz.s.nptxfdep = usbcx_ghwcfg3.s.dfifodepth / 2; 62562306a36Sopenharmony_ci npsiz.s.nptxfstaddr = usbcx_ghwcfg3.s.dfifodepth / 4; 62662306a36Sopenharmony_ci cvmx_usb_write_csr32(usb, CVMX_USBCX_GNPTXFSIZ(usb->index), npsiz.u32); 62762306a36Sopenharmony_ci 62862306a36Sopenharmony_ci /* 62962306a36Sopenharmony_ci * Program the USBC_HPTXFSIZ register to select the size and start 63062306a36Sopenharmony_ci * address of the periodic transmit FIFO for periodic transactions 63162306a36Sopenharmony_ci * (25%). 63262306a36Sopenharmony_ci */ 63362306a36Sopenharmony_ci psiz.u32 = cvmx_usb_read_csr32(usb, CVMX_USBCX_HPTXFSIZ(usb->index)); 63462306a36Sopenharmony_ci psiz.s.ptxfsize = usbcx_ghwcfg3.s.dfifodepth / 4; 63562306a36Sopenharmony_ci psiz.s.ptxfstaddr = 3 * usbcx_ghwcfg3.s.dfifodepth / 4; 63662306a36Sopenharmony_ci cvmx_usb_write_csr32(usb, CVMX_USBCX_HPTXFSIZ(usb->index), psiz.u32); 63762306a36Sopenharmony_ci 63862306a36Sopenharmony_ci /* Flush all FIFOs */ 63962306a36Sopenharmony_ci USB_SET_FIELD32(CVMX_USBCX_GRSTCTL(usb->index), 64062306a36Sopenharmony_ci cvmx_usbcx_grstctl, txfnum, 0x10); 64162306a36Sopenharmony_ci USB_SET_FIELD32(CVMX_USBCX_GRSTCTL(usb->index), 64262306a36Sopenharmony_ci cvmx_usbcx_grstctl, txfflsh, 1); 64362306a36Sopenharmony_ci cvmx_wait_tx_rx(usb, 0); 64462306a36Sopenharmony_ci USB_SET_FIELD32(CVMX_USBCX_GRSTCTL(usb->index), 64562306a36Sopenharmony_ci cvmx_usbcx_grstctl, rxfflsh, 1); 64662306a36Sopenharmony_ci cvmx_wait_tx_rx(usb, 1); 64762306a36Sopenharmony_ci} 64862306a36Sopenharmony_ci 64962306a36Sopenharmony_ci/** 65062306a36Sopenharmony_ci * Shutdown a USB port after a call to cvmx_usb_initialize(). 65162306a36Sopenharmony_ci * The port should be disabled with all pipes closed when this 65262306a36Sopenharmony_ci * function is called. 65362306a36Sopenharmony_ci * 65462306a36Sopenharmony_ci * @usb: USB device state populated by cvmx_usb_initialize(). 65562306a36Sopenharmony_ci * 65662306a36Sopenharmony_ci * Returns: 0 or a negative error code. 65762306a36Sopenharmony_ci */ 65862306a36Sopenharmony_cistatic int cvmx_usb_shutdown(struct octeon_hcd *usb) 65962306a36Sopenharmony_ci{ 66062306a36Sopenharmony_ci union cvmx_usbnx_clk_ctl usbn_clk_ctl; 66162306a36Sopenharmony_ci 66262306a36Sopenharmony_ci /* Make sure all pipes are closed */ 66362306a36Sopenharmony_ci if (!list_empty(&usb->idle_pipes) || 66462306a36Sopenharmony_ci !list_empty(&usb->active_pipes[CVMX_USB_TRANSFER_ISOCHRONOUS]) || 66562306a36Sopenharmony_ci !list_empty(&usb->active_pipes[CVMX_USB_TRANSFER_INTERRUPT]) || 66662306a36Sopenharmony_ci !list_empty(&usb->active_pipes[CVMX_USB_TRANSFER_CONTROL]) || 66762306a36Sopenharmony_ci !list_empty(&usb->active_pipes[CVMX_USB_TRANSFER_BULK])) 66862306a36Sopenharmony_ci return -EBUSY; 66962306a36Sopenharmony_ci 67062306a36Sopenharmony_ci /* Disable the clocks and put them in power on reset */ 67162306a36Sopenharmony_ci usbn_clk_ctl.u64 = cvmx_read64_uint64(CVMX_USBNX_CLK_CTL(usb->index)); 67262306a36Sopenharmony_ci usbn_clk_ctl.s.enable = 1; 67362306a36Sopenharmony_ci usbn_clk_ctl.s.por = 1; 67462306a36Sopenharmony_ci usbn_clk_ctl.s.hclk_rst = 1; 67562306a36Sopenharmony_ci usbn_clk_ctl.s.prst = 0; 67662306a36Sopenharmony_ci usbn_clk_ctl.s.hrst = 0; 67762306a36Sopenharmony_ci cvmx_write64_uint64(CVMX_USBNX_CLK_CTL(usb->index), usbn_clk_ctl.u64); 67862306a36Sopenharmony_ci return 0; 67962306a36Sopenharmony_ci} 68062306a36Sopenharmony_ci 68162306a36Sopenharmony_ci/** 68262306a36Sopenharmony_ci * Initialize a USB port for use. This must be called before any 68362306a36Sopenharmony_ci * other access to the Octeon USB port is made. The port starts 68462306a36Sopenharmony_ci * off in the disabled state. 68562306a36Sopenharmony_ci * 68662306a36Sopenharmony_ci * @dev: Pointer to struct device for logging purposes. 68762306a36Sopenharmony_ci * @usb: Pointer to struct octeon_hcd. 68862306a36Sopenharmony_ci * 68962306a36Sopenharmony_ci * Returns: 0 or a negative error code. 69062306a36Sopenharmony_ci */ 69162306a36Sopenharmony_cistatic int cvmx_usb_initialize(struct device *dev, 69262306a36Sopenharmony_ci struct octeon_hcd *usb) 69362306a36Sopenharmony_ci{ 69462306a36Sopenharmony_ci int channel; 69562306a36Sopenharmony_ci int divisor; 69662306a36Sopenharmony_ci int retries = 0; 69762306a36Sopenharmony_ci union cvmx_usbcx_hcfg usbcx_hcfg; 69862306a36Sopenharmony_ci union cvmx_usbnx_clk_ctl usbn_clk_ctl; 69962306a36Sopenharmony_ci union cvmx_usbcx_gintsts usbc_gintsts; 70062306a36Sopenharmony_ci union cvmx_usbcx_gahbcfg usbcx_gahbcfg; 70162306a36Sopenharmony_ci union cvmx_usbcx_gintmsk usbcx_gintmsk; 70262306a36Sopenharmony_ci union cvmx_usbcx_gusbcfg usbcx_gusbcfg; 70362306a36Sopenharmony_ci union cvmx_usbnx_usbp_ctl_status usbn_usbp_ctl_status; 70462306a36Sopenharmony_ci 70562306a36Sopenharmony_ciretry: 70662306a36Sopenharmony_ci /* 70762306a36Sopenharmony_ci * Power On Reset and PHY Initialization 70862306a36Sopenharmony_ci * 70962306a36Sopenharmony_ci * 1. Wait for DCOK to assert (nothing to do) 71062306a36Sopenharmony_ci * 71162306a36Sopenharmony_ci * 2a. Write USBN0/1_CLK_CTL[POR] = 1 and 71262306a36Sopenharmony_ci * USBN0/1_CLK_CTL[HRST,PRST,HCLK_RST] = 0 71362306a36Sopenharmony_ci */ 71462306a36Sopenharmony_ci usbn_clk_ctl.u64 = cvmx_read64_uint64(CVMX_USBNX_CLK_CTL(usb->index)); 71562306a36Sopenharmony_ci usbn_clk_ctl.s.por = 1; 71662306a36Sopenharmony_ci usbn_clk_ctl.s.hrst = 0; 71762306a36Sopenharmony_ci usbn_clk_ctl.s.prst = 0; 71862306a36Sopenharmony_ci usbn_clk_ctl.s.hclk_rst = 0; 71962306a36Sopenharmony_ci usbn_clk_ctl.s.enable = 0; 72062306a36Sopenharmony_ci /* 72162306a36Sopenharmony_ci * 2b. Select the USB reference clock/crystal parameters by writing 72262306a36Sopenharmony_ci * appropriate values to USBN0/1_CLK_CTL[P_C_SEL, P_RTYPE, P_COM_ON] 72362306a36Sopenharmony_ci */ 72462306a36Sopenharmony_ci if (usb->init_flags & CVMX_USB_INITIALIZE_FLAGS_CLOCK_XO_GND) { 72562306a36Sopenharmony_ci /* 72662306a36Sopenharmony_ci * The USB port uses 12/24/48MHz 2.5V board clock 72762306a36Sopenharmony_ci * source at USB_XO. USB_XI should be tied to GND. 72862306a36Sopenharmony_ci * Most Octeon evaluation boards require this setting 72962306a36Sopenharmony_ci */ 73062306a36Sopenharmony_ci if (OCTEON_IS_MODEL(OCTEON_CN3XXX) || 73162306a36Sopenharmony_ci OCTEON_IS_MODEL(OCTEON_CN56XX) || 73262306a36Sopenharmony_ci OCTEON_IS_MODEL(OCTEON_CN50XX)) 73362306a36Sopenharmony_ci /* From CN56XX,CN50XX,CN31XX,CN30XX manuals */ 73462306a36Sopenharmony_ci usbn_clk_ctl.s.p_rtype = 2; /* p_rclk=1 & p_xenbn=0 */ 73562306a36Sopenharmony_ci else 73662306a36Sopenharmony_ci /* From CN52XX manual */ 73762306a36Sopenharmony_ci usbn_clk_ctl.s.p_rtype = 1; 73862306a36Sopenharmony_ci 73962306a36Sopenharmony_ci switch (usb->init_flags & 74062306a36Sopenharmony_ci CVMX_USB_INITIALIZE_FLAGS_CLOCK_MHZ_MASK) { 74162306a36Sopenharmony_ci case CVMX_USB_INITIALIZE_FLAGS_CLOCK_12MHZ: 74262306a36Sopenharmony_ci usbn_clk_ctl.s.p_c_sel = 0; 74362306a36Sopenharmony_ci break; 74462306a36Sopenharmony_ci case CVMX_USB_INITIALIZE_FLAGS_CLOCK_24MHZ: 74562306a36Sopenharmony_ci usbn_clk_ctl.s.p_c_sel = 1; 74662306a36Sopenharmony_ci break; 74762306a36Sopenharmony_ci case CVMX_USB_INITIALIZE_FLAGS_CLOCK_48MHZ: 74862306a36Sopenharmony_ci usbn_clk_ctl.s.p_c_sel = 2; 74962306a36Sopenharmony_ci break; 75062306a36Sopenharmony_ci } 75162306a36Sopenharmony_ci } else { 75262306a36Sopenharmony_ci /* 75362306a36Sopenharmony_ci * The USB port uses a 12MHz crystal as clock source 75462306a36Sopenharmony_ci * at USB_XO and USB_XI 75562306a36Sopenharmony_ci */ 75662306a36Sopenharmony_ci if (OCTEON_IS_MODEL(OCTEON_CN3XXX)) 75762306a36Sopenharmony_ci /* From CN31XX,CN30XX manual */ 75862306a36Sopenharmony_ci usbn_clk_ctl.s.p_rtype = 3; /* p_rclk=1 & p_xenbn=1 */ 75962306a36Sopenharmony_ci else 76062306a36Sopenharmony_ci /* From CN56XX,CN52XX,CN50XX manuals. */ 76162306a36Sopenharmony_ci usbn_clk_ctl.s.p_rtype = 0; 76262306a36Sopenharmony_ci 76362306a36Sopenharmony_ci usbn_clk_ctl.s.p_c_sel = 0; 76462306a36Sopenharmony_ci } 76562306a36Sopenharmony_ci /* 76662306a36Sopenharmony_ci * 2c. Select the HCLK via writing USBN0/1_CLK_CTL[DIVIDE, DIVIDE2] and 76762306a36Sopenharmony_ci * setting USBN0/1_CLK_CTL[ENABLE] = 1. Divide the core clock down 76862306a36Sopenharmony_ci * such that USB is as close as possible to 125Mhz 76962306a36Sopenharmony_ci */ 77062306a36Sopenharmony_ci divisor = DIV_ROUND_UP(octeon_get_clock_rate(), 125000000); 77162306a36Sopenharmony_ci /* Lower than 4 doesn't seem to work properly */ 77262306a36Sopenharmony_ci if (divisor < 4) 77362306a36Sopenharmony_ci divisor = 4; 77462306a36Sopenharmony_ci usbn_clk_ctl.s.divide = divisor; 77562306a36Sopenharmony_ci usbn_clk_ctl.s.divide2 = 0; 77662306a36Sopenharmony_ci cvmx_write64_uint64(CVMX_USBNX_CLK_CTL(usb->index), usbn_clk_ctl.u64); 77762306a36Sopenharmony_ci 77862306a36Sopenharmony_ci /* 2d. Write USBN0/1_CLK_CTL[HCLK_RST] = 1 */ 77962306a36Sopenharmony_ci usbn_clk_ctl.s.hclk_rst = 1; 78062306a36Sopenharmony_ci cvmx_write64_uint64(CVMX_USBNX_CLK_CTL(usb->index), usbn_clk_ctl.u64); 78162306a36Sopenharmony_ci /* 2e. Wait 64 core-clock cycles for HCLK to stabilize */ 78262306a36Sopenharmony_ci __delay(64); 78362306a36Sopenharmony_ci /* 78462306a36Sopenharmony_ci * 3. Program the power-on reset field in the USBN clock-control 78562306a36Sopenharmony_ci * register: 78662306a36Sopenharmony_ci * USBN_CLK_CTL[POR] = 0 78762306a36Sopenharmony_ci */ 78862306a36Sopenharmony_ci usbn_clk_ctl.s.por = 0; 78962306a36Sopenharmony_ci cvmx_write64_uint64(CVMX_USBNX_CLK_CTL(usb->index), usbn_clk_ctl.u64); 79062306a36Sopenharmony_ci /* 4. Wait 1 ms for PHY clock to start */ 79162306a36Sopenharmony_ci mdelay(1); 79262306a36Sopenharmony_ci /* 79362306a36Sopenharmony_ci * 5. Program the Reset input from automatic test equipment field in the 79462306a36Sopenharmony_ci * USBP control and status register: 79562306a36Sopenharmony_ci * USBN_USBP_CTL_STATUS[ATE_RESET] = 1 79662306a36Sopenharmony_ci */ 79762306a36Sopenharmony_ci usbn_usbp_ctl_status.u64 = 79862306a36Sopenharmony_ci cvmx_read64_uint64(CVMX_USBNX_USBP_CTL_STATUS(usb->index)); 79962306a36Sopenharmony_ci usbn_usbp_ctl_status.s.ate_reset = 1; 80062306a36Sopenharmony_ci cvmx_write64_uint64(CVMX_USBNX_USBP_CTL_STATUS(usb->index), 80162306a36Sopenharmony_ci usbn_usbp_ctl_status.u64); 80262306a36Sopenharmony_ci /* 6. Wait 10 cycles */ 80362306a36Sopenharmony_ci __delay(10); 80462306a36Sopenharmony_ci /* 80562306a36Sopenharmony_ci * 7. Clear ATE_RESET field in the USBN clock-control register: 80662306a36Sopenharmony_ci * USBN_USBP_CTL_STATUS[ATE_RESET] = 0 80762306a36Sopenharmony_ci */ 80862306a36Sopenharmony_ci usbn_usbp_ctl_status.s.ate_reset = 0; 80962306a36Sopenharmony_ci cvmx_write64_uint64(CVMX_USBNX_USBP_CTL_STATUS(usb->index), 81062306a36Sopenharmony_ci usbn_usbp_ctl_status.u64); 81162306a36Sopenharmony_ci /* 81262306a36Sopenharmony_ci * 8. Program the PHY reset field in the USBN clock-control register: 81362306a36Sopenharmony_ci * USBN_CLK_CTL[PRST] = 1 81462306a36Sopenharmony_ci */ 81562306a36Sopenharmony_ci usbn_clk_ctl.s.prst = 1; 81662306a36Sopenharmony_ci cvmx_write64_uint64(CVMX_USBNX_CLK_CTL(usb->index), usbn_clk_ctl.u64); 81762306a36Sopenharmony_ci /* 81862306a36Sopenharmony_ci * 9. Program the USBP control and status register to select host or 81962306a36Sopenharmony_ci * device mode. USBN_USBP_CTL_STATUS[HST_MODE] = 0 for host, = 1 for 82062306a36Sopenharmony_ci * device 82162306a36Sopenharmony_ci */ 82262306a36Sopenharmony_ci usbn_usbp_ctl_status.s.hst_mode = 0; 82362306a36Sopenharmony_ci cvmx_write64_uint64(CVMX_USBNX_USBP_CTL_STATUS(usb->index), 82462306a36Sopenharmony_ci usbn_usbp_ctl_status.u64); 82562306a36Sopenharmony_ci /* 10. Wait 1 us */ 82662306a36Sopenharmony_ci udelay(1); 82762306a36Sopenharmony_ci /* 82862306a36Sopenharmony_ci * 11. Program the hreset_n field in the USBN clock-control register: 82962306a36Sopenharmony_ci * USBN_CLK_CTL[HRST] = 1 83062306a36Sopenharmony_ci */ 83162306a36Sopenharmony_ci usbn_clk_ctl.s.hrst = 1; 83262306a36Sopenharmony_ci cvmx_write64_uint64(CVMX_USBNX_CLK_CTL(usb->index), usbn_clk_ctl.u64); 83362306a36Sopenharmony_ci /* 12. Proceed to USB core initialization */ 83462306a36Sopenharmony_ci usbn_clk_ctl.s.enable = 1; 83562306a36Sopenharmony_ci cvmx_write64_uint64(CVMX_USBNX_CLK_CTL(usb->index), usbn_clk_ctl.u64); 83662306a36Sopenharmony_ci udelay(1); 83762306a36Sopenharmony_ci 83862306a36Sopenharmony_ci /* 83962306a36Sopenharmony_ci * USB Core Initialization 84062306a36Sopenharmony_ci * 84162306a36Sopenharmony_ci * 1. Read USBC_GHWCFG1, USBC_GHWCFG2, USBC_GHWCFG3, USBC_GHWCFG4 to 84262306a36Sopenharmony_ci * determine USB core configuration parameters. 84362306a36Sopenharmony_ci * 84462306a36Sopenharmony_ci * Nothing needed 84562306a36Sopenharmony_ci * 84662306a36Sopenharmony_ci * 2. Program the following fields in the global AHB configuration 84762306a36Sopenharmony_ci * register (USBC_GAHBCFG) 84862306a36Sopenharmony_ci * DMA mode, USBC_GAHBCFG[DMAEn]: 1 = DMA mode, 0 = slave mode 84962306a36Sopenharmony_ci * Burst length, USBC_GAHBCFG[HBSTLEN] = 0 85062306a36Sopenharmony_ci * Nonperiodic TxFIFO empty level (slave mode only), 85162306a36Sopenharmony_ci * USBC_GAHBCFG[NPTXFEMPLVL] 85262306a36Sopenharmony_ci * Periodic TxFIFO empty level (slave mode only), 85362306a36Sopenharmony_ci * USBC_GAHBCFG[PTXFEMPLVL] 85462306a36Sopenharmony_ci * Global interrupt mask, USBC_GAHBCFG[GLBLINTRMSK] = 1 85562306a36Sopenharmony_ci */ 85662306a36Sopenharmony_ci usbcx_gahbcfg.u32 = 0; 85762306a36Sopenharmony_ci usbcx_gahbcfg.s.dmaen = !(usb->init_flags & 85862306a36Sopenharmony_ci CVMX_USB_INITIALIZE_FLAGS_NO_DMA); 85962306a36Sopenharmony_ci usbcx_gahbcfg.s.hbstlen = 0; 86062306a36Sopenharmony_ci usbcx_gahbcfg.s.nptxfemplvl = 1; 86162306a36Sopenharmony_ci usbcx_gahbcfg.s.ptxfemplvl = 1; 86262306a36Sopenharmony_ci usbcx_gahbcfg.s.glblintrmsk = 1; 86362306a36Sopenharmony_ci cvmx_usb_write_csr32(usb, CVMX_USBCX_GAHBCFG(usb->index), 86462306a36Sopenharmony_ci usbcx_gahbcfg.u32); 86562306a36Sopenharmony_ci 86662306a36Sopenharmony_ci /* 86762306a36Sopenharmony_ci * 3. Program the following fields in USBC_GUSBCFG register. 86862306a36Sopenharmony_ci * HS/FS timeout calibration, USBC_GUSBCFG[TOUTCAL] = 0 86962306a36Sopenharmony_ci * ULPI DDR select, USBC_GUSBCFG[DDRSEL] = 0 87062306a36Sopenharmony_ci * USB turnaround time, USBC_GUSBCFG[USBTRDTIM] = 0x5 87162306a36Sopenharmony_ci * PHY low-power clock select, USBC_GUSBCFG[PHYLPWRCLKSEL] = 0 87262306a36Sopenharmony_ci */ 87362306a36Sopenharmony_ci usbcx_gusbcfg.u32 = cvmx_usb_read_csr32(usb, 87462306a36Sopenharmony_ci CVMX_USBCX_GUSBCFG(usb->index)); 87562306a36Sopenharmony_ci usbcx_gusbcfg.s.toutcal = 0; 87662306a36Sopenharmony_ci usbcx_gusbcfg.s.ddrsel = 0; 87762306a36Sopenharmony_ci usbcx_gusbcfg.s.usbtrdtim = 0x5; 87862306a36Sopenharmony_ci usbcx_gusbcfg.s.phylpwrclksel = 0; 87962306a36Sopenharmony_ci cvmx_usb_write_csr32(usb, CVMX_USBCX_GUSBCFG(usb->index), 88062306a36Sopenharmony_ci usbcx_gusbcfg.u32); 88162306a36Sopenharmony_ci 88262306a36Sopenharmony_ci /* 88362306a36Sopenharmony_ci * 4. The software must unmask the following bits in the USBC_GINTMSK 88462306a36Sopenharmony_ci * register. 88562306a36Sopenharmony_ci * OTG interrupt mask, USBC_GINTMSK[OTGINTMSK] = 1 88662306a36Sopenharmony_ci * Mode mismatch interrupt mask, USBC_GINTMSK[MODEMISMSK] = 1 88762306a36Sopenharmony_ci */ 88862306a36Sopenharmony_ci usbcx_gintmsk.u32 = cvmx_usb_read_csr32(usb, 88962306a36Sopenharmony_ci CVMX_USBCX_GINTMSK(usb->index)); 89062306a36Sopenharmony_ci usbcx_gintmsk.s.otgintmsk = 1; 89162306a36Sopenharmony_ci usbcx_gintmsk.s.modemismsk = 1; 89262306a36Sopenharmony_ci usbcx_gintmsk.s.hchintmsk = 1; 89362306a36Sopenharmony_ci usbcx_gintmsk.s.sofmsk = 0; 89462306a36Sopenharmony_ci /* We need RX FIFO interrupts if we don't have DMA */ 89562306a36Sopenharmony_ci if (usb->init_flags & CVMX_USB_INITIALIZE_FLAGS_NO_DMA) 89662306a36Sopenharmony_ci usbcx_gintmsk.s.rxflvlmsk = 1; 89762306a36Sopenharmony_ci cvmx_usb_write_csr32(usb, CVMX_USBCX_GINTMSK(usb->index), 89862306a36Sopenharmony_ci usbcx_gintmsk.u32); 89962306a36Sopenharmony_ci 90062306a36Sopenharmony_ci /* 90162306a36Sopenharmony_ci * Disable all channel interrupts. We'll enable them per channel later. 90262306a36Sopenharmony_ci */ 90362306a36Sopenharmony_ci for (channel = 0; channel < 8; channel++) 90462306a36Sopenharmony_ci cvmx_usb_write_csr32(usb, 90562306a36Sopenharmony_ci CVMX_USBCX_HCINTMSKX(channel, usb->index), 90662306a36Sopenharmony_ci 0); 90762306a36Sopenharmony_ci 90862306a36Sopenharmony_ci /* 90962306a36Sopenharmony_ci * Host Port Initialization 91062306a36Sopenharmony_ci * 91162306a36Sopenharmony_ci * 1. Program the host-port interrupt-mask field to unmask, 91262306a36Sopenharmony_ci * USBC_GINTMSK[PRTINT] = 1 91362306a36Sopenharmony_ci */ 91462306a36Sopenharmony_ci USB_SET_FIELD32(CVMX_USBCX_GINTMSK(usb->index), 91562306a36Sopenharmony_ci cvmx_usbcx_gintmsk, prtintmsk, 1); 91662306a36Sopenharmony_ci USB_SET_FIELD32(CVMX_USBCX_GINTMSK(usb->index), 91762306a36Sopenharmony_ci cvmx_usbcx_gintmsk, disconnintmsk, 1); 91862306a36Sopenharmony_ci 91962306a36Sopenharmony_ci /* 92062306a36Sopenharmony_ci * 2. Program the USBC_HCFG register to select full-speed host 92162306a36Sopenharmony_ci * or high-speed host. 92262306a36Sopenharmony_ci */ 92362306a36Sopenharmony_ci usbcx_hcfg.u32 = cvmx_usb_read_csr32(usb, CVMX_USBCX_HCFG(usb->index)); 92462306a36Sopenharmony_ci usbcx_hcfg.s.fslssupp = 0; 92562306a36Sopenharmony_ci usbcx_hcfg.s.fslspclksel = 0; 92662306a36Sopenharmony_ci cvmx_usb_write_csr32(usb, CVMX_USBCX_HCFG(usb->index), usbcx_hcfg.u32); 92762306a36Sopenharmony_ci 92862306a36Sopenharmony_ci cvmx_fifo_setup(usb); 92962306a36Sopenharmony_ci 93062306a36Sopenharmony_ci /* 93162306a36Sopenharmony_ci * If the controller is getting port events right after the reset, it 93262306a36Sopenharmony_ci * means the initialization failed. Try resetting the controller again 93362306a36Sopenharmony_ci * in such case. This is seen to happen after cold boot on DSR-1000N. 93462306a36Sopenharmony_ci */ 93562306a36Sopenharmony_ci usbc_gintsts.u32 = cvmx_usb_read_csr32(usb, 93662306a36Sopenharmony_ci CVMX_USBCX_GINTSTS(usb->index)); 93762306a36Sopenharmony_ci cvmx_usb_write_csr32(usb, CVMX_USBCX_GINTSTS(usb->index), 93862306a36Sopenharmony_ci usbc_gintsts.u32); 93962306a36Sopenharmony_ci dev_dbg(dev, "gintsts after reset: 0x%x\n", (int)usbc_gintsts.u32); 94062306a36Sopenharmony_ci if (!usbc_gintsts.s.disconnint && !usbc_gintsts.s.prtint) 94162306a36Sopenharmony_ci return 0; 94262306a36Sopenharmony_ci if (retries++ >= 5) 94362306a36Sopenharmony_ci return -EAGAIN; 94462306a36Sopenharmony_ci dev_info(dev, "controller reset failed (gintsts=0x%x) - retrying\n", 94562306a36Sopenharmony_ci (int)usbc_gintsts.u32); 94662306a36Sopenharmony_ci msleep(50); 94762306a36Sopenharmony_ci cvmx_usb_shutdown(usb); 94862306a36Sopenharmony_ci msleep(50); 94962306a36Sopenharmony_ci goto retry; 95062306a36Sopenharmony_ci} 95162306a36Sopenharmony_ci 95262306a36Sopenharmony_ci/** 95362306a36Sopenharmony_ci * Reset a USB port. After this call succeeds, the USB port is 95462306a36Sopenharmony_ci * online and servicing requests. 95562306a36Sopenharmony_ci * 95662306a36Sopenharmony_ci * @usb: USB device state populated by cvmx_usb_initialize(). 95762306a36Sopenharmony_ci */ 95862306a36Sopenharmony_cistatic void cvmx_usb_reset_port(struct octeon_hcd *usb) 95962306a36Sopenharmony_ci{ 96062306a36Sopenharmony_ci usb->usbcx_hprt.u32 = cvmx_usb_read_csr32(usb, 96162306a36Sopenharmony_ci CVMX_USBCX_HPRT(usb->index)); 96262306a36Sopenharmony_ci 96362306a36Sopenharmony_ci /* Program the port reset bit to start the reset process */ 96462306a36Sopenharmony_ci USB_SET_FIELD32(CVMX_USBCX_HPRT(usb->index), cvmx_usbcx_hprt, 96562306a36Sopenharmony_ci prtrst, 1); 96662306a36Sopenharmony_ci 96762306a36Sopenharmony_ci /* 96862306a36Sopenharmony_ci * Wait at least 50ms (high speed), or 10ms (full speed) for the reset 96962306a36Sopenharmony_ci * process to complete. 97062306a36Sopenharmony_ci */ 97162306a36Sopenharmony_ci mdelay(50); 97262306a36Sopenharmony_ci 97362306a36Sopenharmony_ci /* Program the port reset bit to 0, USBC_HPRT[PRTRST] = 0 */ 97462306a36Sopenharmony_ci USB_SET_FIELD32(CVMX_USBCX_HPRT(usb->index), cvmx_usbcx_hprt, 97562306a36Sopenharmony_ci prtrst, 0); 97662306a36Sopenharmony_ci 97762306a36Sopenharmony_ci /* 97862306a36Sopenharmony_ci * Read the port speed field to get the enumerated speed, 97962306a36Sopenharmony_ci * USBC_HPRT[PRTSPD]. 98062306a36Sopenharmony_ci */ 98162306a36Sopenharmony_ci usb->usbcx_hprt.u32 = cvmx_usb_read_csr32(usb, 98262306a36Sopenharmony_ci CVMX_USBCX_HPRT(usb->index)); 98362306a36Sopenharmony_ci} 98462306a36Sopenharmony_ci 98562306a36Sopenharmony_ci/** 98662306a36Sopenharmony_ci * Disable a USB port. After this call the USB port will not 98762306a36Sopenharmony_ci * generate data transfers and will not generate events. 98862306a36Sopenharmony_ci * Transactions in process will fail and call their 98962306a36Sopenharmony_ci * associated callbacks. 99062306a36Sopenharmony_ci * 99162306a36Sopenharmony_ci * @usb: USB device state populated by cvmx_usb_initialize(). 99262306a36Sopenharmony_ci * 99362306a36Sopenharmony_ci * Returns: 0 or a negative error code. 99462306a36Sopenharmony_ci */ 99562306a36Sopenharmony_cistatic int cvmx_usb_disable(struct octeon_hcd *usb) 99662306a36Sopenharmony_ci{ 99762306a36Sopenharmony_ci /* Disable the port */ 99862306a36Sopenharmony_ci USB_SET_FIELD32(CVMX_USBCX_HPRT(usb->index), cvmx_usbcx_hprt, 99962306a36Sopenharmony_ci prtena, 1); 100062306a36Sopenharmony_ci return 0; 100162306a36Sopenharmony_ci} 100262306a36Sopenharmony_ci 100362306a36Sopenharmony_ci/** 100462306a36Sopenharmony_ci * Get the current state of the USB port. Use this call to 100562306a36Sopenharmony_ci * determine if the usb port has anything connected, is enabled, 100662306a36Sopenharmony_ci * or has some sort of error condition. The return value of this 100762306a36Sopenharmony_ci * call has "changed" bits to signal of the value of some fields 100862306a36Sopenharmony_ci * have changed between calls. 100962306a36Sopenharmony_ci * 101062306a36Sopenharmony_ci * @usb: USB device state populated by cvmx_usb_initialize(). 101162306a36Sopenharmony_ci * 101262306a36Sopenharmony_ci * Returns: Port status information 101362306a36Sopenharmony_ci */ 101462306a36Sopenharmony_cistatic struct cvmx_usb_port_status cvmx_usb_get_status(struct octeon_hcd *usb) 101562306a36Sopenharmony_ci{ 101662306a36Sopenharmony_ci union cvmx_usbcx_hprt usbc_hprt; 101762306a36Sopenharmony_ci struct cvmx_usb_port_status result; 101862306a36Sopenharmony_ci 101962306a36Sopenharmony_ci memset(&result, 0, sizeof(result)); 102062306a36Sopenharmony_ci 102162306a36Sopenharmony_ci usbc_hprt.u32 = cvmx_usb_read_csr32(usb, CVMX_USBCX_HPRT(usb->index)); 102262306a36Sopenharmony_ci result.port_enabled = usbc_hprt.s.prtena; 102362306a36Sopenharmony_ci result.port_over_current = usbc_hprt.s.prtovrcurract; 102462306a36Sopenharmony_ci result.port_powered = usbc_hprt.s.prtpwr; 102562306a36Sopenharmony_ci result.port_speed = usbc_hprt.s.prtspd; 102662306a36Sopenharmony_ci result.connected = usbc_hprt.s.prtconnsts; 102762306a36Sopenharmony_ci result.connect_change = 102862306a36Sopenharmony_ci result.connected != usb->port_status.connected; 102962306a36Sopenharmony_ci 103062306a36Sopenharmony_ci return result; 103162306a36Sopenharmony_ci} 103262306a36Sopenharmony_ci 103362306a36Sopenharmony_ci/** 103462306a36Sopenharmony_ci * Open a virtual pipe between the host and a USB device. A pipe 103562306a36Sopenharmony_ci * must be opened before data can be transferred between a device 103662306a36Sopenharmony_ci * and Octeon. 103762306a36Sopenharmony_ci * 103862306a36Sopenharmony_ci * @usb: USB device state populated by cvmx_usb_initialize(). 103962306a36Sopenharmony_ci * @device_addr: 104062306a36Sopenharmony_ci * USB device address to open the pipe to 104162306a36Sopenharmony_ci * (0-127). 104262306a36Sopenharmony_ci * @endpoint_num: 104362306a36Sopenharmony_ci * USB endpoint number to open the pipe to 104462306a36Sopenharmony_ci * (0-15). 104562306a36Sopenharmony_ci * @device_speed: 104662306a36Sopenharmony_ci * The speed of the device the pipe is going 104762306a36Sopenharmony_ci * to. This must match the device's speed, 104862306a36Sopenharmony_ci * which may be different than the port speed. 104962306a36Sopenharmony_ci * @max_packet: The maximum packet length the device can 105062306a36Sopenharmony_ci * transmit/receive (low speed=0-8, full 105162306a36Sopenharmony_ci * speed=0-1023, high speed=0-1024). This value 105262306a36Sopenharmony_ci * comes from the standard endpoint descriptor 105362306a36Sopenharmony_ci * field wMaxPacketSize bits <10:0>. 105462306a36Sopenharmony_ci * @transfer_type: 105562306a36Sopenharmony_ci * The type of transfer this pipe is for. 105662306a36Sopenharmony_ci * @transfer_dir: 105762306a36Sopenharmony_ci * The direction the pipe is in. This is not 105862306a36Sopenharmony_ci * used for control pipes. 105962306a36Sopenharmony_ci * @interval: For ISOCHRONOUS and INTERRUPT transfers, 106062306a36Sopenharmony_ci * this is how often the transfer is scheduled 106162306a36Sopenharmony_ci * for. All other transfers should specify 106262306a36Sopenharmony_ci * zero. The units are in frames (8000/sec at 106362306a36Sopenharmony_ci * high speed, 1000/sec for full speed). 106462306a36Sopenharmony_ci * @multi_count: 106562306a36Sopenharmony_ci * For high speed devices, this is the maximum 106662306a36Sopenharmony_ci * allowed number of packet per microframe. 106762306a36Sopenharmony_ci * Specify zero for non high speed devices. This 106862306a36Sopenharmony_ci * value comes from the standard endpoint descriptor 106962306a36Sopenharmony_ci * field wMaxPacketSize bits <12:11>. 107062306a36Sopenharmony_ci * @hub_device_addr: 107162306a36Sopenharmony_ci * Hub device address this device is connected 107262306a36Sopenharmony_ci * to. Devices connected directly to Octeon 107362306a36Sopenharmony_ci * use zero. This is only used when the device 107462306a36Sopenharmony_ci * is full/low speed behind a high speed hub. 107562306a36Sopenharmony_ci * The address will be of the high speed hub, 107662306a36Sopenharmony_ci * not and full speed hubs after it. 107762306a36Sopenharmony_ci * @hub_port: Which port on the hub the device is 107862306a36Sopenharmony_ci * connected. Use zero for devices connected 107962306a36Sopenharmony_ci * directly to Octeon. Like hub_device_addr, 108062306a36Sopenharmony_ci * this is only used for full/low speed 108162306a36Sopenharmony_ci * devices behind a high speed hub. 108262306a36Sopenharmony_ci * 108362306a36Sopenharmony_ci * Returns: A non-NULL value is a pipe. NULL means an error. 108462306a36Sopenharmony_ci */ 108562306a36Sopenharmony_cistatic struct cvmx_usb_pipe *cvmx_usb_open_pipe(struct octeon_hcd *usb, 108662306a36Sopenharmony_ci int device_addr, 108762306a36Sopenharmony_ci int endpoint_num, 108862306a36Sopenharmony_ci enum cvmx_usb_speed 108962306a36Sopenharmony_ci device_speed, 109062306a36Sopenharmony_ci int max_packet, 109162306a36Sopenharmony_ci enum cvmx_usb_transfer 109262306a36Sopenharmony_ci transfer_type, 109362306a36Sopenharmony_ci enum cvmx_usb_direction 109462306a36Sopenharmony_ci transfer_dir, 109562306a36Sopenharmony_ci int interval, int multi_count, 109662306a36Sopenharmony_ci int hub_device_addr, 109762306a36Sopenharmony_ci int hub_port) 109862306a36Sopenharmony_ci{ 109962306a36Sopenharmony_ci struct cvmx_usb_pipe *pipe; 110062306a36Sopenharmony_ci 110162306a36Sopenharmony_ci pipe = kzalloc(sizeof(*pipe), GFP_ATOMIC); 110262306a36Sopenharmony_ci if (!pipe) 110362306a36Sopenharmony_ci return NULL; 110462306a36Sopenharmony_ci if ((device_speed == CVMX_USB_SPEED_HIGH) && 110562306a36Sopenharmony_ci (transfer_dir == CVMX_USB_DIRECTION_OUT) && 110662306a36Sopenharmony_ci (transfer_type == CVMX_USB_TRANSFER_BULK)) 110762306a36Sopenharmony_ci pipe->flags |= CVMX_USB_PIPE_FLAGS_NEED_PING; 110862306a36Sopenharmony_ci pipe->device_addr = device_addr; 110962306a36Sopenharmony_ci pipe->endpoint_num = endpoint_num; 111062306a36Sopenharmony_ci pipe->device_speed = device_speed; 111162306a36Sopenharmony_ci pipe->max_packet = max_packet; 111262306a36Sopenharmony_ci pipe->transfer_type = transfer_type; 111362306a36Sopenharmony_ci pipe->transfer_dir = transfer_dir; 111462306a36Sopenharmony_ci INIT_LIST_HEAD(&pipe->transactions); 111562306a36Sopenharmony_ci 111662306a36Sopenharmony_ci /* 111762306a36Sopenharmony_ci * All pipes use interval to rate limit NAK processing. Force an 111862306a36Sopenharmony_ci * interval if one wasn't supplied 111962306a36Sopenharmony_ci */ 112062306a36Sopenharmony_ci if (!interval) 112162306a36Sopenharmony_ci interval = 1; 112262306a36Sopenharmony_ci if (cvmx_usb_pipe_needs_split(usb, pipe)) { 112362306a36Sopenharmony_ci pipe->interval = interval * 8; 112462306a36Sopenharmony_ci /* Force start splits to be schedule on uFrame 0 */ 112562306a36Sopenharmony_ci pipe->next_tx_frame = ((usb->frame_number + 7) & ~7) + 112662306a36Sopenharmony_ci pipe->interval; 112762306a36Sopenharmony_ci } else { 112862306a36Sopenharmony_ci pipe->interval = interval; 112962306a36Sopenharmony_ci pipe->next_tx_frame = usb->frame_number + pipe->interval; 113062306a36Sopenharmony_ci } 113162306a36Sopenharmony_ci pipe->multi_count = multi_count; 113262306a36Sopenharmony_ci pipe->hub_device_addr = hub_device_addr; 113362306a36Sopenharmony_ci pipe->hub_port = hub_port; 113462306a36Sopenharmony_ci pipe->pid_toggle = 0; 113562306a36Sopenharmony_ci pipe->split_sc_frame = -1; 113662306a36Sopenharmony_ci list_add_tail(&pipe->node, &usb->idle_pipes); 113762306a36Sopenharmony_ci 113862306a36Sopenharmony_ci /* 113962306a36Sopenharmony_ci * We don't need to tell the hardware about this pipe yet since 114062306a36Sopenharmony_ci * it doesn't have any submitted requests 114162306a36Sopenharmony_ci */ 114262306a36Sopenharmony_ci 114362306a36Sopenharmony_ci return pipe; 114462306a36Sopenharmony_ci} 114562306a36Sopenharmony_ci 114662306a36Sopenharmony_ci/** 114762306a36Sopenharmony_ci * Poll the RX FIFOs and remove data as needed. This function is only used 114862306a36Sopenharmony_ci * in non DMA mode. It is very important that this function be called quickly 114962306a36Sopenharmony_ci * enough to prevent FIFO overflow. 115062306a36Sopenharmony_ci * 115162306a36Sopenharmony_ci * @usb: USB device state populated by cvmx_usb_initialize(). 115262306a36Sopenharmony_ci */ 115362306a36Sopenharmony_cistatic void cvmx_usb_poll_rx_fifo(struct octeon_hcd *usb) 115462306a36Sopenharmony_ci{ 115562306a36Sopenharmony_ci union cvmx_usbcx_grxstsph rx_status; 115662306a36Sopenharmony_ci int channel; 115762306a36Sopenharmony_ci int bytes; 115862306a36Sopenharmony_ci u64 address; 115962306a36Sopenharmony_ci u32 *ptr; 116062306a36Sopenharmony_ci 116162306a36Sopenharmony_ci rx_status.u32 = cvmx_usb_read_csr32(usb, 116262306a36Sopenharmony_ci CVMX_USBCX_GRXSTSPH(usb->index)); 116362306a36Sopenharmony_ci /* Only read data if IN data is there */ 116462306a36Sopenharmony_ci if (rx_status.s.pktsts != 2) 116562306a36Sopenharmony_ci return; 116662306a36Sopenharmony_ci /* Check if no data is available */ 116762306a36Sopenharmony_ci if (!rx_status.s.bcnt) 116862306a36Sopenharmony_ci return; 116962306a36Sopenharmony_ci 117062306a36Sopenharmony_ci channel = rx_status.s.chnum; 117162306a36Sopenharmony_ci bytes = rx_status.s.bcnt; 117262306a36Sopenharmony_ci if (!bytes) 117362306a36Sopenharmony_ci return; 117462306a36Sopenharmony_ci 117562306a36Sopenharmony_ci /* Get where the DMA engine would have written this data */ 117662306a36Sopenharmony_ci address = cvmx_read64_uint64(CVMX_USBNX_DMA0_INB_CHN0(usb->index) + 117762306a36Sopenharmony_ci channel * 8); 117862306a36Sopenharmony_ci 117962306a36Sopenharmony_ci ptr = cvmx_phys_to_ptr(address); 118062306a36Sopenharmony_ci cvmx_write64_uint64(CVMX_USBNX_DMA0_INB_CHN0(usb->index) + channel * 8, 118162306a36Sopenharmony_ci address + bytes); 118262306a36Sopenharmony_ci 118362306a36Sopenharmony_ci /* Loop writing the FIFO data for this packet into memory */ 118462306a36Sopenharmony_ci while (bytes > 0) { 118562306a36Sopenharmony_ci *ptr++ = cvmx_usb_read_csr32(usb, 118662306a36Sopenharmony_ci USB_FIFO_ADDRESS(channel, usb->index)); 118762306a36Sopenharmony_ci bytes -= 4; 118862306a36Sopenharmony_ci } 118962306a36Sopenharmony_ci CVMX_SYNCW; 119062306a36Sopenharmony_ci} 119162306a36Sopenharmony_ci 119262306a36Sopenharmony_ci/** 119362306a36Sopenharmony_ci * Fill the TX hardware fifo with data out of the software 119462306a36Sopenharmony_ci * fifos 119562306a36Sopenharmony_ci * 119662306a36Sopenharmony_ci * @usb: USB device state populated by cvmx_usb_initialize(). 119762306a36Sopenharmony_ci * @fifo: Software fifo to use 119862306a36Sopenharmony_ci * @available: Amount of space in the hardware fifo 119962306a36Sopenharmony_ci * 120062306a36Sopenharmony_ci * Returns: Non zero if the hardware fifo was too small and needs 120162306a36Sopenharmony_ci * to be serviced again. 120262306a36Sopenharmony_ci */ 120362306a36Sopenharmony_cistatic int cvmx_usb_fill_tx_hw(struct octeon_hcd *usb, 120462306a36Sopenharmony_ci struct cvmx_usb_tx_fifo *fifo, int available) 120562306a36Sopenharmony_ci{ 120662306a36Sopenharmony_ci /* 120762306a36Sopenharmony_ci * We're done either when there isn't anymore space or the software FIFO 120862306a36Sopenharmony_ci * is empty 120962306a36Sopenharmony_ci */ 121062306a36Sopenharmony_ci while (available && (fifo->head != fifo->tail)) { 121162306a36Sopenharmony_ci int i = fifo->tail; 121262306a36Sopenharmony_ci const u32 *ptr = cvmx_phys_to_ptr(fifo->entry[i].address); 121362306a36Sopenharmony_ci u64 csr_address = USB_FIFO_ADDRESS(fifo->entry[i].channel, 121462306a36Sopenharmony_ci usb->index) ^ 4; 121562306a36Sopenharmony_ci int words = available; 121662306a36Sopenharmony_ci 121762306a36Sopenharmony_ci /* Limit the amount of data to what the SW fifo has */ 121862306a36Sopenharmony_ci if (fifo->entry[i].size <= available) { 121962306a36Sopenharmony_ci words = fifo->entry[i].size; 122062306a36Sopenharmony_ci fifo->tail++; 122162306a36Sopenharmony_ci if (fifo->tail > MAX_CHANNELS) 122262306a36Sopenharmony_ci fifo->tail = 0; 122362306a36Sopenharmony_ci } 122462306a36Sopenharmony_ci 122562306a36Sopenharmony_ci /* Update the next locations and counts */ 122662306a36Sopenharmony_ci available -= words; 122762306a36Sopenharmony_ci fifo->entry[i].address += words * 4; 122862306a36Sopenharmony_ci fifo->entry[i].size -= words; 122962306a36Sopenharmony_ci 123062306a36Sopenharmony_ci /* 123162306a36Sopenharmony_ci * Write the HW fifo data. The read every three writes is due 123262306a36Sopenharmony_ci * to an errata on CN3XXX chips 123362306a36Sopenharmony_ci */ 123462306a36Sopenharmony_ci while (words > 3) { 123562306a36Sopenharmony_ci cvmx_write64_uint32(csr_address, *ptr++); 123662306a36Sopenharmony_ci cvmx_write64_uint32(csr_address, *ptr++); 123762306a36Sopenharmony_ci cvmx_write64_uint32(csr_address, *ptr++); 123862306a36Sopenharmony_ci cvmx_read64_uint64(CVMX_USBNX_DMA0_INB_CHN0(usb->index)); 123962306a36Sopenharmony_ci words -= 3; 124062306a36Sopenharmony_ci } 124162306a36Sopenharmony_ci cvmx_write64_uint32(csr_address, *ptr++); 124262306a36Sopenharmony_ci if (--words) { 124362306a36Sopenharmony_ci cvmx_write64_uint32(csr_address, *ptr++); 124462306a36Sopenharmony_ci if (--words) 124562306a36Sopenharmony_ci cvmx_write64_uint32(csr_address, *ptr++); 124662306a36Sopenharmony_ci } 124762306a36Sopenharmony_ci cvmx_read64_uint64(CVMX_USBNX_DMA0_INB_CHN0(usb->index)); 124862306a36Sopenharmony_ci } 124962306a36Sopenharmony_ci return fifo->head != fifo->tail; 125062306a36Sopenharmony_ci} 125162306a36Sopenharmony_ci 125262306a36Sopenharmony_ci/** 125362306a36Sopenharmony_ci * Check the hardware FIFOs and fill them as needed 125462306a36Sopenharmony_ci * 125562306a36Sopenharmony_ci * @usb: USB device state populated by cvmx_usb_initialize(). 125662306a36Sopenharmony_ci */ 125762306a36Sopenharmony_cistatic void cvmx_usb_poll_tx_fifo(struct octeon_hcd *usb) 125862306a36Sopenharmony_ci{ 125962306a36Sopenharmony_ci if (usb->periodic.head != usb->periodic.tail) { 126062306a36Sopenharmony_ci union cvmx_usbcx_hptxsts tx_status; 126162306a36Sopenharmony_ci 126262306a36Sopenharmony_ci tx_status.u32 = cvmx_usb_read_csr32(usb, 126362306a36Sopenharmony_ci CVMX_USBCX_HPTXSTS(usb->index)); 126462306a36Sopenharmony_ci if (cvmx_usb_fill_tx_hw(usb, &usb->periodic, 126562306a36Sopenharmony_ci tx_status.s.ptxfspcavail)) 126662306a36Sopenharmony_ci USB_SET_FIELD32(CVMX_USBCX_GINTMSK(usb->index), 126762306a36Sopenharmony_ci cvmx_usbcx_gintmsk, ptxfempmsk, 1); 126862306a36Sopenharmony_ci else 126962306a36Sopenharmony_ci USB_SET_FIELD32(CVMX_USBCX_GINTMSK(usb->index), 127062306a36Sopenharmony_ci cvmx_usbcx_gintmsk, ptxfempmsk, 0); 127162306a36Sopenharmony_ci } 127262306a36Sopenharmony_ci 127362306a36Sopenharmony_ci if (usb->nonperiodic.head != usb->nonperiodic.tail) { 127462306a36Sopenharmony_ci union cvmx_usbcx_gnptxsts tx_status; 127562306a36Sopenharmony_ci 127662306a36Sopenharmony_ci tx_status.u32 = cvmx_usb_read_csr32(usb, 127762306a36Sopenharmony_ci CVMX_USBCX_GNPTXSTS(usb->index)); 127862306a36Sopenharmony_ci if (cvmx_usb_fill_tx_hw(usb, &usb->nonperiodic, 127962306a36Sopenharmony_ci tx_status.s.nptxfspcavail)) 128062306a36Sopenharmony_ci USB_SET_FIELD32(CVMX_USBCX_GINTMSK(usb->index), 128162306a36Sopenharmony_ci cvmx_usbcx_gintmsk, nptxfempmsk, 1); 128262306a36Sopenharmony_ci else 128362306a36Sopenharmony_ci USB_SET_FIELD32(CVMX_USBCX_GINTMSK(usb->index), 128462306a36Sopenharmony_ci cvmx_usbcx_gintmsk, nptxfempmsk, 0); 128562306a36Sopenharmony_ci } 128662306a36Sopenharmony_ci} 128762306a36Sopenharmony_ci 128862306a36Sopenharmony_ci/** 128962306a36Sopenharmony_ci * Fill the TX FIFO with an outgoing packet 129062306a36Sopenharmony_ci * 129162306a36Sopenharmony_ci * @usb: USB device state populated by cvmx_usb_initialize(). 129262306a36Sopenharmony_ci * @channel: Channel number to get packet from 129362306a36Sopenharmony_ci */ 129462306a36Sopenharmony_cistatic void cvmx_usb_fill_tx_fifo(struct octeon_hcd *usb, int channel) 129562306a36Sopenharmony_ci{ 129662306a36Sopenharmony_ci union cvmx_usbcx_hccharx hcchar; 129762306a36Sopenharmony_ci union cvmx_usbcx_hcspltx usbc_hcsplt; 129862306a36Sopenharmony_ci union cvmx_usbcx_hctsizx usbc_hctsiz; 129962306a36Sopenharmony_ci struct cvmx_usb_tx_fifo *fifo; 130062306a36Sopenharmony_ci 130162306a36Sopenharmony_ci /* We only need to fill data on outbound channels */ 130262306a36Sopenharmony_ci hcchar.u32 = cvmx_usb_read_csr32(usb, 130362306a36Sopenharmony_ci CVMX_USBCX_HCCHARX(channel, usb->index)); 130462306a36Sopenharmony_ci if (hcchar.s.epdir != CVMX_USB_DIRECTION_OUT) 130562306a36Sopenharmony_ci return; 130662306a36Sopenharmony_ci 130762306a36Sopenharmony_ci /* OUT Splits only have data on the start and not the complete */ 130862306a36Sopenharmony_ci usbc_hcsplt.u32 = cvmx_usb_read_csr32(usb, 130962306a36Sopenharmony_ci CVMX_USBCX_HCSPLTX(channel, usb->index)); 131062306a36Sopenharmony_ci if (usbc_hcsplt.s.spltena && usbc_hcsplt.s.compsplt) 131162306a36Sopenharmony_ci return; 131262306a36Sopenharmony_ci 131362306a36Sopenharmony_ci /* 131462306a36Sopenharmony_ci * Find out how many bytes we need to fill and convert it into 32bit 131562306a36Sopenharmony_ci * words. 131662306a36Sopenharmony_ci */ 131762306a36Sopenharmony_ci usbc_hctsiz.u32 = cvmx_usb_read_csr32(usb, 131862306a36Sopenharmony_ci CVMX_USBCX_HCTSIZX(channel, usb->index)); 131962306a36Sopenharmony_ci if (!usbc_hctsiz.s.xfersize) 132062306a36Sopenharmony_ci return; 132162306a36Sopenharmony_ci 132262306a36Sopenharmony_ci if ((hcchar.s.eptype == CVMX_USB_TRANSFER_INTERRUPT) || 132362306a36Sopenharmony_ci (hcchar.s.eptype == CVMX_USB_TRANSFER_ISOCHRONOUS)) 132462306a36Sopenharmony_ci fifo = &usb->periodic; 132562306a36Sopenharmony_ci else 132662306a36Sopenharmony_ci fifo = &usb->nonperiodic; 132762306a36Sopenharmony_ci 132862306a36Sopenharmony_ci fifo->entry[fifo->head].channel = channel; 132962306a36Sopenharmony_ci fifo->entry[fifo->head].address = 133062306a36Sopenharmony_ci cvmx_read64_uint64(CVMX_USBNX_DMA0_OUTB_CHN0(usb->index) + 133162306a36Sopenharmony_ci channel * 8); 133262306a36Sopenharmony_ci fifo->entry[fifo->head].size = (usbc_hctsiz.s.xfersize + 3) >> 2; 133362306a36Sopenharmony_ci fifo->head++; 133462306a36Sopenharmony_ci if (fifo->head > MAX_CHANNELS) 133562306a36Sopenharmony_ci fifo->head = 0; 133662306a36Sopenharmony_ci 133762306a36Sopenharmony_ci cvmx_usb_poll_tx_fifo(usb); 133862306a36Sopenharmony_ci} 133962306a36Sopenharmony_ci 134062306a36Sopenharmony_ci/** 134162306a36Sopenharmony_ci * Perform channel specific setup for Control transactions. All 134262306a36Sopenharmony_ci * the generic stuff will already have been done in cvmx_usb_start_channel(). 134362306a36Sopenharmony_ci * 134462306a36Sopenharmony_ci * @usb: USB device state populated by cvmx_usb_initialize(). 134562306a36Sopenharmony_ci * @channel: Channel to setup 134662306a36Sopenharmony_ci * @pipe: Pipe for control transaction 134762306a36Sopenharmony_ci */ 134862306a36Sopenharmony_cistatic void cvmx_usb_start_channel_control(struct octeon_hcd *usb, 134962306a36Sopenharmony_ci int channel, 135062306a36Sopenharmony_ci struct cvmx_usb_pipe *pipe) 135162306a36Sopenharmony_ci{ 135262306a36Sopenharmony_ci struct usb_hcd *hcd = octeon_to_hcd(usb); 135362306a36Sopenharmony_ci struct device *dev = hcd->self.controller; 135462306a36Sopenharmony_ci struct cvmx_usb_transaction *transaction = 135562306a36Sopenharmony_ci list_first_entry(&pipe->transactions, typeof(*transaction), 135662306a36Sopenharmony_ci node); 135762306a36Sopenharmony_ci struct usb_ctrlrequest *header = 135862306a36Sopenharmony_ci cvmx_phys_to_ptr(transaction->control_header); 135962306a36Sopenharmony_ci int bytes_to_transfer = transaction->buffer_length - 136062306a36Sopenharmony_ci transaction->actual_bytes; 136162306a36Sopenharmony_ci int packets_to_transfer; 136262306a36Sopenharmony_ci union cvmx_usbcx_hctsizx usbc_hctsiz; 136362306a36Sopenharmony_ci 136462306a36Sopenharmony_ci usbc_hctsiz.u32 = cvmx_usb_read_csr32(usb, 136562306a36Sopenharmony_ci CVMX_USBCX_HCTSIZX(channel, usb->index)); 136662306a36Sopenharmony_ci 136762306a36Sopenharmony_ci switch (transaction->stage) { 136862306a36Sopenharmony_ci case CVMX_USB_STAGE_NON_CONTROL: 136962306a36Sopenharmony_ci case CVMX_USB_STAGE_NON_CONTROL_SPLIT_COMPLETE: 137062306a36Sopenharmony_ci dev_err(dev, "%s: ERROR - Non control stage\n", __func__); 137162306a36Sopenharmony_ci break; 137262306a36Sopenharmony_ci case CVMX_USB_STAGE_SETUP: 137362306a36Sopenharmony_ci usbc_hctsiz.s.pid = 3; /* Setup */ 137462306a36Sopenharmony_ci bytes_to_transfer = sizeof(*header); 137562306a36Sopenharmony_ci /* All Control operations start with a setup going OUT */ 137662306a36Sopenharmony_ci USB_SET_FIELD32(CVMX_USBCX_HCCHARX(channel, usb->index), 137762306a36Sopenharmony_ci cvmx_usbcx_hccharx, epdir, 137862306a36Sopenharmony_ci CVMX_USB_DIRECTION_OUT); 137962306a36Sopenharmony_ci /* 138062306a36Sopenharmony_ci * Setup send the control header instead of the buffer data. The 138162306a36Sopenharmony_ci * buffer data will be used in the next stage 138262306a36Sopenharmony_ci */ 138362306a36Sopenharmony_ci cvmx_write64_uint64(CVMX_USBNX_DMA0_OUTB_CHN0(usb->index) + 138462306a36Sopenharmony_ci channel * 8, 138562306a36Sopenharmony_ci transaction->control_header); 138662306a36Sopenharmony_ci break; 138762306a36Sopenharmony_ci case CVMX_USB_STAGE_SETUP_SPLIT_COMPLETE: 138862306a36Sopenharmony_ci usbc_hctsiz.s.pid = 3; /* Setup */ 138962306a36Sopenharmony_ci bytes_to_transfer = 0; 139062306a36Sopenharmony_ci /* All Control operations start with a setup going OUT */ 139162306a36Sopenharmony_ci USB_SET_FIELD32(CVMX_USBCX_HCCHARX(channel, usb->index), 139262306a36Sopenharmony_ci cvmx_usbcx_hccharx, epdir, 139362306a36Sopenharmony_ci CVMX_USB_DIRECTION_OUT); 139462306a36Sopenharmony_ci 139562306a36Sopenharmony_ci USB_SET_FIELD32(CVMX_USBCX_HCSPLTX(channel, usb->index), 139662306a36Sopenharmony_ci cvmx_usbcx_hcspltx, compsplt, 1); 139762306a36Sopenharmony_ci break; 139862306a36Sopenharmony_ci case CVMX_USB_STAGE_DATA: 139962306a36Sopenharmony_ci usbc_hctsiz.s.pid = cvmx_usb_get_data_pid(pipe); 140062306a36Sopenharmony_ci if (cvmx_usb_pipe_needs_split(usb, pipe)) { 140162306a36Sopenharmony_ci if (header->bRequestType & USB_DIR_IN) 140262306a36Sopenharmony_ci bytes_to_transfer = 0; 140362306a36Sopenharmony_ci else if (bytes_to_transfer > pipe->max_packet) 140462306a36Sopenharmony_ci bytes_to_transfer = pipe->max_packet; 140562306a36Sopenharmony_ci } 140662306a36Sopenharmony_ci USB_SET_FIELD32(CVMX_USBCX_HCCHARX(channel, usb->index), 140762306a36Sopenharmony_ci cvmx_usbcx_hccharx, epdir, 140862306a36Sopenharmony_ci ((header->bRequestType & USB_DIR_IN) ? 140962306a36Sopenharmony_ci CVMX_USB_DIRECTION_IN : 141062306a36Sopenharmony_ci CVMX_USB_DIRECTION_OUT)); 141162306a36Sopenharmony_ci break; 141262306a36Sopenharmony_ci case CVMX_USB_STAGE_DATA_SPLIT_COMPLETE: 141362306a36Sopenharmony_ci usbc_hctsiz.s.pid = cvmx_usb_get_data_pid(pipe); 141462306a36Sopenharmony_ci if (!(header->bRequestType & USB_DIR_IN)) 141562306a36Sopenharmony_ci bytes_to_transfer = 0; 141662306a36Sopenharmony_ci USB_SET_FIELD32(CVMX_USBCX_HCCHARX(channel, usb->index), 141762306a36Sopenharmony_ci cvmx_usbcx_hccharx, epdir, 141862306a36Sopenharmony_ci ((header->bRequestType & USB_DIR_IN) ? 141962306a36Sopenharmony_ci CVMX_USB_DIRECTION_IN : 142062306a36Sopenharmony_ci CVMX_USB_DIRECTION_OUT)); 142162306a36Sopenharmony_ci USB_SET_FIELD32(CVMX_USBCX_HCSPLTX(channel, usb->index), 142262306a36Sopenharmony_ci cvmx_usbcx_hcspltx, compsplt, 1); 142362306a36Sopenharmony_ci break; 142462306a36Sopenharmony_ci case CVMX_USB_STAGE_STATUS: 142562306a36Sopenharmony_ci usbc_hctsiz.s.pid = cvmx_usb_get_data_pid(pipe); 142662306a36Sopenharmony_ci bytes_to_transfer = 0; 142762306a36Sopenharmony_ci USB_SET_FIELD32(CVMX_USBCX_HCCHARX(channel, usb->index), 142862306a36Sopenharmony_ci cvmx_usbcx_hccharx, epdir, 142962306a36Sopenharmony_ci ((header->bRequestType & USB_DIR_IN) ? 143062306a36Sopenharmony_ci CVMX_USB_DIRECTION_OUT : 143162306a36Sopenharmony_ci CVMX_USB_DIRECTION_IN)); 143262306a36Sopenharmony_ci break; 143362306a36Sopenharmony_ci case CVMX_USB_STAGE_STATUS_SPLIT_COMPLETE: 143462306a36Sopenharmony_ci usbc_hctsiz.s.pid = cvmx_usb_get_data_pid(pipe); 143562306a36Sopenharmony_ci bytes_to_transfer = 0; 143662306a36Sopenharmony_ci USB_SET_FIELD32(CVMX_USBCX_HCCHARX(channel, usb->index), 143762306a36Sopenharmony_ci cvmx_usbcx_hccharx, epdir, 143862306a36Sopenharmony_ci ((header->bRequestType & USB_DIR_IN) ? 143962306a36Sopenharmony_ci CVMX_USB_DIRECTION_OUT : 144062306a36Sopenharmony_ci CVMX_USB_DIRECTION_IN)); 144162306a36Sopenharmony_ci USB_SET_FIELD32(CVMX_USBCX_HCSPLTX(channel, usb->index), 144262306a36Sopenharmony_ci cvmx_usbcx_hcspltx, compsplt, 1); 144362306a36Sopenharmony_ci break; 144462306a36Sopenharmony_ci } 144562306a36Sopenharmony_ci 144662306a36Sopenharmony_ci /* 144762306a36Sopenharmony_ci * Make sure the transfer never exceeds the byte limit of the hardware. 144862306a36Sopenharmony_ci * Further bytes will be sent as continued transactions 144962306a36Sopenharmony_ci */ 145062306a36Sopenharmony_ci if (bytes_to_transfer > MAX_TRANSFER_BYTES) { 145162306a36Sopenharmony_ci /* Round MAX_TRANSFER_BYTES to a multiple of out packet size */ 145262306a36Sopenharmony_ci bytes_to_transfer = MAX_TRANSFER_BYTES / pipe->max_packet; 145362306a36Sopenharmony_ci bytes_to_transfer *= pipe->max_packet; 145462306a36Sopenharmony_ci } 145562306a36Sopenharmony_ci 145662306a36Sopenharmony_ci /* 145762306a36Sopenharmony_ci * Calculate the number of packets to transfer. If the length is zero 145862306a36Sopenharmony_ci * we still need to transfer one packet 145962306a36Sopenharmony_ci */ 146062306a36Sopenharmony_ci packets_to_transfer = DIV_ROUND_UP(bytes_to_transfer, 146162306a36Sopenharmony_ci pipe->max_packet); 146262306a36Sopenharmony_ci if (packets_to_transfer == 0) { 146362306a36Sopenharmony_ci packets_to_transfer = 1; 146462306a36Sopenharmony_ci } else if ((packets_to_transfer > 1) && 146562306a36Sopenharmony_ci (usb->init_flags & CVMX_USB_INITIALIZE_FLAGS_NO_DMA)) { 146662306a36Sopenharmony_ci /* 146762306a36Sopenharmony_ci * Limit to one packet when not using DMA. Channels must be 146862306a36Sopenharmony_ci * restarted between every packet for IN transactions, so there 146962306a36Sopenharmony_ci * is no reason to do multiple packets in a row 147062306a36Sopenharmony_ci */ 147162306a36Sopenharmony_ci packets_to_transfer = 1; 147262306a36Sopenharmony_ci bytes_to_transfer = packets_to_transfer * pipe->max_packet; 147362306a36Sopenharmony_ci } else if (packets_to_transfer > MAX_TRANSFER_PACKETS) { 147462306a36Sopenharmony_ci /* 147562306a36Sopenharmony_ci * Limit the number of packet and data transferred to what the 147662306a36Sopenharmony_ci * hardware can handle 147762306a36Sopenharmony_ci */ 147862306a36Sopenharmony_ci packets_to_transfer = MAX_TRANSFER_PACKETS; 147962306a36Sopenharmony_ci bytes_to_transfer = packets_to_transfer * pipe->max_packet; 148062306a36Sopenharmony_ci } 148162306a36Sopenharmony_ci 148262306a36Sopenharmony_ci usbc_hctsiz.s.xfersize = bytes_to_transfer; 148362306a36Sopenharmony_ci usbc_hctsiz.s.pktcnt = packets_to_transfer; 148462306a36Sopenharmony_ci 148562306a36Sopenharmony_ci cvmx_usb_write_csr32(usb, CVMX_USBCX_HCTSIZX(channel, usb->index), 148662306a36Sopenharmony_ci usbc_hctsiz.u32); 148762306a36Sopenharmony_ci} 148862306a36Sopenharmony_ci 148962306a36Sopenharmony_ci/** 149062306a36Sopenharmony_ci * Start a channel to perform the pipe's head transaction 149162306a36Sopenharmony_ci * 149262306a36Sopenharmony_ci * @usb: USB device state populated by cvmx_usb_initialize(). 149362306a36Sopenharmony_ci * @channel: Channel to setup 149462306a36Sopenharmony_ci * @pipe: Pipe to start 149562306a36Sopenharmony_ci */ 149662306a36Sopenharmony_cistatic void cvmx_usb_start_channel(struct octeon_hcd *usb, int channel, 149762306a36Sopenharmony_ci struct cvmx_usb_pipe *pipe) 149862306a36Sopenharmony_ci{ 149962306a36Sopenharmony_ci struct cvmx_usb_transaction *transaction = 150062306a36Sopenharmony_ci list_first_entry(&pipe->transactions, typeof(*transaction), 150162306a36Sopenharmony_ci node); 150262306a36Sopenharmony_ci 150362306a36Sopenharmony_ci /* Make sure all writes to the DMA region get flushed */ 150462306a36Sopenharmony_ci CVMX_SYNCW; 150562306a36Sopenharmony_ci 150662306a36Sopenharmony_ci /* Attach the channel to the pipe */ 150762306a36Sopenharmony_ci usb->pipe_for_channel[channel] = pipe; 150862306a36Sopenharmony_ci pipe->channel = channel; 150962306a36Sopenharmony_ci pipe->flags |= CVMX_USB_PIPE_FLAGS_SCHEDULED; 151062306a36Sopenharmony_ci 151162306a36Sopenharmony_ci /* Mark this channel as in use */ 151262306a36Sopenharmony_ci usb->idle_hardware_channels &= ~(1 << channel); 151362306a36Sopenharmony_ci 151462306a36Sopenharmony_ci /* Enable the channel interrupt bits */ 151562306a36Sopenharmony_ci { 151662306a36Sopenharmony_ci union cvmx_usbcx_hcintx usbc_hcint; 151762306a36Sopenharmony_ci union cvmx_usbcx_hcintmskx usbc_hcintmsk; 151862306a36Sopenharmony_ci union cvmx_usbcx_haintmsk usbc_haintmsk; 151962306a36Sopenharmony_ci 152062306a36Sopenharmony_ci /* Clear all channel status bits */ 152162306a36Sopenharmony_ci usbc_hcint.u32 = cvmx_usb_read_csr32(usb, 152262306a36Sopenharmony_ci CVMX_USBCX_HCINTX(channel, usb->index)); 152362306a36Sopenharmony_ci 152462306a36Sopenharmony_ci cvmx_usb_write_csr32(usb, 152562306a36Sopenharmony_ci CVMX_USBCX_HCINTX(channel, usb->index), 152662306a36Sopenharmony_ci usbc_hcint.u32); 152762306a36Sopenharmony_ci 152862306a36Sopenharmony_ci usbc_hcintmsk.u32 = 0; 152962306a36Sopenharmony_ci usbc_hcintmsk.s.chhltdmsk = 1; 153062306a36Sopenharmony_ci if (usb->init_flags & CVMX_USB_INITIALIZE_FLAGS_NO_DMA) { 153162306a36Sopenharmony_ci /* 153262306a36Sopenharmony_ci * Channels need these extra interrupts when we aren't 153362306a36Sopenharmony_ci * in DMA mode. 153462306a36Sopenharmony_ci */ 153562306a36Sopenharmony_ci usbc_hcintmsk.s.datatglerrmsk = 1; 153662306a36Sopenharmony_ci usbc_hcintmsk.s.frmovrunmsk = 1; 153762306a36Sopenharmony_ci usbc_hcintmsk.s.bblerrmsk = 1; 153862306a36Sopenharmony_ci usbc_hcintmsk.s.xacterrmsk = 1; 153962306a36Sopenharmony_ci if (cvmx_usb_pipe_needs_split(usb, pipe)) { 154062306a36Sopenharmony_ci /* 154162306a36Sopenharmony_ci * Splits don't generate xfercompl, so we need 154262306a36Sopenharmony_ci * ACK and NYET. 154362306a36Sopenharmony_ci */ 154462306a36Sopenharmony_ci usbc_hcintmsk.s.nyetmsk = 1; 154562306a36Sopenharmony_ci usbc_hcintmsk.s.ackmsk = 1; 154662306a36Sopenharmony_ci } 154762306a36Sopenharmony_ci usbc_hcintmsk.s.nakmsk = 1; 154862306a36Sopenharmony_ci usbc_hcintmsk.s.stallmsk = 1; 154962306a36Sopenharmony_ci usbc_hcintmsk.s.xfercomplmsk = 1; 155062306a36Sopenharmony_ci } 155162306a36Sopenharmony_ci cvmx_usb_write_csr32(usb, 155262306a36Sopenharmony_ci CVMX_USBCX_HCINTMSKX(channel, usb->index), 155362306a36Sopenharmony_ci usbc_hcintmsk.u32); 155462306a36Sopenharmony_ci 155562306a36Sopenharmony_ci /* Enable the channel interrupt to propagate */ 155662306a36Sopenharmony_ci usbc_haintmsk.u32 = cvmx_usb_read_csr32(usb, 155762306a36Sopenharmony_ci CVMX_USBCX_HAINTMSK(usb->index)); 155862306a36Sopenharmony_ci usbc_haintmsk.s.haintmsk |= 1 << channel; 155962306a36Sopenharmony_ci cvmx_usb_write_csr32(usb, CVMX_USBCX_HAINTMSK(usb->index), 156062306a36Sopenharmony_ci usbc_haintmsk.u32); 156162306a36Sopenharmony_ci } 156262306a36Sopenharmony_ci 156362306a36Sopenharmony_ci /* Setup the location the DMA engine uses. */ 156462306a36Sopenharmony_ci { 156562306a36Sopenharmony_ci u64 reg; 156662306a36Sopenharmony_ci u64 dma_address = transaction->buffer + 156762306a36Sopenharmony_ci transaction->actual_bytes; 156862306a36Sopenharmony_ci 156962306a36Sopenharmony_ci if (transaction->type == CVMX_USB_TRANSFER_ISOCHRONOUS) 157062306a36Sopenharmony_ci dma_address = transaction->buffer + 157162306a36Sopenharmony_ci transaction->iso_packets[0].offset + 157262306a36Sopenharmony_ci transaction->actual_bytes; 157362306a36Sopenharmony_ci 157462306a36Sopenharmony_ci if (pipe->transfer_dir == CVMX_USB_DIRECTION_OUT) 157562306a36Sopenharmony_ci reg = CVMX_USBNX_DMA0_OUTB_CHN0(usb->index); 157662306a36Sopenharmony_ci else 157762306a36Sopenharmony_ci reg = CVMX_USBNX_DMA0_INB_CHN0(usb->index); 157862306a36Sopenharmony_ci cvmx_write64_uint64(reg + channel * 8, dma_address); 157962306a36Sopenharmony_ci } 158062306a36Sopenharmony_ci 158162306a36Sopenharmony_ci /* Setup both the size of the transfer and the SPLIT characteristics */ 158262306a36Sopenharmony_ci { 158362306a36Sopenharmony_ci union cvmx_usbcx_hcspltx usbc_hcsplt = {.u32 = 0}; 158462306a36Sopenharmony_ci union cvmx_usbcx_hctsizx usbc_hctsiz = {.u32 = 0}; 158562306a36Sopenharmony_ci int packets_to_transfer; 158662306a36Sopenharmony_ci int bytes_to_transfer = transaction->buffer_length - 158762306a36Sopenharmony_ci transaction->actual_bytes; 158862306a36Sopenharmony_ci 158962306a36Sopenharmony_ci /* 159062306a36Sopenharmony_ci * ISOCHRONOUS transactions store each individual transfer size 159162306a36Sopenharmony_ci * in the packet structure, not the global buffer_length 159262306a36Sopenharmony_ci */ 159362306a36Sopenharmony_ci if (transaction->type == CVMX_USB_TRANSFER_ISOCHRONOUS) 159462306a36Sopenharmony_ci bytes_to_transfer = 159562306a36Sopenharmony_ci transaction->iso_packets[0].length - 159662306a36Sopenharmony_ci transaction->actual_bytes; 159762306a36Sopenharmony_ci 159862306a36Sopenharmony_ci /* 159962306a36Sopenharmony_ci * We need to do split transactions when we are talking to non 160062306a36Sopenharmony_ci * high speed devices that are behind a high speed hub 160162306a36Sopenharmony_ci */ 160262306a36Sopenharmony_ci if (cvmx_usb_pipe_needs_split(usb, pipe)) { 160362306a36Sopenharmony_ci /* 160462306a36Sopenharmony_ci * On the start split phase (stage is even) record the 160562306a36Sopenharmony_ci * frame number we will need to send the split complete. 160662306a36Sopenharmony_ci * We only store the lower two bits since the time ahead 160762306a36Sopenharmony_ci * can only be two frames 160862306a36Sopenharmony_ci */ 160962306a36Sopenharmony_ci if ((transaction->stage & 1) == 0) { 161062306a36Sopenharmony_ci if (transaction->type == CVMX_USB_TRANSFER_BULK) 161162306a36Sopenharmony_ci pipe->split_sc_frame = 161262306a36Sopenharmony_ci (usb->frame_number + 1) & 0x7f; 161362306a36Sopenharmony_ci else 161462306a36Sopenharmony_ci pipe->split_sc_frame = 161562306a36Sopenharmony_ci (usb->frame_number + 2) & 0x7f; 161662306a36Sopenharmony_ci } else { 161762306a36Sopenharmony_ci pipe->split_sc_frame = -1; 161862306a36Sopenharmony_ci } 161962306a36Sopenharmony_ci 162062306a36Sopenharmony_ci usbc_hcsplt.s.spltena = 1; 162162306a36Sopenharmony_ci usbc_hcsplt.s.hubaddr = pipe->hub_device_addr; 162262306a36Sopenharmony_ci usbc_hcsplt.s.prtaddr = pipe->hub_port; 162362306a36Sopenharmony_ci usbc_hcsplt.s.compsplt = (transaction->stage == 162462306a36Sopenharmony_ci CVMX_USB_STAGE_NON_CONTROL_SPLIT_COMPLETE); 162562306a36Sopenharmony_ci 162662306a36Sopenharmony_ci /* 162762306a36Sopenharmony_ci * SPLIT transactions can only ever transmit one data 162862306a36Sopenharmony_ci * packet so limit the transfer size to the max packet 162962306a36Sopenharmony_ci * size 163062306a36Sopenharmony_ci */ 163162306a36Sopenharmony_ci if (bytes_to_transfer > pipe->max_packet) 163262306a36Sopenharmony_ci bytes_to_transfer = pipe->max_packet; 163362306a36Sopenharmony_ci 163462306a36Sopenharmony_ci /* 163562306a36Sopenharmony_ci * ISOCHRONOUS OUT splits are unique in that they limit 163662306a36Sopenharmony_ci * data transfers to 188 byte chunks representing the 163762306a36Sopenharmony_ci * begin/middle/end of the data or all 163862306a36Sopenharmony_ci */ 163962306a36Sopenharmony_ci if (!usbc_hcsplt.s.compsplt && 164062306a36Sopenharmony_ci (pipe->transfer_dir == CVMX_USB_DIRECTION_OUT) && 164162306a36Sopenharmony_ci (pipe->transfer_type == 164262306a36Sopenharmony_ci CVMX_USB_TRANSFER_ISOCHRONOUS)) { 164362306a36Sopenharmony_ci /* 164462306a36Sopenharmony_ci * Clear the split complete frame number as 164562306a36Sopenharmony_ci * there isn't going to be a split complete 164662306a36Sopenharmony_ci */ 164762306a36Sopenharmony_ci pipe->split_sc_frame = -1; 164862306a36Sopenharmony_ci /* 164962306a36Sopenharmony_ci * See if we've started this transfer and sent 165062306a36Sopenharmony_ci * data 165162306a36Sopenharmony_ci */ 165262306a36Sopenharmony_ci if (transaction->actual_bytes == 0) { 165362306a36Sopenharmony_ci /* 165462306a36Sopenharmony_ci * Nothing sent yet, this is either a 165562306a36Sopenharmony_ci * begin or the entire payload 165662306a36Sopenharmony_ci */ 165762306a36Sopenharmony_ci if (bytes_to_transfer <= 188) 165862306a36Sopenharmony_ci /* Entire payload in one go */ 165962306a36Sopenharmony_ci usbc_hcsplt.s.xactpos = 3; 166062306a36Sopenharmony_ci else 166162306a36Sopenharmony_ci /* First part of payload */ 166262306a36Sopenharmony_ci usbc_hcsplt.s.xactpos = 2; 166362306a36Sopenharmony_ci } else { 166462306a36Sopenharmony_ci /* 166562306a36Sopenharmony_ci * Continuing the previous data, we must 166662306a36Sopenharmony_ci * either be in the middle or at the end 166762306a36Sopenharmony_ci */ 166862306a36Sopenharmony_ci if (bytes_to_transfer <= 188) 166962306a36Sopenharmony_ci /* End of payload */ 167062306a36Sopenharmony_ci usbc_hcsplt.s.xactpos = 1; 167162306a36Sopenharmony_ci else 167262306a36Sopenharmony_ci /* Middle of payload */ 167362306a36Sopenharmony_ci usbc_hcsplt.s.xactpos = 0; 167462306a36Sopenharmony_ci } 167562306a36Sopenharmony_ci /* 167662306a36Sopenharmony_ci * Again, the transfer size is limited to 188 167762306a36Sopenharmony_ci * bytes 167862306a36Sopenharmony_ci */ 167962306a36Sopenharmony_ci if (bytes_to_transfer > 188) 168062306a36Sopenharmony_ci bytes_to_transfer = 188; 168162306a36Sopenharmony_ci } 168262306a36Sopenharmony_ci } 168362306a36Sopenharmony_ci 168462306a36Sopenharmony_ci /* 168562306a36Sopenharmony_ci * Make sure the transfer never exceeds the byte limit of the 168662306a36Sopenharmony_ci * hardware. Further bytes will be sent as continued 168762306a36Sopenharmony_ci * transactions 168862306a36Sopenharmony_ci */ 168962306a36Sopenharmony_ci if (bytes_to_transfer > MAX_TRANSFER_BYTES) { 169062306a36Sopenharmony_ci /* 169162306a36Sopenharmony_ci * Round MAX_TRANSFER_BYTES to a multiple of out packet 169262306a36Sopenharmony_ci * size 169362306a36Sopenharmony_ci */ 169462306a36Sopenharmony_ci bytes_to_transfer = MAX_TRANSFER_BYTES / 169562306a36Sopenharmony_ci pipe->max_packet; 169662306a36Sopenharmony_ci bytes_to_transfer *= pipe->max_packet; 169762306a36Sopenharmony_ci } 169862306a36Sopenharmony_ci 169962306a36Sopenharmony_ci /* 170062306a36Sopenharmony_ci * Calculate the number of packets to transfer. If the length is 170162306a36Sopenharmony_ci * zero we still need to transfer one packet 170262306a36Sopenharmony_ci */ 170362306a36Sopenharmony_ci packets_to_transfer = 170462306a36Sopenharmony_ci DIV_ROUND_UP(bytes_to_transfer, pipe->max_packet); 170562306a36Sopenharmony_ci if (packets_to_transfer == 0) { 170662306a36Sopenharmony_ci packets_to_transfer = 1; 170762306a36Sopenharmony_ci } else if ((packets_to_transfer > 1) && 170862306a36Sopenharmony_ci (usb->init_flags & 170962306a36Sopenharmony_ci CVMX_USB_INITIALIZE_FLAGS_NO_DMA)) { 171062306a36Sopenharmony_ci /* 171162306a36Sopenharmony_ci * Limit to one packet when not using DMA. Channels must 171262306a36Sopenharmony_ci * be restarted between every packet for IN 171362306a36Sopenharmony_ci * transactions, so there is no reason to do multiple 171462306a36Sopenharmony_ci * packets in a row 171562306a36Sopenharmony_ci */ 171662306a36Sopenharmony_ci packets_to_transfer = 1; 171762306a36Sopenharmony_ci bytes_to_transfer = packets_to_transfer * 171862306a36Sopenharmony_ci pipe->max_packet; 171962306a36Sopenharmony_ci } else if (packets_to_transfer > MAX_TRANSFER_PACKETS) { 172062306a36Sopenharmony_ci /* 172162306a36Sopenharmony_ci * Limit the number of packet and data transferred to 172262306a36Sopenharmony_ci * what the hardware can handle 172362306a36Sopenharmony_ci */ 172462306a36Sopenharmony_ci packets_to_transfer = MAX_TRANSFER_PACKETS; 172562306a36Sopenharmony_ci bytes_to_transfer = packets_to_transfer * 172662306a36Sopenharmony_ci pipe->max_packet; 172762306a36Sopenharmony_ci } 172862306a36Sopenharmony_ci 172962306a36Sopenharmony_ci usbc_hctsiz.s.xfersize = bytes_to_transfer; 173062306a36Sopenharmony_ci usbc_hctsiz.s.pktcnt = packets_to_transfer; 173162306a36Sopenharmony_ci 173262306a36Sopenharmony_ci /* Update the DATA0/DATA1 toggle */ 173362306a36Sopenharmony_ci usbc_hctsiz.s.pid = cvmx_usb_get_data_pid(pipe); 173462306a36Sopenharmony_ci /* 173562306a36Sopenharmony_ci * High speed pipes may need a hardware ping before they start 173662306a36Sopenharmony_ci */ 173762306a36Sopenharmony_ci if (pipe->flags & CVMX_USB_PIPE_FLAGS_NEED_PING) 173862306a36Sopenharmony_ci usbc_hctsiz.s.dopng = 1; 173962306a36Sopenharmony_ci 174062306a36Sopenharmony_ci cvmx_usb_write_csr32(usb, 174162306a36Sopenharmony_ci CVMX_USBCX_HCSPLTX(channel, usb->index), 174262306a36Sopenharmony_ci usbc_hcsplt.u32); 174362306a36Sopenharmony_ci cvmx_usb_write_csr32(usb, 174462306a36Sopenharmony_ci CVMX_USBCX_HCTSIZX(channel, usb->index), 174562306a36Sopenharmony_ci usbc_hctsiz.u32); 174662306a36Sopenharmony_ci } 174762306a36Sopenharmony_ci 174862306a36Sopenharmony_ci /* Setup the Host Channel Characteristics Register */ 174962306a36Sopenharmony_ci { 175062306a36Sopenharmony_ci union cvmx_usbcx_hccharx usbc_hcchar = {.u32 = 0}; 175162306a36Sopenharmony_ci 175262306a36Sopenharmony_ci /* 175362306a36Sopenharmony_ci * Set the startframe odd/even properly. This is only used for 175462306a36Sopenharmony_ci * periodic 175562306a36Sopenharmony_ci */ 175662306a36Sopenharmony_ci usbc_hcchar.s.oddfrm = usb->frame_number & 1; 175762306a36Sopenharmony_ci 175862306a36Sopenharmony_ci /* 175962306a36Sopenharmony_ci * Set the number of back to back packets allowed by this 176062306a36Sopenharmony_ci * endpoint. Split transactions interpret "ec" as the number of 176162306a36Sopenharmony_ci * immediate retries of failure. These retries happen too 176262306a36Sopenharmony_ci * quickly, so we disable these entirely for splits 176362306a36Sopenharmony_ci */ 176462306a36Sopenharmony_ci if (cvmx_usb_pipe_needs_split(usb, pipe)) 176562306a36Sopenharmony_ci usbc_hcchar.s.ec = 1; 176662306a36Sopenharmony_ci else if (pipe->multi_count < 1) 176762306a36Sopenharmony_ci usbc_hcchar.s.ec = 1; 176862306a36Sopenharmony_ci else if (pipe->multi_count > 3) 176962306a36Sopenharmony_ci usbc_hcchar.s.ec = 3; 177062306a36Sopenharmony_ci else 177162306a36Sopenharmony_ci usbc_hcchar.s.ec = pipe->multi_count; 177262306a36Sopenharmony_ci 177362306a36Sopenharmony_ci /* Set the rest of the endpoint specific settings */ 177462306a36Sopenharmony_ci usbc_hcchar.s.devaddr = pipe->device_addr; 177562306a36Sopenharmony_ci usbc_hcchar.s.eptype = transaction->type; 177662306a36Sopenharmony_ci usbc_hcchar.s.lspddev = 177762306a36Sopenharmony_ci (pipe->device_speed == CVMX_USB_SPEED_LOW); 177862306a36Sopenharmony_ci usbc_hcchar.s.epdir = pipe->transfer_dir; 177962306a36Sopenharmony_ci usbc_hcchar.s.epnum = pipe->endpoint_num; 178062306a36Sopenharmony_ci usbc_hcchar.s.mps = pipe->max_packet; 178162306a36Sopenharmony_ci cvmx_usb_write_csr32(usb, 178262306a36Sopenharmony_ci CVMX_USBCX_HCCHARX(channel, usb->index), 178362306a36Sopenharmony_ci usbc_hcchar.u32); 178462306a36Sopenharmony_ci } 178562306a36Sopenharmony_ci 178662306a36Sopenharmony_ci /* Do transaction type specific fixups as needed */ 178762306a36Sopenharmony_ci switch (transaction->type) { 178862306a36Sopenharmony_ci case CVMX_USB_TRANSFER_CONTROL: 178962306a36Sopenharmony_ci cvmx_usb_start_channel_control(usb, channel, pipe); 179062306a36Sopenharmony_ci break; 179162306a36Sopenharmony_ci case CVMX_USB_TRANSFER_BULK: 179262306a36Sopenharmony_ci case CVMX_USB_TRANSFER_INTERRUPT: 179362306a36Sopenharmony_ci break; 179462306a36Sopenharmony_ci case CVMX_USB_TRANSFER_ISOCHRONOUS: 179562306a36Sopenharmony_ci if (!cvmx_usb_pipe_needs_split(usb, pipe)) { 179662306a36Sopenharmony_ci /* 179762306a36Sopenharmony_ci * ISO transactions require different PIDs depending on 179862306a36Sopenharmony_ci * direction and how many packets are needed 179962306a36Sopenharmony_ci */ 180062306a36Sopenharmony_ci if (pipe->transfer_dir == CVMX_USB_DIRECTION_OUT) { 180162306a36Sopenharmony_ci if (pipe->multi_count < 2) /* Need DATA0 */ 180262306a36Sopenharmony_ci USB_SET_FIELD32( 180362306a36Sopenharmony_ci CVMX_USBCX_HCTSIZX(channel, 180462306a36Sopenharmony_ci usb->index), 180562306a36Sopenharmony_ci cvmx_usbcx_hctsizx, pid, 0); 180662306a36Sopenharmony_ci else /* Need MDATA */ 180762306a36Sopenharmony_ci USB_SET_FIELD32( 180862306a36Sopenharmony_ci CVMX_USBCX_HCTSIZX(channel, 180962306a36Sopenharmony_ci usb->index), 181062306a36Sopenharmony_ci cvmx_usbcx_hctsizx, pid, 3); 181162306a36Sopenharmony_ci } 181262306a36Sopenharmony_ci } 181362306a36Sopenharmony_ci break; 181462306a36Sopenharmony_ci } 181562306a36Sopenharmony_ci { 181662306a36Sopenharmony_ci union cvmx_usbcx_hctsizx usbc_hctsiz = { .u32 = 181762306a36Sopenharmony_ci cvmx_usb_read_csr32(usb, 181862306a36Sopenharmony_ci CVMX_USBCX_HCTSIZX(channel, 181962306a36Sopenharmony_ci usb->index)) 182062306a36Sopenharmony_ci }; 182162306a36Sopenharmony_ci transaction->xfersize = usbc_hctsiz.s.xfersize; 182262306a36Sopenharmony_ci transaction->pktcnt = usbc_hctsiz.s.pktcnt; 182362306a36Sopenharmony_ci } 182462306a36Sopenharmony_ci /* Remember when we start a split transaction */ 182562306a36Sopenharmony_ci if (cvmx_usb_pipe_needs_split(usb, pipe)) 182662306a36Sopenharmony_ci usb->active_split = transaction; 182762306a36Sopenharmony_ci USB_SET_FIELD32(CVMX_USBCX_HCCHARX(channel, usb->index), 182862306a36Sopenharmony_ci cvmx_usbcx_hccharx, chena, 1); 182962306a36Sopenharmony_ci if (usb->init_flags & CVMX_USB_INITIALIZE_FLAGS_NO_DMA) 183062306a36Sopenharmony_ci cvmx_usb_fill_tx_fifo(usb, channel); 183162306a36Sopenharmony_ci} 183262306a36Sopenharmony_ci 183362306a36Sopenharmony_ci/** 183462306a36Sopenharmony_ci * Find a pipe that is ready to be scheduled to hardware. 183562306a36Sopenharmony_ci * @usb: USB device state populated by cvmx_usb_initialize(). 183662306a36Sopenharmony_ci * @xfer_type: Transfer type 183762306a36Sopenharmony_ci * 183862306a36Sopenharmony_ci * Returns: Pipe or NULL if none are ready 183962306a36Sopenharmony_ci */ 184062306a36Sopenharmony_cistatic struct cvmx_usb_pipe *cvmx_usb_find_ready_pipe(struct octeon_hcd *usb, 184162306a36Sopenharmony_ci enum cvmx_usb_transfer xfer_type) 184262306a36Sopenharmony_ci{ 184362306a36Sopenharmony_ci struct list_head *list = usb->active_pipes + xfer_type; 184462306a36Sopenharmony_ci u64 current_frame = usb->frame_number; 184562306a36Sopenharmony_ci struct cvmx_usb_pipe *pipe; 184662306a36Sopenharmony_ci 184762306a36Sopenharmony_ci list_for_each_entry(pipe, list, node) { 184862306a36Sopenharmony_ci struct cvmx_usb_transaction *t = 184962306a36Sopenharmony_ci list_first_entry(&pipe->transactions, typeof(*t), 185062306a36Sopenharmony_ci node); 185162306a36Sopenharmony_ci if (!(pipe->flags & CVMX_USB_PIPE_FLAGS_SCHEDULED) && t && 185262306a36Sopenharmony_ci (pipe->next_tx_frame <= current_frame) && 185362306a36Sopenharmony_ci ((pipe->split_sc_frame == -1) || 185462306a36Sopenharmony_ci ((((int)current_frame - pipe->split_sc_frame) & 0x7f) < 185562306a36Sopenharmony_ci 0x40)) && 185662306a36Sopenharmony_ci (!usb->active_split || (usb->active_split == t))) { 185762306a36Sopenharmony_ci prefetch(t); 185862306a36Sopenharmony_ci return pipe; 185962306a36Sopenharmony_ci } 186062306a36Sopenharmony_ci } 186162306a36Sopenharmony_ci return NULL; 186262306a36Sopenharmony_ci} 186362306a36Sopenharmony_ci 186462306a36Sopenharmony_cistatic struct cvmx_usb_pipe *cvmx_usb_next_pipe(struct octeon_hcd *usb, 186562306a36Sopenharmony_ci int is_sof) 186662306a36Sopenharmony_ci{ 186762306a36Sopenharmony_ci struct cvmx_usb_pipe *pipe; 186862306a36Sopenharmony_ci 186962306a36Sopenharmony_ci /* Find a pipe needing service. */ 187062306a36Sopenharmony_ci if (is_sof) { 187162306a36Sopenharmony_ci /* 187262306a36Sopenharmony_ci * Only process periodic pipes on SOF interrupts. This way we 187362306a36Sopenharmony_ci * are sure that the periodic data is sent in the beginning of 187462306a36Sopenharmony_ci * the frame. 187562306a36Sopenharmony_ci */ 187662306a36Sopenharmony_ci pipe = cvmx_usb_find_ready_pipe(usb, 187762306a36Sopenharmony_ci CVMX_USB_TRANSFER_ISOCHRONOUS); 187862306a36Sopenharmony_ci if (pipe) 187962306a36Sopenharmony_ci return pipe; 188062306a36Sopenharmony_ci pipe = cvmx_usb_find_ready_pipe(usb, 188162306a36Sopenharmony_ci CVMX_USB_TRANSFER_INTERRUPT); 188262306a36Sopenharmony_ci if (pipe) 188362306a36Sopenharmony_ci return pipe; 188462306a36Sopenharmony_ci } 188562306a36Sopenharmony_ci pipe = cvmx_usb_find_ready_pipe(usb, CVMX_USB_TRANSFER_CONTROL); 188662306a36Sopenharmony_ci if (pipe) 188762306a36Sopenharmony_ci return pipe; 188862306a36Sopenharmony_ci return cvmx_usb_find_ready_pipe(usb, CVMX_USB_TRANSFER_BULK); 188962306a36Sopenharmony_ci} 189062306a36Sopenharmony_ci 189162306a36Sopenharmony_ci/** 189262306a36Sopenharmony_ci * Called whenever a pipe might need to be scheduled to the 189362306a36Sopenharmony_ci * hardware. 189462306a36Sopenharmony_ci * 189562306a36Sopenharmony_ci * @usb: USB device state populated by cvmx_usb_initialize(). 189662306a36Sopenharmony_ci * @is_sof: True if this schedule was called on a SOF interrupt. 189762306a36Sopenharmony_ci */ 189862306a36Sopenharmony_cistatic void cvmx_usb_schedule(struct octeon_hcd *usb, int is_sof) 189962306a36Sopenharmony_ci{ 190062306a36Sopenharmony_ci int channel; 190162306a36Sopenharmony_ci struct cvmx_usb_pipe *pipe; 190262306a36Sopenharmony_ci int need_sof; 190362306a36Sopenharmony_ci enum cvmx_usb_transfer ttype; 190462306a36Sopenharmony_ci 190562306a36Sopenharmony_ci if (usb->init_flags & CVMX_USB_INITIALIZE_FLAGS_NO_DMA) { 190662306a36Sopenharmony_ci /* 190762306a36Sopenharmony_ci * Without DMA we need to be careful to not schedule something 190862306a36Sopenharmony_ci * at the end of a frame and cause an overrun. 190962306a36Sopenharmony_ci */ 191062306a36Sopenharmony_ci union cvmx_usbcx_hfnum hfnum = { 191162306a36Sopenharmony_ci .u32 = cvmx_usb_read_csr32(usb, 191262306a36Sopenharmony_ci CVMX_USBCX_HFNUM(usb->index)) 191362306a36Sopenharmony_ci }; 191462306a36Sopenharmony_ci 191562306a36Sopenharmony_ci union cvmx_usbcx_hfir hfir = { 191662306a36Sopenharmony_ci .u32 = cvmx_usb_read_csr32(usb, 191762306a36Sopenharmony_ci CVMX_USBCX_HFIR(usb->index)) 191862306a36Sopenharmony_ci }; 191962306a36Sopenharmony_ci 192062306a36Sopenharmony_ci if (hfnum.s.frrem < hfir.s.frint / 4) 192162306a36Sopenharmony_ci goto done; 192262306a36Sopenharmony_ci } 192362306a36Sopenharmony_ci 192462306a36Sopenharmony_ci while (usb->idle_hardware_channels) { 192562306a36Sopenharmony_ci /* Find an idle channel */ 192662306a36Sopenharmony_ci channel = __fls(usb->idle_hardware_channels); 192762306a36Sopenharmony_ci if (unlikely(channel > 7)) 192862306a36Sopenharmony_ci break; 192962306a36Sopenharmony_ci 193062306a36Sopenharmony_ci pipe = cvmx_usb_next_pipe(usb, is_sof); 193162306a36Sopenharmony_ci if (!pipe) 193262306a36Sopenharmony_ci break; 193362306a36Sopenharmony_ci 193462306a36Sopenharmony_ci cvmx_usb_start_channel(usb, channel, pipe); 193562306a36Sopenharmony_ci } 193662306a36Sopenharmony_ci 193762306a36Sopenharmony_cidone: 193862306a36Sopenharmony_ci /* 193962306a36Sopenharmony_ci * Only enable SOF interrupts when we have transactions pending in the 194062306a36Sopenharmony_ci * future that might need to be scheduled 194162306a36Sopenharmony_ci */ 194262306a36Sopenharmony_ci need_sof = 0; 194362306a36Sopenharmony_ci for (ttype = CVMX_USB_TRANSFER_CONTROL; 194462306a36Sopenharmony_ci ttype <= CVMX_USB_TRANSFER_INTERRUPT; ttype++) { 194562306a36Sopenharmony_ci list_for_each_entry(pipe, &usb->active_pipes[ttype], node) { 194662306a36Sopenharmony_ci if (pipe->next_tx_frame > usb->frame_number) { 194762306a36Sopenharmony_ci need_sof = 1; 194862306a36Sopenharmony_ci break; 194962306a36Sopenharmony_ci } 195062306a36Sopenharmony_ci } 195162306a36Sopenharmony_ci } 195262306a36Sopenharmony_ci USB_SET_FIELD32(CVMX_USBCX_GINTMSK(usb->index), 195362306a36Sopenharmony_ci cvmx_usbcx_gintmsk, sofmsk, need_sof); 195462306a36Sopenharmony_ci} 195562306a36Sopenharmony_ci 195662306a36Sopenharmony_cistatic void octeon_usb_urb_complete_callback(struct octeon_hcd *usb, 195762306a36Sopenharmony_ci enum cvmx_usb_status status, 195862306a36Sopenharmony_ci struct cvmx_usb_pipe *pipe, 195962306a36Sopenharmony_ci struct cvmx_usb_transaction 196062306a36Sopenharmony_ci *transaction, 196162306a36Sopenharmony_ci int bytes_transferred, 196262306a36Sopenharmony_ci struct urb *urb) 196362306a36Sopenharmony_ci{ 196462306a36Sopenharmony_ci struct usb_hcd *hcd = octeon_to_hcd(usb); 196562306a36Sopenharmony_ci struct device *dev = hcd->self.controller; 196662306a36Sopenharmony_ci 196762306a36Sopenharmony_ci if (likely(status == CVMX_USB_STATUS_OK)) 196862306a36Sopenharmony_ci urb->actual_length = bytes_transferred; 196962306a36Sopenharmony_ci else 197062306a36Sopenharmony_ci urb->actual_length = 0; 197162306a36Sopenharmony_ci 197262306a36Sopenharmony_ci urb->hcpriv = NULL; 197362306a36Sopenharmony_ci 197462306a36Sopenharmony_ci /* For Isochronous transactions we need to update the URB packet status 197562306a36Sopenharmony_ci * list from data in our private copy 197662306a36Sopenharmony_ci */ 197762306a36Sopenharmony_ci if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) { 197862306a36Sopenharmony_ci int i; 197962306a36Sopenharmony_ci /* 198062306a36Sopenharmony_ci * The pointer to the private list is stored in the setup_packet 198162306a36Sopenharmony_ci * field. 198262306a36Sopenharmony_ci */ 198362306a36Sopenharmony_ci struct cvmx_usb_iso_packet *iso_packet = 198462306a36Sopenharmony_ci (struct cvmx_usb_iso_packet *)urb->setup_packet; 198562306a36Sopenharmony_ci /* Recalculate the transfer size by adding up each packet */ 198662306a36Sopenharmony_ci urb->actual_length = 0; 198762306a36Sopenharmony_ci for (i = 0; i < urb->number_of_packets; i++) { 198862306a36Sopenharmony_ci if (iso_packet[i].status == CVMX_USB_STATUS_OK) { 198962306a36Sopenharmony_ci urb->iso_frame_desc[i].status = 0; 199062306a36Sopenharmony_ci urb->iso_frame_desc[i].actual_length = 199162306a36Sopenharmony_ci iso_packet[i].length; 199262306a36Sopenharmony_ci urb->actual_length += 199362306a36Sopenharmony_ci urb->iso_frame_desc[i].actual_length; 199462306a36Sopenharmony_ci } else { 199562306a36Sopenharmony_ci dev_dbg(dev, "ISOCHRONOUS packet=%d of %d status=%d pipe=%p transaction=%p size=%d\n", 199662306a36Sopenharmony_ci i, urb->number_of_packets, 199762306a36Sopenharmony_ci iso_packet[i].status, pipe, 199862306a36Sopenharmony_ci transaction, iso_packet[i].length); 199962306a36Sopenharmony_ci urb->iso_frame_desc[i].status = -EREMOTEIO; 200062306a36Sopenharmony_ci } 200162306a36Sopenharmony_ci } 200262306a36Sopenharmony_ci /* Free the private list now that we don't need it anymore */ 200362306a36Sopenharmony_ci kfree(iso_packet); 200462306a36Sopenharmony_ci urb->setup_packet = NULL; 200562306a36Sopenharmony_ci } 200662306a36Sopenharmony_ci 200762306a36Sopenharmony_ci switch (status) { 200862306a36Sopenharmony_ci case CVMX_USB_STATUS_OK: 200962306a36Sopenharmony_ci urb->status = 0; 201062306a36Sopenharmony_ci break; 201162306a36Sopenharmony_ci case CVMX_USB_STATUS_CANCEL: 201262306a36Sopenharmony_ci if (urb->status == 0) 201362306a36Sopenharmony_ci urb->status = -ENOENT; 201462306a36Sopenharmony_ci break; 201562306a36Sopenharmony_ci case CVMX_USB_STATUS_STALL: 201662306a36Sopenharmony_ci dev_dbg(dev, "status=stall pipe=%p transaction=%p size=%d\n", 201762306a36Sopenharmony_ci pipe, transaction, bytes_transferred); 201862306a36Sopenharmony_ci urb->status = -EPIPE; 201962306a36Sopenharmony_ci break; 202062306a36Sopenharmony_ci case CVMX_USB_STATUS_BABBLEERR: 202162306a36Sopenharmony_ci dev_dbg(dev, "status=babble pipe=%p transaction=%p size=%d\n", 202262306a36Sopenharmony_ci pipe, transaction, bytes_transferred); 202362306a36Sopenharmony_ci urb->status = -EPIPE; 202462306a36Sopenharmony_ci break; 202562306a36Sopenharmony_ci case CVMX_USB_STATUS_SHORT: 202662306a36Sopenharmony_ci dev_dbg(dev, "status=short pipe=%p transaction=%p size=%d\n", 202762306a36Sopenharmony_ci pipe, transaction, bytes_transferred); 202862306a36Sopenharmony_ci urb->status = -EREMOTEIO; 202962306a36Sopenharmony_ci break; 203062306a36Sopenharmony_ci case CVMX_USB_STATUS_ERROR: 203162306a36Sopenharmony_ci case CVMX_USB_STATUS_XACTERR: 203262306a36Sopenharmony_ci case CVMX_USB_STATUS_DATATGLERR: 203362306a36Sopenharmony_ci case CVMX_USB_STATUS_FRAMEERR: 203462306a36Sopenharmony_ci dev_dbg(dev, "status=%d pipe=%p transaction=%p size=%d\n", 203562306a36Sopenharmony_ci status, pipe, transaction, bytes_transferred); 203662306a36Sopenharmony_ci urb->status = -EPROTO; 203762306a36Sopenharmony_ci break; 203862306a36Sopenharmony_ci } 203962306a36Sopenharmony_ci usb_hcd_unlink_urb_from_ep(octeon_to_hcd(usb), urb); 204062306a36Sopenharmony_ci spin_unlock(&usb->lock); 204162306a36Sopenharmony_ci usb_hcd_giveback_urb(octeon_to_hcd(usb), urb, urb->status); 204262306a36Sopenharmony_ci spin_lock(&usb->lock); 204362306a36Sopenharmony_ci} 204462306a36Sopenharmony_ci 204562306a36Sopenharmony_ci/** 204662306a36Sopenharmony_ci * Signal the completion of a transaction and free it. The 204762306a36Sopenharmony_ci * transaction will be removed from the pipe transaction list. 204862306a36Sopenharmony_ci * 204962306a36Sopenharmony_ci * @usb: USB device state populated by cvmx_usb_initialize(). 205062306a36Sopenharmony_ci * @pipe: Pipe the transaction is on 205162306a36Sopenharmony_ci * @transaction: 205262306a36Sopenharmony_ci * Transaction that completed 205362306a36Sopenharmony_ci * @complete_code: 205462306a36Sopenharmony_ci * Completion code 205562306a36Sopenharmony_ci */ 205662306a36Sopenharmony_cistatic void cvmx_usb_complete(struct octeon_hcd *usb, 205762306a36Sopenharmony_ci struct cvmx_usb_pipe *pipe, 205862306a36Sopenharmony_ci struct cvmx_usb_transaction *transaction, 205962306a36Sopenharmony_ci enum cvmx_usb_status complete_code) 206062306a36Sopenharmony_ci{ 206162306a36Sopenharmony_ci /* If this was a split then clear our split in progress marker */ 206262306a36Sopenharmony_ci if (usb->active_split == transaction) 206362306a36Sopenharmony_ci usb->active_split = NULL; 206462306a36Sopenharmony_ci 206562306a36Sopenharmony_ci /* 206662306a36Sopenharmony_ci * Isochronous transactions need extra processing as they might not be 206762306a36Sopenharmony_ci * done after a single data transfer 206862306a36Sopenharmony_ci */ 206962306a36Sopenharmony_ci if (unlikely(transaction->type == CVMX_USB_TRANSFER_ISOCHRONOUS)) { 207062306a36Sopenharmony_ci /* Update the number of bytes transferred in this ISO packet */ 207162306a36Sopenharmony_ci transaction->iso_packets[0].length = transaction->actual_bytes; 207262306a36Sopenharmony_ci transaction->iso_packets[0].status = complete_code; 207362306a36Sopenharmony_ci 207462306a36Sopenharmony_ci /* 207562306a36Sopenharmony_ci * If there are more ISOs pending and we succeeded, schedule the 207662306a36Sopenharmony_ci * next one 207762306a36Sopenharmony_ci */ 207862306a36Sopenharmony_ci if ((transaction->iso_number_packets > 1) && 207962306a36Sopenharmony_ci (complete_code == CVMX_USB_STATUS_OK)) { 208062306a36Sopenharmony_ci /* No bytes transferred for this packet as of yet */ 208162306a36Sopenharmony_ci transaction->actual_bytes = 0; 208262306a36Sopenharmony_ci /* One less ISO waiting to transfer */ 208362306a36Sopenharmony_ci transaction->iso_number_packets--; 208462306a36Sopenharmony_ci /* Increment to the next location in our packet array */ 208562306a36Sopenharmony_ci transaction->iso_packets++; 208662306a36Sopenharmony_ci transaction->stage = CVMX_USB_STAGE_NON_CONTROL; 208762306a36Sopenharmony_ci return; 208862306a36Sopenharmony_ci } 208962306a36Sopenharmony_ci } 209062306a36Sopenharmony_ci 209162306a36Sopenharmony_ci /* Remove the transaction from the pipe list */ 209262306a36Sopenharmony_ci list_del(&transaction->node); 209362306a36Sopenharmony_ci if (list_empty(&pipe->transactions)) 209462306a36Sopenharmony_ci list_move_tail(&pipe->node, &usb->idle_pipes); 209562306a36Sopenharmony_ci octeon_usb_urb_complete_callback(usb, complete_code, pipe, 209662306a36Sopenharmony_ci transaction, 209762306a36Sopenharmony_ci transaction->actual_bytes, 209862306a36Sopenharmony_ci transaction->urb); 209962306a36Sopenharmony_ci kfree(transaction); 210062306a36Sopenharmony_ci} 210162306a36Sopenharmony_ci 210262306a36Sopenharmony_ci/** 210362306a36Sopenharmony_ci * Submit a usb transaction to a pipe. Called for all types 210462306a36Sopenharmony_ci * of transactions. 210562306a36Sopenharmony_ci * 210662306a36Sopenharmony_ci * @usb: 210762306a36Sopenharmony_ci * @pipe: Which pipe to submit to. 210862306a36Sopenharmony_ci * @type: Transaction type 210962306a36Sopenharmony_ci * @buffer: User buffer for the transaction 211062306a36Sopenharmony_ci * @buffer_length: 211162306a36Sopenharmony_ci * User buffer's length in bytes 211262306a36Sopenharmony_ci * @control_header: 211362306a36Sopenharmony_ci * For control transactions, the 8 byte standard header 211462306a36Sopenharmony_ci * @iso_start_frame: 211562306a36Sopenharmony_ci * For ISO transactions, the start frame 211662306a36Sopenharmony_ci * @iso_number_packets: 211762306a36Sopenharmony_ci * For ISO, the number of packet in the transaction. 211862306a36Sopenharmony_ci * @iso_packets: 211962306a36Sopenharmony_ci * A description of each ISO packet 212062306a36Sopenharmony_ci * @urb: URB for the callback 212162306a36Sopenharmony_ci * 212262306a36Sopenharmony_ci * Returns: Transaction or NULL on failure. 212362306a36Sopenharmony_ci */ 212462306a36Sopenharmony_cistatic struct cvmx_usb_transaction *cvmx_usb_submit_transaction( 212562306a36Sopenharmony_ci struct octeon_hcd *usb, 212662306a36Sopenharmony_ci struct cvmx_usb_pipe *pipe, 212762306a36Sopenharmony_ci enum cvmx_usb_transfer type, 212862306a36Sopenharmony_ci u64 buffer, 212962306a36Sopenharmony_ci int buffer_length, 213062306a36Sopenharmony_ci u64 control_header, 213162306a36Sopenharmony_ci int iso_start_frame, 213262306a36Sopenharmony_ci int iso_number_packets, 213362306a36Sopenharmony_ci struct cvmx_usb_iso_packet *iso_packets, 213462306a36Sopenharmony_ci struct urb *urb) 213562306a36Sopenharmony_ci{ 213662306a36Sopenharmony_ci struct cvmx_usb_transaction *transaction; 213762306a36Sopenharmony_ci 213862306a36Sopenharmony_ci if (unlikely(pipe->transfer_type != type)) 213962306a36Sopenharmony_ci return NULL; 214062306a36Sopenharmony_ci 214162306a36Sopenharmony_ci transaction = kzalloc(sizeof(*transaction), GFP_ATOMIC); 214262306a36Sopenharmony_ci if (unlikely(!transaction)) 214362306a36Sopenharmony_ci return NULL; 214462306a36Sopenharmony_ci 214562306a36Sopenharmony_ci transaction->type = type; 214662306a36Sopenharmony_ci transaction->buffer = buffer; 214762306a36Sopenharmony_ci transaction->buffer_length = buffer_length; 214862306a36Sopenharmony_ci transaction->control_header = control_header; 214962306a36Sopenharmony_ci /* FIXME: This is not used, implement it. */ 215062306a36Sopenharmony_ci transaction->iso_start_frame = iso_start_frame; 215162306a36Sopenharmony_ci transaction->iso_number_packets = iso_number_packets; 215262306a36Sopenharmony_ci transaction->iso_packets = iso_packets; 215362306a36Sopenharmony_ci transaction->urb = urb; 215462306a36Sopenharmony_ci if (transaction->type == CVMX_USB_TRANSFER_CONTROL) 215562306a36Sopenharmony_ci transaction->stage = CVMX_USB_STAGE_SETUP; 215662306a36Sopenharmony_ci else 215762306a36Sopenharmony_ci transaction->stage = CVMX_USB_STAGE_NON_CONTROL; 215862306a36Sopenharmony_ci 215962306a36Sopenharmony_ci if (!list_empty(&pipe->transactions)) { 216062306a36Sopenharmony_ci list_add_tail(&transaction->node, &pipe->transactions); 216162306a36Sopenharmony_ci } else { 216262306a36Sopenharmony_ci list_add_tail(&transaction->node, &pipe->transactions); 216362306a36Sopenharmony_ci list_move_tail(&pipe->node, 216462306a36Sopenharmony_ci &usb->active_pipes[pipe->transfer_type]); 216562306a36Sopenharmony_ci 216662306a36Sopenharmony_ci /* 216762306a36Sopenharmony_ci * We may need to schedule the pipe if this was the head of the 216862306a36Sopenharmony_ci * pipe. 216962306a36Sopenharmony_ci */ 217062306a36Sopenharmony_ci cvmx_usb_schedule(usb, 0); 217162306a36Sopenharmony_ci } 217262306a36Sopenharmony_ci 217362306a36Sopenharmony_ci return transaction; 217462306a36Sopenharmony_ci} 217562306a36Sopenharmony_ci 217662306a36Sopenharmony_ci/** 217762306a36Sopenharmony_ci * Call to submit a USB Bulk transfer to a pipe. 217862306a36Sopenharmony_ci * 217962306a36Sopenharmony_ci * @usb: USB device state populated by cvmx_usb_initialize(). 218062306a36Sopenharmony_ci * @pipe: Handle to the pipe for the transfer. 218162306a36Sopenharmony_ci * @urb: URB. 218262306a36Sopenharmony_ci * 218362306a36Sopenharmony_ci * Returns: A submitted transaction or NULL on failure. 218462306a36Sopenharmony_ci */ 218562306a36Sopenharmony_cistatic struct cvmx_usb_transaction *cvmx_usb_submit_bulk( 218662306a36Sopenharmony_ci struct octeon_hcd *usb, 218762306a36Sopenharmony_ci struct cvmx_usb_pipe *pipe, 218862306a36Sopenharmony_ci struct urb *urb) 218962306a36Sopenharmony_ci{ 219062306a36Sopenharmony_ci return cvmx_usb_submit_transaction(usb, pipe, CVMX_USB_TRANSFER_BULK, 219162306a36Sopenharmony_ci urb->transfer_dma, 219262306a36Sopenharmony_ci urb->transfer_buffer_length, 219362306a36Sopenharmony_ci 0, /* control_header */ 219462306a36Sopenharmony_ci 0, /* iso_start_frame */ 219562306a36Sopenharmony_ci 0, /* iso_number_packets */ 219662306a36Sopenharmony_ci NULL, /* iso_packets */ 219762306a36Sopenharmony_ci urb); 219862306a36Sopenharmony_ci} 219962306a36Sopenharmony_ci 220062306a36Sopenharmony_ci/** 220162306a36Sopenharmony_ci * Call to submit a USB Interrupt transfer to a pipe. 220262306a36Sopenharmony_ci * 220362306a36Sopenharmony_ci * @usb: USB device state populated by cvmx_usb_initialize(). 220462306a36Sopenharmony_ci * @pipe: Handle to the pipe for the transfer. 220562306a36Sopenharmony_ci * @urb: URB returned when the callback is called. 220662306a36Sopenharmony_ci * 220762306a36Sopenharmony_ci * Returns: A submitted transaction or NULL on failure. 220862306a36Sopenharmony_ci */ 220962306a36Sopenharmony_cistatic struct cvmx_usb_transaction *cvmx_usb_submit_interrupt( 221062306a36Sopenharmony_ci struct octeon_hcd *usb, 221162306a36Sopenharmony_ci struct cvmx_usb_pipe *pipe, 221262306a36Sopenharmony_ci struct urb *urb) 221362306a36Sopenharmony_ci{ 221462306a36Sopenharmony_ci return cvmx_usb_submit_transaction(usb, pipe, 221562306a36Sopenharmony_ci CVMX_USB_TRANSFER_INTERRUPT, 221662306a36Sopenharmony_ci urb->transfer_dma, 221762306a36Sopenharmony_ci urb->transfer_buffer_length, 221862306a36Sopenharmony_ci 0, /* control_header */ 221962306a36Sopenharmony_ci 0, /* iso_start_frame */ 222062306a36Sopenharmony_ci 0, /* iso_number_packets */ 222162306a36Sopenharmony_ci NULL, /* iso_packets */ 222262306a36Sopenharmony_ci urb); 222362306a36Sopenharmony_ci} 222462306a36Sopenharmony_ci 222562306a36Sopenharmony_ci/** 222662306a36Sopenharmony_ci * Call to submit a USB Control transfer to a pipe. 222762306a36Sopenharmony_ci * 222862306a36Sopenharmony_ci * @usb: USB device state populated by cvmx_usb_initialize(). 222962306a36Sopenharmony_ci * @pipe: Handle to the pipe for the transfer. 223062306a36Sopenharmony_ci * @urb: URB. 223162306a36Sopenharmony_ci * 223262306a36Sopenharmony_ci * Returns: A submitted transaction or NULL on failure. 223362306a36Sopenharmony_ci */ 223462306a36Sopenharmony_cistatic struct cvmx_usb_transaction *cvmx_usb_submit_control( 223562306a36Sopenharmony_ci struct octeon_hcd *usb, 223662306a36Sopenharmony_ci struct cvmx_usb_pipe *pipe, 223762306a36Sopenharmony_ci struct urb *urb) 223862306a36Sopenharmony_ci{ 223962306a36Sopenharmony_ci int buffer_length = urb->transfer_buffer_length; 224062306a36Sopenharmony_ci u64 control_header = urb->setup_dma; 224162306a36Sopenharmony_ci struct usb_ctrlrequest *header = cvmx_phys_to_ptr(control_header); 224262306a36Sopenharmony_ci 224362306a36Sopenharmony_ci if ((header->bRequestType & USB_DIR_IN) == 0) 224462306a36Sopenharmony_ci buffer_length = le16_to_cpu(header->wLength); 224562306a36Sopenharmony_ci 224662306a36Sopenharmony_ci return cvmx_usb_submit_transaction(usb, pipe, 224762306a36Sopenharmony_ci CVMX_USB_TRANSFER_CONTROL, 224862306a36Sopenharmony_ci urb->transfer_dma, buffer_length, 224962306a36Sopenharmony_ci control_header, 225062306a36Sopenharmony_ci 0, /* iso_start_frame */ 225162306a36Sopenharmony_ci 0, /* iso_number_packets */ 225262306a36Sopenharmony_ci NULL, /* iso_packets */ 225362306a36Sopenharmony_ci urb); 225462306a36Sopenharmony_ci} 225562306a36Sopenharmony_ci 225662306a36Sopenharmony_ci/** 225762306a36Sopenharmony_ci * Call to submit a USB Isochronous transfer to a pipe. 225862306a36Sopenharmony_ci * 225962306a36Sopenharmony_ci * @usb: USB device state populated by cvmx_usb_initialize(). 226062306a36Sopenharmony_ci * @pipe: Handle to the pipe for the transfer. 226162306a36Sopenharmony_ci * @urb: URB returned when the callback is called. 226262306a36Sopenharmony_ci * 226362306a36Sopenharmony_ci * Returns: A submitted transaction or NULL on failure. 226462306a36Sopenharmony_ci */ 226562306a36Sopenharmony_cistatic struct cvmx_usb_transaction *cvmx_usb_submit_isochronous( 226662306a36Sopenharmony_ci struct octeon_hcd *usb, 226762306a36Sopenharmony_ci struct cvmx_usb_pipe *pipe, 226862306a36Sopenharmony_ci struct urb *urb) 226962306a36Sopenharmony_ci{ 227062306a36Sopenharmony_ci struct cvmx_usb_iso_packet *packets; 227162306a36Sopenharmony_ci 227262306a36Sopenharmony_ci packets = (struct cvmx_usb_iso_packet *)urb->setup_packet; 227362306a36Sopenharmony_ci return cvmx_usb_submit_transaction(usb, pipe, 227462306a36Sopenharmony_ci CVMX_USB_TRANSFER_ISOCHRONOUS, 227562306a36Sopenharmony_ci urb->transfer_dma, 227662306a36Sopenharmony_ci urb->transfer_buffer_length, 227762306a36Sopenharmony_ci 0, /* control_header */ 227862306a36Sopenharmony_ci urb->start_frame, 227962306a36Sopenharmony_ci urb->number_of_packets, 228062306a36Sopenharmony_ci packets, urb); 228162306a36Sopenharmony_ci} 228262306a36Sopenharmony_ci 228362306a36Sopenharmony_ci/** 228462306a36Sopenharmony_ci * Cancel one outstanding request in a pipe. Canceling a request 228562306a36Sopenharmony_ci * can fail if the transaction has already completed before cancel 228662306a36Sopenharmony_ci * is called. Even after a successful cancel call, it may take 228762306a36Sopenharmony_ci * a frame or two for the cvmx_usb_poll() function to call the 228862306a36Sopenharmony_ci * associated callback. 228962306a36Sopenharmony_ci * 229062306a36Sopenharmony_ci * @usb: USB device state populated by cvmx_usb_initialize(). 229162306a36Sopenharmony_ci * @pipe: Pipe to cancel requests in. 229262306a36Sopenharmony_ci * @transaction: Transaction to cancel, returned by the submit function. 229362306a36Sopenharmony_ci * 229462306a36Sopenharmony_ci * Returns: 0 or a negative error code. 229562306a36Sopenharmony_ci */ 229662306a36Sopenharmony_cistatic int cvmx_usb_cancel(struct octeon_hcd *usb, 229762306a36Sopenharmony_ci struct cvmx_usb_pipe *pipe, 229862306a36Sopenharmony_ci struct cvmx_usb_transaction *transaction) 229962306a36Sopenharmony_ci{ 230062306a36Sopenharmony_ci /* 230162306a36Sopenharmony_ci * If the transaction is the HEAD of the queue and scheduled. We need to 230262306a36Sopenharmony_ci * treat it special 230362306a36Sopenharmony_ci */ 230462306a36Sopenharmony_ci if (list_first_entry(&pipe->transactions, typeof(*transaction), node) == 230562306a36Sopenharmony_ci transaction && (pipe->flags & CVMX_USB_PIPE_FLAGS_SCHEDULED)) { 230662306a36Sopenharmony_ci union cvmx_usbcx_hccharx usbc_hcchar; 230762306a36Sopenharmony_ci 230862306a36Sopenharmony_ci usb->pipe_for_channel[pipe->channel] = NULL; 230962306a36Sopenharmony_ci pipe->flags &= ~CVMX_USB_PIPE_FLAGS_SCHEDULED; 231062306a36Sopenharmony_ci 231162306a36Sopenharmony_ci CVMX_SYNCW; 231262306a36Sopenharmony_ci 231362306a36Sopenharmony_ci usbc_hcchar.u32 = cvmx_usb_read_csr32(usb, 231462306a36Sopenharmony_ci CVMX_USBCX_HCCHARX(pipe->channel, 231562306a36Sopenharmony_ci usb->index)); 231662306a36Sopenharmony_ci /* 231762306a36Sopenharmony_ci * If the channel isn't enabled then the transaction already 231862306a36Sopenharmony_ci * completed. 231962306a36Sopenharmony_ci */ 232062306a36Sopenharmony_ci if (usbc_hcchar.s.chena) { 232162306a36Sopenharmony_ci usbc_hcchar.s.chdis = 1; 232262306a36Sopenharmony_ci cvmx_usb_write_csr32(usb, 232362306a36Sopenharmony_ci CVMX_USBCX_HCCHARX(pipe->channel, 232462306a36Sopenharmony_ci usb->index), 232562306a36Sopenharmony_ci usbc_hcchar.u32); 232662306a36Sopenharmony_ci } 232762306a36Sopenharmony_ci } 232862306a36Sopenharmony_ci cvmx_usb_complete(usb, pipe, transaction, CVMX_USB_STATUS_CANCEL); 232962306a36Sopenharmony_ci return 0; 233062306a36Sopenharmony_ci} 233162306a36Sopenharmony_ci 233262306a36Sopenharmony_ci/** 233362306a36Sopenharmony_ci * Cancel all outstanding requests in a pipe. Logically all this 233462306a36Sopenharmony_ci * does is call cvmx_usb_cancel() in a loop. 233562306a36Sopenharmony_ci * 233662306a36Sopenharmony_ci * @usb: USB device state populated by cvmx_usb_initialize(). 233762306a36Sopenharmony_ci * @pipe: Pipe to cancel requests in. 233862306a36Sopenharmony_ci * 233962306a36Sopenharmony_ci * Returns: 0 or a negative error code. 234062306a36Sopenharmony_ci */ 234162306a36Sopenharmony_cistatic int cvmx_usb_cancel_all(struct octeon_hcd *usb, 234262306a36Sopenharmony_ci struct cvmx_usb_pipe *pipe) 234362306a36Sopenharmony_ci{ 234462306a36Sopenharmony_ci struct cvmx_usb_transaction *transaction, *next; 234562306a36Sopenharmony_ci 234662306a36Sopenharmony_ci /* Simply loop through and attempt to cancel each transaction */ 234762306a36Sopenharmony_ci list_for_each_entry_safe(transaction, next, &pipe->transactions, node) { 234862306a36Sopenharmony_ci int result = cvmx_usb_cancel(usb, pipe, transaction); 234962306a36Sopenharmony_ci 235062306a36Sopenharmony_ci if (unlikely(result != 0)) 235162306a36Sopenharmony_ci return result; 235262306a36Sopenharmony_ci } 235362306a36Sopenharmony_ci return 0; 235462306a36Sopenharmony_ci} 235562306a36Sopenharmony_ci 235662306a36Sopenharmony_ci/** 235762306a36Sopenharmony_ci * Close a pipe created with cvmx_usb_open_pipe(). 235862306a36Sopenharmony_ci * 235962306a36Sopenharmony_ci * @usb: USB device state populated by cvmx_usb_initialize(). 236062306a36Sopenharmony_ci * @pipe: Pipe to close. 236162306a36Sopenharmony_ci * 236262306a36Sopenharmony_ci * Returns: 0 or a negative error code. EBUSY is returned if the pipe has 236362306a36Sopenharmony_ci * outstanding transfers. 236462306a36Sopenharmony_ci */ 236562306a36Sopenharmony_cistatic int cvmx_usb_close_pipe(struct octeon_hcd *usb, 236662306a36Sopenharmony_ci struct cvmx_usb_pipe *pipe) 236762306a36Sopenharmony_ci{ 236862306a36Sopenharmony_ci /* Fail if the pipe has pending transactions */ 236962306a36Sopenharmony_ci if (!list_empty(&pipe->transactions)) 237062306a36Sopenharmony_ci return -EBUSY; 237162306a36Sopenharmony_ci 237262306a36Sopenharmony_ci list_del(&pipe->node); 237362306a36Sopenharmony_ci kfree(pipe); 237462306a36Sopenharmony_ci 237562306a36Sopenharmony_ci return 0; 237662306a36Sopenharmony_ci} 237762306a36Sopenharmony_ci 237862306a36Sopenharmony_ci/** 237962306a36Sopenharmony_ci * Get the current USB protocol level frame number. The frame 238062306a36Sopenharmony_ci * number is always in the range of 0-0x7ff. 238162306a36Sopenharmony_ci * 238262306a36Sopenharmony_ci * @usb: USB device state populated by cvmx_usb_initialize(). 238362306a36Sopenharmony_ci * 238462306a36Sopenharmony_ci * Returns: USB frame number 238562306a36Sopenharmony_ci */ 238662306a36Sopenharmony_cistatic int cvmx_usb_get_frame_number(struct octeon_hcd *usb) 238762306a36Sopenharmony_ci{ 238862306a36Sopenharmony_ci union cvmx_usbcx_hfnum usbc_hfnum; 238962306a36Sopenharmony_ci 239062306a36Sopenharmony_ci usbc_hfnum.u32 = cvmx_usb_read_csr32(usb, CVMX_USBCX_HFNUM(usb->index)); 239162306a36Sopenharmony_ci 239262306a36Sopenharmony_ci return usbc_hfnum.s.frnum; 239362306a36Sopenharmony_ci} 239462306a36Sopenharmony_ci 239562306a36Sopenharmony_cistatic void cvmx_usb_transfer_control(struct octeon_hcd *usb, 239662306a36Sopenharmony_ci struct cvmx_usb_pipe *pipe, 239762306a36Sopenharmony_ci struct cvmx_usb_transaction *transaction, 239862306a36Sopenharmony_ci union cvmx_usbcx_hccharx usbc_hcchar, 239962306a36Sopenharmony_ci int buffer_space_left, 240062306a36Sopenharmony_ci int bytes_in_last_packet) 240162306a36Sopenharmony_ci{ 240262306a36Sopenharmony_ci switch (transaction->stage) { 240362306a36Sopenharmony_ci case CVMX_USB_STAGE_NON_CONTROL: 240462306a36Sopenharmony_ci case CVMX_USB_STAGE_NON_CONTROL_SPLIT_COMPLETE: 240562306a36Sopenharmony_ci /* This should be impossible */ 240662306a36Sopenharmony_ci cvmx_usb_complete(usb, pipe, transaction, 240762306a36Sopenharmony_ci CVMX_USB_STATUS_ERROR); 240862306a36Sopenharmony_ci break; 240962306a36Sopenharmony_ci case CVMX_USB_STAGE_SETUP: 241062306a36Sopenharmony_ci pipe->pid_toggle = 1; 241162306a36Sopenharmony_ci if (cvmx_usb_pipe_needs_split(usb, pipe)) { 241262306a36Sopenharmony_ci transaction->stage = 241362306a36Sopenharmony_ci CVMX_USB_STAGE_SETUP_SPLIT_COMPLETE; 241462306a36Sopenharmony_ci } else { 241562306a36Sopenharmony_ci struct usb_ctrlrequest *header = 241662306a36Sopenharmony_ci cvmx_phys_to_ptr(transaction->control_header); 241762306a36Sopenharmony_ci if (header->wLength) 241862306a36Sopenharmony_ci transaction->stage = CVMX_USB_STAGE_DATA; 241962306a36Sopenharmony_ci else 242062306a36Sopenharmony_ci transaction->stage = CVMX_USB_STAGE_STATUS; 242162306a36Sopenharmony_ci } 242262306a36Sopenharmony_ci break; 242362306a36Sopenharmony_ci case CVMX_USB_STAGE_SETUP_SPLIT_COMPLETE: 242462306a36Sopenharmony_ci { 242562306a36Sopenharmony_ci struct usb_ctrlrequest *header = 242662306a36Sopenharmony_ci cvmx_phys_to_ptr(transaction->control_header); 242762306a36Sopenharmony_ci if (header->wLength) 242862306a36Sopenharmony_ci transaction->stage = CVMX_USB_STAGE_DATA; 242962306a36Sopenharmony_ci else 243062306a36Sopenharmony_ci transaction->stage = CVMX_USB_STAGE_STATUS; 243162306a36Sopenharmony_ci } 243262306a36Sopenharmony_ci break; 243362306a36Sopenharmony_ci case CVMX_USB_STAGE_DATA: 243462306a36Sopenharmony_ci if (cvmx_usb_pipe_needs_split(usb, pipe)) { 243562306a36Sopenharmony_ci transaction->stage = CVMX_USB_STAGE_DATA_SPLIT_COMPLETE; 243662306a36Sopenharmony_ci /* 243762306a36Sopenharmony_ci * For setup OUT data that are splits, 243862306a36Sopenharmony_ci * the hardware doesn't appear to count 243962306a36Sopenharmony_ci * transferred data. Here we manually 244062306a36Sopenharmony_ci * update the data transferred 244162306a36Sopenharmony_ci */ 244262306a36Sopenharmony_ci if (!usbc_hcchar.s.epdir) { 244362306a36Sopenharmony_ci if (buffer_space_left < pipe->max_packet) 244462306a36Sopenharmony_ci transaction->actual_bytes += 244562306a36Sopenharmony_ci buffer_space_left; 244662306a36Sopenharmony_ci else 244762306a36Sopenharmony_ci transaction->actual_bytes += 244862306a36Sopenharmony_ci pipe->max_packet; 244962306a36Sopenharmony_ci } 245062306a36Sopenharmony_ci } else if ((buffer_space_left == 0) || 245162306a36Sopenharmony_ci (bytes_in_last_packet < pipe->max_packet)) { 245262306a36Sopenharmony_ci pipe->pid_toggle = 1; 245362306a36Sopenharmony_ci transaction->stage = CVMX_USB_STAGE_STATUS; 245462306a36Sopenharmony_ci } 245562306a36Sopenharmony_ci break; 245662306a36Sopenharmony_ci case CVMX_USB_STAGE_DATA_SPLIT_COMPLETE: 245762306a36Sopenharmony_ci if ((buffer_space_left == 0) || 245862306a36Sopenharmony_ci (bytes_in_last_packet < pipe->max_packet)) { 245962306a36Sopenharmony_ci pipe->pid_toggle = 1; 246062306a36Sopenharmony_ci transaction->stage = CVMX_USB_STAGE_STATUS; 246162306a36Sopenharmony_ci } else { 246262306a36Sopenharmony_ci transaction->stage = CVMX_USB_STAGE_DATA; 246362306a36Sopenharmony_ci } 246462306a36Sopenharmony_ci break; 246562306a36Sopenharmony_ci case CVMX_USB_STAGE_STATUS: 246662306a36Sopenharmony_ci if (cvmx_usb_pipe_needs_split(usb, pipe)) 246762306a36Sopenharmony_ci transaction->stage = 246862306a36Sopenharmony_ci CVMX_USB_STAGE_STATUS_SPLIT_COMPLETE; 246962306a36Sopenharmony_ci else 247062306a36Sopenharmony_ci cvmx_usb_complete(usb, pipe, transaction, 247162306a36Sopenharmony_ci CVMX_USB_STATUS_OK); 247262306a36Sopenharmony_ci break; 247362306a36Sopenharmony_ci case CVMX_USB_STAGE_STATUS_SPLIT_COMPLETE: 247462306a36Sopenharmony_ci cvmx_usb_complete(usb, pipe, transaction, CVMX_USB_STATUS_OK); 247562306a36Sopenharmony_ci break; 247662306a36Sopenharmony_ci } 247762306a36Sopenharmony_ci} 247862306a36Sopenharmony_ci 247962306a36Sopenharmony_cistatic void cvmx_usb_transfer_bulk(struct octeon_hcd *usb, 248062306a36Sopenharmony_ci struct cvmx_usb_pipe *pipe, 248162306a36Sopenharmony_ci struct cvmx_usb_transaction *transaction, 248262306a36Sopenharmony_ci union cvmx_usbcx_hcintx usbc_hcint, 248362306a36Sopenharmony_ci int buffer_space_left, 248462306a36Sopenharmony_ci int bytes_in_last_packet) 248562306a36Sopenharmony_ci{ 248662306a36Sopenharmony_ci /* 248762306a36Sopenharmony_ci * The only time a bulk transfer isn't complete when it finishes with 248862306a36Sopenharmony_ci * an ACK is during a split transaction. For splits we need to continue 248962306a36Sopenharmony_ci * the transfer if more data is needed. 249062306a36Sopenharmony_ci */ 249162306a36Sopenharmony_ci if (cvmx_usb_pipe_needs_split(usb, pipe)) { 249262306a36Sopenharmony_ci if (transaction->stage == CVMX_USB_STAGE_NON_CONTROL) 249362306a36Sopenharmony_ci transaction->stage = 249462306a36Sopenharmony_ci CVMX_USB_STAGE_NON_CONTROL_SPLIT_COMPLETE; 249562306a36Sopenharmony_ci else if (buffer_space_left && 249662306a36Sopenharmony_ci (bytes_in_last_packet == pipe->max_packet)) 249762306a36Sopenharmony_ci transaction->stage = CVMX_USB_STAGE_NON_CONTROL; 249862306a36Sopenharmony_ci else 249962306a36Sopenharmony_ci cvmx_usb_complete(usb, pipe, transaction, 250062306a36Sopenharmony_ci CVMX_USB_STATUS_OK); 250162306a36Sopenharmony_ci } else { 250262306a36Sopenharmony_ci if ((pipe->device_speed == CVMX_USB_SPEED_HIGH) && 250362306a36Sopenharmony_ci (pipe->transfer_dir == CVMX_USB_DIRECTION_OUT) && 250462306a36Sopenharmony_ci (usbc_hcint.s.nak)) 250562306a36Sopenharmony_ci pipe->flags |= CVMX_USB_PIPE_FLAGS_NEED_PING; 250662306a36Sopenharmony_ci if (!buffer_space_left || 250762306a36Sopenharmony_ci (bytes_in_last_packet < pipe->max_packet)) 250862306a36Sopenharmony_ci cvmx_usb_complete(usb, pipe, transaction, 250962306a36Sopenharmony_ci CVMX_USB_STATUS_OK); 251062306a36Sopenharmony_ci } 251162306a36Sopenharmony_ci} 251262306a36Sopenharmony_ci 251362306a36Sopenharmony_cistatic void cvmx_usb_transfer_intr(struct octeon_hcd *usb, 251462306a36Sopenharmony_ci struct cvmx_usb_pipe *pipe, 251562306a36Sopenharmony_ci struct cvmx_usb_transaction *transaction, 251662306a36Sopenharmony_ci int buffer_space_left, 251762306a36Sopenharmony_ci int bytes_in_last_packet) 251862306a36Sopenharmony_ci{ 251962306a36Sopenharmony_ci if (cvmx_usb_pipe_needs_split(usb, pipe)) { 252062306a36Sopenharmony_ci if (transaction->stage == CVMX_USB_STAGE_NON_CONTROL) { 252162306a36Sopenharmony_ci transaction->stage = 252262306a36Sopenharmony_ci CVMX_USB_STAGE_NON_CONTROL_SPLIT_COMPLETE; 252362306a36Sopenharmony_ci } else if (buffer_space_left && 252462306a36Sopenharmony_ci (bytes_in_last_packet == pipe->max_packet)) { 252562306a36Sopenharmony_ci transaction->stage = CVMX_USB_STAGE_NON_CONTROL; 252662306a36Sopenharmony_ci } else { 252762306a36Sopenharmony_ci pipe->next_tx_frame += pipe->interval; 252862306a36Sopenharmony_ci cvmx_usb_complete(usb, pipe, transaction, 252962306a36Sopenharmony_ci CVMX_USB_STATUS_OK); 253062306a36Sopenharmony_ci } 253162306a36Sopenharmony_ci } else if (!buffer_space_left || 253262306a36Sopenharmony_ci (bytes_in_last_packet < pipe->max_packet)) { 253362306a36Sopenharmony_ci pipe->next_tx_frame += pipe->interval; 253462306a36Sopenharmony_ci cvmx_usb_complete(usb, pipe, transaction, CVMX_USB_STATUS_OK); 253562306a36Sopenharmony_ci } 253662306a36Sopenharmony_ci} 253762306a36Sopenharmony_ci 253862306a36Sopenharmony_cistatic void cvmx_usb_transfer_isoc(struct octeon_hcd *usb, 253962306a36Sopenharmony_ci struct cvmx_usb_pipe *pipe, 254062306a36Sopenharmony_ci struct cvmx_usb_transaction *transaction, 254162306a36Sopenharmony_ci int buffer_space_left, 254262306a36Sopenharmony_ci int bytes_in_last_packet, 254362306a36Sopenharmony_ci int bytes_this_transfer) 254462306a36Sopenharmony_ci{ 254562306a36Sopenharmony_ci if (cvmx_usb_pipe_needs_split(usb, pipe)) { 254662306a36Sopenharmony_ci /* 254762306a36Sopenharmony_ci * ISOCHRONOUS OUT splits don't require a complete split stage. 254862306a36Sopenharmony_ci * Instead they use a sequence of begin OUT splits to transfer 254962306a36Sopenharmony_ci * the data 188 bytes at a time. Once the transfer is complete, 255062306a36Sopenharmony_ci * the pipe sleeps until the next schedule interval. 255162306a36Sopenharmony_ci */ 255262306a36Sopenharmony_ci if (pipe->transfer_dir == CVMX_USB_DIRECTION_OUT) { 255362306a36Sopenharmony_ci /* 255462306a36Sopenharmony_ci * If no space left or this wasn't a max size packet 255562306a36Sopenharmony_ci * then this transfer is complete. Otherwise start it 255662306a36Sopenharmony_ci * again to send the next 188 bytes 255762306a36Sopenharmony_ci */ 255862306a36Sopenharmony_ci if (!buffer_space_left || (bytes_this_transfer < 188)) { 255962306a36Sopenharmony_ci pipe->next_tx_frame += pipe->interval; 256062306a36Sopenharmony_ci cvmx_usb_complete(usb, pipe, transaction, 256162306a36Sopenharmony_ci CVMX_USB_STATUS_OK); 256262306a36Sopenharmony_ci } 256362306a36Sopenharmony_ci return; 256462306a36Sopenharmony_ci } 256562306a36Sopenharmony_ci if (transaction->stage == 256662306a36Sopenharmony_ci CVMX_USB_STAGE_NON_CONTROL_SPLIT_COMPLETE) { 256762306a36Sopenharmony_ci /* 256862306a36Sopenharmony_ci * We are in the incoming data phase. Keep getting data 256962306a36Sopenharmony_ci * until we run out of space or get a small packet 257062306a36Sopenharmony_ci */ 257162306a36Sopenharmony_ci if ((buffer_space_left == 0) || 257262306a36Sopenharmony_ci (bytes_in_last_packet < pipe->max_packet)) { 257362306a36Sopenharmony_ci pipe->next_tx_frame += pipe->interval; 257462306a36Sopenharmony_ci cvmx_usb_complete(usb, pipe, transaction, 257562306a36Sopenharmony_ci CVMX_USB_STATUS_OK); 257662306a36Sopenharmony_ci } 257762306a36Sopenharmony_ci } else { 257862306a36Sopenharmony_ci transaction->stage = 257962306a36Sopenharmony_ci CVMX_USB_STAGE_NON_CONTROL_SPLIT_COMPLETE; 258062306a36Sopenharmony_ci } 258162306a36Sopenharmony_ci } else { 258262306a36Sopenharmony_ci pipe->next_tx_frame += pipe->interval; 258362306a36Sopenharmony_ci cvmx_usb_complete(usb, pipe, transaction, CVMX_USB_STATUS_OK); 258462306a36Sopenharmony_ci } 258562306a36Sopenharmony_ci} 258662306a36Sopenharmony_ci 258762306a36Sopenharmony_ci/** 258862306a36Sopenharmony_ci * Poll a channel for status 258962306a36Sopenharmony_ci * 259062306a36Sopenharmony_ci * @usb: USB device 259162306a36Sopenharmony_ci * @channel: Channel to poll 259262306a36Sopenharmony_ci * 259362306a36Sopenharmony_ci * Returns: Zero on success 259462306a36Sopenharmony_ci */ 259562306a36Sopenharmony_cistatic int cvmx_usb_poll_channel(struct octeon_hcd *usb, int channel) 259662306a36Sopenharmony_ci{ 259762306a36Sopenharmony_ci struct usb_hcd *hcd = octeon_to_hcd(usb); 259862306a36Sopenharmony_ci struct device *dev = hcd->self.controller; 259962306a36Sopenharmony_ci union cvmx_usbcx_hcintx usbc_hcint; 260062306a36Sopenharmony_ci union cvmx_usbcx_hctsizx usbc_hctsiz; 260162306a36Sopenharmony_ci union cvmx_usbcx_hccharx usbc_hcchar; 260262306a36Sopenharmony_ci struct cvmx_usb_pipe *pipe; 260362306a36Sopenharmony_ci struct cvmx_usb_transaction *transaction; 260462306a36Sopenharmony_ci int bytes_this_transfer; 260562306a36Sopenharmony_ci int bytes_in_last_packet; 260662306a36Sopenharmony_ci int packets_processed; 260762306a36Sopenharmony_ci int buffer_space_left; 260862306a36Sopenharmony_ci 260962306a36Sopenharmony_ci /* Read the interrupt status bits for the channel */ 261062306a36Sopenharmony_ci usbc_hcint.u32 = cvmx_usb_read_csr32(usb, 261162306a36Sopenharmony_ci CVMX_USBCX_HCINTX(channel, usb->index)); 261262306a36Sopenharmony_ci 261362306a36Sopenharmony_ci if (usb->init_flags & CVMX_USB_INITIALIZE_FLAGS_NO_DMA) { 261462306a36Sopenharmony_ci usbc_hcchar.u32 = cvmx_usb_read_csr32(usb, 261562306a36Sopenharmony_ci CVMX_USBCX_HCCHARX(channel, 261662306a36Sopenharmony_ci usb->index)); 261762306a36Sopenharmony_ci 261862306a36Sopenharmony_ci if (usbc_hcchar.s.chena && usbc_hcchar.s.chdis) { 261962306a36Sopenharmony_ci /* 262062306a36Sopenharmony_ci * There seems to be a bug in CN31XX which can cause 262162306a36Sopenharmony_ci * interrupt IN transfers to get stuck until we do a 262262306a36Sopenharmony_ci * write of HCCHARX without changing things 262362306a36Sopenharmony_ci */ 262462306a36Sopenharmony_ci cvmx_usb_write_csr32(usb, 262562306a36Sopenharmony_ci CVMX_USBCX_HCCHARX(channel, 262662306a36Sopenharmony_ci usb->index), 262762306a36Sopenharmony_ci usbc_hcchar.u32); 262862306a36Sopenharmony_ci return 0; 262962306a36Sopenharmony_ci } 263062306a36Sopenharmony_ci 263162306a36Sopenharmony_ci /* 263262306a36Sopenharmony_ci * In non DMA mode the channels don't halt themselves. We need 263362306a36Sopenharmony_ci * to manually disable channels that are left running 263462306a36Sopenharmony_ci */ 263562306a36Sopenharmony_ci if (!usbc_hcint.s.chhltd) { 263662306a36Sopenharmony_ci if (usbc_hcchar.s.chena) { 263762306a36Sopenharmony_ci union cvmx_usbcx_hcintmskx hcintmsk; 263862306a36Sopenharmony_ci /* Disable all interrupts except CHHLTD */ 263962306a36Sopenharmony_ci hcintmsk.u32 = 0; 264062306a36Sopenharmony_ci hcintmsk.s.chhltdmsk = 1; 264162306a36Sopenharmony_ci cvmx_usb_write_csr32(usb, 264262306a36Sopenharmony_ci CVMX_USBCX_HCINTMSKX(channel, usb->index), 264362306a36Sopenharmony_ci hcintmsk.u32); 264462306a36Sopenharmony_ci usbc_hcchar.s.chdis = 1; 264562306a36Sopenharmony_ci cvmx_usb_write_csr32(usb, 264662306a36Sopenharmony_ci CVMX_USBCX_HCCHARX(channel, usb->index), 264762306a36Sopenharmony_ci usbc_hcchar.u32); 264862306a36Sopenharmony_ci return 0; 264962306a36Sopenharmony_ci } else if (usbc_hcint.s.xfercompl) { 265062306a36Sopenharmony_ci /* 265162306a36Sopenharmony_ci * Successful IN/OUT with transfer complete. 265262306a36Sopenharmony_ci * Channel halt isn't needed. 265362306a36Sopenharmony_ci */ 265462306a36Sopenharmony_ci } else { 265562306a36Sopenharmony_ci dev_err(dev, "USB%d: Channel %d interrupt without halt\n", 265662306a36Sopenharmony_ci usb->index, channel); 265762306a36Sopenharmony_ci return 0; 265862306a36Sopenharmony_ci } 265962306a36Sopenharmony_ci } 266062306a36Sopenharmony_ci } else { 266162306a36Sopenharmony_ci /* 266262306a36Sopenharmony_ci * There is are no interrupts that we need to process when the 266362306a36Sopenharmony_ci * channel is still running 266462306a36Sopenharmony_ci */ 266562306a36Sopenharmony_ci if (!usbc_hcint.s.chhltd) 266662306a36Sopenharmony_ci return 0; 266762306a36Sopenharmony_ci } 266862306a36Sopenharmony_ci 266962306a36Sopenharmony_ci /* Disable the channel interrupts now that it is done */ 267062306a36Sopenharmony_ci cvmx_usb_write_csr32(usb, CVMX_USBCX_HCINTMSKX(channel, usb->index), 0); 267162306a36Sopenharmony_ci usb->idle_hardware_channels |= (1 << channel); 267262306a36Sopenharmony_ci 267362306a36Sopenharmony_ci /* Make sure this channel is tied to a valid pipe */ 267462306a36Sopenharmony_ci pipe = usb->pipe_for_channel[channel]; 267562306a36Sopenharmony_ci prefetch(pipe); 267662306a36Sopenharmony_ci if (!pipe) 267762306a36Sopenharmony_ci return 0; 267862306a36Sopenharmony_ci transaction = list_first_entry(&pipe->transactions, 267962306a36Sopenharmony_ci typeof(*transaction), 268062306a36Sopenharmony_ci node); 268162306a36Sopenharmony_ci prefetch(transaction); 268262306a36Sopenharmony_ci 268362306a36Sopenharmony_ci /* 268462306a36Sopenharmony_ci * Disconnect this pipe from the HW channel. Later the schedule 268562306a36Sopenharmony_ci * function will figure out which pipe needs to go 268662306a36Sopenharmony_ci */ 268762306a36Sopenharmony_ci usb->pipe_for_channel[channel] = NULL; 268862306a36Sopenharmony_ci pipe->flags &= ~CVMX_USB_PIPE_FLAGS_SCHEDULED; 268962306a36Sopenharmony_ci 269062306a36Sopenharmony_ci /* 269162306a36Sopenharmony_ci * Read the channel config info so we can figure out how much data 269262306a36Sopenharmony_ci * transferred 269362306a36Sopenharmony_ci */ 269462306a36Sopenharmony_ci usbc_hcchar.u32 = cvmx_usb_read_csr32(usb, 269562306a36Sopenharmony_ci CVMX_USBCX_HCCHARX(channel, usb->index)); 269662306a36Sopenharmony_ci usbc_hctsiz.u32 = cvmx_usb_read_csr32(usb, 269762306a36Sopenharmony_ci CVMX_USBCX_HCTSIZX(channel, usb->index)); 269862306a36Sopenharmony_ci 269962306a36Sopenharmony_ci /* 270062306a36Sopenharmony_ci * Calculating the number of bytes successfully transferred is dependent 270162306a36Sopenharmony_ci * on the transfer direction 270262306a36Sopenharmony_ci */ 270362306a36Sopenharmony_ci packets_processed = transaction->pktcnt - usbc_hctsiz.s.pktcnt; 270462306a36Sopenharmony_ci if (usbc_hcchar.s.epdir) { 270562306a36Sopenharmony_ci /* 270662306a36Sopenharmony_ci * IN transactions are easy. For every byte received the 270762306a36Sopenharmony_ci * hardware decrements xfersize. All we need to do is subtract 270862306a36Sopenharmony_ci * the current value of xfersize from its starting value and we 270962306a36Sopenharmony_ci * know how many bytes were written to the buffer 271062306a36Sopenharmony_ci */ 271162306a36Sopenharmony_ci bytes_this_transfer = transaction->xfersize - 271262306a36Sopenharmony_ci usbc_hctsiz.s.xfersize; 271362306a36Sopenharmony_ci } else { 271462306a36Sopenharmony_ci /* 271562306a36Sopenharmony_ci * OUT transaction don't decrement xfersize. Instead pktcnt is 271662306a36Sopenharmony_ci * decremented on every successful packet send. The hardware 271762306a36Sopenharmony_ci * does this when it receives an ACK, or NYET. If it doesn't 271862306a36Sopenharmony_ci * receive one of these responses pktcnt doesn't change 271962306a36Sopenharmony_ci */ 272062306a36Sopenharmony_ci bytes_this_transfer = packets_processed * usbc_hcchar.s.mps; 272162306a36Sopenharmony_ci /* 272262306a36Sopenharmony_ci * The last packet may not be a full transfer if we didn't have 272362306a36Sopenharmony_ci * enough data 272462306a36Sopenharmony_ci */ 272562306a36Sopenharmony_ci if (bytes_this_transfer > transaction->xfersize) 272662306a36Sopenharmony_ci bytes_this_transfer = transaction->xfersize; 272762306a36Sopenharmony_ci } 272862306a36Sopenharmony_ci /* Figure out how many bytes were in the last packet of the transfer */ 272962306a36Sopenharmony_ci if (packets_processed) 273062306a36Sopenharmony_ci bytes_in_last_packet = bytes_this_transfer - 273162306a36Sopenharmony_ci (packets_processed - 1) * usbc_hcchar.s.mps; 273262306a36Sopenharmony_ci else 273362306a36Sopenharmony_ci bytes_in_last_packet = bytes_this_transfer; 273462306a36Sopenharmony_ci 273562306a36Sopenharmony_ci /* 273662306a36Sopenharmony_ci * As a special case, setup transactions output the setup header, not 273762306a36Sopenharmony_ci * the user's data. For this reason we don't count setup data as bytes 273862306a36Sopenharmony_ci * transferred 273962306a36Sopenharmony_ci */ 274062306a36Sopenharmony_ci if ((transaction->stage == CVMX_USB_STAGE_SETUP) || 274162306a36Sopenharmony_ci (transaction->stage == CVMX_USB_STAGE_SETUP_SPLIT_COMPLETE)) 274262306a36Sopenharmony_ci bytes_this_transfer = 0; 274362306a36Sopenharmony_ci 274462306a36Sopenharmony_ci /* 274562306a36Sopenharmony_ci * Add the bytes transferred to the running total. It is important that 274662306a36Sopenharmony_ci * bytes_this_transfer doesn't count any data that needs to be 274762306a36Sopenharmony_ci * retransmitted 274862306a36Sopenharmony_ci */ 274962306a36Sopenharmony_ci transaction->actual_bytes += bytes_this_transfer; 275062306a36Sopenharmony_ci if (transaction->type == CVMX_USB_TRANSFER_ISOCHRONOUS) 275162306a36Sopenharmony_ci buffer_space_left = transaction->iso_packets[0].length - 275262306a36Sopenharmony_ci transaction->actual_bytes; 275362306a36Sopenharmony_ci else 275462306a36Sopenharmony_ci buffer_space_left = transaction->buffer_length - 275562306a36Sopenharmony_ci transaction->actual_bytes; 275662306a36Sopenharmony_ci 275762306a36Sopenharmony_ci /* 275862306a36Sopenharmony_ci * We need to remember the PID toggle state for the next transaction. 275962306a36Sopenharmony_ci * The hardware already updated it for the next transaction 276062306a36Sopenharmony_ci */ 276162306a36Sopenharmony_ci pipe->pid_toggle = !(usbc_hctsiz.s.pid == 0); 276262306a36Sopenharmony_ci 276362306a36Sopenharmony_ci /* 276462306a36Sopenharmony_ci * For high speed bulk out, assume the next transaction will need to do 276562306a36Sopenharmony_ci * a ping before proceeding. If this isn't true the ACK processing below 276662306a36Sopenharmony_ci * will clear this flag 276762306a36Sopenharmony_ci */ 276862306a36Sopenharmony_ci if ((pipe->device_speed == CVMX_USB_SPEED_HIGH) && 276962306a36Sopenharmony_ci (pipe->transfer_type == CVMX_USB_TRANSFER_BULK) && 277062306a36Sopenharmony_ci (pipe->transfer_dir == CVMX_USB_DIRECTION_OUT)) 277162306a36Sopenharmony_ci pipe->flags |= CVMX_USB_PIPE_FLAGS_NEED_PING; 277262306a36Sopenharmony_ci 277362306a36Sopenharmony_ci if (WARN_ON_ONCE(bytes_this_transfer < 0)) { 277462306a36Sopenharmony_ci /* 277562306a36Sopenharmony_ci * In some rare cases the DMA engine seems to get stuck and 277662306a36Sopenharmony_ci * keeps substracting same byte count over and over again. In 277762306a36Sopenharmony_ci * such case we just need to fail every transaction. 277862306a36Sopenharmony_ci */ 277962306a36Sopenharmony_ci cvmx_usb_complete(usb, pipe, transaction, 278062306a36Sopenharmony_ci CVMX_USB_STATUS_ERROR); 278162306a36Sopenharmony_ci return 0; 278262306a36Sopenharmony_ci } 278362306a36Sopenharmony_ci 278462306a36Sopenharmony_ci if (usbc_hcint.s.stall) { 278562306a36Sopenharmony_ci /* 278662306a36Sopenharmony_ci * STALL as a response means this transaction cannot be 278762306a36Sopenharmony_ci * completed because the device can't process transactions. Tell 278862306a36Sopenharmony_ci * the user. Any data that was transferred will be counted on 278962306a36Sopenharmony_ci * the actual bytes transferred 279062306a36Sopenharmony_ci */ 279162306a36Sopenharmony_ci pipe->pid_toggle = 0; 279262306a36Sopenharmony_ci cvmx_usb_complete(usb, pipe, transaction, 279362306a36Sopenharmony_ci CVMX_USB_STATUS_STALL); 279462306a36Sopenharmony_ci } else if (usbc_hcint.s.xacterr) { 279562306a36Sopenharmony_ci /* 279662306a36Sopenharmony_ci * XactErr as a response means the device signaled 279762306a36Sopenharmony_ci * something wrong with the transfer. For example, PID 279862306a36Sopenharmony_ci * toggle errors cause these. 279962306a36Sopenharmony_ci */ 280062306a36Sopenharmony_ci cvmx_usb_complete(usb, pipe, transaction, 280162306a36Sopenharmony_ci CVMX_USB_STATUS_XACTERR); 280262306a36Sopenharmony_ci } else if (usbc_hcint.s.bblerr) { 280362306a36Sopenharmony_ci /* Babble Error (BblErr) */ 280462306a36Sopenharmony_ci cvmx_usb_complete(usb, pipe, transaction, 280562306a36Sopenharmony_ci CVMX_USB_STATUS_BABBLEERR); 280662306a36Sopenharmony_ci } else if (usbc_hcint.s.datatglerr) { 280762306a36Sopenharmony_ci /* Data toggle error */ 280862306a36Sopenharmony_ci cvmx_usb_complete(usb, pipe, transaction, 280962306a36Sopenharmony_ci CVMX_USB_STATUS_DATATGLERR); 281062306a36Sopenharmony_ci } else if (usbc_hcint.s.nyet) { 281162306a36Sopenharmony_ci /* 281262306a36Sopenharmony_ci * NYET as a response is only allowed in three cases: as a 281362306a36Sopenharmony_ci * response to a ping, as a response to a split transaction, and 281462306a36Sopenharmony_ci * as a response to a bulk out. The ping case is handled by 281562306a36Sopenharmony_ci * hardware, so we only have splits and bulk out 281662306a36Sopenharmony_ci */ 281762306a36Sopenharmony_ci if (!cvmx_usb_pipe_needs_split(usb, pipe)) { 281862306a36Sopenharmony_ci transaction->retries = 0; 281962306a36Sopenharmony_ci /* 282062306a36Sopenharmony_ci * If there is more data to go then we need to try 282162306a36Sopenharmony_ci * again. Otherwise this transaction is complete 282262306a36Sopenharmony_ci */ 282362306a36Sopenharmony_ci if ((buffer_space_left == 0) || 282462306a36Sopenharmony_ci (bytes_in_last_packet < pipe->max_packet)) 282562306a36Sopenharmony_ci cvmx_usb_complete(usb, pipe, 282662306a36Sopenharmony_ci transaction, 282762306a36Sopenharmony_ci CVMX_USB_STATUS_OK); 282862306a36Sopenharmony_ci } else { 282962306a36Sopenharmony_ci /* 283062306a36Sopenharmony_ci * Split transactions retry the split complete 4 times 283162306a36Sopenharmony_ci * then rewind to the start split and do the entire 283262306a36Sopenharmony_ci * transactions again 283362306a36Sopenharmony_ci */ 283462306a36Sopenharmony_ci transaction->retries++; 283562306a36Sopenharmony_ci if ((transaction->retries & 0x3) == 0) { 283662306a36Sopenharmony_ci /* 283762306a36Sopenharmony_ci * Rewind to the beginning of the transaction by 283862306a36Sopenharmony_ci * anding off the split complete bit 283962306a36Sopenharmony_ci */ 284062306a36Sopenharmony_ci transaction->stage &= ~1; 284162306a36Sopenharmony_ci pipe->split_sc_frame = -1; 284262306a36Sopenharmony_ci } 284362306a36Sopenharmony_ci } 284462306a36Sopenharmony_ci } else if (usbc_hcint.s.ack) { 284562306a36Sopenharmony_ci transaction->retries = 0; 284662306a36Sopenharmony_ci /* 284762306a36Sopenharmony_ci * The ACK bit can only be checked after the other error bits. 284862306a36Sopenharmony_ci * This is because a multi packet transfer may succeed in a 284962306a36Sopenharmony_ci * number of packets and then get a different response on the 285062306a36Sopenharmony_ci * last packet. In this case both ACK and the last response bit 285162306a36Sopenharmony_ci * will be set. If none of the other response bits is set, then 285262306a36Sopenharmony_ci * the last packet must have been an ACK 285362306a36Sopenharmony_ci * 285462306a36Sopenharmony_ci * Since we got an ACK, we know we don't need to do a ping on 285562306a36Sopenharmony_ci * this pipe 285662306a36Sopenharmony_ci */ 285762306a36Sopenharmony_ci pipe->flags &= ~CVMX_USB_PIPE_FLAGS_NEED_PING; 285862306a36Sopenharmony_ci 285962306a36Sopenharmony_ci switch (transaction->type) { 286062306a36Sopenharmony_ci case CVMX_USB_TRANSFER_CONTROL: 286162306a36Sopenharmony_ci cvmx_usb_transfer_control(usb, pipe, transaction, 286262306a36Sopenharmony_ci usbc_hcchar, 286362306a36Sopenharmony_ci buffer_space_left, 286462306a36Sopenharmony_ci bytes_in_last_packet); 286562306a36Sopenharmony_ci break; 286662306a36Sopenharmony_ci case CVMX_USB_TRANSFER_BULK: 286762306a36Sopenharmony_ci cvmx_usb_transfer_bulk(usb, pipe, transaction, 286862306a36Sopenharmony_ci usbc_hcint, buffer_space_left, 286962306a36Sopenharmony_ci bytes_in_last_packet); 287062306a36Sopenharmony_ci break; 287162306a36Sopenharmony_ci case CVMX_USB_TRANSFER_INTERRUPT: 287262306a36Sopenharmony_ci cvmx_usb_transfer_intr(usb, pipe, transaction, 287362306a36Sopenharmony_ci buffer_space_left, 287462306a36Sopenharmony_ci bytes_in_last_packet); 287562306a36Sopenharmony_ci break; 287662306a36Sopenharmony_ci case CVMX_USB_TRANSFER_ISOCHRONOUS: 287762306a36Sopenharmony_ci cvmx_usb_transfer_isoc(usb, pipe, transaction, 287862306a36Sopenharmony_ci buffer_space_left, 287962306a36Sopenharmony_ci bytes_in_last_packet, 288062306a36Sopenharmony_ci bytes_this_transfer); 288162306a36Sopenharmony_ci break; 288262306a36Sopenharmony_ci } 288362306a36Sopenharmony_ci } else if (usbc_hcint.s.nak) { 288462306a36Sopenharmony_ci /* 288562306a36Sopenharmony_ci * If this was a split then clear our split in progress marker. 288662306a36Sopenharmony_ci */ 288762306a36Sopenharmony_ci if (usb->active_split == transaction) 288862306a36Sopenharmony_ci usb->active_split = NULL; 288962306a36Sopenharmony_ci /* 289062306a36Sopenharmony_ci * NAK as a response means the device couldn't accept the 289162306a36Sopenharmony_ci * transaction, but it should be retried in the future. Rewind 289262306a36Sopenharmony_ci * to the beginning of the transaction by anding off the split 289362306a36Sopenharmony_ci * complete bit. Retry in the next interval 289462306a36Sopenharmony_ci */ 289562306a36Sopenharmony_ci transaction->retries = 0; 289662306a36Sopenharmony_ci transaction->stage &= ~1; 289762306a36Sopenharmony_ci pipe->next_tx_frame += pipe->interval; 289862306a36Sopenharmony_ci if (pipe->next_tx_frame < usb->frame_number) 289962306a36Sopenharmony_ci pipe->next_tx_frame = usb->frame_number + 290062306a36Sopenharmony_ci pipe->interval - 290162306a36Sopenharmony_ci (usb->frame_number - pipe->next_tx_frame) % 290262306a36Sopenharmony_ci pipe->interval; 290362306a36Sopenharmony_ci } else { 290462306a36Sopenharmony_ci struct cvmx_usb_port_status port; 290562306a36Sopenharmony_ci 290662306a36Sopenharmony_ci port = cvmx_usb_get_status(usb); 290762306a36Sopenharmony_ci if (port.port_enabled) { 290862306a36Sopenharmony_ci /* We'll retry the exact same transaction again */ 290962306a36Sopenharmony_ci transaction->retries++; 291062306a36Sopenharmony_ci } else { 291162306a36Sopenharmony_ci /* 291262306a36Sopenharmony_ci * We get channel halted interrupts with no result bits 291362306a36Sopenharmony_ci * sets when the cable is unplugged 291462306a36Sopenharmony_ci */ 291562306a36Sopenharmony_ci cvmx_usb_complete(usb, pipe, transaction, 291662306a36Sopenharmony_ci CVMX_USB_STATUS_ERROR); 291762306a36Sopenharmony_ci } 291862306a36Sopenharmony_ci } 291962306a36Sopenharmony_ci return 0; 292062306a36Sopenharmony_ci} 292162306a36Sopenharmony_ci 292262306a36Sopenharmony_cistatic void octeon_usb_port_callback(struct octeon_hcd *usb) 292362306a36Sopenharmony_ci{ 292462306a36Sopenharmony_ci spin_unlock(&usb->lock); 292562306a36Sopenharmony_ci usb_hcd_poll_rh_status(octeon_to_hcd(usb)); 292662306a36Sopenharmony_ci spin_lock(&usb->lock); 292762306a36Sopenharmony_ci} 292862306a36Sopenharmony_ci 292962306a36Sopenharmony_ci/** 293062306a36Sopenharmony_ci * Poll the USB block for status and call all needed callback 293162306a36Sopenharmony_ci * handlers. This function is meant to be called in the interrupt 293262306a36Sopenharmony_ci * handler for the USB controller. It can also be called 293362306a36Sopenharmony_ci * periodically in a loop for non-interrupt based operation. 293462306a36Sopenharmony_ci * 293562306a36Sopenharmony_ci * @usb: USB device state populated by cvmx_usb_initialize(). 293662306a36Sopenharmony_ci * 293762306a36Sopenharmony_ci * Returns: 0 or a negative error code. 293862306a36Sopenharmony_ci */ 293962306a36Sopenharmony_cistatic int cvmx_usb_poll(struct octeon_hcd *usb) 294062306a36Sopenharmony_ci{ 294162306a36Sopenharmony_ci union cvmx_usbcx_hfnum usbc_hfnum; 294262306a36Sopenharmony_ci union cvmx_usbcx_gintsts usbc_gintsts; 294362306a36Sopenharmony_ci 294462306a36Sopenharmony_ci prefetch_range(usb, sizeof(*usb)); 294562306a36Sopenharmony_ci 294662306a36Sopenharmony_ci /* Update the frame counter */ 294762306a36Sopenharmony_ci usbc_hfnum.u32 = cvmx_usb_read_csr32(usb, CVMX_USBCX_HFNUM(usb->index)); 294862306a36Sopenharmony_ci if ((usb->frame_number & 0x3fff) > usbc_hfnum.s.frnum) 294962306a36Sopenharmony_ci usb->frame_number += 0x4000; 295062306a36Sopenharmony_ci usb->frame_number &= ~0x3fffull; 295162306a36Sopenharmony_ci usb->frame_number |= usbc_hfnum.s.frnum; 295262306a36Sopenharmony_ci 295362306a36Sopenharmony_ci /* Read the pending interrupts */ 295462306a36Sopenharmony_ci usbc_gintsts.u32 = cvmx_usb_read_csr32(usb, 295562306a36Sopenharmony_ci CVMX_USBCX_GINTSTS(usb->index)); 295662306a36Sopenharmony_ci 295762306a36Sopenharmony_ci /* Clear the interrupts now that we know about them */ 295862306a36Sopenharmony_ci cvmx_usb_write_csr32(usb, CVMX_USBCX_GINTSTS(usb->index), 295962306a36Sopenharmony_ci usbc_gintsts.u32); 296062306a36Sopenharmony_ci 296162306a36Sopenharmony_ci if (usbc_gintsts.s.rxflvl) { 296262306a36Sopenharmony_ci /* 296362306a36Sopenharmony_ci * RxFIFO Non-Empty (RxFLvl) 296462306a36Sopenharmony_ci * Indicates that there is at least one packet pending to be 296562306a36Sopenharmony_ci * read from the RxFIFO. 296662306a36Sopenharmony_ci * 296762306a36Sopenharmony_ci * In DMA mode this is handled by hardware 296862306a36Sopenharmony_ci */ 296962306a36Sopenharmony_ci if (usb->init_flags & CVMX_USB_INITIALIZE_FLAGS_NO_DMA) 297062306a36Sopenharmony_ci cvmx_usb_poll_rx_fifo(usb); 297162306a36Sopenharmony_ci } 297262306a36Sopenharmony_ci if (usbc_gintsts.s.ptxfemp || usbc_gintsts.s.nptxfemp) { 297362306a36Sopenharmony_ci /* Fill the Tx FIFOs when not in DMA mode */ 297462306a36Sopenharmony_ci if (usb->init_flags & CVMX_USB_INITIALIZE_FLAGS_NO_DMA) 297562306a36Sopenharmony_ci cvmx_usb_poll_tx_fifo(usb); 297662306a36Sopenharmony_ci } 297762306a36Sopenharmony_ci if (usbc_gintsts.s.disconnint || usbc_gintsts.s.prtint) { 297862306a36Sopenharmony_ci union cvmx_usbcx_hprt usbc_hprt; 297962306a36Sopenharmony_ci /* 298062306a36Sopenharmony_ci * Disconnect Detected Interrupt (DisconnInt) 298162306a36Sopenharmony_ci * Asserted when a device disconnect is detected. 298262306a36Sopenharmony_ci * 298362306a36Sopenharmony_ci * Host Port Interrupt (PrtInt) 298462306a36Sopenharmony_ci * The core sets this bit to indicate a change in port status of 298562306a36Sopenharmony_ci * one of the O2P USB core ports in Host mode. The application 298662306a36Sopenharmony_ci * must read the Host Port Control and Status (HPRT) register to 298762306a36Sopenharmony_ci * determine the exact event that caused this interrupt. The 298862306a36Sopenharmony_ci * application must clear the appropriate status bit in the Host 298962306a36Sopenharmony_ci * Port Control and Status register to clear this bit. 299062306a36Sopenharmony_ci * 299162306a36Sopenharmony_ci * Call the user's port callback 299262306a36Sopenharmony_ci */ 299362306a36Sopenharmony_ci octeon_usb_port_callback(usb); 299462306a36Sopenharmony_ci /* Clear the port change bits */ 299562306a36Sopenharmony_ci usbc_hprt.u32 = 299662306a36Sopenharmony_ci cvmx_usb_read_csr32(usb, CVMX_USBCX_HPRT(usb->index)); 299762306a36Sopenharmony_ci usbc_hprt.s.prtena = 0; 299862306a36Sopenharmony_ci cvmx_usb_write_csr32(usb, CVMX_USBCX_HPRT(usb->index), 299962306a36Sopenharmony_ci usbc_hprt.u32); 300062306a36Sopenharmony_ci } 300162306a36Sopenharmony_ci if (usbc_gintsts.s.hchint) { 300262306a36Sopenharmony_ci /* 300362306a36Sopenharmony_ci * Host Channels Interrupt (HChInt) 300462306a36Sopenharmony_ci * The core sets this bit to indicate that an interrupt is 300562306a36Sopenharmony_ci * pending on one of the channels of the core (in Host mode). 300662306a36Sopenharmony_ci * The application must read the Host All Channels Interrupt 300762306a36Sopenharmony_ci * (HAINT) register to determine the exact number of the channel 300862306a36Sopenharmony_ci * on which the interrupt occurred, and then read the 300962306a36Sopenharmony_ci * corresponding Host Channel-n Interrupt (HCINTn) register to 301062306a36Sopenharmony_ci * determine the exact cause of the interrupt. The application 301162306a36Sopenharmony_ci * must clear the appropriate status bit in the HCINTn register 301262306a36Sopenharmony_ci * to clear this bit. 301362306a36Sopenharmony_ci */ 301462306a36Sopenharmony_ci union cvmx_usbcx_haint usbc_haint; 301562306a36Sopenharmony_ci 301662306a36Sopenharmony_ci usbc_haint.u32 = cvmx_usb_read_csr32(usb, 301762306a36Sopenharmony_ci CVMX_USBCX_HAINT(usb->index)); 301862306a36Sopenharmony_ci while (usbc_haint.u32) { 301962306a36Sopenharmony_ci int channel; 302062306a36Sopenharmony_ci 302162306a36Sopenharmony_ci channel = __fls(usbc_haint.u32); 302262306a36Sopenharmony_ci cvmx_usb_poll_channel(usb, channel); 302362306a36Sopenharmony_ci usbc_haint.u32 ^= 1 << channel; 302462306a36Sopenharmony_ci } 302562306a36Sopenharmony_ci } 302662306a36Sopenharmony_ci 302762306a36Sopenharmony_ci cvmx_usb_schedule(usb, usbc_gintsts.s.sof); 302862306a36Sopenharmony_ci 302962306a36Sopenharmony_ci return 0; 303062306a36Sopenharmony_ci} 303162306a36Sopenharmony_ci 303262306a36Sopenharmony_ci/* convert between an HCD pointer and the corresponding struct octeon_hcd */ 303362306a36Sopenharmony_cistatic inline struct octeon_hcd *hcd_to_octeon(struct usb_hcd *hcd) 303462306a36Sopenharmony_ci{ 303562306a36Sopenharmony_ci return (struct octeon_hcd *)(hcd->hcd_priv); 303662306a36Sopenharmony_ci} 303762306a36Sopenharmony_ci 303862306a36Sopenharmony_cistatic irqreturn_t octeon_usb_irq(struct usb_hcd *hcd) 303962306a36Sopenharmony_ci{ 304062306a36Sopenharmony_ci struct octeon_hcd *usb = hcd_to_octeon(hcd); 304162306a36Sopenharmony_ci unsigned long flags; 304262306a36Sopenharmony_ci 304362306a36Sopenharmony_ci spin_lock_irqsave(&usb->lock, flags); 304462306a36Sopenharmony_ci cvmx_usb_poll(usb); 304562306a36Sopenharmony_ci spin_unlock_irqrestore(&usb->lock, flags); 304662306a36Sopenharmony_ci return IRQ_HANDLED; 304762306a36Sopenharmony_ci} 304862306a36Sopenharmony_ci 304962306a36Sopenharmony_cistatic int octeon_usb_start(struct usb_hcd *hcd) 305062306a36Sopenharmony_ci{ 305162306a36Sopenharmony_ci hcd->state = HC_STATE_RUNNING; 305262306a36Sopenharmony_ci return 0; 305362306a36Sopenharmony_ci} 305462306a36Sopenharmony_ci 305562306a36Sopenharmony_cistatic void octeon_usb_stop(struct usb_hcd *hcd) 305662306a36Sopenharmony_ci{ 305762306a36Sopenharmony_ci hcd->state = HC_STATE_HALT; 305862306a36Sopenharmony_ci} 305962306a36Sopenharmony_ci 306062306a36Sopenharmony_cistatic int octeon_usb_get_frame_number(struct usb_hcd *hcd) 306162306a36Sopenharmony_ci{ 306262306a36Sopenharmony_ci struct octeon_hcd *usb = hcd_to_octeon(hcd); 306362306a36Sopenharmony_ci 306462306a36Sopenharmony_ci return cvmx_usb_get_frame_number(usb); 306562306a36Sopenharmony_ci} 306662306a36Sopenharmony_ci 306762306a36Sopenharmony_cistatic int octeon_usb_urb_enqueue(struct usb_hcd *hcd, 306862306a36Sopenharmony_ci struct urb *urb, 306962306a36Sopenharmony_ci gfp_t mem_flags) 307062306a36Sopenharmony_ci{ 307162306a36Sopenharmony_ci struct octeon_hcd *usb = hcd_to_octeon(hcd); 307262306a36Sopenharmony_ci struct device *dev = hcd->self.controller; 307362306a36Sopenharmony_ci struct cvmx_usb_transaction *transaction = NULL; 307462306a36Sopenharmony_ci struct cvmx_usb_pipe *pipe; 307562306a36Sopenharmony_ci unsigned long flags; 307662306a36Sopenharmony_ci struct cvmx_usb_iso_packet *iso_packet; 307762306a36Sopenharmony_ci struct usb_host_endpoint *ep = urb->ep; 307862306a36Sopenharmony_ci int rc; 307962306a36Sopenharmony_ci 308062306a36Sopenharmony_ci urb->status = 0; 308162306a36Sopenharmony_ci spin_lock_irqsave(&usb->lock, flags); 308262306a36Sopenharmony_ci 308362306a36Sopenharmony_ci rc = usb_hcd_link_urb_to_ep(hcd, urb); 308462306a36Sopenharmony_ci if (rc) { 308562306a36Sopenharmony_ci spin_unlock_irqrestore(&usb->lock, flags); 308662306a36Sopenharmony_ci return rc; 308762306a36Sopenharmony_ci } 308862306a36Sopenharmony_ci 308962306a36Sopenharmony_ci if (!ep->hcpriv) { 309062306a36Sopenharmony_ci enum cvmx_usb_transfer transfer_type; 309162306a36Sopenharmony_ci enum cvmx_usb_speed speed; 309262306a36Sopenharmony_ci int split_device = 0; 309362306a36Sopenharmony_ci int split_port = 0; 309462306a36Sopenharmony_ci 309562306a36Sopenharmony_ci switch (usb_pipetype(urb->pipe)) { 309662306a36Sopenharmony_ci case PIPE_ISOCHRONOUS: 309762306a36Sopenharmony_ci transfer_type = CVMX_USB_TRANSFER_ISOCHRONOUS; 309862306a36Sopenharmony_ci break; 309962306a36Sopenharmony_ci case PIPE_INTERRUPT: 310062306a36Sopenharmony_ci transfer_type = CVMX_USB_TRANSFER_INTERRUPT; 310162306a36Sopenharmony_ci break; 310262306a36Sopenharmony_ci case PIPE_CONTROL: 310362306a36Sopenharmony_ci transfer_type = CVMX_USB_TRANSFER_CONTROL; 310462306a36Sopenharmony_ci break; 310562306a36Sopenharmony_ci default: 310662306a36Sopenharmony_ci transfer_type = CVMX_USB_TRANSFER_BULK; 310762306a36Sopenharmony_ci break; 310862306a36Sopenharmony_ci } 310962306a36Sopenharmony_ci switch (urb->dev->speed) { 311062306a36Sopenharmony_ci case USB_SPEED_LOW: 311162306a36Sopenharmony_ci speed = CVMX_USB_SPEED_LOW; 311262306a36Sopenharmony_ci break; 311362306a36Sopenharmony_ci case USB_SPEED_FULL: 311462306a36Sopenharmony_ci speed = CVMX_USB_SPEED_FULL; 311562306a36Sopenharmony_ci break; 311662306a36Sopenharmony_ci default: 311762306a36Sopenharmony_ci speed = CVMX_USB_SPEED_HIGH; 311862306a36Sopenharmony_ci break; 311962306a36Sopenharmony_ci } 312062306a36Sopenharmony_ci /* 312162306a36Sopenharmony_ci * For slow devices on high speed ports we need to find the hub 312262306a36Sopenharmony_ci * that does the speed translation so we know where to send the 312362306a36Sopenharmony_ci * split transactions. 312462306a36Sopenharmony_ci */ 312562306a36Sopenharmony_ci if (speed != CVMX_USB_SPEED_HIGH) { 312662306a36Sopenharmony_ci /* 312762306a36Sopenharmony_ci * Start at this device and work our way up the usb 312862306a36Sopenharmony_ci * tree. 312962306a36Sopenharmony_ci */ 313062306a36Sopenharmony_ci struct usb_device *dev = urb->dev; 313162306a36Sopenharmony_ci 313262306a36Sopenharmony_ci while (dev->parent) { 313362306a36Sopenharmony_ci /* 313462306a36Sopenharmony_ci * If our parent is high speed then he'll 313562306a36Sopenharmony_ci * receive the splits. 313662306a36Sopenharmony_ci */ 313762306a36Sopenharmony_ci if (dev->parent->speed == USB_SPEED_HIGH) { 313862306a36Sopenharmony_ci split_device = dev->parent->devnum; 313962306a36Sopenharmony_ci split_port = dev->portnum; 314062306a36Sopenharmony_ci break; 314162306a36Sopenharmony_ci } 314262306a36Sopenharmony_ci /* 314362306a36Sopenharmony_ci * Move up the tree one level. If we make it all 314462306a36Sopenharmony_ci * the way up the tree, then the port must not 314562306a36Sopenharmony_ci * be in high speed mode and we don't need a 314662306a36Sopenharmony_ci * split. 314762306a36Sopenharmony_ci */ 314862306a36Sopenharmony_ci dev = dev->parent; 314962306a36Sopenharmony_ci } 315062306a36Sopenharmony_ci } 315162306a36Sopenharmony_ci pipe = cvmx_usb_open_pipe(usb, usb_pipedevice(urb->pipe), 315262306a36Sopenharmony_ci usb_pipeendpoint(urb->pipe), speed, 315362306a36Sopenharmony_ci le16_to_cpu(ep->desc.wMaxPacketSize) 315462306a36Sopenharmony_ci & 0x7ff, 315562306a36Sopenharmony_ci transfer_type, 315662306a36Sopenharmony_ci usb_pipein(urb->pipe) ? 315762306a36Sopenharmony_ci CVMX_USB_DIRECTION_IN : 315862306a36Sopenharmony_ci CVMX_USB_DIRECTION_OUT, 315962306a36Sopenharmony_ci urb->interval, 316062306a36Sopenharmony_ci (le16_to_cpu(ep->desc.wMaxPacketSize) 316162306a36Sopenharmony_ci >> 11) & 0x3, 316262306a36Sopenharmony_ci split_device, split_port); 316362306a36Sopenharmony_ci if (!pipe) { 316462306a36Sopenharmony_ci usb_hcd_unlink_urb_from_ep(hcd, urb); 316562306a36Sopenharmony_ci spin_unlock_irqrestore(&usb->lock, flags); 316662306a36Sopenharmony_ci dev_dbg(dev, "Failed to create pipe\n"); 316762306a36Sopenharmony_ci return -ENOMEM; 316862306a36Sopenharmony_ci } 316962306a36Sopenharmony_ci ep->hcpriv = pipe; 317062306a36Sopenharmony_ci } else { 317162306a36Sopenharmony_ci pipe = ep->hcpriv; 317262306a36Sopenharmony_ci } 317362306a36Sopenharmony_ci 317462306a36Sopenharmony_ci switch (usb_pipetype(urb->pipe)) { 317562306a36Sopenharmony_ci case PIPE_ISOCHRONOUS: 317662306a36Sopenharmony_ci dev_dbg(dev, "Submit isochronous to %d.%d\n", 317762306a36Sopenharmony_ci usb_pipedevice(urb->pipe), 317862306a36Sopenharmony_ci usb_pipeendpoint(urb->pipe)); 317962306a36Sopenharmony_ci /* 318062306a36Sopenharmony_ci * Allocate a structure to use for our private list of 318162306a36Sopenharmony_ci * isochronous packets. 318262306a36Sopenharmony_ci */ 318362306a36Sopenharmony_ci iso_packet = kmalloc_array(urb->number_of_packets, 318462306a36Sopenharmony_ci sizeof(struct cvmx_usb_iso_packet), 318562306a36Sopenharmony_ci GFP_ATOMIC); 318662306a36Sopenharmony_ci if (iso_packet) { 318762306a36Sopenharmony_ci int i; 318862306a36Sopenharmony_ci /* Fill the list with the data from the URB */ 318962306a36Sopenharmony_ci for (i = 0; i < urb->number_of_packets; i++) { 319062306a36Sopenharmony_ci iso_packet[i].offset = 319162306a36Sopenharmony_ci urb->iso_frame_desc[i].offset; 319262306a36Sopenharmony_ci iso_packet[i].length = 319362306a36Sopenharmony_ci urb->iso_frame_desc[i].length; 319462306a36Sopenharmony_ci iso_packet[i].status = CVMX_USB_STATUS_ERROR; 319562306a36Sopenharmony_ci } 319662306a36Sopenharmony_ci /* 319762306a36Sopenharmony_ci * Store a pointer to the list in the URB setup_packet 319862306a36Sopenharmony_ci * field. We know this currently isn't being used and 319962306a36Sopenharmony_ci * this saves us a bunch of logic. 320062306a36Sopenharmony_ci */ 320162306a36Sopenharmony_ci urb->setup_packet = (char *)iso_packet; 320262306a36Sopenharmony_ci transaction = cvmx_usb_submit_isochronous(usb, 320362306a36Sopenharmony_ci pipe, urb); 320462306a36Sopenharmony_ci /* 320562306a36Sopenharmony_ci * If submit failed we need to free our private packet 320662306a36Sopenharmony_ci * list. 320762306a36Sopenharmony_ci */ 320862306a36Sopenharmony_ci if (!transaction) { 320962306a36Sopenharmony_ci urb->setup_packet = NULL; 321062306a36Sopenharmony_ci kfree(iso_packet); 321162306a36Sopenharmony_ci } 321262306a36Sopenharmony_ci } 321362306a36Sopenharmony_ci break; 321462306a36Sopenharmony_ci case PIPE_INTERRUPT: 321562306a36Sopenharmony_ci dev_dbg(dev, "Submit interrupt to %d.%d\n", 321662306a36Sopenharmony_ci usb_pipedevice(urb->pipe), 321762306a36Sopenharmony_ci usb_pipeendpoint(urb->pipe)); 321862306a36Sopenharmony_ci transaction = cvmx_usb_submit_interrupt(usb, pipe, urb); 321962306a36Sopenharmony_ci break; 322062306a36Sopenharmony_ci case PIPE_CONTROL: 322162306a36Sopenharmony_ci dev_dbg(dev, "Submit control to %d.%d\n", 322262306a36Sopenharmony_ci usb_pipedevice(urb->pipe), 322362306a36Sopenharmony_ci usb_pipeendpoint(urb->pipe)); 322462306a36Sopenharmony_ci transaction = cvmx_usb_submit_control(usb, pipe, urb); 322562306a36Sopenharmony_ci break; 322662306a36Sopenharmony_ci case PIPE_BULK: 322762306a36Sopenharmony_ci dev_dbg(dev, "Submit bulk to %d.%d\n", 322862306a36Sopenharmony_ci usb_pipedevice(urb->pipe), 322962306a36Sopenharmony_ci usb_pipeendpoint(urb->pipe)); 323062306a36Sopenharmony_ci transaction = cvmx_usb_submit_bulk(usb, pipe, urb); 323162306a36Sopenharmony_ci break; 323262306a36Sopenharmony_ci } 323362306a36Sopenharmony_ci if (!transaction) { 323462306a36Sopenharmony_ci usb_hcd_unlink_urb_from_ep(hcd, urb); 323562306a36Sopenharmony_ci spin_unlock_irqrestore(&usb->lock, flags); 323662306a36Sopenharmony_ci dev_dbg(dev, "Failed to submit\n"); 323762306a36Sopenharmony_ci return -ENOMEM; 323862306a36Sopenharmony_ci } 323962306a36Sopenharmony_ci urb->hcpriv = transaction; 324062306a36Sopenharmony_ci spin_unlock_irqrestore(&usb->lock, flags); 324162306a36Sopenharmony_ci return 0; 324262306a36Sopenharmony_ci} 324362306a36Sopenharmony_ci 324462306a36Sopenharmony_cistatic int octeon_usb_urb_dequeue(struct usb_hcd *hcd, 324562306a36Sopenharmony_ci struct urb *urb, 324662306a36Sopenharmony_ci int status) 324762306a36Sopenharmony_ci{ 324862306a36Sopenharmony_ci struct octeon_hcd *usb = hcd_to_octeon(hcd); 324962306a36Sopenharmony_ci unsigned long flags; 325062306a36Sopenharmony_ci int rc; 325162306a36Sopenharmony_ci 325262306a36Sopenharmony_ci if (!urb->dev) 325362306a36Sopenharmony_ci return -EINVAL; 325462306a36Sopenharmony_ci 325562306a36Sopenharmony_ci spin_lock_irqsave(&usb->lock, flags); 325662306a36Sopenharmony_ci 325762306a36Sopenharmony_ci rc = usb_hcd_check_unlink_urb(hcd, urb, status); 325862306a36Sopenharmony_ci if (rc) 325962306a36Sopenharmony_ci goto out; 326062306a36Sopenharmony_ci 326162306a36Sopenharmony_ci urb->status = status; 326262306a36Sopenharmony_ci cvmx_usb_cancel(usb, urb->ep->hcpriv, urb->hcpriv); 326362306a36Sopenharmony_ci 326462306a36Sopenharmony_ciout: 326562306a36Sopenharmony_ci spin_unlock_irqrestore(&usb->lock, flags); 326662306a36Sopenharmony_ci 326762306a36Sopenharmony_ci return rc; 326862306a36Sopenharmony_ci} 326962306a36Sopenharmony_ci 327062306a36Sopenharmony_cistatic void octeon_usb_endpoint_disable(struct usb_hcd *hcd, 327162306a36Sopenharmony_ci struct usb_host_endpoint *ep) 327262306a36Sopenharmony_ci{ 327362306a36Sopenharmony_ci struct device *dev = hcd->self.controller; 327462306a36Sopenharmony_ci 327562306a36Sopenharmony_ci if (ep->hcpriv) { 327662306a36Sopenharmony_ci struct octeon_hcd *usb = hcd_to_octeon(hcd); 327762306a36Sopenharmony_ci struct cvmx_usb_pipe *pipe = ep->hcpriv; 327862306a36Sopenharmony_ci unsigned long flags; 327962306a36Sopenharmony_ci 328062306a36Sopenharmony_ci spin_lock_irqsave(&usb->lock, flags); 328162306a36Sopenharmony_ci cvmx_usb_cancel_all(usb, pipe); 328262306a36Sopenharmony_ci if (cvmx_usb_close_pipe(usb, pipe)) 328362306a36Sopenharmony_ci dev_dbg(dev, "Closing pipe %p failed\n", pipe); 328462306a36Sopenharmony_ci spin_unlock_irqrestore(&usb->lock, flags); 328562306a36Sopenharmony_ci ep->hcpriv = NULL; 328662306a36Sopenharmony_ci } 328762306a36Sopenharmony_ci} 328862306a36Sopenharmony_ci 328962306a36Sopenharmony_cistatic int octeon_usb_hub_status_data(struct usb_hcd *hcd, char *buf) 329062306a36Sopenharmony_ci{ 329162306a36Sopenharmony_ci struct octeon_hcd *usb = hcd_to_octeon(hcd); 329262306a36Sopenharmony_ci struct cvmx_usb_port_status port_status; 329362306a36Sopenharmony_ci unsigned long flags; 329462306a36Sopenharmony_ci 329562306a36Sopenharmony_ci spin_lock_irqsave(&usb->lock, flags); 329662306a36Sopenharmony_ci port_status = cvmx_usb_get_status(usb); 329762306a36Sopenharmony_ci spin_unlock_irqrestore(&usb->lock, flags); 329862306a36Sopenharmony_ci buf[0] = port_status.connect_change << 1; 329962306a36Sopenharmony_ci 330062306a36Sopenharmony_ci return buf[0] != 0; 330162306a36Sopenharmony_ci} 330262306a36Sopenharmony_ci 330362306a36Sopenharmony_cistatic int octeon_usb_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, 330462306a36Sopenharmony_ci u16 wIndex, char *buf, u16 wLength) 330562306a36Sopenharmony_ci{ 330662306a36Sopenharmony_ci struct octeon_hcd *usb = hcd_to_octeon(hcd); 330762306a36Sopenharmony_ci struct device *dev = hcd->self.controller; 330862306a36Sopenharmony_ci struct cvmx_usb_port_status usb_port_status; 330962306a36Sopenharmony_ci int port_status; 331062306a36Sopenharmony_ci struct usb_hub_descriptor *desc; 331162306a36Sopenharmony_ci unsigned long flags; 331262306a36Sopenharmony_ci 331362306a36Sopenharmony_ci switch (typeReq) { 331462306a36Sopenharmony_ci case ClearHubFeature: 331562306a36Sopenharmony_ci dev_dbg(dev, "ClearHubFeature\n"); 331662306a36Sopenharmony_ci switch (wValue) { 331762306a36Sopenharmony_ci case C_HUB_LOCAL_POWER: 331862306a36Sopenharmony_ci case C_HUB_OVER_CURRENT: 331962306a36Sopenharmony_ci /* Nothing required here */ 332062306a36Sopenharmony_ci break; 332162306a36Sopenharmony_ci default: 332262306a36Sopenharmony_ci return -EINVAL; 332362306a36Sopenharmony_ci } 332462306a36Sopenharmony_ci break; 332562306a36Sopenharmony_ci case ClearPortFeature: 332662306a36Sopenharmony_ci dev_dbg(dev, "ClearPortFeature\n"); 332762306a36Sopenharmony_ci if (wIndex != 1) { 332862306a36Sopenharmony_ci dev_dbg(dev, " INVALID\n"); 332962306a36Sopenharmony_ci return -EINVAL; 333062306a36Sopenharmony_ci } 333162306a36Sopenharmony_ci 333262306a36Sopenharmony_ci switch (wValue) { 333362306a36Sopenharmony_ci case USB_PORT_FEAT_ENABLE: 333462306a36Sopenharmony_ci dev_dbg(dev, " ENABLE\n"); 333562306a36Sopenharmony_ci spin_lock_irqsave(&usb->lock, flags); 333662306a36Sopenharmony_ci cvmx_usb_disable(usb); 333762306a36Sopenharmony_ci spin_unlock_irqrestore(&usb->lock, flags); 333862306a36Sopenharmony_ci break; 333962306a36Sopenharmony_ci case USB_PORT_FEAT_SUSPEND: 334062306a36Sopenharmony_ci dev_dbg(dev, " SUSPEND\n"); 334162306a36Sopenharmony_ci /* Not supported on Octeon */ 334262306a36Sopenharmony_ci break; 334362306a36Sopenharmony_ci case USB_PORT_FEAT_POWER: 334462306a36Sopenharmony_ci dev_dbg(dev, " POWER\n"); 334562306a36Sopenharmony_ci /* Not supported on Octeon */ 334662306a36Sopenharmony_ci break; 334762306a36Sopenharmony_ci case USB_PORT_FEAT_INDICATOR: 334862306a36Sopenharmony_ci dev_dbg(dev, " INDICATOR\n"); 334962306a36Sopenharmony_ci /* Port inidicator not supported */ 335062306a36Sopenharmony_ci break; 335162306a36Sopenharmony_ci case USB_PORT_FEAT_C_CONNECTION: 335262306a36Sopenharmony_ci dev_dbg(dev, " C_CONNECTION\n"); 335362306a36Sopenharmony_ci /* Clears drivers internal connect status change flag */ 335462306a36Sopenharmony_ci spin_lock_irqsave(&usb->lock, flags); 335562306a36Sopenharmony_ci usb->port_status = cvmx_usb_get_status(usb); 335662306a36Sopenharmony_ci spin_unlock_irqrestore(&usb->lock, flags); 335762306a36Sopenharmony_ci break; 335862306a36Sopenharmony_ci case USB_PORT_FEAT_C_RESET: 335962306a36Sopenharmony_ci dev_dbg(dev, " C_RESET\n"); 336062306a36Sopenharmony_ci /* 336162306a36Sopenharmony_ci * Clears the driver's internal Port Reset Change flag. 336262306a36Sopenharmony_ci */ 336362306a36Sopenharmony_ci spin_lock_irqsave(&usb->lock, flags); 336462306a36Sopenharmony_ci usb->port_status = cvmx_usb_get_status(usb); 336562306a36Sopenharmony_ci spin_unlock_irqrestore(&usb->lock, flags); 336662306a36Sopenharmony_ci break; 336762306a36Sopenharmony_ci case USB_PORT_FEAT_C_ENABLE: 336862306a36Sopenharmony_ci dev_dbg(dev, " C_ENABLE\n"); 336962306a36Sopenharmony_ci /* 337062306a36Sopenharmony_ci * Clears the driver's internal Port Enable/Disable 337162306a36Sopenharmony_ci * Change flag. 337262306a36Sopenharmony_ci */ 337362306a36Sopenharmony_ci spin_lock_irqsave(&usb->lock, flags); 337462306a36Sopenharmony_ci usb->port_status = cvmx_usb_get_status(usb); 337562306a36Sopenharmony_ci spin_unlock_irqrestore(&usb->lock, flags); 337662306a36Sopenharmony_ci break; 337762306a36Sopenharmony_ci case USB_PORT_FEAT_C_SUSPEND: 337862306a36Sopenharmony_ci dev_dbg(dev, " C_SUSPEND\n"); 337962306a36Sopenharmony_ci /* 338062306a36Sopenharmony_ci * Clears the driver's internal Port Suspend Change 338162306a36Sopenharmony_ci * flag, which is set when resume signaling on the host 338262306a36Sopenharmony_ci * port is complete. 338362306a36Sopenharmony_ci */ 338462306a36Sopenharmony_ci break; 338562306a36Sopenharmony_ci case USB_PORT_FEAT_C_OVER_CURRENT: 338662306a36Sopenharmony_ci dev_dbg(dev, " C_OVER_CURRENT\n"); 338762306a36Sopenharmony_ci /* Clears the driver's overcurrent Change flag */ 338862306a36Sopenharmony_ci spin_lock_irqsave(&usb->lock, flags); 338962306a36Sopenharmony_ci usb->port_status = cvmx_usb_get_status(usb); 339062306a36Sopenharmony_ci spin_unlock_irqrestore(&usb->lock, flags); 339162306a36Sopenharmony_ci break; 339262306a36Sopenharmony_ci default: 339362306a36Sopenharmony_ci dev_dbg(dev, " UNKNOWN\n"); 339462306a36Sopenharmony_ci return -EINVAL; 339562306a36Sopenharmony_ci } 339662306a36Sopenharmony_ci break; 339762306a36Sopenharmony_ci case GetHubDescriptor: 339862306a36Sopenharmony_ci dev_dbg(dev, "GetHubDescriptor\n"); 339962306a36Sopenharmony_ci desc = (struct usb_hub_descriptor *)buf; 340062306a36Sopenharmony_ci desc->bDescLength = 9; 340162306a36Sopenharmony_ci desc->bDescriptorType = 0x29; 340262306a36Sopenharmony_ci desc->bNbrPorts = 1; 340362306a36Sopenharmony_ci desc->wHubCharacteristics = cpu_to_le16(0x08); 340462306a36Sopenharmony_ci desc->bPwrOn2PwrGood = 1; 340562306a36Sopenharmony_ci desc->bHubContrCurrent = 0; 340662306a36Sopenharmony_ci desc->u.hs.DeviceRemovable[0] = 0; 340762306a36Sopenharmony_ci desc->u.hs.DeviceRemovable[1] = 0xff; 340862306a36Sopenharmony_ci break; 340962306a36Sopenharmony_ci case GetHubStatus: 341062306a36Sopenharmony_ci dev_dbg(dev, "GetHubStatus\n"); 341162306a36Sopenharmony_ci *(__le32 *)buf = 0; 341262306a36Sopenharmony_ci break; 341362306a36Sopenharmony_ci case GetPortStatus: 341462306a36Sopenharmony_ci dev_dbg(dev, "GetPortStatus\n"); 341562306a36Sopenharmony_ci if (wIndex != 1) { 341662306a36Sopenharmony_ci dev_dbg(dev, " INVALID\n"); 341762306a36Sopenharmony_ci return -EINVAL; 341862306a36Sopenharmony_ci } 341962306a36Sopenharmony_ci 342062306a36Sopenharmony_ci spin_lock_irqsave(&usb->lock, flags); 342162306a36Sopenharmony_ci usb_port_status = cvmx_usb_get_status(usb); 342262306a36Sopenharmony_ci spin_unlock_irqrestore(&usb->lock, flags); 342362306a36Sopenharmony_ci port_status = 0; 342462306a36Sopenharmony_ci 342562306a36Sopenharmony_ci if (usb_port_status.connect_change) { 342662306a36Sopenharmony_ci port_status |= (1 << USB_PORT_FEAT_C_CONNECTION); 342762306a36Sopenharmony_ci dev_dbg(dev, " C_CONNECTION\n"); 342862306a36Sopenharmony_ci } 342962306a36Sopenharmony_ci 343062306a36Sopenharmony_ci if (usb_port_status.port_enabled) { 343162306a36Sopenharmony_ci port_status |= (1 << USB_PORT_FEAT_C_ENABLE); 343262306a36Sopenharmony_ci dev_dbg(dev, " C_ENABLE\n"); 343362306a36Sopenharmony_ci } 343462306a36Sopenharmony_ci 343562306a36Sopenharmony_ci if (usb_port_status.connected) { 343662306a36Sopenharmony_ci port_status |= (1 << USB_PORT_FEAT_CONNECTION); 343762306a36Sopenharmony_ci dev_dbg(dev, " CONNECTION\n"); 343862306a36Sopenharmony_ci } 343962306a36Sopenharmony_ci 344062306a36Sopenharmony_ci if (usb_port_status.port_enabled) { 344162306a36Sopenharmony_ci port_status |= (1 << USB_PORT_FEAT_ENABLE); 344262306a36Sopenharmony_ci dev_dbg(dev, " ENABLE\n"); 344362306a36Sopenharmony_ci } 344462306a36Sopenharmony_ci 344562306a36Sopenharmony_ci if (usb_port_status.port_over_current) { 344662306a36Sopenharmony_ci port_status |= (1 << USB_PORT_FEAT_OVER_CURRENT); 344762306a36Sopenharmony_ci dev_dbg(dev, " OVER_CURRENT\n"); 344862306a36Sopenharmony_ci } 344962306a36Sopenharmony_ci 345062306a36Sopenharmony_ci if (usb_port_status.port_powered) { 345162306a36Sopenharmony_ci port_status |= (1 << USB_PORT_FEAT_POWER); 345262306a36Sopenharmony_ci dev_dbg(dev, " POWER\n"); 345362306a36Sopenharmony_ci } 345462306a36Sopenharmony_ci 345562306a36Sopenharmony_ci if (usb_port_status.port_speed == CVMX_USB_SPEED_HIGH) { 345662306a36Sopenharmony_ci port_status |= USB_PORT_STAT_HIGH_SPEED; 345762306a36Sopenharmony_ci dev_dbg(dev, " HIGHSPEED\n"); 345862306a36Sopenharmony_ci } else if (usb_port_status.port_speed == CVMX_USB_SPEED_LOW) { 345962306a36Sopenharmony_ci port_status |= (1 << USB_PORT_FEAT_LOWSPEED); 346062306a36Sopenharmony_ci dev_dbg(dev, " LOWSPEED\n"); 346162306a36Sopenharmony_ci } 346262306a36Sopenharmony_ci 346362306a36Sopenharmony_ci *((__le32 *)buf) = cpu_to_le32(port_status); 346462306a36Sopenharmony_ci break; 346562306a36Sopenharmony_ci case SetHubFeature: 346662306a36Sopenharmony_ci dev_dbg(dev, "SetHubFeature\n"); 346762306a36Sopenharmony_ci /* No HUB features supported */ 346862306a36Sopenharmony_ci break; 346962306a36Sopenharmony_ci case SetPortFeature: 347062306a36Sopenharmony_ci dev_dbg(dev, "SetPortFeature\n"); 347162306a36Sopenharmony_ci if (wIndex != 1) { 347262306a36Sopenharmony_ci dev_dbg(dev, " INVALID\n"); 347362306a36Sopenharmony_ci return -EINVAL; 347462306a36Sopenharmony_ci } 347562306a36Sopenharmony_ci 347662306a36Sopenharmony_ci switch (wValue) { 347762306a36Sopenharmony_ci case USB_PORT_FEAT_SUSPEND: 347862306a36Sopenharmony_ci dev_dbg(dev, " SUSPEND\n"); 347962306a36Sopenharmony_ci return -EINVAL; 348062306a36Sopenharmony_ci case USB_PORT_FEAT_POWER: 348162306a36Sopenharmony_ci dev_dbg(dev, " POWER\n"); 348262306a36Sopenharmony_ci /* 348362306a36Sopenharmony_ci * Program the port power bit to drive VBUS on the USB. 348462306a36Sopenharmony_ci */ 348562306a36Sopenharmony_ci spin_lock_irqsave(&usb->lock, flags); 348662306a36Sopenharmony_ci USB_SET_FIELD32(CVMX_USBCX_HPRT(usb->index), 348762306a36Sopenharmony_ci cvmx_usbcx_hprt, prtpwr, 1); 348862306a36Sopenharmony_ci spin_unlock_irqrestore(&usb->lock, flags); 348962306a36Sopenharmony_ci return 0; 349062306a36Sopenharmony_ci case USB_PORT_FEAT_RESET: 349162306a36Sopenharmony_ci dev_dbg(dev, " RESET\n"); 349262306a36Sopenharmony_ci spin_lock_irqsave(&usb->lock, flags); 349362306a36Sopenharmony_ci cvmx_usb_reset_port(usb); 349462306a36Sopenharmony_ci spin_unlock_irqrestore(&usb->lock, flags); 349562306a36Sopenharmony_ci return 0; 349662306a36Sopenharmony_ci case USB_PORT_FEAT_INDICATOR: 349762306a36Sopenharmony_ci dev_dbg(dev, " INDICATOR\n"); 349862306a36Sopenharmony_ci /* Not supported */ 349962306a36Sopenharmony_ci break; 350062306a36Sopenharmony_ci default: 350162306a36Sopenharmony_ci dev_dbg(dev, " UNKNOWN\n"); 350262306a36Sopenharmony_ci return -EINVAL; 350362306a36Sopenharmony_ci } 350462306a36Sopenharmony_ci break; 350562306a36Sopenharmony_ci default: 350662306a36Sopenharmony_ci dev_dbg(dev, "Unknown root hub request\n"); 350762306a36Sopenharmony_ci return -EINVAL; 350862306a36Sopenharmony_ci } 350962306a36Sopenharmony_ci return 0; 351062306a36Sopenharmony_ci} 351162306a36Sopenharmony_ci 351262306a36Sopenharmony_cistatic const struct hc_driver octeon_hc_driver = { 351362306a36Sopenharmony_ci .description = "Octeon USB", 351462306a36Sopenharmony_ci .product_desc = "Octeon Host Controller", 351562306a36Sopenharmony_ci .hcd_priv_size = sizeof(struct octeon_hcd), 351662306a36Sopenharmony_ci .irq = octeon_usb_irq, 351762306a36Sopenharmony_ci .flags = HCD_MEMORY | HCD_DMA | HCD_USB2, 351862306a36Sopenharmony_ci .start = octeon_usb_start, 351962306a36Sopenharmony_ci .stop = octeon_usb_stop, 352062306a36Sopenharmony_ci .urb_enqueue = octeon_usb_urb_enqueue, 352162306a36Sopenharmony_ci .urb_dequeue = octeon_usb_urb_dequeue, 352262306a36Sopenharmony_ci .endpoint_disable = octeon_usb_endpoint_disable, 352362306a36Sopenharmony_ci .get_frame_number = octeon_usb_get_frame_number, 352462306a36Sopenharmony_ci .hub_status_data = octeon_usb_hub_status_data, 352562306a36Sopenharmony_ci .hub_control = octeon_usb_hub_control, 352662306a36Sopenharmony_ci .map_urb_for_dma = octeon_map_urb_for_dma, 352762306a36Sopenharmony_ci .unmap_urb_for_dma = octeon_unmap_urb_for_dma, 352862306a36Sopenharmony_ci}; 352962306a36Sopenharmony_ci 353062306a36Sopenharmony_cistatic int octeon_usb_probe(struct platform_device *pdev) 353162306a36Sopenharmony_ci{ 353262306a36Sopenharmony_ci int status; 353362306a36Sopenharmony_ci int initialize_flags; 353462306a36Sopenharmony_ci int usb_num; 353562306a36Sopenharmony_ci struct resource *res_mem; 353662306a36Sopenharmony_ci struct device_node *usbn_node; 353762306a36Sopenharmony_ci int irq = platform_get_irq(pdev, 0); 353862306a36Sopenharmony_ci struct device *dev = &pdev->dev; 353962306a36Sopenharmony_ci struct octeon_hcd *usb; 354062306a36Sopenharmony_ci struct usb_hcd *hcd; 354162306a36Sopenharmony_ci u32 clock_rate = 48000000; 354262306a36Sopenharmony_ci bool is_crystal_clock = false; 354362306a36Sopenharmony_ci const char *clock_type; 354462306a36Sopenharmony_ci int i; 354562306a36Sopenharmony_ci 354662306a36Sopenharmony_ci if (!dev->of_node) { 354762306a36Sopenharmony_ci dev_err(dev, "Error: empty of_node\n"); 354862306a36Sopenharmony_ci return -ENXIO; 354962306a36Sopenharmony_ci } 355062306a36Sopenharmony_ci usbn_node = dev->of_node->parent; 355162306a36Sopenharmony_ci 355262306a36Sopenharmony_ci i = of_property_read_u32(usbn_node, 355362306a36Sopenharmony_ci "clock-frequency", &clock_rate); 355462306a36Sopenharmony_ci if (i) 355562306a36Sopenharmony_ci i = of_property_read_u32(usbn_node, 355662306a36Sopenharmony_ci "refclk-frequency", &clock_rate); 355762306a36Sopenharmony_ci if (i) { 355862306a36Sopenharmony_ci dev_err(dev, "No USBN \"clock-frequency\"\n"); 355962306a36Sopenharmony_ci return -ENXIO; 356062306a36Sopenharmony_ci } 356162306a36Sopenharmony_ci switch (clock_rate) { 356262306a36Sopenharmony_ci case 12000000: 356362306a36Sopenharmony_ci initialize_flags = CVMX_USB_INITIALIZE_FLAGS_CLOCK_12MHZ; 356462306a36Sopenharmony_ci break; 356562306a36Sopenharmony_ci case 24000000: 356662306a36Sopenharmony_ci initialize_flags = CVMX_USB_INITIALIZE_FLAGS_CLOCK_24MHZ; 356762306a36Sopenharmony_ci break; 356862306a36Sopenharmony_ci case 48000000: 356962306a36Sopenharmony_ci initialize_flags = CVMX_USB_INITIALIZE_FLAGS_CLOCK_48MHZ; 357062306a36Sopenharmony_ci break; 357162306a36Sopenharmony_ci default: 357262306a36Sopenharmony_ci dev_err(dev, "Illegal USBN \"clock-frequency\" %u\n", 357362306a36Sopenharmony_ci clock_rate); 357462306a36Sopenharmony_ci return -ENXIO; 357562306a36Sopenharmony_ci } 357662306a36Sopenharmony_ci 357762306a36Sopenharmony_ci i = of_property_read_string(usbn_node, 357862306a36Sopenharmony_ci "cavium,refclk-type", &clock_type); 357962306a36Sopenharmony_ci if (i) 358062306a36Sopenharmony_ci i = of_property_read_string(usbn_node, 358162306a36Sopenharmony_ci "refclk-type", &clock_type); 358262306a36Sopenharmony_ci 358362306a36Sopenharmony_ci if (!i && strcmp("crystal", clock_type) == 0) 358462306a36Sopenharmony_ci is_crystal_clock = true; 358562306a36Sopenharmony_ci 358662306a36Sopenharmony_ci if (is_crystal_clock) 358762306a36Sopenharmony_ci initialize_flags |= CVMX_USB_INITIALIZE_FLAGS_CLOCK_XO_XI; 358862306a36Sopenharmony_ci else 358962306a36Sopenharmony_ci initialize_flags |= CVMX_USB_INITIALIZE_FLAGS_CLOCK_XO_GND; 359062306a36Sopenharmony_ci 359162306a36Sopenharmony_ci res_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); 359262306a36Sopenharmony_ci if (!res_mem) { 359362306a36Sopenharmony_ci dev_err(dev, "found no memory resource\n"); 359462306a36Sopenharmony_ci return -ENXIO; 359562306a36Sopenharmony_ci } 359662306a36Sopenharmony_ci usb_num = (res_mem->start >> 44) & 1; 359762306a36Sopenharmony_ci 359862306a36Sopenharmony_ci if (irq < 0) { 359962306a36Sopenharmony_ci /* Defective device tree, but we know how to fix it. */ 360062306a36Sopenharmony_ci irq_hw_number_t hwirq = usb_num ? (1 << 6) + 17 : 56; 360162306a36Sopenharmony_ci 360262306a36Sopenharmony_ci irq = irq_create_mapping(NULL, hwirq); 360362306a36Sopenharmony_ci } 360462306a36Sopenharmony_ci 360562306a36Sopenharmony_ci /* 360662306a36Sopenharmony_ci * Set the DMA mask to 64bits so we get buffers already translated for 360762306a36Sopenharmony_ci * DMA. 360862306a36Sopenharmony_ci */ 360962306a36Sopenharmony_ci i = dma_coerce_mask_and_coherent(dev, DMA_BIT_MASK(64)); 361062306a36Sopenharmony_ci if (i) 361162306a36Sopenharmony_ci return i; 361262306a36Sopenharmony_ci 361362306a36Sopenharmony_ci /* 361462306a36Sopenharmony_ci * Only cn52XX and cn56XX have DWC_OTG USB hardware and the 361562306a36Sopenharmony_ci * IOB priority registers. Under heavy network load USB 361662306a36Sopenharmony_ci * hardware can be starved by the IOB causing a crash. Give 361762306a36Sopenharmony_ci * it a priority boost if it has been waiting more than 400 361862306a36Sopenharmony_ci * cycles to avoid this situation. 361962306a36Sopenharmony_ci * 362062306a36Sopenharmony_ci * Testing indicates that a cnt_val of 8192 is not sufficient, 362162306a36Sopenharmony_ci * but no failures are seen with 4096. We choose a value of 362262306a36Sopenharmony_ci * 400 to give a safety factor of 10. 362362306a36Sopenharmony_ci */ 362462306a36Sopenharmony_ci if (OCTEON_IS_MODEL(OCTEON_CN52XX) || OCTEON_IS_MODEL(OCTEON_CN56XX)) { 362562306a36Sopenharmony_ci union cvmx_iob_n2c_l2c_pri_cnt pri_cnt; 362662306a36Sopenharmony_ci 362762306a36Sopenharmony_ci pri_cnt.u64 = 0; 362862306a36Sopenharmony_ci pri_cnt.s.cnt_enb = 1; 362962306a36Sopenharmony_ci pri_cnt.s.cnt_val = 400; 363062306a36Sopenharmony_ci cvmx_write_csr(CVMX_IOB_N2C_L2C_PRI_CNT, pri_cnt.u64); 363162306a36Sopenharmony_ci } 363262306a36Sopenharmony_ci 363362306a36Sopenharmony_ci hcd = usb_create_hcd(&octeon_hc_driver, dev, dev_name(dev)); 363462306a36Sopenharmony_ci if (!hcd) { 363562306a36Sopenharmony_ci dev_dbg(dev, "Failed to allocate memory for HCD\n"); 363662306a36Sopenharmony_ci return -1; 363762306a36Sopenharmony_ci } 363862306a36Sopenharmony_ci hcd->uses_new_polling = 1; 363962306a36Sopenharmony_ci usb = (struct octeon_hcd *)hcd->hcd_priv; 364062306a36Sopenharmony_ci 364162306a36Sopenharmony_ci spin_lock_init(&usb->lock); 364262306a36Sopenharmony_ci 364362306a36Sopenharmony_ci usb->init_flags = initialize_flags; 364462306a36Sopenharmony_ci 364562306a36Sopenharmony_ci /* Initialize the USB state structure */ 364662306a36Sopenharmony_ci usb->index = usb_num; 364762306a36Sopenharmony_ci INIT_LIST_HEAD(&usb->idle_pipes); 364862306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(usb->active_pipes); i++) 364962306a36Sopenharmony_ci INIT_LIST_HEAD(&usb->active_pipes[i]); 365062306a36Sopenharmony_ci 365162306a36Sopenharmony_ci /* Due to an errata, CN31XX doesn't support DMA */ 365262306a36Sopenharmony_ci if (OCTEON_IS_MODEL(OCTEON_CN31XX)) { 365362306a36Sopenharmony_ci usb->init_flags |= CVMX_USB_INITIALIZE_FLAGS_NO_DMA; 365462306a36Sopenharmony_ci /* Only use one channel with non DMA */ 365562306a36Sopenharmony_ci usb->idle_hardware_channels = 0x1; 365662306a36Sopenharmony_ci } else if (OCTEON_IS_MODEL(OCTEON_CN5XXX)) { 365762306a36Sopenharmony_ci /* CN5XXX have an errata with channel 3 */ 365862306a36Sopenharmony_ci usb->idle_hardware_channels = 0xf7; 365962306a36Sopenharmony_ci } else { 366062306a36Sopenharmony_ci usb->idle_hardware_channels = 0xff; 366162306a36Sopenharmony_ci } 366262306a36Sopenharmony_ci 366362306a36Sopenharmony_ci status = cvmx_usb_initialize(dev, usb); 366462306a36Sopenharmony_ci if (status) { 366562306a36Sopenharmony_ci dev_dbg(dev, "USB initialization failed with %d\n", status); 366662306a36Sopenharmony_ci usb_put_hcd(hcd); 366762306a36Sopenharmony_ci return -1; 366862306a36Sopenharmony_ci } 366962306a36Sopenharmony_ci 367062306a36Sopenharmony_ci status = usb_add_hcd(hcd, irq, 0); 367162306a36Sopenharmony_ci if (status) { 367262306a36Sopenharmony_ci dev_dbg(dev, "USB add HCD failed with %d\n", status); 367362306a36Sopenharmony_ci usb_put_hcd(hcd); 367462306a36Sopenharmony_ci return -1; 367562306a36Sopenharmony_ci } 367662306a36Sopenharmony_ci device_wakeup_enable(hcd->self.controller); 367762306a36Sopenharmony_ci 367862306a36Sopenharmony_ci dev_info(dev, "Registered HCD for port %d on irq %d\n", usb_num, irq); 367962306a36Sopenharmony_ci 368062306a36Sopenharmony_ci return 0; 368162306a36Sopenharmony_ci} 368262306a36Sopenharmony_ci 368362306a36Sopenharmony_cistatic void octeon_usb_remove(struct platform_device *pdev) 368462306a36Sopenharmony_ci{ 368562306a36Sopenharmony_ci int status; 368662306a36Sopenharmony_ci struct device *dev = &pdev->dev; 368762306a36Sopenharmony_ci struct usb_hcd *hcd = dev_get_drvdata(dev); 368862306a36Sopenharmony_ci struct octeon_hcd *usb = hcd_to_octeon(hcd); 368962306a36Sopenharmony_ci unsigned long flags; 369062306a36Sopenharmony_ci 369162306a36Sopenharmony_ci usb_remove_hcd(hcd); 369262306a36Sopenharmony_ci spin_lock_irqsave(&usb->lock, flags); 369362306a36Sopenharmony_ci status = cvmx_usb_shutdown(usb); 369462306a36Sopenharmony_ci spin_unlock_irqrestore(&usb->lock, flags); 369562306a36Sopenharmony_ci if (status) 369662306a36Sopenharmony_ci dev_dbg(dev, "USB shutdown failed with %d\n", status); 369762306a36Sopenharmony_ci 369862306a36Sopenharmony_ci usb_put_hcd(hcd); 369962306a36Sopenharmony_ci} 370062306a36Sopenharmony_ci 370162306a36Sopenharmony_cistatic const struct of_device_id octeon_usb_match[] = { 370262306a36Sopenharmony_ci { 370362306a36Sopenharmony_ci .compatible = "cavium,octeon-5750-usbc", 370462306a36Sopenharmony_ci }, 370562306a36Sopenharmony_ci {}, 370662306a36Sopenharmony_ci}; 370762306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, octeon_usb_match); 370862306a36Sopenharmony_ci 370962306a36Sopenharmony_cistatic struct platform_driver octeon_usb_driver = { 371062306a36Sopenharmony_ci .driver = { 371162306a36Sopenharmony_ci .name = "octeon-hcd", 371262306a36Sopenharmony_ci .of_match_table = octeon_usb_match, 371362306a36Sopenharmony_ci }, 371462306a36Sopenharmony_ci .probe = octeon_usb_probe, 371562306a36Sopenharmony_ci .remove_new = octeon_usb_remove, 371662306a36Sopenharmony_ci}; 371762306a36Sopenharmony_ci 371862306a36Sopenharmony_cistatic int __init octeon_usb_driver_init(void) 371962306a36Sopenharmony_ci{ 372062306a36Sopenharmony_ci if (usb_disabled()) 372162306a36Sopenharmony_ci return 0; 372262306a36Sopenharmony_ci 372362306a36Sopenharmony_ci return platform_driver_register(&octeon_usb_driver); 372462306a36Sopenharmony_ci} 372562306a36Sopenharmony_cimodule_init(octeon_usb_driver_init); 372662306a36Sopenharmony_ci 372762306a36Sopenharmony_cistatic void __exit octeon_usb_driver_exit(void) 372862306a36Sopenharmony_ci{ 372962306a36Sopenharmony_ci if (usb_disabled()) 373062306a36Sopenharmony_ci return; 373162306a36Sopenharmony_ci 373262306a36Sopenharmony_ci platform_driver_unregister(&octeon_usb_driver); 373362306a36Sopenharmony_ci} 373462306a36Sopenharmony_cimodule_exit(octeon_usb_driver_exit); 373562306a36Sopenharmony_ci 373662306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 373762306a36Sopenharmony_ciMODULE_AUTHOR("Cavium, Inc. <support@cavium.com>"); 373862306a36Sopenharmony_ciMODULE_DESCRIPTION("Cavium Inc. OCTEON USB Host driver."); 3739