i2c changes for v2020.10
- Add support for I2C controllers found on Octeon II/III and Octeon TX
  TX2 SoC platforms.
- Add I2C controller support for Cortina Access CAxxxx SoCs
- new rtc methods, rtc command, and tests
- imx_lpi2c: Improve the codes to use private data
- stm32f7_i2c.c: Add new compatible "st,stm32mp15-i2c"
- stm32f7_i2c.c: Add Fast Mode Plus support
- pwm: Add PWM driver for SiFive SoC
This commit is contained in:
Tom Rini 2020-07-09 08:22:44 -04:00
commit d9107930af
28 changed files with 2083 additions and 63 deletions

View file

@ -182,6 +182,8 @@ F: drivers/gpio/cortina_gpio.c
F: drivers/watchdog/cortina_wdt.c
F: drivers/serial/serial_cortina.c
F: drivers/mmc/ca_dw_mmc.c
F: drivers/i2c/i2c-cortina.c
F: drivers/i2c/i2c-cortina.h
ARM/CZ.NIC TURRIS MOX SUPPORT
M: Marek Behun <marek.behun@nic.cz>
@ -740,6 +742,8 @@ F: drivers/gpio/cortina_gpio.c
F: drivers/watchdog/cortina_wdt.c
F: drivers/serial/serial_cortina.c
F: drivers/mmc/ca_dw_mmc.c
F: drivers/i2c/i2c-cortina.c
F: drivers/i2c/i2c-cortina.h
MIPS MSCC
M: Gregory CLEMENT <gregory.clement@bootlin.com>

View file

@ -21,6 +21,11 @@ enum {
REG_RESET = 0x20,
REG_AUX0 = 0x30,
REG_AUX1,
REG_AUX2,
REG_AUX3,
REG_COUNT = 0x80,
};

View file

@ -1739,6 +1739,12 @@ config CMD_DATE
Enable the 'date' command for getting/setting the time/date in RTC
devices.
config CMD_RTC
bool "rtc"
depends on DM_RTC
help
Enable the 'rtc' command for low-level access to RTC devices.
config CMD_TIME
bool "time"
help

View file

@ -122,6 +122,7 @@ obj-$(CONFIG_CMD_REISER) += reiser.o
obj-$(CONFIG_CMD_REMOTEPROC) += remoteproc.o
obj-$(CONFIG_CMD_RNG) += rng.o
obj-$(CONFIG_CMD_ROCKUSB) += rockusb.o
obj-$(CONFIG_CMD_RTC) += rtc.o
obj-$(CONFIG_SANDBOX) += host.o
obj-$(CONFIG_CMD_SATA) += sata.o
obj-$(CONFIG_CMD_NVME) += nvme.o

167
cmd/rtc.c Normal file
View file

@ -0,0 +1,167 @@
// SPDX-License-Identifier: GPL-2.0+
#include <common.h>
#include <command.h>
#include <dm.h>
#include <hexdump.h>
#include <i2c.h>
#include <mapmem.h>
#include <rtc.h>
#define MAX_RTC_BYTES 32
static int do_rtc_read(struct udevice *dev, int argc, char * const argv[])
{
u8 buf[MAX_RTC_BYTES];
int reg, len, ret, r;
if (argc < 2 || argc > 3)
return CMD_RET_USAGE;
reg = simple_strtoul(argv[0], NULL, 16);
len = simple_strtoul(argv[1], NULL, 16);
if (argc == 3) {
u8 *addr;
addr = map_sysmem(simple_strtoul(argv[2], NULL, 16), len);
ret = dm_rtc_read(dev, reg, addr, len);
unmap_sysmem(addr);
if (ret) {
printf("dm_rtc_read() failed: %d\n", ret);
return CMD_RET_FAILURE;
}
return CMD_RET_SUCCESS;
}
while (len) {
r = min_t(int, len, sizeof(buf));
ret = dm_rtc_read(dev, reg, buf, r);
if (ret) {
printf("dm_rtc_read() failed: %d\n", ret);
return CMD_RET_FAILURE;
}
print_buffer(reg, buf, 1, r, 0);
len -= r;
reg += r;
}
return CMD_RET_SUCCESS;
}
static int do_rtc_write(struct udevice *dev, int argc, char * const argv[])
{
u8 buf[MAX_RTC_BYTES];
int reg, len, ret;
const char *s;
int slen;
if (argc < 2 || argc > 3)
return CMD_RET_USAGE;
reg = simple_strtoul(argv[0], NULL, 16);
if (argc == 3) {
u8 *addr;
len = simple_strtoul(argv[1], NULL, 16);
addr = map_sysmem(simple_strtoul(argv[2], NULL, 16), len);
ret = dm_rtc_write(dev, reg, addr, len);
unmap_sysmem(addr);
if (ret) {
printf("dm_rtc_write() failed: %d\n", ret);
return CMD_RET_FAILURE;
}
return CMD_RET_SUCCESS;
}
s = argv[1];
slen = strlen(s);
if (slen % 2) {
printf("invalid hex string\n");
return CMD_RET_FAILURE;
}
while (slen) {
len = min_t(int, slen / 2, sizeof(buf));
if (hex2bin(buf, s, len)) {
printf("invalid hex string\n");
return CMD_RET_FAILURE;
}
ret = dm_rtc_write(dev, reg, buf, len);
if (ret) {
printf("dm_rtc_write() failed: %d\n", ret);
return CMD_RET_FAILURE;
}
s += 2 * len;
slen -= 2 * len;
}
return CMD_RET_SUCCESS;
}
int do_rtc(struct cmd_tbl *cmdtp, int flag, int argc, char * const argv[])
{
static int curr_rtc;
struct udevice *dev;
int ret, idx;
if (argc < 2)
return CMD_RET_USAGE;
argc--;
argv++;
if (!strcmp(argv[0], "list")) {
struct uclass *uc;
idx = 0;
uclass_id_foreach_dev(UCLASS_RTC, dev, uc) {
printf("RTC #%d - %s\n", idx++, dev->name);
}
if (!idx) {
printf("*** no RTC devices available ***\n");
return CMD_RET_FAILURE;
}
return CMD_RET_SUCCESS;
}
idx = curr_rtc;
if (!strcmp(argv[0], "dev") && argc >= 2)
idx = simple_strtoul(argv[1], NULL, 10);
ret = uclass_get_device(UCLASS_RTC, idx, &dev);
if (ret) {
printf("Cannot find RTC #%d: err=%d\n", idx, ret);
return CMD_RET_FAILURE;
}
if (!strcmp(argv[0], "dev")) {
/* Show the existing or newly selected RTC */
if (argc >= 2)
curr_rtc = idx;
printf("RTC #%d - %s\n", idx, dev->name);
return CMD_RET_SUCCESS;
}
if (!strcmp(argv[0], "read"))
return do_rtc_read(dev, argc - 1, argv + 1);
if (!strcmp(argv[0], "write"))
return do_rtc_write(dev, argc - 1, argv + 1);
return CMD_RET_USAGE;
}
U_BOOT_CMD(
rtc, 5, 0, do_rtc,
"RTC subsystem",
"list - show available rtc devices\n"
"rtc dev [n] - show or set current rtc device\n"
"rtc read <reg> <count> - read and display 8-bit registers starting at <reg>\n"
"rtc read <reg> <count> <addr> - read 8-bit registers starting at <reg> to memory <addr>\n"
"rtc write <reg> <hexstring> - write 8-bit registers starting at <reg>\n"
"rtc write <reg> <count> <addr> - write from memory <addr> to 8-bit registers starting at <reg>\n"
);

View file

@ -10,6 +10,7 @@ CONFIG_SHOW_BOOT_PROGRESS=y
CONFIG_BOOTDELAY=3
CONFIG_BOARD_EARLY_INIT_R=y
CONFIG_SYS_PROMPT="G3#"
CONFIG_CMD_I2C=y
CONFIG_CMD_MMC=y
CONFIG_CMD_PART=y
CONFIG_CMD_WDT=y
@ -24,6 +25,8 @@ CONFIG_DEFAULT_DEVICE_TREE="ca-presidio-engboard"
# CONFIG_NET is not set
CONFIG_DM=y
CONFIG_CORTINA_GPIO=y
CONFIG_DM_I2C=y
CONFIG_SYS_I2C_CA=y
CONFIG_DM_MMC=y
CONFIG_MMC_DW=y
CONFIG_MMC_DW_CORTINA=y

View file

@ -60,6 +60,7 @@ CONFIG_CMD_LINK_LOCAL=y
CONFIG_CMD_ETHSW=y
CONFIG_CMD_BMP=y
CONFIG_CMD_EFIDEBUG=y
CONFIG_CMD_RTC=y
CONFIG_CMD_TIME=y
CONFIG_CMD_TIMER=y
CONFIG_CMD_SOUND=y

View file

@ -69,6 +69,7 @@ CONFIG_CMD_ETHSW=y
CONFIG_CMD_BMP=y
CONFIG_CMD_BOOTCOUNT=y
CONFIG_CMD_EFIDEBUG=y
CONFIG_CMD_RTC=y
CONFIG_CMD_TIME=y
CONFIG_CMD_TIMER=y
CONFIG_CMD_SOUND=y

View file

@ -49,6 +49,7 @@ CONFIG_CMD_SNTP=y
CONFIG_CMD_DNS=y
CONFIG_CMD_LINK_LOCAL=y
CONFIG_CMD_EFIDEBUG=y
CONFIG_CMD_RTC=y
CONFIG_CMD_TIME=y
CONFIG_CMD_TIMER=y
CONFIG_CMD_SOUND=y

View file

@ -0,0 +1,18 @@
* I2C for Cortina platforms
Required properties :
- compatible : Must be "cortina,ca-i2c"
- reg : Offset and length of the register set for the device
Recommended properties :
- clock-frequency : desired I2C bus clock frequency in Hz. If not specified,
default value is 100000. Possible values are 100000,
400000 and 1000000.
Examples :
i2c: i2c@f4329120 {
compatible = "cortina,ca-i2c";
reg = <0x0 0xf4329120 0x28>;
clock-frequency = <400000>;
};

View file

@ -0,0 +1,24 @@
* I2C controller embedded in Marvell Octeon platforms
Required properties :
- compatible : Must be "cavium,octeon-7890-twsi" or a compatible string
- reg : Offset and length of the register set for the device
- clocks: Must contain the input clock of the I2C instance
- #address-cells = <1>;
- #size-cells = <0>;
Optional properties :
- clock-frequency : Desired I2C bus clock frequency in Hz. If not specified,
the default 100 kHz frequency will be used. As only Normal, Fast and Fast+
modes are implemented, possible values are 100000, 400000 and 1000000.
Example :
i2c0: i2c@1180000001000 {
#address-cells = <1>;
#size-cells = <0>;
compatible = "cavium,octeon-7890-twsi";
reg = <0x11800 0x00001000 0x0 0x200>;
clock-frequency = <100000>;
clocks = <&sclk>;
};

