18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Renesas R-Car Gen3 for USB2.0 PHY driver 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2015-2017 Renesas Electronics Corporation 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * This is based on the phy-rcar-gen2 driver: 88c2ecf20Sopenharmony_ci * Copyright (C) 2014 Renesas Solutions Corp. 98c2ecf20Sopenharmony_ci * Copyright (C) 2014 Cogent Embedded, Inc. 108c2ecf20Sopenharmony_ci */ 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci#include <linux/extcon-provider.h> 138c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 148c2ecf20Sopenharmony_ci#include <linux/io.h> 158c2ecf20Sopenharmony_ci#include <linux/module.h> 168c2ecf20Sopenharmony_ci#include <linux/mutex.h> 178c2ecf20Sopenharmony_ci#include <linux/of.h> 188c2ecf20Sopenharmony_ci#include <linux/of_address.h> 198c2ecf20Sopenharmony_ci#include <linux/of_device.h> 208c2ecf20Sopenharmony_ci#include <linux/phy/phy.h> 218c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 228c2ecf20Sopenharmony_ci#include <linux/pm_runtime.h> 238c2ecf20Sopenharmony_ci#include <linux/regulator/consumer.h> 248c2ecf20Sopenharmony_ci#include <linux/string.h> 258c2ecf20Sopenharmony_ci#include <linux/usb/of.h> 268c2ecf20Sopenharmony_ci#include <linux/workqueue.h> 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci/******* USB2.0 Host registers (original offset is +0x200) *******/ 298c2ecf20Sopenharmony_ci#define USB2_INT_ENABLE 0x000 308c2ecf20Sopenharmony_ci#define USB2_USBCTR 0x00c 318c2ecf20Sopenharmony_ci#define USB2_SPD_RSM_TIMSET 0x10c 328c2ecf20Sopenharmony_ci#define USB2_OC_TIMSET 0x110 338c2ecf20Sopenharmony_ci#define USB2_COMMCTRL 0x600 348c2ecf20Sopenharmony_ci#define USB2_OBINTSTA 0x604 358c2ecf20Sopenharmony_ci#define USB2_OBINTEN 0x608 368c2ecf20Sopenharmony_ci#define USB2_VBCTRL 0x60c 378c2ecf20Sopenharmony_ci#define USB2_LINECTRL1 0x610 388c2ecf20Sopenharmony_ci#define USB2_ADPCTRL 0x630 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_ci/* INT_ENABLE */ 418c2ecf20Sopenharmony_ci#define USB2_INT_ENABLE_UCOM_INTEN BIT(3) 428c2ecf20Sopenharmony_ci#define USB2_INT_ENABLE_USBH_INTB_EN BIT(2) /* For EHCI */ 438c2ecf20Sopenharmony_ci#define USB2_INT_ENABLE_USBH_INTA_EN BIT(1) /* For OHCI */ 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci/* USBCTR */ 468c2ecf20Sopenharmony_ci#define USB2_USBCTR_DIRPD BIT(2) 478c2ecf20Sopenharmony_ci#define USB2_USBCTR_PLL_RST BIT(1) 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_ci/* SPD_RSM_TIMSET */ 508c2ecf20Sopenharmony_ci#define USB2_SPD_RSM_TIMSET_INIT 0x014e029b 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci/* OC_TIMSET */ 538c2ecf20Sopenharmony_ci#define USB2_OC_TIMSET_INIT 0x000209ab 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci/* COMMCTRL */ 568c2ecf20Sopenharmony_ci#define USB2_COMMCTRL_OTG_PERI BIT(31) /* 1 = Peripheral mode */ 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci/* OBINTSTA and OBINTEN */ 598c2ecf20Sopenharmony_ci#define USB2_OBINT_SESSVLDCHG BIT(12) 608c2ecf20Sopenharmony_ci#define USB2_OBINT_IDDIGCHG BIT(11) 618c2ecf20Sopenharmony_ci#define USB2_OBINT_BITS (USB2_OBINT_SESSVLDCHG | \ 628c2ecf20Sopenharmony_ci USB2_OBINT_IDDIGCHG) 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci/* VBCTRL */ 658c2ecf20Sopenharmony_ci#define USB2_VBCTRL_OCCLREN BIT(16) 668c2ecf20Sopenharmony_ci#define USB2_VBCTRL_DRVVBUSSEL BIT(8) 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci/* LINECTRL1 */ 698c2ecf20Sopenharmony_ci#define USB2_LINECTRL1_DPRPD_EN BIT(19) 708c2ecf20Sopenharmony_ci#define USB2_LINECTRL1_DP_RPD BIT(18) 718c2ecf20Sopenharmony_ci#define USB2_LINECTRL1_DMRPD_EN BIT(17) 728c2ecf20Sopenharmony_ci#define USB2_LINECTRL1_DM_RPD BIT(16) 738c2ecf20Sopenharmony_ci#define USB2_LINECTRL1_OPMODE_NODRV BIT(6) 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci/* ADPCTRL */ 768c2ecf20Sopenharmony_ci#define USB2_ADPCTRL_OTGSESSVLD BIT(20) 778c2ecf20Sopenharmony_ci#define USB2_ADPCTRL_IDDIG BIT(19) 788c2ecf20Sopenharmony_ci#define USB2_ADPCTRL_IDPULLUP BIT(5) /* 1 = ID sampling is enabled */ 798c2ecf20Sopenharmony_ci#define USB2_ADPCTRL_DRVVBUS BIT(4) 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci#define NUM_OF_PHYS 4 828c2ecf20Sopenharmony_cienum rcar_gen3_phy_index { 838c2ecf20Sopenharmony_ci PHY_INDEX_BOTH_HC, 848c2ecf20Sopenharmony_ci PHY_INDEX_OHCI, 858c2ecf20Sopenharmony_ci PHY_INDEX_EHCI, 868c2ecf20Sopenharmony_ci PHY_INDEX_HSUSB 878c2ecf20Sopenharmony_ci}; 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_cistatic const u32 rcar_gen3_int_enable[NUM_OF_PHYS] = { 908c2ecf20Sopenharmony_ci USB2_INT_ENABLE_USBH_INTB_EN | USB2_INT_ENABLE_USBH_INTA_EN, 918c2ecf20Sopenharmony_ci USB2_INT_ENABLE_USBH_INTA_EN, 928c2ecf20Sopenharmony_ci USB2_INT_ENABLE_USBH_INTB_EN, 938c2ecf20Sopenharmony_ci 0 948c2ecf20Sopenharmony_ci}; 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_cistruct rcar_gen3_phy { 978c2ecf20Sopenharmony_ci struct phy *phy; 988c2ecf20Sopenharmony_ci struct rcar_gen3_chan *ch; 998c2ecf20Sopenharmony_ci u32 int_enable_bits; 1008c2ecf20Sopenharmony_ci bool initialized; 1018c2ecf20Sopenharmony_ci bool otg_initialized; 1028c2ecf20Sopenharmony_ci bool powered; 1038c2ecf20Sopenharmony_ci}; 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_cistruct rcar_gen3_chan { 1068c2ecf20Sopenharmony_ci void __iomem *base; 1078c2ecf20Sopenharmony_ci struct device *dev; /* platform_device's device */ 1088c2ecf20Sopenharmony_ci struct extcon_dev *extcon; 1098c2ecf20Sopenharmony_ci struct rcar_gen3_phy rphys[NUM_OF_PHYS]; 1108c2ecf20Sopenharmony_ci struct regulator *vbus; 1118c2ecf20Sopenharmony_ci struct work_struct work; 1128c2ecf20Sopenharmony_ci struct mutex lock; /* protects rphys[...].powered */ 1138c2ecf20Sopenharmony_ci enum usb_dr_mode dr_mode; 1148c2ecf20Sopenharmony_ci int irq; 1158c2ecf20Sopenharmony_ci bool extcon_host; 1168c2ecf20Sopenharmony_ci bool is_otg_channel; 1178c2ecf20Sopenharmony_ci bool uses_otg_pins; 1188c2ecf20Sopenharmony_ci}; 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci/* 1218c2ecf20Sopenharmony_ci * Combination about is_otg_channel and uses_otg_pins: 1228c2ecf20Sopenharmony_ci * 1238c2ecf20Sopenharmony_ci * Parameters || Behaviors 1248c2ecf20Sopenharmony_ci * is_otg_channel | uses_otg_pins || irqs | role sysfs 1258c2ecf20Sopenharmony_ci * ---------------------+---------------++--------------+------------ 1268c2ecf20Sopenharmony_ci * true | true || enabled | enabled 1278c2ecf20Sopenharmony_ci * true | false || disabled | enabled 1288c2ecf20Sopenharmony_ci * false | any || disabled | disabled 1298c2ecf20Sopenharmony_ci */ 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_cistatic void rcar_gen3_phy_usb2_work(struct work_struct *work) 1328c2ecf20Sopenharmony_ci{ 1338c2ecf20Sopenharmony_ci struct rcar_gen3_chan *ch = container_of(work, struct rcar_gen3_chan, 1348c2ecf20Sopenharmony_ci work); 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci if (ch->extcon_host) { 1378c2ecf20Sopenharmony_ci extcon_set_state_sync(ch->extcon, EXTCON_USB_HOST, true); 1388c2ecf20Sopenharmony_ci extcon_set_state_sync(ch->extcon, EXTCON_USB, false); 1398c2ecf20Sopenharmony_ci } else { 1408c2ecf20Sopenharmony_ci extcon_set_state_sync(ch->extcon, EXTCON_USB_HOST, false); 1418c2ecf20Sopenharmony_ci extcon_set_state_sync(ch->extcon, EXTCON_USB, true); 1428c2ecf20Sopenharmony_ci } 1438c2ecf20Sopenharmony_ci} 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_cistatic void rcar_gen3_set_host_mode(struct rcar_gen3_chan *ch, int host) 1468c2ecf20Sopenharmony_ci{ 1478c2ecf20Sopenharmony_ci void __iomem *usb2_base = ch->base; 1488c2ecf20Sopenharmony_ci u32 val = readl(usb2_base + USB2_COMMCTRL); 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci dev_vdbg(ch->dev, "%s: %08x, %d\n", __func__, val, host); 1518c2ecf20Sopenharmony_ci if (host) 1528c2ecf20Sopenharmony_ci val &= ~USB2_COMMCTRL_OTG_PERI; 1538c2ecf20Sopenharmony_ci else 1548c2ecf20Sopenharmony_ci val |= USB2_COMMCTRL_OTG_PERI; 1558c2ecf20Sopenharmony_ci writel(val, usb2_base + USB2_COMMCTRL); 1568c2ecf20Sopenharmony_ci} 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_cistatic void rcar_gen3_set_linectrl(struct rcar_gen3_chan *ch, int dp, int dm) 1598c2ecf20Sopenharmony_ci{ 1608c2ecf20Sopenharmony_ci void __iomem *usb2_base = ch->base; 1618c2ecf20Sopenharmony_ci u32 val = readl(usb2_base + USB2_LINECTRL1); 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci dev_vdbg(ch->dev, "%s: %08x, %d, %d\n", __func__, val, dp, dm); 1648c2ecf20Sopenharmony_ci val &= ~(USB2_LINECTRL1_DP_RPD | USB2_LINECTRL1_DM_RPD); 1658c2ecf20Sopenharmony_ci if (dp) 1668c2ecf20Sopenharmony_ci val |= USB2_LINECTRL1_DP_RPD; 1678c2ecf20Sopenharmony_ci if (dm) 1688c2ecf20Sopenharmony_ci val |= USB2_LINECTRL1_DM_RPD; 1698c2ecf20Sopenharmony_ci writel(val, usb2_base + USB2_LINECTRL1); 1708c2ecf20Sopenharmony_ci} 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_cistatic void rcar_gen3_enable_vbus_ctrl(struct rcar_gen3_chan *ch, int vbus) 1738c2ecf20Sopenharmony_ci{ 1748c2ecf20Sopenharmony_ci void __iomem *usb2_base = ch->base; 1758c2ecf20Sopenharmony_ci u32 val = readl(usb2_base + USB2_ADPCTRL); 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci dev_vdbg(ch->dev, "%s: %08x, %d\n", __func__, val, vbus); 1788c2ecf20Sopenharmony_ci if (vbus) 1798c2ecf20Sopenharmony_ci val |= USB2_ADPCTRL_DRVVBUS; 1808c2ecf20Sopenharmony_ci else 1818c2ecf20Sopenharmony_ci val &= ~USB2_ADPCTRL_DRVVBUS; 1828c2ecf20Sopenharmony_ci writel(val, usb2_base + USB2_ADPCTRL); 1838c2ecf20Sopenharmony_ci} 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_cistatic void rcar_gen3_control_otg_irq(struct rcar_gen3_chan *ch, int enable) 1868c2ecf20Sopenharmony_ci{ 1878c2ecf20Sopenharmony_ci void __iomem *usb2_base = ch->base; 1888c2ecf20Sopenharmony_ci u32 val = readl(usb2_base + USB2_OBINTEN); 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_ci if (ch->uses_otg_pins && enable) 1918c2ecf20Sopenharmony_ci val |= USB2_OBINT_BITS; 1928c2ecf20Sopenharmony_ci else 1938c2ecf20Sopenharmony_ci val &= ~USB2_OBINT_BITS; 1948c2ecf20Sopenharmony_ci writel(val, usb2_base + USB2_OBINTEN); 1958c2ecf20Sopenharmony_ci} 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_cistatic void rcar_gen3_init_for_host(struct rcar_gen3_chan *ch) 1988c2ecf20Sopenharmony_ci{ 1998c2ecf20Sopenharmony_ci rcar_gen3_set_linectrl(ch, 1, 1); 2008c2ecf20Sopenharmony_ci rcar_gen3_set_host_mode(ch, 1); 2018c2ecf20Sopenharmony_ci rcar_gen3_enable_vbus_ctrl(ch, 1); 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ci ch->extcon_host = true; 2048c2ecf20Sopenharmony_ci schedule_work(&ch->work); 2058c2ecf20Sopenharmony_ci} 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_cistatic void rcar_gen3_init_for_peri(struct rcar_gen3_chan *ch) 2088c2ecf20Sopenharmony_ci{ 2098c2ecf20Sopenharmony_ci rcar_gen3_set_linectrl(ch, 0, 1); 2108c2ecf20Sopenharmony_ci rcar_gen3_set_host_mode(ch, 0); 2118c2ecf20Sopenharmony_ci rcar_gen3_enable_vbus_ctrl(ch, 0); 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_ci ch->extcon_host = false; 2148c2ecf20Sopenharmony_ci schedule_work(&ch->work); 2158c2ecf20Sopenharmony_ci} 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_cistatic void rcar_gen3_init_for_b_host(struct rcar_gen3_chan *ch) 2188c2ecf20Sopenharmony_ci{ 2198c2ecf20Sopenharmony_ci void __iomem *usb2_base = ch->base; 2208c2ecf20Sopenharmony_ci u32 val; 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_ci val = readl(usb2_base + USB2_LINECTRL1); 2238c2ecf20Sopenharmony_ci writel(val | USB2_LINECTRL1_OPMODE_NODRV, usb2_base + USB2_LINECTRL1); 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_ci rcar_gen3_set_linectrl(ch, 1, 1); 2268c2ecf20Sopenharmony_ci rcar_gen3_set_host_mode(ch, 1); 2278c2ecf20Sopenharmony_ci rcar_gen3_enable_vbus_ctrl(ch, 0); 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_ci val = readl(usb2_base + USB2_LINECTRL1); 2308c2ecf20Sopenharmony_ci writel(val & ~USB2_LINECTRL1_OPMODE_NODRV, usb2_base + USB2_LINECTRL1); 2318c2ecf20Sopenharmony_ci} 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_cistatic void rcar_gen3_init_for_a_peri(struct rcar_gen3_chan *ch) 2348c2ecf20Sopenharmony_ci{ 2358c2ecf20Sopenharmony_ci rcar_gen3_set_linectrl(ch, 0, 1); 2368c2ecf20Sopenharmony_ci rcar_gen3_set_host_mode(ch, 0); 2378c2ecf20Sopenharmony_ci rcar_gen3_enable_vbus_ctrl(ch, 1); 2388c2ecf20Sopenharmony_ci} 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_cistatic void rcar_gen3_init_from_a_peri_to_a_host(struct rcar_gen3_chan *ch) 2418c2ecf20Sopenharmony_ci{ 2428c2ecf20Sopenharmony_ci rcar_gen3_control_otg_irq(ch, 0); 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_ci rcar_gen3_enable_vbus_ctrl(ch, 1); 2458c2ecf20Sopenharmony_ci rcar_gen3_init_for_host(ch); 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_ci rcar_gen3_control_otg_irq(ch, 1); 2488c2ecf20Sopenharmony_ci} 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_cistatic bool rcar_gen3_check_id(struct rcar_gen3_chan *ch) 2518c2ecf20Sopenharmony_ci{ 2528c2ecf20Sopenharmony_ci if (!ch->uses_otg_pins) 2538c2ecf20Sopenharmony_ci return (ch->dr_mode == USB_DR_MODE_HOST) ? false : true; 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_ci return !!(readl(ch->base + USB2_ADPCTRL) & USB2_ADPCTRL_IDDIG); 2568c2ecf20Sopenharmony_ci} 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_cistatic void rcar_gen3_device_recognition(struct rcar_gen3_chan *ch) 2598c2ecf20Sopenharmony_ci{ 2608c2ecf20Sopenharmony_ci if (!rcar_gen3_check_id(ch)) 2618c2ecf20Sopenharmony_ci rcar_gen3_init_for_host(ch); 2628c2ecf20Sopenharmony_ci else 2638c2ecf20Sopenharmony_ci rcar_gen3_init_for_peri(ch); 2648c2ecf20Sopenharmony_ci} 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_cistatic bool rcar_gen3_is_host(struct rcar_gen3_chan *ch) 2678c2ecf20Sopenharmony_ci{ 2688c2ecf20Sopenharmony_ci return !(readl(ch->base + USB2_COMMCTRL) & USB2_COMMCTRL_OTG_PERI); 2698c2ecf20Sopenharmony_ci} 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_cistatic enum phy_mode rcar_gen3_get_phy_mode(struct rcar_gen3_chan *ch) 2728c2ecf20Sopenharmony_ci{ 2738c2ecf20Sopenharmony_ci if (rcar_gen3_is_host(ch)) 2748c2ecf20Sopenharmony_ci return PHY_MODE_USB_HOST; 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_ci return PHY_MODE_USB_DEVICE; 2778c2ecf20Sopenharmony_ci} 2788c2ecf20Sopenharmony_ci 2798c2ecf20Sopenharmony_cistatic bool rcar_gen3_is_any_rphy_initialized(struct rcar_gen3_chan *ch) 2808c2ecf20Sopenharmony_ci{ 2818c2ecf20Sopenharmony_ci int i; 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_ci for (i = 0; i < NUM_OF_PHYS; i++) { 2848c2ecf20Sopenharmony_ci if (ch->rphys[i].initialized) 2858c2ecf20Sopenharmony_ci return true; 2868c2ecf20Sopenharmony_ci } 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_ci return false; 2898c2ecf20Sopenharmony_ci} 2908c2ecf20Sopenharmony_ci 2918c2ecf20Sopenharmony_cistatic bool rcar_gen3_needs_init_otg(struct rcar_gen3_chan *ch) 2928c2ecf20Sopenharmony_ci{ 2938c2ecf20Sopenharmony_ci int i; 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_ci for (i = 0; i < NUM_OF_PHYS; i++) { 2968c2ecf20Sopenharmony_ci if (ch->rphys[i].otg_initialized) 2978c2ecf20Sopenharmony_ci return false; 2988c2ecf20Sopenharmony_ci } 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_ci return true; 3018c2ecf20Sopenharmony_ci} 3028c2ecf20Sopenharmony_ci 3038c2ecf20Sopenharmony_cistatic bool rcar_gen3_are_all_rphys_power_off(struct rcar_gen3_chan *ch) 3048c2ecf20Sopenharmony_ci{ 3058c2ecf20Sopenharmony_ci int i; 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_ci for (i = 0; i < NUM_OF_PHYS; i++) { 3088c2ecf20Sopenharmony_ci if (ch->rphys[i].powered) 3098c2ecf20Sopenharmony_ci return false; 3108c2ecf20Sopenharmony_ci } 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_ci return true; 3138c2ecf20Sopenharmony_ci} 3148c2ecf20Sopenharmony_ci 3158c2ecf20Sopenharmony_cistatic ssize_t role_store(struct device *dev, struct device_attribute *attr, 3168c2ecf20Sopenharmony_ci const char *buf, size_t count) 3178c2ecf20Sopenharmony_ci{ 3188c2ecf20Sopenharmony_ci struct rcar_gen3_chan *ch = dev_get_drvdata(dev); 3198c2ecf20Sopenharmony_ci bool is_b_device; 3208c2ecf20Sopenharmony_ci enum phy_mode cur_mode, new_mode; 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_ci if (!ch->is_otg_channel || !rcar_gen3_is_any_rphy_initialized(ch)) 3238c2ecf20Sopenharmony_ci return -EIO; 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_ci if (sysfs_streq(buf, "host")) 3268c2ecf20Sopenharmony_ci new_mode = PHY_MODE_USB_HOST; 3278c2ecf20Sopenharmony_ci else if (sysfs_streq(buf, "peripheral")) 3288c2ecf20Sopenharmony_ci new_mode = PHY_MODE_USB_DEVICE; 3298c2ecf20Sopenharmony_ci else 3308c2ecf20Sopenharmony_ci return -EINVAL; 3318c2ecf20Sopenharmony_ci 3328c2ecf20Sopenharmony_ci /* is_b_device: true is B-Device. false is A-Device. */ 3338c2ecf20Sopenharmony_ci is_b_device = rcar_gen3_check_id(ch); 3348c2ecf20Sopenharmony_ci cur_mode = rcar_gen3_get_phy_mode(ch); 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_ci /* If current and new mode is the same, this returns the error */ 3378c2ecf20Sopenharmony_ci if (cur_mode == new_mode) 3388c2ecf20Sopenharmony_ci return -EINVAL; 3398c2ecf20Sopenharmony_ci 3408c2ecf20Sopenharmony_ci if (new_mode == PHY_MODE_USB_HOST) { /* And is_host must be false */ 3418c2ecf20Sopenharmony_ci if (!is_b_device) /* A-Peripheral */ 3428c2ecf20Sopenharmony_ci rcar_gen3_init_from_a_peri_to_a_host(ch); 3438c2ecf20Sopenharmony_ci else /* B-Peripheral */ 3448c2ecf20Sopenharmony_ci rcar_gen3_init_for_b_host(ch); 3458c2ecf20Sopenharmony_ci } else { /* And is_host must be true */ 3468c2ecf20Sopenharmony_ci if (!is_b_device) /* A-Host */ 3478c2ecf20Sopenharmony_ci rcar_gen3_init_for_a_peri(ch); 3488c2ecf20Sopenharmony_ci else /* B-Host */ 3498c2ecf20Sopenharmony_ci rcar_gen3_init_for_peri(ch); 3508c2ecf20Sopenharmony_ci } 3518c2ecf20Sopenharmony_ci 3528c2ecf20Sopenharmony_ci return count; 3538c2ecf20Sopenharmony_ci} 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_cistatic ssize_t role_show(struct device *dev, struct device_attribute *attr, 3568c2ecf20Sopenharmony_ci char *buf) 3578c2ecf20Sopenharmony_ci{ 3588c2ecf20Sopenharmony_ci struct rcar_gen3_chan *ch = dev_get_drvdata(dev); 3598c2ecf20Sopenharmony_ci 3608c2ecf20Sopenharmony_ci if (!ch->is_otg_channel || !rcar_gen3_is_any_rphy_initialized(ch)) 3618c2ecf20Sopenharmony_ci return -EIO; 3628c2ecf20Sopenharmony_ci 3638c2ecf20Sopenharmony_ci return sprintf(buf, "%s\n", rcar_gen3_is_host(ch) ? "host" : 3648c2ecf20Sopenharmony_ci "peripheral"); 3658c2ecf20Sopenharmony_ci} 3668c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RW(role); 3678c2ecf20Sopenharmony_ci 3688c2ecf20Sopenharmony_cistatic void rcar_gen3_init_otg(struct rcar_gen3_chan *ch) 3698c2ecf20Sopenharmony_ci{ 3708c2ecf20Sopenharmony_ci void __iomem *usb2_base = ch->base; 3718c2ecf20Sopenharmony_ci u32 val; 3728c2ecf20Sopenharmony_ci 3738c2ecf20Sopenharmony_ci /* Should not use functions of read-modify-write a register */ 3748c2ecf20Sopenharmony_ci val = readl(usb2_base + USB2_LINECTRL1); 3758c2ecf20Sopenharmony_ci val = (val & ~USB2_LINECTRL1_DP_RPD) | USB2_LINECTRL1_DPRPD_EN | 3768c2ecf20Sopenharmony_ci USB2_LINECTRL1_DMRPD_EN | USB2_LINECTRL1_DM_RPD; 3778c2ecf20Sopenharmony_ci writel(val, usb2_base + USB2_LINECTRL1); 3788c2ecf20Sopenharmony_ci 3798c2ecf20Sopenharmony_ci val = readl(usb2_base + USB2_VBCTRL); 3808c2ecf20Sopenharmony_ci val &= ~USB2_VBCTRL_OCCLREN; 3818c2ecf20Sopenharmony_ci writel(val | USB2_VBCTRL_DRVVBUSSEL, usb2_base + USB2_VBCTRL); 3828c2ecf20Sopenharmony_ci val = readl(usb2_base + USB2_ADPCTRL); 3838c2ecf20Sopenharmony_ci writel(val | USB2_ADPCTRL_IDPULLUP, usb2_base + USB2_ADPCTRL); 3848c2ecf20Sopenharmony_ci 3858c2ecf20Sopenharmony_ci msleep(20); 3868c2ecf20Sopenharmony_ci 3878c2ecf20Sopenharmony_ci writel(0xffffffff, usb2_base + USB2_OBINTSTA); 3888c2ecf20Sopenharmony_ci writel(USB2_OBINT_BITS, usb2_base + USB2_OBINTEN); 3898c2ecf20Sopenharmony_ci 3908c2ecf20Sopenharmony_ci rcar_gen3_device_recognition(ch); 3918c2ecf20Sopenharmony_ci} 3928c2ecf20Sopenharmony_ci 3938c2ecf20Sopenharmony_cistatic irqreturn_t rcar_gen3_phy_usb2_irq(int irq, void *_ch) 3948c2ecf20Sopenharmony_ci{ 3958c2ecf20Sopenharmony_ci struct rcar_gen3_chan *ch = _ch; 3968c2ecf20Sopenharmony_ci void __iomem *usb2_base = ch->base; 3978c2ecf20Sopenharmony_ci u32 status = readl(usb2_base + USB2_OBINTSTA); 3988c2ecf20Sopenharmony_ci irqreturn_t ret = IRQ_NONE; 3998c2ecf20Sopenharmony_ci 4008c2ecf20Sopenharmony_ci if (status & USB2_OBINT_BITS) { 4018c2ecf20Sopenharmony_ci dev_vdbg(ch->dev, "%s: %08x\n", __func__, status); 4028c2ecf20Sopenharmony_ci writel(USB2_OBINT_BITS, usb2_base + USB2_OBINTSTA); 4038c2ecf20Sopenharmony_ci rcar_gen3_device_recognition(ch); 4048c2ecf20Sopenharmony_ci ret = IRQ_HANDLED; 4058c2ecf20Sopenharmony_ci } 4068c2ecf20Sopenharmony_ci 4078c2ecf20Sopenharmony_ci return ret; 4088c2ecf20Sopenharmony_ci} 4098c2ecf20Sopenharmony_ci 4108c2ecf20Sopenharmony_cistatic int rcar_gen3_phy_usb2_init(struct phy *p) 4118c2ecf20Sopenharmony_ci{ 4128c2ecf20Sopenharmony_ci struct rcar_gen3_phy *rphy = phy_get_drvdata(p); 4138c2ecf20Sopenharmony_ci struct rcar_gen3_chan *channel = rphy->ch; 4148c2ecf20Sopenharmony_ci void __iomem *usb2_base = channel->base; 4158c2ecf20Sopenharmony_ci u32 val; 4168c2ecf20Sopenharmony_ci int ret; 4178c2ecf20Sopenharmony_ci 4188c2ecf20Sopenharmony_ci if (!rcar_gen3_is_any_rphy_initialized(channel) && channel->irq >= 0) { 4198c2ecf20Sopenharmony_ci INIT_WORK(&channel->work, rcar_gen3_phy_usb2_work); 4208c2ecf20Sopenharmony_ci ret = request_irq(channel->irq, rcar_gen3_phy_usb2_irq, 4218c2ecf20Sopenharmony_ci IRQF_SHARED, dev_name(channel->dev), channel); 4228c2ecf20Sopenharmony_ci if (ret < 0) { 4238c2ecf20Sopenharmony_ci dev_err(channel->dev, "No irq handler (%d)\n", channel->irq); 4248c2ecf20Sopenharmony_ci return ret; 4258c2ecf20Sopenharmony_ci } 4268c2ecf20Sopenharmony_ci } 4278c2ecf20Sopenharmony_ci 4288c2ecf20Sopenharmony_ci /* Initialize USB2 part */ 4298c2ecf20Sopenharmony_ci val = readl(usb2_base + USB2_INT_ENABLE); 4308c2ecf20Sopenharmony_ci val |= USB2_INT_ENABLE_UCOM_INTEN | rphy->int_enable_bits; 4318c2ecf20Sopenharmony_ci writel(val, usb2_base + USB2_INT_ENABLE); 4328c2ecf20Sopenharmony_ci writel(USB2_SPD_RSM_TIMSET_INIT, usb2_base + USB2_SPD_RSM_TIMSET); 4338c2ecf20Sopenharmony_ci writel(USB2_OC_TIMSET_INIT, usb2_base + USB2_OC_TIMSET); 4348c2ecf20Sopenharmony_ci 4358c2ecf20Sopenharmony_ci /* Initialize otg part */ 4368c2ecf20Sopenharmony_ci if (channel->is_otg_channel) { 4378c2ecf20Sopenharmony_ci if (rcar_gen3_needs_init_otg(channel)) 4388c2ecf20Sopenharmony_ci rcar_gen3_init_otg(channel); 4398c2ecf20Sopenharmony_ci rphy->otg_initialized = true; 4408c2ecf20Sopenharmony_ci } 4418c2ecf20Sopenharmony_ci 4428c2ecf20Sopenharmony_ci rphy->initialized = true; 4438c2ecf20Sopenharmony_ci 4448c2ecf20Sopenharmony_ci return 0; 4458c2ecf20Sopenharmony_ci} 4468c2ecf20Sopenharmony_ci 4478c2ecf20Sopenharmony_cistatic int rcar_gen3_phy_usb2_exit(struct phy *p) 4488c2ecf20Sopenharmony_ci{ 4498c2ecf20Sopenharmony_ci struct rcar_gen3_phy *rphy = phy_get_drvdata(p); 4508c2ecf20Sopenharmony_ci struct rcar_gen3_chan *channel = rphy->ch; 4518c2ecf20Sopenharmony_ci void __iomem *usb2_base = channel->base; 4528c2ecf20Sopenharmony_ci u32 val; 4538c2ecf20Sopenharmony_ci 4548c2ecf20Sopenharmony_ci rphy->initialized = false; 4558c2ecf20Sopenharmony_ci 4568c2ecf20Sopenharmony_ci if (channel->is_otg_channel) 4578c2ecf20Sopenharmony_ci rphy->otg_initialized = false; 4588c2ecf20Sopenharmony_ci 4598c2ecf20Sopenharmony_ci val = readl(usb2_base + USB2_INT_ENABLE); 4608c2ecf20Sopenharmony_ci val &= ~rphy->int_enable_bits; 4618c2ecf20Sopenharmony_ci if (!rcar_gen3_is_any_rphy_initialized(channel)) 4628c2ecf20Sopenharmony_ci val &= ~USB2_INT_ENABLE_UCOM_INTEN; 4638c2ecf20Sopenharmony_ci writel(val, usb2_base + USB2_INT_ENABLE); 4648c2ecf20Sopenharmony_ci 4658c2ecf20Sopenharmony_ci if (channel->irq >= 0 && !rcar_gen3_is_any_rphy_initialized(channel)) 4668c2ecf20Sopenharmony_ci free_irq(channel->irq, channel); 4678c2ecf20Sopenharmony_ci 4688c2ecf20Sopenharmony_ci return 0; 4698c2ecf20Sopenharmony_ci} 4708c2ecf20Sopenharmony_ci 4718c2ecf20Sopenharmony_cistatic int rcar_gen3_phy_usb2_power_on(struct phy *p) 4728c2ecf20Sopenharmony_ci{ 4738c2ecf20Sopenharmony_ci struct rcar_gen3_phy *rphy = phy_get_drvdata(p); 4748c2ecf20Sopenharmony_ci struct rcar_gen3_chan *channel = rphy->ch; 4758c2ecf20Sopenharmony_ci void __iomem *usb2_base = channel->base; 4768c2ecf20Sopenharmony_ci u32 val; 4778c2ecf20Sopenharmony_ci int ret = 0; 4788c2ecf20Sopenharmony_ci 4798c2ecf20Sopenharmony_ci mutex_lock(&channel->lock); 4808c2ecf20Sopenharmony_ci if (!rcar_gen3_are_all_rphys_power_off(channel)) 4818c2ecf20Sopenharmony_ci goto out; 4828c2ecf20Sopenharmony_ci 4838c2ecf20Sopenharmony_ci if (channel->vbus) { 4848c2ecf20Sopenharmony_ci ret = regulator_enable(channel->vbus); 4858c2ecf20Sopenharmony_ci if (ret) 4868c2ecf20Sopenharmony_ci goto out; 4878c2ecf20Sopenharmony_ci } 4888c2ecf20Sopenharmony_ci 4898c2ecf20Sopenharmony_ci val = readl(usb2_base + USB2_USBCTR); 4908c2ecf20Sopenharmony_ci val |= USB2_USBCTR_PLL_RST; 4918c2ecf20Sopenharmony_ci writel(val, usb2_base + USB2_USBCTR); 4928c2ecf20Sopenharmony_ci val &= ~USB2_USBCTR_PLL_RST; 4938c2ecf20Sopenharmony_ci writel(val, usb2_base + USB2_USBCTR); 4948c2ecf20Sopenharmony_ci 4958c2ecf20Sopenharmony_ciout: 4968c2ecf20Sopenharmony_ci /* The powered flag should be set for any other phys anyway */ 4978c2ecf20Sopenharmony_ci rphy->powered = true; 4988c2ecf20Sopenharmony_ci mutex_unlock(&channel->lock); 4998c2ecf20Sopenharmony_ci 5008c2ecf20Sopenharmony_ci return 0; 5018c2ecf20Sopenharmony_ci} 5028c2ecf20Sopenharmony_ci 5038c2ecf20Sopenharmony_cistatic int rcar_gen3_phy_usb2_power_off(struct phy *p) 5048c2ecf20Sopenharmony_ci{ 5058c2ecf20Sopenharmony_ci struct rcar_gen3_phy *rphy = phy_get_drvdata(p); 5068c2ecf20Sopenharmony_ci struct rcar_gen3_chan *channel = rphy->ch; 5078c2ecf20Sopenharmony_ci int ret = 0; 5088c2ecf20Sopenharmony_ci 5098c2ecf20Sopenharmony_ci mutex_lock(&channel->lock); 5108c2ecf20Sopenharmony_ci rphy->powered = false; 5118c2ecf20Sopenharmony_ci 5128c2ecf20Sopenharmony_ci if (!rcar_gen3_are_all_rphys_power_off(channel)) 5138c2ecf20Sopenharmony_ci goto out; 5148c2ecf20Sopenharmony_ci 5158c2ecf20Sopenharmony_ci if (channel->vbus) 5168c2ecf20Sopenharmony_ci ret = regulator_disable(channel->vbus); 5178c2ecf20Sopenharmony_ci 5188c2ecf20Sopenharmony_ciout: 5198c2ecf20Sopenharmony_ci mutex_unlock(&channel->lock); 5208c2ecf20Sopenharmony_ci 5218c2ecf20Sopenharmony_ci return ret; 5228c2ecf20Sopenharmony_ci} 5238c2ecf20Sopenharmony_ci 5248c2ecf20Sopenharmony_cistatic const struct phy_ops rcar_gen3_phy_usb2_ops = { 5258c2ecf20Sopenharmony_ci .init = rcar_gen3_phy_usb2_init, 5268c2ecf20Sopenharmony_ci .exit = rcar_gen3_phy_usb2_exit, 5278c2ecf20Sopenharmony_ci .power_on = rcar_gen3_phy_usb2_power_on, 5288c2ecf20Sopenharmony_ci .power_off = rcar_gen3_phy_usb2_power_off, 5298c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 5308c2ecf20Sopenharmony_ci}; 5318c2ecf20Sopenharmony_ci 5328c2ecf20Sopenharmony_cistatic const struct phy_ops rz_g1c_phy_usb2_ops = { 5338c2ecf20Sopenharmony_ci .init = rcar_gen3_phy_usb2_init, 5348c2ecf20Sopenharmony_ci .exit = rcar_gen3_phy_usb2_exit, 5358c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 5368c2ecf20Sopenharmony_ci}; 5378c2ecf20Sopenharmony_ci 5388c2ecf20Sopenharmony_cistatic const struct of_device_id rcar_gen3_phy_usb2_match_table[] = { 5398c2ecf20Sopenharmony_ci { 5408c2ecf20Sopenharmony_ci .compatible = "renesas,usb2-phy-r8a77470", 5418c2ecf20Sopenharmony_ci .data = &rz_g1c_phy_usb2_ops, 5428c2ecf20Sopenharmony_ci }, 5438c2ecf20Sopenharmony_ci { 5448c2ecf20Sopenharmony_ci .compatible = "renesas,usb2-phy-r8a7795", 5458c2ecf20Sopenharmony_ci .data = &rcar_gen3_phy_usb2_ops, 5468c2ecf20Sopenharmony_ci }, 5478c2ecf20Sopenharmony_ci { 5488c2ecf20Sopenharmony_ci .compatible = "renesas,usb2-phy-r8a7796", 5498c2ecf20Sopenharmony_ci .data = &rcar_gen3_phy_usb2_ops, 5508c2ecf20Sopenharmony_ci }, 5518c2ecf20Sopenharmony_ci { 5528c2ecf20Sopenharmony_ci .compatible = "renesas,usb2-phy-r8a77965", 5538c2ecf20Sopenharmony_ci .data = &rcar_gen3_phy_usb2_ops, 5548c2ecf20Sopenharmony_ci }, 5558c2ecf20Sopenharmony_ci { 5568c2ecf20Sopenharmony_ci .compatible = "renesas,rcar-gen3-usb2-phy", 5578c2ecf20Sopenharmony_ci .data = &rcar_gen3_phy_usb2_ops, 5588c2ecf20Sopenharmony_ci }, 5598c2ecf20Sopenharmony_ci { /* sentinel */ }, 5608c2ecf20Sopenharmony_ci}; 5618c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, rcar_gen3_phy_usb2_match_table); 5628c2ecf20Sopenharmony_ci 5638c2ecf20Sopenharmony_cistatic const unsigned int rcar_gen3_phy_cable[] = { 5648c2ecf20Sopenharmony_ci EXTCON_USB, 5658c2ecf20Sopenharmony_ci EXTCON_USB_HOST, 5668c2ecf20Sopenharmony_ci EXTCON_NONE, 5678c2ecf20Sopenharmony_ci}; 5688c2ecf20Sopenharmony_ci 5698c2ecf20Sopenharmony_cistatic struct phy *rcar_gen3_phy_usb2_xlate(struct device *dev, 5708c2ecf20Sopenharmony_ci struct of_phandle_args *args) 5718c2ecf20Sopenharmony_ci{ 5728c2ecf20Sopenharmony_ci struct rcar_gen3_chan *ch = dev_get_drvdata(dev); 5738c2ecf20Sopenharmony_ci 5748c2ecf20Sopenharmony_ci if (args->args_count == 0) /* For old version dts */ 5758c2ecf20Sopenharmony_ci return ch->rphys[PHY_INDEX_BOTH_HC].phy; 5768c2ecf20Sopenharmony_ci else if (args->args_count > 1) /* Prevent invalid args count */ 5778c2ecf20Sopenharmony_ci return ERR_PTR(-ENODEV); 5788c2ecf20Sopenharmony_ci 5798c2ecf20Sopenharmony_ci if (args->args[0] >= NUM_OF_PHYS) 5808c2ecf20Sopenharmony_ci return ERR_PTR(-ENODEV); 5818c2ecf20Sopenharmony_ci 5828c2ecf20Sopenharmony_ci return ch->rphys[args->args[0]].phy; 5838c2ecf20Sopenharmony_ci} 5848c2ecf20Sopenharmony_ci 5858c2ecf20Sopenharmony_cistatic enum usb_dr_mode rcar_gen3_get_dr_mode(struct device_node *np) 5868c2ecf20Sopenharmony_ci{ 5878c2ecf20Sopenharmony_ci enum usb_dr_mode candidate = USB_DR_MODE_UNKNOWN; 5888c2ecf20Sopenharmony_ci int i; 5898c2ecf20Sopenharmony_ci 5908c2ecf20Sopenharmony_ci /* 5918c2ecf20Sopenharmony_ci * If one of device nodes has other dr_mode except UNKNOWN, 5928c2ecf20Sopenharmony_ci * this function returns UNKNOWN. To achieve backward compatibility, 5938c2ecf20Sopenharmony_ci * this loop starts the index as 0. 5948c2ecf20Sopenharmony_ci */ 5958c2ecf20Sopenharmony_ci for (i = 0; i < NUM_OF_PHYS; i++) { 5968c2ecf20Sopenharmony_ci enum usb_dr_mode mode = of_usb_get_dr_mode_by_phy(np, i); 5978c2ecf20Sopenharmony_ci 5988c2ecf20Sopenharmony_ci if (mode != USB_DR_MODE_UNKNOWN) { 5998c2ecf20Sopenharmony_ci if (candidate == USB_DR_MODE_UNKNOWN) 6008c2ecf20Sopenharmony_ci candidate = mode; 6018c2ecf20Sopenharmony_ci else if (candidate != mode) 6028c2ecf20Sopenharmony_ci return USB_DR_MODE_UNKNOWN; 6038c2ecf20Sopenharmony_ci } 6048c2ecf20Sopenharmony_ci } 6058c2ecf20Sopenharmony_ci 6068c2ecf20Sopenharmony_ci return candidate; 6078c2ecf20Sopenharmony_ci} 6088c2ecf20Sopenharmony_ci 6098c2ecf20Sopenharmony_cistatic int rcar_gen3_phy_usb2_probe(struct platform_device *pdev) 6108c2ecf20Sopenharmony_ci{ 6118c2ecf20Sopenharmony_ci struct device *dev = &pdev->dev; 6128c2ecf20Sopenharmony_ci struct rcar_gen3_chan *channel; 6138c2ecf20Sopenharmony_ci struct phy_provider *provider; 6148c2ecf20Sopenharmony_ci struct resource *res; 6158c2ecf20Sopenharmony_ci const struct phy_ops *phy_usb2_ops; 6168c2ecf20Sopenharmony_ci int ret = 0, i; 6178c2ecf20Sopenharmony_ci 6188c2ecf20Sopenharmony_ci if (!dev->of_node) { 6198c2ecf20Sopenharmony_ci dev_err(dev, "This driver needs device tree\n"); 6208c2ecf20Sopenharmony_ci return -EINVAL; 6218c2ecf20Sopenharmony_ci } 6228c2ecf20Sopenharmony_ci 6238c2ecf20Sopenharmony_ci channel = devm_kzalloc(dev, sizeof(*channel), GFP_KERNEL); 6248c2ecf20Sopenharmony_ci if (!channel) 6258c2ecf20Sopenharmony_ci return -ENOMEM; 6268c2ecf20Sopenharmony_ci 6278c2ecf20Sopenharmony_ci res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 6288c2ecf20Sopenharmony_ci channel->base = devm_ioremap_resource(dev, res); 6298c2ecf20Sopenharmony_ci if (IS_ERR(channel->base)) 6308c2ecf20Sopenharmony_ci return PTR_ERR(channel->base); 6318c2ecf20Sopenharmony_ci 6328c2ecf20Sopenharmony_ci /* get irq number here and request_irq for OTG in phy_init */ 6338c2ecf20Sopenharmony_ci channel->irq = platform_get_irq_optional(pdev, 0); 6348c2ecf20Sopenharmony_ci channel->dr_mode = rcar_gen3_get_dr_mode(dev->of_node); 6358c2ecf20Sopenharmony_ci if (channel->dr_mode != USB_DR_MODE_UNKNOWN) { 6368c2ecf20Sopenharmony_ci channel->is_otg_channel = true; 6378c2ecf20Sopenharmony_ci channel->uses_otg_pins = !of_property_read_bool(dev->of_node, 6388c2ecf20Sopenharmony_ci "renesas,no-otg-pins"); 6398c2ecf20Sopenharmony_ci channel->extcon = devm_extcon_dev_allocate(dev, 6408c2ecf20Sopenharmony_ci rcar_gen3_phy_cable); 6418c2ecf20Sopenharmony_ci if (IS_ERR(channel->extcon)) 6428c2ecf20Sopenharmony_ci return PTR_ERR(channel->extcon); 6438c2ecf20Sopenharmony_ci 6448c2ecf20Sopenharmony_ci ret = devm_extcon_dev_register(dev, channel->extcon); 6458c2ecf20Sopenharmony_ci if (ret < 0) { 6468c2ecf20Sopenharmony_ci dev_err(dev, "Failed to register extcon\n"); 6478c2ecf20Sopenharmony_ci return ret; 6488c2ecf20Sopenharmony_ci } 6498c2ecf20Sopenharmony_ci } 6508c2ecf20Sopenharmony_ci 6518c2ecf20Sopenharmony_ci /* 6528c2ecf20Sopenharmony_ci * devm_phy_create() will call pm_runtime_enable(&phy->dev); 6538c2ecf20Sopenharmony_ci * And then, phy-core will manage runtime pm for this device. 6548c2ecf20Sopenharmony_ci */ 6558c2ecf20Sopenharmony_ci pm_runtime_enable(dev); 6568c2ecf20Sopenharmony_ci phy_usb2_ops = of_device_get_match_data(dev); 6578c2ecf20Sopenharmony_ci if (!phy_usb2_ops) { 6588c2ecf20Sopenharmony_ci ret = -EINVAL; 6598c2ecf20Sopenharmony_ci goto error; 6608c2ecf20Sopenharmony_ci } 6618c2ecf20Sopenharmony_ci 6628c2ecf20Sopenharmony_ci mutex_init(&channel->lock); 6638c2ecf20Sopenharmony_ci for (i = 0; i < NUM_OF_PHYS; i++) { 6648c2ecf20Sopenharmony_ci channel->rphys[i].phy = devm_phy_create(dev, NULL, 6658c2ecf20Sopenharmony_ci phy_usb2_ops); 6668c2ecf20Sopenharmony_ci if (IS_ERR(channel->rphys[i].phy)) { 6678c2ecf20Sopenharmony_ci dev_err(dev, "Failed to create USB2 PHY\n"); 6688c2ecf20Sopenharmony_ci ret = PTR_ERR(channel->rphys[i].phy); 6698c2ecf20Sopenharmony_ci goto error; 6708c2ecf20Sopenharmony_ci } 6718c2ecf20Sopenharmony_ci channel->rphys[i].ch = channel; 6728c2ecf20Sopenharmony_ci channel->rphys[i].int_enable_bits = rcar_gen3_int_enable[i]; 6738c2ecf20Sopenharmony_ci phy_set_drvdata(channel->rphys[i].phy, &channel->rphys[i]); 6748c2ecf20Sopenharmony_ci } 6758c2ecf20Sopenharmony_ci 6768c2ecf20Sopenharmony_ci channel->vbus = devm_regulator_get_optional(dev, "vbus"); 6778c2ecf20Sopenharmony_ci if (IS_ERR(channel->vbus)) { 6788c2ecf20Sopenharmony_ci if (PTR_ERR(channel->vbus) == -EPROBE_DEFER) { 6798c2ecf20Sopenharmony_ci ret = PTR_ERR(channel->vbus); 6808c2ecf20Sopenharmony_ci goto error; 6818c2ecf20Sopenharmony_ci } 6828c2ecf20Sopenharmony_ci channel->vbus = NULL; 6838c2ecf20Sopenharmony_ci } 6848c2ecf20Sopenharmony_ci 6858c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, channel); 6868c2ecf20Sopenharmony_ci channel->dev = dev; 6878c2ecf20Sopenharmony_ci 6888c2ecf20Sopenharmony_ci provider = devm_of_phy_provider_register(dev, rcar_gen3_phy_usb2_xlate); 6898c2ecf20Sopenharmony_ci if (IS_ERR(provider)) { 6908c2ecf20Sopenharmony_ci dev_err(dev, "Failed to register PHY provider\n"); 6918c2ecf20Sopenharmony_ci ret = PTR_ERR(provider); 6928c2ecf20Sopenharmony_ci goto error; 6938c2ecf20Sopenharmony_ci } else if (channel->is_otg_channel) { 6948c2ecf20Sopenharmony_ci ret = device_create_file(dev, &dev_attr_role); 6958c2ecf20Sopenharmony_ci if (ret < 0) 6968c2ecf20Sopenharmony_ci goto error; 6978c2ecf20Sopenharmony_ci } 6988c2ecf20Sopenharmony_ci 6998c2ecf20Sopenharmony_ci return 0; 7008c2ecf20Sopenharmony_ci 7018c2ecf20Sopenharmony_cierror: 7028c2ecf20Sopenharmony_ci pm_runtime_disable(dev); 7038c2ecf20Sopenharmony_ci 7048c2ecf20Sopenharmony_ci return ret; 7058c2ecf20Sopenharmony_ci} 7068c2ecf20Sopenharmony_ci 7078c2ecf20Sopenharmony_cistatic int rcar_gen3_phy_usb2_remove(struct platform_device *pdev) 7088c2ecf20Sopenharmony_ci{ 7098c2ecf20Sopenharmony_ci struct rcar_gen3_chan *channel = platform_get_drvdata(pdev); 7108c2ecf20Sopenharmony_ci 7118c2ecf20Sopenharmony_ci if (channel->is_otg_channel) 7128c2ecf20Sopenharmony_ci device_remove_file(&pdev->dev, &dev_attr_role); 7138c2ecf20Sopenharmony_ci 7148c2ecf20Sopenharmony_ci pm_runtime_disable(&pdev->dev); 7158c2ecf20Sopenharmony_ci 7168c2ecf20Sopenharmony_ci return 0; 7178c2ecf20Sopenharmony_ci}; 7188c2ecf20Sopenharmony_ci 7198c2ecf20Sopenharmony_cistatic struct platform_driver rcar_gen3_phy_usb2_driver = { 7208c2ecf20Sopenharmony_ci .driver = { 7218c2ecf20Sopenharmony_ci .name = "phy_rcar_gen3_usb2", 7228c2ecf20Sopenharmony_ci .of_match_table = rcar_gen3_phy_usb2_match_table, 7238c2ecf20Sopenharmony_ci }, 7248c2ecf20Sopenharmony_ci .probe = rcar_gen3_phy_usb2_probe, 7258c2ecf20Sopenharmony_ci .remove = rcar_gen3_phy_usb2_remove, 7268c2ecf20Sopenharmony_ci}; 7278c2ecf20Sopenharmony_cimodule_platform_driver(rcar_gen3_phy_usb2_driver); 7288c2ecf20Sopenharmony_ci 7298c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 7308c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Renesas R-Car Gen3 USB 2.0 PHY"); 7318c2ecf20Sopenharmony_ciMODULE_AUTHOR("Yoshihiro Shimoda <yoshihiro.shimoda.uh@renesas.com>"); 732