Rtwo/kernel/motorola/sm8550/drivers/usb/redriver/ps5169.c
2025-09-30 19:22:48 -05:00

1077 lines
27 KiB
C

// SPDX-License-Identifier: GPL-2.0-only
#include <linux/module.h>
#include <linux/i2c.h>
#include <linux/regmap.h>
#include <linux/debugfs.h>
#include <linux/seq_file.h>
#include <linux/slab.h>
#include <linux/usb/ch9.h>
#include <linux/debugfs.h>
#include <linux/uaccess.h>
#include <linux/ctype.h>
#include <linux/pinctrl/consumer.h>
#include <linux/of_gpio.h>
#include <linux/usb/redriver.h>
#include <linux/regulator/consumer.h>
/* dp eq */
#define PS5169_DP_EQ0_REG 0x52
#define PS5169_DP_EQ0_MASK 0x70
#define PS5169_DP_EQ1_REG 0x5e
#define PS5169_DP_EQ1_MASK 0x07
#define PS5169_DP_EQ_MAX 5
#define PS5169_DP_EQ_DEFAULT 0
static const struct ps5169_dp_eq {
unsigned int eq0; /* 0x52 bit 6:4 */
unsigned int eq1; /* 0x5e bit 2:0 */
} dp_eq[] = {
{0x00, 0x04}, /* 0:2db */
{0x10, 0x05}, /* 1:5.5db */
{0x20, 0x06}, /* 2:6.5db */
{0x30, 0x06}, /* 3:7.5db */
{0x40, 0x06}, /* 4:8db */
{0x50, 0x07}, /* 5:8.5db */
{0x60, 0x07}, /* 6:9.5db */
{0x70, 0x07}, /* 7:10db ??? */
};
#define PS5169_DP_EQ0_VAL(eq) dp_eq[eq].eq0
#define PS5169_DP_EQ1_VAL(eq) dp_eq[eq].eq1
/* dp gain */
#define PS5169_DP_GAIN_REG 0x5c
#define PS5169_DP_GAIN_MASK BIT(4)
#define PS5169_DP_GAIN_MAX 1 /* -0.9db */
#define PS5169_DP_GAIN_DEFAULT 0 /* 0 db */
/* usb tx eq */
#define PS5169_USB_TX_EQ0_REG 0x50
#define PS5169_USB_TX_EQ0_MASK 0x70
#define PS5169_USB_TX_EQ1_REG 0x5d
#define PS5169_USB_TX_EQ1_MASK 0x70
#define PS5169_USB_TX_EQ2_REG 0x54
#define PS5169_USB_TX_EQ2_MASK 0xf0
#define PS5169_USB_TX_EQ_MAX 7
#define PS5169_USB_TX_EQ_DEFAULT 1
static const struct ps5169_usb_tx_eq {
unsigned int eq0; /* 0x50 bit 6:4 */
unsigned int eq1; /* 0x5d bit 6:4 */
unsigned int eq2; /* 0x54 bit 7:4 */
} usb_tx_eq[] = {
{0x00, 0x40, 0x00}, /* 0:2db */
{0x10, 0x50, 0x10}, /* 1:5.5db */
{0x20, 0x60, 0x10}, /* 2:6.5db */
{0x30, 0x60, 0x50}, /* 3:7.5db */
{0x40, 0x60, 0xc0}, /* 4:8db */
{0x50, 0x70, 0x50}, /* 5:8.5db */
{0x60, 0x70, 0xf0}, /* 6:9.5db */
{0x70, 0x70, 0xf0}, /* 7:10db ???? */
};
#define PS5169_USB_TX_EQ0_VAL(eq) usb_tx_eq[eq].eq0
#define PS5169_USB_TX_EQ1_VAL(eq) usb_tx_eq[eq].eq1
#define PS5169_USB_TX_EQ2_VAL(eq) usb_tx_eq[eq].eq2
/* usb tx gain */
#define PS5169_USB_TX_GAIN_REG 0x5c
#define PS5169_USB_TX_GAIN_MASK BIT(0)
#define PS5169_USB_TX_GAIN_MAX 1 /* -0.9db */
#define PS5169_USB_TX_GAIN_DEFAULT 0 /* 0db */
/* usb rx eq */
#define PS5169_USB_RX_EQ0_REG 0x51
#define PS5169_USB_RX_EQ0_MASK 0x70
#define PS5169_USB_RX_EQ1_REG 0x77
#define PS5169_USB_RX_EQ1_MASK 0xf0
#define PS5169_USB_RX_EQ2_REG 0x54
#define PS5169_USB_RX_EQ2_MASK 0x0f
#define PS5169_USB_RX_EQ3_REG 0x78
#define PS5169_USB_RX_EQ3_MASK 0xe1
#define PS5169_USB_RX_EQ_MAX 7
static const struct ps5169_usb_rx_eq {
unsigned int eq0; /* 0x51 bit 7:4 2:1 */
unsigned int eq1; /* 0x77 bit 7:4 */
unsigned int eq2; /* 0x54 bit 3:0 */
unsigned int eq3; /* 0x78 bit 7:5 0 */
} usb_rx_eq[] = {
{0x86, 0x00, 0x00, 0x20}, /* 0:5.2db */
{0x96, 0x00, 0x01, 0x20}, /* 1:6db */
{0xa6, 0x50, 0x01, 0x40}, /* 2:7db */
{0xb6, 0x50, 0x05, 0x40}, /* 3:8db */
{0xc6, 0xb0, 0x0c, 0x80}, /* 4:8.8db */
{0xd6, 0xf0, 0x05, 0x80}, /* 5:9.6db */
{0xe6, 0xf0, 0x0f, 0x80}, /* 6:10.4db */
{0xf6, 0x30, 0x0f, 0xa1}, /* 7:11.2db */
};
#define PS5169_USB_RX_EQ0_VAL(eq) usb_rx_eq[eq].eq0
#define PS5169_USB_RX_EQ1_VAL(eq) usb_rx_eq[eq].eq1
#define PS5169_USB_RX_EQ2_VAL(eq) usb_rx_eq[eq].eq2
#define PS5169_USB_RX_EQ3_VAL(eq) usb_rx_eq[eq].eq3
/* usb rx gain */
#define PS5169_USB_RX_GAIN_REG 0x5c
#define PS5169_USB_RX_GAIN_MASK BIT(2)
#define PS5169_USB_RX_GAIN_MAX 1 /* -0.9db */
#define PS5169_USB_RX_GAIN_DEFAULT 0 /* 0db */
/* configuration */
#define PS5169_CONFIG_REG 0x40
#define PS5169_CONFIG_REG_DEFAULT 0x40
#define PS5169_PD_ACTIVE BIT(7)
#define PS5169_USB3_EN BIT(6)
#define PS5169_DP_EN BIT(5)
#define PS5169_FLIPPED BIT(4)
#define PS5169_EMODE BIT(3)
/* chip id */
#define PS5169_CHIPID_REG 0xAC
#define PS5169_CHIP_ID 0x6987
enum operation_mode {
OP_MODE_NONE, /* 4 lanes disabled */
OP_MODE_USB3, /* 2 lanes for USB and 2 lanes disabled */
OP_MODE_DP, /* 4 lanes DP */
OP_MODE_USB3_AND_DP, /* 2 lanes for USB and 2 lanes DP */
OP_MODE_DEFAULT, /* 4 lanes USB */
};
#define NOTIFIER_PRIORITY 1
struct ps5169_redriver {
struct usb_redriver r;
struct device *dev;
struct i2c_client *client;
struct regulator *vcc;
struct regulator *vio;
struct regmap *regmap;
struct reg_sequence *config_seqs;
unsigned int config_seqs_cnt;
struct dentry *debug_root;
/* tuning purpose */
unsigned int dpeq;
unsigned int dpgain;
unsigned int usbtxeq;
unsigned int usbtxgain;
unsigned int usbrxeq;
unsigned int usbrxgain;
int typec_orientation;
enum operation_mode op_mode;
int orientation_gpio;
int enable_gpio;
struct work_struct pullup_work;
bool work_ongoing;
struct work_struct host_work;
};
static const char * const opmode_string[] = {
[OP_MODE_NONE] = "NONE",
[OP_MODE_USB3] = "USB3",
[OP_MODE_DP] = "DP",
[OP_MODE_USB3_AND_DP] = "USB3 and DP",
[OP_MODE_DEFAULT] = "DEFAULT",
};
#define OPMODESTR(x) opmode_string[x]
static inline int ps5169_redriver_write_reg_bits(struct ps5169_redriver *ps5169,
unsigned int reg, unsigned int val, unsigned int mask);
static void ps5169_config_work_mode(struct ps5169_redriver *ps5169,
enum operation_mode mode);
static int ps5169_config_seqs_init(struct ps5169_redriver *ps5169);
static int ps5169_redriver_enable_chip(struct ps5169_redriver *ps5169, bool en)
{
if (!gpio_is_valid(ps5169->enable_gpio)) {
dev_err(ps5169->dev, "%s: Invalid enable_gpio: %d\n", __func__,
ps5169->enable_gpio);
return -EINVAL;
}
gpio_set_value(ps5169->enable_gpio, en);
if (en)
mdelay(10);
return 0;
}
static int ps5169_get_orientation(struct usb_redriver *r)
{
struct ps5169_redriver *ps5169 =
container_of(r, struct ps5169_redriver, r);
int orientation;
dev_dbg(ps5169->dev, "%s: mode %s\n", __func__,
OPMODESTR(ps5169->op_mode));
if (!gpio_is_valid(ps5169->orientation_gpio))
orientation = 0;
else
orientation = gpio_get_value(ps5169->orientation_gpio);
dev_info(ps5169->dev, "%s: mode %s, orientation %d\n", __func__,
OPMODESTR(ps5169->op_mode), orientation);
return orientation;
}
static int ps5169_notify_connect(struct usb_redriver *r, int ort)
{
struct ps5169_redriver *ps5169 =
container_of(r, struct ps5169_redriver, r);
enum operation_mode mode = ps5169->op_mode;
if (ps5169->op_mode == OP_MODE_NONE)
mode = OP_MODE_USB3;
dev_info(ps5169->dev, "%s: op mode %s -> %s, orientation %s\n",
__func__,
OPMODESTR(ps5169->op_mode), OPMODESTR(mode),
ort == ORIENTATION_CC1 ? "CC1" : "CC2");
ps5169->op_mode = mode;
ps5169->typec_orientation = ort;
ps5169_redriver_enable_chip(ps5169, true);
ps5169_config_seqs_init(ps5169);
ps5169_config_work_mode(ps5169, ps5169->op_mode);
return 0;
}
static int ps5169_notify_disconnect(struct usb_redriver *r)
{
struct ps5169_redriver *ps5169 =
container_of(r, struct ps5169_redriver, r);
if (ps5169->op_mode == OP_MODE_NONE)
return 0;
dev_info(ps5169->dev, "%s: op mode %s -> %s\n", __func__,
OPMODESTR(ps5169->op_mode), OPMODESTR(OP_MODE_NONE));
ps5169->op_mode = OP_MODE_NONE;
ps5169_redriver_enable_chip(ps5169, false);
return 0;
}
static int ps5169_release_usb_lanes(struct usb_redriver *r, int ort, int num)
{
struct ps5169_redriver *ps5169 =
container_of(r, struct ps5169_redriver, r);
enum operation_mode mode = ps5169->op_mode;
if (num == 4)
mode = OP_MODE_DP;
else if (num == 2)
mode = OP_MODE_USB3_AND_DP;
else
mode = ps5169->op_mode;
ps5169->typec_orientation = ort;
if (mode != ps5169->op_mode) {
dev_info(ps5169->dev, "%s: op mode %s -> %s\n", __func__,
OPMODESTR(ps5169->op_mode), OPMODESTR(mode));
ps5169->op_mode = mode;
ps5169_redriver_enable_chip(ps5169, true);
ps5169_config_seqs_init(ps5169);
ps5169_config_work_mode(ps5169, mode);
}
return 0;
}
static void ps5169_gadget_pullup_work(struct work_struct *w)
{
struct ps5169_redriver *ps5169 =
container_of(w, struct ps5169_redriver, pullup_work);
ps5169_redriver_enable_chip(ps5169, false);
usleep_range(1000, 1500);
ps5169_redriver_enable_chip(ps5169, true);
ps5169_config_seqs_init(ps5169);
ps5169_config_work_mode(ps5169, ps5169->op_mode);
ps5169->work_ongoing = false;
}
static int ps5169_gadget_pullup_enter(struct usb_redriver *r, int is_on)
{
struct ps5169_redriver *ps5169 =
container_of(r, struct ps5169_redriver, r);
u64 time = 0;
dev_info(ps5169->dev, "%s: mode %s, %d, %d\n", __func__,
OPMODESTR(ps5169->op_mode), is_on, ps5169->work_ongoing);
if (ps5169->op_mode != OP_MODE_USB3)
return -EINVAL;
if (!is_on)
return 0;
while (ps5169->work_ongoing) {
udelay(1);
if (time++ > 500000) {
dev_warn(ps5169->dev, "pullup timeout\n");
break;
}
}
dev_info(ps5169->dev, "pull-up disable work took %llu us\n", time);
return 0;
}
static int ps5169_gadget_pullup_exit(struct usb_redriver *r, int is_on)
{
struct ps5169_redriver *ps5169 =
container_of(r, struct ps5169_redriver, r);
dev_info(ps5169->dev, "%s: mode %s, %d, %d\n", __func__,
OPMODESTR(ps5169->op_mode), is_on, ps5169->work_ongoing);
if (ps5169->op_mode != OP_MODE_USB3)
return -EINVAL;
if (is_on)
return 0;
ps5169->work_ongoing = true;
queue_work(system_highpri_wq, &ps5169->pullup_work);
return 0;
}
static void ps5169_host_work(struct work_struct *w)
{
struct ps5169_redriver *ps5169 =
container_of(w, struct ps5169_redriver, host_work);
ps5169_redriver_enable_chip(ps5169, false);
usleep_range(2000, 2500);
ps5169_redriver_enable_chip(ps5169, true);
ps5169_config_seqs_init(ps5169);
ps5169_config_work_mode(ps5169, ps5169->op_mode);
}
static int ps5169_host_powercycle(struct usb_redriver *r)
{
struct ps5169_redriver *ps5169 =
container_of(r, struct ps5169_redriver, r);
if (ps5169->op_mode != OP_MODE_USB3)
return -EINVAL;
schedule_work(&ps5169->host_work);
return 0;
}
static void ps5169_redriver_gpio_init(struct ps5169_redriver *ps5169)
{
struct device *dev = ps5169->dev;
int rc;
ps5169->orientation_gpio = of_get_named_gpio(dev->of_node, "redriver,orientation-gpio", 0);
if (!gpio_is_valid(ps5169->orientation_gpio)) {
dev_err(dev, "Failed to get orientation gpio\n");
return;
}
rc = devm_gpio_request(dev, ps5169->orientation_gpio, "redriver-orientation-gpio");
if (rc < 0) {
dev_err(dev, "Failed to request orientation gpio\n");
ps5169->orientation_gpio = -EINVAL;
return;
}
ps5169->r.has_orientation = true;
ps5169->enable_gpio = of_get_named_gpio(dev->of_node, "redriver,enable-gpio", 0);
if (!gpio_is_valid(ps5169->enable_gpio)) {
dev_err(dev, "Failed to get enable gpio\n");
return;
}
rc = devm_gpio_request(dev, ps5169->enable_gpio, "redriver-enable-gpio");
if (rc < 0) {
dev_err(dev, "Failed to request enable gpio\n");
ps5169->enable_gpio = -EINVAL;
return;
}
gpio_direction_output(ps5169->enable_gpio, 0);
}
static inline int ps5169_redriver_write_reg_bits(struct ps5169_redriver *ps5169,
unsigned int reg, unsigned int val, unsigned int mask)
{
int ret = regmap_update_bits(ps5169->regmap, reg, mask, val);
if (ret < 0)
dev_err(&ps5169->client->dev, "error update reg %u, ret=%d\n", reg, ret);
dev_dbg(&ps5169->client->dev, "update reg[%u]=[%x], %x\n", reg, val, mask);
return ret;
}
static int ps5169_config_dp_eq(struct ps5169_redriver *ps5169,
unsigned int eq)
{
if (eq > PS5169_DP_EQ_MAX) {
dev_err(&ps5169->client->dev, "error dp eq value %u\n", eq);
return -EINVAL;
}
ps5169_redriver_write_reg_bits(ps5169, PS5169_DP_EQ0_REG,
PS5169_DP_EQ0_VAL(eq), PS5169_DP_EQ0_MASK);
ps5169_redriver_write_reg_bits(ps5169, PS5169_DP_EQ1_REG,
PS5169_DP_EQ1_VAL(eq), PS5169_DP_EQ1_MASK);
return 0;
}
static int ps5169_config_dp_gain(struct ps5169_redriver *ps5169,
unsigned int gain)
{
if (gain > PS5169_DP_GAIN_MAX) {
dev_err(&ps5169->client->dev, "error dp gain value %u\n", gain);
return -EINVAL;
}
ps5169_redriver_write_reg_bits(ps5169, PS5169_DP_GAIN_REG, gain,
PS5169_DP_GAIN_MASK);
return 0;
}
static int ps5169_config_usb_tx_eq(struct ps5169_redriver *ps5169,
unsigned int eq)
{
if (eq > PS5169_USB_TX_EQ_MAX) {
dev_err(&ps5169->client->dev, "error usb tx eq value %u\n", eq);
return -EINVAL;
}
ps5169_redriver_write_reg_bits(ps5169, PS5169_USB_TX_EQ0_REG,
PS5169_USB_TX_EQ0_VAL(eq), PS5169_USB_TX_EQ0_MASK);
ps5169_redriver_write_reg_bits(ps5169, PS5169_USB_TX_EQ1_REG,
PS5169_USB_TX_EQ1_VAL(eq), PS5169_USB_TX_EQ1_MASK);
ps5169_redriver_write_reg_bits(ps5169, PS5169_USB_TX_EQ2_REG,
PS5169_USB_TX_EQ2_VAL(eq), PS5169_USB_TX_EQ2_MASK);
return 0;
}
static int ps5169_config_usb_tx_gain(struct ps5169_redriver *ps5169,
unsigned int gain)
{
if (gain > PS5169_USB_TX_GAIN_MAX) {
dev_err(&ps5169->client->dev,
"error usb tx gain value %u\n", gain);
return -EINVAL;
}
ps5169_redriver_write_reg_bits(ps5169, PS5169_USB_TX_GAIN_REG, gain,
PS5169_USB_TX_GAIN_MASK);
return 0;
}
static int ps5169_config_usb_rx_eq(struct ps5169_redriver *ps5169,
unsigned int eq)
{
if (eq > PS5169_USB_RX_EQ_MAX) {
dev_err(&ps5169->client->dev, "error usb rx eq value %u\n", eq);
return -EINVAL;
}
ps5169_redriver_write_reg_bits(ps5169, PS5169_USB_RX_EQ0_REG,
PS5169_USB_RX_EQ0_VAL(eq), PS5169_USB_RX_EQ0_MASK);
ps5169_redriver_write_reg_bits(ps5169, PS5169_USB_RX_EQ1_REG,
PS5169_USB_RX_EQ1_VAL(eq), PS5169_USB_RX_EQ1_MASK);
ps5169_redriver_write_reg_bits(ps5169, PS5169_USB_RX_EQ2_REG,
PS5169_USB_RX_EQ2_VAL(eq), PS5169_USB_RX_EQ2_MASK);
ps5169_redriver_write_reg_bits(ps5169, PS5169_USB_RX_EQ3_REG,
PS5169_USB_RX_EQ3_VAL(eq), PS5169_USB_RX_EQ3_MASK);
return 0;
}
static int ps5169_config_usb_rx_gain(struct ps5169_redriver *ps5169,
unsigned int gain)
{
if (gain > PS5169_USB_RX_GAIN_MAX) {
dev_err(&ps5169->client->dev,
"error usb rx gain value %u\n", gain);
return -EINVAL;
}
ps5169_redriver_write_reg_bits(ps5169, PS5169_USB_RX_GAIN_REG, gain,
PS5169_USB_RX_GAIN_MASK);
return 0;
}
/* save user configuration and dump for device tree */
static inline void update_config_reg(struct ps5169_redriver *ps5169,
unsigned int reg, unsigned int val, unsigned int mask)
{
int i;
for (i = 0; i < ps5169->config_seqs_cnt; i++)
if (ps5169->config_seqs[i].reg == reg) {
ps5169->config_seqs[i].def &= ~mask;
ps5169->config_seqs[i].def |= (val & mask);
break;
}
if (i == ps5169->config_seqs_cnt)
pr_err("can't find reg %u in init regs\n", reg);
}
static int dp_eq_get(void *data, u64 *val)
{
struct ps5169_redriver *ps5169 = data;
*val = ps5169->dpeq;
return 0;
}
static int dp_eq_set(void *data, u64 val)
{
int ret;
struct ps5169_redriver *ps5169 = data;
ret = ps5169_config_dp_eq(ps5169, val);
if (ret)
return ret;
ps5169->dpeq = val;
update_config_reg(ps5169, PS5169_DP_EQ0_REG,
PS5169_DP_EQ0_VAL(val), PS5169_DP_EQ0_MASK);
update_config_reg(ps5169, PS5169_DP_EQ1_REG,
PS5169_DP_EQ1_VAL(val), PS5169_DP_EQ1_MASK);
return 0;
}
DEFINE_DEBUGFS_ATTRIBUTE(dp_eq_ops, dp_eq_get, dp_eq_set, "%llu\n");
static int dp_gain_get(void *data, u64 *val)
{
struct ps5169_redriver *ps5169 = data;
*val = ps5169->dpgain;
return 0;
}
static int dp_gain_set(void *data, u64 val)
{
int ret;
struct ps5169_redriver *ps5169 = data;
ret = ps5169_config_dp_gain(ps5169, val);
if (ret)
return ret;
ps5169->dpgain = val;
update_config_reg(ps5169, PS5169_DP_GAIN_REG, val,
PS5169_DP_GAIN_MASK);
return 0;
}
DEFINE_DEBUGFS_ATTRIBUTE(dp_gain_ops, dp_gain_get, dp_gain_set, "%llu\n");
static int usb_tx_eq_get(void *data, u64 *val)
{
struct ps5169_redriver *ps5169 = data;
*val = ps5169->usbtxeq;
return 0;
}
static int usb_tx_eq_set(void *data, u64 val)
{
int ret;
struct ps5169_redriver *ps5169 = data;
ret = ps5169_config_usb_tx_eq(ps5169, val);
if (ret)
return ret;
ps5169->usbtxeq = val;
update_config_reg(ps5169, PS5169_USB_TX_EQ0_REG,
PS5169_USB_TX_EQ0_VAL(val), PS5169_USB_TX_EQ0_MASK);
update_config_reg(ps5169, PS5169_USB_TX_EQ1_REG,
PS5169_USB_TX_EQ1_VAL(val), PS5169_USB_TX_EQ1_MASK);
update_config_reg(ps5169, PS5169_USB_TX_EQ2_REG,
PS5169_USB_TX_EQ2_VAL(val), PS5169_USB_TX_EQ2_MASK);
return 0;
}
DEFINE_DEBUGFS_ATTRIBUTE(usb_tx_eq_ops, usb_tx_eq_get, usb_tx_eq_set, "%llu\n");
static int usb_tx_gain_get(void *data, u64 *val)
{
struct ps5169_redriver *ps5169 = data;
*val = ps5169->usbtxgain;
return 0;
}
static int usb_tx_gain_set(void *data, u64 val)
{
int ret;
struct ps5169_redriver *ps5169 = data;
ret = ps5169_config_usb_tx_gain(ps5169, val);
if (ret)
return ret;
ps5169->usbtxgain = val;
update_config_reg(ps5169, PS5169_USB_TX_GAIN_REG, val,
PS5169_USB_TX_GAIN_MASK);
return ret;
}
DEFINE_DEBUGFS_ATTRIBUTE(usb_tx_gain_ops,
usb_tx_gain_get, usb_tx_gain_set, "%llu\n");
static int usb_rx_eq_get(void *data, u64 *val)
{
struct ps5169_redriver *ps5169 = data;
*val = ps5169->usbrxeq;
return 0;
}
static int usb_rx_eq_set(void *data, u64 val)
{
int ret;
struct ps5169_redriver *ps5169 = data;
ret = ps5169_config_usb_rx_eq(ps5169, val);
if (ret)
return ret;
ps5169->usbtxeq = val;
update_config_reg(ps5169, PS5169_USB_RX_EQ0_REG,
PS5169_USB_RX_EQ0_VAL(val), PS5169_USB_RX_EQ0_MASK);
update_config_reg(ps5169, PS5169_USB_RX_EQ1_REG,
PS5169_USB_RX_EQ1_VAL(val), PS5169_USB_RX_EQ1_MASK);
update_config_reg(ps5169, PS5169_USB_RX_EQ2_REG,
PS5169_USB_RX_EQ2_VAL(val), PS5169_USB_RX_EQ2_MASK);
update_config_reg(ps5169, PS5169_USB_RX_EQ3_REG,
PS5169_USB_RX_EQ3_VAL(val), PS5169_USB_RX_EQ3_MASK);
return 0;
}
DEFINE_DEBUGFS_ATTRIBUTE(usb_rx_eq_ops,
usb_rx_eq_get, usb_rx_eq_set, "%llu\n");
static int usb_rx_gain_get(void *data, u64 *val)
{
struct ps5169_redriver *ps5169 = data;
*val = ps5169->usbrxgain;
return 0;
}
static int usb_rx_gain_set(void *data, u64 val)
{
int ret;
struct ps5169_redriver *ps5169 = data;
ret = ps5169_config_usb_rx_gain(ps5169, val);
if (ret)
return ret;
ps5169->usbrxgain = val;
update_config_reg(ps5169, PS5169_USB_RX_GAIN_REG, val,
PS5169_USB_RX_GAIN_MASK);
return ret;
}
static int config_seqs_show(struct seq_file *s, void *unused)
{
struct ps5169_redriver *ps5169 = s->private;
int i;
for (i = 0; i < ps5169->config_seqs_cnt; i++)
seq_printf(s, "0x%02x 0x%02x %u\n",
ps5169->config_seqs[i].reg,
ps5169->config_seqs[i].def,
ps5169->config_seqs[i].delay_us);
return 0;
}
DEFINE_SHOW_ATTRIBUTE(config_seqs);
DEFINE_DEBUGFS_ATTRIBUTE(usb_rx_gain_ops,
usb_rx_gain_get, usb_rx_gain_set, "%llu\n");
static void ps5169_debugfs_create(struct ps5169_redriver *ps5169)
{
struct i2c_client *client = ps5169->client;
ps5169->debug_root = debugfs_create_dir("ps5169", NULL);
if (!ps5169->debug_root) {
dev_warn(&client->dev, "couldn't create debug dir\n");
return;
}
debugfs_create_file("dp-eq", 0644,
ps5169->debug_root, ps5169, &dp_eq_ops);
debugfs_create_file("dp-gain", 0644,
ps5169->debug_root, ps5169, &dp_gain_ops);
debugfs_create_file("usb-tx-eq", 0644,
ps5169->debug_root, ps5169, &usb_tx_eq_ops);
debugfs_create_file("usb-tx-gain", 0644,
ps5169->debug_root, ps5169, &usb_tx_gain_ops);
debugfs_create_file("usb-rx-eq", 0644,
ps5169->debug_root, ps5169, &usb_rx_eq_ops);
debugfs_create_file("usb-rx-gain", 0644,
ps5169->debug_root, ps5169, &usb_rx_gain_ops);
debugfs_create_file("config_seqs", 0444,
ps5169->debug_root, ps5169, &config_seqs_fops);
}
static int ps5169_config_seqs_init(struct ps5169_redriver *ps5169)
{
int ret = 0;
regmap_register_patch(ps5169->regmap,
ps5169->config_seqs, ps5169->config_seqs_cnt);
ret = ps5169_redriver_write_reg_bits(ps5169, PS5169_CONFIG_REG,
PS5169_PD_ACTIVE, PS5169_PD_ACTIVE);
return ret;
}
static void ps5169_config_work_mode(struct ps5169_redriver *ps5169,
enum operation_mode mode)
{
struct i2c_client *client = ps5169->client;
unsigned int val, mask;
switch (mode) {
case OP_MODE_NONE:
mask = PS5169_PD_ACTIVE | PS5169_USB3_EN | PS5169_DP_EN;
val = PS5169_PD_ACTIVE;
dev_dbg(&client->dev, "write config %02x | %02x\n", val, mask);
ps5169_redriver_write_reg_bits(ps5169,
PS5169_CONFIG_REG, val, mask);
ps5169_redriver_write_reg_bits(ps5169, 0xa0, 0x02, 0xff);
ps5169_redriver_write_reg_bits(ps5169, 0xa1, 0x00, 0xff);
break;
case OP_MODE_USB3:
mask = PS5169_PD_ACTIVE | PS5169_USB3_EN | PS5169_DP_EN |
PS5169_FLIPPED;
val = PS5169_PD_ACTIVE | PS5169_USB3_EN;
if (ps5169->typec_orientation == ORIENTATION_CC2)
val |= PS5169_FLIPPED;
dev_dbg(&client->dev, "write config %02x | %02x\n", val, mask);
ps5169_redriver_write_reg_bits(ps5169,
PS5169_CONFIG_REG, val, mask);
break;
case OP_MODE_DP:
mask = PS5169_PD_ACTIVE | PS5169_USB3_EN | PS5169_DP_EN |
PS5169_FLIPPED;
val = PS5169_PD_ACTIVE | PS5169_DP_EN;
if (ps5169->typec_orientation == ORIENTATION_CC2)
val |= PS5169_FLIPPED;
dev_dbg(&client->dev, "write config %02x | %02x\n", val, mask);
ps5169_redriver_write_reg_bits(ps5169,
PS5169_CONFIG_REG, val, mask);
ps5169_redriver_write_reg_bits(ps5169, 0xa0, 0x00, 0xff);
ps5169_redriver_write_reg_bits(ps5169, 0xa1, 0x04, 0xff);
break;
case OP_MODE_USB3_AND_DP:
mask = PS5169_PD_ACTIVE | PS5169_USB3_EN | PS5169_DP_EN |
PS5169_FLIPPED;
val = PS5169_PD_ACTIVE | PS5169_USB3_EN | PS5169_DP_EN;
if (ps5169->typec_orientation == ORIENTATION_CC2)
val |= PS5169_FLIPPED;
dev_dbg(&client->dev, "write config %02x | %02x\n", val, mask);
ps5169_redriver_write_reg_bits(ps5169,
PS5169_CONFIG_REG, val, mask);
ps5169_redriver_write_reg_bits(ps5169, 0xa0, 0x00, 0xff);
ps5169_redriver_write_reg_bits(ps5169, 0xa1, 0x04, 0xff);
break;
default:
pr_err("operation mode is not support\n");
return;
}
}
static const struct regmap_config ps5169_regmap = {
.max_register = 0xff,
.reg_bits = 8,
.val_bits = 8,
};
static int ps5169_i2c_probe(struct i2c_client *client,
const struct i2c_device_id *dev_id)
{
struct device *dev = &client->dev;
struct ps5169_redriver *ps5169;
int ret, size = 0;
u32 device_id;
ps5169 = devm_kzalloc(dev, sizeof(*ps5169), GFP_KERNEL);
if (!ps5169)
return -ENOMEM;
ps5169->dev = dev;
ps5169->client = client;
ps5169_redriver_gpio_init(ps5169);
of_get_property(dev->of_node, "config-seq", &size);
if (!size || size % (3 * sizeof(unsigned int))) {
dev_err(dev, "no config-seq or size is wrong\n");
return -EINVAL;
}
ps5169->config_seqs = devm_kzalloc(dev, size, GFP_KERNEL);
if (!ps5169->config_seqs)
return -ENOMEM;
ps5169->config_seqs_cnt = size / (3 * sizeof(unsigned int));
of_property_read_u32_array(dev->of_node,
"config-seq",
(u32 *)ps5169->config_seqs,
ps5169->config_seqs_cnt * 3);
ps5169->regmap = devm_regmap_init_i2c(client, &ps5169_regmap);
if (IS_ERR(ps5169->regmap)) {
ret = PTR_ERR(ps5169->regmap);
dev_err(dev, "failed to allocate register map: %d\n", ret);
return ret;
}
ps5169->vcc = devm_regulator_get_optional(&client->dev, "vcc");
if (IS_ERR(ps5169->vcc) || ps5169->vcc == NULL) {
dev_err(&client->dev, "Could not get vcc power regulator\n");
} else {
ret = regulator_enable(ps5169->vcc);
if (ret)
dev_err(&client->dev, "Could not enable vcc power regulator\n");
}
ps5169->vio = devm_regulator_get_optional(&client->dev, "vio");
if (IS_ERR(ps5169->vio) || ps5169->vio == NULL) {
dev_err(&client->dev, "Could not get vio power regulator\n");
} else {
ret = regulator_enable(ps5169->vio);
if (ret)
dev_err(&client->dev, "Could not enable vio power regulator\n");
}
ps5169_redriver_enable_chip(ps5169, true);
ret = regmap_raw_read(ps5169->regmap, PS5169_CHIPID_REG, &device_id, 2);
if (ret != 0) {
dev_err(dev, "%s,device id read failed:%d\n", __func__, ret);
} else if (device_id != PS5169_CHIP_ID) {
dev_err(dev, "%s,device id unknown: 0x%x\n", __func__, device_id);
}
dev_info(dev, "%s,device_id=0x%x\n", __func__, device_id);
i2c_set_clientdata(client, ps5169);
ps5169_config_seqs_init(ps5169);
/* how about USB enumerated before this driver load */
ps5169_config_work_mode(ps5169, OP_MODE_NONE);
ps5169->op_mode = OP_MODE_NONE;
ps5169_redriver_enable_chip(ps5169, false);
ps5169_debugfs_create(ps5169);
INIT_WORK(&ps5169->pullup_work, ps5169_gadget_pullup_work);
INIT_WORK(&ps5169->host_work, ps5169_host_work);
ps5169->r.of_node = ps5169->dev->of_node;
ps5169->r.release_usb_lanes = ps5169_release_usb_lanes;
ps5169->r.notify_connect = ps5169_notify_connect;
ps5169->r.notify_disconnect = ps5169_notify_disconnect;
ps5169->r.get_orientation = ps5169_get_orientation;
ps5169->r.gadget_pullup_enter = ps5169_gadget_pullup_enter;
ps5169->r.gadget_pullup_exit = ps5169_gadget_pullup_exit;
ps5169->r.host_powercycle = ps5169_host_powercycle;
usb_add_redriver(&ps5169->r);
dev_info(dev, "%s,ps5169 probe done.\n", __func__);
return 0;
}
static int ps5169_i2c_remove(struct i2c_client *client)
{
struct ps5169_redriver *ps5169 = i2c_get_clientdata(client);
if (usb_remove_redriver(&ps5169->r))
return -EINVAL;
debugfs_remove(ps5169->debug_root);
if (!IS_ERR_OR_NULL(ps5169->vcc)) {
if (regulator_is_enabled(ps5169->vcc))
regulator_disable(ps5169->vcc);
devm_regulator_put(ps5169->vcc);
}
if (!IS_ERR_OR_NULL(ps5169->vio)) {
if (regulator_is_enabled(ps5169->vio))
regulator_disable(ps5169->vio);
devm_regulator_put(ps5169->vio);
}
if (gpio_is_valid(ps5169->enable_gpio)) {
devm_gpio_free(ps5169->dev, ps5169->enable_gpio);
ps5169->enable_gpio = -EINVAL;
}
if (gpio_is_valid(ps5169->orientation_gpio)) {
devm_gpio_free(ps5169->dev, ps5169->orientation_gpio);
ps5169->orientation_gpio = -EINVAL;
}
return 0;
}
static int __maybe_unused redriver_i2c_suspend(struct device *dev)
{
struct i2c_client *client = to_i2c_client(dev);
struct ps5169_redriver *ps5169 = i2c_get_clientdata(client);
dev_dbg(ps5169->dev, "%s: SS USB redriver suspend.\n",
__func__);
/*
* 1. when in 4 lanes display mode, it can't disable;
* 2. when in NONE mode, there is no need to re-disable;
* 3. when in DEFAULT mode, there is no adsp and can't disable;
*/
if (ps5169->op_mode == OP_MODE_DP ||
ps5169->op_mode == OP_MODE_NONE ||
ps5169->op_mode == OP_MODE_DEFAULT)
return 0;
ps5169_redriver_enable_chip(ps5169, false);
return 0;
}
static int __maybe_unused redriver_i2c_resume(struct device *dev)
{
struct i2c_client *client = to_i2c_client(dev);
struct ps5169_redriver *ps5169 = i2c_get_clientdata(client);
dev_dbg(ps5169->dev, "%s: SS USB redriver resume.\n",
__func__);
/* no suspend happen in following mode */
if (ps5169->op_mode == OP_MODE_DP ||
ps5169->op_mode == OP_MODE_NONE ||
ps5169->op_mode == OP_MODE_DEFAULT)
return 0;
ps5169_redriver_enable_chip(ps5169, true);
ps5169_config_seqs_init(ps5169);
ps5169_config_work_mode(ps5169, ps5169->op_mode);
return 0;
}
static SIMPLE_DEV_PM_OPS(redriver_i2c_pm, redriver_i2c_suspend,
redriver_i2c_resume);
static void ps5169_i2c_shutdown(struct i2c_client *client)
{
struct ps5169_redriver *ps5169 = i2c_get_clientdata(client);
int ret;
ret = ps5169_redriver_enable_chip(ps5169, false);
if (ret < 0)
dev_err(&client->dev,
"%s: fail to disable redriver.\n",
__func__);
else
dev_dbg(&client->dev,
"%s: successfully disable redriver.\n",
__func__);
}
static const struct of_device_id ps5169_match_table[] = {
{ .compatible = "parade,ps5169-redriver",},
{ },
};
static const struct i2c_device_id ps5169_i2c_id[] = {
{ "ps5169 redriver", 0 },
{ },
};
MODULE_DEVICE_TABLE(i2c, ps5169_i2c_id);
static struct i2c_driver ps5169_i2c_driver = {
.driver = {
.name = "ps5169 redriver",
.of_match_table = ps5169_match_table,
.pm = &redriver_i2c_pm,
},
.probe = ps5169_i2c_probe,
.remove = ps5169_i2c_remove,
.shutdown = ps5169_i2c_shutdown,
.id_table = ps5169_i2c_id,
};
module_i2c_driver(ps5169_i2c_driver);
MODULE_DESCRIPTION("Parade PS5169 USB3.1 Gen2 Type-C 10Gbps Linear Redriver");
MODULE_LICENSE("GPL v2");