phy: usb: Add support for new Synopsys USB controller on the 7216

The 7216 has the new USB XHCI controller from Synopsys. While
this new controller and the PHY are similar to the STB versions,
the major differences are:

- Many of the registers and fields in the CTRL block have been
  removed or changed.
- A new set of Synopsys control registers, BCHP_USB_XHCI_GBL, were
  added.
- MDIO functionality has been replaced with direct access registers
  in the BCHP_USB_XHCI_GBL block.
- Power up PHY defaults that had to be changed by MDIO in previous
  chips will now power up with the correct defaults.

A new init module was created for this new Synopsys USB controller.
A new compatible string was added and the driver will dispatch
into one of two init modules based on it. A "reg-names" field was
added so the driver can more easily get optional registers.
A DT bindings document was also added for this driver.

Signed-off-by: Al Cooper <alcooperx@gmail.com>
Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>
Signed-off-by: Kishon Vijay Abraham I <kishon@ti.com>
This commit is contained in:
Al Cooper 2020-01-03 13:18:05 -05:00 committed by Kishon Vijay Abraham I
parent b11df0c9ef
commit 4e5b9c9a73
4 changed files with 227 additions and 18 deletions

View File

@ -8,7 +8,7 @@ obj-$(CONFIG_PHY_NS2_USB_DRD) += phy-bcm-ns2-usbdrd.o
obj-$(CONFIG_PHY_BRCM_SATA) += phy-brcm-sata.o
obj-$(CONFIG_PHY_BRCM_USB) += phy-brcm-usb-dvr.o
phy-brcm-usb-dvr-objs := phy-brcm-usb.o phy-brcm-usb-init.o
phy-brcm-usb-dvr-objs := phy-brcm-usb.o phy-brcm-usb-init.o phy-brcm-usb-init-synopsys.o
obj-$(CONFIG_PHY_BCM_SR_PCIE) += phy-bcm-sr-pcie.o
obj-$(CONFIG_PHY_BCM_SR_USB) += phy-bcm-sr-usb.o

View File

