aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/dsa/mv88e6xxx
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net/dsa/mv88e6xxx')
-rw-r--r--drivers/net/dsa/mv88e6xxx/Makefile1
-rw-r--r--drivers/net/dsa/mv88e6xxx/chip.c287
-rw-r--r--drivers/net/dsa/mv88e6xxx/chip.h11
-rw-r--r--drivers/net/dsa/mv88e6xxx/port.c24
-rw-r--r--drivers/net/dsa/mv88e6xxx/port.h1
-rw-r--r--drivers/net/dsa/mv88e6xxx/smi.c158
-rw-r--r--drivers/net/dsa/mv88e6xxx/smi.h59
7 files changed, 276 insertions, 265 deletions
diff --git a/drivers/net/dsa/mv88e6xxx/Makefile b/drivers/net/dsa/mv88e6xxx/Makefile
index 50de304abe2f..e85755dde90b 100644
--- a/drivers/net/dsa/mv88e6xxx/Makefile
+++ b/drivers/net/dsa/mv88e6xxx/Makefile
@@ -12,3 +12,4 @@ mv88e6xxx-objs += phy.o
12mv88e6xxx-objs += port.o 12mv88e6xxx-objs += port.o
13mv88e6xxx-$(CONFIG_NET_DSA_MV88E6XXX_PTP) += ptp.o 13mv88e6xxx-$(CONFIG_NET_DSA_MV88E6XXX_PTP) += ptp.o
14mv88e6xxx-objs += serdes.o 14mv88e6xxx-objs += serdes.o
15mv88e6xxx-objs += smi.o
diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c
index f4e2db44ad91..28414db979b0 100644
--- a/drivers/net/dsa/mv88e6xxx/chip.c
+++ b/drivers/net/dsa/mv88e6xxx/chip.c
@@ -43,6 +43,7 @@
43#include "port.h" 43#include "port.h"
44#include "ptp.h" 44#include "ptp.h"
45#include "serdes.h" 45#include "serdes.h"
46#include "smi.h"
46 47
47static void assert_reg_lock(struct mv88e6xxx_chip *chip) 48static void assert_reg_lock(struct mv88e6xxx_chip *chip)
48{ 49{
@@ -52,149 +53,6 @@ static void assert_reg_lock(struct mv88e6xxx_chip *chip)
52 } 53 }
53} 54}
54 55
55/* The switch ADDR[4:1] configuration pins define the chip SMI device address
56 * (ADDR[0] is always zero, thus only even SMI addresses can be strapped).
57 *
58 * When ADDR is all zero, the chip uses Single-chip Addressing Mode, assuming it
59 * is the only device connected to the SMI master. In this mode it responds to
60 * all 32 possible SMI addresses, and thus maps directly the internal devices.
61 *
62 * When ADDR is non-zero, the chip uses Multi-chip Addressing Mode, allowing
63 * multiple devices to share the SMI interface. In this mode it responds to only
64 * 2 registers, used to indirectly access the internal SMI devices.
65 */
66
67static int mv88e6xxx_smi_read(struct mv88e6xxx_chip *chip,
68 int addr, int reg, u16 *val)
69{
70 if (!chip->smi_ops)
71 return -EOPNOTSUPP;
72
73 return chip->smi_ops->read(chip, addr, reg, val);
74}
75
76static int mv88e6xxx_smi_write(struct mv88e6xxx_chip *chip,
77 int addr, int reg, u16 val)
78{
79 if (!chip->smi_ops)
80 return -EOPNOTSUPP;
81
82 return chip->smi_ops->write(chip, addr, reg, val);
83}
84
85static int mv88e6xxx_smi_single_chip_read(struct mv88e6xxx_chip *chip,
86 int addr, int reg, u16 *val)
87{
88 int ret;
89
90 ret = mdiobus_read_nested(chip->bus, addr, reg);
91 if (ret < 0)
92 return ret;
93
94 *val = ret & 0xffff;
95
96 return 0;
97}
98
99static int mv88e6xxx_smi_single_chip_write(struct mv88e6xxx_chip *chip,
100 int addr, int reg, u16 val)
101{
102 int ret;
103
104 ret = mdiobus_write_nested(chip->bus, addr, reg, val);
105 if (ret < 0)
106 return ret;
107
108 return 0;
109}
110
111static const struct mv88e6xxx_bus_ops mv88e6xxx_smi_single_chip_ops = {
112 .read = mv88e6xxx_smi_single_chip_read,
113 .write = mv88e6xxx_smi_single_chip_write,
114};
115
116static int mv88e6xxx_smi_multi_chip_wait(struct mv88e6xxx_chip *chip)
117{
118 int ret;
119 int i;
120
121 for (i = 0; i < 16; i++) {
122 ret = mdiobus_read_nested(chip->bus, chip->sw_addr, SMI_CMD);
123 if (ret < 0)
124 return ret;
125
126 if ((ret & SMI_CMD_BUSY) == 0)
127 return 0;
128 }
129
130 return -ETIMEDOUT;
131}
132
133static int mv88e6xxx_smi_multi_chip_read(struct mv88e6xxx_chip *chip,
134 int addr, int reg, u16 *val)
135{
136 int ret;
137
138 /* Wait for the bus to become free. */
139 ret = mv88e6xxx_smi_multi_chip_wait(chip);
140 if (ret < 0)
141 return ret;
142
143 /* Transmit the read command. */
144 ret = mdiobus_write_nested(chip->bus, chip->sw_addr, SMI_CMD,
145 SMI_CMD_OP_22_READ | (addr << 5) | reg);
146 if (ret < 0)
147 return ret;
148
149 /* Wait for the read command to complete. */
150 ret = mv88e6xxx_smi_multi_chip_wait(chip);
151 if (ret < 0)
152 return ret;
153
154 /* Read the data. */
155 ret = mdiobus_read_nested(chip->bus, chip->sw_addr, SMI_DATA);
156 if (ret < 0)
157 return ret;
158
159 *val = ret & 0xffff;
160
161 return 0;
162}
163
164static int mv88e6xxx_smi_multi_chip_write(struct mv88e6xxx_chip *chip,
165 int addr, int reg, u16 val)
166{
167 int ret;
168
169 /* Wait for the bus to become free. */
170 ret = mv88e6xxx_smi_multi_chip_wait(chip);
171 if (ret < 0)
172 return ret;
173
174 /* Transmit the data to write. */
175 ret = mdiobus_write_nested(chip->bus, chip->sw_addr, SMI_DATA, val);
176 if (ret < 0)
177 return ret;
178
179 /* Transmit the write command. */
180 ret = mdiobus_write_nested(chip->bus, chip->sw_addr, SMI_CMD,
181 SMI_CMD_OP_22_WRITE | (addr << 5) | reg);
182 if (ret < 0)
183 return ret;
184
185 /* Wait for the write command to complete. */
186 ret = mv88e6xxx_smi_multi_chip_wait(chip);
187 if (ret < 0)
188 return ret;
189
190 return 0;
191}
192
193static const struct mv88e6xxx_bus_ops mv88e6xxx_smi_multi_chip_ops = {
194 .read = mv88e6xxx_smi_multi_chip_read,
195 .write = mv88e6xxx_smi_multi_chip_write,
196};
197
198int mv88e6xxx_read(struct mv88e6xxx_chip *chip, int addr, int reg, u16 *val) 56int mv88e6xxx_read(struct mv88e6xxx_chip *chip, int addr, int reg, u16 *val)
199{ 57{
200 int err; 58 int err;
@@ -553,11 +411,28 @@ int mv88e6xxx_port_setup_mac(struct mv88e6xxx_chip *chip, int port, int link,
553 int speed, int duplex, int pause, 411 int speed, int duplex, int pause,
554 phy_interface_t mode) 412 phy_interface_t mode)
555{ 413{
414 struct phylink_link_state state;
556 int err; 415 int err;
557 416
558 if (!chip->info->ops->port_set_link) 417 if (!chip->info->ops->port_set_link)
559 return 0; 418 return 0;
560 419
420 if (!chip->info->ops->port_link_state)
421 return 0;
422
423 err = chip->info->ops->port_link_state(chip, port, &state);
424 if (err)
425 return err;
426
427 /* Has anything actually changed? We don't expect the
428 * interface mode to change without one of the other
429 * parameters also changing
430 */
431 if (state.link == link &&
432 state.speed == speed &&
433 state.duplex == duplex)
434 return 0;
435
561 /* Port's MAC control must not be changed unless the link is down */ 436 /* Port's MAC control must not be changed unless the link is down */
562 err = chip->info->ops->port_set_link(chip, port, 0); 437 err = chip->info->ops->port_set_link(chip, port, 0);
563 if (err) 438 if (err)
@@ -2411,6 +2286,9 @@ static void mv88e6xxx_port_disable(struct dsa_switch *ds, int port)
2411 2286
2412 mutex_lock(&chip->reg_lock); 2287 mutex_lock(&chip->reg_lock);
2413 2288
2289 if (mv88e6xxx_port_set_state(chip, port, BR_STATE_DISABLED))
2290 dev_err(chip->dev, "failed to disable port\n");
2291
2414 if (chip->info->ops->serdes_irq_free) 2292 if (chip->info->ops->serdes_irq_free)
2415 chip->info->ops->serdes_irq_free(chip, port); 2293 chip->info->ops->serdes_irq_free(chip, port);
2416 2294
@@ -2579,8 +2457,18 @@ static int mv88e6xxx_setup(struct dsa_switch *ds)
2579 2457
2580 /* Setup Switch Port Registers */ 2458 /* Setup Switch Port Registers */
2581 for (i = 0; i < mv88e6xxx_num_ports(chip); i++) { 2459 for (i = 0; i < mv88e6xxx_num_ports(chip); i++) {
2582 if (dsa_is_unused_port(ds, i)) 2460 if (dsa_is_unused_port(ds, i)) {
2461 err = mv88e6xxx_port_set_state(chip, i,
2462 BR_STATE_DISABLED);
2463 if (err)
2464 goto unlock;
2465
2466 err = mv88e6xxx_serdes_power(chip, i, false);
2467 if (err)
2468 goto unlock;
2469
2583 continue; 2470 continue;
2471 }
2584 2472
2585 err = mv88e6xxx_setup_port(chip, i); 2473 err = mv88e6xxx_setup_port(chip, i);
2586 if (err) 2474 if (err)
@@ -4615,30 +4503,6 @@ static struct mv88e6xxx_chip *mv88e6xxx_alloc_chip(struct device *dev)
4615 return chip; 4503 return chip;
4616} 4504}
4617 4505
4618static int mv88e6xxx_smi_init(struct mv88e6xxx_chip *chip,
4619 struct mii_bus *bus, int sw_addr)
4620{
4621 if (sw_addr == 0)
4622 chip->smi_ops = &mv88e6xxx_smi_single_chip_ops;
4623 else if (chip->info->multi_chip)
4624 chip->smi_ops = &mv88e6xxx_smi_multi_chip_ops;
4625 else
4626 return -EINVAL;
4627
4628 chip->bus = bus;
4629 chip->sw_addr = sw_addr;
4630
4631 return 0;
4632}
4633
4634static void mv88e6xxx_ports_cmode_init(struct mv88e6xxx_chip *chip)
4635{
4636 int i;
4637
4638 for (i = 0; i < mv88e6xxx_num_ports(chip); i++)
4639 chip->ports[i].cmode = MV88E6XXX_PORT_STS_CMODE_INVALID;
4640}
4641
4642static enum dsa_tag_protocol mv88e6xxx_get_tag_protocol(struct dsa_switch *ds, 4506static enum dsa_tag_protocol mv88e6xxx_get_tag_protocol(struct dsa_switch *ds,
4643 int port) 4507 int port)
4644{ 4508{
@@ -4647,58 +4511,6 @@ static enum dsa_tag_protocol mv88e6xxx_get_tag_protocol(struct dsa_switch *ds,
4647 return chip->info->tag_protocol; 4511 return chip->info->tag_protocol;
4648} 4512}
4649 4513
4650#if IS_ENABLED(CONFIG_NET_DSA_LEGACY)
4651static const char *mv88e6xxx_drv_probe(struct device *dsa_dev,
4652 struct device *host_dev, int sw_addr,
4653 void **priv)
4654{
4655 struct mv88e6xxx_chip *chip;
4656 struct mii_bus *bus;
4657 int err;
4658
4659 bus = dsa_host_dev_to_mii_bus(host_dev);
4660 if (!bus)
4661 return NULL;
4662
4663 chip = mv88e6xxx_alloc_chip(dsa_dev);
4664 if (!chip)
4665 return NULL;
4666
4667 /* Legacy SMI probing will only support chips similar to 88E6085 */
4668 chip->info = &mv88e6xxx_table[MV88E6085];
4669
4670 err = mv88e6xxx_smi_init(chip, bus, sw_addr);
4671 if (err)
4672 goto free;
4673
4674 err = mv88e6xxx_detect(chip);
4675 if (err)
4676 goto free;
4677
4678 mv88e6xxx_ports_cmode_init(chip);
4679
4680 mutex_lock(&chip->reg_lock);
4681 err = mv88e6xxx_switch_reset(chip);
4682 mutex_unlock(&chip->reg_lock);
4683 if (err)
4684 goto free;
4685
4686 mv88e6xxx_phy_init(chip);
4687
4688 err = mv88e6xxx_mdios_register(chip, NULL);
4689 if (err)
4690 goto free;
4691
4692 *priv = chip;
4693
4694 return chip->info->name;
4695free:
4696 devm_kfree(dsa_dev, chip);
4697
4698 return NULL;
4699}
4700#endif
4701
4702static int mv88e6xxx_port_mdb_prepare(struct dsa_switch *ds, int port, 4514static int mv88e6xxx_port_mdb_prepare(struct dsa_switch *ds, int port,
4703 const struct switchdev_obj_port_mdb *mdb) 4515 const struct switchdev_obj_port_mdb *mdb)
4704{ 4516{
@@ -4753,9 +4565,6 @@ static int mv88e6xxx_port_egress_floods(struct dsa_switch *ds, int port,
4753} 4565}
4754 4566
4755static const struct dsa_switch_ops mv88e6xxx_switch_ops = { 4567static const struct dsa_switch_ops mv88e6xxx_switch_ops = {
4756#if IS_ENABLED(CONFIG_NET_DSA_LEGACY)
4757 .probe = mv88e6xxx_drv_probe,
4758#endif
4759 .get_tag_protocol = mv88e6xxx_get_tag_protocol, 4568 .get_tag_protocol = mv88e6xxx_get_tag_protocol,
4760 .setup = mv88e6xxx_setup, 4569 .setup = mv88e6xxx_setup,
4761 .adjust_link = mv88e6xxx_adjust_link, 4570 .adjust_link = mv88e6xxx_adjust_link,
@@ -4801,10 +4610,6 @@ static const struct dsa_switch_ops mv88e6xxx_switch_ops = {
4801 .get_ts_info = mv88e6xxx_get_ts_info, 4610 .get_ts_info = mv88e6xxx_get_ts_info,
4802}; 4611};
4803 4612
4804static struct dsa_switch_driver mv88e6xxx_switch_drv = {
4805 .ops = &mv88e6xxx_switch_ops,
4806};
4807
4808static int mv88e6xxx_register_switch(struct mv88e6xxx_chip *chip) 4613static int mv88e6xxx_register_switch(struct mv88e6xxx_chip *chip)
4809{ 4614{
4810 struct device *dev = chip->dev; 4615 struct device *dev = chip->dev;
@@ -4915,7 +4720,6 @@ static int mv88e6xxx_probe(struct mdio_device *mdiodev)
4915 if (err) 4720 if (err)
4916 goto out; 4721 goto out;
4917 4722
4918 mv88e6xxx_ports_cmode_init(chip);
4919 mv88e6xxx_phy_init(chip); 4723 mv88e6xxx_phy_init(chip);
4920 4724
4921 if (chip->info->ops->get_eeprom) { 4725 if (chip->info->ops->get_eeprom) {
@@ -4932,12 +4736,17 @@ static int mv88e6xxx_probe(struct mdio_device *mdiodev)
4932 if (err) 4736 if (err)
4933 goto out; 4737 goto out;
4934 4738
4935 chip->irq = of_irq_get(np, 0); 4739 if (np) {
4936 if (chip->irq == -EPROBE_DEFER) { 4740 chip->irq = of_irq_get(np, 0);
4937 err = chip->irq; 4741 if (chip->irq == -EPROBE_DEFER) {
4938 goto out; 4742 err = chip->irq;
4743 goto out;
4744 }
4939 } 4745 }
4940 4746
4747 if (pdata)
4748 chip->irq = pdata->irq;
4749
4941 /* Has to be performed before the MDIO bus is created, because 4750 /* Has to be performed before the MDIO bus is created, because
4942 * the PHYs will link their interrupts to these interrupt 4751 * the PHYs will link their interrupts to these interrupt
4943 * controllers 4752 * controllers
@@ -5047,19 +4856,7 @@ static struct mdio_driver mv88e6xxx_driver = {
5047 }, 4856 },
5048}; 4857};
5049 4858
5050static int __init mv88e6xxx_init(void) 4859mdio_module_driver(mv88e6xxx_driver);
5051{
5052 register_switch_driver(&mv88e6xxx_switch_drv);
5053 return mdio_driver_register(&mv88e6xxx_driver);
5054}
5055module_init(mv88e6xxx_init);
5056
5057static void __exit mv88e6xxx_cleanup(void)
5058{
5059 mdio_driver_unregister(&mv88e6xxx_driver);
5060 unregister_switch_driver(&mv88e6xxx_switch_drv);
5061}
5062module_exit(mv88e6xxx_cleanup);
5063 4860
5064MODULE_AUTHOR("Lennert Buytenhek <buytenh@wantstofly.org>"); 4861MODULE_AUTHOR("Lennert Buytenhek <buytenh@wantstofly.org>");
5065MODULE_DESCRIPTION("Driver for Marvell 88E6XXX ethernet switch chips"); 4862MODULE_DESCRIPTION("Driver for Marvell 88E6XXX ethernet switch chips");
diff --git a/drivers/net/dsa/mv88e6xxx/chip.h b/drivers/net/dsa/mv88e6xxx/chip.h
index 19c07dff0440..faa3fa889f19 100644
--- a/drivers/net/dsa/mv88e6xxx/chip.h
+++ b/drivers/net/dsa/mv88e6xxx/chip.h
@@ -21,17 +21,6 @@
21#include <linux/timecounter.h> 21#include <linux/timecounter.h>
22#include <net/dsa.h> 22#include <net/dsa.h>
23 23
24#define SMI_CMD 0x00
25#define SMI_CMD_BUSY BIT(15)
26#define SMI_CMD_CLAUSE_22 BIT(12)
27#define SMI_CMD_OP_22_WRITE ((1 << 10) | SMI_CMD_BUSY | SMI_CMD_CLAUSE_22)
28#define SMI_CMD_OP_22_READ ((2 << 10) | SMI_CMD_BUSY | SMI_CMD_CLAUSE_22)
29#define SMI_CMD_OP_45_WRITE_ADDR ((0 << 10) | SMI_CMD_BUSY)
30#define SMI_CMD_OP_45_WRITE_DATA ((1 << 10) | SMI_CMD_BUSY)
31#define SMI_CMD_OP_45_READ_DATA ((2 << 10) | SMI_CMD_BUSY)
32#define SMI_CMD_OP_45_READ_DATA_INC ((3 << 10) | SMI_CMD_BUSY)
33#define SMI_DATA 0x01
34
35#define MV88E6XXX_N_FID 4096 24#define MV88E6XXX_N_FID 4096
36 25
37/* PVT limits for 4-bit port and 5-bit switch */ 26/* PVT limits for 4-bit port and 5-bit switch */
diff --git a/drivers/net/dsa/mv88e6xxx/port.c b/drivers/net/dsa/mv88e6xxx/port.c
index dce84a2a65c7..c44b2822e4dd 100644
--- a/drivers/net/dsa/mv88e6xxx/port.c
+++ b/drivers/net/dsa/mv88e6xxx/port.c
@@ -427,18 +427,22 @@ int mv88e6390x_port_set_cmode(struct mv88e6xxx_chip *chip, int port,
427 return 0; 427 return 0;
428 428
429 lane = mv88e6390x_serdes_get_lane(chip, port); 429 lane = mv88e6390x_serdes_get_lane(chip, port);
430 if (lane < 0) 430 if (lane < 0 && lane != -ENODEV)
431 return lane; 431 return lane;
432 432
433 if (chip->ports[port].serdes_irq) { 433 if (lane >= 0) {
434 err = mv88e6390_serdes_irq_disable(chip, port, lane); 434 if (chip->ports[port].serdes_irq) {
435 err = mv88e6390_serdes_irq_disable(chip, port, lane);
436 if (err)
437 return err;
438 }
439
440 err = mv88e6390x_serdes_power(chip, port, false);
435 if (err) 441 if (err)
436 return err; 442 return err;
437 } 443 }
438 444
439 err = mv88e6390x_serdes_power(chip, port, false); 445 chip->ports[port].cmode = 0;
440 if (err)
441 return err;
442 446
443 if (cmode) { 447 if (cmode) {
444 err = mv88e6xxx_port_read(chip, port, MV88E6XXX_PORT_STS, &reg); 448 err = mv88e6xxx_port_read(chip, port, MV88E6XXX_PORT_STS, &reg);
@@ -452,6 +456,12 @@ int mv88e6390x_port_set_cmode(struct mv88e6xxx_chip *chip, int port,
452 if (err) 456 if (err)
453 return err; 457 return err;
454 458
459 chip->ports[port].cmode = cmode;
460
461 lane = mv88e6390x_serdes_get_lane(chip, port);
462 if (lane < 0)
463 return lane;
464
455 err = mv88e6390x_serdes_power(chip, port, true); 465 err = mv88e6390x_serdes_power(chip, port, true);
456 if (err) 466 if (err)
457 return err; 467 return err;
@@ -463,8 +473,6 @@ int mv88e6390x_port_set_cmode(struct mv88e6xxx_chip *chip, int port,
463 } 473 }
464 } 474 }
465 475
466 chip->ports[port].cmode = cmode;
467
468 return 0; 476 return 0;
469} 477}
470 478
diff --git a/drivers/net/dsa/mv88e6xxx/port.h b/drivers/net/dsa/mv88e6xxx/port.h
index c7bed263a0f4..39c85e98fb92 100644
--- a/drivers/net/dsa/mv88e6xxx/port.h
+++ b/drivers/net/dsa/mv88e6xxx/port.h
@@ -52,7 +52,6 @@
52#define MV88E6185_PORT_STS_CMODE_1000BASE_X 0x0005 52#define MV88E6185_PORT_STS_CMODE_1000BASE_X 0x0005
53#define MV88E6185_PORT_STS_CMODE_PHY 0x0006 53#define MV88E6185_PORT_STS_CMODE_PHY 0x0006
54#define MV88E6185_PORT_STS_CMODE_DISABLED 0x0007 54#define MV88E6185_PORT_STS_CMODE_DISABLED 0x0007
55#define MV88E6XXX_PORT_STS_CMODE_INVALID 0xff
56 55
57/* Offset 0x01: MAC (or PCS or Physical) Control Register */ 56/* Offset 0x01: MAC (or PCS or Physical) Control Register */
58#define MV88E6XXX_PORT_MAC_CTL 0x01 57#define MV88E6XXX_PORT_MAC_CTL 0x01
diff --git a/drivers/net/dsa/mv88e6xxx/smi.c b/drivers/net/dsa/mv88e6xxx/smi.c
new file mode 100644
index 000000000000..96f7d2685bdc
--- /dev/null
+++ b/drivers/net/dsa/mv88e6xxx/smi.c
@@ -0,0 +1,158 @@
1/*
2 * Marvell 88E6xxx System Management Interface (SMI) support
3 *
4 * Copyright (c) 2008 Marvell Semiconductor
5 *
6 * Copyright (c) 2019 Vivien Didelot <vivien.didelot@gmail.com>
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
14#include "chip.h"
15#include "smi.h"
16
17/* The switch ADDR[4:1] configuration pins define the chip SMI device address
18 * (ADDR[0] is always zero, thus only even SMI addresses can be strapped).
19 *
20 * When ADDR is all zero, the chip uses Single-chip Addressing Mode, assuming it
21 * is the only device connected to the SMI master. In this mode it responds to
22 * all 32 possible SMI addresses, and thus maps directly the internal devices.
23 *
24 * When ADDR is non-zero, the chip uses Multi-chip Addressing Mode, allowing
25 * multiple devices to share the SMI interface. In this mode it responds to only
26 * 2 registers, used to indirectly access the internal SMI devices.
27 */
28
29static int mv88e6xxx_smi_direct_read(struct mv88e6xxx_chip *chip,
30 int dev, int reg, u16 *data)
31{
32 int ret;
33
34 ret = mdiobus_read_nested(chip->bus, dev, reg);
35 if (ret < 0)
36 return ret;
37
38 *data = ret & 0xffff;
39
40 return 0;
41}
42
43static int mv88e6xxx_smi_direct_write(struct mv88e6xxx_chip *chip,
44 int dev, int reg, u16 data)
45{
46 int ret;
47
48 ret = mdiobus_write_nested(chip->bus, dev, reg, data);
49 if (ret < 0)
50 return ret;
51
52 return 0;
53}
54
55static int mv88e6xxx_smi_direct_wait(struct mv88e6xxx_chip *chip,
56 int dev, int reg, int bit, int val)
57{
58 u16 data;
59 int err;
60 int i;
61
62 for (i = 0; i < 16; i++) {
63 err = mv88e6xxx_smi_direct_read(chip, dev, reg, &data);
64 if (err)
65 return err;
66
67 if (!!(data >> bit) == !!val)
68 return 0;
69 }
70
71 return -ETIMEDOUT;
72}
73
74static const struct mv88e6xxx_bus_ops mv88e6xxx_smi_direct_ops = {
75 .read = mv88e6xxx_smi_direct_read,
76 .write = mv88e6xxx_smi_direct_write,
77};
78
79/* Offset 0x00: SMI Command Register
80 * Offset 0x01: SMI Data Register
81 */
82
83static int mv88e6xxx_smi_indirect_read(struct mv88e6xxx_chip *chip,
84 int dev, int reg, u16 *data)
85{
86 int err;
87
88 err = mv88e6xxx_smi_direct_wait(chip, chip->sw_addr,
89 MV88E6XXX_SMI_CMD, 15, 0);
90 if (err)
91 return err;
92
93 err = mv88e6xxx_smi_direct_write(chip, chip->sw_addr,
94 MV88E6XXX_SMI_CMD,
95 MV88E6XXX_SMI_CMD_BUSY |
96 MV88E6XXX_SMI_CMD_MODE_22 |
97 MV88E6XXX_SMI_CMD_OP_22_READ |
98 (dev << 5) | reg);
99 if (err)
100 return err;
101
102 err = mv88e6xxx_smi_direct_wait(chip, chip->sw_addr,
103 MV88E6XXX_SMI_CMD, 15, 0);
104 if (err)
105 return err;
106
107 return mv88e6xxx_smi_direct_read(chip, chip->sw_addr,
108 MV88E6XXX_SMI_DATA, data);
109}
110
111static int mv88e6xxx_smi_indirect_write(struct mv88e6xxx_chip *chip,
112 int dev, int reg, u16 data)
113{
114 int err;
115
116 err = mv88e6xxx_smi_direct_wait(chip, chip->sw_addr,
117 MV88E6XXX_SMI_CMD, 15, 0);
118 if (err)
119 return err;
120
121 err = mv88e6xxx_smi_direct_write(chip, chip->sw_addr,
122 MV88E6XXX_SMI_DATA, data);
123 if (err)
124 return err;
125
126 err = mv88e6xxx_smi_direct_write(chip, chip->sw_addr,
127 MV88E6XXX_SMI_CMD,
128 MV88E6XXX_SMI_CMD_BUSY |
129 MV88E6XXX_SMI_CMD_MODE_22 |
130 MV88E6XXX_SMI_CMD_OP_22_WRITE |
131 (dev << 5) | reg);
132 if (err)
133 return err;
134
135 return mv88e6xxx_smi_direct_wait(chip, chip->sw_addr,
136 MV88E6XXX_SMI_CMD, 15, 0);
137}
138
139static const struct mv88e6xxx_bus_ops mv88e6xxx_smi_indirect_ops = {
140 .read = mv88e6xxx_smi_indirect_read,
141 .write = mv88e6xxx_smi_indirect_write,
142};
143
144int mv88e6xxx_smi_init(struct mv88e6xxx_chip *chip,
145 struct mii_bus *bus, int sw_addr)
146{
147 if (sw_addr == 0)
148 chip->smi_ops = &mv88e6xxx_smi_direct_ops;
149 else if (chip->info->multi_chip)
150 chip->smi_ops = &mv88e6xxx_smi_indirect_ops;
151 else
152 return -EINVAL;
153
154 chip->bus = bus;
155 chip->sw_addr = sw_addr;
156
157 return 0;
158}
diff --git a/drivers/net/dsa/mv88e6xxx/smi.h b/drivers/net/dsa/mv88e6xxx/smi.h
new file mode 100644
index 000000000000..35e6403b65dc
--- /dev/null
+++ b/drivers/net/dsa/mv88e6xxx/smi.h
@@ -0,0 +1,59 @@
1/*
2 * Marvell 88E6xxx System Management Interface (SMI) support
3 *
4 * Copyright (c) 2008 Marvell Semiconductor
5 *
6 * Copyright (c) 2019 Vivien Didelot <vivien.didelot@gmail.com>
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
14#ifndef _MV88E6XXX_SMI_H
15#define _MV88E6XXX_SMI_H
16
17#include "chip.h"
18
19/* Offset 0x00: SMI Command Register */
20#define MV88E6XXX_SMI_CMD 0x00
21#define MV88E6XXX_SMI_CMD_BUSY 0x8000
22#define MV88E6XXX_SMI_CMD_MODE_MASK 0x1000
23#define MV88E6XXX_SMI_CMD_MODE_45 0x0000
24#define MV88E6XXX_SMI_CMD_MODE_22 0x1000
25#define MV88E6XXX_SMI_CMD_OP_MASK 0x0c00
26#define MV88E6XXX_SMI_CMD_OP_22_WRITE 0x0400
27#define MV88E6XXX_SMI_CMD_OP_22_READ 0x0800
28#define MV88E6XXX_SMI_CMD_OP_45_WRITE_ADDR 0x0000
29#define MV88E6XXX_SMI_CMD_OP_45_WRITE_DATA 0x0400
30#define MV88E6XXX_SMI_CMD_OP_45_READ_DATA 0x0800
31#define MV88E6XXX_SMI_CMD_OP_45_READ_DATA_INC 0x0c00
32#define MV88E6XXX_SMI_CMD_DEV_ADDR_MASK 0x003e
33#define MV88E6XXX_SMI_CMD_REG_ADDR_MASK 0x001f
34
35/* Offset 0x01: SMI Data Register */
36#define MV88E6XXX_SMI_DATA 0x01
37
38int mv88e6xxx_smi_init(struct mv88e6xxx_chip *chip,
39 struct mii_bus *bus, int sw_addr);
40
41static inline int mv88e6xxx_smi_read(struct mv88e6xxx_chip *chip,
42 int dev, int reg, u16 *data)
43{
44 if (chip->smi_ops && chip->smi_ops->read)
45 return chip->smi_ops->read(chip, dev, reg, data);
46
47 return -EOPNOTSUPP;
48}
49
50static inline int mv88e6xxx_smi_write(struct mv88e6xxx_chip *chip,
51 int dev, int reg, u16 data)
52{
53 if (chip->smi_ops && chip->smi_ops->write)
54 return chip->smi_ops->write(chip, dev, reg, data);
55
56 return -EOPNOTSUPP;
57}
58
59#endif /* _MV88E6XXX_SMI_H */