Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit c62d0c7a authored by qctecmdr's avatar qctecmdr Committed by Gerrit - the friendly Code Review server
Browse files

Merge "usb: gadget: Add snapshot of ChipIdea driver"

parents 529e9219 7cd293f5
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -21,6 +21,7 @@ Required properties:
- compatible:   Should contain:
  "qcom,usb-otg-ci" for chipsets with ChipIdea 45nm PHY
  "qcom,usb-otg-snps" for chipsets with Synopsys 28nm PHY
  "qcom,hsusb-otg"  for chipsets with ChipIdea 45nm PHY

- regs:         Offset and length of the register set in the memory map
- interrupts:   interrupt-specifier for the OTG interrupt.
+2 −0
Original line number Diff line number Diff line
@@ -11,3 +11,5 @@ libcomposite-y := usbstring.o config.o epautoconf.o
libcomposite-y			+= composite.o functions.o configfs.o u_f.o

obj-$(CONFIG_USB_GADGET)	+= udc/ function/ legacy/

obj-$(CONFIG_USB_CI13XXX_MSM)	+= ci13xxx_msm.o
+555 −0
Original line number Diff line number Diff line
/* Copyright (c) 2010-2019, The Linux Foundation. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 and
 * only version 2 as published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 */

#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/usb/msm_hsusb_hw.h>
#include <linux/usb/ulpi.h>
#include <linux/gpio.h>
#include <linux/pinctrl/consumer.h>
#include <linux/sched/clock.h>

#include "ci13xxx_udc.c"

#define MSM_USB_BASE	(udc->regs)

#define CI13XXX_MSM_MAX_LOG2_ITC	7

struct ci13xxx_udc_context {
	int irq;
	void __iomem *regs;
	int wake_gpio;
	int wake_irq;
	bool wake_irq_state;
	struct pinctrl *ci13xxx_pinctrl;
	struct timer_list irq_enable_timer;
	bool irq_disabled;
};

static struct ci13xxx_udc_context _udc_ctxt;
#define IRQ_ENABLE_DELAY	(jiffies + msecs_to_jiffies(1000))

static irqreturn_t msm_udc_irq(int irq, void *data)
{
	return udc_irq();
}

static void ci13xxx_msm_suspend(void)
{
	struct device *dev = _udc->gadget.dev.parent;

	dev_dbg(dev, "%s\n", __func__);

	if (_udc_ctxt.wake_irq && !_udc_ctxt.wake_irq_state) {
		enable_irq_wake(_udc_ctxt.wake_irq);
		enable_irq(_udc_ctxt.wake_irq);
		_udc_ctxt.wake_irq_state = true;
	}
}

static void ci13xxx_msm_resume(void)
{
	struct device *dev = _udc->gadget.dev.parent;

	dev_dbg(dev, "%s\n", __func__);

	if (_udc_ctxt.wake_irq && _udc_ctxt.wake_irq_state) {
		disable_irq_wake(_udc_ctxt.wake_irq);
		disable_irq_nosync(_udc_ctxt.wake_irq);
		_udc_ctxt.wake_irq_state = false;
	}
}

static void ci13xxx_msm_disconnect(void)
{
	struct ci13xxx *udc = _udc;
	struct usb_phy *phy = udc->transceiver;

	if (phy && (phy->flags & ENABLE_DP_MANUAL_PULLUP)) {
		usb_phy_io_write(phy,
				ULPI_MISC_A_VBUSVLDEXT |
				ULPI_MISC_A_VBUSVLDEXTSEL,
				ULPI_CLR(ULPI_MISC_A));

		/*
		 * Add memory barrier as it is must to complete
		 * above USB PHY and Link register writes before
		 * moving ahead with USB peripheral mode enumeration,
		 * otherwise USB peripheral mode may not work.
		 */
		mb();
	}
}

/* Link power management will reduce power consumption by
 * short time HW suspend/resume.
 */
