18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-1.0+ 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Renesas USB driver 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2011 Renesas Solutions Corp. 68c2ecf20Sopenharmony_ci * Copyright (C) 2019 Renesas Electronics Corporation 78c2ecf20Sopenharmony_ci * Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> 88c2ecf20Sopenharmony_ci */ 98c2ecf20Sopenharmony_ci#include <linux/clk.h> 108c2ecf20Sopenharmony_ci#include <linux/err.h> 118c2ecf20Sopenharmony_ci#include <linux/gpio/consumer.h> 128c2ecf20Sopenharmony_ci#include <linux/io.h> 138c2ecf20Sopenharmony_ci#include <linux/module.h> 148c2ecf20Sopenharmony_ci#include <linux/of_device.h> 158c2ecf20Sopenharmony_ci#include <linux/pm_runtime.h> 168c2ecf20Sopenharmony_ci#include <linux/reset.h> 178c2ecf20Sopenharmony_ci#include <linux/slab.h> 188c2ecf20Sopenharmony_ci#include <linux/sysfs.h> 198c2ecf20Sopenharmony_ci#include "common.h" 208c2ecf20Sopenharmony_ci#include "rcar2.h" 218c2ecf20Sopenharmony_ci#include "rcar3.h" 228c2ecf20Sopenharmony_ci#include "rza.h" 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_ci/* 258c2ecf20Sopenharmony_ci * image of renesas_usbhs 268c2ecf20Sopenharmony_ci * 278c2ecf20Sopenharmony_ci * ex) gadget case 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci * mod.c 308c2ecf20Sopenharmony_ci * mod_gadget.c 318c2ecf20Sopenharmony_ci * mod_host.c pipe.c fifo.c 328c2ecf20Sopenharmony_ci * 338c2ecf20Sopenharmony_ci * +-------+ +-----------+ 348c2ecf20Sopenharmony_ci * | pipe0 |------>| fifo pio | 358c2ecf20Sopenharmony_ci * +------------+ +-------+ +-----------+ 368c2ecf20Sopenharmony_ci * | mod_gadget |=====> | pipe1 |--+ 378c2ecf20Sopenharmony_ci * +------------+ +-------+ | +-----------+ 388c2ecf20Sopenharmony_ci * | pipe2 | | +-| fifo dma0 | 398c2ecf20Sopenharmony_ci * +------------+ +-------+ | | +-----------+ 408c2ecf20Sopenharmony_ci * | mod_host | | pipe3 |<-|--+ 418c2ecf20Sopenharmony_ci * +------------+ +-------+ | +-----------+ 428c2ecf20Sopenharmony_ci * | .... | +--->| fifo dma1 | 438c2ecf20Sopenharmony_ci * | .... | +-----------+ 448c2ecf20Sopenharmony_ci */ 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci/* 478c2ecf20Sopenharmony_ci * platform call back 488c2ecf20Sopenharmony_ci * 498c2ecf20Sopenharmony_ci * renesas usb support platform callback function. 508c2ecf20Sopenharmony_ci * Below macro call it. 518c2ecf20Sopenharmony_ci * if platform doesn't have callback, it return 0 (no error) 528c2ecf20Sopenharmony_ci */ 538c2ecf20Sopenharmony_ci#define usbhs_platform_call(priv, func, args...)\ 548c2ecf20Sopenharmony_ci (!(priv) ? -ENODEV : \ 558c2ecf20Sopenharmony_ci !((priv)->pfunc->func) ? 0 : \ 568c2ecf20Sopenharmony_ci (priv)->pfunc->func(args)) 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci/* 598c2ecf20Sopenharmony_ci * common functions 608c2ecf20Sopenharmony_ci */ 618c2ecf20Sopenharmony_ciu16 usbhs_read(struct usbhs_priv *priv, u32 reg) 628c2ecf20Sopenharmony_ci{ 638c2ecf20Sopenharmony_ci return ioread16(priv->base + reg); 648c2ecf20Sopenharmony_ci} 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_civoid usbhs_write(struct usbhs_priv *priv, u32 reg, u16 data) 678c2ecf20Sopenharmony_ci{ 688c2ecf20Sopenharmony_ci iowrite16(data, priv->base + reg); 698c2ecf20Sopenharmony_ci} 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_civoid usbhs_bset(struct usbhs_priv *priv, u32 reg, u16 mask, u16 data) 728c2ecf20Sopenharmony_ci{ 738c2ecf20Sopenharmony_ci u16 val = usbhs_read(priv, reg); 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci val &= ~mask; 768c2ecf20Sopenharmony_ci val |= data & mask; 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci usbhs_write(priv, reg, val); 798c2ecf20Sopenharmony_ci} 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_cistruct usbhs_priv *usbhs_pdev_to_priv(struct platform_device *pdev) 828c2ecf20Sopenharmony_ci{ 838c2ecf20Sopenharmony_ci return dev_get_drvdata(&pdev->dev); 848c2ecf20Sopenharmony_ci} 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ciint usbhs_get_id_as_gadget(struct platform_device *pdev) 878c2ecf20Sopenharmony_ci{ 888c2ecf20Sopenharmony_ci return USBHS_GADGET; 898c2ecf20Sopenharmony_ci} 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci/* 928c2ecf20Sopenharmony_ci * syscfg functions 938c2ecf20Sopenharmony_ci */ 948c2ecf20Sopenharmony_cistatic void usbhs_sys_clock_ctrl(struct usbhs_priv *priv, int enable) 958c2ecf20Sopenharmony_ci{ 968c2ecf20Sopenharmony_ci usbhs_bset(priv, SYSCFG, SCKE, enable ? SCKE : 0); 978c2ecf20Sopenharmony_ci} 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_civoid usbhs_sys_host_ctrl(struct usbhs_priv *priv, int enable) 1008c2ecf20Sopenharmony_ci{ 1018c2ecf20Sopenharmony_ci u16 mask = DCFM | DRPD | DPRPU | HSE | USBE; 1028c2ecf20Sopenharmony_ci u16 val = DCFM | DRPD | HSE | USBE; 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci /* 1058c2ecf20Sopenharmony_ci * if enable 1068c2ecf20Sopenharmony_ci * 1078c2ecf20Sopenharmony_ci * - select Host mode 1088c2ecf20Sopenharmony_ci * - D+ Line/D- Line Pull-down 1098c2ecf20Sopenharmony_ci */ 1108c2ecf20Sopenharmony_ci usbhs_bset(priv, SYSCFG, mask, enable ? val : 0); 1118c2ecf20Sopenharmony_ci} 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_civoid usbhs_sys_function_ctrl(struct usbhs_priv *priv, int enable) 1148c2ecf20Sopenharmony_ci{ 1158c2ecf20Sopenharmony_ci u16 mask = DCFM | DRPD | DPRPU | HSE | USBE; 1168c2ecf20Sopenharmony_ci u16 val = HSE | USBE; 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci /* CNEN bit is required for function operation */ 1198c2ecf20Sopenharmony_ci if (usbhs_get_dparam(priv, has_cnen)) { 1208c2ecf20Sopenharmony_ci mask |= CNEN; 1218c2ecf20Sopenharmony_ci val |= CNEN; 1228c2ecf20Sopenharmony_ci } 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci /* 1258c2ecf20Sopenharmony_ci * if enable 1268c2ecf20Sopenharmony_ci * 1278c2ecf20Sopenharmony_ci * - select Function mode 1288c2ecf20Sopenharmony_ci * - D+ Line Pull-up is disabled 1298c2ecf20Sopenharmony_ci * When D+ Line Pull-up is enabled, 1308c2ecf20Sopenharmony_ci * calling usbhs_sys_function_pullup(,1) 1318c2ecf20Sopenharmony_ci */ 1328c2ecf20Sopenharmony_ci usbhs_bset(priv, SYSCFG, mask, enable ? val : 0); 1338c2ecf20Sopenharmony_ci} 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_civoid usbhs_sys_function_pullup(struct usbhs_priv *priv, int enable) 1368c2ecf20Sopenharmony_ci{ 1378c2ecf20Sopenharmony_ci usbhs_bset(priv, SYSCFG, DPRPU, enable ? DPRPU : 0); 1388c2ecf20Sopenharmony_ci} 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_civoid usbhs_sys_set_test_mode(struct usbhs_priv *priv, u16 mode) 1418c2ecf20Sopenharmony_ci{ 1428c2ecf20Sopenharmony_ci usbhs_write(priv, TESTMODE, mode); 1438c2ecf20Sopenharmony_ci} 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci/* 1468c2ecf20Sopenharmony_ci * frame functions 1478c2ecf20Sopenharmony_ci */ 1488c2ecf20Sopenharmony_ciint usbhs_frame_get_num(struct usbhs_priv *priv) 1498c2ecf20Sopenharmony_ci{ 1508c2ecf20Sopenharmony_ci return usbhs_read(priv, FRMNUM) & FRNM_MASK; 1518c2ecf20Sopenharmony_ci} 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci/* 1548c2ecf20Sopenharmony_ci * usb request functions 1558c2ecf20Sopenharmony_ci */ 1568c2ecf20Sopenharmony_civoid usbhs_usbreq_get_val(struct usbhs_priv *priv, struct usb_ctrlrequest *req) 1578c2ecf20Sopenharmony_ci{ 1588c2ecf20Sopenharmony_ci u16 val; 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci val = usbhs_read(priv, USBREQ); 1618c2ecf20Sopenharmony_ci req->bRequest = (val >> 8) & 0xFF; 1628c2ecf20Sopenharmony_ci req->bRequestType = (val >> 0) & 0xFF; 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci req->wValue = cpu_to_le16(usbhs_read(priv, USBVAL)); 1658c2ecf20Sopenharmony_ci req->wIndex = cpu_to_le16(usbhs_read(priv, USBINDX)); 1668c2ecf20Sopenharmony_ci req->wLength = cpu_to_le16(usbhs_read(priv, USBLENG)); 1678c2ecf20Sopenharmony_ci} 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_civoid usbhs_usbreq_set_val(struct usbhs_priv *priv, struct usb_ctrlrequest *req) 1708c2ecf20Sopenharmony_ci{ 1718c2ecf20Sopenharmony_ci usbhs_write(priv, USBREQ, (req->bRequest << 8) | req->bRequestType); 1728c2ecf20Sopenharmony_ci usbhs_write(priv, USBVAL, le16_to_cpu(req->wValue)); 1738c2ecf20Sopenharmony_ci usbhs_write(priv, USBINDX, le16_to_cpu(req->wIndex)); 1748c2ecf20Sopenharmony_ci usbhs_write(priv, USBLENG, le16_to_cpu(req->wLength)); 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_ci usbhs_bset(priv, DCPCTR, SUREQ, SUREQ); 1778c2ecf20Sopenharmony_ci} 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci/* 1808c2ecf20Sopenharmony_ci * bus/vbus functions 1818c2ecf20Sopenharmony_ci */ 1828c2ecf20Sopenharmony_civoid usbhs_bus_send_sof_enable(struct usbhs_priv *priv) 1838c2ecf20Sopenharmony_ci{ 1848c2ecf20Sopenharmony_ci u16 status = usbhs_read(priv, DVSTCTR) & (USBRST | UACT); 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci if (status != USBRST) { 1878c2ecf20Sopenharmony_ci struct device *dev = usbhs_priv_to_dev(priv); 1888c2ecf20Sopenharmony_ci dev_err(dev, "usbhs should be reset\n"); 1898c2ecf20Sopenharmony_ci } 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ci usbhs_bset(priv, DVSTCTR, (USBRST | UACT), UACT); 1928c2ecf20Sopenharmony_ci} 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_civoid usbhs_bus_send_reset(struct usbhs_priv *priv) 1958c2ecf20Sopenharmony_ci{ 1968c2ecf20Sopenharmony_ci usbhs_bset(priv, DVSTCTR, (USBRST | UACT), USBRST); 1978c2ecf20Sopenharmony_ci} 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ciint usbhs_bus_get_speed(struct usbhs_priv *priv) 2008c2ecf20Sopenharmony_ci{ 2018c2ecf20Sopenharmony_ci u16 dvstctr = usbhs_read(priv, DVSTCTR); 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ci switch (RHST & dvstctr) { 2048c2ecf20Sopenharmony_ci case RHST_LOW_SPEED: 2058c2ecf20Sopenharmony_ci return USB_SPEED_LOW; 2068c2ecf20Sopenharmony_ci case RHST_FULL_SPEED: 2078c2ecf20Sopenharmony_ci return USB_SPEED_FULL; 2088c2ecf20Sopenharmony_ci case RHST_HIGH_SPEED: 2098c2ecf20Sopenharmony_ci return USB_SPEED_HIGH; 2108c2ecf20Sopenharmony_ci } 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci return USB_SPEED_UNKNOWN; 2138c2ecf20Sopenharmony_ci} 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_ciint usbhs_vbus_ctrl(struct usbhs_priv *priv, int enable) 2168c2ecf20Sopenharmony_ci{ 2178c2ecf20Sopenharmony_ci struct platform_device *pdev = usbhs_priv_to_pdev(priv); 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_ci return usbhs_platform_call(priv, set_vbus, pdev, enable); 2208c2ecf20Sopenharmony_ci} 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_cistatic void usbhsc_bus_init(struct usbhs_priv *priv) 2238c2ecf20Sopenharmony_ci{ 2248c2ecf20Sopenharmony_ci usbhs_write(priv, DVSTCTR, 0); 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_ci usbhs_vbus_ctrl(priv, 0); 2278c2ecf20Sopenharmony_ci} 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_ci/* 2308c2ecf20Sopenharmony_ci * device configuration 2318c2ecf20Sopenharmony_ci */ 2328c2ecf20Sopenharmony_ciint usbhs_set_device_config(struct usbhs_priv *priv, int devnum, 2338c2ecf20Sopenharmony_ci u16 upphub, u16 hubport, u16 speed) 2348c2ecf20Sopenharmony_ci{ 2358c2ecf20Sopenharmony_ci struct device *dev = usbhs_priv_to_dev(priv); 2368c2ecf20Sopenharmony_ci u16 usbspd = 0; 2378c2ecf20Sopenharmony_ci u32 reg = DEVADD0 + (2 * devnum); 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_ci if (devnum > 10) { 2408c2ecf20Sopenharmony_ci dev_err(dev, "cannot set speed to unknown device %d\n", devnum); 2418c2ecf20Sopenharmony_ci return -EIO; 2428c2ecf20Sopenharmony_ci } 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_ci if (upphub > 0xA) { 2458c2ecf20Sopenharmony_ci dev_err(dev, "unsupported hub number %d\n", upphub); 2468c2ecf20Sopenharmony_ci return -EIO; 2478c2ecf20Sopenharmony_ci } 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_ci switch (speed) { 2508c2ecf20Sopenharmony_ci case USB_SPEED_LOW: 2518c2ecf20Sopenharmony_ci usbspd = USBSPD_SPEED_LOW; 2528c2ecf20Sopenharmony_ci break; 2538c2ecf20Sopenharmony_ci case USB_SPEED_FULL: 2548c2ecf20Sopenharmony_ci usbspd = USBSPD_SPEED_FULL; 2558c2ecf20Sopenharmony_ci break; 2568c2ecf20Sopenharmony_ci case USB_SPEED_HIGH: 2578c2ecf20Sopenharmony_ci usbspd = USBSPD_SPEED_HIGH; 2588c2ecf20Sopenharmony_ci break; 2598c2ecf20Sopenharmony_ci default: 2608c2ecf20Sopenharmony_ci dev_err(dev, "unsupported speed %d\n", speed); 2618c2ecf20Sopenharmony_ci return -EIO; 2628c2ecf20Sopenharmony_ci } 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_ci usbhs_write(priv, reg, UPPHUB(upphub) | 2658c2ecf20Sopenharmony_ci HUBPORT(hubport)| 2668c2ecf20Sopenharmony_ci USBSPD(usbspd)); 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_ci return 0; 2698c2ecf20Sopenharmony_ci} 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_ci/* 2728c2ecf20Sopenharmony_ci * interrupt functions 2738c2ecf20Sopenharmony_ci */ 2748c2ecf20Sopenharmony_civoid usbhs_xxxsts_clear(struct usbhs_priv *priv, u16 sts_reg, u16 bit) 2758c2ecf20Sopenharmony_ci{ 2768c2ecf20Sopenharmony_ci u16 pipe_mask = (u16)GENMASK(usbhs_get_dparam(priv, pipe_size), 0); 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_ci usbhs_write(priv, sts_reg, ~(1 << bit) & pipe_mask); 2798c2ecf20Sopenharmony_ci} 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_ci/* 2828c2ecf20Sopenharmony_ci * local functions 2838c2ecf20Sopenharmony_ci */ 2848c2ecf20Sopenharmony_cistatic void usbhsc_set_buswait(struct usbhs_priv *priv) 2858c2ecf20Sopenharmony_ci{ 2868c2ecf20Sopenharmony_ci int wait = usbhs_get_dparam(priv, buswait_bwait); 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_ci /* set bus wait if platform have */ 2898c2ecf20Sopenharmony_ci if (wait) 2908c2ecf20Sopenharmony_ci usbhs_bset(priv, BUSWAIT, 0x000F, wait); 2918c2ecf20Sopenharmony_ci} 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_cistatic bool usbhsc_is_multi_clks(struct usbhs_priv *priv) 2948c2ecf20Sopenharmony_ci{ 2958c2ecf20Sopenharmony_ci return priv->dparam.multi_clks; 2968c2ecf20Sopenharmony_ci} 2978c2ecf20Sopenharmony_ci 2988c2ecf20Sopenharmony_cistatic int usbhsc_clk_get(struct device *dev, struct usbhs_priv *priv) 2998c2ecf20Sopenharmony_ci{ 3008c2ecf20Sopenharmony_ci if (!usbhsc_is_multi_clks(priv)) 3018c2ecf20Sopenharmony_ci return 0; 3028c2ecf20Sopenharmony_ci 3038c2ecf20Sopenharmony_ci /* The first clock should exist */ 3048c2ecf20Sopenharmony_ci priv->clks[0] = of_clk_get(dev_of_node(dev), 0); 3058c2ecf20Sopenharmony_ci if (IS_ERR(priv->clks[0])) 3068c2ecf20Sopenharmony_ci return PTR_ERR(priv->clks[0]); 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_ci /* 3098c2ecf20Sopenharmony_ci * To backward compatibility with old DT, this driver checks the return 3108c2ecf20Sopenharmony_ci * value if it's -ENOENT or not. 3118c2ecf20Sopenharmony_ci */ 3128c2ecf20Sopenharmony_ci priv->clks[1] = of_clk_get(dev_of_node(dev), 1); 3138c2ecf20Sopenharmony_ci if (PTR_ERR(priv->clks[1]) == -ENOENT) 3148c2ecf20Sopenharmony_ci priv->clks[1] = NULL; 3158c2ecf20Sopenharmony_ci else if (IS_ERR(priv->clks[1])) 3168c2ecf20Sopenharmony_ci return PTR_ERR(priv->clks[1]); 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_ci return 0; 3198c2ecf20Sopenharmony_ci} 3208c2ecf20Sopenharmony_ci 3218c2ecf20Sopenharmony_cistatic void usbhsc_clk_put(struct usbhs_priv *priv) 3228c2ecf20Sopenharmony_ci{ 3238c2ecf20Sopenharmony_ci int i; 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_ci if (!usbhsc_is_multi_clks(priv)) 3268c2ecf20Sopenharmony_ci return; 3278c2ecf20Sopenharmony_ci 3288c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(priv->clks); i++) 3298c2ecf20Sopenharmony_ci clk_put(priv->clks[i]); 3308c2ecf20Sopenharmony_ci} 3318c2ecf20Sopenharmony_ci 3328c2ecf20Sopenharmony_cistatic int usbhsc_clk_prepare_enable(struct usbhs_priv *priv) 3338c2ecf20Sopenharmony_ci{ 3348c2ecf20Sopenharmony_ci int i, ret; 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_ci if (!usbhsc_is_multi_clks(priv)) 3378c2ecf20Sopenharmony_ci return 0; 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(priv->clks); i++) { 3408c2ecf20Sopenharmony_ci ret = clk_prepare_enable(priv->clks[i]); 3418c2ecf20Sopenharmony_ci if (ret) { 3428c2ecf20Sopenharmony_ci while (--i >= 0) 3438c2ecf20Sopenharmony_ci clk_disable_unprepare(priv->clks[i]); 3448c2ecf20Sopenharmony_ci return ret; 3458c2ecf20Sopenharmony_ci } 3468c2ecf20Sopenharmony_ci } 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_ci return ret; 3498c2ecf20Sopenharmony_ci} 3508c2ecf20Sopenharmony_ci 3518c2ecf20Sopenharmony_cistatic void usbhsc_clk_disable_unprepare(struct usbhs_priv *priv) 3528c2ecf20Sopenharmony_ci{ 3538c2ecf20Sopenharmony_ci int i; 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_ci if (!usbhsc_is_multi_clks(priv)) 3568c2ecf20Sopenharmony_ci return; 3578c2ecf20Sopenharmony_ci 3588c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(priv->clks); i++) 3598c2ecf20Sopenharmony_ci clk_disable_unprepare(priv->clks[i]); 3608c2ecf20Sopenharmony_ci} 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_ci/* 3638c2ecf20Sopenharmony_ci * platform default param 3648c2ecf20Sopenharmony_ci */ 3658c2ecf20Sopenharmony_ci 3668c2ecf20Sopenharmony_ci/* commonly used on old SH-Mobile SoCs */ 3678c2ecf20Sopenharmony_cistatic struct renesas_usbhs_driver_pipe_config usbhsc_default_pipe[] = { 3688c2ecf20Sopenharmony_ci RENESAS_USBHS_PIPE(USB_ENDPOINT_XFER_CONTROL, 64, 0x00, false), 3698c2ecf20Sopenharmony_ci RENESAS_USBHS_PIPE(USB_ENDPOINT_XFER_ISOC, 1024, 0x08, false), 3708c2ecf20Sopenharmony_ci RENESAS_USBHS_PIPE(USB_ENDPOINT_XFER_ISOC, 1024, 0x18, false), 3718c2ecf20Sopenharmony_ci RENESAS_USBHS_PIPE(USB_ENDPOINT_XFER_BULK, 512, 0x28, true), 3728c2ecf20Sopenharmony_ci RENESAS_USBHS_PIPE(USB_ENDPOINT_XFER_BULK, 512, 0x38, true), 3738c2ecf20Sopenharmony_ci RENESAS_USBHS_PIPE(USB_ENDPOINT_XFER_BULK, 512, 0x48, true), 3748c2ecf20Sopenharmony_ci RENESAS_USBHS_PIPE(USB_ENDPOINT_XFER_INT, 64, 0x04, false), 3758c2ecf20Sopenharmony_ci RENESAS_USBHS_PIPE(USB_ENDPOINT_XFER_INT, 64, 0x05, false), 3768c2ecf20Sopenharmony_ci RENESAS_USBHS_PIPE(USB_ENDPOINT_XFER_INT, 64, 0x06, false), 3778c2ecf20Sopenharmony_ci RENESAS_USBHS_PIPE(USB_ENDPOINT_XFER_INT, 64, 0x07, false), 3788c2ecf20Sopenharmony_ci}; 3798c2ecf20Sopenharmony_ci 3808c2ecf20Sopenharmony_ci/* commonly used on newer SH-Mobile and R-Car SoCs */ 3818c2ecf20Sopenharmony_cistatic struct renesas_usbhs_driver_pipe_config usbhsc_new_pipe[] = { 3828c2ecf20Sopenharmony_ci RENESAS_USBHS_PIPE(USB_ENDPOINT_XFER_CONTROL, 64, 0x00, false), 3838c2ecf20Sopenharmony_ci RENESAS_USBHS_PIPE(USB_ENDPOINT_XFER_ISOC, 1024, 0x08, true), 3848c2ecf20Sopenharmony_ci RENESAS_USBHS_PIPE(USB_ENDPOINT_XFER_ISOC, 1024, 0x28, true), 3858c2ecf20Sopenharmony_ci RENESAS_USBHS_PIPE(USB_ENDPOINT_XFER_BULK, 512, 0x48, true), 3868c2ecf20Sopenharmony_ci RENESAS_USBHS_PIPE(USB_ENDPOINT_XFER_BULK, 512, 0x58, true), 3878c2ecf20Sopenharmony_ci RENESAS_USBHS_PIPE(USB_ENDPOINT_XFER_BULK, 512, 0x68, true), 3888c2ecf20Sopenharmony_ci RENESAS_USBHS_PIPE(USB_ENDPOINT_XFER_INT, 64, 0x04, false), 3898c2ecf20Sopenharmony_ci RENESAS_USBHS_PIPE(USB_ENDPOINT_XFER_INT, 64, 0x05, false), 3908c2ecf20Sopenharmony_ci RENESAS_USBHS_PIPE(USB_ENDPOINT_XFER_INT, 64, 0x06, false), 3918c2ecf20Sopenharmony_ci RENESAS_USBHS_PIPE(USB_ENDPOINT_XFER_BULK, 512, 0x78, true), 3928c2ecf20Sopenharmony_ci RENESAS_USBHS_PIPE(USB_ENDPOINT_XFER_BULK, 512, 0x88, true), 3938c2ecf20Sopenharmony_ci RENESAS_USBHS_PIPE(USB_ENDPOINT_XFER_BULK, 512, 0x98, true), 3948c2ecf20Sopenharmony_ci RENESAS_USBHS_PIPE(USB_ENDPOINT_XFER_BULK, 512, 0xa8, true), 3958c2ecf20Sopenharmony_ci RENESAS_USBHS_PIPE(USB_ENDPOINT_XFER_BULK, 512, 0xb8, true), 3968c2ecf20Sopenharmony_ci RENESAS_USBHS_PIPE(USB_ENDPOINT_XFER_BULK, 512, 0xc8, true), 3978c2ecf20Sopenharmony_ci RENESAS_USBHS_PIPE(USB_ENDPOINT_XFER_BULK, 512, 0xd8, true), 3988c2ecf20Sopenharmony_ci}; 3998c2ecf20Sopenharmony_ci 4008c2ecf20Sopenharmony_ci/* 4018c2ecf20Sopenharmony_ci * power control 4028c2ecf20Sopenharmony_ci */ 4038c2ecf20Sopenharmony_cistatic void usbhsc_power_ctrl(struct usbhs_priv *priv, int enable) 4048c2ecf20Sopenharmony_ci{ 4058c2ecf20Sopenharmony_ci struct platform_device *pdev = usbhs_priv_to_pdev(priv); 4068c2ecf20Sopenharmony_ci struct device *dev = usbhs_priv_to_dev(priv); 4078c2ecf20Sopenharmony_ci 4088c2ecf20Sopenharmony_ci if (enable) { 4098c2ecf20Sopenharmony_ci /* enable PM */ 4108c2ecf20Sopenharmony_ci pm_runtime_get_sync(dev); 4118c2ecf20Sopenharmony_ci 4128c2ecf20Sopenharmony_ci /* enable clks */ 4138c2ecf20Sopenharmony_ci if (usbhsc_clk_prepare_enable(priv)) 4148c2ecf20Sopenharmony_ci return; 4158c2ecf20Sopenharmony_ci 4168c2ecf20Sopenharmony_ci /* enable platform power */ 4178c2ecf20Sopenharmony_ci usbhs_platform_call(priv, power_ctrl, pdev, priv->base, enable); 4188c2ecf20Sopenharmony_ci 4198c2ecf20Sopenharmony_ci /* USB on */ 4208c2ecf20Sopenharmony_ci usbhs_sys_clock_ctrl(priv, enable); 4218c2ecf20Sopenharmony_ci } else { 4228c2ecf20Sopenharmony_ci /* USB off */ 4238c2ecf20Sopenharmony_ci usbhs_sys_clock_ctrl(priv, enable); 4248c2ecf20Sopenharmony_ci 4258c2ecf20Sopenharmony_ci /* disable platform power */ 4268c2ecf20Sopenharmony_ci usbhs_platform_call(priv, power_ctrl, pdev, priv->base, enable); 4278c2ecf20Sopenharmony_ci 4288c2ecf20Sopenharmony_ci /* disable clks */ 4298c2ecf20Sopenharmony_ci usbhsc_clk_disable_unprepare(priv); 4308c2ecf20Sopenharmony_ci 4318c2ecf20Sopenharmony_ci /* disable PM */ 4328c2ecf20Sopenharmony_ci pm_runtime_put_sync(dev); 4338c2ecf20Sopenharmony_ci } 4348c2ecf20Sopenharmony_ci} 4358c2ecf20Sopenharmony_ci 4368c2ecf20Sopenharmony_ci/* 4378c2ecf20Sopenharmony_ci * hotplug 4388c2ecf20Sopenharmony_ci */ 4398c2ecf20Sopenharmony_cistatic void usbhsc_hotplug(struct usbhs_priv *priv) 4408c2ecf20Sopenharmony_ci{ 4418c2ecf20Sopenharmony_ci struct platform_device *pdev = usbhs_priv_to_pdev(priv); 4428c2ecf20Sopenharmony_ci struct usbhs_mod *mod = usbhs_mod_get_current(priv); 4438c2ecf20Sopenharmony_ci int id; 4448c2ecf20Sopenharmony_ci int enable; 4458c2ecf20Sopenharmony_ci int cable; 4468c2ecf20Sopenharmony_ci int ret; 4478c2ecf20Sopenharmony_ci 4488c2ecf20Sopenharmony_ci /* 4498c2ecf20Sopenharmony_ci * get vbus status from platform 4508c2ecf20Sopenharmony_ci */ 4518c2ecf20Sopenharmony_ci enable = usbhs_mod_info_call(priv, get_vbus, pdev); 4528c2ecf20Sopenharmony_ci 4538c2ecf20Sopenharmony_ci /* 4548c2ecf20Sopenharmony_ci * get id from platform 4558c2ecf20Sopenharmony_ci */ 4568c2ecf20Sopenharmony_ci id = usbhs_platform_call(priv, get_id, pdev); 4578c2ecf20Sopenharmony_ci 4588c2ecf20Sopenharmony_ci if (enable && !mod) { 4598c2ecf20Sopenharmony_ci if (priv->edev) { 4608c2ecf20Sopenharmony_ci cable = extcon_get_state(priv->edev, EXTCON_USB_HOST); 4618c2ecf20Sopenharmony_ci if ((cable > 0 && id != USBHS_HOST) || 4628c2ecf20Sopenharmony_ci (!cable && id != USBHS_GADGET)) { 4638c2ecf20Sopenharmony_ci dev_info(&pdev->dev, 4648c2ecf20Sopenharmony_ci "USB cable plugged in doesn't match the selected role!\n"); 4658c2ecf20Sopenharmony_ci return; 4668c2ecf20Sopenharmony_ci } 4678c2ecf20Sopenharmony_ci } 4688c2ecf20Sopenharmony_ci 4698c2ecf20Sopenharmony_ci ret = usbhs_mod_change(priv, id); 4708c2ecf20Sopenharmony_ci if (ret < 0) 4718c2ecf20Sopenharmony_ci return; 4728c2ecf20Sopenharmony_ci 4738c2ecf20Sopenharmony_ci dev_dbg(&pdev->dev, "%s enable\n", __func__); 4748c2ecf20Sopenharmony_ci 4758c2ecf20Sopenharmony_ci /* power on */ 4768c2ecf20Sopenharmony_ci if (usbhs_get_dparam(priv, runtime_pwctrl)) 4778c2ecf20Sopenharmony_ci usbhsc_power_ctrl(priv, enable); 4788c2ecf20Sopenharmony_ci 4798c2ecf20Sopenharmony_ci /* bus init */ 4808c2ecf20Sopenharmony_ci usbhsc_set_buswait(priv); 4818c2ecf20Sopenharmony_ci usbhsc_bus_init(priv); 4828c2ecf20Sopenharmony_ci 4838c2ecf20Sopenharmony_ci /* module start */ 4848c2ecf20Sopenharmony_ci usbhs_mod_call(priv, start, priv); 4858c2ecf20Sopenharmony_ci 4868c2ecf20Sopenharmony_ci } else if (!enable && mod) { 4878c2ecf20Sopenharmony_ci dev_dbg(&pdev->dev, "%s disable\n", __func__); 4888c2ecf20Sopenharmony_ci 4898c2ecf20Sopenharmony_ci /* module stop */ 4908c2ecf20Sopenharmony_ci usbhs_mod_call(priv, stop, priv); 4918c2ecf20Sopenharmony_ci 4928c2ecf20Sopenharmony_ci /* bus init */ 4938c2ecf20Sopenharmony_ci usbhsc_bus_init(priv); 4948c2ecf20Sopenharmony_ci 4958c2ecf20Sopenharmony_ci /* power off */ 4968c2ecf20Sopenharmony_ci if (usbhs_get_dparam(priv, runtime_pwctrl)) 4978c2ecf20Sopenharmony_ci usbhsc_power_ctrl(priv, enable); 4988c2ecf20Sopenharmony_ci 4998c2ecf20Sopenharmony_ci usbhs_mod_change(priv, -1); 5008c2ecf20Sopenharmony_ci 5018c2ecf20Sopenharmony_ci /* reset phy for next connection */ 5028c2ecf20Sopenharmony_ci usbhs_platform_call(priv, phy_reset, pdev); 5038c2ecf20Sopenharmony_ci } 5048c2ecf20Sopenharmony_ci} 5058c2ecf20Sopenharmony_ci 5068c2ecf20Sopenharmony_ci/* 5078c2ecf20Sopenharmony_ci * notify hotplug 5088c2ecf20Sopenharmony_ci */ 5098c2ecf20Sopenharmony_cistatic void usbhsc_notify_hotplug(struct work_struct *work) 5108c2ecf20Sopenharmony_ci{ 5118c2ecf20Sopenharmony_ci struct usbhs_priv *priv = container_of(work, 5128c2ecf20Sopenharmony_ci struct usbhs_priv, 5138c2ecf20Sopenharmony_ci notify_hotplug_work.work); 5148c2ecf20Sopenharmony_ci usbhsc_hotplug(priv); 5158c2ecf20Sopenharmony_ci} 5168c2ecf20Sopenharmony_ci 5178c2ecf20Sopenharmony_ciint usbhsc_schedule_notify_hotplug(struct platform_device *pdev) 5188c2ecf20Sopenharmony_ci{ 5198c2ecf20Sopenharmony_ci struct usbhs_priv *priv = usbhs_pdev_to_priv(pdev); 5208c2ecf20Sopenharmony_ci int delay = usbhs_get_dparam(priv, detection_delay); 5218c2ecf20Sopenharmony_ci 5228c2ecf20Sopenharmony_ci /* 5238c2ecf20Sopenharmony_ci * This functions will be called in interrupt. 5248c2ecf20Sopenharmony_ci * To make sure safety context, 5258c2ecf20Sopenharmony_ci * use workqueue for usbhs_notify_hotplug 5268c2ecf20Sopenharmony_ci */ 5278c2ecf20Sopenharmony_ci schedule_delayed_work(&priv->notify_hotplug_work, 5288c2ecf20Sopenharmony_ci msecs_to_jiffies(delay)); 5298c2ecf20Sopenharmony_ci return 0; 5308c2ecf20Sopenharmony_ci} 5318c2ecf20Sopenharmony_ci 5328c2ecf20Sopenharmony_ci/* 5338c2ecf20Sopenharmony_ci * platform functions 5348c2ecf20Sopenharmony_ci */ 5358c2ecf20Sopenharmony_cistatic const struct of_device_id usbhs_of_match[] = { 5368c2ecf20Sopenharmony_ci { 5378c2ecf20Sopenharmony_ci .compatible = "renesas,usbhs-r8a774c0", 5388c2ecf20Sopenharmony_ci .data = &usbhs_rcar_gen3_with_pll_plat_info, 5398c2ecf20Sopenharmony_ci }, 5408c2ecf20Sopenharmony_ci { 5418c2ecf20Sopenharmony_ci .compatible = "renesas,usbhs-r8a7790", 5428c2ecf20Sopenharmony_ci .data = &usbhs_rcar_gen2_plat_info, 5438c2ecf20Sopenharmony_ci }, 5448c2ecf20Sopenharmony_ci { 5458c2ecf20Sopenharmony_ci .compatible = "renesas,usbhs-r8a7791", 5468c2ecf20Sopenharmony_ci .data = &usbhs_rcar_gen2_plat_info, 5478c2ecf20Sopenharmony_ci }, 5488c2ecf20Sopenharmony_ci { 5498c2ecf20Sopenharmony_ci .compatible = "renesas,usbhs-r8a7794", 5508c2ecf20Sopenharmony_ci .data = &usbhs_rcar_gen2_plat_info, 5518c2ecf20Sopenharmony_ci }, 5528c2ecf20Sopenharmony_ci { 5538c2ecf20Sopenharmony_ci .compatible = "renesas,usbhs-r8a7795", 5548c2ecf20Sopenharmony_ci .data = &usbhs_rcar_gen3_plat_info, 5558c2ecf20Sopenharmony_ci }, 5568c2ecf20Sopenharmony_ci { 5578c2ecf20Sopenharmony_ci .compatible = "renesas,usbhs-r8a7796", 5588c2ecf20Sopenharmony_ci .data = &usbhs_rcar_gen3_plat_info, 5598c2ecf20Sopenharmony_ci }, 5608c2ecf20Sopenharmony_ci { 5618c2ecf20Sopenharmony_ci .compatible = "renesas,usbhs-r8a77990", 5628c2ecf20Sopenharmony_ci .data = &usbhs_rcar_gen3_with_pll_plat_info, 5638c2ecf20Sopenharmony_ci }, 5648c2ecf20Sopenharmony_ci { 5658c2ecf20Sopenharmony_ci .compatible = "renesas,usbhs-r8a77995", 5668c2ecf20Sopenharmony_ci .data = &usbhs_rcar_gen3_with_pll_plat_info, 5678c2ecf20Sopenharmony_ci }, 5688c2ecf20Sopenharmony_ci { 5698c2ecf20Sopenharmony_ci .compatible = "renesas,rcar-gen2-usbhs", 5708c2ecf20Sopenharmony_ci .data = &usbhs_rcar_gen2_plat_info, 5718c2ecf20Sopenharmony_ci }, 5728c2ecf20Sopenharmony_ci { 5738c2ecf20Sopenharmony_ci .compatible = "renesas,rcar-gen3-usbhs", 5748c2ecf20Sopenharmony_ci .data = &usbhs_rcar_gen3_plat_info, 5758c2ecf20Sopenharmony_ci }, 5768c2ecf20Sopenharmony_ci { 5778c2ecf20Sopenharmony_ci .compatible = "renesas,rza1-usbhs", 5788c2ecf20Sopenharmony_ci .data = &usbhs_rza1_plat_info, 5798c2ecf20Sopenharmony_ci }, 5808c2ecf20Sopenharmony_ci { 5818c2ecf20Sopenharmony_ci .compatible = "renesas,rza2-usbhs", 5828c2ecf20Sopenharmony_ci .data = &usbhs_rza2_plat_info, 5838c2ecf20Sopenharmony_ci }, 5848c2ecf20Sopenharmony_ci { }, 5858c2ecf20Sopenharmony_ci}; 5868c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, usbhs_of_match); 5878c2ecf20Sopenharmony_ci 5888c2ecf20Sopenharmony_cistatic int usbhs_probe(struct platform_device *pdev) 5898c2ecf20Sopenharmony_ci{ 5908c2ecf20Sopenharmony_ci const struct renesas_usbhs_platform_info *info; 5918c2ecf20Sopenharmony_ci struct usbhs_priv *priv; 5928c2ecf20Sopenharmony_ci struct resource *irq_res; 5938c2ecf20Sopenharmony_ci struct device *dev = &pdev->dev; 5948c2ecf20Sopenharmony_ci struct gpio_desc *gpiod; 5958c2ecf20Sopenharmony_ci int ret; 5968c2ecf20Sopenharmony_ci u32 tmp; 5978c2ecf20Sopenharmony_ci 5988c2ecf20Sopenharmony_ci /* check device node */ 5998c2ecf20Sopenharmony_ci if (dev_of_node(dev)) 6008c2ecf20Sopenharmony_ci info = of_device_get_match_data(dev); 6018c2ecf20Sopenharmony_ci else 6028c2ecf20Sopenharmony_ci info = renesas_usbhs_get_info(pdev); 6038c2ecf20Sopenharmony_ci 6048c2ecf20Sopenharmony_ci /* check platform information */ 6058c2ecf20Sopenharmony_ci if (!info) { 6068c2ecf20Sopenharmony_ci dev_err(dev, "no platform information\n"); 6078c2ecf20Sopenharmony_ci return -EINVAL; 6088c2ecf20Sopenharmony_ci } 6098c2ecf20Sopenharmony_ci 6108c2ecf20Sopenharmony_ci /* platform data */ 6118c2ecf20Sopenharmony_ci irq_res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); 6128c2ecf20Sopenharmony_ci if (!irq_res) { 6138c2ecf20Sopenharmony_ci dev_err(dev, "Not enough Renesas USB platform resources.\n"); 6148c2ecf20Sopenharmony_ci return -ENODEV; 6158c2ecf20Sopenharmony_ci } 6168c2ecf20Sopenharmony_ci 6178c2ecf20Sopenharmony_ci /* usb private data */ 6188c2ecf20Sopenharmony_ci priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); 6198c2ecf20Sopenharmony_ci if (!priv) 6208c2ecf20Sopenharmony_ci return -ENOMEM; 6218c2ecf20Sopenharmony_ci 6228c2ecf20Sopenharmony_ci priv->base = devm_platform_ioremap_resource(pdev, 0); 6238c2ecf20Sopenharmony_ci if (IS_ERR(priv->base)) 6248c2ecf20Sopenharmony_ci return PTR_ERR(priv->base); 6258c2ecf20Sopenharmony_ci 6268c2ecf20Sopenharmony_ci if (of_property_read_bool(dev_of_node(dev), "extcon")) { 6278c2ecf20Sopenharmony_ci priv->edev = extcon_get_edev_by_phandle(dev, 0); 6288c2ecf20Sopenharmony_ci if (IS_ERR(priv->edev)) 6298c2ecf20Sopenharmony_ci return PTR_ERR(priv->edev); 6308c2ecf20Sopenharmony_ci } 6318c2ecf20Sopenharmony_ci 6328c2ecf20Sopenharmony_ci priv->rsts = devm_reset_control_array_get_optional_shared(dev); 6338c2ecf20Sopenharmony_ci if (IS_ERR(priv->rsts)) 6348c2ecf20Sopenharmony_ci return PTR_ERR(priv->rsts); 6358c2ecf20Sopenharmony_ci 6368c2ecf20Sopenharmony_ci /* 6378c2ecf20Sopenharmony_ci * care platform info 6388c2ecf20Sopenharmony_ci */ 6398c2ecf20Sopenharmony_ci 6408c2ecf20Sopenharmony_ci priv->dparam = info->driver_param; 6418c2ecf20Sopenharmony_ci 6428c2ecf20Sopenharmony_ci if (!info->platform_callback.get_id) { 6438c2ecf20Sopenharmony_ci dev_err(dev, "no platform callbacks\n"); 6448c2ecf20Sopenharmony_ci return -EINVAL; 6458c2ecf20Sopenharmony_ci } 6468c2ecf20Sopenharmony_ci priv->pfunc = &info->platform_callback; 6478c2ecf20Sopenharmony_ci 6488c2ecf20Sopenharmony_ci /* set default param if platform doesn't have */ 6498c2ecf20Sopenharmony_ci if (usbhs_get_dparam(priv, has_new_pipe_configs)) { 6508c2ecf20Sopenharmony_ci priv->dparam.pipe_configs = usbhsc_new_pipe; 6518c2ecf20Sopenharmony_ci priv->dparam.pipe_size = ARRAY_SIZE(usbhsc_new_pipe); 6528c2ecf20Sopenharmony_ci } else if (!priv->dparam.pipe_configs) { 6538c2ecf20Sopenharmony_ci priv->dparam.pipe_configs = usbhsc_default_pipe; 6548c2ecf20Sopenharmony_ci priv->dparam.pipe_size = ARRAY_SIZE(usbhsc_default_pipe); 6558c2ecf20Sopenharmony_ci } 6568c2ecf20Sopenharmony_ci if (!priv->dparam.pio_dma_border) 6578c2ecf20Sopenharmony_ci priv->dparam.pio_dma_border = 64; /* 64byte */ 6588c2ecf20Sopenharmony_ci if (!of_property_read_u32(dev_of_node(dev), "renesas,buswait", &tmp)) 6598c2ecf20Sopenharmony_ci priv->dparam.buswait_bwait = tmp; 6608c2ecf20Sopenharmony_ci gpiod = devm_gpiod_get_optional(dev, "renesas,enable", GPIOD_IN); 6618c2ecf20Sopenharmony_ci if (IS_ERR(gpiod)) 6628c2ecf20Sopenharmony_ci return PTR_ERR(gpiod); 6638c2ecf20Sopenharmony_ci 6648c2ecf20Sopenharmony_ci /* FIXME */ 6658c2ecf20Sopenharmony_ci /* runtime power control ? */ 6668c2ecf20Sopenharmony_ci if (priv->pfunc->get_vbus) 6678c2ecf20Sopenharmony_ci usbhs_get_dparam(priv, runtime_pwctrl) = 1; 6688c2ecf20Sopenharmony_ci 6698c2ecf20Sopenharmony_ci /* 6708c2ecf20Sopenharmony_ci * priv settings 6718c2ecf20Sopenharmony_ci */ 6728c2ecf20Sopenharmony_ci priv->irq = irq_res->start; 6738c2ecf20Sopenharmony_ci if (irq_res->flags & IORESOURCE_IRQ_SHAREABLE) 6748c2ecf20Sopenharmony_ci priv->irqflags = IRQF_SHARED; 6758c2ecf20Sopenharmony_ci priv->pdev = pdev; 6768c2ecf20Sopenharmony_ci INIT_DELAYED_WORK(&priv->notify_hotplug_work, usbhsc_notify_hotplug); 6778c2ecf20Sopenharmony_ci spin_lock_init(usbhs_priv_to_lock(priv)); 6788c2ecf20Sopenharmony_ci 6798c2ecf20Sopenharmony_ci /* call pipe and module init */ 6808c2ecf20Sopenharmony_ci ret = usbhs_pipe_probe(priv); 6818c2ecf20Sopenharmony_ci if (ret < 0) 6828c2ecf20Sopenharmony_ci return ret; 6838c2ecf20Sopenharmony_ci 6848c2ecf20Sopenharmony_ci ret = usbhs_fifo_probe(priv); 6858c2ecf20Sopenharmony_ci if (ret < 0) 6868c2ecf20Sopenharmony_ci goto probe_end_pipe_exit; 6878c2ecf20Sopenharmony_ci 6888c2ecf20Sopenharmony_ci ret = usbhs_mod_probe(priv); 6898c2ecf20Sopenharmony_ci if (ret < 0) 6908c2ecf20Sopenharmony_ci goto probe_end_fifo_exit; 6918c2ecf20Sopenharmony_ci 6928c2ecf20Sopenharmony_ci /* dev_set_drvdata should be called after usbhs_mod_init */ 6938c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, priv); 6948c2ecf20Sopenharmony_ci 6958c2ecf20Sopenharmony_ci ret = reset_control_deassert(priv->rsts); 6968c2ecf20Sopenharmony_ci if (ret) 6978c2ecf20Sopenharmony_ci goto probe_fail_rst; 6988c2ecf20Sopenharmony_ci 6998c2ecf20Sopenharmony_ci ret = usbhsc_clk_get(dev, priv); 7008c2ecf20Sopenharmony_ci if (ret) 7018c2ecf20Sopenharmony_ci goto probe_fail_clks; 7028c2ecf20Sopenharmony_ci 7038c2ecf20Sopenharmony_ci /* 7048c2ecf20Sopenharmony_ci * deviece reset here because 7058c2ecf20Sopenharmony_ci * USB device might be used in boot loader. 7068c2ecf20Sopenharmony_ci */ 7078c2ecf20Sopenharmony_ci usbhs_sys_clock_ctrl(priv, 0); 7088c2ecf20Sopenharmony_ci 7098c2ecf20Sopenharmony_ci /* check GPIO determining if USB function should be enabled */ 7108c2ecf20Sopenharmony_ci if (gpiod) { 7118c2ecf20Sopenharmony_ci ret = !gpiod_get_value(gpiod); 7128c2ecf20Sopenharmony_ci if (ret) { 7138c2ecf20Sopenharmony_ci dev_warn(dev, "USB function not selected (GPIO)\n"); 7148c2ecf20Sopenharmony_ci ret = -ENOTSUPP; 7158c2ecf20Sopenharmony_ci goto probe_end_mod_exit; 7168c2ecf20Sopenharmony_ci } 7178c2ecf20Sopenharmony_ci } 7188c2ecf20Sopenharmony_ci 7198c2ecf20Sopenharmony_ci /* 7208c2ecf20Sopenharmony_ci * platform call 7218c2ecf20Sopenharmony_ci * 7228c2ecf20Sopenharmony_ci * USB phy setup might depend on CPU/Board. 7238c2ecf20Sopenharmony_ci * If platform has its callback functions, 7248c2ecf20Sopenharmony_ci * call it here. 7258c2ecf20Sopenharmony_ci */ 7268c2ecf20Sopenharmony_ci ret = usbhs_platform_call(priv, hardware_init, pdev); 7278c2ecf20Sopenharmony_ci if (ret < 0) { 7288c2ecf20Sopenharmony_ci dev_err(dev, "platform init failed.\n"); 7298c2ecf20Sopenharmony_ci goto probe_end_mod_exit; 7308c2ecf20Sopenharmony_ci } 7318c2ecf20Sopenharmony_ci 7328c2ecf20Sopenharmony_ci /* reset phy for connection */ 7338c2ecf20Sopenharmony_ci usbhs_platform_call(priv, phy_reset, pdev); 7348c2ecf20Sopenharmony_ci 7358c2ecf20Sopenharmony_ci /* power control */ 7368c2ecf20Sopenharmony_ci pm_runtime_enable(dev); 7378c2ecf20Sopenharmony_ci if (!usbhs_get_dparam(priv, runtime_pwctrl)) { 7388c2ecf20Sopenharmony_ci usbhsc_power_ctrl(priv, 1); 7398c2ecf20Sopenharmony_ci usbhs_mod_autonomy_mode(priv); 7408c2ecf20Sopenharmony_ci } else { 7418c2ecf20Sopenharmony_ci usbhs_mod_non_autonomy_mode(priv); 7428c2ecf20Sopenharmony_ci } 7438c2ecf20Sopenharmony_ci 7448c2ecf20Sopenharmony_ci /* 7458c2ecf20Sopenharmony_ci * manual call notify_hotplug for cold plug 7468c2ecf20Sopenharmony_ci */ 7478c2ecf20Sopenharmony_ci usbhsc_schedule_notify_hotplug(pdev); 7488c2ecf20Sopenharmony_ci 7498c2ecf20Sopenharmony_ci dev_info(dev, "probed\n"); 7508c2ecf20Sopenharmony_ci 7518c2ecf20Sopenharmony_ci return ret; 7528c2ecf20Sopenharmony_ci 7538c2ecf20Sopenharmony_ciprobe_end_mod_exit: 7548c2ecf20Sopenharmony_ci usbhsc_clk_put(priv); 7558c2ecf20Sopenharmony_ciprobe_fail_clks: 7568c2ecf20Sopenharmony_ci reset_control_assert(priv->rsts); 7578c2ecf20Sopenharmony_ciprobe_fail_rst: 7588c2ecf20Sopenharmony_ci usbhs_mod_remove(priv); 7598c2ecf20Sopenharmony_ciprobe_end_fifo_exit: 7608c2ecf20Sopenharmony_ci usbhs_fifo_remove(priv); 7618c2ecf20Sopenharmony_ciprobe_end_pipe_exit: 7628c2ecf20Sopenharmony_ci usbhs_pipe_remove(priv); 7638c2ecf20Sopenharmony_ci 7648c2ecf20Sopenharmony_ci dev_info(dev, "probe failed (%d)\n", ret); 7658c2ecf20Sopenharmony_ci 7668c2ecf20Sopenharmony_ci return ret; 7678c2ecf20Sopenharmony_ci} 7688c2ecf20Sopenharmony_ci 7698c2ecf20Sopenharmony_cistatic int usbhs_remove(struct platform_device *pdev) 7708c2ecf20Sopenharmony_ci{ 7718c2ecf20Sopenharmony_ci struct usbhs_priv *priv = usbhs_pdev_to_priv(pdev); 7728c2ecf20Sopenharmony_ci 7738c2ecf20Sopenharmony_ci dev_dbg(&pdev->dev, "usb remove\n"); 7748c2ecf20Sopenharmony_ci 7758c2ecf20Sopenharmony_ci /* power off */ 7768c2ecf20Sopenharmony_ci if (!usbhs_get_dparam(priv, runtime_pwctrl)) 7778c2ecf20Sopenharmony_ci usbhsc_power_ctrl(priv, 0); 7788c2ecf20Sopenharmony_ci 7798c2ecf20Sopenharmony_ci pm_runtime_disable(&pdev->dev); 7808c2ecf20Sopenharmony_ci 7818c2ecf20Sopenharmony_ci usbhs_platform_call(priv, hardware_exit, pdev); 7828c2ecf20Sopenharmony_ci usbhsc_clk_put(priv); 7838c2ecf20Sopenharmony_ci reset_control_assert(priv->rsts); 7848c2ecf20Sopenharmony_ci usbhs_mod_remove(priv); 7858c2ecf20Sopenharmony_ci usbhs_fifo_remove(priv); 7868c2ecf20Sopenharmony_ci usbhs_pipe_remove(priv); 7878c2ecf20Sopenharmony_ci 7888c2ecf20Sopenharmony_ci return 0; 7898c2ecf20Sopenharmony_ci} 7908c2ecf20Sopenharmony_ci 7918c2ecf20Sopenharmony_cistatic __maybe_unused int usbhsc_suspend(struct device *dev) 7928c2ecf20Sopenharmony_ci{ 7938c2ecf20Sopenharmony_ci struct usbhs_priv *priv = dev_get_drvdata(dev); 7948c2ecf20Sopenharmony_ci struct usbhs_mod *mod = usbhs_mod_get_current(priv); 7958c2ecf20Sopenharmony_ci 7968c2ecf20Sopenharmony_ci if (mod) { 7978c2ecf20Sopenharmony_ci usbhs_mod_call(priv, stop, priv); 7988c2ecf20Sopenharmony_ci usbhs_mod_change(priv, -1); 7998c2ecf20Sopenharmony_ci } 8008c2ecf20Sopenharmony_ci 8018c2ecf20Sopenharmony_ci if (mod || !usbhs_get_dparam(priv, runtime_pwctrl)) 8028c2ecf20Sopenharmony_ci usbhsc_power_ctrl(priv, 0); 8038c2ecf20Sopenharmony_ci 8048c2ecf20Sopenharmony_ci return 0; 8058c2ecf20Sopenharmony_ci} 8068c2ecf20Sopenharmony_ci 8078c2ecf20Sopenharmony_cistatic __maybe_unused int usbhsc_resume(struct device *dev) 8088c2ecf20Sopenharmony_ci{ 8098c2ecf20Sopenharmony_ci struct usbhs_priv *priv = dev_get_drvdata(dev); 8108c2ecf20Sopenharmony_ci struct platform_device *pdev = usbhs_priv_to_pdev(priv); 8118c2ecf20Sopenharmony_ci 8128c2ecf20Sopenharmony_ci if (!usbhs_get_dparam(priv, runtime_pwctrl)) { 8138c2ecf20Sopenharmony_ci usbhsc_power_ctrl(priv, 1); 8148c2ecf20Sopenharmony_ci usbhs_mod_autonomy_mode(priv); 8158c2ecf20Sopenharmony_ci } 8168c2ecf20Sopenharmony_ci 8178c2ecf20Sopenharmony_ci usbhs_platform_call(priv, phy_reset, pdev); 8188c2ecf20Sopenharmony_ci 8198c2ecf20Sopenharmony_ci usbhsc_schedule_notify_hotplug(pdev); 8208c2ecf20Sopenharmony_ci 8218c2ecf20Sopenharmony_ci return 0; 8228c2ecf20Sopenharmony_ci} 8238c2ecf20Sopenharmony_ci 8248c2ecf20Sopenharmony_cistatic SIMPLE_DEV_PM_OPS(usbhsc_pm_ops, usbhsc_suspend, usbhsc_resume); 8258c2ecf20Sopenharmony_ci 8268c2ecf20Sopenharmony_cistatic struct platform_driver renesas_usbhs_driver = { 8278c2ecf20Sopenharmony_ci .driver = { 8288c2ecf20Sopenharmony_ci .name = "renesas_usbhs", 8298c2ecf20Sopenharmony_ci .pm = &usbhsc_pm_ops, 8308c2ecf20Sopenharmony_ci .of_match_table = of_match_ptr(usbhs_of_match), 8318c2ecf20Sopenharmony_ci }, 8328c2ecf20Sopenharmony_ci .probe = usbhs_probe, 8338c2ecf20Sopenharmony_ci .remove = usbhs_remove, 8348c2ecf20Sopenharmony_ci}; 8358c2ecf20Sopenharmony_ci 8368c2ecf20Sopenharmony_cimodule_platform_driver(renesas_usbhs_driver); 8378c2ecf20Sopenharmony_ci 8388c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 8398c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Renesas USB driver"); 8408c2ecf20Sopenharmony_ciMODULE_AUTHOR("Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>"); 841