View file

@ -0,0 +1,31 @@
SiFive PWM controller
Unlike most other PWM controllers, the SiFive PWM controller currently only
supports one period for all channels in the PWM. All PWMs need to run at
the same period. The period also has significant restrictions on the values
it can achieve, which the driver rounds to the nearest achievable period.
PWM RTL that corresponds to the IP block version numbers can be found
here:
https://github.com/sifive/sifive-blocks/tree/master/src/main/scala/devices/pwm
Required properties:
- compatible: Should be "sifive,<chip>-pwm" and "sifive,pwm<version>".
Supported compatible strings are: "sifive,fu540-c000-pwm" for the SiFive
PWM v0 as integrated onto the SiFive FU540 chip, and "sifive,pwm0" for the
SiFive PWM v0 IP block with no chip integration tweaks.
- reg: physical base address and length of the controller's registers
- clocks: Should contain a clock identifier for the PWM's parent clock.
- #pwm-cells: Should be 3.
- interrupts: one interrupt per PWM channel
Examples:
pwm: pwm@10020000 {
compatible = "sifive,fu540-c000-pwm", "sifive,pwm0";
reg = <0x0 0x10020000 0x0 0x1000>;
clocks = <&tlclk>;
interrupt-parent = <&plic>;
interrupts = <42 43 44 45>;
#pwm-cells = <3>;
};

View file

@ -93,6 +93,14 @@ config SYS_I2C_CADENCE
Say yes here to select Cadence I2C Host Controller. This controller is
e.g. used by Xilinx Zynq.
config SYS_I2C_CA
tristate "Cortina-Access I2C Controller"
depends on DM_I2C && CORTINA_PLATFORM
default n
help
Add support for the Cortina Access I2C host controller.
Say yes here to select Cortina-Access I2C Host Controller.
config SYS_I2C_DAVINCI
bool "Davinci I2C Controller"
depends on (ARCH_KEYSTONE || ARCH_DAVINCI)
@ -374,6 +382,16 @@ config SYS_I2C_SANDBOX
bus. Devices can be attached to the bus using the device tree
which specifies the driver to use. See sandbox.dts as an example.
config SYS_I2C_OCTEON
bool "Octeon II/III/TX/TX2 I2C driver"
depends on (ARCH_OCTEON || ARCH_OCTEONTX || ARCH_OCTEONTX2) && DM_I2C
default y
help
Add support for the Marvell Octeon I2C driver. This is used with
various Octeon parts such as Octeon II/III and OcteonTX/TX2. All
chips have several I2C ports and all are provided, controlled by
the device tree.
config SYS_I2C_S3C24X0
bool "Samsung I2C driver"
depends on ARCH_EXYNOS4 && DM_I2C

View file

@ -12,6 +12,7 @@ obj-$(CONFIG_SYS_I2C) += i2c_core.o
obj-$(CONFIG_SYS_I2C_ASPEED) += ast_i2c.o
obj-$(CONFIG_SYS_I2C_AT91) += at91_i2c.o
obj-$(CONFIG_SYS_I2C_CADENCE) += i2c-cdns.o
obj-$(CONFIG_SYS_I2C_CA) += i2c-cortina.o
obj-$(CONFIG_SYS_I2C_DAVINCI) += davinci_i2c.o
obj-$(CONFIG_SYS_I2C_DW) += designware_i2c.o
ifdef CONFIG_DM_PCI
@ -27,6 +28,7 @@ obj-$(CONFIG_SYS_I2C_LPC32XX) += lpc32xx_i2c.o
obj-$(CONFIG_SYS_I2C_MESON) += meson_i2c.o
obj-$(CONFIG_SYS_I2C_MVTWSI) += mvtwsi.o
obj-$(CONFIG_SYS_I2C_MXC) += mxc_i2c.o
obj-$(CONFIG_SYS_I2C_OCTEON) += octeon_i2c.o
obj-$(CONFIG_SYS_I2C_OMAP24XX) += omap24xx_i2c.o
obj-$(CONFIG_SYS_I2C_RCAR_I2C) += rcar_i2c.o
obj-$(CONFIG_SYS_I2C_RCAR_IIC) += rcar_iic.o

347
drivers/i2c/i2c-cortina.c Normal file
View file

@ -0,0 +1,347 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* (C) Copyright 2020
* Arthur Li, Cortina Access, arthur.li@cortina-access.com.
*/
#include <common.h>
#include <i2c.h>
#include <log.h>
#include <asm/io.h>
#include <dm.h>
#include <mapmem.h>
#include "i2c-cortina.h"
static void set_speed(struct i2c_regs *regs, int i2c_spd)
{
union ca_biw_cfg i2c_cfg;
i2c_cfg.wrd = readl(&regs->i2c_cfg);
i2c_cfg.bf.core_en = 0;
writel(i2c_cfg.wrd, &regs->i2c_cfg);
switch (i2c_spd) {
case IC_SPEED_MODE_FAST_PLUS:
i2c_cfg.bf.prer = CORTINA_PER_IO_FREQ /
(5 * I2C_SPEED_FAST_PLUS_RATE) - 1;
break;
case IC_SPEED_MODE_STANDARD:
i2c_cfg.bf.prer = CORTINA_PER_IO_FREQ /
(5 * I2C_SPEED_STANDARD_RATE) - 1;
break;
case IC_SPEED_MODE_FAST:
default:
i2c_cfg.bf.prer = CORTINA_PER_IO_FREQ /
(5 * I2C_SPEED_FAST_RATE) - 1;
break;
}
i2c_cfg.bf.core_en = 1;
writel(i2c_cfg.wrd, &regs->i2c_cfg);
}
static int ca_i2c_set_bus_speed(struct udevice *bus, unsigned int speed)
{
struct ca_i2c *priv = dev_get_priv(bus);
int i2c_spd;
if (speed >= I2C_SPEED_FAST_PLUS_RATE) {
i2c_spd = IC_SPEED_MODE_FAST_PLUS;
priv->speed = I2C_SPEED_FAST_PLUS_RATE;
} else if (speed >= I2C_SPEED_FAST_RATE) {
i2c_spd = IC_SPEED_MODE_FAST;
priv->speed = I2C_SPEED_FAST_RATE;
} else {
i2c_spd = IC_SPEED_MODE_STANDARD;
priv->speed = I2C_SPEED_STANDARD_RATE;
}
set_speed(priv->regs, i2c_spd);
return 0;
}
static int ca_i2c_get_bus_speed(struct udevice *bus)
{
struct ca_i2c *priv = dev_get_priv(bus);
return priv->speed;
}
static void ca_i2c_init(struct i2c_regs *regs)
{
union ca_biw_cfg i2c_cfg;
i2c_cfg.wrd = readl(&regs->i2c_cfg);
i2c_cfg.bf.core_en = 0;
i2c_cfg.bf.biw_soft_reset = 1;
writel(i2c_cfg.wrd, &regs->i2c_cfg);
mdelay(10);
i2c_cfg.bf.biw_soft_reset = 0;
writel(i2c_cfg.wrd, &regs->i2c_cfg);
set_speed(regs, IC_SPEED_MODE_STANDARD);
i2c_cfg.wrd = readl(&regs->i2c_cfg);
i2c_cfg.bf.core_en = 1;
writel(i2c_cfg.wrd, &regs->i2c_cfg);
}
static int i2c_wait_complete(struct i2c_regs *regs)
{
union ca_biw_ctrl i2c_ctrl;
unsigned long start_time_bb = get_timer(0);
i2c_ctrl.wrd = readl(&regs->i2c_ctrl);
while (i2c_ctrl.bf.biwdone == 0) {
i2c_ctrl.wrd = readl(&regs->i2c_ctrl);
if (get_timer(start_time_bb) >
(unsigned long)(I2C_BYTE_TO_BB)) {
printf("%s not done!!!\n", __func__);
return -ETIMEDOUT;
}
}
/* Clear done bit */
writel(i2c_ctrl.wrd, &regs->i2c_ctrl);
return 0;
}
static void i2c_setaddress(struct i2c_regs *regs, unsigned int i2c_addr,
int write_read)
{
writel(i2c_addr | write_read, &regs->i2c_txr);
writel(BIW_CTRL_START | BIW_CTRL_WRITE,
&regs->i2c_ctrl);
i2c_wait_complete(regs);
}
static int i2c_wait_for_bus_busy(struct i2c_regs *regs)
{
union ca_biw_ack i2c_ack;
unsigned long start_time_bb = get_timer(0);
i2c_ack.wrd = readl(&regs->i2c_ack);
while (i2c_ack.bf.biw_busy) {
i2c_ack.wrd = readl(&regs->i2c_ack);
if (get_timer(start_time_bb) >
(unsigned long)(I2C_BYTE_TO_BB)) {
printf("%s: timeout!\n", __func__);
return -ETIMEDOUT;
}
}
return 0;
}
static int i2c_xfer_init(struct i2c_regs *regs, uint8_t chip, uint addr,
int alen, int write_read)
{
int addr_len = alen;
if (i2c_wait_for_bus_busy(regs))
return 1;
/* First cycle must write addr + offset */
chip = ((chip & 0x7F) << 1);
if (alen == 0 && write_read == I2C_CMD_RD)
i2c_setaddress(regs, chip, I2C_CMD_RD);
else
i2c_setaddress(regs, chip, I2C_CMD_WT);
while (alen) {
alen--;
writel(addr, &regs->i2c_txr);
if (write_read == I2C_CMD_RD)
writel(BIW_CTRL_WRITE | BIW_CTRL_STOP,
&regs->i2c_ctrl);
else
writel(BIW_CTRL_WRITE, &regs->i2c_ctrl);
i2c_wait_complete(regs);
}
/* Send address again with Read flag if it's read command */
if (write_read == I2C_CMD_RD && addr_len > 0)
i2c_setaddress(regs, chip, I2C_CMD_RD);
return 0;
}
static int i2c_xfer_finish(struct i2c_regs *regs)
{
/* Dummy read makes bus free */
writel(BIW_CTRL_READ | BIW_CTRL_STOP, &regs->i2c_ctrl);
i2c_wait_complete(regs);
if (i2c_wait_for_bus_busy(regs)) {
printf("Timed out waiting for bus\n");
return -ETIMEDOUT;
}
return 0;
}
static int ca_i2c_read(struct i2c_regs *regs, uint8_t chip, uint addr,
int alen, uint8_t *buffer, int len)
{
unsigned long start_time_rx;
int rc = 0;
rc = i2c_xfer_init(regs, chip, addr, alen, I2C_CMD_RD);
if (rc)
return rc;
start_time_rx = get_timer(0);
while (len) {
/* ACK_IN is ack value to send during read.
* ack high only on the very last byte!
*/
if (len == 1)
writel(BIW_CTRL_READ | BIW_CTRL_ACK_IN | BIW_CTRL_STOP,
&regs->i2c_ctrl);
else
writel(BIW_CTRL_READ, &regs->i2c_ctrl);
rc = i2c_wait_complete(regs);
udelay(1);
if (rc == 0) {
*buffer++ =
(uchar) readl(&regs->i2c_rxr);
len--;
start_time_rx = get_timer(0);
} else if (get_timer(start_time_rx) > I2C_BYTE_TO) {
return -ETIMEDOUT;
}
}
i2c_xfer_finish(regs);
return rc;
}
static int ca_i2c_write(struct i2c_regs *regs, uint8_t chip, uint addr,
int alen, uint8_t *buffer, int len)
{
int rc, nb = len;
unsigned long start_time_tx;
rc = i2c_xfer_init(regs, chip, addr, alen, I2C_CMD_WT);
if (rc)
return rc;
start_time_tx = get_timer(0);
while (len) {
writel(*buffer, &regs->i2c_txr);
if (len == 1)
writel(BIW_CTRL_WRITE | BIW_CTRL_STOP,
&regs->i2c_ctrl);
else
writel(BIW_CTRL_WRITE, &regs->i2c_ctrl);
rc = i2c_wait_complete(regs);
if (rc == 0) {
len--;
buffer++;
start_time_tx = get_timer(0);
} else if (get_timer(start_time_tx) > (nb * I2C_BYTE_TO)) {
return -ETIMEDOUT;
}
}
return 0;
}
static int ca_i2c_probe_chip(struct udevice *bus, uint chip_addr,
uint chip_flags)
{
struct ca_i2c *priv = dev_get_priv(bus);
int ret;
u32 tmp;
/* Try to read the first location of the chip */
ret = ca_i2c_read(priv->regs, chip_addr, 0, 1, (uchar *)&tmp, 1);
if (ret)
ca_i2c_init(priv->regs);
return ret;
}
static int ca_i2c_xfer(struct udevice *bus, struct i2c_msg *msg, int nmsgs)
{
struct ca_i2c *priv = dev_get_priv(bus);
int ret;
debug("i2c_xfer: %d messages\n", nmsgs);
for (; nmsgs > 0; nmsgs--, msg++) {
debug("i2c_xfer: chip=0x%x, len=0x%x\n", msg->addr, msg->len);
if (msg->flags & I2C_M_RD)
ret = ca_i2c_read(priv->regs, msg->addr, 0, 0,
msg->buf, msg->len);
else
ret = ca_i2c_write(priv->regs, msg->addr, 0, 0,
msg->buf, msg->len);
if (ret) {
printf("i2c_xfer: %s error\n",
msg->flags & I2C_M_RD ? "read" : "write");
return ret;
}
}
return 0;
}
static const struct dm_i2c_ops ca_i2c_ops = {
.xfer = ca_i2c_xfer,
.probe_chip = ca_i2c_probe_chip,
.set_bus_speed = ca_i2c_set_bus_speed,
.get_bus_speed = ca_i2c_get_bus_speed,
};
static const struct udevice_id ca_i2c_ids[] = {
{ .compatible = "cortina,ca-i2c", },
{ }
};
static int ca_i2c_probe(struct udevice *bus)
{
struct ca_i2c *priv = dev_get_priv(bus);
ca_i2c_init(priv->regs);
return 0;
}
static int ca_i2c_ofdata_to_platdata(struct udevice *bus)
{
struct ca_i2c *priv = dev_get_priv(bus);
priv->regs = map_sysmem(dev_read_addr(bus), sizeof(struct i2c_regs));
if (!priv->regs) {
printf("I2C: base address is invalid\n");
return -EINVAL;
}
return 0;
}
U_BOOT_DRIVER(i2c_cortina) = {
.name = "i2c_cortina",
.id = UCLASS_I2C,
.of_match = ca_i2c_ids,
.ofdata_to_platdata = ca_i2c_ofdata_to_platdata,
.probe = ca_i2c_probe,
.priv_auto_alloc_size = sizeof(struct ca_i2c),
.ops = &ca_i2c_ops,
.flags = DM_FLAG_PRE_RELOC,
};

