cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

davicom.c (5052B)


      1// SPDX-License-Identifier: GPL-2.0+
      2/*
      3 * drivers/net/phy/davicom.c
      4 *
      5 * Driver for Davicom 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 <asm/io.h>
     29#include <asm/irq.h>
     30#include <linux/uaccess.h>
     31
     32#define MII_DM9161_SCR		0x10
     33#define MII_DM9161_SCR_INIT	0x0610
     34#define MII_DM9161_SCR_RMII	0x0100
     35
     36/* DM9161 Interrupt Register */
     37#define MII_DM9161_INTR	0x15
     38#define MII_DM9161_INTR_PEND		0x8000
     39#define MII_DM9161_INTR_DPLX_MASK	0x0800
     40#define MII_DM9161_INTR_SPD_MASK	0x0400
     41#define MII_DM9161_INTR_LINK_MASK	0x0200
     42#define MII_DM9161_INTR_MASK		0x0100
     43#define MII_DM9161_INTR_DPLX_CHANGE	0x0010
     44#define MII_DM9161_INTR_SPD_CHANGE	0x0008
     45#define MII_DM9161_INTR_LINK_CHANGE	0x0004
     46#define MII_DM9161_INTR_INIT		0x0000
     47#define MII_DM9161_INTR_STOP	\
     48	(MII_DM9161_INTR_DPLX_MASK | MII_DM9161_INTR_SPD_MASK |	\
     49	 MII_DM9161_INTR_LINK_MASK | MII_DM9161_INTR_MASK)
     50#define MII_DM9161_INTR_CHANGE	\
     51	(MII_DM9161_INTR_DPLX_CHANGE | \
     52	 MII_DM9161_INTR_SPD_CHANGE | \
     53	 MII_DM9161_INTR_LINK_CHANGE)
     54
     55/* DM9161 10BT Configuration/Status */
     56#define MII_DM9161_10BTCSR	0x12
     57#define MII_DM9161_10BTCSR_INIT	0x7800
     58
     59MODULE_DESCRIPTION("Davicom PHY driver");
     60MODULE_AUTHOR("Andy Fleming");
     61MODULE_LICENSE("GPL");
     62
     63
     64static int dm9161_ack_interrupt(struct phy_device *phydev)
     65{
     66	int err = phy_read(phydev, MII_DM9161_INTR);
     67
     68	return (err < 0) ? err : 0;
     69}
     70
     71#define DM9161_DELAY 1
     72static int dm9161_config_intr(struct phy_device *phydev)
     73{
     74	int temp, err;
     75
     76	temp = phy_read(phydev, MII_DM9161_INTR);
     77
     78	if (temp < 0)
     79		return temp;
     80
     81	if (phydev->interrupts == PHY_INTERRUPT_ENABLED) {
     82		err = dm9161_ack_interrupt(phydev);
     83		if (err)
     84			return err;
     85
     86		temp &= ~(MII_DM9161_INTR_STOP);
     87		err = phy_write(phydev, MII_DM9161_INTR, temp);
     88	} else {
     89		temp |= MII_DM9161_INTR_STOP;
     90		err = phy_write(phydev, MII_DM9161_INTR, temp);
     91		if (err)
     92			return err;
     93
     94		err = dm9161_ack_interrupt(phydev);
     95	}
     96
     97	return err;
     98}
     99
    100static irqreturn_t dm9161_handle_interrupt(struct phy_device *phydev)
    101{
    102	int irq_status;
    103
    104	irq_status = phy_read(phydev, MII_DM9161_INTR);
    105	if (irq_status < 0) {
    106		phy_error(phydev);
    107		return IRQ_NONE;
    108	}
    109
    110	if (!(irq_status & MII_DM9161_INTR_CHANGE))
    111		return IRQ_NONE;
    112
    113	phy_trigger_machine(phydev);
    114
    115	return IRQ_HANDLED;
    116}
    117
    118static int dm9161_config_aneg(struct phy_device *phydev)
    119{
    120	int err;
    121
    122	/* Isolate the PHY */
    123	err = phy_write(phydev, MII_BMCR, BMCR_ISOLATE);
    124
    125	if (err < 0)
    126		return err;
    127
    128	/* Configure the new settings */
    129	err = genphy_config_aneg(phydev);
    130
    131	if (err < 0)
    132		return err;
    133
    134	return 0;
    135}
    136
    137static int dm9161_config_init(struct phy_device *phydev)
    138{
    139	int err, temp;
    140
    141	/* Isolate the PHY */
    142	err = phy_write(phydev, MII_BMCR, BMCR_ISOLATE);
    143
    144	if (err < 0)
    145		return err;
    146
    147	switch (phydev->interface) {
    148	case PHY_INTERFACE_MODE_MII:
    149		temp = MII_DM9161_SCR_INIT;
    150		break;
    151	case PHY_INTERFACE_MODE_RMII:
    152		temp =  MII_DM9161_SCR_INIT | MII_DM9161_SCR_RMII;
    153		break;
    154	default:
    155		return -EINVAL;
    156	}
    157
    158	/* Do not bypass the scrambler/descrambler */
    159	err = phy_write(phydev, MII_DM9161_SCR, temp);
    160	if (err < 0)
    161		return err;
    162
    163	/* Clear 10BTCSR to default */
    164	err = phy_write(phydev, MII_DM9161_10BTCSR, MII_DM9161_10BTCSR_INIT);
    165
    166	if (err < 0)
    167		return err;
    168
    169	/* Reconnect the PHY, and enable Autonegotiation */
    170	return phy_write(phydev, MII_BMCR, BMCR_ANENABLE);
    171}
    172
    173static struct phy_driver dm91xx_driver[] = {
    174{
    175	.phy_id		= 0x0181b880,
    176	.name		= "Davicom DM9161E",
    177	.phy_id_mask	= 0x0ffffff0,
    178	/* PHY_BASIC_FEATURES */
    179	.config_init	= dm9161_config_init,
    180	.config_aneg	= dm9161_config_aneg,
    181	.config_intr	= dm9161_config_intr,
    182	.handle_interrupt = dm9161_handle_interrupt,
    183}, {
    184	.phy_id		= 0x0181b8b0,
    185	.name		= "Davicom DM9161B/C",
    186	.phy_id_mask	= 0x0ffffff0,
    187	/* PHY_BASIC_FEATURES */
    188	.config_init	= dm9161_config_init,
    189	.config_aneg	= dm9161_config_aneg,
    190	.config_intr	= dm9161_config_intr,
    191	.handle_interrupt = dm9161_handle_interrupt,
    192}, {
    193	.phy_id		= 0x0181b8a0,
    194	.name		= "Davicom DM9161A",
    195	.phy_id_mask	= 0x0ffffff0,
    196	/* PHY_BASIC_FEATURES */
    197	.config_init	= dm9161_config_init,
    198	.config_aneg	= dm9161_config_aneg,
    199	.config_intr	= dm9161_config_intr,
    200	.handle_interrupt = dm9161_handle_interrupt,
    201}, {
    202	.phy_id		= 0x00181b80,
    203	.name		= "Davicom DM9131",
    204	.phy_id_mask	= 0x0ffffff0,
    205	/* PHY_BASIC_FEATURES */
    206	.config_intr	= dm9161_config_intr,
    207	.handle_interrupt = dm9161_handle_interrupt,
    208} };
    209
    210module_phy_driver(dm91xx_driver);
    211
    212static struct mdio_device_id __maybe_unused davicom_tbl[] = {
    213	{ 0x0181b880, 0x0ffffff0 },
    214	{ 0x0181b8b0, 0x0ffffff0 },
    215	{ 0x0181b8a0, 0x0ffffff0 },
    216	{ 0x00181b80, 0x0ffffff0 },
    217	{ }
    218};
    219
    220MODULE_DEVICE_TABLE(mdio, davicom_tbl);