]>
Commit | Line | Data |
---|---|---|
1802d0be | 1 | // SPDX-License-Identifier: GPL-2.0-only |
dc700583 JB |
2 | /* |
3 | * Copyright (C) 2017 Pengutronix, Juergen Borleis <kernel@pengutronix.de> | |
4 | * | |
5 | * Partially based on a patch from | |
6 | * Copyright (c) 2014 Stefan Roese <sr@denx.de> | |
dc700583 JB |
7 | */ |
8 | #include <linux/kernel.h> | |
9 | #include <linux/module.h> | |
10 | #include <linux/mdio.h> | |
11 | #include <linux/phy.h> | |
12 | #include <linux/of.h> | |
13 | ||
14 | #include "lan9303.h" | |
15 | ||
16 | /* Generate phy-addr and -reg from the input address */ | |
17 | #define PHY_ADDR(x) ((((x) >> 6) + 0x10) & 0x1f) | |
18 | #define PHY_REG(x) (((x) >> 1) & 0x1f) | |
19 | ||
20 | struct lan9303_mdio { | |
21 | struct mdio_device *device; | |
22 | struct lan9303 chip; | |
23 | }; | |
24 | ||
25 | static void lan9303_mdio_real_write(struct mdio_device *mdio, int reg, u16 val) | |
26 | { | |
27 | mdio->bus->write(mdio->bus, PHY_ADDR(reg), PHY_REG(reg), val); | |
28 | } | |
29 | ||
30 | static int lan9303_mdio_write(void *ctx, uint32_t reg, uint32_t val) | |
31 | { | |
32 | struct lan9303_mdio *sw_dev = (struct lan9303_mdio *)ctx; | |
33 | ||
ab78acb1 | 34 | reg <<= 2; /* reg num to offset */ |
dc700583 JB |
35 | mutex_lock(&sw_dev->device->bus->mdio_lock); |
36 | lan9303_mdio_real_write(sw_dev->device, reg, val & 0xffff); | |
37 | lan9303_mdio_real_write(sw_dev->device, reg + 2, (val >> 16) & 0xffff); | |
38 | mutex_unlock(&sw_dev->device->bus->mdio_lock); | |
39 | ||
40 | return 0; | |
41 | } | |
42 | ||
43 | static u16 lan9303_mdio_real_read(struct mdio_device *mdio, int reg) | |
44 | { | |
45 | return mdio->bus->read(mdio->bus, PHY_ADDR(reg), PHY_REG(reg)); | |
46 | } | |
47 | ||
48 | static int lan9303_mdio_read(void *ctx, uint32_t reg, uint32_t *val) | |
49 | { | |
50 | struct lan9303_mdio *sw_dev = (struct lan9303_mdio *)ctx; | |
51 | ||
ab78acb1 | 52 | reg <<= 2; /* reg num to offset */ |
dc700583 JB |
53 | mutex_lock(&sw_dev->device->bus->mdio_lock); |
54 | *val = lan9303_mdio_real_read(sw_dev->device, reg); | |
55 | *val |= (lan9303_mdio_real_read(sw_dev->device, reg + 2) << 16); | |
56 | mutex_unlock(&sw_dev->device->bus->mdio_lock); | |
57 | ||
58 | return 0; | |
59 | } | |
60 | ||
161ae6b0 CIK |
61 | static int lan9303_mdio_phy_write(struct lan9303 *chip, int phy, int reg, |
62 | u16 val) | |
2c340898 EH |
63 | { |
64 | struct lan9303_mdio *sw_dev = dev_get_drvdata(chip->dev); | |
65 | ||
66 | return mdiobus_write_nested(sw_dev->device->bus, phy, reg, val); | |
67 | } | |
68 | ||
161ae6b0 | 69 | static int lan9303_mdio_phy_read(struct lan9303 *chip, int phy, int reg) |
2c340898 EH |
70 | { |
71 | struct lan9303_mdio *sw_dev = dev_get_drvdata(chip->dev); | |
72 | ||
73 | return mdiobus_read_nested(sw_dev->device->bus, phy, reg); | |
74 | } | |
75 | ||
76 | static const struct lan9303_phy_ops lan9303_mdio_phy_ops = { | |
77 | .phy_read = lan9303_mdio_phy_read, | |
78 | .phy_write = lan9303_mdio_phy_write, | |
79 | }; | |
80 | ||
dc700583 JB |
81 | static const struct regmap_config lan9303_mdio_regmap_config = { |
82 | .reg_bits = 8, | |
83 | .val_bits = 32, | |
84 | .reg_stride = 1, | |
85 | .can_multi_write = true, | |
86 | .max_register = 0x0ff, /* address bits 0..1 are not used */ | |
87 | .reg_format_endian = REGMAP_ENDIAN_LITTLE, | |
88 | ||
89 | .volatile_table = &lan9303_register_set, | |
90 | .wr_table = &lan9303_register_set, | |
91 | .rd_table = &lan9303_register_set, | |
92 | ||
93 | .reg_read = lan9303_mdio_read, | |
94 | .reg_write = lan9303_mdio_write, | |
95 | ||
96 | .cache_type = REGCACHE_NONE, | |
97 | }; | |
98 | ||
99 | static int lan9303_mdio_probe(struct mdio_device *mdiodev) | |
100 | { | |
101 | struct lan9303_mdio *sw_dev; | |
102 | int ret; | |
103 | ||
104 | sw_dev = devm_kzalloc(&mdiodev->dev, sizeof(struct lan9303_mdio), | |
105 | GFP_KERNEL); | |
106 | if (!sw_dev) | |
107 | return -ENOMEM; | |
108 | ||
109 | sw_dev->chip.regmap = devm_regmap_init(&mdiodev->dev, NULL, sw_dev, | |
92f25caf | 110 | &lan9303_mdio_regmap_config); |
dc700583 JB |
111 | if (IS_ERR(sw_dev->chip.regmap)) { |
112 | ret = PTR_ERR(sw_dev->chip.regmap); | |
113 | dev_err(&mdiodev->dev, "regmap init failed: %d\n", ret); | |
114 | return ret; | |
115 | } | |
116 | ||
117 | /* link forward and backward */ | |
118 | sw_dev->device = mdiodev; | |
119 | dev_set_drvdata(&mdiodev->dev, sw_dev); | |
120 | sw_dev->chip.dev = &mdiodev->dev; | |
121 | ||
2c340898 EH |
122 | sw_dev->chip.ops = &lan9303_mdio_phy_ops; |
123 | ||
dc700583 JB |
124 | ret = lan9303_probe(&sw_dev->chip, mdiodev->dev.of_node); |
125 | if (ret != 0) | |
126 | return ret; | |
127 | ||
128 | dev_info(&mdiodev->dev, "LAN9303 MDIO driver loaded successfully\n"); | |
129 | ||
130 | return 0; | |
131 | } | |
132 | ||
133 | static void lan9303_mdio_remove(struct mdio_device *mdiodev) | |
134 | { | |
135 | struct lan9303_mdio *sw_dev = dev_get_drvdata(&mdiodev->dev); | |
136 | ||
137 | if (!sw_dev) | |
138 | return; | |
139 | ||
140 | lan9303_remove(&sw_dev->chip); | |
0650bf52 VO |
141 | |
142 | dev_set_drvdata(&mdiodev->dev, NULL); | |
143 | } | |
144 | ||
145 | static void lan9303_mdio_shutdown(struct mdio_device *mdiodev) | |
146 | { | |
147 | struct lan9303_mdio *sw_dev = dev_get_drvdata(&mdiodev->dev); | |
148 | ||
149 | if (!sw_dev) | |
150 | return; | |
151 | ||
152 | lan9303_shutdown(&sw_dev->chip); | |
153 | ||
154 | dev_set_drvdata(&mdiodev->dev, NULL); | |
dc700583 JB |
155 | } |
156 | ||
157 | /*-------------------------------------------------------------------------*/ | |
158 | ||
159 | static const struct of_device_id lan9303_mdio_of_match[] = { | |
160 | { .compatible = "smsc,lan9303-mdio" }, | |
161 | { /* sentinel */ }, | |
162 | }; | |
163 | MODULE_DEVICE_TABLE(of, lan9303_mdio_of_match); | |
164 | ||
165 | static struct mdio_driver lan9303_mdio_driver = { | |
166 | .mdiodrv.driver = { | |
167 | .name = "LAN9303_MDIO", | |
168 | .of_match_table = of_match_ptr(lan9303_mdio_of_match), | |
169 | }, | |
170 | .probe = lan9303_mdio_probe, | |
171 | .remove = lan9303_mdio_remove, | |
0650bf52 | 172 | .shutdown = lan9303_mdio_shutdown, |
dc700583 JB |
173 | }; |
174 | mdio_module_driver(lan9303_mdio_driver); | |
175 | ||
176 | MODULE_AUTHOR("Stefan Roese <sr@denx.de>, Juergen Borleis <kernel@pengutronix.de>"); | |
177 | MODULE_DESCRIPTION("Driver for SMSC/Microchip LAN9303 three port ethernet switch in MDIO managed mode"); | |
178 | MODULE_LICENSE("GPL v2"); |