Home | History | Annotate | Download | only in nvs
      1 /*
      2  * Copyright (C) 2006 Michael Brown <mbrown (at) fensystems.co.uk>.
      3  *
      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.
      8  *
      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.
     13  *
     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.
     17  */
     18 
     19 FILE_LICENCE ( GPL2_OR_LATER );
     20 
     21 #include <stddef.h>
     22 #include <string.h>
     23 #include <assert.h>
     24 #include <unistd.h>
     25 #include <gpxe/threewire.h>
     26 
     27 /** @file
     28  *
     29  * Three-wire serial devices
     30  *
     31  */
     32 
     33 /**
     34  * Read data from three-wire device
     35  *
     36  * @v nvs		NVS device
     37  * @v address		Address from which to read
     38  * @v data		Data buffer
     39  * @v len		Length of data buffer
     40  * @ret rc		Return status code
     41  */
     42 int threewire_read ( struct nvs_device *nvs, unsigned int address,
     43 		     void *data, size_t len ) {
     44 	struct spi_device *device = nvs_to_spi ( nvs );
     45 	struct spi_bus *bus = device->bus;
     46 	int rc;
     47 
     48 	assert ( bus->mode == SPI_MODE_THREEWIRE );
     49 
     50 	DBGC ( device, "3wire %p reading %zd bytes at %04x\n",
     51 	       device, len, address );
     52 
     53 	if ( ( rc = bus->rw ( bus, device, THREEWIRE_READ, address,
     54 			      NULL, data, len ) ) != 0 ) {
     55 		DBGC ( device, "3wire %p could not read: %s\n",
     56 		       device, strerror ( rc ) );
     57 		return rc;
     58 	}
     59 
     60 	return 0;
     61 }
     62 
     63 /**
     64  * Write data to three-wire device
     65  *
     66  * @v nvs		NVS device
     67  * @v address		Address from which to read
     68  * @v data		Data buffer
     69  * @v len		Length of data buffer
     70  * @ret rc		Return status code
     71  */
     72 int threewire_write ( struct nvs_device *nvs, unsigned int address,
     73 		      const void *data, size_t len ) {
     74 	struct spi_device *device = nvs_to_spi ( nvs );
     75 	struct spi_bus *bus = device->bus;
     76 	int rc;
     77 
     78 	assert ( bus->mode == SPI_MODE_THREEWIRE );
     79 
     80 	DBGC ( device, "3wire %p writing %zd bytes at %04x\n",
     81 	       device, len, address );
     82 
     83 	/* Enable device for writing */
     84 	if ( ( rc = bus->rw ( bus, device, THREEWIRE_EWEN,
     85 			      THREEWIRE_EWEN_ADDRESS, NULL, NULL, 0 ) ) != 0 ){
     86 		DBGC ( device, "3wire %p could not enable writing: %s\n",
     87 		       device, strerror ( rc ) );
     88 		return rc;
     89 	}
     90 
     91 	/* Write data */
     92 	if ( ( rc = bus->rw ( bus, device, THREEWIRE_WRITE, address,
     93 			      data, NULL, len ) ) != 0 ) {
     94 		DBGC ( device, "3wire %p could not write: %s\n",
     95 		       device, strerror ( rc ) );
     96 		return rc;
     97 	}
     98 
     99 	/* Our model of an SPI bus doesn't provide a mechanism for
    100 	 * "assert CS, wait for MISO to become high, so just wait for
    101 	 * long enough to ensure that the write has completed.
    102 	 */
    103 	mdelay ( THREEWIRE_WRITE_MDELAY );
    104 
    105 	return 0;
    106 }
    107 
    108 /**
    109  * Autodetect device address length
    110  *
    111  * @v device		SPI device
    112  * @ret rc		Return status code
    113  */
    114 int threewire_detect_address_len ( struct spi_device *device ) {
    115 	struct nvs_device *nvs = &device->nvs;
    116 	int rc;
    117 
    118 	DBGC ( device, "3wire %p autodetecting address length\n", device );
    119 
    120 	device->address_len = SPI_AUTODETECT_ADDRESS_LEN;
    121 	if ( ( rc = threewire_read ( nvs, 0, NULL,
    122 				     ( 1 << nvs->word_len_log2 ) ) ) != 0 ) {
    123 		DBGC ( device, "3wire %p could not autodetect address "
    124 		       "length: %s\n", device, strerror ( rc ) );
    125 		return rc;
    126 	}
    127 
    128 	DBGC ( device, "3wire %p autodetected address length %d\n",
    129 	       device, device->address_len );
    130 	return 0;
    131 }
    132