87
drivers/i2c/i2c-cortina.h Normal file
View file

@ -0,0 +1,87 @@
/* SPDX-License-Identifier: GPL-2.0+ */
/*
* (C) Copyright 2019
* Cortina Access, <www.cortina-access.com>
*/
#ifndef __CA_I2C_H_
#define __CA_I2C_H_
#include <linux/bitops.h>
#include <linux/delay.h>
#if !defined(__ASSEMBLER__) && !defined(__ASSEMBLY__)
struct i2c_regs {
u32 i2c_cfg;
u32 i2c_ctrl;
u32 i2c_txr;
u32 i2c_rxr;
u32 i2c_ack;
u32 i2c_ie0;
u32 i2c_int0;
u32 i2c_ie1;
u32 i2c_int1;
u32 i2c_stat;
};
union ca_biw_cfg {
struct biw_cfg {
u32 core_en : 1;
u32 biw_soft_reset : 1;
u32 busywait_en : 1;
u32 stretch_en : 1;
u32 arb_en : 1;
u32 clksync_en : 1;
u32 rsrvd1 : 2;
u32 spike_cnt : 4;
u32 rsrvd2 : 4;
u32 prer : 16;
} bf;
unsigned int wrd;
};
union ca_biw_ctrl {
struct biw_ctrl {
u32 biwdone : 1;
u32 rsrvd1 : 2;
u32 ack_in : 1;
u32 write : 1;
u32 read : 1;
u32 stop : 1;
u32 start : 1;
u32 rsrvd2 : 24;
} bf;
unsigned int wrd;
};
union ca_biw_ack {
struct biw_ack {
u32 al :1;
u32 biw_busy :1;
u32 ack_out :1;
u32 rsrvd1 :29;
} bf;
unsigned int wrd;
};
#endif /* !__ASSEMBLER__*/
struct ca_i2c {
struct i2c_regs *regs;
unsigned int speed;
};
#define I2C_CMD_WT 0
#define I2C_CMD_RD 1
#define BIW_CTRL_DONE BIT(0)
#define BIW_CTRL_ACK_IN BIT(3)
#define BIW_CTRL_WRITE BIT(4)
#define BIW_CTRL_READ BIT(5)
#define BIW_CTRL_STOP BIT(6)
#define BIW_CTRL_START BIT(7)
#define I2C_BYTE_TO (CONFIG_SYS_HZ / 500)
#define I2C_STOPDET_TO (CONFIG_SYS_HZ / 500)
#define I2C_BYTE_TO_BB (10)
#endif /* __CA_I2C_H_ */

View file