static void ci13xxx_msm_set_l1(struct ci13xxx *udc)
{
	int temp;
	struct device *dev = udc->gadget.dev.parent;

	dev_dbg(dev, "Enable link power management\n");

	/* Enable remote wakeup and L1 for IN EPs */
	writel_relaxed(0xffff0000, USB_L1_EP_CTRL);

	temp = readl_relaxed(USB_L1_CONFIG);
	temp |= L1_CONFIG_LPM_EN | L1_CONFIG_REMOTE_WAKEUP |
		L1_CONFIG_GATE_SYS_CLK | L1_CONFIG_PHY_LPM |
		L1_CONFIG_PLL;
	writel_relaxed(temp, USB_L1_CONFIG);
}

static void ci13xxx_msm_connect(void)
{
	struct ci13xxx *udc = _udc;
	struct usb_phy *phy = udc->transceiver;

	if (phy && (phy->flags & ENABLE_DP_MANUAL_PULLUP)) {
		int	temp;

		usb_phy_io_write(phy,
			ULPI_MISC_A_VBUSVLDEXT |
			ULPI_MISC_A_VBUSVLDEXTSEL,
			ULPI_SET(ULPI_MISC_A));

		temp = readl_relaxed(USB_GENCONFIG_2);
		temp |= GENCONFIG_2_SESS_VLD_CTRL_EN;
		writel_relaxed(temp, USB_GENCONFIG_2);

		temp = readl_relaxed(USB_USBCMD);
		temp |= USBCMD_SESS_VLD_CTRL;
		writel_relaxed(temp, USB_USBCMD);

		/*
		 * Add memory barrier as it is must to complete
		 * above USB PHY and Link register writes before
		 * moving ahead with USB peripheral mode enumeration,
		 * otherwise USB peripheral mode may not work.
		 */
		mb();
	}
}

static void ci13xxx_msm_reset(void)
{
	struct ci13xxx *udc = _udc;
	struct usb_phy *phy = udc->transceiver;
	struct device *dev = udc->gadget.dev.parent;
	int	temp;

	writel_relaxed(0, USB_AHBBURST);
	writel_relaxed(0x08, USB_AHBMODE);

	/* workaround for rx buffer collision issue */
	temp = readl_relaxed(USB_GENCONFIG);
	temp &= ~GENCONFIG_TXFIFO_IDLE_FORCE_DISABLE;
	temp &= ~GENCONFIG_ULPI_SERIAL_EN;
	writel_relaxed(temp, USB_GENCONFIG);

	if (udc->gadget.l1_supported)
		ci13xxx_msm_set_l1(udc);

	if (phy && (phy->flags & ENABLE_SECONDARY_PHY)) {
		int	temp;

		dev_dbg(dev, "using secondary hsphy\n");
		temp = readl_relaxed(USB_PHY_CTRL2);
		temp |= (1<<16);
		writel_relaxed(temp, USB_PHY_CTRL2);

		/*
		 * Add memory barrier to make sure above LINK writes are
		 * complete before moving ahead with USB peripheral mode
		 * enumeration.
		 */
		mb();
	}
}

static void ci13xxx_msm_mark_err_event(void)
{
	struct ci13xxx *udc = _udc;
	struct msm_otg *otg;

	if (udc == NULL)
		return;

	if (udc->transceiver == NULL)
		return;

	otg = container_of(udc->transceiver, struct msm_otg, phy);

	/* This will trigger hardware reset before next connection */
	otg->err_event_seen = true;
}

