USB/PHY patches for 4.15-rc1

Here is the big set of USB and PHY driver updates for 4.15-rc1.
 
 There is the usual amount of gadget and xhci driver updates, along with
 phy and chipidea enhancements.  There's also a lot of SPDX tags and
 license boilerplate cleanups as well, which provide some churn in the
 diffstat.
 
 Other major thing is the typec code that moved out of staging and into
 the "real" part of the drivers/usb/ tree, which was nice to see happen.
 
 All of these have been in linux-next with no reported issues for a
 while.
 
 Signed-off-by: Greg Kroah-Hartman <[email protected]>
 -----BEGIN PGP SIGNATURE-----
 
 iG0EABECAC0WIQT0tgzFv3jCIUoxPcsxR9QN2y37KQUCWgm/Vw8cZ3JlZ0Brcm9h
 aC5jb20ACgkQMUfUDdst+yktXwCdGgpInfOEvOGFd83EPDL7a1ncyc4AoM5wI8yl
 1CeLipqVIN3IsMMJptvb
 =zvDI
 -----END PGP SIGNATURE-----

Merge tag 'usb-4.15-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb

Pull USB/PHY updates from Greg KH:
 "Here is the big set of USB and PHY driver updates for 4.15-rc1.

  There is the usual amount of gadget and xhci driver updates, along
  with phy and chipidea enhancements. There's also a lot of SPDX tags
  and license boilerplate cleanups as well, which provide some churn in
  the diffstat.

  Other major thing is the typec code that moved out of staging and into
  the "real" part of the drivers/usb/ tree, which was nice to see
  happen.

  All of these have been in linux-next with no reported issues for a
  while"

* tag 'usb-4.15-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb: (263 commits)
  usb: gadget: f_fs: Fix use-after-free in ffs_free_inst
  USB: usbfs: compute urb->actual_length for isochronous
  usb: core: message: remember to reset 'ret' to 0 when necessary
  USB: typec: Remove remaining redundant license text
  USB: typec: add SPDX identifiers to some files
  USB: renesas_usbhs: rcar?.h: add SPDX tags
  USB: chipidea: ci_hdrc_tegra.c: add SPDX line
  USB: host: xhci-debugfs: add SPDX lines
  USB: add SPDX identifiers to all remaining Makefiles
  usb: host: isp1362-hcd: remove a couple of redundant assignments
  USB: adutux: remove redundant variable minor
  usb: core: add a new usb_get_ptm_status() helper
  usb: core: add a 'type' parameter to usb_get_status()
  usb: core: introduce a new usb_get_std_status() helper
  usb: core: rename usb_get_status() 'type' argument to 'recip'
  usb: core: add Status Type definitions
  USB: gadget: Remove redundant license text
  USB: gadget: function: Remove redundant license text
  USB: gadget: udc: Remove redundant license text
  USB: gadget: legacy: Remove redundant license text
  ...
This commit is contained in:
Linus Torvalds 2017-11-13 21:14:07 -08:00
commit 894025f24b
764 changed files with 6609 additions and 9083 deletions

View file

