Targets were build tested and patches are refreshed. Signed-off-by: Luka Perkov <luka@openwrt.org> SVN-Revision: 42463
		
			
				
	
	
		
			252 lines
		
	
	
		
			8.0 KiB
		
	
	
	
		
			Diff
		
	
	
	
	
	
			
		
		
	
	
			252 lines
		
	
	
		
			8.0 KiB
		
	
	
	
		
			Diff
		
	
	
	
	
	
From 7554dfbbec255e4cd5dd4445841c28c48fd4a855 Mon Sep 17 00:00:00 2001
 | 
						|
From: Kumar Gala <galak@codeaurora.org>
 | 
						|
Date: Tue, 3 Jun 2014 11:59:09 -0500
 | 
						|
Subject: [PATCH 144/182] phy: qcom: Add driver for QCOM IPQ806x SATA PHY
 | 
						|
 | 
						|
Add a PHY driver for uses with AHCI based SATA controller driver on the
 | 
						|
IPQ806x family of SoCs.
 | 
						|
 | 
						|
Signed-off-by: Kumar Gala <galak@codeaurora.org>
 | 
						|
---
 | 
						|
 drivers/phy/Kconfig                 |    7 ++
 | 
						|
 drivers/phy/Makefile                |    1 +
 | 
						|
 drivers/phy/phy-qcom-ipq806x-sata.c |  211 +++++++++++++++++++++++++++++++++++
 | 
						|
 3 files changed, 219 insertions(+)
 | 
						|
 create mode 100644 drivers/phy/phy-qcom-ipq806x-sata.c
 | 
						|
 | 
						|
--- a/drivers/phy/Kconfig
 | 
						|
+++ b/drivers/phy/Kconfig
 | 
						|
@@ -65,4 +65,11 @@ config BCM_KONA_USB2_PHY
 | 
						|
 	help
 | 
						|
 	  Enable this to support the Broadcom Kona USB 2.0 PHY.
 | 
						|
 
 | 
						|
+config PHY_QCOM_IPQ806X_SATA
 | 
						|
+	tristate "Qualcomm IPQ806x SATA SerDes/PHY driver"
 | 
						|
+	depends on ARCH_QCOM
 | 
						|
+	depends on HAS_IOMEM
 | 
						|
+	depends on OF
 | 
						|
+	select GENERIC_PHY
 | 
						|
+
 | 
						|
 endmenu
 | 
						|
--- a/drivers/phy/Makefile
 | 
						|
+++ b/drivers/phy/Makefile
 | 
						|
@@ -9,3 +9,4 @@ obj-$(CONFIG_PHY_EXYNOS_MIPI_VIDEO)	+= p
 | 
						|
 obj-$(CONFIG_PHY_MVEBU_SATA)		+= phy-mvebu-sata.o
 | 
						|
 obj-$(CONFIG_OMAP_USB2)			+= phy-omap-usb2.o
 | 
						|
 obj-$(CONFIG_TWL4030_USB)		+= phy-twl4030-usb.o
 | 
						|
+obj-$(CONFIG_PHY_QCOM_IPQ806X_SATA)	+= phy-qcom-ipq806x-sata.o
 | 
						|
--- /dev/null
 | 
						|
+++ b/drivers/phy/phy-qcom-ipq806x-sata.c
 | 
						|
@@ -0,0 +1,211 @@
 | 
						|
+/*
 | 
						|
+ * Copyright (c) 2014, The Linux Foundation. All rights reserved.
 | 
						|
+ *
 | 
						|
+ * This program is free software; you can redistribute it and/or modify
 | 
						|
+ * it under the terms of the GNU General Public License version 2 and
 | 
						|
+ * only version 2 as published by the Free Software Foundation.
 | 
						|
+ *
 | 
						|
+ * This program is distributed in the hope that it will be useful,
 | 
						|
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
						|
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
						|
+ * GNU General Public License for more details.
 | 
						|
+ */
 | 
						|
+
 | 
						|
+#include <linux/io.h>
 | 
						|
+#include <linux/kernel.h>
 | 
						|
+#include <linux/module.h>
 | 
						|
+#include <linux/of.h>
 | 
						|
+#include <linux/of_address.h>
 | 
						|
