dm: core: Create a new header file for 'compat' features
[oweals/u-boot.git] / drivers / power / pmic / i2c_pmic_emul.c
1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  *  Copyright (C) 2015 Samsung Electronics
4  *  Przemyslaw Marczak  <p.marczak@samsung.com>
5  */
6
7 #include <common.h>
8 #include <errno.h>
9 #include <dm.h>
10 #include <i2c.h>
11 #include <malloc.h>
12 #include <power/pmic.h>
13 #include <power/sandbox_pmic.h>
14
15 /**
16  * struct sandbox_i2c_pmic_plat_data - platform data for the PMIC
17  *
18  * @rw_reg: PMICs register of the chip I/O transaction
19  * @reg:    PMICs registers array
20  */
21 struct sandbox_i2c_pmic_plat_data {
22         u8 rw_reg, rw_idx;
23         u8 reg_count;
24         u8 trans_len;
25         u8 buf_size;
26         u8 *reg;
27 };
28
29 static int sandbox_i2c_pmic_read_data(struct udevice *emul, uchar chip,
30                                       uchar *buffer, int len)
31 {
32         struct sandbox_i2c_pmic_plat_data *plat = dev_get_platdata(emul);
33
34         if (plat->rw_idx + len > plat->buf_size) {
35                 pr_err("Request exceeds PMIC register range! Max register: %#x",
36                       plat->reg_count);
37                 return -EFAULT;
38         }
39
40         debug("Read PMIC: %#x at register: %#x idx: %#x count: %d\n",
41               (unsigned int)chip & 0xff, plat->rw_reg, plat->rw_idx, len);
42
43         memcpy(buffer, plat->reg + plat->rw_idx, len);
44
45         return 0;
46 }
47
48 static int sandbox_i2c_pmic_write_data(struct udevice *emul, uchar chip,
49                                        uchar *buffer, int len,
50                                        bool next_is_read)
51 {
52         struct sandbox_i2c_pmic_plat_data *plat = dev_get_platdata(emul);
53
54         /* Probe only */
55         if (!len)
56                 return 0;
57
58         /* Set PMIC register for I/O */
59         plat->rw_reg = *buffer;
60         plat->rw_idx = plat->rw_reg * plat->trans_len;
61
62         debug("Write PMIC: %#x at register: %#x idx: %#x count: %d\n",
63               (unsigned int)chip & 0xff, plat->rw_reg, plat->rw_idx, len);
64
65         /* For read operation, set (write) only chip reg */
66         if (next_is_read)
67                 return 0;
68
69         buffer++;
70         len--;
71
72         if (plat->rw_idx + len > plat->buf_size) {
73                 pr_err("Request exceeds PMIC register range! Max register: %#x",
74                       plat->reg_count);
75         }
76
77         memcpy(plat->reg + plat->rw_idx, buffer, len);
78
79         return 0;
80 }
81
82 static int sandbox_i2c_pmic_xfer(struct udevice *emul, struct i2c_msg *msg,
83                                  int nmsgs)
84 {
85         int ret = 0;
86
87         for (; nmsgs > 0; nmsgs--, msg++) {
88                 bool next_is_read = nmsgs > 1 && (msg[1].flags & I2C_M_RD);
89                 if (msg->flags & I2C_M_RD) {
90                         ret = sandbox_i2c_pmic_read_data(emul, msg->addr,
91                                                          msg->buf, msg->len);
92                 } else {
93                         ret = sandbox_i2c_pmic_write_data(emul, msg->addr,
94                                                           msg->buf, msg->len,
95                                                           next_is_read);
96                 }
97
98                 if (ret)
99                         break;
100         }
101
102         return ret;
103 }
104
105 static int sandbox_i2c_pmic_ofdata_to_platdata(struct udevice *emul)
106 {
107         struct sandbox_i2c_pmic_plat_data *plat = dev_get_platdata(emul);
108         struct udevice *pmic_dev = i2c_emul_get_device(emul);
109
110         debug("%s:%d Setting PMIC default registers\n", __func__, __LINE__);
111         plat->reg_count = pmic_reg_count(pmic_dev);
112
113         return 0;
114 }
115
116 static int sandbox_i2c_pmic_probe(struct udevice *emul)
117 {
118         struct sandbox_i2c_pmic_plat_data *plat = dev_get_platdata(emul);
119         struct udevice *pmic_dev = i2c_emul_get_device(emul);
120         struct uc_pmic_priv *upriv = dev_get_uclass_priv(pmic_dev);
121         const u8 *reg_defaults;
122
123         plat->trans_len = upriv->trans_len;
124         plat->buf_size = plat->reg_count * plat->trans_len;
125
126         plat->reg = calloc(1, plat->buf_size);
127         if (!plat->reg) {
128                 debug("Canot allocate memory (%d B) for PMIC I2C emulation!\n",
129                       plat->buf_size);
130                 return -ENOMEM;
131         }
132
133         reg_defaults = dev_read_u8_array_ptr(emul, "reg-defaults",
134                                              plat->buf_size);
135
136         if (!reg_defaults) {
137                 pr_err("Property \"reg-defaults\" not found for device: %s!",
138                       emul->name);
139                 free(plat->reg);
140                 return -EINVAL;
141         }
142
143         memcpy(plat->reg, reg_defaults, plat->buf_size);
144
145         return 0;
146 }
147
148 struct dm_i2c_ops sandbox_i2c_pmic_emul_ops = {
149         .xfer = sandbox_i2c_pmic_xfer,
150 };
151
152 static const struct udevice_id sandbox_i2c_pmic_ids[] = {
153         { .compatible = "sandbox,i2c-pmic" },
154         { }
155 };
156
157 U_BOOT_DRIVER(sandbox_i2c_pmic_emul) = {
158         .name           = "sandbox_i2c_pmic_emul",
159         .id             = UCLASS_I2C_EMUL,
160         .of_match       = sandbox_i2c_pmic_ids,
161         .ofdata_to_platdata = sandbox_i2c_pmic_ofdata_to_platdata,
162         .probe          = sandbox_i2c_pmic_probe,
163         .platdata_auto_alloc_size = sizeof(struct sandbox_i2c_pmic_plat_data),
164         .ops            = &sandbox_i2c_pmic_emul_ops,
165 };