brintos

brintos / linux-shallow public Read only

0
0
Text · 7.3 KiB · 26e86cd Raw
321 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 * drivers/net/ethernet/ibm/emac/zmii.c4 *5 * Driver for PowerPC 4xx on-chip ethernet controller, ZMII bridge support.6 *7 * Copyright 2007 Benjamin Herrenschmidt, IBM Corp.8 *                <benh@kernel.crashing.org>9 *10 * Based on the arch/ppc version of the driver:11 *12 * Copyright (c) 2004, 2005 Zultys Technologies.13 * Eugene Surovegin <eugene.surovegin@zultys.com> or <ebs@ebshome.net>14 *15 * Based on original work by16 *      Armin Kuster <akuster@mvista.com>17 * 	Copyright 2001 MontaVista Softare Inc.18 */19#include <linux/slab.h>20#include <linux/kernel.h>21#include <linux/ethtool.h>22#include <linux/mod_devicetable.h>23#include <linux/of_address.h>24#include <linux/platform_device.h>25#include <asm/io.h>26 27#include "emac.h"28#include "core.h"29 30/* ZMIIx_FER */31#define ZMII_FER_MDI(idx)	(0x80000000 >> ((idx) * 4))32#define ZMII_FER_MDI_ALL	(ZMII_FER_MDI(0) | ZMII_FER_MDI(1) | \33				 ZMII_FER_MDI(2) | ZMII_FER_MDI(3))34 35#define ZMII_FER_SMII(idx)	(0x40000000 >> ((idx) * 4))36#define ZMII_FER_RMII(idx)	(0x20000000 >> ((idx) * 4))37#define ZMII_FER_MII(idx)	(0x10000000 >> ((idx) * 4))38 39/* ZMIIx_SSR */40#define ZMII_SSR_SCI(idx)	(0x40000000 >> ((idx) * 4))41#define ZMII_SSR_FSS(idx)	(0x20000000 >> ((idx) * 4))42#define ZMII_SSR_SP(idx)	(0x10000000 >> ((idx) * 4))43 44/* ZMII only supports MII, RMII and SMII45 * we also support autodetection for backward compatibility46 */47static inline int zmii_valid_mode(int mode)48{49	return  mode == PHY_INTERFACE_MODE_MII ||50		mode == PHY_INTERFACE_MODE_RMII ||51		mode == PHY_INTERFACE_MODE_SMII ||52		mode == PHY_INTERFACE_MODE_NA;53}54 55static inline const char *zmii_mode_name(int mode)56{57	switch (mode) {58	case PHY_INTERFACE_MODE_MII:59		return "MII";60	case PHY_INTERFACE_MODE_RMII:61		return "RMII";62	case PHY_INTERFACE_MODE_SMII:63		return "SMII";64	default:65		BUG();66	}67}68 69static inline u32 zmii_mode_mask(int mode, int input)70{71	switch (mode) {72	case PHY_INTERFACE_MODE_MII:73		return ZMII_FER_MII(input);74	case PHY_INTERFACE_MODE_RMII:75		return ZMII_FER_RMII(input);76	case PHY_INTERFACE_MODE_SMII:77		return ZMII_FER_SMII(input);78	default:79		return 0;80	}81}82 83int zmii_attach(struct platform_device *ofdev, int input,84		phy_interface_t *mode)85{86	struct zmii_instance *dev = platform_get_drvdata(ofdev);87	struct zmii_regs __iomem *p = dev->base;88 89	ZMII_DBG(dev, "init(%d, %d)" NL, input, *mode);90 91	if (!zmii_valid_mode(*mode)) {92		/* Probably an EMAC connected to RGMII,93		 * but it still may need ZMII for MDIO so94		 * we don't fail here.95		 */96		dev->users++;97		return 0;98	}99 100	mutex_lock(&dev->lock);101 102	/* Autodetect ZMII mode if not specified.103	 * This is only for backward compatibility with the old driver.104	 * Please, always specify PHY mode in your board port to avoid105	 * any surprises.106	 */107	if (dev->mode == PHY_INTERFACE_MODE_NA) {108		if (*mode == PHY_INTERFACE_MODE_NA) {109			u32 r = dev->fer_save;110 111			ZMII_DBG(dev, "autodetecting mode, FER = 0x%08x" NL, r);112 113			if (r & (ZMII_FER_MII(0) | ZMII_FER_MII(1)))114				dev->mode = PHY_INTERFACE_MODE_MII;115			else if (r & (ZMII_FER_RMII(0) | ZMII_FER_RMII(1)))116				dev->mode = PHY_INTERFACE_MODE_RMII;117			else118				dev->mode = PHY_INTERFACE_MODE_SMII;119		} else {120			dev->mode = *mode;121		}122		printk(KERN_NOTICE "%pOF: bridge in %s mode\n",123		       ofdev->dev.of_node,124		       zmii_mode_name(dev->mode));125	} else {126		/* All inputs must use the same mode */127		if (*mode != PHY_INTERFACE_MODE_NA && *mode != dev->mode) {128			printk(KERN_ERR129			       "%pOF: invalid mode %d specified for input %d\n",130			       ofdev->dev.of_node, *mode, input);131			mutex_unlock(&dev->lock);132			return -EINVAL;133		}134	}135 136	/* Report back correct PHY mode,137	 * it may be used during PHY initialization.138	 */139	*mode = dev->mode;140 141	/* Enable this input */142	out_be32(&p->fer, in_be32(&p->fer) | zmii_mode_mask(dev->mode, input));143	++dev->users;144 145	mutex_unlock(&dev->lock);146 147	return 0;148}149 150void zmii_get_mdio(struct platform_device *ofdev, int input)151{152	struct zmii_instance *dev = platform_get_drvdata(ofdev);153	u32 fer;154 155	ZMII_DBG2(dev, "get_mdio(%d)" NL, input);156 157	mutex_lock(&dev->lock);158 159	fer = in_be32(&dev->base->fer) & ~ZMII_FER_MDI_ALL;160	out_be32(&dev->base->fer, fer | ZMII_FER_MDI(input));161}162 163void zmii_put_mdio(struct platform_device *ofdev, int input)164{165	struct zmii_instance *dev = platform_get_drvdata(ofdev);166 167	ZMII_DBG2(dev, "put_mdio(%d)" NL, input);168	mutex_unlock(&dev->lock);169}170 171 172void zmii_set_speed(struct platform_device *ofdev, int input, int speed)173{174	struct zmii_instance *dev = platform_get_drvdata(ofdev);175	u32 ssr;176 177	mutex_lock(&dev->lock);178 179	ssr = in_be32(&dev->base->ssr);180 181	ZMII_DBG(dev, "speed(%d, %d)" NL, input, speed);182 183	if (speed == SPEED_100)184		ssr |= ZMII_SSR_SP(input);185	else186		ssr &= ~ZMII_SSR_SP(input);187 188	out_be32(&dev->base->ssr, ssr);189 190	mutex_unlock(&dev->lock);191}192 193void zmii_detach(struct platform_device *ofdev, int input)194{195	struct zmii_instance *dev = platform_get_drvdata(ofdev);196 197	BUG_ON(!dev || dev->users == 0);198 199	mutex_lock(&dev->lock);200 201	ZMII_DBG(dev, "detach(%d)" NL, input);202 203	/* Disable this input */204	out_be32(&dev->base->fer,205		 in_be32(&dev->base->fer) & ~zmii_mode_mask(dev->mode, input));206 207	--dev->users;208 209	mutex_unlock(&dev->lock);210}211 212int zmii_get_regs_len(struct platform_device *ofdev)213{214	return sizeof(struct emac_ethtool_regs_subhdr) +215		sizeof(struct zmii_regs);216}217 218void *zmii_dump_regs(struct platform_device *ofdev, void *buf)219{220	struct zmii_instance *dev = platform_get_drvdata(ofdev);221	struct emac_ethtool_regs_subhdr *hdr = buf;222	struct zmii_regs *regs = (struct zmii_regs *)(hdr + 1);223 224	hdr->version = 0;225	hdr->index = 0; /* for now, are there chips with more than one226			 * zmii ? if yes, then we'll add a cell_index227			 * like we do for emac228			 */229	memcpy_fromio(regs, dev->base, sizeof(struct zmii_regs));230	return regs + 1;231}232 233static int zmii_probe(struct platform_device *ofdev)234{235	struct device_node *np = ofdev->dev.of_node;236	struct zmii_instance *dev;237	struct resource regs;238	int rc;239 240	rc = -ENOMEM;241	dev = kzalloc(sizeof(struct zmii_instance), GFP_KERNEL);242	if (dev == NULL)243		goto err_gone;244 245	mutex_init(&dev->lock);246	dev->ofdev = ofdev;247	dev->mode = PHY_INTERFACE_MODE_NA;248 249	rc = -ENXIO;250	if (of_address_to_resource(np, 0, &regs)) {251		printk(KERN_ERR "%pOF: Can't get registers address\n", np);252		goto err_free;253	}254 255	rc = -ENOMEM;256	dev->base = (struct zmii_regs __iomem *)ioremap(regs.start,257						sizeof(struct zmii_regs));258	if (dev->base == NULL) {259		printk(KERN_ERR "%pOF: Can't map device registers!\n", np);260		goto err_free;261	}262 263	/* We may need FER value for autodetection later */264	dev->fer_save = in_be32(&dev->base->fer);265 266	/* Disable all inputs by default */267	out_be32(&dev->base->fer, 0);268 269	printk(KERN_INFO "ZMII %pOF initialized\n", ofdev->dev.of_node);270	wmb();271	platform_set_drvdata(ofdev, dev);272 273	return 0;274 275 err_free:276	kfree(dev);277 err_gone:278	return rc;279}280 281static void zmii_remove(struct platform_device *ofdev)282{283	struct zmii_instance *dev = platform_get_drvdata(ofdev);284 285	WARN_ON(dev->users != 0);286 287	iounmap(dev->base);288	kfree(dev);289}290 291static const struct of_device_id zmii_match[] =292{293	{294		.compatible	= "ibm,zmii",295	},296	/* For backward compat with old DT */297	{298		.type		= "emac-zmii",299	},300	{},301};302 303static struct platform_driver zmii_driver = {304	.driver = {305		.name = "emac-zmii",306		.of_match_table = zmii_match,307	},308	.probe = zmii_probe,309	.remove_new = zmii_remove,310};311 312int __init zmii_init(void)313{314	return platform_driver_register(&zmii_driver);315}316 317void zmii_exit(void)318{319	platform_driver_unregister(&zmii_driver);320}321