All of lore.kernel.org
 help / color / mirror / Atom feed
From: Kishon Vijay Abraham I <kishon@ti.com>
To: Ray Jui <rjui@broadcom.com>
Cc: <linux-kernel@vger.kernel.org>,
	"JD (Jiandong) Zheng" <jdzheng@broadcom.com>,
	Arun Parameswaran <arunp@broadcom.com>,
	<bcm-kernel-feedback-list@broadcom.com>
Subject: Re: [PATCH 4/5] phy: cygnus: pcie: Add Cygnus PCIe PHY support
Date: Thu, 21 May 2015 18:49:14 +0530	[thread overview]
Message-ID: <555DDB52.5030301@ti.com> (raw)
In-Reply-To: <1432085014-20758-5-git-send-email-rjui@broadcom.com>



On Wednesday 20 May 2015 06:53 AM, Ray Jui wrote:
> This patch adds the PCIe PHY support for the Broadcom PCIe RC interface
>
> Signed-off-by: Ray Jui <rjui@broadcom.com>
> Reviewed-by: Arun Parameswaran <aparames@broadcom.com>
> Reviewed-by: JD (Jiandong) Zheng <jdzheng@broadcom.com>
> Reviewed-by: Scott Branden <sbranden@broadcom.com>
> ---
>   drivers/phy/Kconfig           |   15 ++
>   drivers/phy/Makefile          |    1 +
>   drivers/phy/phy-cygnus-pcie.c |  340 +++++++++++++++++++++++++++++++++++++++++
>   3 files changed, 356 insertions(+)
>   create mode 100644 drivers/phy/phy-cygnus-pcie.c
>
> diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig
> index 2664285..8250169 100644
> --- a/drivers/phy/Kconfig
> +++ b/drivers/phy/Kconfig
> @@ -35,6 +35,21 @@ config ARMADA375_USBCLUSTER_PHY
>   	depends on OF
>   	select GENERIC_PHY
>
> +config PHY_CYGNUS_PCIE
> +	bool "Broadcom Cygnus PCIe PHY driver"
> +	depends on ARCH_BCM_CYGNUS
> +	select GENERIC_PHY
> +	select PHY_IPROC_MDIO
> +	default ARCH_BCM_CYGNUS
> +	help
> +	  Enable this to support the Broadcom Cygnus PCIe PHY.
> +
> +	  The host communicates with the PHY through the iProc MDC/MDIO
> +	  interface.
> +
> +	  If unsure, say N.
> +
> +

