Compare commits
5
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8066ef7d79 | ||
|
|
f6add60a9c | ||
|
|
4a5481b6a5 | ||
|
|
9ff60d60db | ||
|
|
e12bb2c554 |
@@ -1,12 +1,14 @@
|
|||||||
* RV1106 USB2 PHY: single OTG port at 0xff3e0000 (ported from the vendor driver;
|
* RV1106 USB2 PHY: single OTG port at 0xff3e0000 (ported from the vendor driver;
|
||||||
* fields map 1:1 to 6.18 except utmi_iddig -> utmi_id, and the 5.10-only
|
* fields map 1:1 to 6.18 except utmi_iddig -> utmi_id, and the 5.10-only
|
||||||
* iddig_output/iddig_en/bvalid_grf_sel are dropped). Signal-quality phy_tuning is
|
* iddig_output/iddig_en/bvalid_grf_sel are dropped). Signal-quality phy_tuning
|
||||||
* left off for now (guarded, so NULL is safe): the phy is functional without it.
|
* was originally left off here; it is now implemented (patches/27-usb-phy-tuning.patch)
|
||||||
|
* because "functional without it" turned out not to mean reliable -- see #160.
|
||||||
*/
|
*/
|
||||||
static const struct rockchip_usb2phy_cfg rv1106_phy_cfgs[] = {
|
static const struct rockchip_usb2phy_cfg rv1106_phy_cfgs[] = {
|
||||||
{
|
{
|
||||||
.reg = 0xff3e0000,
|
.reg = 0xff3e0000,
|
||||||
.num_ports = 1,
|
.num_ports = 1,
|
||||||
|
.phy_tuning = rv1106_usb2phy_tuning,
|
||||||
.clkout_ctl = { 0x0058, 4, 4, 1, 0 },
|
.clkout_ctl = { 0x0058, 4, 4, 1, 0 },
|
||||||
.port_cfgs = {
|
.port_cfgs = {
|
||||||
[USB2PHY_PORT_OTG] = {
|
[USB2PHY_PORT_OTG] = {
|
||||||
|
|||||||
@@ -0,0 +1,128 @@
|
|||||||
|
diff --git a/drivers/phy/rockchip/phy-rockchip-inno-usb2.c b/drivers/phy/rockchip/phy-rockchip-inno-usb2.c
|
||||||
|
--- a/drivers/phy/rockchip/phy-rockchip-inno-usb2.c
|
||||||
|
+++ b/drivers/phy/rockchip/phy-rockchip-inno-usb2.c
|
||||||
|
@@ -247,6 +247,7 @@
|
||||||
|
struct device *dev;
|
||||||
|
struct regmap *grf;
|
||||||
|
struct regmap *usbgrf;
|
||||||
|
+ void __iomem *phy_base;
|
||||||
|
struct clk_bulk_data *clks;
|
||||||
|
struct clk *clk480m;
|
||||||
|
struct clk_hw clk480m_hw;
|
||||||
|
@@ -1417,6 +1418,19 @@
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
+ /*
|
||||||
|
+ * RV1106 tunes signal quality through the PHY's own register block, which
|
||||||
|
+ * is this node's "reg" resource and is distinct from the GRF regmap above.
|
||||||
|
+ * Map it only where a tuning callback needs it; every other SoC here tunes
|
||||||
|
+ * through the GRF and must keep working with phy_base left NULL.
|
||||||
|
+ */
|
||||||
|
+ if (of_device_is_compatible(np, "rockchip,rv1106-usb2phy")) {
|
||||||
|
+ rphy->phy_base = devm_platform_ioremap_resource(pdev, 0);
|
||||||
|
+ if (IS_ERR(rphy->phy_base))
|
||||||
|
+ return dev_err_probe(dev, PTR_ERR(rphy->phy_base),
|
||||||
|
+ "failed to map phy registers\n");
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
rphy->phy_reset = devm_reset_control_get_optional(dev, "phy");
|
||||||
|
if (IS_ERR(rphy->phy_reset))
|
||||||
|
return PTR_ERR(rphy->phy_reset);
|
||||||
|
@@ -1506,6 +1520,30 @@
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
+/*
|
||||||
|
+ * Direct PHY-register accessors. Most SoCs here tune through the GRF regmap,
|
||||||
|
+ * but the RV1106's signal-quality registers live in the PHY's own block, which
|
||||||
|
+ * is mapped separately into rphy->phy_base.
|
||||||
|
+ */
|
||||||
|
+static inline void phy_update_bits(void __iomem *reg, u32 mask, u32 val)
|
||||||
|
+{
|
||||||
|
+ u32 tmp = readl(reg);
|
||||||
|
+
|
||||||
|
+ tmp &= ~mask;
|
||||||
|
+ tmp |= val & mask;
|
||||||
|
+ writel(tmp, reg);
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+static inline void phy_set_bits(void __iomem *reg, u32 bits)
|
||||||
|
+{
|
||||||
|
+ writel(readl(reg) | bits, reg);
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+static inline void phy_clear_bits(void __iomem *reg, u32 bits)
|
||||||
|
+{
|
||||||
|
+ writel(readl(reg) & ~bits, reg);
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
static int rk3128_usb2phy_tuning(struct rockchip_usb2phy *rphy)
|
||||||
|
{
|
||||||
|
/* Turn off differential receiver in suspend mode */
|
||||||
|
@@ -2291,10 +2329,65 @@
|
||||||
|
* iddig_output/iddig_en/bvalid_grf_sel are dropped). Signal-quality phy_tuning is
|
||||||
|
* left off for now (guarded, so NULL is safe) — the phy is functional without it.
|
||||||
|
*/
|
||||||
|
+/*
|
||||||
|
+ * RV1106 USB2 PHY signal-quality tuning, ported from the vendor 5.10 driver
|
||||||
|
+ * (drivers/phy/rockchip/phy-rockchip-inno-usb2.c, rv1106_usb2phy_tuning).
|
||||||
|
+ *
|
||||||
|
+ * The 6.18 forward-port originally omitted this ("the phy is functional without
|
||||||
|
+ * it"). It is functional, but not reliable: with these registers left at reset
|
||||||
|
+ * defaults the HS link is electrically marginal under sustained traffic. On a
|
||||||
|
+ * bench panel a USB-Ethernet dongle would stall a bulk transfer after a few
|
||||||
|
+ * minutes; the r8152 Tx-timeout recovery then issues an xHCI Stop Endpoint
|
||||||
|
+ * command which the controller never answers, and xhci-hcd declares the
|
||||||
|
+ * controller dead and tears the bus down (flare-edge issue #160). Measured
|
||||||
|
+ * carrier-on to death across four runs: 14s, 519s, 521s, 584s -- a stochastic
|
||||||
|
+ * spread, not a timer, which is what marginal signalling looks like.
|
||||||
|
+ *
|
||||||
|
+ * Pre-emphasis strength is silicon-revision dependent. The vendor driver picks
|
||||||
|
+ * it from rockchip_get_cpu_version(), which does not exist in mainline; it
|
||||||
|
+ * derives from RV1106_OS_REG1 (0xff020204) & 0x7. Our panels read 1, i.e. the
|
||||||
|
+ * non-zero branch, so 3'b001 is pinned here. If a board ever reports version 0
|
||||||
|
+ * it wants 3'b011 instead -- see the vendor driver.
|
||||||
|
+ */
|
||||||
|
+static int rv1106_usb2phy_tuning(struct rockchip_usb2phy *rphy)
|
||||||
|
+{
|
||||||
|
+ /* Mapped only for this compatible; refuse to poke a NULL base. */
|
||||||
|
+ if (!rphy->phy_base)
|
||||||
|
+ return -ENODEV;
|
||||||
|
+
|
||||||
|
+ /* Always enable pre-emphasis in SOF & EOP & chirp & non-chirp state */
|
||||||
|
+ phy_update_bits(rphy->phy_base + 0x30, GENMASK(2, 0), 0x07);
|
||||||
|
+
|
||||||
|
+ /* Set Tx HS pre_emphasize strength to 3'b001 (cpu_version != 0) */
|
||||||
|
+ phy_update_bits(rphy->phy_base + 0x40, GENMASK(5, 3), (0x01 << 3));
|
||||||
|
+
|
||||||
|
+ /* Set RX Squelch trigger point configure to 4'b0000(112.5 mV) */
|
||||||
|
+ phy_update_bits(rphy->phy_base + 0x64, GENMASK(6, 3), (0x00 << 3));
|
||||||
|
+
|
||||||
|
+ /* Turn off differential receiver by default to save power */
|
||||||
|
+ phy_clear_bits(rphy->phy_base + 0x100, BIT(6));
|
||||||
|
+
|
||||||
|
+ /* Set 45ohm HS ODT value to 5'b10111 to increase driver strength */
|
||||||
|
+ phy_update_bits(rphy->phy_base + 0x11c, GENMASK(4, 0), 0x17);
|
||||||
|
+
|
||||||
|
+ /* Set Tx HS eye height tuning to 3'b011(462 mV) */
|
||||||
|
+ phy_update_bits(rphy->phy_base + 0x124, GENMASK(4, 2), (0x03 << 2));
|
||||||
|
+
|
||||||
|
+ /* Bypass Squelch detector calibration */
|
||||||
|
+ phy_update_bits(rphy->phy_base + 0x1a4, GENMASK(7, 4), (0x01 << 4));
|
||||||
|
+ phy_update_bits(rphy->phy_base + 0x1b4, GENMASK(7, 4), (0x01 << 4));
|
||||||
|
+
|
||||||
|
+ /* Set HS disconnect detect mode to single ended detect mode */
|
||||||
|
+ phy_set_bits(rphy->phy_base + 0x70, BIT(2));
|
||||||
|
+
|
||||||
|
+ return 0;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
static const struct rockchip_usb2phy_cfg rv1106_phy_cfgs[] = {
|
||||||
|
{
|
||||||
|
.reg = 0xff3e0000,
|
||||||
|
.num_ports = 1,
|
||||||
|
+ .phy_tuning = rv1106_usb2phy_tuning,
|
||||||
|
.clkout_ctl = { 0x0058, 4, 4, 1, 0 },
|
||||||
|
.port_cfgs = {
|
||||||
|
[USB2PHY_PORT_OTG] = {
|
||||||
@@ -0,0 +1,136 @@
|
|||||||
|
diff --git a/drivers/phy/rockchip/phy-rockchip-inno-usb2.c b/drivers/phy/rockchip/phy-rockchip-inno-usb2.c
|
||||||
|
--- a/drivers/phy/rockchip/phy-rockchip-inno-usb2.c
|
||||||
|
+++ b/drivers/phy/rockchip/phy-rockchip-inno-usb2.c
|
||||||
|
@@ -203,6 +203,7 @@
|
||||||
|
* @event_nb: hold event notification callback.
|
||||||
|
* @state: define OTG enumeration states before device reset.
|
||||||
|
* @mode: the dr_mode of the controller.
|
||||||
|
+ * @host_mode: the port is currently acting as a USB host.
|
||||||
|
*/
|
||||||
|
struct rockchip_usb2phy_port {
|
||||||
|
struct phy *phy;
|
||||||
|
@@ -222,6 +223,7 @@
|
||||||
|
struct notifier_block event_nb;
|
||||||
|
enum usb_otg_state state;
|
||||||
|
enum usb_dr_mode mode;
|
||||||
|
+ bool host_mode;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
@@ -239,6 +241,8 @@
|
||||||
|
* @dcd_retries: The retry count used to track Data contact
|
||||||
|
* detection process.
|
||||||
|
* @edev: extcon device for notification registration
|
||||||
|
+ * @edev_self_allocated: no extcon phandle in DT, so the OTG state machine
|
||||||
|
+ * has no external role signal at all
|
||||||
|
* @irq: muxed interrupt for single irq configuration
|
||||||
|
* @phy_cfg: phy register configuration, assigned by driver data.
|
||||||
|
* @ports: phy port instance.
|
||||||
|
@@ -257,6 +261,7 @@
|
||||||
|
enum power_supply_type chg_type;
|
||||||
|
u8 dcd_retries;
|
||||||
|
struct extcon_dev *edev;
|
||||||
|
+ bool edev_self_allocated;
|
||||||
|
int irq;
|
||||||
|
const struct rockchip_usb2phy_cfg *phy_cfg;
|
||||||
|
struct rockchip_usb2phy_port ports[USB2PHY_NUM_PORTS];
|
||||||
|
@@ -459,6 +464,14 @@
|
||||||
|
if (ret)
|
||||||
|
return dev_err_probe(rphy->dev, ret,
|
||||||
|
"failed to register extcon device\n");
|
||||||
|
+
|
||||||
|
+ /*
|
||||||
|
+ * Nothing external drives the role on this board. The extcon
|
||||||
|
+ * below is ours, and no code ever sets a cable state on it, so
|
||||||
|
+ * the OTG state machine's view of the world is permanently
|
||||||
|
+ * blank rather than merely stale.
|
||||||
|
+ */
|
||||||
|
+ rphy->edev_self_allocated = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
rphy->edev = edev;
|
||||||
|
@@ -649,11 +662,52 @@
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
+/*
|
||||||
|
+ * The OTG port's state machines only make sense while the port is a
|
||||||
|
+ * peripheral. In host role their inputs are meaningless on a board with no
|
||||||
|
+ * ID source: charger detection drives the analog front end of a port that is
|
||||||
|
+ * already enumerating a device, and its DCP verdict ends in
|
||||||
|
+ * rockchip_usb2phy_power_off(), which suspends the phy and drops the 480M
|
||||||
|
+ * clkout that the host controller's core runs on. The controller then stops
|
||||||
|
+ * clocking with its registers still readable, so it reports itself healthy
|
||||||
|
+ * while no transfer ever completes.
|
||||||
|
+ *
|
||||||
|
+ * dwc3 already calls phy_set_mode() on every role change, but this driver
|
||||||
|
+ * implemented no .set_mode, so the phy never learned the role. Track it and
|
||||||
|
+ * keep both state machines out of host role.
|
||||||
|
+ */
|
||||||
|
+static int rockchip_usb2phy_set_mode(struct phy *phy, enum phy_mode mode,
|
||||||
|
+ int submode)
|
||||||
|
+{
|
||||||
|
+ struct rockchip_usb2phy_port *rport = phy_get_drvdata(phy);
|
||||||
|
+
|
||||||
|
+ if (rport->port_id != USB2PHY_PORT_OTG)
|
||||||
|
+ return 0;
|
||||||
|
+
|
||||||
|
+ switch (mode) {
|
||||||
|
+ case PHY_MODE_USB_HOST:
|
||||||
|
+ if (!rport->host_mode) {
|
||||||
|
+ rport->host_mode = true;
|
||||||
|
+ cancel_delayed_work_sync(&rport->otg_sm_work);
|
||||||
|
+ cancel_delayed_work_sync(&rport->chg_work);
|
||||||
|
+ }
|
||||||
|
+ break;
|
||||||
|
+ case PHY_MODE_USB_DEVICE:
|
||||||
|
+ rport->host_mode = false;
|
||||||
|
+ break;
|
||||||
|
+ default:
|
||||||
|
+ break;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ return 0;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
static const struct phy_ops rockchip_usb2phy_ops = {
|
||||||
|
.init = rockchip_usb2phy_init,
|
||||||
|
.exit = rockchip_usb2phy_exit,
|
||||||
|
.power_on = rockchip_usb2phy_power_on,
|
||||||
|
.power_off = rockchip_usb2phy_power_off,
|
||||||
|
+ .set_mode = rockchip_usb2phy_set_mode,
|
||||||
|
.owner = THIS_MODULE,
|
||||||
|
};
|
||||||
|
|
||||||
|
@@ -667,6 +721,15 @@
|
||||||
|
unsigned long delay;
|
||||||
|
bool vbus_attach, sch_work, notify_charger;
|
||||||
|
|
||||||
|
+ /*
|
||||||
|
+ * This machine can only get the answer wrong here. In host role its
|
||||||
|
+ * verdict suspends a phy that is enumerating a device (#160); with a
|
||||||
|
+ * self-allocated extcon it has no role signal in EITHER direction, and
|
||||||
|
+ * in device role it strands the gadget the same way (#168).
|
||||||
|
+ */
|
||||||
|
+ if (rport->host_mode || rphy->edev_self_allocated)
|
||||||
|
+ return;
|
||||||
|
+
|
||||||
|
vbus_attach = property_enabled(rphy->grf,
|
||||||
|
&rport->port_cfg->utmi_bvalid);
|
||||||
|
|
||||||
|
@@ -825,6 +888,15 @@
|
||||||
|
bool is_dcd, tmout, vout, vbus_attach;
|
||||||
|
unsigned long delay;
|
||||||
|
|
||||||
|
+ /*
|
||||||
|
+ * Never probe the front end of a port that is hosting a device, and
|
||||||
|
+ * never at all on a board whose bvalid is pinned high by the VBUS
|
||||||
|
+ * bypass: charger detection cannot be meaningful there, and its DCP
|
||||||
|
+ * verdict powers the phy off underneath a working link (#160, #168).
|
||||||
|
+ */
|
||||||
|
+ if (rport->host_mode || rphy->edev_self_allocated)
|
||||||
|
+ return;
|
||||||
|
+
|
||||||
|
vbus_attach = property_enabled(rphy->grf, &rport->port_cfg->utmi_bvalid);
|
||||||
|
|
||||||
|
dev_dbg(&rport->phy->dev, "chg detection work state = %d\n",
|
||||||
@@ -18,6 +18,8 @@ with `../build/warden_defconfig` -> `zImage` + `rv1106-warden.dtb`).
|
|||||||
| `15-pinctrl.patch` | `pinctrl-rockchip` RV1106 iomux |
|
| `15-pinctrl.patch` | `pinctrl-rockchip` RV1106 iomux |
|
||||||
| `20-arch-dts-bindings.patch` | `arch/arm` DTs (`rv1106.dtsi`, `rv1106-warden.dts`, pinctrl), mach, configs, `include/dt-bindings` |
|
| `20-arch-dts-bindings.patch` | `arch/arm` DTs (`rv1106.dtsi`, `rv1106-warden.dts`, pinctrl), mach, configs, `include/dt-bindings` |
|
||||||
| `25-usb-phy.patch` | inno-usb2 phy |
|
| `25-usb-phy.patch` | inno-usb2 phy |
|
||||||
|
| `27-usb-phy-tuning.patch` | inno-usb2 phy: RV1106 signal-quality register tuning |
|
||||||
|
| `29-usb2phy-no-blind-otg-sm.patch` | inno-usb2 phy: stop the OTG/charger state machines running blind |
|
||||||
| `30-drm-vop-panel-rgb.patch` | VOP (`rgb_dclk_pol`=0 + WIN1 scanout), panel-simple bus-format, rockchip_rgb |
|
| `30-drm-vop-panel-rgb.patch` | VOP (`rgb_dclk_pol`=0 + WIN1 scanout), panel-simple bus-format, rockchip_rgb |
|
||||||
| `35-mailbox-hpmcu.patch` | rockchip-mailbox (rv1106 num_chans=1) |
|
| `35-mailbox-hpmcu.patch` | rockchip-mailbox (rv1106 num_chans=1) |
|
||||||
| `40-pvtm-soc.patch` | PVTM (core+pmu) + `include/soc/rockchip/pvtm.h` |
|
| `40-pvtm-soc.patch` | PVTM (core+pmu) + `include/soc/rockchip/pvtm.h` |
|
||||||
|
|||||||
Reference in New Issue
Block a user