brintos

brintos / linux-shallow public Read only

0
0
Text · 10.1 KiB · 9d8f43b Raw
406 lines · c
1// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause2/* Copyright (c) 2015, The Linux Foundation. All rights reserved. */3/* Copyright (c) 2020 Sartura Ltd. */4 5#include <linux/delay.h>6#include <linux/io.h>7#include <linux/iopoll.h>8#include <linux/kernel.h>9#include <linux/module.h>10#include <linux/of_address.h>11#include <linux/of_mdio.h>12#include <linux/phy.h>13#include <linux/platform_device.h>14#include <linux/clk.h>15 16#define MDIO_MODE_REG				0x4017#define   MDIO_MODE_MDC_MODE			BIT(12)18/* 0 = Clause 22, 1 = Clause 45 */19#define   MDIO_MODE_C45				BIT(8)20#define   MDIO_MODE_DIV_MASK			GENMASK(7, 0)21#define     MDIO_MODE_DIV(x)			FIELD_PREP(MDIO_MODE_DIV_MASK, (x) - 1)22#define     MDIO_MODE_DIV_1			0x023#define     MDIO_MODE_DIV_2			0x124#define     MDIO_MODE_DIV_4			0x325#define     MDIO_MODE_DIV_8			0x726#define     MDIO_MODE_DIV_16			0xf27#define     MDIO_MODE_DIV_32			0x1f28#define     MDIO_MODE_DIV_64			0x3f29#define     MDIO_MODE_DIV_128			0x7f30#define     MDIO_MODE_DIV_256			0xff31#define MDIO_ADDR_REG				0x4432#define MDIO_DATA_WRITE_REG			0x4833#define MDIO_DATA_READ_REG			0x4c34#define MDIO_CMD_REG				0x5035#define MDIO_CMD_ACCESS_BUSY		BIT(16)36#define MDIO_CMD_ACCESS_START		BIT(8)37#define MDIO_CMD_ACCESS_CODE_READ	038#define MDIO_CMD_ACCESS_CODE_WRITE	139#define MDIO_CMD_ACCESS_CODE_C45_ADDR	040#define MDIO_CMD_ACCESS_CODE_C45_WRITE	141#define MDIO_CMD_ACCESS_CODE_C45_READ	242 43#define IPQ4019_MDIO_TIMEOUT	1000044#define IPQ4019_MDIO_SLEEP		1045 46/* MDIO clock source frequency is fixed to 100M */47#define IPQ_MDIO_CLK_RATE	10000000048 49#define IPQ_PHY_SET_DELAY_US	10000050 51struct ipq4019_mdio_data {52	void __iomem	*membase;53	void __iomem *eth_ldo_rdy;54	struct clk *mdio_clk;55	unsigned int mdc_rate;56};57 58static int ipq4019_mdio_wait_busy(struct mii_bus *bus)59{60	struct ipq4019_mdio_data *priv = bus->priv;61	unsigned int busy;62 63	return readl_poll_timeout(priv->membase + MDIO_CMD_REG, busy,64				  (busy & MDIO_CMD_ACCESS_BUSY) == 0,65				  IPQ4019_MDIO_SLEEP, IPQ4019_MDIO_TIMEOUT);66}67 68static int ipq4019_mdio_read_c45(struct mii_bus *bus, int mii_id, int mmd,69				 int reg)70{71	struct ipq4019_mdio_data *priv = bus->priv;72	unsigned int data;73	unsigned int cmd;74 75	if (ipq4019_mdio_wait_busy(bus))76		return -ETIMEDOUT;77 78	data = readl(priv->membase + MDIO_MODE_REG);79 80	data |= MDIO_MODE_C45;81 82	writel(data, priv->membase + MDIO_MODE_REG);83 84	/* issue the phy address and mmd */85	writel((mii_id << 8) | mmd, priv->membase + MDIO_ADDR_REG);86 87	/* issue reg */88	writel(reg, priv->membase + MDIO_DATA_WRITE_REG);89 90	cmd = MDIO_CMD_ACCESS_START | MDIO_CMD_ACCESS_CODE_C45_ADDR;91 92	/* issue read command */93	writel(cmd, priv->membase + MDIO_CMD_REG);94 95	/* Wait read complete */96	if (ipq4019_mdio_wait_busy(bus))97		return -ETIMEDOUT;98 99	cmd = MDIO_CMD_ACCESS_START | MDIO_CMD_ACCESS_CODE_C45_READ;100 101	writel(cmd, priv->membase + MDIO_CMD_REG);102 103	if (ipq4019_mdio_wait_busy(bus))104		return -ETIMEDOUT;105 106	/* Read and return data */107	return readl(priv->membase + MDIO_DATA_READ_REG);108}109 110static int ipq4019_mdio_read_c22(struct mii_bus *bus, int mii_id, int regnum)111{112	struct ipq4019_mdio_data *priv = bus->priv;113	unsigned int data;114	unsigned int cmd;115 116	if (ipq4019_mdio_wait_busy(bus))117		return -ETIMEDOUT;118 119	data = readl(priv->membase + MDIO_MODE_REG);120 121	data &= ~MDIO_MODE_C45;122 123	writel(data, priv->membase + MDIO_MODE_REG);124 125	/* issue the phy address and reg */126	writel((mii_id << 8) | regnum, priv->membase + MDIO_ADDR_REG);127 128	cmd = MDIO_CMD_ACCESS_START | MDIO_CMD_ACCESS_CODE_READ;129 130	/* issue read command */131	writel(cmd, priv->membase + MDIO_CMD_REG);132 133	/* Wait read complete */134	if (ipq4019_mdio_wait_busy(bus))135		return -ETIMEDOUT;136 137	/* Read and return data */138	return readl(priv->membase + MDIO_DATA_READ_REG);139}140 141static int ipq4019_mdio_write_c45(struct mii_bus *bus, int mii_id, int mmd,142				  int reg, u16 value)143{144	struct ipq4019_mdio_data *priv = bus->priv;145	unsigned int data;146	unsigned int cmd;147 148	if (ipq4019_mdio_wait_busy(bus))149		return -ETIMEDOUT;150 151	data = readl(priv->membase + MDIO_MODE_REG);152 153	data |= MDIO_MODE_C45;154 155	writel(data, priv->membase + MDIO_MODE_REG);156 157	/* issue the phy address and mmd */158	writel((mii_id << 8) | mmd, priv->membase + MDIO_ADDR_REG);159 160	/* issue reg */161	writel(reg, priv->membase + MDIO_DATA_WRITE_REG);162 163	cmd = MDIO_CMD_ACCESS_START | MDIO_CMD_ACCESS_CODE_C45_ADDR;164 165	writel(cmd, priv->membase + MDIO_CMD_REG);166 167	if (ipq4019_mdio_wait_busy(bus))168		return -ETIMEDOUT;169 170	/* issue write data */171	writel(value, priv->membase + MDIO_DATA_WRITE_REG);172 173	cmd = MDIO_CMD_ACCESS_START | MDIO_CMD_ACCESS_CODE_C45_WRITE;174	writel(cmd, priv->membase + MDIO_CMD_REG);175 176	/* Wait write complete */177	if (ipq4019_mdio_wait_busy(bus))178		return -ETIMEDOUT;179 180	return 0;181}182 183static int ipq4019_mdio_write_c22(struct mii_bus *bus, int mii_id, int regnum,184				  u16 value)185{186	struct ipq4019_mdio_data *priv = bus->priv;187	unsigned int data;188	unsigned int cmd;189 190	if (ipq4019_mdio_wait_busy(bus))191		return -ETIMEDOUT;192 193	/* Enter Clause 22 mode */194	data = readl(priv->membase + MDIO_MODE_REG);195 196	data &= ~MDIO_MODE_C45;197 198	writel(data, priv->membase + MDIO_MODE_REG);199 200	/* issue the phy address and reg */201	writel((mii_id << 8) | regnum, priv->membase + MDIO_ADDR_REG);202 203	/* issue write data */204	writel(value, priv->membase + MDIO_DATA_WRITE_REG);205 206	/* issue write command */207	cmd = MDIO_CMD_ACCESS_START | MDIO_CMD_ACCESS_CODE_WRITE;208 209	writel(cmd, priv->membase + MDIO_CMD_REG);210 211	/* Wait write complete */212	if (ipq4019_mdio_wait_busy(bus))213		return -ETIMEDOUT;214 215	return 0;216}217 218static int ipq4019_mdio_set_div(struct ipq4019_mdio_data *priv)219{220	unsigned long ahb_rate;221	int div;222	u32 val;223 224	/* If we don't have a clock for AHB use the fixed value */225	ahb_rate = IPQ_MDIO_CLK_RATE;226	if (priv->mdio_clk)227		ahb_rate = clk_get_rate(priv->mdio_clk);228 229	/* MDC rate is ahb_rate/(MDIO_MODE_DIV + 1)230	 * While supported, internal documentation doesn't231	 * assure correct functionality of the MDIO bus232	 * with divider of 1, 2 or 4.233	 */234	for (div = 8; div <= 256; div *= 2) {235		/* The requested rate is supported by the div */236		if (priv->mdc_rate == DIV_ROUND_UP(ahb_rate, div)) {237			val = readl(priv->membase + MDIO_MODE_REG);238			val &= ~MDIO_MODE_DIV_MASK;239			val |= MDIO_MODE_DIV(div);240			writel(val, priv->membase + MDIO_MODE_REG);241 242			return 0;243		}244	}245 246	/* The requested rate is not supported */247	return -EINVAL;248}249 250static int ipq_mdio_reset(struct mii_bus *bus)251{252	struct ipq4019_mdio_data *priv = bus->priv;253	u32 val;254	int ret;255 256	/* To indicate CMN_PLL that ethernet_ldo has been ready if platform resource 1257	 * is specified in the device tree.258	 */259	if (priv->eth_ldo_rdy) {260		val = readl(priv->eth_ldo_rdy);261		val |= BIT(0);262		writel(val, priv->eth_ldo_rdy);263		fsleep(IPQ_PHY_SET_DELAY_US);264	}265 266	/* Configure MDIO clock source frequency if clock is specified in the device tree */267	ret = clk_set_rate(priv->mdio_clk, IPQ_MDIO_CLK_RATE);268	if (ret)269		return ret;270 271	ret = clk_prepare_enable(priv->mdio_clk);272	if (ret)273		return ret;274 275	mdelay(10);276 277	/* Restore MDC rate */278	return ipq4019_mdio_set_div(priv);279}280 281static void ipq4019_mdio_select_mdc_rate(struct platform_device *pdev,282					 struct ipq4019_mdio_data *priv)283{284	unsigned long ahb_rate;285	int div;286	u32 val;287 288	/* MDC rate defined in DT, we don't have to decide a default value */289	if (!of_property_read_u32(pdev->dev.of_node, "clock-frequency",290				  &priv->mdc_rate))291		return;292 293	/* If we don't have a clock for AHB use the fixed value */294	ahb_rate = IPQ_MDIO_CLK_RATE;295	if (priv->mdio_clk)296		ahb_rate = clk_get_rate(priv->mdio_clk);297 298	/* Check what is the current div set */299	val = readl(priv->membase + MDIO_MODE_REG);300	div = FIELD_GET(MDIO_MODE_DIV_MASK, val);301 302	/* div is not set to the default value of /256303	 * Probably someone changed that (bootloader, other drivers)304	 * Keep this and don't overwrite it.305	 */306	if (div != MDIO_MODE_DIV_256) {307		priv->mdc_rate = DIV_ROUND_UP(ahb_rate, div + 1);308		return;309	}310 311	/* If div is /256 assume nobody have set this value and312	 * try to find one MDC rate that is close the 802.3 spec of313	 * 2.5MHz314	 */315	for (div = 256; div >= 8; div /= 2) {316		/* Stop as soon as we found a divider that317		 * reached the closest value to 2.5MHz318		 */319		if (DIV_ROUND_UP(ahb_rate, div) > 2500000)320			break;321 322		priv->mdc_rate = DIV_ROUND_UP(ahb_rate, div);323	}324}325 326static int ipq4019_mdio_probe(struct platform_device *pdev)327{328	struct ipq4019_mdio_data *priv;329	struct mii_bus *bus;330	struct resource *res;331	int ret;332 333	bus = devm_mdiobus_alloc_size(&pdev->dev, sizeof(*priv));334	if (!bus)335		return -ENOMEM;336 337	priv = bus->priv;338 339	priv->membase = devm_platform_ioremap_resource(pdev, 0);340	if (IS_ERR(priv->membase))341		return PTR_ERR(priv->membase);342 343	priv->mdio_clk = devm_clk_get_optional(&pdev->dev, "gcc_mdio_ahb_clk");344	if (IS_ERR(priv->mdio_clk))345		return PTR_ERR(priv->mdio_clk);346 347	ipq4019_mdio_select_mdc_rate(pdev, priv);348	ret = ipq4019_mdio_set_div(priv);349	if (ret)350		return ret;351 352	/* The platform resource is provided on the chipset IPQ5018 */353	/* This resource is optional */354	res = platform_get_resource(pdev, IORESOURCE_MEM, 1);355	if (res)356		priv->eth_ldo_rdy = devm_ioremap_resource(&pdev->dev, res);357 358	bus->name = "ipq4019_mdio";359	bus->read = ipq4019_mdio_read_c22;360	bus->write = ipq4019_mdio_write_c22;361	bus->read_c45 = ipq4019_mdio_read_c45;362	bus->write_c45 = ipq4019_mdio_write_c45;363	bus->reset = ipq_mdio_reset;364	bus->parent = &pdev->dev;365	snprintf(bus->id, MII_BUS_ID_SIZE, "%s%d", pdev->name, pdev->id);366 367	ret = of_mdiobus_register(bus, pdev->dev.of_node);368	if (ret) {369		dev_err(&pdev->dev, "Cannot register MDIO bus!\n");370		return ret;371	}372 373	platform_set_drvdata(pdev, bus);374 375	return 0;376}377 378static void ipq4019_mdio_remove(struct platform_device *pdev)379{380	struct mii_bus *bus = platform_get_drvdata(pdev);381 382	mdiobus_unregister(bus);383}384 385static const struct of_device_id ipq4019_mdio_dt_ids[] = {386	{ .compatible = "qcom,ipq4019-mdio" },387	{ .compatible = "qcom,ipq5018-mdio" },388	{ }389};390MODULE_DEVICE_TABLE(of, ipq4019_mdio_dt_ids);391 392static struct platform_driver ipq4019_mdio_driver = {393	.probe = ipq4019_mdio_probe,394	.remove_new = ipq4019_mdio_remove,395	.driver = {396		.name = "ipq4019-mdio",397		.of_match_table = ipq4019_mdio_dt_ids,398	},399};400 401module_platform_driver(ipq4019_mdio_driver);402 403MODULE_DESCRIPTION("ipq4019 MDIO interface driver");404MODULE_AUTHOR("Qualcomm Atheros");405MODULE_LICENSE("Dual BSD/GPL");406