]>
Commit | Line | Data |
---|---|---|
47269605 | 1 | /* |
16a27dfd | 2 | * MediaTek AHCI SATA driver |
47269605 RL |
3 | * |
4 | * Copyright (c) 2017 MediaTek Inc. | |
5 | * Author: Ryder Lee <[email protected]> | |
6 | * | |
7 | * This program is free software; you can redistribute it and/or modify | |
8 | * it under the terms of the GNU General Public License version 2 as | |
9 | * published by the Free Software Foundation. | |
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/ahci_platform.h> | |
18 | #include <linux/kernel.h> | |
19 | #include <linux/libata.h> | |
20 | #include <linux/mfd/syscon.h> | |
21 | #include <linux/module.h> | |
22 | #include <linux/platform_device.h> | |
23 | #include <linux/pm.h> | |
24 | #include <linux/regmap.h> | |
25 | #include <linux/reset.h> | |
26 | #include "ahci.h" | |
27 | ||
2467c045 | 28 | #define DRV_NAME "ahci-mtk" |
47269605 RL |
29 | |
30 | #define SYS_CFG 0x14 | |
31 | #define SYS_CFG_SATA_MSK GENMASK(31, 30) | |
32 | #define SYS_CFG_SATA_EN BIT(31) | |
33 | ||
34 | struct mtk_ahci_plat { | |
35 | struct regmap *mode; | |
36 | struct reset_control *axi_rst; | |
37 | struct reset_control *sw_rst; | |
38 | struct reset_control *reg_rst; | |
39 | }; | |
40 | ||
41 | static const struct ata_port_info ahci_port_info = { | |
42 | .flags = AHCI_FLAG_COMMON, | |
43 | .pio_mask = ATA_PIO4, | |
44 | .udma_mask = ATA_UDMA6, | |
45 | .port_ops = &ahci_platform_ops, | |
46 | }; | |
47 | ||
48 | static struct scsi_host_template ahci_platform_sht = { | |
49 | AHCI_SHT(DRV_NAME), | |
50 | }; | |
51 | ||
52 | static int mtk_ahci_platform_resets(struct ahci_host_priv *hpriv, | |
53 | struct device *dev) | |
54 | { | |
55 | struct mtk_ahci_plat *plat = hpriv->plat_data; | |
56 | int err; | |
57 | ||
58 | /* reset AXI bus and PHY part */ | |
59 | plat->axi_rst = devm_reset_control_get_optional_exclusive(dev, "axi"); | |
60 | if (PTR_ERR(plat->axi_rst) == -EPROBE_DEFER) | |
61 | return PTR_ERR(plat->axi_rst); | |
62 | ||
63 | plat->sw_rst = devm_reset_control_get_optional_exclusive(dev, "sw"); | |
64 | if (PTR_ERR(plat->sw_rst) == -EPROBE_DEFER) | |
65 | return PTR_ERR(plat->sw_rst); | |
66 | ||
67 | plat->reg_rst = devm_reset_control_get_optional_exclusive(dev, "reg"); | |
68 | if (PTR_ERR(plat->reg_rst) == -EPROBE_DEFER) | |
69 | return PTR_ERR(plat->reg_rst); | |
70 | ||
71 | err = reset_control_assert(plat->axi_rst); | |
72 | if (err) { | |
73 | dev_err(dev, "failed to assert AXI bus\n"); | |
74 | return err; | |
75 | } | |
76 | ||
77 | err = reset_control_assert(plat->sw_rst); | |
78 | if (err) { | |
79 | dev_err(dev, "failed to assert PHY digital part\n"); | |
80 | return err; | |
81 | } | |
82 | ||
83 | err = reset_control_assert(plat->reg_rst); | |
84 | if (err) { | |
85 | dev_err(dev, "failed to assert PHY register part\n"); | |
86 | return err; | |
87 | } | |
88 | ||
89 | err = reset_control_deassert(plat->reg_rst); | |
90 | if (err) { | |
91 | dev_err(dev, "failed to deassert PHY register part\n"); | |
92 | return err; | |
93 | } | |
94 | ||
95 | err = reset_control_deassert(plat->sw_rst); | |
96 | if (err) { | |
97 | dev_err(dev, "failed to deassert PHY digital part\n"); | |
98 | return err; | |
99 | } | |
100 | ||
101 | err = reset_control_deassert(plat->axi_rst); | |
102 | if (err) { | |
103 | dev_err(dev, "failed to deassert AXI bus\n"); | |
104 | return err; | |
105 | } | |
106 | ||
107 | return 0; | |
108 | } | |
109 | ||
110 | static int mtk_ahci_parse_property(struct ahci_host_priv *hpriv, | |
111 | struct device *dev) | |
112 | { | |
113 | struct mtk_ahci_plat *plat = hpriv->plat_data; | |
114 | struct device_node *np = dev->of_node; | |
115 | ||
116 | /* enable SATA function if needed */ | |
117 | if (of_find_property(np, "mediatek,phy-mode", NULL)) { | |
118 | plat->mode = syscon_regmap_lookup_by_phandle( | |
119 | np, "mediatek,phy-mode"); | |
120 | if (IS_ERR(plat->mode)) { | |
121 | dev_err(dev, "missing phy-mode phandle\n"); | |
122 | return PTR_ERR(plat->mode); | |
123 | } | |
124 | ||
125 | regmap_update_bits(plat->mode, SYS_CFG, SYS_CFG_SATA_MSK, | |
126 | SYS_CFG_SATA_EN); | |
127 | } | |
128 | ||
129 | of_property_read_u32(np, "ports-implemented", &hpriv->force_port_map); | |
130 | ||
131 | return 0; | |
132 | } | |
133 | ||
134 | static int mtk_ahci_probe(struct platform_device *pdev) | |
135 | { | |
136 | struct device *dev = &pdev->dev; | |
137 | struct mtk_ahci_plat *plat; | |
138 | struct ahci_host_priv *hpriv; | |
139 | int err; | |
140 | ||
141 | plat = devm_kzalloc(dev, sizeof(*plat), GFP_KERNEL); | |
142 | if (!plat) | |
143 | return -ENOMEM; | |
144 | ||
145 | hpriv = ahci_platform_get_resources(pdev); | |
146 | if (IS_ERR(hpriv)) | |
147 | return PTR_ERR(hpriv); | |
148 | ||
149 | hpriv->plat_data = plat; | |
150 | ||
151 | err = mtk_ahci_parse_property(hpriv, dev); | |
152 | if (err) | |
153 | return err; | |
154 | ||
155 | err = mtk_ahci_platform_resets(hpriv, dev); | |
156 | if (err) | |
157 | return err; | |
158 | ||
159 | err = ahci_platform_enable_resources(hpriv); | |
160 | if (err) | |
161 | return err; | |
162 | ||
163 | err = ahci_platform_init_host(pdev, hpriv, &ahci_port_info, | |
164 | &ahci_platform_sht); | |
165 | if (err) | |
166 | goto disable_resources; | |
167 | ||
168 | return 0; | |
169 | ||
170 | disable_resources: | |
171 | ahci_platform_disable_resources(hpriv); | |
172 | return err; | |
173 | } | |
174 | ||
175 | static SIMPLE_DEV_PM_OPS(ahci_pm_ops, ahci_platform_suspend, | |
176 | ahci_platform_resume); | |
177 | ||
178 | static const struct of_device_id ahci_of_match[] = { | |
179 | { .compatible = "mediatek,mtk-ahci", }, | |
180 | {}, | |
181 | }; | |
182 | MODULE_DEVICE_TABLE(of, ahci_of_match); | |
183 | ||
184 | static struct platform_driver mtk_ahci_driver = { | |
185 | .probe = mtk_ahci_probe, | |
186 | .remove = ata_platform_remove_one, | |
187 | .driver = { | |
188 | .name = DRV_NAME, | |
189 | .of_match_table = ahci_of_match, | |
190 | .pm = &ahci_pm_ops, | |
191 | }, | |
192 | }; | |
193 | module_platform_driver(mtk_ahci_driver); | |
194 | ||
16a27dfd | 195 | MODULE_DESCRIPTION("MediaTek SATA AHCI Driver"); |
47269605 | 196 | MODULE_LICENSE("GPL v2"); |