bmips: hg556a: switch to kmod-owl-loader
[openwrt/staging/dedeckeh.git] / target / linux / bmips / files / arch / mips / bmips / ath9k-fixup.c
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3 * ATH9K Fixup Driver
4 *
5 * Copyright (C) 2020 Álvaro Fernández Rojas <noltari@gmail.com>
6 * Copyright (C) 2014 Jonas Gorski <jonas.gorski@gmail.com>
7 * Copyright (C) 2008 Maxime Bizon <mbizon@freebox.fr>
8 * Copyright (C) 2008 Florian Fainelli <f.fainelli@gmail.com>
9 */
10
11 #include <linux/delay.h>
12 #include <linux/etherdevice.h>
13 #include <linux/init.h>
14 #include <linux/kernel.h>
15 #include <linux/mtd/mtd.h>
16 #include <linux/of_net.h>
17 #include <linux/of_platform.h>
18 #include <linux/pci.h>
19 #include <linux/types.h>
20 #include <linux/ath9k_platform.h>
21
22 #define ATH9K_MAX_FIXUPS 2
23
24 #define ATH9K_DEF_LED_PIN -1
25 #define ATH9K_DEF_PCI_DEV 255
26
27 struct ath9k_fixup {
28 struct device *dev;
29 struct resource *mem_res;
30 u32 pci_dev;
31 u8 mac[ETH_ALEN];
32 struct ath9k_platform_data pdata;
33 };
34
35 static int ath9k_num_fixups;
36 static struct ath9k_fixup *ath9k_fixups[ATH9K_MAX_FIXUPS];
37
38 static void ath9k_pci_fixup(struct pci_dev *dev)
39 {
40 void __iomem *mem;
41 struct ath9k_fixup *priv = NULL;
42 struct ath9k_platform_data *pdata = NULL;
43 struct pci_dev *bridge = pci_upstream_bridge(dev);
44 u16 *cal_data = NULL;
45 u16 cmd;
46 u32 bar0;
47 u32 val;
48 unsigned i;
49 int rc;
50
51 for (i = 0; i < ath9k_num_fixups; i++) {
52 if (ath9k_fixups[i]->pci_dev != PCI_SLOT(dev->devfn))
53 continue;
54
55 priv = ath9k_fixups[i];
56 cal_data = priv->pdata.eeprom_data;
57 pdata = &priv->pdata;
58 break;
59 }
60
61 if (cal_data == NULL)
62 return;
63
64 if (*cal_data != 0xa55a) {
65 pr_err("pci %s: invalid calibration data\n", pci_name(dev));
66 return;
67 }
68
69 pr_info("pci %s: fixup device configuration\n", pci_name(dev));
70
71 val = priv->mem_res->start;
72 mem = ioremap(priv->mem_res->start, resource_size(priv->mem_res));
73 if (!mem) {
74 pr_err("pci %s: ioremap error\n", pci_name(dev));
75 return;
76 }
77
78 if (bridge) {
79 rc = pci_enable_device(bridge);
80 if (rc < 0)
81 pr_err("pci %s: bridge enable error\n", pci_name(dev));
82 }
83
84 pci_read_config_dword(dev, PCI_BASE_ADDRESS_0, &bar0);
85 pci_read_config_dword(dev, PCI_BASE_ADDRESS_0, &bar0);
86 pci_write_config_dword(dev, PCI_BASE_ADDRESS_0, val);
87
88 pci_read_config_word(dev, PCI_COMMAND, &cmd);
89 cmd |= PCI_COMMAND_MASTER | PCI_COMMAND_MEMORY;
90 pci_write_config_word(dev, PCI_COMMAND, cmd);
91
92 /* set offset to first reg address */
93 cal_data += 3;
94 while(*cal_data != 0xffff) {
95 u32 reg;
96
97 reg = *cal_data++;
98 val = *cal_data++;
99 val |= (*cal_data++) << 16;
100
101 writel(val, mem + reg);
102 udelay(100);
103 }
104
105 pci_read_config_dword(dev, PCI_VENDOR_ID, &val);
106 dev->vendor = val & 0xffff;
107 dev->device = (val >> 16) & 0xffff;
108
109 pci_read_config_dword(dev, PCI_CLASS_REVISION, &val);
110 dev->revision = val & 0xff;
111 dev->class = val >> 8; /* upper 3 bytes */
112
113 pci_read_config_word(dev, PCI_COMMAND, &cmd);
114 cmd &= ~(PCI_COMMAND_MASTER | PCI_COMMAND_MEMORY);
115 pci_write_config_word(dev, PCI_COMMAND, cmd);
116
117 pci_write_config_dword(dev, PCI_BASE_ADDRESS_0, bar0);
118
119 if (bridge)
120 pci_disable_device(bridge);
121
122 iounmap(mem);
123
124 dev->dev.platform_data = pdata;
125 }
126 DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_ATHEROS, PCI_ANY_ID, ath9k_pci_fixup);
127
128 static int ath9k_mtd_eeprom(struct ath9k_fixup *priv)
129 {
130 struct device *dev = priv->dev;
131 struct device_node *node = dev->of_node;
132 struct device_node *mtd_np = NULL;
133 struct mtd_info *the_mtd;
134 phandle phandle;
135 size_t eeprom_readlen;
136 const char *part;
137 const __be32 *list;
138 int ret, i;
139
140 list = of_get_property(node, "ath,eeprom", &i);
141 if (!list || (i != (2 * sizeof(*list))))
142 return -ENODEV;
143
144 phandle = be32_to_cpup(list++);
145 if (phandle)
146 mtd_np = of_find_node_by_phandle(phandle);
147 if (!mtd_np)
148 return -ENODEV;
149
150 part = of_get_property(mtd_np, "label", NULL);
151 if (!part)
152 part = mtd_np->name;
153
154 the_mtd = get_mtd_device_nm(part);
155 if (IS_ERR(the_mtd))
156 return -ENODEV;
157
158 ret = mtd_read(the_mtd, be32_to_cpup(list),
159 ATH9K_PLAT_EEP_MAX_WORDS * sizeof(u16),
160 &eeprom_readlen, (void *) priv->pdata.eeprom_data);
161 put_mtd_device(the_mtd);
162 if (ret)
163 return ret;
164
165 return 0;
166 }
167
168 static int ath9k_fixup_probe(struct platform_device *pdev)
169 {
170 struct device *dev = &pdev->dev;
171 struct device_node *node = dev->of_node;
172 struct ath9k_fixup *priv;
173 struct resource *res;
174 int ret;
175
176 if (ath9k_num_fixups >= ATH9K_MAX_FIXUPS)
177 return -ENOMEM;
178
179 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
180 if (!res)
181 return -EINVAL;
182
183 priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
184 if (!priv)
185 return -ENOMEM;
186
187 priv->dev = dev;
188 priv->mem_res = res;
189
190 ret = of_property_read_u32(node, "pci-dev", &priv->pci_dev);
191 if (ret)
192 priv->pci_dev = ATH9K_DEF_PCI_DEV;
193
194 ret = ath9k_mtd_eeprom(priv);
195 if (ret)
196 return ret;
197
198 priv->pdata.endian_check = of_property_read_bool(node,
199 "ath,endian-check");
200 ret = of_property_read_s32(node, "ath,led-pin", &priv->pdata.led_pin);
201 if (ret)
202 priv->pdata.led_pin = ATH9K_DEF_LED_PIN;
203 priv->pdata.led_active_high = of_property_read_bool(node,
204 "ath,led-active-high");
205
206 of_get_mac_address(node, priv->mac);
207 if (is_valid_ether_addr(priv->mac)) {
208 dev_info(dev, "mtd mac %pM\n", priv->mac);
209 } else {
210 random_ether_addr(priv->mac);
211 dev_info(dev, "random mac %pM\n", priv->mac);
212 }
213
214 priv->pdata.macaddr = priv->mac;
215
216 ath9k_fixups[ath9k_num_fixups] = priv;
217 ath9k_num_fixups++;
218
219 return 0;
220 }
221
222 static const struct of_device_id ath9k_fixup_of_match[] = {
223 { .compatible = "brcm,ath9k-fixup" },
224 { /* sentinel */ }
225 };
226
227 static struct platform_driver ath9k_fixup_driver = {
228 .probe = ath9k_fixup_probe,
229 .driver = {
230 .name = "ath9k-fixup",
231 .of_match_table = ath9k_fixup_of_match,
232 },
233 };
234
235 int __init ath9k_fixup_init(void)
236 {
237 int ret = platform_driver_register(&ath9k_fixup_driver);
238 if (ret)
239 pr_err("ath9k_fixup: Error registering platform driver!\n");
240 return ret;
241 }
242 late_initcall(ath9k_fixup_init);