2014-06-05 15:48:53

by Antoine Tenart

[permalink] [raw]
Subject: [PATCH 0/9] ARM: Berlin: USB support

This series adds the support for the Marvell Berlin USB controllers,
the USB PHYs and also adds a reset controller.

The reset controller is used by the USB PHY driver and shares the
existing chip controller node with the clocks and one pin controller.

The Marvell Berlin USB controllers are host only on the BG2Q and are
compatible with USB ChipIdea. We here add a glue to use the available
common functions for this kind of controllers. A USB PHY driver is also
added to control the PHY.

Antoine Ténart (9):
reset: add the Berlin reset controller driver
ARM: Berlin: select the reset controller
ARM: dts: berlin: add a required reset property in the chip controller
node
usb: phy: add the Berlin USB PHY driver
Documentation: bindings: add doc for the Berlin USB PHY
usb: chipidea: add Berlin USB support
Documentation: bindings: add doc for the Berlin ChipIdea USB driver
ARM: dts: berlin: add BG2Q nodes for USB support
ARM: dts: Berlin: enable USB on the BG2Q DMP

.../devicetree/bindings/usb/berlin-usbphy.txt | 18 ++
.../devicetree/bindings/usb/ci-hdrc-berlin.txt | 18 ++
arch/arm/boot/dts/berlin2.dtsi | 1 +
arch/arm/boot/dts/berlin2cd.dtsi | 1 +
arch/arm/boot/dts/berlin2q-marvell-dmp.dts | 20 ++
arch/arm/boot/dts/berlin2q.dtsi | 52 +++++
arch/arm/mach-berlin/Kconfig | 2 +
drivers/reset/Makefile | 1 +
drivers/reset/reset-berlin.c | 113 +++++++++++
drivers/usb/chipidea/Makefile | 1 +
drivers/usb/chipidea/ci_hdrc_berlin.c | 108 ++++++++++
drivers/usb/phy/Kconfig | 9 +
drivers/usb/phy/Makefile | 1 +
drivers/usb/phy/phy-berlin-usb.c | 223 +++++++++++++++++++++
14 files changed, 568 insertions(+)
create mode 100644 Documentation/devicetree/bindings/usb/berlin-usbphy.txt
create mode 100644 Documentation/devicetree/bindings/usb/ci-hdrc-berlin.txt
create mode 100644 drivers/reset/reset-berlin.c
create mode 100644 drivers/usb/chipidea/ci_hdrc_berlin.c
create mode 100644 drivers/usb/phy/phy-berlin-usb.c

--
1.9.1


2014-06-05 15:48:59

by Antoine Tenart

[permalink] [raw]
Subject: [PATCH 9/9] ARM: dts: Berlin: enable USB on the BG2Q DMP

Enable the 2 available USB PHY and USB nodes on the Marvell Berlin BG2Q
DMP.

Signed-off-by: Antoine Ténart <[email protected]>
---
arch/arm/boot/dts/berlin2q-marvell-dmp.dts | 20 ++++++++++++++++++++
1 file changed, 20 insertions(+)

diff --git a/arch/arm/boot/dts/berlin2q-marvell-dmp.dts b/arch/arm/boot/dts/berlin2q-marvell-dmp.dts
index 2da9c41e29d8..4195874b9bed 100644
--- a/arch/arm/boot/dts/berlin2q-marvell-dmp.dts
+++ b/arch/arm/boot/dts/berlin2q-marvell-dmp.dts
@@ -7,6 +7,8 @@
*/

/dts-v1/;
+
+#include <dt-bindings/gpio/gpio.h>
#include "berlin2q.dtsi"

/ {
@@ -26,3 +28,21 @@
&uart0 {
status = "okay";
};
+
+&usb_phy0 {
+ power-gpio = <&portb 8 GPIO_ACTIVE_HIGH>;
+ status = "okay";
+};
+
+&usb_phy2 {
+ power-gpio = <&portb 12 GPIO_ACTIVE_HIGH>;
+ status = "okay";
+};
+
+&usb0 {
+ status = "okay";
+};
+
+&usb2 {
+ status = "okay";
+};
--
1.9.1

2014-06-05 15:48:58

by Antoine Tenart

[permalink] [raw]
Subject: [PATCH 8/9] ARM: dts: berlin: add BG2Q nodes for USB support

Adds nodes describing the Marvell Berlin BG2Q USB PHY and USB. The BG2Q
SoC has 3 USB host controller, compatible with ChipIdea.

Signed-off-by: Antoine Ténart <[email protected]>
---
arch/arm/boot/dts/berlin2q.dtsi | 51 +++++++++++++++++++++++++++++++++++++++++
1 file changed, 51 insertions(+)

diff --git a/arch/arm/boot/dts/berlin2q.dtsi b/arch/arm/boot/dts/berlin2q.dtsi
index b74e5ec66354..a6902ea5580c 100644
--- a/arch/arm/boot/dts/berlin2q.dtsi
+++ b/arch/arm/boot/dts/berlin2q.dtsi
@@ -87,6 +87,39 @@
#interrupt-cells = <3>;
};

+ usb_phy2: usbphy@a2f400 {
+ compatible = "marvell,berlin-usbphy";
+ reg = <0xa2f400 0x128>;
+ #phy-cells = <0>;
+ resets = <&chip 14>;
+ status = "disabled";
+ };
+
+ usb2: usb@a30000 {
+ compatible = "marvell,berlin-usb";
+ reg = <0xa30000 0x10000>;
+ interrupts = <GIC_SPI 52 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&chip CLKID_USB2>;
+ marvell,usbphy = <&usb_phy2>;
+ status = "disabled";
+ };
+
+ usb_phy0: usbphy@b74000 {
+ compatible = "marvell,berlin-usbphy";
+ reg = <0xb74000 0x128>;
+ #phy-cells = <0>;
+ resets = <&chip 12>;
+ status = "disabled";
+ };
+
+ usb_phy1: usbphy@b78000 {
+ compatible = "marvell,berlin-usbphy";
+ reg = <0xb78000 0x128>;
+ #phy-cells = <0>;
+ resets = <&chip 13>;
+ status = "disabled";
+ };
+
apb@e80000 {
compatible = "simple-bus";
#address-cells = <1>;
@@ -280,6 +313,24 @@
clock-names = "refclk";
};

+ usb0: usb@ed0000 {
+ compatible = "marvell,berlin-usb";
+ reg = <0xed0000 0x10000>;
+ interrupts = <GIC_SPI 11 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&chip CLKID_USB0>;
+ marvell,usbphy = <&usb_phy0>;
+ status = "disabled";
+ };
+
+ usb1: usb@ee0000 {
+ compatible = "marvell,berlin-usb";
+ reg = <0xee0000 0x10000>;
+ interrupts = <GIC_SPI 12 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&chip CLKID_USB1>;
+ marvell,usbphy = <&usb_phy1>;
+ status = "disabled";
+ };
+
apb@fc0000 {
compatible = "simple-bus";
#address-cells = <1>;
--
1.9.1

2014-06-05 15:48:55

by Antoine Tenart

[permalink] [raw]
Subject: [PATCH 2/9] ARM: Berlin: select the reset controller

The Marvell Berlin SoCs now has a reset controller. Add the needed
configuration.

Signed-off-by: Antoine Ténart <[email protected]>
---
arch/arm/mach-berlin/Kconfig | 2 ++
1 file changed, 2 insertions(+)

diff --git a/arch/arm/mach-berlin/Kconfig b/arch/arm/mach-berlin/Kconfig
index 101e0f356730..d61b336951b5 100644
--- a/arch/arm/mach-berlin/Kconfig
+++ b/arch/arm/mach-berlin/Kconfig
@@ -1,11 +1,13 @@
config ARCH_BERLIN
bool "Marvell Berlin SoCs" if ARCH_MULTI_V7
+ select ARCH_HAS_RESET_CONTROLLER
select ARCH_REQUIRE_GPIOLIB
select ARM_GIC
select GENERIC_IRQ_CHIP
select DW_APB_ICTL
select DW_APB_TIMER_OF
select PINCTRL
+ select RESET_CONTROLLER

if ARCH_BERLIN

--
1.9.1

2014-06-05 15:50:05

by Antoine Tenart

[permalink] [raw]
Subject: [PATCH 7/9] Documentation: bindings: add doc for the Berlin ChipIdea USB driver

Document the Marvell Berlin USB driver bindings.

Signed-off-by: Antoine Ténart <[email protected]>
---
.../devicetree/bindings/usb/ci-hdrc-berlin.txt | 18 ++++++++++++++++++
1 file changed, 18 insertions(+)
create mode 100644 Documentation/devicetree/bindings/usb/ci-hdrc-berlin.txt

diff --git a/Documentation/devicetree/bindings/usb/ci-hdrc-berlin.txt b/Documentation/devicetree/bindings/usb/ci-hdrc-berlin.txt
new file mode 100644
index 000000000000..a3fc9054b830
--- /dev/null
+++ b/Documentation/devicetree/bindings/usb/ci-hdrc-berlin.txt
@@ -0,0 +1,18 @@
+* Marvell Berlin usb controllers
+
+Required properties:
+- compatible: should be "marvell,berlin-usb"
+- reg: base address and length of the registers
+- interrupts: interrupt for the USB controller
+- clocks: reference to the USB clock
+- marvell,usbphy: reference to the USB PHY
+
+Example:
+
+ usb@f7ed0000 {
+ compatible = "marvell,berlin-usb";
+ reg = <0xf7ed0000 0x10000>;
+ interrupts = <GIC_SPI 11 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&chip CLKID_USB0>;
+ marvell,usbphy = <&usb_phy0>;
+ };
--
1.9.1

2014-06-05 15:51:10

by Antoine Tenart

[permalink] [raw]
Subject: [PATCH 6/9] usb: chipidea: add Berlin USB support

The Marvell Berlin USB controllers are compatible with ChipIdea. Add a
driver using the ChipIdea common functions to support them.

Signed-off-by: Antoine Ténart <[email protected]>
---
drivers/usb/chipidea/Makefile | 1 +
drivers/usb/chipidea/ci_hdrc_berlin.c | 108 ++++++++++++++++++++++++++++++++++
2 files changed, 109 insertions(+)
create mode 100644 drivers/usb/chipidea/ci_hdrc_berlin.c

diff --git a/drivers/usb/chipidea/Makefile b/drivers/usb/chipidea/Makefile
index 480bd4d5710a..b3a1b0b3b7a9 100644
--- a/drivers/usb/chipidea/Makefile
+++ b/drivers/usb/chipidea/Makefile
@@ -19,4 +19,5 @@ endif

ifneq ($(CONFIG_OF),)
obj-$(CONFIG_USB_CHIPIDEA) += usbmisc_imx.o ci_hdrc_imx.o
+ obj-$(CONFIG_USB_CHIPIDEA) += ci_hdrc_berlin.o
endif
diff --git a/drivers/usb/chipidea/ci_hdrc_berlin.c b/drivers/usb/chipidea/ci_hdrc_berlin.c
new file mode 100644
index 000000000000..567d83039e3e
--- /dev/null
+++ b/drivers/usb/chipidea/ci_hdrc_berlin.c
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2014 Marvell Technology Group Ltd.
+ *
+ * Antoine Ténart <[email protected]>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#include <linux/clk.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/usb/chipidea.h>
+#include <linux/usb/hcd.h>
+#include <linux/usb/ulpi.h>
+
+#include "ci.h"
+
+struct ci_hdrc_berlin_priv {
+ struct platform_device *ci_pdev;
+ struct clk *clk;
+};
+
+static int ci_hdrc_berlin_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct ci_hdrc_berlin_priv *priv;
+ struct ci_hdrc_platform_data ci_pdata = {
+ .name = "ci_hdrc_berlin",
+ .capoffset = DEF_CAPOFFSET,
+ .flags = CI_HDRC_REQUIRE_TRANSCEIVER |
+ CI_HDRC_DISABLE_STREAMING,
+ };
+ int ret;
+
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ priv->clk = devm_clk_get(dev, NULL);
+ if (IS_ERR(priv->clk)) {
+ dev_err(dev, "failed to get clock: %ld\n", PTR_ERR(priv->clk));
+ return PTR_ERR(priv->clk);
+ }
+
+ ret = clk_prepare_enable(priv->clk);
+ if (ret)
+ return ret;
+
+ ci_pdata.phy = devm_usb_get_phy_by_phandle(dev, "marvell,usbphy", 0);
+ if (IS_ERR(ci_pdata.phy)) {
+ ret = PTR_ERR(ci_pdata.phy);
+ goto clk_err;
+ }
+
+ priv->ci_pdev = ci_hdrc_add_device(dev, pdev->resource,
+ pdev->num_resources, &ci_pdata);
+ if (IS_ERR(priv->ci_pdev)) {
+ ret = PTR_ERR(priv->ci_pdev);
+ dev_err(dev,
+ "failed to register ci_hdrc platform device: %d\n",
+ ret);
+ goto clk_err;
+ }
+
+ platform_set_drvdata(pdev, priv);
+
+ pm_runtime_no_callbacks(dev);
+ pm_runtime_enable(dev);
+
+ return 0;
+
+clk_err:
+ clk_disable_unprepare(priv->clk);
+ return ret;
+}
+
+static int ci_hdrc_berlin_remove(struct platform_device *pdev)
+{
+ struct ci_hdrc_berlin_priv *priv = platform_get_drvdata(pdev);
+
+ pm_runtime_disable(&pdev->dev);
+ ci_hdrc_remove_device(priv->ci_pdev);
+ clk_disable_unprepare(priv->clk);
+
+ return 0;
+}
+
+static const struct of_device_id ci_hdrc_berlin_of_match[] = {
+ { .compatible = "marvell,berlin-usb" },
+ { }
+};
+
+static struct platform_driver ci_hdrc_berlin_driver = {
+ .probe = ci_hdrc_berlin_probe,
+ .remove = ci_hdrc_berlin_remove,
+ .driver = {
+ .name = "berlin-usb",
+ .owner = THIS_MODULE,
+ .of_match_table = ci_hdrc_berlin_of_match,
+ },
+};
+module_platform_driver(ci_hdrc_berlin_driver);
+
+MODULE_DESCRIPTION("ChipIdea HDRC Berlin USB binding");
+MODULE_AUTHOR("Antoine Ténart <[email protected]>");
+MODULE_LICENSE("GPL");
--
1.9.1

