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;
49*b91e6107SEmanuele 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;
113*b91e6107SEmanuele 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;
119*b91e6107SEmanuele Ghidoli bypass = 0;
1206a099c63SDongjin Kim break;
1216a099c63SDongjin Kim case USB3503_MODE_STANDBY:
122b04b32cdSEmanuele Ghidoli conn = 0;
123b04b32cdSEmanuele Ghidoli rst = 1;
124*b91e6107SEmanuele Ghidoli bypass = 1;
12568b14134SMark Brown dev_info(dev, "switched to STANDBY mode\n");
1266a099c63SDongjin Kim break;
127*b91e6107SEmanuele Ghidoli case USB3503_MODE_BYPASS:
128*b91e6107SEmanuele Ghidoli conn = 0;
129*b91e6107SEmanuele Ghidoli rst = 0;
130*b91e6107SEmanuele Ghidoli bypass = 1;
131*b91e6107SEmanuele 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
143*b91e6107SEmanuele Ghidoli if (hub->bypass)
144*b91e6107SEmanuele Ghidoli gpiod_set_value_cansleep(hub->bypass, bypass);
145*b91e6107SEmanuele 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
261*b91e6107SEmanuele Ghidoli hub->bypass = devm_gpiod_get_optional(dev, "bypass", GPIOD_OUT_HIGH);
262*b91e6107SEmanuele Ghidoli if (IS_ERR(hub->bypass)) {
263*b91e6107SEmanuele Ghidoli err = PTR_ERR(hub->bypass);
264*b91e6107SEmanuele Ghidoli goto err_clk;
265*b91e6107SEmanuele Ghidoli }
266*b91e6107SEmanuele Ghidoli if (hub->bypass)
267*b91e6107SEmanuele Ghidoli gpiod_set_consumer_name(hub->bypass, "usb3503 bypass");
268*b91e6107SEmanuele 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)33862c32e46SKrzysztof Kozlowski 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)34662c32e46SKrzysztof Kozlowski static int __maybe_unused usb3503_suspend(struct usb3503 *hub)
34762c32e46SKrzysztof Kozlowski {
348879a4a66SPaul Cercueil 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)354ecdc071dSJoonyoung Shim static int __maybe_unused usb3503_resume(struct usb3503 *hub)
355ecdc071dSJoonyoung Shim {
356879a4a66SPaul Cercueil clk_prepare_enable(hub->clk);
357ecdc071dSJoonyoung Shim usb3503_switch_mode(hub, hub->mode);
358ecdc071dSJoonyoung Shim
359ecdc071dSJoonyoung Shim return 0;
360ecdc071dSJoonyoung Shim }
361ecdc071dSJoonyoung Shim
usb3503_i2c_suspend(struct device * dev)362ecdc071dSJoonyoung Shim static int __maybe_unused usb3503_i2c_suspend(struct device *dev)
363f84f9ae3SMarek Szyprowski {
364879a4a66SPaul Cercueil 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)369f84f9ae3SMarek Szyprowski static int __maybe_unused usb3503_i2c_resume(struct device *dev)
370f84f9ae3SMarek Szyprowski {
371879a4a66SPaul Cercueil 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)376f84f9ae3SMarek Szyprowski static int __maybe_unused usb3503_platform_suspend(struct device *dev)
377f84f9ae3SMarek Szyprowski {
378879a4a66SPaul Cercueil return usb3503_suspend(dev_get_drvdata(dev));
379f84f9ae3SMarek Szyprowski }
380f84f9ae3SMarek Szyprowski
usb3503_platform_resume(struct device * dev)381f84f9ae3SMarek Szyprowski static int __maybe_unused usb3503_platform_resume(struct device *dev)
382f84f9ae3SMarek Szyprowski {
383879a4a66SPaul Cercueil return usb3503_resume(dev_get_drvdata(dev));
384f84f9ae3SMarek Szyprowski }
385f84f9ae3SMarek Szyprowski
386f84f9ae3SMarek Szyprowski static SIMPLE_DEV_PM_OPS(usb3503_i2c_pm_ops, usb3503_i2c_suspend,
387ecdc071dSJoonyoung Shim usb3503_i2c_resume);
388ecdc071dSJoonyoung Shim
389ecdc071dSJoonyoung Shim static SIMPLE_DEV_PM_OPS(usb3503_platform_pm_ops, usb3503_platform_suspend,
390ecdc071dSJoonyoung Shim usb3503_platform_resume);
391f84f9ae3SMarek Szyprowski
392f84f9ae3SMarek Szyprowski static const struct i2c_device_id usb3503_id[] = {
393f84f9ae3SMarek Szyprowski { USB3503_I2C_NAME, 0 },
3946a099c63SDongjin Kim { }
3956a099c63SDongjin Kim };
3966a099c63SDongjin Kim MODULE_DEVICE_TABLE(i2c, usb3503_id);
3976a099c63SDongjin Kim
3986a099c63SDongjin Kim #ifdef CONFIG_OF
3996a099c63SDongjin Kim static const struct of_device_id usb3503_of_match[] = {
400eab8050cSDongjin Kim { .compatible = "smsc,usb3503", },
401eab8050cSDongjin Kim { .compatible = "smsc,usb3503a", },
402eab8050cSDongjin Kim { .compatible = "smsc,usb3803", },
4035bdd1f4aSMark Brown {},
404*b91e6107SEmanuele Ghidoli };
405eab8050cSDongjin Kim MODULE_DEVICE_TABLE(of, usb3503_of_match);
406eab8050cSDongjin Kim #endif
407eab8050cSDongjin Kim
408eab8050cSDongjin Kim static struct i2c_driver usb3503_i2c_driver = {
409eab8050cSDongjin Kim .driver = {
4103f0d1c67SMark Brown .name = USB3503_I2C_NAME,
4116a099c63SDongjin Kim .pm = pm_ptr(&usb3503_i2c_pm_ops),
4126a099c63SDongjin Kim .of_match_table = of_match_ptr(usb3503_of_match),
413879a4a66SPaul Cercueil },
414eab8050cSDongjin Kim .probe = usb3503_i2c_probe,
4156a099c63SDongjin Kim .remove = usb3503_i2c_remove,
4164b1e537aSUwe Kleine-König .id_table = usb3503_id,
41762c32e46SKrzysztof Kozlowski };
4186a099c63SDongjin Kim
4196a099c63SDongjin Kim static struct platform_driver usb3503_platform_driver = {
4206a099c63SDongjin Kim .driver = {
4213f0d1c67SMark Brown .name = USB3503_I2C_NAME,
4223f0d1c67SMark Brown .of_match_table = of_match_ptr(usb3503_of_match),
4233f0d1c67SMark Brown .pm = pm_ptr(&usb3503_platform_pm_ops),
4243f0d1c67SMark Brown },
425879a4a66SPaul Cercueil .probe = usb3503_platform_probe,
4263f0d1c67SMark Brown .remove_new = usb3503_platform_remove,
4273f0d1c67SMark Brown };
42862c32e46SKrzysztof Kozlowski
usb3503_init(void)4293f0d1c67SMark Brown static int __init usb3503_init(void)
4303f0d1c67SMark Brown {
4313f0d1c67SMark Brown int err;
4323f0d1c67SMark Brown
4333f0d1c67SMark Brown err = i2c_add_driver(&usb3503_i2c_driver);
4343f0d1c67SMark Brown if (err) {
435aa5b477dSAndrew F. Davis pr_err("usb3503: Failed to register I2C driver: %d\n", err);
4367a1e838dSMichal Simek return err;
4373f0d1c67SMark Brown }
4387a1e838dSMichal Simek
4397a1e838dSMichal Simek err = platform_driver_register(&usb3503_platform_driver);
4403f0d1c67SMark Brown if (err) {
4413f0d1c67SMark Brown pr_err("usb3503: Failed to register platform driver: %d\n",
4427a1e838dSMichal Simek err);
4433f0d1c67SMark Brown i2c_del_driver(&usb3503_i2c_driver);
4443f0d1c67SMark Brown return err;
4457a1e838dSMichal Simek }
4467a1e838dSMichal Simek
4477a1e838dSMichal Simek 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);
4573f0d1c67SMark Brown
4583f0d1c67SMark Brown MODULE_AUTHOR("Dongjin Kim <tobetter@gmail.com>");
4596a099c63SDongjin Kim MODULE_DESCRIPTION("USB3503 USB HUB driver");
4606a099c63SDongjin Kim MODULE_LICENSE("GPL");
4616a099c63SDongjin Kim