Return-Path: Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1752655AbbETBX5 (ORCPT ); Tue, 19 May 2015 21:23:57 -0400 Received: from mail-gw1-out.broadcom.com ([216.31.210.62]:62607 "EHLO mail-gw1-out.broadcom.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1752456AbbETBXv (ORCPT ); Tue, 19 May 2015 21:23:51 -0400 X-IronPort-AV: E=Sophos;i="5.13,461,1427785200"; d="scan'208";a="65468589" From: Ray Jui To: Kishon Vijay Abraham I CC: , "JD (Jiandong) Zheng" , Arun Parameswaran , , Ray Jui Subject: [PATCH 2/5] phy: iproc-mdio: Initial iProc MDC/MDIO support Date: Tue, 19 May 2015 18:23:31 -0700 Message-ID: <1432085014-20758-3-git-send-email-rjui@broadcom.com> X-Mailer: git-send-email 1.7.9.5 In-Reply-To: <1432085014-20758-1-git-send-email-rjui@broadcom.com> References: <1432085014-20758-1-git-send-email-rjui@broadcom.com> MIME-Version: 1.0 Content-Type: text/plain Sender: linux-kernel-owner@vger.kernel.org List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Length: 9890 Lines: 343 This adds the support for the iProc MDC/MDIO interface. Multiple iProc SoCs contain the MDC/MDIO interface that can be used for the host to communicate with various Serdes/PHYs including Ethernet, PCIe, USB, etc. Signed-off-by: Ray Jui Reviewed-by: Arun Parameswaran Reviewed-by: JD (Jiandong) Zheng Reviewed-by: Scott Branden --- drivers/phy/Kconfig | 12 ++ drivers/phy/Makefile | 1 + drivers/phy/phy-iproc-mdio.c | 249 ++++++++++++++++++++++++++++++++++++ include/linux/phy/iproc_mdio_phy.h | 22 ++++ 4 files changed, 284 insertions(+) create mode 100644 drivers/phy/phy-iproc-mdio.c create mode 100644 include/linux/phy/iproc_mdio_phy.h diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig index a53bd5b..2664285 100644 --- a/drivers/phy/Kconfig +++ b/drivers/phy/Kconfig @@ -52,6 +52,18 @@ config PHY_EXYNOS_MIPI_VIDEO Support for MIPI CSI-2 and MIPI DSI DPHY found on Samsung S5P and EXYNOS SoCs. +config PHY_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 interface found + in various iProc based SoCs. The generic MDC/MDIO interface can be + used to communicate with various types of Serdes/PHYs including + Ethernet, PCIe, USB, and etc. + + If unsure, say N. + config PHY_MVEBU_SATA def_bool y depends on ARCH_DOVE || MACH_DOVE || MACH_KIRKWOOD diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile index f126251..d989dd7b 100644 --- a/drivers/phy/Makefile +++ b/drivers/phy/Makefile @@ -13,6 +13,7 @@ obj-$(CONFIG_PHY_EXYNOS_MIPI_VIDEO) += phy-exynos-mipi-video.o obj-$(CONFIG_PHY_MVEBU_SATA) += phy-mvebu-sata.o obj-$(CONFIG_PHY_MIPHY28LP) += phy-miphy28lp.o obj-$(CONFIG_PHY_MIPHY365X) += phy-miphy365x.o +obj-$(CONFIG_PHY_IPROC_MDIO) += phy-iproc-mdio.o obj-$(CONFIG_PHY_RCAR_GEN2) += phy-rcar-gen2.o obj-$(CONFIG_OMAP_CONTROL_PHY) += phy-omap-control.o obj-$(CONFIG_OMAP_USB2) += phy-omap-usb2.o diff --git a/drivers/phy/phy-iproc-mdio.c b/drivers/phy/phy-iproc-mdio.c new file mode 100644 index 0000000..a37e92f --- /dev/null +++ b/drivers/phy/phy-iproc-mdio.c @@ -0,0 +1,249 @@ +/* + * 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 +#include +#include +#include +#include +#include + +#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 device + * @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 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; + 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 "); +MODULE_DESCRIPTION("Broadcom iPROC MDC/MDIO driver"); +MODULE_LICENSE("GPL v2"); diff --git a/include/linux/phy/iproc_mdio_phy.h b/include/linux/phy/iproc_mdio_phy.h new file mode 100644 index 0000000..d3e88ce --- /dev/null +++ b/include/linux/phy/iproc_mdio_phy.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_PHY_H +#define __IPROC_MDIO_PHY_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_PHY_H */ -- 1.7.9.5 -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/