// SPDX-License-Identifier: GPL-2.0
/**
* Wrapper driver for SERDES used in J721E
*
* Copyright (C) 2019 Texas Instruments Incorporated - http://www.ti.com/
* Author: Kishon Vijay Abraham I <kishon@ti.com>
*/
#include <dt-bindings/phy/phy.h>
#include <linux/clk.h>
#include <linux/clk-provider.h>
#include <linux/gpio.h>
#include <linux/gpio/consumer.h>
#include <linux/io.h>
#include <linux/module.h>
#include <linux/mux/consumer.h>
#include <linux/of_address.h>
#include <linux/of_platform.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/regmap.h>
#include <linux/reset-controller.h>
#define WIZ_SERDES_CTRL 0x404
#define WIZ_SERDES_TOP_CTRL 0x408
#define WIZ_SERDES_RST 0x40c
#define WIZ_SERDES_TYPEC 0x410
#define WIZ_LANECTL(n) (0x480 + (0x40 * (n)))
#define WIZ_MAX_LANES 4
#define WIZ_MUX_NUM_CLOCKS 3
#define WIZ_DIV_NUM_CLOCKS_16G 2
#define WIZ_DIV_NUM_CLOCKS_10G 1
#define WIZ_SERDES_TYPEC_LN10_SWAP BIT(30)
enum wiz_lane_standard_mode {
LANE_MODE_GEN1,
LANE_MODE_GEN2,
LANE_MODE_GEN3,
LANE_MODE_GEN4,
};
enum wiz_refclk_mux_sel {
PLL0_REFCLK,
PLL1_REFCLK,
REFCLK_DIG,
};
enum wiz_refclk_div_sel {
CMN_REFCLK_DIG_DIV,
CMN_REFCLK1_DIG_DIV,
};
static const struct reg_field por_en = REG_FIELD(WIZ_SERDES_CTRL, 31, 31);
static const struct reg_field phy_reset_n = REG_FIELD(WIZ_SERDES_RST, 31, 31);
static const struct reg_field pll1_refclk_mux_sel =
REG_FIELD(WIZ_SERDES_RST, 29, 29);
static const struct reg_field pll0_refclk_mux_sel =
REG_FIELD(WIZ_SERDES_RST, 28, 28);
static const struct reg_field refclk_dig_sel_16g =
REG_FIELD(WIZ_SERDES_RST, 24, 25);
static const struct reg_field refclk_dig_sel_10g =
REG_FIELD(WIZ_SERDES_RST, 24, 24);
static const struct reg_field pma_cmn_refclk_int_mode =
REG_FIELD(WIZ_SERDES_TOP_CTRL, 28, 29);
static const struct reg_field pma_cmn_refclk_mode =
REG_FIELD(WIZ_SERDES_TOP_CTRL, 30, 31);
static const struct reg_field pma_cmn_refclk_dig_div =
REG_FIELD(WIZ_SERDES_TOP_CTRL, 26, 27);
static const struct reg_field pma_cmn_refclk1_dig_div =
REG_FIELD(WIZ_SERDES_TOP_CTRL, 24, 25);
static const struct reg_field p_enable[WIZ_MAX_LANES] = {
REG_FIELD(WIZ_LANECTL(0), 30, 31),
REG_FIELD(WIZ_LANECTL(1), 30, 31),
REG_FIELD(WIZ_LANECTL(2), 30, 31),
REG_FIELD(WIZ_LANECTL(3), 30, 31),
};
static const struct reg_field p_align[WIZ_MAX_LANES] = {
REG_FIELD(WIZ_LANECTL(0), 29, 29),
REG_FIELD(WIZ_LANECTL(1), 29, 29),
REG_FIELD(WIZ_LANECTL(2), 29, 29),
REG_FIELD(WIZ_LANECTL(3), 29, 29),
};
static const struct reg_field p_raw_auto_start[WIZ_MAX_LANES] = {
REG_FIELD(WIZ_LANECTL(0), 28, 28),
REG_FIELD(WIZ_LANECTL(1), 28, 28),
REG_FIELD(WIZ_LANECTL(2), 28, 28),
REG_FIELD(WIZ_LANECTL(3), 28, 28),
};
static const struct reg_field p_standard_mode[WIZ_MAX_LANES] = {
REG_FIELD(WIZ_LANECTL(0), 24, 25),
REG_FIELD(WIZ_LANECTL(1), 24, 25),
REG_FIELD(WIZ_LANECTL(2), 24, 25),
REG_FIELD(WIZ_LANECTL(3), 24, 25),
};
static const struct reg_field typec_ln10_swap =
REG_FIELD(WIZ_SERDES_TYPEC, 30, 30);
struct wiz_clk_mux {
struct clk_hw hw;
struct regmap_field *field;
u32 *table;
struct clk_init_data clk_data;
};
#define to_wiz_clk_mux(_hw) container_of(_hw, struct wiz_clk_mux, hw)
struct wiz_clk_divider {
struct clk_hw hw;
struct regmap_field *field;
struct clk_div_table *table;
struct clk_init_data clk_data;
};
#define to_wiz_clk_div(_hw) container_of(_hw, struct wiz_clk_divider, hw)
struct wiz_clk_mux_sel {
struct regmap_field *field;
u32 table[4];
const char *node_name;
};
struct wiz_clk_div_sel {
struct regmap_field *field;
struct clk_div_table *table;
const char *node_name;
};
static struct wiz_clk_mux_sel clk_mux_sel_16g[] = {
{
/*
* Mux value to be configured for each of the input clocks
* in the order populated in device tree
*/
.table = { 1, 0 },
.node_name = "pll0-refclk",
},
{
.table = { 1, 0 },
.node_name = "pll1-refclk",
},
{
.table = { 1, 3, 0, 2 },
.node_name = "refclk-dig",
},
};
static struct wiz_clk_mux_sel clk_mux_sel_10g[] =