#include <linux/err.h>
#include <linux/gpio/consumer.h>
#include <linux/init.h>
+#include <linux/i2c.h>
#include <linux/kernel.h>
#include <linux/list.h>
#include <linux/module.h>
#include "onboard_usb_dev.h"
+/* USB5744 register offset and mask */
+#define USB5744_CMD_ATTACH 0xAA
+#define USB5744_CMD_ATTACH_LSB 0x56
+#define USB5744_CMD_CREG_ACCESS 0x99
+#define USB5744_CMD_CREG_ACCESS_LSB 0x37
+#define USB5744_CREG_MEM_ADDR 0x00
+#define USB5744_CREG_WRITE 0x00
+#define USB5744_CREG_RUNTIMEFLAGS2 0x41
+#define USB5744_CREG_RUNTIMEFLAGS2_LSB 0x1D
+#define USB5744_CREG_BYPASS_UDC_SUSPEND BIT(3)
+
static void onboard_dev_attach_usb_driver(struct work_struct *work);
static struct usb_device_driver onboard_dev_usbdev_driver;
pr_err("Failed to attach USB driver: %pe\n", ERR_PTR(err));
}
+static int onboard_dev_5744_i2c_init(struct i2c_client *client)
+{
+#if IS_ENABLED(CONFIG_I2C)
+ struct device *dev = &client->dev;
+ int ret;
+
+ /*
+ * Set BYPASS_UDC_SUSPEND bit to ensure MCU is always enabled
+ * and ready to respond to SMBus runtime commands.
+ * The command writes 5 bytes to memory and single data byte in
+ * configuration register.
+ */
+ char wr_buf[7] = {USB5744_CREG_MEM_ADDR, 5,
+ USB5744_CREG_WRITE, 1,
+ USB5744_CREG_RUNTIMEFLAGS2,
+ USB5744_CREG_RUNTIMEFLAGS2_LSB,
+ USB5744_CREG_BYPASS_UDC_SUSPEND};
+
+ ret = i2c_smbus_write_block_data(client, 0, sizeof(wr_buf), wr_buf);
+ if (ret)
+ return dev_err_probe(dev, ret, "BYPASS_UDC_SUSPEND bit configuration failed\n");
+
+ ret = i2c_smbus_write_word_data(client, USB5744_CMD_CREG_ACCESS,
+ USB5744_CMD_CREG_ACCESS_LSB);
+ if (ret)
+ return dev_err_probe(dev, ret, "Configuration Register Access Command failed\n");
+
+ /* Send SMBus command to boot hub. */
+ ret = i2c_smbus_write_word_data(client, USB5744_CMD_ATTACH,
+ USB5744_CMD_ATTACH_LSB);
+ if (ret < 0)
+ return dev_err_probe(dev, ret, "USB Attach with SMBus command failed\n");
+
+ return ret;
+#else
+ return -ENODEV;
+#endif
+}
+
static int onboard_dev_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct onboard_dev *onboard_dev;
+ struct device_node *i2c_node;
int err;
onboard_dev = devm_kzalloc(dev, sizeof(*onboard_dev), GFP_KERNEL);
if (err)
return err;
+ i2c_node = of_parse_phandle(pdev->dev.of_node, "i2c-bus", 0);
+ if (i2c_node) {
+ struct i2c_client *client;
+
+ client = of_find_i2c_device_by_node(i2c_node);
+ of_node_put(i2c_node);
+
+ if (!client) {
+ err = -EPROBE_DEFER;
+ goto err_power_off;
+ }
+
+ if (of_device_is_compatible(pdev->dev.of_node, "usb424,2744") ||
+ of_device_is_compatible(pdev->dev.of_node, "usb424,5744"))
+ err = onboard_dev_5744_i2c_init(client);
+
+ put_device(&client->dev);
+ if (err < 0)
+ goto err_power_off;
+ }
+
/*
* The USB driver might have been detached from the USB devices by
* onboard_dev_remove() (e.g. through an 'unbind' by userspace),
schedule_work(&attach_usb_driver_work);
return 0;
+
+err_power_off:
+ onboard_dev_power_off(onboard_dev);
+ return err;
}
static void onboard_dev_remove(struct platform_device *pdev)