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

Commit c9de713b authored by Hemant Kumar's avatar Hemant Kumar
Browse files

usb: dwc3: Add snapshot of DWC3 MSM drivers



This change adds dwc3-msm.c and dbm.c which together form
the driver for the USB controller on QTI MSM devices. Also
included are the Makefile, Kconfig, and supporting devicetree
bindings documentation. This snapshot is taken as of msm-4.14
commit <a9cdbef84b97de> ("Merge "usb: phy: Enable proper DP DM
masks for PHY interrupts""). The files have been slightly modified
to correct minor checkpatch style warnings.

Change-Id: I9a13cb5bae2f5c1e98906d1cb52134c2dea0bc80
Signed-off-by: default avatarHemant Kumar <hemantk@codeaurora.org>
parent b12cf215
Loading
Loading
Loading
Loading
+133 −0
Original line number Diff line number Diff line
MSM SuperSpeed USB3.0 SoC controller

Required properties :
- compatible : should be "qcom,dwc-usb3-msm"
 - reg: Address and length of the register set for the device
   Required regs are:
	"core_base" : usb controller register set
- interrupts: IRQ lines used by this controller
- interrupt-names : Interrupt resource entries are :
	"pwr_event_irq" : Interrupt to controller for asynchronous events in LPM.
	Used for SS-USB power events.
 - clocks: a list of phandles to the controller clocks. Use as per
   Documentation/devicetree/bindings/clock/clock-bindings.txt
 - clock-names: Names of the clocks in 1-1 correspondence with the "clocks"
   property. Required clocks are "xo", "iface_clk", "core_clk", "sleep_clk"
   and "utmi_clk".
- resets: reset specifier pair consists of phandle for the reset provider
  and reset lines used by this controller.
- reset-names: reset signal name strings sorted in the same order as the resets
  property.

Optional properties :
- reg: Additional registers
     "ahb2phy_base" : top-level register to configure read/write wait cycle with
     both QMP and QUSB PHY registers.
- Refer to "Documentation/devicetree/bindings/arm/msm/msm_bus.txt" for
  below optional properties:
    - qcom,msm_bus,name
    - qcom,msm_bus,num_cases
    - qcom,msm_bus,num_paths
    - qcom,msm_bus,vectors
- interrupt-names : Optional interrupt resource entries are:
    "ss_phy_irq"  : Interrupt from super speed phy for wake up notification.
    "hs_phy_irq" : Interrupt from HS PHY for asynchronous events in LPM.
    "dp_hs_phy_irq" : Interrupt from HS PHY for asynchronous events in LPM
    going through PDC. (use qcom,use-pdc-interrupts property)
    "dm_hs_phy_irq" : Interrupt from HS PHY for asynchronous events in LPM
    going through PDC. (use qcom,use-pdc-interrupts property)

 - clocks: a list of phandles to the controller clocks. Use as per
   Documentation/devicetree/bindings/clock/clock-bindings.txt
 - clock-names: Names of the clocks in 1-1 correspondence with the "clocks"
   property. Optional clocks are "bus_aggr_clk", "noc_aggr_clk" and "cfg_ahb_clk".
- qcom,charging-disabled: If present then battery charging using USB
  is disabled.
- vbus_dwc3-supply: phandle to the 5V VBUS supply regulator used for host mode.
- USB3_GDSC-supply : phandle to the globally distributed switch controller
  regulator node to the USB controller.
- qcom,dwc-usb3-msm-tx-fifo-size: If present, represents RAM size available for
		TX fifo allocation in bytes
- qcom,lpm-to-suspend-delay-ms: Indicates timeout (in milliseconds) to release wakeup source
  after USB is kept into LPM.
- qcom,disable-dev-mode-pm: If present, it disables PM runtime functionality for device mode.
- qcom,core-clk-rate: If present, indicates clock frequency to be set for USB master clock.
- qcom,core-clk-rate-hs: If present, indicates min core clock frequency required to support
  hs speed.
- qcom,use-pdc-interrupts: It present, it configures provided PDC IRQ with required
  configuration for wakeup functionality.
