]>
Commit | Line | Data |
---|---|---|
0fabfeb2 LD |
1 | // SPDX-License-Identifier: GPL-2.0+ |
2 | /* | |
3 | * Copyright (C) 2018 Arm Ltd | |
4 | * Author: Liviu Dudau <[email protected]> | |
5 | * | |
6 | */ | |
0fabfeb2 | 7 | #include <dm.h> |
336d4615 | 8 | #include <malloc.h> |
0fabfeb2 LD |
9 | #include <dm/read.h> |
10 | #include <asm/io.h> | |
cd93d625 | 11 | #include <linux/bitops.h> |
0fabfeb2 LD |
12 | #include <linux/delay.h> |
13 | #include <misc.h> | |
14 | ||
15 | #define SYS_CFGDATA 0xa0 | |
16 | ||
17 | #define SYS_CFGCTRL 0xa4 | |
18 | #define SYS_CFGCTRL_START BIT(31) | |
19 | #define SYS_CFGCTRL_WRITE BIT(30) | |
20 | ||
21 | #define SYS_CFGSTAT 0xa8 | |
22 | #define SYS_CFGSTAT_ERR BIT(1) | |
23 | #define SYS_CFGSTAT_COMPLETE BIT(0) | |
24 | ||
25 | struct vexpress_config_sysreg { | |
26 | phys_addr_t addr; | |
27 | u32 site; | |
28 | }; | |
29 | ||
30 | static int vexpress_config_exec(struct vexpress_config_sysreg *syscfg, | |
31 | bool write, void *buf, int size) | |
32 | { | |
33 | u32 cmd, status, tries = 100; | |
34 | ||
35 | cmd = (*(u32 *)buf) | SYS_CFGCTRL_START | (syscfg->site << 16); | |
36 | ||
37 | if (!write) { | |
38 | /* write a canary in the data register for reads */ | |
39 | writel(0xdeadbeef, syscfg->addr + SYS_CFGDATA); | |
40 | } else { | |
41 | cmd |= SYS_CFGCTRL_WRITE; | |
42 | writel(((u32 *)buf)[1], syscfg->addr + SYS_CFGDATA); | |
43 | } | |
44 | writel(0, syscfg->addr + SYS_CFGSTAT); | |
45 | writel(cmd, syscfg->addr + SYS_CFGCTRL); | |
46 | ||
47 | /* completion of command takes ages, go to sleep (150us) */ | |
48 | do { | |
49 | udelay(150); | |
50 | status = readl(syscfg->addr + SYS_CFGSTAT); | |
51 | if (status & SYS_CFGSTAT_ERR) | |
52 | return -EFAULT; | |
53 | } while (--tries && !(status & SYS_CFGSTAT_COMPLETE)); | |
54 | ||
55 | if (!tries) | |
56 | return -ETIMEDOUT; | |
57 | ||
58 | if (!write) | |
59 | (*(u32 *)buf) = readl(syscfg->addr + SYS_CFGDATA); | |
60 | ||
61 | return 0; | |
62 | } | |
63 | ||
64 | static int vexpress_config_read(struct udevice *dev, | |
65 | int offset, void *buf, int size) | |
66 | { | |
67 | struct vexpress_config_sysreg *priv = dev_get_uclass_priv(dev); | |
68 | ||
69 | if (size != sizeof(u32)) | |
70 | return -EINVAL; | |
71 | ||
72 | return vexpress_config_exec(priv, false, buf, size); | |
73 | } | |
74 | ||
75 | static int vexpress_config_write(struct udevice *dev, | |
76 | int offset, const void *buf, int size) | |
77 | { | |
78 | struct vexpress_config_sysreg *priv = dev_get_uclass_priv(dev); | |
79 | ||
80 | if (size != sizeof(u32) * 2) | |
81 | return -EINVAL; | |
82 | ||
83 | return vexpress_config_exec(priv, true, (void *)buf, size); | |
84 | } | |
85 | ||
86 | static struct misc_ops vexpress_config_ops = { | |
87 | .read = vexpress_config_read, | |
88 | .write = vexpress_config_write, | |
89 | }; | |
90 | ||
91 | static int vexpress_config_probe(struct udevice *dev) | |
92 | { | |
93 | struct ofnode_phandle_args args; | |
ffdb85bf | 94 | struct vexpress_config_sysreg *priv = dev_get_priv(dev); |
0fabfeb2 LD |
95 | const char *prop; |
96 | int err, prop_size; | |
97 | ||
98 | err = dev_read_phandle_with_args(dev, "arm,vexpress,config-bridge", | |
99 | NULL, 0, 0, &args); | |
100 | if (err) | |
101 | return err; | |
102 | ||
103 | prop = ofnode_get_property(args.node, "compatible", &prop_size); | |
104 | if (!prop || (strncmp(prop, "arm,vexpress-sysreg", 19) != 0)) | |
105 | return -ENOENT; | |
106 | ||
0fabfeb2 LD |
107 | if (!priv) |
108 | return -ENOMEM; | |
109 | ||
0fabfeb2 LD |
110 | priv->addr = ofnode_get_addr(args.node); |
111 | ||
112 | return dev_read_u32(dev, "arm,vexpress,site", &priv->site); | |
113 | } | |
114 | ||
115 | static const struct udevice_id vexpress_config_ids[] = { | |
116 | { .compatible = "arm,vexpress,config-bus" }, | |
117 | { } | |
118 | }; | |
119 | ||
120 | U_BOOT_DRIVER(vexpress_config_drv) = { | |
121 | .name = "vexpress_config_bus", | |
122 | .id = UCLASS_MISC, | |
123 | .of_match = vexpress_config_ids, | |
124 | .bind = dm_scan_fdt_dev, | |
125 | .probe = vexpress_config_probe, | |
126 | .ops = &vexpress_config_ops, | |
ffdb85bf | 127 | .priv_auto = sizeof(struct vexpress_config_sysreg), |
0fabfeb2 | 128 | }; |