From 946967f09ce0ab9ab438a0647d393601dd0fca23 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Mon, 4 Dec 2006 18:23:06 +0000 Subject: [PATCH] Abstracted out part of the concept of an SPI device to a generalised NVS device. Separated the mechanisms of non-volatile storage access and non-volatile stored options. --- src/core/nvo.c | 109 ++++++++++++++++++++++++++++++++++ src/core/nvs.c | 106 --------------------------------- src/drivers/bitbash/spi_bit.c | 9 ++- src/drivers/net/etherfabric.c | 7 +-- src/drivers/net/rtl8139.c | 18 +++--- src/drivers/nvs/nvs.c | 31 ++++++++++ src/drivers/nvs/threewire.c | 5 +- src/include/gpxe/nvo.h | 21 +++++++ src/include/gpxe/nvs.h | 55 +++++++++++++---- src/include/gpxe/spi.h | 93 ++++++----------------------- src/include/gpxe/spi_bit.h | 1 + src/include/gpxe/threewire.h | 60 +++++++++++-------- 12 files changed, 275 insertions(+), 240 deletions(-) create mode 100644 src/core/nvo.c delete mode 100644 src/core/nvs.c create mode 100644 src/drivers/nvs/nvs.c create mode 100644 src/include/gpxe/nvo.h diff --git a/src/core/nvo.c b/src/core/nvo.c new file mode 100644 index 00000000..9bd92526 --- /dev/null +++ b/src/core/nvo.c @@ -0,0 +1,109 @@ +/* + * Copyright (C) 2006 Michael Brown . + * + * 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 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., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include + +/** @file + * + * Non-volatile stored options + * + */ + +static size_t nvo_options_len ( struct nvs_options *nvo ) { + struct dhcp_option *option; + uint8_t sum; + unsigned int i; + size_t len; + + for ( sum = 0, i = 0 ; i < nvo->nvs->size ; i++ ) { + sum += * ( ( uint8_t * ) ( nvo->options->data + i ) ); + } + if ( sum != 0 ) { + DBG ( "NVO %p has bad checksum %02x; assuming empty\n", + nvo, sum ); + return 0; + } + + option = nvo->options->data; + if ( option->tag == DHCP_PAD ) { + DBG ( "NVO %p has bad start; assuming empty\n", nvo ); + return 0; + } + + option = find_dhcp_option ( nvo->options, DHCP_END ); + if ( ! option ) { + DBG ( "NVO %p has no end tag; assuming empty\n", nvo ); + return 0; + } + + len = ( ( void * ) option - nvo->options->data + 1 ); + DBG ( "NVO %p contains %zd bytes of options (maximum %zd)\n", + nvo, len, nvo->nvs->size ); + + return len; +} + +int nvo_register ( struct nvs_options *nvo ) { + struct dhcp_option *option; + int rc; + + nvo->options = alloc_dhcp_options ( nvo->nvs->size ); + if ( ! nvo->options ) { + DBG ( "NVO %p could not allocate %zd bytes\n", + nvo, nvo->nvs->size ); + rc = -ENOMEM; + goto err; + } + + if ( ( rc = nvo->nvs->read ( nvo->nvs, 0, nvo->options->data, + nvo->nvs->size ) ) != 0 ) { + DBG ( "NVO %p could not read [0,%zd)\n", + nvo, nvo->nvs->size ); + goto err; + } + + nvo->options->len = nvo->options->max_len; + nvo->options->len = nvo_options_len ( nvo ); + if ( ! nvo->options->len ) { + option = nvo->options->data; + option->tag = DHCP_END; + nvo->options->len = 1; + } + + register_dhcp_options ( nvo->options ); + + return 0; + + err: + + free_dhcp_options ( nvo->options ); + nvo->options = NULL; + return rc; +} + +void nvo_unregister ( struct nvs_options *nvo ) { + if ( nvo->options ) { + unregister_dhcp_options ( nvo->options ); + free_dhcp_options ( nvo->options ); + nvo->options = NULL; + } +} diff --git a/src/core/nvs.c b/src/core/nvs.c deleted file mode 100644 index 9b4d1310..00000000 --- a/src/core/nvs.c +++ /dev/null @@ -1,106 +0,0 @@ -/* - * Copyright (C) 2006 Michael Brown . - * - * 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 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., 675 Mass Ave, Cambridge, MA 02139, USA. - */ - -#include -#include -#include -#include - -/** @file - * - * Non-volatile storage - * - */ - -static size_t nvs_options_len ( struct nvs_device *nvs ) { - struct dhcp_option *option; - uint8_t sum; - unsigned int i; - size_t len; - - for ( sum = 0, i = 0 ; i < nvs->len ; i++ ) { - sum += * ( ( uint8_t * ) ( nvs->options->data + i ) ); - } - if ( sum != 0 ) { - DBG ( "NVS %p has bad checksum %02x; assuming empty\n", - nvs, sum ); - return 0; - } - - option = nvs->options->data; - if ( option->tag == DHCP_PAD ) { - DBG ( "NVS %p has bad start; assuming empty\n", nvs ); - return 0; - } - - option = find_dhcp_option ( nvs->options, DHCP_END ); - if ( ! option ) { - DBG ( "NVS %p has no end tag; assuming empty\n", nvs ); - return 0; - } - - len = ( ( void * ) option - nvs->options->data + 1 ); - DBG ( "NVS %p contains %zd bytes of options (maximum %zd)\n", - nvs, len, nvs->len ); - - return len; -} - -int nvs_register ( struct nvs_device *nvs ) { - struct dhcp_option *option; - int rc; - - nvs->options = alloc_dhcp_options ( nvs->len ); - if ( ! nvs->options ) { - DBG ( "NVS %p could not allocate %zd bytes\n", nvs, nvs->len ); - rc = -ENOMEM; - goto err; - } - - if ( ( rc = nvs->op->read ( nvs, 0, nvs->options->data, - nvs->len ) ) != 0 ) { - DBG ( "NVS %p could not read [0,%zd)\n", nvs, nvs->len ); - goto err; - } - - nvs->options->len = nvs->options->max_len; - nvs->options->len = nvs_options_len ( nvs ); - if ( ! nvs->options->len ) { - option = nvs->options->data; - option->tag = DHCP_END; - nvs->options->len = 1; - } - - register_dhcp_options ( nvs->options ); - - return 0; - - err: - - free_dhcp_options ( nvs->options ); - nvs->options = NULL; - return rc; -} - -void nvs_unregister ( struct nvs_device *nvs ) { - if ( nvs->options ) { - unregister_dhcp_options ( nvs->options ); - free_dhcp_options ( nvs->options ); - nvs->options = NULL; - } -} diff --git a/src/drivers/bitbash/spi_bit.c b/src/drivers/bitbash/spi_bit.c index ef1d8ff7..8b89f01d 100644 --- a/src/drivers/bitbash/spi_bit.c +++ b/src/drivers/bitbash/spi_bit.c @@ -147,22 +147,21 @@ static int spi_bit_rw ( struct spi_bus *bus, struct spi_device *device, const void *data_out, void *data_in, size_t len ) { struct spi_bit_basher *spibit = container_of ( bus, struct spi_bit_basher, bus ); - struct spi_device_type *devtype = device->type; uint32_t tmp; /* Assert chip select on specified slave */ spi_bit_set_slave_select ( spibit, device->slave, SELECT_SLAVE ); /* Transmit command */ - assert ( devtype->command_len <= ( 8 * sizeof ( tmp ) ) ); + assert ( device->command_len <= ( 8 * sizeof ( tmp ) ) ); tmp = cpu_to_le32 ( command ); - spi_bit_transfer ( spibit, &tmp, NULL, devtype->command_len ); + spi_bit_transfer ( spibit, &tmp, NULL, device->command_len ); /* Transmit address, if present */ if ( address >= 0 ) { - assert ( devtype->address_len <= ( 8 * sizeof ( tmp ) ) ); + assert ( device->address_len <= ( 8 * sizeof ( tmp ) ) ); tmp = cpu_to_le32 ( address ); - spi_bit_transfer ( spibit, &tmp, NULL, devtype->address_len ); + spi_bit_transfer ( spibit, &tmp, NULL, device->address_len ); } /* Transmit/receive data */ diff --git a/src/drivers/net/etherfabric.c b/src/drivers/net/etherfabric.c index 9afe65df..790bfb54 100644 --- a/src/drivers/net/etherfabric.c +++ b/src/drivers/net/etherfabric.c @@ -2388,11 +2388,6 @@ static int falcon_write_nvs ( struct nvs_device *nvs, unsigned int offset, return 0; } -static struct nvs_operations falcon_nvs_operations = { - .read = falcon_read_nvs, - .write = falcon_write_nvs, -}; - /** RX descriptor */ typedef efab_qword_t falcon_rx_desc_t; @@ -3046,10 +3041,12 @@ static int falcon_init_nic ( struct efab_nic *efab ) { /* Register non-volatile storage */ if ( efab->has_eeprom ) { + /* efab->nvs.op = &falcon_nvs_operations; efab->nvs.len = 0x100; if ( nvs_register ( &efab->nvs ) != 0 ) return 0; + */ } return 1; diff --git a/src/drivers/net/rtl8139.c b/src/drivers/net/rtl8139.c index 34d6572a..503cee25 100644 --- a/src/drivers/net/rtl8139.c +++ b/src/drivers/net/rtl8139.c @@ -240,15 +240,12 @@ static struct bit_basher_operations rtl_basher_ops = { .write = rtl_spi_write_bit, }; -static struct spi_device_type rtl_ee9346 = AT93C46 ( 16 ); -static struct spi_device_type rtl_ee9356 = AT93C56 ( 16 ); - /** * Set up for EEPROM access * * @v rtl RTL8139 NIC */ -static void rtl_init_eeprom ( struct rtl8139_nic *rtl ) { + void rtl_init_eeprom ( struct rtl8139_nic *rtl ) { int ee9356; /* Initialise three-wire bus */ @@ -258,8 +255,13 @@ static void rtl_init_eeprom ( struct rtl8139_nic *rtl ) { /* Detect EEPROM type and initialise three-wire device */ ee9356 = ( inw ( rtl->ioaddr + RxConfig ) & Eeprom9356 ); - DBG ( "EEPROM is an %s\n", ( ee9356 ? "AT93C56" : "AT93C46" ) ); - rtl->eeprom.type = ( ee9356 ? &rtl_ee9356 : &rtl_ee9346 ); + if ( ee9356 ) { + DBG ( "EEPROM is an AT93C56\n" ); + init_at93c56 ( &rtl->eeprom, 16 ); + } else { + DBG ( "EEPROM is an AT93C46\n" ); + init_at93c46 ( &rtl->eeprom, 16 ); + } rtl->eeprom.bus = &rtl->spibit.bus; } @@ -271,12 +273,12 @@ static void rtl_init_eeprom ( struct rtl8139_nic *rtl ) { */ static void rtl_read_mac ( struct rtl8139_nic *rtl, uint8_t *mac_addr ) { - struct spi_device *device = &rtl->eeprom; + struct nvs_device *nvs = &rtl->eeprom.nvs; int i; DBG ( "MAC address is " ); for ( i = EE_MAC ; i < ( EE_MAC + ( ETH_ALEN / 2 ) ) ; i++ ) { - device->type->read ( device, i, mac_addr, 2 ); + nvs_read ( nvs, i, mac_addr, 2 ); DBG ( "%02x%02x", mac_addr[0], mac_addr[1] ); mac_addr += 2; } diff --git a/src/drivers/nvs/nvs.c b/src/drivers/nvs/nvs.c new file mode 100644 index 00000000..55cd8283 --- /dev/null +++ b/src/drivers/nvs/nvs.c @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2006 Michael Brown . + * + * 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 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., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include + +/** @file + * + * Non-volatile storage + * + */ + +int nvs_read ( struct nvs_device *nvs, unsigned int address, + void *data, size_t len ) { + return nvs->read ( nvs, address, data, len ); +} diff --git a/src/drivers/nvs/threewire.c b/src/drivers/nvs/threewire.c index 552c3b46..043cc8fc 100644 --- a/src/drivers/nvs/threewire.c +++ b/src/drivers/nvs/threewire.c @@ -28,14 +28,15 @@ /** Read data from three-wire device * - * @v device SPI device + * @v nvs NVS device * @v address Address from which to read * @v data Data buffer * @v len Length of data buffer * @ret rc Return status code */ -int threewire_read ( struct spi_device *device, unsigned int address, +int threewire_read ( struct nvs_device *nvs, unsigned int address, void *data, size_t len ) { + struct spi_device *device = nvs_to_spi ( nvs ); struct spi_bus *bus = device->bus; assert ( bus->mode == SPI_MODE_THREEWIRE ); diff --git a/src/include/gpxe/nvo.h b/src/include/gpxe/nvo.h new file mode 100644 index 00000000..b51431aa --- /dev/null +++ b/src/include/gpxe/nvo.h @@ -0,0 +1,21 @@ +#ifndef _GPXE_NVO_H +#define _GPXE_NVO_H + +/** @file + * + * Non-volatile stored options + * + */ + +struct nvs_device; +struct dhcp_option_block; + +struct nvs_options { + struct nvs_device *nvs; + struct dhcp_option_block *options; +}; + +extern int nvo_register ( struct nvs_options *nvo ); +extern void nvo_unregister ( struct nvs_options *nvo ); + +#endif /* _GPXE_NVO_H */ diff --git a/src/include/gpxe/nvs.h b/src/include/gpxe/nvs.h index f8a80787..38bc9c55 100644 --- a/src/include/gpxe/nvs.h +++ b/src/include/gpxe/nvs.h @@ -9,22 +9,53 @@ #include -struct nvs_operations; - +/** A non-volatile storage device */ struct nvs_device { - struct dhcp_option_block *options; - size_t len; - struct nvs_operations *op; -}; - -struct nvs_operations { - int ( * read ) ( struct nvs_device *nvs, unsigned int offset, + /** Word length, in bits */ + unsigned int word_len; + /** Device size (in words) */ + unsigned int size; + /** Data block size (in words) + * + * This is the block size used by the device. It must be a + * power of two. Data reads and writes must not cross a block + * boundary. + * + * Many devices allow reads to cross a block boundary, and + * restrict only writes. For the sake of simplicity, we + * assume that the same restriction applies to both reads and + * writes. + */ + unsigned int block_size; + /** Read data from device + * + * @v nvs NVS device + * @v address Address from which to read + * @v data Data buffer + * @v len Length of data buffer + * @ret rc Return status code + * + * Reads may not cross a block boundary. + */ + int ( * read ) ( struct nvs_device *nvs, unsigned int address, void *data, size_t len ); - int ( * write ) ( struct nvs_device *nvs, unsigned int offset, + /** Write data to device + * + * @v nvs NVS device + * @v address Address to which to write + * @v data Data buffer + * @v len Length of data buffer + * @ret rc Return status code + * + * Writes may not cross a block boundary. + */ + int ( * write ) ( struct nvs_device *nvs, unsigned int address, const void *data, size_t len ); }; -extern int nvs_register ( struct nvs_device *nvs ); -extern void nvs_unregister ( struct nvs_device *nvs ); +extern int nvs_read ( struct nvs_device *nvs, unsigned int address, + void *data, size_t len ); +extern int nvs_write ( struct nvs_device *nvs, unsigned int address, + const void *data, size_t len ); #endif /* _GPXE_NVS_H */ diff --git a/src/include/gpxe/spi.h b/src/include/gpxe/spi.h index dba4c743..bf60efc2 100644 --- a/src/include/gpxe/spi.h +++ b/src/include/gpxe/spi.h @@ -7,7 +7,7 @@ * */ -#include +#include /** * @defgroup spicmds SPI commands @@ -75,32 +75,19 @@ /** @} */ -struct spi_device; - /** - * An SPI device type + * An SPI device * - * This data structure represents all the characteristics belonging to - * a particular type of SPI device, e.g. "an Atmel 251024 serial flash", - * or "a Microchip 25040 serial EEPROM". + * This data structure represents a physical SPI device attached to an + * SPI bus. */ -struct spi_device_type { - /** Word length, in bits */ - unsigned int word_len; - /** Device size (in words) */ - unsigned int size; - /** Data block size (in words) - * - * This is the block size used by the device. It must be a - * power of two. Data reads and writes must not cross a block - * boundary. - * - * Many devices allow reads to cross a block boundary, and - * restrict only writes. For the sake of simplicity, we - * assume that the same restriction applies to both reads and - * writes. - */ - unsigned int block_size; +struct spi_device { + /** NVS device */ + struct nvs_device nvs; + /** SPI bus to which device is attached */ + struct spi_bus *bus; + /** Slave number */ + unsigned int slave; /** Command length, in bits */ unsigned int command_len; /** Address length, in bits */ @@ -113,64 +100,18 @@ struct spi_device_type { * commands should be munged in this way. */ unsigned int munge_address : 1; - /** Read data from device - * - * @v device SPI device - * @v address Address from which to read - * @v data Data buffer - * @v len Length of data buffer - * @ret rc Return status code - */ - int ( * read ) ( struct spi_device *device, unsigned int address, - void *data, size_t len ); - /** Write data to device - * - * @v device SPI device - * @v address Address to which to write - * @v data Data buffer - * @v len Length of data buffer - * @ret rc Return status code - */ - int ( * write ) ( struct spi_device *device, unsigned int address, - const void *data, size_t len ); }; -/** - * @defgroup spidevs SPI device types - * @{ - */ - -/** Atmel AT25010 serial EEPROM */ -#define AT25010 { \ - .word_len = 8, \ - .size = 128, \ - .block_size = 8, \ - .command_len = 8, \ - .address_len = 8, \ - } - -/** @} */ - -/** - * An SPI device - * - * This data structure represents a real, physical SPI device attached - * to an SPI controller. It comprises the device type plus - * instantiation-specific information such as the slave number. - */ -struct spi_device { - /** SPI device type */ - struct spi_device_type *type; - /** SPI bus to which device is attached */ - struct spi_bus *bus; - /** Slave number */ - unsigned int slave; -}; +static inline __attribute__ (( always_inline )) struct spi_device * +nvs_to_spi ( struct nvs_device *nvs ) { + return container_of ( nvs, struct spi_device, nvs ); +} /** * An SPI bus * - * + * This data structure represents an SPI bus controller capable of + * issuing commands to attached SPI devices. */ struct spi_bus { /** SPI interface mode diff --git a/src/include/gpxe/spi_bit.h b/src/include/gpxe/spi_bit.h index 46f6af76..04af9136 100644 --- a/src/include/gpxe/spi_bit.h +++ b/src/include/gpxe/spi_bit.h @@ -8,6 +8,7 @@ */ #include +#include /** A bit-bashing SPI bus */ struct spi_bit_basher { diff --git a/src/include/gpxe/threewire.h b/src/include/gpxe/threewire.h index 4277d8ab..cbf6180a 100644 --- a/src/include/gpxe/threewire.h +++ b/src/include/gpxe/threewire.h @@ -22,40 +22,48 @@ /** @} */ +extern int threewire_read ( struct nvs_device *nvs, unsigned int address, + void *data, size_t len ); + /** - * @defgroup spidevs SPI device types + * @defgroup tdevs Three-wire device types * @{ */ -/** Atmel AT93C46 serial EEPROM - * - * @v org Word size (8 or 16) - */ -#define AT93C46( org ) { \ - .word_len = (org), \ - .size = ( 1024 / (org) ), \ - .block_size = 1, \ - .command_len = 3, \ - .address_len = ( ( (org) == 8 ) ? 7 : 6 ), \ - .read = threewire_read, \ - } +static inline __attribute__ (( always_inline )) void +init_at93cx6 ( struct spi_device *device, unsigned int organisation ) { + device->nvs.word_len = organisation; + device->nvs.block_size = 1; + device->command_len = 3, + device->nvs.read = threewire_read; +} -/** Atmel AT93C56 serial EEPROM +/** + * Initialise Atmel AT93C46 serial EEPROM * - * @v org Word size (8 or 16) + * @v device SPI device + * @v organisation Word organisation (8 or 16) */ -#define AT93C56( org ) { \ - .word_len = (org), \ - .size = ( 2048 / (org) ), \ - .block_size = 1, \ - .command_len = 3, \ - .address_len = ( ( (org) == 8 ) ? 9 : 8 ), \ - .read = threewire_read, \ - } +static inline __attribute__ (( always_inline )) void +init_at93c46 ( struct spi_device *device, unsigned int organisation ) { + device->nvs.size = ( 1024 / organisation ); + device->address_len = ( ( organisation == 8 ) ? 7 : 6 ); + init_at93cx6 ( device, organisation ); +} + +/** + * Initialise Atmel AT93C56 serial EEPROM + * + * @v device SPI device + * @v organisation Word organisation (8 or 16) + */ +static inline __attribute__ (( always_inline )) void +init_at93c56 ( struct spi_device *device, unsigned int organisation ) { + device->nvs.size = ( 2048 / organisation ); + device->address_len = ( ( organisation == 8 ) ? 9 : 8 ); + init_at93cx6 ( device, organisation ); +} /** @} */ -extern int threewire_read ( struct spi_device *device, unsigned int address, - void *data, size_t len ); - #endif /* _GPXE_THREEWIRE_H */