2014-06-05 15:51:36

by Antoine Tenart

[permalink] [raw]
Subject: [PATCH 5/9] Documentation: bindings: add doc for the Berlin USB PHY

Document the bindings of the Marvell Berlin USB PHY driver.

Signed-off-by: Antoine Ténart <[email protected]>
---
.../devicetree/bindings/usb/berlin-usbphy.txt | 18 ++++++++++++++++++
1 file changed, 18 insertions(+)
create mode 100644 Documentation/devicetree/bindings/usb/berlin-usbphy.txt

diff --git a/Documentation/devicetree/bindings/usb/berlin-usbphy.txt b/Documentation/devicetree/bindings/usb/berlin-usbphy.txt
new file mode 100644
index 000000000000..d97eb1f9d53f
--- /dev/null
+++ b/Documentation/devicetree/bindings/usb/berlin-usbphy.txt
@@ -0,0 +1,18 @@
+* Marvell Berlin USB PHY
+
+Required properties:
+- compatible: should be "marvell,berlin-usbphy"
+- reg: base address and length of the registers
+- #phys-cells: should be 0
+- reset: reference to the reset controller
+- power-gpio: reference the GPIO pin to power the PHY
+
+Example:
+
+ usbphy@f774000 {
+ compatible = "marvell,berlin-usbphy";
+ reg = <0xf774000 0x128>;
+ #phy-cells = <0>;
+ resets = <&chip 14>;
+ power-gpio = <&portb 8 GPIO_ACTIVE_HIGH>;
+ };
--
1.9.1

2014-06-05 15:51:56

by Antoine Tenart

[permalink] [raw]
Subject: [PATCH 4/9] usb: phy: add the Berlin USB PHY driver

Add the driver driving the Marvell Berlin USB PHY. This allows to
initialize the PHY and to use it from the USB driver later.

Signed-off-by: Antoine Ténart <[email protected]>
---
drivers/usb/phy/Kconfig | 9 ++
drivers/usb/phy/Makefile | 1 +
drivers/usb/phy/phy-berlin-usb.c | 223 +++++++++++++++++++++++++++++++++++++++
3 files changed, 233 insertions(+)
create mode 100644 drivers/usb/phy/phy-berlin-usb.c

diff --git a/drivers/usb/phy/Kconfig b/drivers/usb/phy/Kconfig
index 416e0c8cf6ff..8be8d4afc428 100644
--- a/drivers/usb/phy/Kconfig
+++ b/drivers/usb/phy/Kconfig
@@ -27,6 +27,15 @@ config AB8500_USB
This transceiver supports high and full speed devices plus,
in host mode, low speed.

+config BERLIN_USBPHY
+ tristate "Marvell Berlin USB Transceiver Driver"
+ depends on ARCH_BERLIN
+ select USB_PHY
+ help
+ Enable this to support the USB tranceiver on Marvell Berlin
+ SoCs.
+
+
config FSL_USB2_OTG
bool "Freescale USB OTG Transceiver Driver"
depends on USB_EHCI_FSL && USB_FSL_USB2 && USB_OTG_FSM && PM_RUNTIME
diff --git a/drivers/usb/phy/Makefile b/drivers/usb/phy/Makefile
index f8fa719a31b9..9253f59cf82a 100644
--- a/drivers/usb/phy/Makefile
+++ b/drivers/usb/phy/Makefile
@@ -8,6 +8,7 @@ obj-$(CONFIG_USB_OTG_FSM) += phy-fsm-usb.o
# transceiver drivers, keep the list sorted

obj-$(CONFIG_AB8500_USB) += phy-ab8500-usb.o
+obj-$(CONFIG_BERLIN_USBPHY) += phy-berlin-usb.o
obj-$(CONFIG_FSL_USB2_OTG) += phy-fsl-usb.o
obj-$(CONFIG_ISP1301_OMAP) += phy-isp1301-omap.o
obj-$(CONFIG_MV_U3D_PHY) += phy-mv-u3d-usb.o
diff --git a/drivers/usb/phy/phy-berlin-usb.c b/drivers/usb/phy/phy-berlin-usb.c
new file mode 100644
index 000000000000..79416668a71b
--- /dev/null
+++ b/drivers/usb/phy/phy-berlin-usb.c
@@ -0,0 +1,223 @@
+/*
+ * Copyright (C) 2014 Marvell Technology Group Ltd.
+ *
+ * Antoine Ténart <[email protected]>
+ * Jisheng Zhang <[email protected]>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#include <linux/gpio.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of_gpio.h>
+#include <linux/usb/phy.h>
+#include <linux/platform_device.h>
+#include <linux/reset.h>
+
+#define USB_PHY_PLL 0x04
+#define USB_PHY_PLL_CONTROL 0x08
+#define USB_PHY_TX_CTRL0 0x10
+#define USB_PHY_TX_CTRL1 0x14
+#define USB_PHY_TX_CTRL2 0x18
+#define USB_PHY_RX_CTRL 0x20
+#define USB_PHY_ANALOG 0x34
+
+/* USB_PHY_PLL */
+#define CLK_REF_DIV(x) ((x) << 4)
+#define FEEDBACK_CLK_DIV(x) ((x) << 8)
+
+/* USB_PHY_PLL_CONTROL */
+#define CLK_STABLE (0x1 << 0)
+#define PLL_CTRL_PIN (0x1 << 1)
+#define PLL_CTRL_REG (0x1 << 2)
+#define PLL_ON (0x1 << 3)
+#define PHASE_OFF_TOL_125 (0x0 << 5)
+#define PHASE_OFF_TOL_250 (0x1 << 5)
+#define KVC0_CALIB (0x0 << 9)
+#define KVC0_REG_CTRL (0x1 << 9)
+#define KVC0_HIGH (0x0 << 10)
+#define KVC0_LOW (0x3 << 10)
+#define CLK_BLK_EN (0x1 << 13)
+
+/* USB_PHY_TX_CTRL0 */
+#define EXT_HS_RCAL_EN (0x1 << 3)
+#define EXT_FS_RCAL_EN (0x1 << 4)
+#define IMPCAL_VTH_DIV(x) ((x) << 5)
+#define EXT_RS_RCAL_DIV(x) ((x) << 8)
+#define EXT_FS_RCAL_DIV(x) ((x) << 12)
+
+/* USB_PHY_TX_CTRL1 */
+#define TX_VDD15_14 (0x0 << 4)
+#define TX_VDD15_15 (0x1 << 4)
+#define TX_VDD15_16 (0x2 << 4)
+#define TX_VDD15_17 (0x3 << 4)
+#define TX_VDD12_VDD (0x0 << 6)
+#define TX_VDD12_11 (0x1 << 6)
+#define TX_VDD12_12 (0x2 << 6)
+#define TX_VDD12_13 (0x3 << 6)
+#define LOW_VDD_EN (0x1 << 8)
+#define TX_OUT_AMP(x) ((x) << 9)
+
+/* USB_PHY_TX_CTRL2 */
+#define TX_CHAN_CTRL_REG(x) ((x) << 0)
+#define DRV_SLEWRATE(x) ((x) << 4)
+#define IMP_CAL_FS_HS_DLY_0 (0x0 << 6)
+#define IMP_CAL_FS_HS_DLY_1 (0x1 << 6)
+#define IMP_CAL_FS_HS_DLY_2 (0x2 << 6)
+#define IMP_CAL_FS_HS_DLY_3 (0x3 << 6)
+#define FS_DRV_EN_MASK(x) ((x) << 8)
+#define HS_DRV_EN_MASK(x) ((x) << 12)
+
+/* USB_PHY_RX_CTRL */
+#define PHASE_FREEZE_DLY_2_CL (0x0 << 0)
+#define PHASE_FREEZE_DLY_4_CL (0x1 << 0)
+#define ACK_LENGTH_8_CL (0x0 << 2)
+#define ACK_LENGTH_12_CL (0x1 << 2)
+#define ACK_LENGTH_16_CL (0x2 << 2)
+#define ACK_LENGTH_20_CL (0x3 << 2)
+#define SQ_LENGTH_3 (0x0 << 4)
+#define SQ_LENGTH_6 (0x1 << 4)
+#define SQ_LENGTH_9 (0x2 << 4)
+#define SQ_LENGTH_12 (0x3 << 4)
+#define DISCON_THRESHOLD_260 (0x0 << 6)
+#define DISCON_THRESHOLD_270 (0x1 << 6)
+#define DISCON_THRESHOLD_280 (0x2 << 6)
+#define DISCON_THRESHOLD_290 (0x3 << 6)
+#define SQ_THRESHOLD(x) ((x) << 8)
+#define LPF_COEF(x) ((x) << 12)
+#define INTPL_CUR_10 (0x0 << 14)
+#define INTPL_CUR_20 (0x1 << 14)
+#define INTPL_CUR_30 (0x2 << 14)
+#define INTPL_CUR_40 (0x3 << 14)
+
+/* USB_PHY_ANALOG */
+#define ANA_PWR_UP (0x1 << 1)
+#define ANA_PWR_DOWN (0x1 << 2)
+#define V2I_VCO_RATIO(x) ((x) << 7)
+#define R_ROTATE_90 (0x0 << 10)
+#define R_ROTATE_0 (0x1 << 10)
+#define MODE_TEST_EN (0x1 << 11)
+#define ANA_TEST_DC_CTRL(x) ((x) << 12)
+
+#define to_berlin_phy_priv(p) container_of((p), struct berlin_phy_priv, phy)
+
+struct berlin_phy_priv {
+ void __iomem *base;
+ struct usb_phy phy;
+ struct reset_control *rst_ctrl;
+ int pwr_gpio;
+};
+
+static int berlin_phy_init(struct usb_phy *phy)
+{
+ struct berlin_phy_priv *priv = to_berlin_phy_priv(phy);
+ int ret;
+
+ reset_control_reset(priv->rst_ctrl);
+
+ writel(CLK_REF_DIV(0xc) | FEEDBACK_CLK_DIV(0x54),
+ priv->base + USB_PHY_PLL);
+ writel(CLK_STABLE | PLL_CTRL_REG | PHASE_OFF_TOL_250 | KVC0_REG_CTRL |
+ CLK_BLK_EN, priv->base + USB_PHY_PLL_CONTROL);
+ writel(V2I_VCO_RATIO(0x5) | R_ROTATE_0 | ANA_TEST_DC_CTRL(0x5),
+ priv->base + USB_PHY_ANALOG);
+ writel(PHASE_FREEZE_DLY_4_CL | ACK_LENGTH_16_CL | SQ_LENGTH_12 |
+ DISCON_THRESHOLD_260 | SQ_THRESHOLD(0xa) | LPF_COEF(0x2) |
+ INTPL_CUR_30, priv->base + USB_PHY_RX_CTRL);
+
+ writel(TX_VDD12_13 | TX_OUT_AMP(0x3), priv->base + USB_PHY_TX_CTRL1);
+ writel(EXT_HS_RCAL_EN | IMPCAL_VTH_DIV(0x3) | EXT_RS_RCAL_DIV(0x4),
+ priv->base + USB_PHY_TX_CTRL0);
+
+ writel(EXT_HS_RCAL_EN | IMPCAL_VTH_DIV(0x3) | EXT_RS_RCAL_DIV(0x4) |
+ EXT_FS_RCAL_DIV(0x2), priv->base + USB_PHY_TX_CTRL0);
+
+ writel(EXT_HS_RCAL_EN | IMPCAL_VTH_DIV(0x3) | EXT_RS_RCAL_DIV(0x4),
+ priv->base + USB_PHY_TX_CTRL0);
+ writel(TX_CHAN_CTRL_REG(0xf) | DRV_SLEWRATE(0x3) | IMP_CAL_FS_HS_DLY_3 |
+ FS_DRV_EN_MASK(0xd), priv->base + USB_PHY_TX_CTRL2);
+
+ ret = gpio_direction_output(priv->pwr_gpio, 0);
+ if (ret)
+ return ret;
+
+ gpio_set_value(priv->pwr_gpio, 1);
+
+ return 0;
+}
+
+static void berlin_phy_shutdown(struct usb_phy *phy)
+{
+ struct berlin_phy_priv *priv = to_berlin_phy_priv(phy);
+
+ gpio_set_value(priv->pwr_gpio, 0);
+}
+
+static int berlin_phy_probe(struct platform_device *pdev)
+{
+ struct device_node *np = pdev->dev.of_node;
+ struct berlin_phy_priv *priv;
+ struct resource *res;
+ int ret, gpio;
+
+ priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ priv->base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(priv->base))
+ return PTR_ERR(priv->base);
+
+ priv->rst_ctrl = devm_reset_control_get(&pdev->dev, NULL);
+ if (IS_ERR(priv->rst_ctrl)) {
+ ret = PTR_ERR(priv->rst_ctrl);
+ dev_err(&pdev->dev, "cannot get reset controller: %d\n", ret);
+ return ret;
+ }
+
+ gpio = of_get_named_gpio(np, "power-gpio", 0);
+ if (!gpio_is_valid(gpio))
+ return gpio;
+
+ ret = gpio_request(gpio, "power-gpio");
+ if (ret) {
+ dev_err(&pdev->dev, "cannot request GPIO %d", gpio);
+ return ret;
+ }
+ priv->pwr_gpio = gpio;
+
+ priv->phy.io_priv = priv->base;
+ priv->phy.dev = &pdev->dev;
+ priv->phy.label = "phy-berlin-usb";
+ priv->phy.init = berlin_phy_init;
+ priv->phy.shutdown = berlin_phy_shutdown;
+ priv->phy.type = USB_PHY_TYPE_USB2;
+
+ platform_set_drvdata(pdev, priv);
+
+ return usb_add_phy_dev(&priv->phy);
+}
+
+static const struct of_device_id phy_berlin_sata_of_match[] = {
+ { .compatible = "marvell,berlin-usbphy" },
+ { },
+};
+MODULE_DEVICE_TABLE(of, phy_berlin_sata_of_match);
+
+static struct platform_driver phy_berlin_usb_driver = {
+ .probe = berlin_phy_probe,
+ .driver = {
+ .name = "phy-berlin-usb",
+ .owner = THIS_MODULE,
+ .of_match_table = phy_berlin_sata_of_match,
+ },
+};
+module_platform_driver(phy_berlin_usb_driver);
+
+MODULE_AUTHOR("Antoine Ténart <[email protected]>");
+MODULE_DESCRIPTION("Marvell Berlin USB PHY driver");
+MODULE_LICENSE("GPL");
--
1.9.1

