net: mdio: mvusb: Fix an error handling path in mvusb_mdio_probe()
[platform/kernel/linux-starfive.git] / drivers / net / mdio / mdio-mvusb.c
1 // SPDX-License-Identifier: GPL-2.0
2
3 #include <linux/kernel.h>
4 #include <linux/module.h>
5 #include <linux/of_mdio.h>
6 #include <linux/phy.h>
7 #include <linux/usb.h>
8
9 #define USB_MARVELL_VID 0x1286
10
11 static const struct usb_device_id mvusb_mdio_table[] = {
12         { USB_DEVICE(USB_MARVELL_VID, 0x1fa4) },
13
14         {}
15 };
16 MODULE_DEVICE_TABLE(usb, mvusb_mdio_table);
17
18 enum {
19         MVUSB_CMD_PREAMBLE0,
20         MVUSB_CMD_PREAMBLE1,
21         MVUSB_CMD_ADDR,
22         MVUSB_CMD_VAL,
23 };
24
25 struct mvusb_mdio {
26         struct usb_device *udev;
27         struct mii_bus *mdio;
28
29         __le16 buf[4];
30 };
31
32 static int mvusb_mdio_read(struct mii_bus *mdio, int dev, int reg)
33 {
34         struct mvusb_mdio *mvusb = mdio->priv;
35         int err, alen;
36
37         mvusb->buf[MVUSB_CMD_ADDR] = cpu_to_le16(0xa400 | (dev << 5) | reg);
38
39         err = usb_bulk_msg(mvusb->udev, usb_sndbulkpipe(mvusb->udev, 2),
40                            mvusb->buf, 6, &alen, 100);
41         if (err)
42                 return err;
43
44         err = usb_bulk_msg(mvusb->udev, usb_rcvbulkpipe(mvusb->udev, 6),
45                            &mvusb->buf[MVUSB_CMD_VAL], 2, &alen, 100);
46         if (err)
47                 return err;
48
49         return le16_to_cpu(mvusb->buf[MVUSB_CMD_VAL]);
50 }
51
52 static int mvusb_mdio_write(struct mii_bus *mdio, int dev, int reg, u16 val)
53 {
54         struct mvusb_mdio *mvusb = mdio->priv;
55         int alen;
56
57         mvusb->buf[MVUSB_CMD_ADDR] = cpu_to_le16(0x8000 | (dev << 5) | reg);
58         mvusb->buf[MVUSB_CMD_VAL]  = cpu_to_le16(val);
59
60         return usb_bulk_msg(mvusb->udev, usb_sndbulkpipe(mvusb->udev, 2),
61                             mvusb->buf, 8, &alen, 100);
62 }
63
64 static int mvusb_mdio_probe(struct usb_interface *interface,
65                             const struct usb_device_id *id)
66 {
67         struct device *dev = &interface->dev;
68         struct mvusb_mdio *mvusb;
69         struct mii_bus *mdio;
70         int ret;
71
72         mdio = devm_mdiobus_alloc_size(dev, sizeof(*mvusb));
73         if (!mdio)
74                 return -ENOMEM;
75
76         mvusb = mdio->priv;
77         mvusb->mdio = mdio;
78         mvusb->udev = usb_get_dev(interface_to_usbdev(interface));
79
80         /* Reversed from USB PCAPs, no idea what these mean. */
81         mvusb->buf[MVUSB_CMD_PREAMBLE0] = cpu_to_le16(0xe800);
82         mvusb->buf[MVUSB_CMD_PREAMBLE1] = cpu_to_le16(0x0001);
83
84         snprintf(mdio->id, MII_BUS_ID_SIZE, "mvusb-%s", dev_name(dev));
85         mdio->name = mdio->id;
86         mdio->parent = dev;
87         mdio->read = mvusb_mdio_read;
88         mdio->write = mvusb_mdio_write;
89
90         usb_set_intfdata(interface, mvusb);
91         ret = of_mdiobus_register(mdio, dev->of_node);
92         if (ret)
93                 goto put_dev;
94
95         return 0;
96
97 put_dev:
98         usb_put_dev(mvusb->udev);
99         return ret;
100 }
101
102 static void mvusb_mdio_disconnect(struct usb_interface *interface)
103 {
104         struct mvusb_mdio *mvusb = usb_get_intfdata(interface);
105         struct usb_device *udev = mvusb->udev;
106
107         mdiobus_unregister(mvusb->mdio);
108         usb_set_intfdata(interface, NULL);
109         usb_put_dev(udev);
110 }
111
112 static struct usb_driver mvusb_mdio_driver = {
113         .name       = "mvusb_mdio",
114         .id_table   = mvusb_mdio_table,
115         .probe      = mvusb_mdio_probe,
116         .disconnect = mvusb_mdio_disconnect,
117 };
118
119 module_usb_driver(mvusb_mdio_driver);
120
121 MODULE_AUTHOR("Tobias Waldekranz <tobias@waldekranz.com>");
122 MODULE_DESCRIPTION("Marvell USB MDIO Adapter");
123 MODULE_LICENSE("GPL");