static void ci13xxx_msm_notify_event(struct ci13xxx *udc, unsigned int event)
{
	struct device *dev = udc->gadget.dev.parent;

	switch (event) {
	case CI13XXX_CONTROLLER_RESET_EVENT:
		dev_info(dev, "CI13XXX_CONTROLLER_RESET_EVENT received\n");
		ci13xxx_msm_reset();
		break;
	case CI13XXX_CONTROLLER_DISCONNECT_EVENT:
		dev_info(dev, "CI13XXX_CONTROLLER_DISCONNECT_EVENT received\n");
		ci13xxx_msm_disconnect();
		ci13xxx_msm_resume();
		break;
	case CI13XXX_CONTROLLER_CONNECT_EVENT:
		dev_info(dev, "CI13XXX_CONTROLLER_CONNECT_EVENT received\n");
		ci13xxx_msm_connect();
		break;
	case CI13XXX_CONTROLLER_SUSPEND_EVENT:
		dev_info(dev, "CI13XXX_CONTROLLER_SUSPEND_EVENT received\n");
		ci13xxx_msm_suspend();
		break;
	case CI13XXX_CONTROLLER_RESUME_EVENT:
		dev_info(dev, "CI13XXX_CONTROLLER_RESUME_EVENT received\n");
		ci13xxx_msm_resume();
		break;
	case CI13XXX_CONTROLLER_ERROR_EVENT:
		dev_info(dev, "CI13XXX_CONTROLLER_ERROR_EVENT received\n");
		ci13xxx_msm_mark_err_event();
		break;
	case CI13XXX_CONTROLLER_UDC_STARTED_EVENT:
		dev_info(dev,
			 "CI13XXX_CONTROLLER_UDC_STARTED_EVENT received\n");
		break;
	default:
		dev_dbg(dev, "unknown ci13xxx_udc event\n");
		break;
	}
}

static bool ci13xxx_msm_in_lpm(struct ci13xxx *udc)
{
	struct msm_otg *otg;

	if (udc == NULL)
		return false;

	if (udc->transceiver == NULL)
		return false;

	otg = container_of(udc->transceiver, struct msm_otg, phy);

	return (atomic_read(&otg->in_lpm) != 0);
}


static irqreturn_t ci13xxx_msm_resume_irq(int irq, void *data)
{
	struct ci13xxx *udc = _udc;

	if (udc->transceiver && udc->vbus_active && udc->suspended)
		usb_phy_set_suspend(udc->transceiver, 0);
	else if (!udc->suspended)
		ci13xxx_msm_resume();

	return IRQ_HANDLED;
}

static struct ci13xxx_udc_driver ci13xxx_msm_udc_driver = {
	.name			= "ci13xxx_msm",
	.flags			= CI13XXX_REGS_SHARED |
				  CI13XXX_REQUIRE_TRANSCEIVER |
				  CI13XXX_PULLUP_ON_VBUS |
				  CI13XXX_ZERO_ITC |
				  CI13XXX_DISABLE_STREAMING,
	.nz_itc			= 0,
	.notify_event		= ci13xxx_msm_notify_event,
	.in_lpm                 = ci13xxx_msm_in_lpm,
};

static int ci13xxx_msm_install_wake_gpio(struct platform_device *pdev,
				struct resource *res)
{
	int wake_irq;
	int ret;
	struct pinctrl_state *set_state;

	dev_dbg(&pdev->dev, "%s\n", __func__);

	_udc_ctxt.wake_gpio = res->start;
	if (_udc_ctxt.ci13xxx_pinctrl) {
		set_state = pinctrl_lookup_state(_udc_ctxt.ci13xxx_pinctrl,
				"ci13xxx_active");
		if (IS_ERR(set_state)) {
			pr_err("cannot get ci13xxx pinctrl active state\n");
			return PTR_ERR(set_state);
		}
		pinctrl_select_state(_udc_ctxt.ci13xxx_pinctrl, set_state);
	}
	gpio_request(_udc_ctxt.wake_gpio, "USB_RESUME");
	gpio_direction_input(_udc_ctxt.wake_gpio);
	wake_irq = gpio_to_irq(_udc_ctxt.wake_gpio);
	if (wake_irq < 0) {
		dev_err(&pdev->dev, "could not register USB_RESUME GPIO.\n");
		return -ENXIO;
	}

	dev_dbg(&pdev->dev, "_udc_ctxt.gpio_irq = %d and irq = %d\n",
			_udc_ctxt.wake_gpio, wake_irq);
	ret = request_irq(wake_irq, ci13xxx_msm_resume_irq,
		IRQF_TRIGGER_RISING | IRQF_ONESHOT, "usb resume", NULL);
	if (ret < 0) {
		dev_err(&pdev->dev, "could not register USB_RESUME IRQ.\n");
		goto gpio_free;
	}
	disable_irq(wake_irq);
	_udc_ctxt.wake_irq = wake_irq;

