mirror of
https://github.com/smaeul/u-boot.git
synced 2025-10-18 06:38:15 +01:00
When bringing in the series 'arm: dts: am62-beagleplay: Fix Beagleplay Ethernet"' I failed to notice that b4 noticed it was based on next and so took that as the base commit and merged that part of next to master. This reverts commit c8ffd1356d42223cbb8c86280a083cc3c93e6426, reversing changes made to 2ee6f3a5f7550de3599faef9704e166e5dcace35. Reported-by: Jonas Karlman <jonas@kwiboo.se> Signed-off-by: Tom Rini <trini@konsulko.com>
178 lines
4.4 KiB
C
178 lines
4.4 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
/*
|
|
* PCF8575 I2C GPIO EXPANDER DRIVER
|
|
*
|
|
* Copyright (C) 2016 Texas Instruments Incorporated - https://www.ti.com/
|
|
*
|
|
* Vignesh R <vigneshr@ti.com>
|
|
*
|
|
*
|
|
* Driver for TI PCF-8575 16-bit I2C gpio expander. Based on
|
|
* gpio-pcf857x Linux Kernel(v4.7) driver.
|
|
*
|
|
* Copyright (C) 2007 David Brownell
|
|
*
|
|
* Add support for 8 bit expanders - like pca8574
|
|
* Copyright (C) 2021 Lukasz Majewski - DENX Software Engineering
|
|
*
|
|
*/
|
|
|
|
#include <common.h>
|
|
#include <dm.h>
|
|
#include <i2c.h>
|
|
#include <log.h>
|
|
#include <asm-generic/gpio.h>
|
|
#include <asm/global_data.h>
|
|
#include <linux/bitops.h>
|
|
|
|
DECLARE_GLOBAL_DATA_PTR;
|
|
|
|
struct pcf8575_chip {
|
|
/* NOTE: these chips have strange "quasi-bidirectional" I/O pins.
|
|
* We can't actually know whether a pin is configured (a) as output
|
|
* and driving the signal low, or (b) as input and reporting a low
|
|
* value ... without knowing the last value written since the chip
|
|
* came out of reset (if any). We can't read the latched output.
|
|
* In short, the only reliable solution for setting up pin direction
|
|
* is to do it explicitly.
|
|
*
|
|
* Using "out" avoids that trouble. When left initialized to zero,
|
|
* our software copy of the "latch" then matches the chip's all-ones
|
|
* reset state. Otherwise it flags pins to be driven low.
|
|
*/
|
|
unsigned int out; /* software latch */
|
|
};
|
|
|
|
/* Read/Write to I/O expander */
|
|
|
|
static int pcf8575_i2c_write(struct udevice *dev, unsigned int word)
|
|
{
|
|
struct dm_i2c_chip *chip = dev_get_parent_plat(dev);
|
|
u8 buf[2] = { word & 0xff, word >> 8, };
|
|
int ret;
|
|
|
|
ret = dm_i2c_write(dev, 0, buf, dev_get_driver_data(dev));
|
|
if (ret)
|
|
printf("%s i2c write failed to addr %x\n", __func__,
|
|
chip->chip_addr);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int pcf8575_i2c_read(struct udevice *dev)
|
|
{
|
|
struct dm_i2c_chip *chip = dev_get_parent_plat(dev);
|
|
u8 buf[2] = {0x00, 0x00};
|
|
int ret;
|
|
|
|
ret = dm_i2c_read(dev, 0, buf, dev_get_driver_data(dev));
|
|
if (ret) {
|
|
printf("%s i2c read failed from addr %x\n", __func__,
|
|
chip->chip_addr);
|
|
return ret;
|
|
}
|
|
|
|
return (buf[1] << 8) | buf[0];
|
|
}
|
|
|
|
static int pcf8575_direction_input(struct udevice *dev, unsigned offset)
|
|
{
|
|
struct pcf8575_chip *plat = dev_get_plat(dev);
|
|
int status;
|
|
|
|
plat->out |= BIT(offset);
|
|
status = pcf8575_i2c_write(dev, plat->out);
|
|
|
|
return status;
|
|
}
|
|
|
|
static int pcf8575_direction_output(struct udevice *dev,
|
|
unsigned int offset, int value)
|
|
{
|
|
struct pcf8575_chip *plat = dev_get_plat(dev);
|
|
int ret;
|
|
|
|
if (value)
|
|
plat->out |= BIT(offset);
|
|
else
|
|
plat->out &= ~BIT(offset);
|
|
|
|
ret = pcf8575_i2c_write(dev, plat->out);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int pcf8575_get_value(struct udevice *dev, unsigned int offset)
|
|
{
|
|
int value;
|
|
|
|
value = pcf8575_i2c_read(dev);
|
|
|
|
return (value < 0) ? value : ((value & BIT(offset)) >> offset);
|
|
}
|
|
|
|
static int pcf8575_set_value(struct udevice *dev, unsigned int offset,
|
|
int value)
|
|
{
|
|
return pcf8575_direction_output(dev, offset, value);
|
|
}
|
|
|
|
static int pcf8575_ofdata_plat(struct udevice *dev)
|
|
{
|
|
struct pcf8575_chip *plat = dev_get_plat(dev);
|
|
struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev);
|
|
|
|
int n_latch;
|
|
|
|
/*
|
|
* Number of pins depends on the expander device and is specified
|
|
* in the struct udevice_id (as in the Linue kernel).
|
|
*/
|
|
uc_priv->gpio_count = dev_get_driver_data(dev) * 8;
|
|
uc_priv->bank_name = fdt_getprop(gd->fdt_blob, dev_of_offset(dev),
|
|
"gpio-bank-name", NULL);
|
|
if (!uc_priv->bank_name)
|
|
uc_priv->bank_name = fdt_get_name(gd->fdt_blob,
|
|
dev_of_offset(dev), NULL);
|
|
|
|
n_latch = fdtdec_get_uint(gd->fdt_blob, dev_of_offset(dev),
|
|
"lines-initial-states", 0);
|
|
plat->out = ~n_latch;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int pcf8575_gpio_probe(struct udevice *dev)
|
|
{
|
|
struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev);
|
|
|
|
debug("%s GPIO controller with %d gpios probed\n",
|
|
uc_priv->bank_name, uc_priv->gpio_count);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static const struct dm_gpio_ops pcf8575_gpio_ops = {
|
|
.direction_input = pcf8575_direction_input,
|
|
.direction_output = pcf8575_direction_output,
|
|
.get_value = pcf8575_get_value,
|
|
.set_value = pcf8575_set_value,
|
|
};
|
|
|
|
static const struct udevice_id pcf8575_gpio_ids[] = {
|
|
{ .compatible = "nxp,pcf8575", .data = 2 },
|
|
{ .compatible = "ti,pcf8575", .data = 2 },
|
|
{ .compatible = "nxp,pca8574", .data = 1 },
|
|
{ }
|
|
};
|
|
|
|
U_BOOT_DRIVER(gpio_pcf8575) = {
|
|
.name = "gpio_pcf8575",
|
|
.id = UCLASS_GPIO,
|
|
.ops = &pcf8575_gpio_ops,
|
|
.of_match = pcf8575_gpio_ids,
|
|
.of_to_plat = pcf8575_ofdata_plat,
|
|
.probe = pcf8575_gpio_probe,
|
|
.plat_auto = sizeof(struct pcf8575_chip),
|
|
};
|