- extcon: phandles to external connector devices. First phandle should point to
	  external connector, which provide type-C based "USB" cable events, the
	  second should point to external connector device, which provide type-C
	  "USB-HOST" cable events. A single phandle may be specified if a single
	  connector device provides both "USB" and "USB-HOST" events. An optional
	  third phandle may be specified for EUD based attach/detach events. A
	  mandatory fourth phandle has to be specified to provide microUSB based
	  "USB" cable events. An optional fifth phandle may be specified to provide
	  microUSB based "USB-HOST" cable events. Only the fourth phandle may be
	  specified if a single connector device provides both "USB" and "USB-HOST"
	  events.
- qcom,num-gsi-evt-buffs: If present, specifies number of GSI based hardware accelerated
  event buffers. 1 event buffer is needed per h/w accelerated endpoint.
- qcom,pm-qos-latency: This represents max tolerable CPU latency in microsecs,
	which is used as a vote by driver to get max performance in perf mode.
- qcom,smmu-s1-bypass: If present, configure SMMU to bypass stage 1 translation.
- qcom,dbm-version: If present, specifies DBM version. Currently "1.4" or "1.5"
	are supported. If omitted, assume HW supports "1.5".
- qcom,reset-ep-after-lpm-resume: If present, dbm requires ep reset after
	going to lpm

Sub nodes:
- Sub node for "DWC3- USB3 controller".
  This sub node is required property for device node. The properties of this subnode
  are specified in dwc3.txt.

Example MSM USB3.0 controller device node :
	usb@f9200000 {
		compatible = "qcom,dwc-usb3-msm";
		reg = <0xf9200000 0xfc000>,
		      <0xf9b3e000 0x3ff>;
		reg-names = "core_base",
			"ahb2phy_base",
		interrupts = <0 133 0>;
		interrupt-names = "hs_phy_irq";
		vbus_dwc3-supply = <&pm8941_mvs1>;
		USB3_GDSC-supply = <&gdsc_usb30>;
		qcom,dwc-usb3-msm-dbm-eps = <4>
		qcom,dwc_usb3-adc_tm = <&pm8941_adc_tm>;
		qcom,dwc-usb3-msm-tx-fifo-size = <29696>;
		qcom,usb-dbm = <&dbm_1p4>;
		qcom,lpm-to-suspend-delay-ms = <2>;
		qcom,num-gsi-evt-buffs = <0x2>;
		qcom,pm-qos-latency = <2>;

		qcom,msm_bus,name = "usb3";
		qcom,msm_bus,num_cases = <2>;
		qcom,msm_bus,num_paths = <1>;
		qcom,msm_bus,vectors =
				<61 512 0 0>,
				<61 512 240000000 960000000>;

		clocks = <&clock_gcc clk_gcc_usb30_master_clk>,
			<&clock_gcc clk_gcc_cfg_noc_usb3_axi_clk>,
			<&clock_gcc clk_gcc_aggre1_usb3_axi_clk>,
			<&clock_rpmcc RPM_AGGR2_NOC_CLK>,
			<&clock_gcc clk_gcc_usb30_mock_utmi_clk>,
			<&clock_gcc clk_gcc_usb30_sleep_clk>,
			<&clock_gcc clk_gcc_usb_phy_cfg_ahb2phy_clk>,
			<&clock_gcc clk_cxo_dwc3_clk>;

		clock-names = "core_clk", "iface_clk", "bus_aggr_clk", "noc_aggr_clk",
				"utmi_clk", "sleep_clk", "cfg_ahb_clk", "xo";

		resets = <&clock_gcc GCC_USB_30_BCR>;
		reset-names = "core_reset";

		dwc3@f9200000 {
			compatible = "synopsys,dwc3";
			reg = <0xf9200000 0xfc000>;
			interrupts = <0 131 0>, <0 179 0>;
			interrupt-names = "irq", "otg_irq";
			tx-fifo-resize;
		};
	};
+9 −0
Original line number Diff line number Diff line
@@ -118,4 +118,13 @@ config USB_DWC3_QCOM
	  for peripheral mode support.
	  Say 'Y' or 'M' if you have one such device.

