1 // SPDX-License-Identifier: GPL-2.0+ 2 /* 3 * Sample SPMI bus driver 4 * 5 * It emulates bus with single pm8916-like pmic that has only GPIO reigsters. 6 * 7 * (C) Copyright 2015 Mateusz Kulikowski <mateusz.kulikowski (at) gmail.com> 8 */ 9 10 #include <common.h> 11 #include <dm.h> 12 #include <errno.h> 13 #include <spmi/spmi.h> 14 #include <asm/gpio.h> 15 #include <asm/io.h> 16 17 #define EMUL_GPIO_PID_START 0xC0 18 #define EMUL_GPIO_PID_END 0xC3 19 20 #define EMUL_GPIO_COUNT 4 21 22 #define EMUL_GPIO_REG_END 0x46 /* Last valid register */ 23 24 #define EMUL_PERM_R 0x1 25 #define EMUL_PERM_W 0x2 26 #define EMUL_PERM_RW (EMUL_PERM_R | EMUL_PERM_W) 27 28 struct sandbox_emul_fake_regs { 29 u8 value; 30 u8 access_mask; 31 u8 perms; /* Access permissions */ 32 }; 33 34 struct sandbox_emul_gpio { 35 /* Fake registers - need one more entry as REG_END is valid address. */ 36 struct sandbox_emul_fake_regs r[EMUL_GPIO_REG_END + 1]; 37 }; 38 39 struct sandbox_spmi_priv { 40 struct sandbox_emul_gpio gpios[EMUL_GPIO_COUNT]; 41 }; 42 43 /* Check if valid register was requested */ 44 static bool check_address_valid(int usid, int pid, int off) 45 { 46 if (usid != 0) 47 return false; 48 if (pid < EMUL_GPIO_PID_START || pid > EMUL_GPIO_PID_END) 49 return false; 50 if (off > EMUL_GPIO_REG_END) 51 return false; 52 return true; 53 } 54 55 static int sandbox_spmi_write(struct udevice *dev, int usid, int pid, int off, 56 uint8_t val) 57 { 58 struct sandbox_spmi_priv *priv = dev_get_priv(dev); 59 struct sandbox_emul_fake_regs *regs; 60 61 if (!check_address_valid(usid, pid, off)) 62 return -EIO; 63 64 regs = priv->gpios[pid & 0x3].r; /* Last 3 bits of pid are gpio # */ 65 66 switch (off) { 67 case 0x40: /* Control */ 68 val &= regs[off].access_mask; 69 if (((val & 0x30) == 0x10) || ((val & 0x30) == 0x20)) { 70 /* out/inout - set status register */ 71 regs[0x8].value &= ~0x1; 72 regs[0x8].value |= val & 0x1; 73 } 74 break; 75 default: 76 if (regs[off].perms & EMUL_PERM_W) 77 regs[off].value = val & regs[off].access_mask; 78 } 79 return 0; 80 } 81 82 static int sandbox_spmi_read(struct udevice *dev, int usid, int pid, int off) 83 { 84 struct sandbox_spmi_priv *priv = dev_get_priv(dev); 85 struct sandbox_emul_fake_regs *regs; 86 87 if (!check_address_valid(usid, pid, off)) 88 return -EIO; 89 90 regs = priv->gpios[pid & 0x3].r; /* Last 3 bits of pid are gpio # */ 91 92 if (regs[0x46].value == 0) /* Block disabled */ 93 return 0; 94 95 switch (off) { 96 case 0x8: /* Status */ 97 if (regs[0x46].value == 0) /* Block disabled */ 98 return 0; 99 return regs[off].value; 100 default: 101 if (regs[off].perms & EMUL_PERM_R) 102 return regs[off].value; 103 else 104 return 0; 105 } 106 } 107 108 static struct dm_spmi_ops sandbox_spmi_ops = { 109 .read = sandbox_spmi_read, 110 .write = sandbox_spmi_write, 111 }; 112 113 static int sandbox_spmi_probe(struct udevice *dev) 114 { 115 struct sandbox_spmi_priv *priv = dev_get_priv(dev); 116 int i; 117 118 for (i = 0; i < EMUL_GPIO_COUNT; ++i) { 119 struct sandbox_emul_fake_regs *regs = priv->gpios[i].r; 120 regs[4].perms = EMUL_PERM_R; 121 regs[4].value = 0x10; 122 regs[5].perms = EMUL_PERM_R; 123 regs[5].value = 0x5; 124 regs[8].access_mask = 0x81; 125 regs[8].perms = EMUL_PERM_RW; 126 regs[0x40].access_mask = 0x7F; 127 regs[0x40].perms = EMUL_PERM_RW; 128 regs[0x41].access_mask = 7; 129 regs[0x41].perms = EMUL_PERM_RW; 130 regs[0x42].access_mask = 7; 131 regs[0x42].perms = EMUL_PERM_RW; 132 regs[0x42].value = 0x4; 133 regs[0x45].access_mask = 0x3F; 134 regs[0x45].perms = EMUL_PERM_RW; 135 regs[0x45].value = 0x1; 136 regs[0x46].access_mask = 0x80; 137 regs[0x46].perms = EMUL_PERM_RW; 138 regs[0x46].value = 0x80; 139 } 140 return 0; 141 } 142 143 static const struct udevice_id sandbox_spmi_ids[] = { 144 { .compatible = "sandbox,spmi" }, 145 { } 146 }; 147 148 U_BOOT_DRIVER(msm_spmi) = { 149 .name = "sandbox_spmi", 150 .id = UCLASS_SPMI, 151 .of_match = sandbox_spmi_ids, 152 .ops = &sandbox_spmi_ops, 153 .probe = sandbox_spmi_probe, 154 .priv_auto_alloc_size = sizeof(struct sandbox_spmi_priv), 155 }; 156