📄 sata_sil3114.c
字号:
/* * Copyright (C) Excito Elektronik i Sk錸e AB, All rights reserved. * Author: Tor Krill <tor@excito.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., 59 Temple Place, Suite 330, Boston, * MA 02111-1307 USA * * This is a driver for Silicon Image sil3114 sata chip modelled on * the ata_piix driver */#include <common.h>#include <pci.h>#include <command.h>#include <config.h>#include <asm/byteorder.h>#include <asm/io.h>#include <ide.h>#include <libata.h>#include "sata_sil3114.h"/* Convert sectorsize to wordsize */#define ATA_SECTOR_WORDS (ATA_SECT_SIZE/2)/* Forwards */u8 sil3114_spin_up (int num);u8 sil3114_spin_down (int num);static int sata_bus_softreset (int num);static void sata_identify (int num, int dev);static u8 check_power_mode (int num);static void sata_port (struct sata_ioports *ioport);static void set_Feature_cmd (int num, int dev);static u8 sata_busy_wait (struct sata_ioports *ioaddr, int bits, unsigned int max, u8 usealtstatus);static u8 sata_chk_status (struct sata_ioports *ioaddr, u8 usealtstatus);static void msleep (int count);static u32 iobase[6] = { 0, 0, 0, 0, 0, 0}; /* PCI BAR registers for device */extern block_dev_desc_t sata_dev_desc[CFG_SATA_MAX_DEVICE];static struct sata_port port[CFG_SATA_MAX_DEVICE];static void output_data (struct sata_ioports *ioaddr, u16 * sect_buf, int words){ while (words--) { __raw_writew (*sect_buf++, (void *)ioaddr->data_addr); }}static int input_data (struct sata_ioports *ioaddr, u16 * sect_buf, int words){ while (words--) { *sect_buf++ = __raw_readw ((void *)ioaddr->data_addr); } return 0;}static int sata_bus_softreset (int num){ u8 status = 0; port[num].dev_mask = 1; port[num].ctl_reg = 0x08; /*Default value of control reg */ writeb (port[num].ctl_reg, port[num].ioaddr.ctl_addr); udelay (10); writeb (port[num].ctl_reg | ATA_SRST, port[num].ioaddr.ctl_addr); udelay (10); writeb (port[num].ctl_reg, port[num].ioaddr.ctl_addr); /* spec mandates ">= 2ms" before checking status. * We wait 150ms, because that was the magic delay used for * ATAPI devices in Hale Landis's ATADRVR, for the period of time * between when the ATA command register is written, and then * status is checked. Because waiting for "a while" before * checking status is fine, post SRST, we perform this magic * delay here as well. */ msleep (150); status = sata_busy_wait (&port[num].ioaddr, ATA_BUSY, 300, 0); while ((status & ATA_BUSY)) { msleep (100); status = sata_busy_wait (&port[num].ioaddr, ATA_BUSY, 3, 0); } if (status & ATA_BUSY) { printf ("ata%u is slow to respond,plz be patient\n", port); } while ((status & ATA_BUSY)) { msleep (100); status = sata_chk_status (&port[num].ioaddr, 0); } if (status & ATA_BUSY) { printf ("ata%u failed to respond : ", port); printf ("bus reset failed\n"); port[num].dev_mask = 0; return 1; } return 0;}static void sata_identify (int num, int dev){ u8 cmd = 0, status = 0, devno = num; u16 iobuf[ATA_SECTOR_WORDS]; u64 n_sectors = 0; memset (iobuf, 0, sizeof (iobuf)); if (!(port[num].dev_mask & 0x01)) { printf ("dev%d is not present on port#%d\n", dev, num); return; } debug ("port=%d dev=%d\n", num, dev); status = 0; cmd = ATA_CMD_ID_ATA; /*Device Identify Command */ writeb (cmd, port[num].ioaddr.command_addr); readb (port[num].ioaddr.altstatus_addr); udelay (10); status = sata_busy_wait (&port[num].ioaddr, ATA_BUSY, 1000, 0); if (status & ATA_ERR) { printf ("\ndevice not responding\n"); port[num].dev_mask &= ~0x01; return; } input_data (&port[num].ioaddr, iobuf, ATA_SECTOR_WORDS); ata_swap_buf_le16 (iobuf, ATA_SECTOR_WORDS); debug ("Specific config: %x\n", iobuf[2]); /* we require LBA and DMA support (bits 8 & 9 of word 49) */ if (!ata_id_has_dma (iobuf) || !ata_id_has_lba (iobuf)) { debug ("ata%u: no dma/lba\n", num); }#ifdef DEBUG ata_dump_id (iobuf);#endif n_sectors = ata_id_n_sectors (iobuf); if (n_sectors == 0) { port[num].dev_mask &= ~0x01; return; } ata_id_c_string (iobuf, (unsigned char *)sata_dev_desc[devno].revision, ATA_ID_FW_REV, sizeof (sata_dev_desc[devno].revision)); ata_id_c_string (iobuf, (unsigned char *)sata_dev_desc[devno].vendor, ATA_ID_PROD, sizeof (sata_dev_desc[devno].vendor)); ata_id_c_string (iobuf, (unsigned char *)sata_dev_desc[devno].product, ATA_ID_SERNO, sizeof (sata_dev_desc[devno].product)); /* TODO - atm we asume harddisk ie not removable */ sata_dev_desc[devno].removable = 0; sata_dev_desc[devno].lba = (u32) n_sectors; debug ("lba=0x%x\n", sata_dev_desc[devno].lba);#ifdef CONFIG_LBA48 if (iobuf[83] & (1 << 10)) { sata_dev_desc[devno].lba48 = 1; } else { sata_dev_desc[devno].lba48 = 0; }#endif /* assuming HD */ sata_dev_desc[devno].type = DEV_TYPE_HARDDISK; sata_dev_desc[devno].blksz = ATA_SECT_SIZE; sata_dev_desc[devno].lun = 0; /* just to fill something in... */}static void set_Feature_cmd (int num, int dev){ u8 status = 0; if (!(port[num].dev_mask & 0x01)) { debug ("dev%d is not present on port#%d\n", dev, num); return; } writeb (SETFEATURES_XFER, port[num].ioaddr.feature_addr); writeb (XFER_PIO_4, port[num].ioaddr.nsect_addr); writeb (0, port[num].ioaddr.lbal_addr); writeb (0, port[num].ioaddr.lbam_addr); writeb (0, port[num].ioaddr.lbah_addr); writeb (ATA_DEVICE_OBS, port[num].ioaddr.device_addr); writeb (ATA_CMD_SET_FEATURES, port[num].ioaddr.command_addr); udelay (50); msleep (150); status = sata_busy_wait (&port[num].ioaddr, ATA_BUSY, 5000, 0); if ((status & (ATA_BUSY | ATA_ERR))) { printf ("Error : status 0x%02x\n", status); port[num].dev_mask &= ~0x01; }}u8 sil3114_spin_down (int num){ u8 status = 0; debug ("Spin down disk\n"); if (!(port[num].dev_mask & 0x01)) { debug ("Device ata%d is not present\n", num); return 1; } if ((status = check_power_mode (num)) == 0x00) { debug ("Already in standby\n"); return 0; } if (status == 0x01) { printf ("Failed to check power mode on ata%d\n", num); return 1; } if (!((status = sata_chk_status (&port[num].ioaddr, 0)) & ATA_DRDY)) { printf ("Device ata%d not ready\n", num); return 1; } writeb (0x00, port[num].ioaddr.feature_addr); writeb (0x00, port[num].ioaddr.nsect_addr); writeb (0x00, port[num].ioaddr.lbal_addr); writeb (0x00, port[num].ioaddr.lbam_addr); writeb (0x00, port[num].ioaddr.lbah_addr); writeb (ATA_DEVICE_OBS, port[num].ioaddr.device_addr); writeb (ATA_CMD_STANDBY, port[num].ioaddr.command_addr); status = sata_busy_wait (&port[num].ioaddr, ATA_BUSY, 30000, 0); if ((status & (ATA_BUSY | ATA_ERR))) { printf ("Error waiting for disk spin down: status 0x%02x\n", status); port[num].dev_mask &= ~0x01; return 1; } return 0;}u8 sil3114_spin_up (int num){ u8 status = 0; debug ("Spin up disk\n"); if (!(port[num].dev_mask & 0x01)) { debug ("Device ata%d is not present\n", num); return 1; } if ((status = check_power_mode (num)) != 0x00) { if (status == 0x01) { printf ("Failed to check power mode on ata%d\n", num); return 1; } else { /* should be up and running already */ return 0; } } if (!((status = sata_chk_status (&port[num].ioaddr, 0)) & ATA_DRDY)) { printf ("Device ata%d not ready\n", num); return 1; } debug ("Stautus of device check: %d\n", status); writeb (0x00, port[num].ioaddr.feature_addr); writeb (0x00, port[num].ioaddr.nsect_addr); writeb (0x00, port[num].ioaddr.lbal_addr); writeb (0x00, port[num].ioaddr.lbam_addr); writeb (0x00, port[num].ioaddr.lbah_addr); writeb (ATA_DEVICE_OBS, port[num].ioaddr.device_addr); writeb (ATA_CMD_IDLE, port[num].ioaddr.command_addr); status = sata_busy_wait (&port[num].ioaddr, ATA_BUSY, 30000, 0); if ((status & (ATA_BUSY | ATA_ERR))) { printf ("Error waiting for disk spin up: status 0x%02x\n", status); port[num].dev_mask &= ~0x01; return 1; } /* Wait for disk to enter Active state */ do { msleep (10); status = check_power_mode (num); } while ((status == 0x00) || (status == 0x80)); if (status == 0x01) { printf ("Falied waiting for disk to spin up\n"); return 1; } return 0;}/* Return value is not the usual here * 0x00 - Device stand by * 0x01 - Operation failed * 0x80 - Device idle * 0xff - Device active*/static u8 check_power_mode (int num){ u8 status = 0; u8 res = 0; if (!(port[num].dev_mask & 0x01)) { debug ("Device ata%d is not present\n", num); return 1; } if (!(sata_chk_status (&port[num].ioaddr, 0) & ATA_DRDY)) { printf ("Device ata%d not ready\n", num); return 1; } writeb (0, port[num].ioaddr.feature_addr); writeb (0, port[num].ioaddr.nsect_addr); writeb (0, port[num].ioaddr.lbal_addr); writeb (0, port[num].ioaddr.lbam_addr); writeb (0, port[num].ioaddr.lbah_addr); writeb (ATA_DEVICE_OBS, port[num].ioaddr.device_addr); writeb (ATA_CMD_CHK_POWER, port[num].ioaddr.command_addr); status = sata_busy_wait (&port[num].ioaddr, ATA_BUSY, 5000, 0); if ((status & (ATA_BUSY | ATA_ERR))) { printf ("Error waiting for check power mode complete : status 0x%02x\n", status); port[num].dev_mask &= ~0x01; return 1; } res = readb (port[num].ioaddr.nsect_addr); debug ("Check powermode: %d\n", res); return res;}static void sata_port (struct sata_ioports *ioport){ ioport->data_addr = ioport->cmd_addr + ATA_REG_DATA; ioport->error_addr = ioport->cmd_addr + ATA_REG_ERR; ioport->feature_addr = ioport->cmd_addr + ATA_REG_FEATURE; ioport->nsect_addr = ioport->cmd_addr + ATA_REG_NSECT; ioport->lbal_addr = ioport->cmd_addr + ATA_REG_LBAL; ioport->lbam_addr = ioport->cmd_addr + ATA_REG_LBAM; ioport->lbah_addr = ioport->cmd_addr + ATA_REG_LBAH; ioport->device_addr = ioport->cmd_addr + ATA_REG_DEVICE; ioport->status_addr = ioport->cmd_addr + ATA_REG_STATUS; ioport->command_addr = ioport->cmd_addr + ATA_REG_CMD;}static u8 wait_for_irq (int num, unsigned int max){ u32 port = iobase[5]; switch (num) { case 0: port += VND_TF_CNST_CH0; break; case 1: port += VND_TF_CNST_CH1; break; case 2: port += VND_TF_CNST_CH2; break; case 3: port += VND_TF_CNST_CH3; break; default: return 1; } do { if (readl (port) & VND_TF_CNST_INTST) { break; } udelay (1000); max--; } while ((max > 0)); return (max == 0);}static u8 sata_busy_wait (struct sata_ioports *ioaddr, int bits, unsigned int max, u8 usealtstatus){ u8 status; do { if (!((status = sata_chk_status (ioaddr, usealtstatus)) & bits)) { break;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -