dm: cros_ec: Add support for driver model

Add support for driver model if enabled. This involves minimal changes
to the code, mostly just plumbing around the edges.

Signed-off-by: Simon Glass <sjg@chromium.org>
Acked-by: Jagannadha Sutradharudu Teki <jagannadh.teki@gmail.com>
diff --git a/common/cros_ec.c b/common/cros_ec.c
index b8ce1b5..bb299bc 100644
--- a/common/cros_ec.c
+++ b/common/cros_ec.c
@@ -10,25 +10,44 @@
 
 #include <common.h>
 #include <cros_ec.h>
+#include <dm.h>
+#include <errno.h>
+
 DECLARE_GLOBAL_DATA_PTR;
 
+#ifndef CONFIG_DM_CROS_EC
 struct local_info {
 	struct cros_ec_dev *cros_ec_dev;	/* Pointer to cros_ec device */
 	int cros_ec_err;			/* Error for cros_ec, 0 if ok */
 };
 
 static struct local_info local;
+#endif
 
 struct cros_ec_dev *board_get_cros_ec_dev(void)
 {
+#ifdef CONFIG_DM_CROS_EC
+	struct udevice *dev;
+	int ret;
+
+	ret = uclass_get_device(UCLASS_CROS_EC, 0, &dev);
+	if (ret) {
+		debug("%s: Error %d\n", __func__, ret);
+		return NULL;
+	}
+	return dev->uclass_priv;
+#else
 	return local.cros_ec_dev;
+#endif
 }
 
 static int board_init_cros_ec_devices(const void *blob)
 {
+#ifndef CONFIG_DM_CROS_EC
 	local.cros_ec_err = cros_ec_init(blob, &local.cros_ec_dev);
 	if (local.cros_ec_err)
 		return -1;  /* Will report in board_late_init() */
+#endif
 
 	return 0;
 }
@@ -40,5 +59,16 @@
 
 int cros_ec_get_error(void)
 {
+#ifdef CONFIG_DM_CROS_EC
+	struct udevice *dev;
+	int ret;
+
+	ret = uclass_get_device(UCLASS_CROS_EC, 0, &dev);
+	if (ret && ret != -ENODEV)
+		return ret;
+
+	return 0;
+#else
 	return local.cros_ec_err;
+#endif
 }
diff --git a/drivers/misc/cros_ec.c b/drivers/misc/cros_ec.c
index 068373b..521edfd 100644
--- a/drivers/misc/cros_ec.c
+++ b/drivers/misc/cros_ec.c
@@ -16,6 +16,7 @@
 
 #include <common.h>
 #include <command.h>
+#include <dm.h>
 #include <i2c.h>
 #include <cros_ec.h>
 #include <fdtdec.h>
@@ -24,6 +25,8 @@
 #include <asm/errno.h>
 #include <asm/io.h>
 #include <asm-generic/gpio.h>
+#include <dm/device-internal.h>
+#include <dm/uclass-internal.h>
 
 #ifdef DEBUG_TRACE
 #define debug_trace(fmt, b...)	debug(fmt, #b)
@@ -38,7 +41,9 @@
 	CROS_EC_CMD_HASH_TIMEOUT_MS = 2000,
 };
 
+#ifndef CONFIG_DM_CROS_EC
 static struct cros_ec_dev static_dev, *last_dev;
+#endif
 
 DECLARE_GLOBAL_DATA_PTR;
 