	return 0;

gpio_free:
	gpio_free(_udc_ctxt.wake_gpio);
	if (_udc_ctxt.ci13xxx_pinctrl) {
		set_state = pinctrl_lookup_state(_udc_ctxt.ci13xxx_pinctrl,
				"ci13xxx_sleep");
		if (IS_ERR(set_state))
			pr_err("cannot get ci13xxx pinctrl sleep state\n");
		else
			pinctrl_select_state(_udc_ctxt.ci13xxx_pinctrl,
					set_state);
	}
	_udc_ctxt.wake_gpio = 0;
	return ret;
}

static void ci13xxx_msm_uninstall_wake_gpio(struct platform_device *pdev)
{
	struct pinctrl_state *set_state;

	dev_dbg(&pdev->dev, "%s\n", __func__);

	if (_udc_ctxt.wake_gpio) {
		gpio_free(_udc_ctxt.wake_gpio);
		if (_udc_ctxt.ci13xxx_pinctrl) {
			set_state =
				pinctrl_lookup_state(_udc_ctxt.ci13xxx_pinctrl,
						"ci13xxx_sleep");
			if (IS_ERR(set_state))
				pr_err("cannot get ci13xxx pinctrl sleep state\n");
			else
				pinctrl_select_state(_udc_ctxt.ci13xxx_pinctrl,
						set_state);
		}
		_udc_ctxt.wake_gpio = 0;
	}
}

static void enable_usb_irq_timer_func(unsigned long data);
static int ci13xxx_msm_probe(struct platform_device *pdev)
{
	struct resource *res;
	int ret;
	struct ci13xxx_platform_data *pdata = pdev->dev.platform_data;
	bool is_l1_supported = false;

	dev_dbg(&pdev->dev, "%s\n", __func__);

	if (pdata) {
		/* Acceptable values for nz_itc are: 0,1,2,4,8,16,32,64 */
		if (pdata->log2_itc > CI13XXX_MSM_MAX_LOG2_ITC ||
			pdata->log2_itc <= 0)
			ci13xxx_msm_udc_driver.nz_itc = 0;
		else
			ci13xxx_msm_udc_driver.nz_itc =
				1 << (pdata->log2_itc-1);

		is_l1_supported = pdata->l1_supported;
		/* Set ahb2ahb bypass flag if it is requested. */
		if (pdata->enable_ahb2ahb_bypass)
			ci13xxx_msm_udc_driver.flags |=
				CI13XXX_ENABLE_AHB2AHB_BYPASS;

		/* Clear disable streaming flag if is requested. */
		if (pdata->enable_streaming)
			ci13xxx_msm_udc_driver.flags &=
						~CI13XXX_DISABLE_STREAMING;
	}

	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
	if (!res) {
		dev_err(&pdev->dev, "failed to get platform resource mem\n");
		return -ENXIO;
	}

	_udc_ctxt.regs = ioremap(res->start, resource_size(res));
	if (!_udc_ctxt.regs) {
		dev_err(&pdev->dev, "ioremap failed\n");
		return -ENOMEM;
	}

	ret = udc_probe(&ci13xxx_msm_udc_driver, &pdev->dev, _udc_ctxt.regs);
	if (ret < 0) {
		dev_err(&pdev->dev, "udc_probe failed\n");
		goto iounmap;
	}

	_udc->gadget.l1_supported = is_l1_supported;

	_udc_ctxt.irq = platform_get_irq(pdev, 0);
	if (_udc_ctxt.irq < 0) {
		dev_err(&pdev->dev, "IRQ not found\n");
		ret = -ENXIO;
		goto udc_remove;
	}

	res = platform_get_resource_byname(pdev, IORESOURCE_IO, "USB_RESUME");
	/* Get pinctrl if target uses pinctrl */
	_udc_ctxt.ci13xxx_pinctrl = devm_pinctrl_get(&pdev->dev);
	if (IS_ERR(_udc_ctxt.ci13xxx_pinctrl)) {
		if (of_property_read_bool(pdev->dev.of_node, "pinctrl-names")) {
			dev_err(&pdev->dev, "Error encountered while getting pinctrl");
			ret = PTR_ERR(_udc_ctxt.ci13xxx_pinctrl);
			goto udc_remove;
		}
		dev_dbg(&pdev->dev, "Target does not use pinctrl\n");
		_udc_ctxt.ci13xxx_pinctrl = NULL;
	}
	if (res) {
		ret = ci13xxx_msm_install_wake_gpio(pdev, res);
		if (ret < 0) {
			dev_err(&pdev->dev, "gpio irq install failed\n");
			goto udc_remove;
		}
	}

	ret = request_irq(_udc_ctxt.irq, msm_udc_irq, IRQF_SHARED, pdev->name,
					  pdev);
	if (ret < 0) {
		dev_err(&pdev->dev, "request_irq failed\n");
		goto gpio_uninstall;
	}

	setup_timer(&_udc_ctxt.irq_enable_timer, enable_usb_irq_timer_func,
							(unsigned long)NULL);

	pm_runtime_no_callbacks(&pdev->dev);
	pm_runtime_set_active(&pdev->dev);
	pm_runtime_enable(&pdev->dev);

	return 0;

gpio_uninstall:
	ci13xxx_msm_uninstall_wake_gpio(pdev);
udc_remove:
	udc_remove();
iounmap:
	iounmap(_udc_ctxt.regs);

	return ret;
}

