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