From patchwork Thu Apr 21 09:18:40 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Pramod Kumar X-Patchwork-Id: 8898011 Return-Path: X-Original-To: patchwork-linux-arm@patchwork.kernel.org Delivered-To: patchwork-parsemail@patchwork2.web.kernel.org Received: from mail.kernel.org (mail.kernel.org [198.145.29.136]) by patchwork2.web.kernel.org (Postfix) with ESMTP id F0D9ABF29F for ; Thu, 21 Apr 2016 09:20:50 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id B761F20256 for ; Thu, 21 Apr 2016 09:20:49 +0000 (UTC) Received: from bombadil.infradead.org (bombadil.infradead.org [198.137.202.9]) (using TLSv1.2 with cipher AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPS id 72E48200DF for ; Thu, 21 Apr 2016 09:20:48 +0000 (UTC) Received: from localhost ([127.0.0.1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.80.1 #2 (Red Hat Linux)) id 1atAlJ-0001rI-Vr; Thu, 21 Apr 2016 09:19:02 +0000 Received: from mail-gw3-out.broadcom.com ([216.31.210.64]) by bombadil.infradead.org with esmtp (Exim 4.80.1 #2 (Red Hat Linux)) id 1atAlB-0001lT-Ry for linux-arm-kernel@lists.infradead.org; Thu, 21 Apr 2016 09:18:55 +0000 X-IronPort-AV: E=Sophos;i="5.24,512,1455004800"; d="scan'208";a="93233591" Received: from mail-irv-18.broadcom.com ([10.15.198.37]) by mail-gw3-out.broadcom.com with ESMTP; 21 Apr 2016 02:30:48 -0700 Received: from mail-irva-12.broadcom.com (mail-irva-12.broadcom.com [10.11.16.101]) by mail-irv-18.broadcom.com (Postfix) with ESMTP id B09FB82023; Thu, 21 Apr 2016 02:18:53 -0700 (PDT) Received: from pramodku-OptiPlex-7010.ban.broadcom.com (unknown [10.132.81.79]) by mail-irva-12.broadcom.com (Postfix) with ESMTP id 10F63127644; Thu, 21 Apr 2016 02:18:31 -0700 (PDT) From: Pramod Kumar To: Rob Herring , Catalin Marinas , Will Deacon , Masahiro Yamada , Chen-Yu Tsai Subject: [PATCH 3/6] bus: Add platform driver for iProc shared MDIO Controller Date: Thu, 21 Apr 2016 14:48:40 +0530 Message-Id: <1461230323-27891-4-git-send-email-pramod.kumar@broadcom.com> X-Mailer: git-send-email 1.9.1 In-Reply-To: <1461230323-27891-1-git-send-email-pramod.kumar@broadcom.com> References: <1461230323-27891-1-git-send-email-pramod.kumar@broadcom.com> X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20160421_021854_074850_48995427 X-CRM114-Status: GOOD ( 22.85 ) X-Spam-Score: -5.2 (-----) X-BeenThere: linux-arm-kernel@lists.infradead.org X-Mailman-Version: 2.1.20 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: Mark Rutland , devicetree@vger.kernel.org, Pawel Moll , Arnd Bergmann , Suzuki K Poulose , netdev@vger.kernel.org, Punit Agrawal , linux-kernel@vger.kernel.org, Pramod Kumar , BCM Kernel Feedback , linux-arm-kernel@lists.infradead.org MIME-Version: 1.0 Sender: "linux-arm-kernel" Errors-To: linux-arm-kernel-bounces+patchwork-linux-arm=patchwork.kernel.org@lists.infradead.org X-Spam-Status: No, score=-5.2 required=5.0 tests=BAYES_00, RCVD_IN_DNSWL_MED, RP_MATCHES_RCVD, UNPARSEABLE_RELAY autolearn=unavailable version=3.3.1 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on mail.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP Add platform driver to populate shared MDIO masters on iProc SoC. Signed-off-by: Pramod Kumar Reviewed-by: Anup Patel Reviewed-by: Ray Jui Reviewed-by: Scott Branden Reviewed-by: Vikram Prakash --- drivers/bus/Kconfig | 11 ++ drivers/bus/Makefile | 1 + drivers/bus/iproc_shared_mdio.c | 287 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 299 insertions(+) create mode 100644 drivers/bus/iproc_shared_mdio.c diff --git a/drivers/bus/Kconfig b/drivers/bus/Kconfig index 1b51b1a..c3f2cb9 100644 --- a/drivers/bus/Kconfig +++ b/drivers/bus/Kconfig @@ -118,6 +118,17 @@ config SHARED_MDIO Respective drivers would be called matching a compatible string provided in master node. +config IPROC_SHARED_MDIO + tristate "iProc Shared MDIO Driver" + depends on OF + select SHARED_MDIO + default ARCH_BCM_IPROC + help + Platform driver for shared MDIO controller. Broadcom's iProc based + SoCs have many masters which uses one shared bus to accessed its PHY. + This platform driver populates all master to Shared bus and provide + the MDIO controller specific implementation. + config SIMPLE_PM_BUS bool "Simple Power-Managed Bus Driver" depends on OF && PM diff --git a/drivers/bus/Makefile b/drivers/bus/Makefile index 2b6d6e9..3e364da 100644 --- a/drivers/bus/Makefile +++ b/drivers/bus/Makefile @@ -20,3 +20,4 @@ obj-$(CONFIG_SIMPLE_PM_BUS) += simple-pm-bus.o obj-$(CONFIG_UNIPHIER_SYSTEM_BUS) += uniphier-system-bus.o obj-$(CONFIG_VEXPRESS_CONFIG) += vexpress-config.o obj-$(CONFIG_SHARED_MDIO) += shared_mdio.o +obj-$(CONFIG_IPROC_SHARED_MDIO) += iproc_shared_mdio.o diff --git a/drivers/bus/iproc_shared_mdio.c b/drivers/bus/iproc_shared_mdio.c new file mode 100644 index 0000000..af232eb --- /dev/null +++ b/drivers/bus/iproc_shared_mdio.c @@ -0,0 +1,287 @@ +/* + * Copyright (C) 2016 Broadcom + * + * 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. + * + * Platform driver for Shared MDIO Masters on iProc SoC + */ + +#include +#include +#include +#include +#include +#include +#include + +struct shared_mdio_master_param { + bool is_internal; + bool is_c45; + unsigned int master_id; +}; + +struct iproc_shared_mdio { + struct device *dev; + struct mutex lock; + void __iomem *regs; + unsigned int num_masters; + struct shared_mdio_master **masters; +}; + +#define PHY_INTERNAL 1 +#define CLAUSE_45 1 + +#define MDIO_PARAM_OFFSET 0x00 +#define MDIO_PARAM_MIIM_CYCLE 29 +#define MDIO_PARAM_INTERNAL_SEL 25 +#define MDIO_PARAM_BUS_ID 22 +#define MDIO_PARAM_C45_SEL 21 +#define MDIO_PARAM_PHY_ID 16 +#define MDIO_PARAM_PHY_DATA 0 + +#define MDIO_READ_OFFSET 0x04 +#define MDIO_READ_DATA_MASK 0xffff +#define MDIO_ADDR_OFFSET 0x08 + +#define MDIO_CTRL_OFFSET 0x0C +#define MDIO_CTRL_WRITE_OP 0x1 +#define MDIO_CTRL_READ_OP 0x2 + +#define MDIO_STAT_OFFSET 0x10 +#define MDIO_STAT_DONE 1 + +static int iproc_mdio_wait_for_idle(void __iomem *base, bool result) +{ + u32 val; + unsigned int timeout = 1000; /* loop for 1s */ + + do { + val = readl(base + MDIO_STAT_OFFSET); + if ((val & MDIO_STAT_DONE) == result) + return 0; + + usleep_range(1000, 2000); + } while (timeout--); + + return -ETIMEDOUT; +} + +static inline u32 get_cmd(bool con, u16 mid, bool is_c45, u16 phyid, u16 val) +{ + u32 cmd; + + cmd = (con ? 1 : 0) << MDIO_PARAM_INTERNAL_SEL; + cmd |= mid << MDIO_PARAM_BUS_ID; + cmd |= (is_c45 ? 1 : 0) << MDIO_PARAM_C45_SEL; + cmd |= phyid << MDIO_PARAM_PHY_ID; + cmd |= val << MDIO_PARAM_PHY_DATA; + + return cmd; +} + +/* + * start_miim_ops- Program and start MDIO transaction over mdio bus. + * @base: Base address + * @cmd: param values that need to be programmed in MDIO PARAM_OFFSET. + * @reg: register offset ot be read/written. + * @op: Operation that need to be carried out. + * MDIO_CTRL_READ_OP: Read transaction. + * MDIO_CTRL_WRITE_OP: Write transaction. + * + * Return value: Sucessfull Read operation returns read reg values and write + * operation returns 0. Failure opertation returns negative error code. + */ +static int start_miim_ops(void __iomem *base, u32 cmd, u32 reg, u32 op) +{ + int ret = 0; + + writel(0, base + MDIO_CTRL_OFFSET); + ret = iproc_mdio_wait_for_idle(base, false); + if (ret) + return ret; + + writel(cmd, base + MDIO_PARAM_OFFSET); + writel(reg, base + MDIO_ADDR_OFFSET); + + writel(op, base + MDIO_CTRL_OFFSET); + + ret = iproc_mdio_wait_for_idle(base, true); + if (ret) + return ret; + + if (op == MDIO_CTRL_READ_OP) + ret = readl(base + MDIO_READ_OFFSET) & MDIO_READ_DATA_MASK; + + return ret; +} + +static int iproc_shared_mdio_read(struct shared_mdio_master *master, + u16 phyid, u16 reg) +{ + int ret; + u32 cmd; + struct iproc_shared_mdio *imdio = dev_get_drvdata(master->dev.parent); + struct shared_mdio_master_param *param = master->priv; + + mutex_lock(&imdio->lock); + + dev_dbg(imdio->dev, "master=%d phy=%d reg=%d\n", + param->master_id, phyid, reg); + + cmd = get_cmd(param->is_internal, param->master_id, param->is_c45, + phyid, 0x0); + ret = start_miim_ops(imdio->regs, cmd, reg, MDIO_CTRL_READ_OP); + if (ret < 0) + dev_err(imdio->dev, "MDIO read operation failed!!!"); + + mutex_unlock(&imdio->lock); + return ret; +} + +static int iproc_shared_mdio_write(struct shared_mdio_master *master, + u16 phyid, u16 reg, u16 val) +{ + u32 cmd; + int ret; + struct iproc_shared_mdio *imdio = dev_get_drvdata(master->dev.parent); + struct shared_mdio_master_param *param = master->priv; + + mutex_lock(&imdio->lock); + + dev_dbg(imdio->dev, "master=%d phy=%d reg=%d val=0x%x\n", + param->master_id, phyid, reg, val); + + /* Write val at reg offset */ + cmd = get_cmd(param->is_internal, param->master_id, param->is_c45, + phyid, val); + ret = start_miim_ops(imdio->regs, cmd, reg, MDIO_CTRL_WRITE_OP); + if (ret < 0) + dev_err(imdio->dev, "MDIO Write operation failed!!!"); + + mutex_unlock(&imdio->lock); + return ret; +} + +static int iproc_shared_mdio_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct device_node *dn = dev->of_node, *child; + struct iproc_shared_mdio *imdio; + struct resource *res; + int ret = 0, cnt; + + imdio = devm_kzalloc(dev, sizeof(*imdio), GFP_KERNEL); + if (!imdio) + return -ENOMEM; + + imdio->num_masters = of_get_available_child_count(dn); + if (!imdio->num_masters) + return -ENODEV; + + imdio->masters = devm_kcalloc(dev, imdio->num_masters, + sizeof(**imdio->masters), GFP_KERNEL); + if (!imdio->masters) + return -ENOMEM; + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "mdio"); + imdio->regs = devm_ioremap_resource(dev, res); + if (IS_ERR(imdio->regs)) + return PTR_ERR(imdio->regs); + + imdio->dev = dev; + mutex_init(&imdio->lock); + + cnt = 0; + for_each_available_child_of_node(dn, child) { + unsigned int id; + struct shared_mdio_master *master; + struct shared_mdio_master_param *param; + + if (of_property_read_u32(child, "reg", &id)) { + dev_err(dev, "missing reg property in node %s\n", + child->name); + ret = -EINVAL; + goto fail; + } + + master = shared_mdio_alloc_master(dev, child); + if (IS_ERR(master)) { + ret = PTR_ERR(master); + goto fail; + } + imdio->masters[cnt] = master; + + param = devm_kzalloc(dev, sizeof(*param), GFP_KERNEL); + if (!param) { + ret = -ENOMEM; + goto fail; + } + master->priv = param; + param->master_id = id; + + param->is_internal = of_property_read_bool(child, + "brcm,phy-internal"); + param->is_c45 = of_property_read_bool(child, "brcm,is-c45"); + master->mdio_read = iproc_shared_mdio_read; + master->mdio_write = iproc_shared_mdio_write; + + ret = shared_mdio_add_master(master); + if (ret) + goto fail; + cnt++; + } + platform_set_drvdata(pdev, imdio); + dev_info(dev, "registered %d masters(s)\n", cnt); + return 0; +fail: + for (cnt = 0; cnt < imdio->num_masters; cnt++) { + if (imdio->masters[cnt]) { + shared_mdio_remove_master(imdio->masters[cnt]); + imdio->masters[cnt] = NULL; + } + } + return ret; +} + +static int iproc_shared_mdio_remove(struct platform_device *pdev) +{ + int i; + struct iproc_shared_mdio *imdio = platform_get_drvdata(pdev); + + for (i = 0; i < imdio->num_masters; i++) { + if (imdio->masters[i]) { + shared_mdio_remove_master(imdio->masters[i]); + imdio->masters[i] = NULL; + } + } + + return 0; +} + +static const struct of_device_id iproc_shared_mdio_of_match[] = { + { .compatible = "brcm,iproc-shared-mdio", }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, iproc_shared_mdio_of_match); + +static struct platform_driver iproc_shared_mdio_driver = { + .probe = iproc_shared_mdio_probe, + .remove = iproc_shared_mdio_remove, + .driver = { + .name = "iproc-shared-mdio", + .of_match_table = iproc_shared_mdio_of_match, + }, +}; + +module_platform_driver(iproc_shared_mdio_driver); + +MODULE_DESCRIPTION("iProc Shared MDIO Master Driver"); +MODULE_AUTHOR("Pramod Kumar "); +MODULE_LICENSE("GPL v2");