@@ -204,6 +209,9 @@
 			       const void *dout, int dout_len,
 			       uint8_t **dinp, int din_len)
 {
+#ifdef CONFIG_DM_CROS_EC
+	struct dm_cros_ec_ops *ops;
+#endif
 	int out_bytes, in_bytes;
 	int rv;
 
@@ -218,6 +226,10 @@
 	if (in_bytes < 0)
 		return in_bytes;
 
+#ifdef CONFIG_DM_CROS_EC
+	ops = dm_cros_ec_get_ops(dev->dev);
+	rv = ops->packet(dev->dev, out_bytes, in_bytes);
+#else
 	switch (dev->interface) {
 #ifdef CONFIG_CROS_EC_SPI
 	case CROS_EC_IF_SPI:
@@ -235,6 +247,7 @@
 		debug("%s: Unsupported interface\n", __func__);
 		rv = -1;
 	}
+#endif
 	if (rv < 0)
 		return rv;
 
@@ -246,6 +259,9 @@
 			const void *dout, int dout_len,
 			uint8_t **dinp, int din_len)
 {
+#ifdef CONFIG_DM_CROS_EC
+	struct dm_cros_ec_ops *ops;
+#endif
 	int ret = -1;
 
 	/* Handle protocol version 3 support */
@@ -254,6 +270,11 @@
 					   dout, dout_len, dinp, din_len);
 	}
 
+#ifdef CONFIG_DM_CROS_EC
+	ops = dm_cros_ec_get_ops(dev->dev);
+	ret = ops->command(dev->dev, cmd, cmd_version,
+			   (const uint8_t *)dout, dout_len, dinp, din_len);
+#else
 	switch (dev->interface) {
 #ifdef CONFIG_CROS_EC_SPI
 	case CROS_EC_IF_SPI:
@@ -280,6 +301,7 @@
 	default:
 		ret = -1;
 	}
+#endif
 
 	return ret;
 }
@@ -990,6 +1012,7 @@
 	return 0;
 }
 
+#ifndef CONFIG_DM_CROS_EC
 /**
  * Decode EC interface details from the device tree and allocate a suitable
  * device.
@@ -1055,11 +1078,61 @@
 
 	return 0;
 }
+#endif
 
-int cros_ec_init(const void *blob, struct cros_ec_dev **cros_ecp)
+#ifdef CONFIG_DM_CROS_EC
+int cros_ec_register(struct udevice *dev)
 {
+	struct cros_ec_dev *cdev = dev->uclass_priv;
+	const void *blob = gd->fdt_blob;
+	int node = dev->of_offset;
 	char id[MSG_BYTES];
+
+	cdev->dev = dev;
+	fdtdec_decode_gpio(blob, node, "ec-interrupt", &cdev->ec_int);
+	cdev->optimise_flash_write = fdtdec_get_bool(blob, node,
+						     "optimise-flash-write");
+
+	/* we will poll the EC interrupt line */
+	fdtdec_setup_gpio(&cdev->ec_int);
+	if (fdt_gpio_isvalid(&cdev->ec_int)) {
+		gpio_request(cdev->ec_int.gpio, "cros-ec-irq");
+		gpio_direction_input(cdev->ec_int.gpio);
+	}
+
+	if (cros_ec_check_version(cdev)) {
+		debug("%s: Could not detect CROS-EC version\n", __func__);
+		return -CROS_EC_ERR_CHECK_VERSION;
+	}
+
+	if (cros_ec_read_id(cdev, id, sizeof(id))) {
+		debug("%s: Could not read KBC ID\n", __func__);
+		return -CROS_EC_ERR_READ_ID;
+	}
+
+	/* Remember this device for use by the cros_ec command */
+	debug("Google Chrome EC CROS-EC driver ready, id '%s'\n", id);
+
+	return 0;
+}
+#else
+int cros_ec_init(const void *blob, struct cros_ec_dev **cros_ecp)
+{
 	struct cros_ec_dev *dev;
+	char id[MSG_BYTES];
+#ifdef CONFIG_DM_CROS_EC
+	struct udevice *udev;
+	int ret;
+
+	ret = uclass_find_device(UCLASS_CROS_EC, 0, &udev);
+	if (!ret)
+		device_remove(udev);
+	ret = uclass_get_device(UCLASS_CROS_EC, 0, &udev);
+	if (ret)
+		return ret;
+	dev = udev->uclass_priv;
+	return 0;
+#else
 	int node = 0;
 
 	*cros_ecp = NULL;
@@ -1108,11 +1181,14 @@
 	default:
 		return 0;
 	}
+#endif
 
 	/* we will poll the EC interrupt line */
 	fdtdec_setup_gpio(&dev->ec_int);