config USB_DWC3_MSM
	tristate "QTI MSM Platforms"
	depends on ARCH_QCOM || COMPILE_TEST
	help
	  Applicable to QTI MSM Platforms with DesignWare Core
	  USB3 IP.
	  Driver supports host, device and dual-role modes of operation.
	  Say 'Y' or 'M' if you have one such device.

endif
+1 −0
Original line number Diff line number Diff line
@@ -49,3 +49,4 @@ obj-$(CONFIG_USB_DWC3_KEYSTONE) += dwc3-keystone.o
obj-$(CONFIG_USB_DWC3_OF_SIMPLE)	+= dwc3-of-simple.o
obj-$(CONFIG_USB_DWC3_ST)		+= dwc3-st.o
obj-$(CONFIG_USB_DWC3_QCOM)		+= dwc3-qcom.o
obj-$(CONFIG_USB_DWC3_MSM)		+= dwc3-msm.o dbm.o

drivers/usb/dwc3/dbm.c

0 → 100644
+564 −0
Original line number Diff line number Diff line
// SPDX-License-Identifier: GPL-2.0
/*
 * Copyright (c) 2012-2015, 2017-2018, The Linux Foundation. All rights reserved.
 */

#include <linux/platform_device.h>
#include <linux/of.h>
#include <linux/module.h>
#include <linux/delay.h>
#include <linux/io.h>

#include "dbm.h"

/* USB DBM Hardware registers */

#define DBM_REG_OFFSET		0xF8000

enum dbm_reg {
	DBM_EP_CFG,
	DBM_DATA_FIFO,
	DBM_DATA_FIFO_SIZE,
	DBM_DATA_FIFO_EN,
	DBM_GEVNTADR,
	DBM_GEVNTSIZ,
	DBM_DBG_CNFG,
	DBM_HW_TRB0_EP,
	DBM_HW_TRB1_EP,
	DBM_HW_TRB2_EP,
	DBM_HW_TRB3_EP,
	DBM_PIPE_CFG,
	DBM_DISABLE_UPDXFER,
	DBM_SOFT_RESET,
	DBM_GEN_CFG,
	DBM_GEVNTADR_LSB,
	DBM_GEVNTADR_MSB,
	DBM_DATA_FIFO_LSB,
	DBM_DATA_FIFO_MSB,
	DBM_DATA_FIFO_ADDR_EN,
	DBM_DATA_FIFO_SIZE_EN,
};

struct dbm_reg_data {
	u32 offset;
	unsigned int ep_mult;
};

#define DBM_1_4_NUM_EP		4
#define DBM_1_5_NUM_EP		8

struct dbm {
	void __iomem *base;
	const struct dbm_reg_data *reg_table;
	struct device *mdwc_dev;

	int dbm_num_eps;
	u8 ep_num_mapping[DBM_1_5_NUM_EP];
	bool dbm_reset_ep_after_lpm;

	bool is_1p4;
};

static const struct dbm_reg_data dbm_1_4_regtable[] = {
	[DBM_EP_CFG]		= { 0x0000, 0x4 },
	[DBM_DATA_FIFO]		= { 0x0010, 0x4 },
	[DBM_DATA_FIFO_SIZE]	= { 0x0020, 0x4 },
	[DBM_DATA_FIFO_EN]	= { 0x0030, 0x0 },
	[DBM_GEVNTADR]		= { 0x0034, 0x0 },
	[DBM_GEVNTSIZ]		= { 0x0038, 0x0 },
	[DBM_DBG_CNFG]		= { 0x003C, 0x0 },
	[DBM_HW_TRB0_EP]	= { 0x0040, 0x4 },
	[DBM_HW_TRB1_EP]	= { 0x0050, 0x4 },
	[DBM_HW_TRB2_EP]	= { 0x0060, 0x4 },
	[DBM_HW_TRB3_EP]	= { 0x0070, 0x4 },
	[DBM_PIPE_CFG]		= { 0x0080, 0x0 },
	[DBM_SOFT_RESET]	= { 0x0084, 0x0 },
	[DBM_GEN_CFG]		= { 0x0088, 0x0 },
	[DBM_GEVNTADR_LSB]	= { 0x0098, 0x0 },
	[DBM_GEVNTADR_MSB]	= { 0x009C, 0x0 },
	[DBM_DATA_FIFO_LSB]	= { 0x00A0, 0x8 },
	[DBM_DATA_FIFO_MSB]	= { 0x00A4, 0x8 },
};

