summaryrefslogtreecommitdiff
path: root/drivers/net/mdio-ipq4019.c
blob: 50134b4d9b6a17869bd055bf1e8d1cb3f3cf6318 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
// SPDX-License-Identifier: GPL-2.0+
/*
 * Qualcomm IPQ4019 MDIO driver
 *
 * Copyright (c) 2020 Sartura Ltd.
 *
 * Author: Luka Kovacic <luka.kovacic@sartura.hr>
 * Author: Robert Marko <robert.marko@sartura.hr>
 *
 * Based on Linux driver
 */

#include <asm/io.h>
#include <common.h>
#include <dm.h>
#include <errno.h>
#include <linux/bitops.h>
#include <linux/iopoll.h>
#include <miiphy.h>
#include <phy.h>

#define MDIO_MODE_REG               0x40
#define MDIO_ADDR_REG               0x44
#define MDIO_DATA_WRITE_REG         0x48
#define MDIO_DATA_READ_REG          0x4c
#define MDIO_CMD_REG                0x50
#define MDIO_CMD_ACCESS_BUSY        BIT(16)
#define MDIO_CMD_ACCESS_START       BIT(8)
#define MDIO_CMD_ACCESS_CODE_READ   0
#define MDIO_CMD_ACCESS_CODE_WRITE  1

/* 0 = Clause 22, 1 = Clause 45 */
#define MDIO_MODE_BIT               BIT(8)

#define IPQ4019_MDIO_TIMEOUT    10000
#define IPQ4019_MDIO_SLEEP      10

struct ipq4019_mdio_priv {
	phys_addr_t mdio_base;
};

static int ipq4019_mdio_wait_busy(struct ipq4019_mdio_priv *priv)
{
	unsigned int busy;

	return readl_poll_sleep_timeout(priv->mdio_base + MDIO_CMD_REG, busy,
				  (busy & MDIO_CMD_ACCESS_BUSY) == 0, IPQ4019_MDIO_SLEEP,
				  IPQ4019_MDIO_TIMEOUT);
}

int ipq4019_mdio_read(struct udevice *dev, int addr, int devad, int reg)
{
	struct ipq4019_mdio_priv *priv = dev_get_priv(dev);
	unsigned int cmd;

	if (ipq4019_mdio_wait_busy(priv))
		return -ETIMEDOUT;

	/* Issue the phy address and reg */
	writel((addr << 8) | reg, priv->mdio_base + MDIO_ADDR_REG);

	cmd = MDIO_CMD_ACCESS_START | MDIO_CMD_ACCESS_CODE_READ;

	/* Issue read command */
	writel(cmd, priv->mdio_base + MDIO_CMD_REG);

	/* Wait read complete */
	if (ipq4019_mdio_wait_busy(priv))
		return -ETIMEDOUT;

	/* Read and return data */
	return readl(priv->mdio_base + MDIO_DATA_READ_REG);
}

int ipq4019_mdio_write(struct udevice *dev, int addr, int devad,
					  int reg, u16 val)
{
	struct ipq4019_mdio_priv *priv = dev_get_priv(dev);
	unsigned int cmd;

	if (ipq4019_mdio_wait_busy(priv))
		return -ETIMEDOUT;

	/* Issue the phy addreass and reg */
	writel((addr << 8) | reg, priv->mdio_base + MDIO_ADDR_REG);

	/* Issue write data */
	writel(val, priv->mdio_base + MDIO_DATA_WRITE_REG);

	cmd = MDIO_CMD_ACCESS_START | MDIO_CMD_ACCESS_CODE_WRITE;

	/* Issue write command */
	writel(cmd, priv->mdio_base + MDIO_CMD_REG);

	/* Wait for write complete */

	if (ipq4019_mdio_wait_busy(priv))
		return -ETIMEDOUT;

	return 0;
}

static const struct mdio_ops ipq4019_mdio_ops = {
	.read = ipq4019_mdio_read,
	.write = ipq4019_mdio_write,
};

static int ipq4019_mdio_bind(struct udevice *dev)
{
	if (ofnode_valid(dev_ofnode(dev)))
		device_set_name(dev, ofnode_get_name(dev_ofnode(dev)));

	return 0;
}

static int ipq4019_mdio_probe(struct udevice *dev)
{
	struct ipq4019_mdio_priv *priv = dev_get_priv(dev);
	unsigned int data;

	priv->mdio_base = dev_read_addr(dev);
	if (priv->mdio_base == FDT_ADDR_T_NONE)
		return -EINVAL;

	/* Enter Clause 22 mode */
	data = readl(priv->mdio_base + MDIO_MODE_REG);
	data &= ~MDIO_MODE_BIT;
	writel(data, priv->mdio_base + MDIO_MODE_REG);

	return 0;
}

static const struct udevice_id ipq4019_mdio_ids[] = {
	{ .compatible = "qcom,ipq4019-mdio", },
	{ }
};

U_BOOT_DRIVER(ipq4019_mdio) = {
	.name           = "ipq4019_mdio",
	.id             = UCLASS_MDIO,
	.of_match       = ipq4019_mdio_ids,
	.bind           = ipq4019_mdio_bind,
	.probe          = ipq4019_mdio_probe,
	.ops            = &ipq4019_mdio_ops,
	.priv_auto	  = sizeof(struct ipq4019_mdio_priv),
};