1fab33579SXu Ziyuan /* 2fab33579SXu Ziyuan * Copyright 2016 Rockchip Electronics Co., Ltd 3fab33579SXu Ziyuan * 4fab33579SXu Ziyuan * SPDX-License-Identifier: GPL-2.0+ 5fab33579SXu Ziyuan */ 6fab33579SXu Ziyuan 7fab33579SXu Ziyuan #include <common.h> 8471ba803SFrank Wang #include <asm/arch/clock.h> 9fab33579SXu Ziyuan #include <asm/io.h> 10da1b89c5SWilliam Wu #include <dm.h> 11471ba803SFrank Wang #include <fdtdec.h> 12*6a3f8006SWilliam Wu #include <fdt_support.h> 13471ba803SFrank Wang #include <syscon.h> 14fab33579SXu Ziyuan 15fab33579SXu Ziyuan #include "../gadget/dwc2_udc_otg_priv.h" 16fab33579SXu Ziyuan 17fab33579SXu Ziyuan DECLARE_GLOBAL_DATA_PTR; 18fab33579SXu Ziyuan 19fab33579SXu Ziyuan #define BIT_WRITEABLE_SHIFT 16 20fab33579SXu Ziyuan 21fab33579SXu Ziyuan struct usb2phy_reg { 22fab33579SXu Ziyuan unsigned int offset; 23fab33579SXu Ziyuan unsigned int bitend; 24fab33579SXu Ziyuan unsigned int bitstart; 25fab33579SXu Ziyuan unsigned int disable; 26fab33579SXu Ziyuan unsigned int enable; 27fab33579SXu Ziyuan }; 28fab33579SXu Ziyuan 29fab33579SXu Ziyuan /** 30fab33579SXu Ziyuan * struct rockchip_usb2_phy_cfg: usb-phy port configuration 31fab33579SXu Ziyuan * @port_reset: usb otg per-port reset register 32fab33579SXu Ziyuan * @soft_con: software control usb otg register 33fab33579SXu Ziyuan * @suspend: phy suspend register 34fab33579SXu Ziyuan */ 35fab33579SXu Ziyuan struct rockchip_usb2_phy_cfg { 36fab33579SXu Ziyuan struct usb2phy_reg port_reset; 37fee1ae34SNickey Yang struct usb2phy_reg siddq; 38fab33579SXu Ziyuan struct usb2phy_reg soft_con; 39fab33579SXu Ziyuan struct usb2phy_reg suspend; 40fab33579SXu Ziyuan }; 41fab33579SXu Ziyuan 42fab33579SXu Ziyuan struct rockchip_usb2_phy_dt_id { 43fab33579SXu Ziyuan char compatible[128]; 44fab33579SXu Ziyuan const void *data; 45fab33579SXu Ziyuan }; 46fab33579SXu Ziyuan 47fab33579SXu Ziyuan static const struct rockchip_usb2_phy_cfg rk3288_pdata = { 48fab33579SXu Ziyuan .port_reset = {0x00, 12, 12, 0, 1}, 49fee1ae34SNickey Yang .siddq = {0x00, 13, 13, 0, 1}, 50fab33579SXu Ziyuan .soft_con = {0x08, 2, 2, 0, 1}, 51fab33579SXu Ziyuan .suspend = {0x0c, 5, 0, 0x01, 0x2A}, 52fab33579SXu Ziyuan }; 53fab33579SXu Ziyuan 54fab33579SXu Ziyuan static struct rockchip_usb2_phy_dt_id rockchip_usb2_phy_dt_ids[] = { 55fab33579SXu Ziyuan { .compatible = "rockchip,rk3288-usb-phy", .data = &rk3288_pdata }, 56fab33579SXu Ziyuan {} 57fab33579SXu Ziyuan }; 58fab33579SXu Ziyuan 59fab33579SXu Ziyuan static void property_enable(struct dwc2_plat_otg_data *pdata, 60fab33579SXu Ziyuan const struct usb2phy_reg *reg, bool en) 61fab33579SXu Ziyuan { 62fab33579SXu Ziyuan unsigned int val, mask, tmp; 63fab33579SXu Ziyuan 64fab33579SXu Ziyuan tmp = en ? reg->enable : reg->disable; 65fab33579SXu Ziyuan mask = GENMASK(reg->bitend, reg->bitstart); 66fab33579SXu Ziyuan val = (tmp << reg->bitstart) | (mask << BIT_WRITEABLE_SHIFT); 67fab33579SXu Ziyuan 68fab33579SXu Ziyuan writel(val, pdata->regs_phy + reg->offset); 69fab33579SXu Ziyuan } 70fab33579SXu Ziyuan 713e4afe6bSWilliam Wu int rockchip_u2phy_vbus_detect(void) 723e4afe6bSWilliam Wu { 733e4afe6bSWilliam Wu u32 val = 0; 743e4afe6bSWilliam Wu 753e4afe6bSWilliam Wu #ifdef CONFIG_ROCKCHIP_RK3288 763e4afe6bSWilliam Wu u32 grf_base = (u32)syscon_get_first_range(ROCKCHIP_SYSCON_GRF); 773e4afe6bSWilliam Wu 783e4afe6bSWilliam Wu val = readl(grf_base + 0x288); 793e4afe6bSWilliam Wu val = (val & BIT(14)) >> 14; 803e4afe6bSWilliam Wu #endif 813e4afe6bSWilliam Wu 823e4afe6bSWilliam Wu return val; 833e4afe6bSWilliam Wu } 843e4afe6bSWilliam Wu 85471ba803SFrank Wang static int otg_phy_parse(struct dwc2_udc *dev) 86471ba803SFrank Wang { 87471ba803SFrank Wang int node, phy_node; 88471ba803SFrank Wang u32 grf_base, grf_offset; 89471ba803SFrank Wang const void *blob = gd->fdt_blob; 90da1b89c5SWilliam Wu const fdt32_t *reg; 91da1b89c5SWilliam Wu fdt_addr_t addr; 92471ba803SFrank Wang struct dwc2_plat_otg_data *pdata = dev->pdata; 93471ba803SFrank Wang 94471ba803SFrank Wang /* Find the usb_otg node */ 95471ba803SFrank Wang node = fdt_node_offset_by_compatible(blob, -1, "snps,dwc2"); 96da1b89c5SWilliam Wu 97*6a3f8006SWilliam Wu #if defined(CONFIG_ROCKCHIP_RK3288) 98da1b89c5SWilliam Wu retry: 99*6a3f8006SWilliam Wu #endif 100da1b89c5SWilliam Wu if (node > 0) { 101da1b89c5SWilliam Wu reg = fdt_getprop(blob, node, "reg", NULL); 102da1b89c5SWilliam Wu if (!reg) 103da1b89c5SWilliam Wu return -EINVAL; 104da1b89c5SWilliam Wu 105da1b89c5SWilliam Wu addr = fdt_translate_address(blob, node, reg); 106da1b89c5SWilliam Wu if (addr == OF_BAD_ADDR) { 107da1b89c5SWilliam Wu pr_err("Not found usb_otg address\n"); 108da1b89c5SWilliam Wu return -EINVAL; 109471ba803SFrank Wang } 110471ba803SFrank Wang 111da1b89c5SWilliam Wu #if defined(CONFIG_ROCKCHIP_RK3288) 112da1b89c5SWilliam Wu if (addr != 0xff580000) { 113da1b89c5SWilliam Wu node = fdt_node_offset_by_compatible(blob, node, 114da1b89c5SWilliam Wu "snps,dwc2"); 115da1b89c5SWilliam Wu goto retry; 116471ba803SFrank Wang } 117da1b89c5SWilliam Wu #endif 118da1b89c5SWilliam Wu } else { 119e7b5bb3cSWilliam Wu /* 120e7b5bb3cSWilliam Wu * With kernel dtb support, rk3288 dwc2 otg node 121e7b5bb3cSWilliam Wu * use the rockchip legacy dwc2 driver "dwc_otg_310" 122e7b5bb3cSWilliam Wu * with the compatible "rockchip,rk3288_usb20_otg". 123e7b5bb3cSWilliam Wu */ 124da1b89c5SWilliam Wu #if defined(CONFIG_ROCKCHIP_RK3288) 125e7b5bb3cSWilliam Wu node = fdt_node_offset_by_compatible(blob, -1, 126e7b5bb3cSWilliam Wu "rockchip,rk3288_usb20_otg"); 127da1b89c5SWilliam Wu #endif 128da1b89c5SWilliam Wu if (node < 0) { 129471ba803SFrank Wang pr_err("Not found usb_otg device\n"); 130471ba803SFrank Wang return -ENODEV; 131471ba803SFrank Wang } 132e7b5bb3cSWilliam Wu } 133471ba803SFrank Wang 134471ba803SFrank Wang /* Find the usb phy node */ 135471ba803SFrank Wang node = fdtdec_lookup_phandle(blob, node, "phys"); 136471ba803SFrank Wang if (node <= 0) { 137471ba803SFrank Wang pr_err("Not found usbphy device\n"); 138471ba803SFrank Wang return -ENODEV; 139471ba803SFrank Wang } 140471ba803SFrank Wang 141471ba803SFrank Wang /* Find the usb otg-phy node */ 142471ba803SFrank Wang phy_node = fdt_parent_offset(blob, node); 143471ba803SFrank Wang if (phy_node <= 0) { 144471ba803SFrank Wang pr_err("Not found sub usbphy device\n"); 145471ba803SFrank Wang return -ENODEV; 146471ba803SFrank Wang } 147471ba803SFrank Wang 148471ba803SFrank Wang grf_base = (u32)syscon_get_first_range(ROCKCHIP_SYSCON_GRF); 149471ba803SFrank Wang grf_offset = fdtdec_get_addr(blob, node, "reg"); 150471ba803SFrank Wang 151471ba803SFrank Wang /* Pad dwc2_plat_otg_data related to phy */ 152471ba803SFrank Wang pdata->phy_of_node = phy_node; 153471ba803SFrank Wang pdata->regs_phy = grf_base + grf_offset; 154471ba803SFrank Wang 155471ba803SFrank Wang return 0; 156471ba803SFrank Wang } 157fab33579SXu Ziyuan 158fab33579SXu Ziyuan void otg_phy_init(struct dwc2_udc *dev) 159fab33579SXu Ziyuan { 160fab33579SXu Ziyuan struct dwc2_plat_otg_data *pdata = dev->pdata; 161fab33579SXu Ziyuan struct rockchip_usb2_phy_cfg *phy_cfg = NULL; 162fab33579SXu Ziyuan struct rockchip_usb2_phy_dt_id *of_id; 163fab33579SXu Ziyuan int i; 164fab33579SXu Ziyuan 165471ba803SFrank Wang if (!pdata->regs_phy && otg_phy_parse(dev)) { 166471ba803SFrank Wang pr_err("otg-phy parse error\n"); 167471ba803SFrank Wang return; 168471ba803SFrank Wang } 169471ba803SFrank Wang 170fab33579SXu Ziyuan for (i = 0; i < ARRAY_SIZE(rockchip_usb2_phy_dt_ids); i++) { 171fab33579SXu Ziyuan of_id = &rockchip_usb2_phy_dt_ids[i]; 172fab33579SXu Ziyuan if (fdt_node_check_compatible(gd->fdt_blob, pdata->phy_of_node, 173fab33579SXu Ziyuan of_id->compatible) == 0) { 174fab33579SXu Ziyuan phy_cfg = (struct rockchip_usb2_phy_cfg *)of_id->data; 175fab33579SXu Ziyuan break; 176fab33579SXu Ziyuan } 177fab33579SXu Ziyuan } 178fab33579SXu Ziyuan if (!phy_cfg) { 179fab33579SXu Ziyuan debug("Can't find device platform data\n"); 180fab33579SXu Ziyuan 181fab33579SXu Ziyuan hang(); 182fab33579SXu Ziyuan return; 183fab33579SXu Ziyuan } 184fab33579SXu Ziyuan pdata->priv = phy_cfg; 185fee1ae34SNickey Yang 186fee1ae34SNickey Yang /* power up usb phy analog blocks by set siddq 0 */ 187fee1ae34SNickey Yang property_enable(pdata, &phy_cfg->siddq, false); 188fee1ae34SNickey Yang 189fab33579SXu Ziyuan /* disable software control */ 190fab33579SXu Ziyuan property_enable(pdata, &phy_cfg->soft_con, false); 191fab33579SXu Ziyuan 192fab33579SXu Ziyuan /* reset otg port */ 193fab33579SXu Ziyuan property_enable(pdata, &phy_cfg->port_reset, true); 194fab33579SXu Ziyuan mdelay(1); 195fab33579SXu Ziyuan property_enable(pdata, &phy_cfg->port_reset, false); 196fab33579SXu Ziyuan udelay(1); 197fab33579SXu Ziyuan } 198fab33579SXu Ziyuan 199fab33579SXu Ziyuan void otg_phy_off(struct dwc2_udc *dev) 200fab33579SXu Ziyuan { 201fab33579SXu Ziyuan struct dwc2_plat_otg_data *pdata = dev->pdata; 202fab33579SXu Ziyuan struct rockchip_usb2_phy_cfg *phy_cfg = pdata->priv; 203fab33579SXu Ziyuan 204471ba803SFrank Wang if (!pdata->regs_phy && otg_phy_parse(dev)) { 205471ba803SFrank Wang pr_err("otg-phy parse error\n"); 206471ba803SFrank Wang return; 207471ba803SFrank Wang } 208471ba803SFrank Wang 209fab33579SXu Ziyuan /* enable software control */ 210fab33579SXu Ziyuan property_enable(pdata, &phy_cfg->soft_con, true); 211fab33579SXu Ziyuan /* enter suspend */ 212fab33579SXu Ziyuan property_enable(pdata, &phy_cfg->suspend, true); 213fab33579SXu Ziyuan } 214