-	if (fdt_gpio_isvalid(&dev->ec_int))
+	if (fdt_gpio_isvalid(&dev->ec_int)) {
+		gpio_request(dev->ec_int.gpio, "cros-ec-irq");
 		gpio_direction_input(dev->ec_int.gpio);
+	}
 
 	if (cros_ec_check_version(dev)) {
 		debug("%s: Could not detect CROS-EC version\n", __func__);
@@ -1125,11 +1201,15 @@
 	}
 
 	/* Remember this device for use by the cros_ec command */
-	last_dev = *cros_ecp = dev;
+	*cros_ecp = dev;
+#ifndef CONFIG_DM_CROS_EC
+	last_dev = dev;
+#endif
 	debug("Google Chrome EC CROS-EC driver ready, id '%s'\n", id);
 
 	return 0;
 }
+#endif
 
 int cros_ec_decode_region(int argc, char * const argv[])
 {
@@ -1147,15 +1227,10 @@
 	return -1;
 }
 
-int cros_ec_decode_ec_flash(const void *blob, struct fdt_cros_ec *config)
+int cros_ec_decode_ec_flash(const void *blob, int node,
+			    struct fdt_cros_ec *config)
 {
-	int flash_node, node;
-
-	node = fdtdec_next_compatible(blob, 0, COMPAT_GOOGLE_CROS_EC);
-	if (node < 0) {
-		debug("Failed to find chrome-ec node'\n");
-		return -1;
-	}
+	int flash_node;
 
 	flash_node = fdt_subnode_offset(blob, node, "flash");
 	if (flash_node < 0) {
@@ -1516,7 +1591,10 @@
 
 static int do_cros_ec(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
 {
-	struct cros_ec_dev *dev = last_dev;
+	struct cros_ec_dev *dev;
+#ifdef CONFIG_DM_CROS_EC
+	struct udevice *udev;
+#endif
 	const char *cmd;
 	int ret = 0;
 
@@ -1525,19 +1603,31 @@
 
 	cmd = argv[1];
 	if (0 == strcmp("init", cmd)) {
+#ifndef CONFIG_DM_CROS_EC
 		ret = cros_ec_init(gd->fdt_blob, &dev);
 		if (ret) {
 			printf("Could not init cros_ec device (err %d)\n", ret);
 			return 1;
 		}
+#endif
 		return 0;
 	}
 
+#ifdef CONFIG_DM_CROS_EC
+	ret = uclass_get_device(UCLASS_CROS_EC, 0, &udev);
+	if (ret) {
+		printf("Cannot get cros-ec device (err=%d)\n", ret);
+		return 1;
+	}
+	dev = udev->uclass_priv;
+#else
 	/* Just use the last allocated device; there should be only one */
 	if (!last_dev) {
 		printf("No CROS-EC device available\n");
 		return 1;
 	}
+	dev = last_dev;
+#endif
 	if (0 == strcmp("id", cmd)) {
 		char id[MSG_BYTES];
 
@@ -1794,3 +1884,11 @@
 	"crosec i2c mw chip address[.0, .1, .2] value [count] - write to I2C passthru (fill)"
 );
 #endif
+
+#ifdef CONFIG_DM_CROS_EC
+UCLASS_DRIVER(cros_ec) = {
+	.id		= UCLASS_CROS_EC,
+	.name		= "cros_ec",
+	.per_device_auto_alloc_size = sizeof(struct cros_ec_dev),
+};
+#endif
diff --git a/drivers/misc/cros_ec_sandbox.c b/drivers/misc/cros_ec_sandbox.c
index 8a04af5..431cf26 100644
--- a/drivers/misc/cros_ec_sandbox.c
+++ b/drivers/misc/cros_ec_sandbox.c
@@ -525,8 +525,13 @@
 	int node;
 	int err;
 
-	state = &s_state;
-	err = cros_ec_decode_ec_flash(blob, &ec->ec_config);
+	node = fdtdec_next_compatible(blob, 0, COMPAT_GOOGLE_CROS_EC);
+	if (node < 0) {
+		debug("Failed to find chrome-ec node'\n");
+		return -1;
+	}
+
+	err = cros_ec_decode_ec_flash(blob, node, &ec->ec_config);
 	if (err)
 		return err;
 
diff --git a/include/cros_ec.h b/include/cros_ec.h
index 1e4d8db..9e13146 100644
--- a/include/cros_ec.h
+++ b/include/cros_ec.h
@@ -14,6 +14,7 @@
 #include <fdtdec.h>
 #include <cros_ec_message.h>
 
+#ifndef CONFIG_DM_CROS_EC
 /* Which interface is the device on? */
 enum cros_ec_interface_t {
 	CROS_EC_IF_NONE,
@@ -22,9 +23,13 @@
 	CROS_EC_IF_LPC,	/* Intel Low Pin Count interface */
 	CROS_EC_IF_SANDBOX,
 };
+#endif
 
 /* Our configuration information */
 struct cros_ec_dev {
+#ifdef CONFIG_DM_CROS_EC
+	struct udevice *dev;		/* Transport device */
+#else
 	enum cros_ec_interface_t interface;
 	struct spi_slave *spi;		/* Our SPI slave, if using SPI */
 	int node;                       /* Our node */
@@ -33,6 +38,7 @@
 	unsigned int addr;		/* Device address (for I2C) */
 	unsigned int bus_num;		/* Bus number (for I2C) */
 	unsigned int max_frequency;	/* Maximum interface frequency */
+#endif
 	struct fdt_gpio_state ec_int;	/* GPIO used as EC interrupt line */
 	int protocol_version;           /* Protocol version to use */
 	int optimise_flash_write;	/* Don't write erased flash blocks */
@@ -233,6 +239,22 @@
  */
 struct cros_ec_dev *board_get_cros_ec_dev(void);
 
+#ifdef CONFIG_DM_CROS_EC
+
+struct dm_cros_ec_ops {
+	int (*check_version)(struct udevice *dev);
+	int (*command)(struct udevice *dev, uint8_t cmd, int cmd_version,
+		       const uint8_t *dout, int dout_len,
+		       uint8_t **dinp, int din_len);
+	int (*packet)(struct udevice *dev, int out_bytes, int in_bytes);
+};
+
+#define dm_cros_ec_get_ops(dev) \
+		((struct dm_cros_ec_ops *)(dev)->driver->ops)
+
+int cros_ec_register(struct udevice *dev);
+
+#else /* !CONFIG_DM_CROS_EC */
 
 /* Internal interfaces */
 int cros_ec_i2c_init(struct cros_ec_dev *dev, const void *blob);
@@ -336,6 +358,7 @@
 int cros_ec_spi_packet(struct cros_ec_dev *dev, int out_bytes, int in_bytes);
 int cros_ec_sandbox_packet(struct cros_ec_dev *dev, int out_bytes,
 			   int in_bytes);
+#endif
 
 /**
  * Dump a block of data for a command.
@@ -489,9 +512,11 @@
  * Returns information from the FDT about the Chrome EC flash
  *
  * @param blob		FDT blob to use
+ * @param node		Node offset to read from
  * @param config	Structure to use to return information
  */
-int cros_ec_decode_ec_flash(const void *blob, struct fdt_cros_ec *config);
+int cros_ec_decode_ec_flash(const void *blob, int node,
+			    struct fdt_cros_ec *config);
 
 /**
  * Check the current keyboard state, in case recovery mode is requested.
diff --git a/include/dm/uclass-id.h b/include/dm/uclass-id.h
index c92adb4..0ea7a6f 100644
--- a/include/dm/uclass-id.h
+++ b/include/dm/uclass-id.h
@@ -26,6 +26,7 @@
 	UCLASS_SPI,		/* SPI bus */
 	UCLASS_SPI_GENERIC,	/* Generic SPI flash target */
 	UCLASS_SPI_FLASH,	/* SPI flash */
+	UCLASS_CROS_EC,	/* Chrome OS EC */
 
 	UCLASS_COUNT,
 	UCLASS_INVALID = -1,