162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Driver for the Texas Instruments DP83848 PHY
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (C) 2015-2016 Texas Instruments Incorporated - http://www.ti.com/
662306a36Sopenharmony_ci */
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#include <linux/module.h>
962306a36Sopenharmony_ci#include <linux/phy.h>
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_ci#define TI_DP83848C_PHY_ID		0x20005ca0
1262306a36Sopenharmony_ci#define TI_DP83620_PHY_ID		0x20005ce0
1362306a36Sopenharmony_ci#define NS_DP83848C_PHY_ID		0x20005c90
1462306a36Sopenharmony_ci#define TLK10X_PHY_ID			0x2000a210
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_ci/* Registers */
1762306a36Sopenharmony_ci#define DP83848_MICR			0x11 /* MII Interrupt Control Register */
1862306a36Sopenharmony_ci#define DP83848_MISR			0x12 /* MII Interrupt Status Register */
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_ci/* MICR Register Fields */
2162306a36Sopenharmony_ci#define DP83848_MICR_INT_OE		BIT(0) /* Interrupt Output Enable */
2262306a36Sopenharmony_ci#define DP83848_MICR_INTEN		BIT(1) /* Interrupt Enable */
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_ci/* MISR Register Fields */
2562306a36Sopenharmony_ci#define DP83848_MISR_RHF_INT_EN		BIT(0) /* Receive Error Counter */
2662306a36Sopenharmony_ci#define DP83848_MISR_FHF_INT_EN		BIT(1) /* False Carrier Counter */
2762306a36Sopenharmony_ci#define DP83848_MISR_ANC_INT_EN		BIT(2) /* Auto-negotiation complete */
2862306a36Sopenharmony_ci#define DP83848_MISR_DUP_INT_EN		BIT(3) /* Duplex Status */
2962306a36Sopenharmony_ci#define DP83848_MISR_SPD_INT_EN		BIT(4) /* Speed status */
3062306a36Sopenharmony_ci#define DP83848_MISR_LINK_INT_EN	BIT(5) /* Link status */
3162306a36Sopenharmony_ci#define DP83848_MISR_ED_INT_EN		BIT(6) /* Energy detect */
3262306a36Sopenharmony_ci#define DP83848_MISR_LQM_INT_EN		BIT(7) /* Link Quality Monitor */
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_ci#define DP83848_INT_EN_MASK		\
3562306a36Sopenharmony_ci	(DP83848_MISR_ANC_INT_EN |	\
3662306a36Sopenharmony_ci	 DP83848_MISR_DUP_INT_EN |	\
3762306a36Sopenharmony_ci	 DP83848_MISR_SPD_INT_EN |	\
3862306a36Sopenharmony_ci	 DP83848_MISR_LINK_INT_EN)
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_ci#define DP83848_MISR_RHF_INT		BIT(8)
4162306a36Sopenharmony_ci#define DP83848_MISR_FHF_INT		BIT(9)
4262306a36Sopenharmony_ci#define DP83848_MISR_ANC_INT		BIT(10)
4362306a36Sopenharmony_ci#define DP83848_MISR_DUP_INT		BIT(11)
4462306a36Sopenharmony_ci#define DP83848_MISR_SPD_INT		BIT(12)
4562306a36Sopenharmony_ci#define DP83848_MISR_LINK_INT		BIT(13)
4662306a36Sopenharmony_ci#define DP83848_MISR_ED_INT		BIT(14)
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_ci#define DP83848_INT_MASK		\
4962306a36Sopenharmony_ci	(DP83848_MISR_ANC_INT |	\
5062306a36Sopenharmony_ci	 DP83848_MISR_DUP_INT |	\
5162306a36Sopenharmony_ci	 DP83848_MISR_SPD_INT |	\
5262306a36Sopenharmony_ci	 DP83848_MISR_LINK_INT)
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_cistatic int dp83848_ack_interrupt(struct phy_device *phydev)
5562306a36Sopenharmony_ci{
5662306a36Sopenharmony_ci	int err = phy_read(phydev, DP83848_MISR);
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_ci	return err < 0 ? err : 0;
5962306a36Sopenharmony_ci}
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_cistatic int dp83848_config_intr(struct phy_device *phydev)
6262306a36Sopenharmony_ci{
6362306a36Sopenharmony_ci	int control, ret;
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_ci	control = phy_read(phydev, DP83848_MICR);
6662306a36Sopenharmony_ci	if (control < 0)
6762306a36Sopenharmony_ci		return control;
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_ci	if (phydev->interrupts == PHY_INTERRUPT_ENABLED) {
7062306a36Sopenharmony_ci		ret = dp83848_ack_interrupt(phydev);
7162306a36Sopenharmony_ci		if (ret)
7262306a36Sopenharmony_ci			return ret;
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_ci		control |= DP83848_MICR_INT_OE;
7562306a36Sopenharmony_ci		control |= DP83848_MICR_INTEN;
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_ci		ret = phy_write(phydev, DP83848_MISR, DP83848_INT_EN_MASK);
7862306a36Sopenharmony_ci		if (ret < 0)
7962306a36Sopenharmony_ci			return ret;
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_ci		ret = phy_write(phydev, DP83848_MICR, control);
8262306a36Sopenharmony_ci	} else {
8362306a36Sopenharmony_ci		control &= ~DP83848_MICR_INTEN;
8462306a36Sopenharmony_ci		ret = phy_write(phydev, DP83848_MICR, control);
8562306a36Sopenharmony_ci		if (ret)
8662306a36Sopenharmony_ci			return ret;
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_ci		ret = dp83848_ack_interrupt(phydev);
8962306a36Sopenharmony_ci	}
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_ci	return ret;
9262306a36Sopenharmony_ci}
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_cistatic irqreturn_t dp83848_handle_interrupt(struct phy_device *phydev)
9562306a36Sopenharmony_ci{
9662306a36Sopenharmony_ci	int irq_status;
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_ci	irq_status = phy_read(phydev, DP83848_MISR);
9962306a36Sopenharmony_ci	if (irq_status < 0) {
10062306a36Sopenharmony_ci		phy_error(phydev);
10162306a36Sopenharmony_ci		return IRQ_NONE;
10262306a36Sopenharmony_ci	}
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_ci	if (!(irq_status & DP83848_INT_MASK))
10562306a36Sopenharmony_ci		return IRQ_NONE;
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_ci	phy_trigger_machine(phydev);
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_ci	return IRQ_HANDLED;
11062306a36Sopenharmony_ci}
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_cistatic int dp83848_config_init(struct phy_device *phydev)
11362306a36Sopenharmony_ci{
11462306a36Sopenharmony_ci	int val;
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_ci	/* DP83620 always reports Auto Negotiation Ability on BMSR. Instead,
11762306a36Sopenharmony_ci	 * we check initial value of BMCR Auto negotiation enable bit
11862306a36Sopenharmony_ci	 */
11962306a36Sopenharmony_ci	val = phy_read(phydev, MII_BMCR);
12062306a36Sopenharmony_ci	if (!(val & BMCR_ANENABLE))
12162306a36Sopenharmony_ci		phydev->autoneg = AUTONEG_DISABLE;
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_ci	return 0;
12462306a36Sopenharmony_ci}
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_cistatic struct mdio_device_id __maybe_unused dp83848_tbl[] = {
12762306a36Sopenharmony_ci	{ TI_DP83848C_PHY_ID, 0xfffffff0 },
12862306a36Sopenharmony_ci	{ NS_DP83848C_PHY_ID, 0xfffffff0 },
12962306a36Sopenharmony_ci	{ TI_DP83620_PHY_ID, 0xfffffff0 },
13062306a36Sopenharmony_ci	{ TLK10X_PHY_ID, 0xfffffff0 },
13162306a36Sopenharmony_ci	{ }
13262306a36Sopenharmony_ci};
13362306a36Sopenharmony_ciMODULE_DEVICE_TABLE(mdio, dp83848_tbl);
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_ci#define DP83848_PHY_DRIVER(_id, _name, _config_init)		\
13662306a36Sopenharmony_ci	{							\
13762306a36Sopenharmony_ci		.phy_id		= _id,				\
13862306a36Sopenharmony_ci		.phy_id_mask	= 0xfffffff0,			\
13962306a36Sopenharmony_ci		.name		= _name,			\
14062306a36Sopenharmony_ci		/* PHY_BASIC_FEATURES */			\
14162306a36Sopenharmony_ci								\
14262306a36Sopenharmony_ci		.soft_reset	= genphy_soft_reset,		\
14362306a36Sopenharmony_ci		.config_init	= _config_init,			\
14462306a36Sopenharmony_ci		.suspend	= genphy_suspend,		\
14562306a36Sopenharmony_ci		.resume		= genphy_resume,		\
14662306a36Sopenharmony_ci								\
14762306a36Sopenharmony_ci		/* IRQ related */				\
14862306a36Sopenharmony_ci		.config_intr	= dp83848_config_intr,		\
14962306a36Sopenharmony_ci		.handle_interrupt = dp83848_handle_interrupt,	\
15062306a36Sopenharmony_ci	}
15162306a36Sopenharmony_ci
15262306a36Sopenharmony_cistatic struct phy_driver dp83848_driver[] = {
15362306a36Sopenharmony_ci	DP83848_PHY_DRIVER(TI_DP83848C_PHY_ID, "TI DP83848C 10/100 Mbps PHY",
15462306a36Sopenharmony_ci			   NULL),
15562306a36Sopenharmony_ci	DP83848_PHY_DRIVER(NS_DP83848C_PHY_ID, "NS DP83848C 10/100 Mbps PHY",
15662306a36Sopenharmony_ci			   NULL),
15762306a36Sopenharmony_ci	DP83848_PHY_DRIVER(TI_DP83620_PHY_ID, "TI DP83620 10/100 Mbps PHY",
15862306a36Sopenharmony_ci			   dp83848_config_init),
15962306a36Sopenharmony_ci	DP83848_PHY_DRIVER(TLK10X_PHY_ID, "TI TLK10X 10/100 Mbps PHY",
16062306a36Sopenharmony_ci			   NULL),
16162306a36Sopenharmony_ci};
16262306a36Sopenharmony_cimodule_phy_driver(dp83848_driver);
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_ciMODULE_DESCRIPTION("Texas Instruments DP83848 PHY driver");
16562306a36Sopenharmony_ciMODULE_AUTHOR("Andrew F. Davis <afd@ti.com>");
16662306a36Sopenharmony_ciMODULE_LICENSE("GPL v2");
167