1/* 2 * Driver for ICPlus PHYs 3 * 4 * Copyright (c) 2007 Freescale Semiconductor, Inc. 5 * 6 * This program is free software; you can redistribute it and/or modify it 7 * under the terms of the GNU General Public License as published by the 8 * Free Software Foundation; either version 2 of the License, or (at your 9 * option) any later version. 10 * 11 */ 12#include <linux/kernel.h> 13#include <linux/string.h> 14#include <linux/errno.h> 15#include <linux/unistd.h> 16#include <linux/interrupt.h> 17#include <linux/init.h> 18#include <linux/delay.h> 19#include <linux/netdevice.h> 20#include <linux/etherdevice.h> 21#include <linux/skbuff.h> 22#include <linux/spinlock.h> 23#include <linux/mm.h> 24#include <linux/module.h> 25#include <linux/mii.h> 26#include <linux/ethtool.h> 27#include <linux/phy.h> 28 29#include <asm/io.h> 30#include <asm/irq.h> 31#include <asm/uaccess.h> 32 33MODULE_DESCRIPTION("ICPlus IP175C/IP101A/IP101G/IC1001 PHY drivers"); 34MODULE_AUTHOR("Michael Barkowski"); 35MODULE_LICENSE("GPL"); 36 37/* IP101A/G - IP1001 */ 38#define IP10XX_SPEC_CTRL_STATUS 16 /* Spec. Control Register */ 39#define IP1001_RXPHASE_SEL (1<<0) /* Add delay on RX_CLK */ 40#define IP1001_TXPHASE_SEL (1<<1) /* Add delay on TX_CLK */ 41#define IP1001_SPEC_CTRL_STATUS_2 20 /* IP1001 Spec. Control Reg 2 */ 42#define IP1001_APS_ON 11 /* IP1001 APS Mode bit */ 43#define IP101A_G_APS_ON 2 /* IP101A/G APS Mode bit */ 44#define IP101A_G_IRQ_CONF_STATUS 0x11 /* Conf Info IRQ & Status Reg */ 45#define IP101A_G_IRQ_PIN_USED (1<<15) /* INTR pin used */ 46#define IP101A_G_IRQ_DEFAULT IP101A_G_IRQ_PIN_USED 47 48static int ip175c_config_init(struct phy_device *phydev) 49{ 50 int err, i; 51 static int full_reset_performed; 52 53 if (full_reset_performed == 0) { 54 55 /* master reset */ 56 err = mdiobus_write(phydev->bus, 30, 0, 0x175c); 57 if (err < 0) 58 return err; 59 60 /* ensure no bus delays overlap reset period */ 61 err = mdiobus_read(phydev->bus, 30, 0); 62 63 /* data sheet specifies reset period is 2 msec */ 64 mdelay(2); 65 66 /* enable IP175C mode */ 67 err = mdiobus_write(phydev->bus, 29, 31, 0x175c); 68 if (err < 0) 69 return err; 70 71 /* Set MII0 speed and duplex (in PHY mode) */ 72 err = mdiobus_write(phydev->bus, 29, 22, 0x420); 73 if (err < 0) 74 return err; 75 76 /* reset switch ports */ 77 for (i = 0; i < 5; i++) { 78 err = mdiobus_write(phydev->bus, i, 79 MII_BMCR, BMCR_RESET); 80 if (err < 0) 81 return err; 82 } 83 84 for (i = 0; i < 5; i++) 85 err = mdiobus_read(phydev->bus, i, MII_BMCR); 86 87 mdelay(2); 88 89 full_reset_performed = 1; 90 } 91 92 if (phydev->addr != 4) { 93 phydev->state = PHY_RUNNING; 94 phydev->speed = SPEED_100; 95 phydev->duplex = DUPLEX_FULL; 96 phydev->link = 1; 97 netif_carrier_on(phydev->attached_dev); 98 } 99 100 return 0; 101} 102 103static int ip1xx_reset(struct phy_device *phydev) 104{ 105 int bmcr; 106 107 /* Software Reset PHY */ 108 bmcr = phy_read(phydev, MII_BMCR); 109 if (bmcr < 0) 110 return bmcr; 111 bmcr |= BMCR_RESET; 112 bmcr = phy_write(phydev, MII_BMCR, bmcr); 113 if (bmcr < 0) 114 return bmcr; 115 116 do { 117 bmcr = phy_read(phydev, MII_BMCR); 118 if (bmcr < 0) 119 return bmcr; 120 } while (bmcr & BMCR_RESET); 121 122 return 0; 123} 124 125static int ip1001_config_init(struct phy_device *phydev) 126{ 127 int c; 128 129 c = ip1xx_reset(phydev); 130 if (c < 0) 131 return c; 132 133 /* Enable Auto Power Saving mode */ 134 c = phy_read(phydev, IP1001_SPEC_CTRL_STATUS_2); 135 if (c < 0) 136 return c; 137 c |= IP1001_APS_ON; 138 c = phy_write(phydev, IP1001_SPEC_CTRL_STATUS_2, c); 139 if (c < 0) 140 return c; 141 142 if ((phydev->interface == PHY_INTERFACE_MODE_RGMII) || 143 (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID) || 144 (phydev->interface == PHY_INTERFACE_MODE_RGMII_RXID) || 145 (phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID)) { 146 147 c = phy_read(phydev, IP10XX_SPEC_CTRL_STATUS); 148 if (c < 0) 149 return c; 150 151 c &= ~(IP1001_RXPHASE_SEL | IP1001_TXPHASE_SEL); 152 153 if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID) 154 c |= (IP1001_RXPHASE_SEL | IP1001_TXPHASE_SEL); 155 else if (phydev->interface == PHY_INTERFACE_MODE_RGMII_RXID) 156 c |= IP1001_RXPHASE_SEL; 157 else if (phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID) 158 c |= IP1001_TXPHASE_SEL; 159 160 c = phy_write(phydev, IP10XX_SPEC_CTRL_STATUS, c); 161 if (c < 0) 162 return c; 163 } 164 165 return 0; 166} 167 168static int ip101a_g_config_init(struct phy_device *phydev) 169{ 170 int c; 171 172 c = ip1xx_reset(phydev); 173 if (c < 0) 174 return c; 175 176 /* INTR pin used: speed/link/duplex will cause an interrupt */ 177 c = phy_write(phydev, IP101A_G_IRQ_CONF_STATUS, IP101A_G_IRQ_DEFAULT); 178 if (c < 0) 179 return c; 180 181 /* Enable Auto Power Saving mode */ 182 c = phy_read(phydev, IP10XX_SPEC_CTRL_STATUS); 183 c |= IP101A_G_APS_ON; 184 185 return phy_write(phydev, IP10XX_SPEC_CTRL_STATUS, c); 186} 187 188static int ip175c_read_status(struct phy_device *phydev) 189{ 190 if (phydev->addr == 4) /* WAN port */ 191 genphy_read_status(phydev); 192 else 193 /* Don't need to read status for switch ports */ 194 phydev->irq = PHY_IGNORE_INTERRUPT; 195 196 return 0; 197} 198 199static int ip175c_config_aneg(struct phy_device *phydev) 200{ 201 if (phydev->addr == 4) /* WAN port */ 202 genphy_config_aneg(phydev); 203 204 return 0; 205} 206 207static int ip101a_g_ack_interrupt(struct phy_device *phydev) 208{ 209 int err = phy_read(phydev, IP101A_G_IRQ_CONF_STATUS); 210 if (err < 0) 211 return err; 212 213 return 0; 214} 215 216static struct phy_driver icplus_driver[] = { 217{ 218 .phy_id = 0x02430d80, 219 .name = "ICPlus IP175C", 220 .phy_id_mask = 0x0ffffff0, 221 .features = PHY_BASIC_FEATURES, 222 .config_init = &ip175c_config_init, 223 .config_aneg = &ip175c_config_aneg, 224 .read_status = &ip175c_read_status, 225 .suspend = genphy_suspend, 226 .resume = genphy_resume, 227 .driver = { .owner = THIS_MODULE,}, 228}, { 229 .phy_id = 0x02430d90, 230 .name = "ICPlus IP1001", 231 .phy_id_mask = 0x0ffffff0, 232 .features = PHY_GBIT_FEATURES | SUPPORTED_Pause | 233 SUPPORTED_Asym_Pause, 234 .config_init = &ip1001_config_init, 235 .config_aneg = &genphy_config_aneg, 236 .read_status = &genphy_read_status, 237 .suspend = genphy_suspend, 238 .resume = genphy_resume, 239 .driver = { .owner = THIS_MODULE,}, 240}, { 241 .phy_id = 0x02430c54, 242 .name = "ICPlus IP101A/G", 243 .phy_id_mask = 0x0ffffff0, 244 .features = PHY_BASIC_FEATURES | SUPPORTED_Pause | 245 SUPPORTED_Asym_Pause, 246 .flags = PHY_HAS_INTERRUPT, 247 .ack_interrupt = ip101a_g_ack_interrupt, 248 .config_init = &ip101a_g_config_init, 249 .config_aneg = &genphy_config_aneg, 250 .read_status = &genphy_read_status, 251 .suspend = genphy_suspend, 252 .resume = genphy_resume, 253 .driver = { .owner = THIS_MODULE,}, 254} }; 255 256module_phy_driver(icplus_driver); 257 258static struct mdio_device_id __maybe_unused icplus_tbl[] = { 259 { 0x02430d80, 0x0ffffff0 }, 260 { 0x02430d90, 0x0ffffff0 }, 261 { 0x02430c54, 0x0ffffff0 }, 262 { } 263}; 264 265MODULE_DEVICE_TABLE(mdio, icplus_tbl); 266