Merge git://git.denx.de/u-boot-dm
This commit is contained in:
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
259
drivers/core/devres.c
Normal 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);
|
||||
}
|
||||
@@ -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[] = {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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
77
drivers/i2c/cros_ec_ldo.c
Normal 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,
|
||||
};
|
||||
41
drivers/i2c/cros_ec_tunnel.c
Normal file
41
drivers/i2c/cros_ec_tunnel.c
Normal 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,
|
||||
};
|
||||
@@ -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
17
drivers/i2c/muxes/Kconfig
Normal 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.
|
||||
7
drivers/i2c/muxes/Makefile
Normal file
7
drivers/i2c/muxes/Makefile
Normal 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
|
||||
147
drivers/i2c/muxes/i2c-arb-gpio-challenge.c
Normal file
147
drivers/i2c/muxes/i2c-arb-gpio-challenge.c
Normal 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),
|
||||
};
|
||||
198
drivers/i2c/muxes/i2c-mux-uclass.c
Normal file
198
drivers/i2c/muxes/i2c-mux-uclass.c
Normal 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,
|
||||
};
|
||||
@@ -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 */
|
||||
|
||||
@@ -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,
|
||||
¶ms, sizeof(params),
|
||||
NULL, 0))
|
||||
if (ec_command_inptr(cdev, EC_CMD_LDO_SET, 0, ¶ms, 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,
|
||||
¶ms, sizeof(params),
|
||||
(uint8_t **)&resp, sizeof(*resp)) != sizeof(*resp))
|
||||
if (ec_command_inptr(cdev, EC_CMD_LDO_GET, 0, ¶ms, 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 = ¶ms.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,
|
||||
};
|
||||
|
||||
@@ -202,6 +202,6 @@ int exynos_mmc_init(const void *blob)
|
||||
|
||||
process_nodes(blob, node_list, count);
|
||||
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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, ®))
|
||||
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, ®);
|
||||
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;
|
||||
}
|
||||
@@ -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, ®))
|
||||
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, ®);
|
||||
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;
|
||||
}
|
||||
95
drivers/power/pmic/s5m8767.c
Normal file
95
drivers/power/pmic/s5m8767.c
Normal 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,
|
||||
};
|
||||
94
drivers/power/pmic/tps65090.c
Normal file
94
drivers/power/pmic/tps65090.c
Normal 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,
|
||||
};
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
269
drivers/power/regulator/s5m8767.c
Normal file
269
drivers/power/regulator/s5m8767.c
Normal 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,
|
||||
};
|
||||
138
drivers/power/regulator/tps65090_regulator.c
Normal file
138
drivers/power/regulator/tps65090_regulator.c
Normal 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,
|
||||
};
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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(ð->enetaddr[0]);
|
||||
u32 addr_hi = __get_unaligned_le16(ð->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
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
|
||||
#include <common.h>
|
||||
#include <dm.h>
|
||||
#include <errno.h>
|
||||
#include <malloc.h>
|
||||
#include <usb.h>
|
||||
#include <dm/device-internal.h>
|
||||
|
||||
@@ -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 = ®s->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 = ®s->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(®s->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(®s->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
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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/
|
||||
|
||||
27
drivers/video/bridge/Kconfig
Normal file
27
drivers/video/bridge/Kconfig
Normal 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.
|
||||
9
drivers/video/bridge/Makefile
Normal file
9
drivers/video/bridge/Makefile
Normal 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
|
||||
134
drivers/video/bridge/ps862x.c
Normal file
134
drivers/video/bridge/ps862x.c
Normal 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", ®);
|
||||
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,
|
||||
};
|
||||
38
drivers/video/bridge/ptn3460.c
Normal file
38
drivers/video/bridge/ptn3460.c
Normal 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,
|
||||
};
|
||||
119
drivers/video/bridge/video-bridge-uclass.c
Normal file
119
drivers/video/bridge/video-bridge-uclass.c
Normal 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,
|
||||
};
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user