lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite for Android: free password hash cracker in your pocket
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Date:	Thu, 21 May 2015 18:07:06 -0700
From:	Ray Jui <rjui@...adcom.com>
To:	Kishon Vijay Abraham I <kishon@...com>,
	Arnd Bergmann <arnd@...db.de>
CC:	<linux-kernel@...r.kernel.org>,
	"JD (Jiandong) Zheng" <jdzheng@...adcom.com>,
	Arun Parameswaran <arunp@...adcom.com>,
	<bcm-kernel-feedback-list@...adcom.com>,
	Ray Jui <rjui@...adcom.com>
Subject: [PATCH v2 2/5] phy: iproc-mdio: Initial iProc MDC/MDIO support

This adds the support for the iProc MDC/MDIO bus interface. Various
iProc SoCs can use the MDC/MDIO bus interface to communicate with
various Serdes/PHYs including Ethernet, PCIe, USB, etc.

Signed-off-by: Ray Jui <rjui@...adcom.com>
Reviewed-by: Arun Parameswaran <aparames@...adcom.com>
Reviewed-by: JD (Jiandong) Zheng <jdzheng@...adcom.com>
Reviewed-by: Scott Branden <sbranden@...adcom.com>
---
 drivers/bus/Kconfig            |   12 ++
 drivers/bus/Makefile           |    1 +
 drivers/bus/iproc-mdio-bus.c   |  255 ++++++++++++++++++++++++++++++++++++++++
 include/linux/iproc_mdio_bus.h |   22 ++++
 4 files changed, 290 insertions(+)
 create mode 100644 drivers/bus/iproc-mdio-bus.c
 create mode 100644 include/linux/iproc_mdio_bus.h

diff --git a/drivers/bus/Kconfig b/drivers/bus/Kconfig
index a1d4af6..99b38cb 100644
--- a/drivers/bus/Kconfig
+++ b/drivers/bus/Kconfig
@@ -55,6 +55,18 @@ config IMX_WEIM
 	  The WEIM(Wireless External Interface Module) works like a bus.
 	  You can attach many different devices on it, such as NOR, onenand.
 
+config IPROC_MDIO
+	bool "Broadcom iProc MDC/MDIO driver"
+	depends on ARCH_BCM_IPROC
+	default ARCH_BCM_IPROC
+	help
+	  Enable this to support the iProc generic MDC/MDIO bus interface found
+	  in various iProc based SoCs. The generic MDC/MDIO bus interface can
+	  be used to communicate with various types of Serdes/PHYs including
+	  Ethernet, PCIe, USB, and etc.
+
+	  If unsure, say N.
+
 config MIPS_CDMM
 	bool "MIPS Common Device Memory Map (CDMM) Driver"
 	depends on CPU_MIPSR2
diff --git a/drivers/bus/Makefile b/drivers/bus/Makefile
index 790e7b9..6dff76a 100644
--- a/drivers/bus/Makefile
+++ b/drivers/bus/Makefile
@@ -8,6 +8,7 @@ obj-$(CONFIG_ARM_CCN)		+= arm-ccn.o
 
 obj-$(CONFIG_BRCMSTB_GISB_ARB)	+= brcmstb_gisb.o
 obj-$(CONFIG_IMX_WEIM)		+= imx-weim.o
+obj-$(CONFIG_IPROC_MDIO)	+= iproc-mdio-bus.o
 obj-$(CONFIG_MIPS_CDMM)		+= mips_cdmm.o
 obj-$(CONFIG_MVEBU_MBUS) 	+= mvebu-mbus.o
 
