linux/drivers/usb/misc/usb4604.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0+
   2/*
   3 * Driver for SMSC USB4604 USB HSIC 4-port 2.0 hub controller driver
   4 * Based on usb3503 driver
   5 *
   6 * Copyright (c) 2012-2013 Dongjin Kim (tobetter@gmail.com)
   7 * Copyright (c) 2016 Linaro Ltd.
   8 */
   9
  10#include <linux/i2c.h>
  11#include <linux/delay.h>
  12#include <linux/slab.h>
  13#include <linux/module.h>
  14#include <linux/gpio/consumer.h>
  15
  16enum usb4604_mode {
  17        USB4604_MODE_UNKNOWN,
  18        USB4604_MODE_HUB,
  19        USB4604_MODE_STANDBY,
  20};
  21
  22struct usb4604 {
  23        enum usb4604_mode       mode;
  24        struct device           *dev;
  25        struct gpio_desc        *gpio_reset;
  26};
  27
  28static void usb4604_reset(struct usb4604 *hub, int state)
  29{
  30        gpiod_set_value_cansleep(hub->gpio_reset, state);
  31
  32        /* Wait for i2c logic to come up */
  33        if (state)
  34                msleep(250);
  35}
  36
  37static int usb4604_connect(struct usb4604 *hub)
  38{
  39        struct device *dev = hub->dev;
  40        struct i2c_client *client = to_i2c_client(dev);
  41        int err;
  42        u8 connect_cmd[] = { 0xaa, 0x55, 0x00 };
  43
  44        usb4604_reset(hub, 1);
  45
  46        err = i2c_master_send(client, connect_cmd, ARRAY_SIZE(connect_cmd));
  47        if (err < 0) {
  48                usb4604_reset(hub, 0);
  49                return err;
  50        }
  51
  52        hub->mode = USB4604_MODE_HUB;
  53        dev_dbg(dev, "switched to HUB mode\n");
  54
  55        return 0;
  56}
  57
  58static int usb4604_switch_mode(struct usb4604 *hub, enum usb4604_mode mode)
  59{
  60        struct device *dev = hub->dev;
  61        int err = 0;
  62
  63        switch (mode) {
  64        case USB4604_MODE_HUB:
  65                err = usb4604_connect(hub);
  66                break;
  67
  68        case USB4604_MODE_STANDBY:
  69                usb4604_reset(hub, 0);
  70                dev_dbg(dev, "switched to STANDBY mode\n");
  71                break;
  72
  73        default:
  74                dev_err(dev, "unknown mode is requested\n");
  75                err = -EINVAL;
  76                break;
  77        }
  78
  79        return err;
  80}
  81
  82static int usb4604_probe(struct usb4604 *hub)
  83{
  84        struct device *dev = hub->dev;
  85        struct device_node *np = dev->of_node;
  86        struct gpio_desc *gpio;
  87        u32 mode = USB4604_MODE_HUB;
  88
  89        gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW);
  90        if (IS_ERR(gpio))
  91                return PTR_ERR(gpio);
  92        hub->gpio_reset = gpio;
  93
  94        if (of_property_read_u32(np, "initial-mode", &hub->mode))
  95                hub->mode = mode;
  96
  97        return usb4604_switch_mode(hub, hub->mode);
  98}
  99
 100static int usb4604_i2c_probe(struct i2c_client *i2c,
 101                             const struct i2c_device_id *id)
 102{
 103        struct usb4604 *hub;
 104
 105        hub = devm_kzalloc(&i2c->dev, sizeof(*hub), GFP_KERNEL);
 106        if (!hub)
 107                return -ENOMEM;
 108
 109        i2c_set_clientdata(i2c, hub);
 110        hub->dev = &i2c->dev;
 111
 112        return usb4604_probe(hub);
 113}
 114
 115static int __maybe_unused usb4604_i2c_suspend(struct device *dev)
 116{
 117        struct i2c_client *client = to_i2c_client(dev);
 118        struct usb4604 *hub = i2c_get_clientdata(client);
 119
 120        usb4604_switch_mode(hub, USB4604_MODE_STANDBY);
 121
 122        return 0;
 123}
 124
 125static int __maybe_unused usb4604_i2c_resume(struct device *dev)
 126{
 127        struct i2c_client *client = to_i2c_client(dev);
 128        struct usb4604 *hub = i2c_get_clientdata(client);
 129
 130        usb4604_switch_mode(hub, hub->mode);
 131
 132        return 0;
 133}
 134
 135static SIMPLE_DEV_PM_OPS(usb4604_i2c_pm_ops, usb4604_i2c_suspend,
 136                usb4604_i2c_resume);
 137
 138static const struct i2c_device_id usb4604_id[] = {
 139        { "usb4604", 0 },
 140        { }
 141};
 142MODULE_DEVICE_TABLE(i2c, usb4604_id);
 143
 144#ifdef CONFIG_OF
 145static const struct of_device_id usb4604_of_match[] = {
 146        { .compatible = "smsc,usb4604" },
 147        {}
 148};
 149MODULE_DEVICE_TABLE(of, usb4604_of_match);
 150#endif
 151
 152static struct i2c_driver usb4604_i2c_driver = {
 153        .driver = {
 154                .name = "usb4604",
 155                .pm = pm_ptr(&usb4604_i2c_pm_ops),
 156                .of_match_table = of_match_ptr(usb4604_of_match),
 157        },
 158        .probe          = usb4604_i2c_probe,
 159        .id_table       = usb4604_id,
 160};
 161module_i2c_driver(usb4604_i2c_driver);
 162
 163MODULE_DESCRIPTION("USB4604 USB HUB driver");
 164MODULE_LICENSE("GPL v2");
 165