Linux Audio

Check our new training course

Loading...
Note: File does not exist in v4.6.
  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");