blob: d1d9d5812dcf9314bb987cfe0396bae43a985200 [file] [log] [blame]
/*
* AHCI SATA platform driver
*
* Copyright 2004-2005 Red Hat, Inc.
* Jeff Garzik <jgarzik@pobox.com>
* Copyright 2010 MontaVista Software, LLC.
* Anton Vorontsov <avorontsov@ru.mvista.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*/
#include <linux/kernel.h>
#include <linux/gfp.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/device.h>
#include <linux/platform_device.h>
#include <linux/libata.h>
#include <linux/ahci_platform.h>
#include <linux/clk.h>
#include <mach/reset.h>
#include <mach/comcerto-2000/pm.h>
#include "ahci.h"
#include <mach/serdes-c2000.h>
#ifdef CONFIG_ARCH_M86XXX
/* SATA Clocks */
static struct clk *sata_oob_clk; /* Core clock */
static struct clk *sata_pmu_clk; /* PMU alive clock */
static struct clk *sata_clk; /* Sata AXI ref clock */
#if defined(CONFIG_COMCERTO_SATA_OCC_CLOCK)
static struct clk *sata_occ_clk; /* sata OCC clock */
#endif
#endif
enum ahci_type {
AHCI, /* standard platform ahci */
IMX53_AHCI, /* ahci on i.mx53 */
};
static struct platform_device_id ahci_devtype[] = {
{
.name = "ahci",
.driver_data = AHCI,
}, {
.name = "imx53-ahci",
.driver_data = IMX53_AHCI,
}, {
/* sentinel */
}
};
MODULE_DEVICE_TABLE(platform, ahci_devtype);
static const struct ata_port_info ahci_port_info[] = {
/* by features */
[AHCI] = {
.flags = AHCI_FLAG_COMMON,
.pio_mask = ATA_PIO4,
.udma_mask = ATA_UDMA6,
.port_ops = &ahci_ops,
},
[IMX53_AHCI] = {
.flags = AHCI_FLAG_COMMON,
.pio_mask = ATA_PIO4,
.udma_mask = ATA_UDMA6,
.port_ops = &ahci_pmp_retry_srst_ops,
},
};
static struct scsi_host_template ahci_platform_sht = {
AHCI_SHT("ahci_platform"),
};
#ifdef CONFIG_PM
static int ahci_platform_suspend(struct platform_device *pdev, pm_message_t state)
{
struct ata_host *host = platform_get_drvdata(pdev);
int ret=0;
#ifdef CONFIG_ARCH_M86XXX
/* Check for the Bit_Mask bit for SATA, if it is enabled
* then we are not going suspend the SATA device , as by
* this device , we will wake from System Resume.
*/
if ( !(host_utilpe_shared_pmu_bitmask & SATA_IRQ )){
/* We will Just return
*/
return ret;
}
#endif
if (host)
ret = ata_host_suspend(host, state);
#ifdef CONFIG_ARCH_M86XXX
if (!ret) /* sucessfully done the host suspend */
{
/* No do the clock disable PMU,OOB,AXI here */
clk_disable(sata_clk);
clk_disable(sata_oob_clk);
clk_disable(sata_pmu_clk);
/* PM Performance Enhancement : SRDS1 PD SATA1/SRDS2 PD SATA2 - P2 state, */
/* Resets the entire PHY module and CMU power down */
if (readl(COMCERTO_GPIO_SYSTEM_CONFIG) & BOOT_SERDES1_CNF_SATA0)
writel((readl((COMCERTO_DWC1_CFG_BASE+0x44)) | 0xCC), (COMCERTO_DWC1_CFG_BASE+0x44));
else if (readl(COMCERTO_GPIO_SYSTEM_CONFIG) & BOOT_SERDES2_CNF_SATA1)
writel((readl((COMCERTO_DWC1_CFG_BASE+0x54)) | 0xCC), (COMCERTO_DWC1_CFG_BASE+0x54));
}
#endif
return ret;
}
static int ahci_platform_resume(struct platform_device *pdev)
{
struct ata_host *host = platform_get_drvdata(pdev);
#ifdef CONFIG_ARCH_M86XXX
/* PM Performance Enhancement : SRDS1 PD SATA1/SRDS2 PD SATA2 - P2 state, */
/* Enable PHY module and CMU power UP */
if (readl(COMCERTO_GPIO_SYSTEM_CONFIG) & BOOT_SERDES1_CNF_SATA0)
writel((readl((COMCERTO_DWC1_CFG_BASE+0x44)) & ~0xCC), (COMCERTO_DWC1_CFG_BASE+0x44));
else if (readl(COMCERTO_GPIO_SYSTEM_CONFIG) & BOOT_SERDES2_CNF_SATA1)
writel((readl((COMCERTO_DWC1_CFG_BASE+0x54)) & ~0xCC), (COMCERTO_DWC1_CFG_BASE+0x54));
/* Check for the Bit_Mask bit for SATA, if it is enabled
* then we are not going suspend the SATA device , as by
* this device , we will wake from System Resume.
*/
if ( !(host_utilpe_shared_pmu_bitmask & SATA_IRQ )){
/* We will Just return
*/
return 0;
}
/* Do the clock enable here PMU,OOB,AXI */
clk_enable(sata_clk);
clk_enable(sata_oob_clk);
clk_enable(sata_pmu_clk);
#endif
if (host)
ata_host_resume(host);
return 0;
}
#else
#define ahci_platform_suspend NULL
#define ahci_platform_resume NULL
#endif
static int __init ahci_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct ahci_platform_data *pdata = dev_get_platdata(dev);
const struct platform_device_id *id = platform_get_device_id(pdev);
struct ata_port_info pi = ahci_port_info[id ? id->driver_data : 0];
const struct ata_port_info *ppi[] = { &pi, NULL };
struct ahci_host_priv *hpriv;
struct ata_host *host;
struct resource *mem;
int irq;
int n_ports;
int i;
int rc;
#ifdef CONFIG_ARCH_M86XXX
/* Get the Reference and Enable the SATA clocks here */
sata_clk = clk_get(NULL,"sata");
/* Error Handling , if no SATA(AXI) clock reference: return error */
if (IS_ERR(sata_clk)) {
pr_err("%s: Unable to obtain SATA(AXI) clock: %ld\n",__func__,PTR_ERR(sata_clk));
return PTR_ERR(sata_clk);
}
/*Enable the SATA(AXI) clock here */
rc = clk_enable(sata_clk);
if (rc){
pr_err("%s: SATA(AXI) clock enable failed \n",__func__);
return rc;
}
sata_oob_clk = clk_get(NULL,"sata_oob");
/* Error Handling , if no SATA_OOB clock reference: return error */
if (IS_ERR(sata_oob_clk)) {
pr_err("%s: Unable to obtain SATA_OOB clock: %ld\n",__func__,PTR_ERR(sata_oob_clk));
return PTR_ERR(sata_oob_clk);
}
sata_pmu_clk = clk_get(NULL,"sata_pmu");
/* Error Handling , if no SATA_PMU clock reference: return error */
if (IS_ERR(sata_pmu_clk)) {
pr_err("%s: Unable to obtain SATA_PMU clock: %ld\n",__func__,PTR_ERR(sata_pmu_clk));
return PTR_ERR(sata_pmu_clk);
}
/*Enable the SATA(PMU and OOB) clocks here */
rc = clk_enable(sata_oob_clk);
if (rc){
pr_err("%s: SATA_OOB clock enable failed \n",__func__);
return rc;
}
rc = clk_enable(sata_pmu_clk);
if (rc){
pr_err("%s: SATA_PMU clock enable failed \n",__func__);
return rc;
}
#if defined(CONFIG_COMCERTO_SATA_OCC_CLOCK)
sata_occ_clk = clk_get(NULL,"sata_occ");
/* Error Handling , if no sata occ clock reference: return error */
if (IS_ERR(sata_occ_clk)) {
pr_err("%s: Unable to obtain sata occ clock: %ld\n",__func__,PTR_ERR(sata_occ_clk));
return PTR_ERR(sata_occ_clk);
}
/*Enable the sata_occ clocks here */
rc = clk_enable(sata_occ_clk);
if (rc){
pr_err("%s: sata occ clock enable failed \n",__func__);
return rc;
}
#endif
/* Set the SATA PMU clock to 30 MHZ and OOB clock to 125MHZ */
clk_set_rate(sata_oob_clk,125000000);
clk_set_rate(sata_pmu_clk,30000000);
#endif
mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!mem) {
dev_err(dev, "no mmio space\n");
return -EINVAL;
}
irq = platform_get_irq(pdev, 0);
if (irq <= 0) {
dev_err(dev, "no irq\n");
return -EINVAL;
}
if (pdata && pdata->ata_port_info)
pi = *pdata->ata_port_info;
hpriv = devm_kzalloc(dev, sizeof(*hpriv), GFP_KERNEL);
if (!hpriv) {
dev_err(dev, "can't alloc ahci_host_priv\n");
return -ENOMEM;
}
hpriv->flags |= (unsigned long)pi.private_data;
hpriv->mmio = devm_ioremap(dev, mem->start, resource_size(mem));
if (!hpriv->mmio) {
dev_err(dev, "can't map %pR\n", mem);
return -ENOMEM;
}
/*
* Some platforms might need to prepare for mmio region access,
* which could be done in the following init call. So, the mmio
* region shouldn't be accessed before init (if provided) has
* returned successfully.
*/
if (pdata && pdata->init) {
rc = pdata->init(dev, hpriv->mmio);
if (rc)
return rc;
}
ahci_save_initial_config(dev, hpriv,
pdata ? pdata->force_port_map : 0,
pdata ? pdata->mask_port_map : 0);
/* prepare host */
if (hpriv->cap & HOST_CAP_NCQ)
pi.flags |= ATA_FLAG_NCQ;
if (hpriv->cap & HOST_CAP_PMP)
pi.flags |= ATA_FLAG_PMP;
ahci_set_em_messages(hpriv, &pi);
/* CAP.NP sometimes indicate the index of the last enabled
* port, at other times, that of the last possible port, so
* determining the maximum port number requires looking at
* both CAP.NP and port_map.
*/
n_ports = max(ahci_nr_ports(hpriv->cap), fls(hpriv->port_map));
host = ata_host_alloc_pinfo(dev, ppi, n_ports);
if (!host) {
rc = -ENOMEM;
goto err0;
}
host->private_data = hpriv;
if (!(hpriv->cap & HOST_CAP_SSS) || ahci_ignore_sss)
host->flags |= ATA_HOST_PARALLEL_SCAN;
else
printk(KERN_INFO "ahci: SSS flag set, parallel bus scan disabled\n");
if (pi.flags & ATA_FLAG_EM)
ahci_reset_em(host);
for (i = 0; i < host->n_ports; i++) {
struct ata_port *ap = host->ports[i];
ata_port_desc(ap, "mmio %pR", mem);
ata_port_desc(ap, "port 0x%x", 0x100 + ap->port_no * 0x80);
/* set enclosure management message type */
if (ap->flags & ATA_FLAG_EM)
ap->em_message_type = hpriv->em_msg_type;
#ifdef CONFIG_ARCH_M86XXX
/* Optimized PFE/SATA DDR interaction,
limit read burst size of SATA controller */
writel(0x41, ahci_port_base(ap) + 0x70);
#endif
/* disabled/not-implemented port */
if (!(hpriv->port_map & (1 << i)))
ap->ops = &ata_dummy_port_ops;
}
rc = ahci_reset_controller(host);
if (rc)
goto err0;
ahci_init_controller(host);
ahci_print_info(host, "platform");
rc = ata_host_activate(host, irq, ahci_interrupt, IRQF_SHARED,
&ahci_platform_sht);
if (rc)
goto err0;
return 0;
err0:
if (pdata && pdata->exit)
pdata->exit(dev);
return rc;
}
static int __devexit ahci_remove(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct ahci_platform_data *pdata = dev_get_platdata(dev);
struct ata_host *host = dev_get_drvdata(dev);
ata_host_detach(host);
if (pdata && pdata->exit)
pdata->exit(dev);
#ifdef CONFIG_ARCH_M86XXX
/* Disbale the SATA clocks Here */
clk_disable(sata_clk);
clk_put(sata_clk);
clk_disable(sata_oob_clk);
clk_put(sata_oob_clk);
clk_disable(sata_pmu_clk);
clk_put(sata_pmu_clk);
#if defined(CONFIG_COMCERTO_SATA_OCC_CLOCK)
clk_disable(sata_occ_clk);
clk_put(sata_occ_clk);
#endif
/*Putting SATA in reset state
* Sata axi clock domain in reset state
* Serdes 1/2 in reset state, this depends upon PCIE1 and SGMII
* sata 0/1 serdes controller in reset state
*/
c2000_block_reset(COMPONENT_AXI_SATA,1);
c2000_block_reset(COMPONENT_SERDES1,1);
c2000_block_reset(COMPONENT_SERDES_SATA0,1);
c2000_block_reset(COMPONENT_SERDES2,1);
c2000_block_reset(COMPONENT_SERDES_SATA1,1);
#endif
return 0;
}
static const struct of_device_id ahci_of_match[] = {
{ .compatible = "calxeda,hb-ahci", },
{},
};
MODULE_DEVICE_TABLE(of, ahci_of_match);
static struct platform_driver ahci_driver = {
.remove = __devexit_p(ahci_remove),
#ifdef CONFIG_PM
.suspend = ahci_platform_suspend,
.resume = ahci_platform_resume,
#endif
.driver = {
.name = "ahci",
.owner = THIS_MODULE,
.of_match_table = ahci_of_match,
},
.id_table = ahci_devtype,
};
static int __init ahci_init(void)
{
return platform_driver_probe(&ahci_driver, ahci_probe);
}
module_init(ahci_init);
static void __exit ahci_exit(void)
{
platform_driver_unregister(&ahci_driver);
}
module_exit(ahci_exit);
MODULE_DESCRIPTION("AHCI SATA platform driver");
MODULE_AUTHOR("Anton Vorontsov <avorontsov@ru.mvista.com>");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:ahci");