162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-1.0+ 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Renesas USB driver 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2011 Renesas Solutions Corp. 662306a36Sopenharmony_ci * Copyright (C) 2019 Renesas Electronics Corporation 762306a36Sopenharmony_ci * Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> 862306a36Sopenharmony_ci */ 962306a36Sopenharmony_ci#include <linux/clk.h> 1062306a36Sopenharmony_ci#include <linux/err.h> 1162306a36Sopenharmony_ci#include <linux/gpio/consumer.h> 1262306a36Sopenharmony_ci#include <linux/io.h> 1362306a36Sopenharmony_ci#include <linux/module.h> 1462306a36Sopenharmony_ci#include <linux/of.h> 1562306a36Sopenharmony_ci#include <linux/pm_runtime.h> 1662306a36Sopenharmony_ci#include <linux/reset.h> 1762306a36Sopenharmony_ci#include <linux/slab.h> 1862306a36Sopenharmony_ci#include <linux/sysfs.h> 1962306a36Sopenharmony_ci#include "common.h" 2062306a36Sopenharmony_ci#include "rcar2.h" 2162306a36Sopenharmony_ci#include "rcar3.h" 2262306a36Sopenharmony_ci#include "rza.h" 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci/* 2562306a36Sopenharmony_ci * image of renesas_usbhs 2662306a36Sopenharmony_ci * 2762306a36Sopenharmony_ci * ex) gadget case 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci * mod.c 3062306a36Sopenharmony_ci * mod_gadget.c 3162306a36Sopenharmony_ci * mod_host.c pipe.c fifo.c 3262306a36Sopenharmony_ci * 3362306a36Sopenharmony_ci * +-------+ +-----------+ 3462306a36Sopenharmony_ci * | pipe0 |------>| fifo pio | 3562306a36Sopenharmony_ci * +------------+ +-------+ +-----------+ 3662306a36Sopenharmony_ci * | mod_gadget |=====> | pipe1 |--+ 3762306a36Sopenharmony_ci * +------------+ +-------+ | +-----------+ 3862306a36Sopenharmony_ci * | pipe2 | | +-| fifo dma0 | 3962306a36Sopenharmony_ci * +------------+ +-------+ | | +-----------+ 4062306a36Sopenharmony_ci * | mod_host | | pipe3 |<-|--+ 4162306a36Sopenharmony_ci * +------------+ +-------+ | +-----------+ 4262306a36Sopenharmony_ci * | .... | +--->| fifo dma1 | 4362306a36Sopenharmony_ci * | .... | +-----------+ 4462306a36Sopenharmony_ci */ 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci/* 4762306a36Sopenharmony_ci * platform call back 4862306a36Sopenharmony_ci * 4962306a36Sopenharmony_ci * renesas usb support platform callback function. 5062306a36Sopenharmony_ci * Below macro call it. 5162306a36Sopenharmony_ci * if platform doesn't have callback, it return 0 (no error) 5262306a36Sopenharmony_ci */ 5362306a36Sopenharmony_ci#define usbhs_platform_call(priv, func, args...)\ 5462306a36Sopenharmony_ci (!(priv) ? -ENODEV : \ 5562306a36Sopenharmony_ci !((priv)->pfunc->func) ? 0 : \ 5662306a36Sopenharmony_ci (priv)->pfunc->func(args)) 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci/* 5962306a36Sopenharmony_ci * common functions 6062306a36Sopenharmony_ci */ 6162306a36Sopenharmony_ciu16 usbhs_read(struct usbhs_priv *priv, u32 reg) 6262306a36Sopenharmony_ci{ 6362306a36Sopenharmony_ci return ioread16(priv->base + reg); 6462306a36Sopenharmony_ci} 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_civoid usbhs_write(struct usbhs_priv *priv, u32 reg, u16 data) 6762306a36Sopenharmony_ci{ 6862306a36Sopenharmony_ci iowrite16(data, priv->base + reg); 6962306a36Sopenharmony_ci} 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_civoid usbhs_bset(struct usbhs_priv *priv, u32 reg, u16 mask, u16 data) 7262306a36Sopenharmony_ci{ 7362306a36Sopenharmony_ci u16 val = usbhs_read(priv, reg); 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci val &= ~mask; 7662306a36Sopenharmony_ci val |= data & mask; 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci usbhs_write(priv, reg, val); 7962306a36Sopenharmony_ci} 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_cistruct usbhs_priv *usbhs_pdev_to_priv(struct platform_device *pdev) 8262306a36Sopenharmony_ci{ 8362306a36Sopenharmony_ci return dev_get_drvdata(&pdev->dev); 8462306a36Sopenharmony_ci} 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ciint usbhs_get_id_as_gadget(struct platform_device *pdev) 8762306a36Sopenharmony_ci{ 8862306a36Sopenharmony_ci return USBHS_GADGET; 8962306a36Sopenharmony_ci} 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci/* 9262306a36Sopenharmony_ci * syscfg functions 9362306a36Sopenharmony_ci */ 9462306a36Sopenharmony_cistatic void usbhs_sys_clock_ctrl(struct usbhs_priv *priv, int enable) 9562306a36Sopenharmony_ci{ 9662306a36Sopenharmony_ci usbhs_bset(priv, SYSCFG, SCKE, enable ? SCKE : 0); 9762306a36Sopenharmony_ci} 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_civoid usbhs_sys_host_ctrl(struct usbhs_priv *priv, int enable) 10062306a36Sopenharmony_ci{ 10162306a36Sopenharmony_ci u16 mask = DCFM | DRPD | DPRPU | HSE | USBE; 10262306a36Sopenharmony_ci u16 val = DCFM | DRPD | HSE | USBE; 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci /* 10562306a36Sopenharmony_ci * if enable 10662306a36Sopenharmony_ci * 10762306a36Sopenharmony_ci * - select Host mode 10862306a36Sopenharmony_ci * - D+ Line/D- Line Pull-down 10962306a36Sopenharmony_ci */ 11062306a36Sopenharmony_ci usbhs_bset(priv, SYSCFG, mask, enable ? val : 0); 11162306a36Sopenharmony_ci} 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_civoid usbhs_sys_function_ctrl(struct usbhs_priv *priv, int enable) 11462306a36Sopenharmony_ci{ 11562306a36Sopenharmony_ci u16 mask = DCFM | DRPD | DPRPU | HSE | USBE; 11662306a36Sopenharmony_ci u16 val = HSE | USBE; 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci /* CNEN bit is required for function operation */ 11962306a36Sopenharmony_ci if (usbhs_get_dparam(priv, has_cnen)) { 12062306a36Sopenharmony_ci mask |= CNEN; 12162306a36Sopenharmony_ci val |= CNEN; 12262306a36Sopenharmony_ci } 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci /* 12562306a36Sopenharmony_ci * if enable 12662306a36Sopenharmony_ci * 12762306a36Sopenharmony_ci * - select Function mode 12862306a36Sopenharmony_ci * - D+ Line Pull-up is disabled 12962306a36Sopenharmony_ci * When D+ Line Pull-up is enabled, 13062306a36Sopenharmony_ci * calling usbhs_sys_function_pullup(,1) 13162306a36Sopenharmony_ci */ 13262306a36Sopenharmony_ci usbhs_bset(priv, SYSCFG, mask, enable ? val : 0); 13362306a36Sopenharmony_ci} 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_civoid usbhs_sys_function_pullup(struct usbhs_priv *priv, int enable) 13662306a36Sopenharmony_ci{ 13762306a36Sopenharmony_ci usbhs_bset(priv, SYSCFG, DPRPU, enable ? DPRPU : 0); 13862306a36Sopenharmony_ci} 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_civoid usbhs_sys_set_test_mode(struct usbhs_priv *priv, u16 mode) 14162306a36Sopenharmony_ci{ 14262306a36Sopenharmony_ci usbhs_write(priv, TESTMODE, mode); 14362306a36Sopenharmony_ci} 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci/* 14662306a36Sopenharmony_ci * frame functions 14762306a36Sopenharmony_ci */ 14862306a36Sopenharmony_ciint usbhs_frame_get_num(struct usbhs_priv *priv) 14962306a36Sopenharmony_ci{ 15062306a36Sopenharmony_ci return usbhs_read(priv, FRMNUM) & FRNM_MASK; 15162306a36Sopenharmony_ci} 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci/* 15462306a36Sopenharmony_ci * usb request functions 15562306a36Sopenharmony_ci */ 15662306a36Sopenharmony_civoid usbhs_usbreq_get_val(struct usbhs_priv *priv, struct usb_ctrlrequest *req) 15762306a36Sopenharmony_ci{ 15862306a36Sopenharmony_ci u16 val; 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci val = usbhs_read(priv, USBREQ); 16162306a36Sopenharmony_ci req->bRequest = (val >> 8) & 0xFF; 16262306a36Sopenharmony_ci req->bRequestType = (val >> 0) & 0xFF; 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci req->wValue = cpu_to_le16(usbhs_read(priv, USBVAL)); 16562306a36Sopenharmony_ci req->wIndex = cpu_to_le16(usbhs_read(priv, USBINDX)); 16662306a36Sopenharmony_ci req->wLength = cpu_to_le16(usbhs_read(priv, USBLENG)); 16762306a36Sopenharmony_ci} 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_civoid usbhs_usbreq_set_val(struct usbhs_priv *priv, struct usb_ctrlrequest *req) 17062306a36Sopenharmony_ci{ 17162306a36Sopenharmony_ci usbhs_write(priv, USBREQ, (req->bRequest << 8) | req->bRequestType); 17262306a36Sopenharmony_ci usbhs_write(priv, USBVAL, le16_to_cpu(req->wValue)); 17362306a36Sopenharmony_ci usbhs_write(priv, USBINDX, le16_to_cpu(req->wIndex)); 17462306a36Sopenharmony_ci usbhs_write(priv, USBLENG, le16_to_cpu(req->wLength)); 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci usbhs_bset(priv, DCPCTR, SUREQ, SUREQ); 17762306a36Sopenharmony_ci} 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci/* 18062306a36Sopenharmony_ci * bus/vbus functions 18162306a36Sopenharmony_ci */ 18262306a36Sopenharmony_civoid usbhs_bus_send_sof_enable(struct usbhs_priv *priv) 18362306a36Sopenharmony_ci{ 18462306a36Sopenharmony_ci u16 status = usbhs_read(priv, DVSTCTR) & (USBRST | UACT); 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ci if (status != USBRST) { 18762306a36Sopenharmony_ci struct device *dev = usbhs_priv_to_dev(priv); 18862306a36Sopenharmony_ci dev_err(dev, "usbhs should be reset\n"); 18962306a36Sopenharmony_ci } 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ci usbhs_bset(priv, DVSTCTR, (USBRST | UACT), UACT); 19262306a36Sopenharmony_ci} 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_civoid usbhs_bus_send_reset(struct usbhs_priv *priv) 19562306a36Sopenharmony_ci{ 19662306a36Sopenharmony_ci usbhs_bset(priv, DVSTCTR, (USBRST | UACT), USBRST); 19762306a36Sopenharmony_ci} 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ciint usbhs_bus_get_speed(struct usbhs_priv *priv) 20062306a36Sopenharmony_ci{ 20162306a36Sopenharmony_ci u16 dvstctr = usbhs_read(priv, DVSTCTR); 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci switch (RHST & dvstctr) { 20462306a36Sopenharmony_ci case RHST_LOW_SPEED: 20562306a36Sopenharmony_ci return USB_SPEED_LOW; 20662306a36Sopenharmony_ci case RHST_FULL_SPEED: 20762306a36Sopenharmony_ci return USB_SPEED_FULL; 20862306a36Sopenharmony_ci case RHST_HIGH_SPEED: 20962306a36Sopenharmony_ci return USB_SPEED_HIGH; 21062306a36Sopenharmony_ci } 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ci return USB_SPEED_UNKNOWN; 21362306a36Sopenharmony_ci} 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ciint usbhs_vbus_ctrl(struct usbhs_priv *priv, int enable) 21662306a36Sopenharmony_ci{ 21762306a36Sopenharmony_ci struct platform_device *pdev = usbhs_priv_to_pdev(priv); 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ci return usbhs_platform_call(priv, set_vbus, pdev, enable); 22062306a36Sopenharmony_ci} 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_cistatic void usbhsc_bus_init(struct usbhs_priv *priv) 22362306a36Sopenharmony_ci{ 22462306a36Sopenharmony_ci usbhs_write(priv, DVSTCTR, 0); 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ci usbhs_vbus_ctrl(priv, 0); 22762306a36Sopenharmony_ci} 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ci/* 23062306a36Sopenharmony_ci * device configuration 23162306a36Sopenharmony_ci */ 23262306a36Sopenharmony_ciint usbhs_set_device_config(struct usbhs_priv *priv, int devnum, 23362306a36Sopenharmony_ci u16 upphub, u16 hubport, u16 speed) 23462306a36Sopenharmony_ci{ 23562306a36Sopenharmony_ci struct device *dev = usbhs_priv_to_dev(priv); 23662306a36Sopenharmony_ci u16 usbspd = 0; 23762306a36Sopenharmony_ci u32 reg = DEVADD0 + (2 * devnum); 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_ci if (devnum > 10) { 24062306a36Sopenharmony_ci dev_err(dev, "cannot set speed to unknown device %d\n", devnum); 24162306a36Sopenharmony_ci return -EIO; 24262306a36Sopenharmony_ci } 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ci if (upphub > 0xA) { 24562306a36Sopenharmony_ci dev_err(dev, "unsupported hub number %d\n", upphub); 24662306a36Sopenharmony_ci return -EIO; 24762306a36Sopenharmony_ci } 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_ci switch (speed) { 25062306a36Sopenharmony_ci case USB_SPEED_LOW: 25162306a36Sopenharmony_ci usbspd = USBSPD_SPEED_LOW; 25262306a36Sopenharmony_ci break; 25362306a36Sopenharmony_ci case USB_SPEED_FULL: 25462306a36Sopenharmony_ci usbspd = USBSPD_SPEED_FULL; 25562306a36Sopenharmony_ci break; 25662306a36Sopenharmony_ci case USB_SPEED_HIGH: 25762306a36Sopenharmony_ci usbspd = USBSPD_SPEED_HIGH; 25862306a36Sopenharmony_ci break; 25962306a36Sopenharmony_ci default: 26062306a36Sopenharmony_ci dev_err(dev, "unsupported speed %d\n", speed); 26162306a36Sopenharmony_ci return -EIO; 26262306a36Sopenharmony_ci } 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_ci usbhs_write(priv, reg, UPPHUB(upphub) | 26562306a36Sopenharmony_ci HUBPORT(hubport)| 26662306a36Sopenharmony_ci USBSPD(usbspd)); 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_ci return 0; 26962306a36Sopenharmony_ci} 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ci/* 27262306a36Sopenharmony_ci * interrupt functions 27362306a36Sopenharmony_ci */ 27462306a36Sopenharmony_civoid usbhs_xxxsts_clear(struct usbhs_priv *priv, u16 sts_reg, u16 bit) 27562306a36Sopenharmony_ci{ 27662306a36Sopenharmony_ci u16 pipe_mask = (u16)GENMASK(usbhs_get_dparam(priv, pipe_size), 0); 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_ci usbhs_write(priv, sts_reg, ~(1 << bit) & pipe_mask); 27962306a36Sopenharmony_ci} 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_ci/* 28262306a36Sopenharmony_ci * local functions 28362306a36Sopenharmony_ci */ 28462306a36Sopenharmony_cistatic void usbhsc_set_buswait(struct usbhs_priv *priv) 28562306a36Sopenharmony_ci{ 28662306a36Sopenharmony_ci int wait = usbhs_get_dparam(priv, buswait_bwait); 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_ci /* set bus wait if platform have */ 28962306a36Sopenharmony_ci if (wait) 29062306a36Sopenharmony_ci usbhs_bset(priv, BUSWAIT, 0x000F, wait); 29162306a36Sopenharmony_ci} 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_cistatic bool usbhsc_is_multi_clks(struct usbhs_priv *priv) 29462306a36Sopenharmony_ci{ 29562306a36Sopenharmony_ci return priv->dparam.multi_clks; 29662306a36Sopenharmony_ci} 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_cistatic int usbhsc_clk_get(struct device *dev, struct usbhs_priv *priv) 29962306a36Sopenharmony_ci{ 30062306a36Sopenharmony_ci if (!usbhsc_is_multi_clks(priv)) 30162306a36Sopenharmony_ci return 0; 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_ci /* The first clock should exist */ 30462306a36Sopenharmony_ci priv->clks[0] = of_clk_get(dev_of_node(dev), 0); 30562306a36Sopenharmony_ci if (IS_ERR(priv->clks[0])) 30662306a36Sopenharmony_ci return PTR_ERR(priv->clks[0]); 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_ci /* 30962306a36Sopenharmony_ci * To backward compatibility with old DT, this driver checks the return 31062306a36Sopenharmony_ci * value if it's -ENOENT or not. 31162306a36Sopenharmony_ci */ 31262306a36Sopenharmony_ci priv->clks[1] = of_clk_get(dev_of_node(dev), 1); 31362306a36Sopenharmony_ci if (PTR_ERR(priv->clks[1]) == -ENOENT) 31462306a36Sopenharmony_ci priv->clks[1] = NULL; 31562306a36Sopenharmony_ci else if (IS_ERR(priv->clks[1])) 31662306a36Sopenharmony_ci return PTR_ERR(priv->clks[1]); 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_ci return 0; 31962306a36Sopenharmony_ci} 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_cistatic void usbhsc_clk_put(struct usbhs_priv *priv) 32262306a36Sopenharmony_ci{ 32362306a36Sopenharmony_ci int i; 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_ci if (!usbhsc_is_multi_clks(priv)) 32662306a36Sopenharmony_ci return; 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(priv->clks); i++) 32962306a36Sopenharmony_ci clk_put(priv->clks[i]); 33062306a36Sopenharmony_ci} 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_cistatic int usbhsc_clk_prepare_enable(struct usbhs_priv *priv) 33362306a36Sopenharmony_ci{ 33462306a36Sopenharmony_ci int i, ret; 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_ci if (!usbhsc_is_multi_clks(priv)) 33762306a36Sopenharmony_ci return 0; 33862306a36Sopenharmony_ci 33962306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(priv->clks); i++) { 34062306a36Sopenharmony_ci ret = clk_prepare_enable(priv->clks[i]); 34162306a36Sopenharmony_ci if (ret) { 34262306a36Sopenharmony_ci while (--i >= 0) 34362306a36Sopenharmony_ci clk_disable_unprepare(priv->clks[i]); 34462306a36Sopenharmony_ci return ret; 34562306a36Sopenharmony_ci } 34662306a36Sopenharmony_ci } 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_ci return ret; 34962306a36Sopenharmony_ci} 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_cistatic void usbhsc_clk_disable_unprepare(struct usbhs_priv *priv) 35262306a36Sopenharmony_ci{ 35362306a36Sopenharmony_ci int i; 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_ci if (!usbhsc_is_multi_clks(priv)) 35662306a36Sopenharmony_ci return; 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(priv->clks); i++) 35962306a36Sopenharmony_ci clk_disable_unprepare(priv->clks[i]); 36062306a36Sopenharmony_ci} 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_ci/* 36362306a36Sopenharmony_ci * platform default param 36462306a36Sopenharmony_ci */ 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_ci/* commonly used on old SH-Mobile SoCs */ 36762306a36Sopenharmony_cistatic struct renesas_usbhs_driver_pipe_config usbhsc_default_pipe[] = { 36862306a36Sopenharmony_ci RENESAS_USBHS_PIPE(USB_ENDPOINT_XFER_CONTROL, 64, 0x00, false), 36962306a36Sopenharmony_ci RENESAS_USBHS_PIPE(USB_ENDPOINT_XFER_ISOC, 1024, 0x08, false), 37062306a36Sopenharmony_ci RENESAS_USBHS_PIPE(USB_ENDPOINT_XFER_ISOC, 1024, 0x18, false), 37162306a36Sopenharmony_ci RENESAS_USBHS_PIPE(USB_ENDPOINT_XFER_BULK, 512, 0x28, true), 37262306a36Sopenharmony_ci RENESAS_USBHS_PIPE(USB_ENDPOINT_XFER_BULK, 512, 0x38, true), 37362306a36Sopenharmony_ci RENESAS_USBHS_PIPE(USB_ENDPOINT_XFER_BULK, 512, 0x48, true), 37462306a36Sopenharmony_ci RENESAS_USBHS_PIPE(USB_ENDPOINT_XFER_INT, 64, 0x04, false), 37562306a36Sopenharmony_ci RENESAS_USBHS_PIPE(USB_ENDPOINT_XFER_INT, 64, 0x05, false), 37662306a36Sopenharmony_ci RENESAS_USBHS_PIPE(USB_ENDPOINT_XFER_INT, 64, 0x06, false), 37762306a36Sopenharmony_ci RENESAS_USBHS_PIPE(USB_ENDPOINT_XFER_INT, 64, 0x07, false), 37862306a36Sopenharmony_ci}; 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_ci/* commonly used on newer SH-Mobile and R-Car SoCs */ 38162306a36Sopenharmony_cistatic struct renesas_usbhs_driver_pipe_config usbhsc_new_pipe[] = { 38262306a36Sopenharmony_ci RENESAS_USBHS_PIPE(USB_ENDPOINT_XFER_CONTROL, 64, 0x00, false), 38362306a36Sopenharmony_ci RENESAS_USBHS_PIPE(USB_ENDPOINT_XFER_ISOC, 1024, 0x08, true), 38462306a36Sopenharmony_ci RENESAS_USBHS_PIPE(USB_ENDPOINT_XFER_ISOC, 1024, 0x28, true), 38562306a36Sopenharmony_ci RENESAS_USBHS_PIPE(USB_ENDPOINT_XFER_BULK, 512, 0x48, true), 38662306a36Sopenharmony_ci RENESAS_USBHS_PIPE(USB_ENDPOINT_XFER_BULK, 512, 0x58, true), 38762306a36Sopenharmony_ci RENESAS_USBHS_PIPE(USB_ENDPOINT_XFER_BULK, 512, 0x68, true), 38862306a36Sopenharmony_ci RENESAS_USBHS_PIPE(USB_ENDPOINT_XFER_INT, 64, 0x04, false), 38962306a36Sopenharmony_ci RENESAS_USBHS_PIPE(USB_ENDPOINT_XFER_INT, 64, 0x05, false), 39062306a36Sopenharmony_ci RENESAS_USBHS_PIPE(USB_ENDPOINT_XFER_INT, 64, 0x06, false), 39162306a36Sopenharmony_ci RENESAS_USBHS_PIPE(USB_ENDPOINT_XFER_BULK, 512, 0x78, true), 39262306a36Sopenharmony_ci RENESAS_USBHS_PIPE(USB_ENDPOINT_XFER_BULK, 512, 0x88, true), 39362306a36Sopenharmony_ci RENESAS_USBHS_PIPE(USB_ENDPOINT_XFER_BULK, 512, 0x98, true), 39462306a36Sopenharmony_ci RENESAS_USBHS_PIPE(USB_ENDPOINT_XFER_BULK, 512, 0xa8, true), 39562306a36Sopenharmony_ci RENESAS_USBHS_PIPE(USB_ENDPOINT_XFER_BULK, 512, 0xb8, true), 39662306a36Sopenharmony_ci RENESAS_USBHS_PIPE(USB_ENDPOINT_XFER_BULK, 512, 0xc8, true), 39762306a36Sopenharmony_ci RENESAS_USBHS_PIPE(USB_ENDPOINT_XFER_BULK, 512, 0xd8, true), 39862306a36Sopenharmony_ci}; 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_ci/* 40162306a36Sopenharmony_ci * power control 40262306a36Sopenharmony_ci */ 40362306a36Sopenharmony_cistatic void usbhsc_power_ctrl(struct usbhs_priv *priv, int enable) 40462306a36Sopenharmony_ci{ 40562306a36Sopenharmony_ci struct platform_device *pdev = usbhs_priv_to_pdev(priv); 40662306a36Sopenharmony_ci struct device *dev = usbhs_priv_to_dev(priv); 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_ci if (enable) { 40962306a36Sopenharmony_ci /* enable PM */ 41062306a36Sopenharmony_ci pm_runtime_get_sync(dev); 41162306a36Sopenharmony_ci 41262306a36Sopenharmony_ci /* enable clks */ 41362306a36Sopenharmony_ci if (usbhsc_clk_prepare_enable(priv)) 41462306a36Sopenharmony_ci return; 41562306a36Sopenharmony_ci 41662306a36Sopenharmony_ci /* enable platform power */ 41762306a36Sopenharmony_ci usbhs_platform_call(priv, power_ctrl, pdev, priv->base, enable); 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_ci /* USB on */ 42062306a36Sopenharmony_ci usbhs_sys_clock_ctrl(priv, enable); 42162306a36Sopenharmony_ci } else { 42262306a36Sopenharmony_ci /* USB off */ 42362306a36Sopenharmony_ci usbhs_sys_clock_ctrl(priv, enable); 42462306a36Sopenharmony_ci 42562306a36Sopenharmony_ci /* disable platform power */ 42662306a36Sopenharmony_ci usbhs_platform_call(priv, power_ctrl, pdev, priv->base, enable); 42762306a36Sopenharmony_ci 42862306a36Sopenharmony_ci /* disable clks */ 42962306a36Sopenharmony_ci usbhsc_clk_disable_unprepare(priv); 43062306a36Sopenharmony_ci 43162306a36Sopenharmony_ci /* disable PM */ 43262306a36Sopenharmony_ci pm_runtime_put_sync(dev); 43362306a36Sopenharmony_ci } 43462306a36Sopenharmony_ci} 43562306a36Sopenharmony_ci 43662306a36Sopenharmony_ci/* 43762306a36Sopenharmony_ci * hotplug 43862306a36Sopenharmony_ci */ 43962306a36Sopenharmony_cistatic void usbhsc_hotplug(struct usbhs_priv *priv) 44062306a36Sopenharmony_ci{ 44162306a36Sopenharmony_ci struct platform_device *pdev = usbhs_priv_to_pdev(priv); 44262306a36Sopenharmony_ci struct usbhs_mod *mod = usbhs_mod_get_current(priv); 44362306a36Sopenharmony_ci int id; 44462306a36Sopenharmony_ci int enable; 44562306a36Sopenharmony_ci int cable; 44662306a36Sopenharmony_ci int ret; 44762306a36Sopenharmony_ci 44862306a36Sopenharmony_ci /* 44962306a36Sopenharmony_ci * get vbus status from platform 45062306a36Sopenharmony_ci */ 45162306a36Sopenharmony_ci enable = usbhs_mod_info_call(priv, get_vbus, pdev); 45262306a36Sopenharmony_ci 45362306a36Sopenharmony_ci /* 45462306a36Sopenharmony_ci * get id from platform 45562306a36Sopenharmony_ci */ 45662306a36Sopenharmony_ci id = usbhs_platform_call(priv, get_id, pdev); 45762306a36Sopenharmony_ci 45862306a36Sopenharmony_ci if (enable && !mod) { 45962306a36Sopenharmony_ci if (priv->edev) { 46062306a36Sopenharmony_ci cable = extcon_get_state(priv->edev, EXTCON_USB_HOST); 46162306a36Sopenharmony_ci if ((cable > 0 && id != USBHS_HOST) || 46262306a36Sopenharmony_ci (!cable && id != USBHS_GADGET)) { 46362306a36Sopenharmony_ci dev_info(&pdev->dev, 46462306a36Sopenharmony_ci "USB cable plugged in doesn't match the selected role!\n"); 46562306a36Sopenharmony_ci return; 46662306a36Sopenharmony_ci } 46762306a36Sopenharmony_ci } 46862306a36Sopenharmony_ci 46962306a36Sopenharmony_ci ret = usbhs_mod_change(priv, id); 47062306a36Sopenharmony_ci if (ret < 0) 47162306a36Sopenharmony_ci return; 47262306a36Sopenharmony_ci 47362306a36Sopenharmony_ci dev_dbg(&pdev->dev, "%s enable\n", __func__); 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_ci /* power on */ 47662306a36Sopenharmony_ci if (usbhs_get_dparam(priv, runtime_pwctrl)) 47762306a36Sopenharmony_ci usbhsc_power_ctrl(priv, enable); 47862306a36Sopenharmony_ci 47962306a36Sopenharmony_ci /* bus init */ 48062306a36Sopenharmony_ci usbhsc_set_buswait(priv); 48162306a36Sopenharmony_ci usbhsc_bus_init(priv); 48262306a36Sopenharmony_ci 48362306a36Sopenharmony_ci /* module start */ 48462306a36Sopenharmony_ci usbhs_mod_call(priv, start, priv); 48562306a36Sopenharmony_ci 48662306a36Sopenharmony_ci } else if (!enable && mod) { 48762306a36Sopenharmony_ci dev_dbg(&pdev->dev, "%s disable\n", __func__); 48862306a36Sopenharmony_ci 48962306a36Sopenharmony_ci /* module stop */ 49062306a36Sopenharmony_ci usbhs_mod_call(priv, stop, priv); 49162306a36Sopenharmony_ci 49262306a36Sopenharmony_ci /* bus init */ 49362306a36Sopenharmony_ci usbhsc_bus_init(priv); 49462306a36Sopenharmony_ci 49562306a36Sopenharmony_ci /* power off */ 49662306a36Sopenharmony_ci if (usbhs_get_dparam(priv, runtime_pwctrl)) 49762306a36Sopenharmony_ci usbhsc_power_ctrl(priv, enable); 49862306a36Sopenharmony_ci 49962306a36Sopenharmony_ci usbhs_mod_change(priv, -1); 50062306a36Sopenharmony_ci 50162306a36Sopenharmony_ci /* reset phy for next connection */ 50262306a36Sopenharmony_ci usbhs_platform_call(priv, phy_reset, pdev); 50362306a36Sopenharmony_ci } 50462306a36Sopenharmony_ci} 50562306a36Sopenharmony_ci 50662306a36Sopenharmony_ci/* 50762306a36Sopenharmony_ci * notify hotplug 50862306a36Sopenharmony_ci */ 50962306a36Sopenharmony_cistatic void usbhsc_notify_hotplug(struct work_struct *work) 51062306a36Sopenharmony_ci{ 51162306a36Sopenharmony_ci struct usbhs_priv *priv = container_of(work, 51262306a36Sopenharmony_ci struct usbhs_priv, 51362306a36Sopenharmony_ci notify_hotplug_work.work); 51462306a36Sopenharmony_ci usbhsc_hotplug(priv); 51562306a36Sopenharmony_ci} 51662306a36Sopenharmony_ci 51762306a36Sopenharmony_ciint usbhsc_schedule_notify_hotplug(struct platform_device *pdev) 51862306a36Sopenharmony_ci{ 51962306a36Sopenharmony_ci struct usbhs_priv *priv = usbhs_pdev_to_priv(pdev); 52062306a36Sopenharmony_ci int delay = usbhs_get_dparam(priv, detection_delay); 52162306a36Sopenharmony_ci 52262306a36Sopenharmony_ci /* 52362306a36Sopenharmony_ci * This functions will be called in interrupt. 52462306a36Sopenharmony_ci * To make sure safety context, 52562306a36Sopenharmony_ci * use workqueue for usbhs_notify_hotplug 52662306a36Sopenharmony_ci */ 52762306a36Sopenharmony_ci schedule_delayed_work(&priv->notify_hotplug_work, 52862306a36Sopenharmony_ci msecs_to_jiffies(delay)); 52962306a36Sopenharmony_ci return 0; 53062306a36Sopenharmony_ci} 53162306a36Sopenharmony_ci 53262306a36Sopenharmony_ci/* 53362306a36Sopenharmony_ci * platform functions 53462306a36Sopenharmony_ci */ 53562306a36Sopenharmony_cistatic const struct of_device_id usbhs_of_match[] = { 53662306a36Sopenharmony_ci { 53762306a36Sopenharmony_ci .compatible = "renesas,usbhs-r8a774c0", 53862306a36Sopenharmony_ci .data = &usbhs_rcar_gen3_with_pll_plat_info, 53962306a36Sopenharmony_ci }, 54062306a36Sopenharmony_ci { 54162306a36Sopenharmony_ci .compatible = "renesas,usbhs-r8a7790", 54262306a36Sopenharmony_ci .data = &usbhs_rcar_gen2_plat_info, 54362306a36Sopenharmony_ci }, 54462306a36Sopenharmony_ci { 54562306a36Sopenharmony_ci .compatible = "renesas,usbhs-r8a7791", 54662306a36Sopenharmony_ci .data = &usbhs_rcar_gen2_plat_info, 54762306a36Sopenharmony_ci }, 54862306a36Sopenharmony_ci { 54962306a36Sopenharmony_ci .compatible = "renesas,usbhs-r8a7794", 55062306a36Sopenharmony_ci .data = &usbhs_rcar_gen2_plat_info, 55162306a36Sopenharmony_ci }, 55262306a36Sopenharmony_ci { 55362306a36Sopenharmony_ci .compatible = "renesas,usbhs-r8a7795", 55462306a36Sopenharmony_ci .data = &usbhs_rcar_gen3_plat_info, 55562306a36Sopenharmony_ci }, 55662306a36Sopenharmony_ci { 55762306a36Sopenharmony_ci .compatible = "renesas,usbhs-r8a7796", 55862306a36Sopenharmony_ci .data = &usbhs_rcar_gen3_plat_info, 55962306a36Sopenharmony_ci }, 56062306a36Sopenharmony_ci { 56162306a36Sopenharmony_ci .compatible = "renesas,usbhs-r8a77990", 56262306a36Sopenharmony_ci .data = &usbhs_rcar_gen3_with_pll_plat_info, 56362306a36Sopenharmony_ci }, 56462306a36Sopenharmony_ci { 56562306a36Sopenharmony_ci .compatible = "renesas,usbhs-r8a77995", 56662306a36Sopenharmony_ci .data = &usbhs_rcar_gen3_with_pll_plat_info, 56762306a36Sopenharmony_ci }, 56862306a36Sopenharmony_ci { 56962306a36Sopenharmony_ci .compatible = "renesas,rcar-gen2-usbhs", 57062306a36Sopenharmony_ci .data = &usbhs_rcar_gen2_plat_info, 57162306a36Sopenharmony_ci }, 57262306a36Sopenharmony_ci { 57362306a36Sopenharmony_ci .compatible = "renesas,rcar-gen3-usbhs", 57462306a36Sopenharmony_ci .data = &usbhs_rcar_gen3_plat_info, 57562306a36Sopenharmony_ci }, 57662306a36Sopenharmony_ci { 57762306a36Sopenharmony_ci .compatible = "renesas,rza1-usbhs", 57862306a36Sopenharmony_ci .data = &usbhs_rza1_plat_info, 57962306a36Sopenharmony_ci }, 58062306a36Sopenharmony_ci { 58162306a36Sopenharmony_ci .compatible = "renesas,rza2-usbhs", 58262306a36Sopenharmony_ci .data = &usbhs_rza2_plat_info, 58362306a36Sopenharmony_ci }, 58462306a36Sopenharmony_ci { }, 58562306a36Sopenharmony_ci}; 58662306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, usbhs_of_match); 58762306a36Sopenharmony_ci 58862306a36Sopenharmony_cistatic int usbhs_probe(struct platform_device *pdev) 58962306a36Sopenharmony_ci{ 59062306a36Sopenharmony_ci const struct renesas_usbhs_platform_info *info; 59162306a36Sopenharmony_ci struct usbhs_priv *priv; 59262306a36Sopenharmony_ci struct device *dev = &pdev->dev; 59362306a36Sopenharmony_ci struct gpio_desc *gpiod; 59462306a36Sopenharmony_ci int ret; 59562306a36Sopenharmony_ci u32 tmp; 59662306a36Sopenharmony_ci int irq; 59762306a36Sopenharmony_ci 59862306a36Sopenharmony_ci /* check device node */ 59962306a36Sopenharmony_ci if (dev_of_node(dev)) 60062306a36Sopenharmony_ci info = of_device_get_match_data(dev); 60162306a36Sopenharmony_ci else 60262306a36Sopenharmony_ci info = renesas_usbhs_get_info(pdev); 60362306a36Sopenharmony_ci 60462306a36Sopenharmony_ci /* check platform information */ 60562306a36Sopenharmony_ci if (!info) { 60662306a36Sopenharmony_ci dev_err(dev, "no platform information\n"); 60762306a36Sopenharmony_ci return -EINVAL; 60862306a36Sopenharmony_ci } 60962306a36Sopenharmony_ci 61062306a36Sopenharmony_ci /* platform data */ 61162306a36Sopenharmony_ci irq = platform_get_irq(pdev, 0); 61262306a36Sopenharmony_ci if (irq < 0) 61362306a36Sopenharmony_ci return irq; 61462306a36Sopenharmony_ci 61562306a36Sopenharmony_ci /* usb private data */ 61662306a36Sopenharmony_ci priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); 61762306a36Sopenharmony_ci if (!priv) 61862306a36Sopenharmony_ci return -ENOMEM; 61962306a36Sopenharmony_ci 62062306a36Sopenharmony_ci priv->base = devm_platform_ioremap_resource(pdev, 0); 62162306a36Sopenharmony_ci if (IS_ERR(priv->base)) 62262306a36Sopenharmony_ci return PTR_ERR(priv->base); 62362306a36Sopenharmony_ci 62462306a36Sopenharmony_ci if (of_property_read_bool(dev_of_node(dev), "extcon")) { 62562306a36Sopenharmony_ci priv->edev = extcon_get_edev_by_phandle(dev, 0); 62662306a36Sopenharmony_ci if (IS_ERR(priv->edev)) 62762306a36Sopenharmony_ci return PTR_ERR(priv->edev); 62862306a36Sopenharmony_ci } 62962306a36Sopenharmony_ci 63062306a36Sopenharmony_ci priv->rsts = devm_reset_control_array_get_optional_shared(dev); 63162306a36Sopenharmony_ci if (IS_ERR(priv->rsts)) 63262306a36Sopenharmony_ci return PTR_ERR(priv->rsts); 63362306a36Sopenharmony_ci 63462306a36Sopenharmony_ci /* 63562306a36Sopenharmony_ci * care platform info 63662306a36Sopenharmony_ci */ 63762306a36Sopenharmony_ci 63862306a36Sopenharmony_ci priv->dparam = info->driver_param; 63962306a36Sopenharmony_ci 64062306a36Sopenharmony_ci if (!info->platform_callback.get_id) { 64162306a36Sopenharmony_ci dev_err(dev, "no platform callbacks\n"); 64262306a36Sopenharmony_ci return -EINVAL; 64362306a36Sopenharmony_ci } 64462306a36Sopenharmony_ci priv->pfunc = &info->platform_callback; 64562306a36Sopenharmony_ci 64662306a36Sopenharmony_ci /* set default param if platform doesn't have */ 64762306a36Sopenharmony_ci if (usbhs_get_dparam(priv, has_new_pipe_configs)) { 64862306a36Sopenharmony_ci priv->dparam.pipe_configs = usbhsc_new_pipe; 64962306a36Sopenharmony_ci priv->dparam.pipe_size = ARRAY_SIZE(usbhsc_new_pipe); 65062306a36Sopenharmony_ci } else if (!priv->dparam.pipe_configs) { 65162306a36Sopenharmony_ci priv->dparam.pipe_configs = usbhsc_default_pipe; 65262306a36Sopenharmony_ci priv->dparam.pipe_size = ARRAY_SIZE(usbhsc_default_pipe); 65362306a36Sopenharmony_ci } 65462306a36Sopenharmony_ci if (!priv->dparam.pio_dma_border) 65562306a36Sopenharmony_ci priv->dparam.pio_dma_border = 64; /* 64byte */ 65662306a36Sopenharmony_ci if (!of_property_read_u32(dev_of_node(dev), "renesas,buswait", &tmp)) 65762306a36Sopenharmony_ci priv->dparam.buswait_bwait = tmp; 65862306a36Sopenharmony_ci gpiod = devm_gpiod_get_optional(dev, "renesas,enable", GPIOD_IN); 65962306a36Sopenharmony_ci if (IS_ERR(gpiod)) 66062306a36Sopenharmony_ci return PTR_ERR(gpiod); 66162306a36Sopenharmony_ci 66262306a36Sopenharmony_ci /* FIXME */ 66362306a36Sopenharmony_ci /* runtime power control ? */ 66462306a36Sopenharmony_ci if (priv->pfunc->get_vbus) 66562306a36Sopenharmony_ci usbhs_get_dparam(priv, runtime_pwctrl) = 1; 66662306a36Sopenharmony_ci 66762306a36Sopenharmony_ci /* 66862306a36Sopenharmony_ci * priv settings 66962306a36Sopenharmony_ci */ 67062306a36Sopenharmony_ci priv->irq = irq; 67162306a36Sopenharmony_ci priv->pdev = pdev; 67262306a36Sopenharmony_ci INIT_DELAYED_WORK(&priv->notify_hotplug_work, usbhsc_notify_hotplug); 67362306a36Sopenharmony_ci spin_lock_init(usbhs_priv_to_lock(priv)); 67462306a36Sopenharmony_ci 67562306a36Sopenharmony_ci /* call pipe and module init */ 67662306a36Sopenharmony_ci ret = usbhs_pipe_probe(priv); 67762306a36Sopenharmony_ci if (ret < 0) 67862306a36Sopenharmony_ci return ret; 67962306a36Sopenharmony_ci 68062306a36Sopenharmony_ci ret = usbhs_fifo_probe(priv); 68162306a36Sopenharmony_ci if (ret < 0) 68262306a36Sopenharmony_ci goto probe_end_pipe_exit; 68362306a36Sopenharmony_ci 68462306a36Sopenharmony_ci ret = usbhs_mod_probe(priv); 68562306a36Sopenharmony_ci if (ret < 0) 68662306a36Sopenharmony_ci goto probe_end_fifo_exit; 68762306a36Sopenharmony_ci 68862306a36Sopenharmony_ci /* dev_set_drvdata should be called after usbhs_mod_init */ 68962306a36Sopenharmony_ci platform_set_drvdata(pdev, priv); 69062306a36Sopenharmony_ci 69162306a36Sopenharmony_ci ret = reset_control_deassert(priv->rsts); 69262306a36Sopenharmony_ci if (ret) 69362306a36Sopenharmony_ci goto probe_fail_rst; 69462306a36Sopenharmony_ci 69562306a36Sopenharmony_ci ret = usbhsc_clk_get(dev, priv); 69662306a36Sopenharmony_ci if (ret) 69762306a36Sopenharmony_ci goto probe_fail_clks; 69862306a36Sopenharmony_ci 69962306a36Sopenharmony_ci /* 70062306a36Sopenharmony_ci * deviece reset here because 70162306a36Sopenharmony_ci * USB device might be used in boot loader. 70262306a36Sopenharmony_ci */ 70362306a36Sopenharmony_ci usbhs_sys_clock_ctrl(priv, 0); 70462306a36Sopenharmony_ci 70562306a36Sopenharmony_ci /* check GPIO determining if USB function should be enabled */ 70662306a36Sopenharmony_ci if (gpiod) { 70762306a36Sopenharmony_ci ret = !gpiod_get_value(gpiod); 70862306a36Sopenharmony_ci if (ret) { 70962306a36Sopenharmony_ci dev_warn(dev, "USB function not selected (GPIO)\n"); 71062306a36Sopenharmony_ci ret = -ENOTSUPP; 71162306a36Sopenharmony_ci goto probe_end_mod_exit; 71262306a36Sopenharmony_ci } 71362306a36Sopenharmony_ci } 71462306a36Sopenharmony_ci 71562306a36Sopenharmony_ci /* 71662306a36Sopenharmony_ci * platform call 71762306a36Sopenharmony_ci * 71862306a36Sopenharmony_ci * USB phy setup might depend on CPU/Board. 71962306a36Sopenharmony_ci * If platform has its callback functions, 72062306a36Sopenharmony_ci * call it here. 72162306a36Sopenharmony_ci */ 72262306a36Sopenharmony_ci ret = usbhs_platform_call(priv, hardware_init, pdev); 72362306a36Sopenharmony_ci if (ret < 0) { 72462306a36Sopenharmony_ci dev_err(dev, "platform init failed.\n"); 72562306a36Sopenharmony_ci goto probe_end_mod_exit; 72662306a36Sopenharmony_ci } 72762306a36Sopenharmony_ci 72862306a36Sopenharmony_ci /* reset phy for connection */ 72962306a36Sopenharmony_ci usbhs_platform_call(priv, phy_reset, pdev); 73062306a36Sopenharmony_ci 73162306a36Sopenharmony_ci /* power control */ 73262306a36Sopenharmony_ci pm_runtime_enable(dev); 73362306a36Sopenharmony_ci if (!usbhs_get_dparam(priv, runtime_pwctrl)) { 73462306a36Sopenharmony_ci usbhsc_power_ctrl(priv, 1); 73562306a36Sopenharmony_ci usbhs_mod_autonomy_mode(priv); 73662306a36Sopenharmony_ci } else { 73762306a36Sopenharmony_ci usbhs_mod_non_autonomy_mode(priv); 73862306a36Sopenharmony_ci } 73962306a36Sopenharmony_ci 74062306a36Sopenharmony_ci /* 74162306a36Sopenharmony_ci * manual call notify_hotplug for cold plug 74262306a36Sopenharmony_ci */ 74362306a36Sopenharmony_ci usbhsc_schedule_notify_hotplug(pdev); 74462306a36Sopenharmony_ci 74562306a36Sopenharmony_ci dev_info(dev, "probed\n"); 74662306a36Sopenharmony_ci 74762306a36Sopenharmony_ci return ret; 74862306a36Sopenharmony_ci 74962306a36Sopenharmony_ciprobe_end_mod_exit: 75062306a36Sopenharmony_ci usbhsc_clk_put(priv); 75162306a36Sopenharmony_ciprobe_fail_clks: 75262306a36Sopenharmony_ci reset_control_assert(priv->rsts); 75362306a36Sopenharmony_ciprobe_fail_rst: 75462306a36Sopenharmony_ci usbhs_mod_remove(priv); 75562306a36Sopenharmony_ciprobe_end_fifo_exit: 75662306a36Sopenharmony_ci usbhs_fifo_remove(priv); 75762306a36Sopenharmony_ciprobe_end_pipe_exit: 75862306a36Sopenharmony_ci usbhs_pipe_remove(priv); 75962306a36Sopenharmony_ci 76062306a36Sopenharmony_ci dev_info(dev, "probe failed (%d)\n", ret); 76162306a36Sopenharmony_ci 76262306a36Sopenharmony_ci return ret; 76362306a36Sopenharmony_ci} 76462306a36Sopenharmony_ci 76562306a36Sopenharmony_cistatic void usbhs_remove(struct platform_device *pdev) 76662306a36Sopenharmony_ci{ 76762306a36Sopenharmony_ci struct usbhs_priv *priv = usbhs_pdev_to_priv(pdev); 76862306a36Sopenharmony_ci 76962306a36Sopenharmony_ci dev_dbg(&pdev->dev, "usb remove\n"); 77062306a36Sopenharmony_ci 77162306a36Sopenharmony_ci /* power off */ 77262306a36Sopenharmony_ci if (!usbhs_get_dparam(priv, runtime_pwctrl)) 77362306a36Sopenharmony_ci usbhsc_power_ctrl(priv, 0); 77462306a36Sopenharmony_ci 77562306a36Sopenharmony_ci pm_runtime_disable(&pdev->dev); 77662306a36Sopenharmony_ci 77762306a36Sopenharmony_ci usbhs_platform_call(priv, hardware_exit, pdev); 77862306a36Sopenharmony_ci usbhsc_clk_put(priv); 77962306a36Sopenharmony_ci reset_control_assert(priv->rsts); 78062306a36Sopenharmony_ci usbhs_mod_remove(priv); 78162306a36Sopenharmony_ci usbhs_fifo_remove(priv); 78262306a36Sopenharmony_ci usbhs_pipe_remove(priv); 78362306a36Sopenharmony_ci} 78462306a36Sopenharmony_ci 78562306a36Sopenharmony_cistatic __maybe_unused int usbhsc_suspend(struct device *dev) 78662306a36Sopenharmony_ci{ 78762306a36Sopenharmony_ci struct usbhs_priv *priv = dev_get_drvdata(dev); 78862306a36Sopenharmony_ci struct usbhs_mod *mod = usbhs_mod_get_current(priv); 78962306a36Sopenharmony_ci 79062306a36Sopenharmony_ci if (mod) { 79162306a36Sopenharmony_ci usbhs_mod_call(priv, stop, priv); 79262306a36Sopenharmony_ci usbhs_mod_change(priv, -1); 79362306a36Sopenharmony_ci } 79462306a36Sopenharmony_ci 79562306a36Sopenharmony_ci if (mod || !usbhs_get_dparam(priv, runtime_pwctrl)) 79662306a36Sopenharmony_ci usbhsc_power_ctrl(priv, 0); 79762306a36Sopenharmony_ci 79862306a36Sopenharmony_ci return 0; 79962306a36Sopenharmony_ci} 80062306a36Sopenharmony_ci 80162306a36Sopenharmony_cistatic __maybe_unused int usbhsc_resume(struct device *dev) 80262306a36Sopenharmony_ci{ 80362306a36Sopenharmony_ci struct usbhs_priv *priv = dev_get_drvdata(dev); 80462306a36Sopenharmony_ci struct platform_device *pdev = usbhs_priv_to_pdev(priv); 80562306a36Sopenharmony_ci 80662306a36Sopenharmony_ci if (!usbhs_get_dparam(priv, runtime_pwctrl)) { 80762306a36Sopenharmony_ci usbhsc_power_ctrl(priv, 1); 80862306a36Sopenharmony_ci usbhs_mod_autonomy_mode(priv); 80962306a36Sopenharmony_ci } 81062306a36Sopenharmony_ci 81162306a36Sopenharmony_ci usbhs_platform_call(priv, phy_reset, pdev); 81262306a36Sopenharmony_ci 81362306a36Sopenharmony_ci usbhsc_schedule_notify_hotplug(pdev); 81462306a36Sopenharmony_ci 81562306a36Sopenharmony_ci return 0; 81662306a36Sopenharmony_ci} 81762306a36Sopenharmony_ci 81862306a36Sopenharmony_cistatic SIMPLE_DEV_PM_OPS(usbhsc_pm_ops, usbhsc_suspend, usbhsc_resume); 81962306a36Sopenharmony_ci 82062306a36Sopenharmony_cistatic struct platform_driver renesas_usbhs_driver = { 82162306a36Sopenharmony_ci .driver = { 82262306a36Sopenharmony_ci .name = "renesas_usbhs", 82362306a36Sopenharmony_ci .pm = &usbhsc_pm_ops, 82462306a36Sopenharmony_ci .of_match_table = usbhs_of_match, 82562306a36Sopenharmony_ci }, 82662306a36Sopenharmony_ci .probe = usbhs_probe, 82762306a36Sopenharmony_ci .remove_new = usbhs_remove, 82862306a36Sopenharmony_ci}; 82962306a36Sopenharmony_ci 83062306a36Sopenharmony_cimodule_platform_driver(renesas_usbhs_driver); 83162306a36Sopenharmony_ci 83262306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 83362306a36Sopenharmony_ciMODULE_DESCRIPTION("Renesas USB driver"); 83462306a36Sopenharmony_ciMODULE_AUTHOR("Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>"); 835