static const struct dbm_reg_data dbm_1_5_regtable[] = {
	[DBM_EP_CFG]		= { 0x0000, 0x4 },
	[DBM_DATA_FIFO]		= { 0x0280, 0x4 },
	[DBM_DATA_FIFO_SIZE]	= { 0x0080, 0x4 },
	[DBM_DATA_FIFO_EN]	= { 0x026C, 0x0 },
	[DBM_GEVNTADR]		= { 0x0270, 0x0 },
	[DBM_GEVNTSIZ]		= { 0x0268, 0x0 },
	[DBM_DBG_CNFG]		= { 0x0208, 0x0 },
	[DBM_HW_TRB0_EP]	= { 0x0220, 0x4 },
	[DBM_HW_TRB1_EP]	= { 0x0230, 0x4 },
	[DBM_HW_TRB2_EP]	= { 0x0240, 0x4 },
	[DBM_HW_TRB3_EP]	= { 0x0250, 0x4 },
	[DBM_PIPE_CFG]		= { 0x0274, 0x0 },
	[DBM_DISABLE_UPDXFER]	= { 0x0298, 0x0 },
	[DBM_SOFT_RESET]	= { 0x020C, 0x0 },
	[DBM_GEN_CFG]		= { 0x0210, 0x0 },
	[DBM_GEVNTADR_LSB]	= { 0x0260, 0x0 },
	[DBM_GEVNTADR_MSB]	= { 0x0264, 0x0 },
	[DBM_DATA_FIFO_LSB]	= { 0x0100, 0x8 },
	[DBM_DATA_FIFO_MSB]	= { 0x0104, 0x8 },
	[DBM_DATA_FIFO_ADDR_EN]	= { 0x0200, 0x0 },
	[DBM_DATA_FIFO_SIZE_EN]	= { 0x0204, 0x0 },
};

/**
 * Write register masked field with debug info.
 *
 * @dbm - DBM specific data
 * @reg - DBM register, used to look up the offset value
 * @ep - endpoint number
 * @mask - register bitmask.
 * @val - value to write.
 *
 */
static inline void msm_dbm_write_ep_reg_field(struct dbm *dbm,
					      enum dbm_reg reg, int ep,
					      const u32 mask, u32 val)
{
	u32 shift = __ffs(mask);
	u32 offset = dbm->reg_table[reg].offset +
			(dbm->reg_table[reg].ep_mult * ep);
	u32 tmp = ioread32(dbm->base + offset);

	tmp &= ~mask;		/* clear written bits */
	val = tmp | (val << shift);
	iowrite32(val, dbm->base + offset);
}

#define msm_dbm_write_reg_field(d, r, m, v) \
	msm_dbm_write_ep_reg_field(d, r, 0, m, v)

/**
 *
 * Read register with debug info.
 *
 * @dbm - DBM specific data
 * @reg - DBM register, used to look up the offset value
 * @ep - endpoint number
 *
 * @return u32
 */
static inline u32 msm_dbm_read_ep_reg(struct dbm *dbm, enum dbm_reg reg, int ep)
{
	u32 offset = dbm->reg_table[reg].offset +
			(dbm->reg_table[reg].ep_mult * ep);
	return ioread32(dbm->base + offset);
}

#define msm_dbm_read_reg(d, r) msm_dbm_read_ep_reg(d, r, 0)

/**
 *
 * Write register with debug info.
 *
 * @dbm - DBM specific data
 * @reg - DBM register, used to look up the offset value
 * @ep - endpoint number
 *
 */
static inline void msm_dbm_write_ep_reg(struct dbm *dbm, enum dbm_reg reg,
					int ep, u32 val)
{
	u32 offset = dbm->reg_table[reg].offset +
			(dbm->reg_table[reg].ep_mult * ep);
	iowrite32(val, dbm->base + offset);
}

