// SPDX-License-Identifier: GPL-2.0
/*
* phy-rtk-usb2.c RTK usb2.0 PHY driver
*
* Copyright (C) 2023 Realtek Semiconductor Corporation
*
*/
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/platform_device.h>
#include <linux/uaccess.h>
#include <linux/debugfs.h>
#include <linux/nvmem-consumer.h>
#include <linux/regmap.h>
#include <linux/sys_soc.h>
#include <linux/mfd/syscon.h>
#include <linux/phy/phy.h>
#include <linux/usb.h>
/* GUSB2PHYACCn register */
#define PHY_NEW_REG_REQ BIT(25)
#define PHY_VSTS_BUSY BIT(23)
#define PHY_VCTRL_SHIFT 8
#define PHY_REG_DATA_MASK 0xff
#define GET_LOW_NIBBLE(addr) ((addr) & 0x0f)
#define GET_HIGH_NIBBLE(addr) (((addr) & 0xf0) >> 4)
#define EFUS_USB_DC_CAL_RATE 2
#define EFUS_USB_DC_CAL_MAX 7
#define EFUS_USB_DC_DIS_RATE 1
#define EFUS_USB_DC_DIS_MAX 7
#define MAX_PHY_DATA_SIZE 20
#define OFFEST_PHY_READ 0x20
#define MAX_USB_PHY_NUM 4
#define MAX_USB_PHY_PAGE0_DATA_SIZE 16
#define MAX_USB_PHY_PAGE1_DATA_SIZE 16
#define MAX_USB_PHY_PAGE2_DATA_SIZE 8
#define SET_PAGE_OFFSET 0xf4
#define SET_PAGE_0 0x9b
#define SET_PAGE_1 0xbb
#define SET_PAGE_2 0xdb
#define PAGE_START 0xe0
#define PAGE0_0XE4 0xe4
#define PAGE0_0XE6 0xe6
#define PAGE0_0XE7 0xe7
#define PAGE1_0XE0 0xe0
#define PAGE1_0XE2 0xe2
#define SENSITIVITY_CTRL (BIT(4) | BIT(5) | BIT(6))
#define ENABLE_AUTO_SENSITIVITY_CALIBRATION BIT(2)
#define DEFAULT_DC_DRIVING_VALUE (0x8)
#define DEFAULT_DC_DISCONNECTION_VALUE (0x6)
#define HS_CLK_SELECT BIT(6)
struct phy_reg {
void __iomem *reg_wrap_vstatus;
void __iomem *reg_gusb2phyacc0;
int vstatus_index;
};
struct phy_data {
u8 addr;
u8 data;
};
struct phy_cfg {
int page0_size;
struct phy_data page0[MAX_USB_PHY_PAGE0_DATA_SIZE];
int page1_size;
struct phy_data page1[MAX_USB_PHY_PAGE1_DATA_SIZE];
int page2_size;
struct phy_data page2[MAX_USB_PHY_PAGE2_DATA_SIZE];
int num_phy;
bool check_efuse;
int check_efuse_version;
#define CHECK_EFUSE_V1 1
#define CHECK_EFUSE_V2 2
int efuse_dc_driving_rate;
int efuse_dc_disconnect_rate;
int dc_driving_mask;
int dc_disconnect_mask;
bool usb_dc_disconnect_at_page0;
int driving_updated_for_dev_dis;
bool do_toggle;
bool do_toggle_driving;
bool use_default_parameter;
bool is_double_sensitivity_mode;
};
struct phy_parameter {
struct phy_reg phy_reg;
/* Get from efuse */
s8 efuse_usb_dc_cal;
s8 efuse_usb_dc_dis;
/* Get from dts */
bool inverse_hstx_sync_clock;
u32 driving_level;
s32 driving_level_compensate;
s32 disconnection_compensate;
};
struct rtk_phy {
struct device *dev;
struct phy_cfg *phy_cfg;
int num_phy;
struct phy_parameter *phy_parameter;
struct dentry *debug_dir;
};
/* mapping 0xE0 to 0 ... 0xE7 to 7, 0xF0 to 8 ,,, 0xF7 to 15 */
static inline int page_addr_to_array_index(u8 addr)
{
return (int)((((addr) - PAGE_START) & 0x7) +
((((addr) - PAGE_START) & 0x10) >> 1));
}
static inline u8 array_index_to_page_addr(int index)
{
return ((((index) + PAGE_START) & 0x7) +
((((index) & 0x8) << 1) + PAGE_START));
}
#define PHY_IO_TIMEOUT_USEC (50000)
#define PHY_IO_DELAY_US (100)
static inline int utmi_wait_register(void __iomem *reg, u32 mask, u32 result)
{
int ret;
unsigned int val;
ret = read_poll_timeout(readl, val, ((val & mask) == result),