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