13d0407baSopenharmony_ci/*
23d0407baSopenharmony_ci * Rockchip USB 3.0 PHY with Innosilicon IP block driver
33d0407baSopenharmony_ci *
43d0407baSopenharmony_ci * Copyright (C) 2016 Fuzhou Rockchip Electronics Co., Ltd
53d0407baSopenharmony_ci *
63d0407baSopenharmony_ci * This program is free software; you can redistribute it and/or modify
73d0407baSopenharmony_ci * it under the terms of the GNU General Public License as published by
83d0407baSopenharmony_ci * the Free Software Foundation; either version 2 of the License, or
93d0407baSopenharmony_ci * (at your option) any later version.
103d0407baSopenharmony_ci *
113d0407baSopenharmony_ci * This program is distributed in the hope that it will be useful,
123d0407baSopenharmony_ci * but WITHOUT ANY WARRANTY; without even the implied warranty of
133d0407baSopenharmony_ci * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
143d0407baSopenharmony_ci * GNU General Public License for more details.
153d0407baSopenharmony_ci */
163d0407baSopenharmony_ci
173d0407baSopenharmony_ci#include <linux/clk.h>
183d0407baSopenharmony_ci#include <linux/delay.h>
193d0407baSopenharmony_ci#include <linux/debugfs.h>
203d0407baSopenharmony_ci#include <linux/gpio/consumer.h>
213d0407baSopenharmony_ci#include <linux/interrupt.h>
223d0407baSopenharmony_ci#include <linux/io.h>
233d0407baSopenharmony_ci#include <linux/kernel.h>
243d0407baSopenharmony_ci#include <linux/mfd/syscon.h>
253d0407baSopenharmony_ci#include <linux/module.h>
263d0407baSopenharmony_ci#include <linux/of.h>
273d0407baSopenharmony_ci#include <linux/of_address.h>
283d0407baSopenharmony_ci#include <linux/of_irq.h>
293d0407baSopenharmony_ci#include <linux/of_platform.h>
303d0407baSopenharmony_ci#include <linux/phy/phy.h>
313d0407baSopenharmony_ci#include <linux/platform_device.h>
323d0407baSopenharmony_ci#include <linux/regmap.h>
333d0407baSopenharmony_ci#include <linux/reset.h>
343d0407baSopenharmony_ci#include <linux/usb/phy.h>
353d0407baSopenharmony_ci#include <linux/uaccess.h>
363d0407baSopenharmony_ci
373d0407baSopenharmony_ci#define FILE_RIGHT_644 0644
383d0407baSopenharmony_ci
393d0407baSopenharmony_ci#define U3PHY_PORT_NUM 2
403d0407baSopenharmony_ci#define U3PHY_MAX_CLKS 4
413d0407baSopenharmony_ci#define BIT_WRITEABLE_SHIFT 16
423d0407baSopenharmony_ci#define SCHEDULE_DELAY (60 * HZ)
433d0407baSopenharmony_ci
443d0407baSopenharmony_ci#define U3PHY_APB_RST BIT(0)
453d0407baSopenharmony_ci#define U3PHY_POR_RST BIT(1)
463d0407baSopenharmony_ci#define U3PHY_MAC_RST BIT(2)
473d0407baSopenharmony_ci
483d0407baSopenharmony_cistruct rockchip_u3phy;
493d0407baSopenharmony_cistruct rockchip_u3phy_port;
503d0407baSopenharmony_ci
513d0407baSopenharmony_cienum rockchip_u3phy_type {
523d0407baSopenharmony_ci    U3PHY_TYPE_PIPE,
533d0407baSopenharmony_ci    U3PHY_TYPE_UTMI,
543d0407baSopenharmony_ci};
553d0407baSopenharmony_ci
563d0407baSopenharmony_cienum rockchip_u3phy_pipe_pwr {
573d0407baSopenharmony_ci    PIPE_PWR_P0 = 0,
583d0407baSopenharmony_ci    PIPE_PWR_P1 = 1,
593d0407baSopenharmony_ci    PIPE_PWR_P2 = 2,
603d0407baSopenharmony_ci    PIPE_PWR_P3 = 3,
613d0407baSopenharmony_ci    PIPE_PWR_MAX = 4,
623d0407baSopenharmony_ci};
633d0407baSopenharmony_ci
643d0407baSopenharmony_cienum rockchip_u3phy_rest_req {
653d0407baSopenharmony_ci    U3_POR_RSTN = 0,
663d0407baSopenharmony_ci    U2_POR_RSTN = 1,
673d0407baSopenharmony_ci    PIPE_MAC_RSTN = 2,
683d0407baSopenharmony_ci    UTMI_MAC_RSTN = 3,
693d0407baSopenharmony_ci    PIPE_APB_RSTN = 4,
703d0407baSopenharmony_ci    UTMI_APB_RSTN = 5,
713d0407baSopenharmony_ci    U3PHY_RESET_MAX = 6,
723d0407baSopenharmony_ci};
733d0407baSopenharmony_ci
743d0407baSopenharmony_cienum rockchip_u3phy_utmi_state {
753d0407baSopenharmony_ci    PHY_UTMI_HS_ONLINE = 0,
763d0407baSopenharmony_ci    PHY_UTMI_DISCONNECT = 1,
773d0407baSopenharmony_ci    PHY_UTMI_CONNECT = 2,
783d0407baSopenharmony_ci    PHY_UTMI_FS_LS_ONLINE = 4,
793d0407baSopenharmony_ci};
803d0407baSopenharmony_ci
813d0407baSopenharmony_ci/*
823d0407baSopenharmony_ci * @rvalue: reset value
833d0407baSopenharmony_ci * @dvalue: desired value
843d0407baSopenharmony_ci */
853d0407baSopenharmony_cistruct u3phy_reg {
863d0407baSopenharmony_ci    unsigned int offset;
873d0407baSopenharmony_ci    unsigned int bitend;
883d0407baSopenharmony_ci    unsigned int bitstart;
893d0407baSopenharmony_ci    unsigned int rvalue;
903d0407baSopenharmony_ci    unsigned int dvalue;
913d0407baSopenharmony_ci};
923d0407baSopenharmony_ci
933d0407baSopenharmony_cistruct rockchip_u3phy_grfcfg {
943d0407baSopenharmony_ci    struct u3phy_reg um_suspend;
953d0407baSopenharmony_ci    struct u3phy_reg ls_det_en;
963d0407baSopenharmony_ci    struct u3phy_reg ls_det_st;
973d0407baSopenharmony_ci    struct u3phy_reg um_ls;
983d0407baSopenharmony_ci    struct u3phy_reg um_hstdct;
993d0407baSopenharmony_ci    struct u3phy_reg u2_only_ctrl;
1003d0407baSopenharmony_ci    struct u3phy_reg u3_disable;
1013d0407baSopenharmony_ci    struct u3phy_reg pp_pwr_st;
1023d0407baSopenharmony_ci    struct u3phy_reg pp_pwr_en[PIPE_PWR_MAX];
1033d0407baSopenharmony_ci};
1043d0407baSopenharmony_ci
1053d0407baSopenharmony_ci/**
1063d0407baSopenharmony_ci * struct rockchip_u3phy_apbcfg: usb3-phy apb configuration.
1073d0407baSopenharmony_ci * @u2_pre_emp: usb2-phy pre-emphasis tuning.
1083d0407baSopenharmony_ci * @u2_pre_emp_sth: usb2-phy pre-emphasis strength tuning.
1093d0407baSopenharmony_ci * @u2_odt_tuning: usb2-phy odt 45ohm tuning.
1103d0407baSopenharmony_ci */
1113d0407baSopenharmony_cistruct rockchip_u3phy_apbcfg {
1123d0407baSopenharmony_ci    unsigned int u2_pre_emp;
1133d0407baSopenharmony_ci    unsigned int u2_pre_emp_sth;
1143d0407baSopenharmony_ci    unsigned int u2_odt_tuning;
1153d0407baSopenharmony_ci};
1163d0407baSopenharmony_ci
1173d0407baSopenharmony_cistruct rockchip_u3phy_cfg {
1183d0407baSopenharmony_ci    unsigned int reg;
1193d0407baSopenharmony_ci    const struct rockchip_u3phy_grfcfg grfcfg;
1203d0407baSopenharmony_ci
1213d0407baSopenharmony_ci    int (*phy_pipe_power)(struct rockchip_u3phy *, struct rockchip_u3phy_port *, bool on);
1223d0407baSopenharmony_ci    int (*phy_tuning)(struct rockchip_u3phy *, struct rockchip_u3phy_port *, struct device_node *);
1233d0407baSopenharmony_ci};
1243d0407baSopenharmony_ci
1253d0407baSopenharmony_cistruct rockchip_u3phy_port {
1263d0407baSopenharmony_ci    struct phy *phy;
1273d0407baSopenharmony_ci    void __iomem *base;
1283d0407baSopenharmony_ci    unsigned int index;
1293d0407baSopenharmony_ci    unsigned char type;
1303d0407baSopenharmony_ci    bool suspended;
1313d0407baSopenharmony_ci    bool refclk_25m_quirk;
1323d0407baSopenharmony_ci    struct mutex mutex; /* mutex for updating register */
1333d0407baSopenharmony_ci    struct delayed_work um_sm_work;
1343d0407baSopenharmony_ci};
1353d0407baSopenharmony_ci
1363d0407baSopenharmony_cistruct rockchip_u3phy {
1373d0407baSopenharmony_ci    struct device *dev;
1383d0407baSopenharmony_ci    struct regmap *u3phy_grf;
1393d0407baSopenharmony_ci    struct regmap *grf;
1403d0407baSopenharmony_ci    int um_ls_irq;
1413d0407baSopenharmony_ci    struct clk *clks[U3PHY_MAX_CLKS];
1423d0407baSopenharmony_ci    struct dentry *root;
1433d0407baSopenharmony_ci    struct regulator *vbus;
1443d0407baSopenharmony_ci    struct reset_control *rsts[U3PHY_RESET_MAX];
1453d0407baSopenharmony_ci    struct rockchip_u3phy_apbcfg apbcfg;
1463d0407baSopenharmony_ci    const struct rockchip_u3phy_cfg *cfgs;
1473d0407baSopenharmony_ci    struct rockchip_u3phy_port ports[U3PHY_PORT_NUM];
1483d0407baSopenharmony_ci    struct usb_phy usb_phy;
1493d0407baSopenharmony_ci    bool vbus_enabled;
1503d0407baSopenharmony_ci};
1513d0407baSopenharmony_ci
1523d0407baSopenharmony_cistatic inline int param_write(void __iomem *base, const struct u3phy_reg *reg, bool desired)
1533d0407baSopenharmony_ci{
1543d0407baSopenharmony_ci    unsigned int val, mask;
1553d0407baSopenharmony_ci    unsigned int tmp = desired ? reg->dvalue : reg->rvalue;
1563d0407baSopenharmony_ci    int ret = 0;
1573d0407baSopenharmony_ci
1583d0407baSopenharmony_ci    mask = GENMASK(reg->bitend, reg->bitstart);
1593d0407baSopenharmony_ci    val = (tmp << reg->bitstart) | (mask << BIT_WRITEABLE_SHIFT);
1603d0407baSopenharmony_ci    ret = regmap_write(base, reg->offset, val);
1613d0407baSopenharmony_ci
1623d0407baSopenharmony_ci    return ret;
1633d0407baSopenharmony_ci}
1643d0407baSopenharmony_ci
1653d0407baSopenharmony_cistatic inline bool param_exped(void __iomem *base, const struct u3phy_reg *reg, unsigned int value)
1663d0407baSopenharmony_ci{
1673d0407baSopenharmony_ci    int ret;
1683d0407baSopenharmony_ci    unsigned int tmp, orig;
1693d0407baSopenharmony_ci    unsigned int mask = GENMASK(reg->bitend, reg->bitstart);
1703d0407baSopenharmony_ci
1713d0407baSopenharmony_ci    ret = regmap_read(base, reg->offset, &orig);
1723d0407baSopenharmony_ci    if (ret) {
1733d0407baSopenharmony_ci        return false;
1743d0407baSopenharmony_ci    }
1753d0407baSopenharmony_ci
1763d0407baSopenharmony_ci    tmp = (orig & mask) >> reg->bitstart;
1773d0407baSopenharmony_ci    return tmp == value;
1783d0407baSopenharmony_ci}
1793d0407baSopenharmony_ci
1803d0407baSopenharmony_cistatic int rockchip_set_vbus_power(struct rockchip_u3phy *u3phy, bool en)
1813d0407baSopenharmony_ci{
1823d0407baSopenharmony_ci    int ret = 0;
1833d0407baSopenharmony_ci
1843d0407baSopenharmony_ci    if (!u3phy->vbus) {
1853d0407baSopenharmony_ci        return 0;
1863d0407baSopenharmony_ci    }
1873d0407baSopenharmony_ci
1883d0407baSopenharmony_ci    if (en && !u3phy->vbus_enabled) {
1893d0407baSopenharmony_ci        ret = regulator_enable(u3phy->vbus);
1903d0407baSopenharmony_ci        if (ret) {
1913d0407baSopenharmony_ci            dev_err(u3phy->dev, "Failed to enable VBUS supply\n");
1923d0407baSopenharmony_ci        }
1933d0407baSopenharmony_ci    } else if (!en && u3phy->vbus_enabled) {
1943d0407baSopenharmony_ci        ret = regulator_disable(u3phy->vbus);
1953d0407baSopenharmony_ci    }
1963d0407baSopenharmony_ci
1973d0407baSopenharmony_ci    if (ret == 0) {
1983d0407baSopenharmony_ci        u3phy->vbus_enabled = en;
1993d0407baSopenharmony_ci    }
2003d0407baSopenharmony_ci
2013d0407baSopenharmony_ci    return ret;
2023d0407baSopenharmony_ci}
2033d0407baSopenharmony_ci
2043d0407baSopenharmony_cistatic int rockchip_u3phy_usb2_only_show(struct seq_file *s, void *unused)
2053d0407baSopenharmony_ci{
2063d0407baSopenharmony_ci    struct rockchip_u3phy *u3phy = s->private;
2073d0407baSopenharmony_ci
2083d0407baSopenharmony_ci    if (param_exped(u3phy->u3phy_grf, &u3phy->cfgs->grfcfg.u2_only_ctrl, 1)) {
2093d0407baSopenharmony_ci        dev_info(u3phy->dev, "u2\n");
2103d0407baSopenharmony_ci    } else {
2113d0407baSopenharmony_ci        dev_info(u3phy->dev, "u3\n");
2123d0407baSopenharmony_ci    }
2133d0407baSopenharmony_ci
2143d0407baSopenharmony_ci    return 0;
2153d0407baSopenharmony_ci}
2163d0407baSopenharmony_ci
2173d0407baSopenharmony_cistatic int rockchip_u3phy_usb2_only_open(struct inode *inode, struct file *file)
2183d0407baSopenharmony_ci{
2193d0407baSopenharmony_ci    return single_open(file, rockchip_u3phy_usb2_only_show, inode->i_private);
2203d0407baSopenharmony_ci}
2213d0407baSopenharmony_ci
2223d0407baSopenharmony_cistatic ssize_t rockchip_u3phy_usb2_only_write(struct file *file, const char __user *ubuf, size_t count, loff_t *ppos)
2233d0407baSopenharmony_ci{
2243d0407baSopenharmony_ci    struct seq_file *s = file->private_data;
2253d0407baSopenharmony_ci    struct rockchip_u3phy *u3phy = s->private;
2263d0407baSopenharmony_ci    struct rockchip_u3phy_port *u3phy_port;
2273d0407baSopenharmony_ci    char buf[32];
2283d0407baSopenharmony_ci    u8 index;
2293d0407baSopenharmony_ci
2303d0407baSopenharmony_ci    if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count))) {
2313d0407baSopenharmony_ci        return -EFAULT;
2323d0407baSopenharmony_ci    }
2333d0407baSopenharmony_ci
2343d0407baSopenharmony_ci    if (!strncmp(buf, "u3", 2) && param_exped(u3phy->u3phy_grf, &u3phy->cfgs->grfcfg.u2_only_ctrl, 1)) {
2353d0407baSopenharmony_ci        dev_info(u3phy->dev, "Set usb3.0 and usb2.0 mode successfully\n");
2363d0407baSopenharmony_ci
2373d0407baSopenharmony_ci        rockchip_set_vbus_power(u3phy, false);
2383d0407baSopenharmony_ci
2393d0407baSopenharmony_ci        param_write(u3phy->grf, &u3phy->cfgs->grfcfg.u3_disable, false);
2403d0407baSopenharmony_ci        param_write(u3phy->u3phy_grf, &u3phy->cfgs->grfcfg.u2_only_ctrl, false);
2413d0407baSopenharmony_ci
2423d0407baSopenharmony_ci        for (index = 0; index < U3PHY_PORT_NUM; index++) {
2433d0407baSopenharmony_ci            u3phy_port = &u3phy->ports[index];
2443d0407baSopenharmony_ci            /* enable u3 rx termimation */
2453d0407baSopenharmony_ci            if (u3phy_port->type == U3PHY_TYPE_PIPE) {
2463d0407baSopenharmony_ci                writel(0x30, u3phy_port->base + 0xd8);
2473d0407baSopenharmony_ci            }
2483d0407baSopenharmony_ci        }
2493d0407baSopenharmony_ci
2503d0407baSopenharmony_ci        atomic_notifier_call_chain(&u3phy->usb_phy.notifier, 0, NULL);
2513d0407baSopenharmony_ci
2523d0407baSopenharmony_ci        rockchip_set_vbus_power(u3phy, true);
2533d0407baSopenharmony_ci    } else if (!strncmp(buf, "u2", 2) && param_exped(u3phy->u3phy_grf, &u3phy->cfgs->grfcfg.u2_only_ctrl, 0)) {
2543d0407baSopenharmony_ci        dev_info(u3phy->dev, "Set usb2.0 only mode successfully\n");
2553d0407baSopenharmony_ci
2563d0407baSopenharmony_ci        rockchip_set_vbus_power(u3phy, false);
2573d0407baSopenharmony_ci
2583d0407baSopenharmony_ci        param_write(u3phy->grf, &u3phy->cfgs->grfcfg.u3_disable, true);
2593d0407baSopenharmony_ci        param_write(u3phy->u3phy_grf, &u3phy->cfgs->grfcfg.u2_only_ctrl, true);
2603d0407baSopenharmony_ci
2613d0407baSopenharmony_ci        for (index = 0; index < U3PHY_PORT_NUM; index++) {
2623d0407baSopenharmony_ci            u3phy_port = &u3phy->ports[index];
2633d0407baSopenharmony_ci            /* disable u3 rx termimation */
2643d0407baSopenharmony_ci            if (u3phy_port->type == U3PHY_TYPE_PIPE) {
2653d0407baSopenharmony_ci                writel(0x20, u3phy_port->base + 0xd8);
2663d0407baSopenharmony_ci            }
2673d0407baSopenharmony_ci        }
2683d0407baSopenharmony_ci
2693d0407baSopenharmony_ci        atomic_notifier_call_chain(&u3phy->usb_phy.notifier, 0, NULL);
2703d0407baSopenharmony_ci
2713d0407baSopenharmony_ci        rockchip_set_vbus_power(u3phy, true);
2723d0407baSopenharmony_ci    } else {
2733d0407baSopenharmony_ci        dev_info(u3phy->dev, "Same or illegal mode\n");
2743d0407baSopenharmony_ci    }
2753d0407baSopenharmony_ci
2763d0407baSopenharmony_ci    return count;
2773d0407baSopenharmony_ci}
2783d0407baSopenharmony_ci
2793d0407baSopenharmony_cistatic const struct file_operations rockchip_u3phy_usb2_only_fops = {
2803d0407baSopenharmony_ci    .open = rockchip_u3phy_usb2_only_open,
2813d0407baSopenharmony_ci    .write = rockchip_u3phy_usb2_only_write,
2823d0407baSopenharmony_ci    .read = seq_read,
2833d0407baSopenharmony_ci    .llseek = seq_lseek,
2843d0407baSopenharmony_ci    .release = single_release,
2853d0407baSopenharmony_ci};
2863d0407baSopenharmony_ci
2873d0407baSopenharmony_ciint rockchip_u3phy_debugfs_init(struct rockchip_u3phy *u3phy)
2883d0407baSopenharmony_ci{
2893d0407baSopenharmony_ci    struct dentry *root;
2903d0407baSopenharmony_ci    struct dentry *file;
2913d0407baSopenharmony_ci    int ret;
2923d0407baSopenharmony_ci
2933d0407baSopenharmony_ci    root = debugfs_create_dir(dev_name(u3phy->dev), NULL);
2943d0407baSopenharmony_ci    if (!root) {
2953d0407baSopenharmony_ci        ret = -ENOMEM;
2963d0407baSopenharmony_ci        goto err0;
2973d0407baSopenharmony_ci    }
2983d0407baSopenharmony_ci
2993d0407baSopenharmony_ci    u3phy->root = root;
3003d0407baSopenharmony_ci
3013d0407baSopenharmony_ci    file = debugfs_create_file("u3phy_mode", FILE_RIGHT_644, root, u3phy, &rockchip_u3phy_usb2_only_fops);
3023d0407baSopenharmony_ci    if (!file) {
3033d0407baSopenharmony_ci        ret = -ENOMEM;
3043d0407baSopenharmony_ci        goto err1;
3053d0407baSopenharmony_ci    }
3063d0407baSopenharmony_ci    return 0;
3073d0407baSopenharmony_ci
3083d0407baSopenharmony_cierr1:
3093d0407baSopenharmony_ci    debugfs_remove_recursive(root);
3103d0407baSopenharmony_cierr0:
3113d0407baSopenharmony_ci    return ret;
3123d0407baSopenharmony_ci}
3133d0407baSopenharmony_ci
3143d0407baSopenharmony_cistatic const char *get_rest_name(enum rockchip_u3phy_rest_req rst)
3153d0407baSopenharmony_ci{
3163d0407baSopenharmony_ci    switch (rst) {
3173d0407baSopenharmony_ci        case U2_POR_RSTN:
3183d0407baSopenharmony_ci            return "u3phy-u2-por";
3193d0407baSopenharmony_ci        case U3_POR_RSTN:
3203d0407baSopenharmony_ci            return "u3phy-u3-por";
3213d0407baSopenharmony_ci        case PIPE_MAC_RSTN:
3223d0407baSopenharmony_ci            return "u3phy-pipe-mac";
3233d0407baSopenharmony_ci        case UTMI_MAC_RSTN:
3243d0407baSopenharmony_ci            return "u3phy-utmi-mac";
3253d0407baSopenharmony_ci        case UTMI_APB_RSTN:
3263d0407baSopenharmony_ci            return "u3phy-utmi-apb";
3273d0407baSopenharmony_ci        case PIPE_APB_RSTN:
3283d0407baSopenharmony_ci            return "u3phy-pipe-apb";
3293d0407baSopenharmony_ci        default:
3303d0407baSopenharmony_ci            return "invalid";
3313d0407baSopenharmony_ci    }
3323d0407baSopenharmony_ci}
3333d0407baSopenharmony_ci
3343d0407baSopenharmony_cistatic void rockchip_u3phy_rest_deassert(struct rockchip_u3phy *u3phy, unsigned int flag)
3353d0407baSopenharmony_ci{
3363d0407baSopenharmony_ci    int rst;
3373d0407baSopenharmony_ci
3383d0407baSopenharmony_ci    if (flag & U3PHY_APB_RST) {
3393d0407baSopenharmony_ci        dev_dbg(u3phy->dev, "deassert APB bus interface reset\n");
3403d0407baSopenharmony_ci        for (rst = PIPE_APB_RSTN; rst <= UTMI_APB_RSTN; rst++) {
3413d0407baSopenharmony_ci            if (u3phy->rsts[rst]) {
3423d0407baSopenharmony_ci                reset_control_deassert(u3phy->rsts[rst]);
3433d0407baSopenharmony_ci            }
3443d0407baSopenharmony_ci        }
3453d0407baSopenharmony_ci    }
3463d0407baSopenharmony_ci
3473d0407baSopenharmony_ci    if (flag & U3PHY_POR_RST) {
3483d0407baSopenharmony_ci        usleep_range(0x0C, 0x0F);
3493d0407baSopenharmony_ci        dev_dbg(u3phy->dev, "deassert u2 and u3 phy power on reset\n");
3503d0407baSopenharmony_ci        for (rst = U3_POR_RSTN; rst <= U2_POR_RSTN; rst++) {
3513d0407baSopenharmony_ci            if (u3phy->rsts[rst]) {
3523d0407baSopenharmony_ci                reset_control_deassert(u3phy->rsts[rst]);
3533d0407baSopenharmony_ci            }
3543d0407baSopenharmony_ci        }
3553d0407baSopenharmony_ci    }
3563d0407baSopenharmony_ci
3573d0407baSopenharmony_ci    if (flag & U3PHY_MAC_RST) {
3583d0407baSopenharmony_ci        usleep_range(0x4B0, 0x5DC);
3593d0407baSopenharmony_ci        dev_dbg(u3phy->dev, "deassert pipe and utmi MAC reset\n");
3603d0407baSopenharmony_ci        for (rst = PIPE_MAC_RSTN; rst <= UTMI_MAC_RSTN; rst++) {
3613d0407baSopenharmony_ci            if (u3phy->rsts[rst]) {
3623d0407baSopenharmony_ci                reset_control_deassert(u3phy->rsts[rst]);
3633d0407baSopenharmony_ci            }
3643d0407baSopenharmony_ci        }
3653d0407baSopenharmony_ci    }
3663d0407baSopenharmony_ci}
3673d0407baSopenharmony_ci
3683d0407baSopenharmony_cistatic void rockchip_u3phy_rest_assert(struct rockchip_u3phy *u3phy)
3693d0407baSopenharmony_ci{
3703d0407baSopenharmony_ci    int rst;
3713d0407baSopenharmony_ci
3723d0407baSopenharmony_ci    dev_dbg(u3phy->dev, "assert u3phy reset\n");
3733d0407baSopenharmony_ci    for (rst = 0; rst < U3PHY_RESET_MAX; rst++) {
3743d0407baSopenharmony_ci        if (u3phy->rsts[rst]) {
3753d0407baSopenharmony_ci            reset_control_assert(u3phy->rsts[rst]);
3763d0407baSopenharmony_ci        }
3773d0407baSopenharmony_ci    }
3783d0407baSopenharmony_ci}
3793d0407baSopenharmony_ci
3803d0407baSopenharmony_cistatic int rockchip_u3phy_clk_enable(struct rockchip_u3phy *u3phy)
3813d0407baSopenharmony_ci{
3823d0407baSopenharmony_ci    int ret, clk;
3833d0407baSopenharmony_ci
3843d0407baSopenharmony_ci    for (clk = 0; clk < U3PHY_MAX_CLKS && u3phy->clks[clk]; clk++) {
3853d0407baSopenharmony_ci        ret = clk_prepare_enable(u3phy->clks[clk]);
3863d0407baSopenharmony_ci        if (ret) {
3873d0407baSopenharmony_ci            goto err_disable_clks;
3883d0407baSopenharmony_ci        }
3893d0407baSopenharmony_ci    }
3903d0407baSopenharmony_ci    return 0;
3913d0407baSopenharmony_ci
3923d0407baSopenharmony_cierr_disable_clks:
3933d0407baSopenharmony_ci    while (--clk >= 0) {
3943d0407baSopenharmony_ci        clk_disable_unprepare(u3phy->clks[clk]);
3953d0407baSopenharmony_ci    }
3963d0407baSopenharmony_ci    return ret;
3973d0407baSopenharmony_ci}
3983d0407baSopenharmony_ci
3993d0407baSopenharmony_cistatic void rockchip_u3phy_clk_disable(struct rockchip_u3phy *u3phy)
4003d0407baSopenharmony_ci{
4013d0407baSopenharmony_ci    int clk;
4023d0407baSopenharmony_ci
4033d0407baSopenharmony_ci    for (clk = U3PHY_MAX_CLKS - 1; clk >= 0; clk--) {
4043d0407baSopenharmony_ci        if (u3phy->clks[clk]) {
4053d0407baSopenharmony_ci            clk_disable_unprepare(u3phy->clks[clk]);
4063d0407baSopenharmony_ci        }
4073d0407baSopenharmony_ci    }
4083d0407baSopenharmony_ci}
4093d0407baSopenharmony_ci
4103d0407baSopenharmony_cistatic int rockchip_u3phy_init(struct phy *phy)
4113d0407baSopenharmony_ci{
4123d0407baSopenharmony_ci    return 0;
4133d0407baSopenharmony_ci}
4143d0407baSopenharmony_ci
4153d0407baSopenharmony_cistatic int rockchip_u3phy_exit(struct phy *phy)
4163d0407baSopenharmony_ci{
4173d0407baSopenharmony_ci    return 0;
4183d0407baSopenharmony_ci}
4193d0407baSopenharmony_ci
4203d0407baSopenharmony_cistatic int rockchip_u3phy_power_on(struct phy *phy)
4213d0407baSopenharmony_ci{
4223d0407baSopenharmony_ci    struct rockchip_u3phy_port *u3phy_port = phy_get_drvdata(phy);
4233d0407baSopenharmony_ci    struct rockchip_u3phy *u3phy = dev_get_drvdata(phy->dev.parent);
4243d0407baSopenharmony_ci    int ret;
4253d0407baSopenharmony_ci
4263d0407baSopenharmony_ci    dev_info(&u3phy_port->phy->dev, "u3phy %s power on\n", (u3phy_port->type == U3PHY_TYPE_UTMI) ? "u2" : "u3");
4273d0407baSopenharmony_ci
4283d0407baSopenharmony_ci    if (!u3phy_port->suspended) {
4293d0407baSopenharmony_ci        return 0;
4303d0407baSopenharmony_ci    }
4313d0407baSopenharmony_ci
4323d0407baSopenharmony_ci    ret = rockchip_u3phy_clk_enable(u3phy);
4333d0407baSopenharmony_ci    if (ret) {
4343d0407baSopenharmony_ci        return ret;
4353d0407baSopenharmony_ci    }
4363d0407baSopenharmony_ci
4373d0407baSopenharmony_ci    if (u3phy_port->type == U3PHY_TYPE_UTMI) {
4383d0407baSopenharmony_ci        param_write(u3phy->u3phy_grf, &u3phy->cfgs->grfcfg.um_suspend, false);
4393d0407baSopenharmony_ci    } else {
4403d0407baSopenharmony_ci        /* current in p2 ? */
4413d0407baSopenharmony_ci        if (param_exped(u3phy->u3phy_grf, &u3phy->cfgs->grfcfg.pp_pwr_st, PIPE_PWR_P2)) {
4423d0407baSopenharmony_ci            goto done;
4433d0407baSopenharmony_ci        }
4443d0407baSopenharmony_ci
4453d0407baSopenharmony_ci        if (u3phy->cfgs->phy_pipe_power) {
4463d0407baSopenharmony_ci            dev_dbg(u3phy->dev, "do pipe power up\n");
4473d0407baSopenharmony_ci            u3phy->cfgs->phy_pipe_power(u3phy, u3phy_port, true);
4483d0407baSopenharmony_ci        }
4493d0407baSopenharmony_ci
4503d0407baSopenharmony_ci        /* exit to p0 */
4513d0407baSopenharmony_ci        param_write(u3phy->u3phy_grf, &u3phy->cfgs->grfcfg.pp_pwr_en[PIPE_PWR_P0], true);
4523d0407baSopenharmony_ci        usleep_range(0x5A, 0x64);
4533d0407baSopenharmony_ci
4543d0407baSopenharmony_ci        /* enter to p2 from p0 */
4553d0407baSopenharmony_ci        param_write(u3phy->u3phy_grf, &u3phy->cfgs->grfcfg.pp_pwr_en[PIPE_PWR_P2], false);
4563d0407baSopenharmony_ci        udelay(0x03);
4573d0407baSopenharmony_ci    }
4583d0407baSopenharmony_ci
4593d0407baSopenharmony_cidone:
4603d0407baSopenharmony_ci    rockchip_set_vbus_power(u3phy, true);
4613d0407baSopenharmony_ci    u3phy_port->suspended = false;
4623d0407baSopenharmony_ci    return 0;
4633d0407baSopenharmony_ci}
4643d0407baSopenharmony_ci
4653d0407baSopenharmony_cistatic int rockchip_u3phy_power_off(struct phy *phy)
4663d0407baSopenharmony_ci{
4673d0407baSopenharmony_ci    struct rockchip_u3phy_port *u3phy_port = phy_get_drvdata(phy);
4683d0407baSopenharmony_ci    struct rockchip_u3phy *u3phy = dev_get_drvdata(phy->dev.parent);
4693d0407baSopenharmony_ci
4703d0407baSopenharmony_ci    dev_info(&u3phy_port->phy->dev, "u3phy %s power off\n", (u3phy_port->type == U3PHY_TYPE_UTMI) ? "u2" : "u3");
4713d0407baSopenharmony_ci
4723d0407baSopenharmony_ci    if (u3phy_port->suspended) {
4733d0407baSopenharmony_ci        return 0;
4743d0407baSopenharmony_ci    }
4753d0407baSopenharmony_ci
4763d0407baSopenharmony_ci    if (u3phy_port->type == U3PHY_TYPE_UTMI) {
4773d0407baSopenharmony_ci        param_write(u3phy->u3phy_grf, &u3phy->cfgs->grfcfg.um_suspend, true);
4783d0407baSopenharmony_ci    } else {
4793d0407baSopenharmony_ci        /* current in p3 ? */
4803d0407baSopenharmony_ci        if (param_exped(u3phy->u3phy_grf, &u3phy->cfgs->grfcfg.pp_pwr_st, PIPE_PWR_P3)) {
4813d0407baSopenharmony_ci            goto done;
4823d0407baSopenharmony_ci        }
4833d0407baSopenharmony_ci
4843d0407baSopenharmony_ci        /* exit to p0 */
4853d0407baSopenharmony_ci        param_write(u3phy->u3phy_grf, &u3phy->cfgs->grfcfg.pp_pwr_en[PIPE_PWR_P0], true);
4863d0407baSopenharmony_ci        udelay(0x02);
4873d0407baSopenharmony_ci
4883d0407baSopenharmony_ci        /* enter to p3 from p0 */
4893d0407baSopenharmony_ci        param_write(u3phy->u3phy_grf, &u3phy->cfgs->grfcfg.pp_pwr_en[PIPE_PWR_P3], true);
4903d0407baSopenharmony_ci        udelay(0x06);
4913d0407baSopenharmony_ci
4923d0407baSopenharmony_ci        if (u3phy->cfgs->phy_pipe_power) {
4933d0407baSopenharmony_ci            dev_dbg(u3phy->dev, "do pipe power down\n");
4943d0407baSopenharmony_ci            u3phy->cfgs->phy_pipe_power(u3phy, u3phy_port, false);
4953d0407baSopenharmony_ci        }
4963d0407baSopenharmony_ci    }
4973d0407baSopenharmony_ci
4983d0407baSopenharmony_cidone:
4993d0407baSopenharmony_ci    rockchip_u3phy_clk_disable(u3phy);
5003d0407baSopenharmony_ci    u3phy_port->suspended = true;
5013d0407baSopenharmony_ci    return 0;
5023d0407baSopenharmony_ci}
5033d0407baSopenharmony_ci
5043d0407baSopenharmony_cistatic __maybe_unused struct phy *rockchip_u3phy_xlate(struct device *dev, struct of_phandle_args *args)
5053d0407baSopenharmony_ci{
5063d0407baSopenharmony_ci    struct rockchip_u3phy *u3phy = dev_get_drvdata(dev);
5073d0407baSopenharmony_ci    struct rockchip_u3phy_port *u3phy_port = NULL;
5083d0407baSopenharmony_ci    struct device_node *phy_np = args->np;
5093d0407baSopenharmony_ci    int index;
5103d0407baSopenharmony_ci
5113d0407baSopenharmony_ci    if (args->args_count != 1) {
5123d0407baSopenharmony_ci        dev_err(dev, "invalid number of cells in 'phy' property\n");
5133d0407baSopenharmony_ci        return ERR_PTR(-EINVAL);
5143d0407baSopenharmony_ci    }
5153d0407baSopenharmony_ci
5163d0407baSopenharmony_ci    for (index = 0; index < U3PHY_PORT_NUM; index++) {
5173d0407baSopenharmony_ci        if (phy_np == u3phy->ports[index].phy->dev.of_node) {
5183d0407baSopenharmony_ci            u3phy_port = &u3phy->ports[index];
5193d0407baSopenharmony_ci            break;
5203d0407baSopenharmony_ci        }
5213d0407baSopenharmony_ci    }
5223d0407baSopenharmony_ci
5233d0407baSopenharmony_ci    if (!u3phy_port) {
5243d0407baSopenharmony_ci        dev_err(dev, "failed to find appropriate phy\n");
5253d0407baSopenharmony_ci        return ERR_PTR(-EINVAL);
5263d0407baSopenharmony_ci    }
5273d0407baSopenharmony_ci
5283d0407baSopenharmony_ci    return u3phy_port->phy;
5293d0407baSopenharmony_ci}
5303d0407baSopenharmony_ci
5313d0407baSopenharmony_cistatic struct phy_ops rockchip_u3phy_ops = {
5323d0407baSopenharmony_ci    .init = rockchip_u3phy_init,
5333d0407baSopenharmony_ci    .exit = rockchip_u3phy_exit,
5343d0407baSopenharmony_ci    .power_on = rockchip_u3phy_power_on,
5353d0407baSopenharmony_ci    .power_off = rockchip_u3phy_power_off,
5363d0407baSopenharmony_ci    .owner = THIS_MODULE,
5373d0407baSopenharmony_ci};
5383d0407baSopenharmony_ci
5393d0407baSopenharmony_ci/*
5403d0407baSopenharmony_ci * The function manage host-phy port state and suspend/resume phy port
5413d0407baSopenharmony_ci * to save power automatically.
5423d0407baSopenharmony_ci *
5433d0407baSopenharmony_ci * we rely on utmi_linestate and utmi_hostdisconnect to identify whether
5443d0407baSopenharmony_ci * devices is disconnect or not. Besides, we do not need care it is FS/LS
5453d0407baSopenharmony_ci * disconnected or HS disconnected, actually, we just only need get the
5463d0407baSopenharmony_ci * device is disconnected at last through rearm the delayed work,
5473d0407baSopenharmony_ci * to suspend the phy port in _PHY_STATE_DISCONNECT_ case.
5483d0407baSopenharmony_ci */
5493d0407baSopenharmony_cistatic void rockchip_u3phy_um_sm_work(struct work_struct *work)
5503d0407baSopenharmony_ci{
5513d0407baSopenharmony_ci    struct rockchip_u3phy_port *u3phy_port = container_of(work, struct rockchip_u3phy_port, um_sm_work.work);
5523d0407baSopenharmony_ci    struct rockchip_u3phy *u3phy = dev_get_drvdata(u3phy_port->phy->dev.parent);
5533d0407baSopenharmony_ci    unsigned int sh = u3phy->cfgs->grfcfg.um_hstdct.bitend - u3phy->cfgs->grfcfg.um_hstdct.bitstart + 1;
5543d0407baSopenharmony_ci    unsigned int ul, uhd, state;
5553d0407baSopenharmony_ci    unsigned int ul_mask, uhd_mask;
5563d0407baSopenharmony_ci    int ret;
5573d0407baSopenharmony_ci
5583d0407baSopenharmony_ci    mutex_lock(&u3phy_port->mutex);
5593d0407baSopenharmony_ci
5603d0407baSopenharmony_ci    ret = regmap_read(u3phy->u3phy_grf, u3phy->cfgs->grfcfg.um_ls.offset, &ul);
5613d0407baSopenharmony_ci    if (ret < 0) {
5623d0407baSopenharmony_ci        goto next_schedule;
5633d0407baSopenharmony_ci    }
5643d0407baSopenharmony_ci
5653d0407baSopenharmony_ci    ret = regmap_read(u3phy->u3phy_grf, u3phy->cfgs->grfcfg.um_hstdct.offset, &uhd);
5663d0407baSopenharmony_ci    if (ret < 0) {
5673d0407baSopenharmony_ci        goto next_schedule;
5683d0407baSopenharmony_ci    }
5693d0407baSopenharmony_ci
5703d0407baSopenharmony_ci    uhd_mask = GENMASK(u3phy->cfgs->grfcfg.um_hstdct.bitend, u3phy->cfgs->grfcfg.um_hstdct.bitstart);
5713d0407baSopenharmony_ci    ul_mask = GENMASK(u3phy->cfgs->grfcfg.um_ls.bitend, u3phy->cfgs->grfcfg.um_ls.bitstart);
5723d0407baSopenharmony_ci
5733d0407baSopenharmony_ci    /* stitch on um_ls and um_hstdct as phy state */
5743d0407baSopenharmony_ci    state = ((uhd & uhd_mask) >> u3phy->cfgs->grfcfg.um_hstdct.bitstart) |
5753d0407baSopenharmony_ci            (((ul & ul_mask) >> u3phy->cfgs->grfcfg.um_ls.bitstart) << sh);
5763d0407baSopenharmony_ci
5773d0407baSopenharmony_ci    switch (state) {
5783d0407baSopenharmony_ci        case PHY_UTMI_HS_ONLINE:
5793d0407baSopenharmony_ci            dev_dbg(&u3phy_port->phy->dev, "HS online\n");
5803d0407baSopenharmony_ci            break;
5813d0407baSopenharmony_ci        case PHY_UTMI_FS_LS_ONLINE:
5823d0407baSopenharmony_ci            /*
5833d0407baSopenharmony_ci             * For FS/LS device, the online state share with connect state
5843d0407baSopenharmony_ci             * from um_ls and um_hstdct register, so we distinguish
5853d0407baSopenharmony_ci             * them via suspended flag.
5863d0407baSopenharmony_ci             *
5873d0407baSopenharmony_ci             * Plus, there are two cases, one is D- Line pull-up, and D+
5883d0407baSopenharmony_ci             * line pull-down, the state is 4; another is D+ line pull-up,
5893d0407baSopenharmony_ci             * and D- line pull-down, the state is 2.
5903d0407baSopenharmony_ci             */
5913d0407baSopenharmony_ci            if (!u3phy_port->suspended) {
5923d0407baSopenharmony_ci                /* D- line pull-up, D+ line pull-down */
5933d0407baSopenharmony_ci                dev_dbg(&u3phy_port->phy->dev, "FS/LS online\n");
5943d0407baSopenharmony_ci                break;
5953d0407baSopenharmony_ci            }
5963d0407baSopenharmony_ci            /* fall through */
5973d0407baSopenharmony_ci        case PHY_UTMI_CONNECT:
5983d0407baSopenharmony_ci            if (u3phy_port->suspended) {
5993d0407baSopenharmony_ci                dev_dbg(&u3phy_port->phy->dev, "Connected\n");
6003d0407baSopenharmony_ci                rockchip_u3phy_power_on(u3phy_port->phy);
6013d0407baSopenharmony_ci                u3phy_port->suspended = false;
6023d0407baSopenharmony_ci            } else {
6033d0407baSopenharmony_ci                /* D+ line pull-up, D- line pull-down */
6043d0407baSopenharmony_ci                dev_dbg(&u3phy_port->phy->dev, "FS/LS online\n");
6053d0407baSopenharmony_ci            }
6063d0407baSopenharmony_ci            break;
6073d0407baSopenharmony_ci        case PHY_UTMI_DISCONNECT:
6083d0407baSopenharmony_ci            if (!u3phy_port->suspended) {
6093d0407baSopenharmony_ci                dev_dbg(&u3phy_port->phy->dev, "Disconnected\n");
6103d0407baSopenharmony_ci                rockchip_u3phy_power_off(u3phy_port->phy);
6113d0407baSopenharmony_ci                u3phy_port->suspended = true;
6123d0407baSopenharmony_ci            }
6133d0407baSopenharmony_ci
6143d0407baSopenharmony_ci            /*
6153d0407baSopenharmony_ci             * activate the linestate detection to get the next device
6163d0407baSopenharmony_ci             * plug-in irq.
6173d0407baSopenharmony_ci             */
6183d0407baSopenharmony_ci            param_write(u3phy->u3phy_grf, &u3phy->cfgs->grfcfg.ls_det_st, true);
6193d0407baSopenharmony_ci            param_write(u3phy->u3phy_grf, &u3phy->cfgs->grfcfg.ls_det_en, true);
6203d0407baSopenharmony_ci
6213d0407baSopenharmony_ci            /*
6223d0407baSopenharmony_ci             * we don't need to rearm the delayed work when the phy port
6233d0407baSopenharmony_ci             * is suspended.
6243d0407baSopenharmony_ci             */
6253d0407baSopenharmony_ci            mutex_unlock(&u3phy_port->mutex);
6263d0407baSopenharmony_ci            return;
6273d0407baSopenharmony_ci        default:
6283d0407baSopenharmony_ci            dev_dbg(&u3phy_port->phy->dev, "unknown phy state\n");
6293d0407baSopenharmony_ci            break;
6303d0407baSopenharmony_ci    }
6313d0407baSopenharmony_ci
6323d0407baSopenharmony_cinext_schedule:
6333d0407baSopenharmony_ci    mutex_unlock(&u3phy_port->mutex);
6343d0407baSopenharmony_ci    schedule_delayed_work(&u3phy_port->um_sm_work, SCHEDULE_DELAY);
6353d0407baSopenharmony_ci}
6363d0407baSopenharmony_ci
6373d0407baSopenharmony_cistatic irqreturn_t rockchip_u3phy_um_ls_irq(int irq, void *data)
6383d0407baSopenharmony_ci{
6393d0407baSopenharmony_ci    struct rockchip_u3phy_port *u3phy_port = data;
6403d0407baSopenharmony_ci    struct rockchip_u3phy *u3phy = dev_get_drvdata(u3phy_port->phy->dev.parent);
6413d0407baSopenharmony_ci
6423d0407baSopenharmony_ci    if (!param_exped(u3phy->u3phy_grf, &u3phy->cfgs->grfcfg.ls_det_st, u3phy->cfgs->grfcfg.ls_det_st.dvalue)) {
6433d0407baSopenharmony_ci        return IRQ_NONE;
6443d0407baSopenharmony_ci    }
6453d0407baSopenharmony_ci
6463d0407baSopenharmony_ci    dev_dbg(u3phy->dev, "utmi linestate interrupt\n");
6473d0407baSopenharmony_ci    mutex_lock(&u3phy_port->mutex);
6483d0407baSopenharmony_ci
6493d0407baSopenharmony_ci    /* disable linestate detect irq and clear its status */
6503d0407baSopenharmony_ci    param_write(u3phy->u3phy_grf, &u3phy->cfgs->grfcfg.ls_det_en, false);
6513d0407baSopenharmony_ci    param_write(u3phy->u3phy_grf, &u3phy->cfgs->grfcfg.ls_det_st, true);
6523d0407baSopenharmony_ci
6533d0407baSopenharmony_ci    mutex_unlock(&u3phy_port->mutex);
6543d0407baSopenharmony_ci
6553d0407baSopenharmony_ci    /*
6563d0407baSopenharmony_ci     * In this case for host phy, a new device is plugged in, meanwhile,
6573d0407baSopenharmony_ci     * if the phy port is suspended, we need rearm the work to resume it
6583d0407baSopenharmony_ci     * and mange its states; otherwise, we just return irq handled.
6593d0407baSopenharmony_ci     */
6603d0407baSopenharmony_ci    if (u3phy_port->suspended) {
6613d0407baSopenharmony_ci        dev_dbg(u3phy->dev, "schedule utmi sm work\n");
6623d0407baSopenharmony_ci        rockchip_u3phy_um_sm_work(&u3phy_port->um_sm_work.work);
6633d0407baSopenharmony_ci    }
6643d0407baSopenharmony_ci
6653d0407baSopenharmony_ci    return IRQ_HANDLED;
6663d0407baSopenharmony_ci}
6673d0407baSopenharmony_ci
6683d0407baSopenharmony_cistatic int rockchip_u3phy_parse_dt(struct rockchip_u3phy *u3phy, struct platform_device *pdev)
6693d0407baSopenharmony_ci
6703d0407baSopenharmony_ci{
6713d0407baSopenharmony_ci    struct device *dev = &pdev->dev;
6723d0407baSopenharmony_ci    struct device_node *np = dev->of_node;
6733d0407baSopenharmony_ci    int ret, i, clk;
6743d0407baSopenharmony_ci
6753d0407baSopenharmony_ci    u3phy->um_ls_irq = platform_get_irq_byname(pdev, "linestate");
6763d0407baSopenharmony_ci    if (u3phy->um_ls_irq < 0) {
6773d0407baSopenharmony_ci        dev_err(dev, "get utmi linestate irq failed\n");
6783d0407baSopenharmony_ci        return -ENXIO;
6793d0407baSopenharmony_ci    }
6803d0407baSopenharmony_ci
6813d0407baSopenharmony_ci    /* Get Vbus regulators */
6823d0407baSopenharmony_ci    u3phy->vbus = devm_regulator_get_optional(dev, "vbus");
6833d0407baSopenharmony_ci    if (IS_ERR(u3phy->vbus)) {
6843d0407baSopenharmony_ci        ret = PTR_ERR(u3phy->vbus);
6853d0407baSopenharmony_ci        if (ret == -EPROBE_DEFER) {
6863d0407baSopenharmony_ci            return ret;
6873d0407baSopenharmony_ci        }
6883d0407baSopenharmony_ci
6893d0407baSopenharmony_ci        dev_warn(dev, "Failed to get VBUS supply regulator\n");
6903d0407baSopenharmony_ci        u3phy->vbus = NULL;
6913d0407baSopenharmony_ci    }
6923d0407baSopenharmony_ci
6933d0407baSopenharmony_ci    for (clk = 0; clk < U3PHY_MAX_CLKS; clk++) {
6943d0407baSopenharmony_ci        u3phy->clks[clk] = of_clk_get(np, clk);
6953d0407baSopenharmony_ci        if (IS_ERR(u3phy->clks[clk])) {
6963d0407baSopenharmony_ci            ret = PTR_ERR(u3phy->clks[clk]);
6973d0407baSopenharmony_ci            if (ret == -EPROBE_DEFER) {
6983d0407baSopenharmony_ci                goto err_put_clks;
6993d0407baSopenharmony_ci            }
7003d0407baSopenharmony_ci            u3phy->clks[clk] = NULL;
7013d0407baSopenharmony_ci            break;
7023d0407baSopenharmony_ci        }
7033d0407baSopenharmony_ci    }
7043d0407baSopenharmony_ci
7053d0407baSopenharmony_ci    for (i = 0; i < U3PHY_RESET_MAX; i++) {
7063d0407baSopenharmony_ci        u3phy->rsts[i] = devm_reset_control_get(dev, get_rest_name(i));
7073d0407baSopenharmony_ci        if (IS_ERR(u3phy->rsts[i])) {
7083d0407baSopenharmony_ci            dev_info(dev, "no %s reset control specified\n", get_rest_name(i));
7093d0407baSopenharmony_ci            u3phy->rsts[i] = NULL;
7103d0407baSopenharmony_ci        }
7113d0407baSopenharmony_ci    }
7123d0407baSopenharmony_ci
7133d0407baSopenharmony_ci    return 0;
7143d0407baSopenharmony_ci
7153d0407baSopenharmony_cierr_put_clks:
7163d0407baSopenharmony_ci    while (--clk >= 0) {
7173d0407baSopenharmony_ci        clk_put(u3phy->clks[clk]);
7183d0407baSopenharmony_ci    }
7193d0407baSopenharmony_ci    return ret;
7203d0407baSopenharmony_ci}
7213d0407baSopenharmony_ci
7223d0407baSopenharmony_cistatic int rockchip_u3phy_port_init(struct rockchip_u3phy *u3phy, struct rockchip_u3phy_port *u3phy_port,
7233d0407baSopenharmony_ci                                    struct device_node *child_np)
7243d0407baSopenharmony_ci{
7253d0407baSopenharmony_ci    struct resource res;
7263d0407baSopenharmony_ci    struct phy *phy;
7273d0407baSopenharmony_ci    int ret;
7283d0407baSopenharmony_ci
7293d0407baSopenharmony_ci    dev_dbg(u3phy->dev, "u3phy port initialize\n");
7303d0407baSopenharmony_ci
7313d0407baSopenharmony_ci    mutex_init(&u3phy_port->mutex);
7323d0407baSopenharmony_ci    u3phy_port->suspended = true; /* initial status */
7333d0407baSopenharmony_ci
7343d0407baSopenharmony_ci    phy = devm_phy_create(u3phy->dev, child_np, &rockchip_u3phy_ops);
7353d0407baSopenharmony_ci    if (IS_ERR(phy)) {
7363d0407baSopenharmony_ci        dev_err(u3phy->dev, "failed to create phy\n");
7373d0407baSopenharmony_ci        return PTR_ERR(phy);
7383d0407baSopenharmony_ci    }
7393d0407baSopenharmony_ci
7403d0407baSopenharmony_ci    u3phy_port->phy = phy;
7413d0407baSopenharmony_ci
7423d0407baSopenharmony_ci    ret = of_address_to_resource(child_np, 0, &res);
7433d0407baSopenharmony_ci    if (ret) {
7443d0407baSopenharmony_ci        dev_err(u3phy->dev, "failed to get address resource(np-%s)\n", child_np->name);
7453d0407baSopenharmony_ci        return ret;
7463d0407baSopenharmony_ci    }
7473d0407baSopenharmony_ci
7483d0407baSopenharmony_ci    u3phy_port->base = devm_ioremap_resource(&u3phy_port->phy->dev, &res);
7493d0407baSopenharmony_ci    if (IS_ERR(u3phy_port->base)) {
7503d0407baSopenharmony_ci        dev_err(u3phy->dev, "failed to remap phy regs\n");
7513d0407baSopenharmony_ci        return PTR_ERR(u3phy_port->base);
7523d0407baSopenharmony_ci    }
7533d0407baSopenharmony_ci
7543d0407baSopenharmony_ci    if (!of_node_cmp(child_np->name, "pipe")) {
7553d0407baSopenharmony_ci        u3phy_port->type = U3PHY_TYPE_PIPE;
7563d0407baSopenharmony_ci        u3phy_port->refclk_25m_quirk = of_property_read_bool(child_np, "rockchip,refclk-25m-quirk");
7573d0407baSopenharmony_ci    } else {
7583d0407baSopenharmony_ci        u3phy_port->type = U3PHY_TYPE_UTMI;
7593d0407baSopenharmony_ci        INIT_DELAYED_WORK(&u3phy_port->um_sm_work, rockchip_u3phy_um_sm_work);
7603d0407baSopenharmony_ci
7613d0407baSopenharmony_ci        ret = devm_request_threaded_irq(u3phy->dev, u3phy->um_ls_irq, NULL, rockchip_u3phy_um_ls_irq, IRQF_ONESHOT,
7623d0407baSopenharmony_ci                                        "rockchip_u3phy", u3phy_port);
7633d0407baSopenharmony_ci        if (ret) {
7643d0407baSopenharmony_ci            dev_err(u3phy->dev, "failed to request utmi linestate irq handle\n");
7653d0407baSopenharmony_ci            return ret;
7663d0407baSopenharmony_ci        }
7673d0407baSopenharmony_ci    }
7683d0407baSopenharmony_ci
7693d0407baSopenharmony_ci    if (u3phy->cfgs->phy_tuning) {
7703d0407baSopenharmony_ci        dev_dbg(u3phy->dev, "do u3phy tuning\n");
7713d0407baSopenharmony_ci        ret = u3phy->cfgs->phy_tuning(u3phy, u3phy_port, child_np);
7723d0407baSopenharmony_ci        if (ret) {
7733d0407baSopenharmony_ci            return ret;
7743d0407baSopenharmony_ci        }
7753d0407baSopenharmony_ci    }
7763d0407baSopenharmony_ci
7773d0407baSopenharmony_ci    phy_set_drvdata(u3phy_port->phy, u3phy_port);
7783d0407baSopenharmony_ci    return 0;
7793d0407baSopenharmony_ci}
7803d0407baSopenharmony_ci
7813d0407baSopenharmony_cistatic int rockchip_u3phy_on_init(struct usb_phy *usb_phy)
7823d0407baSopenharmony_ci{
7833d0407baSopenharmony_ci    struct rockchip_u3phy *u3phy = container_of(usb_phy, struct rockchip_u3phy, usb_phy);
7843d0407baSopenharmony_ci
7853d0407baSopenharmony_ci    rockchip_u3phy_rest_deassert(u3phy, U3PHY_POR_RST | U3PHY_MAC_RST);
7863d0407baSopenharmony_ci    return 0;
7873d0407baSopenharmony_ci}
7883d0407baSopenharmony_ci
7893d0407baSopenharmony_cistatic void rockchip_u3phy_on_shutdown(struct usb_phy *usb_phy)
7903d0407baSopenharmony_ci{
7913d0407baSopenharmony_ci    struct rockchip_u3phy *u3phy = container_of(usb_phy, struct rockchip_u3phy, usb_phy);
7923d0407baSopenharmony_ci    int rst;
7933d0407baSopenharmony_ci
7943d0407baSopenharmony_ci    for (rst = 0; rst < U3PHY_RESET_MAX; rst++) {
7953d0407baSopenharmony_ci        if (u3phy->rsts[rst] && rst != UTMI_APB_RSTN && rst != PIPE_APB_RSTN) {
7963d0407baSopenharmony_ci            reset_control_assert(u3phy->rsts[rst]);
7973d0407baSopenharmony_ci        }
7983d0407baSopenharmony_ci    }
7993d0407baSopenharmony_ci    udelay(1);
8003d0407baSopenharmony_ci}
8013d0407baSopenharmony_ci
8023d0407baSopenharmony_cistatic int rockchip_u3phy_on_disconnect(struct usb_phy *usb_phy, enum usb_device_speed speed)
8033d0407baSopenharmony_ci{
8043d0407baSopenharmony_ci    struct rockchip_u3phy *u3phy = container_of(usb_phy, struct rockchip_u3phy, usb_phy);
8053d0407baSopenharmony_ci
8063d0407baSopenharmony_ci    dev_info(u3phy->dev, "%s device has disconnected\n", (speed == USB_SPEED_SUPER) ? "U3" : "UW/U2/U1.1/U1");
8073d0407baSopenharmony_ci
8083d0407baSopenharmony_ci    if (speed == USB_SPEED_SUPER) {
8093d0407baSopenharmony_ci        atomic_notifier_call_chain(&usb_phy->notifier, 0, NULL);
8103d0407baSopenharmony_ci    }
8113d0407baSopenharmony_ci
8123d0407baSopenharmony_ci    return 0;
8133d0407baSopenharmony_ci}
8143d0407baSopenharmony_ci
8153d0407baSopenharmony_cistatic int rockchip_u3phy_probe(struct platform_device *pdev)
8163d0407baSopenharmony_ci{
8173d0407baSopenharmony_ci    struct device *dev = &pdev->dev;
8183d0407baSopenharmony_ci    struct device_node *np = dev->of_node;
8193d0407baSopenharmony_ci    struct device_node *child_np;
8203d0407baSopenharmony_ci    struct phy_provider *provider;
8213d0407baSopenharmony_ci    struct rockchip_u3phy *u3phy;
8223d0407baSopenharmony_ci    const struct rockchip_u3phy_cfg *phy_cfgs;
8233d0407baSopenharmony_ci    const struct of_device_id *match;
8243d0407baSopenharmony_ci    unsigned int reg[2];
8253d0407baSopenharmony_ci    int index, ret;
8263d0407baSopenharmony_ci
8273d0407baSopenharmony_ci    match = of_match_device(dev->driver->of_match_table, dev);
8283d0407baSopenharmony_ci    if (!match || !match->data) {
8293d0407baSopenharmony_ci        dev_err(dev, "phy-cfgs are not assigned!\n");
8303d0407baSopenharmony_ci        return -EINVAL;
8313d0407baSopenharmony_ci    }
8323d0407baSopenharmony_ci
8333d0407baSopenharmony_ci    u3phy = devm_kzalloc(dev, sizeof(*u3phy), GFP_KERNEL);
8343d0407baSopenharmony_ci    if (!u3phy) {
8353d0407baSopenharmony_ci        return -ENOMEM;
8363d0407baSopenharmony_ci    }
8373d0407baSopenharmony_ci
8383d0407baSopenharmony_ci    u3phy->u3phy_grf = syscon_regmap_lookup_by_phandle(np, "rockchip,u3phygrf");
8393d0407baSopenharmony_ci    if (IS_ERR(u3phy->u3phy_grf)) {
8403d0407baSopenharmony_ci        return PTR_ERR(u3phy->u3phy_grf);
8413d0407baSopenharmony_ci    }
8423d0407baSopenharmony_ci
8433d0407baSopenharmony_ci    u3phy->grf = syscon_regmap_lookup_by_phandle(np, "rockchip,grf");
8443d0407baSopenharmony_ci    if (IS_ERR(u3phy->grf)) {
8453d0407baSopenharmony_ci        dev_err(dev, "Missing rockchip,grf property\n");
8463d0407baSopenharmony_ci        return PTR_ERR(u3phy->grf);
8473d0407baSopenharmony_ci    }
8483d0407baSopenharmony_ci
8493d0407baSopenharmony_ci    if (of_property_read_u32_array(np, "reg", reg, 0x02)) {
8503d0407baSopenharmony_ci        dev_err(dev, "the reg property is not assigned in %s node\n", np->name);
8513d0407baSopenharmony_ci        return -EINVAL;
8523d0407baSopenharmony_ci    }
8533d0407baSopenharmony_ci
8543d0407baSopenharmony_ci    u3phy->dev = dev;
8553d0407baSopenharmony_ci    u3phy->vbus_enabled = false;
8563d0407baSopenharmony_ci    phy_cfgs = match->data;
8573d0407baSopenharmony_ci    platform_set_drvdata(pdev, u3phy);
8583d0407baSopenharmony_ci
8593d0407baSopenharmony_ci    /* find out a proper config which can be matched with dt. */
8603d0407baSopenharmony_ci    index = 0;
8613d0407baSopenharmony_ci    while (phy_cfgs[index].reg) {
8623d0407baSopenharmony_ci        if (phy_cfgs[index].reg == reg[1]) {
8633d0407baSopenharmony_ci            u3phy->cfgs = &phy_cfgs[index];
8643d0407baSopenharmony_ci            break;
8653d0407baSopenharmony_ci        }
8663d0407baSopenharmony_ci
8673d0407baSopenharmony_ci        ++index;
8683d0407baSopenharmony_ci    }
8693d0407baSopenharmony_ci
8703d0407baSopenharmony_ci    if (!u3phy->cfgs) {
8713d0407baSopenharmony_ci        dev_err(dev, "no phy-cfgs can be matched with %s node\n", np->name);
8723d0407baSopenharmony_ci        return -EINVAL;
8733d0407baSopenharmony_ci    }
8743d0407baSopenharmony_ci
8753d0407baSopenharmony_ci    ret = rockchip_u3phy_parse_dt(u3phy, pdev);
8763d0407baSopenharmony_ci    if (ret) {
8773d0407baSopenharmony_ci        dev_err(dev, "parse dt failed, ret(%d)\n", ret);
8783d0407baSopenharmony_ci        return ret;
8793d0407baSopenharmony_ci    }
8803d0407baSopenharmony_ci
8813d0407baSopenharmony_ci    ret = rockchip_u3phy_clk_enable(u3phy);
8823d0407baSopenharmony_ci    if (ret) {
8833d0407baSopenharmony_ci        dev_err(dev, "clk enable failed, ret(%d)\n", ret);
8843d0407baSopenharmony_ci        return ret;
8853d0407baSopenharmony_ci    }
8863d0407baSopenharmony_ci
8873d0407baSopenharmony_ci    rockchip_u3phy_rest_assert(u3phy);
8883d0407baSopenharmony_ci    rockchip_u3phy_rest_deassert(u3phy, U3PHY_APB_RST | U3PHY_POR_RST);
8893d0407baSopenharmony_ci
8903d0407baSopenharmony_ci    index = 0;
8913d0407baSopenharmony_ci    for_each_available_child_of_node(np, child_np)
8923d0407baSopenharmony_ci    {
8933d0407baSopenharmony_ci        struct rockchip_u3phy_port *u3phy_port = &u3phy->ports[index];
8943d0407baSopenharmony_ci
8953d0407baSopenharmony_ci        u3phy_port->index = index;
8963d0407baSopenharmony_ci        ret = rockchip_u3phy_port_init(u3phy, u3phy_port, child_np);
8973d0407baSopenharmony_ci        if (ret) {
8983d0407baSopenharmony_ci            dev_err(dev, "u3phy port init failed,ret(%d)\n", ret);
8993d0407baSopenharmony_ci            goto put_child;
9003d0407baSopenharmony_ci        }
9013d0407baSopenharmony_ci
9023d0407baSopenharmony_ci        /* to prevent out of boundary */
9033d0407baSopenharmony_ci        if (++index >= U3PHY_PORT_NUM) {
9043d0407baSopenharmony_ci            break;
9053d0407baSopenharmony_ci        }
9063d0407baSopenharmony_ci    }
9073d0407baSopenharmony_ci
9083d0407baSopenharmony_ci    provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
9093d0407baSopenharmony_ci    if (IS_ERR_OR_NULL(provider)) {
9103d0407baSopenharmony_ci        goto put_child;
9113d0407baSopenharmony_ci    }
9123d0407baSopenharmony_ci
9133d0407baSopenharmony_ci    rockchip_u3phy_rest_deassert(u3phy, U3PHY_MAC_RST);
9143d0407baSopenharmony_ci    rockchip_u3phy_clk_disable(u3phy);
9153d0407baSopenharmony_ci
9163d0407baSopenharmony_ci    u3phy->usb_phy.dev = dev;
9173d0407baSopenharmony_ci    u3phy->usb_phy.init = rockchip_u3phy_on_init;
9183d0407baSopenharmony_ci    u3phy->usb_phy.shutdown = rockchip_u3phy_on_shutdown;
9193d0407baSopenharmony_ci    u3phy->usb_phy.notify_disconnect = rockchip_u3phy_on_disconnect;
9203d0407baSopenharmony_ci    usb_add_phy(&u3phy->usb_phy, USB_PHY_TYPE_USB3);
9213d0407baSopenharmony_ci    ATOMIC_INIT_NOTIFIER_HEAD(&u3phy->usb_phy.notifier);
9223d0407baSopenharmony_ci
9233d0407baSopenharmony_ci    rockchip_u3phy_debugfs_init(u3phy);
9243d0407baSopenharmony_ci
9253d0407baSopenharmony_ci    dev_info(dev, "Rockchip u3phy initialized successfully\n");
9263d0407baSopenharmony_ci    return 0;
9273d0407baSopenharmony_ci
9283d0407baSopenharmony_ciput_child:
9293d0407baSopenharmony_ci    of_node_put(child_np);
9303d0407baSopenharmony_ci    return ret;
9313d0407baSopenharmony_ci}
9323d0407baSopenharmony_ci
9333d0407baSopenharmony_cistatic int rk3328_u3phy_pipe_power(struct rockchip_u3phy *u3phy, struct rockchip_u3phy_port *u3phy_port, bool on)
9343d0407baSopenharmony_ci{
9353d0407baSopenharmony_ci    unsigned int reg;
9363d0407baSopenharmony_ci
9373d0407baSopenharmony_ci    if (on) {
9383d0407baSopenharmony_ci        reg = readl(u3phy_port->base + 0x1a8);
9393d0407baSopenharmony_ci        reg &= ~BIT(0x04); /* ldo power up */
9403d0407baSopenharmony_ci        writel(reg, u3phy_port->base + 0x1a8);
9413d0407baSopenharmony_ci
9423d0407baSopenharmony_ci        reg = readl(u3phy_port->base + 0x044);
9433d0407baSopenharmony_ci        reg &= ~BIT(0x04); /* bg power on */
9443d0407baSopenharmony_ci        writel(reg, u3phy_port->base + 0x044);
9453d0407baSopenharmony_ci
9463d0407baSopenharmony_ci        reg = readl(u3phy_port->base + 0x150);
9473d0407baSopenharmony_ci        reg |= BIT(0x06); /* tx bias enable */
9483d0407baSopenharmony_ci        writel(reg, u3phy_port->base + 0x150);
9493d0407baSopenharmony_ci
9503d0407baSopenharmony_ci        reg = readl(u3phy_port->base + 0x080);
9513d0407baSopenharmony_ci        reg &= ~BIT(0x02); /* tx cm power up */
9523d0407baSopenharmony_ci        writel(reg, u3phy_port->base + 0x080);
9533d0407baSopenharmony_ci
9543d0407baSopenharmony_ci        reg = readl(u3phy_port->base + 0x0c0);
9553d0407baSopenharmony_ci        /* tx obs enable and rx cm enable */
9563d0407baSopenharmony_ci        reg |= (BIT(0x03) | BIT(0x04));
9573d0407baSopenharmony_ci        writel(reg, u3phy_port->base + 0x0c0);
9583d0407baSopenharmony_ci
9593d0407baSopenharmony_ci        udelay(1);
9603d0407baSopenharmony_ci    } else {
9613d0407baSopenharmony_ci        reg = readl(u3phy_port->base + 0x1a8);
9623d0407baSopenharmony_ci        reg |= BIT(0x04); /* ldo power down */
9633d0407baSopenharmony_ci        writel(reg, u3phy_port->base + 0x1a8);
9643d0407baSopenharmony_ci
9653d0407baSopenharmony_ci        reg = readl(u3phy_port->base + 0x044);
9663d0407baSopenharmony_ci        reg |= BIT(0x04); /* bg power down */
9673d0407baSopenharmony_ci        writel(reg, u3phy_port->base + 0x044);
9683d0407baSopenharmony_ci
9693d0407baSopenharmony_ci        reg = readl(u3phy_port->base + 0x150);
9703d0407baSopenharmony_ci        reg &= ~BIT(0x06); /* tx bias disable */
9713d0407baSopenharmony_ci        writel(reg, u3phy_port->base + 0x150);
9723d0407baSopenharmony_ci
9733d0407baSopenharmony_ci        reg = readl(u3phy_port->base + 0x080);
9743d0407baSopenharmony_ci        reg |= BIT(0x02); /* tx cm power down */
9753d0407baSopenharmony_ci        writel(reg, u3phy_port->base + 0x080);
9763d0407baSopenharmony_ci
9773d0407baSopenharmony_ci        reg = readl(u3phy_port->base + 0x0c0);
9783d0407baSopenharmony_ci        /* tx obs disable and rx cm disable */
9793d0407baSopenharmony_ci        reg &= ~(BIT(0x03) | BIT(0x04));
9803d0407baSopenharmony_ci        writel(reg, u3phy_port->base + 0x0c0);
9813d0407baSopenharmony_ci    }
9823d0407baSopenharmony_ci
9833d0407baSopenharmony_ci    return 0;
9843d0407baSopenharmony_ci}
9853d0407baSopenharmony_ci
9863d0407baSopenharmony_cistatic int rk3328_u3phy_tuning(struct rockchip_u3phy *u3phy, struct rockchip_u3phy_port *u3phy_port,
9873d0407baSopenharmony_ci                               struct device_node *child_np)
9883d0407baSopenharmony_ci{
9893d0407baSopenharmony_ci    if (u3phy_port->type == U3PHY_TYPE_UTMI) {
9903d0407baSopenharmony_ci        /*
9913d0407baSopenharmony_ci         * For rk3328 SoC, pre-emphasis and pre-emphasis strength must
9923d0407baSopenharmony_ci         * be written as one fixed value as below.
9933d0407baSopenharmony_ci         *
9943d0407baSopenharmony_ci         * Dissimilarly, the odt 45ohm value should be flexibly tuninged
9953d0407baSopenharmony_ci         * for the different boards to adjust HS eye height, so its
9963d0407baSopenharmony_ci         * value can be assigned in DT in code design.
9973d0407baSopenharmony_ci         */
9983d0407baSopenharmony_ci
9993d0407baSopenharmony_ci        /* {bits[2:0]=111}: always enable pre-emphasis */
10003d0407baSopenharmony_ci        u3phy->apbcfg.u2_pre_emp = 0x0f;
10013d0407baSopenharmony_ci
10023d0407baSopenharmony_ci        /* {bits[5:3]=000}: pre-emphasis strength as the weakest */
10033d0407baSopenharmony_ci        u3phy->apbcfg.u2_pre_emp_sth = 0x41;
10043d0407baSopenharmony_ci
10053d0407baSopenharmony_ci        /* {bits[4:0]=10101}: odt 45ohm tuning */
10063d0407baSopenharmony_ci        u3phy->apbcfg.u2_odt_tuning = 0xb5;
10073d0407baSopenharmony_ci        /* optional override of the odt 45ohm tuning */
10083d0407baSopenharmony_ci        of_property_read_u32(child_np, "rockchip,odt-val-tuning", &u3phy->apbcfg.u2_odt_tuning);
10093d0407baSopenharmony_ci
10103d0407baSopenharmony_ci        writel(u3phy->apbcfg.u2_pre_emp, u3phy_port->base + 0x030);
10113d0407baSopenharmony_ci        writel(u3phy->apbcfg.u2_pre_emp_sth, u3phy_port->base + 0x040);
10123d0407baSopenharmony_ci        writel(u3phy->apbcfg.u2_odt_tuning, u3phy_port->base + 0x11c);
10133d0407baSopenharmony_ci    } else if (u3phy_port->type == U3PHY_TYPE_PIPE) {
10143d0407baSopenharmony_ci        if (u3phy_port->refclk_25m_quirk) {
10153d0407baSopenharmony_ci            dev_dbg(u3phy->dev, "switch to 25m refclk\n");
10163d0407baSopenharmony_ci            /* ref clk switch to 25M */
10173d0407baSopenharmony_ci            writel(0x64, u3phy_port->base + 0x11c);
10183d0407baSopenharmony_ci            writel(0x64, u3phy_port->base + 0x028);
10193d0407baSopenharmony_ci            writel(0x01, u3phy_port->base + 0x020);
10203d0407baSopenharmony_ci            writel(0x21, u3phy_port->base + 0x030);
10213d0407baSopenharmony_ci            writel(0x06, u3phy_port->base + 0x108);
10223d0407baSopenharmony_ci            writel(0x00, u3phy_port->base + 0x118);
10233d0407baSopenharmony_ci        } else {
10243d0407baSopenharmony_ci            /* configure for 24M ref clk */
10253d0407baSopenharmony_ci            writel(0x80, u3phy_port->base + 0x10c);
10263d0407baSopenharmony_ci            writel(0x01, u3phy_port->base + 0x118);
10273d0407baSopenharmony_ci            writel(0x38, u3phy_port->base + 0x11c);
10283d0407baSopenharmony_ci            writel(0x83, u3phy_port->base + 0x020);
10293d0407baSopenharmony_ci            writel(0x02, u3phy_port->base + 0x108);
10303d0407baSopenharmony_ci        }
10313d0407baSopenharmony_ci
10323d0407baSopenharmony_ci        /* Enable SSC */
10333d0407baSopenharmony_ci        udelay(0x03);
10343d0407baSopenharmony_ci        writel(0x08, u3phy_port->base + 0x000);
10353d0407baSopenharmony_ci        writel(0x0c, u3phy_port->base + 0x120);
10363d0407baSopenharmony_ci
10373d0407baSopenharmony_ci        /* Tuning Rx for compliance RJTL test */
10383d0407baSopenharmony_ci        writel(0x70, u3phy_port->base + 0x150);
10393d0407baSopenharmony_ci        writel(0x12, u3phy_port->base + 0x0c8);
10403d0407baSopenharmony_ci        writel(0x05, u3phy_port->base + 0x148);
10413d0407baSopenharmony_ci        writel(0x08, u3phy_port->base + 0x068);
10423d0407baSopenharmony_ci        writel(0xf0, u3phy_port->base + 0x1c4);
10433d0407baSopenharmony_ci        writel(0xff, u3phy_port->base + 0x070);
10443d0407baSopenharmony_ci        writel(0x0f, u3phy_port->base + 0x06c);
10453d0407baSopenharmony_ci        writel(0xe0, u3phy_port->base + 0x060);
10463d0407baSopenharmony_ci
10473d0407baSopenharmony_ci        /*
10483d0407baSopenharmony_ci         * Tuning Tx to increase the bias current
10493d0407baSopenharmony_ci         * used in TX driver and RX EQ, it can
10503d0407baSopenharmony_ci         * also increase the voltage of LFPS.
10513d0407baSopenharmony_ci         */
10523d0407baSopenharmony_ci        writel(0x08, u3phy_port->base + 0x180);
10533d0407baSopenharmony_ci    } else {
10543d0407baSopenharmony_ci        dev_err(u3phy->dev, "invalid u3phy port type\n");
10553d0407baSopenharmony_ci        return -EINVAL;
10563d0407baSopenharmony_ci    }
10573d0407baSopenharmony_ci
10583d0407baSopenharmony_ci    return 0;
10593d0407baSopenharmony_ci}
10603d0407baSopenharmony_ci
10613d0407baSopenharmony_cistatic const struct rockchip_u3phy_cfg rk3328_u3phy_cfgs[] = {{
10623d0407baSopenharmony_ci    .reg = 0xff470000,
10633d0407baSopenharmony_ci    .grfcfg =
10643d0407baSopenharmony_ci        {
10653d0407baSopenharmony_ci            .um_suspend = {0x0004, 15, 0, 0x1452, 0x15d1},
10663d0407baSopenharmony_ci            .u2_only_ctrl = {0x0020, 15, 15, 0, 1},
10673d0407baSopenharmony_ci            .um_ls = {0x0030, 5, 4, 0, 1},
10683d0407baSopenharmony_ci            .um_hstdct = {0x0030, 7, 7, 0, 1},
10693d0407baSopenharmony_ci            .ls_det_en = {0x0040, 0, 0, 0, 1},
10703d0407baSopenharmony_ci            .ls_det_st = {0x0044, 0, 0, 0, 1},
10713d0407baSopenharmony_ci            .pp_pwr_st = {0x0034, 14, 13, 0, 0},
10723d0407baSopenharmony_ci            .pp_pwr_en = {
10733d0407baSopenharmony_ci                {0x0020, 14, 0, 0x0014, 0x0005},
10743d0407baSopenharmony_ci                {0x0020, 14, 0, 0x0014, 0x000d},
10753d0407baSopenharmony_ci                {0x0020, 14, 0, 0x0014, 0x0015},
10763d0407baSopenharmony_ci                {0x0020, 14, 0, 0x0014, 0x001d}},
10773d0407baSopenharmony_ci            .u3_disable = {0x04c4, 15, 0, 0x1100, 0x101},
10783d0407baSopenharmony_ci        },
10793d0407baSopenharmony_ci    .phy_pipe_power = rk3328_u3phy_pipe_power,
10803d0407baSopenharmony_ci    .phy_tuning = rk3328_u3phy_tuning,
10813d0407baSopenharmony_ci}, {
10823d0407baSopenharmony_ci}};
10833d0407baSopenharmony_ci
10843d0407baSopenharmony_cistatic const struct of_device_id rockchip_u3phy_dt_match[] = {
10853d0407baSopenharmony_ci    {.compatible = "rockchip,rk3328-u3phy", .data = &rk3328_u3phy_cfgs}, {}};
10863d0407baSopenharmony_ciMODULE_DEVICE_TABLE(of, rockchip_u3phy_dt_match);
10873d0407baSopenharmony_ci
10883d0407baSopenharmony_cistatic struct platform_driver rockchip_u3phy_driver = {
10893d0407baSopenharmony_ci    .probe = rockchip_u3phy_probe,
10903d0407baSopenharmony_ci    .driver =
10913d0407baSopenharmony_ci        {
10923d0407baSopenharmony_ci            .name = "rockchip-u3phy",
10933d0407baSopenharmony_ci            .of_match_table = rockchip_u3phy_dt_match,
10943d0407baSopenharmony_ci        },
10953d0407baSopenharmony_ci};
10963d0407baSopenharmony_cimodule_platform_driver(rockchip_u3phy_driver);
10973d0407baSopenharmony_ci
10983d0407baSopenharmony_ciMODULE_AUTHOR("Frank Wang <frank.wang@rock-chips.com>");
10993d0407baSopenharmony_ciMODULE_AUTHOR("William Wu <william.wu@rock-chips.com>");
11003d0407baSopenharmony_ciMODULE_DESCRIPTION("Rockchip USB 3.0 PHY driver");
11013d0407baSopenharmony_ciMODULE_LICENSE("GPL v2");
1102