#define msm_dbm_write_reg(d, r, v) msm_dbm_write_ep_reg(d, r, 0, v)

/**
 * Return DBM EP number according to usb endpoint number.
 *
 */
static int find_matching_dbm_ep(struct dbm *dbm, u8 usb_ep)
{
	int i;

	for (i = 0; i < dbm->dbm_num_eps; i++)
		if (dbm->ep_num_mapping[i] == usb_ep)
			return i;

	pr_debug("%s: No DBM EP matches USB EP %d", __func__, usb_ep);
	return -ENODEV; /* Not found */
}


/**
 * Reset the DBM registers upon initialization.
 *
 */
int dbm_soft_reset(struct dbm *dbm, bool reset)
{
	if (!dbm) {
		pr_err("%s: dbm pointer is NULL!\n", __func__);
		return -EPERM;
	}

	pr_debug("%s DBM reset\n", (reset ? "Enter" : "Exit"));

	msm_dbm_write_reg_field(dbm, DBM_SOFT_RESET, DBM_SFT_RST_MASK, reset);

	return 0;
}

/**
 * Soft reset specific DBM ep.
 * This function is called by the function driver upon events
 * such as transfer aborting, USB re-enumeration and USB
 * disconnection.
 *
 * @dbm_ep - DBM ep number.
 * @enter_reset - should we enter a reset state or get out of it.
 *
 */
static int ep_soft_reset(struct dbm *dbm, u8 dbm_ep, bool enter_reset)
{
	pr_debug("Setting DBM ep %d reset to %d\n", dbm_ep, enter_reset);

	if (dbm_ep >= dbm->dbm_num_eps) {
		pr_err("Invalid DBM ep index %d\n", dbm_ep);
		return -ENODEV;
	}

	if (enter_reset) {
		msm_dbm_write_reg_field(dbm, DBM_SOFT_RESET,
			DBM_SFT_RST_EPS_MASK & 1 << dbm_ep, 1);
	} else {
		msm_dbm_write_reg_field(dbm, DBM_SOFT_RESET,
			DBM_SFT_RST_EPS_MASK & 1 << dbm_ep, 0);
	}

	return 0;
}


/**
 * Soft reset specific DBM ep (by USB EP number).
 * This function is called by the function driver upon events
 * such as transfer aborting, USB re-enumeration and USB
 * disconnection.
 *
 * The function relies on ep_soft_reset() for checking
 * the legality of the resulting DBM ep number.
 *
 * @usb_ep - USB ep number.
 * @enter_reset - should we enter a reset state or get out of it.
 *
 */
int dbm_ep_soft_reset(struct dbm *dbm, u8 usb_ep, bool enter_reset)
{
	int dbm_ep;

	if (!dbm) {
		pr_err("%s: dbm pointer is NULL!\n", __func__);
		return -EPERM;
	}

	dbm_ep = find_matching_dbm_ep(dbm, usb_ep);

	pr_debug("Setting USB ep %d reset to %d\n", usb_ep, enter_reset);
	return ep_soft_reset(dbm, dbm_ep, enter_reset);
}

/**
 * Configure a USB DBM ep to work in BAM mode.
 *
 *
 * @usb_ep - USB physical EP number.
 * @producer - producer/consumer.
 * @disable_wb - disable write back to system memory.
 * @internal_mem - use internal USB memory for data fifo.
 * @ioc - enable interrupt on completion.
 *
 * @return int - DBM ep number.
 */
