123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038 |
- // SPDX-License-Identifier: GPL-2.0-only
- /*
- * Copyright (c) 2018-2021, The Linux Foundation. All rights reserved.
- * Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved.
- */
- #include <linux/module.h>
- #include <linux/i2c.h>
- #include <linux/slab.h>
- #include <linux/usb/ch9.h>
- #include <linux/debugfs.h>
- #include <linux/uaccess.h>
- #include <linux/regmap.h>
- #include <linux/ctype.h>
- #include <linux/pinctrl/consumer.h>
- #include <linux/of_gpio.h>
- #include <linux/regulator/consumer.h>
- #include <linux/usb/redriver.h>
- /* priority: INT_MAX >= x >= 0 */
- #define NOTIFIER_PRIORITY 1
- /* Registers Address */
- #define GEN_DEV_SET_REG 0x00
- #define CHIP_VERSION_REG 0x17
- #define REDRIVER_REG_MAX 0x1f
- #define EQ_SET_REG_BASE 0x01
- #define FLAT_GAIN_REG_BASE 0x18
- #define OUT_COMP_AND_POL_REG_BASE 0x02
- #define LOSS_MATCH_REG_BASE 0x19
- #define AUX_SWITCH_REG 0x09
- #define AUX_NORMAL_VAL 0
- #define AUX_FLIP_VAL 1
- #define AUX_DISABLE_VAL 2
- /* Default Register Value */
- #define GEN_DEV_SET_REG_DEFAULT 0xFB
- /* Register bits */
- /* General Device Settings Register Bits */
- #define CHIP_EN BIT(0)
- #define CHNA_EN BIT(4)
- #define CHNB_EN BIT(5)
- #define CHNC_EN BIT(6)
- #define CHND_EN BIT(7)
- #define CHANNEL_NUM 4
- #define OP_MODE_SHIFT 1
- #define EQ_SETTING_MASK 0x07
- #define OUTPUT_COMPRESSION_MASK 0x0b
- #define LOSS_MATCH_MASK 0x03
- #define FLAT_GAIN_MASK 0x03
- #define EQ_SETTING_SHIFT 0x01
- #define OUTPUT_COMPRESSION_SHIFT 0x01
- #define LOSS_MATCH_SHIFT 0x00
- #define FLAT_GAIN_SHIFT 0x00
- #define CHNA_INDEX 0
- #define CHNB_INDEX 1
- #define CHNC_INDEX 2
- #define CHND_INDEX 3
- enum operation_mode {
- OP_MODE_NONE, /* 4 lanes disabled */
- OP_MODE_USB, /* 2 lanes for USB and 2 lanes disabled */
- OP_MODE_DP, /* 4 lanes DP */
- OP_MODE_USB_AND_DP, /* 2 lanes for USB and 2 lanes DP */
- OP_MODE_DEFAULT, /* 4 lanes USB */
- };
- #define CHAN_MODE_USB 0
- #define CHAN_MODE_DP 1
- #define CHAN_MODE_NUM 2
- #define CHAN_MODE_DISABLE 0xff /* when disable, not configure eq, gain ... */
- #define LANES_DP 4
- #define LANES_DP_AND_USB 2
- #define PULLUP_WORKER_DELAY_US 500000
- #define CHIP_MAX_PWR_UA 260000
- #define CHIP_MIN_PWR_UV 1710000
- #define CHIP_MAX_PWR_UV 1890000
- struct nb7vpq904m_redriver {
- struct usb_redriver r;
- struct device *dev;
- struct regmap *regmap;
- struct i2c_client *client;
- struct regulator *vdd;
- int typec_orientation;
- enum operation_mode op_mode;
- u8 chan_mode[CHANNEL_NUM];
- u8 eq[CHAN_MODE_NUM][CHANNEL_NUM];
- u8 output_comp[CHAN_MODE_NUM][CHANNEL_NUM];
- u8 loss_match[CHAN_MODE_NUM][CHANNEL_NUM];
- u8 flat_gain[CHAN_MODE_NUM][CHANNEL_NUM];
- u8 gen_dev_val;
- bool lane_channel_swap;
- bool vdd_enable;
- bool is_set_aux;
- struct workqueue_struct *pullup_wq;
- struct work_struct pullup_work;
- bool work_ongoing;
- struct work_struct host_work;
- struct dentry *debug_root;
- };
- static int nb7vpq904m_channel_update(struct nb7vpq904m_redriver *redriver);
- static void nb7vpq904m_debugfs_entries(struct nb7vpq904m_redriver *redriver);
- static const char * const opmode_string[] = {
- [OP_MODE_NONE] = "NONE",
- [OP_MODE_USB] = "USB",
- [OP_MODE_DP] = "DP",
- [OP_MODE_USB_AND_DP] = "USB and DP",
- [OP_MODE_DEFAULT] = "DEFAULT",
- };
- #define OPMODESTR(x) opmode_string[x]
- static int nb7vpq904m_reg_set(struct nb7vpq904m_redriver *redriver,
- u8 reg, u8 val)
- {
- int ret;
- ret = regmap_write(redriver->regmap, (unsigned int)reg,
- (unsigned int)val);
- if (ret < 0) {
- dev_err(redriver->dev, "writing reg 0x%02x failure\n", reg);
- return ret;
- }
- dev_dbg(redriver->dev, "writing reg 0x%02x=0x%02x\n", reg, val);
- return 0;
- }
- static void nb7vpq904m_vdd_enable(struct nb7vpq904m_redriver *redriver, bool on)
- {
- int l, v, s;
- if (!redriver->vdd) {
- dev_dbg(redriver->dev, "no vdd regulator operation\n");
- return;
- }
- if (on && !redriver->vdd_enable) {
- redriver->vdd_enable = true;
- l = regulator_set_load(redriver->vdd, CHIP_MAX_PWR_UA);
- v = regulator_set_voltage(redriver->vdd, CHIP_MIN_PWR_UV, CHIP_MAX_PWR_UV);
- s = regulator_enable(redriver->vdd);
- dev_dbg(redriver->dev, "vdd regulator enable return %d-%d-%d\n", l, v, s);
- } else if (!on && redriver->vdd_enable) {
- redriver->vdd_enable = false;
- s = regulator_disable(redriver->vdd);
- v = regulator_set_voltage(redriver->vdd, 0, CHIP_MAX_PWR_UV);
- l = regulator_set_load(redriver->vdd, 0);
- dev_dbg(redriver->dev, "vdd regulator disable return %d-%d-%d\n", l, v, s);
- }
- }
- static void nb7vpq904m_dev_aux_set(struct nb7vpq904m_redriver *redriver)
- {
- u8 aux_val = AUX_DISABLE_VAL;
- if (!redriver->is_set_aux)
- return;
- switch (redriver->op_mode) {
- case OP_MODE_DP:
- case OP_MODE_USB_AND_DP:
- if (redriver->typec_orientation == ORIENTATION_CC1)
- aux_val = AUX_NORMAL_VAL;
- else
- aux_val = AUX_FLIP_VAL;
- break;
- default:
- break;
- }
- nb7vpq904m_reg_set(redriver, AUX_SWITCH_REG, aux_val);
- }
- static int nb7vpq904m_gen_dev_set(struct nb7vpq904m_redriver *redriver)
- {
- u8 val = 0;
- switch (redriver->op_mode) {
- case OP_MODE_DEFAULT:
- /* Enable channel A, B, C and D */
- val |= (CHNA_EN | CHNB_EN);
- val |= (CHNC_EN | CHND_EN);
- val |= (0x5 << OP_MODE_SHIFT);
- val |= CHIP_EN;
- break;
- case OP_MODE_USB:
- /* Use source side I/O mapping */
- if (redriver->typec_orientation
- == ORIENTATION_CC1) {
- /* Enable channel C and D */
- val &= ~(CHNA_EN | CHNB_EN);
- val |= (CHNC_EN | CHND_EN);
- } else if (redriver->typec_orientation
- == ORIENTATION_CC2) {
- /* Enable channel A and B*/
- val |= (CHNA_EN | CHNB_EN);
- val &= ~(CHNC_EN | CHND_EN);
- }
- /* Set to default USB Mode */
- val |= (0x5 << OP_MODE_SHIFT);
- val |= CHIP_EN;
- break;
- case OP_MODE_DP:
- /* Enable channel A, B, C and D */
- val |= (CHNA_EN | CHNB_EN);
- val |= (CHNC_EN | CHND_EN);
- /* Set to DP 4 Lane Mode (OP Mode 2) */
- val |= (0x2 << OP_MODE_SHIFT);
- val |= CHIP_EN;
- break;
- case OP_MODE_USB_AND_DP:
- /* Enable channel A, B, C and D */
- val |= (CHNA_EN | CHNB_EN);
- val |= (CHNC_EN | CHND_EN);
- val |= CHIP_EN;
- if (redriver->typec_orientation
- == ORIENTATION_CC1)
- val |= (0x1 << OP_MODE_SHIFT);
- else if (redriver->typec_orientation
- == ORIENTATION_CC2)
- val |= (0x0 << OP_MODE_SHIFT);
- break;
- default:
- val &= ~CHIP_EN;
- break;
- }
- redriver->gen_dev_val = val;
- return nb7vpq904m_reg_set(redriver, GEN_DEV_SET_REG, val);
- }
- static int nb7vpq904m_param_config(struct nb7vpq904m_redriver *redriver,
- u8 reg_base, u8 channel, u8 chan_mode, u8 mask, u8 shift,
- u8 val, u8 (*stored_val)[CHANNEL_NUM])
- {
- int i, j, ret = -EINVAL;
- u8 reg_addr, reg_val;
- if (channel == CHANNEL_NUM) {
- for (i = 0; i < CHAN_MODE_NUM; i++)
- for (j = 0; j < CHANNEL_NUM; j++) {
- if (redriver->chan_mode[j] == i) {
- reg_addr = reg_base + (j << 1);
- reg_val = (val << shift);
- reg_val &= (mask << shift);
- ret = nb7vpq904m_reg_set(redriver,
- reg_addr, reg_val);
- if (ret < 0)
- return ret;
- }
- stored_val[i][j] = val;
- }
- } else {
- if (redriver->chan_mode[channel] == chan_mode) {
- reg_addr = reg_base + (channel << 1);
- reg_val = (val << shift);
- reg_val &= (mask << shift);
- ret = nb7vpq904m_reg_set(redriver,
- reg_addr, reg_val);
- if (ret < 0)
- return ret;
- }
- stored_val[chan_mode][channel] = val;
- }
- return 0;
- }
- static int nb7vpq904m_eq_config(
- struct nb7vpq904m_redriver *redriver, u8 channel, u8 chan_mode, u8 val)
- {
- return nb7vpq904m_param_config(redriver,
- EQ_SET_REG_BASE, channel, chan_mode,
- EQ_SETTING_MASK, EQ_SETTING_SHIFT,
- val, redriver->eq);
- }
- static int nb7vpq904m_flat_gain_config(
- struct nb7vpq904m_redriver *redriver, u8 channel, u8 chan_mode, u8 val)
- {
- return nb7vpq904m_param_config(redriver,
- FLAT_GAIN_REG_BASE, channel, chan_mode,
- FLAT_GAIN_MASK, FLAT_GAIN_SHIFT,
- val, redriver->flat_gain);
- }
- static int nb7vpq904m_output_comp_config(
- struct nb7vpq904m_redriver *redriver, u8 channel, u8 chan_mode, u8 val)
- {
- return nb7vpq904m_param_config(redriver,
- OUT_COMP_AND_POL_REG_BASE, channel, chan_mode,
- OUTPUT_COMPRESSION_MASK, OUTPUT_COMPRESSION_SHIFT,
- val, redriver->output_comp);
- }
- static int nb7vpq904m_loss_match_config(
- struct nb7vpq904m_redriver *redriver, u8 channel, u8 chan_mode, u8 val)
- {
- return nb7vpq904m_param_config(redriver,
- LOSS_MATCH_REG_BASE, channel, chan_mode,
- LOSS_MATCH_MASK, LOSS_MATCH_SHIFT, val,
- redriver->loss_match);
- }
- static int nb7vpq904m_channel_update(struct nb7vpq904m_redriver *redriver)
- {
- int ret;
- u8 i, chan_mode;
- switch (redriver->op_mode) {
- case OP_MODE_DEFAULT:
- redriver->chan_mode[CHNA_INDEX] = CHAN_MODE_USB;
- redriver->chan_mode[CHNB_INDEX] = CHAN_MODE_USB;
- redriver->chan_mode[CHNC_INDEX] = CHAN_MODE_USB;
- redriver->chan_mode[CHND_INDEX] = CHAN_MODE_USB;
- break;
- case OP_MODE_USB:
- if (redriver->typec_orientation == ORIENTATION_CC1) {
- redriver->chan_mode[CHNA_INDEX] = CHAN_MODE_DISABLE;
- redriver->chan_mode[CHNB_INDEX] = CHAN_MODE_DISABLE;
- redriver->chan_mode[CHNC_INDEX] = CHAN_MODE_USB;
- redriver->chan_mode[CHND_INDEX] = CHAN_MODE_USB;
- } else {
- redriver->chan_mode[CHNA_INDEX] = CHAN_MODE_USB;
- redriver->chan_mode[CHNB_INDEX] = CHAN_MODE_USB;
- redriver->chan_mode[CHNC_INDEX] = CHAN_MODE_DISABLE;
- redriver->chan_mode[CHND_INDEX] = CHAN_MODE_DISABLE;
- }
- break;
- case OP_MODE_USB_AND_DP:
- if (redriver->typec_orientation == ORIENTATION_CC1) {
- redriver->chan_mode[CHNA_INDEX] = CHAN_MODE_DP;
- redriver->chan_mode[CHNB_INDEX] = CHAN_MODE_DP;
- redriver->chan_mode[CHNC_INDEX] = CHAN_MODE_USB;
- redriver->chan_mode[CHND_INDEX] = CHAN_MODE_USB;
- } else {
- redriver->chan_mode[CHNA_INDEX] = CHAN_MODE_USB;
- redriver->chan_mode[CHNB_INDEX] = CHAN_MODE_USB;
- redriver->chan_mode[CHNC_INDEX] = CHAN_MODE_DP;
- redriver->chan_mode[CHND_INDEX] = CHAN_MODE_DP;
- }
- break;
- case OP_MODE_DP:
- redriver->chan_mode[CHNA_INDEX] = CHAN_MODE_DP;
- redriver->chan_mode[CHNB_INDEX] = CHAN_MODE_DP;
- redriver->chan_mode[CHNC_INDEX] = CHAN_MODE_DP;
- redriver->chan_mode[CHND_INDEX] = CHAN_MODE_DP;
- break;
- default:
- return 0;
- }
- for (i = 0; i < CHANNEL_NUM; i++) {
- if (redriver->chan_mode[i] == CHAN_MODE_DISABLE)
- continue;
- chan_mode = redriver->chan_mode[i];
- ret = nb7vpq904m_eq_config(redriver, i, chan_mode,
- redriver->eq[chan_mode][i]);
- if (ret)
- goto err;
- ret = nb7vpq904m_flat_gain_config(redriver, i, chan_mode,
- redriver->flat_gain[chan_mode][i]);
- if (ret)
- goto err;
- ret = nb7vpq904m_output_comp_config(redriver, i, chan_mode,
- redriver->output_comp[chan_mode][i]);
- if (ret)
- goto err;
- ret = nb7vpq904m_loss_match_config(redriver, i, chan_mode,
- redriver->loss_match[chan_mode][i]);
- if (ret)
- goto err;
- }
- return 0;
- err:
- dev_err(redriver->dev, "channel parameters update failure(%d).\n", ret);
- return ret;
- }
- static int nb7vpq904m_read_configuration(struct nb7vpq904m_redriver *redriver)
- {
- struct device_node *node = redriver->dev->of_node;
- int ret = 0;
- if (of_find_property(node, "eq", NULL)) {
- ret = of_property_read_u8_array(node, "eq",
- redriver->eq[0], sizeof(redriver->eq));
- if (ret)
- goto err;
- }
- if (of_find_property(node, "flat-gain", NULL)) {
- ret = of_property_read_u8_array(node,
- "flat-gain", redriver->flat_gain[0],
- sizeof(redriver->flat_gain));
- if (ret)
- goto err;
- }
- if (of_find_property(node, "output-comp", NULL)) {
- ret = of_property_read_u8_array(node,
- "output-comp", redriver->output_comp[0],
- sizeof(redriver->output_comp));
- if (ret)
- goto err;
- }
- if (of_find_property(node, "loss-match", NULL)) {
- ret = of_property_read_u8_array(node,
- "loss-match", redriver->loss_match[0],
- sizeof(redriver->loss_match));
- if (ret)
- goto err;
- }
- redriver->is_set_aux = of_property_read_bool(node, "set-aux");
- return 0;
- err:
- dev_err(redriver->dev,
- "%s: error read parameters.\n", __func__);
- return ret;
- }
- static inline void orientation_set(struct nb7vpq904m_redriver *redriver, int ort)
- {
- redriver->typec_orientation = ort;
- if (redriver->lane_channel_swap) {
- if (redriver->typec_orientation == ORIENTATION_CC1)
- redriver->typec_orientation = ORIENTATION_CC2;
- else
- redriver->typec_orientation = ORIENTATION_CC1;
- }
- }
- static int nb7vpq904m_notify_connect(struct usb_redriver *r, int ort)
- {
- struct nb7vpq904m_redriver *redriver =
- container_of(r, struct nb7vpq904m_redriver, r);
- dev_dbg(redriver->dev, "%s: mode %s, orientation %s, %d\n", __func__,
- OPMODESTR(redriver->op_mode),
- ort == ORIENTATION_CC1 ? "CC1" : "CC2",
- redriver->lane_channel_swap);
- nb7vpq904m_vdd_enable(redriver, true);
- if (redriver->op_mode == OP_MODE_NONE)
- redriver->op_mode = OP_MODE_USB;
- orientation_set(redriver, ort);
- nb7vpq904m_gen_dev_set(redriver);
- nb7vpq904m_channel_update(redriver);
- return 0;
- }
- static int nb7vpq904m_notify_disconnect(struct usb_redriver *r)
- {
- int ret = 0;
- struct nb7vpq904m_redriver *redriver =
- container_of(r, struct nb7vpq904m_redriver, r);
- dev_dbg(redriver->dev, "%s: mode %s\n", __func__,
- OPMODESTR(redriver->op_mode));
- if (redriver->op_mode == OP_MODE_NONE)
- return 0;
- redriver->op_mode = OP_MODE_NONE;
- ret = nb7vpq904m_reg_set(redriver, GEN_DEV_SET_REG, 0);
- if (!ret)
- nb7vpq904m_vdd_enable(redriver, false);
- return 0;
- }
- static int nb7vpq904m_release_usb_lanes(struct usb_redriver *r, int ort, int num)
- {
- struct nb7vpq904m_redriver *redriver =
- container_of(r, struct nb7vpq904m_redriver, r);
- dev_dbg(redriver->dev, "%s: mode %s, orientation %s-%d, lanes %d\n", __func__,
- OPMODESTR(redriver->op_mode), ort == ORIENTATION_CC1 ? "CC1" : "CC2",
- redriver->lane_channel_swap, num);
- if (num == LANES_DP)
- redriver->op_mode = OP_MODE_DP;
- else if (num == LANES_DP_AND_USB)
- redriver->op_mode = OP_MODE_USB_AND_DP;
- nb7vpq904m_vdd_enable(redriver, true);
- /* in case it need aux function from redriver and the first call is release lane */
- orientation_set(redriver, ort);
- nb7vpq904m_gen_dev_set(redriver);
- nb7vpq904m_dev_aux_set(redriver);
- nb7vpq904m_channel_update(redriver);
- return 0;
- }
- static void nb7vpq904m_gadget_pullup_work(struct work_struct *w)
- {
- struct nb7vpq904m_redriver *redriver =
- container_of(w, struct nb7vpq904m_redriver, pullup_work);
- u8 val = redriver->gen_dev_val;
- nb7vpq904m_reg_set(redriver, GEN_DEV_SET_REG, val & ~CHIP_EN);
- usleep_range(1000, 1500);
- nb7vpq904m_reg_set(redriver, GEN_DEV_SET_REG, val);
- redriver->work_ongoing = false;
- }
- static int nb7vpq904m_gadget_pullup_enter(struct usb_redriver *r, int is_on)
- {
- struct nb7vpq904m_redriver *redriver =
- container_of(r, struct nb7vpq904m_redriver, r);
- u64 time = 0;
- dev_dbg(redriver->dev, "%s: mode %s, %d, %d\n", __func__,
- OPMODESTR(redriver->op_mode), is_on, redriver->work_ongoing);
- if (redriver->op_mode != OP_MODE_USB)
- return -EINVAL;
- if (!is_on)
- return 0;
- while (redriver->work_ongoing) {
- /*
- * this function can work in atomic context, no sleep function here,
- * it need wait pull down complete before pull up again.
- */
- udelay(1);
- if (time++ > PULLUP_WORKER_DELAY_US) {
- dev_warn(redriver->dev, "pullup timeout\n");
- break;
- }
- }
- dev_dbg(redriver->dev, "pull-up disable work took %llu us\n", time);
- return 0;
- }
- static int nb7vpq904m_gadget_pullup_exit(struct usb_redriver *r, int is_on)
- {
- struct nb7vpq904m_redriver *redriver =
- container_of(r, struct nb7vpq904m_redriver, r);
- dev_dbg(redriver->dev, "%s: mode %s, %d, %d\n", __func__,
- OPMODESTR(redriver->op_mode), is_on, redriver->work_ongoing);
- if (redriver->op_mode != OP_MODE_USB)
- return -EINVAL;
- if (is_on)
- return 0;
- redriver->work_ongoing = true;
- queue_work(redriver->pullup_wq, &redriver->pullup_work);
- return 0;
- }
- static void nb7vpq904m_host_work(struct work_struct *w)
- {
- struct nb7vpq904m_redriver *redriver =
- container_of(w, struct nb7vpq904m_redriver, host_work);
- u8 val = redriver->gen_dev_val;
- nb7vpq904m_reg_set(redriver, GEN_DEV_SET_REG, val & ~CHIP_EN);
- /* sleep for a while to make sure xhci host detect device disconnect */
- usleep_range(2000, 2500);
- nb7vpq904m_reg_set(redriver, GEN_DEV_SET_REG, val);
- }
- static int nb7vpq904m_host_powercycle(struct usb_redriver *r)
- {
- struct nb7vpq904m_redriver *redriver =
- container_of(r, struct nb7vpq904m_redriver, r);
- if (redriver->op_mode != OP_MODE_USB)
- return -EINVAL;
- schedule_work(&redriver->host_work);
- return 0;
- }
- static const struct regmap_config redriver_regmap = {
- .name = "nb7vpq904m",
- .max_register = REDRIVER_REG_MAX,
- .reg_bits = 8,
- .val_bits = 8,
- };
- static int nb7vpq904m_probe(struct i2c_client *client,
- const struct i2c_device_id *dev_id)
- {
- struct nb7vpq904m_redriver *redriver;
- int ret;
- redriver = devm_kzalloc(&client->dev, sizeof(struct nb7vpq904m_redriver),
- GFP_KERNEL);
- if (!redriver)
- return -ENOMEM;
- redriver->pullup_wq = alloc_workqueue("%s:pullup",
- WQ_UNBOUND | WQ_HIGHPRI, 0,
- dev_name(&client->dev));
- if (!redriver->pullup_wq) {
- dev_err(&client->dev, "Failed to create pullup workqueue\n");
- return -ENOMEM;
- }
- redriver->regmap = devm_regmap_init_i2c(client, &redriver_regmap);
- if (IS_ERR(redriver->regmap)) {
- ret = PTR_ERR(redriver->regmap);
- dev_err(&client->dev,
- "Failed to allocate register map: %d\n", ret);
- return ret;
- }
- redriver->dev = &client->dev;
- i2c_set_clientdata(client, redriver);
- ret = nb7vpq904m_read_configuration(redriver);
- if (ret < 0) {
- dev_err(&client->dev,
- "Failed to read default configuration: %d\n", ret);
- return ret;
- }
- redriver->vdd = devm_regulator_get_optional(&client->dev, "vdd");
- if (IS_ERR(redriver->vdd)) {
- ret = PTR_ERR(redriver->vdd);
- redriver->vdd = NULL;
- if (ret != -ENODEV)
- dev_err(&client->dev, "Failed to get vdd regulator %d\n", ret);
- }
- INIT_WORK(&redriver->pullup_work, nb7vpq904m_gadget_pullup_work);
- INIT_WORK(&redriver->host_work, nb7vpq904m_host_work);
- redriver->lane_channel_swap =
- of_property_read_bool(redriver->dev->of_node, "lane-channel-swap");
- /* disable it at start, one i2c register write time is acceptable */
- redriver->op_mode = OP_MODE_NONE;
- nb7vpq904m_vdd_enable(redriver, true);
- nb7vpq904m_gen_dev_set(redriver);
- /* when private vdd present and change to none mode, it can simply disable vdd regulator,
- * but to keep things simple and avoid if/else operation, keep one same rule as,
- * allow original register write operation then control vdd regulator.
- * also it will keep consistent behavior if it still need vdd control when multiple
- * clients share the same vdd regulator.
- */
- nb7vpq904m_vdd_enable(redriver, false);
- nb7vpq904m_debugfs_entries(redriver);
- redriver->r.of_node = redriver->dev->of_node;
- redriver->r.release_usb_lanes = nb7vpq904m_release_usb_lanes;
- redriver->r.notify_connect = nb7vpq904m_notify_connect;
- redriver->r.notify_disconnect = nb7vpq904m_notify_disconnect;
- redriver->r.gadget_pullup_enter = nb7vpq904m_gadget_pullup_enter;
- redriver->r.gadget_pullup_exit = nb7vpq904m_gadget_pullup_exit;
- redriver->r.host_powercycle = nb7vpq904m_host_powercycle;
- usb_add_redriver(&redriver->r);
- return 0;
- }
- static void nb7vpq904m_remove(struct i2c_client *client)
- {
- struct nb7vpq904m_redriver *redriver = i2c_get_clientdata(client);
- if (usb_remove_redriver(&redriver->r))
- return;
- debugfs_remove_recursive(redriver->debug_root);
- redriver->work_ongoing = false;
- destroy_workqueue(redriver->pullup_wq);
- if (redriver->vdd)
- regulator_disable(redriver->vdd);
- }
- static ssize_t channel_config_write(struct file *file,
- const char __user *ubuf, size_t count, loff_t *ppos,
- int (*config_func)(struct nb7vpq904m_redriver *redriver,
- u8 channel, u8 chan_mode, u8 val))
- {
- struct seq_file *s = file->private_data;
- struct nb7vpq904m_redriver *redriver = s->private;
- char buf[40];
- char *token_chan, *token_val, *this_buf;
- u8 channel, chan_mode;
- int ret = 0;
- memset(buf, 0, sizeof(buf));
- this_buf = buf;
- if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count)))
- return -EFAULT;
- if (isdigit(buf[0])) {
- ret = config_func(redriver, CHANNEL_NUM, -1, buf[0] - '0');
- if (ret < 0)
- goto err;
- } else if (isalpha(buf[0])) {
- while ((token_chan = strsep(&this_buf, " ")) != NULL) {
- switch (*token_chan) {
- case 'A':
- case 'B':
- case 'C':
- case 'D':
- channel = *token_chan - 'A';
- chan_mode = CHAN_MODE_USB;
- token_val = strsep(&this_buf, " ");
- if (!isdigit(*token_val))
- goto err;
- break;
- case 'a':
- case 'b':
- case 'c':
- case 'd':
- channel = *token_chan - 'a';
- chan_mode = CHAN_MODE_DP;
- token_val = strsep(&this_buf, " ");
- if (!isdigit(*token_val))
- goto err;
- break;
- default:
- goto err;
- }
- ret = config_func(redriver, channel, chan_mode,
- *token_val - '0');
- if (ret < 0)
- goto err;
- }
- } else
- goto err;
- return count;
- err:
- pr_err("Used to config redriver A/B/C/D channels' parameters\n"
- "A/B/C/D represent for re-driver parameters for USB\n"
- "a/b/c/d represent for re-driver parameters for DP\n"
- "1. Set all channels to same value(both USB and DP)\n"
- "echo n > [eq|output_comp|flat_gain|loss_match]\n"
- "- eq: Equalization, range 0-7\n"
- "- output_comp: Output Compression, range 0-3\n"
- "- loss_match: LOSS Profile Matching, range 0-3\n"
- "- flat_gain: Flat Gain, range 0-3\n"
- "Example: Set all channels to same EQ value\n"
- "echo 1 > eq\n"
- "2. Set two channels to different values leave others unchanged\n"
- "echo [A|B|C|D] n [A|B|C|D] n > [eq|output_comp|flat_gain|loss_match]\n"
- "Example2: USB mode: set channel B flat gain to 2, set channel C flat gain to 3\n"
- "echo B 2 C 3 > flat_gain\n"
- "Example3: DP mode: set channel A equalization to 6, set channel B equalization to 4\n"
- "echo a 6 b 4 > eq\n");
- return -EFAULT;
- }
- static int eq_status(struct seq_file *s, void *p)
- {
- struct nb7vpq904m_redriver *redriver = s->private;
- seq_puts(s, "\t\t\t A(USB)\t B(USB)\t C(USB)\t D(USB)\t"
- "A(DP)\t B(DP)\t C(DP)\t D(DP)\n");
- seq_printf(s, "Equalization:\t\t %d\t %d\t %d\t %d\t"
- "%d\t %d\t %d\t %d\n",
- redriver->eq[CHAN_MODE_USB][CHNA_INDEX],
- redriver->eq[CHAN_MODE_USB][CHNB_INDEX],
- redriver->eq[CHAN_MODE_USB][CHNC_INDEX],
- redriver->eq[CHAN_MODE_USB][CHND_INDEX],
- redriver->eq[CHAN_MODE_DP][CHNA_INDEX],
- redriver->eq[CHAN_MODE_DP][CHNB_INDEX],
- redriver->eq[CHAN_MODE_DP][CHNC_INDEX],
- redriver->eq[CHAN_MODE_DP][CHND_INDEX]);
- return 0;
- }
- static int eq_status_open(struct inode *inode,
- struct file *file)
- {
- return single_open(file, eq_status, inode->i_private);
- }
- static ssize_t eq_write(struct file *file,
- const char __user *ubuf, size_t count, loff_t *ppos)
- {
- return channel_config_write(file, ubuf, count, ppos,
- nb7vpq904m_eq_config);
- }
- static const struct file_operations eq_ops = {
- .open = eq_status_open,
- .read = seq_read,
- .write = eq_write,
- };
- static int flat_gain_status(struct seq_file *s, void *p)
- {
- struct nb7vpq904m_redriver *redriver = s->private;
- seq_puts(s, "\t\t\t A(USB)\t B(USB)\t C(USB)\t D(USB)\t"
- "A(DP)\t B(DP)\t C(DP)\t D(DP)\n");
- seq_printf(s, "TX/RX Flat Gain:\t %d\t %d\t %d\t %d\t"
- "%d\t %d\t %d\t %d\n",
- redriver->flat_gain[CHAN_MODE_USB][CHNA_INDEX],
- redriver->flat_gain[CHAN_MODE_USB][CHNB_INDEX],
- redriver->flat_gain[CHAN_MODE_USB][CHNC_INDEX],
- redriver->flat_gain[CHAN_MODE_USB][CHND_INDEX],
- redriver->flat_gain[CHAN_MODE_DP][CHNA_INDEX],
- redriver->flat_gain[CHAN_MODE_DP][CHNB_INDEX],
- redriver->flat_gain[CHAN_MODE_DP][CHNC_INDEX],
- redriver->flat_gain[CHAN_MODE_DP][CHND_INDEX]);
- return 0;
- }
- static int flat_gain_status_open(struct inode *inode,
- struct file *file)
- {
- return single_open(file, flat_gain_status, inode->i_private);
- }
- static ssize_t flat_gain_write(struct file *file,
- const char __user *ubuf, size_t count, loff_t *ppos)
- {
- return channel_config_write(file, ubuf, count, ppos,
- nb7vpq904m_flat_gain_config);
- }
- static const struct file_operations flat_gain_ops = {
- .open = flat_gain_status_open,
- .read = seq_read,
- .write = flat_gain_write,
- };
- static int output_comp_status(struct seq_file *s, void *p)
- {
- struct nb7vpq904m_redriver *redriver = s->private;
- seq_puts(s, "\t\t\t A(USB)\t B(USB)\t C(USB)\t D(USB)\t"
- "A(DP)\t B(DP)\t C(DP)\t D(DP)\n");
- seq_printf(s, "Output Compression:\t %d\t %d\t %d\t %d\t"
- "%d\t %d\t %d\t %d\n",
- redriver->output_comp[CHAN_MODE_USB][CHNA_INDEX],
- redriver->output_comp[CHAN_MODE_USB][CHNB_INDEX],
- redriver->output_comp[CHAN_MODE_USB][CHNC_INDEX],
- redriver->output_comp[CHAN_MODE_USB][CHND_INDEX],
- redriver->output_comp[CHAN_MODE_DP][CHNA_INDEX],
- redriver->output_comp[CHAN_MODE_DP][CHNB_INDEX],
- redriver->output_comp[CHAN_MODE_DP][CHNC_INDEX],
- redriver->output_comp[CHAN_MODE_DP][CHND_INDEX]);
- return 0;
- }
- static int output_comp_status_open(struct inode *inode,
- struct file *file)
- {
- return single_open(file, output_comp_status, inode->i_private);
- }
- static ssize_t output_comp_write(struct file *file,
- const char __user *ubuf, size_t count, loff_t *ppos)
- {
- return channel_config_write(file, ubuf, count, ppos,
- nb7vpq904m_output_comp_config);
- }
- static const struct file_operations output_comp_ops = {
- .open = output_comp_status_open,
- .read = seq_read,
- .write = output_comp_write,
- };
- static int loss_match_status(struct seq_file *s, void *p)
- {
- struct nb7vpq904m_redriver *redriver = s->private;
- seq_puts(s, "\t\t\t A(USB)\t B(USB)\t C(USB)\t D(USB)\t"
- "A(DP)\t B(DP)\t C(DP)\t D(DP)\n");
- seq_printf(s, "Loss Profile Match:\t %d\t %d\t %d\t %d\t"
- "%d\t %d\t %d\t %d\n",
- redriver->loss_match[CHAN_MODE_USB][CHNA_INDEX],
- redriver->loss_match[CHAN_MODE_USB][CHNB_INDEX],
- redriver->loss_match[CHAN_MODE_USB][CHNC_INDEX],
- redriver->loss_match[CHAN_MODE_USB][CHND_INDEX],
- redriver->loss_match[CHAN_MODE_DP][CHNA_INDEX],
- redriver->loss_match[CHAN_MODE_DP][CHNB_INDEX],
- redriver->loss_match[CHAN_MODE_DP][CHNC_INDEX],
- redriver->loss_match[CHAN_MODE_DP][CHND_INDEX]);
- return 0;
- }
- static int loss_match_status_open(struct inode *inode,
- struct file *file)
- {
- return single_open(file, loss_match_status, inode->i_private);
- }
- static ssize_t loss_match_write(struct file *file,
- const char __user *ubuf, size_t count, loff_t *ppos)
- {
- return channel_config_write(file, ubuf, count, ppos,
- nb7vpq904m_loss_match_config);
- }
- static const struct file_operations loss_match_ops = {
- .open = loss_match_status_open,
- .read = seq_read,
- .write = loss_match_write,
- };
- static void nb7vpq904m_debugfs_entries(
- struct nb7vpq904m_redriver *redriver)
- {
- redriver->debug_root = debugfs_create_dir("nb7vpq904m_redriver", NULL);
- if (!redriver->debug_root) {
- dev_warn(redriver->dev, "Couldn't create debug dir\n");
- return;
- }
- debugfs_create_file("eq", 0600,
- redriver->debug_root, redriver, &eq_ops);
- debugfs_create_file("flat_gain", 0600,
- redriver->debug_root, redriver, &flat_gain_ops);
- debugfs_create_file("output_comp", 0600,
- redriver->debug_root, redriver, &output_comp_ops);
- debugfs_create_file("loss_match", 0600,
- redriver->debug_root, redriver, &loss_match_ops);
- debugfs_create_bool("lane-channel-swap", 0644,
- redriver->debug_root, &redriver->lane_channel_swap);
- }
- static void nb7vpq904m_shutdown(struct i2c_client *client)
- {
- struct nb7vpq904m_redriver *redriver = i2c_get_clientdata(client);
- int ret;
- /* Set back to USB mode with four channel enabled */
- ret = nb7vpq904m_reg_set(redriver, GEN_DEV_SET_REG,
- GEN_DEV_SET_REG_DEFAULT);
- if (ret < 0)
- dev_err(&client->dev,
- "%s: fail to set USB mode with 4 channel enabled.\n",
- __func__);
- else
- dev_dbg(&client->dev,
- "%s: successfully set back to USB mode.\n",
- __func__);
- }
- static const struct of_device_id nb7vpq904m_match_table[] = {
- { .compatible = "onnn,redriver" },
- { }
- };
- static struct i2c_driver nb7vpq904m_driver = {
- .driver = {
- .name = "ssusb-redriver",
- .of_match_table = nb7vpq904m_match_table,
- },
- .probe = nb7vpq904m_probe,
- .remove = nb7vpq904m_remove,
- .shutdown = nb7vpq904m_shutdown,
- };
- module_i2c_driver(nb7vpq904m_driver);
- MODULE_DESCRIPTION("USB Super Speed Linear Re-Driver");
- MODULE_LICENSE("GPL");
|