Compare commits

...

2 Commits

Author SHA1 Message Date
Kenshi0000
35e7c12b2e Merge branch 'main' into WIFI-14509-CIG-WiFi7-WF-672A-bring-up 2025-05-21 09:54:13 +08:00
Justin.Guo
590f97e254 WIFI-14509 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

Signed-off-by: Justin.Guo <guoxijun@actiontec.com>
2025-05-14 15:12:52 +08:00
27 changed files with 4508 additions and 8 deletions

View File

@@ -78,6 +78,11 @@ $(call Package/ath12k-wifi-default)
TITLE:=board-2.bin for AP72TIP-v4
endef
define Package/ath12k-wifi-cig-wf672
$(call Package/ath12k-wifi-default)
TITLE:=board-2.bin for WF672
endef
define Package/ath12k-wifi-zyxel-nwa130be
$(call Package/ath12k-wifi-default)
TITLE:=board-2.bin for NWA130BE
@@ -153,6 +158,14 @@ define Package/ath12k-wifi-sercomm-ap72tip-v4/install
$(INSTALL_DATA) ./board-2.bin.ap72tip-v4.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
define Package/ath12k-wifi-zyxel-nwa130be/install
$(INSTALL_DIR) $(1)/lib/firmware/ath12k/QCN92XX/hw1.0/
$(INSTALL_DIR) $(1)/lib/firmware/ath12k/IPQ5332/hw1.0/
@@ -169,4 +182,5 @@ $(eval $(call BuildPackage,ath12k-wifi-cig-wf189w))
$(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-cig-wf672))
$(eval $(call BuildPackage,ath12k-wifi-zyxel-nwa130be))

Binary file not shown.

Binary file not shown.

View File

@@ -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))

View File

@@ -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

View File

@@ -0,0 +1 @@
obj-m += cig_rf_switch.o

View File

@@ -0,0 +1,276 @@
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/mmc/core.h>
#include <linux/mmc/host.h>
#include <linux/major.h>
#include <linux/mmc/mmc.h>
#include <linux/mmc/card.h>
#include <linux/slab.h>
#include <linux/string.h>
#include <linux/uaccess.h>
#include <linux/list.h>
#include <linux/pagemap.h>
#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");

View File

@@ -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)

View File

@@ -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
;;

View File

@@ -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"

View File

@@ -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 <dt-bindings/gpio/gpio.h>
#include <dt-bindings/input/input.h>
#include <dt-bindings/leds/common.h>
#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: <Rx-fill Tx-cmpl Rx Tx Queue-base Queue-count> */
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 = <KEY_RESTART>;
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";
};

View File

@@ -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 <linux/bitfield.h>
#include <linux/iio/iio.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/of.h>
#include <linux/regmap.h>
#include <linux/types.h>
#include <linux/workqueue.h>
#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 */

View File

@@ -0,0 +1,927 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* STMicroelectronics ilps22qs driver
*
* Copyright 2023 STMicroelectronics Inc.
*
* MEMS Software Solutions Team
*/
#include <linux/delay.h>
#include <linux/kernel.h>
#include <linux/iio/buffer.h>
#include <linux/iio/events.h>
#include <linux/iio/iio.h>
#include <linux/iio/kfifo_buf.h>
#include <linux/iio/sysfs.h>
#include <linux/iio/trigger_consumer.h>
#include <linux/iio/triggered_buffer.h>
#include <linux/iio/trigger.h>
#include <linux/regulator/consumer.h>
#include <linux/slab.h>
#include <linux/types.h>
#include <linux/version.h>
#if KERNEL_VERSION(6, 11, 0) < LINUX_VERSION_CODE
#include <linux/unaligned.h>
#else /* LINUX_VERSION_CODE */
#include <asm/unaligned.h>
#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");

View File

@@ -0,0 +1,86 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* STMicroelectronics ilps22qs i2c driver
*
* Copyright 2023 STMicroelectronics Inc.
*
* MEMS Software Solutions Team
*/
#include <linux/kernel.h>
#include <linux/i2c.h>
#include <linux/iio/iio.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/version.h>
#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");

View File

@@ -0,0 +1,6 @@
config INPUT_LSM303AGR
tristate "STM LSM303AGR sensor"
depends on I2C && SYSFS
help
This driver support the STMicroelectronics LSM303AGR sensor.

View File

@@ -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

View File

@@ -0,0 +1,885 @@
/*
* STMicroelectronics lsm303agr_acc.c driver
*
* Copyright 2016 STMicroelectronics Inc.
*
* Giuseppe Barba <giuseppe.barba@st.com>
*
* Licensed under the GPL-2.
*/
#include <linux/err.h>
#include <linux/errno.h>
#include <linux/delay.h>
#include <linux/fs.h>
#include <linux/i2c.h>
#include <linux/input.h>
#include <linux/uaccess.h>
#include <linux/workqueue.h>
#include <linux/irq.h>
#include <linux/gpio.h>
#include <linux/interrupt.h>
#include <linux/kernel.h>
#include <linux/device.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/version.h>
#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");

View File

@@ -0,0 +1,207 @@
/*
* STMicroelectronics lsm303agr_acc_i2c.c driver
*
* Copyright 2016 STMicroelectronics Inc.
*
* Giuseppe Barba <giuseppe.barba@st.com>
*
* Licensed under the GPL-2.
*/
#include <linux/err.h>
#include <linux/errno.h>
#include <linux/slab.h>
#include <linux/delay.h>
#include <linux/i2c.h>
#include <linux/input.h>
#include <linux/uaccess.h>
#include <linux/workqueue.h>
#include <linux/irq.h>
#include <linux/interrupt.h>
#include <linux/kernel.h>
#include <linux/device.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/version.h>
#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 = &reg_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");

View File

@@ -0,0 +1,104 @@
/*
* STMicroelectronics lsm303agr driver
*
* Copyright 2016 STMicroelectronics Inc.
*
* Giuseppe Barba <giuseppe.barba@st.com>
*
* 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__ */

View File

@@ -0,0 +1,556 @@
/*
* STMicroelectronics lsm303agr_mag.c driver
*
* Copyright 2016 STMicroelectronics Inc.
*
* Giuseppe Barba <giuseppe.barba@st.com>
*
* Licensed under the GPL-2.
*/
#include <linux/err.h>
#include <linux/errno.h>
#include <linux/delay.h>
#include <linux/fs.h>
#include <linux/i2c.h>
#include <linux/input.h>
#include <linux/uaccess.h>
#include <linux/workqueue.h>
#include <linux/irq.h>
#include <linux/gpio.h>
#include <linux/interrupt.h>
#include <linux/slab.h>
#include <linux/kernel.h>
#include <linux/device.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/version.h>
#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");

View File

@@ -0,0 +1,210 @@
/*
* STMicroelectronics lsm303agr_mag_i2c.c driver
*
* Copyright 2016 STMicroelectronics Inc.
*
* Giuseppe Barba <giuseppe.barba@st.com>
*
* Licensed under the GPL-2.
*/
#include <linux/err.h>
#include <linux/errno.h>
#include <linux/slab.h>
#include <linux/delay.h>
#include <linux/i2c.h>
#include <linux/input.h>
#include <linux/uaccess.h>
#include <linux/workqueue.h>
#include <linux/irq.h>
#include <linux/interrupt.h>
#include <linux/kernel.h>
#include <linux/device.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/version.h>
#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 = &reg_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");

View File

@@ -0,0 +1,228 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Driver for Rtl PHY
*
* Author: Huang Yunxiang <huangyunxiang@cigtech.com>
*
* Copyright 2024 cig, Inc.
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/delay.h>
#include <linux/bitfield.h>
#include <linux/phy.h>
#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");

View File

@@ -120,6 +120,18 @@ define Device/cig_wf189h
endef
TARGET_DEVICES += cig_wf189h
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
define Device/zyxel_nwa130be
DEVICE_TITLE := Zyxel NWA130BE
DEVICE_DTS := ipq5332-zyxel-nwa130be
@@ -134,3 +146,4 @@ define Device/zyxel_nwa130be
DEVICE_PACKAGES := ath12k-wifi-zyxel-nwa130be ath12k-firmware-qcn92xx ath12k-firmware-ipq5332
endef
TARGET_DEVICES += zyxel_nwa130be

View File

@@ -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

View File

@@ -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))

View File

@@ -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

23
profiles/cig_wf672.yml Normal file
View File

@@ -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