int dbm_ep_config(struct dbm *dbm, u8 usb_ep, u8 bam_pipe, bool producer,
		  bool disable_wb, bool internal_mem, bool ioc)
{
	int dbm_ep;
	u32 ep_cfg;
	u32 data;

	if (!dbm) {
		pr_err("%s: dbm pointer is NULL!\n", __func__);
		return -EPERM;
	}

	pr_debug("Configuring DBM ep\n");

	dbm_ep = find_matching_dbm_ep(dbm, usb_ep);

	if (dbm_ep < 0) {
		pr_err("usb ep index %d has no corresponding dbm ep\n", usb_ep);
		return -ENODEV;
	}

	/* Due to HW issue, EP 7 can be set as IN EP only */
	if (!dbm->is_1p4 && dbm_ep == 7 && producer) {
		pr_err("last DBM EP can't be OUT EP\n");
		return -ENODEV;
	}

	/* Set ioc bit for dbm_ep if needed */
	msm_dbm_write_reg_field(dbm, DBM_DBG_CNFG,
		DBM_ENABLE_IOC_MASK & 1 << dbm_ep, ioc ? 1 : 0);

	ep_cfg = (producer ? DBM_PRODUCER : 0) |
		(disable_wb ? DBM_DISABLE_WB : 0) |
		(internal_mem ? DBM_INT_RAM_ACC : 0);

	msm_dbm_write_ep_reg_field(dbm, DBM_EP_CFG, dbm_ep,
		DBM_PRODUCER | DBM_DISABLE_WB | DBM_INT_RAM_ACC, ep_cfg >> 8);

	msm_dbm_write_ep_reg_field(dbm, DBM_EP_CFG, dbm_ep, USB3_EPNUM,
		usb_ep);

	if (dbm->is_1p4) {
		msm_dbm_write_ep_reg_field(dbm, DBM_EP_CFG, dbm_ep,
				DBM_BAM_PIPE_NUM, bam_pipe);
		msm_dbm_write_reg_field(dbm, DBM_PIPE_CFG, 0x000000ff, 0xe4);
	}

	msm_dbm_write_ep_reg_field(dbm, DBM_EP_CFG, dbm_ep, DBM_EN_EP, 1);

	data = msm_dbm_read_reg(dbm, DBM_DISABLE_UPDXFER);
	data &= ~(0x1 << dbm_ep);
	msm_dbm_write_reg(dbm, DBM_DISABLE_UPDXFER, data);

	return dbm_ep;
}

/**
 * Return number of configured DBM endpoints.
 */
int dbm_get_num_of_eps_configured(struct dbm *dbm)
{
	int i;
	int count = 0;

	if (!dbm) {
		pr_err("%s: dbm pointer is NULL!\n", __func__);
		return -EPERM;
	}

	for (i = 0; i < dbm->dbm_num_eps; i++)
		if (dbm->ep_num_mapping[i])
			count++;

	return count;
}

/**
 * Configure a USB DBM ep to work in normal mode.
 *
 * @usb_ep - USB ep number.
 *
 */
int dbm_ep_unconfig(struct dbm *dbm, u8 usb_ep)
{
	int dbm_ep;
	u32 data;

	if (!dbm) {
		pr_err("%s: dbm pointer is NULL!\n", __func__);
		return -EPERM;
	}

	pr_debug("Unconfiguring DB ep\n");

	dbm_ep = find_matching_dbm_ep(dbm, usb_ep);

	if (dbm_ep < 0) {
		pr_debug("usb ep index %d has no corespondng dbm ep\n", usb_ep);
		return -ENODEV;
	}

	dbm->ep_num_mapping[dbm_ep] = 0;

	data = msm_dbm_read_ep_reg(dbm, DBM_EP_CFG, dbm_ep);
	data &= (~0x1);
	msm_dbm_write_ep_reg(dbm, DBM_EP_CFG, dbm_ep, data);

	/*
	 * ep_soft_reset is not required during disconnect as pipe reset on
	 * next connect will take care of the same.
	 */
	return 0;
}

/**
 * Configure the DBM with the USB3 core event buffer.
 * This function is called by the SNPS UDC upon initialization.
 *
 * @addr - address of the event buffer.
 * @size - size of the event buffer.
 *
 */
int dbm_event_buffer_config(struct dbm *dbm, u32 addr_lo, u32 addr_hi, int size)
{
	if (!dbm) {
		pr_err("%s: dbm pointer is NULL!\n", __func__);
		return -EPERM;
	}

	pr_debug("Configuring event buffer\n");

	if (size < 0) {
		pr_err("Invalid size. size = %d", size);
		return -EINVAL;
	}

	/* In case event buffer is already configured, Do nothing. */
	if (msm_dbm_read_reg(dbm, DBM_GEVNTSIZ))
		return 0;

	if (!dbm->is_1p4 || sizeof(phys_addr_t) > sizeof(u32)) {
		msm_dbm_write_reg(dbm, DBM_GEVNTADR_LSB, addr_lo);
		msm_dbm_write_reg(dbm, DBM_GEVNTADR_MSB, addr_hi);
	} else {
		msm_dbm_write_reg(dbm, DBM_GEVNTADR, addr_lo);
	}

	msm_dbm_write_reg_field(dbm, DBM_GEVNTSIZ, DBM_GEVNTSIZ_MASK, size);

	return 0;
}

