1e7e333cbSJonas Gorski /*
2e7e333cbSJonas Gorski * This file is subject to the terms and conditions of the GNU General Public
3e7e333cbSJonas Gorski * License. See the file "COPYING" in the main directory of this archive
4e7e333cbSJonas Gorski * for more details.
5e7e333cbSJonas Gorski *
6e7e333cbSJonas Gorski * Copyright (C) 2008 Maxime Bizon <mbizon@freebox.fr>
7e7e333cbSJonas Gorski * Copyright (C) 2008 Florian Fainelli <florian@openwrt.org>
8e7e333cbSJonas Gorski * Copyright (C) 2012 Jonas Gorski <jonas.gorski@gmail.com>
9e7e333cbSJonas Gorski */
10e7e333cbSJonas Gorski
11e7e333cbSJonas Gorski #define pr_fmt(fmt) "bcm63xx_nvram: " fmt
12e7e333cbSJonas Gorski
135a8b0b13SSimon Arlott #include <linux/bcm963xx_nvram.h>
14e7e333cbSJonas Gorski #include <linux/init.h>
15ce8f0d06SJonas Gorski #include <linux/crc32.h>
16e7e333cbSJonas Gorski #include <linux/export.h>
17e7e333cbSJonas Gorski #include <linux/kernel.h>
18e7e333cbSJonas Gorski #include <linux/if_ether.h>
19e7e333cbSJonas Gorski
20e7e333cbSJonas Gorski #include <bcm63xx_nvram.h>
21e7e333cbSJonas Gorski
22*b0a119fdSRalf Baechle #define BCM63XX_DEFAULT_PSI_SIZE 64
23*b0a119fdSRalf Baechle
24e7e333cbSJonas Gorski static struct bcm963xx_nvram nvram;
25e7e333cbSJonas Gorski static int mac_addr_used;
26e7e333cbSJonas Gorski
bcm63xx_nvram_init(void * addr)2797367519SJonas Gorski void __init bcm63xx_nvram_init(void *addr)
28e7e333cbSJonas Gorski {
29ce8f0d06SJonas Gorski u32 crc, expected_crc;
300680dc86SFlorian Fainelli u8 hcs_mac_addr[ETH_ALEN] = { 0x00, 0x10, 0x18, 0xff, 0xff, 0xff };
31e7e333cbSJonas Gorski
32e7e333cbSJonas Gorski /* extract nvram data */
335a8b0b13SSimon Arlott memcpy(&nvram, addr, BCM963XX_NVRAM_V5_SIZE);
34e7e333cbSJonas Gorski
35e7e333cbSJonas Gorski /* check checksum before using data */
365a8b0b13SSimon Arlott if (bcm963xx_nvram_checksum(&nvram, &expected_crc, &crc))
3797367519SJonas Gorski pr_warn("nvram checksum failed, contents may be invalid (expected %08x, got %08x)\n",
3897367519SJonas Gorski expected_crc, crc);
390680dc86SFlorian Fainelli
400680dc86SFlorian Fainelli /* Cable modems have a different NVRAM which is embedded in the eCos
410680dc86SFlorian Fainelli * firmware and not easily extractible, give at least a MAC address
420680dc86SFlorian Fainelli * pool.
430680dc86SFlorian Fainelli */
440680dc86SFlorian Fainelli if (BCMCPU_IS_3368()) {
450680dc86SFlorian Fainelli memcpy(nvram.mac_addr_base, hcs_mac_addr, ETH_ALEN);
460680dc86SFlorian Fainelli nvram.mac_addr_count = 2;
470680dc86SFlorian Fainelli }
48e7e333cbSJonas Gorski }
49e7e333cbSJonas Gorski
bcm63xx_nvram_get_name(void)50e7e333cbSJonas Gorski u8 *bcm63xx_nvram_get_name(void)
51e7e333cbSJonas Gorski {
52e7e333cbSJonas Gorski return nvram.name;
53e7e333cbSJonas Gorski }
54e7e333cbSJonas Gorski EXPORT_SYMBOL(bcm63xx_nvram_get_name);
55e7e333cbSJonas Gorski
bcm63xx_nvram_get_mac_address(u8 * mac)56e7e333cbSJonas Gorski int bcm63xx_nvram_get_mac_address(u8 *mac)
57e7e333cbSJonas Gorski {
58e7e333cbSJonas Gorski u8 *oui;
59e7e333cbSJonas Gorski int count;
60e7e333cbSJonas Gorski
61e7e333cbSJonas Gorski if (mac_addr_used >= nvram.mac_addr_count) {
62e7e333cbSJonas Gorski pr_err("not enough mac addresses\n");
63e7e333cbSJonas Gorski return -ENODEV;
64e7e333cbSJonas Gorski }
65e7e333cbSJonas Gorski
66e7e333cbSJonas Gorski memcpy(mac, nvram.mac_addr_base, ETH_ALEN);
67e7e333cbSJonas Gorski oui = mac + ETH_ALEN/2 - 1;
68e7e333cbSJonas Gorski count = mac_addr_used;
69e7e333cbSJonas Gorski
70e7e333cbSJonas Gorski while (count--) {
71e7e333cbSJonas Gorski u8 *p = mac + ETH_ALEN - 1;
72e7e333cbSJonas Gorski
73e7e333cbSJonas Gorski do {
74e7e333cbSJonas Gorski (*p)++;
75e7e333cbSJonas Gorski if (*p != 0)
76e7e333cbSJonas Gorski break;
77e7e333cbSJonas Gorski p--;
78e7e333cbSJonas Gorski } while (p != oui);
79e7e333cbSJonas Gorski
80e7e333cbSJonas Gorski if (p == oui) {
81e7e333cbSJonas Gorski pr_err("unable to fetch mac address\n");
82e7e333cbSJonas Gorski return -ENODEV;
83e7e333cbSJonas Gorski }
84e7e333cbSJonas Gorski }
85e7e333cbSJonas Gorski
86e7e333cbSJonas Gorski mac_addr_used++;
87e7e333cbSJonas Gorski return 0;
88e7e333cbSJonas Gorski }
89e7e333cbSJonas Gorski EXPORT_SYMBOL(bcm63xx_nvram_get_mac_address);
90*b0a119fdSRalf Baechle
bcm63xx_nvram_get_psi_size(void)91*b0a119fdSRalf Baechle int bcm63xx_nvram_get_psi_size(void)
92*b0a119fdSRalf Baechle {
93*b0a119fdSRalf Baechle if (nvram.psi_size > 0)
94*b0a119fdSRalf Baechle return nvram.psi_size;
95*b0a119fdSRalf Baechle
96*b0a119fdSRalf Baechle return BCM63XX_DEFAULT_PSI_SIZE;
97*b0a119fdSRalf Baechle }
98*b0a119fdSRalf Baechle EXPORT_SYMBOL(bcm63xx_nvram_get_psi_size);
99