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