155 lines · c
1// SPDX-License-Identifier: GPL-2.0+2/*3 * drivers/net/phy/cicada.c4 *5 * Driver for Cicada PHYs6 *7 * Author: Andy Fleming8 *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/* Cicada Extended Control Register 1 */33#define MII_CIS8201_EXT_CON1 0x1734#define MII_CIS8201_EXTCON1_INIT 0x000035 36/* Cicada Interrupt Mask Register */37#define MII_CIS8201_IMASK 0x1938#define MII_CIS8201_IMASK_IEN 0x800039#define MII_CIS8201_IMASK_SPEED 0x400040#define MII_CIS8201_IMASK_LINK 0x200041#define MII_CIS8201_IMASK_DUPLEX 0x100042#define MII_CIS8201_IMASK_MASK 0xf00043 44/* Cicada Interrupt Status Register */45#define MII_CIS8201_ISTAT 0x1a46#define MII_CIS8201_ISTAT_STATUS 0x800047#define MII_CIS8201_ISTAT_SPEED 0x400048#define MII_CIS8201_ISTAT_LINK 0x200049#define MII_CIS8201_ISTAT_DUPLEX 0x100050 51/* Cicada Auxiliary Control/Status Register */52#define MII_CIS8201_AUX_CONSTAT 0x1c53#define MII_CIS8201_AUXCONSTAT_INIT 0x000454#define MII_CIS8201_AUXCONSTAT_DUPLEX 0x002055#define MII_CIS8201_AUXCONSTAT_SPEED 0x001856#define MII_CIS8201_AUXCONSTAT_GBIT 0x001057#define MII_CIS8201_AUXCONSTAT_100 0x000858 59MODULE_DESCRIPTION("Cicadia PHY driver");60MODULE_AUTHOR("Andy Fleming");61MODULE_LICENSE("GPL");62 63static 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 79static 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 86static 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 108static 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/* Cicada 8201, a.k.a Vitesse VSC8201 */127static struct phy_driver cis820x_driver[] = {128{129 .phy_id = 0x000fc410,130 .name = "Cicada Cis8201",131 .phy_id_mask = 0x000ffff0,132 /* PHY_GBIT_FEATURES */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 /* PHY_GBIT_FEATURES */141 .config_init = &cis820x_config_init,142 .config_intr = &cis820x_config_intr,143 .handle_interrupt = &cis820x_handle_interrupt,144} };145 146module_phy_driver(cis820x_driver);147 148static struct mdio_device_id __maybe_unused cicada_tbl[] = {149 { 0x000fc410, 0x000ffff0 },150 { 0x000fc440, 0x000fffc0 },151 { }152};153 154MODULE_DEVICE_TABLE(mdio, cicada_tbl);155