xref: /openbmc/linux/drivers/usb/misc/usb3503.c (revision 7126a2ae)
15fd54aceSGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0+
26a099c63SDongjin Kim /*
36a099c63SDongjin Kim  * Driver for SMSC USB3503 USB 2.0 hub controller driver
46a099c63SDongjin Kim  *
56a099c63SDongjin Kim  * Copyright (c) 2012-2013 Dongjin Kim (tobetter@gmail.com)
66a099c63SDongjin Kim  */
76a099c63SDongjin Kim 
8657d898aSMarek Szyprowski #include <linux/clk.h>
96a099c63SDongjin Kim #include <linux/i2c.h>
1051d22e85SLinus Walleij #include <linux/gpio/consumer.h>
116a099c63SDongjin Kim #include <linux/delay.h>
126a099c63SDongjin Kim #include <linux/slab.h>
136a099c63SDongjin Kim #include <linux/module.h>
146a099c63SDongjin Kim #include <linux/platform_device.h>
156a099c63SDongjin Kim #include <linux/platform_data/usb3503.h>
1668b14134SMark Brown #include <linux/regmap.h>
176a099c63SDongjin Kim 
186a099c63SDongjin Kim #define USB3503_VIDL		0x00
196a099c63SDongjin Kim #define USB3503_VIDM		0x01
206a099c63SDongjin Kim #define USB3503_PIDL		0x02
216a099c63SDongjin Kim #define USB3503_PIDM		0x03
226a099c63SDongjin Kim #define USB3503_DIDL		0x04
236a099c63SDongjin Kim #define USB3503_DIDM		0x05
246a099c63SDongjin Kim 
256a099c63SDongjin Kim #define USB3503_CFG1		0x06
266a099c63SDongjin Kim #define USB3503_SELF_BUS_PWR	(1 << 7)
276a099c63SDongjin Kim 
286a099c63SDongjin Kim #define USB3503_CFG2		0x07
296a099c63SDongjin Kim #define USB3503_CFG3		0x08
306a099c63SDongjin Kim #define USB3503_NRD		0x09
316a099c63SDongjin Kim 
326a099c63SDongjin Kim #define USB3503_PDS		0x0a
336a099c63SDongjin Kim 
346a099c63SDongjin Kim #define USB3503_SP_ILOCK	0xe7
356a099c63SDongjin Kim #define USB3503_SPILOCK_CONNECT	(1 << 1)
366a099c63SDongjin Kim #define USB3503_SPILOCK_CONFIG	(1 << 0)
376a099c63SDongjin Kim 
386a099c63SDongjin Kim #define USB3503_CFGP		0xee
396a099c63SDongjin Kim #define USB3503_CLKSUSP		(1 << 7)
406a099c63SDongjin Kim 
4168b14134SMark Brown #define USB3503_RESET		0xff
4268b14134SMark Brown 
436a099c63SDongjin Kim struct usb3503 {
446a099c63SDongjin Kim 	enum usb3503_mode	mode;
4568b14134SMark Brown 	struct regmap		*regmap;
4668b14134SMark Brown 	struct device		*dev;
47657d898aSMarek Szyprowski 	struct clk		*clk;
48e8e44a48SDongjin Kim 	u8	port_off_mask;
49b91e6107SEmanuele Ghidoli 	struct gpio_desc	*bypass;
5051d22e85SLinus Walleij 	struct gpio_desc	*intn;
5151d22e85SLinus Walleij 	struct gpio_desc 	*reset;
5251d22e85SLinus Walleij 	struct gpio_desc 	*connect;
53657d898aSMarek Szyprowski 	bool	secondary_ref_clk;
546a099c63SDongjin Kim };
556a099c63SDongjin Kim 
usb3503_connect(struct usb3503 * hub)563f0d1c67SMark Brown static int usb3503_connect(struct usb3503 *hub)
576a099c63SDongjin Kim {
5868b14134SMark Brown 	struct device *dev = hub->dev;
593f0d1c67SMark Brown 	int err;
606a099c63SDongjin Kim 
613f0d1c67SMark Brown 	if (hub->regmap) {
626a099c63SDongjin Kim 		/* SP_ILOCK: set connect_n, config_n for config */
6368b14134SMark Brown 		err = regmap_write(hub->regmap, USB3503_SP_ILOCK,
646a099c63SDongjin Kim 			   (USB3503_SPILOCK_CONNECT
656a099c63SDongjin Kim 				 | USB3503_SPILOCK_CONFIG));
666a099c63SDongjin Kim 		if (err < 0) {
6768b14134SMark Brown 			dev_err(dev, "SP_ILOCK failed (%d)\n", err);
683f0d1c67SMark Brown 			return err;
696a099c63SDongjin Kim 		}
706a099c63SDongjin Kim 
714463e152STobias Jakobi 		/* PDS : Set the ports which are disabled in self-powered mode. */
72e8e44a48SDongjin Kim 		if (hub->port_off_mask) {
7368b14134SMark Brown 			err = regmap_update_bits(hub->regmap, USB3503_PDS,
7468b14134SMark Brown 					hub->port_off_mask,
75e8e44a48SDongjin Kim 					hub->port_off_mask);
766a099c63SDongjin Kim 			if (err < 0) {
7768b14134SMark Brown 				dev_err(dev, "PDS failed (%d)\n", err);
783f0d1c67SMark Brown 				return err;
796a099c63SDongjin Kim 			}
80e8e44a48SDongjin Kim 		}
816a099c63SDongjin Kim 
824463e152STobias Jakobi 		/* CFG1 : Set SELF_BUS_PWR, this enables self-powered operation. */
8368b14134SMark Brown 		err = regmap_update_bits(hub->regmap, USB3503_CFG1,
8468b14134SMark Brown 					 USB3503_SELF_BUS_PWR,
8568b14134SMark Brown 					 USB3503_SELF_BUS_PWR);
866a099c63SDongjin Kim 		if (err < 0) {
8768b14134SMark Brown 			dev_err(dev, "CFG1 failed (%d)\n", err);
883f0d1c67SMark Brown 			return err;
896a099c63SDongjin Kim 		}
906a099c63SDongjin Kim 
916a099c63SDongjin Kim 		/* SP_LOCK: clear connect_n, config_n for hub connect */
9268b14134SMark Brown 		err = regmap_update_bits(hub->regmap, USB3503_SP_ILOCK,
936a099c63SDongjin Kim 					 (USB3503_SPILOCK_CONNECT
9468b14134SMark Brown 					  | USB3503_SPILOCK_CONFIG), 0);
956a099c63SDongjin Kim 		if (err < 0) {
9668b14134SMark Brown 			dev_err(dev, "SP_ILOCK failed (%d)\n", err);
973f0d1c67SMark Brown 			return err;
983f0d1c67SMark Brown 		}
996a099c63SDongjin Kim 	}
1006a099c63SDongjin Kim 
10151d22e85SLinus Walleij 	if (hub->connect)
10251d22e85SLinus Walleij 		gpiod_set_value_cansleep(hub->connect, 1);
1038e7245b8SMark Brown 
1043f0d1c67SMark Brown 	hub->mode = USB3503_MODE_HUB;
10568b14134SMark Brown 	dev_info(dev, "switched to HUB mode\n");
1063f0d1c67SMark Brown 
1073f0d1c67SMark Brown 	return 0;
1083f0d1c67SMark Brown }
1093f0d1c67SMark Brown 
usb3503_switch_mode(struct usb3503 * hub,enum usb3503_mode mode)1103f0d1c67SMark Brown static int usb3503_switch_mode(struct usb3503 *hub, enum usb3503_mode mode)
1113f0d1c67SMark Brown {
1123f0d1c67SMark Brown 	struct device *dev = hub->dev;
113b91e6107SEmanuele Ghidoli 	int rst, bypass, conn;
1143f0d1c67SMark Brown 
1153f0d1c67SMark Brown 	switch (mode) {
1163f0d1c67SMark Brown 	case USB3503_MODE_HUB:
117b04b32cdSEmanuele Ghidoli 		conn = 1;
118b04b32cdSEmanuele Ghidoli 		rst = 0;
119b91e6107SEmanuele Ghidoli 		bypass = 0;
1206a099c63SDongjin Kim 		break;
1216a099c63SDongjin Kim 	case USB3503_MODE_STANDBY:
122b04b32cdSEmanuele Ghidoli 		conn = 0;
123b04b32cdSEmanuele Ghidoli 		rst = 1;
124b91e6107SEmanuele Ghidoli 		bypass = 1;
12568b14134SMark Brown 		dev_info(dev, "switched to STANDBY mode\n");
1266a099c63SDongjin Kim 		break;
127b91e6107SEmanuele Ghidoli 	case USB3503_MODE_BYPASS:
128b91e6107SEmanuele Ghidoli 		conn = 0;
129b91e6107SEmanuele Ghidoli 		rst = 0;
130b91e6107SEmanuele Ghidoli 		bypass = 1;
131b91e6107SEmanuele Ghidoli 		break;
1326a099c63SDongjin Kim 	default:
133dd8e670dSMark Brown 		dev_err(dev, "unknown mode is requested\n");
134b04b32cdSEmanuele Ghidoli 		return -EINVAL;
1356a099c63SDongjin Kim 	}
1366a099c63SDongjin Kim 
137b04b32cdSEmanuele Ghidoli 	if (!conn && hub->connect)
138b04b32cdSEmanuele Ghidoli 		gpiod_set_value_cansleep(hub->connect, 0);
139b04b32cdSEmanuele Ghidoli 
140b04b32cdSEmanuele Ghidoli 	if (hub->reset)
141b04b32cdSEmanuele Ghidoli 		gpiod_set_value_cansleep(hub->reset, rst);
142b04b32cdSEmanuele Ghidoli 
143b91e6107SEmanuele Ghidoli 	if (hub->bypass)
144b91e6107SEmanuele Ghidoli 		gpiod_set_value_cansleep(hub->bypass, bypass);
145b91e6107SEmanuele Ghidoli 
146b04b32cdSEmanuele Ghidoli 	if (conn) {
147b04b32cdSEmanuele Ghidoli 		/* Wait T_HUBINIT == 4ms for hub logic to stabilize */
148b04b32cdSEmanuele Ghidoli 		usleep_range(4000, 10000);
149b04b32cdSEmanuele Ghidoli 		return usb3503_connect(hub);
150b04b32cdSEmanuele Ghidoli 	}
151b04b32cdSEmanuele Ghidoli 
152b04b32cdSEmanuele Ghidoli 	return 0;
1536a099c63SDongjin Kim }
1546a099c63SDongjin Kim 
15568b14134SMark Brown static const struct regmap_config usb3503_regmap_config = {
15668b14134SMark Brown 	.reg_bits = 8,
15768b14134SMark Brown 	.val_bits = 8,
15868b14134SMark Brown 
15968b14134SMark Brown 	.max_register = USB3503_RESET,
16068b14134SMark Brown };
16168b14134SMark Brown 
usb3503_probe(struct usb3503 * hub)1622487e3eeSMark Brown static int usb3503_probe(struct usb3503 *hub)
1636a099c63SDongjin Kim {
1642487e3eeSMark Brown 	struct device *dev = hub->dev;
1652487e3eeSMark Brown 	struct usb3503_platform_data *pdata = dev_get_platdata(dev);
1662487e3eeSMark Brown 	struct device_node *np = dev->of_node;
1672487e3eeSMark Brown 	int err;
1687eb2bf87SDongliang Mu 	bool is_clk_enabled = false;
169e5a0c874SMark Brown 	u32 mode = USB3503_MODE_HUB;
170e8b58b49SDongjin Kim 	const u32 *property;
17151d22e85SLinus Walleij 	enum gpiod_flags flags;
172e8b58b49SDongjin Kim 	int len;
1736a099c63SDongjin Kim 
174eab8050cSDongjin Kim 	if (pdata) {
175e8e44a48SDongjin Kim 		hub->port_off_mask	= pdata->port_off_mask;
1766a099c63SDongjin Kim 		hub->mode		= pdata->initial_mode;
177eab8050cSDongjin Kim 	} else if (np) {
178c0ab6bb0SBen Gamari 		u32 rate = 0;
179e8b58b49SDongjin Kim 		hub->port_off_mask = 0;
180e8b58b49SDongjin Kim 
181c0ab6bb0SBen Gamari 		if (!of_property_read_u32(np, "refclk-frequency", &rate)) {
182657d898aSMarek Szyprowski 			switch (rate) {
183657d898aSMarek Szyprowski 			case 38400000:
184657d898aSMarek Szyprowski 			case 26000000:
185657d898aSMarek Szyprowski 			case 19200000:
186657d898aSMarek Szyprowski 			case 12000000:
187657d898aSMarek Szyprowski 				hub->secondary_ref_clk = 0;
188657d898aSMarek Szyprowski 				break;
189657d898aSMarek Szyprowski 			case 24000000:
190657d898aSMarek Szyprowski 			case 27000000:
191657d898aSMarek Szyprowski 			case 25000000:
192657d898aSMarek Szyprowski 			case 50000000:
193657d898aSMarek Szyprowski 				hub->secondary_ref_clk = 1;
194657d898aSMarek Szyprowski 				break;
195657d898aSMarek Szyprowski 			default:
196657d898aSMarek Szyprowski 				dev_err(dev,
197657d898aSMarek Szyprowski 					"unsupported reference clock rate (%d)\n",
198657d898aSMarek Szyprowski 					(int) rate);
199657d898aSMarek Szyprowski 				return -EINVAL;
200657d898aSMarek Szyprowski 			}
201c0ab6bb0SBen Gamari 		}
202c0ab6bb0SBen Gamari 
203bbe2028fSChunfeng Yun 		hub->clk = devm_clk_get_optional(dev, "refclk");
204bbe2028fSChunfeng Yun 		if (IS_ERR(hub->clk)) {
205c0ab6bb0SBen Gamari 			dev_err(dev, "unable to request refclk (%ld)\n",
206bbe2028fSChunfeng Yun 					PTR_ERR(hub->clk));
207bbe2028fSChunfeng Yun 			return PTR_ERR(hub->clk);
208c0ab6bb0SBen Gamari 		}
209c0ab6bb0SBen Gamari 
210c0ab6bb0SBen Gamari 		if (rate != 0) {
211657d898aSMarek Szyprowski 			err = clk_set_rate(hub->clk, rate);
212657d898aSMarek Szyprowski 			if (err) {
213657d898aSMarek Szyprowski 				dev_err(dev,
214657d898aSMarek Szyprowski 					"unable to set reference clock rate to %d\n",
215657d898aSMarek Szyprowski 					(int)rate);
216657d898aSMarek Szyprowski 				return err;
217657d898aSMarek Szyprowski 			}
218657d898aSMarek Szyprowski 		}
219657d898aSMarek Szyprowski 
220657d898aSMarek Szyprowski 		err = clk_prepare_enable(hub->clk);
221657d898aSMarek Szyprowski 		if (err) {
222bbe2028fSChunfeng Yun 			dev_err(dev, "unable to enable reference clock\n");
223657d898aSMarek Szyprowski 			return err;
224657d898aSMarek Szyprowski 		}
225657d898aSMarek Szyprowski 
2267eb2bf87SDongliang Mu 		is_clk_enabled = true;
227e8b58b49SDongjin Kim 		property = of_get_property(np, "disabled-ports", &len);
228e8b58b49SDongjin Kim 		if (property && (len / sizeof(u32)) > 0) {
229e8b58b49SDongjin Kim 			int i;
230e8b58b49SDongjin Kim 			for (i = 0; i < len / sizeof(u32); i++) {
231e8b58b49SDongjin Kim 				u32 port = be32_to_cpu(property[i]);
232e8b58b49SDongjin Kim 				if ((1 <= port) && (port <= 3))
233e8b58b49SDongjin Kim 					hub->port_off_mask |= (1 << port);
234e8b58b49SDongjin Kim 			}
235e8b58b49SDongjin Kim 		}
236e8b58b49SDongjin Kim 
237eab8050cSDongjin Kim 		of_property_read_u32(np, "initial-mode", &mode);
238eab8050cSDongjin Kim 		hub->mode = mode;
2396a099c63SDongjin Kim 	}
2406a099c63SDongjin Kim 
24151d22e85SLinus Walleij 	if (hub->secondary_ref_clk)
24251d22e85SLinus Walleij 		flags = GPIOD_OUT_LOW;
24351d22e85SLinus Walleij 	else
24451d22e85SLinus Walleij 		flags = GPIOD_OUT_HIGH;
24551d22e85SLinus Walleij 	hub->intn = devm_gpiod_get_optional(dev, "intn", flags);
2467eb2bf87SDongliang Mu 	if (IS_ERR(hub->intn)) {
2477eb2bf87SDongliang Mu 		err = PTR_ERR(hub->intn);
2487eb2bf87SDongliang Mu 		goto err_clk;
2497eb2bf87SDongliang Mu 	}
25051d22e85SLinus Walleij 	if (hub->intn)
25151d22e85SLinus Walleij 		gpiod_set_consumer_name(hub->intn, "usb3503 intn");
2523f0d1c67SMark Brown 
25351d22e85SLinus Walleij 	hub->connect = devm_gpiod_get_optional(dev, "connect", GPIOD_OUT_LOW);
2547eb2bf87SDongliang Mu 	if (IS_ERR(hub->connect)) {
2557eb2bf87SDongliang Mu 		err = PTR_ERR(hub->connect);
2567eb2bf87SDongliang Mu 		goto err_clk;
2577eb2bf87SDongliang Mu 	}
25851d22e85SLinus Walleij 	if (hub->connect)
25951d22e85SLinus Walleij 		gpiod_set_consumer_name(hub->connect, "usb3503 connect");
2606a099c63SDongjin Kim 
261b91e6107SEmanuele Ghidoli 	hub->bypass = devm_gpiod_get_optional(dev, "bypass", GPIOD_OUT_HIGH);
262b91e6107SEmanuele Ghidoli 	if (IS_ERR(hub->bypass)) {
263b91e6107SEmanuele Ghidoli 		err = PTR_ERR(hub->bypass);
264b91e6107SEmanuele Ghidoli 		goto err_clk;
265b91e6107SEmanuele Ghidoli 	}
266b91e6107SEmanuele Ghidoli 	if (hub->bypass)
267b91e6107SEmanuele Ghidoli 		gpiod_set_consumer_name(hub->bypass, "usb3503 bypass");
268b91e6107SEmanuele Ghidoli 
26951d22e85SLinus Walleij 	hub->reset = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH);
2707eb2bf87SDongliang Mu 	if (IS_ERR(hub->reset)) {
2717eb2bf87SDongliang Mu 		err = PTR_ERR(hub->reset);
2727eb2bf87SDongliang Mu 		goto err_clk;
2737eb2bf87SDongliang Mu 	}
27451d22e85SLinus Walleij 	if (hub->reset) {
275b8626f1dSStefan Agner 		/* Datasheet defines a hardware reset to be at least 100us */
276b8626f1dSStefan Agner 		usleep_range(100, 10000);
27751d22e85SLinus Walleij 		gpiod_set_consumer_name(hub->reset, "usb3503 reset");
2786a099c63SDongjin Kim 	}
27951d22e85SLinus Walleij 
28051d22e85SLinus Walleij 	if (hub->port_off_mask && !hub->regmap)
28151d22e85SLinus Walleij 		dev_err(dev, "Ports disabled with no control interface\n");
2826a099c63SDongjin Kim 
283eab8050cSDongjin Kim 	usb3503_switch_mode(hub, hub->mode);
2846a099c63SDongjin Kim 
285dd8e670dSMark Brown 	dev_info(dev, "%s: probed in %s mode\n", __func__,
2866a099c63SDongjin Kim 			(hub->mode == USB3503_MODE_HUB) ? "hub" : "standby");
2876a099c63SDongjin Kim 
2886a099c63SDongjin Kim 	return 0;
2897eb2bf87SDongliang Mu 
2907eb2bf87SDongliang Mu err_clk:
2917eb2bf87SDongliang Mu 	if (is_clk_enabled)
2927eb2bf87SDongliang Mu 		clk_disable_unprepare(hub->clk);
2937eb2bf87SDongliang Mu 	return err;
2946a099c63SDongjin Kim }
2956a099c63SDongjin Kim 
usb3503_i2c_probe(struct i2c_client * i2c)2964b1e537aSUwe Kleine-König static int usb3503_i2c_probe(struct i2c_client *i2c)
2972487e3eeSMark Brown {
2982487e3eeSMark Brown 	struct usb3503 *hub;
2992487e3eeSMark Brown 	int err;
3002487e3eeSMark Brown 
3012487e3eeSMark Brown 	hub = devm_kzalloc(&i2c->dev, sizeof(struct usb3503), GFP_KERNEL);
30239d98bdcSPeter Chen 	if (!hub)
3032487e3eeSMark Brown 		return -ENOMEM;
3042487e3eeSMark Brown 
3052487e3eeSMark Brown 	i2c_set_clientdata(i2c, hub);
3062487e3eeSMark Brown 	hub->regmap = devm_regmap_init_i2c(i2c, &usb3503_regmap_config);
3072487e3eeSMark Brown 	if (IS_ERR(hub->regmap)) {
3082487e3eeSMark Brown 		err = PTR_ERR(hub->regmap);
3092487e3eeSMark Brown 		dev_err(&i2c->dev, "Failed to initialise regmap: %d\n", err);
3102487e3eeSMark Brown 		return err;
3112487e3eeSMark Brown 	}
3122487e3eeSMark Brown 	hub->dev = &i2c->dev;
3132487e3eeSMark Brown 
3142487e3eeSMark Brown 	return usb3503_probe(hub);
3152487e3eeSMark Brown }
3162487e3eeSMark Brown 
usb3503_i2c_remove(struct i2c_client * i2c)317ed5c2f5fSUwe Kleine-König static void usb3503_i2c_remove(struct i2c_client *i2c)
31862c32e46SKrzysztof Kozlowski {
31962c32e46SKrzysztof Kozlowski 	struct usb3503 *hub;
32062c32e46SKrzysztof Kozlowski 
32162c32e46SKrzysztof Kozlowski 	hub = i2c_get_clientdata(i2c);
32262c32e46SKrzysztof Kozlowski 	clk_disable_unprepare(hub->clk);
32362c32e46SKrzysztof Kozlowski }
32462c32e46SKrzysztof Kozlowski 
usb3503_platform_probe(struct platform_device * pdev)3253f0d1c67SMark Brown static int usb3503_platform_probe(struct platform_device *pdev)
3263f0d1c67SMark Brown {
3273f0d1c67SMark Brown 	struct usb3503 *hub;
3283f0d1c67SMark Brown 
3293f0d1c67SMark Brown 	hub = devm_kzalloc(&pdev->dev, sizeof(struct usb3503), GFP_KERNEL);
33039d98bdcSPeter Chen 	if (!hub)
3313f0d1c67SMark Brown 		return -ENOMEM;
3323f0d1c67SMark Brown 	hub->dev = &pdev->dev;
333495660cbSKrzysztof Kozlowski 	platform_set_drvdata(pdev, hub);
3343f0d1c67SMark Brown 
3353f0d1c67SMark Brown 	return usb3503_probe(hub);
3363f0d1c67SMark Brown }
3373f0d1c67SMark Brown 
usb3503_platform_remove(struct platform_device * pdev)33881a7d006SUwe Kleine-König static void usb3503_platform_remove(struct platform_device *pdev)
33962c32e46SKrzysztof Kozlowski {
34062c32e46SKrzysztof Kozlowski 	struct usb3503 *hub;
34162c32e46SKrzysztof Kozlowski 
34262c32e46SKrzysztof Kozlowski 	hub = platform_get_drvdata(pdev);
34362c32e46SKrzysztof Kozlowski 	clk_disable_unprepare(hub->clk);
34462c32e46SKrzysztof Kozlowski }
34562c32e46SKrzysztof Kozlowski 
usb3503_suspend(struct usb3503 * hub)346879a4a66SPaul Cercueil static int __maybe_unused usb3503_suspend(struct usb3503 *hub)
347ecdc071dSJoonyoung Shim {
348ecdc071dSJoonyoung Shim 	usb3503_switch_mode(hub, USB3503_MODE_STANDBY);
349ecdc071dSJoonyoung Shim 	clk_disable_unprepare(hub->clk);
350ecdc071dSJoonyoung Shim 
351ecdc071dSJoonyoung Shim 	return 0;
352ecdc071dSJoonyoung Shim }
353ecdc071dSJoonyoung Shim 
usb3503_resume(struct usb3503 * hub)354879a4a66SPaul Cercueil static int __maybe_unused usb3503_resume(struct usb3503 *hub)
355ecdc071dSJoonyoung Shim {
356ecdc071dSJoonyoung Shim 	clk_prepare_enable(hub->clk);
357ecdc071dSJoonyoung Shim 	usb3503_switch_mode(hub, hub->mode);
358ecdc071dSJoonyoung Shim 
359ecdc071dSJoonyoung Shim 	return 0;
360ecdc071dSJoonyoung Shim }
361f84f9ae3SMarek Szyprowski 
usb3503_i2c_suspend(struct device * dev)362879a4a66SPaul Cercueil static int __maybe_unused usb3503_i2c_suspend(struct device *dev)
363f84f9ae3SMarek Szyprowski {
364f84f9ae3SMarek Szyprowski 	struct i2c_client *client = to_i2c_client(dev);
365f84f9ae3SMarek Szyprowski 
366f84f9ae3SMarek Szyprowski 	return usb3503_suspend(i2c_get_clientdata(client));
367f84f9ae3SMarek Szyprowski }
368f84f9ae3SMarek Szyprowski 
usb3503_i2c_resume(struct device * dev)369879a4a66SPaul Cercueil static int __maybe_unused usb3503_i2c_resume(struct device *dev)
370f84f9ae3SMarek Szyprowski {
371f84f9ae3SMarek Szyprowski 	struct i2c_client *client = to_i2c_client(dev);
372f84f9ae3SMarek Szyprowski 
373f84f9ae3SMarek Szyprowski 	return usb3503_resume(i2c_get_clientdata(client));
374f84f9ae3SMarek Szyprowski }
375f84f9ae3SMarek Szyprowski 
usb3503_platform_suspend(struct device * dev)376879a4a66SPaul Cercueil static int __maybe_unused usb3503_platform_suspend(struct device *dev)
377f84f9ae3SMarek Szyprowski {
378f84f9ae3SMarek Szyprowski 	return usb3503_suspend(dev_get_drvdata(dev));
379f84f9ae3SMarek Szyprowski }
380f84f9ae3SMarek Szyprowski 
usb3503_platform_resume(struct device * dev)381879a4a66SPaul Cercueil static int __maybe_unused usb3503_platform_resume(struct device *dev)
382f84f9ae3SMarek Szyprowski {
383f84f9ae3SMarek Szyprowski 	return usb3503_resume(dev_get_drvdata(dev));
384f84f9ae3SMarek Szyprowski }
385ecdc071dSJoonyoung Shim 
386ecdc071dSJoonyoung Shim static SIMPLE_DEV_PM_OPS(usb3503_i2c_pm_ops, usb3503_i2c_suspend,
387ecdc071dSJoonyoung Shim 		usb3503_i2c_resume);
388ecdc071dSJoonyoung Shim 
389f84f9ae3SMarek Szyprowski static SIMPLE_DEV_PM_OPS(usb3503_platform_pm_ops, usb3503_platform_suspend,
390f84f9ae3SMarek Szyprowski 		usb3503_platform_resume);
391f84f9ae3SMarek Szyprowski 
3926a099c63SDongjin Kim static const struct i2c_device_id usb3503_id[] = {
3936a099c63SDongjin Kim 	{ USB3503_I2C_NAME, 0 },
3946a099c63SDongjin Kim 	{ }
3956a099c63SDongjin Kim };
3966a099c63SDongjin Kim MODULE_DEVICE_TABLE(i2c, usb3503_id);
3976a099c63SDongjin Kim 
398eab8050cSDongjin Kim #ifdef CONFIG_OF
399eab8050cSDongjin Kim static const struct of_device_id usb3503_of_match[] = {
400eab8050cSDongjin Kim 	{ .compatible = "smsc,usb3503", },
4015bdd1f4aSMark Brown 	{ .compatible = "smsc,usb3503a", },
402b91e6107SEmanuele Ghidoli 	{ .compatible = "smsc,usb3803", },
403eab8050cSDongjin Kim 	{},
404eab8050cSDongjin Kim };
405eab8050cSDongjin Kim MODULE_DEVICE_TABLE(of, usb3503_of_match);
406eab8050cSDongjin Kim #endif
407eab8050cSDongjin Kim 
4083f0d1c67SMark Brown static struct i2c_driver usb3503_i2c_driver = {
4096a099c63SDongjin Kim 	.driver = {
4106a099c63SDongjin Kim 		.name = USB3503_I2C_NAME,
411879a4a66SPaul Cercueil 		.pm = pm_ptr(&usb3503_i2c_pm_ops),
412eab8050cSDongjin Kim 		.of_match_table = of_match_ptr(usb3503_of_match),
4136a099c63SDongjin Kim 	},
414*7126a2aeSUwe Kleine-König 	.probe		= usb3503_i2c_probe,
41562c32e46SKrzysztof Kozlowski 	.remove		= usb3503_i2c_remove,
4166a099c63SDongjin Kim 	.id_table	= usb3503_id,
4176a099c63SDongjin Kim };
4186a099c63SDongjin Kim 
4193f0d1c67SMark Brown static struct platform_driver usb3503_platform_driver = {
4203f0d1c67SMark Brown 	.driver = {
4213f0d1c67SMark Brown 		.name = USB3503_I2C_NAME,
4223f0d1c67SMark Brown 		.of_match_table = of_match_ptr(usb3503_of_match),
423879a4a66SPaul Cercueil 		.pm = pm_ptr(&usb3503_platform_pm_ops),
4243f0d1c67SMark Brown 	},
4253f0d1c67SMark Brown 	.probe		= usb3503_platform_probe,
42681a7d006SUwe Kleine-König 	.remove_new	= usb3503_platform_remove,
4273f0d1c67SMark Brown };
4283f0d1c67SMark Brown 
usb3503_init(void)4293f0d1c67SMark Brown static int __init usb3503_init(void)
4303f0d1c67SMark Brown {
4313f0d1c67SMark Brown 	int err;
4323f0d1c67SMark Brown 
433aa5b477dSAndrew F. Davis 	err = i2c_add_driver(&usb3503_i2c_driver);
4347a1e838dSMichal Simek 	if (err) {
4353f0d1c67SMark Brown 		pr_err("usb3503: Failed to register I2C driver: %d\n", err);
4367a1e838dSMichal Simek 		return err;
4377a1e838dSMichal Simek 	}
4383f0d1c67SMark Brown 
4393f0d1c67SMark Brown 	err = platform_driver_register(&usb3503_platform_driver);
4407a1e838dSMichal Simek 	if (err) {
4413f0d1c67SMark Brown 		pr_err("usb3503: Failed to register platform driver: %d\n",
4423f0d1c67SMark Brown 		       err);
4437a1e838dSMichal Simek 		i2c_del_driver(&usb3503_i2c_driver);
4447a1e838dSMichal Simek 		return err;
4457a1e838dSMichal Simek 	}
4463f0d1c67SMark Brown 
4473f0d1c67SMark Brown 	return 0;
4483f0d1c67SMark Brown }
4493f0d1c67SMark Brown module_init(usb3503_init);
4503f0d1c67SMark Brown 
usb3503_exit(void)4513f0d1c67SMark Brown static void __exit usb3503_exit(void)
4523f0d1c67SMark Brown {
4533f0d1c67SMark Brown 	platform_driver_unregister(&usb3503_platform_driver);
4543f0d1c67SMark Brown 	i2c_del_driver(&usb3503_i2c_driver);
4553f0d1c67SMark Brown }
4563f0d1c67SMark Brown module_exit(usb3503_exit);
4576a099c63SDongjin Kim 
4586a099c63SDongjin Kim MODULE_AUTHOR("Dongjin Kim <tobetter@gmail.com>");
4596a099c63SDongjin Kim MODULE_DESCRIPTION("USB3503 USB HUB driver");
4606a099c63SDongjin Kim MODULE_LICENSE("GPL");
461