trailing blank line
>   config PHY_DM816X_USB
>   	tristate "TI dm816x USB PHY driver"
>   	depends on ARCH_OMAP2PLUS
> diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile
> index d989dd7b..6545950 100644
> --- a/drivers/phy/Makefile
> +++ b/drivers/phy/Makefile
> @@ -5,6 +5,7 @@
>   obj-$(CONFIG_GENERIC_PHY)		+= phy-core.o
>   obj-$(CONFIG_PHY_BERLIN_USB)		+= phy-berlin-usb.o
>   obj-$(CONFIG_PHY_BERLIN_SATA)		+= phy-berlin-sata.o
> +obj-$(CONFIG_PHY_CYGNUS_PCIE)		+= phy-cygnus-pcie.o
>   obj-$(CONFIG_PHY_DM816X_USB)		+= phy-dm816x-usb.o
>   obj-$(CONFIG_ARMADA375_USBCLUSTER_PHY)	+= phy-armada375-usb2.o
>   obj-$(CONFIG_BCM_KONA_USB2_PHY)		+= phy-bcm-kona-usb2.o
> diff --git a/drivers/phy/phy-cygnus-pcie.c b/drivers/phy/phy-cygnus-pcie.c
> new file mode 100644
> index 0000000..5817cd4
> --- /dev/null
> +++ b/drivers/phy/phy-cygnus-pcie.c
> @@ -0,0 +1,340 @@
> +/*
> + * 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/delay.h>
> +#include <linux/io.h>
> +#include <linux/module.h>
> +#include <linux/of.h>
> +#include <linux/phy/phy.h>
> +#include <linux/platform_device.h>
> +#include <linux/phy/iproc_mdio_phy.h>
> +
> +#define MAX_PHY_ADDR                  0x1f
> +
> +#define PCIE_MDCDIV_VAL               0x3e
> +
> +#define PCIE_BLK_ADDR_OFFSET          0x1f
> +#define PCIE_BLK_ADDR_MASK            0xff00
> +#define PCIE_REG_ADDR_MASK            0x1f
> +
> +#define PCIE_SW_CTRL0_OFFSET          0x1000
> +#define PCIE_SW_PWRDOWN_SHIFT         0
> +
> +#define PCIE_AFE1_100MHZ_C3_OFFSET    0x2103
> +#define PCIE_AFE1_100MHZ_C3_VAL       0x2b1c
> +
> +#define CRMU_PCIE_CFG_OFFSET          0x00
> +#define CRMU_PCIE1_PHY_IDDQ_SHIFT     10
> +#define CRMU_PCIE0_PHY_IDDQ_SHIFT     2
> +
> +enum cygnus_pcie_phy_id {
> +	CYGNUS_PHY_PCIE0 = 0,
> +	CYGNUS_PHY_PCIE1,
> +	MAX_NUM_PHYS,
> +};
> +
> +struct cygnus_pcie_phy_core;
> +
> +/**
> + * struct cygnus_pcie_phy - Cygnus PCIe PHY device
> + * @core: pointer to the Cygnus PCIe PHY core control
> + * @id: internal ID to identify the Cygnus PCIe PHY
> + * @addr: PHY address used by MDC to communicate with the PHY
> + * @phy: pointer to the kernel PHY device
> + *
> + */
> +struct cygnus_pcie_phy {
> +	struct cygnus_pcie_phy_core *core;
> +	enum cygnus_pcie_phy_id id;
> +	unsigned addr;
> +	struct phy *phy;
> +};
> +
> +/**
> + * struct cygnus_pcie_phy_core - Cygnus PCIe PHY core control
> + * @dev: pointer to device
> + * @crmu: CRMU register base
> + * @lock: mutex to protect access to individual PHYs
> + * @phys: pointer to Cygnus PHY device
> + *
> + */
> +struct cygnus_pcie_phy_core {
> +	struct device *dev;
> +	void __iomem *crmu;
> +	struct mutex lock;
> +	struct cygnus_pcie_phy phys[MAX_NUM_PHYS];
> +};
> +
> +static int cygnus_pcie_phy_reg_read(unsigned phy_addr, unsigned reg_addr,
> +				    u16 *val)
> +{
> +	int ret;
> +	u16 addr = (u16)(reg_addr & PCIE_BLK_ADDR_MASK);
> +
> +	ret = iproc_mdio_write(PCIE_MDCDIV_VAL, phy_addr, PCIE_BLK_ADDR_OFFSET,
> +			       addr);
> +	if (ret)
> +		return ret;
> +
> +	ret = iproc_mdio_read(PCIE_MDCDIV_VAL, phy_addr,
> +			      reg_addr & PCIE_REG_ADDR_MASK, val);
> +	return ret;
> +}
> +
> +static int cygnus_pcie_phy_reg_write(unsigned phy_addr, unsigned reg_addr,
> +				     u16 val)
> +{
> +	int ret;
> +	u16 addr = (u16)(reg_addr & PCIE_BLK_ADDR_MASK);
> +
> +	ret = iproc_mdio_write(PCIE_MDCDIV_VAL, phy_addr, PCIE_BLK_ADDR_OFFSET,
> +			       addr);
> +	if (ret)
> +		return ret;
> +
> +	ret = iproc_mdio_write(PCIE_MDCDIV_VAL, phy_addr,
> +			       reg_addr & PCIE_REG_ADDR_MASK, val);
> +	return ret;
> +}
> +
> +static void cygnus_pcie_afe_enable_disable(void __iomem *reg,
> +					   enum cygnus_pcie_phy_id id,
> +					   bool enable)
> +{
> +	unsigned shift;
> +	u32 val;
> +
> +	if (id == CYGNUS_PHY_PCIE0)
> +		shift = CRMU_PCIE0_PHY_IDDQ_SHIFT;
> +	else
> +		shift = CRMU_PCIE1_PHY_IDDQ_SHIFT;
> +
> +	if (enable) {
> +		val = readl(reg + CRMU_PCIE_CFG_OFFSET);
> +		val &= ~BIT(shift);
> +		writel(val, reg + CRMU_PCIE_CFG_OFFSET);
> +	} else {
> +		val = readl(reg + CRMU_PCIE_CFG_OFFSET);
> +		val |= BIT(shift);
> +		writel(val, reg + CRMU_PCIE_CFG_OFFSET);
> +	}
> +
> +	/* wait for AFE to come up or shut down */
> +	msleep(100);
> +}
> +
> +static int cygnus_pcie_power_config(struct cygnus_pcie_phy *phy, bool on)
> +{
> +	struct cygnus_pcie_phy_core *core = phy->core;
> +	u16 val;
> +	int ret;
> +
> +	if (phy->id != CYGNUS_PHY_PCIE0 && phy->id != CYGNUS_PHY_PCIE1)
> +		return -EINVAL;
> +
> +	if (on) {
> +		/* enable AFE through the CRMU interface */
> +		cygnus_pcie_afe_enable_disable(core->crmu, phy->id, true);
> +
> +		/* to get the reference clock configured */
> +		ret = cygnus_pcie_phy_reg_write(phy->addr,
> +						PCIE_AFE1_100MHZ_C3_OFFSET,
> +						PCIE_AFE1_100MHZ_C3_VAL);
> +		if (ret)
> +			goto err_disable_afe;
> +		cygnus_pcie_phy_reg_read(phy->addr,
> +					 PCIE_AFE1_100MHZ_C3_OFFSET, &val);
> +		if (ret)
> +			goto err_disable_afe;
> +
> +		msleep(20);
> +
> +		/* now toggle AFE */
> +		cygnus_pcie_afe_enable_disable(core->crmu, phy->id, false);
> +		cygnus_pcie_afe_enable_disable(core->crmu, phy->id, true);
> +
> +		dev_info(core->dev,
> +			 "pcie phy on, addr:0x%02x off:0x%04x val:0x%04x\n",
> +			 phy->addr, PCIE_AFE1_100MHZ_C3_OFFSET, val);
> +	} else {
> +		/* disable AFE through the CRMU interface */
> +		cygnus_pcie_afe_enable_disable(core->crmu, phy->id, false);
> +		dev_info(core->dev, "pcie phy off\n");
> +	}
> +
> +	return 0;
> +
> +err_disable_afe:
> +	cygnus_pcie_afe_enable_disable(core->crmu, phy->id, false);
> +	return ret;
> +}
> +
> +static int cygnus_pcie_phy_init(struct phy *p)
> +{
> +	return 0;
> +}
> +
> +static int cygnus_pcie_phy_exit(struct phy *p)
> +{
> +	return 0;
> +}

