diff options
author | Brian Norris <computersforpeace@gmail.com> | 2015-05-20 20:18:40 -0400 |
---|---|---|
committer | Kishon Vijay Abraham I <kishon@ti.com> | 2015-05-22 07:23:09 -0400 |
commit | 0d486806ffd0de672f151763b38c19c0a357bf56 (patch) | |
tree | 513780ec82f108df250a435b4be66e1fbe296c75 /drivers/phy | |
parent | 5972edbd8386020347656a37dc014ae838a0adff (diff) |
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>
Diffstat (limited to 'drivers/phy')
-rw-r--r-- | drivers/phy/Kconfig | 9 | ||||
-rw-r--r-- | drivers/phy/Makefile | 1 | ||||
-rw-r--r-- | drivers/phy/phy-brcmstb-sata.c | 216 |
3 files changed, 226 insertions, 0 deletions
diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig index a53bd5b52df9..36788b6f0220 100644 --- a/drivers/phy/Kconfig +++ b/drivers/phy/Kconfig | |||
@@ -309,4 +309,13 @@ config PHY_QCOM_UFS | |||
309 | help | 309 | help |
310 | Support for UFS PHY on QCOM chipsets. | 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 | endmenu | 321 | endmenu |
diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile index f12625178780..c61f3fdd191e 100644 --- a/drivers/phy/Makefile +++ b/drivers/phy/Makefile | |||
@@ -40,3 +40,4 @@ obj-$(CONFIG_PHY_STIH41X_USB) += phy-stih41x-usb.o | |||
40 | obj-$(CONFIG_PHY_QCOM_UFS) += phy-qcom-ufs.o | 40 | obj-$(CONFIG_PHY_QCOM_UFS) += phy-qcom-ufs.o |
41 | obj-$(CONFIG_PHY_QCOM_UFS) += phy-qcom-ufs-qmp-20nm.o | 41 | obj-$(CONFIG_PHY_QCOM_UFS) += phy-qcom-ufs-qmp-20nm.o |
42 | obj-$(CONFIG_PHY_QCOM_UFS) += phy-qcom-ufs-qmp-14nm.o | 42 | obj-$(CONFIG_PHY_QCOM_UFS) += phy-qcom-ufs-qmp-14nm.o |
43 | obj-$(CONFIG_PHY_BRCMSTB_SATA) += phy-brcmstb-sata.o | ||
diff --git a/drivers/phy/phy-brcmstb-sata.c b/drivers/phy/phy-brcmstb-sata.c new file mode 100644 index 000000000000..b7e303d28caf --- /dev/null +++ b/drivers/phy/phy-brcmstb-sata.c | |||
@@ -0,0 +1,216 @@ | |||
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"); | ||