From 6c5133740e83d1af01d53298d340cffdf979f60e Mon Sep 17 00:00:00 2001 From: yangwei1 Date: Mon, 3 Jun 2024 19:06:17 +0800 Subject: [PATCH 045/219] feat:add dirver for fsub303 to support type-c Changelogs: if you want to otg,Set as follows 1.set dr_mode in dX_usbdrd_dwc3_0 to otg and add usb-role-switch 2.enable port 3.data-role of connector in fusb303b to otg --- .../boot/dts/eswin/hifive-premier-550.dts | 39 +- arch/riscv/configs/win2030_defconfig | 10 +- drivers/usb/typec/Kconfig | 8 + drivers/usb/typec/Makefile | 1 + drivers/usb/typec/fusb303b.c | 685 ++++++++++++++++++ 5 files changed, 723 insertions(+), 20 deletions(-) create mode 100644 drivers/usb/typec/fusb303b.c diff --git a/arch/riscv/boot/dts/eswin/hifive-premier-550.dts b/arch/riscv/boot/dts/eswin/hifive-premier-550.dts index 92a708552e52..2b08a6bbfd2a 100644 --- a/arch/riscv/boot/dts/eswin/hifive-premier-550.dts +++ b/arch/riscv/boot/dts/eswin/hifive-premier-550.dts @@ -61,12 +61,6 @@ memory@80000000 { reg = <0x0 0x80000000 MEMORY_SIZE_H MEMORY_SIZE_L>; numa-node-id = <0>; }; - extcon_usb1: extcon_usb1 { - compatible = "linux,extcon-usb-gpio"; - pinctrl-names = "default"; - pinctrl-0 = <&pinctrl_gpio6_default>; - id-gpio = <&porta 6 GPIO_ACTIVE_HIGH>; - }; reserved-memory { #address-cells = <2>; #size-cells = <2>; @@ -445,15 +439,22 @@ &d0_sata { &d0_usbdrd3_0 { status = "okay"; - extcon = <&extcon_usb1>; }; &d0_usbdrd_dwc3_0 { status = "okay"; - dr_mode = "otg"; - usb-role-switch; - role-switch-default-mode = "host"; + dr_mode = "host"; + /* usb-role-switch; + role-switch-default-mode = "host"; */ maximum-speed = "super-speed"; + port { + #address-cells = <1>; + #size-cells = <0>; + dwc3_0_role_switch: endpoint@0 { + reg = <0>; + remote-endpoint = <&usbc0_role_sw>; + }; + }; }; &d0_usbdrd3_1 { @@ -604,14 +605,20 @@ fusb303b@21 { pinctrl-names = "default"; pinctrl-0 = <&pinctrl_gpio5_default>; int-gpios = <&porta 5 GPIO_ACTIVE_HIGH>; + ports { + #address-cells = <1>; + #size-cells = <0>; + port@0 { + reg = <0>; + usbc0_role_sw: endpoint@0 { + remote-endpoint = <&dwc3_0_role_switch>; + }; + }; + }; connector { - label = "USB-C"; - pd-disable = "true"; - power-role = "dual"; - data-role = "dual"; compatible = "usb-c-connector"; - typec-power-opmode = "default"; - try-power-role = "sink"; + label = "USB-C"; + data-role = "host"; }; }; diff --git a/arch/riscv/configs/win2030_defconfig b/arch/riscv/configs/win2030_defconfig index 61b8f4de69ad..47122461e3fc 100644 --- a/arch/riscv/configs/win2030_defconfig +++ b/arch/riscv/configs/win2030_defconfig @@ -148,6 +148,8 @@ CONFIG_PINCTRL_EIC7700=y CONFIG_GPIOLIB=y CONFIG_GPIO_SYSFS=y CONFIG_GPIO_DWAPB=y +CONFIG_SENSORS_ESWIN_FAN_CONTROL=y +CONFIG_SENSORS_ESWIN_PVT=y CONFIG_SENSORS_INA2XX=y CONFIG_SENSORS_PAC1934=y CONFIG_WATCHDOG=y @@ -228,6 +230,8 @@ CONFIG_USB_CONFIGFS_F_LB_SS=y CONFIG_USB_CONFIGFS_F_FS=y CONFIG_USB_ZERO=m CONFIG_USB_MASS_STORAGE=m +CONFIG_TYPEC=y +CONFIG_TYPEC_FUSB303B=y CONFIG_MMC=y CONFIG_MMC_TEST=y CONFIG_MMC_DEBUG=y @@ -248,6 +252,7 @@ CONFIG_EDAC=y CONFIG_EDAC_ESWIN=y CONFIG_RTC_CLASS=y CONFIG_RTC_DRV_PCF8563=y +CONFIG_RTC_DRV_ESWIN=y CONFIG_DMADEVICES=y CONFIG_DW_AXI_DMAC=y CONFIG_DMATEST=y @@ -259,6 +264,7 @@ CONFIG_VIRTIO_INPUT=y CONFIG_VIRTIO_MMIO=y CONFIG_STAGING=y CONFIG_COMMON_CLK_WIN2030=y +CONFIG_TIMER_ESWIN=y CONFIG_MAILBOX=y CONFIG_ESWIN_MBOX=y CONFIG_ARM_SMMU_V3=y @@ -268,9 +274,6 @@ CONFIG_EXTCON=y CONFIG_MEMORY=y CONFIG_PWM=y CONFIG_PWM_ESWIN=y -CONFIG_SENSORS_ESWIN_FAN_CONTROL=y -CONFIG_SENSORS_ESWIN_PVT=y -CONFIG_RTC_DRV_ESWIN=y CONFIG_RESET_ESWIN_WIN2030=y CONFIG_INTERCONNECT=y CONFIG_EXT4_FS=y @@ -320,4 +323,3 @@ CONFIG_RCU_EQS_DEBUG=y # CONFIG_FTRACE is not set # CONFIG_RUNTIME_TESTING_MENU is not set CONFIG_MEMTEST=y -CONFIG_TIMER_ESWIN=y \ No newline at end of file diff --git a/drivers/usb/typec/Kconfig b/drivers/usb/typec/Kconfig index 2f80c2792dbd..b8bcb74d8d53 100644 --- a/drivers/usb/typec/Kconfig +++ b/drivers/usb/typec/Kconfig @@ -110,6 +110,14 @@ config TYPEC_WUSB3801 If you choose to build this driver as a dynamically linked module, the module will be called wusb3801.ko. +config TYPEC_FUSB303B + tristate "Onsemi FUSB303B Type-C chip driver" + depends on I2C + depends on EXTCON || !EXTCON + help + The Onsemi FUSB303B Type-C chip driver that works with + Type-C Port Controller Manager to provide USB + Type-C functionalities. source "drivers/usb/typec/mux/Kconfig" source "drivers/usb/typec/altmodes/Kconfig" diff --git a/drivers/usb/typec/Makefile b/drivers/usb/typec/Makefile index 7a368fea61bc..e53e9ea83d6b 100644 --- a/drivers/usb/typec/Makefile +++ b/drivers/usb/typec/Makefile @@ -12,3 +12,4 @@ obj-$(CONFIG_TYPEC_STUSB160X) += stusb160x.o obj-$(CONFIG_TYPEC_RT1719) += rt1719.o obj-$(CONFIG_TYPEC_WUSB3801) += wusb3801.o obj-$(CONFIG_TYPEC) += mux/ +obj-$(CONFIG_TYPEC_FUSB303B) += fusb303b.o diff --git a/drivers/usb/typec/fusb303b.c b/drivers/usb/typec/fusb303b.c new file mode 100644 index 000000000000..5ffc27640a05 --- /dev/null +++ b/drivers/usb/typec/fusb303b.c @@ -0,0 +1,685 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Onsemi FUSB303B Type-C Chip Driver + * + * Copyright 2024, Beijing ESWIN Computing Technology Co., Ltd.. All rights reserved. + * SPDX-License-Identifier: GPL-2.0 + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 2. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * Authors: Yang Wei + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#define FUSB303B_REG_DEVICE_ID 0X1 +#define FUSB303B_REG_DEVICE_TYPE 0X2 +#define FUSB303B_REG_PORTROLE 0X3 +#define FUSB303B_REG_CONTROL 0X4 +#define FUSB303B_REG_CONTROL1 0X5 +#define FUSB303B_REG_MANUAL 0X9 +#define FUSB303B_REG_RESET 0XA +#define FUSB303B_REG_MASK 0XE +#define FUSB303B_REG_MASK1 0XF +#define FUSB303B_REG_STATUS 0X11 +#define FUSB303B_REG_STATUS1 0X12 +#define FUSB303B_REG_TYPE 0X13 +#define FUSB303B_REG_INTERRUPT 0X14 +#define FUSB303B_REG_INTERRUPT1 0X15 + +#define FUSB303B_PORTROLE_DRP BIT(2) +#define FUSB303B_PORTROLE_SINK BIT(1) +#define FUSB303B_PORTROLE_SOURCE BIT(0) + +#define FUSB303B_CONTROL_T_DRP BIT(6) +#define FUSB303B_CONTROL_DRPTOGGLE BIT(4) +#define FUSB303B_CONTROL_DCABLE_EN BIT(3) +#define FUSB303B_CONTROL_HOST_CUR BIT(1) +#define FUSB303B_CONTROL_INT_MASK BIT(0) + +#define FUSB303B_CONTROL1_REMEDY_EN BIT(7) +#define FUSB303B_CONTROL1_AUTO_SNK_TH BIT(5) +#define FUSB303B_CONTROL1_AUTO_SNK_EN BIT(4) +#define FUSB303B_CONTROL1_ENABLE BIT(3) +#define FUSB303B_CONTROL1_TCCDEB BIT(0) + +#define FUSB303B_STATUS_AUTOSNK BIT(7) +#define FUSB303B_STATUS_VSAFE0V BIT(6) +#define FUSB303B_STATUS_ORIENT BIT(4) +#define FUSB303B_STATUS_VBUSOK BIT(3) +#define FUSB303B_STATUS_BC_LVL BIT(1) +#define FUSB303B_STATUS_BC_LVL_MASK 0X6 +#define FUSB303B_STATUS_ATTACH BIT(0) + +#define FUSB303B_STATUS_MASK 0X30 + +#define FUSB303B_BC_LVL_SINK_OR_RA 0 +#define FUSB303B_BC_LVL_SINK_DEFAULT 1 +#define FUSB303B_BC_LVL_SINK_1_5A 2 +#define FUSB303B_BC_LVL_SINK_3A 3 + +#define FUSB303B_INT_I_ORIENT BIT(6) +#define FUSB303B_INT_I_FAULT BIT(5) +#define FUSB303B_INT_I_VBUS_CHG BIT(4) +#define FUSB303B_INT_I_AUTOSNK BIT(3) +#define FUSB303B_INT_I_BC_LVL BIT(2) +#define FUSB303B_INT_I_DETACH BIT(1) +#define FUSB303B_INT_I_ATTACH BIT(0) + +#define FUSB303B_INT1_I_REM_VBOFF BIT(6) +#define FUSB303B_INT1_I_REM_VBON BIT(5) +#define FUSB303B_INT1_I_REM_FAIL BIT(3) +#define FUSB303B_INT1_I_FRC_FAIL BIT(2) +#define FUSB303B_INT1_I_FRC_SUCC BIT(1) +#define FUSB303B_INT1_I_REMEDY BIT(0) + +#define FUSB303B_TYPE_SINK BIT(4) +#define FUSB303B_TYPE_SOURCE BIT(3) + +#define FUSB_REG_MASK_M_ORIENT BIT(6) +#define FUSB_REG_MASK_M_FAULT BIT(5) +#define FUSB_REG_MASK_M_VBUS_CHG BIT(4) +#define FUSB_REG_MASK_M_AUTOSNK BIT(3) +#define FUSB_REG_MASK_M_BC_LVL BIT(2) +#define FUSB_REG_MASK_M_DETACH BIT(1) +#define FUSB_REG_MASK_M_ATTACH BIT(0) + +#define LOG_BUFFER_ENTRIES 1024 +#define LOG_BUFFER_ENTRY_SIZE 128 + +struct fusb303b_chip +{ + struct device *dev; + struct i2c_client *i2c_client; + struct fwnode_handle *fwnode; + spinlock_t irq_lock; + struct gpio_desc *gpio_int_n; + int gpio_int_n_irq; + struct usb_role_switch *role_sw; + /* lock for sharing chip states */ + struct mutex lock; + bool vbus_ok; + bool attch_ok; +#ifdef CONFIG_DEBUG_FS + struct dentry *dentry; + /* lock for log buffer access */ + struct mutex logbuffer_lock; + int logbuffer_head; + int logbuffer_tail; + u8 *logbuffer[LOG_BUFFER_ENTRIES]; +#endif +}; + +#ifdef CONFIG_DEBUG_FS +static bool fusb303b_log_full(struct fusb303b_chip *chip) +{ + return chip->logbuffer_tail == + (chip->logbuffer_head + 1) % LOG_BUFFER_ENTRIES; +} + +__printf(2, 0) static void _fusb303b_log(struct fusb303b_chip *chip, + const char *fmt, va_list args) +{ + char tmpbuffer[LOG_BUFFER_ENTRY_SIZE]; + u64 ts_nsec = local_clock(); + unsigned long rem_nsec; + + if (!chip->logbuffer[chip->logbuffer_head]) + { + chip->logbuffer[chip->logbuffer_head] = + kzalloc(LOG_BUFFER_ENTRY_SIZE, GFP_KERNEL); + if (!chip->logbuffer[chip->logbuffer_head]) + return; + } + + vsnprintf(tmpbuffer, sizeof(tmpbuffer), fmt, args); + + mutex_lock(&chip->logbuffer_lock); + + if (fusb303b_log_full(chip)) + { + chip->logbuffer_head = max(chip->logbuffer_head - 1, 0); + strlcpy(tmpbuffer, "overflow", sizeof(tmpbuffer)); + } + + if (chip->logbuffer_head < 0 || + chip->logbuffer_head >= LOG_BUFFER_ENTRIES) + { + dev_warn(chip->dev, "Bad log buffer index %d\n", + chip->logbuffer_head); + goto abort; + } + + if (!chip->logbuffer[chip->logbuffer_head]) + { + dev_warn(chip->dev, "Log buffer index %d is NULL\n", + chip->logbuffer_head); + goto abort; + } + + rem_nsec = do_div(ts_nsec, 1000000000); + scnprintf(chip->logbuffer[chip->logbuffer_head], LOG_BUFFER_ENTRY_SIZE, + "[%5lu.%06lu] %s", (unsigned long)ts_nsec, rem_nsec / 1000, + tmpbuffer); + chip->logbuffer_head = (chip->logbuffer_head + 1) % LOG_BUFFER_ENTRIES; + +abort: + mutex_unlock(&chip->logbuffer_lock); +} + +__printf(2, 3) static void fusb303b_log(struct fusb303b_chip *chip, + const char *fmt, ...) +{ + va_list args; + + va_start(args, fmt); + _fusb303b_log(chip, fmt, args); + va_end(args); +} + +static int fusb303b_debug_show(struct seq_file *s, void *v) +{ + struct fusb303b_chip *chip = (struct fusb303b_chip *)s->private; + int tail; + + mutex_lock(&chip->logbuffer_lock); + tail = chip->logbuffer_tail; + while (tail != chip->logbuffer_head) + { + seq_printf(s, "%s\n", chip->logbuffer[tail]); + tail = (tail + 1) % LOG_BUFFER_ENTRIES; + } + if (!seq_has_overflowed(s)) + chip->logbuffer_tail = tail; + mutex_unlock(&chip->logbuffer_lock); + + return 0; +} +DEFINE_SHOW_ATTRIBUTE(fusb303b_debug); + +static void fusb303b_debugfs_init(struct fusb303b_chip *chip) +{ + char name[NAME_MAX]; + + mutex_init(&chip->logbuffer_lock); + snprintf(name, NAME_MAX, "fusb303b-%s", dev_name(chip->dev)); + chip->dentry = debugfs_create_dir(name, usb_debug_root); + debugfs_create_file("log", S_IFREG | 0444, chip->dentry, chip, + &fusb303b_debug_fops); +} + +static void fusb303b_debugfs_exit(struct fusb303b_chip *chip) +{ + debugfs_remove(chip->dentry); +} + +#else + +static void fusb303b_log(const struct fusb303b_chip *chip, const char *fmt, ...) +{ +} +static void fusb303b_debugfs_init(const struct fusb303b_chip *chip) +{ +} +static void fusb303b_debugfs_exit(const struct fusb303b_chip *chip) +{ +} + +#endif + +static int fusb303b_i2c_write(struct fusb303b_chip *chip, u8 address, u8 data) +{ + int ret = 0; + + ret = i2c_smbus_write_byte_data(chip->i2c_client, address, data); + if (ret < 0) + fusb303b_log(chip, "cannot write 0x%02x to 0x%02x, ret=%d", + data, address, ret); + + return ret; +} + +static int fusb303b_i2c_read(struct fusb303b_chip *chip, u8 address, u8 *data) +{ + int ret = 0; + + ret = i2c_smbus_read_byte_data(chip->i2c_client, address); + *data = (u8)ret; + if (ret < 0) + fusb303b_log(chip, "cannot read %02x, ret=%d", address, ret); + + return ret; +} + +static int fusb303b_i2c_mask_write(struct fusb303b_chip *chip, u8 address, + u8 mask, u8 value) +{ + int ret = 0; + u8 data; + + ret = fusb303b_i2c_read(chip, address, &data); + if (ret < 0) + return ret; + data &= ~mask; + data |= value; + ret = fusb303b_i2c_write(chip, address, data); + if (ret < 0) + return ret; + + return ret; +} + +static int fusb303b_i2c_clear_bits(struct fusb303b_chip *chip, u8 address, + u8 clear_bits) +{ + return fusb303b_i2c_mask_write(chip, address, clear_bits, 0x00); +} + +static int fusb303b_sw_reset(struct fusb303b_chip *chip) +{ + int ret = 0; + + ret = fusb303b_i2c_write(chip, FUSB303B_REG_RESET, 1); + if (ret < 0) + fusb303b_log(chip, "cannot sw reset the chip, ret=%d", ret); + else + fusb303b_log(chip, "sw reset"); + + return ret; +} + +/* + * initialize interrupt on the chip + * - unmasked interrupt: VBUS_OK + */ +static int fusb303b_init_interrupt(struct fusb303b_chip *chip) +{ + int ret = 0; + u8 int_unmask = FUSB_REG_MASK_M_VBUS_CHG | + FUSB_REG_MASK_M_DETACH | FUSB_REG_MASK_M_ATTACH; + ret = fusb303b_i2c_write(chip, FUSB303B_REG_MASK, + 0xFF & ~(int_unmask)); + if (ret < 0) + return ret; + ret = fusb303b_i2c_write(chip, FUSB303B_REG_MASK1, 0xFF); + if (ret < 0) + return ret; + + ret = fusb303b_i2c_clear_bits(chip, FUSB303B_REG_CONTROL, + FUSB303B_CONTROL_INT_MASK); + if (ret < 0) + return ret; + + return ret; +} + +static int fusb303b_init(struct fusb303b_chip *chip) +{ + int ret = 0; + u8 data; + + ret = fusb303b_sw_reset(chip); + if (ret < 0) + return ret; + fusb303b_i2c_read(chip, FUSB303B_REG_STATUS, &data); + fusb303b_i2c_mask_write(chip, FUSB303B_REG_CONTROL, + FUSB303B_CONTROL_DCABLE_EN, + FUSB303B_CONTROL_DCABLE_EN); + fusb303b_i2c_mask_write(chip, FUSB303B_REG_CONTROL1, + FUSB303B_CONTROL1_ENABLE | FUSB303B_CONTROL1_REMEDY_EN, + FUSB303B_CONTROL1_ENABLE | FUSB303B_CONTROL1_REMEDY_EN); + ret = fusb303b_init_interrupt(chip); + if (ret < 0) + return ret; + + ret = fusb303b_i2c_read(chip, FUSB303B_REG_STATUS, &data); + if (ret < 0) + return ret; + chip->vbus_ok = !!(data & FUSB303B_STATUS_VBUSOK); + chip->attch_ok = !!(data & FUSB303B_STATUS_ATTACH); + ret = fusb303b_i2c_read(chip, FUSB303B_REG_DEVICE_ID, &data); + if (ret < 0) + return ret; + fusb303b_log(chip, "fusb303b device ID: 0x%02x", data); + + ret = fusb303b_i2c_read(chip, FUSB303B_REG_DEVICE_TYPE, &data); + if (ret < 0) + return ret; + fusb303b_log(chip, "fusb303b type:0x%02x", data); + + return ret; +} + +static s32 fusb303b_set_port_check(struct fusb303b_chip *chip, + enum typec_port_type port_type) +{ + s32 ret = 0; + + fusb303b_log(chip, "%s.%d port_type:%d", + __FUNCTION__, __LINE__, port_type); + switch (port_type) + { + case TYPEC_PORT_DRP: + ret = fusb303b_i2c_write(chip, FUSB303B_REG_PORTROLE, + FUSB303B_PORTROLE_DRP); + break; + case TYPEC_PORT_SRC: + ret = fusb303b_i2c_write(chip, FUSB303B_REG_PORTROLE, + FUSB303B_PORTROLE_SOURCE); + break; + default: + ret = fusb303b_i2c_write(chip, FUSB303B_REG_PORTROLE, + FUSB303B_PORTROLE_SINK); + break; + } + + return ret; +} + +int fusb303b_set_usb_role(struct fusb303b_chip *chip) +{ + u8 type = 0; + int ret = 0; + + if ((true == chip->attch_ok) && (true == chip->vbus_ok)) + { + ret = fusb303b_i2c_read(chip, FUSB303B_REG_TYPE, &type); + if (ret < 0) + { + fusb303b_log(chip, "read type error:%d", ret); + return ret; + } + fusb303b_log(chip, "%s type: 0x%02x", __func__, type); + if (FUSB303B_TYPE_SOURCE == (FUSB303B_TYPE_SOURCE & type)) + { + usb_role_switch_set_role(chip->role_sw, USB_ROLE_HOST); + fusb303b_log(chip, "set usb to host"); + } + else + { + usb_role_switch_set_role(chip->role_sw, USB_ROLE_DEVICE); + fusb303b_log(chip, "set usb to device"); + if (FUSB303B_TYPE_SINK != (FUSB303B_TYPE_SINK & type)) + { + fusb303b_log(chip, "illegel type:0x%02x,set usb to device", type); + } + } + } + + return 0; +} + +static irqreturn_t fusb303b_irq_intn(int irq, void *dev_id) +{ + struct fusb303b_chip *chip = dev_id; + int ret = 0; + u8 interrupt = 0, interrupt1 = 0, status = 0; + + mutex_lock(&chip->lock); + ret = fusb303b_i2c_read(chip, FUSB303B_REG_INTERRUPT, &interrupt); + if (ret < 0) + goto done; + ret = fusb303b_i2c_read(chip, FUSB303B_REG_INTERRUPT1, &interrupt1); + if (ret < 0) + goto done; + ret = fusb303b_i2c_read(chip, FUSB303B_REG_STATUS, &status); + if (ret < 0) + goto done; + + fusb303b_log(chip, "IRQ: 0x%02x,0x%02x status: 0x%02x", + interrupt, interrupt1, status); + + if (interrupt & FUSB303B_INT_I_VBUS_CHG) + { + chip->vbus_ok = !!(status & FUSB303B_STATUS_VBUSOK); + fusb303b_log(chip, "IRQ: VBUS_OK, vbus=%s", + chip->vbus_ok ? "On" : "Off"); + } + if (interrupt & (FUSB303B_INT_I_ATTACH | FUSB303B_INT_I_DETACH)) + { + chip->attch_ok = !!(status & FUSB303B_STATUS_ATTACH); + fusb303b_log(chip, "IRQ: attach OK, attach=%s", + chip->attch_ok ? "On" : "Off"); + } + fusb303b_set_usb_role(chip); + if (0 != interrupt) + fusb303b_i2c_write(chip, FUSB303B_REG_INTERRUPT, interrupt); + if (0 != interrupt1) + fusb303b_i2c_write(chip, FUSB303B_REG_INTERRUPT1, interrupt1); + +done: + mutex_unlock(&chip->lock); + // enable_irq(chip->gpio_int_n_irq); + return IRQ_HANDLED; +} + +static int init_gpio(struct fusb303b_chip *chip) +{ + struct device *dev = chip->dev; + int ret = 0; + + chip->gpio_int_n = devm_gpiod_get(dev, "int", GPIOD_IN); + if (IS_ERR(chip->gpio_int_n)) + { + fusb303b_log(chip, "failed to request gpio_int_n\n"); + return PTR_ERR(chip->gpio_int_n); + } + ret = gpiod_to_irq(chip->gpio_int_n); + if (ret < 0) + { + fusb303b_log(chip, "cannot request IRQ for GPIO Int_N, ret=%d", ret); + return ret; + } + chip->gpio_int_n_irq = ret; + + return 0; +} + +static const struct property_entry port_props[] = { + PROPERTY_ENTRY_STRING("data-role", "dual"), + PROPERTY_ENTRY_STRING("power-role", "dual"), + PROPERTY_ENTRY_STRING("try-power-role", "sink"), + {}}; + +static struct fwnode_handle *fusb303b_fwnode_get(struct device *dev) +{ + struct fwnode_handle *fwnode; + fwnode = device_get_named_child_node(dev, "connector"); + if (!fwnode) + fwnode = fwnode_create_software_node(port_props, NULL); + + return fwnode; +} + +static int fusb303b_probe(struct i2c_client *client) +{ + struct fusb303b_chip *chip; + struct device *dev = &client->dev; + int ret = 0; + struct regmap *regmap; + int irq_sel_reg; + int irq_sel_bit; + const char *cap_str; + int usb_data_role = 0; + regmap = syscon_regmap_lookup_by_phandle(dev->of_node, "eswin,syscfg"); + if (!IS_ERR(regmap)) + { + ret = of_property_read_u32_index(dev->of_node, "eswin,syscfg", + 1, &irq_sel_reg); + if (ret) + { + dev_err(dev, + "can't get irq cfg reg offset in sys_con(errno:%d)\n", ret); + return ret; + } + ret = of_property_read_u32_index(dev->of_node, "eswin,syscfg", + 2, &irq_sel_bit); + if (ret) + { + dev_err(dev, + "can't get irq cfg bit offset in sys_con(errno:%d)\n", ret); + return ret; + } + regmap_clear_bits(regmap, irq_sel_reg, BIT_ULL(irq_sel_bit)); + } + + chip = devm_kzalloc(&client->dev, sizeof(*chip), GFP_KERNEL); + if (!chip) + return -ENOMEM; + chip->i2c_client = client; + chip->dev = &client->dev; + mutex_init(&chip->lock); + spin_lock_init(&chip->irq_lock); + fusb303b_init(chip); + fusb303b_debugfs_init(chip); + if (client->irq) + { + chip->gpio_int_n_irq = client->irq; + } + else + { + ret = init_gpio(chip); + if (ret < 0) + goto destroy_workqueue; + } + chip->fwnode = fusb303b_fwnode_get(dev); + if (IS_ERR(chip->fwnode)) + { + ret = PTR_ERR(chip->fwnode); + goto destroy_workqueue; + } + /* + * This fwnode has a "compatible" property, but is never populated as a + * struct device. Instead we simply parse it to read the properties. + * This it breaks fw_devlink=on. To maintain backward compatibility + * with existing DT files, we work around this by deleting any + * fwnode_links to/from this fwnode. + */ + fw_devlink_purge_absent_suppliers(chip->fwnode); + + /* USB data support is optional */ + ret = fwnode_property_read_string(chip->fwnode, "data-role", &cap_str); + if (ret == 0) + { + ret = typec_find_port_data_role(cap_str); + if (ret < 0) + { + fusb303b_log(chip, "%s is not leage data-role\n", cap_str); + goto put_fwnode; + } + usb_data_role = ret; + } + else + { + fusb303b_log(chip, "cannot find data-role in dts\n"); + } + chip->role_sw = usb_role_switch_get(chip->dev); + if (IS_ERR(chip->role_sw)) + { + ret = PTR_ERR(chip->role_sw); + fusb303b_log(chip, "get role_sw error"); + goto put_fwnode; + } + + ret = devm_request_threaded_irq(dev, chip->gpio_int_n_irq, NULL, + fusb303b_irq_intn, + IRQF_ONESHOT | IRQF_TRIGGER_LOW, + "fusb303b_interrupt_int_n", chip); + if (ret < 0) + { + fusb303b_log(chip, "cannot request IRQ for GPIO Int_N, ret=%d", ret); + goto err_put_role; + } + + enable_irq_wake(chip->gpio_int_n_irq); + fusb303b_set_port_check(chip, usb_data_role); + + i2c_set_clientdata(client, chip); + + fusb303b_log(chip, "Kernel thread created successfully"); + return ret; +err_put_role: + usb_role_switch_put(chip->role_sw); +put_fwnode: + fwnode_handle_put(chip->fwnode); +destroy_workqueue: + fusb303b_debugfs_exit(chip); + + return ret; +} + +static void fusb303b_remove(struct i2c_client *client) +{ + struct fusb303b_chip *chip = i2c_get_clientdata(client); + + disable_irq_wake(chip->gpio_int_n_irq); + free_irq(chip->gpio_int_n_irq, chip); + usb_role_switch_put(chip->role_sw); + fwnode_handle_put(chip->fwnode); + fusb303b_debugfs_exit(chip); +} + +static const struct of_device_id fusb303b_dt_match[] = { + {.compatible = "fcs,fusb303b"}, + {}, +}; +MODULE_DEVICE_TABLE(of, fusb303b_dt_match); + +static const struct i2c_device_id fusb303b_i2c_device_id[] = { + {"typec_fusb303b", 0}, + {}, +}; +MODULE_DEVICE_TABLE(i2c, fusb303b_i2c_device_id); + +static struct i2c_driver fusb303b_driver = { + .driver = { + .name = "typec_fusb303b", + .of_match_table = of_match_ptr(fusb303b_dt_match), + }, + .probe = fusb303b_probe, + .remove = fusb303b_remove, + .id_table = fusb303b_i2c_device_id, +}; +module_i2c_driver(fusb303b_driver); + +MODULE_AUTHOR("Yang Wei "); +MODULE_DESCRIPTION("Onsemi FUSB303B Type-C Chip Driver"); +MODULE_LICENSE("GPL"); -- 2.47.0