usb: misc: usb3503: Support operation with no I2C control
authorMark Brown <broonie@linaro.org>
Fri, 9 Aug 2013 10:41:58 +0000 (11:41 +0100)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Mon, 12 Aug 2013 19:20:27 +0000 (12:20 -0700)
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@linaro.org>
Cc: devicetree@vger.kernel.org
Reviewed-by: Dongjin Kim <tobetter@gmail.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Documentation/devicetree/bindings/usb/usb3503.txt
drivers/usb/misc/usb3503.c

index 44a03f33842486d0b30ddab7029e9d4754bfc077..a018da4a7ad7527a5b926b6024362dd03a6f2ce1 100644 (file)
@@ -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
index da45ed9713321edd1aba7e674c62dd86bf8d8c8f..a31641e18d198fa12ffde7b5bbc32cac01ae1d73 100644 (file)
@@ -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@gmail.com>");
 MODULE_DESCRIPTION("USB3503 USB HUB driver");