2 * Copyright (C) 2006 Michael Brown <mbrown@fensystems.co.uk>.
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License as
6 * published by the Free Software Foundation; either version 2 of the
7 * License, or any later version.
9 * This program is distributed in the hope that it will be useful, but
10 * WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
22 #include <gpxe/blockdev.h>
31 static inline __attribute__ (( always_inline )) struct ata_device *
32 block_to_ata ( struct block_device *blockdev ) {
33 return container_of ( blockdev, struct ata_device, blockdev );
40 * @v command ATA command
41 * @ret rc Return status code
43 static inline __attribute__ (( always_inline )) int
44 ata_command ( struct ata_device *ata, struct ata_command *command ) {
45 DBG ( "ATA cmd %02x dev %02x fl %02x LBA %llx count %04x\n",
46 command->cb.cmd_stat, command->cb.device, command->cb.flags,
47 ( unsigned long long ) command->cb.lba.native,
48 command->cb.count.native );
50 return ata->command ( ata, command );
54 * Read block from / write block to ATA device
56 * @v write Write flag (ATA_FL_WRITE or 0)
57 * @v blockdev Block device
58 * @v block LBA block number
59 * @v count Block count
60 * @v buffer Data buffer
61 * @ret rc Return status code
63 static __attribute__ (( regparm ( 1 ) )) int
64 ata_rw ( int write, struct block_device *blockdev, uint64_t block,
65 unsigned long count, userptr_t buffer ) {
66 struct ata_device *ata = block_to_ata ( blockdev );
67 struct ata_command command;
68 int lba48 = ( ata->flags & ATA_FL_LBA48 );
70 memset ( &command, 0, sizeof ( command ) );
71 command.cb.lba.native = block;
72 command.cb.count.native = count;
73 command.cb.device = ( ata->flags | ATA_DEV_OBSOLETE | ATA_DEV_LBA );
74 command.cb.flags = ( ata->flags | write );
75 command.cb.cmd_stat = ( write ? ATA_CMD_WRITE : ATA_CMD_READ );
77 command.cb.cmd_stat |= ATA_CMD_EXT;
79 command.cb.device |= command.cb.lba.bytes.low_prev;
81 command.data = buffer;
82 command.data_len = ( count * blockdev->blksize );
83 return ata_command ( ata, &command );
87 * Read block from ATA device
89 * @v blockdev Block device
90 * @v block LBA block number
91 * @v count Block count
92 * @v buffer Data buffer
93 * @ret rc Return status code
95 static int ata_read ( struct block_device *blockdev, uint64_t block,
96 unsigned long count, userptr_t buffer ) {
97 /* Pass through to ata_rw(). Since ata_rw is regparm(1), this
98 * is extremely efficient; just a mov and a jmp.
100 return ata_rw ( 0, blockdev, block, count, buffer );
104 * Write block to ATA device
106 * @v blockdev Block device
107 * @v block LBA block number
108 * @v count Block count
109 * @v buffer Data buffer
110 * @ret rc Return status code
112 static int ata_write ( struct block_device *blockdev, uint64_t block,
113 unsigned long count, userptr_t buffer ) {
114 /* Pass through to ata_rw(). Since ata_rw is regparm(1), this
115 * is extremely efficient; just a mov and a jmp.
117 return ata_rw ( ATA_FL_WRITE, blockdev, block, count, buffer );
121 * Identify ATA device
123 * @v blockdev Block device
124 * @ret rc Return status code
126 static int ata_identify ( struct block_device *blockdev ) {
127 struct ata_device *ata = block_to_ata ( blockdev );
128 struct ata_command command;
129 struct ata_identity identity;
133 memset ( &command, 0, sizeof ( command ) );
134 command.cb.device = ( ata->flags | ATA_DEV_OBSOLETE | ATA_DEV_LBA );
135 command.cb.cmd_stat = ATA_CMD_IDENTIFY;
136 command.data = virt_to_user ( &identity );
137 command.data_len = sizeof ( identity );
138 if ( ( rc = ata_command ( ata, &command ) ) != 0 )
141 /* Fill in block device parameters */
142 blockdev->blksize = ATA_SECTOR_SIZE;
143 if ( identity.supports_lba48 & cpu_to_le16 ( ATA_SUPPORTS_LBA48 ) ) {
144 ata->flags |= ATA_FL_LBA48;
145 blockdev->blocks = le64_to_cpu ( identity.lba48_sectors );
147 blockdev->blocks = le32_to_cpu ( identity.lba_sectors );
153 * Initialise ATA device
156 * @ret rc Return status code
158 * Initialises an ATA device. The ata_device::command field and the
159 * @c ATA_FL_SLAVE portion of the ata_device::flags field must already
160 * be filled in. This function will configure ata_device::blockdev,
161 * including issuing an IDENTIFY DEVICE call to determine the block
162 * size and total device size.
164 int init_atadev ( struct ata_device *ata ) {
165 /** Fill in read and write methods, and get device capacity */
166 ata->blockdev.read = ata_read;
167 ata->blockdev.write = ata_write;
168 return ata_identify ( &ata->blockdev );