linux/drivers/ata/ahci_mtk.c
<<
>>
Prefs
   1/*
   2 * MediaTek AHCI SATA driver
   3 *
   4 * Copyright (c) 2017 MediaTek Inc.
   5 * Author: Ryder Lee <ryder.lee@mediatek.com>
   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
  28#define DRV_NAME                "ahci-mtk"
  29
  30#define SYS_CFG                 0x14
  31#define SYS_CFG_SATA_MSK        GENMASK(31, 30)
  32#define SYS_CFG_SATA_EN         BIT(31)
  33
  34struct 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
  41static 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
  48static struct scsi_host_template ahci_platform_sht = {
  49        AHCI_SHT(DRV_NAME),
  50};
  51
  52static 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
 110static 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
 134static 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, 0);
 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
 170disable_resources:
 171        ahci_platform_disable_resources(hpriv);
 172        return err;
 173}
 174
 175static SIMPLE_DEV_PM_OPS(ahci_pm_ops, ahci_platform_suspend,
 176                         ahci_platform_resume);
 177
 178static const struct of_device_id ahci_of_match[] = {
 179        { .compatible = "mediatek,mtk-ahci", },
 180        {},
 181};
 182MODULE_DEVICE_TABLE(of, ahci_of_match);
 183
 184static 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};
 193module_platform_driver(mtk_ahci_driver);
 194
 195MODULE_DESCRIPTION("MediaTek SATA AHCI Driver");
 196MODULE_LICENSE("GPL v2");
 197