int ci13xxx_msm_remove(struct platform_device *pdev)
{
	pm_runtime_disable(&pdev->dev);
	free_irq(_udc_ctxt.irq, pdev);
	ci13xxx_msm_uninstall_wake_gpio(pdev);
	udc_remove();
	iounmap(_udc_ctxt.regs);
	return 0;
}

void ci13xxx_msm_shutdown(struct platform_device *pdev)
{
	ci13xxx_pullup(&_udc->gadget, 0);
}

void msm_hw_soft_reset(void)
{
	struct ci13xxx *udc = _udc;

	hw_device_reset(udc);
}

void msm_hw_bam_disable(bool bam_disable)
{
	u32 val;
	struct ci13xxx *udc = _udc;

	if (bam_disable)
		val = readl_relaxed(USB_GENCONFIG) | GENCONFIG_BAM_DISABLE;
	else
		val = readl_relaxed(USB_GENCONFIG) & ~GENCONFIG_BAM_DISABLE;

	writel_relaxed(val, USB_GENCONFIG);
}

void msm_usb_irq_disable(bool disable)
{
	struct ci13xxx *udc = _udc;
	unsigned long flags;

	spin_lock_irqsave(udc->lock, flags);

	if (_udc_ctxt.irq_disabled == disable) {
		pr_debug("Interrupt state already disable = %d\n", disable);
		if (disable)
			mod_timer(&_udc_ctxt.irq_enable_timer,
					IRQ_ENABLE_DELAY);
		spin_unlock_irqrestore(udc->lock, flags);
		return;
	}

	if (disable) {
		disable_irq_nosync(_udc_ctxt.irq);
		/* start timer here */
		pr_debug("%s: Disabling interrupts\n", __func__);
		mod_timer(&_udc_ctxt.irq_enable_timer, IRQ_ENABLE_DELAY);
		_udc_ctxt.irq_disabled = true;

	} else {
		pr_debug("%s: Enabling interrupts\n", __func__);
		del_timer(&_udc_ctxt.irq_enable_timer);
		enable_irq(_udc_ctxt.irq);
		_udc_ctxt.irq_disabled = false;
	}

	spin_unlock_irqrestore(udc->lock, flags);
}

static void enable_usb_irq_timer_func(unsigned long data)
{
	pr_debug("enabling interrupt from timer\n");
	msm_usb_irq_disable(false);
}

