Commit f48eaf01b2f7212987166aae970b895c7e215466

Authored by Simon Glass
1 parent cc456bd7df

cros_ec: Support the LDO access method used by spring

Add a driver to support the special LDO access used by spring. This is a
custom method in the cros_ec protocol - it does not use an I2C
pass-through.

There are two implementation choices:

1. Write a special LDO driver which can talk across the EC. Duplicate all
the logic from TPS65090 for retrying when the LDO fails to come up.

2. Write a special I2C bus driver which pretends to be a TPS65090 and
transfers reads and writes using the LDO message.

Either is distasteful. The latter method is chosen since it results in less
code duplication and a fairly simple (30-line) implementation of the core
logic.

The crosec 'ldo' subcommand could be removed (since i2c md/mw will work
instead) but is retained as a convenience.

Signed-off-by: Simon Glass <sjg@chromium.org>

Showing 5 changed files with 104 additions and 12 deletions Side-by-side Diff

... ... @@ -29,6 +29,19 @@
29 29 I2C or LPC). Some Chromebooks use this when the hardware design
30 30 does not allow direct access to the main PMIC from the AP.
31 31  
  32 +config I2C_CROS_EC_LDO
  33 + bool "Provide access to LDOs on the Chrome OS EC"
  34 + depends on CROS_EC
  35 + ---help---
  36 + On many Chromebooks the main PMIC is inaccessible to the AP. This is
  37 + often dealt with by using an I2C pass-through interface provided by
  38 + the EC. On some unfortunate models (e.g. Spring) the pass-through
  39 + is not available, and an LDO message is available instead. This
  40 + option enables a driver which provides very basic access to those
  41 + regulators, via the EC. We implement this as an I2C bus which
  42 + emulates just the TPS65090 messages we know about. This is done to
  43 + avoid duplicating the logic in the TPS65090 regulator driver for
  44 + enabling/disabling an LDO.
32 45  
33 46 config DM_I2C_GPIO
34 47 bool "Enable Driver Model for software emulated I2C bus driver"
drivers/i2c/Makefile
... ... @@ -8,6 +8,7 @@
8 8 obj-$(CONFIG_DM_I2C_COMPAT) += i2c-uclass-compat.o
9 9 obj-$(CONFIG_DM_I2C_GPIO) += i2c-gpio.o
10 10 obj-$(CONFIG_I2C_CROS_EC_TUNNEL) += cros_ec_tunnel.o
  11 +obj-$(CONFIG_I2C_CROS_EC_LDO) += cros_ec_ldo.o
11 12  
12 13 obj-$(CONFIG_SYS_I2C_ADI) += adi_i2c.o
13 14 obj-$(CONFIG_I2C_MV) += mv_i2c.o
drivers/i2c/cros_ec_ldo.c
  1 +/*
  2 + * Copyright (c) 2015 Google, Inc
  3 + * Written by Simon Glass <sjg@chromium.org>
  4 + *
  5 + * SPDX-License-Identifier: GPL-2.0+
  6 + */
  7 +
  8 +#include <common.h>
  9 +#include <dm.h>
  10 +#include <cros_ec.h>
  11 +#include <errno.h>
  12 +#include <i2c.h>
  13 +#include <power/tps65090.h>
  14 +
  15 +static int cros_ec_ldo_set_bus_speed(struct udevice *dev, unsigned int speed)
  16 +{
  17 + return 0;
  18 +}
  19 +
  20 +static int cros_ec_ldo_xfer(struct udevice *dev, struct i2c_msg *msg,
  21 + int nmsgs)
  22 +{
  23 + bool is_read = nmsgs > 1;
  24 + int fet_id, ret;
  25 +
  26 + /*
  27 + * Look for reads and writes of the LDO registers. In either case the
  28 + * first message is a write with the register number as the first byte.
  29 + */
  30 + if (!nmsgs || !msg->len || (msg->flags & I2C_M_RD)) {
  31 + debug("%s: Invalid message\n", __func__);
  32 + goto err;
  33 + }
  34 +
  35 + fet_id = msg->buf[0] - REG_FET_BASE;
  36 + if (fet_id < 1 || fet_id > MAX_FET_NUM) {
  37 + debug("%s: Invalid FET %d\n", __func__, fet_id);
  38 + goto err;
  39 + }
  40 +
  41 + if (is_read) {
  42 + uint8_t state;
  43 +
  44 + ret = cros_ec_get_ldo(dev->parent, fet_id, &state);
  45 + if (!ret)
  46 + msg[1].buf[0] = state ?
  47 + FET_CTRL_ENFET | FET_CTRL_PGFET : 0;
  48 + } else {
  49 + bool on = msg->buf[1] & FET_CTRL_ENFET;
  50 +
  51 + ret = cros_ec_set_ldo(dev->parent, fet_id, on);
  52 + }
  53 +
  54 + return ret;
  55 +
  56 +err:
  57 + /* Indicate that the message is unimplemented */
  58 + return -ENOSYS;
  59 +}
  60 +
  61 +static const struct dm_i2c_ops cros_ec_i2c_ops = {
  62 + .xfer = cros_ec_ldo_xfer,
  63 + .set_bus_speed = cros_ec_ldo_set_bus_speed,
  64 +};
  65 +
  66 +static const struct udevice_id cros_ec_i2c_ids[] = {
  67 + { .compatible = "google,cros-ec-ldo-tunnel" },
  68 + { }
  69 +};
  70 +
  71 +U_BOOT_DRIVER(cros_ec_ldo) = {
  72 + .name = "cros_ec_ldo_tunnel",
  73 + .id = UCLASS_I2C,
  74 + .of_match = cros_ec_i2c_ids,
  75 + .per_child_auto_alloc_size = sizeof(struct dm_i2c_chip),
  76 + .ops = &cros_ec_i2c_ops,
  77 +};
