[PATCH 11/11] usb: misc: usb3503: Support operation with no I2C control

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 




From: Mark Brown <broonie@xxxxxxxxxx>

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: Mark Brown <broonie@xxxxxxxxxx>
Cc: devicetree@xxxxxxxxxxxxxxx
---
 Documentation/devicetree/bindings/usb/usb3503.txt |  3 +-
 drivers/usb/misc/usb3503.c                        | 93 ++++++++++++++++++-----
 2 files changed, 78 insertions(+), 18 deletions(-)

diff --git a/Documentation/devicetree/bindings/usb/usb3503.txt b/Documentation/devicetree/bindings/usb/usb3503.txt
index 44a03f3..a018da4 100644
--- a/Documentation/devicetree/bindings/usb/usb3503.txt
+++ b/Documentation/devicetree/bindings/usb/usb3503.txt
@@ -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
diff --git a/drivers/usb/misc/usb3503.c b/drivers/usb/misc/usb3503.c
index da45ed9..a31641e 100644
--- a/drivers/usb/misc/usb3503.c
+++ b/drivers/usb/misc/usb3503.c
@@ -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);
+	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_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);
+	if (gpio_is_valid(hub->gpio_connect))
+		gpio_set_value_cansleep(hub->gpio_connect, 1);
 
-		hub->mode = mode;
-		dev_info(dev, "switched to HUB mode\n");
+	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@xxxxxxxxx>");
 MODULE_DESCRIPTION("USB3503 USB HUB driver");
-- 
1.8.4.rc1

--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html




[Index of Archives]     [Device Tree Compilter]     [Device Tree Spec]     [Linux Driver Backports]     [Video for Linux]     [Linux USB Devel]     [Linux PCI Devel]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [XFree86]     [Yosemite Backpacking]
  Powered by Linux