static struct platform_driver ci13xxx_msm_driver = {
	.probe = ci13xxx_msm_probe,
	.driver = {
		.name = "msm_hsusb",
	},
	.remove = ci13xxx_msm_remove,
	.shutdown = ci13xxx_msm_shutdown,
};
MODULE_ALIAS("platform:msm_hsusb");

static int __init ci13xxx_msm_init(void)
{
	return platform_driver_register(&ci13xxx_msm_driver);
}
module_init(ci13xxx_msm_init);

static void __exit ci13xxx_msm_exit(void)
{
	platform_driver_unregister(&ci13xxx_msm_driver);
}
module_exit(ci13xxx_msm_exit);

MODULE_LICENSE("GPL v2");
+4004 −0

File added.

Preview size limit exceeded, changes collapsed.

+282 −0
Original line number Diff line number Diff line
/*
 * ci13xxx_udc.h - structures, registers, and macros MIPS USB IP core
 *
 * Copyright (C) 2008 Chipidea - MIPS Technologies, Inc. All rights reserved.
 *
 * Author: David Lopo
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation.
 *
 * Description: MIPS USB IP core family device controller
 *              Structures, registers and logging macros
 */

#ifndef _CI13XXX_h_
#define _CI13XXX_h_

/******************************************************************************
 * DEFINE
 *****************************************************************************/
#define CI13XXX_PAGE_SIZE  4096ul /* page size for TD's */
#define ENDPT_MAX          (32)
#define CTRL_PAYLOAD_MAX   (64)
#define RX        (0)  /* similar to USB_DIR_OUT but can be used as an index */
#define TX        (1)  /* similar to USB_DIR_IN  but can be used as an index */

/* UDC private data:
 *  16MSb - Vendor ID | 16 LSb Vendor private data
 */
#define CI13XX_REQ_VENDOR_ID(id)  (id & 0xFFFF0000UL)

#define MSM_ETD_TYPE			BIT(1)
#define MSM_EP_PIPE_ID_RESET_VAL	0x1F001F

/******************************************************************************
 * STRUCTURES
 *****************************************************************************/
/* DMA layout of transfer descriptors */
struct ci13xxx_td {
	/* 0 */
	u32 next;
#define TD_TERMINATE          BIT(0)
#define TD_ADDR_MASK          (0xFFFFFFEUL << 5)
	/* 1 */
	u32 token;
#define TD_STATUS             (0x00FFUL <<  0)
#define TD_STATUS_TR_ERR      BIT(3)
#define TD_STATUS_DT_ERR      BIT(5)
#define TD_STATUS_HALTED      BIT(6)
#define TD_STATUS_ACTIVE      BIT(7)
#define TD_MULTO              (0x0003UL << 10)
#define TD_IOC                BIT(15)
#define TD_TOTAL_BYTES        (0x7FFFUL << 16)
	/* 2 */
	u32 page[5];
#define TD_CURR_OFFSET        (0x0FFFUL <<  0)
#define TD_FRAME_NUM          (0x07FFUL <<  0)
#define TD_RESERVED_MASK      (0x0FFFUL <<  0)
} __packed __aligned(4);

/* DMA layout of queue heads */
struct ci13xxx_qh {
	/* 0 */
	u32 cap;
#define QH_IOS                BIT(15)
#define QH_MAX_PKT            (0x07FFUL << 16)
#define QH_ZLT                BIT(29)
#define QH_MULT               (0x0003UL << 30)
#define QH_MULT_SHIFT         11
	/* 1 */
	u32 curr;
	/* 2 - 8 */
	struct ci13xxx_td        td;
	/* 9 */
	u32 RESERVED;
	struct usb_ctrlrequest   setup;
} __packed __aligned(4);

/* cache of larger request's original attributes */
struct ci13xxx_multi_req {
	unsigned int	     len;
	unsigned int	     actual;
	void                *buf;
};

