1/* 2 * ST SPEAr1310-miphy driver 3 * 4 * Copyright (C) 2014 ST Microelectronics 5 * Pratyush Anand <pratyush.anand@st.com> 6 * Mohit Kumar <mohit.kumar@st.com> 7 * 8 * This program is free software; you can redistribute it and/or modify 9 * it under the terms of the GNU General Public License version 2 as 10 * published by the Free Software Foundation. 11 * 12 */ 13 14#include <linux/bitops.h> 15#include <linux/delay.h> 16#include <linux/dma-mapping.h> 17#include <linux/kernel.h> 18#include <linux/mfd/syscon.h> 19#include <linux/module.h> 20#include <linux/of_device.h> 21#include <linux/phy/phy.h> 22#include <linux/regmap.h> 23 24/* SPEAr1310 Registers */ 25#define SPEAR1310_PCIE_SATA_CFG 0x3A4 26 #define SPEAR1310_PCIE_SATA2_SEL_PCIE (0 << 31) 27 #define SPEAR1310_PCIE_SATA1_SEL_PCIE (0 << 30) 28 #define SPEAR1310_PCIE_SATA0_SEL_PCIE (0 << 29) 29 #define SPEAR1310_PCIE_SATA2_SEL_SATA BIT(31) 30 #define SPEAR1310_PCIE_SATA1_SEL_SATA BIT(30) 31 #define SPEAR1310_PCIE_SATA0_SEL_SATA BIT(29) 32 #define SPEAR1310_SATA2_CFG_TX_CLK_EN BIT(27) 33 #define SPEAR1310_SATA2_CFG_RX_CLK_EN BIT(26) 34 #define SPEAR1310_SATA2_CFG_POWERUP_RESET BIT(25) 35 #define SPEAR1310_SATA2_CFG_PM_CLK_EN BIT(24) 36 #define SPEAR1310_SATA1_CFG_TX_CLK_EN BIT(23) 37 #define SPEAR1310_SATA1_CFG_RX_CLK_EN BIT(22) 38 #define SPEAR1310_SATA1_CFG_POWERUP_RESET BIT(21) 39 #define SPEAR1310_SATA1_CFG_PM_CLK_EN BIT(20) 40 #define SPEAR1310_SATA0_CFG_TX_CLK_EN BIT(19) 41 #define SPEAR1310_SATA0_CFG_RX_CLK_EN BIT(18) 42 #define SPEAR1310_SATA0_CFG_POWERUP_RESET BIT(17) 43 #define SPEAR1310_SATA0_CFG_PM_CLK_EN BIT(16) 44 #define SPEAR1310_PCIE2_CFG_DEVICE_PRESENT BIT(11) 45 #define SPEAR1310_PCIE2_CFG_POWERUP_RESET BIT(10) 46 #define SPEAR1310_PCIE2_CFG_CORE_CLK_EN BIT(9) 47 #define SPEAR1310_PCIE2_CFG_AUX_CLK_EN BIT(8) 48 #define SPEAR1310_PCIE1_CFG_DEVICE_PRESENT BIT(7) 49 #define SPEAR1310_PCIE1_CFG_POWERUP_RESET BIT(6) 50 #define SPEAR1310_PCIE1_CFG_CORE_CLK_EN BIT(5) 51 #define SPEAR1310_PCIE1_CFG_AUX_CLK_EN BIT(4) 52 #define SPEAR1310_PCIE0_CFG_DEVICE_PRESENT BIT(3) 53 #define SPEAR1310_PCIE0_CFG_POWERUP_RESET BIT(2) 54 #define SPEAR1310_PCIE0_CFG_CORE_CLK_EN BIT(1) 55 #define SPEAR1310_PCIE0_CFG_AUX_CLK_EN BIT(0) 56 57 #define SPEAR1310_PCIE_CFG_MASK(x) ((0xF << (x * 4)) | BIT((x + 29))) 58 #define SPEAR1310_SATA_CFG_MASK(x) ((0xF << (x * 4 + 16)) | \ 59 BIT((x + 29))) 60 #define SPEAR1310_PCIE_CFG_VAL(x) \ 61 (SPEAR1310_PCIE_SATA##x##_SEL_PCIE | \ 62 SPEAR1310_PCIE##x##_CFG_AUX_CLK_EN | \ 63 SPEAR1310_PCIE##x##_CFG_CORE_CLK_EN | \ 64 SPEAR1310_PCIE##x##_CFG_POWERUP_RESET | \ 65 SPEAR1310_PCIE##x##_CFG_DEVICE_PRESENT) 66 #define SPEAR1310_SATA_CFG_VAL(x) \ 67 (SPEAR1310_PCIE_SATA##x##_SEL_SATA | \ 68 SPEAR1310_SATA##x##_CFG_PM_CLK_EN | \ 69 SPEAR1310_SATA##x##_CFG_POWERUP_RESET | \ 70 SPEAR1310_SATA##x##_CFG_RX_CLK_EN | \ 71 SPEAR1310_SATA##x##_CFG_TX_CLK_EN) 72 73#define SPEAR1310_PCIE_MIPHY_CFG_1 0x3A8 74 #define SPEAR1310_MIPHY_DUAL_OSC_BYPASS_EXT BIT(31) 75 #define SPEAR1310_MIPHY_DUAL_CLK_REF_DIV2 BIT(28) 76 #define SPEAR1310_MIPHY_DUAL_PLL_RATIO_TOP(x) (x << 16) 77 #define SPEAR1310_MIPHY_SINGLE_OSC_BYPASS_EXT BIT(15) 78 #define SPEAR1310_MIPHY_SINGLE_CLK_REF_DIV2 BIT(12) 79 #define SPEAR1310_MIPHY_SINGLE_PLL_RATIO_TOP(x) (x << 0) 80 #define SPEAR1310_PCIE_SATA_MIPHY_CFG_SATA_MASK (0xFFFF) 81 #define SPEAR1310_PCIE_SATA_MIPHY_CFG_PCIE_MASK (0xFFFF << 16) 82 #define SPEAR1310_PCIE_SATA_MIPHY_CFG_SATA \ 83 (SPEAR1310_MIPHY_DUAL_OSC_BYPASS_EXT | \ 84 SPEAR1310_MIPHY_DUAL_CLK_REF_DIV2 | \ 85 SPEAR1310_MIPHY_DUAL_PLL_RATIO_TOP(60) | \ 86 SPEAR1310_MIPHY_SINGLE_OSC_BYPASS_EXT | \ 87 SPEAR1310_MIPHY_SINGLE_CLK_REF_DIV2 | \ 88 SPEAR1310_MIPHY_SINGLE_PLL_RATIO_TOP(60)) 89 #define SPEAR1310_PCIE_SATA_MIPHY_CFG_SATA_25M_CRYSTAL_CLK \ 90 (SPEAR1310_MIPHY_SINGLE_PLL_RATIO_TOP(120)) 91 #define SPEAR1310_PCIE_SATA_MIPHY_CFG_PCIE \ 92 (SPEAR1310_MIPHY_DUAL_OSC_BYPASS_EXT | \ 93 SPEAR1310_MIPHY_DUAL_PLL_RATIO_TOP(25) | \ 94 SPEAR1310_MIPHY_SINGLE_OSC_BYPASS_EXT | \ 95 SPEAR1310_MIPHY_SINGLE_PLL_RATIO_TOP(25)) 96 97#define SPEAR1310_PCIE_MIPHY_CFG_2 0x3AC 98 99enum spear1310_miphy_mode { 100 SATA, 101 PCIE, 102}; 103 104struct spear1310_miphy_priv { 105 /* instance id of this phy */ 106 u32 id; 107 /* phy mode: 0 for SATA 1 for PCIe */ 108 enum spear1310_miphy_mode mode; 109 /* regmap for any soc specific misc registers */ 110 struct regmap *misc; 111 /* phy struct pointer */ 112 struct phy *phy; 113}; 114 115static int spear1310_miphy_pcie_init(struct spear1310_miphy_priv *priv) 116{ 117 u32 val; 118 119 regmap_update_bits(priv->misc, SPEAR1310_PCIE_MIPHY_CFG_1, 120 SPEAR1310_PCIE_SATA_MIPHY_CFG_PCIE_MASK, 121 SPEAR1310_PCIE_SATA_MIPHY_CFG_PCIE); 122 123 switch (priv->id) { 124 case 0: 125 val = SPEAR1310_PCIE_CFG_VAL(0); 126 break; 127 case 1: 128 val = SPEAR1310_PCIE_CFG_VAL(1); 129 break; 130 case 2: 131 val = SPEAR1310_PCIE_CFG_VAL(2); 132 break; 133 default: 134 return -EINVAL; 135 } 136 137 regmap_update_bits(priv->misc, SPEAR1310_PCIE_SATA_CFG, 138 SPEAR1310_PCIE_CFG_MASK(priv->id), val); 139 140 return 0; 141} 142 143static int spear1310_miphy_pcie_exit(struct spear1310_miphy_priv *priv) 144{ 145 regmap_update_bits(priv->misc, SPEAR1310_PCIE_SATA_CFG, 146 SPEAR1310_PCIE_CFG_MASK(priv->id), 0); 147 148 regmap_update_bits(priv->misc, SPEAR1310_PCIE_MIPHY_CFG_1, 149 SPEAR1310_PCIE_SATA_MIPHY_CFG_PCIE_MASK, 0); 150 151 return 0; 152} 153 154static int spear1310_miphy_init(struct phy *phy) 155{ 156 struct spear1310_miphy_priv *priv = phy_get_drvdata(phy); 157 int ret = 0; 158 159 if (priv->mode == PCIE) 160 ret = spear1310_miphy_pcie_init(priv); 161 162 return ret; 163} 164 165static int spear1310_miphy_exit(struct phy *phy) 166{ 167 struct spear1310_miphy_priv *priv = phy_get_drvdata(phy); 168 int ret = 0; 169 170 if (priv->mode == PCIE) 171 ret = spear1310_miphy_pcie_exit(priv); 172 173 return ret; 174} 175 176static const struct of_device_id spear1310_miphy_of_match[] = { 177 { .compatible = "st,spear1310-miphy" }, 178 { }, 179}; 180MODULE_DEVICE_TABLE(of, spear1310_miphy_of_match); 181 182static struct phy_ops spear1310_miphy_ops = { 183 .init = spear1310_miphy_init, 184 .exit = spear1310_miphy_exit, 185 .owner = THIS_MODULE, 186}; 187 188static struct phy *spear1310_miphy_xlate(struct device *dev, 189 struct of_phandle_args *args) 190{ 191 struct spear1310_miphy_priv *priv = dev_get_drvdata(dev); 192 193 if (args->args_count < 1) { 194 dev_err(dev, "DT did not pass correct no of args\n"); 195 return ERR_PTR(-ENODEV); 196 } 197 198 priv->mode = args->args[0]; 199 200 if (priv->mode != SATA && priv->mode != PCIE) { 201 dev_err(dev, "DT did not pass correct phy mode\n"); 202 return ERR_PTR(-ENODEV); 203 } 204 205 return priv->phy; 206} 207 208static int spear1310_miphy_probe(struct platform_device *pdev) 209{ 210 struct device *dev = &pdev->dev; 211 struct spear1310_miphy_priv *priv; 212 struct phy_provider *phy_provider; 213 214 priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); 215 if (!priv) 216 return -ENOMEM; 217 218 priv->misc = 219 syscon_regmap_lookup_by_phandle(dev->of_node, "misc"); 220 if (IS_ERR(priv->misc)) { 221 dev_err(dev, "failed to find misc regmap\n"); 222 return PTR_ERR(priv->misc); 223 } 224 225 if (of_property_read_u32(dev->of_node, "phy-id", &priv->id)) { 226 dev_err(dev, "failed to find phy id\n"); 227 return -EINVAL; 228 } 229 230 priv->phy = devm_phy_create(dev, NULL, &spear1310_miphy_ops); 231 if (IS_ERR(priv->phy)) { 232 dev_err(dev, "failed to create SATA PCIe PHY\n"); 233 return PTR_ERR(priv->phy); 234 } 235 236 dev_set_drvdata(dev, priv); 237 phy_set_drvdata(priv->phy, priv); 238 239 phy_provider = 240 devm_of_phy_provider_register(dev, spear1310_miphy_xlate); 241 if (IS_ERR(phy_provider)) { 242 dev_err(dev, "failed to register phy provider\n"); 243 return PTR_ERR(phy_provider); 244 } 245 246 return 0; 247} 248 249static struct platform_driver spear1310_miphy_driver = { 250 .probe = spear1310_miphy_probe, 251 .driver = { 252 .name = "spear1310-miphy", 253 .of_match_table = of_match_ptr(spear1310_miphy_of_match), 254 }, 255}; 256 257module_platform_driver(spear1310_miphy_driver); 258 259MODULE_DESCRIPTION("ST SPEAR1310-MIPHY driver"); 260MODULE_AUTHOR("Pratyush Anand <pratyush.anand@st.com>"); 261MODULE_LICENSE("GPL v2"); 262