From a9f47c9e1e8ad158ce99f10344d630e101e63689 Mon Sep 17 00:00:00 2001 From: "Justin.Guo" Date: Mon, 12 May 2025 14:06:38 +0800 Subject: [PATCH] qca-wifi-7: CIG WiFi7 WF-672A bring up * bring up wf672a * add drivers lsm303agr rtl8221d ilps22qs * add cig-wifi-mode-sw for switching radio to 2 bands or 3 bands Fixes: WIFI-14509 Signed-off-by: Justin.Guo --- feeds/qca-wifi-7/ath12k-wifi/Makefile | 13 + .../ath12k-wifi/board-2.bin.wf672.IPQ5332 | Bin 0 -> 661260 bytes .../ath12k-wifi/board-2.bin.wf672.QCN92XX | Bin 0 -> 1470400 bytes feeds/qca-wifi-7/cig-wifi-mode-sw/Makefile | 30 + .../cig-wifi-mode-sw/files/cig_wifi_mode_sw | 11 + .../qca-wifi-7/cig-wifi-mode-sw/src/Makefile | 1 + .../cig-wifi-mode-sw/src/cig_rf_switch.c | 276 ++++++ .../ipq53xx/base-files/etc/board.d/02_network | 31 +- .../etc/hotplug.d/firmware/10-ath12k-caldata | 39 + .../base-files/lib/upgrade/platform.sh | 49 + .../ipq53xx/dts/ipq5332-cig-wf672.dts | 548 +++++++++++ .../drivers/iio/pressure/st_ilps22qs.h | 157 +++ .../drivers/iio/pressure/st_ilps22qs_core.c | 927 ++++++++++++++++++ .../drivers/iio/pressure/st_ilps22qs_i2c.c | 86 ++ .../drivers/input/misc/lsm303agr/Kconfig | 6 + .../drivers/input/misc/lsm303agr/Makefile | 7 + .../input/misc/lsm303agr/lsm303agr_acc.c | 885 +++++++++++++++++ .../input/misc/lsm303agr/lsm303agr_acc_i2c.c | 207 ++++ .../input/misc/lsm303agr/lsm303agr_core.h | 104 ++ .../input/misc/lsm303agr/lsm303agr_mag.c | 556 +++++++++++ .../input/misc/lsm303agr/lsm303agr_mag_i2c.c | 210 ++++ .../files-6.1/drivers/net/phy/rtl8221d.c | 228 +++++ feeds/qca-wifi-7/ipq53xx/image/ipq53xx.mk | 12 + ...-drivers-lsm303agr-rtl8221d-ilps22qs.patch | 63 ++ feeds/qca-wifi-7/platform_drivers/Makefile | 41 + .../certificates/files/usr/bin/mount_certs | 4 + profiles/cig_wf672.yml | 23 + 27 files changed, 4506 insertions(+), 8 deletions(-) create mode 100644 feeds/qca-wifi-7/ath12k-wifi/board-2.bin.wf672.IPQ5332 create mode 100644 feeds/qca-wifi-7/ath12k-wifi/board-2.bin.wf672.QCN92XX create mode 100644 feeds/qca-wifi-7/cig-wifi-mode-sw/Makefile create mode 100755 feeds/qca-wifi-7/cig-wifi-mode-sw/files/cig_wifi_mode_sw create mode 100644 feeds/qca-wifi-7/cig-wifi-mode-sw/src/Makefile create mode 100644 feeds/qca-wifi-7/cig-wifi-mode-sw/src/cig_rf_switch.c create mode 100644 feeds/qca-wifi-7/ipq53xx/dts/ipq5332-cig-wf672.dts create mode 100644 feeds/qca-wifi-7/ipq53xx/files-6.1/drivers/iio/pressure/st_ilps22qs.h create mode 100644 feeds/qca-wifi-7/ipq53xx/files-6.1/drivers/iio/pressure/st_ilps22qs_core.c create mode 100644 feeds/qca-wifi-7/ipq53xx/files-6.1/drivers/iio/pressure/st_ilps22qs_i2c.c create mode 100644 feeds/qca-wifi-7/ipq53xx/files-6.1/drivers/input/misc/lsm303agr/Kconfig create mode 100644 feeds/qca-wifi-7/ipq53xx/files-6.1/drivers/input/misc/lsm303agr/Makefile create mode 100644 feeds/qca-wifi-7/ipq53xx/files-6.1/drivers/input/misc/lsm303agr/lsm303agr_acc.c create mode 100644 feeds/qca-wifi-7/ipq53xx/files-6.1/drivers/input/misc/lsm303agr/lsm303agr_acc_i2c.c create mode 100644 feeds/qca-wifi-7/ipq53xx/files-6.1/drivers/input/misc/lsm303agr/lsm303agr_core.h create mode 100644 feeds/qca-wifi-7/ipq53xx/files-6.1/drivers/input/misc/lsm303agr/lsm303agr_mag.c create mode 100644 feeds/qca-wifi-7/ipq53xx/files-6.1/drivers/input/misc/lsm303agr/lsm303agr_mag_i2c.c create mode 100644 feeds/qca-wifi-7/ipq53xx/files-6.1/drivers/net/phy/rtl8221d.c create mode 100644 feeds/qca-wifi-7/ipq53xx/patches-6.1/802-add-drivers-lsm303agr-rtl8221d-ilps22qs.patch create mode 100644 profiles/cig_wf672.yml 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 0000000000000000000000000000000000000000..cad03b80e1a9fdeea48983023e9e63fbcd8f9bc6 GIT binary patch literal 661260 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 literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..a5107d1e08229ff8b74c25016fa91931f5673c83 GIT binary patch literal 1470400 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 literal 0 HcmV?d00001 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