182 lines · c
1// SPDX-License-Identifier: GPL-2.0+2/*3 * drivers/net/phy/national.c4 *5 * Driver for National Semiconductor PHYs6 *7 * Author: Stuart Menefy <stuart.menefy@st.com>8 * Maintainer: Giuseppe Cavallaro <peppe.cavallaro@st.com>9 *10 * Copyright (c) 2008 STMicroelectronics Limited11 */12 13#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt14 15#include <linux/kernel.h>16#include <linux/module.h>17#include <linux/mii.h>18#include <linux/ethtool.h>19#include <linux/phy.h>20#include <linux/netdevice.h>21 22/* DP83865 phy identifier values */23#define DP83865_PHY_ID 0x20005c7a24 25#define DP83865_INT_STATUS 0x1426#define DP83865_INT_MASK 0x1527#define DP83865_INT_CLEAR 0x1728 29#define DP83865_INT_REMOTE_FAULT 0x000830#define DP83865_INT_ANE_COMPLETED 0x001031#define DP83865_INT_LINK_CHANGE 0xe00032#define DP83865_INT_MASK_DEFAULT (DP83865_INT_REMOTE_FAULT | \33 DP83865_INT_ANE_COMPLETED | \34 DP83865_INT_LINK_CHANGE)35 36/* Advanced proprietary configuration */37#define NS_EXP_MEM_CTL 0x1638#define NS_EXP_MEM_DATA 0x1d39#define NS_EXP_MEM_ADD 0x1e40 41#define LED_CTRL_REG 0x1342#define AN_FALLBACK_AN 0x000143#define AN_FALLBACK_CRC 0x000244#define AN_FALLBACK_IE 0x000445#define ALL_FALLBACK_ON (AN_FALLBACK_AN | AN_FALLBACK_CRC | AN_FALLBACK_IE)46 47enum hdx_loopback {48 hdx_loopback_on = 0,49 hdx_loopback_off = 1,50};51 52static u8 ns_exp_read(struct phy_device *phydev, u16 reg)53{54 phy_write(phydev, NS_EXP_MEM_ADD, reg);55 return phy_read(phydev, NS_EXP_MEM_DATA);56}57 58static void ns_exp_write(struct phy_device *phydev, u16 reg, u8 data)59{60 phy_write(phydev, NS_EXP_MEM_ADD, reg);61 phy_write(phydev, NS_EXP_MEM_DATA, data);62}63 64static int ns_ack_interrupt(struct phy_device *phydev)65{66 int ret = phy_read(phydev, DP83865_INT_STATUS);67 if (ret < 0)68 return ret;69 70 /* Clear the interrupt status bit by writing a “1”71 * to the corresponding bit in INT_CLEAR (2:0 are reserved)72 */73 ret = phy_write(phydev, DP83865_INT_CLEAR, ret & ~0x7);74 75 return ret;76}77 78static irqreturn_t ns_handle_interrupt(struct phy_device *phydev)79{80 int irq_status;81 82 irq_status = phy_read(phydev, DP83865_INT_STATUS);83 if (irq_status < 0) {84 phy_error(phydev);85 return IRQ_NONE;86 }87 88 if (!(irq_status & DP83865_INT_MASK_DEFAULT))89 return IRQ_NONE;90 91 /* clear the interrupt */92 phy_write(phydev, DP83865_INT_CLEAR, irq_status & ~0x7);93 94 phy_trigger_machine(phydev);95 96 return IRQ_HANDLED;97}98 99static int ns_config_intr(struct phy_device *phydev)100{101 int err;102 103 if (phydev->interrupts == PHY_INTERRUPT_ENABLED) {104 err = ns_ack_interrupt(phydev);105 if (err)106 return err;107 108 err = phy_write(phydev, DP83865_INT_MASK,109 DP83865_INT_MASK_DEFAULT);110 } else {111 err = phy_write(phydev, DP83865_INT_MASK, 0);112 if (err)113 return err;114 115 err = ns_ack_interrupt(phydev);116 }117 118 return err;119}120 121static void ns_giga_speed_fallback(struct phy_device *phydev, int mode)122{123 int bmcr = phy_read(phydev, MII_BMCR);124 125 phy_write(phydev, MII_BMCR, (bmcr | BMCR_PDOWN));126 127 /* Enable 8 bit expended memory read/write (no auto increment) */128 phy_write(phydev, NS_EXP_MEM_CTL, 0);129 phy_write(phydev, NS_EXP_MEM_ADD, 0x1C0);130 phy_write(phydev, NS_EXP_MEM_DATA, 0x0008);131 phy_write(phydev, MII_BMCR, (bmcr & ~BMCR_PDOWN));132 phy_write(phydev, LED_CTRL_REG, mode);133}134 135static void ns_10_base_t_hdx_loopack(struct phy_device *phydev, int disable)136{137 u16 lb_dis = BIT(1);138 139 if (disable)140 ns_exp_write(phydev, 0x1c0,141 ns_exp_read(phydev, 0x1c0) | lb_dis);142 else143 ns_exp_write(phydev, 0x1c0,144 ns_exp_read(phydev, 0x1c0) & ~lb_dis);145 146 pr_debug("10BASE-T HDX loopback %s\n",147 (ns_exp_read(phydev, 0x1c0) & lb_dis) ? "off" : "on");148}149 150static int ns_config_init(struct phy_device *phydev)151{152 ns_giga_speed_fallback(phydev, ALL_FALLBACK_ON);153 /* In the latest MAC or switches design, the 10 Mbps loopback154 * is desired to be turned off.155 */156 ns_10_base_t_hdx_loopack(phydev, hdx_loopback_off);157 return ns_ack_interrupt(phydev);158}159 160static struct phy_driver dp83865_driver[] = { {161 .phy_id = DP83865_PHY_ID,162 .phy_id_mask = 0xfffffff0,163 .name = "NatSemi DP83865",164 /* PHY_GBIT_FEATURES */165 .config_init = ns_config_init,166 .config_intr = ns_config_intr,167 .handle_interrupt = ns_handle_interrupt,168} };169 170module_phy_driver(dp83865_driver);171 172MODULE_DESCRIPTION("NatSemi PHY driver");173MODULE_AUTHOR("Stuart Menefy");174MODULE_LICENSE("GPL");175 176static struct mdio_device_id __maybe_unused ns_tbl[] = {177 { DP83865_PHY_ID, 0xfffffff0 },178 { }179};180 181MODULE_DEVICE_TABLE(mdio, ns_tbl);182