diff options
-rw-r--r-- | arch/sandbox/dts/sandbox.dts | 20 | ||||
-rw-r--r-- | arch/sandbox/dts/test.dts | 20 | ||||
-rw-r--r-- | configs/sandbox_defconfig | 4 | ||||
-rw-r--r-- | doc/device-tree-bindings/spmi/spmi-sandbox.txt | 31 | ||||
-rw-r--r-- | drivers/spmi/Kconfig | 8 | ||||
-rw-r--r-- | drivers/spmi/Makefile | 1 | ||||
-rw-r--r-- | drivers/spmi/spmi-sandbox.c | 157 | ||||
-rw-r--r-- | test/dm/Makefile | 1 | ||||
-rw-r--r-- | test/dm/spmi.c | 114 |
9 files changed, 356 insertions, 0 deletions
diff --git a/arch/sandbox/dts/sandbox.dts b/arch/sandbox/dts/sandbox.dts index e3f02bf..2ae4014 100644 --- a/arch/sandbox/dts/sandbox.dts +++ b/arch/sandbox/dts/sandbox.dts @@ -240,6 +240,26 @@ status = "disabled"; }; + spmi: spmi@0 { + compatible = "sandbox,spmi"; + #address-cells = <0x1>; + #size-cells = <0x1>; + pm8916@0 { + compatible = "qcom,spmi-pmic"; + reg = <0x0 0x1>; + #address-cells = <0x1>; + #size-cells = <0x1>; + + spmi_gpios: gpios@c000 { + compatible = "qcom,pm8916-gpio"; + reg = <0xc000 0x400>; + gpio-controller; + gpio-count = <4>; + #gpio-cells = <2>; + gpio-bank-name="spmi"; + }; + }; + }; }; #include "cros-ec-keyboard.dtsi" diff --git a/arch/sandbox/dts/test.dts b/arch/sandbox/dts/test.dts index 9b8d658..8930009 100644 --- a/arch/sandbox/dts/test.dts +++ b/arch/sandbox/dts/test.dts @@ -351,6 +351,26 @@ status = "disabled"; }; + spmi: spmi@0 { + compatible = "sandbox,spmi"; + #address-cells = <0x1>; + #size-cells = <0x1>; + pm8916@0 { + compatible = "qcom,spmi-pmic"; + reg = <0x0 0x1>; + #address-cells = <0x1>; + #size-cells = <0x1>; + + spmi_gpios: gpios@c000 { + compatible = "qcom,pm8916-gpio"; + reg = <0xc000 0x400>; + gpio-controller; + gpio-count = <4>; + #gpio-cells = <2>; + gpio-bank-name="spmi"; + }; + }; + }; }; #include "sandbox_pmic.dtsi" diff --git a/configs/sandbox_defconfig b/configs/sandbox_defconfig index d69c9fc..bfc8b61 100644 --- a/configs/sandbox_defconfig +++ b/configs/sandbox_defconfig @@ -31,6 +31,7 @@ CONFIG_ADC_SANDBOX=y CONFIG_BLK=y CONFIG_CLK=y CONFIG_SANDBOX_GPIO=y +CONFIG_PM8916_GPIO=y CONFIG_SYS_I2C_SANDBOX=y CONFIG_CROS_EC_KEYB=y CONFIG_LED=y @@ -59,6 +60,9 @@ CONFIG_PINCONF=y CONFIG_PINCTRL_SANDBOX=y CONFIG_DM_PMIC=y CONFIG_DM_PMIC_SANDBOX=y +CONFIG_PMIC_PM8916=y +CONFIG_SPMI=y +CONFIG_SPMI_SANDBOX=y CONFIG_DM_REGULATOR=y CONFIG_DM_REGULATOR_SANDBOX=y CONFIG_RAM=y diff --git a/doc/device-tree-bindings/spmi/spmi-sandbox.txt b/doc/device-tree-bindings/spmi/spmi-sandbox.txt new file mode 100644 index 0000000..8569a1a --- /dev/null +++ b/doc/device-tree-bindings/spmi/spmi-sandbox.txt @@ -0,0 +1,31 @@ +Sandbox SPMI emulated arbiter. + +This is bus driver for Sandbox. It includes part of emulated pm8916 pmic. + +Required properties: +- compatible: "sandbox,spmi" +- #address-cells: 0x1 - childs slave ID address +- #size-cells: 0x1 + +Example: + +spmi: spmi@0 { + compatible = "sandbox,spmi"; + #address-cells = <0x1>; + #size-cells = <0x1>; + pm8916@0 { + compatible = "qcom,spmi-pmic"; + reg = <0x0 0x1>; + #address-cells = <0x1>; + #size-cells = <0x1>; + + spmi_gpios: gpios@c000 { + compatible = "qcom,pm8916-gpio"; + reg = <0xc000 0x400>; + gpio-controller; + gpio-count = <4>; + #gpio-cells = <2>; + gpio-bank-name="spmi"; + }; + }; +}; diff --git a/drivers/spmi/Kconfig b/drivers/spmi/Kconfig index 0b9bd31..c70d675 100644 --- a/drivers/spmi/Kconfig +++ b/drivers/spmi/Kconfig @@ -7,4 +7,12 @@ config SPMI Select this to enable to support SPMI bus. SPMI (System Power Management Interface) bus is used to connect PMIC devices on various SoCs. + +config SPMI_SANDBOX + boolean "Support for Sandbox SPMI bus" + depends on SPMI + ---help--- + Demo SPMI bus implementation. Emulates part of PM8916 as single + slave (0) on bus. It has 4 GPIO peripherals, pid 0xC0-0xC3. + endmenu diff --git a/drivers/spmi/Makefile b/drivers/spmi/Makefile index 99092eb..4ca65a9 100644 --- a/drivers/spmi/Makefile +++ b/drivers/spmi/Makefile @@ -5,3 +5,4 @@ # obj-$(CONFIG_SPMI) += spmi-uclass.o +obj-$(CONFIG_SPMI_SANDBOX) += spmi-sandbox.o diff --git a/drivers/spmi/spmi-sandbox.c b/drivers/spmi/spmi-sandbox.c new file mode 100644 index 0000000..2f0fea0 --- /dev/null +++ b/drivers/spmi/spmi-sandbox.c @@ -0,0 +1,157 @@ +/* + * 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 { + struct sandbox_emul_fake_regs r[EMUL_GPIO_REG_END]; /* Fake registers */ +}; + +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), +}; diff --git a/test/dm/Makefile b/test/dm/Makefile index df2d71f..9a11ae0 100644 --- a/test/dm/Makefile +++ b/test/dm/Makefile @@ -37,4 +37,5 @@ obj-$(CONFIG_DM_REGULATOR) += regulator.o obj-$(CONFIG_TIMER) += timer.o obj-$(CONFIG_DM_VIDEO) += video.o obj-$(CONFIG_ADC) += adc.o +obj-$(CONFIG_SPMI) += spmi.o endif diff --git a/test/dm/spmi.c b/test/dm/spmi.c new file mode 100644 index 0000000..d519a90 --- /dev/null +++ b/test/dm/spmi.c @@ -0,0 +1,114 @@ +/* + * (C) Copyright 2015 Mateusz Kulikowski <mateusz.kulikowski@gmail.com> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <fdtdec.h> +#include <dm.h> +#include <dm/device.h> +#include <dm/root.h> +#include <dm/test.h> +#include <dm/util.h> +#include <power/pmic.h> +#include <spmi/spmi.h> +#include <asm/gpio.h> +#include <test/ut.h> + +DECLARE_GLOBAL_DATA_PTR; + +/* Test if bus childs got probed propperly*/ +static int dm_test_spmi_probe(struct unit_test_state *uts) +{ + const char *name = "spmi@0"; + struct udevice *bus, *dev; + + ut_assertok(uclass_get_device(UCLASS_SPMI, 0, &bus)); + + /* Check bus name */ + ut_asserteq_str(name, bus->name); + + /* Check that it has some devices */ + ut_asserteq(device_has_children(bus), true); + + ut_assertok(device_find_first_child(bus, &dev)); + + /* There should be at least one child */ + ut_assertnonnull(dev); + + /* Check that only PMICs are connected to the bus */ + while (dev) { + ut_asserteq(device_get_uclass_id(dev), UCLASS_PMIC); + device_find_next_child(&dev); + } + + return 0; +} +DM_TEST(dm_test_spmi_probe, DM_TESTF_SCAN_FDT); + +/* Test if it's possible to read bus directly and indirectly */ +static int dm_test_spmi_access(struct unit_test_state *uts) +{ + const char *pmic_name = "pm8916@0"; + struct udevice *bus, *pmic; + + ut_assertok(uclass_get_device(UCLASS_SPMI, 0, &bus)); + + ut_assertok(device_get_child(bus, 0, &pmic)); + + /* Sanity check if it's proper PMIC */ + ut_asserteq_str(pmic_name, pmic->name); + + /* Read PMIC ID reg using SPMI bus - it assumes it has slaveID == 0*/ + ut_asserteq(spmi_reg_read(bus, 0, 0xC0, 0x4), 0x10); + ut_asserteq(spmi_reg_read(bus, 0, 0xC0, 0x5), 0x5); + + /* Read ID reg via pmic interface */ + ut_asserteq(pmic_reg_read(pmic, 0xC004), 0x10); + ut_asserteq(pmic_reg_read(pmic, 0xC005), 0x5); + + return 0; +} +DM_TEST(dm_test_spmi_access, DM_TESTF_SCAN_FDT); + + +/* Test if it's possible to access GPIO that should be in pmic */ +static int dm_test_spmi_access_peripheral(struct unit_test_state *uts) +{ + struct udevice *dev; + unsigned int offset, gpio; + const char *name; + int offset_count; + + /* Get second pin of PMIC GPIO */ + ut_assertok(gpio_lookup_name("spmi1", &dev, &offset, &gpio)); + + /* Check if PMIC is parent */ + ut_asserteq(device_get_uclass_id(dev->parent), UCLASS_PMIC); + + /* This should be second gpio */ + ut_asserteq(1, offset); + + name = gpio_get_bank_info(dev, &offset_count); + + /* Check bank name */ + ut_asserteq_str("spmi", name); + /* Check pin count */ + ut_asserteq(4, offset_count); + + ut_assertok(gpio_request(gpio, "testing")); + + /* Try to set/clear gpio */ + ut_assertok(gpio_direction_output(gpio, 0)); + ut_asserteq(gpio_get_value(gpio), 0); + ut_assertok(gpio_direction_output(gpio, 1)); + ut_asserteq(gpio_get_value(gpio), 1); + ut_assertok(gpio_direction_input(gpio)); + ut_asserteq(gpio_get_value(gpio), 1); + + ut_assertok(gpio_free(gpio)); + + return 0; +} +DM_TEST(dm_test_spmi_access_peripheral, DM_TESTF_SCAN_FDT); |