18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Allwinner sun4i USB phy driver 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2014-2015 Hans de Goede <hdegoede@redhat.com> 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Based on code from 88c2ecf20Sopenharmony_ci * Allwinner Technology Co., Ltd. <www.allwinnertech.com> 98c2ecf20Sopenharmony_ci * 108c2ecf20Sopenharmony_ci * Modelled after: Samsung S5P/Exynos SoC series MIPI CSIS/DSIM DPHY driver 118c2ecf20Sopenharmony_ci * Copyright (C) 2013 Samsung Electronics Co., Ltd. 128c2ecf20Sopenharmony_ci * Author: Sylwester Nawrocki <s.nawrocki@samsung.com> 138c2ecf20Sopenharmony_ci */ 148c2ecf20Sopenharmony_ci 158c2ecf20Sopenharmony_ci#include <linux/clk.h> 168c2ecf20Sopenharmony_ci#include <linux/delay.h> 178c2ecf20Sopenharmony_ci#include <linux/err.h> 188c2ecf20Sopenharmony_ci#include <linux/extcon-provider.h> 198c2ecf20Sopenharmony_ci#include <linux/gpio/consumer.h> 208c2ecf20Sopenharmony_ci#include <linux/io.h> 218c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 228c2ecf20Sopenharmony_ci#include <linux/kernel.h> 238c2ecf20Sopenharmony_ci#include <linux/module.h> 248c2ecf20Sopenharmony_ci#include <linux/mutex.h> 258c2ecf20Sopenharmony_ci#include <linux/of.h> 268c2ecf20Sopenharmony_ci#include <linux/of_address.h> 278c2ecf20Sopenharmony_ci#include <linux/of_device.h> 288c2ecf20Sopenharmony_ci#include <linux/of_gpio.h> 298c2ecf20Sopenharmony_ci#include <linux/phy/phy.h> 308c2ecf20Sopenharmony_ci#include <linux/phy/phy-sun4i-usb.h> 318c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 328c2ecf20Sopenharmony_ci#include <linux/power_supply.h> 338c2ecf20Sopenharmony_ci#include <linux/regulator/consumer.h> 348c2ecf20Sopenharmony_ci#include <linux/reset.h> 358c2ecf20Sopenharmony_ci#include <linux/spinlock.h> 368c2ecf20Sopenharmony_ci#include <linux/usb/of.h> 378c2ecf20Sopenharmony_ci#include <linux/workqueue.h> 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci#define REG_ISCR 0x00 408c2ecf20Sopenharmony_ci#define REG_PHYCTL_A10 0x04 418c2ecf20Sopenharmony_ci#define REG_PHYBIST 0x08 428c2ecf20Sopenharmony_ci#define REG_PHYTUNE 0x0c 438c2ecf20Sopenharmony_ci#define REG_PHYCTL_A33 0x10 448c2ecf20Sopenharmony_ci#define REG_PHY_OTGCTL 0x20 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci#define REG_PMU_UNK1 0x10 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_ci#define PHYCTL_DATA BIT(7) 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ci#define OTGCTL_ROUTE_MUSB BIT(0) 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci#define SUNXI_AHB_ICHR8_EN BIT(10) 538c2ecf20Sopenharmony_ci#define SUNXI_AHB_INCR4_BURST_EN BIT(9) 548c2ecf20Sopenharmony_ci#define SUNXI_AHB_INCRX_ALIGN_EN BIT(8) 558c2ecf20Sopenharmony_ci#define SUNXI_ULPI_BYPASS_EN BIT(0) 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci/* ISCR, Interface Status and Control bits */ 588c2ecf20Sopenharmony_ci#define ISCR_ID_PULLUP_EN (1 << 17) 598c2ecf20Sopenharmony_ci#define ISCR_DPDM_PULLUP_EN (1 << 16) 608c2ecf20Sopenharmony_ci/* sunxi has the phy id/vbus pins not connected, so we use the force bits */ 618c2ecf20Sopenharmony_ci#define ISCR_FORCE_ID_MASK (3 << 14) 628c2ecf20Sopenharmony_ci#define ISCR_FORCE_ID_LOW (2 << 14) 638c2ecf20Sopenharmony_ci#define ISCR_FORCE_ID_HIGH (3 << 14) 648c2ecf20Sopenharmony_ci#define ISCR_FORCE_VBUS_MASK (3 << 12) 658c2ecf20Sopenharmony_ci#define ISCR_FORCE_VBUS_LOW (2 << 12) 668c2ecf20Sopenharmony_ci#define ISCR_FORCE_VBUS_HIGH (3 << 12) 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci/* Common Control Bits for Both PHYs */ 698c2ecf20Sopenharmony_ci#define PHY_PLL_BW 0x03 708c2ecf20Sopenharmony_ci#define PHY_RES45_CAL_EN 0x0c 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci/* Private Control Bits for Each PHY */ 738c2ecf20Sopenharmony_ci#define PHY_TX_AMPLITUDE_TUNE 0x20 748c2ecf20Sopenharmony_ci#define PHY_TX_SLEWRATE_TUNE 0x22 758c2ecf20Sopenharmony_ci#define PHY_VBUSVALID_TH_SEL 0x25 768c2ecf20Sopenharmony_ci#define PHY_PULLUP_RES_SEL 0x27 778c2ecf20Sopenharmony_ci#define PHY_OTG_FUNC_EN 0x28 788c2ecf20Sopenharmony_ci#define PHY_VBUS_DET_EN 0x29 798c2ecf20Sopenharmony_ci#define PHY_DISCON_TH_SEL 0x2a 808c2ecf20Sopenharmony_ci#define PHY_SQUELCH_DETECT 0x3c 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci/* A83T specific control bits for PHY0 */ 838c2ecf20Sopenharmony_ci#define PHY_CTL_VBUSVLDEXT BIT(5) 848c2ecf20Sopenharmony_ci#define PHY_CTL_SIDDQ BIT(3) 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci/* A83T specific control bits for PHY2 HSIC */ 878c2ecf20Sopenharmony_ci#define SUNXI_EHCI_HS_FORCE BIT(20) 888c2ecf20Sopenharmony_ci#define SUNXI_HSIC_CONNECT_DET BIT(17) 898c2ecf20Sopenharmony_ci#define SUNXI_HSIC_CONNECT_INT BIT(16) 908c2ecf20Sopenharmony_ci#define SUNXI_HSIC BIT(1) 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci#define MAX_PHYS 4 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci/* 958c2ecf20Sopenharmony_ci * Note do not raise the debounce time, we must report Vusb high within 100ms 968c2ecf20Sopenharmony_ci * otherwise we get Vbus errors 978c2ecf20Sopenharmony_ci */ 988c2ecf20Sopenharmony_ci#define DEBOUNCE_TIME msecs_to_jiffies(50) 998c2ecf20Sopenharmony_ci#define POLL_TIME msecs_to_jiffies(250) 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_cienum sun4i_usb_phy_type { 1028c2ecf20Sopenharmony_ci sun4i_a10_phy, 1038c2ecf20Sopenharmony_ci sun6i_a31_phy, 1048c2ecf20Sopenharmony_ci sun8i_a33_phy, 1058c2ecf20Sopenharmony_ci sun8i_a83t_phy, 1068c2ecf20Sopenharmony_ci sun8i_h3_phy, 1078c2ecf20Sopenharmony_ci sun8i_r40_phy, 1088c2ecf20Sopenharmony_ci sun8i_v3s_phy, 1098c2ecf20Sopenharmony_ci sun50i_a64_phy, 1108c2ecf20Sopenharmony_ci sun50i_h6_phy, 1118c2ecf20Sopenharmony_ci}; 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_cistruct sun4i_usb_phy_cfg { 1148c2ecf20Sopenharmony_ci int num_phys; 1158c2ecf20Sopenharmony_ci int hsic_index; 1168c2ecf20Sopenharmony_ci enum sun4i_usb_phy_type type; 1178c2ecf20Sopenharmony_ci u32 disc_thresh; 1188c2ecf20Sopenharmony_ci u8 phyctl_offset; 1198c2ecf20Sopenharmony_ci bool dedicated_clocks; 1208c2ecf20Sopenharmony_ci bool enable_pmu_unk1; 1218c2ecf20Sopenharmony_ci bool phy0_dual_route; 1228c2ecf20Sopenharmony_ci int missing_phys; 1238c2ecf20Sopenharmony_ci}; 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_cistruct sun4i_usb_phy_data { 1268c2ecf20Sopenharmony_ci void __iomem *base; 1278c2ecf20Sopenharmony_ci const struct sun4i_usb_phy_cfg *cfg; 1288c2ecf20Sopenharmony_ci enum usb_dr_mode dr_mode; 1298c2ecf20Sopenharmony_ci spinlock_t reg_lock; /* guard access to phyctl reg */ 1308c2ecf20Sopenharmony_ci struct sun4i_usb_phy { 1318c2ecf20Sopenharmony_ci struct phy *phy; 1328c2ecf20Sopenharmony_ci void __iomem *pmu; 1338c2ecf20Sopenharmony_ci struct regulator *vbus; 1348c2ecf20Sopenharmony_ci struct reset_control *reset; 1358c2ecf20Sopenharmony_ci struct clk *clk; 1368c2ecf20Sopenharmony_ci struct clk *clk2; 1378c2ecf20Sopenharmony_ci bool regulator_on; 1388c2ecf20Sopenharmony_ci int index; 1398c2ecf20Sopenharmony_ci } phys[MAX_PHYS]; 1408c2ecf20Sopenharmony_ci /* phy0 / otg related variables */ 1418c2ecf20Sopenharmony_ci struct extcon_dev *extcon; 1428c2ecf20Sopenharmony_ci bool phy0_init; 1438c2ecf20Sopenharmony_ci struct gpio_desc *id_det_gpio; 1448c2ecf20Sopenharmony_ci struct gpio_desc *vbus_det_gpio; 1458c2ecf20Sopenharmony_ci struct power_supply *vbus_power_supply; 1468c2ecf20Sopenharmony_ci struct notifier_block vbus_power_nb; 1478c2ecf20Sopenharmony_ci bool vbus_power_nb_registered; 1488c2ecf20Sopenharmony_ci bool force_session_end; 1498c2ecf20Sopenharmony_ci int id_det_irq; 1508c2ecf20Sopenharmony_ci int vbus_det_irq; 1518c2ecf20Sopenharmony_ci int id_det; 1528c2ecf20Sopenharmony_ci int vbus_det; 1538c2ecf20Sopenharmony_ci struct delayed_work detect; 1548c2ecf20Sopenharmony_ci}; 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci#define to_sun4i_usb_phy_data(phy) \ 1578c2ecf20Sopenharmony_ci container_of((phy), struct sun4i_usb_phy_data, phys[(phy)->index]) 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_cistatic void sun4i_usb_phy0_update_iscr(struct phy *_phy, u32 clr, u32 set) 1608c2ecf20Sopenharmony_ci{ 1618c2ecf20Sopenharmony_ci struct sun4i_usb_phy *phy = phy_get_drvdata(_phy); 1628c2ecf20Sopenharmony_ci struct sun4i_usb_phy_data *data = to_sun4i_usb_phy_data(phy); 1638c2ecf20Sopenharmony_ci u32 iscr; 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci iscr = readl(data->base + REG_ISCR); 1668c2ecf20Sopenharmony_ci iscr &= ~clr; 1678c2ecf20Sopenharmony_ci iscr |= set; 1688c2ecf20Sopenharmony_ci writel(iscr, data->base + REG_ISCR); 1698c2ecf20Sopenharmony_ci} 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_cistatic void sun4i_usb_phy0_set_id_detect(struct phy *phy, u32 val) 1728c2ecf20Sopenharmony_ci{ 1738c2ecf20Sopenharmony_ci if (val) 1748c2ecf20Sopenharmony_ci val = ISCR_FORCE_ID_HIGH; 1758c2ecf20Sopenharmony_ci else 1768c2ecf20Sopenharmony_ci val = ISCR_FORCE_ID_LOW; 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci sun4i_usb_phy0_update_iscr(phy, ISCR_FORCE_ID_MASK, val); 1798c2ecf20Sopenharmony_ci} 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_cistatic void sun4i_usb_phy0_set_vbus_detect(struct phy *phy, u32 val) 1828c2ecf20Sopenharmony_ci{ 1838c2ecf20Sopenharmony_ci if (val) 1848c2ecf20Sopenharmony_ci val = ISCR_FORCE_VBUS_HIGH; 1858c2ecf20Sopenharmony_ci else 1868c2ecf20Sopenharmony_ci val = ISCR_FORCE_VBUS_LOW; 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci sun4i_usb_phy0_update_iscr(phy, ISCR_FORCE_VBUS_MASK, val); 1898c2ecf20Sopenharmony_ci} 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_cistatic void sun4i_usb_phy_write(struct sun4i_usb_phy *phy, u32 addr, u32 data, 1928c2ecf20Sopenharmony_ci int len) 1938c2ecf20Sopenharmony_ci{ 1948c2ecf20Sopenharmony_ci struct sun4i_usb_phy_data *phy_data = to_sun4i_usb_phy_data(phy); 1958c2ecf20Sopenharmony_ci u32 temp, usbc_bit = BIT(phy->index * 2); 1968c2ecf20Sopenharmony_ci void __iomem *phyctl = phy_data->base + phy_data->cfg->phyctl_offset; 1978c2ecf20Sopenharmony_ci unsigned long flags; 1988c2ecf20Sopenharmony_ci int i; 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_ci spin_lock_irqsave(&phy_data->reg_lock, flags); 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci if (phy_data->cfg->phyctl_offset == REG_PHYCTL_A33) { 2038c2ecf20Sopenharmony_ci /* SoCs newer than A33 need us to set phyctl to 0 explicitly */ 2048c2ecf20Sopenharmony_ci writel(0, phyctl); 2058c2ecf20Sopenharmony_ci } 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_ci for (i = 0; i < len; i++) { 2088c2ecf20Sopenharmony_ci temp = readl(phyctl); 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_ci /* clear the address portion */ 2118c2ecf20Sopenharmony_ci temp &= ~(0xff << 8); 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_ci /* set the address */ 2148c2ecf20Sopenharmony_ci temp |= ((addr + i) << 8); 2158c2ecf20Sopenharmony_ci writel(temp, phyctl); 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_ci /* set the data bit and clear usbc bit*/ 2188c2ecf20Sopenharmony_ci temp = readb(phyctl); 2198c2ecf20Sopenharmony_ci if (data & 0x1) 2208c2ecf20Sopenharmony_ci temp |= PHYCTL_DATA; 2218c2ecf20Sopenharmony_ci else 2228c2ecf20Sopenharmony_ci temp &= ~PHYCTL_DATA; 2238c2ecf20Sopenharmony_ci temp &= ~usbc_bit; 2248c2ecf20Sopenharmony_ci writeb(temp, phyctl); 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_ci /* pulse usbc_bit */ 2278c2ecf20Sopenharmony_ci temp = readb(phyctl); 2288c2ecf20Sopenharmony_ci temp |= usbc_bit; 2298c2ecf20Sopenharmony_ci writeb(temp, phyctl); 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_ci temp = readb(phyctl); 2328c2ecf20Sopenharmony_ci temp &= ~usbc_bit; 2338c2ecf20Sopenharmony_ci writeb(temp, phyctl); 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci data >>= 1; 2368c2ecf20Sopenharmony_ci } 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&phy_data->reg_lock, flags); 2398c2ecf20Sopenharmony_ci} 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_cistatic void sun4i_usb_phy_passby(struct sun4i_usb_phy *phy, int enable) 2428c2ecf20Sopenharmony_ci{ 2438c2ecf20Sopenharmony_ci struct sun4i_usb_phy_data *phy_data = to_sun4i_usb_phy_data(phy); 2448c2ecf20Sopenharmony_ci u32 bits, reg_value; 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_ci if (!phy->pmu) 2478c2ecf20Sopenharmony_ci return; 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_ci bits = SUNXI_AHB_ICHR8_EN | SUNXI_AHB_INCR4_BURST_EN | 2508c2ecf20Sopenharmony_ci SUNXI_AHB_INCRX_ALIGN_EN | SUNXI_ULPI_BYPASS_EN; 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_ci /* A83T USB2 is HSIC */ 2538c2ecf20Sopenharmony_ci if (phy_data->cfg->type == sun8i_a83t_phy && phy->index == 2) 2548c2ecf20Sopenharmony_ci bits |= SUNXI_EHCI_HS_FORCE | SUNXI_HSIC_CONNECT_INT | 2558c2ecf20Sopenharmony_ci SUNXI_HSIC; 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_ci reg_value = readl(phy->pmu); 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_ci if (enable) 2608c2ecf20Sopenharmony_ci reg_value |= bits; 2618c2ecf20Sopenharmony_ci else 2628c2ecf20Sopenharmony_ci reg_value &= ~bits; 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_ci writel(reg_value, phy->pmu); 2658c2ecf20Sopenharmony_ci} 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_cistatic int sun4i_usb_phy_init(struct phy *_phy) 2688c2ecf20Sopenharmony_ci{ 2698c2ecf20Sopenharmony_ci struct sun4i_usb_phy *phy = phy_get_drvdata(_phy); 2708c2ecf20Sopenharmony_ci struct sun4i_usb_phy_data *data = to_sun4i_usb_phy_data(phy); 2718c2ecf20Sopenharmony_ci int ret; 2728c2ecf20Sopenharmony_ci u32 val; 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_ci ret = clk_prepare_enable(phy->clk); 2758c2ecf20Sopenharmony_ci if (ret) 2768c2ecf20Sopenharmony_ci return ret; 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_ci ret = clk_prepare_enable(phy->clk2); 2798c2ecf20Sopenharmony_ci if (ret) { 2808c2ecf20Sopenharmony_ci clk_disable_unprepare(phy->clk); 2818c2ecf20Sopenharmony_ci return ret; 2828c2ecf20Sopenharmony_ci } 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_ci ret = reset_control_deassert(phy->reset); 2858c2ecf20Sopenharmony_ci if (ret) { 2868c2ecf20Sopenharmony_ci clk_disable_unprepare(phy->clk2); 2878c2ecf20Sopenharmony_ci clk_disable_unprepare(phy->clk); 2888c2ecf20Sopenharmony_ci return ret; 2898c2ecf20Sopenharmony_ci } 2908c2ecf20Sopenharmony_ci 2918c2ecf20Sopenharmony_ci if (data->cfg->type == sun8i_a83t_phy || 2928c2ecf20Sopenharmony_ci data->cfg->type == sun50i_h6_phy) { 2938c2ecf20Sopenharmony_ci if (phy->index == 0) { 2948c2ecf20Sopenharmony_ci val = readl(data->base + data->cfg->phyctl_offset); 2958c2ecf20Sopenharmony_ci val |= PHY_CTL_VBUSVLDEXT; 2968c2ecf20Sopenharmony_ci val &= ~PHY_CTL_SIDDQ; 2978c2ecf20Sopenharmony_ci writel(val, data->base + data->cfg->phyctl_offset); 2988c2ecf20Sopenharmony_ci } 2998c2ecf20Sopenharmony_ci } else { 3008c2ecf20Sopenharmony_ci if (phy->pmu && data->cfg->enable_pmu_unk1) { 3018c2ecf20Sopenharmony_ci val = readl(phy->pmu + REG_PMU_UNK1); 3028c2ecf20Sopenharmony_ci writel(val & ~2, phy->pmu + REG_PMU_UNK1); 3038c2ecf20Sopenharmony_ci } 3048c2ecf20Sopenharmony_ci 3058c2ecf20Sopenharmony_ci /* Enable USB 45 Ohm resistor calibration */ 3068c2ecf20Sopenharmony_ci if (phy->index == 0) 3078c2ecf20Sopenharmony_ci sun4i_usb_phy_write(phy, PHY_RES45_CAL_EN, 0x01, 1); 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_ci /* Adjust PHY's magnitude and rate */ 3108c2ecf20Sopenharmony_ci sun4i_usb_phy_write(phy, PHY_TX_AMPLITUDE_TUNE, 0x14, 5); 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_ci /* Disconnect threshold adjustment */ 3138c2ecf20Sopenharmony_ci sun4i_usb_phy_write(phy, PHY_DISCON_TH_SEL, 3148c2ecf20Sopenharmony_ci data->cfg->disc_thresh, 2); 3158c2ecf20Sopenharmony_ci } 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_ci sun4i_usb_phy_passby(phy, 1); 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_ci if (phy->index == 0) { 3208c2ecf20Sopenharmony_ci data->phy0_init = true; 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_ci /* Enable pull-ups */ 3238c2ecf20Sopenharmony_ci sun4i_usb_phy0_update_iscr(_phy, 0, ISCR_DPDM_PULLUP_EN); 3248c2ecf20Sopenharmony_ci sun4i_usb_phy0_update_iscr(_phy, 0, ISCR_ID_PULLUP_EN); 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_ci /* Force ISCR and cable state updates */ 3278c2ecf20Sopenharmony_ci data->id_det = -1; 3288c2ecf20Sopenharmony_ci data->vbus_det = -1; 3298c2ecf20Sopenharmony_ci queue_delayed_work(system_wq, &data->detect, 0); 3308c2ecf20Sopenharmony_ci } 3318c2ecf20Sopenharmony_ci 3328c2ecf20Sopenharmony_ci return 0; 3338c2ecf20Sopenharmony_ci} 3348c2ecf20Sopenharmony_ci 3358c2ecf20Sopenharmony_cistatic int sun4i_usb_phy_exit(struct phy *_phy) 3368c2ecf20Sopenharmony_ci{ 3378c2ecf20Sopenharmony_ci struct sun4i_usb_phy *phy = phy_get_drvdata(_phy); 3388c2ecf20Sopenharmony_ci struct sun4i_usb_phy_data *data = to_sun4i_usb_phy_data(phy); 3398c2ecf20Sopenharmony_ci 3408c2ecf20Sopenharmony_ci if (phy->index == 0) { 3418c2ecf20Sopenharmony_ci if (data->cfg->type == sun8i_a83t_phy || 3428c2ecf20Sopenharmony_ci data->cfg->type == sun50i_h6_phy) { 3438c2ecf20Sopenharmony_ci void __iomem *phyctl = data->base + 3448c2ecf20Sopenharmony_ci data->cfg->phyctl_offset; 3458c2ecf20Sopenharmony_ci 3468c2ecf20Sopenharmony_ci writel(readl(phyctl) | PHY_CTL_SIDDQ, phyctl); 3478c2ecf20Sopenharmony_ci } 3488c2ecf20Sopenharmony_ci 3498c2ecf20Sopenharmony_ci /* Disable pull-ups */ 3508c2ecf20Sopenharmony_ci sun4i_usb_phy0_update_iscr(_phy, ISCR_DPDM_PULLUP_EN, 0); 3518c2ecf20Sopenharmony_ci sun4i_usb_phy0_update_iscr(_phy, ISCR_ID_PULLUP_EN, 0); 3528c2ecf20Sopenharmony_ci data->phy0_init = false; 3538c2ecf20Sopenharmony_ci } 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_ci sun4i_usb_phy_passby(phy, 0); 3568c2ecf20Sopenharmony_ci reset_control_assert(phy->reset); 3578c2ecf20Sopenharmony_ci clk_disable_unprepare(phy->clk2); 3588c2ecf20Sopenharmony_ci clk_disable_unprepare(phy->clk); 3598c2ecf20Sopenharmony_ci 3608c2ecf20Sopenharmony_ci return 0; 3618c2ecf20Sopenharmony_ci} 3628c2ecf20Sopenharmony_ci 3638c2ecf20Sopenharmony_cistatic int sun4i_usb_phy0_get_id_det(struct sun4i_usb_phy_data *data) 3648c2ecf20Sopenharmony_ci{ 3658c2ecf20Sopenharmony_ci switch (data->dr_mode) { 3668c2ecf20Sopenharmony_ci case USB_DR_MODE_OTG: 3678c2ecf20Sopenharmony_ci if (data->id_det_gpio) 3688c2ecf20Sopenharmony_ci return gpiod_get_value_cansleep(data->id_det_gpio); 3698c2ecf20Sopenharmony_ci else 3708c2ecf20Sopenharmony_ci return 1; /* Fallback to peripheral mode */ 3718c2ecf20Sopenharmony_ci case USB_DR_MODE_HOST: 3728c2ecf20Sopenharmony_ci return 0; 3738c2ecf20Sopenharmony_ci case USB_DR_MODE_PERIPHERAL: 3748c2ecf20Sopenharmony_ci default: 3758c2ecf20Sopenharmony_ci return 1; 3768c2ecf20Sopenharmony_ci } 3778c2ecf20Sopenharmony_ci} 3788c2ecf20Sopenharmony_ci 3798c2ecf20Sopenharmony_cistatic int sun4i_usb_phy0_get_vbus_det(struct sun4i_usb_phy_data *data) 3808c2ecf20Sopenharmony_ci{ 3818c2ecf20Sopenharmony_ci if (data->vbus_det_gpio) 3828c2ecf20Sopenharmony_ci return gpiod_get_value_cansleep(data->vbus_det_gpio); 3838c2ecf20Sopenharmony_ci 3848c2ecf20Sopenharmony_ci if (data->vbus_power_supply) { 3858c2ecf20Sopenharmony_ci union power_supply_propval val; 3868c2ecf20Sopenharmony_ci int r; 3878c2ecf20Sopenharmony_ci 3888c2ecf20Sopenharmony_ci r = power_supply_get_property(data->vbus_power_supply, 3898c2ecf20Sopenharmony_ci POWER_SUPPLY_PROP_PRESENT, &val); 3908c2ecf20Sopenharmony_ci if (r == 0) 3918c2ecf20Sopenharmony_ci return val.intval; 3928c2ecf20Sopenharmony_ci } 3938c2ecf20Sopenharmony_ci 3948c2ecf20Sopenharmony_ci /* Fallback: report vbus as high */ 3958c2ecf20Sopenharmony_ci return 1; 3968c2ecf20Sopenharmony_ci} 3978c2ecf20Sopenharmony_ci 3988c2ecf20Sopenharmony_cistatic bool sun4i_usb_phy0_have_vbus_det(struct sun4i_usb_phy_data *data) 3998c2ecf20Sopenharmony_ci{ 4008c2ecf20Sopenharmony_ci return data->vbus_det_gpio || data->vbus_power_supply; 4018c2ecf20Sopenharmony_ci} 4028c2ecf20Sopenharmony_ci 4038c2ecf20Sopenharmony_cistatic bool sun4i_usb_phy0_poll(struct sun4i_usb_phy_data *data) 4048c2ecf20Sopenharmony_ci{ 4058c2ecf20Sopenharmony_ci if ((data->id_det_gpio && data->id_det_irq <= 0) || 4068c2ecf20Sopenharmony_ci (data->vbus_det_gpio && data->vbus_det_irq <= 0)) 4078c2ecf20Sopenharmony_ci return true; 4088c2ecf20Sopenharmony_ci 4098c2ecf20Sopenharmony_ci /* 4108c2ecf20Sopenharmony_ci * The A31/A23/A33 companion pmics (AXP221/AXP223) do not 4118c2ecf20Sopenharmony_ci * generate vbus change interrupts when the board is driving 4128c2ecf20Sopenharmony_ci * vbus using the N_VBUSEN pin on the pmic, so we must poll 4138c2ecf20Sopenharmony_ci * when using the pmic for vbus-det _and_ we're driving vbus. 4148c2ecf20Sopenharmony_ci */ 4158c2ecf20Sopenharmony_ci if ((data->cfg->type == sun6i_a31_phy || 4168c2ecf20Sopenharmony_ci data->cfg->type == sun8i_a33_phy) && 4178c2ecf20Sopenharmony_ci data->vbus_power_supply && data->phys[0].regulator_on) 4188c2ecf20Sopenharmony_ci return true; 4198c2ecf20Sopenharmony_ci 4208c2ecf20Sopenharmony_ci return false; 4218c2ecf20Sopenharmony_ci} 4228c2ecf20Sopenharmony_ci 4238c2ecf20Sopenharmony_cistatic int sun4i_usb_phy_power_on(struct phy *_phy) 4248c2ecf20Sopenharmony_ci{ 4258c2ecf20Sopenharmony_ci struct sun4i_usb_phy *phy = phy_get_drvdata(_phy); 4268c2ecf20Sopenharmony_ci struct sun4i_usb_phy_data *data = to_sun4i_usb_phy_data(phy); 4278c2ecf20Sopenharmony_ci int ret; 4288c2ecf20Sopenharmony_ci 4298c2ecf20Sopenharmony_ci if (!phy->vbus || phy->regulator_on) 4308c2ecf20Sopenharmony_ci return 0; 4318c2ecf20Sopenharmony_ci 4328c2ecf20Sopenharmony_ci /* For phy0 only turn on Vbus if we don't have an ext. Vbus */ 4338c2ecf20Sopenharmony_ci if (phy->index == 0 && sun4i_usb_phy0_have_vbus_det(data) && 4348c2ecf20Sopenharmony_ci data->vbus_det) { 4358c2ecf20Sopenharmony_ci dev_warn(&_phy->dev, "External vbus detected, not enabling our own vbus\n"); 4368c2ecf20Sopenharmony_ci return 0; 4378c2ecf20Sopenharmony_ci } 4388c2ecf20Sopenharmony_ci 4398c2ecf20Sopenharmony_ci ret = regulator_enable(phy->vbus); 4408c2ecf20Sopenharmony_ci if (ret) 4418c2ecf20Sopenharmony_ci return ret; 4428c2ecf20Sopenharmony_ci 4438c2ecf20Sopenharmony_ci phy->regulator_on = true; 4448c2ecf20Sopenharmony_ci 4458c2ecf20Sopenharmony_ci /* We must report Vbus high within OTG_TIME_A_WAIT_VRISE msec. */ 4468c2ecf20Sopenharmony_ci if (phy->index == 0 && sun4i_usb_phy0_poll(data)) 4478c2ecf20Sopenharmony_ci mod_delayed_work(system_wq, &data->detect, DEBOUNCE_TIME); 4488c2ecf20Sopenharmony_ci 4498c2ecf20Sopenharmony_ci return 0; 4508c2ecf20Sopenharmony_ci} 4518c2ecf20Sopenharmony_ci 4528c2ecf20Sopenharmony_cistatic int sun4i_usb_phy_power_off(struct phy *_phy) 4538c2ecf20Sopenharmony_ci{ 4548c2ecf20Sopenharmony_ci struct sun4i_usb_phy *phy = phy_get_drvdata(_phy); 4558c2ecf20Sopenharmony_ci struct sun4i_usb_phy_data *data = to_sun4i_usb_phy_data(phy); 4568c2ecf20Sopenharmony_ci 4578c2ecf20Sopenharmony_ci if (!phy->vbus || !phy->regulator_on) 4588c2ecf20Sopenharmony_ci return 0; 4598c2ecf20Sopenharmony_ci 4608c2ecf20Sopenharmony_ci regulator_disable(phy->vbus); 4618c2ecf20Sopenharmony_ci phy->regulator_on = false; 4628c2ecf20Sopenharmony_ci 4638c2ecf20Sopenharmony_ci /* 4648c2ecf20Sopenharmony_ci * phy0 vbus typically slowly discharges, sometimes this causes the 4658c2ecf20Sopenharmony_ci * Vbus gpio to not trigger an edge irq on Vbus off, so force a rescan. 4668c2ecf20Sopenharmony_ci */ 4678c2ecf20Sopenharmony_ci if (phy->index == 0 && !sun4i_usb_phy0_poll(data)) 4688c2ecf20Sopenharmony_ci mod_delayed_work(system_wq, &data->detect, POLL_TIME); 4698c2ecf20Sopenharmony_ci 4708c2ecf20Sopenharmony_ci return 0; 4718c2ecf20Sopenharmony_ci} 4728c2ecf20Sopenharmony_ci 4738c2ecf20Sopenharmony_cistatic int sun4i_usb_phy_set_mode(struct phy *_phy, 4748c2ecf20Sopenharmony_ci enum phy_mode mode, int submode) 4758c2ecf20Sopenharmony_ci{ 4768c2ecf20Sopenharmony_ci struct sun4i_usb_phy *phy = phy_get_drvdata(_phy); 4778c2ecf20Sopenharmony_ci struct sun4i_usb_phy_data *data = to_sun4i_usb_phy_data(phy); 4788c2ecf20Sopenharmony_ci int new_mode; 4798c2ecf20Sopenharmony_ci 4808c2ecf20Sopenharmony_ci if (phy->index != 0) { 4818c2ecf20Sopenharmony_ci if (mode == PHY_MODE_USB_HOST) 4828c2ecf20Sopenharmony_ci return 0; 4838c2ecf20Sopenharmony_ci return -EINVAL; 4848c2ecf20Sopenharmony_ci } 4858c2ecf20Sopenharmony_ci 4868c2ecf20Sopenharmony_ci switch (mode) { 4878c2ecf20Sopenharmony_ci case PHY_MODE_USB_HOST: 4888c2ecf20Sopenharmony_ci new_mode = USB_DR_MODE_HOST; 4898c2ecf20Sopenharmony_ci break; 4908c2ecf20Sopenharmony_ci case PHY_MODE_USB_DEVICE: 4918c2ecf20Sopenharmony_ci new_mode = USB_DR_MODE_PERIPHERAL; 4928c2ecf20Sopenharmony_ci break; 4938c2ecf20Sopenharmony_ci case PHY_MODE_USB_OTG: 4948c2ecf20Sopenharmony_ci new_mode = USB_DR_MODE_OTG; 4958c2ecf20Sopenharmony_ci break; 4968c2ecf20Sopenharmony_ci default: 4978c2ecf20Sopenharmony_ci return -EINVAL; 4988c2ecf20Sopenharmony_ci } 4998c2ecf20Sopenharmony_ci 5008c2ecf20Sopenharmony_ci if (new_mode != data->dr_mode) { 5018c2ecf20Sopenharmony_ci dev_info(&_phy->dev, "Changing dr_mode to %d\n", new_mode); 5028c2ecf20Sopenharmony_ci data->dr_mode = new_mode; 5038c2ecf20Sopenharmony_ci } 5048c2ecf20Sopenharmony_ci 5058c2ecf20Sopenharmony_ci data->id_det = -1; /* Force reprocessing of id */ 5068c2ecf20Sopenharmony_ci data->force_session_end = true; 5078c2ecf20Sopenharmony_ci queue_delayed_work(system_wq, &data->detect, 0); 5088c2ecf20Sopenharmony_ci 5098c2ecf20Sopenharmony_ci return 0; 5108c2ecf20Sopenharmony_ci} 5118c2ecf20Sopenharmony_ci 5128c2ecf20Sopenharmony_civoid sun4i_usb_phy_set_squelch_detect(struct phy *_phy, bool enabled) 5138c2ecf20Sopenharmony_ci{ 5148c2ecf20Sopenharmony_ci struct sun4i_usb_phy *phy = phy_get_drvdata(_phy); 5158c2ecf20Sopenharmony_ci 5168c2ecf20Sopenharmony_ci sun4i_usb_phy_write(phy, PHY_SQUELCH_DETECT, enabled ? 0 : 2, 2); 5178c2ecf20Sopenharmony_ci} 5188c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(sun4i_usb_phy_set_squelch_detect); 5198c2ecf20Sopenharmony_ci 5208c2ecf20Sopenharmony_cistatic const struct phy_ops sun4i_usb_phy_ops = { 5218c2ecf20Sopenharmony_ci .init = sun4i_usb_phy_init, 5228c2ecf20Sopenharmony_ci .exit = sun4i_usb_phy_exit, 5238c2ecf20Sopenharmony_ci .power_on = sun4i_usb_phy_power_on, 5248c2ecf20Sopenharmony_ci .power_off = sun4i_usb_phy_power_off, 5258c2ecf20Sopenharmony_ci .set_mode = sun4i_usb_phy_set_mode, 5268c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 5278c2ecf20Sopenharmony_ci}; 5288c2ecf20Sopenharmony_ci 5298c2ecf20Sopenharmony_cistatic void sun4i_usb_phy0_reroute(struct sun4i_usb_phy_data *data, int id_det) 5308c2ecf20Sopenharmony_ci{ 5318c2ecf20Sopenharmony_ci u32 regval; 5328c2ecf20Sopenharmony_ci 5338c2ecf20Sopenharmony_ci regval = readl(data->base + REG_PHY_OTGCTL); 5348c2ecf20Sopenharmony_ci if (id_det == 0) { 5358c2ecf20Sopenharmony_ci /* Host mode. Route phy0 to EHCI/OHCI */ 5368c2ecf20Sopenharmony_ci regval &= ~OTGCTL_ROUTE_MUSB; 5378c2ecf20Sopenharmony_ci } else { 5388c2ecf20Sopenharmony_ci /* Peripheral mode. Route phy0 to MUSB */ 5398c2ecf20Sopenharmony_ci regval |= OTGCTL_ROUTE_MUSB; 5408c2ecf20Sopenharmony_ci } 5418c2ecf20Sopenharmony_ci writel(regval, data->base + REG_PHY_OTGCTL); 5428c2ecf20Sopenharmony_ci} 5438c2ecf20Sopenharmony_ci 5448c2ecf20Sopenharmony_cistatic void sun4i_usb_phy0_id_vbus_det_scan(struct work_struct *work) 5458c2ecf20Sopenharmony_ci{ 5468c2ecf20Sopenharmony_ci struct sun4i_usb_phy_data *data = 5478c2ecf20Sopenharmony_ci container_of(work, struct sun4i_usb_phy_data, detect.work); 5488c2ecf20Sopenharmony_ci struct phy *phy0 = data->phys[0].phy; 5498c2ecf20Sopenharmony_ci struct sun4i_usb_phy *phy; 5508c2ecf20Sopenharmony_ci bool force_session_end, id_notify = false, vbus_notify = false; 5518c2ecf20Sopenharmony_ci int id_det, vbus_det; 5528c2ecf20Sopenharmony_ci 5538c2ecf20Sopenharmony_ci if (!phy0) 5548c2ecf20Sopenharmony_ci return; 5558c2ecf20Sopenharmony_ci 5568c2ecf20Sopenharmony_ci phy = phy_get_drvdata(phy0); 5578c2ecf20Sopenharmony_ci id_det = sun4i_usb_phy0_get_id_det(data); 5588c2ecf20Sopenharmony_ci vbus_det = sun4i_usb_phy0_get_vbus_det(data); 5598c2ecf20Sopenharmony_ci 5608c2ecf20Sopenharmony_ci mutex_lock(&phy0->mutex); 5618c2ecf20Sopenharmony_ci 5628c2ecf20Sopenharmony_ci if (!data->phy0_init) { 5638c2ecf20Sopenharmony_ci mutex_unlock(&phy0->mutex); 5648c2ecf20Sopenharmony_ci return; 5658c2ecf20Sopenharmony_ci } 5668c2ecf20Sopenharmony_ci 5678c2ecf20Sopenharmony_ci force_session_end = data->force_session_end; 5688c2ecf20Sopenharmony_ci data->force_session_end = false; 5698c2ecf20Sopenharmony_ci 5708c2ecf20Sopenharmony_ci if (id_det != data->id_det) { 5718c2ecf20Sopenharmony_ci /* id-change, force session end if we've no vbus detection */ 5728c2ecf20Sopenharmony_ci if (data->dr_mode == USB_DR_MODE_OTG && 5738c2ecf20Sopenharmony_ci !sun4i_usb_phy0_have_vbus_det(data)) 5748c2ecf20Sopenharmony_ci force_session_end = true; 5758c2ecf20Sopenharmony_ci 5768c2ecf20Sopenharmony_ci /* When entering host mode (id = 0) force end the session now */ 5778c2ecf20Sopenharmony_ci if (force_session_end && id_det == 0) { 5788c2ecf20Sopenharmony_ci sun4i_usb_phy0_set_vbus_detect(phy0, 0); 5798c2ecf20Sopenharmony_ci msleep(200); 5808c2ecf20Sopenharmony_ci sun4i_usb_phy0_set_vbus_detect(phy0, 1); 5818c2ecf20Sopenharmony_ci } 5828c2ecf20Sopenharmony_ci sun4i_usb_phy0_set_id_detect(phy0, id_det); 5838c2ecf20Sopenharmony_ci data->id_det = id_det; 5848c2ecf20Sopenharmony_ci id_notify = true; 5858c2ecf20Sopenharmony_ci } 5868c2ecf20Sopenharmony_ci 5878c2ecf20Sopenharmony_ci if (vbus_det != data->vbus_det) { 5888c2ecf20Sopenharmony_ci sun4i_usb_phy0_set_vbus_detect(phy0, vbus_det); 5898c2ecf20Sopenharmony_ci data->vbus_det = vbus_det; 5908c2ecf20Sopenharmony_ci vbus_notify = true; 5918c2ecf20Sopenharmony_ci } 5928c2ecf20Sopenharmony_ci 5938c2ecf20Sopenharmony_ci mutex_unlock(&phy0->mutex); 5948c2ecf20Sopenharmony_ci 5958c2ecf20Sopenharmony_ci if (id_notify) { 5968c2ecf20Sopenharmony_ci extcon_set_state_sync(data->extcon, EXTCON_USB_HOST, 5978c2ecf20Sopenharmony_ci !id_det); 5988c2ecf20Sopenharmony_ci /* When leaving host mode force end the session here */ 5998c2ecf20Sopenharmony_ci if (force_session_end && id_det == 1) { 6008c2ecf20Sopenharmony_ci mutex_lock(&phy0->mutex); 6018c2ecf20Sopenharmony_ci sun4i_usb_phy0_set_vbus_detect(phy0, 0); 6028c2ecf20Sopenharmony_ci msleep(1000); 6038c2ecf20Sopenharmony_ci sun4i_usb_phy0_set_vbus_detect(phy0, 1); 6048c2ecf20Sopenharmony_ci mutex_unlock(&phy0->mutex); 6058c2ecf20Sopenharmony_ci } 6068c2ecf20Sopenharmony_ci 6078c2ecf20Sopenharmony_ci /* Enable PHY0 passby for host mode only. */ 6088c2ecf20Sopenharmony_ci sun4i_usb_phy_passby(phy, !id_det); 6098c2ecf20Sopenharmony_ci 6108c2ecf20Sopenharmony_ci /* Re-route PHY0 if necessary */ 6118c2ecf20Sopenharmony_ci if (data->cfg->phy0_dual_route) 6128c2ecf20Sopenharmony_ci sun4i_usb_phy0_reroute(data, id_det); 6138c2ecf20Sopenharmony_ci } 6148c2ecf20Sopenharmony_ci 6158c2ecf20Sopenharmony_ci if (vbus_notify) 6168c2ecf20Sopenharmony_ci extcon_set_state_sync(data->extcon, EXTCON_USB, vbus_det); 6178c2ecf20Sopenharmony_ci 6188c2ecf20Sopenharmony_ci if (sun4i_usb_phy0_poll(data)) 6198c2ecf20Sopenharmony_ci queue_delayed_work(system_wq, &data->detect, POLL_TIME); 6208c2ecf20Sopenharmony_ci} 6218c2ecf20Sopenharmony_ci 6228c2ecf20Sopenharmony_cistatic irqreturn_t sun4i_usb_phy0_id_vbus_det_irq(int irq, void *dev_id) 6238c2ecf20Sopenharmony_ci{ 6248c2ecf20Sopenharmony_ci struct sun4i_usb_phy_data *data = dev_id; 6258c2ecf20Sopenharmony_ci 6268c2ecf20Sopenharmony_ci /* vbus or id changed, let the pins settle and then scan them */ 6278c2ecf20Sopenharmony_ci mod_delayed_work(system_wq, &data->detect, DEBOUNCE_TIME); 6288c2ecf20Sopenharmony_ci 6298c2ecf20Sopenharmony_ci return IRQ_HANDLED; 6308c2ecf20Sopenharmony_ci} 6318c2ecf20Sopenharmony_ci 6328c2ecf20Sopenharmony_cistatic int sun4i_usb_phy0_vbus_notify(struct notifier_block *nb, 6338c2ecf20Sopenharmony_ci unsigned long val, void *v) 6348c2ecf20Sopenharmony_ci{ 6358c2ecf20Sopenharmony_ci struct sun4i_usb_phy_data *data = 6368c2ecf20Sopenharmony_ci container_of(nb, struct sun4i_usb_phy_data, vbus_power_nb); 6378c2ecf20Sopenharmony_ci struct power_supply *psy = v; 6388c2ecf20Sopenharmony_ci 6398c2ecf20Sopenharmony_ci /* Properties on the vbus_power_supply changed, scan vbus_det */ 6408c2ecf20Sopenharmony_ci if (val == PSY_EVENT_PROP_CHANGED && psy == data->vbus_power_supply) 6418c2ecf20Sopenharmony_ci mod_delayed_work(system_wq, &data->detect, DEBOUNCE_TIME); 6428c2ecf20Sopenharmony_ci 6438c2ecf20Sopenharmony_ci return NOTIFY_OK; 6448c2ecf20Sopenharmony_ci} 6458c2ecf20Sopenharmony_ci 6468c2ecf20Sopenharmony_cistatic struct phy *sun4i_usb_phy_xlate(struct device *dev, 6478c2ecf20Sopenharmony_ci struct of_phandle_args *args) 6488c2ecf20Sopenharmony_ci{ 6498c2ecf20Sopenharmony_ci struct sun4i_usb_phy_data *data = dev_get_drvdata(dev); 6508c2ecf20Sopenharmony_ci 6518c2ecf20Sopenharmony_ci if (args->args[0] >= data->cfg->num_phys) 6528c2ecf20Sopenharmony_ci return ERR_PTR(-ENODEV); 6538c2ecf20Sopenharmony_ci 6548c2ecf20Sopenharmony_ci if (data->cfg->missing_phys & BIT(args->args[0])) 6558c2ecf20Sopenharmony_ci return ERR_PTR(-ENODEV); 6568c2ecf20Sopenharmony_ci 6578c2ecf20Sopenharmony_ci return data->phys[args->args[0]].phy; 6588c2ecf20Sopenharmony_ci} 6598c2ecf20Sopenharmony_ci 6608c2ecf20Sopenharmony_cistatic int sun4i_usb_phy_remove(struct platform_device *pdev) 6618c2ecf20Sopenharmony_ci{ 6628c2ecf20Sopenharmony_ci struct device *dev = &pdev->dev; 6638c2ecf20Sopenharmony_ci struct sun4i_usb_phy_data *data = dev_get_drvdata(dev); 6648c2ecf20Sopenharmony_ci 6658c2ecf20Sopenharmony_ci if (data->vbus_power_nb_registered) 6668c2ecf20Sopenharmony_ci power_supply_unreg_notifier(&data->vbus_power_nb); 6678c2ecf20Sopenharmony_ci if (data->id_det_irq > 0) 6688c2ecf20Sopenharmony_ci devm_free_irq(dev, data->id_det_irq, data); 6698c2ecf20Sopenharmony_ci if (data->vbus_det_irq > 0) 6708c2ecf20Sopenharmony_ci devm_free_irq(dev, data->vbus_det_irq, data); 6718c2ecf20Sopenharmony_ci 6728c2ecf20Sopenharmony_ci cancel_delayed_work_sync(&data->detect); 6738c2ecf20Sopenharmony_ci 6748c2ecf20Sopenharmony_ci return 0; 6758c2ecf20Sopenharmony_ci} 6768c2ecf20Sopenharmony_ci 6778c2ecf20Sopenharmony_cistatic const unsigned int sun4i_usb_phy0_cable[] = { 6788c2ecf20Sopenharmony_ci EXTCON_USB, 6798c2ecf20Sopenharmony_ci EXTCON_USB_HOST, 6808c2ecf20Sopenharmony_ci EXTCON_NONE, 6818c2ecf20Sopenharmony_ci}; 6828c2ecf20Sopenharmony_ci 6838c2ecf20Sopenharmony_cistatic int sun4i_usb_phy_probe(struct platform_device *pdev) 6848c2ecf20Sopenharmony_ci{ 6858c2ecf20Sopenharmony_ci struct sun4i_usb_phy_data *data; 6868c2ecf20Sopenharmony_ci struct device *dev = &pdev->dev; 6878c2ecf20Sopenharmony_ci struct device_node *np = dev->of_node; 6888c2ecf20Sopenharmony_ci struct phy_provider *phy_provider; 6898c2ecf20Sopenharmony_ci struct resource *res; 6908c2ecf20Sopenharmony_ci int i, ret; 6918c2ecf20Sopenharmony_ci 6928c2ecf20Sopenharmony_ci data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); 6938c2ecf20Sopenharmony_ci if (!data) 6948c2ecf20Sopenharmony_ci return -ENOMEM; 6958c2ecf20Sopenharmony_ci 6968c2ecf20Sopenharmony_ci spin_lock_init(&data->reg_lock); 6978c2ecf20Sopenharmony_ci INIT_DELAYED_WORK(&data->detect, sun4i_usb_phy0_id_vbus_det_scan); 6988c2ecf20Sopenharmony_ci dev_set_drvdata(dev, data); 6998c2ecf20Sopenharmony_ci data->cfg = of_device_get_match_data(dev); 7008c2ecf20Sopenharmony_ci if (!data->cfg) 7018c2ecf20Sopenharmony_ci return -EINVAL; 7028c2ecf20Sopenharmony_ci 7038c2ecf20Sopenharmony_ci res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "phy_ctrl"); 7048c2ecf20Sopenharmony_ci data->base = devm_ioremap_resource(dev, res); 7058c2ecf20Sopenharmony_ci if (IS_ERR(data->base)) 7068c2ecf20Sopenharmony_ci return PTR_ERR(data->base); 7078c2ecf20Sopenharmony_ci 7088c2ecf20Sopenharmony_ci data->id_det_gpio = devm_gpiod_get_optional(dev, "usb0_id_det", 7098c2ecf20Sopenharmony_ci GPIOD_IN); 7108c2ecf20Sopenharmony_ci if (IS_ERR(data->id_det_gpio)) { 7118c2ecf20Sopenharmony_ci dev_err(dev, "Couldn't request ID GPIO\n"); 7128c2ecf20Sopenharmony_ci return PTR_ERR(data->id_det_gpio); 7138c2ecf20Sopenharmony_ci } 7148c2ecf20Sopenharmony_ci 7158c2ecf20Sopenharmony_ci data->vbus_det_gpio = devm_gpiod_get_optional(dev, "usb0_vbus_det", 7168c2ecf20Sopenharmony_ci GPIOD_IN); 7178c2ecf20Sopenharmony_ci if (IS_ERR(data->vbus_det_gpio)) { 7188c2ecf20Sopenharmony_ci dev_err(dev, "Couldn't request VBUS detect GPIO\n"); 7198c2ecf20Sopenharmony_ci return PTR_ERR(data->vbus_det_gpio); 7208c2ecf20Sopenharmony_ci } 7218c2ecf20Sopenharmony_ci 7228c2ecf20Sopenharmony_ci if (of_find_property(np, "usb0_vbus_power-supply", NULL)) { 7238c2ecf20Sopenharmony_ci data->vbus_power_supply = devm_power_supply_get_by_phandle(dev, 7248c2ecf20Sopenharmony_ci "usb0_vbus_power-supply"); 7258c2ecf20Sopenharmony_ci if (IS_ERR(data->vbus_power_supply)) { 7268c2ecf20Sopenharmony_ci dev_err(dev, "Couldn't get the VBUS power supply\n"); 7278c2ecf20Sopenharmony_ci return PTR_ERR(data->vbus_power_supply); 7288c2ecf20Sopenharmony_ci } 7298c2ecf20Sopenharmony_ci 7308c2ecf20Sopenharmony_ci if (!data->vbus_power_supply) 7318c2ecf20Sopenharmony_ci return -EPROBE_DEFER; 7328c2ecf20Sopenharmony_ci } 7338c2ecf20Sopenharmony_ci 7348c2ecf20Sopenharmony_ci data->dr_mode = of_usb_get_dr_mode_by_phy(np, 0); 7358c2ecf20Sopenharmony_ci 7368c2ecf20Sopenharmony_ci data->extcon = devm_extcon_dev_allocate(dev, sun4i_usb_phy0_cable); 7378c2ecf20Sopenharmony_ci if (IS_ERR(data->extcon)) { 7388c2ecf20Sopenharmony_ci dev_err(dev, "Couldn't allocate our extcon device\n"); 7398c2ecf20Sopenharmony_ci return PTR_ERR(data->extcon); 7408c2ecf20Sopenharmony_ci } 7418c2ecf20Sopenharmony_ci 7428c2ecf20Sopenharmony_ci ret = devm_extcon_dev_register(dev, data->extcon); 7438c2ecf20Sopenharmony_ci if (ret) { 7448c2ecf20Sopenharmony_ci dev_err(dev, "failed to register extcon: %d\n", ret); 7458c2ecf20Sopenharmony_ci return ret; 7468c2ecf20Sopenharmony_ci } 7478c2ecf20Sopenharmony_ci 7488c2ecf20Sopenharmony_ci for (i = 0; i < data->cfg->num_phys; i++) { 7498c2ecf20Sopenharmony_ci struct sun4i_usb_phy *phy = data->phys + i; 7508c2ecf20Sopenharmony_ci char name[16]; 7518c2ecf20Sopenharmony_ci 7528c2ecf20Sopenharmony_ci if (data->cfg->missing_phys & BIT(i)) 7538c2ecf20Sopenharmony_ci continue; 7548c2ecf20Sopenharmony_ci 7558c2ecf20Sopenharmony_ci snprintf(name, sizeof(name), "usb%d_vbus", i); 7568c2ecf20Sopenharmony_ci phy->vbus = devm_regulator_get_optional(dev, name); 7578c2ecf20Sopenharmony_ci if (IS_ERR(phy->vbus)) { 7588c2ecf20Sopenharmony_ci if (PTR_ERR(phy->vbus) == -EPROBE_DEFER) { 7598c2ecf20Sopenharmony_ci dev_err(dev, 7608c2ecf20Sopenharmony_ci "Couldn't get regulator %s... Deferring probe\n", 7618c2ecf20Sopenharmony_ci name); 7628c2ecf20Sopenharmony_ci return -EPROBE_DEFER; 7638c2ecf20Sopenharmony_ci } 7648c2ecf20Sopenharmony_ci 7658c2ecf20Sopenharmony_ci phy->vbus = NULL; 7668c2ecf20Sopenharmony_ci } 7678c2ecf20Sopenharmony_ci 7688c2ecf20Sopenharmony_ci if (data->cfg->dedicated_clocks) 7698c2ecf20Sopenharmony_ci snprintf(name, sizeof(name), "usb%d_phy", i); 7708c2ecf20Sopenharmony_ci else 7718c2ecf20Sopenharmony_ci strlcpy(name, "usb_phy", sizeof(name)); 7728c2ecf20Sopenharmony_ci 7738c2ecf20Sopenharmony_ci phy->clk = devm_clk_get(dev, name); 7748c2ecf20Sopenharmony_ci if (IS_ERR(phy->clk)) { 7758c2ecf20Sopenharmony_ci dev_err(dev, "failed to get clock %s\n", name); 7768c2ecf20Sopenharmony_ci return PTR_ERR(phy->clk); 7778c2ecf20Sopenharmony_ci } 7788c2ecf20Sopenharmony_ci 7798c2ecf20Sopenharmony_ci /* The first PHY is always tied to OTG, and never HSIC */ 7808c2ecf20Sopenharmony_ci if (data->cfg->hsic_index && i == data->cfg->hsic_index) { 7818c2ecf20Sopenharmony_ci /* HSIC needs secondary clock */ 7828c2ecf20Sopenharmony_ci snprintf(name, sizeof(name), "usb%d_hsic_12M", i); 7838c2ecf20Sopenharmony_ci phy->clk2 = devm_clk_get(dev, name); 7848c2ecf20Sopenharmony_ci if (IS_ERR(phy->clk2)) { 7858c2ecf20Sopenharmony_ci dev_err(dev, "failed to get clock %s\n", name); 7868c2ecf20Sopenharmony_ci return PTR_ERR(phy->clk2); 7878c2ecf20Sopenharmony_ci } 7888c2ecf20Sopenharmony_ci } 7898c2ecf20Sopenharmony_ci 7908c2ecf20Sopenharmony_ci snprintf(name, sizeof(name), "usb%d_reset", i); 7918c2ecf20Sopenharmony_ci phy->reset = devm_reset_control_get(dev, name); 7928c2ecf20Sopenharmony_ci if (IS_ERR(phy->reset)) { 7938c2ecf20Sopenharmony_ci dev_err(dev, "failed to get reset %s\n", name); 7948c2ecf20Sopenharmony_ci return PTR_ERR(phy->reset); 7958c2ecf20Sopenharmony_ci } 7968c2ecf20Sopenharmony_ci 7978c2ecf20Sopenharmony_ci if (i || data->cfg->phy0_dual_route) { /* No pmu for musb */ 7988c2ecf20Sopenharmony_ci snprintf(name, sizeof(name), "pmu%d", i); 7998c2ecf20Sopenharmony_ci res = platform_get_resource_byname(pdev, 8008c2ecf20Sopenharmony_ci IORESOURCE_MEM, name); 8018c2ecf20Sopenharmony_ci phy->pmu = devm_ioremap_resource(dev, res); 8028c2ecf20Sopenharmony_ci if (IS_ERR(phy->pmu)) 8038c2ecf20Sopenharmony_ci return PTR_ERR(phy->pmu); 8048c2ecf20Sopenharmony_ci } 8058c2ecf20Sopenharmony_ci 8068c2ecf20Sopenharmony_ci phy->phy = devm_phy_create(dev, NULL, &sun4i_usb_phy_ops); 8078c2ecf20Sopenharmony_ci if (IS_ERR(phy->phy)) { 8088c2ecf20Sopenharmony_ci dev_err(dev, "failed to create PHY %d\n", i); 8098c2ecf20Sopenharmony_ci return PTR_ERR(phy->phy); 8108c2ecf20Sopenharmony_ci } 8118c2ecf20Sopenharmony_ci 8128c2ecf20Sopenharmony_ci phy->index = i; 8138c2ecf20Sopenharmony_ci phy_set_drvdata(phy->phy, &data->phys[i]); 8148c2ecf20Sopenharmony_ci } 8158c2ecf20Sopenharmony_ci 8168c2ecf20Sopenharmony_ci data->id_det_irq = gpiod_to_irq(data->id_det_gpio); 8178c2ecf20Sopenharmony_ci if (data->id_det_irq > 0) { 8188c2ecf20Sopenharmony_ci ret = devm_request_irq(dev, data->id_det_irq, 8198c2ecf20Sopenharmony_ci sun4i_usb_phy0_id_vbus_det_irq, 8208c2ecf20Sopenharmony_ci IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, 8218c2ecf20Sopenharmony_ci "usb0-id-det", data); 8228c2ecf20Sopenharmony_ci if (ret) { 8238c2ecf20Sopenharmony_ci dev_err(dev, "Err requesting id-det-irq: %d\n", ret); 8248c2ecf20Sopenharmony_ci return ret; 8258c2ecf20Sopenharmony_ci } 8268c2ecf20Sopenharmony_ci } 8278c2ecf20Sopenharmony_ci 8288c2ecf20Sopenharmony_ci data->vbus_det_irq = gpiod_to_irq(data->vbus_det_gpio); 8298c2ecf20Sopenharmony_ci if (data->vbus_det_irq > 0) { 8308c2ecf20Sopenharmony_ci ret = devm_request_irq(dev, data->vbus_det_irq, 8318c2ecf20Sopenharmony_ci sun4i_usb_phy0_id_vbus_det_irq, 8328c2ecf20Sopenharmony_ci IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, 8338c2ecf20Sopenharmony_ci "usb0-vbus-det", data); 8348c2ecf20Sopenharmony_ci if (ret) { 8358c2ecf20Sopenharmony_ci dev_err(dev, "Err requesting vbus-det-irq: %d\n", ret); 8368c2ecf20Sopenharmony_ci data->vbus_det_irq = -1; 8378c2ecf20Sopenharmony_ci sun4i_usb_phy_remove(pdev); /* Stop detect work */ 8388c2ecf20Sopenharmony_ci return ret; 8398c2ecf20Sopenharmony_ci } 8408c2ecf20Sopenharmony_ci } 8418c2ecf20Sopenharmony_ci 8428c2ecf20Sopenharmony_ci if (data->vbus_power_supply) { 8438c2ecf20Sopenharmony_ci data->vbus_power_nb.notifier_call = sun4i_usb_phy0_vbus_notify; 8448c2ecf20Sopenharmony_ci data->vbus_power_nb.priority = 0; 8458c2ecf20Sopenharmony_ci ret = power_supply_reg_notifier(&data->vbus_power_nb); 8468c2ecf20Sopenharmony_ci if (ret) { 8478c2ecf20Sopenharmony_ci sun4i_usb_phy_remove(pdev); /* Stop detect work */ 8488c2ecf20Sopenharmony_ci return ret; 8498c2ecf20Sopenharmony_ci } 8508c2ecf20Sopenharmony_ci data->vbus_power_nb_registered = true; 8518c2ecf20Sopenharmony_ci } 8528c2ecf20Sopenharmony_ci 8538c2ecf20Sopenharmony_ci phy_provider = devm_of_phy_provider_register(dev, sun4i_usb_phy_xlate); 8548c2ecf20Sopenharmony_ci if (IS_ERR(phy_provider)) { 8558c2ecf20Sopenharmony_ci sun4i_usb_phy_remove(pdev); /* Stop detect work */ 8568c2ecf20Sopenharmony_ci return PTR_ERR(phy_provider); 8578c2ecf20Sopenharmony_ci } 8588c2ecf20Sopenharmony_ci 8598c2ecf20Sopenharmony_ci dev_dbg(dev, "successfully loaded\n"); 8608c2ecf20Sopenharmony_ci 8618c2ecf20Sopenharmony_ci return 0; 8628c2ecf20Sopenharmony_ci} 8638c2ecf20Sopenharmony_ci 8648c2ecf20Sopenharmony_cistatic const struct sun4i_usb_phy_cfg sun4i_a10_cfg = { 8658c2ecf20Sopenharmony_ci .num_phys = 3, 8668c2ecf20Sopenharmony_ci .type = sun4i_a10_phy, 8678c2ecf20Sopenharmony_ci .disc_thresh = 3, 8688c2ecf20Sopenharmony_ci .phyctl_offset = REG_PHYCTL_A10, 8698c2ecf20Sopenharmony_ci .dedicated_clocks = false, 8708c2ecf20Sopenharmony_ci .enable_pmu_unk1 = false, 8718c2ecf20Sopenharmony_ci}; 8728c2ecf20Sopenharmony_ci 8738c2ecf20Sopenharmony_cistatic const struct sun4i_usb_phy_cfg sun5i_a13_cfg = { 8748c2ecf20Sopenharmony_ci .num_phys = 2, 8758c2ecf20Sopenharmony_ci .type = sun4i_a10_phy, 8768c2ecf20Sopenharmony_ci .disc_thresh = 2, 8778c2ecf20Sopenharmony_ci .phyctl_offset = REG_PHYCTL_A10, 8788c2ecf20Sopenharmony_ci .dedicated_clocks = false, 8798c2ecf20Sopenharmony_ci .enable_pmu_unk1 = false, 8808c2ecf20Sopenharmony_ci}; 8818c2ecf20Sopenharmony_ci 8828c2ecf20Sopenharmony_cistatic const struct sun4i_usb_phy_cfg sun6i_a31_cfg = { 8838c2ecf20Sopenharmony_ci .num_phys = 3, 8848c2ecf20Sopenharmony_ci .type = sun6i_a31_phy, 8858c2ecf20Sopenharmony_ci .disc_thresh = 3, 8868c2ecf20Sopenharmony_ci .phyctl_offset = REG_PHYCTL_A10, 8878c2ecf20Sopenharmony_ci .dedicated_clocks = true, 8888c2ecf20Sopenharmony_ci .enable_pmu_unk1 = false, 8898c2ecf20Sopenharmony_ci}; 8908c2ecf20Sopenharmony_ci 8918c2ecf20Sopenharmony_cistatic const struct sun4i_usb_phy_cfg sun7i_a20_cfg = { 8928c2ecf20Sopenharmony_ci .num_phys = 3, 8938c2ecf20Sopenharmony_ci .type = sun4i_a10_phy, 8948c2ecf20Sopenharmony_ci .disc_thresh = 2, 8958c2ecf20Sopenharmony_ci .phyctl_offset = REG_PHYCTL_A10, 8968c2ecf20Sopenharmony_ci .dedicated_clocks = false, 8978c2ecf20Sopenharmony_ci .enable_pmu_unk1 = false, 8988c2ecf20Sopenharmony_ci}; 8998c2ecf20Sopenharmony_ci 9008c2ecf20Sopenharmony_cistatic const struct sun4i_usb_phy_cfg sun8i_a23_cfg = { 9018c2ecf20Sopenharmony_ci .num_phys = 2, 9028c2ecf20Sopenharmony_ci .type = sun6i_a31_phy, 9038c2ecf20Sopenharmony_ci .disc_thresh = 3, 9048c2ecf20Sopenharmony_ci .phyctl_offset = REG_PHYCTL_A10, 9058c2ecf20Sopenharmony_ci .dedicated_clocks = true, 9068c2ecf20Sopenharmony_ci .enable_pmu_unk1 = false, 9078c2ecf20Sopenharmony_ci}; 9088c2ecf20Sopenharmony_ci 9098c2ecf20Sopenharmony_cistatic const struct sun4i_usb_phy_cfg sun8i_a33_cfg = { 9108c2ecf20Sopenharmony_ci .num_phys = 2, 9118c2ecf20Sopenharmony_ci .type = sun8i_a33_phy, 9128c2ecf20Sopenharmony_ci .disc_thresh = 3, 9138c2ecf20Sopenharmony_ci .phyctl_offset = REG_PHYCTL_A33, 9148c2ecf20Sopenharmony_ci .dedicated_clocks = true, 9158c2ecf20Sopenharmony_ci .enable_pmu_unk1 = false, 9168c2ecf20Sopenharmony_ci}; 9178c2ecf20Sopenharmony_ci 9188c2ecf20Sopenharmony_cistatic const struct sun4i_usb_phy_cfg sun8i_a83t_cfg = { 9198c2ecf20Sopenharmony_ci .num_phys = 3, 9208c2ecf20Sopenharmony_ci .hsic_index = 2, 9218c2ecf20Sopenharmony_ci .type = sun8i_a83t_phy, 9228c2ecf20Sopenharmony_ci .phyctl_offset = REG_PHYCTL_A33, 9238c2ecf20Sopenharmony_ci .dedicated_clocks = true, 9248c2ecf20Sopenharmony_ci}; 9258c2ecf20Sopenharmony_ci 9268c2ecf20Sopenharmony_cistatic const struct sun4i_usb_phy_cfg sun8i_h3_cfg = { 9278c2ecf20Sopenharmony_ci .num_phys = 4, 9288c2ecf20Sopenharmony_ci .type = sun8i_h3_phy, 9298c2ecf20Sopenharmony_ci .disc_thresh = 3, 9308c2ecf20Sopenharmony_ci .phyctl_offset = REG_PHYCTL_A33, 9318c2ecf20Sopenharmony_ci .dedicated_clocks = true, 9328c2ecf20Sopenharmony_ci .enable_pmu_unk1 = true, 9338c2ecf20Sopenharmony_ci .phy0_dual_route = true, 9348c2ecf20Sopenharmony_ci}; 9358c2ecf20Sopenharmony_ci 9368c2ecf20Sopenharmony_cistatic const struct sun4i_usb_phy_cfg sun8i_r40_cfg = { 9378c2ecf20Sopenharmony_ci .num_phys = 3, 9388c2ecf20Sopenharmony_ci .type = sun8i_r40_phy, 9398c2ecf20Sopenharmony_ci .disc_thresh = 3, 9408c2ecf20Sopenharmony_ci .phyctl_offset = REG_PHYCTL_A33, 9418c2ecf20Sopenharmony_ci .dedicated_clocks = true, 9428c2ecf20Sopenharmony_ci .enable_pmu_unk1 = true, 9438c2ecf20Sopenharmony_ci .phy0_dual_route = true, 9448c2ecf20Sopenharmony_ci}; 9458c2ecf20Sopenharmony_ci 9468c2ecf20Sopenharmony_cistatic const struct sun4i_usb_phy_cfg sun8i_v3s_cfg = { 9478c2ecf20Sopenharmony_ci .num_phys = 1, 9488c2ecf20Sopenharmony_ci .type = sun8i_v3s_phy, 9498c2ecf20Sopenharmony_ci .disc_thresh = 3, 9508c2ecf20Sopenharmony_ci .phyctl_offset = REG_PHYCTL_A33, 9518c2ecf20Sopenharmony_ci .dedicated_clocks = true, 9528c2ecf20Sopenharmony_ci .enable_pmu_unk1 = true, 9538c2ecf20Sopenharmony_ci .phy0_dual_route = true, 9548c2ecf20Sopenharmony_ci}; 9558c2ecf20Sopenharmony_ci 9568c2ecf20Sopenharmony_cistatic const struct sun4i_usb_phy_cfg sun50i_a64_cfg = { 9578c2ecf20Sopenharmony_ci .num_phys = 2, 9588c2ecf20Sopenharmony_ci .type = sun50i_a64_phy, 9598c2ecf20Sopenharmony_ci .disc_thresh = 3, 9608c2ecf20Sopenharmony_ci .phyctl_offset = REG_PHYCTL_A33, 9618c2ecf20Sopenharmony_ci .dedicated_clocks = true, 9628c2ecf20Sopenharmony_ci .enable_pmu_unk1 = true, 9638c2ecf20Sopenharmony_ci .phy0_dual_route = true, 9648c2ecf20Sopenharmony_ci}; 9658c2ecf20Sopenharmony_ci 9668c2ecf20Sopenharmony_cistatic const struct sun4i_usb_phy_cfg sun50i_h6_cfg = { 9678c2ecf20Sopenharmony_ci .num_phys = 4, 9688c2ecf20Sopenharmony_ci .type = sun50i_h6_phy, 9698c2ecf20Sopenharmony_ci .disc_thresh = 3, 9708c2ecf20Sopenharmony_ci .phyctl_offset = REG_PHYCTL_A33, 9718c2ecf20Sopenharmony_ci .dedicated_clocks = true, 9728c2ecf20Sopenharmony_ci .enable_pmu_unk1 = true, 9738c2ecf20Sopenharmony_ci .phy0_dual_route = true, 9748c2ecf20Sopenharmony_ci .missing_phys = BIT(1) | BIT(2), 9758c2ecf20Sopenharmony_ci}; 9768c2ecf20Sopenharmony_ci 9778c2ecf20Sopenharmony_cistatic const struct of_device_id sun4i_usb_phy_of_match[] = { 9788c2ecf20Sopenharmony_ci { .compatible = "allwinner,sun4i-a10-usb-phy", .data = &sun4i_a10_cfg }, 9798c2ecf20Sopenharmony_ci { .compatible = "allwinner,sun5i-a13-usb-phy", .data = &sun5i_a13_cfg }, 9808c2ecf20Sopenharmony_ci { .compatible = "allwinner,sun6i-a31-usb-phy", .data = &sun6i_a31_cfg }, 9818c2ecf20Sopenharmony_ci { .compatible = "allwinner,sun7i-a20-usb-phy", .data = &sun7i_a20_cfg }, 9828c2ecf20Sopenharmony_ci { .compatible = "allwinner,sun8i-a23-usb-phy", .data = &sun8i_a23_cfg }, 9838c2ecf20Sopenharmony_ci { .compatible = "allwinner,sun8i-a33-usb-phy", .data = &sun8i_a33_cfg }, 9848c2ecf20Sopenharmony_ci { .compatible = "allwinner,sun8i-a83t-usb-phy", .data = &sun8i_a83t_cfg }, 9858c2ecf20Sopenharmony_ci { .compatible = "allwinner,sun8i-h3-usb-phy", .data = &sun8i_h3_cfg }, 9868c2ecf20Sopenharmony_ci { .compatible = "allwinner,sun8i-r40-usb-phy", .data = &sun8i_r40_cfg }, 9878c2ecf20Sopenharmony_ci { .compatible = "allwinner,sun8i-v3s-usb-phy", .data = &sun8i_v3s_cfg }, 9888c2ecf20Sopenharmony_ci { .compatible = "allwinner,sun50i-a64-usb-phy", 9898c2ecf20Sopenharmony_ci .data = &sun50i_a64_cfg}, 9908c2ecf20Sopenharmony_ci { .compatible = "allwinner,sun50i-h6-usb-phy", .data = &sun50i_h6_cfg }, 9918c2ecf20Sopenharmony_ci { }, 9928c2ecf20Sopenharmony_ci}; 9938c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, sun4i_usb_phy_of_match); 9948c2ecf20Sopenharmony_ci 9958c2ecf20Sopenharmony_cistatic struct platform_driver sun4i_usb_phy_driver = { 9968c2ecf20Sopenharmony_ci .probe = sun4i_usb_phy_probe, 9978c2ecf20Sopenharmony_ci .remove = sun4i_usb_phy_remove, 9988c2ecf20Sopenharmony_ci .driver = { 9998c2ecf20Sopenharmony_ci .of_match_table = sun4i_usb_phy_of_match, 10008c2ecf20Sopenharmony_ci .name = "sun4i-usb-phy", 10018c2ecf20Sopenharmony_ci } 10028c2ecf20Sopenharmony_ci}; 10038c2ecf20Sopenharmony_cimodule_platform_driver(sun4i_usb_phy_driver); 10048c2ecf20Sopenharmony_ci 10058c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Allwinner sun4i USB phy driver"); 10068c2ecf20Sopenharmony_ciMODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>"); 10078c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 1008