2007-02-17 04:40:22 +03:00
|
|
|
/*
|
|
|
|
* 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 <alan@redhat.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 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;
|
2008-07-23 21:55:57 +04:00
|
|
|
struct ide_host *host; /* for removing port from system */
|
2007-02-17 04:40:22 +03:00
|
|
|
} 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;
|
|
|
|
}
|
|
|
|
|
2008-07-23 21:55:51 +04:00
|
|
|
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();
|
|
|
|
}
|
|
|
|
|
2008-07-23 21:55:52 +04:00
|
|
|
static u8 scc_read_status(ide_hwif_t *hwif)
|
|
|
|
{
|
|
|
|
return (u8)in_be32((void *)hwif->io_ports.status_addr);
|
|
|
|
}
|
|
|
|
|
2008-07-23 21:55:52 +04:00
|
|
|
static u8 scc_read_altstatus(ide_hwif_t *hwif)
|
|
|
|
{
|
|
|
|
return (u8)in_be32((void *)hwif->io_ports.ctl_addr);
|
|
|
|
}
|
|
|
|
|
2008-07-23 21:55:50 +04:00
|
|
|
static u8 scc_read_sff_dma_status(ide_hwif_t *hwif)
|
|
|
|
{
|
2008-07-23 21:55:51 +04:00
|
|
|
return (u8)in_be32((void *)(hwif->dma_base + 4));
|
2008-07-23 21:55:50 +04:00
|
|
|
}
|
|
|
|
|
2008-07-23 21:55:52 +04:00
|
|
|
static void scc_set_irq(ide_hwif_t *hwif, int on)
|
|
|
|
{
|
|
|
|
u8 ctl = ATA_DEVCTL_OBS;
|
|
|
|
|
|
|
|
if (on == 4) { /* hack for SRST */
|
|
|
|
ctl |= 4;
|
|
|
|
on &= ~4;
|
|
|
|
}
|
|
|
|
|
|
|
|
ctl |= on ? 0 : 2;
|
|
|
|
|
|
|
|
out_be32((void *)hwif->io_ports.ctl_addr, ctl);
|
|
|
|
eieio();
|
|
|
|
in_be32((void *)(hwif->dma_base + 0x01c));
|
|
|
|
eieio();
|
|
|
|
}
|
|
|
|
|
2007-02-17 04:40:22 +03:00
|
|
|
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++));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
ide: move ide_config_drive_speed() calls to upper layers (take 2)
* Convert {ide_hwif_t,ide_pci_device_t}->host_flag to be u16.
* Add IDE_HFLAG_POST_SET_MODE host flag to indicate the need to program
the host for the transfer mode after programming the device. Set it
in au1xxx-ide, amd74xx, cs5530, cs5535, pdc202xx_new, sc1200, pmac
and via82cxxx host drivers.
* Add IDE_HFLAG_NO_SET_MODE host flag to indicate the need to completely
skip programming of host/device for the transfer mode ("smart" hosts).
Set it in it821x host driver and check it in ide_tune_dma().
* Add ide_set_pio_mode()/ide_set_dma_mode() helpers and convert all
direct ->set_pio_mode/->speedproc users to use these helpers.
* Move ide_config_drive_speed() calls from ->set_pio_mode/->speedproc
methods to callers.
* Rename ->speedproc method to ->set_dma_mode, make it void and update
all implementations accordingly.
* Update ide_set_xfer_rate() comments.
* Unexport ide_config_drive_speed().
v2:
* Fix issues noticed by Sergei:
- export ide_set_dma_mode() instead of moving ->set_pio_mode abuse wrt
to setting DMA modes from sc1200_set_pio_mode() to do_special()
- check IDE_HFLAG_NO_SET_MODE in ide_tune_dma()
- check for (hwif->set_pio_mode) == NULL in ide_set_pio_mode()
- check for (hwif->set_dma_mode) == NULL in ide_set_dma_mode()
- return -1 from ide_set_{pio,dma}_mode() if ->set_{pio,dma}_mode == NULL
- don't set ->set_{pio,dma}_mode on it821x in "smart" mode
- fix build problem in pmac.c
- minor fixes in au1xxx-ide.c/cs5530.c/siimage.c
- improve patch description
Changes in behavior caused by this patch:
- HDIO_SET_PIO_MODE ioctl would now return -ENOSYS for attempts to change
PIO mode if it821x controller is in "smart" mode
- removal of two debugging printk-s (from cs5530.c and sc1200.c)
- transfer modes 0x00-0x07 passed from user space may be programmed twice on
the device (not really an issue since 0x00 is not supported correctly by
any host driver ATM, 0x01 is not supported at all and 0x02-0x07 are invalid)
Acked-by: Sergei Shtylyov <sshtylyov@ru.mvista.com>
Signed-off-by: Bartlomiej Zolnierkiewicz <bzolnier@gmail.com>
2007-10-13 19:47:51 +04:00
|
|
|
* scc_set_pio_mode - set host controller for PIO mode
|
|
|
|
* @drive: drive
|
|
|
|
* @pio: PIO mode number
|
2007-02-17 04:40:22 +03:00
|
|
|
*
|
|
|
|
* Load the timing settings for this device mode into the
|
|
|
|
* controller.
|
|
|
|
*/
|
|
|
|
|
ide: move ide_config_drive_speed() calls to upper layers (take 2)
* Convert {ide_hwif_t,ide_pci_device_t}->host_flag to be u16.
* Add IDE_HFLAG_POST_SET_MODE host flag to indicate the need to program
the host for the transfer mode after programming the device. Set it
in au1xxx-ide, amd74xx, cs5530, cs5535, pdc202xx_new, sc1200, pmac
and via82cxxx host drivers.
* Add IDE_HFLAG_NO_SET_MODE host flag to indicate the need to completely
skip programming of host/device for the transfer mode ("smart" hosts).
Set it in it821x host driver and check it in ide_tune_dma().
* Add ide_set_pio_mode()/ide_set_dma_mode() helpers and convert all
direct ->set_pio_mode/->speedproc users to use these helpers.
* Move ide_config_drive_speed() calls from ->set_pio_mode/->speedproc
methods to callers.
* Rename ->speedproc method to ->set_dma_mode, make it void and update
all implementations accordingly.
* Update ide_set_xfer_rate() comments.
* Unexport ide_config_drive_speed().
v2:
* Fix issues noticed by Sergei:
- export ide_set_dma_mode() instead of moving ->set_pio_mode abuse wrt
to setting DMA modes from sc1200_set_pio_mode() to do_special()
- check IDE_HFLAG_NO_SET_MODE in ide_tune_dma()
- check for (hwif->set_pio_mode) == NULL in ide_set_pio_mode()
- check for (hwif->set_dma_mode) == NULL in ide_set_dma_mode()
- return -1 from ide_set_{pio,dma}_mode() if ->set_{pio,dma}_mode == NULL
- don't set ->set_{pio,dma}_mode on it821x in "smart" mode
- fix build problem in pmac.c
- minor fixes in au1xxx-ide.c/cs5530.c/siimage.c
- improve patch description
Changes in behavior caused by this patch:
- HDIO_SET_PIO_MODE ioctl would now return -ENOSYS for attempts to change
PIO mode if it821x controller is in "smart" mode
- removal of two debugging printk-s (from cs5530.c and sc1200.c)
- transfer modes 0x00-0x07 passed from user space may be programmed twice on
the device (not really an issue since 0x00 is not supported correctly by
any host driver ATM, 0x01 is not supported at all and 0x02-0x07 are invalid)
Acked-by: Sergei Shtylyov <sshtylyov@ru.mvista.com>
Signed-off-by: Bartlomiej Zolnierkiewicz <bzolnier@gmail.com>
2007-10-13 19:47:51 +04:00
|
|
|
static void scc_set_pio_mode(ide_drive_t *drive, const u8 pio)
|
2007-02-17 04:40:22 +03:00
|
|
|
{
|
|
|
|
ide_hwif_t *hwif = HWIF(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;
|
|
|
|
|
2007-02-17 04:40:25 +03:00
|
|
|
reg = in_be32((void __iomem *)cckctrl_port);
|
2007-02-17 04:40:22 +03:00
|
|
|
if (reg & CCKCTRL_ATACLKOEN) {
|
|
|
|
offset = 1; /* 133MHz */
|
|
|
|
} else {
|
|
|
|
offset = 0; /* 100MHz */
|
|
|
|
}
|
2007-08-02 01:46:46 +04:00
|
|
|
reg = JCHSTtbl[offset][pio] << 16 | JCHHTtbl[offset][pio];
|
2007-02-17 04:40:25 +03:00
|
|
|
out_be32((void __iomem *)piosht_port, reg);
|
2007-08-02 01:46:46 +04:00
|
|
|
reg = JCHCTtbl[offset][pio];
|
2007-02-17 04:40:25 +03:00
|
|
|
out_be32((void __iomem *)pioct_port, reg);
|
2007-08-02 01:46:46 +04:00
|
|
|
}
|
2007-02-17 04:40:22 +03:00
|
|
|
|
|
|
|
/**
|
ide: move ide_config_drive_speed() calls to upper layers (take 2)
* Convert {ide_hwif_t,ide_pci_device_t}->host_flag to be u16.
* Add IDE_HFLAG_POST_SET_MODE host flag to indicate the need to program
the host for the transfer mode after programming the device. Set it
in au1xxx-ide, amd74xx, cs5530, cs5535, pdc202xx_new, sc1200, pmac
and via82cxxx host drivers.
* Add IDE_HFLAG_NO_SET_MODE host flag to indicate the need to completely
skip programming of host/device for the transfer mode ("smart" hosts).
Set it in it821x host driver and check it in ide_tune_dma().
* Add ide_set_pio_mode()/ide_set_dma_mode() helpers and convert all
direct ->set_pio_mode/->speedproc users to use these helpers.
* Move ide_config_drive_speed() calls from ->set_pio_mode/->speedproc
methods to callers.
* Rename ->speedproc method to ->set_dma_mode, make it void and update
all implementations accordingly.
* Update ide_set_xfer_rate() comments.
* Unexport ide_config_drive_speed().
v2:
* Fix issues noticed by Sergei:
- export ide_set_dma_mode() instead of moving ->set_pio_mode abuse wrt
to setting DMA modes from sc1200_set_pio_mode() to do_special()
- check IDE_HFLAG_NO_SET_MODE in ide_tune_dma()
- check for (hwif->set_pio_mode) == NULL in ide_set_pio_mode()
- check for (hwif->set_dma_mode) == NULL in ide_set_dma_mode()
- return -1 from ide_set_{pio,dma}_mode() if ->set_{pio,dma}_mode == NULL
- don't set ->set_{pio,dma}_mode on it821x in "smart" mode
- fix build problem in pmac.c
- minor fixes in au1xxx-ide.c/cs5530.c/siimage.c
- improve patch description
Changes in behavior caused by this patch:
- HDIO_SET_PIO_MODE ioctl would now return -ENOSYS for attempts to change
PIO mode if it821x controller is in "smart" mode
- removal of two debugging printk-s (from cs5530.c and sc1200.c)
- transfer modes 0x00-0x07 passed from user space may be programmed twice on
the device (not really an issue since 0x00 is not supported correctly by
any host driver ATM, 0x01 is not supported at all and 0x02-0x07 are invalid)
Acked-by: Sergei Shtylyov <sshtylyov@ru.mvista.com>
Signed-off-by: Bartlomiej Zolnierkiewicz <bzolnier@gmail.com>
2007-10-13 19:47:51 +04:00
|
|
|
* scc_set_dma_mode - set host controller for DMA mode
|
|
|
|
* @drive: drive
|
|
|
|
* @speed: DMA mode
|
2007-02-17 04:40:22 +03:00
|
|
|
*
|
|
|
|
* Load the timing settings for this device mode into the
|
|
|
|
* controller.
|
|
|
|
*/
|
|
|
|
|
ide: move ide_config_drive_speed() calls to upper layers (take 2)
* Convert {ide_hwif_t,ide_pci_device_t}->host_flag to be u16.
* Add IDE_HFLAG_POST_SET_MODE host flag to indicate the need to program
the host for the transfer mode after programming the device. Set it
in au1xxx-ide, amd74xx, cs5530, cs5535, pdc202xx_new, sc1200, pmac
and via82cxxx host drivers.
* Add IDE_HFLAG_NO_SET_MODE host flag to indicate the need to completely
skip programming of host/device for the transfer mode ("smart" hosts).
Set it in it821x host driver and check it in ide_tune_dma().
* Add ide_set_pio_mode()/ide_set_dma_mode() helpers and convert all
direct ->set_pio_mode/->speedproc users to use these helpers.
* Move ide_config_drive_speed() calls from ->set_pio_mode/->speedproc
methods to callers.
* Rename ->speedproc method to ->set_dma_mode, make it void and update
all implementations accordingly.
* Update ide_set_xfer_rate() comments.
* Unexport ide_config_drive_speed().
v2:
* Fix issues noticed by Sergei:
- export ide_set_dma_mode() instead of moving ->set_pio_mode abuse wrt
to setting DMA modes from sc1200_set_pio_mode() to do_special()
- check IDE_HFLAG_NO_SET_MODE in ide_tune_dma()
- check for (hwif->set_pio_mode) == NULL in ide_set_pio_mode()
- check for (hwif->set_dma_mode) == NULL in ide_set_dma_mode()
- return -1 from ide_set_{pio,dma}_mode() if ->set_{pio,dma}_mode == NULL
- don't set ->set_{pio,dma}_mode on it821x in "smart" mode
- fix build problem in pmac.c
- minor fixes in au1xxx-ide.c/cs5530.c/siimage.c
- improve patch description
Changes in behavior caused by this patch:
- HDIO_SET_PIO_MODE ioctl would now return -ENOSYS for attempts to change
PIO mode if it821x controller is in "smart" mode
- removal of two debugging printk-s (from cs5530.c and sc1200.c)
- transfer modes 0x00-0x07 passed from user space may be programmed twice on
the device (not really an issue since 0x00 is not supported correctly by
any host driver ATM, 0x01 is not supported at all and 0x02-0x07 are invalid)
Acked-by: Sergei Shtylyov <sshtylyov@ru.mvista.com>
Signed-off-by: Bartlomiej Zolnierkiewicz <bzolnier@gmail.com>
2007-10-13 19:47:51 +04:00
|
|
|
static void scc_set_dma_mode(ide_drive_t *drive, const u8 speed)
|
2007-02-17 04:40:22 +03:00
|
|
|
{
|
|
|
|
ide_hwif_t *hwif = HWIF(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 = (&hwif->drives[1] == drive);
|
|
|
|
int offset, idx;
|
|
|
|
unsigned long reg;
|
|
|
|
unsigned long jcactsel;
|
|
|
|
|
2007-02-17 04:40:25 +03:00
|
|
|
reg = in_be32((void __iomem *)cckctrl_port);
|
2007-02-17 04:40:22 +03:00
|
|
|
if (reg & CCKCTRL_ATACLKOEN) {
|
|
|
|
offset = 1; /* 133MHz */
|
|
|
|
} else {
|
|
|
|
offset = 0; /* 100MHz */
|
|
|
|
}
|
|
|
|
|
2008-01-26 00:17:18 +03:00
|
|
|
idx = speed - XFER_UDMA_0;
|
2007-02-17 04:40:22 +03:00
|
|
|
|
|
|
|
jcactsel = JCACTSELtbl[offset][idx];
|
|
|
|
if (is_slave) {
|
2007-02-17 04:40:25 +03:00
|
|
|
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);
|
2007-02-17 04:40:22 +03:00
|
|
|
} else {
|
2007-02-17 04:40:25 +03:00
|
|
|
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);
|
2007-02-17 04:40:22 +03:00
|
|
|
}
|
|
|
|
reg = JCTSStbl[offset][idx] << 16 | JCENVTtbl[offset][idx];
|
2007-02-17 04:40:25 +03:00
|
|
|
out_be32((void __iomem *)udenvt_port, reg);
|
2007-02-17 04:40:22 +03:00
|
|
|
}
|
|
|
|
|
2008-04-29 01:44:41 +04:00
|
|
|
static void scc_dma_host_set(ide_drive_t *drive, int on)
|
|
|
|
{
|
|
|
|
ide_hwif_t *hwif = drive->hwif;
|
2008-10-13 23:39:40 +04:00
|
|
|
u8 unit = drive->dn & 1;
|
2008-07-23 21:55:51 +04:00
|
|
|
u8 dma_stat = scc_ide_inb(hwif->dma_base + 4);
|
2008-04-29 01:44:41 +04:00
|
|
|
|
|
|
|
if (on)
|
|
|
|
dma_stat |= (1 << (5 + unit));
|
|
|
|
else
|
|
|
|
dma_stat &= ~(1 << (5 + unit));
|
|
|
|
|
2008-07-23 21:55:51 +04:00
|
|
|
scc_ide_outb(dma_stat, hwif->dma_base + 4);
|
2008-04-29 01:44:41 +04:00
|
|
|
}
|
|
|
|
|
2007-02-17 04:40:25 +03:00
|
|
|
/**
|
|
|
|
* scc_ide_dma_setup - begin a DMA phase
|
|
|
|
* @drive: target device
|
|
|
|
*
|
|
|
|
* 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)
|
|
|
|
{
|
|
|
|
ide_hwif_t *hwif = drive->hwif;
|
|
|
|
struct request *rq = HWGROUP(drive)->rq;
|
|
|
|
unsigned int reading;
|
|
|
|
u8 dma_stat;
|
|
|
|
|
|
|
|
if (rq_data_dir(rq))
|
|
|
|
reading = 0;
|
|
|
|
else
|
|
|
|
reading = 1 << 3;
|
|
|
|
|
|
|
|
/* fall back to pio! */
|
|
|
|
if (!ide_build_dmatable(drive, rq)) {
|
|
|
|
ide_map_sg(drive, rq);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* PRD table */
|
2008-04-29 01:44:42 +04:00
|
|
|
out_be32((void __iomem *)(hwif->dma_base + 8), hwif->dmatable_dma);
|
2007-02-17 04:40:25 +03:00
|
|
|
|
|
|
|
/* specify r/w */
|
2008-07-23 21:55:51 +04:00
|
|
|
out_be32((void __iomem *)hwif->dma_base, reading);
|
2007-02-17 04:40:25 +03:00
|
|
|
|
2008-07-23 21:55:51 +04:00
|
|
|
/* read DMA status for INTR & ERROR flags */
|
|
|
|
dma_stat = in_be32((void __iomem *)(hwif->dma_base + 4));
|
2007-02-17 04:40:25 +03:00
|
|
|
|
|
|
|
/* clear INTR & ERROR flags */
|
2008-07-23 21:55:51 +04:00
|
|
|
out_be32((void __iomem *)(hwif->dma_base + 4), dma_stat | 6);
|
2007-02-17 04:40:25 +03:00
|
|
|
drive->waiting_for_dma = 1;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2008-04-29 01:44:41 +04:00
|
|
|
static void scc_dma_start(ide_drive_t *drive)
|
|
|
|
{
|
|
|
|
ide_hwif_t *hwif = drive->hwif;
|
2008-07-23 21:55:51 +04:00
|
|
|
u8 dma_cmd = scc_ide_inb(hwif->dma_base);
|
2008-04-29 01:44:41 +04:00
|
|
|
|
|
|
|
/* start DMA */
|
2008-07-23 21:55:51 +04:00
|
|
|
scc_ide_outb(dma_cmd | 1, hwif->dma_base);
|
2008-04-29 01:44:41 +04:00
|
|
|
wmb();
|
|
|
|
}
|
|
|
|
|
|
|
|
static int __scc_dma_end(ide_drive_t *drive)
|
|
|
|
{
|
|
|
|
ide_hwif_t *hwif = drive->hwif;
|
|
|
|
u8 dma_stat, dma_cmd;
|
|
|
|
|
|
|
|
drive->waiting_for_dma = 0;
|
|
|
|
/* get DMA command mode */
|
2008-07-23 21:55:51 +04:00
|
|
|
dma_cmd = scc_ide_inb(hwif->dma_base);
|
2008-04-29 01:44:41 +04:00
|
|
|
/* stop DMA */
|
2008-07-23 21:55:51 +04:00
|
|
|
scc_ide_outb(dma_cmd & ~1, hwif->dma_base);
|
2008-04-29 01:44:41 +04:00
|
|
|
/* get DMA status */
|
2008-07-23 21:55:51 +04:00
|
|
|
dma_stat = scc_ide_inb(hwif->dma_base + 4);
|
2008-04-29 01:44:41 +04:00
|
|
|
/* clear the INTR & ERROR bits */
|
2008-07-23 21:55:51 +04:00
|
|
|
scc_ide_outb(dma_stat | 6, hwif->dma_base + 4);
|
2008-04-29 01:44:41 +04:00
|
|
|
/* purge DMA mappings */
|
|
|
|
ide_destroy_dmatable(drive);
|
|
|
|
/* verify good DMA status */
|
|
|
|
wmb();
|
|
|
|
return (dma_stat & 7) != 4 ? (0x10 | dma_stat) : 0;
|
|
|
|
}
|
2007-02-17 04:40:25 +03:00
|
|
|
|
2007-02-17 04:40:22 +03:00
|
|
|
/**
|
2008-04-27 00:25:24 +04:00
|
|
|
* scc_dma_end - Stop DMA
|
2007-02-17 04:40:22 +03:00
|
|
|
* @drive: IDE drive
|
|
|
|
*
|
|
|
|
* Check and clear INT Status register.
|
2008-04-29 01:44:41 +04:00
|
|
|
* Then call __scc_dma_end().
|
2007-02-17 04:40:22 +03:00
|
|
|
*/
|
|
|
|
|
2008-04-27 00:25:24 +04:00
|
|
|
static int scc_dma_end(ide_drive_t *drive)
|
2007-02-17 04:40:22 +03:00
|
|
|
{
|
|
|
|
ide_hwif_t *hwif = HWIF(drive);
|
2008-07-23 21:55:51 +04:00
|
|
|
void __iomem *dma_base = (void __iomem *)hwif->dma_base;
|
2007-02-17 04:40:22 +03:00
|
|
|
unsigned long intsts_port = hwif->dma_base + 0x014;
|
|
|
|
u32 reg;
|
2007-07-20 03:11:53 +04:00
|
|
|
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 */
|
2008-04-27 17:38:32 +04:00
|
|
|
if (!(in_be32((void __iomem *)hwif->io_ports.ctl_addr)
|
2008-10-11 00:39:21 +04:00
|
|
|
& ATA_ERR) &&
|
2007-07-20 03:11:53 +04:00
|
|
|
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 = HWGROUP(drive)->rq;
|
|
|
|
int unit;
|
|
|
|
/* ERROR_RESET and drive->crc_count are needed
|
|
|
|
* to reduce DMA transfer mode in retry process.
|
|
|
|
*/
|
|
|
|
if (rq)
|
|
|
|
rq->errors |= ERROR_RESET;
|
|
|
|
for (unit = 0; unit < MAX_DRIVES; unit++) {
|
|
|
|
ide_drive_t *drive = &hwif->drives[unit];
|
|
|
|
drive->crc_count++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2007-02-17 04:40:22 +03:00
|
|
|
|
|
|
|
while (1) {
|
2007-02-17 04:40:25 +03:00
|
|
|
reg = in_be32((void __iomem *)intsts_port);
|
2007-02-17 04:40:22 +03:00
|
|
|
|
|
|
|
if (reg & INTSTS_SERROR) {
|
|
|
|
printk(KERN_WARNING "%s: SERROR\n", SCC_PATA_NAME);
|
2007-02-17 04:40:25 +03:00
|
|
|
out_be32((void __iomem *)intsts_port, INTSTS_SERROR|INTSTS_BMSINT);
|
2007-02-17 04:40:22 +03:00
|
|
|
|
2008-07-23 21:55:51 +04:00
|
|
|
out_be32(dma_base, in_be32(dma_base) & ~QCHCD_IOS_SS);
|
2007-02-17 04:40:22 +03:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (reg & INTSTS_PRERR) {
|
|
|
|
u32 maea0, maec0;
|
|
|
|
unsigned long ctl_base = hwif->config_data;
|
|
|
|
|
2007-02-17 04:40:25 +03:00
|
|
|
maea0 = in_be32((void __iomem *)(ctl_base + 0xF50));
|
|
|
|
maec0 = in_be32((void __iomem *)(ctl_base + 0xF54));
|
2007-02-17 04:40:22 +03:00
|
|
|
|
|
|
|
printk(KERN_WARNING "%s: PRERR [addr:%x cmd:%x]\n", SCC_PATA_NAME, maea0, maec0);
|
|
|
|
|
2007-02-17 04:40:25 +03:00
|
|
|
out_be32((void __iomem *)intsts_port, INTSTS_PRERR|INTSTS_BMSINT);
|
2007-02-17 04:40:22 +03:00
|
|
|
|
2008-07-23 21:55:51 +04:00
|
|
|
out_be32(dma_base, in_be32(dma_base) & ~QCHCD_IOS_SS);
|
2007-02-17 04:40:22 +03:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (reg & INTSTS_RERR) {
|
|
|
|
printk(KERN_WARNING "%s: Response Error\n", SCC_PATA_NAME);
|
2007-02-17 04:40:25 +03:00
|
|
|
out_be32((void __iomem *)intsts_port, INTSTS_RERR|INTSTS_BMSINT);
|
2007-02-17 04:40:22 +03:00
|
|
|
|
2008-07-23 21:55:51 +04:00
|
|
|
out_be32(dma_base, in_be32(dma_base) & ~QCHCD_IOS_SS);
|
2007-02-17 04:40:22 +03:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (reg & INTSTS_ICERR) {
|
2008-07-23 21:55:51 +04:00
|
|
|
out_be32(dma_base, in_be32(dma_base) & ~QCHCD_IOS_SS);
|
2007-02-17 04:40:22 +03:00
|
|
|
|
|
|
|
printk(KERN_WARNING "%s: Illegal Configuration\n", SCC_PATA_NAME);
|
2007-02-17 04:40:25 +03:00
|
|
|
out_be32((void __iomem *)intsts_port, INTSTS_ICERR|INTSTS_BMSINT);
|
2007-02-17 04:40:22 +03:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (reg & INTSTS_BMSINT) {
|
|
|
|
printk(KERN_WARNING "%s: Internal Bus Error\n", SCC_PATA_NAME);
|
2007-02-17 04:40:25 +03:00
|
|
|
out_be32((void __iomem *)intsts_port, INTSTS_BMSINT);
|
2007-02-17 04:40:22 +03:00
|
|
|
|
|
|
|
ide_do_reset(drive);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (reg & INTSTS_BMHE) {
|
2007-02-17 04:40:25 +03:00
|
|
|
out_be32((void __iomem *)intsts_port, INTSTS_BMHE);
|
2007-02-17 04:40:22 +03:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (reg & INTSTS_ACTEINT) {
|
2007-02-17 04:40:25 +03:00
|
|
|
out_be32((void __iomem *)intsts_port, INTSTS_ACTEINT);
|
2007-02-17 04:40:22 +03:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (reg & INTSTS_IOIRQS) {
|
2007-02-17 04:40:25 +03:00
|
|
|
out_be32((void __iomem *)intsts_port, INTSTS_IOIRQS);
|
2007-02-17 04:40:22 +03:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2008-04-29 01:44:41 +04:00
|
|
|
dma_stat = __scc_dma_end(drive);
|
2007-07-20 03:11:53 +04:00
|
|
|
if (data_loss)
|
|
|
|
dma_stat |= 2; /* emulate DMA error (to retry command) */
|
|
|
|
return dma_stat;
|
2007-02-17 04:40:22 +03:00
|
|
|
}
|
|
|
|
|
2007-03-03 19:48:55 +03:00
|
|
|
/* returns 1 if dma irq issued, 0 otherwise */
|
|
|
|
static int scc_dma_test_irq(ide_drive_t *drive)
|
|
|
|
{
|
2007-07-20 03:11:53 +04:00
|
|
|
ide_hwif_t *hwif = HWIF(drive);
|
|
|
|
u32 int_stat = in_be32((void __iomem *)hwif->dma_base + 0x014);
|
2007-03-03 19:48:55 +03:00
|
|
|
|
2007-07-20 03:11:53 +04:00
|
|
|
/* SCC errata A252,A308 workaround: Step4 */
|
2008-04-27 17:38:32 +04:00
|
|
|
if ((in_be32((void __iomem *)hwif->io_ports.ctl_addr)
|
2008-10-11 00:39:21 +04:00
|
|
|
& ATA_ERR) &&
|
2007-07-20 03:11:53 +04:00
|
|
|
(int_stat & INTSTS_INTRQ))
|
2007-03-03 19:48:55 +03:00
|
|
|
return 1;
|
|
|
|
|
2007-07-20 03:11:53 +04:00
|
|
|
/* SCC errata A308 workaround: Step5 (polling IOIRQS) */
|
|
|
|
if (int_stat & INTSTS_IOIRQS)
|
2007-03-03 19:48:55 +03:00
|
|
|
return 1;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2007-07-20 03:11:53 +04:00
|
|
|
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);
|
2007-10-19 02:30:07 +04:00
|
|
|
mask = ATA_UDMA4;
|
2007-07-20 03:11:53 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
return mask;
|
|
|
|
}
|
|
|
|
|
2007-02-17 04:40:22 +03:00
|
|
|
/**
|
|
|
|
* 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)
|
|
|
|
{
|
2007-07-26 20:36:09 +04:00
|
|
|
void __iomem *ctl_addr;
|
|
|
|
void __iomem *dma_addr;
|
2008-04-27 00:25:19 +04:00
|
|
|
int i, ret;
|
2007-02-17 04:40:22 +03:00
|
|
|
|
|
|
|
for (i = 0; i < MAX_HWIFS; i++) {
|
|
|
|
if (scc_ports[i].ctl == 0)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (i >= MAX_HWIFS)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
2008-04-27 00:25:19 +04:00
|
|
|
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;
|
2007-02-17 04:40:22 +03:00
|
|
|
}
|
|
|
|
|
2008-10-24 01:22:07 +04:00
|
|
|
ctl_addr = pci_ioremap_bar(dev, 0);
|
|
|
|
if (!ctl_addr)
|
2008-04-27 00:25:19 +04:00
|
|
|
goto fail_0;
|
2007-02-17 04:40:22 +03:00
|
|
|
|
2008-10-24 01:22:07 +04:00
|
|
|
dma_addr = pci_ioremap_bar(dev, 1);
|
|
|
|
if (!dma_addr)
|
2008-04-27 00:25:19 +04:00
|
|
|
goto fail_1;
|
2007-02-17 04:40:22 +03:00
|
|
|
|
|
|
|
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:
|
2008-04-27 00:25:19 +04:00
|
|
|
iounmap(ctl_addr);
|
2007-02-17 04:40:22 +03:00
|
|
|
fail_0:
|
|
|
|
return -ENOMEM;
|
|
|
|
}
|
|
|
|
|
2008-04-18 02:46:25 +04:00
|
|
|
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);
|
2008-07-23 21:55:57 +04:00
|
|
|
struct ide_host *host;
|
2008-07-23 21:55:50 +04:00
|
|
|
hw_regs_t hw, *hws[] = { &hw, NULL, NULL, NULL };
|
ide: add ide_host_add() helper
Add ide_host_add() helper which does ide_host_alloc()+ide_host_register(),
then convert ide_setup_pci_device[s](), ide_legacy_device_add() and some
host drivers to use it.
While at it:
* Fix ide_setup_pci_device[s](), ide_arm.c, gayle.c, ide-4drives.c,
macide.c, q40ide.c, cmd640.c and cs5520.c to return correct error value.
* -ENOENT -> -ENOMEM in rapide.c, ide-h8300.c, ide-generic.c, au1xxx-ide.c
and pmac.c
* -ENODEV -> -ENOMEM in palm_bk3710.c, ide_platform.c and delkin_cb.c
* -1 -> -ENOMEM in ide-pnp.c
Signed-off-by: Bartlomiej Zolnierkiewicz <bzolnier@gmail.com>
2008-07-23 21:55:57 +04:00
|
|
|
int i, rc;
|
2008-04-18 02:46:25 +04:00
|
|
|
|
|
|
|
memset(&hw, 0, sizeof(hw));
|
2008-04-27 17:38:32 +04:00
|
|
|
for (i = 0; i <= 8; i++)
|
|
|
|
hw.io_ports_array[i] = ports->dma + 0x20 + i * 4;
|
2008-04-18 02:46:25 +04:00
|
|
|
hw.irq = dev->irq;
|
|
|
|
hw.dev = &dev->dev;
|
|
|
|
hw.chipset = ide_pci;
|
|
|
|
|
ide: add ide_host_add() helper
Add ide_host_add() helper which does ide_host_alloc()+ide_host_register(),
then convert ide_setup_pci_device[s](), ide_legacy_device_add() and some
host drivers to use it.
While at it:
* Fix ide_setup_pci_device[s](), ide_arm.c, gayle.c, ide-4drives.c,
macide.c, q40ide.c, cmd640.c and cs5520.c to return correct error value.
* -ENOENT -> -ENOMEM in rapide.c, ide-h8300.c, ide-generic.c, au1xxx-ide.c
and pmac.c
* -ENODEV -> -ENOMEM in palm_bk3710.c, ide_platform.c and delkin_cb.c
* -1 -> -ENOMEM in ide-pnp.c
Signed-off-by: Bartlomiej Zolnierkiewicz <bzolnier@gmail.com>
2008-07-23 21:55:57 +04:00
|
|
|
rc = ide_host_add(d, hws, &host);
|
|
|
|
if (rc)
|
|
|
|
return rc;
|
2008-07-23 21:55:57 +04:00
|
|
|
|
|
|
|
ports->host = host;
|
2008-04-18 02:46:25 +04:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2007-02-17 04:40:22 +03:00
|
|
|
/**
|
|
|
|
* init_setup_scc - set up an SCC PATA Controller
|
|
|
|
* @dev: PCI device
|
2007-10-20 02:32:34 +04:00
|
|
|
* @d: IDE port info
|
2007-02-17 04:40:22 +03:00
|
|
|
*
|
|
|
|
* Perform the initial set up for this device.
|
|
|
|
*/
|
|
|
|
|
2007-10-20 02:32:34 +04:00
|
|
|
static int __devinit init_setup_scc(struct pci_dev *dev,
|
2007-10-20 02:32:34 +04:00
|
|
|
const struct ide_port_info *d)
|
2007-02-17 04:40:22 +03:00
|
|
|
{
|
|
|
|
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;
|
|
|
|
|
2008-04-18 02:46:25 +04:00
|
|
|
rc = pci_enable_device(dev);
|
|
|
|
if (rc)
|
|
|
|
goto end;
|
|
|
|
|
2007-02-17 04:40:22 +03:00
|
|
|
rc = setup_mmio_scc(dev, d->name);
|
2008-04-18 02:46:25 +04:00
|
|
|
if (rc < 0)
|
|
|
|
goto end;
|
2007-02-17 04:40:22 +03:00
|
|
|
|
|
|
|
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);
|
|
|
|
|
2008-04-18 02:46:25 +04:00
|
|
|
rc = scc_ide_setup_pci_device(dev, d);
|
|
|
|
|
|
|
|
end:
|
|
|
|
return rc;
|
2007-02-17 04:40:22 +03:00
|
|
|
}
|
|
|
|
|
2008-04-29 01:44:40 +04:00
|
|
|
static void scc_tf_load(ide_drive_t *drive, ide_task_t *task)
|
|
|
|
{
|
|
|
|
struct ide_io_ports *io_ports = &drive->hwif->io_ports;
|
|
|
|
struct ide_taskfile *tf = &task->tf;
|
|
|
|
u8 HIHI = (task->tf_flags & IDE_TFLAG_LBA48) ? 0xE0 : 0xEF;
|
|
|
|
|
|
|
|
if (task->tf_flags & IDE_TFLAG_FLAGGED)
|
|
|
|
HIHI = 0xFF;
|
|
|
|
|
|
|
|
if (task->tf_flags & IDE_TFLAG_OUT_DATA)
|
2008-04-29 01:44:41 +04:00
|
|
|
out_be32((void *)io_ports->data_addr,
|
|
|
|
(tf->hob_data << 8) | tf->data);
|
2008-04-29 01:44:40 +04:00
|
|
|
|
|
|
|
if (task->tf_flags & IDE_TFLAG_OUT_HOB_FEATURE)
|
|
|
|
scc_ide_outb(tf->hob_feature, io_ports->feature_addr);
|
|
|
|
if (task->tf_flags & IDE_TFLAG_OUT_HOB_NSECT)
|
|
|
|
scc_ide_outb(tf->hob_nsect, io_ports->nsect_addr);
|
|
|
|
if (task->tf_flags & IDE_TFLAG_OUT_HOB_LBAL)
|
|
|
|
scc_ide_outb(tf->hob_lbal, io_ports->lbal_addr);
|
|
|
|
if (task->tf_flags & IDE_TFLAG_OUT_HOB_LBAM)
|
|
|
|
scc_ide_outb(tf->hob_lbam, io_ports->lbam_addr);
|
|
|
|
if (task->tf_flags & IDE_TFLAG_OUT_HOB_LBAH)
|
|
|
|
scc_ide_outb(tf->hob_lbah, io_ports->lbah_addr);
|
|
|
|
|
|
|
|
if (task->tf_flags & IDE_TFLAG_OUT_FEATURE)
|
|
|
|
scc_ide_outb(tf->feature, io_ports->feature_addr);
|
|
|
|
if (task->tf_flags & IDE_TFLAG_OUT_NSECT)
|
|
|
|
scc_ide_outb(tf->nsect, io_ports->nsect_addr);
|
|
|
|
if (task->tf_flags & IDE_TFLAG_OUT_LBAL)
|
|
|
|
scc_ide_outb(tf->lbal, io_ports->lbal_addr);
|
|
|
|
if (task->tf_flags & IDE_TFLAG_OUT_LBAM)
|
|
|
|
scc_ide_outb(tf->lbam, io_ports->lbam_addr);
|
|
|
|
if (task->tf_flags & IDE_TFLAG_OUT_LBAH)
|
|
|
|
scc_ide_outb(tf->lbah, io_ports->lbah_addr);
|
|
|
|
|
|
|
|
if (task->tf_flags & IDE_TFLAG_OUT_DEVICE)
|
2008-10-13 23:39:40 +04:00
|
|
|
scc_ide_outb((tf->device & HIHI) | drive->select,
|
2008-04-29 01:44:40 +04:00
|
|
|
io_ports->device_addr);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void scc_tf_read(ide_drive_t *drive, ide_task_t *task)
|
|
|
|
{
|
|
|
|
struct ide_io_ports *io_ports = &drive->hwif->io_ports;
|
|
|
|
struct ide_taskfile *tf = &task->tf;
|
|
|
|
|
|
|
|
if (task->tf_flags & IDE_TFLAG_IN_DATA) {
|
2008-04-29 01:44:41 +04:00
|
|
|
u16 data = (u16)in_be32((void *)io_ports->data_addr);
|
2008-04-29 01:44:40 +04:00
|
|
|
|
|
|
|
tf->data = data & 0xff;
|
|
|
|
tf->hob_data = (data >> 8) & 0xff;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* be sure we're looking at the low order bits */
|
2008-07-15 23:21:50 +04:00
|
|
|
scc_ide_outb(ATA_DEVCTL_OBS & ~0x80, io_ports->ctl_addr);
|
2008-04-29 01:44:40 +04:00
|
|
|
|
2008-07-23 21:55:53 +04:00
|
|
|
if (task->tf_flags & IDE_TFLAG_IN_FEATURE)
|
|
|
|
tf->feature = scc_ide_inb(io_ports->feature_addr);
|
2008-04-29 01:44:40 +04:00
|
|
|
if (task->tf_flags & IDE_TFLAG_IN_NSECT)
|
|
|
|
tf->nsect = scc_ide_inb(io_ports->nsect_addr);
|
|
|
|
if (task->tf_flags & IDE_TFLAG_IN_LBAL)
|
|
|
|
tf->lbal = scc_ide_inb(io_ports->lbal_addr);
|
|
|
|
if (task->tf_flags & IDE_TFLAG_IN_LBAM)
|
|
|
|
tf->lbam = scc_ide_inb(io_ports->lbam_addr);
|
|
|
|
if (task->tf_flags & IDE_TFLAG_IN_LBAH)
|
|
|
|
tf->lbah = scc_ide_inb(io_ports->lbah_addr);
|
|
|
|
if (task->tf_flags & IDE_TFLAG_IN_DEVICE)
|
|
|
|
tf->device = scc_ide_inb(io_ports->device_addr);
|
|
|
|
|
|
|
|
if (task->tf_flags & IDE_TFLAG_LBA48) {
|
2008-07-15 23:21:50 +04:00
|
|
|
scc_ide_outb(ATA_DEVCTL_OBS | 0x80, io_ports->ctl_addr);
|
2008-04-29 01:44:40 +04:00
|
|
|
|
|
|
|
if (task->tf_flags & IDE_TFLAG_IN_HOB_FEATURE)
|
|
|
|
tf->hob_feature = scc_ide_inb(io_ports->feature_addr);
|
|
|
|
if (task->tf_flags & IDE_TFLAG_IN_HOB_NSECT)
|
|
|
|
tf->hob_nsect = scc_ide_inb(io_ports->nsect_addr);
|
|
|
|
if (task->tf_flags & IDE_TFLAG_IN_HOB_LBAL)
|
|
|
|
tf->hob_lbal = scc_ide_inb(io_ports->lbal_addr);
|
|
|
|
if (task->tf_flags & IDE_TFLAG_IN_HOB_LBAM)
|
|
|
|
tf->hob_lbam = scc_ide_inb(io_ports->lbam_addr);
|
|
|
|
if (task->tf_flags & IDE_TFLAG_IN_HOB_LBAH)
|
|
|
|
tf->hob_lbah = scc_ide_inb(io_ports->lbah_addr);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-04-29 01:44:36 +04:00
|
|
|
static void scc_input_data(ide_drive_t *drive, struct request *rq,
|
|
|
|
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 request *rq,
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2007-02-17 04:40:22 +03:00
|
|
|
/**
|
|
|
|
* init_mmio_iops_scc - set up the iops for MMIO
|
|
|
|
* @hwif: interface to set up
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
|
|
|
static void __devinit init_mmio_iops_scc(ide_hwif_t *hwif)
|
|
|
|
{
|
2008-02-02 01:09:31 +03:00
|
|
|
struct pci_dev *dev = to_pci_dev(hwif->dev);
|
2007-02-17 04:40:22 +03:00
|
|
|
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 __devinit init_iops_scc(ide_hwif_t *hwif)
|
|
|
|
{
|
2008-02-02 01:09:31 +03:00
|
|
|
struct pci_dev *dev = to_pci_dev(hwif->dev);
|
|
|
|
|
2007-02-17 04:40:22 +03:00
|
|
|
hwif->hwif_data = NULL;
|
|
|
|
if (pci_get_drvdata(dev) == NULL)
|
|
|
|
return;
|
|
|
|
init_mmio_iops_scc(hwif);
|
|
|
|
}
|
|
|
|
|
2008-10-13 23:39:47 +04:00
|
|
|
static int __devinit scc_init_dma(ide_hwif_t *hwif,
|
|
|
|
const struct ide_port_info *d)
|
|
|
|
{
|
|
|
|
return ide_allocate_dma_engine(hwif);
|
|
|
|
}
|
|
|
|
|
2008-08-05 20:17:04 +04:00
|
|
|
static u8 scc_cable_detect(ide_hwif_t *hwif)
|
2008-02-02 21:56:29 +03:00
|
|
|
{
|
|
|
|
return ATA_CBL_PATA80;
|
|
|
|
}
|
|
|
|
|
2007-02-17 04:40:22 +03:00
|
|
|
/**
|
|
|
|
* 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 __devinit init_hwif_scc(ide_hwif_t *hwif)
|
|
|
|
{
|
2007-02-17 04:40:25 +03:00
|
|
|
/* PTERADD */
|
|
|
|
out_be32((void __iomem *)(hwif->dma_base + 0x018), hwif->dmatable_dma);
|
2007-02-17 04:40:22 +03:00
|
|
|
|
2007-10-19 02:30:07 +04:00
|
|
|
if (in_be32((void __iomem *)(hwif->config_data + 0xff0)) & CCKCTRL_ATACLKOEN)
|
|
|
|
hwif->ultra_mask = ATA_UDMA6; /* 133MHz */
|
|
|
|
else
|
|
|
|
hwif->ultra_mask = ATA_UDMA5; /* 100MHz */
|
2007-02-17 04:40:22 +03:00
|
|
|
}
|
|
|
|
|
2008-07-23 21:55:56 +04:00
|
|
|
static const struct ide_tp_ops scc_tp_ops = {
|
|
|
|
.exec_command = scc_exec_command,
|
|
|
|
.read_status = scc_read_status,
|
|
|
|
.read_altstatus = scc_read_altstatus,
|
|
|
|
.read_sff_dma_status = scc_read_sff_dma_status,
|
|
|
|
|
|
|
|
.set_irq = scc_set_irq,
|
|
|
|
|
|
|
|
.tf_load = scc_tf_load,
|
|
|
|
.tf_read = scc_tf_read,
|
|
|
|
|
|
|
|
.input_data = scc_input_data,
|
|
|
|
.output_data = scc_output_data,
|
|
|
|
};
|
|
|
|
|
2008-04-27 00:25:14 +04:00
|
|
|
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,
|
|
|
|
};
|
|
|
|
|
2008-04-27 00:25:24 +04:00
|
|
|
static const struct ide_dma_ops scc_dma_ops = {
|
2008-04-29 01:44:41 +04:00
|
|
|
.dma_host_set = scc_dma_host_set,
|
2008-04-27 00:25:24 +04:00
|
|
|
.dma_setup = scc_dma_setup,
|
2008-04-27 00:25:24 +04:00
|
|
|
.dma_exec_cmd = ide_dma_exec_cmd,
|
2008-04-29 01:44:41 +04:00
|
|
|
.dma_start = scc_dma_start,
|
2008-04-27 00:25:24 +04:00
|
|
|
.dma_end = scc_dma_end,
|
|
|
|
.dma_test_irq = scc_dma_test_irq,
|
2008-04-27 00:25:24 +04:00
|
|
|
.dma_lost_irq = ide_dma_lost_irq,
|
|
|
|
.dma_timeout = ide_dma_timeout,
|
2008-04-27 00:25:24 +04:00
|
|
|
};
|
|
|
|
|
2007-02-17 04:40:22 +03:00
|
|
|
#define DECLARE_SCC_DEV(name_str) \
|
|
|
|
{ \
|
|
|
|
.name = name_str, \
|
|
|
|
.init_iops = init_iops_scc, \
|
2008-10-13 23:39:47 +04:00
|
|
|
.init_dma = scc_init_dma, \
|
2007-02-17 04:40:22 +03:00
|
|
|
.init_hwif = init_hwif_scc, \
|
2008-07-23 21:55:56 +04:00
|
|
|
.tp_ops = &scc_tp_ops, \
|
2008-04-27 00:25:14 +04:00
|
|
|
.port_ops = &scc_port_ops, \
|
2008-04-27 00:25:24 +04:00
|
|
|
.dma_ops = &scc_dma_ops, \
|
2008-04-26 19:36:35 +04:00
|
|
|
.host_flags = IDE_HFLAG_SINGLE, \
|
2007-07-20 03:11:59 +04:00
|
|
|
.pio_mask = ATA_PIO4, \
|
2007-02-17 04:40:22 +03:00
|
|
|
}
|
|
|
|
|
2007-10-20 02:32:34 +04:00
|
|
|
static const struct ide_port_info scc_chipsets[] __devinitdata = {
|
2007-02-17 04:40:22 +03:00
|
|
|
/* 0 */ DECLARE_SCC_DEV("sccIDE"),
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 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 __devinit scc_init_one(struct pci_dev *dev, const struct pci_device_id *id)
|
|
|
|
{
|
2007-10-20 02:32:34 +04:00
|
|
|
return init_setup_scc(dev, &scc_chipsets[id->driver_data]);
|
2007-02-17 04:40:22 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* scc_remove - pci layer remove entry
|
|
|
|
* @dev: PCI device
|
|
|
|
*
|
|
|
|
* Called by the PCI code when it removes an SCC PATA controller.
|
|
|
|
*/
|
|
|
|
|
|
|
|
static void __devexit scc_remove(struct pci_dev *dev)
|
|
|
|
{
|
|
|
|
struct scc_ports *ports = pci_get_drvdata(dev);
|
2008-07-23 21:55:57 +04:00
|
|
|
struct ide_host *host = ports->host;
|
2007-02-17 04:40:22 +03:00
|
|
|
|
2008-07-23 21:55:57 +04:00
|
|
|
ide_host_remove(host);
|
2007-02-17 04:40:22 +03:00
|
|
|
|
|
|
|
iounmap((void*)ports->dma);
|
|
|
|
iounmap((void*)ports->ctl);
|
2008-04-27 00:25:19 +04:00
|
|
|
pci_release_selected_regions(dev, (1 << 2) - 1);
|
2007-02-17 04:40:22 +03:00
|
|
|
memset(ports, 0, sizeof(*ports));
|
|
|
|
}
|
|
|
|
|
2007-10-17 00:29:56 +04:00
|
|
|
static const struct pci_device_id scc_pci_tbl[] = {
|
|
|
|
{ PCI_VDEVICE(TOSHIBA_2, PCI_DEVICE_ID_TOSHIBA_SCC_ATA), 0 },
|
2007-02-17 04:40:22 +03:00
|
|
|
{ 0, },
|
|
|
|
};
|
|
|
|
MODULE_DEVICE_TABLE(pci, scc_pci_tbl);
|
|
|
|
|
2008-10-13 23:39:41 +04:00
|
|
|
static struct pci_driver scc_pci_driver = {
|
2007-02-17 04:40:22 +03:00
|
|
|
.name = "SCC IDE",
|
|
|
|
.id_table = scc_pci_tbl,
|
|
|
|
.probe = scc_init_one,
|
2008-08-18 23:40:03 +04:00
|
|
|
.remove = __devexit_p(scc_remove),
|
2007-02-17 04:40:22 +03:00
|
|
|
};
|
|
|
|
|
|
|
|
static int scc_ide_init(void)
|
|
|
|
{
|
2008-10-13 23:39:41 +04:00
|
|
|
return ide_pci_register_driver(&scc_pci_driver);
|
2007-02-17 04:40:22 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
module_init(scc_ide_init);
|
|
|
|
/* -- No exit code?
|
|
|
|
static void scc_ide_exit(void)
|
|
|
|
{
|
2008-10-13 23:39:41 +04:00
|
|
|
ide_pci_unregister_driver(&scc_pci_driver);
|
2007-02-17 04:40:22 +03:00
|
|
|
}
|
|
|
|
module_exit(scc_ide_exit);
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
MODULE_DESCRIPTION("PCI driver module for Toshiba SCC IDE");
|
|
|
|
MODULE_LICENSE("GPL");
|