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> 10471ba803SFrank Wang #include <fdtdec.h> 11fab33579SXu Ziyuan #include <libfdt.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; 36fab33579SXu Ziyuan struct usb2phy_reg soft_con; 37fab33579SXu Ziyuan struct usb2phy_reg suspend; 38fab33579SXu Ziyuan }; 39fab33579SXu Ziyuan 40fab33579SXu Ziyuan struct rockchip_usb2_phy_dt_id { 41fab33579SXu Ziyuan char compatible[128]; 42fab33579SXu Ziyuan const void *data; 43fab33579SXu Ziyuan }; 44fab33579SXu Ziyuan 45fab33579SXu Ziyuan static const struct rockchip_usb2_phy_cfg rk3288_pdata = { 46fab33579SXu Ziyuan .port_reset = {0x00, 12, 12, 0, 1}, 47fab33579SXu Ziyuan .soft_con = {0x08, 2, 2, 0, 1}, 48fab33579SXu Ziyuan .suspend = {0x0c, 5, 0, 0x01, 0x2A}, 49fab33579SXu Ziyuan }; 50fab33579SXu Ziyuan 51fab33579SXu Ziyuan static struct rockchip_usb2_phy_dt_id rockchip_usb2_phy_dt_ids[] = { 52fab33579SXu Ziyuan { .compatible = "rockchip,rk3288-usb-phy", .data = &rk3288_pdata }, 53fab33579SXu Ziyuan {} 54fab33579SXu Ziyuan }; 55fab33579SXu Ziyuan 56fab33579SXu Ziyuan static void property_enable(struct dwc2_plat_otg_data *pdata, 57fab33579SXu Ziyuan const struct usb2phy_reg *reg, bool en) 58fab33579SXu Ziyuan { 59fab33579SXu Ziyuan unsigned int val, mask, tmp; 60fab33579SXu Ziyuan 61fab33579SXu Ziyuan tmp = en ? reg->enable : reg->disable; 62fab33579SXu Ziyuan mask = GENMASK(reg->bitend, reg->bitstart); 63fab33579SXu Ziyuan val = (tmp << reg->bitstart) | (mask << BIT_WRITEABLE_SHIFT); 64fab33579SXu Ziyuan 65fab33579SXu Ziyuan writel(val, pdata->regs_phy + reg->offset); 66fab33579SXu Ziyuan } 67fab33579SXu Ziyuan 68471ba803SFrank Wang static int otg_phy_parse(struct dwc2_udc *dev) 69471ba803SFrank Wang { 70471ba803SFrank Wang int node, phy_node; 71471ba803SFrank Wang u32 grf_base, grf_offset; 72471ba803SFrank Wang const char *mode; 73471ba803SFrank Wang bool matched = false; 74471ba803SFrank Wang const void *blob = gd->fdt_blob; 75471ba803SFrank Wang struct dwc2_plat_otg_data *pdata = dev->pdata; 76471ba803SFrank Wang 77471ba803SFrank Wang /* Find the usb_otg node */ 78471ba803SFrank Wang node = fdt_node_offset_by_compatible(blob, -1, "snps,dwc2"); 79471ba803SFrank Wang while (node > 0) { 80471ba803SFrank Wang mode = fdt_getprop(blob, node, "dr_mode", NULL); 81471ba803SFrank Wang if (mode && strcmp(mode, "otg") == 0) { 82471ba803SFrank Wang matched = true; 83471ba803SFrank Wang break; 84471ba803SFrank Wang } 85471ba803SFrank Wang 86471ba803SFrank Wang node = fdt_node_offset_by_compatible(blob, node, "snps,dwc2"); 87471ba803SFrank Wang } 88471ba803SFrank Wang 89471ba803SFrank Wang if (!matched) { 90*e7b5bb3cSWilliam Wu /* 91*e7b5bb3cSWilliam Wu * With kernel dtb support, rk3288 dwc2 otg node 92*e7b5bb3cSWilliam Wu * use the rockchip legacy dwc2 driver "dwc_otg_310" 93*e7b5bb3cSWilliam Wu * with the compatible "rockchip,rk3288_usb20_otg". 94*e7b5bb3cSWilliam Wu */ 95*e7b5bb3cSWilliam Wu node = fdt_node_offset_by_compatible(blob, -1, 96*e7b5bb3cSWilliam Wu "rockchip,rk3288_usb20_otg"); 97*e7b5bb3cSWilliam Wu if (node > 0) { 98*e7b5bb3cSWilliam Wu matched = true; 99*e7b5bb3cSWilliam Wu } else { 100471ba803SFrank Wang pr_err("Not found usb_otg device\n"); 101471ba803SFrank Wang return -ENODEV; 102471ba803SFrank Wang } 103*e7b5bb3cSWilliam Wu } 104471ba803SFrank Wang 105471ba803SFrank Wang /* Find the usb phy node */ 106471ba803SFrank Wang node = fdtdec_lookup_phandle(blob, node, "phys"); 107471ba803SFrank Wang if (node <= 0) { 108471ba803SFrank Wang pr_err("Not found usbphy device\n"); 109471ba803SFrank Wang return -ENODEV; 110471ba803SFrank Wang } 111471ba803SFrank Wang 112471ba803SFrank Wang /* Find the usb otg-phy node */ 113471ba803SFrank Wang phy_node = fdt_parent_offset(blob, node); 114471ba803SFrank Wang if (phy_node <= 0) { 115471ba803SFrank Wang pr_err("Not found sub usbphy device\n"); 116471ba803SFrank Wang return -ENODEV; 117471ba803SFrank Wang } 118471ba803SFrank Wang 119471ba803SFrank Wang grf_base = (u32)syscon_get_first_range(ROCKCHIP_SYSCON_GRF); 120471ba803SFrank Wang grf_offset = fdtdec_get_addr(blob, node, "reg"); 121471ba803SFrank Wang 122471ba803SFrank Wang /* Pad dwc2_plat_otg_data related to phy */ 123471ba803SFrank Wang pdata->phy_of_node = phy_node; 124471ba803SFrank Wang pdata->regs_phy = grf_base + grf_offset; 125471ba803SFrank Wang 126471ba803SFrank Wang return 0; 127471ba803SFrank Wang } 128fab33579SXu Ziyuan 129fab33579SXu Ziyuan void otg_phy_init(struct dwc2_udc *dev) 130fab33579SXu Ziyuan { 131fab33579SXu Ziyuan struct dwc2_plat_otg_data *pdata = dev->pdata; 132fab33579SXu Ziyuan struct rockchip_usb2_phy_cfg *phy_cfg = NULL; 133fab33579SXu Ziyuan struct rockchip_usb2_phy_dt_id *of_id; 134fab33579SXu Ziyuan int i; 135fab33579SXu Ziyuan 136471ba803SFrank Wang if (!pdata->regs_phy && otg_phy_parse(dev)) { 137471ba803SFrank Wang pr_err("otg-phy parse error\n"); 138471ba803SFrank Wang return; 139471ba803SFrank Wang } 140471ba803SFrank Wang 141fab33579SXu Ziyuan for (i = 0; i < ARRAY_SIZE(rockchip_usb2_phy_dt_ids); i++) { 142fab33579SXu Ziyuan of_id = &rockchip_usb2_phy_dt_ids[i]; 143fab33579SXu Ziyuan if (fdt_node_check_compatible(gd->fdt_blob, pdata->phy_of_node, 144fab33579SXu Ziyuan of_id->compatible) == 0) { 145fab33579SXu Ziyuan phy_cfg = (struct rockchip_usb2_phy_cfg *)of_id->data; 146fab33579SXu Ziyuan break; 147fab33579SXu Ziyuan } 148fab33579SXu Ziyuan } 149fab33579SXu Ziyuan if (!phy_cfg) { 150fab33579SXu Ziyuan debug("Can't find device platform data\n"); 151fab33579SXu Ziyuan 152fab33579SXu Ziyuan hang(); 153fab33579SXu Ziyuan return; 154fab33579SXu Ziyuan } 155fab33579SXu Ziyuan pdata->priv = phy_cfg; 156fab33579SXu Ziyuan /* disable software control */ 157fab33579SXu Ziyuan property_enable(pdata, &phy_cfg->soft_con, false); 158fab33579SXu Ziyuan 159fab33579SXu Ziyuan /* reset otg port */ 160fab33579SXu Ziyuan property_enable(pdata, &phy_cfg->port_reset, true); 161fab33579SXu Ziyuan mdelay(1); 162fab33579SXu Ziyuan property_enable(pdata, &phy_cfg->port_reset, false); 163fab33579SXu Ziyuan udelay(1); 164fab33579SXu Ziyuan } 165fab33579SXu Ziyuan 166fab33579SXu Ziyuan void otg_phy_off(struct dwc2_udc *dev) 167fab33579SXu Ziyuan { 168fab33579SXu Ziyuan struct dwc2_plat_otg_data *pdata = dev->pdata; 169fab33579SXu Ziyuan struct rockchip_usb2_phy_cfg *phy_cfg = pdata->priv; 170fab33579SXu Ziyuan 171471ba803SFrank Wang if (!pdata->regs_phy && otg_phy_parse(dev)) { 172471ba803SFrank Wang pr_err("otg-phy parse error\n"); 173471ba803SFrank Wang return; 174471ba803SFrank Wang } 175471ba803SFrank Wang 176fab33579SXu Ziyuan /* enable software control */ 177fab33579SXu Ziyuan property_enable(pdata, &phy_cfg->soft_con, true); 178fab33579SXu Ziyuan /* enter suspend */ 179fab33579SXu Ziyuan property_enable(pdata, &phy_cfg->suspend, true); 180fab33579SXu Ziyuan } 181