Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

phy: add Broadcom SATA3 PHY driver for Broadcom STB SoCs

Supports up to two ports which can each be powered on/off and configured
independently.

Signed-off-by: Brian Norris <computersforpeace@gmail.com>
Signed-off-by: Kishon Vijay Abraham I <kishon@ti.com>

authored by

Brian Norris and committed by
Kishon Vijay Abraham I
0d486806 5972edbd

+226
+9
drivers/phy/Kconfig
··· 309 309 help 310 310 Support for UFS PHY on QCOM chipsets. 311 311 312 + config PHY_BRCMSTB_SATA 313 + tristate "Broadcom STB SATA PHY driver" 314 + depends on ARCH_BRCMSTB 315 + depends on OF 316 + select GENERIC_PHY 317 + help 318 + Enable this to support the SATA3 PHY on 28nm Broadcom STB SoCs. 319 + Likely useful only with CONFIG_SATA_BRCMSTB enabled. 320 + 312 321 endmenu
+1
drivers/phy/Makefile
··· 40 40 obj-$(CONFIG_PHY_QCOM_UFS) += phy-qcom-ufs.o 41 41 obj-$(CONFIG_PHY_QCOM_UFS) += phy-qcom-ufs-qmp-20nm.o 42 42 obj-$(CONFIG_PHY_QCOM_UFS) += phy-qcom-ufs-qmp-14nm.o 43 + obj-$(CONFIG_PHY_BRCMSTB_SATA) += phy-brcmstb-sata.o
+216
drivers/phy/phy-brcmstb-sata.c
··· 1 + /* 2 + * Broadcom SATA3 AHCI Controller PHY Driver 3 + * 4 + * Copyright © 2009-2015 Broadcom Corporation 5 + * 6 + * This program is free software; you can redistribute it and/or modify 7 + * it under the terms of the GNU General Public License as published by 8 + * the Free Software Foundation; either version 2, or (at your option) 9 + * any later version. 10 + * 11 + * This program is distributed in the hope that it will be useful, 12 + * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 + * GNU General Public License for more details. 15 + */ 16 + 17 + #include <linux/device.h> 18 + #include <linux/init.h> 19 + #include <linux/interrupt.h> 20 + #include <linux/io.h> 21 + #include <linux/kernel.h> 22 + #include <linux/module.h> 23 + #include <linux/of.h> 24 + #include <linux/phy/phy.h> 25 + #include <linux/platform_device.h> 26 + 27 + #define SATA_MDIO_BANK_OFFSET 0x23c 28 + #define SATA_MDIO_REG_OFFSET(ofs) ((ofs) * 4) 29 + #define SATA_MDIO_REG_SPACE_SIZE 0x1000 30 + #define SATA_MDIO_REG_LENGTH 0x1f00 31 + 32 + #define MAX_PORTS 2 33 + 34 + /* Register offset between PHYs in PCB space */ 35 + #define SATA_MDIO_REG_SPACE_SIZE 0x1000 36 + 37 + struct brcm_sata_port { 38 + int portnum; 39 + struct phy *phy; 40 + struct brcm_sata_phy *phy_priv; 41 + bool ssc_en; 42 + }; 43 + 44 + struct brcm_sata_phy { 45 + struct device *dev; 46 + void __iomem *phy_base; 47 + 48 + struct brcm_sata_port phys[MAX_PORTS]; 49 + }; 50 + 51 + enum sata_mdio_phy_regs_28nm { 52 + PLL_REG_BANK_0 = 0x50, 53 + PLL_REG_BANK_0_PLLCONTROL_0 = 0x81, 54 + 55 + TXPMD_REG_BANK = 0x1a0, 56 + TXPMD_CONTROL1 = 0x81, 57 + TXPMD_CONTROL1_TX_SSC_EN_FRC = BIT(0), 58 + TXPMD_CONTROL1_TX_SSC_EN_FRC_VAL = BIT(1), 59 + TXPMD_TX_FREQ_CTRL_CONTROL1 = 0x82, 60 + TXPMD_TX_FREQ_CTRL_CONTROL2 = 0x83, 61 + TXPMD_TX_FREQ_CTRL_CONTROL2_FMIN_MASK = 0x3ff, 62 + TXPMD_TX_FREQ_CTRL_CONTROL3 = 0x84, 63 + TXPMD_TX_FREQ_CTRL_CONTROL3_FMAX_MASK = 0x3ff, 64 + }; 65 + 66 + static inline void __iomem *brcm_sata_phy_base(struct brcm_sata_port *port) 67 + { 68 + struct brcm_sata_phy *priv = port->phy_priv; 69 + 70 + return priv->phy_base + (port->portnum * SATA_MDIO_REG_SPACE_SIZE); 71 + } 72 + 73 + static void brcm_sata_mdio_wr(void __iomem *addr, u32 bank, u32 ofs, 74 + u32 msk, u32 value) 75 + { 76 + u32 tmp; 77 + 78 + writel(bank, addr + SATA_MDIO_BANK_OFFSET); 79 + tmp = readl(addr + SATA_MDIO_REG_OFFSET(ofs)); 80 + tmp = (tmp & msk) | value; 81 + writel(tmp, addr + SATA_MDIO_REG_OFFSET(ofs)); 82 + } 83 + 84 + /* These defaults were characterized by H/W group */ 85 + #define FMIN_VAL_DEFAULT 0x3df 86 + #define FMAX_VAL_DEFAULT 0x3df 87 + #define FMAX_VAL_SSC 0x83 88 + 89 + static void brcm_sata_cfg_ssc_28nm(struct brcm_sata_port *port) 90 + { 91 + void __iomem *base = brcm_sata_phy_base(port); 92 + struct brcm_sata_phy *priv = port->phy_priv; 93 + u32 tmp; 94 + 95 + /* override the TX spread spectrum setting */ 96 + tmp = TXPMD_CONTROL1_TX_SSC_EN_FRC_VAL | TXPMD_CONTROL1_TX_SSC_EN_FRC; 97 + brcm_sata_mdio_wr(base, TXPMD_REG_BANK, TXPMD_CONTROL1, ~tmp, tmp); 98 + 99 + /* set fixed min freq */ 100 + brcm_sata_mdio_wr(base, TXPMD_REG_BANK, TXPMD_TX_FREQ_CTRL_CONTROL2, 101 + ~TXPMD_TX_FREQ_CTRL_CONTROL2_FMIN_MASK, 102 + FMIN_VAL_DEFAULT); 103 + 104 + /* set fixed max freq depending on SSC config */ 105 + if (port->ssc_en) { 106 + dev_info(priv->dev, "enabling SSC on port %d\n", port->portnum); 107 + tmp = FMAX_VAL_SSC; 108 + } else { 109 + tmp = FMAX_VAL_DEFAULT; 110 + } 111 + 112 + brcm_sata_mdio_wr(base, TXPMD_REG_BANK, TXPMD_TX_FREQ_CTRL_CONTROL3, 113 + ~TXPMD_TX_FREQ_CTRL_CONTROL3_FMAX_MASK, tmp); 114 + } 115 + 116 + static int brcm_sata_phy_init(struct phy *phy) 117 + { 118 + struct brcm_sata_port *port = phy_get_drvdata(phy); 119 + 120 + brcm_sata_cfg_ssc_28nm(port); 121 + 122 + return 0; 123 + } 124 + 125 + static struct phy_ops phy_ops_28nm = { 126 + .init = brcm_sata_phy_init, 127 + .owner = THIS_MODULE, 128 + }; 129 + 130 + static const struct of_device_id brcm_sata_phy_of_match[] = { 131 + { .compatible = "brcm,bcm7445-sata-phy" }, 132 + {}, 133 + }; 134 + MODULE_DEVICE_TABLE(of, brcm_sata_phy_of_match); 135 + 136 + static int brcm_sata_phy_probe(struct platform_device *pdev) 137 + { 138 + struct device *dev = &pdev->dev; 139 + struct device_node *dn = dev->of_node, *child; 140 + struct brcm_sata_phy *priv; 141 + struct resource *res; 142 + struct phy_provider *provider; 143 + int count = 0; 144 + 145 + if (of_get_child_count(dn) == 0) 146 + return -ENODEV; 147 + 148 + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); 149 + if (!priv) 150 + return -ENOMEM; 151 + dev_set_drvdata(dev, priv); 152 + priv->dev = dev; 153 + 154 + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "phy"); 155 + priv->phy_base = devm_ioremap_resource(dev, res); 156 + if (IS_ERR(priv->phy_base)) 157 + return PTR_ERR(priv->phy_base); 158 + 159 + for_each_available_child_of_node(dn, child) { 160 + unsigned int id; 161 + struct brcm_sata_port *port; 162 + 163 + if (of_property_read_u32(child, "reg", &id)) { 164 + dev_err(dev, "missing reg property in node %s\n", 165 + child->name); 166 + return -EINVAL; 167 + } 168 + 169 + if (id >= MAX_PORTS) { 170 + dev_err(dev, "invalid reg: %u\n", id); 171 + return -EINVAL; 172 + } 173 + if (priv->phys[id].phy) { 174 + dev_err(dev, "already registered port %u\n", id); 175 + return -EINVAL; 176 + } 177 + 178 + port = &priv->phys[id]; 179 + port->portnum = id; 180 + port->phy_priv = priv; 181 + port->phy = devm_phy_create(dev, child, &phy_ops_28nm); 182 + port->ssc_en = of_property_read_bool(child, "brcm,enable-ssc"); 183 + if (IS_ERR(port->phy)) { 184 + dev_err(dev, "failed to create PHY\n"); 185 + return PTR_ERR(port->phy); 186 + } 187 + 188 + phy_set_drvdata(port->phy, port); 189 + count++; 190 + } 191 + 192 + provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); 193 + if (IS_ERR(provider)) { 194 + dev_err(dev, "could not register PHY provider\n"); 195 + return PTR_ERR(provider); 196 + } 197 + 198 + dev_info(dev, "registered %d port(s)\n", count); 199 + 200 + return 0; 201 + } 202 + 203 + static struct platform_driver brcm_sata_phy_driver = { 204 + .probe = brcm_sata_phy_probe, 205 + .driver = { 206 + .of_match_table = brcm_sata_phy_of_match, 207 + .name = "brcmstb-sata-phy", 208 + } 209 + }; 210 + module_platform_driver(brcm_sata_phy_driver); 211 + 212 + MODULE_DESCRIPTION("Broadcom STB SATA PHY driver"); 213 + MODULE_LICENSE("GPL"); 214 + MODULE_AUTHOR("Marc Carino"); 215 + MODULE_AUTHOR("Brian Norris"); 216 + MODULE_ALIAS("platform:phy-brcmstb-sata");