Merge git://git.denx.de/u-boot-dm

This commit is contained in:
Tom Rini
2015-08-06 19:56:03 -04:00
124 changed files with 5743 additions and 2291 deletions

View File

@@ -59,3 +59,46 @@ config DM_SEQ_ALIAS
Most boards will have a '/aliases' node containing the path to
numbered devices (e.g. serial0 = &serial0). This feature can be
disabled if it is not required, to save code space in SPL.
config REGMAP
bool "Support register maps"
depends on DM
help
Hardware peripherals tend to have one or more sets of registers
which can be accessed to control the hardware. A register map
models this with a simple read/write interface. It can in principle
support any bus type (I2C, SPI) but so far this only supports
direct memory access.
config SYSCON
bool "Support system controllers"
depends on REGMAP
help
Many SoCs have a number of system controllers which are dealt with
as a group by a single driver. Some common functionality is provided
by this uclass, including accessing registers via regmap and
assigning a unique number to each.
config DEVRES
bool "Managed device resources"
depends on DM
help
This option enables the Managed device resources core support.
Device resources managed by the devres framework are automatically
released whether initialization fails half-way or the device gets
detached.
If this option is disabled, devres functions fall back to
non-managed variants. For example, devres_alloc() to kzalloc(),
devm_kmalloc() to kmalloc(), etc.
config DEBUG_DEVRES
bool "Managed device resources debugging functions"
depends on DEVRES
help
If this option is enabled, devres debug messages are printed.
Also, a function is available to dump a list of device resources.
Select this if you are having a problem with devres or want to
debug resource management for a managed device.
If you are unsure about this, Say N here.

View File

@@ -5,10 +5,11 @@
#
obj-y += device.o lists.o root.o uclass.o util.o
obj-$(CONFIG_DEVRES) += devres.o
ifndef CONFIG_SPL_BUILD
obj-$(CONFIG_OF_CONTROL) += simple-bus.o
endif
obj-$(CONFIG_DM_DEVICE_REMOVE) += device-remove.o
obj-$(CONFIG_DM) += dump.o
obj-$(CONFIG_OF_CONTROL) += regmap.o
obj-$(CONFIG_OF_CONTROL) += syscon-uclass.o
obj-$(CONFIG_REGMAP) += regmap.o
obj-$(CONFIG_SYSCON) += syscon-uclass.o

View File

@@ -61,6 +61,9 @@ int device_unbind(struct udevice *dev)
if (dev->flags & DM_FLAG_ACTIVATED)
return -EINVAL;
if (!(dev->flags & DM_FLAG_BOUND))
return -EINVAL;
drv = dev->driver;
assert(drv);
@@ -92,6 +95,9 @@ int device_unbind(struct udevice *dev)
if (dev->parent)
list_del(&dev->sibling_node);
devres_release_all(dev);
free(dev);
return 0;
@@ -125,6 +131,8 @@ void device_free(struct udevice *dev)
dev->parent_priv = NULL;
}
}
devres_release_probe(dev);
}
int device_remove(struct udevice *dev)

View File

@@ -47,6 +47,9 @@ int device_bind(struct udevice *parent, const struct driver *drv,
INIT_LIST_HEAD(&dev->sibling_node);
INIT_LIST_HEAD(&dev->child_head);
INIT_LIST_HEAD(&dev->uclass_node);
#ifdef CONFIG_DEVRES
INIT_LIST_HEAD(&dev->devres_head);
#endif
dev->platdata = platdata;
dev->name = name;
dev->of_offset = of_offset;
@@ -132,6 +135,8 @@ int device_bind(struct udevice *parent, const struct driver *drv,
dm_dbg("Bound device %s to %s\n", dev->name, parent->name);
*devp = dev;
dev->flags |= DM_FLAG_BOUND;
return 0;
fail_child_post_bind:
@@ -168,6 +173,8 @@ fail_alloc2:
dev->platdata = NULL;
}
fail_alloc1:
devres_release_all(dev);
free(dev);
return ret;
@@ -552,17 +559,22 @@ const char *dev_get_uclass_name(struct udevice *dev)
return dev->uclass->uc_drv->name;
}
fdt_addr_t dev_get_addr(struct udevice *dev)
{
#ifdef CONFIG_OF_CONTROL
fdt_addr_t dev_get_addr(struct udevice *dev)
{
return fdtdec_get_addr(gd->fdt_blob, dev->of_offset, "reg");
}
fdt_addr_t addr;
addr = fdtdec_get_addr(gd->fdt_blob, dev->of_offset, "reg");
if (addr != FDT_ADDR_T_NONE) {
if (device_get_uclass_id(dev->parent) == UCLASS_SIMPLE_BUS)
addr = simple_bus_translate(dev->parent, addr);
}
return addr;
#else
fdt_addr_t dev_get_addr(struct udevice *dev)
{
return FDT_ADDR_T_NONE;
}
#endif
}
bool device_has_children(struct udevice *dev)
{
@@ -591,3 +603,13 @@ bool device_is_last_sibling(struct udevice *dev)
return false;
return list_is_last(&dev->sibling_node, &parent->child_head);
}
int device_set_name(struct udevice *dev, const char *name)
{
name = strdup(name);
if (!name)
return -ENOMEM;
dev->name = name;
return 0;
}

259
drivers/core/devres.c Normal file
View File

@@ -0,0 +1,259 @@
/*
* Copyright (C) 2015 Masahiro Yamada <yamada.masahiro@socionext.com>
*
* Based on the original work in Linux by
* Copyright (c) 2006 SUSE Linux Products GmbH
* Copyright (c) 2006 Tejun Heo <teheo@suse.de>
*
* SPDX-License-Identifier: GPL-2.0+
*/
#include <common.h>
#include <linux/compat.h>
#include <linux/kernel.h>
#include <linux/list.h>
#include <dm/device.h>
#include <dm/root.h>
#include <dm/util.h>
/**
* struct devres - Bookkeeping info for managed device resource
* @entry: List to associate this structure with a device
* @release: Callback invoked when this resource is released
* @probe: Flag to show when this resource was allocated
(true = probe, false = bind)
* @name: Name of release function
* @size: Size of resource data
* @data: Resource data
*/
struct devres {
struct list_head entry;
dr_release_t release;
bool probe;
#ifdef CONFIG_DEBUG_DEVRES
const char *name;
size_t size;
#endif
unsigned long long data[];
};
#ifdef CONFIG_DEBUG_DEVRES
static void set_node_dbginfo(struct devres *dr, const char *name, size_t size)
{
dr->name = name;
dr->size = size;
}
static void devres_log(struct udevice *dev, struct devres *dr,
const char *op)
{
printf("%s: DEVRES %3s %p %s (%lu bytes)\n",
dev->name, op, dr, dr->name, (unsigned long)dr->size);
}
#else /* CONFIG_DEBUG_DEVRES */
#define set_node_dbginfo(dr, n, s) do {} while (0)
#define devres_log(dev, dr, op) do {} while (0)
#endif
#if CONFIG_DEBUG_DEVRES
void *__devres_alloc(dr_release_t release, size_t size, gfp_t gfp,
const char *name)
#else
void *_devres_alloc(dr_release_t release, size_t size, gfp_t gfp)
#endif
{
size_t tot_size = sizeof(struct devres) + size;
struct devres *dr;
dr = kmalloc(tot_size, gfp);
if (unlikely(!dr))
return NULL;
INIT_LIST_HEAD(&dr->entry);
dr->release = release;
set_node_dbginfo(dr, name, size);
return dr->data;
}
void devres_free(void *res)
{
if (res) {
struct devres *dr = container_of(res, struct devres, data);
BUG_ON(!list_empty(&dr->entry));
kfree(dr);
}
}
void devres_add(struct udevice *dev, void *res)
{
struct devres *dr = container_of(res, struct devres, data);
devres_log(dev, dr, "ADD");
BUG_ON(!list_empty(&dr->entry));
dr->probe = dev->flags & DM_FLAG_BOUND ? true : false;
list_add_tail(&dr->entry, &dev->devres_head);
}
void *devres_find(struct udevice *dev, dr_release_t release,
dr_match_t match, void *match_data)
{
struct devres *dr;
list_for_each_entry_reverse(dr, &dev->devres_head, entry) {
if (dr->release != release)
continue;
if (match && !match(dev, dr->data, match_data))
continue;
return dr->data;
}
return NULL;
}
void *devres_get(struct udevice *dev, void *new_res,
dr_match_t match, void *match_data)
{
struct devres *new_dr = container_of(new_res, struct devres, data);
void *res;
res = devres_find(dev, new_dr->release, match, match_data);
if (!res) {
devres_add(dev, new_res);
res = new_res;
new_res = NULL;
}
devres_free(new_res);
return res;
}
void *devres_remove(struct udevice *dev, dr_release_t release,
dr_match_t match, void *match_data)
{
void *res;
res = devres_find(dev, release, match, match_data);
if (res) {
struct devres *dr = container_of(res, struct devres, data);
list_del_init(&dr->entry);
devres_log(dev, dr, "REM");
}
return res;
}
int devres_destroy(struct udevice *dev, dr_release_t release,
dr_match_t match, void *match_data)
{
void *res;
res = devres_remove(dev, release, match, match_data);
if (unlikely(!res))
return -ENOENT;
devres_free(res);
return 0;
}
int devres_release(struct udevice *dev, dr_release_t release,
dr_match_t match, void *match_data)
{
void *res;
res = devres_remove(dev, release, match, match_data);
if (unlikely(!res))
return -ENOENT;
(*release)(dev, res);
devres_free(res);
return 0;
}
static void release_nodes(struct udevice *dev, struct list_head *head,
bool probe_only)
{
struct devres *dr, *tmp;
list_for_each_entry_safe_reverse(dr, tmp, head, entry) {
if (probe_only && !dr->probe)
break;
devres_log(dev, dr, "REL");
dr->release(dev, dr->data);
list_del(&dr->entry);
kfree(dr);
}
}
void devres_release_probe(struct udevice *dev)
{
release_nodes(dev, &dev->devres_head, true);
}
void devres_release_all(struct udevice *dev)
{
release_nodes(dev, &dev->devres_head, false);
}
#ifdef CONFIG_DEBUG_DEVRES
static void dump_resources(struct udevice *dev, int depth)
{
struct devres *dr;
struct udevice *child;
printf("- %s\n", dev->name);
list_for_each_entry(dr, &dev->devres_head, entry)
printf(" %p (%lu byte) %s %s\n", dr,
(unsigned long)dr->size, dr->name,
dr->probe ? "PROBE" : "BIND");
list_for_each_entry(child, &dev->child_head, sibling_node)
dump_resources(child, depth + 1);
}
void dm_dump_devres(void)
{
struct udevice *root;
root = dm_root();
if (root)
dump_resources(root, 0);
}
#endif
/*
* Managed kmalloc/kfree
*/
static void devm_kmalloc_release(struct udevice *dev, void *res)
{
/* noop */
}
static int devm_kmalloc_match(struct udevice *dev, void *res, void *data)
{
return res == data;
}
void *devm_kmalloc(struct udevice *dev, size_t size, gfp_t gfp)
{
void *data;
data = _devres_alloc(devm_kmalloc_release, size, gfp);
if (unlikely(!data))
return NULL;
devres_add(dev, data);
return data;
}
void devm_kfree(struct udevice *dev, void *p)
{
int rc;
rc = devres_destroy(dev, devm_kmalloc_release, devm_kmalloc_match, p);
WARN_ON(rc);
}

View File

