Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit d3feb406 authored by Rafał Miłecki's avatar Rafał Miłecki Committed by Kishon Vijay Abraham I
Browse files

phy: bcm-ns-usb2: new driver for USB 2.0 PHY on Northstar



Northstar is a family of SoCs used in home routers. They have USB 2.0
and 3.0 controllers with PHYs that need to be properly initialized.
This driver provides PHY init support in a generic way and can be bound
with an EHCI controller driver.
There are (just a few) registers being defined in bcma header. It's
because DMU/CRU registers will be also needed in other drivers. We will
need them e.g. in PCIe controller/PHY driver and at some point probably
in clock driver for BCM53573 chipset. By using include/linux/bcma/ we
avoid code duplication.

Signed-off-by: default avatarRafał Miłecki <zajec5@gmail.com>
Signed-off-by: default avatarKishon Vijay Abraham I <kishon@ti.com>
parent 444525d4
Loading
Loading
Loading
Loading
+21 −0
Original line number Original line Diff line number Diff line
Driver for Broadcom Northstar USB 2.0 PHY

Required properties:
- compatible: brcm,ns-usb2-phy
- reg: iomem address range of DMU (Device Management Unit)
- reg-names: "dmu", the only needed & supported reg right now
- clocks: USB PHY reference clock
- clock-names: "phy-ref-clk", the only needed & supported clock right now

To initialize USB 2.0 PHY driver needs to setup PLL correctly. To do this it
requires passing phandle to the USB PHY reference clock.

Example:
	usb2-phy {
		compatible = "brcm,ns-usb2-phy";
		reg = <0x1800c000 0x1000>;
		reg-names = "dmu";
		#phy-cells = <0>;
		clocks = <&genpll BCM_NSP_GENPLL_USB_PHY_REF_CLK>;
		clock-names = "phy-ref-clk";
	};
+9 −0
Original line number Original line Diff line number Diff line
@@ -15,6 +15,15 @@ config GENERIC_PHY
	  phy users can obtain reference to the PHY. All the users of this
	  phy users can obtain reference to the PHY. All the users of this
	  framework should select this config.
	  framework should select this config.


config PHY_BCM_NS_USB2
	tristate "Broadcom Northstar USB 2.0 PHY Driver"
	depends on ARCH_BCM_IPROC || COMPILE_TEST
	depends on HAS_IOMEM && OF
	select GENERIC_PHY
	help
	  Enable this to support Broadcom USB 2.0 PHY connected to the USB
	  controller on Northstar family.

config PHY_BERLIN_USB
config PHY_BERLIN_USB
	tristate "Marvell Berlin USB PHY Driver"
	tristate "Marvell Berlin USB PHY Driver"
	depends on ARCH_BERLIN && RESET_CONTROLLER && HAS_IOMEM && OF
	depends on ARCH_BERLIN && RESET_CONTROLLER && HAS_IOMEM && OF
+1 −0
Original line number Original line Diff line number Diff line
@@ -3,6 +3,7 @@
#
#


obj-$(CONFIG_GENERIC_PHY)		+= phy-core.o
obj-$(CONFIG_GENERIC_PHY)		+= phy-core.o
obj-$(CONFIG_PHY_BCM_NS_USB2)		+= phy-bcm-ns-usb2.o
obj-$(CONFIG_PHY_BERLIN_USB)		+= phy-berlin-usb.o
obj-$(CONFIG_PHY_BERLIN_USB)		+= phy-berlin-usb.o
obj-$(CONFIG_PHY_BERLIN_SATA)		+= phy-berlin-sata.o
obj-$(CONFIG_PHY_BERLIN_SATA)		+= phy-berlin-sata.o
obj-$(CONFIG_PHY_DM816X_USB)		+= phy-dm816x-usb.o
obj-$(CONFIG_PHY_DM816X_USB)		+= phy-dm816x-usb.o
+137 −0
Original line number Original line Diff line number Diff line
/*
 * Broadcom Northstar USB 2.0 PHY Driver
 *
 * Copyright (C) 2016 Rafał Miłecki <zajec5@gmail.com>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation.
 *
 */

#include <linux/bcma/bcma.h>
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/err.h>
#include <linux/module.h>
#include <linux/of_address.h>
#include <linux/of_platform.h>
#include <linux/phy/phy.h>
#include <linux/platform_device.h>
#include <linux/slab.h>

struct bcm_ns_usb2 {
	struct device *dev;
	struct clk *ref_clk;
	struct phy *phy;
	void __iomem *dmu;
};