/* Extension of usb_request */
struct ci13xxx_req {
	struct usb_request   req;
	unsigned int	     map;
	struct list_head     queue;
	struct ci13xxx_td   *ptr;
	dma_addr_t           dma;
	struct ci13xxx_td   *zptr;
	dma_addr_t           zdma;
	struct ci13xxx_multi_req multi;
};

/* Extension of usb_ep */
struct ci13xxx_ep {
	struct usb_ep                          ep;
	const struct usb_endpoint_descriptor  *desc;
	u8                                     dir;
	u8                                     num;
	u8                                     type;
	char                                   name[16];
	struct {
		struct list_head   queue;
		struct ci13xxx_qh *ptr;
		dma_addr_t         dma;
	}                                      qh;
	struct list_head                       rw_queue;
	int                                    wedge;

	/* global resources */
	spinlock_t                            *lock;
	struct device                         *device;
	struct dma_pool                       *td_pool;
	struct ci13xxx_td                     *last_zptr;
	dma_addr_t                            last_zdma;
	unsigned long                         dTD_update_fail_count;
	unsigned long                         dTD_active_re_q_count;
	unsigned long			      prime_fail_count;
	int				      prime_timer_count;
	struct timer_list		      prime_timer;

	bool                                  multi_req;
};

struct ci13xxx;
struct ci13xxx_udc_driver {
	const char	*name;
	unsigned long	 flags;
	unsigned int nz_itc;
#define CI13XXX_REGS_SHARED		BIT(0)
#define CI13XXX_REQUIRE_TRANSCEIVER	BIT(1)
#define CI13XXX_PULLUP_ON_VBUS		BIT(2)
#define CI13XXX_DISABLE_STREAMING	BIT(3)
#define CI13XXX_ZERO_ITC		BIT(4)
#define CI13XXX_ENABLE_AHB2AHB_BYPASS	BIT(6)

#define CI13XXX_CONTROLLER_RESET_EVENT			0
#define CI13XXX_CONTROLLER_CONNECT_EVENT		1
#define CI13XXX_CONTROLLER_SUSPEND_EVENT		2
#define CI13XXX_CONTROLLER_REMOTE_WAKEUP_EVENT		3
#define CI13XXX_CONTROLLER_RESUME_EVENT		4
#define CI13XXX_CONTROLLER_DISCONNECT_EVENT		5
#define CI13XXX_CONTROLLER_UDC_STARTED_EVENT		6
#define CI13XXX_CONTROLLER_ERROR_EVENT			7

	void	(*notify_event)(struct ci13xxx *udc, unsigned int event);
	bool    (*in_lpm)(struct ci13xxx *udc);
};

/* CI13XXX UDC descriptor & global resources */
struct ci13xxx {
	spinlock_t		  *lock;      /* ctrl register bank access */
	void __iomem              *regs;      /* registers address space */

	struct dma_pool           *qh_pool;   /* DMA pool for queue heads */
	struct dma_pool           *td_pool;   /* DMA pool for transfer descs */
	struct usb_request        *status;    /* ep0 status request */
	void                      *status_buf;/* GET_STATUS buffer */

	struct usb_gadget          gadget;     /* USB slave device */
	struct ci13xxx_ep          ci13xxx_ep[ENDPT_MAX]; /* extended endpts */
	u32                        ep0_dir;    /* ep0 direction */
#define ep0out ci13xxx_ep[0]
#define ep0in  ci13xxx_ep[hw_ep_max / 2]
	u8                         suspended;  /* suspended by the host */
	u8                         configured;  /* is device configured */
	u8                         test_mode;  /* the selected test mode */
	bool                       rw_pending; /* Remote wakeup pending flag */
	struct delayed_work        rw_work;    /* remote wakeup delayed work */
	struct usb_gadget_driver  *driver;     /* 3rd party gadget driver */
	struct ci13xxx_udc_driver *udc_driver; /* device controller driver */
	int                        vbus_active; /* is VBUS active */
	int                        softconnect; /* is pull-up enable allowed */
	unsigned long dTD_update_fail_count;
	struct usb_phy            *transceiver; /* Transceiver struct */
	bool                      skip_flush;   /*
						 * skip flushing remaining EP
						 * upon flush timeout for the
						 * first EP.
						 */
};

