mirror of
https://github.com/torvalds/linux.git
synced 2024-12-29 06:12:08 +00:00
Merge branch 'for-4.1-fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/tj/libata
Pull libata fixes from Tejun Heo: "Rather big for fixes pull. - SCC controllers never lived to see the light of the day. Both libata and ide drivers removed. - In some configurations, link power management policy changes sometimes cause delayed spurious PHY events which can develop into noticeable failures. This has been reported several times over the years. Gabriele's patches suppress PHY events for a while after LPM policy changes which should help most of these failures without causing too much problem for hotplug use cases. - A few controller specific fixes" [ Hmm. I don't think removing SSC support is really a "fix", but hey, it removes a lot of lines of code. Which I like. So ... good riddance ] * 'for-4.1-fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/tj/libata: ahci: avoton port-disable reset-quirk ata: select DW_DMAC in case of SATA_DWC libata: Blacklist queued TRIM on all Samsung 800-series libata: Ignore spurious PHY event on LPM policy change libata: Add helper to determine when PHY events should be ignored ata: ahci_st: fixup layering violations / drvdata errors Remove celleb-only SCC PATA drivers
This commit is contained in:
commit
b3e5838ac0
@ -270,6 +270,7 @@ config ATA_PIIX
|
||||
config SATA_DWC
|
||||
tristate "DesignWare Cores SATA support"
|
||||
depends on 460EX
|
||||
select DW_DMAC
|
||||
help
|
||||
This option enables support for the on-chip SATA controller of the
|
||||
AppliedMicro processor 460EX.
|
||||
@ -729,15 +730,6 @@ config PATA_SC1200
|
||||
|
||||
If unsure, say N.
|
||||
|
||||
config PATA_SCC
|
||||
tristate "Toshiba's Cell Reference Set IDE support"
|
||||
depends on PCI && PPC_CELLEB
|
||||
help
|
||||
This option enables support for the built-in IDE controller on
|
||||
Toshiba Cell Reference Board.
|
||||
|
||||
If unsure, say N.
|
||||
|
||||
config PATA_SCH
|
||||
tristate "Intel SCH PATA support"
|
||||
depends on PCI
|
||||
|
@ -75,7 +75,6 @@ obj-$(CONFIG_PATA_PDC_OLD) += pata_pdc202xx_old.o
|
||||
obj-$(CONFIG_PATA_RADISYS) += pata_radisys.o
|
||||
obj-$(CONFIG_PATA_RDC) += pata_rdc.o
|
||||
obj-$(CONFIG_PATA_SC1200) += pata_sc1200.o
|
||||
obj-$(CONFIG_PATA_SCC) += pata_scc.o
|
||||
obj-$(CONFIG_PATA_SCH) += pata_sch.o
|
||||
obj-$(CONFIG_PATA_SERVERWORKS) += pata_serverworks.o
|
||||
obj-$(CONFIG_PATA_SIL680) += pata_sil680.o
|
||||
|
@ -66,6 +66,7 @@ enum board_ids {
|
||||
board_ahci_yes_fbs,
|
||||
|
||||
/* board IDs for specific chipsets in alphabetical order */
|
||||
board_ahci_avn,
|
||||
board_ahci_mcp65,
|
||||
board_ahci_mcp77,
|
||||
board_ahci_mcp89,
|
||||
@ -84,6 +85,8 @@ enum board_ids {
|
||||
static int ahci_init_one(struct pci_dev *pdev, const struct pci_device_id *ent);
|
||||
static int ahci_vt8251_hardreset(struct ata_link *link, unsigned int *class,
|
||||
unsigned long deadline);
|
||||
static int ahci_avn_hardreset(struct ata_link *link, unsigned int *class,
|
||||
unsigned long deadline);
|
||||
static void ahci_mcp89_apple_enable(struct pci_dev *pdev);
|
||||
static bool is_mcp89_apple(struct pci_dev *pdev);
|
||||
static int ahci_p5wdh_hardreset(struct ata_link *link, unsigned int *class,
|
||||
@ -107,6 +110,11 @@ static struct ata_port_operations ahci_p5wdh_ops = {
|
||||
.hardreset = ahci_p5wdh_hardreset,
|
||||
};
|
||||
|
||||
static struct ata_port_operations ahci_avn_ops = {
|
||||
.inherits = &ahci_ops,
|
||||
.hardreset = ahci_avn_hardreset,
|
||||
};
|
||||
|
||||
static const struct ata_port_info ahci_port_info[] = {
|
||||
/* by features */
|
||||
[board_ahci] = {
|
||||
@ -151,6 +159,12 @@ static const struct ata_port_info ahci_port_info[] = {
|
||||
.port_ops = &ahci_ops,
|
||||
},
|
||||
/* by chipsets */
|
||||
[board_ahci_avn] = {
|
||||
.flags = AHCI_FLAG_COMMON,
|
||||
.pio_mask = ATA_PIO4,
|
||||
.udma_mask = ATA_UDMA6,
|
||||
.port_ops = &ahci_avn_ops,
|
||||
},
|
||||
[board_ahci_mcp65] = {
|
||||
AHCI_HFLAGS (AHCI_HFLAG_NO_FPDMA_AA | AHCI_HFLAG_NO_PMP |
|
||||
AHCI_HFLAG_YES_NCQ),
|
||||
@ -290,14 +304,14 @@ static const struct pci_device_id ahci_pci_tbl[] = {
|
||||
{ PCI_VDEVICE(INTEL, 0x1f27), board_ahci }, /* Avoton RAID */
|
||||
{ PCI_VDEVICE(INTEL, 0x1f2e), board_ahci }, /* Avoton RAID */
|
||||
{ PCI_VDEVICE(INTEL, 0x1f2f), board_ahci }, /* Avoton RAID */
|
||||
{ PCI_VDEVICE(INTEL, 0x1f32), board_ahci }, /* Avoton AHCI */
|
||||
{ PCI_VDEVICE(INTEL, 0x1f33), board_ahci }, /* Avoton AHCI */
|
||||
{ PCI_VDEVICE(INTEL, 0x1f34), board_ahci }, /* Avoton RAID */
|
||||
{ PCI_VDEVICE(INTEL, 0x1f35), board_ahci }, /* Avoton RAID */
|
||||
{ PCI_VDEVICE(INTEL, 0x1f36), board_ahci }, /* Avoton RAID */
|
||||
{ PCI_VDEVICE(INTEL, 0x1f37), board_ahci }, /* Avoton RAID */
|
||||
{ PCI_VDEVICE(INTEL, 0x1f3e), board_ahci }, /* Avoton RAID */
|
||||
{ PCI_VDEVICE(INTEL, 0x1f3f), board_ahci }, /* Avoton RAID */
|
||||
{ PCI_VDEVICE(INTEL, 0x1f32), board_ahci_avn }, /* Avoton AHCI */
|
||||
{ PCI_VDEVICE(INTEL, 0x1f33), board_ahci_avn }, /* Avoton AHCI */
|
||||
{ PCI_VDEVICE(INTEL, 0x1f34), board_ahci_avn }, /* Avoton RAID */
|
||||
{ PCI_VDEVICE(INTEL, 0x1f35), board_ahci_avn }, /* Avoton RAID */
|
||||
{ PCI_VDEVICE(INTEL, 0x1f36), board_ahci_avn }, /* Avoton RAID */
|
||||
{ PCI_VDEVICE(INTEL, 0x1f37), board_ahci_avn }, /* Avoton RAID */
|
||||
{ PCI_VDEVICE(INTEL, 0x1f3e), board_ahci_avn }, /* Avoton RAID */
|
||||
{ PCI_VDEVICE(INTEL, 0x1f3f), board_ahci_avn }, /* Avoton RAID */
|
||||
{ PCI_VDEVICE(INTEL, 0x2823), board_ahci }, /* Wellsburg RAID */
|
||||
{ PCI_VDEVICE(INTEL, 0x2827), board_ahci }, /* Wellsburg RAID */
|
||||
{ PCI_VDEVICE(INTEL, 0x8d02), board_ahci }, /* Wellsburg AHCI */
|
||||
@ -670,6 +684,79 @@ static int ahci_p5wdh_hardreset(struct ata_link *link, unsigned int *class,
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
* ahci_avn_hardreset - attempt more aggressive recovery of Avoton ports.
|
||||
*
|
||||
* It has been observed with some SSDs that the timing of events in the
|
||||
* link synchronization phase can leave the port in a state that can not
|
||||
* be recovered by a SATA-hard-reset alone. The failing signature is
|
||||
* SStatus.DET stuck at 1 ("Device presence detected but Phy
|
||||
* communication not established"). It was found that unloading and
|
||||
* reloading the driver when this problem occurs allows the drive
|
||||
* connection to be recovered (DET advanced to 0x3). The critical
|
||||
* component of reloading the driver is that the port state machines are
|
||||
* reset by bouncing "port enable" in the AHCI PCS configuration
|
||||
* register. So, reproduce that effect by bouncing a port whenever we
|
||||
* see DET==1 after a reset.
|
||||
*/
|
||||
static int ahci_avn_hardreset(struct ata_link *link, unsigned int *class,
|
||||
unsigned long deadline)
|
||||
{
|
||||
const unsigned long *timing = sata_ehc_deb_timing(&link->eh_context);
|
||||
struct ata_port *ap = link->ap;
|
||||
struct ahci_port_priv *pp = ap->private_data;
|
||||
struct ahci_host_priv *hpriv = ap->host->private_data;
|
||||
u8 *d2h_fis = pp->rx_fis + RX_FIS_D2H_REG;
|
||||
unsigned long tmo = deadline - jiffies;
|
||||
struct ata_taskfile tf;
|
||||
bool online;
|
||||
int rc, i;
|
||||
|
||||
DPRINTK("ENTER\n");
|
||||
|
||||
ahci_stop_engine(ap);
|
||||
|
||||
for (i = 0; i < 2; i++) {
|
||||
u16 val;
|
||||
u32 sstatus;
|
||||
int port = ap->port_no;
|
||||
struct ata_host *host = ap->host;
|
||||
struct pci_dev *pdev = to_pci_dev(host->dev);
|
||||
|
||||
/* clear D2H reception area to properly wait for D2H FIS */
|
||||
ata_tf_init(link->device, &tf);
|
||||
tf.command = ATA_BUSY;
|
||||
ata_tf_to_fis(&tf, 0, 0, d2h_fis);
|
||||
|
||||
rc = sata_link_hardreset(link, timing, deadline, &online,
|
||||
ahci_check_ready);
|
||||
|
||||
if (sata_scr_read(link, SCR_STATUS, &sstatus) != 0 ||
|
||||
(sstatus & 0xf) != 1)
|
||||
break;
|
||||
|
||||
ata_link_printk(link, KERN_INFO, "avn bounce port%d\n",
|
||||
port);
|
||||
|
||||
pci_read_config_word(pdev, 0x92, &val);
|
||||
val &= ~(1 << port);
|
||||
pci_write_config_word(pdev, 0x92, val);
|
||||
ata_msleep(ap, 1000);
|
||||
val |= 1 << port;
|
||||
pci_write_config_word(pdev, 0x92, val);
|
||||
deadline += tmo;
|
||||
}
|
||||
|
||||
hpriv->start_engine(ap);
|
||||
|
||||
if (online)
|
||||
*class = ahci_dev_classify(ap);
|
||||
|
||||
DPRINTK("EXIT, rc=%d, class=%u\n", rc, *class);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int ahci_pci_device_suspend(struct pci_dev *pdev, pm_message_t mesg)
|
||||
{
|
||||
|
@ -37,7 +37,6 @@ struct st_ahci_drv_data {
|
||||
struct reset_control *pwr;
|
||||
struct reset_control *sw_rst;
|
||||
struct reset_control *pwr_rst;
|
||||
struct ahci_host_priv *hpriv;
|
||||
};
|
||||
|
||||
static void st_ahci_configure_oob(void __iomem *mmio)
|
||||
@ -55,9 +54,10 @@ static void st_ahci_configure_oob(void __iomem *mmio)
|
||||
writel(new_val, mmio + ST_AHCI_OOBR);
|
||||
}
|
||||
|
||||
static int st_ahci_deassert_resets(struct device *dev)
|
||||
static int st_ahci_deassert_resets(struct ahci_host_priv *hpriv,
|
||||
struct device *dev)
|
||||
{
|
||||
struct st_ahci_drv_data *drv_data = dev_get_drvdata(dev);
|
||||
struct st_ahci_drv_data *drv_data = hpriv->plat_data;
|
||||
int err;
|
||||
|
||||
if (drv_data->pwr) {
|
||||
@ -90,8 +90,8 @@ static int st_ahci_deassert_resets(struct device *dev)
|
||||
static void st_ahci_host_stop(struct ata_host *host)
|
||||
{
|
||||
struct ahci_host_priv *hpriv = host->private_data;
|
||||
struct st_ahci_drv_data *drv_data = hpriv->plat_data;
|
||||
struct device *dev = host->dev;
|
||||
struct st_ahci_drv_data *drv_data = dev_get_drvdata(dev);
|
||||
int err;
|
||||
|
||||
if (drv_data->pwr) {
|
||||
@ -103,29 +103,30 @@ static void st_ahci_host_stop(struct ata_host *host)
|
||||
ahci_platform_disable_resources(hpriv);
|
||||
}
|
||||
|
||||
static int st_ahci_probe_resets(struct platform_device *pdev)
|
||||
static int st_ahci_probe_resets(struct ahci_host_priv *hpriv,
|
||||
struct device *dev)
|
||||
{
|
||||
struct st_ahci_drv_data *drv_data = platform_get_drvdata(pdev);
|
||||
struct st_ahci_drv_data *drv_data = hpriv->plat_data;
|
||||
|
||||
drv_data->pwr = devm_reset_control_get(&pdev->dev, "pwr-dwn");
|
||||
drv_data->pwr = devm_reset_control_get(dev, "pwr-dwn");
|
||||
if (IS_ERR(drv_data->pwr)) {
|
||||
dev_info(&pdev->dev, "power reset control not defined\n");
|
||||
dev_info(dev, "power reset control not defined\n");
|
||||
drv_data->pwr = NULL;
|
||||
}
|
||||
|
||||
drv_data->sw_rst = devm_reset_control_get(&pdev->dev, "sw-rst");
|
||||
drv_data->sw_rst = devm_reset_control_get(dev, "sw-rst");
|
||||
if (IS_ERR(drv_data->sw_rst)) {
|
||||
dev_info(&pdev->dev, "soft reset control not defined\n");
|
||||
dev_info(dev, "soft reset control not defined\n");
|
||||
drv_data->sw_rst = NULL;
|
||||
}
|
||||
|
||||
drv_data->pwr_rst = devm_reset_control_get(&pdev->dev, "pwr-rst");
|
||||
drv_data->pwr_rst = devm_reset_control_get(dev, "pwr-rst");
|
||||
if (IS_ERR(drv_data->pwr_rst)) {
|
||||
dev_dbg(&pdev->dev, "power soft reset control not defined\n");
|
||||
dev_dbg(dev, "power soft reset control not defined\n");
|
||||
drv_data->pwr_rst = NULL;
|
||||
}
|
||||
|
||||
return st_ahci_deassert_resets(&pdev->dev);
|
||||
return st_ahci_deassert_resets(hpriv, dev);
|
||||
}
|
||||
|
||||
static struct ata_port_operations st_ahci_port_ops = {
|
||||
@ -154,15 +155,12 @@ static int st_ahci_probe(struct platform_device *pdev)
|
||||
if (!drv_data)
|
||||
return -ENOMEM;
|
||||
|
||||
platform_set_drvdata(pdev, drv_data);
|
||||
|
||||
hpriv = ahci_platform_get_resources(pdev);
|
||||
if (IS_ERR(hpriv))
|
||||
return PTR_ERR(hpriv);
|
||||
hpriv->plat_data = drv_data;
|
||||
|
||||
drv_data->hpriv = hpriv;
|
||||
|
||||
err = st_ahci_probe_resets(pdev);
|
||||
err = st_ahci_probe_resets(hpriv, &pdev->dev);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
@ -170,7 +168,7 @@ static int st_ahci_probe(struct platform_device *pdev)
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
st_ahci_configure_oob(drv_data->hpriv->mmio);
|
||||
st_ahci_configure_oob(hpriv->mmio);
|
||||
|
||||
err = ahci_platform_init_host(pdev, hpriv, &st_ahci_port_info,
|
||||
&ahci_platform_sht);
|
||||
@ -185,8 +183,9 @@ static int st_ahci_probe(struct platform_device *pdev)
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int st_ahci_suspend(struct device *dev)
|
||||
{
|
||||
struct st_ahci_drv_data *drv_data = dev_get_drvdata(dev);
|
||||
struct ahci_host_priv *hpriv = drv_data->hpriv;
|
||||
struct ata_host *host = dev_get_drvdata(dev);
|
||||
struct ahci_host_priv *hpriv = host->private_data;
|
||||
struct st_ahci_drv_data *drv_data = hpriv->plat_data;
|
||||
int err;
|
||||
|
||||
err = ahci_platform_suspend_host(dev);
|
||||
@ -208,21 +207,21 @@ static int st_ahci_suspend(struct device *dev)
|
||||
|
||||
static int st_ahci_resume(struct device *dev)
|
||||
{
|
||||
struct st_ahci_drv_data *drv_data = dev_get_drvdata(dev);
|
||||
struct ahci_host_priv *hpriv = drv_data->hpriv;
|
||||
struct ata_host *host = dev_get_drvdata(dev);
|
||||
struct ahci_host_priv *hpriv = host->private_data;
|
||||
int err;
|
||||
|
||||
err = ahci_platform_enable_resources(hpriv);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = st_ahci_deassert_resets(dev);
|
||||
err = st_ahci_deassert_resets(hpriv, dev);
|
||||
if (err) {
|
||||
ahci_platform_disable_resources(hpriv);
|
||||
return err;
|
||||
}
|
||||
|
||||
st_ahci_configure_oob(drv_data->hpriv->mmio);
|
||||
st_ahci_configure_oob(hpriv->mmio);
|
||||
|
||||
return ahci_platform_resume_host(dev);
|
||||
}
|
||||
|
@ -1707,8 +1707,7 @@ static void ahci_handle_port_interrupt(struct ata_port *ap,
|
||||
if (unlikely(resetting))
|
||||
status &= ~PORT_IRQ_BAD_PMP;
|
||||
|
||||
/* if LPM is enabled, PHYRDY doesn't mean anything */
|
||||
if (ap->link.lpm_policy > ATA_LPM_MAX_POWER) {
|
||||
if (sata_lpm_ignore_phy_events(&ap->link)) {
|
||||
status &= ~PORT_IRQ_PHYRDY;
|
||||
ahci_scr_write(&ap->link, SCR_ERROR, SERR_PHYRDY_CHG);
|
||||
}
|
||||
|
@ -4235,7 +4235,7 @@ static const struct ata_blacklist_entry ata_device_blacklist [] = {
|
||||
ATA_HORKAGE_ZERO_AFTER_TRIM, },
|
||||
{ "Crucial_CT*MX100*", "MU01", ATA_HORKAGE_NO_NCQ_TRIM |
|
||||
ATA_HORKAGE_ZERO_AFTER_TRIM, },
|
||||
{ "Samsung SSD 850 PRO*", NULL, ATA_HORKAGE_NO_NCQ_TRIM |
|
||||
{ "Samsung SSD 8*", NULL, ATA_HORKAGE_NO_NCQ_TRIM |
|
||||
ATA_HORKAGE_ZERO_AFTER_TRIM, },
|
||||
|
||||
/*
|
||||
@ -6752,6 +6752,38 @@ u32 ata_wait_register(struct ata_port *ap, void __iomem *reg, u32 mask, u32 val,
|
||||
return tmp;
|
||||
}
|
||||
|
||||
/**
|
||||
* sata_lpm_ignore_phy_events - test if PHY event should be ignored
|
||||
* @link: Link receiving the event
|
||||
*
|
||||
* Test whether the received PHY event has to be ignored or not.
|
||||
*
|
||||
* LOCKING:
|
||||
* None:
|
||||
*
|
||||
* RETURNS:
|
||||
* True if the event has to be ignored.
|
||||
*/
|
||||
bool sata_lpm_ignore_phy_events(struct ata_link *link)
|
||||
{
|
||||
unsigned long lpm_timeout = link->last_lpm_change +
|
||||
msecs_to_jiffies(ATA_TMOUT_SPURIOUS_PHY);
|
||||
|
||||
/* if LPM is enabled, PHYRDY doesn't mean anything */
|
||||
if (link->lpm_policy > ATA_LPM_MAX_POWER)
|
||||
return true;
|
||||
|
||||
/* ignore the first PHY event after the LPM policy changed
|
||||
* as it is might be spurious
|
||||
*/
|
||||
if ((link->flags & ATA_LFLAG_CHANGED) &&
|
||||
time_before(jiffies, lpm_timeout))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(sata_lpm_ignore_phy_events);
|
||||
|
||||
/*
|
||||
* Dummy port_ops
|
||||
*/
|
||||
|
@ -3597,6 +3597,9 @@ static int ata_eh_set_lpm(struct ata_link *link, enum ata_lpm_policy policy,
|
||||
}
|
||||
}
|
||||
|
||||
link->last_lpm_change = jiffies;
|
||||
link->flags |= ATA_LFLAG_CHANGED;
|
||||
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -643,15 +643,6 @@ config BLK_DEV_TC86C001
|
||||
help
|
||||
This driver adds support for Toshiba TC86C001 GOKU-S chip.
|
||||
|
||||
config BLK_DEV_CELLEB
|
||||
tristate "Toshiba's Cell Reference Set IDE support"
|
||||
depends on PPC_CELLEB
|
||||
select BLK_DEV_IDEDMA_PCI
|
||||
help
|
||||
This driver provides support for the on-board IDE controller on
|
||||
Toshiba Cell Reference Board.
|
||||
If unsure, say Y.
|
||||
|
||||
endif
|
||||
|
||||
# TODO: BLK_DEV_IDEDMA_PCI -> BLK_DEV_IDEDMA_SFF
|
||||
|
@ -38,7 +38,6 @@ obj-$(CONFIG_BLK_DEV_AEC62XX) += aec62xx.o
|
||||
obj-$(CONFIG_BLK_DEV_ALI15X3) += alim15x3.o
|
||||
obj-$(CONFIG_BLK_DEV_AMD74XX) += amd74xx.o
|
||||
obj-$(CONFIG_BLK_DEV_ATIIXP) += atiixp.o
|
||||
obj-$(CONFIG_BLK_DEV_CELLEB) += scc_pata.o
|
||||
obj-$(CONFIG_BLK_DEV_CMD64X) += cmd64x.o
|
||||
obj-$(CONFIG_BLK_DEV_CS5520) += cs5520.o
|
||||
obj-$(CONFIG_BLK_DEV_CS5530) += cs5530.o
|
||||
|
@ -1,887 +0,0 @@
|
||||
/*
|
||||
* Support for IDE interfaces on Celleb platform
|
||||
*
|
||||
* (C) Copyright 2006 TOSHIBA CORPORATION
|
||||
*
|
||||
* This code is based on drivers/ide/pci/siimage.c:
|
||||
* Copyright (C) 2001-2002 Andre Hedrick <andre@linux-ide.org>
|
||||
* Copyright (C) 2003 Red Hat
|
||||
*
|
||||
* 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 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/ide.h>
|
||||
#include <linux/init.h>
|
||||
|
||||
#define PCI_DEVICE_ID_TOSHIBA_SCC_ATA 0x01b4
|
||||
|
||||
#define SCC_PATA_NAME "scc IDE"
|
||||
|
||||
#define TDVHSEL_MASTER 0x00000001
|
||||
#define TDVHSEL_SLAVE 0x00000004
|
||||
|
||||
#define MODE_JCUSFEN 0x00000080
|
||||
|
||||
#define CCKCTRL_ATARESET 0x00040000
|
||||
#define CCKCTRL_BUFCNT 0x00020000
|
||||
#define CCKCTRL_CRST 0x00010000
|
||||
#define CCKCTRL_OCLKEN 0x00000100
|
||||
#define CCKCTRL_ATACLKOEN 0x00000002
|
||||
#define CCKCTRL_LCLKEN 0x00000001
|
||||
|
||||
#define QCHCD_IOS_SS 0x00000001
|
||||
|
||||
#define QCHSD_STPDIAG 0x00020000
|
||||
|
||||
#define INTMASK_MSK 0xD1000012
|
||||
#define INTSTS_SERROR 0x80000000
|
||||
#define INTSTS_PRERR 0x40000000
|
||||
#define INTSTS_RERR 0x10000000
|
||||
#define INTSTS_ICERR 0x01000000
|
||||
#define INTSTS_BMSINT 0x00000010
|
||||
#define INTSTS_BMHE 0x00000008
|
||||
#define INTSTS_IOIRQS 0x00000004
|
||||
#define INTSTS_INTRQ 0x00000002
|
||||
#define INTSTS_ACTEINT 0x00000001
|
||||
|
||||
#define ECMODE_VALUE 0x01
|
||||
|
||||
static struct scc_ports {
|
||||
unsigned long ctl, dma;
|
||||
struct ide_host *host; /* for removing port from system */
|
||||
} scc_ports[MAX_HWIFS];
|
||||
|
||||
/* PIO transfer mode table */
|
||||
/* JCHST */
|
||||
static unsigned long JCHSTtbl[2][7] = {
|
||||
{0x0E, 0x05, 0x02, 0x03, 0x02, 0x00, 0x00}, /* 100MHz */
|
||||
{0x13, 0x07, 0x04, 0x04, 0x03, 0x00, 0x00} /* 133MHz */
|
||||
};
|
||||
|
||||
/* JCHHT */
|
||||
static unsigned long JCHHTtbl[2][7] = {
|
||||
{0x0E, 0x02, 0x02, 0x02, 0x02, 0x00, 0x00}, /* 100MHz */
|
||||
{0x13, 0x03, 0x03, 0x03, 0x03, 0x00, 0x00} /* 133MHz */
|
||||
};
|
||||
|
||||
/* JCHCT */
|
||||
static unsigned long JCHCTtbl[2][7] = {
|
||||
{0x1D, 0x1D, 0x1C, 0x0B, 0x06, 0x00, 0x00}, /* 100MHz */
|
||||
{0x27, 0x26, 0x26, 0x0E, 0x09, 0x00, 0x00} /* 133MHz */
|
||||
};
|
||||
|
||||
|
||||
/* DMA transfer mode table */
|
||||
/* JCHDCTM/JCHDCTS */
|
||||
static unsigned long JCHDCTxtbl[2][7] = {
|
||||
{0x0A, 0x06, 0x04, 0x03, 0x01, 0x00, 0x00}, /* 100MHz */
|
||||
{0x0E, 0x09, 0x06, 0x04, 0x02, 0x01, 0x00} /* 133MHz */
|
||||
};
|
||||
|
||||
/* JCSTWTM/JCSTWTS */
|
||||
static unsigned long JCSTWTxtbl[2][7] = {
|
||||
{0x06, 0x04, 0x03, 0x02, 0x02, 0x02, 0x00}, /* 100MHz */
|
||||
{0x09, 0x06, 0x04, 0x02, 0x02, 0x02, 0x02} /* 133MHz */
|
||||
};
|
||||
|
||||
/* JCTSS */
|
||||
static unsigned long JCTSStbl[2][7] = {
|
||||
{0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x00}, /* 100MHz */
|
||||
{0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05} /* 133MHz */
|
||||
};
|
||||
|
||||
/* JCENVT */
|
||||
static unsigned long JCENVTtbl[2][7] = {
|
||||
{0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00}, /* 100MHz */
|
||||
{0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02} /* 133MHz */
|
||||
};
|
||||
|
||||
/* JCACTSELS/JCACTSELM */
|
||||
static unsigned long JCACTSELtbl[2][7] = {
|
||||
{0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00}, /* 100MHz */
|
||||
{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01} /* 133MHz */
|
||||
};
|
||||
|
||||
|
||||
static u8 scc_ide_inb(unsigned long port)
|
||||
{
|
||||
u32 data = in_be32((void*)port);
|
||||
return (u8)data;
|
||||
}
|
||||
|
||||
static void scc_exec_command(ide_hwif_t *hwif, u8 cmd)
|
||||
{
|
||||
out_be32((void *)hwif->io_ports.command_addr, cmd);
|
||||
eieio();
|
||||
in_be32((void *)(hwif->dma_base + 0x01c));
|
||||
eieio();
|
||||
}
|
||||
|
||||
static u8 scc_read_status(ide_hwif_t *hwif)
|
||||
{
|
||||
return (u8)in_be32((void *)hwif->io_ports.status_addr);
|
||||
}
|
||||
|
||||
static u8 scc_read_altstatus(ide_hwif_t *hwif)
|
||||
{
|
||||
return (u8)in_be32((void *)hwif->io_ports.ctl_addr);
|
||||
}
|
||||
|
||||
static u8 scc_dma_sff_read_status(ide_hwif_t *hwif)
|
||||
{
|
||||
return (u8)in_be32((void *)(hwif->dma_base + 4));
|
||||
}
|
||||
|
||||
static void scc_write_devctl(ide_hwif_t *hwif, u8 ctl)
|
||||
{
|
||||
out_be32((void *)hwif->io_ports.ctl_addr, ctl);
|
||||
eieio();
|
||||
in_be32((void *)(hwif->dma_base + 0x01c));
|
||||
eieio();
|
||||
}
|
||||
|
||||
static void scc_ide_insw(unsigned long port, void *addr, u32 count)
|
||||
{
|
||||
u16 *ptr = (u16 *)addr;
|
||||
while (count--) {
|
||||
*ptr++ = le16_to_cpu(in_be32((void*)port));
|
||||
}
|
||||
}
|
||||
|
||||
static void scc_ide_insl(unsigned long port, void *addr, u32 count)
|
||||
{
|
||||
u16 *ptr = (u16 *)addr;
|
||||
while (count--) {
|
||||
*ptr++ = le16_to_cpu(in_be32((void*)port));
|
||||
*ptr++ = le16_to_cpu(in_be32((void*)port));
|
||||
}
|
||||
}
|
||||
|
||||
static void scc_ide_outb(u8 addr, unsigned long port)
|
||||
{
|
||||
out_be32((void*)port, addr);
|
||||
}
|
||||
|
||||
static void
|
||||
scc_ide_outsw(unsigned long port, void *addr, u32 count)
|
||||
{
|
||||
u16 *ptr = (u16 *)addr;
|
||||
while (count--) {
|
||||
out_be32((void*)port, cpu_to_le16(*ptr++));
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
scc_ide_outsl(unsigned long port, void *addr, u32 count)
|
||||
{
|
||||
u16 *ptr = (u16 *)addr;
|
||||
while (count--) {
|
||||
out_be32((void*)port, cpu_to_le16(*ptr++));
|
||||
out_be32((void*)port, cpu_to_le16(*ptr++));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* scc_set_pio_mode - set host controller for PIO mode
|
||||
* @hwif: port
|
||||
* @drive: drive
|
||||
*
|
||||
* Load the timing settings for this device mode into the
|
||||
* controller.
|
||||
*/
|
||||
|
||||
static void scc_set_pio_mode(ide_hwif_t *hwif, ide_drive_t *drive)
|
||||
{
|
||||
struct scc_ports *ports = ide_get_hwifdata(hwif);
|
||||
unsigned long ctl_base = ports->ctl;
|
||||
unsigned long cckctrl_port = ctl_base + 0xff0;
|
||||
unsigned long piosht_port = ctl_base + 0x000;
|
||||
unsigned long pioct_port = ctl_base + 0x004;
|
||||
unsigned long reg;
|
||||
int offset;
|
||||
const u8 pio = drive->pio_mode - XFER_PIO_0;
|
||||
|
||||
reg = in_be32((void __iomem *)cckctrl_port);
|
||||
if (reg & CCKCTRL_ATACLKOEN) {
|
||||
offset = 1; /* 133MHz */
|
||||
} else {
|
||||
offset = 0; /* 100MHz */
|
||||
}
|
||||
reg = JCHSTtbl[offset][pio] << 16 | JCHHTtbl[offset][pio];
|
||||
out_be32((void __iomem *)piosht_port, reg);
|
||||
reg = JCHCTtbl[offset][pio];
|
||||
out_be32((void __iomem *)pioct_port, reg);
|
||||
}
|
||||
|
||||
/**
|
||||
* scc_set_dma_mode - set host controller for DMA mode
|
||||
* @hwif: port
|
||||
* @drive: drive
|
||||
*
|
||||
* Load the timing settings for this device mode into the
|
||||
* controller.
|
||||
*/
|
||||
|
||||
static void scc_set_dma_mode(ide_hwif_t *hwif, ide_drive_t *drive)
|
||||
{
|
||||
struct scc_ports *ports = ide_get_hwifdata(hwif);
|
||||
unsigned long ctl_base = ports->ctl;
|
||||
unsigned long cckctrl_port = ctl_base + 0xff0;
|
||||
unsigned long mdmact_port = ctl_base + 0x008;
|
||||
unsigned long mcrcst_port = ctl_base + 0x00c;
|
||||
unsigned long sdmact_port = ctl_base + 0x010;
|
||||
unsigned long scrcst_port = ctl_base + 0x014;
|
||||
unsigned long udenvt_port = ctl_base + 0x018;
|
||||
unsigned long tdvhsel_port = ctl_base + 0x020;
|
||||
int is_slave = drive->dn & 1;
|
||||
int offset, idx;
|
||||
unsigned long reg;
|
||||
unsigned long jcactsel;
|
||||
const u8 speed = drive->dma_mode;
|
||||
|
||||
reg = in_be32((void __iomem *)cckctrl_port);
|
||||
if (reg & CCKCTRL_ATACLKOEN) {
|
||||
offset = 1; /* 133MHz */
|
||||
} else {
|
||||
offset = 0; /* 100MHz */
|
||||
}
|
||||
|
||||
idx = speed - XFER_UDMA_0;
|
||||
|
||||
jcactsel = JCACTSELtbl[offset][idx];
|
||||
if (is_slave) {
|
||||
out_be32((void __iomem *)sdmact_port, JCHDCTxtbl[offset][idx]);
|
||||
out_be32((void __iomem *)scrcst_port, JCSTWTxtbl[offset][idx]);
|
||||
jcactsel = jcactsel << 2;
|
||||
out_be32((void __iomem *)tdvhsel_port, (in_be32((void __iomem *)tdvhsel_port) & ~TDVHSEL_SLAVE) | jcactsel);
|
||||
} else {
|
||||
out_be32((void __iomem *)mdmact_port, JCHDCTxtbl[offset][idx]);
|
||||
out_be32((void __iomem *)mcrcst_port, JCSTWTxtbl[offset][idx]);
|
||||
out_be32((void __iomem *)tdvhsel_port, (in_be32((void __iomem *)tdvhsel_port) & ~TDVHSEL_MASTER) | jcactsel);
|
||||
}
|
||||
reg = JCTSStbl[offset][idx] << 16 | JCENVTtbl[offset][idx];
|
||||
out_be32((void __iomem *)udenvt_port, reg);
|
||||
}
|
||||
|
||||
static void scc_dma_host_set(ide_drive_t *drive, int on)
|
||||
{
|
||||
ide_hwif_t *hwif = drive->hwif;
|
||||
u8 unit = drive->dn & 1;
|
||||
u8 dma_stat = scc_dma_sff_read_status(hwif);
|
||||
|
||||
if (on)
|
||||
dma_stat |= (1 << (5 + unit));
|
||||
else
|
||||
dma_stat &= ~(1 << (5 + unit));
|
||||
|
||||
scc_ide_outb(dma_stat, hwif->dma_base + 4);
|
||||
}
|
||||
|
||||
/**
|
||||
* scc_dma_setup - begin a DMA phase
|
||||
* @drive: target device
|
||||
* @cmd: command
|
||||
*
|
||||
* Build an IDE DMA PRD (IDE speak for scatter gather table)
|
||||
* and then set up the DMA transfer registers.
|
||||
*
|
||||
* Returns 0 on success. If a PIO fallback is required then 1
|
||||
* is returned.
|
||||
*/
|
||||
|
||||
static int scc_dma_setup(ide_drive_t *drive, struct ide_cmd *cmd)
|
||||
{
|
||||
ide_hwif_t *hwif = drive->hwif;
|
||||
u32 rw = (cmd->tf_flags & IDE_TFLAG_WRITE) ? 0 : ATA_DMA_WR;
|
||||
u8 dma_stat;
|
||||
|
||||
/* fall back to pio! */
|
||||
if (ide_build_dmatable(drive, cmd) == 0)
|
||||
return 1;
|
||||
|
||||
/* PRD table */
|
||||
out_be32((void __iomem *)(hwif->dma_base + 8), hwif->dmatable_dma);
|
||||
|
||||
/* specify r/w */
|
||||
out_be32((void __iomem *)hwif->dma_base, rw);
|
||||
|
||||
/* read DMA status for INTR & ERROR flags */
|
||||
dma_stat = scc_dma_sff_read_status(hwif);
|
||||
|
||||
/* clear INTR & ERROR flags */
|
||||
out_be32((void __iomem *)(hwif->dma_base + 4), dma_stat | 6);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void scc_dma_start(ide_drive_t *drive)
|
||||
{
|
||||
ide_hwif_t *hwif = drive->hwif;
|
||||
u8 dma_cmd = scc_ide_inb(hwif->dma_base);
|
||||
|
||||
/* start DMA */
|
||||
scc_ide_outb(dma_cmd | 1, hwif->dma_base);
|
||||
}
|
||||
|
||||
static int __scc_dma_end(ide_drive_t *drive)
|
||||
{
|
||||
ide_hwif_t *hwif = drive->hwif;
|
||||
u8 dma_stat, dma_cmd;
|
||||
|
||||
/* get DMA command mode */
|
||||
dma_cmd = scc_ide_inb(hwif->dma_base);
|
||||
/* stop DMA */
|
||||
scc_ide_outb(dma_cmd & ~1, hwif->dma_base);
|
||||
/* get DMA status */
|
||||
dma_stat = scc_dma_sff_read_status(hwif);
|
||||
/* clear the INTR & ERROR bits */
|
||||
scc_ide_outb(dma_stat | 6, hwif->dma_base + 4);
|
||||
/* verify good DMA status */
|
||||
return (dma_stat & 7) != 4 ? (0x10 | dma_stat) : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* scc_dma_end - Stop DMA
|
||||
* @drive: IDE drive
|
||||
*
|
||||
* Check and clear INT Status register.
|
||||
* Then call __scc_dma_end().
|
||||
*/
|
||||
|
||||
static int scc_dma_end(ide_drive_t *drive)
|
||||
{
|
||||
ide_hwif_t *hwif = drive->hwif;
|
||||
void __iomem *dma_base = (void __iomem *)hwif->dma_base;
|
||||
unsigned long intsts_port = hwif->dma_base + 0x014;
|
||||
u32 reg;
|
||||
int dma_stat, data_loss = 0;
|
||||
static int retry = 0;
|
||||
|
||||
/* errata A308 workaround: Step5 (check data loss) */
|
||||
/* We don't check non ide_disk because it is limited to UDMA4 */
|
||||
if (!(in_be32((void __iomem *)hwif->io_ports.ctl_addr)
|
||||
& ATA_ERR) &&
|
||||
drive->media == ide_disk && drive->current_speed > XFER_UDMA_4) {
|
||||
reg = in_be32((void __iomem *)intsts_port);
|
||||
if (!(reg & INTSTS_ACTEINT)) {
|
||||
printk(KERN_WARNING "%s: operation failed (transfer data loss)\n",
|
||||
drive->name);
|
||||
data_loss = 1;
|
||||
if (retry++) {
|
||||
struct request *rq = hwif->rq;
|
||||
ide_drive_t *drive;
|
||||
int i;
|
||||
|
||||
/* ERROR_RESET and drive->crc_count are needed
|
||||
* to reduce DMA transfer mode in retry process.
|
||||
*/
|
||||
if (rq)
|
||||
rq->errors |= ERROR_RESET;
|
||||
|
||||
ide_port_for_each_dev(i, drive, hwif)
|
||||
drive->crc_count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
while (1) {
|
||||
reg = in_be32((void __iomem *)intsts_port);
|
||||
|
||||
if (reg & INTSTS_SERROR) {
|
||||
printk(KERN_WARNING "%s: SERROR\n", SCC_PATA_NAME);
|
||||
out_be32((void __iomem *)intsts_port, INTSTS_SERROR|INTSTS_BMSINT);
|
||||
|
||||
out_be32(dma_base, in_be32(dma_base) & ~QCHCD_IOS_SS);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (reg & INTSTS_PRERR) {
|
||||
u32 maea0, maec0;
|
||||
unsigned long ctl_base = hwif->config_data;
|
||||
|
||||
maea0 = in_be32((void __iomem *)(ctl_base + 0xF50));
|
||||
maec0 = in_be32((void __iomem *)(ctl_base + 0xF54));
|
||||
|
||||
printk(KERN_WARNING "%s: PRERR [addr:%x cmd:%x]\n", SCC_PATA_NAME, maea0, maec0);
|
||||
|
||||
out_be32((void __iomem *)intsts_port, INTSTS_PRERR|INTSTS_BMSINT);
|
||||
|
||||
out_be32(dma_base, in_be32(dma_base) & ~QCHCD_IOS_SS);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (reg & INTSTS_RERR) {
|
||||
printk(KERN_WARNING "%s: Response Error\n", SCC_PATA_NAME);
|
||||
out_be32((void __iomem *)intsts_port, INTSTS_RERR|INTSTS_BMSINT);
|
||||
|
||||
out_be32(dma_base, in_be32(dma_base) & ~QCHCD_IOS_SS);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (reg & INTSTS_ICERR) {
|
||||
out_be32(dma_base, in_be32(dma_base) & ~QCHCD_IOS_SS);
|
||||
|
||||
printk(KERN_WARNING "%s: Illegal Configuration\n", SCC_PATA_NAME);
|
||||
out_be32((void __iomem *)intsts_port, INTSTS_ICERR|INTSTS_BMSINT);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (reg & INTSTS_BMSINT) {
|
||||
printk(KERN_WARNING "%s: Internal Bus Error\n", SCC_PATA_NAME);
|
||||
out_be32((void __iomem *)intsts_port, INTSTS_BMSINT);
|
||||
|
||||
ide_do_reset(drive);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (reg & INTSTS_BMHE) {
|
||||
out_be32((void __iomem *)intsts_port, INTSTS_BMHE);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (reg & INTSTS_ACTEINT) {
|
||||
out_be32((void __iomem *)intsts_port, INTSTS_ACTEINT);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (reg & INTSTS_IOIRQS) {
|
||||
out_be32((void __iomem *)intsts_port, INTSTS_IOIRQS);
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
dma_stat = __scc_dma_end(drive);
|
||||
if (data_loss)
|
||||
dma_stat |= 2; /* emulate DMA error (to retry command) */
|
||||
return dma_stat;
|
||||
}
|
||||
|
||||
/* returns 1 if dma irq issued, 0 otherwise */
|
||||
static int scc_dma_test_irq(ide_drive_t *drive)
|
||||
{
|
||||
ide_hwif_t *hwif = drive->hwif;
|
||||
u32 int_stat = in_be32((void __iomem *)hwif->dma_base + 0x014);
|
||||
|
||||
/* SCC errata A252,A308 workaround: Step4 */
|
||||
if ((in_be32((void __iomem *)hwif->io_ports.ctl_addr)
|
||||
& ATA_ERR) &&
|
||||
(int_stat & INTSTS_INTRQ))
|
||||
return 1;
|
||||
|
||||
/* SCC errata A308 workaround: Step5 (polling IOIRQS) */
|
||||
if (int_stat & INTSTS_IOIRQS)
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static u8 scc_udma_filter(ide_drive_t *drive)
|
||||
{
|
||||
ide_hwif_t *hwif = drive->hwif;
|
||||
u8 mask = hwif->ultra_mask;
|
||||
|
||||
/* errata A308 workaround: limit non ide_disk drive to UDMA4 */
|
||||
if ((drive->media != ide_disk) && (mask & 0xE0)) {
|
||||
printk(KERN_INFO "%s: limit %s to UDMA4\n",
|
||||
SCC_PATA_NAME, drive->name);
|
||||
mask = ATA_UDMA4;
|
||||
}
|
||||
|
||||
return mask;
|
||||
}
|
||||
|
||||
/**
|
||||
* setup_mmio_scc - map CTRL/BMID region
|
||||
* @dev: PCI device we are configuring
|
||||
* @name: device name
|
||||
*
|
||||
*/
|
||||
|
||||
static int setup_mmio_scc (struct pci_dev *dev, const char *name)
|
||||
{
|
||||
void __iomem *ctl_addr;
|
||||
void __iomem *dma_addr;
|
||||
int i, ret;
|
||||
|
||||
for (i = 0; i < MAX_HWIFS; i++) {
|
||||
if (scc_ports[i].ctl == 0)
|
||||
break;
|
||||
}
|
||||
if (i >= MAX_HWIFS)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = pci_request_selected_regions(dev, (1 << 2) - 1, name);
|
||||
if (ret < 0) {
|
||||
printk(KERN_ERR "%s: can't reserve resources\n", name);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ctl_addr = pci_ioremap_bar(dev, 0);
|
||||
if (!ctl_addr)
|
||||
goto fail_0;
|
||||
|
||||
dma_addr = pci_ioremap_bar(dev, 1);
|
||||
if (!dma_addr)
|
||||
goto fail_1;
|
||||
|
||||
pci_set_master(dev);
|
||||
scc_ports[i].ctl = (unsigned long)ctl_addr;
|
||||
scc_ports[i].dma = (unsigned long)dma_addr;
|
||||
pci_set_drvdata(dev, (void *) &scc_ports[i]);
|
||||
|
||||
return 1;
|
||||
|
||||
fail_1:
|
||||
iounmap(ctl_addr);
|
||||
fail_0:
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
static int scc_ide_setup_pci_device(struct pci_dev *dev,
|
||||
const struct ide_port_info *d)
|
||||
{
|
||||
struct scc_ports *ports = pci_get_drvdata(dev);
|
||||
struct ide_host *host;
|
||||
struct ide_hw hw, *hws[] = { &hw };
|
||||
int i, rc;
|
||||
|
||||
memset(&hw, 0, sizeof(hw));
|
||||
for (i = 0; i <= 8; i++)
|
||||
hw.io_ports_array[i] = ports->dma + 0x20 + i * 4;
|
||||
hw.irq = dev->irq;
|
||||
hw.dev = &dev->dev;
|
||||
|
||||
rc = ide_host_add(d, hws, 1, &host);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
ports->host = host;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* init_setup_scc - set up an SCC PATA Controller
|
||||
* @dev: PCI device
|
||||
* @d: IDE port info
|
||||
*
|
||||
* Perform the initial set up for this device.
|
||||
*/
|
||||
|
||||
static int init_setup_scc(struct pci_dev *dev, const struct ide_port_info *d)
|
||||
{
|
||||
unsigned long ctl_base;
|
||||
unsigned long dma_base;
|
||||
unsigned long cckctrl_port;
|
||||
unsigned long intmask_port;
|
||||
unsigned long mode_port;
|
||||
unsigned long ecmode_port;
|
||||
u32 reg = 0;
|
||||
struct scc_ports *ports;
|
||||
int rc;
|
||||
|
||||
rc = pci_enable_device(dev);
|
||||
if (rc)
|
||||
goto end;
|
||||
|
||||
rc = setup_mmio_scc(dev, d->name);
|
||||
if (rc < 0)
|
||||
goto end;
|
||||
|
||||
ports = pci_get_drvdata(dev);
|
||||
ctl_base = ports->ctl;
|
||||
dma_base = ports->dma;
|
||||
cckctrl_port = ctl_base + 0xff0;
|
||||
intmask_port = dma_base + 0x010;
|
||||
mode_port = ctl_base + 0x024;
|
||||
ecmode_port = ctl_base + 0xf00;
|
||||
|
||||
/* controller initialization */
|
||||
reg = 0;
|
||||
out_be32((void*)cckctrl_port, reg);
|
||||
reg |= CCKCTRL_ATACLKOEN;
|
||||
out_be32((void*)cckctrl_port, reg);
|
||||
reg |= CCKCTRL_LCLKEN | CCKCTRL_OCLKEN;
|
||||
out_be32((void*)cckctrl_port, reg);
|
||||
reg |= CCKCTRL_CRST;
|
||||
out_be32((void*)cckctrl_port, reg);
|
||||
|
||||
for (;;) {
|
||||
reg = in_be32((void*)cckctrl_port);
|
||||
if (reg & CCKCTRL_CRST)
|
||||
break;
|
||||
udelay(5000);
|
||||
}
|
||||
|
||||
reg |= CCKCTRL_ATARESET;
|
||||
out_be32((void*)cckctrl_port, reg);
|
||||
|
||||
out_be32((void*)ecmode_port, ECMODE_VALUE);
|
||||
out_be32((void*)mode_port, MODE_JCUSFEN);
|
||||
out_be32((void*)intmask_port, INTMASK_MSK);
|
||||
|
||||
rc = scc_ide_setup_pci_device(dev, d);
|
||||
|
||||
end:
|
||||
return rc;
|
||||
}
|
||||
|
||||
static void scc_tf_load(ide_drive_t *drive, struct ide_taskfile *tf, u8 valid)
|
||||
{
|
||||
struct ide_io_ports *io_ports = &drive->hwif->io_ports;
|
||||
|
||||
if (valid & IDE_VALID_FEATURE)
|
||||
scc_ide_outb(tf->feature, io_ports->feature_addr);
|
||||
if (valid & IDE_VALID_NSECT)
|
||||
scc_ide_outb(tf->nsect, io_ports->nsect_addr);
|
||||
if (valid & IDE_VALID_LBAL)
|
||||
scc_ide_outb(tf->lbal, io_ports->lbal_addr);
|
||||
if (valid & IDE_VALID_LBAM)
|
||||
scc_ide_outb(tf->lbam, io_ports->lbam_addr);
|
||||
if (valid & IDE_VALID_LBAH)
|
||||
scc_ide_outb(tf->lbah, io_ports->lbah_addr);
|
||||
if (valid & IDE_VALID_DEVICE)
|
||||
scc_ide_outb(tf->device, io_ports->device_addr);
|
||||
}
|
||||
|
||||
static void scc_tf_read(ide_drive_t *drive, struct ide_taskfile *tf, u8 valid)
|
||||
{
|
||||
struct ide_io_ports *io_ports = &drive->hwif->io_ports;
|
||||
|
||||
if (valid & IDE_VALID_ERROR)
|
||||
tf->error = scc_ide_inb(io_ports->feature_addr);
|
||||
if (valid & IDE_VALID_NSECT)
|
||||
tf->nsect = scc_ide_inb(io_ports->nsect_addr);
|
||||
if (valid & IDE_VALID_LBAL)
|
||||
tf->lbal = scc_ide_inb(io_ports->lbal_addr);
|
||||
if (valid & IDE_VALID_LBAM)
|
||||
tf->lbam = scc_ide_inb(io_ports->lbam_addr);
|
||||
if (valid & IDE_VALID_LBAH)
|
||||
tf->lbah = scc_ide_inb(io_ports->lbah_addr);
|
||||
if (valid & IDE_VALID_DEVICE)
|
||||
tf->device = scc_ide_inb(io_ports->device_addr);
|
||||
}
|
||||
|
||||
static void scc_input_data(ide_drive_t *drive, struct ide_cmd *cmd,
|
||||
void *buf, unsigned int len)
|
||||
{
|
||||
unsigned long data_addr = drive->hwif->io_ports.data_addr;
|
||||
|
||||
len++;
|
||||
|
||||
if (drive->io_32bit) {
|
||||
scc_ide_insl(data_addr, buf, len / 4);
|
||||
|
||||
if ((len & 3) >= 2)
|
||||
scc_ide_insw(data_addr, (u8 *)buf + (len & ~3), 1);
|
||||
} else
|
||||
scc_ide_insw(data_addr, buf, len / 2);
|
||||
}
|
||||
|
||||
static void scc_output_data(ide_drive_t *drive, struct ide_cmd *cmd,
|
||||
void *buf, unsigned int len)
|
||||
{
|
||||
unsigned long data_addr = drive->hwif->io_ports.data_addr;
|
||||
|
||||
len++;
|
||||
|
||||
if (drive->io_32bit) {
|
||||
scc_ide_outsl(data_addr, buf, len / 4);
|
||||
|
||||
if ((len & 3) >= 2)
|
||||
scc_ide_outsw(data_addr, (u8 *)buf + (len & ~3), 1);
|
||||
} else
|
||||
scc_ide_outsw(data_addr, buf, len / 2);
|
||||
}
|
||||
|
||||
/**
|
||||
* init_mmio_iops_scc - set up the iops for MMIO
|
||||
* @hwif: interface to set up
|
||||
*
|
||||
*/
|
||||
|
||||
static void init_mmio_iops_scc(ide_hwif_t *hwif)
|
||||
{
|
||||
struct pci_dev *dev = to_pci_dev(hwif->dev);
|
||||
struct scc_ports *ports = pci_get_drvdata(dev);
|
||||
unsigned long dma_base = ports->dma;
|
||||
|
||||
ide_set_hwifdata(hwif, ports);
|
||||
|
||||
hwif->dma_base = dma_base;
|
||||
hwif->config_data = ports->ctl;
|
||||
}
|
||||
|
||||
/**
|
||||
* init_iops_scc - set up iops
|
||||
* @hwif: interface to set up
|
||||
*
|
||||
* Do the basic setup for the SCC hardware interface
|
||||
* and then do the MMIO setup.
|
||||
*/
|
||||
|
||||
static void init_iops_scc(ide_hwif_t *hwif)
|
||||
{
|
||||
struct pci_dev *dev = to_pci_dev(hwif->dev);
|
||||
|
||||
hwif->hwif_data = NULL;
|
||||
if (pci_get_drvdata(dev) == NULL)
|
||||
return;
|
||||
init_mmio_iops_scc(hwif);
|
||||
}
|
||||
|
||||
static int scc_init_dma(ide_hwif_t *hwif, const struct ide_port_info *d)
|
||||
{
|
||||
return ide_allocate_dma_engine(hwif);
|
||||
}
|
||||
|
||||
static u8 scc_cable_detect(ide_hwif_t *hwif)
|
||||
{
|
||||
return ATA_CBL_PATA80;
|
||||
}
|
||||
|
||||
/**
|
||||
* init_hwif_scc - set up hwif
|
||||
* @hwif: interface to set up
|
||||
*
|
||||
* We do the basic set up of the interface structure. The SCC
|
||||
* requires several custom handlers so we override the default
|
||||
* ide DMA handlers appropriately.
|
||||
*/
|
||||
|
||||
static void init_hwif_scc(ide_hwif_t *hwif)
|
||||
{
|
||||
/* PTERADD */
|
||||
out_be32((void __iomem *)(hwif->dma_base + 0x018), hwif->dmatable_dma);
|
||||
|
||||
if (in_be32((void __iomem *)(hwif->config_data + 0xff0)) & CCKCTRL_ATACLKOEN)
|
||||
hwif->ultra_mask = ATA_UDMA6; /* 133MHz */
|
||||
else
|
||||
hwif->ultra_mask = ATA_UDMA5; /* 100MHz */
|
||||
}
|
||||
|
||||
static const struct ide_tp_ops scc_tp_ops = {
|
||||
.exec_command = scc_exec_command,
|
||||
.read_status = scc_read_status,
|
||||
.read_altstatus = scc_read_altstatus,
|
||||
.write_devctl = scc_write_devctl,
|
||||
|
||||
.dev_select = ide_dev_select,
|
||||
.tf_load = scc_tf_load,
|
||||
.tf_read = scc_tf_read,
|
||||
|
||||
.input_data = scc_input_data,
|
||||
.output_data = scc_output_data,
|
||||
};
|
||||
|
||||
static const struct ide_port_ops scc_port_ops = {
|
||||
.set_pio_mode = scc_set_pio_mode,
|
||||
.set_dma_mode = scc_set_dma_mode,
|
||||
.udma_filter = scc_udma_filter,
|
||||
.cable_detect = scc_cable_detect,
|
||||
};
|
||||
|
||||
static const struct ide_dma_ops scc_dma_ops = {
|
||||
.dma_host_set = scc_dma_host_set,
|
||||
.dma_setup = scc_dma_setup,
|
||||
.dma_start = scc_dma_start,
|
||||
.dma_end = scc_dma_end,
|
||||
.dma_test_irq = scc_dma_test_irq,
|
||||
.dma_lost_irq = ide_dma_lost_irq,
|
||||
.dma_timer_expiry = ide_dma_sff_timer_expiry,
|
||||
.dma_sff_read_status = scc_dma_sff_read_status,
|
||||
};
|
||||
|
||||
static const struct ide_port_info scc_chipset = {
|
||||
.name = "sccIDE",
|
||||
.init_iops = init_iops_scc,
|
||||
.init_dma = scc_init_dma,
|
||||
.init_hwif = init_hwif_scc,
|
||||
.tp_ops = &scc_tp_ops,
|
||||
.port_ops = &scc_port_ops,
|
||||
.dma_ops = &scc_dma_ops,
|
||||
.host_flags = IDE_HFLAG_SINGLE,
|
||||
.irq_flags = IRQF_SHARED,
|
||||
.pio_mask = ATA_PIO4,
|
||||
.chipset = ide_pci,
|
||||
};
|
||||
|
||||
/**
|
||||
* scc_init_one - pci layer discovery entry
|
||||
* @dev: PCI device
|
||||
* @id: ident table entry
|
||||
*
|
||||
* Called by the PCI code when it finds an SCC PATA controller.
|
||||
* We then use the IDE PCI generic helper to do most of the work.
|
||||
*/
|
||||
|
||||
static int scc_init_one(struct pci_dev *dev, const struct pci_device_id *id)
|
||||
{
|
||||
return init_setup_scc(dev, &scc_chipset);
|
||||
}
|
||||
|
||||
/**
|
||||
* scc_remove - pci layer remove entry
|
||||
* @dev: PCI device
|
||||
*
|
||||
* Called by the PCI code when it removes an SCC PATA controller.
|
||||
*/
|
||||
|
||||
static void scc_remove(struct pci_dev *dev)
|
||||
{
|
||||
struct scc_ports *ports = pci_get_drvdata(dev);
|
||||
struct ide_host *host = ports->host;
|
||||
|
||||
ide_host_remove(host);
|
||||
|
||||
iounmap((void*)ports->dma);
|
||||
iounmap((void*)ports->ctl);
|
||||
pci_release_selected_regions(dev, (1 << 2) - 1);
|
||||
memset(ports, 0, sizeof(*ports));
|
||||
}
|
||||
|
||||
static const struct pci_device_id scc_pci_tbl[] = {
|
||||
{ PCI_VDEVICE(TOSHIBA_2, PCI_DEVICE_ID_TOSHIBA_SCC_ATA), 0 },
|
||||
{ 0, },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(pci, scc_pci_tbl);
|
||||
|
||||
static struct pci_driver scc_pci_driver = {
|
||||
.name = "SCC IDE",
|
||||
.id_table = scc_pci_tbl,
|
||||
.probe = scc_init_one,
|
||||
.remove = scc_remove,
|
||||
};
|
||||
|
||||
static int __init scc_ide_init(void)
|
||||
{
|
||||
return ide_pci_register_driver(&scc_pci_driver);
|
||||
}
|
||||
|
||||
static void __exit scc_ide_exit(void)
|
||||
{
|
||||
pci_unregister_driver(&scc_pci_driver);
|
||||
}
|
||||
|
||||
module_init(scc_ide_init);
|
||||
module_exit(scc_ide_exit);
|
||||
|
||||
MODULE_DESCRIPTION("PCI driver module for Toshiba SCC IDE");
|
||||
MODULE_LICENSE("GPL");
|
@ -205,6 +205,7 @@ enum {
|
||||
ATA_LFLAG_SW_ACTIVITY = (1 << 7), /* keep activity stats */
|
||||
ATA_LFLAG_NO_LPM = (1 << 8), /* disable LPM on this link */
|
||||
ATA_LFLAG_RST_ONCE = (1 << 9), /* limit recovery to one reset */
|
||||
ATA_LFLAG_CHANGED = (1 << 10), /* LPM state changed on this link */
|
||||
|
||||
/* struct ata_port flags */
|
||||
ATA_FLAG_SLAVE_POSS = (1 << 0), /* host supports slave dev */
|
||||
@ -309,6 +310,12 @@ enum {
|
||||
*/
|
||||
ATA_TMOUT_PMP_SRST_WAIT = 5000,
|
||||
|
||||
/* When the LPM policy is set to ATA_LPM_MAX_POWER, there might
|
||||
* be a spurious PHY event, so ignore the first PHY event that
|
||||
* occurs within 10s after the policy change.
|
||||
*/
|
||||
ATA_TMOUT_SPURIOUS_PHY = 10000,
|
||||
|
||||
/* ATA bus states */
|
||||
BUS_UNKNOWN = 0,
|
||||
BUS_DMA = 1,
|
||||
@ -788,6 +795,8 @@ struct ata_link {
|
||||
struct ata_eh_context eh_context;
|
||||
|
||||
struct ata_device device[ATA_MAX_DEVICES];
|
||||
|
||||
unsigned long last_lpm_change; /* when last LPM change happened */
|
||||
};
|
||||
#define ATA_LINK_CLEAR_BEGIN offsetof(struct ata_link, active_tag)
|
||||
#define ATA_LINK_CLEAR_END offsetof(struct ata_link, device[0])
|
||||
@ -1201,6 +1210,7 @@ extern struct ata_device *ata_dev_pair(struct ata_device *adev);
|
||||
extern int ata_do_set_mode(struct ata_link *link, struct ata_device **r_failed_dev);
|
||||
extern void ata_scsi_port_error_handler(struct Scsi_Host *host, struct ata_port *ap);
|
||||
extern void ata_scsi_cmd_error_handler(struct Scsi_Host *host, struct ata_port *ap, struct list_head *eh_q);
|
||||
extern bool sata_lpm_ignore_phy_events(struct ata_link *link);
|
||||
|
||||
extern int ata_cable_40wire(struct ata_port *ap);
|
||||
extern int ata_cable_80wire(struct ata_port *ap);
|
||||
|
Loading…
Reference in New Issue
Block a user