usb: dwc3-msm: DBM refactoring

Refactoring of DBM code to allow easy support for future
DBM versions.
All DBM specific operation are now defined in dbm.h,
and implemented in a dbm specific version module.

Note that only a single instance of DBM is currently
supported.

Change-Id: I751300240951a84a42358c3b8b5222e73c537653
Signed-off-by: Bar Weiner <bweiner@codeaurora.org>
This commit is contained in:
Bar Weiner 2013-12-31 13:34:54 +02:00
parent f82aaf71a3
commit 3bc20703e5
10 changed files with 735 additions and 325 deletions

View file

@ -0,0 +1,11 @@
MSM DBM (Device Bus Manager)
Required properties :
- compatible : should be "qcom,usb-dbm-<dbm version number>"
- reg : offset and length of the register set in the memory map.
Example MSM DBM (Device Bus Manager) device node :
dbm_1p4: dbm@f92f8000 {
compatible = "qcom,usb-dbm-1p4";
reg = <0xf92f8000 0x1000>;
};

View file

@ -11,10 +11,6 @@ Required properties :
"vbus_dwc3" : vbus supply for host mode when DWC3 operating as DRD.
This can be left as optional if "host-only-mode" is selected in the
'dwc3' sub node for "DWC3-USB3 Core device".
- qcom,dwc-usb3-msm-dbm-eps: Number of endpoints avaliable for
the DBM (Device Bus Manager). The DBM is HW unit which is part of
the MSM USB3.0 core (which also includes the Synopsys DesignWare
USB3.0 controller)
Optional properties :
- Refer to "Documentation/devicetree/bindings/arm/msm/msm_bus.txt" for
@ -52,6 +48,7 @@ Optional properties :
being reset in initialization.
- qcom,no-suspend-resume: If present, the device will not perform any activity
during suspend/resume
- qcom,usb-dbm : phandle for the DBM device
Sub nodes:
- Sub node for "DWC3- USB3 controller".
@ -71,6 +68,7 @@ Example MSM USB3.0 controller device node :
qcom,dwc_usb3-adc_tm = <&pm8941_adc_tm>;
qcom,dwc-usb3-msm-tx-fifo-size = <29696>;
qcom,dwc-usb3-msm-qdss-tx-fifo-size = <16384>;
qcom,usb-dbm = <&dbm_1p4>;
qcom,msm_bus,name = "usb3";
qcom,msm_bus,num_cases = <2>;

View file

