0001
0002
0003
0004
0005
0006
0007
0008
0009
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 <linux/io.h>
0029 #include <asm/irq.h>
0030 #include <linux/uaccess.h>
0031
0032
0033 #define MII_CIS8201_EXT_CON1 0x17
0034 #define MII_CIS8201_EXTCON1_INIT 0x0000
0035
0036
0037 #define MII_CIS8201_IMASK 0x19
0038 #define MII_CIS8201_IMASK_IEN 0x8000
0039 #define MII_CIS8201_IMASK_SPEED 0x4000
0040 #define MII_CIS8201_IMASK_LINK 0x2000
0041 #define MII_CIS8201_IMASK_DUPLEX 0x1000
0042 #define MII_CIS8201_IMASK_MASK 0xf000
0043
0044
0045 #define MII_CIS8201_ISTAT 0x1a
0046 #define MII_CIS8201_ISTAT_STATUS 0x8000
0047 #define MII_CIS8201_ISTAT_SPEED 0x4000
0048 #define MII_CIS8201_ISTAT_LINK 0x2000
0049 #define MII_CIS8201_ISTAT_DUPLEX 0x1000
0050
0051
0052 #define MII_CIS8201_AUX_CONSTAT 0x1c
0053 #define MII_CIS8201_AUXCONSTAT_INIT 0x0004
0054 #define MII_CIS8201_AUXCONSTAT_DUPLEX 0x0020
0055 #define MII_CIS8201_AUXCONSTAT_SPEED 0x0018
0056 #define MII_CIS8201_AUXCONSTAT_GBIT 0x0010
0057 #define MII_CIS8201_AUXCONSTAT_100 0x0008
0058
0059 MODULE_DESCRIPTION("Cicadia PHY driver");
0060 MODULE_AUTHOR("Andy Fleming");
0061 MODULE_LICENSE("GPL");
0062
0063 static int cis820x_config_init(struct phy_device *phydev)
0064 {
0065 int err;
0066
0067 err = phy_write(phydev, MII_CIS8201_AUX_CONSTAT,
0068 MII_CIS8201_AUXCONSTAT_INIT);
0069
0070 if (err < 0)
0071 return err;
0072
0073 err = phy_write(phydev, MII_CIS8201_EXT_CON1,
0074 MII_CIS8201_EXTCON1_INIT);
0075
0076 return err;
0077 }
0078
0079 static int cis820x_ack_interrupt(struct phy_device *phydev)
0080 {
0081 int err = phy_read(phydev, MII_CIS8201_ISTAT);
0082
0083 return (err < 0) ? err : 0;
0084 }
0085
0086 static int cis820x_config_intr(struct phy_device *phydev)
0087 {
0088 int err;
0089
0090 if (phydev->interrupts == PHY_INTERRUPT_ENABLED) {
0091 err = cis820x_ack_interrupt(phydev);
0092 if (err)
0093 return err;
0094
0095 err = phy_write(phydev, MII_CIS8201_IMASK,
0096 MII_CIS8201_IMASK_MASK);
0097 } else {
0098 err = phy_write(phydev, MII_CIS8201_IMASK, 0);
0099 if (err)
0100 return err;
0101
0102 err = cis820x_ack_interrupt(phydev);
0103 }
0104
0105 return err;
0106 }
0107
0108 static irqreturn_t cis820x_handle_interrupt(struct phy_device *phydev)
0109 {
0110 int irq_status;
0111
0112 irq_status = phy_read(phydev, MII_CIS8201_ISTAT);
0113 if (irq_status < 0) {
0114 phy_error(phydev);
0115 return IRQ_NONE;
0116 }
0117
0118 if (!(irq_status & MII_CIS8201_IMASK_MASK))
0119 return IRQ_NONE;
0120
0121 phy_trigger_machine(phydev);
0122
0123 return IRQ_HANDLED;
0124 }
0125
0126
0127 static struct phy_driver cis820x_driver[] = {
0128 {
0129 .phy_id = 0x000fc410,
0130 .name = "Cicada Cis8201",
0131 .phy_id_mask = 0x000ffff0,
0132
0133 .config_init = &cis820x_config_init,
0134 .config_intr = &cis820x_config_intr,
0135 .handle_interrupt = &cis820x_handle_interrupt,
0136 }, {
0137 .phy_id = 0x000fc440,
0138 .name = "Cicada Cis8204",
0139 .phy_id_mask = 0x000fffc0,
0140
0141 .config_init = &cis820x_config_init,
0142 .config_intr = &cis820x_config_intr,
0143 .handle_interrupt = &cis820x_handle_interrupt,
0144 } };
0145
0146 module_phy_driver(cis820x_driver);
0147
0148 static struct mdio_device_id __maybe_unused cicada_tbl[] = {
0149 { 0x000fc410, 0x000ffff0 },
0150 { 0x000fc440, 0x000fffc0 },
0151 { }
0152 };
0153
0154 MODULE_DEVICE_TABLE(mdio, cicada_tbl);