Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0+
0002 /*
0003  * drivers/net/phy/davicom.c
0004  *
0005  * Driver for Davicom PHYs
0006  *
0007  * Author: Andy Fleming
0008  *
0009  * Copyright (c) 2004 Freescale Semiconductor, Inc.
0010  */
0011 #include <linux/kernel.h>
0012 #include <linux/string.h>
0013 #include <linux/errno.h>
0014 #include <linux/unistd.h>
0015 #include <linux/interrupt.h>
0016 #include <linux/init.h>
0017 #include <linux/delay.h>
0018 #include <linux/netdevice.h>
0019 #include <linux/etherdevice.h>
0020 #include <linux/skbuff.h>
0021 #include <linux/spinlock.h>
0022 #include <linux/mm.h>
0023 #include <linux/module.h>
0024 #include <linux/mii.h>
0025 #include <linux/ethtool.h>
0026 #include <linux/phy.h>
0027 
0028 #include <asm/io.h>
0029 #include <asm/irq.h>
0030 #include <linux/uaccess.h>
0031 
0032 #define MII_DM9161_SCR      0x10
0033 #define MII_DM9161_SCR_INIT 0x0610
0034 #define MII_DM9161_SCR_RMII 0x0100
0035 
0036 /* DM9161 Interrupt Register */
0037 #define MII_DM9161_INTR 0x15
0038 #define MII_DM9161_INTR_PEND        0x8000
0039 #define MII_DM9161_INTR_DPLX_MASK   0x0800
0040 #define MII_DM9161_INTR_SPD_MASK    0x0400
0041 #define MII_DM9161_INTR_LINK_MASK   0x0200
0042 #define MII_DM9161_INTR_MASK        0x0100
0043 #define MII_DM9161_INTR_DPLX_CHANGE 0x0010
0044 #define MII_DM9161_INTR_SPD_CHANGE  0x0008
0045 #define MII_DM9161_INTR_LINK_CHANGE 0x0004
0046 #define MII_DM9161_INTR_INIT        0x0000
0047 #define MII_DM9161_INTR_STOP    \
0048     (MII_DM9161_INTR_DPLX_MASK | MII_DM9161_INTR_SPD_MASK | \
0049      MII_DM9161_INTR_LINK_MASK | MII_DM9161_INTR_MASK)
0050 #define MII_DM9161_INTR_CHANGE  \
0051     (MII_DM9161_INTR_DPLX_CHANGE | \
0052      MII_DM9161_INTR_SPD_CHANGE | \
0053      MII_DM9161_INTR_LINK_CHANGE)
0054 
0055 /* DM9161 10BT Configuration/Status */
0056 #define MII_DM9161_10BTCSR  0x12
0057 #define MII_DM9161_10BTCSR_INIT 0x7800
0058 
0059 MODULE_DESCRIPTION("Davicom PHY driver");
0060 MODULE_AUTHOR("Andy Fleming");
0061 MODULE_LICENSE("GPL");
0062 
0063 
0064 static int dm9161_ack_interrupt(struct phy_device *phydev)
0065 {
0066     int err = phy_read(phydev, MII_DM9161_INTR);
0067 
0068     return (err < 0) ? err : 0;
0069 }
0070 
0071 #define DM9161_DELAY 1
0072 static int dm9161_config_intr(struct phy_device *phydev)
0073 {
0074     int temp, err;
0075 
0076     temp = phy_read(phydev, MII_DM9161_INTR);
0077 
0078     if (temp < 0)
0079         return temp;
0080 
0081     if (phydev->interrupts == PHY_INTERRUPT_ENABLED) {
0082         err = dm9161_ack_interrupt(phydev);
0083         if (err)
0084             return err;
0085 
0086         temp &= ~(MII_DM9161_INTR_STOP);
0087         err = phy_write(phydev, MII_DM9161_INTR, temp);
0088     } else {
0089         temp |= MII_DM9161_INTR_STOP;
0090         err = phy_write(phydev, MII_DM9161_INTR, temp);
0091         if (err)
0092             return err;
0093 
0094         err = dm9161_ack_interrupt(phydev);
0095     }
0096 
0097     return err;
0098 }
0099 
0100 static irqreturn_t dm9161_handle_interrupt(struct phy_device *phydev)
0101 {
0102     int irq_status;
0103 
0104     irq_status = phy_read(phydev, MII_DM9161_INTR);
0105     if (irq_status < 0) {
0106         phy_error(phydev);
0107         return IRQ_NONE;
0108     }
0109 
0110     if (!(irq_status & MII_DM9161_INTR_CHANGE))
0111         return IRQ_NONE;
0112 
0113     phy_trigger_machine(phydev);
0114 
0115     return IRQ_HANDLED;
0116 }
0117 
0118 static int dm9161_config_aneg(struct phy_device *phydev)
0119 {
0120     int err;
0121 
0122     /* Isolate the PHY */
0123     err = phy_write(phydev, MII_BMCR, BMCR_ISOLATE);
0124 
0125     if (err < 0)
0126         return err;
0127 
0128     /* Configure the new settings */
0129     err = genphy_config_aneg(phydev);
0130 
0131     if (err < 0)
0132         return err;
0133 
0134     return 0;
0135 }
0136 
0137 static int dm9161_config_init(struct phy_device *phydev)
0138 {
0139     int err, temp;
0140 
0141     /* Isolate the PHY */
0142     err = phy_write(phydev, MII_BMCR, BMCR_ISOLATE);
0143 
0144     if (err < 0)
0145         return err;
0146 
0147     switch (phydev->interface) {
0148     case PHY_INTERFACE_MODE_MII:
0149         temp = MII_DM9161_SCR_INIT;
0150         break;
0151     case PHY_INTERFACE_MODE_RMII:
0152         temp =  MII_DM9161_SCR_INIT | MII_DM9161_SCR_RMII;
0153         break;
0154     default:
0155         return -EINVAL;
0156     }
0157 
0158     /* Do not bypass the scrambler/descrambler */
0159     err = phy_write(phydev, MII_DM9161_SCR, temp);
0160     if (err < 0)
0161         return err;
0162 
0163     /* Clear 10BTCSR to default */
0164     err = phy_write(phydev, MII_DM9161_10BTCSR, MII_DM9161_10BTCSR_INIT);
0165 
0166     if (err < 0)
0167         return err;
0168 
0169     /* Reconnect the PHY, and enable Autonegotiation */
0170     return phy_write(phydev, MII_BMCR, BMCR_ANENABLE);
0171 }
0172 
0173 static struct phy_driver dm91xx_driver[] = {
0174 {
0175     .phy_id     = 0x0181b880,
0176     .name       = "Davicom DM9161E",
0177     .phy_id_mask    = 0x0ffffff0,
0178     /* PHY_BASIC_FEATURES */
0179     .config_init    = dm9161_config_init,
0180     .config_aneg    = dm9161_config_aneg,
0181     .config_intr    = dm9161_config_intr,
0182     .handle_interrupt = dm9161_handle_interrupt,
0183 }, {
0184     .phy_id     = 0x0181b8b0,
0185     .name       = "Davicom DM9161B/C",
0186     .phy_id_mask    = 0x0ffffff0,
0187     /* PHY_BASIC_FEATURES */
0188     .config_init    = dm9161_config_init,
0189     .config_aneg    = dm9161_config_aneg,
0190     .config_intr    = dm9161_config_intr,
0191     .handle_interrupt = dm9161_handle_interrupt,
0192 }, {
0193     .phy_id     = 0x0181b8a0,
0194     .name       = "Davicom DM9161A",
0195     .phy_id_mask    = 0x0ffffff0,
0196     /* PHY_BASIC_FEATURES */
0197     .config_init    = dm9161_config_init,
0198     .config_aneg    = dm9161_config_aneg,
0199     .config_intr    = dm9161_config_intr,
0200     .handle_interrupt = dm9161_handle_interrupt,
0201 }, {
0202     .phy_id     = 0x00181b80,
0203     .name       = "Davicom DM9131",
0204     .phy_id_mask    = 0x0ffffff0,
0205     /* PHY_BASIC_FEATURES */
0206     .config_intr    = dm9161_config_intr,
0207     .handle_interrupt = dm9161_handle_interrupt,
0208 } };
0209 
0210 module_phy_driver(dm91xx_driver);
0211 
0212 static struct mdio_device_id __maybe_unused davicom_tbl[] = {
0213     { 0x0181b880, 0x0ffffff0 },
0214     { 0x0181b8b0, 0x0ffffff0 },
0215     { 0x0181b8a0, 0x0ffffff0 },
0216     { 0x00181b80, 0x0ffffff0 },
0217     { }
0218 };
0219 
0220 MODULE_DEVICE_TABLE(mdio, davicom_tbl);