1a9770eacSAndrew Lunn // SPDX-License-Identifier: GPL-2.0
2a9770eacSAndrew Lunn /*
3a9770eacSAndrew Lunn * Copyright (C) 2009-2016 Cavium, Inc.
4a9770eacSAndrew Lunn */
5a9770eacSAndrew Lunn
6a9770eacSAndrew Lunn #include <linux/delay.h>
71bf34366SCalvin Johnson #include <linux/io.h>
8a9770eacSAndrew Lunn #include <linux/module.h>
9a9770eacSAndrew Lunn #include <linux/phy.h>
10a9770eacSAndrew Lunn
11a9770eacSAndrew Lunn #include "mdio-cavium.h"
12a9770eacSAndrew Lunn
cavium_mdiobus_set_mode(struct cavium_mdiobus * p,enum cavium_mdiobus_mode m)13a9770eacSAndrew Lunn static void cavium_mdiobus_set_mode(struct cavium_mdiobus *p,
14a9770eacSAndrew Lunn enum cavium_mdiobus_mode m)
15a9770eacSAndrew Lunn {
16a9770eacSAndrew Lunn union cvmx_smix_clk smi_clk;
17a9770eacSAndrew Lunn
18a9770eacSAndrew Lunn if (m == p->mode)
19a9770eacSAndrew Lunn return;
20a9770eacSAndrew Lunn
21a9770eacSAndrew Lunn smi_clk.u64 = oct_mdio_readq(p->register_base + SMI_CLK);
22a9770eacSAndrew Lunn smi_clk.s.mode = (m == C45) ? 1 : 0;
23a9770eacSAndrew Lunn smi_clk.s.preamble = 1;
24a9770eacSAndrew Lunn oct_mdio_writeq(smi_clk.u64, p->register_base + SMI_CLK);
25a9770eacSAndrew Lunn p->mode = m;
26a9770eacSAndrew Lunn }
27a9770eacSAndrew Lunn
cavium_mdiobus_c45_addr(struct cavium_mdiobus * p,int phy_id,int devad,int regnum)28a9770eacSAndrew Lunn static int cavium_mdiobus_c45_addr(struct cavium_mdiobus *p,
2993641ecbSAndrew Lunn int phy_id, int devad, int regnum)
30a9770eacSAndrew Lunn {
31a9770eacSAndrew Lunn union cvmx_smix_cmd smi_cmd;
32a9770eacSAndrew Lunn union cvmx_smix_wr_dat smi_wr;
33a9770eacSAndrew Lunn int timeout = 1000;
34a9770eacSAndrew Lunn
35a9770eacSAndrew Lunn cavium_mdiobus_set_mode(p, C45);
36a9770eacSAndrew Lunn
37a9770eacSAndrew Lunn smi_wr.u64 = 0;
38a9770eacSAndrew Lunn smi_wr.s.dat = regnum & 0xffff;
39a9770eacSAndrew Lunn oct_mdio_writeq(smi_wr.u64, p->register_base + SMI_WR_DAT);
40a9770eacSAndrew Lunn
41a9770eacSAndrew Lunn smi_cmd.u64 = 0;
42a9770eacSAndrew Lunn smi_cmd.s.phy_op = 0; /* MDIO_CLAUSE_45_ADDRESS */
43a9770eacSAndrew Lunn smi_cmd.s.phy_adr = phy_id;
4493641ecbSAndrew Lunn smi_cmd.s.reg_adr = devad;
45a9770eacSAndrew Lunn oct_mdio_writeq(smi_cmd.u64, p->register_base + SMI_CMD);
46a9770eacSAndrew Lunn
47a9770eacSAndrew Lunn do {
48a9770eacSAndrew Lunn /* Wait 1000 clocks so we don't saturate the RSL bus
49a9770eacSAndrew Lunn * doing reads.
50a9770eacSAndrew Lunn */
51a9770eacSAndrew Lunn __delay(1000);
52a9770eacSAndrew Lunn smi_wr.u64 = oct_mdio_readq(p->register_base + SMI_WR_DAT);
53a9770eacSAndrew Lunn } while (smi_wr.s.pending && --timeout);
54a9770eacSAndrew Lunn
55a9770eacSAndrew Lunn if (timeout <= 0)
56a9770eacSAndrew Lunn return -EIO;
57a9770eacSAndrew Lunn return 0;
58a9770eacSAndrew Lunn }
59a9770eacSAndrew Lunn
cavium_mdiobus_read_c22(struct mii_bus * bus,int phy_id,int regnum)6093641ecbSAndrew Lunn int cavium_mdiobus_read_c22(struct mii_bus *bus, int phy_id, int regnum)
61a9770eacSAndrew Lunn {
62a9770eacSAndrew Lunn struct cavium_mdiobus *p = bus->priv;
63a9770eacSAndrew Lunn union cvmx_smix_cmd smi_cmd;
64a9770eacSAndrew Lunn union cvmx_smix_rd_dat smi_rd;
65a9770eacSAndrew Lunn int timeout = 1000;
66a9770eacSAndrew Lunn
67a9770eacSAndrew Lunn cavium_mdiobus_set_mode(p, C22);
68a9770eacSAndrew Lunn
69a9770eacSAndrew Lunn smi_cmd.u64 = 0;
70*0c68c8e5SAndrew Lunn smi_cmd.s.phy_op = 1; /* MDIO_CLAUSE_22_READ */
71a9770eacSAndrew Lunn smi_cmd.s.phy_adr = phy_id;
72a9770eacSAndrew Lunn smi_cmd.s.reg_adr = regnum;
73a9770eacSAndrew Lunn oct_mdio_writeq(smi_cmd.u64, p->register_base + SMI_CMD);
74a9770eacSAndrew Lunn
75a9770eacSAndrew Lunn do {
76a9770eacSAndrew Lunn /* Wait 1000 clocks so we don't saturate the RSL bus
77a9770eacSAndrew Lunn * doing reads.
78a9770eacSAndrew Lunn */
79a9770eacSAndrew Lunn __delay(1000);
80a9770eacSAndrew Lunn smi_rd.u64 = oct_mdio_readq(p->register_base + SMI_RD_DAT);
81a9770eacSAndrew Lunn } while (smi_rd.s.pending && --timeout);
82a9770eacSAndrew Lunn
83a9770eacSAndrew Lunn if (smi_rd.s.val)
84a9770eacSAndrew Lunn return smi_rd.s.dat;
85a9770eacSAndrew Lunn else
86a9770eacSAndrew Lunn return -EIO;
87a9770eacSAndrew Lunn }
8893641ecbSAndrew Lunn EXPORT_SYMBOL(cavium_mdiobus_read_c22);
89a9770eacSAndrew Lunn
cavium_mdiobus_read_c45(struct mii_bus * bus,int phy_id,int devad,int regnum)9093641ecbSAndrew Lunn int cavium_mdiobus_read_c45(struct mii_bus *bus, int phy_id, int devad,
9193641ecbSAndrew Lunn int regnum)
9293641ecbSAndrew Lunn {
9393641ecbSAndrew Lunn struct cavium_mdiobus *p = bus->priv;
9493641ecbSAndrew Lunn union cvmx_smix_cmd smi_cmd;
9593641ecbSAndrew Lunn union cvmx_smix_rd_dat smi_rd;
9693641ecbSAndrew Lunn int timeout = 1000;
9793641ecbSAndrew Lunn int r;
9893641ecbSAndrew Lunn
9993641ecbSAndrew Lunn r = cavium_mdiobus_c45_addr(p, phy_id, devad, regnum);
10093641ecbSAndrew Lunn if (r < 0)
10193641ecbSAndrew Lunn return r;
10293641ecbSAndrew Lunn
10393641ecbSAndrew Lunn smi_cmd.u64 = 0;
10493641ecbSAndrew Lunn smi_cmd.s.phy_op = 3; /* MDIO_CLAUSE_45_READ */
10593641ecbSAndrew Lunn smi_cmd.s.phy_adr = phy_id;
10693641ecbSAndrew Lunn smi_cmd.s.reg_adr = regnum;
10793641ecbSAndrew Lunn oct_mdio_writeq(smi_cmd.u64, p->register_base + SMI_CMD);
10893641ecbSAndrew Lunn
10993641ecbSAndrew Lunn do {
11093641ecbSAndrew Lunn /* Wait 1000 clocks so we don't saturate the RSL bus
11193641ecbSAndrew Lunn * doing reads.
11293641ecbSAndrew Lunn */
11393641ecbSAndrew Lunn __delay(1000);
11493641ecbSAndrew Lunn smi_rd.u64 = oct_mdio_readq(p->register_base + SMI_RD_DAT);
11593641ecbSAndrew Lunn } while (smi_rd.s.pending && --timeout);
11693641ecbSAndrew Lunn
11793641ecbSAndrew Lunn if (smi_rd.s.val)
11893641ecbSAndrew Lunn return smi_rd.s.dat;
11993641ecbSAndrew Lunn else
12093641ecbSAndrew Lunn return -EIO;
12193641ecbSAndrew Lunn }
12293641ecbSAndrew Lunn EXPORT_SYMBOL(cavium_mdiobus_read_c45);
12393641ecbSAndrew Lunn
cavium_mdiobus_write_c22(struct mii_bus * bus,int phy_id,int regnum,u16 val)12493641ecbSAndrew Lunn int cavium_mdiobus_write_c22(struct mii_bus *bus, int phy_id, int regnum,
12593641ecbSAndrew Lunn u16 val)
126a9770eacSAndrew Lunn {
127a9770eacSAndrew Lunn struct cavium_mdiobus *p = bus->priv;
128a9770eacSAndrew Lunn union cvmx_smix_cmd smi_cmd;
129a9770eacSAndrew Lunn union cvmx_smix_wr_dat smi_wr;
130a9770eacSAndrew Lunn int timeout = 1000;
131a9770eacSAndrew Lunn
132a9770eacSAndrew Lunn cavium_mdiobus_set_mode(p, C22);
133a9770eacSAndrew Lunn
134a9770eacSAndrew Lunn smi_wr.u64 = 0;
135a9770eacSAndrew Lunn smi_wr.s.dat = val;
136a9770eacSAndrew Lunn oct_mdio_writeq(smi_wr.u64, p->register_base + SMI_WR_DAT);
137a9770eacSAndrew Lunn
138a9770eacSAndrew Lunn smi_cmd.u64 = 0;
139*0c68c8e5SAndrew Lunn smi_cmd.s.phy_op = 0; /* MDIO_CLAUSE_22_WRITE */
140a9770eacSAndrew Lunn smi_cmd.s.phy_adr = phy_id;
141a9770eacSAndrew Lunn smi_cmd.s.reg_adr = regnum;
142a9770eacSAndrew Lunn oct_mdio_writeq(smi_cmd.u64, p->register_base + SMI_CMD);
143a9770eacSAndrew Lunn
144a9770eacSAndrew Lunn do {
145a9770eacSAndrew Lunn /* Wait 1000 clocks so we don't saturate the RSL bus
146a9770eacSAndrew Lunn * doing reads.
147a9770eacSAndrew Lunn */
148a9770eacSAndrew Lunn __delay(1000);
149a9770eacSAndrew Lunn smi_wr.u64 = oct_mdio_readq(p->register_base + SMI_WR_DAT);
150a9770eacSAndrew Lunn } while (smi_wr.s.pending && --timeout);
151a9770eacSAndrew Lunn
152a9770eacSAndrew Lunn if (timeout <= 0)
153a9770eacSAndrew Lunn return -EIO;
154a9770eacSAndrew Lunn
155a9770eacSAndrew Lunn return 0;
156a9770eacSAndrew Lunn }
15793641ecbSAndrew Lunn EXPORT_SYMBOL(cavium_mdiobus_write_c22);
15893641ecbSAndrew Lunn
cavium_mdiobus_write_c45(struct mii_bus * bus,int phy_id,int devad,int regnum,u16 val)15993641ecbSAndrew Lunn int cavium_mdiobus_write_c45(struct mii_bus *bus, int phy_id, int devad,
16093641ecbSAndrew Lunn int regnum, u16 val)
16193641ecbSAndrew Lunn {
16293641ecbSAndrew Lunn struct cavium_mdiobus *p = bus->priv;
16393641ecbSAndrew Lunn union cvmx_smix_cmd smi_cmd;
16493641ecbSAndrew Lunn union cvmx_smix_wr_dat smi_wr;
16593641ecbSAndrew Lunn int timeout = 1000;
16693641ecbSAndrew Lunn int r;
16793641ecbSAndrew Lunn
16893641ecbSAndrew Lunn r = cavium_mdiobus_c45_addr(p, phy_id, devad, regnum);
16993641ecbSAndrew Lunn if (r < 0)
17093641ecbSAndrew Lunn return r;
17193641ecbSAndrew Lunn
17293641ecbSAndrew Lunn smi_wr.u64 = 0;
17393641ecbSAndrew Lunn smi_wr.s.dat = val;
17493641ecbSAndrew Lunn oct_mdio_writeq(smi_wr.u64, p->register_base + SMI_WR_DAT);
17593641ecbSAndrew Lunn
17693641ecbSAndrew Lunn smi_cmd.u64 = 0;
17793641ecbSAndrew Lunn smi_cmd.s.phy_op = 1; /* MDIO_CLAUSE_45_WRITE */
17893641ecbSAndrew Lunn smi_cmd.s.phy_adr = phy_id;
17993641ecbSAndrew Lunn smi_cmd.s.reg_adr = devad;
18093641ecbSAndrew Lunn oct_mdio_writeq(smi_cmd.u64, p->register_base + SMI_CMD);
18193641ecbSAndrew Lunn
18293641ecbSAndrew Lunn do {
18393641ecbSAndrew Lunn /* Wait 1000 clocks so we don't saturate the RSL bus
18493641ecbSAndrew Lunn * doing reads.
18593641ecbSAndrew Lunn */
18693641ecbSAndrew Lunn __delay(1000);
18793641ecbSAndrew Lunn smi_wr.u64 = oct_mdio_readq(p->register_base + SMI_WR_DAT);
18893641ecbSAndrew Lunn } while (smi_wr.s.pending && --timeout);
18993641ecbSAndrew Lunn
19093641ecbSAndrew Lunn if (timeout <= 0)
19193641ecbSAndrew Lunn return -EIO;
19293641ecbSAndrew Lunn
19393641ecbSAndrew Lunn return 0;
19493641ecbSAndrew Lunn }
19593641ecbSAndrew Lunn EXPORT_SYMBOL(cavium_mdiobus_write_c45);
196a9770eacSAndrew Lunn
197a9770eacSAndrew Lunn MODULE_DESCRIPTION("Common code for OCTEON and Thunder MDIO bus drivers");
198a9770eacSAndrew Lunn MODULE_AUTHOR("David Daney");
199a9770eacSAndrew Lunn MODULE_LICENSE("GPL v2");
200