@ -24,7 +24,7 @@
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/err.h>
#include <linux/extcon.h>
#include <linux/extcon-provider.h>
#include <linux/io.h>
#include <linux/interrupt.h>
#include <linux/kernel.h>
@ -926,6 +926,7 @@ static const struct sun4i_usb_phy_cfg sun8i_v3s_cfg = {
.phyctl_offset = REG_PHYCTL_A33,
.dedicated_clocks = true,
.enable_pmu_unk1 = true,
.phy0_dual_route = true,
};
static const struct sun4i_usb_phy_cfg sun50i_a64_cfg = {

View file

@ -67,3 +67,16 @@ config PHY_BRCM_SATA
help
Enable this to support the Broadcom SATA PHY.
If unsure, say N.
config PHY_BRCM_USB
tristate "Broadcom STB USB PHY driver"
depends on ARCH_BRCMSTB
depends on OF
select GENERIC_PHY
select SOC_BRCMSTB
default ARCH_BRCMSTB
help
Enable this to support the Broadcom STB USB PHY.
This driver is required by the USB XHCI, EHCI and OHCI
drivers.
If unsure, say N.

View file

@ -6,3 +6,6 @@ obj-$(CONFIG_PHY_BCM_NS_USB3) += phy-bcm-ns-usb3.o
obj-$(CONFIG_PHY_NS2_PCIE) += phy-bcm-ns2-pcie.o
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

View file

@ -12,7 +12,7 @@
*/
#include <linux/delay.h>
#include <linux/extcon.h>
#include <linux/extcon-provider.h>
#include <linux/gpio.h>
#include <linux/gpio/consumer.h>
#include <linux/init.h>

View file

@ -49,11 +49,29 @@ enum brcm_sata_phy_version {
BRCM_SATA_PHY_IPROC_SR,
};
enum brcm_sata_phy_rxaeq_mode {
RXAEQ_MODE_OFF = 0,
RXAEQ_MODE_AUTO,
RXAEQ_MODE_MANUAL,
};
static enum brcm_sata_phy_rxaeq_mode rxaeq_to_val(const char *m)
{
if (!strcmp(m, "auto"))
return RXAEQ_MODE_AUTO;
else if (!strcmp(m, "manual"))
return RXAEQ_MODE_MANUAL;
else
return RXAEQ_MODE_OFF;
}
struct brcm_sata_port {
int portnum;
struct phy *phy;
struct brcm_sata_phy *phy_priv;
bool ssc_en;
enum brcm_sata_phy_rxaeq_mode rxaeq_mode;
u32 rxaeq_val;
};
struct brcm_sata_phy {
@ -93,6 +111,15 @@ enum sata_phy_regs {
TX_ACTRL0 = 0x80,
TX_ACTRL0_TXPOL_FLIP = BIT(6),
AEQRX_REG_BANK_0 = 0xd0,
AEQ_CONTROL1 = 0x81,
AEQ_CONTROL1_ENABLE = BIT(2),
AEQ_CONTROL1_FREEZE = BIT(3),
AEQ_FRC_EQ = 0x83,
AEQ_FRC_EQ_FORCE = BIT(0),
AEQ_FRC_EQ_FORCE_VAL = BIT(1),
AEQRX_REG_BANK_1 = 0xe0,
OOB_REG_BANK = 0x150,
OOB1_REG_BANK = 0x160,
OOB_CTRL1 = 0x80,
@ -190,7 +217,7 @@ static u32 brcm_sata_phy_rd(void __iomem *pcb_base, u32 bank, u32 ofs)
#define STB_FMAX_VAL_DEFAULT 0x3df
#define STB_FMAX_VAL_SSC 0x83
static int brcm_stb_sata_init(struct brcm_sata_port *port)
static void brcm_stb_sata_ssc_init(struct brcm_sata_port *port)
{
void __iomem *base = brcm_sata_pcb_base(port);
struct brcm_sata_phy *priv = port->phy_priv;
@ -215,10 +242,47 @@ static int brcm_stb_sata_init(struct brcm_sata_port *port)
brcm_sata_phy_wr(base, TXPMD_REG_BANK, TXPMD_TX_FREQ_CTRL_CONTROL3,
~TXPMD_TX_FREQ_CTRL_CONTROL3_FMAX_MASK, tmp);
}
#define AEQ_FRC_EQ_VAL_SHIFT 2
#define AEQ_FRC_EQ_VAL_MASK 0x3f
static int brcm_stb_sata_rxaeq_init(struct brcm_sata_port *port)
{
void __iomem *base = brcm_sata_pcb_base(port);
u32 tmp = 0, reg = 0;
switch (port->rxaeq_mode) {
case RXAEQ_MODE_OFF:
return 0;
case RXAEQ_MODE_AUTO:
reg = AEQ_CONTROL1;
tmp = AEQ_CONTROL1_ENABLE | AEQ_CONTROL1_FREEZE;
break;
case RXAEQ_MODE_MANUAL:
reg = AEQ_FRC_EQ;
tmp = AEQ_FRC_EQ_FORCE | AEQ_FRC_EQ_FORCE_VAL;
if (port->rxaeq_val > AEQ_FRC_EQ_VAL_MASK)
return -EINVAL;
tmp |= port->rxaeq_val << AEQ_FRC_EQ_VAL_SHIFT;
break;
}
brcm_sata_phy_wr(base, AEQRX_REG_BANK_0, reg, ~tmp, tmp);
brcm_sata_phy_wr(base, AEQRX_REG_BANK_1, reg, ~tmp, tmp);
return 0;
}
static int brcm_stb_sata_init(struct brcm_sata_port *port)
{
brcm_stb_sata_ssc_init(port);
return brcm_stb_sata_rxaeq_init(port);
}
/* NS2 SATA PLL1 defaults were characterized by H/W group */
#define NS2_PLL1_ACTRL2_MAGIC 0x1df8
#define NS2_PLL1_ACTRL3_MAGIC 0x2b00
@ -463,6 +527,7 @@ MODULE_DEVICE_TABLE(of, brcm_sata_phy_of_match);
static int brcm_sata_phy_probe(struct platform_device *pdev)
{
const char *rxaeq_mode;
struct device *dev = &pdev->dev;
struct device_node *dn = dev->of_node, *child;
const struct of_device_id *of_id;
@ -525,6 +590,13 @@ static int brcm_sata_phy_probe(struct platform_device *pdev)
port->portnum = id;
port->phy_priv = priv;
port->phy = devm_phy_create(dev, child, &phy_ops);
port->rxaeq_mode = RXAEQ_MODE_OFF;
if (!of_property_read_string(child, "brcm,rxaeq-mode",
&rxaeq_mode))
port->rxaeq_mode = rxaeq_to_val(rxaeq_mode);
if (port->rxaeq_mode == RXAEQ_MODE_MANUAL)
of_property_read_u32(child, "brcm,rxaeq-value",
&port->rxaeq_val);
port->ssc_en = of_property_read_bool(child, "brcm,enable-ssc");
if (IS_ERR(port->phy)) {
dev_err(dev, "failed to create PHY\n");

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,50 @@
/*
* Copyright (C) 2014-2017 Broadcom
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*
* 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 _USB_BRCM_COMMON_INIT_H
#define _USB_BRCM_COMMON_INIT_H
#define USB_CTLR_MODE_HOST 0
#define USB_CTLR_MODE_DEVICE 1
#define USB_CTLR_MODE_DRD 2
#define USB_CTLR_MODE_TYPEC_PD 3
struct brcm_usb_init_params;
struct brcm_usb_init_params {
void __iomem *ctrl_regs;
void __iomem *xhci_ec_regs;
int ioc;
int ipp;
int mode;
u32 family_id;
u32 product_id;
int selected_family;
const char *family_name;
const u32 *usb_reg_bits_map;
};
void brcm_usb_set_family_map(struct brcm_usb_init_params *params);
int brcm_usb_init_get_dual_select(struct brcm_usb_init_params *params);
void brcm_usb_init_set_dual_select(struct brcm_usb_init_params *params,
int mode);
void brcm_usb_init_ipp(struct brcm_usb_init_params *ini);
void brcm_usb_init_common(struct brcm_usb_init_params *ini);
void brcm_usb_init_eohci(struct brcm_usb_init_params *ini);
void brcm_usb_init_xhci(struct brcm_usb_init_params *ini);
void brcm_usb_uninit_common(struct brcm_usb_init_params *ini);
void brcm_usb_uninit_eohci(struct brcm_usb_init_params *ini);
void brcm_usb_uninit_xhci(struct brcm_usb_init_params *ini);
#endif /* _USB_BRCM_COMMON_INIT_H */

View file

@ -0,0 +1,459 @@
/*
* phy-brcm-usb.c - Broadcom USB Phy Driver
*
* Copyright (C) 2015-2017 Broadcom
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*
* 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/clk.h>
#include <linux/delay.h>
#include <linux/err.h>
#include <linux/io.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/phy/phy.h>
#include <linux/platform_device.h>
#include <linux/interrupt.h>
#include <linux/soc/brcmstb/brcmstb.h>
#include <dt-bindings/phy/phy.h>
#include "phy-brcm-usb-init.h"
static DEFINE_MUTEX(sysfs_lock);
enum brcm_usb_phy_id {
BRCM_USB_PHY_2_0 = 0,
BRCM_USB_PHY_3_0,
BRCM_USB_PHY_ID_MAX
};
struct value_to_name_map {
int value;
const char *name;
};
static struct value_to_name_map brcm_dr_mode_to_name[] = {
{ USB_CTLR_MODE_HOST, "host" },
{ USB_CTLR_MODE_DEVICE, "peripheral" },
{ USB_CTLR_MODE_DRD, "drd" },
{ USB_CTLR_MODE_TYPEC_PD, "typec-pd" }
};
static struct value_to_name_map brcm_dual_mode_to_name[] = {
{ 0, "host" },
{ 1, "device" },
{ 2, "auto" },
};
struct brcm_usb_phy {
struct phy *phy;
unsigned int id;
bool inited;
};
struct brcm_usb_phy_data {
struct brcm_usb_init_params ini;
bool has_eohci;
bool has_xhci;
struct clk *usb_20_clk;
struct clk *usb_30_clk;
struct mutex mutex; /* serialize phy init */
int init_count;
struct brcm_usb_phy phys[BRCM_USB_PHY_ID_MAX];
};
static int brcm_usb_phy_init(struct phy *gphy)
{
struct brcm_usb_phy *phy = phy_get_drvdata(gphy);
struct brcm_usb_phy_data *priv =
container_of(phy, struct brcm_usb_phy_data, phys[phy->id]);
/*
* Use a lock to make sure a second caller waits until
* the base phy is inited before using it.
*/
mutex_lock(&priv->mutex);
if (priv->init_count++ == 0) {
clk_enable(priv->usb_20_clk);
clk_enable(priv->usb_30_clk);
brcm_usb_init_common(&priv->ini);
}
mutex_unlock(&priv->mutex);
if (phy->id == BRCM_USB_PHY_2_0)
brcm_usb_init_eohci(&priv->ini);
else if (phy->id == BRCM_USB_PHY_3_0)
brcm_usb_init_xhci(&priv->ini);
phy->inited = true;
dev_dbg(&gphy->dev, "INIT, id: %d, total: %d\n", phy->id,
priv->init_count);
return 0;
}
static int brcm_usb_phy_exit(struct phy *gphy)
{
struct brcm_usb_phy *phy = phy_get_drvdata(gphy);
struct brcm_usb_phy_data *priv =
container_of(phy, struct brcm_usb_phy_data, phys[phy->id]);
dev_dbg(&gphy->dev, "EXIT\n");
if (phy->id == BRCM_USB_PHY_2_0)
brcm_usb_uninit_eohci(&priv->ini);
if (phy->id == BRCM_USB_PHY_3_0)
brcm_usb_uninit_xhci(&priv->ini);
/* If both xhci and eohci are gone, reset everything else */
mutex_lock(&priv->mutex);
if (--priv->init_count == 0) {
brcm_usb_uninit_common(&priv->ini);
clk_disable(priv->usb_20_clk);
clk_disable(priv->usb_30_clk);
}
mutex_unlock(&priv->mutex);
phy->inited = false;
return 0;
}
static struct phy_ops brcm_usb_phy_ops = {
.init = brcm_usb_phy_init,
.exit = brcm_usb_phy_exit,
.owner = THIS_MODULE,
};
static struct phy *brcm_usb_phy_xlate(struct device *dev,
struct of_phandle_args *args)
{
struct brcm_usb_phy_data *data = dev_get_drvdata(dev);
/*
* values 0 and 1 are for backward compatibility with
* device tree nodes from older bootloaders.
*/
switch (args->args[0]) {
case 0:
case PHY_TYPE_USB2:
if (data->phys[BRCM_USB_PHY_2_0].phy)
return data->phys[BRCM_USB_PHY_2_0].phy;
dev_warn(dev, "Error, 2.0 Phy not found\n");
break;
case 1:
case PHY_TYPE_USB3:
if (data->phys[BRCM_USB_PHY_3_0].phy)
return data->phys[BRCM_USB_PHY_3_0].phy;
dev_warn(dev, "Error, 3.0 Phy not found\n");
break;
}
return ERR_PTR(-ENODEV);
}
static int name_to_value(struct value_to_name_map *table, int count,
const char *name, int *value)
{
int x;
*value = 0;
for (x = 0; x < count; x++) {
if (sysfs_streq(name, table[x].name)) {
*value = x;
return 0;
}
}
return -EINVAL;
}
static const char *value_to_name(struct value_to_name_map *table, int count,
int value)
{
if (value >= count)
return "unknown";
return table[value].name;
}
static ssize_t dr_mode_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct brcm_usb_phy_data *priv = dev_get_drvdata(dev);
return sprintf(buf, "%s\n",
value_to_name(&brcm_dr_mode_to_name[0],
ARRAY_SIZE(brcm_dr_mode_to_name),
priv->ini.mode));
}
static DEVICE_ATTR_RO(dr_mode);
static ssize_t dual_select_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t len)
{
struct brcm_usb_phy_data *priv = dev_get_drvdata(dev);
int value;
int res;
mutex_lock(&sysfs_lock);
res = name_to_value(&brcm_dual_mode_to_name[0],
ARRAY_SIZE(brcm_dual_mode_to_name), buf, &value);
if (!res) {
brcm_usb_init_set_dual_select(&priv->ini, value);
res = len;
}
mutex_unlock(&sysfs_lock);
return res;
}
static ssize_t dual_select_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct brcm_usb_phy_data *priv = dev_get_drvdata(dev);
int value;
mutex_lock(&sysfs_lock);
value = brcm_usb_init_get_dual_select(&priv->ini);
mutex_unlock(&sysfs_lock);
return sprintf(buf, "%s\n",
value_to_name(&brcm_dual_mode_to_name[0],
ARRAY_SIZE(brcm_dual_mode_to_name),
value));
}
static DEVICE_ATTR_RW(dual_select);
static struct attribute *brcm_usb_phy_attrs[] = {
&dev_attr_dr_mode.attr,
&dev_attr_dual_select.attr,
NULL
};
static const struct attribute_group brcm_usb_phy_group = {
.attrs = brcm_usb_phy_attrs,
};
static int brcm_usb_phy_dvr_init(struct device *dev,
struct brcm_usb_phy_data *priv,
struct device_node *dn)
{
struct phy *gphy;
int err;
priv->usb_20_clk = of_clk_get_by_name(dn, "sw_usb");
if (IS_ERR(priv->usb_20_clk)) {
dev_info(dev, "Clock not found in Device Tree\n");
priv->usb_20_clk = NULL;
}
err = clk_prepare_enable(priv->usb_20_clk);
if (err)
return err;
if (priv->has_eohci) {
gphy = devm_phy_create(dev, NULL, &brcm_usb_phy_ops);
if (IS_ERR(gphy)) {
dev_err(dev, "failed to create EHCI/OHCI PHY\n");
return PTR_ERR(gphy);
}
priv->phys[BRCM_USB_PHY_2_0].phy = gphy;
priv->phys[BRCM_USB_PHY_2_0].id = BRCM_USB_PHY_2_0;
phy_set_drvdata(gphy, &priv->phys[BRCM_USB_PHY_2_0]);
}
if (priv->has_xhci) {
gphy = devm_phy_create(dev, NULL, &brcm_usb_phy_ops);
if (IS_ERR(gphy)) {
dev_err(dev, "failed to create XHCI PHY\n");
return PTR_ERR(gphy);
}
priv->phys[BRCM_USB_PHY_3_0].phy = gphy;
priv->phys[BRCM_USB_PHY_3_0].id = BRCM_USB_PHY_3_0;
phy_set_drvdata(gphy, &priv->phys[BRCM_USB_PHY_3_0]);
priv->usb_30_clk = of_clk_get_by_name(dn, "sw_usb3");
if (IS_ERR(priv->usb_30_clk)) {
dev_info(dev,
"USB3.0 clock not found in Device Tree\n");
priv->usb_30_clk = NULL;
}
err = clk_prepare_enable(priv->usb_30_clk);
if (err)
return err;
}
return 0;
}
static int brcm_usb_phy_probe(struct platform_device *pdev)
{
struct resource *res;
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;
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
platform_set_drvdata(pdev, priv);
priv->ini.family_id = brcmstb_get_family_id();
priv->ini.product_id = brcmstb_get_product_id();
brcm_usb_set_family_map(&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;
}
priv->ini.ctrl_regs = devm_ioremap_resource(dev, res);
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) {
priv->ini.xhci_ec_regs =
devm_ioremap_resource(dev, res);
if (IS_ERR(priv->ini.xhci_ec_regs)) {
dev_err(dev, "can't map XHCI EC 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);
priv->ini.mode = USB_CTLR_MODE_HOST;
err = of_property_read_string(dn, "dr_mode", &mode);
if (err == 0) {
name_to_value(&brcm_dr_mode_to_name[0],
ARRAY_SIZE(brcm_dr_mode_to_name),
mode, &priv->ini.mode);
}
if (of_property_read_bool(dn, "brcm,has_xhci"))
priv->has_xhci = true;
if (of_property_read_bool(dn, "brcm,has_eohci"))
priv->has_eohci = true;
err = brcm_usb_phy_dvr_init(dev, priv, dn);
if (err)
return err;
mutex_init(&priv->mutex);
/* make sure invert settings are correct */
brcm_usb_init_ipp(&priv->ini);
/*
* Create sysfs entries for mode.
* Remove "dual_select" attribute if not in dual mode
*/
if (priv->ini.mode != USB_CTLR_MODE_DRD)
brcm_usb_phy_attrs[1] = NULL;
err = sysfs_create_group(&dev->kobj, &brcm_usb_phy_group);
if (err)
dev_warn(dev, "Error creating sysfs attributes\n");
/* start with everything off */
if (priv->has_xhci)
brcm_usb_uninit_xhci(&priv->ini);
if (priv->has_eohci)
brcm_usb_uninit_eohci(&priv->ini);
brcm_usb_uninit_common(&priv->ini);
clk_disable(priv->usb_20_clk);
clk_disable(priv->usb_30_clk);
phy_provider = devm_of_phy_provider_register(dev, brcm_usb_phy_xlate);
if (IS_ERR(phy_provider))
return PTR_ERR(phy_provider);
return 0;
}
#ifdef CONFIG_PM_SLEEP
static int brcm_usb_phy_suspend(struct device *dev)
{
struct brcm_usb_phy_data *priv = dev_get_drvdata(dev);
if (priv->init_count) {
clk_disable(priv->usb_20_clk);
clk_disable(priv->usb_30_clk);
}
return 0;
}
static int brcm_usb_phy_resume(struct device *dev)
{
struct brcm_usb_phy_data *priv = dev_get_drvdata(dev);
clk_enable(priv->usb_20_clk);
clk_enable(priv->usb_30_clk);
brcm_usb_init_ipp(&priv->ini);
/*
* Initialize anything that was previously initialized.
* Uninitialize anything that wasn't previously initialized.
*/
if (priv->init_count) {
brcm_usb_init_common(&priv->ini);
if (priv->phys[BRCM_USB_PHY_2_0].inited) {
brcm_usb_init_eohci(&priv->ini);
} else if (priv->has_eohci) {
brcm_usb_uninit_eohci(&priv->ini);
clk_disable(priv->usb_20_clk);
}
if (priv->phys[BRCM_USB_PHY_3_0].inited) {
brcm_usb_init_xhci(&priv->ini);
} else if (priv->has_xhci) {
brcm_usb_uninit_xhci(&priv->ini);
clk_disable(priv->usb_30_clk);
}
} else {
if (priv->has_xhci)
brcm_usb_uninit_xhci(&priv->ini);
if (priv->has_eohci)
brcm_usb_uninit_eohci(&priv->ini);
brcm_usb_uninit_common(&priv->ini);
clk_disable(priv->usb_20_clk);
clk_disable(priv->usb_30_clk);
}
return 0;
}
#endif /* CONFIG_PM_SLEEP */
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 = {
.probe = brcm_usb_phy_probe,
.driver = {
.name = "brcmstb-usb-phy",
.owner = THIS_MODULE,
.pm = &brcm_usb_phy_pm_ops,
.of_match_table = brcm_usb_dt_ids,
},
};
module_platform_driver(brcm_usb_driver);
MODULE_ALIAS("platform:brcmstb-usb-phy");
MODULE_AUTHOR("Al Cooper <[email protected]>");
MODULE_DESCRIPTION("BRCM USB PHY driver");
MODULE_LICENSE("GPL v2");

View file

@ -154,7 +154,6 @@ struct mvebu_comphy_priv {
void __iomem *base;
struct regmap *regmap;
struct device *dev;
int modes[MVEBU_COMPHY_LANES];
};
struct mvebu_comphy_lane {

View file

@ -96,9 +96,11 @@
#define U3P_U2PHYDTM1 0x06C
#define P2C_RG_UART_EN BIT(16)
#define P2C_FORCE_IDDIG BIT(9)
#define P2C_RG_VBUSVALID BIT(5)
#define P2C_RG_SESSEND BIT(4)
#define P2C_RG_AVALID BIT(2)
#define P2C_RG_IDDIG BIT(1)
#define U3P_U3_CHIP_GPIO_CTLD 0x0c
#define P3C_REG_IP_SW_RST BIT(31)
@ -585,6 +587,31 @@ static void u2_phy_instance_exit(struct mtk_tphy *tphy,
}
}
static void u2_phy_instance_set_mode(struct mtk_tphy *tphy,
struct mtk_phy_instance *instance,
enum phy_mode mode)
{
struct u2phy_banks *u2_banks = &instance->u2_banks;
u32 tmp;
tmp = readl(u2_banks->com + U3P_U2PHYDTM1);
switch (mode) {
case PHY_MODE_USB_DEVICE:
tmp |= P2C_FORCE_IDDIG | P2C_RG_IDDIG;
break;
case PHY_MODE_USB_HOST:
tmp |= P2C_FORCE_IDDIG;
tmp &= ~P2C_RG_IDDIG;
break;
case PHY_MODE_USB_OTG:
tmp &= ~(P2C_FORCE_IDDIG | P2C_RG_IDDIG);
break;
default:
return;
}
writel(tmp, u2_banks->com + U3P_U2PHYDTM1);
}
static void pcie_phy_instance_init(struct mtk_tphy *tphy,
struct mtk_phy_instance *instance)
{
@ -881,6 +908,17 @@ static int mtk_phy_exit(struct phy *phy)
return 0;
}
static int mtk_phy_set_mode(struct phy *phy, enum phy_mode mode)
{
struct mtk_phy_instance *instance = phy_get_drvdata(phy);
struct mtk_tphy *tphy = dev_get_drvdata(phy->dev.parent);
if (instance->type == PHY_TYPE_USB2)
u2_phy_instance_set_mode(tphy, instance, mode);
return 0;
}
static struct phy *mtk_phy_xlate(struct device *dev,
struct of_phandle_args *args)
{
@ -931,6 +969,7 @@ static const struct phy_ops mtk_tphy_ops = {
.exit = mtk_phy_exit,
.power_on = mtk_phy_power_on,
.power_off = mtk_phy_power_off,
.set_mode = mtk_phy_set_mode,
.owner = THIS_MODULE,
};

View file

@ -372,6 +372,21 @@ int phy_reset(struct phy *phy)
}
EXPORT_SYMBOL_GPL(phy_reset);
int phy_calibrate(struct phy *phy)
{
int ret;
if (!phy || !phy->ops->calibrate)
return 0;
mutex_lock(&phy->mutex);
ret = phy->ops->calibrate(phy);
mutex_unlock(&phy->mutex);
return ret;
}
EXPORT_SYMBOL_GPL(phy_calibrate);
/**
* _of_phy_get() - lookup and obtain a reference to a phy by phandle
* @np: device_node for which to get the phy

View file

@ -114,14 +114,16 @@ struct ufs_qcom_phy {
struct ufs_qcom_phy_calibration *cached_regs;
int cached_regs_table_size;
bool is_powered_on;
bool is_started;
struct ufs_qcom_phy_specific_ops *phy_spec_ops;
enum phy_mode mode;
};
/**
* struct ufs_qcom_phy_specific_ops - set of pointers to functions which have a
* specific implementation per phy. Each UFS phy, should implement
* those functions according to its spec and requirements
* @calibrate_phy: pointer to a function that calibrate the phy
* @start_serdes: pointer to a function that starts the serdes
* @is_physical_coding_sublayer_ready: pointer to a function that
* checks pcs readiness. returns 0 for success and non-zero for error.
@ -130,7 +132,6 @@ struct ufs_qcom_phy {
* and writes to QSERDES_RX_SIGDET_CNTRL attribute
*/
struct ufs_qcom_phy_specific_ops {
int (*calibrate_phy)(struct ufs_qcom_phy *phy, bool is_rate_B);
void (*start_serdes)(struct ufs_qcom_phy *phy);
int (*is_physical_coding_sublayer_ready)(struct ufs_qcom_phy *phy);
void (*set_tx_lane_enable)(struct ufs_qcom_phy *phy, u32 val);

View file

@ -44,7 +44,19 @@ void ufs_qcom_phy_qmp_14nm_advertise_quirks(struct ufs_qcom_phy *phy_common)
static int ufs_qcom_phy_qmp_14nm_init(struct phy *generic_phy)
{
return 0;
struct ufs_qcom_phy *phy_common = get_ufs_qcom_phy(generic_phy);
bool is_rate_B = false;
int ret;
if (phy_common->mode == PHY_MODE_UFS_HS_B)
is_rate_B = true;
ret = ufs_qcom_phy_qmp_14nm_phy_calibrate(phy_common, is_rate_B);
if (!ret)
/* phy calibrated, but yet to be started */
phy_common->is_started = false;
return ret;
}
static int ufs_qcom_phy_qmp_14nm_exit(struct phy *generic_phy)
@ -52,6 +64,19 @@ static int ufs_qcom_phy_qmp_14nm_exit(struct phy *generic_phy)
return 0;
}
static
int ufs_qcom_phy_qmp_14nm_set_mode(struct phy *generic_phy, enum phy_mode mode)
{
struct ufs_qcom_phy *phy_common = get_ufs_qcom_phy(generic_phy);
phy_common->mode = PHY_MODE_INVALID;
if (mode > 0)
phy_common->mode = mode;
return 0;
}
static
void ufs_qcom_phy_qmp_14nm_power_control(struct ufs_qcom_phy *phy, bool val)
{
@ -102,11 +127,11 @@ static const struct phy_ops ufs_qcom_phy_qmp_14nm_phy_ops = {
.exit = ufs_qcom_phy_qmp_14nm_exit,
.power_on = ufs_qcom_phy_power_on,
.power_off = ufs_qcom_phy_power_off,
.set_mode = ufs_qcom_phy_qmp_14nm_set_mode,
.owner = THIS_MODULE,
};
static struct ufs_qcom_phy_specific_ops phy_14nm_ops = {
.calibrate_phy = ufs_qcom_phy_qmp_14nm_phy_calibrate,
.start_serdes = ufs_qcom_phy_qmp_14nm_start_serdes,
.is_physical_coding_sublayer_ready = ufs_qcom_phy_qmp_14nm_is_pcs_ready,
.set_tx_lane_enable = ufs_qcom_phy_qmp_14nm_set_tx_lane_enable,

View file

@ -63,7 +63,19 @@ void ufs_qcom_phy_qmp_20nm_advertise_quirks(struct ufs_qcom_phy *phy_common)
static int ufs_qcom_phy_qmp_20nm_init(struct phy *generic_phy)
{
return 0;
struct ufs_qcom_phy *phy_common = get_ufs_qcom_phy(generic_phy);
bool is_rate_B = false;
int ret;
if (phy_common->mode == PHY_MODE_UFS_HS_B)
is_rate_B = true;
ret = ufs_qcom_phy_qmp_20nm_phy_calibrate(phy_common, is_rate_B);
if (!ret)
/* phy calibrated, but yet to be started */
phy_common->is_started = false;
return ret;
}
static int ufs_qcom_phy_qmp_20nm_exit(struct phy *generic_phy)
@ -71,6 +83,19 @@ static int ufs_qcom_phy_qmp_20nm_exit(struct phy *generic_phy)
return 0;
}
static
int ufs_qcom_phy_qmp_20nm_set_mode(struct phy *generic_phy, enum phy_mode mode)
{
struct ufs_qcom_phy *phy_common = get_ufs_qcom_phy(generic_phy);
phy_common->mode = PHY_MODE_INVALID;
if (mode > 0)
phy_common->mode = mode;
return 0;
}
static
void ufs_qcom_phy_qmp_20nm_power_control(struct ufs_qcom_phy *phy, bool val)
{
@ -160,11 +185,11 @@ static const struct phy_ops ufs_qcom_phy_qmp_20nm_phy_ops = {
.exit = ufs_qcom_phy_qmp_20nm_exit,
.power_on = ufs_qcom_phy_power_on,
.power_off = ufs_qcom_phy_power_off,
.set_mode = ufs_qcom_phy_qmp_20nm_set_mode,
.owner = THIS_MODULE,
};
static struct ufs_qcom_phy_specific_ops phy_20nm_ops = {
.calibrate_phy = ufs_qcom_phy_qmp_20nm_phy_calibrate,
.start_serdes = ufs_qcom_phy_qmp_20nm_start_serdes,
.is_physical_coding_sublayer_ready = ufs_qcom_phy_qmp_20nm_is_pcs_ready,
.set_tx_lane_enable = ufs_qcom_phy_qmp_20nm_set_tx_lane_enable,

View file

@ -518,9 +518,8 @@ void ufs_qcom_phy_disable_iface_clk(struct ufs_qcom_phy *phy)
}
}
int ufs_qcom_phy_start_serdes(struct phy *generic_phy)
static int ufs_qcom_phy_start_serdes(struct ufs_qcom_phy *ufs_qcom_phy)
{
struct ufs_qcom_phy *ufs_qcom_phy = get_ufs_qcom_phy(generic_phy);
int ret = 0;
if (!ufs_qcom_phy->phy_spec_ops->start_serdes) {
@ -533,7 +532,6 @@ int ufs_qcom_phy_start_serdes(struct phy *generic_phy)
return ret;
}
EXPORT_SYMBOL_GPL(ufs_qcom_phy_start_serdes);
int ufs_qcom_phy_set_tx_lane_enable(struct phy *generic_phy, u32 tx_lanes)
{
@ -564,31 +562,8 @@ void ufs_qcom_phy_save_controller_version(struct phy *generic_phy,
}
EXPORT_SYMBOL_GPL(ufs_qcom_phy_save_controller_version);
int ufs_qcom_phy_calibrate_phy(struct phy *generic_phy, bool is_rate_B)
static int ufs_qcom_phy_is_pcs_ready(struct ufs_qcom_phy *ufs_qcom_phy)
{
struct ufs_qcom_phy *ufs_qcom_phy = get_ufs_qcom_phy(generic_phy);
int ret = 0;
if (!ufs_qcom_phy->phy_spec_ops->calibrate_phy) {
dev_err(ufs_qcom_phy->dev, "%s: calibrate_phy() callback is not supported\n",
__func__);
ret = -ENOTSUPP;
} else {
ret = ufs_qcom_phy->phy_spec_ops->
calibrate_phy(ufs_qcom_phy, is_rate_B);
if (ret)
dev_err(ufs_qcom_phy->dev, "%s: calibrate_phy() failed %d\n",
__func__, ret);
}
return ret;
}
EXPORT_SYMBOL_GPL(ufs_qcom_phy_calibrate_phy);
int ufs_qcom_phy_is_pcs_ready(struct phy *generic_phy)
{
struct ufs_qcom_phy *ufs_qcom_phy = get_ufs_qcom_phy(generic_phy);
if (!ufs_qcom_phy->phy_spec_ops->is_physical_coding_sublayer_ready) {
dev_err(ufs_qcom_phy->dev, "%s: is_physical_coding_sublayer_ready() callback is not supported\n",
__func__);
@ -598,7 +573,6 @@ int ufs_qcom_phy_is_pcs_ready(struct phy *generic_phy)
return ufs_qcom_phy->phy_spec_ops->
is_physical_coding_sublayer_ready(ufs_qcom_phy);
}
EXPORT_SYMBOL_GPL(ufs_qcom_phy_is_pcs_ready);
int ufs_qcom_phy_power_on(struct phy *generic_phy)
{
@ -609,6 +583,18 @@ int ufs_qcom_phy_power_on(struct phy *generic_phy)
if (phy_common->is_powered_on)
return 0;
if (!phy_common->is_started) {
err = ufs_qcom_phy_start_serdes(phy_common);
if (err)
return err;
err = ufs_qcom_phy_is_pcs_ready(phy_common);
if (err)
return err;
phy_common->is_started = true;
}
err = ufs_qcom_phy_enable_vreg(dev, &phy_common->vdda_phy);
if (err) {
dev_err(dev, "%s enable vdda_phy failed, err=%d\n",

View file

@ -1,7 +1,7 @@
/*
* Renesas R-Car Gen3 for USB2.0 PHY driver
*
* Copyright (C) 2015 Renesas Electronics Corporation
* Copyright (C) 2015-2017 Renesas Electronics Corporation
*
* This is based on the phy-rcar-gen2 driver:
* Copyright (C) 2014 Renesas Solutions Corp.
@ -12,16 +12,18 @@
* published by the Free Software Foundation.
*/
#include <linux/extcon.h>
#include <linux/extcon-provider.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_device.h>
#include <linux/phy/phy.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/regulator/consumer.h>
#include <linux/usb/of.h>
#include <linux/workqueue.h>
/******* USB2.0 Host registers (original offset is +0x200) *******/
@ -79,6 +81,8 @@
#define USB2_ADPCTRL_IDPULLUP BIT(5) /* 1 = ID sampling is enabled */
#define USB2_ADPCTRL_DRVVBUS BIT(4)
#define RCAR_GEN3_PHY_HAS_DEDICATED_PINS 1
struct rcar_gen3_chan {
void __iomem *base;
struct extcon_dev *extcon;
@ -86,7 +90,7 @@ struct rcar_gen3_chan {
struct regulator *vbus;
struct work_struct work;
bool extcon_host;
bool has_otg;
bool has_otg_pins;
};
static void rcar_gen3_phy_usb2_work(struct work_struct *work)
@ -218,33 +222,40 @@ static bool rcar_gen3_is_host(struct rcar_gen3_chan *ch)
return !(readl(ch->base + USB2_COMMCTRL) & USB2_COMMCTRL_OTG_PERI);
}
static enum phy_mode rcar_gen3_get_phy_mode(struct rcar_gen3_chan *ch)
{
if (rcar_gen3_is_host(ch))
return PHY_MODE_USB_HOST;
return PHY_MODE_USB_DEVICE;
}
static ssize_t role_store(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
struct rcar_gen3_chan *ch = dev_get_drvdata(dev);
bool is_b_device, is_host, new_mode_is_host;
bool is_b_device;
enum phy_mode cur_mode, new_mode;
if (!ch->has_otg || !ch->phy->init_count)
if (!ch->has_otg_pins || !ch->phy->init_count)
return -EIO;
/*
* is_b_device: true is B-Device. false is A-Device.
* If {new_mode_}is_host: true is Host mode. false is Peripheral mode.
*/
is_b_device = rcar_gen3_check_id(ch);
is_host = rcar_gen3_is_host(ch);
if (!strncmp(buf, "host", strlen("host")))
new_mode_is_host = true;
new_mode = PHY_MODE_USB_HOST;
else if (!strncmp(buf, "peripheral", strlen("peripheral")))
new_mode_is_host = false;
new_mode = PHY_MODE_USB_DEVICE;
else
return -EINVAL;
/* is_b_device: true is B-Device. false is A-Device. */
is_b_device = rcar_gen3_check_id(ch);
cur_mode = rcar_gen3_get_phy_mode(ch);
/* If current and new mode is the same, this returns the error */
if (is_host == new_mode_is_host)
if (cur_mode == new_mode)
return -EINVAL;
if (new_mode_is_host) { /* And is_host must be false */
if (new_mode == PHY_MODE_USB_HOST) { /* And is_host must be false */
if (!is_b_device) /* A-Peripheral */
rcar_gen3_init_from_a_peri_to_a_host(ch);
else /* B-Peripheral */
@ -264,7 +275,7 @@ static ssize_t role_show(struct device *dev, struct device_attribute *attr,
{
struct rcar_gen3_chan *ch = dev_get_drvdata(dev);
if (!ch->has_otg || !ch->phy->init_count)
if (!ch->has_otg_pins || !ch->phy->init_count)
return -EIO;
return sprintf(buf, "%s\n", rcar_gen3_is_host(ch) ? "host" :
@ -303,7 +314,7 @@ static int rcar_gen3_phy_usb2_init(struct phy *p)
writel(USB2_OC_TIMSET_INIT, usb2_base + USB2_OC_TIMSET);
/* Initialize otg part */
if (channel->has_otg)
if (channel->has_otg_pins)
rcar_gen3_init_otg(channel);
return 0;
@ -377,9 +388,17 @@ static irqreturn_t rcar_gen3_phy_usb2_irq(int irq, void *_ch)
}
static const struct of_device_id rcar_gen3_phy_usb2_match_table[] = {
{ .compatible = "renesas,usb2-phy-r8a7795" },
{ .compatible = "renesas,usb2-phy-r8a7796" },
{ .compatible = "renesas,rcar-gen3-usb2-phy" },
{
.compatible = "renesas,usb2-phy-r8a7795",
.data = (void *)RCAR_GEN3_PHY_HAS_DEDICATED_PINS,
},
{
.compatible = "renesas,usb2-phy-r8a7796",
.data = (void *)RCAR_GEN3_PHY_HAS_DEDICATED_PINS,
},
{
.compatible = "renesas,rcar-gen3-usb2-phy",
},
{ }
};
MODULE_DEVICE_TABLE(of, rcar_gen3_phy_usb2_match_table);
@ -415,14 +434,17 @@ static int rcar_gen3_phy_usb2_probe(struct platform_device *pdev)
/* call request_irq for OTG */
irq = platform_get_irq(pdev, 0);
if (irq >= 0) {
int ret;
INIT_WORK(&channel->work, rcar_gen3_phy_usb2_work);
irq = devm_request_irq(dev, irq, rcar_gen3_phy_usb2_irq,
IRQF_SHARED, dev_name(dev), channel);
if (irq < 0)
dev_err(dev, "No irq handler (%d)\n", irq);
channel->has_otg = true;
}
if (of_usb_get_dr_mode_by_phy(dev->of_node, 0) == USB_DR_MODE_OTG) {
int ret;
channel->has_otg_pins = (uintptr_t)of_device_get_match_data(dev);
channel->extcon = devm_extcon_dev_allocate(dev,
rcar_gen3_phy_cable);
if (IS_ERR(channel->extcon))
@ -464,7 +486,7 @@ static int rcar_gen3_phy_usb2_probe(struct platform_device *pdev)
dev_err(dev, "Failed to register PHY provider\n");
ret = PTR_ERR(provider);
goto error;
} else if (channel->has_otg) {
} else if (channel->has_otg_pins) {
int ret;
ret = device_create_file(dev, &dev_attr_role);
@ -484,7 +506,7 @@ static int rcar_gen3_phy_usb2_remove(struct platform_device *pdev)
{
struct rcar_gen3_chan *channel = platform_get_drvdata(pdev);
if (channel->has_otg)
if (channel->has_otg_pins)
device_remove_file(&pdev->dev, &dev_attr_role);
pm_runtime_disable(&pdev->dev);

View file

@ -17,7 +17,7 @@
#include <linux/clk.h>
#include <linux/clk-provider.h>
#include <linux/delay.h>
#include <linux/extcon.h>
#include <linux/extcon-provider.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/gpio/consumer.h>

View file

@ -102,9 +102,40 @@
#define CMN_PLL1_SS_CTRL1 (0xb8 << 2)
#define CMN_PLL1_SS_CTRL2 (0xb9 << 2)
#define CMN_RXCAL_OVRD (0xd1 << 2)
#define CMN_TXPUCAL_CTRL (0xe0 << 2)
#define CMN_TXPUCAL_OVRD (0xe1 << 2)
#define CMN_TXPDCAL_CTRL (0xf0 << 2)
#define CMN_TXPDCAL_OVRD (0xf1 << 2)
/* For CMN_TXPUCAL_CTRL, CMN_TXPDCAL_CTRL */
#define CMN_TXPXCAL_START BIT(15)
#define CMN_TXPXCAL_DONE BIT(14)
#define CMN_TXPXCAL_NO_RESPONSE BIT(13)
#define CMN_TXPXCAL_CURRENT_RESPONSE BIT(12)
#define CMN_TXPU_ADJ_CTRL (0x108 << 2)
#define CMN_TXPD_ADJ_CTRL (0x10c << 2)
/*
* For CMN_TXPUCAL_CTRL, CMN_TXPDCAL_CTRL,
* CMN_TXPU_ADJ_CTRL, CMN_TXPDCAL_CTRL
*
* NOTE: some of these registers are documented to be 2's complement
* signed numbers, but then documented to be always positive. Weird.
* In such a case, using CMN_CALIB_CODE_POS() avoids the unnecessary
* sign extension.
*/
#define CMN_CALIB_CODE_WIDTH 7
#define CMN_CALIB_CODE_OFFSET 0
#define CMN_CALIB_CODE_MASK GENMASK(CMN_CALIB_CODE_WIDTH, 0)
#define CMN_CALIB_CODE(x) \
sign_extend32((x) >> CMN_CALIB_CODE_OFFSET, CMN_CALIB_CODE_WIDTH)
#define CMN_CALIB_CODE_POS_MASK GENMASK(CMN_CALIB_CODE_WIDTH - 1, 0)
#define CMN_CALIB_CODE_POS(x) \
(((x) >> CMN_CALIB_CODE_OFFSET) & CMN_CALIB_CODE_POS_MASK)
#define CMN_DIAG_PLL0_FBH_OVRD (0x1c0 << 2)
#define CMN_DIAG_PLL0_FBL_OVRD (0x1c1 << 2)
#define CMN_DIAG_PLL0_OVRD (0x1c2 << 2)
@ -138,6 +169,15 @@
#define TX_TXCC_MGNFS_MULT_101(n) ((0x4055 | ((n) << 9)) << 2)
#define TX_TXCC_MGNFS_MULT_110(n) ((0x4056 | ((n) << 9)) << 2)
#define TX_TXCC_MGNFS_MULT_111(n) ((0x4057 | ((n) << 9)) << 2)
#define TX_TXCC_MGNLS_MULT_000(n) ((0x4058 | ((n) << 9)) << 2)
#define TX_TXCC_MGNLS_MULT_001(n) ((0x4059 | ((n) << 9)) << 2)
#define TX_TXCC_MGNLS_MULT_010(n) ((0x405a | ((n) << 9)) << 2)
#define TX_TXCC_MGNLS_MULT_011(n) ((0x405b | ((n) << 9)) << 2)
#define TX_TXCC_MGNLS_MULT_100(n) ((0x405c | ((n) << 9)) << 2)
#define TX_TXCC_MGNLS_MULT_101(n) ((0x405d | ((n) << 9)) << 2)
#define TX_TXCC_MGNLS_MULT_110(n) ((0x405e | ((n) << 9)) << 2)
#define TX_TXCC_MGNLS_MULT_111(n) ((0x405f | ((n) << 9)) << 2)
#define XCVR_DIAG_PLLDRC_CTRL(n) ((0x40e0 | ((n) << 9)) << 2)
#define XCVR_DIAG_BIDI_CTRL(n) ((0x40e8 | ((n) << 9)) << 2)
#define XCVR_DIAG_LANE_FCM_EN_MGN(n) ((0x40f2 | ((n) << 9)) << 2)
@ -150,10 +190,63 @@
#define TX_RCVDET_ST_TMR(n) ((0x4123 | ((n) << 9)) << 2)
#define TX_DIAG_TX_DRV(n) ((0x41e1 | ((n) << 9)) << 2)
#define TX_DIAG_BGREF_PREDRV_DELAY (0x41e7 << 2)
/* Use this for "n" in macros like "_MULT_XXX" to target the aux channel */
#define AUX_CH_LANE 8
#define TX_ANA_CTRL_REG_1 (0x5020 << 2)
#define TXDA_DP_AUX_EN BIT(15)
#define AUXDA_SE_EN BIT(14)
#define TXDA_CAL_LATCH_EN BIT(13)
#define AUXDA_POLARITY BIT(12)
#define TXDA_DRV_POWER_ISOLATION_EN BIT(11)
#define TXDA_DRV_POWER_EN_PH_2_N BIT(10)
#define TXDA_DRV_POWER_EN_PH_1_N BIT(9)
#define TXDA_BGREF_EN BIT(8)
#define TXDA_DRV_LDO_EN BIT(7)
#define TXDA_DECAP_EN_DEL BIT(6)
#define TXDA_DECAP_EN BIT(5)
#define TXDA_UPHY_SUPPLY_EN_DEL BIT(4)
#define TXDA_UPHY_SUPPLY_EN BIT(3)
#define TXDA_LOW_LEAKAGE_EN BIT(2)
#define TXDA_DRV_IDLE_LOWI_EN BIT(1)
#define TXDA_DRV_CMN_MODE_EN BIT(0)
#define TX_ANA_CTRL_REG_2 (0x5021 << 2)
#define AUXDA_DEBOUNCING_CLK BIT(15)
#define TXDA_LPBK_RECOVERED_CLK_EN BIT(14)
#define TXDA_LPBK_ISI_GEN_EN BIT(13)
#define TXDA_LPBK_SERIAL_EN BIT(12)
#define TXDA_LPBK_LINE_EN BIT(11)
#define TXDA_DRV_LDO_REDC_SINKIQ BIT(10)
#define XCVR_DECAP_EN_DEL BIT(9)
#define XCVR_DECAP_EN BIT(8)
#define TXDA_MPHY_ENABLE_HS_NT BIT(7)
#define TXDA_MPHY_SA_MODE BIT(6)
#define TXDA_DRV_LDO_RBYR_FB_EN BIT(5)
#define TXDA_DRV_RST_PULL_DOWN BIT(4)
#define TXDA_DRV_LDO_BG_FB_EN BIT(3)
#define TXDA_DRV_LDO_BG_REF_EN BIT(2)
#define TXDA_DRV_PREDRV_EN_DEL BIT(1)
#define TXDA_DRV_PREDRV_EN BIT(0)
#define TXDA_COEFF_CALC_CTRL (0x5022 << 2)
#define TX_HIGH_Z BIT(6)
#define TX_VMARGIN_OFFSET 3
#define TX_VMARGIN_MASK 0x7
#define LOW_POWER_SWING_EN BIT(2)
#define TX_FCM_DRV_MAIN_EN BIT(1)
#define TX_FCM_FULL_MARGIN BIT(0)
#define TX_DIG_CTRL_REG_2 (0x5024 << 2)
#define TX_HIGH_Z_TM_EN BIT(15)
#define TX_RESCAL_CODE_OFFSET 0
#define TX_RESCAL_CODE_MASK 0x3f
#define TXDA_CYA_AUXDA_CYA (0x5025 << 2)
#define TX_ANA_CTRL_REG_3 (0x5026 << 2)
#define TX_ANA_CTRL_REG_4 (0x5027 << 2)
@ -456,54 +549,72 @@ static void tcphy_dp_aux_set_flip(struct rockchip_typec_phy *tcphy)
*/
tx_ana_ctrl_reg_1 = readl(tcphy->base + TX_ANA_CTRL_REG_1);
if (!tcphy->flip)
tx_ana_ctrl_reg_1 |= BIT(12);
tx_ana_ctrl_reg_1 |= AUXDA_POLARITY;
else
tx_ana_ctrl_reg_1 &= ~BIT(12);
tx_ana_ctrl_reg_1 &= ~AUXDA_POLARITY;
writel(tx_ana_ctrl_reg_1, tcphy->base + TX_ANA_CTRL_REG_1);
}
static void tcphy_dp_aux_calibration(struct rockchip_typec_phy *tcphy)
{
u16 val;
u16 tx_ana_ctrl_reg_1;
u16 rdata, rdata2, val;
u16 tx_ana_ctrl_reg_2;
s32 pu_calib_code, pd_calib_code;
s32 pu_adj, pd_adj;
u16 calib;
/*
* Calculate calibration code as per docs: use an average of the
* pull down and pull up. Then add in adjustments.
*/
val = readl(tcphy->base + CMN_TXPUCAL_CTRL);
pu_calib_code = CMN_CALIB_CODE_POS(val);
val = readl(tcphy->base + CMN_TXPDCAL_CTRL);
pd_calib_code = CMN_CALIB_CODE_POS(val);
val = readl(tcphy->base + CMN_TXPU_ADJ_CTRL);
pu_adj = CMN_CALIB_CODE(val);
val = readl(tcphy->base + CMN_TXPD_ADJ_CTRL);
pd_adj = CMN_CALIB_CODE(val);
calib = (pu_calib_code + pd_calib_code) / 2 + pu_adj + pd_adj;
/* disable txda_cal_latch_en for rewrite the calibration values */
tx_ana_ctrl_reg_1 = readl(tcphy->base + TX_ANA_CTRL_REG_1);
tx_ana_ctrl_reg_1 &= ~BIT(13);
tx_ana_ctrl_reg_1 &= ~TXDA_CAL_LATCH_EN;
writel(tx_ana_ctrl_reg_1, tcphy->base + TX_ANA_CTRL_REG_1);
/*
* read a resistor calibration code from CMN_TXPUCAL_CTRL[6:0] and
* write it to TX_DIG_CTRL_REG_2[6:0], and delay 1ms to make sure it
* works.
*/
rdata = readl(tcphy->base + TX_DIG_CTRL_REG_2);
rdata = rdata & 0xffc0;
rdata2 = readl(tcphy->base + CMN_TXPUCAL_CTRL);
rdata2 = rdata2 & 0x3f;
val = rdata | rdata2;
/* write the calibration, then delay 10 ms as sample in docs */
val = readl(tcphy->base + TX_DIG_CTRL_REG_2);
val &= ~(TX_RESCAL_CODE_MASK << TX_RESCAL_CODE_OFFSET);
val |= calib << TX_RESCAL_CODE_OFFSET;
writel(val, tcphy->base + TX_DIG_CTRL_REG_2);
usleep_range(1000, 1050);
usleep_range(10000, 10050);
/*
* Enable signal for latch that sample and holds calibration values.
* Activate this signal for 1 clock cycle to sample new calibration
* values.
*/
tx_ana_ctrl_reg_1 |= BIT(13);
tx_ana_ctrl_reg_1 |= TXDA_CAL_LATCH_EN;
writel(tx_ana_ctrl_reg_1, tcphy->base + TX_ANA_CTRL_REG_1);
usleep_range(150, 200);
/* set TX Voltage Level and TX Deemphasis to 0 */
writel(0, tcphy->base + PHY_DP_TX_CTL);
/* re-enable decap */
writel(0x100, tcphy->base + TX_ANA_CTRL_REG_2);
writel(0x300, tcphy->base + TX_ANA_CTRL_REG_2);
tx_ana_ctrl_reg_1 |= BIT(3);
tx_ana_ctrl_reg_2 = XCVR_DECAP_EN;
writel(tx_ana_ctrl_reg_2, tcphy->base + TX_ANA_CTRL_REG_2);
udelay(1);
tx_ana_ctrl_reg_2 |= XCVR_DECAP_EN_DEL;
writel(tx_ana_ctrl_reg_2, tcphy->base + TX_ANA_CTRL_REG_2);
writel(0, tcphy->base + TX_ANA_CTRL_REG_3);
tx_ana_ctrl_reg_1 |= TXDA_UPHY_SUPPLY_EN;
writel(tx_ana_ctrl_reg_1, tcphy->base + TX_ANA_CTRL_REG_1);
tx_ana_ctrl_reg_1 |= BIT(4);
udelay(1);
tx_ana_ctrl_reg_1 |= TXDA_UPHY_SUPPLY_EN_DEL;
writel(tx_ana_ctrl_reg_1, tcphy->base + TX_ANA_CTRL_REG_1);
writel(0, tcphy->base + TX_ANA_CTRL_REG_5);
@ -515,44 +626,66 @@ static void tcphy_dp_aux_calibration(struct rockchip_typec_phy *tcphy)
writel(0x1001, tcphy->base + TX_ANA_CTRL_REG_4);
/* re-enables Bandgap reference for LDO */
tx_ana_ctrl_reg_1 |= BIT(7);
tx_ana_ctrl_reg_1 |= TXDA_DRV_LDO_EN;
writel(tx_ana_ctrl_reg_1, tcphy->base + TX_ANA_CTRL_REG_1);
tx_ana_ctrl_reg_1 |= BIT(8);
udelay(5);
tx_ana_ctrl_reg_1 |= TXDA_BGREF_EN;
writel(tx_ana_ctrl_reg_1, tcphy->base + TX_ANA_CTRL_REG_1);
/*
* re-enables the transmitter pre-driver, driver data selection MUX,
* and receiver detect circuits.
*/
writel(0x301, tcphy->base + TX_ANA_CTRL_REG_2);
writel(0x303, tcphy->base + TX_ANA_CTRL_REG_2);
tx_ana_ctrl_reg_2 |= TXDA_DRV_PREDRV_EN;
writel(tx_ana_ctrl_reg_2, tcphy->base + TX_ANA_CTRL_REG_2);
udelay(1);
tx_ana_ctrl_reg_2 |= TXDA_DRV_PREDRV_EN_DEL;
writel(tx_ana_ctrl_reg_2, tcphy->base + TX_ANA_CTRL_REG_2);
/*
* Do some magic undocumented stuff, some of which appears to
* undo the "re-enables Bandgap reference for LDO" above.
* Do all the undocumented magic:
* - Turn on TXDA_DP_AUX_EN, whatever that is, even though sample
* never shows this going on.
* - Turn on TXDA_DECAP_EN (and TXDA_DECAP_EN_DEL) even though
* docs say for aux it's always 0.
* - Turn off the LDO and BGREF, which we just spent time turning
* on above (???).
*
* Without this magic, things seem worse.
*/
tx_ana_ctrl_reg_1 |= BIT(15);
tx_ana_ctrl_reg_1 &= ~BIT(8);
tx_ana_ctrl_reg_1 &= ~BIT(7);
tx_ana_ctrl_reg_1 |= BIT(6);
tx_ana_ctrl_reg_1 |= BIT(5);
tx_ana_ctrl_reg_1 |= TXDA_DP_AUX_EN;
tx_ana_ctrl_reg_1 |= TXDA_DECAP_EN;
tx_ana_ctrl_reg_1 &= ~TXDA_DRV_LDO_EN;
tx_ana_ctrl_reg_1 &= ~TXDA_BGREF_EN;
writel(tx_ana_ctrl_reg_1, tcphy->base + TX_ANA_CTRL_REG_1);
udelay(1);
tx_ana_ctrl_reg_1 |= TXDA_DECAP_EN_DEL;
writel(tx_ana_ctrl_reg_1, tcphy->base + TX_ANA_CTRL_REG_1);
writel(0, tcphy->base + TX_ANA_CTRL_REG_3);
writel(0, tcphy->base + TX_ANA_CTRL_REG_4);
writel(0, tcphy->base + TX_ANA_CTRL_REG_5);
/*
* Controls low_power_swing_en, don't set the voltage swing of the
* driver to 400mv. The values below are peak to peak (differential)
* values.
* Undo the work we did to set the LDO voltage.
* This doesn't seem to help nor hurt, but it kinda goes with the
* undocumented magic above.
*/
writel(0, tcphy->base + TX_ANA_CTRL_REG_4);
/* Don't set voltage swing to 400 mV peak to peak (differential) */
writel(0, tcphy->base + TXDA_COEFF_CALC_CTRL);
/* Init TXDA_CYA_AUXDA_CYA for unknown magic reasons */
writel(0, tcphy->base + TXDA_CYA_AUXDA_CYA);
/* Controls tx_high_z_tm_en */
/*
* More undocumented magic, presumably the goal of which is to
* make the "auxda_source_aux_oen" be ignored and instead to decide
* about "high impedance state" based on what software puts in the
* register TXDA_COEFF_CALC_CTRL (see TX_HIGH_Z). Since we only
* program that register once and we don't set the bit TX_HIGH_Z,
* presumably the goal here is that we should never put the analog
* driver in high impedance state.
*/
val = readl(tcphy->base + TX_DIG_CTRL_REG_2);
val |= BIT(15);
val |= TX_HIGH_Z_TM_EN;
writel(val, tcphy->base + TX_DIG_CTRL_REG_2);
}

View file

@ -68,6 +68,40 @@
#define PCIE_PCS_MASK 0xFF0000
#define PCIE_PCS_DELAY_COUNT_SHIFT 0x10
#define PCIEPHYRX_ANA_PROGRAMMABILITY 0x0000000C
#define INTERFACE_MASK GENMASK(31, 27)
#define INTERFACE_SHIFT 27
#define LOSD_MASK GENMASK(17, 14)
#define LOSD_SHIFT 14
#define MEM_PLLDIV GENMASK(6, 5)
#define PCIEPHYRX_TRIM 0x0000001C
#define MEM_DLL_TRIM_SEL GENMASK(31, 30)
#define MEM_DLL_TRIM_SHIFT 30
#define PCIEPHYRX_DLL 0x00000024
#define MEM_DLL_PHINT_RATE GENMASK(31, 30)
#define PCIEPHYRX_DIGITAL_MODES 0x00000028
#define MEM_CDR_FASTLOCK BIT(23)
#define MEM_CDR_LBW GENMASK(22, 21)
#define MEM_CDR_STEPCNT GENMASK(20, 19)
#define MEM_CDR_STL_MASK GENMASK(18, 16)
#define MEM_CDR_STL_SHIFT 16
#define MEM_CDR_THR_MASK GENMASK(15, 13)
#define MEM_CDR_THR_SHIFT 13
#define MEM_CDR_THR_MODE BIT(12)
#define MEM_CDR_CDR_2NDO_SDM_MODE BIT(11)
#define MEM_OVRD_HS_RATE BIT(26)
#define PCIEPHYRX_EQUALIZER 0x00000038
#define MEM_EQLEV GENMASK(31, 16)
#define MEM_EQFTC GENMASK(15, 11)
#define MEM_EQCTL GENMASK(10, 7)
#define MEM_EQCTL_SHIFT 7
#define MEM_OVRD_EQLEV BIT(2)
#define MEM_OVRD_EQFTC BIT(1)
/*
* This is an Empirical value that works, need to confirm the actual
* value required for the PIPE3PHY_PLL_CONFIGURATION2.PLL_IDLE status
@ -91,6 +125,8 @@ struct pipe3_dpll_map {
struct ti_pipe3 {
void __iomem *pll_ctrl_base;
void __iomem *phy_rx;
void __iomem *phy_tx;
struct device *dev;
struct device *control_dev;
struct clk *wkupclk;
@ -261,6 +297,37 @@ static int ti_pipe3_dpll_program(struct ti_pipe3 *phy)
return ti_pipe3_dpll_wait_lock(phy);
}
static void ti_pipe3_calibrate(struct ti_pipe3 *phy)
{
u32 val;
val = ti_pipe3_readl(phy->phy_rx, PCIEPHYRX_ANA_PROGRAMMABILITY);
val &= ~(INTERFACE_MASK | LOSD_MASK | MEM_PLLDIV);
val = (0x1 << INTERFACE_SHIFT | 0xA << LOSD_SHIFT);
ti_pipe3_writel(phy->phy_rx, PCIEPHYRX_ANA_PROGRAMMABILITY, val);
val = ti_pipe3_readl(phy->phy_rx, PCIEPHYRX_DIGITAL_MODES);
val &= ~(MEM_CDR_STEPCNT | MEM_CDR_STL_MASK | MEM_CDR_THR_MASK |
MEM_CDR_CDR_2NDO_SDM_MODE | MEM_OVRD_HS_RATE);
val |= (MEM_CDR_FASTLOCK | MEM_CDR_LBW | 0x3 << MEM_CDR_STL_SHIFT |
0x1 << MEM_CDR_THR_SHIFT | MEM_CDR_THR_MODE);
ti_pipe3_writel(phy->phy_rx, PCIEPHYRX_DIGITAL_MODES, val);
val = ti_pipe3_readl(phy->phy_rx, PCIEPHYRX_TRIM);
val &= ~MEM_DLL_TRIM_SEL;
val |= 0x2 << MEM_DLL_TRIM_SHIFT;
ti_pipe3_writel(phy->phy_rx, PCIEPHYRX_TRIM, val);
val = ti_pipe3_readl(phy->phy_rx, PCIEPHYRX_DLL);
val |= MEM_DLL_PHINT_RATE;
ti_pipe3_writel(phy->phy_rx, PCIEPHYRX_DLL, val);
val = ti_pipe3_readl(phy->phy_rx, PCIEPHYRX_EQUALIZER);
val &= ~(MEM_EQLEV | MEM_EQCTL | MEM_OVRD_EQLEV | MEM_OVRD_EQFTC);
val |= MEM_EQFTC | 0x1 << MEM_EQCTL_SHIFT;
ti_pipe3_writel(phy->phy_rx, PCIEPHYRX_EQUALIZER, val);
}
static int ti_pipe3_init(struct phy *x)
{
struct ti_pipe3 *phy = phy_get_drvdata(x);
@ -282,7 +349,12 @@ static int ti_pipe3_init(struct phy *x)
val = 0x96 << OMAP_CTRL_PCIE_PCS_DELAY_COUNT_SHIFT;
ret = regmap_update_bits(phy->pcs_syscon, phy->pcie_pcs_reg,
PCIE_PCS_MASK, val);
return ret;
if (ret)
return ret;
ti_pipe3_calibrate(phy);
return 0;
}
/* Bring it out of IDLE if it is IDLE */
@ -513,6 +585,29 @@ static int ti_pipe3_get_sysctrl(struct ti_pipe3 *phy)
return 0;
}
static int ti_pipe3_get_tx_rx_base(struct ti_pipe3 *phy)
{
struct resource *res;
struct device *dev = phy->dev;
struct device_node *node = dev->of_node;
struct platform_device *pdev = to_platform_device(dev);
if (!of_device_is_compatible(node, "ti,phy-pipe3-pcie"))
return 0;
res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
"phy_rx");
phy->phy_rx = devm_ioremap_resource(dev, res);
if (IS_ERR(phy->phy_rx))
return PTR_ERR(phy->phy_rx);
res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
"phy_tx");
phy->phy_tx = devm_ioremap_resource(dev, res);
return PTR_ERR_OR_ZERO(phy->phy_tx);
}
static int ti_pipe3_get_pll_base(struct ti_pipe3 *phy)
{
struct resource *res;
@ -559,6 +654,10 @@ static int ti_pipe3_probe(struct platform_device *pdev)
if (ret)
return ret;
ret = ti_pipe3_get_tx_rx_base(phy);
if (ret)
return ret;
ret = ti_pipe3_get_sysctrl(phy);
if (ret)
return ret;