1213346Sjmallett/*-
2213346Sjmallett * Copyright (c) 2010 Juli Mallett <jmallett@FreeBSD.org>
3213346Sjmallett * All rights reserved.
4213346Sjmallett *
5213346Sjmallett * Redistribution and use in source and binary forms, with or without
6213346Sjmallett * modification, are permitted provided that the following conditions
7213346Sjmallett * are met:
8213346Sjmallett * 1. Redistributions of source code must retain the above copyright
9213346Sjmallett *    notice, this list of conditions and the following disclaimer.
10213346Sjmallett * 2. Redistributions in binary form must reproduce the above copyright
11213346Sjmallett *    notice, this list of conditions and the following disclaimer in the
12213346Sjmallett *    documentation and/or other materials provided with the distribution.
13213346Sjmallett *
14213346Sjmallett * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15213346Sjmallett * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16213346Sjmallett * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17213346Sjmallett * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18213346Sjmallett * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19213346Sjmallett * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20213346Sjmallett * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21213346Sjmallett * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22213346Sjmallett * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23213346Sjmallett * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24213346Sjmallett * SUCH DAMAGE.
25213346Sjmallett *
26213346Sjmallett * $FreeBSD$
27213346Sjmallett */
28213346Sjmallett
29213346Sjmallett/*
30213346Sjmallett * Interface to the Marvell 88E61XX SMI/MDIO.
31213346Sjmallett */
32213346Sjmallett
33213346Sjmallett#include <sys/cdefs.h>
34213346Sjmallett__FBSDID("$FreeBSD$");
35213346Sjmallett
36213346Sjmallett#include <sys/param.h>
37213346Sjmallett#include <sys/systm.h>
38213346Sjmallett#include <sys/bus.h>
39213346Sjmallett#include <sys/endian.h>
40213346Sjmallett#include <sys/kernel.h>
41213346Sjmallett#include <sys/mbuf.h>
42213346Sjmallett#include <sys/socket.h>
43213346Sjmallett
44213346Sjmallett#include <dev/mii/mii.h>
45213346Sjmallett
46213346Sjmallett#include <net/ethernet.h>
47213346Sjmallett#include <net/if.h>
48213346Sjmallett
49213346Sjmallett#include "wrapper-cvmx-includes.h"
50213346Sjmallett#include "ethernet-headers.h"
51213346Sjmallett
52213346Sjmallett#define	MV88E61XX_SMI_REG_CMD	0x00	/* Indirect command register.  */
53213346Sjmallett#define	 MV88E61XX_SMI_CMD_BUSY		0x8000	/* Busy bit.  */
54213346Sjmallett#define	 MV88E61XX_SMI_CMD_22		0x1000	/* Clause 22 (default 45.)  */
55213346Sjmallett#define	 MV88E61XX_SMI_CMD_READ		0x0800	/* Read command.  */
56213346Sjmallett#define	 MV88E61XX_SMI_CMD_WRITE	0x0400	/* Write command.  */
57213346Sjmallett#define	 MV88E61XX_SMI_CMD_PHY(phy)	(((phy) & 0x1f) << 5)
58213346Sjmallett#define	 MV88E61XX_SMI_CMD_REG(reg)	((reg) & 0x1f)
59213346Sjmallett
60213346Sjmallett#define	MV88E61XX_SMI_REG_DAT	0x01	/* Indirect data register.  */
61213346Sjmallett
62213346Sjmallettstatic int cvm_oct_mv88e61xx_smi_read(struct ifnet *, int, int);
63213346Sjmallettstatic void cvm_oct_mv88e61xx_smi_write(struct ifnet *, int, int, int);
64213762Sjmallettstatic int cvm_oct_mv88e61xx_smi_wait(struct ifnet *);
65213346Sjmallett
66213346Sjmallettint
67213346Sjmallettcvm_oct_mv88e61xx_setup_device(struct ifnet *ifp)
68213346Sjmallett{
69213346Sjmallett	cvm_oct_private_t *priv = (cvm_oct_private_t *)ifp->if_softc;
70213346Sjmallett
71213762Sjmallett	priv->mdio_read = cvm_oct_mv88e61xx_smi_read;
72213762Sjmallett	priv->mdio_write = cvm_oct_mv88e61xx_smi_write;
73213762Sjmallett	priv->phy_device = "mv88e61xxphy";
74213346Sjmallett
75213346Sjmallett	return (0);
76213346Sjmallett}
77213346Sjmallett
78213346Sjmallettstatic int
79213346Sjmallettcvm_oct_mv88e61xx_smi_read(struct ifnet *ifp, int phy_id, int location)
80213346Sjmallett{
81213762Sjmallett	cvm_oct_private_t *priv = (cvm_oct_private_t *)ifp->if_softc;
82213346Sjmallett	int error;
83213346Sjmallett
84213762Sjmallett	error = cvm_oct_mv88e61xx_smi_wait(ifp);
85213346Sjmallett	if (error != 0)
86213346Sjmallett		return (0);
87213346Sjmallett
88213762Sjmallett	cvm_oct_mdio_write(ifp, priv->phy_id, MV88E61XX_SMI_REG_CMD,
89213346Sjmallett	    MV88E61XX_SMI_CMD_BUSY | MV88E61XX_SMI_CMD_22 |
90213346Sjmallett	    MV88E61XX_SMI_CMD_READ | MV88E61XX_SMI_CMD_PHY(phy_id) |
91213346Sjmallett	    MV88E61XX_SMI_CMD_REG(location));
92213346Sjmallett
93213762Sjmallett	error = cvm_oct_mv88e61xx_smi_wait(ifp);
94213346Sjmallett	if (error != 0)
95213346Sjmallett		return (0);
96213346Sjmallett
97213762Sjmallett	return (cvm_oct_mdio_read(ifp, priv->phy_id, MV88E61XX_SMI_REG_DAT));
98213346Sjmallett}
99213346Sjmallett
100213346Sjmallettstatic void
101213346Sjmallettcvm_oct_mv88e61xx_smi_write(struct ifnet *ifp, int phy_id, int location, int val)
102213346Sjmallett{
103213762Sjmallett	cvm_oct_private_t *priv = (cvm_oct_private_t *)ifp->if_softc;
104213762Sjmallett
105213762Sjmallett	cvm_oct_mv88e61xx_smi_wait(ifp);
106213762Sjmallett	cvm_oct_mdio_write(ifp, priv->phy_id, MV88E61XX_SMI_REG_DAT, val);
107213762Sjmallett	cvm_oct_mdio_write(ifp, priv->phy_id, MV88E61XX_SMI_REG_CMD,
108213346Sjmallett	    MV88E61XX_SMI_CMD_BUSY | MV88E61XX_SMI_CMD_22 |
109213346Sjmallett	    MV88E61XX_SMI_CMD_WRITE | MV88E61XX_SMI_CMD_PHY(phy_id) |
110213346Sjmallett	    MV88E61XX_SMI_CMD_REG(location));
111213762Sjmallett	cvm_oct_mv88e61xx_smi_wait(ifp);
112213346Sjmallett}
113213346Sjmallett
114213346Sjmallettstatic int
115213762Sjmallettcvm_oct_mv88e61xx_smi_wait(struct ifnet *ifp)
116213346Sjmallett{
117213762Sjmallett	cvm_oct_private_t *priv = (cvm_oct_private_t *)ifp->if_softc;
118213346Sjmallett	uint16_t cmd;
119213346Sjmallett	unsigned i;
120213346Sjmallett
121213346Sjmallett	for (i = 0; i < 10000; i++) {
122213762Sjmallett		cmd = cvm_oct_mdio_read(ifp, priv->phy_id, MV88E61XX_SMI_REG_CMD);
123213346Sjmallett		if ((cmd & MV88E61XX_SMI_CMD_BUSY) == 0)
124213346Sjmallett			return (0);
125213346Sjmallett	}
126213346Sjmallett	return (ETIMEDOUT);
127213346Sjmallett}
128