brintos

brintos / linux-shallow public Read only

0
0
Text · 11.5 KiB · ec924c6 Raw
494 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Synopsys DWC Ethernet Quality-of-Service v4.10a linux driver4 *5 * Copyright (C) 2016 Joao Pinto <jpinto@synopsys.com>6 */7 8#include <linux/clk.h>9#include <linux/clk-provider.h>10#include <linux/device.h>11#include <linux/gpio/consumer.h>12#include <linux/ethtool.h>13#include <linux/io.h>14#include <linux/iopoll.h>15#include <linux/ioport.h>16#include <linux/module.h>17#include <linux/of.h>18#include <linux/of_net.h>19#include <linux/mfd/syscon.h>20#include <linux/platform_device.h>21#include <linux/reset.h>22#include <linux/stmmac.h>23 24#include "stmmac_platform.h"25#include "dwmac4.h"26 27struct tegra_eqos {28	struct device *dev;29	void __iomem *regs;30 31	struct reset_control *rst;32	struct clk *clk_master;33	struct clk *clk_slave;34	struct clk *clk_tx;35	struct clk *clk_rx;36 37	struct gpio_desc *reset;38};39 40static int dwc_eth_dwmac_config_dt(struct platform_device *pdev,41				   struct plat_stmmacenet_data *plat_dat)42{43	struct device *dev = &pdev->dev;44	u32 burst_map = 0;45	u32 bit_index = 0;46	u32 a_index = 0;47 48	if (!plat_dat->axi) {49		plat_dat->axi = kzalloc(sizeof(struct stmmac_axi), GFP_KERNEL);50 51		if (!plat_dat->axi)52			return -ENOMEM;53	}54 55	plat_dat->axi->axi_lpi_en = device_property_read_bool(dev,56							      "snps,en-lpi");57	if (device_property_read_u32(dev, "snps,write-requests",58				     &plat_dat->axi->axi_wr_osr_lmt)) {59		/**60		 * Since the register has a reset value of 1, if property61		 * is missing, default to 1.62		 */63		plat_dat->axi->axi_wr_osr_lmt = 1;64	} else {65		/**66		 * If property exists, to keep the behavior from dwc_eth_qos,67		 * subtract one after parsing.68		 */69		plat_dat->axi->axi_wr_osr_lmt--;70	}71 72	if (device_property_read_u32(dev, "snps,read-requests",73				     &plat_dat->axi->axi_rd_osr_lmt)) {74		/**75		 * Since the register has a reset value of 1, if property76		 * is missing, default to 1.77		 */78		plat_dat->axi->axi_rd_osr_lmt = 1;79	} else {80		/**81		 * If property exists, to keep the behavior from dwc_eth_qos,82		 * subtract one after parsing.83		 */84		plat_dat->axi->axi_rd_osr_lmt--;85	}86	device_property_read_u32(dev, "snps,burst-map", &burst_map);87 88	/* converts burst-map bitmask to burst array */89	for (bit_index = 0; bit_index < 7; bit_index++) {90		if (burst_map & (1 << bit_index)) {91			switch (bit_index) {92			case 0:93			plat_dat->axi->axi_blen[a_index] = 4; break;94			case 1:95			plat_dat->axi->axi_blen[a_index] = 8; break;96			case 2:97			plat_dat->axi->axi_blen[a_index] = 16; break;98			case 3:99			plat_dat->axi->axi_blen[a_index] = 32; break;100			case 4:101			plat_dat->axi->axi_blen[a_index] = 64; break;102			case 5:103			plat_dat->axi->axi_blen[a_index] = 128; break;104			case 6:105			plat_dat->axi->axi_blen[a_index] = 256; break;106			default:107			break;108			}109			a_index++;110		}111	}112 113	/* dwc-qos needs GMAC4, AAL, TSO and PMT */114	plat_dat->has_gmac4 = 1;115	plat_dat->dma_cfg->aal = 1;116	plat_dat->flags |= STMMAC_FLAG_TSO_EN;117	plat_dat->pmt = 1;118 119	return 0;120}121 122static int dwc_qos_probe(struct platform_device *pdev,123			 struct plat_stmmacenet_data *plat_dat,124			 struct stmmac_resources *stmmac_res)125{126	int err;127 128	plat_dat->stmmac_clk = devm_clk_get(&pdev->dev, "apb_pclk");129	if (IS_ERR(plat_dat->stmmac_clk)) {130		dev_err(&pdev->dev, "apb_pclk clock not found.\n");131		return PTR_ERR(plat_dat->stmmac_clk);132	}133 134	err = clk_prepare_enable(plat_dat->stmmac_clk);135	if (err < 0) {136		dev_err(&pdev->dev, "failed to enable apb_pclk clock: %d\n",137			err);138		return err;139	}140 141	plat_dat->pclk = devm_clk_get(&pdev->dev, "phy_ref_clk");142	if (IS_ERR(plat_dat->pclk)) {143		dev_err(&pdev->dev, "phy_ref_clk clock not found.\n");144		err = PTR_ERR(plat_dat->pclk);145		goto disable;146	}147 148	err = clk_prepare_enable(plat_dat->pclk);149	if (err < 0) {150		dev_err(&pdev->dev, "failed to enable phy_ref clock: %d\n",151			err);152		goto disable;153	}154 155	return 0;156 157disable:158	clk_disable_unprepare(plat_dat->stmmac_clk);159	return err;160}161 162static void dwc_qos_remove(struct platform_device *pdev)163{164	struct net_device *ndev = platform_get_drvdata(pdev);165	struct stmmac_priv *priv = netdev_priv(ndev);166 167	clk_disable_unprepare(priv->plat->pclk);168	clk_disable_unprepare(priv->plat->stmmac_clk);169}170 171#define SDMEMCOMPPADCTRL 0x8800172#define  SDMEMCOMPPADCTRL_PAD_E_INPUT_OR_E_PWRD BIT(31)173 174#define AUTO_CAL_CONFIG 0x8804175#define  AUTO_CAL_CONFIG_START BIT(31)176#define  AUTO_CAL_CONFIG_ENABLE BIT(29)177 178#define AUTO_CAL_STATUS 0x880c179#define  AUTO_CAL_STATUS_ACTIVE BIT(31)180 181static void tegra_eqos_fix_speed(void *priv, unsigned int speed, unsigned int mode)182{183	struct tegra_eqos *eqos = priv;184	unsigned long rate = 125000000;185	bool needs_calibration = false;186	u32 value;187	int err;188 189	switch (speed) {190	case SPEED_1000:191		needs_calibration = true;192		rate = 125000000;193		break;194 195	case SPEED_100:196		needs_calibration = true;197		rate = 25000000;198		break;199 200	case SPEED_10:201		rate = 2500000;202		break;203 204	default:205		dev_err(eqos->dev, "invalid speed %u\n", speed);206		break;207	}208 209	if (needs_calibration) {210		/* calibrate */211		value = readl(eqos->regs + SDMEMCOMPPADCTRL);212		value |= SDMEMCOMPPADCTRL_PAD_E_INPUT_OR_E_PWRD;213		writel(value, eqos->regs + SDMEMCOMPPADCTRL);214 215		udelay(1);216 217		value = readl(eqos->regs + AUTO_CAL_CONFIG);218		value |= AUTO_CAL_CONFIG_START | AUTO_CAL_CONFIG_ENABLE;219		writel(value, eqos->regs + AUTO_CAL_CONFIG);220 221		err = readl_poll_timeout_atomic(eqos->regs + AUTO_CAL_STATUS,222						value,223						value & AUTO_CAL_STATUS_ACTIVE,224						1, 10);225		if (err < 0) {226			dev_err(eqos->dev, "calibration did not start\n");227			goto failed;228		}229 230		err = readl_poll_timeout_atomic(eqos->regs + AUTO_CAL_STATUS,231						value,232						(value & AUTO_CAL_STATUS_ACTIVE) == 0,233						20, 200);234		if (err < 0) {235			dev_err(eqos->dev, "calibration didn't finish\n");236			goto failed;237		}238 239	failed:240		value = readl(eqos->regs + SDMEMCOMPPADCTRL);241		value &= ~SDMEMCOMPPADCTRL_PAD_E_INPUT_OR_E_PWRD;242		writel(value, eqos->regs + SDMEMCOMPPADCTRL);243	} else {244		value = readl(eqos->regs + AUTO_CAL_CONFIG);245		value &= ~AUTO_CAL_CONFIG_ENABLE;246		writel(value, eqos->regs + AUTO_CAL_CONFIG);247	}248 249	err = clk_set_rate(eqos->clk_tx, rate);250	if (err < 0)251		dev_err(eqos->dev, "failed to set TX rate: %d\n", err);252}253 254static int tegra_eqos_init(struct platform_device *pdev, void *priv)255{256	struct tegra_eqos *eqos = priv;257	unsigned long rate;258	u32 value;259 260	rate = clk_get_rate(eqos->clk_slave);261 262	value = (rate / 1000000) - 1;263	writel(value, eqos->regs + GMAC_1US_TIC_COUNTER);264 265	return 0;266}267 268static int tegra_eqos_probe(struct platform_device *pdev,269			    struct plat_stmmacenet_data *data,270			    struct stmmac_resources *res)271{272	struct device *dev = &pdev->dev;273	struct tegra_eqos *eqos;274	int err;275 276	eqos = devm_kzalloc(&pdev->dev, sizeof(*eqos), GFP_KERNEL);277	if (!eqos)278		return -ENOMEM;279 280	eqos->dev = &pdev->dev;281	eqos->regs = res->addr;282 283	if (!is_of_node(dev->fwnode))284		goto bypass_clk_reset_gpio;285 286	eqos->clk_master = devm_clk_get(&pdev->dev, "master_bus");287	if (IS_ERR(eqos->clk_master)) {288		err = PTR_ERR(eqos->clk_master);289		goto error;290	}291 292	err = clk_prepare_enable(eqos->clk_master);293	if (err < 0)294		goto error;295 296	eqos->clk_slave = devm_clk_get(&pdev->dev, "slave_bus");297	if (IS_ERR(eqos->clk_slave)) {298		err = PTR_ERR(eqos->clk_slave);299		goto disable_master;300	}301 302	data->stmmac_clk = eqos->clk_slave;303 304	err = clk_prepare_enable(eqos->clk_slave);305	if (err < 0)306		goto disable_master;307 308	eqos->clk_rx = devm_clk_get(&pdev->dev, "rx");309	if (IS_ERR(eqos->clk_rx)) {310		err = PTR_ERR(eqos->clk_rx);311		goto disable_slave;312	}313 314	err = clk_prepare_enable(eqos->clk_rx);315	if (err < 0)316		goto disable_slave;317 318	eqos->clk_tx = devm_clk_get(&pdev->dev, "tx");319	if (IS_ERR(eqos->clk_tx)) {320		err = PTR_ERR(eqos->clk_tx);321		goto disable_rx;322	}323 324	err = clk_prepare_enable(eqos->clk_tx);325	if (err < 0)326		goto disable_rx;327 328	eqos->reset = devm_gpiod_get(&pdev->dev, "phy-reset", GPIOD_OUT_HIGH);329	if (IS_ERR(eqos->reset)) {330		err = PTR_ERR(eqos->reset);331		goto disable_tx;332	}333 334	usleep_range(2000, 4000);335	gpiod_set_value(eqos->reset, 0);336 337	/* MDIO bus was already reset just above */338	data->mdio_bus_data->needs_reset = false;339 340	eqos->rst = devm_reset_control_get(&pdev->dev, "eqos");341	if (IS_ERR(eqos->rst)) {342		err = PTR_ERR(eqos->rst);343		goto reset_phy;344	}345 346	err = reset_control_assert(eqos->rst);347	if (err < 0)348		goto reset_phy;349 350	usleep_range(2000, 4000);351 352	err = reset_control_deassert(eqos->rst);353	if (err < 0)354		goto reset_phy;355 356	usleep_range(2000, 4000);357 358bypass_clk_reset_gpio:359	data->fix_mac_speed = tegra_eqos_fix_speed;360	data->init = tegra_eqos_init;361	data->bsp_priv = eqos;362	data->flags |= STMMAC_FLAG_SPH_DISABLE;363 364	err = tegra_eqos_init(pdev, eqos);365	if (err < 0)366		goto reset;367 368	return 0;369reset:370	reset_control_assert(eqos->rst);371reset_phy:372	gpiod_set_value(eqos->reset, 1);373disable_tx:374	clk_disable_unprepare(eqos->clk_tx);375disable_rx:376	clk_disable_unprepare(eqos->clk_rx);377disable_slave:378	clk_disable_unprepare(eqos->clk_slave);379disable_master:380	clk_disable_unprepare(eqos->clk_master);381error:382	return err;383}384 385static void tegra_eqos_remove(struct platform_device *pdev)386{387	struct tegra_eqos *eqos = get_stmmac_bsp_priv(&pdev->dev);388 389	reset_control_assert(eqos->rst);390	gpiod_set_value(eqos->reset, 1);391	clk_disable_unprepare(eqos->clk_tx);392	clk_disable_unprepare(eqos->clk_rx);393	clk_disable_unprepare(eqos->clk_slave);394	clk_disable_unprepare(eqos->clk_master);395}396 397struct dwc_eth_dwmac_data {398	int (*probe)(struct platform_device *pdev,399		     struct plat_stmmacenet_data *data,400		     struct stmmac_resources *res);401	void (*remove)(struct platform_device *pdev);402};403 404static const struct dwc_eth_dwmac_data dwc_qos_data = {405	.probe = dwc_qos_probe,406	.remove = dwc_qos_remove,407};408 409static const struct dwc_eth_dwmac_data tegra_eqos_data = {410	.probe = tegra_eqos_probe,411	.remove = tegra_eqos_remove,412};413 414static int dwc_eth_dwmac_probe(struct platform_device *pdev)415{416	const struct dwc_eth_dwmac_data *data;417	struct plat_stmmacenet_data *plat_dat;418	struct stmmac_resources stmmac_res;419	int ret;420 421	data = device_get_match_data(&pdev->dev);422 423	memset(&stmmac_res, 0, sizeof(struct stmmac_resources));424 425	/**426	 * Since stmmac_platform supports name IRQ only, basic platform427	 * resource initialization is done in the glue logic.428	 */429	stmmac_res.irq = platform_get_irq(pdev, 0);430	if (stmmac_res.irq < 0)431		return stmmac_res.irq;432	stmmac_res.wol_irq = stmmac_res.irq;433 434	stmmac_res.addr = devm_platform_ioremap_resource(pdev, 0);435	if (IS_ERR(stmmac_res.addr))436		return PTR_ERR(stmmac_res.addr);437 438	plat_dat = devm_stmmac_probe_config_dt(pdev, stmmac_res.mac);439	if (IS_ERR(plat_dat))440		return PTR_ERR(plat_dat);441 442	ret = data->probe(pdev, plat_dat, &stmmac_res);443	if (ret < 0) {444		dev_err_probe(&pdev->dev, ret, "failed to probe subdriver\n");445		return ret;446	}447 448	ret = dwc_eth_dwmac_config_dt(pdev, plat_dat);449	if (ret)450		goto remove;451 452	ret = stmmac_dvr_probe(&pdev->dev, plat_dat, &stmmac_res);453	if (ret)454		goto remove;455 456	return ret;457 458remove:459	data->remove(pdev);460 461	return ret;462}463 464static void dwc_eth_dwmac_remove(struct platform_device *pdev)465{466	const struct dwc_eth_dwmac_data *data = device_get_match_data(&pdev->dev);467 468	stmmac_dvr_remove(&pdev->dev);469 470	data->remove(pdev);471}472 473static const struct of_device_id dwc_eth_dwmac_match[] = {474	{ .compatible = "snps,dwc-qos-ethernet-4.10", .data = &dwc_qos_data },475	{ .compatible = "nvidia,tegra186-eqos", .data = &tegra_eqos_data },476	{ }477};478MODULE_DEVICE_TABLE(of, dwc_eth_dwmac_match);479 480static struct platform_driver dwc_eth_dwmac_driver = {481	.probe  = dwc_eth_dwmac_probe,482	.remove_new = dwc_eth_dwmac_remove,483	.driver = {484		.name           = "dwc-eth-dwmac",485		.pm             = &stmmac_pltfr_pm_ops,486		.of_match_table = dwc_eth_dwmac_match,487	},488};489module_platform_driver(dwc_eth_dwmac_driver);490 491MODULE_AUTHOR("Joao Pinto <jpinto@synopsys.com>");492MODULE_DESCRIPTION("Synopsys DWC Ethernet Quality-of-Service v4.10a driver");493MODULE_LICENSE("GPL v2");494