2014-06-05 15:48:52

by Antoine Tenart

[permalink] [raw]
Subject: [PATCH 1/9] reset: add the Berlin reset controller driver

Add a reset controller for Marvell Berlin SoCs which is used by the
USB PHYs drivers (for now).

Signed-off-by: Antoine Ténart <[email protected]>
---
drivers/reset/Makefile | 1 +
drivers/reset/reset-berlin.c | 113 +++++++++++++++++++++++++++++++++++++++++++
2 files changed, 114 insertions(+)
create mode 100644 drivers/reset/reset-berlin.c

diff --git a/drivers/reset/Makefile b/drivers/reset/Makefile
index 4f60caf750ce..fffe2a3dd255 100644
--- a/drivers/reset/Makefile
+++ b/drivers/reset/Makefile
@@ -1,3 +1,4 @@
obj-$(CONFIG_RESET_CONTROLLER) += core.o
+obj-$(CONFIG_ARCH_BERLIN) += reset-berlin.o
obj-$(CONFIG_ARCH_SUNXI) += reset-sunxi.o
obj-$(CONFIG_ARCH_STI) += sti/
diff --git a/drivers/reset/reset-berlin.c b/drivers/reset/reset-berlin.c
new file mode 100644
index 000000000000..78b42c882cb2
--- /dev/null
+++ b/drivers/reset/reset-berlin.c
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2014 Marvell Technology Group Ltd.
+ *
+ * Antoine Ténart <[email protected]>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/platform_device.h>
+#include <linux/reset-controller.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/types.h>
+
+#define BERLIN_RESET_REGISTER 0x104
+
+#define to_berlin_reset_priv(p) \
+ container_of((p), struct berlin_reset_priv, rcdev)
+
+struct berlin_reset_priv {
+ spinlock_t lock;
+ void __iomem *base;
+ struct reset_controller_dev rcdev;
+};
+
+static int berlin_reset_reset(struct reset_controller_dev *rcdev,
+ unsigned long id)
+{
+ struct berlin_reset_priv *priv = to_berlin_reset_priv(rcdev);
+ unsigned long flags;
+ int bank = id / BITS_PER_LONG;
+ int offset = id % BITS_PER_LONG;
+
+ spin_lock_irqsave(&priv->lock, flags);
+
+ writel(BIT(offset), priv->base + bank * 4);
+
+ spin_unlock_irqrestore(&priv->lock, flags);
+
+ /* let the reset be effective */
+ udelay(10);
+
+ return 0;
+}
+
+static struct reset_control_ops berlin_reset_ops = {
+ .reset = berlin_reset_reset,
+};
+
+static int __berlin_reset_init(struct device_node *np)
+{
+ struct berlin_reset_priv *priv;
+ struct resource res;
+ resource_size_t size;
+ int ret;
+
+ priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ ret = of_address_to_resource(np, 0, &res);
+ if (ret)
+ goto err;
+
+ size = resource_size(&res);
+
+ priv->base = ioremap(res.start, size);
+ if (!priv->base) {
+ ret = -ENOMEM;
+ goto err;
+ }
+ priv->base += BERLIN_RESET_REGISTER;
+
+ priv->rcdev.owner = THIS_MODULE;
+ priv->rcdev.nr_resets = size * 32;
+ priv->rcdev.ops = &berlin_reset_ops;
+ priv->rcdev.of_node = np;
+
+ reset_controller_register(&priv->rcdev);
+
+ return 0;
+
+err:
+ kfree(priv);
+ return ret;
+}
+
+static const struct of_device_id berlin_reset_of_match[] __initdata = {
+ { .compatible = "marvell,berlin2q-chip-ctrl" },
+ { },
+};
+
+static int __init berlin_reset_init(void)
+{
+ struct device_node *np;
+ int ret;
+
+ for_each_matching_node(np, berlin_reset_of_match) {
+ ret = __berlin_reset_init(np);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+arch_initcall(berlin_reset_init);
--
1.9.1

2014-06-05 15:52:45

by Antoine Tenart

[permalink] [raw]
Subject: [PATCH 3/9] ARM: dts: berlin: add a required reset property in the chip controller node

The chip controller node now also describes the Marvell Berlin reset
controller. Add the required 'reset-cells' property.

Signed-off-by: Antoine Ténart <[email protected]>
---
arch/arm/boot/dts/berlin2.dtsi | 1 +
arch/arm/boot/dts/berlin2cd.dtsi | 1 +
arch/arm/boot/dts/berlin2q.dtsi | 1 +
3 files changed, 3 insertions(+)

diff --git a/arch/arm/boot/dts/berlin2.dtsi b/arch/arm/boot/dts/berlin2.dtsi
index 2477dac4d643..379ff1a38377 100644
--- a/arch/arm/boot/dts/berlin2.dtsi
+++ b/arch/arm/boot/dts/berlin2.dtsi
@@ -243,6 +243,7 @@
chip: chip-control@ea0000 {
compatible = "marvell,berlin2-chip-ctrl";
#clock-cells = <1>;
+ #reset-cells = <1>;
reg = <0xea0000 0x400>;
clocks = <&refclk>;
clock-names = "refclk";
diff --git a/arch/arm/boot/dts/berlin2cd.dtsi b/arch/arm/boot/dts/berlin2cd.dtsi
index cc1df65da504..50d6518b5744 100644
--- a/arch/arm/boot/dts/berlin2cd.dtsi
+++ b/arch/arm/boot/dts/berlin2cd.dtsi
@@ -231,6 +231,7 @@
chip: chip-control@ea0000 {
compatible = "marvell,berlin2cd-chip-ctrl";
#clock-cells = <1>;
+ #reset-cells = <1>;
reg = <0xea0000 0x400>;
clocks = <&refclk>;
clock-names = "refclk";
diff --git a/arch/arm/boot/dts/berlin2q.dtsi b/arch/arm/boot/dts/berlin2q.dtsi
index 81712f5954ef..b74e5ec66354 100644
--- a/arch/arm/boot/dts/berlin2q.dtsi
+++ b/arch/arm/boot/dts/berlin2q.dtsi
@@ -274,6 +274,7 @@
chip: chip-control@ea0000 {
compatible = "marvell,berlin2q-chip-ctrl";
#clock-cells = <1>;
+ #reset-cells = <1>;
reg = <0xea0000 0x400>, <0xdd0170 0x10>;
clocks = <&refclk>;
clock-names = "refclk";
--
1.9.1

2014-06-05 16:36:53

by Philipp Zabel

[permalink] [raw]
Subject: Re: [PATCH 1/9] reset: add the Berlin reset controller driver

Hi Antoine,

thank you for the patch. I have a few comments below.

Am Donnerstag, den 05.06.2014, 17:48 +0200 schrieb Antoine Ténart:
> Add a reset controller for Marvell Berlin SoCs which is used by the
> USB PHYs drivers (for now).
>
> Signed-off-by: Antoine Ténart <[email protected]>
> ---
> drivers/reset/Makefile | 1 +
> drivers/reset/reset-berlin.c | 113 +++++++++++++++++++++++++++++++++++++++++++
> 2 files changed, 114 insertions(+)
> create mode 100644 drivers/reset/reset-berlin.c
>
> diff --git a/drivers/reset/Makefile b/drivers/reset/Makefile
> index 4f60caf750ce..fffe2a3dd255 100644
> --- a/drivers/reset/Makefile
> +++ b/drivers/reset/Makefile
> @@ -1,3 +1,4 @@
> obj-$(CONFIG_RESET_CONTROLLER) += core.o
> +obj-$(CONFIG_ARCH_BERLIN) += reset-berlin.o
> obj-$(CONFIG_ARCH_SUNXI) += reset-sunxi.o
> obj-$(CONFIG_ARCH_STI) += sti/
> diff --git a/drivers/reset/reset-berlin.c b/drivers/reset/reset-berlin.c
> new file mode 100644
> index 000000000000..78b42c882cb2
> --- /dev/null
> +++ b/drivers/reset/reset-berlin.c
> @@ -0,0 +1,113 @@
> +/*
> + * Copyright (C) 2014 Marvell Technology Group Ltd.
> + *
> + * Antoine Ténart <[email protected]>
> + *
> + * This file is licensed under the terms of the GNU General Public
> + * License version 2. This program is licensed "as is" without any
> + * warranty of any kind, whether express or implied.
> + */
> +
> +#include <linux/delay.h>
> +#include <linux/io.h>
> +#include <linux/module.h>
> +#include <linux/of.h>
> +#include <linux/of_address.h>
> +#include <linux/platform_device.h>

Is there a reason this is not actually implemented as platform device?

> +#include <linux/reset-controller.h>
> +#include <linux/slab.h>
> +#include <linux/spinlock.h>
> +#include <linux/types.h>
> +
> +#define BERLIN_RESET_REGISTER 0x104

How many reset registers are there? (See below).

> +#define to_berlin_reset_priv(p) \
> + container_of((p), struct berlin_reset_priv, rcdev)
> +
> +struct berlin_reset_priv {
> + spinlock_t lock;
> + void __iomem *base;
> + struct reset_controller_dev rcdev;
> +};
> +
> +static int berlin_reset_reset(struct reset_controller_dev *rcdev,
> + unsigned long id)
> +{
> + struct berlin_reset_priv *priv = to_berlin_reset_priv(rcdev);
> + unsigned long flags;
> + int bank = id / BITS_PER_LONG;
> + int offset = id % BITS_PER_LONG;
> +
> + spin_lock_irqsave(&priv->lock, flags);
> +
> + writel(BIT(offset), priv->base + bank * 4);
> +
> + spin_unlock_irqrestore(&priv->lock, flags);

Since this is a single write into an apparently self-clearing
register, I see no need for the spinlock here.

> + /* let the reset be effective */
> + udelay(10);
> +
> + return 0;
> +}
> +
> +static struct reset_control_ops berlin_reset_ops = {
> + .reset = berlin_reset_reset,
> +};
> +
> +static int __berlin_reset_init(struct device_node *np)
> +{
> + struct berlin_reset_priv *priv;
> + struct resource res;
> + resource_size_t size;
> + int ret;
> +
> + priv = kzalloc(sizeof(*priv), GFP_KERNEL);
> + if (!priv)
> + return -ENOMEM;
> +
> + ret = of_address_to_resource(np, 0, &res);
> + if (ret)
> + goto err;
> +
> + size = resource_size(&res);
> +
> + priv->base = ioremap(res.start, size);
> + if (!priv->base) {
> + ret = -ENOMEM;
> + goto err;
> + }

A platform driver could use devm_kzalloc, platform_get_resource,
and devm_ioremap_resource here.

> + priv->base += BERLIN_RESET_REGISTER;
> +
> + priv->rcdev.owner = THIS_MODULE;
> + priv->rcdev.nr_resets = size * 32;

This doesn't seem right. The device tree patch shows that
size = 0x400.

> + priv->rcdev.ops = &berlin_reset_ops;
> + priv->rcdev.of_node = np;
> +
> + reset_controller_register(&priv->rcdev);
> +
> + return 0;
> +
> +err:
> + kfree(priv);
> + return ret;
> +}
> +
> +static const struct of_device_id berlin_reset_of_match[] __initdata = {
> + { .compatible = "marvell,berlin2q-chip-ctrl" },
> + { },
> +};
> +
> +static int __init berlin_reset_init(void)
> +{
> + struct device_node *np;
> + int ret;
> +
> + for_each_matching_node(np, berlin_reset_of_match) {
> + ret = __berlin_reset_init(np);
> + if (ret)
> + return ret;
> + }
> +
> + return 0;
> +}
> +arch_initcall(berlin_reset_init);

regards
Philipp

2014-06-05 16:39:16

by Philipp Zabel

[permalink] [raw]
Subject: Re: [PATCH 3/9] ARM: dts: berlin: add a required reset property in the chip controller node

Am Donnerstag, den 05.06.2014, 17:48 +0200 schrieb Antoine Ténart:
> The chip controller node now also describes the Marvell Berlin reset
> controller. Add the required 'reset-cells' property.
>
> Signed-off-by: Antoine Ténart <[email protected]>
> ---
> arch/arm/boot/dts/berlin2.dtsi | 1 +
> arch/arm/boot/dts/berlin2cd.dtsi | 1 +
> arch/arm/boot/dts/berlin2q.dtsi | 1 +
> 3 files changed, 3 insertions(+)
>
> diff --git a/arch/arm/boot/dts/berlin2.dtsi b/arch/arm/boot/dts/berlin2.dtsi
> index 2477dac4d643..379ff1a38377 100644
> --- a/arch/arm/boot/dts/berlin2.dtsi
> +++ b/arch/arm/boot/dts/berlin2.dtsi
> @@ -243,6 +243,7 @@
> chip: chip-control@ea0000 {
> compatible = "marvell,berlin2-chip-ctrl";
> #clock-cells = <1>;
> + #reset-cells = <1>;

Are the marvell,berlin2q-chip-ctrl bindings already documented? This
should also be added to the documentation.

regards
Philipp

2014-06-05 16:44:35

by Antoine Tenart

[permalink] [raw]
Subject: Re: [PATCH 3/9] ARM: dts: berlin: add a required reset property in the chip controller node

On Thu, Jun 05, 2014 at 06:39:07PM +0200, Philipp Zabel wrote:
> Am Donnerstag, den 05.06.2014, 17:48 +0200 schrieb Antoine T?nart:
> > The chip controller node now also describes the Marvell Berlin reset
> > controller. Add the required 'reset-cells' property.
> >
> > Signed-off-by: Antoine T?nart <[email protected]>
> > ---
> > arch/arm/boot/dts/berlin2.dtsi | 1 +
> > arch/arm/boot/dts/berlin2cd.dtsi | 1 +
> > arch/arm/boot/dts/berlin2q.dtsi | 1 +
> > 3 files changed, 3 insertions(+)
> >
> > diff --git a/arch/arm/boot/dts/berlin2.dtsi b/arch/arm/boot/dts/berlin2.dtsi
> > index 2477dac4d643..379ff1a38377 100644
> > --- a/arch/arm/boot/dts/berlin2.dtsi
> > +++ b/arch/arm/boot/dts/berlin2.dtsi
> > @@ -243,6 +243,7 @@
> > chip: chip-control@ea0000 {
> > compatible = "marvell,berlin2-chip-ctrl";
> > #clock-cells = <1>;
> > + #reset-cells = <1>;
>
> Are the marvell,berlin2q-chip-ctrl bindings already documented? This
> should also be added to the documentation.

It is, I'll add information about its reset function.

Antoine

--
Antoine T?nart, Free Electrons
Embedded Linux, Kernel and Android engineering
http://free-electrons.com

2014-06-05 16:56:27

by Antoine Tenart

[permalink] [raw]
Subject: Re: [PATCH 1/9] reset: add the Berlin reset controller driver

Hi Philipp,

On Thu, Jun 05, 2014 at 06:36:45PM +0200, Philipp Zabel wrote:
> > +#include <linux/platform_device.h>
>
> Is there a reason this is not actually implemented as platform device?

The node describing this driver is shared with a pin controller and a
clock driver. The pin controller is implemented as a platform device. I
don't think we can have two platform device drivers using the device
tree for the same node. Or I'm maybe missing something.

>
> > +#include <linux/reset-controller.h>
> > +#include <linux/slab.h>
> > +#include <linux/spinlock.h>
> > +#include <linux/types.h>
> > +
> > +#define BERLIN_RESET_REGISTER 0x104
>
> How many reset registers are there? (See below).

I don't have lots of information about this. For now only the one used
to reset the USB PHY but others may come later.

> > +
> > +static int berlin_reset_reset(struct reset_controller_dev *rcdev,
> > + unsigned long id)
> > +{
> > + struct berlin_reset_priv *priv = to_berlin_reset_priv(rcdev);
> > + unsigned long flags;
> > + int bank = id / BITS_PER_LONG;
> > + int offset = id % BITS_PER_LONG;
> > +
> > + spin_lock_irqsave(&priv->lock, flags);
> > +
> > + writel(BIT(offset), priv->base + bank * 4);
> > +
> > + spin_unlock_irqrestore(&priv->lock, flags);
>
> Since this is a single write into an apparently self-clearing
> register, I see no need for the spinlock here.

Sure.

>
> > + /* let the reset be effective */
> > + udelay(10);
> > +
> > + return 0;
> > +}
> > +
> > +static struct reset_control_ops berlin_reset_ops = {
> > + .reset = berlin_reset_reset,
> > +};
> > +
> > +static int __berlin_reset_init(struct device_node *np)
> > +{
> > + struct berlin_reset_priv *priv;
> > + struct resource res;
> > + resource_size_t size;
> > + int ret;
> > +
> > + priv = kzalloc(sizeof(*priv), GFP_KERNEL);
> > + if (!priv)
> > + return -ENOMEM;
> > +
> > + ret = of_address_to_resource(np, 0, &res);
> > + if (ret)
> > + goto err;
> > +
> > + size = resource_size(&res);
> > +
> > + priv->base = ioremap(res.start, size);
> > + if (!priv->base) {
> > + ret = -ENOMEM;
> > + goto err;
> > + }
>
> A platform driver could use devm_kzalloc, platform_get_resource,
> and devm_ioremap_resource here.
>
> > + priv->base += BERLIN_RESET_REGISTER;
> > +
> > + priv->rcdev.owner = THIS_MODULE;
> > + priv->rcdev.nr_resets = size * 32;
>
> This doesn't seem right. The device tree patch shows that
> size = 0x400.

The reg property is shared between drivers using the common chip
controller node. I do not know how many registers are actually used
to reset.

We then could hardcode the size, with the registers actually used here?


Thanks for the review!

Antoine

--
Antoine T?nart, Free Electrons
Embedded Linux, Kernel and Android engineering
http://free-electrons.com

2014-06-06 06:39:10

by Vivek Gautam

[permalink] [raw]
Subject: Re: [PATCH 4/9] usb: phy: add the Berlin USB PHY driver

+ Kishon.


Hi,

On Thu, Jun 5, 2014 at 9:18 PM, Antoine Ténart
<[email protected]> wrote:
> Add the driver driving the Marvell Berlin USB PHY. This allows to
> initialize the PHY and to use it from the USB driver later.

Just out of curiosity, going forward we would like to have phy drivers based on
generic phy framework (drivers/phy).
Any particular reason that we are still adding phy drivers in usb-phy layer ?

Looking at it, seems like it can very well be written based on phy framework.

>
> Signed-off-by: Antoine Ténart <[email protected]>
> ---
> drivers/usb/phy/Kconfig | 9 ++
> drivers/usb/phy/Makefile | 1 +
> drivers/usb/phy/phy-berlin-usb.c | 223 +++++++++++++++++++++++++++++++++++++++
> 3 files changed, 233 insertions(+)
> create mode 100644 drivers/usb/phy/phy-berlin-usb.c
>
> diff --git a/drivers/usb/phy/Kconfig b/drivers/usb/phy/Kconfig
> index 416e0c8cf6ff..8be8d4afc428 100644
> --- a/drivers/usb/phy/Kconfig
> +++ b/drivers/usb/phy/Kconfig
> @@ -27,6 +27,15 @@ config AB8500_USB
> This transceiver supports high and full speed devices plus,
> in host mode, low speed.
>
> +config BERLIN_USBPHY
> + tristate "Marvell Berlin USB Transceiver Driver"
> + depends on ARCH_BERLIN
> + select USB_PHY
> + help
> + Enable this to support the USB tranceiver on Marvell Berlin
> + SoCs.
> +
> +
> config FSL_USB2_OTG
> bool "Freescale USB OTG Transceiver Driver"
> depends on USB_EHCI_FSL && USB_FSL_USB2 && USB_OTG_FSM && PM_RUNTIME
> diff --git a/drivers/usb/phy/Makefile b/drivers/usb/phy/Makefile
> index f8fa719a31b9..9253f59cf82a 100644
> --- a/drivers/usb/phy/Makefile
> +++ b/drivers/usb/phy/Makefile
> @@ -8,6 +8,7 @@ obj-$(CONFIG_USB_OTG_FSM) += phy-fsm-usb.o
> # transceiver drivers, keep the list sorted
>
> obj-$(CONFIG_AB8500_USB) += phy-ab8500-usb.o
> +obj-$(CONFIG_BERLIN_USBPHY) += phy-berlin-usb.o
> obj-$(CONFIG_FSL_USB2_OTG) += phy-fsl-usb.o
> obj-$(CONFIG_ISP1301_OMAP) += phy-isp1301-omap.o
> obj-$(CONFIG_MV_U3D_PHY) += phy-mv-u3d-usb.o
> diff --git a/drivers/usb/phy/phy-berlin-usb.c b/drivers/usb/phy/phy-berlin-usb.c
> new file mode 100644
> index 000000000000..79416668a71b
> --- /dev/null
> +++ b/drivers/usb/phy/phy-berlin-usb.c
> @@ -0,0 +1,223 @@
> +/*
> + * Copyright (C) 2014 Marvell Technology Group Ltd.
> + *
> + * Antoine Ténart <[email protected]>
> + * Jisheng Zhang <[email protected]>
> + *
> + * This file is licensed under the terms of the GNU General Public
> + * License version 2. This program is licensed "as is" without any
> + * warranty of any kind, whether express or implied.
> + */
> +
> +#include <linux/gpio.h>
> +#include <linux/io.h>
> +#include <linux/module.h>
> +#include <linux/of_gpio.h>
> +#include <linux/usb/phy.h>
> +#include <linux/platform_device.h>
> +#include <linux/reset.h>
> +
> +#define USB_PHY_PLL 0x04
> +#define USB_PHY_PLL_CONTROL 0x08
> +#define USB_PHY_TX_CTRL0 0x10
> +#define USB_PHY_TX_CTRL1 0x14
> +#define USB_PHY_TX_CTRL2 0x18
> +#define USB_PHY_RX_CTRL 0x20
> +#define USB_PHY_ANALOG 0x34
> +
> +/* USB_PHY_PLL */
> +#define CLK_REF_DIV(x) ((x) << 4)
> +#define FEEDBACK_CLK_DIV(x) ((x) << 8)
> +
> +/* USB_PHY_PLL_CONTROL */
> +#define CLK_STABLE (0x1 << 0)
> +#define PLL_CTRL_PIN (0x1 << 1)
> +#define PLL_CTRL_REG (0x1 << 2)
> +#define PLL_ON (0x1 << 3)
> +#define PHASE_OFF_TOL_125 (0x0 << 5)
> +#define PHASE_OFF_TOL_250 (0x1 << 5)
> +#define KVC0_CALIB (0x0 << 9)
> +#define KVC0_REG_CTRL (0x1 << 9)
> +#define KVC0_HIGH (0x0 << 10)
> +#define KVC0_LOW (0x3 << 10)
> +#define CLK_BLK_EN (0x1 << 13)
> +
> +/* USB_PHY_TX_CTRL0 */
> +#define EXT_HS_RCAL_EN (0x1 << 3)
> +#define EXT_FS_RCAL_EN (0x1 << 4)
> +#define IMPCAL_VTH_DIV(x) ((x) << 5)
> +#define EXT_RS_RCAL_DIV(x) ((x) << 8)
> +#define EXT_FS_RCAL_DIV(x) ((x) << 12)
> +
> +/* USB_PHY_TX_CTRL1 */
> +#define TX_VDD15_14 (0x0 << 4)
> +#define TX_VDD15_15 (0x1 << 4)
> +#define TX_VDD15_16 (0x2 << 4)
> +#define TX_VDD15_17 (0x3 << 4)
> +#define TX_VDD12_VDD (0x0 << 6)
> +#define TX_VDD12_11 (0x1 << 6)
> +#define TX_VDD12_12 (0x2 << 6)
> +#define TX_VDD12_13 (0x3 << 6)
> +#define LOW_VDD_EN (0x1 << 8)
> +#define TX_OUT_AMP(x) ((x) << 9)
> +
> +/* USB_PHY_TX_CTRL2 */
> +#define TX_CHAN_CTRL_REG(x) ((x) << 0)
> +#define DRV_SLEWRATE(x) ((x) << 4)
> +#define IMP_CAL_FS_HS_DLY_0 (0x0 << 6)
> +#define IMP_CAL_FS_HS_DLY_1 (0x1 << 6)
> +#define IMP_CAL_FS_HS_DLY_2 (0x2 << 6)
> +#define IMP_CAL_FS_HS_DLY_3 (0x3 << 6)
> +#define FS_DRV_EN_MASK(x) ((x) << 8)
> +#define HS_DRV_EN_MASK(x) ((x) << 12)
> +
> +/* USB_PHY_RX_CTRL */
> +#define PHASE_FREEZE_DLY_2_CL (0x0 << 0)
> +#define PHASE_FREEZE_DLY_4_CL (0x1 << 0)
> +#define ACK_LENGTH_8_CL (0x0 << 2)
> +#define ACK_LENGTH_12_CL (0x1 << 2)
> +#define ACK_LENGTH_16_CL (0x2 << 2)
> +#define ACK_LENGTH_20_CL (0x3 << 2)
> +#define SQ_LENGTH_3 (0x0 << 4)
> +#define SQ_LENGTH_6 (0x1 << 4)
> +#define SQ_LENGTH_9 (0x2 << 4)
> +#define SQ_LENGTH_12 (0x3 << 4)
> +#define DISCON_THRESHOLD_260 (0x0 << 6)
> +#define DISCON_THRESHOLD_270 (0x1 << 6)
> +#define DISCON_THRESHOLD_280 (0x2 << 6)
> +#define DISCON_THRESHOLD_290 (0x3 << 6)
> +#define SQ_THRESHOLD(x) ((x) << 8)
> +#define LPF_COEF(x) ((x) << 12)
> +#define INTPL_CUR_10 (0x0 << 14)
> +#define INTPL_CUR_20 (0x1 << 14)
> +#define INTPL_CUR_30 (0x2 << 14)
> +#define INTPL_CUR_40 (0x3 << 14)
> +
> +/* USB_PHY_ANALOG */
> +#define ANA_PWR_UP (0x1 << 1)
> +#define ANA_PWR_DOWN (0x1 << 2)
> +#define V2I_VCO_RATIO(x) ((x) << 7)
> +#define R_ROTATE_90 (0x0 << 10)
> +#define R_ROTATE_0 (0x1 << 10)
> +#define MODE_TEST_EN (0x1 << 11)
> +#define ANA_TEST_DC_CTRL(x) ((x) << 12)
> +
> +#define to_berlin_phy_priv(p) container_of((p), struct berlin_phy_priv, phy)
> +
> +struct berlin_phy_priv {
> + void __iomem *base;
> + struct usb_phy phy;
> + struct reset_control *rst_ctrl;
> + int pwr_gpio;
> +};
> +
> +static int berlin_phy_init(struct usb_phy *phy)
> +{
> + struct berlin_phy_priv *priv = to_berlin_phy_priv(phy);
> + int ret;
> +
> + reset_control_reset(priv->rst_ctrl);
> +
> + writel(CLK_REF_DIV(0xc) | FEEDBACK_CLK_DIV(0x54),
> + priv->base + USB_PHY_PLL);
> + writel(CLK_STABLE | PLL_CTRL_REG | PHASE_OFF_TOL_250 | KVC0_REG_CTRL |
> + CLK_BLK_EN, priv->base + USB_PHY_PLL_CONTROL);
> + writel(V2I_VCO_RATIO(0x5) | R_ROTATE_0 | ANA_TEST_DC_CTRL(0x5),
> + priv->base + USB_PHY_ANALOG);
> + writel(PHASE_FREEZE_DLY_4_CL | ACK_LENGTH_16_CL | SQ_LENGTH_12 |
> + DISCON_THRESHOLD_260 | SQ_THRESHOLD(0xa) | LPF_COEF(0x2) |
> + INTPL_CUR_30, priv->base + USB_PHY_RX_CTRL);
> +
> + writel(TX_VDD12_13 | TX_OUT_AMP(0x3), priv->base + USB_PHY_TX_CTRL1);
> + writel(EXT_HS_RCAL_EN | IMPCAL_VTH_DIV(0x3) | EXT_RS_RCAL_DIV(0x4),
> + priv->base + USB_PHY_TX_CTRL0);
> +
> + writel(EXT_HS_RCAL_EN | IMPCAL_VTH_DIV(0x3) | EXT_RS_RCAL_DIV(0x4) |
> + EXT_FS_RCAL_DIV(0x2), priv->base + USB_PHY_TX_CTRL0);
> +
> + writel(EXT_HS_RCAL_EN | IMPCAL_VTH_DIV(0x3) | EXT_RS_RCAL_DIV(0x4),
> + priv->base + USB_PHY_TX_CTRL0);
> + writel(TX_CHAN_CTRL_REG(0xf) | DRV_SLEWRATE(0x3) | IMP_CAL_FS_HS_DLY_3 |
> + FS_DRV_EN_MASK(0xd), priv->base + USB_PHY_TX_CTRL2);
> +
> + ret = gpio_direction_output(priv->pwr_gpio, 0);
> + if (ret)
> + return ret;
> +
> + gpio_set_value(priv->pwr_gpio, 1);
> +
> + return 0;
> +}
> +
> +static void berlin_phy_shutdown(struct usb_phy *phy)
> +{
> + struct berlin_phy_priv *priv = to_berlin_phy_priv(phy);
> +
> + gpio_set_value(priv->pwr_gpio, 0);
> +}
> +
> +static int berlin_phy_probe(struct platform_device *pdev)
> +{
> + struct device_node *np = pdev->dev.of_node;
> + struct berlin_phy_priv *priv;
> + struct resource *res;
> + int ret, gpio;
> +
> + priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
> + if (!priv)
> + return -ENOMEM;
> +
> + res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> + priv->base = devm_ioremap_resource(&pdev->dev, res);
> + if (IS_ERR(priv->base))
> + return PTR_ERR(priv->base);
> +
> + priv->rst_ctrl = devm_reset_control_get(&pdev->dev, NULL);
> + if (IS_ERR(priv->rst_ctrl)) {
> + ret = PTR_ERR(priv->rst_ctrl);
> + dev_err(&pdev->dev, "cannot get reset controller: %d\n", ret);
> + return ret;
> + }
> +
> + gpio = of_get_named_gpio(np, "power-gpio", 0);
> + if (!gpio_is_valid(gpio))
> + return gpio;
> +
> + ret = gpio_request(gpio, "power-gpio");
> + if (ret) {
> + dev_err(&pdev->dev, "cannot request GPIO %d", gpio);
> + return ret;
> + }
> + priv->pwr_gpio = gpio;
> +
> + priv->phy.io_priv = priv->base;
> + priv->phy.dev = &pdev->dev;
> + priv->phy.label = "phy-berlin-usb";
> + priv->phy.init = berlin_phy_init;
> + priv->phy.shutdown = berlin_phy_shutdown;
> + priv->phy.type = USB_PHY_TYPE_USB2;
> +
> + platform_set_drvdata(pdev, priv);
> +
> + return usb_add_phy_dev(&priv->phy);
> +}
> +
> +static const struct of_device_id phy_berlin_sata_of_match[] = {
> + { .compatible = "marvell,berlin-usbphy" },
> + { },
> +};
> +MODULE_DEVICE_TABLE(of, phy_berlin_sata_of_match);
> +
> +static struct platform_driver phy_berlin_usb_driver = {
> + .probe = berlin_phy_probe,
> + .driver = {
> + .name = "phy-berlin-usb",
> + .owner = THIS_MODULE,
> + .of_match_table = phy_berlin_sata_of_match,
> + },
> +};
> +module_platform_driver(phy_berlin_usb_driver);
> +
> +MODULE_AUTHOR("Antoine Ténart <[email protected]>");
> +MODULE_DESCRIPTION("Marvell Berlin USB PHY driver");
> +MODULE_LICENSE("GPL");
> --
> 1.9.1
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-usb" in
> the body of a message to [email protected]
> More majordomo info at http://vger.kernel.org/majordomo-info.html



--
Best Regards
Vivek Gautam
Samsung R&D Institute, Bangalore
India

2014-06-06 07:11:17

by Antoine Tenart

[permalink] [raw]
Subject: Re: [PATCH 4/9] usb: phy: add the Berlin USB PHY driver

Hi,

On Fri, Jun 06, 2014 at 12:09:06PM +0530, Vivek Gautam wrote:
> On Thu, Jun 5, 2014 at 9:18 PM, Antoine T?nart
> <[email protected]> wrote:
> > Add the driver driving the Marvell Berlin USB PHY. This allows to
> > initialize the PHY and to use it from the USB driver later.
>
> Just out of curiosity, going forward we would like to have phy drivers based on
> generic phy framework (drivers/phy).
> Any particular reason that we are still adding phy drivers in usb-phy layer ?
>
> Looking at it, seems like it can very well be written based on phy framework.

This USB controller are ChipIdea compatible, and the ChipIdea common
functions use the usb_phy framework. That's why this PHY driver is
there.

Antoine

--
Antoine T?nart, Free Electrons
Embedded Linux, Kernel and Android engineering
http://free-electrons.com

2014-06-06 10:44:26

by Sebastian Hesselbarth

[permalink] [raw]
Subject: Re: [PATCH 1/9] reset: add the Berlin reset controller driver

On 06/05/2014 06:36 PM, Philipp Zabel wrote:
> Am Donnerstag, den 05.06.2014, 17:48 +0200 schrieb Antoine Ténart:
>> Add a reset controller for Marvell Berlin SoCs which is used by the
>> USB PHYs drivers (for now).
>>
>> Signed-off-by: Antoine Ténart <[email protected]>
>> ---
>> drivers/reset/Makefile | 1 +
>> drivers/reset/reset-berlin.c | 113 +++++++++++++++++++++++++++++++++++++++++++
>> 2 files changed, 114 insertions(+)
>> create mode 100644 drivers/reset/reset-berlin.c
>>
[...]
>> obj-$(CONFIG_ARCH_STI) += sti/
>> diff --git a/drivers/reset/reset-berlin.c b/drivers/reset/reset-berlin.c
>> new file mode 100644
>> index 000000000000..78b42c882cb2
>> --- /dev/null
>> +++ b/drivers/reset/reset-berlin.c
>> @@ -0,0 +1,113 @@
>> +/*
>> + * Copyright (C) 2014 Marvell Technology Group Ltd.
>> + *
>> + * Antoine Ténart <[email protected]>
>> + *
>> + * This file is licensed under the terms of the GNU General Public
>> + * License version 2. This program is licensed "as is" without any
>> + * warranty of any kind, whether express or implied.
>> + */
>> +
>> +#include <linux/delay.h>
>> +#include <linux/io.h>
>> +#include <linux/module.h>
>> +#include <linux/of.h>
>> +#include <linux/of_address.h>
>> +#include <linux/platform_device.h>
>
> Is there a reason this is not actually implemented as platform device?
>
[...]
>> +static int __berlin_reset_init(struct device_node *np)
>> +{
>> + struct berlin_reset_priv *priv;
>> + struct resource res;
>> + resource_size_t size;
>> + int ret;
>> +
>> + priv = kzalloc(sizeof(*priv), GFP_KERNEL);
>> + if (!priv)
>> + return -ENOMEM;
>> +
>> + ret = of_address_to_resource(np, 0, &res);
>> + if (ret)
>> + goto err;
>> +
>> + size = resource_size(&res);
>> +
>> + priv->base = ioremap(res.start, size);
>> + if (!priv->base) {
>> + ret = -ENOMEM;
>> + goto err;
>> + }
>
> A platform driver could use devm_kzalloc, platform_get_resource,
> and devm_ioremap_resource here.
>
>> + priv->base += BERLIN_RESET_REGISTER;
>> +
>> + priv->rcdev.owner = THIS_MODULE;
>> + priv->rcdev.nr_resets = size * 32;
>
> This doesn't seem right. The device tree patch shows that
> size = 0x400.

Actually, not using a platform_device now is the outcome of
some late DT node rework we had for the clock driver. The reason
we only have one node for the whole register set providing
pinctrl, reset, clock, ... is that it would require tiny separate
register ranges spread over the whole register set.

Instead, the idea is to have a single DT node, register a
driver providing a mmio regmap, and registering individual
platform_devices for the non-early drivers using the regmap.
We also evaluated syscon, but it will require dummy nodes for
each proper platform_device and that is something we really
want to avoid here.

Of course, writing that driver is delayed on my side because
of other non-Linux stuff that has to be taken care of first.
As I cannot tell how much time I can spend on it now, I prefer
to take this as is and provide update patches as soon as I have
worked out the regset driver.

Sebastian

2014-06-06 10:54:14

by Sebastian Hesselbarth

[permalink] [raw]
Subject: Re: [PATCH 4/9] usb: phy: add the Berlin USB PHY driver

On 06/05/2014 05:48 PM, Antoine Ténart wrote:
> Add the driver driving the Marvell Berlin USB PHY. This allows to
> initialize the PHY and to use it from the USB driver later.
>
> Signed-off-by: Antoine Ténart <[email protected]>
> ---
> drivers/usb/phy/Kconfig | 9 ++
> drivers/usb/phy/Makefile | 1 +
> drivers/usb/phy/phy-berlin-usb.c | 223 +++++++++++++++++++++++++++++++++++++++
> 3 files changed, 233 insertions(+)
> create mode 100644 drivers/usb/phy/phy-berlin-usb.c
>
[...]
> diff --git a/drivers/usb/phy/phy-berlin-usb.c b/drivers/usb/phy/phy-berlin-usb.c
> new file mode 100644
> index 000000000000..79416668a71b
> --- /dev/null
> +++ b/drivers/usb/phy/phy-berlin-usb.c
> @@ -0,0 +1,223 @@
> +/*
> + * Copyright (C) 2014 Marvell Technology Group Ltd.
> + *
> + * Antoine Ténart <[email protected]>
> + * Jisheng Zhang <[email protected]>
> + *
> + * This file is licensed under the terms of the GNU General Public
> + * License version 2. This program is licensed "as is" without any
> + * warranty of any kind, whether express or implied.
> + */
> +
> +#include <linux/gpio.h>
> +#include <linux/io.h>
> +#include <linux/module.h>
> +#include <linux/of_gpio.h>
> +#include <linux/usb/phy.h>
> +#include <linux/platform_device.h>
> +#include <linux/reset.h>

nit: keep above alphabetically sorted.

> +#define USB_PHY_PLL 0x04
> +#define USB_PHY_PLL_CONTROL 0x08
> +#define USB_PHY_TX_CTRL0 0x10
> +#define USB_PHY_TX_CTRL1 0x14
> +#define USB_PHY_TX_CTRL2 0x18
> +#define USB_PHY_RX_CTRL 0x20
> +#define USB_PHY_ANALOG 0x34
> +
> +/* USB_PHY_PLL */
> +#define CLK_REF_DIV(x) ((x) << 4)
> +#define FEEDBACK_CLK_DIV(x) ((x) << 8)
> +
> +/* USB_PHY_PLL_CONTROL */
> +#define CLK_STABLE (0x1 << 0)
> +#define PLL_CTRL_PIN (0x1 << 1)
> +#define PLL_CTRL_REG (0x1 << 2)
> +#define PLL_ON (0x1 << 3)
> +#define PHASE_OFF_TOL_125 (0x0 << 5)
> +#define PHASE_OFF_TOL_250 (0x1 << 5)
> +#define KVC0_CALIB (0x0 << 9)
> +#define KVC0_REG_CTRL (0x1 << 9)
> +#define KVC0_HIGH (0x0 << 10)
> +#define KVC0_LOW (0x3 << 10)
> +#define CLK_BLK_EN (0x1 << 13)

BIT() for the single bit flags above and below.

> +/* USB_PHY_TX_CTRL0 */
> +#define EXT_HS_RCAL_EN (0x1 << 3)
> +#define EXT_FS_RCAL_EN (0x1 << 4)
> +#define IMPCAL_VTH_DIV(x) ((x) << 5)
> +#define EXT_RS_RCAL_DIV(x) ((x) << 8)
> +#define EXT_FS_RCAL_DIV(x) ((x) << 12)
> +
> +/* USB_PHY_TX_CTRL1 */
> +#define TX_VDD15_14 (0x0 << 4)
> +#define TX_VDD15_15 (0x1 << 4)
> +#define TX_VDD15_16 (0x2 << 4)
> +#define TX_VDD15_17 (0x3 << 4)
> +#define TX_VDD12_VDD (0x0 << 6)
> +#define TX_VDD12_11 (0x1 << 6)
> +#define TX_VDD12_12 (0x2 << 6)
> +#define TX_VDD12_13 (0x3 << 6)
> +#define LOW_VDD_EN (0x1 << 8)
> +#define TX_OUT_AMP(x) ((x) << 9)
> +
> +/* USB_PHY_TX_CTRL2 */
> +#define TX_CHAN_CTRL_REG(x) ((x) << 0)
> +#define DRV_SLEWRATE(x) ((x) << 4)
> +#define IMP_CAL_FS_HS_DLY_0 (0x0 << 6)
> +#define IMP_CAL_FS_HS_DLY_1 (0x1 << 6)
> +#define IMP_CAL_FS_HS_DLY_2 (0x2 << 6)
> +#define IMP_CAL_FS_HS_DLY_3 (0x3 << 6)
> +#define FS_DRV_EN_MASK(x) ((x) << 8)
> +#define HS_DRV_EN_MASK(x) ((x) << 12)
> +
> +/* USB_PHY_RX_CTRL */
> +#define PHASE_FREEZE_DLY_2_CL (0x0 << 0)
> +#define PHASE_FREEZE_DLY_4_CL (0x1 << 0)
> +#define ACK_LENGTH_8_CL (0x0 << 2)
> +#define ACK_LENGTH_12_CL (0x1 << 2)
> +#define ACK_LENGTH_16_CL (0x2 << 2)
> +#define ACK_LENGTH_20_CL (0x3 << 2)
> +#define SQ_LENGTH_3 (0x0 << 4)
> +#define SQ_LENGTH_6 (0x1 << 4)
> +#define SQ_LENGTH_9 (0x2 << 4)
> +#define SQ_LENGTH_12 (0x3 << 4)
> +#define DISCON_THRESHOLD_260 (0x0 << 6)
> +#define DISCON_THRESHOLD_270 (0x1 << 6)
> +#define DISCON_THRESHOLD_280 (0x2 << 6)
> +#define DISCON_THRESHOLD_290 (0x3 << 6)
> +#define SQ_THRESHOLD(x) ((x) << 8)
> +#define LPF_COEF(x) ((x) << 12)
> +#define INTPL_CUR_10 (0x0 << 14)
> +#define INTPL_CUR_20 (0x1 << 14)
> +#define INTPL_CUR_30 (0x2 << 14)
> +#define INTPL_CUR_40 (0x3 << 14)
> +
> +/* USB_PHY_ANALOG */
> +#define ANA_PWR_UP (0x1 << 1)
> +#define ANA_PWR_DOWN (0x1 << 2)
> +#define V2I_VCO_RATIO(x) ((x) << 7)
> +#define R_ROTATE_90 (0x0 << 10)
> +#define R_ROTATE_0 (0x1 << 10)
> +#define MODE_TEST_EN (0x1 << 11)
> +#define ANA_TEST_DC_CTRL(x) ((x) << 12)
> +
> +#define to_berlin_phy_priv(p) container_of((p), struct berlin_phy_priv, phy)
> +
> +struct berlin_phy_priv {
> + void __iomem *base;
> + struct usb_phy phy;
> + struct reset_control *rst_ctrl;
> + int pwr_gpio;

Is the GPIO used for USB power? If so, we should not rely on
GPIO at all but use regulator API. Thinking of Chromecast which
is externally powered over USB, there will be no regulator nor
GPIO at all.

> +};
> +
> +static int berlin_phy_init(struct usb_phy *phy)
> +{
> + struct berlin_phy_priv *priv = to_berlin_phy_priv(phy);
> + int ret;
> +
> + reset_control_reset(priv->rst_ctrl);
> +
> + writel(CLK_REF_DIV(0xc) | FEEDBACK_CLK_DIV(0x54),
> + priv->base + USB_PHY_PLL);

@Jisheng: IIRC the dividers above are different for BG2? Can you please
evaluate?

> + writel(CLK_STABLE | PLL_CTRL_REG | PHASE_OFF_TOL_250 | KVC0_REG_CTRL |
> + CLK_BLK_EN, priv->base + USB_PHY_PLL_CONTROL);
> + writel(V2I_VCO_RATIO(0x5) | R_ROTATE_0 | ANA_TEST_DC_CTRL(0x5),
> + priv->base + USB_PHY_ANALOG);
> + writel(PHASE_FREEZE_DLY_4_CL | ACK_LENGTH_16_CL | SQ_LENGTH_12 |
> + DISCON_THRESHOLD_260 | SQ_THRESHOLD(0xa) | LPF_COEF(0x2) |
> + INTPL_CUR_30, priv->base + USB_PHY_RX_CTRL);
> +
> + writel(TX_VDD12_13 | TX_OUT_AMP(0x3), priv->base + USB_PHY_TX_CTRL1);
> + writel(EXT_HS_RCAL_EN | IMPCAL_VTH_DIV(0x3) | EXT_RS_RCAL_DIV(0x4),
> + priv->base + USB_PHY_TX_CTRL0);
> +
> + writel(EXT_HS_RCAL_EN | IMPCAL_VTH_DIV(0x3) | EXT_RS_RCAL_DIV(0x4) |
> + EXT_FS_RCAL_DIV(0x2), priv->base + USB_PHY_TX_CTRL0);
> +
> + writel(EXT_HS_RCAL_EN | IMPCAL_VTH_DIV(0x3) | EXT_RS_RCAL_DIV(0x4),
> + priv->base + USB_PHY_TX_CTRL0);
> + writel(TX_CHAN_CTRL_REG(0xf) | DRV_SLEWRATE(0x3) | IMP_CAL_FS_HS_DLY_3 |
> + FS_DRV_EN_MASK(0xd), priv->base + USB_PHY_TX_CTRL2);
> +
> + ret = gpio_direction_output(priv->pwr_gpio, 0);

As mentioned above, this should be using regulator API. And also, if
there is no dummy regulator allowed, it should be optional.

> + if (ret)
> + return ret;
> +
> + gpio_set_value(priv->pwr_gpio, 1);
> +
> + return 0;
> +}
> +
> +static void berlin_phy_shutdown(struct usb_phy *phy)
> +{
> + struct berlin_phy_priv *priv = to_berlin_phy_priv(phy);
> +
> + gpio_set_value(priv->pwr_gpio, 0);
> +}
> +
> +static int berlin_phy_probe(struct platform_device *pdev)
> +{
> + struct device_node *np = pdev->dev.of_node;
> + struct berlin_phy_priv *priv;
> + struct resource *res;
> + int ret, gpio;
> +
> + priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
> + if (!priv)
> + return -ENOMEM;
> +
> + res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> + priv->base = devm_ioremap_resource(&pdev->dev, res);
> + if (IS_ERR(priv->base))
> + return PTR_ERR(priv->base);
> +
> + priv->rst_ctrl = devm_reset_control_get(&pdev->dev, NULL);
> + if (IS_ERR(priv->rst_ctrl)) {
> + ret = PTR_ERR(priv->rst_ctrl);
> + dev_err(&pdev->dev, "cannot get reset controller: %d\n", ret);

Hmm, considering a non arch_init call registered reset driver, it does
also spit out an error for -EPROBE_DEFER, does it?

> + return ret;
> + }
> +
> + gpio = of_get_named_gpio(np, "power-gpio", 0);
> + if (!gpio_is_valid(gpio))
> + return gpio;
> +
> + ret = gpio_request(gpio, "power-gpio");
> + if (ret) {
> + dev_err(&pdev->dev, "cannot request GPIO %d", gpio);
> + return ret;
> + }
> + priv->pwr_gpio = gpio;
> +
> + priv->phy.io_priv = priv->base;
> + priv->phy.dev = &pdev->dev;
> + priv->phy.label = "phy-berlin-usb";
> + priv->phy.init = berlin_phy_init;
> + priv->phy.shutdown = berlin_phy_shutdown;
> + priv->phy.type = USB_PHY_TYPE_USB2;
> +
> + platform_set_drvdata(pdev, priv);
> +
> + return usb_add_phy_dev(&priv->phy);
> +}
> +
> +static const struct of_device_id phy_berlin_sata_of_match[] = {
> + { .compatible = "marvell,berlin-usbphy" },

If we need to distinguish BG2 and later SoCs, we either need two
different compatibles. Or we just have a vendor specific property
describing the divider values above.

Sebastian

> + { },
> +};
> +MODULE_DEVICE_TABLE(of, phy_berlin_sata_of_match);
> +
> +static struct platform_driver phy_berlin_usb_driver = {
> + .probe = berlin_phy_probe,
> + .driver = {
> + .name = "phy-berlin-usb",
> + .owner = THIS_MODULE,
> + .of_match_table = phy_berlin_sata_of_match,
> + },
> +};
> +module_platform_driver(phy_berlin_usb_driver);
> +
> +MODULE_AUTHOR("Antoine Ténart <[email protected]>");
> +MODULE_DESCRIPTION("Marvell Berlin USB PHY driver");
> +MODULE_LICENSE("GPL");
>

2014-06-06 10:56:00

by Sebastian Hesselbarth

[permalink] [raw]
Subject: Re: [PATCH 6/9] usb: chipidea: add Berlin USB support

On 06/05/2014 05:48 PM, Antoine Ténart wrote:
> The Marvell Berlin USB controllers are compatible with ChipIdea. Add a
> driver using the ChipIdea common functions to support them.
>
> Signed-off-by: Antoine Ténart <[email protected]>
> ---
> drivers/usb/chipidea/Makefile | 1 +
> drivers/usb/chipidea/ci_hdrc_berlin.c | 108 ++++++++++++++++++++++++++++++++++
> 2 files changed, 109 insertions(+)
> create mode 100644 drivers/usb/chipidea/ci_hdrc_berlin.c
>
> diff --git a/drivers/usb/chipidea/Makefile b/drivers/usb/chipidea/Makefile
> index 480bd4d5710a..b3a1b0b3b7a9 100644
> --- a/drivers/usb/chipidea/Makefile
> +++ b/drivers/usb/chipidea/Makefile
> @@ -19,4 +19,5 @@ endif
>
> ifneq ($(CONFIG_OF),)
> obj-$(CONFIG_USB_CHIPIDEA) += usbmisc_imx.o ci_hdrc_imx.o
> + obj-$(CONFIG_USB_CHIPIDEA) += ci_hdrc_berlin.o
> endif
> diff --git a/drivers/usb/chipidea/ci_hdrc_berlin.c b/drivers/usb/chipidea/ci_hdrc_berlin.c
> new file mode 100644
> index 000000000000..567d83039e3e
> --- /dev/null
> +++ b/drivers/usb/chipidea/ci_hdrc_berlin.c
> @@ -0,0 +1,108 @@
> +/*
> + * Copyright (C) 2014 Marvell Technology Group Ltd.
> + *
> + * Antoine Ténart <[email protected]>
> + *
> + * This file is licensed under the terms of the GNU General Public
> + * License version 2. This program is licensed "as is" without any
> + * warranty of any kind, whether express or implied.
> + */
> +
> +#include <linux/clk.h>
> +#include <linux/module.h>
> +#include <linux/platform_device.h>
> +#include <linux/usb/chipidea.h>
> +#include <linux/usb/hcd.h>
> +#include <linux/usb/ulpi.h>
> +
> +#include "ci.h"
> +
> +struct ci_hdrc_berlin_priv {
> + struct platform_device *ci_pdev;
> + struct clk *clk;
> +};
> +
> +static int ci_hdrc_berlin_probe(struct platform_device *pdev)
> +{
> + struct device *dev = &pdev->dev;
> + struct ci_hdrc_berlin_priv *priv;
> + struct ci_hdrc_platform_data ci_pdata = {
> + .name = "ci_hdrc_berlin",
> + .capoffset = DEF_CAPOFFSET,
> + .flags = CI_HDRC_REQUIRE_TRANSCEIVER |
> + CI_HDRC_DISABLE_STREAMING,
> + };
> + int ret;
> +
> + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
> + if (!priv)
> + return -ENOMEM;
> +
> + priv->clk = devm_clk_get(dev, NULL);
> + if (IS_ERR(priv->clk)) {
> + dev_err(dev, "failed to get clock: %ld\n", PTR_ERR(priv->clk));
> + return PTR_ERR(priv->clk);
> + }
> +
> + ret = clk_prepare_enable(priv->clk);
> + if (ret)
> + return ret;
> +
> + ci_pdata.phy = devm_usb_get_phy_by_phandle(dev, "marvell,usbphy", 0);
> + if (IS_ERR(ci_pdata.phy)) {
> + ret = PTR_ERR(ci_pdata.phy);
> + goto clk_err;
> + }
> +
> + priv->ci_pdev = ci_hdrc_add_device(dev, pdev->resource,
> + pdev->num_resources, &ci_pdata);
> + if (IS_ERR(priv->ci_pdev)) {
> + ret = PTR_ERR(priv->ci_pdev);
> + dev_err(dev,
> + "failed to register ci_hdrc platform device: %d\n",
> + ret);
> + goto clk_err;
> + }
> +
> + platform_set_drvdata(pdev, priv);
> +
> + pm_runtime_no_callbacks(dev);
> + pm_runtime_enable(dev);
> +
> + return 0;
> +
> +clk_err:
> + clk_disable_unprepare(priv->clk);
> + return ret;
> +}
> +
> +static int ci_hdrc_berlin_remove(struct platform_device *pdev)
> +{
> + struct ci_hdrc_berlin_priv *priv = platform_get_drvdata(pdev);
> +
> + pm_runtime_disable(&pdev->dev);
> + ci_hdrc_remove_device(priv->ci_pdev);
> + clk_disable_unprepare(priv->clk);
> +
> + return 0;
> +}
> +
> +static const struct of_device_id ci_hdrc_berlin_of_match[] = {
> + { .compatible = "marvell,berlin-usb" },

Looking at the driver, I can see no Berlin-specific code at all. Maybe
we just take the chance and have a generic chipidea boiler plate driver
now?

Sebastian

> + { }
> +};
> +
> +static struct platform_driver ci_hdrc_berlin_driver = {
> + .probe = ci_hdrc_berlin_probe,
> + .remove = ci_hdrc_berlin_remove,
> + .driver = {
> + .name = "berlin-usb",
> + .owner = THIS_MODULE,
> + .of_match_table = ci_hdrc_berlin_of_match,
> + },
> +};
> +module_platform_driver(ci_hdrc_berlin_driver);
> +
> +MODULE_DESCRIPTION("ChipIdea HDRC Berlin USB binding");
> +MODULE_AUTHOR("Antoine Ténart <[email protected]>");
> +MODULE_LICENSE("GPL");
>

2014-06-06 11:02:06

by Vivek Gautam

[permalink] [raw]
Subject: Re: [PATCH 4/9] usb: phy: add the Berlin USB PHY driver

Hi,

On Fri, Jun 6, 2014 at 12:41 PM, Antoine Ténart
<[email protected]> wrote:
> Hi,
>
> On Fri, Jun 06, 2014 at 12:09:06PM +0530, Vivek Gautam wrote:
>> On Thu, Jun 5, 2014 at 9:18 PM, Antoine Ténart
>> <[email protected]> wrote:
>> > Add the driver driving the Marvell Berlin USB PHY. This allows to
>> > initialize the PHY and to use it from the USB driver later.
>>
>> Just out of curiosity, going forward we would like to have phy drivers based on
>> generic phy framework (drivers/phy).
>> Any particular reason that we are still adding phy drivers in usb-phy layer ?
>>
>> Looking at it, seems like it can very well be written based on phy framework.
>
> This USB controller are ChipIdea compatible, and the ChipIdea common
> functions use the usb_phy framework. That's why this PHY driver is
> there.

Ok, i see that now. In that case shouldn't we be moving even the chipidea
drivers to use the generic phy functions to get the phy and init/exit it.

I think Felipe and Kishon can comment on this better, and tell how
things should be. :-)

[snip]


--
Best Regards
Vivek Gautam
Samsung R&D Institute, Bangalore
India

2014-06-06 11:59:39

by Antoine Tenart

[permalink] [raw]
Subject: Re: [PATCH 4/9] usb: phy: add the Berlin USB PHY driver

Sebastian,

On Fri, Jun 06, 2014 at 12:54:06PM +0200, Sebastian Hesselbarth wrote:
> On 06/05/2014 05:48 PM, Antoine Ténart wrote:
> >+
> >+#include <linux/gpio.h>
> >+#include <linux/io.h>
> >+#include <linux/module.h>
> >+#include <linux/of_gpio.h>
> >+#include <linux/usb/phy.h>
> >+#include <linux/platform_device.h>
> >+#include <linux/reset.h>
>
> nit: keep above alphabetically sorted.

Oops. Sure.

> >+
> >+/* USB_PHY_PLL */
> >+#define CLK_REF_DIV(x) ((x) << 4)
> >+#define FEEDBACK_CLK_DIV(x) ((x) << 8)
> >+
> >+/* USB_PHY_PLL_CONTROL */
> >+#define CLK_STABLE (0x1 << 0)
> >+#define PLL_CTRL_PIN (0x1 << 1)
> >+#define PLL_CTRL_REG (0x1 << 2)
> >+#define PLL_ON (0x1 << 3)
> >+#define PHASE_OFF_TOL_125 (0x0 << 5)
> >+#define PHASE_OFF_TOL_250 (0x1 << 5)
> >+#define KVC0_CALIB (0x0 << 9)
> >+#define KVC0_REG_CTRL (0x1 << 9)
> >+#define KVC0_HIGH (0x0 << 10)
> >+#define KVC0_LOW (0x3 << 10)
> >+#define CLK_BLK_EN (0x1 << 13)
>
> BIT() for the single bit flags above and below.

I'll update with BIT().

> >+
> >+struct berlin_phy_priv {
> >+ void __iomem *base;
> >+ struct usb_phy phy;
> >+ struct reset_control *rst_ctrl;
> >+ int pwr_gpio;
>
> Is the GPIO used for USB power? If so, we should not rely on
> GPIO at all but use regulator API. Thinking of Chromecast which
> is externally powered over USB, there will be no regulator nor
> GPIO at all.

[…]

>
> As mentioned above, this should be using regulator API. And also, if
> there is no dummy regulator allowed, it should be optional.

I'll make this optional.

>
> >+static int berlin_phy_probe(struct platform_device *pdev)
> >+{
> >+ struct device_node *np = pdev->dev.of_node;
> >+ struct berlin_phy_priv *priv;
> >+ struct resource *res;
> >+ int ret, gpio;
> >+
> >+ priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
> >+ if (!priv)
> >+ return -ENOMEM;
> >+
> >+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> >+ priv->base = devm_ioremap_resource(&pdev->dev, res);
> >+ if (IS_ERR(priv->base))
> >+ return PTR_ERR(priv->base);
> >+
> >+ priv->rst_ctrl = devm_reset_control_get(&pdev->dev, NULL);
> >+ if (IS_ERR(priv->rst_ctrl)) {
> >+ ret = PTR_ERR(priv->rst_ctrl);
> >+ dev_err(&pdev->dev, "cannot get reset controller: %d\n", ret);
>
> Hmm, considering a non arch_init call registered reset driver, it does
> also spit out an error for -EPROBE_DEFER, does it?

Yes, it does.


Antoine

--
Antoine Ténart, Free Electrons
Embedded Linux, Kernel and Android engineering
http://free-electrons.com

2014-06-06 12:01:58

by Antoine Tenart

[permalink] [raw]
Subject: Re: [PATCH 6/9] usb: chipidea: add Berlin USB support

Sebastian,

On Fri, Jun 06, 2014 at 12:55:54PM +0200, Sebastian Hesselbarth wrote:
> On 06/05/2014 05:48 PM, Antoine T?nart wrote:
> >+
> >+static const struct of_device_id ci_hdrc_berlin_of_match[] = {
> >+ { .compatible = "marvell,berlin-usb" },
>
> Looking at the driver, I can see no Berlin-specific code at all. Maybe
> we just take the chance and have a generic chipidea boiler plate driver
> now?

This driver does nothing special. If we define the PHY and the clock
properties as optional, we can do a generic driver here.


Antoine

--
Antoine T?nart, Free Electrons
Embedded Linux, Kernel and Android engineering
http://free-electrons.com

2014-06-09 05:55:31

by Peter Chen

[permalink] [raw]
Subject: Re: [PATCH 0/9] ARM: Berlin: USB support

On Thu, Jun 05, 2014 at 05:48:37PM +0200, Antoine T?nart wrote:
> This series adds the support for the Marvell Berlin USB controllers,
> the USB PHYs and also adds a reset controller.
>
> The reset controller is used by the USB PHY driver and shares the
> existing chip controller node with the clocks and one pin controller.
>
> The Marvell Berlin USB controllers are host only on the BG2Q and are
> compatible with USB ChipIdea. We here add a glue to use the available
> common functions for this kind of controllers. A USB PHY driver is also
> added to control the PHY.
>
> Antoine T?nart (9):
> reset: add the Berlin reset controller driver
> ARM: Berlin: select the reset controller
> ARM: dts: berlin: add a required reset property in the chip controller
> node
> usb: phy: add the Berlin USB PHY driver
> Documentation: bindings: add doc for the Berlin USB PHY
> usb: chipidea: add Berlin USB support
> Documentation: bindings: add doc for the Berlin ChipIdea USB driver
> ARM: dts: berlin: add BG2Q nodes for USB support
> ARM: dts: Berlin: enable USB on the BG2Q DMP
>
> .../devicetree/bindings/usb/berlin-usbphy.txt | 18 ++
> .../devicetree/bindings/usb/ci-hdrc-berlin.txt | 18 ++
> arch/arm/boot/dts/berlin2.dtsi | 1 +
> arch/arm/boot/dts/berlin2cd.dtsi | 1 +
> arch/arm/boot/dts/berlin2q-marvell-dmp.dts | 20 ++
> arch/arm/boot/dts/berlin2q.dtsi | 52 +++++
> arch/arm/mach-berlin/Kconfig | 2 +
> drivers/reset/Makefile | 1 +
> drivers/reset/reset-berlin.c | 113 +++++++++++
> drivers/usb/chipidea/Makefile | 1 +
> drivers/usb/chipidea/ci_hdrc_berlin.c | 108 ++++++++++
> drivers/usb/phy/Kconfig | 9 +
> drivers/usb/phy/Makefile | 1 +
> drivers/usb/phy/phy-berlin-usb.c | 223 +++++++++++++++++++++
> 14 files changed, 568 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/usb/berlin-usbphy.txt
> create mode 100644 Documentation/devicetree/bindings/usb/ci-hdrc-berlin.txt
> create mode 100644 drivers/reset/reset-berlin.c
> create mode 100644 drivers/usb/chipidea/ci_hdrc_berlin.c
> create mode 100644 drivers/usb/phy/phy-berlin-usb.c
>
> --
> 1.9.1
>

I am fine with 6/9, 7/9, will queue them if no other objections.

--

Best Regards,
Peter Chen

2014-06-09 08:28:38

by Jisheng Zhang

[permalink] [raw]
Subject: Re: [PATCH 4/9] usb: phy: add the Berlin USB PHY driver

Dear Sebastian and Antoine,

On Fri, 6 Jun 2014 03:54:06 -0700
Sebastian Hesselbarth <[email protected]> wrote:

> > +
> > +#define to_berlin_phy_priv(p) container_of((p), struct
> > berlin_phy_priv, phy) +
> > +struct berlin_phy_priv {
> > + void __iomem *base;
> > + struct usb_phy phy;
> > + struct reset_control *rst_ctrl;
> > + int pwr_gpio;
>
> Is the GPIO used for USB power? If so, we should not rely on

The GPIO is used for vbus. Sorry for using the confusing "pwr". Do we still
need to use regulator API?

> GPIO at all but use regulator API. Thinking of Chromecast which
> is externally powered over USB, there will be no regulator nor
> GPIO at all.
>
> > +};
> > +
> > +static int berlin_phy_init(struct usb_phy *phy)
> > +{
> > + struct berlin_phy_priv *priv = to_berlin_phy_priv(phy);
> > + int ret;
> > +
> > + reset_control_reset(priv->rst_ctrl);
> > +
> > + writel(CLK_REF_DIV(0xc) | FEEDBACK_CLK_DIV(0x54),
> > + priv->base + USB_PHY_PLL);
>
> @Jisheng: IIRC the dividers above are different for BG2? Can you please
> evaluate?

Yes, BG2 uses different refdiv and fbdiv. Is there any suggestions about how to
handle this difference? The value is chosen after carefully tunning

>
> > + writel(CLK_STABLE | PLL_CTRL_REG | PHASE_OFF_TOL_250 |
> > KVC0_REG_CTRL |
> > + CLK_BLK_EN, priv->base + USB_PHY_PLL_CONTROL);
> > + writel(V2I_VCO_RATIO(0x5) | R_ROTATE_0 | ANA_TEST_DC_CTRL(0x5),
> > + priv->base + USB_PHY_ANALOG);
> > + writel(PHASE_FREEZE_DLY_4_CL | ACK_LENGTH_16_CL | SQ_LENGTH_12 |
> > + DISCON_THRESHOLD_260 | SQ_THRESHOLD(0xa) | LPF_COEF(0x2) |
> > + INTPL_CUR_30, priv->base + USB_PHY_RX_CTRL);
> > +
> > + writel(TX_VDD12_13 | TX_OUT_AMP(0x3), priv->base +
> > USB_PHY_TX_CTRL1);
> > + writel(EXT_HS_RCAL_EN | IMPCAL_VTH_DIV(0x3) |
> > EXT_RS_RCAL_DIV(0x4),
> > + priv->base + USB_PHY_TX_CTRL0);
> > +
> > + writel(EXT_HS_RCAL_EN | IMPCAL_VTH_DIV(0x3) |
> > EXT_RS_RCAL_DIV(0x4) |
> > + EXT_FS_RCAL_DIV(0x2), priv->base + USB_PHY_TX_CTRL0);
> > +
> > + writel(EXT_HS_RCAL_EN | IMPCAL_VTH_DIV(0x3) |
> > EXT_RS_RCAL_DIV(0x4),
> > + priv->base + USB_PHY_TX_CTRL0);
> > + writel(TX_CHAN_CTRL_REG(0xf) | DRV_SLEWRATE(0x3) |
> > IMP_CAL_FS_HS_DLY_3 |
> > + FS_DRV_EN_MASK(0xd), priv->base + USB_PHY_TX_CTRL2);
> > +
> > + ret = gpio_direction_output(priv->pwr_gpio, 0);
>
> As mentioned above, this should be using regulator API. And also, if
> there is no dummy regulator allowed, it should be optional.
>
> > + if (ret)
> > + return ret;
> > +
> > + gpio_set_value(priv->pwr_gpio, 1);
> > +
> > + return 0;
> > +}
> > +
> > +static void berlin_phy_shutdown(struct usb_phy *phy)
> > +{
> > + struct berlin_phy_priv *priv = to_berlin_phy_priv(phy);
> > +
> > + gpio_set_value(priv->pwr_gpio, 0);
> > +}
> > +
> > +static int berlin_phy_probe(struct platform_device *pdev)
> > +{
> > + struct device_node *np = pdev->dev.of_node;
> > + struct berlin_phy_priv *priv;
> > + struct resource *res;
> > + int ret, gpio;
> > +
> > + priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
> > + if (!priv)
> > + return -ENOMEM;
> > +
> > + res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> > + priv->base = devm_ioremap_resource(&pdev->dev, res);
> > + if (IS_ERR(priv->base))
> > + return PTR_ERR(priv->base);
> > +
> > + priv->rst_ctrl = devm_reset_control_get(&pdev->dev, NULL);
> > + if (IS_ERR(priv->rst_ctrl)) {
> > + ret = PTR_ERR(priv->rst_ctrl);
> > + dev_err(&pdev->dev, "cannot get reset controller: %d\n",
> > ret);
>
> Hmm, considering a non arch_init call registered reset driver, it does
> also spit out an error for -EPROBE_DEFER, does it?
>
> > + return ret;
> > + }
> > +
> > + gpio = of_get_named_gpio(np, "power-gpio", 0);
> > + if (!gpio_is_valid(gpio))
> > + return gpio;

Some BG2Q boards hardwired the vbus to be always powered on, we should continue
the probe if vbus gpio is missing.

Thanks,
Jisheng

2014-06-09 10:12:07

by Sebastian Hesselbarth

[permalink] [raw]
Subject: Re: [PATCH 4/9] usb: phy: add the Berlin USB PHY driver

On 06/09/2014 10:26 AM, Jisheng Zhang wrote:
> Dear Sebastian and Antoine,
>
> On Fri, 6 Jun 2014 03:54:06 -0700
> Sebastian Hesselbarth <[email protected]> wrote:
>
>>> +
>>> +#define to_berlin_phy_priv(p) container_of((p), struct
>>> berlin_phy_priv, phy) +
>>> +struct berlin_phy_priv {
>>> + void __iomem *base;
>>> + struct usb_phy phy;
>>> + struct reset_control *rst_ctrl;
>>> + int pwr_gpio;
>>
>> Is the GPIO used for USB power? If so, we should not rely on
>
> The GPIO is used for vbus. Sorry for using the confusing "pwr". Do we still
> need to use regulator API?

Yes, I guess using regulator is still the way to go. Also, I think
it should be up to the controller to power on/off the device. That
way, we could make use of the dual role controller features on BG2
and BG2CD.

>> GPIO at all but use regulator API. Thinking of Chromecast which
>> is externally powered over USB, there will be no regulator nor
>> GPIO at all.
>>
>>> +};
>>> +
>>> +static int berlin_phy_init(struct usb_phy *phy)
>>> +{
>>> + struct berlin_phy_priv *priv = to_berlin_phy_priv(phy);
>>> + int ret;
>>> +
>>> + reset_control_reset(priv->rst_ctrl);
>>> +
>>> + writel(CLK_REF_DIV(0xc) | FEEDBACK_CLK_DIV(0x54),
>>> + priv->base + USB_PHY_PLL);
>>
>> @Jisheng: IIRC the dividers above are different for BG2? Can you please
>> evaluate?
>
> Yes, BG2 uses different refdiv and fbdiv. Is there any suggestions about how to
> handle this difference? The value is chosen after carefully tunning

I guess it depends on how many different div values you expect for
berlin2 usb PHYs. If it is just the two, we can go with different
compatibles for e.g. "berlin2-usb-phy" and "berlin2cd-usb-phy".

If you know of more PHYs with different div, a corresponding vendor-
specific property should do the trick, e.g.
marvell,pll-divider = <0x54c0>;

I am fine with both.

>>
>>> + writel(CLK_STABLE | PLL_CTRL_REG | PHASE_OFF_TOL_250 |
>>> KVC0_REG_CTRL |
>>> + CLK_BLK_EN, priv->base + USB_PHY_PLL_CONTROL);
>>> + writel(V2I_VCO_RATIO(0x5) | R_ROTATE_0 | ANA_TEST_DC_CTRL(0x5),
>>> + priv->base + USB_PHY_ANALOG);
>>> + writel(PHASE_FREEZE_DLY_4_CL | ACK_LENGTH_16_CL | SQ_LENGTH_12 |
>>> + DISCON_THRESHOLD_260 | SQ_THRESHOLD(0xa) | LPF_COEF(0x2) |
>>> + INTPL_CUR_30, priv->base + USB_PHY_RX_CTRL);
>>> +
>>> + writel(TX_VDD12_13 | TX_OUT_AMP(0x3), priv->base +
>>> USB_PHY_TX_CTRL1);
>>> + writel(EXT_HS_RCAL_EN | IMPCAL_VTH_DIV(0x3) |
>>> EXT_RS_RCAL_DIV(0x4),
>>> + priv->base + USB_PHY_TX_CTRL0);
>>> +
>>> + writel(EXT_HS_RCAL_EN | IMPCAL_VTH_DIV(0x3) |
>>> EXT_RS_RCAL_DIV(0x4) |
>>> + EXT_FS_RCAL_DIV(0x2), priv->base + USB_PHY_TX_CTRL0);
>>> +
>>> + writel(EXT_HS_RCAL_EN | IMPCAL_VTH_DIV(0x3) |
>>> EXT_RS_RCAL_DIV(0x4),
>>> + priv->base + USB_PHY_TX_CTRL0);
>>> + writel(TX_CHAN_CTRL_REG(0xf) | DRV_SLEWRATE(0x3) |
>>> IMP_CAL_FS_HS_DLY_3 |
>>> + FS_DRV_EN_MASK(0xd), priv->base + USB_PHY_TX_CTRL2);
>>> +
>>> + ret = gpio_direction_output(priv->pwr_gpio, 0);
>>
>> As mentioned above, this should be using regulator API. And also, if
>> there is no dummy regulator allowed, it should be optional.
>>
>>> + if (ret)
>>> + return ret;
>>> +
>>> + gpio_set_value(priv->pwr_gpio, 1);
>>> +
>>> + return 0;
>>> +}
>>> +
>>> +static void berlin_phy_shutdown(struct usb_phy *phy)
>>> +{
>>> + struct berlin_phy_priv *priv = to_berlin_phy_priv(phy);
>>> +
>>> + gpio_set_value(priv->pwr_gpio, 0);
>>> +}
>>> +
>>> +static int berlin_phy_probe(struct platform_device *pdev)
>>> +{
>>> + struct device_node *np = pdev->dev.of_node;
>>> + struct berlin_phy_priv *priv;
>>> + struct resource *res;
>>> + int ret, gpio;
>>> +
>>> + priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
>>> + if (!priv)
>>> + return -ENOMEM;
>>> +
>>> + res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
>>> + priv->base = devm_ioremap_resource(&pdev->dev, res);
>>> + if (IS_ERR(priv->base))
>>> + return PTR_ERR(priv->base);
>>> +
>>> + priv->rst_ctrl = devm_reset_control_get(&pdev->dev, NULL);
>>> + if (IS_ERR(priv->rst_ctrl)) {
>>> + ret = PTR_ERR(priv->rst_ctrl);
>>> + dev_err(&pdev->dev, "cannot get reset controller: %d\n",
>>> ret);
>>
>> Hmm, considering a non arch_init call registered reset driver, it does
>> also spit out an error for -EPROBE_DEFER, does it?
>>
>>> + return ret;
>>> + }
>>> +
>>> + gpio = of_get_named_gpio(np, "power-gpio", 0);
>>> + if (!gpio_is_valid(gpio))
>>> + return gpio;
>
> Some BG2Q boards hardwired the vbus to be always powered on, we should continue
> the probe if vbus gpio is missing.

Yeah, the same applies for regulators. But with the comments above, it
should move to the controller stub instead.

Sebastian

2014-06-09 10:14:53

by Sebastian Hesselbarth

[permalink] [raw]
Subject: Re: [PATCH 0/9] ARM: Berlin: USB support

On 06/09/2014 06:30 AM, Peter Chen wrote:
> On Thu, Jun 05, 2014 at 05:48:37PM +0200, Antoine T?nart wrote:
>> This series adds the support for the Marvell Berlin USB controllers,
>> the USB PHYs and also adds a reset controller.
>>
>> The reset controller is used by the USB PHY driver and shares the
>> existing chip controller node with the clocks and one pin controller.
>>
>> The Marvell Berlin USB controllers are host only on the BG2Q and are
>> compatible with USB ChipIdea. We here add a glue to use the available
>> common functions for this kind of controllers. A USB PHY driver is also
>> added to control the PHY.
[...]
>
> I am fine with 6/9, 7/9, will queue them if no other objections.
>

Thanks Peter,

we are early in the cycle and I feel there will be a v2 of the
chipidea stub. IMHO, controlling the vbus regulator should not
be business of the phy driver, so ci will have to deal with it.

Anyways, good to know you are fine with it already!

Sebastian

2014-06-09 10:33:05

by Sebastian Hesselbarth

[permalink] [raw]
Subject: Re: [PATCH 1/9] reset: add the Berlin reset controller driver

On 06/05/2014 05:48 PM, Antoine Ténart wrote:
> Add a reset controller for Marvell Berlin SoCs which is used by the
> USB PHYs drivers (for now).
>
> Signed-off-by: Antoine Ténart <[email protected]>
> ---
> drivers/reset/Makefile | 1 +
> drivers/reset/reset-berlin.c | 113 +++++++++++++++++++++++++++++++++++++++++++
> 2 files changed, 114 insertions(+)
> create mode 100644 drivers/reset/reset-berlin.c
>
> diff --git a/drivers/reset/Makefile b/drivers/reset/Makefile
> index 4f60caf750ce..fffe2a3dd255 100644
> --- a/drivers/reset/Makefile
> +++ b/drivers/reset/Makefile
> @@ -1,3 +1,4 @@
> obj-$(CONFIG_RESET_CONTROLLER) += core.o
> +obj-$(CONFIG_ARCH_BERLIN) += reset-berlin.o
> obj-$(CONFIG_ARCH_SUNXI) += reset-sunxi.o
> obj-$(CONFIG_ARCH_STI) += sti/
> diff --git a/drivers/reset/reset-berlin.c b/drivers/reset/reset-berlin.c
> new file mode 100644
> index 000000000000..78b42c882cb2
> --- /dev/null
> +++ b/drivers/reset/reset-berlin.c
> @@ -0,0 +1,113 @@
> +/*
> + * Copyright (C) 2014 Marvell Technology Group Ltd.
> + *
> + * Antoine Ténart <[email protected]>
> + *
> + * This file is licensed under the terms of the GNU General Public
> + * License version 2. This program is licensed "as is" without any
> + * warranty of any kind, whether express or implied.
> + */
> +
> +#include <linux/delay.h>
> +#include <linux/io.h>
> +#include <linux/module.h>
> +#include <linux/of.h>
> +#include <linux/of_address.h>
> +#include <linux/platform_device.h>
> +#include <linux/reset-controller.h>
> +#include <linux/slab.h>
> +#include <linux/spinlock.h>
> +#include <linux/types.h>
> +
> +#define BERLIN_RESET_REGISTER 0x104
> +
> +#define to_berlin_reset_priv(p) \
> + container_of((p), struct berlin_reset_priv, rcdev)
> +
> +struct berlin_reset_priv {
> + spinlock_t lock;
> + void __iomem *base;
> + struct reset_controller_dev rcdev;
> +};
> +
> +static int berlin_reset_reset(struct reset_controller_dev *rcdev,
> + unsigned long id)
> +{
> + struct berlin_reset_priv *priv = to_berlin_reset_priv(rcdev);
> + unsigned long flags;
> + int bank = id / BITS_PER_LONG;
> + int offset = id % BITS_PER_LONG;
> +
> + spin_lock_irqsave(&priv->lock, flags);
> +
> + writel(BIT(offset), priv->base + bank * 4);
> +
> + spin_unlock_irqrestore(&priv->lock, flags);
> +
> + /* let the reset be effective */
> + udelay(10);
> +
> + return 0;
> +}
> +
> +static struct reset_control_ops berlin_reset_ops = {
> + .reset = berlin_reset_reset,
> +};
> +
> +static int __berlin_reset_init(struct device_node *np)
> +{
> + struct berlin_reset_priv *priv;
> + struct resource res;
> + resource_size_t size;
> + int ret;
> +
> + priv = kzalloc(sizeof(*priv), GFP_KERNEL);
> + if (!priv)
> + return -ENOMEM;
> +
> + ret = of_address_to_resource(np, 0, &res);
> + if (ret)
> + goto err;
> +
> + size = resource_size(&res);
> +
> + priv->base = ioremap(res.start, size);
> + if (!priv->base) {
> + ret = -ENOMEM;
> + goto err;
> + }
> + priv->base += BERLIN_RESET_REGISTER;

I currently have no way to look it up myself, but is reset API providing
a way to deal with phandle+specifier with more than one parameter?
Chip-ctrl has a bunch of other reset bits spread over the regset, having
the offset encoded in the specifier would save us some SoC specific
boiler plate, i.e.

reset = <&chip 0x104 14>;

Sebastian

> + priv->rcdev.owner = THIS_MODULE;
> + priv->rcdev.nr_resets = size * 32;
> + priv->rcdev.ops = &berlin_reset_ops;
> + priv->rcdev.of_node = np;
> +
> + reset_controller_register(&priv->rcdev);
> +
> + return 0;
> +
> +err:
> + kfree(priv);
> + return ret;
> +}
> +
> +static const struct of_device_id berlin_reset_of_match[] __initdata = {
> + { .compatible = "marvell,berlin2q-chip-ctrl" },
> + { },
> +};
> +
> +static int __init berlin_reset_init(void)
> +{
> + struct device_node *np;
> + int ret;
> +
> + for_each_matching_node(np, berlin_reset_of_match) {
> + ret = __berlin_reset_init(np);
> + if (ret)
> + return ret;
> + }
> +
> + return 0;
> +}
> +arch_initcall(berlin_reset_init);
>

2014-06-09 10:52:48

by Alexandre Belloni

[permalink] [raw]
Subject: Re: [PATCH 4/9] usb: phy: add the Berlin USB PHY driver

On 09/06/2014 at 12:11:57 +0200, Sebastian Hesselbarth wrote :
> > Some BG2Q boards hardwired the vbus to be always powered on, we should continue
> > the probe if vbus gpio is missing.
>
> Yeah, the same applies for regulators. But with the comments above, it
> should move to the controller stub instead.
>

We should use a regulator and in the case it is hardwired, use a fixed
regulator. Then, we can stop if it is missing.

--
Alexandre Belloni, Free Electrons
Embedded Linux, Kernel and Android engineering
http://free-electrons.com

2014-06-09 11:23:23

by Philipp Zabel

[permalink] [raw]
Subject: Re: [PATCH 1/9] reset: add the Berlin reset controller driver

Hi Sebastian,

On Mon, Jun 09, 2014 at 12:32:50PM +0200, Sebastian Hesselbarth wrote:
> I currently have no way to look it up myself, but is reset API providing
> a way to deal with phandle+specifier with more than one parameter?

You could provide a custom rcdev->of_xlate callback and encode
multiple phandle args into the reset line "index".

static int of_reset_xlate(struct reset_controller_dev *rcdev,
const struct of_phandle_args *reset_spec)
{
return reset_spec->args[0] * 32 + reset_spec->args[1];
}

> Chip-ctrl has a bunch of other reset bits spread over the regset, having
> the offset encoded in the specifier would save us some SoC specific
> boiler plate, i.e.
>
> reset = <&chip 0x104 14>;

That should be possible.

regards
Philipp

2014-06-10 01:16:24

by Peter Chen

[permalink] [raw]
Subject: RE: [PATCH 0/9] ARM: Berlin: USB support


>
> we are early in the cycle and I feel there will be a v2 of the chipidea
> stub. IMHO, controlling the vbus regulator should not be business of the
> phy driver, so ci will have to deal with it.
>

vbus handling has already in chipidea common code, it treats vbus as a regulator.
So, the glue layer only needs to register and add vbus-supply to dts file, no source
code change is needed.

Peter

> Anyways, good to know you are fine with it already!
>
> Sebastian

2014-06-19 13:46:18

by Felipe Balbi

[permalink] [raw]
Subject: Re: [PATCH 4/9] usb: phy: add the Berlin USB PHY driver

On Fri, Jun 06, 2014 at 04:32:03PM +0530, Vivek Gautam wrote:
> Hi,
>
> On Fri, Jun 6, 2014 at 12:41 PM, Antoine T?nart
> <[email protected]> wrote:
> > Hi,
> >
> > On Fri, Jun 06, 2014 at 12:09:06PM +0530, Vivek Gautam wrote:
> >> On Thu, Jun 5, 2014 at 9:18 PM, Antoine T?nart
> >> <[email protected]> wrote:
> >> > Add the driver driving the Marvell Berlin USB PHY. This allows to
> >> > initialize the PHY and to use it from the USB driver later.
> >>
> >> Just out of curiosity, going forward we would like to have phy drivers based on
> >> generic phy framework (drivers/phy).
> >> Any particular reason that we are still adding phy drivers in usb-phy layer ?
> >>
> >> Looking at it, seems like it can very well be written based on phy framework.
> >
> > This USB controller are ChipIdea compatible, and the ChipIdea common
> > functions use the usb_phy framework. That's why this PHY driver is
> > there.
>
> Ok, i see that now. In that case shouldn't we be moving even the chipidea
> drivers to use the generic phy functions to get the phy and init/exit it.

yes, we should :-)

> I think Felipe and Kishon can comment on this better, and tell how
> things should be. :-)

yeah, this will just make it a little more difficult to deprecate
current usb phy layer.

--
balbi


Attachments:
(No filename) (1.29 kB)
signature.asc (819.00 B)
Digital signature
Download all attachments