Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

wifi: brcmfmac: pcie: Read Apple OTP information

On Apple platforms, the One Time Programmable ROM in the Broadcom chips
contains information about the specific board design (module, vendor,
version) that is required to select the correct NVRAM file. Parse this
OTP ROM and extract the required strings.

Note that the user OTP offset/size is per-chip. This patch does not add
any chips yet.

Reviewed-by: Arend van Spriel <arend.vanspriel@broadcom.com>
Signed-off-by: Hector Martin <marcan@marcan.st>
Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
Signed-off-by: Kalle Valo <kvalo@kernel.org>
Link: https://lore.kernel.org/r/E1oZDni-0077aM-I6@rmk-PC.armlinux.org.uk

authored by

Hector Martin and committed by
Kalle Valo
e63efbca 7cb46e72

+219
+218
drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c
··· 256 256 u32 wrapbase; 257 257 }; 258 258 259 + #define BRCMF_OTP_MAX_PARAM_LEN 16 260 + 261 + struct brcmf_otp_params { 262 + char module[BRCMF_OTP_MAX_PARAM_LEN]; 263 + char vendor[BRCMF_OTP_MAX_PARAM_LEN]; 264 + char version[BRCMF_OTP_MAX_PARAM_LEN]; 265 + bool valid; 266 + }; 267 + 259 268 struct brcmf_pciedev_info { 260 269 enum brcmf_pcie_state state; 261 270 bool in_irq; ··· 292 283 void (*write_ptr)(struct brcmf_pciedev_info *devinfo, u32 mem_offset, 293 284 u16 value); 294 285 struct brcmf_mp_device *settings; 286 + struct brcmf_otp_params otp; 295 287 }; 296 288 297 289 struct brcmf_pcie_ringbuf { ··· 363 353 struct brcmf_fw_request *fwreq); 364 354 static struct brcmf_fw_request * 365 355 brcmf_pcie_prepare_fw_request(struct brcmf_pciedev_info *devinfo); 356 + 357 + static u16 358 + brcmf_pcie_read_reg16(struct brcmf_pciedev_info *devinfo, u32 reg_offset) 359 + { 360 + void __iomem *address = devinfo->regs + reg_offset; 361 + 362 + return ioread16(address); 363 + } 366 364 367 365 static u32 368 366 brcmf_pcie_read_reg32(struct brcmf_pciedev_info *devinfo, u32 reg_offset) ··· 517 499 } 518 500 519 501 502 + #define READCC32(devinfo, reg) brcmf_pcie_read_reg32(devinfo, \ 503 + CHIPCREGOFFS(reg)) 520 504 #define WRITECC32(devinfo, reg, value) brcmf_pcie_write_reg32(devinfo, \ 521 505 CHIPCREGOFFS(reg), value) 522 506 ··· 1754 1734 .write32 = brcmf_pcie_buscore_write32, 1755 1735 }; 1756 1736 1737 + #define BRCMF_OTP_SYS_VENDOR 0x15 1738 + #define BRCMF_OTP_BRCM_CIS 0x80 1739 + 1740 + #define BRCMF_OTP_VENDOR_HDR 0x00000008 1741 + 1742 + static int 1743 + brcmf_pcie_parse_otp_sys_vendor(struct brcmf_pciedev_info *devinfo, 1744 + u8 *data, size_t size) 1745 + { 1746 + int idx = 4; 1747 + const char *chip_params; 1748 + const char *board_params; 1749 + const char *p; 1750 + 1751 + /* 4-byte header and two empty strings */ 1752 + if (size < 6) 1753 + return -EINVAL; 1754 + 1755 + if (get_unaligned_le32(data) != BRCMF_OTP_VENDOR_HDR) 1756 + return -EINVAL; 1757 + 1758 + chip_params = &data[idx]; 1759 + 1760 + /* Skip first string, including terminator */ 1761 + idx += strnlen(chip_params, size - idx) + 1; 1762 + if (idx >= size) 1763 + return -EINVAL; 1764 + 1765 + board_params = &data[idx]; 1766 + 1767 + /* Skip to terminator of second string */ 1768 + idx += strnlen(board_params, size - idx); 1769 + if (idx >= size) 1770 + return -EINVAL; 1771 + 1772 + /* At this point both strings are guaranteed NUL-terminated */ 1773 + brcmf_dbg(PCIE, "OTP: chip_params='%s' board_params='%s'\n", 1774 + chip_params, board_params); 1775 + 1776 + p = skip_spaces(board_params); 1777 + while (*p) { 1778 + char tag = *p++; 1779 + const char *end; 1780 + size_t len; 1781 + 1782 + if (*p++ != '=') /* implicit NUL check */ 1783 + return -EINVAL; 1784 + 1785 + /* *p might be NUL here, if so end == p and len == 0 */ 1786 + end = strchrnul(p, ' '); 1787 + len = end - p; 1788 + 1789 + /* leave 1 byte for NUL in destination string */ 1790 + if (len > (BRCMF_OTP_MAX_PARAM_LEN - 1)) 1791 + return -EINVAL; 1792 + 1793 + /* Copy len characters plus a NUL terminator */ 1794 + switch (tag) { 1795 + case 'M': 1796 + strscpy(devinfo->otp.module, p, len + 1); 1797 + break; 1798 + case 'V': 1799 + strscpy(devinfo->otp.vendor, p, len + 1); 1800 + break; 1801 + case 'm': 1802 + strscpy(devinfo->otp.version, p, len + 1); 1803 + break; 1804 + } 1805 + 1806 + /* Skip to next arg, if any */ 1807 + p = skip_spaces(end); 1808 + } 1809 + 1810 + brcmf_dbg(PCIE, "OTP: module=%s vendor=%s version=%s\n", 1811 + devinfo->otp.module, devinfo->otp.vendor, 1812 + devinfo->otp.version); 1813 + 1814 + if (!devinfo->otp.module[0] || 1815 + !devinfo->otp.vendor[0] || 1816 + !devinfo->otp.version[0]) 1817 + return -EINVAL; 1818 + 1819 + devinfo->otp.valid = true; 1820 + return 0; 1821 + } 1822 + 1823 + static int 1824 + brcmf_pcie_parse_otp(struct brcmf_pciedev_info *devinfo, u8 *otp, size_t size) 1825 + { 1826 + int p = 0; 1827 + int ret = -EINVAL; 1828 + 1829 + brcmf_dbg(PCIE, "parse_otp size=%zd\n", size); 1830 + 1831 + while (p < (size - 1)) { 1832 + u8 type = otp[p]; 1833 + u8 length = otp[p + 1]; 1834 + 1835 + if (type == 0) 1836 + break; 1837 + 1838 + if ((p + 2 + length) > size) 1839 + break; 1840 + 1841 + switch (type) { 1842 + case BRCMF_OTP_SYS_VENDOR: 1843 + brcmf_dbg(PCIE, "OTP @ 0x%x (%d): SYS_VENDOR\n", 1844 + p, length); 1845 + ret = brcmf_pcie_parse_otp_sys_vendor(devinfo, 1846 + &otp[p + 2], 1847 + length); 1848 + break; 1849 + case BRCMF_OTP_BRCM_CIS: 1850 + brcmf_dbg(PCIE, "OTP @ 0x%x (%d): BRCM_CIS\n", 1851 + p, length); 1852 + break; 1853 + default: 1854 + brcmf_dbg(PCIE, "OTP @ 0x%x (%d): Unknown type 0x%x\n", 1855 + p, length, type); 1856 + break; 1857 + } 1858 + 1859 + p += 2 + length; 1860 + } 1861 + 1862 + return ret; 1863 + } 1864 + 1865 + static int brcmf_pcie_read_otp(struct brcmf_pciedev_info *devinfo) 1866 + { 1867 + const struct pci_dev *pdev = devinfo->pdev; 1868 + struct brcmf_bus *bus = dev_get_drvdata(&pdev->dev); 1869 + u32 coreid, base, words, idx, sromctl; 1870 + u16 *otp; 1871 + struct brcmf_core *core; 1872 + int ret; 1873 + 1874 + switch (devinfo->ci->chip) { 1875 + default: 1876 + /* OTP not supported on this chip */ 1877 + return 0; 1878 + } 1879 + 1880 + core = brcmf_chip_get_core(devinfo->ci, coreid); 1881 + if (!core) { 1882 + brcmf_err(bus, "No OTP core\n"); 1883 + return -ENODEV; 1884 + } 1885 + 1886 + if (coreid == BCMA_CORE_CHIPCOMMON) { 1887 + /* Chips with OTP accessed via ChipCommon need additional 1888 + * handling to access the OTP 1889 + */ 1890 + brcmf_pcie_select_core(devinfo, coreid); 1891 + sromctl = READCC32(devinfo, sromcontrol); 1892 + 1893 + if (!(sromctl & BCMA_CC_SROM_CONTROL_OTP_PRESENT)) { 1894 + /* Chip lacks OTP, try without it... */ 1895 + brcmf_err(bus, 1896 + "OTP unavailable, using default firmware\n"); 1897 + return 0; 1898 + } 1899 + 1900 + /* Map OTP to shadow area */ 1901 + WRITECC32(devinfo, sromcontrol, 1902 + sromctl | BCMA_CC_SROM_CONTROL_OTPSEL); 1903 + } 1904 + 1905 + otp = kcalloc(words, sizeof(u16), GFP_KERNEL); 1906 + if (!otp) 1907 + return -ENOMEM; 1908 + 1909 + /* Map bus window to SROM/OTP shadow area in core */ 1910 + base = brcmf_pcie_buscore_prep_addr(devinfo->pdev, base + core->base); 1911 + 1912 + brcmf_dbg(PCIE, "OTP data:\n"); 1913 + for (idx = 0; idx < words; idx++) { 1914 + otp[idx] = brcmf_pcie_read_reg16(devinfo, base + 2 * idx); 1915 + brcmf_dbg(PCIE, "[%8x] 0x%04x\n", base + 2 * idx, otp[idx]); 1916 + } 1917 + 1918 + if (coreid == BCMA_CORE_CHIPCOMMON) { 1919 + brcmf_pcie_select_core(devinfo, coreid); 1920 + WRITECC32(devinfo, sromcontrol, sromctl); 1921 + } 1922 + 1923 + ret = brcmf_pcie_parse_otp(devinfo, (u8 *)otp, 2 * words); 1924 + kfree(otp); 1925 + 1926 + return ret; 1927 + } 1928 + 1757 1929 #define BRCMF_PCIE_FW_CODE 0 1758 1930 #define BRCMF_PCIE_FW_NVRAM 1 1759 1931 #define BRCMF_PCIE_FW_CLM 2 ··· 2141 1929 ret = brcmf_alloc(&devinfo->pdev->dev, devinfo->settings); 2142 1930 if (ret) 2143 1931 goto fail_bus; 1932 + 1933 + ret = brcmf_pcie_read_otp(devinfo); 1934 + if (ret) { 1935 + brcmf_err(bus, "failed to parse OTP\n"); 1936 + goto fail_brcmf; 1937 + } 2144 1938 2145 1939 fwreq = brcmf_pcie_prepare_fw_request(devinfo); 2146 1940 if (!fwreq) {
+1
include/linux/bcma/bcma_driver_chipcommon.h
··· 271 271 #define BCMA_CC_SROM_CONTROL_OP_WRDIS 0x40000000 272 272 #define BCMA_CC_SROM_CONTROL_OP_WREN 0x60000000 273 273 #define BCMA_CC_SROM_CONTROL_OTPSEL 0x00000010 274 + #define BCMA_CC_SROM_CONTROL_OTP_PRESENT 0x00000020 274 275 #define BCMA_CC_SROM_CONTROL_LOCK 0x00000008 275 276 #define BCMA_CC_SROM_CONTROL_SIZE_MASK 0x00000006 276 277 #define BCMA_CC_SROM_CONTROL_SIZE_1K 0x00000000