Commit fbd28841 authored by Alessandro Rubini's avatar Alessandro Rubini

kernel/spec-i2c: support fake eeprom images

The module parameter eeprom=  accepts a list of filenames (looked for
in /lib/firmware) to fake eeprom images. The list is in probe order,
which means it is unpredictable if you have more than one spec card
plugged -- but it's the same every time your reload the module.

This is a tool for developers who want to experiment with eeproms, so
this "rude" approach to multi-card setups is not considered an issue.
Signed-off-by: Alessandro Rubini's avatarAlessandro Rubini <rubini@gnudd.com>
parent 3501d908
......@@ -33,7 +33,7 @@
@setchapternewpage off
@set update-month April 2013
@set update-month May 2013
@finalout
......@@ -224,6 +224,20 @@ The module can receive the following parameters to customize its operation:
If not zero, the SDB internal structure of the golden binary
is reported through kernel messages. It is disabled by default.
@item eeprom
The parameter accepts one or more comma-separated filenames,
to be used as fake @sc{eeprom} images for the various cards
in their probe order. The probe order is unpredictable, but it's
always the same when you unload and reload the module. This is
a tool for developers, to allow experimenting with eeprom
images (likely related to @sc{sdb} configurations)
without the need to wear the real i2c device.
Please note that the fake eeprom is only used with the @i{golden}
firmware: mezzanine-specific drivers that read and write the
@sc{eeprom} with their own gateware-specific code will still
access the real I2C device.
@end table
Any mezzanine-specific action must be performed by the driver for the
......
......@@ -24,6 +24,11 @@
static int spec_i2c_dump;
module_param_named(i2c_dump, spec_i2c_dump, int, 0444);
/* The following parameter is to pass fake eeprom images to spec cards */
static char *spec_eeprom[FMC_MAX_CARDS];
static int spec_nr_eeprom;
module_param_array_named(eeprom, spec_eeprom, charp, &spec_nr_eeprom, 0444);
/* Stupid dumping tool */
static void dumpstruct(char *name, void *ptr, int size)
{
......@@ -148,10 +153,14 @@ int mi2c_scan(struct fmc_device *fmc)
int spec_eeprom_read(struct fmc_device *fmc, uint32_t offset,
void *buf, size_t size)
{
struct spec_dev *spec = fmc->carrier_data;
int ret = size;
uint8_t *buf8 = buf;
unsigned char c;
if (spec->flags & SPEC_FLAG_FAKE_EEPROM)
return size; /* no hw access */
if (offset > SPEC_I2C_EEPROM_SIZE)
return -EINVAL;
if (offset + size > SPEC_I2C_EEPROM_SIZE)
......@@ -180,10 +189,23 @@ int spec_eeprom_read(struct fmc_device *fmc, uint32_t offset,
int spec_eeprom_write(struct fmc_device *fmc, uint32_t offset,
const void *buf, size_t size)
{
struct spec_dev *spec = fmc->carrier_data;
int i, busy;
const uint8_t *buf8 = buf;
if (offset > SPEC_I2C_EEPROM_SIZE)
return -EINVAL;
if (offset + size > SPEC_I2C_EEPROM_SIZE)
return -EINVAL;
for(i = 0; i < size; i++) {
/* if (we are using a fake eeprom, don't access hw */
if (spec->flags & SPEC_FLAG_FAKE_EEPROM) {
fmc->eeprom[offset++] = *buf8++;
continue;
}
mi2c_start((fmc));
if(mi2c_put_byte(fmc, fmc->eeprom_addr << 1) < 0) {
......@@ -192,8 +214,7 @@ int spec_eeprom_write(struct fmc_device *fmc, uint32_t offset,
}
mi2c_put_byte(fmc, (offset >> 8) & 0xff);
mi2c_put_byte(fmc, offset & 0xff);
mi2c_put_byte(fmc, *buf8++);
offset++;
mi2c_put_byte(fmc, *buf8);
mi2c_stop(fmc);
do { /* wait until the chip becomes ready */
......@@ -211,6 +232,7 @@ int spec_i2c_init(struct fmc_device *fmc)
struct spec_dev *spec = fmc->carrier_data;
void *buf;
int i, found;
static int eeprom_index;
found = mi2c_scan(fmc);
if (!found) {
......@@ -218,10 +240,29 @@ int spec_i2c_init(struct fmc_device *fmc)
return 0;
}
buf = kmalloc(SPEC_I2C_EEPROM_SIZE, GFP_KERNEL);
buf = kzalloc(SPEC_I2C_EEPROM_SIZE, GFP_KERNEL);
if (!buf)
return -ENOMEM;
if (eeprom_index < spec_nr_eeprom) {
const struct firmware *fw;
/* We got a modparam request to fake an eeprom */
i = request_firmware(&fw, spec_eeprom[eeprom_index],
&spec->pdev->dev);
if (i < 0) {
dev_warn(&spec->pdev->dev,
"Can't load eeprom file \"%s\"\n",
spec_eeprom[eeprom_index]);
} else {
spec->flags |= SPEC_FLAG_FAKE_EEPROM;
memcpy(buf, fw->data, min(fw->size,
SPEC_I2C_EEPROM_SIZE));
}
release_firmware(fw);
eeprom_index++;
}
i = spec_eeprom_read(fmc, 0, buf, SPEC_I2C_EEPROM_SIZE);
if (i != SPEC_I2C_EEPROM_SIZE) {
dev_err(&spec->pdev->dev, "EEPROM read error %i\n", i);
......
......@@ -27,6 +27,7 @@ struct spec_dev {
struct pci_dev *pdev;
struct resource *area[3]; /* bar 0, 2, 4 */
void __iomem *remap[3]; /* ioremap of bar 0, 2, 4 */
unsigned long flags; /* see below */
struct list_head list;
struct fmc_device *fmc;
int irq_count; /* for mezzanine use too */
......@@ -34,6 +35,8 @@ struct spec_dev {
struct gpio_chip *gpio;
};
#define SPEC_FLAG_FAKE_EEPROM 0x00000001
/* Registers for GN4124 access */
enum {
/* page 106 */
......@@ -132,7 +135,7 @@ extern int spec_eeprom_write(struct fmc_device *fmc, uint32_t offset,
/* The eeprom is at address 0x50 */
#define SPEC_I2C_EEPROM_ADDR 0x50
#define SPEC_I2C_EEPROM_SIZE (8 * 1024)
#define SPEC_I2C_EEPROM_SIZE ((size_t)(8 * 1024))
/* Functions in spec-gpio.c */
extern int spec_gpio_init(struct fmc_device *fmc);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment