162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * ST spear1340-miphy driver
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (C) 2014 ST Microelectronics
662306a36Sopenharmony_ci * Pratyush Anand <pratyush.anand@gmail.com>
762306a36Sopenharmony_ci * Mohit Kumar <mohit.kumar.dhaka@gmail.com>
862306a36Sopenharmony_ci */
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci#include <linux/bitops.h>
1162306a36Sopenharmony_ci#include <linux/delay.h>
1262306a36Sopenharmony_ci#include <linux/dma-mapping.h>
1362306a36Sopenharmony_ci#include <linux/kernel.h>
1462306a36Sopenharmony_ci#include <linux/mfd/syscon.h>
1562306a36Sopenharmony_ci#include <linux/module.h>
1662306a36Sopenharmony_ci#include <linux/of.h>
1762306a36Sopenharmony_ci#include <linux/phy/phy.h>
1862306a36Sopenharmony_ci#include <linux/platform_device.h>
1962306a36Sopenharmony_ci#include <linux/regmap.h>
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_ci/* SPEAr1340 Registers */
2262306a36Sopenharmony_ci/* Power Management Registers */
2362306a36Sopenharmony_ci#define SPEAR1340_PCM_CFG			0x100
2462306a36Sopenharmony_ci	#define SPEAR1340_PCM_CFG_SATA_POWER_EN		BIT(11)
2562306a36Sopenharmony_ci#define SPEAR1340_PCM_WKUP_CFG			0x104
2662306a36Sopenharmony_ci#define SPEAR1340_SWITCH_CTR			0x108
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_ci#define SPEAR1340_PERIP1_SW_RST			0x318
2962306a36Sopenharmony_ci	#define SPEAR1340_PERIP1_SW_RSATA		BIT(12)
3062306a36Sopenharmony_ci#define SPEAR1340_PERIP2_SW_RST			0x31C
3162306a36Sopenharmony_ci#define SPEAR1340_PERIP3_SW_RST			0x320
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_ci/* PCIE - SATA configuration registers */
3462306a36Sopenharmony_ci#define SPEAR1340_PCIE_SATA_CFG			0x424
3562306a36Sopenharmony_ci	/* PCIE CFG MASks */
3662306a36Sopenharmony_ci	#define SPEAR1340_PCIE_CFG_DEVICE_PRESENT	BIT(11)
3762306a36Sopenharmony_ci	#define SPEAR1340_PCIE_CFG_POWERUP_RESET	BIT(10)
3862306a36Sopenharmony_ci	#define SPEAR1340_PCIE_CFG_CORE_CLK_EN		BIT(9)
3962306a36Sopenharmony_ci	#define SPEAR1340_PCIE_CFG_AUX_CLK_EN		BIT(8)
4062306a36Sopenharmony_ci	#define SPEAR1340_SATA_CFG_TX_CLK_EN		BIT(4)
4162306a36Sopenharmony_ci	#define SPEAR1340_SATA_CFG_RX_CLK_EN		BIT(3)
4262306a36Sopenharmony_ci	#define SPEAR1340_SATA_CFG_POWERUP_RESET	BIT(2)
4362306a36Sopenharmony_ci	#define SPEAR1340_SATA_CFG_PM_CLK_EN		BIT(1)
4462306a36Sopenharmony_ci	#define SPEAR1340_PCIE_SATA_SEL_PCIE		(0)
4562306a36Sopenharmony_ci	#define SPEAR1340_PCIE_SATA_SEL_SATA		(1)
4662306a36Sopenharmony_ci	#define SPEAR1340_PCIE_SATA_CFG_MASK		0xF1F
4762306a36Sopenharmony_ci	#define SPEAR1340_PCIE_CFG_VAL	(SPEAR1340_PCIE_SATA_SEL_PCIE | \
4862306a36Sopenharmony_ci			SPEAR1340_PCIE_CFG_AUX_CLK_EN | \
4962306a36Sopenharmony_ci			SPEAR1340_PCIE_CFG_CORE_CLK_EN | \
5062306a36Sopenharmony_ci			SPEAR1340_PCIE_CFG_POWERUP_RESET | \
5162306a36Sopenharmony_ci			SPEAR1340_PCIE_CFG_DEVICE_PRESENT)
5262306a36Sopenharmony_ci	#define SPEAR1340_SATA_CFG_VAL	(SPEAR1340_PCIE_SATA_SEL_SATA | \
5362306a36Sopenharmony_ci			SPEAR1340_SATA_CFG_PM_CLK_EN | \
5462306a36Sopenharmony_ci			SPEAR1340_SATA_CFG_POWERUP_RESET | \
5562306a36Sopenharmony_ci			SPEAR1340_SATA_CFG_RX_CLK_EN | \
5662306a36Sopenharmony_ci			SPEAR1340_SATA_CFG_TX_CLK_EN)
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_ci#define SPEAR1340_PCIE_MIPHY_CFG		0x428
5962306a36Sopenharmony_ci	#define SPEAR1340_MIPHY_OSC_BYPASS_EXT		BIT(31)
6062306a36Sopenharmony_ci	#define SPEAR1340_MIPHY_CLK_REF_DIV2		BIT(27)
6162306a36Sopenharmony_ci	#define SPEAR1340_MIPHY_CLK_REF_DIV4		(2 << 27)
6262306a36Sopenharmony_ci	#define SPEAR1340_MIPHY_CLK_REF_DIV8		(3 << 27)
6362306a36Sopenharmony_ci	#define SPEAR1340_MIPHY_PLL_RATIO_TOP(x)	(x << 0)
6462306a36Sopenharmony_ci	#define SPEAR1340_PCIE_MIPHY_CFG_MASK		0xF80000FF
6562306a36Sopenharmony_ci	#define SPEAR1340_PCIE_SATA_MIPHY_CFG_SATA \
6662306a36Sopenharmony_ci			(SPEAR1340_MIPHY_OSC_BYPASS_EXT | \
6762306a36Sopenharmony_ci			SPEAR1340_MIPHY_CLK_REF_DIV2 | \
6862306a36Sopenharmony_ci			SPEAR1340_MIPHY_PLL_RATIO_TOP(60))
6962306a36Sopenharmony_ci	#define SPEAR1340_PCIE_SATA_MIPHY_CFG_SATA_25M_CRYSTAL_CLK \
7062306a36Sopenharmony_ci			(SPEAR1340_MIPHY_PLL_RATIO_TOP(120))
7162306a36Sopenharmony_ci	#define SPEAR1340_PCIE_SATA_MIPHY_CFG_PCIE \
7262306a36Sopenharmony_ci			(SPEAR1340_MIPHY_OSC_BYPASS_EXT | \
7362306a36Sopenharmony_ci			SPEAR1340_MIPHY_PLL_RATIO_TOP(25))
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_cienum spear1340_miphy_mode {
7662306a36Sopenharmony_ci	SATA,
7762306a36Sopenharmony_ci	PCIE,
7862306a36Sopenharmony_ci};
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_cistruct spear1340_miphy_priv {
8162306a36Sopenharmony_ci	/* phy mode: 0 for SATA 1 for PCIe */
8262306a36Sopenharmony_ci	enum spear1340_miphy_mode	mode;
8362306a36Sopenharmony_ci	/* regmap for any soc specific misc registers */
8462306a36Sopenharmony_ci	struct regmap			*misc;
8562306a36Sopenharmony_ci	/* phy struct pointer */
8662306a36Sopenharmony_ci	struct phy			*phy;
8762306a36Sopenharmony_ci};
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_cistatic int spear1340_miphy_sata_init(struct spear1340_miphy_priv *priv)
9062306a36Sopenharmony_ci{
9162306a36Sopenharmony_ci	regmap_update_bits(priv->misc, SPEAR1340_PCIE_SATA_CFG,
9262306a36Sopenharmony_ci			   SPEAR1340_PCIE_SATA_CFG_MASK,
9362306a36Sopenharmony_ci			   SPEAR1340_SATA_CFG_VAL);
9462306a36Sopenharmony_ci	regmap_update_bits(priv->misc, SPEAR1340_PCIE_MIPHY_CFG,
9562306a36Sopenharmony_ci			   SPEAR1340_PCIE_MIPHY_CFG_MASK,
9662306a36Sopenharmony_ci			   SPEAR1340_PCIE_SATA_MIPHY_CFG_SATA_25M_CRYSTAL_CLK);
9762306a36Sopenharmony_ci	/* Switch on sata power domain */
9862306a36Sopenharmony_ci	regmap_update_bits(priv->misc, SPEAR1340_PCM_CFG,
9962306a36Sopenharmony_ci			   SPEAR1340_PCM_CFG_SATA_POWER_EN,
10062306a36Sopenharmony_ci			   SPEAR1340_PCM_CFG_SATA_POWER_EN);
10162306a36Sopenharmony_ci	/* Wait for SATA power domain on */
10262306a36Sopenharmony_ci	msleep(20);
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_ci	/* Disable PCIE SATA Controller reset */
10562306a36Sopenharmony_ci	regmap_update_bits(priv->misc, SPEAR1340_PERIP1_SW_RST,
10662306a36Sopenharmony_ci			   SPEAR1340_PERIP1_SW_RSATA, 0);
10762306a36Sopenharmony_ci	/* Wait for SATA reset de-assert completion */
10862306a36Sopenharmony_ci	msleep(20);
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_ci	return 0;
11162306a36Sopenharmony_ci}
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_cistatic int spear1340_miphy_sata_exit(struct spear1340_miphy_priv *priv)
11462306a36Sopenharmony_ci{
11562306a36Sopenharmony_ci	regmap_update_bits(priv->misc, SPEAR1340_PCIE_SATA_CFG,
11662306a36Sopenharmony_ci			   SPEAR1340_PCIE_SATA_CFG_MASK, 0);
11762306a36Sopenharmony_ci	regmap_update_bits(priv->misc, SPEAR1340_PCIE_MIPHY_CFG,
11862306a36Sopenharmony_ci			   SPEAR1340_PCIE_MIPHY_CFG_MASK, 0);
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_ci	/* Enable PCIE SATA Controller reset */
12162306a36Sopenharmony_ci	regmap_update_bits(priv->misc, SPEAR1340_PERIP1_SW_RST,
12262306a36Sopenharmony_ci			   SPEAR1340_PERIP1_SW_RSATA,
12362306a36Sopenharmony_ci			   SPEAR1340_PERIP1_SW_RSATA);
12462306a36Sopenharmony_ci	/* Wait for SATA power domain off */
12562306a36Sopenharmony_ci	msleep(20);
12662306a36Sopenharmony_ci	/* Switch off sata power domain */
12762306a36Sopenharmony_ci	regmap_update_bits(priv->misc, SPEAR1340_PCM_CFG,
12862306a36Sopenharmony_ci			   SPEAR1340_PCM_CFG_SATA_POWER_EN, 0);
12962306a36Sopenharmony_ci	/* Wait for SATA reset assert completion */
13062306a36Sopenharmony_ci	msleep(20);
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_ci	return 0;
13362306a36Sopenharmony_ci}
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_cistatic int spear1340_miphy_pcie_init(struct spear1340_miphy_priv *priv)
13662306a36Sopenharmony_ci{
13762306a36Sopenharmony_ci	regmap_update_bits(priv->misc, SPEAR1340_PCIE_MIPHY_CFG,
13862306a36Sopenharmony_ci			   SPEAR1340_PCIE_MIPHY_CFG_MASK,
13962306a36Sopenharmony_ci			   SPEAR1340_PCIE_SATA_MIPHY_CFG_PCIE);
14062306a36Sopenharmony_ci	regmap_update_bits(priv->misc, SPEAR1340_PCIE_SATA_CFG,
14162306a36Sopenharmony_ci			   SPEAR1340_PCIE_SATA_CFG_MASK,
14262306a36Sopenharmony_ci			   SPEAR1340_PCIE_CFG_VAL);
14362306a36Sopenharmony_ci
14462306a36Sopenharmony_ci	return 0;
14562306a36Sopenharmony_ci}
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_cistatic int spear1340_miphy_pcie_exit(struct spear1340_miphy_priv *priv)
14862306a36Sopenharmony_ci{
14962306a36Sopenharmony_ci	regmap_update_bits(priv->misc, SPEAR1340_PCIE_MIPHY_CFG,
15062306a36Sopenharmony_ci			   SPEAR1340_PCIE_MIPHY_CFG_MASK, 0);
15162306a36Sopenharmony_ci	regmap_update_bits(priv->misc, SPEAR1340_PCIE_SATA_CFG,
15262306a36Sopenharmony_ci			   SPEAR1340_PCIE_SATA_CFG_MASK, 0);
15362306a36Sopenharmony_ci
15462306a36Sopenharmony_ci	return 0;
15562306a36Sopenharmony_ci}
15662306a36Sopenharmony_ci
15762306a36Sopenharmony_cistatic int spear1340_miphy_init(struct phy *phy)
15862306a36Sopenharmony_ci{
15962306a36Sopenharmony_ci	struct spear1340_miphy_priv *priv = phy_get_drvdata(phy);
16062306a36Sopenharmony_ci	int ret = 0;
16162306a36Sopenharmony_ci
16262306a36Sopenharmony_ci	if (priv->mode == SATA)
16362306a36Sopenharmony_ci		ret = spear1340_miphy_sata_init(priv);
16462306a36Sopenharmony_ci	else if (priv->mode == PCIE)
16562306a36Sopenharmony_ci		ret = spear1340_miphy_pcie_init(priv);
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_ci	return ret;
16862306a36Sopenharmony_ci}
16962306a36Sopenharmony_ci
17062306a36Sopenharmony_cistatic int spear1340_miphy_exit(struct phy *phy)
17162306a36Sopenharmony_ci{
17262306a36Sopenharmony_ci	struct spear1340_miphy_priv *priv = phy_get_drvdata(phy);
17362306a36Sopenharmony_ci	int ret = 0;
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_ci	if (priv->mode == SATA)
17662306a36Sopenharmony_ci		ret = spear1340_miphy_sata_exit(priv);
17762306a36Sopenharmony_ci	else if (priv->mode == PCIE)
17862306a36Sopenharmony_ci		ret = spear1340_miphy_pcie_exit(priv);
17962306a36Sopenharmony_ci
18062306a36Sopenharmony_ci	return ret;
18162306a36Sopenharmony_ci}
18262306a36Sopenharmony_ci
18362306a36Sopenharmony_cistatic const struct of_device_id spear1340_miphy_of_match[] = {
18462306a36Sopenharmony_ci	{ .compatible = "st,spear1340-miphy" },
18562306a36Sopenharmony_ci	{ },
18662306a36Sopenharmony_ci};
18762306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, spear1340_miphy_of_match);
18862306a36Sopenharmony_ci
18962306a36Sopenharmony_cistatic const struct phy_ops spear1340_miphy_ops = {
19062306a36Sopenharmony_ci	.init = spear1340_miphy_init,
19162306a36Sopenharmony_ci	.exit = spear1340_miphy_exit,
19262306a36Sopenharmony_ci	.owner = THIS_MODULE,
19362306a36Sopenharmony_ci};
19462306a36Sopenharmony_ci
19562306a36Sopenharmony_ci#ifdef CONFIG_PM_SLEEP
19662306a36Sopenharmony_cistatic int spear1340_miphy_suspend(struct device *dev)
19762306a36Sopenharmony_ci{
19862306a36Sopenharmony_ci	struct spear1340_miphy_priv *priv = dev_get_drvdata(dev);
19962306a36Sopenharmony_ci	int ret = 0;
20062306a36Sopenharmony_ci
20162306a36Sopenharmony_ci	if (priv->mode == SATA)
20262306a36Sopenharmony_ci		ret = spear1340_miphy_sata_exit(priv);
20362306a36Sopenharmony_ci
20462306a36Sopenharmony_ci	return ret;
20562306a36Sopenharmony_ci}
20662306a36Sopenharmony_ci
20762306a36Sopenharmony_cistatic int spear1340_miphy_resume(struct device *dev)
20862306a36Sopenharmony_ci{
20962306a36Sopenharmony_ci	struct spear1340_miphy_priv *priv = dev_get_drvdata(dev);
21062306a36Sopenharmony_ci	int ret = 0;
21162306a36Sopenharmony_ci
21262306a36Sopenharmony_ci	if (priv->mode == SATA)
21362306a36Sopenharmony_ci		ret = spear1340_miphy_sata_init(priv);
21462306a36Sopenharmony_ci
21562306a36Sopenharmony_ci	return ret;
21662306a36Sopenharmony_ci}
21762306a36Sopenharmony_ci#endif
21862306a36Sopenharmony_ci
21962306a36Sopenharmony_cistatic SIMPLE_DEV_PM_OPS(spear1340_miphy_pm_ops, spear1340_miphy_suspend,
22062306a36Sopenharmony_ci			 spear1340_miphy_resume);
22162306a36Sopenharmony_ci
22262306a36Sopenharmony_cistatic struct phy *spear1340_miphy_xlate(struct device *dev,
22362306a36Sopenharmony_ci					 struct of_phandle_args *args)
22462306a36Sopenharmony_ci{
22562306a36Sopenharmony_ci	struct spear1340_miphy_priv *priv = dev_get_drvdata(dev);
22662306a36Sopenharmony_ci
22762306a36Sopenharmony_ci	if (args->args_count < 1) {
22862306a36Sopenharmony_ci		dev_err(dev, "DT did not pass correct no of args\n");
22962306a36Sopenharmony_ci		return ERR_PTR(-ENODEV);
23062306a36Sopenharmony_ci	}
23162306a36Sopenharmony_ci
23262306a36Sopenharmony_ci	priv->mode = args->args[0];
23362306a36Sopenharmony_ci
23462306a36Sopenharmony_ci	if (priv->mode != SATA && priv->mode != PCIE) {
23562306a36Sopenharmony_ci		dev_err(dev, "DT did not pass correct phy mode\n");
23662306a36Sopenharmony_ci		return ERR_PTR(-ENODEV);
23762306a36Sopenharmony_ci	}
23862306a36Sopenharmony_ci
23962306a36Sopenharmony_ci	return priv->phy;
24062306a36Sopenharmony_ci}
24162306a36Sopenharmony_ci
24262306a36Sopenharmony_cistatic int spear1340_miphy_probe(struct platform_device *pdev)
24362306a36Sopenharmony_ci{
24462306a36Sopenharmony_ci	struct device *dev = &pdev->dev;
24562306a36Sopenharmony_ci	struct spear1340_miphy_priv *priv;
24662306a36Sopenharmony_ci	struct phy_provider *phy_provider;
24762306a36Sopenharmony_ci
24862306a36Sopenharmony_ci	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
24962306a36Sopenharmony_ci	if (!priv)
25062306a36Sopenharmony_ci		return -ENOMEM;
25162306a36Sopenharmony_ci
25262306a36Sopenharmony_ci	priv->misc =
25362306a36Sopenharmony_ci		syscon_regmap_lookup_by_phandle(dev->of_node, "misc");
25462306a36Sopenharmony_ci	if (IS_ERR(priv->misc)) {
25562306a36Sopenharmony_ci		dev_err(dev, "failed to find misc regmap\n");
25662306a36Sopenharmony_ci		return PTR_ERR(priv->misc);
25762306a36Sopenharmony_ci	}
25862306a36Sopenharmony_ci
25962306a36Sopenharmony_ci	priv->phy = devm_phy_create(dev, NULL, &spear1340_miphy_ops);
26062306a36Sopenharmony_ci	if (IS_ERR(priv->phy)) {
26162306a36Sopenharmony_ci		dev_err(dev, "failed to create SATA PCIe PHY\n");
26262306a36Sopenharmony_ci		return PTR_ERR(priv->phy);
26362306a36Sopenharmony_ci	}
26462306a36Sopenharmony_ci
26562306a36Sopenharmony_ci	dev_set_drvdata(dev, priv);
26662306a36Sopenharmony_ci	phy_set_drvdata(priv->phy, priv);
26762306a36Sopenharmony_ci
26862306a36Sopenharmony_ci	phy_provider =
26962306a36Sopenharmony_ci		devm_of_phy_provider_register(dev, spear1340_miphy_xlate);
27062306a36Sopenharmony_ci	if (IS_ERR(phy_provider)) {
27162306a36Sopenharmony_ci		dev_err(dev, "failed to register phy provider\n");
27262306a36Sopenharmony_ci		return PTR_ERR(phy_provider);
27362306a36Sopenharmony_ci	}
27462306a36Sopenharmony_ci
27562306a36Sopenharmony_ci	return 0;
27662306a36Sopenharmony_ci}
27762306a36Sopenharmony_ci
27862306a36Sopenharmony_cistatic struct platform_driver spear1340_miphy_driver = {
27962306a36Sopenharmony_ci	.probe		= spear1340_miphy_probe,
28062306a36Sopenharmony_ci	.driver = {
28162306a36Sopenharmony_ci		.name = "spear1340-miphy",
28262306a36Sopenharmony_ci		.pm = &spear1340_miphy_pm_ops,
28362306a36Sopenharmony_ci		.of_match_table = spear1340_miphy_of_match,
28462306a36Sopenharmony_ci	},
28562306a36Sopenharmony_ci};
28662306a36Sopenharmony_ci
28762306a36Sopenharmony_cimodule_platform_driver(spear1340_miphy_driver);
28862306a36Sopenharmony_ci
28962306a36Sopenharmony_ciMODULE_DESCRIPTION("ST SPEAR1340-MIPHY driver");
29062306a36Sopenharmony_ciMODULE_AUTHOR("Pratyush Anand <pratyush.anand@gmail.com>");
29162306a36Sopenharmony_ciMODULE_LICENSE("GPL v2");
292