u-boot/board/nm/nbhw18_v1/nbhw_fpga_config.c

352 lines
7.3 KiB
C

#undef DEBUG
#include <common.h>
#include <dm.h>
#include <dm/device.h>
#include <dm/ofnode.h>
#include <asm/gpio.h>
#include <asm/io.h>
#include <errno.h>
#include "../common/nbhw_bd.h"
DECLARE_GLOBAL_DATA_PTR;
struct gpio_desc leds[16];
struct udevice *driver_dev;
struct pcieslot {
struct gpio_desc reset;
struct gpio_desc power;
struct gpio_desc wdis_out;
struct gpio_desc wdis;
};
struct config_priv {
struct pcieslot pcieslots[4];
u32 led_count;
} priv;
typedef enum {
TYPE_USB,
TYPE_USB3,
TYPE_PCIE,
} slot_type_t;
static slot_type_t get_pcie_slot_type(const int slot)
{
int module;
char pdValue[200];
char slotDescr[20];
sprintf(slotDescr, "slot=%d", slot);
for (module=0; module<4; module++) {
strcpy(pdValue, "" ); /*init with an empty string*/
if (bd_get_pd_module(module, pdValue, sizeof(pdValue))==0) {
if ((strstr(pdValue, slotDescr)) && (strstr(pdValue, "wlan-"))) {
/* Wifi module needs PCIe */
return TYPE_PCIE;
}
}
}
return TYPE_USB;
}
static int serdes_en_hack(struct gpio_desc *gpio)
{
slot_type_t slot_type = get_pcie_slot_type(0);
/* Lucky for us pcie slot1 has some muxes, to workaround buggy Sierra Wireless Modules */
if (slot_type == TYPE_USB) {
/* Buggy Sierra Wireless Modules don't like to have USB3 enabled
* because the TX of the USB3 Lane is attached to it's system
* reset which is the default (see dts) */
printf("Slot1: wwan\n");
}
else if (slot_type == TYPE_PCIE) {
dm_gpio_set_value(gpio, 1);
printf("Slot1: wlan\n");
}
else {
/* If once we would use other buggy Sierra Wireless modules, where they have
* decided to use USB3 too, then we have to enable the SERDES, the reset signal
* was moved away from this pins again (bravo!!!) */
printf("Slot1: wwan (usb3)\n");
}
return 0;
}
struct pcie_slot_gpios {
struct gpio_desc reset;
struct gpio_desc power;
struct gpio_desc wdis_out;
struct gpio_desc wdis;
};
#define PCIE_SLOT_COUNT 8
static struct pcie_slot_gpios pcie_slots[PCIE_SLOT_COUNT];
static int pcie_slot_count = 0;
static int request_and_set_gpio_by_name(ofnode fdt,
const char *name, struct gpio_desc *desc)
{
int default_value = 0;
u32 gpio_array[4];
debug("%s\n", __func__);
/* Request the gpio described by the property */
if (gpio_request_by_name_nodev(fdt, name, 0, desc,
GPIOD_IS_OUT))
{
printf("Could not request gpio %s\n", name);
return -1;
}
/* Get the gpio array, to find out its default value (4 index) */
if (ofnode_read_u32_array(fdt, name, gpio_array, 4)) {
printf("Could not request gpio array %s\n", name);
return -1;
}
default_value = gpio_array[3];
debug("Set GPIO %s to %d\n", name, default_value);
dm_gpio_set_value(desc, default_value);
return 0;
}
static int add_pcie_slot(ofnode fdt)
{
debug("%s\n", __func__);
request_and_set_gpio_by_name(fdt, "reset",
&pcie_slots[pcie_slot_count].reset);
request_and_set_gpio_by_name(fdt, "power",
&pcie_slots[pcie_slot_count].power);
request_and_set_gpio_by_name(fdt, "wdis-out",
&pcie_slots[pcie_slot_count].wdis_out);
request_and_set_gpio_by_name(fdt, "wdis",
&pcie_slots[pcie_slot_count].wdis);
pcie_slot_count++;
return 0;
}
static int configure_pcie_slots(void)
{
int i;
udelay(1200000); /* 1.2 s */
for (i = 0; i < pcie_slot_count;i ++) {
dm_gpio_set_value(&pcie_slots[i].reset, 1);
}
for (i = 0; i < pcie_slot_count;i ++) {
dm_gpio_set_value(&pcie_slots[i].wdis_out, 1);
dm_gpio_set_value(&pcie_slots[i].wdis, 0);
udelay(1000); /* 1 ms */
}
/* Apply power to all PCIe slots */
for (i = 0; i < pcie_slot_count;i ++) {
dm_gpio_set_value(&pcie_slots[i].power, 1);
udelay(200000); /* 200 ms */
}
for (i = 0; i < pcie_slot_count;i ++) {
dm_gpio_set_value(&pcie_slots[i].wdis_out, 1);
dm_gpio_set_value(&pcie_slots[i].wdis, 0);
udelay(1000); /* 1 ms */
}
udelay(12000); /* 12 ms */
for (i = 0; i < pcie_slot_count;i ++) {
dm_gpio_set_value(&pcie_slots[i].reset, 0);
}
return 0;
}
static int configure_leds(void)
{
int i;
int led_count;
led_count = gpio_request_list_by_name(driver_dev, "leds", leds,
ARRAY_SIZE(leds), GPIOD_IS_OUT);
dm_gpio_set_value(&leds[0], 1);
for (i = 1; i < ARRAY_SIZE(leds); i++) {
dm_gpio_set_value(&leds[i], 0);
}
priv.led_count = led_count;
return 0;
}
struct hack_list_entry {
const char* name;
int (*fn)(struct gpio_desc *gpio);
};
struct hack_list_entry hack_list[] = {
{"serdes-en", serdes_en_hack}
};
static int exec_hack_list_fn(const char *name, struct gpio_desc *gpio)
{
int i;
for (i = 0; i < ARRAY_SIZE(hack_list); i++) {
if (strcmp(hack_list[i].name, name) == 0) {
if (hack_list[i].fn(gpio)) {
printf("Hack for %s failed\n", name);
return -1;
}
return 0;
}
}
/* Not part of hacklist */
return 0;
}
static int request_and_set_gpios(ofnode fdt,
struct gpio_desc *gpios, u32 max_gpios)
{
int i = 0;
int default_value = 0;
int offset = 0;
for (offset = fdt_first_property_offset(gd->fdt_blob, ofnode_to_offset(fdt));
offset >= 0;
offset = fdt_next_property_offset(gd->fdt_blob, offset)) {
u32 gpio_array[4];
const char *name;
const struct fdt_property *prop;
if (i >= max_gpios) {
printf("Too many gpio entries (max %d)\n", max_gpios);
break;
}
prop = fdt_get_property_by_offset(gd->fdt_blob, offset, NULL);
name = fdt_string(gd->fdt_blob, fdt32_to_cpu(prop->nameoff));
debug("Name: %s\n", name);
if (name[0] == '#') {
continue;
}
/* Request the gpio descriped by the property */
if (gpio_request_by_name_nodev(fdt, name, 0, &gpios[i],
GPIOD_IS_OUT))
{
printf("Could not request gpio %s\n", name);
return -1;
}
/* Get the gpio array, to find out its default value (4 index) */
if (ofnode_read_u32_array(fdt, name,
gpio_array, 4)) {
printf("Could not request gpio array %s\n", name);
return -1;
}
default_value = gpio_array[3];
debug("Set GPIO %s to %d\n", name, default_value);
dm_gpio_set_value(&gpios[i], default_value);
if (exec_hack_list_fn(name, &gpios[i])) {
printf("Execution of hack for %s failed\n", name);
continue;
}
i++;
}
debug("leave %s with %d gpios\n", __func__, i);
return i;
}
static int configure_misc(ofnode fdt)
{
struct gpio_desc misc_gpios[16];
int gpio_count;
debug("%s\n", __func__);
gpio_count = request_and_set_gpios(fdt,
misc_gpios, ARRAY_SIZE(misc_gpios));
if (gpio_count < 1) {
return -1;
}
debug("Free gpios\n");
/* Free gpios so that we could use them via gpio subsystem */
gpio_free_list_nodev(misc_gpios, gpio_count);
debug("return %s\n", __func__);
return 0;
}
int nbhw_fpga_configure(void)
{
ofnode subnode;
ofnode node = driver_dev->node;
debug("%s\n", __func__);
ofnode_for_each_subnode(subnode, node) {
const char *name;
name = ofnode_get_name(subnode);
debug("Try to configure %s\n", name);
if (!strncmp("misc", name, 4)) {
configure_misc(subnode);
}
if (!strncmp("pcieslot", name, 4)) {
add_pcie_slot(subnode);
}
}
if (configure_leds())
return -1;
if (configure_pcie_slots()) {
return -1;
}
return 0;
}
static int nbhw_fpga_config_bind(struct udevice *dev)
{
debug("%s\n", __func__);
driver_dev = dev;
return 0;
}
static const struct udevice_id nbhw_fpga_config_ids[] = {
{ .compatible = "nm,nbhw18-fpga-config" },
{ }
};
U_BOOT_DRIVER(gpio_nbhw_fpga_config) = {
.name = "nbhw18_fpga_config",
.id = UCLASS_ROOT,
.of_match = nbhw_fpga_config_ids,
.bind = nbhw_fpga_config_bind,
};