static int bcm_ns_usb2_phy_init(struct phy *phy)
{
	struct bcm_ns_usb2 *usb2 = phy_get_drvdata(phy);
	struct device *dev = usb2->dev;
	void __iomem *dmu = usb2->dmu;
	u32 ref_clk_rate, usb2ctl, usb_pll_ndiv, usb_pll_pdiv;
	int err = 0;

	err = clk_prepare_enable(usb2->ref_clk);
	if (err < 0) {
		dev_err(dev, "Failed to prepare ref clock: %d\n", err);
		goto err_out;
	}

	ref_clk_rate = clk_get_rate(usb2->ref_clk);
	if (!ref_clk_rate) {
		dev_err(dev, "Failed to get ref clock rate\n");
		err = -EINVAL;
		goto err_clk_off;
	}

	usb2ctl = readl(dmu + BCMA_DMU_CRU_USB2_CONTROL);

	if (usb2ctl & BCMA_DMU_CRU_USB2_CONTROL_USB_PLL_PDIV_MASK) {
		usb_pll_pdiv = usb2ctl;
		usb_pll_pdiv &= BCMA_DMU_CRU_USB2_CONTROL_USB_PLL_PDIV_MASK;
		usb_pll_pdiv >>= BCMA_DMU_CRU_USB2_CONTROL_USB_PLL_PDIV_SHIFT;
	} else {
		usb_pll_pdiv = 1 << 3;
	}

	/* Calculate ndiv based on a solid 1920 MHz that is for USB2 PHY */
	usb_pll_ndiv = (1920000000 * usb_pll_pdiv) / ref_clk_rate;

	/* Unlock DMU PLL settings with some magic value */
	writel(0x0000ea68, dmu + BCMA_DMU_CRU_CLKSET_KEY);

	/* Write USB 2.0 PLL control setting */
	usb2ctl &= ~BCMA_DMU_CRU_USB2_CONTROL_USB_PLL_NDIV_MASK;
	usb2ctl |= usb_pll_ndiv << BCMA_DMU_CRU_USB2_CONTROL_USB_PLL_NDIV_SHIFT;
	writel(usb2ctl, dmu + BCMA_DMU_CRU_USB2_CONTROL);

	/* Lock DMU PLL settings */
	writel(0x00000000, dmu + BCMA_DMU_CRU_CLKSET_KEY);

err_clk_off:
	clk_disable_unprepare(usb2->ref_clk);
err_out:
	return err;
}

static const struct phy_ops ops = {
	.init		= bcm_ns_usb2_phy_init,
	.owner		= THIS_MODULE,
};

static int bcm_ns_usb2_probe(struct platform_device *pdev)
{
	struct device *dev = &pdev->dev;
	struct bcm_ns_usb2 *usb2;
	struct resource *res;
	struct phy_provider *phy_provider;

	usb2 = devm_kzalloc(&pdev->dev, sizeof(*usb2), GFP_KERNEL);
	if (!usb2)
		return -ENOMEM;
	usb2->dev = dev;

	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dmu");
	usb2->dmu = devm_ioremap_resource(dev, res);
	if (IS_ERR(usb2->dmu)) {
		dev_err(dev, "Failed to map DMU regs\n");
		return PTR_ERR(usb2->dmu);
	}

	usb2->ref_clk = devm_clk_get(dev, "phy-ref-clk");
	if (IS_ERR(usb2->ref_clk)) {
		dev_err(dev, "Clock not defined\n");
		return PTR_ERR(usb2->ref_clk);
	}

	usb2->phy = devm_phy_create(dev, NULL, &ops);
	if (IS_ERR(dev))
		return PTR_ERR(dev);

	phy_set_drvdata(usb2->phy, usb2);
	platform_set_drvdata(pdev, usb2);

	phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
	return PTR_ERR_OR_ZERO(phy_provider);
}

static const struct of_device_id bcm_ns_usb2_id_table[] = {
	{ .compatible = "brcm,ns-usb2-phy", },
	{},
};
MODULE_DEVICE_TABLE(of, bcm_ns_usb2_id_table);

static struct platform_driver bcm_ns_usb2_driver = {
	.probe		= bcm_ns_usb2_probe,
	.driver = {
		.name = "bcm_ns_usb2",
		.of_match_table = bcm_ns_usb2_id_table,
	},
};
module_platform_driver(bcm_ns_usb2_driver);

MODULE_LICENSE("GPL v2");
+1 −0
Original line number Original line Diff line number Diff line
@@ -4,6 +4,7 @@
#include <linux/pci.h>
#include <linux/pci.h>
#include <linux/mod_devicetable.h>
#include <linux/mod_devicetable.h>


#include <linux/bcma/bcma_driver_arm_c9.h>
#include <linux/bcma/bcma_driver_chipcommon.h>
#include <linux/bcma/bcma_driver_chipcommon.h>
#include <linux/bcma/bcma_driver_pci.h>
#include <linux/bcma/bcma_driver_pci.h>
#include <linux/bcma/bcma_driver_pcie2.h>
#include <linux/bcma/bcma_driver_pcie2.h>
Loading