@ -0,0 +1,171 @@
// SPDX-License-Identifier: GPL-2.0
/* Copyright (c) 2018, Broadcom */
/*
* This module contains USB PHY initialization for power up and S3 resume
* for newer Synopsys based USB hardware first used on the bcm7216.
*/
#include <linux/delay.h>
#include <linux/io.h>
#include <linux/soc/brcmstb/brcmstb.h>
#include "phy-brcm-usb-init.h"
/* Register definitions for the USB CTRL block */
#define USB_CTRL_SETUP 0x00
#define USB_CTRL_SETUP_STRAP_IPP_SEL_MASK 0x02000000
#define USB_CTRL_SETUP_SCB2_EN_MASK 0x00008000
#define USB_CTRL_SETUP_SCB1_EN_MASK 0x00004000
#define USB_CTRL_SETUP_SOFT_SHUTDOWN_MASK 0x00000200
#define USB_CTRL_SETUP_IPP_MASK 0x00000020
#define USB_CTRL_SETUP_IOC_MASK 0x00000010
#define USB_CTRL_USB_PM 0x04
#define USB_CTRL_USB_PM_USB_PWRDN_MASK 0x80000000
#define USB_CTRL_USB_PM_SOFT_RESET_MASK 0x40000000
#define USB_CTRL_USB_PM_BDC_SOFT_RESETB_MASK 0x00800000
#define USB_CTRL_USB_PM_XHC_SOFT_RESETB_MASK 0x00400000
#define USB_CTRL_USB_PM_STATUS 0x08
#define USB_CTRL_USB_DEVICE_CTL1 0x10
#define USB_CTRL_USB_DEVICE_CTL1_PORT_MODE_MASK 0x00000003
static void xhci_soft_reset(struct brcm_usb_init_params *params,
int on_off)
{
void __iomem *ctrl = params->ctrl_regs;
/* Assert reset */
if (on_off)
USB_CTRL_UNSET(ctrl, USB_PM, XHC_SOFT_RESETB);
/* De-assert reset */
else
USB_CTRL_SET(ctrl, USB_PM, XHC_SOFT_RESETB);
}
static void usb_init_ipp(struct brcm_usb_init_params *params)
{
void __iomem *ctrl = params->ctrl_regs;
u32 reg;
u32 orig_reg;
pr_debug("%s\n", __func__);
orig_reg = reg = brcm_usb_readl(USB_CTRL_REG(ctrl, SETUP));
if (params->ipp != 2)
/* override ipp strap pin (if it exits) */
reg &= ~(USB_CTRL_MASK(SETUP, STRAP_IPP_SEL));
/* Override the default OC and PP polarity */
reg &= ~(USB_CTRL_MASK(SETUP, IPP) | USB_CTRL_MASK(SETUP, IOC));
if (params->ioc)
reg |= USB_CTRL_MASK(SETUP, IOC);
if (params->ipp == 1)
reg |= USB_CTRL_MASK(SETUP, IPP);
brcm_usb_writel(reg, USB_CTRL_REG(ctrl, SETUP));
/*
* If we're changing IPP, make sure power is off long enough
* to turn off any connected devices.
*/
if ((reg ^ orig_reg) & USB_CTRL_MASK(SETUP, IPP))
msleep(50);
}
static void usb_init_common(struct brcm_usb_init_params *params)
{
u32 reg;
void __iomem *ctrl = params->ctrl_regs;
pr_debug("%s\n", __func__);
USB_CTRL_UNSET(ctrl, USB_PM, USB_PWRDN);
/* 1 millisecond - for USB clocks to settle down */
usleep_range(1000, 2000);
if (USB_CTRL_MASK(USB_DEVICE_CTL1, PORT_MODE)) {
reg = brcm_usb_readl(USB_CTRL_REG(ctrl, USB_DEVICE_CTL1));
reg &= ~USB_CTRL_MASK(USB_DEVICE_CTL1, PORT_MODE);
reg |= params->mode;
brcm_usb_writel(reg, USB_CTRL_REG(ctrl, USB_DEVICE_CTL1));
}
switch (params->mode) {
case USB_CTLR_MODE_HOST:
USB_CTRL_UNSET(ctrl, USB_PM, BDC_SOFT_RESETB);
break;
default:
USB_CTRL_UNSET(ctrl, USB_PM, BDC_SOFT_RESETB);
USB_CTRL_SET(ctrl, USB_PM, BDC_SOFT_RESETB);
break;
}
}
static void usb_init_xhci(struct brcm_usb_init_params *params)
{
pr_debug("%s\n", __func__);
xhci_soft_reset(params, 0);
}
static void usb_uninit_common(struct brcm_usb_init_params *params)
{
void __iomem *ctrl = params->ctrl_regs;
pr_debug("%s\n", __func__);
USB_CTRL_SET(ctrl, USB_PM, USB_PWRDN);
}
static void usb_uninit_xhci(struct brcm_usb_init_params *params)
{
pr_debug("%s\n", __func__);
xhci_soft_reset(params, 1);
}
static int usb_get_dual_select(struct brcm_usb_init_params *params)
{
void __iomem *ctrl = params->ctrl_regs;
u32 reg = 0;
pr_debug("%s\n", __func__);
reg = brcm_usb_readl(USB_CTRL_REG(ctrl, USB_DEVICE_CTL1));
reg &= USB_CTRL_MASK(USB_DEVICE_CTL1, PORT_MODE);
return reg;
}
static void usb_set_dual_select(struct brcm_usb_init_params *params, int mode)
{
void __iomem *ctrl = params->ctrl_regs;
u32 reg;
pr_debug("%s\n", __func__);
reg = brcm_usb_readl(USB_CTRL_REG(ctrl, USB_DEVICE_CTL1));
reg &= ~USB_CTRL_MASK(USB_DEVICE_CTL1, PORT_MODE);
reg |= mode;
brcm_usb_writel(reg, USB_CTRL_REG(ctrl, USB_DEVICE_CTL1));
}
static const struct brcm_usb_init_ops bcm7216_ops = {
.init_ipp = usb_init_ipp,
.init_common = usb_init_common,
.init_xhci = usb_init_xhci,
.uninit_common = usb_uninit_common,
.uninit_xhci = usb_uninit_xhci,
.get_dual_select = usb_get_dual_select,
.set_dual_select = usb_set_dual_select,
};
void brcm_usb_dvr_init_7216(struct brcm_usb_init_params *params)
{
pr_debug("%s\n", __func__);
params->family_name = "7216";
params->ops = &bcm7216_ops;
}

View File

