| /* |
| * This program is free software; you can redistribute it and/or modify it |
| * under the terms of the GNU General Public License version 2 as published |
| * by the Free Software Foundation. |
| * |
| * Copyright (C) 2012 John Crispin <john@phrozen.org> |
| */ |
| |
| #include <linux/delay.h> |
| #include <linux/dma-mapping.h> |
| #include <linux/module.h> |
| #include <linux/firmware.h> |
| #include <linux/of_platform.h> |
| |
| #include <lantiq_soc.h> |
| |
| #define XRX200_GPHY_FW_ALIGN (16 * 1024) |
| |
| static dma_addr_t xway_gphy_load(struct platform_device *pdev) |
| { |
| const struct firmware *fw; |
| dma_addr_t dev_addr = 0; |
| const char *fw_name; |
| void *fw_addr; |
| size_t size; |
| |
| if (of_get_property(pdev->dev.of_node, "firmware1", NULL) || |
| of_get_property(pdev->dev.of_node, "firmware2", NULL)) { |
| switch (ltq_soc_type()) { |
| case SOC_TYPE_VR9: |
| if (of_property_read_string(pdev->dev.of_node, |
| "firmware1", &fw_name)) { |
| dev_err(&pdev->dev, |
| "failed to load firmware filename\n"); |
| return 0; |
| } |
| break; |
| case SOC_TYPE_VR9_2: |
| if (of_property_read_string(pdev->dev.of_node, |
| "firmware2", &fw_name)) { |
| dev_err(&pdev->dev, |
| "failed to load firmware filename\n"); |
| return 0; |
| } |
| break; |
| } |
| } else if (of_property_read_string(pdev->dev.of_node, |
| "firmware", &fw_name)) { |
| dev_err(&pdev->dev, "failed to load firmware filename\n"); |
| return 0; |
| } |
| |
| dev_info(&pdev->dev, "requesting %s\n", fw_name); |
| if (request_firmware(&fw, fw_name, &pdev->dev)) { |
| dev_err(&pdev->dev, "failed to load firmware: %s\n", fw_name); |
| return 0; |
| } |
| |
| /* |
| * GPHY cores need the firmware code in a persistent and contiguous |
| * memory area with a 16 kB boundary aligned start address |
| */ |
| size = fw->size + XRX200_GPHY_FW_ALIGN; |
| |
| fw_addr = dma_alloc_coherent(&pdev->dev, size, &dev_addr, GFP_KERNEL); |
| if (fw_addr) { |
| fw_addr = PTR_ALIGN(fw_addr, XRX200_GPHY_FW_ALIGN); |
| dev_addr = ALIGN(dev_addr, XRX200_GPHY_FW_ALIGN); |
| memcpy(fw_addr, fw->data, fw->size); |
| } else { |
| dev_err(&pdev->dev, "failed to alloc firmware memory\n"); |
| } |
| |
| release_firmware(fw); |
| return dev_addr; |
| } |
| |
| static int xway_phy_fw_probe(struct platform_device *pdev) |
| { |
| dma_addr_t fw_addr; |
| struct property *pp; |
| unsigned char *phyids; |
| int i, ret = 0; |
| |
| fw_addr = xway_gphy_load(pdev); |
| if (!fw_addr) |
| return -EINVAL; |
| pp = of_find_property(pdev->dev.of_node, "phys", NULL); |
| if (!pp) |
| return -ENOENT; |
| phyids = pp->value; |
| for (i = 0; i < pp->length && !ret; i++) |
| ret = xrx200_gphy_boot(&pdev->dev, phyids[i], fw_addr); |
| if (!ret) |
| mdelay(100); |
| return ret; |
| } |
| |
| static const struct of_device_id xway_phy_match[] = { |
| { .compatible = "lantiq,phy-xrx200" }, |
| {}, |
| }; |
| MODULE_DEVICE_TABLE(of, xway_phy_match); |
| |
| static struct platform_driver xway_phy_driver = { |
| .probe = xway_phy_fw_probe, |
| .driver = { |
| .name = "phy-xrx200", |
| .of_match_table = xway_phy_match, |
| }, |
| }; |
| |
| module_platform_driver(xway_phy_driver); |
| |
| MODULE_AUTHOR("John Crispin <john@phrozen.org>"); |
| MODULE_DESCRIPTION("Lantiq XRX200 PHY Firmware Loader"); |
| MODULE_LICENSE("GPL"); |