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

Commit 22661e1d authored by Jack Pham's avatar Jack Pham
Browse files

usb: dwc3-msm: dbm: Merge dbm '1.4' into dbm-1_5.c



Since there is practically no functionality difference between
DBM versions 1.4 and 1.5, the two drivers can be combined. Add
the register offset table for 1.4 which will be chosen based
on the device tree compatible string.

Change-Id: Ibcb070846f02174c0225835617742a786f4c55d1
Signed-off-by: default avatarJack Pham <jackp@codeaurora.org>
parent 1dc6fd8d
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -38,5 +38,5 @@ obj-$(CONFIG_USB_DWC3_EXYNOS) += dwc3-exynos.o
obj-$(CONFIG_USB_DWC3_PCI)		+= dwc3-pci.o
obj-$(CONFIG_USB_DWC3_KEYSTONE)		+= dwc3-keystone.o
obj-$(CONFIG_USB_DWC3_QCOM)		+= dwc3-qcom.o
obj-$(CONFIG_USB_DWC3_MSM)		+= dbm-1_4.o dbm-1_5.o dwc3-msm.o dbm.o
obj-$(CONFIG_USB_DWC3_MSM)		+= dbm-1_5.o dwc3-msm.o dbm.o
obj-$(CONFIG_USB_DWC3_ST)		+= dwc3-st.o

drivers/usb/dwc3/dbm-1_4.c

deleted100644 → 0
+0 −458
Original line number Diff line number Diff line
/*
 * Copyright (c) 2012-2014, 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/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_EP_CFG(n)		(0x00 + 4 * (n))
#define DBM_DATA_FIFO(n)	(0x10 + 4 * (n))
#define DBM_DATA_FIFO_SIZE(n)	(0x20 + 4 * (n))
#define DBM_DATA_FIFO_EN	(0x30)
#define DBM_GEVNTADR		(0x34)
#define DBM_GEVNTSIZ		(0x38)
#define DBM_DBG_CNFG		(0x3C)
#define DBM_HW_TRB0_EP(n)	(0x40 + 4 * (n))
#define DBM_HW_TRB1_EP(n)	(0x50 + 4 * (n))
#define DBM_HW_TRB2_EP(n)	(0x60 + 4 * (n))
#define DBM_HW_TRB3_EP(n)	(0x70 + 4 * (n))
#define DBM_PIPE_CFG		(0x80)
#define DBM_SOFT_RESET		(0x84)
#define DBM_GEN_CFG		(0x88)
#define DBM_GEVNTADR_LSB	(0x98)
#define DBM_GEVNTADR_MSB	(0x9C)
#define DBM_DATA_FIFO_LSB(n)	(0xA0 + 8 * (n))
#define DBM_DATA_FIFO_MSB(n)	(0xA4 + 8 * (n))

#define DBM_1_4_NUM_EP		4

struct dbm_data {
	void __iomem *base;

	int dbm_num_eps;
	u8 ep_num_mapping[DBM_1_4_NUM_EP];
};

static struct dbm_data *dbm_data;

/**
 * Write register masked field with debug info.
 *
 * @base - DWC3 base virtual address.
 * @offset - register offset.
 * @mask - register bitmask.
 * @val - value to write.
 *
 */
