/* * soclib_bdv.c - soclib simple block device driver implementation. * * Author Alain Greiner (2016) * * Copyright (c) UPMC Sorbonne Universites * * This file is part of ALMOS-MKH. * * ALMOS-MKH.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; version 2.0 of the License. * * ALMOS-MKH.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 ALMOS-MKH.; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #include #define PIO_ATA_CBR_BASE 0x1F0 # define ATA_DATA 0x000 # define ATA_ERRFEAT 0x001 /* two regs */ # define ATA_SCR 0x002 # define ATA_SNR 0x003 /* lba0 */ # define ATA_CLR 0x004 /* lba1 */ # define ATA_CHR 0x005 /* lba2 */ # define ATA_DHR 0x006 /* drive | lba3 */ # define ATA_SR 0x007 # define ATA_SR_ERR 0x01 # define ATA_SR_IDX 0x02 # define ATA_SR_CORR 0x04 # define ATA_SR_DRQ 0x08 # define ATA_SR_DSC 0x10 # define ATA_SR_DF 0x20 # define ATA_SR_DRDY 0x40 # define ATA_SR_BSY 0x80 # define ATA_CR 0x007 /* two regs */ #define ATA_CMD_READ_SECTORS_RETRY 0x20 #define ATA_CMD_READ_SECTORS_NORETRY 0x21 #define ATA_CMD_WRITE_SECTORS_RETRY 0x30 #define ATA_CMD_WRITE_SECTORS_NORETRY 0x31 #define ATA_CMD_IDENTIFY 0xEC static inline uint16_t ata_data_read() { return in16(PIO_ATA_CBR_BASE + ATA_DATA); } static inline void ata_data_write(uint16_t val) { out16(PIO_ATA_CBR_BASE + ATA_DATA, val); } static inline uint8_t ata_cbr_read(uint32_t reg) { return in8(PIO_ATA_CBR_BASE + reg); } static inline void ata_cbr_write(uint32_t reg, uint8_t val) { out8(PIO_ATA_CBR_BASE + reg, val); } static inline int ata_wait() { uint8_t status; while (!(ata_cbr_read(ATA_SR) & ATA_SR_DRQ)); status = ata_cbr_read(ATA_SR); return ((status & ATA_SR_ERR) ? -1 : 0); } static void ata_prepare(uint8_t slave, uint32_t lba, uint8_t count) { ata_cbr_write(ATA_ERRFEAT, 0x00); /* NULL byte to port 0x1F1 */ ata_cbr_write(ATA_SCR, count); /* sector count */ /* set the lba */ ata_cbr_write(ATA_SNR, (lba >> 0) & 0xFF); ata_cbr_write(ATA_CLR, (lba >> 8) & 0xFF); ata_cbr_write(ATA_CHR, (lba >> 16)& 0xFF); /* set the drive and lba3 */ ata_cbr_write(ATA_DHR, 0xE0 | (slave << 4) | ((lba >> 24) & 0x0F)); } static int ata_read(uint8_t count, char *buf) { uint16_t tmpword; int idx; /* wait for the drive to signal that it's ready */ if (ata_wait() == -1) x86_panic("ata_wait"); for (idx = 0; idx < 256 * count; idx++) { tmpword = ata_data_read(); buf[idx * 2] = (uint8_t)(tmpword & 0xFF); buf[idx * 2 + 1] = (uint8_t)(tmpword >> 8); } return 0; } static int ata_write(uint8_t count, char *buf) { uint16_t tmpword; int idx; /* wait for the drive to signal that it's ready */ if (ata_wait() == -1) x86_panic("ata_wait"); for (idx = 0; idx < 256 * count; idx++) { tmpword = (buf[idx * 2 + 1] << 8) | buf[idx * 2]; ata_data_write(tmpword); } return 0; } static uint16_t bswap16(uint16_t x) { return ((x << 8) & 0xFF00) | ((x >> 8) & 0x00FF); } static void ata_init() { uint8_t id[512]; uint16_t tmpw, *p; size_t idx; char *model; uint8_t ncyl; int ret; ata_prepare(0, 0, 0); ata_cbr_write(ATA_CR, ATA_CMD_IDENTIFY); /* wait for the drive to signal that it's ready */ ret = ata_wait(); if (ret == -1) x86_printf("-> unable to identify ATA\n"); for (idx = 0; idx < 256; idx++) { tmpw = ata_data_read(); id[idx * 2] = (uint8_t)((tmpw >> 0) & 0xFF); id[idx * 2 + 1] = (uint8_t)((tmpw >> 8) & 0xFF); } ncyl = id[0] & 0x1; x86_printf("-> cyl: %z\n", (uint64_t)ncyl); for (idx = 27*2; idx < 46*2; idx += 2) { p = (uint16_t *)(&id[idx]); *p = bswap16(*p); } model = &id[27*2]; id[46*2] = '\0'; x86_printf("-> ATA model: '%s'\n", model); } /* -------------------------------------------------------------------------- */ void soclib_bdv_init( chdev_t * chdev ) { ata_init(); } void __attribute__ ((noinline)) soclib_bdv_cmd( xptr_t th_xp ) { uint32_t cmd_type; // IOC_READ / IOC_WRITE / IOC_SYNC_READ uint32_t lba; // command argument uint32_t count; // command argument xptr_t buf_xp; // command argument // get client thread cluster and local pointer cxy_t th_cxy = GET_CXY( th_xp ); thread_t * th_ptr = (thread_t *)GET_PTR( th_xp ); // get command arguments and extended pointer on IOC device cmd_type = hal_remote_lw ( XPTR( th_cxy , &th_ptr->command.ioc.type ) ); lba = hal_remote_lw ( XPTR( th_cxy , &th_ptr->command.ioc.lba ) ); count = hal_remote_lw ( XPTR( th_cxy , &th_ptr->command.ioc.count ) ); buf_xp = (xptr_t)hal_remote_lwd( XPTR( th_cxy , &th_ptr->command.ioc.buf_xp ) ); /* execute operation */ ata_prepare(0, lba, count); if (cmd_type == IOC_WRITE) { ata_cbr_write(ATA_CR, ATA_CMD_WRITE_SECTORS_RETRY); } else { ata_cbr_write(ATA_CR, ATA_CMD_READ_SECTORS_RETRY); } /* waiting policy depends on the command type */ if (cmd_type == IOC_SYNC_READ) { ata_read(count, (char *)buf_xp); } else { x86_panic("!IOC_SYNC_READ not supported"); } } void __attribute__ ((noinline)) soclib_bdv_isr( chdev_t * chdev ) { x86_panic((char *)__func__); }