Linux Audio

Check our new training course

Loading...
v6.8
  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{
102	struct usb4604 *hub;
103
104	hub = devm_kzalloc(&i2c->dev, sizeof(*hub), GFP_KERNEL);
105	if (!hub)
106		return -ENOMEM;
107
108	i2c_set_clientdata(i2c, hub);
109	hub->dev = &i2c->dev;
110
111	return usb4604_probe(hub);
112}
113
114static int __maybe_unused usb4604_i2c_suspend(struct device *dev)
 
115{
116	struct i2c_client *client = to_i2c_client(dev);
117	struct usb4604 *hub = i2c_get_clientdata(client);
118
119	usb4604_switch_mode(hub, USB4604_MODE_STANDBY);
120
121	return 0;
122}
123
124static int __maybe_unused usb4604_i2c_resume(struct device *dev)
125{
126	struct i2c_client *client = to_i2c_client(dev);
127	struct usb4604 *hub = i2c_get_clientdata(client);
128
129	usb4604_switch_mode(hub, hub->mode);
130
131	return 0;
132}
 
133
134static SIMPLE_DEV_PM_OPS(usb4604_i2c_pm_ops, usb4604_i2c_suspend,
135		usb4604_i2c_resume);
136
137static const struct i2c_device_id usb4604_id[] = {
138	{ "usb4604", 0 },
139	{ }
140};
141MODULE_DEVICE_TABLE(i2c, usb4604_id);
142
143#ifdef CONFIG_OF
144static const struct of_device_id usb4604_of_match[] = {
145	{ .compatible = "smsc,usb4604" },
146	{}
147};
148MODULE_DEVICE_TABLE(of, usb4604_of_match);
149#endif
150
151static struct i2c_driver usb4604_i2c_driver = {
152	.driver = {
153		.name = "usb4604",
154		.pm = pm_ptr(&usb4604_i2c_pm_ops),
155		.of_match_table = of_match_ptr(usb4604_of_match),
156	},
157	.probe		= usb4604_i2c_probe,
158	.id_table	= usb4604_id,
159};
160module_i2c_driver(usb4604_i2c_driver);
161
162MODULE_DESCRIPTION("USB4604 USB HUB driver");
163MODULE_LICENSE("GPL v2");
v4.10.11
 
  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");