18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Driver for the Texas Instruments DP83848 PHY
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (C) 2015-2016 Texas Instruments Incorporated - http://www.ti.com/
68c2ecf20Sopenharmony_ci */
78c2ecf20Sopenharmony_ci
88c2ecf20Sopenharmony_ci#include <linux/module.h>
98c2ecf20Sopenharmony_ci#include <linux/phy.h>
108c2ecf20Sopenharmony_ci
118c2ecf20Sopenharmony_ci#define TI_DP83848C_PHY_ID		0x20005ca0
128c2ecf20Sopenharmony_ci#define TI_DP83620_PHY_ID		0x20005ce0
138c2ecf20Sopenharmony_ci#define NS_DP83848C_PHY_ID		0x20005c90
148c2ecf20Sopenharmony_ci#define TLK10X_PHY_ID			0x2000a210
158c2ecf20Sopenharmony_ci
168c2ecf20Sopenharmony_ci/* Registers */
178c2ecf20Sopenharmony_ci#define DP83848_MICR			0x11 /* MII Interrupt Control Register */
188c2ecf20Sopenharmony_ci#define DP83848_MISR			0x12 /* MII Interrupt Status Register */
198c2ecf20Sopenharmony_ci
208c2ecf20Sopenharmony_ci/* MICR Register Fields */
218c2ecf20Sopenharmony_ci#define DP83848_MICR_INT_OE		BIT(0) /* Interrupt Output Enable */
228c2ecf20Sopenharmony_ci#define DP83848_MICR_INTEN		BIT(1) /* Interrupt Enable */
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_ci/* MISR Register Fields */
258c2ecf20Sopenharmony_ci#define DP83848_MISR_RHF_INT_EN		BIT(0) /* Receive Error Counter */
268c2ecf20Sopenharmony_ci#define DP83848_MISR_FHF_INT_EN		BIT(1) /* False Carrier Counter */
278c2ecf20Sopenharmony_ci#define DP83848_MISR_ANC_INT_EN		BIT(2) /* Auto-negotiation complete */
288c2ecf20Sopenharmony_ci#define DP83848_MISR_DUP_INT_EN		BIT(3) /* Duplex Status */
298c2ecf20Sopenharmony_ci#define DP83848_MISR_SPD_INT_EN		BIT(4) /* Speed status */
308c2ecf20Sopenharmony_ci#define DP83848_MISR_LINK_INT_EN	BIT(5) /* Link status */
318c2ecf20Sopenharmony_ci#define DP83848_MISR_ED_INT_EN		BIT(6) /* Energy detect */
328c2ecf20Sopenharmony_ci#define DP83848_MISR_LQM_INT_EN		BIT(7) /* Link Quality Monitor */
338c2ecf20Sopenharmony_ci
348c2ecf20Sopenharmony_ci#define DP83848_INT_EN_MASK		\
358c2ecf20Sopenharmony_ci	(DP83848_MISR_ANC_INT_EN |	\
368c2ecf20Sopenharmony_ci	 DP83848_MISR_DUP_INT_EN |	\
378c2ecf20Sopenharmony_ci	 DP83848_MISR_SPD_INT_EN |	\
388c2ecf20Sopenharmony_ci	 DP83848_MISR_LINK_INT_EN)
398c2ecf20Sopenharmony_ci
408c2ecf20Sopenharmony_cistatic int dp83848_ack_interrupt(struct phy_device *phydev)
418c2ecf20Sopenharmony_ci{
428c2ecf20Sopenharmony_ci	int err = phy_read(phydev, DP83848_MISR);
438c2ecf20Sopenharmony_ci
448c2ecf20Sopenharmony_ci	return err < 0 ? err : 0;
458c2ecf20Sopenharmony_ci}
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_cistatic int dp83848_config_intr(struct phy_device *phydev)
488c2ecf20Sopenharmony_ci{
498c2ecf20Sopenharmony_ci	int control, ret;
508c2ecf20Sopenharmony_ci
518c2ecf20Sopenharmony_ci	control = phy_read(phydev, DP83848_MICR);
528c2ecf20Sopenharmony_ci	if (control < 0)
538c2ecf20Sopenharmony_ci		return control;
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_ci	if (phydev->interrupts == PHY_INTERRUPT_ENABLED) {
568c2ecf20Sopenharmony_ci		control |= DP83848_MICR_INT_OE;
578c2ecf20Sopenharmony_ci		control |= DP83848_MICR_INTEN;
588c2ecf20Sopenharmony_ci
598c2ecf20Sopenharmony_ci		ret = phy_write(phydev, DP83848_MISR, DP83848_INT_EN_MASK);
608c2ecf20Sopenharmony_ci		if (ret < 0)
618c2ecf20Sopenharmony_ci			return ret;
628c2ecf20Sopenharmony_ci	} else {
638c2ecf20Sopenharmony_ci		control &= ~DP83848_MICR_INTEN;
648c2ecf20Sopenharmony_ci	}
658c2ecf20Sopenharmony_ci
668c2ecf20Sopenharmony_ci	return phy_write(phydev, DP83848_MICR, control);
678c2ecf20Sopenharmony_ci}
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_cistatic int dp83848_config_init(struct phy_device *phydev)
708c2ecf20Sopenharmony_ci{
718c2ecf20Sopenharmony_ci	int val;
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_ci	/* DP83620 always reports Auto Negotiation Ability on BMSR. Instead,
748c2ecf20Sopenharmony_ci	 * we check initial value of BMCR Auto negotiation enable bit
758c2ecf20Sopenharmony_ci	 */
768c2ecf20Sopenharmony_ci	val = phy_read(phydev, MII_BMCR);
778c2ecf20Sopenharmony_ci	if (!(val & BMCR_ANENABLE))
788c2ecf20Sopenharmony_ci		phydev->autoneg = AUTONEG_DISABLE;
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_ci	return 0;
818c2ecf20Sopenharmony_ci}
828c2ecf20Sopenharmony_ci
838c2ecf20Sopenharmony_cistatic struct mdio_device_id __maybe_unused dp83848_tbl[] = {
848c2ecf20Sopenharmony_ci	{ TI_DP83848C_PHY_ID, 0xfffffff0 },
858c2ecf20Sopenharmony_ci	{ NS_DP83848C_PHY_ID, 0xfffffff0 },
868c2ecf20Sopenharmony_ci	{ TI_DP83620_PHY_ID, 0xfffffff0 },
878c2ecf20Sopenharmony_ci	{ TLK10X_PHY_ID, 0xfffffff0 },
888c2ecf20Sopenharmony_ci	{ }
898c2ecf20Sopenharmony_ci};
908c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(mdio, dp83848_tbl);
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_ci#define DP83848_PHY_DRIVER(_id, _name, _config_init)		\
938c2ecf20Sopenharmony_ci	{							\
948c2ecf20Sopenharmony_ci		.phy_id		= _id,				\
958c2ecf20Sopenharmony_ci		.phy_id_mask	= 0xfffffff0,			\
968c2ecf20Sopenharmony_ci		.name		= _name,			\
978c2ecf20Sopenharmony_ci		/* PHY_BASIC_FEATURES */			\
988c2ecf20Sopenharmony_ci								\
998c2ecf20Sopenharmony_ci		.soft_reset	= genphy_soft_reset,		\
1008c2ecf20Sopenharmony_ci		.config_init	= _config_init,			\
1018c2ecf20Sopenharmony_ci		.suspend	= genphy_suspend,		\
1028c2ecf20Sopenharmony_ci		.resume		= genphy_resume,		\
1038c2ecf20Sopenharmony_ci								\
1048c2ecf20Sopenharmony_ci		/* IRQ related */				\
1058c2ecf20Sopenharmony_ci		.ack_interrupt	= dp83848_ack_interrupt,	\
1068c2ecf20Sopenharmony_ci		.config_intr	= dp83848_config_intr,		\
1078c2ecf20Sopenharmony_ci	}
1088c2ecf20Sopenharmony_ci
1098c2ecf20Sopenharmony_cistatic struct phy_driver dp83848_driver[] = {
1108c2ecf20Sopenharmony_ci	DP83848_PHY_DRIVER(TI_DP83848C_PHY_ID, "TI DP83848C 10/100 Mbps PHY",
1118c2ecf20Sopenharmony_ci			   NULL),
1128c2ecf20Sopenharmony_ci	DP83848_PHY_DRIVER(NS_DP83848C_PHY_ID, "NS DP83848C 10/100 Mbps PHY",
1138c2ecf20Sopenharmony_ci			   NULL),
1148c2ecf20Sopenharmony_ci	DP83848_PHY_DRIVER(TI_DP83620_PHY_ID, "TI DP83620 10/100 Mbps PHY",
1158c2ecf20Sopenharmony_ci			   dp83848_config_init),
1168c2ecf20Sopenharmony_ci	DP83848_PHY_DRIVER(TLK10X_PHY_ID, "TI TLK10X 10/100 Mbps PHY",
1178c2ecf20Sopenharmony_ci			   NULL),
1188c2ecf20Sopenharmony_ci};
1198c2ecf20Sopenharmony_cimodule_phy_driver(dp83848_driver);
1208c2ecf20Sopenharmony_ci
1218c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Texas Instruments DP83848 PHY driver");
1228c2ecf20Sopenharmony_ciMODULE_AUTHOR("Andrew F. Davis <afd@ti.com>");
1238c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2");
124