brintos

brintos / linux-shallow public Read only

0
0
Text · 4.9 KiB · 4ac4bce Raw
221 lines · c
1// SPDX-License-Identifier: GPL-2.0+2/*3 * drivers/net/phy/davicom.c4 *5 * Driver for Davicom 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 <asm/io.h>29#include <asm/irq.h>30#include <linux/uaccess.h>31 32#define MII_DM9161_SCR		0x1033#define MII_DM9161_SCR_INIT	0x061034#define MII_DM9161_SCR_RMII	0x010035 36/* DM9161 Interrupt Register */37#define MII_DM9161_INTR	0x1538#define MII_DM9161_INTR_PEND		0x800039#define MII_DM9161_INTR_DPLX_MASK	0x080040#define MII_DM9161_INTR_SPD_MASK	0x040041#define MII_DM9161_INTR_LINK_MASK	0x020042#define MII_DM9161_INTR_MASK		0x010043#define MII_DM9161_INTR_DPLX_CHANGE	0x001044#define MII_DM9161_INTR_SPD_CHANGE	0x000845#define MII_DM9161_INTR_LINK_CHANGE	0x000446#define MII_DM9161_INTR_INIT		0x000047#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	0x1257#define MII_DM9161_10BTCSR_INIT	0x780058 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 172static 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);221