diff --git a/feeds/qca-wifi-7/ath12k-wifi/Makefile b/feeds/qca-wifi-7/ath12k-wifi/Makefile index 236efb52d..e483581b2 100755 --- a/feeds/qca-wifi-7/ath12k-wifi/Makefile +++ b/feeds/qca-wifi-7/ath12k-wifi/Makefile @@ -83,6 +83,11 @@ $(call Package/ath12k-wifi-default) TITLE:=board-2.bin for NWA130BE endef +define Package/ath12k-wifi-cig-wf672 +$(call Package/ath12k-wifi-default) + TITLE:=board-2.bin for WF672 +endef + define Package/ath12k-wifi-cig-wf189/install $(INSTALL_DIR) $(1)/lib/firmware/ath12k/QCN92XX/hw1.0/ $(INSTALL_DIR) $(1)/lib/firmware/ath12k/IPQ5332/hw1.0/ @@ -160,6 +165,13 @@ define Package/ath12k-wifi-zyxel-nwa130be/install $(INSTALL_DATA) ./board-2.bin.nwa130be.IPQ5332 $(1)/lib/firmware/ath12k/IPQ5332/hw1.0/board-2.bin endef +define Package/ath12k-wifi-cig-wf672/install + $(INSTALL_DIR) $(1)/lib/firmware/ath12k/QCN92XX/hw1.0/ + $(INSTALL_DIR) $(1)/lib/firmware/ath12k/IPQ5332/hw1.0/ + $(INSTALL_DATA) ./board-2.bin.wf672.QCN92XX $(1)/lib/firmware/ath12k/QCN92XX/hw1.0/board-2.bin + $(INSTALL_DATA) ./board-2.bin.wf672.IPQ5332 $(1)/lib/firmware/ath12k/IPQ5332/hw1.0/board-2.bin +endef + $(eval $(call BuildPackage,ath12k-wifi-cig-wf189)) $(eval $(call BuildPackage,ath12k-wifi-edgecore-eap105)) $(eval $(call BuildPackage,ath12k-wifi-sonicfi-rap7110c-341x)) @@ -170,3 +182,4 @@ $(eval $(call BuildPackage,ath12k-wifi-cig-wf189h)) $(eval $(call BuildPackage,ath12k-wifi-sercomm-ap72tip)) $(eval $(call BuildPackage,ath12k-wifi-sercomm-ap72tip-v4)) $(eval $(call BuildPackage,ath12k-wifi-zyxel-nwa130be)) +$(eval $(call BuildPackage,ath12k-wifi-cig-wf672)) diff --git a/feeds/qca-wifi-7/ath12k-wifi/board-2.bin.wf672.IPQ5332 b/feeds/qca-wifi-7/ath12k-wifi/board-2.bin.wf672.IPQ5332 new file mode 100644 index 000000000..cad03b80e Binary files /dev/null and b/feeds/qca-wifi-7/ath12k-wifi/board-2.bin.wf672.IPQ5332 differ diff --git a/feeds/qca-wifi-7/ath12k-wifi/board-2.bin.wf672.QCN92XX b/feeds/qca-wifi-7/ath12k-wifi/board-2.bin.wf672.QCN92XX new file mode 100644 index 000000000..a5107d1e0 Binary files /dev/null and b/feeds/qca-wifi-7/ath12k-wifi/board-2.bin.wf672.QCN92XX differ diff --git a/feeds/qca-wifi-7/cig-wifi-mode-sw/Makefile b/feeds/qca-wifi-7/cig-wifi-mode-sw/Makefile new file mode 100644 index 000000000..8863802b9 --- /dev/null +++ b/feeds/qca-wifi-7/cig-wifi-mode-sw/Makefile @@ -0,0 +1,30 @@ +include $(TOPDIR)/rules.mk +include $(INCLUDE_DIR)/kernel.mk + +PKG_NAME:=cig-wifi-mode-switch +PKG_RELEASE:=1 +PKG_LICENSE:=GPL-2.0 + +include $(INCLUDE_DIR)/package.mk + +define KernelPackage/cig-wifi-mode-sw + SUBMENU:=Other modules + TITLE:=CIG wifi mode switch tool + FILES:=$(PKG_BUILD_DIR)/cig_rf_switch.ko + AUTOLOAD:=$(call AutoLoad,60,cig_rf_switch) +endef + +define KernelPackage/cig-wifi-mode-sw/description + CIG wifi mode switch tool for configure wifi mode 2 bands or 3 bands +endef + +define Build/Compile + $(KERNEL_MAKE) M="$(PKG_BUILD_DIR)" modules +endef + +define KernelPackage/cig-wifi-mode-sw/install + $(INSTALL_DIR) $(1)/usr/sbin/ + $(INSTALL_BIN) ./files/cig_wifi_mode_sw $(1)/usr/sbin/cig_wms +endef + +$(eval $(call KernelPackage,cig-wifi-mode-sw)) diff --git a/feeds/qca-wifi-7/cig-wifi-mode-sw/files/cig_wifi_mode_sw b/feeds/qca-wifi-7/cig-wifi-mode-sw/files/cig_wifi_mode_sw new file mode 100755 index 000000000..157a6eada --- /dev/null +++ b/feeds/qca-wifi-7/cig-wifi-mode-sw/files/cig_wifi_mode_sw @@ -0,0 +1,11 @@ +#!/bin/sh + +band=$1 +if [ $band -eq 2 ] || [ $band -eq 3 ]; then + echo $band > /proc/rf_switch + echo "reboot for switch wifi mode 2/3 bands" + sleep 1 + reboot +else + echo "error band param" +fi diff --git a/feeds/qca-wifi-7/cig-wifi-mode-sw/src/Makefile b/feeds/qca-wifi-7/cig-wifi-mode-sw/src/Makefile new file mode 100644 index 000000000..753bb0af7 --- /dev/null +++ b/feeds/qca-wifi-7/cig-wifi-mode-sw/src/Makefile @@ -0,0 +1 @@ +obj-m += cig_rf_switch.o diff --git a/feeds/qca-wifi-7/cig-wifi-mode-sw/src/cig_rf_switch.c b/feeds/qca-wifi-7/cig-wifi-mode-sw/src/cig_rf_switch.c new file mode 100644 index 000000000..220b2efae --- /dev/null +++ b/feeds/qca-wifi-7/cig-wifi-mode-sw/src/cig_rf_switch.c @@ -0,0 +1,276 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define PROC_NAME "rf_switch" +#define PARTITION_NAME "RF_SWITCH" +#define MAX_DATA_SIZE 2 +#define MAX_MMC_DEVICE 2 + +struct block_device *target_bdev = NULL; +static char current_value[MAX_DATA_SIZE] = "3"; + +static unsigned int __blkdev_sectors_to_bio_pages(sector_t nr_sects) +{ + sector_t pages = DIV_ROUND_UP_SECTOR_T(nr_sects, PAGE_SIZE / 512); + + return min(pages, (sector_t)BIO_MAX_VECS); +} + +struct block_device *find_mmc_partition(void) +{ + struct gendisk *disk = NULL; + unsigned long idx; + struct block_device *bdev = NULL; + unsigned int i; + + for (i = 0; i < MAX_MMC_DEVICE; i++) { + bdev = blkdev_get_by_dev(MKDEV(MMC_BLOCK_MAJOR, i * CONFIG_MMC_BLOCK_MINORS), FMODE_READ | FMODE_WRITE, NULL); + if (IS_ERR(bdev)) { + pr_err("Failed to open MMC device %u: %ld\n", i, PTR_ERR(bdev)); + continue; + } + + disk = bdev->bd_disk; + if (!disk) { + blkdev_put(bdev, FMODE_READ | FMODE_WRITE); + continue; + } + + xa_for_each_start(&disk->part_tbl, idx, bdev, 1) { + if (bdev->bd_meta_info && strcmp(bdev->bd_meta_info->volname, PARTITION_NAME) == 0) { + pr_info("Found RF_SWITCH partition at device %u\n", i); + return bdev; + } + } + + blkdev_put(bdev, FMODE_READ | FMODE_WRITE); + } + + return NULL; +} + +int read_string_from_emmc(struct block_device *bdev, size_t max_length, char *buffer) +{ + struct bio *bio; + struct page *page; + int err = 0; + void *data; + + page = alloc_page(GFP_KERNEL); + if (!page) { + return -ENOMEM; + } + + bio = bio_alloc(bdev, __blkdev_sectors_to_bio_pages(1), REQ_OP_READ, GFP_KERNEL); + bio_set_dev(bio, bdev); + bio->bi_iter.bi_sector = 0; + bio_add_page(bio, page, PAGE_SIZE, 0); + + submit_bio_wait(bio); + + if (bio->bi_status) { + err = -EIO; + goto out_bio; + } + + data = kmap(page); + kunmap(page); + memcpy(buffer, data, max_length - 1); + buffer[max_length - 1] = '\0'; + +out_bio: + bio_put(bio); + __free_page(page); + + return err; +} + +static int rf_switch_proc_show(struct seq_file *m, void *v) +{ + char buffer[MAX_DATA_SIZE] = {0}; + int ret; + + ret = read_string_from_emmc(target_bdev, MAX_DATA_SIZE, buffer); + if (ret) { + seq_printf(m, "%s\n", current_value); + return 0; + } + + if (strcmp(buffer, "2") == 0 || strcmp(buffer, "3") == 0) { + strncpy(current_value, buffer, MAX_DATA_SIZE); + seq_printf(m, "%s\n", current_value); + } else { + seq_printf(m, "%s\n", current_value); + } + + return 0; +} + +static int blkdev_issue_write(struct block_device *bdev, sector_t sector, sector_t nr_sects, gfp_t gfp_mask, struct page *page) +{ + int ret = 0; + sector_t bs_mask; + struct bio *bio; + + int bi_size = 0; + unsigned int sz; + + if (bdev_read_only(bdev)) + return -EPERM; + + bs_mask = (bdev_logical_block_size(bdev) >> 9) - 1; + if ((sector | nr_sects) & bs_mask) + return -EINVAL; + + bio = bio_alloc(bdev, __blkdev_sectors_to_bio_pages(nr_sects), + REQ_OP_WRITE, gfp_mask); + if (!bio) { + pr_err("Couldn't alloc bio"); + return -1; + } + + bio->bi_iter.bi_sector = sector; + bio_set_dev(bio, bdev); + bio_set_op_attrs(bio, REQ_OP_WRITE, 0); + + sz = bdev_logical_block_size(bdev); + bi_size = bio_add_page(bio, page, sz, 0); + + if(bi_size != sz) { + pr_err("Couldn't add page to the log block"); + goto error; + } + if (bio) + { + ret = submit_bio_wait(bio); + bio_put(bio); + } + + return ret; +error: + bio_put(bio); + return -1; +} + +static int write_data_to_emmc(struct block_device *bdev, const unsigned char *data, unsigned char fill_byte) +{ + struct page *page; + void *ptr; + sector_t sector_offset = 0; + int ret = 0; + + if (!bdev || !data) + return -EINVAL; + + page = alloc_page(GFP_KERNEL); + if (!page) { + pr_err("Failed to allocate page\n"); + return -ENOMEM; + } + + ptr = kmap_atomic(page); + + memcpy(ptr, data, MAX_DATA_SIZE); + + memset(ptr + MAX_DATA_SIZE, fill_byte, 512-MAX_DATA_SIZE); + kunmap_atomic(ptr); + + ret = blkdev_issue_write(bdev, sector_offset , 1 ,GFP_ATOMIC, page); + if (ret) { + pr_err("Failed to write to eMMC at offset 0: %d\n", ret); + __free_page(page); + return ret; + } + + sync_blockdev(bdev); + __free_page(page); + return ret; +} + +static ssize_t rf_switch_proc_write(struct file *file, const char __user *user_buffer, size_t count, loff_t *ppos) +{ + unsigned char buffer[MAX_DATA_SIZE] = {0}; + int ret; + + if (count != MAX_DATA_SIZE) + return -EINVAL; + + if (copy_from_user(buffer, user_buffer, count)) + return -EFAULT; + + buffer[count -1] = '\0'; + + if (strcmp(buffer, "2") != 0 && strcmp(buffer, "3") != 0) { + pr_err("Invalid value: %s. Only '2' or '3' are allowed.\n", buffer); + return -EINVAL; + } + + ret = write_data_to_emmc(target_bdev, buffer, 0xFF); + if (ret) { + pr_err("Failed to write to RF_SWITCH\n"); + return ret; + } + + strncpy(current_value, buffer, MAX_DATA_SIZE); + + return count; +} + +static int rf_switch_proc_open(struct inode *inode, struct file *file) +{ + return single_open(file, rf_switch_proc_show, NULL); +} + +static const struct proc_ops rf_switch_proc_fops = { + .proc_open = rf_switch_proc_open, + .proc_read = seq_read, + .proc_write = rf_switch_proc_write, + .proc_lseek = seq_lseek, + .proc_release = single_release, +}; + +static int __init rf_switch_init(void) +{ + target_bdev = find_mmc_partition(); + if (!target_bdev) { + pr_err("Failed to find eMMC card or RF_SWITCH partition\n"); + return -ENOMEM; + } + + if (!proc_create(PROC_NAME, 0666, NULL, &rf_switch_proc_fops)) { + pr_err("Failed to create proc entry\n"); + return -ENOMEM; + } + + pr_info("RF_SWITCH partition proc interface created\n"); + return 0; +} + +static void __exit rf_switch_exit(void) +{ + if (target_bdev) { + blkdev_put(target_bdev, FMODE_READ | FMODE_WRITE); + target_bdev = NULL; + } + remove_proc_entry(PROC_NAME, NULL); + pr_info("RF_SWITCH partition proc interface removed\n"); +} + +module_init(rf_switch_init); +module_exit(rf_switch_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Yunxiang Huang"); +MODULE_DESCRIPTION("RF_SWITCH partition read/write driver"); diff --git a/feeds/qca-wifi-7/ipq53xx/base-files/etc/board.d/02_network b/feeds/qca-wifi-7/ipq53xx/base-files/etc/board.d/02_network index 1c43803f8..a2432d63e 100755 --- a/feeds/qca-wifi-7/ipq53xx/base-files/etc/board.d/02_network +++ b/feeds/qca-wifi-7/ipq53xx/base-files/etc/board.d/02_network @@ -13,6 +13,7 @@ ipq53xx_setup_interfaces() ucidef_set_interfaces_lan_wan "eth1 eth2 eth3 eth4 eth5" "eth0" ;; cig,wf189|\ + cig,wf672|\ edgecore,eap105|\ sercomm,ap72tip|\ sonicfi,rap750w-311a) @@ -43,21 +44,35 @@ qcom_setup_macs() { local board="$1" case $board in - cig,wf189w|\ - cig,wf189h|\ + cig,wf189w|\ + cig,wf189h|\ cig,wf189) - mtd=$(find_mtd_chardev "0:APPSBLENV") - [ -z "$mtd" ] && return; - mac=$(grep eth1addr= $mtd | cut -d= -f2) + mtd=$(find_mtd_chardev "0:APPSBLENV") + [ -z "$mtd" ] && return; + mac=$(grep eth1addr= $mtd | cut -d= -f2) [ -z "$mac" ] && return; wan_mac=$(macaddr_canonicalize $mac) lan_mac=$(macaddr_add "$wan_mac" 1) ucidef_set_network_device_mac eth0 $wan_mac ucidef_set_network_device_mac eth1 $lan_mac ucidef_set_label_macaddr $wan_mac - ucidef_set_wireless_macaddr_base 2g $(macaddr_add "$wan_mac" 2) - ucidef_set_wireless_macaddr_base 5g $(macaddr_add "$wan_mac" 3) - ucidef_set_wireless_macaddr_base 6g $(macaddr_add "$wan_mac" 4) + ucidef_set_wireless_macaddr_base 2g $(macaddr_add "$wan_mac" 2) + ucidef_set_wireless_macaddr_base 5g $(macaddr_add "$wan_mac" 3) + ucidef_set_wireless_macaddr_base 6g $(macaddr_add "$wan_mac" 4) + ;; + cig,wf672) + mmc=$(find_mmc_part "0:APPSBLENV") + [ -z "$mmc" ] && return; + mac=$(grep eth1addr= $mmc | cut -d= -f2) + [ -z "$mac" ] && return; + wan_mac=$(macaddr_canonicalize $mac) + lan_mac=$(macaddr_add "$wan_mac" 1) + ucidef_set_network_device_mac eth0 $wan_mac + ucidef_set_network_device_mac eth1 $lan_mac + ucidef_set_label_macaddr $wan_mac + ucidef_set_wireless_macaddr_base 2g $(macaddr_add "$wan_mac" 2) + ucidef_set_wireless_macaddr_base 5g $(macaddr_add "$wan_mac" 3) + ucidef_set_wireless_macaddr_base 6g $(macaddr_add "$wan_mac" 4) ;; sercomm,ap72tip) wan_mac=$(cat /sys/class/net/eth0/address) diff --git a/feeds/qca-wifi-7/ipq53xx/base-files/etc/hotplug.d/firmware/10-ath12k-caldata b/feeds/qca-wifi-7/ipq53xx/base-files/etc/hotplug.d/firmware/10-ath12k-caldata index 71e79930b..3442e780d 100755 --- a/feeds/qca-wifi-7/ipq53xx/base-files/etc/hotplug.d/firmware/10-ath12k-caldata +++ b/feeds/qca-wifi-7/ipq53xx/base-files/etc/hotplug.d/firmware/10-ath12k-caldata @@ -23,6 +23,39 @@ caldata_extract() { caldata_die "failed to extract calibration data from $mtd" } +cig_ipq5322_cal() { + local ext_ant=0 + + [ -f /sys/firmware/devicetree/base/soc@0/wifi@c0000000/ext_antenna ] && ext_ant=1 + + if [ "$ext_ant" = "0" ]; then + caldata_extract_mmc "0:ART" 0x1000 0x20000 + else + caldata_extract_mmc "0:ART" 0xbd800 0x20000 + fi +} + +cig_qcn92xx_cal() { + local bands=$(cat /proc/rf_switch) + local ext_ant=0 + + [ -f /sys/firmware/devicetree/base/soc@0/wifi@c0000000/ext_antenna ] && ext_ant=1 + + if [ "$bands" = "2" ]; then + if [ "$ext_ant" = "0" ]; then + caldata_extract_mmc "0:ART" 0x8b800 0x2d000 + else + caldata_extract_mmc "0:ART" 0xe3000 0x2d000 + fi + else + if [ "$ext_ant" = "0" ]; then + caldata_extract_mmc "0:ART" 0x58800 0x2d000 + else + caldata_extract_mmc "0:ART" 0x115000 0x2d000 + fi + fi +} + board=$(board_name) case "$FIRMWARE" in ath12k/IPQ5332/hw1.0/caldata.bin) @@ -36,6 +69,9 @@ ath12k/IPQ5332/hw1.0/caldata.bin) zyxel,nwa130be) caldata_extract "0:ART" 0x1000 0x20000 ;; + cig,wf672) + cig_ipq5322_cal + ;; sonicfi,rap7110c-341x) caldata_extract_mmc "0:ART" 0x1000 0xF800 ;; @@ -54,6 +90,9 @@ ath12k/QCN92XX/hw1.0/cal-pci-0001:01:00.0.bin) zyxel,nwa130be) caldata_extract "0:ART" 0x58800 0x2d000 ;; + cig,wf672) + cig_qcn92xx_cal + ;; sonicfi,rap7110c-341x) caldata_extract_mmc "0:ART" 0x58800 0x2d000 ;; diff --git a/feeds/qca-wifi-7/ipq53xx/base-files/lib/upgrade/platform.sh b/feeds/qca-wifi-7/ipq53xx/base-files/lib/upgrade/platform.sh index f7271cb4b..8305293a2 100755 --- a/feeds/qca-wifi-7/ipq53xx/base-files/lib/upgrade/platform.sh +++ b/feeds/qca-wifi-7/ipq53xx/base-files/lib/upgrade/platform.sh @@ -57,6 +57,52 @@ sonicfi_dualimage_check() { fi } +spi_nor_emmc_do_upgrade_bootconfig() { + local tar_file="$1" + + local board_dir=$(tar tf $tar_file | grep -m 1 '^sysupgrade-.*/$') + board_dir=${board_dir%/} + [ -f /proc/boot_info/bootconfig0/getbinary_bootconfig ] || { + echo "bootconfig does not exist" + exit + } + CI_ROOTPART="$(cat /proc/boot_info/bootconfig0/rootfs/upgradepartition)" + CI_KERNPART="$(cat /proc/boot_info/bootconfig0/0:HLOS/upgradepartition)" + + [ -n "$CI_KERNPART" -a -n "$CI_ROOTPART" ] || { + echo "kernel or rootfs partition is unknown" + exit + } + + local primary="0" + [ "$(cat /proc/boot_info/bootconfig0/rootfs/primaryboot)" = "0" ] && primary="1" + echo "$primary" > /proc/boot_info/bootconfig0/rootfs/primaryboot 2>/dev/null + echo "$primary" > /proc/boot_info/bootconfig0/0:HLOS/primaryboot 2>/dev/null + cp /proc/boot_info/bootconfig0/getbinary_bootconfig /tmp/bootconfig + + do_flash_emmc $tar_file $CI_KERNPART $board_dir kernel + do_flash_emmc $tar_file $CI_ROOTPART $board_dir root + + local emmcblock="$(find_mmc_part "rootfs_data")" + if [ -e "$emmcblock" ]; then + mkfs.ext4 -F "$emmcblock" + fi + + for part in "0:BOOTCONFIG" "0:BOOTCONFIG1"; do + local mtdchar=$(echo $(find_mtd_chardev $part) | sed 's/^.\{5\}//') + if [ -n "$mtdchar" ]; then + echo start to update $mtdchar + mtd -qq write /proc/boot_info/bootconfig0/getbinary_bootconfig "/dev/${mtdchar}" 2>/dev/null && echo update mtd $mtdchar + else + emmcblock=$(find_mmc_part $part) + echo erase ${emmcblock} + dd if=/dev/zero of=${emmcblock} 2> /dev/null + echo update $emmcblock + dd if=/tmp/bootconfig of=${emmcblock} 2> /dev/null + fi + done +} + emmc_do_upgrade() { local tar_file="$1" local board_dir=$(tar tf $tar_file | grep -m 1 '^sysupgrade-.*/$') @@ -98,6 +144,9 @@ platform_do_upgrade() { fi nand_upgrade_tar "$1" ;; + cig,wf672) + spi_nor_emmc_do_upgrade_bootconfig $1 + ;; edgecore,eap105) if [ "$(find_mtd_chardev rootfs)" ]; then CI_UBIPART="rootfs" diff --git a/feeds/qca-wifi-7/ipq53xx/dts/ipq5332-cig-wf672.dts b/feeds/qca-wifi-7/ipq53xx/dts/ipq5332-cig-wf672.dts new file mode 100644 index 000000000..7756e42ce --- /dev/null +++ b/feeds/qca-wifi-7/ipq53xx/dts/ipq5332-cig-wf672.dts @@ -0,0 +1,548 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) +/* + * IPQ5332 RDP468 board device tree source + * + * Copyright (c) 2020-2021 The Linux Foundation. All rights reserved. + * Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved. + */ + +/dts-v1/; + +#include +#include +#include +#include "ipq5332.dtsi" +#include "ipq5332-default-memory.dtsi" + +/ { + model = "CIG WF672"; + compatible = "cig,wf672", "qcom,ipq5332-rdp468", "qcom,ipq5332"; + + aliases { + serial0 = &blsp1_uart0; + serial1 = &blsp1_uart1; + ethernet0 = "/soc/dp1"; + ethernet1 = "/soc/dp2"; + led-boot = &led_power_green; + led-failsafe = &led_power_red; + led-running = &led_power_green; + led-upgrade = &led_power_green; + }; + + chosen { + stdout-path = "serial0"; + }; + + soc@0 { + mdio:mdio@90000 { + pinctrl-0 = <&mdio1_pins>; + pinctrl-names = "default"; + /*gpio51 for manhattan reset*/ + phy-reset-gpio = <&tlmm 21 GPIO_ACTIVE_LOW>; + phyaddr_fixup = <0xC90F018>; + uniphyaddr_fixup = <0xC90F014>; + mdio_clk_fixup; /* MDIO clock sequence fix up flag */ + status = "okay"; + + phy0: ethernet-phy@0 { + reg = <1>; + compatible ="ethernet-phy-ieee802.3-c45"; + fixup; + }; + phy1: ethernet-phy@1 { + reg = <30>; + fixup; + }; + }; + + ess-instance { + num_devices = <0x1>; + + ess-switch@3a000000 { + switch_cpu_bmp = <0x1>; /* cpu port bitmap */ + switch_lan_bmp = <0x2>; /* lan port bitmap */ + switch_wan_bmp = <0x4>; /* wan port bitmap */ + switch_mac_mode = <0xc>; /* mac mode for uniphy instance0*/ + switch_mac_mode1 = <0xe>; /* mac mode for uniphy instance1*/ + switch_mac_mode2 = <0xff>; /* mac mode for uniphy instance2*/ + + qcom,port_phyinfo { + port@0 { + port_id = <1>; + phy_address = <1>; + ethernet-phy-ieee802.3-c45; + forced-speed = <2500>; + forced-duplex = <1>; + mdiobus = <&mdio>; + }; + port@1 { + port_id = <2>; + phy_address = <30>; + phy_i2c_address =<30>; + phy-i2c-mode; + sfp_rx_los_pin = <&tlmm 43 0>; + sfp_mod_present_pin = <&tlmm 45 0>; + sfp_tx_dis_pin = <&extgpio 11 0>; + media-type = "sfp"; /* fiber mode */ + }; + }; + + }; + + }; + + dp2 { + device_type = "network"; + compatible = "qcom,nss-dp"; + qcom,id = <1>; + reg = <0x3a500000 0x4000>; + qcom,mactype = <1>; + local-mac-address = [000000000000]; + mdio-bus = <&mdio>; + qcom,phy-mdio-addr = <1>; + qcom,link-poll = <1>; + phy-mode = "sgmii"; + }; + + gmac2:dp1 { + device_type = "network"; + compatible = "qcom,nss-dp"; + qcom,id = <2>; + reg = <0x3a504000 0x4000>; + qcom,mactype = <1>; + local-mac-address = [000000000000]; + qcom,phy-mdio-addr = <30>; + qcom,link-poll = <1>; + phy-mode = "sgmii"; + }; + + x5-cpe { + compatible = "x55-poweron"; + pwykey = <&extgpio2 13 0>; + status = "ok"; + }; + + /* EDMA host driver configuration for the board */ + edma@3ab00000 { + qcom,txdesc-ring-start = <4>; /* Tx desc ring start ID */ + qcom,txdesc-rings = <12>; /* Total number of Tx desc rings to be provisioned */ + qcom,mht-txdesc-rings = <8>; /* Extra Tx desc rings to be provisioned for MHT SW ports */ + qcom,txcmpl-ring-start = <4>; /* Tx complete ring start ID */ + qcom,txcmpl-rings = <12>; /* Total number of Tx complete rings to be provisioned */ + qcom,mht-txcmpl-rings = <8>; /* Extra Tx complete rings to be provisioned for mht sw ports. */ + qcom,rxfill-ring-start = <4>; /* Rx fill ring start ID */ + qcom,rxfill-rings = <4>; /* Total number of Rx fill rings to be provisioned */ + qcom,rxdesc-ring-start = <12>; /* Rx desc ring start ID */ + qcom,rxdesc-rings = <4>; /* Total number of Rx desc rings to be provisioned */ + qcom,rx-page-mode = <0>; /* Rx fill ring page mode */ + qcom,tx-map-priority-level = <1>; /* Tx priority level per port */ + qcom,rx-map-priority-level = <1>; /* Rx priority level per core */ + qcom,ppeds-num = <2>; /* Number of PPEDS nodes */ + /* PPE-DS node format: */ + qcom,ppeds-map = <1 1 1 1 32 8>, /* PPEDS Node#0 ring and queue map */ + <2 2 2 2 40 8>; /* PPEDS Node#1 ring and queue map */ + qcom,txdesc-map = <8 9 10 11>, /* Port0 per-core Tx ring map */ + <12 13 14 15>, /* MHT-Port1 per-core Tx ring map */ + <4 5 6 7>, /* MHT-Port2 per-core Tx ring map/packets from vp*/ + <16 17 18 19>, /* MHT-Port3 per-core Tx ring map */ + <20 21 22 23>; /* MHT-Port4 per-core Tx ring map */ + qcom,txdesc-fc-grp-map = <1 2 3 4 5>; /* Per GMAC flow control group map */ + qcom,rxfill-map = <4 5 6 7>; /* Per-core Rx fill ring map */ + qcom,rxdesc-map = <12 13 14 15>; /* Per-core Rx desc ring map */ + qcom,rx-queue-start = <0>; /* Rx queue start */ + qcom,rx-ring-queue-map = <0 8 16 24>, /* Priority 0 queues per-core Rx ring map */ + <1 9 17 25>, /* Priority 1 queues per-core Rx ring map */ + <2 10 18 26>, /* Priority 2 queues per-core Rx ring map */ + <3 11 19 27>, /* Priority 3 queues per-core Rx ring map */ + <4 12 20 28>, /* Priority 4 queues per-core Rx ring map */ + <5 13 21 29>, /* Priority 5 queues per-core Rx ring map */ + <6 14 22 30>, /* Priority 6 queues per-core Rx ring map */ + <7 15 23 31>; /* Priority 7 queues per-core Rx ring map */ + interrupts = <0 163 4>, /* Tx complete ring id #4 IRQ info */ + <0 164 4>, /* Tx complete ring id #5 IRQ info */ + <0 165 4>, /* Tx complete ring id #6 IRQ info */ + <0 166 4>, /* Tx complete ring id #7 IRQ info */ + <0 167 4>, /* Tx complete ring id #8 IRQ info */ + <0 168 4>, /* Tx complete ring id #9 IRQ info */ + <0 169 4>, /* Tx complete ring id #10 IRQ info */ + <0 170 4>, /* Tx complete ring id #11 IRQ info */ + <0 171 4>, /* Tx complete ring id #12 IRQ info */ + <0 172 4>, /* Tx complete ring id #13 IRQ info */ + <0 173 4>, /* Tx complete ring id #14 IRQ info */ + <0 174 4>, /* Tx complete ring id #15 IRQ info */ + <0 139 4>, /* Rx desc ring id #12 IRQ info */ + <0 140 4>, /* Rx desc ring id #13 IRQ info */ + <0 141 4>, /* Rx desc ring id #14 IRQ info */ + <0 142 4>, /* Rx desc ring id #15 IRQ info */ + <0 191 4>, /* Misc error IRQ info */ + <0 160 4>, /* PPEDS Node #1(TxComp ring id #1) TxComplete IRQ info */ + <0 128 4>, /* PPEDS Node #1(Rx Desc ring id #1) Rx Desc IRQ info */ + <0 152 4>, /* PPEDS Node #1(RxFill Desc ring id #1) Rx Fill IRQ info */ + <0 161 4>, /* PPEDS Node #2(TxComp ring id #2) TxComplete IRQ info */ + <0 129 4>, /* PPEDS Node #2(Rx Desc ring id #2) Rx Desc IRQ info */ + <0 153 4>, /* PPEDS Node #2(RxFill Desc ring id #2) Rx Fill IRQ info */ + <0 175 4>, /* MHT port Tx complete ring id #16 IRQ info */ + <0 176 4>, /* MHT port Tx complete ring id #17 IRQ info */ + <0 177 4>, /* MHT port Tx complete ring id #18 IRQ info */ + <0 178 4>, /* MHT port Tx complete ring id #19 IRQ info */ + <0 179 4>, /* MHT port Tx complete ring id #20 IRQ info */ + <0 180 4>, /* MHT port Tx complete ring id #21 IRQ info */ + <0 181 4>, /* MHT port Tx complete ring id #22 IRQ info */ + <0 182 4>; /* MHT port Tx complete ring id #23 IRQ info */ + }; + + pwmleds { + compatible = "pwm-leds"; + + led_power_red:red { + label = "pwm:red"; + pwms = <&pwm 3 1250000>; + max-brightness = <160>; + linux,default-trigger = "none"; + default-state = "off"; + }; + + led_power_green:green { + label = "pwm:green"; + pwms = <&pwm 2 1250000>; + max-brightness = <160>; + linux,default-trigger = "none"; + default-state = "off"; + }; + + led_power_blue: blue { + label = "pwm:blue"; + pwms = <&pwm 1 1250000>; + max-brightness = <160>; + linux,default-trigger = "none"; + default-state = "off"; + }; + }; + + gpio_keys { + compatible = "gpio-keys"; + pinctrl-0 = <&button_pins>; + pinctrl-names = "default"; + status = "okay"; + + button@1 { + label = "rst"; + linux,code = ; + gpios = <&tlmm 24 GPIO_ACTIVE_LOW>; + linux,input-type = <1>; + debounce-interval = <60>; + }; + }; + + wsi: wsi { + id = <0>; + num_chip = <2>; + status = "okay"; + chip_info = <0 1 1>, + <1 1 0>; + }; + }; +}; + +&wifi0 { + led-gpio = <&tlmm 36 GPIO_ACTIVE_HIGH>; + qcom,rproc = <&q6_wcss_pd1>; + qcom,rproc_rpd = <&q6v5_wcss>; + qcom,multipd_arch; + qcom,userpd-subsys-name = "q6v5_wcss_userpd1"; + memory-region = <&q6_region>; + qcom,wsi = <&wsi>; + qcom,wsi_index = <0>; + qcom,board_id = <0x12>; + status = "okay"; +}; + +&qcn9224_pcie1 { + status = "okay"; +}; + +&blsp1_uart0 { + pinctrl-0 = <&serial_0_pins>; + pinctrl-names = "default"; + status = "okay"; +}; + +&blsp1_uart1 { + pinctrl-0 = <&serial_1_pins &pta_slic>; + pinctrl-names = "default"; + status = "okay"; +}; + +&blsp1_i2c1 { + status = "okay"; + clock-frequency = <400000>; + pinctrl-0 = <&i2c_1_pins>; + pinctrl-names = "default"; + extgpio:pca9555@20{ + compatible = "nxp,pca9555"; + reg = <0x20>; + pinctrl-names = "default"; + gpio-controller; + #gpio-cells = <2>; + status = "okay"; + + }; + extgpio2:pca9555@21{ + compatible = "nxp,pca9555"; + reg = <0x21>; + pinctrl-names = "default"; + gpio-controller; + #gpio-cells = <2>; + status = "okay"; + }; + lsm303_acc@19{ + compatible = "st,lsm303agr_acc"; + reg = <0x19>; + }; + lsm303_mag@1e{ + compatible = "st,lsm303agr_mag"; + reg = <0x1e>; + }; + ilps22qs@5c{ + compatible = "st,ilps22qs"; + reg = <0x5c>; + }; + temp-sense@70 { + compatible = "ti,tmp103"; + reg = <0x70>; + }; +}; + +&blsp1_spi0 { + pinctrl-0 = <&spi_0_data_clk_pins &spi_0_cs_pins>; + pinctrl-names = "default"; + status = "okay"; + + flash@0 { + compatible = "n25q128a11", "micron,n25q128a11", "jedec,spi-nor"; + reg = <0>; + #address-cells = <1>; + #size-cells = <1>; + spi-max-frequency = <50000000>; + }; +}; + +&blsp1_spi2 { + pinctrl-0 = <&spi_2_pins>; + pinctrl-names = "default"; + cs-select = <0>; + status = "disabled"; +}; + +&sdhc { + bus-width = <4>; + max-frequency = <192000000>; + mmc-ddr-1_8v; + mmc-hs200-1_8v; + non-removable; + pinctrl-0 = <&sdc_default_state>; + reset = <&tlmm 20 0>; + pinctrl-names = "default"; + status = "okay"; +}; + +&sleep_clk { + clock-frequency = <32000>; +}; + +&xo { + clock-frequency = <24000000>; +}; + +&qpic_bam { + status = "okay"; +}; + +&pcie1_phy_x2 { + status = "okay"; +}; + +&pcie0_phy { + status = "okay"; +}; + +&pcie0 { + pinctrl-0 = <&pcie0_default_state>; + pinctrl-names = "default"; + perst-gpios = <&tlmm 38 GPIO_ACTIVE_LOW>; + status = "okay"; +}; + +&pcie1 { + pinctrl-0 = <&pcie1_default_state>; + pinctrl-names = "default"; + perst-gpios = <&tlmm 47 GPIO_ACTIVE_LOW>; + status = "okay"; + + pcie1_rp { + reg = <0 0 0 0 0>; + + qcom,mhi@1 { + reg = <0 0 0 0 0>; + boot-args = <0x2 0x4 0x34 0x3 0x0 0x0 /* MX Rail, GPIO52, Drive strength 0x3 */ + 0x4 0x4 0x18 0x3 0x0 0x0 /* RFA1p2 Rail, GPIO24, Drive strength 0x3 */ + 0x0 0x4 0x0 0x0 0x0 0x0>; /* End of arguments */ + memory-region = <&qcn9224_pcie1>; + qcom,wsi = <&wsi>; + qcom,wsi_index = <1>; + qcom,board_id = <0x1015>; + }; + }; +}; + +/* PINCTRL */ + +&tlmm { + i2c_1_pins: i2c-1-state { + pins = "gpio29", "gpio30"; + function = "blsp1_i2c0"; + drive-strength = <8>; + bias-pull-up; + }; + + spi_2_pins: spi-2-pins { + pins = "gpio33", "gpio34", "gpio35", "gpio36"; + function = "blsp2_spi0"; + drive-strength = <8>; + bias-pull-down; + }; + + pta_slic: pta_slic { + pta1_0 { + pins = "gpio49"; + function = "pta1_0"; + drive-strength = <8>; + bias-disable; + }; + pta1_1 { + pins = "gpio50"; + function = "pta1_1"; + drive-strength = <8>; + bias-disable; + }; + pta1_2 { + pins = "gpio51"; + function = "pta1_2"; + drive-strength = <8>; + bias-disable; + }; + }; + + spi_0_data_clk_pins: spi-0-data-clk-state { + pins = "gpio14", "gpio15", "gpio16"; + function = "blsp0_spi"; + drive-strength = <2>; + bias-pull-down; + }; + + spi_0_cs_pins: spi-0-cs-state { + pins = "gpio17"; + function = "blsp0_spi"; + drive-strength = <2>; + bias-pull-up; + }; + + serial_1_pins: serial1-pinmux { + pins = "gpio33", "gpio34", "gpio35", "gpio36"; + function = "blsp1_uart2"; + drive-strength = <8>; + bias-pull-up; + }; + + button_pins: button-state { + pins = "gpio24"; + function = "gpio"; + drive-strength = <8>; + bias-pull-up; + }; + + pwm_pins: pwm-state { + + mux_1 { + pins = "gpio25"; + function = "pwm2"; + drive-strength = <8>; + }; + mux_2 { + pins = "gpio26"; + function = "pwm2"; + drive-strength = <8>; + }; + mux_3 { + pins = "gpio31"; + function = "pwm1"; + drive-strength = <8>; + }; + }; + + sdc_default_state: sdc-default-state { + clk-pins { + pins = "gpio13"; + function = "sdc_clk"; + drive-strength = <8>; + bias-disable; + }; + + cmd-pins { + pins = "gpio12"; + function = "sdc_cmd"; + drive-strength = <8>; + bias-pull-up; + }; + + data-pins { + pins = "gpio8", "gpio9", "gpio10", "gpio11"; + function = "sdc_data"; + drive-strength = <8>; + bias-pull-up; + }; + }; + + pcie0_default_state: pcie0-default-state { + pins = "gpio38"; + function = "gpio"; + drive-strength = <8>; + bias-pull-up; + output-low; + }; + + pcie1_default_state: pcie1-default-state { + pins = "gpio47"; + function = "gpio"; + drive-strength = <8>; + bias-pull-up; + output-low; + }; +}; + +&license_manager { + status = "okay"; +}; + +&usb3 { + qcom,select-utmi-as-pipe-clk; + status = "okay"; + dwc3@8a00000 { + /delete-property/ #phy-cells; + /delete-property/ phys; + /delete-property/ phy-names; + }; +}; + +&pwm { + pinctrl-0 = <&pwm_pins>; + pinctrl-names = "default"; + dft-pwm-status = <0>, <0>, <1>, <0>; + poe_type_pin = <&extgpio 0 0 &extgpio 1 0 &extgpio 2 0>; + status = "okay"; +}; + +&hs_m31phy_0 { + status = "okay"; +}; + diff --git a/feeds/qca-wifi-7/ipq53xx/files-6.1/drivers/iio/pressure/st_ilps22qs.h b/feeds/qca-wifi-7/ipq53xx/files-6.1/drivers/iio/pressure/st_ilps22qs.h new file mode 100644 index 000000000..98514b76c --- /dev/null +++ b/feeds/qca-wifi-7/ipq53xx/files-6.1/drivers/iio/pressure/st_ilps22qs.h @@ -0,0 +1,157 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * STMicroelectronics ilps22qs driver + * + * Copyright 2023 STMicroelectronics Inc. + * + * MEMS Software Solutions Team + */ + +#ifndef __ST_ILPS22QS_H +#define __ST_ILPS22QS_H + +#include +#include +#include +#include +#include +#include +#include +#include + +#define ST_ILPS22QS_DEV_NAME "ilps22qs" +#define ST_ILPS28QSW_DEV_NAME "ilps28qsw" + +#define ST_ILPS22QS_WHO_AM_I_ADDR 0x0f +#define ST_ILPS22QS_WHOAMI_VAL 0xb4 + +#define ST_ILPS22QS_CTRL1_ADDR 0x10 +#define ST_ILPS22QS_ODR_MASK GENMASK(6, 3) + +#define ST_ILPS22QS_CTRL2_ADDR 0x11 +#define ST_ILPS22QS_SOFT_RESET_MASK BIT(2) +#define ST_ILPS22QS_BDU_MASK BIT(3) + +#define ST_ILPS22QS_CTRL3_ADDR 0x12 +#define ST_ILPS22QS_AH_QVAR_EN_MASK BIT(7) +#define ST_ILPS22QS_AH_QVAR_P_AUTO_EN_MASK BIT(5) + +#define ST_ILPS22QS_PRESS_OUT_XL_ADDR 0x28 +#define ST_ILPS22QS_TEMP_OUT_L_ADDR 0x2b + +#define ST_ILPS22QS_PRESS_FS_AVL_GAIN (1000000000UL / 4096UL) +#define ST_ILPS22QS_TEMP_FS_AVL_GAIN 100 +#define ST_ILPS22QS_QVAR_FS_AVL_GAIN 438000 + +#define ST_ILPS22QS_SHIFT_VAL(val, mask) (((val) << __ffs(mask)) & (mask)) + +#define ST_ILPS22QS_ODR_LIST_NUM 8 + +enum st_ilps22qs_sensor_id { + ST_ILPS22QS_PRESS = 0, + ST_ILPS22QS_TEMP, + ST_ILPS22QS_QVAR, + ST_ILPS22QS_SENSORS_NUM, +}; + +struct st_ilps22qs_odr_t { + u8 hz; + u8 val; +}; + +struct st_ilps22qs_reg { + u8 addr; + u8 mask; +}; + +struct st_ilps22qs_odr_table_t { + u8 size; + struct st_ilps22qs_reg reg; + struct st_ilps22qs_odr_t odr_avl[ST_ILPS22QS_ODR_LIST_NUM]; +}; + +struct st_ilps22qs_hw { + struct iio_dev *iio_devs[ST_ILPS22QS_SENSORS_NUM]; + struct workqueue_struct *workqueue; + struct regulator *vddio_supply; + struct regulator *vdd_supply; + struct regmap *regmap; + struct device *dev; + struct mutex lock; + bool interleave; + u8 enable_mask; + u8 odr; +}; + +struct st_ilps22qs_sensor { + enum st_ilps22qs_sensor_id id; + struct work_struct iio_work; + struct st_ilps22qs_hw *hw; + struct hrtimer hr_timer; + ktime_t ktime; + int64_t timestamp; + char name[32]; + u32 gain; + u8 odr; +}; + +extern const struct dev_pm_ops st_ilps22qs_pm_ops; + +static inline int st_ilps22qs_update_locked(struct st_ilps22qs_hw *hw, + unsigned int addr, + unsigned int mask, + unsigned int data) +{ + unsigned int val = ST_ILPS22QS_SHIFT_VAL(data, mask); + int err; + + mutex_lock(&hw->lock); + err = regmap_update_bits(hw->regmap, addr, mask, val); + mutex_unlock(&hw->lock); + + return err; +} + +static inline int st_ilps22qs_read_locked(struct st_ilps22qs_hw *hw, + unsigned int addr, void *val, + unsigned int len) +{ + int err; + + mutex_lock(&hw->lock); + err = regmap_bulk_read(hw->regmap, addr, val, len); + mutex_unlock(&hw->lock); + + return err; +} + +static inline void st_ilps22qs_flush_works(struct st_ilps22qs_hw *hw) +{ + flush_workqueue(hw->workqueue); +} + +static inline int st_ilps22qs_destroy_workqueue(struct st_ilps22qs_hw *hw) +{ + if (hw->workqueue) + destroy_workqueue(hw->workqueue); + + return 0; +} + +static inline int st_ilps22qs_allocate_workqueue(struct st_ilps22qs_hw *hw) +{ + if (!hw->workqueue) + hw->workqueue = create_workqueue(ST_ILPS22QS_DEV_NAME); + + return !hw->workqueue ? -ENOMEM : 0; +} + +static inline s64 st_ilps22qs_get_time_ns(struct st_ilps22qs_hw *hw) +{ + return iio_get_time_ns(hw->iio_devs[ST_ILPS22QS_PRESS]); +} + +int st_ilps22qs_probe(struct device *dev, struct regmap *regmap); +int st_ilps22qs_remove(struct device *dev); + +#endif /* __ST_ILPS22QS_H */ diff --git a/feeds/qca-wifi-7/ipq53xx/files-6.1/drivers/iio/pressure/st_ilps22qs_core.c b/feeds/qca-wifi-7/ipq53xx/files-6.1/drivers/iio/pressure/st_ilps22qs_core.c new file mode 100644 index 000000000..75c527a9c --- /dev/null +++ b/feeds/qca-wifi-7/ipq53xx/files-6.1/drivers/iio/pressure/st_ilps22qs_core.c @@ -0,0 +1,927 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * STMicroelectronics ilps22qs driver + * + * Copyright 2023 STMicroelectronics Inc. + * + * MEMS Software Solutions Team + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if KERNEL_VERSION(6, 11, 0) < LINUX_VERSION_CODE +#include +#else /* LINUX_VERSION_CODE */ +#include +#endif /* LINUX_VERSION_CODE */ + +#include "st_ilps22qs.h" + +static const struct st_ilps22qs_odr_table_t st_ilps22qs_odr_table = { + .size = ST_ILPS22QS_ODR_LIST_NUM, + .reg = { + .addr = ST_ILPS22QS_CTRL1_ADDR, + .mask = ST_ILPS22QS_ODR_MASK, + }, + .odr_avl[0] = { 1, 0x01 }, + .odr_avl[1] = { 4, 0x02 }, + .odr_avl[2] = { 10, 0x03 }, + .odr_avl[3] = { 25, 0x04 }, + .odr_avl[4] = { 50, 0x05 }, + .odr_avl[5] = { 75, 0x06 }, + .odr_avl[6] = { 100, 0x07 }, + .odr_avl[7] = { 200, 0x08 }, +}; + +static const struct iio_chan_spec st_ilps22qs_press_channels[] = { + { + .type = IIO_PRESSURE, + .address = ST_ILPS22QS_PRESS_OUT_XL_ADDR, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SCALE), + .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ), + .channel2 = IIO_NO_MOD, + .scan_index = 0, + .scan_type = { + .sign = 'u', + .realbits = 24, + .storagebits = 32, + .endianness = IIO_LE, + }, + }, + IIO_CHAN_SOFT_TIMESTAMP(1) +}; + +static const struct iio_chan_spec st_ilps22qs_temp_channels[] = { + { + .type = IIO_TEMP, + .address = ST_ILPS22QS_TEMP_OUT_L_ADDR, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SCALE), + .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ), + .channel2 = IIO_NO_MOD, + .scan_index = 0, + .scan_type = { + .sign = 's', + .realbits = 16, + .storagebits = 16, + .endianness = IIO_LE, + }, + }, + IIO_CHAN_SOFT_TIMESTAMP(1) +}; + +static const struct iio_chan_spec st_ilps22qs_qvar_channels[] = { + { + .type = IIO_ALTVOLTAGE, + .address = ST_ILPS22QS_PRESS_OUT_XL_ADDR, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SCALE), + .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ), + .channel2 = IIO_NO_MOD, + .scan_index = 0, + .scan_type = { + .sign = 's', + .realbits = 24, + .storagebits = 32, + .endianness = IIO_LE, + } + }, + IIO_CHAN_SOFT_TIMESTAMP(1), +}; + +static enum hrtimer_restart +st_ilps22qs_poll_function_read(struct hrtimer *timer) +{ + struct st_ilps22qs_sensor *sensor; + + sensor = container_of((struct hrtimer *)timer, + struct st_ilps22qs_sensor, hr_timer); + + sensor->timestamp = st_ilps22qs_get_time_ns(sensor->hw); + queue_work(sensor->hw->workqueue, &sensor->iio_work); + + return HRTIMER_NORESTART; +} + +static void st_ilps22qs_report_temp(struct st_ilps22qs_sensor *sensor, + u8 *tmp, int64_t timestamp) +{ + struct iio_dev *iio_dev = sensor->hw->iio_devs[sensor->id]; + u8 iio_buf[ALIGN(2, sizeof(s64)) + sizeof(s64)]; + + memcpy(iio_buf, tmp, 2); + iio_push_to_buffers_with_timestamp(iio_dev, iio_buf, timestamp); +} + +static void st_silps22qs_report_press_qvar(struct st_ilps22qs_sensor *sensor, + u8 *tmp, int64_t timestamp) +{ + u8 iio_buf[ALIGN(3, sizeof(s64)) + sizeof(s64)]; + struct st_ilps22qs_hw *hw = sensor->hw; + struct iio_dev *iio_dev; + + mutex_lock(&hw->lock); + if (hw->interleave) { + if (tmp[0] & 0x01) + iio_dev = sensor->hw->iio_devs[ST_ILPS22QS_QVAR]; + else + iio_dev = sensor->hw->iio_devs[ST_ILPS22QS_PRESS]; + } else { + iio_dev = sensor->hw->iio_devs[sensor->id]; + } + mutex_unlock(&hw->lock); + + memcpy(iio_buf, tmp, 3); + iio_push_to_buffers_with_timestamp(iio_dev, iio_buf, timestamp); +} + +static void st_ilps22qs_poll_function_work(struct work_struct *iio_work) +{ + struct st_ilps22qs_sensor *sensor; + struct st_ilps22qs_hw *hw; + ktime_t tmpkt, ktdelta; + int len; + int err; + int id; + + sensor = container_of((struct work_struct *)iio_work, + struct st_ilps22qs_sensor, iio_work); + hw = sensor->hw; + id = sensor->id; + + /* adjust delta time */ + ktdelta = ktime_set(0, + (st_ilps22qs_get_time_ns(hw) - sensor->timestamp)); + + /* avoid negative value in case of high odr */ + mutex_lock(&hw->lock); + if (ktime_after(sensor->ktime, ktdelta)) + tmpkt = ktime_sub(sensor->ktime, ktdelta); + else + tmpkt = sensor->ktime; + + hrtimer_start(&sensor->hr_timer, tmpkt, HRTIMER_MODE_REL); + mutex_unlock(&sensor->hw->lock); + + len = hw->iio_devs[id]->channels->scan_type.realbits >> 3; + + switch (id) { + case ST_ILPS22QS_PRESS: + case ST_ILPS22QS_QVAR: { + u8 data[3]; + + err = st_ilps22qs_read_locked(hw, + hw->iio_devs[id]->channels->address, + data, len); + if (err < 0) + return; + + st_silps22qs_report_press_qvar(sensor, data, sensor->timestamp); + } + break; + case ST_ILPS22QS_TEMP: { + u8 data[2]; + + err = st_ilps22qs_read_locked(hw, + hw->iio_devs[id]->channels->address, + data, len); + if (err < 0) + return; + + st_ilps22qs_report_temp(sensor, data, sensor->timestamp); + } + break; + default: + break; + } +} + +static int st_ilps22qs_check_whoami(struct st_ilps22qs_hw *hw) +{ + int data; + int err; + + err = regmap_read(hw->regmap, ST_ILPS22QS_WHO_AM_I_ADDR, &data); + if (err < 0) { + dev_err(hw->dev, "failed to read whoami register\n"); + + return err; + } + + if (data != ST_ILPS22QS_WHOAMI_VAL) { + dev_err(hw->dev, "unsupported whoami [%02x]\n", data); + + return -ENODEV; + } + + return 0; +} + +static __maybe_unused int st_ilps22qs_reg_access(struct iio_dev *iio_dev, + unsigned int reg, + unsigned int writeval, + unsigned int *readval) +{ + struct st_ilps22qs_sensor *sensor = iio_priv(iio_dev); + int ret; + + ret = iio_device_claim_direct_mode(iio_dev); + if (ret) + return ret; + + if (readval == NULL) + ret = regmap_write(sensor->hw->regmap, reg, writeval); + else + ret = regmap_read(sensor->hw->regmap, reg, readval); + + iio_device_release_direct_mode(iio_dev); + + return (ret < 0) ? ret : 0; +} + +static int st_ilps22qs_get_odr(struct st_ilps22qs_sensor *sensor, u8 odr) +{ + int i; + + for (i = 0; i < st_ilps22qs_odr_table.size; i++) { + if (st_ilps22qs_odr_table.odr_avl[i].hz >= odr) + break; + } + + return i == st_ilps22qs_odr_table.size ? -EINVAL : i; +} + +static int st_ilps22qs_set_odr(struct st_ilps22qs_sensor *sensor, u8 odr) +{ + struct st_ilps22qs_hw *hw = sensor->hw; + u8 max_odr = odr; + int i; + + for (i = 0; i < ST_ILPS22QS_SENSORS_NUM; i++) { + if ((hw->enable_mask & BIT(i)) && (sensor->id != i)) { + struct st_ilps22qs_sensor *temp; + + temp = iio_priv(hw->iio_devs[i]); + max_odr = max_t(u32, max_odr, temp->odr); + } + } + + if (max_odr != hw->odr) { + int err, ret; + + ret = st_ilps22qs_get_odr(sensor, max_odr); + if (ret < 0) + return ret; + + err = st_ilps22qs_update_locked(hw, + st_ilps22qs_odr_table.reg.addr, + st_ilps22qs_odr_table.reg.mask, + st_ilps22qs_odr_table.odr_avl[ret].val); + if (err < 0) + return err; + + hw->odr = max_odr; + } + + return 0; +} + +/* need hw->lock */ +static int st_ilps22qs_set_interleave(struct st_ilps22qs_sensor *sensor, + bool enable) +{ + struct st_ilps22qs_hw *hw = sensor->hw; + int otherid = sensor->id == ST_ILPS22QS_PRESS ? ST_ILPS22QS_QVAR : + ST_ILPS22QS_PRESS; + int interleave; + int err = 0; + + /* both press / qvar enabling ? */ + mutex_lock(&hw->lock); + interleave = (!!(hw->enable_mask & BIT(otherid))) && enable; + if (interleave) { + unsigned int ctrl1; + + err = regmap_bulk_read(hw->regmap, + ST_ILPS22QS_CTRL1_ADDR, + &ctrl1, 1); + if (err < 0) + goto unlock; + + err = regmap_update_bits(hw->regmap, + ST_ILPS22QS_CTRL1_ADDR, + ST_ILPS22QS_ODR_MASK, 0); + if (err < 0) + goto unlock; + + err = regmap_update_bits(hw->regmap, + ST_ILPS22QS_CTRL3_ADDR, + ST_ILPS22QS_AH_QVAR_EN_MASK, 0); + if (err < 0) + goto unlock; + + err = regmap_update_bits(hw->regmap, ST_ILPS22QS_CTRL3_ADDR, + ST_ILPS22QS_AH_QVAR_P_AUTO_EN_MASK, + ST_ILPS22QS_SHIFT_VAL(interleave, + ST_ILPS22QS_AH_QVAR_P_AUTO_EN_MASK)); + if (err < 0) + goto unlock; + + err = regmap_update_bits(hw->regmap, ST_ILPS22QS_CTRL1_ADDR, + ST_ILPS22QS_ODR_MASK, ctrl1); + if (err < 0) + goto unlock; + } else if (hw->interleave) { + err = regmap_update_bits(hw->regmap, ST_ILPS22QS_CTRL3_ADDR, + ST_ILPS22QS_AH_QVAR_P_AUTO_EN_MASK, 0); + if (err < 0) + goto unlock; + } + + hw->interleave = interleave; + +unlock: + mutex_unlock(&hw->lock); + + return err; +} + +static int st_ilps22qs_hw_enable(struct st_ilps22qs_sensor *sensor, bool enable) +{ + int ret = 0; + + switch (sensor->id) { + case ST_ILPS22QS_QVAR: + case ST_ILPS22QS_PRESS: + ret = st_ilps22qs_set_interleave(sensor, enable); + break; + default: + return 0; + } + + return ret; +} + +static int st_ilps22qs_set_enable(struct st_ilps22qs_sensor *sensor, + bool enable) +{ + struct st_ilps22qs_hw *hw = sensor->hw; + u8 odr = enable ? sensor->odr : 0; + int err; + + err = st_ilps22qs_hw_enable(sensor, enable); + if (err < 0) + return err; + + err = st_ilps22qs_set_odr(sensor, odr); + if (err < 0) + return err; + + mutex_lock(&hw->lock); + if (enable) { + ktime_t ktime = ktime_set(0, 1000000000 / sensor->odr); + + hrtimer_start(&sensor->hr_timer, ktime, HRTIMER_MODE_REL); + sensor->ktime = ktime; + hw->enable_mask |= BIT(sensor->id); + } else { + cancel_work_sync(&sensor->iio_work); + hrtimer_cancel(&sensor->hr_timer); + hw->enable_mask &= ~BIT(sensor->id); + } + + if (!hw->interleave) { + err = regmap_update_bits(hw->regmap, + ST_ILPS22QS_CTRL3_ADDR, + ST_ILPS22QS_AH_QVAR_EN_MASK, + ST_ILPS22QS_SHIFT_VAL(!!(hw->enable_mask & BIT(ST_ILPS22QS_QVAR)), + ST_ILPS22QS_AH_QVAR_EN_MASK)); + } + mutex_unlock(&hw->lock); + + return err; +} + +static int st_ilps22qs_init_sensors(struct st_ilps22qs_hw *hw) +{ + int err; + + /* soft reset the device on power on */ + err = st_ilps22qs_update_locked(hw, ST_ILPS22QS_CTRL2_ADDR, + ST_ILPS22QS_SOFT_RESET_MASK, 1); + if (err < 0) + return err; + + usleep_range(50, 60); + + /* interleave disabled by default */ + hw->interleave = false; + + /* enable BDU */ + return st_ilps22qs_update_locked(hw, ST_ILPS22QS_CTRL1_ADDR, + ST_ILPS22QS_BDU_MASK, 1); +} + +static ssize_t +st_ilps22qs_get_sampling_frequency_avail(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + int i, len = 0; + + for (i = 0; i < st_ilps22qs_odr_table.size; i++) { + len += scnprintf(buf + len, PAGE_SIZE - len, "%d ", + st_ilps22qs_odr_table.odr_avl[i].hz); + } + + buf[len - 1] = '\n'; + + return len; +} + +static int st_ilps22qs_read_raw(struct iio_dev *iio_dev, + struct iio_chan_spec const *ch, + int *val, int *val2, long mask) +{ + struct st_ilps22qs_sensor *sensor = iio_priv(iio_dev); + struct st_ilps22qs_hw *hw = sensor->hw; + int ret; + + switch (mask) { + case IIO_CHAN_INFO_RAW: { + u8 data[4] = {}; + int delay; + + ret = iio_device_claim_direct_mode(iio_dev); + if (ret) + return ret; + + ret = st_ilps22qs_set_enable(sensor, true); + if (ret < 0) + goto read_error; + + delay = 1000000 / sensor->odr; + usleep_range(delay, 2 * delay); + + ret = regmap_bulk_read(hw->regmap, ch->address, data, + ch->scan_type.realbits >> 3); + if (ret < 0) + goto read_error; + + switch (sensor->id) { + case ST_ILPS22QS_PRESS: + *val = (s32)get_unaligned_le32(data); + break; + case ST_ILPS22QS_TEMP: + *val = (s16)get_unaligned_le16(data); + break; + case ST_ILPS22QS_QVAR: + *val = (s32)get_unaligned_le32(data); + break; + default: + ret = -ENODEV; + goto read_error; + } + +read_error: + st_ilps22qs_set_enable(sensor, false); + iio_device_release_direct_mode(iio_dev); + + if (ret < 0) + return ret; + + ret = IIO_VAL_INT; + break; + } + case IIO_CHAN_INFO_SCALE: + switch (ch->type) { + case IIO_TEMP: + *val = 1000; + *val2 = sensor->gain; + ret = IIO_VAL_FRACTIONAL; + break; + case IIO_PRESSURE: + *val = 0; + *val2 = sensor->gain; + ret = IIO_VAL_INT_PLUS_NANO; + break; + case IIO_ALTVOLTAGE: + *val = 0; + *val2 = sensor->gain; + ret = IIO_VAL_INT_PLUS_NANO; + break; + default: + ret = -ENODEV; + break; + } + break; + case IIO_CHAN_INFO_SAMP_FREQ: + *val = sensor->odr; + ret = IIO_VAL_INT; + break; + default: + ret = -EINVAL; + break; + } + + return ret; +} + +static int st_ilps22qs_write_raw(struct iio_dev *iio_dev, + struct iio_chan_spec const *ch, + int val, int val2, long mask) +{ + struct st_ilps22qs_sensor *sensor = iio_priv(iio_dev); + int ret; + + ret = iio_device_claim_direct_mode(iio_dev); + if (ret) + return ret; + + switch (mask) { + case IIO_CHAN_INFO_SAMP_FREQ: + ret = st_ilps22qs_get_odr(sensor, val); + if (ret < 0) + goto exit_fail; + + sensor->odr = st_ilps22qs_odr_table.odr_avl[ret].hz; + break; + default: + ret = -EINVAL; + break; + } + +exit_fail: + iio_device_release_direct_mode(iio_dev); + + return ret < 0 ? ret : 0; +} + +static IIO_DEV_ATTR_SAMP_FREQ_AVAIL(st_ilps22qs_get_sampling_frequency_avail); + +static struct attribute *st_ilps22qs_press_attributes[] = { + &iio_dev_attr_sampling_frequency_available.dev_attr.attr, + NULL, +}; + +static const struct attribute_group st_ilps22qs_press_attribute_group = { + .attrs = st_ilps22qs_press_attributes, +}; + +static const struct iio_info st_ilps22qs_press_info = { + .attrs = &st_ilps22qs_press_attribute_group, + .read_raw = st_ilps22qs_read_raw, + .write_raw = st_ilps22qs_write_raw, + .debugfs_reg_access = st_ilps22qs_reg_access, +}; + +static struct attribute *st_ilps22qs_temp_attributes[] = { + &iio_dev_attr_sampling_frequency_available.dev_attr.attr, + NULL, +}; + +static const struct attribute_group st_ilps22qs_temp_attribute_group = { + .attrs = st_ilps22qs_temp_attributes, +}; + +static const struct iio_info st_ilps22qs_temp_info = { + .attrs = &st_ilps22qs_temp_attribute_group, + .read_raw = st_ilps22qs_read_raw, + .write_raw = st_ilps22qs_write_raw, + .debugfs_reg_access = st_ilps22qs_reg_access, +}; + +static struct attribute *st_ilps22qs_qvar_attributes[] = { + &iio_dev_attr_sampling_frequency_available.dev_attr.attr, + NULL, +}; + +static const struct attribute_group st_ilps22qs_qvar_attribute_group = { + .attrs = st_ilps22qs_qvar_attributes, +}; + +static const struct iio_info st_ilps22qs_qvar_info = { + .attrs = &st_ilps22qs_qvar_attribute_group, + .read_raw = st_ilps22qs_read_raw, + .write_raw = st_ilps22qs_write_raw, + .debugfs_reg_access = st_ilps22qs_reg_access, +}; + +static int st_ilps22qs_preenable(struct iio_dev *iio_dev) +{ + struct st_ilps22qs_sensor *sensor = iio_priv(iio_dev); + + return st_ilps22qs_set_enable(sensor, true); +} + +static int st_ilps22qs_postdisable(struct iio_dev *iio_dev) +{ + struct st_ilps22qs_sensor *sensor = iio_priv(iio_dev); + + return st_ilps22qs_set_enable(sensor, false); +} + +static const struct iio_buffer_setup_ops st_ilps22qs_fifo_ops = { + .preenable = st_ilps22qs_preenable, + .postdisable = st_ilps22qs_postdisable, +}; + +static void st_ilps22qs_disable_regulator_action(void *_data) +{ + struct st_ilps22qs_hw *hw = _data; + + regulator_disable(hw->vddio_supply); + regulator_disable(hw->vdd_supply); +} + +static int st_ilps22qs_power_enable(struct st_ilps22qs_hw *hw) +{ + int err; + + hw->vdd_supply = devm_regulator_get(hw->dev, "vdd"); + if (IS_ERR(hw->vdd_supply)) { + if (PTR_ERR(hw->vdd_supply) != -EPROBE_DEFER) + dev_err(hw->dev, "Failed to get vdd regulator %d\n", + (int)PTR_ERR(hw->vdd_supply)); + + return PTR_ERR(hw->vdd_supply); + } + + hw->vddio_supply = devm_regulator_get(hw->dev, "vddio"); + if (IS_ERR(hw->vddio_supply)) { + if (PTR_ERR(hw->vddio_supply) != -EPROBE_DEFER) + dev_err(hw->dev, "Failed to get vddio regulator %d\n", + (int)PTR_ERR(hw->vddio_supply)); + + return PTR_ERR(hw->vddio_supply); + } + + err = regulator_enable(hw->vdd_supply); + if (err) { + dev_err(hw->dev, "Failed to enable vdd regulator: %d\n", err); + + return err; + } + + err = regulator_enable(hw->vddio_supply); + if (err) { + regulator_disable(hw->vdd_supply); + + return err; + } + + err = devm_add_action_or_reset(hw->dev, + st_ilps22qs_disable_regulator_action, + hw); + if (err) { + dev_err(hw->dev, + "Failed to setup regulator cleanup action %d\n", err); + + return err; + } + + /* + * after the device is powered up, the ILPS22QS performs a 10 ms + * boot procedure to load the trimming parameters + */ + usleep_range(10000, 11000); + + return 0; +} + +static struct iio_dev *st_ilps22qs_alloc_iiodev(struct st_ilps22qs_hw *hw, + enum st_ilps22qs_sensor_id id) +{ + struct st_ilps22qs_sensor *sensor; + struct iio_dev *iio_dev; + + iio_dev = devm_iio_device_alloc(hw->dev, sizeof(*sensor)); + if (!iio_dev) + return NULL; + + iio_dev->modes = INDIO_DIRECT_MODE; + iio_dev->dev.parent = hw->dev; + + sensor = iio_priv(iio_dev); + sensor->hw = hw; + sensor->id = id; + sensor->odr = st_ilps22qs_odr_table.odr_avl[0].hz; + + switch (id) { + case ST_ILPS22QS_PRESS: + sensor->gain = ST_ILPS22QS_PRESS_FS_AVL_GAIN; + scnprintf(sensor->name, sizeof(sensor->name), + ST_ILPS22QS_DEV_NAME "_press"); + iio_dev->channels = st_ilps22qs_press_channels; + iio_dev->num_channels = ARRAY_SIZE(st_ilps22qs_press_channels); + iio_dev->info = &st_ilps22qs_press_info; + break; + case ST_ILPS22QS_TEMP: + sensor->gain = ST_ILPS22QS_TEMP_FS_AVL_GAIN; + scnprintf(sensor->name, sizeof(sensor->name), + ST_ILPS22QS_DEV_NAME "_temp"); + iio_dev->channels = st_ilps22qs_temp_channels; + iio_dev->num_channels = ARRAY_SIZE(st_ilps22qs_temp_channels); + iio_dev->info = &st_ilps22qs_temp_info; + break; + case ST_ILPS22QS_QVAR: + sensor->gain = ST_ILPS22QS_QVAR_FS_AVL_GAIN; + scnprintf(sensor->name, sizeof(sensor->name), + ST_ILPS22QS_DEV_NAME "_qvar"); + iio_dev->channels = st_ilps22qs_qvar_channels; + iio_dev->num_channels = ARRAY_SIZE(st_ilps22qs_qvar_channels); + iio_dev->info = &st_ilps22qs_qvar_info; + break; + default: + return NULL; + } + + iio_dev->name = sensor->name; + + /* configure sensor hrtimer */ + hrtimer_init(&sensor->hr_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); + sensor->hr_timer.function = &st_ilps22qs_poll_function_read; + INIT_WORK(&sensor->iio_work, st_ilps22qs_poll_function_work); + + return iio_dev; +} + +int st_ilps22qs_probe(struct device *dev, struct regmap *regmap) +{ + struct st_ilps22qs_hw *hw; + int err, i; + + hw = devm_kzalloc(dev, sizeof(*hw), GFP_KERNEL); + if (!hw) + return -ENOMEM; + + mutex_init(&hw->lock); + + dev_set_drvdata(dev, (void *)hw); + hw->dev = dev; + hw->regmap = regmap; + + err = st_ilps22qs_power_enable(hw); + if (err) + return err; + + err = st_ilps22qs_check_whoami(hw); + if (err < 0) + return err; + + err = st_ilps22qs_init_sensors(hw); + if (err < 0) + return err; + + for (i = 0; i < ST_ILPS22QS_SENSORS_NUM; i++) { + +#if KERNEL_VERSION(5, 13, 0) > LINUX_VERSION_CODE + struct iio_buffer *buffer; +#endif /* LINUX_VERSION_CODE */ + + hw->iio_devs[i] = st_ilps22qs_alloc_iiodev(hw, i); + if (!hw->iio_devs[i]) + return -ENOMEM; + +#if KERNEL_VERSION(5, 19, 0) <= LINUX_VERSION_CODE + err = devm_iio_kfifo_buffer_setup(hw->dev, + hw->iio_devs[i], + &st_ilps22qs_fifo_ops); + if (err) + return err; +#elif KERNEL_VERSION(5, 13, 0) <= LINUX_VERSION_CODE + err = devm_iio_kfifo_buffer_setup(hw->dev, hw->iio_devs[i], + INDIO_BUFFER_SOFTWARE, + &st_ilps22qs_fifo_ops); + if (err) + return err; +#else /* LINUX_VERSION_CODE */ + buffer = devm_iio_kfifo_allocate(hw->dev); + if (!buffer) + return -ENOMEM; + + iio_device_attach_buffer(hw->iio_devs[i], buffer); + hw->iio_devs[i]->modes |= INDIO_BUFFER_SOFTWARE; + hw->iio_devs[i]->setup_ops = &st_ilps22qs_fifo_ops; +#endif /* LINUX_VERSION_CODE */ + + err = devm_iio_device_register(hw->dev, hw->iio_devs[i]); + if (err) + return err; + } + + err = st_ilps22qs_allocate_workqueue(hw); + if (err) + return err; + + dev_info(dev, "device probed\n"); + + return 0; +} +EXPORT_SYMBOL(st_ilps22qs_probe); + +int st_ilps22qs_remove(struct device *dev) +{ + struct st_ilps22qs_hw *hw = dev_get_drvdata(dev); + struct st_ilps22qs_sensor *sensor; + int i; + + for (i = 0; i < ST_ILPS22QS_SENSORS_NUM; i++) { + int err; + + if (!hw->iio_devs[i]) + continue; + + sensor = iio_priv(hw->iio_devs[i]); + if (!(hw->enable_mask & BIT(sensor->id))) + continue; + + err = st_ilps22qs_set_enable(sensor, false); + if (err < 0) + return err; + } + + st_ilps22qs_flush_works(hw); + st_ilps22qs_destroy_workqueue(hw); + + return 0; +} +EXPORT_SYMBOL(st_ilps22qs_remove); + +static int __maybe_unused st_ilps22qs_suspend(struct device *dev) +{ + struct st_ilps22qs_hw *hw = dev_get_drvdata(dev); + struct st_ilps22qs_sensor *sensor; + int i; + + for (i = 0; i < ST_ILPS22QS_SENSORS_NUM; i++) { + int err; + + if (!hw->iio_devs[i]) + continue; + + sensor = iio_priv(hw->iio_devs[i]); + if (!(hw->enable_mask & BIT(sensor->id))) + continue; + + err = st_ilps22qs_set_odr(sensor, 0); + if (err < 0) + return err; + + cancel_work_sync(&sensor->iio_work); + hrtimer_cancel(&sensor->hr_timer); + } + + dev_info(dev, "Suspending device\n"); + + return 0; +} + +static int __maybe_unused st_ilps22qs_resume(struct device *dev) +{ + struct st_ilps22qs_hw *hw = dev_get_drvdata(dev); + struct st_ilps22qs_sensor *sensor; + int i; + + dev_info(dev, "Resuming device\n"); + + for (i = 0; i < ST_ILPS22QS_SENSORS_NUM; i++) { + int err; + + if (!hw->iio_devs[i]) + continue; + + sensor = iio_priv(hw->iio_devs[i]); + if (!(hw->enable_mask & BIT(sensor->id))) + continue; + + err = st_ilps22qs_set_enable(sensor, true); + if (err < 0) + return err; + } + + return 0; +} + +const struct dev_pm_ops st_ilps22qs_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(st_ilps22qs_suspend, st_ilps22qs_resume) +}; +EXPORT_SYMBOL(st_ilps22qs_pm_ops); + +MODULE_AUTHOR("MEMS Software Solutions Team"); +MODULE_DESCRIPTION("STMicroelectronics ilps22qs driver"); +MODULE_LICENSE("GPL v2"); diff --git a/feeds/qca-wifi-7/ipq53xx/files-6.1/drivers/iio/pressure/st_ilps22qs_i2c.c b/feeds/qca-wifi-7/ipq53xx/files-6.1/drivers/iio/pressure/st_ilps22qs_i2c.c new file mode 100644 index 000000000..8e28c5eab --- /dev/null +++ b/feeds/qca-wifi-7/ipq53xx/files-6.1/drivers/iio/pressure/st_ilps22qs_i2c.c @@ -0,0 +1,86 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * STMicroelectronics ilps22qs i2c driver + * + * Copyright 2023 STMicroelectronics Inc. + * + * MEMS Software Solutions Team + */ + +#include +#include +#include +#include +#include +#include + +#include "st_ilps22qs.h" + +static const struct regmap_config st_ilps22qs_i2c_regmap_config = { + .reg_bits = 8, + .val_bits = 8, +}; + +#if KERNEL_VERSION(6, 2, 0) <= LINUX_VERSION_CODE +static int st_ilps22qs_i2c_probe(struct i2c_client *client) +#else /* LINUX_VERSION_CODE */ +static int st_ilps22qs_i2c_probe(struct i2c_client *client, + const struct i2c_device_id *id) +#endif /* LINUX_VERSION_CODE */ +{ + struct regmap *regmap; + + regmap = devm_regmap_init_i2c(client, &st_ilps22qs_i2c_regmap_config); + if (IS_ERR(regmap)) { + dev_err(&client->dev, + "Failed to register i2c regmap %d\n", + (int)PTR_ERR(regmap)); + + return PTR_ERR(regmap); + } + + return st_ilps22qs_probe(&client->dev, regmap); +} + +#if KERNEL_VERSION(6, 1, 0) <= LINUX_VERSION_CODE +static void st_ilps22qs_i2c_remove(struct i2c_client *client) +{ + st_ilps22qs_remove(&client->dev); +} +#else /* LINUX_VERSION_CODE */ +static int st_ilps22qs_i2c_remove(struct i2c_client *client) +{ + return st_ilps22qs_remove(&client->dev); +} +#endif /* LINUX_VERSION_CODE */ + +static const struct i2c_device_id st_ilps22qs_ids[] = { + { ST_ILPS22QS_DEV_NAME }, + { ST_ILPS28QSW_DEV_NAME }, + {} +}; +MODULE_DEVICE_TABLE(i2c, st_ilps22qs_ids); + +static const struct of_device_id st_ilps22qs_id_table[] = { + { .compatible = "st," ST_ILPS22QS_DEV_NAME }, + { .compatible = "st," ST_ILPS28QSW_DEV_NAME }, + {}, +}; +MODULE_DEVICE_TABLE(of, st_ilps22qs_id_table); + +static struct i2c_driver st_ilps22qs_i2c_driver = { + .driver = { + .owner = THIS_MODULE, + .name = "st_" ST_ILPS22QS_DEV_NAME "_i2c", + .pm = &st_ilps22qs_pm_ops, + .of_match_table = of_match_ptr(st_ilps22qs_id_table), + }, + .probe = st_ilps22qs_i2c_probe, + .remove = st_ilps22qs_i2c_remove, + .id_table = st_ilps22qs_ids, +}; +module_i2c_driver(st_ilps22qs_i2c_driver); + +MODULE_AUTHOR("MEMS Software Solutions Team"); +MODULE_DESCRIPTION("STMicroelectronics ilps22qs i2c driver"); +MODULE_LICENSE("GPL v2"); diff --git a/feeds/qca-wifi-7/ipq53xx/files-6.1/drivers/input/misc/lsm303agr/Kconfig b/feeds/qca-wifi-7/ipq53xx/files-6.1/drivers/input/misc/lsm303agr/Kconfig new file mode 100644 index 000000000..de0c9884f --- /dev/null +++ b/feeds/qca-wifi-7/ipq53xx/files-6.1/drivers/input/misc/lsm303agr/Kconfig @@ -0,0 +1,6 @@ + +config INPUT_LSM303AGR + tristate "STM LSM303AGR sensor" + depends on I2C && SYSFS + help + This driver support the STMicroelectronics LSM303AGR sensor. diff --git a/feeds/qca-wifi-7/ipq53xx/files-6.1/drivers/input/misc/lsm303agr/Makefile b/feeds/qca-wifi-7/ipq53xx/files-6.1/drivers/input/misc/lsm303agr/Makefile new file mode 100644 index 000000000..dba1a7405 --- /dev/null +++ b/feeds/qca-wifi-7/ipq53xx/files-6.1/drivers/input/misc/lsm303agr/Makefile @@ -0,0 +1,7 @@ +# +# Makefile for the input misc lsm303agr driver. +# + +# Each configuration option enables a list of files. + +obj-$(CONFIG_INPUT_LSM303AGR) += lsm303agr_acc.o lsm303agr_mag.o lsm303agr_acc_i2c.o lsm303agr_mag_i2c.o diff --git a/feeds/qca-wifi-7/ipq53xx/files-6.1/drivers/input/misc/lsm303agr/lsm303agr_acc.c b/feeds/qca-wifi-7/ipq53xx/files-6.1/drivers/input/misc/lsm303agr/lsm303agr_acc.c new file mode 100644 index 000000000..e98e54ac7 --- /dev/null +++ b/feeds/qca-wifi-7/ipq53xx/files-6.1/drivers/input/misc/lsm303agr/lsm303agr_acc.c @@ -0,0 +1,885 @@ +/* + * STMicroelectronics lsm303agr_acc.c driver + * + * Copyright 2016 STMicroelectronics Inc. + * + * Giuseppe Barba + * + * Licensed under the GPL-2. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "lsm303agr_core.h" + +#define LSM303AGR_ACC_DEV_NAME "lsm303agr_acc" + +#define LSM303AGR_ACC_MIN_POLL_PERIOD_MS 1 + +/* I2C slave address */ +#define LSM303AGR_ACC_I2C_SAD 0x29 +/* Accelerometer Sensor Full Scale */ +#define LSM303AGR_ACC_FS_MSK 0x20 +#define LSM303AGR_ACC_G_2G 0x00 +#define LSM303AGR_ACC_G_8G 0x20 + +#define AXISDATA_REG 0x28 +#define WHOAMI_LSM303AGR_ACC 0x33 +#define WHO_AM_I 0x0F +#define CTRL_REG1 0x20 +#define CTRL_REG2 0x23 + +#define LSM303AGR_ACC_PM_OFF 0x00 +#define LSM303AGR_ACC_ENABLE_ALL_AXIS 0x07 +#define LSM303AGR_ACC_AXIS_MSK 0x07 +#define LSM303AGR_ACC_ODR_MSK 0xf0 +#define LSM303AGR_ACC_LP_MSK 0X08 +#define LSM303AGR_ACC_HR_MSK 0X08 + +/* device opmode */ +enum lsm303agr_acc_opmode { + LSM303AGR_ACC_OPMODE_NORMAL, + LSM303AGR_ACC_OPMODE_HR, + LSM303AGR_ACC_OPMODE_LP, +}; + +/* Device sensitivities [ug/digit] */ +#define LSM303AGR_ACC_SENSITIVITY_NORMAL_2G 3900 +#define LSM303AGR_ACC_SENSITIVITY_NORMAL_4G 7820 +#define LSM303AGR_ACC_SENSITIVITY_NORMAL_8G 15630 +#define LSM303AGR_ACC_SENSITIVITY_NORMAL_16G 46900 +#define LSM303AGR_ACC_SENSITIVITY_HR_2G 980 +#define LSM303AGR_ACC_SENSITIVITY_HR_4G 1950 +#define LSM303AGR_ACC_SENSITIVITY_HR_8G 3900 +#define LSM303AGR_ACC_SENSITIVITY_HR_16G 11720 +#define LSM303AGR_ACC_SENSITIVITY_LP_2G 15630 +#define LSM303AGR_ACC_SENSITIVITY_LP_4G 31260 +#define LSM303AGR_ACC_SENSITIVITY_LP_8G 62520 +#define LSM303AGR_ACC_SENSITIVITY_LP_16G 187580 + +/* Device shift values */ +#define LSM303AGR_ACC_SHIFT_NORMAL_MODE 6 +#define LSM303AGR_ACC_SHIFT_HR_MODE 4 +#define LSM303AGR_ACC_SHIFT_LP_MODE 8 + +const struct { + u16 shift; + u32 sensitivity[4]; +} lsm303agr_acc_opmode_table[] = { + { + /* normal mode */ + LSM303AGR_ACC_SHIFT_NORMAL_MODE, + { + LSM303AGR_ACC_SENSITIVITY_NORMAL_2G, + LSM303AGR_ACC_SENSITIVITY_NORMAL_4G, + LSM303AGR_ACC_SENSITIVITY_NORMAL_8G, + LSM303AGR_ACC_SENSITIVITY_NORMAL_16G + } + }, + { + /* hr mode */ + LSM303AGR_ACC_SHIFT_HR_MODE, + { + LSM303AGR_ACC_SENSITIVITY_HR_2G, + LSM303AGR_ACC_SENSITIVITY_HR_4G, + LSM303AGR_ACC_SENSITIVITY_HR_8G, + LSM303AGR_ACC_SENSITIVITY_HR_16G + } + }, + { + /* lp mode */ + LSM303AGR_ACC_SHIFT_LP_MODE, + { + LSM303AGR_ACC_SENSITIVITY_LP_2G, + LSM303AGR_ACC_SENSITIVITY_LP_4G, + LSM303AGR_ACC_SENSITIVITY_LP_8G, + LSM303AGR_ACC_SENSITIVITY_LP_16G + } + } +}; + +#define LSM303AGR_ACC_ODR10 0x20 /* 10Hz output data rate */ +#define LSM303AGR_ACC_ODR50 0x40 /* 50Hz output data rate */ +#define LSM303AGR_ACC_ODR100 0x50 /* 100Hz output data rate */ +#define LSM303AGR_ACC_ODR200 0x60 /* 200Hz output data rate */ + +/* read and write with mask a given register */ +static int lsm303agr_acc_write_data_with_mask(struct lsm303agr_common_data *cdata, + u8 reg_addr, u8 mask, u8 *data) +{ + int err; + u8 new_data, old_data = 0; + + err = cdata->tf->read(cdata->dev, reg_addr, 1, &old_data); + if (err < 0) + return err; + + new_data = ((old_data & (~mask)) | ((*data) & mask)); + +#ifdef LSM303AGR_ACC_DEBUG + dev_info(cdata->dev, "%s %02x o=%02x d=%02x n=%02x\n", + LSM303AGR_ACC_DEV_NAME, reg_addr, old_data, *data, new_data); +#endif + + /* Save for caller usage the data that is about to be written */ + *data = new_data; + + if (new_data == old_data) + return 1; + + return cdata->tf->write(cdata->dev, reg_addr, 1, &new_data); +} + +static int lsm303agr_acc_input_init(struct lsm303agr_sensor_data *sdata, + const char* description) +{ + int err; + + sdata->input_dev = input_allocate_device(); + if (!sdata->input_dev) { + dev_err(sdata->cdata->dev, "input device allocation failed\n"); + return -ENOMEM; + } + + sdata->input_dev->name = description; + sdata->input_dev->id.bustype = sdata->cdata->bus_type; + sdata->input_dev->dev.parent = sdata->cdata->dev; + + input_set_drvdata(sdata->input_dev, sdata); + + /* Set the input event characteristics of the probed sensor driver */ + set_bit(INPUT_EVENT_TYPE, sdata->input_dev->evbit); + set_bit(INPUT_EVENT_TIME_MSB, sdata->input_dev->mscbit); + set_bit(INPUT_EVENT_TIME_LSB, sdata->input_dev->mscbit); + set_bit(INPUT_EVENT_X, sdata->input_dev->mscbit); + set_bit(INPUT_EVENT_Y, sdata->input_dev->mscbit); + set_bit(INPUT_EVENT_Z, sdata->input_dev->mscbit); + + err = input_register_device(sdata->input_dev); + if (err) { + dev_err(sdata->cdata->dev, + "unable to register input device %s\n", + sdata->input_dev->name); + input_free_device(sdata->input_dev); + return err; + } + + return 0; +} + +struct { + unsigned int cutoff_ms; + unsigned int mask; +} lsm303agr_acc_odr_table[] = { + { 5, LSM303AGR_ACC_ODR200 }, /* ODR = 200Hz */ + { 10, LSM303AGR_ACC_ODR100 }, /* ODR = 100Hz */ + { 20, LSM303AGR_ACC_ODR50 }, /* ODR = 50Hz */ + { 100, LSM303AGR_ACC_ODR10 }, /* ODR = 10Hz */ +}; + +static int lsm303agr_acc_hw_init(struct lsm303agr_common_data *cdata) +{ + int err; + u8 buf, wai = 0; + +#ifdef LSM303AGR_ACC_DEBUG + pr_info("%s: hw init start\n", LSM303AGR_ACC_DEV_NAME); +#endif + + err = cdata->tf->read(cdata->dev, WHO_AM_I, 1, &wai); + if (err < 0) { + dev_warn(cdata->dev, "Error reading WHO_AM_I\n"); + goto error; + } + + if (wai != WHOAMI_LSM303AGR_ACC) { + dev_err(cdata->dev, + "device unknown (0x%02x-0x%02x)\n", + WHOAMI_LSM303AGR_ACC, wai); + err = -1; /* choose the right coded error */ + goto error; + } + + buf = cdata->sensors[LSM303AGR_ACC_SENSOR].c_odr; + err = lsm303agr_acc_write_data_with_mask(cdata, CTRL_REG1, + LSM303AGR_ACC_ODR_MSK, &buf); + if (err < 0) + goto error; + + cdata->hw_initialized = 1; + +#ifdef LSM303AGR_ACC_DEBUG + pr_info("%s: hw init done\n", LSM303AGR_ACC_DEV_NAME); +#endif + return 0; + +error: + cdata->hw_initialized = 0; + dev_err(cdata->dev, "hw init error 0x%02x: %d\n", buf, err); + + return err; +} + +static void lsm303agr_acc_device_power_off(struct lsm303agr_common_data *cdata) +{ + int err; + u8 buf = LSM303AGR_ACC_PM_OFF; + + err = lsm303agr_acc_write_data_with_mask(cdata, CTRL_REG1, + LSM303AGR_ACC_ODR_MSK, &buf); + if (err < 0) + dev_err(cdata->dev, "soft power off failed: %d\n", err); + + if (cdata->hw_initialized) + cdata->hw_initialized = 0; +} + +static int lsm303agr_acc_device_power_on(struct lsm303agr_common_data *cdata) +{ + if (!cdata->hw_initialized) { + int err = lsm303agr_acc_hw_init(cdata); + if (err < 0) { + lsm303agr_acc_device_power_off(cdata); + return err; + } + } + + return 0; +} + +static int lsm303agr_acc_update_fs_range(struct lsm303agr_common_data *cdata, + u8 new_fs_range) +{ + int err; + u8 indx; + u16 opmode; + unsigned int new_sensitivity; + struct lsm303agr_sensor_data *sdata; + + sdata = &cdata->sensors[LSM303AGR_ACC_SENSOR]; + opmode = sdata->opmode; + + switch (new_fs_range) { + case LSM303AGR_ACC_G_2G: + indx = 0; + break; + case LSM303AGR_ACC_G_8G: + indx = 2; + break; + default: + dev_err(cdata->dev, "invalid fs range requested: %u\n", + new_fs_range); + return -EINVAL; + } + + /* Updates configuration register 4 which contains fs range setting */ + err = lsm303agr_acc_write_data_with_mask(cdata, CTRL_REG2, + LSM303AGR_ACC_FS_MSK, + &new_fs_range); + if (err < 0) + goto error; + + new_sensitivity = lsm303agr_acc_opmode_table[opmode].sensitivity[indx]; + sdata->sensitivity = new_sensitivity; + +#ifdef LSM303AGR_ACC_DEBUG + dev_info(cdata->dev, "%s shift=%d, sens=%d, opm=%d\n", + LSM303AGR_ACC_DEV_NAME, sdata->shift, sdata->sensitivity, + sdata->opmode); +#endif + + return err; +error: + dev_err(cdata->dev, "update fs range failed %d\n", err); + + return err; +} + +static int lsm303agr_acc_update_odr(struct lsm303agr_common_data *cdata, + int poll_interval) +{ + u8 buf; + int i, err = -1; + struct lsm303agr_sensor_data *sdata; + + /** + * Following, looks for the longest possible odr + * interval od -x /dev/input/event0 scrolling the + * odr_table vector from the end (shortest interval) backward (longest + * interval), to support the poll_interval requested by the system. + * It must be the longest interval lower then the poll interval + */ + for (i = ARRAY_SIZE(lsm303agr_acc_odr_table) - 1; i >= 0; i--) { + if ((lsm303agr_acc_odr_table[i].cutoff_ms <= poll_interval) || + (i == 0)) + break; + } + + sdata = &cdata->sensors[LSM303AGR_ACC_SENSOR]; + /* also save requested odr */ + buf = (sdata->c_odr = lsm303agr_acc_odr_table[i].mask) | + LSM303AGR_ACC_ENABLE_ALL_AXIS; + + /* + * If device is currently enabled, we need to write new + * configuration out to it + */ + if (atomic_read(&cdata->enabled)) { + err = lsm303agr_acc_write_data_with_mask(cdata, CTRL_REG1, + LSM303AGR_ACC_ODR_MSK | + LSM303AGR_ACC_AXIS_MSK, + &buf); + if (err < 0) + goto error; + } + +#ifdef LSM303AGR_ACC_DEBUG + dev_info(cdata->dev, "update odr to 0x%02x,0x%02x: %d\n", + CTRL_REG1, buf, err); +#endif + + return err; + +error: + dev_err(cdata->dev, "update odr failed 0x%02x,0x%02x: %d\n", + CTRL_REG1, buf, err); + + return err; +} + +static int lsm303agr_acc_update_opmode(struct lsm303agr_common_data *cdata, + unsigned short opmode) +{ + int err; + struct lsm303agr_sensor_data *sdata; + u8 lp = 0, hr = 0, indx = 0; + + switch (opmode) { + case LSM303AGR_ACC_OPMODE_NORMAL: + break; + case LSM303AGR_ACC_OPMODE_HR: + hr = (1 << __ffs(LSM303AGR_ACC_LP_MSK)); + break; + case LSM303AGR_ACC_OPMODE_LP: + lp = (1 << __ffs(LSM303AGR_ACC_HR_MSK)); + break; + default: + return -EINVAL; + } + + /* Set LP bit in CTRL_REG1 */ + err = lsm303agr_acc_write_data_with_mask(cdata, CTRL_REG1, + LSM303AGR_ACC_LP_MSK, &lp); + if (err < 0) + goto error; + + /* Set HR bit in CTRL_REG4 */ + err = lsm303agr_acc_write_data_with_mask(cdata, CTRL_REG2, + LSM303AGR_ACC_HR_MSK, + &hr); + if (err < 0) + goto error; + + /* Change platform data */ + sdata = &cdata->sensors[LSM303AGR_ACC_SENSOR]; + sdata->opmode = opmode; + sdata->shift = lsm303agr_acc_opmode_table[opmode].shift; + + switch (sdata->fs_range) { + case LSM303AGR_ACC_G_2G: + indx = 0; + break; + case LSM303AGR_ACC_G_8G: + indx = 2; + break; + } + sdata->sensitivity = lsm303agr_acc_opmode_table[opmode].sensitivity[indx]; + +#ifdef LSM303AGR_ACC_DEBUG + dev_info(cdata->dev, "%s shift=%d, sens=%d, opm=%d\n", + LSM303AGR_ACC_DEV_NAME, sdata->shift, sdata->sensitivity, + sdata->opmode); +#endif + + return err; + +error: + dev_err(cdata->dev, "update opmode failed: %d\n", err); + + return err; +} + +static int +lsm303agr_acc_get_acceleration_data(struct lsm303agr_common_data *cdata, int *xyz) +{ + int err; + /* Data bytes from hardware xL, xH, yL, yH, zL, zH */ + u8 acc_data[6]; + /* x,y,z hardware data */ + u32 sensitivity, shift; + + err = cdata->tf->read(cdata->dev, AXISDATA_REG, 6, acc_data); + if (err < 0) + return err; + + /* Get the current sensitivity and shift values */ + sensitivity = cdata->sensors[LSM303AGR_ACC_SENSOR].sensitivity; + shift = cdata->sensors[LSM303AGR_ACC_SENSOR].shift; + + /* Transform LSBs into ug */ + xyz[0] = (s32)((s16)(acc_data[0] | (acc_data[1] << 8)) >> shift) * sensitivity; + xyz[1] = (s32)((s16)(acc_data[2] | (acc_data[3] << 8)) >> shift) * sensitivity; + xyz[2] = (s32)((s16)(acc_data[4] | (acc_data[5] << 8)) >> shift) * sensitivity; + +#ifdef LSM303AGR_ACC_DEBUG + dev_info(cdata->dev, "%s read x=%d, y=%d, z=%d\n", + LSM303AGR_ACC_DEV_NAME, xyz[0], xyz[1], xyz[2]); +#endif + + return err; +} + +static void lsm303agr_acc_report_values(struct lsm303agr_common_data *cdata, + int *xyz, s64 timestamp) +{ + struct lsm303agr_sensor_data *sdata; + + sdata = &cdata->sensors[LSM303AGR_ACC_SENSOR]; + input_event(sdata->input_dev, INPUT_EVENT_TYPE, INPUT_EVENT_X, xyz[0]); + input_event(sdata->input_dev, INPUT_EVENT_TYPE, INPUT_EVENT_Y, xyz[1]); + input_event(sdata->input_dev, INPUT_EVENT_TYPE, INPUT_EVENT_Z, xyz[2]); + input_event(sdata->input_dev, INPUT_EVENT_TYPE, INPUT_EVENT_TIME_MSB, + timestamp >> 32); + input_event(sdata->input_dev, INPUT_EVENT_TYPE, INPUT_EVENT_TIME_LSB, + timestamp & 0xffffffff); + input_sync(sdata->input_dev); +} + +int lsm303agr_acc_enable(struct lsm303agr_common_data *cdata) +{ + if (!atomic_cmpxchg(&cdata->enabled, 0, 1)) { + int err; + struct lsm303agr_sensor_data *sdata; + + mutex_lock(&cdata->lock); + + sdata = &cdata->sensors[LSM303AGR_ACC_SENSOR]; + err = lsm303agr_acc_device_power_on(cdata); + if (err < 0) { + atomic_set(&cdata->enabled, 0); + return err; + } + schedule_delayed_work(&sdata->input_work, + msecs_to_jiffies(sdata->poll_interval)); + + mutex_unlock(&cdata->lock); + } + + return 0; +} +EXPORT_SYMBOL(lsm303agr_acc_enable); + +int lsm303agr_acc_disable(struct lsm303agr_common_data *cdata) +{ + if (atomic_cmpxchg(&cdata->enabled, 1, 0)) { + struct lsm303agr_sensor_data *sdata; + + sdata = &cdata->sensors[LSM303AGR_ACC_SENSOR]; + cancel_delayed_work_sync(&sdata->input_work); + + mutex_lock(&cdata->lock); + lsm303agr_acc_device_power_off(cdata); + mutex_unlock(&cdata->lock); + } + + return 0; +} +EXPORT_SYMBOL(lsm303agr_acc_disable); + +static ssize_t attr_get_sched_num_acc(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int val; + struct lsm303agr_common_data *cdata = dev_get_drvdata(dev); + + mutex_lock(&cdata->lock); + val = cdata->sensors[LSM303AGR_ACC_SENSOR].schedule_num; + mutex_unlock(&cdata->lock); + + return sprintf(buf, "%d\n", val); +} + +static ssize_t attr_set_sched_num_acc(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + unsigned long sched_num; + struct lsm303agr_common_data *cdata = dev_get_drvdata(dev); + + if (kstrtoul(buf, 10, &sched_num)) + return -EINVAL; + + mutex_lock(&cdata->lock); + cdata->sensors[LSM303AGR_ACC_SENSOR].schedule_num = sched_num; + mutex_unlock(&cdata->lock); + + return size; +} + +static ssize_t attr_get_polling_rate_acc(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + int val; + struct lsm303agr_common_data *cdata = dev_get_drvdata(dev); + + mutex_lock(&cdata->lock); + val = cdata->sensors[LSM303AGR_ACC_SENSOR].poll_interval; + mutex_unlock(&cdata->lock); + + return sprintf(buf, "%d\n", val); +} + +static ssize_t attr_set_polling_rate_acc(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + unsigned long interval_ms; + struct lsm303agr_sensor_data *sdata; + struct lsm303agr_common_data *cdata = dev_get_drvdata(dev); + + if (kstrtoul(buf, 10, &interval_ms)) + return -EINVAL; + + if (!interval_ms) + return -EINVAL; + + sdata = &cdata->sensors[LSM303AGR_ACC_SENSOR]; + interval_ms = max_t(unsigned int, (unsigned int)interval_ms, + sdata->min_interval); + + mutex_lock(&cdata->lock); + sdata->poll_interval = interval_ms; + lsm303agr_acc_update_odr(cdata, interval_ms); + mutex_unlock(&cdata->lock); + + return size; +} + +static ssize_t attr_get_range_acc(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct lsm303agr_sensor_data *sdata; + char range = 2; + struct lsm303agr_common_data *cdata = dev_get_drvdata(dev); + + sdata = &cdata->sensors[LSM303AGR_ACC_SENSOR]; + + mutex_lock(&cdata->lock); + switch (sdata->fs_range) { + case LSM303AGR_ACC_G_2G: + range = 2; + break; + case LSM303AGR_ACC_G_8G: + range = 8; + break; + } + mutex_unlock(&cdata->lock); + + return sprintf(buf, "%d\n", range); +} + +static ssize_t attr_set_range_acc(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + u8 range; + int err; + unsigned long val; + struct lsm303agr_sensor_data *sdata; + struct lsm303agr_common_data *cdata = dev_get_drvdata(dev); + + sdata = &cdata->sensors[LSM303AGR_ACC_SENSOR]; + if (kstrtoul(buf, 10, &val)) + return -EINVAL; + + switch (val) { + case 2: + range = LSM303AGR_ACC_G_2G; + break; + case 8: + range = LSM303AGR_ACC_G_8G; + break; + default: + dev_err(cdata->dev, + "invalid range request: %lu, discarded\n", val); + return -EINVAL; + } + + mutex_lock(&cdata->lock); + err = lsm303agr_acc_update_fs_range(cdata, range); + if (err < 0) { + mutex_unlock(&cdata->lock); + return err; + } + sdata->fs_range = range; + mutex_unlock(&cdata->lock); + + dev_info(cdata->dev, "range set to: %lu g\n", val); + + return size; +} + +static ssize_t attr_get_opmode_acc(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + char opmode; + struct lsm303agr_common_data *cdata = dev_get_drvdata(dev); + + mutex_lock(&cdata->lock); + opmode = cdata->sensors[LSM303AGR_ACC_SENSOR].opmode; + mutex_unlock(&cdata->lock); + + return sprintf(buf, "%d\n", opmode); +} + +static ssize_t attr_set_opmode_acc(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + int err; + u16 opmode; + unsigned long val; + struct lsm303agr_common_data *cdata = dev_get_drvdata(dev); + + if (kstrtoul(buf, 10, &val)) + return -EINVAL; + + /* Check if argument is valid opmode */ + switch (val) { + case LSM303AGR_ACC_OPMODE_NORMAL: + case LSM303AGR_ACC_OPMODE_HR: + case LSM303AGR_ACC_OPMODE_LP: + opmode = val; + break; + default: + dev_err(cdata->dev, + "invalid range request: %lu, discarded\n", val); + return -EINVAL; + } + + mutex_lock(&cdata->lock); + err = lsm303agr_acc_update_opmode(cdata, opmode); + if (err < 0) { + mutex_unlock(&cdata->lock); + return err; + } + mutex_unlock(&cdata->lock); + + dev_info(cdata->dev, "opmode set to: %u\n", opmode); + + return size; +} + +static ssize_t attr_get_enable_acc(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct lsm303agr_common_data *cdata = dev_get_drvdata(dev); + int val = atomic_read(&cdata->enabled); + + return sprintf(buf, "%d\n", val); +} + +static ssize_t attr_set_enable_acc(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + struct lsm303agr_common_data *cdata = dev_get_drvdata(dev); + unsigned long val; + + if (kstrtoul(buf, 10, &val)) + return -EINVAL; + + if (val) + lsm303agr_acc_enable(cdata); + else + lsm303agr_acc_disable(cdata); + + return size; +} + +static struct device_attribute attributes[] = { + + __ATTR(pollrate_ms, 0664, attr_get_polling_rate_acc, + attr_set_polling_rate_acc), + __ATTR(range, 0664, attr_get_range_acc, attr_set_range_acc), + __ATTR(opmode, 0664, attr_get_opmode_acc, attr_set_opmode_acc), + __ATTR(enable_device, 0664, attr_get_enable_acc, attr_set_enable_acc), + __ATTR(schedule_num, 0664, attr_get_sched_num_acc, + attr_set_sched_num_acc), +}; + +static int create_sysfs_interfaces(struct device *dev) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(attributes); i++) + if (device_create_file(dev, attributes + i)) + goto error; + return 0; + +error: + for (; i >= 0; i--) + device_remove_file(dev, attributes + i); + + dev_err(dev, "%s:Unable to create interface\n", __func__); + + return -1; +} + +static int remove_sysfs_interfaces(struct device *dev) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(attributes); i++) + device_remove_file(dev, attributes + i); + + return 0; +} + +static void lsm303agr_acc_input_work_func(struct work_struct *work) +{ + struct lsm303agr_common_data *cdata; + struct lsm303agr_sensor_data *sdata; + int err, xyz[3] = {}; + + sdata = container_of((struct delayed_work *)work, + struct lsm303agr_sensor_data, input_work); + cdata = sdata->cdata; + + mutex_lock(&cdata->lock); + sdata->schedule_num++; + err = lsm303agr_acc_get_acceleration_data(cdata, xyz); + if (err < 0) + dev_err(cdata->dev, "get_acceleration_data failed\n"); + else + lsm303agr_acc_report_values(cdata, xyz, lsm303agr_get_time_ns()); + + schedule_delayed_work(&sdata->input_work, msecs_to_jiffies( + sdata->poll_interval)); + mutex_unlock(&cdata->lock); +} + +static void lsm303agr_acc_input_cleanup(struct lsm303agr_common_data *cdata) +{ + struct lsm303agr_sensor_data *sdata; + + sdata = &cdata->sensors[LSM303AGR_ACC_SENSOR]; + input_unregister_device(sdata->input_dev); + input_free_device(sdata->input_dev); +} + +int lsm303agr_acc_probe(struct lsm303agr_common_data *cdata) +{ + int err; + struct lsm303agr_sensor_data *sdata; + + mutex_lock(&cdata->lock); + /* init sensor data structure */ + sdata = &cdata->sensors[LSM303AGR_ACC_SENSOR]; + + sdata->cdata = cdata; + sdata->poll_interval = 100; + sdata->min_interval = LSM303AGR_ACC_MIN_POLL_PERIOD_MS; + + err = lsm303agr_acc_device_power_on(cdata); + if (err < 0) { + dev_err(cdata->dev, "power on failed: %d\n", err); + goto err_power_off; + } + + atomic_set(&cdata->enabled, 1); + + err = lsm303agr_acc_update_fs_range(cdata, LSM303AGR_ACC_G_2G); + if (err < 0) { + dev_err(cdata->dev, "update_fs_range failed\n"); + goto err_power_off; + } + + err = lsm303agr_acc_update_odr(cdata, sdata->poll_interval); + if (err < 0) { + dev_err(cdata->dev, "update_odr failed\n"); + goto err_power_off; + } + + err = lsm303agr_acc_update_opmode(cdata, LSM303AGR_ACC_OPMODE_NORMAL); + if (err < 0) { + dev_err(cdata->dev, "update_opmode failed\n"); + goto err_power_off; + } + + err = lsm303agr_acc_input_init(sdata, LSM303AGR_ACC_DEV_NAME); + if (err < 0) { + dev_err(cdata->dev, "input init failed\n"); + goto err_power_off; + } + INIT_DELAYED_WORK(&sdata->input_work, lsm303agr_acc_input_work_func); + + + err = create_sysfs_interfaces(cdata->dev); + if (err < 0) { + dev_err(cdata->dev, + "device LSM303AGR_ACC_DEV_NAME sysfs register failed\n"); + goto err_input_cleanup; + } + + lsm303agr_acc_device_power_off(cdata); + + /* As default, do not report information */ + atomic_set(&cdata->enabled, 0); + + dev_info(cdata->dev, "%s: probed\n", LSM303AGR_ACC_DEV_NAME); + + mutex_unlock(&cdata->lock); + + return 0; + +err_input_cleanup: + lsm303agr_acc_input_cleanup(cdata); +err_power_off: + lsm303agr_acc_device_power_off(cdata); + mutex_unlock(&cdata->lock); + pr_err("%s: Driver Init failed\n", LSM303AGR_ACC_DEV_NAME); + + return err; +} +EXPORT_SYMBOL(lsm303agr_acc_probe); + +void lsm303agr_acc_remove(struct lsm303agr_common_data *cdata) +{ + lsm303agr_acc_disable(cdata); + lsm303agr_acc_input_cleanup(cdata); + remove_sysfs_interfaces(cdata->dev); +} +EXPORT_SYMBOL(lsm303agr_acc_remove); + +MODULE_LICENSE("GPL v2"); + diff --git a/feeds/qca-wifi-7/ipq53xx/files-6.1/drivers/input/misc/lsm303agr/lsm303agr_acc_i2c.c b/feeds/qca-wifi-7/ipq53xx/files-6.1/drivers/input/misc/lsm303agr/lsm303agr_acc_i2c.c new file mode 100644 index 000000000..16aaba293 --- /dev/null +++ b/feeds/qca-wifi-7/ipq53xx/files-6.1/drivers/input/misc/lsm303agr/lsm303agr_acc_i2c.c @@ -0,0 +1,207 @@ +/* + * STMicroelectronics lsm303agr_acc_i2c.c driver + * + * Copyright 2016 STMicroelectronics Inc. + * + * Giuseppe Barba + * + * Licensed under the GPL-2. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "lsm303agr_core.h" + +#define I2C_AUTO_INCREMENT 0x80 + +/* XXX: caller must hold cdata->lock */ +static int lsm303agr_acc_i2c_read(struct device *dev, u8 reg_addr, int len, + u8 *data) +{ + struct i2c_msg msg[2]; + struct i2c_client *client = to_i2c_client(dev); + + if (len > 1) + reg_addr |= I2C_AUTO_INCREMENT; + + msg[0].addr = client->addr; + msg[0].flags = client->flags; + msg[0].len = 1; + msg[0].buf = ®_addr; + + msg[1].addr = client->addr; + msg[1].flags = client->flags | I2C_M_RD; + msg[1].len = len; + msg[1].buf = data; + + return i2c_transfer(client->adapter, msg, 2); +} + +/* XXX: caller must hold cdata->lock */ +static int lsm303agr_acc_i2c_write(struct device *dev, u8 reg_addr, int len, + u8 *data) +{ + u8 send[len + 1]; + struct i2c_msg msg; + struct i2c_client *client = to_i2c_client(dev); + + if (len > 1) + reg_addr |= I2C_AUTO_INCREMENT; + + send[0] = reg_addr; + memcpy(&send[1], data, len * sizeof(u8)); + len++; + + msg.addr = client->addr; + msg.flags = client->flags; + msg.len = len; + msg.buf = send; + + return i2c_transfer(client->adapter, &msg, 1); +} + +/* I2C IO routines */ +static const struct lsm303agr_transfer_function lsm303agr_acc_i2c_tf = { + .write = lsm303agr_acc_i2c_write, + .read = lsm303agr_acc_i2c_read, +}; + +#ifdef CONFIG_PM_SLEEP +static int lsm303agr_acc_resume(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct lsm303agr_common_data *cdata = i2c_get_clientdata(client); + + if (cdata->on_before_suspend) + return lsm303agr_acc_enable(cdata); + return 0; +} + +static int lsm303agr_acc_suspend(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct lsm303agr_common_data *cdata = i2c_get_clientdata(client); + + cdata->on_before_suspend = atomic_read(&cdata->enabled); + return lsm303agr_acc_disable(cdata); +} + +static SIMPLE_DEV_PM_OPS(lsm303agr_acc_pm_ops, + lsm303agr_acc_suspend, + lsm303agr_acc_resume); + +#define LSM303AGR_ACC_PM_OPS (&lsm303agr_acc_pm_ops) +#else /* CONFIG_PM_SLEEP */ +#define LSM303AGR_ACC_PM_OPS NULL +#endif /* CONFIG_PM_SLEEP */ + +static int lsm303agr_acc_i2c_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + int err; + struct lsm303agr_common_data *cdata; + + dev_info(&client->dev, "probe start.\n"); + + /* Alloc Common data structure */ + cdata = kzalloc(sizeof(struct lsm303agr_common_data), GFP_KERNEL); + if (!cdata) { + dev_err(&client->dev, "failed to allocate module data\n"); + return -ENOMEM; + } + + cdata->sensor_num = LSM303AGR_MAX_SENSORS_NUM; + cdata->dev = &client->dev; + cdata->name = client->name; + cdata->bus_type = BUS_I2C; + cdata->tf = &lsm303agr_acc_i2c_tf; + + i2c_set_clientdata(client, cdata); + + mutex_init(&cdata->lock); + + err = lsm303agr_acc_probe(cdata); + if (err < 0) { + kfree(cdata); + + return err; + } + + return 0; +} + +#if KERNEL_VERSION(6, 1, 0) <= LINUX_VERSION_CODE +static void lsm303agr_acc_i2c_remove(struct i2c_client *client) +{ + struct lsm303agr_common_data *cdata = i2c_get_clientdata(client); + + dev_info(cdata->dev, "driver removing\n"); + + lsm303agr_acc_remove(cdata); + kfree(cdata); +} +#else +static int lsm303agr_acc_i2c_remove(struct i2c_client *client) +{ + struct lsm303agr_common_data *cdata = i2c_get_clientdata(client); + + dev_info(cdata->dev, "driver removing\n"); + + lsm303agr_acc_remove(cdata); + kfree(cdata); + + return 0; +} +#endif + +static const struct i2c_device_id lsm303agr_acc_i2c_id[] = { + { "lsm303agr_acc", 0 }, + { }, +}; +MODULE_DEVICE_TABLE(i2c, lsm303agr_acc_i2c_id); + +#ifdef CONFIG_OF +static const struct of_device_id lsm303agr_acc_i2c_id_table[] = { + {.compatible = "st,lsm303agr_acc", }, + { }, +}; +MODULE_DEVICE_TABLE(of, lsm303agr_acc_i2c_id_table); +#endif /* CONFIG_OF */ + +static struct i2c_driver lsm303agr_acc_i2c_driver = { + .driver = { + .owner = THIS_MODULE, + .name = "lsm303agr_acc", + .pm = LSM303AGR_ACC_PM_OPS, +#ifdef CONFIG_OF + .of_match_table = lsm303agr_acc_i2c_id_table, +#endif /* CONFIG_OF */ + }, + .probe = lsm303agr_acc_i2c_probe, + .remove = lsm303agr_acc_i2c_remove, + .id_table = lsm303agr_acc_i2c_id, +}; + +module_i2c_driver(lsm303agr_acc_i2c_driver); + +MODULE_DESCRIPTION("lsm303agr accelerometer i2c driver"); +MODULE_AUTHOR("Armando Visconti"); +MODULE_AUTHOR("Matteo Dameno"); +MODULE_AUTHOR("Denis Ciocca"); +MODULE_AUTHOR("STMicroelectronics"); +MODULE_LICENSE("GPL v2"); + diff --git a/feeds/qca-wifi-7/ipq53xx/files-6.1/drivers/input/misc/lsm303agr/lsm303agr_core.h b/feeds/qca-wifi-7/ipq53xx/files-6.1/drivers/input/misc/lsm303agr/lsm303agr_core.h new file mode 100644 index 000000000..1d0b794f5 --- /dev/null +++ b/feeds/qca-wifi-7/ipq53xx/files-6.1/drivers/input/misc/lsm303agr/lsm303agr_core.h @@ -0,0 +1,104 @@ +/* + * STMicroelectronics lsm303agr driver + * + * Copyright 2016 STMicroelectronics Inc. + * + * Giuseppe Barba + * + * Licensed under the GPL-2. + */ + +#ifndef __LSM303AGR_H__ +#define __LSM303AGR_H__ + +#ifdef __KERNEL__ + +#define LSM303AGR_MAX_SENSORS_NUM 1 +#define LSM303AGR_ACC_SENSOR 0 /* only this sensor */ +#define LSM303AGR_MAG_SENSOR 0 /* only this sensor */ + +struct lsm303agr_common_data; + +/* specific bus I/O functions */ +struct lsm303agr_transfer_function { + int (*write) (struct device *dev, u8 reg_addr, int len, u8 *data); + int (*read) (struct device *dev, u8 reg_addr, int len, u8 *data); +}; + +#if defined(CONFIG_INPUT_LSM303AGR_SPI) || \ + defined(CONFIG_INPUT_LSM303AGR_SPI_MODULE) +#define LSM303AGR_RX_MAX_LENGTH 500 +#define LSM303AGR_TX_MAX_LENGTH 500 + +struct lsm303agr_transfer_buffer { + u8 rx_buf[LSM303AGR_RX_MAX_LENGTH]; + u8 tx_buf[LSM303AGR_TX_MAX_LENGTH] ____cacheline_aligned; +}; +#endif /* CONFIG_INPUT_LSM303AGR_SPI */ + +/* Sensor data */ +struct lsm303agr_sensor_data { + struct lsm303agr_common_data *cdata; + const char* name; + s64 timestamp; + u8 enabled; + u32 c_odr; + u32 c_gain; + u8 sindex; + u8 sample_to_discard; + u32 poll_interval; + u32 min_interval; + u8 fs_range; + u32 sensitivity; + u16 shift; + u16 opmode; + struct input_dev *input_dev; + struct delayed_work input_work; + u32 schedule_num; /* Number of time work_input routine is called */ +}; + +struct lsm303agr_common_data { + const char *name; + struct mutex lock; + struct device *dev; + int hw_initialized; + atomic_t enabled; + int on_before_suspend; + u8 sensor_num; + u16 bus_type; + struct lsm303agr_sensor_data sensors[LSM303AGR_MAX_SENSORS_NUM]; + const struct lsm303agr_transfer_function *tf; +#if defined(CONFIG_INPUT_LSM303AGR_SPI) || \ + defined(CONFIG_INPUT_LSM303AGR_SPI_MODULE) + struct lsm303agr_transfer_buffer tb; +#endif /* CONFIG_INPUT_LSM303AGR_SPI */ +}; + +/* Input events used by lsm303agr driver */ +#define INPUT_EVENT_TYPE EV_MSC +#define INPUT_EVENT_X MSC_SERIAL +#define INPUT_EVENT_Y MSC_PULSELED +#define INPUT_EVENT_Z MSC_GESTURE +#define INPUT_EVENT_TIME_MSB MSC_SCAN +#define INPUT_EVENT_TIME_LSB MSC_MAX + +static inline s64 lsm303agr_get_time_ns(void) +{ + return ktime_to_ns(ktime_get_boottime()); +} + +void lsm303agr_acc_remove(struct lsm303agr_common_data *cdata); +int lsm303agr_acc_probe(struct lsm303agr_common_data *cdata); +int lsm303agr_acc_enable(struct lsm303agr_common_data *cdata); +int lsm303agr_acc_disable(struct lsm303agr_common_data *cdata); + +void lsm303agr_mag_remove(struct lsm303agr_common_data *cdata); +int lsm303agr_mag_probe(struct lsm303agr_common_data *cdata); +int lsm303agr_mag_enable(struct lsm303agr_common_data *cdata); +int lsm303agr_mag_disable(struct lsm303agr_common_data *cdata); + +#endif /* __KERNEL__ */ +#endif /* __LSM303AGR_H__ */ + + + diff --git a/feeds/qca-wifi-7/ipq53xx/files-6.1/drivers/input/misc/lsm303agr/lsm303agr_mag.c b/feeds/qca-wifi-7/ipq53xx/files-6.1/drivers/input/misc/lsm303agr/lsm303agr_mag.c new file mode 100644 index 000000000..ea1e6ebad --- /dev/null +++ b/feeds/qca-wifi-7/ipq53xx/files-6.1/drivers/input/misc/lsm303agr/lsm303agr_mag.c @@ -0,0 +1,556 @@ +/* + * STMicroelectronics lsm303agr_mag.c driver + * + * Copyright 2016 STMicroelectronics Inc. + * + * Giuseppe Barba + * + * Licensed under the GPL-2. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "lsm303agr_core.h" + +#define LSM303AGR_MAG_DEV_NAME "lsm303agr_mag" + +/* DEVICE REGISTERS */ +#define WHO_AM_I 0x4F +#define CFG_REG_A 0x60 +#define AXISDATA_REG 0x68 +#define WHOAMI_LSM303AGR_MAG 0x40 +/* Device operating modes */ +#define MD_CONTINUOS_MODE 0x00 +#define MD_SINGLE_MODE 0x01 +#define MD_IDLE1_MODE 0x02 +#define MD_IDLE2_MODE 0x03 +#define LSM303AGR_MAG_MODE_MSK 0x03 + +/* Device ODRs */ +#define LSM303AGR_MAG_ODR10_HZ 0x00 +#define LSM303AGR_MAG_ODR20_HZ 0x04 +#define LSM303AGR_MAG_ODR50_HZ 0x08 +#define LSM303AGR_MAG_ODR100_HZ 0x0C +#define LSM303AGR_MAG_ODR_MSK 0x0C + +#define LSM303AGR_MAG_SENSITIVITY 1500 /* uGa/LSB */ + +/* ODR table */ +struct { + u32 time_ms; + u32 reg_val; +} lsm303agr_mag_odr_table[] = { + { 10, LSM303AGR_MAG_ODR100_HZ }, /* ODR = 100Hz */ + { 20, LSM303AGR_MAG_ODR50_HZ }, /* ODR = 50Hz */ + { 50, LSM303AGR_MAG_ODR20_HZ }, /* ODR = 20Hz */ + { 100, LSM303AGR_MAG_ODR10_HZ }, /* ODR = 10Hz */ +}; + +/* read and write with mask a given register */ +static int lsm303agr_mag_write_data_with_mask(struct lsm303agr_common_data *cdata, + u8 reg_addr, u8 mask, u8 *data) +{ + int err; + u8 new_data, old_data = 0; + + err = cdata->tf->read(cdata->dev, reg_addr, 1, &old_data); + if (err < 0) + return err; + + new_data = ((old_data & (~mask)) | ((*data) & mask)); + +#ifdef LSM303AGR_MAG_DEBUG + dev_info(cdata->dev, "%s %02x o=%02x d=%02x n=%02x\n", + LSM303AGR_MAG_DEV_NAME, reg_addr, old_data, *data, new_data); +#endif + + /* Save for caller usage the data that is about to be written */ + *data = new_data; + + if (new_data == old_data) + return 1; + + return cdata->tf->write(cdata->dev, reg_addr, 1, &new_data); +} + +int lsm303agr_mag_input_init(struct lsm303agr_sensor_data *sdata, + const char* description) +{ + int err; + + sdata->input_dev = input_allocate_device(); + if (!sdata->input_dev) { + dev_err(sdata->cdata->dev, "input device allocation failed\n"); + return -ENOMEM; + } + + sdata->input_dev->name = description; + sdata->input_dev->id.bustype = sdata->cdata->bus_type; + sdata->input_dev->dev.parent = sdata->cdata->dev; + + input_set_drvdata(sdata->input_dev, sdata); + + /* Set the input event characteristics of the probed sensor driver */ + set_bit(INPUT_EVENT_TYPE, sdata->input_dev->evbit ); + set_bit(INPUT_EVENT_TIME_MSB, sdata->input_dev->mscbit); + set_bit(INPUT_EVENT_TIME_LSB, sdata->input_dev->mscbit); + set_bit(INPUT_EVENT_X, sdata->input_dev->mscbit); + set_bit(INPUT_EVENT_Y, sdata->input_dev->mscbit); + set_bit(INPUT_EVENT_Z, sdata->input_dev->mscbit); + + err = input_register_device(sdata->input_dev); + if (err) { + dev_err(sdata->cdata->dev, + "unable to register input device %s\n", + sdata->input_dev->name); + input_free_device(sdata->input_dev); + return err; + } + + return 0; +} + +/* Check if WHO_AM_I is correct */ +static int lsm303agr_mag_check_wai(struct lsm303agr_common_data *cdata) +{ + int err; + u8 wai; + +#ifdef LSM303AGR_MAG_DEBUG + pr_info("%s: check WAI start\n", LSM303AGR_MAG_DEV_NAME); +#endif + + err = cdata->tf->read(cdata->dev, WHO_AM_I, 1, &wai); + if (err < 0) { + dev_warn(cdata->dev, + "Error reading WHO_AM_I: is device available/working?\n"); + goto error; + } + + if (wai != WHOAMI_LSM303AGR_MAG) { + dev_err(cdata->dev, + "device unknown. Expected: 0x%02x," " Replies: 0x%02x\n", + WHOAMI_LSM303AGR_MAG, wai); + err = -1; /* choose the right coded error */ + goto error; + } + + cdata->hw_initialized = 1; +#ifdef LSM303AGR_MAG_DEBUG + pr_info("%s: check WAI done\n", LSM303AGR_MAG_DEV_NAME); +#endif + return 0; + +error: + cdata->hw_initialized = 0; + dev_err(cdata->dev, + "check WAI error 0x%02x,0x%02x: %d\n", WHO_AM_I, wai, err); + + return err; +} + +static int lsm303agr_mag_set_odr(struct lsm303agr_common_data *cdata, u8 odr) +{ + u8 odr_reg; + int err = -1, i; + struct lsm303agr_sensor_data *sdata; + + /** + * Following, looks for the longest possible odr interval scrolling the + * odr_table vector from the end (shortest interval) backward + * (longest interval), to support the poll_interval requested + * by the system. It must be the longest interval lower + * than the poll interval + */ + for (i = ARRAY_SIZE(lsm303agr_mag_odr_table) - 1; i >= 0; i--) { + if ((lsm303agr_mag_odr_table[i].time_ms <= odr) || (i == 0)) + break; + } + + odr_reg = lsm303agr_mag_odr_table[i].reg_val; + sdata = &cdata->sensors[LSM303AGR_MAG_SENSOR]; + sdata->poll_interval = sdata->c_odr = lsm303agr_mag_odr_table[i].time_ms; + + /* If device is currently enabled, we need to write new + * configuration out to it */ + if (atomic_read(&cdata->enabled)) { + err = lsm303agr_mag_write_data_with_mask(cdata, CFG_REG_A, + LSM303AGR_MAG_ODR_MSK, + &odr_reg); + if (err < 0) + goto error; +#ifdef LSM303AGR_MAG_DEBUG + dev_info(cdata->dev, "update odr to 0x%02x,0x%02x: %d\n", + CFG_REG_A, odr_reg, err); +#endif + } + + return lsm303agr_mag_odr_table[i].time_ms; + +error: + dev_err(cdata->dev, "set odr failed 0x%02x,0x%02x: %d\n", + CFG_REG_A, odr_reg, err); + + return err; +} + +static int lsm303agr_mag_set_device_mode(struct lsm303agr_common_data *cdata, + u8 mode) +{ + int err; + + err = lsm303agr_mag_write_data_with_mask(cdata, CFG_REG_A, + LSM303AGR_MAG_MODE_MSK, + &mode); + if (err < 0) + goto error; + +#ifdef LSM303AGR_MAG_DEBUG + dev_info(cdata->dev, "update mode to 0x%02x,0x%02x: %d\n", + CFG_REG_A, mode, err); +#endif + + return 0; + +error: + dev_err(cdata->dev, + "set continuos mode failed 0x%02x,0x%02x: %d\n", + CFG_REG_A, mode, err); + + return err; +} + +/* Set device in continuos mode */ +int lsm303agr_mag_enable(struct lsm303agr_common_data *cdata) +{ + struct lsm303agr_sensor_data *sdata; + int err; + + mutex_lock(&cdata->lock); + /* Set the magnetometer in continuos mode */ + err = lsm303agr_mag_set_device_mode(cdata, MD_CONTINUOS_MODE); + if (err < 0) { + dev_err(cdata->dev, "set_continuos failed: %d\n", + err); + mutex_unlock(&cdata->lock); + return err; + } + + atomic_set(&cdata->enabled, 1); + + sdata = &cdata->sensors[LSM303AGR_MAG_SENSOR]; + if (lsm303agr_mag_set_odr(cdata, sdata->c_odr) < 0) { + mutex_unlock(&cdata->lock); + return -1; + } + + /* Start scheduling input */ + schedule_delayed_work(&sdata->input_work, + msecs_to_jiffies(sdata->poll_interval)); + mutex_unlock(&cdata->lock); + + return 0; +} +EXPORT_SYMBOL(lsm303agr_mag_enable); + +/* Set device in idle mode */ +int lsm303agr_mag_disable(struct lsm303agr_common_data *cdata) +{ + if (atomic_cmpxchg(&cdata->enabled, 1, 0)) { + int err; + struct lsm303agr_sensor_data *sdata; + + sdata = &cdata->sensors[LSM303AGR_MAG_SENSOR]; + cancel_delayed_work_sync(&sdata->input_work); + + mutex_lock(&cdata->lock); + /* Set the magnetometer in idle mode */ + err = lsm303agr_mag_set_device_mode(cdata, MD_IDLE2_MODE); + if (err < 0) { + dev_err(cdata->dev, "set_idle failed: %d\n", + err); + mutex_unlock(&cdata->lock); + return err; + } + mutex_unlock(&cdata->lock); + } + + return 0; +} +EXPORT_SYMBOL(lsm303agr_mag_disable); + +static void lsm303agr_mag_report_event(struct lsm303agr_common_data *cdata, + int *xyz, s64 timestamp) +{ + struct lsm303agr_sensor_data *sdata; + + sdata = &cdata->sensors[LSM303AGR_MAG_SENSOR]; + + input_event(sdata->input_dev, INPUT_EVENT_TYPE, INPUT_EVENT_X, xyz[0]); + input_event(sdata->input_dev, INPUT_EVENT_TYPE, INPUT_EVENT_Y, xyz[1]); + input_event(sdata->input_dev, INPUT_EVENT_TYPE, INPUT_EVENT_Z, xyz[2]); + input_event(sdata->input_dev, INPUT_EVENT_TYPE, INPUT_EVENT_TIME_MSB, + timestamp >> 32); + input_event(sdata->input_dev, INPUT_EVENT_TYPE, INPUT_EVENT_TIME_LSB, + timestamp & 0xffffffff); + input_sync(sdata->input_dev); +} + +static int lsm303agr_mag_get_data(struct lsm303agr_common_data *cdata, + int *xyz) +{ + int err; + /* Data bytes from hardware xL, xH, yL, yH, zL, zH */ + u8 mag_data[6]; + + err = cdata->tf->read(cdata->dev, AXISDATA_REG, 6, mag_data); + if (err < 0) + return err; + + /* Transform LSBs into ug */ + xyz[0] = (s32)((s16)(mag_data[0] | (mag_data[1] << 8))); + xyz[0] *= LSM303AGR_MAG_SENSITIVITY; + xyz[1] = (s32)((s16)(mag_data[2] | (mag_data[3] << 8))); + xyz[1] *= LSM303AGR_MAG_SENSITIVITY; + xyz[2] = (s32)((s16)(mag_data[4] | (mag_data[5] << 8))); + xyz[2] *= LSM303AGR_MAG_SENSITIVITY; + +#ifdef LSM303AGR_MAG_DEBUG + dev_info(cdata->dev, "%s read x=%d, y=%d, z=%d\n", + LSM303AGR_MAG_DEV_NAME, xyz[0], xyz[1], xyz[2]); +#endif + + return err; +} + +static void lsm303agr_mag_input_work_func(struct work_struct *work) +{ + struct lsm303agr_common_data *cdata; + struct lsm303agr_sensor_data *sdata; + int err, xyz[3] = {}; + + sdata = container_of((struct delayed_work *)work, + struct lsm303agr_sensor_data, input_work); + cdata = sdata->cdata; + + mutex_lock(&cdata->lock); + sdata->schedule_num++; + err = lsm303agr_mag_get_data(cdata, xyz); + if (err < 0) + dev_err(cdata->dev, "get_mag_data failed\n"); + else + lsm303agr_mag_report_event(cdata, xyz, + lsm303agr_get_time_ns()); + + schedule_delayed_work(&sdata->input_work, + msecs_to_jiffies(sdata->poll_interval)); + mutex_unlock(&cdata->lock); +} + +static void lsm303agr_mag_input_cleanup(struct lsm303agr_common_data *cdata) +{ + struct lsm303agr_sensor_data *sdata; + + sdata = &cdata->sensors[LSM303AGR_MAG_SENSOR]; + input_unregister_device(sdata->input_dev); + input_free_device(sdata->input_dev); +} + +/* SYSFS: set val to polling_ms ATTR */ +static ssize_t attr_get_polling_rate_mag(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + u32 val = 0; + struct lsm303agr_sensor_data *sdata; + struct lsm303agr_common_data *cdata = dev_get_drvdata(dev); + + sdata = &cdata->sensors[LSM303AGR_MAG_SENSOR]; + + /* read from platform data */ + mutex_lock(&cdata->lock); + val = sdata->poll_interval; + mutex_unlock(&cdata->lock); + + return sprintf(buf, "%d\n", val); +} + +static ssize_t attr_set_polling_rate_mag(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + unsigned long val = 0; + struct lsm303agr_common_data *cdata = dev_get_drvdata(dev); + + if (kstrtoul(buf, 10, &val)) + return -EINVAL; + + if (!val) + return -EINVAL; + + mutex_lock(&cdata->lock); + /* set ODR */ + val = lsm303agr_mag_set_odr(cdata, val); + if (val < 0) { + mutex_unlock(&cdata->lock); + return -1; + } + + /* write to platform data */ + cdata->sensors[LSM303AGR_MAG_SENSOR].poll_interval = val; + + mutex_unlock(&cdata->lock); + + return size; +} + +/* SYSFS: set val to enable_device ATTR */ +static ssize_t attr_get_enable_mag(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct lsm303agr_common_data *cdata = dev_get_drvdata(dev); + int val = atomic_read(&cdata->enabled); + + return sprintf(buf, "%d\n", val); +} + +/* SYSFS: get val from enable_device ATTR */ +static ssize_t attr_set_enable_mag(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + unsigned long val; + struct lsm303agr_common_data *cdata = dev_get_drvdata(dev); + + if (kstrtoul(buf, 10, &val)) + return -EINVAL; + + if (val) + lsm303agr_mag_enable(cdata); + else + lsm303agr_mag_disable(cdata); + + return size; +} + +static struct device_attribute lsm303agr_mag_attributes[] = { + __ATTR(pollrate_ms, 0664, attr_get_polling_rate_mag, + attr_set_polling_rate_mag), + __ATTR(enable_device, 0664, attr_get_enable_mag, attr_set_enable_mag), +}; + +static int create_sysfs_interfaces(struct device *dev) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(lsm303agr_mag_attributes); i++) + if (device_create_file(dev, lsm303agr_mag_attributes + i)) + goto error; + return 0; + +error: + for (; i >= 0; i--) + device_remove_file(dev, lsm303agr_mag_attributes + i); + + dev_err(dev, "%s:Unable to create interface\n", __func__); + + return -1; +} + +static int remove_sysfs_interfaces(struct device *dev) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(lsm303agr_mag_attributes); i++) + device_remove_file(dev, lsm303agr_mag_attributes + i); + + return 0; +} + +int lsm303agr_mag_probe(struct lsm303agr_common_data *cdata) +{ + int err; + struct lsm303agr_sensor_data *sdata; + + mutex_lock(&cdata->lock); + /* init sensor data structure */ + sdata = &cdata->sensors[LSM303AGR_MAG_SENSOR]; + sdata->cdata = cdata; + + /* Check WHO_AM_I */ + err = lsm303agr_mag_check_wai(cdata); + if (err < 0) { + dev_err(cdata->dev, "check WAI failed\n"); + mutex_unlock(&cdata->lock); + return err; + } + + /* Set device ODR to 100ms (10Hz) */ + err = lsm303agr_mag_set_odr(cdata, 100); + if (err < 0) { + dev_err(cdata->dev, "Set ODR On failed\n"); + mutex_unlock(&cdata->lock); + return err; + } + + /* Disable Magnetometer to save power */ + mutex_unlock(&cdata->lock); + err = lsm303agr_mag_disable(cdata); + if (err < 0) { + dev_err(cdata->dev, "Power On failed\n"); + mutex_unlock(&cdata->lock); + return err; + } + + mutex_lock(&cdata->lock); + /* Init the input framework */ + err = lsm303agr_mag_input_init(sdata, LSM303AGR_MAG_DEV_NAME); + if (err < 0) { + dev_err(cdata->dev, "input init failed\n"); + mutex_unlock(&cdata->lock); + return err; + } + INIT_DELAYED_WORK(&sdata->input_work, lsm303agr_mag_input_work_func); + + /* Create SYSFS interface */ + err = create_sysfs_interfaces(cdata->dev); + if (err < 0) { + dev_err(cdata->dev, + "device LSM303AGR_MAG_DEV_NAME sysfs register failed\n"); + mutex_unlock(&cdata->lock); + return err; + } + + dev_info(cdata->dev, "%s: probed\n", LSM303AGR_MAG_DEV_NAME); + mutex_unlock(&cdata->lock); + + return 0; +} +EXPORT_SYMBOL(lsm303agr_mag_probe); + +void lsm303agr_mag_remove(struct lsm303agr_common_data *cdata) +{ + lsm303agr_mag_disable(cdata); + lsm303agr_mag_input_cleanup(cdata); + remove_sysfs_interfaces(cdata->dev); +} +EXPORT_SYMBOL(lsm303agr_mag_remove); + +MODULE_LICENSE("GPL v2"); diff --git a/feeds/qca-wifi-7/ipq53xx/files-6.1/drivers/input/misc/lsm303agr/lsm303agr_mag_i2c.c b/feeds/qca-wifi-7/ipq53xx/files-6.1/drivers/input/misc/lsm303agr/lsm303agr_mag_i2c.c new file mode 100644 index 000000000..0019b22a7 --- /dev/null +++ b/feeds/qca-wifi-7/ipq53xx/files-6.1/drivers/input/misc/lsm303agr/lsm303agr_mag_i2c.c @@ -0,0 +1,210 @@ +/* + * STMicroelectronics lsm303agr_mag_i2c.c driver + * + * Copyright 2016 STMicroelectronics Inc. + * + * Giuseppe Barba + * + * Licensed under the GPL-2. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "lsm303agr_core.h" + +#define I2C_AUTO_INCREMENT 0x80 + +/* XXX: caller must hold cdata->lock */ +static int lsm303agr_mag_i2c_read(struct device *dev, u8 reg_addr, int len, + u8 *data) +{ + struct i2c_msg msg[2]; + struct i2c_client *client = to_i2c_client(dev); + + if (len > 1) + reg_addr |= I2C_AUTO_INCREMENT; + + msg[0].addr = client->addr; + msg[0].flags = client->flags; + msg[0].len = 1; + msg[0].buf = ®_addr; + + msg[1].addr = client->addr; + msg[1].flags = client->flags | I2C_M_RD; + msg[1].len = len; + msg[1].buf = data; + + return i2c_transfer(client->adapter, msg, 2); +} + +/* XXX: caller must hold cdata->lock */ +static int lsm303agr_mag_i2c_write(struct device *dev, u8 reg_addr, int len, + u8 *data) +{ + u8 send[len + 1]; + struct i2c_msg msg; + struct i2c_client *client = to_i2c_client(dev); + + if (len > 1) + reg_addr |= I2C_AUTO_INCREMENT; + + send[0] = reg_addr; + memcpy(&send[1], data, len * sizeof(u8)); + len++; + + msg.addr = client->addr; + msg.flags = client->flags; + msg.len = len; + msg.buf = send; + + return i2c_transfer(client->adapter, &msg, 1); +} + +/* I2C IO routines */ +static const struct lsm303agr_transfer_function lsm303agr_mag_i2c_tf = { + .write = lsm303agr_mag_i2c_write, + .read = lsm303agr_mag_i2c_read, +}; + +#ifdef CONFIG_PM_SLEEP +static int lsm303agr_mag_resume(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct lsm303agr_common_data *cdata = i2c_get_clientdata(client); + + if (cdata->on_before_suspend) + return lsm303agr_mag_enable(cdata); + return 0; +} + +static int lsm303agr_mag_suspend(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct lsm303agr_common_data *cdata = i2c_get_clientdata(client); + + cdata->on_before_suspend = atomic_read(&cdata->enabled); + return lsm303agr_mag_disable(cdata); +} + +static SIMPLE_DEV_PM_OPS(lsm303agr_mag_pm_ops, + lsm303agr_mag_suspend, + lsm303agr_mag_resume); + +#define LSM303AGR_MAG_PM_OPS (&lsm303agr_mag_pm_ops) +#else /* CONFIG_PM_SLEEP */ +#define LSM303AGR_MAG_PM_OPS NULL +#endif /* CONFIG_PM_SLEEP */ + +static int lsm303agr_mag_i2c_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + int err; + struct lsm303agr_common_data *cdata; + + dev_info(&client->dev, "probe start.\n"); + + /* Alloc Common data structure */ + cdata = kzalloc(sizeof(struct lsm303agr_common_data), GFP_KERNEL); + if (cdata == NULL) { + dev_err(&client->dev, + "failed to allocate memory for module data\n"); + mutex_unlock(&cdata->lock); + + return -ENOMEM; + } + + cdata->sensor_num = LSM303AGR_MAX_SENSORS_NUM; + cdata->dev = &client->dev; + cdata->name = client->name; + cdata->bus_type = BUS_I2C; + cdata->tf = &lsm303agr_mag_i2c_tf; + + i2c_set_clientdata(client, cdata); + + mutex_init(&cdata->lock); + + err = lsm303agr_mag_probe(cdata); + if (err < 0) { + kfree(cdata); + + return err; + } + + return 0; +} + +#if KERNEL_VERSION(6, 1, 0) <= LINUX_VERSION_CODE +static void lsm303agr_mag_i2c_remove(struct i2c_client *client) +{ + struct lsm303agr_common_data *cdata = i2c_get_clientdata(client); + + dev_info(cdata->dev, "driver removing\n"); + + lsm303agr_mag_remove(cdata); + kfree(cdata); +} +#else +static int lsm303agr_mag_i2c_remove(struct i2c_client *client) +{ + struct lsm303agr_common_data *cdata = i2c_get_clientdata(client); + + dev_info(cdata->dev, "driver removing\n"); + + lsm303agr_mag_remove(cdata); + kfree(cdata); + + return 0; +} +#endif + +static const struct i2c_device_id lsm303agr_mag_i2c_id[] = { + { "lsm303agr_mag", 0 }, + { }, +}; +MODULE_DEVICE_TABLE(i2c, lsm303agr_mag_i2c_id); + +#ifdef CONFIG_OF +static const struct of_device_id lsm303agr_mag_i2c_id_table[] = { + {.compatible = "st,lsm303agr_mag", }, + { }, +}; +MODULE_DEVICE_TABLE(of, lsm303agr_mag_i2c_id_table); +#endif /* CONFIG_OF */ + +static struct i2c_driver lsm303agr_mag_i2c_driver = { + .driver = { + .owner = THIS_MODULE, + .name = "lsm303agr_mag", + .pm = LSM303AGR_MAG_PM_OPS, +#ifdef CONFIG_OF + .of_match_table = lsm303agr_mag_i2c_id_table, +#endif /* CONFIG_OF */ + }, + .probe = lsm303agr_mag_i2c_probe, + .remove = lsm303agr_mag_i2c_remove, + .id_table = lsm303agr_mag_i2c_id, +}; + +module_i2c_driver(lsm303agr_mag_i2c_driver); + +MODULE_DESCRIPTION("lsm303agr magnetometer i2c driver"); +MODULE_AUTHOR("Armando Visconti"); +MODULE_AUTHOR("Matteo Dameno"); +MODULE_AUTHOR("Denis Ciocca"); +MODULE_AUTHOR("STMicroelectronics"); +MODULE_LICENSE("GPL v2"); + diff --git a/feeds/qca-wifi-7/ipq53xx/files-6.1/drivers/net/phy/rtl8221d.c b/feeds/qca-wifi-7/ipq53xx/files-6.1/drivers/net/phy/rtl8221d.c new file mode 100644 index 000000000..c98d6337d --- /dev/null +++ b/feeds/qca-wifi-7/ipq53xx/files-6.1/drivers/net/phy/rtl8221d.c @@ -0,0 +1,228 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Driver for Rtl PHY + * + * Author: Huang Yunxiang + * + * Copyright 2024 cig, Inc. + */ + +#include +#include +#include +#include +#include + +#define PHY_ID_RTL8221D 0x001CC841 +#define BIT_0 0x0001 +#define BIT_1 0x0002 +#define BIT_2 0x0004 +#define BIT_3 0x0008 +#define BIT_4 0x0010 +#define BIT_5 0x0020 +#define BIT_6 0x0040 +#define BIT_7 0x0080 +#define BIT_8 0x0100 +#define BIT_9 0x0200 +#define BIT_10 0x0400 +#define BIT_11 0x0800 +#define BIT_12 0x1000 +#define BIT_13 0x2000 +#define BIT_14 0x4000 +#define BIT_15 0x8000 + + +static int rtl8221d_config_aneg(struct phy_device *phydev) +{ + return 0; +} + + +static u32 Rtl8226b_is_link(struct phy_device *phydev) +{ + int phydata = 0; + + int i = 0; + + // must read twice + for(i=0;i<2;i++) + { + phydata = phy_read_mmd(phydev, MDIO_MMD_VEND2, 0xA402); + } + + phydev->link = (phydata & BIT_2) ? (1) : (0); + return 0; + +} + +static int rtl8221d_read_status(struct phy_device *phydev) +{ + int phydata, speed_grp, speed; + + Rtl8226b_is_link(phydev); + if (phydev->link) + { + phydata = phy_read_mmd(phydev, MDIO_MMD_VEND2, 0xA434); + speed_grp = (phydata & (BIT_9 | BIT_10)) >> 9; + speed = (phydata & (BIT_4 | BIT_5)) >> 4; + switch(speed_grp) + { + case 0: + { + switch(speed) + { + case 1: + phydev->speed = SPEED_100; + break; + case 2: + phydev->speed = SPEED_1000; + break; + default: + phydev->speed = SPEED_10; + break; + } + break; + } + case 1: + { + switch(speed) + { + case 1: + phydev->speed = SPEED_2500; + break; + case 3: + phydev->speed = SPEED_1000; // 2.5G lite + break; + default: + phydev->speed = SPEED_10; + break; + } + break; + } + default: + phydev->speed = SPEED_10; + break; + } + } + else + { + phydev->speed = SPEED_10; + } + + phydata = phy_read_mmd(phydev, MDIO_MMD_VEND2, 0xA434); + phydev->duplex = (phydata & BIT_3) ? (DUPLEX_FULL) : (DUPLEX_HALF); + + return 0; +} + +static int rtl8221d_config_init(struct phy_device *phydev) +{ + int err; + int phydata; + u16 timeoutms = 100; + + err = phy_write_mmd(phydev, MDIO_MMD_VEND1, 0x7588, 0x2); + if (err < 0) + return err; + err = phy_write_mmd(phydev, MDIO_MMD_VEND1, 0x7589, 0x71d0); + if (err < 0) + return err; + err = phy_write_mmd(phydev, MDIO_MMD_VEND1, 0x7587, 0x3); + if (err < 0) + return err; + while(--timeoutms) + { + phydata = phy_read_mmd(phydev, MDIO_MMD_VEND1, 0x7587); + if((phydata & 0x01) == 0) + break; + mdelay(10); + } + + phydata = phy_read_mmd(phydev, MDIO_MMD_VEND1, 0x75F3); + phydata &= ~BIT_0; + err = phy_write_mmd(phydev, MDIO_MMD_VEND1, 0x75F3, phydata); + if (err < 0) + return err; + phydata = phy_read_mmd(phydev, MDIO_MMD_VEND1, 0x697A); + phydata &= (~(BIT_0 | BIT_1 | BIT_2 | BIT_3 | BIT_4 | BIT_5)); + phydata |= 2; + phydata |=0x8000; + err = phy_write_mmd(phydev, MDIO_MMD_VEND1, 0x697A, phydata); + if (err < 0) + return err; + + phydata = phy_read_mmd(phydev, MDIO_MMD_VEND1, 0x697A); + + Rtl8226b_is_link(phydev); + if (phydev->link) + { + phydata = phy_read_mmd(phydev, MDIO_MMD_PMAPMD, 0x0); + phydata |= BIT_15; + err = phy_write_mmd(phydev, MDIO_MMD_PMAPMD, 0x0, phydata); + if (err < 0) + return err; + + while(--timeoutms) + { + phydata = phy_read_mmd(phydev, MDIO_MMD_PMAPMD, 0x0); + if (!(phydata & BIT_15)) + break; + mdelay(10); + } + err = phy_write_mmd(phydev, MDIO_MMD_PMAPMD, 0x0, phydata); + if (err < 0) + return err; + } + else + { + phydata = phy_read_mmd(phydev, MDIO_MMD_VEND2, 0xa400); + phydata |= BIT_14; + err = phy_write_mmd(phydev, MDIO_MMD_VEND2, 0xa400, phydata); + if (err < 0) + return err; + while(--timeoutms) + { + phydata = phy_read_mmd(phydev, MDIO_MMD_VEND2, 0xA434); + if (phydata & BIT_2) + break; + mdelay(10); + } + phydata = phy_read_mmd(phydev, MDIO_MMD_VEND2, 0xa400); + phydata &= ~BIT_14; + err = phy_write_mmd(phydev, MDIO_MMD_VEND2, 0xa400, phydata); + if (err < 0) + return err; + } + return 0; +} + +static int rtl8221d_probe(struct phy_device *phydev) +{ + printk("rtl8221d probe"); + return 0; +} + +static struct phy_driver aqr_driver[] = { +{ + PHY_ID_MATCH_MODEL(PHY_ID_RTL8221D), + .name = "Rtl 8221D", + .features = PHY_GBIT_FEATURES, + .probe = rtl8221d_probe, + .config_init = rtl8221d_config_init, + .config_aneg = rtl8221d_config_aneg, + .read_status = rtl8221d_read_status, +}, +}; + +module_phy_driver(aqr_driver); + +static struct mdio_device_id __maybe_unused rtl_tbl[] = { + { PHY_ID_MATCH_MODEL(PHY_ID_RTL8221D) }, + { } +}; + +MODULE_DEVICE_TABLE(mdio, rtl_tbl); + +MODULE_DESCRIPTION("rtl8221d PHY driver"); +MODULE_AUTHOR("haungyunxiang huangyunxiang@cigtech.com>"); +MODULE_LICENSE("GPL v2"); diff --git a/feeds/qca-wifi-7/ipq53xx/image/ipq53xx.mk b/feeds/qca-wifi-7/ipq53xx/image/ipq53xx.mk index aa0bfb83a..24b4609e8 100755 --- a/feeds/qca-wifi-7/ipq53xx/image/ipq53xx.mk +++ b/feeds/qca-wifi-7/ipq53xx/image/ipq53xx.mk @@ -134,3 +134,15 @@ define Device/zyxel_nwa130be DEVICE_PACKAGES := ath12k-wifi-zyxel-nwa130be ath12k-firmware-qcn92xx ath12k-firmware-ipq5332 endef TARGET_DEVICES += zyxel_nwa130be + +define Device/cig_wf672 + DEVICE_TITLE := CIG WF672 + DEVICE_DTS := ipq5332-cig-wf672 + DEVICE_DTS_DIR := ../dts + DEVICE_DTS_CONFIG := config@mi01.6 + IMAGES := sysupgrade.tar mmc-factory.bin + IMAGE/mmc-factory.bin := append-ubi | qsdk-ipq-factory-mmc + IMAGE/sysupgrade.tar := sysupgrade-tar | append-metadata + DEVICE_PACKAGES := ath12k-wifi-cig-wf672 ath12k-firmware-ipq5332 ath12k-firmware-qcn92xx +endef +TARGET_DEVICES += cig_wf672 diff --git a/feeds/qca-wifi-7/ipq53xx/patches-6.1/802-add-drivers-lsm303agr-rtl8221d-ilps22qs.patch b/feeds/qca-wifi-7/ipq53xx/patches-6.1/802-add-drivers-lsm303agr-rtl8221d-ilps22qs.patch new file mode 100644 index 000000000..4f248bfe2 --- /dev/null +++ b/feeds/qca-wifi-7/ipq53xx/patches-6.1/802-add-drivers-lsm303agr-rtl8221d-ilps22qs.patch @@ -0,0 +1,63 @@ +--- a/drivers/input/misc/Kconfig 2025-04-22 14:35:28.228629072 +0800 ++++ b/drivers/input/misc/Kconfig 2025-04-22 14:42:29.630935581 +0800 +@@ -929,4 +929,5 @@ + To compile this driver as a module, choose M here: the + module will be called stpmic1_onkey. + ++source "drivers/input/misc/lsm303agr/Kconfig" + endif +--- a/drivers/input/misc/Makefile 2025-04-22 14:35:28.228629072 +0800 ++++ b/drivers/input/misc/Makefile 2025-04-22 14:41:23.840267623 +0800 +@@ -89,3 +89,4 @@ + obj-$(CONFIG_INPUT_XEN_KBDDEV_FRONTEND) += xen-kbdfront.o + obj-$(CONFIG_INPUT_YEALINK) += yealink.o + obj-$(CONFIG_INPUT_IDEAPAD_SLIDEBAR) += ideapad_slidebar.o ++obj-$(CONFIG_INPUT_LSM303AGR) += lsm303agr/ +--- a/drivers/net/phy/Kconfig 2025-04-22 14:48:04.858191804 +0800 ++++ b/drivers/net/phy/Kconfig 2025-04-22 10:17:06.822705335 +0800 +@@ -99,6 +99,9 @@ + tristate "Lantiq XWAY Tantos (PSB6970) Ethernet switch" + select SWCONFIG + ++config RTL8221D_PHY ++ tristate "Driver for Realtek RTL8221D phy" ++ + config RTL8306_PHY + tristate "Driver for Realtek RTL8306S switches" + select SWCONFIG +--- a/drivers/net/phy/Makefile 2025-04-22 14:48:18.254372527 +0800 ++++ b/drivers/net/phy/Makefile 2025-04-22 10:17:10.546732447 +0800 +@@ -32,6 +32,7 @@ + obj-$(CONFIG_SWCONFIG_B53) += b53/ + obj-$(CONFIG_IP17XX_PHY) += ip17xx.o + obj-$(CONFIG_PSB6970_PHY) += psb6970.o ++obj-$(CONFIG_RTL8221D_PHY) += rtl8221d.o + obj-$(CONFIG_RTL8306_PHY) += rtl8306.o + obj-$(CONFIG_RTL8366_SMI) += rtl8366_smi.o + obj-$(CONFIG_RTL8366S_PHY) += rtl8366s.o +--- a/drivers/iio/pressure/Kconfig 2025-05-13 15:16:05.019840003 +0800 ++++ b/drivers/iio/pressure/Kconfig 2025-05-13 15:17:45.032040654 +0800 +@@ -266,4 +266,11 @@ + tristate + select REGMAP_SPI + ++config ILPS22QS ++ tristate "ILPS22QS pressure support" ++ help ++ Say Y to enable support ILPS22QS ++ ++ To compile this driver as a module, choose M here: the ++ module will be called ILPS22QS. + endmenu + +--- a/drivers/iio/pressure/Makefile 2025-05-13 15:17:58.049195788 +0800 ++++ b/drivers/iio/pressure/Makefile 2025-05-13 15:19:07.205016058 +0800 +@@ -31,6 +31,8 @@ + obj-$(CONFIG_ZPA2326) += zpa2326.o + obj-$(CONFIG_ZPA2326_I2C) += zpa2326_i2c.o + obj-$(CONFIG_ZPA2326_SPI) += zpa2326_spi.o ++obj-$(CONFIG_ILPS22QS) += st_ilps22qs.o ++st_ilps22qs-objs := st_ilps22qs_i2c.o st_ilps22qs_core.o + + obj-$(CONFIG_IIO_ST_PRESS_I2C) += st_pressure_i2c.o + obj-$(CONFIG_IIO_ST_PRESS_SPI) += st_pressure_spi.o diff --git a/feeds/qca-wifi-7/platform_drivers/Makefile b/feeds/qca-wifi-7/platform_drivers/Makefile index 18e73ddbb..e5ccb0db4 100644 --- a/feeds/qca-wifi-7/platform_drivers/Makefile +++ b/feeds/qca-wifi-7/platform_drivers/Makefile @@ -702,3 +702,44 @@ define KernelPackage/llcc_perfmon/description endef $(eval $(call KernelPackage,llcc_perfmon)) + +define KernelPackage/input-lsm303agr + SUBMENU:=$(OTHER_MENU) + TITLE:=LSM303AGR Driver + DEPENDS+=@TARGET_ipq53xx + KCONFIG:=CONFIG_INPUT_LSM303AGR=y +endef + +define KernelPackage/input-lsm303agr/description + lsm303agr driver support +endef + +$(eval $(call KernelPackage,input-lsm303agr)) + +define KernelPackage/rtl8221d-phy + SUBMENU:=$(OTHER_MENU) + TITLE:=RTL8221d PHY Driver + DEPENDS+=@TARGET_ipq53xx + KCONFIG:=CONFIG_RTL8221D_PHY=y +endef + +define KernelPackage/rtl8221d-phy/description + RTL8221d PHY driver support +endef + +$(eval $(call KernelPackage,rtl8221d-phy)) + +define KernelPackage/iio-ilps22qs + SUBMENU:=$(IIO_MENU) + TITLE:= pressure sensors ilps22qs Driver + DEPENDS+=@TARGET_ipq53xx +kmod-iio-core +kmod-industrialio-triggered-buffer + KCONFIG:=CONFIG_ILPS22QS + FILES:=$(LINUX_DIR)/drivers/iio/pressure/st_ilps22qs.ko + AUTOLOAD:=$(call AutoLoad,80,st_ilps22qs) +endef + +define KernelPackage/iio-ilps22qs/description + pressure sensors ilps22qs driver support +endef + +$(eval $(call KernelPackage,iio-ilps22qs)) diff --git a/feeds/tip/certificates/files/usr/bin/mount_certs b/feeds/tip/certificates/files/usr/bin/mount_certs index 9cf0ec8d3..378ded061 100755 --- a/feeds/tip/certificates/files/usr/bin/mount_certs +++ b/feeds/tip/certificates/files/usr/bin/mount_certs @@ -25,6 +25,10 @@ cig,wf660a) mmc_dev=$(echo $(find_mmc_part "0:ETHPHYFW") | sed 's/^.\{5\}//') [ -n "$mmc_dev" ] && mount -t ext4 /dev/$mmc_dev /certificates ;; +cig,wf672) + mmc_dev=$(echo $(find_mmc_part "cert") | sed 's/^.\{5\}//') + [ -n "$mmc_dev" ] && mount -t ext4 /dev/$mmc_dev /certificates + ;; sonicfi,rap7110c-341x) mmc_dev=$(echo $(find_mmc_part "certificates") | sed 's/^.\{5\}//') [ -n "$mmc_dev" ] && mount -t squashfs /dev/$mmc_dev /certificates diff --git a/profiles/cig_wf672.yml b/profiles/cig_wf672.yml new file mode 100644 index 000000000..d34183c06 --- /dev/null +++ b/profiles/cig_wf672.yml @@ -0,0 +1,23 @@ +--- +profile: cig_wf672 +target: ipq53xx +subtarget: generic +description: Build image for the CIG WF672 +image: bin/targets/ipq53xx/generic/openwrt-ipq53xx-cig_wf672-squashfs-sysupgrade.tar +feeds: + - name: qca + path: ../../feeds/qca-wifi-7 +include: + - ucentral-ap +packages: + - ipq53xx + - ftm + - qca-ssdk-shell + - iperf3 + - sysstat + - kmod-cig-wifi-mode-sw + - kmod-input-lsm303agr + - kmod-rtl8221d-phy + - kmod-gpio-pca953x + - kmod-hwmon-tmp103 + - kmod-iio-ilps22qs