(Patches split per file for review, see cover letter for more information) Signed-off-by: Lachlan Hodges --- drivers/net/wireless/morsemicro/mm81x/hw.c | 372 +++++++++++++++++++++ 1 file changed, 372 insertions(+) create mode 100644 drivers/net/wireless/morsemicro/mm81x/hw.c diff --git a/drivers/net/wireless/morsemicro/mm81x/hw.c b/drivers/net/wireless/morsemicro/mm81x/hw.c new file mode 100644 index 000000000000..83cfc2c693af --- /dev/null +++ b/drivers/net/wireless/morsemicro/mm81x/hw.c @@ -0,0 +1,372 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2017-2026 Morse Micro + */ +#include +#include +#include +#include +#include "hif.h" +#include "debug.h" +#include "mac.h" +#include "bus.h" +#include "core.h" +#include "fw.h" +#include "yaps.h" + +#define MM8108_REG_HOST_MAGIC_VALUE 0xDEADBEEF +#define MM8108_REG_RESET_VALUE 0xDEAD + +#define MM8108_REG_SDIO_DEVICE_ADDR 0x0000207C + +#define MM8108_REG_SDIO_DEVICE_BURST_OFFSET 9 +#define MM8108_REG_TRGR_BASE 0x00003c00 +#define MM8108_REG_INT_BASE 0x00003c50 +#define MM8108_REG_MSI_ADDRESS 0x00004100 +#define MM8108_REG_MSI_VALUE 0x1 +#define MM8108_REG_MANIFEST_PTR_ADDRESS 0x00002d40 +#define MM8108_REG_APPS_BOOT_ADDR 0x00002084 +#define MM8108_REG_RESET 0x000020AC +#define MM8108_REG_AON_COUNT 2 + +#define MM8108_REG_AON_ADDR 0x00002114 +#define MM8108_REG_AON_LATCH_ADDR 0x00405020 +#define MM8108_REG_AON_LATCH_MASK 0x1 +#define MM8108_REG_AON_RESET_USB_VALUE 0x8 +#define MM8108_APPS_MAC_DMEM_ADDR_START 0x00100000 + +#define MM8108_REG_RC_CLK_POWER_OFF_ADDR 0x00405020 +#define MM8108_REG_RC_CLK_POWER_OFF_MASK 0x00000040 +#define MM8108_SLOW_RC_POWER_ON_DELAY_MS 2 + +#define MM8108_RESET_DELAY_TIME_MS 400 + +#define MM8108_REG_OTPCTRL_PLDO 0x00004014 +#define MM8108_REG_OTPCTRL_PENVDD2 0x00004010 +#define MM8108_REG_OTPCTRL_PDSTB 0x00004018 +#define MM8108_REG_OTPCTRL_PTM 0x0000401c +#define MM8108_REG_OTPCTRL_PCE 0x00004020 +#define MM8108_REG_OTPCTRL_PA 0x00004034 +#define MM8108_REG_OTPCTRL_PECCRDB 0x00004048 +#define MM8108_REG_OTPCTRL_ACTION_AUTO_RD_START 0x0000400c +#define MM8108_REG_OTPCTRL_PDOUT 0x00004040 + +#define MM81X_OTP_MAC_ADDR_2_BANK_NUM 27 +#define MM81X_OTP_MAC_ADDR_1_BANK_NUM 26 +#define MM81X_OTP_MAC_ADDR_1_MASK GENMASK(31, 16) +#define MM81X_OTP_BOARD_TYPE_BANK_NUM 26 +#define MM81X_OTP_BOARD_TYPE_MASK GENMASK(15, 0) + +#define MM810X_BOARD_TYPE_MAX_VALUE (MM81X_OTP_BOARD_TYPE_MASK - 1) + +static void mm81x_hw_otp_power_up(struct mm81x *mm) +{ + mm81x_reg32_write(mm, MM8108_REG_OTPCTRL_PENVDD2, 1); + udelay(2); + + mm81x_reg32_write(mm, MM8108_REG_OTPCTRL_PLDO, 1); + usleep_range(10, 20); + + mm81x_reg32_write(mm, MM8108_REG_OTPCTRL_PDSTB, 1); + udelay(3); +} + +static void mm81x_hw_otp_power_down(struct mm81x *mm) +{ + mm81x_reg32_write(mm, MM8108_REG_OTPCTRL_PDSTB, 0); + mm81x_reg32_write(mm, MM8108_REG_OTPCTRL_PLDO, 0); + mm81x_reg32_write(mm, MM8108_REG_OTPCTRL_PENVDD2, 0); +} + +static void mm81x_hw_otp_read_enable(struct mm81x *mm) +{ + mm81x_reg32_write(mm, MM8108_REG_OTPCTRL_PTM, 0); + mm81x_reg32_write(mm, MM8108_REG_OTPCTRL_PCE, 1); + usleep_range(10, 20); +} + +static void mm81x_hw_otp_read_disable(struct mm81x *mm) +{ + mm81x_reg32_write(mm, MM8108_REG_OTPCTRL_PCE, 0); + udelay(1); +} + +static int mm81x_hw_otp_read(struct mm81x *mm, u8 bank_num, u32 *buf, + u8 ignore_ecc) +{ + u32 auto_rd_start_tmp; + u32 auto_rd_start = 1; + int i; + + mm81x_reg32_write(mm, MM8108_REG_OTPCTRL_PA, bank_num); + mm81x_reg32_write(mm, MM8108_REG_OTPCTRL_PECCRDB, ignore_ecc); + + mm81x_reg32_read(mm, MM8108_REG_OTPCTRL_ACTION_AUTO_RD_START, + &auto_rd_start_tmp); + auto_rd_start_tmp &= 0xfffffffe; + + mm81x_reg32_write(mm, MM8108_REG_OTPCTRL_ACTION_AUTO_RD_START, + auto_rd_start | auto_rd_start_tmp); + + /* Attempt reading up to 5 times. */ + for (i = 0; i < 5 && auto_rd_start; i++) { + usleep_range(15, 20); + mm81x_reg32_read(mm, MM8108_REG_OTPCTRL_ACTION_AUTO_RD_START, + &auto_rd_start_tmp); + auto_rd_start = auto_rd_start_tmp & 0x1; + } + + if (i == 5) + return -EIO; + + mm81x_reg32_read(mm, MM8108_REG_OTPCTRL_PDOUT, buf); + + return 0; +} + +int mm81x_hw_otp_get_board_type(struct mm81x *mm) +{ + int board_type = 0; + u32 otp_word = 0; + int ret; + + mm81x_claim_bus(mm); + mm81x_hw_otp_power_up(mm); + mm81x_hw_otp_read_enable(mm); + + ret = mm81x_hw_otp_read(mm, MM81X_OTP_BOARD_TYPE_BANK_NUM, &otp_word, + 1); + + mm81x_hw_otp_read_disable(mm); + mm81x_hw_otp_power_down(mm); + mm81x_release_bus(mm); + + if (ret) + return -EINVAL; + + board_type = otp_word & MM81X_OTP_BOARD_TYPE_MASK; + + return board_type; +} + +bool mm81x_hw_otp_valid_board_type(u32 board_type) +{ + return board_type > 0 && board_type < MM810X_BOARD_TYPE_MAX_VALUE; +} + +int mm81x_hw_otp_get_mac_addr(struct mm81x *mm) +{ + u32 mac1 = 0; + u32 mac2 = 0; + int ret = 0; + + mm81x_claim_bus(mm); + mm81x_hw_otp_power_up(mm); + mm81x_hw_otp_read_enable(mm); + + ret = mm81x_hw_otp_read(mm, MM81X_OTP_MAC_ADDR_1_BANK_NUM, &mac1, 1); + if (ret) + goto exit; + + ret = mm81x_hw_otp_read(mm, MM81X_OTP_MAC_ADDR_2_BANK_NUM, &mac2, 1); + if (ret) + goto exit; + + *((u16 *)&mm->macaddr[0]) = (mac1 & MM81X_OTP_MAC_ADDR_1_MASK) >> 16; + *((u32 *)&mm->macaddr[2]) = mac2; + +exit: + mm81x_hw_otp_read_disable(mm); + mm81x_hw_otp_power_down(mm); + mm81x_release_bus(mm); + + return ret; +} + +void mm81x_hw_irq_enable(struct mm81x *mm, u32 irq, bool enable) +{ + u32 irq_en, irq_en_addr = irq < 32 ? MM81X_REG_INT1_EN(mm) : + MM81X_REG_INT2_EN(mm); + u32 irq_clr_addr = irq < 32 ? MM81X_REG_INT1_CLR(mm) : + MM81X_REG_INT2_CLR(mm); + u32 mask = irq < 32 ? (1 << irq) : (1 << (irq - 32)); + + mm81x_claim_bus(mm); + mm81x_reg32_read(mm, irq_en_addr, &irq_en); + if (enable) + irq_en |= (mask); + else + irq_en &= ~(mask); + mm81x_reg32_write(mm, irq_clr_addr, mask); + mm81x_reg32_write(mm, irq_en_addr, irq_en); + mm81x_release_bus(mm); +} + +int mm81x_hw_irq_handle(struct mm81x *mm) +{ + u32 status1 = 0; + + mm81x_reg32_read(mm, MM81X_REG_INT1_STS(mm), &status1); + + if (status1 & MM81X_HIF_IRQ_MASK_ALL) + mm81x_hif_handle_irq(mm, status1); + + if (status1 & MM81X_INT_BEACON_VIF_MASK_ALL) + mm81x_mac_beacon_irq_handle(mm, status1); + + mm81x_reg32_write(mm, MM81X_REG_INT1_CLR(mm), status1); + + return status1 ? 1 : 0; +} + +void mm81x_hw_irq_clear(struct mm81x *mm) +{ + mm81x_claim_bus(mm); + mm81x_reg32_write(mm, MM81X_REG_INT1_CLR(mm), 0xFFFFFFFF); + mm81x_reg32_write(mm, MM81X_REG_INT2_CLR(mm), 0xFFFFFFFF); + mm81x_release_bus(mm); +} + +int mm81x_hw_toggle_aon_latch(struct mm81x *mm) +{ + u32 address = MM81X_REG_AON_LATCH_ADDR(mm); + u32 mask = MM81X_REG_AON_LATCH_MASK(mm); + u32 latch; + + if (address) { + mm81x_reg32_read(mm, address, &latch); + mm81x_reg32_write(mm, address, latch & ~(mask)); + mdelay(5); + mm81x_reg32_write(mm, address, latch | mask); + mdelay(5); + mm81x_reg32_write(mm, address, latch & ~(mask)); + mdelay(5); + } + + return 0; +} + +void mm81x_hw_enable_stop_notifications(struct mm81x *mm, bool enable) +{ + mm81x_hw_irq_enable(mm, MM81X_INT_HW_STOP_NOTIFICATION_NUM, enable); +} + +void mm81x_hw_enable_burst_mode(struct mm81x *mm, const u8 burst_mode) +{ + u32 reg32_value; + + mm81x_claim_bus(mm); + if (mm81x_reg32_read(mm, MM8108_REG_SDIO_DEVICE_ADDR, ®32_value)) + goto end; + + reg32_value &= ~(u32)(SDIO_WORD_BURST_MASK + << MM8108_REG_SDIO_DEVICE_BURST_OFFSET); + reg32_value |= (u32)(burst_mode << MM8108_REG_SDIO_DEVICE_BURST_OFFSET); + + mm81x_dbg(mm, MM81X_DBG_ANY, + "Setting Burst mode to %d Writing 0x%08X to the register", + burst_mode, reg32_value); + + if (mm81x_reg32_write(mm, MM8108_REG_SDIO_DEVICE_ADDR, reg32_value)) + goto end; + +end: + mm81x_release_bus(mm); +} + +static int mm81x_hw_enable_internal_slow_clock(struct mm81x *mm) +{ + u32 rc_clock_reg_value; + int ret = 0; + + mm81x_dbg(mm, MM81X_DBG_ANY, "Enabling internal slow clock"); + + ret = mm81x_reg32_read(mm, MM8108_REG_RC_CLK_POWER_OFF_ADDR, + &rc_clock_reg_value); + if (ret) + goto exit; + + rc_clock_reg_value &= ~MM8108_REG_RC_CLK_POWER_OFF_MASK; + ret = mm81x_reg32_write(mm, MM8108_REG_RC_CLK_POWER_OFF_ADDR, + rc_clock_reg_value); + if (ret) + goto exit; + + ret = mm81x_hw_toggle_aon_latch(mm); + if (ret) + goto exit; + + /* Wait for the clock to turn on and settle */ + mdelay(MM8108_SLOW_RC_POWER_ON_DELAY_MS); +exit: + return ret; +} + +int mm81x_hw_digital_reset(struct mm81x *mm) +{ + int ret = 0; + + mm81x_claim_bus(mm); + + /* This should be the first step in digital reset, do not reorder */ + ret = mm81x_hw_enable_internal_slow_clock(mm); + if (ret) + goto exit; + + if (mm->bus_type == MM81X_BUS_TYPE_USB) { +#ifdef CONFIG_MM81X_USB + ret = mm81x_usb_ndr_reset(mm); +#endif + goto usb_done; + } + + if (MM81X_REG_RESET(mm) != 0) + ret = mm81x_reg32_write(mm, MM81X_REG_RESET(mm), + MM81X_REG_RESET_VALUE(mm)); + +usb_done: + msleep(MM8108_RESET_DELAY_TIME_MS); +exit: + mm81x_release_bus(mm); + + if (!ret) + mm->chip_was_reset = true; + + return ret; +} + +void mm81x_hw_pre_firmware_ndr_hook(struct mm81x *mm) +{ + /* We need disable bursting for firmware download/init procedure */ + mm81x_bus_config_burst_mode(mm, false); +} + +void mm81x_hw_post_firmware_ndr_hook(struct mm81x *mm) +{ + /* We are safe here to reenable bursting again, if supported */ + mm81x_bus_config_burst_mode(mm, true); +} + +const struct mm81x_regs mm8108_regs = { + .chip_id_address = MM8108_REG_CHIP_ID, + .irq_base_address = MM8108_REG_INT_BASE, + .trgr_base_address = MM8108_REG_TRGR_BASE, + .cpu_reset_address = MM8108_REG_RESET, + .cpu_reset_value = MM8108_REG_RESET_VALUE, + .manifest_ptr_address = MM8108_REG_MANIFEST_PTR_ADDRESS, + .msi_address = MM8108_REG_MSI_ADDRESS, + .msi_value = MM8108_REG_MSI_VALUE, + .magic_num_value = MM8108_REG_HOST_MAGIC_VALUE, + .early_clk_ctrl_value = 0, + .pager_base_address = MM8108_APPS_MAC_DMEM_ADDR_START, + .aon_latch = MM8108_REG_AON_LATCH_ADDR, + .aon_latch_mask = MM8108_REG_AON_LATCH_MASK, + .aon_reset_usb_value = MM8108_REG_AON_RESET_USB_VALUE, + .aon = MM8108_REG_AON_ADDR, + .aon_count = MM8108_REG_AON_COUNT, + .boot_address = MM8108_REG_APPS_BOOT_ADDR, +}; + +/* B2 ROM_LINKED */ +MODULE_FIRMWARE(MM81X_FW_DIR "/" MM8108_FW_BASE MM8108B2_REV_STRING + FW_ROM_LINKED_STRING MM81X_FW_EXT); -- 2.43.0