352 lines
7.3 KiB
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,
|
|
};
|
|
|