<commit_before>/* Copyright (c) 2013 The Chromium OS Authors. All rights reserved.
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

#include "chipset.h"
#include "clock.h"
#include "common.h"
#include "console.h"
#include "gpio.h"
#include "hooks.h"
#include "host_command.h"
#include "i2c.h"
#include "registers.h"
#include "system.h"
#include "task.h"
#include "timer.h"
#include "usb_pd_tcpc.h"
#include "usb_pd_tcpm.h"
#include "util.h"

/* Console output macros */
#define CPUTS(outstr) cputs(CC_I2C, outstr)
#define CPRINTS(format, args...) cprints(CC_I2C, format, ## args)

/* Transmit timeout in microseconds */
#define I2C_TX_TIMEOUT_MASTER	(10 * MSEC)

/*
 * Max data size for a version 3 request/response packet. This is
 * big enough for EC_CMD_GET_VERSION plus header info.
 */
#define I2C_MAX_HOST_PACKET_SIZE 128

#ifdef CONFIG_HOSTCMD_I2C_SLAVE_ADDR
#if (I2C_PORT_EC == STM32_I2C1_PORT)
#define IRQ_SLAVE STM32_IRQ_I2C1
#else
#define IRQ_SLAVE STM32_IRQ_I2C2
#endif
#endif


/* I2C port state data */
struct i2c_port_data {
	uint32_t timeout_us;    /* Transaction timeout, or 0 to use default */
};
static struct i2c_port_data pdata[I2C_PORT_COUNT];

void i2c_set_timeout(int port, uint32_t timeout)
{
	pdata[port].timeout_us = timeout ? timeout : I2C_TX_TIMEOUT_MASTER;
}

/**
 * Wait for ISR register to contain the specified mask.
 *
 * Returns EC_SUCCESS, EC_ERROR_TIMEOUT if timed out waiting, or
 * EC_ERROR_UNKNOWN if an error bit appeared in the status register.
 */
static int wait_isr(int port, int mask)
{
	uint64_t timeout = get_time().val + pdata[port].timeout_us;

	while (get_time().val < timeout) {
		int isr = STM32_I2C_ISR(port);

		/* Check for errors */
		if (isr & (STM32_I2C_ISR_ARLO | STM32_I2C_ISR_BERR |
			STM32_I2C_ISR_NACK))
			return EC_ERROR_UNKNOWN;

		/* Check for desired mask */
		if ((isr & mask) == mask)
			return EC_SUCCESS;

		/* I2C is slow, so let other things run while we wait */
		usleep(100);
	}

	return EC_ERROR_TIMEOUT;
}

/* Supported i2c input clocks */
enum stm32_i2c_clk_src {
	I2C_CLK_SRC_48MHZ = 0,
	I2C_CLK_SRC_8MHZ = 1,
	I2C_CLK_SRC_COUNT,
};

/* timingr register values for supported input clks / i2c clk rates */
static const uint32_t timingr_regs[I2C_CLK_SRC_COUNT][I2C_FREQ_COUNT] = {
	[I2C_CLK_SRC_48MHZ] = {
		[I2C_FREQ_1000KHZ] = 0x50100103,
		[I2C_FREQ_400KHZ] = 0x50330309,
		[I2C_FREQ_100KHZ] = 0xB0421214,
	},
	[I2C_CLK_SRC_8MHZ] = {
		[I2C_FREQ_1000KHZ] = 0x00100306,
		[I2C_FREQ_400KHZ] = 0x00310309,
		[I2C_FREQ_100KHZ] = 0x10420f13,
	},
};

static void i2c_set_freq_port(const struct i2c_port_t *p,
			      enum stm32_i2c_clk_src src,
			      enum i2c_freq freq)
{
	int port = p->port;
	const uint32_t *regs = timingr_regs[src];

	/* Disable port */
	STM32_I2C_CR1(port) = 0;
	STM32_I2C_CR2(port) = 0;
	/* Set clock frequency */
	STM32_I2C_TIMINGR(port) = regs[freq];
	/* Enable port */
	STM32_I2C_CR1(port) = STM32_I2C_CR1_PE;
}

/**
 * Initialize on the specified I2C port.
 *
 * @param p		the I2c port
 */