diff --git a/drivers/bus/iproc-mdio-bus.c b/drivers/bus/iproc-mdio-bus.c
new file mode 100644
index 0000000..ae91722
--- /dev/null
+++ b/drivers/bus/iproc-mdio-bus.c
@@ -0,0 +1,255 @@
+/*
+ * Copyright (C) 2015 Broadcom Corporation
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation version 2.
+ *
+ * This program is distributed "as is" WITHOUT ANY WARRANTY of any
+ * kind, whether express or implied; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/of_platform.h>
+#include <linux/iproc_mdio_bus.h>
+
+#define MDIO_TIMEOUT_MSEC             10
+
+#define MDC_CTRL_OFFSET               0x000
+
+#define MDC_CTRL_DIV_SHIFT            0
+#define MDC_CTRL_DIV_WIDTH            7
+#define MDC_MAX_DIV                   (BIT(MDC_CTRL_DIV_WIDTH) - 1)
+#define MDC_CTRL_PRE_SHIFT            7
+#define MDC_CTRL_BUSY_SHIFT           8
+#define MDC_CTRL_EXT_SHIFT            9
+#define MDC_CTRL_BTP_SHIFT            10
+
+#define MDC_DATA_OFFSET               0x004
+
+#define MDC_DATA_SHIFT                0
+#define MDC_DATA_MASK                 0xffff
+
+#define MDC_DATA_TA_SHIFT             16
+#define MDC_DATA_TA_VAL               2
+
+#define MDC_DATA_RA_SHIFT             18
+#define MDC_DATA_RA_WIDTH             5
+#define MDC_MAX_RA                    (BIT(MDC_DATA_RA_WIDTH) - 1)
+
+#define MDC_DATA_PA_SHIFT             23
+#define MDC_DATA_PA_WIDTH             5
+#define MDC_MAX_PA                    (BIT(MDC_DATA_PA_WIDTH) - 1)
+
+#define MDC_DATA_OP_SHIFT             28
+#define MDC_DATA_OP_WRITE             1
+#define MDC_DATA_OP_READ              2
+
+#define MDC_DATA_SB_SHIFT             30
+
+/**
+ * struct iproc_mdc - iProc MDC/MDIO bus interface
+ * @dev: pointer to device
+ * @base: MDC controller register base pointer
+ * @lock: mutex to protect access to the MDC device
+ * @is_ready: flag to indicate the driver has been initialized and is ready for
+ * use
+ */
+struct iproc_mdc {
+	struct device *dev;
+	void __iomem *base;
+	struct mutex lock;
+	bool is_ready;
+};
+
+static struct iproc_mdc iproc_mdc;
+
+static inline int iproc_mdio_check_params(unsigned clk_div, unsigned pa,
+					  unsigned ra)
+{
+	if (clk_div > MDC_MAX_DIV || pa > MDC_MAX_PA || ra > MDC_MAX_RA)
+		return -EINVAL;
+
+	return 0;
+}
+
+static inline int iproc_mdio_wait_for_idle(void __iomem *base)
+{
+	u32 val;
+	unsigned long timeout = jiffies + msecs_to_jiffies(MDIO_TIMEOUT_MSEC);
+
+	while (!time_after(jiffies, timeout)) {
+		val = readl(base + MDC_CTRL_OFFSET);
+		if ((val & BIT(MDC_CTRL_BUSY_SHIFT)) == 0)
+			return 0;
+
+		cpu_relax();
+	}
+
+	return -ETIMEDOUT;
+}
+
+static inline void iproc_mdio_config_clk(void __iomem *base, unsigned clk_div)
+{
+	u32 val;
+
+	val = (clk_div << MDC_CTRL_DIV_SHIFT) | BIT(MDC_CTRL_PRE_SHIFT);
+	writel(val, base + MDC_CTRL_OFFSET);
+}
+
+static int iproc_mdio_read_write(void __iomem *base, bool dir_is_write,
+				 unsigned clk_div, unsigned pa, unsigned ra,
+				 u16 *data)
+{
+	int ret;
+	u32 val;
+
+	ret = iproc_mdio_check_params(clk_div, pa, ra);
+	if (ret)
+		return ret;
+
+	ret = iproc_mdio_wait_for_idle(base);
+	if (ret)
+		return ret;
+
+	iproc_mdio_config_clk(base, clk_div);
+
+	ret = iproc_mdio_wait_for_idle(base);
+	if (ret)
+		return ret;
+
+	val = (MDC_DATA_TA_VAL << MDC_DATA_TA_SHIFT) |
+		(ra << MDC_DATA_RA_SHIFT) |
+		(pa << MDC_DATA_PA_SHIFT) |
+		BIT(MDC_DATA_SB_SHIFT);
+
+	if (dir_is_write) {
+		val |= (MDC_DATA_OP_WRITE << MDC_DATA_OP_SHIFT);
+		val |= ((u32)(*data) & MDC_DATA_MASK);
+	} else {
+		val |= (MDC_DATA_OP_READ << MDC_DATA_OP_SHIFT);
+	}
+
+	writel(val, base + MDC_DATA_OFFSET);
+
+	ret = iproc_mdio_wait_for_idle(base);
+	if (ret)
+		return ret;
+
+	if (!dir_is_write) {
+		val = readl(base + MDC_DATA_OFFSET) & MDC_DATA_MASK;
+		*data = (u16)val;
+	}
+
+	return 0;
+}
+
+
+/**
+ * iproc_mdio_read() - read from a PHY register through the MDC interface
+ *
+ * @clk_div: MDC clock divisor
+ * @phy_addr: MDC PHY address
+ * @reg_addr: PHY register address
+ * @data: pointer to the memory where data will be stored
+ */
+int iproc_mdio_read(unsigned clk_div, unsigned phy_addr, unsigned reg_addr,
+		    u16 *data)
+{
+	int ret;
+	struct iproc_mdc *mdc = &iproc_mdc;
+
+	if (!mdc->is_ready)
+		return -ENODEV;
+
+	mutex_lock(&mdc->lock);
+	ret = iproc_mdio_read_write(mdc->base, false, clk_div, phy_addr,
+				    reg_addr, data);
+	if (ret)
+		dev_err(mdc->dev, "mdio read failed\n");
+	mutex_unlock(&mdc->lock);
+
+	return ret;
+}
+EXPORT_SYMBOL(iproc_mdio_read);
+
+/**
+ * iproc_mdio_write() - write to a PHY register through the MDC interface
+ *
+ * @clk_div: MDC clock divisor
+ * @phy_addr: MDC PHY address
+ * @reg_addr: PHY register address
+ * @data: data value to be written to the PHY register
+ */
+int iproc_mdio_write(unsigned clk_div, unsigned phy_addr, unsigned reg_addr,
+		     u16 data)
+{
+	int ret;
+	struct iproc_mdc *mdc = &iproc_mdc;
+
+	if (!mdc->is_ready)
+		return -ENODEV;
+
+	mutex_lock(&mdc->lock);
+	ret = iproc_mdio_read_write(mdc->base, true, clk_div, phy_addr,
+				    reg_addr, &data);
+	if (ret)
+		dev_err(mdc->dev, "mdio write failed\n");
+	mutex_unlock(&mdc->lock);
+
+	return ret;
+}
+EXPORT_SYMBOL(iproc_mdio_write);
+
+static int iproc_mdio_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct device_node *np = dev->of_node;
+	struct iproc_mdc *mdc = &iproc_mdc;
+	struct resource *res;
+
+	mdc->dev = dev;
+	mutex_init(&mdc->lock);
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	mdc->base = devm_ioremap_resource(dev, res);
+	if (IS_ERR(mdc->base))
+		return PTR_ERR(mdc->base);
+
+	mdc->is_ready = true;
+
+	if (np)
+		of_platform_populate(np, NULL, NULL, dev);
+
+	return 0;
+}
+
+static const struct of_device_id iproc_mdio_match_table[] = {
+	{ .compatible = "brcm,iproc-mdio" },
+	{ }
+};
+
+static struct platform_driver iproc_mdio_driver = {
+	.driver = {
+		.name = "iproc-mdio",
+		.of_match_table = iproc_mdio_match_table,
+		.suppress_bind_attrs = true,
+	},
+	.probe	= iproc_mdio_probe,
+};
+
+static int __init iproc_mdio_init(void)
+{
+	return platform_driver_register(&iproc_mdio_driver);
+}
+arch_initcall_sync(iproc_mdio_init);
+
+MODULE_AUTHOR("Ray Jui <rjui@...adcom.com>");
+MODULE_DESCRIPTION("Broadcom iPROC MDC/MDIO driver");
+MODULE_LICENSE("GPL v2");
diff --git a/include/linux/iproc_mdio_bus.h b/include/linux/iproc_mdio_bus.h
new file mode 100644
index 0000000..4d1235a
--- /dev/null
+++ b/include/linux/iproc_mdio_bus.h
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2015 Broadcom Corporation
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation version 2.
+ *
+ * This program is distributed "as is" WITHOUT ANY WARRANTY of any
+ * kind, whether express or implied; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __IPROC_MDIO_BUS_H
+#define __IPROC_MDIO_BUS_H
+
+int iproc_mdio_read(unsigned clk_div, unsigned phy_addr, unsigned reg_addr,
+		    u16 *data);
+int iproc_mdio_write(unsigned clk_div, unsigned phy_addr, unsigned reg_addr,
+		     u16 data);
+
+#endif /* __IPROC_MDIO_BUS_H */
-- 
1.7.9.5

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@...r.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