@ -1928,12 +1928,12 @@
interrupt-names = "hs_phy_irq", "pmic_id_irq";
USB3_GDSC-supply = <&gdsc_usb30>;
qcom,dwc-usb3-msm-dbm-eps = <4>;
qcom,dwc-usb3-msm-tx-fifo-size = <29696>;
qcom,dwc-usb3-msm-qdss-tx-fifo-size = <8192>;
qcom,otg-capability;
qcom,misc-ref = <&pma8084_misc>;
qcom,restore-sec-cfg-for-scm-dev-id = <9>;
qcom,usb-dbm = <&dbm0_1p4>;
qcom,msm-bus,name = "usb3";
qcom,msm-bus,num-cases = <2>;
@ -2006,6 +2006,11 @@
qcom,vbus-valid-override;
};
dbm0_1p4: dbm@f92f8000 {
compatible = "qcom,usb-dbm-1p4";
reg = <0xf92f8000 0x1000>;
};
hsphy1: hsphy@f94f8800 {
compatible = "qcom,usb-hsphy";
reg = <0xf94f8800 0x3ff>;

View file

@ -301,9 +301,9 @@
vbus_dwc3-supply = <&usb3_otg>;
qcom,charging-disabled;
qcom,dwc-usb3-msm-dbm-eps = <4>;
qcom,dwc-usb3-msm-tx-fifo-size = <29696>;
qcom,dwc-usb3-msm-qdss-tx-fifo-size = <8192>;
qcom,usb-dbm = <&dbm0_1p4>;
qcom,msm-bus,name = "usb3";
qcom,msm-bus,num-cases = <2>;
@ -343,6 +343,11 @@
qcom,vdd-voltage-level = <1 5 7>;
};
dbm0_1p4: dbm@f92f8000 {
compatible = "qcom,usb-dbm-1p4";
reg = <0xf92f8000 0x1000>;
};
spmi_bus: qcom,spmi@fc4c0000 {
cell-index = <0>;
compatible = "qcom,spmi-pmic-arb";

View file

@ -1647,11 +1647,11 @@
interrupt-names = "hs_phy_irq", "pmic_id_irq";
vbus_dwc3-supply = <&pm8941_mvs1>;
qcom,dwc-usb3-msm-dbm-eps = <4>;
qcom,misc-ref = <&pm8941_misc>;
dwc_usb3-adc_tm = <&pm8941_adc_tm>;
qcom,dwc-usb3-msm-tx-fifo-size = <29696>;
qcom,dwc-usb3-msm-qdss-tx-fifo-size = <8192>;
qcom,usb-dbm = <&dbm_1p4>;
qcom,msm-bus,name = "usb3";
qcom,msm-bus,num-cases = <2>;
@ -1689,6 +1689,10 @@
qcom,vdd-voltage-level = <1 5 7>;
};
dbm_1p4: dbm@f92f8000 {
compatible = "qcom,usb-dbm-1p4";
reg = <0xf92f8000 0x1000>;
};
ehci: qcom,ehci-host@f9a55000 {
compatible = "qcom,ehci-host";
status = "disabled";

View file

@ -38,7 +38,7 @@ endif
obj-$(CONFIG_USB_DWC3) += dwc3-omap.o
obj-$(CONFIG_USB_DWC3) += dwc3-exynos.o
obj-$(CONFIG_USB_DWC3_MSM) += dwc3-msm.o
obj-$(CONFIG_USB_DWC3_MSM) += dbm-1_4.o dwc3-msm.o dbm.o
ifneq ($(CONFIG_PCI),)
obj-$(CONFIG_USB_DWC3) += dwc3-pci.o

428
drivers/usb/dwc3/dbm-1_4.c Normal file
View file

@ -0,0 +1,428 @@
/*
* Copyright (c) 2012-2013, 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;
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 dbm_ep_soft_reset(u8 dbm_ep, bool enter_reset)
{
pr_debug("%s\n", __func__);
if (dbm_ep >= dbm_data->dbm_num_eps) {
pr_err("%s: Invalid DBM ep index\n", __func__);
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;
}
/**
* 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("%s\n", __func__);
dbm_ep = msm_dbm_find_matching_dbm_ep(usb_ep);
if (dbm_ep < 0) {
pr_err("%s: Invalid usb ep index\n", __func__);
return -ENODEV;
}
/* First, reset the dbm endpoint */
dbm_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("%s\n", __func__);
dbm_ep = msm_dbm_find_matching_dbm_ep(usb_ep);
if (dbm_ep < 0) {
pr_err("%s: Invalid usb ep index\n", __func__);
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 */
dbm_ep_soft_reset(dbm_ep, true);
/*
* 10 usec delay is required before deasserting DBM endpoint reset
* according to hardware programming guide.
*/
udelay(10);
dbm_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("%s\n", __func__);
if (size < 0) {
pr_err("%s: Invalid size. size = %d", __func__, 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 >> 2);
}
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;
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");

70
drivers/usb/dwc3/dbm.c Normal file
View file

@ -0,0 +1,70 @@
/*
* Copyright (c) 2012-2013, 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/err.h>
#include <linux/of.h>
#include "dbm.h"
struct dbm_manager {
struct list_head dbm_list;
};
static struct dbm_manager dbm_manager = {
.dbm_list = LIST_HEAD_INIT(dbm_manager.dbm_list)
};
static struct dbm *of_usb_find_dbm(struct device_node *node)
{
struct dbm *dbm;
list_for_each_entry(dbm, &dbm_manager.dbm_list, head) {
if (node != dbm->dev->of_node)
continue;
return dbm;
}
return ERR_PTR(-ENODEV);
}
struct dbm *usb_get_dbm_by_phandle(struct device *dev,
const char *phandle, u8 index)
{
struct device_node *node;
if (!dev->of_node) {
dev_dbg(dev, "device does not have a device node entry\n");
return ERR_PTR(-EINVAL);
}
node = of_parse_phandle(dev->of_node, phandle, index);
if (!node) {
dev_dbg(dev, "failed to get %s phandle in %s node\n", phandle,
dev->of_node->full_name);
return ERR_PTR(-ENODEV);
}
return of_usb_find_dbm(node);
}
EXPORT_SYMBOL(usb_get_dbm_by_phandle);
int usb_add_dbm(struct dbm *x)
{
list_add_tail(&x->head, &dbm_manager.dbm_list);
return 0;
}
EXPORT_SYMBOL(usb_add_dbm);

164
drivers/usb/dwc3/dbm.h Normal file
View file

@ -0,0 +1,164 @@
/* Copyright (c) 2012-2013, 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.
*/
#ifndef __DBM_H
#define __DBM_H
#include <linux/types.h>
#include <linux/usb/gadget.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 device *dev;
struct list_head head;
/* Reset the DBM registers upon initialization */
int (*soft_reset)(bool reset);
/* Configure a USB DBM ep to work in BAM mode */
int (*ep_config)(u8 usb_ep, u8 bam_pipe,
bool producer, bool disable_wb,
bool internal_mem, bool ioc);
/* Configure a USB DBM ep to work in normal mode */
int (*ep_unconfig)(u8 usb_ep);
/* Return number of configured DBM endpoints */
int (*get_num_of_eps_configured)(void);
/* Configure the DBM with the USB3 core event buffer */
int (*event_buffer_config)(u32 addr_lo, u32 addr_hi, int size);
/* Configure the DBM with the BAM's data fifo */
int (*data_fifo_config)(u8 dep_num, phys_addr_t addr,
u32 size, u8 dst_pipe_idx);
/* Configure DBM speed : hs/ss */
void (*set_speed)(bool speed);
/* Enable DBM */
void (*enable)(void);
};
struct dbm *usb_get_dbm_by_phandle(struct device *dev,
const char *phandle, u8 index);
int usb_add_dbm(struct dbm *x);
#define CHECK_DBM_PTR_INT(dbm) do { \
if (!(dbm)) { \
pr_err("Can't call %s, dbp pointer == NULL\n", __func__); \
return -EPERM; \
} \
} while (0)
#define CHECK_DBM_PTR_VOID(dbm) do { \
if (!(dbm)) { \
pr_err("Can't call %s, dbp pointer == NULL\n", __func__); \
return; \
} \
} while (0)
static inline int dbm_soft_reset(struct dbm *dbm, bool enter_reset)
{
CHECK_DBM_PTR_INT(dbm);
return dbm->soft_reset(enter_reset);
}
static inline int dbm_ep_config(struct dbm *dbm, u8 usb_ep, u8 bam_pipe,
bool producer, bool disable_wb, bool internal_mem,
bool ioc)
{
CHECK_DBM_PTR_INT(dbm);
return dbm->ep_config(usb_ep, bam_pipe, producer, disable_wb,
internal_mem, ioc);
}
static inline int dbm_ep_unconfig(struct dbm *dbm, u8 usb_ep)
{
CHECK_DBM_PTR_INT(dbm);
return dbm->ep_unconfig(usb_ep);
}
static inline int dbm_get_num_of_eps_configured(struct dbm *dbm)
{
CHECK_DBM_PTR_INT(dbm);
return dbm->get_num_of_eps_configured();
}
static inline int dbm_event_buffer_config(struct dbm *dbm, u32 addr_lo,
u32 addr_hi, int size)
{
CHECK_DBM_PTR_INT(dbm);
return dbm->event_buffer_config(addr_lo, addr_hi, size);
}
static inline int dbm_data_fifo_config(struct dbm *dbm, u8 dep_num,
phys_addr_t addr, u32 size, u8 dst_pipe_idx)
{
CHECK_DBM_PTR_INT(dbm);
return dbm->data_fifo_config(dep_num, addr, size, dst_pipe_idx);
}
static inline void dbm_set_speed(struct dbm *dbm, bool speed)
{
CHECK_DBM_PTR_VOID(dbm);
dbm->set_speed(speed);
}
static inline void dbm_enable(struct dbm *dbm)
{
CHECK_DBM_PTR_VOID(dbm);
dbm->enable();
}
#endif /* __DBM_H */

