1 // SPDX-License-Identifier: (GPL-2.0+ OR MIT)
2 /*
3 * Copyright (c) 2018 Microsemi Corporation
4 */
5
6 #include <miiphy.h>
7 #include <wait_bit.h>
8 #include <linux/bitops.h>
9 #include "mscc_miim.h"
10
11 #define MIIM_STATUS 0x0
12 #define MIIM_STAT_BUSY BIT(3)
13 #define MIIM_CMD 0x8
14 #define MIIM_CMD_SCAN BIT(0)
15 #define MIIM_CMD_OPR_WRITE BIT(1)
16 #define MIIM_CMD_OPR_READ BIT(2)
17 #define MIIM_CMD_SINGLE_SCAN BIT(3)
18 #define MIIM_CMD_WRDATA(x) ((x) << 4)
19 #define MIIM_CMD_REGAD(x) ((x) << 20)
20 #define MIIM_CMD_PHYAD(x) ((x) << 25)
21 #define MIIM_CMD_VLD BIT(31)
22 #define MIIM_DATA 0xC
23 #define MIIM_DATA_ERROR (0x2 << 16)
24
mscc_miim_wait_ready(struct mscc_miim_dev * miim)25 static int mscc_miim_wait_ready(struct mscc_miim_dev *miim)
26 {
27 return wait_for_bit_le32(miim->regs + MIIM_STATUS, MIIM_STAT_BUSY,
28 false, 250, false);
29 }
30
mscc_miim_read(struct mii_dev * bus,int addr,int devad,int reg)31 int mscc_miim_read(struct mii_dev *bus, int addr, int devad, int reg)
32 {
33 struct mscc_miim_dev *miim = (struct mscc_miim_dev *)bus->priv;
34 u32 val;
35 int ret;
36
37 ret = mscc_miim_wait_ready(miim);
38 if (ret)
39 goto out;
40
41 writel(MIIM_CMD_VLD | MIIM_CMD_PHYAD(addr) |
42 MIIM_CMD_REGAD(reg) | MIIM_CMD_OPR_READ,
43 miim->regs + MIIM_CMD);
44
45 ret = mscc_miim_wait_ready(miim);
46 if (ret)
47 goto out;
48
49 val = readl(miim->regs + MIIM_DATA);
50 if (val & MIIM_DATA_ERROR) {
51 ret = -EIO;
52 goto out;
53 }
54
55 ret = val & 0xFFFF;
56 out:
57 return ret;
58 }
59
mscc_miim_write(struct mii_dev * bus,int addr,int devad,int reg,u16 val)60 int mscc_miim_write(struct mii_dev *bus, int addr, int devad, int reg,
61 u16 val)
62 {
63 struct mscc_miim_dev *miim = (struct mscc_miim_dev *)bus->priv;
64 int ret;
65
66 ret = mscc_miim_wait_ready(miim);
67 if (ret < 0)
68 goto out;
69
70 writel(MIIM_CMD_VLD | MIIM_CMD_PHYAD(addr) |
71 MIIM_CMD_REGAD(reg) | MIIM_CMD_WRDATA(val) |
72 MIIM_CMD_OPR_WRITE, miim->regs + MIIM_CMD);
73 out:
74 return ret;
75 }
76
mscc_mdiobus_init(struct mscc_miim_dev * miim,int * miim_count,phys_addr_t miim_base,unsigned long miim_size)77 struct mii_dev *mscc_mdiobus_init(struct mscc_miim_dev *miim, int *miim_count,
78 phys_addr_t miim_base,
79 unsigned long miim_size)
80 {
81 struct mii_dev *bus;
82
83 bus = mdio_alloc();
84
85 if (!bus)
86 return NULL;
87
88 *miim_count += 1;
89 sprintf(bus->name, "miim-bus%d", *miim_count);
90
91 miim[*miim_count].regs = ioremap(miim_base, miim_size);
92 miim[*miim_count].miim_base = miim_base;
93 miim[*miim_count].miim_size = miim_size;
94 bus->priv = &miim[*miim_count];
95 bus->read = mscc_miim_read;
96 bus->write = mscc_miim_write;
97
98 if (mdio_register(bus))
99 return NULL;
100
101 miim[*miim_count].bus = bus;
102 return bus;
103 }
104