+#include <linux/time.h>
 | 
						|
+#include <linux/delay.h>
 | 
						|
+#include <linux/clk.h>
 | 
						|
+#include <linux/slab.h>
 | 
						|
+#include <linux/platform_device.h>
 | 
						|
+#include <linux/phy/phy.h>
 | 
						|
+
 | 
						|
+struct qcom_ipq806x_sata_phy {
 | 
						|
+	void __iomem *mmio;
 | 
						|
+	struct clk *cfg_clk;
 | 
						|
+};
 | 
						|
+
 | 
						|
+#define __set(v, a, b)	(((v) << (b)) & GENMASK(a, b))
 | 
						|
+
 | 
						|
+#define SATA_PHY_P0_PARAM0		0x200
 | 
						|
+#define SATA_PHY_P0_PARAM0_P0_TX_PREEMPH_GEN3(x)	__set(x, 17, 12)
 | 
						|
+#define SATA_PHY_P0_PARAM0_P0_TX_PREEMPH_GEN3_MASK	GENMASK(17, 12)
 | 
						|
+#define SATA_PHY_P0_PARAM0_P0_TX_PREEMPH_GEN2(x)	__set(x, 11, 6)
 | 
						|
+#define SATA_PHY_P0_PARAM0_P0_TX_PREEMPH_GEN2_MASK	GENMASK(11, 6)
 | 
						|
+#define SATA_PHY_P0_PARAM0_P0_TX_PREEMPH_GEN1(x)	__set(x, 5, 0)
 | 
						|
+#define SATA_PHY_P0_PARAM0_P0_TX_PREEMPH_GEN1_MASK	GENMASK(5, 0)
 | 
						|
+
 | 
						|
+#define SATA_PHY_P0_PARAM1		0x204
 | 
						|
+#define SATA_PHY_P0_PARAM1_RESERVED_BITS31_21(x)	__set(x, 31, 21)
 | 
						|
+#define SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN3(x)	__set(x, 20, 14)
 | 
						|
+#define SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN3_MASK	GENMASK(20, 14)
 | 
						|
+#define SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN2(x)	__set(x, 13, 7)
 | 
						|
+#define SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN2_MASK	GENMASK(13, 7)
 | 
						|
+#define SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN1(x)	__set(x, 6, 0)
 | 
						|
+#define SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN1_MASK	GENMASK(6, 0)
 | 
						|
+
 | 
						|
+#define SATA_PHY_P0_PARAM2		0x208
 | 
						|
+#define SATA_PHY_P0_PARAM2_RX_EQ(x)	__set(x, 20, 18)
 | 
						|
+#define SATA_PHY_P0_PARAM2_RX_EQ_MASK	GENMASK(20, 18)
 | 
						|
+
 | 
						|
+#define SATA_PHY_P0_PARAM3		0x20C
 | 
						|
+#define SATA_PHY_SSC_EN			0x8
 | 
						|
+#define SATA_PHY_P0_PARAM4		0x210
 | 
						|
+#define SATA_PHY_REF_SSP_EN		0x2
 | 
						|
+#define SATA_PHY_RESET			0x1
 | 
						|
+
 | 
						|
+static inline void qcom_ipq806x_sata_delay_us(unsigned int delay)
 | 
						|
+{
 | 
						|
+	/* sleep for max. 50us more to combine processor wakeups */
 | 
						|
+	usleep_range(delay, delay + 50);
 | 
						|
+}
 | 
						|
+
 | 
						|
+static int qcom_ipq806x_sata_phy_init(struct phy *generic_phy)
 | 
						|
