// SPDX-License-Identifier: GPL-2.0+
/*
 * clk-synthesizer.c
 *
 * Clock synthesizer apis
 *
 * Copyright (C) 2016, Texas Instruments, Incorporated - https://www.ti.com/
 */

#include <stdio.h>
#include <asm/arch/clk_synthesizer.h>
#include <i2c.h>

/**
 * clk_synthesizer_reg_read - Read register from synthesizer.
 * dev:		i2c bus device (not used if CONFIG_DM_I2C is not set)
 * @addr:	addr within the i2c device
 * buf:		Buffer to which value is to be read.
 *
 * For reading the register from this clock synthesizer, a command needs to
 * be send along with enabling byte read more, and then read can happen.
 * Returns 0 on success
 */
static int clk_synthesizer_reg_read(struct udevice *dev, int addr, u8 *buf)
{
	int rc;

	/* Enable Bye read */
	addr = addr | CLK_SYNTHESIZER_BYTE_MODE;

#if !CONFIG_IS_ENABLED(DM_I2C)
	/* Send the command byte */
	rc = i2c_write(CONFIG_CLK_SYNTHESIZER_I2C_ADDR, addr, 1, buf, 1);
	if (rc)
		printf("Failed to send command to clock synthesizer\n");

	/* Read the Data */
	return i2c_read(CONFIG_CLK_SYNTHESIZER_I2C_ADDR, addr, 1, buf, 1);
#else
	/* Send the command byte */
	rc = dm_i2c_reg_write(dev, addr, *buf);
	if (rc)
		printf("Failed to send command to clock synthesizer\n");

	/* Read the Data */
	rc = dm_i2c_reg_read(dev, addr);
	if (rc < 0)
		return rc;

	*buf = (u8)rc;
	return 0;
#endif

}

/**
 * clk_synthesizer_reg_write - Write a value to register in synthesizer.
 * dev:		i2c bus device (not used if CONFIG_DM_I2C is not set)
 * @addr:	addr within the i2c device
 * val:		Value to be written in the addr.
 *
 * Enable the byte read mode in the address and start the i2c transfer.
 * Returns 0 on success
 */
static int clk_synthesizer_reg_write(struct udevice *dev, int addr, u8 val)
{
	u8 cmd[2];
	int rc = 0;

	/* Enable byte write */
	cmd[0] = addr | CLK_SYNTHESIZER_BYTE_MODE;
	cmd[1] = val;

#if !CONFIG_IS_ENABLED(DM_I2C)
	rc = i2c_write(CONFIG_CLK_SYNTHESIZER_I2C_ADDR, addr, 1, cmd, 2);
#else
	rc = dm_i2c_write(dev, addr, cmd, 2);
#endif
	if (rc)
		printf("Clock synthesizer reg write failed at addr = 0x%x\n",
		       addr);
	return rc;
}

/**
 * setup_clock_syntherizer - Program the clock synthesizer to get the desired
 *				frequency.
 * @data: Data containing the desired output
 *
 * This is a PLL-based high performance synthesizer which gives 3 outputs
 * as per the PLL_DIV and load capacitor programmed.
 */
int setup_clock_synthesizer(struct clk_synth *data)
{
	int rc;
	u8 val = 0;
	struct udevice *dev = NULL;
#if !CONFIG_IS_ENABLED(DM_I2C)
	rc =  i2c_probe(CONFIG_CLK_SYNTHESIZER_I2C_ADDR);
	if (rc) {
		printf("i2c probe failed at address 0x%x\n",
		       CONFIG_CLK_SYNTHESIZER_I2C_ADDR);
		return rc;
	}
#else
	rc = i2c_get_chip_for_busnum(0, CONFIG_CLK_SYNTHESIZER_I2C_ADDR, 1, &dev);
	if (rc) {
		printf("failed to get device for synthesizer at address 0x%x\n",
		       CONFIG_CLK_SYNTHESIZER_I2C_ADDR);
		return rc;
	}
#endif
	rc = clk_synthesizer_reg_read(dev, CLK_SYNTHESIZER_ID_REG, &val);
	if (val != data->id)
		return rc;

	/* Crystal Load capacitor selection */
	rc = clk_synthesizer_reg_write(dev, CLK_SYNTHESIZER_XCSEL,
				       data->capacitor);
	if (rc)
		return rc;
	rc = clk_synthesizer_reg_write(dev, CLK_SYNTHESIZER_MUX_REG,
				       data->mux);
	if (rc)
		return rc;
	rc = clk_synthesizer_reg_write(dev, CLK_SYNTHESIZER_PDIV2_REG,
				       data->pdiv2);
	if (rc)
		return rc;
	rc = clk_synthesizer_reg_write(dev, CLK_SYNTHESIZER_PDIV3_REG,
				       data->pdiv3);
	if (rc)
		return rc;

	return 0;
}
