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

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
...@@ -2,9 +2,10 @@ SMSC USB3503 High-Speed Hub Controller ...@@ -2,9 +2,10 @@ SMSC USB3503 High-Speed Hub Controller
Required properties: Required properties:
- compatible: Should be "smsc,usb3503" or "smsc,usb3503a". - compatible: Should be "smsc,usb3503" or "smsc,usb3503a".
- reg: Specifies the i2c slave address, it should be 0x08.
Optional properties: 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. - connect-gpios: Should specify GPIO for connect.
- disabled-ports: Should specify the ports unused. - disabled-ports: Should specify the ports unused.
'1' or '2' or '3' are availe for this property to describe the port '1' or '2' or '3' are availe for this property to describe the port
......
...@@ -78,22 +78,21 @@ static int usb3503_reset(struct usb3503 *hub, int state) ...@@ -78,22 +78,21 @@ static int usb3503_reset(struct usb3503 *hub, int state)
return 0; 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; 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 */ /* SP_ILOCK: set connect_n, config_n for config */
err = regmap_write(hub->regmap, USB3503_SP_ILOCK, err = regmap_write(hub->regmap, USB3503_SP_ILOCK,
(USB3503_SPILOCK_CONNECT (USB3503_SPILOCK_CONNECT
| USB3503_SPILOCK_CONFIG)); | USB3503_SPILOCK_CONFIG));
if (err < 0) { if (err < 0) {
dev_err(dev, "SP_ILOCK failed (%d)\n", err); dev_err(dev, "SP_ILOCK failed (%d)\n", err);
goto err_hubmode; return err;
} }
/* PDS : Disable For Self Powered Operation */ /* PDS : Disable For Self Powered Operation */
...@@ -103,7 +102,7 @@ static int usb3503_switch_mode(struct usb3503 *hub, enum usb3503_mode mode) ...@@ -103,7 +102,7 @@ static int usb3503_switch_mode(struct usb3503 *hub, enum usb3503_mode mode)
hub->port_off_mask); hub->port_off_mask);
if (err < 0) { if (err < 0) {
dev_err(dev, "PDS failed (%d)\n", err); 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) ...@@ -113,7 +112,7 @@ static int usb3503_switch_mode(struct usb3503 *hub, enum usb3503_mode mode)
USB3503_SELF_BUS_PWR); USB3503_SELF_BUS_PWR);
if (err < 0) { if (err < 0) {
dev_err(dev, "CFG1 failed (%d)\n", err); dev_err(dev, "CFG1 failed (%d)\n", err);
goto err_hubmode; return err;
} }
/* SP_LOCK: clear connect_n, config_n for hub connect */ /* 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) ...@@ -122,14 +121,27 @@ static int usb3503_switch_mode(struct usb3503 *hub, enum usb3503_mode mode)
| USB3503_SPILOCK_CONFIG), 0); | USB3503_SPILOCK_CONFIG), 0);
if (err < 0) { if (err < 0) {
dev_err(dev, "SP_ILOCK failed (%d)\n", err); dev_err(dev, "SP_ILOCK failed (%d)\n", err);
goto err_hubmode; return err;
}
} }
if (gpio_is_valid(hub->gpio_connect)) if (gpio_is_valid(hub->gpio_connect))
gpio_set_value_cansleep(hub->gpio_connect, 1); gpio_set_value_cansleep(hub->gpio_connect, 1);
hub->mode = mode; hub->mode = USB3503_MODE_HUB;
dev_info(dev, "switched to HUB mode\n"); 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; break;
case USB3503_MODE_STANDBY: case USB3503_MODE_STANDBY:
...@@ -145,7 +157,6 @@ static int usb3503_switch_mode(struct usb3503 *hub, enum usb3503_mode mode) ...@@ -145,7 +157,6 @@ static int usb3503_switch_mode(struct usb3503 *hub, enum usb3503_mode mode)
break; break;
} }
err_hubmode:
return err; return err;
} }
...@@ -198,6 +209,9 @@ static int usb3503_probe(struct usb3503 *hub) ...@@ -198,6 +209,9 @@ static int usb3503_probe(struct usb3503 *hub)
hub->mode = mode; 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)) { if (gpio_is_valid(hub->gpio_intn)) {
err = devm_gpio_request_one(dev, hub->gpio_intn, err = devm_gpio_request_one(dev, hub->gpio_intn,
GPIOF_OUT_INIT_HIGH, "usb3503 intn"); GPIOF_OUT_INIT_HIGH, "usb3503 intn");
...@@ -263,6 +277,20 @@ static int usb3503_i2c_probe(struct i2c_client *i2c, ...@@ -263,6 +277,20 @@ static int usb3503_i2c_probe(struct i2c_client *i2c,
return usb3503_probe(hub); 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[] = { static const struct i2c_device_id usb3503_id[] = {
{ USB3503_I2C_NAME, 0 }, { USB3503_I2C_NAME, 0 },
{ } { }
...@@ -278,7 +306,7 @@ static const struct of_device_id usb3503_of_match[] = { ...@@ -278,7 +306,7 @@ static const struct of_device_id usb3503_of_match[] = {
MODULE_DEVICE_TABLE(of, usb3503_of_match); MODULE_DEVICE_TABLE(of, usb3503_of_match);
#endif #endif
static struct i2c_driver usb3503_driver = { static struct i2c_driver usb3503_i2c_driver = {
.driver = { .driver = {
.name = USB3503_I2C_NAME, .name = USB3503_I2C_NAME,
.of_match_table = of_match_ptr(usb3503_of_match), .of_match_table = of_match_ptr(usb3503_of_match),
...@@ -287,7 +315,38 @@ static struct i2c_driver usb3503_driver = { ...@@ -287,7 +315,38 @@ static struct i2c_driver usb3503_driver = {
.id_table = usb3503_id, .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_AUTHOR("Dongjin Kim <tobetter@gmail.com>");
MODULE_DESCRIPTION("USB3503 USB HUB driver"); MODULE_DESCRIPTION("USB3503 USB HUB driver");
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment