1/*
2 * Copyright 2008-2013 Freescale Semiconductor Inc.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are met:
6 *     * Redistributions of source code must retain the above copyright
7 *       notice, this list of conditions and the following disclaimer.
8 *     * Redistributions in binary form must reproduce the above copyright
9 *       notice, this list of conditions and the following disclaimer in the
10 *       documentation and/or other materials provided with the distribution.
11 *     * Neither the name of Freescale Semiconductor nor the
12 *       names of its contributors may be used to endorse or promote products
13 *       derived from this software without specific prior written permission.
14 *
15 *
16 * ALTERNATIVELY, this software may be distributed under the terms of the
17 * GNU General Public License ("GPL") as published by the Free Software
18 * Foundation, either version 2 of that License or (at your option) any
19 * later version.
20 *
21 * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``AS IS'' AND ANY
22 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
23 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
24 * DISCLAIMED. IN NO EVENT SHALL Freescale Semiconductor BE LIABLE FOR ANY
25 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
26 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
27 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
28 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
30 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 */
32
33
34#include "common/general.h"
35#include "fsl_fman_dtsec_mii_acc.h"
36
37
38/**
39 * dtsec_mii_get_div() - calculates the value of the dtsec mii divider
40 * @dtsec_freq:		dtsec clock frequency (in Mhz)
41 *
42 * This function calculates the dtsec mii clock divider that determines
43 * the MII MDC clock. MII MDC clock will be set to work in the range
44 * of 1.5 to 2.5Mhz
45 * The output of this function is the value of MIIMCFG[MgmtClk] which
46 * implicitly determines the divider value.
47 * Note: the dTSEC system clock is equal to 1/2 of the FMan clock.
48 *
49 * The table below which reflects dtsec_mii_get_div() functionality
50 * shows the relations among dtsec_freq, MgmtClk, actual divider
51 * and the MII frequency:
52 *
53 * dtsec freq   MgmtClk     div        MII freq Mhz
54 * [0.....80]     1      (1/4)(1/8)    [0   to 2.5]
55 * [81...120]     2      (1/6)(1/8)    [1.6 to 2.5]
56 * [121..160]     3      (1/8)(1/8)    [1.8 to 2.5]
57 * [161..200]     4      (1/10)(1/8)   [2.0 to 2.5]
58 * [201..280]     5      (1/14)(1/8)   [1.8 to 2.5]
59 * [281..400]     6      (1/20)(1/8)   [1.1 to 2.5]
60 * [401..560]     7      (1/28)(1/8)   [1.8 to 2.5]
61 * [560..frq]     7      (1/28)(1/8)   [frq/224]
62 *
63 * Returns: the MIIMCFG[MgmtClk] appropriate value
64 */
65
66static uint8_t dtsec_mii_get_div(uint16_t dtsec_freq)
67{
68	uint16_t mgmt_clk;
69
70	if (dtsec_freq < 80) mgmt_clk = 1;
71	else if (dtsec_freq < 120) mgmt_clk = 2;
72	else if (dtsec_freq < 160) mgmt_clk = 3;
73	else if (dtsec_freq < 200) mgmt_clk = 4;
74	else if (dtsec_freq < 280) mgmt_clk = 5;
75	else if (dtsec_freq < 400) mgmt_clk = 6;
76	else mgmt_clk = 7;
77
78	return (uint8_t)mgmt_clk;
79}
80
81void fman_dtsec_mii_reset(struct dtsec_mii_reg *regs)
82{
83	/* Reset the management interface */
84	iowrite32be(ioread32be(&regs->miimcfg) | MIIMCFG_RESET_MGMT,
85			&regs->miimcfg);
86	iowrite32be(ioread32be(&regs->miimcfg) & ~MIIMCFG_RESET_MGMT,
87			&regs->miimcfg);
88}
89
90
91int fman_dtsec_mii_write_reg(struct dtsec_mii_reg *regs, uint8_t addr,
92		uint8_t reg, uint16_t data, uint16_t dtsec_freq)
93{
94	uint32_t	tmp;
95
96	/* Setup the MII Mgmt clock speed */
97	iowrite32be((uint32_t)dtsec_mii_get_div(dtsec_freq), &regs->miimcfg);
98	wmb();
99
100	/* Stop the MII management read cycle */
101	iowrite32be(0, &regs->miimcom);
102	/* Dummy read to make sure MIIMCOM is written */
103	tmp = ioread32be(&regs->miimcom);
104	wmb();
105
106	/* Setting up MII Management Address Register */
107	tmp = (uint32_t)((addr << MIIMADD_PHY_ADDR_SHIFT) | reg);
108	iowrite32be(tmp, &regs->miimadd);
109	wmb();
110
111	/* Setting up MII Management Control Register with data */
112	iowrite32be((uint32_t)data, &regs->miimcon);
113	/* Dummy read to make sure MIIMCON is written */
114	tmp = ioread32be(&regs->miimcon);
115	wmb();
116
117	/* Wait until MII management write is complete */
118	/* todo: a timeout could be useful here */
119	while ((ioread32be(&regs->miimind)) & MIIMIND_BUSY)
120		/* busy wait */;
121
122	return 0;
123}
124
125int fman_dtsec_mii_read_reg(struct dtsec_mii_reg *regs, uint8_t  addr,
126		uint8_t reg, uint16_t *data, uint16_t dtsec_freq)
127{
128	uint32_t	tmp;
129
130	/* Setup the MII Mgmt clock speed */
131	iowrite32be((uint32_t)dtsec_mii_get_div(dtsec_freq), &regs->miimcfg);
132	wmb();
133
134	/* Setting up the MII Management Address Register */
135	tmp = (uint32_t)((addr << MIIMADD_PHY_ADDR_SHIFT) | reg);
136	iowrite32be(tmp, &regs->miimadd);
137	wmb();
138
139	/* Perform an MII management read cycle */
140	iowrite32be(MIIMCOM_READ_CYCLE, &regs->miimcom);
141	/* Dummy read to make sure MIIMCOM is written */
142	tmp = ioread32be(&regs->miimcom);
143	wmb();
144
145	/* Wait until MII management read is complete */
146	/* todo: a timeout could be useful here */
147	while ((ioread32be(&regs->miimind)) & MIIMIND_BUSY)
148		/* busy wait */;
149
150	/* Read MII management status  */
151	*data = (uint16_t)ioread32be(&regs->miimstat);
152	wmb();
153
154	iowrite32be(0, &regs->miimcom);
155	/* Dummy read to make sure MIIMCOM is written */
156	tmp = ioread32be(&regs->miimcom);
157
158	if (*data == 0xffff)
159		return -ENXIO;
160
161	return 0;
162}
163
164