162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * drivers/net/phy/cicada.c 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Driver for Cicada PHYs 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Author: Andy Fleming 862306a36Sopenharmony_ci * 962306a36Sopenharmony_ci * Copyright (c) 2004 Freescale Semiconductor, Inc. 1062306a36Sopenharmony_ci */ 1162306a36Sopenharmony_ci#include <linux/kernel.h> 1262306a36Sopenharmony_ci#include <linux/string.h> 1362306a36Sopenharmony_ci#include <linux/errno.h> 1462306a36Sopenharmony_ci#include <linux/unistd.h> 1562306a36Sopenharmony_ci#include <linux/interrupt.h> 1662306a36Sopenharmony_ci#include <linux/init.h> 1762306a36Sopenharmony_ci#include <linux/delay.h> 1862306a36Sopenharmony_ci#include <linux/netdevice.h> 1962306a36Sopenharmony_ci#include <linux/etherdevice.h> 2062306a36Sopenharmony_ci#include <linux/skbuff.h> 2162306a36Sopenharmony_ci#include <linux/spinlock.h> 2262306a36Sopenharmony_ci#include <linux/mm.h> 2362306a36Sopenharmony_ci#include <linux/module.h> 2462306a36Sopenharmony_ci#include <linux/mii.h> 2562306a36Sopenharmony_ci#include <linux/ethtool.h> 2662306a36Sopenharmony_ci#include <linux/phy.h> 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci#include <linux/io.h> 2962306a36Sopenharmony_ci#include <asm/irq.h> 3062306a36Sopenharmony_ci#include <linux/uaccess.h> 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci/* Cicada Extended Control Register 1 */ 3362306a36Sopenharmony_ci#define MII_CIS8201_EXT_CON1 0x17 3462306a36Sopenharmony_ci#define MII_CIS8201_EXTCON1_INIT 0x0000 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci/* Cicada Interrupt Mask Register */ 3762306a36Sopenharmony_ci#define MII_CIS8201_IMASK 0x19 3862306a36Sopenharmony_ci#define MII_CIS8201_IMASK_IEN 0x8000 3962306a36Sopenharmony_ci#define MII_CIS8201_IMASK_SPEED 0x4000 4062306a36Sopenharmony_ci#define MII_CIS8201_IMASK_LINK 0x2000 4162306a36Sopenharmony_ci#define MII_CIS8201_IMASK_DUPLEX 0x1000 4262306a36Sopenharmony_ci#define MII_CIS8201_IMASK_MASK 0xf000 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ci/* Cicada Interrupt Status Register */ 4562306a36Sopenharmony_ci#define MII_CIS8201_ISTAT 0x1a 4662306a36Sopenharmony_ci#define MII_CIS8201_ISTAT_STATUS 0x8000 4762306a36Sopenharmony_ci#define MII_CIS8201_ISTAT_SPEED 0x4000 4862306a36Sopenharmony_ci#define MII_CIS8201_ISTAT_LINK 0x2000 4962306a36Sopenharmony_ci#define MII_CIS8201_ISTAT_DUPLEX 0x1000 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ci/* Cicada Auxiliary Control/Status Register */ 5262306a36Sopenharmony_ci#define MII_CIS8201_AUX_CONSTAT 0x1c 5362306a36Sopenharmony_ci#define MII_CIS8201_AUXCONSTAT_INIT 0x0004 5462306a36Sopenharmony_ci#define MII_CIS8201_AUXCONSTAT_DUPLEX 0x0020 5562306a36Sopenharmony_ci#define MII_CIS8201_AUXCONSTAT_SPEED 0x0018 5662306a36Sopenharmony_ci#define MII_CIS8201_AUXCONSTAT_GBIT 0x0010 5762306a36Sopenharmony_ci#define MII_CIS8201_AUXCONSTAT_100 0x0008 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ciMODULE_DESCRIPTION("Cicadia PHY driver"); 6062306a36Sopenharmony_ciMODULE_AUTHOR("Andy Fleming"); 6162306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_cistatic int cis820x_config_init(struct phy_device *phydev) 6462306a36Sopenharmony_ci{ 6562306a36Sopenharmony_ci int err; 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci err = phy_write(phydev, MII_CIS8201_AUX_CONSTAT, 6862306a36Sopenharmony_ci MII_CIS8201_AUXCONSTAT_INIT); 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci if (err < 0) 7162306a36Sopenharmony_ci return err; 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci err = phy_write(phydev, MII_CIS8201_EXT_CON1, 7462306a36Sopenharmony_ci MII_CIS8201_EXTCON1_INIT); 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci return err; 7762306a36Sopenharmony_ci} 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_cistatic int cis820x_ack_interrupt(struct phy_device *phydev) 8062306a36Sopenharmony_ci{ 8162306a36Sopenharmony_ci int err = phy_read(phydev, MII_CIS8201_ISTAT); 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci return (err < 0) ? err : 0; 8462306a36Sopenharmony_ci} 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_cistatic int cis820x_config_intr(struct phy_device *phydev) 8762306a36Sopenharmony_ci{ 8862306a36Sopenharmony_ci int err; 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci if (phydev->interrupts == PHY_INTERRUPT_ENABLED) { 9162306a36Sopenharmony_ci err = cis820x_ack_interrupt(phydev); 9262306a36Sopenharmony_ci if (err) 9362306a36Sopenharmony_ci return err; 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci err = phy_write(phydev, MII_CIS8201_IMASK, 9662306a36Sopenharmony_ci MII_CIS8201_IMASK_MASK); 9762306a36Sopenharmony_ci } else { 9862306a36Sopenharmony_ci err = phy_write(phydev, MII_CIS8201_IMASK, 0); 9962306a36Sopenharmony_ci if (err) 10062306a36Sopenharmony_ci return err; 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci err = cis820x_ack_interrupt(phydev); 10362306a36Sopenharmony_ci } 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci return err; 10662306a36Sopenharmony_ci} 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_cistatic irqreturn_t cis820x_handle_interrupt(struct phy_device *phydev) 10962306a36Sopenharmony_ci{ 11062306a36Sopenharmony_ci int irq_status; 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci irq_status = phy_read(phydev, MII_CIS8201_ISTAT); 11362306a36Sopenharmony_ci if (irq_status < 0) { 11462306a36Sopenharmony_ci phy_error(phydev); 11562306a36Sopenharmony_ci return IRQ_NONE; 11662306a36Sopenharmony_ci } 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci if (!(irq_status & MII_CIS8201_IMASK_MASK)) 11962306a36Sopenharmony_ci return IRQ_NONE; 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_ci phy_trigger_machine(phydev); 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci return IRQ_HANDLED; 12462306a36Sopenharmony_ci} 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci/* Cicada 8201, a.k.a Vitesse VSC8201 */ 12762306a36Sopenharmony_cistatic struct phy_driver cis820x_driver[] = { 12862306a36Sopenharmony_ci{ 12962306a36Sopenharmony_ci .phy_id = 0x000fc410, 13062306a36Sopenharmony_ci .name = "Cicada Cis8201", 13162306a36Sopenharmony_ci .phy_id_mask = 0x000ffff0, 13262306a36Sopenharmony_ci /* PHY_GBIT_FEATURES */ 13362306a36Sopenharmony_ci .config_init = &cis820x_config_init, 13462306a36Sopenharmony_ci .config_intr = &cis820x_config_intr, 13562306a36Sopenharmony_ci .handle_interrupt = &cis820x_handle_interrupt, 13662306a36Sopenharmony_ci}, { 13762306a36Sopenharmony_ci .phy_id = 0x000fc440, 13862306a36Sopenharmony_ci .name = "Cicada Cis8204", 13962306a36Sopenharmony_ci .phy_id_mask = 0x000fffc0, 14062306a36Sopenharmony_ci /* PHY_GBIT_FEATURES */ 14162306a36Sopenharmony_ci .config_init = &cis820x_config_init, 14262306a36Sopenharmony_ci .config_intr = &cis820x_config_intr, 14362306a36Sopenharmony_ci .handle_interrupt = &cis820x_handle_interrupt, 14462306a36Sopenharmony_ci} }; 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_cimodule_phy_driver(cis820x_driver); 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_cistatic struct mdio_device_id __maybe_unused cicada_tbl[] = { 14962306a36Sopenharmony_ci { 0x000fc410, 0x000ffff0 }, 15062306a36Sopenharmony_ci { 0x000fc440, 0x000fffc0 }, 15162306a36Sopenharmony_ci { } 15262306a36Sopenharmony_ci}; 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ciMODULE_DEVICE_TABLE(mdio, cicada_tbl); 155