Linux Audio

Check our new training course

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