@ -97,7 +97,8 @@ static int bus_i2c_wait_for_tx_ready(struct imx_lpi2c_reg *regs)
static int bus_i2c_send(struct udevice *bus, u8 *txbuf, int len)
{
struct imx_lpi2c_reg *regs = (struct imx_lpi2c_reg *)devfdt_get_addr(bus);
struct imx_lpi2c_bus *i2c_bus = dev_get_priv(bus);
struct imx_lpi2c_reg *regs = (struct imx_lpi2c_reg *)(i2c_bus->base);
lpi2c_status_t result = LPI2C_SUCESS;
/* empty tx */
@ -118,7 +119,8 @@ static int bus_i2c_send(struct udevice *bus, u8 *txbuf, int len)
static int bus_i2c_receive(struct udevice *bus, u8 *rxbuf, int len)
{
struct imx_lpi2c_reg *regs = (struct imx_lpi2c_reg *)devfdt_get_addr(bus);
struct imx_lpi2c_bus *i2c_bus = dev_get_priv(bus);
struct imx_lpi2c_reg *regs = (struct imx_lpi2c_reg *)(i2c_bus->base);
lpi2c_status_t result = LPI2C_SUCESS;
u32 val;
ulong start_time = get_timer(0);
@ -162,8 +164,8 @@ static int bus_i2c_receive(struct udevice *bus, u8 *rxbuf, int len)
static int bus_i2c_start(struct udevice *bus, u8 addr, u8 dir)
{
lpi2c_status_t result;
struct imx_lpi2c_reg *regs =
(struct imx_lpi2c_reg *)devfdt_get_addr(bus);
struct imx_lpi2c_bus *i2c_bus = dev_get_priv(bus);
struct imx_lpi2c_reg *regs = (struct imx_lpi2c_reg *)(i2c_bus->base);
u32 val;
result = imx_lpci2c_check_busy_bus(regs);
@ -199,8 +201,8 @@ static int bus_i2c_start(struct udevice *bus, u8 addr, u8 dir)
static int bus_i2c_stop(struct udevice *bus)
{
lpi2c_status_t result;
struct imx_lpi2c_reg *regs =
(struct imx_lpi2c_reg *)devfdt_get_addr(bus);
struct imx_lpi2c_bus *i2c_bus = dev_get_priv(bus);
struct imx_lpi2c_reg *regs = (struct imx_lpi2c_reg *)(i2c_bus->base);
u32 status;
ulong start_time;
@ -271,7 +273,7 @@ u32 __weak imx_get_i2cclk(u32 i2c_num)
static int bus_i2c_set_bus_speed(struct udevice *bus, int speed)
{
struct imx_lpi2c_bus *i2c_bus = dev_get_priv(bus);
struct imx_lpi2c_reg *regs;
struct imx_lpi2c_reg *regs = (struct imx_lpi2c_reg *)(i2c_bus->base);
u32 val;
u32 preescale = 0, best_pre = 0, clkhi = 0;
u32 best_clkhi = 0, abs_error = 0, rate;
@ -280,8 +282,6 @@ static int bus_i2c_set_bus_speed(struct udevice *bus, int speed)
bool mode;
int i;
regs = (struct imx_lpi2c_reg *)devfdt_get_addr(bus);
if (IS_ENABLED(CONFIG_CLK)) {
clock_rate = clk_get_rate(&i2c_bus->per_clk);
if (clock_rate <= 0) {
@ -348,11 +348,11 @@ static int bus_i2c_set_bus_speed(struct udevice *bus, int speed)
static int bus_i2c_init(struct udevice *bus, int speed)
{
struct imx_lpi2c_reg *regs;
u32 val;
int ret;
regs = (struct imx_lpi2c_reg *)devfdt_get_addr(bus);
struct imx_lpi2c_bus *i2c_bus = dev_get_priv(bus);
struct imx_lpi2c_reg *regs = (struct imx_lpi2c_reg *)(i2c_bus->base);
/* reset peripheral */
writel(LPI2C_MCR_RST_MASK, &regs->mcr);
writel(0x0, &regs->mcr);

847
drivers/i2c/octeon_i2c.c Normal file
View file

@ -0,0 +1,847 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2018 Marvell International Ltd.
*/
#include <common.h>
#include <clk.h>
#include <dm.h>
#include <i2c.h>
#include <pci_ids.h>
#include <asm/io.h>
#include <linux/bitfield.h>
#include <linux/compat.h>
#include <linux/delay.h>
#define TWSI_SW_TWSI 0x00
#define TWSI_TWSI_SW 0x08
#define TWSI_INT 0x10
#define TWSI_SW_TWSI_EXT 0x18
#define TWSI_SW_DATA_MASK GENMASK_ULL(31, 0)
#define TWSI_SW_EOP_IA_MASK GENMASK_ULL(34, 32)
#define TWSI_SW_IA_MASK GENMASK_ULL(39, 35)
#define TWSI_SW_ADDR_MASK GENMASK_ULL(49, 40)
#define TWSI_SW_SCR_MASK GENMASK_ULL(51, 50)
#define TWSI_SW_SIZE_MASK GENMASK_ULL(54, 52)
#define TWSI_SW_SOVR BIT_ULL(55)
#define TWSI_SW_R BIT_ULL(56)
#define TWSI_SW_OP_MASK GENMASK_ULL(60, 57)
#define TWSI_SW_EIA GENMASK_ULL(61)
#define TWSI_SW_SLONLY BIT_ULL(62)
#define TWSI_SW_V BIT_ULL(63)
#define TWSI_INT_SDA_OVR BIT_ULL(8)
#define TWSI_INT_SCL_OVR BIT_ULL(9)
#define TWSI_INT_SDA BIT_ULL(10)
#define TWSI_INT_SCL BIT_ULL(11)
enum {
TWSI_OP_WRITE = 0,
TWSI_OP_READ = 1,
};
enum {
TWSI_EOP_SLAVE_ADDR = 0,
TWSI_EOP_CLK_CTL = 3,
TWSI_SW_EOP_IA = 6,
};
enum {
TWSI_SLAVEADD = 0,
TWSI_DATA = 1,
TWSI_CTL = 2,
TWSI_CLKCTL = 3,
TWSI_STAT = 3,
TWSI_SLAVEADD_EXT = 4,
TWSI_RST = 7,
};
enum {
TWSI_CTL_AAK = BIT(2),
TWSI_CTL_IFLG = BIT(3),
TWSI_CTL_STP = BIT(4),
TWSI_CTL_STA = BIT(5),
TWSI_CTL_ENAB = BIT(6),
TWSI_CTL_CE = BIT(7),
};
/*
* Internal errors. When debugging is enabled, the driver will report the
* error number and the user / developer can check the table below for the
* detailed error description.
*/
enum {
/** Bus error */
TWSI_STAT_BUS_ERROR = 0x00,
/** Start condition transmitted */
TWSI_STAT_START = 0x08,
/** Repeat start condition transmitted */
TWSI_STAT_RSTART = 0x10,
/** Address + write bit transmitted, ACK received */
TWSI_STAT_TXADDR_ACK = 0x18,
/** Address + write bit transmitted, /ACK received */
TWSI_STAT_TXADDR_NAK = 0x20,
/** Data byte transmitted in master mode, ACK received */
TWSI_STAT_TXDATA_ACK = 0x28,
/** Data byte transmitted in master mode, ACK received */
TWSI_STAT_TXDATA_NAK = 0x30,
/** Arbitration lost in address or data byte */
TWSI_STAT_TX_ARB_LOST = 0x38,
/** Address + read bit transmitted, ACK received */
TWSI_STAT_RXADDR_ACK = 0x40,
/** Address + read bit transmitted, /ACK received */
TWSI_STAT_RXADDR_NAK = 0x48,
/** Data byte received in master mode, ACK transmitted */
TWSI_STAT_RXDATA_ACK_SENT = 0x50,
/** Data byte received, NACK transmitted */
TWSI_STAT_RXDATA_NAK_SENT = 0x58,
/** Slave address received, sent ACK */
TWSI_STAT_SLAVE_RXADDR_ACK = 0x60,
/**
* Arbitration lost in address as master, slave address + write bit
* received, ACK transmitted
*/
TWSI_STAT_TX_ACK_ARB_LOST = 0x68,
/** General call address received, ACK transmitted */
TWSI_STAT_RX_GEN_ADDR_ACK = 0x70,
/**
* Arbitration lost in address as master, general call address
* received, ACK transmitted
*/
TWSI_STAT_RX_GEN_ADDR_ARB_LOST = 0x78,
/** Data byte received after slave address received, ACK transmitted */
TWSI_STAT_SLAVE_RXDATA_ACK = 0x80,
/** Data byte received after slave address received, /ACK transmitted */
TWSI_STAT_SLAVE_RXDATA_NAK = 0x88,
/**
* Data byte received after general call address received, ACK
* transmitted
*/
TWSI_STAT_GEN_RXADDR_ACK = 0x90,
/**
* Data byte received after general call address received, /ACK
* transmitted
*/
TWSI_STAT_GEN_RXADDR_NAK = 0x98,
/** STOP or repeated START condition received in slave mode */
TWSI_STAT_STOP_MULTI_START = 0xa0,
/** Slave address + read bit received, ACK transmitted */
TWSI_STAT_SLAVE_RXADDR2_ACK = 0xa8,
/**
* Arbitration lost in address as master, slave address + read bit
* received, ACK transmitted
*/
TWSI_STAT_RXDATA_ACK_ARB_LOST = 0xb0,
/** Data byte transmitted in slave mode, ACK received */
TWSI_STAT_SLAVE_TXDATA_ACK = 0xb8,
/** Data byte transmitted in slave mode, /ACK received */
TWSI_STAT_SLAVE_TXDATA_NAK = 0xc0,
/** Last byte transmitted in slave mode, ACK received */
TWSI_STAT_SLAVE_TXDATA_END_ACK = 0xc8,
/** Second address byte + write bit transmitted, ACK received */
TWSI_STAT_TXADDR2DATA_ACK = 0xd0,
/** Second address byte + write bit transmitted, /ACK received */
TWSI_STAT_TXADDR2DATA_NAK = 0xd8,
/** No relevant status information */
TWSI_STAT_IDLE = 0xf8
};
#define CONFIG_SYS_I2C_OCTEON_SLAVE_ADDR 0x77
enum {
PROBE_PCI = 0, /* PCI based probing */
PROBE_DT, /* DT based probing */
};
enum {
CLK_METHOD_OCTEON = 0,
CLK_METHOD_OCTEONTX2,
};
/**
* struct octeon_i2c_data - SoC specific data of this driver
*
* @probe: Probing of this SoC (DT vs PCI)
* @reg_offs: Register offset
* @thp: THP define for divider calculation
* @clk_method: Clock calculation method
*/
struct octeon_i2c_data {
int probe;
u32 reg_offs;
int thp;
int clk_method;
};
/**
* struct octeon_twsi - Private data of this driver
*
* @base: Base address of i2c registers
* @data: Pointer to SoC specific data struct
*/
struct octeon_twsi {
void __iomem *base;
const struct octeon_i2c_data *data;
struct clk clk;
};
static void twsi_unblock(void *base);
static int twsi_stop(void *base);
/**
* Returns true if we lost arbitration
*
* @code status code
* @final_read true if this is the final read operation
* @return true if arbitration has been lost, false if it hasn't been lost.
*/
static int twsi_i2c_lost_arb(u8 code, int final_read)
{
switch (code) {
case TWSI_STAT_TX_ARB_LOST:
case TWSI_STAT_TX_ACK_ARB_LOST:
case TWSI_STAT_RX_GEN_ADDR_ARB_LOST:
case TWSI_STAT_RXDATA_ACK_ARB_LOST:
/* Arbitration lost */
return -EAGAIN;
case TWSI_STAT_SLAVE_RXADDR_ACK:
case TWSI_STAT_RX_GEN_ADDR_ACK:
case TWSI_STAT_GEN_RXADDR_ACK:
case TWSI_STAT_GEN_RXADDR_NAK:
/* Being addressed as slave, should back off and listen */
return -EIO;
case TWSI_STAT_SLAVE_RXDATA_ACK:
case TWSI_STAT_SLAVE_RXDATA_NAK:
case TWSI_STAT_STOP_MULTI_START:
case TWSI_STAT_SLAVE_RXADDR2_ACK:
case TWSI_STAT_SLAVE_TXDATA_ACK:
case TWSI_STAT_SLAVE_TXDATA_NAK:
case TWSI_STAT_SLAVE_TXDATA_END_ACK:
/* Core busy as slave */
return -EIO;
case TWSI_STAT_RXDATA_ACK_SENT:
/* Ack allowed on pre-terminal bytes only */
if (!final_read)
return 0;
return -EAGAIN;
case TWSI_STAT_RXDATA_NAK_SENT:
/* NAK allowed on terminal byte only */
if (!final_read)
return 0;
return -EAGAIN;
case TWSI_STAT_TXDATA_NAK:
case TWSI_STAT_TXADDR_NAK:
case TWSI_STAT_RXADDR_NAK:
case TWSI_STAT_TXADDR2DATA_NAK:
return -EAGAIN;
}
return 0;
}
/**
* Writes to the MIO_TWS(0..5)_SW_TWSI register
*
* @base Base address of i2c registers
* @val value to write
* @return 0 for success, otherwise error
*/
static u64 twsi_write_sw(void __iomem *base, u64 val)
{
unsigned long start = get_timer(0);
val &= ~TWSI_SW_R;
val |= TWSI_SW_V;
debug("%s(%p, 0x%llx)\n", __func__, base, val);
writeq(val, base + TWSI_SW_TWSI);
do {
val = readq(base + TWSI_SW_TWSI);
} while ((val & TWSI_SW_V) && (get_timer(start) < 50));
if (val & TWSI_SW_V)
debug("%s: timed out\n", __func__);
return val;
}
/**
* Reads the MIO_TWS(0..5)_SW_TWSI register
*
* @base Base address of i2c registers
* @val value for eia and op, etc. to read
* @return value of the register
*/
static u64 twsi_read_sw(void __iomem *base, u64 val)
{
unsigned long start = get_timer(0);
val |= TWSI_SW_R | TWSI_SW_V;
debug("%s(%p, 0x%llx)\n", __func__, base, val);
writeq(val, base + TWSI_SW_TWSI);
do {
val = readq(base + TWSI_SW_TWSI);
} while ((val & TWSI_SW_V) && (get_timer(start) < 50));
if (val & TWSI_SW_V)
debug("%s: Error writing 0x%llx\n", __func__, val);
debug("%s: Returning 0x%llx\n", __func__, val);
return val;
}
/**
* Write control register
*
* @base Base address for i2c registers
* @data data to write
*/
static void twsi_write_ctl(void __iomem *base, u8 data)
{
u64 val;
debug("%s(%p, 0x%x)\n", __func__, base, data);
val = data | FIELD_PREP(TWSI_SW_EOP_IA_MASK, TWSI_CTL) |
FIELD_PREP(TWSI_SW_OP_MASK, TWSI_SW_EOP_IA);
twsi_write_sw(base, val);
}
/**
* Reads the TWSI Control Register
*
* @base Base address for i2c
* @return 8-bit TWSI control register
*/
static u8 twsi_read_ctl(void __iomem *base)
{
u64 val;
val = FIELD_PREP(TWSI_SW_EOP_IA_MASK, TWSI_CTL) |
FIELD_PREP(TWSI_SW_OP_MASK, TWSI_SW_EOP_IA);
val = twsi_read_sw(base, val);
debug("%s(%p): 0x%x\n", __func__, base, (u8)val);
return (u8)val;
}
/**
* Read i2c status register
*
* @base Base address of i2c registers
* @return value of status register
*/
static u8 twsi_read_status(void __iomem *base)
{
u64 val;
val = FIELD_PREP(TWSI_SW_EOP_IA_MASK, TWSI_STAT) |
FIELD_PREP(TWSI_SW_OP_MASK, TWSI_SW_EOP_IA);
return twsi_read_sw(base, val);
}
/**
* Waits for an i2c operation to complete
*
* @param base Base address of registers
* @return 0 for success, 1 if timeout
*/
static int twsi_wait(void __iomem *base)
{
unsigned long start = get_timer(0);
u8 twsi_ctl;
debug("%s(%p)\n", __func__, base);
do {
twsi_ctl = twsi_read_ctl(base);
twsi_ctl &= TWSI_CTL_IFLG;
} while (!twsi_ctl && get_timer(start) < 50);
debug(" return: %u\n", !twsi_ctl);
return !twsi_ctl;
}
/**
* Unsticks the i2c bus
*
* @base base address of registers
*/
static int twsi_start_unstick(void __iomem *base)
{
twsi_stop(base);
twsi_unblock(base);
return 0;
}
/**
* Sends an i2c start condition
*
* @base base address of registers
* @return 0 for success, otherwise error
*/
static int twsi_start(void __iomem *base)
{
int ret;
u8 stat;
debug("%s(%p)\n", __func__, base);
twsi_write_ctl(base, TWSI_CTL_STA | TWSI_CTL_ENAB);
ret = twsi_wait(base);
if (ret) {
stat = twsi_read_status(base);
debug("%s: ret: 0x%x, status: 0x%x\n", __func__, ret, stat);
switch (stat) {
case TWSI_STAT_START:
case TWSI_STAT_RSTART:
return 0;
case TWSI_STAT_RXADDR_ACK:
default:
return twsi_start_unstick(base);
}
}
debug("%s: success\n", __func__);
return 0;
}
/**
* Sends an i2c stop condition
*
* @base register base address
* @return 0 for success, -1 if error
*/
static int twsi_stop(void __iomem *base)
{
u8 stat;
twsi_write_ctl(base, TWSI_CTL_STP | TWSI_CTL_ENAB);
stat = twsi_read_status(base);
if (stat != TWSI_STAT_IDLE) {
debug("%s: Bad status on bus@%p\n", __func__, base);
return -1;
}
return 0;
}
/**
* Writes data to the i2c bus
*
* @base register base address
* @slave_addr address of slave to write to
* @buffer Pointer to buffer to write
* @length Number of bytes in buffer to write
* @return 0 for success, otherwise error
*/
static int twsi_write_data(void __iomem *base, u8 slave_addr,
u8 *buffer, unsigned int length)
{
unsigned int curr = 0;
u64 val;
int ret;
debug("%s(%p, 0x%x, %p, 0x%x)\n", __func__, base, slave_addr,
buffer, length);
ret = twsi_start(base);
if (ret) {
debug("%s: Could not start BUS transaction\n", __func__);
return -1;
}
ret = twsi_wait(base);
if (ret) {
debug("%s: wait failed\n", __func__);
return ret;
}
val = (u32)(slave_addr << 1) | TWSI_OP_WRITE |
FIELD_PREP(TWSI_SW_EOP_IA_MASK, TWSI_DATA) |
FIELD_PREP(TWSI_SW_OP_MASK, TWSI_SW_EOP_IA);
twsi_write_sw(base, val);
twsi_write_ctl(base, TWSI_CTL_ENAB);
debug("%s: Waiting\n", __func__);
ret = twsi_wait(base);
if (ret) {
debug("%s: Timed out writing slave address 0x%x to target\n",
__func__, slave_addr);
return ret;
}
ret = twsi_read_status(base);
debug("%s: status: 0x%x\n", __func__, ret);
if (ret != TWSI_STAT_TXADDR_ACK) {
debug("%s: status: 0x%x\n", __func__, ret);
twsi_stop(base);
return twsi_i2c_lost_arb(ret, 0);
}
while (curr < length) {
val = buffer[curr++] |
FIELD_PREP(TWSI_SW_EOP_IA_MASK, TWSI_DATA) |
FIELD_PREP(TWSI_SW_OP_MASK, TWSI_SW_EOP_IA);
twsi_write_sw(base, val);
twsi_write_ctl(base, TWSI_CTL_ENAB);
debug("%s: Writing 0x%llx\n", __func__, val);
ret = twsi_wait(base);
if (ret) {
debug("%s: Timed out writing data to 0x%x\n",
__func__, slave_addr);
return ret;
}
ret = twsi_read_status(base);
debug("%s: status: 0x%x\n", __func__, ret);
}
debug("%s: Stopping\n", __func__);
return twsi_stop(base);
}
/**
* Manually clear the I2C bus and send a stop
*
* @base register base address
*/
static void twsi_unblock(void __iomem *base)
{
int i;
for (i = 0; i < 9; i++) {
writeq(0, base + TWSI_INT);
udelay(5);
writeq(TWSI_INT_SCL_OVR, base + TWSI_INT);
udelay(5);
}
writeq(TWSI_INT_SCL_OVR | TWSI_INT_SDA_OVR, base + TWSI_INT);
udelay(5);
writeq(TWSI_INT_SDA_OVR, base + TWSI_INT);
udelay(5);
writeq(0, base + TWSI_INT);
udelay(5);
}
/**
* Performs a read transaction on the i2c bus
*
* @base Base address of twsi registers
* @slave_addr i2c bus address to read from
* @buffer buffer to read into
* @length number of bytes to read
* @return 0 for success, otherwise error
*/
static int twsi_read_data(void __iomem *base, u8 slave_addr,
u8 *buffer, unsigned int length)
{
unsigned int curr = 0;
u64 val;
int ret;
debug("%s(%p, 0x%x, %p, %u)\n", __func__, base, slave_addr,
buffer, length);
ret = twsi_start(base);
if (ret) {
debug("%s: start failed\n", __func__);
return ret;
}
ret = twsi_wait(base);
if (ret) {
debug("%s: wait failed\n", __func__);
return ret;
}
val = (u32)(slave_addr << 1) | TWSI_OP_READ |
FIELD_PREP(TWSI_SW_EOP_IA_MASK, TWSI_DATA) |
FIELD_PREP(TWSI_SW_OP_MASK, TWSI_SW_EOP_IA);
twsi_write_sw(base, val);
twsi_write_ctl(base, TWSI_CTL_ENAB);
ret = twsi_wait(base);
if (ret) {
debug("%s: waiting for sending addr failed\n", __func__);
return ret;
}
ret = twsi_read_status(base);
debug("%s: status: 0x%x\n", __func__, ret);
if (ret != TWSI_STAT_RXADDR_ACK) {
debug("%s: status: 0x%x\n", __func__, ret);
twsi_stop(base);
return twsi_i2c_lost_arb(ret, 0);
}
while (curr < length) {
twsi_write_ctl(base, TWSI_CTL_ENAB |
((curr < length - 1) ? TWSI_CTL_AAK : 0));
ret = twsi_wait(base);
if (ret) {
debug("%s: waiting for data failed\n", __func__);
return ret;
}
val = twsi_read_sw(base, val);
buffer[curr++] = (u8)val;
}
twsi_stop(base);
return 0;
}
/**
* Calculate the divisor values
*
* @speed Speed to set
* @m_div Pointer to M divisor
* @n_div Pointer to N divisor
* @return 0 for success, otherwise error
*/
static void twsi_calc_div(struct udevice *bus, ulong sclk, unsigned int speed,
int *m_div, int *n_div)
{
struct octeon_twsi *twsi = dev_get_priv(bus);
int thp = twsi->data->thp;
int tclk, fsamp;
int ndiv, mdiv;
if (twsi->data->clk_method == CLK_METHOD_OCTEON) {
tclk = sclk / (2 * (thp + 1));
} else {
/* Refclk src in mode register defaults to 100MHz clock */
sclk = 100000000; /* 100 Mhz */
tclk = sclk / (thp + 2);
}
debug("%s( io_clock %lu tclk %u)\n", __func__, sclk, tclk);
/*
* Compute the clocks M divider:
*
* TWSI freq = (core freq) / (10 x (M+1) x 2 * (thp+1) x 2^N)
* M = ((core freq) / (10 x (TWSI freq) x 2 * (thp+1) x 2^N)) - 1
*
* For OcteonTX2 -
* TWSI freq = (core freq) / (10 x (M+1) x (thp+2) x 2^N)
* M = ((core freq) / (10 x (TWSI freq) x (thp+2) x 2^N)) - 1
*/
for (ndiv = 0; ndiv < 8; ndiv++) {
fsamp = tclk / (1 << ndiv);
mdiv = fsamp / speed / 10;
mdiv -= 1;
if (mdiv < 16)
break;
}
*m_div = mdiv;
*n_div = ndiv;
}
/**
* Init I2C controller
*
* @base Base address of twsi registers
* @slave_addr I2C slave address to configure this controller to
* @return 0 for success, otherwise error
*/
static int twsi_init(void __iomem *base, int slaveaddr)
{
u64 val;
debug("%s (%p, 0x%x)\n", __func__, base, slaveaddr);
val = slaveaddr << 1 |
FIELD_PREP(TWSI_SW_EOP_IA_MASK, 0) |
FIELD_PREP(TWSI_SW_OP_MASK, TWSI_SW_EOP_IA) |
TWSI_SW_V;
twsi_write_sw(base, val);
/* Set slave address */
val = slaveaddr |
FIELD_PREP(TWSI_SW_EOP_IA_MASK, TWSI_EOP_SLAVE_ADDR) |
FIELD_PREP(TWSI_SW_OP_MASK, TWSI_SW_EOP_IA) |
TWSI_SW_V;
twsi_write_sw(base, val);
return 0;
}
/**
* Transfers data over the i2c bus
*
* @bus i2c bus to transfer data over
* @msg Array of i2c messages
* @nmsgs Number of messages to send/receive
* @return 0 for success, otherwise error
*/
static int octeon_i2c_xfer(struct udevice *bus, struct i2c_msg *msg,
int nmsgs)
{
struct octeon_twsi *twsi = dev_get_priv(bus);
int ret;
int i;
debug("%s: %d messages\n", __func__, nmsgs);
for (i = 0; i < nmsgs; i++, msg++) {
debug("%s: chip=0x%x, len=0x%x\n", __func__, msg->addr,
msg->len);
if (msg->flags & I2C_M_RD) {
debug("%s: Reading data\n", __func__);
ret = twsi_read_data(twsi->base, msg->addr,
msg->buf, msg->len);
} else {
debug("%s: Writing data\n", __func__);
ret = twsi_write_data(twsi->base, msg->addr,
msg->buf, msg->len);
}
if (ret) {
debug("%s: error sending\n", __func__);
return -EREMOTEIO;
}
}
return 0;
}
/**
* Set I2C bus speed
*
* @bus i2c bus to transfer data over
* @speed Speed in Hz to set
* @return 0 for success, otherwise error
*/
static int octeon_i2c_set_bus_speed(struct udevice *bus, unsigned int speed)
{
struct octeon_twsi *twsi = dev_get_priv(bus);
int m_div, n_div;
ulong clk_rate;
u64 val;
debug("%s(%p, %u)\n", __func__, bus, speed);
clk_rate = clk_get_rate(&twsi->clk);
if (IS_ERR_VALUE(clk_rate))
return -EINVAL;
twsi_calc_div(bus, clk_rate, speed, &m_div, &n_div);
if (m_div >= 16)
return -1;
val = (u32)(((m_div & 0xf) << 3) | ((n_div & 0x7) << 0)) |
FIELD_PREP(TWSI_SW_EOP_IA_MASK, TWSI_CLKCTL) |
FIELD_PREP(TWSI_SW_OP_MASK, TWSI_SW_EOP_IA) |
TWSI_SW_V;
/* Only init non-slave ports */
writeq(val, twsi->base + TWSI_SW_TWSI);
debug("%s: Wrote 0x%llx to sw_twsi\n", __func__, val);
return 0;
}
/**
* Driver probe function
*
* @dev I2C device to probe
* @return 0 for success, otherwise error
*/
static int octeon_i2c_probe(struct udevice *dev)
{
struct octeon_twsi *twsi = dev_get_priv(dev);
u32 i2c_slave_addr;
int ret;
twsi->data = (const struct octeon_i2c_data *)dev_get_driver_data(dev);
if (twsi->data->probe == PROBE_PCI) {
pci_dev_t bdf = dm_pci_get_bdf(dev);
debug("TWSI PCI device: %x\n", bdf);
dev->req_seq = PCI_FUNC(bdf);
twsi->base = dm_pci_map_bar(dev, PCI_BASE_ADDRESS_0,
PCI_REGION_MEM);
} else {
twsi->base = dev_remap_addr(dev);
}
twsi->base += twsi->data->reg_offs;
i2c_slave_addr = dev_read_u32_default(dev, "i2c-sda-hold-time-ns",
CONFIG_SYS_I2C_OCTEON_SLAVE_ADDR);
ret = clk_get_by_index(dev, 0, &twsi->clk);
if (ret < 0)
return ret;
ret = clk_enable(&twsi->clk);
if (ret)
return ret;
debug("TWSI bus %d at %p\n", dev->seq, twsi->base);
/* Start with standard speed, real speed set via DT or cmd */
return twsi_init(twsi->base, i2c_slave_addr);
}
static const struct dm_i2c_ops octeon_i2c_ops = {
.xfer = octeon_i2c_xfer,
.set_bus_speed = octeon_i2c_set_bus_speed,
};
static const struct octeon_i2c_data i2c_octeon_data = {
.probe = PROBE_DT,
.reg_offs = 0x0000,
.thp = 3,
.clk_method = CLK_METHOD_OCTEON,
};
static const struct octeon_i2c_data i2c_octeontx_data = {
.probe = PROBE_PCI,
.reg_offs = 0x8000,
.thp = 3,
.clk_method = CLK_METHOD_OCTEON,
};
static const struct octeon_i2c_data i2c_octeontx2_data = {
.probe = PROBE_PCI,
.reg_offs = 0x8000,
.thp = 24,
.clk_method = CLK_METHOD_OCTEONTX2,
};
static const struct udevice_id octeon_i2c_ids[] = {
{ .compatible = "cavium,octeon-7890-twsi",
.data = (ulong)&i2c_octeon_data },
{ .compatible = "cavium,thunder-8890-twsi",
.data = (ulong)&i2c_octeontx_data },
{ .compatible = "cavium,thunder2-99xx-twsi",
.data = (ulong)&i2c_octeontx2_data },
{ }
};
U_BOOT_DRIVER(octeon_pci_twsi) = {
.name = "i2c_octeon",
.id = UCLASS_I2C,
.of_match = octeon_i2c_ids,
.probe = octeon_i2c_probe,
.priv_auto_alloc_size = sizeof(struct octeon_twsi),
.ops = &octeon_i2c_ops,
};
static struct pci_device_id octeon_twsi_supported[] = {
{ PCI_VDEVICE(CAVIUM, PCI_DEVICE_ID_CAVIUM_TWSI),
.driver_data = (ulong)&i2c_octeontx2_data },
{ },
};
U_BOOT_PCI_DEVICE(octeon_pci_twsi, octeon_twsi_supported);

View file

@ -8,7 +8,9 @@
#include <dm.h>
#include <i2c.h>
#include <log.h>
#include <regmap.h>
#include <reset.h>
#include <syscon.h>
#include <linux/bitops.h>
#include <linux/delay.h>
@ -154,6 +156,7 @@ struct stm32_i2c_spec {
* @fall_time: Fall time (ns)
* @dnf: Digital filter coefficient (0-16)
* @analog_filter: Analog filter delay (On/Off)
* @fmp_clr_offset: Fast Mode Plus clear register offset from set register
*/
struct stm32_i2c_setup {
u32 speed_freq;
@ -162,6 +165,7 @@ struct stm32_i2c_setup {
u32 fall_time;
u8 dnf;
bool analog_filter;
u32 fmp_clr_offset;
};
/**
@ -181,11 +185,26 @@ struct stm32_i2c_timings {
u8 scll;
};
/**
* struct stm32_i2c_priv - private data of the controller
* @regs: I2C registers address
* @clk: hw i2c clock
* @setup: I2C timing setup parameters
* @speed: I2C clock frequency of the controller. Standard, Fast or Fast+
* @regmap: holds SYSCFG phandle for Fast Mode Plus bit
* @regmap_sreg: register address for setting Fast Mode Plus bits
* @regmap_creg: register address for clearing Fast Mode Plus bits
* @regmap_mask: mask for Fast Mode Plus bits
*/
struct stm32_i2c_priv {
struct stm32_i2c_regs *regs;
struct clk clk;
struct stm32_i2c_setup *setup;
u32 speed;
struct regmap *regmap;
u32 regmap_sreg;
u32 regmap_creg;
u32 regmap_mask;
};
static const struct stm32_i2c_spec i2c_specs[] = {
@ -237,6 +256,14 @@ static const struct stm32_i2c_setup stm32f7_setup = {
.analog_filter = STM32_I2C_ANALOG_FILTER_ENABLE,
};
static const struct stm32_i2c_setup stm32mp15_setup = {
.rise_time = STM32_I2C_RISE_TIME_DEFAULT,
.fall_time = STM32_I2C_FALL_TIME_DEFAULT,
.dnf = STM32_I2C_DNF_DEFAULT,
.analog_filter = STM32_I2C_ANALOG_FILTER_ENABLE,
.fmp_clr_offset = 0x40,
};
static int stm32_i2c_check_device_busy(struct stm32_i2c_priv *i2c_priv)
{
struct stm32_i2c_regs *regs = i2c_priv->regs;
@ -761,6 +788,29 @@ static int stm32_i2c_setup_timing(struct stm32_i2c_priv *i2c_priv,
return 0;
}
static int stm32_i2c_write_fm_plus_bits(struct stm32_i2c_priv *i2c_priv)
{
int ret;
bool enable = i2c_priv->speed > I2C_SPEED_FAST_RATE;
/* Optional */
if (IS_ERR_OR_NULL(i2c_priv->regmap))
return 0;
if (i2c_priv->regmap_sreg == i2c_priv->regmap_creg)
ret = regmap_update_bits(i2c_priv->regmap,
i2c_priv->regmap_sreg,
i2c_priv->regmap_mask,
enable ? i2c_priv->regmap_mask : 0);
else
ret = regmap_write(i2c_priv->regmap,
enable ? i2c_priv->regmap_sreg :
i2c_priv->regmap_creg,
i2c_priv->regmap_mask);
return ret;
}
static int stm32_i2c_hw_config(struct stm32_i2c_priv *i2c_priv)
{
struct stm32_i2c_regs *regs = i2c_priv->regs;
@ -775,6 +825,11 @@ static int stm32_i2c_hw_config(struct stm32_i2c_priv *i2c_priv)
/* Disable I2C */
clrbits_le32(&regs->cr1, STM32_I2C_CR1_PE);
/* Setup Fast mode plus if necessary */
ret = stm32_i2c_write_fm_plus_bits(i2c_priv);
if (ret)
return ret;
/* Timing settings */
timing |= STM32_I2C_TIMINGR_PRESC(t.presc);
timing |= STM32_I2C_TIMINGR_SCLDEL(t.scldel);
@ -850,6 +905,7 @@ static int stm32_ofdata_to_platdata(struct udevice *dev)
{
struct stm32_i2c_priv *i2c_priv = dev_get_priv(dev);
u32 rise_time, fall_time;
int ret;
i2c_priv->setup = (struct stm32_i2c_setup *)dev_get_driver_data(dev);
if (!i2c_priv->setup)
@ -863,6 +919,22 @@ static int stm32_ofdata_to_platdata(struct udevice *dev)
if (fall_time)
i2c_priv->setup->fall_time = fall_time;
/* Optional */
i2c_priv->regmap = syscon_regmap_lookup_by_phandle(dev,
"st,syscfg-fmp");
if (!IS_ERR(i2c_priv->regmap)) {
u32 fmp[3];
ret = dev_read_u32_array(dev, "st,syscfg-fmp", fmp, 3);
if (ret)
return ret;
i2c_priv->regmap_sreg = fmp[1];
i2c_priv->regmap_creg = fmp[1] +
i2c_priv->setup->fmp_clr_offset;
i2c_priv->regmap_mask = fmp[2];
}
return 0;
}
@ -873,6 +945,7 @@ static const struct dm_i2c_ops stm32_i2c_ops = {
static const struct udevice_id stm32_i2c_of_match[] = {
{ .compatible = "st,stm32f7-i2c", .data = (ulong)&stm32f7_setup },
{ .compatible = "st,stm32mp15-i2c", .data = (ulong)&stm32mp15_setup },
{}
};

View file

@ -47,6 +47,12 @@ config PWM_SANDBOX
useful. The PWM can be enabled but is not connected to any outputs
so this is not very useful.
config PWM_SIFIVE
bool "Enable support for SiFive PWM"
depends on DM_PWM
help
This PWM is found SiFive's FU540 and other SoCs.
config PWM_TEGRA
bool "Enable support for the Tegra PWM"
depends on DM_PWM

View file

@ -15,5 +15,6 @@ obj-$(CONFIG_PWM_IMX) += pwm-imx.o pwm-imx-util.o
obj-$(CONFIG_PWM_MTK) += pwm-mtk.o
obj-$(CONFIG_PWM_ROCKCHIP) += rk_pwm.o
obj-$(CONFIG_PWM_SANDBOX) += sandbox_pwm.o
obj-$(CONFIG_PWM_SIFIVE) += pwm-sifive.o
obj-$(CONFIG_PWM_TEGRA) += tegra_pwm.o
obj-$(CONFIG_PWM_SUNXI) += sunxi_pwm.o

172
drivers/pwm/pwm-sifive.c Normal file
View file

@ -0,0 +1,172 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (C) 2020 SiFive, Inc
* For SiFive's PWM IP block documentation please refer Chapter 14 of
* Reference Manual : https://static.dev.sifive.com/FU540-C000-v1.0.pdf
*
* Limitations:
* - When changing both duty cycle and period, we cannot prevent in
* software that the output might produce a period with mixed
* settings (new period length and old duty cycle).
* - The hardware cannot generate a 100% duty cycle.
* - The hardware generates only inverted output.
*/
#include <common.h>
#include <clk.h>
#include <div64.h>
#include <dm.h>
#include <pwm.h>
#include <regmap.h>
#include <linux/io.h>
#include <linux/log2.h>
#include <linux/bitfield.h>
/* PWMCFG fields */
#define PWM_SIFIVE_PWMCFG_SCALE GENMASK(3, 0)
#define PWM_SIFIVE_PWMCFG_STICKY BIT(8)
#define PWM_SIFIVE_PWMCFG_ZERO_CMP BIT(9)
#define PWM_SIFIVE_PWMCFG_DEGLITCH BIT(10)
#define PWM_SIFIVE_PWMCFG_EN_ALWAYS BIT(12)
#define PWM_SIFIVE_PWMCFG_EN_ONCE BIT(13)
#define PWM_SIFIVE_PWMCFG_CENTER BIT(16)
#define PWM_SIFIVE_PWMCFG_GANG BIT(24)
#define PWM_SIFIVE_PWMCFG_IP BIT(28)
/* PWM_SIFIVE_SIZE_PWMCMP is used to calculate offset for pwmcmpX registers */
#define PWM_SIFIVE_SIZE_PWMCMP 4
#define PWM_SIFIVE_CMPWIDTH 16
DECLARE_GLOBAL_DATA_PTR;
struct pwm_sifive_regs {
unsigned long cfg;
unsigned long cnt;
unsigned long pwms;
unsigned long cmp0;
};
struct pwm_sifive_data {
struct pwm_sifive_regs regs;
};
struct pwm_sifive_priv {
void __iomem *base;
ulong freq;
const struct pwm_sifive_data *data;
};
static int pwm_sifive_set_config(struct udevice *dev, uint channel,
uint period_ns, uint duty_ns)
{
struct pwm_sifive_priv *priv = dev_get_priv(dev);
const struct pwm_sifive_regs *regs = &priv->data->regs;
unsigned long scale_pow;
unsigned long long num;
u32 scale, val = 0, frac;
debug("%s: period_ns=%u, duty_ns=%u\n", __func__, period_ns, duty_ns);
/*
* The PWM unit is used with pwmzerocmp=0, so the only way to modify the
* period length is using pwmscale which provides the number of bits the
* counter is shifted before being feed to the comparators. A period
* lasts (1 << (PWM_SIFIVE_CMPWIDTH + pwmscale)) clock ticks.
* (1 << (PWM_SIFIVE_CMPWIDTH + scale)) * 10^9/rate = period
*/
scale_pow = lldiv((uint64_t)priv->freq * period_ns, 1000000000);
scale = clamp(ilog2(scale_pow) - PWM_SIFIVE_CMPWIDTH, 0, 0xf);
val |= FIELD_PREP(PWM_SIFIVE_PWMCFG_SCALE, scale);
/*
* The problem of output producing mixed setting as mentioned at top,
* occurs here. To minimize the window for this problem, we are
* calculating the register values first and then writing them
* consecutively
*/
num = (u64)duty_ns * (1U << PWM_SIFIVE_CMPWIDTH);
frac = DIV_ROUND_CLOSEST_ULL(num, period_ns);
frac = min(frac, (1U << PWM_SIFIVE_CMPWIDTH) - 1);
writel(val, priv->base + regs->cfg);
writel(frac, priv->base + regs->cmp0 + channel *
PWM_SIFIVE_SIZE_PWMCMP);
return 0;
}
static int pwm_sifive_set_enable(struct udevice *dev, uint channel, bool enable)
{
struct pwm_sifive_priv *priv = dev_get_priv(dev);
const struct pwm_sifive_regs *regs = &priv->data->regs;
u32 val;
debug("%s: Enable '%s'\n", __func__, dev->name);
if (enable) {
val = readl(priv->base + regs->cfg);
val |= PWM_SIFIVE_PWMCFG_EN_ALWAYS;
writel(val, priv->base + regs->cfg);
} else {
writel(0, priv->base + regs->cmp0 + channel *
PWM_SIFIVE_SIZE_PWMCMP);
}
return 0;
}
static int pwm_sifive_ofdata_to_platdata(struct udevice *dev)
{
struct pwm_sifive_priv *priv = dev_get_priv(dev);
priv->base = dev_read_addr_ptr(dev);
return 0;
}
static int pwm_sifive_probe(struct udevice *dev)
{
struct pwm_sifive_priv *priv = dev_get_priv(dev);
struct clk clk;
int ret = 0;
ret = clk_get_by_index(dev, 0, &clk);
if (ret < 0) {
debug("%s get clock fail!\n", __func__);
return -EINVAL;
}
priv->freq = clk_get_rate(&clk);
priv->data = (struct pwm_sifive_data *)dev_get_driver_data(dev);
return 0;
}
static const struct pwm_ops pwm_sifive_ops = {
.set_config = pwm_sifive_set_config,
.set_enable = pwm_sifive_set_enable,
};
static const struct pwm_sifive_data pwm_data = {
.regs = {
.cfg = 0x00,
.cnt = 0x08,
.pwms = 0x10,
.cmp0 = 0x20,
},
};
static const struct udevice_id pwm_sifive_ids[] = {
{ .compatible = "sifive,pwm0", .data = (ulong)&pwm_data},
{ }
};
U_BOOT_DRIVER(pwm_sifive) = {
.name = "pwm_sifive",
.id = UCLASS_PWM,
.of_match = pwm_sifive_ids,
.ops = &pwm_sifive_ops,
.ofdata_to_platdata = pwm_sifive_ofdata_to_platdata,
.probe = pwm_sifive_probe,
.priv_auto_alloc_size = sizeof(struct pwm_sifive_priv),
};

View file

@ -197,7 +197,8 @@ static int sandbox_i2c_rtc_xfer(struct udevice *emul, struct i2c_msg *msg,
/* Write the register */
memcpy(plat->reg + offset, ptr, len);
if (offset == REG_RESET)
/* If the reset register was written to, do reset. */
if (offset <= REG_RESET && REG_RESET < offset + len)
reset_time(emul);
}
}

View file

@ -23,8 +23,7 @@
#define PCF2127_REG_MO 0x08
#define PCF2127_REG_YR 0x09
static int pcf2127_read_reg(struct udevice *dev, uint offset,
u8 *buffer, int len)
static int pcf2127_rtc_read(struct udevice *dev, uint offset, u8 *buffer, uint len)
{
struct dm_i2c_chip *chip = dev_get_parent_platdata(dev);
struct i2c_msg msg;
@ -44,6 +43,12 @@ static int pcf2127_read_reg(struct udevice *dev, uint offset,
return dm_i2c_xfer(dev, &msg, 1);
}
static int pcf2127_rtc_write(struct udevice *dev, uint offset,
const u8 *buffer, uint len)
{
return dm_i2c_write(dev, offset, buffer, len);
}
static int pcf2127_rtc_set(struct udevice *dev, const struct rtc_time *tm)
{
uchar buf[7] = {0};
@ -73,7 +78,7 @@ static int pcf2127_rtc_get(struct udevice *dev, struct rtc_time *tm)
int ret = 0;
uchar buf[10] = { PCF2127_REG_CTRL1 };
ret = pcf2127_read_reg(dev, PCF2127_REG_CTRL1, buf, sizeof(buf));
ret = pcf2127_rtc_read(dev, PCF2127_REG_CTRL1, buf, sizeof(buf));
if (ret < 0)
return ret;
@ -110,6 +115,8 @@ static const struct rtc_ops pcf2127_rtc_ops = {
.get = pcf2127_rtc_get,
.set = pcf2127_rtc_set,
.reset = pcf2127_rtc_reset,
.read = pcf2127_rtc_read,
.write = pcf2127_rtc_write,
};
static const struct udevice_id pcf2127_rtc_ids[] = {

View file

@ -40,14 +40,60 @@ int dm_rtc_reset(struct udevice *dev)
return ops->reset(dev);
}
int dm_rtc_read(struct udevice *dev, unsigned int reg, u8 *buf, unsigned int len)
{
struct rtc_ops *ops = rtc_get_ops(dev);
assert(ops);
if (ops->read)
return ops->read(dev, reg, buf, len);
if (!ops->read8)
return -ENOSYS;
while (len--) {
int ret = ops->read8(dev, reg++);
if (ret < 0)
return ret;
*buf++ = ret;
}
return 0;
}
int dm_rtc_write(struct udevice *dev, unsigned int reg,
const u8 *buf, unsigned int len)
{
struct rtc_ops *ops = rtc_get_ops(dev);
assert(ops);
if (ops->write)
return ops->write(dev, reg, buf, len);
if (!ops->write8)
return -ENOSYS;
while (len--) {
int ret = ops->write8(dev, reg++, *buf++);
if (ret < 0)
return ret;
}
return 0;
}
int rtc_read8(struct udevice *dev, unsigned int reg)
{
struct rtc_ops *ops = rtc_get_ops(dev);
assert(ops);
if (!ops->read8)
return -ENOSYS;
return ops->read8(dev, reg);
if (ops->read8)
return ops->read8(dev, reg);
if (ops->read) {
u8 buf[1];
int ret = ops->read(dev, reg, buf, 1);
if (ret < 0)
return ret;
return buf[0];
}
return -ENOSYS;
}
int rtc_write8(struct udevice *dev, unsigned int reg, int val)
@ -55,9 +101,14 @@ int rtc_write8(struct udevice *dev, unsigned int reg, int val)
struct rtc_ops *ops = rtc_get_ops(dev);
assert(ops);
if (!ops->write8)
return -ENOSYS;
return ops->write8(dev, reg, val);
if (ops->write8)
return ops->write8(dev, reg, val);
if (ops->write) {
u8 buf[1] = { val };
return ops->write(dev, reg, buf, 1);
}
return -ENOSYS;
}
int rtc_read16(struct udevice *dev, unsigned int reg, u16 *valuep)

View file

@ -14,55 +14,38 @@
static int sandbox_rtc_get(struct udevice *dev, struct rtc_time *time)
{
time->tm_sec = dm_i2c_reg_read(dev, REG_SEC);
if (time->tm_sec < 0)
return time->tm_sec;
time->tm_min = dm_i2c_reg_read(dev, REG_MIN);
if (time->tm_min < 0)
return time->tm_min;
time->tm_hour = dm_i2c_reg_read(dev, REG_HOUR);
if (time->tm_hour < 0)
return time->tm_hour;
time->tm_mday = dm_i2c_reg_read(dev, REG_MDAY);
if (time->tm_mday < 0)
return time->tm_mday;
time->tm_mon = dm_i2c_reg_read(dev, REG_MON);
if (time->tm_mon < 0)
return time->tm_mon;
time->tm_year = dm_i2c_reg_read(dev, REG_YEAR);
if (time->tm_year < 0)
return time->tm_year;
time->tm_year += 1900;
time->tm_wday = dm_i2c_reg_read(dev, REG_WDAY);
if (time->tm_wday < 0)
return time->tm_wday;
u8 buf[7];
int ret;
ret = dm_i2c_read(dev, REG_SEC, buf, sizeof(buf));
if (ret < 0)
return ret;
time->tm_sec = buf[REG_SEC - REG_SEC];
time->tm_min = buf[REG_MIN - REG_SEC];
time->tm_hour = buf[REG_HOUR - REG_SEC];
time->tm_mday = buf[REG_MDAY - REG_SEC];
time->tm_mon = buf[REG_MON - REG_SEC];
time->tm_year = buf[REG_YEAR - REG_SEC] + 1900;
time->tm_wday = buf[REG_WDAY - REG_SEC];
return 0;
}
static int sandbox_rtc_set(struct udevice *dev, const struct rtc_time *time)
{
u8 buf[7];
int ret;
ret = dm_i2c_reg_write(dev, REG_SEC, time->tm_sec);
if (ret < 0)
return ret;
ret = dm_i2c_reg_write(dev, REG_MIN, time->tm_min);
if (ret < 0)
return ret;
ret = dm_i2c_reg_write(dev, REG_HOUR, time->tm_hour);
if (ret < 0)
return ret;
ret = dm_i2c_reg_write(dev, REG_MDAY, time->tm_mday);
if (ret < 0)
return ret;
ret = dm_i2c_reg_write(dev, REG_MON, time->tm_mon);
if (ret < 0)
return ret;
ret = dm_i2c_reg_write(dev, REG_YEAR, time->tm_year - 1900);
if (ret < 0)
return ret;
ret = dm_i2c_reg_write(dev, REG_WDAY, time->tm_wday);
buf[REG_SEC - REG_SEC] = time->tm_sec;
buf[REG_MIN - REG_SEC] = time->tm_min;
buf[REG_HOUR - REG_SEC] = time->tm_hour;
buf[REG_MDAY - REG_SEC] = time->tm_mday;
buf[REG_MON - REG_SEC] = time->tm_mon;
buf[REG_YEAR - REG_SEC] = time->tm_year - 1900;
buf[REG_WDAY - REG_SEC] = time->tm_wday;
ret = dm_i2c_write(dev, REG_SEC, buf, sizeof(buf));
if (ret < 0)
return ret;

View file

@ -55,6 +55,30 @@ struct rtc_ops {
*/
int (*reset)(struct udevice *dev);
/**
* read() - Read multiple 8-bit registers
*
* @dev: Device to read from
* @reg: First register to read
* @buf: Output buffer
* @len: Number of registers to read
* @return 0 if OK, -ve on error
*/
int (*read)(struct udevice *dev, unsigned int reg,
u8 *buf, unsigned int len);
/**
* write() - Write multiple 8-bit registers
*
* @dev: Device to write to
* @reg: First register to write
* @buf: Input buffer
* @len: Number of registers to write
* @return 0 if OK, -ve on error
*/
int (*write)(struct udevice *dev, unsigned int reg,
const u8 *buf, unsigned int len);
/**
* read8() - Read an 8-bit register
*
@ -109,6 +133,29 @@ int dm_rtc_set(struct udevice *dev, struct rtc_time *time);
*/
int dm_rtc_reset(struct udevice *dev);
/**
* dm_rtc_read() - Read multiple 8-bit registers
*
* @dev: Device to read from
* @reg: First register to read
* @buf: Output buffer
* @len: Number of registers to read
* @return 0 if OK, -ve on error
*/
int dm_rtc_read(struct udevice *dev, unsigned int reg, u8 *buf, unsigned int len);
/**
* dm_rtc_write() - Write multiple 8-bit registers
*
* @dev: Device to write to
* @reg: First register to write
* @buf: Input buffer
* @len: Number of registers to write
* @return 0 if OK, -ve on error
*/
int dm_rtc_write(struct udevice *dev, unsigned int reg,
const u8 *buf, unsigned int len);
/**
* rtc_read8() - Read an 8-bit register
*

View file

@ -5,11 +5,13 @@
*/
#include <common.h>
#include <console.h>
#include <dm.h>
#include <i2c.h>
#include <log.h>
#include <rtc.h>
#include <asm/io.h>
#include <asm/rtc.h>
#include <asm/test.h>
#include <dm/test.h>
#include <test/ut.h>
@ -70,7 +72,20 @@ static int dm_test_rtc_set_get(struct unit_test_state *uts)
old_base_time = sandbox_i2c_rtc_get_set_base_time(emul, -1);
memset(&time, '\0', sizeof(time));
time.tm_mday = 25;
time.tm_mday = 3;
time.tm_mon = 6;
time.tm_year = 2004;
time.tm_sec = 0;
time.tm_min = 18;
time.tm_hour = 18;
ut_assertok(dm_rtc_set(dev, &time));
memset(&cmp, '\0', sizeof(cmp));
ut_assertok(dm_rtc_get(dev, &cmp));
ut_assertok(cmp_times(&time, &cmp, true));
memset(&time, '\0', sizeof(time));
time.tm_mday = 31;
time.tm_mon = 8;
time.tm_year = 2004;
time.tm_sec = 0;
@ -117,6 +132,107 @@ static int dm_test_rtc_set_get(struct unit_test_state *uts)
}
DM_TEST(dm_test_rtc_set_get, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT);
static int dm_test_rtc_read_write(struct unit_test_state *uts)
{
struct rtc_time time;
struct udevice *dev, *emul;
long old_offset;
u8 buf[4], reg;
ut_assertok(uclass_get_device(UCLASS_RTC, 0, &dev));
memcpy(buf, "car", 4);
ut_assertok(dm_rtc_write(dev, REG_AUX0, buf, 4));
memset(buf, '\0', sizeof(buf));
ut_assertok(dm_rtc_read(dev, REG_AUX0, buf, 4));
ut_asserteq(memcmp(buf, "car", 4), 0);
reg = 'b';
ut_assertok(dm_rtc_write(dev, REG_AUX0, &reg, 1));
memset(buf, '\0', sizeof(buf));
ut_assertok(dm_rtc_read(dev, REG_AUX0, buf, 4));
ut_asserteq(memcmp(buf, "bar", 4), 0);
reg = 't';
ut_assertok(dm_rtc_write(dev, REG_AUX2, &reg, 1));
memset(buf, '\0', sizeof(buf));
ut_assertok(dm_rtc_read(dev, REG_AUX1, buf, 3));
ut_asserteq(memcmp(buf, "at", 3), 0);
ut_assertok(i2c_emul_find(dev, &emul));
ut_assert(emul != NULL);
old_offset = sandbox_i2c_rtc_set_offset(emul, false, 0);
ut_assertok(dm_rtc_get(dev, &time));
ut_assertok(dm_rtc_read(dev, REG_SEC, &reg, 1));
ut_asserteq(time.tm_sec, reg);
ut_assertok(dm_rtc_read(dev, REG_MDAY, &reg, 1));
ut_asserteq(time.tm_mday, reg);
sandbox_i2c_rtc_set_offset(emul, true, old_offset);
return 0;
}
DM_TEST(dm_test_rtc_read_write, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT);
/* Test 'rtc list' command */
static int dm_test_rtc_cmd_list(struct unit_test_state *uts)
{
console_record_reset();
run_command("rtc list", 0);
ut_assert_nextline("RTC #0 - rtc@43");
ut_assert_nextline("RTC #1 - rtc@61");
ut_assert_console_end();
return 0;
}
DM_TEST(dm_test_rtc_cmd_list, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT);
/* Test 'rtc read' and 'rtc write' commands */
static int dm_test_rtc_cmd_rw(struct unit_test_state *uts)
{
console_record_reset();
run_command("rtc dev 0", 0);
ut_assert_nextline("RTC #0 - rtc@43");
ut_assert_console_end();
run_command("rtc write 0x30 aabb", 0);
ut_assert_console_end();
run_command("rtc read 0x30 2", 0);
ut_assert_nextline("00000030: aa bb ..");
ut_assert_console_end();
run_command("rtc dev 1", 0);
ut_assert_nextline("RTC #1 - rtc@61");
ut_assert_console_end();
run_command("rtc write 0x30 ccdd", 0);
ut_assert_console_end();
run_command("rtc read 0x30 2", 0);
ut_assert_nextline("00000030: cc dd ..");
ut_assert_console_end();
/*
* Switch back to device #0, check that its aux registers
* still have the same values.
*/
run_command("rtc dev 0", 0);
ut_assert_nextline("RTC #0 - rtc@43");
ut_assert_console_end();
run_command("rtc read 0x30 2", 0);
ut_assert_nextline("00000030: aa bb ..");
ut_assert_console_end();
return 0;
}
DM_TEST(dm_test_rtc_cmd_rw, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT);
/* Reset the time */
static int dm_test_rtc_reset(struct unit_test_state *uts)
{