Message ID | 20230904051253.23208-1-stanley_chang@realtek.com (mailing list archive) |
---|---|
State | Accepted |
Commit | 8a590d7371f02ba37d073bed9f988f529f95a03c |
Headers | show |
Series | [v3,1/2] extcon: add Realtek DHC RTD SoC Type-C driver | expand |
Hi Chanwoo, Can you help review this patch? Thanks, Stanley > -----Original Message----- > From: Stanley Chang <stanley_chang@realtek.com> > Sent: Monday, September 4, 2023 1:13 PM > To: MyungJoo Ham <myungjoo.ham@samsung.com> > Cc: Stanley Chang[昌育德] <stanley_chang@realtek.com>; Greg > Kroah-Hartman <gregkh@linuxfoundation.org>; Rob Herring > <robh+dt@kernel.org>; Krzysztof Kozlowski > <krzysztof.kozlowski+dt@linaro.org>; Conor Dooley <conor+dt@kernel.org>; > Chanwoo Choi <cw00.choi@samsung.com>; linux-usb@vger.kernel.org; > devicetree@vger.kernel.org; linux-kernel@vger.kernel.org > Subject: [PATCH v3 1/2] extcon: add Realtek DHC RTD SoC Type-C driver > > This patch adds the extcon driver for Realtek DHC (digital home center) > RTD SoCs type-c module. This can be used to detect whether the port is > configured as a downstream or upstream facing port. And notify the status > of extcon to listeners. > > Signed-off-by: Stanley Chang <stanley_chang@realtek.com> > --- > v2 to v3 change: > removed the error check for debugfs > v1 to v2 change: > 1. added "depends on TYPEC" and "select USB_COMMON" in Kconfig > 2. revised the code about gpio. > 3. revised the definitions to keep the left-aligned of value > 4. add the comment for delay or sleep > 5. changed some functions to connector_attached/connector_detached > 6. removed to check 'CONFIG_TYPEC' definition with ifdef > --- > drivers/extcon/Kconfig | 11 + > drivers/extcon/Makefile | 1 + > drivers/extcon/extcon-rtk-type-c.c | 1792 ++++++++++++++++++++++++++++ > 3 files changed, 1804 insertions(+) > create mode 100644 drivers/extcon/extcon-rtk-type-c.c > > diff --git a/drivers/extcon/Kconfig b/drivers/extcon/Kconfig > index 0ef1971d22bb..0f4c061e7321 100644 > --- a/drivers/extcon/Kconfig > +++ b/drivers/extcon/Kconfig > @@ -190,4 +190,15 @@ config EXTCON_USBC_TUSB320 > Say Y here to enable support for USB Type C cable detection extcon > support using a TUSB320. > > +config EXTCON_RTK_TYPE_C > + tristate "Realtek RTD SoC extcon Type-C Driver" > + depends on ARCH_REALTEK || COMPILE_TEST > + depends on TYPEC > + select USB_COMMON > + help > + Say Y here to enable extcon support for USB Type C cable detection > + when using the Realtek RTD SoC USB Type-C port. > + The DHC (Digital Home Hub) RTD series SoC contains a type c module. > + This driver will detect the status of the type-c port. > + > endif > diff --git a/drivers/extcon/Makefile b/drivers/extcon/Makefile > index 1b390d934ca9..f779adb5e4c7 100644 > --- a/drivers/extcon/Makefile > +++ b/drivers/extcon/Makefile > @@ -25,3 +25,4 @@ obj-$(CONFIG_EXTCON_SM5502) += > extcon-sm5502.o > obj-$(CONFIG_EXTCON_USB_GPIO) += extcon-usb-gpio.o > obj-$(CONFIG_EXTCON_USBC_CROS_EC) += extcon-usbc-cros-ec.o > obj-$(CONFIG_EXTCON_USBC_TUSB320) += extcon-usbc-tusb320.o > +obj-$(CONFIG_EXTCON_RTK_TYPE_C) += extcon-rtk-type-c.o > diff --git a/drivers/extcon/extcon-rtk-type-c.c > b/drivers/extcon/extcon-rtk-type-c.c > new file mode 100644 > index 000000000000..00465cfba23e > --- /dev/null > +++ b/drivers/extcon/extcon-rtk-type-c.c > @@ -0,0 +1,1792 @@ > +// SPDX-License-Identifier: GPL-2.0 > +/* > + * * extcon-rtk-type-c.c - Realtek Extcon Type C driver > + * > + * Copyright (C) 2023 Realtek Semiconductor Corporation > + * > + */ > + > +#include <linux/module.h> > +#include <linux/kernel.h> > +#include <linux/slab.h> > +#include <linux/platform_device.h> > +#include <linux/of.h> > +#include <linux/of_address.h> > +#include <linux/of_irq.h> > +#include <linux/of_gpio.h> > +#include <linux/io.h> > +#include <linux/interrupt.h> > +#include <linux/syscalls.h> > +#include <linux/suspend.h> > +#include <linux/debugfs.h> > +#include <linux/extcon.h> > +#include <linux/extcon-provider.h> > +#include <linux/sys_soc.h> > +#include <linux/nvmem-consumer.h> > +#include <linux/gpio/consumer.h> > +#include <linux/usb/otg.h> > +#include <linux/usb/typec.h> > + > +struct cc_param { > + u32 rp_4p7k_code; > + u32 rp_36k_code; > + u32 rp_12k_code; > + u32 rd_code; > + u32 ra_code; > + u32 vref_2p6v; > + u32 vref_1p23v; > + u32 vref_0p8v; > + u32 vref_0p66v; > + u32 vref_0p4v; > + u32 vref_0p2v; > + u32 vref_1_1p6v; > + u32 vref_0_1p6v; > +}; > + > +struct type_c_cfg { > + int parameter_ver; /* Parameter version */ > + int cc_dfp_mode; > + struct cc_param cc1_param; > + struct cc_param cc2_param; > + > + u32 debounce_val; > + bool use_defalut_parameter; > +}; > + > +struct type_c_data { > + void __iomem *reg_base; > + struct device *dev; > + struct extcon_dev *edev; > + > + u32 irq; > + > + /* rd control GPIO only for rtd1295 */ > + struct gpio_desc *rd_ctrl_gpio_desc; > + > + /* Parameters */ > + struct type_c_cfg *type_c_cfg; > + u32 dfp_mode_rp_en; > + u32 ufp_mode_rd_en; > + u32 cc1_code; > + u32 cc2_code; > + u32 cc1_vref; > + u32 cc2_vref; > + u32 debounce; /* 1b,1us 7f,4.7us */ > + > + /* type_c state */ > + int connect_change; > +#define CONNECT_CHANGE 1 > +#define CONNECT_NO_CHANGE 0 > + int cc_mode; /* cc is host or device */ > +#define IN_HOST_MODE 0x10 > +#define IN_DEVICE_MODE 0x20 > + int is_attach; > +#define IN_ATTACH 1 > +#define TO_ATTACH 1 > +#define IN_DETACH 0 > +#define TO_DETACH 0 > + int at_cc1; > +#define AT_CC1 1 > +#define AT_CC2 0 > + > + u32 int_status; > + u32 cc_status; > + /* protect the data member */ > + spinlock_t lock; > + struct delayed_work delayed_work; > + > + bool rd_en_at_first; > + > + struct dentry *debug_dir; > + > + struct typec_port *port; > +}; > + > +/* Type C register offset */ > +#define USB_TYPEC_CTRL_CC1_0 0x0 > +#define USB_TYPEC_CTRL_CC1_1 0x4 > +#define USB_TYPEC_CTRL_CC2_0 0x8 > +#define USB_TYPEC_CTRL_CC2_1 0xC > +#define USB_TYPEC_STS 0x10 > +#define USB_TYPEC_CTRL 0x14 > +#define USB_DBUS_PWR_CTRL 0x18 > + > +#define ENABLE_CC1 0x1 > +#define ENABLE_CC2 0x2 > +#define DISABLE_CC 0x0 > + > +/* Bit mapping USB_TYPEC_CTRL_CC1_0 and USB_TYPEC_CTRL_CC2_0 */ > +#define PLR_EN BIT(29) > +#define CC_SWITCH_MASK (BIT(29) | BIT(28) | BIT(27)) > +#define CC_CODE_MASK (0xfffff << 7) > +#define rp4pk_code(val) ((0x1f & (val)) << 22) > +#define code_rp4pk(val) (((val) >> 22) & 0x1f) > +#define rp36k_code(val) ((0x1f & (val)) << 17) > +#define code_rp36k(val) (((val) >> 17) & 0x1f) > +#define rp12k_code(val) ((0x1f & (val)) << 12) > +#define code_rp12k(val) (((val) >> 12) & 0x1f) > +#define rd_code(val) ((0x1f & (val)) << 7) > +#define code_rd(val) (((val) >> 7) & 0x1f) > +#define dfp_mode(val) ((0x3 & (val)) << 5) > +#define EN_RP4P7K BIT(4) > +#define EN_RP36K BIT(3) > +#define EN_RP12K BIT(2) > +#define EN_RD BIT(1) > +#define EN_CC_DET BIT(0) > + > +#define CC_MODE_UFP 0x0 > +#define CC_MODE_DFP_USB 0x1 > +#define CC_MODE_DFP_1_5 0x2 > +#define CC_MODE_DFP_3_0 0x3 > + > +/* > + * PARAMETER_V0: > + * Realtek Kylin rtd1295 > + * Realtek Hercules rtd1395 > + * Realtek Thor rtd1619 > + * Realtek Hank rtd1319 > + * Realtek Groot rtd1312c > + * PARAMETER_V1: > + * Realtek Stark rtd1619b > + * Realtek Parker rtd1319d > + * Realtek Danvers rtd1315e > + */ > +enum parameter_version { > + PARAMETER_V0 = 0, > + PARAMETER_V1 = 1, > +}; > + > +/* Bit mapping USB_TYPEC_CTRL_CC1_1 and USB_TYPEC_CTRL_CC2_1 */ > +#define V0_vref_2p6v(val) ((0xf & (val)) << 26) /* Bit 29 for groot */ > +#define V0_vref_1p23v(val) ((0xf & (val)) << 22) > +#define V0_vref_0p8v(val) ((0xf & (val)) << 18) > +#define V0_vref_0p66v(val) ((0xf & (val)) << 14) > +#define V0_vref_0p4v(val) ((0x7 & (val)) << 11) > +#define V0_vref_0p2v(val) ((0x7 & (val)) << 8) > +#define V0_vref_1_1p6v(val) ((0xf & (val)) << 4) > +#define V0_vref_0_1p6v(val) ((0xf & (val)) << 0) > + > +#define V0_decode_2p6v(val) (((val) >> 26) & 0xf) /* Bit 29 for groot */ > +#define V0_decode_1p23v(val) (((val) >> 22) & 0xf) > +#define V0_decode_0p8v(val) (((val) >> 18) & 0xf) > +#define V0_decode_0p66v(val) (((val) >> 14) & 0xf) > +#define V0_decode_0p4v(val) (((val) >> 11) & 0x7) > +#define V0_decode_0p2v(val) (((val) >> 8) & 0x7) > +#define V0_decode_1_1p6v(val) (((val) >> 4) & 0xf) > +#define V0_decode_0_1p6v(val) (((val) >> 0) & 0xf) > + > +/* new Bit mapping USB_TYPEC_CTRL_CC1_1 and USB_TYPEC_CTRL_CC2_1 > */ > +#define V1_vref_2p6v(val) ((0xf & (val)) << 28) > +#define V1_vref_1p23v(val) ((0xf & (val)) << 24) > +#define V1_vref_0p8v(val) ((0xf & (val)) << 20) > +#define V1_vref_0p66v(val) ((0xf & (val)) << 16) > +#define V1_vref_0p4v(val) ((0xf & (val)) << 12) > +#define V1_vref_0p2v(val) ((0xf & (val)) << 8) > +#define V1_vref_1_1p6v(val) ((0xf & (val)) << 4) > +#define V1_vref_0_1p6v(val) ((0xf & (val)) << 0) > + > +#define V1_decode_2p6v(val) (((val) >> 28) & 0xf) > +#define V1_decode_1p23v(val) (((val) >> 24) & 0xf) > +#define V1_decode_0p8v(val) (((val) >> 20) & 0xf) > +#define V1_decode_0p66v(val) (((val) >> 16) & 0xf) > +#define V1_decode_0p4v(val) (((val) >> 12) & 0xf) > +#define V1_decode_0p2v(val) (((val) >> 8) & 0xf) > +#define V1_decode_1_1p6v(val) (((val) >> 4) & 0xf) > +#define V1_decode_0_1p6v(val) (((val) >> 0) & 0xf) > + > +/* Bit mapping USB_TYPEC_STS */ > +#define DET_STS 0x7 > +#define CC1_DET_STS (DET_STS) > +#define CC2_DET_STS (DET_STS << 3) > +#define DET_STS_RA 0x1 > +#define DET_STS_RD 0x3 > +#define DET_STS_RP 0x1 > +#define CC1_DET_STS_RA (DET_STS_RA) > +#define CC1_DET_STS_RD (DET_STS_RD) > +#define CC1_DET_STS_RP (DET_STS_RP) > +#define CC2_DET_STS_RA (DET_STS_RA << 3) > +#define CC2_DET_STS_RD (DET_STS_RD << 3) > +#define CC2_DET_STS_RP (DET_STS_RP << 3) > + > +/* Bit mapping USB_TYPEC_CTRL */ > +#define CC2_INT_EN BIT(11) > +#define CC1_INT_EN BIT(10) > +#define CC2_INT_STS BIT(9) > +#define CC1_INT_STS BIT(8) > +#define DEBOUNCE_TIME_MASK 0xff > +#define DEBOUNCE_EN BIT(0) > +#define ENABLE_TYPE_C_DETECT (CC1_INT_EN | CC2_INT_EN) > +#define ALL_CC_INT_STS (CC1_INT_STS | CC2_INT_STS) > + > +/* Parameter */ > +#define DETECT_TIME 50 /* ms */ > + > +static const unsigned int usb_type_c_cable[] = { > + EXTCON_USB, > + EXTCON_USB_HOST, > + EXTCON_NONE, > +}; > + > +enum usb_data_roles { > + DR_NONE, > + DR_HOST, > + DR_DEVICE, > +}; > + > +static const struct soc_device_attribute rtk_soc_kylin[] = { > + { .family = "Realtek Kylin", }, > + { /* empty */ } > +}; > + > +static int rtd129x_switch_type_c_plug_config(struct type_c_data *type_c, > + int dr_mode, int cc) > +{ > + void __iomem *reg = type_c->reg_base + USB_TYPEC_CTRL_CC1_0; > + int val_cc; > + > +#define TYPE_C_EN_SWITCH BIT(29) > +#define TYPE_C_TXRX_SEL (BIT(28) | BIT(27)) > +#define TYPE_C_SWITCH_MASK (TYPE_C_EN_SWITCH | TYPE_C_TXRX_SEL) > +#define TYPE_C_ENABLE_CC1 TYPE_C_EN_SWITCH > +#define TYPE_C_ENABLE_CC2 (TYPE_C_EN_SWITCH | TYPE_C_TXRX_SEL) > +#define TYPE_C_DISABLE_CC ~TYPE_C_SWITCH_MASK > + > + val_cc = readl(reg); > + val_cc &= ~TYPE_C_SWITCH_MASK; > + > + if (cc == DISABLE_CC) { > + val_cc &= TYPE_C_DISABLE_CC; > + } else if (cc == ENABLE_CC1) { > + val_cc |= TYPE_C_ENABLE_CC1; > + } else if (cc == ENABLE_CC2) { > + val_cc |= TYPE_C_ENABLE_CC2; > + } else { > + dev_err(type_c->dev, "%s: Error cc setting cc=0x%x\n", __func__, > cc); > + return -EINVAL; > + } > + writel(val_cc, reg); > + > + /* waiting cc stable for enable/disable */ > + mdelay(1); > + > + dev_dbg(type_c->dev, "%s: cc=0x%x val_cc=0x%x > usb_typec_ctrl_cc1_0=0x%x\n", > + __func__, cc, val_cc, readl(reg)); > + > + return 0; > +} > + > +static inline void switch_type_c_plug_config(struct type_c_data *type_c, > + int dr_mode, int cc) > +{ > + int ret = 0; > + > + if (soc_device_match(rtk_soc_kylin)) > + ret = rtd129x_switch_type_c_plug_config(type_c, dr_mode, cc); > + > + if (ret < 0) > + dev_err(type_c->dev, "%s: Error set type c plug config\n", > + __func__); > +} > + > +static void switch_type_c_dr_mode(struct type_c_data *type_c, int dr_mode, > int cc) > +{ > + bool is_host = false; > + bool is_device = false; > + bool polarity = false; > + bool vbus = false; > + bool ss = true; > + > + switch_type_c_plug_config(type_c, dr_mode, cc); > + if (cc == ENABLE_CC2) > + polarity = true; > + > + switch (dr_mode) { > + case USB_DR_MODE_HOST: > + is_host = true; > + break; > + case USB_DR_MODE_PERIPHERAL: > + is_device = true; > + vbus = true; > + break; > + default: > + dev_dbg(type_c->dev, "%s dr_mode=%d ==> no host or device\n", > + __func__, dr_mode); > + break; > + } > + > + dev_dbg(type_c->dev, "%s is_host=%d is_device=%d vbus=%d > polarity=%d\n", > + __func__, is_host, is_device, vbus, polarity); > + > + /* for EXTCON_USB device mode */ > + extcon_set_state(type_c->edev, EXTCON_USB, is_device); > + extcon_set_property(type_c->edev, EXTCON_USB, > + EXTCON_PROP_USB_VBUS, > + (union extcon_property_value)(int)vbus); > + extcon_set_property(type_c->edev, EXTCON_USB, > + EXTCON_PROP_USB_TYPEC_POLARITY, > + (union extcon_property_value)(int)polarity); > + extcon_set_property(type_c->edev, EXTCON_USB, > + EXTCON_PROP_USB_SS, > + (union extcon_property_value)(int)ss); > + > + /* for EXTCON_USB_HOST host mode */ > + extcon_set_state(type_c->edev, EXTCON_USB_HOST, is_host); > + extcon_set_property(type_c->edev, EXTCON_USB_HOST, > + EXTCON_PROP_USB_VBUS, > + (union extcon_property_value)(int)vbus); > + extcon_set_property(type_c->edev, EXTCON_USB_HOST, > + EXTCON_PROP_USB_TYPEC_POLARITY, > + (union extcon_property_value)(int)polarity); > + extcon_set_property(type_c->edev, EXTCON_USB_HOST, > + EXTCON_PROP_USB_SS, > + (union extcon_property_value)(int)ss); > + > + /* sync EXTCON_USB and EXTCON_USB_HOST */ > + extcon_sync(type_c->edev, EXTCON_USB); > + extcon_sync(type_c->edev, EXTCON_USB_HOST); > + > + if (type_c->port) { > + switch (dr_mode) { > + case USB_DR_MODE_HOST: > + typec_set_data_role(type_c->port, TYPEC_HOST); > + typec_set_pwr_role(type_c->port, TYPEC_SOURCE); > + break; > + case USB_DR_MODE_PERIPHERAL: > + typec_set_data_role(type_c->port, TYPEC_DEVICE); > + typec_set_pwr_role(type_c->port, TYPEC_SINK); > + break; > + default: > + dev_dbg(type_c->dev, "%s unknown dr_mode=%d\n", > + __func__, dr_mode); > + break; > + } > + } > +} > + > +/* connector attached/detached */ > +static int connector_attached(struct type_c_data *type_c, u32 cc, int > dr_mode) > +{ > + void __iomem *reg = type_c->reg_base + USB_TYPEC_CTRL; > + > + cancel_delayed_work(&type_c->delayed_work); > + > + switch_type_c_dr_mode(type_c, dr_mode, cc); > + > + writel(ENABLE_TYPE_C_DETECT | readl(reg), reg); > + > + return 0; > +} > + > +static int connector_detached(struct type_c_data *type_c, u32 cc, int > dr_mode) > +{ > + void __iomem *reg = type_c->reg_base + USB_TYPEC_CTRL; > + > + writel(~ENABLE_TYPE_C_DETECT & readl(reg), reg); > + > + switch_type_c_dr_mode(type_c, 0, cc); > + > + schedule_delayed_work(&type_c->delayed_work, > msecs_to_jiffies(DETECT_TIME)); > + > + return 0; > +} > + > +/* detect host device switch */ > +static int __detect_host_device(struct type_c_data *type_c, u32 rp_or_rd_en) > +{ > + struct device *dev = type_c->dev; > + void __iomem *reg_base = type_c->reg_base; > + u32 cc1_config, cc2_config, default_ctrl; > + u32 cc1_switch = 0; > + > + default_ctrl = readl(reg_base + USB_TYPEC_CTRL) & > DEBOUNCE_TIME_MASK; > + writel(default_ctrl, reg_base + USB_TYPEC_CTRL); > + > + cc1_config = readl(reg_base + USB_TYPEC_CTRL_CC1_0); > + cc2_config = readl(reg_base + USB_TYPEC_CTRL_CC2_0); > + > + cc1_config &= ~EN_CC_DET; > + cc2_config &= ~EN_CC_DET; > + writel(cc1_config, reg_base + USB_TYPEC_CTRL_CC1_0); > + writel(cc2_config, reg_base + USB_TYPEC_CTRL_CC2_0); > + > + if (soc_device_match(rtk_soc_kylin)) > + cc1_switch = cc1_config & CC_SWITCH_MASK; > + > + cc1_config &= CC_CODE_MASK; > + cc1_config |= rp_or_rd_en | cc1_switch; > + cc2_config &= CC_CODE_MASK; > + cc2_config |= rp_or_rd_en; > + writel(cc2_config, reg_base + USB_TYPEC_CTRL_CC2_0); > + writel(cc1_config, reg_base + USB_TYPEC_CTRL_CC1_0); > + > + /* For kylin to disable external rd control gpio */ > + if (soc_device_match(rtk_soc_kylin)) { > + struct gpio_desc *gpio = type_c->rd_ctrl_gpio_desc; > + > + if (gpio && gpiod_direction_output(gpio, 1)) > + dev_err(dev, "%s ERROR set rd_ctrl_gpio_desc fail\n", > __func__); > + } > + > + cc1_config |= EN_CC_DET; > + cc2_config |= EN_CC_DET; > + writel(cc1_config, reg_base + USB_TYPEC_CTRL_CC1_0); > + writel(cc2_config, reg_base + USB_TYPEC_CTRL_CC2_0); > + > + return 0; > +} > + > +static int detect_device(struct type_c_data *type_c) > +{ > + return __detect_host_device(type_c, type_c->dfp_mode_rp_en); > +} > + > +static int detect_host(struct type_c_data *type_c) > +{ > + return __detect_host_device(type_c, type_c->ufp_mode_rd_en); > +} > + > +static int host_device_switch_detection(struct type_c_data *type_c) > +{ > + if (type_c->cc_mode == IN_HOST_MODE) { > + type_c->cc_mode = IN_DEVICE_MODE; > + detect_host(type_c); > + } else { > + type_c->cc_mode = IN_HOST_MODE; > + detect_device(type_c); > + } > + > + return 0; > +} > + > +static int detect_type_c_state(struct type_c_data *type_c) > +{ > + struct device *dev = type_c->dev; > + void __iomem *reg_base = type_c->reg_base; > + u32 int_status, cc_status, cc_status_check; > + unsigned long flags; > + > + spin_lock_irqsave(&type_c->lock, flags); > + > + int_status = readl(reg_base + USB_TYPEC_CTRL); > + cc_status = readl(reg_base + USB_TYPEC_STS); > + > + type_c->connect_change = CONNECT_NO_CHANGE; > + > + switch (type_c->cc_mode | type_c->is_attach) { > + case IN_HOST_MODE | IN_ATTACH: > + if (((cc_status & CC1_DET_STS) == CC1_DET_STS) && > type_c->at_cc1 == AT_CC1) { > + dev_dbg(dev, "IN host mode and cc1 device detach > (cc_status=0x%x)", > + cc_status); > + type_c->is_attach = TO_DETACH; > + type_c->connect_change = CONNECT_CHANGE; > + } else if (((cc_status & CC2_DET_STS) == CC2_DET_STS) && > + type_c->at_cc1 == AT_CC2) { > + dev_dbg(dev, "IN host mode and cc2 device detach > (cc_status=0x%x)", > + cc_status); > + type_c->is_attach = TO_DETACH; > + type_c->connect_change = CONNECT_CHANGE; > + } > + break; > + case IN_HOST_MODE | IN_DETACH: > + cc_status_check = readl(reg_base + USB_TYPEC_STS); > + if (cc_status_check != (CC1_DET_STS | CC2_DET_STS)) { > + if (in_interrupt()) { > + /* Add delay time to avoid capacitive effect of cable. */ > + mdelay(300); > + } else { > + spin_unlock_irqrestore(&type_c->lock, flags); > + /* Add delay time to avoid capacitive effect of cable. */ > + msleep(300); > + spin_lock_irqsave(&type_c->lock, flags); > + } > + cc_status_check = readl(reg_base + USB_TYPEC_STS); > + } > + if (cc_status != cc_status_check) { > + dev_warn(dev, "IN_HOST_MODE: cc_status (0x%x) != > cc_status_check (0x%x)\n", > + cc_status, cc_status_check); > + cc_status = readl(reg_base + USB_TYPEC_STS); > + } > + > + if ((cc_status & CC1_DET_STS) == CC1_DET_STS_RD) { > + dev_dbg(dev, "IN host mode and cc1 device attach > (cc_status=0x%x)", > + cc_status); > + type_c->is_attach = TO_ATTACH; > + type_c->at_cc1 = AT_CC1; > + type_c->connect_change = CONNECT_CHANGE; > + } else if ((cc_status & CC2_DET_STS) == CC2_DET_STS_RD) { > + dev_dbg(dev, "In host mode and cc2 device attach > (cc_status=0x%x)", > + cc_status); > + type_c->is_attach = TO_ATTACH; > + type_c->at_cc1 = AT_CC2; > + type_c->connect_change = CONNECT_CHANGE; > + } > + break; > + case IN_DEVICE_MODE | IN_ATTACH: > + if ((cc_status & CC1_DET_STS) < CC1_DET_STS_RP || > + (cc_status & CC2_DET_STS) < CC2_DET_STS_RP) { > + /* Add a sw debounce to filter cc signal sent from apple pd > adapter */ > + mdelay(5); > + cc_status_check = readl(reg_base + USB_TYPEC_STS); > + > + if (cc_status != cc_status_check) { > + dev_dbg(dev, "IN_DEVICE_MODE: cc_status (0x%x) != > cc_status_check (0x%x) maybe use a pd adapter\n", > + cc_status, cc_status_check); > + cc_status = cc_status_check; > + } > + } > + > + if ((cc_status & CC1_DET_STS) < CC1_DET_STS_RP && > type_c->at_cc1 == AT_CC1) { > + dev_dbg(dev, "IN device mode and cc1 host disconnect > (cc_status=0x%x)", > + cc_status); > + type_c->is_attach = TO_DETACH; > + type_c->connect_change = CONNECT_CHANGE; > + } else if ((cc_status & CC2_DET_STS) < CC2_DET_STS_RP && > + type_c->at_cc1 == AT_CC2) { > + dev_dbg(dev, "IN device mode and cc2 host disconnect > (cc_status=0x%x)", > + cc_status); > + type_c->is_attach = TO_DETACH; > + type_c->connect_change = CONNECT_CHANGE; > + } > + break; > + case IN_DEVICE_MODE | IN_DETACH: > + cc_status_check = readl(reg_base + USB_TYPEC_STS); > + if (cc_status_check != 0x0) { > + if (in_interrupt()) { > + /* Add delay time to avoid capacitive effect of cable. */ > + mdelay(300); > + } else { > + spin_unlock_irqrestore(&type_c->lock, flags); > + /* Add delay time to avoid capacitive effect of cable. */ > + msleep(300); > + spin_lock_irqsave(&type_c->lock, flags); > + } > + cc_status_check = readl(reg_base + USB_TYPEC_STS); > + } > + > + if (cc_status != cc_status_check) { > + dev_warn(dev, "IN_DEVICE_MODE: cc_status (0x%x) != > cc_status_check (0x%x)\n", > + cc_status, cc_status_check); > + cc_status = readl(reg_base + USB_TYPEC_STS); > + } > + > + if ((cc_status & CC1_DET_STS) >= CC1_DET_STS_RP) { > + dev_dbg(dev, "IN device mode and cc1 host connect > (cc_status=0x%x)", > + cc_status); > + type_c->at_cc1 = AT_CC1; > + type_c->is_attach = TO_ATTACH; > + type_c->connect_change = CONNECT_CHANGE; > + } else if ((cc_status & CC2_DET_STS) >= CC2_DET_STS_RP) { > + dev_dbg(dev, "IN device mode and cc2 host connect > (cc_status=0x%x)", > + cc_status); > + type_c->at_cc1 = AT_CC2; > + type_c->is_attach = TO_ATTACH; > + type_c->connect_change = CONNECT_CHANGE; > + } > + break; > + default: > + dev_err(dev, "error host or device mode (cc_mode=%d, > is_attach=%d) ", > + type_c->cc_mode, type_c->is_attach); > + } > + > + type_c->int_status = int_status; > + type_c->cc_status = cc_status; > + > + spin_unlock_irqrestore(&type_c->lock, flags); > + return 0; > +} > + > +static void host_device_switch(struct work_struct *work) > +{ > + struct type_c_data *type_c = container_of(work, struct type_c_data, > + delayed_work.work); > + struct device *dev = type_c->dev; > + unsigned long flags; > + int connect_change = 0; > + int cc_mode = 0; > + int is_attach = 0; > + int at_cc1 = 0; > + > + spin_lock_irqsave(&type_c->lock, flags); > + if (type_c->connect_change) > + connect_change = type_c->connect_change; > + spin_unlock_irqrestore(&type_c->lock, flags); > + > + if (!connect_change) > + detect_type_c_state(type_c); > + > + spin_lock_irqsave(&type_c->lock, flags); > + if (type_c->connect_change) { > + connect_change = type_c->connect_change; > + cc_mode = type_c->cc_mode; > + is_attach = type_c->is_attach; > + at_cc1 = type_c->at_cc1; > + type_c->connect_change = CONNECT_NO_CHANGE; > + } else { > + host_device_switch_detection(type_c); > + > + schedule_delayed_work(&type_c->delayed_work, > msecs_to_jiffies(DETECT_TIME)); > + } > + spin_unlock_irqrestore(&type_c->lock, flags); > + > + if (!connect_change) > + return; > + > + dev_dbg(dev, "%s: usb cable connection change\n", __func__); > + if (cc_mode == IN_HOST_MODE) { > + if (is_attach && at_cc1) > + connector_attached(type_c, ENABLE_CC1, > USB_DR_MODE_HOST); > + else if (is_attach && !at_cc1) > + connector_attached(type_c, ENABLE_CC2, > USB_DR_MODE_HOST); > + else > + connector_detached(type_c, DISABLE_CC, > USB_DR_MODE_HOST); > + } else if (cc_mode == IN_DEVICE_MODE) { > + if (is_attach && at_cc1) > + connector_attached(type_c, ENABLE_CC1, > USB_DR_MODE_PERIPHERAL); > + else if (is_attach && !at_cc1) > + connector_attached(type_c, ENABLE_CC2, > USB_DR_MODE_PERIPHERAL); > + else > + connector_detached(type_c, DISABLE_CC, > USB_DR_MODE_PERIPHERAL); > + } else { > + dev_err(dev, "Error: IN unknown mode %d to %s at %s > (cc_status=0x%x)\n", > + cc_mode, is_attach ? "attach" : "detach", > + at_cc1 ? "cc1" : "cc2", type_c->cc_status); > + } > + dev_info(dev, "Connection change OK: IN %s mode to %s at %s > (cc_status=0x%x)\n", > + cc_mode == IN_HOST_MODE ? "host" : "device", > + is_attach ? "attach" : "detach", > + at_cc1 ? "cc1" : "cc2", type_c->cc_status); > +} > + > +static irqreturn_t type_c_detect_irq(int irq, void *__data) > +{ > + struct type_c_data *type_c = (struct type_c_data *)__data; > + struct device *dev = type_c->dev; > + void __iomem *reg = type_c->reg_base + USB_TYPEC_CTRL; > + unsigned long flags; > + > + detect_type_c_state(type_c); > + > + spin_lock_irqsave(&type_c->lock, flags); > + > + if (type_c->connect_change) { > + dev_dbg(dev, "%s: IN %s mode to %s (at %s interrupt) > int_status=0x%x, cc_status=0x%x", > + __func__, > + type_c->cc_mode == IN_HOST_MODE ? "host" : "device", > + type_c->is_attach ? "attach" : "detach", > + type_c->at_cc1 ? "cc1" : "cc2", > + type_c->int_status, type_c->cc_status); > + > + /* clear interrupt status */ > + writel(~ALL_CC_INT_STS & readl(reg), reg); > + > + cancel_delayed_work(&type_c->delayed_work); > + schedule_delayed_work(&type_c->delayed_work, > msecs_to_jiffies(0)); > + } else { > + static int local_count; > + > + /* if no connect_change, we keep the status to avoid status lose */ > + if (local_count++ > 10) { > + /* clear interrupt status */ > + writel(~ALL_CC_INT_STS & readl(reg), reg); > + local_count = 0; > + } > + } > + > + spin_unlock_irqrestore(&type_c->lock, flags); > + > + return IRQ_HANDLED; > +} > + > +static int type_c_port_dr_set(struct typec_port *port, > + enum typec_data_role role) > +{ > + struct type_c_data *type_c = typec_get_drvdata(port); > + u32 enable_cc; > + unsigned long flags; > + > + spin_lock_irqsave(&type_c->lock, flags); > + enable_cc = type_c->at_cc1 ? ENABLE_CC1 : ENABLE_CC2; > + spin_unlock_irqrestore(&type_c->lock, flags); > + > + if (role == TYPEC_HOST) > + switch_type_c_dr_mode(type_c, USB_DR_MODE_HOST, enable_cc); > + else if (role == TYPEC_DEVICE) > + switch_type_c_dr_mode(type_c, USB_DR_MODE_PERIPHERAL, > enable_cc); > + else > + switch_type_c_dr_mode(type_c, 0, DISABLE_CC); > + > + return 0; > +} > + > +static const struct typec_operations type_c_port_ops = { > + .dr_set = type_c_port_dr_set, > +}; > + > +#ifdef CONFIG_DEBUG_FS > +static int type_c_parameter_show(struct seq_file *s, void *unused) > +{ > + struct type_c_data *type_c = s->private; > + struct type_c_cfg *type_c_cfg = type_c->type_c_cfg; > + struct cc_param *cc_param; > + unsigned long flags; > + > + spin_lock_irqsave(&type_c->lock, flags); > + > + seq_printf(s, "cc_dfp_mode %s\n", > + ({ char *tmp; > + switch (type_c_cfg->cc_dfp_mode) { > + case CC_MODE_DFP_USB: > + tmp = "CC_MODE_DFP_USB"; break; > + case CC_MODE_DFP_1_5: > + tmp = "CC_MODE_DFP_1_5"; break; > + case CC_MODE_DFP_3_0: > + tmp = "CC_MODE_DFP_3_0"; break; > + default: > + tmp = "?"; break; > + } tmp; })); > + > + seq_printf(s, "dfp_mode_rp_en 0x%x\n", type_c->dfp_mode_rp_en); > + seq_printf(s, "ufp_mode_rd_en 0x%x\n", type_c->ufp_mode_rd_en); > + seq_printf(s, "cc1_code 0x%x\n", type_c->cc1_code); > + seq_printf(s, "cc2_code 0x%x\n", type_c->cc2_code); > + seq_printf(s, "cc1_vref 0x%x\n", type_c->cc1_vref); > + seq_printf(s, "cc2_vref 0x%x\n", type_c->cc2_vref); > + seq_printf(s, "debounce 0x%x\n", type_c->debounce); > + seq_puts(s, "\n"); > + > + cc_param = &type_c_cfg->cc1_param; > + seq_puts(s, "cc1_param:\n"); > + seq_printf(s, " rp_4p7k_code 0x%x\n", cc_param->rp_4p7k_code); > + seq_printf(s, " rp_36k_code 0x%x\n", cc_param->rp_36k_code); > + seq_printf(s, " rp_12k_code 0x%x\n", cc_param->rp_12k_code); > + seq_printf(s, " rd_code 0x%x\n", cc_param->rd_code); > + seq_printf(s, " vref_2p6v 0x%x\n", cc_param->vref_2p6v); > + seq_printf(s, " vref_1p23v 0x%x\n", cc_param->vref_1p23v); > + seq_printf(s, " vref_0p8v 0x%x\n", cc_param->vref_0p8v); > + seq_printf(s, " vref_0p66v 0x%x\n", cc_param->vref_0p66v); > + seq_printf(s, " vref_0p4v 0x%x\n", cc_param->vref_0p4v); > + seq_printf(s, " vref_0p2v 0x%x\n", cc_param->vref_0p2v); > + seq_printf(s, " vref_1_1p6v 0x%x\n", cc_param->vref_1_1p6v); > + seq_printf(s, " vref_0_1p6v 0x%x\n", cc_param->vref_0_1p6v); > + > + cc_param = &type_c_cfg->cc2_param; > + seq_puts(s, "cc2_param:\n"); > + seq_printf(s, " rp_4p7k_code 0x%x\n", cc_param->rp_4p7k_code); > + seq_printf(s, " rp_36k_code 0x%x\n", cc_param->rp_36k_code); > + seq_printf(s, " rp_12k_code 0x%x\n", cc_param->rp_12k_code); > + seq_printf(s, " rd_code 0x%x\n", cc_param->rd_code); > + seq_printf(s, " vref_2p6v 0x%x\n", cc_param->vref_2p6v); > + seq_printf(s, " vref_1p23v 0x%x\n", cc_param->vref_1p23v); > + seq_printf(s, " vref_0p8v 0x%x\n", cc_param->vref_0p8v); > + seq_printf(s, " vref_0p66v 0x%x\n", cc_param->vref_0p66v); > + seq_printf(s, " vref_0p4v 0x%x\n", cc_param->vref_0p4v); > + seq_printf(s, " vref_0p2v 0x%x\n", cc_param->vref_0p2v); > + seq_printf(s, " vref_1_1p6v 0x%x\n", cc_param->vref_1_1p6v); > + seq_printf(s, " vref_0_1p6v 0x%x\n", cc_param->vref_0_1p6v); > + > + spin_unlock_irqrestore(&type_c->lock, flags); > + > + return 0; > +} > + > +static int type_c_parameter_open(struct inode *inode, struct file *file) > +{ > + return single_open(file, type_c_parameter_show, inode->i_private); > +} > + > +static const struct file_operations type_c_parameter_fops = { > + .open = type_c_parameter_open, > + .read = seq_read, > + .llseek = seq_lseek, > + .release = single_release, > +}; > + > +static int type_c_status_show(struct seq_file *s, void *unused) > +{ > + struct type_c_data *type_c = s->private; > + unsigned long flags; > + > + spin_lock_irqsave(&type_c->lock, flags); > + > + seq_printf(s, "In %s mode %s at %s (cc_status=0x%x)\n", > + type_c->cc_mode == IN_HOST_MODE ? "host" : "device", > + type_c->is_attach ? "attach" : "detach", > + type_c->at_cc1 ? "cc1" : "cc2", type_c->cc_status); > + > + seq_printf(s, "Read Register (type_c_ctrl_cc1_0=0x%x)\n", > + readl(type_c->reg_base + 0x0)); > + seq_printf(s, "Read Register (type_c_ctrl_cc1_1=0x%x)\n", > + readl(type_c->reg_base + 0x4)); > + seq_printf(s, "Read Register (type_c_ctrl_cc2_0=0x%x)\n", > + readl(type_c->reg_base + 0x8)); > + seq_printf(s, "Read Register (type_c_ctrl_cc2_1=0x%x)\n", > + readl(type_c->reg_base + 0xc)); > + seq_printf(s, "Read Register (type_c_status=0x%x)\n", > + readl(type_c->reg_base + 0x10)); > + seq_printf(s, "Read Register (type_c_ctrl=0x%x)\n", > + readl(type_c->reg_base + 0x14)); > + > + spin_unlock_irqrestore(&type_c->lock, flags); > + > + return 0; > +} > + > +static int type_c_status_open(struct inode *inode, struct file *file) > +{ > + return single_open(file, type_c_status_show, inode->i_private); > +} > + > +static const struct file_operations type_c_status_fops = { > + .open = type_c_status_open, > + .read = seq_read, > + .llseek = seq_lseek, > + .release = single_release, > +}; > + > +static inline void create_debug_files(struct type_c_data *type_c) > +{ > + type_c->debug_dir = debugfs_create_dir("type_c", usb_debug_root); > + > + debugfs_create_file("parameter", 0444, type_c->debug_dir, type_c, > + &type_c_parameter_fops); > + > + debugfs_create_file("status", 0444, type_c->debug_dir, type_c, > + &type_c_status_fops); > +} > + > +static inline void remove_debug_files(struct type_c_data *type_c) > +{ > + debugfs_remove_recursive(type_c->debug_dir); > +} > +#else > +static inline void create_debug_files(struct type_c_data *type_c) { } > +static inline void remove_debug_files(struct type_c_data *type_c) { } > +#endif /* CONFIG_DEBUG_FS */ > + > +/* Init and probe */ > + > +static inline s8 get_value(s8 value) > +{ > + return (((s8)value & 0x8) ? (-(s8)(0x7 & value)) : ((s8)(value))); > +} > + > +static int __updated_type_c_parameter_by_efuse(struct type_c_data > *type_c) > +{ > + struct type_c_cfg *type_c_cfg = type_c->type_c_cfg; > + struct cc_param *cc_param; > + struct nvmem_cell *cell; > + s8 cc1_4p7k = 0; > + s8 cc1_12k = 0; > + s8 cc1_0p2v = 0; > + s8 cc1_0p8v = 0; > + s8 cc1_2p6v = 0; > + s8 cc1_0p66v = 0; > + s8 cc1_1p23v = 0; > + s8 cc2_4p7k = 0; > + s8 cc2_12k = 0; > + s8 cc2_0p2v = 0; > + s8 cc2_0p8v = 0; > + s8 cc2_2p6v = 0; > + s8 cc2_0p66v = 0; > + s8 cc2_1p23v = 0; > + > + cell = nvmem_cell_get(type_c->dev, "usb-cal"); > + if (IS_ERR(cell)) { > + dev_warn(type_c->dev, "%s failed to get usb-cal: %ld\n", > + __func__, PTR_ERR(cell)); > + } else { > + unsigned char *buf; > + size_t buf_size; > + int value_size = 4; > + int value_mask = (BIT(value_size) - 1); > + > + buf = nvmem_cell_read(cell, &buf_size); > + > + cc1_0p2v = get_value((buf[0] >> value_size * 0) & value_mask); > + cc1_0p8v = get_value((buf[0] >> value_size * 1) & value_mask); > + cc1_2p6v = get_value((buf[1] >> value_size * 0) & value_mask); > + cc1_0p66v = get_value((buf[1] >> value_size * 1) & value_mask); > + cc1_1p23v = get_value((buf[2] >> value_size * 0) & value_mask); > + > + cc2_0p2v = get_value((buf[3] >> value_size * 0) & value_mask); > + cc2_0p8v = get_value((buf[3] >> value_size * 1) & value_mask); > + cc2_2p6v = get_value((buf[4] >> value_size * 0) & value_mask); > + cc2_0p66v = get_value((buf[4] >> value_size * 1) & value_mask); > + cc2_1p23v = get_value((buf[5] >> value_size * 0) & value_mask); > + > + cc1_4p7k = get_value((buf[6] >> value_size * 0) & value_mask); > + cc1_12k = get_value((buf[6] >> value_size * 1) & value_mask); > + cc2_4p7k = get_value((buf[7] >> value_size * 0) & value_mask); > + cc2_12k = get_value((buf[7] >> value_size * 1) & value_mask); > + > + kfree(buf); > + nvmem_cell_put(cell); > + } > + > + dev_dbg(type_c->dev, "check efuse cc1_4p7k=%d cc1_12k=%d > cc2_4p7k=%d cc2_12k=%d\n", > + cc1_4p7k, cc1_12k, cc2_4p7k, cc2_12k); > + dev_dbg(type_c->dev, "check efuse cc1_0p2v=%d cc1_0p8v=%d > cc1_2p6v=%d cc1_0p66v=%d cc1_1p23v=%d\n", > + cc1_0p2v, cc1_0p8v, cc1_2p6v, cc1_0p66v, cc1_1p23v); > + dev_dbg(type_c->dev, "check efuse cc2_0p2v=%d cc2_0p8v=%d > cc2_2p6v=%d cc2_0p66v=%d cc2_1p23v=%d\n", > + cc2_0p2v, cc2_0p8v, cc2_2p6v, cc2_0p66v, cc2_1p23v); > + > + cc_param = &type_c_cfg->cc1_param; > + cc_param->rp_4p7k_code = cc_param->rp_4p7k_code + cc1_4p7k; > + cc_param->rp_12k_code = cc_param->rp_12k_code + cc1_12k; > + > + cc_param->vref_1p23v = cc_param->vref_1p23v + cc1_1p23v; > + cc_param->vref_0p66v = cc_param->vref_0p66v + cc1_0p66v; > + cc_param->vref_2p6v = cc_param->vref_2p6v + cc1_2p6v; > + cc_param->vref_0p8v = cc_param->vref_0p8v + cc1_0p8v; > + cc_param->vref_0p2v = cc_param->vref_0p2v + cc1_0p2v; > + > + cc_param = &type_c_cfg->cc2_param; > + cc_param->rp_4p7k_code = cc_param->rp_4p7k_code + cc2_4p7k; > + cc_param->rp_12k_code = cc_param->rp_12k_code + cc2_12k; > + > + cc_param->vref_1p23v = cc_param->vref_1p23v + cc2_1p23v; > + cc_param->vref_0p66v = cc_param->vref_0p66v + cc2_0p66v; > + cc_param->vref_2p6v = cc_param->vref_2p6v + cc2_2p6v; > + cc_param->vref_0p8v = cc_param->vref_0p8v + cc2_0p8v; > + cc_param->vref_0p2v = cc_param->vref_0p2v + cc2_0p2v; > + > + return 0; > +} > + > +static int __updated_type_c_parameter_by_efuse_v2(struct type_c_data > *type_c) > +{ > + struct type_c_cfg *type_c_cfg = type_c->type_c_cfg; > + struct cc_param *cc_param; > + struct nvmem_cell *cell; > + s8 cc1_4p7k = 0; > + s8 cc1_12k = 0; > + s8 cc1_0p2v = 0; > + s8 cc1_0p8v = 0; > + s8 cc1_2p6v = 0; > + s8 cc1_0p66v = 0; > + s8 cc1_1p23v = 0; > + s8 cc2_4p7k = 0; > + s8 cc2_12k = 0; > + s8 cc2_0p2v = 0; > + s8 cc2_0p8v = 0; > + s8 cc2_2p6v = 0; > + s8 cc2_0p66v = 0; > + s8 cc2_1p23v = 0; > + > + cell = nvmem_cell_get(type_c->dev, "usb-type-c-cal"); > + if (IS_ERR(cell)) { > + dev_warn(type_c->dev, "%s failed to get usb-type-c-cal: %ld\n", > + __func__, PTR_ERR(cell)); > + } else { > + unsigned char *buf; > + size_t buf_size; > + int value_size = 0; > + int value_mask = (BIT(value_size) - 1); > + > + buf = nvmem_cell_read(cell, &buf_size); > + > + value_size = 5; > + value_mask = (BIT(value_size) - 1); > + cc1_4p7k = buf[0] & value_mask; > + cc1_12k = buf[1] & value_mask; > + cc2_4p7k = buf[2] & value_mask; > + cc2_12k = buf[3] & value_mask; > + > + value_size = 4; > + value_mask = (BIT(value_size) - 1); > + cc1_0p2v = (buf[4] >> value_size * 0) & value_mask; > + cc1_0p66v = (buf[4] >> value_size * 1) & value_mask; > + cc1_0p8v = (buf[5] >> value_size * 0) & value_mask; > + cc1_1p23v = (buf[5] >> value_size * 1) & value_mask; > + cc1_2p6v = (buf[6] >> value_size * 0) & value_mask; > + > + cc2_0p2v = (buf[6] >> value_size * 1) & value_mask; > + cc2_0p66v = (buf[7] >> value_size * 0) & value_mask; > + cc2_0p8v = (buf[7] >> value_size * 1) & value_mask; > + cc2_1p23v = (buf[8] >> value_size * 0) & value_mask; > + cc2_2p6v = (buf[8] >> value_size * 1) & value_mask; > + > + kfree(buf); > + nvmem_cell_put(cell); > + } > + > + dev_dbg(type_c->dev, "check efuse v2 cc1_4p7k=%d cc1_12k=%d > cc2_4p7k=%d cc2_12k=%d\n", > + cc1_4p7k, cc1_12k, cc2_4p7k, cc2_12k); > + dev_dbg(type_c->dev, "check efuse v2 cc1_0p2v=%d cc1_0p8v=%d > cc1_2p6v=%d cc1_0p66v=%d cc1_1p23v=%d\n", > + cc1_0p2v, cc1_0p8v, cc1_2p6v, cc1_0p66v, cc1_1p23v); > + dev_dbg(type_c->dev, "check efuse v2 cc2_0p2v=%d cc2_0p8v=%d > cc2_2p6v=%d cc2_0p66v=%d cc2_1p23v=%d\n", > + cc2_0p2v, cc2_0p8v, cc2_2p6v, cc2_0p66v, cc2_1p23v); > + > + cc_param = &type_c_cfg->cc1_param; > + if (cc1_4p7k) > + cc_param->rp_4p7k_code = cc1_4p7k; > + if (cc1_12k) > + cc_param->rp_12k_code = cc1_12k; > + > + if (cc1_1p23v) > + cc_param->vref_1p23v = cc1_1p23v; > + if (cc1_0p66v) > + cc_param->vref_0p66v = cc1_0p66v; > + if (cc1_2p6v) > + cc_param->vref_2p6v = cc1_2p6v; > + if (cc1_0p8v) > + cc_param->vref_0p8v = cc1_0p8v; > + if (cc1_0p2v) > + cc_param->vref_0p2v = cc1_0p2v; > + > + cc_param = &type_c_cfg->cc2_param; > + if (cc2_4p7k) > + cc_param->rp_4p7k_code = cc2_4p7k; > + if (cc2_12k) > + cc_param->rp_12k_code = cc2_12k; > + > + if (cc2_1p23v) > + cc_param->vref_1p23v = cc2_1p23v; > + if (cc2_0p66v) > + cc_param->vref_0p66v = cc2_0p66v; > + if (cc2_2p6v) > + cc_param->vref_2p6v = cc2_2p6v; > + if (cc2_0p8v) > + cc_param->vref_0p8v = cc2_0p8v; > + if (cc2_0p2v) > + cc_param->vref_0p2v = cc2_0p2v; > + > + return 0; > +} > + > +static void get_default_type_c_parameter(struct type_c_data *type_c) > +{ > + void __iomem *reg; > + int val; > + > + type_c->dfp_mode_rp_en = dfp_mode(CC_MODE_DFP_3_0) | > EN_RP4P7K; > + type_c->ufp_mode_rd_en = EN_RD; > + > + reg = type_c->reg_base + USB_TYPEC_CTRL_CC1_0; > + val = readl(reg); > + type_c->cc1_code = CC_CODE_MASK & val; > + > + reg = type_c->reg_base + USB_TYPEC_CTRL_CC2_0; > + val = readl(reg); > + type_c->cc2_code = CC_CODE_MASK & val; > + > + reg = type_c->reg_base + USB_TYPEC_CTRL_CC1_1; > + val = readl(reg); > + type_c->cc1_vref = val; > + > + reg = type_c->reg_base + USB_TYPEC_CTRL_CC2_1; > + val = readl(reg); > + type_c->cc2_vref = val; > + > + reg = type_c->reg_base + USB_TYPEC_CTRL; > + val = readl(reg); > + type_c->debounce = DEBOUNCE_TIME_MASK & val; > +} > + > +static int setup_type_c_parameter(struct type_c_data *type_c) > +{ > + struct type_c_cfg *type_c_cfg = type_c->type_c_cfg; > + struct cc_param *cc_param; > + struct soc_device_attribute rtk_soc_efuse_v1[] = { > + { .family = "Realtek Phoenix",}, > + { .family = "Realtek Kylin",}, > + { .family = "Realtek Hercules",}, > + { .family = "Realtek Thor",}, > + { .family = "Realtek Hank",}, > + { .family = "Realtek Groot",}, > + { .family = "Realtek Stark",}, > + { .family = "Realtek Parker",}, > + { /* empty */ } > + }; > + > + if (type_c_cfg->use_defalut_parameter) { > + get_default_type_c_parameter(type_c); > + return 0; > + } > + > + if (soc_device_match(rtk_soc_efuse_v1)) > + __updated_type_c_parameter_by_efuse(type_c); > + else > + __updated_type_c_parameter_by_efuse_v2(type_c); > + > + /* > + * UFP rd vref_ufp : 1p23v, 0p66v, 0p2v > + * DFP_USB rp36k vref_dfp_usb: 0_1p6v, 0p2v, unused > + * DFP_1.5 rp12k vref_dfp_1_5: 1_1p6v, 0p4v, 0p2v > + * DFP_3.0 rp4p7k vref_dfp_3_0: 2p6v, 0p8v, 0p2v > + */ > + > + switch (type_c_cfg->cc_dfp_mode) { > + case CC_MODE_DFP_USB: > + type_c->dfp_mode_rp_en = dfp_mode(CC_MODE_DFP_USB) | > EN_RP36K; > + break; > + case CC_MODE_DFP_1_5: > + type_c->dfp_mode_rp_en = dfp_mode(CC_MODE_DFP_1_5) | > EN_RP12K; > + break; > + case CC_MODE_DFP_3_0: > + type_c->dfp_mode_rp_en = dfp_mode(CC_MODE_DFP_3_0) | > EN_RP4P7K; > + break; > + default: > + dev_err(type_c->dev, "%s: unknown cc_dfp_mode %d\n", > + __func__, type_c_cfg->cc_dfp_mode); > + } > + > + type_c->ufp_mode_rd_en = EN_RD; > + > + cc_param = &type_c_cfg->cc1_param; > + type_c->cc1_code = rp4pk_code(cc_param->rp_4p7k_code) | > + rp36k_code(cc_param->rp_36k_code) | > + rp12k_code(cc_param->rp_12k_code) | > + rd_code(cc_param->rd_code); > + > + if (type_c_cfg->parameter_ver == PARAMETER_V0) > + type_c->cc1_vref = V0_vref_2p6v(cc_param->vref_2p6v) | > + V0_vref_1p23v(cc_param->vref_1p23v) | > + V0_vref_0p8v(cc_param->vref_0p8v) | > + V0_vref_0p66v(cc_param->vref_0p66v) | > + V0_vref_0p4v(cc_param->vref_0p4v) | > + V0_vref_0p2v(cc_param->vref_0p2v) | > + V0_vref_1_1p6v(cc_param->vref_1_1p6v) | > + V0_vref_0_1p6v(cc_param->vref_0_1p6v); > + else if (type_c_cfg->parameter_ver == PARAMETER_V1) > + type_c->cc1_vref = V1_vref_2p6v(cc_param->vref_2p6v) | > + V1_vref_1p23v(cc_param->vref_1p23v) | > + V1_vref_0p8v(cc_param->vref_0p8v) | > + V1_vref_0p66v(cc_param->vref_0p66v) | > + V1_vref_0p4v(cc_param->vref_0p4v) | > + V1_vref_0p2v(cc_param->vref_0p2v) | > + V1_vref_1_1p6v(cc_param->vref_1_1p6v) | > + V1_vref_0_1p6v(cc_param->vref_0_1p6v); > + else > + dev_err(type_c->dev, "%s: unknown parameter_ver %d\n", > + __func__, type_c_cfg->parameter_ver); > + > + cc_param = &type_c_cfg->cc2_param; > + type_c->cc2_code = rp4pk_code(cc_param->rp_4p7k_code) > + | rp36k_code(cc_param->rp_36k_code) > + | rp12k_code(cc_param->rp_12k_code) > + | rd_code(cc_param->rd_code); > + > + if (type_c_cfg->parameter_ver == PARAMETER_V0) > + type_c->cc2_vref = V0_vref_2p6v(cc_param->vref_2p6v) | > + V0_vref_1p23v(cc_param->vref_1p23v) | > + V0_vref_0p8v(cc_param->vref_0p8v) | > + V0_vref_0p66v(cc_param->vref_0p66v) | > + V0_vref_0p4v(cc_param->vref_0p4v) | > + V0_vref_0p2v(cc_param->vref_0p2v) | > + V0_vref_1_1p6v(cc_param->vref_1_1p6v) | > + V0_vref_0_1p6v(cc_param->vref_0_1p6v); > + else if (type_c_cfg->parameter_ver == PARAMETER_V1) > + type_c->cc2_vref = V1_vref_2p6v(cc_param->vref_2p6v) | > + V1_vref_1p23v(cc_param->vref_1p23v) | > + V1_vref_0p8v(cc_param->vref_0p8v) | > + V1_vref_0p66v(cc_param->vref_0p66v) | > + V1_vref_0p4v(cc_param->vref_0p4v) | > + V1_vref_0p2v(cc_param->vref_0p2v) | > + V1_vref_1_1p6v(cc_param->vref_1_1p6v) | > + V1_vref_0_1p6v(cc_param->vref_0_1p6v); > + else > + dev_err(type_c->dev, "%s: unknown parameter_ver %d\n", > + __func__, type_c_cfg->parameter_ver); > + > + type_c->debounce = (type_c_cfg->debounce_val << 1) | DEBOUNCE_EN; > + > + return 0; > +} > + > +static int extcon_rtk_type_c_init(struct type_c_data *type_c) > +{ > + struct device *dev = type_c->dev; > + unsigned long flags; > + void __iomem *reg; > + int val; > + > + spin_lock_irqsave(&type_c->lock, flags); > + > + /* set parameter */ > + reg = type_c->reg_base + USB_TYPEC_CTRL_CC1_0; > + val = readl(reg); > + val = (~CC_CODE_MASK & val) | (type_c->cc1_code & CC_CODE_MASK); > + writel(val, reg); > + > + reg = type_c->reg_base + USB_TYPEC_CTRL_CC2_0; > + val = readl(reg); > + val = (~CC_CODE_MASK & val) | (type_c->cc2_code & CC_CODE_MASK); > + > + reg = type_c->reg_base + USB_TYPEC_CTRL_CC1_1; > + writel(type_c->cc1_vref, reg); > + > + reg = type_c->reg_base + USB_TYPEC_CTRL_CC2_1; > + writel(type_c->cc2_vref, reg); > + > + reg = type_c->reg_base + USB_TYPEC_CTRL; > + val = readl(reg); > + val = (~DEBOUNCE_TIME_MASK & val) | (type_c->debounce & > DEBOUNCE_TIME_MASK); > + > + dev_info(dev, "First check USB_DR_MODE_PERIPHERAL"); > + type_c->cc_mode = IN_DEVICE_MODE; > + type_c->is_attach = IN_DETACH; > + type_c->connect_change = CONNECT_NO_CHANGE; > + > + detect_host(type_c); > + > + spin_unlock_irqrestore(&type_c->lock, flags); > + > + schedule_delayed_work(&type_c->delayed_work, msecs_to_jiffies(0)); > + > + if (!type_c->port) { > + struct typec_capability typec_cap = { }; > + struct fwnode_handle *fwnode; > + const char *buf; > + int ret; > + > + typec_cap.revision = USB_TYPEC_REV_1_0; > + typec_cap.prefer_role = TYPEC_NO_PREFERRED_ROLE; > + typec_cap.driver_data = type_c; > + typec_cap.ops = &type_c_port_ops; > + > + fwnode = device_get_named_child_node(dev, "connector"); > + if (!fwnode) > + return -EINVAL; > + > + ret = fwnode_property_read_string(fwnode, "power-role", &buf); > + if (ret) { > + dev_err(dev, "power-role not found: %d\n", ret); > + return ret; > + } > + > + ret = typec_find_port_power_role(buf); > + if (ret < 0) > + return ret; > + typec_cap.type = ret; > + > + ret = fwnode_property_read_string(fwnode, "data-role", &buf); > + if (ret) { > + dev_err(dev, "data-role not found: %d\n", ret); > + return ret; > + } > + > + ret = typec_find_port_data_role(buf); > + if (ret < 0) > + return ret; > + typec_cap.data = ret; > + > + type_c->port = typec_register_port(type_c->dev, &typec_cap); > + if (IS_ERR(type_c->port)) > + return PTR_ERR(type_c->port); > + } > + > + return 0; > +} > + > +static int extcon_rtk_type_c_edev_register(struct type_c_data *type_c) > +{ > + struct device *dev = type_c->dev; > + int ret = 0; > + > + type_c->edev = devm_extcon_dev_allocate(dev, usb_type_c_cable); > + if (IS_ERR(type_c->edev)) { > + dev_err(dev, "failed to allocate extcon device\n"); > + return -ENOMEM; > + } > + > + ret = devm_extcon_dev_register(dev, type_c->edev); > + if (ret < 0) { > + dev_err(dev, "failed to register extcon device\n"); > + return ret; > + } > + > + extcon_set_property_capability(type_c->edev, EXTCON_USB, > + EXTCON_PROP_USB_VBUS); > + extcon_set_property_capability(type_c->edev, EXTCON_USB, > + EXTCON_PROP_USB_TYPEC_POLARITY); > + extcon_set_property_capability(type_c->edev, EXTCON_USB, > + EXTCON_PROP_USB_SS); > + > + extcon_set_property_capability(type_c->edev, EXTCON_USB_HOST, > + EXTCON_PROP_USB_VBUS); > + extcon_set_property_capability(type_c->edev, EXTCON_USB_HOST, > + EXTCON_PROP_USB_TYPEC_POLARITY); > + extcon_set_property_capability(type_c->edev, EXTCON_USB_HOST, > + EXTCON_PROP_USB_SS); > + > + return ret; > +} > + > +static int extcon_rtk_type_c_probe(struct platform_device *pdev) > +{ > + struct device *dev = &pdev->dev; > + struct type_c_data *type_c; > + const struct type_c_cfg *type_c_cfg; > + int ret = 0; > + > + type_c = devm_kzalloc(dev, sizeof(*type_c), GFP_KERNEL); > + if (!type_c) > + return -ENOMEM; > + > + type_c->reg_base = devm_platform_ioremap_resource(pdev, 0); > + if (IS_ERR(type_c->reg_base)) > + return PTR_ERR(type_c->reg_base); > + > + type_c->dev = dev; > + > + type_c->irq = irq_of_parse_and_map(pdev->dev.of_node, 0); > + if (type_c->irq <= 0) { > + dev_err(&pdev->dev, "Type C driver with no IRQ. Check %s setup!\n", > + dev_name(&pdev->dev)); > + ret = -ENODEV; > + goto err; > + } > + > + ret = devm_request_irq(dev, type_c->irq, type_c_detect_irq, > + IRQF_SHARED, "type_c_detect", type_c); > + > + spin_lock_init(&type_c->lock); > + > + type_c->rd_ctrl_gpio_desc = NULL; > + if (soc_device_match(rtk_soc_kylin)) { > + struct gpio_desc *gpio; > + > + gpio = fwnode_gpiod_get_index(of_fwnode_handle(dev->of_node), > + "realtek,rd-ctrl-gpios", > + 0, GPIOD_OUT_HIGH, "rd-ctrl-gpio"); > + if (IS_ERR(gpio)) { > + dev_err(dev, "Error rd_ctrl-gpios no found (err=%d)\n", > + (int)PTR_ERR(gpio)); > + } else { > + type_c->rd_ctrl_gpio_desc = gpio; > + dev_dbg(dev, "%s get rd-ctrl-gpios (id=%d) OK\n", > + __func__, desc_to_gpio(gpio)); > + } > + } > + > + type_c_cfg = of_device_get_match_data(dev); > + if (!type_c_cfg) { > + dev_err(dev, "type_c config are not assigned!\n"); > + ret = -EINVAL; > + goto err; > + } > + > + type_c->type_c_cfg = devm_kzalloc(dev, sizeof(*type_c_cfg), > GFP_KERNEL); > + > + memcpy(type_c->type_c_cfg, type_c_cfg, sizeof(*type_c_cfg)); > + > + if (setup_type_c_parameter(type_c)) { > + dev_err(dev, "ERROR: %s to setup type c parameter!!", __func__); > + ret = -EINVAL; > + goto err; > + } > + > + INIT_DELAYED_WORK(&type_c->delayed_work, host_device_switch); > + > + ret = extcon_rtk_type_c_init(type_c); > + if (ret) { > + dev_err(dev, "%s failed to init type_c\n", __func__); > + goto err; > + } > + > + platform_set_drvdata(pdev, type_c); > + > + ret = extcon_rtk_type_c_edev_register(type_c); > + > + create_debug_files(type_c); > + > + return 0; > + > +err: > + dev_err(&pdev->dev, "%s: Probe fail, %d\n", __func__, ret); > + > + return ret; > +} > + > +static void extcon_rtk_type_c_remove(struct platform_device *pdev) > +{ > + struct device *dev = &pdev->dev; > + struct type_c_data *type_c = dev_get_drvdata(dev); > + u32 default_ctrl; > + unsigned long flags; > + > + remove_debug_files(type_c); > + > + if (type_c->port) { > + typec_unregister_port(type_c->port); > + type_c->port = NULL; > + } > + > + cancel_delayed_work_sync(&type_c->delayed_work); > + flush_delayed_work(&type_c->delayed_work); > + WARN_ON_ONCE(delayed_work_pending(&type_c->delayed_work)); > + > + spin_lock_irqsave(&type_c->lock, flags); > + /* disable interrupt */ > + default_ctrl = readl(type_c->reg_base + USB_TYPEC_CTRL) & > + DEBOUNCE_TIME_MASK; > + writel(default_ctrl, type_c->reg_base + USB_TYPEC_CTRL); > + > + /* disable cc detect, rp, rd */ > + writel(PLR_EN, type_c->reg_base + USB_TYPEC_CTRL_CC1_0); > + writel(0, type_c->reg_base + USB_TYPEC_CTRL_CC2_0); > + > + spin_unlock_irqrestore(&type_c->lock, flags); > + > + if (type_c->rd_ctrl_gpio_desc) > + gpiod_put(type_c->rd_ctrl_gpio_desc); > + type_c->rd_ctrl_gpio_desc = NULL; > + > + free_irq(type_c->irq, type_c); > +} > + > +static const struct type_c_cfg rtd1295_type_c_cfg = { > + .parameter_ver = PARAMETER_V0, > + .cc_dfp_mode = CC_MODE_DFP_3_0, > + .cc1_param = { .rp_4p7k_code = 0xb, > + .rp_36k_code = 0x17, > + .rp_12k_code = 0x10, > + .rd_code = 0, > + .ra_code = 0, > + .vref_2p6v = 0x0, > + .vref_1p23v = 0x0, > + .vref_0p8v = 0x3, > + .vref_0p66v = 0x0, > + .vref_0p4v = 0x0, > + .vref_0p2v = 0x4, > + .vref_1_1p6v = 0, > + .vref_0_1p6v = 0 }, > + .cc2_param = { .rp_4p7k_code = 0xc, > + .rp_36k_code = 0x17, > + .rp_12k_code = 0x12, > + .rd_code = 0, > + .ra_code = 0, > + .vref_2p6v = 0x2, > + .vref_1p23v = 0x0, > + .vref_0p8v = 0x3, > + .vref_0p66v = 0x0, > + .vref_0p4v = 0x0, > + .vref_0p2v = 0x5, > + .vref_1_1p6v = 0, > + .vref_0_1p6v = 0 }, > + .debounce_val = 0x7f, /* 1b,1us 7f,4.7us */ > + .use_defalut_parameter = false, > +}; > + > +static const struct type_c_cfg rtd1395_type_c_cfg = { > + .parameter_ver = PARAMETER_V0, > + .cc_dfp_mode = CC_MODE_DFP_3_0, > + .cc1_param = { .rp_4p7k_code = 0xc, > + .rp_36k_code = 0xb, > + .rp_12k_code = 0xe, > + .rd_code = 0x10, > + .ra_code = 0x0, > + .vref_2p6v = 0x0, > + .vref_1p23v = 0x1, > + .vref_0p8v = 0x0, > + .vref_0p66v = 0x0, > + .vref_0p4v = 0x3, > + .vref_0p2v = 0x0, > + .vref_1_1p6v = 0x7, > + .vref_0_1p6v = 0x7 }, > + .cc2_param = { .rp_4p7k_code = 0xb, > + .rp_36k_code = 0x9, > + .rp_12k_code = 0xe, > + .rd_code = 0xf, > + .ra_code = 0x0, > + .vref_2p6v = 0x1, > + .vref_1p23v = 0x3, > + .vref_0p8v = 0x3, > + .vref_0p66v = 0x2, > + .vref_0p4v = 0x3, > + .vref_0p2v = 0x2, > + .vref_1_1p6v = 0x7, > + .vref_0_1p6v = 0x7 }, > + .debounce_val = 0x7f, /* 1b,1us 7f,4.7us */ > + .use_defalut_parameter = false, > +}; > + > +static const struct type_c_cfg rtd1619_type_c_cfg = { > + .parameter_ver = PARAMETER_V0, > + .cc_dfp_mode = CC_MODE_DFP_3_0, > + .cc1_param = { .rp_4p7k_code = 0xc, > + .rp_36k_code = 0xf, > + .rp_12k_code = 0xe, > + .rd_code = 0x11, > + .ra_code = 0x0, > + .vref_2p6v = 0x5, > + .vref_1p23v = 0x7, > + .vref_0p8v = 0xa, > + .vref_0p66v = 0xa, > + .vref_0p4v = 0x3, > + .vref_0p2v = 0x2, > + .vref_1_1p6v = 0x7, > + .vref_0_1p6v = 0x7 }, > + .cc2_param = { .rp_4p7k_code = 0xc, > + .rp_36k_code = 0xf, > + .rp_12k_code = 0xe, > + .rd_code = 0xf, > + .ra_code = 0x0, > + .vref_2p6v = 0x5, > + .vref_1p23v = 0x8, > + .vref_0p8v = 0xa, > + .vref_0p66v = 0xa, > + .vref_0p4v = 0x3, > + .vref_0p2v = 0x2, > + .vref_1_1p6v = 0x7, > + .vref_0_1p6v = 0x7 }, > + .debounce_val = 0x7f, /* 1b,1us 7f,4.7us */ > + .use_defalut_parameter = false, > +}; > + > +static const struct type_c_cfg rtd1319_type_c_cfg = { > + .parameter_ver = PARAMETER_V0, > + .cc_dfp_mode = CC_MODE_DFP_1_5, > + .cc1_param = { .rp_4p7k_code = 0x9, > + .rp_36k_code = 0xe, > + .rp_12k_code = 0x9, > + .rd_code = 0x9, > + .ra_code = 0x7, > + .vref_2p6v = 0x3, > + .vref_1p23v = 0x7, > + .vref_0p8v = 0x7, > + .vref_0p66v = 0x6, > + .vref_0p4v = 0x2, > + .vref_0p2v = 0x3, > + .vref_1_1p6v = 0x4, > + .vref_0_1p6v = 0x7 }, > + .cc2_param = { .rp_4p7k_code = 0x8, > + .rp_36k_code = 0xe, > + .rp_12k_code = 0x9, > + .rd_code = 0x9, > + .ra_code = 0x7, > + .vref_2p6v = 0x3, > + .vref_1p23v = 0x7, > + .vref_0p8v = 0x7, > + .vref_0p66v = 0x6, > + .vref_0p4v = 0x3, > + .vref_0p2v = 0x3, > + .vref_1_1p6v = 0x6, > + .vref_0_1p6v = 0x7 }, > + .debounce_val = 0x7f, /* 1b,1us 7f,4.7us */ > + .use_defalut_parameter = false, > +}; > + > +static const struct type_c_cfg rtd1312c_type_c_cfg = { > + .parameter_ver = PARAMETER_V0, > + .cc_dfp_mode = CC_MODE_DFP_1_5, > + .cc1_param = { .rp_4p7k_code = 0xe, > + .rp_36k_code = 0xc, > + .rp_12k_code = 0xc, > + .rd_code = 0xa, > + .ra_code = 0x3, > + .vref_2p6v = 0xa, > + .vref_1p23v = 0x7, > + .vref_0p8v = 0x7, > + .vref_0p66v = 0x7, > + .vref_0p4v = 0x4, > + .vref_0p2v = 0x4, > + .vref_1_1p6v = 0x7, > + .vref_0_1p6v = 0x7 }, > + .cc2_param = { .rp_4p7k_code = 0xe, > + .rp_36k_code = 0xc, > + .rp_12k_code = 0xc, > + .rd_code = 0xa, > + .ra_code = 0x3, > + .vref_2p6v = 0xa, > + .vref_1p23v = 0x7, > + .vref_0p8v = 0x7, > + .vref_0p66v = 0x7, > + .vref_0p4v = 0x4, > + .vref_0p2v = 0x4, > + .vref_1_1p6v = 0x7, > + .vref_0_1p6v = 0x7 }, > + .debounce_val = 0x7f, /* 1b,1us 7f,4.7us */ > + .use_defalut_parameter = false, > +}; > + > +static const struct type_c_cfg rtd1619b_type_c_cfg = { > + .parameter_ver = PARAMETER_V1, > + .cc_dfp_mode = CC_MODE_DFP_1_5, > + .cc1_param = { .rp_4p7k_code = 0xf, > + .rp_36k_code = 0xf, > + .rp_12k_code = 0xf, > + .rd_code = 0xf, > + .ra_code = 0x7, > + .vref_2p6v = 0x9, > + .vref_1p23v = 0x7, > + .vref_0p8v = 0x9, > + .vref_0p66v = 0x8, > + .vref_0p4v = 0x7, > + .vref_0p2v = 0x9, > + .vref_1_1p6v = 0x7, > + .vref_0_1p6v = 0x7 }, > + .cc2_param = { .rp_4p7k_code = 0xf, > + .rp_36k_code = 0xf, > + .rp_12k_code = 0xf, > + .rd_code = 0xf, > + .ra_code = 0x7, > + .vref_1p23v = 0x7, > + .vref_0p8v = 0x9, > + .vref_0p66v = 0x8, > + .vref_0p4v = 0x7, > + .vref_0p2v = 0x8, > + .vref_1_1p6v = 0x7, > + .vref_0_1p6v = 0x7 }, > + .debounce_val = 0x7f, /* 1b,1us 7f,4.7us */ > + .use_defalut_parameter = false, > +}; > + > +static const struct type_c_cfg rtd1319d_type_c_cfg = { > + .parameter_ver = PARAMETER_V1, > + .cc_dfp_mode = CC_MODE_DFP_1_5, > + .cc1_param = { .rp_4p7k_code = 0xe, > + .rp_36k_code = 0x3, > + .rp_12k_code = 0xe, > + .rd_code = 0xf, > + .ra_code = 0x6, > + .vref_2p6v = 0x7, > + .vref_1p23v = 0x7, > + .vref_0p8v = 0x8, > + .vref_0p66v = 0x7, > + .vref_0p4v = 0x7, > + .vref_0p2v = 0x7, > + .vref_1_1p6v = 0x7, > + .vref_0_1p6v = 0x7 }, > + .cc2_param = { .rp_4p7k_code = 0xe, > + .rp_36k_code = 0x3, > + .rp_12k_code = 0xe, > + .rd_code = 0xf, > + .ra_code = 0x6, > + .vref_2p6v = 0x7, > + .vref_1p23v = 0x7, > + .vref_0p8v = 0x8, > + .vref_0p66v = 0x7, > + .vref_0p4v = 0x7, > + .vref_0p2v = 0x8, > + .vref_1_1p6v = 0x7, > + .vref_0_1p6v = 0x7 }, > + .debounce_val = 0x7f, /* 1b,1us 7f,4.7us */ > + .use_defalut_parameter = false, > +}; > + > +static const struct type_c_cfg rtd1315e_type_c_cfg = { > + .parameter_ver = PARAMETER_V1, > + .cc_dfp_mode = CC_MODE_DFP_1_5, > + .cc1_param = { .rp_4p7k_code = 0xe, > + .rp_36k_code = 0x3, > + .rp_12k_code = 0xe, > + .rd_code = 0xf, > + .ra_code = 0x6, > + .vref_2p6v = 0x7, > + .vref_1p23v = 0x7, > + .vref_0p8v = 0x8, > + .vref_0p66v = 0x7, > + .vref_0p4v = 0x7, > + .vref_0p2v = 0x7, > + .vref_1_1p6v = 0x7, > + .vref_0_1p6v = 0x7 }, > + .cc2_param = { .rp_4p7k_code = 0xe, > + .rp_36k_code = 0x3, > + .rp_12k_code = 0xe, > + .rd_code = 0xf, > + .ra_code = 0x6, > + .vref_2p6v = 0x7, > + .vref_1p23v = 0x7, > + .vref_0p8v = 0x8, > + .vref_0p66v = 0x7, > + .vref_0p4v = 0x7, > + .vref_0p2v = 0x8, > + .vref_1_1p6v = 0x7, > + .vref_0_1p6v = 0x7 }, > + .debounce_val = 0x7f, /* 1b,1us 7f,4.7us */ > + .use_defalut_parameter = false, > +}; > + > +static const struct of_device_id extcon_rtk_type_c_match[] = { > + { .compatible = "realtek,rtd1295-type-c", .data = &rtd1295_type_c_cfg }, > + { .compatible = "realtek,rtd1312c-type-c", .data = > &rtd1312c_type_c_cfg }, > + { .compatible = "realtek,rtd1315e-type-c", .data = > &rtd1315e_type_c_cfg }, > + { .compatible = "realtek,rtd1319-type-c", .data = &rtd1319_type_c_cfg }, > + { .compatible = "realtek,rtd1319d-type-c", .data = > &rtd1319d_type_c_cfg }, > + { .compatible = "realtek,rtd1395-type-c", .data = &rtd1395_type_c_cfg }, > + { .compatible = "realtek,rtd1619-type-c", .data = &rtd1619_type_c_cfg }, > + { .compatible = "realtek,rtd1619b-type-c", .data = > &rtd1619b_type_c_cfg }, > + {}, > +}; > +MODULE_DEVICE_TABLE(of, extcon_rtk_type_c_match); > + > +#ifdef CONFIG_PM_SLEEP > +static int extcon_rtk_type_c_prepare(struct device *dev) > +{ > + struct type_c_data *type_c = dev_get_drvdata(dev); > + u32 default_ctrl; > + unsigned long flags; > + > + cancel_delayed_work_sync(&type_c->delayed_work); > + flush_delayed_work(&type_c->delayed_work); > + WARN_ON_ONCE(delayed_work_pending(&type_c->delayed_work)); > + > + spin_lock_irqsave(&type_c->lock, flags); > + /* disable interrupt */ > + default_ctrl = readl(type_c->reg_base + USB_TYPEC_CTRL) & > + DEBOUNCE_TIME_MASK; > + writel(default_ctrl, type_c->reg_base + USB_TYPEC_CTRL); > + > + /* disable cc detect, rp, rd */ > + writel(PLR_EN, type_c->reg_base + USB_TYPEC_CTRL_CC1_0); > + writel(0, type_c->reg_base + USB_TYPEC_CTRL_CC2_0); > + > + spin_unlock_irqrestore(&type_c->lock, flags); > + > + return 0; > +} > + > +static void extcon_rtk_type_c_complete(struct device *dev) > +{ > + /* nothing */ > +} > + > +static int extcon_rtk_type_c_suspend(struct device *dev) > +{ > + /* nothing */ > + > + return 0; > +} > + > +static int extcon_rtk_type_c_resume(struct device *dev) > +{ > + struct type_c_data *type_c = dev_get_drvdata(dev); > + int ret; > + > + ret = extcon_rtk_type_c_init(type_c); > + if (ret) { > + dev_err(dev, "%s failed to init type_c\n", __func__); > + return ret; > + } > + > + return 0; > +} > + > +static const struct dev_pm_ops extcon_rtk_type_c_pm_ops = { > + SET_LATE_SYSTEM_SLEEP_PM_OPS(extcon_rtk_type_c_suspend, > extcon_rtk_type_c_resume) > + .prepare = extcon_rtk_type_c_prepare, > + .complete = extcon_rtk_type_c_complete, > +}; > + > +#define DEV_PM_OPS (&extcon_rtk_type_c_pm_ops) > +#else > +#define DEV_PM_OPS NULL > +#endif /* CONFIG_PM_SLEEP */ > + > +static struct platform_driver extcon_rtk_type_c_driver = { > + .probe = extcon_rtk_type_c_probe, > + .remove_new = extcon_rtk_type_c_remove, > + .driver = { > + .name = "extcon-rtk-type_c", > + .of_match_table = extcon_rtk_type_c_match, > + .pm = DEV_PM_OPS, > + }, > +}; > + > +module_platform_driver(extcon_rtk_type_c_driver); > + > +MODULE_DESCRIPTION("Realtek Extcon Type C driver"); > +MODULE_AUTHOR("Stanley Chang <stanley_chang@realtek.com>"); > +MODULE_LICENSE("GPL"); > -- > 2.34.1
Hi Stanley, Applied them with patch1/2. I'm sorry for late reply. Regards, Chanwoo Choi On Mon, Sep 18, 2023 at 3:41 PM Stanley Chang[昌育德] <stanley_chang@realtek.com> wrote: > > Hi Chanwoo, > > Can you help review this patch? > > Thanks, > Stanley > > > -----Original Message----- > > From: Stanley Chang <stanley_chang@realtek.com> > > Sent: Monday, September 4, 2023 1:13 PM > > To: MyungJoo Ham <myungjoo.ham@samsung.com> > > Cc: Stanley Chang[昌育德] <stanley_chang@realtek.com>; Greg > > Kroah-Hartman <gregkh@linuxfoundation.org>; Rob Herring > > <robh+dt@kernel.org>; Krzysztof Kozlowski > > <krzysztof.kozlowski+dt@linaro.org>; Conor Dooley <conor+dt@kernel.org>; > > Chanwoo Choi <cw00.choi@samsung.com>; linux-usb@vger.kernel.org; > > devicetree@vger.kernel.org; linux-kernel@vger.kernel.org > > Subject: [PATCH v3 1/2] extcon: add Realtek DHC RTD SoC Type-C driver > > > > This patch adds the extcon driver for Realtek DHC (digital home center) > > RTD SoCs type-c module. This can be used to detect whether the port is > > configured as a downstream or upstream facing port. And notify the status > > of extcon to listeners. > > > > Signed-off-by: Stanley Chang <stanley_chang@realtek.com> > > --- > > v2 to v3 change: > > removed the error check for debugfs > > v1 to v2 change: > > 1. added "depends on TYPEC" and "select USB_COMMON" in Kconfig > > 2. revised the code about gpio. > > 3. revised the definitions to keep the left-aligned of value > > 4. add the comment for delay or sleep > > 5. changed some functions to connector_attached/connector_detached > > 6. removed to check 'CONFIG_TYPEC' definition with ifdef > > --- > > drivers/extcon/Kconfig | 11 + > > drivers/extcon/Makefile | 1 + > > drivers/extcon/extcon-rtk-type-c.c | 1792 ++++++++++++++++++++++++++++ > > 3 files changed, 1804 insertions(+) > > create mode 100644 drivers/extcon/extcon-rtk-type-c.c > > > > diff --git a/drivers/extcon/Kconfig b/drivers/extcon/Kconfig > > index 0ef1971d22bb..0f4c061e7321 100644 > > --- a/drivers/extcon/Kconfig > > +++ b/drivers/extcon/Kconfig > > @@ -190,4 +190,15 @@ config EXTCON_USBC_TUSB320 > > Say Y here to enable support for USB Type C cable detection extcon > > support using a TUSB320. > > > > +config EXTCON_RTK_TYPE_C > > + tristate "Realtek RTD SoC extcon Type-C Driver" > > + depends on ARCH_REALTEK || COMPILE_TEST > > + depends on TYPEC > > + select USB_COMMON > > + help > > + Say Y here to enable extcon support for USB Type C cable detection > > + when using the Realtek RTD SoC USB Type-C port. > > + The DHC (Digital Home Hub) RTD series SoC contains a type c module. > > + This driver will detect the status of the type-c port. > > + > > endif > > diff --git a/drivers/extcon/Makefile b/drivers/extcon/Makefile > > index 1b390d934ca9..f779adb5e4c7 100644 > > --- a/drivers/extcon/Makefile > > +++ b/drivers/extcon/Makefile > > @@ -25,3 +25,4 @@ obj-$(CONFIG_EXTCON_SM5502) += > > extcon-sm5502.o > > obj-$(CONFIG_EXTCON_USB_GPIO) += extcon-usb-gpio.o > > obj-$(CONFIG_EXTCON_USBC_CROS_EC) += extcon-usbc-cros-ec.o > > obj-$(CONFIG_EXTCON_USBC_TUSB320) += extcon-usbc-tusb320.o > > +obj-$(CONFIG_EXTCON_RTK_TYPE_C) += extcon-rtk-type-c.o > > diff --git a/drivers/extcon/extcon-rtk-type-c.c > > b/drivers/extcon/extcon-rtk-type-c.c > > new file mode 100644 > > index 000000000000..00465cfba23e > > --- /dev/null > > +++ b/drivers/extcon/extcon-rtk-type-c.c > > @@ -0,0 +1,1792 @@ > > +// SPDX-License-Identifier: GPL-2.0 > > +/* > > + * * extcon-rtk-type-c.c - Realtek Extcon Type C driver > > + * > > + * Copyright (C) 2023 Realtek Semiconductor Corporation > > + * > > + */ > > + > > +#include <linux/module.h> > > +#include <linux/kernel.h> > > +#include <linux/slab.h> > > +#include <linux/platform_device.h> > > +#include <linux/of.h> > > +#include <linux/of_address.h> > > +#include <linux/of_irq.h> > > +#include <linux/of_gpio.h> > > +#include <linux/io.h> > > +#include <linux/interrupt.h> > > +#include <linux/syscalls.h> > > +#include <linux/suspend.h> > > +#include <linux/debugfs.h> > > +#include <linux/extcon.h> > > +#include <linux/extcon-provider.h> > > +#include <linux/sys_soc.h> > > +#include <linux/nvmem-consumer.h> > > +#include <linux/gpio/consumer.h> > > +#include <linux/usb/otg.h> > > +#include <linux/usb/typec.h> > > + > > +struct cc_param { > > + u32 rp_4p7k_code; > > + u32 rp_36k_code; > > + u32 rp_12k_code; > > + u32 rd_code; > > + u32 ra_code; > > + u32 vref_2p6v; > > + u32 vref_1p23v; > > + u32 vref_0p8v; > > + u32 vref_0p66v; > > + u32 vref_0p4v; > > + u32 vref_0p2v; > > + u32 vref_1_1p6v; > > + u32 vref_0_1p6v; > > +}; > > + > > +struct type_c_cfg { > > + int parameter_ver; /* Parameter version */ > > + int cc_dfp_mode; > > + struct cc_param cc1_param; > > + struct cc_param cc2_param; > > + > > + u32 debounce_val; > > + bool use_defalut_parameter; > > +}; > > + > > +struct type_c_data { > > + void __iomem *reg_base; > > + struct device *dev; > > + struct extcon_dev *edev; > > + > > + u32 irq; > > + > > + /* rd control GPIO only for rtd1295 */ > > + struct gpio_desc *rd_ctrl_gpio_desc; > > + > > + /* Parameters */ > > + struct type_c_cfg *type_c_cfg; > > + u32 dfp_mode_rp_en; > > + u32 ufp_mode_rd_en; > > + u32 cc1_code; > > + u32 cc2_code; > > + u32 cc1_vref; > > + u32 cc2_vref; > > + u32 debounce; /* 1b,1us 7f,4.7us */ > > + > > + /* type_c state */ > > + int connect_change; > > +#define CONNECT_CHANGE 1 > > +#define CONNECT_NO_CHANGE 0 > > + int cc_mode; /* cc is host or device */ > > +#define IN_HOST_MODE 0x10 > > +#define IN_DEVICE_MODE 0x20 > > + int is_attach; > > +#define IN_ATTACH 1 > > +#define TO_ATTACH 1 > > +#define IN_DETACH 0 > > +#define TO_DETACH 0 > > + int at_cc1; > > +#define AT_CC1 1 > > +#define AT_CC2 0 > > + > > + u32 int_status; > > + u32 cc_status; > > + /* protect the data member */ > > + spinlock_t lock; > > + struct delayed_work delayed_work; > > + > > + bool rd_en_at_first; > > + > > + struct dentry *debug_dir; > > + > > + struct typec_port *port; > > +}; > > + > > +/* Type C register offset */ > > +#define USB_TYPEC_CTRL_CC1_0 0x0 > > +#define USB_TYPEC_CTRL_CC1_1 0x4 > > +#define USB_TYPEC_CTRL_CC2_0 0x8 > > +#define USB_TYPEC_CTRL_CC2_1 0xC > > +#define USB_TYPEC_STS 0x10 > > +#define USB_TYPEC_CTRL 0x14 > > +#define USB_DBUS_PWR_CTRL 0x18 > > + > > +#define ENABLE_CC1 0x1 > > +#define ENABLE_CC2 0x2 > > +#define DISABLE_CC 0x0 > > + > > +/* Bit mapping USB_TYPEC_CTRL_CC1_0 and USB_TYPEC_CTRL_CC2_0 */ > > +#define PLR_EN BIT(29) > > +#define CC_SWITCH_MASK (BIT(29) | BIT(28) | BIT(27)) > > +#define CC_CODE_MASK (0xfffff << 7) > > +#define rp4pk_code(val) ((0x1f & (val)) << 22) > > +#define code_rp4pk(val) (((val) >> 22) & 0x1f) > > +#define rp36k_code(val) ((0x1f & (val)) << 17) > > +#define code_rp36k(val) (((val) >> 17) & 0x1f) > > +#define rp12k_code(val) ((0x1f & (val)) << 12) > > +#define code_rp12k(val) (((val) >> 12) & 0x1f) > > +#define rd_code(val) ((0x1f & (val)) << 7) > > +#define code_rd(val) (((val) >> 7) & 0x1f) > > +#define dfp_mode(val) ((0x3 & (val)) << 5) > > +#define EN_RP4P7K BIT(4) > > +#define EN_RP36K BIT(3) > > +#define EN_RP12K BIT(2) > > +#define EN_RD BIT(1) > > +#define EN_CC_DET BIT(0) > > + > > +#define CC_MODE_UFP 0x0 > > +#define CC_MODE_DFP_USB 0x1 > > +#define CC_MODE_DFP_1_5 0x2 > > +#define CC_MODE_DFP_3_0 0x3 > > + > > +/* > > + * PARAMETER_V0: > > + * Realtek Kylin rtd1295 > > + * Realtek Hercules rtd1395 > > + * Realtek Thor rtd1619 > > + * Realtek Hank rtd1319 > > + * Realtek Groot rtd1312c > > + * PARAMETER_V1: > > + * Realtek Stark rtd1619b > > + * Realtek Parker rtd1319d > > + * Realtek Danvers rtd1315e > > + */ > > +enum parameter_version { > > + PARAMETER_V0 = 0, > > + PARAMETER_V1 = 1, > > +}; > > + > > +/* Bit mapping USB_TYPEC_CTRL_CC1_1 and USB_TYPEC_CTRL_CC2_1 */ > > +#define V0_vref_2p6v(val) ((0xf & (val)) << 26) /* Bit 29 for groot */ > > +#define V0_vref_1p23v(val) ((0xf & (val)) << 22) > > +#define V0_vref_0p8v(val) ((0xf & (val)) << 18) > > +#define V0_vref_0p66v(val) ((0xf & (val)) << 14) > > +#define V0_vref_0p4v(val) ((0x7 & (val)) << 11) > > +#define V0_vref_0p2v(val) ((0x7 & (val)) << 8) > > +#define V0_vref_1_1p6v(val) ((0xf & (val)) << 4) > > +#define V0_vref_0_1p6v(val) ((0xf & (val)) << 0) > > + > > +#define V0_decode_2p6v(val) (((val) >> 26) & 0xf) /* Bit 29 for groot */ > > +#define V0_decode_1p23v(val) (((val) >> 22) & 0xf) > > +#define V0_decode_0p8v(val) (((val) >> 18) & 0xf) > > +#define V0_decode_0p66v(val) (((val) >> 14) & 0xf) > > +#define V0_decode_0p4v(val) (((val) >> 11) & 0x7) > > +#define V0_decode_0p2v(val) (((val) >> 8) & 0x7) > > +#define V0_decode_1_1p6v(val) (((val) >> 4) & 0xf) > > +#define V0_decode_0_1p6v(val) (((val) >> 0) & 0xf) > > + > > +/* new Bit mapping USB_TYPEC_CTRL_CC1_1 and USB_TYPEC_CTRL_CC2_1 > > */ > > +#define V1_vref_2p6v(val) ((0xf & (val)) << 28) > > +#define V1_vref_1p23v(val) ((0xf & (val)) << 24) > > +#define V1_vref_0p8v(val) ((0xf & (val)) << 20) > > +#define V1_vref_0p66v(val) ((0xf & (val)) << 16) > > +#define V1_vref_0p4v(val) ((0xf & (val)) << 12) > > +#define V1_vref_0p2v(val) ((0xf & (val)) << 8) > > +#define V1_vref_1_1p6v(val) ((0xf & (val)) << 4) > > +#define V1_vref_0_1p6v(val) ((0xf & (val)) << 0) > > + > > +#define V1_decode_2p6v(val) (((val) >> 28) & 0xf) > > +#define V1_decode_1p23v(val) (((val) >> 24) & 0xf) > > +#define V1_decode_0p8v(val) (((val) >> 20) & 0xf) > > +#define V1_decode_0p66v(val) (((val) >> 16) & 0xf) > > +#define V1_decode_0p4v(val) (((val) >> 12) & 0xf) > > +#define V1_decode_0p2v(val) (((val) >> 8) & 0xf) > > +#define V1_decode_1_1p6v(val) (((val) >> 4) & 0xf) > > +#define V1_decode_0_1p6v(val) (((val) >> 0) & 0xf) > > + > > +/* Bit mapping USB_TYPEC_STS */ > > +#define DET_STS 0x7 > > +#define CC1_DET_STS (DET_STS) > > +#define CC2_DET_STS (DET_STS << 3) > > +#define DET_STS_RA 0x1 > > +#define DET_STS_RD 0x3 > > +#define DET_STS_RP 0x1 > > +#define CC1_DET_STS_RA (DET_STS_RA) > > +#define CC1_DET_STS_RD (DET_STS_RD) > > +#define CC1_DET_STS_RP (DET_STS_RP) > > +#define CC2_DET_STS_RA (DET_STS_RA << 3) > > +#define CC2_DET_STS_RD (DET_STS_RD << 3) > > +#define CC2_DET_STS_RP (DET_STS_RP << 3) > > + > > +/* Bit mapping USB_TYPEC_CTRL */ > > +#define CC2_INT_EN BIT(11) > > +#define CC1_INT_EN BIT(10) > > +#define CC2_INT_STS BIT(9) > > +#define CC1_INT_STS BIT(8) > > +#define DEBOUNCE_TIME_MASK 0xff > > +#define DEBOUNCE_EN BIT(0) > > +#define ENABLE_TYPE_C_DETECT (CC1_INT_EN | CC2_INT_EN) > > +#define ALL_CC_INT_STS (CC1_INT_STS | CC2_INT_STS) > > + > > +/* Parameter */ > > +#define DETECT_TIME 50 /* ms */ > > + > > +static const unsigned int usb_type_c_cable[] = { > > + EXTCON_USB, > > + EXTCON_USB_HOST, > > + EXTCON_NONE, > > +}; > > + > > +enum usb_data_roles { > > + DR_NONE, > > + DR_HOST, > > + DR_DEVICE, > > +}; > > + > > +static const struct soc_device_attribute rtk_soc_kylin[] = { > > + { .family = "Realtek Kylin", }, > > + { /* empty */ } > > +}; > > + > > +static int rtd129x_switch_type_c_plug_config(struct type_c_data *type_c, > > + int dr_mode, int cc) > > +{ > > + void __iomem *reg = type_c->reg_base + USB_TYPEC_CTRL_CC1_0; > > + int val_cc; > > + > > +#define TYPE_C_EN_SWITCH BIT(29) > > +#define TYPE_C_TXRX_SEL (BIT(28) | BIT(27)) > > +#define TYPE_C_SWITCH_MASK (TYPE_C_EN_SWITCH | TYPE_C_TXRX_SEL) > > +#define TYPE_C_ENABLE_CC1 TYPE_C_EN_SWITCH > > +#define TYPE_C_ENABLE_CC2 (TYPE_C_EN_SWITCH | TYPE_C_TXRX_SEL) > > +#define TYPE_C_DISABLE_CC ~TYPE_C_SWITCH_MASK > > + > > + val_cc = readl(reg); > > + val_cc &= ~TYPE_C_SWITCH_MASK; > > + > > + if (cc == DISABLE_CC) { > > + val_cc &= TYPE_C_DISABLE_CC; > > + } else if (cc == ENABLE_CC1) { > > + val_cc |= TYPE_C_ENABLE_CC1; > > + } else if (cc == ENABLE_CC2) { > > + val_cc |= TYPE_C_ENABLE_CC2; > > + } else { > > + dev_err(type_c->dev, "%s: Error cc setting cc=0x%x\n", __func__, > > cc); > > + return -EINVAL; > > + } > > + writel(val_cc, reg); > > + > > + /* waiting cc stable for enable/disable */ > > + mdelay(1); > > + > > + dev_dbg(type_c->dev, "%s: cc=0x%x val_cc=0x%x > > usb_typec_ctrl_cc1_0=0x%x\n", > > + __func__, cc, val_cc, readl(reg)); > > + > > + return 0; > > +} > > + > > +static inline void switch_type_c_plug_config(struct type_c_data *type_c, > > + int dr_mode, int cc) > > +{ > > + int ret = 0; > > + > > + if (soc_device_match(rtk_soc_kylin)) > > + ret = rtd129x_switch_type_c_plug_config(type_c, dr_mode, cc); > > + > > + if (ret < 0) > > + dev_err(type_c->dev, "%s: Error set type c plug config\n", > > + __func__); > > +} > > + > > +static void switch_type_c_dr_mode(struct type_c_data *type_c, int dr_mode, > > int cc) > > +{ > > + bool is_host = false; > > + bool is_device = false; > > + bool polarity = false; > > + bool vbus = false; > > + bool ss = true; > > + > > + switch_type_c_plug_config(type_c, dr_mode, cc); > > + if (cc == ENABLE_CC2) > > + polarity = true; > > + > > + switch (dr_mode) { > > + case USB_DR_MODE_HOST: > > + is_host = true; > > + break; > > + case USB_DR_MODE_PERIPHERAL: > > + is_device = true; > > + vbus = true; > > + break; > > + default: > > + dev_dbg(type_c->dev, "%s dr_mode=%d ==> no host or device\n", > > + __func__, dr_mode); > > + break; > > + } > > + > > + dev_dbg(type_c->dev, "%s is_host=%d is_device=%d vbus=%d > > polarity=%d\n", > > + __func__, is_host, is_device, vbus, polarity); > > + > > + /* for EXTCON_USB device mode */ > > + extcon_set_state(type_c->edev, EXTCON_USB, is_device); > > + extcon_set_property(type_c->edev, EXTCON_USB, > > + EXTCON_PROP_USB_VBUS, > > + (union extcon_property_value)(int)vbus); > > + extcon_set_property(type_c->edev, EXTCON_USB, > > + EXTCON_PROP_USB_TYPEC_POLARITY, > > + (union extcon_property_value)(int)polarity); > > + extcon_set_property(type_c->edev, EXTCON_USB, > > + EXTCON_PROP_USB_SS, > > + (union extcon_property_value)(int)ss); > > + > > + /* for EXTCON_USB_HOST host mode */ > > + extcon_set_state(type_c->edev, EXTCON_USB_HOST, is_host); > > + extcon_set_property(type_c->edev, EXTCON_USB_HOST, > > + EXTCON_PROP_USB_VBUS, > > + (union extcon_property_value)(int)vbus); > > + extcon_set_property(type_c->edev, EXTCON_USB_HOST, > > + EXTCON_PROP_USB_TYPEC_POLARITY, > > + (union extcon_property_value)(int)polarity); > > + extcon_set_property(type_c->edev, EXTCON_USB_HOST, > > + EXTCON_PROP_USB_SS, > > + (union extcon_property_value)(int)ss); > > + > > + /* sync EXTCON_USB and EXTCON_USB_HOST */ > > + extcon_sync(type_c->edev, EXTCON_USB); > > + extcon_sync(type_c->edev, EXTCON_USB_HOST); > > + > > + if (type_c->port) { > > + switch (dr_mode) { > > + case USB_DR_MODE_HOST: > > + typec_set_data_role(type_c->port, TYPEC_HOST); > > + typec_set_pwr_role(type_c->port, TYPEC_SOURCE); > > + break; > > + case USB_DR_MODE_PERIPHERAL: > > + typec_set_data_role(type_c->port, TYPEC_DEVICE); > > + typec_set_pwr_role(type_c->port, TYPEC_SINK); > > + break; > > + default: > > + dev_dbg(type_c->dev, "%s unknown dr_mode=%d\n", > > + __func__, dr_mode); > > + break; > > + } > > + } > > +} > > + > > +/* connector attached/detached */ > > +static int connector_attached(struct type_c_data *type_c, u32 cc, int > > dr_mode) > > +{ > > + void __iomem *reg = type_c->reg_base + USB_TYPEC_CTRL; > > + > > + cancel_delayed_work(&type_c->delayed_work); > > + > > + switch_type_c_dr_mode(type_c, dr_mode, cc); > > + > > + writel(ENABLE_TYPE_C_DETECT | readl(reg), reg); > > + > > + return 0; > > +} > > + > > +static int connector_detached(struct type_c_data *type_c, u32 cc, int > > dr_mode) > > +{ > > + void __iomem *reg = type_c->reg_base + USB_TYPEC_CTRL; > > + > > + writel(~ENABLE_TYPE_C_DETECT & readl(reg), reg); > > + > > + switch_type_c_dr_mode(type_c, 0, cc); > > + > > + schedule_delayed_work(&type_c->delayed_work, > > msecs_to_jiffies(DETECT_TIME)); > > + > > + return 0; > > +} > > + > > +/* detect host device switch */ > > +static int __detect_host_device(struct type_c_data *type_c, u32 rp_or_rd_en) > > +{ > > + struct device *dev = type_c->dev; > > + void __iomem *reg_base = type_c->reg_base; > > + u32 cc1_config, cc2_config, default_ctrl; > > + u32 cc1_switch = 0; > > + > > + default_ctrl = readl(reg_base + USB_TYPEC_CTRL) & > > DEBOUNCE_TIME_MASK; > > + writel(default_ctrl, reg_base + USB_TYPEC_CTRL); > > + > > + cc1_config = readl(reg_base + USB_TYPEC_CTRL_CC1_0); > > + cc2_config = readl(reg_base + USB_TYPEC_CTRL_CC2_0); > > + > > + cc1_config &= ~EN_CC_DET; > > + cc2_config &= ~EN_CC_DET; > > + writel(cc1_config, reg_base + USB_TYPEC_CTRL_CC1_0); > > + writel(cc2_config, reg_base + USB_TYPEC_CTRL_CC2_0); > > + > > + if (soc_device_match(rtk_soc_kylin)) > > + cc1_switch = cc1_config & CC_SWITCH_MASK; > > + > > + cc1_config &= CC_CODE_MASK; > > + cc1_config |= rp_or_rd_en | cc1_switch; > > + cc2_config &= CC_CODE_MASK; > > + cc2_config |= rp_or_rd_en; > > + writel(cc2_config, reg_base + USB_TYPEC_CTRL_CC2_0); > > + writel(cc1_config, reg_base + USB_TYPEC_CTRL_CC1_0); > > + > > + /* For kylin to disable external rd control gpio */ > > + if (soc_device_match(rtk_soc_kylin)) { > > + struct gpio_desc *gpio = type_c->rd_ctrl_gpio_desc; > > + > > + if (gpio && gpiod_direction_output(gpio, 1)) > > + dev_err(dev, "%s ERROR set rd_ctrl_gpio_desc fail\n", > > __func__); > > + } > > + > > + cc1_config |= EN_CC_DET; > > + cc2_config |= EN_CC_DET; > > + writel(cc1_config, reg_base + USB_TYPEC_CTRL_CC1_0); > > + writel(cc2_config, reg_base + USB_TYPEC_CTRL_CC2_0); > > + > > + return 0; > > +} > > + > > +static int detect_device(struct type_c_data *type_c) > > +{ > > + return __detect_host_device(type_c, type_c->dfp_mode_rp_en); > > +} > > + > > +static int detect_host(struct type_c_data *type_c) > > +{ > > + return __detect_host_device(type_c, type_c->ufp_mode_rd_en); > > +} > > + > > +static int host_device_switch_detection(struct type_c_data *type_c) > > +{ > > + if (type_c->cc_mode == IN_HOST_MODE) { > > + type_c->cc_mode = IN_DEVICE_MODE; > > + detect_host(type_c); > > + } else { > > + type_c->cc_mode = IN_HOST_MODE; > > + detect_device(type_c); > > + } > > + > > + return 0; > > +} > > + > > +static int detect_type_c_state(struct type_c_data *type_c) > > +{ > > + struct device *dev = type_c->dev; > > + void __iomem *reg_base = type_c->reg_base; > > + u32 int_status, cc_status, cc_status_check; > > + unsigned long flags; > > + > > + spin_lock_irqsave(&type_c->lock, flags); > > + > > + int_status = readl(reg_base + USB_TYPEC_CTRL); > > + cc_status = readl(reg_base + USB_TYPEC_STS); > > + > > + type_c->connect_change = CONNECT_NO_CHANGE; > > + > > + switch (type_c->cc_mode | type_c->is_attach) { > > + case IN_HOST_MODE | IN_ATTACH: > > + if (((cc_status & CC1_DET_STS) == CC1_DET_STS) && > > type_c->at_cc1 == AT_CC1) { > > + dev_dbg(dev, "IN host mode and cc1 device detach > > (cc_status=0x%x)", > > + cc_status); > > + type_c->is_attach = TO_DETACH; > > + type_c->connect_change = CONNECT_CHANGE; > > + } else if (((cc_status & CC2_DET_STS) == CC2_DET_STS) && > > + type_c->at_cc1 == AT_CC2) { > > + dev_dbg(dev, "IN host mode and cc2 device detach > > (cc_status=0x%x)", > > + cc_status); > > + type_c->is_attach = TO_DETACH; > > + type_c->connect_change = CONNECT_CHANGE; > > + } > > + break; > > + case IN_HOST_MODE | IN_DETACH: > > + cc_status_check = readl(reg_base + USB_TYPEC_STS); > > + if (cc_status_check != (CC1_DET_STS | CC2_DET_STS)) { > > + if (in_interrupt()) { > > + /* Add delay time to avoid capacitive effect of cable. */ > > + mdelay(300); > > + } else { > > + spin_unlock_irqrestore(&type_c->lock, flags); > > + /* Add delay time to avoid capacitive effect of cable. */ > > + msleep(300); > > + spin_lock_irqsave(&type_c->lock, flags); > > + } > > + cc_status_check = readl(reg_base + USB_TYPEC_STS); > > + } > > + if (cc_status != cc_status_check) { > > + dev_warn(dev, "IN_HOST_MODE: cc_status (0x%x) != > > cc_status_check (0x%x)\n", > > + cc_status, cc_status_check); > > + cc_status = readl(reg_base + USB_TYPEC_STS); > > + } > > + > > + if ((cc_status & CC1_DET_STS) == CC1_DET_STS_RD) { > > + dev_dbg(dev, "IN host mode and cc1 device attach > > (cc_status=0x%x)", > > + cc_status); > > + type_c->is_attach = TO_ATTACH; > > + type_c->at_cc1 = AT_CC1; > > + type_c->connect_change = CONNECT_CHANGE; > > + } else if ((cc_status & CC2_DET_STS) == CC2_DET_STS_RD) { > > + dev_dbg(dev, "In host mode and cc2 device attach > > (cc_status=0x%x)", > > + cc_status); > > + type_c->is_attach = TO_ATTACH; > > + type_c->at_cc1 = AT_CC2; > > + type_c->connect_change = CONNECT_CHANGE; > > + } > > + break; > > + case IN_DEVICE_MODE | IN_ATTACH: > > + if ((cc_status & CC1_DET_STS) < CC1_DET_STS_RP || > > + (cc_status & CC2_DET_STS) < CC2_DET_STS_RP) { > > + /* Add a sw debounce to filter cc signal sent from apple pd > > adapter */ > > + mdelay(5); > > + cc_status_check = readl(reg_base + USB_TYPEC_STS); > > + > > + if (cc_status != cc_status_check) { > > + dev_dbg(dev, "IN_DEVICE_MODE: cc_status (0x%x) != > > cc_status_check (0x%x) maybe use a pd adapter\n", > > + cc_status, cc_status_check); > > + cc_status = cc_status_check; > > + } > > + } > > + > > + if ((cc_status & CC1_DET_STS) < CC1_DET_STS_RP && > > type_c->at_cc1 == AT_CC1) { > > + dev_dbg(dev, "IN device mode and cc1 host disconnect > > (cc_status=0x%x)", > > + cc_status); > > + type_c->is_attach = TO_DETACH; > > + type_c->connect_change = CONNECT_CHANGE; > > + } else if ((cc_status & CC2_DET_STS) < CC2_DET_STS_RP && > > + type_c->at_cc1 == AT_CC2) { > > + dev_dbg(dev, "IN device mode and cc2 host disconnect > > (cc_status=0x%x)", > > + cc_status); > > + type_c->is_attach = TO_DETACH; > > + type_c->connect_change = CONNECT_CHANGE; > > + } > > + break; > > + case IN_DEVICE_MODE | IN_DETACH: > > + cc_status_check = readl(reg_base + USB_TYPEC_STS); > > + if (cc_status_check != 0x0) { > > + if (in_interrupt()) { > > + /* Add delay time to avoid capacitive effect of cable. */ > > + mdelay(300); > > + } else { > > + spin_unlock_irqrestore(&type_c->lock, flags); > > + /* Add delay time to avoid capacitive effect of cable. */ > > + msleep(300); > > + spin_lock_irqsave(&type_c->lock, flags); > > + } > > + cc_status_check = readl(reg_base + USB_TYPEC_STS); > > + } > > + > > + if (cc_status != cc_status_check) { > > + dev_warn(dev, "IN_DEVICE_MODE: cc_status (0x%x) != > > cc_status_check (0x%x)\n", > > + cc_status, cc_status_check); > > + cc_status = readl(reg_base + USB_TYPEC_STS); > > + } > > + > > + if ((cc_status & CC1_DET_STS) >= CC1_DET_STS_RP) { > > + dev_dbg(dev, "IN device mode and cc1 host connect > > (cc_status=0x%x)", > > + cc_status); > > + type_c->at_cc1 = AT_CC1; > > + type_c->is_attach = TO_ATTACH; > > + type_c->connect_change = CONNECT_CHANGE; > > + } else if ((cc_status & CC2_DET_STS) >= CC2_DET_STS_RP) { > > + dev_dbg(dev, "IN device mode and cc2 host connect > > (cc_status=0x%x)", > > + cc_status); > > + type_c->at_cc1 = AT_CC2; > > + type_c->is_attach = TO_ATTACH; > > + type_c->connect_change = CONNECT_CHANGE; > > + } > > + break; > > + default: > > + dev_err(dev, "error host or device mode (cc_mode=%d, > > is_attach=%d) ", > > + type_c->cc_mode, type_c->is_attach); > > + } > > + > > + type_c->int_status = int_status; > > + type_c->cc_status = cc_status; > > + > > + spin_unlock_irqrestore(&type_c->lock, flags); > > + return 0; > > +} > > + > > +static void host_device_switch(struct work_struct *work) > > +{ > > + struct type_c_data *type_c = container_of(work, struct type_c_data, > > + delayed_work.work); > > + struct device *dev = type_c->dev; > > + unsigned long flags; > > + int connect_change = 0; > > + int cc_mode = 0; > > + int is_attach = 0; > > + int at_cc1 = 0; > > + > > + spin_lock_irqsave(&type_c->lock, flags); > > + if (type_c->connect_change) > > + connect_change = type_c->connect_change; > > + spin_unlock_irqrestore(&type_c->lock, flags); > > + > > + if (!connect_change) > > + detect_type_c_state(type_c); > > + > > + spin_lock_irqsave(&type_c->lock, flags); > > + if (type_c->connect_change) { > > + connect_change = type_c->connect_change; > > + cc_mode = type_c->cc_mode; > > + is_attach = type_c->is_attach; > > + at_cc1 = type_c->at_cc1; > > + type_c->connect_change = CONNECT_NO_CHANGE; > > + } else { > > + host_device_switch_detection(type_c); > > + > > + schedule_delayed_work(&type_c->delayed_work, > > msecs_to_jiffies(DETECT_TIME)); > > + } > > + spin_unlock_irqrestore(&type_c->lock, flags); > > + > > + if (!connect_change) > > + return; > > + > > + dev_dbg(dev, "%s: usb cable connection change\n", __func__); > > + if (cc_mode == IN_HOST_MODE) { > > + if (is_attach && at_cc1) > > + connector_attached(type_c, ENABLE_CC1, > > USB_DR_MODE_HOST); > > + else if (is_attach && !at_cc1) > > + connector_attached(type_c, ENABLE_CC2, > > USB_DR_MODE_HOST); > > + else > > + connector_detached(type_c, DISABLE_CC, > > USB_DR_MODE_HOST); > > + } else if (cc_mode == IN_DEVICE_MODE) { > > + if (is_attach && at_cc1) > > + connector_attached(type_c, ENABLE_CC1, > > USB_DR_MODE_PERIPHERAL); > > + else if (is_attach && !at_cc1) > > + connector_attached(type_c, ENABLE_CC2, > > USB_DR_MODE_PERIPHERAL); > > + else > > + connector_detached(type_c, DISABLE_CC, > > USB_DR_MODE_PERIPHERAL); > > + } else { > > + dev_err(dev, "Error: IN unknown mode %d to %s at %s > > (cc_status=0x%x)\n", > > + cc_mode, is_attach ? "attach" : "detach", > > + at_cc1 ? "cc1" : "cc2", type_c->cc_status); > > + } > > + dev_info(dev, "Connection change OK: IN %s mode to %s at %s > > (cc_status=0x%x)\n", > > + cc_mode == IN_HOST_MODE ? "host" : "device", > > + is_attach ? "attach" : "detach", > > + at_cc1 ? "cc1" : "cc2", type_c->cc_status); > > +} > > + > > +static irqreturn_t type_c_detect_irq(int irq, void *__data) > > +{ > > + struct type_c_data *type_c = (struct type_c_data *)__data; > > + struct device *dev = type_c->dev; > > + void __iomem *reg = type_c->reg_base + USB_TYPEC_CTRL; > > + unsigned long flags; > > + > > + detect_type_c_state(type_c); > > + > > + spin_lock_irqsave(&type_c->lock, flags); > > + > > + if (type_c->connect_change) { > > + dev_dbg(dev, "%s: IN %s mode to %s (at %s interrupt) > > int_status=0x%x, cc_status=0x%x", > > + __func__, > > + type_c->cc_mode == IN_HOST_MODE ? "host" : "device", > > + type_c->is_attach ? "attach" : "detach", > > + type_c->at_cc1 ? "cc1" : "cc2", > > + type_c->int_status, type_c->cc_status); > > + > > + /* clear interrupt status */ > > + writel(~ALL_CC_INT_STS & readl(reg), reg); > > + > > + cancel_delayed_work(&type_c->delayed_work); > > + schedule_delayed_work(&type_c->delayed_work, > > msecs_to_jiffies(0)); > > + } else { > > + static int local_count; > > + > > + /* if no connect_change, we keep the status to avoid status lose */ > > + if (local_count++ > 10) { > > + /* clear interrupt status */ > > + writel(~ALL_CC_INT_STS & readl(reg), reg); > > + local_count = 0; > > + } > > + } > > + > > + spin_unlock_irqrestore(&type_c->lock, flags); > > + > > + return IRQ_HANDLED; > > +} > > + > > +static int type_c_port_dr_set(struct typec_port *port, > > + enum typec_data_role role) > > +{ > > + struct type_c_data *type_c = typec_get_drvdata(port); > > + u32 enable_cc; > > + unsigned long flags; > > + > > + spin_lock_irqsave(&type_c->lock, flags); > > + enable_cc = type_c->at_cc1 ? ENABLE_CC1 : ENABLE_CC2; > > + spin_unlock_irqrestore(&type_c->lock, flags); > > + > > + if (role == TYPEC_HOST) > > + switch_type_c_dr_mode(type_c, USB_DR_MODE_HOST, enable_cc); > > + else if (role == TYPEC_DEVICE) > > + switch_type_c_dr_mode(type_c, USB_DR_MODE_PERIPHERAL, > > enable_cc); > > + else > > + switch_type_c_dr_mode(type_c, 0, DISABLE_CC); > > + > > + return 0; > > +} > > + > > +static const struct typec_operations type_c_port_ops = { > > + .dr_set = type_c_port_dr_set, > > +}; > > + > > +#ifdef CONFIG_DEBUG_FS > > +static int type_c_parameter_show(struct seq_file *s, void *unused) > > +{ > > + struct type_c_data *type_c = s->private; > > + struct type_c_cfg *type_c_cfg = type_c->type_c_cfg; > > + struct cc_param *cc_param; > > + unsigned long flags; > > + > > + spin_lock_irqsave(&type_c->lock, flags); > > + > > + seq_printf(s, "cc_dfp_mode %s\n", > > + ({ char *tmp; > > + switch (type_c_cfg->cc_dfp_mode) { > > + case CC_MODE_DFP_USB: > > + tmp = "CC_MODE_DFP_USB"; break; > > + case CC_MODE_DFP_1_5: > > + tmp = "CC_MODE_DFP_1_5"; break; > > + case CC_MODE_DFP_3_0: > > + tmp = "CC_MODE_DFP_3_0"; break; > > + default: > > + tmp = "?"; break; > > + } tmp; })); > > + > > + seq_printf(s, "dfp_mode_rp_en 0x%x\n", type_c->dfp_mode_rp_en); > > + seq_printf(s, "ufp_mode_rd_en 0x%x\n", type_c->ufp_mode_rd_en); > > + seq_printf(s, "cc1_code 0x%x\n", type_c->cc1_code); > > + seq_printf(s, "cc2_code 0x%x\n", type_c->cc2_code); > > + seq_printf(s, "cc1_vref 0x%x\n", type_c->cc1_vref); > > + seq_printf(s, "cc2_vref 0x%x\n", type_c->cc2_vref); > > + seq_printf(s, "debounce 0x%x\n", type_c->debounce); > > + seq_puts(s, "\n"); > > + > > + cc_param = &type_c_cfg->cc1_param; > > + seq_puts(s, "cc1_param:\n"); > > + seq_printf(s, " rp_4p7k_code 0x%x\n", cc_param->rp_4p7k_code); > > + seq_printf(s, " rp_36k_code 0x%x\n", cc_param->rp_36k_code); > > + seq_printf(s, " rp_12k_code 0x%x\n", cc_param->rp_12k_code); > > + seq_printf(s, " rd_code 0x%x\n", cc_param->rd_code); > > + seq_printf(s, " vref_2p6v 0x%x\n", cc_param->vref_2p6v); > > + seq_printf(s, " vref_1p23v 0x%x\n", cc_param->vref_1p23v); > > + seq_printf(s, " vref_0p8v 0x%x\n", cc_param->vref_0p8v); > > + seq_printf(s, " vref_0p66v 0x%x\n", cc_param->vref_0p66v); > > + seq_printf(s, " vref_0p4v 0x%x\n", cc_param->vref_0p4v); > > + seq_printf(s, " vref_0p2v 0x%x\n", cc_param->vref_0p2v); > > + seq_printf(s, " vref_1_1p6v 0x%x\n", cc_param->vref_1_1p6v); > > + seq_printf(s, " vref_0_1p6v 0x%x\n", cc_param->vref_0_1p6v); > > + > > + cc_param = &type_c_cfg->cc2_param; > > + seq_puts(s, "cc2_param:\n"); > > + seq_printf(s, " rp_4p7k_code 0x%x\n", cc_param->rp_4p7k_code); > > + seq_printf(s, " rp_36k_code 0x%x\n", cc_param->rp_36k_code); > > + seq_printf(s, " rp_12k_code 0x%x\n", cc_param->rp_12k_code); > > + seq_printf(s, " rd_code 0x%x\n", cc_param->rd_code); > > + seq_printf(s, " vref_2p6v 0x%x\n", cc_param->vref_2p6v); > > + seq_printf(s, " vref_1p23v 0x%x\n", cc_param->vref_1p23v); > > + seq_printf(s, " vref_0p8v 0x%x\n", cc_param->vref_0p8v); > > + seq_printf(s, " vref_0p66v 0x%x\n", cc_param->vref_0p66v); > > + seq_printf(s, " vref_0p4v 0x%x\n", cc_param->vref_0p4v); > > + seq_printf(s, " vref_0p2v 0x%x\n", cc_param->vref_0p2v); > > + seq_printf(s, " vref_1_1p6v 0x%x\n", cc_param->vref_1_1p6v); > > + seq_printf(s, " vref_0_1p6v 0x%x\n", cc_param->vref_0_1p6v); > > + > > + spin_unlock_irqrestore(&type_c->lock, flags); > > + > > + return 0; > > +} > > + > > +static int type_c_parameter_open(struct inode *inode, struct file *file) > > +{ > > + return single_open(file, type_c_parameter_show, inode->i_private); > > +} > > + > > +static const struct file_operations type_c_parameter_fops = { > > + .open = type_c_parameter_open, > > + .read = seq_read, > > + .llseek = seq_lseek, > > + .release = single_release, > > +}; > > + > > +static int type_c_status_show(struct seq_file *s, void *unused) > > +{ > > + struct type_c_data *type_c = s->private; > > + unsigned long flags; > > + > > + spin_lock_irqsave(&type_c->lock, flags); > > + > > + seq_printf(s, "In %s mode %s at %s (cc_status=0x%x)\n", > > + type_c->cc_mode == IN_HOST_MODE ? "host" : "device", > > + type_c->is_attach ? "attach" : "detach", > > + type_c->at_cc1 ? "cc1" : "cc2", type_c->cc_status); > > + > > + seq_printf(s, "Read Register (type_c_ctrl_cc1_0=0x%x)\n", > > + readl(type_c->reg_base + 0x0)); > > + seq_printf(s, "Read Register (type_c_ctrl_cc1_1=0x%x)\n", > > + readl(type_c->reg_base + 0x4)); > > + seq_printf(s, "Read Register (type_c_ctrl_cc2_0=0x%x)\n", > > + readl(type_c->reg_base + 0x8)); > > + seq_printf(s, "Read Register (type_c_ctrl_cc2_1=0x%x)\n", > > + readl(type_c->reg_base + 0xc)); > > + seq_printf(s, "Read Register (type_c_status=0x%x)\n", > > + readl(type_c->reg_base + 0x10)); > > + seq_printf(s, "Read Register (type_c_ctrl=0x%x)\n", > > + readl(type_c->reg_base + 0x14)); > > + > > + spin_unlock_irqrestore(&type_c->lock, flags); > > + > > + return 0; > > +} > > + > > +static int type_c_status_open(struct inode *inode, struct file *file) > > +{ > > + return single_open(file, type_c_status_show, inode->i_private); > > +} > > + > > +static const struct file_operations type_c_status_fops = { > > + .open = type_c_status_open, > > + .read = seq_read, > > + .llseek = seq_lseek, > > + .release = single_release, > > +}; > > + > > +static inline void create_debug_files(struct type_c_data *type_c) > > +{ > > + type_c->debug_dir = debugfs_create_dir("type_c", usb_debug_root); > > + > > + debugfs_create_file("parameter", 0444, type_c->debug_dir, type_c, > > + &type_c_parameter_fops); > > + > > + debugfs_create_file("status", 0444, type_c->debug_dir, type_c, > > + &type_c_status_fops); > > +} > > + > > +static inline void remove_debug_files(struct type_c_data *type_c) > > +{ > > + debugfs_remove_recursive(type_c->debug_dir); > > +} > > +#else > > +static inline void create_debug_files(struct type_c_data *type_c) { } > > +static inline void remove_debug_files(struct type_c_data *type_c) { } > > +#endif /* CONFIG_DEBUG_FS */ > > + > > +/* Init and probe */ > > + > > +static inline s8 get_value(s8 value) > > +{ > > + return (((s8)value & 0x8) ? (-(s8)(0x7 & value)) : ((s8)(value))); > > +} > > + > > +static int __updated_type_c_parameter_by_efuse(struct type_c_data > > *type_c) > > +{ > > + struct type_c_cfg *type_c_cfg = type_c->type_c_cfg; > > + struct cc_param *cc_param; > > + struct nvmem_cell *cell; > > + s8 cc1_4p7k = 0; > > + s8 cc1_12k = 0; > > + s8 cc1_0p2v = 0; > > + s8 cc1_0p8v = 0; > > + s8 cc1_2p6v = 0; > > + s8 cc1_0p66v = 0; > > + s8 cc1_1p23v = 0; > > + s8 cc2_4p7k = 0; > > + s8 cc2_12k = 0; > > + s8 cc2_0p2v = 0; > > + s8 cc2_0p8v = 0; > > + s8 cc2_2p6v = 0; > > + s8 cc2_0p66v = 0; > > + s8 cc2_1p23v = 0; > > + > > + cell = nvmem_cell_get(type_c->dev, "usb-cal"); > > + if (IS_ERR(cell)) { > > + dev_warn(type_c->dev, "%s failed to get usb-cal: %ld\n", > > + __func__, PTR_ERR(cell)); > > + } else { > > + unsigned char *buf; > > + size_t buf_size; > > + int value_size = 4; > > + int value_mask = (BIT(value_size) - 1); > > + > > + buf = nvmem_cell_read(cell, &buf_size); > > + > > + cc1_0p2v = get_value((buf[0] >> value_size * 0) & value_mask); > > + cc1_0p8v = get_value((buf[0] >> value_size * 1) & value_mask); > > + cc1_2p6v = get_value((buf[1] >> value_size * 0) & value_mask); > > + cc1_0p66v = get_value((buf[1] >> value_size * 1) & value_mask); > > + cc1_1p23v = get_value((buf[2] >> value_size * 0) & value_mask); > > + > > + cc2_0p2v = get_value((buf[3] >> value_size * 0) & value_mask); > > + cc2_0p8v = get_value((buf[3] >> value_size * 1) & value_mask); > > + cc2_2p6v = get_value((buf[4] >> value_size * 0) & value_mask); > > + cc2_0p66v = get_value((buf[4] >> value_size * 1) & value_mask); > > + cc2_1p23v = get_value((buf[5] >> value_size * 0) & value_mask); > > + > > + cc1_4p7k = get_value((buf[6] >> value_size * 0) & value_mask); > > + cc1_12k = get_value((buf[6] >> value_size * 1) & value_mask); > > + cc2_4p7k = get_value((buf[7] >> value_size * 0) & value_mask); > > + cc2_12k = get_value((buf[7] >> value_size * 1) & value_mask); > > + > > + kfree(buf); > > + nvmem_cell_put(cell); > > + } > > + > > + dev_dbg(type_c->dev, "check efuse cc1_4p7k=%d cc1_12k=%d > > cc2_4p7k=%d cc2_12k=%d\n", > > + cc1_4p7k, cc1_12k, cc2_4p7k, cc2_12k); > > + dev_dbg(type_c->dev, "check efuse cc1_0p2v=%d cc1_0p8v=%d > > cc1_2p6v=%d cc1_0p66v=%d cc1_1p23v=%d\n", > > + cc1_0p2v, cc1_0p8v, cc1_2p6v, cc1_0p66v, cc1_1p23v); > > + dev_dbg(type_c->dev, "check efuse cc2_0p2v=%d cc2_0p8v=%d > > cc2_2p6v=%d cc2_0p66v=%d cc2_1p23v=%d\n", > > + cc2_0p2v, cc2_0p8v, cc2_2p6v, cc2_0p66v, cc2_1p23v); > > + > > + cc_param = &type_c_cfg->cc1_param; > > + cc_param->rp_4p7k_code = cc_param->rp_4p7k_code + cc1_4p7k; > > + cc_param->rp_12k_code = cc_param->rp_12k_code + cc1_12k; > > + > > + cc_param->vref_1p23v = cc_param->vref_1p23v + cc1_1p23v; > > + cc_param->vref_0p66v = cc_param->vref_0p66v + cc1_0p66v; > > + cc_param->vref_2p6v = cc_param->vref_2p6v + cc1_2p6v; > > + cc_param->vref_0p8v = cc_param->vref_0p8v + cc1_0p8v; > > + cc_param->vref_0p2v = cc_param->vref_0p2v + cc1_0p2v; > > + > > + cc_param = &type_c_cfg->cc2_param; > > + cc_param->rp_4p7k_code = cc_param->rp_4p7k_code + cc2_4p7k; > > + cc_param->rp_12k_code = cc_param->rp_12k_code + cc2_12k; > > + > > + cc_param->vref_1p23v = cc_param->vref_1p23v + cc2_1p23v; > > + cc_param->vref_0p66v = cc_param->vref_0p66v + cc2_0p66v; > > + cc_param->vref_2p6v = cc_param->vref_2p6v + cc2_2p6v; > > + cc_param->vref_0p8v = cc_param->vref_0p8v + cc2_0p8v; > > + cc_param->vref_0p2v = cc_param->vref_0p2v + cc2_0p2v; > > + > > + return 0; > > +} > > + > > +static int __updated_type_c_parameter_by_efuse_v2(struct type_c_data > > *type_c) > > +{ > > + struct type_c_cfg *type_c_cfg = type_c->type_c_cfg; > > + struct cc_param *cc_param; > > + struct nvmem_cell *cell; > > + s8 cc1_4p7k = 0; > > + s8 cc1_12k = 0; > > + s8 cc1_0p2v = 0; > > + s8 cc1_0p8v = 0; > > + s8 cc1_2p6v = 0; > > + s8 cc1_0p66v = 0; > > + s8 cc1_1p23v = 0; > > + s8 cc2_4p7k = 0; > > + s8 cc2_12k = 0; > > + s8 cc2_0p2v = 0; > > + s8 cc2_0p8v = 0; > > + s8 cc2_2p6v = 0; > > + s8 cc2_0p66v = 0; > > + s8 cc2_1p23v = 0; > > + > > + cell = nvmem_cell_get(type_c->dev, "usb-type-c-cal"); > > + if (IS_ERR(cell)) { > > + dev_warn(type_c->dev, "%s failed to get usb-type-c-cal: %ld\n", > > + __func__, PTR_ERR(cell)); > > + } else { > > + unsigned char *buf; > > + size_t buf_size; > > + int value_size = 0; > > + int value_mask = (BIT(value_size) - 1); > > + > > + buf = nvmem_cell_read(cell, &buf_size); > > + > > + value_size = 5; > > + value_mask = (BIT(value_size) - 1); > > + cc1_4p7k = buf[0] & value_mask; > > + cc1_12k = buf[1] & value_mask; > > + cc2_4p7k = buf[2] & value_mask; > > + cc2_12k = buf[3] & value_mask; > > + > > + value_size = 4; > > + value_mask = (BIT(value_size) - 1); > > + cc1_0p2v = (buf[4] >> value_size * 0) & value_mask; > > + cc1_0p66v = (buf[4] >> value_size * 1) & value_mask; > > + cc1_0p8v = (buf[5] >> value_size * 0) & value_mask; > > + cc1_1p23v = (buf[5] >> value_size * 1) & value_mask; > > + cc1_2p6v = (buf[6] >> value_size * 0) & value_mask; > > + > > + cc2_0p2v = (buf[6] >> value_size * 1) & value_mask; > > + cc2_0p66v = (buf[7] >> value_size * 0) & value_mask; > > + cc2_0p8v = (buf[7] >> value_size * 1) & value_mask; > > + cc2_1p23v = (buf[8] >> value_size * 0) & value_mask; > > + cc2_2p6v = (buf[8] >> value_size * 1) & value_mask; > > + > > + kfree(buf); > > + nvmem_cell_put(cell); > > + } > > + > > + dev_dbg(type_c->dev, "check efuse v2 cc1_4p7k=%d cc1_12k=%d > > cc2_4p7k=%d cc2_12k=%d\n", > > + cc1_4p7k, cc1_12k, cc2_4p7k, cc2_12k); > > + dev_dbg(type_c->dev, "check efuse v2 cc1_0p2v=%d cc1_0p8v=%d > > cc1_2p6v=%d cc1_0p66v=%d cc1_1p23v=%d\n", > > + cc1_0p2v, cc1_0p8v, cc1_2p6v, cc1_0p66v, cc1_1p23v); > > + dev_dbg(type_c->dev, "check efuse v2 cc2_0p2v=%d cc2_0p8v=%d > > cc2_2p6v=%d cc2_0p66v=%d cc2_1p23v=%d\n", > > + cc2_0p2v, cc2_0p8v, cc2_2p6v, cc2_0p66v, cc2_1p23v); > > + > > + cc_param = &type_c_cfg->cc1_param; > > + if (cc1_4p7k) > > + cc_param->rp_4p7k_code = cc1_4p7k; > > + if (cc1_12k) > > + cc_param->rp_12k_code = cc1_12k; > > + > > + if (cc1_1p23v) > > + cc_param->vref_1p23v = cc1_1p23v; > > + if (cc1_0p66v) > > + cc_param->vref_0p66v = cc1_0p66v; > > + if (cc1_2p6v) > > + cc_param->vref_2p6v = cc1_2p6v; > > + if (cc1_0p8v) > > + cc_param->vref_0p8v = cc1_0p8v; > > + if (cc1_0p2v) > > + cc_param->vref_0p2v = cc1_0p2v; > > + > > + cc_param = &type_c_cfg->cc2_param; > > + if (cc2_4p7k) > > + cc_param->rp_4p7k_code = cc2_4p7k; > > + if (cc2_12k) > > + cc_param->rp_12k_code = cc2_12k; > > + > > + if (cc2_1p23v) > > + cc_param->vref_1p23v = cc2_1p23v; > > + if (cc2_0p66v) > > + cc_param->vref_0p66v = cc2_0p66v; > > + if (cc2_2p6v) > > + cc_param->vref_2p6v = cc2_2p6v; > > + if (cc2_0p8v) > > + cc_param->vref_0p8v = cc2_0p8v; > > + if (cc2_0p2v) > > + cc_param->vref_0p2v = cc2_0p2v; > > + > > + return 0; > > +} > > + > > +static void get_default_type_c_parameter(struct type_c_data *type_c) > > +{ > > + void __iomem *reg; > > + int val; > > + > > + type_c->dfp_mode_rp_en = dfp_mode(CC_MODE_DFP_3_0) | > > EN_RP4P7K; > > + type_c->ufp_mode_rd_en = EN_RD; > > + > > + reg = type_c->reg_base + USB_TYPEC_CTRL_CC1_0; > > + val = readl(reg); > > + type_c->cc1_code = CC_CODE_MASK & val; > > + > > + reg = type_c->reg_base + USB_TYPEC_CTRL_CC2_0; > > + val = readl(reg); > > + type_c->cc2_code = CC_CODE_MASK & val; > > + > > + reg = type_c->reg_base + USB_TYPEC_CTRL_CC1_1; > > + val = readl(reg); > > + type_c->cc1_vref = val; > > + > > + reg = type_c->reg_base + USB_TYPEC_CTRL_CC2_1; > > + val = readl(reg); > > + type_c->cc2_vref = val; > > + > > + reg = type_c->reg_base + USB_TYPEC_CTRL; > > + val = readl(reg); > > + type_c->debounce = DEBOUNCE_TIME_MASK & val; > > +} > > + > > +static int setup_type_c_parameter(struct type_c_data *type_c) > > +{ > > + struct type_c_cfg *type_c_cfg = type_c->type_c_cfg; > > + struct cc_param *cc_param; > > + struct soc_device_attribute rtk_soc_efuse_v1[] = { > > + { .family = "Realtek Phoenix",}, > > + { .family = "Realtek Kylin",}, > > + { .family = "Realtek Hercules",}, > > + { .family = "Realtek Thor",}, > > + { .family = "Realtek Hank",}, > > + { .family = "Realtek Groot",}, > > + { .family = "Realtek Stark",}, > > + { .family = "Realtek Parker",}, > > + { /* empty */ } > > + }; > > + > > + if (type_c_cfg->use_defalut_parameter) { > > + get_default_type_c_parameter(type_c); > > + return 0; > > + } > > + > > + if (soc_device_match(rtk_soc_efuse_v1)) > > + __updated_type_c_parameter_by_efuse(type_c); > > + else > > + __updated_type_c_parameter_by_efuse_v2(type_c); > > + > > + /* > > + * UFP rd vref_ufp : 1p23v, 0p66v, 0p2v > > + * DFP_USB rp36k vref_dfp_usb: 0_1p6v, 0p2v, unused > > + * DFP_1.5 rp12k vref_dfp_1_5: 1_1p6v, 0p4v, 0p2v > > + * DFP_3.0 rp4p7k vref_dfp_3_0: 2p6v, 0p8v, 0p2v > > + */ > > + > > + switch (type_c_cfg->cc_dfp_mode) { > > + case CC_MODE_DFP_USB: > > + type_c->dfp_mode_rp_en = dfp_mode(CC_MODE_DFP_USB) | > > EN_RP36K; > > + break; > > + case CC_MODE_DFP_1_5: > > + type_c->dfp_mode_rp_en = dfp_mode(CC_MODE_DFP_1_5) | > > EN_RP12K; > > + break; > > + case CC_MODE_DFP_3_0: > > + type_c->dfp_mode_rp_en = dfp_mode(CC_MODE_DFP_3_0) | > > EN_RP4P7K; > > + break; > > + default: > > + dev_err(type_c->dev, "%s: unknown cc_dfp_mode %d\n", > > + __func__, type_c_cfg->cc_dfp_mode); > > + } > > + > > + type_c->ufp_mode_rd_en = EN_RD; > > + > > + cc_param = &type_c_cfg->cc1_param; > > + type_c->cc1_code = rp4pk_code(cc_param->rp_4p7k_code) | > > + rp36k_code(cc_param->rp_36k_code) | > > + rp12k_code(cc_param->rp_12k_code) | > > + rd_code(cc_param->rd_code); > > + > > + if (type_c_cfg->parameter_ver == PARAMETER_V0) > > + type_c->cc1_vref = V0_vref_2p6v(cc_param->vref_2p6v) | > > + V0_vref_1p23v(cc_param->vref_1p23v) | > > + V0_vref_0p8v(cc_param->vref_0p8v) | > > + V0_vref_0p66v(cc_param->vref_0p66v) | > > + V0_vref_0p4v(cc_param->vref_0p4v) | > > + V0_vref_0p2v(cc_param->vref_0p2v) | > > + V0_vref_1_1p6v(cc_param->vref_1_1p6v) | > > + V0_vref_0_1p6v(cc_param->vref_0_1p6v); > > + else if (type_c_cfg->parameter_ver == PARAMETER_V1) > > + type_c->cc1_vref = V1_vref_2p6v(cc_param->vref_2p6v) | > > + V1_vref_1p23v(cc_param->vref_1p23v) | > > + V1_vref_0p8v(cc_param->vref_0p8v) | > > + V1_vref_0p66v(cc_param->vref_0p66v) | > > + V1_vref_0p4v(cc_param->vref_0p4v) | > > + V1_vref_0p2v(cc_param->vref_0p2v) | > > + V1_vref_1_1p6v(cc_param->vref_1_1p6v) | > > + V1_vref_0_1p6v(cc_param->vref_0_1p6v); > > + else > > + dev_err(type_c->dev, "%s: unknown parameter_ver %d\n", > > + __func__, type_c_cfg->parameter_ver); > > + > > + cc_param = &type_c_cfg->cc2_param; > > + type_c->cc2_code = rp4pk_code(cc_param->rp_4p7k_code) > > + | rp36k_code(cc_param->rp_36k_code) > > + | rp12k_code(cc_param->rp_12k_code) > > + | rd_code(cc_param->rd_code); > > + > > + if (type_c_cfg->parameter_ver == PARAMETER_V0) > > + type_c->cc2_vref = V0_vref_2p6v(cc_param->vref_2p6v) | > > + V0_vref_1p23v(cc_param->vref_1p23v) | > > + V0_vref_0p8v(cc_param->vref_0p8v) | > > + V0_vref_0p66v(cc_param->vref_0p66v) | > > + V0_vref_0p4v(cc_param->vref_0p4v) | > > + V0_vref_0p2v(cc_param->vref_0p2v) | > > + V0_vref_1_1p6v(cc_param->vref_1_1p6v) | > > + V0_vref_0_1p6v(cc_param->vref_0_1p6v); > > + else if (type_c_cfg->parameter_ver == PARAMETER_V1) > > + type_c->cc2_vref = V1_vref_2p6v(cc_param->vref_2p6v) | > > + V1_vref_1p23v(cc_param->vref_1p23v) | > > + V1_vref_0p8v(cc_param->vref_0p8v) | > > + V1_vref_0p66v(cc_param->vref_0p66v) | > > + V1_vref_0p4v(cc_param->vref_0p4v) | > > + V1_vref_0p2v(cc_param->vref_0p2v) | > > + V1_vref_1_1p6v(cc_param->vref_1_1p6v) | > > + V1_vref_0_1p6v(cc_param->vref_0_1p6v); > > + else > > + dev_err(type_c->dev, "%s: unknown parameter_ver %d\n", > > + __func__, type_c_cfg->parameter_ver); > > + > > + type_c->debounce = (type_c_cfg->debounce_val << 1) | DEBOUNCE_EN; > > + > > + return 0; > > +} > > + > > +static int extcon_rtk_type_c_init(struct type_c_data *type_c) > > +{ > > + struct device *dev = type_c->dev; > > + unsigned long flags; > > + void __iomem *reg; > > + int val; > > + > > + spin_lock_irqsave(&type_c->lock, flags); > > + > > + /* set parameter */ > > + reg = type_c->reg_base + USB_TYPEC_CTRL_CC1_0; > > + val = readl(reg); > > + val = (~CC_CODE_MASK & val) | (type_c->cc1_code & CC_CODE_MASK); > > + writel(val, reg); > > + > > + reg = type_c->reg_base + USB_TYPEC_CTRL_CC2_0; > > + val = readl(reg); > > + val = (~CC_CODE_MASK & val) | (type_c->cc2_code & CC_CODE_MASK); > > + > > + reg = type_c->reg_base + USB_TYPEC_CTRL_CC1_1; > > + writel(type_c->cc1_vref, reg); > > + > > + reg = type_c->reg_base + USB_TYPEC_CTRL_CC2_1; > > + writel(type_c->cc2_vref, reg); > > + > > + reg = type_c->reg_base + USB_TYPEC_CTRL; > > + val = readl(reg); > > + val = (~DEBOUNCE_TIME_MASK & val) | (type_c->debounce & > > DEBOUNCE_TIME_MASK); > > + > > + dev_info(dev, "First check USB_DR_MODE_PERIPHERAL"); > > + type_c->cc_mode = IN_DEVICE_MODE; > > + type_c->is_attach = IN_DETACH; > > + type_c->connect_change = CONNECT_NO_CHANGE; > > + > > + detect_host(type_c); > > + > > + spin_unlock_irqrestore(&type_c->lock, flags); > > + > > + schedule_delayed_work(&type_c->delayed_work, msecs_to_jiffies(0)); > > + > > + if (!type_c->port) { > > + struct typec_capability typec_cap = { }; > > + struct fwnode_handle *fwnode; > > + const char *buf; > > + int ret; > > + > > + typec_cap.revision = USB_TYPEC_REV_1_0; > > + typec_cap.prefer_role = TYPEC_NO_PREFERRED_ROLE; > > + typec_cap.driver_data = type_c; > > + typec_cap.ops = &type_c_port_ops; > > + > > + fwnode = device_get_named_child_node(dev, "connector"); > > + if (!fwnode) > > + return -EINVAL; > > + > > + ret = fwnode_property_read_string(fwnode, "power-role", &buf); > > + if (ret) { > > + dev_err(dev, "power-role not found: %d\n", ret); > > + return ret; > > + } > > + > > + ret = typec_find_port_power_role(buf); > > + if (ret < 0) > > + return ret; > > + typec_cap.type = ret; > > + > > + ret = fwnode_property_read_string(fwnode, "data-role", &buf); > > + if (ret) { > > + dev_err(dev, "data-role not found: %d\n", ret); > > + return ret; > > + } > > + > > + ret = typec_find_port_data_role(buf); > > + if (ret < 0) > > + return ret; > > + typec_cap.data = ret; > > + > > + type_c->port = typec_register_port(type_c->dev, &typec_cap); > > + if (IS_ERR(type_c->port)) > > + return PTR_ERR(type_c->port); > > + } > > + > > + return 0; > > +} > > + > > +static int extcon_rtk_type_c_edev_register(struct type_c_data *type_c) > > +{ > > + struct device *dev = type_c->dev; > > + int ret = 0; > > + > > + type_c->edev = devm_extcon_dev_allocate(dev, usb_type_c_cable); > > + if (IS_ERR(type_c->edev)) { > > + dev_err(dev, "failed to allocate extcon device\n"); > > + return -ENOMEM; > > + } > > + > > + ret = devm_extcon_dev_register(dev, type_c->edev); > > + if (ret < 0) { > > + dev_err(dev, "failed to register extcon device\n"); > > + return ret; > > + } > > + > > + extcon_set_property_capability(type_c->edev, EXTCON_USB, > > + EXTCON_PROP_USB_VBUS); > > + extcon_set_property_capability(type_c->edev, EXTCON_USB, > > + EXTCON_PROP_USB_TYPEC_POLARITY); > > + extcon_set_property_capability(type_c->edev, EXTCON_USB, > > + EXTCON_PROP_USB_SS); > > + > > + extcon_set_property_capability(type_c->edev, EXTCON_USB_HOST, > > + EXTCON_PROP_USB_VBUS); > > + extcon_set_property_capability(type_c->edev, EXTCON_USB_HOST, > > + EXTCON_PROP_USB_TYPEC_POLARITY); > > + extcon_set_property_capability(type_c->edev, EXTCON_USB_HOST, > > + EXTCON_PROP_USB_SS); > > + > > + return ret; > > +} > > + > > +static int extcon_rtk_type_c_probe(struct platform_device *pdev) > > +{ > > + struct device *dev = &pdev->dev; > > + struct type_c_data *type_c; > > + const struct type_c_cfg *type_c_cfg; > > + int ret = 0; > > + > > + type_c = devm_kzalloc(dev, sizeof(*type_c), GFP_KERNEL); > > + if (!type_c) > > + return -ENOMEM; > > + > > + type_c->reg_base = devm_platform_ioremap_resource(pdev, 0); > > + if (IS_ERR(type_c->reg_base)) > > + return PTR_ERR(type_c->reg_base); > > + > > + type_c->dev = dev; > > + > > + type_c->irq = irq_of_parse_and_map(pdev->dev.of_node, 0); > > + if (type_c->irq <= 0) { > > + dev_err(&pdev->dev, "Type C driver with no IRQ. Check %s setup!\n", > > + dev_name(&pdev->dev)); > > + ret = -ENODEV; > > + goto err; > > + } > > + > > + ret = devm_request_irq(dev, type_c->irq, type_c_detect_irq, > > + IRQF_SHARED, "type_c_detect", type_c); > > + > > + spin_lock_init(&type_c->lock); > > + > > + type_c->rd_ctrl_gpio_desc = NULL; > > + if (soc_device_match(rtk_soc_kylin)) { > > + struct gpio_desc *gpio; > > + > > + gpio = fwnode_gpiod_get_index(of_fwnode_handle(dev->of_node), > > + "realtek,rd-ctrl-gpios", > > + 0, GPIOD_OUT_HIGH, "rd-ctrl-gpio"); > > + if (IS_ERR(gpio)) { > > + dev_err(dev, "Error rd_ctrl-gpios no found (err=%d)\n", > > + (int)PTR_ERR(gpio)); > > + } else { > > + type_c->rd_ctrl_gpio_desc = gpio; > > + dev_dbg(dev, "%s get rd-ctrl-gpios (id=%d) OK\n", > > + __func__, desc_to_gpio(gpio)); > > + } > > + } > > + > > + type_c_cfg = of_device_get_match_data(dev); > > + if (!type_c_cfg) { > > + dev_err(dev, "type_c config are not assigned!\n"); > > + ret = -EINVAL; > > + goto err; > > + } > > + > > + type_c->type_c_cfg = devm_kzalloc(dev, sizeof(*type_c_cfg), > > GFP_KERNEL); > > + > > + memcpy(type_c->type_c_cfg, type_c_cfg, sizeof(*type_c_cfg)); > > + > > + if (setup_type_c_parameter(type_c)) { > > + dev_err(dev, "ERROR: %s to setup type c parameter!!", __func__); > > + ret = -EINVAL; > > + goto err; > > + } > > + > > + INIT_DELAYED_WORK(&type_c->delayed_work, host_device_switch); > > + > > + ret = extcon_rtk_type_c_init(type_c); > > + if (ret) { > > + dev_err(dev, "%s failed to init type_c\n", __func__); > > + goto err; > > + } > > + > > + platform_set_drvdata(pdev, type_c); > > + > > + ret = extcon_rtk_type_c_edev_register(type_c); > > + > > + create_debug_files(type_c); > > + > > + return 0; > > + > > +err: > > + dev_err(&pdev->dev, "%s: Probe fail, %d\n", __func__, ret); > > + > > + return ret; > > +} > > + > > +static void extcon_rtk_type_c_remove(struct platform_device *pdev) > > +{ > > + struct device *dev = &pdev->dev; > > + struct type_c_data *type_c = dev_get_drvdata(dev); > > + u32 default_ctrl; > > + unsigned long flags; > > + > > + remove_debug_files(type_c); > > + > > + if (type_c->port) { > > + typec_unregister_port(type_c->port); > > + type_c->port = NULL; > > + } > > + > > + cancel_delayed_work_sync(&type_c->delayed_work); > > + flush_delayed_work(&type_c->delayed_work); > > + WARN_ON_ONCE(delayed_work_pending(&type_c->delayed_work)); > > + > > + spin_lock_irqsave(&type_c->lock, flags); > > + /* disable interrupt */ > > + default_ctrl = readl(type_c->reg_base + USB_TYPEC_CTRL) & > > + DEBOUNCE_TIME_MASK; > > + writel(default_ctrl, type_c->reg_base + USB_TYPEC_CTRL); > > + > > + /* disable cc detect, rp, rd */ > > + writel(PLR_EN, type_c->reg_base + USB_TYPEC_CTRL_CC1_0); > > + writel(0, type_c->reg_base + USB_TYPEC_CTRL_CC2_0); > > + > > + spin_unlock_irqrestore(&type_c->lock, flags); > > + > > + if (type_c->rd_ctrl_gpio_desc) > > + gpiod_put(type_c->rd_ctrl_gpio_desc); > > + type_c->rd_ctrl_gpio_desc = NULL; > > + > > + free_irq(type_c->irq, type_c); > > +} > > + > > +static const struct type_c_cfg rtd1295_type_c_cfg = { > > + .parameter_ver = PARAMETER_V0, > > + .cc_dfp_mode = CC_MODE_DFP_3_0, > > + .cc1_param = { .rp_4p7k_code = 0xb, > > + .rp_36k_code = 0x17, > > + .rp_12k_code = 0x10, > > + .rd_code = 0, > > + .ra_code = 0, > > + .vref_2p6v = 0x0, > > + .vref_1p23v = 0x0, > > + .vref_0p8v = 0x3, > > + .vref_0p66v = 0x0, > > + .vref_0p4v = 0x0, > > + .vref_0p2v = 0x4, > > + .vref_1_1p6v = 0, > > + .vref_0_1p6v = 0 }, > > + .cc2_param = { .rp_4p7k_code = 0xc, > > + .rp_36k_code = 0x17, > > + .rp_12k_code = 0x12, > > + .rd_code = 0, > > + .ra_code = 0, > > + .vref_2p6v = 0x2, > > + .vref_1p23v = 0x0, > > + .vref_0p8v = 0x3, > > + .vref_0p66v = 0x0, > > + .vref_0p4v = 0x0, > > + .vref_0p2v = 0x5, > > + .vref_1_1p6v = 0, > > + .vref_0_1p6v = 0 }, > > + .debounce_val = 0x7f, /* 1b,1us 7f,4.7us */ > > + .use_defalut_parameter = false, > > +}; > > + > > +static const struct type_c_cfg rtd1395_type_c_cfg = { > > + .parameter_ver = PARAMETER_V0, > > + .cc_dfp_mode = CC_MODE_DFP_3_0, > > + .cc1_param = { .rp_4p7k_code = 0xc, > > + .rp_36k_code = 0xb, > > + .rp_12k_code = 0xe, > > + .rd_code = 0x10, > > + .ra_code = 0x0, > > + .vref_2p6v = 0x0, > > + .vref_1p23v = 0x1, > > + .vref_0p8v = 0x0, > > + .vref_0p66v = 0x0, > > + .vref_0p4v = 0x3, > > + .vref_0p2v = 0x0, > > + .vref_1_1p6v = 0x7, > > + .vref_0_1p6v = 0x7 }, > > + .cc2_param = { .rp_4p7k_code = 0xb, > > + .rp_36k_code = 0x9, > > + .rp_12k_code = 0xe, > > + .rd_code = 0xf, > > + .ra_code = 0x0, > > + .vref_2p6v = 0x1, > > + .vref_1p23v = 0x3, > > + .vref_0p8v = 0x3, > > + .vref_0p66v = 0x2, > > + .vref_0p4v = 0x3, > > + .vref_0p2v = 0x2, > > + .vref_1_1p6v = 0x7, > > + .vref_0_1p6v = 0x7 }, > > + .debounce_val = 0x7f, /* 1b,1us 7f,4.7us */ > > + .use_defalut_parameter = false, > > +}; > > + > > +static const struct type_c_cfg rtd1619_type_c_cfg = { > > + .parameter_ver = PARAMETER_V0, > > + .cc_dfp_mode = CC_MODE_DFP_3_0, > > + .cc1_param = { .rp_4p7k_code = 0xc, > > + .rp_36k_code = 0xf, > > + .rp_12k_code = 0xe, > > + .rd_code = 0x11, > > + .ra_code = 0x0, > > + .vref_2p6v = 0x5, > > + .vref_1p23v = 0x7, > > + .vref_0p8v = 0xa, > > + .vref_0p66v = 0xa, > > + .vref_0p4v = 0x3, > > + .vref_0p2v = 0x2, > > + .vref_1_1p6v = 0x7, > > + .vref_0_1p6v = 0x7 }, > > + .cc2_param = { .rp_4p7k_code = 0xc, > > + .rp_36k_code = 0xf, > > + .rp_12k_code = 0xe, > > + .rd_code = 0xf, > > + .ra_code = 0x0, > > + .vref_2p6v = 0x5, > > + .vref_1p23v = 0x8, > > + .vref_0p8v = 0xa, > > + .vref_0p66v = 0xa, > > + .vref_0p4v = 0x3, > > + .vref_0p2v = 0x2, > > + .vref_1_1p6v = 0x7, > > + .vref_0_1p6v = 0x7 }, > > + .debounce_val = 0x7f, /* 1b,1us 7f,4.7us */ > > + .use_defalut_parameter = false, > > +}; > > + > > +static const struct type_c_cfg rtd1319_type_c_cfg = { > > + .parameter_ver = PARAMETER_V0, > > + .cc_dfp_mode = CC_MODE_DFP_1_5, > > + .cc1_param = { .rp_4p7k_code = 0x9, > > + .rp_36k_code = 0xe, > > + .rp_12k_code = 0x9, > > + .rd_code = 0x9, > > + .ra_code = 0x7, > > + .vref_2p6v = 0x3, > > + .vref_1p23v = 0x7, > > + .vref_0p8v = 0x7, > > + .vref_0p66v = 0x6, > > + .vref_0p4v = 0x2, > > + .vref_0p2v = 0x3, > > + .vref_1_1p6v = 0x4, > > + .vref_0_1p6v = 0x7 }, > > + .cc2_param = { .rp_4p7k_code = 0x8, > > + .rp_36k_code = 0xe, > > + .rp_12k_code = 0x9, > > + .rd_code = 0x9, > > + .ra_code = 0x7, > > + .vref_2p6v = 0x3, > > + .vref_1p23v = 0x7, > > + .vref_0p8v = 0x7, > > + .vref_0p66v = 0x6, > > + .vref_0p4v = 0x3, > > + .vref_0p2v = 0x3, > > + .vref_1_1p6v = 0x6, > > + .vref_0_1p6v = 0x7 }, > > + .debounce_val = 0x7f, /* 1b,1us 7f,4.7us */ > > + .use_defalut_parameter = false, > > +}; > > + > > +static const struct type_c_cfg rtd1312c_type_c_cfg = { > > + .parameter_ver = PARAMETER_V0, > > + .cc_dfp_mode = CC_MODE_DFP_1_5, > > + .cc1_param = { .rp_4p7k_code = 0xe, > > + .rp_36k_code = 0xc, > > + .rp_12k_code = 0xc, > > + .rd_code = 0xa, > > + .ra_code = 0x3, > > + .vref_2p6v = 0xa, > > + .vref_1p23v = 0x7, > > + .vref_0p8v = 0x7, > > + .vref_0p66v = 0x7, > > + .vref_0p4v = 0x4, > > + .vref_0p2v = 0x4, > > + .vref_1_1p6v = 0x7, > > + .vref_0_1p6v = 0x7 }, > > + .cc2_param = { .rp_4p7k_code = 0xe, > > + .rp_36k_code = 0xc, > > + .rp_12k_code = 0xc, > > + .rd_code = 0xa, > > + .ra_code = 0x3, > > + .vref_2p6v = 0xa, > > + .vref_1p23v = 0x7, > > + .vref_0p8v = 0x7, > > + .vref_0p66v = 0x7, > > + .vref_0p4v = 0x4, > > + .vref_0p2v = 0x4, > > + .vref_1_1p6v = 0x7, > > + .vref_0_1p6v = 0x7 }, > > + .debounce_val = 0x7f, /* 1b,1us 7f,4.7us */ > > + .use_defalut_parameter = false, > > +}; > > + > > +static const struct type_c_cfg rtd1619b_type_c_cfg = { > > + .parameter_ver = PARAMETER_V1, > > + .cc_dfp_mode = CC_MODE_DFP_1_5, > > + .cc1_param = { .rp_4p7k_code = 0xf, > > + .rp_36k_code = 0xf, > > + .rp_12k_code = 0xf, > > + .rd_code = 0xf, > > + .ra_code = 0x7, > > + .vref_2p6v = 0x9, > > + .vref_1p23v = 0x7, > > + .vref_0p8v = 0x9, > > + .vref_0p66v = 0x8, > > + .vref_0p4v = 0x7, > > + .vref_0p2v = 0x9, > > + .vref_1_1p6v = 0x7, > > + .vref_0_1p6v = 0x7 }, > > + .cc2_param = { .rp_4p7k_code = 0xf, > > + .rp_36k_code = 0xf, > > + .rp_12k_code = 0xf, > > + .rd_code = 0xf, > > + .ra_code = 0x7, > > + .vref_1p23v = 0x7, > > + .vref_0p8v = 0x9, > > + .vref_0p66v = 0x8, > > + .vref_0p4v = 0x7, > > + .vref_0p2v = 0x8, > > + .vref_1_1p6v = 0x7, > > + .vref_0_1p6v = 0x7 }, > > + .debounce_val = 0x7f, /* 1b,1us 7f,4.7us */ > > + .use_defalut_parameter = false, > > +}; > > + > > +static const struct type_c_cfg rtd1319d_type_c_cfg = { > > + .parameter_ver = PARAMETER_V1, > > + .cc_dfp_mode = CC_MODE_DFP_1_5, > > + .cc1_param = { .rp_4p7k_code = 0xe, > > + .rp_36k_code = 0x3, > > + .rp_12k_code = 0xe, > > + .rd_code = 0xf, > > + .ra_code = 0x6, > > + .vref_2p6v = 0x7, > > + .vref_1p23v = 0x7, > > + .vref_0p8v = 0x8, > > + .vref_0p66v = 0x7, > > + .vref_0p4v = 0x7, > > + .vref_0p2v = 0x7, > > + .vref_1_1p6v = 0x7, > > + .vref_0_1p6v = 0x7 }, > > + .cc2_param = { .rp_4p7k_code = 0xe, > > + .rp_36k_code = 0x3, > > + .rp_12k_code = 0xe, > > + .rd_code = 0xf, > > + .ra_code = 0x6, > > + .vref_2p6v = 0x7, > > + .vref_1p23v = 0x7, > > + .vref_0p8v = 0x8, > > + .vref_0p66v = 0x7, > > + .vref_0p4v = 0x7, > > + .vref_0p2v = 0x8, > > + .vref_1_1p6v = 0x7, > > + .vref_0_1p6v = 0x7 }, > > + .debounce_val = 0x7f, /* 1b,1us 7f,4.7us */ > > + .use_defalut_parameter = false, > > +}; > > + > > +static const struct type_c_cfg rtd1315e_type_c_cfg = { > > + .parameter_ver = PARAMETER_V1, > > + .cc_dfp_mode = CC_MODE_DFP_1_5, > > + .cc1_param = { .rp_4p7k_code = 0xe, > > + .rp_36k_code = 0x3, > > + .rp_12k_code = 0xe, > > + .rd_code = 0xf, > > + .ra_code = 0x6, > > + .vref_2p6v = 0x7, > > + .vref_1p23v = 0x7, > > + .vref_0p8v = 0x8, > > + .vref_0p66v = 0x7, > > + .vref_0p4v = 0x7, > > + .vref_0p2v = 0x7, > > + .vref_1_1p6v = 0x7, > > + .vref_0_1p6v = 0x7 }, > > + .cc2_param = { .rp_4p7k_code = 0xe, > > + .rp_36k_code = 0x3, > > + .rp_12k_code = 0xe, > > + .rd_code = 0xf, > > + .ra_code = 0x6, > > + .vref_2p6v = 0x7, > > + .vref_1p23v = 0x7, > > + .vref_0p8v = 0x8, > > + .vref_0p66v = 0x7, > > + .vref_0p4v = 0x7, > > + .vref_0p2v = 0x8, > > + .vref_1_1p6v = 0x7, > > + .vref_0_1p6v = 0x7 }, > > + .debounce_val = 0x7f, /* 1b,1us 7f,4.7us */ > > + .use_defalut_parameter = false, > > +}; > > + > > +static const struct of_device_id extcon_rtk_type_c_match[] = { > > + { .compatible = "realtek,rtd1295-type-c", .data = &rtd1295_type_c_cfg }, > > + { .compatible = "realtek,rtd1312c-type-c", .data = > > &rtd1312c_type_c_cfg }, > > + { .compatible = "realtek,rtd1315e-type-c", .data = > > &rtd1315e_type_c_cfg }, > > + { .compatible = "realtek,rtd1319-type-c", .data = &rtd1319_type_c_cfg }, > > + { .compatible = "realtek,rtd1319d-type-c", .data = > > &rtd1319d_type_c_cfg }, > > + { .compatible = "realtek,rtd1395-type-c", .data = &rtd1395_type_c_cfg }, > > + { .compatible = "realtek,rtd1619-type-c", .data = &rtd1619_type_c_cfg }, > > + { .compatible = "realtek,rtd1619b-type-c", .data = > > &rtd1619b_type_c_cfg }, > > + {}, > > +}; > > +MODULE_DEVICE_TABLE(of, extcon_rtk_type_c_match); > > + > > +#ifdef CONFIG_PM_SLEEP > > +static int extcon_rtk_type_c_prepare(struct device *dev) > > +{ > > + struct type_c_data *type_c = dev_get_drvdata(dev); > > + u32 default_ctrl; > > + unsigned long flags; > > + > > + cancel_delayed_work_sync(&type_c->delayed_work); > > + flush_delayed_work(&type_c->delayed_work); > > + WARN_ON_ONCE(delayed_work_pending(&type_c->delayed_work)); > > + > > + spin_lock_irqsave(&type_c->lock, flags); > > + /* disable interrupt */ > > + default_ctrl = readl(type_c->reg_base + USB_TYPEC_CTRL) & > > + DEBOUNCE_TIME_MASK; > > + writel(default_ctrl, type_c->reg_base + USB_TYPEC_CTRL); > > + > > + /* disable cc detect, rp, rd */ > > + writel(PLR_EN, type_c->reg_base + USB_TYPEC_CTRL_CC1_0); > > + writel(0, type_c->reg_base + USB_TYPEC_CTRL_CC2_0); > > + > > + spin_unlock_irqrestore(&type_c->lock, flags); > > + > > + return 0; > > +} > > + > > +static void extcon_rtk_type_c_complete(struct device *dev) > > +{ > > + /* nothing */ > > +} > > + > > +static int extcon_rtk_type_c_suspend(struct device *dev) > > +{ > > + /* nothing */ > > + > > + return 0; > > +} > > + > > +static int extcon_rtk_type_c_resume(struct device *dev) > > +{ > > + struct type_c_data *type_c = dev_get_drvdata(dev); > > + int ret; > > + > > + ret = extcon_rtk_type_c_init(type_c); > > + if (ret) { > > + dev_err(dev, "%s failed to init type_c\n", __func__); > > + return ret; > > + } > > + > > + return 0; > > +} > > + > > +static const struct dev_pm_ops extcon_rtk_type_c_pm_ops = { > > + SET_LATE_SYSTEM_SLEEP_PM_OPS(extcon_rtk_type_c_suspend, > > extcon_rtk_type_c_resume) > > + .prepare = extcon_rtk_type_c_prepare, > > + .complete = extcon_rtk_type_c_complete, > > +}; > > + > > +#define DEV_PM_OPS (&extcon_rtk_type_c_pm_ops) > > +#else > > +#define DEV_PM_OPS NULL > > +#endif /* CONFIG_PM_SLEEP */ > > + > > +static struct platform_driver extcon_rtk_type_c_driver = { > > + .probe = extcon_rtk_type_c_probe, > > + .remove_new = extcon_rtk_type_c_remove, > > + .driver = { > > + .name = "extcon-rtk-type_c", > > + .of_match_table = extcon_rtk_type_c_match, > > + .pm = DEV_PM_OPS, > > + }, > > +}; > > + > > +module_platform_driver(extcon_rtk_type_c_driver); > > + > > +MODULE_DESCRIPTION("Realtek Extcon Type C driver"); > > +MODULE_AUTHOR("Stanley Chang <stanley_chang@realtek.com>"); > > +MODULE_LICENSE("GPL"); > > -- > > 2.34.1 >
Hi Chanwoo, Thank you so much. Stanley > > > Hi Stanley, > > Applied them with patch1/2. > > I'm sorry for late reply. > > Regards, > Chanwoo Choi >
diff --git a/drivers/extcon/Kconfig b/drivers/extcon/Kconfig index 0ef1971d22bb..0f4c061e7321 100644 --- a/drivers/extcon/Kconfig +++ b/drivers/extcon/Kconfig @@ -190,4 +190,15 @@ config EXTCON_USBC_TUSB320 Say Y here to enable support for USB Type C cable detection extcon support using a TUSB320. +config EXTCON_RTK_TYPE_C + tristate "Realtek RTD SoC extcon Type-C Driver" + depends on ARCH_REALTEK || COMPILE_TEST + depends on TYPEC + select USB_COMMON + help + Say Y here to enable extcon support for USB Type C cable detection + when using the Realtek RTD SoC USB Type-C port. + The DHC (Digital Home Hub) RTD series SoC contains a type c module. + This driver will detect the status of the type-c port. + endif diff --git a/drivers/extcon/Makefile b/drivers/extcon/Makefile index 1b390d934ca9..f779adb5e4c7 100644 --- a/drivers/extcon/Makefile +++ b/drivers/extcon/Makefile @@ -25,3 +25,4 @@ obj-$(CONFIG_EXTCON_SM5502) += extcon-sm5502.o obj-$(CONFIG_EXTCON_USB_GPIO) += extcon-usb-gpio.o obj-$(CONFIG_EXTCON_USBC_CROS_EC) += extcon-usbc-cros-ec.o obj-$(CONFIG_EXTCON_USBC_TUSB320) += extcon-usbc-tusb320.o +obj-$(CONFIG_EXTCON_RTK_TYPE_C) += extcon-rtk-type-c.o diff --git a/drivers/extcon/extcon-rtk-type-c.c b/drivers/extcon/extcon-rtk-type-c.c new file mode 100644 index 000000000000..00465cfba23e --- /dev/null +++ b/drivers/extcon/extcon-rtk-type-c.c @@ -0,0 +1,1792 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * * extcon-rtk-type-c.c - Realtek Extcon Type C driver + * + * Copyright (C) 2023 Realtek Semiconductor Corporation + * + */ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/slab.h> +#include <linux/platform_device.h> +#include <linux/of.h> +#include <linux/of_address.h> +#include <linux/of_irq.h> +#include <linux/of_gpio.h> +#include <linux/io.h> +#include <linux/interrupt.h> +#include <linux/syscalls.h> +#include <linux/suspend.h> +#include <linux/debugfs.h> +#include <linux/extcon.h> +#include <linux/extcon-provider.h> +#include <linux/sys_soc.h> +#include <linux/nvmem-consumer.h> +#include <linux/gpio/consumer.h> +#include <linux/usb/otg.h> +#include <linux/usb/typec.h> + +struct cc_param { + u32 rp_4p7k_code; + u32 rp_36k_code; + u32 rp_12k_code; + u32 rd_code; + u32 ra_code; + u32 vref_2p6v; + u32 vref_1p23v; + u32 vref_0p8v; + u32 vref_0p66v; + u32 vref_0p4v; + u32 vref_0p2v; + u32 vref_1_1p6v; + u32 vref_0_1p6v; +}; + +struct type_c_cfg { + int parameter_ver; /* Parameter version */ + int cc_dfp_mode; + struct cc_param cc1_param; + struct cc_param cc2_param; + + u32 debounce_val; + bool use_defalut_parameter; +}; + +struct type_c_data { + void __iomem *reg_base; + struct device *dev; + struct extcon_dev *edev; + + u32 irq; + + /* rd control GPIO only for rtd1295 */ + struct gpio_desc *rd_ctrl_gpio_desc; + + /* Parameters */ + struct type_c_cfg *type_c_cfg; + u32 dfp_mode_rp_en; + u32 ufp_mode_rd_en; + u32 cc1_code; + u32 cc2_code; + u32 cc1_vref; + u32 cc2_vref; + u32 debounce; /* 1b,1us 7f,4.7us */ + + /* type_c state */ + int connect_change; +#define CONNECT_CHANGE 1 +#define CONNECT_NO_CHANGE 0 + int cc_mode; /* cc is host or device */ +#define IN_HOST_MODE 0x10 +#define IN_DEVICE_MODE 0x20 + int is_attach; +#define IN_ATTACH 1 +#define TO_ATTACH 1 +#define IN_DETACH 0 +#define TO_DETACH 0 + int at_cc1; +#define AT_CC1 1 +#define AT_CC2 0 + + u32 int_status; + u32 cc_status; + /* protect the data member */ + spinlock_t lock; + struct delayed_work delayed_work; + + bool rd_en_at_first; + + struct dentry *debug_dir; + + struct typec_port *port; +}; + +/* Type C register offset */ +#define USB_TYPEC_CTRL_CC1_0 0x0 +#define USB_TYPEC_CTRL_CC1_1 0x4 +#define USB_TYPEC_CTRL_CC2_0 0x8 +#define USB_TYPEC_CTRL_CC2_1 0xC +#define USB_TYPEC_STS 0x10 +#define USB_TYPEC_CTRL 0x14 +#define USB_DBUS_PWR_CTRL 0x18 + +#define ENABLE_CC1 0x1 +#define ENABLE_CC2 0x2 +#define DISABLE_CC 0x0 + +/* Bit mapping USB_TYPEC_CTRL_CC1_0 and USB_TYPEC_CTRL_CC2_0 */ +#define PLR_EN BIT(29) +#define CC_SWITCH_MASK (BIT(29) | BIT(28) | BIT(27)) +#define CC_CODE_MASK (0xfffff << 7) +#define rp4pk_code(val) ((0x1f & (val)) << 22) +#define code_rp4pk(val) (((val) >> 22) & 0x1f) +#define rp36k_code(val) ((0x1f & (val)) << 17) +#define code_rp36k(val) (((val) >> 17) & 0x1f) +#define rp12k_code(val) ((0x1f & (val)) << 12) +#define code_rp12k(val) (((val) >> 12) & 0x1f) +#define rd_code(val) ((0x1f & (val)) << 7) +#define code_rd(val) (((val) >> 7) & 0x1f) +#define dfp_mode(val) ((0x3 & (val)) << 5) +#define EN_RP4P7K BIT(4) +#define EN_RP36K BIT(3) +#define EN_RP12K BIT(2) +#define EN_RD BIT(1) +#define EN_CC_DET BIT(0) + +#define CC_MODE_UFP 0x0 +#define CC_MODE_DFP_USB 0x1 +#define CC_MODE_DFP_1_5 0x2 +#define CC_MODE_DFP_3_0 0x3 + +/* + * PARAMETER_V0: + * Realtek Kylin rtd1295 + * Realtek Hercules rtd1395 + * Realtek Thor rtd1619 + * Realtek Hank rtd1319 + * Realtek Groot rtd1312c + * PARAMETER_V1: + * Realtek Stark rtd1619b + * Realtek Parker rtd1319d + * Realtek Danvers rtd1315e + */ +enum parameter_version { + PARAMETER_V0 = 0, + PARAMETER_V1 = 1, +}; + +/* Bit mapping USB_TYPEC_CTRL_CC1_1 and USB_TYPEC_CTRL_CC2_1 */ +#define V0_vref_2p6v(val) ((0xf & (val)) << 26) /* Bit 29 for groot */ +#define V0_vref_1p23v(val) ((0xf & (val)) << 22) +#define V0_vref_0p8v(val) ((0xf & (val)) << 18) +#define V0_vref_0p66v(val) ((0xf & (val)) << 14) +#define V0_vref_0p4v(val) ((0x7 & (val)) << 11) +#define V0_vref_0p2v(val) ((0x7 & (val)) << 8) +#define V0_vref_1_1p6v(val) ((0xf & (val)) << 4) +#define V0_vref_0_1p6v(val) ((0xf & (val)) << 0) + +#define V0_decode_2p6v(val) (((val) >> 26) & 0xf) /* Bit 29 for groot */ +#define V0_decode_1p23v(val) (((val) >> 22) & 0xf) +#define V0_decode_0p8v(val) (((val) >> 18) & 0xf) +#define V0_decode_0p66v(val) (((val) >> 14) & 0xf) +#define V0_decode_0p4v(val) (((val) >> 11) & 0x7) +#define V0_decode_0p2v(val) (((val) >> 8) & 0x7) +#define V0_decode_1_1p6v(val) (((val) >> 4) & 0xf) +#define V0_decode_0_1p6v(val) (((val) >> 0) & 0xf) + +/* new Bit mapping USB_TYPEC_CTRL_CC1_1 and USB_TYPEC_CTRL_CC2_1 */ +#define V1_vref_2p6v(val) ((0xf & (val)) << 28) +#define V1_vref_1p23v(val) ((0xf & (val)) << 24) +#define V1_vref_0p8v(val) ((0xf & (val)) << 20) +#define V1_vref_0p66v(val) ((0xf & (val)) << 16) +#define V1_vref_0p4v(val) ((0xf & (val)) << 12) +#define V1_vref_0p2v(val) ((0xf & (val)) << 8) +#define V1_vref_1_1p6v(val) ((0xf & (val)) << 4) +#define V1_vref_0_1p6v(val) ((0xf & (val)) << 0) + +#define V1_decode_2p6v(val) (((val) >> 28) & 0xf) +#define V1_decode_1p23v(val) (((val) >> 24) & 0xf) +#define V1_decode_0p8v(val) (((val) >> 20) & 0xf) +#define V1_decode_0p66v(val) (((val) >> 16) & 0xf) +#define V1_decode_0p4v(val) (((val) >> 12) & 0xf) +#define V1_decode_0p2v(val) (((val) >> 8) & 0xf) +#define V1_decode_1_1p6v(val) (((val) >> 4) & 0xf) +#define V1_decode_0_1p6v(val) (((val) >> 0) & 0xf) + +/* Bit mapping USB_TYPEC_STS */ +#define DET_STS 0x7 +#define CC1_DET_STS (DET_STS) +#define CC2_DET_STS (DET_STS << 3) +#define DET_STS_RA 0x1 +#define DET_STS_RD 0x3 +#define DET_STS_RP 0x1 +#define CC1_DET_STS_RA (DET_STS_RA) +#define CC1_DET_STS_RD (DET_STS_RD) +#define CC1_DET_STS_RP (DET_STS_RP) +#define CC2_DET_STS_RA (DET_STS_RA << 3) +#define CC2_DET_STS_RD (DET_STS_RD << 3) +#define CC2_DET_STS_RP (DET_STS_RP << 3) + +/* Bit mapping USB_TYPEC_CTRL */ +#define CC2_INT_EN BIT(11) +#define CC1_INT_EN BIT(10) +#define CC2_INT_STS BIT(9) +#define CC1_INT_STS BIT(8) +#define DEBOUNCE_TIME_MASK 0xff +#define DEBOUNCE_EN BIT(0) +#define ENABLE_TYPE_C_DETECT (CC1_INT_EN | CC2_INT_EN) +#define ALL_CC_INT_STS (CC1_INT_STS | CC2_INT_STS) + +/* Parameter */ +#define DETECT_TIME 50 /* ms */ + +static const unsigned int usb_type_c_cable[] = { + EXTCON_USB, + EXTCON_USB_HOST, + EXTCON_NONE, +}; + +enum usb_data_roles { + DR_NONE, + DR_HOST, + DR_DEVICE, +}; + +static const struct soc_device_attribute rtk_soc_kylin[] = { + { .family = "Realtek Kylin", }, + { /* empty */ } +}; + +static int rtd129x_switch_type_c_plug_config(struct type_c_data *type_c, + int dr_mode, int cc) +{ + void __iomem *reg = type_c->reg_base + USB_TYPEC_CTRL_CC1_0; + int val_cc; + +#define TYPE_C_EN_SWITCH BIT(29) +#define TYPE_C_TXRX_SEL (BIT(28) | BIT(27)) +#define TYPE_C_SWITCH_MASK (TYPE_C_EN_SWITCH | TYPE_C_TXRX_SEL) +#define TYPE_C_ENABLE_CC1 TYPE_C_EN_SWITCH +#define TYPE_C_ENABLE_CC2 (TYPE_C_EN_SWITCH | TYPE_C_TXRX_SEL) +#define TYPE_C_DISABLE_CC ~TYPE_C_SWITCH_MASK + + val_cc = readl(reg); + val_cc &= ~TYPE_C_SWITCH_MASK; + + if (cc == DISABLE_CC) { + val_cc &= TYPE_C_DISABLE_CC; + } else if (cc == ENABLE_CC1) { + val_cc |= TYPE_C_ENABLE_CC1; + } else if (cc == ENABLE_CC2) { + val_cc |= TYPE_C_ENABLE_CC2; + } else { + dev_err(type_c->dev, "%s: Error cc setting cc=0x%x\n", __func__, cc); + return -EINVAL; + } + writel(val_cc, reg); + + /* waiting cc stable for enable/disable */ + mdelay(1); + + dev_dbg(type_c->dev, "%s: cc=0x%x val_cc=0x%x usb_typec_ctrl_cc1_0=0x%x\n", + __func__, cc, val_cc, readl(reg)); + + return 0; +} + +static inline void switch_type_c_plug_config(struct type_c_data *type_c, + int dr_mode, int cc) +{ + int ret = 0; + + if (soc_device_match(rtk_soc_kylin)) + ret = rtd129x_switch_type_c_plug_config(type_c, dr_mode, cc); + + if (ret < 0) + dev_err(type_c->dev, "%s: Error set type c plug config\n", + __func__); +} + +static void switch_type_c_dr_mode(struct type_c_data *type_c, int dr_mode, int cc) +{ + bool is_host = false; + bool is_device = false; + bool polarity = false; + bool vbus = false; + bool ss = true; + + switch_type_c_plug_config(type_c, dr_mode, cc); + if (cc == ENABLE_CC2) + polarity = true; + + switch (dr_mode) { + case USB_DR_MODE_HOST: + is_host = true; + break; + case USB_DR_MODE_PERIPHERAL: + is_device = true; + vbus = true; + break; + default: + dev_dbg(type_c->dev, "%s dr_mode=%d ==> no host or device\n", + __func__, dr_mode); + break; + } + + dev_dbg(type_c->dev, "%s is_host=%d is_device=%d vbus=%d polarity=%d\n", + __func__, is_host, is_device, vbus, polarity); + + /* for EXTCON_USB device mode */ + extcon_set_state(type_c->edev, EXTCON_USB, is_device); + extcon_set_property(type_c->edev, EXTCON_USB, + EXTCON_PROP_USB_VBUS, + (union extcon_property_value)(int)vbus); + extcon_set_property(type_c->edev, EXTCON_USB, + EXTCON_PROP_USB_TYPEC_POLARITY, + (union extcon_property_value)(int)polarity); + extcon_set_property(type_c->edev, EXTCON_USB, + EXTCON_PROP_USB_SS, + (union extcon_property_value)(int)ss); + + /* for EXTCON_USB_HOST host mode */ + extcon_set_state(type_c->edev, EXTCON_USB_HOST, is_host); + extcon_set_property(type_c->edev, EXTCON_USB_HOST, + EXTCON_PROP_USB_VBUS, + (union extcon_property_value)(int)vbus); + extcon_set_property(type_c->edev, EXTCON_USB_HOST, + EXTCON_PROP_USB_TYPEC_POLARITY, + (union extcon_property_value)(int)polarity); + extcon_set_property(type_c->edev, EXTCON_USB_HOST, + EXTCON_PROP_USB_SS, + (union extcon_property_value)(int)ss); + + /* sync EXTCON_USB and EXTCON_USB_HOST */ + extcon_sync(type_c->edev, EXTCON_USB); + extcon_sync(type_c->edev, EXTCON_USB_HOST); + + if (type_c->port) { + switch (dr_mode) { + case USB_DR_MODE_HOST: + typec_set_data_role(type_c->port, TYPEC_HOST); + typec_set_pwr_role(type_c->port, TYPEC_SOURCE); + break; + case USB_DR_MODE_PERIPHERAL: + typec_set_data_role(type_c->port, TYPEC_DEVICE); + typec_set_pwr_role(type_c->port, TYPEC_SINK); + break; + default: + dev_dbg(type_c->dev, "%s unknown dr_mode=%d\n", + __func__, dr_mode); + break; + } + } +} + +/* connector attached/detached */ +static int connector_attached(struct type_c_data *type_c, u32 cc, int dr_mode) +{ + void __iomem *reg = type_c->reg_base + USB_TYPEC_CTRL; + + cancel_delayed_work(&type_c->delayed_work); + + switch_type_c_dr_mode(type_c, dr_mode, cc); + + writel(ENABLE_TYPE_C_DETECT | readl(reg), reg); + + return 0; +} + +static int connector_detached(struct type_c_data *type_c, u32 cc, int dr_mode) +{ + void __iomem *reg = type_c->reg_base + USB_TYPEC_CTRL; + + writel(~ENABLE_TYPE_C_DETECT & readl(reg), reg); + + switch_type_c_dr_mode(type_c, 0, cc); + + schedule_delayed_work(&type_c->delayed_work, msecs_to_jiffies(DETECT_TIME)); + + return 0; +} + +/* detect host device switch */ +static int __detect_host_device(struct type_c_data *type_c, u32 rp_or_rd_en) +{ + struct device *dev = type_c->dev; + void __iomem *reg_base = type_c->reg_base; + u32 cc1_config, cc2_config, default_ctrl; + u32 cc1_switch = 0; + + default_ctrl = readl(reg_base + USB_TYPEC_CTRL) & DEBOUNCE_TIME_MASK; + writel(default_ctrl, reg_base + USB_TYPEC_CTRL); + + cc1_config = readl(reg_base + USB_TYPEC_CTRL_CC1_0); + cc2_config = readl(reg_base + USB_TYPEC_CTRL_CC2_0); + + cc1_config &= ~EN_CC_DET; + cc2_config &= ~EN_CC_DET; + writel(cc1_config, reg_base + USB_TYPEC_CTRL_CC1_0); + writel(cc2_config, reg_base + USB_TYPEC_CTRL_CC2_0); + + if (soc_device_match(rtk_soc_kylin)) + cc1_switch = cc1_config & CC_SWITCH_MASK; + + cc1_config &= CC_CODE_MASK; + cc1_config |= rp_or_rd_en | cc1_switch; + cc2_config &= CC_CODE_MASK; + cc2_config |= rp_or_rd_en; + writel(cc2_config, reg_base + USB_TYPEC_CTRL_CC2_0); + writel(cc1_config, reg_base + USB_TYPEC_CTRL_CC1_0); + + /* For kylin to disable external rd control gpio */ + if (soc_device_match(rtk_soc_kylin)) { + struct gpio_desc *gpio = type_c->rd_ctrl_gpio_desc; + + if (gpio && gpiod_direction_output(gpio, 1)) + dev_err(dev, "%s ERROR set rd_ctrl_gpio_desc fail\n", __func__); + } + + cc1_config |= EN_CC_DET; + cc2_config |= EN_CC_DET; + writel(cc1_config, reg_base + USB_TYPEC_CTRL_CC1_0); + writel(cc2_config, reg_base + USB_TYPEC_CTRL_CC2_0); + + return 0; +} + +static int detect_device(struct type_c_data *type_c) +{ + return __detect_host_device(type_c, type_c->dfp_mode_rp_en); +} + +static int detect_host(struct type_c_data *type_c) +{ + return __detect_host_device(type_c, type_c->ufp_mode_rd_en); +} + +static int host_device_switch_detection(struct type_c_data *type_c) +{ + if (type_c->cc_mode == IN_HOST_MODE) { + type_c->cc_mode = IN_DEVICE_MODE; + detect_host(type_c); + } else { + type_c->cc_mode = IN_HOST_MODE; + detect_device(type_c); + } + + return 0; +} + +static int detect_type_c_state(struct type_c_data *type_c) +{ + struct device *dev = type_c->dev; + void __iomem *reg_base = type_c->reg_base; + u32 int_status, cc_status, cc_status_check; + unsigned long flags; + + spin_lock_irqsave(&type_c->lock, flags); + + int_status = readl(reg_base + USB_TYPEC_CTRL); + cc_status = readl(reg_base + USB_TYPEC_STS); + + type_c->connect_change = CONNECT_NO_CHANGE; + + switch (type_c->cc_mode | type_c->is_attach) { + case IN_HOST_MODE | IN_ATTACH: + if (((cc_status & CC1_DET_STS) == CC1_DET_STS) && type_c->at_cc1 == AT_CC1) { + dev_dbg(dev, "IN host mode and cc1 device detach (cc_status=0x%x)", + cc_status); + type_c->is_attach = TO_DETACH; + type_c->connect_change = CONNECT_CHANGE; + } else if (((cc_status & CC2_DET_STS) == CC2_DET_STS) && + type_c->at_cc1 == AT_CC2) { + dev_dbg(dev, "IN host mode and cc2 device detach (cc_status=0x%x)", + cc_status); + type_c->is_attach = TO_DETACH; + type_c->connect_change = CONNECT_CHANGE; + } + break; + case IN_HOST_MODE | IN_DETACH: + cc_status_check = readl(reg_base + USB_TYPEC_STS); + if (cc_status_check != (CC1_DET_STS | CC2_DET_STS)) { + if (in_interrupt()) { + /* Add delay time to avoid capacitive effect of cable. */ + mdelay(300); + } else { + spin_unlock_irqrestore(&type_c->lock, flags); + /* Add delay time to avoid capacitive effect of cable. */ + msleep(300); + spin_lock_irqsave(&type_c->lock, flags); + } + cc_status_check = readl(reg_base + USB_TYPEC_STS); + } + if (cc_status != cc_status_check) { + dev_warn(dev, "IN_HOST_MODE: cc_status (0x%x) != cc_status_check (0x%x)\n", + cc_status, cc_status_check); + cc_status = readl(reg_base + USB_TYPEC_STS); + } + + if ((cc_status & CC1_DET_STS) == CC1_DET_STS_RD) { + dev_dbg(dev, "IN host mode and cc1 device attach (cc_status=0x%x)", + cc_status); + type_c->is_attach = TO_ATTACH; + type_c->at_cc1 = AT_CC1; + type_c->connect_change = CONNECT_CHANGE; + } else if ((cc_status & CC2_DET_STS) == CC2_DET_STS_RD) { + dev_dbg(dev, "In host mode and cc2 device attach (cc_status=0x%x)", + cc_status); + type_c->is_attach = TO_ATTACH; + type_c->at_cc1 = AT_CC2; + type_c->connect_change = CONNECT_CHANGE; + } + break; + case IN_DEVICE_MODE | IN_ATTACH: + if ((cc_status & CC1_DET_STS) < CC1_DET_STS_RP || + (cc_status & CC2_DET_STS) < CC2_DET_STS_RP) { + /* Add a sw debounce to filter cc signal sent from apple pd adapter */ + mdelay(5); + cc_status_check = readl(reg_base + USB_TYPEC_STS); + + if (cc_status != cc_status_check) { + dev_dbg(dev, "IN_DEVICE_MODE: cc_status (0x%x) != cc_status_check (0x%x) maybe use a pd adapter\n", + cc_status, cc_status_check); + cc_status = cc_status_check; + } + } + + if ((cc_status & CC1_DET_STS) < CC1_DET_STS_RP && type_c->at_cc1 == AT_CC1) { + dev_dbg(dev, "IN device mode and cc1 host disconnect (cc_status=0x%x)", + cc_status); + type_c->is_attach = TO_DETACH; + type_c->connect_change = CONNECT_CHANGE; + } else if ((cc_status & CC2_DET_STS) < CC2_DET_STS_RP && + type_c->at_cc1 == AT_CC2) { + dev_dbg(dev, "IN device mode and cc2 host disconnect (cc_status=0x%x)", + cc_status); + type_c->is_attach = TO_DETACH; + type_c->connect_change = CONNECT_CHANGE; + } + break; + case IN_DEVICE_MODE | IN_DETACH: + cc_status_check = readl(reg_base + USB_TYPEC_STS); + if (cc_status_check != 0x0) { + if (in_interrupt()) { + /* Add delay time to avoid capacitive effect of cable. */ + mdelay(300); + } else { + spin_unlock_irqrestore(&type_c->lock, flags); + /* Add delay time to avoid capacitive effect of cable. */ + msleep(300); + spin_lock_irqsave(&type_c->lock, flags); + } + cc_status_check = readl(reg_base + USB_TYPEC_STS); + } + + if (cc_status != cc_status_check) { + dev_warn(dev, "IN_DEVICE_MODE: cc_status (0x%x) != cc_status_check (0x%x)\n", + cc_status, cc_status_check); + cc_status = readl(reg_base + USB_TYPEC_STS); + } + + if ((cc_status & CC1_DET_STS) >= CC1_DET_STS_RP) { + dev_dbg(dev, "IN device mode and cc1 host connect (cc_status=0x%x)", + cc_status); + type_c->at_cc1 = AT_CC1; + type_c->is_attach = TO_ATTACH; + type_c->connect_change = CONNECT_CHANGE; + } else if ((cc_status & CC2_DET_STS) >= CC2_DET_STS_RP) { + dev_dbg(dev, "IN device mode and cc2 host connect (cc_status=0x%x)", + cc_status); + type_c->at_cc1 = AT_CC2; + type_c->is_attach = TO_ATTACH; + type_c->connect_change = CONNECT_CHANGE; + } + break; + default: + dev_err(dev, "error host or device mode (cc_mode=%d, is_attach=%d) ", + type_c->cc_mode, type_c->is_attach); + } + + type_c->int_status = int_status; + type_c->cc_status = cc_status; + + spin_unlock_irqrestore(&type_c->lock, flags); + return 0; +} + +static void host_device_switch(struct work_struct *work) +{ + struct type_c_data *type_c = container_of(work, struct type_c_data, + delayed_work.work); + struct device *dev = type_c->dev; + unsigned long flags; + int connect_change = 0; + int cc_mode = 0; + int is_attach = 0; + int at_cc1 = 0; + + spin_lock_irqsave(&type_c->lock, flags); + if (type_c->connect_change) + connect_change = type_c->connect_change; + spin_unlock_irqrestore(&type_c->lock, flags); + + if (!connect_change) + detect_type_c_state(type_c); + + spin_lock_irqsave(&type_c->lock, flags); + if (type_c->connect_change) { + connect_change = type_c->connect_change; + cc_mode = type_c->cc_mode; + is_attach = type_c->is_attach; + at_cc1 = type_c->at_cc1; + type_c->connect_change = CONNECT_NO_CHANGE; + } else { + host_device_switch_detection(type_c); + + schedule_delayed_work(&type_c->delayed_work, msecs_to_jiffies(DETECT_TIME)); + } + spin_unlock_irqrestore(&type_c->lock, flags); + + if (!connect_change) + return; + + dev_dbg(dev, "%s: usb cable connection change\n", __func__); + if (cc_mode == IN_HOST_MODE) { + if (is_attach && at_cc1) + connector_attached(type_c, ENABLE_CC1, USB_DR_MODE_HOST); + else if (is_attach && !at_cc1) + connector_attached(type_c, ENABLE_CC2, USB_DR_MODE_HOST); + else + connector_detached(type_c, DISABLE_CC, USB_DR_MODE_HOST); + } else if (cc_mode == IN_DEVICE_MODE) { + if (is_attach && at_cc1) + connector_attached(type_c, ENABLE_CC1, USB_DR_MODE_PERIPHERAL); + else if (is_attach && !at_cc1) + connector_attached(type_c, ENABLE_CC2, USB_DR_MODE_PERIPHERAL); + else + connector_detached(type_c, DISABLE_CC, USB_DR_MODE_PERIPHERAL); + } else { + dev_err(dev, "Error: IN unknown mode %d to %s at %s (cc_status=0x%x)\n", + cc_mode, is_attach ? "attach" : "detach", + at_cc1 ? "cc1" : "cc2", type_c->cc_status); + } + dev_info(dev, "Connection change OK: IN %s mode to %s at %s (cc_status=0x%x)\n", + cc_mode == IN_HOST_MODE ? "host" : "device", + is_attach ? "attach" : "detach", + at_cc1 ? "cc1" : "cc2", type_c->cc_status); +} + +static irqreturn_t type_c_detect_irq(int irq, void *__data) +{ + struct type_c_data *type_c = (struct type_c_data *)__data; + struct device *dev = type_c->dev; + void __iomem *reg = type_c->reg_base + USB_TYPEC_CTRL; + unsigned long flags; + + detect_type_c_state(type_c); + + spin_lock_irqsave(&type_c->lock, flags); + + if (type_c->connect_change) { + dev_dbg(dev, "%s: IN %s mode to %s (at %s interrupt) int_status=0x%x, cc_status=0x%x", + __func__, + type_c->cc_mode == IN_HOST_MODE ? "host" : "device", + type_c->is_attach ? "attach" : "detach", + type_c->at_cc1 ? "cc1" : "cc2", + type_c->int_status, type_c->cc_status); + + /* clear interrupt status */ + writel(~ALL_CC_INT_STS & readl(reg), reg); + + cancel_delayed_work(&type_c->delayed_work); + schedule_delayed_work(&type_c->delayed_work, msecs_to_jiffies(0)); + } else { + static int local_count; + + /* if no connect_change, we keep the status to avoid status lose */ + if (local_count++ > 10) { + /* clear interrupt status */ + writel(~ALL_CC_INT_STS & readl(reg), reg); + local_count = 0; + } + } + + spin_unlock_irqrestore(&type_c->lock, flags); + + return IRQ_HANDLED; +} + +static int type_c_port_dr_set(struct typec_port *port, + enum typec_data_role role) +{ + struct type_c_data *type_c = typec_get_drvdata(port); + u32 enable_cc; + unsigned long flags; + + spin_lock_irqsave(&type_c->lock, flags); + enable_cc = type_c->at_cc1 ? ENABLE_CC1 : ENABLE_CC2; + spin_unlock_irqrestore(&type_c->lock, flags); + + if (role == TYPEC_HOST) + switch_type_c_dr_mode(type_c, USB_DR_MODE_HOST, enable_cc); + else if (role == TYPEC_DEVICE) + switch_type_c_dr_mode(type_c, USB_DR_MODE_PERIPHERAL, enable_cc); + else + switch_type_c_dr_mode(type_c, 0, DISABLE_CC); + + return 0; +} + +static const struct typec_operations type_c_port_ops = { + .dr_set = type_c_port_dr_set, +}; + +#ifdef CONFIG_DEBUG_FS +static int type_c_parameter_show(struct seq_file *s, void *unused) +{ + struct type_c_data *type_c = s->private; + struct type_c_cfg *type_c_cfg = type_c->type_c_cfg; + struct cc_param *cc_param; + unsigned long flags; + + spin_lock_irqsave(&type_c->lock, flags); + + seq_printf(s, "cc_dfp_mode %s\n", + ({ char *tmp; + switch (type_c_cfg->cc_dfp_mode) { + case CC_MODE_DFP_USB: + tmp = "CC_MODE_DFP_USB"; break; + case CC_MODE_DFP_1_5: + tmp = "CC_MODE_DFP_1_5"; break; + case CC_MODE_DFP_3_0: + tmp = "CC_MODE_DFP_3_0"; break; + default: + tmp = "?"; break; + } tmp; })); + + seq_printf(s, "dfp_mode_rp_en 0x%x\n", type_c->dfp_mode_rp_en); + seq_printf(s, "ufp_mode_rd_en 0x%x\n", type_c->ufp_mode_rd_en); + seq_printf(s, "cc1_code 0x%x\n", type_c->cc1_code); + seq_printf(s, "cc2_code 0x%x\n", type_c->cc2_code); + seq_printf(s, "cc1_vref 0x%x\n", type_c->cc1_vref); + seq_printf(s, "cc2_vref 0x%x\n", type_c->cc2_vref); + seq_printf(s, "debounce 0x%x\n", type_c->debounce); + seq_puts(s, "\n"); + + cc_param = &type_c_cfg->cc1_param; + seq_puts(s, "cc1_param:\n"); + seq_printf(s, " rp_4p7k_code 0x%x\n", cc_param->rp_4p7k_code); + seq_printf(s, " rp_36k_code 0x%x\n", cc_param->rp_36k_code); + seq_printf(s, " rp_12k_code 0x%x\n", cc_param->rp_12k_code); + seq_printf(s, " rd_code 0x%x\n", cc_param->rd_code); + seq_printf(s, " vref_2p6v 0x%x\n", cc_param->vref_2p6v); + seq_printf(s, " vref_1p23v 0x%x\n", cc_param->vref_1p23v); + seq_printf(s, " vref_0p8v 0x%x\n", cc_param->vref_0p8v); + seq_printf(s, " vref_0p66v 0x%x\n", cc_param->vref_0p66v); + seq_printf(s, " vref_0p4v 0x%x\n", cc_param->vref_0p4v); + seq_printf(s, " vref_0p2v 0x%x\n", cc_param->vref_0p2v); + seq_printf(s, " vref_1_1p6v 0x%x\n", cc_param->vref_1_1p6v); + seq_printf(s, " vref_0_1p6v 0x%x\n", cc_param->vref_0_1p6v); + + cc_param = &type_c_cfg->cc2_param; + seq_puts(s, "cc2_param:\n"); + seq_printf(s, " rp_4p7k_code 0x%x\n", cc_param->rp_4p7k_code); + seq_printf(s, " rp_36k_code 0x%x\n", cc_param->rp_36k_code); + seq_printf(s, " rp_12k_code 0x%x\n", cc_param->rp_12k_code); + seq_printf(s, " rd_code 0x%x\n", cc_param->rd_code); + seq_printf(s, " vref_2p6v 0x%x\n", cc_param->vref_2p6v); + seq_printf(s, " vref_1p23v 0x%x\n", cc_param->vref_1p23v); + seq_printf(s, " vref_0p8v 0x%x\n", cc_param->vref_0p8v); + seq_printf(s, " vref_0p66v 0x%x\n", cc_param->vref_0p66v); + seq_printf(s, " vref_0p4v 0x%x\n", cc_param->vref_0p4v); + seq_printf(s, " vref_0p2v 0x%x\n", cc_param->vref_0p2v); + seq_printf(s, " vref_1_1p6v 0x%x\n", cc_param->vref_1_1p6v); + seq_printf(s, " vref_0_1p6v 0x%x\n", cc_param->vref_0_1p6v); + + spin_unlock_irqrestore(&type_c->lock, flags); + + return 0; +} + +static int type_c_parameter_open(struct inode *inode, struct file *file) +{ + return single_open(file, type_c_parameter_show, inode->i_private); +} + +static const struct file_operations type_c_parameter_fops = { + .open = type_c_parameter_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static int type_c_status_show(struct seq_file *s, void *unused) +{ + struct type_c_data *type_c = s->private; + unsigned long flags; + + spin_lock_irqsave(&type_c->lock, flags); + + seq_printf(s, "In %s mode %s at %s (cc_status=0x%x)\n", + type_c->cc_mode == IN_HOST_MODE ? "host" : "device", + type_c->is_attach ? "attach" : "detach", + type_c->at_cc1 ? "cc1" : "cc2", type_c->cc_status); + + seq_printf(s, "Read Register (type_c_ctrl_cc1_0=0x%x)\n", + readl(type_c->reg_base + 0x0)); + seq_printf(s, "Read Register (type_c_ctrl_cc1_1=0x%x)\n", + readl(type_c->reg_base + 0x4)); + seq_printf(s, "Read Register (type_c_ctrl_cc2_0=0x%x)\n", + readl(type_c->reg_base + 0x8)); + seq_printf(s, "Read Register (type_c_ctrl_cc2_1=0x%x)\n", + readl(type_c->reg_base + 0xc)); + seq_printf(s, "Read Register (type_c_status=0x%x)\n", + readl(type_c->reg_base + 0x10)); + seq_printf(s, "Read Register (type_c_ctrl=0x%x)\n", + readl(type_c->reg_base + 0x14)); + + spin_unlock_irqrestore(&type_c->lock, flags); + + return 0; +} + +static int type_c_status_open(struct inode *inode, struct file *file) +{ + return single_open(file, type_c_status_show, inode->i_private); +} + +static const struct file_operations type_c_status_fops = { + .open = type_c_status_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static inline void create_debug_files(struct type_c_data *type_c) +{ + type_c->debug_dir = debugfs_create_dir("type_c", usb_debug_root); + + debugfs_create_file("parameter", 0444, type_c->debug_dir, type_c, + &type_c_parameter_fops); + + debugfs_create_file("status", 0444, type_c->debug_dir, type_c, + &type_c_status_fops); +} + +static inline void remove_debug_files(struct type_c_data *type_c) +{ + debugfs_remove_recursive(type_c->debug_dir); +} +#else +static inline void create_debug_files(struct type_c_data *type_c) { } +static inline void remove_debug_files(struct type_c_data *type_c) { } +#endif /* CONFIG_DEBUG_FS */ + +/* Init and probe */ + +static inline s8 get_value(s8 value) +{ + return (((s8)value & 0x8) ? (-(s8)(0x7 & value)) : ((s8)(value))); +} + +static int __updated_type_c_parameter_by_efuse(struct type_c_data *type_c) +{ + struct type_c_cfg *type_c_cfg = type_c->type_c_cfg; + struct cc_param *cc_param; + struct nvmem_cell *cell; + s8 cc1_4p7k = 0; + s8 cc1_12k = 0; + s8 cc1_0p2v = 0; + s8 cc1_0p8v = 0; + s8 cc1_2p6v = 0; + s8 cc1_0p66v = 0; + s8 cc1_1p23v = 0; + s8 cc2_4p7k = 0; + s8 cc2_12k = 0; + s8 cc2_0p2v = 0; + s8 cc2_0p8v = 0; + s8 cc2_2p6v = 0; + s8 cc2_0p66v = 0; + s8 cc2_1p23v = 0; + + cell = nvmem_cell_get(type_c->dev, "usb-cal"); + if (IS_ERR(cell)) { + dev_warn(type_c->dev, "%s failed to get usb-cal: %ld\n", + __func__, PTR_ERR(cell)); + } else { + unsigned char *buf; + size_t buf_size; + int value_size = 4; + int value_mask = (BIT(value_size) - 1); + + buf = nvmem_cell_read(cell, &buf_size); + + cc1_0p2v = get_value((buf[0] >> value_size * 0) & value_mask); + cc1_0p8v = get_value((buf[0] >> value_size * 1) & value_mask); + cc1_2p6v = get_value((buf[1] >> value_size * 0) & value_mask); + cc1_0p66v = get_value((buf[1] >> value_size * 1) & value_mask); + cc1_1p23v = get_value((buf[2] >> value_size * 0) & value_mask); + + cc2_0p2v = get_value((buf[3] >> value_size * 0) & value_mask); + cc2_0p8v = get_value((buf[3] >> value_size * 1) & value_mask); + cc2_2p6v = get_value((buf[4] >> value_size * 0) & value_mask); + cc2_0p66v = get_value((buf[4] >> value_size * 1) & value_mask); + cc2_1p23v = get_value((buf[5] >> value_size * 0) & value_mask); + + cc1_4p7k = get_value((buf[6] >> value_size * 0) & value_mask); + cc1_12k = get_value((buf[6] >> value_size * 1) & value_mask); + cc2_4p7k = get_value((buf[7] >> value_size * 0) & value_mask); + cc2_12k = get_value((buf[7] >> value_size * 1) & value_mask); + + kfree(buf); + nvmem_cell_put(cell); + } + + dev_dbg(type_c->dev, "check efuse cc1_4p7k=%d cc1_12k=%d cc2_4p7k=%d cc2_12k=%d\n", + cc1_4p7k, cc1_12k, cc2_4p7k, cc2_12k); + dev_dbg(type_c->dev, "check efuse cc1_0p2v=%d cc1_0p8v=%d cc1_2p6v=%d cc1_0p66v=%d cc1_1p23v=%d\n", + cc1_0p2v, cc1_0p8v, cc1_2p6v, cc1_0p66v, cc1_1p23v); + dev_dbg(type_c->dev, "check efuse cc2_0p2v=%d cc2_0p8v=%d cc2_2p6v=%d cc2_0p66v=%d cc2_1p23v=%d\n", + cc2_0p2v, cc2_0p8v, cc2_2p6v, cc2_0p66v, cc2_1p23v); + + cc_param = &type_c_cfg->cc1_param; + cc_param->rp_4p7k_code = cc_param->rp_4p7k_code + cc1_4p7k; + cc_param->rp_12k_code = cc_param->rp_12k_code + cc1_12k; + + cc_param->vref_1p23v = cc_param->vref_1p23v + cc1_1p23v; + cc_param->vref_0p66v = cc_param->vref_0p66v + cc1_0p66v; + cc_param->vref_2p6v = cc_param->vref_2p6v + cc1_2p6v; + cc_param->vref_0p8v = cc_param->vref_0p8v + cc1_0p8v; + cc_param->vref_0p2v = cc_param->vref_0p2v + cc1_0p2v; + + cc_param = &type_c_cfg->cc2_param; + cc_param->rp_4p7k_code = cc_param->rp_4p7k_code + cc2_4p7k; + cc_param->rp_12k_code = cc_param->rp_12k_code + cc2_12k; + + cc_param->vref_1p23v = cc_param->vref_1p23v + cc2_1p23v; + cc_param->vref_0p66v = cc_param->vref_0p66v + cc2_0p66v; + cc_param->vref_2p6v = cc_param->vref_2p6v + cc2_2p6v; + cc_param->vref_0p8v = cc_param->vref_0p8v + cc2_0p8v; + cc_param->vref_0p2v = cc_param->vref_0p2v + cc2_0p2v; + + return 0; +} + +static int __updated_type_c_parameter_by_efuse_v2(struct type_c_data *type_c) +{ + struct type_c_cfg *type_c_cfg = type_c->type_c_cfg; + struct cc_param *cc_param; + struct nvmem_cell *cell; + s8 cc1_4p7k = 0; + s8 cc1_12k = 0; + s8 cc1_0p2v = 0; + s8 cc1_0p8v = 0; + s8 cc1_2p6v = 0; + s8 cc1_0p66v = 0; + s8 cc1_1p23v = 0; + s8 cc2_4p7k = 0; + s8 cc2_12k = 0; + s8 cc2_0p2v = 0; + s8 cc2_0p8v = 0; + s8 cc2_2p6v = 0; + s8 cc2_0p66v = 0; + s8 cc2_1p23v = 0; + + cell = nvmem_cell_get(type_c->dev, "usb-type-c-cal"); + if (IS_ERR(cell)) { + dev_warn(type_c->dev, "%s failed to get usb-type-c-cal: %ld\n", + __func__, PTR_ERR(cell)); + } else { + unsigned char *buf; + size_t buf_size; + int value_size = 0; + int value_mask = (BIT(value_size) - 1); + + buf = nvmem_cell_read(cell, &buf_size); + + value_size = 5; + value_mask = (BIT(value_size) - 1); + cc1_4p7k = buf[0] & value_mask; + cc1_12k = buf[1] & value_mask; + cc2_4p7k = buf[2] & value_mask; + cc2_12k = buf[3] & value_mask; + + value_size = 4; + value_mask = (BIT(value_size) - 1); + cc1_0p2v = (buf[4] >> value_size * 0) & value_mask; + cc1_0p66v = (buf[4] >> value_size * 1) & value_mask; + cc1_0p8v = (buf[5] >> value_size * 0) & value_mask; + cc1_1p23v = (buf[5] >> value_size * 1) & value_mask; + cc1_2p6v = (buf[6] >> value_size * 0) & value_mask; + + cc2_0p2v = (buf[6] >> value_size * 1) & value_mask; + cc2_0p66v = (buf[7] >> value_size * 0) & value_mask; + cc2_0p8v = (buf[7] >> value_size * 1) & value_mask; + cc2_1p23v = (buf[8] >> value_size * 0) & value_mask; + cc2_2p6v = (buf[8] >> value_size * 1) & value_mask; + + kfree(buf); + nvmem_cell_put(cell); + } + + dev_dbg(type_c->dev, "check efuse v2 cc1_4p7k=%d cc1_12k=%d cc2_4p7k=%d cc2_12k=%d\n", + cc1_4p7k, cc1_12k, cc2_4p7k, cc2_12k); + dev_dbg(type_c->dev, "check efuse v2 cc1_0p2v=%d cc1_0p8v=%d cc1_2p6v=%d cc1_0p66v=%d cc1_1p23v=%d\n", + cc1_0p2v, cc1_0p8v, cc1_2p6v, cc1_0p66v, cc1_1p23v); + dev_dbg(type_c->dev, "check efuse v2 cc2_0p2v=%d cc2_0p8v=%d cc2_2p6v=%d cc2_0p66v=%d cc2_1p23v=%d\n", + cc2_0p2v, cc2_0p8v, cc2_2p6v, cc2_0p66v, cc2_1p23v); + + cc_param = &type_c_cfg->cc1_param; + if (cc1_4p7k) + cc_param->rp_4p7k_code = cc1_4p7k; + if (cc1_12k) + cc_param->rp_12k_code = cc1_12k; + + if (cc1_1p23v) + cc_param->vref_1p23v = cc1_1p23v; + if (cc1_0p66v) + cc_param->vref_0p66v = cc1_0p66v; + if (cc1_2p6v) + cc_param->vref_2p6v = cc1_2p6v; + if (cc1_0p8v) + cc_param->vref_0p8v = cc1_0p8v; + if (cc1_0p2v) + cc_param->vref_0p2v = cc1_0p2v; + + cc_param = &type_c_cfg->cc2_param; + if (cc2_4p7k) + cc_param->rp_4p7k_code = cc2_4p7k; + if (cc2_12k) + cc_param->rp_12k_code = cc2_12k; + + if (cc2_1p23v) + cc_param->vref_1p23v = cc2_1p23v; + if (cc2_0p66v) + cc_param->vref_0p66v = cc2_0p66v; + if (cc2_2p6v) + cc_param->vref_2p6v = cc2_2p6v; + if (cc2_0p8v) + cc_param->vref_0p8v = cc2_0p8v; + if (cc2_0p2v) + cc_param->vref_0p2v = cc2_0p2v; + + return 0; +} + +static void get_default_type_c_parameter(struct type_c_data *type_c) +{ + void __iomem *reg; + int val; + + type_c->dfp_mode_rp_en = dfp_mode(CC_MODE_DFP_3_0) | EN_RP4P7K; + type_c->ufp_mode_rd_en = EN_RD; + + reg = type_c->reg_base + USB_TYPEC_CTRL_CC1_0; + val = readl(reg); + type_c->cc1_code = CC_CODE_MASK & val; + + reg = type_c->reg_base + USB_TYPEC_CTRL_CC2_0; + val = readl(reg); + type_c->cc2_code = CC_CODE_MASK & val; + + reg = type_c->reg_base + USB_TYPEC_CTRL_CC1_1; + val = readl(reg); + type_c->cc1_vref = val; + + reg = type_c->reg_base + USB_TYPEC_CTRL_CC2_1; + val = readl(reg); + type_c->cc2_vref = val; + + reg = type_c->reg_base + USB_TYPEC_CTRL; + val = readl(reg); + type_c->debounce = DEBOUNCE_TIME_MASK & val; +} + +static int setup_type_c_parameter(struct type_c_data *type_c) +{ + struct type_c_cfg *type_c_cfg = type_c->type_c_cfg; + struct cc_param *cc_param; + struct soc_device_attribute rtk_soc_efuse_v1[] = { + { .family = "Realtek Phoenix",}, + { .family = "Realtek Kylin",}, + { .family = "Realtek Hercules",}, + { .family = "Realtek Thor",}, + { .family = "Realtek Hank",}, + { .family = "Realtek Groot",}, + { .family = "Realtek Stark",}, + { .family = "Realtek Parker",}, + { /* empty */ } + }; + + if (type_c_cfg->use_defalut_parameter) { + get_default_type_c_parameter(type_c); + return 0; + } + + if (soc_device_match(rtk_soc_efuse_v1)) + __updated_type_c_parameter_by_efuse(type_c); + else + __updated_type_c_parameter_by_efuse_v2(type_c); + + /* + * UFP rd vref_ufp : 1p23v, 0p66v, 0p2v + * DFP_USB rp36k vref_dfp_usb: 0_1p6v, 0p2v, unused + * DFP_1.5 rp12k vref_dfp_1_5: 1_1p6v, 0p4v, 0p2v + * DFP_3.0 rp4p7k vref_dfp_3_0: 2p6v, 0p8v, 0p2v + */ + + switch (type_c_cfg->cc_dfp_mode) { + case CC_MODE_DFP_USB: + type_c->dfp_mode_rp_en = dfp_mode(CC_MODE_DFP_USB) | EN_RP36K; + break; + case CC_MODE_DFP_1_5: + type_c->dfp_mode_rp_en = dfp_mode(CC_MODE_DFP_1_5) | EN_RP12K; + break; + case CC_MODE_DFP_3_0: + type_c->dfp_mode_rp_en = dfp_mode(CC_MODE_DFP_3_0) | EN_RP4P7K; + break; + default: + dev_err(type_c->dev, "%s: unknown cc_dfp_mode %d\n", + __func__, type_c_cfg->cc_dfp_mode); + } + + type_c->ufp_mode_rd_en = EN_RD; + + cc_param = &type_c_cfg->cc1_param; + type_c->cc1_code = rp4pk_code(cc_param->rp_4p7k_code) | + rp36k_code(cc_param->rp_36k_code) | + rp12k_code(cc_param->rp_12k_code) | + rd_code(cc_param->rd_code); + + if (type_c_cfg->parameter_ver == PARAMETER_V0) + type_c->cc1_vref = V0_vref_2p6v(cc_param->vref_2p6v) | + V0_vref_1p23v(cc_param->vref_1p23v) | + V0_vref_0p8v(cc_param->vref_0p8v) | + V0_vref_0p66v(cc_param->vref_0p66v) | + V0_vref_0p4v(cc_param->vref_0p4v) | + V0_vref_0p2v(cc_param->vref_0p2v) | + V0_vref_1_1p6v(cc_param->vref_1_1p6v) | + V0_vref_0_1p6v(cc_param->vref_0_1p6v); + else if (type_c_cfg->parameter_ver == PARAMETER_V1) + type_c->cc1_vref = V1_vref_2p6v(cc_param->vref_2p6v) | + V1_vref_1p23v(cc_param->vref_1p23v) | + V1_vref_0p8v(cc_param->vref_0p8v) | + V1_vref_0p66v(cc_param->vref_0p66v) | + V1_vref_0p4v(cc_param->vref_0p4v) | + V1_vref_0p2v(cc_param->vref_0p2v) | + V1_vref_1_1p6v(cc_param->vref_1_1p6v) | + V1_vref_0_1p6v(cc_param->vref_0_1p6v); + else + dev_err(type_c->dev, "%s: unknown parameter_ver %d\n", + __func__, type_c_cfg->parameter_ver); + + cc_param = &type_c_cfg->cc2_param; + type_c->cc2_code = rp4pk_code(cc_param->rp_4p7k_code) + | rp36k_code(cc_param->rp_36k_code) + | rp12k_code(cc_param->rp_12k_code) + | rd_code(cc_param->rd_code); + + if (type_c_cfg->parameter_ver == PARAMETER_V0) + type_c->cc2_vref = V0_vref_2p6v(cc_param->vref_2p6v) | + V0_vref_1p23v(cc_param->vref_1p23v) | + V0_vref_0p8v(cc_param->vref_0p8v) | + V0_vref_0p66v(cc_param->vref_0p66v) | + V0_vref_0p4v(cc_param->vref_0p4v) | + V0_vref_0p2v(cc_param->vref_0p2v) | + V0_vref_1_1p6v(cc_param->vref_1_1p6v) | + V0_vref_0_1p6v(cc_param->vref_0_1p6v); + else if (type_c_cfg->parameter_ver == PARAMETER_V1) + type_c->cc2_vref = V1_vref_2p6v(cc_param->vref_2p6v) | + V1_vref_1p23v(cc_param->vref_1p23v) | + V1_vref_0p8v(cc_param->vref_0p8v) | + V1_vref_0p66v(cc_param->vref_0p66v) | + V1_vref_0p4v(cc_param->vref_0p4v) | + V1_vref_0p2v(cc_param->vref_0p2v) | + V1_vref_1_1p6v(cc_param->vref_1_1p6v) | + V1_vref_0_1p6v(cc_param->vref_0_1p6v); + else + dev_err(type_c->dev, "%s: unknown parameter_ver %d\n", + __func__, type_c_cfg->parameter_ver); + + type_c->debounce = (type_c_cfg->debounce_val << 1) | DEBOUNCE_EN; + + return 0; +} + +static int extcon_rtk_type_c_init(struct type_c_data *type_c) +{ + struct device *dev = type_c->dev; + unsigned long flags; + void __iomem *reg; + int val; + + spin_lock_irqsave(&type_c->lock, flags); + + /* set parameter */ + reg = type_c->reg_base + USB_TYPEC_CTRL_CC1_0; + val = readl(reg); + val = (~CC_CODE_MASK & val) | (type_c->cc1_code & CC_CODE_MASK); + writel(val, reg); + + reg = type_c->reg_base + USB_TYPEC_CTRL_CC2_0; + val = readl(reg); + val = (~CC_CODE_MASK & val) | (type_c->cc2_code & CC_CODE_MASK); + + reg = type_c->reg_base + USB_TYPEC_CTRL_CC1_1; + writel(type_c->cc1_vref, reg); + + reg = type_c->reg_base + USB_TYPEC_CTRL_CC2_1; + writel(type_c->cc2_vref, reg); + + reg = type_c->reg_base + USB_TYPEC_CTRL; + val = readl(reg); + val = (~DEBOUNCE_TIME_MASK & val) | (type_c->debounce & DEBOUNCE_TIME_MASK); + + dev_info(dev, "First check USB_DR_MODE_PERIPHERAL"); + type_c->cc_mode = IN_DEVICE_MODE; + type_c->is_attach = IN_DETACH; + type_c->connect_change = CONNECT_NO_CHANGE; + + detect_host(type_c); + + spin_unlock_irqrestore(&type_c->lock, flags); + + schedule_delayed_work(&type_c->delayed_work, msecs_to_jiffies(0)); + + if (!type_c->port) { + struct typec_capability typec_cap = { }; + struct fwnode_handle *fwnode; + const char *buf; + int ret; + + typec_cap.revision = USB_TYPEC_REV_1_0; + typec_cap.prefer_role = TYPEC_NO_PREFERRED_ROLE; + typec_cap.driver_data = type_c; + typec_cap.ops = &type_c_port_ops; + + fwnode = device_get_named_child_node(dev, "connector"); + if (!fwnode) + return -EINVAL; + + ret = fwnode_property_read_string(fwnode, "power-role", &buf); + if (ret) { + dev_err(dev, "power-role not found: %d\n", ret); + return ret; + } + + ret = typec_find_port_power_role(buf); + if (ret < 0) + return ret; + typec_cap.type = ret; + + ret = fwnode_property_read_string(fwnode, "data-role", &buf); + if (ret) { + dev_err(dev, "data-role not found: %d\n", ret); + return ret; + } + + ret = typec_find_port_data_role(buf); + if (ret < 0) + return ret; + typec_cap.data = ret; + + type_c->port = typec_register_port(type_c->dev, &typec_cap); + if (IS_ERR(type_c->port)) + return PTR_ERR(type_c->port); + } + + return 0; +} + +static int extcon_rtk_type_c_edev_register(struct type_c_data *type_c) +{ + struct device *dev = type_c->dev; + int ret = 0; + + type_c->edev = devm_extcon_dev_allocate(dev, usb_type_c_cable); + if (IS_ERR(type_c->edev)) { + dev_err(dev, "failed to allocate extcon device\n"); + return -ENOMEM; + } + + ret = devm_extcon_dev_register(dev, type_c->edev); + if (ret < 0) { + dev_err(dev, "failed to register extcon device\n"); + return ret; + } + + extcon_set_property_capability(type_c->edev, EXTCON_USB, + EXTCON_PROP_USB_VBUS); + extcon_set_property_capability(type_c->edev, EXTCON_USB, + EXTCON_PROP_USB_TYPEC_POLARITY); + extcon_set_property_capability(type_c->edev, EXTCON_USB, + EXTCON_PROP_USB_SS); + + extcon_set_property_capability(type_c->edev, EXTCON_USB_HOST, + EXTCON_PROP_USB_VBUS); + extcon_set_property_capability(type_c->edev, EXTCON_USB_HOST, + EXTCON_PROP_USB_TYPEC_POLARITY); + extcon_set_property_capability(type_c->edev, EXTCON_USB_HOST, + EXTCON_PROP_USB_SS); + + return ret; +} + +static int extcon_rtk_type_c_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct type_c_data *type_c; + const struct type_c_cfg *type_c_cfg; + int ret = 0; + + type_c = devm_kzalloc(dev, sizeof(*type_c), GFP_KERNEL); + if (!type_c) + return -ENOMEM; + + type_c->reg_base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(type_c->reg_base)) + return PTR_ERR(type_c->reg_base); + + type_c->dev = dev; + + type_c->irq = irq_of_parse_and_map(pdev->dev.of_node, 0); + if (type_c->irq <= 0) { + dev_err(&pdev->dev, "Type C driver with no IRQ. Check %s setup!\n", + dev_name(&pdev->dev)); + ret = -ENODEV; + goto err; + } + + ret = devm_request_irq(dev, type_c->irq, type_c_detect_irq, + IRQF_SHARED, "type_c_detect", type_c); + + spin_lock_init(&type_c->lock); + + type_c->rd_ctrl_gpio_desc = NULL; + if (soc_device_match(rtk_soc_kylin)) { + struct gpio_desc *gpio; + + gpio = fwnode_gpiod_get_index(of_fwnode_handle(dev->of_node), + "realtek,rd-ctrl-gpios", + 0, GPIOD_OUT_HIGH, "rd-ctrl-gpio"); + if (IS_ERR(gpio)) { + dev_err(dev, "Error rd_ctrl-gpios no found (err=%d)\n", + (int)PTR_ERR(gpio)); + } else { + type_c->rd_ctrl_gpio_desc = gpio; + dev_dbg(dev, "%s get rd-ctrl-gpios (id=%d) OK\n", + __func__, desc_to_gpio(gpio)); + } + } + + type_c_cfg = of_device_get_match_data(dev); + if (!type_c_cfg) { + dev_err(dev, "type_c config are not assigned!\n"); + ret = -EINVAL; + goto err; + } + + type_c->type_c_cfg = devm_kzalloc(dev, sizeof(*type_c_cfg), GFP_KERNEL); + + memcpy(type_c->type_c_cfg, type_c_cfg, sizeof(*type_c_cfg)); + + if (setup_type_c_parameter(type_c)) { + dev_err(dev, "ERROR: %s to setup type c parameter!!", __func__); + ret = -EINVAL; + goto err; + } + + INIT_DELAYED_WORK(&type_c->delayed_work, host_device_switch); + + ret = extcon_rtk_type_c_init(type_c); + if (ret) { + dev_err(dev, "%s failed to init type_c\n", __func__); + goto err; + } + + platform_set_drvdata(pdev, type_c); + + ret = extcon_rtk_type_c_edev_register(type_c); + + create_debug_files(type_c); + + return 0; + +err: + dev_err(&pdev->dev, "%s: Probe fail, %d\n", __func__, ret); + + return ret; +} + +static void extcon_rtk_type_c_remove(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct type_c_data *type_c = dev_get_drvdata(dev); + u32 default_ctrl; + unsigned long flags; + + remove_debug_files(type_c); + + if (type_c->port) { + typec_unregister_port(type_c->port); + type_c->port = NULL; + } + + cancel_delayed_work_sync(&type_c->delayed_work); + flush_delayed_work(&type_c->delayed_work); + WARN_ON_ONCE(delayed_work_pending(&type_c->delayed_work)); + + spin_lock_irqsave(&type_c->lock, flags); + /* disable interrupt */ + default_ctrl = readl(type_c->reg_base + USB_TYPEC_CTRL) & + DEBOUNCE_TIME_MASK; + writel(default_ctrl, type_c->reg_base + USB_TYPEC_CTRL); + + /* disable cc detect, rp, rd */ + writel(PLR_EN, type_c->reg_base + USB_TYPEC_CTRL_CC1_0); + writel(0, type_c->reg_base + USB_TYPEC_CTRL_CC2_0); + + spin_unlock_irqrestore(&type_c->lock, flags); + + if (type_c->rd_ctrl_gpio_desc) + gpiod_put(type_c->rd_ctrl_gpio_desc); + type_c->rd_ctrl_gpio_desc = NULL; + + free_irq(type_c->irq, type_c); +} + +static const struct type_c_cfg rtd1295_type_c_cfg = { + .parameter_ver = PARAMETER_V0, + .cc_dfp_mode = CC_MODE_DFP_3_0, + .cc1_param = { .rp_4p7k_code = 0xb, + .rp_36k_code = 0x17, + .rp_12k_code = 0x10, + .rd_code = 0, + .ra_code = 0, + .vref_2p6v = 0x0, + .vref_1p23v = 0x0, + .vref_0p8v = 0x3, + .vref_0p66v = 0x0, + .vref_0p4v = 0x0, + .vref_0p2v = 0x4, + .vref_1_1p6v = 0, + .vref_0_1p6v = 0 }, + .cc2_param = { .rp_4p7k_code = 0xc, + .rp_36k_code = 0x17, + .rp_12k_code = 0x12, + .rd_code = 0, + .ra_code = 0, + .vref_2p6v = 0x2, + .vref_1p23v = 0x0, + .vref_0p8v = 0x3, + .vref_0p66v = 0x0, + .vref_0p4v = 0x0, + .vref_0p2v = 0x5, + .vref_1_1p6v = 0, + .vref_0_1p6v = 0 }, + .debounce_val = 0x7f, /* 1b,1us 7f,4.7us */ + .use_defalut_parameter = false, +}; + +static const struct type_c_cfg rtd1395_type_c_cfg = { + .parameter_ver = PARAMETER_V0, + .cc_dfp_mode = CC_MODE_DFP_3_0, + .cc1_param = { .rp_4p7k_code = 0xc, + .rp_36k_code = 0xb, + .rp_12k_code = 0xe, + .rd_code = 0x10, + .ra_code = 0x0, + .vref_2p6v = 0x0, + .vref_1p23v = 0x1, + .vref_0p8v = 0x0, + .vref_0p66v = 0x0, + .vref_0p4v = 0x3, + .vref_0p2v = 0x0, + .vref_1_1p6v = 0x7, + .vref_0_1p6v = 0x7 }, + .cc2_param = { .rp_4p7k_code = 0xb, + .rp_36k_code = 0x9, + .rp_12k_code = 0xe, + .rd_code = 0xf, + .ra_code = 0x0, + .vref_2p6v = 0x1, + .vref_1p23v = 0x3, + .vref_0p8v = 0x3, + .vref_0p66v = 0x2, + .vref_0p4v = 0x3, + .vref_0p2v = 0x2, + .vref_1_1p6v = 0x7, + .vref_0_1p6v = 0x7 }, + .debounce_val = 0x7f, /* 1b,1us 7f,4.7us */ + .use_defalut_parameter = false, +}; + +static const struct type_c_cfg rtd1619_type_c_cfg = { + .parameter_ver = PARAMETER_V0, + .cc_dfp_mode = CC_MODE_DFP_3_0, + .cc1_param = { .rp_4p7k_code = 0xc, + .rp_36k_code = 0xf, + .rp_12k_code = 0xe, + .rd_code = 0x11, + .ra_code = 0x0, + .vref_2p6v = 0x5, + .vref_1p23v = 0x7, + .vref_0p8v = 0xa, + .vref_0p66v = 0xa, + .vref_0p4v = 0x3, + .vref_0p2v = 0x2, + .vref_1_1p6v = 0x7, + .vref_0_1p6v = 0x7 }, + .cc2_param = { .rp_4p7k_code = 0xc, + .rp_36k_code = 0xf, + .rp_12k_code = 0xe, + .rd_code = 0xf, + .ra_code = 0x0, + .vref_2p6v = 0x5, + .vref_1p23v = 0x8, + .vref_0p8v = 0xa, + .vref_0p66v = 0xa, + .vref_0p4v = 0x3, + .vref_0p2v = 0x2, + .vref_1_1p6v = 0x7, + .vref_0_1p6v = 0x7 }, + .debounce_val = 0x7f, /* 1b,1us 7f,4.7us */ + .use_defalut_parameter = false, +}; + +static const struct type_c_cfg rtd1319_type_c_cfg = { + .parameter_ver = PARAMETER_V0, + .cc_dfp_mode = CC_MODE_DFP_1_5, + .cc1_param = { .rp_4p7k_code = 0x9, + .rp_36k_code = 0xe, + .rp_12k_code = 0x9, + .rd_code = 0x9, + .ra_code = 0x7, + .vref_2p6v = 0x3, + .vref_1p23v = 0x7, + .vref_0p8v = 0x7, + .vref_0p66v = 0x6, + .vref_0p4v = 0x2, + .vref_0p2v = 0x3, + .vref_1_1p6v = 0x4, + .vref_0_1p6v = 0x7 }, + .cc2_param = { .rp_4p7k_code = 0x8, + .rp_36k_code = 0xe, + .rp_12k_code = 0x9, + .rd_code = 0x9, + .ra_code = 0x7, + .vref_2p6v = 0x3, + .vref_1p23v = 0x7, + .vref_0p8v = 0x7, + .vref_0p66v = 0x6, + .vref_0p4v = 0x3, + .vref_0p2v = 0x3, + .vref_1_1p6v = 0x6, + .vref_0_1p6v = 0x7 }, + .debounce_val = 0x7f, /* 1b,1us 7f,4.7us */ + .use_defalut_parameter = false, +}; + +static const struct type_c_cfg rtd1312c_type_c_cfg = { + .parameter_ver = PARAMETER_V0, + .cc_dfp_mode = CC_MODE_DFP_1_5, + .cc1_param = { .rp_4p7k_code = 0xe, + .rp_36k_code = 0xc, + .rp_12k_code = 0xc, + .rd_code = 0xa, + .ra_code = 0x3, + .vref_2p6v = 0xa, + .vref_1p23v = 0x7, + .vref_0p8v = 0x7, + .vref_0p66v = 0x7, + .vref_0p4v = 0x4, + .vref_0p2v = 0x4, + .vref_1_1p6v = 0x7, + .vref_0_1p6v = 0x7 }, + .cc2_param = { .rp_4p7k_code = 0xe, + .rp_36k_code = 0xc, + .rp_12k_code = 0xc, + .rd_code = 0xa, + .ra_code = 0x3, + .vref_2p6v = 0xa, + .vref_1p23v = 0x7, + .vref_0p8v = 0x7, + .vref_0p66v = 0x7, + .vref_0p4v = 0x4, + .vref_0p2v = 0x4, + .vref_1_1p6v = 0x7, + .vref_0_1p6v = 0x7 }, + .debounce_val = 0x7f, /* 1b,1us 7f,4.7us */ + .use_defalut_parameter = false, +}; + +static const struct type_c_cfg rtd1619b_type_c_cfg = { + .parameter_ver = PARAMETER_V1, + .cc_dfp_mode = CC_MODE_DFP_1_5, + .cc1_param = { .rp_4p7k_code = 0xf, + .rp_36k_code = 0xf, + .rp_12k_code = 0xf, + .rd_code = 0xf, + .ra_code = 0x7, + .vref_2p6v = 0x9, + .vref_1p23v = 0x7, + .vref_0p8v = 0x9, + .vref_0p66v = 0x8, + .vref_0p4v = 0x7, + .vref_0p2v = 0x9, + .vref_1_1p6v = 0x7, + .vref_0_1p6v = 0x7 }, + .cc2_param = { .rp_4p7k_code = 0xf, + .rp_36k_code = 0xf, + .rp_12k_code = 0xf, + .rd_code = 0xf, + .ra_code = 0x7, + .vref_1p23v = 0x7, + .vref_0p8v = 0x9, + .vref_0p66v = 0x8, + .vref_0p4v = 0x7, + .vref_0p2v = 0x8, + .vref_1_1p6v = 0x7, + .vref_0_1p6v = 0x7 }, + .debounce_val = 0x7f, /* 1b,1us 7f,4.7us */ + .use_defalut_parameter = false, +}; + +static const struct type_c_cfg rtd1319d_type_c_cfg = { + .parameter_ver = PARAMETER_V1, + .cc_dfp_mode = CC_MODE_DFP_1_5, + .cc1_param = { .rp_4p7k_code = 0xe, + .rp_36k_code = 0x3, + .rp_12k_code = 0xe, + .rd_code = 0xf, + .ra_code = 0x6, + .vref_2p6v = 0x7, + .vref_1p23v = 0x7, + .vref_0p8v = 0x8, + .vref_0p66v = 0x7, + .vref_0p4v = 0x7, + .vref_0p2v = 0x7, + .vref_1_1p6v = 0x7, + .vref_0_1p6v = 0x7 }, + .cc2_param = { .rp_4p7k_code = 0xe, + .rp_36k_code = 0x3, + .rp_12k_code = 0xe, + .rd_code = 0xf, + .ra_code = 0x6, + .vref_2p6v = 0x7, + .vref_1p23v = 0x7, + .vref_0p8v = 0x8, + .vref_0p66v = 0x7, + .vref_0p4v = 0x7, + .vref_0p2v = 0x8, + .vref_1_1p6v = 0x7, + .vref_0_1p6v = 0x7 }, + .debounce_val = 0x7f, /* 1b,1us 7f,4.7us */ + .use_defalut_parameter = false, +}; + +static const struct type_c_cfg rtd1315e_type_c_cfg = { + .parameter_ver = PARAMETER_V1, + .cc_dfp_mode = CC_MODE_DFP_1_5, + .cc1_param = { .rp_4p7k_code = 0xe, + .rp_36k_code = 0x3, + .rp_12k_code = 0xe, + .rd_code = 0xf, + .ra_code = 0x6, + .vref_2p6v = 0x7, + .vref_1p23v = 0x7, + .vref_0p8v = 0x8, + .vref_0p66v = 0x7, + .vref_0p4v = 0x7, + .vref_0p2v = 0x7, + .vref_1_1p6v = 0x7, + .vref_0_1p6v = 0x7 }, + .cc2_param = { .rp_4p7k_code = 0xe, + .rp_36k_code = 0x3, + .rp_12k_code = 0xe, + .rd_code = 0xf, + .ra_code = 0x6, + .vref_2p6v = 0x7, + .vref_1p23v = 0x7, + .vref_0p8v = 0x8, + .vref_0p66v = 0x7, + .vref_0p4v = 0x7, + .vref_0p2v = 0x8, + .vref_1_1p6v = 0x7, + .vref_0_1p6v = 0x7 }, + .debounce_val = 0x7f, /* 1b,1us 7f,4.7us */ + .use_defalut_parameter = false, +}; + +static const struct of_device_id extcon_rtk_type_c_match[] = { + { .compatible = "realtek,rtd1295-type-c", .data = &rtd1295_type_c_cfg }, + { .compatible = "realtek,rtd1312c-type-c", .data = &rtd1312c_type_c_cfg }, + { .compatible = "realtek,rtd1315e-type-c", .data = &rtd1315e_type_c_cfg }, + { .compatible = "realtek,rtd1319-type-c", .data = &rtd1319_type_c_cfg }, + { .compatible = "realtek,rtd1319d-type-c", .data = &rtd1319d_type_c_cfg }, + { .compatible = "realtek,rtd1395-type-c", .data = &rtd1395_type_c_cfg }, + { .compatible = "realtek,rtd1619-type-c", .data = &rtd1619_type_c_cfg }, + { .compatible = "realtek,rtd1619b-type-c", .data = &rtd1619b_type_c_cfg }, + {}, +}; +MODULE_DEVICE_TABLE(of, extcon_rtk_type_c_match); + +#ifdef CONFIG_PM_SLEEP +static int extcon_rtk_type_c_prepare(struct device *dev) +{ + struct type_c_data *type_c = dev_get_drvdata(dev); + u32 default_ctrl; + unsigned long flags; + + cancel_delayed_work_sync(&type_c->delayed_work); + flush_delayed_work(&type_c->delayed_work); + WARN_ON_ONCE(delayed_work_pending(&type_c->delayed_work)); + + spin_lock_irqsave(&type_c->lock, flags); + /* disable interrupt */ + default_ctrl = readl(type_c->reg_base + USB_TYPEC_CTRL) & + DEBOUNCE_TIME_MASK; + writel(default_ctrl, type_c->reg_base + USB_TYPEC_CTRL); + + /* disable cc detect, rp, rd */ + writel(PLR_EN, type_c->reg_base + USB_TYPEC_CTRL_CC1_0); + writel(0, type_c->reg_base + USB_TYPEC_CTRL_CC2_0); + + spin_unlock_irqrestore(&type_c->lock, flags); + + return 0; +} + +static void extcon_rtk_type_c_complete(struct device *dev) +{ + /* nothing */ +} + +static int extcon_rtk_type_c_suspend(struct device *dev) +{ + /* nothing */ + + return 0; +} + +static int extcon_rtk_type_c_resume(struct device *dev) +{ + struct type_c_data *type_c = dev_get_drvdata(dev); + int ret; + + ret = extcon_rtk_type_c_init(type_c); + if (ret) { + dev_err(dev, "%s failed to init type_c\n", __func__); + return ret; + } + + return 0; +} + +static const struct dev_pm_ops extcon_rtk_type_c_pm_ops = { + SET_LATE_SYSTEM_SLEEP_PM_OPS(extcon_rtk_type_c_suspend, extcon_rtk_type_c_resume) + .prepare = extcon_rtk_type_c_prepare, + .complete = extcon_rtk_type_c_complete, +}; + +#define DEV_PM_OPS (&extcon_rtk_type_c_pm_ops) +#else +#define DEV_PM_OPS NULL +#endif /* CONFIG_PM_SLEEP */ + +static struct platform_driver extcon_rtk_type_c_driver = { + .probe = extcon_rtk_type_c_probe, + .remove_new = extcon_rtk_type_c_remove, + .driver = { + .name = "extcon-rtk-type_c", + .of_match_table = extcon_rtk_type_c_match, + .pm = DEV_PM_OPS, + }, +}; + +module_platform_driver(extcon_rtk_type_c_driver); + +MODULE_DESCRIPTION("Realtek Extcon Type C driver"); +MODULE_AUTHOR("Stanley Chang <stanley_chang@realtek.com>"); +MODULE_LICENSE("GPL");
This patch adds the extcon driver for Realtek DHC (digital home center) RTD SoCs type-c module. This can be used to detect whether the port is configured as a downstream or upstream facing port. And notify the status of extcon to listeners. Signed-off-by: Stanley Chang <stanley_chang@realtek.com> --- v2 to v3 change: removed the error check for debugfs v1 to v2 change: 1. added "depends on TYPEC" and "select USB_COMMON" in Kconfig 2. revised the code about gpio. 3. revised the definitions to keep the left-aligned of value 4. add the comment for delay or sleep 5. changed some functions to connector_attached/connector_detached 6. removed to check 'CONFIG_TYPEC' definition with ifdef --- drivers/extcon/Kconfig | 11 + drivers/extcon/Makefile | 1 + drivers/extcon/extcon-rtk-type-c.c | 1792 ++++++++++++++++++++++++++++ 3 files changed, 1804 insertions(+) create mode 100644 drivers/extcon/extcon-rtk-type-c.c