// SPDX-License-Identifier: GPL-2.0+
/*
   Copyright (c) 2016 Broadcom Corporation
   All Rights Reserved

    
*/

/*
 *  Created on: June 2017
 *      Author: dima.mamut@broadcom.com
 */

/*
 * MDIO driver for BCM96846 6856 and 6878
 */

#include "os_dep.h"
#include "mdio_drv_impl5.h"
#include "dt_access.h"

static uintptr_t mdio_base;
static uintptr_t pMdioMasterSelect;

static int mdio_probe(dt_device_t *pdev)
{
    int ret;

    mdio_base = dt_dev_remap_resource(pdev, 0);
    if (IS_ERR(mdio_base))
    {
        ret = PTR_ERR(mdio_base);
        mdio_base = NULL;
        dev_err(&pdev->dev, "Missing mdio_base entry\n");
        goto Exit;
    }

    pMdioMasterSelect = dt_dev_remap_resource(pdev, 1);
    if (IS_ERR(pMdioMasterSelect))
    {
        ret = PTR_ERR(pMdioMasterSelect);
        pMdioMasterSelect = NULL;
        dev_err(&pdev->dev, "Missing MdioMasterSelect entry\n");
        goto Exit;
    }

    dev_dbg(&pdev->dev, "mdio_base=0x%lx\n", mdio_base);
    dev_dbg(&pdev->dev, "pMdioMasterSelect=0x%lx\n", pMdioMasterSelect);

    dev_info(&pdev->dev, "registered\n");

    return 0;

Exit:
    return ret;
}

#ifdef __UBOOT__
static const struct udevice_id mdio_ids[] = {
#if defined(CONFIG_BCM63146) || defined(CONFIG_BCM4912) 
    { .compatible = "brcm,mdio-sf2" },      //FIXME! redirect mdio-sf2 to mdio5 for now
#else
    { .compatible = "brcm,mdio5" },
#endif
    { /* end of list */ },
};

U_BOOT_DRIVER(brcm_mdio) = {
    .name	= "brcm-mdio",
    .id	= UCLASS_MISC,
    .of_match = mdio_ids,
    .probe = mdio_probe,
};
#else
static const struct of_device_id of_platform_table[] = {
    { .compatible = "brcm,mdio5" },
    { /* end of list */ },
};

static struct platform_driver of_platform_driver = {
    .driver = {
        .name = "brcm-mdio",
        .of_match_table = of_platform_table,
    },
    .probe = mdio_probe,
};
module_platform_driver(of_platform_driver);
#endif

#define MDIO_CMD        (void *)mdio_base

static DEFINE_SPINLOCK(mdio_access);

static void mdio_cfg_type(mdio_type_t type)
{
#if defined(CONFIG_BCM63146) || defined(CONFIG_BCM4912)
    if (type != MDIO_INT)
#else
    if (type == MDIO_INT)
#endif
        *(uint32_t *)pMdioMasterSelect = 0;
    else
        *(uint32_t *)pMdioMasterSelect = 1;
}

int32_t mdio_read_c22_register(mdio_type_t type, uint32_t addr, uint32_t reg, uint16_t *val)
{
    int ret = MDIO_OK;

    spin_lock_bh(&mdio_access);
    mdio_cfg_type(type);
    ret =  mdio_cmd_read_22(MDIO_CMD, addr, reg, val);
    spin_unlock_bh(&mdio_access);

    return ret;
}
EXPORT_SYMBOL(mdio_read_c22_register);

int32_t mdio_write_c22_register(mdio_type_t type, uint32_t addr, uint32_t reg, uint16_t val)
{
    int ret = MDIO_OK;

    spin_lock_bh(&mdio_access);
    mdio_cfg_type(type);
    ret = mdio_cmd_write_22(MDIO_CMD, addr, reg, val);
    spin_unlock_bh(&mdio_access);

    return ret;
}
EXPORT_SYMBOL(mdio_write_c22_register);

int32_t mdio_read_c45_register(mdio_type_t type, uint32_t addr, uint32_t dev, uint16_t reg, uint16_t *val)
{
    int ret = MDIO_OK;

    spin_lock_bh(&mdio_access);
    mdio_cfg_type(type);
    ret = mdio_cmd_read_45(MDIO_CMD, addr, dev, reg, val);
    spin_unlock_bh(&mdio_access);

    return ret;
}
EXPORT_SYMBOL(mdio_read_c45_register);

int32_t mdio_write_c45_register(mdio_type_t type, uint32_t addr, uint32_t dev, uint16_t reg, uint16_t val)
{
    int ret = MDIO_OK;

    spin_lock_bh(&mdio_access);
    mdio_cfg_type(type);
    ret = mdio_cmd_write_45(MDIO_CMD, addr, dev, reg, val);
    spin_unlock_bh(&mdio_access);

    return ret;
}
EXPORT_SYMBOL(mdio_write_c45_register);