drivers/misc/cros_ec.c
... ... @@ -931,31 +931,32 @@
931 931 return 0;
932 932 }
933 933  
934   -int cros_ec_set_ldo(struct cros_ec_dev *dev, uint8_t index, uint8_t state)
  934 +int cros_ec_set_ldo(struct udevice *dev, uint8_t index, uint8_t state)
935 935 {
  936 + struct cros_ec_dev *cdev = dev_get_uclass_priv(dev);
936 937 struct ec_params_ldo_set params;
937 938  
938 939 params.index = index;
939 940 params.state = state;
940 941  
941   - if (ec_command_inptr(dev, EC_CMD_LDO_SET, 0,
942   - &params, sizeof(params),
943   - NULL, 0))
  942 + if (ec_command_inptr(cdev, EC_CMD_LDO_SET, 0, &params, sizeof(params),
  943 + NULL, 0))
944 944 return -1;
945 945  
946 946 return 0;
947 947 }
948 948  
949   -int cros_ec_get_ldo(struct cros_ec_dev *dev, uint8_t index, uint8_t *state)
  949 +int cros_ec_get_ldo(struct udevice *dev, uint8_t index, uint8_t *state)
950 950 {
  951 + struct cros_ec_dev *cdev = dev_get_uclass_priv(dev);
951 952 struct ec_params_ldo_get params;
952 953 struct ec_response_ldo_get *resp;
953 954  
954 955 params.index = index;
955 956  
956   - if (ec_command_inptr(dev, EC_CMD_LDO_GET, 0,
957   - &params, sizeof(params),
958   - (uint8_t **)&resp, sizeof(*resp)) != sizeof(*resp))
  957 + if (ec_command_inptr(cdev, EC_CMD_LDO_GET, 0, &params, sizeof(params),
  958 + (uint8_t **)&resp, sizeof(*resp)) !=
  959 + sizeof(*resp))
959 960 return -1;
960 961  
961 962 *state = resp->state;
962 963  
... ... @@ -1681,9 +1682,9 @@
1681 1682 state = simple_strtoul(argv[3], &endp, 10);
1682 1683 if (*argv[3] == 0 || *endp != 0)
1683 1684 return CMD_RET_USAGE;
1684   - ret = cros_ec_set_ldo(dev, index, state);
  1685 + ret = cros_ec_set_ldo(udev, index, state);
1685 1686 } else {
1686   - ret = cros_ec_get_ldo(dev, index, &state);
  1687 + ret = cros_ec_get_ldo(udev, index, &state);
1687 1688 if (!ret) {
1688 1689 printf("LDO%d: %s\n", index,
1689 1690 state == EC_LDO_STATE_ON ?
... ... @@ -350,7 +350,7 @@
350 350 * @param state new state of the LDO/FET : EC_LDO_STATE_ON|OFF
351 351 * @return 0 if ok, -1 on error
352 352 */
353   -int cros_ec_set_ldo(struct cros_ec_dev *dev, uint8_t index, uint8_t state);
  353 +int cros_ec_set_ldo(struct udevice *dev, uint8_t index, uint8_t state);
354 354  
355 355 /**
356 356 * Read back a LDO / FET current state.
... ... @@ -360,7 +360,7 @@
360 360 * @param state current state of the LDO/FET : EC_LDO_STATE_ON|OFF
361 361 * @return 0 if ok, -1 on error
362 362 */
363   -int cros_ec_get_ldo(struct cros_ec_dev *dev, uint8_t index, uint8_t *state);
  363 +int cros_ec_get_ldo(struct udevice *dev, uint8_t index, uint8_t *state);
364 364  
365 365 /**
366 366 * Get access to the error reported when cros_ec_board_init() was called