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