dm: core: Create a new header file for 'compat' features
[oweals/u-boot.git] / drivers / misc / p2sb-uclass.c
1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * Uclass for Primary-to-sideband bus, used to access various peripherals
4  *
5  * Copyright 2019 Google LLC
6  * Written by Simon Glass <sjg@chromium.org>
7  */
8
9 #include <common.h>
10 #include <dm.h>
11 #include <malloc.h>
12 #include <mapmem.h>
13 #include <p2sb.h>
14 #include <spl.h>
15 #include <asm/io.h>
16 #include <dm/uclass-internal.h>
17
18 #define PCR_COMMON_IOSF_1_0     1
19
20 static void *_pcr_reg_address(struct udevice *dev, uint offset)
21 {
22         struct p2sb_child_platdata *pplat = dev_get_parent_platdata(dev);
23         struct udevice *p2sb = dev_get_parent(dev);
24         struct p2sb_uc_priv *upriv = dev_get_uclass_priv(p2sb);
25         uintptr_t reg_addr;
26
27         /* Create an address based off of port id and offset */
28         reg_addr = upriv->mmio_base;
29         reg_addr += pplat->pid << PCR_PORTID_SHIFT;
30         reg_addr += offset;
31
32         return map_sysmem(reg_addr, 4);
33 }
34
35 /*
36  * The mapping of addresses via the SBREG_BAR assumes the IOSF-SB
37  * agents are using 32-bit aligned accesses for their configuration
38  * registers. For IOSF versions greater than 1_0, IOSF-SB
39  * agents can use any access (8/16/32 bit aligned) for their
40  * configuration registers
41  */
42 static inline void check_pcr_offset_align(uint offset, uint size)
43 {
44         const size_t align = PCR_COMMON_IOSF_1_0 ? sizeof(uint32_t) : size;
45
46         assert(IS_ALIGNED(offset, align));
47 }
48
49 uint pcr_read32(struct udevice *dev, uint offset)
50 {
51         void *ptr;
52         uint val;
53
54         /* Ensure the PCR offset is correctly aligned */
55         assert(IS_ALIGNED(offset, sizeof(uint32_t)));
56
57         ptr = _pcr_reg_address(dev, offset);
58         val = readl(ptr);
59         unmap_sysmem(ptr);
60
61         return val;
62 }
63
64 uint pcr_read16(struct udevice *dev, uint offset)
65 {
66         /* Ensure the PCR offset is correctly aligned */
67         check_pcr_offset_align(offset, sizeof(uint16_t));
68
69         return readw(_pcr_reg_address(dev, offset));
70 }
71
72 uint pcr_read8(struct udevice *dev, uint offset)
73 {
74         /* Ensure the PCR offset is correctly aligned */
75         check_pcr_offset_align(offset, sizeof(uint8_t));
76
77         return readb(_pcr_reg_address(dev, offset));
78 }
79
80 /*
81  * After every write one needs to perform a read an innocuous register to
82  * ensure the writes are completed for certain ports. This is done for
83  * all ports so that the callers don't need the per-port knowledge for
84  * each transaction.
85  */
86 static void write_completion(struct udevice *dev, uint offset)
87 {
88         readl(_pcr_reg_address(dev, ALIGN_DOWN(offset, sizeof(uint32_t))));
89 }
90
91 void pcr_write32(struct udevice *dev, uint offset, uint indata)
92 {
93         /* Ensure the PCR offset is correctly aligned */
94         assert(IS_ALIGNED(offset, sizeof(indata)));
95
96         writel(indata, _pcr_reg_address(dev, offset));
97         /* Ensure the writes complete */
98         write_completion(dev, offset);
99 }
100
101 void pcr_write16(struct udevice *dev, uint offset, uint indata)
102 {
103         /* Ensure the PCR offset is correctly aligned */
104         check_pcr_offset_align(offset, sizeof(uint16_t));
105
106         writew(indata, _pcr_reg_address(dev, offset));
107         /* Ensure the writes complete */
108         write_completion(dev, offset);
109 }
110
111 void pcr_write8(struct udevice *dev, uint offset, uint indata)
112 {
113         /* Ensure the PCR offset is correctly aligned */
114         check_pcr_offset_align(offset, sizeof(uint8_t));
115
116         writeb(indata, _pcr_reg_address(dev, offset));
117         /* Ensure the writes complete */
118         write_completion(dev, offset);
119 }
120
121 void pcr_clrsetbits32(struct udevice *dev, uint offset, uint clr, uint set)
122 {
123         uint data32;
124
125         data32 = pcr_read32(dev, offset);
126         data32 &= ~clr;
127         data32 |= set;
128         pcr_write32(dev, offset, data32);
129 }
130
131 void pcr_clrsetbits16(struct udevice *dev, uint offset, uint clr, uint set)
132 {
133         uint data16;
134
135         data16 = pcr_read16(dev, offset);
136         data16 &= ~clr;
137         data16 |= set;
138         pcr_write16(dev, offset, data16);
139 }
140
141 void pcr_clrsetbits8(struct udevice *dev, uint offset, uint clr, uint set)
142 {
143         uint data8;
144
145         data8 = pcr_read8(dev, offset);
146         data8 &= ~clr;
147         data8 |= set;
148         pcr_write8(dev, offset, data8);
149 }
150
151 int p2sb_get_port_id(struct udevice *dev)
152 {
153         struct p2sb_child_platdata *pplat = dev_get_parent_platdata(dev);
154
155         return pplat->pid;
156 }
157
158 int p2sb_set_port_id(struct udevice *dev, int portid)
159 {
160         struct udevice *ps2b;
161         struct p2sb_child_platdata *pplat;
162
163         if (!CONFIG_IS_ENABLED(OF_PLATDATA))
164                 return -ENOSYS;
165
166         uclass_find_first_device(UCLASS_P2SB, &ps2b);
167         if (!ps2b)
168                 return -EDEADLK;
169         dev->parent = ps2b;
170
171         /*
172          * We must allocate this, since when the device was bound it did not
173          * have a parent.
174          * TODO(sjg@chromium.org): Add a parent pointer to child devices in dtoc
175          */
176         dev->parent_platdata = malloc(sizeof(*pplat));
177         if (!dev->parent_platdata)
178                 return -ENOMEM;
179         pplat = dev_get_parent_platdata(dev);
180         pplat->pid = portid;
181
182         return 0;
183 }
184
185 static int p2sb_child_post_bind(struct udevice *dev)
186 {
187 #if !CONFIG_IS_ENABLED(OF_PLATDATA)
188         struct p2sb_child_platdata *pplat = dev_get_parent_platdata(dev);
189         int ret;
190         u32 pid;
191
192         ret = dev_read_u32(dev, "intel,p2sb-port-id", &pid);
193         if (ret)
194                 return ret;
195         pplat->pid = pid;
196 #endif
197
198         return 0;
199 }
200
201 static int p2sb_post_bind(struct udevice *dev)
202 {
203         if (spl_phase() > PHASE_TPL && !CONFIG_IS_ENABLED(OF_PLATDATA))
204                 return dm_scan_fdt_dev(dev);
205
206         return 0;
207 }
208
209 UCLASS_DRIVER(p2sb) = {
210         .id             = UCLASS_P2SB,
211         .name           = "p2sb",
212         .per_device_auto_alloc_size = sizeof(struct p2sb_uc_priv),
213         .post_bind      = p2sb_post_bind,
214         .child_post_bind = p2sb_child_post_bind,
215         .per_child_platdata_auto_alloc_size =
216                 sizeof(struct p2sb_child_platdata),
217 };