static void i2c_init_port(const struct i2c_port_t *p)
{
	int port = p->port;
	enum stm32_i2c_clk_src src = I2C_CLK_SRC_48MHZ;
	enum i2c_freq freq;

	/* Enable clocks to I2C modules if necessary */
	if (!(STM32_RCC_APB1ENR & (1 << (21 + port))))
		STM32_RCC_APB1ENR |= 1 << (21 + port);

	if (port == STM32_I2C1_PORT) {
#if defined(CONFIG_HOSTCMD_I2C_SLAVE_ADDR) && \
defined(CONFIG_LOW_POWER_IDLE) && \
(I2C_PORT_EC == STM32_I2C1_PORT)
		/*
		 * Use HSI (8MHz) for i2c clock. This allows smooth wakeup
		 * from STOP mode since HSI is only clock running immediately
		 * upon exit from STOP mode.
		 */
		STM32_RCC_CFGR3 &= ~0x10;<commit_msg>stm32: i2c: Busy loop for a while waiting for interrupt<commit_after>/* Copyright (c) 2013 The Chromium OS Authors. All rights reserved.
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

#include "chipset.h"
#include "clock.h"
#include "common.h"
#include "console.h"
#include "gpio.h"
#include "hooks.h"
#include "host_command.h"
#include "hwtimer.h"
#include "i2c.h"
#include "registers.h"
#include "system.h"
#include "task.h"
#include "timer.h"
#include "usb_pd_tcpc.h"
#include "usb_pd_tcpm.h"
#include "util.h"

/* Console output macros */
#define CPUTS(outstr) cputs(CC_I2C, outstr)
#define CPRINTS(format, args...) cprints(CC_I2C, format, ## args)

/* Transmit timeout in microseconds */
#define I2C_TX_TIMEOUT_MASTER	(10 * MSEC)

/*
 * Max data size for a version 3 request/response packet. This is
 * big enough for EC_CMD_GET_VERSION plus header info.
 */
#define I2C_MAX_HOST_PACKET_SIZE 128

#ifdef CONFIG_HOSTCMD_I2C_SLAVE_ADDR
#if (I2C_PORT_EC == STM32_I2C1_PORT)
#define IRQ_SLAVE STM32_IRQ_I2C1
#else
#define IRQ_SLAVE STM32_IRQ_I2C2
#endif
#endif


/* I2C port state data */
struct i2c_port_data {
	uint32_t timeout_us;    /* Transaction timeout, or 0 to use default */
	enum i2c_freq freq;	/* Port clock speed */
};
static struct i2c_port_data pdata[I2C_PORT_COUNT];

void i2c_set_timeout(int port, uint32_t timeout)
{
	pdata[port].timeout_us = timeout ? timeout : I2C_TX_TIMEOUT_MASTER;
}

/* timingr register values for supported input clks / i2c clk rates */
static const uint32_t busyloop_us[I2C_FREQ_COUNT] = {
	[I2C_FREQ_1000KHZ] = 16, /* Enough for 2 bytes */
	[I2C_FREQ_400KHZ] = 40,  /* Enough for 2 bytes */
	[I2C_FREQ_100KHZ] = 0,   /* No busy looping at 100kHz (bus is slow) */
};

/**
 * Wait for ISR register to contain the specified mask.
 *
 * Returns EC_SUCCESS, EC_ERROR_TIMEOUT if timed out waiting, or
 * EC_ERROR_UNKNOWN if an error bit appeared in the status register.
 */
static int wait_isr(int port, int mask)
{
	uint32_t start = __hw_clock_source_read();
	uint32_t delta = 0;

	do {
		int isr = STM32_I2C_ISR(port);

		/* Check for errors */
		if (isr & (STM32_I2C_ISR_ARLO | STM32_I2C_ISR_BERR |
			STM32_I2C_ISR_NACK))
			return EC_ERROR_UNKNOWN;

		/* Check for desired mask */
		if ((isr & mask) == mask)
			return EC_SUCCESS;

		delta = __hw_clock_source_read() - start;

		/**
		 * Depending on the bus speed, busy loop for a while before
		 * sleeping and letting other things run.
		 */
		if (delta >= busyloop_us[pdata[port].freq])
			usleep(100);
	} while (delta < pdata[port].timeout_us);

	return EC_ERROR_TIMEOUT;
}

/* Supported i2c input clocks */
enum stm32_i2c_clk_src {
	I2C_CLK_SRC_48MHZ = 0,
	I2C_CLK_SRC_8MHZ = 1,
	I2C_CLK_SRC_COUNT,
};

/* timingr register values for supported input clks / i2c clk rates */
static const uint32_t timingr_regs[I2C_CLK_SRC_COUNT][I2C_FREQ_COUNT] = {
	[I2C_CLK_SRC_48MHZ] = {
		[I2C_FREQ_1000KHZ] = 0x50100103,
		[I2C_FREQ_400KHZ] = 0x50330309,
		[I2C_FREQ_100KHZ] = 0xB0421214,
	},
	[I2C_CLK_SRC_8MHZ] = {
		[I2C_FREQ_1000KHZ] = 0x00100306,
		[I2C_FREQ_400KHZ] = 0x00310309,
		[I2C_FREQ_100KHZ] = 0x10420f13,
	},
};

static void i2c_set_freq_port(const struct i2c_port_t *p,
			      enum stm32_i2c_clk_src src,
			      enum i2c_freq freq)
{
	int port = p->port;
	const uint32_t *regs = timingr_regs[src];

	/* Disable port */
	STM32_I2C_CR1(port) = 0;
	STM32_I2C_CR2(port) = 0;
	/* Set clock frequency */
	STM32_I2C_TIMINGR(port) = regs[freq];
	/* Enable port */
	STM32_I2C_CR1(port) = STM32_I2C_CR1_PE;

	pdata[port].freq = freq;
}

/**
 * Initialize on the specified I2C port.
 *
 * @param p		the I2c port
 */
static void i2c_init_port(const struct i2c_port_t *p)
{
	int port = p->port;
	enum stm32_i2c_clk_src src = I2C_CLK_SRC_48MHZ;
	enum i2c_freq freq;

	/* Enable clocks to I2C modules if necessary */
	if (!(STM32_RCC_APB1ENR & (1 << (21 + port))))
		STM32_RCC_APB1ENR |= 1 << (21 + port);

	if (port == STM32_I2C1_PORT) {
#if defined(CONFIG_HOSTCMD_I2C_SLAVE_ADDR) && \
defined(CONFIG_LOW_POWER_IDLE) && \
(I2C_PORT_EC == STM32_I2C1_PORT)
		/*
		 * Use HSI (8MHz) for i2c clock. This allows smooth wakeup
		 * from STOP mode since HSI is only clock running immediately
		 * upon exit from STOP mode.
		 */
		STM32_RCC_CFGR3 &= ~0x10;
{'fraction_non_alphanumeric': 0.11234691327168209, 'fraction_numerical': 0.06135966008497876, 'mean_word_length': 4.203511053315995, 'pattern_counts': {'":': 0, '<': 13, '<?xml version=': 0, '>': 8, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3066267', 'n_tokens_mistral': 3713, 'n_tokens_neox': 3359, 'n_words': 813}