| 1 | |
| 2 | |
| 3 | * drivers/net/phy/cicada.c
|
| 4 | *
|
| 5 | * Driver for Cicada 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 <linux/io.h>
|
| 29 | #include <asm/irq.h>
|
| 30 | #include <linux/uaccess.h>
|
| 31 |
|
| 32 |
|
| 33 | #define MII_CIS8201_EXT_CON1 0x17
|
| 34 | #define MII_CIS8201_EXTCON1_INIT 0x0000
|
| 35 |
|
| 36 |
|
| 37 | #define MII_CIS8201_IMASK 0x19
|
| 38 | #define MII_CIS8201_IMASK_IEN 0x8000
|
| 39 | #define MII_CIS8201_IMASK_SPEED 0x4000
|
| 40 | #define MII_CIS8201_IMASK_LINK 0x2000
|
| 41 | #define MII_CIS8201_IMASK_DUPLEX 0x1000
|
| 42 | #define MII_CIS8201_IMASK_MASK 0xf000
|
| 43 |
|
| 44 |
|
| 45 | #define MII_CIS8201_ISTAT 0x1a
|
| 46 | #define MII_CIS8201_ISTAT_STATUS 0x8000
|
| 47 | #define MII_CIS8201_ISTAT_SPEED 0x4000
|
| 48 | #define MII_CIS8201_ISTAT_LINK 0x2000
|
| 49 | #define MII_CIS8201_ISTAT_DUPLEX 0x1000
|
| 50 |
|
| 51 |
|
| 52 | #define MII_CIS8201_AUX_CONSTAT 0x1c
|
| 53 | #define MII_CIS8201_AUXCONSTAT_INIT 0x0004
|
| 54 | #define MII_CIS8201_AUXCONSTAT_DUPLEX 0x0020
|
| 55 | #define MII_CIS8201_AUXCONSTAT_SPEED 0x0018
|
| 56 | #define MII_CIS8201_AUXCONSTAT_GBIT 0x0010
|
| 57 | #define MII_CIS8201_AUXCONSTAT_100 0x0008
|
| 58 |
|
| 59 | MODULE_DESCRIPTION("Cicadia PHY driver");
|
| 60 | MODULE_AUTHOR("Andy Fleming");
|
| 61 | MODULE_LICENSE("GPL");
|
| 62 |
|
| 63 | static int cis820x_config_init(struct phy_device *phydev)
|
| 64 | {
|
| 65 | int err;
|
| 66 |
|
| 67 | err = phy_write(phydev, MII_CIS8201_AUX_CONSTAT,
|
| 68 | MII_CIS8201_AUXCONSTAT_INIT);
|
| 69 |
|
| 70 | if (err < 0)
|
| 71 | return err;
|
| 72 |
|
| 73 | err = phy_write(phydev, MII_CIS8201_EXT_CON1,
|
| 74 | MII_CIS8201_EXTCON1_INIT);
|
| 75 |
|
| 76 | return err;
|
| 77 | }
|
| 78 |
|
| 79 | static int cis820x_ack_interrupt(struct phy_device *phydev)
|
| 80 | {
|
| 81 | int err = phy_read(phydev, MII_CIS8201_ISTAT);
|
| 82 |
|
| 83 | return (err < 0) ? err : 0;
|
| 84 | }
|
| 85 |
|
| 86 | static int cis820x_config_intr(struct phy_device *phydev)
|
| 87 | {
|
| 88 | int err;
|
| 89 |
|
| 90 | if (phydev->interrupts == PHY_INTERRUPT_ENABLED) {
|
| 91 | err = cis820x_ack_interrupt(phydev);
|
| 92 | if (err)
|
| 93 | return err;
|
| 94 |
|
| 95 | err = phy_write(phydev, MII_CIS8201_IMASK,
|
| 96 | MII_CIS8201_IMASK_MASK);
|
| 97 | } else {
|
| 98 | err = phy_write(phydev, MII_CIS8201_IMASK, 0);
|
| 99 | if (err)
|
| 100 | return err;
|
| 101 |
|
| 102 | err = cis820x_ack_interrupt(phydev);
|
| 103 | }
|
| 104 |
|
| 105 | return err;
|
| 106 | }
|
| 107 |
|
| 108 | static irqreturn_t cis820x_handle_interrupt(struct phy_device *phydev)
|
| 109 | {
|
| 110 | int irq_status;
|
| 111 |
|
| 112 | irq_status = phy_read(phydev, MII_CIS8201_ISTAT);
|
| 113 | if (irq_status < 0) {
|
| 114 | phy_error(phydev);
|
| 115 | return IRQ_NONE;
|
| 116 | }
|
| 117 |
|
| 118 | if (!(irq_status & MII_CIS8201_IMASK_MASK))
|
| 119 | return IRQ_NONE;
|
| 120 |
|
| 121 | phy_trigger_machine(phydev);
|
| 122 |
|
| 123 | return IRQ_HANDLED;
|
| 124 | }
|
| 125 |
|
| 126 |
|
| 127 | static struct phy_driver cis820x_driver[] = {
|
| 128 | {
|
| 129 | .phy_id = 0x000fc410,
|
| 130 | .name = "Cicada Cis8201",
|
| 131 | .phy_id_mask = 0x000ffff0,
|
| 132 |
|
| 133 | .config_init = &cis820x_config_init,
|
| 134 | .config_intr = &cis820x_config_intr,
|
| 135 | .handle_interrupt = &cis820x_handle_interrupt,
|
| 136 | }, {
|
| 137 | .phy_id = 0x000fc440,
|
| 138 | .name = "Cicada Cis8204",
|
| 139 | .phy_id_mask = 0x000fffc0,
|
| 140 |
|
| 141 | .config_init = &cis820x_config_init,
|
| 142 | .config_intr = &cis820x_config_intr,
|
| 143 | .handle_interrupt = &cis820x_handle_interrupt,
|
| 144 | } };
|
| 145 |
|
| 146 | module_phy_driver(cis820x_driver);
|
| 147 |
|
| 148 | static const struct mdio_device_id __maybe_unused cicada_tbl[] = {
|
| 149 | { 0x000fc410, 0x000ffff0 },
|
| 150 | { 0x000fc440, 0x000fffc0 },
|
| 151 | { }
|
| 152 | };
|
| 153 |
|
| 154 | MODULE_DEVICE_TABLE(mdio, cicada_tbl);
|