2009-07-20 07:59:37 +00:00
|
|
|
/*
|
2010-08-27 16:26:05 +00:00
|
|
|
* Driver for the TWSI (i2c) controller found on the Marvell
|
|
|
|
* orion5x and kirkwood SoC families.
|
2009-07-20 07:59:37 +00:00
|
|
|
*
|
2011-04-22 17:41:02 +00:00
|
|
|
* Author: Albert Aribaud <albert.u.boot@aribaud.net>
|
2010-08-27 16:26:05 +00:00
|
|
|
* Copyright (c) 2010 Albert Aribaud.
|
2009-07-20 07:59:37 +00:00
|
|
|
*
|
2013-07-08 07:37:19 +00:00
|
|
|
* SPDX-License-Identifier: GPL-2.0+
|
2009-07-20 07:59:37 +00:00
|
|
|
*/
|
2010-08-27 16:26:05 +00:00
|
|
|
|
2009-07-20 07:59:37 +00:00
|
|
|
#include <common.h>
|
|
|
|
#include <i2c.h>
|
|
|
|
#include <asm/errno.h>
|
|
|
|
#include <asm/io.h>
|
2016-07-21 09:57:10 +00:00
|
|
|
#ifdef CONFIG_DM_I2C
|
|
|
|
#include <dm.h>
|
|
|
|
#endif
|
|
|
|
|
|
|
|
DECLARE_GLOBAL_DATA_PTR;
|
2009-07-20 07:59:37 +00:00
|
|
|
|
2010-08-27 16:26:05 +00:00
|
|
|
/*
|
2016-07-21 09:57:03 +00:00
|
|
|
* Include a file that will provide CONFIG_I2C_MVTWSI_BASE*, and possibly other
|
|
|
|
* settings
|
2010-08-27 16:26:05 +00:00
|
|
|
*/
|
2009-07-20 07:59:37 +00:00
|
|
|
|
2016-07-21 09:57:10 +00:00
|
|
|
#ifndef CONFIG_DM_I2C
|
2010-08-27 16:26:05 +00:00
|
|
|
#if defined(CONFIG_ORION5X)
|
|
|
|
#include <asm/arch/orion5x.h>
|
2015-12-21 12:56:33 +00:00
|
|
|
#elif (defined(CONFIG_KIRKWOOD) || defined(CONFIG_ARCH_MVEBU))
|
2014-10-22 10:13:06 +00:00
|
|
|
#include <asm/arch/soc.h>
|
2014-06-13 20:55:49 +00:00
|
|
|
#elif defined(CONFIG_SUNXI)
|
|
|
|
#include <asm/arch/i2c.h>
|
2010-08-27 16:26:05 +00:00
|
|
|
#else
|
|
|
|
#error Driver mvtwsi not supported by SoC or board
|
2009-07-20 07:59:37 +00:00
|
|
|
#endif
|
2016-07-21 09:57:10 +00:00
|
|
|
#endif /* CONFIG_DM_I2C */
|
2009-07-20 07:59:37 +00:00
|
|
|
|
2010-08-27 16:26:05 +00:00
|
|
|
/*
|
|
|
|
* TWSI register structure
|
|
|
|
*/
|
2009-07-20 07:59:37 +00:00
|
|
|
|
2014-06-13 20:55:49 +00:00
|
|
|
#ifdef CONFIG_SUNXI
|
|
|
|
|
|
|
|
struct mvtwsi_registers {
|
|
|
|
u32 slave_address;
|
|
|
|
u32 xtnd_slave_addr;
|
|
|
|
u32 data;
|
|
|
|
u32 control;
|
|
|
|
u32 status;
|
|
|
|
u32 baudrate;
|
|
|
|
u32 soft_reset;
|
|
|
|
};
|
|
|
|
|
|
|
|
#else
|
|
|
|
|
2010-08-27 16:26:05 +00:00
|
|
|
struct mvtwsi_registers {
|
|
|
|
u32 slave_address;
|
|
|
|
u32 data;
|
|
|
|
u32 control;
|
|
|
|
union {
|
2016-07-21 09:57:03 +00:00
|
|
|
u32 status; /* When reading */
|
|
|
|
u32 baudrate; /* When writing */
|
2010-08-27 16:26:05 +00:00
|
|
|
};
|
|
|
|
u32 xtnd_slave_addr;
|
|
|
|
u32 reserved[2];
|
|
|
|
u32 soft_reset;
|
2009-07-20 07:59:37 +00:00
|
|
|
};
|
|
|
|
|
2014-06-13 20:55:49 +00:00
|
|
|
#endif
|
|
|
|
|
2016-07-21 09:57:10 +00:00
|
|
|
#ifdef CONFIG_DM_I2C
|
|
|
|
struct mvtwsi_i2c_dev {
|
|
|
|
/* TWSI Register base for the device */
|
|
|
|
struct mvtwsi_registers *base;
|
|
|
|
/* Number of the device (determined from cell-index property) */
|
|
|
|
int index;
|
|
|
|
/* The I2C slave address for the device */
|
|
|
|
u8 slaveadd;
|
|
|
|
/* The configured I2C speed in Hz */
|
|
|
|
uint speed;
|
|
|
|
};
|
|
|
|
#endif /* CONFIG_DM_I2C */
|
|
|
|
|
2010-08-27 16:26:05 +00:00
|
|
|
/*
|
2016-07-21 09:57:02 +00:00
|
|
|
* enum mvtwsi_ctrl_register_fields - Bit masks for flags in the control
|
|
|
|
* register
|
2010-08-27 16:26:05 +00:00
|
|
|
*/
|
2016-07-21 09:57:02 +00:00
|
|
|
enum mvtwsi_ctrl_register_fields {
|
|
|
|
/* Acknowledge bit */
|
|
|
|
MVTWSI_CONTROL_ACK = 0x00000004,
|
|
|
|
/* Interrupt flag */
|
|
|
|
MVTWSI_CONTROL_IFLG = 0x00000008,
|
|
|
|
/* Stop bit */
|
|
|
|
MVTWSI_CONTROL_STOP = 0x00000010,
|
|
|
|
/* Start bit */
|
|
|
|
MVTWSI_CONTROL_START = 0x00000020,
|
|
|
|
/* I2C enable */
|
|
|
|
MVTWSI_CONTROL_TWSIEN = 0x00000040,
|
|
|
|
/* Interrupt enable */
|
|
|
|
MVTWSI_CONTROL_INTEN = 0x00000080,
|
|
|
|
};
|
2009-07-20 07:59:37 +00:00
|
|
|
|
2016-01-14 13:06:25 +00:00
|
|
|
/*
|
2016-07-21 09:57:03 +00:00
|
|
|
* On sun6i and newer, IFLG is a write-clear bit, which is cleared by writing 1;
|
|
|
|
* on other platforms, it is a normal r/w bit, which is cleared by writing 0.
|
2016-01-14 13:06:25 +00:00
|
|
|
*/
|
|
|
|
|
|
|
|
#ifdef CONFIG_SUNXI_GEN_SUN6I
|
|
|
|
#define MVTWSI_CONTROL_CLEAR_IFLG 0x00000008
|
|
|
|
#else
|
|
|
|
#define MVTWSI_CONTROL_CLEAR_IFLG 0x00000000
|
|
|
|
#endif
|
|
|
|
|
2010-08-27 16:26:05 +00:00
|
|
|
/*
|
2016-07-21 09:57:02 +00:00
|
|
|
* enum mvstwsi_status_values - Possible values of I2C controller's status
|
|
|
|
* register
|
|
|
|
*
|
|
|
|
* Only those statuses expected in normal master operation on
|
|
|
|
* non-10-bit-address devices are specified.
|
|
|
|
*
|
|
|
|
* Every status that's unexpected during normal operation (bus errors,
|
|
|
|
* arbitration losses, missing ACKs...) is passed back to the caller as an error
|
2010-08-27 16:26:05 +00:00
|
|
|
* code.
|
|
|
|
*/
|
2016-07-21 09:57:02 +00:00
|
|
|
enum mvstwsi_status_values {
|
|
|
|
/* START condition transmitted */
|
|
|
|
MVTWSI_STATUS_START = 0x08,
|
|
|
|
/* Repeated START condition transmitted */
|
|
|
|
MVTWSI_STATUS_REPEATED_START = 0x10,
|
|
|
|
/* Address + write bit transmitted, ACK received */
|
|
|
|
MVTWSI_STATUS_ADDR_W_ACK = 0x18,
|
|
|
|
/* Data transmitted, ACK received */
|
|
|
|
MVTWSI_STATUS_DATA_W_ACK = 0x28,
|
|
|
|
/* Address + read bit transmitted, ACK received */
|
|
|
|
MVTWSI_STATUS_ADDR_R_ACK = 0x40,
|
|
|
|
/* Address + read bit transmitted, ACK not received */
|
|
|
|
MVTWSI_STATUS_ADDR_R_NAK = 0x48,
|
|
|
|
/* Data received, ACK transmitted */
|
|
|
|
MVTWSI_STATUS_DATA_R_ACK = 0x50,
|
|
|
|
/* Data received, ACK not transmitted */
|
|
|
|
MVTWSI_STATUS_DATA_R_NAK = 0x58,
|
|
|
|
/* No relevant status */
|
|
|
|
MVTWSI_STATUS_IDLE = 0xF8,
|
|
|
|
};
|
2010-08-27 16:26:05 +00:00
|
|
|
|
i2c: mvtwsi: Eliminate flags parameter
Due to breaking boots from NOR flashes, commit d6b7757 ("i2c: mvtwsi:
Eliminate twsi_control_flags") removed the static global
twsi_control_flags variable, which kept a set of default flags that were
always or'd to the control register when writing. It was replaced with a
flags parameter, which was passed around between the functions that
needed it.
Since the twsi_control_flags variable was used just for the purposes of
a) setting the MVTWSI_CONTROL_TWSIEN on every control register write,
and
b) setting the MVTWSI_CONTROL_ACK from twsi_i2c_read if needed,
anyway, the added overhead of another variable being passed around is no
longer justified, and we are better off implementing this flag setting
logic locally in the functions that actually write to the control
register.
Therefore, this patch sets MVTWSI_CONTROL_TWSIEN on every control
register write, replaces the twsi_i2c_read's flags parameter with a
ack_flag parameter, which tells the function whether to acknowledge the
read or not, and removes every other instance of the flags variable.
This has the added benefit that now every notion of "global default
flags" is gone, and it's much easier to see which control flags are
actually set at which point in time.
Signed-off-by: Mario Six <mario.six@gdsys.cc>
Reviewed-by: Stefan Roese <sr@denx.de>
2016-07-21 09:57:04 +00:00
|
|
|
/*
|
|
|
|
* enum mvstwsi_ack_flags - Determine whether a read byte should be
|
|
|
|
* acknowledged or not.
|
|
|
|
*/
|
|
|
|
enum mvtwsi_ack_flags {
|
|
|
|
/* Send NAK after received byte */
|
|
|
|
MVTWSI_READ_NAK = 0,
|
|
|
|
/* Send ACK after received byte */
|
|
|
|
MVTWSI_READ_ACK = 1,
|
|
|
|
};
|
|
|
|
|
2016-07-21 09:57:10 +00:00
|
|
|
#ifndef CONFIG_DM_I2C
|
2010-08-27 16:26:05 +00:00
|
|
|
/*
|
2015-04-10 21:09:51 +00:00
|
|
|
* MVTWSI controller base
|
2010-08-27 16:26:05 +00:00
|
|
|
*/
|
2009-07-20 07:59:37 +00:00
|
|
|
|
2015-04-10 21:09:51 +00:00
|
|
|
static struct mvtwsi_registers *twsi_get_base(struct i2c_adapter *adap)
|
|
|
|
{
|
|
|
|
switch (adap->hwadapnr) {
|
|
|
|
#ifdef CONFIG_I2C_MVTWSI_BASE0
|
|
|
|
case 0:
|
2016-07-21 09:57:01 +00:00
|
|
|
return (struct mvtwsi_registers *)CONFIG_I2C_MVTWSI_BASE0;
|
2015-04-10 21:09:51 +00:00
|
|
|
#endif
|
|
|
|
#ifdef CONFIG_I2C_MVTWSI_BASE1
|
|
|
|
case 1:
|
2016-07-21 09:57:01 +00:00
|
|
|
return (struct mvtwsi_registers *)CONFIG_I2C_MVTWSI_BASE1;
|
2015-04-10 21:09:51 +00:00
|
|
|
#endif
|
|
|
|
#ifdef CONFIG_I2C_MVTWSI_BASE2
|
|
|
|
case 2:
|
2016-07-21 09:57:01 +00:00
|
|
|
return (struct mvtwsi_registers *)CONFIG_I2C_MVTWSI_BASE2;
|
2015-04-10 21:09:51 +00:00
|
|
|
#endif
|
|
|
|
#ifdef CONFIG_I2C_MVTWSI_BASE3
|
|
|
|
case 3:
|
2016-07-21 09:57:01 +00:00
|
|
|
return (struct mvtwsi_registers *)CONFIG_I2C_MVTWSI_BASE3;
|
2015-04-10 21:09:51 +00:00
|
|
|
#endif
|
|
|
|
#ifdef CONFIG_I2C_MVTWSI_BASE4
|
|
|
|
case 4:
|
2016-07-21 09:57:01 +00:00
|
|
|
return (struct mvtwsi_registers *)CONFIG_I2C_MVTWSI_BASE4;
|
2016-01-14 13:06:26 +00:00
|
|
|
#endif
|
|
|
|
#ifdef CONFIG_I2C_MVTWSI_BASE5
|
|
|
|
case 5:
|
2016-07-21 09:57:01 +00:00
|
|
|
return (struct mvtwsi_registers *)CONFIG_I2C_MVTWSI_BASE5;
|
2015-04-10 21:09:51 +00:00
|
|
|
#endif
|
|
|
|
default:
|
|
|
|
printf("Missing mvtwsi controller %d base\n", adap->hwadapnr);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
2016-07-21 09:57:10 +00:00
|
|
|
#endif
|
2009-07-20 07:59:37 +00:00
|
|
|
|
|
|
|
/*
|
2016-07-21 09:57:02 +00:00
|
|
|
* enum mvtwsi_error_class - types of I2C errors
|
2009-07-20 07:59:37 +00:00
|
|
|
*/
|
2016-07-21 09:57:02 +00:00
|
|
|
enum mvtwsi_error_class {
|
|
|
|
/* The controller returned a different status than expected */
|
|
|
|
MVTWSI_ERROR_WRONG_STATUS = 0x01,
|
|
|
|
/* The controller timed out */
|
|
|
|
MVTWSI_ERROR_TIMEOUT = 0x02,
|
|
|
|
};
|
2009-07-20 07:59:37 +00:00
|
|
|
|
2016-07-21 09:57:02 +00:00
|
|
|
/*
|
|
|
|
* mvtwsi_error() - Build I2C return code from error information
|
|
|
|
*
|
|
|
|
* For debugging purposes, this function packs some information of an occurred
|
|
|
|
* error into a return code. These error codes are returned from I2C API
|
|
|
|
* functions (i2c_{read,write}, dm_i2c_{read,write}, etc.).
|
|
|
|
*
|
|
|
|
* @ec: The error class of the error (enum mvtwsi_error_class).
|
|
|
|
* @lc: The last value of the control register.
|
|
|
|
* @ls: The last value of the status register.
|
|
|
|
* @es: The expected value of the status register.
|
|
|
|
* @return The generated error code.
|
|
|
|
*/
|
|
|
|
inline uint mvtwsi_error(uint ec, uint lc, uint ls, uint es)
|
|
|
|
{
|
|
|
|
return ((ec << 24) & 0xFF000000)
|
|
|
|
| ((lc << 16) & 0x00FF0000)
|
|
|
|
| ((ls << 8) & 0x0000FF00)
|
|
|
|
| (es & 0xFF);
|
|
|
|
}
|
2009-07-20 07:59:37 +00:00
|
|
|
|
2010-08-27 16:26:05 +00:00
|
|
|
/*
|
2016-07-21 09:57:03 +00:00
|
|
|
* Wait for IFLG to raise, or return 'timeout.' Then, if the status is as
|
|
|
|
* expected, return 0 (ok) or 'wrong status' otherwise.
|
2010-08-27 16:26:05 +00:00
|
|
|
*/
|
2016-07-21 09:57:08 +00:00
|
|
|
static int twsi_wait(struct mvtwsi_registers *twsi, int expected_status)
|
2010-08-27 16:26:05 +00:00
|
|
|
{
|
|
|
|
int control, status;
|
|
|
|
int timeout = 1000;
|
|
|
|
|
|
|
|
do {
|
|
|
|
control = readl(&twsi->control);
|
|
|
|
if (control & MVTWSI_CONTROL_IFLG) {
|
|
|
|
status = readl(&twsi->status);
|
|
|
|
if (status == expected_status)
|
|
|
|
return 0;
|
|
|
|
else
|
2016-07-21 09:57:02 +00:00
|
|
|
return mvtwsi_error(
|
2010-08-27 16:26:05 +00:00
|
|
|
MVTWSI_ERROR_WRONG_STATUS,
|
|
|
|
control, status, expected_status);
|
2009-07-20 07:59:37 +00:00
|
|
|
}
|
2016-07-21 09:57:03 +00:00
|
|
|
udelay(10); /* One clock cycle at 100 kHz */
|
2010-08-27 16:26:05 +00:00
|
|
|
} while (timeout--);
|
|
|
|
status = readl(&twsi->status);
|
2016-07-21 09:57:02 +00:00
|
|
|
return mvtwsi_error(MVTWSI_ERROR_TIMEOUT, control, status,
|
|
|
|
expected_status);
|
2009-07-20 07:59:37 +00:00
|
|
|
}
|
|
|
|
|
2010-08-27 16:26:05 +00:00
|
|
|
/*
|
|
|
|
* Assert the START condition, either in a single I2C transaction
|
|
|
|
* or inside back-to-back ones (repeated starts).
|
|
|
|
*/
|
2016-07-21 09:57:08 +00:00
|
|
|
static int twsi_start(struct mvtwsi_registers *twsi, int expected_status)
|
2009-07-20 07:59:37 +00:00
|
|
|
{
|
2016-07-21 09:57:03 +00:00
|
|
|
/* Assert START */
|
i2c: mvtwsi: Eliminate flags parameter
Due to breaking boots from NOR flashes, commit d6b7757 ("i2c: mvtwsi:
Eliminate twsi_control_flags") removed the static global
twsi_control_flags variable, which kept a set of default flags that were
always or'd to the control register when writing. It was replaced with a
flags parameter, which was passed around between the functions that
needed it.
Since the twsi_control_flags variable was used just for the purposes of
a) setting the MVTWSI_CONTROL_TWSIEN on every control register write,
and
b) setting the MVTWSI_CONTROL_ACK from twsi_i2c_read if needed,
anyway, the added overhead of another variable being passed around is no
longer justified, and we are better off implementing this flag setting
logic locally in the functions that actually write to the control
register.
Therefore, this patch sets MVTWSI_CONTROL_TWSIEN on every control
register write, replaces the twsi_i2c_read's flags parameter with a
ack_flag parameter, which tells the function whether to acknowledge the
read or not, and removes every other instance of the flags variable.
This has the added benefit that now every notion of "global default
flags" is gone, and it's much easier to see which control flags are
actually set at which point in time.
Signed-off-by: Mario Six <mario.six@gdsys.cc>
Reviewed-by: Stefan Roese <sr@denx.de>
2016-07-21 09:57:04 +00:00
|
|
|
writel(MVTWSI_CONTROL_TWSIEN | MVTWSI_CONTROL_START |
|
2016-07-21 09:57:03 +00:00
|
|
|
MVTWSI_CONTROL_CLEAR_IFLG, &twsi->control);
|
|
|
|
/* Wait for controller to process START */
|
2016-07-21 09:57:08 +00:00
|
|
|
return twsi_wait(twsi, expected_status);
|
2009-07-20 07:59:37 +00:00
|
|
|
}
|
|
|
|
|
2010-08-27 16:26:05 +00:00
|
|
|
/*
|
|
|
|
* Send a byte (i2c address or data).
|
|
|
|
*/
|
2016-07-21 09:57:08 +00:00
|
|
|
static int twsi_send(struct mvtwsi_registers *twsi, u8 byte,
|
|
|
|
int expected_status)
|
2009-07-20 07:59:37 +00:00
|
|
|
{
|
2016-07-21 09:57:03 +00:00
|
|
|
/* Write byte to data register for sending */
|
2010-08-27 16:26:05 +00:00
|
|
|
writel(byte, &twsi->data);
|
2016-07-21 09:57:03 +00:00
|
|
|
/* Clear any pending interrupt -- that will cause sending */
|
i2c: mvtwsi: Eliminate flags parameter
Due to breaking boots from NOR flashes, commit d6b7757 ("i2c: mvtwsi:
Eliminate twsi_control_flags") removed the static global
twsi_control_flags variable, which kept a set of default flags that were
always or'd to the control register when writing. It was replaced with a
flags parameter, which was passed around between the functions that
needed it.
Since the twsi_control_flags variable was used just for the purposes of
a) setting the MVTWSI_CONTROL_TWSIEN on every control register write,
and
b) setting the MVTWSI_CONTROL_ACK from twsi_i2c_read if needed,
anyway, the added overhead of another variable being passed around is no
longer justified, and we are better off implementing this flag setting
logic locally in the functions that actually write to the control
register.
Therefore, this patch sets MVTWSI_CONTROL_TWSIEN on every control
register write, replaces the twsi_i2c_read's flags parameter with a
ack_flag parameter, which tells the function whether to acknowledge the
read or not, and removes every other instance of the flags variable.
This has the added benefit that now every notion of "global default
flags" is gone, and it's much easier to see which control flags are
actually set at which point in time.
Signed-off-by: Mario Six <mario.six@gdsys.cc>
Reviewed-by: Stefan Roese <sr@denx.de>
2016-07-21 09:57:04 +00:00
|
|
|
writel(MVTWSI_CONTROL_TWSIEN | MVTWSI_CONTROL_CLEAR_IFLG,
|
|
|
|
&twsi->control);
|
2016-07-21 09:57:03 +00:00
|
|
|
/* Wait for controller to receive byte, and check ACK */
|
2016-07-21 09:57:08 +00:00
|
|
|
return twsi_wait(twsi, expected_status);
|
2009-07-20 07:59:37 +00:00
|
|
|
}
|
|
|
|
|
2010-08-27 16:26:05 +00:00
|
|
|
/*
|
|
|
|
* Receive a byte.
|
|
|
|
*/
|
2016-07-21 09:57:08 +00:00
|
|
|
static int twsi_recv(struct mvtwsi_registers *twsi, u8 *byte, int ack_flag)
|
2009-07-20 07:59:37 +00:00
|
|
|
{
|
i2c: mvtwsi: Eliminate flags parameter
Due to breaking boots from NOR flashes, commit d6b7757 ("i2c: mvtwsi:
Eliminate twsi_control_flags") removed the static global
twsi_control_flags variable, which kept a set of default flags that were
always or'd to the control register when writing. It was replaced with a
flags parameter, which was passed around between the functions that
needed it.
Since the twsi_control_flags variable was used just for the purposes of
a) setting the MVTWSI_CONTROL_TWSIEN on every control register write,
and
b) setting the MVTWSI_CONTROL_ACK from twsi_i2c_read if needed,
anyway, the added overhead of another variable being passed around is no
longer justified, and we are better off implementing this flag setting
logic locally in the functions that actually write to the control
register.
Therefore, this patch sets MVTWSI_CONTROL_TWSIEN on every control
register write, replaces the twsi_i2c_read's flags parameter with a
ack_flag parameter, which tells the function whether to acknowledge the
read or not, and removes every other instance of the flags variable.
This has the added benefit that now every notion of "global default
flags" is gone, and it's much easier to see which control flags are
actually set at which point in time.
Signed-off-by: Mario Six <mario.six@gdsys.cc>
Reviewed-by: Stefan Roese <sr@denx.de>
2016-07-21 09:57:04 +00:00
|
|
|
int expected_status, status, control;
|
2010-08-27 16:26:05 +00:00
|
|
|
|
i2c: mvtwsi: Eliminate flags parameter
Due to breaking boots from NOR flashes, commit d6b7757 ("i2c: mvtwsi:
Eliminate twsi_control_flags") removed the static global
twsi_control_flags variable, which kept a set of default flags that were
always or'd to the control register when writing. It was replaced with a
flags parameter, which was passed around between the functions that
needed it.
Since the twsi_control_flags variable was used just for the purposes of
a) setting the MVTWSI_CONTROL_TWSIEN on every control register write,
and
b) setting the MVTWSI_CONTROL_ACK from twsi_i2c_read if needed,
anyway, the added overhead of another variable being passed around is no
longer justified, and we are better off implementing this flag setting
logic locally in the functions that actually write to the control
register.
Therefore, this patch sets MVTWSI_CONTROL_TWSIEN on every control
register write, replaces the twsi_i2c_read's flags parameter with a
ack_flag parameter, which tells the function whether to acknowledge the
read or not, and removes every other instance of the flags variable.
This has the added benefit that now every notion of "global default
flags" is gone, and it's much easier to see which control flags are
actually set at which point in time.
Signed-off-by: Mario Six <mario.six@gdsys.cc>
Reviewed-by: Stefan Roese <sr@denx.de>
2016-07-21 09:57:04 +00:00
|
|
|
/* Compute expected status based on passed ACK flag */
|
|
|
|
expected_status = ack_flag ? MVTWSI_STATUS_DATA_R_ACK :
|
|
|
|
MVTWSI_STATUS_DATA_R_NAK;
|
2016-07-21 09:57:03 +00:00
|
|
|
/* Acknowledge *previous state*, and launch receive */
|
i2c: mvtwsi: Eliminate flags parameter
Due to breaking boots from NOR flashes, commit d6b7757 ("i2c: mvtwsi:
Eliminate twsi_control_flags") removed the static global
twsi_control_flags variable, which kept a set of default flags that were
always or'd to the control register when writing. It was replaced with a
flags parameter, which was passed around between the functions that
needed it.
Since the twsi_control_flags variable was used just for the purposes of
a) setting the MVTWSI_CONTROL_TWSIEN on every control register write,
and
b) setting the MVTWSI_CONTROL_ACK from twsi_i2c_read if needed,
anyway, the added overhead of another variable being passed around is no
longer justified, and we are better off implementing this flag setting
logic locally in the functions that actually write to the control
register.
Therefore, this patch sets MVTWSI_CONTROL_TWSIEN on every control
register write, replaces the twsi_i2c_read's flags parameter with a
ack_flag parameter, which tells the function whether to acknowledge the
read or not, and removes every other instance of the flags variable.
This has the added benefit that now every notion of "global default
flags" is gone, and it's much easier to see which control flags are
actually set at which point in time.
Signed-off-by: Mario Six <mario.six@gdsys.cc>
Reviewed-by: Stefan Roese <sr@denx.de>
2016-07-21 09:57:04 +00:00
|
|
|
control = MVTWSI_CONTROL_TWSIEN;
|
|
|
|
control |= ack_flag == MVTWSI_READ_ACK ? MVTWSI_CONTROL_ACK : 0;
|
|
|
|
writel(control | MVTWSI_CONTROL_CLEAR_IFLG, &twsi->control);
|
2016-07-21 09:57:03 +00:00
|
|
|
/* Wait for controller to receive byte, and assert ACK or NAK */
|
2016-07-21 09:57:08 +00:00
|
|
|
status = twsi_wait(twsi, expected_status);
|
2016-07-21 09:57:03 +00:00
|
|
|
/* If we did receive the expected byte, store it */
|
2010-08-27 16:26:05 +00:00
|
|
|
if (status == 0)
|
|
|
|
*byte = readl(&twsi->data);
|
|
|
|
return status;
|
2009-07-20 07:59:37 +00:00
|
|
|
}
|
|
|
|
|
2010-08-27 16:26:05 +00:00
|
|
|
/*
|
|
|
|
* Assert the STOP condition.
|
2016-07-21 09:57:03 +00:00
|
|
|
* This is also used to force the bus back to idle (SDA = SCL = 1).
|
2010-08-27 16:26:05 +00:00
|
|
|
*/
|
2016-07-21 09:57:08 +00:00
|
|
|
static int twsi_stop(struct mvtwsi_registers *twsi)
|
2009-07-20 07:59:37 +00:00
|
|
|
{
|
2010-08-27 16:26:05 +00:00
|
|
|
int control, stop_status;
|
2016-07-21 09:57:05 +00:00
|
|
|
int status = 0;
|
2010-08-27 16:26:05 +00:00
|
|
|
int timeout = 1000;
|
|
|
|
|
2016-07-21 09:57:03 +00:00
|
|
|
/* Assert STOP */
|
2010-08-27 16:26:05 +00:00
|
|
|
control = MVTWSI_CONTROL_TWSIEN | MVTWSI_CONTROL_STOP;
|
2016-01-14 13:06:25 +00:00
|
|
|
writel(control | MVTWSI_CONTROL_CLEAR_IFLG, &twsi->control);
|
2016-07-21 09:57:03 +00:00
|
|
|
/* Wait for IDLE; IFLG won't rise, so we can't use twsi_wait() */
|
2010-08-27 16:26:05 +00:00
|
|
|
do {
|
|
|
|
stop_status = readl(&twsi->status);
|
|
|
|
if (stop_status == MVTWSI_STATUS_IDLE)
|
|
|
|
break;
|
2016-07-21 09:57:03 +00:00
|
|
|
udelay(10); /* One clock cycle at 100 kHz */
|
2010-08-27 16:26:05 +00:00
|
|
|
} while (timeout--);
|
|
|
|
control = readl(&twsi->control);
|
|
|
|
if (stop_status != MVTWSI_STATUS_IDLE)
|
2016-07-21 09:57:05 +00:00
|
|
|
status = mvtwsi_error(MVTWSI_ERROR_TIMEOUT,
|
|
|
|
control, status, MVTWSI_STATUS_IDLE);
|
2010-08-27 16:26:05 +00:00
|
|
|
return status;
|
2009-07-20 07:59:37 +00:00
|
|
|
}
|
|
|
|
|
2016-07-21 09:57:06 +00:00
|
|
|
static uint twsi_calc_freq(const int n, const int m)
|
2015-03-18 08:30:54 +00:00
|
|
|
{
|
|
|
|
#ifdef CONFIG_SUNXI
|
|
|
|
return CONFIG_SYS_TCLK / (10 * (m + 1) * (1 << n));
|
|
|
|
#else
|
|
|
|
return CONFIG_SYS_TCLK / (10 * (m + 1) * (2 << n));
|
|
|
|
#endif
|
|
|
|
}
|
2010-08-27 16:26:05 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Reset controller.
|
|
|
|
* Controller reset also resets the baud rate and slave address, so
|
2014-06-13 20:55:48 +00:00
|
|
|
* they must be re-established afterwards.
|
2010-08-27 16:26:05 +00:00
|
|
|
*/
|
2016-07-21 09:57:08 +00:00
|
|
|
static void twsi_reset(struct mvtwsi_registers *twsi)
|
2010-08-27 16:26:05 +00:00
|
|
|
{
|
2016-07-21 09:57:03 +00:00
|
|
|
/* Reset controller */
|
2010-08-27 16:26:05 +00:00
|
|
|
writel(0, &twsi->soft_reset);
|
2016-07-21 09:57:03 +00:00
|
|
|
/* Wait 2 ms -- this is what the Marvell LSP does */
|
2010-08-27 16:26:05 +00:00
|
|
|
udelay(20000);
|
2009-07-20 07:59:37 +00:00
|
|
|
}
|
|
|
|
|
2010-08-27 16:26:05 +00:00
|
|
|
/*
|
2016-07-21 09:57:03 +00:00
|
|
|
* Sets baud to the highest possible value not exceeding the requested one.
|
2010-08-27 16:26:05 +00:00
|
|
|
*/
|
2016-07-21 09:57:08 +00:00
|
|
|
static uint __twsi_i2c_set_bus_speed(struct mvtwsi_registers *twsi,
|
2016-07-21 09:57:07 +00:00
|
|
|
uint requested_speed)
|
2009-07-20 07:59:37 +00:00
|
|
|
{
|
2016-07-21 09:57:06 +00:00
|
|
|
uint tmp_speed, highest_speed, n, m;
|
|
|
|
uint baud = 0x44; /* Baud rate after controller reset */
|
2010-08-27 16:26:05 +00:00
|
|
|
|
|
|
|
highest_speed = 0;
|
2016-07-21 09:57:03 +00:00
|
|
|
/* Successively try m, n combinations, and use the combination
|
|
|
|
* resulting in the largest speed that's not above the requested
|
|
|
|
* speed */
|
2010-08-27 16:26:05 +00:00
|
|
|
for (n = 0; n < 8; n++) {
|
|
|
|
for (m = 0; m < 16; m++) {
|
2015-03-18 08:30:54 +00:00
|
|
|
tmp_speed = twsi_calc_freq(n, m);
|
2016-07-21 09:57:01 +00:00
|
|
|
if ((tmp_speed <= requested_speed) &&
|
|
|
|
(tmp_speed > highest_speed)) {
|
2010-08-27 16:26:05 +00:00
|
|
|
highest_speed = tmp_speed;
|
|
|
|
baud = (m << 3) | n;
|
|
|
|
}
|
|
|
|
}
|
2009-07-20 07:59:37 +00:00
|
|
|
}
|
2014-06-13 20:55:48 +00:00
|
|
|
writel(baud, &twsi->baudrate);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2016-07-21 09:57:08 +00:00
|
|
|
static void __twsi_i2c_init(struct mvtwsi_registers *twsi, int speed,
|
2016-07-21 09:57:10 +00:00
|
|
|
int slaveadd)
|
2014-06-13 20:55:48 +00:00
|
|
|
{
|
2016-07-21 09:57:03 +00:00
|
|
|
/* Reset controller */
|
2016-07-21 09:57:08 +00:00
|
|
|
twsi_reset(twsi);
|
2016-07-21 09:57:03 +00:00
|
|
|
/* Set speed */
|
2016-07-21 09:57:08 +00:00
|
|
|
__twsi_i2c_set_bus_speed(twsi, speed);
|
2016-07-21 09:57:03 +00:00
|
|
|
/* Set slave address; even though we don't use it */
|
2014-06-13 20:55:48 +00:00
|
|
|
writel(slaveadd, &twsi->slave_address);
|
|
|
|
writel(0, &twsi->xtnd_slave_addr);
|
2016-07-21 09:57:03 +00:00
|
|
|
/* Assert STOP, but don't care for the result */
|
2016-07-21 09:57:08 +00:00
|
|
|
(void) twsi_stop(twsi);
|
2009-07-20 07:59:37 +00:00
|
|
|
}
|
|
|
|
|
2010-08-27 16:26:05 +00:00
|
|
|
/*
|
|
|
|
* Begin I2C transaction with expected start status, at given address.
|
|
|
|
* Expected address status will derive from direction bit (bit 0) in addr.
|
|
|
|
*/
|
2016-07-21 09:57:08 +00:00
|
|
|
static int i2c_begin(struct mvtwsi_registers *twsi, int expected_start_status,
|
i2c: mvtwsi: Eliminate flags parameter
Due to breaking boots from NOR flashes, commit d6b7757 ("i2c: mvtwsi:
Eliminate twsi_control_flags") removed the static global
twsi_control_flags variable, which kept a set of default flags that were
always or'd to the control register when writing. It was replaced with a
flags parameter, which was passed around between the functions that
needed it.
Since the twsi_control_flags variable was used just for the purposes of
a) setting the MVTWSI_CONTROL_TWSIEN on every control register write,
and
b) setting the MVTWSI_CONTROL_ACK from twsi_i2c_read if needed,
anyway, the added overhead of another variable being passed around is no
longer justified, and we are better off implementing this flag setting
logic locally in the functions that actually write to the control
register.
Therefore, this patch sets MVTWSI_CONTROL_TWSIEN on every control
register write, replaces the twsi_i2c_read's flags parameter with a
ack_flag parameter, which tells the function whether to acknowledge the
read or not, and removes every other instance of the flags variable.
This has the added benefit that now every notion of "global default
flags" is gone, and it's much easier to see which control flags are
actually set at which point in time.
Signed-off-by: Mario Six <mario.six@gdsys.cc>
Reviewed-by: Stefan Roese <sr@denx.de>
2016-07-21 09:57:04 +00:00
|
|
|
u8 addr)
|
2009-07-20 07:59:37 +00:00
|
|
|
{
|
2010-08-27 16:26:05 +00:00
|
|
|
int status, expected_addr_status;
|
|
|
|
|
2016-07-21 09:57:03 +00:00
|
|
|
/* Compute the expected address status from the direction bit in
|
|
|
|
* the address byte */
|
|
|
|
if (addr & 1) /* Reading */
|
2010-08-27 16:26:05 +00:00
|
|
|
expected_addr_status = MVTWSI_STATUS_ADDR_R_ACK;
|
2016-07-21 09:57:03 +00:00
|
|
|
else /* Writing */
|
2010-08-27 16:26:05 +00:00
|
|
|
expected_addr_status = MVTWSI_STATUS_ADDR_W_ACK;
|
2016-07-21 09:57:03 +00:00
|
|
|
/* Assert START */
|
2016-07-21 09:57:08 +00:00
|
|
|
status = twsi_start(twsi, expected_start_status);
|
2016-07-21 09:57:03 +00:00
|
|
|
/* Send out the address if the start went well */
|
2010-08-27 16:26:05 +00:00
|
|
|
if (status == 0)
|
2016-07-21 09:57:08 +00:00
|
|
|
status = twsi_send(twsi, addr, expected_addr_status);
|
2016-07-21 09:57:03 +00:00
|
|
|
/* Return 0, or the status of the first failure */
|
2010-08-27 16:26:05 +00:00
|
|
|
return status;
|
2009-07-20 07:59:37 +00:00
|
|
|
}
|
|
|
|
|
2010-08-27 16:26:05 +00:00
|
|
|
/*
|
|
|
|
* Begin read, nak data byte, end.
|
|
|
|
*/
|
2016-07-21 09:57:08 +00:00
|
|
|
static int __twsi_i2c_probe_chip(struct mvtwsi_registers *twsi, uchar chip)
|
2009-07-20 07:59:37 +00:00
|
|
|
{
|
2010-08-27 16:26:05 +00:00
|
|
|
u8 dummy_byte;
|
|
|
|
int status;
|
|
|
|
|
2016-07-21 09:57:03 +00:00
|
|
|
/* Begin i2c read */
|
2016-07-21 09:57:08 +00:00
|
|
|
status = i2c_begin(twsi, MVTWSI_STATUS_START, (chip << 1) | 1);
|
2016-07-21 09:57:03 +00:00
|
|
|
/* Dummy read was accepted: receive byte, but NAK it. */
|
2010-08-27 16:26:05 +00:00
|
|
|
if (status == 0)
|
2016-07-21 09:57:08 +00:00
|
|
|
status = twsi_recv(twsi, &dummy_byte, MVTWSI_READ_NAK);
|
2010-08-27 16:26:05 +00:00
|
|
|
/* Stop transaction */
|
2016-07-21 09:57:08 +00:00
|
|
|
twsi_stop(twsi);
|
2016-07-21 09:57:03 +00:00
|
|
|
/* Return 0, or the status of the first failure */
|
2010-08-27 16:26:05 +00:00
|
|
|
return status;
|
2009-07-20 07:59:37 +00:00
|
|
|
}
|
|
|
|
|
2010-08-27 16:26:05 +00:00
|
|
|
/*
|
|
|
|
* Begin write, send address byte(s), begin read, receive data bytes, end.
|
|
|
|
*
|
2016-07-21 09:57:03 +00:00
|
|
|
* NOTE: Some devices want a stop right before the second start, while some
|
|
|
|
* will choke if it is there. Since deciding this is not yet supported in
|
|
|
|
* higher level APIs, we need to make a decision here, and for the moment that
|
|
|
|
* will be a repeated start without a preceding stop.
|
2010-08-27 16:26:05 +00:00
|
|
|
*/
|
2016-07-21 09:57:08 +00:00
|
|
|
static int __twsi_i2c_read(struct mvtwsi_registers *twsi, uchar chip,
|
2016-07-21 09:57:09 +00:00
|
|
|
u8 *addr, int alen, uchar *data, int length)
|
2009-07-20 07:59:37 +00:00
|
|
|
{
|
2016-07-21 09:57:05 +00:00
|
|
|
int status = 0;
|
|
|
|
int stop_status;
|
2016-07-21 09:57:11 +00:00
|
|
|
int expected_start = MVTWSI_STATUS_START;
|
|
|
|
|
|
|
|
if (alen > 0) {
|
|
|
|
/* Begin i2c write to send the address bytes */
|
|
|
|
status = i2c_begin(twsi, expected_start, (chip << 1));
|
|
|
|
/* Send address bytes */
|
|
|
|
while ((status == 0) && alen--)
|
|
|
|
status = twsi_send(twsi, *(addr++),
|
|
|
|
MVTWSI_STATUS_DATA_W_ACK);
|
|
|
|
/* Send repeated STARTs after the initial START */
|
|
|
|
expected_start = MVTWSI_STATUS_REPEATED_START;
|
|
|
|
}
|
2016-07-21 09:57:03 +00:00
|
|
|
/* Begin i2c read to receive data bytes */
|
2010-08-27 16:26:05 +00:00
|
|
|
if (status == 0)
|
2016-07-21 09:57:11 +00:00
|
|
|
status = i2c_begin(twsi, expected_start, (chip << 1) | 1);
|
i2c: mvtwsi: Eliminate flags parameter
Due to breaking boots from NOR flashes, commit d6b7757 ("i2c: mvtwsi:
Eliminate twsi_control_flags") removed the static global
twsi_control_flags variable, which kept a set of default flags that were
always or'd to the control register when writing. It was replaced with a
flags parameter, which was passed around between the functions that
needed it.
Since the twsi_control_flags variable was used just for the purposes of
a) setting the MVTWSI_CONTROL_TWSIEN on every control register write,
and
b) setting the MVTWSI_CONTROL_ACK from twsi_i2c_read if needed,
anyway, the added overhead of another variable being passed around is no
longer justified, and we are better off implementing this flag setting
logic locally in the functions that actually write to the control
register.
Therefore, this patch sets MVTWSI_CONTROL_TWSIEN on every control
register write, replaces the twsi_i2c_read's flags parameter with a
ack_flag parameter, which tells the function whether to acknowledge the
read or not, and removes every other instance of the flags variable.
This has the added benefit that now every notion of "global default
flags" is gone, and it's much easier to see which control flags are
actually set at which point in time.
Signed-off-by: Mario Six <mario.six@gdsys.cc>
Reviewed-by: Stefan Roese <sr@denx.de>
2016-07-21 09:57:04 +00:00
|
|
|
/* Receive actual data bytes; set NAK if we if we have nothing more to
|
|
|
|
* read */
|
|
|
|
while ((status == 0) && length--)
|
2016-07-21 09:57:08 +00:00
|
|
|
status = twsi_recv(twsi, data++,
|
i2c: mvtwsi: Eliminate flags parameter
Due to breaking boots from NOR flashes, commit d6b7757 ("i2c: mvtwsi:
Eliminate twsi_control_flags") removed the static global
twsi_control_flags variable, which kept a set of default flags that were
always or'd to the control register when writing. It was replaced with a
flags parameter, which was passed around between the functions that
needed it.
Since the twsi_control_flags variable was used just for the purposes of
a) setting the MVTWSI_CONTROL_TWSIEN on every control register write,
and
b) setting the MVTWSI_CONTROL_ACK from twsi_i2c_read if needed,
anyway, the added overhead of another variable being passed around is no
longer justified, and we are better off implementing this flag setting
logic locally in the functions that actually write to the control
register.
Therefore, this patch sets MVTWSI_CONTROL_TWSIEN on every control
register write, replaces the twsi_i2c_read's flags parameter with a
ack_flag parameter, which tells the function whether to acknowledge the
read or not, and removes every other instance of the flags variable.
This has the added benefit that now every notion of "global default
flags" is gone, and it's much easier to see which control flags are
actually set at which point in time.
Signed-off-by: Mario Six <mario.six@gdsys.cc>
Reviewed-by: Stefan Roese <sr@denx.de>
2016-07-21 09:57:04 +00:00
|
|
|
length > 0 ?
|
|
|
|
MVTWSI_READ_ACK : MVTWSI_READ_NAK);
|
2010-08-27 16:26:05 +00:00
|
|
|
/* Stop transaction */
|
2016-07-21 09:57:08 +00:00
|
|
|
stop_status = twsi_stop(twsi);
|
2016-07-21 09:57:03 +00:00
|
|
|
/* Return 0, or the status of the first failure */
|
2016-07-21 09:57:05 +00:00
|
|
|
return status != 0 ? status : stop_status;
|
2009-07-20 07:59:37 +00:00
|
|
|
}
|
|
|
|
|
2010-08-27 16:26:05 +00:00
|
|
|
/*
|
|
|
|
* Begin write, send address byte(s), send data bytes, end.
|
|
|
|
*/
|
2016-07-21 09:57:08 +00:00
|
|
|
static int __twsi_i2c_write(struct mvtwsi_registers *twsi, uchar chip,
|
2016-07-21 09:57:09 +00:00
|
|
|
u8 *addr, int alen, uchar *data, int length)
|
2009-07-20 07:59:37 +00:00
|
|
|
{
|
2016-07-21 09:57:05 +00:00
|
|
|
int status, stop_status;
|
2010-08-27 16:26:05 +00:00
|
|
|
|
2016-07-21 09:57:03 +00:00
|
|
|
/* Begin i2c write to send first the address bytes, then the
|
|
|
|
* data bytes */
|
2016-07-21 09:57:08 +00:00
|
|
|
status = i2c_begin(twsi, MVTWSI_STATUS_START, (chip << 1));
|
2016-07-21 09:57:03 +00:00
|
|
|
/* Send address bytes */
|
2016-07-21 09:57:09 +00:00
|
|
|
while ((status == 0) && (alen-- > 0))
|
|
|
|
status = twsi_send(twsi, *(addr++), MVTWSI_STATUS_DATA_W_ACK);
|
2016-07-21 09:57:03 +00:00
|
|
|
/* Send data bytes */
|
2010-08-27 16:26:05 +00:00
|
|
|
while ((status == 0) && (length-- > 0))
|
2016-07-21 09:57:08 +00:00
|
|
|
status = twsi_send(twsi, *(data++), MVTWSI_STATUS_DATA_W_ACK);
|
2010-08-27 16:26:05 +00:00
|
|
|
/* Stop transaction */
|
2016-07-21 09:57:08 +00:00
|
|
|
stop_status = twsi_stop(twsi);
|
2016-07-21 09:57:03 +00:00
|
|
|
/* Return 0, or the status of the first failure */
|
2016-07-21 09:57:05 +00:00
|
|
|
return status != 0 ? status : stop_status;
|
2009-07-20 07:59:37 +00:00
|
|
|
}
|
|
|
|
|
2016-07-21 09:57:10 +00:00
|
|
|
#ifndef CONFIG_DM_I2C
|
2016-07-21 09:57:07 +00:00
|
|
|
static void twsi_i2c_init(struct i2c_adapter *adap, int speed,
|
|
|
|
int slaveadd)
|
|
|
|
{
|
2016-07-21 09:57:08 +00:00
|
|
|
struct mvtwsi_registers *twsi = twsi_get_base(adap);
|
|
|
|
__twsi_i2c_init(twsi, speed, slaveadd);
|
2016-07-21 09:57:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static uint twsi_i2c_set_bus_speed(struct i2c_adapter *adap,
|
|
|
|
uint requested_speed)
|
|
|
|
{
|
2016-07-21 09:57:08 +00:00
|
|
|
struct mvtwsi_registers *twsi = twsi_get_base(adap);
|
|
|
|
return __twsi_i2c_set_bus_speed(twsi, requested_speed);
|
2016-07-21 09:57:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static int twsi_i2c_probe(struct i2c_adapter *adap, uchar chip)
|
|
|
|
{
|
2016-07-21 09:57:08 +00:00
|
|
|
struct mvtwsi_registers *twsi = twsi_get_base(adap);
|
|
|
|
return __twsi_i2c_probe_chip(twsi, chip);
|
2016-07-21 09:57:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static int twsi_i2c_read(struct i2c_adapter *adap, uchar chip, uint addr,
|
|
|
|
int alen, uchar *data, int length)
|
|
|
|
{
|
2016-07-21 09:57:08 +00:00
|
|
|
struct mvtwsi_registers *twsi = twsi_get_base(adap);
|
2016-07-21 09:57:09 +00:00
|
|
|
u8 addr_bytes[4];
|
|
|
|
|
|
|
|
addr_bytes[0] = (addr >> 0) & 0xFF;
|
|
|
|
addr_bytes[1] = (addr >> 8) & 0xFF;
|
|
|
|
addr_bytes[2] = (addr >> 16) & 0xFF;
|
|
|
|
addr_bytes[3] = (addr >> 24) & 0xFF;
|
|
|
|
|
|
|
|
return __twsi_i2c_read(twsi, chip, addr_bytes, alen, data, length);
|
2016-07-21 09:57:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static int twsi_i2c_write(struct i2c_adapter *adap, uchar chip, uint addr,
|
|
|
|
int alen, uchar *data, int length)
|
|
|
|
{
|
2016-07-21 09:57:08 +00:00
|
|
|
struct mvtwsi_registers *twsi = twsi_get_base(adap);
|
2016-07-21 09:57:09 +00:00
|
|
|
u8 addr_bytes[4];
|
|
|
|
|
|
|
|
addr_bytes[0] = (addr >> 0) & 0xFF;
|
|
|
|
addr_bytes[1] = (addr >> 8) & 0xFF;
|
|
|
|
addr_bytes[2] = (addr >> 16) & 0xFF;
|
|
|
|
addr_bytes[3] = (addr >> 24) & 0xFF;
|
|
|
|
|
|
|
|
return __twsi_i2c_write(twsi, chip, addr_bytes, alen, data, length);
|
2016-07-21 09:57:07 +00:00
|
|
|
}
|
|
|
|
|
2015-04-10 21:09:51 +00:00
|
|
|
#ifdef CONFIG_I2C_MVTWSI_BASE0
|
2014-06-13 20:55:48 +00:00
|
|
|
U_BOOT_I2C_ADAP_COMPLETE(twsi0, twsi_i2c_init, twsi_i2c_probe,
|
|
|
|
twsi_i2c_read, twsi_i2c_write,
|
|
|
|
twsi_i2c_set_bus_speed,
|
|
|
|
CONFIG_SYS_I2C_SPEED, CONFIG_SYS_I2C_SLAVE, 0)
|
2015-04-10 21:09:51 +00:00
|
|
|
#endif
|
|
|
|
#ifdef CONFIG_I2C_MVTWSI_BASE1
|
|
|
|
U_BOOT_I2C_ADAP_COMPLETE(twsi1, twsi_i2c_init, twsi_i2c_probe,
|
|
|
|
twsi_i2c_read, twsi_i2c_write,
|
|
|
|
twsi_i2c_set_bus_speed,
|
|
|
|
CONFIG_SYS_I2C_SPEED, CONFIG_SYS_I2C_SLAVE, 1)
|
|
|
|
|
|
|
|
#endif
|
|
|
|
#ifdef CONFIG_I2C_MVTWSI_BASE2
|
|
|
|
U_BOOT_I2C_ADAP_COMPLETE(twsi2, twsi_i2c_init, twsi_i2c_probe,
|
|
|
|
twsi_i2c_read, twsi_i2c_write,
|
|
|
|
twsi_i2c_set_bus_speed,
|
|
|
|
CONFIG_SYS_I2C_SPEED, CONFIG_SYS_I2C_SLAVE, 2)
|
|
|
|
|
|
|
|
#endif
|
|
|
|
#ifdef CONFIG_I2C_MVTWSI_BASE3
|
|
|
|
U_BOOT_I2C_ADAP_COMPLETE(twsi3, twsi_i2c_init, twsi_i2c_probe,
|
|
|
|
twsi_i2c_read, twsi_i2c_write,
|
|
|
|
twsi_i2c_set_bus_speed,
|
|
|
|
CONFIG_SYS_I2C_SPEED, CONFIG_SYS_I2C_SLAVE, 3)
|
|
|
|
|
|
|
|
#endif
|
|
|
|
#ifdef CONFIG_I2C_MVTWSI_BASE4
|
|
|
|
U_BOOT_I2C_ADAP_COMPLETE(twsi4, twsi_i2c_init, twsi_i2c_probe,
|
|
|
|
twsi_i2c_read, twsi_i2c_write,
|
|
|
|
twsi_i2c_set_bus_speed,
|
|
|
|
CONFIG_SYS_I2C_SPEED, CONFIG_SYS_I2C_SLAVE, 4)
|
|
|
|
|
|
|
|
#endif
|
2016-01-14 13:06:26 +00:00
|
|
|
#ifdef CONFIG_I2C_MVTWSI_BASE5
|
|
|
|
U_BOOT_I2C_ADAP_COMPLETE(twsi5, twsi_i2c_init, twsi_i2c_probe,
|
|
|
|
twsi_i2c_read, twsi_i2c_write,
|
|
|
|
twsi_i2c_set_bus_speed,
|
|
|
|
CONFIG_SYS_I2C_SPEED, CONFIG_SYS_I2C_SLAVE, 5)
|
|
|
|
|
|
|
|
#endif
|
2016-07-21 09:57:10 +00:00
|
|
|
#else /* CONFIG_DM_I2C */
|
|
|
|
|
|
|
|
static int mvtwsi_i2c_probe_chip(struct udevice *bus, u32 chip_addr,
|
|
|
|
u32 chip_flags)
|
|
|
|
{
|
|
|
|
struct mvtwsi_i2c_dev *dev = dev_get_priv(bus);
|
|
|
|
return __twsi_i2c_probe_chip(dev->base, chip_addr);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int mvtwsi_i2c_set_bus_speed(struct udevice *bus, uint speed)
|
|
|
|
{
|
|
|
|
struct mvtwsi_i2c_dev *dev = dev_get_priv(bus);
|
|
|
|
return __twsi_i2c_set_bus_speed(dev->base, speed);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int mvtwsi_i2c_ofdata_to_platdata(struct udevice *bus)
|
|
|
|
{
|
|
|
|
struct mvtwsi_i2c_dev *dev = dev_get_priv(bus);
|
|
|
|
|
|
|
|
dev->base = dev_get_addr_ptr(bus);
|
|
|
|
|
|
|
|
if (!dev->base)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
dev->index = fdtdec_get_int(gd->fdt_blob, bus->of_offset,
|
|
|
|
"cell-index", -1);
|
|
|
|
dev->slaveadd = fdtdec_get_int(gd->fdt_blob, bus->of_offset,
|
|
|
|
"u-boot,i2c-slave-addr", 0x0);
|
|
|
|
dev->speed = fdtdec_get_int(gd->fdt_blob, bus->of_offset,
|
|
|
|
"clock-frequency", 100000);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int mvtwsi_i2c_probe(struct udevice *bus)
|
|
|
|
{
|
|
|
|
struct mvtwsi_i2c_dev *dev = dev_get_priv(bus);
|
|
|
|
__twsi_i2c_init(dev->base, dev->speed, dev->slaveadd);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int mvtwsi_i2c_xfer(struct udevice *bus, struct i2c_msg *msg, int nmsgs)
|
|
|
|
{
|
|
|
|
struct mvtwsi_i2c_dev *dev = dev_get_priv(bus);
|
|
|
|
struct i2c_msg *dmsg, *omsg, dummy;
|
|
|
|
|
|
|
|
memset(&dummy, 0, sizeof(struct i2c_msg));
|
|
|
|
|
|
|
|
/* We expect either two messages (one with an offset and one with the
|
|
|
|
* actual data) or one message (just data or offset/data combined) */
|
|
|
|
if (nmsgs > 2 || nmsgs == 0) {
|
|
|
|
debug("%s: Only one or two messages are supported.", __func__);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
omsg = nmsgs == 1 ? &dummy : msg;
|
|
|
|
dmsg = nmsgs == 1 ? msg : msg + 1;
|
|
|
|
|
|
|
|
if (dmsg->flags & I2C_M_RD)
|
|
|
|
return __twsi_i2c_read(dev->base, dmsg->addr, omsg->buf,
|
|
|
|
omsg->len, dmsg->buf, dmsg->len);
|
|
|
|
else
|
|
|
|
return __twsi_i2c_write(dev->base, dmsg->addr, omsg->buf,
|
|
|
|
omsg->len, dmsg->buf, dmsg->len);
|
|
|
|
}
|
|
|
|
|
|
|
|
static const struct dm_i2c_ops mvtwsi_i2c_ops = {
|
|
|
|
.xfer = mvtwsi_i2c_xfer,
|
|
|
|
.probe_chip = mvtwsi_i2c_probe_chip,
|
|
|
|
.set_bus_speed = mvtwsi_i2c_set_bus_speed,
|
|
|
|
};
|
|
|
|
|
|
|
|
static const struct udevice_id mvtwsi_i2c_ids[] = {
|
|
|
|
{ .compatible = "marvell,mv64xxx-i2c", },
|
|
|
|
{ /* sentinel */ }
|
|
|
|
};
|
|
|
|
|
|
|
|
U_BOOT_DRIVER(i2c_mvtwsi) = {
|
|
|
|
.name = "i2c_mvtwsi",
|
|
|
|
.id = UCLASS_I2C,
|
|
|
|
.of_match = mvtwsi_i2c_ids,
|
|
|
|
.probe = mvtwsi_i2c_probe,
|
|
|
|
.ofdata_to_platdata = mvtwsi_i2c_ofdata_to_platdata,
|
|
|
|
.priv_auto_alloc_size = sizeof(struct mvtwsi_i2c_dev),
|
|
|
|
.ops = &mvtwsi_i2c_ops,
|
|
|
|
};
|
|
|
|
#endif /* CONFIG_DM_I2C */
|