View file

@ -51,6 +51,7 @@
#include "dwc3_otg.h"
#include "core.h"
#include "gadget.h"
#include "dbm.h"
#include "debug.h"
/* ADC threshold values */
@ -75,68 +76,6 @@ static bool prop_chg_detect;
module_param(prop_chg_detect, bool, S_IRUGO | S_IWUSR);
MODULE_PARM_DESC(prop_chg_detect, "Enable Proprietary charger detection");
/**
* USB DBM Hardware registers.
*
*/
#define DBM_BASE 0x000F8000
#define DBM_EP_CFG(n) (DBM_BASE + (0x00 + 4 * (n)))
#define DBM_DATA_FIFO(n) (DBM_BASE + (0x10 + 4 * (n)))
#define DBM_DATA_FIFO_SIZE(n) (DBM_BASE + (0x20 + 4 * (n)))
#define DBM_DATA_FIFO_EN (DBM_BASE + (0x30))
#define DBM_GEVNTADR (DBM_BASE + (0x34))
#define DBM_GEVNTSIZ (DBM_BASE + (0x38))
#define DBM_DBG_CNFG (DBM_BASE + (0x3C))
#define DBM_HW_TRB0_EP(n) (DBM_BASE + (0x40 + 4 * (n)))
#define DBM_HW_TRB1_EP(n) (DBM_BASE + (0x50 + 4 * (n)))
#define DBM_HW_TRB2_EP(n) (DBM_BASE + (0x60 + 4 * (n)))
#define DBM_HW_TRB3_EP(n) (DBM_BASE + (0x70 + 4 * (n)))
#define DBM_PIPE_CFG (DBM_BASE + (0x80))
#define DBM_SOFT_RESET (DBM_BASE + (0x84))
#define DBM_GEN_CFG (DBM_BASE + (0x88))
#define DBM_GEVNTADR_LSB (DBM_BASE + (0x98))
#define DBM_GEVNTADR_MSB (DBM_BASE + (0x9C))
#define DBM_DATA_FIFO_LSB(n) (DBM_BASE + (0xA0 + 8 * (n)))
#define DBM_DATA_FIFO_MSB(n) (DBM_BASE + (0xA4 + 8 * (n)))
/**
* 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
#define DBM_MAX_EPS 8
/* 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)
#define USB3_PORTSC (0x430)
#define PORT_PE (0x1 << 1)
/**
@ -185,8 +124,6 @@ struct dwc3_msm {
void __iomem *base;
struct resource *io_res;
struct platform_device *dwc3;
int dbm_num_eps;
u8 ep_num_mapping[DBM_MAX_EPS];
const struct usb_ep_ops *original_ep_ops[DWC3_ENDPOINTS_NUM];
struct list_head req_complete_list;
struct clk *xo_clk;
@ -201,6 +138,8 @@ struct dwc3_msm {
struct usb_phy *hs_phy, *ss_phy;
struct dbm *dbm;
/* VBUS regulator if no OTG and running in host only mode */
struct regulator *vbus_otg;
struct dwc3_ext_xceiv ext_xceiv;
@ -394,207 +333,6 @@ static void dwc3_msm_dump_phy_info(struct dwc3_msm *mdwc)
PWR_EVNT_IRQ_MASK_REG));
}
/**
* Return DBM EP number according to usb endpoint number.
*
*/
static int dwc3_msm_find_matching_dbm_ep(struct dwc3_msm *mdwc, u8 usb_ep)
{
int i;
for (i = 0; i < mdwc->dbm_num_eps; i++)
if (mdwc->ep_num_mapping[i] == usb_ep)
return i;
return -ENODEV; /* Not found */
}
/**
* Return number of configured DBM endpoints.
*
*/
static int dwc3_msm_configured_dbm_ep_num(struct dwc3_msm *mdwc)
{
int i;
int count = 0;
for (i = 0; i < mdwc->dbm_num_eps; i++)
if (mdwc->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 dwc3_msm_event_buffer_config(struct dwc3_msm *mdwc,
u32 addr_lo, u32 addr_hi, u16 size)
{
dev_dbg(mdwc->dev, "%s\n", __func__);
if (sizeof(phys_addr_t) > sizeof(u32)) {
dwc3_msm_write_reg(mdwc->base, DBM_GEVNTADR_LSB, addr_lo);
dwc3_msm_write_reg(mdwc->base, DBM_GEVNTADR_MSB, addr_hi);
} else {
dwc3_msm_write_reg(mdwc->base, DBM_GEVNTADR, addr_lo);
}
dwc3_msm_write_reg_field(mdwc->base, DBM_GEVNTSIZ,
DBM_GEVNTSIZ_MASK, size);
return 0;
}
/**
* Reset the DBM registers upon initialization.
*
*/
static int dwc3_msm_dbm_soft_reset(struct dwc3_msm *mdwc, int enter_reset)
{
dev_dbg(mdwc->dev, "%s\n", __func__);
if (enter_reset) {
dev_dbg(mdwc->dev, "enter DBM reset\n");
dwc3_msm_write_reg_field(mdwc->base, DBM_SOFT_RESET,
DBM_SFT_RST_MASK, 1);
} else {
dev_dbg(mdwc->dev, "exit DBM reset\n");
dwc3_msm_write_reg_field(mdwc->base, DBM_SOFT_RESET,
DBM_SFT_RST_MASK, 0);
/*enable DBM*/
dwc3_msm_write_reg_field(mdwc->base, QSCRATCH_GENERAL_CFG,
DBM_EN_MASK, 0x1);
}
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 dwc3_msm_dbm_ep_soft_reset(struct dwc3_msm *mdwc,
u8 dbm_ep, bool enter_reset)
{
dev_dbg(mdwc->dev, "%s\n", __func__);
if (dbm_ep >= mdwc->dbm_num_eps) {
dev_err(mdwc->dev, "%s: Invalid DBM ep index\n", __func__);
return -ENODEV;
}
if (enter_reset) {
dwc3_msm_write_reg_field(mdwc->base, DBM_SOFT_RESET,
DBM_SFT_RST_EPS_MASK & 1 << dbm_ep, 1);
} else {
dwc3_msm_write_reg_field(mdwc->base, DBM_SOFT_RESET,
DBM_SFT_RST_EPS_MASK & 1 << dbm_ep, 0);
}
return 0;
}
/**
* 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 dwc3_msm_dbm_ep_config(struct dwc3_msm *mdwc, u8 usb_ep, u8 bam_pipe,
bool producer, bool disable_wb,
bool internal_mem, bool ioc)
{
int dbm_ep;
u32 ep_cfg;
dev_dbg(mdwc->dev, "%s\n", __func__);
dbm_ep = dwc3_msm_find_matching_dbm_ep(mdwc, usb_ep);
if (dbm_ep < 0) {
dev_err(mdwc->dev,
"%s: Invalid usb ep index\n", __func__);
return -ENODEV;
}
/* First, reset the dbm endpoint */
dwc3_msm_dbm_ep_soft_reset(mdwc, dbm_ep, 0);
/* Set ioc bit for dbm_ep if needed */
dwc3_msm_write_reg_field(mdwc->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);
dwc3_msm_write_reg_field(mdwc->base, DBM_EP_CFG(dbm_ep),
DBM_PRODUCER | DBM_DISABLE_WB | DBM_INT_RAM_ACC, ep_cfg >> 8);
dwc3_msm_write_reg_field(mdwc->base, DBM_EP_CFG(dbm_ep), USB3_EPNUM,
usb_ep);
dwc3_msm_write_reg_field(mdwc->base, DBM_EP_CFG(dbm_ep),
DBM_BAM_PIPE_NUM, bam_pipe);
dwc3_msm_write_reg_field(mdwc->base, DBM_PIPE_CFG, 0x000000ff,
0xe4);
dwc3_msm_write_reg_field(mdwc->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 dwc3_msm_dbm_ep_unconfig(struct dwc3_msm *mdwc, u8 usb_ep)
{
int dbm_ep;
u32 data;
dev_dbg(mdwc->dev, "%s\n", __func__);
dbm_ep = dwc3_msm_find_matching_dbm_ep(mdwc, usb_ep);
if (dbm_ep < 0) {
dev_err(mdwc->dev, "%s: Invalid usb ep index\n", __func__);
return -ENODEV;
}
mdwc->ep_num_mapping[dbm_ep] = 0;
data = dwc3_msm_read_reg(mdwc->base, DBM_EP_CFG(dbm_ep));
data &= (~0x1);
dwc3_msm_write_reg(mdwc->base, DBM_EP_CFG(dbm_ep), data);
/* Reset the dbm endpoint */
dwc3_msm_dbm_ep_soft_reset(mdwc, dbm_ep, true);
/*
* 10 usec delay is required before deasserting DBM endpoint reset
* according to hardware programming guide.
*/
udelay(10);
dwc3_msm_dbm_ep_soft_reset(mdwc, dbm_ep, false);
return 0;
}
/**
* Configure the DBM with the BAM's data fifo.
@ -609,31 +347,17 @@ static int dwc3_msm_dbm_ep_unconfig(struct dwc3_msm *mdwc, u8 usb_ep)
int msm_data_fifo_config(struct usb_ep *ep, phys_addr_t addr,
u32 size, u8 dst_pipe_idx)
{
u8 dbm_ep;
struct dwc3_ep *dep = to_dwc3_ep(ep);
struct dwc3 *dwc = dep->dwc;
struct dwc3_msm *mdwc = dev_get_drvdata(dwc->dev->parent);
u8 bam_pipe = dst_pipe_idx;
dev_dbg(mdwc->dev, "%s\n", __func__);
dbm_ep = bam_pipe;
mdwc->ep_num_mapping[dbm_ep] = dep->number;
if (sizeof(addr) > sizeof(u32)) {
u32 lo = lower_32_bits(addr);
u32 hi = upper_32_bits(addr);
dwc3_msm_write_reg(mdwc->base, DBM_DATA_FIFO_LSB(dbm_ep), lo);
dwc3_msm_write_reg(mdwc->base, DBM_DATA_FIFO_MSB(dbm_ep), hi);
} else {
dwc3_msm_write_reg(mdwc->base, DBM_DATA_FIFO(dbm_ep), addr);
}
dwc3_msm_write_reg_field(mdwc->base, DBM_DATA_FIFO_SIZE(dbm_ep),
DBM_DATA_FIFO_SIZE_MASK, size);
return 0;
return dbm_data_fifo_config(mdwc->dbm, dep->number, addr, size,
dst_pipe_idx);
}
/**
* Cleanups for msm endpoint on request complete.
*
@ -642,7 +366,7 @@ int msm_data_fifo_config(struct usb_ep *ep, phys_addr_t addr,
* @usb_ep - pointer to usb_ep instance.
* @request - pointer to usb_request instance.
*
* @return int - 0 on success, negetive on error.
* @return int - 0 on success, negative on error.
*/
static void dwc3_msm_req_complete_func(struct usb_ep *ep,
struct usb_request *request)
@ -672,14 +396,14 @@ static void dwc3_msm_req_complete_func(struct usb_ep *ep,
dep->busy_slot++;
/* Unconfigure dbm ep */
dwc3_msm_dbm_ep_unconfig(mdwc, dep->number);
dbm_ep_unconfig(mdwc->dbm, dep->number);
/*
* If this is the last endpoint we unconfigured, than reset also
* the event buffers.
*/
if (0 == dwc3_msm_configured_dbm_ep_num(mdwc))
dwc3_msm_event_buffer_config(mdwc, 0, 0, 0);
if (0 == dbm_get_num_of_eps_configured(mdwc->dbm))
dbm_event_buffer_config(mdwc->dbm, 0, 0, 0);
/*
* Call original complete function, notice that dwc->lock is already
@ -699,7 +423,7 @@ static void dwc3_msm_req_complete_func(struct usb_ep *ep,
* @dwc3_ep - pointer to dwc3_ep instance.
* @req - pointer to dwc3_request instance.
*
* @return int - 0 on success, negetive on error.
* @return int - 0 on success, negative on error.
*/
static int __dwc3_msm_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req)
{
@ -727,7 +451,8 @@ static int __dwc3_msm_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req)
req->trb = trb;
trb->bph = DBM_TRB_BIT | DBM_TRB_DMA | DBM_TRB_EP_NUM(dep->number);
trb->size = DWC3_TRB_SIZE_LENGTH(req->request.length);
trb->ctrl = DWC3_TRBCTL_NORMAL | DWC3_TRB_CTRL_HWO | DWC3_TRB_CTRL_CHN;
trb->ctrl = DWC3_TRBCTL_NORMAL | DWC3_TRB_CTRL_HWO |
DWC3_TRB_CTRL_CHN | (req->direction ? 0 : DWC3_TRB_CTRL_CSP);
req->trb_dma = dwc3_trb_dma_offset(dep, trb);
/* Second, prepare a Link TRB that points to the first TRB*/
@ -770,7 +495,7 @@ static int __dwc3_msm_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req)
* was enabled by the ep_enable.
*
* This function prepares special structure of TRBs which
* is familier with the DBM HW, so it will possible to use
* is familiar with the DBM HW, so it will possible to use
* this endpoint in DBM mode.
*
* The TRBs prepared by this function, is one normal TRB
@ -784,7 +509,7 @@ static int __dwc3_msm_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req)
* @request - pointer to usb_request instance.
* @gfp_flags - possible flags.
*
* @return int - 0 on success, negetive on error.
* @return int - 0 on success, negative on error.
*/
static int dwc3_msm_ep_queue(struct usb_ep *ep,
struct usb_request *request, gfp_t gfp_flags)
@ -862,13 +587,11 @@ static int dwc3_msm_ep_queue(struct usb_ep *ep,
internal_mem = ((request->udc_priv & MSM_INTERNAL_MEM) ? true : false);
ioc = ((request->udc_priv & MSM_ETD_IOC) ? true : false);
ret = dwc3_msm_dbm_ep_config(mdwc, dep->number,
bam_pipe, producer,
disable_wb, internal_mem, ioc);
ret = dbm_ep_config(mdwc->dbm, dep->number, bam_pipe, producer,
disable_wb, internal_mem, ioc);
if (ret < 0) {
dev_err(mdwc->dev,
"error %d after calling dwc3_msm_dbm_ep_config\n",
ret);
"error %d after calling dbm_ep_config\n", ret);
return ret;
}
@ -892,7 +615,7 @@ static int dwc3_msm_ep_queue(struct usb_ep *ep,
}
speed = dwc3_readl(dwc->regs, DWC3_DSTS) & DWC3_DSTS_CONNECTSPD;
dwc3_msm_write_reg(mdwc->base, DBM_GEN_CFG, speed >> 2);
dbm_set_speed(mdwc->dbm, speed >> 2);
return 0;
}
@ -918,10 +641,6 @@ int msm_ep_config(struct usb_ep *ep)
struct dwc3_msm *mdwc = dev_get_drvdata(dwc->dev->parent);
struct usb_ep_ops *new_ep_ops;
dwc3_msm_event_buffer_config(mdwc,
dwc3_msm_read_reg(mdwc->base, DWC3_GEVNTADRLO(0)),
dwc3_msm_read_reg(mdwc->base, DWC3_GEVNTADRHI(0)),
dwc3_msm_read_reg(mdwc->base, DWC3_GEVNTSIZ(0)));
/* Save original ep ops for future restore*/
if (mdwc->original_ep_ops[dep->number]) {
@ -962,7 +681,7 @@ EXPORT_SYMBOL(msm_ep_config);
*
* @ep - a pointer to some usb_ep instance
*
* @return int - 0 on success, negetive on error.
* @return int - 0 on success, negative on error.
*/
int msm_ep_unconfig(struct usb_ep *ep)
{
@ -1285,9 +1004,20 @@ static void dwc3_msm_block_reset(struct dwc3_ext_xceiv *xceiv, bool core_reset)
}
/* Reset the DBM */
dwc3_msm_dbm_soft_reset(mdwc, 1);
dbm_soft_reset(mdwc->dbm, 1);
usleep_range(1000, 1200);
dwc3_msm_dbm_soft_reset(mdwc, 0);
dbm_soft_reset(mdwc->dbm, 0);
/*enable DBM*/
dwc3_msm_write_reg_field(mdwc->base, QSCRATCH_GENERAL_CFG,
DBM_EN_MASK, 0x1);
dbm_event_buffer_config(mdwc->dbm,
dwc3_msm_read_reg(mdwc->base, DWC3_GEVNTADRLO(0)),
dwc3_msm_read_reg(mdwc->base, DWC3_GEVNTADRHI(0)),
dwc3_msm_read_reg(mdwc->base, DWC3_GEVNTSIZ(0)));
dbm_enable(mdwc->dbm);
}
static void dwc3_block_reset_usb_work(struct work_struct *w)
@ -2623,21 +2353,16 @@ static int dwc3_msm_probe(struct platform_device *pdev)
mdwc->io_res = res; /* used to calculate chg block offset */
if (of_property_read_u32(node, "qcom,dwc-usb3-msm-dbm-eps",
&mdwc->dbm_num_eps)) {
dev_err(&pdev->dev,
"unable to read platform data num of dbm eps\n");
mdwc->dbm_num_eps = DBM_MAX_EPS;
}
if (of_get_property(pdev->dev.of_node, "qcom,usb-dbm", NULL)) {
mdwc->dbm = usb_get_dbm_by_phandle(&pdev->dev, "qcom,usb-dbm",
0);
if (IS_ERR(mdwc->dbm)) {
dev_err(&pdev->dev, "unable to get dbm device\n");
ret = -EPROBE_DEFER;
goto disable_ref_clk;
}
}
if (mdwc->dbm_num_eps > DBM_MAX_EPS) {
dev_err(&pdev->dev,
"Driver doesn't support number of DBM EPs. "
"max: %d, dbm_num_eps: %d\n",
DBM_MAX_EPS, mdwc->dbm_num_eps);
ret = -ENODEV;
goto disable_ref_clk;
}
if (of_property_read_u32(node, "qcom,dwc-usb3-msm-tx-fifo-size",
&mdwc->tx_fifo_size))