spmi-sandbox.c 3.68 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
/*
 * Sample SPMI bus driver
 *
 * It emulates bus with single pm8916-like pmic that has only GPIO reigsters.
 *
 * (C) Copyright 2015 Mateusz Kulikowski <mateusz.kulikowski@gmail.com>
 *
 * SPDX-License-Identifier:	GPL-2.0+
 */

#include <common.h>
#include <dm.h>
#include <errno.h>
#include <spmi/spmi.h>
#include <asm/gpio.h>
#include <asm/io.h>

DECLARE_GLOBAL_DATA_PTR;

#define EMUL_GPIO_PID_START 0xC0
#define EMUL_GPIO_PID_END   0xC3

#define EMUL_GPIO_COUNT 4

#define EMUL_GPIO_REG_END 0x46 /* Last valid register */

#define EMUL_PERM_R 0x1
#define EMUL_PERM_W 0x2
#define EMUL_PERM_RW (EMUL_PERM_R | EMUL_PERM_W)

struct sandbox_emul_fake_regs {
	u8 value;
	u8 access_mask;
	u8 perms; /* Access permissions */
};

struct sandbox_emul_gpio {
38 39
	/* Fake registers - need one more entry as REG_END is valid address. */
	struct sandbox_emul_fake_regs r[EMUL_GPIO_REG_END + 1];
40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158
};

struct sandbox_spmi_priv {
	struct sandbox_emul_gpio gpios[EMUL_GPIO_COUNT];
};

/* Check if valid register was requested */
static bool check_address_valid(int usid, int pid, int off)
{
	if (usid != 0)
		return false;
	if (pid < EMUL_GPIO_PID_START || pid > EMUL_GPIO_PID_END)
		return false;
	if (off > EMUL_GPIO_REG_END)
		return false;
	return true;
}

static int sandbox_spmi_write(struct udevice *dev, int usid, int pid, int off,
			  uint8_t val)
{
	struct sandbox_spmi_priv *priv = dev_get_priv(dev);
	struct sandbox_emul_fake_regs *regs;

	if (!check_address_valid(usid, pid, off))
		return -EIO;

	regs = priv->gpios[pid & 0x3].r; /* Last 3 bits of pid are gpio # */

	switch (off) {
	case 0x40: /* Control */
		val &= regs[off].access_mask;
		if (((val & 0x30) == 0x10) || ((val & 0x30) == 0x20)) {
			/* out/inout - set status register */
			regs[0x8].value &= ~0x1;
			regs[0x8].value |= val & 0x1;
		}
		break;
	default:
		if (regs[off].perms & EMUL_PERM_W)
			regs[off].value = val & regs[off].access_mask;
	}
	return 0;
}

static int sandbox_spmi_read(struct udevice *dev, int usid, int pid, int off)
{
	struct sandbox_spmi_priv *priv = dev_get_priv(dev);
	struct sandbox_emul_fake_regs *regs;

	if (!check_address_valid(usid, pid, off))
		return -EIO;

	regs = priv->gpios[pid & 0x3].r; /* Last 3 bits of pid are gpio # */

	if (regs[0x46].value == 0) /* Block disabled */
		return 0;

	switch (off) {
	case 0x8: /* Status */
		if (regs[0x46].value == 0) /* Block disabled */
			return 0;
		return regs[off].value;
	default:
		if (regs[off].perms & EMUL_PERM_R)
			return regs[off].value;
		else
			return 0;
	}
}

static struct dm_spmi_ops sandbox_spmi_ops = {
	.read = sandbox_spmi_read,
	.write = sandbox_spmi_write,
};

static int sandbox_spmi_probe(struct udevice *dev)
{
	struct sandbox_spmi_priv *priv = dev_get_priv(dev);
	int i;

	for (i = 0; i < EMUL_GPIO_COUNT; ++i) {
		struct sandbox_emul_fake_regs *regs = priv->gpios[i].r;
		regs[4].perms = EMUL_PERM_R;
		regs[4].value = 0x10;
		regs[5].perms = EMUL_PERM_R;
		regs[5].value = 0x5;
		regs[8].access_mask = 0x81;
		regs[8].perms = EMUL_PERM_RW;
		regs[0x40].access_mask = 0x7F;
		regs[0x40].perms = EMUL_PERM_RW;
		regs[0x41].access_mask = 7;
		regs[0x41].perms = EMUL_PERM_RW;
		regs[0x42].access_mask = 7;
		regs[0x42].perms = EMUL_PERM_RW;
		regs[0x42].value = 0x4;
		regs[0x45].access_mask = 0x3F;
		regs[0x45].perms = EMUL_PERM_RW;
		regs[0x45].value = 0x1;
		regs[0x46].access_mask = 0x80;
		regs[0x46].perms = EMUL_PERM_RW;
		regs[0x46].value = 0x80;
	}
	return 0;
}

static const struct udevice_id sandbox_spmi_ids[] = {
	{ .compatible = "sandbox,spmi" },
	{ }
};

U_BOOT_DRIVER(msm_spmi) = {
	.name = "sandbox_spmi",
	.id = UCLASS_SPMI,
	.of_match = sandbox_spmi_ids,
	.ops = &sandbox_spmi_ops,
	.probe = sandbox_spmi_probe,
	.priv_auto_alloc_size = sizeof(struct sandbox_spmi_priv),
};