static inline void msm_dbm_write_reg_field(void *base, u32 offset,
					    const u32 mask, u32 val)
{
	u32 shift = find_first_bit((void *)&mask, 32);
	u32 tmp = ioread32(base + offset);

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

/**
 *
 * Read register with debug info.
 *
 * @base - DWC3 base virtual address.
 * @offset - register offset.
 *
 * @return u32
 */
static inline u32 msm_dbm_read_reg(void *base, u32 offset)
{
	u32 val = ioread32(base + offset);
	return val;
}

/**
 *
 * Write register with debug info.
 *
 * @base - DWC3 base virtual address.
 * @offset - register offset.
 * @val - value to write.
 *
 */
static inline void msm_dbm_write_reg(void *base, u32 offset, u32 val)
{
	iowrite32(val, base + offset);
}


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

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

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


/**
 * Reset the DBM registers upon initialization.
 *
 */
static int soft_reset(bool reset)
{
	pr_debug("%s DBM reset\n", (reset ? "Enter" : "Exit"));

	msm_dbm_write_reg_field(dbm_data->base, 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(u8 dbm_ep, bool enter_reset)
{
	pr_debug("Setting DBM ep %d reset to %d\n", dbm_ep, enter_reset);

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

	if (enter_reset) {
		msm_dbm_write_reg_field(dbm_data->base, DBM_SOFT_RESET,
			DBM_SFT_RST_EPS_MASK & 1 << dbm_ep, 1);
	} else {
		msm_dbm_write_reg_field(dbm_data->base, 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.
 *
 */
static int usb_ep_soft_reset(u8 usb_ep, bool enter_reset)
{
	int dbm_ep = msm_dbm_find_matching_dbm_ep(usb_ep);

	pr_debug("Setting USB ep %d reset to %d\n", usb_ep, enter_reset);
	return ep_soft_reset(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.
 */
static int ep_config(u8 usb_ep, u8 bam_pipe, bool producer, bool disable_wb,
		bool internal_mem, bool ioc)
{
	int dbm_ep;
	u32 ep_cfg;

	pr_debug("Configuring DBM ep\n");

	dbm_ep = msm_dbm_find_matching_dbm_ep(usb_ep);

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

	/* First, reset the dbm endpoint */
	ep_soft_reset(dbm_ep, 0);

	/* Set ioc bit for dbm_ep if needed */
	msm_dbm_write_reg_field(dbm_data->base, 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_reg_field(dbm_data->base, DBM_EP_CFG(dbm_ep),
		DBM_PRODUCER | DBM_DISABLE_WB | DBM_INT_RAM_ACC, ep_cfg >> 8);

	msm_dbm_write_reg_field(dbm_data->base, DBM_EP_CFG(dbm_ep), USB3_EPNUM,
		usb_ep);
	msm_dbm_write_reg_field(dbm_data->base, DBM_EP_CFG(dbm_ep),
		DBM_BAM_PIPE_NUM, bam_pipe);
	msm_dbm_write_reg_field(dbm_data->base, DBM_PIPE_CFG, 0x000000ff,
		0xe4);
	msm_dbm_write_reg_field(dbm_data->base, DBM_EP_CFG(dbm_ep), DBM_EN_EP,
		1);

	return dbm_ep;
}

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

	pr_debug("Unconfiguring DB ep\n");

	dbm_ep = msm_dbm_find_matching_dbm_ep(usb_ep);

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

	dbm_data->ep_num_mapping[dbm_ep] = 0;

	data = msm_dbm_read_reg(dbm_data->base, DBM_EP_CFG(dbm_ep));
	data &= (~0x1);
	msm_dbm_write_reg(dbm_data->base, DBM_EP_CFG(dbm_ep), data);

	/* Reset the dbm endpoint */
	ep_soft_reset(dbm_ep, true);
	/*
	 * 10 usec delay is required before deasserting DBM endpoint reset
	 * according to hardware programming guide.
	 */
	udelay(10);
	ep_soft_reset(dbm_ep, false);

	return 0;
}

/**
 * Return number of configured DBM endpoints.
 *
 */
static int get_num_of_eps_configured(void)
{
	int i;
	int count = 0;

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

	return count;
}

/**
 * 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.
 *
 */
static int event_buffer_config(u32 addr_lo, u32 addr_hi, int size)
{
	pr_debug("Configuring event buffer\n");

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

	if (sizeof(phys_addr_t) > sizeof(u32)) {
		msm_dbm_write_reg(dbm_data->base, DBM_GEVNTADR_LSB, addr_lo);
		msm_dbm_write_reg(dbm_data->base, DBM_GEVNTADR_MSB, addr_hi);
	} else {
		msm_dbm_write_reg(dbm_data->base, DBM_GEVNTADR, addr_lo);
	}
	msm_dbm_write_reg_field(dbm_data->base, DBM_GEVNTSIZ,
		DBM_GEVNTSIZ_MASK, size);

	return 0;
}


static int data_fifo_config(u8 dep_num, phys_addr_t addr,
		 u32 size, u8 dst_pipe_idx)
{
	u8 dbm_ep;
	dbm_ep = dst_pipe_idx;

	dbm_data->ep_num_mapping[dbm_ep] = dep_num;

	if (sizeof(addr) > sizeof(u32)) {
		u32 lo = lower_32_bits(addr);
		u32 hi = upper_32_bits(addr);
		msm_dbm_write_reg(dbm_data->base,
					DBM_DATA_FIFO_LSB(dbm_ep), lo);
		msm_dbm_write_reg(dbm_data->base,
					DBM_DATA_FIFO_MSB(dbm_ep), hi);
	} else {
		msm_dbm_write_reg(dbm_data->base,
					DBM_DATA_FIFO(dbm_ep), addr);
	}
	msm_dbm_write_reg_field(dbm_data->base, DBM_DATA_FIFO_SIZE(dbm_ep),
		DBM_DATA_FIFO_SIZE_MASK, size);

	return 0;

}

static void set_speed(bool speed)
{
	msm_dbm_write_reg(dbm_data->base, DBM_GEN_CFG, speed);
}

static bool reset_ep_after_lpm(void)
{
	return false;
}

static void enable(void) {}


static int msm_dbm_probe(struct platform_device *pdev)
{
	struct device *dev = &pdev->dev;
	struct dbm *dbm;
	struct resource *res;
	int ret = 0;

	dbm_data = devm_kzalloc(dev, sizeof(*dbm_data), GFP_KERNEL);
	if (!dbm_data)
		return -ENOMEM;
	dbm_data->dbm_num_eps = DBM_1_4_NUM_EP;

	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
	if (!res) {
		dev_err(&pdev->dev, "missing memory base resource\n");
		ret = -ENODEV;
		goto free_dbm_data;
	}

	dbm_data->base = devm_ioremap_nocache(&pdev->dev, res->start,
		resource_size(res));
	if (!dbm_data->base) {
		dev_err(&pdev->dev, "ioremap failed\n");
		ret = -ENOMEM;
		goto free_dbm_data;
	}


	dbm = devm_kzalloc(dev, sizeof(*dbm), GFP_KERNEL);
	if (!dbm) {
		dev_err(&pdev->dev, "not enough memory\n");
		ret = -ENOMEM;
		goto free_dbm_data;
	}

	dbm->dev = dev;

	dbm->soft_reset = soft_reset;
	dbm->ep_config = ep_config;
	dbm->ep_unconfig = ep_unconfig;
	dbm->get_num_of_eps_configured = get_num_of_eps_configured;
	dbm->event_buffer_config = event_buffer_config;
	dbm->data_fifo_config = data_fifo_config;
	dbm->set_speed = set_speed;
	dbm->enable = enable;
	dbm->ep_soft_reset = usb_ep_soft_reset;
	dbm->reset_ep_after_lpm = reset_ep_after_lpm;

	platform_set_drvdata(pdev, dbm);

	return usb_add_dbm(dbm);

free_dbm_data:
	kfree(dbm_data);
	return ret;
}

static int msm_dbm_remove(struct platform_device *pdev)
{
	struct dbm *dbm = platform_get_drvdata(pdev);

	kfree(dbm);
	kfree(dbm_data);

	return 0;
}

static const struct of_device_id msm_dbm_1_4_id_table[] = {
	{
		.compatible = "qcom,usb-dbm-1p4",
	},
	{ },
};
MODULE_DEVICE_TABLE(of, msm_dbm_1_4_id_table);

static struct platform_driver msm_dbm_driver = {
	.probe		= msm_dbm_probe,
	.remove		= msm_dbm_remove,
	.driver = {
		.name	= "msm-usb-dbm-1-4",
		.of_match_table = of_match_ptr(msm_dbm_1_4_id_table),
	},
};

module_platform_driver(msm_dbm_driver);

MODULE_DESCRIPTION("MSM USB DBM 1.4 driver");
MODULE_LICENSE("GPL v2");
+59 −9
Original line number Diff line number Diff line
@@ -52,6 +52,7 @@ struct dbm_reg_data {
	unsigned int ep_mult;
};

#define DBM_1_4_NUM_EP		4
#define DBM_1_5_NUM_EP		8

struct dbm_data {
@@ -61,10 +62,33 @@ struct dbm_data {
	int dbm_num_eps;
	u8 ep_num_mapping[DBM_1_5_NUM_EP];
	bool dbm_reset_ep_after_lpm;

	bool is_1p4;
};

static struct dbm_data *dbm_data;

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 },
@@ -265,7 +289,7 @@ static int ep_config(u8 usb_ep, u8 bam_pipe, bool producer, bool disable_wb,
	}

	/* Due to HW issue, EP 7 can be set as IN EP only */
	if (dbm_ep == 7 && producer) {
	if (!dbm_data->is_1p4 && dbm_ep == 7 && producer) {
		pr_err("last DBM EP can't be OUT EP\n");
		return -ENODEV;
	}
@@ -287,6 +311,13 @@ static int ep_config(u8 usb_ep, u8 bam_pipe, bool producer, bool disable_wb,
	msm_dbm_write_ep_reg_field(dbm_data, DBM_EP_CFG, dbm_ep, USB3_EPNUM,
		usb_ep);

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

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

	return dbm_ep;
@@ -374,8 +405,13 @@ static int event_buffer_config(u32 addr_lo, u32 addr_hi, int size)
	if (msm_dbm_read_reg(dbm_data, DBM_GEVNTSIZ))
		return 0;

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

	msm_dbm_write_reg_field(dbm_data, DBM_GEVNTSIZ,
		DBM_GEVNTSIZ_MASK, size);

@@ -392,8 +428,12 @@ static int data_fifo_config(u8 dep_num, phys_addr_t addr,

	dbm_data->ep_num_mapping[dbm_ep] = dep_num;

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

	msm_dbm_write_ep_reg_field(dbm_data, DBM_DATA_FIFO_SIZE, dbm_ep,
		DBM_DATA_FIFO_SIZE_MASK, size);
@@ -409,6 +449,9 @@ static void set_speed(bool speed)

static void enable(void)
{
	if (dbm_data->is_1p4) /* no-op */
		return;

	msm_dbm_write_reg(dbm_data, DBM_DATA_FIFO_ADDR_EN, 0x000000FF);
	msm_dbm_write_reg(dbm_data, DBM_DATA_FIFO_SIZE_EN, 0x000000FF);
}
@@ -420,10 +463,11 @@ static bool reset_ep_after_lpm(void)

static bool l1_lpm_interrupt(void)
{
	return true;
	return !dbm_data->is_1p4;
}

static const struct of_device_id msm_dbm_id_table[] = {
	{ .compatible = "qcom,usb-dbm-1p4", .data = &dbm_1_4_regtable },
	{ .compatible = "qcom,usb-dbm-1p5", .data = &dbm_1_5_regtable },
	{ },
};
@@ -441,7 +485,6 @@ static int msm_dbm_probe(struct platform_device *pdev)
	dbm_data = devm_kzalloc(dev, sizeof(*dbm_data), GFP_KERNEL);
	if (!dbm_data)
		return -ENOMEM;
	dbm_data->dbm_num_eps = DBM_1_5_NUM_EP;

	match = of_match_node(msm_dbm_id_table, node);
	if (!match) {
@@ -450,6 +493,13 @@ static int msm_dbm_probe(struct platform_device *pdev)
	}
	dbm_data->reg_table = match->data;

	if (!strcmp(match->compatible, "qcom,usb-dbm-1p4")) {
		dbm_data->dbm_num_eps = DBM_1_4_NUM_EP;
		dbm_data->is_1p4 = true;
	} else {
		dbm_data->dbm_num_eps = DBM_1_5_NUM_EP;
	}

	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
	if (!res) {
		dev_err(&pdev->dev, "missing memory base resource\n");
@@ -513,14 +563,14 @@ static struct platform_driver msm_dbm_driver = {
	.probe		= msm_dbm_probe,
	.remove		= msm_dbm_remove,
	.driver = {
		.name	= "msm-usb-dbm-1-5",
		.name	= "msm-usb-dbm",
		.of_match_table = of_match_ptr(msm_dbm_id_table),
	},
};

module_platform_driver(msm_dbm_driver);

MODULE_DESCRIPTION("MSM USB DBM 1.5 driver");
MODULE_DESCRIPTION("MSM USB DBM driver");
MODULE_LICENSE("GPL v2");