162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci *    Copyright 2017 NXP
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci *    CORTINA is a registered trademark of Cortina Systems, Inc.
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci */
862306a36Sopenharmony_ci#include <linux/module.h>
962306a36Sopenharmony_ci#include <linux/phy.h>
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_ci#define PHY_ID_CS4340	0x13e51002
1262306a36Sopenharmony_ci
1362306a36Sopenharmony_ci#define VILLA_GLOBAL_CHIP_ID_LSB			0x0
1462306a36Sopenharmony_ci#define VILLA_GLOBAL_CHIP_ID_MSB			0x1
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_ci#define VILLA_GLOBAL_GPIO_1_INTS			0x017
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_cistatic int cortina_read_reg(struct phy_device *phydev, u16 regnum)
1962306a36Sopenharmony_ci{
2062306a36Sopenharmony_ci	return mdiobus_c45_read(phydev->mdio.bus, phydev->mdio.addr, 0, regnum);
2162306a36Sopenharmony_ci}
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_cistatic int cortina_read_status(struct phy_device *phydev)
2462306a36Sopenharmony_ci{
2562306a36Sopenharmony_ci	int gpio_int_status, ret = 0;
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_ci	gpio_int_status = cortina_read_reg(phydev, VILLA_GLOBAL_GPIO_1_INTS);
2862306a36Sopenharmony_ci	if (gpio_int_status < 0) {
2962306a36Sopenharmony_ci		ret = gpio_int_status;
3062306a36Sopenharmony_ci		goto err;
3162306a36Sopenharmony_ci	}
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_ci	if (gpio_int_status & 0x8) {
3462306a36Sopenharmony_ci		/* up when edc_convergedS set */
3562306a36Sopenharmony_ci		phydev->speed = SPEED_10000;
3662306a36Sopenharmony_ci		phydev->duplex = DUPLEX_FULL;
3762306a36Sopenharmony_ci		phydev->link = 1;
3862306a36Sopenharmony_ci	} else {
3962306a36Sopenharmony_ci		phydev->link = 0;
4062306a36Sopenharmony_ci	}
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_cierr:
4362306a36Sopenharmony_ci	return ret;
4462306a36Sopenharmony_ci}
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_cistatic int cortina_probe(struct phy_device *phydev)
4762306a36Sopenharmony_ci{
4862306a36Sopenharmony_ci	u32 phy_id = 0;
4962306a36Sopenharmony_ci	int id_lsb = 0, id_msb = 0;
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_ci	/* Read device id from phy registers. */
5262306a36Sopenharmony_ci	id_lsb = cortina_read_reg(phydev, VILLA_GLOBAL_CHIP_ID_LSB);
5362306a36Sopenharmony_ci	if (id_lsb < 0)
5462306a36Sopenharmony_ci		return -ENXIO;
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_ci	phy_id = id_lsb << 16;
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_ci	id_msb = cortina_read_reg(phydev, VILLA_GLOBAL_CHIP_ID_MSB);
5962306a36Sopenharmony_ci	if (id_msb < 0)
6062306a36Sopenharmony_ci		return -ENXIO;
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ci	phy_id |= id_msb;
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_ci	/* Make sure the device tree binding matched the driver with the
6562306a36Sopenharmony_ci	 * right device.
6662306a36Sopenharmony_ci	 */
6762306a36Sopenharmony_ci	if (phy_id != phydev->drv->phy_id) {
6862306a36Sopenharmony_ci		phydev_err(phydev, "Error matching phy with %s driver\n",
6962306a36Sopenharmony_ci			   phydev->drv->name);
7062306a36Sopenharmony_ci		return -ENODEV;
7162306a36Sopenharmony_ci	}
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_ci	return 0;
7462306a36Sopenharmony_ci}
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_cistatic struct phy_driver cortina_driver[] = {
7762306a36Sopenharmony_ci{
7862306a36Sopenharmony_ci	.phy_id		= PHY_ID_CS4340,
7962306a36Sopenharmony_ci	.phy_id_mask	= 0xffffffff,
8062306a36Sopenharmony_ci	.name		= "Cortina CS4340",
8162306a36Sopenharmony_ci	.features       = PHY_10GBIT_FEATURES,
8262306a36Sopenharmony_ci	.config_aneg	= gen10g_config_aneg,
8362306a36Sopenharmony_ci	.read_status	= cortina_read_status,
8462306a36Sopenharmony_ci	.probe		= cortina_probe,
8562306a36Sopenharmony_ci},
8662306a36Sopenharmony_ci};
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_cimodule_phy_driver(cortina_driver);
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_cistatic struct mdio_device_id __maybe_unused cortina_tbl[] = {
9162306a36Sopenharmony_ci	{ PHY_ID_CS4340, 0xffffffff},
9262306a36Sopenharmony_ci	{},
9362306a36Sopenharmony_ci};
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_ciMODULE_DEVICE_TABLE(mdio, cortina_tbl);
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_ciMODULE_DESCRIPTION("Cortina EDC CDR 10G Ethernet PHY driver");
9862306a36Sopenharmony_ciMODULE_AUTHOR("NXP");
9962306a36Sopenharmony_ciMODULE_LICENSE("GPL");
100