/**
 * Disable update xfer before queueing stop xfer command to USB3 core.
 *
 * @usb_ep - USB physical EP number.
 *
 */
int dwc3_dbm_disable_update_xfer(struct dbm *dbm, u8 usb_ep)
{
	u32 data;
	int dbm_ep;

	if (!dbm) {
		pr_err("%s: dbm pointer is NULL!\n", __func__);
		return -EPERM;
	}

	dbm_ep = find_matching_dbm_ep(dbm, usb_ep);

	if (dbm_ep < 0) {
		pr_err("usb ep index %d has no corresponding dbm ep\n", usb_ep);
		return -ENODEV;
	}

	data = msm_dbm_read_reg(dbm, DBM_DISABLE_UPDXFER);
	data |= (0x1 << dbm_ep);
	msm_dbm_write_reg(dbm, DBM_DISABLE_UPDXFER, data);

	return 0;
}

int dbm_data_fifo_config(struct dbm *dbm, u8 dep_num, unsigned long addr,
				u32 size, u8 dst_pipe_idx)
{
	u8 dbm_ep = dst_pipe_idx;
	u32 lo = lower_32_bits(addr);
	u32 hi = upper_32_bits(addr);

	if (!dbm) {
		pr_err("%s: dbm pointer is NULL!\n", __func__);
		return -EPERM;
	}

	dbm->ep_num_mapping[dbm_ep] = dep_num;

	if (!dbm->is_1p4 || sizeof(addr) > sizeof(u32)) {
		msm_dbm_write_ep_reg(dbm, DBM_DATA_FIFO_LSB, dbm_ep, lo);
		msm_dbm_write_ep_reg(dbm, DBM_DATA_FIFO_MSB, dbm_ep, hi);
	} else {
		msm_dbm_write_ep_reg(dbm, DBM_DATA_FIFO, dbm_ep, addr);
	}

	msm_dbm_write_ep_reg_field(dbm, DBM_DATA_FIFO_SIZE, dbm_ep,
		DBM_DATA_FIFO_SIZE_MASK, size);

	return 0;
}

void dbm_set_speed(struct dbm *dbm, bool speed)
{
	if (!dbm) {
		pr_err("%s: dbm pointer is NULL!\n", __func__);
		return;
	}

	msm_dbm_write_reg(dbm, DBM_GEN_CFG, speed);
}

void dbm_enable(struct dbm *dbm)
{
	if (!dbm) {
		pr_err("%s: dbm pointer is NULL!\n", __func__);
		return;
	}

	if (dbm->is_1p4) /* no-op */
		return;

	msm_dbm_write_reg(dbm, DBM_DATA_FIFO_ADDR_EN, 0x000000FF);
	msm_dbm_write_reg(dbm, DBM_DATA_FIFO_SIZE_EN, 0x000000FF);
}

bool dbm_reset_ep_after_lpm(struct dbm *dbm)
{
	if (!dbm) {
		pr_err("%s: dbm pointer is NULL!\n", __func__);
		return false;
	}

	return dbm->dbm_reset_ep_after_lpm;
}

bool dbm_l1_lpm_interrupt(struct dbm *dbm)
{
	if (!dbm) {
		pr_err("%s: dbm pointer is NULL!\n", __func__);
		return false;
	}

	return !dbm->is_1p4;
}

struct dbm *dwc3_init_dbm(struct device *dev, void __iomem *base)
{
	const char *dbm_ver;
	int ret;
	struct dbm *dbm;

	if (!dev->of_node) {
		dev_dbg(dev, "device does not have a device node entry\n");
		return ERR_PTR(-EINVAL);
	}