@@ -10,8 +10,37 @@
DECLARE_GLOBAL_DATA_PTR;
struct simple_bus_plat {
u32 base;
u32 size;
u32 target;
};
fdt_addr_t simple_bus_translate(struct udevice *dev, fdt_addr_t addr)
{
struct simple_bus_plat *plat = dev_get_uclass_platdata(dev);
if (addr >= plat->base && addr < plat->base + plat->size)
addr = (addr - plat->base) + plat->target;
return addr;
}
static int simple_bus_post_bind(struct udevice *dev)
{
u32 cell[3];
int ret;
ret = fdtdec_get_int_array(gd->fdt_blob, dev->of_offset, "ranges",
cell, ARRAY_SIZE(cell));
if (!ret) {
struct simple_bus_plat *plat = dev_get_uclass_platdata(dev);
plat->base = cell[0];
plat->target = cell[1];
plat->size = cell[2];
}
return dm_scan_fdt_node(dev, gd->fdt_blob, dev->of_offset, false);
}
@@ -19,6 +48,7 @@ UCLASS_DRIVER(simple_bus) = {
.id = UCLASS_SIMPLE_BUS,
.name = "simple_bus",
.post_bind = simple_bus_post_bind,
.per_device_platdata_auto_alloc_size = sizeof(struct simple_bus_plat),
};
static const struct udevice_id generic_simple_bus_ids[] = {

View File

@@ -273,6 +273,37 @@ static int uclass_find_device_by_of_offset(enum uclass_id id, int node,
return -ENODEV;
}
static int uclass_find_device_by_phandle(enum uclass_id id,
struct udevice *parent,
const char *name,
struct udevice **devp)
{
struct udevice *dev;
struct uclass *uc;
int find_phandle;
int ret;
*devp = NULL;
find_phandle = fdtdec_get_int(gd->fdt_blob, parent->of_offset, name,
-1);
if (find_phandle <= 0)
return -ENOENT;
ret = uclass_get(id, &uc);
if (ret)
return ret;
list_for_each_entry(dev, &uc->dev_head, uclass_node) {
uint phandle = fdt_get_phandle(gd->fdt_blob, dev->of_offset);
if (phandle == find_phandle) {
*devp = dev;
return 0;
}
}
return -ENODEV;
}
int uclass_get_device_tail(struct udevice *dev, int ret,
struct udevice **devp)
{
@@ -338,6 +369,17 @@ int uclass_get_device_by_of_offset(enum uclass_id id, int node,
return uclass_get_device_tail(dev, ret, devp);
}
int uclass_get_device_by_phandle(enum uclass_id id, struct udevice *parent,
const char *name, struct udevice **devp)
{
struct udevice *dev;
int ret;
*devp = NULL;
ret = uclass_find_device_by_phandle(id, parent, name, &dev);
return uclass_get_device_tail(dev, ret, devp);
}
int uclass_first_device(enum uclass_id id, struct udevice **devp)
{
struct udevice *dev;

View File

@@ -250,8 +250,12 @@ int gpio_free(unsigned gpio)
static int check_reserved(struct gpio_desc *desc, const char *func)
{
struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(desc->dev);
struct gpio_dev_priv *uc_priv;
if (!dm_gpio_is_valid(desc))
return -ENOENT;
uc_priv = dev_get_uclass_priv(desc->dev);
if (!uc_priv->name[desc->offset]) {
printf("%s: %s: error: gpio %s%d not reserved\n",
desc->dev->name, func,

View File

@@ -19,6 +19,30 @@ config DM_I2C_COMPAT
to convert all code for a board in a single commit. It should not
be enabled for any board in an official release.
config I2C_CROS_EC_TUNNEL
tristate "Chrome OS EC tunnel I2C bus"
depends on CROS_EC
help
This provides an I2C bus that will tunnel i2c commands through to
the other side of the Chrome OS EC to the I2C bus connected there.
This will work whatever the interface used to talk to the EC (SPI,
I2C or LPC). Some Chromebooks use this when the hardware design
does not allow direct access to the main PMIC from the AP.
config I2C_CROS_EC_LDO
bool "Provide access to LDOs on the Chrome OS EC"
depends on CROS_EC
---help---
On many Chromebooks the main PMIC is inaccessible to the AP. This is
often dealt with by using an I2C pass-through interface provided by
the EC. On some unfortunate models (e.g. Spring) the pass-through
is not available, and an LDO message is available instead. This
option enables a driver which provides very basic access to those
regulators, via the EC. We implement this as an I2C bus which
emulates just the TPS65090 messages we know about. This is done to
avoid duplicating the logic in the TPS65090 regulator driver for
enabling/disabling an LDO.
config DM_I2C_GPIO
bool "Enable Driver Model for software emulated I2C bus driver"
depends on DM_I2C && DM_GPIO
@@ -73,3 +97,5 @@ config SYS_I2C_UNIPHIER_F
help
Support for UniPhier FIFO-builtin I2C controller driver.
This I2C controller is used on PH1-Pro4 or newer UniPhier SoCs.
source "drivers/i2c/muxes/Kconfig"

View File

@@ -7,6 +7,8 @@
obj-$(CONFIG_DM_I2C) += i2c-uclass.o
obj-$(CONFIG_DM_I2C_COMPAT) += i2c-uclass-compat.o
obj-$(CONFIG_DM_I2C_GPIO) += i2c-gpio.o
obj-$(CONFIG_I2C_CROS_EC_TUNNEL) += cros_ec_tunnel.o
obj-$(CONFIG_I2C_CROS_EC_LDO) += cros_ec_ldo.o
obj-$(CONFIG_SYS_I2C_ADI) += adi_i2c.o
obj-$(CONFIG_I2C_MV) += mv_i2c.o
@@ -37,3 +39,5 @@ obj-$(CONFIG_SYS_I2C_TEGRA) += tegra_i2c.o
obj-$(CONFIG_SYS_I2C_UNIPHIER) += i2c-uniphier.o
obj-$(CONFIG_SYS_I2C_UNIPHIER_F) += i2c-uniphier-f.o
obj-$(CONFIG_SYS_I2C_ZYNQ) += zynq_i2c.o
obj-y += muxes/

77
drivers/i2c/cros_ec_ldo.c Normal file
View File

@@ -0,0 +1,77 @@
/*
* Copyright (c) 2015 Google, Inc
* Written by Simon Glass <sjg@chromium.org>
*
* SPDX-License-Identifier: GPL-2.0+
*/
#include <common.h>
#include <dm.h>
#include <cros_ec.h>
#include <errno.h>
#include <i2c.h>
#include <power/tps65090.h>
static int cros_ec_ldo_set_bus_speed(struct udevice *dev, unsigned int speed)
{
return 0;
}
static int cros_ec_ldo_xfer(struct udevice *dev, struct i2c_msg *msg,
int nmsgs)
{
bool is_read = nmsgs > 1;
int fet_id, ret;
/*
* Look for reads and writes of the LDO registers. In either case the
* first message is a write with the register number as the first byte.
*/
if (!nmsgs || !msg->len || (msg->flags & I2C_M_RD)) {
debug("%s: Invalid message\n", __func__);
goto err;
}
fet_id = msg->buf[0] - REG_FET_BASE;
if (fet_id < 1 || fet_id > MAX_FET_NUM) {
debug("%s: Invalid FET %d\n", __func__, fet_id);
goto err;
}
if (is_read) {
uint8_t state;
ret = cros_ec_get_ldo(dev->parent, fet_id, &state);
if (!ret)
msg[1].buf[0] = state ?
FET_CTRL_ENFET | FET_CTRL_PGFET : 0;
} else {
bool on = msg->buf[1] & FET_CTRL_ENFET;
ret = cros_ec_set_ldo(dev->parent, fet_id, on);
}
return ret;
err:
/* Indicate that the message is unimplemented */
return -ENOSYS;
}
static const struct dm_i2c_ops cros_ec_i2c_ops = {
.xfer = cros_ec_ldo_xfer,
.set_bus_speed = cros_ec_ldo_set_bus_speed,
};
static const struct udevice_id cros_ec_i2c_ids[] = {
{ .compatible = "google,cros-ec-ldo-tunnel" },
{ }
};
U_BOOT_DRIVER(cros_ec_ldo) = {
.name = "cros_ec_ldo_tunnel",
.id = UCLASS_I2C,
.of_match = cros_ec_i2c_ids,
.per_child_auto_alloc_size = sizeof(struct dm_i2c_chip),
.ops = &cros_ec_i2c_ops,
};

View File

@@ -0,0 +1,41 @@
/*
* Copyright (c) 2015 Google, Inc
* Written by Simon Glass <sjg@chromium.org>
*
* SPDX-License-Identifier: GPL-2.0+
*/
#include <common.h>
#include <dm.h>
#include <cros_ec.h>
#include <errno.h>
#include <i2c.h>
static int cros_ec_i2c_set_bus_speed(struct udevice *dev, unsigned int speed)
{
return 0;
}
static int cros_ec_i2c_xfer(struct udevice *dev, struct i2c_msg *msg,
int nmsgs)
{
return cros_ec_i2c_tunnel(dev->parent, msg, nmsgs);
}
static const struct dm_i2c_ops cros_ec_i2c_ops = {
.xfer = cros_ec_i2c_xfer,
.set_bus_speed = cros_ec_i2c_set_bus_speed,
};
static const struct udevice_id cros_ec_i2c_ids[] = {
{ .compatible = "google,cros-ec-i2c-tunnel" },
{ }
};
U_BOOT_DRIVER(cros_ec_tunnel) = {
.name = "cros_ec_tunnel",
.id = UCLASS_I2C,
.of_match = cros_ec_i2c_ids,
.per_child_auto_alloc_size = sizeof(struct dm_i2c_chip),
.ops = &cros_ec_i2c_ops,
};

View File

@@ -18,6 +18,22 @@ DECLARE_GLOBAL_DATA_PTR;
#define I2C_MAX_OFFSET_LEN 4
/* Useful debugging function */
void i2c_dump_msgs(struct i2c_msg *msg, int nmsgs)
{
int i;
for (i = 0; i < nmsgs; i++) {
struct i2c_msg *m = &msg[i];
printf(" %s %x len=%x", m->flags & I2C_M_RD ? "R" : "W",
msg->addr, msg->len);
if (!(m->flags & I2C_M_RD))
printf(": %x", m->buf[0]);
printf("\n");
}
}
/**
* i2c_setup_offset() - Set up a new message with a chip offset
*
@@ -186,6 +202,17 @@ int dm_i2c_write(struct udevice *dev, uint offset, const uint8_t *buffer,
}
}
int dm_i2c_xfer(struct udevice *dev, struct i2c_msg *msg, int nmsgs)
{
struct udevice *bus = dev_get_parent(dev);
struct dm_i2c_ops *ops = i2c_get_ops(bus);
if (!ops->xfer)
return -ENOSYS;
return ops->xfer(bus, msg, nmsgs);
}
int dm_i2c_reg_read(struct udevice *dev, uint offset)
{
uint8_t val;

17
drivers/i2c/muxes/Kconfig Normal file
View File

@@ -0,0 +1,17 @@
config I2C_MUX
bool "Suport I2C multiplexers"
depends on DM_I2C
help
This enables I2C buses to be multiplexed, so that you can select
one of several buses using some sort of control mechanism. The
bus select is handled automatically when that bus is accessed,
using a suitable I2C MUX driver.
config I2C_ARB_GPIO_CHALLENGE
bool "GPIO-based I2C arbitration"
depends on I2C_MUX
help
If you say yes to this option, support will be included for an
I2C multimaster arbitration scheme using GPIOs and a challenge &
response mechanism where masters have to claim the bus by asserting
a GPIO.

View File

@@ -0,0 +1,7 @@
#
# Copyright (c) 2015 Google, Inc
#
# SPDX-License-Identifier: GPL-2.0+
#
obj-$(CONFIG_I2C_ARB_GPIO_CHALLENGE) += i2c-arb-gpio-challenge.o
obj-$(CONFIG_I2C_MUX) += i2c-mux-uclass.o

View File

@@ -0,0 +1,147 @@
/*
* Copyright (c) 2015 Google, Inc
* Written by Simon Glass <sjg@chromium.org>
*
* SPDX-License-Identifier: GPL-2.0+
*/
#include <common.h>
#include <dm.h>
#include <errno.h>
#include <i2c.h>
#include <asm/gpio.h>
DECLARE_GLOBAL_DATA_PTR;
struct i2c_arbitrator_priv {
struct gpio_desc ap_claim;
struct gpio_desc ec_claim;
uint slew_delay_us;
uint wait_retry_ms;
uint wait_free_ms;
};
int i2c_arbitrator_deselect(struct udevice *mux, struct udevice *bus,
uint channel)
{
struct i2c_arbitrator_priv *priv = dev_get_priv(mux);
int ret;
debug("%s: %s\n", __func__, mux->name);
ret = dm_gpio_set_value(&priv->ap_claim, 0);
udelay(priv->slew_delay_us);
return ret;
}
int i2c_arbitrator_select(struct udevice *mux, struct udevice *bus,
uint channel)
{
struct i2c_arbitrator_priv *priv = dev_get_priv(mux);
unsigned start;
int ret;
debug("%s: %s\n", __func__, mux->name);
/* Start a round of trying to claim the bus */
start = get_timer(0);
do {
unsigned start_retry;
int waiting = 0;
/* Indicate that we want to claim the bus */
ret = dm_gpio_set_value(&priv->ap_claim, 1);
if (ret)
goto err;
udelay(priv->slew_delay_us);
/* Wait for the EC to release it */
start_retry = get_timer(0);
while (get_timer(start_retry) < priv->wait_retry_ms) {
ret = dm_gpio_get_value(&priv->ec_claim);
if (ret < 0) {
goto err;
} else if (!ret) {
/* We got it, so return */
return 0;
}
if (!waiting)
waiting = 1;
}
/* It didn't release, so give up, wait, and try again */
ret = dm_gpio_set_value(&priv->ap_claim, 0);
if (ret)
goto err;
mdelay(priv->wait_retry_ms);
} while (get_timer(start) < priv->wait_free_ms);
/* Give up, release our claim */
printf("I2C: Could not claim bus, timeout %lu\n", get_timer(start));
ret = -ETIMEDOUT;
ret = 0;
err:
return ret;
}
static int i2c_arbitrator_probe(struct udevice *dev)
{
struct i2c_arbitrator_priv *priv = dev_get_priv(dev);
const void *blob = gd->fdt_blob;
int node = dev->of_offset;
int ret;
debug("%s: %s\n", __func__, dev->name);
priv->slew_delay_us = fdtdec_get_int(blob, node, "slew-delay-us", 0);
priv->wait_retry_ms = fdtdec_get_int(blob, node, "wait-retry-us", 0) /
1000;
priv->wait_free_ms = fdtdec_get_int(blob, node, "wait-free-us", 0) /
1000;
ret = gpio_request_by_name(dev, "our-claim-gpio", 0, &priv->ap_claim,
GPIOD_IS_OUT);
if (ret)
goto err;
ret = gpio_request_by_name(dev, "their-claim-gpios", 0, &priv->ec_claim,
GPIOD_IS_IN);
if (ret)
goto err_ec_gpio;
return 0;
err_ec_gpio:
dm_gpio_free(dev, &priv->ap_claim);
err:
debug("%s: ret=%d\n", __func__, ret);
return ret;
}
static int i2c_arbitrator_remove(struct udevice *dev)
{
struct i2c_arbitrator_priv *priv = dev_get_priv(dev);
dm_gpio_free(dev, &priv->ap_claim);
dm_gpio_free(dev, &priv->ec_claim);
return 0;
}
static const struct i2c_mux_ops i2c_arbitrator_ops = {
.select = i2c_arbitrator_select,
.deselect = i2c_arbitrator_deselect,
};
static const struct udevice_id i2c_arbitrator_ids[] = {
{ .compatible = "i2c-arb-gpio-challenge" },
{ }
};
U_BOOT_DRIVER(i2c_arbitrator) = {
.name = "i2c_arbitrator",
.id = UCLASS_I2C_MUX,
.of_match = i2c_arbitrator_ids,
.probe = i2c_arbitrator_probe,
.remove = i2c_arbitrator_remove,
.ops = &i2c_arbitrator_ops,
.priv_auto_alloc_size = sizeof(struct i2c_arbitrator_priv),
};

View File

@@ -0,0 +1,198 @@
/*
* Copyright (c) 2015 Google, Inc
* Written by Simon Glass <sjg@chromium.org>
*
* SPDX-License-Identifier: GPL-2.0+
*/
#include <common.h>
#include <dm.h>
#include <errno.h>
#include <i2c.h>
#include <dm/lists.h>
#include <dm/root.h>
DECLARE_GLOBAL_DATA_PTR;
/**
* struct i2c_mux: Information the uclass stores about an I2C mux
*
* @selected: Currently selected mux, or -1 for none
* @i2c_bus: I2C bus to use for communcation
*/
struct i2c_mux {
int selected;
struct udevice *i2c_bus;
};
/**
* struct i2c_mux_bus: Information about each bus the mux controls
*
* @channel: Channel number used to select this bus
*/
struct i2c_mux_bus {
uint channel;
};
/* Find out the mux channel number */
static int i2c_mux_child_post_bind(struct udevice *dev)
{
struct i2c_mux_bus *plat = dev_get_parent_platdata(dev);
int channel;
channel = fdtdec_get_int(gd->fdt_blob, dev->of_offset, "reg", -1);
if (channel < 0)
return -EINVAL;
plat->channel = channel;
return 0;
}
/* Find the I2C buses selected by this mux */
static int i2c_mux_post_bind(struct udevice *mux)
{
const void *blob = gd->fdt_blob;
int ret;
int offset;
debug("%s: %s\n", __func__, mux->name);
/*
* There is no compatible string in the sub-nodes, so we must manually
* bind these
*/
for (offset = fdt_first_subnode(blob, mux->of_offset);
offset > 0;
offset = fdt_next_subnode(blob, offset)) {
struct udevice *dev;
const char *name;
name = fdt_get_name(blob, offset, NULL);
ret = device_bind_driver_to_node(mux, "i2c_mux_bus_drv", name,
offset, &dev);
debug(" - bind ret=%d, %s\n", ret, dev ? dev->name : NULL);
if (ret)
return ret;
}
return 0;
}
/* Set up the mux ready for use */
static int i2c_mux_post_probe(struct udevice *mux)
{
struct i2c_mux *priv = dev_get_uclass_priv(mux);
int ret;
debug("%s: %s\n", __func__, mux->name);
priv->selected = -1;
ret = uclass_get_device_by_phandle(UCLASS_I2C, mux, "i2c-parent",
&priv->i2c_bus);
if (ret)
return ret;
debug("%s: bus=%p/%s\n", __func__, priv->i2c_bus, priv->i2c_bus->name);
return 0;
}
int i2c_mux_select(struct udevice *dev)
{
struct i2c_mux_bus *plat = dev_get_parent_platdata(dev);
struct udevice *mux = dev->parent;
struct i2c_mux_ops *ops = i2c_mux_get_ops(mux);
if (!ops->select)
return -ENOSYS;
return ops->select(mux, dev, plat->channel);
}
int i2c_mux_deselect(struct udevice *dev)
{
struct i2c_mux_bus *plat = dev_get_parent_platdata(dev);
struct udevice *mux = dev->parent;
struct i2c_mux_ops *ops = i2c_mux_get_ops(mux);
if (!ops->deselect)
return -ENOSYS;
return ops->deselect(mux, dev, plat->channel);
}
static int i2c_mux_bus_set_bus_speed(struct udevice *dev, unsigned int speed)
{
struct udevice *mux = dev->parent;
struct i2c_mux *priv = dev_get_uclass_priv(mux);
int ret, ret2;
ret = i2c_mux_select(dev);
if (ret)
return ret;
ret = dm_i2c_set_bus_speed(priv->i2c_bus, speed);
ret2 = i2c_mux_deselect(dev);
return ret ? ret : ret2;
}
static int i2c_mux_bus_probe(struct udevice *dev, uint chip_addr,
uint chip_flags)
{
struct udevice *mux = dev->parent;
struct i2c_mux *priv = dev_get_uclass_priv(mux);
struct dm_i2c_ops *ops = i2c_get_ops(priv->i2c_bus);
int ret, ret2;
debug("%s: %s, bus %s\n", __func__, dev->name, priv->i2c_bus->name);
if (!ops->probe_chip)
return -ENOSYS;
ret = i2c_mux_select(dev);
if (ret)
return ret;
ret = ops->probe_chip(priv->i2c_bus, chip_addr, chip_flags);
ret2 = i2c_mux_deselect(dev);
return ret ? ret : ret2;
}
static int i2c_mux_bus_xfer(struct udevice *dev, struct i2c_msg *msg,
int nmsgs)
{
struct udevice *mux = dev->parent;
struct i2c_mux *priv = dev_get_uclass_priv(mux);
struct dm_i2c_ops *ops = i2c_get_ops(priv->i2c_bus);
int ret, ret2;
debug("%s: %s, bus %s\n", __func__, dev->name, priv->i2c_bus->name);
if (!ops->xfer)
return -ENOSYS;
ret = i2c_mux_select(dev);
if (ret)
return ret;
ret = ops->xfer(priv->i2c_bus, msg, nmsgs);
ret2 = i2c_mux_deselect(dev);
return ret ? ret : ret2;
}
static const struct dm_i2c_ops i2c_mux_bus_ops = {
.xfer = i2c_mux_bus_xfer,
.probe_chip = i2c_mux_bus_probe,
.set_bus_speed = i2c_mux_bus_set_bus_speed,
};
U_BOOT_DRIVER(i2c_mux_bus) = {
.name = "i2c_mux_bus_drv",
.id = UCLASS_I2C,
.per_child_auto_alloc_size = sizeof(struct dm_i2c_chip),
.ops = &i2c_mux_bus_ops,
};
UCLASS_DRIVER(i2c_mux) = {
.id = UCLASS_I2C_MUX,
.name = "i2c_mux",
.post_bind = i2c_mux_post_bind,
.post_probe = i2c_mux_post_probe,
.per_device_auto_alloc_size = sizeof(struct i2c_mux),
.per_child_platdata_auto_alloc_size = sizeof(struct i2c_mux_bus),
.child_post_bind = i2c_mux_child_post_bind,
};

View File

@@ -258,9 +258,9 @@ static int hsi2c_wait_for_trx(struct exynos5_hsi2c *i2c)
return I2C_NOK_TOUT;
}
static void ReadWriteByte(struct s3c24x0_i2c *i2c)
static void read_write_byte(struct s3c24x0_i2c *i2c)
{
writel(readl(&i2c->iiccon) & ~I2CCON_IRPND, &i2c->iiccon);
clrbits_le32(&i2c->iiccon, I2CCON_IRPND);
}
#ifdef CONFIG_SYS_I2C
@@ -794,7 +794,7 @@ static int i2c_transfer(struct s3c24x0_i2c *i2c,
if (addr && addr_len) {
while ((i < addr_len) && (result == I2C_OK)) {
writel(addr[i++], &i2c->iicds);
ReadWriteByte(i2c);
read_write_byte(i2c);
result = WaitForXfer(i2c);
}
i = 0;
@@ -806,7 +806,7 @@ static int i2c_transfer(struct s3c24x0_i2c *i2c,
case I2C_WRITE:
while ((i < data_len) && (result == I2C_OK)) {
writel(data[i++], &i2c->iicds);
ReadWriteByte(i2c);
read_write_byte(i2c);
result = WaitForXfer(i2c);
}
break;
@@ -822,7 +822,7 @@ static int i2c_transfer(struct s3c24x0_i2c *i2c,
/* Generate a re-START. */
writel(I2C_MODE_MR | I2C_TXRX_ENA | I2C_START_STOP,
&i2c->iicstat);
ReadWriteByte(i2c);
read_write_byte(i2c);
result = WaitForXfer(i2c);
if (result != I2C_OK)
@@ -835,7 +835,7 @@ static int i2c_transfer(struct s3c24x0_i2c *i2c,
writel(readl(&i2c->iiccon)
& ~I2CCON_ACKGEN,
&i2c->iiccon);
ReadWriteByte(i2c);
read_write_byte(i2c);
result = WaitForXfer(i2c);
data[i++] = readl(&i2c->iicds);
}
@@ -852,7 +852,7 @@ static int i2c_transfer(struct s3c24x0_i2c *i2c,
bailout:
/* Send STOP. */
writel(I2C_MODE_MR | I2C_TXRX_ENA, &i2c->iicstat);
ReadWriteByte(i2c);
read_write_byte(i2c);
return result;
}
@@ -1284,62 +1284,106 @@ U_BOOT_I2C_ADAP_COMPLETE(s3c0, s3c24x0_i2c_init, s3c24x0_i2c_probe,
#endif /* CONFIG_SYS_I2C */
#ifdef CONFIG_DM_I2C
static int i2c_write_data(struct s3c24x0_i2c_bus *i2c_bus, uchar chip,
uchar *buffer, int len, bool end_with_repeated_start)
static int exynos_hs_i2c_xfer(struct udevice *dev, struct i2c_msg *msg,
int nmsgs)
{
struct s3c24x0_i2c_bus *i2c_bus = dev_get_priv(dev);
struct exynos5_hsi2c *hsregs = i2c_bus->hsregs;
int ret;
if (i2c_bus->is_highspeed) {
ret = hsi2c_write(i2c_bus->hsregs, chip, 0, 0,
buffer, len, true);
if (ret)
for (; nmsgs > 0; nmsgs--, msg++) {
if (msg->flags & I2C_M_RD) {
ret = hsi2c_read(hsregs, msg->addr, 0, 0, msg->buf,
msg->len);
} else {
ret = hsi2c_write(hsregs, msg->addr, 0, 0, msg->buf,
msg->len, true);
}
if (ret) {
exynos5_i2c_reset(i2c_bus);
} else {
ret = i2c_transfer(i2c_bus->regs, I2C_WRITE,
chip << 1, 0, 0, buffer, len);
return -EREMOTEIO;
}
}
return ret != I2C_OK;
return 0;
}
static int i2c_read_data(struct s3c24x0_i2c_bus *i2c_bus, uchar chip,
uchar *buffer, int len)
static int s3c24x0_do_msg(struct s3c24x0_i2c_bus *i2c_bus, struct i2c_msg *msg,
int seq)
{
int ret;
struct s3c24x0_i2c *i2c = i2c_bus->regs;
bool is_read = msg->flags & I2C_M_RD;
uint status;
uint addr;
int ret, i;
if (i2c_bus->is_highspeed) {
ret = hsi2c_read(i2c_bus->hsregs, chip, 0, 0, buffer, len);
if (ret)
exynos5_i2c_reset(i2c_bus);
if (!seq)
setbits_le32(&i2c->iiccon, I2CCON_ACKGEN);
/* Get the slave chip address going */
addr = msg->addr << 1;
writel(addr, &i2c->iicds);
status = I2C_TXRX_ENA | I2C_START_STOP;
if (is_read)
status |= I2C_MODE_MR;
else
status |= I2C_MODE_MT;
writel(status, &i2c->iicstat);
if (seq)
read_write_byte(i2c);
/* Wait for chip address to transmit */
ret = WaitForXfer(i2c);
if (ret)
goto err;
if (is_read) {
for (i = 0; !ret && i < msg->len; i++) {
/* disable ACK for final READ */
if (i == msg->len - 1)
clrbits_le32(&i2c->iiccon, I2CCON_ACKGEN);
read_write_byte(i2c);
ret = WaitForXfer(i2c);
msg->buf[i] = readl(&i2c->iicds);
}
if (ret == I2C_NACK)
ret = I2C_OK; /* Normal terminated read */
} else {
ret = i2c_transfer(i2c_bus->regs, I2C_READ,
chip << 1, 0, 0, buffer, len);
for (i = 0; !ret && i < msg->len; i++) {
writel(msg->buf[i], &i2c->iicds);
read_write_byte(i2c);
ret = WaitForXfer(i2c);
}
}
return ret != I2C_OK;
err:
return ret;
}
static int s3c24x0_i2c_xfer(struct udevice *dev, struct i2c_msg *msg,
int nmsgs)
{
struct s3c24x0_i2c_bus *i2c_bus = dev_get_priv(dev);
int ret;
struct s3c24x0_i2c *i2c = i2c_bus->regs;
ulong start_time;
int ret, i;
for (; nmsgs > 0; nmsgs--, msg++) {
bool next_is_read = nmsgs > 1 && (msg[1].flags & I2C_M_RD);
if (msg->flags & I2C_M_RD) {
ret = i2c_read_data(i2c_bus, msg->addr, msg->buf,
msg->len);
} else {
ret = i2c_write_data(i2c_bus, msg->addr, msg->buf,
msg->len, next_is_read);
start_time = get_timer(0);
while (readl(&i2c->iicstat) & I2CSTAT_BSY) {
if (get_timer(start_time) > I2C_TIMEOUT_MS) {
debug("Timeout\n");
return -ETIMEDOUT;
}
if (ret)
return -EREMOTEIO;
}
return 0;
for (ret = 0, i = 0; !ret && i < nmsgs; i++)
ret = s3c24x0_do_msg(i2c_bus, &msg[i], i);
/* Send STOP */
writel(I2C_MODE_MR | I2C_TXRX_ENA, &i2c->iicstat);
read_write_byte(i2c);
return ret ? -EREMOTEIO : 0;
}
static int s3c_i2c_ofdata_to_platdata(struct udevice *dev)
@@ -1364,8 +1408,7 @@ static int s3c_i2c_ofdata_to_platdata(struct udevice *dev)
i2c_bus->id = pinmux_decode_periph_id(blob, node);
i2c_bus->clock_frequency = fdtdec_get_int(blob, node,
"clock-frequency",
CONFIG_SYS_I2C_S3C24X0_SPEED);
"clock-frequency", 100000);
i2c_bus->node = node;
i2c_bus->bus_num = dev->seq;
@@ -1384,7 +1427,6 @@ static const struct dm_i2c_ops s3c_i2c_ops = {
static const struct udevice_id s3c_i2c_ids[] = {
{ .compatible = "samsung,s3c2440-i2c", .data = EXYNOS_I2C_STD },
{ .compatible = "samsung,exynos5-hsi2c", .data = EXYNOS_I2C_HS },
{ }
};
@@ -1397,4 +1439,29 @@ U_BOOT_DRIVER(i2c_s3c) = {
.priv_auto_alloc_size = sizeof(struct s3c24x0_i2c_bus),
.ops = &s3c_i2c_ops,
};
/*
* TODO(sjg@chromium.org): Move this to a separate file when everything uses
* driver model
*/
static const struct dm_i2c_ops exynos_hs_i2c_ops = {
.xfer = exynos_hs_i2c_xfer,
.probe_chip = s3c24x0_i2c_probe,
.set_bus_speed = s3c24x0_i2c_set_bus_speed,
};
static const struct udevice_id exynos_hs_i2c_ids[] = {
{ .compatible = "samsung,exynos5-hsi2c", .data = EXYNOS_I2C_HS },
{ }
};
U_BOOT_DRIVER(hs_i2c) = {
.name = "i2c_s3c_hs",
.id = UCLASS_I2C,
.of_match = exynos_hs_i2c_ids,
.ofdata_to_platdata = s3c_i2c_ofdata_to_platdata,
.per_child_auto_alloc_size = sizeof(struct dm_i2c_chip),
.priv_auto_alloc_size = sizeof(struct s3c24x0_i2c_bus),
.ops = &exynos_hs_i2c_ops,
};
#endif /* CONFIG_DM_I2C */

View File

@@ -26,6 +26,7 @@
#include <asm/io.h>
#include <asm-generic/gpio.h>
#include <dm/device-internal.h>
#include <dm/root.h>
#include <dm/uclass-internal.h>
#ifdef DEBUG_TRACE
@@ -930,31 +931,32 @@ int cros_ec_write_vbnvcontext(struct cros_ec_dev *dev, const uint8_t *block)
return 0;
}
int cros_ec_set_ldo(struct cros_ec_dev *dev, uint8_t index, uint8_t state)
int cros_ec_set_ldo(struct udevice *dev, uint8_t index, uint8_t state)
{
struct cros_ec_dev *cdev = dev_get_uclass_priv(dev);
struct ec_params_ldo_set params;
params.index = index;
params.state = state;
if (ec_command_inptr(dev, EC_CMD_LDO_SET, 0,
&params, sizeof(params),
NULL, 0))
if (ec_command_inptr(cdev, EC_CMD_LDO_SET, 0, &params, sizeof(params),
NULL, 0))
return -1;
return 0;
}
int cros_ec_get_ldo(struct cros_ec_dev *dev, uint8_t index, uint8_t *state)
int cros_ec_get_ldo(struct udevice *dev, uint8_t index, uint8_t *state)
{
struct cros_ec_dev *cdev = dev_get_uclass_priv(dev);
struct ec_params_ldo_get params;
struct ec_response_ldo_get *resp;
params.index = index;
if (ec_command_inptr(dev, EC_CMD_LDO_GET, 0,
&params, sizeof(params),
(uint8_t **)&resp, sizeof(*resp)) != sizeof(*resp))
if (ec_command_inptr(cdev, EC_CMD_LDO_GET, 0, &params, sizeof(params),
(uint8_t **)&resp, sizeof(*resp)) !=
sizeof(*resp))
return -1;
*state = resp->state;
@@ -1053,9 +1055,9 @@ int cros_ec_decode_ec_flash(const void *blob, int node,
return 0;
}
int cros_ec_i2c_xfer(struct cros_ec_dev *dev, uchar chip, uint addr,
int alen, uchar *buffer, int len, int is_read)
int cros_ec_i2c_tunnel(struct udevice *dev, struct i2c_msg *in, int nmsgs)
{
struct cros_ec_dev *cdev = dev_get_uclass_priv(dev);
union {
struct ec_params_i2c_passthru p;
uint8_t outbuf[EC_PROTO2_MAX_PARAM_SIZE];
@@ -1066,53 +1068,46 @@ int cros_ec_i2c_xfer(struct cros_ec_dev *dev, uchar chip, uint addr,
} response;
struct ec_params_i2c_passthru *p = &params.p;
struct ec_response_i2c_passthru *r = &response.r;
struct ec_params_i2c_passthru_msg *msg = p->msg;
uint8_t *pdata;
int read_len, write_len;
struct ec_params_i2c_passthru_msg *msg;
uint8_t *pdata, *read_ptr = NULL;
int read_len;
int size;
int rv;
int i;
p->port = 0;
if (alen != 1) {
printf("Unsupported address length %d\n", alen);
return -1;
}
if (is_read) {
read_len = len;
write_len = alen;
p->num_msgs = 2;
} else {
read_len = 0;
write_len = alen + len;
p->num_msgs = 1;
}
p->num_msgs = nmsgs;
size = sizeof(*p) + p->num_msgs * sizeof(*msg);
if (size + write_len > sizeof(params)) {
puts("Params too large for buffer\n");
return -1;
}
if (sizeof(*r) + read_len > sizeof(response)) {
puts("Read length too big for buffer\n");
return -1;
}
/* Create a message to write the register address and optional data */
pdata = (uint8_t *)p + size;
msg->addr_flags = chip;
msg->len = write_len;
pdata[0] = addr;
if (!is_read)
memcpy(pdata + 1, buffer, len);
msg++;
if (read_len) {
msg->addr_flags = chip | EC_I2C_FLAG_READ;
msg->len = read_len;
read_len = 0;
for (i = 0, msg = p->msg; i < nmsgs; i++, msg++, in++) {
bool is_read = in->flags & I2C_M_RD;
msg->addr_flags = in->addr;
msg->len = in->len;
if (is_read) {
msg->addr_flags |= EC_I2C_FLAG_READ;
read_len += in->len;
read_ptr = in->buf;
if (sizeof(*r) + read_len > sizeof(response)) {
puts("Read length too big for buffer\n");
return -1;
}
} else {
if (pdata - (uint8_t *)p + in->len > sizeof(params)) {
puts("Params too large for buffer\n");
return -1;
}
memcpy(pdata, in->buf, in->len);
pdata += in->len;
}
}
rv = ec_command(dev, EC_CMD_I2C_PASSTHRU, 0, p, size + write_len,
rv = ec_command(cdev, EC_CMD_I2C_PASSTHRU, 0, p, pdata - (uint8_t *)p,
r, sizeof(*r) + read_len);
if (rv < 0)
return rv;
@@ -1128,8 +1123,9 @@ int cros_ec_i2c_xfer(struct cros_ec_dev *dev, uchar chip, uint addr,
return -1;
}
/* We only support a single read message for each transfer */
if (read_len)
memcpy(buffer, r->data, read_len);
memcpy(read_ptr, r->data, read_len);
return 0;
}
@@ -1189,187 +1185,6 @@ static int do_read_write(struct cros_ec_dev *dev, int is_write, int argc,
return 0;
}
/**
* get_alen() - Small parser helper function to get address length
*
* Returns the address length.
*/
static uint get_alen(char *arg)
{
int j;
int alen;
alen = 1;
for (j = 0; j < 8; j++) {
if (arg[j] == '.') {
alen = arg[j+1] - '0';
break;
} else if (arg[j] == '\0') {
break;
}
}
return alen;
}
#define DISP_LINE_LEN 16
/*
* TODO(sjg@chromium.org): This code copied almost verbatim from cmd_i2c.c
* so we can remove it later.
*/
static int cros_ec_i2c_md(struct cros_ec_dev *dev, int flag, int argc,
char * const argv[])
{
u_char chip;
uint addr, alen, length = 0x10;
int j, nbytes, linebytes;
if (argc < 2)
return CMD_RET_USAGE;
if (1 || (flag & CMD_FLAG_REPEAT) == 0) {
/*
* New command specified.
*/
/*
* I2C chip address
*/
chip = simple_strtoul(argv[0], NULL, 16);
/*
* I2C data address within the chip. This can be 1 or
* 2 bytes long. Some day it might be 3 bytes long :-).
*/
addr = simple_strtoul(argv[1], NULL, 16);
alen = get_alen(argv[1]);
if (alen > 3)
return CMD_RET_USAGE;
/*
* If another parameter, it is the length to display.
* Length is the number of objects, not number of bytes.
*/
if (argc > 2)
length = simple_strtoul(argv[2], NULL, 16);
}
/*
* Print the lines.
*
* We buffer all read data, so we can make sure data is read only
* once.
*/
nbytes = length;
do {
unsigned char linebuf[DISP_LINE_LEN];
unsigned char *cp;
linebytes = (nbytes > DISP_LINE_LEN) ? DISP_LINE_LEN : nbytes;
if (cros_ec_i2c_xfer(dev, chip, addr, alen, linebuf, linebytes,
1))
puts("Error reading the chip.\n");
else {
printf("%04x:", addr);
cp = linebuf;
for (j = 0; j < linebytes; j++) {
printf(" %02x", *cp++);
addr++;
}
puts(" ");
cp = linebuf;
for (j = 0; j < linebytes; j++) {
if ((*cp < 0x20) || (*cp > 0x7e))
puts(".");
else
printf("%c", *cp);
cp++;
}
putc('\n');
}
nbytes -= linebytes;
} while (nbytes > 0);
return 0;
}
static int cros_ec_i2c_mw(struct cros_ec_dev *dev, int flag, int argc,
char * const argv[])
{
uchar chip;
ulong addr;
uint alen;
uchar byte;
int count;
if ((argc < 3) || (argc > 4))
return CMD_RET_USAGE;
/*
* Chip is always specified.
*/
chip = simple_strtoul(argv[0], NULL, 16);
/*
* Address is always specified.
*/
addr = simple_strtoul(argv[1], NULL, 16);
alen = get_alen(argv[1]);
if (alen > 3)
return CMD_RET_USAGE;
/*
* Value to write is always specified.
*/
byte = simple_strtoul(argv[2], NULL, 16);
/*
* Optional count
*/
if (argc == 4)
count = simple_strtoul(argv[3], NULL, 16);
else
count = 1;
while (count-- > 0) {
if (cros_ec_i2c_xfer(dev, chip, addr++, alen, &byte, 1, 0))
puts("Error writing the chip.\n");
/*
* Wait for the write to complete. The write can take
* up to 10mSec (we allow a little more time).
*/
/*
* No write delay with FRAM devices.
*/
#if !defined(CONFIG_SYS_I2C_FRAM)
udelay(11000);
#endif
}
return 0;
}
/* Temporary code until we have driver model and can use the i2c command */
static int cros_ec_i2c_passthrough(struct cros_ec_dev *dev, int flag,
int argc, char * const argv[])
{
const char *cmd;
if (argc < 1)
return CMD_RET_USAGE;
cmd = *argv++;
argc--;
if (0 == strcmp("md", cmd))
cros_ec_i2c_md(dev, flag, argc, argv);
else if (0 == strcmp("mw", cmd))
cros_ec_i2c_mw(dev, flag, argc, argv);
else
return CMD_RET_USAGE;
return 0;
}
static int do_cros_ec(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
{
struct cros_ec_dev *dev;
@@ -1605,9 +1420,9 @@ static int do_cros_ec(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
state = simple_strtoul(argv[3], &endp, 10);
if (*argv[3] == 0 || *endp != 0)
return CMD_RET_USAGE;
ret = cros_ec_set_ldo(dev, index, state);
ret = cros_ec_set_ldo(udev, index, state);
} else {
ret = cros_ec_get_ldo(dev, index, &state);
ret = cros_ec_get_ldo(udev, index, &state);
if (!ret) {
printf("LDO%d: %s\n", index,
state == EC_LDO_STATE_ON ?
@@ -1619,8 +1434,6 @@ static int do_cros_ec(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
debug("%s: Could not access LDO%d\n", __func__, index);
return ret;
}
} else if (0 == strcmp("i2c", cmd)) {
ret = cros_ec_i2c_passthrough(dev, flag, argc - 2, argv + 2);
} else {
return CMD_RET_USAGE;
}
@@ -1633,6 +1446,12 @@ static int do_cros_ec(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
return ret;
}
int cros_ec_post_bind(struct udevice *dev)
{
/* Scan for available EC devices (e.g. I2C tunnel) */
return dm_scan_fdt_node(dev, gd->fdt_blob, dev->of_offset, false);
}
U_BOOT_CMD(
crosec, 6, 1, do_cros_ec,
"CROS-EC utility command",
@@ -1651,9 +1470,7 @@ U_BOOT_CMD(
"crosec vbnvcontext [hexstring] Read [write] VbNvContext from EC\n"
"crosec ldo <idx> [<state>] Switch/Read LDO state\n"
"crosec test run tests on cros_ec\n"
"crosec version Read CROS-EC version\n"
"crosec i2c md chip address[.0, .1, .2] [# of objects] - read from I2C passthru\n"
"crosec i2c mw chip address[.0, .1, .2] value [count] - write to I2C passthru (fill)"
"crosec version Read CROS-EC version"
);
#endif
@@ -1661,4 +1478,5 @@ UCLASS_DRIVER(cros_ec) = {
.id = UCLASS_CROS_EC,
.name = "cros_ec",
.per_device_auto_alloc_size = sizeof(struct cros_ec_dev),
.post_bind = cros_ec_post_bind,
};

View File

@@ -202,6 +202,6 @@ int exynos_mmc_init(const void *blob)
process_nodes(blob, node_list, count);
return 1;
return 0;
}
#endif

View File

@@ -445,11 +445,11 @@ static int tegra_pcie_parse_dt_ranges(const void *fdt, int node,
}
debug("PCI regions:\n");
debug(" I/O: %#x-%#x\n", pcie->io.start, pcie->io.end);
debug(" non-prefetchable memory: %#x-%#x\n", pcie->mem.start,
pcie->mem.end);
debug(" prefetchable memory: %#x-%#x\n", pcie->prefetch.start,
pcie->prefetch.end);
debug(" I/O: %pa-%pa\n", &pcie->io.start, &pcie->io.end);
debug(" non-prefetchable memory: %pa-%pa\n", &pcie->mem.start,
&pcie->mem.end);
debug(" prefetchable memory: %pa-%pa\n", &pcie->prefetch.start,
&pcie->prefetch.end);
return 0;
}

View File

@@ -41,3 +41,21 @@ config DM_PMIC_SANDBOX
- set by i2c emul driver's probe() (defaults in header)
Driver binding info: doc/device-tree-bindings/pmic/sandbox.txt
config PMIC_S5M8767
bool "Enable Driver Model for the Samsung S5M8767 PMIC"
depends on DM_PMIC
---help---
The S5M8767 PMIC provides a large array of LDOs and BUCKs for use
as a SoC power controller. It also provides 32KHz clock outputs. This
driver provides basic register access and sets up the attached
regulators if regulator support is enabled.
config PMIC_TPS65090
bool "Enable driver for Texas Instruments TPS65090 PMIC"
depends on DM_PMIC
---help---
The TPS65090 is a PMIC containing several LDOs, DC to DC convertors,
FETs and a battery charger. This driver provides register access
only, and you can enable the regulator/charger drivers separately if
required.

View File

@@ -8,6 +8,9 @@
obj-$(CONFIG_DM_PMIC) += pmic-uclass.o
obj-$(CONFIG_DM_PMIC_MAX77686) += max77686.o
obj-$(CONFIG_DM_PMIC_SANDBOX) += sandbox.o i2c_pmic_emul.o
obj-$(CONFIG_PMIC_TPS65090) += tps65090.o
obj-$(CONFIG_PMIC_S5M8767) += s5m8767.o
obj-$(CONFIG_POWER_LTC3676) += pmic_ltc3676.o
obj-$(CONFIG_POWER_MAX77696) += pmic_max77696.o
obj-$(CONFIG_POWER_MAX8998) += pmic_max8998.o
@@ -15,8 +18,6 @@ obj-$(CONFIG_POWER_MAX8997) += pmic_max8997.o
obj-$(CONFIG_POWER_MUIC_MAX8997) += muic_max8997.o
obj-$(CONFIG_POWER_MAX77686) += pmic_max77686.o
obj-$(CONFIG_POWER_PFUZE100) += pmic_pfuze100.o
obj-$(CONFIG_POWER_TPS65090_I2C) += pmic_tps65090.o
obj-$(CONFIG_POWER_TPS65090_EC) += pmic_tps65090_ec.o
obj-$(CONFIG_POWER_TPS65217) += pmic_tps65217.o
obj-$(CONFIG_POWER_TPS65218) += pmic_tps62362.o
obj-$(CONFIG_POWER_TPS65218) += pmic_tps65218.o

View File

@@ -17,8 +17,8 @@
DECLARE_GLOBAL_DATA_PTR;
static const struct pmic_child_info pmic_children_info[] = {
{ .prefix = "ldo", .driver = MAX77686_LDO_DRIVER },
{ .prefix = "buck", .driver = MAX77686_BUCK_DRIVER },
{ .prefix = "LDO", .driver = MAX77686_LDO_DRIVER },
{ .prefix = "BUCK", .driver = MAX77686_BUCK_DRIVER },
{ },
};
@@ -84,7 +84,7 @@ static const struct udevice_id max77686_ids[] = {
};
U_BOOT_DRIVER(pmic_max77686) = {
.name = "max77686 pmic",
.name = "max77686_pmic",
.id = UCLASS_PMIC,
.of_match = max77686_ids,
.bind = max77686_bind,

View File

@@ -142,7 +142,7 @@ int pmic_reg_write(struct udevice *dev, uint reg, uint value)
u8 byte = value;
debug("%s: reg=%x, value=%x\n", __func__, reg, value);
return pmic_read(dev, reg, &byte, 1);
return pmic_write(dev, reg, &byte, 1);
}
int pmic_clrsetbits(struct udevice *dev, uint reg, uint clr, uint set)

View File

@@ -1,310 +0,0 @@
/*
* Copyright (c) 2012 The Chromium OS Authors.
*
* SPDX-License-Identifier: GPL-2.0+
*/
#include <common.h>
#include <errno.h>
#include <fdtdec.h>
#include <i2c.h>
#include <power/pmic.h>
#include <power/tps65090_pmic.h>
DECLARE_GLOBAL_DATA_PTR;
#define TPS65090_NAME "TPS65090_PMIC"
/* TPS65090 register addresses */
enum {
REG_IRQ1 = 0,
REG_CG_CTRL0 = 4,
REG_CG_STATUS1 = 0xa,
REG_FET1_CTRL = 0x0f,
REG_FET2_CTRL,
REG_FET3_CTRL,
REG_FET4_CTRL,
REG_FET5_CTRL,
REG_FET6_CTRL,
REG_FET7_CTRL,
TPS65090_NUM_REGS,
};
enum {
IRQ1_VBATG = 1 << 3,
CG_CTRL0_ENC_MASK = 0x01,
MAX_FET_NUM = 7,
MAX_CTRL_READ_TRIES = 5,
/* TPS65090 FET_CTRL register values */
FET_CTRL_TOFET = 1 << 7, /* Timeout, startup, overload */
FET_CTRL_PGFET = 1 << 4, /* Power good for FET status */
FET_CTRL_WAIT = 3 << 2, /* Overcurrent timeout max */
FET_CTRL_ADENFET = 1 << 1, /* Enable output auto discharge */
FET_CTRL_ENFET = 1 << 0, /* Enable FET */
};
/**
* Checks for a valid FET number
*
* @param fet_id FET number to check
* @return 0 if ok, -EINVAL if FET value is out of range
*/
static int tps65090_check_fet(unsigned int fet_id)
{
if (fet_id == 0 || fet_id > MAX_FET_NUM) {
debug("parameter fet_id is out of range, %u not in 1 ~ %u\n",
fet_id, MAX_FET_NUM);
return -EINVAL;
}
return 0;
}
/**
* Set the power state for a FET
*
* @param pmic pmic structure for the tps65090
* @param fet_id Fet number to set (1..MAX_FET_NUM)
* @param set 1 to power on FET, 0 to power off
* @return -EIO if we got a comms error, -EAGAIN if the FET failed to
* change state. If all is ok, returns 0.
*/
static int tps65090_fet_set(struct pmic *pmic, int fet_id, bool set)
{
int retry;
u32 reg, value;
value = FET_CTRL_ADENFET | FET_CTRL_WAIT;
if (set)
value |= FET_CTRL_ENFET;
if (pmic_reg_write(pmic, REG_FET1_CTRL + fet_id - 1, value))
return -EIO;
/* Try reading until we get a result */
for (retry = 0; retry < MAX_CTRL_READ_TRIES; retry++) {
if (pmic_reg_read(pmic, REG_FET1_CTRL + fet_id - 1, &reg))
return -EIO;
/* Check that the fet went into the expected state */
if (!!(reg & FET_CTRL_PGFET) == set)
return 0;
/* If we got a timeout, there is no point in waiting longer */
if (reg & FET_CTRL_TOFET)
break;
mdelay(1);
}
debug("FET %d: Power good should have set to %d but reg=%#02x\n",
fet_id, set, reg);
return -EAGAIN;
}
int tps65090_fet_enable(unsigned int fet_id)
{
struct pmic *pmic;
ulong start;
int loops;
int ret;
ret = tps65090_check_fet(fet_id);
if (ret)
return ret;
pmic = pmic_get(TPS65090_NAME);
if (!pmic)
return -EACCES;
start = get_timer(0);
for (loops = 0;; loops++) {
ret = tps65090_fet_set(pmic, fet_id, true);
if (!ret)
break;
if (get_timer(start) > 100)
break;
/* Turn it off and try again until we time out */
tps65090_fet_set(pmic, fet_id, false);
}
if (ret)
debug("%s: FET%d failed to power on: time=%lums, loops=%d\n",
__func__, fet_id, get_timer(start), loops);
else if (loops)
debug("%s: FET%d powered on after %lums, loops=%d\n",
__func__, fet_id, get_timer(start), loops);
/*
* Unfortunately, there are some conditions where the power
* good bit will be 0, but the fet still comes up. One such
* case occurs with the lcd backlight. We'll just return 0 here
* and assume that the fet will eventually come up.
*/
if (ret == -EAGAIN)
ret = 0;
return ret;
}
int tps65090_fet_disable(unsigned int fet_id)
{
struct pmic *pmic;
int ret;
ret = tps65090_check_fet(fet_id);
if (ret)
return ret;
pmic = pmic_get(TPS65090_NAME);
if (!pmic)
return -EACCES;
ret = tps65090_fet_set(pmic, fet_id, false);
return ret;
}
int tps65090_fet_is_enabled(unsigned int fet_id)
{
struct pmic *pmic;
u32 reg;
int ret;
ret = tps65090_check_fet(fet_id);
if (ret)
return ret;
pmic = pmic_get(TPS65090_NAME);
if (!pmic)
return -ENODEV;
ret = pmic_reg_read(pmic, REG_FET1_CTRL + fet_id - 1, &reg);
if (ret) {
debug("fail to read FET%u_CTRL register over I2C", fet_id);
return -EIO;
}
return reg & FET_CTRL_ENFET;
}
int tps65090_get_charging(void)
{
struct pmic *pmic;
u32 val;
int ret;
pmic = pmic_get(TPS65090_NAME);
if (!pmic)
return -EACCES;
ret = pmic_reg_read(pmic, REG_CG_CTRL0, &val);
if (ret)
return ret;
return !!(val & CG_CTRL0_ENC_MASK);
}
static int tps65090_charger_state(struct pmic *pmic, int state,
int current)
{
u32 val;
int ret;
ret = pmic_reg_read(pmic, REG_CG_CTRL0, &val);
if (!ret) {
if (state == PMIC_CHARGER_ENABLE)
val |= CG_CTRL0_ENC_MASK;
else
val &= ~CG_CTRL0_ENC_MASK;
ret = pmic_reg_write(pmic, REG_CG_CTRL0, val);
}
if (ret) {
debug("%s: Failed to read/write register\n", __func__);
return ret;
}
return 0;
}
int tps65090_get_status(void)
{
struct pmic *pmic;
u32 val;
int ret;
pmic = pmic_get(TPS65090_NAME);
if (!pmic)
return -EACCES;
ret = pmic_reg_read(pmic, REG_CG_STATUS1, &val);
if (ret)
return ret;
return val;
}
static int tps65090_charger_bat_present(struct pmic *pmic)
{
u32 val;
int ret;
ret = pmic_reg_read(pmic, REG_IRQ1, &val);
if (ret)
return ret;
return !!(val & IRQ1_VBATG);
}
static struct power_chrg power_chrg_pmic_ops = {
.chrg_bat_present = tps65090_charger_bat_present,
.chrg_state = tps65090_charger_state,
};
int tps65090_init(void)
{
struct pmic *p;
int bus;
int addr;
const void *blob = gd->fdt_blob;
int node, parent;
node = fdtdec_next_compatible(blob, 0, COMPAT_TI_TPS65090);
if (node < 0) {
debug("PMIC: No node for PMIC Chip in device tree\n");
debug("node = %d\n", node);
return -ENODEV;
}
parent = fdt_parent_offset(blob, node);
if (parent < 0) {
debug("%s: Cannot find node parent\n", __func__);
return -EINVAL;
}
bus = i2c_get_bus_num_fdt(parent);
if (bus < 0) {
debug("%s: Cannot find I2C bus\n", __func__);
return -ENOENT;
}
addr = fdtdec_get_int(blob, node, "reg", TPS65090_I2C_ADDR);
p = pmic_alloc();
if (!p) {
printf("%s: POWER allocation error!\n", __func__);
return -ENOMEM;
}
p->name = TPS65090_NAME;
p->bus = bus;
p->interface = PMIC_I2C;
p->number_of_regs = TPS65090_NUM_REGS;
p->hw.i2c.addr = addr;
p->hw.i2c.tx_num = 1;
p->chrg = &power_chrg_pmic_ops;
puts("TPS65090 PMIC init\n");
return 0;
}

View File

@@ -1,218 +0,0 @@
/*
* Copyright (c) 2013 The Chromium OS Authors.
*
* SPDX-License-Identifier: GPL-2.0+
*/
#include <common.h>
#include <cros_ec.h>
#include <errno.h>
#include <power/tps65090_pmic.h>
DECLARE_GLOBAL_DATA_PTR;
#define TPS65090_ADDR 0x48
static struct tps65090 {
struct cros_ec_dev *dev; /* The CROS_EC device */
} config;
/* TPS65090 register addresses */
enum {
REG_IRQ1 = 0,
REG_CG_CTRL0 = 4,
REG_CG_STATUS1 = 0xa,
REG_FET1_CTRL = 0x0f,
REG_FET2_CTRL,
REG_FET3_CTRL,
REG_FET4_CTRL,
REG_FET5_CTRL,
REG_FET6_CTRL,
REG_FET7_CTRL,
TPS65090_NUM_REGS,
};
enum {
IRQ1_VBATG = 1 << 3,
CG_CTRL0_ENC_MASK = 0x01,
MAX_FET_NUM = 7,
MAX_CTRL_READ_TRIES = 5,
/* TPS65090 FET_CTRL register values */
FET_CTRL_TOFET = 1 << 7, /* Timeout, startup, overload */
FET_CTRL_PGFET = 1 << 4, /* Power good for FET status */
FET_CTRL_WAIT = 3 << 2, /* Overcurrent timeout max */
FET_CTRL_ADENFET = 1 << 1, /* Enable output auto discharge */
FET_CTRL_ENFET = 1 << 0, /* Enable FET */
};
/**
* tps65090_read - read a byte from tps6090
*
* @param reg The register address to read from.
* @param val We'll return value value read here.
* @return 0 if ok; error if EC returns failure.
*/
static int tps65090_read(u32 reg, u8 *val)
{
return cros_ec_i2c_xfer(config.dev, TPS65090_ADDR, reg, 1,
val, 1, true);
}
/**
* tps65090_write - write a byte to tps6090
*
* @param reg The register address to write to.
* @param val The value to write.
* @return 0 if ok; error if EC returns failure.
*/
static int tps65090_write(u32 reg, u8 val)
{
return cros_ec_i2c_xfer(config.dev, TPS65090_ADDR, reg, 1,
&val, 1, false);
}
/**
* Checks for a valid FET number
*
* @param fet_id FET number to check
* @return 0 if ok, -EINVAL if FET value is out of range
*/
static int tps65090_check_fet(unsigned int fet_id)
{
if (fet_id == 0 || fet_id > MAX_FET_NUM) {
debug("parameter fet_id is out of range, %u not in 1 ~ %u\n",
fet_id, MAX_FET_NUM);
return -EINVAL;
}
return 0;
}
/**
* Set the power state for a FET
*
* @param fet_id Fet number to set (1..MAX_FET_NUM)
* @param set 1 to power on FET, 0 to power off
* @return -EIO if we got a comms error, -EAGAIN if the FET failed to
* change state. If all is ok, returns 0.
*/
static int tps65090_fet_set(int fet_id, bool set)
{
int retry;
u8 reg, value;
value = FET_CTRL_ADENFET | FET_CTRL_WAIT;
if (set)
value |= FET_CTRL_ENFET;
if (tps65090_write(REG_FET1_CTRL + fet_id - 1, value))
return -EIO;
/* Try reading until we get a result */
for (retry = 0; retry < MAX_CTRL_READ_TRIES; retry++) {
if (tps65090_read(REG_FET1_CTRL + fet_id - 1, &reg))
return -EIO;
/* Check that the fet went into the expected state */
if (!!(reg & FET_CTRL_PGFET) == set)
return 0;
/* If we got a timeout, there is no point in waiting longer */
if (reg & FET_CTRL_TOFET)
break;
mdelay(1);
}
debug("FET %d: Power good should have set to %d but reg=%#02x\n",
fet_id, set, reg);
return -EAGAIN;
}
int tps65090_fet_enable(unsigned int fet_id)
{
ulong start;
int loops;
int ret;
ret = tps65090_check_fet(fet_id);
if (ret)
return ret;
start = get_timer(0);
for (loops = 0;; loops++) {
ret = tps65090_fet_set(fet_id, true);
if (!ret)
break;
if (get_timer(start) > 100)
break;
/* Turn it off and try again until we time out */
tps65090_fet_set(fet_id, false);
}
if (ret) {
debug("%s: FET%d failed to power on: time=%lums, loops=%d\n",
__func__, fet_id, get_timer(start), loops);
} else if (loops) {
debug("%s: FET%d powered on after %lums, loops=%d\n",
__func__, fet_id, get_timer(start), loops);
}
/*
* Unfortunately, there are some conditions where the power
* good bit will be 0, but the fet still comes up. One such
* case occurs with the lcd backlight. We'll just return 0 here
* and assume that the fet will eventually come up.
*/
if (ret == -EAGAIN)
ret = 0;
return ret;
}
int tps65090_fet_disable(unsigned int fet_id)
{
int ret;
ret = tps65090_check_fet(fet_id);
if (ret)
return ret;
ret = tps65090_fet_set(fet_id, false);
return ret;
}
int tps65090_fet_is_enabled(unsigned int fet_id)
{
u8 reg = 0;
int ret;
ret = tps65090_check_fet(fet_id);
if (ret)
return ret;
ret = tps65090_read(REG_FET1_CTRL + fet_id - 1, &reg);
if (ret) {
debug("fail to read FET%u_CTRL register over I2C", fet_id);
return -EIO;
}
return reg & FET_CTRL_ENFET;
}
int tps65090_init(void)
{
puts("TPS65090 PMIC EC init\n");
config.dev = board_get_cros_ec_dev();
if (!config.dev) {
debug("%s: no cros_ec device: cannot init tps65090\n",
__func__);
return -ENODEV;
}
return 0;
}

View File

@@ -0,0 +1,95 @@
/*
* Copyright (C) 2015 Google, Inc
*
* SPDX-License-Identifier: GPL-2.0+
*/
#include <common.h>
#include <fdtdec.h>
#include <errno.h>
#include <dm.h>
#include <i2c.h>
#include <power/pmic.h>
#include <power/regulator.h>
#include <power/s5m8767.h>
DECLARE_GLOBAL_DATA_PTR;
static const struct pmic_child_info pmic_children_info[] = {
{ .prefix = "LDO", .driver = S5M8767_LDO_DRIVER },
{ .prefix = "BUCK", .driver = S5M8767_BUCK_DRIVER },
{ },
};
static int s5m8767_reg_count(struct udevice *dev)
{
return S5M8767_NUM_OF_REGS;
}
static int s5m8767_write(struct udevice *dev, uint reg, const uint8_t *buff,
int len)
{
if (dm_i2c_write(dev, reg, buff, len)) {
error("write error to device: %p register: %#x!", dev, reg);
return -EIO;
}
return 0;
}
static int s5m8767_read(struct udevice *dev, uint reg, uint8_t *buff, int len)
{
if (dm_i2c_read(dev, reg, buff, len)) {
error("read error from device: %p register: %#x!", dev, reg);
return -EIO;
}
return 0;
}
int s5m8767_enable_32khz_cp(struct udevice *dev)
{
return pmic_clrsetbits(dev, S5M8767_EN32KHZ_CP, 0, 1 << 1);
}
static int s5m8767_bind(struct udevice *dev)
{
int node;
const void *blob = gd->fdt_blob;
int children;
node = fdt_subnode_offset(blob, dev->of_offset, "regulators");
if (node <= 0) {
debug("%s: %s regulators subnode not found!", __func__,
dev->name);
return -ENXIO;
}
debug("%s: '%s' - found regulators subnode\n", __func__, dev->name);
children = pmic_bind_children(dev, node, pmic_children_info);
if (!children)
debug("%s: %s - no child found\n", __func__, dev->name);
/* Always return success for this device */
return 0;
}
static struct dm_pmic_ops s5m8767_ops = {
.reg_count = s5m8767_reg_count,
.read = s5m8767_read,
.write = s5m8767_write,
};
static const struct udevice_id s5m8767_ids[] = {
{ .compatible = "samsung,s5m8767-pmic" },
{ }
};
U_BOOT_DRIVER(pmic_s5m8767) = {
.name = "s5m8767_pmic",
.id = UCLASS_PMIC,
.of_match = s5m8767_ids,
.bind = s5m8767_bind,
.ops = &s5m8767_ops,
};

View File

@@ -0,0 +1,94 @@
/*
* Copyright (c) 2015 Google, Inc
* Written by Simon Glass <sjg@chromium.org>
*
* SPDX-License-Identifier: GPL-2.0+
*/
#include <common.h>
#include <dm.h>
#include <errno.h>
#include <fdtdec.h>
#include <i2c.h>
#include <power/pmic.h>
#include <power/tps65090.h>
DECLARE_GLOBAL_DATA_PTR;
static const struct pmic_child_info pmic_children_info[] = {
{ .prefix = "fet", .driver = TPS65090_FET_DRIVER },
{ },
};
static int tps65090_reg_count(struct udevice *dev)
{
return TPS65090_NUM_REGS;
}
static int tps65090_write(struct udevice *dev, uint reg, const uint8_t *buff,
int len)
{
if (dm_i2c_write(dev, reg, buff, len)) {
error("write error to device: %p register: %#x!", dev, reg);
return -EIO;
}
return 0;
}
static int tps65090_read(struct udevice *dev, uint reg, uint8_t *buff, int len)
{
int ret;
ret = dm_i2c_read(dev, reg, buff, len);
if (ret) {
error("read error %d from device: %p register: %#x!", ret, dev,
reg);
return -EIO;
}
return 0;
}
static int tps65090_bind(struct udevice *dev)
{
int regulators_node;
const void *blob = gd->fdt_blob;
int children;
regulators_node = fdt_subnode_offset(blob, dev->of_offset,
"regulators");
if (regulators_node <= 0) {
debug("%s: %s regulators subnode not found!", __func__,
dev->name);
return -ENXIO;
}
debug("%s: '%s' - found regulators subnode\n", __func__, dev->name);
children = pmic_bind_children(dev, regulators_node, pmic_children_info);
if (!children)
debug("%s: %s - no child found\n", __func__, dev->name);
/* Always return success for this device */
return 0;
}
static struct dm_pmic_ops tps65090_ops = {
.reg_count = tps65090_reg_count,
.read = tps65090_read,
.write = tps65090_write,
};
static const struct udevice_id tps65090_ids[] = {
{ .compatible = "ti,tps65090" },
{ }
};
U_BOOT_DRIVER(pmic_tps65090) = {
.name = "tps65090 pmic",
.id = UCLASS_PMIC,
.of_match = tps65090_ids,
.bind = tps65090_bind,
.ops = &tps65090_ops,
};

View File

@@ -32,6 +32,15 @@ config DM_REGULATOR_FIXED
features for fixed value regulators. The driver implements get/set api
for enable and get only for voltage value.
config REGULATOR_S5M8767
bool "Enable support for S5M8767 regulator"
depends on DM_REGULATOR && PMIC_S5M8767
---help---
This enables the regulator features of the S5M8767, allowing voltages
to be set, etc. The driver is not fully complete but supports most
common requirements, including all LDOs and BUCKs. This allows many
supplies to be set automatically using the device tree values.
config DM_REGULATOR_SANDBOX
bool "Enable Driver Model for Sandbox PMIC regulator"
depends on DM_REGULATOR && DM_PMIC_SANDBOX
@@ -61,3 +70,13 @@ config DM_REGULATOR_SANDBOX
A detailed information can be found in header: '<power/sandbox_pmic.h>'
Binding info: 'doc/device-tree-bindings/pmic/max77686.txt'
config REGULATOR_TPS65090
bool "Enable driver for TPS65090 PMIC regulators"
depends on PMIC_TPS65090
---help---
The TPS65090 provides several FETs (Field-effect Transistors,
effectively switches) which are supported by this driver as
regulators, one for each FET. The standard regulator interface is
supported, but it is only possible to turn the regulators on or off.
There is no voltage/current control.

View File

@@ -8,4 +8,6 @@
obj-$(CONFIG_DM_REGULATOR) += regulator-uclass.o
obj-$(CONFIG_DM_REGULATOR_MAX77686) += max77686.o
obj-$(CONFIG_DM_REGULATOR_FIXED) += fixed.o
obj-$(CONFIG_REGULATOR_S5M8767) += s5m8767.o
obj-$(CONFIG_DM_REGULATOR_SANDBOX) += sandbox.o
obj-$(CONFIG_REGULATOR_TPS65090) += tps65090_regulator.o

View File

@@ -61,10 +61,14 @@ static struct dm_regulator_mode max77686_buck_mode_onoff[] = {
MODE(OPMODE_ON, MAX77686_BUCK_MODE_ON, "ON"),
};
static const char max77686_buck_addr[] = {
static const char max77686_buck_ctrl[] = {
0xff, 0x10, 0x12, 0x1c, 0x26, 0x30, 0x32, 0x34, 0x36, 0x38
};
static const char max77686_buck_out[] = {
0xff, 0x11, 0x14, 0x1e, 0x28, 0x31, 0x33, 0x35, 0x37, 0x39
};
static int max77686_buck_volt2hex(int buck, int uV)
{
unsigned int hex = 0;
@@ -77,13 +81,15 @@ static int max77686_buck_volt2hex(int buck, int uV)
/* hex = (uV - 600000) / 12500; */
hex = (uV - MAX77686_BUCK_UV_LMIN) / MAX77686_BUCK_UV_LSTEP;
hex_max = MAX77686_BUCK234_VOLT_MAX_HEX;
/**
* Those use voltage scaller - temporary not implemented
* so return just 0
*/
return -ENOSYS;
break;
default:
/* hex = (uV - 750000) / 50000; */
/*
* hex = (uV - 750000) / 50000. We assume that dynamic voltage
* scaling via GPIOs is not enabled and don't support that.
* If this is enabled then the driver will need to take that
* into account anrd check different registers depending on
* the current setting See the datasheet for details.
*/
hex = (uV - MAX77686_BUCK_UV_HMIN) / MAX77686_BUCK_UV_HSTEP;
hex_max = MAX77686_BUCK_VOLT_MAX_HEX;
break;
@@ -368,18 +374,18 @@ static int max77686_buck_val(struct udevice *dev, int op, int *uV)
*uV = 0;
/* &buck_out = ctrl + 1 */
adr = max77686_buck_addr[buck] + 1;
adr = max77686_buck_out[buck];
/* mask */
switch (buck) {
case 2:
case 3:
case 4:
/* Those use voltage scallers - will support in the future */
mask = MAX77686_BUCK234_VOLT_MASK;
return -ENOSYS;
break;
default:
mask = MAX77686_BUCK_VOLT_MASK;
break;
}
ret = pmic_read(dev->parent, adr, &val, 1);
@@ -549,7 +555,7 @@ static int max77686_buck_mode(struct udevice *dev, int op, int *opmode)
return -EINVAL;
}
adr = max77686_buck_addr[buck];
adr = max77686_buck_ctrl[buck];
/* mask */
switch (buck) {

View File

@@ -319,8 +319,10 @@ int regulators_enable_boot_on(bool verbose)
dev && !ret;
uclass_next_device(&dev)) {
ret = regulator_autoset(dev);
if (ret == -EMEDIUMTYPE)
if (ret == -EMEDIUMTYPE) {
ret = 0;
continue;
}
if (verbose)
regulator_show(dev, ret);
}

View File

@@ -0,0 +1,269 @@
/*
* Copyright (C) 2015 Google, Inc
*
* SPDX-License-Identifier: GPL-2.0+
*/
#include <common.h>
#include <fdtdec.h>
#include <errno.h>
#include <dm.h>
#include <i2c.h>
#include <power/pmic.h>
#include <power/regulator.h>
#include <power/s5m8767.h>
DECLARE_GLOBAL_DATA_PTR;
static const struct sec_voltage_desc buck_v1 = {
.max = 2225000,
.min = 650000,
.step = 6250,
};
static const struct sec_voltage_desc buck_v2 = {
.max = 1600000,
.min = 600000,
.step = 6250,
};
static const struct sec_voltage_desc buck_v3 = {
.max = 3000000,
.min = 750000,
.step = 12500,
};
static const struct sec_voltage_desc ldo_v1 = {
.max = 3950000,
.min = 800000,
.step = 50000,
};
static const struct sec_voltage_desc ldo_v2 = {
.max = 2375000,
.min = 800000,
.step = 25000,
};
static const struct s5m8767_para buck_param[] = {
/*
* | voltage ----| | enable -| voltage
* regnum addr bpos mask addr on desc
*/
{S5M8767_BUCK1, 0x33, 0x0, 0xff, 0x32, 0x3, &buck_v1},
{S5M8767_BUCK2, 0x35, 0x0, 0xff, 0x34, 0x1, &buck_v2},
{S5M8767_BUCK3, 0x3e, 0x0, 0xff, 0x3d, 0x1, &buck_v2},
{S5M8767_BUCK4, 0x47, 0x0, 0xff, 0x46, 0x1, &buck_v2},
{S5M8767_BUCK5, 0x50, 0x0, 0xff, 0x4f, 0x3, &buck_v1},
{S5M8767_BUCK6, 0x55, 0x0, 0xff, 0x54, 0x3, &buck_v1},
{S5M8767_BUCK7, 0x57, 0x0, 0xff, 0x56, 0x3, &buck_v3},
{S5M8767_BUCK8, 0x59, 0x0, 0xff, 0x58, 0x3, &buck_v3},
{S5M8767_BUCK9, 0x5b, 0x0, 0xff, 0x5a, 0x3, &buck_v3},
};
static const struct s5m8767_para ldo_param[] = {
{S5M8767_LDO1, 0x5c, 0x0, 0x3f, 0x5c, 0x3, &ldo_v2},
{S5M8767_LDO2, 0x5d, 0x0, 0x3f, 0x5d, 0x1, &ldo_v2},
{S5M8767_LDO3, 0x61, 0x0, 0x3f, 0x61, 0x3, &ldo_v1},
{S5M8767_LDO4, 0x62, 0x0, 0x3f, 0x62, 0x3, &ldo_v1},
{S5M8767_LDO5, 0x63, 0x0, 0x3f, 0x63, 0x3, &ldo_v1},
{S5M8767_LDO6, 0x64, 0x0, 0x3f, 0x64, 0x1, &ldo_v2},
{S5M8767_LDO7, 0x65, 0x0, 0x3f, 0x65, 0x1, &ldo_v2},
{S5M8767_LDO8, 0x66, 0x0, 0x3f, 0x66, 0x1, &ldo_v2},
{S5M8767_LDO9, 0x67, 0x0, 0x3f, 0x67, 0x3, &ldo_v1},
{S5M8767_LDO10, 0x68, 0x0, 0x3f, 0x68, 0x1, &ldo_v1},
{S5M8767_LDO11, 0x69, 0x0, 0x3f, 0x69, 0x1, &ldo_v1},
{S5M8767_LDO12, 0x6a, 0x0, 0x3f, 0x6a, 0x1, &ldo_v1},
{S5M8767_LDO13, 0x6b, 0x0, 0x3f, 0x6b, 0x3, &ldo_v1},
{S5M8767_LDO14, 0x6c, 0x0, 0x3f, 0x6c, 0x1, &ldo_v1},
{S5M8767_LDO15, 0x6d, 0x0, 0x3f, 0x6d, 0x1, &ldo_v2},
{S5M8767_LDO16, 0x6e, 0x0, 0x3f, 0x6e, 0x1, &ldo_v1},
{S5M8767_LDO17, 0x6f, 0x0, 0x3f, 0x6f, 0x3, &ldo_v1},
{S5M8767_LDO18, 0x70, 0x0, 0x3f, 0x70, 0x3, &ldo_v1},
{S5M8767_LDO19, 0x71, 0x0, 0x3f, 0x71, 0x3, &ldo_v1},
{S5M8767_LDO20, 0x72, 0x0, 0x3f, 0x72, 0x3, &ldo_v1},
{S5M8767_LDO21, 0x73, 0x0, 0x3f, 0x73, 0x3, &ldo_v1},
{S5M8767_LDO22, 0x74, 0x0, 0x3f, 0x74, 0x3, &ldo_v1},
{S5M8767_LDO23, 0x75, 0x0, 0x3f, 0x75, 0x3, &ldo_v1},
{S5M8767_LDO24, 0x76, 0x0, 0x3f, 0x76, 0x3, &ldo_v1},
{S5M8767_LDO25, 0x77, 0x0, 0x3f, 0x77, 0x3, &ldo_v1},
{S5M8767_LDO26, 0x78, 0x0, 0x3f, 0x78, 0x3, &ldo_v1},
{S5M8767_LDO27, 0x79, 0x0, 0x3f, 0x79, 0x3, &ldo_v1},
{S5M8767_LDO28, 0x7a, 0x0, 0x3f, 0x7a, 0x3, &ldo_v1},
};
enum {
ENABLE_SHIFT = 6,
ENABLE_MASK = 3,
};
static int reg_get_value(struct udevice *dev, const struct s5m8767_para *param)
{
const struct sec_voltage_desc *desc;
int ret, uv, val;
ret = pmic_reg_read(dev->parent, param->vol_addr);
if (ret < 0)
return ret;
desc = param->vol;
val = (ret >> param->vol_bitpos) & param->vol_bitmask;
uv = desc->min + val * desc->step;
return uv;
}
static int reg_set_value(struct udevice *dev, const struct s5m8767_para *param,
int uv)
{
const struct sec_voltage_desc *desc;
int ret, val;
desc = param->vol;
if (uv < desc->min || uv > desc->max)
return -EINVAL;
val = (uv - desc->min) / desc->step;
val = (val & param->vol_bitmask) << param->vol_bitpos;
ret = pmic_clrsetbits(dev->parent, param->vol_addr,
param->vol_bitmask << param->vol_bitpos,
val);
return ret;
}
static int s5m8767_ldo_probe(struct udevice *dev)
{
struct dm_regulator_uclass_platdata *uc_pdata;
uc_pdata = dev_get_uclass_platdata(dev);
uc_pdata->type = REGULATOR_TYPE_LDO;
uc_pdata->mode_count = 0;
return 0;
}
static int ldo_get_value(struct udevice *dev)
{
int ldo = dev->driver_data;
return reg_get_value(dev, &ldo_param[ldo]);
}
static int ldo_set_value(struct udevice *dev, int uv)
{
int ldo = dev->driver_data;
return reg_set_value(dev, &ldo_param[ldo], uv);
}
static int reg_get_enable(struct udevice *dev, const struct s5m8767_para *param)
{
bool enable;
int ret;
ret = pmic_reg_read(dev->parent, param->reg_enaddr);
if (ret < 0)
return ret;
enable = (ret >> ENABLE_SHIFT) & ENABLE_MASK;
return enable;
}
static int reg_set_enable(struct udevice *dev, const struct s5m8767_para *param,
bool enable)
{
int ret;
ret = pmic_reg_read(dev->parent, param->reg_enaddr);
if (ret < 0)
return ret;
ret = pmic_clrsetbits(dev->parent, param->reg_enaddr,
ENABLE_MASK << ENABLE_SHIFT,
enable ? param->reg_enbiton << ENABLE_SHIFT : 0);
return ret;
}
static bool ldo_get_enable(struct udevice *dev)
{
int ldo = dev->driver_data;
return reg_get_enable(dev, &ldo_param[ldo]);
}
static int ldo_set_enable(struct udevice *dev, bool enable)
{
int ldo = dev->driver_data;
return reg_set_enable(dev, &ldo_param[ldo], enable);
}
static int s5m8767_buck_probe(struct udevice *dev)
{
struct dm_regulator_uclass_platdata *uc_pdata;
uc_pdata = dev_get_uclass_platdata(dev);
uc_pdata->type = REGULATOR_TYPE_BUCK;
uc_pdata->mode_count = 0;
return 0;
}
static int buck_get_value(struct udevice *dev)
{
int buck = dev->driver_data;
return reg_get_value(dev, &buck_param[buck]);
}
static int buck_set_value(struct udevice *dev, int uv)
{
int buck = dev->driver_data;
return reg_set_value(dev, &buck_param[buck], uv);
}
static bool buck_get_enable(struct udevice *dev)
{
int buck = dev->driver_data;
return reg_get_enable(dev, &buck_param[buck]);
}
static int buck_set_enable(struct udevice *dev, bool enable)
{
int buck = dev->driver_data;
return reg_set_enable(dev, &buck_param[buck], enable);
}
static const struct dm_regulator_ops s5m8767_ldo_ops = {
.get_value = ldo_get_value,
.set_value = ldo_set_value,
.get_enable = ldo_get_enable,
.set_enable = ldo_set_enable,
};
U_BOOT_DRIVER(s5m8767_ldo) = {
.name = S5M8767_LDO_DRIVER,
.id = UCLASS_REGULATOR,
.ops = &s5m8767_ldo_ops,
.probe = s5m8767_ldo_probe,
};
static const struct dm_regulator_ops s5m8767_buck_ops = {
.get_value = buck_get_value,
.set_value = buck_set_value,
.get_enable = buck_get_enable,
.set_enable = buck_set_enable,
};
U_BOOT_DRIVER(s5m8767_buck) = {
.name = S5M8767_BUCK_DRIVER,
.id = UCLASS_REGULATOR,
.ops = &s5m8767_buck_ops,
.probe = s5m8767_buck_probe,
};

View File

@@ -0,0 +1,138 @@
/*
* Copyright (c) 2015 Google, Inc
*
* SPDX-License-Identifier: GPL-2.0+
*/
#include <common.h>
#include <dm.h>
#include <errno.h>
#include <power/pmic.h>
#include <power/regulator.h>
#include <power/tps65090.h>
static int tps65090_fet_probe(struct udevice *dev)
{
struct dm_regulator_uclass_platdata *uc_pdata;
uc_pdata = dev_get_uclass_platdata(dev);
uc_pdata->type = REGULATOR_TYPE_OTHER;
uc_pdata->mode_count = 0;
return 0;
}
static bool tps65090_fet_get_enable(struct udevice *dev)
{
struct udevice *pmic = dev_get_parent(dev);
int ret, fet_id;
fet_id = dev->driver_data;
debug("%s: fet_id=%d\n", __func__, fet_id);
ret = pmic_reg_read(pmic, REG_FET_BASE + fet_id);
if (ret < 0)
return ret;
return ret & FET_CTRL_ENFET;
}
/**
* Set the power state for a FET
*
* @param pmic pmic structure for the tps65090
* @param fet_id FET number to set (1..MAX_FET_NUM)
* @param set 1 to power on FET, 0 to power off
* @return -EIO if we got a comms error, -EAGAIN if the FET failed to
* change state. If all is ok, returns 0.
*/
static int tps65090_fet_set(struct udevice *pmic, int fet_id, bool set)
{
int retry;
u32 value;
int ret;
value = FET_CTRL_ADENFET | FET_CTRL_WAIT;
if (set)
value |= FET_CTRL_ENFET;
if (pmic_reg_write(pmic, REG_FET_BASE + fet_id, value))
return -EIO;
/* Try reading until we get a result */
for (retry = 0; retry < MAX_CTRL_READ_TRIES; retry++) {
ret = pmic_reg_read(pmic, REG_FET_BASE + fet_id);
if (ret < 0)
return ret;
/* Check that the FET went into the expected state */
debug("%s: flags=%x\n", __func__, ret);
if (!!(ret & FET_CTRL_PGFET) == set)
return 0;
/* If we got a timeout, there is no point in waiting longer */
if (ret & FET_CTRL_TOFET)
break;
mdelay(1);
}
debug("FET %d: Power good should have set to %d but reg=%#02x\n",
fet_id, set, ret);
return -EAGAIN;
}
static int tps65090_fet_set_enable(struct udevice *dev, bool enable)
{
struct udevice *pmic = dev_get_parent(dev);
int ret, fet_id;
ulong start;
int loops;
fet_id = dev->driver_data;
debug("%s: fet_id=%d, enable=%d\n", __func__, fet_id, enable);
start = get_timer(0);
for (loops = 0;; loops++) {
ret = tps65090_fet_set(pmic, fet_id, enable);
if (!ret)
break;
if (get_timer(start) > 100)
break;
/* Turn it off and try again until we time out */
tps65090_fet_set(pmic, fet_id, false);
}
if (ret)
debug("%s: FET%d failed to power on: time=%lums, loops=%d\n",
__func__, fet_id, get_timer(start), loops);
else if (loops)
debug("%s: FET%d powered on after %lums, loops=%d\n",
__func__, fet_id, get_timer(start), loops);
/*
* Unfortunately there are some conditions where the power-good bit
* will be 0, but the FET still comes up. One such case occurs with
* the LCD backlight on snow. We'll just return 0 here and assume
* that the FET will eventually come up.
*/
if (ret == -EAGAIN)
ret = 0;
return ret;
}
static const struct dm_regulator_ops tps65090_fet_ops = {
.get_enable = tps65090_fet_get_enable,
.set_enable = tps65090_fet_set_enable,
};
U_BOOT_DRIVER(tps65090_fet) = {
.name = TPS65090_FET_DRIVER,
.id = UCLASS_REGULATOR,
.ops = &tps65090_fet_ops,
.probe = tps65090_fet_probe,
};

View File

@@ -53,6 +53,13 @@ config DEBUG_EFI_CONSOLE
U-Boot when running on top of EFI (Extensive Firmware Interface).
This is a type of BIOS used by PCs.
config DEBUG_UART_S5P
bool "Samsung S5P"
help
Select this to enable a debug UART using the serial_s5p driver. You
will need to provide parameters to make this work. The driver will
be available until the real driver-model serial is running.
endchoice
config DEBUG_UART_BASE

View File

@@ -14,8 +14,8 @@
#include <fdtdec.h>
#include <linux/compiler.h>
#include <asm/io.h>
#include <asm/arch/uart.h>
#include <asm/arch/clk.h>
#include <asm/arch/uart.h>
#include <serial.h>
DECLARE_GLOBAL_DATA_PTR;
@@ -59,11 +59,20 @@ static const int udivslot[] = {
0xffdf,
};
int s5p_serial_setbrg(struct udevice *dev, int baudrate)
static void __maybe_unused s5p_serial_init(struct s5p_uart *uart)
{
/* enable FIFOs, auto clear Rx FIFO */
writel(0x3, &uart->ufcon);
writel(0, &uart->umcon);
/* 8N1 */
writel(0x3, &uart->ulcon);
/* No interrupts, no DMA, pure polling */
writel(0x245, &uart->ucon);
}
static void __maybe_unused s5p_serial_baud(struct s5p_uart *uart, uint uclk,
int baudrate)
{
struct s5p_serial_platdata *plat = dev->platdata;
struct s5p_uart *const uart = plat->reg;
u32 uclk = get_uart_clk(plat->port_id);
u32 val;
val = uclk / baudrate;
@@ -74,6 +83,16 @@ int s5p_serial_setbrg(struct udevice *dev, int baudrate)
writew(udivslot[val % 16], &uart->rest.slot);
else
writeb(val % 16, &uart->rest.value);
}
#ifndef CONFIG_SPL_BUILD
int s5p_serial_setbrg(struct udevice *dev, int baudrate)
{
struct s5p_serial_platdata *plat = dev->platdata;
struct s5p_uart *const uart = plat->reg;
u32 uclk = get_uart_clk(plat->port_id);
s5p_serial_baud(uart, uclk, baudrate);
return 0;
}
@@ -83,13 +102,7 @@ static int s5p_serial_probe(struct udevice *dev)
struct s5p_serial_platdata *plat = dev->platdata;
struct s5p_uart *const uart = plat->reg;
/* enable FIFOs, auto clear Rx FIFO */
writel(0x3, &uart->ufcon);
writel(0, &uart->umcon);
/* 8N1 */
writel(0x3, &uart->ulcon);
/* No interrupts, no DMA, pure polling */
writel(0x245, &uart->ucon);
s5p_serial_init(uart);
return 0;
}
@@ -188,3 +201,29 @@ U_BOOT_DRIVER(serial_s5p) = {
.ops = &s5p_serial_ops,
.flags = DM_FLAG_PRE_RELOC,
};
#endif
#ifdef CONFIG_DEBUG_UART_S5P
#include <debug_uart.h>
void debug_uart_init(void)
{
struct s5p_uart *uart = (struct s5p_uart *)CONFIG_DEBUG_UART_BASE;
s5p_serial_init(uart);
s5p_serial_baud(uart, CONFIG_DEBUG_UART_CLOCK, CONFIG_BAUDRATE);
}
static inline void _debug_uart_putc(int ch)
{
struct s5p_uart *uart = (struct s5p_uart *)CONFIG_DEBUG_UART_BASE;
while (readl(&uart->ufstat) & TX_FIFO_FULL);
writeb(ch, &uart->utxh);
}
DEBUG_UART_FUNCS
#endif

View File

@@ -190,9 +190,9 @@ static int spi_rx_tx(struct exynos_spi_priv *priv, int todo,
spi_request_bytes(regs, toread, step);
}
if (priv->skip_preamble && get_timer(start) > 100) {
printf("SPI timeout: in_bytes=%d, out_bytes=%d, ",
in_bytes, out_bytes);
return -1;
debug("SPI timeout: in_bytes=%d, out_bytes=%d, ",
in_bytes, out_bytes);
return -ETIMEDOUT;
}
}

View File

@@ -664,8 +664,8 @@ static int fsl_dspi_ofdata_to_platdata(struct udevice *bus)
plat->speed_hz = fdtdec_get_int(blob,
node, "spi-max-frequency", FSL_DSPI_DEFAULT_SCK_FREQ);
debug("DSPI: regs=0x%llx, max-frequency=%d, endianess=%s, num-cs=%d\n",
(u64)plat->regs_addr, plat->speed_hz,
debug("DSPI: regs=%pa, max-frequency=%d, endianess=%s, num-cs=%d\n",
&plat->regs_addr, plat->speed_hz,
plat->flags & DSPI_FLAG_REGMAP_ENDIAN_BIG ? "be" : "le",
plat->num_chipselect);

View File

@@ -1,4 +1,5 @@
/*
* Copyright (c) 2015 Google, Inc
* Copyright (c) 2011 The Chromium OS Authors.
* Copyright (C) 2009 NVIDIA, Corporation
* Copyright (C) 2007-2008 SMSC (Steve Glendinning)
@@ -6,12 +7,14 @@
* SPDX-License-Identifier: GPL-2.0+
*/
#include <asm/unaligned.h>
#include <common.h>
#include <dm.h>
#include <errno.h>
#include <malloc.h>
#include <usb.h>
#include <asm/unaligned.h>
#include <linux/mii.h>
#include "usb_ether.h"
#include <malloc.h>
/* SMSC LAN95xx based USB 2.0 Ethernet Devices */
@@ -131,16 +134,21 @@
#define USB_BULK_SEND_TIMEOUT 5000
#define USB_BULK_RECV_TIMEOUT 5000
#define AX_RX_URB_SIZE 2048
#define RX_URB_SIZE 2048
#define PHY_CONNECT_TIMEOUT 5000
#define TURBO_MODE
#ifndef CONFIG_DM_ETH
/* local vars */
static int curr_eth_dev; /* index for name of next device detected */
#endif
/* driver private */
struct smsc95xx_private {
#ifdef CONFIG_DM_ETH
struct ueth_data ueth;
#endif
size_t rx_urb_size; /* maximum USB URB size */
u32 mac_cr; /* MAC control register value */
int have_hwaddr; /* 1 if we have a hardware MAC address */
@@ -149,7 +157,7 @@ struct smsc95xx_private {
/*
* Smsc95xx infrastructure commands
*/
static int smsc95xx_write_reg(struct ueth_data *dev, u32 index, u32 data)
static int smsc95xx_write_reg(struct usb_device *udev, u32 index, u32 data)
{
int len;
ALLOC_CACHE_ALIGN_BUFFER(u32, tmpbuf, 1);
@@ -157,32 +165,34 @@ static int smsc95xx_write_reg(struct ueth_data *dev, u32 index, u32 data)
cpu_to_le32s(&data);
tmpbuf[0] = data;
len = usb_control_msg(dev->pusb_dev, usb_sndctrlpipe(dev->pusb_dev, 0),
USB_VENDOR_REQUEST_WRITE_REGISTER,
USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
00, index, tmpbuf, sizeof(data), USB_CTRL_SET_TIMEOUT);
len = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
USB_VENDOR_REQUEST_WRITE_REGISTER,
USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
0, index, tmpbuf, sizeof(data),
USB_CTRL_SET_TIMEOUT);
if (len != sizeof(data)) {
debug("smsc95xx_write_reg failed: index=%d, data=%d, len=%d",
index, data, len);
return -1;
return -EIO;
}
return 0;
}
static int smsc95xx_read_reg(struct ueth_data *dev, u32 index, u32 *data)
static int smsc95xx_read_reg(struct usb_device *udev, u32 index, u32 *data)
{
int len;
ALLOC_CACHE_ALIGN_BUFFER(u32, tmpbuf, 1);
len = usb_control_msg(dev->pusb_dev, usb_rcvctrlpipe(dev->pusb_dev, 0),
USB_VENDOR_REQUEST_READ_REGISTER,
USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
00, index, tmpbuf, sizeof(data), USB_CTRL_GET_TIMEOUT);
len = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0),
USB_VENDOR_REQUEST_READ_REGISTER,
USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
0, index, tmpbuf, sizeof(data),
USB_CTRL_GET_TIMEOUT);
*data = tmpbuf[0];
if (len != sizeof(data)) {
debug("smsc95xx_read_reg failed: index=%d, len=%d",
index, len);
return -1;
return -EIO;
}
le32_to_cpus(data);
@@ -190,89 +200,89 @@ static int smsc95xx_read_reg(struct ueth_data *dev, u32 index, u32 *data)
}
/* Loop until the read is completed with timeout */
static int smsc95xx_phy_wait_not_busy(struct ueth_data *dev)
static int smsc95xx_phy_wait_not_busy(struct usb_device *udev)
{
unsigned long start_time = get_timer(0);
u32 val;
do {
smsc95xx_read_reg(dev, MII_ADDR, &val);
smsc95xx_read_reg(udev, MII_ADDR, &val);
if (!(val & MII_BUSY_))
return 0;
} while (get_timer(start_time) < 1 * 1000 * 1000);
} while (get_timer(start_time) < 1000);
return -1;
return -ETIMEDOUT;
}
static int smsc95xx_mdio_read(struct ueth_data *dev, int phy_id, int idx)
static int smsc95xx_mdio_read(struct usb_device *udev, int phy_id, int idx)
{
u32 val, addr;
/* confirm MII not busy */
if (smsc95xx_phy_wait_not_busy(dev)) {
if (smsc95xx_phy_wait_not_busy(udev)) {
debug("MII is busy in smsc95xx_mdio_read\n");
return -1;
return -ETIMEDOUT;
}
/* set the address, index & direction (read from PHY) */
addr = (phy_id << 11) | (idx << 6) | MII_READ_;
smsc95xx_write_reg(dev, MII_ADDR, addr);
smsc95xx_write_reg(udev, MII_ADDR, addr);
if (smsc95xx_phy_wait_not_busy(dev)) {
if (smsc95xx_phy_wait_not_busy(udev)) {
debug("Timed out reading MII reg %02X\n", idx);
return -1;
return -ETIMEDOUT;
}
smsc95xx_read_reg(dev, MII_DATA, &val);
smsc95xx_read_reg(udev, MII_DATA, &val);
return (u16)(val & 0xFFFF);
}
static void smsc95xx_mdio_write(struct ueth_data *dev, int phy_id, int idx,
static void smsc95xx_mdio_write(struct usb_device *udev, int phy_id, int idx,
int regval)
{
u32 val, addr;
/* confirm MII not busy */
if (smsc95xx_phy_wait_not_busy(dev)) {
if (smsc95xx_phy_wait_not_busy(udev)) {
debug("MII is busy in smsc95xx_mdio_write\n");
return;
}
val = regval;
smsc95xx_write_reg(dev, MII_DATA, val);
smsc95xx_write_reg(udev, MII_DATA, val);
/* set the address, index & direction (write to PHY) */
addr = (phy_id << 11) | (idx << 6) | MII_WRITE_;
smsc95xx_write_reg(dev, MII_ADDR, addr);
smsc95xx_write_reg(udev, MII_ADDR, addr);
if (smsc95xx_phy_wait_not_busy(dev))
if (smsc95xx_phy_wait_not_busy(udev))
debug("Timed out writing MII reg %02X\n", idx);
}
static int smsc95xx_eeprom_confirm_not_busy(struct ueth_data *dev)
static int smsc95xx_eeprom_confirm_not_busy(struct usb_device *udev)
{
unsigned long start_time = get_timer(0);
u32 val;
do {
smsc95xx_read_reg(dev, E2P_CMD, &val);
smsc95xx_read_reg(udev, E2P_CMD, &val);
if (!(val & E2P_CMD_BUSY_))
return 0;
udelay(40);
} while (get_timer(start_time) < 1 * 1000 * 1000);
debug("EEPROM is busy\n");
return -1;
return -ETIMEDOUT;
}
static int smsc95xx_wait_eeprom(struct ueth_data *dev)
static int smsc95xx_wait_eeprom(struct usb_device *udev)
{
unsigned long start_time = get_timer(0);
u32 val;
do {
smsc95xx_read_reg(dev, E2P_CMD, &val);
smsc95xx_read_reg(udev, E2P_CMD, &val);
if (!(val & E2P_CMD_BUSY_) || (val & E2P_CMD_TIMEOUT_))
break;
udelay(40);
@@ -280,30 +290,30 @@ static int smsc95xx_wait_eeprom(struct ueth_data *dev)
if (val & (E2P_CMD_TIMEOUT_ | E2P_CMD_BUSY_)) {
debug("EEPROM read operation timeout\n");
return -1;
return -ETIMEDOUT;
}
return 0;
}
static int smsc95xx_read_eeprom(struct ueth_data *dev, u32 offset, u32 length,
static int smsc95xx_read_eeprom(struct usb_device *udev, u32 offset, u32 length,
u8 *data)
{
u32 val;
int i, ret;
ret = smsc95xx_eeprom_confirm_not_busy(dev);
ret = smsc95xx_eeprom_confirm_not_busy(udev);
if (ret)
return ret;
for (i = 0; i < length; i++) {
val = E2P_CMD_BUSY_ | E2P_CMD_READ_ | (offset & E2P_CMD_ADDR_);
smsc95xx_write_reg(dev, E2P_CMD, val);
smsc95xx_write_reg(udev, E2P_CMD, val);
ret = smsc95xx_wait_eeprom(dev);
ret = smsc95xx_wait_eeprom(udev);
if (ret < 0)
return ret;
smsc95xx_read_reg(dev, E2P_DATA, &val);
smsc95xx_read_reg(udev, E2P_DATA, &val);
data[i] = val & 0xFF;
offset++;
}
@@ -315,89 +325,96 @@ static int smsc95xx_read_eeprom(struct ueth_data *dev, u32 offset, u32 length,
*
* Returns 0 on success, negative on error.
*/
static int mii_nway_restart(struct ueth_data *dev)
static int mii_nway_restart(struct usb_device *udev, struct ueth_data *dev)
{
int bmcr;
int r = -1;
/* if autoneg is off, it's an error */
bmcr = smsc95xx_mdio_read(dev, dev->phy_id, MII_BMCR);
bmcr = smsc95xx_mdio_read(udev, dev->phy_id, MII_BMCR);
if (bmcr & BMCR_ANENABLE) {
bmcr |= BMCR_ANRESTART;
smsc95xx_mdio_write(dev, dev->phy_id, MII_BMCR, bmcr);
smsc95xx_mdio_write(udev, dev->phy_id, MII_BMCR, bmcr);
r = 0;
}
return r;
}
static int smsc95xx_phy_initialize(struct ueth_data *dev)
static int smsc95xx_phy_initialize(struct usb_device *udev,
struct ueth_data *dev)
{
smsc95xx_mdio_write(dev, dev->phy_id, MII_BMCR, BMCR_RESET);
smsc95xx_mdio_write(dev, dev->phy_id, MII_ADVERTISE,
ADVERTISE_ALL | ADVERTISE_CSMA | ADVERTISE_PAUSE_CAP |
ADVERTISE_PAUSE_ASYM);
smsc95xx_mdio_write(udev, dev->phy_id, MII_BMCR, BMCR_RESET);
smsc95xx_mdio_write(udev, dev->phy_id, MII_ADVERTISE,
ADVERTISE_ALL | ADVERTISE_CSMA |
ADVERTISE_PAUSE_CAP | ADVERTISE_PAUSE_ASYM);
/* read to clear */
smsc95xx_mdio_read(dev, dev->phy_id, PHY_INT_SRC);
smsc95xx_mdio_read(udev, dev->phy_id, PHY_INT_SRC);
smsc95xx_mdio_write(dev, dev->phy_id, PHY_INT_MASK,
PHY_INT_MASK_DEFAULT_);
mii_nway_restart(dev);
smsc95xx_mdio_write(udev, dev->phy_id, PHY_INT_MASK,
PHY_INT_MASK_DEFAULT_);
mii_nway_restart(udev, dev);
debug("phy initialised succesfully\n");
return 0;
}
static int smsc95xx_init_mac_address(struct eth_device *eth,
struct ueth_data *dev)
static int smsc95xx_init_mac_address(unsigned char *enetaddr,
struct usb_device *udev)
{
int ret;
/* try reading mac address from EEPROM */
if (smsc95xx_read_eeprom(dev, EEPROM_MAC_OFFSET, ETH_ALEN,
eth->enetaddr) == 0) {
if (is_valid_ethaddr(eth->enetaddr)) {
/* eeprom values are valid so use them */
debug("MAC address read from EEPROM\n");
return 0;
}
ret = smsc95xx_read_eeprom(udev, EEPROM_MAC_OFFSET, ETH_ALEN, enetaddr);
if (ret)
return ret;
if (is_valid_ethaddr(enetaddr)) {
/* eeprom values are valid so use them */
debug("MAC address read from EEPROM\n");
return 0;
}
/*
* No eeprom, or eeprom values are invalid. Generating a random MAC
* address is not safe. Just return an error.
*/
return -1;
debug("Invalid MAC address read from EEPROM\n");
return -ENXIO;
}
static int smsc95xx_write_hwaddr(struct eth_device *eth)
static int smsc95xx_write_hwaddr_common(struct usb_device *udev,
struct smsc95xx_private *priv,
unsigned char *enetaddr)
{
struct ueth_data *dev = (struct ueth_data *)eth->priv;
struct smsc95xx_private *priv = dev->dev_priv;
u32 addr_lo = __get_unaligned_le32(&eth->enetaddr[0]);
u32 addr_hi = __get_unaligned_le16(&eth->enetaddr[4]);
u32 addr_lo = __get_unaligned_le32(&enetaddr[0]);
u32 addr_hi = __get_unaligned_le16(&enetaddr[4]);
int ret;
/* set hardware address */
debug("** %s()\n", __func__);
ret = smsc95xx_write_reg(dev, ADDRL, addr_lo);
ret = smsc95xx_write_reg(udev, ADDRL, addr_lo);
if (ret < 0)
return ret;
ret = smsc95xx_write_reg(dev, ADDRH, addr_hi);
ret = smsc95xx_write_reg(udev, ADDRH, addr_hi);
if (ret < 0)
return ret;
debug("MAC %pM\n", eth->enetaddr);
debug("MAC %pM\n", enetaddr);
priv->have_hwaddr = 1;
return 0;
}
/* Enable or disable Tx & Rx checksum offload engines */
static int smsc95xx_set_csums(struct ueth_data *dev,
int use_tx_csum, int use_rx_csum)
static int smsc95xx_set_csums(struct usb_device *udev, int use_tx_csum,
int use_rx_csum)
{
u32 read_buf;
int ret = smsc95xx_read_reg(dev, COE_CR, &read_buf);
int ret = smsc95xx_read_reg(udev, COE_CR, &read_buf);
if (ret < 0)
return ret;
@@ -411,7 +428,7 @@ static int smsc95xx_set_csums(struct ueth_data *dev,
else
read_buf &= ~Rx_COE_EN_;
ret = smsc95xx_write_reg(dev, COE_CR, read_buf);
ret = smsc95xx_write_reg(udev, COE_CR, read_buf);
if (ret < 0)
return ret;
@@ -419,52 +436,45 @@ static int smsc95xx_set_csums(struct ueth_data *dev,
return 0;
}
static void smsc95xx_set_multicast(struct ueth_data *dev)
static void smsc95xx_set_multicast(struct smsc95xx_private *priv)
{
struct smsc95xx_private *priv = dev->dev_priv;
/* No multicast in u-boot */
priv->mac_cr &= ~(MAC_CR_PRMS_ | MAC_CR_MCPAS_ | MAC_CR_HPFILT_);
}
/* starts the TX path */
static void smsc95xx_start_tx_path(struct ueth_data *dev)
static void smsc95xx_start_tx_path(struct usb_device *udev,
struct smsc95xx_private *priv)
{
struct smsc95xx_private *priv = dev->dev_priv;
u32 reg_val;
/* Enable Tx at MAC */
priv->mac_cr |= MAC_CR_TXEN_;
smsc95xx_write_reg(dev, MAC_CR, priv->mac_cr);
smsc95xx_write_reg(udev, MAC_CR, priv->mac_cr);
/* Enable Tx at SCSRs */
reg_val = TX_CFG_ON_;
smsc95xx_write_reg(dev, TX_CFG, reg_val);
smsc95xx_write_reg(udev, TX_CFG, reg_val);
}
/* Starts the Receive path */
static void smsc95xx_start_rx_path(struct ueth_data *dev)
static void smsc95xx_start_rx_path(struct usb_device *udev,
struct smsc95xx_private *priv)
{
struct smsc95xx_private *priv = dev->dev_priv;
priv->mac_cr |= MAC_CR_RXEN_;
smsc95xx_write_reg(dev, MAC_CR, priv->mac_cr);
smsc95xx_write_reg(udev, MAC_CR, priv->mac_cr);
}
/*
* Smsc95xx callbacks
*/
static int smsc95xx_init(struct eth_device *eth, bd_t *bd)
static int smsc95xx_init_common(struct usb_device *udev, struct ueth_data *dev,
struct smsc95xx_private *priv,
unsigned char *enetaddr)
{
int ret;
u32 write_buf;
u32 read_buf;
u32 burst_cap;
int timeout;
struct ueth_data *dev = (struct ueth_data *)eth->priv;
struct smsc95xx_private *priv =
(struct smsc95xx_private *)dev->dev_priv;
#define TIMEOUT_RESOLUTION 50 /* ms */
int link_detected;
@@ -472,13 +482,13 @@ static int smsc95xx_init(struct eth_device *eth, bd_t *bd)
dev->phy_id = SMSC95XX_INTERNAL_PHY_ID; /* fixed phy id */
write_buf = HW_CFG_LRST_;
ret = smsc95xx_write_reg(dev, HW_CFG, write_buf);
ret = smsc95xx_write_reg(udev, HW_CFG, write_buf);
if (ret < 0)
return ret;
timeout = 0;
do {
ret = smsc95xx_read_reg(dev, HW_CFG, &read_buf);
ret = smsc95xx_read_reg(udev, HW_CFG, &read_buf);
if (ret < 0)
return ret;
udelay(10 * 1000);
@@ -487,17 +497,17 @@ static int smsc95xx_init(struct eth_device *eth, bd_t *bd)
if (timeout >= 100) {
debug("timeout waiting for completion of Lite Reset\n");
return -1;
return -ETIMEDOUT;
}
write_buf = PM_CTL_PHY_RST_;
ret = smsc95xx_write_reg(dev, PM_CTRL, write_buf);
ret = smsc95xx_write_reg(udev, PM_CTRL, write_buf);
if (ret < 0)
return ret;
timeout = 0;
do {
ret = smsc95xx_read_reg(dev, PM_CTRL, &read_buf);
ret = smsc95xx_read_reg(udev, PM_CTRL, &read_buf);
if (ret < 0)
return ret;
udelay(10 * 1000);
@@ -505,28 +515,30 @@ static int smsc95xx_init(struct eth_device *eth, bd_t *bd)
} while ((read_buf & PM_CTL_PHY_RST_) && (timeout < 100));
if (timeout >= 100) {
debug("timeout waiting for PHY Reset\n");
return -1;
return -ETIMEDOUT;
}
if (!priv->have_hwaddr && smsc95xx_init_mac_address(eth, dev) == 0)
if (!priv->have_hwaddr && smsc95xx_init_mac_address(enetaddr, udev) ==
0)
priv->have_hwaddr = 1;
if (!priv->have_hwaddr) {
puts("Error: SMSC95xx: No MAC address set - set usbethaddr\n");
return -1;
return -EADDRNOTAVAIL;
}
if (smsc95xx_write_hwaddr(eth) < 0)
return -1;
ret = smsc95xx_write_hwaddr_common(udev, priv, enetaddr);
if (ret < 0)
return ret;
ret = smsc95xx_read_reg(dev, HW_CFG, &read_buf);
ret = smsc95xx_read_reg(udev, HW_CFG, &read_buf);
if (ret < 0)
return ret;
debug("Read Value from HW_CFG : 0x%08x\n", read_buf);
read_buf |= HW_CFG_BIR_;
ret = smsc95xx_write_reg(dev, HW_CFG, read_buf);
ret = smsc95xx_write_reg(udev, HW_CFG, read_buf);
if (ret < 0)
return ret;
ret = smsc95xx_read_reg(dev, HW_CFG, &read_buf);
ret = smsc95xx_read_reg(udev, HW_CFG, &read_buf);
if (ret < 0)
return ret;
debug("Read Value from HW_CFG after writing "
@@ -546,27 +558,27 @@ static int smsc95xx_init(struct eth_device *eth, bd_t *bd)
#endif
debug("rx_urb_size=%ld\n", (ulong)priv->rx_urb_size);
ret = smsc95xx_write_reg(dev, BURST_CAP, burst_cap);
ret = smsc95xx_write_reg(udev, BURST_CAP, burst_cap);
if (ret < 0)
return ret;
ret = smsc95xx_read_reg(dev, BURST_CAP, &read_buf);
ret = smsc95xx_read_reg(udev, BURST_CAP, &read_buf);
if (ret < 0)
return ret;
debug("Read Value from BURST_CAP after writing: 0x%08x\n", read_buf);
read_buf = DEFAULT_BULK_IN_DELAY;
ret = smsc95xx_write_reg(dev, BULK_IN_DLY, read_buf);
ret = smsc95xx_write_reg(udev, BULK_IN_DLY, read_buf);
if (ret < 0)
return ret;
ret = smsc95xx_read_reg(dev, BULK_IN_DLY, &read_buf);
ret = smsc95xx_read_reg(udev, BULK_IN_DLY, &read_buf);
if (ret < 0)
return ret;
debug("Read Value from BULK_IN_DLY after writing: "
"0x%08x\n", read_buf);
ret = smsc95xx_read_reg(dev, HW_CFG, &read_buf);
ret = smsc95xx_read_reg(udev, HW_CFG, &read_buf);
if (ret < 0)
return ret;
debug("Read Value from HW_CFG: 0x%08x\n", read_buf);
@@ -579,21 +591,21 @@ static int smsc95xx_init(struct eth_device *eth, bd_t *bd)
#define NET_IP_ALIGN 0
read_buf |= NET_IP_ALIGN << 9;
ret = smsc95xx_write_reg(dev, HW_CFG, read_buf);
ret = smsc95xx_write_reg(udev, HW_CFG, read_buf);
if (ret < 0)
return ret;
ret = smsc95xx_read_reg(dev, HW_CFG, &read_buf);
ret = smsc95xx_read_reg(udev, HW_CFG, &read_buf);
if (ret < 0)
return ret;
debug("Read Value from HW_CFG after writing: 0x%08x\n", read_buf);
write_buf = 0xFFFFFFFF;
ret = smsc95xx_write_reg(dev, INT_STS, write_buf);
ret = smsc95xx_write_reg(udev, INT_STS, write_buf);
if (ret < 0)
return ret;
ret = smsc95xx_read_reg(dev, ID_REV, &read_buf);
ret = smsc95xx_read_reg(udev, ID_REV, &read_buf);
if (ret < 0)
return ret;
debug("ID_REV = 0x%08x\n", read_buf);
@@ -601,59 +613,60 @@ static int smsc95xx_init(struct eth_device *eth, bd_t *bd)
/* Configure GPIO pins as LED outputs */
write_buf = LED_GPIO_CFG_SPD_LED | LED_GPIO_CFG_LNK_LED |
LED_GPIO_CFG_FDX_LED;
ret = smsc95xx_write_reg(dev, LED_GPIO_CFG, write_buf);
ret = smsc95xx_write_reg(udev, LED_GPIO_CFG, write_buf);
if (ret < 0)
return ret;
debug("LED_GPIO_CFG set\n");
/* Init Tx */
write_buf = 0;
ret = smsc95xx_write_reg(dev, FLOW, write_buf);
ret = smsc95xx_write_reg(udev, FLOW, write_buf);
if (ret < 0)
return ret;
read_buf = AFC_CFG_DEFAULT;
ret = smsc95xx_write_reg(dev, AFC_CFG, read_buf);
ret = smsc95xx_write_reg(udev, AFC_CFG, read_buf);
if (ret < 0)
return ret;
ret = smsc95xx_read_reg(dev, MAC_CR, &priv->mac_cr);
ret = smsc95xx_read_reg(udev, MAC_CR, &priv->mac_cr);
if (ret < 0)
return ret;
/* Init Rx. Set Vlan */
write_buf = (u32)ETH_P_8021Q;
ret = smsc95xx_write_reg(dev, VLAN1, write_buf);
ret = smsc95xx_write_reg(udev, VLAN1, write_buf);
if (ret < 0)
return ret;
/* Disable checksum offload engines */
ret = smsc95xx_set_csums(dev, 0, 0);
ret = smsc95xx_set_csums(udev, 0, 0);
if (ret < 0) {
debug("Failed to set csum offload: %d\n", ret);
return ret;
}
smsc95xx_set_multicast(dev);
smsc95xx_set_multicast(priv);
if (smsc95xx_phy_initialize(dev) < 0)
return -1;
ret = smsc95xx_read_reg(dev, INT_EP_CTL, &read_buf);
ret = smsc95xx_phy_initialize(udev, dev);
if (ret < 0)
return ret;
ret = smsc95xx_read_reg(udev, INT_EP_CTL, &read_buf);
if (ret < 0)
return ret;
/* enable PHY interrupts */
read_buf |= INT_EP_CTL_PHY_INT_;
ret = smsc95xx_write_reg(dev, INT_EP_CTL, read_buf);
ret = smsc95xx_write_reg(udev, INT_EP_CTL, read_buf);
if (ret < 0)
return ret;
smsc95xx_start_tx_path(dev);
smsc95xx_start_rx_path(dev);
smsc95xx_start_tx_path(udev, priv);
smsc95xx_start_rx_path(udev, priv);
timeout = 0;
do {
link_detected = smsc95xx_mdio_read(dev, dev->phy_id, MII_BMSR)
link_detected = smsc95xx_mdio_read(udev, dev->phy_id, MII_BMSR)
& BMSR_LSTATUS;
if (!link_detected) {
if (timeout == 0)
@@ -667,14 +680,13 @@ static int smsc95xx_init(struct eth_device *eth, bd_t *bd)
printf("done.\n");
} else {
printf("unable to connect.\n");
return -1;
return -EIO;
}
return 0;
}
static int smsc95xx_send(struct eth_device *eth, void* packet, int length)
static int smsc95xx_send_common(struct ueth_data *dev, void *packet, int length)
{
struct ueth_data *dev = (struct ueth_data *)eth->priv;
int err;
int actual_len;
u32 tx_cmd_a;
@@ -684,7 +696,7 @@ static int smsc95xx_send(struct eth_device *eth, void* packet, int length)
debug("** %s(), len %d, buf %#x\n", __func__, length, (int)msg);
if (length > PKTSIZE)
return -1;
return -ENOSPC;
tx_cmd_a = (u32)length | TX_CMD_A_FIRST_SEG_ | TX_CMD_A_LAST_SEG_;
tx_cmd_b = (u32)length;
@@ -705,13 +717,35 @@ static int smsc95xx_send(struct eth_device *eth, void* packet, int length)
debug("Tx: len = %u, actual = %u, err = %d\n",
length + sizeof(tx_cmd_a) + sizeof(tx_cmd_b),
actual_len, err);
return err;
}
#ifndef CONFIG_DM_ETH
/*
* Smsc95xx callbacks
*/
static int smsc95xx_init(struct eth_device *eth, bd_t *bd)
{
struct ueth_data *dev = (struct ueth_data *)eth->priv;
struct usb_device *udev = dev->pusb_dev;
struct smsc95xx_private *priv =
(struct smsc95xx_private *)dev->dev_priv;
return smsc95xx_init_common(udev, dev, priv, eth->enetaddr);
}
static int smsc95xx_send(struct eth_device *eth, void *packet, int length)
{
struct ueth_data *dev = (struct ueth_data *)eth->priv;
return smsc95xx_send_common(dev, packet, length);
}
static int smsc95xx_recv(struct eth_device *eth)
{
struct ueth_data *dev = (struct ueth_data *)eth->priv;
DEFINE_CACHE_ALIGN_BUFFER(unsigned char, recv_buf, AX_RX_URB_SIZE);
DEFINE_CACHE_ALIGN_BUFFER(unsigned char, recv_buf, RX_URB_SIZE);
unsigned char *buf_ptr;
int err;
int actual_len;
@@ -720,20 +754,18 @@ static int smsc95xx_recv(struct eth_device *eth)
debug("** %s()\n", __func__);
err = usb_bulk_msg(dev->pusb_dev,
usb_rcvbulkpipe(dev->pusb_dev, dev->ep_in),
(void *)recv_buf,
AX_RX_URB_SIZE,
&actual_len,
USB_BULK_RECV_TIMEOUT);
debug("Rx: len = %u, actual = %u, err = %d\n", AX_RX_URB_SIZE,
usb_rcvbulkpipe(dev->pusb_dev, dev->ep_in),
(void *)recv_buf, RX_URB_SIZE, &actual_len,
USB_BULK_RECV_TIMEOUT);
debug("Rx: len = %u, actual = %u, err = %d\n", RX_URB_SIZE,
actual_len, err);
if (err != 0) {
debug("Rx: failed to receive\n");
return -1;
return -err;
}
if (actual_len > AX_RX_URB_SIZE) {
if (actual_len > RX_URB_SIZE) {
debug("Rx: received too many bytes %d\n", actual_len);
return -1;
return -ENOSPC;
}
buf_ptr = recv_buf;
@@ -744,19 +776,19 @@ static int smsc95xx_recv(struct eth_device *eth)
*/
if (actual_len < sizeof(packet_len)) {
debug("Rx: incomplete packet length\n");
return -1;
return -EIO;
}
memcpy(&packet_len, buf_ptr, sizeof(packet_len));
le32_to_cpus(&packet_len);
if (packet_len & RX_STS_ES_) {
debug("Rx: Error header=%#x", packet_len);
return -1;
return -EIO;
}
packet_len = ((packet_len & RX_STS_FL_) >> 16);
if (packet_len > actual_len - sizeof(packet_len)) {
debug("Rx: too large packet: %d\n", packet_len);
return -1;
return -EIO;
}
/* Notify net stack */
@@ -783,6 +815,15 @@ static void smsc95xx_halt(struct eth_device *eth)
debug("** %s()\n", __func__);
}
static int smsc95xx_write_hwaddr(struct eth_device *eth)
{
struct ueth_data *dev = eth->priv;
struct usb_device *udev = dev->pusb_dev;
struct smsc95xx_private *priv = dev->dev_priv;
return smsc95xx_write_hwaddr_common(udev, priv, eth->enetaddr);
}
/*
* SMSC probing functions
*/
@@ -898,3 +939,137 @@ int smsc95xx_eth_get_info(struct usb_device *dev, struct ueth_data *ss,
eth->priv = ss;
return 1;
}
#endif /* !CONFIG_DM_ETH */
#ifdef CONFIG_DM_ETH
static int smsc95xx_eth_start(struct udevice *dev)
{
struct usb_device *udev = dev_get_parentdata(dev);
struct smsc95xx_private *priv = dev_get_priv(dev);
struct eth_pdata *pdata = dev_get_platdata(dev);
/* Driver-model Ethernet ensures we have this */
priv->have_hwaddr = 1;
return smsc95xx_init_common(udev, &priv->ueth, priv, pdata->enetaddr);
}
void smsc95xx_eth_stop(struct udevice *dev)
{
debug("** %s()\n", __func__);
}
int smsc95xx_eth_send(struct udevice *dev, void *packet, int length)
{
struct smsc95xx_private *priv = dev_get_priv(dev);
return smsc95xx_send_common(&priv->ueth, packet, length);
}
int smsc95xx_eth_recv(struct udevice *dev, int flags, uchar **packetp)
{
struct smsc95xx_private *priv = dev_get_priv(dev);
struct ueth_data *ueth = &priv->ueth;
uint8_t *ptr;
int ret, len;
u32 packet_len;
len = usb_ether_get_rx_bytes(ueth, &ptr);
debug("%s: first try, len=%d\n", __func__, len);
if (!len) {
if (!(flags & ETH_RECV_CHECK_DEVICE))
return -EAGAIN;
ret = usb_ether_receive(ueth, RX_URB_SIZE);
if (ret == -EAGAIN)
return ret;
len = usb_ether_get_rx_bytes(ueth, &ptr);
debug("%s: second try, len=%d\n", __func__, len);
}
/*
* 1st 4 bytes contain the length of the actual data plus error info.
* Extract data length.
*/
if (len < sizeof(packet_len)) {
debug("Rx: incomplete packet length\n");
goto err;
}
memcpy(&packet_len, ptr, sizeof(packet_len));
le32_to_cpus(&packet_len);
if (packet_len & RX_STS_ES_) {
debug("Rx: Error header=%#x", packet_len);
goto err;
}
packet_len = ((packet_len & RX_STS_FL_) >> 16);
if (packet_len > len - sizeof(packet_len)) {
debug("Rx: too large packet: %d\n", packet_len);
goto err;
}
*packetp = ptr + sizeof(packet_len);
return packet_len;
err:
usb_ether_advance_rxbuf(ueth, -1);
return -EINVAL;
}
static int smsc95xx_free_pkt(struct udevice *dev, uchar *packet, int packet_len)
{
struct smsc95xx_private *priv = dev_get_priv(dev);
packet_len = ALIGN(packet_len, 4);
usb_ether_advance_rxbuf(&priv->ueth, sizeof(u32) + packet_len);
return 0;
}
int smsc95xx_write_hwaddr(struct udevice *dev)
{
struct usb_device *udev = dev_get_parentdata(dev);
struct eth_pdata *pdata = dev_get_platdata(dev);
struct smsc95xx_private *priv = dev_get_priv(dev);
return smsc95xx_write_hwaddr_common(udev, priv, pdata->enetaddr);
}
static int smsc95xx_eth_probe(struct udevice *dev)
{
struct smsc95xx_private *priv = dev_get_priv(dev);
struct ueth_data *ueth = &priv->ueth;
return usb_ether_register(dev, ueth, RX_URB_SIZE);
}
static const struct eth_ops smsc95xx_eth_ops = {
.start = smsc95xx_eth_start,
.send = smsc95xx_eth_send,
.recv = smsc95xx_eth_recv,
.free_pkt = smsc95xx_free_pkt,
.stop = smsc95xx_eth_stop,
.write_hwaddr = smsc95xx_write_hwaddr,
};
U_BOOT_DRIVER(smsc95xx_eth) = {
.name = "smsc95xx_eth",
.id = UCLASS_ETH,
.probe = smsc95xx_eth_probe,
.ops = &smsc95xx_eth_ops,
.priv_auto_alloc_size = sizeof(struct smsc95xx_private),
.platdata_auto_alloc_size = sizeof(struct eth_pdata),
};
static const struct usb_device_id smsc95xx_eth_id_table[] = {
{ USB_DEVICE(0x05ac, 0x1402) },
{ USB_DEVICE(0x0424, 0xec00) }, /* LAN9512/LAN9514 Ethernet */
{ USB_DEVICE(0x0424, 0x9500) }, /* LAN9500 Ethernet */
{ USB_DEVICE(0x0424, 0x9730) }, /* LAN9730 Ethernet (HSIC) */
{ USB_DEVICE(0x0424, 0x9900) }, /* SMSC9500 USB Ethernet (SAL10) */
{ USB_DEVICE(0x0424, 0x9e00) }, /* LAN9500A Ethernet */
{ } /* Terminating entry */
};
U_BOOT_USB_DEVICE(smsc95xx_eth, smsc95xx_eth_id_table);
#endif

View File

@@ -6,6 +6,7 @@
#include <common.h>
#include <dm.h>
#include <errno.h>
#include <malloc.h>
#include <usb.h>
#include <dm/device-internal.h>

View File

@@ -6,6 +6,7 @@
*/
#include <common.h>
#include <dm.h>
#include <errno.h>
#include <usb.h>
#include <malloc.h>
@@ -21,18 +22,29 @@
#define DWC2_STATUS_BUF_SIZE 64
#define DWC2_DATA_BUF_SIZE (64 * 1024)
/* We need doubleword-aligned buffers for DMA transfers */
DEFINE_ALIGN_BUFFER(uint8_t, aligned_buffer, DWC2_DATA_BUF_SIZE, 8);
DEFINE_ALIGN_BUFFER(uint8_t, status_buffer, DWC2_STATUS_BUF_SIZE, 8);
#define MAX_DEVICE 16
#define MAX_ENDPOINT 16
static int bulk_data_toggle[MAX_DEVICE][MAX_ENDPOINT];
static int root_hub_devnum;
struct dwc2_priv {
#ifdef CONFIG_DM_USB
uint8_t aligned_buffer[DWC2_DATA_BUF_SIZE] __aligned(8);
uint8_t status_buffer[DWC2_STATUS_BUF_SIZE] __aligned(8);
#else
uint8_t *aligned_buffer;
uint8_t *status_buffer;
#endif
int bulk_data_toggle[MAX_DEVICE][MAX_ENDPOINT];
struct dwc2_core_regs *regs;
int root_hub_devnum;
};
static struct dwc2_core_regs *regs =
(struct dwc2_core_regs *)CONFIG_USB_DWC2_REG_ADDR;
#ifndef CONFIG_DM_USB
/* We need doubleword-aligned buffers for DMA transfers */
DEFINE_ALIGN_BUFFER(uint8_t, aligned_buffer_addr, DWC2_DATA_BUF_SIZE, 8);
DEFINE_ALIGN_BUFFER(uint8_t, status_buffer_addr, DWC2_STATUS_BUF_SIZE, 8);
static struct dwc2_priv local;
#endif
/*
* DWC2 IP interface
@@ -428,7 +440,8 @@ static void dwc_otg_hc_init(struct dwc2_core_regs *regs, uint8_t hc_num,
* DWC2 to USB API interface
*/
/* Direction: In ; Request: Status */
static int dwc_otg_submit_rh_msg_in_status(struct usb_device *dev, void *buffer,
static int dwc_otg_submit_rh_msg_in_status(struct dwc2_core_regs *regs,
struct usb_device *dev, void *buffer,
int txlen, struct devrequest *cmd)
{
uint32_t hprt0 = 0;
@@ -602,13 +615,13 @@ static int dwc_otg_submit_rh_msg_in_configuration(struct usb_device *dev,
}
/* Direction: In */
static int dwc_otg_submit_rh_msg_in(struct usb_device *dev,
void *buffer, int txlen,
struct devrequest *cmd)
static int dwc_otg_submit_rh_msg_in(struct dwc2_priv *priv,
struct usb_device *dev, void *buffer,
int txlen, struct devrequest *cmd)
{
switch (cmd->request) {
case USB_REQ_GET_STATUS:
return dwc_otg_submit_rh_msg_in_status(dev, buffer,
return dwc_otg_submit_rh_msg_in_status(priv->regs, dev, buffer,
txlen, cmd);
case USB_REQ_GET_DESCRIPTOR:
return dwc_otg_submit_rh_msg_in_descriptor(dev, buffer,
@@ -623,10 +636,12 @@ static int dwc_otg_submit_rh_msg_in(struct usb_device *dev,
}
/* Direction: Out */
static int dwc_otg_submit_rh_msg_out(struct usb_device *dev,
void *buffer, int txlen,
struct devrequest *cmd)
static int dwc_otg_submit_rh_msg_out(struct dwc2_priv *priv,
struct usb_device *dev,
void *buffer, int txlen,
struct devrequest *cmd)
{
struct dwc2_core_regs *regs = priv->regs;
int len = 0;
int stat = 0;
uint16_t bmrtype_breq = cmd->requesttype | (cmd->request << 8);
@@ -673,7 +688,7 @@ static int dwc_otg_submit_rh_msg_out(struct usb_device *dev,
}
break;
case (USB_REQ_SET_ADDRESS << 8):
root_hub_devnum = wValue;
priv->root_hub_devnum = wValue;
break;
case (USB_REQ_SET_CONFIGURATION << 8):
break;
@@ -690,8 +705,8 @@ static int dwc_otg_submit_rh_msg_out(struct usb_device *dev,
return stat;
}
static int dwc_otg_submit_rh_msg(struct usb_device *dev, unsigned long pipe,
void *buffer, int txlen,
static int dwc_otg_submit_rh_msg(struct dwc2_priv *priv, struct usb_device *dev,
unsigned long pipe, void *buffer, int txlen,
struct devrequest *cmd)
{
int stat = 0;
@@ -702,16 +717,17 @@ static int dwc_otg_submit_rh_msg(struct usb_device *dev, unsigned long pipe,
}
if (cmd->requesttype & USB_DIR_IN)
stat = dwc_otg_submit_rh_msg_in(dev, buffer, txlen, cmd);
stat = dwc_otg_submit_rh_msg_in(priv, dev, buffer, txlen, cmd);
else
stat = dwc_otg_submit_rh_msg_out(dev, buffer, txlen, cmd);
stat = dwc_otg_submit_rh_msg_out(priv, dev, buffer, txlen, cmd);
mdelay(1);
return stat;
}
int wait_for_chhltd(uint32_t *sub, int *toggle, bool ignore_ack)
int wait_for_chhltd(struct dwc2_core_regs *regs, uint32_t *sub, int *toggle,
bool ignore_ack)
{
uint32_t hcint_comp_hlt_ack = DWC2_HCINT_XFERCOMP | DWC2_HCINT_CHHLTD;
struct dwc2_hc_regs *hc_regs = &regs->hc_regs[DWC2_HC_CHANNEL];
@@ -751,9 +767,11 @@ static int dwc2_eptype[] = {
DWC2_HCCHAR_EPTYPE_BULK,
};
int chunk_msg(struct usb_device *dev, unsigned long pipe, int *pid, int in,
void *buffer, int len, bool ignore_ack)
int chunk_msg(struct dwc2_priv *priv, struct usb_device *dev,
unsigned long pipe, int *pid, int in, void *buffer, int len,
bool ignore_ack)
{
struct dwc2_core_regs *regs = priv->regs;
struct dwc2_hc_regs *hc_regs = &regs->hc_regs[DWC2_HC_CHANNEL];
int devnum = usb_pipedevice(pipe);
int ep = usb_pipeendpoint(pipe);
@@ -802,10 +820,12 @@ int chunk_msg(struct usb_device *dev, unsigned long pipe, int *pid, int in,
(*pid << DWC2_HCTSIZ_PID_OFFSET),
&hc_regs->hctsiz);
if (!in)
memcpy(aligned_buffer, (char *)buffer + done, len);
if (!in) {
memcpy(priv->aligned_buffer, (char *)buffer + done,
len);
}
writel(phys_to_bus((unsigned long)aligned_buffer),
writel(phys_to_bus((unsigned long)priv->aligned_buffer),
&hc_regs->hcdma);
/* Set host channel enable after all other setup is complete. */
@@ -814,13 +834,13 @@ int chunk_msg(struct usb_device *dev, unsigned long pipe, int *pid, int in,
(1 << DWC2_HCCHAR_MULTICNT_OFFSET) |
DWC2_HCCHAR_CHEN);
ret = wait_for_chhltd(&sub, pid, ignore_ack);
ret = wait_for_chhltd(regs, &sub, pid, ignore_ack);
if (ret)
break;
if (in) {
xfer_len -= sub;
memcpy(buffer + done, aligned_buffer, xfer_len);
memcpy(buffer + done, priv->aligned_buffer, xfer_len);
if (sub)
stop_transfer = 1;
}
@@ -839,43 +859,45 @@ int chunk_msg(struct usb_device *dev, unsigned long pipe, int *pid, int in,
}
/* U-Boot USB transmission interface */
int submit_bulk_msg(struct usb_device *dev, unsigned long pipe, void *buffer,
int len)
int _submit_bulk_msg(struct dwc2_priv *priv, struct usb_device *dev,
unsigned long pipe, void *buffer, int len)
{
int devnum = usb_pipedevice(pipe);
int ep = usb_pipeendpoint(pipe);
if (devnum == root_hub_devnum) {
if (devnum == priv->root_hub_devnum) {
dev->status = 0;
return -EINVAL;
}
return chunk_msg(dev, pipe, &bulk_data_toggle[devnum][ep],
return chunk_msg(priv, dev, pipe, &priv->bulk_data_toggle[devnum][ep],
usb_pipein(pipe), buffer, len, true);
}
int submit_control_msg(struct usb_device *dev, unsigned long pipe, void *buffer,
int len, struct devrequest *setup)
static int _submit_control_msg(struct dwc2_priv *priv, struct usb_device *dev,
unsigned long pipe, void *buffer, int len,
struct devrequest *setup)
{
int devnum = usb_pipedevice(pipe);
int pid, ret, act_len;
/* For CONTROL endpoint pid should start with DATA1 */
int status_direction;
if (devnum == root_hub_devnum) {
if (devnum == priv->root_hub_devnum) {
dev->status = 0;
dev->speed = USB_SPEED_HIGH;
return dwc_otg_submit_rh_msg(dev, pipe, buffer, len, setup);
return dwc_otg_submit_rh_msg(priv, dev, pipe, buffer, len,
setup);
}
pid = DWC2_HC_PID_SETUP;
ret = chunk_msg(dev, pipe, &pid, 0, setup, 8, true);
ret = chunk_msg(priv, dev, pipe, &pid, 0, setup, 8, true);
if (ret)
return ret;
if (buffer) {
pid = DWC2_HC_PID_DATA1;
ret = chunk_msg(dev, pipe, &pid, usb_pipein(pipe), buffer,
ret = chunk_msg(priv, dev, pipe, &pid, usb_pipein(pipe), buffer,
len, false);
if (ret)
return ret;
@@ -891,8 +913,8 @@ int submit_control_msg(struct usb_device *dev, unsigned long pipe, void *buffer,
status_direction = 0;
pid = DWC2_HC_PID_DATA1;
ret = chunk_msg(dev, pipe, &pid, status_direction, status_buffer, 0,
false);
ret = chunk_msg(priv, dev, pipe, &pid, status_direction,
priv->status_buffer, 0, false);
if (ret)
return ret;
@@ -901,8 +923,8 @@ int submit_control_msg(struct usb_device *dev, unsigned long pipe, void *buffer,
return 0;
}
int submit_int_msg(struct usb_device *dev, unsigned long pipe, void *buffer,
int len, int interval)
int _submit_int_msg(struct dwc2_priv *priv, struct usb_device *dev,
unsigned long pipe, void *buffer, int len, int interval)
{
unsigned long timeout;
int ret;
@@ -915,24 +937,18 @@ int submit_int_msg(struct usb_device *dev, unsigned long pipe, void *buffer,
printf("Timeout poll on interrupt endpoint\n");
return -ETIMEDOUT;
}
ret = submit_bulk_msg(dev, pipe, buffer, len);
ret = _submit_bulk_msg(priv, dev, pipe, buffer, len);
if (ret != -EAGAIN)
return ret;
}
}
/* U-Boot USB control interface */
int usb_lowlevel_init(int index, enum usb_init_type init, void **controller)
static int dwc2_init_common(struct dwc2_priv *priv)
{
struct dwc2_core_regs *regs = priv->regs;
uint32_t snpsid;
int i, j;
root_hub_devnum = 0;
/* board dependant init */
if (board_usb_init(index, USB_INIT_HOST))
return -1;
snpsid = readl(&regs->gsnpsid);
printf("Core Release: %x.%03x\n", snpsid >> 12 & 0xf, snpsid & 0xfff);
@@ -956,18 +972,149 @@ int usb_lowlevel_init(int index, enum usb_init_type init, void **controller)
for (i = 0; i < MAX_DEVICE; i++) {
for (j = 0; j < MAX_ENDPOINT; j++)
bulk_data_toggle[i][j] = DWC2_HC_PID_DATA0;
priv->bulk_data_toggle[i][j] = DWC2_HC_PID_DATA0;
}
return 0;
}
int usb_lowlevel_stop(int index)
static void dwc2_uninit_common(struct dwc2_core_regs *regs)
{
/* Put everything in reset. */
clrsetbits_le32(&regs->hprt0, DWC2_HPRT0_PRTENA |
DWC2_HPRT0_PRTCONNDET | DWC2_HPRT0_PRTENCHNG |
DWC2_HPRT0_PRTOVRCURRCHNG,
DWC2_HPRT0_PRTRST);
}
#ifndef CONFIG_DM_USB
int submit_control_msg(struct usb_device *dev, unsigned long pipe, void *buffer,
int len, struct devrequest *setup)
{
return _submit_control_msg(&local, dev, pipe, buffer, len, setup);
}
int submit_bulk_msg(struct usb_device *dev, unsigned long pipe, void *buffer,
int len)
{
return _submit_bulk_msg(&local, dev, pipe, buffer, len);
}
int submit_int_msg(struct usb_device *dev, unsigned long pipe, void *buffer,
int len, int interval)
{
return _submit_int_msg(&local, dev, pipe, buffer, len, interval);
}
/* U-Boot USB control interface */
int usb_lowlevel_init(int index, enum usb_init_type init, void **controller)
{
struct dwc2_priv *priv = &local;
memset(priv, '\0', sizeof(*priv));
priv->root_hub_devnum = 0;
priv->regs = (struct dwc2_core_regs *)CONFIG_USB_DWC2_REG_ADDR;
priv->aligned_buffer = aligned_buffer_addr;
priv->status_buffer = status_buffer_addr;
/* board-dependant init */
if (board_usb_init(index, USB_INIT_HOST))
return -1;
return dwc2_init_common(priv);
}
int usb_lowlevel_stop(int index)
{
dwc2_uninit_common(local.regs);
return 0;
}
#endif
#ifdef CONFIG_DM_USB
static int dwc2_submit_control_msg(struct udevice *dev, struct usb_device *udev,
unsigned long pipe, void *buffer, int length,
struct devrequest *setup)
{
struct dwc2_priv *priv = dev_get_priv(dev);
debug("%s: dev='%s', udev=%p, udev->dev='%s', portnr=%d\n", __func__,
dev->name, udev, udev->dev->name, udev->portnr);
return _submit_control_msg(priv, udev, pipe, buffer, length, setup);
}
static int dwc2_submit_bulk_msg(struct udevice *dev, struct usb_device *udev,
unsigned long pipe, void *buffer, int length)
{
struct dwc2_priv *priv = dev_get_priv(dev);
debug("%s: dev='%s', udev=%p\n", __func__, dev->name, udev);
return _submit_bulk_msg(priv, udev, pipe, buffer, length);
}
static int dwc2_submit_int_msg(struct udevice *dev, struct usb_device *udev,
unsigned long pipe, void *buffer, int length,
int interval)
{
struct dwc2_priv *priv = dev_get_priv(dev);
debug("%s: dev='%s', udev=%p\n", __func__, dev->name, udev);
return _submit_int_msg(priv, udev, pipe, buffer, length, interval);
}
static int dwc2_usb_ofdata_to_platdata(struct udevice *dev)
{
struct dwc2_priv *priv = dev_get_priv(dev);
fdt_addr_t addr;
addr = dev_get_addr(dev);
if (addr == FDT_ADDR_T_NONE)
return -EINVAL;
priv->regs = (struct dwc2_core_regs *)addr;
return 0;
}
static int dwc2_usb_probe(struct udevice *dev)
{
struct dwc2_priv *priv = dev_get_priv(dev);
return dwc2_init_common(priv);
}
static int dwc2_usb_remove(struct udevice *dev)
{
struct dwc2_priv *priv = dev_get_priv(dev);
dwc2_uninit_common(priv->regs);
return 0;
}
struct dm_usb_ops dwc2_usb_ops = {
.control = dwc2_submit_control_msg,
.bulk = dwc2_submit_bulk_msg,
.interrupt = dwc2_submit_int_msg,
};
static const struct udevice_id dwc2_usb_ids[] = {
{ .compatible = "brcm,bcm2835-usb" },
{ }
};
U_BOOT_DRIVER(usb_dwc2) = {
.name = "dwc2_exynos",
.id = UCLASS_USB,
.of_match = dwc2_usb_ids,
.ofdata_to_platdata = dwc2_usb_ofdata_to_platdata,
.probe = dwc2_usb_probe,
.remove = dwc2_usb_remove,
.ops = &dwc2_usb_ops,
.priv_auto_alloc_size = sizeof(struct dwc2_priv),
.flags = DM_FLAG_ALLOC_PRIV_DMA,
};
#endif

View File

@@ -240,3 +240,5 @@ config VIDEO_TEGRA124
HDMI. At present only eDP is supported by U-Boot. This option
enables this support which can be used on devices which
have an eDP display connected.
source "drivers/video/bridge/Kconfig"

View File

@@ -51,6 +51,7 @@ obj-$(CONFIG_VIDEO_VESA) += vesa_fb.o
obj-$(CONFIG_FORMIKE) += formike.o
obj-$(CONFIG_LG4573) += lg4573.o
obj-$(CONFIG_AM335X_LCD) += am335x-fb.o
obj-$(CONFIG_VIDEO_PARADE) += parade.o
obj-${CONFIG_VIDEO_TEGRA124} += tegra124/
obj-y += bridge/

View File

@@ -0,0 +1,27 @@
config VIDEO_BRIDGE
bool "Support video bridges"
depends on DM
help
Some platforms use video bridges to convert from one output to
another. For example, where the SoC only supports eDP and the LCD
requires LVDS, an eDP->LVDS bridge chip can be used to provide the
necessary conversion. This option enables support for these devices.
config VIDEO_BRIDGE_PARADE_PS862X
bool "Support Parade PS862X DP->LVDS bridge"
depends on VIDEO_BRIDGE
help
The Parade PS8622 and PS8625 are DisplayPort-to-LVDS (Low voltage
differential signalling) converters. They enable an LVDS LCD panel
to be connected to an eDP output device such as an SoC that lacks
LVDS capability, or where LVDS requires too many signals to route
on the PCB. Setup parameters are provided in the device tree.
config VIDEO_BRIDGE_NXP_PTN3460
bool "Support NXP PTN3460 DP->LVDS bridge"
depends on VIDEO_BRIDGE
help
The NXP PTN3460 is a DisplayPort-to-LVDS (Low voltage differential
signalling) converter. It enables an LVDS LCD panel to be connected
to an eDP output device such as an SoC that lacks LVDS capability,
or where LVDS requires too many signals to route on the PCB.

View File

@@ -0,0 +1,9 @@
#
# Copyright (C) 2015 Google, Inc
# Written by Simon Glass <sjg@chromium.org>
#
# SPDX-License-Identifier: GPL-2.0+
obj-$(CONFIG_VIDEO_BRIDGE) += video-bridge-uclass.o
obj-$(CONFIG_VIDEO_BRIDGE_PARADE_PS862X) += ps862x.o
obj-$(CONFIG_VIDEO_BRIDGE_NXP_PTN3460) += ptn3460.o

View File

@@ -0,0 +1,134 @@
/*
* Copyright (C) 2015 Google, Inc
* Written by Simon Glass <sjg@chromium.org>
*
* SPDX-License-Identifier: GPL-2.0+
*/
#include <common.h>
#include <dm.h>
#include <errno.h>
#include <i2c.h>
#include <video_bridge.h>
#include <power/regulator.h>
DECLARE_GLOBAL_DATA_PTR;
/*
* Initialisation of the chip is a process of writing certain values into
* certain registers over i2c bus. The chip in fact responds to a range of
* addresses on the i2c bus, so for each written value three parameters are
* required: i2c address, register address and the actual value.
*
* The base address is derived from the device tree, but oddly the chip
* responds on several addresses with different register sets for each.
*/
/**
* ps8622_write() Write a PS8622 eDP bridge i2c register
*
* @param dev I2C device
* @param addr_off offset from the i2c base address for ps8622
* @param reg_addr register address to write
* @param value value to be written
* @return 0 on success, non-0 on failure
*/
static int ps8622_write(struct udevice *dev, unsigned addr_off,
unsigned char reg_addr, unsigned char value)
{
struct dm_i2c_chip *chip = dev_get_parent_platdata(dev);
uint8_t buf[2];
struct i2c_msg msg;
int ret;
msg.addr = chip->chip_addr + addr_off;
msg.flags = 0;
buf[0] = reg_addr;
buf[1] = value;
msg.buf = buf;
msg.len = 2;
ret = dm_i2c_xfer(dev, &msg, 1);
if (ret) {
debug("%s: write failed, reg=%#x, value=%#x, ret=%d\n",
__func__, reg_addr, value, ret);
return ret;
}
return 0;
}
static int ps8622_set_backlight(struct udevice *dev, int percent)
{
int level = percent * 255 / 100;
debug("%s: level=%d\n", __func__, level);
return ps8622_write(dev, 0x01, 0xa7, level);
}
static int ps8622_attach(struct udevice *dev)
{
const uint8_t *params;
struct udevice *reg;
int ret, i, len;
debug("%s: %s\n", __func__, dev->name);
/* set the LDO providing the 1.2V rail to the Parade bridge */
ret = uclass_get_device_by_phandle(UCLASS_REGULATOR, dev,
"power-supply", &reg);
if (!ret) {
ret = regulator_autoset(reg);
} else if (ret != -ENOENT) {
debug("%s: Failed to enable power: ret=%d\n", __func__, ret);
return ret;
}
ret = video_bridge_set_active(dev, true);
if (ret)
return ret;
params = fdt_getprop(gd->fdt_blob, dev->of_offset, "parade,regs", &len);
if (!params || len % 3) {
debug("%s: missing/invalid params=%p, len=%x\n", __func__,
params, len);
return -EINVAL;
}
/* need to wait 20ms after power on before doing I2C writes */
mdelay(20);
for (i = 0; i < len; i += 3) {
ret = ps8622_write(dev, params[i + 0], params[i + 1],
params[i + 2]);
if (ret)
return ret;
}
return 0;
}
static int ps8622_probe(struct udevice *dev)
{
debug("%s\n", __func__);
if (device_get_uclass_id(dev->parent) != UCLASS_I2C)
return -EPROTONOSUPPORT;
return 0;
}
struct video_bridge_ops ps8622_ops = {
.attach = ps8622_attach,
.set_backlight = ps8622_set_backlight,
};
static const struct udevice_id ps8622_ids[] = {
{ .compatible = "parade,ps8622", },
{ .compatible = "parade,ps8625", },
{ }
};
U_BOOT_DRIVER(parade_ps8622) = {
.name = "parade_ps8622",
.id = UCLASS_VIDEO_BRIDGE,
.of_match = ps8622_ids,
.probe = ps8622_probe,
.ops = &ps8622_ops,
};

View File

@@ -0,0 +1,38 @@
/*
* Copyright (C) 2015 Google, Inc
* Written by Simon Glass <sjg@chromium.org>
*
* SPDX-License-Identifier: GPL-2.0+
*/
#include <common.h>
#include <dm.h>
#include <video_bridge.h>
static int ptn3460_attach(struct udevice *dev)
{
int ret;
debug("%s: %s\n", __func__, dev->name);
ret = video_bridge_set_active(dev, true);
if (ret)
return ret;
return 0;
}
struct video_bridge_ops ptn3460_ops = {
.attach = ptn3460_attach,
};
static const struct udevice_id ptn3460_ids[] = {
{ .compatible = "nxp,ptn3460", },
{ }
};
U_BOOT_DRIVER(parade_ptn3460) = {
.name = "nmp_ptn3460",
.id = UCLASS_VIDEO_BRIDGE,
.of_match = ptn3460_ids,
.ops = &ptn3460_ops,
};

View File

@@ -0,0 +1,119 @@
/*
* Copyright (C) 2015 Google, Inc
* Written by Simon Glass <sjg@chromium.org>
*
* SPDX-License-Identifier: GPL-2.0+
*/
#include <common.h>
#include <dm.h>
#include <errno.h>
#include <video_bridge.h>
int video_bridge_set_backlight(struct udevice *dev, int percent)
{
struct video_bridge_ops *ops = video_bridge_get_ops(dev);
if (!ops->set_backlight)
return -ENOSYS;
return ops->set_backlight(dev, percent);
}
int video_bridge_attach(struct udevice *dev)
{
struct video_bridge_ops *ops = video_bridge_get_ops(dev);
if (!ops->attach)
return -ENOSYS;
return ops->attach(dev);
}
int video_bridge_check_attached(struct udevice *dev)
{
struct video_bridge_priv *uc_priv = dev_get_uclass_priv(dev);
struct video_bridge_ops *ops = video_bridge_get_ops(dev);
int ret;
if (!ops->check_attached) {
ret = dm_gpio_get_value(&uc_priv->hotplug);
return ret > 0 ? 0 : ret == 0 ? -ENOTCONN : ret;
}
return ops->check_attached(dev);
}
static int video_bridge_pre_probe(struct udevice *dev)
{
struct video_bridge_priv *uc_priv = dev_get_uclass_priv(dev);
int ret;
debug("%s\n", __func__);
ret = gpio_request_by_name(dev, "sleep-gpios", 0,
&uc_priv->sleep, GPIOD_IS_OUT);
if (ret) {
debug("%s: Could not decode sleep-gpios (%d)\n", __func__, ret);
return ret;
}
/*
* Drop this for now as we do not have driver model pinctrl support
*
* ret = dm_gpio_set_pull(&uc_priv->sleep, GPIO_PULL_NONE);
* if (ret) {
* debug("%s: Could not set sleep pull value\n", __func__);
* return ret;
* }
*/
ret = gpio_request_by_name(dev, "reset-gpios", 0, &uc_priv->reset,
GPIOD_IS_OUT);
if (ret) {
debug("%s: Could not decode reset-gpios (%d)\n", __func__, ret);
return ret;
}
/*
* Drop this for now as we do not have driver model pinctrl support
*
* ret = dm_gpio_set_pull(&uc_priv->reset, GPIO_PULL_NONE);
* if (ret) {
* debug("%s: Could not set reset pull value\n", __func__);
* return ret;
* }
*/
ret = gpio_request_by_name(dev, "hotplug-gpios", 0, &uc_priv->hotplug,
GPIOD_IS_IN);
if (ret && ret != -ENOENT) {
debug("%s: Could not decode hotplug (%d)\n", __func__, ret);
return ret;
}
return 0;
}
int video_bridge_set_active(struct udevice *dev, bool active)
{
struct video_bridge_priv *uc_priv = dev_get_uclass_priv(dev);
int ret;
debug("%s: %d\n", __func__, active);
ret = dm_gpio_set_value(&uc_priv->sleep, !active);
if (ret)
return ret;
if (active) {
ret = dm_gpio_set_value(&uc_priv->reset, true);
if (ret)
return ret;
udelay(10);
ret = dm_gpio_set_value(&uc_priv->reset, false);
}
return ret;
}
UCLASS_DRIVER(video_bridge) = {
.id = UCLASS_VIDEO_BRIDGE,
.name = "video_bridge",
.per_device_auto_alloc_size = sizeof(struct video_bridge_priv),
.pre_probe = video_bridge_pre_probe,
};

View File

@@ -22,8 +22,6 @@
DECLARE_GLOBAL_DATA_PTR;
static struct exynos_dp_platform_data *dp_pd;
void __exynos_set_dp_phy(unsigned int onoff)
{
}
@@ -851,7 +849,6 @@ static unsigned int exynos_dp_config_video(struct edp_device_info *edp_info)
return ret;
}
#ifdef CONFIG_OF_CONTROL
int exynos_dp_parse_dt(const void *blob, struct edp_device_info *edp_info)
{
unsigned int node = fdtdec_next_compatible(blob, 0,
@@ -905,7 +902,6 @@ int exynos_dp_parse_dt(const void *blob, struct edp_device_info *edp_info)
"samsung,color-depth", 0);
return 0;
}
#endif
unsigned int exynos_init_dp(void)
{
@@ -918,16 +914,8 @@ unsigned int exynos_init_dp(void)
return -EFAULT;
}
#ifdef CONFIG_OF_CONTROL
if (exynos_dp_parse_dt(gd->fdt_blob, edp_info))
debug("unable to parse DP DT node\n");
#else
edp_info = dp_pd->edp_dev_info;
if (edp_info == NULL) {
debug("failed to get edp_info data.\n");
return -EFAULT;
}
#endif
exynos_dp_set_base_addr();
@@ -967,17 +955,7 @@ unsigned int exynos_init_dp(void)
return ret;
}
printf("Exynos DP init done\n");
debug("Exynos DP init done\n");
return ret;
}
void exynos_set_dp_platform_data(struct exynos_dp_platform_data *pd)
{
if (pd == NULL) {
debug("pd is NULL\n");
return;
}
dp_pd = pd;
}

View File

@@ -823,7 +823,7 @@ int exynos_dp_read_bytes_from_i2c(unsigned int device_addr,
reg = readl(&dp_regs->aux_rx_comm);
if (reg == AUX_RX_COMM_AUX_DEFER ||
reg == AUX_RX_COMM_I2C_DEFER) {
printf("DP Defer: %d\n\n", reg);
printf("DP Defer: %d\n", reg);
defer = 1;
}
}

View File

@@ -1,231 +0,0 @@
/*
* Copyright (c) 2014 The Chromium OS Authors. All rights reserved.
*
* SPDX-License-Identifier: GPL-2.0+
*/
/*
* This file is a driver for Parade dP<->LVDS bridges. The original submission
* is for the ps8625 chip.
*/
#include <config.h>
#include <common.h>
#include <i2c.h>
#include <fdtdec.h>
#include <asm/gpio.h>
/*
* Initialization of the chip is a process of writing certaing values into
* certain registers over i2c bus. The chip in fact responds to a range of
* addresses on the i2c bus, so for each written value three parameters are
* required: i2c address, register address and the actual value.
*
* The base address is derived from the device tree, only address offset is
* stored in the table below.
*/
/**
* struct reg_data() - data for a parade register write
*
* @addr_off offset from the i2c base address for parade
* @reg_addr register address to write
* @value value to be written
*/
struct reg_data {
uint8_t addr_off;
uint8_t reg;
uint8_t value;
} _packed;
#define END_OF_TABLE 0xff /* Ficticious offset */
static const struct reg_data parade_values[] = {
{0x02, 0xa1, 0x01}, /* HPD low */
/*
* SW setting
* [1:0] SW output 1.2V voltage is lower to 96%
*/
{0x04, 0x14, 0x01},
/*
* RCO SS setting
* [5:4] = b01 0.5%, b10 1%, b11 1.5%
*/
{0x04, 0xe3, 0x20},
{0x04, 0xe2, 0x80}, /* [7] RCO SS enable */
/*
* RPHY Setting
* [3:2] CDR tune wait cycle before
* measure for fine tune b00: 1us,
* 01: 0.5us, 10:2us, 11:4us.
*/
{0x04, 0x8a, 0x0c},
{0x04, 0x89, 0x08}, /* [3] RFD always on */
/*
* CTN lock in/out:
* 20000ppm/80000ppm. Lock out 2
* times.
*/
{0x04, 0x71, 0x2d},
/*
* 2.7G CDR settings
* NOF=40LSB for HBR CDR setting
*/
{0x04, 0x7d, 0x07},
{0x04, 0x7b, 0x00}, /* [1:0] Fmin=+4bands */
{0x04, 0x7a, 0xfd}, /* [7:5] DCO_FTRNG=+-40% */
/*
* 1.62G CDR settings
* [5:2]NOF=64LSB [1:0]DCO scale is 2/5
*/
{0x04, 0xc0, 0x12},
{0x04, 0xc1, 0x92}, /* Gitune=-37% */
{0x04, 0xc2, 0x1c}, /* Fbstep=100% */
{0x04, 0x32, 0x80}, /* [7] LOS signal disable */
/*
* RPIO Setting
* [7:4] LVDS driver bias current :
* 75% (250mV swing)
*/
{0x04, 0x00, 0xb0},
/*
* [7:6] Right-bar GPIO output strength is 8mA
*/
{0x04, 0x15, 0x40},
/* EQ Training State Machine Setting */
{0x04, 0x54, 0x10}, /* RCO calibration start */
/* [4:0] MAX_LANE_COUNT set to one lane */
{0x01, 0x02, 0x81},
/* [4:0] LANE_COUNT_SET set to one lane */
{0x01, 0x21, 0x81},
{0x00, 0x52, 0x20},
{0x00, 0xf1, 0x03}, /* HPD CP toggle enable */
{0x00, 0x62, 0x41},
/* Counter number, add 1ms counter delay */
{0x00, 0xf6, 0x01},
/*
* [6]PWM function control by
* DPCD0040f[7], default is PWM
* block always works.
*/
{0x00, 0x77, 0x06},
/*
* 04h Adjust VTotal tolerance to
* fix the 30Hz no display issue
*/
{0x00, 0x4c, 0x04},
/* DPCD00400='h00, Parade OUI = 'h001cf8 */
{0x01, 0xc0, 0x00},
{0x01, 0xc1, 0x1c}, /* DPCD00401='h1c */
{0x01, 0xc2, 0xf8}, /* DPCD00402='hf8 */
/*
* DPCD403~408 = ASCII code
* D2SLV5='h4432534c5635
*/
{0x01, 0xc3, 0x44},
{0x01, 0xc4, 0x32}, /* DPCD404 */
{0x01, 0xc5, 0x53}, /* DPCD405 */
{0x01, 0xc6, 0x4c}, /* DPCD406 */
{0x01, 0xc7, 0x56}, /* DPCD407 */
{0x01, 0xc8, 0x35}, /* DPCD408 */
/*
* DPCD40A, Initial Code major revision
* '01'
*/
{0x01, 0xca, 0x01},
/* DPCD40B, Initial Code minor revision '05' */
{0x01, 0xcb, 0x05},
/* DPCD720, Select internal PWM */
{0x01, 0xa5, 0xa0},
/*
* FFh for 100% PWM of brightness, 0h for 0%
* brightness
*/
{0x01, 0xa7, 0xff},
/*
* Set LVDS output as 6bit-VESA mapping,
* single LVDS channel
*/
{0x01, 0xcc, 0x13},
/* Enable SSC set by register */
{0x02, 0xb1, 0x20},
/*
* Set SSC enabled and +/-1% central
* spreading
*/
{0x04, 0x10, 0x16},
/* MPU Clock source: LC => RCO */
{0x04, 0x59, 0x60},
{0x04, 0x54, 0x14}, /* LC -> RCO */
{0x02, 0xa1, 0x91}, /* HPD high */
{END_OF_TABLE}
};
/**
* Write values table into the Parade eDP bridge
*
* @return 0 on success, non-0 on failure
*/
static int parade_write_regs(int base_addr, const struct reg_data *table)
{
int ret = 0;
while (!ret && (table->addr_off != END_OF_TABLE)) {
ret = i2c_write(base_addr + table->addr_off,
table->reg, 1,
(uint8_t *)&table->value,
sizeof(table->value));
table++;
}
return ret;
}
int parade_init(const void *blob)
{
struct gpio_desc rst_gpio;
struct gpio_desc slp_gpio;
int bus, old_bus;
int parent;
int node;
int addr;
int ret;
node = fdtdec_next_compatible(blob, 0, COMPAT_PARADE_PS8625);
if (node < 0)
return 0;
parent = fdt_parent_offset(blob, node);
if (parent < 0) {
debug("%s: Could not find parent i2c node\n", __func__);
return -1;
}
addr = fdtdec_get_int(blob, node, "reg", -1);
if (addr < 0) {
debug("%s: Could not find i2c address\n", __func__);
return -1;
}
gpio_request_by_name_nodev(blob, node, "sleep-gpio", 0, &slp_gpio,
GPIOD_IS_OUT | GPIOD_IS_OUT_ACTIVE);
mdelay(10);
gpio_request_by_name_nodev(blob, node, "reset-gpio", 0, &rst_gpio,
GPIOD_IS_OUT | GPIOD_IS_OUT_ACTIVE);
bus = i2c_get_bus_num_fdt(parent);
old_bus = i2c_get_bus_num();
debug("%s: Using i2c bus %d\n", __func__, bus);
/*
* TODO(sjg@chromium.org): Hmmm we seem to need some sort of delay
* here.
*/
mdelay(40);
i2c_set_bus_num(bus);
ret = parade_write_regs(addr, parade_values);
i2c_set_bus_num(old_bus);
return ret;
}

View File

@@ -92,7 +92,7 @@ void lcd_ctrl_init(void *lcdbase)
/* Enable flushing after LCD writes if requested */
lcd_set_flush_dcache(config.cache_type & FDT_LCD_CACHE_FLUSH);
debug("LCD frame buffer at %08X\n", disp_config->frame_buffer);
debug("LCD frame buffer at %pa\n", &disp_config->frame_buffer);
}
ulong calc_fbsize(void)