kernel/0045-feat-add-dirver-for-fsub303-to-support-type-c.patch
2024-12-19 16:34:44 -05:00

867 lines
23 KiB
Diff

From 6c5133740e83d1af01d53298d340cffdf979f60e Mon Sep 17 00:00:00 2001
From: yangwei1 <yangwei1@eswincomputing.com>
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 <https://www.gnu.org/licenses/>.
+ *
+ * Authors: Yang Wei <yangwei1@eswincomputing.com>
+ */
+#include <linux/debugfs.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/gpio/consumer.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/of_device.h>
+#include <linux/pinctrl/consumer.h>
+#include <linux/proc_fs.h>
+#include <linux/regulator/consumer.h>
+#include <linux/sched/clock.h>
+#include <linux/seq_file.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/string.h>
+#include <linux/types.h>
+#include <linux/usb.h>
+#include <linux/usb/typec.h>
+#include <linux/usb/tcpm.h>
+#include <linux/usb/pd.h>
+#include <linux/workqueue.h>
+
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/mfd/syscon.h>
+#include <linux/regmap.h>
+#include <linux/usb/role.h>
+
+#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 <yangwei1@eswincomputing.com>");
+MODULE_DESCRIPTION("Onsemi FUSB303B Type-C Chip Driver");
+MODULE_LICENSE("GPL");
--
2.47.0