empty callbacks are not required.
> +
> +static int cygnus_pcie_phy_power_on(struct phy *p)
> +{
> +	struct cygnus_pcie_phy *phy = phy_get_drvdata(p);
> +	struct cygnus_pcie_phy_core *core = phy->core;
> +	int ret = 0;
> +
> +	mutex_lock(&core->lock);
> +
> +	switch (phy->id) {
> +	case CYGNUS_PHY_PCIE0:
> +	case CYGNUS_PHY_PCIE1:
> +		ret = cygnus_pcie_power_config(phy, true);
> +		if (ret)
> +			dev_err(core->dev, "unable to power on PCIe PHY\n");
> +		break;
> +
> +	default:
> +		dev_err(core->dev, "PHY id not supported\n");
> +		mutex_unlock(&core->lock);
> +		return -EINVAL;
> +	}
> +
> +	mutex_unlock(&core->lock);
> +
> +	return ret;
> +}
> +
> +static int cygnus_pcie_phy_power_off(struct phy *p)
> +{
> +	struct cygnus_pcie_phy *phy = phy_get_drvdata(p);
> +	struct cygnus_pcie_phy_core *core = phy->core;
> +	int ret = 0;
> +
> +	mutex_lock(&core->lock);
> +
> +	switch (phy->id) {
> +	case CYGNUS_PHY_PCIE0:
> +	case CYGNUS_PHY_PCIE1:
> +		ret = cygnus_pcie_power_config(phy, false);
> +		if (ret)
> +			dev_err(core->dev, "unable to power off PCIe PHY\n");
> +		break;
> +
> +	default:
> +		dev_err(core->dev, "PHY id not supported\n");
> +		mutex_unlock(&core->lock);
> +		break;
> +	}
> +
> +	mutex_unlock(&core->lock);
> +
> +	return ret;
> +}
> +
> +static struct phy_ops cygnus_pcie_phy_ops = {
> +	.init = cygnus_pcie_phy_init,
> +	.exit = cygnus_pcie_phy_exit,
> +	.power_on = cygnus_pcie_phy_power_on,
> +	.power_off = cygnus_pcie_phy_power_off,
> +};
> +
> +static struct phy *cygnus_pcie_phy_xlate(struct device *dev,
> +					 struct of_phandle_args *args)
> +{
> +	struct cygnus_pcie_phy_core *core;
> +	int id, phy_addr;
> +
> +	core = dev_get_drvdata(dev);
> +	if (!core)
> +		return ERR_PTR(-EINVAL);
> +
> +	id = args->args[0];
> +	phy_addr = args->args[1];
> +
> +	if (WARN_ON(id >= MAX_NUM_PHYS))
> +		return ERR_PTR(-ENODEV);
> +
> +	if (WARN_ON(phy_addr > MAX_PHY_ADDR))
> +		return ERR_PTR(-ENODEV);
> +
> +	core->phys[id].addr = phy_addr;
> +
> +	return core->phys[id].phy;
> +}
> +
> +static int cygnus_pcie_phy_probe(struct platform_device *pdev)
> +{
> +	struct device *dev = &pdev->dev;
> +	struct cygnus_pcie_phy_core *core;
> +	struct phy_provider *provider;
> +	struct resource *res;
> +	int i = 0;
> +
> +	core = devm_kzalloc(dev, sizeof(*core), GFP_KERNEL);
> +	if (!core)
> +		return -ENOMEM;
> +
> +	core->dev = dev;
> +
> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	core->crmu = devm_ioremap_resource(dev, res);
> +	if (IS_ERR(core->crmu))
> +		return PTR_ERR(core->crmu);
> +
> +	mutex_init(&core->lock);
> +
> +	for (i = 0; i < MAX_NUM_PHYS; i++) {
> +		struct cygnus_pcie_phy *p = &core->phys[i];
> +
> +		p->phy = devm_phy_create(dev, NULL, &cygnus_pcie_phy_ops);
> +		if (IS_ERR(p->phy)) {
> +			dev_err(dev, "failed to create PHY\n");
> +			return PTR_ERR(p->phy);
> +		}
> +
> +		p->core = core;
> +		p->id = i;
> +		phy_set_drvdata(p->phy, p);
> +	}
> +
> +	dev_set_drvdata(dev, core);
> +
> +	provider = devm_of_phy_provider_register(dev, cygnus_pcie_phy_xlate);
> +	if (IS_ERR(provider)) {
> +		dev_err(dev, "failed to register PHY provider\n");
> +		return PTR_ERR(provider);
> +	}

PTR_ERR_OR_ZERO(phy_provider)?

Thanks
Kishon

  parent reply	other threads:[~2015-05-21 13:19 UTC|newest]

Thread overview: 22+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2015-05-20  1:23 [PATCH 0/5] Add Cygnus PCIe PHY support Ray Jui
2015-05-20  1:23 ` [PATCH 1/5] phy: iproc-mdio: Define DT binding Ray Jui
2015-05-21 13:07   ` Kishon Vijay Abraham I
2015-05-21 21:29     ` Ray Jui
2015-05-20  1:23 ` [PATCH 2/5] phy: iproc-mdio: Initial iProc MDC/MDIO support Ray Jui
2015-05-21  7:41   ` Paul Bolle
2015-05-21 23:52     ` Ray Jui
2015-05-22  8:39       ` Paul Bolle
2015-05-21 13:12   ` Kishon Vijay Abraham I
2015-05-21 21:35     ` Ray Jui
2015-05-21 21:51       ` Florian Fainelli
2015-05-21 22:02         ` Ray Jui
2015-05-20  1:23 ` [PATCH 3/5] phy: cygnus: pcie: Define DT binding Ray Jui
2015-05-21 13:14   ` Kishon Vijay Abraham I
2015-05-21 21:59     ` Ray Jui
2015-05-21 23:36       ` Ray Jui
2015-05-20  1:23 ` [PATCH 4/5] phy: cygnus: pcie: Add Cygnus PCIe PHY support Ray Jui
2015-05-21  7:52   ` Paul Bolle
2015-05-21 23:53     ` Ray Jui
2015-05-22  8:47       ` Paul Bolle
2015-05-21 13:19   ` Kishon Vijay Abraham I [this message]
2015-05-20  1:23 ` [PATCH 5/5] ARM: dts: enable PCIe PHY support for Cygnus Ray Jui

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=555DDB52.5030301@ti.com \
    --to=kishon@ti.com \
    --cc=arunp@broadcom.com \
    --cc=bcm-kernel-feedback-list@broadcom.com \
    --cc=jdzheng@broadcom.com \
    --cc=linux-kernel@vger.kernel.org \
    --cc=rjui@broadcom.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.