Loading...
// SPDX-License-Identifier: GPL-2.0+
/*
 * Copyright (C) 2010-2020 CS Group
 * Charles Frey <charles.frey@c-s.fr>
 * Florent Trinh Thai <florent.trinh-thai@c-s.fr>
 * Christophe Leroy <christophe.leroy@c-s.fr>
 *
 * Common specific routines for the CS Group boards
 */

#include <dm.h>
#include <env.h>
#include <fdt_support.h>
#include <hang.h>
#include <spi.h>
#include <linux/delay.h>
#include <asm/io.h>

#include "common.h"

#define ADDR_FPGA_R_BASE		((unsigned char  __iomem *)CONFIG_FPGA_BASE)

#define FPGA_R_ACQ_AL_FAV	0x04

#define TYPE_MCR			0x22
#define TYPE_MIAE			0x23

#define FAR_CASRSA     2
#define FAR_VGOIP      4
#define FAV_CLA        7
#define FAV_SRSA       8

#define SPI_EEPROM_READ	0x03

static int fdt_set_node_and_value(void *blob, char *node, const char *prop,
				  void *var, int size)
{
	int ret, off;

	off = fdt_path_offset(blob, node);

	if (off < 0) {
		printf("Cannot find %s node err:%s\n", node, fdt_strerror(off));

		return off;
	}

	ret = fdt_setprop(blob, off, prop, var, size);

	if (ret < 0)
		printf("Cannot set %s/%s prop err: %s\n", node, prop, fdt_strerror(ret));

	return ret;
}

/* Checks front/rear id and remove unneeded nodes from the blob */
static void ft_cleanup(void *blob, unsigned long id, const char *prop, const char *compatible)
{
	int off;

	off = fdt_node_offset_by_compatible(blob, -1, compatible);

	while (off != -FDT_ERR_NOTFOUND) {
		const struct fdt_property *ids;
		int nb_ids, idx;
		int tmp = -1;

		ids = fdt_get_property(blob, off, prop, &nb_ids);

		for (idx = 0; idx < nb_ids; idx += 4) {
			if (*((uint32_t *)&ids->data[idx]) == id)
				break;
		}

		if (idx >= nb_ids)
			fdt_del_node(blob, off);
		else
			tmp = off;

		off = fdt_node_offset_by_compatible(blob, tmp, compatible);
	}

	fdt_set_node_and_value(blob, "/", prop, &id, sizeof(uint32_t));
}

int read_eeprom(u8 *din, int len)
{
	struct udevice *eeprom;
	struct spi_slave *slave;
	uchar dout[3] = {SPI_EEPROM_READ, 0, 0};
	int ret;

	ret = uclass_get_device(UCLASS_SPI, 0, &eeprom);
	if (ret)
		return ret;

	ret = _spi_get_bus_and_cs(0, 0, 1000000, 0, "spi_generic_drv",
				  "generic_0:0", &eeprom, &slave);
	if (ret)
		return ret;

	ret = spi_claim_bus(slave);

	ret = spi_xfer(slave, sizeof(dout) << 3, dout, NULL, SPI_XFER_BEGIN);
	if (ret)
		return ret;

	ret = spi_xfer(slave, len << 3, NULL, din, SPI_XFER_END);
	if (ret)
		return ret;

	spi_release_bus(slave);

	return 0;
}

int ft_board_setup_common(void *blob)
{
	u8 far_id, fav_id;

	if (in_8(ADDR_FPGA_R_BASE) != TYPE_MIAE)
		return 0;

	far_id = in_8(ADDR_FPGA_R_BASE + 0x43) >> 5;
	ft_cleanup(blob, far_id, "far-id", "cs,mia-far");

	fav_id = in_8(ADDR_FPGA_R_BASE + 0x44) >> 5;

	if (far_id == FAR_CASRSA && fav_id == FAV_CLA)
		fav_id = FAV_SRSA;

	ft_cleanup(blob, fav_id, "fav-id", "cs,mia-fav");

	if (far_id == FAR_CASRSA)
		ft_board_setup_phy3();

	return 0;
}

