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