732 lines · 19.0 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0-only/* * Driver for onboard USB devices * * Copyright (c) 2022, Google LLC */ #include <linux/clk.h>#include <linux/device.h>#include <linux/export.h>#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 <linux/mutex.h>#include <linux/of.h>#include <linux/of_platform.h>#include <linux/platform_device.h>#include <linux/regulator/consumer.h>#include <linux/slab.h>#include <linux/suspend.h>#include <linux/sysfs.h>#include <linux/usb.h>#include <linux/usb/hcd.h>#include <linux/usb/onboard_dev.h>#include <linux/workqueue.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_MEM_RD_ADDR		0x04#define USB5744_CREG_WRITE			0x00#define USB5744_CREG_READ			0x01#define USB5744_CREG_RUNTIMEFLAGS2		0x411D#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;static DECLARE_WORK(attach_usb_driver_work, onboard_dev_attach_usb_driver); /************************** Platform driver **************************/ struct usbdev_node {	struct usb_device *udev;	struct list_head list;}; struct onboard_dev {	struct regulator_bulk_data supplies[MAX_SUPPLIES];	struct device *dev;	const struct onboard_dev_pdata *pdata;	struct gpio_desc *reset_gpio;	bool always_powered_in_suspend;	bool is_powered_on;	bool going_away;	struct list_head udev_list;	struct mutex lock;	struct clk *clk;}; static int onboard_dev_get_regulators(struct onboard_dev *onboard_dev){	const char * const *supply_names = onboard_dev->pdata->supply_names;	unsigned int num_supplies = onboard_dev->pdata->num_supplies;	struct device *dev = onboard_dev->dev;	unsigned int i;	int err; 	if (num_supplies > MAX_SUPPLIES)		return dev_err_probe(dev, -EINVAL, "max %d supplies supported!\n",				     MAX_SUPPLIES); 	for (i = 0; i < num_supplies; i++)		onboard_dev->supplies[i].supply = supply_names[i]; 	err = devm_regulator_bulk_get(dev, num_supplies, onboard_dev->supplies);	if (err)		dev_err(dev, "Failed to get regulator supplies: %pe\n",			ERR_PTR(err)); 	return err;} static int onboard_dev_power_on(struct onboard_dev *onboard_dev){	int err; 	err = clk_prepare_enable(onboard_dev->clk);	if (err) {		dev_err(onboard_dev->dev, "failed to enable clock: %pe\n",			ERR_PTR(err));		return err;	} 	err = regulator_bulk_enable(onboard_dev->pdata->num_supplies,				    onboard_dev->supplies);	if (err) {		dev_err(onboard_dev->dev, "failed to enable supplies: %pe\n",			ERR_PTR(err));		goto disable_clk;	} 	fsleep(onboard_dev->pdata->reset_us);	gpiod_set_value_cansleep(onboard_dev->reset_gpio, 0);	fsleep(onboard_dev->pdata->power_on_delay_us); 	onboard_dev->is_powered_on = true; 	return 0; disable_clk:	clk_disable_unprepare(onboard_dev->clk);	return err;} static int onboard_dev_power_off(struct onboard_dev *onboard_dev){	int err; 	gpiod_set_value_cansleep(onboard_dev->reset_gpio, 1); 	err = regulator_bulk_disable(onboard_dev->pdata->num_supplies,				     onboard_dev->supplies);	if (err) {		dev_err(onboard_dev->dev, "failed to disable supplies: %pe\n",			ERR_PTR(err));		return err;	} 	clk_disable_unprepare(onboard_dev->clk); 	onboard_dev->is_powered_on = false; 	return 0;} static int __maybe_unused onboard_dev_suspend(struct device *dev){	struct onboard_dev *onboard_dev = dev_get_drvdata(dev);	struct usbdev_node *node;	bool power_off = true; 	if (onboard_dev->always_powered_in_suspend)		return 0; 	mutex_lock(&onboard_dev->lock); 	list_for_each_entry(node, &onboard_dev->udev_list, list) {		if (!device_may_wakeup(node->udev->bus->controller))			continue; 		if (usb_wakeup_enabled_descendants(node->udev)) {			power_off = false;			break;		}	} 	mutex_unlock(&onboard_dev->lock); 	if (!power_off)		return 0; 	return onboard_dev_power_off(onboard_dev);} static int __maybe_unused onboard_dev_resume(struct device *dev){	struct onboard_dev *onboard_dev = dev_get_drvdata(dev); 	if (onboard_dev->is_powered_on)		return 0; 	return onboard_dev_power_on(onboard_dev);} static inline void get_udev_link_name(const struct usb_device *udev, char *buf,				      size_t size){	snprintf(buf, size, "usb_dev.%s", dev_name(&udev->dev));} static int onboard_dev_add_usbdev(struct onboard_dev *onboard_dev,				  struct usb_device *udev){	struct usbdev_node *node;	char link_name[64];	int err; 	mutex_lock(&onboard_dev->lock); 	if (onboard_dev->going_away) {		err = -EINVAL;		goto error;	} 	node = kzalloc_obj(*node);	if (!node) {		err = -ENOMEM;		goto error;	} 	node->udev = udev; 	list_add(&node->list, &onboard_dev->udev_list); 	mutex_unlock(&onboard_dev->lock); 	get_udev_link_name(udev, link_name, sizeof(link_name));	WARN_ON(sysfs_create_link(&onboard_dev->dev->kobj, &udev->dev.kobj,				  link_name)); 	return 0; error:	mutex_unlock(&onboard_dev->lock); 	return err;} static void onboard_dev_remove_usbdev(struct onboard_dev *onboard_dev,				      const struct usb_device *udev){	struct usbdev_node *node;	char link_name[64]; 	get_udev_link_name(udev, link_name, sizeof(link_name));	sysfs_remove_link(&onboard_dev->dev->kobj, link_name); 	mutex_lock(&onboard_dev->lock); 	list_for_each_entry(node, &onboard_dev->udev_list, list) {		if (node->udev == udev) {			list_del(&node->list);			kfree(node);			break;		}	} 	mutex_unlock(&onboard_dev->lock);} static ssize_t always_powered_in_suspend_show(struct device *dev,					      struct device_attribute *attr,					      char *buf){	const struct onboard_dev *onboard_dev = dev_get_drvdata(dev); 	return sysfs_emit(buf, "%d\n", onboard_dev->always_powered_in_suspend);} static ssize_t always_powered_in_suspend_store(struct device *dev,					       struct device_attribute *attr,					       const char *buf, size_t count){	struct onboard_dev *onboard_dev = dev_get_drvdata(dev);	bool val;	int ret; 	ret = kstrtobool(buf, &val);	if (ret < 0)		return ret; 	onboard_dev->always_powered_in_suspend = val; 	return count;}static DEVICE_ATTR_RW(always_powered_in_suspend); static struct attribute *onboard_dev_attrs[] = {	&dev_attr_always_powered_in_suspend.attr,	NULL,}; static umode_t onboard_dev_attrs_are_visible(struct kobject *kobj,					     struct attribute *attr,					     int n){	struct device *dev = kobj_to_dev(kobj);	struct onboard_dev *onboard_dev = dev_get_drvdata(dev); 	if (attr == &dev_attr_always_powered_in_suspend.attr &&	    !onboard_dev->pdata->is_hub)		return 0; 	return attr->mode;} static const struct attribute_group onboard_dev_group = {	.is_visible = onboard_dev_attrs_are_visible,	.attrs = onboard_dev_attrs,};__ATTRIBUTE_GROUPS(onboard_dev);  static void onboard_dev_attach_usb_driver(struct work_struct *work){	int err; 	err = driver_attach(&onboard_dev_usbdev_driver.driver);	if (err)		pr_err("Failed to attach USB driver: %pe\n", ERR_PTR(err));} #if IS_ENABLED(CONFIG_USB_ONBOARD_DEV_USB5744)static int onboard_dev_5744_i2c_read_byte(struct i2c_client *client, u16 addr, u8 *data){	struct i2c_msg msg[2];	u8 rd_buf[3];	int ret; 	u8 wr_buf[7] = {0, USB5744_CREG_MEM_ADDR, 4,			USB5744_CREG_READ, 1,			addr >> 8 & 0xff,			addr & 0xff};	msg[0].addr = client->addr;	msg[0].flags = 0;	msg[0].len = sizeof(wr_buf);	msg[0].buf = wr_buf; 	ret = i2c_transfer(client->adapter, msg, 1);	if (ret < 0)		return ret; 	wr_buf[0] = USB5744_CMD_CREG_ACCESS;	wr_buf[1] = USB5744_CMD_CREG_ACCESS_LSB;	wr_buf[2] = 0;	msg[0].len = 3; 	ret = i2c_transfer(client->adapter, msg, 1);	if (ret < 0)		return ret; 	wr_buf[0] = 0;	wr_buf[1] = USB5744_CREG_MEM_RD_ADDR;	msg[0].len = 2; 	msg[1].addr = client->addr;	msg[1].flags = I2C_M_RD;	msg[1].len = 2;	msg[1].buf = rd_buf; 	ret = i2c_transfer(client->adapter, msg, 2);	if (ret < 0)		return ret;	*data = rd_buf[1]; 	return 0;} static int onboard_dev_5744_i2c_write_byte(struct i2c_client *client, u16 addr, u8 data){	struct i2c_msg msg[2];	int ret; 	u8 wr_buf[8] = {0, USB5744_CREG_MEM_ADDR, 5,			USB5744_CREG_WRITE, 1,			addr >> 8 & 0xff,			addr & 0xff,			data};	msg[0].addr = client->addr;	msg[0].flags = 0;	msg[0].len = sizeof(wr_buf);	msg[0].buf = wr_buf; 	ret = i2c_transfer(client->adapter, msg, 1);	if (ret < 0)		return ret; 	msg[0].len = 3;	wr_buf[0] = USB5744_CMD_CREG_ACCESS;	wr_buf[1] = USB5744_CMD_CREG_ACCESS_LSB;	wr_buf[2] = 0; 	ret = i2c_transfer(client->adapter, msg, 1);	if (ret < 0)		return ret; 	return 0;} static int onboard_dev_5744_i2c_init(struct i2c_client *client){	struct device *dev = &client->dev;	int ret;	u8 reg; 	/*	 * 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.	 */	ret = onboard_dev_5744_i2c_read_byte(client,					     USB5744_CREG_RUNTIMEFLAGS2, &reg);	if (ret)		return dev_err_probe(dev, ret, "CREG_RUNTIMEFLAGS2 read failed\n"); 	reg |= USB5744_CREG_BYPASS_UDC_SUSPEND;	ret = onboard_dev_5744_i2c_write_byte(client,					      USB5744_CREG_RUNTIMEFLAGS2, reg);	if (ret)		return dev_err_probe(dev, ret, "BYPASS_UDC_SUSPEND bit configuration 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;}#elsestatic int onboard_dev_5744_i2c_init(struct i2c_client *client){	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 (!onboard_dev)		return -ENOMEM; 	onboard_dev->pdata = device_get_match_data(dev);	if (!onboard_dev->pdata)		return -EINVAL; 	if (!onboard_dev->pdata->is_hub)		onboard_dev->always_powered_in_suspend = true; 	onboard_dev->dev = dev; 	err = onboard_dev_get_regulators(onboard_dev);	if (err)		return err; 	onboard_dev->clk = devm_clk_get_optional(dev, NULL);	if (IS_ERR(onboard_dev->clk))		return dev_err_probe(dev, PTR_ERR(onboard_dev->clk),				     "failed to get clock\n"); 	onboard_dev->reset_gpio = devm_gpiod_get_optional(dev, "reset",							  GPIOD_OUT_HIGH);	if (IS_ERR(onboard_dev->reset_gpio))		return dev_err_probe(dev, PTR_ERR(onboard_dev->reset_gpio),				     "failed to get reset GPIO\n"); 	mutex_init(&onboard_dev->lock);	INIT_LIST_HEAD(&onboard_dev->udev_list); 	dev_set_drvdata(dev, onboard_dev); 	err = onboard_dev_power_on(onboard_dev);	if (err)		return err; 	i2c_node = of_parse_phandle(pdev->dev.of_node, "i2c-bus", 0);	if (i2c_node) {		struct i2c_client *client = NULL; #if IS_ENABLED(CONFIG_USB_ONBOARD_DEV_USB5744)		client = of_find_i2c_device_by_node(i2c_node);#endif		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);			onboard_dev->always_powered_in_suspend = true;		} 		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),	 * make sure to re-attach it if needed.

Lines 1–500 of 732.