| 1 | From 12f4b99d63edc15849357c09e22a36445c2752cc Mon Sep 17 00:00:00 2001 |
| 2 | From: John Crispin <blogic@openwrt.org> |
| 3 | Date: Mon, 22 Oct 2012 09:28:30 +0200 |
| 4 | Subject: [PATCH 115/123] NET: PHY: adds driver for lantiq PHY11G |
| 5 | |
| 6 | Signed-off-by: John Crispin <blogic@openwrt.org> |
| 7 | --- |
| 8 | drivers/net/phy/Kconfig | 5 ++ |
| 9 | drivers/net/phy/Makefile | 1 + |
| 10 | drivers/net/phy/lantiq.c | 178 ++++++++++++++++++++++++++++++++++++++++++++++ |
| 11 | 3 files changed, 184 insertions(+) |
| 12 | create mode 100644 drivers/net/phy/lantiq.c |
| 13 | |
| 14 | Index: linux-3.7.1/drivers/net/phy/Kconfig |
| 15 | =================================================================== |
| 16 | --- linux-3.7.1.orig/drivers/net/phy/Kconfig 2012-12-21 10:30:28.953462268 +0100 |
| 17 | +++ linux-3.7.1/drivers/net/phy/Kconfig 2012-12-21 10:30:29.609462283 +0100 |
| 18 | @@ -150,6 +150,11 @@ |
| 19 | ---help--- |
| 20 | Currently has a driver for the KSZ8041 |
| 21 | |
| 22 | +config LANTIQ_PHY |
| 23 | + tristate "Driver for Lantiq PHYs" |
| 24 | + ---help--- |
| 25 | + Supports the 11G and 22E PHYs. |
| 26 | + |
| 27 | config FIXED_PHY |
| 28 | bool "Driver for MDIO Bus/PHY emulation with fixed speed/link PHYs" |
| 29 | depends on PHYLIB=y |
| 30 | Index: linux-3.7.1/drivers/net/phy/Makefile |
| 31 | =================================================================== |
| 32 | --- linux-3.7.1.orig/drivers/net/phy/Makefile 2012-12-21 10:30:28.953462268 +0100 |
| 33 | +++ linux-3.7.1/drivers/net/phy/Makefile 2012-12-21 10:30:29.609462283 +0100 |
| 34 | @@ -38,6 +38,7 @@ |
| 35 | obj-$(CONFIG_DP83640_PHY) += dp83640.o |
| 36 | obj-$(CONFIG_STE10XP) += ste10Xp.o |
| 37 | obj-$(CONFIG_MICREL_PHY) += micrel.o |
| 38 | +obj-$(CONFIG_LANTIQ_PHY) += lantiq.o |
| 39 | obj-$(CONFIG_MDIO_OCTEON) += mdio-octeon.o |
| 40 | obj-$(CONFIG_MICREL_KS8995MA) += spi_ks8995.o |
| 41 | obj-$(CONFIG_AT803X_PHY) += at803x.o |
| 42 | Index: linux-3.7.1/drivers/net/phy/lantiq.c |
| 43 | =================================================================== |
| 44 | --- /dev/null 1970-01-01 00:00:00.000000000 +0000 |
| 45 | +++ linux-3.7.1/drivers/net/phy/lantiq.c 2012-12-21 11:47:10.721571920 +0100 |
| 46 | @@ -0,0 +1,220 @@ |
| 47 | +/* |
| 48 | + * This program is free software; you can redistribute it and/or modify |
| 49 | + * it under the terms of the GNU General Public License as published by |
| 50 | + * the Free Software Foundation; either version 2 of the License, or |
| 51 | + * (at your option) any later version. |
| 52 | + * |
| 53 | + * This program is distributed in the hope that it will be useful, |
| 54 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 55 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 56 | + * GNU General Public License for more details. |
| 57 | + * |
| 58 | + * You should have received a copy of the GNU General Public License |
| 59 | + * along with this program; if not, write to the Free Software |
| 60 | + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. |
| 61 | + * |
| 62 | + * Copyright (C) 2012 Daniel Schwierzeck <daniel.schwierzeck@googlemail.com> |
| 63 | + */ |
| 64 | + |
| 65 | +#include <linux/module.h> |
| 66 | +#include <linux/phy.h> |
| 67 | + |
| 68 | +#define MII_MMDCTRL 0x0d |
| 69 | +#define MII_MMDDATA 0x0e |
| 70 | + |
| 71 | +#define MII_VR9_11G_IMASK 0x19 /* interrupt mask */ |
| 72 | +#define MII_VR9_11G_ISTAT 0x1a /* interrupt status */ |
| 73 | + |
| 74 | +#define INT_VR9_11G_WOL BIT(15) /* Wake-On-LAN */ |
| 75 | +#define INT_VR9_11G_ANE BIT(11) /* Auto-Neg error */ |
| 76 | +#define INT_VR9_11G_ANC BIT(10) /* Auto-Neg complete */ |
| 77 | +#define INT_VR9_11G_ADSC BIT(5) /* Link auto-downspeed detect */ |
| 78 | +#define INT_VR9_11G_DXMC BIT(2) /* Duplex mode change */ |
| 79 | +#define INT_VR9_11G_LSPC BIT(1) /* Link speed change */ |
| 80 | +#define INT_VR9_11G_LSTC BIT(0) /* Link state change */ |
| 81 | +#define INT_VR9_11G_MASK (INT_VR9_11G_LSTC | INT_VR9_11G_ADSC) |
| 82 | + |
| 83 | +#define ADVERTISED_MPD BIT(10) /* Multi-port device */ |
| 84 | + |
| 85 | +#define MMD_DEVAD 0x1f |
| 86 | +#define MMD_ACTYPE_SHIFT 14 |
| 87 | +#define MMD_ACTYPE_ADDRESS (0 << MMD_ACTYPE_SHIFT) |
| 88 | +#define MMD_ACTYPE_DATA (1 << MMD_ACTYPE_SHIFT) |
| 89 | +#define MMD_ACTYPE_DATA_PI (2 << MMD_ACTYPE_SHIFT) |
| 90 | +#define MMD_ACTYPE_DATA_PIWR (3 << MMD_ACTYPE_SHIFT) |
| 91 | + |
| 92 | +static __maybe_unused int vr9_gphy_mmd_read(struct phy_device *phydev, |
| 93 | + u16 regnum) |
| 94 | +{ |
| 95 | + phy_write(phydev, MII_MMDCTRL, MMD_ACTYPE_ADDRESS | MMD_DEVAD); |
| 96 | + phy_write(phydev, MII_MMDDATA, regnum); |
| 97 | + phy_write(phydev, MII_MMDCTRL, MMD_ACTYPE_DATA | MMD_DEVAD); |
| 98 | + |
| 99 | + return phy_read(phydev, MII_MMDDATA); |
| 100 | +} |
| 101 | + |
| 102 | +static __maybe_unused int vr9_gphy_mmd_write(struct phy_device *phydev, |
| 103 | + u16 regnum, u16 val) |
| 104 | +{ |
| 105 | + phy_write(phydev, MII_MMDCTRL, MMD_ACTYPE_ADDRESS | MMD_DEVAD); |
| 106 | + phy_write(phydev, MII_MMDDATA, regnum); |
| 107 | + phy_write(phydev, MII_MMDCTRL, MMD_ACTYPE_DATA | MMD_DEVAD); |
| 108 | + phy_write(phydev, MII_MMDDATA, val); |
| 109 | + |
| 110 | + return 0; |
| 111 | +} |
| 112 | + |
| 113 | +static int vr9_gphy_config_init(struct phy_device *phydev) |
| 114 | +{ |
| 115 | + int err; |
| 116 | + |
| 117 | + dev_dbg(&phydev->dev, "%s\n", __func__); |
| 118 | + |
| 119 | + /* Mask all interrupts */ |
| 120 | + err = phy_write(phydev, MII_VR9_11G_IMASK, 0); |
| 121 | + if (err) |
| 122 | + return err; |
| 123 | + |
| 124 | + /* Clear all pending interrupts */ |
| 125 | + phy_read(phydev, MII_VR9_11G_ISTAT); |
| 126 | + |
| 127 | + return 0; |
| 128 | +} |
| 129 | + |
| 130 | +static int vr9_gphy_config_aneg(struct phy_device *phydev) |
| 131 | +{ |
| 132 | + int reg, err; |
| 133 | + |
| 134 | + /* Advertise as multi-port device */ |
| 135 | + reg = phy_read(phydev, MII_CTRL1000); |
| 136 | + reg |= ADVERTISED_MPD; |
| 137 | + err = phy_write(phydev, MII_CTRL1000, reg); |
| 138 | + if (err) |
| 139 | + return err; |
| 140 | + |
| 141 | + return genphy_config_aneg(phydev); |
| 142 | +} |
| 143 | + |
| 144 | +static int vr9_gphy_ack_interrupt(struct phy_device *phydev) |
| 145 | +{ |
| 146 | + int reg; |
| 147 | + |
| 148 | + /* |
| 149 | + * Possible IRQ numbers: |
| 150 | + * - IM3_IRL18 for GPHY0 |
| 151 | + * - IM3_IRL17 for GPHY1 |
| 152 | + * |
| 153 | + * Due to a silicon bug IRQ lines are not really independent from |
| 154 | + * each other. Sometimes the two lines are driven at the same time |
| 155 | + * if only one GPHY core raises the interrupt. |
| 156 | + */ |
| 157 | + |
| 158 | + reg = phy_read(phydev, MII_VR9_11G_ISTAT); |
| 159 | + |
| 160 | + return (reg < 0) ? reg : 0; |
| 161 | +} |
| 162 | + |
| 163 | +static int vr9_gphy_did_interrupt(struct phy_device *phydev) |
| 164 | +{ |
| 165 | + int reg; |
| 166 | + |
| 167 | + reg = phy_read(phydev, MII_VR9_11G_ISTAT); |
| 168 | + |
| 169 | + return reg > 0; |
| 170 | +} |
| 171 | + |
| 172 | +static int vr9_gphy_config_intr(struct phy_device *phydev) |
| 173 | +{ |
| 174 | + int err; |
| 175 | + |
| 176 | + if (phydev->interrupts == PHY_INTERRUPT_ENABLED) |
| 177 | + err = phy_write(phydev, MII_VR9_11G_IMASK, INT_VR9_11G_MASK); |
| 178 | + else |
| 179 | + err = phy_write(phydev, MII_VR9_11G_IMASK, 0); |
| 180 | + |
| 181 | + return err; |
| 182 | +} |
| 183 | + |
| 184 | +static struct phy_driver lantiq_phy[] = { |
| 185 | + { |
| 186 | + .phy_id = 0xd565a400, |
| 187 | + .phy_id_mask = 0xffffffff, |
| 188 | + .name = "Lantiq XWAY PEF7071", |
| 189 | + .features = (PHY_GBIT_FEATURES | SUPPORTED_Pause), |
| 190 | + .flags = 0, /*PHY_HAS_INTERRUPT,*/ |
| 191 | + .config_init = vr9_gphy_config_init, |
| 192 | + .config_aneg = vr9_gphy_config_aneg, |
| 193 | + .read_status = genphy_read_status, |
| 194 | + .ack_interrupt = vr9_gphy_ack_interrupt, |
| 195 | + .did_interrupt = vr9_gphy_did_interrupt, |
| 196 | + .config_intr = vr9_gphy_config_intr, |
| 197 | + .driver = { .owner = THIS_MODULE }, |
| 198 | + }, { |
| 199 | + .phy_id = 0x030260D0, |
| 200 | + .phy_id_mask = 0xfffffff0, |
| 201 | + .name = "Lantiq XWAY VR9 GPHY 11G v1.3", |
| 202 | + .features = (PHY_GBIT_FEATURES | SUPPORTED_Pause), |
| 203 | + .flags = 0, /*PHY_HAS_INTERRUPT,*/ |
| 204 | + .config_init = vr9_gphy_config_init, |
| 205 | + .config_aneg = vr9_gphy_config_aneg, |
| 206 | + .read_status = genphy_read_status, |
| 207 | + .ack_interrupt = vr9_gphy_ack_interrupt, |
| 208 | + .did_interrupt = vr9_gphy_did_interrupt, |
| 209 | + .config_intr = vr9_gphy_config_intr, |
| 210 | + .driver = { .owner = THIS_MODULE }, |
| 211 | + }, { |
| 212 | + .phy_id = 0xd565a408, |
| 213 | + .phy_id_mask = 0xfffffff8, |
| 214 | + .name = "Lantiq XWAY VR9 GPHY 11G v1.4", |
| 215 | + .features = (PHY_GBIT_FEATURES | SUPPORTED_Pause), |
| 216 | + .flags = 0, /*PHY_HAS_INTERRUPT,*/ |
| 217 | + .config_init = vr9_gphy_config_init, |
| 218 | + .config_aneg = vr9_gphy_config_aneg, |
| 219 | + .read_status = genphy_read_status, |
| 220 | + .ack_interrupt = vr9_gphy_ack_interrupt, |
| 221 | + .did_interrupt = vr9_gphy_did_interrupt, |
| 222 | + .config_intr = vr9_gphy_config_intr, |
| 223 | + .driver = { .owner = THIS_MODULE }, |
| 224 | + }, { |
| 225 | + .phy_id = 0xd565a418, |
| 226 | + .phy_id_mask = 0xfffffff8, |
| 227 | + .name = "Lantiq XWAY XRX PHY22F v1.4", |
| 228 | + .features = (PHY_BASIC_FEATURES | SUPPORTED_Pause), |
| 229 | + .flags = 0, /*PHY_HAS_INTERRUPT,*/ |
| 230 | + .config_init = vr9_gphy_config_init, |
| 231 | + .config_aneg = vr9_gphy_config_aneg, |
| 232 | + .read_status = genphy_read_status, |
| 233 | + .ack_interrupt = vr9_gphy_ack_interrupt, |
| 234 | + .did_interrupt = vr9_gphy_did_interrupt, |
| 235 | + .config_intr = vr9_gphy_config_intr, |
| 236 | + .driver = { .owner = THIS_MODULE }, |
| 237 | + }, |
| 238 | +}; |
| 239 | + |
| 240 | +static int __init ltq_phy_init(void) |
| 241 | +{ |
| 242 | + int i; |
| 243 | + |
| 244 | + for (i = 0; i < ARRAY_SIZE(lantiq_phy); i++) { |
| 245 | + int err = phy_driver_register(&lantiq_phy[i]); |
| 246 | + if (err) |
| 247 | + pr_err("lantiq_phy: failed to load %s\n", lantiq_phy[i].name); |
| 248 | + } |
| 249 | + |
| 250 | + return 0; |
| 251 | +} |
| 252 | + |
| 253 | +static void __exit ltq_phy_exit(void) |
| 254 | +{ |
| 255 | + int i; |
| 256 | + |
| 257 | + for (i = 0; i < ARRAY_SIZE(lantiq_phy); i++) |
| 258 | + phy_driver_unregister(&lantiq_phy[i]); |
| 259 | +} |
| 260 | + |
| 261 | +module_init(ltq_phy_init); |
| 262 | +module_exit(ltq_phy_exit); |
| 263 | + |
| 264 | +MODULE_DESCRIPTION("Lantiq PHY drivers"); |
| 265 | +MODULE_AUTHOR("Daniel Schwierzeck <daniel.schwierzeck@googlemail.com>"); |
| 266 | +MODULE_LICENSE("GPL"); |
| 267 | |