blob: dc5e2d573d094928aa3cea752ffa07edff8e4f94 [file] [log] [blame]
// SPDX-License-Identifier: GPL-2.0+
/*
* Meson GXL and GXM USB2 PHY driver
*
* Copyright (C) 2017 Martin Blumenstingl <martin.blumenstingl@googlemail.com>
* Copyright (C) 2018 BayLibre, SAS
* Author: Neil Armstrong <narmstron@baylibre.com>
*/
#include <common.h>
#include <asm/io.h>
#include <bitfield.h>
#include <dm.h>
#include <errno.h>
#include <generic-phy.h>
#include <regmap.h>
#include <power/regulator.h>
#include <clk.h>
#include <asm/arch/usb.h>
#include <asm/arch/cpu_id.h>
#include <linux/compat.h>
#include <linux/ioport.h>
#define RESET_COMPLETE_TIME 500
struct phy_aml_usb2_priv {
/* controller */
unsigned int base_addr;
unsigned int reset_addr;
/* role */
int id_mode;
/* vbus call back */
unsigned int usb_phy2_base_addr;
unsigned int u2_port_num;
unsigned int usb_phy2_pll_base_addr[4];
};
/* return == 1, g12b and revB, tl1 */
static int phy_aml_usb2_check_g12b_revb(void)
{
cpu_id_t cpu_id = get_cpu_id();
if (cpu_id.family_id == MESON_CPU_MAJOR_ID_G12B &&
cpu_id.chip_rev == 0xb)
return 1;
else
return 0;
}
static void set_usb_pll(struct phy *phy, uint32_t volatile *phy2_pll_base)
{
unsigned int pll_set1, pll_set2, pll_set3;
unsigned int tuning_disconnect_threshold = 0x34;
dev_read_u32(phy->dev, "pll-setting-1", &pll_set1);
dev_read_u32(phy->dev, "pll-setting-2", &pll_set2);
dev_read_u32(phy->dev, "pll-setting-3", &pll_set3);
debug("pll1=0x%08x, pll2=0x%08x, pll-setting-3 =0x%08x\n",
pll_set1, pll_set2, pll_set3);
(*(volatile uint32_t *)((unsigned long)phy2_pll_base + 0x40))
= (pll_set1 | USB_PHY2_RESET | USB_PHY2_ENABLE);
(*(volatile uint32_t *)((unsigned long)phy2_pll_base + 0x44)) =
pll_set2;
(*(volatile uint32_t *)((unsigned long)phy2_pll_base + 0x48)) =
pll_set3;
udelay(100);
(*(volatile uint32_t *)(unsigned long)((unsigned long)phy2_pll_base + 0x40))
= (((pll_set1) | (USB_PHY2_ENABLE))
& (~(USB_PHY2_RESET)));
dev_read_u32(phy->dev, "disconnect-threshold", &tuning_disconnect_threshold);
(*(volatile uint32_t *)((unsigned long)phy2_pll_base + 0xc)) =
tuning_disconnect_threshold;
debug("tuning_disconnect_threshold=0x%x\n", tuning_disconnect_threshold);
}
static int phy_aml_usb2_phy_init(struct phy *phy)
{
struct u2p_aml_regs *u2p_aml_reg;
struct phy_aml_usb2_priv *priv = dev_get_priv(phy->dev);
u2p_r0_t dev_u2p_r0;
u2p_r1_t dev_u2p_r1;
int i;
int cnt;
unsigned int u2portnum;
int time_dly = RESET_COMPLETE_TIME;
priv->reset_addr = dev_read_addr_index(phy->dev, 1);
if (priv->reset_addr == FDT_ADDR_T_NONE) {
pr_err("Coun't get usb_phy2_pll_base_addr[%d]\n", i);
return -1;
}
*(volatile unsigned long *)(unsigned long)priv->reset_addr |= (1<<2);
udelay(time_dly);
dev_read_u32(phy->dev, "portnum", &u2portnum);
priv->u2_port_num = u2portnum;
priv->usb_phy2_pll_base_addr[0] = dev_read_addr_index(phy->dev, 2);
if (priv->usb_phy2_pll_base_addr[0] == FDT_ADDR_T_NONE) {
pr_err("Coun't get usb_phy2_pll_base_addr[%d]\n", i);
return -1;
}
priv->usb_phy2_pll_base_addr[1] = dev_read_addr_index(phy->dev, 3);
if (priv->usb_phy2_pll_base_addr[1] == FDT_ADDR_T_NONE) {
pr_err("Coun't get usb_phy2_pll_base_addr[%d]\n", i);
return -1;
}
priv->base_addr = dev_read_addr(phy->dev);
debug("usb2 phy: portnum=%d, addr1= 0x%08x, addr2= 0x%08x\n",
u2portnum, priv->usb_phy2_pll_base_addr[0],
priv->usb_phy2_pll_base_addr[1]);
for (i = 0; i < u2portnum; i++) {
u2p_aml_reg = (struct u2p_aml_regs *)((ulong)priv->base_addr + i * PHY_REGISTER_SIZE);
dev_u2p_r0.d32 = u2p_aml_reg->u2p_r0;
dev_u2p_r0.b.host_device= 1;
dev_u2p_r0.b.POR= 0;
u2p_aml_reg->u2p_r0 = dev_u2p_r0.d32;
udelay(10);
*(volatile unsigned long *)(unsigned long)priv->reset_addr |= (1 << (16 + i));
udelay(50);
/* wait for phy ready */
dev_u2p_r1.d32 = u2p_aml_reg->u2p_r1;
cnt = 0;
while (dev_u2p_r1.b.phy_rdy != 1) {
dev_u2p_r1.d32 = u2p_aml_reg->u2p_r1;
/*we wait phy ready max 1ms, common is 100us*/
if (cnt > 200)
break;
else {
cnt++;
udelay(5);
}
}
}
for (i = 0; i < u2portnum; i++) {
debug("------set usb pll\n");
set_usb_pll(phy, (volatile uint32_t *)(unsigned long)priv->usb_phy2_pll_base_addr[i]);
}
return 0;
}
#if 0
static void phy_aml_usb2_reset(struct phy_aml_usb2_priv *priv)
{
return;
}
#endif /* 0 */
static int phy_aml_usb2_tuning(struct phy *phy, int port)
{
struct phy_aml_usb2_priv *priv = dev_get_priv(phy->dev);
unsigned long phy_reg_base;
unsigned int pll_set1, pll_set2, pll_set3, pll_set4;
if (port > 2)
return 0;
dev_read_u32(phy->dev, "pll-setting-4", &pll_set1);
dev_read_u32(phy->dev, "pll-setting-5", &pll_set2);
dev_read_u32(phy->dev, "pll-setting-7", &pll_set4);
dev_read_u32(phy->dev, "pll-setting-6", &pll_set3);
debug("pll1=0x%08x, pll2=0x%08x, pll-setting-3 =0x%08x\n",
pll_set2, pll_set1, pll_set3);
phy_reg_base = priv->usb_phy2_pll_base_addr[port];
if (phy_aml_usb2_check_g12b_revb() == 1) {
(*(volatile uint32_t *)(phy_reg_base + 0x50)) = pll_set1;
(*(volatile uint32_t *)(phy_reg_base + 0x54)) = 0x2a;
(*(volatile uint32_t *)(phy_reg_base + 0x34)) = pll_set3 & (0x1f << 16);
return 0;
}
(*(volatile uint32_t *)(phy_reg_base + 0x10)) = pll_set2;
(*(volatile uint32_t *)(phy_reg_base + 0x50)) = pll_set1;
(*(volatile uint32_t *)(phy_reg_base + 0x38)) = pll_set4;
(*(volatile uint32_t *)(phy_reg_base + 0x34)) = pll_set3;
return 0;
}
#if 0
static void
phy_aml_usb2_set_host_mode(struct phy_aml_usb2_priv *priv)
{
return;
}
#endif /* 0 */
static int phy_aml_usb2_power_on(struct phy *phy)
{
return 0;
}
static int phy_aml_usb2_power_off(struct phy *phy)
{
return 0;
}
struct phy_ops amlogic_usb2_phy_ops = {
.init = phy_aml_usb2_phy_init,
.power_on = phy_aml_usb2_power_on,
.power_off = phy_aml_usb2_power_off,
.tuning = phy_aml_usb2_tuning,
};
int phy_aml_usb2_probe(struct udevice *dev)
{
return 0;
}
static const struct udevice_id phy_aml_usb2_phy_ids[] = {
{ .compatible = "amlogic, amlogic-new-usb2-v2" },
{ }
};
U_BOOT_DRIVER(meson_gxl_usb2_phy) = {
.name = "amlogic_new_usb2_phy",
.id = UCLASS_PHY,
.of_match = phy_aml_usb2_phy_ids,
.probe = phy_aml_usb2_probe,
.ops = &amlogic_usb2_phy_ops,
.priv_auto_alloc_size = sizeof(struct phy_aml_usb2_priv),
};