1/*
2 **************************************************************************
3 * Copyright (c) 2013-2015, The Linux Foundation. All rights reserved.
4 *
5 * Permission to use, copy, modify, and/or distribute this software for
6 * any purpose with or without fee is hereby granted, provided that the
7 * above copyright notice and this permission notice appear in all copies.
8 *
9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
15 * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16 **************************************************************************
17 */
18/*
19 * @file
20 * IPQ806x MDIO bus support.
21 *
22 * @note Many of the functions other than the device specific functions
23 *  changes for operating system other than Linux 2.6.xx
24 *-----------------------------REVISION HISTORY--------------------------------
25 * Qualcomm Atheros		09/Jun/2013				Created
26 */
27
28#include <linux/version.h>
29#include <linux/kernel.h>
30#include <linux/bitops.h>
31#include <linux/phy.h>
32#include <linux/device.h>
33
34#ifdef CONFIG_OF
35#include <msm_nss_gmac.h>
36#else
37#include <mach/msm_nss_gmac.h>
38#endif
39
40#include <nss_gmac_dev.h>
41#include <nss_gmac_network_interface.h>
42
43
44static int32_t phy_irq[PHY_MAX_ADDR];
45
46
47/**
48 * @brief MDIO bus read
49 * @param[in] pointer to struct mii_bus
50 * @param[in] Phy MDIO address
51 * @param[in] Register number
52 * @return Contents of MDIO register
53 */
54static int32_t nss_gmac_mdiobus_read(struct mii_bus *bus, int32_t phy_id,
55							int32_t regnum)
56{
57	int32_t status;
58	uint16_t data;
59	struct nss_gmac_dev *gmacdev;
60
61	gmacdev = (struct nss_gmac_dev *)bus->priv;
62
63	status = nss_gmac_read_phy_reg((uint32_t *)gmacdev->mac_base,
64					phy_id, regnum,
65					&data, gmacdev->mdc_clk_div);
66
67	if (status != 0)
68		data = 0;
69
70	return (int32_t)data;
71}
72
73
74/**
75 * @brief MDIO bus write
76 * @param[in] pointer to struct mii_bus
77 * @param[in] Phy MDIO address
78 * @param[in] Register number
79 * @param[in] Value to write
80 * @return 0 on Success
81 */
82static int32_t nss_gmac_mdiobus_write(struct mii_bus *bus, int32_t phy_id,
83						int32_t regnum, uint16_t val)
84{
85	struct nss_gmac_dev *gmacdev;
86
87	gmacdev = (struct nss_gmac_dev *)bus->priv;
88
89	nss_gmac_write_phy_reg((uint32_t *)gmacdev->mac_base, phy_id,
90				regnum, val, gmacdev->mdc_clk_div);
91
92	return 0;
93}
94
95
96/**
97 * @brief MDIO bus reset
98 * @param[in] pointer to struct mii_bus
99 * @return 0 on Success
100 */
101int32_t nss_gmac_mdiobus_reset(struct mii_bus *bus)
102{
103	struct nss_gmac_dev *gmacdev;
104
105	gmacdev = (struct nss_gmac_dev *)bus->priv;
106	gmacdev->mdc_clk_div = MDC_CLK_DIV;
107	netdev_dbg(gmacdev->netdev, "%s: GMAC%d MDC Clk div set to - 0x%x\n",
108		      __func__, gmacdev->macid, gmacdev->mdc_clk_div);
109
110	return 0;
111}
112
113
114/**
115 * @brief Initialize and register MDIO bus
116 * @param[in] pointer to nss_gmac_dev
117 * @return 0 on Success
118 */
119int32_t nss_gmac_init_mdiobus(struct nss_gmac_dev *gmacdev)
120{
121	struct mii_bus *miibus = NULL;
122	struct phy_device *phydev = NULL;
123
124	miibus = mdiobus_alloc();
125	if (miibus == NULL)
126		return -ENOMEM;
127
128	miibus->name = "nss gmac mdio bus";
129	snprintf(miibus->id, MII_BUS_ID_SIZE, "mdiobus%x", gmacdev->macid);
130
131	miibus->priv = (void *)gmacdev;
132	miibus->read = nss_gmac_mdiobus_read;
133	miibus->write = nss_gmac_mdiobus_write;
134	miibus->reset = nss_gmac_mdiobus_reset;
135	mutex_init(&(miibus->mdio_lock));
136	miibus->parent = &(gmacdev->pdev->dev);
137
138	phy_irq[gmacdev->phy_base] = PHY_POLL;
139	miibus->irq = phy_irq;
140	miibus->phy_mask = ~((uint32_t)(1 << gmacdev->phy_base));
141
142	if (mdiobus_register(miibus) != 0) {
143		mdiobus_free(miibus);
144		netdev_dbg(gmacdev->netdev, "%s: mdiobus_reg failed\n", __func__);
145		return -EIO;
146	}
147
148	phydev = miibus->phy_map[gmacdev->phy_base];
149	if (!phydev) {
150		netdev_dbg(gmacdev->netdev, "%s: No phy device\n", __func__);
151		mdiobus_unregister(miibus);
152		mdiobus_free(miibus);
153		return -ENODEV;
154	}
155
156	phydev->interface = gmacdev->phy_mii_type;
157
158	gmacdev->miibus = miibus;
159	return 0;
160}
161
162
163/**
164 * @brief De-initialize MDIO bus
165 * @param[in] pointer to nss_gmac_dev
166 * @return void
167 */
168void nss_gmac_deinit_mdiobus(struct nss_gmac_dev *gmacdev)
169{
170	mdiobus_unregister(gmacdev->miibus);
171	mdiobus_free(gmacdev->miibus);
172	gmacdev->miibus = NULL;
173}
174
175
176