/*
 * Copyright (C) 2015 Jan Luebbe <jluebbe@lasnet.de>
 *
 * This file is part of barebox.
 * See file CREDITS for list of people who contributed to this project.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2
 * as published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 */

#include <common.h>
#include <init.h>
#include <io.h>
#include <globalvar.h>
#include <gpio.h>
#include <envfs.h>
#include <fs.h>
#include <fcntl.h>
#include <libfile.h>
#include <net.h>

#include <linux/sizes.h>
#include <linux/clk.h>
#include <asm/armlinux.h>

#define PINMUX0	0x01c40000
#define PINMUX1	0x01c40004
#define PINMUX1_I2C	BIT(7)

#define VDD3P3V_PWDN			(0x01c40048)

#include <net/davinci_emac.h>

#define EMAC_BASE_ADDR 0x01C80000
#define EMAC_WRAPPER_BASE_ADDR 0x01C81000
#define EMAC_WRAPPER_RAM_ADDR 0x01C82000
#define EMAC_MDIO_BASE_ADDR 0x01C84000

static struct resource dm644x_emac_resources[] = {                               
        {                                                                        
                .start  = EMAC_BASE_ADDR,                                        
                .end    = EMAC_BASE_ADDR + 0xff,                                 
                .flags  = IORESOURCE_MEM,                                        
        }, {                                                                     
                .start  = EMAC_WRAPPER_BASE_ADDR,                                
                .end    = EMAC_WRAPPER_BASE_ADDR + 0xfff,                        
                .flags  = IORESOURCE_MEM,                                        
        }, {                                                                     
                .start  = EMAC_MDIO_BASE_ADDR,                                   
                .end    = EMAC_MDIO_BASE_ADDR + 0xff,                            
                .flags  = IORESOURCE_MEM,                                        
        }, {                                                                     
                .start  = EMAC_WRAPPER_RAM_ADDR,                                 
                .end    = EMAC_WRAPPER_RAM_ADDR + 0x1fff, /* 8kB */              
                .flags  = IORESOURCE_MEM,                                        
        }                                                                        
};                                                                               
                                                                                 
static struct davinci_emac_platform_data dm644x_emac_pdata = {
	.force_link = false,
	.interface_rmii = false,
	.phy_addr = 1,
};

static struct device_d dm644x_emac_device = {
	.id = DEVICE_ID_DYNAMIC,
	.name = "davinci_emac",
	.num_resources = ARRAY_SIZE(dm644x_emac_resources),
	.resource = dm644x_emac_resources,
	.platform_data = &dm644x_emac_pdata,
};

static void sysmobts_config_eeprom(const char *compatible)
{
	struct device_node *node;

	node = of_find_node_by_path("/i2c/eeprom@50");
	if (!node) {
		pr_err("can't find eeprom node to configure\n");
		return;
	}
	of_set_property(node, "compatible", compatible, strlen(compatible) + 1,
			1);
	of_device_enable(node);
}

static void sysmobts_board_detect(void)
{
	int board_ver, board_cfg;
	char variant[4];

	board_ver = gpio_get_value(15);
	board_ver |= gpio_get_value(16) << 1;
	board_ver |= gpio_get_value(17) << 2;

	board_cfg = gpio_get_value(10);
	board_cfg |= gpio_get_value(11) << 1;
	board_cfg |= gpio_get_value(12) << 3;
	board_cfg |= gpio_get_value(13) << 4;
	board_cfg |= gpio_get_value(14) << 5;

	variant[0] = 'A' + board_ver;
	variant[1] = '.';
	variant[2] = '0' + board_cfg;
	variant[3] = '\0';

	globalvar_add_simple("board.variant", variant);

	printf("detected 'sysmobts_v2 %s'\n", variant);

	/* enable the correct eeprom */
	if (board_ver <= 2) {
		sysmobts_config_eeprom("24c02");
	} else {
		sysmobts_config_eeprom("24c64");
	}

	/* disable nand write protect */
	if (board_ver >= 5) {
		gpio_direction_output(33, 1);
		udelay(100);
	}
}

static int sysmobts_set_ethaddr(void)
{
	char addr[6];
	int fd, ret;

	fd = open("/dev/eeprom0", O_RDONLY);
	if (fd < 0) {
		ret = fd;
		goto err;
	}

	ret = read_full(fd, addr, 6);
	if (ret < 0)
		goto err_open;

	eth_register_ethaddr(0, addr);

	ret = 0;

err_open:
	close(fd);
err:
	if (ret)
		pr_err("can't read eeprom /dev/eeprom0 (%s)\n", strerror(ret));

	return ret;
}

#define MACH_TYPE_SYSMOBTS_V2	       3758

static void sysmobts_devices_shutdown(void)
{
	writel(readl(PINMUX1) | PINMUX1_I2C, PINMUX1);
}

static int sysmobts_coredevices_init(void)
{
	board_shutdown = sysmobts_devices_shutdown;

	writel(0, VDD3P3V_PWDN);

	writel(0x8000000f, PINMUX0);
	writel(0x00050187, PINMUX1);

	writel(readl(PINMUX1) & ~PINMUX1_I2C, PINMUX1);

	sysmobts_board_detect();

	return 0;
}

coredevice_initcall(sysmobts_coredevices_init);

#define DAVINCI_PLLM      (0x01C40910)  /* PLL 1 Multiplier */
#define DAVINCI_AWCCR     (0x01E00004)  /* EMIF-A async wait cycle config register. */
#define DAVINCI_AWCCR_VAL (0x000000FF)  /* EMIF-A async wait cycle config register value. */
#define DAVINCI_A2CR      (0x01E00014)  /* EMIF-A CS3 config register. */
#define DAVINCI_A2CR_VAL  (0x00430491)  /* EMIF-A CS3 value for FPGA.  */
#define DAVINCI_A2CR_VAL8 (0x00630591)  /* EMIF-A CS3 value for FPGA.  */

static int sysmobts_devices_init(void)
{
	struct clk *dsp_clk;
	int ret;

	/* Configure AEMIF AWCCR */
	writel(DAVINCI_AWCCR_VAL, DAVINCI_AWCCR);

	/* DM644X @ 594/297 MHz */
	if ( (readl(DAVINCI_PLLM) & 0x0FF) < 22 ) {
		/* Configure AEMIF CS3 (fpga) */
		writel(DAVINCI_A2CR_VAL, DAVINCI_A2CR);

	/* DM644X @ 810/405 MHz */
	} else {
		/* Configure AEMIF CS3 (fpga) */
		writel(DAVINCI_A2CR_VAL8, DAVINCI_A2CR);
	}

	sysmobts_set_ethaddr();
	platform_device_register(&dm644x_emac_device);

	defaultenv_append_directory(defaultenv_sysmobts);

	armlinux_set_architecture(MACH_TYPE_SYSMOBTS_V2);

	dsp_clk = clk_get(NULL, "dsp");
        if (IS_ERR(dsp_clk)) {
                ret = PTR_ERR(dsp_clk);
                pr_err("unable to get DSP clock, err %d\n", ret);
		return 1;
        }

        ret = clk_enable(dsp_clk);
        if (ret < 0) {
                pr_err("unable to enable DSP clock, err %d\n", ret);
		return 1;
        }

	return 0;
}

device_initcall(sysmobts_devices_init);
