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