@ -43,6 +43,7 @@ struct brcm_usb_init_ops {
struct brcm_usb_init_params {
void __iomem *ctrl_regs;
void __iomem *xhci_ec_regs;
void __iomem *xhci_gbl_regs;
int ioc;
int ipp;
int mode;
@ -55,6 +56,7 @@ struct brcm_usb_init_params {
};
void brcm_usb_dvr_init_7445(struct brcm_usb_init_params *params);
void brcm_usb_dvr_init_7216(struct brcm_usb_init_params *params);
static inline u32 brcm_usb_readl(void __iomem *addr)
{

View File

@ -241,6 +241,15 @@ static const struct attribute_group brcm_usb_phy_group = {
.attrs = brcm_usb_phy_attrs,
};
static const struct of_device_id brcm_usb_dt_ids[] = {
{
.compatible = "brcm,bcm7216-usb-phy",
.data = &brcm_usb_dvr_init_7216,
},
{ .compatible = "brcm,brcmstb-usb-phy" },
{ /* sentinel */ }
};
static int brcm_usb_phy_dvr_init(struct platform_device *pdev,
struct brcm_usb_phy_data *priv,
struct device_node *dn)
@ -316,13 +325,16 @@ static int brcm_usb_phy_dvr_init(struct platform_device *pdev,
static int brcm_usb_phy_probe(struct platform_device *pdev)
{
struct resource *res;
struct resource *res_ctrl;
struct resource *res_xhciec = NULL;
struct resource *res_xhcigbl = NULL;
struct device *dev = &pdev->dev;
struct brcm_usb_phy_data *priv;
struct phy_provider *phy_provider;
struct device_node *dn = pdev->dev.of_node;
int err;
const char *mode;
const struct of_device_id *match;
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
@ -331,30 +343,59 @@ static int brcm_usb_phy_probe(struct platform_device *pdev)
priv->ini.family_id = brcmstb_get_family_id();
priv->ini.product_id = brcmstb_get_product_id();
brcm_usb_dvr_init_7445(&priv->ini);
match = of_match_node(brcm_usb_dt_ids, dev->of_node);
if (match && match->data) {
void (*dvr_init)(struct brcm_usb_init_params *params);
dvr_init = match->data;
(*dvr_init)(&priv->ini);
} else {
brcm_usb_dvr_init_7445(&priv->ini);
}
dev_dbg(dev, "Best mapping table is for %s\n",
priv->ini.family_name);
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res) {
dev_err(dev, "can't get USB_CTRL base address\n");
return -EINVAL;
/* Newer DT node has reg-names. xhci_ec and xhci_gbl are optional. */
res_ctrl = platform_get_resource_byname(pdev, IORESOURCE_MEM, "ctrl");
if (res_ctrl != NULL) {
res_xhciec = platform_get_resource_byname(pdev,
IORESOURCE_MEM,
"xhci_ec");
res_xhcigbl = platform_get_resource_byname(pdev,
IORESOURCE_MEM,
"xhci_gbl");
} else {
/* Older DT node without reg-names, use index */
res_ctrl = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (res_ctrl == NULL) {
dev_err(dev, "can't get CTRL base address\n");
return -EINVAL;
}
res_xhciec = platform_get_resource(pdev, IORESOURCE_MEM, 1);
}
priv->ini.ctrl_regs = devm_ioremap_resource(dev, res);
priv->ini.ctrl_regs = devm_ioremap_resource(dev, res_ctrl);
if (IS_ERR(priv->ini.ctrl_regs)) {
dev_err(dev, "can't map CTRL register space\n");
return -EINVAL;
}
/* The XHCI EC registers are optional */
res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
if (res) {
if (res_xhciec) {
priv->ini.xhci_ec_regs =
devm_ioremap_resource(dev, res);
devm_ioremap_resource(dev, res_xhciec);
if (IS_ERR(priv->ini.xhci_ec_regs)) {
dev_err(dev, "can't map XHCI EC register space\n");
return -EINVAL;
}
}
if (res_xhcigbl) {
priv->ini.xhci_gbl_regs =
devm_ioremap_resource(dev, res_xhcigbl);
if (IS_ERR(priv->ini.xhci_gbl_regs)) {
dev_err(dev, "can't map XHCI Global register space\n");
return -EINVAL;
}
}
of_property_read_u32(dn, "brcm,ipp", &priv->ini.ipp);
of_property_read_u32(dn, "brcm,ioc", &priv->ini.ioc);
@ -480,11 +521,6 @@ static const struct dev_pm_ops brcm_usb_phy_pm_ops = {
SET_LATE_SYSTEM_SLEEP_PM_OPS(brcm_usb_phy_suspend, brcm_usb_phy_resume)
};
static const struct of_device_id brcm_usb_dt_ids[] = {
{ .compatible = "brcm,brcmstb-usb-phy" },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, brcm_usb_dt_ids);
static struct platform_driver brcm_usb_driver = {