	dbm = devm_kzalloc(dev, sizeof(*dbm), GFP_KERNEL);
	if (!dbm)
		return ERR_PTR(-ENOMEM);

	ret = of_property_read_string(dev->of_node, "qcom,dbm-version",
			&dbm_ver);
	if (!ret && !strcmp(dbm_ver, "1.4")) {
		dbm->reg_table = dbm_1_4_regtable;
		dbm->dbm_num_eps = DBM_1_4_NUM_EP;
		dbm->is_1p4 = true;
	} else {
		/* default to v1.5 register layout */
		dbm->reg_table = dbm_1_5_regtable;
		dbm->dbm_num_eps = DBM_1_5_NUM_EP;
	}

	dbm->base = base + DBM_REG_OFFSET;
	dbm->mdwc_dev = dev;
	dbm->dbm_reset_ep_after_lpm = of_property_read_bool(dev->of_node,
			"qcom,reset-ep-after-lpm-resume");

	return dbm;
}

drivers/usb/dwc3/dbm.h

0 → 100644
+68 −0
Original line number Diff line number Diff line
/* SPDX-License-Identifier: GPL-2.0 */
/*
 * Copyright (c) 2012-2015, 2017-2018, The Linux Foundation. All rights reserved.
 */

#ifndef __DBM_H
#define __DBM_H

#include <linux/device.h>
#include <linux/types.h>

/**
 *  USB DBM  Hardware registers bitmask.
 *
 */
/* DBM_EP_CFG */
#define DBM_EN_EP		0x00000001
#define USB3_EPNUM		0x0000003E
#define DBM_BAM_PIPE_NUM	0x000000C0
#define DBM_PRODUCER		0x00000100
#define DBM_DISABLE_WB		0x00000200
#define DBM_INT_RAM_ACC		0x00000400

/* DBM_DATA_FIFO_SIZE */
#define DBM_DATA_FIFO_SIZE_MASK	0x0000ffff

/* DBM_GEVNTSIZ */
#define DBM_GEVNTSIZ_MASK	0x0000ffff

/* DBM_DBG_CNFG */
#define DBM_ENABLE_IOC_MASK	0x0000000f

/* DBM_SOFT_RESET */
#define DBM_SFT_RST_EP0		0x00000001
#define DBM_SFT_RST_EP1		0x00000002
#define DBM_SFT_RST_EP2		0x00000004
#define DBM_SFT_RST_EP3		0x00000008
#define DBM_SFT_RST_EPS_MASK	0x0000000F
#define DBM_SFT_RST_MASK	0x80000000
#define DBM_EN_MASK		0x00000002

/* DBM TRB configurations */
#define DBM_TRB_BIT		0x80000000
#define DBM_TRB_DATA_SRC	0x40000000
#define DBM_TRB_DMA		0x20000000
#define DBM_TRB_EP_NUM(ep)	(ep<<24)

struct dbm;

struct dbm *dwc3_init_dbm(struct device *dev, void __iomem *base);

int dbm_soft_reset(struct dbm *dbm, bool enter_reset);
int dbm_ep_config(struct dbm  *dbm, u8 usb_ep, u8 bam_pipe, bool producer,
			bool disable_wb, bool internal_mem, bool ioc);
int dbm_ep_unconfig(struct dbm *dbm, u8 usb_ep);
int dbm_get_num_of_eps_configured(struct dbm *dbm);
int dbm_event_buffer_config(struct dbm *dbm, u32 addr_lo, u32 addr_hi,
				int size);
int dbm_data_fifo_config(struct dbm *dbm, u8 dep_num, unsigned long addr,
				u32 size, u8 dst_pipe_idx);
int dwc3_dbm_disable_update_xfer(struct dbm *dbm, u8 usb_ep);
void dbm_set_speed(struct dbm *dbm, bool speed);
void dbm_enable(struct dbm *dbm);
int dbm_ep_soft_reset(struct dbm *dbm, u8 usb_ep, bool enter_reset);
bool dbm_reset_ep_after_lpm(struct dbm *dbm);
bool dbm_l1_lpm_interrupt(struct dbm *dbm);

#endif /* __DBM_H */
Loading