int checkboard_common(void)
{
	switch (in_8(ADDR_FPGA_R_BASE)) {
		int far_id;
	case TYPE_MCR:
		printf("MCR3000_2G (CS GROUP)\n");
		break;
	case TYPE_MIAE:
		far_id = in_8(ADDR_FPGA_R_BASE + 0x43) >> 5;

		if (far_id == FAR_VGOIP)
			printf("VGoIP (CS GROUP)\n");
		else
			printf("MIAE (CS GROUP)\n");

		break;
	default:
		printf("Unknown\n");
		for (;;)
			;
		break;
	}
	return 0;
}

void misc_init_r_common(void)
{
	u8 tmp, far_id, addr;
	int count = 3;

	switch (in_8(ADDR_FPGA_R_BASE)) {
	case TYPE_MCR:
		/* if at boot alarm button is pressed, delay boot */
		if ((in_8(ADDR_FPGA_R_BASE + 0x31) & FPGA_R_ACQ_AL_FAV) == 0)
			env_set("bootdelay", "60");

		addr = in_8(ADDR_FPGA_R_BASE + 0x43);
		printf("Board address: 0x%2.2x (System %d Rack %d Slot %d)\n",
		       addr, addr >> 7, (addr >> 4) & 7, addr & 15);

		env_set("config", CFG_BOARD_MCR3000_2G);
		env_set("hostname", CFG_BOARD_MCR3000_2G);
		break;

	case TYPE_MIAE:
		do {
			tmp = in_8(ADDR_FPGA_R_BASE + 0x41);
			count--;
			mdelay(10); /* 10msec wait */
		} while (count && tmp != in_8(ADDR_FPGA_R_BASE + 0x41));

		if (!count) {
			printf("Cannot read the reset factory switch position\n");
			hang();
		}

		if (tmp & 0x1)
			env_set_default("Factory settings switch ON", 0);

		env_set("config", CFG_BOARD_MIAE);
		far_id = in_8(ADDR_FPGA_R_BASE + 0x43) >> 5;

		if (far_id == FAR_VGOIP)
			env_set("hostname", CFG_BOARD_VGOIP);
		else
			env_set("hostname", CFG_BOARD_MIAE);
		break;

	default:
		env_set("config", CFG_BOARD_CMPCXXX);
		env_set("hostname", CFG_BOARD_CMPCXXX);
		break;
	}
}

static void iop_setup_fpgam_common(void)
{
	u8 far_id = in_8(ADDR_FPGA_R_BASE + 0x43) >> 5;

	if (far_id == FAR_CASRSA) {
		/*
		 * PFDIR[15]  = 0 [0x01]
		 * PFDIR[14]  = 1 [0x02]
		 * PFDIR[13]  = 1 [0x04]
		 */
		clrsetbits_8(ADDR_FPGA_R_BASE + 0x37, 0x01, 0x06);
		/*
		 * PFODR[15]  = 1 [0x01]
		 * PFODR[14]  = 0 [0x02]
		 * PFODR[13]  = 0 [0x04]
		 */
		clrsetbits_8(ADDR_FPGA_R_BASE + 0x39, 0x06, 0x01);
		/*
		 * PFDAT[15]  = 0 [0x01]
		 * PFDAT[14]  = 1 [0x02]
		 * PFDAT[13]  = 1 [0x04]
		 * PFDAT[12]  = 1 [0x08]
		 */
		clrsetbits_8(ADDR_FPGA_R_BASE + 0x3B, 0x01, 0x0E);

		/* Setup TOR_OUT */
		out_8(ADDR_FPGA_R_BASE + 0x32, 0x2A);
	}
}

void iop_setup_common(void)
{
	u8 type = in_8(ADDR_FPGA_R_BASE);

	if (type == TYPE_MCR) {
		iop_setup_mcr();
	} else if (type == TYPE_MIAE) {
		iop_setup_miae();
		iop_setup_fpgam_common();
	}
}