usb4604.c 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176
  1. /*
  2. * Driver for SMSC USB4604 USB HSIC 4-port 2.0 hub controller driver
  3. * Based on usb3503 driver
  4. *
  5. * Copyright (c) 2012-2013 Dongjin Kim (tobetter@gmail.com)
  6. * Copyright (c) 2016 Linaro Ltd.
  7. *
  8. * This program is free software; you can redistribute it and/or modify
  9. * it under the terms of the GNU General Public License as published by
  10. * the Free Software Foundation; either version 2 of the License, or
  11. * (at your option) any later version.
  12. *
  13. * This program is distributed in the hope that it will be useful,
  14. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  16. * GNU General Public License for more details.
  17. */
  18. #include <linux/i2c.h>
  19. #include <linux/delay.h>
  20. #include <linux/slab.h>
  21. #include <linux/module.h>
  22. #include <linux/gpio/consumer.h>
  23. enum usb4604_mode {
  24. USB4604_MODE_UNKNOWN,
  25. USB4604_MODE_HUB,
  26. USB4604_MODE_STANDBY,
  27. };
  28. struct usb4604 {
  29. enum usb4604_mode mode;
  30. struct device *dev;
  31. struct gpio_desc *gpio_reset;
  32. };
  33. static void usb4604_reset(struct usb4604 *hub, int state)
  34. {
  35. gpiod_set_value_cansleep(hub->gpio_reset, state);
  36. /* Wait for i2c logic to come up */
  37. if (state)
  38. msleep(250);
  39. }
  40. static int usb4604_connect(struct usb4604 *hub)
  41. {
  42. struct device *dev = hub->dev;
  43. struct i2c_client *client = to_i2c_client(dev);
  44. int err;
  45. u8 connect_cmd[] = { 0xaa, 0x55, 0x00 };
  46. usb4604_reset(hub, 1);
  47. err = i2c_master_send(client, connect_cmd, ARRAY_SIZE(connect_cmd));
  48. if (err < 0) {
  49. usb4604_reset(hub, 0);
  50. return err;
  51. }
  52. hub->mode = USB4604_MODE_HUB;
  53. dev_dbg(dev, "switched to HUB mode\n");
  54. return 0;
  55. }
  56. static int usb4604_switch_mode(struct usb4604 *hub, enum usb4604_mode mode)
  57. {
  58. struct device *dev = hub->dev;
  59. int err = 0;
  60. switch (mode) {
  61. case USB4604_MODE_HUB:
  62. err = usb4604_connect(hub);
  63. break;
  64. case USB4604_MODE_STANDBY:
  65. usb4604_reset(hub, 0);
  66. dev_dbg(dev, "switched to STANDBY mode\n");
  67. break;
  68. default:
  69. dev_err(dev, "unknown mode is requested\n");
  70. err = -EINVAL;
  71. break;
  72. }
  73. return err;
  74. }
  75. static int usb4604_probe(struct usb4604 *hub)
  76. {
  77. struct device *dev = hub->dev;
  78. struct device_node *np = dev->of_node;
  79. struct gpio_desc *gpio;
  80. u32 mode = USB4604_MODE_HUB;
  81. gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW);
  82. if (IS_ERR(gpio))
  83. return PTR_ERR(gpio);
  84. hub->gpio_reset = gpio;
  85. if (of_property_read_u32(np, "initial-mode", &hub->mode))
  86. hub->mode = mode;
  87. return usb4604_switch_mode(hub, hub->mode);
  88. }
  89. static int usb4604_i2c_probe(struct i2c_client *i2c,
  90. const struct i2c_device_id *id)
  91. {
  92. struct usb4604 *hub;
  93. hub = devm_kzalloc(&i2c->dev, sizeof(*hub), GFP_KERNEL);
  94. if (!hub)
  95. return -ENOMEM;
  96. i2c_set_clientdata(i2c, hub);
  97. hub->dev = &i2c->dev;
  98. return usb4604_probe(hub);
  99. }
  100. #ifdef CONFIG_PM_SLEEP
  101. static int usb4604_i2c_suspend(struct device *dev)
  102. {
  103. struct i2c_client *client = to_i2c_client(dev);
  104. struct usb4604 *hub = i2c_get_clientdata(client);
  105. usb4604_switch_mode(hub, USB4604_MODE_STANDBY);
  106. return 0;
  107. }
  108. static int usb4604_i2c_resume(struct device *dev)
  109. {
  110. struct i2c_client *client = to_i2c_client(dev);
  111. struct usb4604 *hub = i2c_get_clientdata(client);
  112. usb4604_switch_mode(hub, hub->mode);
  113. return 0;
  114. }
  115. #endif
  116. static SIMPLE_DEV_PM_OPS(usb4604_i2c_pm_ops, usb4604_i2c_suspend,
  117. usb4604_i2c_resume);
  118. static const struct i2c_device_id usb4604_id[] = {
  119. { "usb4604", 0 },
  120. { }
  121. };
  122. MODULE_DEVICE_TABLE(i2c, usb4604_id);
  123. #ifdef CONFIG_OF
  124. static const struct of_device_id usb4604_of_match[] = {
  125. { .compatible = "smsc,usb4604" },
  126. {}
  127. };
  128. MODULE_DEVICE_TABLE(of, usb4604_of_match);
  129. #endif
  130. static struct i2c_driver usb4604_i2c_driver = {
  131. .driver = {
  132. .name = "usb4604",
  133. .pm = &usb4604_i2c_pm_ops,
  134. .of_match_table = of_match_ptr(usb4604_of_match),
  135. },
  136. .probe = usb4604_i2c_probe,
  137. .id_table = usb4604_id,
  138. };
  139. module_i2c_driver(usb4604_i2c_driver);
  140. MODULE_DESCRIPTION("USB4604 USB HUB driver");
  141. MODULE_LICENSE("GPL v2");