18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved.
38c2ecf20Sopenharmony_ci */
48c2ecf20Sopenharmony_ci
58c2ecf20Sopenharmony_ci/* Qualcomm Technologies, Inc. EMAC SGMII Controller driver.
68c2ecf20Sopenharmony_ci */
78c2ecf20Sopenharmony_ci
88c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
98c2ecf20Sopenharmony_ci#include <linux/iopoll.h>
108c2ecf20Sopenharmony_ci#include <linux/acpi.h>
118c2ecf20Sopenharmony_ci#include <linux/of_device.h>
128c2ecf20Sopenharmony_ci#include "emac.h"
138c2ecf20Sopenharmony_ci#include "emac-mac.h"
148c2ecf20Sopenharmony_ci#include "emac-sgmii.h"
158c2ecf20Sopenharmony_ci
168c2ecf20Sopenharmony_ci/* EMAC_SGMII register offsets */
178c2ecf20Sopenharmony_ci#define EMAC_SGMII_PHY_AUTONEG_CFG2		0x0048
188c2ecf20Sopenharmony_ci#define EMAC_SGMII_PHY_SPEED_CFG1		0x0074
198c2ecf20Sopenharmony_ci#define EMAC_SGMII_PHY_IRQ_CMD			0x00ac
208c2ecf20Sopenharmony_ci#define EMAC_SGMII_PHY_INTERRUPT_CLEAR		0x00b0
218c2ecf20Sopenharmony_ci#define EMAC_SGMII_PHY_INTERRUPT_MASK		0x00b4
228c2ecf20Sopenharmony_ci#define EMAC_SGMII_PHY_INTERRUPT_STATUS		0x00b8
238c2ecf20Sopenharmony_ci#define EMAC_SGMII_PHY_RX_CHK_STATUS		0x00d4
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_ci#define FORCE_AN_TX_CFG				BIT(5)
268c2ecf20Sopenharmony_ci#define FORCE_AN_RX_CFG				BIT(4)
278c2ecf20Sopenharmony_ci#define AN_ENABLE				BIT(0)
288c2ecf20Sopenharmony_ci
298c2ecf20Sopenharmony_ci#define DUPLEX_MODE				BIT(4)
308c2ecf20Sopenharmony_ci#define SPDMODE_1000				BIT(1)
318c2ecf20Sopenharmony_ci#define SPDMODE_100				BIT(0)
328c2ecf20Sopenharmony_ci#define SPDMODE_10				0
338c2ecf20Sopenharmony_ci
348c2ecf20Sopenharmony_ci#define CDR_ALIGN_DET				BIT(6)
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_ci#define IRQ_GLOBAL_CLEAR			BIT(0)
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_ci#define DECODE_CODE_ERR				BIT(7)
398c2ecf20Sopenharmony_ci#define DECODE_DISP_ERR				BIT(6)
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_ci#define SGMII_PHY_IRQ_CLR_WAIT_TIME		10
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_ci#define SGMII_PHY_INTERRUPT_ERR		(DECODE_CODE_ERR | DECODE_DISP_ERR)
448c2ecf20Sopenharmony_ci#define SGMII_ISR_MASK  		(SGMII_PHY_INTERRUPT_ERR)
458c2ecf20Sopenharmony_ci
468c2ecf20Sopenharmony_ci#define SERDES_START_WAIT_TIMES			100
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_ciint emac_sgmii_init(struct emac_adapter *adpt)
498c2ecf20Sopenharmony_ci{
508c2ecf20Sopenharmony_ci	if (!(adpt->phy.sgmii_ops && adpt->phy.sgmii_ops->init))
518c2ecf20Sopenharmony_ci		return 0;
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_ci	return adpt->phy.sgmii_ops->init(adpt);
548c2ecf20Sopenharmony_ci}
558c2ecf20Sopenharmony_ci
568c2ecf20Sopenharmony_ciint emac_sgmii_open(struct emac_adapter *adpt)
578c2ecf20Sopenharmony_ci{
588c2ecf20Sopenharmony_ci	if (!(adpt->phy.sgmii_ops && adpt->phy.sgmii_ops->open))
598c2ecf20Sopenharmony_ci		return 0;
608c2ecf20Sopenharmony_ci
618c2ecf20Sopenharmony_ci	return adpt->phy.sgmii_ops->open(adpt);
628c2ecf20Sopenharmony_ci}
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_civoid emac_sgmii_close(struct emac_adapter *adpt)
658c2ecf20Sopenharmony_ci{
668c2ecf20Sopenharmony_ci	if (!(adpt->phy.sgmii_ops && adpt->phy.sgmii_ops->close))
678c2ecf20Sopenharmony_ci		return;
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_ci	adpt->phy.sgmii_ops->close(adpt);
708c2ecf20Sopenharmony_ci}
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_ciint emac_sgmii_link_change(struct emac_adapter *adpt, bool link_state)
738c2ecf20Sopenharmony_ci{
748c2ecf20Sopenharmony_ci	if (!(adpt->phy.sgmii_ops && adpt->phy.sgmii_ops->link_change))
758c2ecf20Sopenharmony_ci		return 0;
768c2ecf20Sopenharmony_ci
778c2ecf20Sopenharmony_ci	return adpt->phy.sgmii_ops->link_change(adpt, link_state);
788c2ecf20Sopenharmony_ci}
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_civoid emac_sgmii_reset(struct emac_adapter *adpt)
818c2ecf20Sopenharmony_ci{
828c2ecf20Sopenharmony_ci	if (!(adpt->phy.sgmii_ops && adpt->phy.sgmii_ops->reset))
838c2ecf20Sopenharmony_ci		return;
848c2ecf20Sopenharmony_ci
858c2ecf20Sopenharmony_ci	adpt->phy.sgmii_ops->reset(adpt);
868c2ecf20Sopenharmony_ci}
878c2ecf20Sopenharmony_ci
888c2ecf20Sopenharmony_ci/* Initialize the SGMII link between the internal and external PHYs. */
898c2ecf20Sopenharmony_cistatic void emac_sgmii_link_init(struct emac_adapter *adpt)
908c2ecf20Sopenharmony_ci{
918c2ecf20Sopenharmony_ci	struct emac_sgmii *phy = &adpt->phy;
928c2ecf20Sopenharmony_ci	u32 val;
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_ci	/* Always use autonegotiation. It works no matter how the external
958c2ecf20Sopenharmony_ci	 * PHY is configured.
968c2ecf20Sopenharmony_ci	 */
978c2ecf20Sopenharmony_ci	val = readl(phy->base + EMAC_SGMII_PHY_AUTONEG_CFG2);
988c2ecf20Sopenharmony_ci	val &= ~(FORCE_AN_RX_CFG | FORCE_AN_TX_CFG);
998c2ecf20Sopenharmony_ci	val |= AN_ENABLE;
1008c2ecf20Sopenharmony_ci	writel(val, phy->base + EMAC_SGMII_PHY_AUTONEG_CFG2);
1018c2ecf20Sopenharmony_ci}
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_cistatic int emac_sgmii_irq_clear(struct emac_adapter *adpt, u8 irq_bits)
1048c2ecf20Sopenharmony_ci{
1058c2ecf20Sopenharmony_ci	struct emac_sgmii *phy = &adpt->phy;
1068c2ecf20Sopenharmony_ci	u8 status;
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_ci	writel_relaxed(irq_bits, phy->base + EMAC_SGMII_PHY_INTERRUPT_CLEAR);
1098c2ecf20Sopenharmony_ci	writel_relaxed(IRQ_GLOBAL_CLEAR, phy->base + EMAC_SGMII_PHY_IRQ_CMD);
1108c2ecf20Sopenharmony_ci	/* Ensure interrupt clear command is written to HW */
1118c2ecf20Sopenharmony_ci	wmb();
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_ci	/* After set the IRQ_GLOBAL_CLEAR bit, the status clearing must
1148c2ecf20Sopenharmony_ci	 * be confirmed before clearing the bits in other registers.
1158c2ecf20Sopenharmony_ci	 * It takes a few cycles for hw to clear the interrupt status.
1168c2ecf20Sopenharmony_ci	 */
1178c2ecf20Sopenharmony_ci	if (readl_poll_timeout_atomic(phy->base +
1188c2ecf20Sopenharmony_ci				      EMAC_SGMII_PHY_INTERRUPT_STATUS,
1198c2ecf20Sopenharmony_ci				      status, !(status & irq_bits), 1,
1208c2ecf20Sopenharmony_ci				      SGMII_PHY_IRQ_CLR_WAIT_TIME)) {
1218c2ecf20Sopenharmony_ci		net_err_ratelimited("%s: failed to clear SGMII irq: status:0x%x bits:0x%x\n",
1228c2ecf20Sopenharmony_ci				    adpt->netdev->name, status, irq_bits);
1238c2ecf20Sopenharmony_ci		return -EIO;
1248c2ecf20Sopenharmony_ci	}
1258c2ecf20Sopenharmony_ci
1268c2ecf20Sopenharmony_ci	/* Finalize clearing procedure */
1278c2ecf20Sopenharmony_ci	writel_relaxed(0, phy->base + EMAC_SGMII_PHY_IRQ_CMD);
1288c2ecf20Sopenharmony_ci	writel_relaxed(0, phy->base + EMAC_SGMII_PHY_INTERRUPT_CLEAR);
1298c2ecf20Sopenharmony_ci
1308c2ecf20Sopenharmony_ci	/* Ensure that clearing procedure finalization is written to HW */
1318c2ecf20Sopenharmony_ci	wmb();
1328c2ecf20Sopenharmony_ci
1338c2ecf20Sopenharmony_ci	return 0;
1348c2ecf20Sopenharmony_ci}
1358c2ecf20Sopenharmony_ci
1368c2ecf20Sopenharmony_ci/* The number of decode errors that triggers a reset */
1378c2ecf20Sopenharmony_ci#define DECODE_ERROR_LIMIT	2
1388c2ecf20Sopenharmony_ci
1398c2ecf20Sopenharmony_cistatic irqreturn_t emac_sgmii_interrupt(int irq, void *data)
1408c2ecf20Sopenharmony_ci{
1418c2ecf20Sopenharmony_ci	struct emac_adapter *adpt = data;
1428c2ecf20Sopenharmony_ci	struct emac_sgmii *phy = &adpt->phy;
1438c2ecf20Sopenharmony_ci	u8 status;
1448c2ecf20Sopenharmony_ci
1458c2ecf20Sopenharmony_ci	status = readl(phy->base + EMAC_SGMII_PHY_INTERRUPT_STATUS);
1468c2ecf20Sopenharmony_ci	status &= SGMII_ISR_MASK;
1478c2ecf20Sopenharmony_ci	if (!status)
1488c2ecf20Sopenharmony_ci		return IRQ_HANDLED;
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_ci	/* If we get a decoding error and CDR is not locked, then try
1518c2ecf20Sopenharmony_ci	 * resetting the internal PHY.  The internal PHY uses an embedded
1528c2ecf20Sopenharmony_ci	 * clock with Clock and Data Recovery (CDR) to recover the
1538c2ecf20Sopenharmony_ci	 * clock and data.
1548c2ecf20Sopenharmony_ci	 */
1558c2ecf20Sopenharmony_ci	if (status & SGMII_PHY_INTERRUPT_ERR) {
1568c2ecf20Sopenharmony_ci		int count;
1578c2ecf20Sopenharmony_ci
1588c2ecf20Sopenharmony_ci		/* The SGMII is capable of recovering from some decode
1598c2ecf20Sopenharmony_ci		 * errors automatically.  However, if we get multiple
1608c2ecf20Sopenharmony_ci		 * decode errors in a row, then assume that something
1618c2ecf20Sopenharmony_ci		 * is wrong and reset the interface.
1628c2ecf20Sopenharmony_ci		 */
1638c2ecf20Sopenharmony_ci		count = atomic_inc_return(&phy->decode_error_count);
1648c2ecf20Sopenharmony_ci		if (count == DECODE_ERROR_LIMIT) {
1658c2ecf20Sopenharmony_ci			schedule_work(&adpt->work_thread);
1668c2ecf20Sopenharmony_ci			atomic_set(&phy->decode_error_count, 0);
1678c2ecf20Sopenharmony_ci		}
1688c2ecf20Sopenharmony_ci	} else {
1698c2ecf20Sopenharmony_ci		/* We only care about consecutive decode errors. */
1708c2ecf20Sopenharmony_ci		atomic_set(&phy->decode_error_count, 0);
1718c2ecf20Sopenharmony_ci	}
1728c2ecf20Sopenharmony_ci
1738c2ecf20Sopenharmony_ci	if (emac_sgmii_irq_clear(adpt, status))
1748c2ecf20Sopenharmony_ci		schedule_work(&adpt->work_thread);
1758c2ecf20Sopenharmony_ci
1768c2ecf20Sopenharmony_ci	return IRQ_HANDLED;
1778c2ecf20Sopenharmony_ci}
1788c2ecf20Sopenharmony_ci
1798c2ecf20Sopenharmony_cistatic void emac_sgmii_reset_prepare(struct emac_adapter *adpt)
1808c2ecf20Sopenharmony_ci{
1818c2ecf20Sopenharmony_ci	struct emac_sgmii *phy = &adpt->phy;
1828c2ecf20Sopenharmony_ci	u32 val;
1838c2ecf20Sopenharmony_ci
1848c2ecf20Sopenharmony_ci	/* Reset PHY */
1858c2ecf20Sopenharmony_ci	val = readl(phy->base + EMAC_EMAC_WRAPPER_CSR2);
1868c2ecf20Sopenharmony_ci	writel(((val & ~PHY_RESET) | PHY_RESET), phy->base +
1878c2ecf20Sopenharmony_ci	       EMAC_EMAC_WRAPPER_CSR2);
1888c2ecf20Sopenharmony_ci	/* Ensure phy-reset command is written to HW before the release cmd */
1898c2ecf20Sopenharmony_ci	msleep(50);
1908c2ecf20Sopenharmony_ci	val = readl(phy->base + EMAC_EMAC_WRAPPER_CSR2);
1918c2ecf20Sopenharmony_ci	writel((val & ~PHY_RESET), phy->base + EMAC_EMAC_WRAPPER_CSR2);
1928c2ecf20Sopenharmony_ci	/* Ensure phy-reset release command is written to HW before initializing
1938c2ecf20Sopenharmony_ci	 * SGMII
1948c2ecf20Sopenharmony_ci	 */
1958c2ecf20Sopenharmony_ci	msleep(50);
1968c2ecf20Sopenharmony_ci}
1978c2ecf20Sopenharmony_ci
1988c2ecf20Sopenharmony_cistatic void emac_sgmii_common_reset(struct emac_adapter *adpt)
1998c2ecf20Sopenharmony_ci{
2008c2ecf20Sopenharmony_ci	int ret;
2018c2ecf20Sopenharmony_ci
2028c2ecf20Sopenharmony_ci	emac_sgmii_reset_prepare(adpt);
2038c2ecf20Sopenharmony_ci	emac_sgmii_link_init(adpt);
2048c2ecf20Sopenharmony_ci
2058c2ecf20Sopenharmony_ci	ret = emac_sgmii_init(adpt);
2068c2ecf20Sopenharmony_ci	if (ret)
2078c2ecf20Sopenharmony_ci		netdev_err(adpt->netdev,
2088c2ecf20Sopenharmony_ci			   "could not reinitialize internal PHY (error=%i)\n",
2098c2ecf20Sopenharmony_ci			   ret);
2108c2ecf20Sopenharmony_ci}
2118c2ecf20Sopenharmony_ci
2128c2ecf20Sopenharmony_cistatic int emac_sgmii_common_open(struct emac_adapter *adpt)
2138c2ecf20Sopenharmony_ci{
2148c2ecf20Sopenharmony_ci	struct emac_sgmii *sgmii = &adpt->phy;
2158c2ecf20Sopenharmony_ci	int ret;
2168c2ecf20Sopenharmony_ci
2178c2ecf20Sopenharmony_ci	if (sgmii->irq) {
2188c2ecf20Sopenharmony_ci		/* Make sure interrupts are cleared and disabled first */
2198c2ecf20Sopenharmony_ci		ret = emac_sgmii_irq_clear(adpt, 0xff);
2208c2ecf20Sopenharmony_ci		if (ret)
2218c2ecf20Sopenharmony_ci			return ret;
2228c2ecf20Sopenharmony_ci		writel(0, sgmii->base + EMAC_SGMII_PHY_INTERRUPT_MASK);
2238c2ecf20Sopenharmony_ci
2248c2ecf20Sopenharmony_ci		ret = request_irq(sgmii->irq, emac_sgmii_interrupt, 0,
2258c2ecf20Sopenharmony_ci				  "emac-sgmii", adpt);
2268c2ecf20Sopenharmony_ci		if (ret) {
2278c2ecf20Sopenharmony_ci			netdev_err(adpt->netdev,
2288c2ecf20Sopenharmony_ci				   "could not register handler for internal PHY\n");
2298c2ecf20Sopenharmony_ci			return ret;
2308c2ecf20Sopenharmony_ci		}
2318c2ecf20Sopenharmony_ci	}
2328c2ecf20Sopenharmony_ci
2338c2ecf20Sopenharmony_ci	return 0;
2348c2ecf20Sopenharmony_ci}
2358c2ecf20Sopenharmony_ci
2368c2ecf20Sopenharmony_cistatic void emac_sgmii_common_close(struct emac_adapter *adpt)
2378c2ecf20Sopenharmony_ci{
2388c2ecf20Sopenharmony_ci	struct emac_sgmii *sgmii = &adpt->phy;
2398c2ecf20Sopenharmony_ci
2408c2ecf20Sopenharmony_ci	/* Make sure interrupts are disabled */
2418c2ecf20Sopenharmony_ci	writel(0, sgmii->base + EMAC_SGMII_PHY_INTERRUPT_MASK);
2428c2ecf20Sopenharmony_ci	free_irq(sgmii->irq, adpt);
2438c2ecf20Sopenharmony_ci}
2448c2ecf20Sopenharmony_ci
2458c2ecf20Sopenharmony_ci/* The error interrupts are only valid after the link is up */
2468c2ecf20Sopenharmony_cistatic int emac_sgmii_common_link_change(struct emac_adapter *adpt, bool linkup)
2478c2ecf20Sopenharmony_ci{
2488c2ecf20Sopenharmony_ci	struct emac_sgmii *sgmii = &adpt->phy;
2498c2ecf20Sopenharmony_ci	int ret;
2508c2ecf20Sopenharmony_ci
2518c2ecf20Sopenharmony_ci	if (linkup) {
2528c2ecf20Sopenharmony_ci		/* Clear and enable interrupts */
2538c2ecf20Sopenharmony_ci		ret = emac_sgmii_irq_clear(adpt, 0xff);
2548c2ecf20Sopenharmony_ci		if (ret)
2558c2ecf20Sopenharmony_ci			return ret;
2568c2ecf20Sopenharmony_ci
2578c2ecf20Sopenharmony_ci		writel(SGMII_ISR_MASK,
2588c2ecf20Sopenharmony_ci		       sgmii->base + EMAC_SGMII_PHY_INTERRUPT_MASK);
2598c2ecf20Sopenharmony_ci	} else {
2608c2ecf20Sopenharmony_ci		/* Disable interrupts */
2618c2ecf20Sopenharmony_ci		writel(0, sgmii->base + EMAC_SGMII_PHY_INTERRUPT_MASK);
2628c2ecf20Sopenharmony_ci		synchronize_irq(sgmii->irq);
2638c2ecf20Sopenharmony_ci	}
2648c2ecf20Sopenharmony_ci
2658c2ecf20Sopenharmony_ci	return 0;
2668c2ecf20Sopenharmony_ci}
2678c2ecf20Sopenharmony_ci
2688c2ecf20Sopenharmony_cistatic struct sgmii_ops fsm9900_ops = {
2698c2ecf20Sopenharmony_ci	.init = emac_sgmii_init_fsm9900,
2708c2ecf20Sopenharmony_ci	.open = emac_sgmii_common_open,
2718c2ecf20Sopenharmony_ci	.close = emac_sgmii_common_close,
2728c2ecf20Sopenharmony_ci	.link_change = emac_sgmii_common_link_change,
2738c2ecf20Sopenharmony_ci	.reset = emac_sgmii_common_reset,
2748c2ecf20Sopenharmony_ci};
2758c2ecf20Sopenharmony_ci
2768c2ecf20Sopenharmony_cistatic struct sgmii_ops qdf2432_ops = {
2778c2ecf20Sopenharmony_ci	.init = emac_sgmii_init_qdf2432,
2788c2ecf20Sopenharmony_ci	.open = emac_sgmii_common_open,
2798c2ecf20Sopenharmony_ci	.close = emac_sgmii_common_close,
2808c2ecf20Sopenharmony_ci	.link_change = emac_sgmii_common_link_change,
2818c2ecf20Sopenharmony_ci	.reset = emac_sgmii_common_reset,
2828c2ecf20Sopenharmony_ci};
2838c2ecf20Sopenharmony_ci
2848c2ecf20Sopenharmony_ci#ifdef CONFIG_ACPI
2858c2ecf20Sopenharmony_cistatic struct sgmii_ops qdf2400_ops = {
2868c2ecf20Sopenharmony_ci	.init = emac_sgmii_init_qdf2400,
2878c2ecf20Sopenharmony_ci	.open = emac_sgmii_common_open,
2888c2ecf20Sopenharmony_ci	.close = emac_sgmii_common_close,
2898c2ecf20Sopenharmony_ci	.link_change = emac_sgmii_common_link_change,
2908c2ecf20Sopenharmony_ci	.reset = emac_sgmii_common_reset,
2918c2ecf20Sopenharmony_ci};
2928c2ecf20Sopenharmony_ci#endif
2938c2ecf20Sopenharmony_ci
2948c2ecf20Sopenharmony_cistatic int emac_sgmii_acpi_match(struct device *dev, void *data)
2958c2ecf20Sopenharmony_ci{
2968c2ecf20Sopenharmony_ci#ifdef CONFIG_ACPI
2978c2ecf20Sopenharmony_ci	static const struct acpi_device_id match_table[] = {
2988c2ecf20Sopenharmony_ci		{
2998c2ecf20Sopenharmony_ci			.id = "QCOM8071",
3008c2ecf20Sopenharmony_ci		},
3018c2ecf20Sopenharmony_ci		{}
3028c2ecf20Sopenharmony_ci	};
3038c2ecf20Sopenharmony_ci	const struct acpi_device_id *id = acpi_match_device(match_table, dev);
3048c2ecf20Sopenharmony_ci	struct sgmii_ops **ops = data;
3058c2ecf20Sopenharmony_ci
3068c2ecf20Sopenharmony_ci	if (id) {
3078c2ecf20Sopenharmony_ci		acpi_handle handle = ACPI_HANDLE(dev);
3088c2ecf20Sopenharmony_ci		unsigned long long hrv;
3098c2ecf20Sopenharmony_ci		acpi_status status;
3108c2ecf20Sopenharmony_ci
3118c2ecf20Sopenharmony_ci		status = acpi_evaluate_integer(handle, "_HRV", NULL, &hrv);
3128c2ecf20Sopenharmony_ci		if (status) {
3138c2ecf20Sopenharmony_ci			if (status == AE_NOT_FOUND)
3148c2ecf20Sopenharmony_ci				/* Older versions of the QDF2432 ACPI tables do
3158c2ecf20Sopenharmony_ci				 * not have an _HRV property.
3168c2ecf20Sopenharmony_ci				 */
3178c2ecf20Sopenharmony_ci				hrv = 1;
3188c2ecf20Sopenharmony_ci			else
3198c2ecf20Sopenharmony_ci				/* Something is wrong with the tables */
3208c2ecf20Sopenharmony_ci				return 0;
3218c2ecf20Sopenharmony_ci		}
3228c2ecf20Sopenharmony_ci
3238c2ecf20Sopenharmony_ci		switch (hrv) {
3248c2ecf20Sopenharmony_ci		case 1:
3258c2ecf20Sopenharmony_ci			*ops = &qdf2432_ops;
3268c2ecf20Sopenharmony_ci			return 1;
3278c2ecf20Sopenharmony_ci		case 2:
3288c2ecf20Sopenharmony_ci			*ops = &qdf2400_ops;
3298c2ecf20Sopenharmony_ci			return 1;
3308c2ecf20Sopenharmony_ci		}
3318c2ecf20Sopenharmony_ci	}
3328c2ecf20Sopenharmony_ci#endif
3338c2ecf20Sopenharmony_ci
3348c2ecf20Sopenharmony_ci	return 0;
3358c2ecf20Sopenharmony_ci}
3368c2ecf20Sopenharmony_ci
3378c2ecf20Sopenharmony_cistatic const struct of_device_id emac_sgmii_dt_match[] = {
3388c2ecf20Sopenharmony_ci	{
3398c2ecf20Sopenharmony_ci		.compatible = "qcom,fsm9900-emac-sgmii",
3408c2ecf20Sopenharmony_ci		.data = &fsm9900_ops,
3418c2ecf20Sopenharmony_ci	},
3428c2ecf20Sopenharmony_ci	{
3438c2ecf20Sopenharmony_ci		.compatible = "qcom,qdf2432-emac-sgmii",
3448c2ecf20Sopenharmony_ci		.data = &qdf2432_ops,
3458c2ecf20Sopenharmony_ci	},
3468c2ecf20Sopenharmony_ci	{}
3478c2ecf20Sopenharmony_ci};
3488c2ecf20Sopenharmony_ci
3498c2ecf20Sopenharmony_ciint emac_sgmii_config(struct platform_device *pdev, struct emac_adapter *adpt)
3508c2ecf20Sopenharmony_ci{
3518c2ecf20Sopenharmony_ci	struct platform_device *sgmii_pdev = NULL;
3528c2ecf20Sopenharmony_ci	struct emac_sgmii *phy = &adpt->phy;
3538c2ecf20Sopenharmony_ci	struct resource *res;
3548c2ecf20Sopenharmony_ci	int ret;
3558c2ecf20Sopenharmony_ci
3568c2ecf20Sopenharmony_ci	if (has_acpi_companion(&pdev->dev)) {
3578c2ecf20Sopenharmony_ci		struct device *dev;
3588c2ecf20Sopenharmony_ci
3598c2ecf20Sopenharmony_ci		dev = device_find_child(&pdev->dev, &phy->sgmii_ops,
3608c2ecf20Sopenharmony_ci					emac_sgmii_acpi_match);
3618c2ecf20Sopenharmony_ci
3628c2ecf20Sopenharmony_ci		if (!dev) {
3638c2ecf20Sopenharmony_ci			dev_warn(&pdev->dev, "cannot find internal phy node\n");
3648c2ecf20Sopenharmony_ci			return 0;
3658c2ecf20Sopenharmony_ci		}
3668c2ecf20Sopenharmony_ci
3678c2ecf20Sopenharmony_ci		sgmii_pdev = to_platform_device(dev);
3688c2ecf20Sopenharmony_ci	} else {
3698c2ecf20Sopenharmony_ci		const struct of_device_id *match;
3708c2ecf20Sopenharmony_ci		struct device_node *np;
3718c2ecf20Sopenharmony_ci
3728c2ecf20Sopenharmony_ci		np = of_parse_phandle(pdev->dev.of_node, "internal-phy", 0);
3738c2ecf20Sopenharmony_ci		if (!np) {
3748c2ecf20Sopenharmony_ci			dev_err(&pdev->dev, "missing internal-phy property\n");
3758c2ecf20Sopenharmony_ci			return -ENODEV;
3768c2ecf20Sopenharmony_ci		}
3778c2ecf20Sopenharmony_ci
3788c2ecf20Sopenharmony_ci		sgmii_pdev = of_find_device_by_node(np);
3798c2ecf20Sopenharmony_ci		of_node_put(np);
3808c2ecf20Sopenharmony_ci		if (!sgmii_pdev) {
3818c2ecf20Sopenharmony_ci			dev_err(&pdev->dev, "invalid internal-phy property\n");
3828c2ecf20Sopenharmony_ci			return -ENODEV;
3838c2ecf20Sopenharmony_ci		}
3848c2ecf20Sopenharmony_ci
3858c2ecf20Sopenharmony_ci		match = of_match_device(emac_sgmii_dt_match, &sgmii_pdev->dev);
3868c2ecf20Sopenharmony_ci		if (!match) {
3878c2ecf20Sopenharmony_ci			dev_err(&pdev->dev, "unrecognized internal phy node\n");
3888c2ecf20Sopenharmony_ci			ret = -ENODEV;
3898c2ecf20Sopenharmony_ci			goto error_put_device;
3908c2ecf20Sopenharmony_ci		}
3918c2ecf20Sopenharmony_ci
3928c2ecf20Sopenharmony_ci		phy->sgmii_ops = (struct sgmii_ops *)match->data;
3938c2ecf20Sopenharmony_ci	}
3948c2ecf20Sopenharmony_ci
3958c2ecf20Sopenharmony_ci	/* Base address is the first address */
3968c2ecf20Sopenharmony_ci	res = platform_get_resource(sgmii_pdev, IORESOURCE_MEM, 0);
3978c2ecf20Sopenharmony_ci	if (!res) {
3988c2ecf20Sopenharmony_ci		ret = -EINVAL;
3998c2ecf20Sopenharmony_ci		goto error_put_device;
4008c2ecf20Sopenharmony_ci	}
4018c2ecf20Sopenharmony_ci
4028c2ecf20Sopenharmony_ci	phy->base = ioremap(res->start, resource_size(res));
4038c2ecf20Sopenharmony_ci	if (!phy->base) {
4048c2ecf20Sopenharmony_ci		ret = -ENOMEM;
4058c2ecf20Sopenharmony_ci		goto error_put_device;
4068c2ecf20Sopenharmony_ci	}
4078c2ecf20Sopenharmony_ci
4088c2ecf20Sopenharmony_ci	/* v2 SGMII has a per-lane digital digital, so parse it if it exists */
4098c2ecf20Sopenharmony_ci	res = platform_get_resource(sgmii_pdev, IORESOURCE_MEM, 1);
4108c2ecf20Sopenharmony_ci	if (res) {
4118c2ecf20Sopenharmony_ci		phy->digital = ioremap(res->start, resource_size(res));
4128c2ecf20Sopenharmony_ci		if (!phy->digital) {
4138c2ecf20Sopenharmony_ci			ret = -ENOMEM;
4148c2ecf20Sopenharmony_ci			goto error_unmap_base;
4158c2ecf20Sopenharmony_ci		}
4168c2ecf20Sopenharmony_ci	}
4178c2ecf20Sopenharmony_ci
4188c2ecf20Sopenharmony_ci	ret = emac_sgmii_init(adpt);
4198c2ecf20Sopenharmony_ci	if (ret)
4208c2ecf20Sopenharmony_ci		goto error;
4218c2ecf20Sopenharmony_ci
4228c2ecf20Sopenharmony_ci	emac_sgmii_link_init(adpt);
4238c2ecf20Sopenharmony_ci
4248c2ecf20Sopenharmony_ci	ret = platform_get_irq(sgmii_pdev, 0);
4258c2ecf20Sopenharmony_ci	if (ret > 0)
4268c2ecf20Sopenharmony_ci		phy->irq = ret;
4278c2ecf20Sopenharmony_ci
4288c2ecf20Sopenharmony_ci	/* We've remapped the addresses, so we don't need the device any
4298c2ecf20Sopenharmony_ci	 * more.  of_find_device_by_node() says we should release it.
4308c2ecf20Sopenharmony_ci	 */
4318c2ecf20Sopenharmony_ci	put_device(&sgmii_pdev->dev);
4328c2ecf20Sopenharmony_ci
4338c2ecf20Sopenharmony_ci	return 0;
4348c2ecf20Sopenharmony_ci
4358c2ecf20Sopenharmony_cierror:
4368c2ecf20Sopenharmony_ci	if (phy->digital)
4378c2ecf20Sopenharmony_ci		iounmap(phy->digital);
4388c2ecf20Sopenharmony_cierror_unmap_base:
4398c2ecf20Sopenharmony_ci	iounmap(phy->base);
4408c2ecf20Sopenharmony_cierror_put_device:
4418c2ecf20Sopenharmony_ci	put_device(&sgmii_pdev->dev);
4428c2ecf20Sopenharmony_ci
4438c2ecf20Sopenharmony_ci	return ret;
4448c2ecf20Sopenharmony_ci}
445