diff options
Diffstat (limited to 'drivers/usb/misc/usb3503.c')
-rw-r--r-- | drivers/usb/misc/usb3503.c | 284 |
1 files changed, 155 insertions, 129 deletions
diff --git a/drivers/usb/misc/usb3503.c b/drivers/usb/misc/usb3503.c index c3578393dde..a31641e18d1 100644 --- a/drivers/usb/misc/usb3503.c +++ b/drivers/usb/misc/usb3503.c @@ -26,6 +26,7 @@ #include <linux/of_gpio.h> #include <linux/platform_device.h> #include <linux/platform_data/usb3503.h> +#include <linux/regmap.h> #define USB3503_VIDL 0x00 #define USB3503_VIDM 0x01 @@ -50,60 +51,25 @@ #define USB3503_CFGP 0xee #define USB3503_CLKSUSP (1 << 7) +#define USB3503_RESET 0xff + struct usb3503 { enum usb3503_mode mode; - struct i2c_client *client; + struct regmap *regmap; + struct device *dev; u8 port_off_mask; int gpio_intn; int gpio_reset; int gpio_connect; }; -static int usb3503_write_register(struct i2c_client *client, - char reg, char data) -{ - return i2c_smbus_write_byte_data(client, reg, data); -} - -static int usb3503_read_register(struct i2c_client *client, char reg) -{ - return i2c_smbus_read_byte_data(client, reg); -} - -static int usb3503_set_bits(struct i2c_client *client, char reg, char req) +static int usb3503_reset(struct usb3503 *hub, int state) { - int err; - - err = usb3503_read_register(client, reg); - if (err < 0) - return err; - - err = usb3503_write_register(client, reg, err | req); - if (err < 0) - return err; - - return 0; -} - -static int usb3503_clear_bits(struct i2c_client *client, char reg, char req) -{ - int err; - - err = usb3503_read_register(client, reg); - if (err < 0) - return err; - - err = usb3503_write_register(client, reg, err & ~req); - if (err < 0) - return err; - - return 0; -} + if (!state && gpio_is_valid(hub->gpio_connect)) + gpio_set_value_cansleep(hub->gpio_connect, 0); -static int usb3503_reset(int gpio_reset, int state) -{ - if (gpio_is_valid(gpio_reset)) - gpio_set_value(gpio_reset, state); + if (gpio_is_valid(hub->gpio_reset)) + gpio_set_value_cansleep(hub->gpio_reset, state); /* Wait T_HUBINIT == 4ms for hub logic to stabilize */ if (state) @@ -112,90 +78,105 @@ static int usb3503_reset(int gpio_reset, int state) return 0; } -static int usb3503_switch_mode(struct usb3503 *hub, enum usb3503_mode mode) +static int usb3503_connect(struct usb3503 *hub) { - struct i2c_client *i2c = hub->client; - int err = 0; + struct device *dev = hub->dev; + int err; - switch (mode) { - case USB3503_MODE_HUB: - usb3503_reset(hub->gpio_reset, 1); + usb3503_reset(hub, 1); + if (hub->regmap) { /* SP_ILOCK: set connect_n, config_n for config */ - err = usb3503_write_register(i2c, USB3503_SP_ILOCK, - (USB3503_SPILOCK_CONNECT + err = regmap_write(hub->regmap, USB3503_SP_ILOCK, + (USB3503_SPILOCK_CONNECT | USB3503_SPILOCK_CONFIG)); if (err < 0) { - dev_err(&i2c->dev, "SP_ILOCK failed (%d)\n", err); - goto err_hubmode; + dev_err(dev, "SP_ILOCK failed (%d)\n", err); + return err; } /* PDS : Disable For Self Powered Operation */ if (hub->port_off_mask) { - err = usb3503_set_bits(i2c, USB3503_PDS, + err = regmap_update_bits(hub->regmap, USB3503_PDS, + hub->port_off_mask, hub->port_off_mask); if (err < 0) { - dev_err(&i2c->dev, "PDS failed (%d)\n", err); - goto err_hubmode; + dev_err(dev, "PDS failed (%d)\n", err); + return err; } } /* CFG1 : SELF_BUS_PWR -> Self-Powerd operation */ - err = usb3503_set_bits(i2c, USB3503_CFG1, USB3503_SELF_BUS_PWR); + err = regmap_update_bits(hub->regmap, USB3503_CFG1, + USB3503_SELF_BUS_PWR, + USB3503_SELF_BUS_PWR); if (err < 0) { - dev_err(&i2c->dev, "CFG1 failed (%d)\n", err); - goto err_hubmode; + dev_err(dev, "CFG1 failed (%d)\n", err); + return err; } /* SP_LOCK: clear connect_n, config_n for hub connect */ - err = usb3503_clear_bits(i2c, USB3503_SP_ILOCK, - (USB3503_SPILOCK_CONNECT - | USB3503_SPILOCK_CONFIG)); + err = regmap_update_bits(hub->regmap, USB3503_SP_ILOCK, + (USB3503_SPILOCK_CONNECT + | USB3503_SPILOCK_CONFIG), 0); if (err < 0) { - dev_err(&i2c->dev, "SP_ILOCK failed (%d)\n", err); - goto err_hubmode; + dev_err(dev, "SP_ILOCK failed (%d)\n", err); + return err; } + } - hub->mode = mode; - dev_info(&i2c->dev, "switched to HUB mode\n"); + if (gpio_is_valid(hub->gpio_connect)) + gpio_set_value_cansleep(hub->gpio_connect, 1); + + 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: - usb3503_reset(hub->gpio_reset, 0); + usb3503_reset(hub, 0); hub->mode = mode; - dev_info(&i2c->dev, "switched to STANDBY mode\n"); + dev_info(dev, "switched to STANDBY mode\n"); break; default: - dev_err(&i2c->dev, "unknown mode is request\n"); + dev_err(dev, "unknown mode is requested\n"); err = -EINVAL; break; } -err_hubmode: return err; } -static int usb3503_probe(struct i2c_client *i2c, const struct i2c_device_id *id) +static const struct regmap_config usb3503_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + + .max_register = USB3503_RESET, +}; + +static int usb3503_probe(struct usb3503 *hub) { - struct usb3503_platform_data *pdata = i2c->dev.platform_data; - struct device_node *np = i2c->dev.of_node; - struct usb3503 *hub; - int err = -ENOMEM; - u32 mode = USB3503_MODE_UNKNOWN; + struct device *dev = hub->dev; + struct usb3503_platform_data *pdata = dev_get_platdata(dev); + struct device_node *np = dev->of_node; + int err; + u32 mode = USB3503_MODE_HUB; const u32 *property; int len; - hub = kzalloc(sizeof(struct usb3503), GFP_KERNEL); - if (!hub) { - dev_err(&i2c->dev, "private data alloc fail\n"); - return err; - } - - i2c_set_clientdata(i2c, hub); - hub->client = i2c; - if (pdata) { hub->port_off_mask = pdata->port_off_mask; hub->gpio_intn = pdata->gpio_intn; @@ -215,10 +196,10 @@ static int usb3503_probe(struct i2c_client *i2c, const struct i2c_device_id *id) } } - hub->gpio_intn = of_get_named_gpio(np, "connect-gpios", 0); + hub->gpio_intn = of_get_named_gpio(np, "intn-gpios", 0); if (hub->gpio_intn == -EPROBE_DEFER) return -EPROBE_DEFER; - hub->gpio_connect = of_get_named_gpio(np, "intn-gpios", 0); + hub->gpio_connect = of_get_named_gpio(np, "connect-gpios", 0); if (hub->gpio_connect == -EPROBE_DEFER) return -EPROBE_DEFER; hub->gpio_reset = of_get_named_gpio(np, "reset-gpios", 0); @@ -228,72 +209,86 @@ static int usb3503_probe(struct i2c_client *i2c, const struct i2c_device_id *id) 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 = gpio_request_one(hub->gpio_intn, + err = devm_gpio_request_one(dev, hub->gpio_intn, GPIOF_OUT_INIT_HIGH, "usb3503 intn"); if (err) { - dev_err(&i2c->dev, - "unable to request GPIO %d as connect pin (%d)\n", - hub->gpio_intn, err); - goto err_out; + dev_err(dev, + "unable to request GPIO %d as connect pin (%d)\n", + hub->gpio_intn, err); + return err; } } if (gpio_is_valid(hub->gpio_connect)) { - err = gpio_request_one(hub->gpio_connect, - GPIOF_OUT_INIT_HIGH, "usb3503 connect"); + err = devm_gpio_request_one(dev, hub->gpio_connect, + GPIOF_OUT_INIT_LOW, "usb3503 connect"); if (err) { - dev_err(&i2c->dev, - "unable to request GPIO %d as connect pin (%d)\n", - hub->gpio_connect, err); - goto err_gpio_connect; + dev_err(dev, + "unable to request GPIO %d as connect pin (%d)\n", + hub->gpio_connect, err); + return err; } } if (gpio_is_valid(hub->gpio_reset)) { - err = gpio_request_one(hub->gpio_reset, + err = devm_gpio_request_one(dev, hub->gpio_reset, GPIOF_OUT_INIT_LOW, "usb3503 reset"); if (err) { - dev_err(&i2c->dev, - "unable to request GPIO %d as reset pin (%d)\n", - hub->gpio_reset, err); - goto err_gpio_reset; + dev_err(dev, + "unable to request GPIO %d as reset pin (%d)\n", + hub->gpio_reset, err); + return err; } } usb3503_switch_mode(hub, hub->mode); - dev_info(&i2c->dev, "%s: probed on %s mode\n", __func__, + dev_info(dev, "%s: probed in %s mode\n", __func__, (hub->mode == USB3503_MODE_HUB) ? "hub" : "standby"); return 0; +} -err_gpio_reset: - if (gpio_is_valid(hub->gpio_connect)) - gpio_free(hub->gpio_connect); -err_gpio_connect: - if (gpio_is_valid(hub->gpio_intn)) - gpio_free(hub->gpio_intn); -err_out: - kfree(hub); +static int usb3503_i2c_probe(struct i2c_client *i2c, + const struct i2c_device_id *id) +{ + struct usb3503 *hub; + int err; - return err; + hub = devm_kzalloc(&i2c->dev, sizeof(struct usb3503), GFP_KERNEL); + if (!hub) { + dev_err(&i2c->dev, "private data alloc fail\n"); + return -ENOMEM; + } + + i2c_set_clientdata(i2c, hub); + hub->regmap = devm_regmap_init_i2c(i2c, &usb3503_regmap_config); + if (IS_ERR(hub->regmap)) { + err = PTR_ERR(hub->regmap); + dev_err(&i2c->dev, "Failed to initialise regmap: %d\n", err); + return err; + } + hub->dev = &i2c->dev; + + return usb3503_probe(hub); } -static int usb3503_remove(struct i2c_client *i2c) +static int usb3503_platform_probe(struct platform_device *pdev) { - struct usb3503 *hub = i2c_get_clientdata(i2c); - - if (gpio_is_valid(hub->gpio_intn)) - gpio_free(hub->gpio_intn); - if (gpio_is_valid(hub->gpio_connect)) - gpio_free(hub->gpio_connect); - if (gpio_is_valid(hub->gpio_reset)) - gpio_free(hub->gpio_reset); + struct usb3503 *hub; - kfree(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 0; + return usb3503_probe(hub); } static const struct i2c_device_id usb3503_id[] = { @@ -305,22 +300,53 @@ MODULE_DEVICE_TABLE(i2c, usb3503_id); #ifdef CONFIG_OF static const struct of_device_id usb3503_of_match[] = { { .compatible = "smsc,usb3503", }, + { .compatible = "smsc,usb3503a", }, {}, }; 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), }, - .probe = usb3503_probe, - .remove = usb3503_remove, + .probe = usb3503_i2c_probe, .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"); |