Commit | Line | Data |
---|---|---|
0224cde2 | 1 | /* |
77fcb582 PG |
2 | * Lantiq XRX200 PHY Firmware Loader |
3 | * Author: John Crispin | |
4 | * | |
0224cde2 JC |
5 | * This program is free software; you can redistribute it and/or modify it |
6 | * under the terms of the GNU General Public License version 2 as published | |
7 | * by the Free Software Foundation. | |
8 | * | |
97b92108 | 9 | * Copyright (C) 2012 John Crispin <john@phrozen.org> |
0224cde2 JC |
10 | */ |
11 | ||
12 | #include <linux/delay.h> | |
13 | #include <linux/dma-mapping.h> | |
0224cde2 JC |
14 | #include <linux/firmware.h> |
15 | #include <linux/of_platform.h> | |
16 | ||
17 | #include <lantiq_soc.h> | |
18 | ||
19 | #define XRX200_GPHY_FW_ALIGN (16 * 1024) | |
20 | ||
21 | static dma_addr_t xway_gphy_load(struct platform_device *pdev) | |
22 | { | |
23 | const struct firmware *fw; | |
24 | dma_addr_t dev_addr = 0; | |
25 | const char *fw_name; | |
26 | void *fw_addr; | |
27 | size_t size; | |
28 | ||
276229d2 JC |
29 | if (of_get_property(pdev->dev.of_node, "firmware1", NULL) || |
30 | of_get_property(pdev->dev.of_node, "firmware2", NULL)) { | |
31 | switch (ltq_soc_type()) { | |
32 | case SOC_TYPE_VR9: | |
33 | if (of_property_read_string(pdev->dev.of_node, | |
34 | "firmware1", &fw_name)) { | |
35 | dev_err(&pdev->dev, | |
36 | "failed to load firmware filename\n"); | |
37 | return 0; | |
38 | } | |
39 | break; | |
40 | case SOC_TYPE_VR9_2: | |
41 | if (of_property_read_string(pdev->dev.of_node, | |
42 | "firmware2", &fw_name)) { | |
43 | dev_err(&pdev->dev, | |
44 | "failed to load firmware filename\n"); | |
45 | return 0; | |
46 | } | |
47 | break; | |
48 | } | |
49 | } else if (of_property_read_string(pdev->dev.of_node, | |
50 | "firmware", &fw_name)) { | |
0224cde2 JC |
51 | dev_err(&pdev->dev, "failed to load firmware filename\n"); |
52 | return 0; | |
53 | } | |
54 | ||
55 | dev_info(&pdev->dev, "requesting %s\n", fw_name); | |
56 | if (request_firmware(&fw, fw_name, &pdev->dev)) { | |
57 | dev_err(&pdev->dev, "failed to load firmware: %s\n", fw_name); | |
58 | return 0; | |
59 | } | |
60 | ||
61 | /* | |
62 | * GPHY cores need the firmware code in a persistent and contiguous | |
63 | * memory area with a 16 kB boundary aligned start address | |
64 | */ | |
65 | size = fw->size + XRX200_GPHY_FW_ALIGN; | |
66 | ||
67 | fw_addr = dma_alloc_coherent(&pdev->dev, size, &dev_addr, GFP_KERNEL); | |
68 | if (fw_addr) { | |
69 | fw_addr = PTR_ALIGN(fw_addr, XRX200_GPHY_FW_ALIGN); | |
70 | dev_addr = ALIGN(dev_addr, XRX200_GPHY_FW_ALIGN); | |
71 | memcpy(fw_addr, fw->data, fw->size); | |
72 | } else { | |
73 | dev_err(&pdev->dev, "failed to alloc firmware memory\n"); | |
74 | } | |
75 | ||
76 | release_firmware(fw); | |
77 | return dev_addr; | |
78 | } | |
79 | ||
28eb0e46 | 80 | static int xway_phy_fw_probe(struct platform_device *pdev) |
0224cde2 JC |
81 | { |
82 | dma_addr_t fw_addr; | |
83 | struct property *pp; | |
84 | unsigned char *phyids; | |
85 | int i, ret = 0; | |
86 | ||
87 | fw_addr = xway_gphy_load(pdev); | |
88 | if (!fw_addr) | |
89 | return -EINVAL; | |
90 | pp = of_find_property(pdev->dev.of_node, "phys", NULL); | |
91 | if (!pp) | |
92 | return -ENOENT; | |
93 | phyids = pp->value; | |
94 | for (i = 0; i < pp->length && !ret; i++) | |
95 | ret = xrx200_gphy_boot(&pdev->dev, phyids[i], fw_addr); | |
96 | if (!ret) | |
97 | mdelay(100); | |
98 | return ret; | |
99 | } | |
100 | ||
101 | static const struct of_device_id xway_phy_match[] = { | |
102 | { .compatible = "lantiq,phy-xrx200" }, | |
103 | {}, | |
104 | }; | |
0224cde2 JC |
105 | |
106 | static struct platform_driver xway_phy_driver = { | |
107 | .probe = xway_phy_fw_probe, | |
108 | .driver = { | |
109 | .name = "phy-xrx200", | |
0224cde2 JC |
110 | .of_match_table = xway_phy_match, |
111 | }, | |
112 | }; | |
77fcb582 | 113 | builtin_platform_driver(xway_phy_driver); |