提交 d9107930 编写于 作者: T Tom Rini

Merge tag 'for-v2020.10' of https://gitlab.denx.de/u-boot/custodians/u-boot-i2c

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
......@@ -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>
......
......@@ -21,6 +21,11 @@ enum {
REG_RESET = 0x20,
REG_AUX0 = 0x30,
REG_AUX1,
REG_AUX2,
REG_AUX3,
REG_COUNT = 0x80,
};
......
......@@ -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
......
......@@ -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
......
// 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"
);
......@@ -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
......
......@@ -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
......
......@@ -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
......
......@@ -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
......
* 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>;
};
* 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>;
};
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>;
};
......@@ -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
......
......@@ -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
......
// 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,
};
/* 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_ */
......@@ -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);
......
此差异已折叠。
......@@ -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 },
{}
};
......
......@@ -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
......
......@@ -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
// 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),
};
......@@ -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);
}
}
......
......@@ -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[] = {
......
......@@ -40,24 +40,75 @@ int dm_rtc_reset(struct udevice *dev)
return ops->reset(dev);
}
int rtc_read8(struct udevice *dev, unsigned int reg)
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;
return ops->read8(dev, reg);
while (len--) {
int ret = ops->read8(dev, reg++);
if (ret < 0)
return ret;
*buf++ = ret;
}
return 0;
}
int rtc_write8(struct udevice *dev, unsigned int reg, int val)
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;
return ops->write8(dev, reg, val);
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 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)
{
struct rtc_ops *ops = rtc_get_ops(dev);
assert(ops);
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)
......
......@@ -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;
......
......@@ -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
*
......
......@@ -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)
{
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册