linux/drivers/ata/ahci_mtk.c
<<
>>
Prefs
   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");
 189