/******************************************************************************
 * REGISTERS
 *****************************************************************************/
/* register size */
#define REG_BITS   (32)

/* HCCPARAMS */
#define HCCPARAMS_LEN         BIT(17)

/* DCCPARAMS */
#define DCCPARAMS_DEN         (0x1F << 0)
#define DCCPARAMS_DC          BIT(7)

/* TESTMODE */
#define TESTMODE_FORCE        BIT(0)

/* AHB_MODE */
#define AHB2AHB_BYPASS	      BIT(31)

/* USBCMD */
#define USBCMD_RS             BIT(0)
#define USBCMD_RST            BIT(1)
#define USBCMD_SUTW           BIT(13)
#define USBCMD_ATDTW          BIT(14)

/* USBSTS & USBINTR */
#define USBi_UI               BIT(0)
#define USBi_UEI              BIT(1)
#define USBi_PCI              BIT(2)
#define USBi_URI              BIT(6)
#define USBi_SLI              BIT(8)

/* DEVICEADDR */
#define DEVICEADDR_USBADRA    BIT(24)
#define DEVICEADDR_USBADR     (0x7FUL << 25)

/* PORTSC */
#define PORTSC_FPR            BIT(6)
#define PORTSC_SUSP           BIT(7)
#define PORTSC_PR             BIT(8)
#define PORTSC_HSP            BIT(9)
#define PORTSC_PTC            (0x0FUL << 16)

/* DEVLC */
#define DEVLC_PSPD            (0x03UL << 25)
#define    DEVLC_PSPD_HS      (0x02UL << 25)

/* USBMODE */
#define USBMODE_CM            (0x03UL <<  0)
#define    USBMODE_CM_IDLE    (0x00UL <<  0)
#define    USBMODE_CM_DEVICE  (0x02UL <<  0)
#define    USBMODE_CM_HOST    (0x03UL <<  0)
#define USBMODE_SLOM          BIT(3)
#define USBMODE_SDIS          BIT(4)
#define USBCMD_ITC(n)         (n << 16) /* n = 0, 1, 2, 4, 8, 16, 32, 64 */
#define USBCMD_ITC_MASK       (0xFF << 16)

/* ENDPTCTRL */
#define ENDPTCTRL_RXS         BIT(0)
#define ENDPTCTRL_RXT         (0x03UL <<  2)
#define ENDPTCTRL_RXR         BIT(6)         /* reserved for port 0 */
#define ENDPTCTRL_RXE         BIT(7)
#define ENDPTCTRL_TXS         BIT(16)
#define ENDPTCTRL_TXT         (0x03UL << 18)
#define ENDPTCTRL_TXR         BIT(22)        /* reserved for port 0 */
#define ENDPTCTRL_TXE         BIT(23)

/******************************************************************************
 * LOGGING
 *****************************************************************************/
#define ci13xxx_printk(level, format, args...) \
do { \
	if (_udc == NULL) \
		printk(level "[%s] " format "\n", __func__, ## args); \
	else \
		dev_printk(level, _udc->gadget.dev.parent, \
			   "[%s] " format "\n", __func__, ## args); \
} while (0)

#ifndef err
#define err(format, args...)    ci13xxx_printk(KERN_ERR, format, ## args)
#endif

#define warn(format, args...)   ci13xxx_printk(KERN_WARNING, format, ## args)
#define info(format, args...)   ci13xxx_printk(KERN_INFO, format, ## args)

#ifdef TRACE
#define trace(format, args...)      ci13xxx_printk(KERN_DEBUG, format, ## args)
#define dbg_trace(format, args...)  dev_dbg(dev, format, ##args)
#else
#define trace(format, args...)      do {} while (0)
#define dbg_trace(format, args...)  do {} while (0)
#endif

#endif	/* _CI13XXX_h_ */
Loading