1 // SPDX-License-Identifier: GPL-2.0+ 2 /* 3 * drivers/net/phy/davicom.c 4 * 5 * Driver for Davicom PHYs 6 * 7 * Author: Andy Fleming 8 * 9 * Copyright (c) 2004 Freescale Semiconductor, Inc. 10 */ 11 #include <linux/kernel.h> 12 #include <linux/string.h> 13 #include <linux/errno.h> 14 #include <linux/unistd.h> 15 #include <linux/interrupt.h> 16 #include <linux/init.h> 17 #include <linux/delay.h> 18 #include <linux/netdevice.h> 19 #include <linux/etherdevice.h> 20 #include <linux/skbuff.h> 21 #include <linux/spinlock.h> 22 #include <linux/mm.h> 23 #include <linux/module.h> 24 #include <linux/mii.h> 25 #include <linux/ethtool.h> 26 #include <linux/phy.h> 27 28 #include <asm/io.h> 29 #include <asm/irq.h> 30 #include <linux/uaccess.h> 31 32 #define MII_DM9161_SCR 0x10 33 #define MII_DM9161_SCR_INIT 0x0610 34 #define MII_DM9161_SCR_RMII 0x0100 35 36 /* DM9161 Interrupt Register */ 37 #define MII_DM9161_INTR 0x15 38 #define MII_DM9161_INTR_PEND 0x8000 39 #define MII_DM9161_INTR_DPLX_MASK 0x0800 40 #define MII_DM9161_INTR_SPD_MASK 0x0400 41 #define MII_DM9161_INTR_LINK_MASK 0x0200 42 #define MII_DM9161_INTR_MASK 0x0100 43 #define MII_DM9161_INTR_DPLX_CHANGE 0x0010 44 #define MII_DM9161_INTR_SPD_CHANGE 0x0008 45 #define MII_DM9161_INTR_LINK_CHANGE 0x0004 46 #define MII_DM9161_INTR_INIT 0x0000 47 #define MII_DM9161_INTR_STOP \ 48 (MII_DM9161_INTR_DPLX_MASK | MII_DM9161_INTR_SPD_MASK \ 49 | MII_DM9161_INTR_LINK_MASK | MII_DM9161_INTR_MASK) 50 #define MII_DM9161_INTR_CHANGE \ 51 (MII_DM9161_INTR_DPLX_CHANGE | \ 52 MII_DM9161_INTR_SPD_CHANGE | \ 53 MII_DM9161_INTR_LINK_CHANGE) 54 55 /* DM9161 10BT Configuration/Status */ 56 #define MII_DM9161_10BTCSR 0x12 57 #define MII_DM9161_10BTCSR_INIT 0x7800 58 59 MODULE_DESCRIPTION("Davicom PHY driver"); 60 MODULE_AUTHOR("Andy Fleming"); 61 MODULE_LICENSE("GPL"); 62 63 64 static int dm9161_ack_interrupt(struct phy_device *phydev) 65 { 66 int err = phy_read(phydev, MII_DM9161_INTR); 67 68 return (err < 0) ? err : 0; 69 } 70 71 #define DM9161_DELAY 1 72 static int dm9161_config_intr(struct phy_device *phydev) 73 { 74 int temp, err; 75 76 temp = phy_read(phydev, MII_DM9161_INTR); 77 78 if (temp < 0) 79 return temp; 80 81 if (phydev->interrupts == PHY_INTERRUPT_ENABLED) { 82 err = dm9161_ack_interrupt(phydev); 83 if (err) 84 return err; 85 86 temp &= ~(MII_DM9161_INTR_STOP); 87 err = phy_write(phydev, MII_DM9161_INTR, temp); 88 } else { 89 temp |= MII_DM9161_INTR_STOP; 90 err = phy_write(phydev, MII_DM9161_INTR, temp); 91 if (err) 92 return err; 93 94 err = dm9161_ack_interrupt(phydev); 95 } 96 97 return err; 98 } 99 100 static irqreturn_t dm9161_handle_interrupt(struct phy_device *phydev) 101 { 102 int irq_status; 103 104 irq_status = phy_read(phydev, MII_DM9161_INTR); 105 if (irq_status < 0) { 106 phy_error(phydev); 107 return IRQ_NONE; 108 } 109 110 if (!(irq_status & MII_DM9161_INTR_CHANGE)) 111 return IRQ_NONE; 112 113 phy_trigger_machine(phydev); 114 115 return IRQ_HANDLED; 116 } 117 118 static int dm9161_config_aneg(struct phy_device *phydev) 119 { 120 int err; 121 122 /* Isolate the PHY */ 123 err = phy_write(phydev, MII_BMCR, BMCR_ISOLATE); 124 125 if (err < 0) 126 return err; 127 128 /* Configure the new settings */ 129 err = genphy_config_aneg(phydev); 130 131 if (err < 0) 132 return err; 133 134 return 0; 135 } 136 137 static int dm9161_config_init(struct phy_device *phydev) 138 { 139 int err, temp; 140 141 /* Isolate the PHY */ 142 err = phy_write(phydev, MII_BMCR, BMCR_ISOLATE); 143 144 if (err < 0) 145 return err; 146 147 switch (phydev->interface) { 148 case PHY_INTERFACE_MODE_MII: 149 temp = MII_DM9161_SCR_INIT; 150 break; 151 case PHY_INTERFACE_MODE_RMII: 152 temp = MII_DM9161_SCR_INIT | MII_DM9161_SCR_RMII; 153 break; 154 default: 155 return -EINVAL; 156 } 157 158 /* Do not bypass the scrambler/descrambler */ 159 err = phy_write(phydev, MII_DM9161_SCR, temp); 160 if (err < 0) 161 return err; 162 163 /* Clear 10BTCSR to default */ 164 err = phy_write(phydev, MII_DM9161_10BTCSR, MII_DM9161_10BTCSR_INIT); 165 166 if (err < 0) 167 return err; 168 169 /* Reconnect the PHY, and enable Autonegotiation */ 170 return phy_write(phydev, MII_BMCR, BMCR_ANENABLE); 171 } 172 173 static struct phy_driver dm91xx_driver[] = { 174 { 175 .phy_id = 0x0181b880, 176 .name = "Davicom DM9161E", 177 .phy_id_mask = 0x0ffffff0, 178 /* PHY_BASIC_FEATURES */ 179 .config_init = dm9161_config_init, 180 .config_aneg = dm9161_config_aneg, 181 .config_intr = dm9161_config_intr, 182 .handle_interrupt = dm9161_handle_interrupt, 183 }, { 184 .phy_id = 0x0181b8b0, 185 .name = "Davicom DM9161B/C", 186 .phy_id_mask = 0x0ffffff0, 187 /* PHY_BASIC_FEATURES */ 188 .config_init = dm9161_config_init, 189 .config_aneg = dm9161_config_aneg, 190 .config_intr = dm9161_config_intr, 191 .handle_interrupt = dm9161_handle_interrupt, 192 }, { 193 .phy_id = 0x0181b8a0, 194 .name = "Davicom DM9161A", 195 .phy_id_mask = 0x0ffffff0, 196 /* PHY_BASIC_FEATURES */ 197 .config_init = dm9161_config_init, 198 .config_aneg = dm9161_config_aneg, 199 .config_intr = dm9161_config_intr, 200 .handle_interrupt = dm9161_handle_interrupt, 201 }, { 202 .phy_id = 0x00181b80, 203 .name = "Davicom DM9131", 204 .phy_id_mask = 0x0ffffff0, 205 /* PHY_BASIC_FEATURES */ 206 .config_intr = dm9161_config_intr, 207 .handle_interrupt = dm9161_handle_interrupt, 208 } }; 209 210 module_phy_driver(dm91xx_driver); 211 212 static struct mdio_device_id __maybe_unused davicom_tbl[] = { 213 { 0x0181b880, 0x0ffffff0 }, 214 { 0x0181b8b0, 0x0ffffff0 }, 215 { 0x0181b8a0, 0x0ffffff0 }, 216 { 0x00181b80, 0x0ffffff0 }, 217 { } 218 }; 219 220 MODULE_DEVICE_TABLE(mdio, davicom_tbl); 221