+{
 | 
						|
+	struct qcom_ipq806x_sata_phy *phy = phy_get_drvdata(generic_phy);
 | 
						|
+	u32 reg;
 | 
						|
+
 | 
						|
+	/* Setting SSC_EN to 1 */
 | 
						|
+	reg = readl_relaxed(phy->mmio + SATA_PHY_P0_PARAM3);
 | 
						|
+	reg = reg | SATA_PHY_SSC_EN;
 | 
						|
+	writel_relaxed(reg, phy->mmio + SATA_PHY_P0_PARAM3);
 | 
						|
+
 | 
						|
+	reg = readl_relaxed(phy->mmio + SATA_PHY_P0_PARAM0) &
 | 
						|
+			~(SATA_PHY_P0_PARAM0_P0_TX_PREEMPH_GEN3_MASK |
 | 
						|
+			  SATA_PHY_P0_PARAM0_P0_TX_PREEMPH_GEN2_MASK |
 | 
						|
+			  SATA_PHY_P0_PARAM0_P0_TX_PREEMPH_GEN1_MASK);
 | 
						|
+	reg |= SATA_PHY_P0_PARAM0_P0_TX_PREEMPH_GEN3(0xf);
 | 
						|
+	writel_relaxed(reg, phy->mmio + SATA_PHY_P0_PARAM0);
 | 
						|
+
 | 
						|
+	reg = readl_relaxed(phy->mmio + SATA_PHY_P0_PARAM1) &
 | 
						|
+			~(SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN3_MASK |
 | 
						|
+			  SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN2_MASK |
 | 
						|
+			  SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN1_MASK);
 | 
						|
+	reg |= SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN3(0x55) |
 | 
						|
+		SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN2(0x55) |
 | 
						|
+		SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN1(0x55);
 | 
						|
+	writel_relaxed(reg, phy->mmio + SATA_PHY_P0_PARAM1);
 | 
						|
+
 | 
						|
+	reg = readl_relaxed(phy->mmio + SATA_PHY_P0_PARAM2) &
 | 
						|
+		~SATA_PHY_P0_PARAM2_RX_EQ_MASK;
 | 
						|
+	reg |= SATA_PHY_P0_PARAM2_RX_EQ(0x3);
 | 
						|
+	writel_relaxed(reg, phy->mmio + SATA_PHY_P0_PARAM2);
 | 
						|
+
 | 
						|
+	/* Setting PHY_RESET to 1 */
 | 
						|
+	reg = readl_relaxed(phy->mmio + SATA_PHY_P0_PARAM4);
 | 
						|
+	reg = reg | SATA_PHY_RESET;
 | 
						|
+	writel_relaxed(reg, phy->mmio + SATA_PHY_P0_PARAM4);
 | 
						|
+
 | 
						|
+	/* Setting REF_SSP_EN to 1 */
 | 
						|
+	reg = readl_relaxed(phy->mmio + SATA_PHY_P0_PARAM4);
 | 
						|
+	reg = reg | SATA_PHY_REF_SSP_EN | SATA_PHY_RESET;
 | 
						|
+	writel_relaxed(reg, phy->mmio + SATA_PHY_P0_PARAM4);
 | 
						|
+	mb();
 | 
						|
+	qcom_ipq806x_sata_delay_us(20);
 | 
						|
+
 | 
						|
+	/* Clearing PHY_RESET to 0 */
 | 
						|
+	reg = readl_relaxed(phy->mmio + SATA_PHY_P0_PARAM4);
 | 
						|
+	reg = reg & ~SATA_PHY_RESET;
 | 
						|
+	writel_relaxed(reg, phy->mmio + SATA_PHY_P0_PARAM4);
 | 
						|
+
 | 
						|
+	return 0;
 | 
						|
+}
 | 
						|
+
 | 
						|
+static int qcom_ipq806x_sata_phy_exit(struct phy *generic_phy)
 | 
						|
+{
 | 
						|
+	struct qcom_ipq806x_sata_phy *phy = phy_get_drvdata(generic_phy);
 | 
						|
+	u32 reg;
 | 
						|
+
 | 
						|
+	/* Setting PHY_RESET to 1 */
 | 
						|
+	reg = readl_relaxed(phy->mmio + SATA_PHY_P0_PARAM4);
 | 
						|
+	reg = reg | SATA_PHY_RESET;
 | 
						|
+	writel_relaxed(reg, phy->mmio + SATA_PHY_P0_PARAM4);
 | 
						|
+
 | 
						|
+	return 0;
 | 
						|
+}
 | 
						|
+
 | 
						|
+static struct phy_ops qcom_ipq806x_sata_phy_ops = {
 | 
						|
+	.init		= qcom_ipq806x_sata_phy_init,
 | 
						|
+	.exit		= qcom_ipq806x_sata_phy_exit,
 | 
						|
+	.owner		= THIS_MODULE,
 | 
						|
+};
 | 
						|
