]>
Commit | Line | Data |
---|---|---|
b9b17deb TT |
1 | /* Copyright (c) 2013-2016, The Linux Foundation. All rights reserved. |
2 | * | |
3 | * This program is free software; you can redistribute it and/or modify | |
4 | * it under the terms of the GNU General Public License version 2 and | |
5 | * only version 2 as published by the Free Software Foundation. | |
6 | * | |
7 | * This program is distributed in the hope that it will be useful, | |
8 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
9 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
10 | * GNU General Public License for more details. | |
11 | */ | |
12 | ||
13 | /* Qualcomm Technologies, Inc. EMAC PHY Controller driver. | |
14 | */ | |
15 | ||
b9b17deb TT |
16 | #include <linux/of_mdio.h> |
17 | #include <linux/phy.h> | |
18 | #include <linux/iopoll.h> | |
5f3d3807 | 19 | #include <linux/acpi.h> |
b9b17deb | 20 | #include "emac.h" |
b9b17deb TT |
21 | |
22 | /* EMAC base register offsets */ | |
23 | #define EMAC_MDIO_CTRL 0x001414 | |
24 | #define EMAC_PHY_STS 0x001418 | |
25 | #define EMAC_MDIO_EX_CTRL 0x001440 | |
26 | ||
27 | /* EMAC_MDIO_CTRL */ | |
28 | #define MDIO_MODE BIT(30) | |
29 | #define MDIO_PR BIT(29) | |
30 | #define MDIO_AP_EN BIT(28) | |
31 | #define MDIO_BUSY BIT(27) | |
32 | #define MDIO_CLK_SEL_BMSK 0x7000000 | |
33 | #define MDIO_CLK_SEL_SHFT 24 | |
34 | #define MDIO_START BIT(23) | |
35 | #define SUP_PREAMBLE BIT(22) | |
36 | #define MDIO_RD_NWR BIT(21) | |
37 | #define MDIO_REG_ADDR_BMSK 0x1f0000 | |
38 | #define MDIO_REG_ADDR_SHFT 16 | |
39 | #define MDIO_DATA_BMSK 0xffff | |
40 | #define MDIO_DATA_SHFT 0 | |
41 | ||
42 | /* EMAC_PHY_STS */ | |
43 | #define PHY_ADDR_BMSK 0x1f0000 | |
44 | #define PHY_ADDR_SHFT 16 | |
45 | ||
46 | #define MDIO_CLK_25_4 0 | |
47 | #define MDIO_CLK_25_28 7 | |
48 | ||
49 | #define MDIO_WAIT_TIMES 1000 | |
50 | ||
b9b17deb TT |
51 | static int emac_mdio_read(struct mii_bus *bus, int addr, int regnum) |
52 | { | |
53 | struct emac_adapter *adpt = bus->priv; | |
54 | u32 reg; | |
b9b17deb TT |
55 | |
56 | emac_reg_update32(adpt->base + EMAC_PHY_STS, PHY_ADDR_BMSK, | |
57 | (addr << PHY_ADDR_SHFT)); | |
58 | ||
59 | reg = SUP_PREAMBLE | | |
60 | ((MDIO_CLK_25_4 << MDIO_CLK_SEL_SHFT) & MDIO_CLK_SEL_BMSK) | | |
61 | ((regnum << MDIO_REG_ADDR_SHFT) & MDIO_REG_ADDR_BMSK) | | |
62 | MDIO_START | MDIO_RD_NWR; | |
63 | ||
64 | writel(reg, adpt->base + EMAC_MDIO_CTRL); | |
65 | ||
66 | if (readl_poll_timeout(adpt->base + EMAC_MDIO_CTRL, reg, | |
67 | !(reg & (MDIO_START | MDIO_BUSY)), | |
68 | 100, MDIO_WAIT_TIMES * 100)) | |
24609669 | 69 | return -EIO; |
b9b17deb | 70 | |
24609669 | 71 | return (reg >> MDIO_DATA_SHFT) & MDIO_DATA_BMSK; |
b9b17deb TT |
72 | } |
73 | ||
74 | static int emac_mdio_write(struct mii_bus *bus, int addr, int regnum, u16 val) | |
75 | { | |
76 | struct emac_adapter *adpt = bus->priv; | |
77 | u32 reg; | |
b9b17deb TT |
78 | |
79 | emac_reg_update32(adpt->base + EMAC_PHY_STS, PHY_ADDR_BMSK, | |
80 | (addr << PHY_ADDR_SHFT)); | |
81 | ||
82 | reg = SUP_PREAMBLE | | |
83 | ((MDIO_CLK_25_4 << MDIO_CLK_SEL_SHFT) & MDIO_CLK_SEL_BMSK) | | |
84 | ((regnum << MDIO_REG_ADDR_SHFT) & MDIO_REG_ADDR_BMSK) | | |
85 | ((val << MDIO_DATA_SHFT) & MDIO_DATA_BMSK) | | |
86 | MDIO_START; | |
87 | ||
88 | writel(reg, adpt->base + EMAC_MDIO_CTRL); | |
89 | ||
90 | if (readl_poll_timeout(adpt->base + EMAC_MDIO_CTRL, reg, | |
91 | !(reg & (MDIO_START | MDIO_BUSY)), 100, | |
92 | MDIO_WAIT_TIMES * 100)) | |
24609669 | 93 | return -EIO; |
b9b17deb | 94 | |
24609669 | 95 | return 0; |
b9b17deb TT |
96 | } |
97 | ||
98 | /* Configure the MDIO bus and connect the external PHY */ | |
99 | int emac_phy_config(struct platform_device *pdev, struct emac_adapter *adpt) | |
100 | { | |
101 | struct device_node *np = pdev->dev.of_node; | |
b9b17deb TT |
102 | struct mii_bus *mii_bus; |
103 | int ret; | |
104 | ||
105 | /* Create the mii_bus object for talking to the MDIO bus */ | |
106 | adpt->mii_bus = mii_bus = devm_mdiobus_alloc(&pdev->dev); | |
107 | if (!mii_bus) | |
108 | return -ENOMEM; | |
109 | ||
110 | mii_bus->name = "emac-mdio"; | |
111 | snprintf(mii_bus->id, MII_BUS_ID_SIZE, "%s", pdev->name); | |
112 | mii_bus->read = emac_mdio_read; | |
113 | mii_bus->write = emac_mdio_write; | |
114 | mii_bus->parent = &pdev->dev; | |
115 | mii_bus->priv = adpt; | |
116 | ||
5f3d3807 TT |
117 | if (has_acpi_companion(&pdev->dev)) { |
118 | u32 phy_addr; | |
119 | ||
120 | ret = mdiobus_register(mii_bus); | |
121 | if (ret) { | |
122 | dev_err(&pdev->dev, "could not register mdio bus\n"); | |
123 | return ret; | |
124 | } | |
125 | ret = device_property_read_u32(&pdev->dev, "phy-channel", | |
126 | &phy_addr); | |
127 | if (ret) | |
128 | /* If we can't read a valid phy address, then assume | |
129 | * that there is only one phy on this mdio bus. | |
130 | */ | |
131 | adpt->phydev = phy_find_first(mii_bus); | |
132 | else | |
133 | adpt->phydev = mdiobus_get_phy(mii_bus, phy_addr); | |
134 | ||
994c5483 TT |
135 | /* of_phy_find_device() claims a reference to the phydev, |
136 | * so we do that here manually as well. When the driver | |
137 | * later unloads, it can unilaterally drop the reference | |
138 | * without worrying about ACPI vs DT. | |
139 | */ | |
140 | if (adpt->phydev) | |
141 | get_device(&adpt->phydev->mdio.dev); | |
5f3d3807 TT |
142 | } else { |
143 | struct device_node *phy_np; | |
144 | ||
145 | ret = of_mdiobus_register(mii_bus, np); | |
146 | if (ret) { | |
147 | dev_err(&pdev->dev, "could not register mdio bus\n"); | |
148 | return ret; | |
149 | } | |
150 | ||
151 | phy_np = of_parse_phandle(np, "phy-handle", 0); | |
152 | adpt->phydev = of_phy_find_device(phy_np); | |
6ffe1c4c | 153 | of_node_put(phy_np); |
b9b17deb TT |
154 | } |
155 | ||
b9b17deb TT |
156 | if (!adpt->phydev) { |
157 | dev_err(&pdev->dev, "could not find external phy\n"); | |
158 | mdiobus_unregister(mii_bus); | |
159 | return -ENODEV; | |
160 | } | |
161 | ||
b9b17deb TT |
162 | return 0; |
163 | } |