commit
809e0e398a
@ -0,0 +1,65 @@ |
||||
// SPDX-License-Identifier: (GPL-2.0+ OR MIT) |
||||
/* |
||||
* Copyright (C) 2018 Amarula Solutions B.V. |
||||
* Author: Jagan Teki <jagan@amarulasolutions.com> |
||||
*/ |
||||
|
||||
/dts-v1/; |
||||
|
||||
#include "sun50i-a64.dtsi" |
||||
|
||||
#include <dt-bindings/gpio/gpio.h> |
||||
|
||||
/ { |
||||
model = "Amarula A64-Relic"; |
||||
compatible = "amarula,a64-relic", "allwinner,sun50i-a64"; |
||||
|
||||
aliases { |
||||
serial0 = &uart0; |
||||
}; |
||||
|
||||
chosen { |
||||
stdout-path = "serial0:115200n8"; |
||||
}; |
||||
|
||||
reg_vcc3v3: vcc3v3 { |
||||
compatible = "regulator-fixed"; |
||||
regulator-name = "vcc3v3"; |
||||
regulator-min-microvolt = <3300000>; |
||||
regulator-max-microvolt = <3300000>; |
||||
}; |
||||
}; |
||||
|
||||
&ehci0 { |
||||
status = "okay"; |
||||
}; |
||||
|
||||
&mmc2 { |
||||
pinctrl-names = "default"; |
||||
pinctrl-0 = <&mmc2_pins>; |
||||
vmmc-supply = <®_vcc3v3>; |
||||
bus-width = <8>; |
||||
non-removable; |
||||
cap-mmc-hw-reset; |
||||
status = "okay"; |
||||
}; |
||||
|
||||
&ohci0 { |
||||
status = "okay"; |
||||
}; |
||||
|
||||
&uart0 { |
||||
pinctrl-names = "default"; |
||||
pinctrl-0 = <&uart0_pins_a>; |
||||
status = "okay"; |
||||
}; |
||||
|
||||
&usb_otg { |
||||
dr_mode = "otg"; |
||||
status = "okay"; |
||||
}; |
||||
|
||||
&usbphy { |
||||
usb0_id_det-gpios = <&pio 7 9 GPIO_ACTIVE_HIGH>; /* PH9 */ |
||||
status = "okay"; |
||||
}; |
@ -1,20 +0,0 @@ |
||||
/* SPDX-License-Identifier: GPL-2.0+ */ |
||||
/*
|
||||
* Sunxi usb-phy code |
||||
* |
||||
* Copyright (C) 2015 Hans de Goede <hdegoede@redhat.com> |
||||
* Copyright (C) 2014 Roman Byshko <rbyshko@gmail.com> |
||||
* |
||||
* Based on code from |
||||
* Allwinner Technology Co., Ltd. <www.allwinnertech.com> |
||||
*/ |
||||
|
||||
int sunxi_usb_phy_probe(void); |
||||
int sunxi_usb_phy_remove(void); |
||||
void sunxi_usb_phy_init(int index); |
||||
void sunxi_usb_phy_exit(int index); |
||||
void sunxi_usb_phy_power_on(int index); |
||||
void sunxi_usb_phy_power_off(int index); |
||||
int sunxi_usb_phy_vbus_detect(int index); |
||||
int sunxi_usb_phy_id_detect(int index); |
||||
void sunxi_usb_phy_enable_squelch_detect(int index, int enable); |
@ -1,406 +0,0 @@ |
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Sunxi usb-phy code |
||||
* |
||||
* Copyright (C) 2015 Hans de Goede <hdegoede@redhat.com> |
||||
* Copyright (C) 2014 Roman Byshko <rbyshko@gmail.com> |
||||
* |
||||
* Based on code from |
||||
* Allwinner Technology Co., Ltd. <www.allwinnertech.com> |
||||
*/ |
||||
|
||||
#include <common.h> |
||||
#include <asm/arch/clock.h> |
||||
#include <asm/arch/cpu.h> |
||||
#include <asm/arch/usb_phy.h> |
||||
#include <asm/gpio.h> |
||||
#include <asm/io.h> |
||||
#include <errno.h> |
||||
|
||||
#if defined(CONFIG_MACH_SUN4I) || \ |
||||
defined(CONFIG_MACH_SUN5I) || \
|
||||
defined(CONFIG_MACH_SUN6I) || \
|
||||
defined(CONFIG_MACH_SUN7I) || \
|
||||
defined(CONFIG_MACH_SUN8I_A23) || \
|
||||
defined(CONFIG_MACH_SUN9I) |
||||
#define SUNXI_USB_CSR 0x404 |
||||
#else |
||||
#define SUNXI_USB_CSR 0x410 |
||||
#endif |
||||
|
||||
#define SUNXI_USB_PMU_IRQ_ENABLE 0x800 |
||||
#define SUNXI_USB_PASSBY_EN 1 |
||||
|
||||
#define SUNXI_EHCI_AHB_ICHR8_EN (1 << 10) |
||||
#define SUNXI_EHCI_AHB_INCR4_BURST_EN (1 << 9) |
||||
#define SUNXI_EHCI_AHB_INCRX_ALIGN_EN (1 << 8) |
||||
#define SUNXI_EHCI_ULPI_BYPASS_EN (1 << 0) |
||||
|
||||
#define REG_PHY_UNK_H3 0x420 |
||||
#define REG_PMU_UNK_H3 0x810 |
||||
|
||||
/* A83T specific control bits for PHY0 */ |
||||
#define SUNXI_PHY_CTL_VBUSVLDEXT BIT(5) |
||||
#define SUNXI_PHY_CTL_SIDDQ BIT(3) |
||||
|
||||
/* A83T HSIC specific bits */ |
||||
#define SUNXI_EHCI_HS_FORCE BIT(20) |
||||
#define SUNXI_EHCI_CONNECT_DET BIT(17) |
||||
#define SUNXI_EHCI_CONNECT_INT BIT(16) |
||||
#define SUNXI_EHCI_HSIC BIT(1) |
||||
|
||||
static struct sunxi_usb_phy { |
||||
int usb_rst_mask; |
||||
int gpio_vbus; |
||||
int gpio_vbus_det; |
||||
int gpio_id_det; |
||||
int id; |
||||
int init_count; |
||||
int power_on_count; |
||||
ulong base; |
||||
} sunxi_usb_phy[] = { |
||||
{ |
||||
.usb_rst_mask = CCM_USB_CTRL_PHY0_RST | CCM_USB_CTRL_PHY0_CLK, |
||||
.id = 0, |
||||
.base = SUNXI_USB0_BASE, |
||||
}, |
||||
{ |
||||
.usb_rst_mask = CCM_USB_CTRL_PHY1_RST | CCM_USB_CTRL_PHY1_CLK, |
||||
.id = 1, |
||||
.base = SUNXI_USB1_BASE, |
||||
}, |
||||
#if CONFIG_SUNXI_USB_PHYS >= 3 |
||||
{ |
||||
#ifdef CONFIG_MACH_SUN8I_A83T |
||||
.usb_rst_mask = CCM_USB_CTRL_HSIC_RST | CCM_USB_CTRL_HSIC_CLK | |
||||
CCM_USB_CTRL_12M_CLK, |
||||
#else |
||||
.usb_rst_mask = CCM_USB_CTRL_PHY2_RST | CCM_USB_CTRL_PHY2_CLK, |
||||
#endif |
||||
.id = 2, |
||||
.base = SUNXI_USB2_BASE, |
||||
}, |
||||
#endif |
||||
#if CONFIG_SUNXI_USB_PHYS >= 4 |
||||
{ |
||||
.usb_rst_mask = CCM_USB_CTRL_PHY3_RST | CCM_USB_CTRL_PHY3_CLK, |
||||
.id = 3, |
||||
.base = SUNXI_USB3_BASE, |
||||
} |
||||
#endif |
||||
}; |
||||
|
||||
static int initial_usb_scan_delay = CONFIG_INITIAL_USB_SCAN_DELAY; |
||||
|
||||
static int get_vbus_gpio(int index) |
||||
{ |
||||
switch (index) { |
||||
case 0: return sunxi_name_to_gpio(CONFIG_USB0_VBUS_PIN); |
||||
case 1: return sunxi_name_to_gpio(CONFIG_USB1_VBUS_PIN); |
||||
case 2: return sunxi_name_to_gpio(CONFIG_USB2_VBUS_PIN); |
||||
case 3: return sunxi_name_to_gpio(CONFIG_USB3_VBUS_PIN); |
||||
} |
||||
return -EINVAL; |
||||
} |
||||
|
||||
static int get_vbus_detect_gpio(int index) |
||||
{ |
||||
switch (index) { |
||||
case 0: return sunxi_name_to_gpio(CONFIG_USB0_VBUS_DET); |
||||
} |
||||
return -EINVAL; |
||||
} |
||||
|
||||
static int get_id_detect_gpio(int index) |
||||
{ |
||||
switch (index) { |
||||
case 0: return sunxi_name_to_gpio(CONFIG_USB0_ID_DET); |
||||
} |
||||
return -EINVAL; |
||||
} |
||||
|
||||
__maybe_unused static void usb_phy_write(struct sunxi_usb_phy *phy, int addr, |
||||
int data, int len) |
||||
{ |
||||
int j = 0, usbc_bit = 0; |
||||
void *dest = (void *)SUNXI_USB0_BASE + SUNXI_USB_CSR; |
||||
|
||||
#ifdef CONFIG_MACH_SUN8I_A33 |
||||
/* CSR needs to be explicitly initialized to 0 on A33 */ |
||||
writel(0, dest); |
||||
#endif |
||||
|
||||
usbc_bit = 1 << (phy->id * 2); |
||||
for (j = 0; j < len; j++) { |
||||
/* set the bit address to be written */ |
||||
clrbits_le32(dest, 0xff << 8); |
||||
setbits_le32(dest, (addr + j) << 8); |
||||
|
||||
clrbits_le32(dest, usbc_bit); |
||||
/* set data bit */ |
||||
if (data & 0x1) |
||||
setbits_le32(dest, 1 << 7); |
||||
else |
||||
clrbits_le32(dest, 1 << 7); |
||||
|
||||
setbits_le32(dest, usbc_bit); |
||||
|
||||
clrbits_le32(dest, usbc_bit); |
||||
|
||||
data >>= 1; |
||||
} |
||||
} |
||||
|
||||
#if defined(CONFIG_MACH_SUNXI_H3_H5) || defined(CONFIG_MACH_SUN50I) |
||||
static void sunxi_usb_phy_config(struct sunxi_usb_phy *phy) |
||||
{ |
||||
#if defined CONFIG_MACH_SUNXI_H3_H5 |
||||
if (phy->id == 0) |
||||
clrbits_le32(SUNXI_USBPHY_BASE + REG_PHY_UNK_H3, 0x01); |
||||
#endif |
||||
clrbits_le32(phy->base + REG_PMU_UNK_H3, 0x02); |
||||
} |
||||
#elif defined CONFIG_MACH_SUN8I_A83T |
||||
static void sunxi_usb_phy_config(struct sunxi_usb_phy *phy) |
||||
{ |
||||
} |
||||
#else |
||||
static void sunxi_usb_phy_config(struct sunxi_usb_phy *phy) |
||||
{ |
||||
/* The following comments are machine
|
||||
* translated from Chinese, you have been warned! |
||||
*/ |
||||
|
||||
/* Regulation 45 ohms */ |
||||
if (phy->id == 0) |
||||
usb_phy_write(phy, 0x0c, 0x01, 1); |
||||
|
||||
/* adjust PHY's magnitude and rate */ |
||||
usb_phy_write(phy, 0x20, 0x14, 5); |
||||
|
||||
/* threshold adjustment disconnect */ |
||||
#if defined CONFIG_MACH_SUN5I || defined CONFIG_MACH_SUN7I |
||||
usb_phy_write(phy, 0x2a, 2, 2); |
||||
#else |
||||
usb_phy_write(phy, 0x2a, 3, 2); |
||||
#endif |
||||
|
||||
return; |
||||
} |
||||
#endif |
||||
|
||||
static void sunxi_usb_phy_passby(struct sunxi_usb_phy *phy, int enable) |
||||
{ |
||||
unsigned long bits = 0; |
||||
void *addr; |
||||
|
||||
addr = (void *)phy->base + SUNXI_USB_PMU_IRQ_ENABLE; |
||||
|
||||
bits = SUNXI_EHCI_AHB_ICHR8_EN | |
||||
SUNXI_EHCI_AHB_INCR4_BURST_EN | |
||||
SUNXI_EHCI_AHB_INCRX_ALIGN_EN | |
||||
SUNXI_EHCI_ULPI_BYPASS_EN; |
||||
|
||||
#ifdef CONFIG_MACH_SUN8I_A83T |
||||
if (phy->id == 2) |
||||
bits |= SUNXI_EHCI_HS_FORCE | |
||||
SUNXI_EHCI_CONNECT_INT | |
||||
SUNXI_EHCI_HSIC; |
||||
#endif |
||||
|
||||
if (enable) |
||||
setbits_le32(addr, bits); |
||||
else |
||||
clrbits_le32(addr, bits); |
||||
|
||||
return; |
||||
} |
||||
|
||||
void sunxi_usb_phy_enable_squelch_detect(int index, int enable) |
||||
{ |
||||
#ifndef CONFIG_MACH_SUN8I_A83T |
||||
struct sunxi_usb_phy *phy = &sunxi_usb_phy[index]; |
||||
|
||||
usb_phy_write(phy, 0x3c, enable ? 0 : 2, 2); |
||||
#endif |
||||
} |
||||
|
||||
void sunxi_usb_phy_init(int index) |
||||
{ |
||||
struct sunxi_usb_phy *phy = &sunxi_usb_phy[index]; |
||||
struct sunxi_ccm_reg *ccm = (struct sunxi_ccm_reg *)SUNXI_CCM_BASE; |
||||
|
||||
phy->init_count++; |
||||
if (phy->init_count != 1) |
||||
return; |
||||
|
||||
setbits_le32(&ccm->usb_clk_cfg, phy->usb_rst_mask); |
||||
|
||||
sunxi_usb_phy_config(phy); |
||||
|
||||
if (phy->id != 0) |
||||
sunxi_usb_phy_passby(phy, SUNXI_USB_PASSBY_EN); |
||||
|
||||
#ifdef CONFIG_MACH_SUN8I_A83T |
||||
if (phy->id == 0) { |
||||
setbits_le32(SUNXI_USB0_BASE + SUNXI_USB_CSR, |
||||
SUNXI_PHY_CTL_VBUSVLDEXT); |
||||
clrbits_le32(SUNXI_USB0_BASE + SUNXI_USB_CSR, |
||||
SUNXI_PHY_CTL_SIDDQ); |
||||
} |
||||
#endif |
||||
} |
||||
|
||||
void sunxi_usb_phy_exit(int index) |
||||
{ |
||||
struct sunxi_usb_phy *phy = &sunxi_usb_phy[index]; |
||||
struct sunxi_ccm_reg *ccm = (struct sunxi_ccm_reg *)SUNXI_CCM_BASE; |
||||
|
||||
phy->init_count--; |
||||
if (phy->init_count != 0) |
||||
return; |
||||
|
||||
if (phy->id != 0) |
||||
sunxi_usb_phy_passby(phy, !SUNXI_USB_PASSBY_EN); |
||||
|
||||
#ifdef CONFIG_MACH_SUN8I_A83T |
||||
if (phy->id == 0) { |
||||
setbits_le32(SUNXI_USB0_BASE + SUNXI_USB_CSR, |
||||
SUNXI_PHY_CTL_SIDDQ); |
||||
} |
||||
#endif |
||||
|
||||
clrbits_le32(&ccm->usb_clk_cfg, phy->usb_rst_mask); |
||||
} |
||||
|
||||
void sunxi_usb_phy_power_on(int index) |
||||
{ |
||||
struct sunxi_usb_phy *phy = &sunxi_usb_phy[index]; |
||||
|
||||
if (initial_usb_scan_delay) { |
||||
mdelay(initial_usb_scan_delay); |
||||
initial_usb_scan_delay = 0; |
||||
} |
||||
|
||||
phy->power_on_count++; |
||||
if (phy->power_on_count != 1) |
||||
return; |
||||
|
||||
if (phy->gpio_vbus >= 0) |
||||
gpio_set_value(phy->gpio_vbus, 1); |
||||
} |
||||
|
||||
void sunxi_usb_phy_power_off(int index) |
||||
{ |
||||
struct sunxi_usb_phy *phy = &sunxi_usb_phy[index]; |
||||
|
||||
phy->power_on_count--; |
||||
if (phy->power_on_count != 0) |
||||
return; |
||||
|
||||
if (phy->gpio_vbus >= 0) |
||||
gpio_set_value(phy->gpio_vbus, 0); |
||||
} |
||||
|
||||
int sunxi_usb_phy_vbus_detect(int index) |
||||
{ |
||||
struct sunxi_usb_phy *phy = &sunxi_usb_phy[index]; |
||||
int err, retries = 3; |
||||
|
||||
if (phy->gpio_vbus_det < 0) |
||||
return phy->gpio_vbus_det; |
||||
|
||||
err = gpio_get_value(phy->gpio_vbus_det); |
||||
/*
|
||||
* Vbus may have been provided by the board and just been turned of |
||||
* some milliseconds ago on reset, what we're measuring then is a |
||||
* residual charge on Vbus, sleep a bit and try again. |
||||
*/ |
||||
while (err > 0 && retries--) { |
||||
mdelay(100); |
||||
err = gpio_get_value(phy->gpio_vbus_det); |
||||
} |
||||
|
||||
return err; |
||||
} |
||||
|
||||
int sunxi_usb_phy_id_detect(int index) |
||||
{ |
||||
struct sunxi_usb_phy *phy = &sunxi_usb_phy[index]; |
||||
|
||||
if (phy->gpio_id_det < 0) |
||||
return phy->gpio_id_det; |
||||
|
||||
return gpio_get_value(phy->gpio_id_det); |
||||
} |
||||
|
||||
int sunxi_usb_phy_probe(void) |
||||
{ |
||||
struct sunxi_ccm_reg *ccm = (struct sunxi_ccm_reg *)SUNXI_CCM_BASE; |
||||
struct sunxi_usb_phy *phy; |
||||
int i, ret = 0; |
||||
|
||||
for (i = 0; i < CONFIG_SUNXI_USB_PHYS; i++) { |
||||
phy = &sunxi_usb_phy[i]; |
||||
|
||||
phy->gpio_vbus = get_vbus_gpio(i); |
||||
if (phy->gpio_vbus >= 0) { |
||||
ret = gpio_request(phy->gpio_vbus, "usb_vbus"); |
||||
if (ret) |
||||
return ret; |
||||
ret = gpio_direction_output(phy->gpio_vbus, 0); |
||||
if (ret) |
||||
return ret; |
||||
} |
||||
|
||||
phy->gpio_vbus_det = get_vbus_detect_gpio(i); |
||||
if (phy->gpio_vbus_det >= 0) { |
||||
ret = gpio_request(phy->gpio_vbus_det, "usb_vbus_det"); |
||||
if (ret) |
||||
return ret; |
||||
ret = gpio_direction_input(phy->gpio_vbus_det); |
||||
if (ret) |
||||
return ret; |
||||
} |
||||
|
||||
phy->gpio_id_det = get_id_detect_gpio(i); |
||||
if (phy->gpio_id_det >= 0) { |
||||
ret = gpio_request(phy->gpio_id_det, "usb_id_det"); |
||||
if (ret) |
||||
return ret; |
||||
ret = gpio_direction_input(phy->gpio_id_det); |
||||
if (ret) |
||||
return ret; |
||||
sunxi_gpio_set_pull(phy->gpio_id_det, |
||||
SUNXI_GPIO_PULL_UP); |
||||
} |
||||
} |
||||
|
||||
setbits_le32(&ccm->usb_clk_cfg, CCM_USB_CTRL_PHYGATE); |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
int sunxi_usb_phy_remove(void) |
||||
{ |
||||
struct sunxi_ccm_reg *ccm = (struct sunxi_ccm_reg *)SUNXI_CCM_BASE; |
||||
struct sunxi_usb_phy *phy; |
||||
int i; |
||||
|
||||
clrbits_le32(&ccm->usb_clk_cfg, CCM_USB_CTRL_PHYGATE); |
||||
|
||||
for (i = 0; i < CONFIG_SUNXI_USB_PHYS; i++) { |
||||
phy = &sunxi_usb_phy[i]; |
||||
|
||||
if (phy->gpio_vbus >= 0) |
||||
gpio_free(phy->gpio_vbus); |
||||
|
||||
if (phy->gpio_vbus_det >= 0) |
||||
gpio_free(phy->gpio_vbus_det); |
||||
|
||||
if (phy->gpio_id_det >= 0) |
||||
gpio_free(phy->gpio_id_det); |
||||
} |
||||
|
||||
return 0; |
||||
} |
@ -0,0 +1,17 @@ |
||||
CONFIG_ARM=y |
||||
CONFIG_ARCH_SUNXI=y |
||||
CONFIG_SPL=y |
||||
CONFIG_MACH_SUN50I=y |
||||
CONFIG_DRAM_ODT_EN=y |
||||
CONFIG_RESERVE_ALLWINNER_BOOT0_HEADER=y |
||||
CONFIG_MMC_SUNXI_SLOT_EXTRA=2 |
||||
CONFIG_DEFAULT_DEVICE_TREE="sun50i-a64-amarula-relic" |
||||
# CONFIG_SYS_MALLOC_CLEAR_ON_INIT is not set |
||||
# CONFIG_VIDEO_DE2 is not set |
||||
# CONFIG_CMD_FLASH is not set |
||||
# CONFIG_CMD_FPGA is not set |
||||
# CONFIG_SPL_DOS_PARTITION is not set |
||||
# CONFIG_SPL_ISO_PARTITION is not set |
||||
# CONFIG_SPL_EFI_PARTITION is not set |
||||
CONFIG_USB_MUSB_GADGET=y |
||||
CONFIG_SYS_USB_EVENT_POLL_VIA_INT_QUEUE=y |
@ -0,0 +1,65 @@ |
||||
Allwinner sun4i USB PHY |
||||
----------------------- |
||||
|
||||
Required properties: |
||||
- compatible : should be one of |
||||
* allwinner,sun4i-a10-usb-phy |
||||
* allwinner,sun5i-a13-usb-phy |
||||
* allwinner,sun6i-a31-usb-phy |
||||
* allwinner,sun7i-a20-usb-phy |
||||
* allwinner,sun8i-a23-usb-phy |
||||
* allwinner,sun8i-a33-usb-phy |
||||
* allwinner,sun8i-a83t-usb-phy |
||||
* allwinner,sun8i-h3-usb-phy |
||||
* allwinner,sun8i-v3s-usb-phy |
||||
* allwinner,sun50i-a64-usb-phy |
||||
- reg : a list of offset + length pairs |
||||
- reg-names : |
||||
* "phy_ctrl" |
||||
* "pmu0" for H3, V3s and A64 |
||||
* "pmu1" |
||||
* "pmu2" for sun4i, sun6i, sun7i, sun8i-a83t or sun8i-h3 |
||||
* "pmu3" for sun8i-h3 |
||||
- #phy-cells : from the generic phy bindings, must be 1 |
||||
- clocks : phandle + clock specifier for the phy clocks |
||||
- clock-names : |
||||
* "usb_phy" for sun4i, sun5i or sun7i |
||||
* "usb0_phy", "usb1_phy" and "usb2_phy" for sun6i |
||||
* "usb0_phy", "usb1_phy" for sun8i |
||||
* "usb0_phy", "usb1_phy", "usb2_phy" and "usb2_hsic_12M" for sun8i-a83t |
||||
* "usb0_phy", "usb1_phy", "usb2_phy" and "usb3_phy" for sun8i-h3 |
||||
- resets : a list of phandle + reset specifier pairs |
||||
- reset-names : |
||||
* "usb0_reset" |
||||
* "usb1_reset" |
||||
* "usb2_reset" for sun4i, sun6i, sun7i, sun8i-a83t or sun8i-h3 |
||||
* "usb3_reset" for sun8i-h3 |
||||
|
||||
Optional properties: |
||||
- usb0_id_det-gpios : gpio phandle for reading the otg id pin value |
||||
- usb0_vbus_det-gpios : gpio phandle for detecting the presence of usb0 vbus |
||||
- usb0_vbus_power-supply: power-supply phandle for usb0 vbus presence detect |
||||
- usb0_vbus-supply : regulator phandle for controller usb0 vbus |
||||
- usb1_vbus-supply : regulator phandle for controller usb1 vbus |
||||
- usb2_vbus-supply : regulator phandle for controller usb2 vbus |
||||
- usb3_vbus-supply : regulator phandle for controller usb3 vbus |
||||
|
||||
Example: |
||||
usbphy: phy@01c13400 { |
||||
#phy-cells = <1>; |
||||
compatible = "allwinner,sun4i-a10-usb-phy"; |
||||
/* phy base regs, phy1 pmu reg, phy2 pmu reg */ |
||||
reg = <0x01c13400 0x10 0x01c14800 0x4 0x01c1c800 0x4>; |
||||
reg-names = "phy_ctrl", "pmu1", "pmu2"; |
||||
clocks = <&usb_clk 8>; |
||||
clock-names = "usb_phy"; |
||||
resets = <&usb_clk 0>, <&usb_clk 1>, <&usb_clk 2>; |
||||
reset-names = "usb0_reset", "usb1_reset", "usb2_reset"; |
||||
pinctrl-names = "default"; |
||||
pinctrl-0 = <&usb0_id_detect_pin>, <&usb0_vbus_detect_pin>; |
||||
usb0_id_det-gpios = <&pio 7 19 GPIO_ACTIVE_HIGH>; /* PH19 */ |
||||
usb0_vbus_det-gpios = <&pio 7 22 GPIO_ACTIVE_HIGH>; /* PH22 */ |
||||
usb0_vbus-supply = <®_usb0_vbus>; |
||||
usb1_vbus-supply = <®_usb1_vbus>; |
||||
usb2_vbus-supply = <®_usb2_vbus>; |
||||
}; |
@ -0,0 +1,13 @@ |
||||
# |
||||
# Phy drivers for Allwinner platforms |
||||
# |
||||
config PHY_SUN4I_USB |
||||
bool "Allwinner Sun4I USB PHY driver" |
||||
depends on ARCH_SUNXI |
||||
select PHY |
||||
help |
||||
Enable this to support the transceiver that is part of Allwinner |
||||
sunxi SoCs. |
||||
|
||||
This driver controls the entire USB PHY block, both the USB OTG |
||||
parts, as well as the 2 regular USB 2 host PHYs. |
@ -0,0 +1,6 @@ |
||||
# Copyright (C) 2016 Amarula Solutions
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-2.0+
|
||||
#
|
||||
|
||||
obj-$(CONFIG_PHY_SUN4I_USB) += phy-sun4i-usb.o
|
@ -0,0 +1,575 @@ |
||||
/*
|
||||
* Allwinner sun4i USB PHY driver |
||||
* |
||||
* Copyright (C) 2017 Jagan Teki <jagan@amarulasolutions.com> |
||||
* Copyright (C) 2015 Hans de Goede <hdegoede@redhat.com> |
||||
* Copyright (C) 2014 Roman Byshko <rbyshko@gmail.com> |
||||
* |
||||
* Modelled arch/arm/mach-sunxi/usb_phy.c to compatible with generic-phy. |
||||
* |
||||
* SPDX-License-Identifier: GPL-2.0+ |
||||
*/ |
||||
|
||||
#include <common.h> |
||||
#include <dm.h> |
||||
#include <dm/device.h> |
||||
#include <generic-phy.h> |
||||
#include <phy-sun4i-usb.h> |
||||
#include <asm/gpio.h> |
||||
#include <asm/io.h> |
||||
#include <asm/arch/clock.h> |
||||
#include <asm/arch/cpu.h> |
||||
|
||||
#define REG_ISCR 0x00 |
||||
#define REG_PHYCTL_A10 0x04 |
||||
#define REG_PHYBIST 0x08 |
||||
#define REG_PHYTUNE 0x0c |
||||
#define REG_PHYCTL_A33 0x10 |
||||
#define REG_PHY_OTGCTL 0x20 |
||||
#define REG_PMU_UNK1 0x10 |
||||
|
||||
/* Common Control Bits for Both PHYs */ |
||||
#define PHY_PLL_BW 0x03 |
||||
#define PHY_RES45_CAL_EN 0x0c |
||||
|
||||
/* Private Control Bits for Each PHY */ |
||||
#define PHY_TX_AMPLITUDE_TUNE 0x20 |
||||
#define PHY_TX_SLEWRATE_TUNE 0x22 |
||||
#define PHY_DISCON_TH_SEL 0x2a |
||||
#define PHY_SQUELCH_DETECT 0x3c |
||||
|
||||
#define PHYCTL_DATA BIT(7) |
||||
#define OTGCTL_ROUTE_MUSB BIT(0) |
||||
|
||||
#define PHY_TX_RATE BIT(4) |
||||
#define PHY_TX_MAGNITUDE BIT(2) |
||||
#define PHY_TX_AMPLITUDE_LEN 5 |
||||
|
||||
#define PHY_RES45_CAL_DATA BIT(0) |
||||
#define PHY_RES45_CAL_LEN 1 |
||||
#define PHY_DISCON_TH_LEN 2 |
||||
|
||||
#define SUNXI_AHB_ICHR8_EN BIT(10) |
||||
#define SUNXI_AHB_INCR4_BURST_EN BIT(9) |
||||
#define SUNXI_AHB_INCRX_ALIGN_EN BIT(8) |
||||
#define SUNXI_ULPI_BYPASS_EN BIT(0) |
||||
|
||||
/* A83T specific control bits for PHY0 */ |
||||
#define PHY_CTL_VBUSVLDEXT BIT(5) |
||||
#define PHY_CTL_SIDDQ BIT(3) |
||||
|
||||
/* A83T specific control bits for PHY2 HSIC */ |
||||
#define SUNXI_EHCI_HS_FORCE BIT(20) |
||||
#define SUNXI_HSIC_CONNECT_INT BIT(16) |
||||
#define SUNXI_HSIC BIT(1) |
||||
|
||||
#define MAX_PHYS 4 |
||||
|
||||
enum sun4i_usb_phy_type { |
||||
sun4i_a10_phy, |
||||
sun6i_a31_phy, |
||||
sun8i_a33_phy, |
||||
sun8i_a83t_phy, |
||||
sun8i_h3_phy, |
||||
sun8i_v3s_phy, |
||||
sun50i_a64_phy, |
||||
}; |
||||
|
||||
struct sun4i_usb_phy_cfg { |
||||
int num_phys; |
||||
enum sun4i_usb_phy_type type; |
||||
u32 disc_thresh; |
||||
u8 phyctl_offset; |
||||
bool enable_pmu_unk1; |
||||
bool phy0_dual_route; |
||||
}; |
||||
|
||||
struct sun4i_usb_phy_info { |
||||
const char *gpio_vbus; |
||||
const char *gpio_vbus_det; |
||||
const char *gpio_id_det; |
||||
int rst_mask; |
||||
} phy_info[] = { |
||||
{ |
||||
.gpio_vbus = CONFIG_USB0_VBUS_PIN, |
||||
.gpio_vbus_det = CONFIG_USB0_VBUS_DET, |
||||
.gpio_id_det = CONFIG_USB0_ID_DET, |
||||
.rst_mask = (CCM_USB_CTRL_PHY0_RST | CCM_USB_CTRL_PHY0_CLK), |
||||
}, |
||||
{ |
||||
.gpio_vbus = CONFIG_USB1_VBUS_PIN, |
||||
.gpio_vbus_det = NULL, |
||||
.gpio_id_det = NULL, |
||||
.rst_mask = (CCM_USB_CTRL_PHY1_RST | CCM_USB_CTRL_PHY1_CLK), |
||||
}, |
||||
{ |
||||
.gpio_vbus = CONFIG_USB2_VBUS_PIN, |
||||
.gpio_vbus_det = NULL, |
||||
.gpio_id_det = NULL, |
||||
#ifdef CONFIG_MACH_SUN8I_A83T |
||||
.rst_mask = (CCM_USB_CTRL_HSIC_RST | CCM_USB_CTRL_HSIC_CLK | |
||||
CCM_USB_CTRL_12M_CLK), |
||||
#else |
||||
.rst_mask = (CCM_USB_CTRL_PHY2_RST | CCM_USB_CTRL_PHY2_CLK), |
||||
#endif |
||||
}, |
||||
{ |
||||
.gpio_vbus = CONFIG_USB3_VBUS_PIN, |
||||
.gpio_vbus_det = NULL, |
||||
.gpio_id_det = NULL, |
||||
#ifdef CONFIG_MACH_SUN6I |
||||
.rst_mask = (CCM_USB_CTRL_PHY3_RST | CCM_USB_CTRL_PHY3_CLK), |
||||
#endif |
||||
}, |
||||
}; |
||||
|
||||
struct sun4i_usb_phy_plat { |
||||
void __iomem *pmu; |
||||
int power_on_count; |
||||
int gpio_vbus; |
||||
int gpio_vbus_det; |
||||
int gpio_id_det; |
||||
int rst_mask; |
||||
int id; |
||||
}; |
||||
|
||||
struct sun4i_usb_phy_data { |
||||
void __iomem *base; |
||||
struct sunxi_ccm_reg *ccm; |
||||
const struct sun4i_usb_phy_cfg *cfg; |
||||
struct sun4i_usb_phy_plat *usb_phy; |
||||
}; |
||||
|
||||
static int initial_usb_scan_delay = CONFIG_INITIAL_USB_SCAN_DELAY; |
||||
|
||||
static void sun4i_usb_phy_write(struct phy *phy, u32 addr, u32 data, int len) |
||||
{ |
||||
struct sun4i_usb_phy_data *phy_data = dev_get_priv(phy->dev); |
||||
struct sun4i_usb_phy_plat *usb_phy = &phy_data->usb_phy[phy->id]; |
||||
u32 temp, usbc_bit = BIT(usb_phy->id * 2); |
||||
void __iomem *phyctl = phy_data->base + phy_data->cfg->phyctl_offset; |
||||
int i; |
||||
|
||||
if (phy_data->cfg->phyctl_offset == REG_PHYCTL_A33) { |
||||
/* SoCs newer than A33 need us to set phyctl to 0 explicitly */ |
||||
writel(0, phyctl); |
||||
} |
||||
|
||||
for (i = 0; i < len; i++) { |
||||
temp = readl(phyctl); |
||||
|
||||
/* clear the address portion */ |
||||
temp &= ~(0xff << 8); |
||||
|
||||
/* set the address */ |
||||
temp |= ((addr + i) << 8); |
||||
writel(temp, phyctl); |
||||
|
||||
/* set the data bit and clear usbc bit*/ |
||||
temp = readb(phyctl); |
||||
if (data & 0x1) |
||||
temp |= PHYCTL_DATA; |
||||
else |
||||
temp &= ~PHYCTL_DATA; |
||||
temp &= ~usbc_bit; |
||||
writeb(temp, phyctl); |
||||
|
||||
/* pulse usbc_bit */ |
||||
temp = readb(phyctl); |
||||
temp |= usbc_bit; |
||||
writeb(temp, phyctl); |
||||
|
||||
temp = readb(phyctl); |
||||
temp &= ~usbc_bit; |
||||
writeb(temp, phyctl); |
||||
|
||||
data >>= 1; |
||||
} |
||||
} |
||||
|
||||
static void sun4i_usb_phy_passby(struct phy *phy, bool enable) |
||||
{ |
||||
struct sun4i_usb_phy_data *data = dev_get_priv(phy->dev); |
||||
struct sun4i_usb_phy_plat *usb_phy = &data->usb_phy[phy->id]; |
||||
u32 bits, reg_value; |
||||
|
||||
if (!usb_phy->pmu) |
||||
return; |
||||
|
||||
bits = SUNXI_AHB_ICHR8_EN | SUNXI_AHB_INCR4_BURST_EN | |
||||
SUNXI_AHB_INCRX_ALIGN_EN | SUNXI_ULPI_BYPASS_EN; |
||||
|
||||
/* A83T USB2 is HSIC */ |
||||
if (data->cfg->type == sun8i_a83t_phy && usb_phy->id == 2) |
||||
bits |= SUNXI_EHCI_HS_FORCE | SUNXI_HSIC_CONNECT_INT | |
||||
SUNXI_HSIC; |
||||
|
||||
reg_value = readl(usb_phy->pmu); |
||||
|
||||
if (enable) |
||||
reg_value |= bits; |
||||
else |
||||
reg_value &= ~bits; |
||||
|
||||
writel(reg_value, usb_phy->pmu); |
||||
} |
||||
|
||||
static int sun4i_usb_phy_power_on(struct phy *phy) |
||||
{ |
||||
struct sun4i_usb_phy_data *data = dev_get_priv(phy->dev); |
||||
struct sun4i_usb_phy_plat *usb_phy = &data->usb_phy[phy->id]; |
||||
|
||||
if (initial_usb_scan_delay) { |
||||
mdelay(initial_usb_scan_delay); |
||||
initial_usb_scan_delay = 0; |
||||
} |
||||
|
||||
usb_phy->power_on_count++; |
||||
if (usb_phy->power_on_count != 1) |
||||
return 0; |
||||
|
||||
if (usb_phy->gpio_vbus >= 0) |
||||
gpio_set_value(usb_phy->gpio_vbus, SUNXI_GPIO_PULL_UP); |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
static int sun4i_usb_phy_power_off(struct phy *phy) |
||||
{ |
||||
struct sun4i_usb_phy_data *data = dev_get_priv(phy->dev); |
||||
struct sun4i_usb_phy_plat *usb_phy = &data->usb_phy[phy->id]; |
||||
|
||||
usb_phy->power_on_count--; |
||||
if (usb_phy->power_on_count != 0) |
||||
return 0; |
||||
|
||||
if (usb_phy->gpio_vbus >= 0) |
||||
gpio_set_value(usb_phy->gpio_vbus, SUNXI_GPIO_PULL_DISABLE); |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
static void sun4i_usb_phy0_reroute(struct sun4i_usb_phy_data *data, bool id_det) |
||||
{ |
||||
u32 regval; |
||||
|
||||
regval = readl(data->base + REG_PHY_OTGCTL); |
||||
if (!id_det) { |
||||
/* Host mode. Route phy0 to EHCI/OHCI */ |
||||
regval &= ~OTGCTL_ROUTE_MUSB; |
||||
} else { |
||||
/* Peripheral mode. Route phy0 to MUSB */ |
||||
regval |= OTGCTL_ROUTE_MUSB; |
||||
} |
||||
writel(regval, data->base + REG_PHY_OTGCTL); |
||||
} |
||||
|
||||
static int sun4i_usb_phy_init(struct phy *phy) |
||||
{ |
||||
struct sun4i_usb_phy_data *data = dev_get_priv(phy->dev); |
||||
struct sun4i_usb_phy_plat *usb_phy = &data->usb_phy[phy->id]; |
||||
u32 val; |
||||
|
||||
setbits_le32(&data->ccm->usb_clk_cfg, usb_phy->rst_mask); |
||||
|
||||
if (data->cfg->type == sun8i_a83t_phy) { |
||||
if (phy->id == 0) { |
||||
val = readl(data->base + data->cfg->phyctl_offset); |
||||
val |= PHY_CTL_VBUSVLDEXT; |
||||
val &= ~PHY_CTL_SIDDQ; |
||||
writel(val, data->base + data->cfg->phyctl_offset); |
||||
} |
||||
} else { |
||||
if (usb_phy->pmu && data->cfg->enable_pmu_unk1) { |
||||
val = readl(usb_phy->pmu + REG_PMU_UNK1); |
||||
writel(val & ~2, usb_phy->pmu + REG_PMU_UNK1); |
||||
} |
||||
|
||||
if (usb_phy->id == 0) |
||||
sun4i_usb_phy_write(phy, PHY_RES45_CAL_EN, |
||||
PHY_RES45_CAL_DATA, |
||||
PHY_RES45_CAL_LEN); |
||||
|
||||
/* Adjust PHY's magnitude and rate */ |
||||
sun4i_usb_phy_write(phy, PHY_TX_AMPLITUDE_TUNE, |
||||
PHY_TX_MAGNITUDE | PHY_TX_RATE, |
||||
PHY_TX_AMPLITUDE_LEN); |
||||
|
||||
/* Disconnect threshold adjustment */ |
||||
sun4i_usb_phy_write(phy, PHY_DISCON_TH_SEL, |
||||
data->cfg->disc_thresh, PHY_DISCON_TH_LEN); |
||||
} |
||||
|
||||
if (usb_phy->id != 0) |
||||
sun4i_usb_phy_passby(phy, true); |
||||
|
||||
sun4i_usb_phy0_reroute(data, true); |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
static int sun4i_usb_phy_exit(struct phy *phy) |
||||
{ |
||||
struct sun4i_usb_phy_data *data = dev_get_priv(phy->dev); |
||||
struct sun4i_usb_phy_plat *usb_phy = &data->usb_phy[phy->id]; |
||||
|
||||
if (phy->id == 0) { |
||||
if (data->cfg->type == sun8i_a83t_phy) { |
||||
void __iomem *phyctl = data->base + |
||||
data->cfg->phyctl_offset; |
||||
|
||||
writel(readl(phyctl) | PHY_CTL_SIDDQ, phyctl); |
||||
} |
||||
} |
||||
|
||||
sun4i_usb_phy_passby(phy, false); |
||||
|
||||
clrbits_le32(&data->ccm->usb_clk_cfg, usb_phy->rst_mask); |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
static int sun4i_usb_phy_xlate(struct phy *phy, |
||||
struct ofnode_phandle_args *args) |
||||
{ |
||||
struct sun4i_usb_phy_data *data = dev_get_priv(phy->dev); |
||||
|
||||
if (args->args_count >= data->cfg->num_phys) |
||||
return -EINVAL; |
||||
|
||||
if (args->args_count) |
||||
phy->id = args->args[0]; |
||||
else |
||||
phy->id = 0; |
||||
|
||||
debug("%s: phy_id = %ld\n", __func__, phy->id); |
||||
return 0; |
||||
} |
||||
|
||||
int sun4i_usb_phy_vbus_detect(struct phy *phy) |
||||
{ |
||||
struct sun4i_usb_phy_data *data = dev_get_priv(phy->dev); |
||||
struct sun4i_usb_phy_plat *usb_phy = &data->usb_phy[phy->id]; |
||||
int err, retries = 3; |
||||
|
||||
debug("%s: id_det = %d\n", __func__, usb_phy->gpio_id_det); |
||||
|
||||
if (usb_phy->gpio_vbus_det < 0) |
||||
return usb_phy->gpio_vbus_det; |
||||
|
||||
err = gpio_get_value(usb_phy->gpio_vbus_det); |
||||
/*
|
||||
* Vbus may have been provided by the board and just been turned of |
||||
* some milliseconds ago on reset, what we're measuring then is a |
||||
* residual charge on Vbus, sleep a bit and try again. |
||||
*/ |
||||
while (err > 0 && retries--) { |
||||
mdelay(100); |
||||
err = gpio_get_value(usb_phy->gpio_vbus_det); |
||||
} |
||||
|
||||
return err; |
||||
} |
||||
|
||||
int sun4i_usb_phy_id_detect(struct phy *phy) |
||||
{ |
||||
struct sun4i_usb_phy_data *data = dev_get_priv(phy->dev); |
||||
struct sun4i_usb_phy_plat *usb_phy = &data->usb_phy[phy->id]; |
||||
|
||||
debug("%s: id_det = %d\n", __func__, usb_phy->gpio_id_det); |
||||
|
||||
if (usb_phy->gpio_id_det < 0) |
||||
return usb_phy->gpio_id_det; |
||||
|
||||
return gpio_get_value(usb_phy->gpio_id_det); |
||||
} |
||||
|
||||
void sun4i_usb_phy_set_squelch_detect(struct phy *phy, bool enabled) |
||||
{ |
||||
sun4i_usb_phy_write(phy, PHY_SQUELCH_DETECT, enabled ? 0 : 2, 2); |
||||
} |
||||
|
||||
static struct phy_ops sun4i_usb_phy_ops = { |
||||
.of_xlate = sun4i_usb_phy_xlate, |
||||
.init = sun4i_usb_phy_init, |
||||
.power_on = sun4i_usb_phy_power_on, |
||||
.power_off = sun4i_usb_phy_power_off, |
||||
.exit = sun4i_usb_phy_exit, |
||||
}; |
||||
|
||||
static int sun4i_usb_phy_probe(struct udevice *dev) |
||||
{ |
||||
struct sun4i_usb_phy_plat *plat = dev_get_platdata(dev); |
||||
struct sun4i_usb_phy_data *data = dev_get_priv(dev); |
||||
int i, ret; |
||||
|
||||
data->cfg = (const struct sun4i_usb_phy_cfg *)dev_get_driver_data(dev); |
||||
if (!data->cfg) |
||||
return -EINVAL; |
||||
|
||||
data->base = (void __iomem *)devfdt_get_addr_name(dev, "phy_ctrl"); |
||||
if (IS_ERR(data->base)) |
||||
return PTR_ERR(data->base); |
||||
|
||||
data->ccm = (struct sunxi_ccm_reg *)SUNXI_CCM_BASE; |
||||
if (IS_ERR(data->ccm)) |
||||
return PTR_ERR(data->ccm); |
||||
|
||||
data->usb_phy = plat; |
||||
for (i = 0; i < data->cfg->num_phys; i++) { |
||||
struct sun4i_usb_phy_plat *phy = &plat[i]; |
||||
struct sun4i_usb_phy_info *info = &phy_info[i]; |
||||
char name[16]; |
||||
|
||||
phy->gpio_vbus = sunxi_name_to_gpio(info->gpio_vbus); |
||||
if (phy->gpio_vbus >= 0) { |
||||
ret = gpio_request(phy->gpio_vbus, "usb_vbus"); |
||||
if (ret) |
||||
return ret; |
||||
ret = gpio_direction_output(phy->gpio_vbus, 0); |
||||
if (ret) |
||||
return ret; |
||||
} |
||||
|
||||
phy->gpio_vbus_det = sunxi_name_to_gpio(info->gpio_vbus_det); |
||||
if (phy->gpio_vbus_det >= 0) { |
||||
ret = gpio_request(phy->gpio_vbus_det, "usb_vbus_det"); |
||||
if (ret) |
||||
return ret; |
||||
ret = gpio_direction_input(phy->gpio_vbus_det); |
||||
if (ret) |
||||
return ret; |
||||
} |
||||
|
||||
phy->gpio_id_det = sunxi_name_to_gpio(info->gpio_id_det); |
||||
if (phy->gpio_id_det >= 0) { |
||||
ret = gpio_request(phy->gpio_id_det, "usb_id_det"); |
||||
if (ret) |
||||
return ret; |
||||
ret = gpio_direction_input(phy->gpio_id_det); |
||||
if (ret) |
||||
return ret; |
||||
sunxi_gpio_set_pull(phy->gpio_id_det, SUNXI_GPIO_PULL_UP); |
||||
} |
||||
|
||||
if (i || data->cfg->phy0_dual_route) { |
||||
snprintf(name, sizeof(name), "pmu%d", i); |
||||
phy->pmu = (void __iomem *)devfdt_get_addr_name(dev, name); |
||||
if (IS_ERR(phy->pmu)) |
||||
return PTR_ERR(phy->pmu); |
||||
} |
||||
|
||||
phy->id = i; |
||||
phy->rst_mask = info->rst_mask; |
||||
}; |
||||
|
||||
setbits_le32(&data->ccm->usb_clk_cfg, CCM_USB_CTRL_PHYGATE); |
||||
|
||||
debug("Allwinner Sun4I USB PHY driver loaded\n"); |
||||
return 0; |
||||
} |
||||
|
||||
static const struct sun4i_usb_phy_cfg sun4i_a10_cfg = { |
||||
.num_phys = 3, |
||||
.type = sun4i_a10_phy, |
||||
.disc_thresh = 3, |
||||
.phyctl_offset = REG_PHYCTL_A10, |
||||
.enable_pmu_unk1 = false, |
||||
}; |
||||
|
||||
static const struct sun4i_usb_phy_cfg sun5i_a13_cfg = { |
||||
.num_phys = 2, |
||||
.type = sun4i_a10_phy, |
||||
.disc_thresh = 2, |
||||
.phyctl_offset = REG_PHYCTL_A10, |
||||
.enable_pmu_unk1 = false, |
||||
}; |
||||
|
||||
static const struct sun4i_usb_phy_cfg sun6i_a31_cfg = { |
||||
.num_phys = 3, |
||||
.type = sun6i_a31_phy, |
||||
.disc_thresh = 3, |
||||
.phyctl_offset = REG_PHYCTL_A10, |
||||
.enable_pmu_unk1 = false, |
||||
}; |
||||
|
||||
static const struct sun4i_usb_phy_cfg sun7i_a20_cfg = { |
||||
.num_phys = 3, |
||||
.type = sun4i_a10_phy, |
||||
.disc_thresh = 2, |
||||
.phyctl_offset = REG_PHYCTL_A10, |
||||
.enable_pmu_unk1 = false, |
||||
}; |
||||
|
||||
static const struct sun4i_usb_phy_cfg sun8i_a23_cfg = { |
||||
.num_phys = 2, |
||||
.type = sun4i_a10_phy, |
||||
.disc_thresh = 3, |
||||
.phyctl_offset = REG_PHYCTL_A10, |
||||
.enable_pmu_unk1 = false, |
||||
}; |
||||
|
||||
static const struct sun4i_usb_phy_cfg sun8i_a33_cfg = { |
||||
.num_phys = 2, |
||||
.type = sun8i_a33_phy, |
||||
.disc_thresh = 3, |
||||
.phyctl_offset = REG_PHYCTL_A33, |
||||
.enable_pmu_unk1 = false, |
||||
}; |
||||
|
||||
static const struct sun4i_usb_phy_cfg sun8i_a83t_cfg = { |
||||
.num_phys = 3, |
||||
.type = sun8i_a83t_phy, |
||||
.phyctl_offset = REG_PHYCTL_A33, |
||||
}; |
||||
|
||||
static const struct sun4i_usb_phy_cfg sun8i_h3_cfg = { |
||||
.num_phys = 4, |
||||
.type = sun8i_h3_phy, |
||||
.disc_thresh = 3, |
||||
.phyctl_offset = REG_PHYCTL_A33, |
||||
.enable_pmu_unk1 = true, |
||||
.phy0_dual_route = true, |
||||
}; |
||||
|
||||
static const struct sun4i_usb_phy_cfg sun8i_v3s_cfg = { |
||||
.num_phys = 1, |
||||
.type = sun8i_v3s_phy, |
||||
.disc_thresh = 3, |
||||
.phyctl_offset = REG_PHYCTL_A33, |
||||
.enable_pmu_unk1 = true, |
||||
.phy0_dual_route = true, |
||||
}; |
||||
|
||||
static const struct sun4i_usb_phy_cfg sun50i_a64_cfg = { |
||||
.num_phys = 2, |
||||
.type = sun50i_a64_phy, |
||||
.disc_thresh = 3, |
||||
.phyctl_offset = REG_PHYCTL_A33, |
||||
.enable_pmu_unk1 = true, |
||||
.phy0_dual_route = true, |
||||
}; |
||||
|
||||
static const struct udevice_id sun4i_usb_phy_ids[] = { |
||||
{ .compatible = "allwinner,sun4i-a10-usb-phy", .data = (ulong)&sun4i_a10_cfg }, |
||||
{ .compatible = "allwinner,sun5i-a13-usb-phy", .data = (ulong)&sun5i_a13_cfg }, |
||||
{ .compatible = "allwinner,sun6i-a31-usb-phy", .data = (ulong)&sun6i_a31_cfg }, |
||||
{ .compatible = "allwinner,sun7i-a20-usb-phy", .data = (ulong)&sun7i_a20_cfg }, |
||||
{ .compatible = "allwinner,sun8i-a23-usb-phy", .data = (ulong)&sun8i_a23_cfg }, |
||||
{ .compatible = "allwinner,sun8i-a33-usb-phy", .data = (ulong)&sun8i_a33_cfg }, |
||||
{ .compatible = "allwinner,sun8i-a83t-usb-phy", .data = (ulong)&sun8i_a83t_cfg }, |
||||
{ .compatible = "allwinner,sun8i-h3-usb-phy", .data = (ulong)&sun8i_h3_cfg }, |
||||
{ .compatible = "allwinner,sun8i-v3s-usb-phy", .data = (ulong)&sun8i_v3s_cfg }, |
||||
{ .compatible = "allwinner,sun50i-a64-usb-phy", .data = (ulong)&sun50i_a64_cfg}, |
||||
{ } |
||||
}; |
||||
|
||||
U_BOOT_DRIVER(sun4i_usb_phy) = { |
||||
.name = "sun4i_usb_phy", |
||||
.id = UCLASS_PHY, |
||||
.of_match = sun4i_usb_phy_ids, |
||||
.ops = &sun4i_usb_phy_ops, |
||||
.probe = sun4i_usb_phy_probe, |
||||
.platdata_auto_alloc_size = sizeof(struct sun4i_usb_phy_plat[MAX_PHYS]), |
||||
.priv_auto_alloc_size = sizeof(struct sun4i_usb_phy_data), |
||||
}; |
@ -0,0 +1,34 @@ |
||||
/*
|
||||
* Copyright (C) 2017 Jagan Teki <jagan@amarulasolutions.com> |
||||
* |
||||
* SPDX-License-Identifier: GPL-2.0+ |
||||
*/ |
||||
|
||||
#ifndef __GENERIC_PHY_SUN4I_USB_H |
||||
#define __GENERIC_PHY_SUN4I_USB_H |
||||
|
||||
/**
|
||||
* sun4i_usb_phy_id_detect - detect ID pin of USB PHY |
||||
* |
||||
* @phy: USB PHY port to detect ID pin |
||||
* @return 0 if OK, or a negative error code |
||||
*/ |
||||
int sun4i_usb_phy_id_detect(struct phy *phy); |
||||
|
||||
/**
|
||||
* sun4i_usb_phy_vbus_detect - detect VBUS pin of USB PHY |
||||
* |
||||
* @phy: USB PHY port to detect VBUS pin |
||||
* @return 0 if OK, or a negative error code |
||||
*/ |
||||
int sun4i_usb_phy_vbus_detect(struct phy *phy); |
||||
|
||||
/**
|
||||
* sun4i_usb_phy_set_squelch_detect() - Enable/disable squelch detect |
||||
* |
||||
* @phy: reference to a sun4i usb phy |
||||
* @enabled: wether to enable or disable squelch detect |
||||
*/ |
||||
void sun4i_usb_phy_set_squelch_detect(struct phy *phy, bool enabled); |
||||
|
||||
#endif /*__GENERIC_PHY_SUN4I_USB_H */ |
Loading…
Reference in new issue