Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 3f0d1c67 authored by Mark Brown's avatar Mark Brown Committed by Greg Kroah-Hartman
Browse files

usb: misc: usb3503: Support operation with no I2C control



Refactor so that register writes for configuration are only performed if
the device has a regmap provided and also register as a platform driver.
This allows the driver to be used to manage GPIO based control of the
device.

Signed-off-by: default avatarMark Brown <broonie@linaro.org>
Cc: devicetree@vger.kernel.org
Reviewed-by: default avatarDongjin Kim <tobetter@gmail.com>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent e5162d40
Loading
Loading
Loading
Loading
+2 −1
Original line number Diff line number Diff line
@@ -2,9 +2,10 @@ SMSC USB3503 High-Speed Hub Controller

Required properties:
- compatible: Should be "smsc,usb3503" or "smsc,usb3503a".
- reg: Specifies the i2c slave address, it should be 0x08.

Optional properties:
- reg: Specifies the i2c slave address, it is required and should be 0x08
       if I2C is used.
- connect-gpios: Should specify GPIO for connect.
- disabled-ports: Should specify the ports unused.
	'1' or '2' or '3' are availe for this property to describe the port
+76 −17
Original line number Diff line number Diff line
@@ -78,22 +78,21 @@ static int usb3503_reset(struct usb3503 *hub, int state)
	return 0;
}

static int usb3503_switch_mode(struct usb3503 *hub, enum usb3503_mode mode)
static int usb3503_connect(struct usb3503 *hub)
{
	struct device *dev = hub->dev;
	int err = 0;
	int err;

	switch (mode) {
	case USB3503_MODE_HUB:
	usb3503_reset(hub, 1);

	if (hub->regmap) {
		/* SP_ILOCK: set connect_n, config_n for config */
		err = regmap_write(hub->regmap, USB3503_SP_ILOCK,
			   (USB3503_SPILOCK_CONNECT
				 | USB3503_SPILOCK_CONFIG));
		if (err < 0) {
			dev_err(dev, "SP_ILOCK failed (%d)\n", err);
			goto err_hubmode;
			return err;
		}

		/* PDS : Disable For Self Powered Operation */
@@ -103,7 +102,7 @@ static int usb3503_switch_mode(struct usb3503 *hub, enum usb3503_mode mode)
					hub->port_off_mask);
			if (err < 0) {
				dev_err(dev, "PDS failed (%d)\n", err);
				goto err_hubmode;
				return err;
			}
		}

@@ -113,7 +112,7 @@ static int usb3503_switch_mode(struct usb3503 *hub, enum usb3503_mode mode)
					 USB3503_SELF_BUS_PWR);
		if (err < 0) {
			dev_err(dev, "CFG1 failed (%d)\n", err);
			goto err_hubmode;
			return err;
		}

		/* SP_LOCK: clear connect_n, config_n for hub connect */
@@ -122,14 +121,27 @@ static int usb3503_switch_mode(struct usb3503 *hub, enum usb3503_mode mode)
					  | USB3503_SPILOCK_CONFIG), 0);
		if (err < 0) {
			dev_err(dev, "SP_ILOCK failed (%d)\n", err);
			goto err_hubmode;
			return err;
		}
	}

	if (gpio_is_valid(hub->gpio_connect))
		gpio_set_value_cansleep(hub->gpio_connect, 1);

		hub->mode = mode;
	hub->mode = USB3503_MODE_HUB;
	dev_info(dev, "switched to HUB mode\n");

	return 0;
}

static int usb3503_switch_mode(struct usb3503 *hub, enum usb3503_mode mode)
{
	struct device *dev = hub->dev;
	int err = 0;

	switch (mode) {
	case USB3503_MODE_HUB:
		err = usb3503_connect(hub);
		break;

	case USB3503_MODE_STANDBY:
@@ -145,7 +157,6 @@ static int usb3503_switch_mode(struct usb3503 *hub, enum usb3503_mode mode)
		break;
	}

err_hubmode:
	return err;
}

@@ -198,6 +209,9 @@ static int usb3503_probe(struct usb3503 *hub)
		hub->mode = mode;
	}

	if (hub->port_off_mask && !hub->regmap)
		dev_err(dev, "Ports disabled with no control interface\n");

	if (gpio_is_valid(hub->gpio_intn)) {
		err = devm_gpio_request_one(dev, hub->gpio_intn,
				GPIOF_OUT_INIT_HIGH, "usb3503 intn");
@@ -263,6 +277,20 @@ static int usb3503_i2c_probe(struct i2c_client *i2c,
	return usb3503_probe(hub);
}

static int usb3503_platform_probe(struct platform_device *pdev)
{
	struct usb3503 *hub;

	hub = devm_kzalloc(&pdev->dev, sizeof(struct usb3503), GFP_KERNEL);
	if (!hub) {
		dev_err(&pdev->dev, "private data alloc fail\n");
		return -ENOMEM;
	}
	hub->dev = &pdev->dev;

	return usb3503_probe(hub);
}

static const struct i2c_device_id usb3503_id[] = {
	{ USB3503_I2C_NAME, 0 },
	{ }
@@ -278,7 +306,7 @@ static const struct of_device_id usb3503_of_match[] = {
MODULE_DEVICE_TABLE(of, usb3503_of_match);
#endif

static struct i2c_driver usb3503_driver = {
static struct i2c_driver usb3503_i2c_driver = {
	.driver = {
		.name = USB3503_I2C_NAME,
		.of_match_table = of_match_ptr(usb3503_of_match),
@@ -287,7 +315,38 @@ static struct i2c_driver usb3503_driver = {
	.id_table	= usb3503_id,
};

module_i2c_driver(usb3503_driver);
static struct platform_driver usb3503_platform_driver = {
	.driver = {
		.name = USB3503_I2C_NAME,
		.of_match_table = of_match_ptr(usb3503_of_match),
		.owner = THIS_MODULE,
	},
	.probe		= usb3503_platform_probe,
};

static int __init usb3503_init(void)
{
	int err;

	err = i2c_register_driver(THIS_MODULE, &usb3503_i2c_driver);
	if (err != 0)
		pr_err("usb3503: Failed to register I2C driver: %d\n", err);

	err = platform_driver_register(&usb3503_platform_driver);
	if (err != 0)
		pr_err("usb3503: Failed to register platform driver: %d\n",
		       err);

	return 0;
}
module_init(usb3503_init);

static void __exit usb3503_exit(void)
{
	platform_driver_unregister(&usb3503_platform_driver);
	i2c_del_driver(&usb3503_i2c_driver);
}
module_exit(usb3503_exit);

MODULE_AUTHOR("Dongjin Kim <tobetter@gmail.com>");
MODULE_DESCRIPTION("USB3503 USB HUB driver");