2 * MediaTek AHCI SATA driver
4 * Copyright (c) 2017 MediaTek Inc.
5 * Author: Ryder Lee <ryder.lee@mediatek.com>
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.
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.
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>
24 #include <linux/regmap.h>
25 #include <linux/reset.h>
28 #define DRV_NAME "ahci-mtk"
31 #define SYS_CFG_SATA_MSK GENMASK(31, 30)
32 #define SYS_CFG_SATA_EN BIT(31)
34 struct mtk_ahci_plat
{
36 struct reset_control
*axi_rst
;
37 struct reset_control
*sw_rst
;
38 struct reset_control
*reg_rst
;
41 static const struct ata_port_info ahci_port_info
= {
42 .flags
= AHCI_FLAG_COMMON
,
44 .udma_mask
= ATA_UDMA6
,
45 .port_ops
= &ahci_platform_ops
,
48 static struct scsi_host_template ahci_platform_sht
= {
52 static int mtk_ahci_platform_resets(struct ahci_host_priv
*hpriv
,
55 struct mtk_ahci_plat
*plat
= hpriv
->plat_data
;
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
);
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
);
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
);
71 err
= reset_control_assert(plat
->axi_rst
);
73 dev_err(dev
, "failed to assert AXI bus\n");
77 err
= reset_control_assert(plat
->sw_rst
);
79 dev_err(dev
, "failed to assert PHY digital part\n");
83 err
= reset_control_assert(plat
->reg_rst
);
85 dev_err(dev
, "failed to assert PHY register part\n");
89 err
= reset_control_deassert(plat
->reg_rst
);
91 dev_err(dev
, "failed to deassert PHY register part\n");
95 err
= reset_control_deassert(plat
->sw_rst
);
97 dev_err(dev
, "failed to deassert PHY digital part\n");
101 err
= reset_control_deassert(plat
->axi_rst
);
103 dev_err(dev
, "failed to deassert AXI bus\n");
110 static int mtk_ahci_parse_property(struct ahci_host_priv
*hpriv
,
113 struct mtk_ahci_plat
*plat
= hpriv
->plat_data
;
114 struct device_node
*np
= dev
->of_node
;
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
);
125 regmap_update_bits(plat
->mode
, SYS_CFG
, SYS_CFG_SATA_MSK
,
129 of_property_read_u32(np
, "ports-implemented", &hpriv
->force_port_map
);
134 static int mtk_ahci_probe(struct platform_device
*pdev
)
136 struct device
*dev
= &pdev
->dev
;
137 struct mtk_ahci_plat
*plat
;
138 struct ahci_host_priv
*hpriv
;
141 plat
= devm_kzalloc(dev
, sizeof(*plat
), GFP_KERNEL
);
145 hpriv
= ahci_platform_get_resources(pdev
);
147 return PTR_ERR(hpriv
);
149 hpriv
->plat_data
= plat
;
151 err
= mtk_ahci_parse_property(hpriv
, dev
);
155 err
= mtk_ahci_platform_resets(hpriv
, dev
);
159 err
= ahci_platform_enable_resources(hpriv
);
163 err
= ahci_platform_init_host(pdev
, hpriv
, &ahci_port_info
,
166 goto disable_resources
;
171 ahci_platform_disable_resources(hpriv
);
175 static SIMPLE_DEV_PM_OPS(ahci_pm_ops
, ahci_platform_suspend
,
176 ahci_platform_resume
);
178 static const struct of_device_id ahci_of_match
[] = {
179 { .compatible
= "mediatek,mtk-ahci", },
182 MODULE_DEVICE_TABLE(of
, ahci_of_match
);
184 static struct platform_driver mtk_ahci_driver
= {
185 .probe
= mtk_ahci_probe
,
186 .remove
= ata_platform_remove_one
,
189 .of_match_table
= ahci_of_match
,
193 module_platform_driver(mtk_ahci_driver
);
195 MODULE_DESCRIPTION("MediaTek SATA AHCI Driver");
196 MODULE_LICENSE("GPL v2");