1498 lines
42 KiB
C
1498 lines
42 KiB
C
/*
|
|
* sata_stm.c - STMicroelectronics SATA
|
|
*
|
|
* Copyright 2005 STMicroelectronics Ltd.
|
|
*
|
|
* The contents of this file are subject to the Open
|
|
* Software License version 1.1 that can be found at
|
|
* http://www.opensource.org/licenses/osl-1.1.txt and is included herein
|
|
* by reference.
|
|
*
|
|
* Alternatively, the contents of this file may be used under the terms
|
|
* of the GNU General Public License version 2 (the "GPL") as distributed
|
|
* in the kernel source COPYING file, in which case the provisions of
|
|
* the GPL are applicable instead of the above. If you wish to allow
|
|
* the use of your version of this file only under the terms of the
|
|
* GPL and not to allow others to use your version of this file under
|
|
* the OSL, indicate your decision by deleting the provisions above and
|
|
* replace them with the notice and other provisions required by the GPL.
|
|
* If you do not delete the provisions above, a recipient may use your
|
|
* version of this file under either the OSL or the GPL.
|
|
*/
|
|
|
|
#include <linux/kernel.h>
|
|
#include <linux/module.h>
|
|
#include <linux/init.h>
|
|
#include <linux/blkdev.h>
|
|
#include <linux/delay.h>
|
|
#include <linux/interrupt.h>
|
|
#include <linux/list.h>
|
|
#include <linux/platform_device.h>
|
|
#include <linux/pm_runtime.h>
|
|
#include <scsi/scsi.h>
|
|
#include <scsi/scsi_host.h>
|
|
#include <scsi/scsi_device.h>
|
|
#include <scsi/scsi_cmnd.h>
|
|
#include <scsi/scsi_transport.h>
|
|
#include <linux/libata.h>
|
|
#include <linux/stm/platform.h>
|
|
#include <linux/stm/device.h>
|
|
#include <linux/stm/miphy.h>
|
|
|
|
#define DRV_NAME "sata-stm"
|
|
#define DRV_VERSION "0.8"
|
|
|
|
/* Offsets of the component blocks */
|
|
#define SATA_AHB2STBUS_BASE 0x00000000
|
|
#define SATA_AHBDMA_BASE 0x00000400
|
|
#define SATA_AHBHOST_BASE 0x00000800
|
|
|
|
/* AHB_STBus protocol converter */
|
|
#define SATA_AHB2STBUS_STBUS_OPC (SATA_AHB2STBUS_BASE + 0x0000)
|
|
#define SATA_AHB2STBUS_MESSAGE_SIZE_CONFIG (SATA_AHB2STBUS_BASE + 0x0004)
|
|
#define SATA_AHB2STBUS_CHUNK_SIZE_CONFIG (SATA_AHB2STBUS_BASE + 0x0008)
|
|
#define SATA_AHB2STBUS_SW_RESET (SATA_AHB2STBUS_BASE + 0x000c)
|
|
#define SATA_AHB2STBUS_PC_STATUS (SATA_AHB2STBUS_BASE + 0x0010)
|
|
#define SATA_PC_GLUE_LOGIC (SATA_AHB2STBUS_BASE + 0x0014)
|
|
#define SATA_PC_GLUE_LOGICH (SATA_AHB2STBUS_BASE + 0x0018)
|
|
|
|
/* AHB DMA controller */
|
|
#define DMAC_SAR0 (SATA_AHBDMA_BASE + 0x00000000)
|
|
#define DMAC_DAR0 (SATA_AHBDMA_BASE + 0x00000008)
|
|
#define DMAC_LLP0 (SATA_AHBDMA_BASE + 0x00000010)
|
|
#define DMAC_CTL0_0 (SATA_AHBDMA_BASE + 0x00000018)
|
|
#define DMAC_CTL0_1 (SATA_AHBDMA_BASE + 0x0000001c)
|
|
#define DMAC_SSTAT0 (SATA_AHBDMA_BASE + 0x00000020)
|
|
#define DMAC_DSTAT0 (SATA_AHBDMA_BASE + 0x00000028)
|
|
#define DMAC_SSTATAR0 (SATA_AHBDMA_BASE + 0x00000030)
|
|
#define DMAC_DSTATAR0 (SATA_AHBDMA_BASE + 0x00000038)
|
|
#define DMAC_CFG0_0 (SATA_AHBDMA_BASE + 0x00000040)
|
|
#define DMAC_CFG0_1 (SATA_AHBDMA_BASE + 0x00000044)
|
|
#define DMAC_SGR0 (SATA_AHBDMA_BASE + 0x00000048)
|
|
#define DMAC_DSR0 (SATA_AHBDMA_BASE + 0x00000050)
|
|
#define DMAC_RAWTFR (SATA_AHBDMA_BASE + 0x000002c0)
|
|
#define DMAC_RAWBLOCK (SATA_AHBDMA_BASE + 0x000002c8)
|
|
#define DMAC_RAWSRCTRAN (SATA_AHBDMA_BASE + 0x000002d0)
|
|
#define DMAC_RAWDSTTRAN (SATA_AHBDMA_BASE + 0x000002d8)
|
|
#define DMAC_RAWERR (SATA_AHBDMA_BASE + 0x000002e0)
|
|
#define DMAC_STATUSTFR (SATA_AHBDMA_BASE + 0x000002e8)
|
|
#define DMAC_STATUSBLOCK (SATA_AHBDMA_BASE + 0x000002f0)
|
|
#define DMAC_STATUSSRCTRAN (SATA_AHBDMA_BASE + 0x000002f8)
|
|
#define DMAC_STATUSDSTTRAN (SATA_AHBDMA_BASE + 0x00000300)
|
|
#define DMAC_STATUSERR (SATA_AHBDMA_BASE + 0x00000308)
|
|
#define DMAC_MASKTFR (SATA_AHBDMA_BASE + 0x00000310)
|
|
#define DMAC_MASKBLOCK (SATA_AHBDMA_BASE + 0x00000318)
|
|
#define DMAC_MASKSRCTRAN (SATA_AHBDMA_BASE + 0x00000320)
|
|
#define DMAC_MASKDSTTRAN (SATA_AHBDMA_BASE + 0x00000328)
|
|
#define DMAC_MASKERR (SATA_AHBDMA_BASE + 0x00000330)
|
|
#define DMAC_CLEARTFR (SATA_AHBDMA_BASE + 0x00000338)
|
|
#define DMAC_CLEARBLOCK (SATA_AHBDMA_BASE + 0x00000340)
|
|
#define DMAC_CLEARSRCTRAN (SATA_AHBDMA_BASE + 0x00000348)
|
|
#define DMAC_CLEARDSTTRAN (SATA_AHBDMA_BASE + 0x00000350)
|
|
#define DMAC_CLEARERR (SATA_AHBDMA_BASE + 0x00000358)
|
|
#define DMAC_STATUSINT (SATA_AHBDMA_BASE + 0x00000360)
|
|
#define DMAC_REQSRCREG (SATA_AHBDMA_BASE + 0x00000368)
|
|
#define DMAC_REQDSTREG (SATA_AHBDMA_BASE + 0x00000370)
|
|
#define DMAC_SGLREQSRCREG (SATA_AHBDMA_BASE + 0x00000378)
|
|
#define DMAC_SGLREQDSTREG (SATA_AHBDMA_BASE + 0x00000380)
|
|
#define DMAC_LSTSRCREG (SATA_AHBDMA_BASE + 0x00000388)
|
|
#define DMAC_LSTDSTREG (SATA_AHBDMA_BASE + 0x00000390)
|
|
#define DMAC_DmaCfgReg (SATA_AHBDMA_BASE + 0x00000398)
|
|
#define DMAC_ChEnReg (SATA_AHBDMA_BASE + 0x000003a0)
|
|
#define DMAC_DMAIDREG (SATA_AHBDMA_BASE + 0x000003a8)
|
|
#define DMAC_DMATESTREG (SATA_AHBDMA_BASE + 0x000003b0)
|
|
#define DMAC_DMA_COMP_VERSION (SATA_AHBDMA_BASE + 0x000003b8)
|
|
#define DMAC_COMP_PARAMS_2 (SATA_AHBDMA_BASE + 0x000003e8)
|
|
#define DMAC_COMP_TYPE (SATA_AHBDMA_BASE + 0x000003f8)
|
|
#define DMAC_COMP_VERSION (SATA_AHBDMA_BASE + 0x000003fc)
|
|
|
|
#define DMAC_CTL_INT_EN (1<<0)
|
|
#define DMAC_CTL_DST_TR_WIDTH_32 (2<<1)
|
|
#define DMAC_CTL_SRC_TR_WIDTH_32 (2<<4)
|
|
#define DMAC_CTL_DINC_INC (0<<7)
|
|
#define DMAC_CTL_DINC_DEC (1<<7)
|
|
#define DMAC_CTL_DINC_NC (2<<7)
|
|
#define DMAC_CTL_SINC_INC (0<<9)
|
|
#define DMAC_CTL_SINC_DEC (1<<9)
|
|
#define DMAC_CTL_SINC_NC (2<<9)
|
|
#define DMAC_CTL_DEST_MSIZE_1 (0<<11)
|
|
#define DMAC_CTL_DEST_MSIZE_4 (1<<11)
|
|
#define DMAC_CTL_DEST_MSIZE_8 (2<<11)
|
|
#define DMAC_CTL_DEST_MSIZE_16 (3<<11)
|
|
#define DMAC_CTL_SRC_MSIZE_1 (0<<14)
|
|
#define DMAC_CTL_SRC_MSIZE_4 (1<<14)
|
|
#define DMAC_CTL_SRC_MSIZE_8 (2<<14)
|
|
#define DMAC_CTL_SRC_MSIZE_16 (3<<14)
|
|
#define DMAC_CTL_SRC_GATHER_EN (1<<17)
|
|
#define DMAC_CTL_DST_SCATTER_EN (1<<18)
|
|
#define DMAC_CTL_TT_FC_M2M_DMAC (0<<20) /* memory to memory | DMAC */
|
|
#define DMAC_CTL_TT_FC_M2P_DMAC (1<<20) /* memory to periph | DMAC */
|
|
#define DMAC_CTL_TT_FC_P2M_DMAC (2<<20) /* periph to memory | DMAC */
|
|
#define DMAC_CTL_TT_FC_P2P_DMAC (3<<20) /* periph to periph | DMAC */
|
|
#define DMAC_CTL_TT_FC_P2M_PER (4<<20) /* periph to memory | periph */
|
|
#define DMAC_CTL_TT_FC_P2P_SPER (5<<20) /* periph to periph | src periph */
|
|
#define DMAC_CTL_TT_FC_M2P_PER (6<<20) /* memory to periph | periph */
|
|
#define DMAC_CTL_TT_FC_P2P_DPER (7<<20) /* periph to periph | dest periph */
|
|
#define DMAC_CTL_DMS_1 (0<<23)
|
|
#define DMAC_CTL_DMS_2 (1<<23)
|
|
#define DMAC_CTL_SMS_1 (0<<25)
|
|
#define DMAC_CTL_SMS_2 (1<<25)
|
|
#define DMAC_CTL_LLP_DST_EN (1<<27)
|
|
#define DMAC_CTL_LLP_SRC_EN (1<<28)
|
|
|
|
#define DMAC_CTL_BLOCK_TS (1<<32)
|
|
#define DMAC_CTL_DONE (1<<44)
|
|
|
|
#define DMA_CFG_CH_PRIOR_0 (0<<5)
|
|
#define DMA_CFG_CH_SUSP (1<<8)
|
|
#define DMA_CFG_FIFO_EMPTY (1<<9)
|
|
#define DMA_CFG_HS_SEL_DST_HW (0<<10)
|
|
#define DMA_CFG_HS_SEL_DST_SW (1<<10)
|
|
#define DMA_CFG_HS_SEL_SRC_HW (0<<11)
|
|
#define DMA_CFG_HS_SEL_SRC_SW (1<<11)
|
|
/*efine DMA_CFG_LOCK_CH_L (1<<12) not in config */
|
|
/*efine DMA_CFG_LOCK_B_L (1<<14) not in config */
|
|
/*efine DMA_CFG_LOCK_CH (1<<16) not in config */
|
|
/*efine DMA_CFG_LOCK_B (1<<17) not in config */
|
|
#define DMA_CFG_DST_HS_POL_HI (0<<18)
|
|
#define DMA_CFG_DST_HS_POL_LO (1<<18)
|
|
#define DMA_CFG_SRC_HS_POL_HI (0<<19)
|
|
#define DMA_CFG_SRC_HS_POL_LO (1<<19)
|
|
#define DMA_CFG_MAX_ABRST_UNLIMITED (0<<20)
|
|
#define DMA_CFG_RELOAD_SRC (1<<30)
|
|
#define DMA_CFG_RELOAD_DST (1<<31)
|
|
|
|
#define DMA_CFG_1_FCMODE (1<<(32-32))
|
|
#define DMA_CFG_1_FIFO_MODE (1<<(33-32))
|
|
#define DMA_CFG_1_PROTCTL (1<<(34-32))
|
|
#define DMA_CFG_1_DS_UPD_EN (1<<(37-32))
|
|
/*efine DMA_CFG_1_SS_UPD_EN (1<<(38-32)) not in config */
|
|
#define DMA_CFG_1_SRC_PER_0 (0<<(39-32))
|
|
#define DMA_CFG_1_SRC_PER_1 (1<<(39-32))
|
|
#define DMA_CFG_1_DEST_PER_0 (0<<(43-32))
|
|
#define DMA_CFG_1_DEST_PER_1 (1<<(43-32))
|
|
|
|
#define DMAC_DmaCfgReg_DMA_EN (1<<0)
|
|
|
|
#define DMAC_COMP_PARAMS_2_CH0_HC_LLP (1<<13)
|
|
|
|
/* AHB host controller */
|
|
#define SATA_CDR0 (SATA_AHBHOST_BASE + 0x00000000)
|
|
#define SATA_CDR1 (SATA_AHBHOST_BASE + 0x00000004)
|
|
#define SATA_CDR2 (SATA_AHBHOST_BASE + 0x00000008)
|
|
#define SATA_CDR3 (SATA_AHBHOST_BASE + 0x0000000c)
|
|
#define SATA_CDR4 (SATA_AHBHOST_BASE + 0x00000010)
|
|
#define SATA_CDR5 (SATA_AHBHOST_BASE + 0x00000014)
|
|
#define SATA_CDR6 (SATA_AHBHOST_BASE + 0x00000018)
|
|
#define SATA_CDR7 (SATA_AHBHOST_BASE + 0x0000001c)
|
|
#define SATA_CLR0 (SATA_AHBHOST_BASE + 0x00000020)
|
|
#define SATA_SCR0 (SATA_AHBHOST_BASE + 0x00000024)
|
|
#define SATA_SCR1 (SATA_AHBHOST_BASE + 0x00000028)
|
|
#define SATA_SCR2 (SATA_AHBHOST_BASE + 0x0000002c)
|
|
#define SATA_SCR3 (SATA_AHBHOST_BASE + 0x00000030)
|
|
#define SATA_SCR4 (SATA_AHBHOST_BASE + 0x00000034)
|
|
#define SATA_FPTAGR (SATA_AHBHOST_BASE + 0x00000064)
|
|
#define SATA_FPBOR (SATA_AHBHOST_BASE + 0x00000068)
|
|
#define SATA_FPTCR (SATA_AHBHOST_BASE + 0x0000006c)
|
|
#define SATA_DMACR (SATA_AHBHOST_BASE + 0x00000070)
|
|
#define SATA_DBTSR (SATA_AHBHOST_BASE + 0x00000074)
|
|
#define SATA_INTPR (SATA_AHBHOST_BASE + 0x00000078)
|
|
#define SATA_INTMR (SATA_AHBHOST_BASE + 0x0000007c)
|
|
#define SATA_ERRMR (SATA_AHBHOST_BASE + 0x00000080)
|
|
#define SATA_LLCR (SATA_AHBHOST_BASE + 0x00000084)
|
|
#define SATA_PHYCR (SATA_AHBHOST_BASE + 0x00000088)
|
|
#define SATA_PHYSR (SATA_AHBHOST_BASE + 0x0000008c)
|
|
#define SATA_RXBISTPD (SATA_AHBHOST_BASE + 0x00000090)
|
|
#define SATA_RXBISTD1 (SATA_AHBHOST_BASE + 0x00000094)
|
|
#define SATA_RXBISTD2 (SATA_AHBHOST_BASE + 0x00000098)
|
|
#define SATA_TXBISTPD (SATA_AHBHOST_BASE + 0x0000009c)
|
|
#define SATA_TXBISTD1 (SATA_AHBHOST_BASE + 0x000000a0)
|
|
#define SATA_TXBISTD2 (SATA_AHBHOST_BASE + 0x000000a4)
|
|
#define SATA_BISTCR (SATA_AHBHOST_BASE + 0x000000a8)
|
|
#define SATA_BISTFCTR (SATA_AHBHOST_BASE + 0x000000ac)
|
|
#define SATA_BISTSR (SATA_AHBHOST_BASE + 0x000000b0)
|
|
#define SATA_BISTDECR (SATA_AHBHOST_BASE + 0x000000b4)
|
|
#define SATA_OOBR (SATA_AHBHOST_BASE + 0x000000bc)
|
|
#define SATA_TESTR (SATA_AHBHOST_BASE + 0x000000f4)
|
|
#define SATA_VERSIONR (SATA_AHBHOST_BASE + 0x000000f8)
|
|
#define SATA_IDR (SATA_AHBHOST_BASE + 0x000000fc)
|
|
|
|
|
|
#define SATA_DMACR_TXCHEN (1<<0)
|
|
#define SATA_DMACR_RXCHEN (1<<1)
|
|
|
|
/* Bit values for SATA_INTPR and SATA_INTMR */
|
|
#define SATA_INT_DMAT (1<<0)
|
|
#define SATA_INT_NEWFP (1<<1)
|
|
#define SATA_INT_PMABORT (1<<2)
|
|
#define SATA_INT_ERR (1<<3)
|
|
#define SATA_INT_NEWBIST (1<<4)
|
|
|
|
#define SERROR_ERR_T (1<<8)
|
|
#define SERROR_ERR_C (1<<9)
|
|
#define SERROR_ERR_P (1<<10)
|
|
#define SERROR_ERR_E (1<<11)
|
|
#define SERROR_DIAG_N (1<<16)
|
|
#define SERROR_DIAG_X (1<<26)
|
|
|
|
#define SATA_FIS_SIZE (8*1024)
|
|
|
|
static int stm_sata_scr_read(struct ata_link *link, unsigned int sc_reg,
|
|
u32 *val);
|
|
static int stm_sata_scr_write(struct ata_link *link, unsigned int sc_reg,
|
|
u32 val);
|
|
|
|
/* Layout of a DMAC Linked List Item (LLI)
|
|
* DMAH_CH0_STAT_DST and DMAH_CH0_STAT_SRC are both 0 */
|
|
struct stm_lli {
|
|
u32 sar;
|
|
u32 dar;
|
|
u32 llp;
|
|
u32 ctl0;
|
|
u32 ctl1;
|
|
};
|
|
|
|
struct stm_host_priv
|
|
{
|
|
unsigned long phy_init; /* Initial value for PHYCR */
|
|
int oob_wa; /* OOB-06(a) certification test WA */
|
|
int softsg; /* If using softsg */
|
|
int shared_dma_host_irq; /* If we the interrupt from the DMA
|
|
* and HOSTC are or'ed together */
|
|
struct stm_device_state *device_state;
|
|
void (*host_restart)(int port); /* Full reset of host and phy */
|
|
int port_num; /* Parameter to host_restart */
|
|
struct stm_miphy *miphy_dev; /* MiPHY Dev Struct */
|
|
};
|
|
|
|
struct stm_port_priv
|
|
{
|
|
struct stm_lli *lli; /* Base of the allocated lli nodes */
|
|
dma_addr_t lli_dma; /* Physical version of lli */
|
|
struct stm_lli *softsg_node; /* Current softsg node */
|
|
struct stm_lli *softsg_end; /* End of the softsg node */
|
|
char smallburst; /* Small DMA burst size */
|
|
char pmp; /* Current SControl.PMP */
|
|
struct ata_link *active_link; /* Link for last request */
|
|
};
|
|
|
|
/* There is an undocumented restriction that DMA blocks must not span
|
|
* a FIS boundary. So to ensure we have enough LLIs to cope we restrict
|
|
* the maximum number of sectors, and assume a worst case of one
|
|
* LLI per sector.
|
|
*/
|
|
#define STM_MAX_SECTORS ATA_MAX_SECTORS
|
|
#define STM_MAX_LLIS ATA_MAX_SECTORS
|
|
#define STM_LLI_BYTES (STM_MAX_LLIS * sizeof(struct stm_lli))
|
|
|
|
static void stm_phy_configure(struct ata_port *ap)
|
|
{
|
|
void __iomem *mmio = ap->ioaddr.cmd_addr;
|
|
struct stm_host_priv *hpriv = ap->host->private_data;
|
|
|
|
DPRINTK("ENTER\n");
|
|
|
|
/*
|
|
* "sata1hostc Functional Specification" 1.4 defines the PHYCR as:
|
|
* phy_ctrl[0] sendalign
|
|
* phy_ctrl[1] at
|
|
* phy_ctrl[4:2] divdll[2:0]
|
|
* phy_ctrl[6:5] txslew[1:0]
|
|
* phy_ctrl[8:7] preemph[1:0]
|
|
* phy_ctrl[10:9] sdthres[1:0]
|
|
* phy_ctrl[13:11] swing[2:0]
|
|
* phy_ctrl[14] recen
|
|
* phy_ctrl[15] ensigdet
|
|
* phy_ctrl[16] enasyncdetneg
|
|
* phy_ctrl[17] enasyncdetpos
|
|
* phy_ctrl[18] startcomzc
|
|
* phy_ctrl[19] startcomsr
|
|
* phy_ctrl[24:20] iddqsub[4:0]
|
|
* phy_ctrl[31:25] NOT DEFINED
|
|
*/
|
|
if (hpriv->phy_init) {
|
|
writel(hpriv->phy_init, mmio + SATA_PHYCR);
|
|
mdelay(100);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* We have two problems with the interface between the SATA and DMA
|
|
* controllers:
|
|
* - it can only handle 32 bit quantities
|
|
* - when reading from the device, if the data returned by the device
|
|
* is short (ie less than the length programmed into the DMA engine),
|
|
* and the end of the data doesn't coincide with a DMA burst boundary,
|
|
* then the DMA will stall, and as a result the end of transfer
|
|
* interrupt will never be seen.
|
|
*/
|
|
static int stm_check_atapi_dma(struct ata_queued_cmd *qc)
|
|
{
|
|
struct ata_port *ap = qc->ap;
|
|
struct stm_port_priv *pp = ap->private_data;
|
|
u8 *scsicmd = qc->scsicmd->cmnd;
|
|
|
|
/* Whitelist commands that may use DMA. */
|
|
switch (scsicmd[0]) {
|
|
case WRITE_12:
|
|
case WRITE_10:
|
|
case WRITE_6:
|
|
case READ_12:
|
|
case READ_10:
|
|
case READ_6:
|
|
/* All data is multiples of 2048 */
|
|
pp->smallburst = 0;
|
|
return 0;
|
|
case 0xbe: /* READ_CD */
|
|
/* Data should be a multiple of four bytes */
|
|
pp->smallburst = 1;
|
|
return 0;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
static void stm_bmdma_setup(struct ata_queued_cmd *qc)
|
|
{
|
|
struct ata_port *ap = qc->ap;
|
|
struct stm_port_priv *pp = ap->private_data;
|
|
struct stm_host_priv *hpriv = ap->host->private_data;
|
|
void __iomem *mmio = ap->ioaddr.cmd_addr;
|
|
u32 cfg0, cfg1;
|
|
|
|
cfg0 = DMA_CFG_CH_PRIOR_0 |
|
|
DMA_CFG_HS_SEL_DST_HW |
|
|
DMA_CFG_HS_SEL_SRC_HW |
|
|
DMA_CFG_DST_HS_POL_HI |
|
|
DMA_CFG_SRC_HS_POL_HI |
|
|
DMA_CFG_MAX_ABRST_UNLIMITED;
|
|
|
|
cfg1 = DMA_CFG_1_FIFO_MODE |
|
|
DMA_CFG_1_SRC_PER_0 | /* Used on reads */
|
|
DMA_CFG_1_DEST_PER_1; /* Used on writes */
|
|
|
|
writel(cfg0, mmio + DMAC_CFG0_0);
|
|
writel(cfg1, mmio + DMAC_CFG0_1);
|
|
|
|
/* These reads also have the side effect of flushing any posted
|
|
* writes to the LLI's */
|
|
writel(pp->lli->ctl0, mmio + DMAC_CTL0_0);
|
|
writel(pp->lli->ctl1, mmio + DMAC_CTL0_1);
|
|
|
|
if (hpriv->softsg) {
|
|
/* Write the first node into the DMAC registers */
|
|
writel(pp->lli->sar, mmio + DMAC_SAR0);
|
|
writel(pp->lli->dar, mmio + DMAC_DAR0);
|
|
|
|
/* Clear interrupt from the final SG node of the
|
|
* previous transfer */
|
|
writel(1<<0, mmio + DMAC_CLEARTFR);
|
|
|
|
/* If there are multiple nodes in the sg list, prepare
|
|
* to set up subsequent ones in the interrupt handler.
|
|
*/
|
|
if (pp->softsg_node != pp->softsg_end) {
|
|
pp->softsg_node++;
|
|
writel(1<<8 | 1<<0, mmio + DMAC_MASKTFR);
|
|
}
|
|
} else {
|
|
writel(pp->lli_dma, mmio + DMAC_LLP0);
|
|
}
|
|
|
|
/* Set Rx and Tx FIFO threshholds to 16 DWORDS except if using
|
|
* small burst reads, when we set it to 1 DWORD.
|
|
* Note: this is reset by a COMRESET.
|
|
*/
|
|
if (pp->smallburst) {
|
|
writel((0x1 << 16) | (0x10 << 0), mmio + SATA_DBTSR);
|
|
} else {
|
|
writel((0x10 << 16) | (0x10 << 0), mmio + SATA_DBTSR);
|
|
}
|
|
|
|
/* Enable DMA on the SATA host */
|
|
writel(SATA_DMACR_TXCHEN | SATA_DMACR_RXCHEN,
|
|
mmio + SATA_DMACR);
|
|
|
|
DPRINTK("SAR %08lx, DAR %08lx, CTL0 %08lx CTL1 %08lx\n",
|
|
readl(mmio + DMAC_SAR0),
|
|
readl(mmio + DMAC_DAR0),
|
|
readl(mmio + DMAC_CTL0_0),
|
|
readl(mmio + DMAC_CTL0_1));
|
|
DPRINTK("CFG0 %08lx CFG1 %08lx\n",
|
|
readl(mmio + DMAC_CFG0_0),
|
|
readl(mmio + DMAC_CFG0_1));
|
|
DPRINTK("ChEnReg %08lx DmaCfgReg %08lx\n",
|
|
readl(mmio + DMAC_ChEnReg),
|
|
readl(mmio + DMAC_DmaCfgReg));
|
|
|
|
/* issue r/w command */
|
|
ap->ops->sff_exec_command(ap, &qc->tf);
|
|
}
|
|
|
|
static void stm_bmdma_start(struct ata_queued_cmd *qc)
|
|
{
|
|
struct ata_port *ap = qc->ap;
|
|
void __iomem *mmio = ap->ioaddr.cmd_addr;
|
|
|
|
DPRINTK("ENTER\n");
|
|
|
|
/* Enable channel 0 */
|
|
writel((1<<8) | (1<<0), mmio + DMAC_ChEnReg);
|
|
}
|
|
|
|
static void stm_bmdma_stop(struct ata_queued_cmd *qc)
|
|
{
|
|
struct ata_port *ap = qc->ap;
|
|
void __iomem *mmio = ap->ioaddr.cmd_addr;
|
|
|
|
DPRINTK("ENTER\n");
|
|
|
|
/*
|
|
* Chanel is automatically disabled on completion of DMA, however
|
|
* this also gets called from the timeout handler.
|
|
*/
|
|
|
|
/* Disable channel 0 */
|
|
writel((1<<8) | (0<<0), mmio + DMAC_ChEnReg);
|
|
|
|
/* Disable DMA on the SATA host */
|
|
writel(0, mmio + SATA_DMACR);
|
|
}
|
|
|
|
static u8 stm_bmdma_status(struct ata_port *ap)
|
|
{
|
|
/* We should be checking here whether the current interrupt
|
|
* was raised by this SATA host. Unfortuntaly we have no
|
|
* visibility of the controller's Interrupt Pending Flag (IPF).
|
|
*/
|
|
return ATA_DMA_INTR;
|
|
}
|
|
|
|
static void stm_fill_sg(struct ata_queued_cmd *qc)
|
|
{
|
|
struct scatterlist *sg;
|
|
struct ata_port *ap = qc->ap;
|
|
struct stm_host_priv *hpriv = ap->host->private_data;
|
|
struct stm_port_priv *pp = ap->private_data;
|
|
unsigned int write = (qc->tf.flags & ATA_TFLAG_WRITE);
|
|
unsigned int si, idx;
|
|
u32 sar, dar, ctl0;
|
|
u32 fis_offset;
|
|
|
|
DPRINTK("ENTER\n");
|
|
|
|
ctl0 = DMAC_CTL_DST_TR_WIDTH_32 |
|
|
DMAC_CTL_SRC_TR_WIDTH_32 |
|
|
DMAC_CTL_DINC_INC |
|
|
DMAC_CTL_SINC_INC;
|
|
|
|
if (write) {
|
|
/* memory (master1) to SATA host (master2) transfer */
|
|
ctl0 |= DMAC_CTL_DEST_MSIZE_4 |
|
|
DMAC_CTL_SRC_MSIZE_16 |
|
|
DMAC_CTL_TT_FC_M2P_DMAC |
|
|
DMAC_CTL_DMS_2 |
|
|
DMAC_CTL_SMS_1;
|
|
} else {
|
|
/* SATA host (master2) to memory (master1) transfer */
|
|
ctl0 |= DMAC_CTL_DEST_MSIZE_16 |
|
|
(pp->smallburst ?
|
|
DMAC_CTL_SRC_MSIZE_1 :
|
|
DMAC_CTL_SRC_MSIZE_16) |
|
|
DMAC_CTL_TT_FC_P2M_DMAC |
|
|
DMAC_CTL_DMS_1 |
|
|
DMAC_CTL_SMS_2;
|
|
}
|
|
|
|
if (! hpriv->softsg) {
|
|
ctl0 |= DMAC_CTL_LLP_DST_EN |
|
|
DMAC_CTL_LLP_SRC_EN;
|
|
}
|
|
|
|
idx = 0;
|
|
sar = dar = 0;
|
|
fis_offset = 0;
|
|
for_each_sg(qc->sg, sg, qc->n_elem, si) {
|
|
u32 addr;
|
|
u32 sg_len, len;
|
|
|
|
addr = sg_dma_address(sg);
|
|
sg_len = sg_dma_len(sg);
|
|
|
|
WARN_ON(sg_len & 3);
|
|
|
|
while (sg_len) {
|
|
/* Ensure no DMA block crosses a FIS boundary */
|
|
len = sg_len;
|
|
if (len + fis_offset > SATA_FIS_SIZE)
|
|
len = SATA_FIS_SIZE - fis_offset;
|
|
|
|
/* SATA host (master2) has a hardwired address of
|
|
* DMADR, so leave the address set to 0. */
|
|
if (write) {
|
|
sar = addr;
|
|
} else {
|
|
dar = addr;
|
|
}
|
|
|
|
pp->lli[idx].sar = sar;
|
|
pp->lli[idx].dar = dar;
|
|
pp->lli[idx].llp = pp->lli_dma +
|
|
(sizeof(struct stm_lli) * (idx+1));
|
|
pp->lli[idx].ctl0 = ctl0;
|
|
pp->lli[idx].ctl1 = len >> 2;
|
|
|
|
DPRINTK("lli: %p: SAR %08x, DAR %08x, CTL0 %08x CTL1 %08x\n",
|
|
&pp->lli[idx],
|
|
pp->lli[idx].sar, pp->lli[idx].dar,
|
|
pp->lli[idx].ctl0, pp->lli[idx].ctl1);
|
|
|
|
idx++;
|
|
BUG_ON(idx >= STM_MAX_LLIS);
|
|
sg_len -= len;
|
|
addr += len;
|
|
fis_offset = (fis_offset + len) % SATA_FIS_SIZE;
|
|
}
|
|
}
|
|
|
|
WARN_ON(idx == 0);
|
|
pp->lli[idx-1].llp = 0;
|
|
|
|
if (hpriv->softsg) {
|
|
pp->softsg_node = pp->lli;
|
|
pp->softsg_end = &pp->lli[idx-1];
|
|
}
|
|
|
|
}
|
|
|
|
static void stm_pmp_select(struct ata_port *ap, int pmp)
|
|
{
|
|
if (sata_pmp_supported(ap)) {
|
|
struct stm_port_priv *pp = ap->private_data;
|
|
|
|
if (pp->pmp != pmp) {
|
|
u32 scontrol;
|
|
|
|
stm_sata_scr_read(&ap->link, SCR_CONTROL, &scontrol);
|
|
pp->pmp = pmp;
|
|
stm_sata_scr_write(&ap->link, SCR_CONTROL,
|
|
(scontrol & 0xff0));
|
|
}
|
|
}
|
|
}
|
|
|
|
static void stm_qc_prep(struct ata_queued_cmd *qc)
|
|
{
|
|
struct stm_port_priv *pp = qc->ap->private_data;
|
|
|
|
stm_pmp_select(qc->ap, qc->dev->link->pmp & 0xf);
|
|
pp->active_link = qc->dev->link;
|
|
|
|
if (!(qc->flags & ATA_QCFLAG_DMAMAP))
|
|
return;
|
|
|
|
stm_fill_sg(qc);
|
|
}
|
|
|
|
static unsigned int stm_data_xfer(struct ata_device *adev, unsigned char *buf,
|
|
unsigned int buflen, int rw)
|
|
{
|
|
struct ata_port *ap = adev->link->ap;
|
|
unsigned int i;
|
|
unsigned int words = buflen >> 1;
|
|
u16 *buf16 = (u16 *) buf;
|
|
void __iomem *mmio_base = (void __iomem *) ap->ioaddr.cmd_addr;
|
|
void __iomem *mmio = (void __iomem *)ap->ioaddr.data_addr;
|
|
|
|
/* Disable error reporting */
|
|
writel(~SERROR_ERR_E, mmio_base + SATA_ERRMR);
|
|
|
|
/* Transfer multiple of 2 bytes */
|
|
if (rw == WRITE) {
|
|
for (i = 0; i < words; i++) {
|
|
writew(le16_to_cpu(buf16[i]), mmio);
|
|
ndelay(120);
|
|
}
|
|
} else {
|
|
for (i = 0; i < words; i++) {
|
|
buf16[i] = cpu_to_le16(readw(mmio));
|
|
ndelay(120);
|
|
}
|
|
}
|
|
|
|
/* Transfer trailing 1 byte, if any. */
|
|
if (unlikely(buflen & 0x01)) {
|
|
u16 align_buf[1] = { 0 };
|
|
unsigned char *trailing_buf = buf + buflen - 1;
|
|
|
|
if (rw == WRITE) {
|
|
memcpy(align_buf, trailing_buf, 1);
|
|
writew(le16_to_cpu(align_buf[0]), mmio);
|
|
} else {
|
|
align_buf[0] = cpu_to_le16(readw(mmio));
|
|
memcpy(trailing_buf, align_buf, 1);
|
|
}
|
|
words++;
|
|
}
|
|
|
|
/* Clear any errors and re-enable error reporting */
|
|
writel(-1, mmio_base + SATA_SCR1);
|
|
writel(0xffffffff, mmio_base + SATA_ERRMR);
|
|
|
|
return words << 1;
|
|
}
|
|
|
|
static void stm_freeze(struct ata_port *ap)
|
|
{
|
|
void __iomem *mmio = ap->ioaddr.cmd_addr;
|
|
|
|
DPRINTK("ENTER\n");
|
|
|
|
/* Disable interrupts */
|
|
writel(0, mmio + SATA_INTMR);
|
|
readl(mmio + SATA_INTMR); /* flush */
|
|
ata_sff_freeze(ap);
|
|
}
|
|
|
|
static void stm_thaw(struct ata_port *ap)
|
|
{
|
|
void __iomem *mmio = ap->ioaddr.cmd_addr;
|
|
|
|
DPRINTK("ENTER\n");
|
|
|
|
/* Reenable interrupts */
|
|
writel(SATA_INT_ERR, mmio + SATA_INTMR);
|
|
ata_sff_thaw(ap);
|
|
}
|
|
|
|
static int stm_prereset(struct ata_link *link, unsigned long deadline)
|
|
{
|
|
struct ata_port *ap = link->ap;
|
|
u32 serror;
|
|
struct stm_host_priv *hpriv = ap->host->private_data;
|
|
struct stm_miphy *miphy_dev = hpriv->miphy_dev;
|
|
u8 miphy_int_status = stm_miphy_sata_status(miphy_dev);
|
|
|
|
DPRINTK("ENTER\n");
|
|
stm_sata_scr_read(&ap->link, SCR_ERROR, &serror);
|
|
|
|
/*
|
|
* Defect RnDHV00030463 and STLinux Bugzilla 9770.
|
|
* Try and detect an ESD event which is resolved by
|
|
* a reset of both the host and the phy.
|
|
*/
|
|
if (hpriv->host_restart && ((serror & SERROR_ERR_C) ||
|
|
miphy_int_status)) {
|
|
hpriv->host_restart(hpriv->port_num);
|
|
stm_miphy_start(miphy_dev);
|
|
}
|
|
|
|
stm_phy_configure(ap);
|
|
return ata_sff_prereset(link, deadline);
|
|
}
|
|
|
|
|
|
/*
|
|
* Workaround for drive detection problems (STLinux bugzilla 9873).
|
|
* Need to keep the MiPHY deserializer reset while performing the
|
|
* COMMRESET and wait till COMMWAIT is received from device.
|
|
*/
|
|
static int stm_sata_do_comreset(void __iomem *mmio_base,
|
|
struct stm_host_priv *hpriv)
|
|
{
|
|
/* Make sure that PHY is up */
|
|
int timeout, val, rval = 0;
|
|
struct stm_miphy *miphy_dev = hpriv->miphy_dev;
|
|
|
|
/* OOB-6a certification test workaround */
|
|
/* Refer to RnDHV00020989/RnDHV00032380 for more information */
|
|
if (hpriv->oob_wa) {
|
|
writel(readl(mmio_base + SATA_OOBR) | 0x80000000,
|
|
mmio_base + SATA_OOBR);
|
|
|
|
writel(readl(mmio_base + SATA_OOBR) & 0x82FFFFFF,
|
|
mmio_base + SATA_OOBR);
|
|
}
|
|
|
|
val = readl(mmio_base + SATA_SCR1);
|
|
/* clearing the SATA_SCR1 register */
|
|
writel(val, (mmio_base + SATA_SCR1));
|
|
|
|
/* Assert MiPHY deserializer reset */
|
|
stm_miphy_assert_deserializer(miphy_dev, 1);
|
|
|
|
/* Send COMMRESET */
|
|
writel(0x1, (mmio_base + SATA_SCR2));
|
|
msleep(1);
|
|
writel(0x0, (mmio_base + SATA_SCR2));
|
|
|
|
/* wait a while before checking status*/
|
|
msleep(150);
|
|
/* Wait Till COMWAKE Detected */
|
|
timeout = 100;
|
|
while (timeout-- &&
|
|
((readl(mmio_base + SATA_SCR1) & 0x40000) != 0x40000))
|
|
msleep(1);
|
|
|
|
/* Deassert MiPHY deserializer reset */
|
|
stm_miphy_assert_deserializer(miphy_dev, 0);
|
|
if (timeout <= 0) {
|
|
rval = -1;
|
|
goto err;
|
|
}
|
|
|
|
timeout = 100;
|
|
/* Waiting for PHYRDY to be detected by Host */
|
|
while (timeout-- && ((readl(mmio_base + SATA_SCR0) & 0x03) != 0x03))
|
|
msleep(1);
|
|
|
|
if (timeout <= 0)
|
|
rval = -1;
|
|
|
|
err:
|
|
if (hpriv->oob_wa)
|
|
writel(readl(mmio_base + SATA_OOBR) & 0x7FFFFFFF,
|
|
mmio_base + SATA_OOBR);
|
|
|
|
|
|
return rval;
|
|
}
|
|
|
|
static int stm_sata_hardreset(struct ata_link *link, unsigned int *class,
|
|
unsigned long deadline)
|
|
{
|
|
struct ata_port *ap = link->ap;
|
|
void __iomem *mmio_base = ap->ioaddr.cmd_addr;
|
|
struct stm_host_priv *hpriv = ap->host->private_data;
|
|
/* Debounce timing */
|
|
const unsigned long *timing = sata_ehc_deb_timing(&link->eh_context);
|
|
u32 rc;
|
|
|
|
/* Check if device is detected */
|
|
if (readl(mmio_base + SATA_SCR0) == 0x0)
|
|
return 0; /* No device detected */
|
|
|
|
stm_phy_configure(ap);
|
|
|
|
if (stm_sata_do_comreset(mmio_base, hpriv))
|
|
return 0; /* Link Offline */
|
|
|
|
/* resume with Debounce */
|
|
rc = sata_link_resume(link, timing, deadline);
|
|
if (rc) {
|
|
ata_link_printk(link, KERN_WARNING, "failed to resume "
|
|
"link after reset (errno=%d)\n", rc);
|
|
return rc;
|
|
}
|
|
if (ata_link_offline(link))
|
|
return 0;
|
|
|
|
return -EAGAIN;
|
|
}
|
|
|
|
static void stm_postreset(struct ata_link *link, unsigned int *classes)
|
|
{
|
|
struct ata_port *ap = link->ap;
|
|
void __iomem *mmio = ap->ioaddr.cmd_addr;
|
|
|
|
DPRINTK("ENTER\n");
|
|
|
|
/* Enable notification of errors. These are reset by COMRESET. */
|
|
writel(0xffffffff, mmio + SATA_ERRMR);
|
|
writel(SATA_INT_ERR, mmio + SATA_INTMR);
|
|
|
|
ata_std_postreset(link, classes);
|
|
}
|
|
|
|
static int stm_pmp_hardreset(struct ata_link *link, unsigned int *class,
|
|
unsigned long deadline)
|
|
{
|
|
DPRINTK("ENTER\n");
|
|
|
|
stm_pmp_select(link->ap, sata_srst_pmp(link));
|
|
return sata_std_hardreset(link, class, deadline);
|
|
}
|
|
|
|
static int stm_softreset(struct ata_link *link, unsigned int *class,
|
|
unsigned long deadline)
|
|
{
|
|
DPRINTK("ENTER\n");
|
|
|
|
stm_pmp_select(link->ap, sata_srst_pmp(link));
|
|
return ata_sff_softreset(link, class, deadline);
|
|
}
|
|
|
|
static void stm_dev_config(struct ata_device *adev)
|
|
{
|
|
struct ata_port *ap = adev->link->ap;
|
|
int print_info = ap->link.eh_context.i.flags & ATA_EHI_PRINTINFO;
|
|
|
|
if (adev->max_sectors > STM_MAX_SECTORS) {
|
|
if (print_info)
|
|
ata_dev_printk(adev, KERN_INFO,
|
|
"restricting to %d max sectors\n",
|
|
STM_MAX_SECTORS);
|
|
adev->max_sectors = STM_MAX_SECTORS;
|
|
}
|
|
}
|
|
|
|
/* This needs munging to give per controller stats */
|
|
static unsigned long error_count;
|
|
static unsigned int print_error=1;
|
|
|
|
static unsigned stm_sata_dma_irq(struct ata_port *ap)
|
|
{
|
|
void __iomem *mmio = ap->ioaddr.cmd_addr;
|
|
struct stm_port_priv *pp = ap->private_data;
|
|
int handled = 1;
|
|
|
|
if (readl(mmio + DMAC_STATUSTFR) & 1) {
|
|
/* DMA Transfer complete update soft S/G */
|
|
|
|
/* Ack the interrupt */
|
|
writel(1<<0, mmio + DMAC_CLEARTFR);
|
|
|
|
DPRINTK("softsg_node %p, end %p\n",
|
|
pp->softsg_node, pp->softsg_end);
|
|
|
|
writel(pp->softsg_node->sar, mmio + DMAC_SAR0);
|
|
writel(pp->softsg_node->dar, mmio + DMAC_DAR0);
|
|
|
|
writel(pp->softsg_node->ctl0, mmio + DMAC_CTL0_0);
|
|
writel(pp->softsg_node->ctl1, mmio + DMAC_CTL0_1);
|
|
|
|
if (pp->softsg_node != pp->softsg_end) {
|
|
pp->softsg_node++;
|
|
} else {
|
|
writel(1<<8 | 0<<0, mmio + DMAC_MASKTFR);
|
|
}
|
|
|
|
writel((1<<8) | (1<<0), mmio + DMAC_ChEnReg);
|
|
} else if (readl(mmio + DMAC_RAWERR) & 1) {
|
|
ata_port_printk(ap, KERN_ERR, "DMA error asserted\n");
|
|
}
|
|
|
|
return handled;
|
|
|
|
}
|
|
|
|
static unsigned stm_sata_host_irq(struct ata_port *ap)
|
|
{
|
|
unsigned int handled = 0;
|
|
void __iomem *mmio = ap->ioaddr.cmd_addr;
|
|
struct ata_eh_info *ehi = &ap->link.eh_info;
|
|
u32 sstatus, serror;
|
|
|
|
if (readl(mmio + SATA_INTPR) & (SATA_INT_ERR)) {
|
|
|
|
stm_sata_scr_read(&ap->link, SCR_STATUS, &sstatus);
|
|
stm_sata_scr_read(&ap->link, SCR_ERROR, &serror);
|
|
stm_sata_scr_write(&ap->link, SCR_ERROR, serror);
|
|
|
|
if (print_error)
|
|
ata_port_printk(ap, KERN_ERR,
|
|
"SStatus 0x%08x, SError 0x%08x\n",
|
|
sstatus, serror);
|
|
error_count++;
|
|
|
|
ata_ehi_clear_desc(ehi);
|
|
ata_ehi_push_desc(ehi, "SStatus 0x%08x, SError 0x%08x",
|
|
sstatus, serror);
|
|
ehi->serror |= serror;
|
|
|
|
if (serror & (SERROR_DIAG_N | SERROR_DIAG_X)) {
|
|
ata_ehi_hotplugged(ehi);
|
|
ata_ehi_push_desc(ehi, "Treating as hot-%splug",
|
|
serror & SERROR_DIAG_X ? "" : "un");
|
|
}
|
|
|
|
ata_port_freeze(ap);
|
|
handled = 1;
|
|
} else
|
|
if (ap && (!(ap->flags & ATA_FLAG_DISABLED))) {
|
|
struct ata_queued_cmd *qc;
|
|
struct stm_port_priv *pp = ap->private_data;
|
|
|
|
qc = ata_qc_from_tag(ap, pp->active_link->active_tag);
|
|
/*
|
|
* ata_qc_issue sets qc->dev->link.active_tag
|
|
* rather than ap->link.active_tag. This
|
|
* means for PMP the host link doesn't have
|
|
* the tag set.
|
|
*/
|
|
if (qc && (!(qc->tf.ctl & ATA_NIEN)))
|
|
handled += ata_sff_host_intr(ap, qc);
|
|
}
|
|
|
|
return handled;
|
|
}
|
|
|
|
static irqreturn_t stm_sata_dma_interrupt(int irq, void *dev_instance)
|
|
{
|
|
struct ata_host *host = dev_instance;
|
|
unsigned int handled = 0;
|
|
unsigned int i;
|
|
struct stm_host_priv *hpriv = host->private_data;
|
|
|
|
BUG_ON(hpriv->shared_dma_host_irq);
|
|
|
|
spin_lock(&host->lock);
|
|
|
|
for (i = 0; i < host->n_ports; i++)
|
|
handled += stm_sata_dma_irq(host->ports[i]);
|
|
|
|
spin_unlock(&host->lock);
|
|
|
|
return IRQ_RETVAL(handled);
|
|
}
|
|
|
|
static irqreturn_t stm_sata_interrupt(int irq, void *dev_instance)
|
|
{
|
|
struct ata_host *host = dev_instance;
|
|
unsigned int handled = 0;
|
|
unsigned int i;
|
|
struct stm_host_priv *hpriv = host->private_data;
|
|
|
|
DPRINTK("ENTER\n");
|
|
|
|
spin_lock(&host->lock);
|
|
|
|
for (i = 0; i < host->n_ports; i++) {
|
|
if (hpriv->shared_dma_host_irq)
|
|
handled += stm_sata_dma_irq(host->ports[i]);
|
|
handled += stm_sata_host_irq(host->ports[i]);
|
|
}
|
|
|
|
spin_unlock(&host->lock);
|
|
|
|
return IRQ_RETVAL(handled);
|
|
}
|
|
|
|
static void stm_irq_clear(struct ata_port *ap)
|
|
{
|
|
/* TODO */
|
|
}
|
|
|
|
static int stm_sata_scr_read(struct ata_link *link, unsigned int sc_reg, u32 *val)
|
|
{
|
|
void __iomem *mmio = link->ap->ioaddr.cmd_addr;
|
|
|
|
if (sc_reg > SCR_CONTROL) return -EINVAL;
|
|
|
|
*val = readl(mmio + SATA_SCR0 + (sc_reg * 4));
|
|
return 0;
|
|
}
|
|
|
|
static int stm_sata_scr_write(struct ata_link *link, unsigned int sc_reg, u32 val)
|
|
{
|
|
void __iomem *mmio = link->ap->ioaddr.cmd_addr;
|
|
|
|
DPRINTK("%d = %08x\n", sc_reg, val);
|
|
if (sc_reg > SCR_CONTROL) return -EINVAL;
|
|
|
|
if (sc_reg == SCR_CONTROL) {
|
|
struct stm_port_priv *pp = link->ap->private_data;
|
|
|
|
val &= ~(0xf << 16);
|
|
val |= pp->pmp << 16;
|
|
}
|
|
|
|
writel(val, mmio + SATA_SCR0 + (sc_reg * 4));
|
|
return 0;
|
|
}
|
|
|
|
static int stm_port_start (struct ata_port *ap)
|
|
{
|
|
struct device *dev = ap->host->dev;
|
|
struct stm_host_priv *hpriv = ap->host->private_data;
|
|
struct stm_port_priv *pp;
|
|
|
|
pp = devm_kzalloc(dev, sizeof(*pp), GFP_KERNEL);
|
|
if (pp == NULL)
|
|
return -ENOMEM;
|
|
|
|
if (hpriv->softsg) {
|
|
pp->lli = devm_kzalloc(dev, STM_LLI_BYTES, GFP_KERNEL);
|
|
} else {
|
|
pp->lli = dmam_alloc_coherent(dev, STM_LLI_BYTES, &pp->lli_dma,
|
|
GFP_KERNEL);
|
|
}
|
|
|
|
if (pp->lli == NULL)
|
|
return -ENOMEM;
|
|
|
|
pp->smallburst = 0;
|
|
|
|
ap->private_data = pp;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static ssize_t stm_show_serror(struct device *dev,
|
|
struct device_attribute *attr, char *buf)
|
|
{
|
|
ssize_t len;
|
|
|
|
len = snprintf(buf, PAGE_SIZE, "%ld\n", error_count);
|
|
return len;
|
|
}
|
|
|
|
static ssize_t stm_store_serror(struct device *dev,
|
|
struct device_attribute *attr,
|
|
const char *buf, size_t count)
|
|
{
|
|
error_count = simple_strtoul(buf, NULL, 10);
|
|
return count;
|
|
}
|
|
|
|
static DEVICE_ATTR(serror, S_IRUGO | S_IWUGO,
|
|
stm_show_serror, stm_store_serror);
|
|
|
|
static ssize_t stm_show_printerror(struct device *dev,
|
|
struct device_attribute *attr, char *buf)
|
|
{
|
|
ssize_t len;
|
|
|
|
len = snprintf(buf, PAGE_SIZE, "%d\n", print_error);
|
|
return len;
|
|
}
|
|
|
|
static ssize_t stm_store_printerror(struct device *dev,
|
|
struct device_attribute *attr,
|
|
const char *buf, size_t count)
|
|
{
|
|
print_error = simple_strtoul(buf, NULL, 10);
|
|
return count;
|
|
}
|
|
|
|
static DEVICE_ATTR(printerror, S_IRUGO | S_IWUGO,
|
|
stm_show_printerror, stm_store_printerror);
|
|
|
|
/* Host attributes initializer */
|
|
static struct device_attribute *stm_host_attrs[] = {
|
|
&dev_attr_serror,
|
|
&dev_attr_printerror,
|
|
NULL,
|
|
};
|
|
|
|
static struct scsi_host_template stm_sht = {
|
|
ATA_BMDMA_SHT(DRV_NAME),
|
|
.max_sectors = STM_MAX_SECTORS,
|
|
.shost_attrs = stm_host_attrs,
|
|
};
|
|
|
|
static struct ata_port_operations stm_ops = {
|
|
.inherits = &ata_sff_port_ops,
|
|
|
|
.check_atapi_dma = stm_check_atapi_dma,
|
|
.qc_defer = sata_pmp_qc_defer_cmd_switch,
|
|
.qc_prep = stm_qc_prep,
|
|
|
|
.dev_config = stm_dev_config,
|
|
|
|
.freeze = stm_freeze,
|
|
.thaw = stm_thaw,
|
|
.prereset = stm_prereset,
|
|
.postreset = stm_postreset,
|
|
|
|
.pmp_hardreset = stm_pmp_hardreset,
|
|
.pmp_softreset = stm_softreset,
|
|
.softreset = stm_softreset,
|
|
.hardreset = stm_sata_hardreset,
|
|
.error_handler = sata_pmp_error_handler,
|
|
|
|
.scr_read = stm_sata_scr_read,
|
|
.scr_write = stm_sata_scr_write,
|
|
|
|
.port_start = stm_port_start,
|
|
|
|
.sff_data_xfer = stm_data_xfer,
|
|
.sff_irq_clear = stm_irq_clear,
|
|
|
|
.bmdma_setup = stm_bmdma_setup,
|
|
.bmdma_start = stm_bmdma_start,
|
|
.bmdma_stop = stm_bmdma_stop,
|
|
.bmdma_status = stm_bmdma_status,
|
|
};
|
|
|
|
static const struct ata_port_info stm_port_info = {
|
|
.flags = ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY |
|
|
ATA_FLAG_MMIO | ATA_FLAG_SATA_RESET | ATA_FLAG_PMP,
|
|
.pio_mask = 0x1f, /* pio0-4 */
|
|
.mwdma_mask = 0x07, /* mwdma0-2 */
|
|
.udma_mask = ATA_UDMA6,
|
|
.port_ops = &stm_ops,
|
|
};
|
|
|
|
static int stm_sata_AHB_boot(struct device *dev)
|
|
{
|
|
struct stm_plat_sata_data *sata_private_info = dev->platform_data;
|
|
struct ata_host *host = dev_get_drvdata(dev);
|
|
struct ata_port *ap = host->ports[0];
|
|
void __iomem *mmio_base = ap->ioaddr.cmd_addr;
|
|
|
|
/* AHB bus wrapper setup */
|
|
|
|
/* SATA_AHB2STBUS_STBUS_OPC
|
|
* 2:0 -- 100 = Store64/Load64
|
|
* 4 -- 1 = Enable write posting
|
|
* DMA Read, write posting always = 0
|
|
* opcode = Load4 |Store 4
|
|
*/
|
|
writel(3, mmio_base + SATA_AHB2STBUS_STBUS_OPC);
|
|
|
|
/* SATA_AHB2STBUS_MESSAGE_SIZE_CONFIG
|
|
* 3:0 -- 0111 = 128 Packets
|
|
* 3:0 -- 0110 = 64 Packets
|
|
* WAS: Message size = 64 packet when 6 now 3
|
|
*/
|
|
writel(3, mmio_base + SATA_AHB2STBUS_MESSAGE_SIZE_CONFIG);
|
|
|
|
/* SATA_AHB2STBUS_CHUNK_SIZE_CONFIG
|
|
* 3:0 -- 0110 = 64 Packets
|
|
* 3:0 -- 0001 = 2 Packets
|
|
* WAS Chunk size = 2 packet when 1, now 0
|
|
*/
|
|
writel(2, mmio_base + SATA_AHB2STBUS_CHUNK_SIZE_CONFIG);
|
|
|
|
/* PC_GLUE_LOGIC
|
|
* 7:0 -- 0xFF = Set as reset value, 256 STBus Clock Cycles
|
|
* 8 -- 1 = Time out enabled
|
|
* (has bit 8 moved to bit 16 on 7109 cut2?)
|
|
* time out count = 0xa0(160 dec)
|
|
* time out enable = 1
|
|
*/
|
|
if (sata_private_info->pc_glue_logic_init)
|
|
writel(sata_private_info->pc_glue_logic_init,
|
|
mmio_base + SATA_PC_GLUE_LOGIC);
|
|
|
|
/* DMA controller set up */
|
|
|
|
/* Enable DMA controller */
|
|
writel(DMAC_DmaCfgReg_DMA_EN, mmio_base + DMAC_DmaCfgReg);
|
|
|
|
/* Clear initial Serror */
|
|
writel(-1, mmio_base + SATA_SCR1);
|
|
|
|
/* Finished hardware set up */
|
|
return 0;
|
|
}
|
|
|
|
static int __devinit stm_sata_probe(struct platform_device *pdev)
|
|
{
|
|
struct stm_plat_sata_data *sata_private_info = pdev->dev.platform_data;
|
|
struct device *dev = &pdev->dev;
|
|
struct resource *mem_res;
|
|
unsigned long phys_base, phys_size;
|
|
void __iomem *mmio_base;
|
|
const struct ata_port_info *ppi[] = { &stm_port_info, NULL };
|
|
struct ata_host *host;
|
|
struct ata_port *ap;
|
|
struct stm_host_priv *hpriv = NULL;
|
|
unsigned long sata_rev, dmac_rev;
|
|
int dma_irq;
|
|
int ret;
|
|
|
|
|
|
printk(KERN_DEBUG DRV_NAME " version " DRV_VERSION "\n");
|
|
|
|
host = ata_host_alloc_pinfo(dev, ppi, 1);
|
|
if (!host)
|
|
return -ENOMEM;
|
|
|
|
hpriv = devm_kzalloc(dev, sizeof(*hpriv), GFP_KERNEL);
|
|
if (!hpriv)
|
|
return -ENOMEM;
|
|
|
|
host->private_data = hpriv;
|
|
|
|
hpriv->device_state = devm_stm_device_init(dev,
|
|
sata_private_info->device_config);
|
|
if (!hpriv->device_state)
|
|
return -EBUSY;
|
|
|
|
mem_res = platform_get_resource(pdev,IORESOURCE_MEM,0);
|
|
phys_base = mem_res->start;
|
|
phys_size = mem_res->end - mem_res->start + 1;
|
|
|
|
if (!devm_request_mem_region(dev, phys_base, phys_size, "STM SATA"))
|
|
return -EBUSY;
|
|
|
|
mmio_base = devm_ioremap(dev, phys_base, phys_size);
|
|
if (mmio_base == NULL)
|
|
return ENOMEM;
|
|
|
|
/* Set up the ports */
|
|
ap = host->ports[0];
|
|
ap->ioaddr.cmd_addr = mmio_base;
|
|
ap->ioaddr.data_addr = mmio_base + SATA_CDR0;
|
|
ap->ioaddr.error_addr = mmio_base + SATA_CDR1;
|
|
ap->ioaddr.feature_addr = mmio_base + SATA_CDR1;
|
|
ap->ioaddr.nsect_addr = mmio_base + SATA_CDR2;
|
|
ap->ioaddr.lbal_addr = mmio_base + SATA_CDR3;
|
|
ap->ioaddr.lbam_addr = mmio_base + SATA_CDR4;
|
|
ap->ioaddr.lbah_addr = mmio_base + SATA_CDR5;
|
|
ap->ioaddr.device_addr = mmio_base + SATA_CDR6;
|
|
ap->ioaddr.status_addr = mmio_base + SATA_CDR7;
|
|
ap->ioaddr.command_addr = mmio_base + SATA_CDR7;
|
|
|
|
ap->ioaddr.altstatus_addr = mmio_base + SATA_CLR0;
|
|
ap->ioaddr.ctl_addr = mmio_base + SATA_CLR0;
|
|
|
|
hpriv->phy_init = sata_private_info->phy_init;
|
|
hpriv->oob_wa = sata_private_info->oob_wa;
|
|
hpriv->host_restart = sata_private_info->host_restart;
|
|
hpriv->port_num = sata_private_info->port_num;
|
|
hpriv->softsg = readl(mmio_base + DMAC_COMP_PARAMS_2) &
|
|
DMAC_COMP_PARAMS_2_CH0_HC_LLP;
|
|
//hpriv->softsg = 1;
|
|
|
|
printk(KERN_DEBUG DRV_NAME " using %sware scatter/gather\n",
|
|
hpriv->softsg ? "soft" : "hard");
|
|
|
|
if (sata_private_info->only_32bit) {
|
|
printk(KERN_ERR DRV_NAME " hardware doesn't support "
|
|
"byte/long ops, giving up\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
sata_rev = readl(mmio_base + SATA_VERSIONR);
|
|
dmac_rev = readl(mmio_base + DMAC_COMP_VERSION);
|
|
printk(KERN_DEBUG DRV_NAME " SATA version %c.%c%c DMA version %c.%c%c\n",
|
|
(int)(sata_rev >> 24) & 0xff,
|
|
(int)(sata_rev >> 16) & 0xff,
|
|
(int)(sata_rev >> 8) & 0xff,
|
|
(int)(dmac_rev >> 24) & 0xff,
|
|
(int)(dmac_rev >> 16) & 0xff,
|
|
(int)(dmac_rev >> 8) & 0xff);
|
|
|
|
hpriv->miphy_dev = stm_miphy_claim(sata_private_info->miphy_num,
|
|
SATA_MODE, dev);
|
|
if (!hpriv->miphy_dev) {
|
|
printk(KERN_ERR DRV_NAME " Unable to claim MiPHY %d\n",
|
|
sata_private_info->miphy_num);
|
|
return -EBUSY;
|
|
}
|
|
|
|
stm_sata_AHB_boot(dev);
|
|
|
|
/* Wait & timeout till we detect the disk */
|
|
stm_sata_do_comreset(mmio_base, hpriv);
|
|
|
|
/* Now, are we on one of the later SATA IP's, we have the DMA and
|
|
* host controller interrupt lines separated out. So if we have two
|
|
* irq resources, then it is one of these
|
|
*/
|
|
|
|
dma_irq = platform_get_irq(pdev, 1);
|
|
if (dma_irq > 0) {
|
|
/* We have two interrupts */
|
|
if (devm_request_irq(host->dev, dma_irq, stm_sata_dma_interrupt,
|
|
0, dev_driver_string(host->dev), host) < 0)
|
|
panic("Cannot register SATA dma interrupt %d\n",
|
|
dma_irq);
|
|
hpriv->shared_dma_host_irq = 0;
|
|
} else {
|
|
hpriv->shared_dma_host_irq = 1;
|
|
}
|
|
|
|
ret = ata_host_activate(host, platform_get_irq(pdev, 0),
|
|
stm_sata_interrupt,
|
|
IRQF_SHARED, &stm_sht);
|
|
|
|
if (ret && dma_irq > 0)
|
|
devm_free_irq(host->dev, dma_irq, host);
|
|
else {
|
|
/* by default the device is on */
|
|
pm_runtime_set_active(&pdev->dev);
|
|
pm_suspend_ignore_children(&pdev->dev, 1);
|
|
pm_runtime_enable(&pdev->dev);
|
|
}
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
static int stm_sata_remove(struct platform_device *pdev)
|
|
{
|
|
struct ata_host *host = dev_get_drvdata(&pdev->dev);
|
|
struct stm_host_priv *hpriv = host->private_data;
|
|
|
|
stm_miphy_release(hpriv->miphy_dev);
|
|
stm_device_power(hpriv->device_state, stm_device_power_off);
|
|
|
|
return 0;
|
|
}
|
|
|
|
#ifdef CONFIG_PM
|
|
static int stm_sata_suspend(struct device *dev)
|
|
{
|
|
struct ata_host *host = dev_get_drvdata(dev);
|
|
struct stm_host_priv *hpriv = host->private_data;
|
|
|
|
#ifdef CONFIG_PM_RUNTIME
|
|
if (dev->power.runtime_status != RPM_ACTIVE)
|
|
return 0; /* sata already suspended via runtime_suspend */
|
|
#endif
|
|
|
|
stm_device_power(hpriv->device_state, stm_device_power_off);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int stm_sata_resume(struct device *dev)
|
|
{
|
|
struct ata_host *host = dev_get_drvdata(dev);
|
|
struct stm_host_priv *hpriv = host->private_data;
|
|
|
|
#ifdef CONFIG_PM_RUNTIME
|
|
if (dev->power.runtime_status == RPM_SUSPENDED)
|
|
return 0; /* sata wants resume via runtime_resume... */
|
|
#endif
|
|
|
|
stm_device_power(hpriv->device_state, stm_device_power_on);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int stm_sata_freeze(struct device *dev)
|
|
{
|
|
struct ata_host *host = dev_get_drvdata(dev);
|
|
struct stm_host_priv *hpriv = host->private_data;
|
|
|
|
#ifdef CONFIG_PM_RUNTIME
|
|
if (dev->power.runtime_status != RPM_ACTIVE)
|
|
return 0; /* sata already suspended via runtime_suspend */
|
|
#endif
|
|
|
|
stm_device_power(hpriv->device_state, stm_device_power_off);
|
|
stm_miphy_freeze(hpriv->miphy_dev);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int stm_sata_restore(struct device *dev)
|
|
{
|
|
struct ata_host *host = dev_get_drvdata(dev);
|
|
struct stm_host_priv *hpriv = host->private_data;
|
|
struct ata_port *port;
|
|
struct Scsi_Host *scsi_host;
|
|
unsigned int nr_port, id, lun, channel;
|
|
|
|
#ifdef CONFIG_PM_RUNTIME
|
|
if (dev->power.runtime_status == RPM_SUSPENDED)
|
|
return 0; /* sata wants resume via runtime_resume... */
|
|
#endif
|
|
|
|
stm_device_power(hpriv->device_state, stm_device_power_on);
|
|
|
|
stm_sata_AHB_boot(dev);
|
|
|
|
for (nr_port = 0; nr_port < host->n_ports; ++nr_port) {
|
|
port = host->ports[nr_port];
|
|
scsi_host = port->scsi_host;
|
|
|
|
for (id = 0; id < scsi_host->max_id; ++id)
|
|
for (lun = 0; lun < scsi_host->max_lun; ++lun)
|
|
for (channel = 0;
|
|
channel < scsi_host->max_channel;
|
|
++channel)
|
|
scsi_host->transportt->user_scan(
|
|
scsi_host, channel, id, lun);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int stm_sata_thaw(struct device *dev)
|
|
{
|
|
struct ata_host *host = dev_get_drvdata(dev);
|
|
struct stm_host_priv *hpriv = host->private_data;
|
|
|
|
stm_miphy_thaw(hpriv->miphy_dev);
|
|
stm_device_power(hpriv->device_state, stm_device_power_on);
|
|
|
|
return 0;
|
|
}
|
|
|
|
#ifdef CONFIG_PM_RUNTIME
|
|
static int stm_sata_runtime_suspend(struct device *dev)
|
|
{
|
|
struct ata_host *host = dev_get_drvdata(dev);
|
|
struct stm_host_priv *hpriv = host->private_data;
|
|
unsigned int nr_port;
|
|
|
|
if (dev->power.runtime_status == RPM_SUSPENDED) {
|
|
pr_debug("%s already suspended\n", dev_name(dev));
|
|
return 0;
|
|
}
|
|
|
|
for (nr_port = 0; nr_port < host->n_ports; ++nr_port) {
|
|
struct ata_port *port = host->ports[nr_port];
|
|
struct Scsi_Host *scsi_host = port->scsi_host;
|
|
struct scsi_device *sdev, *next_sdev;
|
|
|
|
list_for_each_entry_safe(sdev, next_sdev,
|
|
&scsi_host->__devices, siblings)
|
|
/* suspend all the child devices */
|
|
scsi_remove_device(sdev);
|
|
}
|
|
|
|
stm_device_power(hpriv->device_state, stm_device_power_off);
|
|
return 0;
|
|
}
|
|
|
|
static int stm_sata_runtime_resume(struct device *dev)
|
|
{
|
|
if (dev->power.runtime_status == RPM_ACTIVE) {
|
|
pr_debug("%s already active\n", dev_name(dev));
|
|
return 0;
|
|
}
|
|
|
|
return stm_sata_restore(dev);
|
|
}
|
|
#else
|
|
#define stm_sata_runtime_suspend NULL
|
|
#define stm_sata_runtime_resume NULL
|
|
#endif
|
|
|
|
static struct dev_pm_ops stm_sata_pm = {
|
|
.suspend = stm_sata_suspend, /* on standby/memstandby */
|
|
.resume = stm_sata_resume, /* resume from standby/memstandby */
|
|
.freeze = stm_sata_freeze,
|
|
.thaw = stm_sata_thaw,
|
|
.restore = stm_sata_restore,
|
|
.runtime_suspend = stm_sata_runtime_suspend,
|
|
.runtime_resume = stm_sata_runtime_resume,
|
|
};
|
|
#else
|
|
static struct dev_pm_ops stm_sata_pm;
|
|
#endif
|
|
|
|
static struct platform_driver stm_sata_driver = {
|
|
.driver = {
|
|
.name = DRV_NAME,
|
|
.owner = THIS_MODULE,
|
|
.pm = &stm_sata_pm,
|
|
},
|
|
.probe = stm_sata_probe,
|
|
.remove = stm_sata_remove,
|
|
};
|
|
|
|
static int __init stm_sata_init(void)
|
|
{
|
|
return platform_driver_register(&stm_sata_driver);
|
|
}
|
|
|
|
static void __exit stm_sata_exit(void)
|
|
{
|
|
platform_driver_unregister(&stm_sata_driver);
|
|
}
|
|
|
|
module_init(stm_sata_init);
|
|
module_exit(stm_sata_exit);
|
|
|
|
MODULE_AUTHOR("Stuart Menefy");
|
|
MODULE_DESCRIPTION("low-level driver for STMicroelectronics SATA");
|
|
MODULE_LICENSE("GPL");
|
|
MODULE_VERSION(DRV_VERSION);
|