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