mirror of
https://github.com/AsahiLinux/u-boot
synced 2024-11-24 21:54:01 +00:00
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
This commit is contained in:
commit
d9107930af
28 changed files with 2083 additions and 63 deletions
|
@ -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
|
||||
|
|
167
cmd/rtc.c
Normal file
167
cmd/rtc.c
Normal 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"
|
||||
);
|
|
@ -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
|
||||
|
|
18
doc/device-tree-bindings/i2c/i2c-cortina.txt
Normal file
18
doc/device-tree-bindings/i2c/i2c-cortina.txt
Normal 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>;
|
||||
};
|
24
doc/device-tree-bindings/i2c/octeon-i2c.txt
Normal file
24
doc/device-tree-bindings/i2c/octeon-i2c.txt
Normal 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>;
|
||||
};
|
31
doc/device-tree-bindings/pwm/pwm-sifive.txt
Normal file
31
doc/device-tree-bindings/pwm/pwm-sifive.txt
Normal 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>;
|
||||
};
|
|
@ -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
|
||||
|
|
347
drivers/i2c/i2c-cortina.c
Normal file
347
drivers/i2c/i2c-cortina.c
Normal 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(®s->i2c_cfg);
|
||||
i2c_cfg.bf.core_en = 0;
|
||||
writel(i2c_cfg.wrd, ®s->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, ®s->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(®s->i2c_cfg);
|
||||
i2c_cfg.bf.core_en = 0;
|
||||
i2c_cfg.bf.biw_soft_reset = 1;
|
||||
writel(i2c_cfg.wrd, ®s->i2c_cfg);
|
||||
mdelay(10);
|
||||
i2c_cfg.bf.biw_soft_reset = 0;
|
||||
writel(i2c_cfg.wrd, ®s->i2c_cfg);
|
||||
|
||||
set_speed(regs, IC_SPEED_MODE_STANDARD);
|
||||
|
||||
i2c_cfg.wrd = readl(®s->i2c_cfg);
|
||||
i2c_cfg.bf.core_en = 1;
|
||||
writel(i2c_cfg.wrd, ®s->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(®s->i2c_ctrl);
|
||||
|
||||
while (i2c_ctrl.bf.biwdone == 0) {
|
||||
i2c_ctrl.wrd = readl(®s->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, ®s->i2c_ctrl);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void i2c_setaddress(struct i2c_regs *regs, unsigned int i2c_addr,
|
||||
int write_read)
|
||||
{
|
||||
writel(i2c_addr | write_read, ®s->i2c_txr);
|
||||
|
||||
writel(BIW_CTRL_START | BIW_CTRL_WRITE,
|
||||
®s->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(®s->i2c_ack);
|
||||
|
||||
while (i2c_ack.bf.biw_busy) {
|
||||
i2c_ack.wrd = readl(®s->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, ®s->i2c_txr);
|
||||
if (write_read == I2C_CMD_RD)
|
||||
writel(BIW_CTRL_WRITE | BIW_CTRL_STOP,
|
||||
®s->i2c_ctrl);
|
||||
else
|
||||
writel(BIW_CTRL_WRITE, ®s->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, ®s->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,
|
||||
®s->i2c_ctrl);
|
||||
else
|
||||
writel(BIW_CTRL_READ, ®s->i2c_ctrl);
|
||||
|
||||
rc = i2c_wait_complete(regs);
|
||||
udelay(1);
|
||||
|
||||
if (rc == 0) {
|
||||
*buffer++ =
|
||||
(uchar) readl(®s->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, ®s->i2c_txr);
|
||||
if (len == 1)
|
||||
writel(BIW_CTRL_WRITE | BIW_CTRL_STOP,
|
||||
®s->i2c_ctrl);
|
||||
else
|
||||
writel(BIW_CTRL_WRITE, ®s->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
87
drivers/i2c/i2c-cortina.h
Normal 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_ */
|
|
@ -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, ®s->mcr);
|
||||
writel(0x0, ®s->mcr);
|
||||
|
|
847
drivers/i2c/octeon_i2c.c
Normal file
847
drivers/i2c/octeon_i2c.c
Normal 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);
|
|
@ -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(®s->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
|
||||
|
|
172
drivers/pwm/pwm-sifive.c
Normal file
172
drivers/pwm/pwm-sifive.c
Normal 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),
|
||||
};
|
|
@ -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,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)
|
||||
|
|
|
@ -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
|
||||
*
|
||||
|
|
118
test/dm/rtc.c
118
test/dm/rtc.c
|
@ -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, ®, 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, ®, 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, ®, 1));
|
||||
ut_asserteq(time.tm_sec, reg);
|
||||
ut_assertok(dm_rtc_read(dev, REG_MDAY, ®, 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)
|
||||
{
|
||||
|
|
Loading…
Reference in a new issue