Merge tag 'efi-2020-07-rc6' of https://gitlab.denx.de/u-boot/custodians/u-boot-efi
[oweals/u-boot.git] / drivers / net / mscc_eswitch / mscc_miim.c
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
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
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
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
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 }