+
 | 
						|
+static int qcom_ipq806x_sata_phy_probe(struct platform_device *pdev)
 | 
						|
+{
 | 
						|
+	struct qcom_ipq806x_sata_phy *phy;
 | 
						|
+	struct device *dev = &pdev->dev;
 | 
						|
+	struct resource *res;
 | 
						|
+	struct phy_provider *phy_provider;
 | 
						|
+	struct phy *generic_phy;
 | 
						|
+	int ret;
 | 
						|
+
 | 
						|
+	phy = devm_kzalloc(dev, sizeof(*phy), GFP_KERNEL);
 | 
						|
+	if (!phy) {
 | 
						|
+		dev_err(dev, "%s: failed to allocate phy\n", __func__);
 | 
						|
+		return -ENOMEM;
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 | 
						|
+	phy->mmio = devm_ioremap_resource(dev, res);
 | 
						|
+	if (IS_ERR(phy->mmio))
 | 
						|
+		return PTR_ERR(phy->mmio);
 | 
						|
+
 | 
						|
+	generic_phy = devm_phy_create(dev, &qcom_ipq806x_sata_phy_ops, NULL);
 | 
						|
+	if (IS_ERR(generic_phy)) {
 | 
						|
+		dev_err(dev, "%s: failed to create phy\n", __func__);
 | 
						|
+		return PTR_ERR(generic_phy);
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	phy_set_drvdata(generic_phy, phy);
 | 
						|
+
 | 
						|
+	phy->cfg_clk = devm_clk_get(dev, "cfg");
 | 
						|
+	if (IS_ERR(phy->cfg_clk)) {
 | 
						|
+		dev_err(dev, "Failed to get sata cfg clock\n");
 | 
						|
+		return PTR_ERR(phy->cfg_clk);
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	ret = clk_prepare_enable(phy->cfg_clk);
 | 
						|
+	if (ret)
 | 
						|
+		return ret;
 | 
						|
+
 | 
						|
+	phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
 | 
						|
+	if (IS_ERR(phy_provider)) {
 | 
						|
+		clk_disable_unprepare(phy->cfg_clk);
 | 
						|
+		dev_err(dev, "%s: failed to register phy\n", __func__);
 | 
						|
+		return PTR_ERR(phy_provider);
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	return 0;
 | 
						|
+}
 | 
						|
+
 | 
						|
+static int qcom_ipq806x_sata_phy_remove(struct platform_device *pdev)
 | 
						|
+{
 | 
						|
+	struct qcom_ipq806x_sata_phy *phy = platform_get_drvdata(pdev);
 | 
						|
+
 | 
						|
+	clk_disable_unprepare(phy->cfg_clk);
 | 
						|
+
 | 
						|
+	return 0;
 | 
						|
+}
 | 
						|
+
 | 
						|
+static const struct of_device_id qcom_ipq806x_sata_phy_of_match[] = {
 | 
						|
+	{ .compatible = "qcom,ipq806x-sata-phy" },
 | 
						|
+	{ },
 | 
						|
+};
 | 
						|
+MODULE_DEVICE_TABLE(of, qcom_ipq806x_sata_phy_of_match);
 | 
						|
+
 | 
						|
+static struct platform_driver qcom_ipq806x_sata_phy_driver = {
 | 
						|
+	.probe	= qcom_ipq806x_sata_phy_probe,
 | 
						|
+	.remove	= qcom_ipq806x_sata_phy_remove,
 | 
						|
+	.driver = {
 | 
						|
+		.name	= "qcom-ipq806x-sata-phy",
 | 
						|
+		.owner	= THIS_MODULE,
 | 
						|
+		.of_match_table	= qcom_ipq806x_sata_phy_of_match,
 | 
						|
+	}
 | 
						|
+};
 | 
						|
+module_platform_driver(qcom_ipq806x_sata_phy_driver);
 | 
						|
+
 | 
						|
+MODULE_DESCRIPTION("QCOM IPQ806x SATA PHY driver");
 | 
						|
+MODULE_LICENSE("GPL v2");
 |