mirror of
https://github.com/AsahiLinux/u-boot
synced 2024-11-30 08:31:03 +00:00
Merge tag 'for-v2021.10-rc3' of https://source.denx.de/u-boot/custodians/u-boot-i2c
i2c changes for for-v2021.10-rc3 new driver: - Introduce mcp230xx support from Sebastian Reichel new feature: - i2c-gpio: add support for "sda-gpios" + "scl-gpios" i2c-gpio bindings. from Samuel Holland - bootcount: add a new driver with syscon as backend from Nandor Han
This commit is contained in:
commit
926fe46a6d
14 changed files with 569 additions and 3 deletions
|
@ -731,6 +731,20 @@
|
|||
i2c-eeprom = <&bootcount_i2c>;
|
||||
};
|
||||
|
||||
bootcount_4@0 {
|
||||
compatible = "u-boot,bootcount-syscon";
|
||||
syscon = <&syscon0>;
|
||||
reg = <0x0 0x04>, <0x0 0x04>;
|
||||
reg-names = "syscon_reg", "offset";
|
||||
};
|
||||
|
||||
bootcount_2@0 {
|
||||
compatible = "u-boot,bootcount-syscon";
|
||||
syscon = <&syscon0>;
|
||||
reg = <0x0 0x04>, <0x0 0x02> ;
|
||||
reg-names = "syscon_reg", "offset";
|
||||
};
|
||||
|
||||
adc: adc@0 {
|
||||
compatible = "sandbox,adc";
|
||||
#io-channel-cells = <1>;
|
||||
|
|
|
@ -131,6 +131,7 @@ CONFIG_AXI=y
|
|||
CONFIG_AXI_SANDBOX=y
|
||||
CONFIG_BOOTCOUNT_LIMIT=y
|
||||
CONFIG_DM_BOOTCOUNT=y
|
||||
CONFIG_DM_BOOTCOUNT_SYSCON=y
|
||||
CONFIG_DM_BOOTCOUNT_RTC=y
|
||||
CONFIG_DM_BOOTCOUNT_I2C_EEPROM=y
|
||||
CONFIG_BUTTON=y
|
||||
|
|
24
doc/device-tree-bindings/bootcount-syscon.txt
Normal file
24
doc/device-tree-bindings/bootcount-syscon.txt
Normal file
|
@ -0,0 +1,24 @@
|
|||
Bootcount Configuration
|
||||
This is the implementation of the feature as described in
|
||||
https://www.denx.de/wiki/DULG/UBootBootCountLimit.
|
||||
|
||||
Required Properties:
|
||||
- compatible: must be "u-boot,bootcount-syscon".
|
||||
- syscon: reference to the syscon device used.
|
||||
- reg: contains address and size of the register and the location and size of the bootcount value.
|
||||
The driver supports a 4 bytes register length and 2 and 4 bytes bootcount value length.
|
||||
- reg-names: must be "syscon_reg", "offset";
|
||||
|
||||
Example:
|
||||
...
|
||||
syscon0: syscon@0 {
|
||||
compatible = "sandbox,syscon0";
|
||||
reg = <0x10 16>;
|
||||
};
|
||||
...
|
||||
bootcount@0 {
|
||||
compatible = "u-boot,bootcount-syscon";
|
||||
syscon = <&syscon0>;
|
||||
reg = <0x0 0x04>, <0x0 0x04>;
|
||||
reg-names = "syscon_reg", "offset";
|
||||
};
|
|
@ -144,6 +144,18 @@ config BOOTCOUNT_MEM
|
|||
is not cleared on softreset.
|
||||
compatible = "u-boot,bootcount";
|
||||
|
||||
config DM_BOOTCOUNT_SYSCON
|
||||
bool "Support SYSCON devices as a backing store for bootcount"
|
||||
select REGMAP
|
||||
select SYSCON
|
||||
help
|
||||
Enable reading/writing the bootcount value in a DM SYSCON device.
|
||||
The driver supports a fixed 32 bits size register using the native
|
||||
endianness. However, this can be controlled from the SYSCON DT node
|
||||
configuration.
|
||||
|
||||
Accessing the backend is done using the regmap interface.
|
||||
|
||||
endmenu
|
||||
|
||||
endif
|
||||
|
|
|
@ -14,3 +14,4 @@ obj-$(CONFIG_DM_BOOTCOUNT) += bootcount-uclass.o
|
|||
obj-$(CONFIG_DM_BOOTCOUNT_RTC) += rtc.o
|
||||
obj-$(CONFIG_DM_BOOTCOUNT_I2C_EEPROM) += i2c-eeprom.o
|
||||
obj-$(CONFIG_DM_BOOTCOUNT_SPI_FLASH) += spi-flash.o
|
||||
obj-$(CONFIG_DM_BOOTCOUNT_SYSCON) += bootcount_syscon.o
|
||||
|
|
159
drivers/bootcount/bootcount_syscon.c
Normal file
159
drivers/bootcount/bootcount_syscon.c
Normal file
|
@ -0,0 +1,159 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright (c) Vaisala Oyj. All rights reserved.
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <bootcount.h>
|
||||
#include <dm.h>
|
||||
#include <dm/device_compat.h>
|
||||
#include <linux/ioport.h>
|
||||
#include <regmap.h>
|
||||
#include <syscon.h>
|
||||
|
||||
#define BYTES_TO_BITS(bytes) ((bytes) << 3)
|
||||
#define GEN_REG_MASK(val_size, val_addr) \
|
||||
(GENMASK(BYTES_TO_BITS(val_size) - 1, 0) \
|
||||
<< (!!((val_addr) == 0x02) * BYTES_TO_BITS(2)))
|
||||
#define GET_DEFAULT_VALUE(val_size) \
|
||||
(CONFIG_SYS_BOOTCOUNT_MAGIC >> \
|
||||
(BYTES_TO_BITS((sizeof(u32) - (val_size)))))
|
||||
|
||||
/**
|
||||
* struct bootcount_syscon_priv - driver's private data
|
||||
*
|
||||
* @regmap: syscon regmap
|
||||
* @reg_addr: register address used to store the bootcount value
|
||||
* @size: size of the bootcount value (2 or 4 bytes)
|
||||
* @magic: magic used to validate/save the bootcount value
|
||||
* @magic_mask: magic value bitmask
|
||||
* @reg_mask: mask used to identify the location of the bootcount value
|
||||
* in the register when 2 bytes length is used
|
||||
* @shift: value used to extract the botcount value from the register
|
||||
*/
|
||||
struct bootcount_syscon_priv {
|
||||
struct regmap *regmap;
|
||||
fdt_addr_t reg_addr;
|
||||
fdt_size_t size;
|
||||
u32 magic;
|
||||
u32 magic_mask;
|
||||
u32 reg_mask;
|
||||
int shift;
|
||||
};
|
||||
|
||||
static int bootcount_syscon_set(struct udevice *dev, const u32 val)
|
||||
{
|
||||
struct bootcount_syscon_priv *priv = dev_get_priv(dev);
|
||||
u32 regval;
|
||||
|
||||
if ((val & priv->magic_mask) != 0)
|
||||
return -EINVAL;
|
||||
|
||||
regval = (priv->magic & priv->magic_mask) | (val & ~priv->magic_mask);
|
||||
|
||||
if (priv->size == 2) {
|
||||
regval &= 0xffff;
|
||||
regval |= (regval & 0xffff) << BYTES_TO_BITS(priv->size);
|
||||
}
|
||||
|
||||
debug("%s: Prepare to write reg value: 0x%08x with register mask: 0x%08x\n",
|
||||
__func__, regval, priv->reg_mask);
|
||||
|
||||
return regmap_update_bits(priv->regmap, priv->reg_addr, priv->reg_mask,
|
||||
regval);
|
||||
}
|
||||
|
||||
static int bootcount_syscon_get(struct udevice *dev, u32 *val)
|
||||
{
|
||||
struct bootcount_syscon_priv *priv = dev_get_priv(dev);
|
||||
u32 regval;
|
||||
int ret;
|
||||
|
||||
ret = regmap_read(priv->regmap, priv->reg_addr, ®val);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
regval &= priv->reg_mask;
|
||||
regval >>= priv->shift;
|
||||
|
||||
if ((regval & priv->magic_mask) == (priv->magic & priv->magic_mask)) {
|
||||
*val = regval & ~priv->magic_mask;
|
||||
} else {
|
||||
dev_err(dev, "%s: Invalid bootcount magic\n", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
debug("%s: Read bootcount value: 0x%08x from regval: 0x%08x\n",
|
||||
__func__, *val, regval);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int bootcount_syscon_of_to_plat(struct udevice *dev)
|
||||
{
|
||||
struct bootcount_syscon_priv *priv = dev_get_priv(dev);
|
||||
fdt_addr_t bootcount_offset;
|
||||
fdt_size_t reg_size;
|
||||
|
||||
priv->regmap = syscon_regmap_lookup_by_phandle(dev, "syscon");
|
||||
if (IS_ERR(priv->regmap)) {
|
||||
dev_err(dev, "%s: Unable to find regmap (%ld)\n", __func__,
|
||||
PTR_ERR(priv->regmap));
|
||||
return PTR_ERR(priv->regmap);
|
||||
}
|
||||
|
||||
priv->reg_addr = dev_read_addr_size_name(dev, "syscon_reg", ®_size);
|
||||
if (priv->reg_addr == FDT_ADDR_T_NONE) {
|
||||
dev_err(dev, "%s: syscon_reg address not found\n", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
if (reg_size != 4) {
|
||||
dev_err(dev, "%s: Unsupported register size: %d\n", __func__,
|
||||
reg_size);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
bootcount_offset = dev_read_addr_size_name(dev, "offset", &priv->size);
|
||||
if (bootcount_offset == FDT_ADDR_T_NONE) {
|
||||
dev_err(dev, "%s: offset configuration not found\n", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
if (bootcount_offset + priv->size > reg_size) {
|
||||
dev_err(dev,
|
||||
"%s: Bootcount value doesn't fit in the reserved space\n",
|
||||
__func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
if (priv->size != 2 && priv->size != 4) {
|
||||
dev_err(dev,
|
||||
"%s: Driver supports only 2 and 4 bytes bootcount size\n",
|
||||
__func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
priv->magic = GET_DEFAULT_VALUE(priv->size);
|
||||
priv->magic_mask = GENMASK(BYTES_TO_BITS(priv->size) - 1,
|
||||
BYTES_TO_BITS(priv->size >> 1));
|
||||
priv->shift = !!(bootcount_offset == 0x02) * BYTES_TO_BITS(priv->size);
|
||||
priv->reg_mask = GEN_REG_MASK(priv->size, bootcount_offset);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct bootcount_ops bootcount_syscon_ops = {
|
||||
.get = bootcount_syscon_get,
|
||||
.set = bootcount_syscon_set,
|
||||
};
|
||||
|
||||
static const struct udevice_id bootcount_syscon_ids[] = {
|
||||
{ .compatible = "u-boot,bootcount-syscon" },
|
||||
{}
|
||||
};
|
||||
|
||||
U_BOOT_DRIVER(bootcount_syscon) = {
|
||||
.name = "bootcount-syscon",
|
||||
.id = UCLASS_BOOTCOUNT,
|
||||
.of_to_plat = bootcount_syscon_of_to_plat,
|
||||
.priv_auto = sizeof(struct bootcount_syscon_priv),
|
||||
.of_match = bootcount_syscon_ids,
|
||||
.ops = &bootcount_syscon_ops,
|
||||
};
|
|
@ -179,6 +179,16 @@ config LPC32XX_GPIO
|
|||
help
|
||||
Support for the LPC32XX GPIO driver.
|
||||
|
||||
config MCP230XX_GPIO
|
||||
bool "MCP230XX GPIO driver"
|
||||
depends on DM
|
||||
help
|
||||
Support for Microchip's MCP230XX I2C connected GPIO devices.
|
||||
The following chips are supported:
|
||||
- MCP23008
|
||||
- MCP23017
|
||||
- MCP23018
|
||||
|
||||
config MSCC_SGPIO
|
||||
bool "Microsemi Serial GPIO driver"
|
||||
depends on DM_GPIO && SOC_VCOREIII
|
||||
|
|
|
@ -24,6 +24,7 @@ obj-$(CONFIG_KIRKWOOD_GPIO) += kw_gpio.o
|
|||
obj-$(CONFIG_KONA_GPIO) += kona_gpio.o
|
||||
obj-$(CONFIG_MARVELL_GPIO) += mvgpio.o
|
||||
obj-$(CONFIG_MARVELL_MFP) += mvmfp.o
|
||||
obj-$(CONFIG_MCP230XX_GPIO) += mcp230xx_gpio.o
|
||||
obj-$(CONFIG_MXC_GPIO) += mxc_gpio.o
|
||||
obj-$(CONFIG_MXS_GPIO) += mxs_gpio.o
|
||||
obj-$(CONFIG_PCA953X) += pca953x.o
|
||||
|
|
235
drivers/gpio/mcp230xx_gpio.c
Normal file
235
drivers/gpio/mcp230xx_gpio.c
Normal file
|
@ -0,0 +1,235 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright (C) 2021, Collabora Ltd.
|
||||
* Copyright (C) 2021, General Electric Company
|
||||
* Author(s): Sebastian Reichel <sebastian.reichel@collabora.com>
|
||||
*/
|
||||
|
||||
#define LOG_CATEGORY UCLASS_GPIO
|
||||
|
||||
#include <common.h>
|
||||
#include <errno.h>
|
||||
#include <dm.h>
|
||||
#include <i2c.h>
|
||||
#include <asm/gpio.h>
|
||||
#include <dm/device_compat.h>
|
||||
#include <dt-bindings/gpio/gpio.h>
|
||||
|
||||
enum mcp230xx_type {
|
||||
UNKNOWN = 0,
|
||||
MCP23008,
|
||||
MCP23017,
|
||||
MCP23018,
|
||||
};
|
||||
|
||||
#define MCP230XX_IODIR 0x00
|
||||
#define MCP230XX_GPPU 0x06
|
||||
#define MCP230XX_GPIO 0x09
|
||||
#define MCP230XX_OLAT 0x0a
|
||||
|
||||
#define BANKSIZE 8
|
||||
|
||||
static int mcp230xx_read(struct udevice *dev, uint reg, uint offset)
|
||||
{
|
||||
struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev);
|
||||
int bank = offset / BANKSIZE;
|
||||
int mask = 1 << (offset % BANKSIZE);
|
||||
int shift = (uc_priv->gpio_count / BANKSIZE) - 1;
|
||||
int ret;
|
||||
|
||||
ret = dm_i2c_reg_read(dev, (reg << shift) | bank);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return !!(ret & mask);
|
||||
}
|
||||
|
||||
static int mcp230xx_write(struct udevice *dev, uint reg, uint offset, bool val)
|
||||
{
|
||||
struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev);
|
||||
int bank = offset / BANKSIZE;
|
||||
int mask = 1 << (offset % BANKSIZE);
|
||||
int shift = (uc_priv->gpio_count / BANKSIZE) - 1;
|
||||
|
||||
return dm_i2c_reg_clrset(dev, (reg << shift) | bank, mask, val ? mask : 0);
|
||||
}
|
||||
|
||||
static int mcp230xx_get_value(struct udevice *dev, uint offset)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = mcp230xx_read(dev, MCP230XX_GPIO, offset);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "%s error: %d\n", __func__, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int mcp230xx_set_value(struct udevice *dev, uint offset, int val)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = mcp230xx_write(dev, MCP230XX_GPIO, offset, val);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "%s error: %d\n", __func__, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int mcp230xx_get_flags(struct udevice *dev, unsigned int offset,
|
||||
ulong *flags)
|
||||
{
|
||||
int direction, pullup;
|
||||
|
||||
pullup = mcp230xx_read(dev, MCP230XX_GPPU, offset);
|
||||
if (pullup < 0) {
|
||||
dev_err(dev, "%s error: %d\n", __func__, pullup);
|
||||
return pullup;
|
||||
}
|
||||
|
||||
direction = mcp230xx_read(dev, MCP230XX_IODIR, offset);
|
||||
if (direction < 0) {
|
||||
dev_err(dev, "%s error: %d\n", __func__, direction);
|
||||
return direction;
|
||||
}
|
||||
|
||||
*flags = direction ? GPIOD_IS_IN : GPIOD_IS_OUT;
|
||||
|
||||
if (pullup)
|
||||
*flags |= GPIOD_PULL_UP;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mcp230xx_set_flags(struct udevice *dev, uint offset, ulong flags)
|
||||
{
|
||||
bool input = !(flags & (GPIOD_IS_OUT | GPIOD_IS_OUT_ACTIVE));
|
||||
bool pullup = flags & GPIOD_PULL_UP;
|
||||
ulong supported_mask;
|
||||
int ret;
|
||||
|
||||
/* Note: active-low is ignored (handled by core) */
|
||||
supported_mask = GPIOD_ACTIVE_LOW | GPIOD_MASK_DIR | GPIOD_PULL_UP;
|
||||
if (flags & ~supported_mask) {
|
||||
dev_err(dev, "%s unsupported flag(s): %lx\n", __func__, flags);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ret = mcp230xx_write(dev, MCP230XX_OLAT, offset, !!(flags & GPIOD_IS_OUT_ACTIVE));
|
||||
if (ret) {
|
||||
dev_err(dev, "%s failed to setup output latch: %d\n", __func__, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = mcp230xx_write(dev, MCP230XX_GPPU, offset, pullup);
|
||||
if (ret) {
|
||||
dev_err(dev, "%s failed to setup pull-up: %d\n", __func__, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = mcp230xx_write(dev, MCP230XX_IODIR, offset, input);
|
||||
if (ret) {
|
||||
dev_err(dev, "%s failed to setup direction: %d\n", __func__, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mcp230xx_direction_input(struct udevice *dev, uint offset)
|
||||
{
|
||||
return mcp230xx_set_flags(dev, offset, GPIOD_IS_IN);
|
||||
}
|
||||
|
||||
static int mcp230xx_direction_output(struct udevice *dev, uint offset, int val)
|
||||
{
|
||||
int ret = mcp230xx_set_value(dev, offset, val);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "%s error: %d\n", __func__, ret);
|
||||
return ret;
|
||||
}
|
||||
return mcp230xx_set_flags(dev, offset, GPIOD_IS_OUT);
|
||||
}
|
||||
|
||||
static int mcp230xx_get_function(struct udevice *dev, uint offset)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = mcp230xx_read(dev, MCP230XX_IODIR, offset);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "%s error: %d\n", __func__, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return ret ? GPIOF_INPUT : GPIOF_OUTPUT;
|
||||
}
|
||||
|
||||
static const struct dm_gpio_ops mcp230xx_ops = {
|
||||
.direction_input = mcp230xx_direction_input,
|
||||
.direction_output = mcp230xx_direction_output,
|
||||
.get_value = mcp230xx_get_value,
|
||||
.set_value = mcp230xx_set_value,
|
||||
.get_function = mcp230xx_get_function,
|
||||
.set_flags = mcp230xx_set_flags,
|
||||
.get_flags = mcp230xx_get_flags,
|
||||
};
|
||||
|
||||
static int mcp230xx_probe(struct udevice *dev)
|
||||
{
|
||||
struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev);
|
||||
char name[32], label[8], *str;
|
||||
int addr, gpio_count, size;
|
||||
const u8 *tmp;
|
||||
|
||||
switch (dev_get_driver_data(dev)) {
|
||||
case MCP23008:
|
||||
gpio_count = 8;
|
||||
break;
|
||||
case MCP23017:
|
||||
case MCP23018:
|
||||
gpio_count = 16;
|
||||
break;
|
||||
default:
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
addr = dev_read_addr(dev);
|
||||
tmp = dev_read_prop(dev, "label", &size);
|
||||
if (tmp) {
|
||||
memcpy(label, tmp, sizeof(label) - 1);
|
||||
label[sizeof(label) - 1] = '\0';
|
||||
snprintf(name, sizeof(name), "%s@%x_", label, addr);
|
||||
} else {
|
||||
snprintf(name, sizeof(name), "gpio@%x_", addr);
|
||||
}
|
||||
|
||||
str = strdup(name);
|
||||
if (!str)
|
||||
return -ENOMEM;
|
||||
|
||||
uc_priv->bank_name = str;
|
||||
uc_priv->gpio_count = gpio_count;
|
||||
|
||||
dev_dbg(dev, "%s is ready\n", str);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct udevice_id mcp230xx_ids[] = {
|
||||
{ .compatible = "microchip,mcp23008", .data = MCP23008, },
|
||||
{ .compatible = "microchip,mcp23017", .data = MCP23017, },
|
||||
{ .compatible = "microchip,mcp23018", .data = MCP23018, },
|
||||
{ }
|
||||
};
|
||||
|
||||
U_BOOT_DRIVER(mcp230xx) = {
|
||||
.name = "mcp230xx",
|
||||
.id = UCLASS_GPIO,
|
||||
.ops = &mcp230xx_ops,
|
||||
.probe = mcp230xx_probe,
|
||||
.of_match = mcp230xx_ids,
|
||||
};
|
|
@ -336,8 +336,17 @@ static int i2c_gpio_of_to_plat(struct udevice *dev)
|
|||
struct i2c_gpio_bus *bus = dev_get_priv(dev);
|
||||
int ret;
|
||||
|
||||
/* "gpios" is deprecated and replaced by "sda-gpios" + "scl-gpios". */
|
||||
ret = gpio_request_list_by_name(dev, "gpios", bus->gpios,
|
||||
ARRAY_SIZE(bus->gpios), 0);
|
||||
if (ret == -ENOENT) {
|
||||
ret = gpio_request_by_name(dev, "sda-gpios", 0,
|
||||
&bus->gpios[PIN_SDA], 0);
|
||||
if (ret < 0)
|
||||
goto error;
|
||||
ret = gpio_request_by_name(dev, "scl-gpios", 0,
|
||||
&bus->gpios[PIN_SCL], 0);
|
||||
}
|
||||
if (ret < 0)
|
||||
goto error;
|
||||
|
||||
|
|
|
@ -247,6 +247,21 @@ int dm_i2c_reg_write(struct udevice *dev, uint offset, uint value)
|
|||
return dm_i2c_write(dev, offset, &val, 1);
|
||||
}
|
||||
|
||||
int dm_i2c_reg_clrset(struct udevice *dev, uint offset, u32 clr, u32 set)
|
||||
{
|
||||
uint8_t val;
|
||||
int ret;
|
||||
|
||||
ret = dm_i2c_read(dev, offset, &val, 1);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
val &= ~clr;
|
||||
val |= set;
|
||||
|
||||
return dm_i2c_write(dev, offset, &val, 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* i2c_probe_chip() - probe for a chip on a bus
|
||||
*
|
||||
|
|
|
@ -242,6 +242,20 @@ int dm_i2c_reg_read(struct udevice *dev, uint offset);
|
|||
*/
|
||||
int dm_i2c_reg_write(struct udevice *dev, uint offset, unsigned int val);
|
||||
|
||||
/**
|
||||
* dm_i2c_reg_clrset() - Apply bitmask to an I2C register
|
||||
*
|
||||
* Read value, apply bitmask and write modified value back to the
|
||||
* given address in an I2C chip
|
||||
*
|
||||
* @dev: Device to use for transfer
|
||||
* @offset: Address for the R/W operation
|
||||
* @clr: Bitmask of bits that should be cleared
|
||||
* @set: Bitmask of bits that should be set
|
||||
* @return 0 on success, -ve on error
|
||||
*/
|
||||
int dm_i2c_reg_clrset(struct udevice *dev, uint offset, u32 clr, u32 set);
|
||||
|
||||
/**
|
||||
* dm_i2c_xfer() - Transfer messages over I2C
|
||||
*
|
||||
|
|
|
@ -12,12 +12,13 @@
|
|||
#include <test/test.h>
|
||||
#include <test/ut.h>
|
||||
|
||||
static int dm_test_bootcount(struct unit_test_state *uts)
|
||||
static int dm_test_bootcount_rtc(struct unit_test_state *uts)
|
||||
{
|
||||
struct udevice *dev;
|
||||
u32 val;
|
||||
|
||||
ut_assertok(uclass_get_device(UCLASS_BOOTCOUNT, 0, &dev));
|
||||
ut_assertok(uclass_get_device_by_name(UCLASS_BOOTCOUNT, "bootcount@0",
|
||||
&dev));
|
||||
ut_assertok(dm_bootcount_set(dev, 0));
|
||||
ut_assertok(dm_bootcount_get(dev, &val));
|
||||
ut_assert(val == 0);
|
||||
|
@ -36,5 +37,46 @@ static int dm_test_bootcount(struct unit_test_state *uts)
|
|||
return 0;
|
||||
}
|
||||
|
||||
DM_TEST(dm_test_bootcount, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
|
||||
DM_TEST(dm_test_bootcount_rtc, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
|
||||
|
||||
static int dm_test_bootcount_syscon_four_bytes(struct unit_test_state *uts)
|
||||
{
|
||||
struct udevice *dev;
|
||||
u32 val;
|
||||
|
||||
sandbox_set_enable_memio(true);
|
||||
ut_assertok(uclass_get_device_by_name(UCLASS_BOOTCOUNT, "bootcount_4@0",
|
||||
&dev));
|
||||
ut_assertok(dm_bootcount_set(dev, 0xab));
|
||||
ut_assertok(dm_bootcount_get(dev, &val));
|
||||
ut_assert(val == 0xab);
|
||||
ut_assertok(dm_bootcount_set(dev, 0));
|
||||
ut_assertok(dm_bootcount_get(dev, &val));
|
||||
ut_assert(val == 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
DM_TEST(dm_test_bootcount_syscon_four_bytes,
|
||||
UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
|
||||
|
||||
static int dm_test_bootcount_syscon_two_bytes(struct unit_test_state *uts)
|
||||
{
|
||||
struct udevice *dev;
|
||||
u32 val;
|
||||
|
||||
sandbox_set_enable_memio(true);
|
||||
ut_assertok(uclass_get_device_by_name(UCLASS_BOOTCOUNT, "bootcount_2@0",
|
||||
&dev));
|
||||
ut_assertok(dm_bootcount_set(dev, 0xab));
|
||||
ut_assertok(dm_bootcount_get(dev, &val));
|
||||
ut_assert(val == 0xab);
|
||||
ut_assertok(dm_bootcount_set(dev, 0));
|
||||
ut_assertok(dm_bootcount_get(dev, &val));
|
||||
ut_assert(val == 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
DM_TEST(dm_test_bootcount_syscon_two_bytes,
|
||||
UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
|
||||
|
|
|
@ -304,3 +304,32 @@ static int dm_test_i2c_addr_offset(struct unit_test_state *uts)
|
|||
}
|
||||
|
||||
DM_TEST(dm_test_i2c_addr_offset, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
|
||||
|
||||
static int dm_test_i2c_reg_clrset(struct unit_test_state *uts)
|
||||
{
|
||||
struct udevice *eeprom;
|
||||
struct udevice *dev;
|
||||
u8 buf[5];
|
||||
|
||||
ut_assertok(i2c_get_chip_for_busnum(busnum, chip, 1, &dev));
|
||||
|
||||
/* Do a transfer so we can find the emulator */
|
||||
ut_assertok(dm_i2c_read(dev, 0, buf, 5));
|
||||
ut_assertok(uclass_first_device(UCLASS_I2C_EMUL, &eeprom));
|
||||
|
||||
/* Dummy data for the test */
|
||||
ut_assertok(dm_i2c_write(dev, 0, "\xff\x00\xff\x00\x10", 5));
|
||||
|
||||
/* Do some clrset tests */
|
||||
ut_assertok(dm_i2c_reg_clrset(dev, 0, 0xff, 0x10));
|
||||
ut_assertok(dm_i2c_reg_clrset(dev, 1, 0x00, 0x11));
|
||||
ut_assertok(dm_i2c_reg_clrset(dev, 2, 0xed, 0x00));
|
||||
ut_assertok(dm_i2c_reg_clrset(dev, 3, 0xff, 0x13));
|
||||
ut_assertok(dm_i2c_reg_clrset(dev, 4, 0x00, 0x14));
|
||||
|
||||
ut_assertok(dm_i2c_read(dev, 0, buf, 5));
|
||||
ut_asserteq_mem("\x10\x11\x12\x13\x14", buf, sizeof(buf));
|
||||
|
||||
return 0;
|
||||
}
|
||||
DM_TEST(dm_test_i2c_reg_clrset, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
|
||||
|
|
Loading…
Reference in a new issue