Newer
Older
/*
* Copyright (C) 2012 CERN (www.cern.ch)
* Author: Alessandro Rubini <rubini@gnudd.com>
*
* Released according to the GNU GPL, version 2 or any later version.
*
* This work is part of the White Rabbit project, a research effort led
* by CERN, the European Institute for Nuclear Research.
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/firmware.h>
#include <linux/delay.h>
#include "spec-nic.h"
#include "wr_nic/wr-nic.h"
static struct fmc_driver wrn_drv;
FMC_PARAM_BUSID(wrn_drv);
FMC_PARAM_GATEWARE(wrn_drv);
static char *wrn_filename = WRN_GATEWARE_DEFAULT_NAME;
module_param_named(file, wrn_filename, charp, 0444);
static char *wrn_wrc_filename = WRN_WRC_DEFAULT_NAME;
module_param_named(wrc, wrn_wrc_filename, charp, 0444);
static int wrn_show_sdb;
module_param_named(show_sdb, wrn_show_sdb, int, 0444);
static int wrn_load_wrc(struct fmc_device *fmc, char *name,
unsigned long ram, unsigned long ramsize)
{
const struct firmware *fw;
struct device *dev = fmc->hwdev;
int ret, count;
const uint32_t *p;
ret = request_firmware(&fw, name, dev);
if (ret < 0) {
dev_err(dev, "request firmware \"%s\": error %i\n",
name, ret);
return ret;
}
if (fw->size > ramsize) {
dev_err(dev, "firmware \"%s\" longer than ram (0x%lx)\n",
name, ramsize);
ret = -ENOMEM;
goto out;
}
for (count = 0, p = (void *)fw->data; count < fw->size; count += 4, p++)
fmc_writel(fmc, __cpu_to_be32(*p), ram + count);
out:
release_firmware(fw);
return ret;
}
int index, need_wrc = 0, ret = 0;
signed long ram, syscon;
unsigned long ramsize;
char *filename;
/* We accept busid= to limit to some spec only */
index = fmc->op->validate(fmc, &wrn_drv);
if (index < 0) {
dev_info(fmc->hwdev, "not using \"%s\" according to "
"modparam\n", KBUILD_MODNAME);
return -ENODEV;
}
/* Driver data */
dd = devm_kzalloc(&fmc->dev, sizeof(*dd), GFP_KERNEL);
if (!dd)
return -ENOMEM;
fmc_set_drvdata(fmc, dd);
/*
* We first write a new binary within the spec. If the user
* passed "gateware=", use the per-board names instead of the
* global name
*/
if (wrn_drv.gw_n)
ret = fmc_reprogram(fmc, &wrn_drv, "", WRN_SDB_ADDR);
ret = fmc_reprogram(fmc, &wrn_drv, wrn_filename, WRN_SDB_ADDR);
if (ret == -ESRCH) {
dev_info(fmc->hwdev, "%s: no gateware at index %i\n",
KBUILD_MODNAME, index);
return -ENODEV;
}
dev_err(dev, "write firmware \"%s\": error %i\n",
wrn_filename, ret);
dev_info(dev, "Gateware successfully loaded\n");
/* FIXME: remove this parameter, fmc.ko shows it already */
if (wrn_show_sdb)
fmc_show_sdb_tree(fmc);
/*
* The gateware may not be including the WRC code, or the
* user may have asked for a specific file name. If so, load.
*/
ram = fmc_find_sdb_device(fmc->sdb, SDB_CERN, WRN_SDB_RAM, &ramsize);
syscon = fmc_find_sdb_device(fmc->sdb, SDB_CERN, WRN_SDB_SYSCON, NULL);
if (ram >= 0 && fmc_readl(fmc, ram) != 0x98000000)
filename = wrn_wrc_filename;
if (strcmp(wrn_wrc_filename, WRN_WRC_DEFAULT_NAME)) {
need_wrc = 1;
/*
* If the user changed it, use the new name.
* But "1" means "do load the default"
*/
if (!strcmp(wrn_wrc_filename, "1"))
filename = WRN_WRC_DEFAULT_NAME;
}
if (need_wrc && ((ram < 0) || (syscon < 0))) {
dev_err(dev, "can't reprogram WRC: SDB failure\n");
goto out;
}
if (need_wrc) {
unsigned long j = jiffies + HZ/2;
fmc_writel(fmc, 0x1deadbee, syscon);
while ( !(fmc_readl(fmc, syscon) & (1 << 28)) )
if (time_after(jiffies, j))
break;
if (time_after(jiffies, j)) {
dev_err(dev, "can't reset LM32\n");
fmc_writel(fmc, 0x0deadbee, syscon);
goto out;
}
ret = wrn_load_wrc(fmc, filename, ram, ramsize);
fmc_writel(fmc, 0x0deadbee, syscon);
if (ret)
goto out; /* message already reported */
if (fmc_readl(fmc, ram) != 0x98000000)
dev_warn(dev, "possible failure in wrc load\n");
else
dev_info(dev, "WRC program reloaded from \"%s\"\n",
filename);
/* After the LM32 started, give it time to set up */
msleep(200);
/* Register the gpio stuff, if we have kernel support */
ret = wrn_gpio_init(fmc);
if (ret < 0)
/* The network device */
ret = wrn_eth_init(fmc);
if (ret < 0)
int wrn_fmc_remove(struct fmc_device *fmc)
{
wrn_eth_exit(fmc);
wrn_gpio_exit(fmc);
static struct fmc_fru_id fd_fru_id[] = {
{
.product_name = "FmcDio5cha",
},
};
.probe = wrn_fmc_probe,
.remove = wrn_fmc_remove,
.id_table = {
.fru_id = fd_fru_id,
.fru_id_nr = ARRAY_SIZE(fd_fru_id),
},
};
static int wrn_init(void)
{
int ret;
ret = fmc_driver_register(&wrn_fmc_drv);
return ret;
platform_driver_register(&wrn_driver);
if (ret < 0)
fmc_driver_unregister(&wrn_fmc_drv);
return ret;
}
static void wrn_exit(void)
{
fmc_driver_unregister(&wrn_fmc_drv);
}
module_init(wrn_init);
module_exit(wrn_exit);
/* If no gpio lib is there, this weak applies */
int __weak wrn_gpio_init(struct fmc_device *fmc)
{
}
void __weak wrn_gpio_exit(struct fmc_device *fmc)
{
}
MODULE_VERSION(GIT_VERSION);
ADDITIONAL_VERSIONS;