From a5f7a5bb3b7f28ba7e4fa246775b29a0e5537255 Mon Sep 17 00:00:00 2001 From: Jinke Han Date: Tue, 8 Sep 2026 15:37:42 +0800 Subject: x86/kprobes: Fix crash when probing CS CALL instructions When using eBPF to probe CS CALL instructions within a function, a crash can be triggered. The eBPF tool probes offset 257 of the __hrtimer_run_queues() function: <__hrtimer_run_queues+249>: nopl 0x0(%rax,%rax,1) <__hrtimer_run_queues+254>: mov %r14,%rdi <__hrtimer_run_queues+257>: cs call <__x86_indirect_thunk_r12> <__hrtimer_run_queues+263>: mov %eax,%r12d <__hrtimer_run_queues+266>: xchg %ax,%ax <__hrtimer_run_queues+268>: mov %r13,%rdi Which triggers this crash: BUG: unable to handle page fault for address: 00000000000f41c9 #PF: supervisor write access in kernel mode #PF: error_code(0x0002) - not-present page PGD 0 P4D 0 Oops: 0002 [#1] SMP NOPTI CPU: 1 PID: 0 Comm: swapper/1 Kdump: loaded Tainted: P RIP: 0010:__hrtimer_run_queues+0x106/0x230 Note that __hrtimer_run_queues+0x106 is __hrtimer_run_queues+262, which is at the 6th byte of the above CS CALL instruction. Since the CS CALL instruction occupies 6 bytes, the exception occurred in the middle of that call instruction. The root cause is that when using eBPF tools to probe in the middle of a function, a kprobe with INT3 is used as the underlying implementation. During single-step emulation of the original CALL instruction, int3_emulate_call() assumes that the probed CALL instruction is 5 bytes long. However, the actual CS-prefixed CALL instruction occupies 6 bytes, so it constructs an incorrect exception return address. When the CPU returns from the kprobe handler, the next instruction to be executed is at the address of the last byte of that CS CALL instruction. Coincidentally, starting from that address, the CPU fetches and decodes a completely different instruction, which ultimately triggers a kernel crash. Fix the issue by using the actual instruction length obtained from the instruction decoder when constructing the exception return address, rather than relying on the hardcoded CALL_INSN_SIZE macro. [ mingo: Refined the changelog ] Fixes: 6256e668b7af ("x86/kprobes: Use int3 instead of debug trap for single-step") Suggested-by: Masami Hiramatsu (Google) Signed-off-by: Jinke Han Signed-off-by: Ingo Molnar Reviewed-by: Masami Hiramatsu (Google) Acked-by: Yafang Shao Acked-by: Borislav Petkov Cc: Peter Zijlstra Link: https://patch.msgid.link/20260908073742.GA10517@didi-ThinkCentre-M920t-N000 --- drivers/iio/adc/88pm886-gpadc.c | 392 +++++ drivers/iio/adc/Kconfig | 2101 +++++++++++++++++++++++ drivers/iio/adc/Makefile | 181 ++ drivers/iio/adc/ab8500-gpadc.c | 1202 +++++++++++++ drivers/iio/adc/ad4000.c | 1263 ++++++++++++++ drivers/iio/adc/ad4030.c | 1802 ++++++++++++++++++++ drivers/iio/adc/ad4062.c | 1614 ++++++++++++++++++ drivers/iio/adc/ad4080.c | 918 ++++++++++ drivers/iio/adc/ad4130.c | 2450 +++++++++++++++++++++++++++ drivers/iio/adc/ad4134.c | 499 ++++++ drivers/iio/adc/ad4170-4.c | 3010 +++++++++++++++++++++++++++++++++ drivers/iio/adc/ad4691.c | 2083 +++++++++++++++++++++++ drivers/iio/adc/ad4695.c | 2019 ++++++++++++++++++++++ drivers/iio/adc/ad4851.c | 1316 ++++++++++++++ drivers/iio/adc/ad7091r-base.c | 397 +++++ drivers/iio/adc/ad7091r-base.h | 99 ++ drivers/iio/adc/ad7091r5.c | 132 ++ drivers/iio/adc/ad7091r8.c | 272 +++ drivers/iio/adc/ad7124.c | 1705 +++++++++++++++++++ drivers/iio/adc/ad7173.c | 2099 +++++++++++++++++++++++ drivers/iio/adc/ad7191.c | 556 ++++++ drivers/iio/adc/ad7192.c | 1473 ++++++++++++++++ drivers/iio/adc/ad7266.c | 473 ++++++ drivers/iio/adc/ad7280a.c | 1101 ++++++++++++ drivers/iio/adc/ad7291.c | 563 ++++++ drivers/iio/adc/ad7292.c | 325 ++++ drivers/iio/adc/ad7298.c | 373 ++++ drivers/iio/adc/ad7380.c | 2154 +++++++++++++++++++++++ drivers/iio/adc/ad7405.c | 252 +++ drivers/iio/adc/ad7476.c | 465 +++++ drivers/iio/adc/ad7606.c | 1712 +++++++++++++++++++ drivers/iio/adc/ad7606.h | 251 +++ drivers/iio/adc/ad7606_bus_iface.h | 16 + drivers/iio/adc/ad7606_par.c | 264 +++ drivers/iio/adc/ad7606_spi.c | 512 ++++++ drivers/iio/adc/ad7625.c | 686 ++++++++ drivers/iio/adc/ad7766.c | 309 ++++ drivers/iio/adc/ad7768-1.c | 1951 +++++++++++++++++++++ drivers/iio/adc/ad7779.c | 1045 ++++++++++++ drivers/iio/adc/ad7780.c | 370 ++++ drivers/iio/adc/ad7791.c | 482 ++++++ drivers/iio/adc/ad7793.c | 857 ++++++++++ drivers/iio/adc/ad7887.c | 347 ++++ drivers/iio/adc/ad7923.c | 399 +++++ drivers/iio/adc/ad7944.c | 889 ++++++++++ drivers/iio/adc/ad7949.c | 432 +++++ drivers/iio/adc/ad799x.c | 968 +++++++++++ drivers/iio/adc/ad9467.c | 1433 ++++++++++++++++ drivers/iio/adc/ad_sigma_delta.c | 950 +++++++++++ drivers/iio/adc/ade9000.c | 1794 ++++++++++++++++++++ drivers/iio/adc/adi-axi-adc.c | 842 +++++++++ drivers/iio/adc/aspeed_adc.c | 789 +++++++++ drivers/iio/adc/at91-sama5d2_adc.c | 2629 ++++++++++++++++++++++++++++ drivers/iio/adc/at91_adc.c | 1348 +++++++++++++++ drivers/iio/adc/axp20x_adc.c | 1192 +++++++++++++ drivers/iio/adc/axp288_adc.c | 319 ++++ drivers/iio/adc/bcm_iproc_adc.c | 622 +++++++ drivers/iio/adc/berlin2-adc.c | 368 ++++ drivers/iio/adc/cc10001_adc.c | 409 +++++ drivers/iio/adc/cpcap-adc.c | 1035 ++++++++++++ drivers/iio/adc/da9150-gpadc.c | 358 ++++ drivers/iio/adc/dln2-adc.c | 678 ++++++++ drivers/iio/adc/envelope-detector.c | 408 +++++ drivers/iio/adc/ep93xx_adc.c | 248 +++ drivers/iio/adc/exynos_adc.c | 721 ++++++++ drivers/iio/adc/fsl-imx25-gcq.c | 389 +++++ drivers/iio/adc/gehc-pmc-adc.c | 228 +++ drivers/iio/adc/hi8435.c | 546 ++++++ drivers/iio/adc/hx711.c | 629 +++++++ drivers/iio/adc/imx7d_adc.c | 560 ++++++ drivers/iio/adc/imx8qxp-adc.c | 496 ++++++ drivers/iio/adc/imx93_adc.c | 494 ++++++ drivers/iio/adc/ina2xx-adc.c | 1133 +++++++++++++ drivers/iio/adc/industrialio-adc.c | 82 + drivers/iio/adc/ingenic-adc.c | 925 ++++++++++ drivers/iio/adc/intel_dc_ti_adc.c | 327 ++++ drivers/iio/adc/intel_mrfld_adc.c | 241 +++ drivers/iio/adc/lp8788_adc.c | 219 +++ drivers/iio/adc/lpc18xx_adc.c | 205 +++ drivers/iio/adc/lpc32xx_adc.c | 235 +++ drivers/iio/adc/ltc2309.c | 264 +++ drivers/iio/adc/ltc2378.c | 857 ++++++++++ drivers/iio/adc/ltc2471.c | 157 ++ drivers/iio/adc/ltc2485.c | 143 ++ drivers/iio/adc/ltc2496.c | 112 ++ drivers/iio/adc/ltc2497-core.c | 244 +++ drivers/iio/adc/ltc2497.c | 170 ++ drivers/iio/adc/ltc2497.h | 26 + drivers/iio/adc/max1027.c | 635 +++++++ drivers/iio/adc/max11100.c | 162 ++ drivers/iio/adc/max1118.c | 286 ++++ drivers/iio/adc/max11205.c | 180 ++ drivers/iio/adc/max11410.c | 1046 ++++++++++++ drivers/iio/adc/max1241.c | 198 +++ drivers/iio/adc/max1363.c | 1691 ++++++++++++++++++ drivers/iio/adc/max14001.c | 390 +++++ drivers/iio/adc/max34408.c | 276 +++ drivers/iio/adc/max77541-adc.c | 193 +++ drivers/iio/adc/max9611.c | 562 ++++++ drivers/iio/adc/mcp320x.c | 508 ++++++ drivers/iio/adc/mcp3422.c | 415 +++++ drivers/iio/adc/mcp3564.c | 1483 ++++++++++++++++ drivers/iio/adc/mcp3911.c | 954 +++++++++++ drivers/iio/adc/men_z188_adc.c | 174 ++ drivers/iio/adc/meson_saradc.c | 1530 +++++++++++++++++ drivers/iio/adc/mp2629_adc.c | 203 +++ drivers/iio/adc/mt6323-auxadc.c | 313 ++++ drivers/iio/adc/mt6359-auxadc.c | 908 ++++++++++ drivers/iio/adc/mt6360-adc.c | 371 ++++ drivers/iio/adc/mt6370-adc.c | 353 ++++ drivers/iio/adc/mt6577_auxadc.c | 335 ++++ drivers/iio/adc/mxs-lradc-adc.c | 829 +++++++++ drivers/iio/adc/nau7802.c | 558 ++++++ drivers/iio/adc/nct7201.c | 500 ++++++ drivers/iio/adc/npcm_adc.c | 341 ++++ drivers/iio/adc/nxp-sar-adc.c | 1030 +++++++++++ drivers/iio/adc/pac1921.c | 1345 +++++++++++++++ drivers/iio/adc/pac1934.c | 1632 ++++++++++++++++++ drivers/iio/adc/palmas_gpadc.c | 1182 +++++++++++++ drivers/iio/adc/qcom-pm8xxx-xoadc.c | 1027 +++++++++++ drivers/iio/adc/qcom-spmi-adc5-gen3.c | 828 +++++++++ drivers/iio/adc/qcom-spmi-adc5.c | 939 ++++++++++ drivers/iio/adc/qcom-spmi-iadc.c | 598 +++++++ drivers/iio/adc/qcom-spmi-rradc.c | 1013 +++++++++++ drivers/iio/adc/qcom-spmi-vadc.c | 950 +++++++++++ drivers/iio/adc/qcom-vadc-common.c | 772 +++++++++ drivers/iio/adc/rcar-gyroadc.c | 594 +++++++ drivers/iio/adc/rn5t618-adc.c | 259 +++ drivers/iio/adc/rockchip_saradc.c | 626 +++++++ drivers/iio/adc/rohm-bd79112.c | 550 ++++++ drivers/iio/adc/rohm-bd79124.c | 1124 ++++++++++++ drivers/iio/adc/rtq6056.c | 900 ++++++++++ drivers/iio/adc/rzg2l_adc.c | 605 +++++++ drivers/iio/adc/rzn1-adc.c | 489 ++++++ drivers/iio/adc/rzt2h_adc.c | 303 ++++ drivers/iio/adc/sc27xx_adc.c | 954 +++++++++++ drivers/iio/adc/sd_adc_modulator.c | 162 ++ drivers/iio/adc/sophgo-cv1800b-adc.c | 226 +++ drivers/iio/adc/spear_adc.c | 365 ++++ drivers/iio/adc/stm32-adc-core.c | 917 ++++++++++ drivers/iio/adc/stm32-adc-core.h | 279 +++ drivers/iio/adc/stm32-adc.c | 2791 ++++++++++++++++++++++++++++++ drivers/iio/adc/stm32-dfsdm-adc.c | 1907 +++++++++++++++++++++ drivers/iio/adc/stm32-dfsdm-core.c | 522 ++++++ drivers/iio/adc/stm32-dfsdm.h | 348 ++++ drivers/iio/adc/stmpe-adc.c | 365 ++++ drivers/iio/adc/sun20i-gpadc-iio.c | 279 +++ drivers/iio/adc/sun4i-gpadc-iio.c | 705 ++++++++ drivers/iio/adc/ti-adc081c.c | 234 +++ drivers/iio/adc/ti-adc0832.c | 336 ++++ drivers/iio/adc/ti-adc084s021.c | 262 +++ drivers/iio/adc/ti-adc108s102.c | 297 ++++ drivers/iio/adc/ti-adc12138.c | 539 ++++++ drivers/iio/adc/ti-adc128s052.c | 285 ++++ drivers/iio/adc/ti-adc161s626.c | 250 +++ drivers/iio/adc/ti-ads1015.c | 1153 +++++++++++++ drivers/iio/adc/ti-ads1018.c | 738 ++++++++ drivers/iio/adc/ti-ads1100.c | 432 +++++ drivers/iio/adc/ti-ads1119.c | 829 +++++++++ drivers/iio/adc/ti-ads112c14.c | 1454 ++++++++++++++++ drivers/iio/adc/ti-ads124s08.c | 376 ++++ drivers/iio/adc/ti-ads1298.c | 769 +++++++++ drivers/iio/adc/ti-ads131e08.c | 939 ++++++++++ drivers/iio/adc/ti-ads131m02.c | 967 +++++++++++ drivers/iio/adc/ti-ads7138.c | 773 +++++++++ drivers/iio/adc/ti-ads7924.c | 469 +++++ drivers/iio/adc/ti-ads7950.c | 667 ++++++++ drivers/iio/adc/ti-ads8344.c | 193 +++ drivers/iio/adc/ti-ads8688.c | 481 ++++++ drivers/iio/adc/ti-lmp92064.c | 383 +++++ drivers/iio/adc/ti-tlc4541.c | 262 +++ drivers/iio/adc/ti-tsc2046.c | 838 +++++++++ drivers/iio/adc/ti_am335x_adc.c | 753 +++++++++ drivers/iio/adc/twl4030-madc.c | 926 ++++++++++ drivers/iio/adc/twl6030-gpadc.c | 1020 +++++++++++ drivers/iio/adc/versal-sysmon-core.c | 1052 ++++++++++++ drivers/iio/adc/versal-sysmon-i2c.c | 134 ++ drivers/iio/adc/versal-sysmon.c | 92 + drivers/iio/adc/versal-sysmon.h | 120 ++ drivers/iio/adc/vf610_adc.c | 961 +++++++++++ drivers/iio/adc/viperboard_adc.c | 150 ++ drivers/iio/adc/xilinx-ams.c | 1401 +++++++++++++++ drivers/iio/adc/xilinx-xadc-core.c | 1459 ++++++++++++++++ drivers/iio/adc/xilinx-xadc-events.c | 244 +++ drivers/iio/adc/xilinx-xadc.h | 214 +++ 185 files changed, 134036 insertions(+) create mode 100644 drivers/iio/adc/88pm886-gpadc.c create mode 100644 drivers/iio/adc/Kconfig create mode 100644 drivers/iio/adc/Makefile create mode 100644 drivers/iio/adc/ab8500-gpadc.c create mode 100644 drivers/iio/adc/ad4000.c create mode 100644 drivers/iio/adc/ad4030.c create mode 100644 drivers/iio/adc/ad4062.c create mode 100644 drivers/iio/adc/ad4080.c create mode 100644 drivers/iio/adc/ad4130.c create mode 100644 drivers/iio/adc/ad4134.c create mode 100644 drivers/iio/adc/ad4170-4.c create mode 100644 drivers/iio/adc/ad4691.c create mode 100644 drivers/iio/adc/ad4695.c create mode 100644 drivers/iio/adc/ad4851.c create mode 100644 drivers/iio/adc/ad7091r-base.c create mode 100644 drivers/iio/adc/ad7091r-base.h create mode 100644 drivers/iio/adc/ad7091r5.c create mode 100644 drivers/iio/adc/ad7091r8.c create mode 100644 drivers/iio/adc/ad7124.c create mode 100644 drivers/iio/adc/ad7173.c create mode 100644 drivers/iio/adc/ad7191.c create mode 100644 drivers/iio/adc/ad7192.c create mode 100644 drivers/iio/adc/ad7266.c create mode 100644 drivers/iio/adc/ad7280a.c create mode 100644 drivers/iio/adc/ad7291.c create mode 100644 drivers/iio/adc/ad7292.c create mode 100644 drivers/iio/adc/ad7298.c create mode 100644 drivers/iio/adc/ad7380.c create mode 100644 drivers/iio/adc/ad7405.c create mode 100644 drivers/iio/adc/ad7476.c create mode 100644 drivers/iio/adc/ad7606.c create mode 100644 drivers/iio/adc/ad7606.h create mode 100644 drivers/iio/adc/ad7606_bus_iface.h create mode 100644 drivers/iio/adc/ad7606_par.c create mode 100644 drivers/iio/adc/ad7606_spi.c create mode 100644 drivers/iio/adc/ad7625.c create mode 100644 drivers/iio/adc/ad7766.c create mode 100644 drivers/iio/adc/ad7768-1.c create mode 100644 drivers/iio/adc/ad7779.c create mode 100644 drivers/iio/adc/ad7780.c create mode 100644 drivers/iio/adc/ad7791.c create mode 100644 drivers/iio/adc/ad7793.c create mode 100644 drivers/iio/adc/ad7887.c create mode 100644 drivers/iio/adc/ad7923.c create mode 100644 drivers/iio/adc/ad7944.c create mode 100644 drivers/iio/adc/ad7949.c create mode 100644 drivers/iio/adc/ad799x.c create mode 100644 drivers/iio/adc/ad9467.c create mode 100644 drivers/iio/adc/ad_sigma_delta.c create mode 100644 drivers/iio/adc/ade9000.c create mode 100644 drivers/iio/adc/adi-axi-adc.c create mode 100644 drivers/iio/adc/aspeed_adc.c create mode 100644 drivers/iio/adc/at91-sama5d2_adc.c create mode 100644 drivers/iio/adc/at91_adc.c create mode 100644 drivers/iio/adc/axp20x_adc.c create mode 100644 drivers/iio/adc/axp288_adc.c create mode 100644 drivers/iio/adc/bcm_iproc_adc.c create mode 100644 drivers/iio/adc/berlin2-adc.c create mode 100644 drivers/iio/adc/cc10001_adc.c create mode 100644 drivers/iio/adc/cpcap-adc.c create mode 100644 drivers/iio/adc/da9150-gpadc.c create mode 100644 drivers/iio/adc/dln2-adc.c create mode 100644 drivers/iio/adc/envelope-detector.c create mode 100644 drivers/iio/adc/ep93xx_adc.c create mode 100644 drivers/iio/adc/exynos_adc.c create mode 100644 drivers/iio/adc/fsl-imx25-gcq.c create mode 100644 drivers/iio/adc/gehc-pmc-adc.c create mode 100644 drivers/iio/adc/hi8435.c create mode 100644 drivers/iio/adc/hx711.c create mode 100644 drivers/iio/adc/imx7d_adc.c create mode 100644 drivers/iio/adc/imx8qxp-adc.c create mode 100644 drivers/iio/adc/imx93_adc.c create mode 100644 drivers/iio/adc/ina2xx-adc.c create mode 100644 drivers/iio/adc/industrialio-adc.c create mode 100644 drivers/iio/adc/ingenic-adc.c create mode 100644 drivers/iio/adc/intel_dc_ti_adc.c create mode 100644 drivers/iio/adc/intel_mrfld_adc.c create mode 100644 drivers/iio/adc/lp8788_adc.c create mode 100644 drivers/iio/adc/lpc18xx_adc.c create mode 100644 drivers/iio/adc/lpc32xx_adc.c create mode 100644 drivers/iio/adc/ltc2309.c create mode 100644 drivers/iio/adc/ltc2378.c create mode 100644 drivers/iio/adc/ltc2471.c create mode 100644 drivers/iio/adc/ltc2485.c create mode 100644 drivers/iio/adc/ltc2496.c create mode 100644 drivers/iio/adc/ltc2497-core.c create mode 100644 drivers/iio/adc/ltc2497.c create mode 100644 drivers/iio/adc/ltc2497.h create mode 100644 drivers/iio/adc/max1027.c create mode 100644 drivers/iio/adc/max11100.c create mode 100644 drivers/iio/adc/max1118.c create mode 100644 drivers/iio/adc/max11205.c create mode 100644 drivers/iio/adc/max11410.c create mode 100644 drivers/iio/adc/max1241.c create mode 100644 drivers/iio/adc/max1363.c create mode 100644 drivers/iio/adc/max14001.c create mode 100644 drivers/iio/adc/max34408.c create mode 100644 drivers/iio/adc/max77541-adc.c create mode 100644 drivers/iio/adc/max9611.c create mode 100644 drivers/iio/adc/mcp320x.c create mode 100644 drivers/iio/adc/mcp3422.c create mode 100644 drivers/iio/adc/mcp3564.c create mode 100644 drivers/iio/adc/mcp3911.c create mode 100644 drivers/iio/adc/men_z188_adc.c create mode 100644 drivers/iio/adc/meson_saradc.c create mode 100644 drivers/iio/adc/mp2629_adc.c create mode 100644 drivers/iio/adc/mt6323-auxadc.c create mode 100644 drivers/iio/adc/mt6359-auxadc.c create mode 100644 drivers/iio/adc/mt6360-adc.c create mode 100644 drivers/iio/adc/mt6370-adc.c create mode 100644 drivers/iio/adc/mt6577_auxadc.c create mode 100644 drivers/iio/adc/mxs-lradc-adc.c create mode 100644 drivers/iio/adc/nau7802.c create mode 100644 drivers/iio/adc/nct7201.c create mode 100644 drivers/iio/adc/npcm_adc.c create mode 100644 drivers/iio/adc/nxp-sar-adc.c create mode 100644 drivers/iio/adc/pac1921.c create mode 100644 drivers/iio/adc/pac1934.c create mode 100644 drivers/iio/adc/palmas_gpadc.c create mode 100644 drivers/iio/adc/qcom-pm8xxx-xoadc.c create mode 100644 drivers/iio/adc/qcom-spmi-adc5-gen3.c create mode 100644 drivers/iio/adc/qcom-spmi-adc5.c create mode 100644 drivers/iio/adc/qcom-spmi-iadc.c create mode 100644 drivers/iio/adc/qcom-spmi-rradc.c create mode 100644 drivers/iio/adc/qcom-spmi-vadc.c create mode 100644 drivers/iio/adc/qcom-vadc-common.c create mode 100644 drivers/iio/adc/rcar-gyroadc.c create mode 100644 drivers/iio/adc/rn5t618-adc.c create mode 100644 drivers/iio/adc/rockchip_saradc.c create mode 100644 drivers/iio/adc/rohm-bd79112.c create mode 100644 drivers/iio/adc/rohm-bd79124.c create mode 100644 drivers/iio/adc/rtq6056.c create mode 100644 drivers/iio/adc/rzg2l_adc.c create mode 100644 drivers/iio/adc/rzn1-adc.c create mode 100644 drivers/iio/adc/rzt2h_adc.c create mode 100644 drivers/iio/adc/sc27xx_adc.c create mode 100644 drivers/iio/adc/sd_adc_modulator.c create mode 100644 drivers/iio/adc/sophgo-cv1800b-adc.c create mode 100644 drivers/iio/adc/spear_adc.c create mode 100644 drivers/iio/adc/stm32-adc-core.c create mode 100644 drivers/iio/adc/stm32-adc-core.h create mode 100644 drivers/iio/adc/stm32-adc.c create mode 100644 drivers/iio/adc/stm32-dfsdm-adc.c create mode 100644 drivers/iio/adc/stm32-dfsdm-core.c create mode 100644 drivers/iio/adc/stm32-dfsdm.h create mode 100644 drivers/iio/adc/stmpe-adc.c create mode 100644 drivers/iio/adc/sun20i-gpadc-iio.c create mode 100644 drivers/iio/adc/sun4i-gpadc-iio.c create mode 100644 drivers/iio/adc/ti-adc081c.c create mode 100644 drivers/iio/adc/ti-adc0832.c create mode 100644 drivers/iio/adc/ti-adc084s021.c create mode 100644 drivers/iio/adc/ti-adc108s102.c create mode 100644 drivers/iio/adc/ti-adc12138.c create mode 100644 drivers/iio/adc/ti-adc128s052.c create mode 100644 drivers/iio/adc/ti-adc161s626.c create mode 100644 drivers/iio/adc/ti-ads1015.c create mode 100644 drivers/iio/adc/ti-ads1018.c create mode 100644 drivers/iio/adc/ti-ads1100.c create mode 100644 drivers/iio/adc/ti-ads1119.c create mode 100644 drivers/iio/adc/ti-ads112c14.c create mode 100644 drivers/iio/adc/ti-ads124s08.c create mode 100644 drivers/iio/adc/ti-ads1298.c create mode 100644 drivers/iio/adc/ti-ads131e08.c create mode 100644 drivers/iio/adc/ti-ads131m02.c create mode 100644 drivers/iio/adc/ti-ads7138.c create mode 100644 drivers/iio/adc/ti-ads7924.c create mode 100644 drivers/iio/adc/ti-ads7950.c create mode 100644 drivers/iio/adc/ti-ads8344.c create mode 100644 drivers/iio/adc/ti-ads8688.c create mode 100644 drivers/iio/adc/ti-lmp92064.c create mode 100644 drivers/iio/adc/ti-tlc4541.c create mode 100644 drivers/iio/adc/ti-tsc2046.c create mode 100644 drivers/iio/adc/ti_am335x_adc.c create mode 100644 drivers/iio/adc/twl4030-madc.c create mode 100644 drivers/iio/adc/twl6030-gpadc.c create mode 100644 drivers/iio/adc/versal-sysmon-core.c create mode 100644 drivers/iio/adc/versal-sysmon-i2c.c create mode 100644 drivers/iio/adc/versal-sysmon.c create mode 100644 drivers/iio/adc/versal-sysmon.h create mode 100644 drivers/iio/adc/vf610_adc.c create mode 100644 drivers/iio/adc/viperboard_adc.c create mode 100644 drivers/iio/adc/xilinx-ams.c create mode 100644 drivers/iio/adc/xilinx-xadc-core.c create mode 100644 drivers/iio/adc/xilinx-xadc-events.c create mode 100644 drivers/iio/adc/xilinx-xadc.h (limited to 'drivers/iio/adc') diff --git a/drivers/iio/adc/88pm886-gpadc.c b/drivers/iio/adc/88pm886-gpadc.c new file mode 100644 index 000000000..938ba43c2 --- /dev/null +++ b/drivers/iio/adc/88pm886-gpadc.c @@ -0,0 +1,392 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright 2025, Duje Mihanović + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +#include + +struct pm886_gpadc { + struct regmap *map; +}; + +enum pm886_gpadc_channel { + VSC_CHAN, + VCHG_PWR_CHAN, + VCF_OUT_CHAN, + VBAT_CHAN, + VBAT_SLP_CHAN, + VBUS_CHAN, + + GPADC0_CHAN, + GPADC1_CHAN, + GPADC2_CHAN, + GPADC3_CHAN, + + GND_DET1_CHAN, + GND_DET2_CHAN, + MIC_DET_CHAN, + + TINT_CHAN, +}; + +static const int pm886_gpadc_regs[] = { + [VSC_CHAN] = PM886_REG_GPADC_VSC, + [VCHG_PWR_CHAN] = PM886_REG_GPADC_VCHG_PWR, + [VCF_OUT_CHAN] = PM886_REG_GPADC_VCF_OUT, + [VBAT_CHAN] = PM886_REG_GPADC_VBAT, + [VBAT_SLP_CHAN] = PM886_REG_GPADC_VBAT_SLP, + [VBUS_CHAN] = PM886_REG_GPADC_VBUS, + + [GPADC0_CHAN] = PM886_REG_GPADC_GPADC0, + [GPADC1_CHAN] = PM886_REG_GPADC_GPADC1, + [GPADC2_CHAN] = PM886_REG_GPADC_GPADC2, + [GPADC3_CHAN] = PM886_REG_GPADC_GPADC3, + + [GND_DET1_CHAN] = PM886_REG_GPADC_GND_DET1, + [GND_DET2_CHAN] = PM886_REG_GPADC_GND_DET2, + [MIC_DET_CHAN] = PM886_REG_GPADC_MIC_DET, + + [TINT_CHAN] = PM886_REG_GPADC_TINT, +}; + +#define ADC_CHANNEL_VOLTAGE(index, lsb, name) \ +{ \ + .type = IIO_VOLTAGE, \ + .indexed = 1, \ + .channel = index, \ + .address = lsb, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ + BIT(IIO_CHAN_INFO_SCALE), \ + .datasheet_name = name, \ +} + +#define ADC_CHANNEL_RESISTANCE(index, lsb, name) \ +{ \ + .type = IIO_RESISTANCE, \ + .indexed = 1, \ + .channel = index, \ + .address = lsb, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED), \ + .datasheet_name = name, \ +} + +#define ADC_CHANNEL_TEMPERATURE(index, lsb, name) \ +{ \ + .type = IIO_TEMP, \ + .indexed = 1, \ + .channel = index, \ + .address = lsb, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ + BIT(IIO_CHAN_INFO_SCALE) | \ + BIT(IIO_CHAN_INFO_OFFSET), \ + .datasheet_name = name, \ +} + +static const struct iio_chan_spec pm886_gpadc_channels[] = { + ADC_CHANNEL_VOLTAGE(VSC_CHAN, 1367, "vsc"), + ADC_CHANNEL_VOLTAGE(VCHG_PWR_CHAN, 1709, "vchg_pwr"), + ADC_CHANNEL_VOLTAGE(VCF_OUT_CHAN, 1367, "vcf_out"), + ADC_CHANNEL_VOLTAGE(VBAT_CHAN, 1367, "vbat"), + ADC_CHANNEL_VOLTAGE(VBAT_SLP_CHAN, 1367, "vbat_slp"), + ADC_CHANNEL_VOLTAGE(VBUS_CHAN, 1709, "vbus"), + + ADC_CHANNEL_RESISTANCE(GPADC0_CHAN, 342, "gpadc0"), + ADC_CHANNEL_RESISTANCE(GPADC1_CHAN, 342, "gpadc1"), + ADC_CHANNEL_RESISTANCE(GPADC2_CHAN, 342, "gpadc2"), + ADC_CHANNEL_RESISTANCE(GPADC3_CHAN, 342, "gpadc3"), + + ADC_CHANNEL_VOLTAGE(GND_DET1_CHAN, 342, "gnddet1"), + ADC_CHANNEL_VOLTAGE(GND_DET2_CHAN, 342, "gnddet2"), + ADC_CHANNEL_VOLTAGE(MIC_DET_CHAN, 1367, "mic_det"), + + ADC_CHANNEL_TEMPERATURE(TINT_CHAN, 104, "tint"), +}; + +static const struct regmap_config pm886_gpadc_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .max_register = PM886_GPADC_MAX_REGISTER, +}; + +static int gpadc_get_raw(struct iio_dev *iio, enum pm886_gpadc_channel chan) +{ + struct pm886_gpadc *gpadc = iio_priv(iio); + __be16 buf; + int ret; + + ret = regmap_bulk_read(gpadc->map, pm886_gpadc_regs[chan], &buf, sizeof(buf)); + if (ret) + return ret; + + return be16_to_cpu(buf) >> 4; +} + +static int +gpadc_set_bias(struct pm886_gpadc *gpadc, enum pm886_gpadc_channel chan, bool on) +{ + unsigned int gpadc_num = chan - GPADC0_CHAN; + unsigned int bits = BIT(gpadc_num + 4) | BIT(gpadc_num); + + return regmap_assign_bits(gpadc->map, PM886_REG_GPADC_CONFIG(0x14), bits, on); +} + +static int +gpadc_find_bias_current(struct iio_dev *iio, struct iio_chan_spec const *chan, + unsigned int *raw_uV, unsigned int *raw_uA) +{ + struct pm886_gpadc *gpadc = iio_priv(iio); + unsigned int gpadc_num = chan->channel - GPADC0_CHAN; + unsigned int reg = PM886_REG_GPADC_CONFIG(0xb + gpadc_num); + unsigned long lsb = chan->address; + int ret; + + for (unsigned int i = 0; i < PM886_GPADC_BIAS_LEVELS; i++) { + ret = regmap_update_bits(gpadc->map, reg, GENMASK(3, 0), i); + if (ret) + return ret; + + /* Wait for the new bias level to apply. */ + fsleep(5 * USEC_PER_MSEC); + + *raw_uA = PM886_GPADC_INDEX_TO_BIAS_uA(i); + *raw_uV = gpadc_get_raw(iio, chan->channel) * lsb; + + /* + * Vendor kernel errors out above 1.25 V, but testing shows + * that the resistance of the battery detection channel (GPADC2 + * on coreprimevelte) reaches about 1.4 MΩ when the battery is + * removed, which can't be measured with such a low upper + * limit. Therefore, to be able to detect the battery without + * ugly externs as used in the vendor fuel gauge driver, + * increase this limit a bit. + */ + if (WARN_ON(*raw_uV > 1500 * (MICRO / MILLI))) + return -EIO; + + /* + * Vendor kernel errors out under 300 mV, but for the same + * reason as above (except the channel hovers around 3.5 kΩ + * with battery present) reduce this limit. + */ + if (*raw_uV < 200 * (MICRO / MILLI)) { + dev_dbg(&iio->dev, "bad bias for chan %d: %d uA @ %d uV\n", + chan->channel, *raw_uA, *raw_uV); + continue; + } + + dev_dbg(&iio->dev, "good bias for chan %d: %d uA @ %d uV\n", + chan->channel, *raw_uA, *raw_uV); + return 0; + } + + dev_err(&iio->dev, "failed to find good bias for chan %d\n", chan->channel); + return -EINVAL; +} + +static int +gpadc_get_resistance_ohm(struct iio_dev *iio, struct iio_chan_spec const *chan) +{ + struct pm886_gpadc *gpadc = iio_priv(iio); + unsigned int raw_uV, raw_uA; + int ret; + + ret = gpadc_set_bias(gpadc, chan->channel, true); + if (ret) + goto out; + + ret = gpadc_find_bias_current(iio, chan, &raw_uV, &raw_uA); + if (ret) + goto out; + + ret = DIV_ROUND_CLOSEST(raw_uV, raw_uA); +out: + gpadc_set_bias(gpadc, chan->channel, false); + return ret; +} + +static int +__pm886_gpadc_read_raw(struct iio_dev *iio, struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + unsigned long lsb = chan->address; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + *val = gpadc_get_raw(iio, chan->channel); + if (*val < 0) + return *val; + + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + *val = lsb; + + if (chan->type == IIO_VOLTAGE) { + *val2 = MILLI; + return IIO_VAL_FRACTIONAL; + } else { + return IIO_VAL_INT; + } + case IIO_CHAN_INFO_OFFSET: + /* Raw value is 104 millikelvin/LSB, convert it to 104 millicelsius/LSB */ + *val = ABSOLUTE_ZERO_MILLICELSIUS; + *val2 = lsb; + return IIO_VAL_FRACTIONAL; + case IIO_CHAN_INFO_PROCESSED: + *val = gpadc_get_resistance_ohm(iio, chan); + if (*val < 0) + return *val; + + return IIO_VAL_INT; + default: + return -EINVAL; + } +} + +static int pm886_gpadc_read_raw(struct iio_dev *iio, struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct device *dev = iio->dev.parent; + int ret; + + ret = pm_runtime_resume_and_get(dev); + if (ret) + return ret; + + ret = __pm886_gpadc_read_raw(iio, chan, val, val2, mask); + + pm_runtime_put_autosuspend(dev); + return ret; +} + +static int pm886_gpadc_hw_enable(struct regmap *map) +{ + static const u8 config[] = { + PM886_GPADC_CONFIG1_EN_ALL, + PM886_GPADC_CONFIG2_EN_ALL, + PM886_GPADC_GND_DET2_EN, + }; + int ret; + + /* Enable the ADC block. */ + ret = regmap_set_bits(map, PM886_REG_GPADC_CONFIG(0x6), BIT(0)); + if (ret) + return ret; + + /* Enable all channels. */ + return regmap_bulk_write(map, PM886_REG_GPADC_CONFIG(0x1), config, ARRAY_SIZE(config)); +} + +static int pm886_gpadc_hw_disable(struct regmap *map) +{ + return regmap_clear_bits(map, PM886_REG_GPADC_CONFIG(0x6), BIT(0)); +} + +static const struct iio_info pm886_gpadc_iio_info = { + .read_raw = pm886_gpadc_read_raw, +}; + +static int pm886_gpadc_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct pm886_chip *chip = dev_get_drvdata(dev->parent); + struct i2c_client *client = chip->client; + struct pm886_gpadc *gpadc; + struct i2c_client *page; + struct iio_dev *iio; + int ret; + + iio = devm_iio_device_alloc(dev, sizeof(*gpadc)); + if (!iio) + return -ENOMEM; + + gpadc = iio_priv(iio); + dev_set_drvdata(dev, iio); + + page = devm_i2c_new_dummy_device(dev, client->adapter, + client->addr + PM886_PAGE_OFFSET_GPADC); + if (IS_ERR(page)) + return dev_err_probe(dev, PTR_ERR(page), "Failed to initialize GPADC page\n"); + + gpadc->map = devm_regmap_init_i2c(page, &pm886_gpadc_regmap_config); + if (IS_ERR(gpadc->map)) + return dev_err_probe(dev, PTR_ERR(gpadc->map), + "Failed to initialize GPADC regmap\n"); + + iio->name = "88pm886-gpadc"; + iio->modes = INDIO_DIRECT_MODE; + iio->info = &pm886_gpadc_iio_info; + iio->channels = pm886_gpadc_channels; + iio->num_channels = ARRAY_SIZE(pm886_gpadc_channels); + device_set_node(&iio->dev, dev_fwnode(dev->parent)); + + ret = devm_pm_runtime_enable(dev); + if (ret) + return dev_err_probe(dev, ret, "Failed to enable runtime PM\n"); + + pm_runtime_set_autosuspend_delay(dev, 50); + pm_runtime_use_autosuspend(dev); + ret = devm_iio_device_register(dev, iio); + if (ret) + return dev_err_probe(dev, ret, "Failed to register ADC\n"); + + return 0; +} + +static int pm886_gpadc_runtime_resume(struct device *dev) +{ + struct iio_dev *iio = dev_get_drvdata(dev); + struct pm886_gpadc *gpadc = iio_priv(iio); + + return pm886_gpadc_hw_enable(gpadc->map); +} + +static int pm886_gpadc_runtime_suspend(struct device *dev) +{ + struct iio_dev *iio = dev_get_drvdata(dev); + struct pm886_gpadc *gpadc = iio_priv(iio); + + return pm886_gpadc_hw_disable(gpadc->map); +} + +static DEFINE_RUNTIME_DEV_PM_OPS(pm886_gpadc_pm_ops, + pm886_gpadc_runtime_suspend, + pm886_gpadc_runtime_resume, NULL); + +static const struct platform_device_id pm886_gpadc_id[] = { + { .name = "88pm886-gpadc" }, + { } +}; +MODULE_DEVICE_TABLE(platform, pm886_gpadc_id); + +static struct platform_driver pm886_gpadc_driver = { + .driver = { + .name = "88pm886-gpadc", + .pm = pm_ptr(&pm886_gpadc_pm_ops), + }, + .probe = pm886_gpadc_probe, + .id_table = pm886_gpadc_id, +}; +module_platform_driver(pm886_gpadc_driver); + +MODULE_AUTHOR("Duje Mihanović "); +MODULE_DESCRIPTION("Marvell 88PM886 GPADC driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig new file mode 100644 index 000000000..415e519ad --- /dev/null +++ b/drivers/iio/adc/Kconfig @@ -0,0 +1,2101 @@ +# SPDX-License-Identifier: GPL-2.0-only +# +# ADC drivers +# +# When adding new entries keep the list in alphabetical order + +menu "Analog to digital converters" + +config IIO_ADC_HELPER + tristate + +config 88PM886_GPADC + tristate "Marvell 88PM886 GPADC driver" + depends on MFD_88PM886_PMIC + default MFD_88PM886_PMIC + help + Say Y here to enable support for the GPADC (General Purpose ADC) + found on the Marvell 88PM886 PMIC. The GPADC measures various + internal voltages and temperatures, including (but not limited to) + system, battery and USB Vbus. + + To compile this driver as a module, choose M here: the module will be + called 88pm886-gpadc. + +config AB8500_GPADC + bool "ST-Ericsson AB8500 GPADC driver" + depends on AB8500_CORE && REGULATOR_AB8500 + default y + help + AB8500 Analog Baseband, mixed signal integrated circuit GPADC + (General Purpose Analog to Digital Converter) driver used to monitor + internal voltages, convert accessory and battery, AC (charger, mains) + and USB voltages integral to the U8500 platform. + +config AD_SIGMA_DELTA + tristate "Analog Devices Sigma-Delta Modulator support" if COMPILE_TEST + depends on SPI + select IIO_BUFFER + select IIO_BUFFER_DMAENGINE + select IIO_TRIGGERED_BUFFER + select SPI_OFFLOAD + +config AD4000 + tristate "Analog Devices AD4000 ADC Driver" + depends on SPI + select IIO_BUFFER + select IIO_BUFFER_DMAENGINE + select IIO_TRIGGERED_BUFFER + select SPI_OFFLOAD + help + Say yes here to build support for Analog Devices AD4000 high speed + SPI analog to digital converters (ADC). If intended to use with + SPI offloading support, it is recommended to enable + CONFIG_SPI_AXI_SPI_ENGINE, CONFIG_PWM_AXI_PWMGEN, and + CONFIG_SPI_OFFLOAD_TRIGGER_PWM. + + To compile this driver as a module, choose M here: the module will be + called ad4000. + +config AD4030 + tristate "Analog Devices AD4030 ADC Driver" + depends on SPI + depends on GPIOLIB + depends on PWM + select REGMAP + select IIO_BUFFER + select IIO_BUFFER_DMA + select IIO_BUFFER_DMAENGINE + select IIO_TRIGGERED_BUFFER + select SPI_OFFLOAD + select SPI_OFFLOAD_TRIGGER_PWM + help + Say yes here to build support for Analog Devices AD4030 and AD4630 high speed + SPI analog to digital converters (ADC). + + To compile this driver as a module, choose M here: the module will be + called ad4030. + +config AD4062 + tristate "Analog Devices AD4062 Driver" + depends on I3C + depends on GPIOLIB + select REGMAP_I3C + select IIO_BUFFER + select IIO_TRIGGERED_BUFFER + help + Say yes here to build support for Analog Devices AD4062 I3C analog + to digital converters (ADC). + + To compile this driver as a module, choose M here: the module will be + called ad4062. + +config AD4080 + tristate "Analog Devices AD4080 high speed ADC" + depends on SPI + select REGMAP_SPI + select IIO_BACKEND + help + Say yes here to build support for Analog Devices AD4080 + high speed, low noise, low distortion, 20-bit, Easy Drive, + successive approximation register (SAR) analog-to-digital + converter (ADC). Supports iio_backended devices for AD4080. + + To compile this driver as a module, choose M here: the module will be + called ad4080. + +config AD4130 + tristate "Analog Device AD4130 ADC Driver" + depends on SPI + depends on GPIOLIB + select IIO_BUFFER + select IIO_TRIGGERED_BUFFER + select IIO_KFIFO_BUF + select REGMAP_SPI + depends on COMMON_CLK + help + Say yes here to build support for Analog Devices AD4130-8 SPI analog + to digital converters (ADC). + + To compile this driver as a module, choose M here: the module will be + called ad4130. + +config AD4134 + tristate "Analog Device AD4134 ADC Driver" + depends on SPI + select REGMAP_SPI + select CRC8 + help + Say yes here to build support for Analog Devices AD4134 SPI analog to + digital converters (ADC). + + To compile this driver as a module, choose M here: the module will be + called ad4134_spi. + +config AD4170_4 + tristate "Analog Device AD4170-4 ADC Driver" + depends on SPI + select REGMAP_SPI + select IIO_BUFFER + select IIO_TRIGGERED_BUFFER + depends on COMMON_CLK + depends on GPIOLIB + help + Say yes here to build support for Analog Devices AD4170-4 SPI analog + to digital converters (ADC). + + To compile this driver as a module, choose M here: the module will be + called ad4170-4. + +config AD4691 + tristate "Analog Devices AD4691 Family ADC Driver" + depends on SPI + depends on REGULATOR || COMPILE_TEST + select IIO_BUFFER + select IIO_BUFFER_DMAENGINE + select IIO_TRIGGERED_BUFFER + select REGMAP + select SPI_OFFLOAD + help + Say yes here to build support for Analog Devices AD4691 Family MuxSAR + SPI analog to digital converters (ADC). + + To compile this driver as a module, choose M here: the module will be + called ad4691. + +config AD4695 + tristate "Analog Device AD4695 ADC Driver" + depends on SPI + select IIO_BUFFER + select IIO_BUFFER_DMAENGINE + select IIO_TRIGGERED_BUFFER + select REGMAP + select SPI_OFFLOAD + help + Say yes here to build support for Analog Devices AD4695 and similar + analog to digital converters (ADC). + + To compile this driver as a module, choose M here: the module will be + called ad4695. + +config AD4851 + tristate "Analog Device AD4851 DAS Driver" + depends on SPI + depends on PWM + select REGMAP_SPI + select IIO_BACKEND + help + Say yes here to build support for Analog Devices AD4851, AD4852, + AD4853, AD4854, AD4855, AD4856, AD4857, AD4858, AD4858I high speed + data acquisition system (DAS). + + To compile this driver as a module, choose M here: the module will be + called ad4851. + +config AD7091R + tristate + +config AD7091R5 + tristate "Analog Devices AD7091R5 ADC Driver" + depends on I2C + select AD7091R + select REGMAP_I2C + help + Say yes here to build support for Analog Devices AD7091R-5 ADC. + +config AD7091R8 + tristate "Analog Devices AD7091R8 ADC Driver" + depends on SPI + select AD7091R + select REGMAP_SPI + help + Say yes here to build support for Analog Devices AD7091R-2, AD7091R-4, + and AD7091R-8 ADC. + + To compile this driver as a module, choose M here: the module will be + called ad7091r8. + +config AD7124 + tristate "Analog Devices AD7124 and similar sigma-delta ADCs driver" + depends on SPI_MASTER + select AD_SIGMA_DELTA + help + Say yes here to build support for Analog Devices AD7124-4 and AD7124-8 + SPI analog to digital converters (ADC). + + To compile this driver as a module, choose M here: the module will be + called ad7124. + +config AD7173 + tristate "Analog Devices AD7173 driver" + depends on SPI_MASTER + select AD_SIGMA_DELTA + select GPIOLIB + select GPIO_REGMAP + select REGMAP_SPI + help + Say yes here to build support for Analog Devices AD7173 and similar ADC + Currently supported models: + - AD7172-2 + - AD7173-8 + - AD7175-2 + - AD7176-2 + + To compile this driver as a module, choose M here: the module will be + called ad7173. + +config AD7191 + tristate "Analog Devices AD7191 ADC driver" + depends on SPI + select AD_SIGMA_DELTA + help + Say yes here to build support for Analog Devices AD7191. + + To compile this driver as a module, choose M here: the + module will be called ad7191. + +config AD7192 + tristate "Analog Devices AD7192 and similar ADC driver" + depends on SPI + select AD_SIGMA_DELTA + help + Say yes here to build support for Analog Devices SPI analog to digital + converters (ADC): + - AD7190 + - AD7192 + - AD7193 + - AD7194 + - AD7195 + If unsure, say N (but it's safe to say "Y"). + + To compile this driver as a module, choose M here: the + module will be called ad7192. + +config AD7266 + tristate "Analog Devices AD7265/AD7266 ADC driver" + depends on SPI_MASTER + select IIO_BUFFER + select IIO_TRIGGER + select IIO_TRIGGERED_BUFFER + help + Say yes here to build support for Analog Devices AD7265 and AD7266 + ADCs. + + To compile this driver as a module, choose M here: the module will be + called ad7266. + +config AD7280 + tristate "Analog Devices AD7280A Lithium Ion Battery Monitoring System" + depends on SPI + select CRC8 + help + Say yes here to build support for Analog Devices AD7280A + Lithium Ion Battery Monitoring System. + + To compile this driver as a module, choose M here: the + module will be called ad7280a + +config AD7291 + tristate "Analog Devices AD7291 ADC driver" + depends on I2C + help + Say yes here to build support for Analog Devices AD7291 + 8 Channel ADC with temperature sensor. + + To compile this driver as a module, choose M here: the + module will be called ad7291. + +config AD7292 + tristate "Analog Devices AD7292 ADC driver" + depends on SPI + help + Say yes here to build support for Analog Devices AD7292 + 8 Channel ADC with temperature sensor. + + To compile this driver as a module, choose M here: the + module will be called ad7292. + +config AD7298 + tristate "Analog Devices AD7298 ADC driver" + depends on SPI + select IIO_BUFFER + select IIO_TRIGGERED_BUFFER + help + Say yes here to build support for Analog Devices AD7298 + 8 Channel ADC with temperature sensor. + + To compile this driver as a module, choose M here: the + module will be called ad7298. + +config AD7380 + tristate "Analog Devices AD7380 ADC driver" + depends on SPI_MASTER + select REGMAP + select SPI_OFFLOAD + select IIO_BUFFER + select IIO_BUFFER_DMAENGINE + select IIO_TRIGGER + select IIO_TRIGGERED_BUFFER + help + AD7380 is a family of simultaneous sampling ADCs that share the same + SPI register map and have similar pinouts. + + Say yes here to build support for Analog Devices AD7380 ADC and + similar chips. + + To compile this driver as a module, choose M here: the module will be + called ad7380. + +config AD7405 + tristate "Analog Device AD7405 ADC Driver" + depends on IIO_BACKEND + help + Say yes here to build support for Analog Devices AD7405, ADUM7701, + ADUM7702, ADUM7703 analog to digital converters (ADC). + + To compile this driver as a module, choose M here: the module will be + called ad7405. + +config AD7476 + tristate "Analog Devices AD7476 1-channel ADCs driver and other similar devices from AD and TI" + depends on SPI + select IIO_BUFFER + select IIO_TRIGGERED_BUFFER + help + Say yes here to build support for the following SPI analog to + digital converters (ADCs): + Analog Devices: AD7273, AD7274, AD7276, AD7277, AD7278, AD7475, + AD7476, AD7477, AD7478, AD7466, AD7467, AD7468, AD7495, AD7910, + AD7920. + Texas Instruments: ADS7866, ADS7867, ADS7868. + + To compile this driver as a module, choose M here: the + module will be called ad7476. + +config AD7606 + tristate + select IIO_BUFFER + select IIO_TRIGGERED_BUFFER + +config AD7606_IFACE_PARALLEL + tristate "Analog Devices AD7606 ADC driver with parallel interface support" + depends on HAS_IOPORT + select AD7606 + select IIO_BACKEND + help + Say yes here to build parallel interface support for Analog Devices: + ad7605-4, ad7606, ad7606-6, ad7606-4 analog to digital converters (ADC). + It also support iio_backended devices for AD7606B. + + To compile this driver as a module, choose M here: the + module will be called ad7606_par. + +config AD7606_IFACE_SPI + tristate "Analog Devices AD7606 ADC driver with spi interface support" + depends on SPI + select AD7606 + select IIO_BUFFER_DMAENGINE + select SPI_OFFLOAD + help + Say yes here to build spi interface support for Analog Devices: + ad7605-4, ad7606, ad7606-6, ad7606-4 analog to digital converters (ADC). + + To compile this driver as a module, choose M here: the + module will be called ad7606_spi. + +config AD7625 + tristate "Analog Devices AD7625/AD7626 High Speed ADC driver" + depends on PWM + select IIO_BACKEND + help + Say yes here to build support for Analog Devices: + * AD7625 16-Bit, 6 MSPS PulSAR Analog-to-Digital Converter + * AD7626 16-Bit, 10 MSPS PulSAR Analog-to-Digital Converter + * AD7960 18-Bit, 5 MSPS PulSAR Analog-to-Digital Converter + * AD7961 16-Bit, 5 MSPS PulSAR Analog-to-Digital Converter + + The driver requires the assistance of the AXI ADC IP core to operate. + + To compile this driver as a module, choose M here: the module will be + called ad7625. + +config AD7766 + tristate "Analog Devices AD7766/AD7767 ADC driver" + depends on SPI_MASTER + select IIO_BUFFER + select IIO_TRIGGERED_BUFFER + help + Say yes here to build support for Analog Devices AD7766, AD7766-1, + AD7766-2, AD7767, AD7767-1, AD7767-2 SPI analog to digital converters. + + To compile this driver as a module, choose M here: the module will be + called ad7766. + +config AD7768_1 + tristate "Analog Devices AD7768-1 ADC driver" + depends on SPI + select GPIOLIB + select REGULATOR + select REGMAP_SPI + select RATIONAL + select IIO_BUFFER + select IIO_BUFFER_DMAENGINE + select IIO_TRIGGER + select IIO_TRIGGERED_BUFFER + select SPI_OFFLOAD + help + Say yes here to build support for Analog Devices AD7768-1 SPI + simultaneously sampling sigma-delta analog to digital converter (ADC). + + To compile this driver as a module, choose M here: the module will be + called ad7768-1. + +config AD7779 + tristate "Analog Devices AD7779 ADC driver" + depends on SPI + select CRC8 + select IIO_BUFFER + select IIO_TRIGGERED_BUFFER + select IIO_BACKEND + help + Say yes here to build support for Analog Devices AD777X family + (AD7770, AD7771, AD7779) analog to digital converter (ADC). + + To compile this driver as a module, choose M here: the module will be + called ad7779. + +config AD7780 + tristate "Analog Devices AD7780 and similar ADCs driver" + depends on SPI + depends on GPIOLIB || COMPILE_TEST + select AD_SIGMA_DELTA + help + Say yes here to build support for Analog Devices AD7170, AD7171, + AD7780 and AD7781 SPI analog to digital converters (ADC). + + To compile this driver as a module, choose M here: the + module will be called ad7780. + +config AD7791 + tristate "Analog Devices AD7791 ADC driver" + depends on SPI + select AD_SIGMA_DELTA + help + Say yes here to build support for Analog Devices AD7787, AD7788, AD7789, + AD7790 and AD7791 SPI analog to digital converters (ADC). + + To compile this driver as a module, choose M here: the module will be + called ad7791. + +config AD7793 + tristate "Analog Devices AD7793 and similar ADCs driver" + depends on SPI + select AD_SIGMA_DELTA + help + Say yes here to build support for Analog Devices AD7785, AD7792, AD7793, + AD7794 and AD7795 SPI analog to digital converters (ADC). + + To compile this driver as a module, choose M here: the + module will be called AD7793. + +config AD7887 + tristate "Analog Devices AD7887 ADC driver" + depends on SPI + select IIO_BUFFER + select IIO_TRIGGERED_BUFFER + help + Say yes here to build support for Analog Devices + AD7887 SPI analog to digital converter (ADC). + + To compile this driver as a module, choose M here: the + module will be called ad7887. + +config AD7923 + tristate "Analog Devices AD7923 and similar ADCs driver" + depends on SPI + select IIO_BUFFER + select IIO_TRIGGERED_BUFFER + help + Say yes here to build support for Analog Devices + AD7904, AD7914, AD7923, AD7924 4 Channel ADCs. + + To compile this driver as a module, choose M here: the + module will be called ad7923. + +config AD7944 + tristate "Analog Devices AD7944 and similar ADCs driver" + depends on SPI + select SPI_OFFLOAD + select IIO_BUFFER + select IIO_BUFFER_DMAENGINE + select IIO_TRIGGERED_BUFFER + help + Say yes here to build support for Analog Devices + AD7944, AD7985, AD7986 ADCs. + + To compile this driver as a module, choose M here: the + module will be called ad7944 + +config AD7949 + tristate "Analog Devices AD7949 and similar ADCs driver" + depends on SPI + help + Say yes here to build support for Analog Devices + AD7949, AD7682, AD7689 8 Channel ADCs. + + To compile this driver as a module, choose M here: the + module will be called ad7949. + +config AD799X + tristate "Analog Devices AD799x ADC driver" + depends on I2C + select IIO_BUFFER + select IIO_TRIGGERED_BUFFER + help + Say yes here to build support for Analog Devices: + ad7991, ad7995, ad7999, ad7992, ad7993, ad7994, ad7997, ad7998 + i2c analog to digital converters (ADC). Provides direct access + via sysfs. + + To compile this driver as a module, choose M here: the module will be + called ad799x. + +config AD9467 + tristate "Analog Devices AD9467 High Speed ADC driver" + depends on SPI + select IIO_BACKEND + help + Say yes here to build support for Analog Devices: + * AD9467 16-Bit, 200 MSPS/250 MSPS Analog-to-Digital Converter + + The driver requires the assistance of the AXI ADC IP core to operate, + since SPI is used for configuration only, while data has to be + streamed into memory via DMA. + + To compile this driver as a module, choose M here: the module will be + called ad9467. + +config ADE9000 + tristate "Analog Devices ADE9000 Multiphase Energy, and Power Quality Monitoring IC Driver" + depends on SPI + select REGMAP_SPI + select IIO_BUFFER + select IIO_KFIFO_BUF + help + Say yes here to build support for the Analog Devices ADE9000, + a highly accurate, multiphase energy and power quality monitoring + integrated circuit. + + The device features high-precision analog-to-digital converters + and digital signal processing to compute RMS values, power factor, + frequency, and harmonic analysis. It supports SPI communication + and provides buffered data output through the IIO framework. + + To compile this driver as a module, choose M here: the module will + be called ade9000. + +config ADI_AXI_ADC + tristate "Analog Devices Generic AXI ADC IP core driver" + depends on MICROBLAZE || NIOS2 || ARCH_ZYNQ || ARCH_ZYNQMP || ARCH_INTEL_SOCFPGA || COMPILE_TEST + select IIO_BUFFER + select IIO_BUFFER_HW_CONSUMER + select IIO_BUFFER_DMAENGINE + select REGMAP_MMIO + select IIO_BACKEND + help + Say yes here to build support for Analog Devices Generic + AXI ADC IP core. The IP core is used for interfacing with + analog-to-digital (ADC) converters that require either a high-speed + serial interface (JESD204B/C) or a source synchronous parallel + interface (LVDS/CMOS). + Typically (for such devices) SPI will be used for configuration only, + while this IP core handles the streaming of data into memory via DMA. + + Link: https://wiki.analog.com/resources/fpga/docs/axi_adc_ip + If unsure, say N (but it's safe to say "Y"). + + To compile this driver as a module, choose M here: the + module will be called adi-axi-adc. + +config ASPEED_ADC + tristate "Aspeed ADC" + depends on ARCH_ASPEED || COMPILE_TEST + depends on COMMON_CLK + help + If you say yes here you get support for the ADC included in Aspeed + BMC SoCs. + + To compile this driver as a module, choose M here: the module will be + called aspeed_adc. + +config AT91_ADC + tristate "Atmel AT91 ADC" + depends on ARCH_AT91 || COMPILE_TEST + depends on INPUT && SYSFS && OF + select IIO_BUFFER + select IIO_TRIGGERED_BUFFER + help + Say yes here to build support for Atmel AT91 ADC. + + To compile this driver as a module, choose M here: the module will be + called at91_adc. + +config AT91_SAMA5D2_ADC + tristate "Atmel AT91 SAMA5D2 ADC" + depends on ARCH_AT91 || COMPILE_TEST + depends on HAS_IOMEM + select IIO_BUFFER + select IIO_TRIGGERED_BUFFER + help + Say yes here to build support for Atmel SAMA5D2 ADC which is + available on SAMA5D2 SoC family. + + To compile this driver as a module, choose M here: the module will be + called at91-sama5d2_adc. + +config AXP20X_ADC + tristate "X-Powers AXP20X and AXP22X ADC driver" + depends on MFD_AXP20X + help + Say yes here to have support for X-Powers power management IC (PMIC) + AXP20X and AXP22X ADC devices. + + To compile this driver as a module, choose M here: the module will be + called axp20x_adc. + +config AXP288_ADC + tristate "X-Powers AXP288 ADC driver" + depends on MFD_AXP20X + help + Say yes here to have support for X-Powers power management IC (PMIC) ADC + device. Depending on platform configuration, this general purpose ADC can + be used for sampling sensors such as thermal resistors. + + To compile this driver as a module, choose M here: the module will be + called axp288_adc. + +config BCM_IPROC_ADC + tristate "Broadcom IPROC ADC driver" + depends on (ARCH_BCM_IPROC && OF) || COMPILE_TEST + depends on MFD_SYSCON + default ARCH_BCM_CYGNUS + help + Say Y here if you want to add support for the Broadcom static + ADC driver. + + Broadcom iProc ADC driver. Broadcom iProc ADC controller has 8 + channels. The driver allows the user to read voltage values. + +config BERLIN2_ADC + tristate "Marvell Berlin2 ADC driver" + depends on ARCH_BERLIN || COMPILE_TEST + help + Marvell Berlin2 ADC driver. This ADC has 8 channels, with one used for + temperature measurement. + +config CC10001_ADC + tristate "Cosmic Circuits 10001 ADC driver" + depends on HAS_IOMEM && HAVE_CLK && REGULATOR + select IIO_BUFFER + select IIO_TRIGGERED_BUFFER + help + Say yes here to build support for Cosmic Circuits 10001 ADC. + + This driver can also be built as a module. If so, the module will be + called cc10001_adc. + +config CPCAP_ADC + tristate "Motorola CPCAP PMIC ADC driver" + depends on MFD_CPCAP + select IIO_BUFFER + select IIO_TRIGGERED_BUFFER + help + Say yes here to build support for Motorola CPCAP PMIC ADC. + + This driver can also be built as a module. If so, the module will be + called cpcap-adc. + +config DA9150_GPADC + tristate "Dialog DA9150 GPADC driver support" + depends on MFD_DA9150 + help + Say yes here to build support for Dialog DA9150 GPADC. + + This driver can also be built as a module. If chosen, the module name + will be da9150-gpadc. + + To compile this driver as a module, choose M here: the module will be + called berlin2-adc. + +config DLN2_ADC + tristate "Diolan DLN-2 ADC driver support" + depends on MFD_DLN2 + select IIO_BUFFER + select IIO_TRIGGERED_BUFFER + help + Say yes here to build support for Diolan DLN-2 ADC. + + This driver can also be built as a module. If so, the module will be + called adc_dln2. + +config ENVELOPE_DETECTOR + tristate "Envelope detector using a DAC and a comparator" + help + Say yes here to build support for an envelope detector using a DAC + and a comparator. + + To compile this driver as a module, choose M here: the module will be + called envelope-detector. + +config EP93XX_ADC + tristate "Cirrus Logic EP93XX ADC driver" + depends on ARCH_EP93XX || COMPILE_TEST + depends on HAS_IOMEM + help + Driver for the ADC module on the EP93XX series of SoC from Cirrus Logic. + It's recommended to switch on CONFIG_HIGH_RES_TIMERS option, in this + case driver will reduce its CPU usage by 90% in some use cases. + + To compile this driver as a module, choose M here: the module will be + called ep93xx_adc. + +config EXYNOS_ADC + tristate "Exynos ADC driver support" + depends on ARCH_EXYNOS || ARCH_S3C64XX || ARCH_S5PV210 || (OF && COMPILE_TEST) + depends on HAS_IOMEM + help + Driver for the ADC block found in the Samsung S3C6410, S5Pv210 and + Exynos SoCs. + Choose Y here only if you build for such Samsung SoC. + + To compile this driver as a module, choose M here: the module will be + called exynos_adc. + +config MXS_LRADC_ADC + tristate "Freescale i.MX23/i.MX28 LRADC ADC" + depends on MFD_MXS_LRADC + select IIO_BUFFER + select IIO_TRIGGERED_BUFFER + help + Say yes here to build support for the ADC functions of the + i.MX23/i.MX28 LRADC. This includes general-purpose ADC readings, + battery voltage measurement, and die temperature measurement. + + This driver can also be built as a module. If so, the module will be + called mxs-lradc-adc. + +config FSL_MX25_ADC + tristate "Freescale MX25 ADC driver" + depends on MFD_MX25_TSADC + help + Generic Conversion Queue driver used for general purpose ADC in the + MX25. This driver supports single measurements using the MX25 ADC. + +config GEHC_PMC_ADC + tristate "GE HealthCare PMC ADC driver" + depends on I2C + help + Say yes here to build support for the GE HealthCare PMC 16-bit + 16-Channel ADC. + + To compile this driver as a module, choose M here: the module will be + called gehc-pmc-adc. + +config HI8435 + tristate "Holt Integrated Circuits HI-8435 threshold detector" + select IIO_TRIGGERED_EVENT + depends on SPI + help + If you say yes here you get support for Holt Integrated Circuits + HI-8435 chip. + + This driver can also be built as a module. If so, the module will be + called hi8435. + +config HX711 + tristate "AVIA HX711 ADC for weight cells" + depends on GPIOLIB + select IIO_BUFFER + select IIO_TRIGGERED_BUFFER + help + If you say yes here you get support for AVIA HX711 ADC which is used + for weigh cells + + This driver uses two GPIOs, one acts as the clock and controls the + channel selection and gain, the other one is used for the measurement + data + + Currently the raw value is read from the chip and delivered. + To get an actual weight one needs to subtract the + zero offset and multiply by a scale factor. + This should be done in userspace. + + This driver can also be built as a module. If so, the module will be + called hx711. + +config INA2XX_ADC + tristate "Texas Instruments INA2xx Power Monitors IIO driver" + depends on I2C && !SENSORS_INA2XX + select REGMAP_I2C + select IIO_BUFFER + select IIO_KFIFO_BUF + help + Say yes here to build support for TI INA2xx family of Power Monitors. + This driver is mutually exclusive with the HWMON version. + +config INGENIC_ADC + tristate "Ingenic JZ47xx SoCs ADC driver" + depends on MIPS || COMPILE_TEST + select IIO_BUFFER + help + Say yes here to build support for the Ingenic JZ47xx SoCs ADC unit. + + This driver can also be built as a module. If so, the module will be + called ingenic_adc. + +config INTEL_DC_TI_ADC + tristate "Intel Bay Trail / Cherry Trail Dollar Cove TI ADC driver" + depends on INTEL_SOC_PMIC_CHTDC_TI + help + Say yes here to have support for the Dollar Cove TI PMIC ADC device. + Depending on platform configuration, this general purpose ADC can be + used for sensors such as battery voltage and thermal resistors. + + To compile this driver as a module, choose M here: the module will be + called intel_dc_ti_adc. + +config INTEL_MRFLD_ADC + tristate "Intel Merrifield Basin Cove ADC driver" + depends on INTEL_SOC_PMIC_MRFLD + help + Say yes here to have support for Basin Cove power management IC (PMIC) ADC + device. Depending on platform configuration, this general purpose ADC can + be used for sampling sensors such as thermal resistors. + + To compile this driver as a module, choose M here: the module will be + called intel_mrfld_adc. + +config IMX7D_ADC + tristate "Freescale IMX7D ADC driver" + depends on ARCH_MXC || COMPILE_TEST + depends on HAS_IOMEM + help + Say yes here to build support for IMX7D ADC. + + This driver can also be built as a module. If so, the module will be + called imx7d_adc. + +config IMX8QXP_ADC + tristate "NXP IMX8QXP ADC driver" + depends on ARCH_MXC || COMPILE_TEST + depends on HAS_IOMEM + help + Say yes here to build support for IMX8QXP ADC. + + This driver can also be built as a module. If so, the module will be + called imx8qxp-adc. + +config IMX93_ADC + tristate "IMX93 ADC driver" + depends on ARCH_MXC || COMPILE_TEST + depends on HAS_IOMEM + help + Say yes here to build support for IMX93 ADC. + + This driver can also be built as a module. If so, the module will be + called imx93_adc. + +config LP8788_ADC + tristate "LP8788 ADC driver" + depends on MFD_LP8788 + help + Say yes here to build support for TI LP8788 ADC. + + To compile this driver as a module, choose M here: the module will be + called lp8788_adc. + +config LPC18XX_ADC + tristate "NXP LPC18xx ADC driver" + depends on ARCH_LPC18XX || COMPILE_TEST + depends on HAS_IOMEM + help + Say yes here to build support for NXP LPC18XX ADC. + + To compile this driver as a module, choose M here: the module will be + called lpc18xx_adc. + +config LPC32XX_ADC + tristate "NXP LPC32XX ADC" + depends on ARCH_LPC32XX || COMPILE_TEST + depends on HAS_IOMEM + help + Say yes here to build support for the integrated ADC inside the + LPC32XX SoC. Note that this feature uses the same hardware as the + touchscreen driver, so you should either select only one of the two + drivers (lpc32xx_adc or lpc32xx_ts) or, in the OpenFirmware case, + activate only one via device tree selection. Provides direct access + via sysfs. + +config LTC2309 + tristate "Linear Technology LTC2309 ADC driver" + depends on I2C + help + Say yes here to build support for Linear Technology LTC2309, a low + noise, low power, 8-channel, 12-bit SAR ADC + + This driver can also be built as a module. If so, the module will + be called ltc2309. + +config LTC2378 + tristate "Analog Devices LTC2378 ADC driver" + depends on SPI + depends on REGULATOR || COMPILE_TEST + depends on GPIOLIB + depends on PWM + select IIO_BUFFER + select IIO_BUFFER_DMA + select IIO_BUFFER_DMAENGINE + select IIO_TRIGGERED_BUFFER + select SPI_OFFLOAD + select SPI_OFFLOAD_TRIGGER_PWM + help + Say yes here to build support for Analog Devices LTC2378-20 and + similar analog to digital converters. + + This driver can also be built as a module. If so, the module will + be called ltc2378. + +config LTC2471 + tristate "Linear Technology LTC2471 and LTC2473 ADC driver" + depends on I2C + help + Say yes here to build support for Linear Technology LTC2471 and + LTC2473 16-bit I2C ADC. + + This driver can also be built as a module. If so, the module will + be called ltc2471. + +config LTC2485 + tristate "Linear Technology LTC2485 ADC driver" + depends on I2C + help + Say yes here to build support for Linear Technology LTC2485 ADC. + + To compile this driver as a module, choose M here: the module will be + called ltc2485. + +config LTC2496 + tristate "Linear Technology LTC2496 ADC driver" + depends on SPI + help + Say yes here to build support for Linear Technology LTC2496 + 16-Bit 8-/16-Channel Delta Sigma ADC. + + To compile this driver as a module, choose M here: the module will be + called ltc2496. + +config LTC2497 + tristate "Linear Technology LTC2497 ADC driver" + depends on I2C + help + Say yes here to build support for Linear Technology LTC2497 + 16-Bit 8-/16-Channel Delta Sigma ADC. + + To compile this driver as a module, choose M here: the module will be + called ltc2497. + +config MAX1027 + tristate "Maxim max1027 ADC driver" + depends on SPI + select IIO_BUFFER + select IIO_TRIGGERED_BUFFER + help + Say yes here to build support for Maxim SPI {10,12}-bit ADC models: + max1027, max1029, max1031, max1227, max1229 and max1231. + + To compile this driver as a module, choose M here: the module will be + called max1027. + +config MAX11100 + tristate "Maxim max11100 ADC driver" + depends on SPI_MASTER + help + Say yes here to build support for Maxim max11100 SPI ADC + + To compile this driver as a module, choose M here: the module will be + called max11100. + +config MAX1118 + tristate "Maxim max1117/max1118/max1119 ADCs driver" + depends on SPI + select IIO_BUFFER + select IIO_TRIGGERED_BUFFER + help + Say yes here to build support for Maxim max1117/max1118/max1119 + 8-bit, dual-channel ADCs. + + To compile this driver as a module, choose M here: the module will be + called max1118. + +config MAX11205 + tristate "Maxim max11205 ADC driver" + depends on SPI + select AD_SIGMA_DELTA + select IIO_BUFFER + select IIO_TRIGGERED_BUFFER + + help + Say yes here to build support for Maxim max11205 16-bit, single-channel + ultra-low power delta-sigma ADC. + + To compile this driver as a module, choose M here: the module will be + called max11205. + +config MAX11410 + tristate "Analog Devices MAX11410 ADC driver" + depends on SPI + select REGMAP_SPI + select IIO_BUFFER + select IIO_TRIGGER + select IIO_TRIGGERED_BUFFER + help + Say yes here to build support for Analog Devices MAX11410 ADCs. + + To compile this driver as a module, choose M here: the module will be + called max11410. + +config MAX1241 + tristate "Maxim max1241 ADC driver" + depends on SPI_MASTER + help + Say yes here to build support for Maxim max1241 12-bit, single-channel + ADC. + + To compile this driver as a module, choose M here: the module will be + called max1241. + +config MAX1363 + tristate "Maxim max1363 ADC driver" + depends on I2C + select IIO_BUFFER + select IIO_TRIGGERED_BUFFER + help + Say yes here to build support for many Maxim i2c analog to digital + converters (ADC). (max1361, max1362, max1363, max1364, max1036, + max1037, max1038, max1039, max1136, max1136, max1137, max1138, + max1139, max1236, max1237, max11238, max1239, max11600, max11601, + max11602, max11603, max11604, max11605, max11606, max11607, + max11608, max11609, max11610, max11611, max11612, max11613, + max11614, max11615, max11616, max11617, max11644, max11645, + max11646, max11647) Provides direct access via sysfs and buffered + data via the iio dev interface. + + To compile this driver as a module, choose M here: the module will be + called max1363. + +config MAX14001 + tristate "Analog Devices MAX14001/MAX14002 ADC driver" + depends on SPI + select REGMAP + help + Say yes here to build support for Analog Devices MAX14001/MAX14002 + Configurable, Isolated 10-bit ADCs for Multi-Range Binary Inputs. + + To compile this driver as a module, choose M here: the module will be + called max14001. + +config MAX34408 + tristate "Maxim max34408/max344089 ADC driver" + depends on I2C + select REGMAP_I2C + help + Say yes here to build support for Maxim max34408/max34409 current sense + monitor with 8-bits ADC interface with overcurrent delay/threshold and + shutdown delay. + + To compile this driver as a module, choose M here: the module will be + called max34408. + +config MAX77541_ADC + tristate "Analog Devices MAX77541 ADC driver" + depends on MFD_MAX77541 + help + This driver controls a Analog Devices MAX77541 ADC + via I2C bus. This device has one adc. Say yes here to build + support for Analog Devices MAX77541 ADC interface. + + To compile this driver as a module, choose M here: + the module will be called max77541-adc. + +config MAX9611 + tristate "Maxim max9611/max9612 ADC driver" + depends on I2C + help + Say yes here to build support for Maxim max9611/max9612 current sense + amplifier with 12-bits ADC interface. + + To compile this driver as a module, choose M here: the module will be + called max9611. + +config MCP320X + tristate "Microchip Technology MCP3x01/02/04/08 and MCP3550/1/3" + depends on SPI + help + Say yes here to build support for Microchip Technology's + MCP3001, MCP3002, MCP3004, MCP3008, MCP3201, MCP3202, MCP3204, + MCP3208, MCP3301, MCP3550, MCP3551 and MCP3553 analog to digital + converters. + + This driver can also be built as a module. If so, the module will be + called mcp320x. + +config MCP3422 + tristate "Microchip Technology MCP3421/2/3/4/5/6/7/8 driver" + depends on I2C + help + Say yes here to build support for Microchip Technology's MCP3421 + MCP3422, MCP3423, MCP3424, MCP3425, MCP3426, MCP3427 or MCP3428 + analog to digital converters. + + This driver can also be built as a module. If so, the module will be + called mcp3422. + +config MCP3564 + tristate "Microchip Technology MCP3461/2/4/R, MCP3561/2/4/R driver" + depends on SPI + depends on IIO + help + Say yes here to build support for Microchip Technology's MCP3461, + MCP3462, MCP3464, MCP3461R, MCP3462R, MCP3464R, MCP3561, MCP3562, + MCP3564, MCP3561R, MCP3562R and MCP3564R analog to digital + converters. + + This driver can also be built as a module. If so, the module will be + called mcp3564. + +config MCP3911 + tristate "Microchip Technology MCP3911 driver" + depends on SPI + select IIO_BUFFER + select IIO_TRIGGERED_BUFFER + help + Say yes here to build support for one of the following + Microchip Technology's analog to digital converters: + MCP3910, MCP3911, MCP3912, MCP3913, MCP3914, + MCP3918 and MCP3919. + + This driver can also be built as a module. If so, the module will be + called mcp3911. + +config MEDIATEK_MT6323_AUXADC + tristate "MediaTek MT6323 PMIC AUXADC driver" + depends on MFD_MT6397 + help + Say yes here to enable support for MediaTek MT6323 PMIC Auxiliary ADC. + This driver provides multiple channels for system monitoring, + such as battery voltage, PMIC temperature, and others. + + This driver can also be built as a module. If so, the module will be + called mt6323-auxadc. + +config MEDIATEK_MT6359_AUXADC + tristate "MediaTek MT6359 PMIC AUXADC driver" + depends on MFD_MT6397 + help + Say yes here to enable support for MediaTek MT6357, MT6358 and + MT6359 PMICs Auxiliary ADC. + This driver provides multiple channels for system monitoring, + such as battery voltage, PMIC temperature, and others. + + This driver can also be built as a module. If so, the module will be + called mt6359-auxadc. + +config MEDIATEK_MT6360_ADC + tristate "Mediatek MT6360 ADC driver" + depends on MFD_MT6360 + select IIO_BUFFER + select IIO_TRIGGERED_BUFFER + help + Say Y here to enable MT6360 ADC support. + Integrated for System Monitoring includes + is used in smartphones and tablets and supports a 11 channel + general purpose ADC. + +config MEDIATEK_MT6370_ADC + tristate "MediaTek MT6370 ADC driver" + depends on MFD_MT6370 + help + Say yes here to enable MediaTek MT6370 ADC support. + + This ADC driver provides 9 channels for system monitoring (charger + current, voltage, and temperature). + + This driver can also be built as a module. If so, the module + will be called "mt6370-adc". + +config MEDIATEK_MT6577_AUXADC + tristate "MediaTek AUXADC driver" + depends on ARCH_MEDIATEK || COMPILE_TEST + depends on HAS_IOMEM + help + Say yes here to enable support for MediaTek mt65xx AUXADC. + + The driver supports immediate mode operation to read from one of sixteen + channels (external or internal). + + This driver can also be built as a module. If so, the module will be + called mt6577_auxadc. + +config MEN_Z188_ADC + tristate "MEN 16z188 ADC IP Core support" + depends on MCB + help + Say yes here to enable support for the MEN 16z188 ADC IP-Core on a MCB + carrier. + + This driver can also be built as a module. If so, the module will be + called men_z188_adc. + +config MESON_SARADC + tristate "Amlogic Meson SAR ADC driver" + default ARCH_MESON + depends on OF && COMMON_CLK && (ARCH_MESON || COMPILE_TEST) + select REGMAP_MMIO + help + Say yes here to build support for the SAR ADC found in Amlogic Meson + SoCs. + + To compile this driver as a module, choose M here: the + module will be called meson_saradc. + +config MP2629_ADC + tristate "Monolithic MP2629 ADC driver" + depends on MFD_MP2629 + help + Say yes to have support for battery charger IC MP2629 ADC device + accessed over I2C. + + This driver provides ADC conversion of system, input power supply + and battery voltage & current information. + +config NAU7802 + tristate "Nuvoton NAU7802 ADC driver" + depends on I2C + help + Say yes here to build support for Nuvoton NAU7802 ADC. + + To compile this driver as a module, choose M here: the + module will be called nau7802. + +config NCT7201 + tristate "Nuvoton Instruments NCT7201 and NCT7202 Power Monitor" + depends on I2C + select REGMAP_I2C + help + If you say yes here you get support for the Nuvoton NCT7201 and + NCT7202 Voltage Monitor. + + This driver can also be built as a module. If so, the module + will be called nct7201. + +config NPCM_ADC + tristate "Nuvoton NPCM ADC driver" + depends on ARCH_NPCM || COMPILE_TEST + depends on HAS_IOMEM + help + Say yes here to build support for Nuvoton NPCM ADC. + + This driver can also be built as a module. If so, the module + will be called npcm_adc. + +config NXP_SAR_ADC + tristate "NXP S32G SAR-ADC driver" + depends on ARCH_S32 || COMPILE_TEST + select IIO_BUFFER + select IIO_TRIGGERED_BUFFER + help + Say yes here to build support for S32G platforms + analog-to-digital converter. + + This driver can also be built as a module. If so, the module will be + called nxp_sar_adc. + +config PAC1921 + tristate "Microchip Technology PAC1921 driver" + depends on I2C + select REGMAP_I2C + select IIO_BUFFER + select IIO_TRIGGERED_BUFFER + help + Say yes here to build support for Microchip Technology's PAC1921 + High-Side Power/Current Monitor with Analog Output. + + This driver can also be built as a module. If so, the module + will be called pac1921. + +config PAC1934 + tristate "Microchip Technology PAC1934 driver" + depends on I2C + help + Say yes here to build support for Microchip Technology's PAC1931, + PAC1932, PAC1933, PAC1934 Single/Multi-Channel Power Monitor with + Accumulator. + + This driver can also be built as a module. If so, the module + will be called pac1934. + +config PALMAS_GPADC + tristate "TI Palmas General Purpose ADC" + depends on MFD_PALMAS + help + Palmas series pmic chip by Texas Instruments (twl6035/6037) + is used in smartphones and tablets and supports a 16 channel + general purpose ADC. + +config QCOM_VADC_COMMON + tristate + +config QCOM_PM8XXX_XOADC + tristate "Qualcomm SSBI PM8xxx PMIC XOADCs" + depends on MFD_PM8XXX + select QCOM_VADC_COMMON + help + ADC driver for the XOADC portions of the Qualcomm PM8xxx PMICs + using SSBI transport: PM8018, PM8038, PM8058, PM8921. + + To compile this driver as a module, choose M here: the module + will be called qcom-pm8xxx-xoadc. + +config QCOM_SPMI_RRADC + tristate "Qualcomm SPMI RRADC" + depends on MFD_SPMI_PMIC + help + This is for the PMIC Round Robin ADC driver. + + This driver exposes the battery ID resistor, battery thermal, PMIC die + temperature, charger USB in and DC in voltage and current. + + To compile this driver as a module, choose M here: the module will + be called qcom-qpmi-rradc. + +config QCOM_SPMI_IADC + tristate "Qualcomm SPMI PMIC current ADC" + depends on SPMI + select REGMAP_SPMI + help + This is the IIO Current ADC driver for Qualcomm QPNP IADC Chip. + + The driver supports single mode operation to read from one of two + channels (external or internal). Hardware have additional + channels internally used for gain and offset calibration. + + To compile this driver as a module, choose M here: the module will + be called qcom-spmi-iadc. + +config QCOM_SPMI_VADC + tristate "Qualcomm SPMI PMIC voltage ADC" + depends on SPMI + select REGMAP_SPMI + select QCOM_VADC_COMMON + help + This is the IIO Voltage ADC driver for Qualcomm QPNP VADC Chip. + + The driver supports multiple channels read. The VADC is a 15-bit + sigma-delta ADC. Some of the channels are internally used for + calibration. + + To compile this driver as a module, choose M here: the module will + be called qcom-spmi-vadc. + +config QCOM_SPMI_ADC5 + tristate "Qualcomm SPMI PMIC5 ADC" + depends on SPMI + select REGMAP_SPMI + select QCOM_VADC_COMMON + help + This is the IIO Voltage PMIC5 ADC driver for Qualcomm Technologies Inc. + + The driver supports multiple channels read. The ADC is a 16-bit + sigma-delta ADC. The hardware supports calibrated results for + conversion requests and clients include reading voltage phone + power, on board system thermistors connected to the PMIC ADC, + PMIC die temperature, charger temperature, battery current, USB voltage + input, voltage signals connected to supported PMIC GPIO inputs. The + hardware supports internal pull-up for thermistors and can choose between + a 100k, 30k and 400k pull up using the ADC channels. + + To compile this driver as a module, choose M here: the module will + be called qcom-spmi-adc5. + +config QCOM_SPMI_ADC5_GEN3 + tristate "Qualcomm SPMI PMIC5 GEN3 ADC" + depends on SPMI && THERMAL + select REGMAP_SPMI + select QCOM_VADC_COMMON + select AUXILIARY_BUS + help + IIO Voltage PMIC5 Gen3 ADC driver for Qualcomm Technologies Inc. + + The driver supports reading multiple channels. The ADC is a 16-bit + sigma-delta ADC. The hardware supports calibrated results for + conversion requests and clients include reading phone power supply + voltage, on board system thermistors connected to the PMIC ADC, + PMIC die temperature, charger temperature, battery current, USB + voltage input and voltage signals connected to supported PMIC GPIO + pins. The hardware supports internal pull-up for thermistors and can + choose between a 30k, 100k or 400k ohm pull up using the ADC channels. + + In addition, the same driver supports ADC thermal monitoring devices + too. They appear as thermal zones with multiple trip points. A thermal + client sets threshold temperature for both warm and cool trips and + gets updated when a threshold is reached. + + To compile this driver as a module, choose M here: the module will + be called qcom-spmi-adc5-gen3. + +config RCAR_GYRO_ADC + tristate "Renesas R-Car GyroADC driver" + depends on ARCH_RCAR_GEN2 || COMPILE_TEST + help + Say yes here to build support for the GyroADC found in Renesas + R-Car Gen2 SoCs. This block is a simple SPI offload engine for + reading data out of attached compatible ADCs in a round-robin + fashion. Up to 4 or 8 ADC channels are supported by this block, + depending on which ADCs are attached. + + To compile this driver as a module, choose M here: the + module will be called rcar-gyroadc. + +config RN5T618_ADC + tristate "ADC for the RN5T618/RC5T619 family of chips" + depends on MFD_RN5T618 + help + Say yes here to build support for the integrated ADC inside the + RN5T618/619 series PMICs: + + This driver can also be built as a module. If so, the module + will be called rn5t618-adc. + +config ROHM_BD79112 + tristate "Rohm BD79112 ADC driver" + depends on SPI && GPIOLIB + select REGMAP_SPI + select IIO_ADC_HELPER + help + Say yes here to build support for the ROHM BD79112 ADC. The + ROHM BD79112 is a 12-bit, 32-channel, SAR ADC. Analog inputs + can also be used for GPIO. + +config ROHM_BD79124 + tristate "Rohm BD79124 ADC driver" + depends on I2C && GPIOLIB + select REGMAP_I2C + select IIO_ADC_HELPER + help + Say yes here to build support for the ROHM BD79124 ADC. The + ROHM BD79124 is a 12-bit, 8-channel, SAR ADC. The ADC supports + also an automatic measurement mode, with an alarm interrupt for + out-of-window measurements. The window is configurable for each + channel. + +config ROCKCHIP_SARADC + tristate "Rockchip SARADC driver" + depends on ARCH_ROCKCHIP || COMPILE_TEST + depends on RESET_CONTROLLER + select IIO_BUFFER + select IIO_TRIGGERED_BUFFER + help + Say yes here to build support for the SARADC found in SoCs from + Rockchip. + + To compile this driver as a module, choose M here: the + module will be called rockchip_saradc. + +config RICHTEK_RTQ6056 + tristate "Richtek RTQ6056 Current and Power Monitor ADC" + depends on I2C + select REGMAP_I2C + select IIO_BUFFER + select IIO_TRIGGERED_BUFFER + help + Say yes here to enable RQT6056 ADC support. + RTQ6056 is a high accuracy current-sense monitor with I2C and SMBus + compatible interface, and the device provides full information for + system by reading out the load current and power. + + This driver can also be built as a module. If so, the module will be + called rtq6056. + +config RZG2L_ADC + tristate "Renesas RZ/G2L ADC driver" + depends on ARCH_RZG2L || COMPILE_TEST + select IIO_ADC_HELPER + help + Say yes here to build support for the ADC found in Renesas + RZ/G2L family. + + To compile this driver as a module, choose M here: the + module will be called rzg2l_adc. + +config RZN1_ADC + tristate "Renesas RZ/N1 ADC driver" + depends on ARCH_RZN1 || COMPILE_TEST + help + Say yes here to build support for the ADC found in Renesas + RZ/N1 family. + + To compile this driver as a module, choose M here: the + module will be called rzn1-adc. + +config RZT2H_ADC + tristate "Renesas RZ/T2H / RZ/N2H ADC driver" + depends on ARCH_RENESAS || COMPILE_TEST + select IIO_ADC_HELPER + help + Say yes here to build support for the ADC found in Renesas + RZ/T2H / RZ/N2H SoCs. + + To compile this driver as a module, choose M here: the + module will be called rzt2h_adc. + +config SC27XX_ADC + tristate "Spreadtrum SC27xx series PMICs ADC" + depends on MFD_SC27XX_PMIC || COMPILE_TEST + help + Say yes here to build support for the integrated ADC inside the + Spreadtrum SC27xx series PMICs. + + This driver can also be built as a module. If so, the module + will be called sc27xx_adc. + +config SOPHGO_CV1800B_ADC + tristate "Sophgo CV1800B SARADC" + depends on ARCH_SOPHGO || COMPILE_TEST + help + Say yes here to build support for the SARADC integrated inside + the Sophgo CV1800B SoC. + + This driver can also be built as a module. If so, the module + will be called sophgo_cv1800b_adc. + +config SPEAR_ADC + tristate "ST SPEAr ADC" + depends on PLAT_SPEAR || COMPILE_TEST + depends on HAS_IOMEM + help + Say yes here to build support for the integrated ADC inside the + ST SPEAr SoC. Provides direct access via sysfs. + + To compile this driver as a module, choose M here: the + module will be called spear_adc. + +config SD_ADC_MODULATOR + tristate "Generic sigma delta modulator" + select IIO_BUFFER + select IIO_TRIGGERED_BUFFER + select IIO_BACKEND + help + Select this option to enables sigma delta modulator. This driver can + support generic sigma delta modulators. + + This driver can also be built as a module. If so, the module + will be called sd_adc_modulator. + +config STM32_ADC_CORE + tristate "STMicroelectronics STM32 adc core" + depends on ARCH_STM32 || COMPILE_TEST + depends on OF + depends on REGULATOR + depends on HAS_IOMEM + select IIO_BUFFER + select MFD_STM32_TIMERS + select IIO_STM32_TIMER_TRIGGER + select IIO_TRIGGERED_BUFFER + help + Select this option to enable the core driver for STMicroelectronics + STM32 analog-to-digital converter (ADC). + + This driver can also be built as a module. If so, the module + will be called stm32-adc-core. + +config STM32_ADC + tristate "STMicroelectronics STM32 adc" + depends on STM32_ADC_CORE + help + Say yes here to build support for STMicroelectronics stm32 Analog + to Digital Converter (ADC). + + This driver can also be built as a module. If so, the module + will be called stm32-adc. + +config STM32_DFSDM_CORE + tristate "STMicroelectronics STM32 DFSDM core" + depends on (ARCH_STM32 && OF) || COMPILE_TEST + select REGMAP + select REGMAP_MMIO + help + Select this option to enable the driver for STMicroelectronics + STM32 digital filter for sigma delta converter. + + This driver can also be built as a module. If so, the module + will be called stm32-dfsdm-core. + +config STM32_DFSDM_ADC + tristate "STMicroelectronics STM32 dfsdm adc" + depends on (ARCH_STM32 && OF) || COMPILE_TEST + select STM32_DFSDM_CORE + select REGMAP_MMIO + select IIO_BUFFER + select IIO_BUFFER_HW_CONSUMER + select IIO_TRIGGERED_BUFFER + select IIO_BACKEND + help + Select this option to support ADCSigma delta modulator for + STMicroelectronics STM32 digital filter for sigma delta converter. + + This driver can also be built as a module. If so, the module + will be called stm32-dfsdm-adc. + +config STMPE_ADC + tristate "STMicroelectronics STMPE ADC driver" + depends on OF && MFD_STMPE + help + Say yes here to build support for ST Microelectronics STMPE + built-in ADC block (stmpe811). + +config SUN4I_GPADC + tristate "Allwinner A10/A13/A31 and similar GPADCs driver" + depends on IIO + depends on MFD_SUN4I_GPADC || MACH_SUN8I + depends on THERMAL || !THERMAL_OF + select REGMAP_IRQ + help + Say yes here to build support for Allwinner (A10, A13 and A31) SoCs + GPADC. This ADC provides 4 channels which can be used as an ADC or as + a touchscreen input and one channel for thermal sensor. + + The thermal sensor slows down ADC readings and can be disabled by + disabling CONFIG_THERMAL_OF. However, the thermal sensor should be + enabled by default since the SoC temperature is usually more critical + than ADC readings. + + To compile this driver as a module, choose M here: the module will be + called sun4i-gpadc-iio. + +config SUN20I_GPADC + tristate "Allwinner D1/T113s/T507/R329 and similar GPADCs driver" + depends on ARCH_SUNXI || COMPILE_TEST + select IIO_ADC_HELPER + help + Say yes here to build support for Allwinner (D1, T113, T507 and R329) + SoCs GPADC. This ADC provides up to 16 channels. + + To compile this driver as a module, choose M here: the module will be + called sun20i-gpadc-iio. + +config TI_ADC081C + tristate "Texas Instruments ADC081C/ADC101C/ADC121C family" + depends on I2C + select IIO_BUFFER + select IIO_TRIGGERED_BUFFER + help + If you say yes here you get support for Texas Instruments ADC081C, + ADC101C and ADC121C ADC chips. + + This driver can also be built as a module. If so, the module will be + called ti-adc081c. + +config TI_ADC0832 + tristate "Texas Instruments ADC0831/ADC0832/ADC0834/ADC0838" + depends on SPI + select IIO_BUFFER + select IIO_TRIGGERED_BUFFER + help + If you say yes here you get support for Texas Instruments ADC0831, + ADC0832, ADC0834, ADC0838 ADC chips. + + This driver can also be built as a module. If so, the module will be + called ti-adc0832. + +config TI_ADC084S021 + tristate "Texas Instruments ADC084S021" + depends on SPI + select IIO_BUFFER + select IIO_TRIGGERED_BUFFER + help + If you say yes here you get support for Texas Instruments ADC084S021 + chips. + + This driver can also be built as a module. If so, the module will be + called ti-adc084s021. + +config TI_ADC108S102 + tristate "Texas Instruments ADC108S102 and ADC128S102 driver" + depends on SPI + select IIO_BUFFER + select IIO_TRIGGERED_BUFFER + help + Say yes here to build support for Texas Instruments ADC108S102 and + ADC128S102 ADC. + + To compile this driver as a module, choose M here: the module will + be called ti-adc108s102. + +config TI_ADC12138 + tristate "Texas Instruments ADC12130/ADC12132/ADC12138" + depends on SPI + select IIO_BUFFER + select IIO_TRIGGERED_BUFFER + help + If you say yes here you get support for Texas Instruments ADC12130, + ADC12132 and ADC12138 chips. + + This driver can also be built as a module. If so, the module will be + called ti-adc12138. + +config TI_ADC128S052 + tristate "Texas Instruments ADC128S052/ADC122S021/ADC124S021" + depends on SPI + help + If you say yes here you get support for Texas Instruments ADC128S052, + ADC122S021, ADC124S021 and ROHM Semiconductor BD79104 chips. + + This driver can also be built as a module. If so, the module will be + called ti-adc128s052. + +config TI_ADC161S626 + tristate "Texas Instruments ADC161S626 1-channel differential ADC" + depends on SPI + select IIO_BUFFER + select IIO_TRIGGERED_BUFFER + help + If you say yes here you get support for Texas Instruments ADC141S626, + and ADC161S626 chips. + + This driver can also be built as a module. If so, the module will be + called ti-adc161s626. + +config TI_ADS1015 + tristate "Texas Instruments ADS1015 ADC" + depends on I2C + select REGMAP_I2C + select IIO_BUFFER + select IIO_TRIGGERED_BUFFER + help + If you say yes here you get support for Texas Instruments ADS1015 + ADC chip. + + This driver can also be built as a module. If so, the module will be + called ti-ads1015. + +config TI_ADS1018 + tristate "Texas Instruments ADS1018 ADC" + depends on SPI + select IIO_BUFFER + select IIO_TRIGGERED_BUFFER + help + If you say yes here you get support for Texas Instruments ADS1018 and + ADS1118 ADC chips. + + This driver can also be built as a module. If so, the module will be + called ti-ads1018. + +config TI_ADS1100 + tristate "Texas Instruments ADS1100 and ADS1000 ADC" + depends on I2C + help + If you say yes here you get support for Texas Instruments ADS1100 and + ADS1000 ADC chips. + + This driver can also be built as a module. If so, the module will be + called ti-ads1100. + +config TI_ADS1119 + tristate "Texas Instruments ADS1119 ADC" + depends on I2C + select IIO_BUFFER + select IIO_TRIGGERED_BUFFER + help + If you say yes here you get support for Texas Instruments ADS1119 + ADC chip. + + This driver can also be built as a module. If so, the module will be + called ti-ads1119. + +config TI_ADS112C14 + tristate "Texas Instruments ADS112C14/ADS122C14" + depends on I2C + select CRC8 + select REGMAP + help + If you say yes here you get support for Texas Instruments ADS112C14, + ADS122C14 ADC chips. + + This driver can also be built as a module. If so, the module will be + called ti-ads112c14. + +config TI_ADS124S08 + tristate "Texas Instruments ADS124S08" + depends on SPI + select IIO_BUFFER + select IIO_TRIGGERED_BUFFER + help + If you say yes here you get support for Texas Instruments ADS124S08 + and ADS124S06 ADC chips + + This driver can also be built as a module. If so, the module will be + called ti-ads124s08. + +config TI_ADS1298 + tristate "Texas Instruments ADS1298" + depends on SPI + select IIO_BUFFER + select IIO_KFIFO_BUF + help + If you say yes here you get support for Texas Instruments ADS1298 + medical ADC chips + + This driver can also be built as a module. If so, the module will be + called ti-ads1298. + +config TI_ADS131E08 + tristate "Texas Instruments ADS131E08" + depends on SPI + select IIO_BUFFER + select IIO_TRIGGERED_BUFFER + help + Say yes here to get support for Texas Instruments ADS131E04, ADS131E06 + and ADS131E08 chips. + + This driver can also be built as a module. If so, the module will be + called ti-ads131e08. + +config TI_ADS131M02 + tristate "Texas Instruments ADS131M02" + depends on SPI && REGULATOR + select CRC_ITU_T + help + Say yes here to get support for Texas Instruments ADS131M02, ADS131M03, + ADS131M04, ADS131M06 and ADS131M08 chips. + + This driver can also be built as a module. If so, the module will be + called ti-ads131m02. + +config TI_ADS7138 + tristate "Texas Instruments ADS7128 and ADS7138 ADC driver" + depends on I2C + help + If you say yes here you get support for Texas Instruments ADS7128 and + ADS7138 8-channel A/D converters with 12-bit resolution. + + This driver can also be built as a module. If so, the module will be + called ti-ads7138. + +config TI_ADS7924 + tristate "Texas Instruments ADS7924 ADC" + depends on I2C + select REGMAP_I2C + help + If you say yes here you get support for Texas Instruments ADS7924 + 4 channels, 12-bit I2C ADC chip. + + This driver can also be built as a module. If so, the module will be + called ti-ads7924. + +config TI_ADS7950 + tristate "Texas Instruments ADS7950 ADC driver" + depends on SPI && GPIOLIB + select IIO_BUFFER + select IIO_TRIGGERED_BUFFER + help + Say yes here to build support for Texas Instruments ADS7950, ADS7951, + ADS7952, ADS7953, ADS7954, ADS7955, ADS7956, ADS7957, ADS7958, ADS7959. + ADS7960, ADS7961. + + To compile this driver as a module, choose M here: the + module will be called ti-ads7950. + +config TI_ADS8344 + tristate "Texas Instruments ADS8344" + depends on SPI + help + If you say yes here you get support for Texas Instruments ADS8344 + ADC chips + + This driver can also be built as a module. If so, the module will be + called ti-ads8344. + +config TI_ADS8688 + tristate "Texas Instruments ADS8688" + depends on SPI + select IIO_BUFFER + select IIO_TRIGGERED_BUFFER + help + If you say yes here you get support for Texas Instruments ADS8684 and + and ADS8688 ADC chips + + This driver can also be built as a module. If so, the module will be + called ti-ads8688. + +config TI_AM335X_ADC + tristate "TI's AM335X ADC driver" + depends on MFD_TI_AM335X_TSCADC && HAS_DMA + select IIO_BUFFER + select IIO_KFIFO_BUF + help + Say yes here to build support for Texas Instruments ADC + driver which is also a MFD client. + + To compile this driver as a module, choose M here: the module will be + called ti_am335x_adc. + +config TI_LMP92064 + tristate "Texas Instruments LMP92064 ADC driver" + depends on SPI + select REGMAP_SPI + select IIO_BUFFER + select IIO_TRIGGERED_BUFFER + help + Say yes here to build support for the LMP92064 Precision Current and Voltage + sensor. + + This driver can also be built as a module. If so, the module will be called + ti-lmp92064. + +config TI_TLC4541 + tristate "Texas Instruments TLC4541 ADC driver" + depends on SPI + select IIO_BUFFER + select IIO_TRIGGERED_BUFFER + help + Say yes here to build support for Texas Instruments TLC4541 / TLC3541 + ADC chips. + + This driver can also be built as a module. If so, the module will be + called ti-tlc4541. + +config TI_TSC2046 + tristate "Texas Instruments TSC2046 ADC driver" + depends on SPI + select IIO_BUFFER + select IIO_TRIGGERED_BUFFER + help + Say yes here to build support for ADC functionality of Texas + Instruments TSC2046 touch screen controller. + + This driver can also be built as a module. If so, the module will be + called ti-tsc2046. + +config TWL4030_MADC + tristate "TWL4030 MADC (Monitoring A/D Converter)" + depends on TWL4030_CORE + help + This driver provides support for Triton TWL4030-MADC. The + driver supports both RT and SW conversion methods. + + This driver can also be built as a module. If so, the module will be + called twl4030-madc. + +config TWL6030_GPADC + tristate "TWL6030 GPADC (General Purpose A/D Converter) Support" + depends on TWL4030_CORE + help + Say yes here if you want support for the TWL6030/TWL6032 General + Purpose A/D Converter. This will add support for battery type + detection, battery voltage and temperature measurement, die + temperature measurement, system supply voltage, audio accessory, + USB ID detection. + + This driver can also be built as a module. If so, the module will be + called twl6030-gpadc. + +config VERSAL_SYSMON_CORE + tristate + select REGMAP + +config VERSAL_SYSMON + tristate "AMD Versal SysMon driver" + depends on ARCH_ZYNQMP || COMPILE_TEST + depends on HAS_IOMEM + select VERSAL_SYSMON_CORE + help + Say yes here to have support for the AMD/Xilinx Versal System + Monitor (SysMon). This driver provides voltage and temperature + monitoring through the IIO subsystem. + + The SysMon measures up to 160 supply voltages and reads up to + 64 temperature satellites distributed across the SoC. + + To compile this driver as a module, choose M here: the module + will be called versal-sysmon. + +config VERSAL_SYSMON_I2C + tristate "AMD Versal SysMon I2C driver" + depends on I2C + select VERSAL_SYSMON_CORE + help + Say yes here to have support for the AMD/Xilinx Versal System + Monitor (SysMon) via I2C interface. This driver enables voltage + and temperature monitoring when the Versal chip has SysMon + configured with I2C access. + + To compile this driver as a module, choose M here: the module + will be called versal-sysmon-i2c. + +config VF610_ADC + tristate "Freescale vf610 ADC driver" + depends on HAS_IOMEM + select IIO_BUFFER + select IIO_TRIGGERED_BUFFER + help + Say yes here to support for Vybrid board analog-to-digital converter. + Since the IP is used for i.MX6SLX, the driver also support i.MX6SLX. + + This driver can also be built as a module. If so, the module will be + called vf610_adc. + +config VIPERBOARD_ADC + tristate "Viperboard ADC support" + depends on MFD_VIPERBOARD && USB + help + Say yes here to access the ADC part of the Nano River + Technologies Viperboard. + + To compile this driver as a module, choose M here: the module will be + called viperboard_adc. + +config XILINX_XADC + tristate "Xilinx XADC driver" + depends on HAS_IOMEM + select IIO_BUFFER + select IIO_TRIGGERED_BUFFER + help + Say yes here to have support for the Xilinx 7 Series XADC or + UltraScale/UltraScale+ System Management Wizard. + + For the 7 Series the driver does support both the ZYNQ interface + to the XADC as well as the AXI-XADC interface. + + The driver also support the Xilinx System Management Wizard IP core + that can be used to access the System Monitor ADC on the Xilinx + UltraScale and UltraScale+ FPGAs. + + The driver can also be build as a module. If so, the module will be called + xilinx-xadc. + +config XILINX_AMS + tristate "Xilinx AMS driver" + depends on ARCH_ZYNQMP || COMPILE_TEST + depends on HAS_IOMEM + help + Say yes here to have support for the Xilinx AMS for Ultrascale/Ultrascale+ + System Monitor. With this you can measure and monitor the Voltages and + Temperature values on the SOC. + + The driver supports Voltage and Temperature monitoring on Xilinx Ultrascale + devices. + + The driver can also be built as a module. If so, the module will be called + xilinx-ams. + +endmenu diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile new file mode 100644 index 000000000..dcec0abb0 --- /dev/null +++ b/drivers/iio/adc/Makefile @@ -0,0 +1,181 @@ +# SPDX-License-Identifier: GPL-2.0 +# +# Makefile for IIO ADC drivers +# + +obj-$(CONFIG_IIO_ADC_HELPER) += industrialio-adc.o + +# When adding new entries keep the list in alphabetical order +obj-$(CONFIG_88PM886_GPADC) += 88pm886-gpadc.o +obj-$(CONFIG_AB8500_GPADC) += ab8500-gpadc.o +obj-$(CONFIG_AD_SIGMA_DELTA) += ad_sigma_delta.o +obj-$(CONFIG_AD4000) += ad4000.o +obj-$(CONFIG_AD4030) += ad4030.o +obj-$(CONFIG_AD4062) += ad4062.o +obj-$(CONFIG_AD4080) += ad4080.o +obj-$(CONFIG_AD4130) += ad4130.o +obj-$(CONFIG_AD4134) += ad4134.o +obj-$(CONFIG_AD4170_4) += ad4170-4.o +obj-$(CONFIG_AD4691) += ad4691.o +obj-$(CONFIG_AD4695) += ad4695.o +obj-$(CONFIG_AD4851) += ad4851.o +obj-$(CONFIG_AD7091R) += ad7091r-base.o +obj-$(CONFIG_AD7091R5) += ad7091r5.o +obj-$(CONFIG_AD7091R8) += ad7091r8.o +obj-$(CONFIG_AD7124) += ad7124.o +obj-$(CONFIG_AD7173) += ad7173.o +obj-$(CONFIG_AD7191) += ad7191.o +obj-$(CONFIG_AD7192) += ad7192.o +obj-$(CONFIG_AD7266) += ad7266.o +obj-$(CONFIG_AD7280) += ad7280a.o +obj-$(CONFIG_AD7291) += ad7291.o +obj-$(CONFIG_AD7292) += ad7292.o +obj-$(CONFIG_AD7298) += ad7298.o +obj-$(CONFIG_AD7380) += ad7380.o +obj-$(CONFIG_AD7405) += ad7405.o +obj-$(CONFIG_AD7476) += ad7476.o +obj-$(CONFIG_AD7606_IFACE_PARALLEL) += ad7606_par.o +obj-$(CONFIG_AD7606_IFACE_SPI) += ad7606_spi.o +obj-$(CONFIG_AD7606) += ad7606.o +obj-$(CONFIG_AD7625) += ad7625.o +obj-$(CONFIG_AD7766) += ad7766.o +obj-$(CONFIG_AD7768_1) += ad7768-1.o +obj-$(CONFIG_AD7779) += ad7779.o +obj-$(CONFIG_AD7780) += ad7780.o +obj-$(CONFIG_AD7791) += ad7791.o +obj-$(CONFIG_AD7793) += ad7793.o +obj-$(CONFIG_AD7887) += ad7887.o +obj-$(CONFIG_AD7923) += ad7923.o +obj-$(CONFIG_AD7944) += ad7944.o +obj-$(CONFIG_AD7949) += ad7949.o +obj-$(CONFIG_AD799X) += ad799x.o +obj-$(CONFIG_AD9467) += ad9467.o +obj-$(CONFIG_ADE9000) += ade9000.o +obj-$(CONFIG_ADI_AXI_ADC) += adi-axi-adc.o +obj-$(CONFIG_ASPEED_ADC) += aspeed_adc.o +obj-$(CONFIG_AT91_ADC) += at91_adc.o +obj-$(CONFIG_AT91_SAMA5D2_ADC) += at91-sama5d2_adc.o +obj-$(CONFIG_AXP20X_ADC) += axp20x_adc.o +obj-$(CONFIG_AXP288_ADC) += axp288_adc.o +obj-$(CONFIG_BCM_IPROC_ADC) += bcm_iproc_adc.o +obj-$(CONFIG_BERLIN2_ADC) += berlin2-adc.o +obj-$(CONFIG_CC10001_ADC) += cc10001_adc.o +obj-$(CONFIG_CPCAP_ADC) += cpcap-adc.o +obj-$(CONFIG_DA9150_GPADC) += da9150-gpadc.o +obj-$(CONFIG_DLN2_ADC) += dln2-adc.o +obj-$(CONFIG_ENVELOPE_DETECTOR) += envelope-detector.o +obj-$(CONFIG_EP93XX_ADC) += ep93xx_adc.o +obj-$(CONFIG_EXYNOS_ADC) += exynos_adc.o +obj-$(CONFIG_FSL_MX25_ADC) += fsl-imx25-gcq.o +obj-$(CONFIG_GEHC_PMC_ADC) += gehc-pmc-adc.o +obj-$(CONFIG_HI8435) += hi8435.o +obj-$(CONFIG_HX711) += hx711.o +obj-$(CONFIG_IMX7D_ADC) += imx7d_adc.o +obj-$(CONFIG_IMX8QXP_ADC) += imx8qxp-adc.o +obj-$(CONFIG_IMX93_ADC) += imx93_adc.o +obj-$(CONFIG_INA2XX_ADC) += ina2xx-adc.o +obj-$(CONFIG_INGENIC_ADC) += ingenic-adc.o +obj-$(CONFIG_INTEL_DC_TI_ADC) += intel_dc_ti_adc.o +obj-$(CONFIG_INTEL_MRFLD_ADC) += intel_mrfld_adc.o +obj-$(CONFIG_LP8788_ADC) += lp8788_adc.o +obj-$(CONFIG_LPC18XX_ADC) += lpc18xx_adc.o +obj-$(CONFIG_LPC32XX_ADC) += lpc32xx_adc.o +obj-$(CONFIG_LTC2309) += ltc2309.o +obj-$(CONFIG_LTC2378) += ltc2378.o +obj-$(CONFIG_LTC2471) += ltc2471.o +obj-$(CONFIG_LTC2485) += ltc2485.o +obj-$(CONFIG_LTC2496) += ltc2496.o ltc2497-core.o +obj-$(CONFIG_LTC2497) += ltc2497.o ltc2497-core.o +obj-$(CONFIG_MAX1027) += max1027.o +obj-$(CONFIG_MAX11100) += max11100.o +obj-$(CONFIG_MAX1118) += max1118.o +obj-$(CONFIG_MAX11205) += max11205.o +obj-$(CONFIG_MAX11410) += max11410.o +obj-$(CONFIG_MAX1241) += max1241.o +obj-$(CONFIG_MAX1363) += max1363.o +obj-$(CONFIG_MAX14001) += max14001.o +obj-$(CONFIG_MAX34408) += max34408.o +obj-$(CONFIG_MAX77541_ADC) += max77541-adc.o +obj-$(CONFIG_MAX9611) += max9611.o +obj-$(CONFIG_MCP320X) += mcp320x.o +obj-$(CONFIG_MCP3422) += mcp3422.o +obj-$(CONFIG_MCP3564) += mcp3564.o +obj-$(CONFIG_MCP3911) += mcp3911.o +obj-$(CONFIG_MEDIATEK_MT6323_AUXADC) += mt6323-auxadc.o +obj-$(CONFIG_MEDIATEK_MT6359_AUXADC) += mt6359-auxadc.o +obj-$(CONFIG_MEDIATEK_MT6360_ADC) += mt6360-adc.o +obj-$(CONFIG_MEDIATEK_MT6370_ADC) += mt6370-adc.o +obj-$(CONFIG_MEDIATEK_MT6577_AUXADC) += mt6577_auxadc.o +obj-$(CONFIG_MEN_Z188_ADC) += men_z188_adc.o +obj-$(CONFIG_MESON_SARADC) += meson_saradc.o +obj-$(CONFIG_MP2629_ADC) += mp2629_adc.o +obj-$(CONFIG_MXS_LRADC_ADC) += mxs-lradc-adc.o +obj-$(CONFIG_NAU7802) += nau7802.o +obj-$(CONFIG_NCT7201) += nct7201.o +obj-$(CONFIG_NPCM_ADC) += npcm_adc.o +obj-$(CONFIG_NXP_SAR_ADC) += nxp-sar-adc.o +obj-$(CONFIG_PAC1921) += pac1921.o +obj-$(CONFIG_PAC1934) += pac1934.o +obj-$(CONFIG_PALMAS_GPADC) += palmas_gpadc.o +obj-$(CONFIG_QCOM_PM8XXX_XOADC) += qcom-pm8xxx-xoadc.o +obj-$(CONFIG_QCOM_SPMI_ADC5) += qcom-spmi-adc5.o +obj-$(CONFIG_QCOM_SPMI_ADC5_GEN3) += qcom-spmi-adc5-gen3.o +obj-$(CONFIG_QCOM_SPMI_IADC) += qcom-spmi-iadc.o +obj-$(CONFIG_QCOM_SPMI_RRADC) += qcom-spmi-rradc.o +obj-$(CONFIG_QCOM_SPMI_VADC) += qcom-spmi-vadc.o +obj-$(CONFIG_QCOM_VADC_COMMON) += qcom-vadc-common.o +obj-$(CONFIG_RCAR_GYRO_ADC) += rcar-gyroadc.o +obj-$(CONFIG_RICHTEK_RTQ6056) += rtq6056.o +obj-$(CONFIG_RN5T618_ADC) += rn5t618-adc.o +obj-$(CONFIG_ROHM_BD79112) += rohm-bd79112.o +obj-$(CONFIG_ROHM_BD79124) += rohm-bd79124.o +obj-$(CONFIG_ROCKCHIP_SARADC) += rockchip_saradc.o +obj-$(CONFIG_RZG2L_ADC) += rzg2l_adc.o +obj-$(CONFIG_RZN1_ADC) += rzn1-adc.o +obj-$(CONFIG_RZT2H_ADC) += rzt2h_adc.o +obj-$(CONFIG_SC27XX_ADC) += sc27xx_adc.o +obj-$(CONFIG_SD_ADC_MODULATOR) += sd_adc_modulator.o +obj-$(CONFIG_SOPHGO_CV1800B_ADC) += sophgo-cv1800b-adc.o +obj-$(CONFIG_SPEAR_ADC) += spear_adc.o +obj-$(CONFIG_STM32_ADC_CORE) += stm32-adc-core.o +obj-$(CONFIG_STM32_ADC) += stm32-adc.o +obj-$(CONFIG_STM32_DFSDM_ADC) += stm32-dfsdm-adc.o +obj-$(CONFIG_STM32_DFSDM_CORE) += stm32-dfsdm-core.o +obj-$(CONFIG_STMPE_ADC) += stmpe-adc.o +obj-$(CONFIG_SUN20I_GPADC) += sun20i-gpadc-iio.o +obj-$(CONFIG_SUN4I_GPADC) += sun4i-gpadc-iio.o +obj-$(CONFIG_TI_ADC081C) += ti-adc081c.o +obj-$(CONFIG_TI_ADC0832) += ti-adc0832.o +obj-$(CONFIG_TI_ADC084S021) += ti-adc084s021.o +obj-$(CONFIG_TI_ADC108S102) += ti-adc108s102.o +obj-$(CONFIG_TI_ADC12138) += ti-adc12138.o +obj-$(CONFIG_TI_ADC128S052) += ti-adc128s052.o +obj-$(CONFIG_TI_ADC161S626) += ti-adc161s626.o +obj-$(CONFIG_TI_ADS1015) += ti-ads1015.o +obj-$(CONFIG_TI_ADS1018) += ti-ads1018.o +obj-$(CONFIG_TI_ADS1100) += ti-ads1100.o +obj-$(CONFIG_TI_ADS1119) += ti-ads1119.o +obj-$(CONFIG_TI_ADS112C14) += ti-ads112c14.o +obj-$(CONFIG_TI_ADS124S08) += ti-ads124s08.o +obj-$(CONFIG_TI_ADS1298) += ti-ads1298.o +obj-$(CONFIG_TI_ADS131E08) += ti-ads131e08.o +obj-$(CONFIG_TI_ADS131M02) += ti-ads131m02.o +obj-$(CONFIG_TI_ADS7138) += ti-ads7138.o +obj-$(CONFIG_TI_ADS7924) += ti-ads7924.o +obj-$(CONFIG_TI_ADS7950) += ti-ads7950.o +obj-$(CONFIG_TI_ADS8344) += ti-ads8344.o +obj-$(CONFIG_TI_ADS8688) += ti-ads8688.o +obj-$(CONFIG_TI_AM335X_ADC) += ti_am335x_adc.o +obj-$(CONFIG_TI_LMP92064) += ti-lmp92064.o +obj-$(CONFIG_TI_TLC4541) += ti-tlc4541.o +obj-$(CONFIG_TI_TSC2046) += ti-tsc2046.o +obj-$(CONFIG_TWL4030_MADC) += twl4030-madc.o +obj-$(CONFIG_TWL6030_GPADC) += twl6030-gpadc.o +obj-$(CONFIG_VERSAL_SYSMON_CORE) += versal-sysmon-core.o +obj-$(CONFIG_VERSAL_SYSMON) += versal-sysmon.o +obj-$(CONFIG_VERSAL_SYSMON_I2C) += versal-sysmon-i2c.o +obj-$(CONFIG_VF610_ADC) += vf610_adc.o +obj-$(CONFIG_VIPERBOARD_ADC) += viperboard_adc.o +obj-$(CONFIG_XILINX_AMS) += xilinx-ams.o +xilinx-xadc-y := xilinx-xadc-core.o xilinx-xadc-events.o +obj-$(CONFIG_XILINX_XADC) += xilinx-xadc.o diff --git a/drivers/iio/adc/ab8500-gpadc.c b/drivers/iio/adc/ab8500-gpadc.c new file mode 100644 index 000000000..8eaa1dd6a --- /dev/null +++ b/drivers/iio/adc/ab8500-gpadc.c @@ -0,0 +1,1202 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) ST-Ericsson SA 2010 + * + * Author: Arun R Murthy + * Author: Daniel Willerud + * Author: Johan Palsson + * Author: M'boumba Cedric Madianga + * Author: Linus Walleij + * + * AB8500 General Purpose ADC driver. The AB8500 uses reference voltages: + * VinVADC, and VADC relative to GND to do its job. It monitors main and backup + * battery voltages, AC (mains) voltage, USB cable voltage, as well as voltages + * representing the temperature of the chip die and battery, accessory + * detection by resistance measurements using relative voltages and GSM burst + * information. + * + * Some of the voltages are measured on external pins on the IC, such as + * battery temperature or "ADC aux" 1 and 2. Other voltages are internal rails + * from other parts of the ASIC such as main charger voltage, main and battery + * backup voltage or USB VBUS voltage. For this reason drivers for other + * parts of the system are required to obtain handles to the ADC to do work + * for them and the IIO driver provides arbitration among these consumers. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* GPADC register offsets and bit definitions */ + +#define AB8500_GPADC_CTRL1_REG 0x00 +/* GPADC control register 1 bits */ +#define AB8500_GPADC_CTRL1_DISABLE 0x00 +#define AB8500_GPADC_CTRL1_ENABLE BIT(0) +#define AB8500_GPADC_CTRL1_TRIG_ENA BIT(1) +#define AB8500_GPADC_CTRL1_START_SW_CONV BIT(2) +#define AB8500_GPADC_CTRL1_BTEMP_PULL_UP BIT(3) +/* 0 = use rising edge, 1 = use falling edge */ +#define AB8500_GPADC_CTRL1_TRIG_EDGE BIT(4) +/* 0 = use VTVOUT, 1 = use VRTC as pull-up supply for battery temp NTC */ +#define AB8500_GPADC_CTRL1_PUPSUPSEL BIT(5) +#define AB8500_GPADC_CTRL1_BUF_ENA BIT(6) +#define AB8500_GPADC_CTRL1_ICHAR_ENA BIT(7) + +#define AB8500_GPADC_CTRL2_REG 0x01 +#define AB8500_GPADC_CTRL3_REG 0x02 +/* + * GPADC control register 2 and 3 bits + * the bit layout is the same for SW and HW conversion set-up + */ +#define AB8500_GPADC_CTRL2_AVG_1 0x00 +#define AB8500_GPADC_CTRL2_AVG_4 BIT(5) +#define AB8500_GPADC_CTRL2_AVG_8 BIT(6) +#define AB8500_GPADC_CTRL2_AVG_16 (BIT(5) | BIT(6)) + +enum ab8500_gpadc_channel { + AB8500_GPADC_CHAN_UNUSED = 0x00, + AB8500_GPADC_CHAN_BAT_CTRL = 0x01, + AB8500_GPADC_CHAN_BAT_TEMP = 0x02, + /* This is not used on AB8505 */ + AB8500_GPADC_CHAN_MAIN_CHARGER = 0x03, + AB8500_GPADC_CHAN_ACC_DET_1 = 0x04, + AB8500_GPADC_CHAN_ACC_DET_2 = 0x05, + AB8500_GPADC_CHAN_ADC_AUX_1 = 0x06, + AB8500_GPADC_CHAN_ADC_AUX_2 = 0x07, + AB8500_GPADC_CHAN_VBAT_A = 0x08, + AB8500_GPADC_CHAN_VBUS = 0x09, + AB8500_GPADC_CHAN_MAIN_CHARGER_CURRENT = 0x0a, + AB8500_GPADC_CHAN_USB_CHARGER_CURRENT = 0x0b, + AB8500_GPADC_CHAN_BACKUP_BAT = 0x0c, + /* Only on AB8505 */ + AB8505_GPADC_CHAN_DIE_TEMP = 0x0d, + AB8500_GPADC_CHAN_ID = 0x0e, + AB8500_GPADC_CHAN_INTERNAL_TEST_1 = 0x0f, + AB8500_GPADC_CHAN_INTERNAL_TEST_2 = 0x10, + AB8500_GPADC_CHAN_INTERNAL_TEST_3 = 0x11, + /* FIXME: Applicable to all ASIC variants? */ + AB8500_GPADC_CHAN_XTAL_TEMP = 0x12, + AB8500_GPADC_CHAN_VBAT_TRUE_MEAS = 0x13, + /* FIXME: Doesn't seem to work with pure AB8500 */ + AB8500_GPADC_CHAN_BAT_CTRL_AND_IBAT = 0x1c, + AB8500_GPADC_CHAN_VBAT_MEAS_AND_IBAT = 0x1d, + AB8500_GPADC_CHAN_VBAT_TRUE_MEAS_AND_IBAT = 0x1e, + AB8500_GPADC_CHAN_BAT_TEMP_AND_IBAT = 0x1f, + /* + * Virtual channel used only for ibat conversion to ampere. + * Battery current conversion (ibat) cannot be requested as a + * single conversion but it is always requested in combination + * with other input requests. + */ + AB8500_GPADC_CHAN_IBAT_VIRTUAL = 0xFF, +}; + +#define AB8500_GPADC_AUTO_TIMER_REG 0x03 + +#define AB8500_GPADC_STAT_REG 0x04 +#define AB8500_GPADC_STAT_BUSY BIT(0) + +#define AB8500_GPADC_MANDATAL_REG 0x05 +#define AB8500_GPADC_MANDATAH_REG 0x06 +#define AB8500_GPADC_AUTODATAL_REG 0x07 +#define AB8500_GPADC_AUTODATAH_REG 0x08 +#define AB8500_GPADC_MUX_CTRL_REG 0x09 +#define AB8540_GPADC_MANDATA2L_REG 0x09 +#define AB8540_GPADC_MANDATA2H_REG 0x0A +#define AB8540_GPADC_APEAAX_REG 0x10 +#define AB8540_GPADC_APEAAT_REG 0x11 +#define AB8540_GPADC_APEAAM_REG 0x12 +#define AB8540_GPADC_APEAAH_REG 0x13 +#define AB8540_GPADC_APEAAL_REG 0x14 + +/* + * OTP register offsets + * Bank : 0x15 + */ +#define AB8500_GPADC_CAL_1 0x0F +#define AB8500_GPADC_CAL_2 0x10 +#define AB8500_GPADC_CAL_3 0x11 +#define AB8500_GPADC_CAL_4 0x12 +#define AB8500_GPADC_CAL_5 0x13 +#define AB8500_GPADC_CAL_6 0x14 +#define AB8500_GPADC_CAL_7 0x15 +/* New calibration for 8540 */ +#define AB8540_GPADC_OTP4_REG_7 0x38 +#define AB8540_GPADC_OTP4_REG_6 0x39 +#define AB8540_GPADC_OTP4_REG_5 0x3A + +#define AB8540_GPADC_DIS_ZERO 0x00 +#define AB8540_GPADC_EN_VBIAS_XTAL_TEMP 0x02 + +/* GPADC constants from AB8500 spec, UM0836 */ +#define AB8500_ADC_RESOLUTION 1024 +#define AB8500_ADC_CH_BTEMP_MIN 0 +#define AB8500_ADC_CH_BTEMP_MAX 1350 +#define AB8500_ADC_CH_DIETEMP_MIN 0 +#define AB8500_ADC_CH_DIETEMP_MAX 1350 +#define AB8500_ADC_CH_CHG_V_MIN 0 +#define AB8500_ADC_CH_CHG_V_MAX 20030 +#define AB8500_ADC_CH_ACCDET2_MIN 0 +#define AB8500_ADC_CH_ACCDET2_MAX 2500 +#define AB8500_ADC_CH_VBAT_MIN 2300 +#define AB8500_ADC_CH_VBAT_MAX 4800 +#define AB8500_ADC_CH_CHG_I_MIN 0 +#define AB8500_ADC_CH_CHG_I_MAX 1500 +#define AB8500_ADC_CH_BKBAT_MIN 0 +#define AB8500_ADC_CH_BKBAT_MAX 3200 + +/* GPADC constants from AB8540 spec */ +#define AB8500_ADC_CH_IBAT_MIN (-6000) /* mA range measured by ADC for ibat */ +#define AB8500_ADC_CH_IBAT_MAX 6000 +#define AB8500_ADC_CH_IBAT_MIN_V (-60) /* mV range measured by ADC for ibat */ +#define AB8500_ADC_CH_IBAT_MAX_V 60 +#define AB8500_GPADC_IBAT_VDROP_L (-56) /* mV */ +#define AB8500_GPADC_IBAT_VDROP_H 56 + +/* This is used to not lose precision when dividing to get gain and offset */ +#define AB8500_GPADC_CALIB_SCALE 1000 +/* + * Number of bits shift used to not lose precision + * when dividing to get ibat gain. + */ +#define AB8500_GPADC_CALIB_SHIFT_IBAT 20 + +/* Time in ms before disabling regulator */ +#define AB8500_GPADC_AUTOSUSPEND_DELAY 1 + +#define AB8500_GPADC_CONVERSION_TIME 500 /* ms */ + +enum ab8500_cal_channels { + AB8500_CAL_VMAIN = 0, + AB8500_CAL_BTEMP, + AB8500_CAL_VBAT, + AB8500_CAL_IBAT, + AB8500_CAL_NR, +}; + +/** + * struct ab8500_adc_cal_data - Table for storing gain and offset for the + * calibrated ADC channels + * @gain: Gain of the ADC channel + * @offset: Offset of the ADC channel + * @otp_calib_hi: Calibration from OTP + * @otp_calib_lo: Calibration from OTP + */ +struct ab8500_adc_cal_data { + s64 gain; + s64 offset; + u16 otp_calib_hi; + u16 otp_calib_lo; +}; + +/** + * struct ab8500_gpadc_chan_info - per-channel GPADC info + * @name: name of the channel + * @id: the internal AB8500 ID number for the channel + * @hardware_control: indicate that we want to use hardware ADC control + * on this channel, the default is software ADC control. Hardware control + * is normally only used to test the battery voltage during GSM bursts + * and needs a hardware trigger on the GPADCTrig pin of the ASIC. + * @falling_edge: indicate that we want to trigger on falling edge + * rather than rising edge, rising edge is the default + * @avg_sample: how many samples to average: must be 1, 4, 8 or 16. + * @trig_timer: how long to wait for the trigger, in 32kHz periods: + * 0 .. 255 periods + */ +struct ab8500_gpadc_chan_info { + const char *name; + u8 id; + bool hardware_control; + bool falling_edge; + u8 avg_sample; + u8 trig_timer; +}; + +/** + * struct ab8500_gpadc - AB8500 GPADC device information + * @dev: pointer to the containing device + * @ab8500: pointer to the parent AB8500 device + * @chans: internal per-channel information container + * @nchans: number of channels + * @complete: pointer to the completion that indicates + * the completion of an gpadc conversion cycle + * @vddadc: pointer to the regulator supplying VDDADC + * @irq_sw: interrupt number that is used by gpadc for software ADC conversion + * @irq_hw: interrupt number that is used by gpadc for hardware ADC conversion + * @cal_data: array of ADC calibration data structs + */ +struct ab8500_gpadc { + struct device *dev; + struct ab8500 *ab8500; + struct ab8500_gpadc_chan_info *chans; + unsigned int nchans; + struct completion complete; + struct regulator *vddadc; + int irq_sw; + int irq_hw; + struct ab8500_adc_cal_data cal_data[AB8500_CAL_NR]; +}; + +static struct ab8500_gpadc_chan_info * +ab8500_gpadc_get_channel(struct ab8500_gpadc *gpadc, u8 chan) +{ + struct ab8500_gpadc_chan_info *ch; + int i; + + for (i = 0; i < gpadc->nchans; i++) { + ch = &gpadc->chans[i]; + if (ch->id == chan) + break; + } + if (i == gpadc->nchans) + return NULL; + + return ch; +} + +/** + * ab8500_gpadc_ad_to_voltage() - Convert a raw ADC value to a voltage + * @gpadc: GPADC instance + * @ch: the sampled channel this raw value is coming from + * @ad_value: the raw value + */ +static int ab8500_gpadc_ad_to_voltage(struct ab8500_gpadc *gpadc, + enum ab8500_gpadc_channel ch, + int ad_value) +{ + int res; + + switch (ch) { + case AB8500_GPADC_CHAN_MAIN_CHARGER: + /* No calibration data available: just interpolate */ + if (!gpadc->cal_data[AB8500_CAL_VMAIN].gain) { + res = AB8500_ADC_CH_CHG_V_MIN + (AB8500_ADC_CH_CHG_V_MAX - + AB8500_ADC_CH_CHG_V_MIN) * ad_value / + AB8500_ADC_RESOLUTION; + break; + } + /* Here we can use calibration */ + res = (int) (ad_value * gpadc->cal_data[AB8500_CAL_VMAIN].gain + + gpadc->cal_data[AB8500_CAL_VMAIN].offset) / AB8500_GPADC_CALIB_SCALE; + break; + + case AB8500_GPADC_CHAN_BAT_CTRL: + case AB8500_GPADC_CHAN_BAT_TEMP: + case AB8500_GPADC_CHAN_ACC_DET_1: + case AB8500_GPADC_CHAN_ADC_AUX_1: + case AB8500_GPADC_CHAN_ADC_AUX_2: + case AB8500_GPADC_CHAN_XTAL_TEMP: + /* No calibration data available: just interpolate */ + if (!gpadc->cal_data[AB8500_CAL_BTEMP].gain) { + res = AB8500_ADC_CH_BTEMP_MIN + (AB8500_ADC_CH_BTEMP_MAX - + AB8500_ADC_CH_BTEMP_MIN) * ad_value / + AB8500_ADC_RESOLUTION; + break; + } + /* Here we can use calibration */ + res = (int) (ad_value * gpadc->cal_data[AB8500_CAL_BTEMP].gain + + gpadc->cal_data[AB8500_CAL_BTEMP].offset) / AB8500_GPADC_CALIB_SCALE; + break; + + case AB8500_GPADC_CHAN_VBAT_A: + case AB8500_GPADC_CHAN_VBAT_TRUE_MEAS: + /* No calibration data available: just interpolate */ + if (!gpadc->cal_data[AB8500_CAL_VBAT].gain) { + res = AB8500_ADC_CH_VBAT_MIN + (AB8500_ADC_CH_VBAT_MAX - + AB8500_ADC_CH_VBAT_MIN) * ad_value / + AB8500_ADC_RESOLUTION; + break; + } + /* Here we can use calibration */ + res = (int) (ad_value * gpadc->cal_data[AB8500_CAL_VBAT].gain + + gpadc->cal_data[AB8500_CAL_VBAT].offset) / AB8500_GPADC_CALIB_SCALE; + break; + + case AB8505_GPADC_CHAN_DIE_TEMP: + res = AB8500_ADC_CH_DIETEMP_MIN + + (AB8500_ADC_CH_DIETEMP_MAX - AB8500_ADC_CH_DIETEMP_MIN) * ad_value / + AB8500_ADC_RESOLUTION; + break; + + case AB8500_GPADC_CHAN_ACC_DET_2: + res = AB8500_ADC_CH_ACCDET2_MIN + + (AB8500_ADC_CH_ACCDET2_MAX - AB8500_ADC_CH_ACCDET2_MIN) * ad_value / + AB8500_ADC_RESOLUTION; + break; + + case AB8500_GPADC_CHAN_VBUS: + res = AB8500_ADC_CH_CHG_V_MIN + + (AB8500_ADC_CH_CHG_V_MAX - AB8500_ADC_CH_CHG_V_MIN) * ad_value / + AB8500_ADC_RESOLUTION; + break; + + case AB8500_GPADC_CHAN_MAIN_CHARGER_CURRENT: + case AB8500_GPADC_CHAN_USB_CHARGER_CURRENT: + res = AB8500_ADC_CH_CHG_I_MIN + + (AB8500_ADC_CH_CHG_I_MAX - AB8500_ADC_CH_CHG_I_MIN) * ad_value / + AB8500_ADC_RESOLUTION; + break; + + case AB8500_GPADC_CHAN_BACKUP_BAT: + res = AB8500_ADC_CH_BKBAT_MIN + + (AB8500_ADC_CH_BKBAT_MAX - AB8500_ADC_CH_BKBAT_MIN) * ad_value / + AB8500_ADC_RESOLUTION; + break; + + case AB8500_GPADC_CHAN_IBAT_VIRTUAL: + /* No calibration data available: just interpolate */ + if (!gpadc->cal_data[AB8500_CAL_IBAT].gain) { + res = AB8500_ADC_CH_IBAT_MIN + (AB8500_ADC_CH_IBAT_MAX - + AB8500_ADC_CH_IBAT_MIN) * ad_value / + AB8500_ADC_RESOLUTION; + break; + } + /* Here we can use calibration */ + res = (int) (ad_value * gpadc->cal_data[AB8500_CAL_IBAT].gain + + gpadc->cal_data[AB8500_CAL_IBAT].offset) + >> AB8500_GPADC_CALIB_SHIFT_IBAT; + break; + + default: + dev_err(gpadc->dev, + "unknown channel ID: %d, not possible to convert\n", + ch); + res = -EINVAL; + break; + + } + + return res; +} + +static int ab8500_gpadc_read(struct ab8500_gpadc *gpadc, + const struct ab8500_gpadc_chan_info *ch, + int *ibat) +{ + int ret; + int looplimit = 0; + unsigned long completion_timeout; + u8 val; + u8 low_data, high_data, low_data2, high_data2; + u8 ctrl1; + u8 ctrl23; + unsigned int delay_min = 0; + unsigned int delay_max = 0; + u8 data_low_addr, data_high_addr; + + if (!gpadc) + return -ENODEV; + + /* check if conversion is supported */ + if ((gpadc->irq_sw <= 0) && !ch->hardware_control) + return -ENOTSUPP; + if ((gpadc->irq_hw <= 0) && ch->hardware_control) + return -ENOTSUPP; + + /* Enable vddadc by grabbing PM runtime */ + pm_runtime_get_sync(gpadc->dev); + + /* Check if ADC is not busy, lock and proceed */ + do { + ret = abx500_get_register_interruptible(gpadc->dev, + AB8500_GPADC, AB8500_GPADC_STAT_REG, &val); + if (ret < 0) + goto out; + if (!(val & AB8500_GPADC_STAT_BUSY)) + break; + msleep(20); + } while (++looplimit < 10); + if (looplimit >= 10 && (val & AB8500_GPADC_STAT_BUSY)) { + dev_err(gpadc->dev, "gpadc_conversion: GPADC busy"); + ret = -EINVAL; + goto out; + } + + /* Enable GPADC */ + ctrl1 = AB8500_GPADC_CTRL1_ENABLE; + + /* Select the channel source and set average samples */ + switch (ch->avg_sample) { + case 1: + ctrl23 = ch->id | AB8500_GPADC_CTRL2_AVG_1; + break; + case 4: + ctrl23 = ch->id | AB8500_GPADC_CTRL2_AVG_4; + break; + case 8: + ctrl23 = ch->id | AB8500_GPADC_CTRL2_AVG_8; + break; + default: + ctrl23 = ch->id | AB8500_GPADC_CTRL2_AVG_16; + break; + } + + if (ch->hardware_control) { + ret = abx500_set_register_interruptible(gpadc->dev, + AB8500_GPADC, AB8500_GPADC_CTRL3_REG, ctrl23); + ctrl1 |= AB8500_GPADC_CTRL1_TRIG_ENA; + if (ch->falling_edge) + ctrl1 |= AB8500_GPADC_CTRL1_TRIG_EDGE; + } else { + ret = abx500_set_register_interruptible(gpadc->dev, + AB8500_GPADC, AB8500_GPADC_CTRL2_REG, ctrl23); + } + if (ret < 0) { + dev_err(gpadc->dev, + "gpadc_conversion: set avg samples failed\n"); + goto out; + } + + /* + * Enable ADC, buffering, select rising edge and enable ADC path + * charging current sense if it needed, ABB 3.0 needs some special + * treatment too. + */ + switch (ch->id) { + case AB8500_GPADC_CHAN_MAIN_CHARGER_CURRENT: + case AB8500_GPADC_CHAN_USB_CHARGER_CURRENT: + ctrl1 |= AB8500_GPADC_CTRL1_BUF_ENA | + AB8500_GPADC_CTRL1_ICHAR_ENA; + break; + case AB8500_GPADC_CHAN_BAT_TEMP: + if (!is_ab8500_2p0_or_earlier(gpadc->ab8500)) { + ctrl1 |= AB8500_GPADC_CTRL1_BUF_ENA | + AB8500_GPADC_CTRL1_BTEMP_PULL_UP; + /* + * Delay might be needed for ABB8500 cut 3.0, if not, + * remove when hardware will be available + */ + delay_min = 1000; /* Delay in micro seconds */ + delay_max = 10000; /* large range optimises sleepmode */ + break; + } + fallthrough; + default: + ctrl1 |= AB8500_GPADC_CTRL1_BUF_ENA; + break; + } + + /* Write configuration to control register 1 */ + ret = abx500_set_register_interruptible(gpadc->dev, + AB8500_GPADC, AB8500_GPADC_CTRL1_REG, ctrl1); + if (ret < 0) { + dev_err(gpadc->dev, + "gpadc_conversion: set Control register failed\n"); + goto out; + } + + if (delay_min != 0) + usleep_range(delay_min, delay_max); + + if (ch->hardware_control) { + /* Set trigger delay timer */ + ret = abx500_set_register_interruptible(gpadc->dev, + AB8500_GPADC, AB8500_GPADC_AUTO_TIMER_REG, + ch->trig_timer); + if (ret < 0) { + dev_err(gpadc->dev, + "gpadc_conversion: trig timer failed\n"); + goto out; + } + completion_timeout = 2 * HZ; + data_low_addr = AB8500_GPADC_AUTODATAL_REG; + data_high_addr = AB8500_GPADC_AUTODATAH_REG; + } else { + /* Start SW conversion */ + ret = abx500_mask_and_set_register_interruptible(gpadc->dev, + AB8500_GPADC, AB8500_GPADC_CTRL1_REG, + AB8500_GPADC_CTRL1_START_SW_CONV, + AB8500_GPADC_CTRL1_START_SW_CONV); + if (ret < 0) { + dev_err(gpadc->dev, + "gpadc_conversion: start s/w conv failed\n"); + goto out; + } + completion_timeout = msecs_to_jiffies(AB8500_GPADC_CONVERSION_TIME); + data_low_addr = AB8500_GPADC_MANDATAL_REG; + data_high_addr = AB8500_GPADC_MANDATAH_REG; + } + + /* Wait for completion of conversion */ + if (!wait_for_completion_timeout(&gpadc->complete, + completion_timeout)) { + dev_err(gpadc->dev, + "timeout didn't receive GPADC conv interrupt\n"); + ret = -EINVAL; + goto out; + } + + /* Read the converted RAW data */ + ret = abx500_get_register_interruptible(gpadc->dev, + AB8500_GPADC, data_low_addr, &low_data); + if (ret < 0) { + dev_err(gpadc->dev, + "gpadc_conversion: read low data failed\n"); + goto out; + } + + ret = abx500_get_register_interruptible(gpadc->dev, + AB8500_GPADC, data_high_addr, &high_data); + if (ret < 0) { + dev_err(gpadc->dev, + "gpadc_conversion: read high data failed\n"); + goto out; + } + + /* Check if double conversion is required */ + if ((ch->id == AB8500_GPADC_CHAN_BAT_CTRL_AND_IBAT) || + (ch->id == AB8500_GPADC_CHAN_VBAT_MEAS_AND_IBAT) || + (ch->id == AB8500_GPADC_CHAN_VBAT_TRUE_MEAS_AND_IBAT) || + (ch->id == AB8500_GPADC_CHAN_BAT_TEMP_AND_IBAT)) { + + if (ch->hardware_control) { + /* not supported */ + ret = -ENOTSUPP; + dev_err(gpadc->dev, + "gpadc_conversion: only SW double conversion supported\n"); + goto out; + } else { + /* Read the converted RAW data 2 */ + ret = abx500_get_register_interruptible(gpadc->dev, + AB8500_GPADC, AB8540_GPADC_MANDATA2L_REG, + &low_data2); + if (ret < 0) { + dev_err(gpadc->dev, + "gpadc_conversion: read sw low data 2 failed\n"); + goto out; + } + + ret = abx500_get_register_interruptible(gpadc->dev, + AB8500_GPADC, AB8540_GPADC_MANDATA2H_REG, + &high_data2); + if (ret < 0) { + dev_err(gpadc->dev, + "gpadc_conversion: read sw high data 2 failed\n"); + goto out; + } + if (ibat != NULL) { + *ibat = (high_data2 << 8) | low_data2; + } else { + dev_warn(gpadc->dev, + "gpadc_conversion: ibat not stored\n"); + } + + } + } + + /* Disable GPADC */ + ret = abx500_set_register_interruptible(gpadc->dev, AB8500_GPADC, + AB8500_GPADC_CTRL1_REG, AB8500_GPADC_CTRL1_DISABLE); + if (ret < 0) { + dev_err(gpadc->dev, "gpadc_conversion: disable gpadc failed\n"); + goto out; + } + + /* This eventually drops the regulator */ + pm_runtime_put_autosuspend(gpadc->dev); + + return (high_data << 8) | low_data; + +out: + /* + * It has shown to be needed to turn off the GPADC if an error occurs, + * otherwise we might have problem when waiting for the busy bit in the + * GPADC status register to go low. In V1.1 there wait_for_completion + * seems to timeout when waiting for an interrupt.. Not seen in V2.0 + */ + (void) abx500_set_register_interruptible(gpadc->dev, AB8500_GPADC, + AB8500_GPADC_CTRL1_REG, AB8500_GPADC_CTRL1_DISABLE); + pm_runtime_put(gpadc->dev); + dev_err(gpadc->dev, + "gpadc_conversion: Failed to AD convert channel %d\n", ch->id); + + return ret; +} + +/** + * ab8500_bm_gpadcconvend_handler() - isr for gpadc conversion completion + * @irq: irq number + * @data: pointer to the data passed during request irq + * + * This is a interrupt service routine for gpadc conversion completion. + * Notifies the gpadc completion is completed and the converted raw value + * can be read from the registers. + * Returns IRQ status(IRQ_HANDLED) + */ +static irqreturn_t ab8500_bm_gpadcconvend_handler(int irq, void *data) +{ + struct ab8500_gpadc *gpadc = data; + + complete(&gpadc->complete); + + return IRQ_HANDLED; +} + +static int otp_cal_regs[] = { + AB8500_GPADC_CAL_1, + AB8500_GPADC_CAL_2, + AB8500_GPADC_CAL_3, + AB8500_GPADC_CAL_4, + AB8500_GPADC_CAL_5, + AB8500_GPADC_CAL_6, + AB8500_GPADC_CAL_7, +}; + +static int otp4_cal_regs[] = { + AB8540_GPADC_OTP4_REG_7, + AB8540_GPADC_OTP4_REG_6, + AB8540_GPADC_OTP4_REG_5, +}; + +static void ab8500_gpadc_read_calibration_data(struct ab8500_gpadc *gpadc) +{ + int i; + int ret[ARRAY_SIZE(otp_cal_regs)]; + u8 gpadc_cal[ARRAY_SIZE(otp_cal_regs)]; + int ret_otp4[ARRAY_SIZE(otp4_cal_regs)]; + u8 gpadc_otp4[ARRAY_SIZE(otp4_cal_regs)]; + int vmain_high, vmain_low; + int btemp_high, btemp_low; + int vbat_high, vbat_low; + int ibat_high, ibat_low; + s64 V_gain, V_offset, V2A_gain, V2A_offset; + + /* First we read all OTP registers and store the error code */ + for (i = 0; i < ARRAY_SIZE(otp_cal_regs); i++) { + ret[i] = abx500_get_register_interruptible(gpadc->dev, + AB8500_OTP_EMUL, otp_cal_regs[i], &gpadc_cal[i]); + if (ret[i] < 0) { + /* Continue anyway: maybe the other registers are OK */ + dev_err(gpadc->dev, "%s: read otp reg 0x%02x failed\n", + __func__, otp_cal_regs[i]); + } else { + /* Put this in the entropy pool as device-unique */ + add_device_randomness(&ret[i], sizeof(ret[i])); + } + } + + /* + * The ADC calibration data is stored in OTP registers. + * The layout of the calibration data is outlined below and a more + * detailed description can be found in UM0836 + * + * vm_h/l = vmain_high/low + * bt_h/l = btemp_high/low + * vb_h/l = vbat_high/low + * + * Data bits 8500/9540: + * | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 + * |.......|.......|.......|.......|.......|.......|.......|....... + * | | vm_h9 | vm_h8 + * |.......|.......|.......|.......|.......|.......|.......|....... + * | | vm_h7 | vm_h6 | vm_h5 | vm_h4 | vm_h3 | vm_h2 + * |.......|.......|.......|.......|.......|.......|.......|....... + * | vm_h1 | vm_h0 | vm_l4 | vm_l3 | vm_l2 | vm_l1 | vm_l0 | bt_h9 + * |.......|.......|.......|.......|.......|.......|.......|....... + * | bt_h8 | bt_h7 | bt_h6 | bt_h5 | bt_h4 | bt_h3 | bt_h2 | bt_h1 + * |.......|.......|.......|.......|.......|.......|.......|....... + * | bt_h0 | bt_l4 | bt_l3 | bt_l2 | bt_l1 | bt_l0 | vb_h9 | vb_h8 + * |.......|.......|.......|.......|.......|.......|.......|....... + * | vb_h7 | vb_h6 | vb_h5 | vb_h4 | vb_h3 | vb_h2 | vb_h1 | vb_h0 + * |.......|.......|.......|.......|.......|.......|.......|....... + * | vb_l5 | vb_l4 | vb_l3 | vb_l2 | vb_l1 | vb_l0 | + * |.......|.......|.......|.......|.......|.......|.......|....... + * + * Data bits 8540: + * OTP2 + * | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 + * |.......|.......|.......|.......|.......|.......|.......|....... + * | + * |.......|.......|.......|.......|.......|.......|.......|....... + * | vm_h9 | vm_h8 | vm_h7 | vm_h6 | vm_h5 | vm_h4 | vm_h3 | vm_h2 + * |.......|.......|.......|.......|.......|.......|.......|....... + * | vm_h1 | vm_h0 | vm_l4 | vm_l3 | vm_l2 | vm_l1 | vm_l0 | bt_h9 + * |.......|.......|.......|.......|.......|.......|.......|....... + * | bt_h8 | bt_h7 | bt_h6 | bt_h5 | bt_h4 | bt_h3 | bt_h2 | bt_h1 + * |.......|.......|.......|.......|.......|.......|.......|....... + * | bt_h0 | bt_l4 | bt_l3 | bt_l2 | bt_l1 | bt_l0 | vb_h9 | vb_h8 + * |.......|.......|.......|.......|.......|.......|.......|....... + * | vb_h7 | vb_h6 | vb_h5 | vb_h4 | vb_h3 | vb_h2 | vb_h1 | vb_h0 + * |.......|.......|.......|.......|.......|.......|.......|....... + * | vb_l5 | vb_l4 | vb_l3 | vb_l2 | vb_l1 | vb_l0 | + * |.......|.......|.......|.......|.......|.......|.......|....... + * + * Data bits 8540: + * OTP4 + * | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 + * |.......|.......|.......|.......|.......|.......|.......|....... + * | | ib_h9 | ib_h8 | ib_h7 + * |.......|.......|.......|.......|.......|.......|.......|....... + * | ib_h6 | ib_h5 | ib_h4 | ib_h3 | ib_h2 | ib_h1 | ib_h0 | ib_l5 + * |.......|.......|.......|.......|.......|.......|.......|....... + * | ib_l4 | ib_l3 | ib_l2 | ib_l1 | ib_l0 | + * + * + * Ideal output ADC codes corresponding to injected input voltages + * during manufacturing is: + * + * vmain_high: Vin = 19500mV / ADC ideal code = 997 + * vmain_low: Vin = 315mV / ADC ideal code = 16 + * btemp_high: Vin = 1300mV / ADC ideal code = 985 + * btemp_low: Vin = 21mV / ADC ideal code = 16 + * vbat_high: Vin = 4700mV / ADC ideal code = 982 + * vbat_low: Vin = 2380mV / ADC ideal code = 33 + */ + + if (is_ab8540(gpadc->ab8500)) { + /* Calculate gain and offset for VMAIN if all reads succeeded*/ + if (!(ret[1] < 0 || ret[2] < 0)) { + vmain_high = (((gpadc_cal[1] & 0xFF) << 2) | + ((gpadc_cal[2] & 0xC0) >> 6)); + vmain_low = ((gpadc_cal[2] & 0x3E) >> 1); + + gpadc->cal_data[AB8500_CAL_VMAIN].otp_calib_hi = + (u16)vmain_high; + gpadc->cal_data[AB8500_CAL_VMAIN].otp_calib_lo = + (u16)vmain_low; + + gpadc->cal_data[AB8500_CAL_VMAIN].gain = AB8500_GPADC_CALIB_SCALE * + (19500 - 315) / (vmain_high - vmain_low); + gpadc->cal_data[AB8500_CAL_VMAIN].offset = AB8500_GPADC_CALIB_SCALE * + 19500 - (AB8500_GPADC_CALIB_SCALE * (19500 - 315) / + (vmain_high - vmain_low)) * vmain_high; + } else { + gpadc->cal_data[AB8500_CAL_VMAIN].gain = 0; + } + + /* Read IBAT calibration Data */ + for (i = 0; i < ARRAY_SIZE(otp4_cal_regs); i++) { + ret_otp4[i] = abx500_get_register_interruptible( + gpadc->dev, AB8500_OTP_EMUL, + otp4_cal_regs[i], &gpadc_otp4[i]); + if (ret_otp4[i] < 0) + dev_err(gpadc->dev, + "%s: read otp4 reg 0x%02x failed\n", + __func__, otp4_cal_regs[i]); + } + + /* Calculate gain and offset for IBAT if all reads succeeded */ + if (!(ret_otp4[0] < 0 || ret_otp4[1] < 0 || ret_otp4[2] < 0)) { + ibat_high = (((gpadc_otp4[0] & 0x07) << 7) | + ((gpadc_otp4[1] & 0xFE) >> 1)); + ibat_low = (((gpadc_otp4[1] & 0x01) << 5) | + ((gpadc_otp4[2] & 0xF8) >> 3)); + + gpadc->cal_data[AB8500_CAL_IBAT].otp_calib_hi = + (u16)ibat_high; + gpadc->cal_data[AB8500_CAL_IBAT].otp_calib_lo = + (u16)ibat_low; + + V_gain = ((AB8500_GPADC_IBAT_VDROP_H - AB8500_GPADC_IBAT_VDROP_L) + << AB8500_GPADC_CALIB_SHIFT_IBAT) / (ibat_high - ibat_low); + + V_offset = (AB8500_GPADC_IBAT_VDROP_H << AB8500_GPADC_CALIB_SHIFT_IBAT) - + (((AB8500_GPADC_IBAT_VDROP_H - AB8500_GPADC_IBAT_VDROP_L) << + AB8500_GPADC_CALIB_SHIFT_IBAT) / (ibat_high - ibat_low)) + * ibat_high; + /* + * Result obtained is in mV (at a scale factor), + * we need to calculate gain and offset to get mA + */ + V2A_gain = (AB8500_ADC_CH_IBAT_MAX - AB8500_ADC_CH_IBAT_MIN)/ + (AB8500_ADC_CH_IBAT_MAX_V - AB8500_ADC_CH_IBAT_MIN_V); + V2A_offset = ((AB8500_ADC_CH_IBAT_MAX_V * AB8500_ADC_CH_IBAT_MIN - + AB8500_ADC_CH_IBAT_MAX * AB8500_ADC_CH_IBAT_MIN_V) + << AB8500_GPADC_CALIB_SHIFT_IBAT) + / (AB8500_ADC_CH_IBAT_MAX_V - AB8500_ADC_CH_IBAT_MIN_V); + + gpadc->cal_data[AB8500_CAL_IBAT].gain = + V_gain * V2A_gain; + gpadc->cal_data[AB8500_CAL_IBAT].offset = + V_offset * V2A_gain + V2A_offset; + } else { + gpadc->cal_data[AB8500_CAL_IBAT].gain = 0; + } + } else { + /* Calculate gain and offset for VMAIN if all reads succeeded */ + if (!(ret[0] < 0 || ret[1] < 0 || ret[2] < 0)) { + vmain_high = (((gpadc_cal[0] & 0x03) << 8) | + ((gpadc_cal[1] & 0x3F) << 2) | + ((gpadc_cal[2] & 0xC0) >> 6)); + vmain_low = ((gpadc_cal[2] & 0x3E) >> 1); + + gpadc->cal_data[AB8500_CAL_VMAIN].otp_calib_hi = + (u16)vmain_high; + gpadc->cal_data[AB8500_CAL_VMAIN].otp_calib_lo = + (u16)vmain_low; + + gpadc->cal_data[AB8500_CAL_VMAIN].gain = AB8500_GPADC_CALIB_SCALE * + (19500 - 315) / (vmain_high - vmain_low); + + gpadc->cal_data[AB8500_CAL_VMAIN].offset = AB8500_GPADC_CALIB_SCALE * + 19500 - (AB8500_GPADC_CALIB_SCALE * (19500 - 315) / + (vmain_high - vmain_low)) * vmain_high; + } else { + gpadc->cal_data[AB8500_CAL_VMAIN].gain = 0; + } + } + + /* Calculate gain and offset for BTEMP if all reads succeeded */ + if (!(ret[2] < 0 || ret[3] < 0 || ret[4] < 0)) { + btemp_high = (((gpadc_cal[2] & 0x01) << 9) | + (gpadc_cal[3] << 1) | ((gpadc_cal[4] & 0x80) >> 7)); + btemp_low = ((gpadc_cal[4] & 0x7C) >> 2); + + gpadc->cal_data[AB8500_CAL_BTEMP].otp_calib_hi = (u16)btemp_high; + gpadc->cal_data[AB8500_CAL_BTEMP].otp_calib_lo = (u16)btemp_low; + + gpadc->cal_data[AB8500_CAL_BTEMP].gain = + AB8500_GPADC_CALIB_SCALE * (1300 - 21) / (btemp_high - btemp_low); + gpadc->cal_data[AB8500_CAL_BTEMP].offset = AB8500_GPADC_CALIB_SCALE * 1300 - + (AB8500_GPADC_CALIB_SCALE * (1300 - 21) / (btemp_high - btemp_low)) + * btemp_high; + } else { + gpadc->cal_data[AB8500_CAL_BTEMP].gain = 0; + } + + /* Calculate gain and offset for VBAT if all reads succeeded */ + if (!(ret[4] < 0 || ret[5] < 0 || ret[6] < 0)) { + vbat_high = (((gpadc_cal[4] & 0x03) << 8) | gpadc_cal[5]); + vbat_low = ((gpadc_cal[6] & 0xFC) >> 2); + + gpadc->cal_data[AB8500_CAL_VBAT].otp_calib_hi = (u16)vbat_high; + gpadc->cal_data[AB8500_CAL_VBAT].otp_calib_lo = (u16)vbat_low; + + gpadc->cal_data[AB8500_CAL_VBAT].gain = AB8500_GPADC_CALIB_SCALE * + (4700 - 2380) / (vbat_high - vbat_low); + gpadc->cal_data[AB8500_CAL_VBAT].offset = AB8500_GPADC_CALIB_SCALE * 4700 - + (AB8500_GPADC_CALIB_SCALE * (4700 - 2380) / + (vbat_high - vbat_low)) * vbat_high; + } else { + gpadc->cal_data[AB8500_CAL_VBAT].gain = 0; + } +} + +static int ab8500_gpadc_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct ab8500_gpadc *gpadc = iio_priv(indio_dev); + const struct ab8500_gpadc_chan_info *ch; + int raw_val; + int processed; + + ch = ab8500_gpadc_get_channel(gpadc, chan->address); + if (!ch) { + dev_err(gpadc->dev, "no such channel %lu\n", + chan->address); + return -EINVAL; + } + + raw_val = ab8500_gpadc_read(gpadc, ch, NULL); + if (raw_val < 0) + return raw_val; + + if (mask == IIO_CHAN_INFO_RAW) { + *val = raw_val; + return IIO_VAL_INT; + } + + if (mask == IIO_CHAN_INFO_PROCESSED) { + processed = ab8500_gpadc_ad_to_voltage(gpadc, ch->id, raw_val); + if (processed < 0) + return processed; + + /* Return millivolt or milliamps or millicentigrades */ + *val = processed; + return IIO_VAL_INT; + } + + return -EINVAL; +} + +static int ab8500_gpadc_fwnode_xlate(struct iio_dev *indio_dev, + const struct fwnode_reference_args *iiospec) +{ + int i; + + for (i = 0; i < indio_dev->num_channels; i++) + if (indio_dev->channels[i].channel == iiospec->args[0]) + return i; + + return -EINVAL; +} + +static const struct iio_info ab8500_gpadc_info = { + .fwnode_xlate = ab8500_gpadc_fwnode_xlate, + .read_raw = ab8500_gpadc_read_raw, +}; + +static int ab8500_gpadc_runtime_suspend(struct device *dev) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct ab8500_gpadc *gpadc = iio_priv(indio_dev); + + regulator_disable(gpadc->vddadc); + + return 0; +} + +static int ab8500_gpadc_runtime_resume(struct device *dev) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct ab8500_gpadc *gpadc = iio_priv(indio_dev); + int ret; + + ret = regulator_enable(gpadc->vddadc); + if (ret) + dev_err(dev, "Failed to enable vddadc: %d\n", ret); + + return ret; +} + +/** + * ab8500_gpadc_parse_channel() - process devicetree channel configuration + * @dev: pointer to containing device + * @fwnode: fw node for the channel to configure + * @ch: channel info to fill in + * @iio_chan: IIO channel specification to fill in + * + * The devicetree will set up the channel for use with the specific device, + * and define usage for things like AUX GPADC inputs more precisely. + */ +static int ab8500_gpadc_parse_channel(struct device *dev, + struct fwnode_handle *fwnode, + struct ab8500_gpadc_chan_info *ch, + struct iio_chan_spec *iio_chan) +{ + const char *name = fwnode_get_name(fwnode); + u32 chan; + int ret; + + ret = fwnode_property_read_u32(fwnode, "reg", &chan); + if (ret) { + dev_err(dev, "invalid channel number %s\n", name); + return ret; + } + if (chan > AB8500_GPADC_CHAN_BAT_TEMP_AND_IBAT) { + dev_err(dev, "%s channel number out of range %d\n", name, chan); + return -EINVAL; + } + + iio_chan->channel = chan; + iio_chan->datasheet_name = name; + iio_chan->indexed = 1; + iio_chan->address = chan; + iio_chan->info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_PROCESSED); + /* Most are voltages (also temperatures), some are currents */ + if ((chan == AB8500_GPADC_CHAN_MAIN_CHARGER_CURRENT) || + (chan == AB8500_GPADC_CHAN_USB_CHARGER_CURRENT)) + iio_chan->type = IIO_CURRENT; + else + iio_chan->type = IIO_VOLTAGE; + + ch->id = chan; + + /* Sensible defaults */ + ch->avg_sample = 16; + ch->hardware_control = false; + ch->falling_edge = false; + ch->trig_timer = 0; + + return 0; +} + +/** + * ab8500_gpadc_parse_channels() - Parse the GPADC channels from DT + * @gpadc: the GPADC to configure the channels for + * @chans_parsed: the IIO channels we parsed + * @nchans_parsed: the number of IIO channels we parsed + */ +static int ab8500_gpadc_parse_channels(struct ab8500_gpadc *gpadc, + struct iio_chan_spec **chans_parsed, + unsigned int *nchans_parsed) +{ + struct ab8500_gpadc_chan_info *ch; + struct iio_chan_spec *iio_chans; + unsigned int nchans; + int i; + + nchans = device_get_child_node_count(gpadc->dev); + if (!nchans) { + dev_err(gpadc->dev, "no channel children\n"); + return -ENODEV; + } + dev_info(gpadc->dev, "found %d ADC channels\n", nchans); + + iio_chans = devm_kcalloc(gpadc->dev, nchans, + sizeof(*iio_chans), GFP_KERNEL); + if (!iio_chans) + return -ENOMEM; + + gpadc->chans = devm_kcalloc(gpadc->dev, nchans, + sizeof(*gpadc->chans), GFP_KERNEL); + if (!gpadc->chans) + return -ENOMEM; + + i = 0; + device_for_each_child_node_scoped(gpadc->dev, child) { + struct iio_chan_spec *iio_chan; + int ret; + + ch = &gpadc->chans[i]; + iio_chan = &iio_chans[i]; + + ret = ab8500_gpadc_parse_channel(gpadc->dev, child, ch, + iio_chan); + if (ret) { + return ret; + } + i++; + } + gpadc->nchans = nchans; + *chans_parsed = iio_chans; + *nchans_parsed = nchans; + + return 0; +} + +static int ab8500_gpadc_probe(struct platform_device *pdev) +{ + struct ab8500_gpadc *gpadc; + struct iio_dev *indio_dev; + struct device *dev = &pdev->dev; + struct iio_chan_spec *iio_chans; + unsigned int n_iio_chans; + int ret; + + indio_dev = devm_iio_device_alloc(dev, sizeof(*gpadc)); + if (!indio_dev) + return -ENOMEM; + + platform_set_drvdata(pdev, indio_dev); + gpadc = iio_priv(indio_dev); + + gpadc->dev = dev; + gpadc->ab8500 = dev_get_drvdata(dev->parent); + + ret = ab8500_gpadc_parse_channels(gpadc, &iio_chans, &n_iio_chans); + if (ret) + return ret; + + gpadc->irq_sw = platform_get_irq_byname(pdev, "SW_CONV_END"); + if (gpadc->irq_sw < 0) + return gpadc->irq_sw; + + if (is_ab8500(gpadc->ab8500)) { + gpadc->irq_hw = platform_get_irq_byname(pdev, "HW_CONV_END"); + if (gpadc->irq_hw < 0) + return gpadc->irq_hw; + } else { + gpadc->irq_hw = 0; + } + + /* Initialize completion used to notify completion of conversion */ + init_completion(&gpadc->complete); + + /* Request interrupts */ + ret = devm_request_threaded_irq(dev, gpadc->irq_sw, NULL, + ab8500_bm_gpadcconvend_handler, IRQF_NO_SUSPEND | IRQF_ONESHOT, + "ab8500-gpadc-sw", gpadc); + if (ret < 0) { + dev_err(dev, + "failed to request sw conversion irq %d\n", + gpadc->irq_sw); + return ret; + } + + if (gpadc->irq_hw) { + ret = devm_request_threaded_irq(dev, gpadc->irq_hw, NULL, + ab8500_bm_gpadcconvend_handler, IRQF_NO_SUSPEND | IRQF_ONESHOT, + "ab8500-gpadc-hw", gpadc); + if (ret < 0) { + dev_err(dev, + "Failed to request hw conversion irq: %d\n", + gpadc->irq_hw); + return ret; + } + } + + /* The VTVout LDO used to power the AB8500 GPADC */ + gpadc->vddadc = devm_regulator_get(dev, "vddadc"); + if (IS_ERR(gpadc->vddadc)) + return dev_err_probe(dev, PTR_ERR(gpadc->vddadc), + "failed to get vddadc\n"); + + ret = regulator_enable(gpadc->vddadc); + if (ret) { + dev_err(dev, "failed to enable vddadc: %d\n", ret); + return ret; + } + + /* Enable runtime PM */ + pm_runtime_get_noresume(dev); + pm_runtime_set_active(dev); + pm_runtime_enable(dev); + pm_runtime_set_autosuspend_delay(dev, AB8500_GPADC_AUTOSUSPEND_DELAY); + pm_runtime_use_autosuspend(dev); + + ab8500_gpadc_read_calibration_data(gpadc); + + pm_runtime_put(dev); + + indio_dev->name = "ab8500-gpadc"; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->info = &ab8500_gpadc_info; + indio_dev->channels = iio_chans; + indio_dev->num_channels = n_iio_chans; + + ret = devm_iio_device_register(dev, indio_dev); + if (ret) + goto out_dis_pm; + + return 0; + +out_dis_pm: + pm_runtime_get_sync(dev); + pm_runtime_put_noidle(dev); + pm_runtime_disable(dev); + regulator_disable(gpadc->vddadc); + + return ret; +} + +static void ab8500_gpadc_remove(struct platform_device *pdev) +{ + struct iio_dev *indio_dev = platform_get_drvdata(pdev); + struct ab8500_gpadc *gpadc = iio_priv(indio_dev); + + pm_runtime_get_sync(gpadc->dev); + pm_runtime_put_noidle(gpadc->dev); + pm_runtime_disable(gpadc->dev); + regulator_disable(gpadc->vddadc); +} + +static DEFINE_RUNTIME_DEV_PM_OPS(ab8500_gpadc_pm_ops, + ab8500_gpadc_runtime_suspend, + ab8500_gpadc_runtime_resume, NULL); + +static struct platform_driver ab8500_gpadc_driver = { + .probe = ab8500_gpadc_probe, + .remove = ab8500_gpadc_remove, + .driver = { + .name = "ab8500-gpadc", + .pm = pm_ptr(&ab8500_gpadc_pm_ops), + }, +}; +builtin_platform_driver(ab8500_gpadc_driver); diff --git a/drivers/iio/adc/ad4000.c b/drivers/iio/adc/ad4000.c new file mode 100644 index 000000000..a33896b2c --- /dev/null +++ b/drivers/iio/adc/ad4000.c @@ -0,0 +1,1263 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * AD4000 SPI ADC driver + * + * Copyright 2024 Analog Devices Inc. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#define AD4000_READ_COMMAND 0x54 +#define AD4000_WRITE_COMMAND 0x14 + +#define AD4000_CONFIG_REG_DEFAULT 0xE1 + +/* AD4000 Configuration Register programmable bits */ +#define AD4000_CFG_SPAN_COMP BIT(3) /* Input span compression */ +#define AD4000_CFG_HIGHZ BIT(2) /* High impedance mode */ +#define AD4000_CFG_TURBO BIT(1) /* Turbo mode */ + +#define AD4000_SCALE_OPTIONS 2 + +#define __AD4000_DIFF_CHANNEL(_sign, _real_bits, _storage_bits, _reg_access, _offl)\ +{ \ + .type = IIO_VOLTAGE, \ + .indexed = 1, \ + .differential = 1, \ + .channel = 0, \ + .channel2 = 1, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ + BIT(IIO_CHAN_INFO_SCALE) | \ + (_offl ? BIT(IIO_CHAN_INFO_SAMP_FREQ) : 0), \ + .info_mask_separate_available = _reg_access ? BIT(IIO_CHAN_INFO_SCALE) : 0,\ + .scan_index = 0, \ + .scan_type = { \ + .sign = _sign, \ + .realbits = _real_bits, \ + .storagebits = _storage_bits, \ + .shift = (_offl ? 0 : _storage_bits - _real_bits), \ + .endianness = _offl ? IIO_CPU : IIO_BE \ + }, \ +} + +#define AD4000_DIFF_CHANNEL(_sign, _real_bits, _reg_access, _offl) \ + __AD4000_DIFF_CHANNEL((_sign), (_real_bits), \ + (((_offl) || ((_real_bits) > 16)) ? 32 : 16), \ + (_reg_access), (_offl)) + +/* + * When SPI offload is configured, transfers are executed without CPU + * intervention so no soft timestamp can be recorded when transfers run. + * Because of that, the macros that set timestamp channel are only used when + * transfers are not offloaded. + */ +#define AD4000_DIFF_CHANNELS(_sign, _real_bits, _reg_access) \ +{ \ + AD4000_DIFF_CHANNEL(_sign, _real_bits, _reg_access, 0), \ + IIO_CHAN_SOFT_TIMESTAMP(1), \ +} + +#define __AD4000_PSEUDO_DIFF_CHANNEL(_sign, _real_bits, _storage_bits, \ + _reg_access, _offl) \ +{ \ + .type = IIO_VOLTAGE, \ + .indexed = 1, \ + .channel = 0, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ + BIT(IIO_CHAN_INFO_SCALE) | \ + BIT(IIO_CHAN_INFO_OFFSET) | \ + (_offl ? BIT(IIO_CHAN_INFO_SAMP_FREQ) : 0), \ + .info_mask_separate_available = _reg_access ? BIT(IIO_CHAN_INFO_SCALE) : 0,\ + .scan_index = 0, \ + .scan_type = { \ + .sign = _sign, \ + .realbits = _real_bits, \ + .storagebits = _storage_bits, \ + .shift = (_offl ? 0 : _storage_bits - _real_bits), \ + .endianness = _offl ? IIO_CPU : IIO_BE \ + }, \ +} + +#define AD4000_PSEUDO_DIFF_CHANNEL(_sign, _real_bits, _reg_access, _offl) \ + __AD4000_PSEUDO_DIFF_CHANNEL((_sign), (_real_bits), \ + (((_offl) || ((_real_bits) > 16)) ? 32 : 16),\ + (_reg_access), (_offl)) + +#define AD4000_PSEUDO_DIFF_CHANNELS(_sign, _real_bits, _reg_access) \ +{ \ + AD4000_PSEUDO_DIFF_CHANNEL(_sign, _real_bits, _reg_access, 0), \ + IIO_CHAN_SOFT_TIMESTAMP(1), \ +} + +static const char * const ad4000_power_supplies[] = { + "vdd", "vio" +}; + +enum ad4000_sdi { + AD4000_SDI_MOSI, + AD4000_SDI_VIO, + AD4000_SDI_CS, + AD4000_SDI_GND, +}; + +/* maps adi,sdi-pin property value to enum */ +static const char * const ad4000_sdi_pin[] = { + [AD4000_SDI_MOSI] = "sdi", + [AD4000_SDI_VIO] = "high", + [AD4000_SDI_CS] = "cs", + [AD4000_SDI_GND] = "low", +}; + +/* Gains stored as fractions of 1000 so they can be expressed by integers. */ +static const int ad4000_gains[] = { + 454, 909, 1000, 1900, +}; + +struct ad4000_time_spec { + int t_conv_ns; + int t_quiet2_ns; +}; + +/* + * Same timing specifications for all of AD4000, AD4001, ..., AD4008, AD4010, + * ADAQ4001, and ADAQ4003. + */ +static const struct ad4000_time_spec ad4000_t_spec = { + .t_conv_ns = 320, + .t_quiet2_ns = 60, +}; + +/* AD4020, AD4021, AD4022 */ +static const struct ad4000_time_spec ad4020_t_spec = { + .t_conv_ns = 350, + .t_quiet2_ns = 60, +}; + +/* AD7983, AD7984 */ +static const struct ad4000_time_spec ad7983_t_spec = { + .t_conv_ns = 500, + .t_quiet2_ns = 0, +}; + +/* AD7980, AD7982 */ +static const struct ad4000_time_spec ad7980_t_spec = { + .t_conv_ns = 800, + .t_quiet2_ns = 0, +}; + +/* AD7946, AD7686, AD7688, AD7988-5, AD7693 */ +static const struct ad4000_time_spec ad7686_t_spec = { + .t_conv_ns = 1600, + .t_quiet2_ns = 0, +}; + +/* AD7690 */ +static const struct ad4000_time_spec ad7690_t_spec = { + .t_conv_ns = 2100, + .t_quiet2_ns = 0, +}; + +/* AD7942, AD7685, AD7687 */ +static const struct ad4000_time_spec ad7687_t_spec = { + .t_conv_ns = 3200, + .t_quiet2_ns = 0, +}; + +/* AD7691 */ +static const struct ad4000_time_spec ad7691_t_spec = { + .t_conv_ns = 3700, + .t_quiet2_ns = 0, +}; + +/* AD7988-1 */ +static const struct ad4000_time_spec ad7988_1_t_spec = { + .t_conv_ns = 9500, + .t_quiet2_ns = 0, +}; + +struct ad4000_chip_info { + const char *dev_name; + struct iio_chan_spec chan_spec[2]; + struct iio_chan_spec reg_access_chan_spec[2]; + struct iio_chan_spec offload_chan_spec; + struct iio_chan_spec reg_access_offload_chan_spec; + const struct ad4000_time_spec *time_spec; + bool has_hardware_gain; + int max_rate_hz; +}; + +static const struct ad4000_chip_info ad4000_chip_info = { + .dev_name = "ad4000", + .chan_spec = AD4000_PSEUDO_DIFF_CHANNELS('u', 16, 0), + .reg_access_chan_spec = AD4000_PSEUDO_DIFF_CHANNELS('u', 16, 1), + .offload_chan_spec = AD4000_PSEUDO_DIFF_CHANNEL('u', 16, 0, 1), + .reg_access_offload_chan_spec = AD4000_PSEUDO_DIFF_CHANNEL('u', 16, 1, 1), + .time_spec = &ad4000_t_spec, + .max_rate_hz = 2 * MEGA, +}; + +static const struct ad4000_chip_info ad4001_chip_info = { + .dev_name = "ad4001", + .chan_spec = AD4000_DIFF_CHANNELS('s', 16, 0), + .reg_access_chan_spec = AD4000_DIFF_CHANNELS('s', 16, 1), + .offload_chan_spec = AD4000_DIFF_CHANNEL('s', 16, 0, 1), + .reg_access_offload_chan_spec = AD4000_DIFF_CHANNEL('s', 16, 1, 1), + .time_spec = &ad4000_t_spec, + .max_rate_hz = 2 * MEGA, +}; + +static const struct ad4000_chip_info ad4002_chip_info = { + .dev_name = "ad4002", + .chan_spec = AD4000_PSEUDO_DIFF_CHANNELS('u', 18, 0), + .reg_access_chan_spec = AD4000_PSEUDO_DIFF_CHANNELS('u', 18, 1), + .offload_chan_spec = AD4000_PSEUDO_DIFF_CHANNEL('u', 18, 0, 1), + .reg_access_offload_chan_spec = AD4000_PSEUDO_DIFF_CHANNEL('u', 18, 1, 1), + .time_spec = &ad4000_t_spec, + .max_rate_hz = 2 * MEGA, +}; + +static const struct ad4000_chip_info ad4003_chip_info = { + .dev_name = "ad4003", + .chan_spec = AD4000_DIFF_CHANNELS('s', 18, 0), + .reg_access_chan_spec = AD4000_DIFF_CHANNELS('s', 18, 1), + .offload_chan_spec = AD4000_DIFF_CHANNEL('s', 18, 0, 1), + .reg_access_offload_chan_spec = AD4000_DIFF_CHANNEL('s', 18, 1, 1), + .time_spec = &ad4000_t_spec, + .max_rate_hz = 2 * MEGA, +}; + +static const struct ad4000_chip_info ad4004_chip_info = { + .dev_name = "ad4004", + .chan_spec = AD4000_PSEUDO_DIFF_CHANNELS('u', 16, 0), + .reg_access_chan_spec = AD4000_PSEUDO_DIFF_CHANNELS('u', 16, 1), + .offload_chan_spec = AD4000_PSEUDO_DIFF_CHANNEL('u', 16, 0, 1), + .reg_access_offload_chan_spec = AD4000_PSEUDO_DIFF_CHANNEL('u', 16, 1, 1), + .time_spec = &ad4000_t_spec, + .max_rate_hz = 1 * MEGA, +}; + +static const struct ad4000_chip_info ad4005_chip_info = { + .dev_name = "ad4005", + .chan_spec = AD4000_DIFF_CHANNELS('s', 16, 0), + .reg_access_chan_spec = AD4000_DIFF_CHANNELS('s', 16, 1), + .offload_chan_spec = AD4000_DIFF_CHANNEL('s', 16, 0, 1), + .reg_access_offload_chan_spec = AD4000_DIFF_CHANNEL('s', 16, 1, 1), + .time_spec = &ad4000_t_spec, + .max_rate_hz = 1 * MEGA, +}; + +static const struct ad4000_chip_info ad4006_chip_info = { + .dev_name = "ad4006", + .chan_spec = AD4000_PSEUDO_DIFF_CHANNELS('u', 18, 0), + .reg_access_chan_spec = AD4000_PSEUDO_DIFF_CHANNELS('u', 18, 1), + .offload_chan_spec = AD4000_PSEUDO_DIFF_CHANNEL('u', 18, 0, 1), + .reg_access_offload_chan_spec = AD4000_PSEUDO_DIFF_CHANNEL('u', 18, 1, 1), + .time_spec = &ad4000_t_spec, + .max_rate_hz = 1 * MEGA, +}; + +static const struct ad4000_chip_info ad4007_chip_info = { + .dev_name = "ad4007", + .chan_spec = AD4000_DIFF_CHANNELS('s', 18, 0), + .reg_access_chan_spec = AD4000_DIFF_CHANNELS('s', 18, 1), + .offload_chan_spec = AD4000_DIFF_CHANNEL('s', 18, 0, 1), + .reg_access_offload_chan_spec = AD4000_DIFF_CHANNEL('s', 18, 1, 1), + .time_spec = &ad4000_t_spec, + .max_rate_hz = 1 * MEGA, +}; + +static const struct ad4000_chip_info ad4008_chip_info = { + .dev_name = "ad4008", + .chan_spec = AD4000_PSEUDO_DIFF_CHANNELS('u', 16, 0), + .reg_access_chan_spec = AD4000_PSEUDO_DIFF_CHANNELS('u', 16, 1), + .offload_chan_spec = AD4000_PSEUDO_DIFF_CHANNEL('u', 16, 0, 1), + .reg_access_offload_chan_spec = AD4000_PSEUDO_DIFF_CHANNEL('u', 16, 1, 1), + .time_spec = &ad4000_t_spec, + .max_rate_hz = 500 * KILO, +}; + +static const struct ad4000_chip_info ad4010_chip_info = { + .dev_name = "ad4010", + .chan_spec = AD4000_PSEUDO_DIFF_CHANNELS('u', 18, 0), + .reg_access_chan_spec = AD4000_PSEUDO_DIFF_CHANNELS('u', 18, 1), + .offload_chan_spec = AD4000_PSEUDO_DIFF_CHANNEL('u', 18, 0, 1), + .reg_access_offload_chan_spec = AD4000_PSEUDO_DIFF_CHANNEL('u', 18, 1, 1), + .time_spec = &ad4000_t_spec, + .max_rate_hz = 500 * KILO, +}; + +static const struct ad4000_chip_info ad4011_chip_info = { + .dev_name = "ad4011", + .chan_spec = AD4000_DIFF_CHANNELS('s', 18, 0), + .reg_access_chan_spec = AD4000_DIFF_CHANNELS('s', 18, 1), + .offload_chan_spec = AD4000_DIFF_CHANNEL('s', 18, 0, 1), + .reg_access_offload_chan_spec = AD4000_DIFF_CHANNEL('s', 18, 1, 1), + .time_spec = &ad4000_t_spec, + .max_rate_hz = 500 * KILO, +}; + +static const struct ad4000_chip_info ad4020_chip_info = { + .dev_name = "ad4020", + .chan_spec = AD4000_DIFF_CHANNELS('s', 20, 0), + .reg_access_chan_spec = AD4000_DIFF_CHANNELS('s', 20, 1), + .offload_chan_spec = AD4000_DIFF_CHANNEL('s', 20, 0, 1), + .reg_access_offload_chan_spec = AD4000_DIFF_CHANNEL('s', 20, 1, 1), + .time_spec = &ad4020_t_spec, + .max_rate_hz = 1800 * KILO, +}; + +static const struct ad4000_chip_info ad4021_chip_info = { + .dev_name = "ad4021", + .chan_spec = AD4000_DIFF_CHANNELS('s', 20, 0), + .reg_access_chan_spec = AD4000_DIFF_CHANNELS('s', 20, 1), + .offload_chan_spec = AD4000_DIFF_CHANNEL('s', 20, 0, 1), + .reg_access_offload_chan_spec = AD4000_DIFF_CHANNEL('s', 20, 1, 1), + .time_spec = &ad4020_t_spec, + .max_rate_hz = 1 * MEGA, +}; + +static const struct ad4000_chip_info ad4022_chip_info = { + .dev_name = "ad4022", + .chan_spec = AD4000_DIFF_CHANNELS('s', 20, 0), + .reg_access_chan_spec = AD4000_DIFF_CHANNELS('s', 20, 1), + .offload_chan_spec = AD4000_DIFF_CHANNEL('s', 20, 0, 1), + .reg_access_offload_chan_spec = AD4000_DIFF_CHANNEL('s', 20, 1, 1), + .time_spec = &ad4020_t_spec, + .max_rate_hz = 500 * KILO, +}; + +static const struct ad4000_chip_info adaq4001_chip_info = { + .dev_name = "adaq4001", + .chan_spec = AD4000_DIFF_CHANNELS('s', 16, 0), + .reg_access_chan_spec = AD4000_DIFF_CHANNELS('s', 16, 1), + .offload_chan_spec = AD4000_DIFF_CHANNEL('s', 16, 0, 1), + .reg_access_offload_chan_spec = AD4000_DIFF_CHANNEL('s', 16, 1, 1), + .time_spec = &ad4000_t_spec, + .has_hardware_gain = true, + .max_rate_hz = 2 * MEGA, +}; + +static const struct ad4000_chip_info adaq4003_chip_info = { + .dev_name = "adaq4003", + .chan_spec = AD4000_DIFF_CHANNELS('s', 18, 0), + .reg_access_chan_spec = AD4000_DIFF_CHANNELS('s', 18, 1), + .offload_chan_spec = AD4000_DIFF_CHANNEL('s', 18, 0, 1), + .reg_access_offload_chan_spec = AD4000_DIFF_CHANNEL('s', 18, 1, 1), + .time_spec = &ad4000_t_spec, + .has_hardware_gain = true, + .max_rate_hz = 2 * MEGA, +}; + +static const struct ad4000_chip_info ad7685_chip_info = { + .dev_name = "ad7685", + .chan_spec = AD4000_PSEUDO_DIFF_CHANNELS('u', 16, 0), + .offload_chan_spec = AD4000_PSEUDO_DIFF_CHANNEL('u', 16, 0, 1), + .time_spec = &ad7687_t_spec, + .max_rate_hz = 250 * KILO, +}; + +static const struct ad4000_chip_info ad7686_chip_info = { + .dev_name = "ad7686", + .chan_spec = AD4000_PSEUDO_DIFF_CHANNELS('u', 16, 0), + .offload_chan_spec = AD4000_PSEUDO_DIFF_CHANNEL('u', 16, 0, 1), + .time_spec = &ad7686_t_spec, + .max_rate_hz = 500 * KILO, +}; + +static const struct ad4000_chip_info ad7687_chip_info = { + .dev_name = "ad7687", + .chan_spec = AD4000_DIFF_CHANNELS('s', 16, 0), + .offload_chan_spec = AD4000_DIFF_CHANNEL('s', 16, 0, 1), + .time_spec = &ad7687_t_spec, + .max_rate_hz = 250 * KILO, +}; + +static const struct ad4000_chip_info ad7688_chip_info = { + .dev_name = "ad7688", + .chan_spec = AD4000_DIFF_CHANNELS('s', 16, 0), + .offload_chan_spec = AD4000_DIFF_CHANNEL('s', 16, 0, 1), + .time_spec = &ad7686_t_spec, + .max_rate_hz = 500 * KILO, +}; + +static const struct ad4000_chip_info ad7690_chip_info = { + .dev_name = "ad7690", + .chan_spec = AD4000_DIFF_CHANNELS('s', 18, 0), + .offload_chan_spec = AD4000_DIFF_CHANNEL('s', 18, 0, 1), + .time_spec = &ad7690_t_spec, + .max_rate_hz = 400 * KILO, +}; + +static const struct ad4000_chip_info ad7691_chip_info = { + .dev_name = "ad7691", + .chan_spec = AD4000_DIFF_CHANNELS('s', 18, 0), + .offload_chan_spec = AD4000_DIFF_CHANNEL('s', 18, 0, 1), + .time_spec = &ad7691_t_spec, + .max_rate_hz = 250 * KILO, +}; + +static const struct ad4000_chip_info ad7693_chip_info = { + .dev_name = "ad7693", + .chan_spec = AD4000_DIFF_CHANNELS('s', 16, 0), + .offload_chan_spec = AD4000_DIFF_CHANNEL('s', 16, 0, 1), + .time_spec = &ad7686_t_spec, + .max_rate_hz = 500 * KILO, +}; + +static const struct ad4000_chip_info ad7942_chip_info = { + .dev_name = "ad7942", + .chan_spec = AD4000_PSEUDO_DIFF_CHANNELS('u', 14, 0), + .offload_chan_spec = AD4000_PSEUDO_DIFF_CHANNEL('u', 14, 0, 1), + .time_spec = &ad7687_t_spec, + .max_rate_hz = 250 * KILO, +}; + +static const struct ad4000_chip_info ad7946_chip_info = { + .dev_name = "ad7946", + .chan_spec = AD4000_PSEUDO_DIFF_CHANNELS('u', 14, 0), + .offload_chan_spec = AD4000_PSEUDO_DIFF_CHANNEL('u', 14, 0, 1), + .time_spec = &ad7686_t_spec, + .max_rate_hz = 500 * KILO, +}; + +static const struct ad4000_chip_info ad7980_chip_info = { + .dev_name = "ad7980", + .chan_spec = AD4000_PSEUDO_DIFF_CHANNELS('u', 16, 0), + .offload_chan_spec = AD4000_PSEUDO_DIFF_CHANNEL('u', 16, 0, 1), + .time_spec = &ad7980_t_spec, + .max_rate_hz = 1 * MEGA, +}; + +static const struct ad4000_chip_info ad7982_chip_info = { + .dev_name = "ad7982", + .chan_spec = AD4000_DIFF_CHANNELS('s', 18, 0), + .offload_chan_spec = AD4000_DIFF_CHANNEL('s', 18, 0, 1), + .time_spec = &ad7980_t_spec, + .max_rate_hz = 1 * MEGA, +}; + +static const struct ad4000_chip_info ad7983_chip_info = { + .dev_name = "ad7983", + .chan_spec = AD4000_PSEUDO_DIFF_CHANNELS('u', 16, 0), + .offload_chan_spec = AD4000_PSEUDO_DIFF_CHANNEL('u', 16, 0, 1), + .time_spec = &ad7983_t_spec, + .max_rate_hz = 1 * MEGA + 333 * KILO + 333, +}; + +static const struct ad4000_chip_info ad7984_chip_info = { + .dev_name = "ad7984", + .chan_spec = AD4000_DIFF_CHANNELS('s', 18, 0), + .offload_chan_spec = AD4000_DIFF_CHANNEL('s', 18, 0, 1), + .time_spec = &ad7983_t_spec, + .max_rate_hz = 1 * MEGA + 333 * KILO + 333, +}; + +static const struct ad4000_chip_info ad7988_1_chip_info = { + .dev_name = "ad7988-1", + .chan_spec = AD4000_PSEUDO_DIFF_CHANNELS('u', 16, 0), + .offload_chan_spec = AD4000_PSEUDO_DIFF_CHANNEL('u', 16, 0, 1), + .time_spec = &ad7988_1_t_spec, + .max_rate_hz = 100 * KILO, +}; + +static const struct ad4000_chip_info ad7988_5_chip_info = { + .dev_name = "ad7988-5", + .chan_spec = AD4000_PSEUDO_DIFF_CHANNELS('u', 16, 0), + .offload_chan_spec = AD4000_PSEUDO_DIFF_CHANNEL('u', 16, 0, 1), + .time_spec = &ad7686_t_spec, + .max_rate_hz = 500 * KILO, +}; + +static const struct spi_offload_config ad4000_offload_config = { + .capability_flags = SPI_OFFLOAD_CAP_TRIGGER | + SPI_OFFLOAD_CAP_RX_STREAM_DMA, +}; + +struct ad4000_state { + struct spi_device *spi; + struct gpio_desc *cnv_gpio; + struct spi_transfer xfers[2]; + struct spi_message msg; + struct spi_transfer offload_xfer; + struct spi_message offload_msg; + struct spi_offload *offload; + struct spi_offload_trigger *offload_trigger; + bool using_offload; + unsigned long offload_trigger_hz; + int max_rate_hz; + struct mutex lock; /* Protect read modify write cycle */ + int vref_mv; + enum ad4000_sdi sdi_pin; + bool span_comp; + u16 gain_milli; + int scale_tbl[AD4000_SCALE_OPTIONS][2]; + const struct ad4000_time_spec *time_spec; + + /* + * DMA (thus cache coherency maintenance) requires the transfer buffers + * to live in their own cache lines. + */ + struct { + union { + __be16 sample_buf16_be; + __be32 sample_buf32_be; + u16 sample_buf16; + u32 sample_buf32; + } data; + aligned_s64 timestamp; + } scan __aligned(IIO_DMA_MINALIGN); + u8 tx_buf[2]; + u8 rx_buf[2]; +}; + +static void ad4000_fill_scale_tbl(struct ad4000_state *st, + struct iio_chan_spec const *chan) +{ + int val, tmp0, tmp1; + int scale_bits; + u64 tmp2; + + /* + * ADCs that output two's complement code have one less bit to express + * voltage magnitude. + */ + if (chan->scan_type.sign == 's') + scale_bits = chan->scan_type.realbits - 1; + else + scale_bits = chan->scan_type.realbits; + + /* + * The gain is stored as a fraction of 1000 and, as we need to + * divide vref_mv by the gain, we invert the gain/1000 fraction. + * Also multiply by an extra MILLI to preserve precision. + * Thus, we have MILLI * MILLI equals MICRO as fraction numerator. + */ + val = mult_frac(st->vref_mv, MICRO, st->gain_milli); + + /* Would multiply by NANO here but we multiplied by extra MILLI */ + tmp2 = (u64)val * MICRO >> scale_bits; + tmp0 = div_s64_rem(tmp2, NANO, &tmp1); + + /* Store scale for when span compression is disabled */ + st->scale_tbl[0][0] = tmp0; /* Integer part */ + st->scale_tbl[0][1] = abs(tmp1); /* Fractional part */ + + /* Store scale for when span compression is enabled */ + st->scale_tbl[1][0] = tmp0; + + /* The integer part is always zero so don't bother to divide it. */ + if (chan->differential) + st->scale_tbl[1][1] = DIV_ROUND_CLOSEST(abs(tmp1) * 4, 5); + else + st->scale_tbl[1][1] = DIV_ROUND_CLOSEST(abs(tmp1) * 9, 10); +} + +static int ad4000_write_reg(struct ad4000_state *st, uint8_t val) +{ + st->tx_buf[0] = AD4000_WRITE_COMMAND; + st->tx_buf[1] = val; + return spi_write(st->spi, st->tx_buf, ARRAY_SIZE(st->tx_buf)); +} + +static int ad4000_read_reg(struct ad4000_state *st, unsigned int *val) +{ + struct spi_transfer t = { + .tx_buf = st->tx_buf, + .rx_buf = st->rx_buf, + .len = 2, + }; + int ret; + + st->tx_buf[0] = AD4000_READ_COMMAND; + ret = spi_sync_transfer(st->spi, &t, 1); + if (ret < 0) + return ret; + + *val = st->rx_buf[1]; + return ret; +} + +static int ad4000_set_sampling_freq(struct ad4000_state *st, int freq) +{ + struct spi_offload_trigger_config config = { + .type = SPI_OFFLOAD_TRIGGER_PERIODIC, + .periodic = { + .frequency_hz = freq, + }, + }; + int ret; + + ret = spi_offload_trigger_validate(st->offload_trigger, &config); + if (ret) + return ret; + + st->offload_trigger_hz = config.periodic.frequency_hz; + + return 0; +} + +static int ad4000_convert_and_acquire(struct ad4000_state *st) +{ + int ret; + + /* + * In 4-wire mode, the CNV line is held high for the entire conversion + * and acquisition process. In other modes, the CNV GPIO is optional + * and, if provided, replaces controller CS. If CNV GPIO is not defined + * gpiod_set_value_cansleep() has no effect. + */ + gpiod_set_value_cansleep(st->cnv_gpio, 1); + ret = spi_sync(st->spi, &st->msg); + gpiod_set_value_cansleep(st->cnv_gpio, 0); + + return ret; +} + +static int ad4000_single_conversion(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, int *val) +{ + struct ad4000_state *st = iio_priv(indio_dev); + u32 sample; + int ret; + + ret = ad4000_convert_and_acquire(st); + if (ret < 0) + return ret; + + if (chan->scan_type.endianness == IIO_BE) { + if (chan->scan_type.realbits > 16) + sample = be32_to_cpu(st->scan.data.sample_buf32_be); + else + sample = be16_to_cpu(st->scan.data.sample_buf16_be); + } else { + if (chan->scan_type.realbits > 16) + sample = st->scan.data.sample_buf32; + else + sample = st->scan.data.sample_buf16; + } + + sample >>= chan->scan_type.shift; + + if (chan->scan_type.sign == 's') + *val = sign_extend32(sample, chan->scan_type.realbits - 1); + else + *val = sample; + + return IIO_VAL_INT; +} + +static int ad4000_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int *val, + int *val2, long info) +{ + struct ad4000_state *st = iio_priv(indio_dev); + int ret; + + switch (info) { + case IIO_CHAN_INFO_RAW: + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; + + ret = ad4000_single_conversion(indio_dev, chan, val); + iio_device_release_direct(indio_dev); + return ret; + case IIO_CHAN_INFO_SCALE: + *val = st->scale_tbl[st->span_comp][0]; + *val2 = st->scale_tbl[st->span_comp][1]; + return IIO_VAL_INT_PLUS_NANO; + case IIO_CHAN_INFO_OFFSET: + *val = 0; + if (st->span_comp) + *val = mult_frac(st->vref_mv, 1, 10); + + return IIO_VAL_INT; + case IIO_CHAN_INFO_SAMP_FREQ: + *val = st->offload_trigger_hz; + return IIO_VAL_INT; + default: + return -EINVAL; + } +} + +static int ad4000_read_avail(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + const int **vals, int *type, int *length, + long info) +{ + struct ad4000_state *st = iio_priv(indio_dev); + + switch (info) { + case IIO_CHAN_INFO_SCALE: + *vals = (int *)st->scale_tbl; + *length = AD4000_SCALE_OPTIONS * 2; + *type = IIO_VAL_INT_PLUS_NANO; + return IIO_AVAIL_LIST; + default: + return -EINVAL; + } +} + +static int ad4000_write_raw_get_fmt(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, long mask) +{ + switch (mask) { + case IIO_CHAN_INFO_SCALE: + return IIO_VAL_INT_PLUS_NANO; + default: + return IIO_VAL_INT_PLUS_MICRO; + } +} + +static int __ad4000_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val2) +{ + struct ad4000_state *st = iio_priv(indio_dev); + unsigned int reg_val; + bool span_comp_en; + int ret; + + guard(mutex)(&st->lock); + + ret = ad4000_read_reg(st, ®_val); + if (ret < 0) + return ret; + + span_comp_en = val2 == st->scale_tbl[1][1]; + reg_val &= ~AD4000_CFG_SPAN_COMP; + reg_val |= FIELD_PREP(AD4000_CFG_SPAN_COMP, span_comp_en); + + ret = ad4000_write_reg(st, reg_val); + if (ret < 0) + return ret; + + st->span_comp = span_comp_en; + return 0; +} + +static int ad4000_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, int val2, long mask) +{ + struct ad4000_state *st = iio_priv(indio_dev); + int ret; + + switch (mask) { + case IIO_CHAN_INFO_SCALE: + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; + ret = __ad4000_write_raw(indio_dev, chan, val2); + iio_device_release_direct(indio_dev); + return ret; + case IIO_CHAN_INFO_SAMP_FREQ: + if (val < 1 || val > st->max_rate_hz) + return -EINVAL; + + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; + ret = ad4000_set_sampling_freq(st, val); + iio_device_release_direct(indio_dev); + return ret; + default: + return -EINVAL; + } +} + +static irqreturn_t ad4000_trigger_handler(int irq, void *p) +{ + struct iio_poll_func *pf = p; + struct iio_dev *indio_dev = pf->indio_dev; + struct ad4000_state *st = iio_priv(indio_dev); + int ret; + + ret = ad4000_convert_and_acquire(st); + if (ret < 0) + goto err_out; + + iio_push_to_buffers_with_ts(indio_dev, &st->scan, sizeof(st->scan), + pf->timestamp); + +err_out: + iio_trigger_notify_done(indio_dev->trig); + return IRQ_HANDLED; +} + +static const struct iio_info ad4000_reg_access_info = { + .read_raw = &ad4000_read_raw, + .read_avail = &ad4000_read_avail, + .write_raw = &ad4000_write_raw, + .write_raw_get_fmt = &ad4000_write_raw_get_fmt, +}; + +static const struct iio_info ad4000_offload_info = { + .read_raw = &ad4000_read_raw, + .write_raw = &ad4000_write_raw, + .write_raw_get_fmt = &ad4000_write_raw_get_fmt, +}; + +static const struct iio_info ad4000_info = { + .read_raw = &ad4000_read_raw, +}; + +static int ad4000_offload_buffer_postenable(struct iio_dev *indio_dev) +{ + struct ad4000_state *st = iio_priv(indio_dev); + struct spi_offload_trigger_config config = { + .type = SPI_OFFLOAD_TRIGGER_PERIODIC, + .periodic = { + .frequency_hz = st->offload_trigger_hz, + }, + }; + + return spi_offload_trigger_enable(st->offload, st->offload_trigger, + &config); +} + +static int ad4000_offload_buffer_predisable(struct iio_dev *indio_dev) +{ + struct ad4000_state *st = iio_priv(indio_dev); + + spi_offload_trigger_disable(st->offload, st->offload_trigger); + + return 0; +} + +static const struct iio_buffer_setup_ops ad4000_offload_buffer_setup_ops = { + .postenable = &ad4000_offload_buffer_postenable, + .predisable = &ad4000_offload_buffer_predisable, +}; + +static int ad4000_spi_offload_setup(struct iio_dev *indio_dev, + struct ad4000_state *st) +{ + struct spi_device *spi = st->spi; + struct device *dev = &spi->dev; + struct dma_chan *rx_dma; + int ret; + + st->offload_trigger = devm_spi_offload_trigger_get(dev, st->offload, + SPI_OFFLOAD_TRIGGER_PERIODIC); + if (IS_ERR(st->offload_trigger)) + return dev_err_probe(dev, PTR_ERR(st->offload_trigger), + "Failed to get offload trigger\n"); + + ret = ad4000_set_sampling_freq(st, st->max_rate_hz); + if (ret) + return dev_err_probe(dev, ret, + "Failed to set sampling frequency\n"); + + rx_dma = devm_spi_offload_rx_stream_request_dma_chan(dev, st->offload); + if (IS_ERR(rx_dma)) + return dev_err_probe(dev, PTR_ERR(rx_dma), + "Failed to get offload RX DMA\n"); + + ret = devm_iio_dmaengine_buffer_setup_with_handle(dev, indio_dev, rx_dma, + IIO_BUFFER_DIRECTION_IN); + if (ret) + return dev_err_probe(dev, ret, "Failed to setup DMA buffer\n"); + + return 0; +} + +/* + * This executes a data sample transfer when using SPI offloading. The device + * connections should be in "3-wire" mode, selected either when the adi,sdi-pin + * device tree property is absent or set to "high". Also, the ADC CNV pin must + * be connected to a SPI controller CS (it can't be connected to a GPIO). + * + * In order to achieve the maximum sample rate, we only do one transfer per + * SPI offload trigger. Because the ADC output has a one sample latency (delay) + * when the device is wired in "3-wire" mode and only one transfer per sample is + * being made in turbo mode, the first data sample is not valid because it + * contains the output of an earlier conversion result. We also set transfer + * `bits_per_word` to achieve higher throughput by using the minimum number of + * SCLK cycles. Also, a delay is added to make sure we meet the minimum quiet + * time before releasing the CS line. + * + * Note that, with `bits_per_word` set to the number of ADC precision bits, + * transfers use larger word sizes that get stored in 'in-memory wordsizes' that + * are always in native CPU byte order. Because of that, IIO buffer elements + * ought to be read in CPU endianness which requires setting IIO scan_type + * endianness accordingly (i.e. IIO_CPU). + */ +static int ad4000_prepare_offload_message(struct ad4000_state *st, + const struct iio_chan_spec *chan) +{ + struct spi_transfer *xfer = &st->offload_xfer; + + xfer->bits_per_word = chan->scan_type.realbits; + xfer->len = chan->scan_type.realbits > 16 ? 4 : 2; + xfer->delay.value = st->time_spec->t_quiet2_ns; + xfer->delay.unit = SPI_DELAY_UNIT_NSECS; + xfer->offload_flags = SPI_OFFLOAD_XFER_RX_STREAM; + + spi_message_init_with_transfers(&st->offload_msg, xfer, 1); + st->offload_msg.offload = st->offload; + + return devm_spi_optimize_message(&st->spi->dev, st->spi, &st->offload_msg); +} + +/* + * This executes a data sample transfer for when the device connections are + * in "3-wire" mode, selected when the adi,sdi-pin device tree property is + * absent or set to "high". In this connection mode, the ADC SDI pin is + * connected to MOSI or to VIO and ADC CNV pin is connected either to a SPI + * controller CS or to a GPIO. + * AD4000 series of devices initiate conversions on the rising edge of CNV pin. + * + * If the CNV pin is connected to an SPI controller CS line (which is by default + * active low), the ADC readings would have a latency (delay) of one read. + * Moreover, since we also do ADC sampling for filling the buffer on triggered + * buffer mode, the timestamps of buffer readings would be disarranged. + * To prevent the read latency and reduce the time discrepancy between the + * sample read request and the time of actual sampling by the ADC, do a + * preparatory transfer to pulse the CS/CNV line. + */ +static int ad4000_prepare_3wire_mode_message(struct ad4000_state *st, + const struct iio_chan_spec *chan) +{ + struct spi_transfer *xfers = st->xfers; + + xfers[0].cs_change = 1; + xfers[0].cs_change_delay.value = st->time_spec->t_conv_ns; + xfers[0].cs_change_delay.unit = SPI_DELAY_UNIT_NSECS; + + xfers[1].rx_buf = &st->scan.data; + xfers[1].len = chan->scan_type.realbits > 16 ? 4 : 2; + + /* + * If the device is set up for SPI offloading, IIO channel scan_type is + * set to IIO_CPU. When that is the case, use larger SPI word sizes for + * single-shot reads too. Thus, sample data can be correctly handled in + * ad4000_single_conversion() according to scan_type endianness. + */ + if (chan->scan_type.endianness != IIO_BE) + xfers[1].bits_per_word = chan->scan_type.realbits; + xfers[1].delay.value = st->time_spec->t_quiet2_ns; + xfers[1].delay.unit = SPI_DELAY_UNIT_NSECS; + + spi_message_init_with_transfers(&st->msg, st->xfers, 2); + + return devm_spi_optimize_message(&st->spi->dev, st->spi, &st->msg); +} + +/* + * This executes a data sample transfer for when the device connections are + * in "4-wire" mode, selected when the adi,sdi-pin device tree property is + * set to "cs". In this connection mode, the controller CS pin is connected to + * ADC SDI pin and a GPIO is connected to ADC CNV pin. + * The GPIO connected to ADC CNV pin is set outside of the SPI transfer. + */ +static int ad4000_prepare_4wire_mode_message(struct ad4000_state *st, + const struct iio_chan_spec *chan) +{ + struct spi_transfer *xfers = st->xfers; + + /* + * Dummy transfer to cause enough delay between CNV going high and SDI + * going low. + */ + xfers[0].cs_off = 1; + xfers[0].delay.value = st->time_spec->t_conv_ns; + xfers[0].delay.unit = SPI_DELAY_UNIT_NSECS; + + xfers[1].rx_buf = &st->scan.data; + xfers[1].len = BITS_TO_BYTES(chan->scan_type.storagebits); + + spi_message_init_with_transfers(&st->msg, st->xfers, 2); + + return devm_spi_optimize_message(&st->spi->dev, st->spi, &st->msg); +} + +static int ad4000_config(struct ad4000_state *st) +{ + unsigned int reg_val = AD4000_CONFIG_REG_DEFAULT; + + if (device_property_present(&st->spi->dev, "adi,high-z-input")) + reg_val |= FIELD_PREP(AD4000_CFG_HIGHZ, 1); + + if (st->using_offload) + reg_val |= FIELD_PREP(AD4000_CFG_TURBO, 1); + + return ad4000_write_reg(st, reg_val); +} + +static int ad4000_probe(struct spi_device *spi) +{ + const struct ad4000_chip_info *chip; + struct device *dev = &spi->dev; + struct iio_dev *indio_dev; + struct ad4000_state *st; + int gain_idx, ret; + + indio_dev = devm_iio_device_alloc(dev, sizeof(*st)); + if (!indio_dev) + return -ENOMEM; + + chip = spi_get_device_match_data(spi); + if (!chip) + return -EINVAL; + + st = iio_priv(indio_dev); + st->spi = spi; + st->time_spec = chip->time_spec; + st->max_rate_hz = chip->max_rate_hz; + + ret = devm_regulator_bulk_get_enable(dev, ARRAY_SIZE(ad4000_power_supplies), + ad4000_power_supplies); + if (ret) + return dev_err_probe(dev, ret, "Failed to enable power supplies\n"); + + ret = devm_regulator_get_enable_read_voltage(dev, "ref"); + if (ret < 0) + return dev_err_probe(dev, ret, + "Failed to get ref regulator reference\n"); + st->vref_mv = ret / 1000; + + st->cnv_gpio = devm_gpiod_get_optional(dev, "cnv", GPIOD_OUT_HIGH); + if (IS_ERR(st->cnv_gpio)) + return dev_err_probe(dev, PTR_ERR(st->cnv_gpio), + "Failed to get CNV GPIO"); + + st->offload = devm_spi_offload_get(dev, spi, &ad4000_offload_config); + ret = PTR_ERR_OR_ZERO(st->offload); + if (ret && ret != -ENODEV) + return dev_err_probe(dev, ret, "Failed to get offload\n"); + + st->using_offload = !IS_ERR(st->offload); + if (st->using_offload) { + indio_dev->setup_ops = &ad4000_offload_buffer_setup_ops; + ret = ad4000_spi_offload_setup(indio_dev, st); + if (ret) + return ret; + } else { + ret = devm_iio_triggered_buffer_setup(dev, indio_dev, + &iio_pollfunc_store_time, + &ad4000_trigger_handler, + NULL); + if (ret) + return ret; + } + + ret = device_property_match_property_string(dev, "adi,sdi-pin", + ad4000_sdi_pin, + ARRAY_SIZE(ad4000_sdi_pin)); + if (ret < 0 && ret != -EINVAL) + return dev_err_probe(dev, ret, + "getting adi,sdi-pin property failed\n"); + + /* Default to usual SPI connections if pin properties are not present */ + st->sdi_pin = ret == -EINVAL ? AD4000_SDI_MOSI : ret; + switch (st->sdi_pin) { + case AD4000_SDI_MOSI: + indio_dev->info = &ad4000_reg_access_info; + + /* + * In "3-wire mode", the ADC SDI line must be kept high when + * data is not being clocked out of the controller. + * Request the SPI controller to make MOSI idle high. + */ + spi->mode |= SPI_MOSI_IDLE_HIGH; + ret = spi_setup(spi); + if (ret < 0) + return ret; + + if (st->using_offload) { + indio_dev->channels = &chip->reg_access_offload_chan_spec; + indio_dev->num_channels = 1; + ret = ad4000_prepare_offload_message(st, indio_dev->channels); + if (ret) + return dev_err_probe(dev, ret, + "Failed to optimize SPI msg\n"); + } else { + indio_dev->channels = chip->reg_access_chan_spec; + indio_dev->num_channels = ARRAY_SIZE(chip->reg_access_chan_spec); + } + + /* + * Call ad4000_prepare_3wire_mode_message() so single-shot read + * SPI messages are always initialized. + */ + ret = ad4000_prepare_3wire_mode_message(st, &indio_dev->channels[0]); + if (ret) + return dev_err_probe(dev, ret, + "Failed to optimize SPI msg\n"); + + ret = ad4000_config(st); + if (ret < 0) + return dev_err_probe(dev, ret, "Failed to config device\n"); + + break; + case AD4000_SDI_VIO: + if (st->using_offload) { + indio_dev->info = &ad4000_offload_info; + indio_dev->channels = &chip->offload_chan_spec; + indio_dev->num_channels = 1; + + ret = ad4000_prepare_offload_message(st, indio_dev->channels); + if (ret) + return dev_err_probe(dev, ret, + "Failed to optimize SPI msg\n"); + } else { + indio_dev->info = &ad4000_info; + indio_dev->channels = chip->chan_spec; + indio_dev->num_channels = ARRAY_SIZE(chip->chan_spec); + } + + ret = ad4000_prepare_3wire_mode_message(st, &indio_dev->channels[0]); + if (ret) + return dev_err_probe(dev, ret, + "Failed to optimize SPI msg\n"); + + break; + case AD4000_SDI_CS: + if (st->using_offload) + return dev_err_probe(dev, -EPROTONOSUPPORT, + "Unsupported sdi-pin + offload config\n"); + indio_dev->info = &ad4000_info; + indio_dev->channels = chip->chan_spec; + indio_dev->num_channels = ARRAY_SIZE(chip->chan_spec); + ret = ad4000_prepare_4wire_mode_message(st, &indio_dev->channels[0]); + if (ret) + return dev_err_probe(dev, ret, + "Failed to optimize SPI msg\n"); + + break; + case AD4000_SDI_GND: + return dev_err_probe(dev, -EPROTONOSUPPORT, + "Unsupported connection mode\n"); + + default: + return dev_err_probe(dev, -EINVAL, "Unrecognized connection mode\n"); + } + + indio_dev->name = chip->dev_name; + + ret = devm_mutex_init(dev, &st->lock); + if (ret) + return ret; + + st->gain_milli = 1000; + if (chip->has_hardware_gain) { + ret = device_property_read_u16(dev, "adi,gain-milli", + &st->gain_milli); + if (!ret) { + /* Match gain value from dt to one of supported gains */ + gain_idx = find_closest(st->gain_milli, ad4000_gains, + ARRAY_SIZE(ad4000_gains)); + st->gain_milli = ad4000_gains[gain_idx]; + } else { + return dev_err_probe(dev, ret, + "Failed to read gain property\n"); + } + } + + ad4000_fill_scale_tbl(st, &indio_dev->channels[0]); + + return devm_iio_device_register(dev, indio_dev); +} + +static const struct spi_device_id ad4000_id[] = { + { .name = "ad4000", .driver_data = (kernel_ulong_t)&ad4000_chip_info }, + { .name = "ad4001", .driver_data = (kernel_ulong_t)&ad4001_chip_info }, + { .name = "ad4002", .driver_data = (kernel_ulong_t)&ad4002_chip_info }, + { .name = "ad4003", .driver_data = (kernel_ulong_t)&ad4003_chip_info }, + { .name = "ad4004", .driver_data = (kernel_ulong_t)&ad4004_chip_info }, + { .name = "ad4005", .driver_data = (kernel_ulong_t)&ad4005_chip_info }, + { .name = "ad4006", .driver_data = (kernel_ulong_t)&ad4006_chip_info }, + { .name = "ad4007", .driver_data = (kernel_ulong_t)&ad4007_chip_info }, + { .name = "ad4008", .driver_data = (kernel_ulong_t)&ad4008_chip_info }, + { .name = "ad4010", .driver_data = (kernel_ulong_t)&ad4010_chip_info }, + { .name = "ad4011", .driver_data = (kernel_ulong_t)&ad4011_chip_info }, + { .name = "ad4020", .driver_data = (kernel_ulong_t)&ad4020_chip_info }, + { .name = "ad4021", .driver_data = (kernel_ulong_t)&ad4021_chip_info }, + { .name = "ad4022", .driver_data = (kernel_ulong_t)&ad4022_chip_info }, + { .name = "adaq4001", .driver_data = (kernel_ulong_t)&adaq4001_chip_info }, + { .name = "adaq4003", .driver_data = (kernel_ulong_t)&adaq4003_chip_info }, + { .name = "ad7685", .driver_data = (kernel_ulong_t)&ad7685_chip_info }, + { .name = "ad7686", .driver_data = (kernel_ulong_t)&ad7686_chip_info }, + { .name = "ad7687", .driver_data = (kernel_ulong_t)&ad7687_chip_info }, + { .name = "ad7688", .driver_data = (kernel_ulong_t)&ad7688_chip_info }, + { .name = "ad7690", .driver_data = (kernel_ulong_t)&ad7690_chip_info }, + { .name = "ad7691", .driver_data = (kernel_ulong_t)&ad7691_chip_info }, + { .name = "ad7693", .driver_data = (kernel_ulong_t)&ad7693_chip_info }, + { .name = "ad7942", .driver_data = (kernel_ulong_t)&ad7942_chip_info }, + { .name = "ad7946", .driver_data = (kernel_ulong_t)&ad7946_chip_info }, + { .name = "ad7980", .driver_data = (kernel_ulong_t)&ad7980_chip_info }, + { .name = "ad7982", .driver_data = (kernel_ulong_t)&ad7982_chip_info }, + { .name = "ad7983", .driver_data = (kernel_ulong_t)&ad7983_chip_info }, + { .name = "ad7984", .driver_data = (kernel_ulong_t)&ad7984_chip_info }, + { .name = "ad7988-1", .driver_data = (kernel_ulong_t)&ad7988_1_chip_info }, + { .name = "ad7988-5", .driver_data = (kernel_ulong_t)&ad7988_5_chip_info }, + { } +}; +MODULE_DEVICE_TABLE(spi, ad4000_id); + +static const struct of_device_id ad4000_of_match[] = { + { .compatible = "adi,ad4000", .data = &ad4000_chip_info }, + { .compatible = "adi,ad4001", .data = &ad4001_chip_info }, + { .compatible = "adi,ad4002", .data = &ad4002_chip_info }, + { .compatible = "adi,ad4003", .data = &ad4003_chip_info }, + { .compatible = "adi,ad4004", .data = &ad4004_chip_info }, + { .compatible = "adi,ad4005", .data = &ad4005_chip_info }, + { .compatible = "adi,ad4006", .data = &ad4006_chip_info }, + { .compatible = "adi,ad4007", .data = &ad4007_chip_info }, + { .compatible = "adi,ad4008", .data = &ad4008_chip_info }, + { .compatible = "adi,ad4010", .data = &ad4010_chip_info }, + { .compatible = "adi,ad4011", .data = &ad4011_chip_info }, + { .compatible = "adi,ad4020", .data = &ad4020_chip_info }, + { .compatible = "adi,ad4021", .data = &ad4021_chip_info }, + { .compatible = "adi,ad4022", .data = &ad4022_chip_info }, + { .compatible = "adi,adaq4001", .data = &adaq4001_chip_info }, + { .compatible = "adi,adaq4003", .data = &adaq4003_chip_info }, + { .compatible = "adi,ad7685", .data = &ad7685_chip_info }, + { .compatible = "adi,ad7686", .data = &ad7686_chip_info }, + { .compatible = "adi,ad7687", .data = &ad7687_chip_info }, + { .compatible = "adi,ad7688", .data = &ad7688_chip_info }, + { .compatible = "adi,ad7690", .data = &ad7690_chip_info }, + { .compatible = "adi,ad7691", .data = &ad7691_chip_info }, + { .compatible = "adi,ad7693", .data = &ad7693_chip_info }, + { .compatible = "adi,ad7942", .data = &ad7942_chip_info }, + { .compatible = "adi,ad7946", .data = &ad7946_chip_info }, + { .compatible = "adi,ad7980", .data = &ad7980_chip_info }, + { .compatible = "adi,ad7982", .data = &ad7982_chip_info }, + { .compatible = "adi,ad7983", .data = &ad7983_chip_info }, + { .compatible = "adi,ad7984", .data = &ad7984_chip_info }, + { .compatible = "adi,ad7988-1", .data = &ad7988_1_chip_info }, + { .compatible = "adi,ad7988-5", .data = &ad7988_5_chip_info }, + { } +}; +MODULE_DEVICE_TABLE(of, ad4000_of_match); + +static struct spi_driver ad4000_driver = { + .driver = { + .name = "ad4000", + .of_match_table = ad4000_of_match, + }, + .probe = ad4000_probe, + .id_table = ad4000_id, +}; +module_spi_driver(ad4000_driver); + +MODULE_AUTHOR("Marcelo Schmitt "); +MODULE_DESCRIPTION("Analog Devices AD4000 ADC driver"); +MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS("IIO_DMAENGINE_BUFFER"); diff --git a/drivers/iio/adc/ad4030.c b/drivers/iio/adc/ad4030.c new file mode 100644 index 000000000..e97400a1a --- /dev/null +++ b/drivers/iio/adc/ad4030.c @@ -0,0 +1,1802 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Analog Devices AD4030 and AD4630 ADC family driver. + * + * Copyright 2024 Analog Devices, Inc. + * Copyright 2024 BayLibre, SAS + * + * based on code from: + * Analog Devices, Inc. + * Sergiu Cuciurean + * Nuno Sa + * Marcelo Schmitt + * Liviu Adace + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#define AD4030_REG_INTERFACE_CONFIG_A 0x00 +#define AD4030_REG_INTERFACE_CONFIG_A_SW_RESET (BIT(0) | BIT(7)) +#define AD4030_REG_INTERFACE_CONFIG_B 0x01 +#define AD4030_REG_DEVICE_CONFIG 0x02 +#define AD4030_REG_CHIP_TYPE 0x03 +#define AD4030_REG_PRODUCT_ID_L 0x04 +#define AD4030_REG_PRODUCT_ID_H 0x05 +#define AD4030_REG_CHIP_GRADE 0x06 +#define AD4030_REG_CHIP_GRADE_AD4030_24_GRADE 0x10 +#define AD4030_REG_CHIP_GRADE_AD4630_16_GRADE 0x03 +#define AD4030_REG_CHIP_GRADE_AD4630_24_GRADE 0x00 +#define AD4030_REG_CHIP_GRADE_AD4632_16_GRADE 0x05 +#define AD4030_REG_CHIP_GRADE_AD4632_24_GRADE 0x02 +#define AD4030_REG_CHIP_GRADE_ADAQ4216_GRADE 0x1E +#define AD4030_REG_CHIP_GRADE_ADAQ4224_GRADE 0x1C +#define AD4030_REG_CHIP_GRADE_MASK_CHIP_GRADE GENMASK(7, 3) +#define AD4030_REG_SCRATCH_PAD 0x0A +#define AD4030_REG_SPI_REVISION 0x0B +#define AD4030_REG_VENDOR_L 0x0C +#define AD4030_REG_VENDOR_H 0x0D +#define AD4030_REG_STREAM_MODE 0x0E +#define AD4030_REG_INTERFACE_CONFIG_C 0x10 +#define AD4030_REG_INTERFACE_STATUS_A 0x11 +#define AD4030_REG_EXIT_CFG_MODE 0x14 +#define AD4030_REG_EXIT_CFG_MODE_EXIT_MSK BIT(0) +#define AD4030_REG_AVG 0x15 +#define AD4030_REG_AVG_MASK_AVG_SYNC BIT(7) +#define AD4030_REG_AVG_MASK_AVG_VAL GENMASK(4, 0) +#define AD4030_REG_OFFSET_X0_0 0x16 +#define AD4030_REG_OFFSET_X0_1 0x17 +#define AD4030_REG_OFFSET_X0_2 0x18 +#define AD4030_REG_OFFSET_X1_0 0x19 +#define AD4030_REG_OFFSET_X1_1 0x1A +#define AD4030_REG_OFFSET_X1_2 0x1B +#define AD4030_REG_OFFSET_BYTES_NB 3 +#define AD4030_REG_OFFSET_CHAN(ch) \ + (AD4030_REG_OFFSET_X0_2 + (AD4030_REG_OFFSET_BYTES_NB * (ch))) +#define AD4030_REG_GAIN_X0_LSB 0x1C +#define AD4030_REG_GAIN_X0_MSB 0x1D +#define AD4030_REG_GAIN_X1_LSB 0x1E +#define AD4030_REG_GAIN_X1_MSB 0x1F +#define AD4030_REG_GAIN_MAX_GAIN 1999970 +#define AD4030_REG_GAIN_BYTES_NB 2 +#define AD4030_REG_GAIN_CHAN(ch) \ + (AD4030_REG_GAIN_X0_MSB + (AD4030_REG_GAIN_BYTES_NB * (ch))) +#define AD4030_REG_MODES 0x20 +#define AD4030_REG_MODES_MASK_OUT_DATA_MODE GENMASK(2, 0) +#define AD4030_REG_MODES_MASK_LANE_MODE GENMASK(7, 6) +#define AD4030_REG_OSCILATOR 0x21 +#define AD4030_REG_IO 0x22 +#define AD4030_REG_IO_MASK_IO2X BIT(1) +#define AD4030_REG_PAT0 0x23 +#define AD4030_REG_PAT1 0x24 +#define AD4030_REG_PAT2 0x25 +#define AD4030_REG_PAT3 0x26 +#define AD4030_REG_DIG_DIAG 0x34 +#define AD4030_REG_DIG_ERR 0x35 + +/* Sequence starting with "1 0 1" to enable reg access */ +#define AD4030_REG_ACCESS 0xA0 + +#define AD4030_MAX_IIO_SAMPLE_SIZE_BUFFERED BITS_TO_BYTES(64) +#define AD4030_MAX_HARDWARE_CHANNEL_NB 2 +#define AD4030_MAX_IIO_CHANNEL_NB 5 +#define AD4030_SINGLE_COMMON_BYTE_CHANNELS_MASK 0b10 +#define AD4030_DUAL_COMMON_BYTE_CHANNELS_MASK 0b1100 +#define AD4030_GAIN_MIDLE_POINT 0x8000 +/* + * This accounts for 1 sample per channel plus one s64 for the timestamp, + * aligned on a s64 boundary + */ +#define AD4030_MAXIMUM_RX_BUFFER_SIZE \ + (ALIGN(AD4030_MAX_IIO_SAMPLE_SIZE_BUFFERED * \ + AD4030_MAX_HARDWARE_CHANNEL_NB, \ + sizeof(s64)) + sizeof(s64)) + +#define AD4030_VREF_MIN_UV (4096 * MILLI) +#define AD4030_VREF_MAX_UV (5000 * MILLI) +#define AD4030_VIO_THRESHOLD_UV (1400 * MILLI) +#define AD4030_SPI_MAX_XFER_LEN 8 +#define AD4030_SPI_MAX_REG_XFER_SPEED (80 * MEGA) +#define AD4030_TCNVH_NS 10 +#define AD4030_TCNVL_NS 20 +#define AD4030_TCYC_NS 500 +#define AD4030_TCYC_ADJUSTED_NS (AD4030_TCYC_NS - AD4030_TCNVL_NS) +#define AD4030_TRESET_PW_NS 50 +#define AD4632_TCYC_NS 2000 +#define AD4632_TCYC_ADJUSTED_NS (AD4632_TCYC_NS - AD4030_TCNVL_NS) +#define AD4030_TRESET_COM_DELAY_MS 750 +/* Datasheet says 9.8ns, so use the closest integer value */ +#define AD4030_TQUIET_CNV_DELAY_NS 10 + +/* HARDWARE_GAIN */ +#define ADAQ4616_PGA_PINS 2 +#define ADAQ4616_PGA_GAIN_MAX_NANO (NANO * 2 / 3) + +enum ad4030_out_mode { + AD4030_OUT_DATA_MD_DIFF, + AD4030_OUT_DATA_MD_16_DIFF_8_COM, + AD4030_OUT_DATA_MD_24_DIFF_8_COM, + AD4030_OUT_DATA_MD_30_AVERAGED_DIFF, + AD4030_OUT_DATA_MD_32_PATTERN, +}; + +enum { + AD4030_LANE_MD_1_PER_CH, + AD4030_LANE_MD_2_PER_CH, + AD4030_LANE_MD_4_PER_CH, + AD4030_LANE_MD_INTERLEAVED, +}; + +enum { + AD4030_SCAN_TYPE_NORMAL, + AD4030_SCAN_TYPE_AVG, +}; + +/* + * Gains computed as fractions of 1000 so they can be expressed by integers. + */ +static const int adaq4216_hw_gains_vpv[] = { + 1 * MILLI / 3, /* 0.333 */ + 5 * MILLI / 9, /* 0.555 */ + 20 * MILLI / 9, /* 0.2222 */ + 20 * MILLI / 3, /* 0.6666 */ +}; + +static const int adaq4216_hw_gains_frac[][2] = { + { 1, 3 }, /* 1/3 V/V gain */ + { 5, 9 }, /* 5/9 V/V gain */ + { 20, 9 }, /* 20/9 V/V gain */ + { 20, 3 }, /* 20/3 V/V gain */ +}; + +struct ad4030_chip_info { + const char *name; + const unsigned long *available_masks; + const struct iio_chan_spec channels[AD4030_MAX_IIO_CHANNEL_NB]; + const struct iio_chan_spec offload_channels[AD4030_MAX_IIO_CHANNEL_NB]; + u8 grade; + u8 precision_bits; + bool has_pga; + /* Number of hardware channels */ + int num_voltage_inputs; + unsigned int tcyc_ns; + unsigned int max_sample_rate_hz; +}; + +struct ad4030_state { + struct spi_device *spi; + struct regmap *regmap; + const struct ad4030_chip_info *chip; + struct gpio_desc *cnv_gpio; + int vref_uv; + int vio_uv; + int offset_avail[3]; + unsigned int avg_log2; + enum ad4030_out_mode mode; + /* Offload sampling */ + struct spi_transfer offload_xfer; + struct spi_message offload_msg; + struct spi_offload *offload; + struct spi_offload_trigger *offload_trigger; + struct spi_offload_trigger_config offload_trigger_config; + struct pwm_device *cnv_trigger; + size_t scale_avail_size; + struct pwm_waveform cnv_wf; + unsigned int scale_avail[ARRAY_SIZE(adaq4216_hw_gains_vpv)][2]; + struct gpio_descs *pga_gpios; + unsigned int pga_index; + + /* + * DMA (thus cache coherency maintenance) requires the transfer buffers + * to live in their own cache lines. + */ + u8 tx_data[AD4030_SPI_MAX_XFER_LEN] __aligned(IIO_DMA_MINALIGN); + union { + u8 raw[AD4030_MAXIMUM_RX_BUFFER_SIZE]; + struct { + s32 diff; + u8 common; + } single; + struct { + s32 diff[2]; + u8 common[2]; + } dual; + } rx_data; +}; + +/* + * For a chip with 2 hardware channel this will be used to create 2 common-mode + * channels: + * - voltage4 + * - voltage5 + * As the common-mode channels are after the differential ones, we compute the + * channel number like this: + * - _idx is the scan_index (the order in the output buffer) + * - _ch is the hardware channel number this common-mode channel is related + * - _idx - _ch gives us the number of channel in the chip + * - _idx - _ch * 2 is the starting number of the common-mode channels, since + * for each differential channel there is a common-mode channel + * - _idx - _ch * 2 + _ch gives the channel number for this specific common-mode + * channel + */ +#define AD4030_CHAN_CMO(_idx, _ch) { \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ + BIT(IIO_CHAN_INFO_SCALE), \ + .type = IIO_VOLTAGE, \ + .indexed = 1, \ + .address = (_ch), \ + .channel = ((_idx) - (_ch)) * 2 + (_ch), \ + .scan_index = (_idx), \ + .scan_type = { \ + .sign = 'u', \ + .storagebits = 8, \ + .realbits = 8, \ + .endianness = IIO_BE, \ + }, \ +} + +/* + * For a chip with 2 hardware channel this will be used to create 2 differential + * channels: + * - voltage0-voltage1 + * - voltage2-voltage3 + */ +#define __AD4030_CHAN_DIFF(_idx, _scan_type, _offload, _pga) { \ + .info_mask_shared_by_all = \ + (_offload ? BIT(IIO_CHAN_INFO_SAMP_FREQ) : 0) | \ + BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \ + .info_mask_shared_by_all_available = \ + BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \ + .info_mask_separate = BIT(IIO_CHAN_INFO_SCALE) | \ + BIT(IIO_CHAN_INFO_CALIBSCALE) | \ + BIT(IIO_CHAN_INFO_CALIBBIAS) | \ + BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_separate_available = BIT(IIO_CHAN_INFO_CALIBBIAS) | \ + (_pga ? BIT(IIO_CHAN_INFO_SCALE) : 0) | \ + BIT(IIO_CHAN_INFO_CALIBSCALE), \ + .type = IIO_VOLTAGE, \ + .indexed = 1, \ + .address = (_idx), \ + .channel = (_idx) * 2, \ + .channel2 = (_idx) * 2 + 1, \ + .scan_index = (_idx), \ + .differential = true, \ + .has_ext_scan_type = 1, \ + .ext_scan_type = _scan_type, \ + .num_ext_scan_type = ARRAY_SIZE(_scan_type), \ +} + +#define AD4030_CHAN_DIFF(_idx, _scan_type) \ + __AD4030_CHAN_DIFF(_idx, _scan_type, 0, 0) + +#define AD4030_OFFLOAD_CHAN_DIFF(_idx, _scan_type) \ + __AD4030_CHAN_DIFF(_idx, _scan_type, 1, 0) + +#define ADAQ4216_CHAN_DIFF(_idx, _scan_type) \ + __AD4030_CHAN_DIFF(_idx, _scan_type, 0, 1) + +#define ADAQ4216_OFFLOAD_CHAN_DIFF(_idx, _scan_type) \ + __AD4030_CHAN_DIFF(_idx, _scan_type, 1, 1) + +/* + * AD4030 can average over 2^N samples, where N = 1, 2, 3, ..., 16. + * We use N = 0 to mean no sample averaging. + */ +static const int ad4030_average_modes[] = { + BIT(0), /* No sampling average */ + BIT(1), BIT(2), BIT(3), BIT(4), + BIT(5), BIT(6), BIT(7), BIT(8), + BIT(9), BIT(10), BIT(11), BIT(12), + BIT(13), BIT(14), BIT(15), BIT(16), +}; + +static const struct spi_offload_config ad4030_offload_config = { + .capability_flags = SPI_OFFLOAD_CAP_TRIGGER | + SPI_OFFLOAD_CAP_RX_STREAM_DMA, +}; + +static int ad4030_enter_config_mode(struct ad4030_state *st) +{ + st->tx_data[0] = AD4030_REG_ACCESS; + + struct spi_transfer xfer = { + .tx_buf = st->tx_data, + .len = 1, + .speed_hz = AD4030_SPI_MAX_REG_XFER_SPEED, + }; + + return spi_sync_transfer(st->spi, &xfer, 1); +} + +static int ad4030_exit_config_mode(struct ad4030_state *st) +{ + st->tx_data[0] = 0; + st->tx_data[1] = AD4030_REG_EXIT_CFG_MODE; + st->tx_data[2] = AD4030_REG_EXIT_CFG_MODE_EXIT_MSK; + + struct spi_transfer xfer = { + .tx_buf = st->tx_data, + .len = 3, + .speed_hz = AD4030_SPI_MAX_REG_XFER_SPEED, + }; + + return spi_sync_transfer(st->spi, &xfer, 1); +} + +static int ad4030_spi_read(void *context, const void *reg, size_t reg_size, + void *val, size_t val_size) +{ + int ret; + struct ad4030_state *st = context; + struct spi_transfer xfer = { + .tx_buf = st->tx_data, + .rx_buf = st->rx_data.raw, + .len = reg_size + val_size, + .speed_hz = AD4030_SPI_MAX_REG_XFER_SPEED, + }; + + if (xfer.len > sizeof(st->tx_data) || + xfer.len > sizeof(st->rx_data.raw)) + return -EINVAL; + + ret = ad4030_enter_config_mode(st); + if (ret) + return ret; + + memset(st->tx_data, 0, sizeof(st->tx_data)); + memcpy(st->tx_data, reg, reg_size); + + ret = spi_sync_transfer(st->spi, &xfer, 1); + if (ret) + return ret; + + memcpy(val, &st->rx_data.raw[reg_size], val_size); + + return ad4030_exit_config_mode(st); +} + +static int ad4030_spi_write(void *context, const void *data, size_t count) +{ + int ret; + struct ad4030_state *st = context; + bool is_reset = count >= 3 && + ((u8 *)data)[0] == 0 && + ((u8 *)data)[1] == 0 && + ((u8 *)data)[2] == 0x81; + struct spi_transfer xfer = { + .tx_buf = st->tx_data, + .len = count, + .speed_hz = AD4030_SPI_MAX_REG_XFER_SPEED, + }; + + if (count > sizeof(st->tx_data)) + return -EINVAL; + + ret = ad4030_enter_config_mode(st); + if (ret) + return ret; + + memcpy(st->tx_data, data, count); + + ret = spi_sync_transfer(st->spi, &xfer, 1); + if (ret) + return ret; + + /* + * From datasheet: "After a [...] reset, no SPI commands or conversions + * can be started for 750us" + * After a reset we are in conversion mode, no need to exit config mode + */ + if (is_reset) { + fsleep(750); + return 0; + } + + return ad4030_exit_config_mode(st); +} + +static const struct regmap_bus ad4030_regmap_bus = { + .read = ad4030_spi_read, + .write = ad4030_spi_write, + .reg_format_endian_default = REGMAP_ENDIAN_BIG, +}; + +static const struct regmap_range ad4030_regmap_rd_range[] = { + regmap_reg_range(AD4030_REG_INTERFACE_CONFIG_A, AD4030_REG_CHIP_GRADE), + regmap_reg_range(AD4030_REG_SCRATCH_PAD, AD4030_REG_STREAM_MODE), + regmap_reg_range(AD4030_REG_INTERFACE_CONFIG_C, + AD4030_REG_INTERFACE_STATUS_A), + regmap_reg_range(AD4030_REG_EXIT_CFG_MODE, AD4030_REG_PAT3), + regmap_reg_range(AD4030_REG_DIG_DIAG, AD4030_REG_DIG_ERR), +}; + +static const struct regmap_range ad4030_regmap_wr_range[] = { + regmap_reg_range(AD4030_REG_CHIP_TYPE, AD4030_REG_CHIP_GRADE), + regmap_reg_range(AD4030_REG_SPI_REVISION, AD4030_REG_VENDOR_H), +}; + +static const struct regmap_access_table ad4030_regmap_rd_table = { + .yes_ranges = ad4030_regmap_rd_range, + .n_yes_ranges = ARRAY_SIZE(ad4030_regmap_rd_range), +}; + +static const struct regmap_access_table ad4030_regmap_wr_table = { + .no_ranges = ad4030_regmap_wr_range, + .n_no_ranges = ARRAY_SIZE(ad4030_regmap_wr_range), +}; + +static const struct regmap_config ad4030_regmap_config = { + .reg_bits = 16, + .val_bits = 8, + .read_flag_mask = 0x80, + .rd_table = &ad4030_regmap_rd_table, + .wr_table = &ad4030_regmap_wr_table, + .max_register = AD4030_REG_DIG_ERR, +}; + +static void ad4030_fill_scale_avail(struct ad4030_state *st) +{ + unsigned int mag_bits, int_part, fract_part; + u64 range; + + /* + * The maximum precision of differential channels is retrieved from the + * chip properties. The output code of differential channels is in two's + * complement format (i.e. signed), so the MSB is the sign bit and only + * (precision_bits - 1) bits express voltage magnitude. + */ + mag_bits = st->chip->precision_bits - 1; + + for (unsigned int i = 0; i < ARRAY_SIZE(adaq4216_hw_gains_frac); i++) { + range = mult_frac(st->vref_uv, adaq4216_hw_gains_frac[i][1], + adaq4216_hw_gains_frac[i][0]); + /* + * If range were in mV, we would multiply it by NANO below. + * Though, range is in µV so multiply it by MICRO only so the + * result after right shift and division scales output codes to + * millivolts. + */ + int_part = div_u64_rem((range * MICRO) >> mag_bits, NANO, &fract_part); + st->scale_avail[i][0] = int_part; + st->scale_avail[i][1] = fract_part; + } +} + +static int ad4030_set_pga_gain(struct ad4030_state *st) +{ + DECLARE_BITMAP(bitmap, ADAQ4616_PGA_PINS) = { }; + + bitmap_write(bitmap, st->pga_index, 0, ADAQ4616_PGA_PINS); + + return gpiod_multi_set_value_cansleep(st->pga_gpios, bitmap); +} + +static int ad4030_set_pga(struct iio_dev *indio_dev, int gain_int, int gain_fract) +{ + struct ad4030_state *st = iio_priv(indio_dev); + unsigned int mag_bits = st->chip->precision_bits - 1; + unsigned int tmp; + u64 gain_nano; + + if (!st->pga_gpios) + return -EINVAL; + + gain_nano = gain_int * NANO + gain_fract; + if (!in_range(gain_nano, 1, ADAQ4616_PGA_GAIN_MAX_NANO)) + return -EINVAL; + + tmp = DIV_ROUND_CLOSEST_ULL(gain_nano << mag_bits, NANO); + gain_nano = DIV_ROUND_CLOSEST(st->vref_uv, tmp); + st->pga_index = find_closest(gain_nano, adaq4216_hw_gains_vpv, + ARRAY_SIZE(adaq4216_hw_gains_vpv)); + + return ad4030_set_pga_gain(st); +} + +static int ad4030_get_chan_scale(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, + int *val2) +{ + struct ad4030_state *st = iio_priv(indio_dev); + const struct iio_scan_type *scan_type; + + scan_type = iio_get_current_scan_type(indio_dev, chan); + if (IS_ERR(scan_type)) + return PTR_ERR(scan_type); + + /* The LSB of the 8-bit common-mode data is always vref/256. */ + if (st->chip->has_pga && scan_type->realbits != 8) { + *val = st->scale_avail[st->pga_index][0]; + *val2 = st->scale_avail[st->pga_index][1]; + return IIO_VAL_INT_PLUS_NANO; + } + + if (chan->differential) + *val = (st->vref_uv * 2) / MILLI; + else + *val = st->vref_uv / MILLI; + + *val2 = scan_type->realbits; + + return IIO_VAL_FRACTIONAL_LOG2; +} + +static int ad4030_get_chan_calibscale(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, + int *val2) +{ + struct ad4030_state *st = iio_priv(indio_dev); + u16 gain; + int ret; + + ret = regmap_bulk_read(st->regmap, AD4030_REG_GAIN_CHAN(chan->address), + st->rx_data.raw, AD4030_REG_GAIN_BYTES_NB); + if (ret) + return ret; + + gain = get_unaligned_be16(st->rx_data.raw); + + /* From datasheet: multiplied output = input × gain word/0x8000 */ + *val = gain / AD4030_GAIN_MIDLE_POINT; + *val2 = mul_u64_u32_div(gain % AD4030_GAIN_MIDLE_POINT, NANO, + AD4030_GAIN_MIDLE_POINT); + + return IIO_VAL_INT_PLUS_NANO; +} + +/* Returns the offset where 1 LSB = (VREF/2^precision_bits - 1)/gain */ +static int ad4030_get_chan_calibbias(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val) +{ + struct ad4030_state *st = iio_priv(indio_dev); + int ret; + + ret = regmap_bulk_read(st->regmap, + AD4030_REG_OFFSET_CHAN(chan->address), + st->rx_data.raw, AD4030_REG_OFFSET_BYTES_NB); + if (ret) + return ret; + + switch (st->chip->precision_bits) { + case 16: + *val = sign_extend32(get_unaligned_be16(st->rx_data.raw), 15); + return IIO_VAL_INT; + + case 24: + *val = sign_extend32(get_unaligned_be24(st->rx_data.raw), 23); + return IIO_VAL_INT; + + default: + return -EINVAL; + } +} + +static void ad4030_get_sampling_freq(struct ad4030_state *st, int *freq) +{ + struct spi_offload_trigger_config *config = &st->offload_trigger_config; + + /* + * Conversion data is fetched from the device when the offload transfer + * is triggered. Thus, provide the SPI offload trigger frequency as the + * sampling frequency. + */ + *freq = config->periodic.frequency_hz; +} + +static int ad4030_update_conversion_rate(struct ad4030_state *st, + unsigned int freq_hz, unsigned int avg_log2) +{ + struct spi_offload_trigger_config *config = &st->offload_trigger_config; + unsigned int offload_period_ns, cnv_rate_hz; + struct pwm_waveform cnv_wf = { }; + u64 target = AD4030_TCNVH_NS; + u64 offload_offset_ns; + int ret; + + /* + * When averaging/oversampling over N samples, we fire the offload + * trigger once at every N pulses of the CNV signal. Conversely, the CNV + * signal needs to be N times faster than the offload trigger. Take that + * into account to correctly re-evaluate both the PWM waveform connected + * to CNV and the SPI offload trigger. + */ + cnv_rate_hz = freq_hz << avg_log2; + + cnv_wf.period_length_ns = DIV_ROUND_CLOSEST(NSEC_PER_SEC, cnv_rate_hz); + /* + * The datasheet lists a minimum time of 9.8 ns, but no maximum. If the + * rounded PWM's value is less than 10, increase the target value by 10 + * and attempt to round the waveform again, until the value is at least + * 10 ns. Use a separate variable to represent the target in case the + * rounding is severe enough to keep putting the first few results under + * the minimum 10ns condition checked by the while loop. + */ + do { + cnv_wf.duty_length_ns = target; + ret = pwm_round_waveform_might_sleep(st->cnv_trigger, &cnv_wf); + if (ret) + return ret; + target += AD4030_TCNVH_NS; + } while (cnv_wf.duty_length_ns < AD4030_TCNVH_NS); + + /* + * The CNV waveform period (period_length_ns) might get rounded down by + * pwm_round_waveform_might_sleep(). Check the resultant PWM period + * is not smaller than the minimum data conversion cycle time. + */ + if (!in_range(cnv_wf.period_length_ns, AD4030_TCYC_NS, INT_MAX)) + return -EINVAL; + + offload_period_ns = DIV_ROUND_CLOSEST(NSEC_PER_SEC, freq_hz); + + config->periodic.frequency_hz = DIV_ROUND_UP(HZ_PER_GHZ, offload_period_ns); + + /* + * The hardware does the capture on zone 2 (when SPI trigger PWM + * is used). This means that the SPI trigger signal should happen at + * tsync + tquiet_con_delay being tsync the conversion signal period + * and tquiet_con_delay 9.8ns. Hence set the PWM phase accordingly. + * + * The PWM waveform API only supports nanosecond resolution right now, + * so round this setting to the closest available value. + */ + offload_offset_ns = AD4030_TQUIET_CNV_DELAY_NS; + do { + config->periodic.offset_ns = offload_offset_ns; + ret = spi_offload_trigger_validate(st->offload_trigger, config); + if (ret) + return ret; + offload_offset_ns += AD4030_TQUIET_CNV_DELAY_NS; + } while (config->periodic.offset_ns < AD4030_TQUIET_CNV_DELAY_NS); + + st->cnv_wf = cnv_wf; + + return 0; +} + +static int ad4030_set_sampling_freq(struct iio_dev *indio_dev, int freq_hz) +{ + struct ad4030_state *st = iio_priv(indio_dev); + + if (freq_hz == 0) + return -EINVAL; + + if (!in_range(freq_hz, 0, st->chip->max_sample_rate_hz)) + return -ERANGE; + + return ad4030_update_conversion_rate(st, freq_hz, st->avg_log2); +} + +static int ad4030_set_chan_calibscale(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int gain_int, + int gain_frac) +{ + struct ad4030_state *st = iio_priv(indio_dev); + u64 gain; + + if (gain_int < 0 || gain_frac < 0) + return -EINVAL; + + gain = mul_u32_u32(gain_int, MICRO) + gain_frac; + + if (gain > AD4030_REG_GAIN_MAX_GAIN) + return -EINVAL; + + put_unaligned_be16(DIV_ROUND_CLOSEST_ULL(gain * AD4030_GAIN_MIDLE_POINT, + MICRO), + st->tx_data); + + return regmap_bulk_write(st->regmap, + AD4030_REG_GAIN_CHAN(chan->address), + st->tx_data, AD4030_REG_GAIN_BYTES_NB); +} + +static int ad4030_set_chan_calibbias(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int offset) +{ + struct ad4030_state *st = iio_priv(indio_dev); + + if (offset < st->offset_avail[0] || offset > st->offset_avail[2]) + return -EINVAL; + + st->tx_data[2] = 0; + + switch (st->chip->precision_bits) { + case 16: + put_unaligned_be16(offset, st->tx_data); + break; + + case 24: + put_unaligned_be24(offset, st->tx_data); + break; + + default: + return -EINVAL; + } + + return regmap_bulk_write(st->regmap, + AD4030_REG_OFFSET_CHAN(chan->address), + st->tx_data, AD4030_REG_OFFSET_BYTES_NB); +} + +static int ad4030_set_avg_frame_len(struct iio_dev *dev, int avg_val) +{ + struct ad4030_state *st = iio_priv(dev); + unsigned int avg_log2 = ilog2(avg_val); + unsigned int last_avg_idx = ARRAY_SIZE(ad4030_average_modes) - 1; + int freq_hz; + int ret; + + if (avg_val < 0 || avg_val > ad4030_average_modes[last_avg_idx]) + return -EINVAL; + + if (st->offload_trigger) { + /* + * The sample averaging and sampling frequency configurations + * are mutually dependent on each other. That's because the + * effective data sample rate is fCNV / 2^N, where N is the + * number of samples being averaged. + * + * When SPI offload is supported and we have control over the + * sample rate, the conversion start signal (CNV) and the SPI + * offload trigger frequencies must be re-evaluated so data is + * fetched only after 'avg_val' conversions. + */ + ad4030_get_sampling_freq(st, &freq_hz); + ret = ad4030_update_conversion_rate(st, freq_hz, avg_log2); + if (ret) + return ret; + } + + ret = regmap_write(st->regmap, AD4030_REG_AVG, + AD4030_REG_AVG_MASK_AVG_SYNC | + FIELD_PREP(AD4030_REG_AVG_MASK_AVG_VAL, avg_log2)); + if (ret) + return ret; + + st->avg_log2 = avg_log2; + + return 0; +} + +static bool ad4030_is_common_byte_asked(struct ad4030_state *st, + unsigned int mask) +{ + return mask & (st->chip->num_voltage_inputs == 1 ? + AD4030_SINGLE_COMMON_BYTE_CHANNELS_MASK : + AD4030_DUAL_COMMON_BYTE_CHANNELS_MASK); +} + +static int ad4030_set_mode(struct iio_dev *indio_dev, unsigned long mask) +{ + struct ad4030_state *st = iio_priv(indio_dev); + + if (st->avg_log2 > 0) { + st->mode = AD4030_OUT_DATA_MD_30_AVERAGED_DIFF; + } else if (ad4030_is_common_byte_asked(st, mask)) { + switch (st->chip->precision_bits) { + case 16: + st->mode = AD4030_OUT_DATA_MD_16_DIFF_8_COM; + break; + + case 24: + st->mode = AD4030_OUT_DATA_MD_24_DIFF_8_COM; + break; + + default: + return -EINVAL; + } + } else { + st->mode = AD4030_OUT_DATA_MD_DIFF; + } + + return regmap_update_bits(st->regmap, AD4030_REG_MODES, + AD4030_REG_MODES_MASK_OUT_DATA_MODE, + st->mode); +} + +/* + * Descramble 2 32bits numbers out of a 64bits. The bits are interleaved: + * 1 bit for first number, 1 bit for the second, and so on... + */ +static void ad4030_extract_interleaved(u8 *src, u32 *ch0, u32 *ch1) +{ + u8 h0, h1, l0, l1; + u32 out0, out1; + u8 *out0_raw = (u8 *)&out0; + u8 *out1_raw = (u8 *)&out1; + + for (int i = 0; i < 4; i++) { + h0 = src[i * 2]; + l1 = src[i * 2 + 1]; + h1 = h0 << 1; + l0 = l1 >> 1; + + h0 &= 0xAA; + l0 &= 0x55; + h1 &= 0xAA; + l1 &= 0x55; + + h0 = (h0 | h0 << 001) & 0xCC; + h1 = (h1 | h1 << 001) & 0xCC; + l0 = (l0 | l0 >> 001) & 0x33; + l1 = (l1 | l1 >> 001) & 0x33; + h0 = (h0 | h0 << 002) & 0xF0; + h1 = (h1 | h1 << 002) & 0xF0; + l0 = (l0 | l0 >> 002) & 0x0F; + l1 = (l1 | l1 >> 002) & 0x0F; + + out0_raw[i] = h0 | l0; + out1_raw[i] = h1 | l1; + } + + *ch0 = out0; + *ch1 = out1; +} + +static int ad4030_conversion(struct iio_dev *indio_dev) +{ + struct ad4030_state *st = iio_priv(indio_dev); + const struct iio_scan_type *scan_type; + unsigned char diff_realbytes, diff_storagebytes; + unsigned int bytes_to_read; + unsigned long cnv_nb = BIT(st->avg_log2); + unsigned int i; + int ret; + + scan_type = iio_get_current_scan_type(indio_dev, st->chip->channels); + if (IS_ERR(scan_type)) + return PTR_ERR(scan_type); + + diff_realbytes = BITS_TO_BYTES(scan_type->realbits); + diff_storagebytes = BITS_TO_BYTES(scan_type->storagebits); + + /* Number of bytes for one differential channel */ + bytes_to_read = diff_realbytes; + /* Add one byte if we are using a differential + common byte mode */ + bytes_to_read += (st->mode == AD4030_OUT_DATA_MD_24_DIFF_8_COM || + st->mode == AD4030_OUT_DATA_MD_16_DIFF_8_COM) ? 1 : 0; + /* Multiply by the number of hardware channels */ + bytes_to_read *= st->chip->num_voltage_inputs; + + for (i = 0; i < cnv_nb; i++) { + gpiod_set_value_cansleep(st->cnv_gpio, 1); + ndelay(AD4030_TCNVH_NS); + gpiod_set_value_cansleep(st->cnv_gpio, 0); + ndelay(st->chip->tcyc_ns); + } + + ret = spi_read(st->spi, st->rx_data.raw, bytes_to_read); + if (ret) + return ret; + + if (st->chip->num_voltage_inputs == 2) + ad4030_extract_interleaved(st->rx_data.raw, + &st->rx_data.dual.diff[0], + &st->rx_data.dual.diff[1]); + + /* + * If no common mode voltage channel is enabled, we can use the raw + * data as is. Otherwise, we need to rearrange the data a bit to match + * the natural alignment of the IIO buffer. + */ + + if (st->mode != AD4030_OUT_DATA_MD_16_DIFF_8_COM && + st->mode != AD4030_OUT_DATA_MD_24_DIFF_8_COM) + return 0; + + if (st->chip->num_voltage_inputs == 1) { + st->rx_data.single.common = st->rx_data.raw[diff_realbytes]; + return 0; + } + + for (i = 0; i < st->chip->num_voltage_inputs; i++) + st->rx_data.dual.common[i] = + st->rx_data.raw[diff_storagebytes * i + diff_realbytes]; + + return 0; +} + +static int ad4030_single_conversion(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, int *val) +{ + struct ad4030_state *st = iio_priv(indio_dev); + int ret; + + ret = ad4030_set_mode(indio_dev, BIT(chan->scan_index)); + if (ret) + return ret; + + ret = ad4030_conversion(indio_dev); + if (ret) + return ret; + + if (chan->differential) + if (st->chip->num_voltage_inputs == 1) + *val = st->rx_data.single.diff; + else + *val = st->rx_data.dual.diff[chan->address]; + else + if (st->chip->num_voltage_inputs == 1) + *val = st->rx_data.single.common; + else + *val = st->rx_data.dual.common[chan->address]; + + return IIO_VAL_INT; +} + +static irqreturn_t ad4030_trigger_handler(int irq, void *p) +{ + struct iio_poll_func *pf = p; + struct iio_dev *indio_dev = pf->indio_dev; + struct ad4030_state *st = iio_priv(indio_dev); + int ret; + + ret = ad4030_conversion(indio_dev); + if (ret) + goto out; + + iio_push_to_buffers_with_ts(indio_dev, &st->rx_data, sizeof(st->rx_data), + pf->timestamp); + +out: + iio_trigger_notify_done(indio_dev->trig); + + return IRQ_HANDLED; +} + +static const int ad4030_gain_avail[3][2] = { + { 0, 0 }, + { 0, 30518 }, + { 1, 999969482 }, +}; + +static int ad4030_read_avail(struct iio_dev *indio_dev, + struct iio_chan_spec const *channel, + const int **vals, int *type, + int *length, long mask) +{ + struct ad4030_state *st = iio_priv(indio_dev); + + switch (mask) { + case IIO_CHAN_INFO_CALIBBIAS: + *vals = st->offset_avail; + *type = IIO_VAL_INT; + return IIO_AVAIL_RANGE; + + case IIO_CHAN_INFO_CALIBSCALE: + *vals = (void *)ad4030_gain_avail; + *type = IIO_VAL_INT_PLUS_NANO; + return IIO_AVAIL_RANGE; + + case IIO_CHAN_INFO_OVERSAMPLING_RATIO: + *vals = ad4030_average_modes; + *type = IIO_VAL_INT; + *length = ARRAY_SIZE(ad4030_average_modes); + return IIO_AVAIL_LIST; + + case IIO_CHAN_INFO_SCALE: + if (st->scale_avail_size == 1) + *vals = (int *)st->scale_avail[st->pga_index]; + else + *vals = (int *)st->scale_avail; + *length = st->scale_avail_size * 2; /* print int and nano part */ + *type = IIO_VAL_INT_PLUS_NANO; + return IIO_AVAIL_LIST; + + default: + return -EINVAL; + } +} + +static int ad4030_read_raw_dispatch(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int *val, + int *val2, long info) +{ + struct ad4030_state *st = iio_priv(indio_dev); + + switch (info) { + case IIO_CHAN_INFO_RAW: + return ad4030_single_conversion(indio_dev, chan, val); + + case IIO_CHAN_INFO_CALIBSCALE: + return ad4030_get_chan_calibscale(indio_dev, chan, val, val2); + + case IIO_CHAN_INFO_CALIBBIAS: + return ad4030_get_chan_calibbias(indio_dev, chan, val); + + case IIO_CHAN_INFO_OVERSAMPLING_RATIO: + *val = BIT(st->avg_log2); + return IIO_VAL_INT; + + case IIO_CHAN_INFO_SAMP_FREQ: + ad4030_get_sampling_freq(st, val); + return IIO_VAL_INT; + + default: + return -EINVAL; + } +} + +static int ad4030_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int *val, + int *val2, long info) +{ + int ret; + + if (info == IIO_CHAN_INFO_SCALE) + return ad4030_get_chan_scale(indio_dev, chan, val, val2); + + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; + + ret = ad4030_read_raw_dispatch(indio_dev, chan, val, val2, info); + + iio_device_release_direct(indio_dev); + + return ret; +} + +static int ad4030_write_raw_dispatch(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int val, + int val2, long info) +{ + switch (info) { + case IIO_CHAN_INFO_CALIBSCALE: + return ad4030_set_chan_calibscale(indio_dev, chan, val, val2); + + case IIO_CHAN_INFO_CALIBBIAS: + if (val2 != 0) + return -EINVAL; + return ad4030_set_chan_calibbias(indio_dev, chan, val); + + case IIO_CHAN_INFO_OVERSAMPLING_RATIO: + return ad4030_set_avg_frame_len(indio_dev, val); + + case IIO_CHAN_INFO_SAMP_FREQ: + return ad4030_set_sampling_freq(indio_dev, val); + + case IIO_CHAN_INFO_SCALE: + return ad4030_set_pga(indio_dev, val, val2); + + default: + return -EINVAL; + } +} + +static int ad4030_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int val, + int val2, long info) +{ + int ret; + + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; + + ret = ad4030_write_raw_dispatch(indio_dev, chan, val, val2, info); + + iio_device_release_direct(indio_dev); + + return ret; +} + +static int ad4030_write_raw_get_fmt(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, long mask) +{ + switch (mask) { + case IIO_CHAN_INFO_SCALE: + return IIO_VAL_INT_PLUS_NANO; + default: + return IIO_VAL_INT_PLUS_MICRO; + } +} + +static int ad4030_reg_access(struct iio_dev *indio_dev, unsigned int reg, + unsigned int writeval, unsigned int *readval) +{ + const struct ad4030_state *st = iio_priv(indio_dev); + int ret; + + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; + + if (readval) + ret = regmap_read(st->regmap, reg, readval); + else + ret = regmap_write(st->regmap, reg, writeval); + + iio_device_release_direct(indio_dev); + + return ret; +} + +static int ad4030_read_label(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + char *label) +{ + if (chan->differential) + return sysfs_emit(label, "differential%lu\n", chan->address); + return sysfs_emit(label, "common-mode%lu\n", chan->address); +} + +static int ad4030_get_current_scan_type(const struct iio_dev *indio_dev, + const struct iio_chan_spec *chan) +{ + struct ad4030_state *st = iio_priv(indio_dev); + + return st->avg_log2 ? AD4030_SCAN_TYPE_AVG : AD4030_SCAN_TYPE_NORMAL; +} + +static int ad4030_update_scan_mode(struct iio_dev *indio_dev, + const unsigned long *scan_mask) +{ + return ad4030_set_mode(indio_dev, *scan_mask); +} + +static const struct iio_info ad4030_iio_info = { + .read_avail = ad4030_read_avail, + .read_raw = ad4030_read_raw, + .write_raw = ad4030_write_raw, + .write_raw_get_fmt = &ad4030_write_raw_get_fmt, + .debugfs_reg_access = ad4030_reg_access, + .read_label = ad4030_read_label, + .get_current_scan_type = ad4030_get_current_scan_type, + .update_scan_mode = ad4030_update_scan_mode, +}; + +static bool ad4030_validate_scan_mask(struct iio_dev *indio_dev, + const unsigned long *scan_mask) +{ + struct ad4030_state *st = iio_priv(indio_dev); + + /* Asking for both common channels and averaging */ + if (st->avg_log2 && ad4030_is_common_byte_asked(st, *scan_mask)) + return false; + + return true; +} + +static const struct iio_buffer_setup_ops ad4030_buffer_setup_ops = { + .validate_scan_mask = ad4030_validate_scan_mask, +}; + +static void ad4030_prepare_offload_msg(struct iio_dev *indio_dev) +{ + struct ad4030_state *st = iio_priv(indio_dev); + u8 offload_bpw; + + if (st->mode == AD4030_OUT_DATA_MD_30_AVERAGED_DIFF) + offload_bpw = 32; + else + offload_bpw = st->chip->precision_bits; + + st->offload_xfer.bits_per_word = offload_bpw; + st->offload_xfer.len = spi_bpw_to_bytes(offload_bpw); + st->offload_xfer.offload_flags = SPI_OFFLOAD_XFER_RX_STREAM; + spi_message_init_with_transfers(&st->offload_msg, &st->offload_xfer, 1); +} + +static int ad4030_offload_buffer_postenable(struct iio_dev *indio_dev) +{ + struct ad4030_state *st = iio_priv(indio_dev); + unsigned int reg_modes; + int ret; + + /* + * When data from 2 analog input channels is output through a single + * bus line (interleaved mode (LANE_MD == 0b11)) and gets pushed through + * DMA, extra hardware is required to do the de-interleaving. While we + * don't support such hardware configurations, disallow interleaved mode + * when using SPI offload. + */ + ret = regmap_read(st->regmap, AD4030_REG_MODES, ®_modes); + if (ret) + return ret; + + if (st->chip->num_voltage_inputs > 1 && + FIELD_GET(AD4030_REG_MODES_MASK_LANE_MODE, reg_modes) == AD4030_LANE_MD_INTERLEAVED) + return -EINVAL; + + ad4030_prepare_offload_msg(indio_dev); + st->offload_msg.offload = st->offload; + ret = spi_optimize_message(st->spi, &st->offload_msg); + if (ret) + return ret; + + ret = pwm_set_waveform_might_sleep(st->cnv_trigger, &st->cnv_wf, false); + if (ret) + goto out_unoptimize; + + ret = spi_offload_trigger_enable(st->offload, st->offload_trigger, + &st->offload_trigger_config); + if (ret) + goto out_pwm_disable; + + return 0; + +out_pwm_disable: + pwm_disable(st->cnv_trigger); +out_unoptimize: + spi_unoptimize_message(&st->offload_msg); + + return ret; +} + +static int ad4030_offload_buffer_predisable(struct iio_dev *indio_dev) +{ + struct ad4030_state *st = iio_priv(indio_dev); + + spi_offload_trigger_disable(st->offload, st->offload_trigger); + + pwm_disable(st->cnv_trigger); + + spi_unoptimize_message(&st->offload_msg); + + return 0; +} + +static const struct iio_buffer_setup_ops ad4030_offload_buffer_setup_ops = { + .postenable = &ad4030_offload_buffer_postenable, + .predisable = &ad4030_offload_buffer_predisable, +}; + +static int ad4030_regulators_get(struct ad4030_state *st) +{ + struct device *dev = &st->spi->dev; + static const char * const ids[] = { "vdd-5v", "vdd-1v8" }; + int ret; + + ret = devm_regulator_bulk_get_enable(dev, ARRAY_SIZE(ids), ids); + if (ret) + return dev_err_probe(dev, ret, "Failed to enable regulators\n"); + + st->vio_uv = devm_regulator_get_enable_read_voltage(dev, "vio"); + if (st->vio_uv < 0) + return dev_err_probe(dev, st->vio_uv, + "Failed to enable and read vio voltage\n"); + + st->vref_uv = devm_regulator_get_enable_read_voltage(dev, "ref"); + if (st->vref_uv < 0) { + if (st->vref_uv != -ENODEV) + return dev_err_probe(dev, st->vref_uv, + "Failed to read ref voltage\n"); + + /* if not using optional REF, the REFIN must be used */ + st->vref_uv = devm_regulator_get_enable_read_voltage(dev, + "refin"); + if (st->vref_uv < 0) + return dev_err_probe(dev, st->vref_uv, + "Failed to read refin voltage\n"); + } + + return 0; +} + +static int ad4030_reset(struct ad4030_state *st) +{ + struct device *dev = &st->spi->dev; + struct gpio_desc *reset; + + reset = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH); + if (IS_ERR(reset)) + return dev_err_probe(dev, PTR_ERR(reset), + "Failed to get reset GPIO\n"); + + if (reset) { + ndelay(50); + gpiod_set_value_cansleep(reset, 0); + return 0; + } + + return regmap_write(st->regmap, AD4030_REG_INTERFACE_CONFIG_A, + AD4030_REG_INTERFACE_CONFIG_A_SW_RESET); +} + +static int ad4030_detect_chip_info(const struct ad4030_state *st) +{ + unsigned int grade; + int ret; + + ret = regmap_read(st->regmap, AD4030_REG_CHIP_GRADE, &grade); + if (ret) + return ret; + + grade = FIELD_GET(AD4030_REG_CHIP_GRADE_MASK_CHIP_GRADE, grade); + if (grade != st->chip->grade) + dev_warn(&st->spi->dev, "Unknown grade(0x%x) for %s\n", grade, + st->chip->name); + + return 0; +} + +static int ad4030_pwm_get(struct ad4030_state *st) +{ + struct device *dev = &st->spi->dev; + + st->cnv_trigger = devm_pwm_get(dev, NULL); + if (IS_ERR(st->cnv_trigger)) + return dev_err_probe(dev, PTR_ERR(st->cnv_trigger), + "Failed to get CNV PWM\n"); + + /* + * Preemptively disable the PWM, since we only want to enable it with + * the buffer. + */ + pwm_disable(st->cnv_trigger); + + return 0; +} + +static int ad4030_config(struct ad4030_state *st) +{ + int ret; + u8 reg_modes; + + st->offset_avail[0] = (int)BIT(st->chip->precision_bits - 1) * -1; + st->offset_avail[1] = 1; + st->offset_avail[2] = BIT(st->chip->precision_bits - 1) - 1; + + if (st->chip->num_voltage_inputs > 1) + reg_modes = FIELD_PREP(AD4030_REG_MODES_MASK_LANE_MODE, + AD4030_LANE_MD_INTERLEAVED); + else + reg_modes = FIELD_PREP(AD4030_REG_MODES_MASK_LANE_MODE, + AD4030_LANE_MD_1_PER_CH); + + ret = regmap_write(st->regmap, AD4030_REG_MODES, reg_modes); + if (ret) + return ret; + + if (st->vio_uv < AD4030_VIO_THRESHOLD_UV) + return regmap_write(st->regmap, AD4030_REG_IO, + AD4030_REG_IO_MASK_IO2X); + + return 0; +} + +static int ad4030_spi_offload_setup(struct iio_dev *indio_dev, + struct ad4030_state *st) +{ + struct device *dev = &st->spi->dev; + struct dma_chan *rx_dma; + + indio_dev->setup_ops = &ad4030_offload_buffer_setup_ops; + + st->offload_trigger = devm_spi_offload_trigger_get(dev, st->offload, + SPI_OFFLOAD_TRIGGER_PERIODIC); + if (IS_ERR(st->offload_trigger)) + return dev_err_probe(dev, PTR_ERR(st->offload_trigger), + "failed to get offload trigger\n"); + + st->offload_trigger_config.type = SPI_OFFLOAD_TRIGGER_PERIODIC; + + rx_dma = devm_spi_offload_rx_stream_request_dma_chan(dev, st->offload); + if (IS_ERR(rx_dma)) + return dev_err_probe(dev, PTR_ERR(rx_dma), + "failed to get offload RX DMA\n"); + + return devm_iio_dmaengine_buffer_setup_with_handle(dev, indio_dev, rx_dma, + IIO_BUFFER_DIRECTION_IN); +} + +static int ad4030_setup_pga(struct device *dev, struct iio_dev *indio_dev, + struct ad4030_state *st) +{ + /* Setup GPIOs for PGA control */ + st->pga_gpios = devm_gpiod_get_array(dev, "pga", GPIOD_OUT_LOW); + if (IS_ERR(st->pga_gpios)) + return dev_err_probe(dev, PTR_ERR(st->pga_gpios), + "Failed to get PGA gpios.\n"); + + if (st->pga_gpios->ndescs != ADAQ4616_PGA_PINS) + return dev_err_probe(dev, -EINVAL, + "Expected %d GPIOs for PGA control.\n", + ADAQ4616_PGA_PINS); + + st->scale_avail_size = ARRAY_SIZE(adaq4216_hw_gains_vpv); + st->pga_index = 0; + + return 0; +} + +static int ad4030_probe(struct spi_device *spi) +{ + struct device *dev = &spi->dev; + struct iio_dev *indio_dev; + struct ad4030_state *st; + int ret; + + indio_dev = devm_iio_device_alloc(dev, sizeof(*st)); + if (!indio_dev) + return -ENOMEM; + + st = iio_priv(indio_dev); + st->spi = spi; + + st->regmap = devm_regmap_init(dev, &ad4030_regmap_bus, st, + &ad4030_regmap_config); + if (IS_ERR(st->regmap)) + return dev_err_probe(dev, PTR_ERR(st->regmap), + "Failed to initialize regmap\n"); + + st->chip = spi_get_device_match_data(spi); + if (!st->chip) + return -EINVAL; + + ret = ad4030_regulators_get(st); + if (ret) + return ret; + + /* + * From datasheet: "Perform a reset no sooner than 3ms after the power + * supplies are valid and stable" + */ + fsleep(3000); + + ret = ad4030_reset(st); + if (ret) + return ret; + + ret = ad4030_detect_chip_info(st); + if (ret) + return ret; + + if (st->chip->has_pga) { + ret = ad4030_setup_pga(dev, indio_dev, st); + if (ret) + return ret; + + ad4030_fill_scale_avail(st); + } + + ret = ad4030_config(st); + if (ret) + return ret; + + st->cnv_gpio = devm_gpiod_get(dev, "cnv", GPIOD_OUT_LOW); + if (IS_ERR(st->cnv_gpio)) + return dev_err_probe(dev, PTR_ERR(st->cnv_gpio), + "Failed to get cnv gpio\n"); + + indio_dev->name = st->chip->name; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->info = &ad4030_iio_info; + indio_dev->available_scan_masks = st->chip->available_masks; + + st->offload = devm_spi_offload_get(dev, spi, &ad4030_offload_config); + ret = PTR_ERR_OR_ZERO(st->offload); + /* Fall back to low speed usage when no SPI offload is available. */ + if (ret == -ENODEV) { + /* + * One hardware channel is split in two software channels when + * using common byte mode. Add one more channel for the timestamp. + */ + indio_dev->num_channels = 2 * st->chip->num_voltage_inputs + 1; + indio_dev->channels = st->chip->channels; + + ret = devm_iio_triggered_buffer_setup(dev, indio_dev, + iio_pollfunc_store_time, + ad4030_trigger_handler, + &ad4030_buffer_setup_ops); + if (ret) + return dev_err_probe(dev, ret, + "Failed to setup triggered buffer\n"); + } else if (ret) { + return dev_err_probe(dev, ret, "failed to get offload\n"); + } else { + /* + * Offloaded SPI transfers can't support software timestamp so + * no additional timestamp channel is added. + */ + indio_dev->num_channels = st->chip->num_voltage_inputs; + indio_dev->channels = st->chip->offload_channels; + ret = ad4030_spi_offload_setup(indio_dev, st); + if (ret) + return dev_err_probe(dev, ret, + "Failed to setup SPI offload\n"); + + ret = ad4030_pwm_get(st); + if (ret) + return dev_err_probe(dev, ret, "Failed to get PWM\n"); + + /* + * Start with a slower sampling rate so there is some room for + * adjusting the sample averaging and the sampling frequency + * without hitting the maximum conversion rate. + */ + ret = ad4030_update_conversion_rate(st, st->chip->max_sample_rate_hz >> 4, + st->avg_log2); + if (ret) + return dev_err_probe(dev, ret, + "Failed to set offload samp freq\n"); + } + + return devm_iio_device_register(dev, indio_dev); +} + +static const unsigned long ad4030_channel_masks[] = { + /* Differential only */ + BIT(0), + /* Differential and common-mode voltage */ + GENMASK(1, 0), + 0, +}; + +static const unsigned long ad4630_channel_masks[] = { + /* Differential only */ + BIT(1) | BIT(0), + /* Differential with common byte */ + GENMASK(3, 0), + 0, +}; + +static const struct iio_scan_type ad4030_24_scan_types[] = { + [AD4030_SCAN_TYPE_NORMAL] = { + .sign = 's', + .storagebits = 32, + .realbits = 24, + .shift = 8, + .endianness = IIO_BE, + }, + [AD4030_SCAN_TYPE_AVG] = { + .sign = 's', + .storagebits = 32, + .realbits = 30, + .shift = 2, + .endianness = IIO_BE, + }, +}; + +static const struct iio_scan_type ad4030_24_offload_scan_types[] = { + [AD4030_SCAN_TYPE_NORMAL] = { + .sign = 's', + .realbits = 24, + .storagebits = 32, + .shift = 0, + .endianness = IIO_CPU, + }, + [AD4030_SCAN_TYPE_AVG] = { + .sign = 's', + .realbits = 30, + .storagebits = 32, + .shift = 2, + .endianness = IIO_CPU, + }, +}; + +static const struct iio_scan_type ad4030_16_scan_types[] = { + [AD4030_SCAN_TYPE_NORMAL] = { + .sign = 's', + .realbits = 16, + .storagebits = 32, + .shift = 16, + .endianness = IIO_BE, + }, + [AD4030_SCAN_TYPE_AVG] = { + .sign = 's', + .storagebits = 32, + .realbits = 30, + .shift = 2, + .endianness = IIO_BE, + } +}; + +static const struct iio_scan_type ad4030_16_offload_scan_types[] = { + [AD4030_SCAN_TYPE_NORMAL] = { + .sign = 's', + .realbits = 16, + .storagebits = 32, + .shift = 0, + .endianness = IIO_CPU, + }, + [AD4030_SCAN_TYPE_AVG] = { + .sign = 's', + .realbits = 30, + .storagebits = 32, + .shift = 2, + .endianness = IIO_CPU, + }, +}; + +static const struct ad4030_chip_info ad4030_24_chip_info = { + .name = "ad4030-24", + .available_masks = ad4030_channel_masks, + .channels = { + AD4030_CHAN_DIFF(0, ad4030_24_scan_types), + AD4030_CHAN_CMO(1, 0), + IIO_CHAN_SOFT_TIMESTAMP(2), + }, + .offload_channels = { + AD4030_OFFLOAD_CHAN_DIFF(0, ad4030_24_offload_scan_types), + }, + .grade = AD4030_REG_CHIP_GRADE_AD4030_24_GRADE, + .precision_bits = 24, + .num_voltage_inputs = 1, + .tcyc_ns = AD4030_TCYC_ADJUSTED_NS, + .max_sample_rate_hz = 2 * HZ_PER_MHZ, +}; + +static const struct ad4030_chip_info ad4630_16_chip_info = { + .name = "ad4630-16", + .available_masks = ad4630_channel_masks, + .channels = { + AD4030_CHAN_DIFF(0, ad4030_16_scan_types), + AD4030_CHAN_DIFF(1, ad4030_16_scan_types), + AD4030_CHAN_CMO(2, 0), + AD4030_CHAN_CMO(3, 1), + IIO_CHAN_SOFT_TIMESTAMP(4), + }, + .offload_channels = { + AD4030_OFFLOAD_CHAN_DIFF(0, ad4030_16_offload_scan_types), + AD4030_OFFLOAD_CHAN_DIFF(1, ad4030_16_offload_scan_types), + }, + .grade = AD4030_REG_CHIP_GRADE_AD4630_16_GRADE, + .precision_bits = 16, + .num_voltage_inputs = 2, + .tcyc_ns = AD4030_TCYC_ADJUSTED_NS, + .max_sample_rate_hz = 2 * HZ_PER_MHZ, +}; + +static const struct ad4030_chip_info ad4630_24_chip_info = { + .name = "ad4630-24", + .available_masks = ad4630_channel_masks, + .channels = { + AD4030_CHAN_DIFF(0, ad4030_24_scan_types), + AD4030_CHAN_DIFF(1, ad4030_24_scan_types), + AD4030_CHAN_CMO(2, 0), + AD4030_CHAN_CMO(3, 1), + IIO_CHAN_SOFT_TIMESTAMP(4), + }, + .offload_channels = { + AD4030_OFFLOAD_CHAN_DIFF(0, ad4030_24_offload_scan_types), + AD4030_OFFLOAD_CHAN_DIFF(1, ad4030_24_offload_scan_types), + }, + .grade = AD4030_REG_CHIP_GRADE_AD4630_24_GRADE, + .precision_bits = 24, + .num_voltage_inputs = 2, + .tcyc_ns = AD4030_TCYC_ADJUSTED_NS, + .max_sample_rate_hz = 2 * HZ_PER_MHZ, +}; + +static const struct ad4030_chip_info ad4632_16_chip_info = { + .name = "ad4632-16", + .available_masks = ad4630_channel_masks, + .channels = { + AD4030_CHAN_DIFF(0, ad4030_16_scan_types), + AD4030_CHAN_DIFF(1, ad4030_16_scan_types), + AD4030_CHAN_CMO(2, 0), + AD4030_CHAN_CMO(3, 1), + IIO_CHAN_SOFT_TIMESTAMP(4), + }, + .offload_channels = { + AD4030_OFFLOAD_CHAN_DIFF(0, ad4030_16_offload_scan_types), + AD4030_OFFLOAD_CHAN_DIFF(1, ad4030_16_offload_scan_types), + }, + .grade = AD4030_REG_CHIP_GRADE_AD4632_16_GRADE, + .precision_bits = 16, + .num_voltage_inputs = 2, + .tcyc_ns = AD4632_TCYC_ADJUSTED_NS, + .max_sample_rate_hz = 500 * HZ_PER_KHZ, +}; + +static const struct ad4030_chip_info ad4632_24_chip_info = { + .name = "ad4632-24", + .available_masks = ad4630_channel_masks, + .channels = { + AD4030_CHAN_DIFF(0, ad4030_24_scan_types), + AD4030_CHAN_DIFF(1, ad4030_24_scan_types), + AD4030_CHAN_CMO(2, 0), + AD4030_CHAN_CMO(3, 1), + IIO_CHAN_SOFT_TIMESTAMP(4), + }, + .offload_channels = { + AD4030_OFFLOAD_CHAN_DIFF(0, ad4030_24_offload_scan_types), + AD4030_OFFLOAD_CHAN_DIFF(1, ad4030_24_offload_scan_types), + }, + .grade = AD4030_REG_CHIP_GRADE_AD4632_24_GRADE, + .precision_bits = 24, + .num_voltage_inputs = 2, + .tcyc_ns = AD4632_TCYC_ADJUSTED_NS, + .max_sample_rate_hz = 500 * HZ_PER_KHZ, +}; + +static const struct ad4030_chip_info adaq4216_chip_info = { + .name = "adaq4216", + .available_masks = ad4030_channel_masks, + .channels = { + ADAQ4216_CHAN_DIFF(0, ad4030_16_scan_types), + AD4030_CHAN_CMO(1, 0), + IIO_CHAN_SOFT_TIMESTAMP(2), + }, + .offload_channels = { + ADAQ4216_OFFLOAD_CHAN_DIFF(0, ad4030_16_offload_scan_types), + }, + .grade = AD4030_REG_CHIP_GRADE_ADAQ4216_GRADE, + .precision_bits = 16, + .has_pga = true, + .num_voltage_inputs = 1, + .tcyc_ns = AD4030_TCYC_ADJUSTED_NS, + .max_sample_rate_hz = 2 * HZ_PER_MHZ, +}; + +static const struct ad4030_chip_info adaq4224_chip_info = { + .name = "adaq4224", + .available_masks = ad4030_channel_masks, + .channels = { + ADAQ4216_CHAN_DIFF(0, ad4030_24_scan_types), + AD4030_CHAN_CMO(1, 0), + IIO_CHAN_SOFT_TIMESTAMP(2), + }, + .offload_channels = { + ADAQ4216_OFFLOAD_CHAN_DIFF(0, ad4030_24_offload_scan_types), + }, + .grade = AD4030_REG_CHIP_GRADE_ADAQ4224_GRADE, + .precision_bits = 24, + .has_pga = true, + .num_voltage_inputs = 1, + .tcyc_ns = AD4030_TCYC_ADJUSTED_NS, + .max_sample_rate_hz = 2 * HZ_PER_MHZ, +}; + +static const struct spi_device_id ad4030_id_table[] = { + { .name = "ad4030-24", .driver_data = (kernel_ulong_t)&ad4030_24_chip_info }, + { .name = "ad4630-16", .driver_data = (kernel_ulong_t)&ad4630_16_chip_info }, + { .name = "ad4630-24", .driver_data = (kernel_ulong_t)&ad4630_24_chip_info }, + { .name = "ad4632-16", .driver_data = (kernel_ulong_t)&ad4632_16_chip_info }, + { .name = "ad4632-24", .driver_data = (kernel_ulong_t)&ad4632_24_chip_info }, + { .name = "adaq4216", .driver_data = (kernel_ulong_t)&adaq4216_chip_info }, + { .name = "adaq4224", .driver_data = (kernel_ulong_t)&adaq4224_chip_info }, + { } +}; +MODULE_DEVICE_TABLE(spi, ad4030_id_table); + +static const struct of_device_id ad4030_of_match[] = { + { .compatible = "adi,ad4030-24", .data = &ad4030_24_chip_info }, + { .compatible = "adi,ad4630-16", .data = &ad4630_16_chip_info }, + { .compatible = "adi,ad4630-24", .data = &ad4630_24_chip_info }, + { .compatible = "adi,ad4632-16", .data = &ad4632_16_chip_info }, + { .compatible = "adi,ad4632-24", .data = &ad4632_24_chip_info }, + { .compatible = "adi,adaq4216", .data = &adaq4216_chip_info }, + { .compatible = "adi,adaq4224", .data = &adaq4224_chip_info }, + { } +}; +MODULE_DEVICE_TABLE(of, ad4030_of_match); + +static struct spi_driver ad4030_driver = { + .driver = { + .name = "ad4030", + .of_match_table = ad4030_of_match, + }, + .probe = ad4030_probe, + .id_table = ad4030_id_table, +}; +module_spi_driver(ad4030_driver); + +MODULE_AUTHOR("Esteban Blanc "); +MODULE_DESCRIPTION("Analog Devices AD4630 ADC family driver"); +MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS("IIO_DMAENGINE_BUFFER"); diff --git a/drivers/iio/adc/ad4062.c b/drivers/iio/adc/ad4062.c new file mode 100644 index 000000000..8e5984055 --- /dev/null +++ b/drivers/iio/adc/ad4062.c @@ -0,0 +1,1614 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Analog Devices AD4062 I3C ADC driver + * + * Copyright 2025 Analog Devices Inc. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define AD4062_REG_INTERFACE_CONFIG_A 0x00 +#define AD4062_REG_DEVICE_CONFIG 0x02 +#define AD4062_REG_DEVICE_CONFIG_POWER_MODE_MSK GENMASK(1, 0) +#define AD4062_REG_DEVICE_CONFIG_LOW_POWER_MODE 3 +#define AD4062_REG_PROD_ID_1 0x05 +#define AD4062_REG_DEVICE_GRADE 0x06 +#define AD4062_REG_SCRATCH_PAD 0x0A +#define AD4062_REG_VENDOR_H 0x0D +#define AD4062_REG_STREAM_MODE 0x0E +#define AD4062_REG_INTERFACE_STATUS 0x11 +#define AD4062_REG_MODE_SET 0x20 +#define AD4062_REG_MODE_SET_ENTER_ADC BIT(0) +#define AD4062_REG_ADC_MODES 0x21 +#define AD4062_REG_ADC_MODES_MODE_MSK GENMASK(1, 0) +#define AD4062_REG_ADC_CONFIG 0x22 +#define AD4062_REG_ADC_CONFIG_REF_EN_MSK BIT(5) +#define AD4062_REG_ADC_CONFIG_SCALE_EN_MSK BIT(4) +#define AD4062_REG_AVG_CONFIG 0x23 +#define AD4062_REG_GP_CONF 0x24 +#define AD4062_REG_GP_CONF_MODE_MSK_0 GENMASK(2, 0) +#define AD4062_REG_GP_CONF_MODE_MSK_1 GENMASK(6, 4) +#define AD4062_REG_INTR_CONF 0x25 +#define AD4062_REG_INTR_CONF_EN_MSK_0 GENMASK(1, 0) +#define AD4062_REG_INTR_CONF_EN_MSK_1 GENMASK(5, 4) +#define AD4062_REG_TIMER_CONFIG 0x27 +#define AD4062_REG_TIMER_CONFIG_FS_MASK GENMASK(7, 4) +#define AD4062_REG_MAX_LIMIT 0x29 +#define AD4062_REG_MIN_LIMIT 0x2B +#define AD4062_REG_MAX_HYST 0x2C +#define AD4062_REG_MIN_HYST 0x2D +#define AD4062_REG_MON_VAL 0x2F +#define AD4062_REG_ADC_IBI_EN 0x31 +#define AD4062_REG_ADC_IBI_EN_CONV_TRIGGER BIT(2) +#define AD4062_REG_ADC_IBI_EN_MAX BIT(1) +#define AD4062_REG_ADC_IBI_EN_MIN BIT(0) +#define AD4062_REG_FUSE_CRC 0x40 +#define AD4062_REG_DEVICE_STATUS 0x41 +#define AD4062_REG_DEVICE_STATUS_DEVICE_RESET BIT(6) +#define AD4062_REG_IBI_STATUS 0x48 +#define AD4062_REG_CONV_READ_LSB 0x50 +#define AD4062_REG_CONV_READ_16BITS 0x51 +#define AD4062_REG_CONV_READ_32BITS 0x53 +#define AD4062_REG_CONV_TRIGGER_16BITS 0x57 +#define AD4062_REG_CONV_TRIGGER_32BITS 0x59 +#define AD4062_REG_CONV_AUTO 0x61 +#define AD4062_MAX_REG AD4062_REG_CONV_AUTO + +#define AD4062_MON_VAL_MIDDLE_POINT 0x8000 + +#define AD4062_I3C_VENDOR 0x0177 +#define AD4062_SOFT_RESET 0x81 +#define AD4060_PROD_ID 0x7A +#define AD4062_PROD_ID 0x7C + +#define AD4062_GP_DISABLED 0x0 +#define AD4062_GP_INTR 0x1 +#define AD4062_GP_DRDY 0x2 +#define AD4062_GP_STATIC_LOW 0x5 +#define AD4062_GP_STATIC_HIGH 0x6 + +#define AD4062_LIMIT_BITS 12 + +#define AD4062_INTR_EN_NEITHER 0x0 +#define AD4062_INTR_EN_EITHER 0x3 + +#define AD4062_TCONV_NS 270 + +enum ad4062_operation_mode { + AD4062_SAMPLE_MODE = 0x0, + AD4062_BURST_AVERAGING_MODE = 0x1, + AD4062_MONITOR_MODE = 0x3, +}; + +struct ad4062_chip_info { + const struct iio_chan_spec channels[1]; + const char *name; + u16 prod_id; + u16 avg_max; +}; + +enum { + AD4062_SCAN_TYPE_SAMPLE, + AD4062_SCAN_TYPE_BURST_AVG, +}; + +static const struct iio_scan_type ad4062_scan_type_12_s[] = { + [AD4062_SCAN_TYPE_SAMPLE] = { + .sign = 's', + .realbits = 12, + .storagebits = 16, + .endianness = IIO_BE, + }, + [AD4062_SCAN_TYPE_BURST_AVG] = { + .sign = 's', + .realbits = 14, + .storagebits = 16, + .endianness = IIO_BE, + }, +}; + +static const struct iio_scan_type ad4062_scan_type_16_s[] = { + [AD4062_SCAN_TYPE_SAMPLE] = { + .sign = 's', + .realbits = 16, + .storagebits = 16, + .endianness = IIO_BE, + }, + [AD4062_SCAN_TYPE_BURST_AVG] = { + .sign = 's', + .realbits = 20, + .storagebits = 32, + .endianness = IIO_BE, + }, +}; + +static const unsigned int ad4062_conversion_freqs[] = { + 2000000, 1000000, 300000, 100000, /* 0 - 3 */ + 33300, 10000, 3000, 500, /* 4 - 7 */ + 333, 250, 200, 166, /* 8 - 11 */ + 140, 124, 111, /* 12 - 15 */ +}; + +struct ad4062_state { + const struct ad4062_chip_info *chip; + const struct ad4062_bus_ops *ops; + enum ad4062_operation_mode mode; + struct work_struct trig_conv; + struct completion completion; + struct iio_trigger *trigger; + struct iio_dev *indio_dev; + struct i3c_device *i3cdev; + struct regmap *regmap; + bool wait_event; + int vref_uV; + unsigned int samp_freqs[ARRAY_SIZE(ad4062_conversion_freqs)]; + bool gpo_irq[2]; + u16 sampling_frequency; + u16 events_frequency; + u8 oversamp_ratio; + u8 conv_sizeof; + u8 conv_addr; + union { + __be32 be32; + __be16 be16; + } buf __aligned(IIO_DMA_MINALIGN); +}; + +static const struct regmap_range ad4062_regmap_rd_ranges[] = { + regmap_reg_range(AD4062_REG_INTERFACE_CONFIG_A, AD4062_REG_DEVICE_GRADE), + regmap_reg_range(AD4062_REG_SCRATCH_PAD, AD4062_REG_INTERFACE_STATUS), + regmap_reg_range(AD4062_REG_MODE_SET, AD4062_REG_ADC_IBI_EN), + regmap_reg_range(AD4062_REG_FUSE_CRC, AD4062_REG_IBI_STATUS), + regmap_reg_range(AD4062_REG_CONV_READ_LSB, AD4062_REG_CONV_AUTO), +}; + +static const struct regmap_access_table ad4062_regmap_rd_table = { + .yes_ranges = ad4062_regmap_rd_ranges, + .n_yes_ranges = ARRAY_SIZE(ad4062_regmap_rd_ranges), +}; + +static const struct regmap_range ad4062_regmap_wr_ranges[] = { + regmap_reg_range(AD4062_REG_INTERFACE_CONFIG_A, AD4062_REG_DEVICE_CONFIG), + regmap_reg_range(AD4062_REG_SCRATCH_PAD, AD4062_REG_SCRATCH_PAD), + regmap_reg_range(AD4062_REG_STREAM_MODE, AD4062_REG_INTERFACE_STATUS), + regmap_reg_range(AD4062_REG_MODE_SET, AD4062_REG_ADC_IBI_EN), + regmap_reg_range(AD4062_REG_FUSE_CRC, AD4062_REG_DEVICE_STATUS), +}; + +static const struct regmap_access_table ad4062_regmap_wr_table = { + .yes_ranges = ad4062_regmap_wr_ranges, + .n_yes_ranges = ARRAY_SIZE(ad4062_regmap_wr_ranges), +}; + +static const struct iio_event_spec ad4062_events[] = { + { + .type = IIO_EV_TYPE_THRESH, + .dir = IIO_EV_DIR_EITHER, + .mask_shared_by_all = BIT(IIO_EV_INFO_ENABLE), + }, + { + .type = IIO_EV_TYPE_THRESH, + .dir = IIO_EV_DIR_RISING, + .mask_shared_by_all = BIT(IIO_EV_INFO_VALUE) | + BIT(IIO_EV_INFO_HYSTERESIS), + }, + { + .type = IIO_EV_TYPE_THRESH, + .dir = IIO_EV_DIR_FALLING, + .mask_shared_by_all = BIT(IIO_EV_INFO_VALUE) | + BIT(IIO_EV_INFO_HYSTERESIS), + }, +}; + +#define AD4062_CHAN(bits) { \ + .type = IIO_VOLTAGE, \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_RAW) | \ + BIT(IIO_CHAN_INFO_SCALE) | \ + BIT(IIO_CHAN_INFO_CALIBSCALE) | \ + BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \ + .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ), \ + .info_mask_shared_by_type_available = BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \ + .info_mask_shared_by_all_available = BIT(IIO_CHAN_INFO_SAMP_FREQ), \ + .indexed = 1, \ + .channel = 0, \ + .event_spec = ad4062_events, \ + .num_event_specs = ARRAY_SIZE(ad4062_events), \ + .has_ext_scan_type = 1, \ + .ext_scan_type = ad4062_scan_type_##bits##_s, \ + .num_ext_scan_type = ARRAY_SIZE(ad4062_scan_type_##bits##_s), \ +} + +static const struct ad4062_chip_info ad4060_chip_info = { + .name = "ad4060", + .channels = { AD4062_CHAN(12) }, + .prod_id = AD4060_PROD_ID, + .avg_max = 256, +}; + +static const struct ad4062_chip_info ad4062_chip_info = { + .name = "ad4062", + .channels = { AD4062_CHAN(16) }, + .prod_id = AD4062_PROD_ID, + .avg_max = 4096, +}; + +static ssize_t sampling_frequency_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct ad4062_state *st = iio_priv(dev_to_iio_dev(dev)); + + return sysfs_emit(buf, "%d\n", ad4062_conversion_freqs[st->events_frequency]); +} + +static int sampling_frequency_store_dispatch(struct iio_dev *indio_dev, + const char *buf) +{ + struct ad4062_state *st = iio_priv(indio_dev); + int val, ret; + + if (st->wait_event) + return -EBUSY; + + ret = kstrtoint(buf, 10, &val); + if (ret) + return ret; + + st->events_frequency = find_closest_descending(val, ad4062_conversion_freqs, + ARRAY_SIZE(ad4062_conversion_freqs)); + return 0; +} + +static ssize_t sampling_frequency_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t len) +{ + struct iio_dev *indio_dev = dev_to_iio_dev(dev); + int ret; + + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; + + ret = sampling_frequency_store_dispatch(indio_dev, buf); + iio_device_release_direct(indio_dev); + return ret ?: len; +} + +static IIO_DEVICE_ATTR_RW(sampling_frequency, 0); + +static ssize_t sampling_frequency_available_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + int ret = 0; + + for (u8 i = 0; i < ARRAY_SIZE(ad4062_conversion_freqs); i++) + ret += sysfs_emit_at(buf, ret, "%d%s", ad4062_conversion_freqs[i], + i != (ARRAY_SIZE(ad4062_conversion_freqs) - 1) ? " " : "\n"); + return ret; +} + +static IIO_DEVICE_ATTR_RO(sampling_frequency_available, 0); + +static struct attribute *ad4062_event_attributes[] = { + &iio_dev_attr_sampling_frequency.dev_attr.attr, + &iio_dev_attr_sampling_frequency_available.dev_attr.attr, + NULL +}; + +static const struct attribute_group ad4062_event_attribute_group = { + .attrs = ad4062_event_attributes, +}; + +static int ad4062_set_oversampling_ratio(struct ad4062_state *st, int val, int val2) +{ + const u32 _max = st->chip->avg_max; + const u32 _min = 1; + int ret; + + if (!in_range(val, _min, _max) || val2 != 0) + return -EINVAL; + + /* 1 disables oversampling */ + val = ilog2(val); + if (val == 0) { + st->mode = AD4062_SAMPLE_MODE; + } else { + st->mode = AD4062_BURST_AVERAGING_MODE; + ret = regmap_write(st->regmap, AD4062_REG_AVG_CONFIG, val - 1); + if (ret) + return ret; + } + st->oversamp_ratio = val; + + return 0; +} + +static int ad4062_get_oversampling_ratio(struct ad4062_state *st, int *val) +{ + int ret, buf; + + if (st->mode == AD4062_SAMPLE_MODE) { + *val = 1; + return 0; + } + + ret = regmap_read(st->regmap, AD4062_REG_AVG_CONFIG, &buf); + if (ret) + return ret; + + *val = BIT(buf + 1); + return 0; +} + +static int ad4062_calc_sampling_frequency(unsigned int fosc, unsigned int oversamp_ratio) +{ + /* From datasheet p.31: (n_avg - 1)/fosc + tconv */ + u32 n_avg = BIT(oversamp_ratio) - 1; + u32 period_ns = NSEC_PER_SEC / fosc; + + /* Result is less than 1 Hz */ + if (n_avg >= fosc) + return 1; + + return NSEC_PER_SEC / (n_avg * period_ns + AD4062_TCONV_NS); +} + +static int ad4062_populate_sampling_frequency(struct ad4062_state *st) +{ + for (u8 i = 0; i < ARRAY_SIZE(ad4062_conversion_freqs); i++) + st->samp_freqs[i] = + ad4062_calc_sampling_frequency(ad4062_conversion_freqs[i], + st->oversamp_ratio); + return 0; +} + +static int ad4062_get_sampling_frequency(struct ad4062_state *st, int *val) +{ + int freq = ad4062_conversion_freqs[st->sampling_frequency]; + + *val = ad4062_calc_sampling_frequency(freq, st->oversamp_ratio); + return IIO_VAL_INT; +} + +static int ad4062_set_sampling_frequency(struct ad4062_state *st, int val, int val2) +{ + int ret; + + if (val2 != 0) + return -EINVAL; + + ret = ad4062_populate_sampling_frequency(st); + if (ret) + return ret; + + st->sampling_frequency = + find_closest_descending(val, st->samp_freqs, + ARRAY_SIZE(ad4062_conversion_freqs)); + return 0; +} + +static int ad4062_check_ids(struct ad4062_state *st) +{ + struct device *dev = &st->i3cdev->dev; + int ret; + u16 val; + + ret = regmap_bulk_read(st->regmap, AD4062_REG_PROD_ID_1, + &st->buf.be16, sizeof(st->buf.be16)); + if (ret) + return ret; + + val = be16_to_cpu(st->buf.be16); + if (val != st->chip->prod_id) + dev_warn(dev, "Production ID x%x does not match known values", val); + + ret = regmap_bulk_read(st->regmap, AD4062_REG_VENDOR_H, + &st->buf.be16, sizeof(st->buf.be16)); + if (ret) + return ret; + + val = be16_to_cpu(st->buf.be16); + if (val != AD4062_I3C_VENDOR) + return dev_err_probe(dev, -ENODEV, + "Vendor ID x%x does not match expected value\n", val); + + return 0; +} + +static int ad4062_conversion_frequency_set(struct ad4062_state *st, u8 val) +{ + return regmap_write(st->regmap, AD4062_REG_TIMER_CONFIG, + FIELD_PREP(AD4062_REG_TIMER_CONFIG_FS_MASK, val)); +} + +static int ad4062_set_operation_mode(struct ad4062_state *st, + enum ad4062_operation_mode mode) +{ + const unsigned int samp_freq = mode == AD4062_MONITOR_MODE ? + st->events_frequency : st->sampling_frequency; + int ret; + + ret = ad4062_conversion_frequency_set(st, samp_freq); + if (ret) + return ret; + + ret = regmap_update_bits(st->regmap, AD4062_REG_ADC_MODES, + AD4062_REG_ADC_MODES_MODE_MSK, mode); + if (ret) + return ret; + + if (mode == AD4062_MONITOR_MODE) { + /* Change address pointer to enter monitor mode */ + struct i3c_xfer xfer_trigger = { + .data.out = &st->conv_addr, + .len = sizeof(st->conv_addr), + .rnw = false, + }; + st->conv_addr = AD4062_REG_CONV_TRIGGER_32BITS; + return i3c_device_do_xfers(st->i3cdev, &xfer_trigger, 1, I3C_SDR); + } + + return regmap_write(st->regmap, AD4062_REG_MODE_SET, + AD4062_REG_MODE_SET_ENTER_ADC); +} + +static int ad4062_soft_reset(struct ad4062_state *st) +{ + u8 val = AD4062_SOFT_RESET; + int ret; + + ret = regmap_write(st->regmap, AD4062_REG_INTERFACE_CONFIG_A, val); + if (ret) + return ret; + + /* Wait AD4062 treset time, datasheet p8 */ + ndelay(60); + + return 0; +} + +static int ad4062_setup(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, + const bool *ref_sel) +{ + struct ad4062_state *st = iio_priv(indio_dev); + const struct iio_scan_type *scan_type; + int ret; + + scan_type = iio_get_current_scan_type(indio_dev, chan); + if (IS_ERR(scan_type)) + return PTR_ERR(scan_type); + + ret = regmap_update_bits(st->regmap, AD4062_REG_GP_CONF, + AD4062_REG_GP_CONF_MODE_MSK_0, + FIELD_PREP(AD4062_REG_GP_CONF_MODE_MSK_0, + AD4062_GP_INTR)); + if (ret) + return ret; + + ret = regmap_update_bits(st->regmap, AD4062_REG_GP_CONF, + AD4062_REG_GP_CONF_MODE_MSK_1, + FIELD_PREP(AD4062_REG_GP_CONF_MODE_MSK_1, + AD4062_GP_DRDY)); + if (ret) + return ret; + + ret = regmap_update_bits(st->regmap, AD4062_REG_ADC_CONFIG, + AD4062_REG_ADC_CONFIG_REF_EN_MSK, + FIELD_PREP(AD4062_REG_ADC_CONFIG_REF_EN_MSK, + *ref_sel)); + if (ret) + return ret; + + ret = regmap_write(st->regmap, AD4062_REG_DEVICE_STATUS, + AD4062_REG_DEVICE_STATUS_DEVICE_RESET); + if (ret) + return ret; + + ret = regmap_update_bits(st->regmap, AD4062_REG_INTR_CONF, + AD4062_REG_INTR_CONF_EN_MSK_0, + FIELD_PREP(AD4062_REG_INTR_CONF_EN_MSK_0, + AD4062_INTR_EN_EITHER)); + if (ret) + return ret; + + ret = regmap_update_bits(st->regmap, AD4062_REG_INTR_CONF, + AD4062_REG_INTR_CONF_EN_MSK_1, + FIELD_PREP(AD4062_REG_INTR_CONF_EN_MSK_1, + AD4062_INTR_EN_NEITHER)); + if (ret) + return ret; + + st->buf.be16 = cpu_to_be16(AD4062_MON_VAL_MIDDLE_POINT); + return regmap_bulk_write(st->regmap, AD4062_REG_MON_VAL, + &st->buf.be16, sizeof(st->buf.be16)); +} + +static irqreturn_t ad4062_irq_handler_thresh(int irq, void *private) +{ + struct iio_dev *indio_dev = private; + + iio_push_event(indio_dev, + IIO_UNMOD_EVENT_CODE(IIO_VOLTAGE, 0, + IIO_EV_TYPE_THRESH, + IIO_EV_DIR_EITHER), + iio_get_time_ns(indio_dev)); + + return IRQ_HANDLED; +} + +static irqreturn_t ad4062_irq_handler_drdy(int irq, void *private) +{ + struct iio_dev *indio_dev = private; + struct ad4062_state *st = iio_priv(indio_dev); + + if (iio_buffer_enabled(indio_dev) && iio_trigger_using_own(indio_dev)) + iio_trigger_poll(st->trigger); + else + complete(&st->completion); + + return IRQ_HANDLED; +} + +static void ad4062_ibi_handler(struct i3c_device *i3cdev, + const struct i3c_ibi_payload *payload) +{ + struct ad4062_state *st = i3cdev_get_drvdata(i3cdev); + + if (st->wait_event) { + iio_push_event(st->indio_dev, + IIO_UNMOD_EVENT_CODE(IIO_VOLTAGE, 0, + IIO_EV_TYPE_THRESH, + IIO_EV_DIR_EITHER), + iio_get_time_ns(st->indio_dev)); + return; + } + if (iio_buffer_enabled(st->indio_dev)) + iio_trigger_poll_nested(st->trigger); + else + complete(&st->completion); +} + +static void ad4062_trigger_work(struct work_struct *work) +{ + struct ad4062_state *st = + container_of(work, struct ad4062_state, trig_conv); + int ret; + + /* + * Read current conversion, if at reg CONV_READ, stop bit triggers + * next sample and does not need writing the address. + */ + struct i3c_xfer xfer_sample = { + .data.in = &st->buf.be32, + .len = st->conv_sizeof, + .rnw = true, + }; + struct i3c_xfer xfer_trigger = { + .data.out = &st->conv_addr, + .len = sizeof(st->conv_addr), + .rnw = false, + }; + + ret = i3c_device_do_xfers(st->i3cdev, &xfer_sample, 1, I3C_SDR); + if (ret) + return; + + iio_push_to_buffers_with_ts(st->indio_dev, &st->buf.be32, st->conv_sizeof, + iio_get_time_ns(st->indio_dev)); + if (st->gpo_irq[1]) + return; + + i3c_device_do_xfers(st->i3cdev, &xfer_trigger, 1, I3C_SDR); +} + +static irqreturn_t ad4062_poll_handler(int irq, void *p) +{ + struct iio_poll_func *pf = p; + struct iio_dev *indio_dev = pf->indio_dev; + struct ad4062_state *st = iio_priv(indio_dev); + + iio_trigger_notify_done(indio_dev->trig); + schedule_work(&st->trig_conv); + + return IRQ_HANDLED; +} + +static void ad4062_disable_ibi(void *data) +{ + struct i3c_device *i3cdev = data; + + i3c_device_disable_ibi(i3cdev); +} + +static void ad4062_free_ibi(void *data) +{ + struct i3c_device *i3cdev = data; + + i3c_device_free_ibi(i3cdev); +} + +static int ad4062_request_ibi(struct i3c_device *i3cdev) +{ + const struct i3c_ibi_setup ibireq = { + .max_payload_len = 1, + .num_slots = 1, + .handler = ad4062_ibi_handler, + }; + int ret; + + ret = i3c_device_request_ibi(i3cdev, &ibireq); + if (ret) + return ret; + + ret = devm_add_action_or_reset(&i3cdev->dev, ad4062_free_ibi, i3cdev); + if (ret) + return ret; + + ret = i3c_device_enable_ibi(i3cdev); + if (ret) + return ret; + + return devm_add_action_or_reset(&i3cdev->dev, ad4062_disable_ibi, i3cdev); +} + +static int ad4062_request_irq(struct iio_dev *indio_dev) +{ + struct ad4062_state *st = iio_priv(indio_dev); + struct device *dev = &st->i3cdev->dev; + int ret; + + ret = fwnode_irq_get_byname(dev_fwnode(&st->i3cdev->dev), "gp0"); + if (ret == -EPROBE_DEFER) + return ret; + + if (ret < 0) { + st->gpo_irq[0] = false; + ret = regmap_update_bits(st->regmap, AD4062_REG_ADC_IBI_EN, + AD4062_REG_ADC_IBI_EN_MAX | AD4062_REG_ADC_IBI_EN_MIN, + AD4062_REG_ADC_IBI_EN_MAX | AD4062_REG_ADC_IBI_EN_MIN); + if (ret) + return ret; + } else { + st->gpo_irq[0] = true; + ret = devm_request_threaded_irq(dev, ret, NULL, + ad4062_irq_handler_thresh, + IRQF_ONESHOT, indio_dev->name, + indio_dev); + if (ret) + return ret; + } + + ret = fwnode_irq_get_byname(dev_fwnode(&st->i3cdev->dev), "gp1"); + if (ret == -EPROBE_DEFER) + return ret; + + if (ret < 0) { + st->gpo_irq[1] = false; + return regmap_update_bits(st->regmap, AD4062_REG_ADC_IBI_EN, + AD4062_REG_ADC_IBI_EN_CONV_TRIGGER, + AD4062_REG_ADC_IBI_EN_CONV_TRIGGER); + } + st->gpo_irq[1] = true; + + return devm_request_irq(dev, ret, ad4062_irq_handler_drdy, + IRQF_NO_THREAD, indio_dev->name, indio_dev); +} + +static const struct iio_trigger_ops ad4062_trigger_ops = { + .validate_device = &iio_trigger_validate_own_device, +}; + +static int ad4062_request_trigger(struct iio_dev *indio_dev) +{ + struct ad4062_state *st = iio_priv(indio_dev); + struct device *dev = &st->i3cdev->dev; + int ret; + + st->trigger = devm_iio_trigger_alloc(dev, "%s-dev%d", + indio_dev->name, + iio_device_id(indio_dev)); + if (!st->trigger) + return -ENOMEM; + + st->trigger->ops = &ad4062_trigger_ops; + iio_trigger_set_drvdata(st->trigger, indio_dev); + + ret = devm_iio_trigger_register(dev, st->trigger); + if (ret) + return ret; + + indio_dev->trig = iio_trigger_get(st->trigger); + + return 0; +} + +static const int ad4062_oversampling_avail[] = { + 1, 2, 4, 8, 16, 32, 64, 128, /* 0 - 7 */ + 256, 512, 1024, 2048, 4096, /* 8 - 12 */ +}; + +static int ad4062_read_avail(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, const int **vals, + int *type, int *len, long mask) +{ + struct ad4062_state *st = iio_priv(indio_dev); + int ret; + + switch (mask) { + case IIO_CHAN_INFO_OVERSAMPLING_RATIO: + *vals = ad4062_oversampling_avail; + *len = ARRAY_SIZE(ad4062_oversampling_avail); + *len -= st->chip->avg_max == 256 ? 4 : 0; + *type = IIO_VAL_INT; + + return IIO_AVAIL_LIST; + case IIO_CHAN_INFO_SAMP_FREQ: + ret = ad4062_populate_sampling_frequency(st); + if (ret) + return ret; + *vals = st->samp_freqs; + *len = st->oversamp_ratio ? ARRAY_SIZE(ad4062_conversion_freqs) : 1; + *type = IIO_VAL_INT; + + return IIO_AVAIL_LIST; + default: + return -EINVAL; + } +} + +static int ad4062_get_chan_scale(struct iio_dev *indio_dev, int *val, int *val2) +{ + struct ad4062_state *st = iio_priv(indio_dev); + const struct iio_scan_type *scan_type; + + /* + * In burst averaging mode the averaging filter accumulates resulting + * in a sample with increased precision. + */ + scan_type = iio_get_current_scan_type(indio_dev, st->chip->channels); + if (IS_ERR(scan_type)) + return PTR_ERR(scan_type); + + *val = (st->vref_uV * 2) / (MICRO / MILLI); /* signed */ + *val2 = scan_type->realbits - 1; + + return IIO_VAL_FRACTIONAL_LOG2; +} + +static int ad4062_get_chan_calibscale(struct ad4062_state *st, int *val, int *val2) +{ + int ret; + + ret = regmap_bulk_read(st->regmap, AD4062_REG_MON_VAL, + &st->buf.be16, sizeof(st->buf.be16)); + if (ret) + return ret; + + /* From datasheet: code out = code in × mon_val/0x8000 */ + *val = be16_to_cpu(st->buf.be16) * 2; + *val2 = 16; + + return IIO_VAL_FRACTIONAL_LOG2; +} + +static int ad4062_set_chan_calibscale(struct ad4062_state *st, int gain_int, + int gain_frac) +{ + /* Divide numerator and denumerator by known great common divider */ + const u32 mon_val = AD4062_MON_VAL_MIDDLE_POINT / 64; + const u32 micro = MICRO / 64; + const u32 gain_fp = gain_int * MICRO + gain_frac; + const u32 reg_val = DIV_ROUND_CLOSEST(gain_fp * mon_val, micro); + int ret; + + /* Checks if the gain is in range and the value fits the field */ + if (gain_int < 0 || gain_int > 1 || reg_val > BIT(16) - 1) + return -EINVAL; + + st->buf.be16 = cpu_to_be16(reg_val); + ret = regmap_bulk_write(st->regmap, AD4062_REG_MON_VAL, + &st->buf.be16, sizeof(st->buf.be16)); + if (ret) + return ret; + + /* Enable scale if gain is not equal to one */ + return regmap_update_bits(st->regmap, AD4062_REG_ADC_CONFIG, + AD4062_REG_ADC_CONFIG_SCALE_EN_MSK, + FIELD_PREP(AD4062_REG_ADC_CONFIG_SCALE_EN_MSK, + !(gain_int == 1 && gain_frac == 0))); +} + +static int ad4062_read_chan_raw(struct ad4062_state *st, int *val) +{ + struct i3c_device *i3cdev = st->i3cdev; + struct i3c_xfer xfer_trigger = { + .data.out = &st->conv_addr, + .len = sizeof(st->conv_addr), + .rnw = false, + }; + struct i3c_xfer xfer_sample = { + .data.in = &st->buf.be32, + .len = sizeof(st->buf.be32), + .rnw = true, + }; + int ret; + + PM_RUNTIME_ACQUIRE(&st->i3cdev->dev, pm); + ret = PM_RUNTIME_ACQUIRE_ERR(&pm); + if (ret) + return ret; + + ret = ad4062_set_operation_mode(st, st->mode); + if (ret) + return ret; + + reinit_completion(&st->completion); + /* Change address pointer to trigger conversion */ + st->conv_addr = AD4062_REG_CONV_TRIGGER_32BITS; + ret = i3c_device_do_xfers(i3cdev, &xfer_trigger, 1, I3C_SDR); + if (ret) + return ret; + /* + * Single sample read should be used only for oversampling and + * sampling frequency pairs that take less than 1 sec. + */ + ret = wait_for_completion_timeout(&st->completion, + msecs_to_jiffies(1000)); + if (!ret) + return -ETIMEDOUT; + + ret = i3c_device_do_xfers(i3cdev, &xfer_sample, 1, I3C_SDR); + if (ret) + return ret; + *val = be32_to_cpu(st->buf.be32); + return 0; +} + +static int ad4062_read_raw_dispatch(struct ad4062_state *st, + int *val, int *val2, long info) +{ + if (st->wait_event) + return -EBUSY; + + switch (info) { + case IIO_CHAN_INFO_RAW: + return ad4062_read_chan_raw(st, val); + + case IIO_CHAN_INFO_CALIBSCALE: + return ad4062_get_chan_calibscale(st, val, val2); + + case IIO_CHAN_INFO_OVERSAMPLING_RATIO: + return ad4062_get_oversampling_ratio(st, val); + + default: + return -EINVAL; + } +} + +static int ad4062_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long info) +{ + struct ad4062_state *st = iio_priv(indio_dev); + int ret; + + switch (info) { + case IIO_CHAN_INFO_SCALE: + return ad4062_get_chan_scale(indio_dev, val, val2); + + case IIO_CHAN_INFO_SAMP_FREQ: + return ad4062_get_sampling_frequency(st, val); + } + + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; + + ret = ad4062_read_raw_dispatch(st, val, val2, info); + iio_device_release_direct(indio_dev); + return ret ?: IIO_VAL_INT; +} + +static int ad4062_write_raw_dispatch(struct ad4062_state *st, int val, int val2, + long info) +{ + if (st->wait_event) + return -EBUSY; + + switch (info) { + case IIO_CHAN_INFO_OVERSAMPLING_RATIO: + return ad4062_set_oversampling_ratio(st, val, val2); + + case IIO_CHAN_INFO_CALIBSCALE: + return ad4062_set_chan_calibscale(st, val, val2); + + default: + return -EINVAL; + } +} + +static int ad4062_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int val, + int val2, long info) +{ + struct ad4062_state *st = iio_priv(indio_dev); + int ret; + + switch (info) { + case IIO_CHAN_INFO_SAMP_FREQ: + return ad4062_set_sampling_frequency(st, val, val2); + } + + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; + + ret = ad4062_write_raw_dispatch(st, val, val2, info); + iio_device_release_direct(indio_dev); + return ret; +} + +static int pm_ad4062_monitor_mode_enable(struct ad4062_state *st) +{ + int ret; + + PM_RUNTIME_ACQUIRE(&st->i3cdev->dev, pm); + ret = PM_RUNTIME_ACQUIRE_ERR(&pm); + if (ret) + return ret; + + return ad4062_set_operation_mode(st, AD4062_MONITOR_MODE); +} + +static int ad4062_monitor_mode_enable(struct ad4062_state *st) +{ + int ret; + + ret = pm_ad4062_monitor_mode_enable(st); + if (ret) + return ret; + + pm_runtime_get_noresume(&st->i3cdev->dev); + return 0; +} + +static int ad4062_monitor_mode_disable(struct ad4062_state *st) +{ + pm_runtime_put_autosuspend(&st->i3cdev->dev); + return 0; +} + +static int ad4062_read_event_config(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir) +{ + struct ad4062_state *st = iio_priv(indio_dev); + + return st->wait_event; +} + +static int ad4062_write_event_config_dispatch(struct iio_dev *indio_dev, + bool state) +{ + struct ad4062_state *st = iio_priv(indio_dev); + int ret; + + if (st->wait_event == state) + ret = 0; + else if (state) + ret = ad4062_monitor_mode_enable(st); + else + ret = ad4062_monitor_mode_disable(st); + if (ret) + return ret; + + st->wait_event = state; + return 0; +} + +static int ad4062_write_event_config(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir, + bool state) +{ + int ret; + + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; + + ret = ad4062_write_event_config_dispatch(indio_dev, state); + iio_device_release_direct(indio_dev); + return ret; +} + +static int __ad4062_read_event_info_value(struct ad4062_state *st, + enum iio_event_direction dir, int *val) +{ + int ret; + u8 reg; + + if (dir == IIO_EV_DIR_RISING) + reg = AD4062_REG_MAX_LIMIT; + else + reg = AD4062_REG_MIN_LIMIT; + + ret = regmap_bulk_read(st->regmap, reg, &st->buf.be16, + sizeof(st->buf.be16)); + if (ret) + return ret; + + *val = sign_extend32(be16_to_cpu(st->buf.be16), AD4062_LIMIT_BITS - 1); + + return 0; +} + +static int __ad4062_read_event_info_hysteresis(struct ad4062_state *st, + enum iio_event_direction dir, int *val) +{ + u8 reg; + + if (dir == IIO_EV_DIR_RISING) + reg = AD4062_REG_MAX_HYST; + else + reg = AD4062_REG_MIN_HYST; + return regmap_read(st->regmap, reg, val); +} + +static int ad4062_read_event_config_dispatch(struct iio_dev *indio_dev, + enum iio_event_direction dir, + enum iio_event_info info, int *val) +{ + struct ad4062_state *st = iio_priv(indio_dev); + + if (st->wait_event) + return -EBUSY; + + switch (info) { + case IIO_EV_INFO_VALUE: + return __ad4062_read_event_info_value(st, dir, val); + case IIO_EV_INFO_HYSTERESIS: + return __ad4062_read_event_info_hysteresis(st, dir, val); + default: + return -EINVAL; + } +} + +static int ad4062_read_event_value(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir, + enum iio_event_info info, int *val, + int *val2) +{ + int ret; + + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; + + ret = ad4062_read_event_config_dispatch(indio_dev, dir, info, val); + iio_device_release_direct(indio_dev); + return ret ?: IIO_VAL_INT; +} + +static int __ad4062_write_event_info_value(struct ad4062_state *st, + enum iio_event_direction dir, int val) +{ + u8 reg; + + if (val != sign_extend32(val, AD4062_LIMIT_BITS - 1)) + return -EINVAL; + if (dir == IIO_EV_DIR_RISING) + reg = AD4062_REG_MAX_LIMIT; + else + reg = AD4062_REG_MIN_LIMIT; + st->buf.be16 = cpu_to_be16(val); + + return regmap_bulk_write(st->regmap, reg, &st->buf.be16, + sizeof(st->buf.be16)); +} + +static int __ad4062_write_event_info_hysteresis(struct ad4062_state *st, + enum iio_event_direction dir, int val) +{ + u8 reg; + + if (val > BIT(7) - 1) + return -EINVAL; + if (dir == IIO_EV_DIR_RISING) + reg = AD4062_REG_MAX_HYST; + else + reg = AD4062_REG_MIN_HYST; + + return regmap_write(st->regmap, reg, val); +} + +static int ad4062_write_event_value_dispatch(struct iio_dev *indio_dev, + enum iio_event_type type, + enum iio_event_direction dir, + enum iio_event_info info, int val) +{ + struct ad4062_state *st = iio_priv(indio_dev); + + if (st->wait_event) + return -EBUSY; + + switch (type) { + case IIO_EV_TYPE_THRESH: + switch (info) { + case IIO_EV_INFO_VALUE: + return __ad4062_write_event_info_value(st, dir, val); + case IIO_EV_INFO_HYSTERESIS: + return __ad4062_write_event_info_hysteresis(st, dir, val); + default: + return -EINVAL; + } + default: + return -EINVAL; + } +} + +static int ad4062_write_event_value(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir, + enum iio_event_info info, int val, + int val2) +{ + int ret; + + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; + + ret = ad4062_write_event_value_dispatch(indio_dev, type, dir, info, val); + iio_device_release_direct(indio_dev); + return ret; +} + +/* + * The AD4062 in burst averaging mode increases realbits from 16-bits to + * 20-bits, increasing the storagebits from 16-bits to 32-bits. + */ +static inline int ad4062_sizeof_storagebits(struct ad4062_state *st) +{ + const struct iio_scan_type *scan_type = + iio_get_current_scan_type(st->indio_dev, st->chip->channels); + + if (IS_ERR(scan_type)) + return PTR_ERR(scan_type); + + return BITS_TO_BYTES(scan_type->storagebits); +} + +/* Read registers only with realbits (no sign extension bytes) */ +static inline size_t ad4062_get_conv_addr(struct ad4062_state *st, size_t _sizeof) +{ + if (st->gpo_irq[1]) + return _sizeof == sizeof(u32) ? AD4062_REG_CONV_READ_32BITS : + AD4062_REG_CONV_READ_16BITS; + return _sizeof == sizeof(u32) ? AD4062_REG_CONV_TRIGGER_32BITS : + AD4062_REG_CONV_TRIGGER_16BITS; +} + +static int pm_ad4062_triggered_buffer_postenable(struct ad4062_state *st) +{ + int ret; + + PM_RUNTIME_ACQUIRE(&st->i3cdev->dev, pm); + ret = PM_RUNTIME_ACQUIRE_ERR(&pm); + if (ret) + return ret; + + if (st->wait_event) + return -EBUSY; + + ret = ad4062_set_operation_mode(st, st->mode); + if (ret) + return ret; + + ret = ad4062_sizeof_storagebits(st); + if (ret < 0) + return ret; + + st->conv_sizeof = ret; + + st->conv_addr = ad4062_get_conv_addr(st, st->conv_sizeof); + /* CONV_READ requires read to trigger first sample. */ + struct i3c_xfer xfer_sample[2] = { + { + .data.out = &st->conv_addr, + .len = sizeof(st->conv_addr), + .rnw = false, + }, + { + .data.in = &st->buf.be32, + .len = sizeof(st->buf.be32), + .rnw = true, + } + }; + + return i3c_device_do_xfers(st->i3cdev, xfer_sample, + st->gpo_irq[1] ? 2 : 1, I3C_SDR); +} + +static int ad4062_triggered_buffer_postenable(struct iio_dev *indio_dev) +{ + struct ad4062_state *st = iio_priv(indio_dev); + int ret; + + ret = pm_ad4062_triggered_buffer_postenable(st); + if (ret) + return ret; + + pm_runtime_get_noresume(&st->i3cdev->dev); + return 0; +} + +static int ad4062_triggered_buffer_predisable(struct iio_dev *indio_dev) +{ + struct ad4062_state *st = iio_priv(indio_dev); + + pm_runtime_put_autosuspend(&st->i3cdev->dev); + return 0; +} + +static const struct iio_buffer_setup_ops ad4062_triggered_buffer_setup_ops = { + .postenable = &ad4062_triggered_buffer_postenable, + .predisable = &ad4062_triggered_buffer_predisable, +}; + +static int ad4062_debugfs_reg_access(struct iio_dev *indio_dev, unsigned int reg, + unsigned int writeval, unsigned int *readval) +{ + struct ad4062_state *st = iio_priv(indio_dev); + + if (readval) + return regmap_read(st->regmap, reg, readval); + else + return regmap_write(st->regmap, reg, writeval); +} + +static int ad4062_get_current_scan_type(const struct iio_dev *indio_dev, + const struct iio_chan_spec *chan) +{ + struct ad4062_state *st = iio_priv(indio_dev); + + return st->mode == AD4062_BURST_AVERAGING_MODE ? + AD4062_SCAN_TYPE_BURST_AVG : + AD4062_SCAN_TYPE_SAMPLE; +} + +static const struct iio_info ad4062_info = { + .read_raw = ad4062_read_raw, + .write_raw = ad4062_write_raw, + .read_avail = ad4062_read_avail, + .read_event_config = ad4062_read_event_config, + .write_event_config = ad4062_write_event_config, + .read_event_value = ad4062_read_event_value, + .write_event_value = ad4062_write_event_value, + .event_attrs = &ad4062_event_attribute_group, + .get_current_scan_type = ad4062_get_current_scan_type, + .debugfs_reg_access = ad4062_debugfs_reg_access, +}; + +static const struct regmap_config ad4062_regmap_config = { + .name = "ad4062", + .reg_bits = 8, + .val_bits = 8, + .max_register = AD4062_MAX_REG, + .rd_table = &ad4062_regmap_rd_table, + .wr_table = &ad4062_regmap_wr_table, + .can_sleep = true, +}; + +static int ad4062_regulators_get(struct ad4062_state *st, bool *ref_sel) +{ + struct device *dev = &st->i3cdev->dev; + int ret; + + ret = devm_regulator_get_enable(dev, "vio"); + if (ret) + return dev_err_probe(dev, ret, "Failed to enable vio voltage\n"); + + st->vref_uV = devm_regulator_get_enable_read_voltage(dev, "ref"); + *ref_sel = st->vref_uV == -ENODEV; + if (st->vref_uV < 0 && !*ref_sel) + return dev_err_probe(dev, st->vref_uV, + "Failed to enable and read ref voltage\n"); + + if (*ref_sel) { + st->vref_uV = devm_regulator_get_enable_read_voltage(dev, "vdd"); + if (st->vref_uV < 0) + return dev_err_probe(dev, st->vref_uV, + "Failed to enable and read vdd voltage\n"); + } else { + ret = devm_regulator_get_enable(dev, "vdd"); + if (ret) + return dev_err_probe(dev, ret, + "Failed to enable vdd regulator\n"); + } + + return 0; +} + +static int ad4062_gpio_get_direction(struct gpio_chip *gc, unsigned int offset) +{ + return GPIO_LINE_DIRECTION_OUT; +} + +static int ad4062_gpio_set(struct gpio_chip *gc, unsigned int offset, int value) +{ + struct ad4062_state *st = gpiochip_get_data(gc); + unsigned int reg_val = value ? AD4062_GP_STATIC_HIGH : AD4062_GP_STATIC_LOW; + + if (offset) + return regmap_update_bits(st->regmap, AD4062_REG_GP_CONF, + AD4062_REG_GP_CONF_MODE_MSK_1, + FIELD_PREP(AD4062_REG_GP_CONF_MODE_MSK_1, reg_val)); + else + return regmap_update_bits(st->regmap, AD4062_REG_GP_CONF, + AD4062_REG_GP_CONF_MODE_MSK_0, + FIELD_PREP(AD4062_REG_GP_CONF_MODE_MSK_0, reg_val)); +} + +static int ad4062_gpio_get(struct gpio_chip *gc, unsigned int offset) +{ + struct ad4062_state *st = gpiochip_get_data(gc); + unsigned int reg_val; + int ret; + + ret = regmap_read(st->regmap, AD4062_REG_GP_CONF, ®_val); + if (ret) + return ret; + + if (offset) + reg_val = FIELD_GET(AD4062_REG_GP_CONF_MODE_MSK_1, reg_val); + else + reg_val = FIELD_GET(AD4062_REG_GP_CONF_MODE_MSK_0, reg_val); + + return reg_val == AD4062_GP_STATIC_HIGH; +} + +static void ad4062_gpio_disable(void *data) +{ + struct ad4062_state *st = data; + u8 val = FIELD_PREP(AD4062_REG_GP_CONF_MODE_MSK_0, AD4062_GP_DISABLED) | + FIELD_PREP(AD4062_REG_GP_CONF_MODE_MSK_1, AD4062_GP_DISABLED); + + regmap_update_bits(st->regmap, AD4062_REG_GP_CONF, + AD4062_REG_GP_CONF_MODE_MSK_1 | AD4062_REG_GP_CONF_MODE_MSK_0, + val); +} + +static int ad4062_gpio_init_valid_mask(struct gpio_chip *gc, + unsigned long *valid_mask, + unsigned int ngpios) +{ + struct ad4062_state *st = gpiochip_get_data(gc); + + bitmap_zero(valid_mask, ngpios); + + for (unsigned int i = 0; i < ARRAY_SIZE(st->gpo_irq); i++) + __assign_bit(i, valid_mask, !st->gpo_irq[i]); + + return 0; +} + +static int ad4062_gpio_init(struct ad4062_state *st) +{ + struct device *dev = &st->i3cdev->dev; + struct gpio_chip *gc; + u8 val, mask; + int ret; + + if (!device_property_read_bool(dev, "gpio-controller")) + return 0; + + gc = devm_kzalloc(dev, sizeof(*gc), GFP_KERNEL); + if (!gc) + return -ENOMEM; + + val = 0; + mask = 0; + if (!st->gpo_irq[0]) { + mask |= AD4062_REG_GP_CONF_MODE_MSK_0; + val |= FIELD_PREP(AD4062_REG_GP_CONF_MODE_MSK_0, AD4062_GP_STATIC_LOW); + } + if (!st->gpo_irq[1]) { + mask |= AD4062_REG_GP_CONF_MODE_MSK_1; + val |= FIELD_PREP(AD4062_REG_GP_CONF_MODE_MSK_1, AD4062_GP_STATIC_LOW); + } + + ret = regmap_update_bits(st->regmap, AD4062_REG_GP_CONF, + mask, val); + if (ret) + return ret; + + ret = devm_add_action_or_reset(dev, ad4062_gpio_disable, st); + if (ret) + return ret; + + gc->parent = dev; + gc->label = st->chip->name; + gc->owner = THIS_MODULE; + gc->base = -1; + gc->ngpio = 2; + gc->init_valid_mask = ad4062_gpio_init_valid_mask; + gc->get_direction = ad4062_gpio_get_direction; + gc->set = ad4062_gpio_set; + gc->get = ad4062_gpio_get; + gc->can_sleep = true; + + ret = devm_gpiochip_add_data(dev, gc, st); + if (ret) + return dev_err_probe(dev, ret, "Unable to register GPIO chip\n"); + + return 0; +} + +static const struct i3c_device_id ad4062_id_table[] = { + I3C_DEVICE(AD4062_I3C_VENDOR, AD4060_PROD_ID, &ad4060_chip_info), + I3C_DEVICE(AD4062_I3C_VENDOR, AD4062_PROD_ID, &ad4062_chip_info), + { } +}; +MODULE_DEVICE_TABLE(i3c, ad4062_id_table); + +static int ad4062_probe(struct i3c_device *i3cdev) +{ + const struct i3c_device_id *id = i3c_device_match_id(i3cdev, ad4062_id_table); + const struct ad4062_chip_info *chip = id->data; + struct device *dev = &i3cdev->dev; + struct iio_dev *indio_dev; + struct ad4062_state *st; + bool ref_sel; + int ret; + + indio_dev = devm_iio_device_alloc(dev, sizeof(*st)); + if (!indio_dev) + return -ENOMEM; + + st = iio_priv(indio_dev); + st->i3cdev = i3cdev; + i3cdev_set_drvdata(i3cdev, st); + init_completion(&st->completion); + + ret = ad4062_regulators_get(st, &ref_sel); + if (ret) + return ret; + + st->regmap = devm_regmap_init_i3c(i3cdev, &ad4062_regmap_config); + if (IS_ERR(st->regmap)) + return dev_err_probe(dev, PTR_ERR(st->regmap), + "Failed to initialize regmap\n"); + + st->mode = AD4062_SAMPLE_MODE; + st->wait_event = false; + st->chip = chip; + st->sampling_frequency = 0; + st->events_frequency = 0; + st->oversamp_ratio = 0; + st->indio_dev = indio_dev; + + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->num_channels = 1; + indio_dev->info = &ad4062_info; + indio_dev->name = chip->name; + indio_dev->channels = chip->channels; + + ret = ad4062_soft_reset(st); + if (ret) + return dev_err_probe(dev, ret, "AD4062 failed to soft reset\n"); + + ret = ad4062_check_ids(st); + if (ret) + return ret; + + ret = ad4062_setup(indio_dev, indio_dev->channels, &ref_sel); + if (ret) + return ret; + + ret = ad4062_request_irq(indio_dev); + if (ret) + return ret; + + ret = ad4062_request_trigger(indio_dev); + if (ret) + return ret; + + ret = devm_iio_triggered_buffer_setup(&i3cdev->dev, indio_dev, + iio_pollfunc_store_time, + ad4062_poll_handler, + &ad4062_triggered_buffer_setup_ops); + if (ret) + return ret; + + pm_runtime_set_active(dev); + ret = devm_pm_runtime_enable(dev); + if (ret) + return dev_err_probe(dev, ret, "Failed to enable pm_runtime\n"); + + pm_runtime_set_autosuspend_delay(dev, 1000); + pm_runtime_use_autosuspend(dev); + + ret = ad4062_request_ibi(i3cdev); + if (ret) + return dev_err_probe(dev, ret, "Failed to request i3c ibi\n"); + + ret = ad4062_gpio_init(st); + if (ret) + return ret; + + ret = devm_work_autocancel(dev, &st->trig_conv, ad4062_trigger_work); + if (ret) + return ret; + + return devm_iio_device_register(dev, indio_dev); +} + +static int ad4062_runtime_suspend(struct device *dev) +{ + struct ad4062_state *st = dev_get_drvdata(dev); + + return regmap_write(st->regmap, AD4062_REG_DEVICE_CONFIG, + FIELD_PREP(AD4062_REG_DEVICE_CONFIG_POWER_MODE_MSK, + AD4062_REG_DEVICE_CONFIG_LOW_POWER_MODE)); +} + +static int ad4062_runtime_resume(struct device *dev) +{ + struct ad4062_state *st = dev_get_drvdata(dev); + int ret; + + ret = regmap_clear_bits(st->regmap, AD4062_REG_DEVICE_CONFIG, + AD4062_REG_DEVICE_CONFIG_POWER_MODE_MSK); + if (ret) + return ret; + + /* Wait device functional blocks to power up */ + fsleep(3 * USEC_PER_MSEC); + return 0; +} + +static DEFINE_RUNTIME_DEV_PM_OPS(ad4062_pm_ops, + ad4062_runtime_suspend, ad4062_runtime_resume, NULL); + +static struct i3c_driver ad4062_driver = { + .driver = { + .name = "ad4062", + .pm = pm_ptr(&ad4062_pm_ops), + }, + .probe = ad4062_probe, + .id_table = ad4062_id_table, +}; +module_i3c_driver(ad4062_driver); + +MODULE_AUTHOR("Jorge Marques "); +MODULE_DESCRIPTION("Analog Devices AD4062"); +MODULE_LICENSE("GPL"); diff --git a/drivers/iio/adc/ad4080.c b/drivers/iio/adc/ad4080.c new file mode 100644 index 000000000..04cd6628e --- /dev/null +++ b/drivers/iio/adc/ad4080.c @@ -0,0 +1,918 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Analog Devices AD4080 SPI ADC driver + * + * Copyright 2025 Analog Devices Inc. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Register Definition */ +#define AD4080_REG_INTERFACE_CONFIG_A 0x00 +#define AD4080_REG_INTERFACE_CONFIG_B 0x01 +#define AD4080_REG_DEVICE_CONFIG 0x02 +#define AD4080_REG_CHIP_TYPE 0x03 +#define AD4080_REG_PRODUCT_ID_L 0x04 +#define AD4080_REG_PRODUCT_ID_H 0x05 +#define AD4080_REG_CHIP_GRADE 0x06 +#define AD4080_REG_SCRATCH_PAD 0x0A +#define AD4080_REG_SPI_REVISION 0x0B +#define AD4080_REG_VENDOR_L 0x0C +#define AD4080_REG_VENDOR_H 0x0D +#define AD4080_REG_STREAM_MODE 0x0E +#define AD4080_REG_TRANSFER_CONFIG 0x0F +#define AD4080_REG_INTERFACE_CONFIG_C 0x10 +#define AD4080_REG_INTERFACE_STATUS_A 0x11 +#define AD4080_REG_DEVICE_STATUS 0x14 +#define AD4080_REG_ADC_DATA_INTF_CONFIG_A 0x15 +#define AD4080_REG_ADC_DATA_INTF_CONFIG_B 0x16 +#define AD4080_REG_ADC_DATA_INTF_CONFIG_C 0x17 +#define AD4080_REG_PWR_CTRL 0x18 +#define AD4080_REG_GPIO_CONFIG_A 0x19 +#define AD4080_REG_GPIO_CONFIG_B 0x1A +#define AD4080_REG_GPIO_CONFIG_C 0x1B +#define AD4080_REG_GENERAL_CONFIG 0x1C +#define AD4080_REG_FIFO_WATERMARK_LSB 0x1D +#define AD4080_REG_FIFO_WATERMARK_MSB 0x1E +#define AD4080_REG_EVENT_HYSTERESIS_LSB 0x1F +#define AD4080_REG_EVENT_HYSTERESIS_MSB 0x20 +#define AD4080_REG_EVENT_DETECTION_HI_LSB 0x21 +#define AD4080_REG_EVENT_DETECTION_HI_MSB 0x22 +#define AD4080_REG_EVENT_DETECTION_LO_LSB 0x23 +#define AD4080_REG_EVENT_DETECTION_LO_MSB 0x24 +#define AD4080_REG_OFFSET_LSB 0x25 +#define AD4080_REG_OFFSET_MSB 0x26 +#define AD4080_REG_GAIN_LSB 0x27 +#define AD4080_REG_GAIN_MSB 0x28 +#define AD4080_REG_FILTER_CONFIG 0x29 + +/* AD4080_REG_INTERFACE_CONFIG_A Bit Definition */ +#define AD4080_INTERFACE_CONFIG_A_SW_RESET (BIT(7) | BIT(0)) +#define AD4080_INTERFACE_CONFIG_A_ADDR_ASC BIT(5) +#define AD4080_INTERFACE_CONFIG_A_SDO_ENABLE BIT(4) + +/* AD4080_REG_INTERFACE_CONFIG_B Bit Definition */ +#define AD4080_INTERFACE_CONFIG_B_SINGLE_INST BIT(7) +#define AD4080_INTERFACE_CONFIG_B_SHORT_INST BIT(3) + +/* AD4080_REG_DEVICE_CONFIG Bit Definition */ +#define AD4080_DEVICE_CONFIG_OPERATING_MODES_MSK GENMASK(1, 0) + +/* AD4080_REG_TRANSFER_CONFIG Bit Definition */ +#define AD4080_TRANSFER_CONFIG_KEEP_STREAM_LENGTH_VAL BIT(2) + +/* AD4080_REG_INTERFACE_CONFIG_C Bit Definition */ +#define AD4080_INTERFACE_CONFIG_C_STRICT_REG_ACCESS BIT(5) + +/* AD4080_REG_ADC_DATA_INTF_CONFIG_A Bit Definition */ +#define AD4080_ADC_DATA_INTF_CONFIG_A_RESERVED_CONFIG_A BIT(6) +#define AD4080_ADC_DATA_INTF_CONFIG_A_INTF_CHK_EN BIT(4) +#define AD4080_ADC_DATA_INTF_CONFIG_A_SPI_LVDS_LANES BIT(2) +#define AD4080_ADC_DATA_INTF_CONFIG_A_DATA_INTF_MODE BIT(0) + +/* AD4080_REG_ADC_DATA_INTF_CONFIG_B Bit Definition */ +#define AD4080_ADC_DATA_INTF_CONFIG_B_LVDS_CNV_CLK_CNT_MSK GENMASK(7, 4) +#define AD4080_ADC_DATA_INTF_CONFIG_B_LVDS_SELF_CLK_MODE BIT(3) +#define AD4080_ADC_DATA_INTF_CONFIG_B_LVDS_CNV_EN BIT(0) + +/* AD4080_REG_ADC_DATA_INTF_CONFIG_C Bit Definition */ +#define AD4080_ADC_DATA_INTF_CONFIG_C_LVDS_VOD_MSK GENMASK(6, 4) + +/* AD4080_REG_PWR_CTRL Bit Definition */ +#define AD4080_PWR_CTRL_ANA_DIG_LDO_PD BIT(1) +#define AD4080_PWR_CTRL_INTF_LDO_PD BIT(0) + +/* AD4080_REG_GPIO_CONFIG_A Bit Definition */ +#define AD4080_GPIO_CONFIG_A_GPO_1_EN BIT(1) +#define AD4080_GPIO_CONFIG_A_GPO_0_EN BIT(0) + +/* AD4080_REG_GPIO_CONFIG_B Bit Definition */ +#define AD4080_GPIO_CONFIG_B_GPIO_1_SEL_MSK GENMASK(7, 4) +#define AD4080_GPIO_CONFIG_B_GPIO_0_SEL_MSK GENMASK(3, 0) +#define AD4080_GPIO_CONFIG_B_GPIO_SPI_SDO 0 +#define AD4080_GPIO_CONFIG_B_GPIO_FIFO_FULL 1 +#define AD4080_GPIO_CONFIG_B_GPIO_FIFO_READ_DONE 2 +#define AD4080_GPIO_CONFIG_B_GPIO_FILTER_RES_RDY 3 +#define AD4080_GPIO_CONFIG_B_GPIO_H_THRESH 4 +#define AD4080_GPIO_CONFIG_B_GPIO_L_THRESH 5 +#define AD4080_GPIO_CONFIG_B_GPIO_STATUS_ALERT 6 +#define AD4080_GPIO_CONFIG_B_GPIO_GPIO_DATA 7 +#define AD4080_GPIO_CONFIG_B_GPIO_FILTER_SYNC 8 +#define AD4080_GPIO_CONFIG_B_GPIO_EXTERNAL_EVENT 9 + +/* AD4080_REG_FIFO_CONFIG Bit Definition */ +#define AD4080_FIFO_CONFIG_FIFO_MODE_MSK GENMASK(1, 0) + +/* AD4080_REG_FILTER_CONFIG Bit Definition */ +#define AD4080_FILTER_CONFIG_SINC_DEC_RATE_MSK GENMASK(6, 3) +#define AD4080_FILTER_CONFIG_FILTER_SEL_MSK GENMASK(1, 0) + +/* Miscellaneous Definitions */ +#define AD4080_SPI_READ BIT(7) +#define AD4080_CHIP_ID 0x0050 +#define AD4081_CHIP_ID 0x0051 +#define AD4082_CHIP_ID 0x0052 +#define AD4083_CHIP_ID 0x0053 +#define AD4084_CHIP_ID 0x0054 +#define AD4085_CHIP_ID 0x0055 +#define AD4086_CHIP_ID 0x0056 +#define AD4087_CHIP_ID 0x0057 +#define AD4088_CHIP_ID 0x0058 +#define AD4880_CHIP_ID 0x0059 +#define AD4883_CHIP_ID 0x005B +#define AD4884_CHIP_ID 0x005C + +#define AD4080_MAX_CHANNELS 2 + +#define AD4080_LVDS_CNV_CLK_CNT_MAX 7 + +#define AD4080_MAX_SAMP_FREQ 40000000 +#define AD4080_MIN_SAMP_FREQ 1250000 + +enum ad4080_filter_type { + FILTER_NONE, + SINC_1, + SINC_5, + SINC_5_COMP +}; + +static const unsigned int ad4080_scale_table[][2] = { + { 6000, 0 }, +}; + +static const char *const ad4080_filter_type_iio_enum[] = { + [FILTER_NONE] = "none", + [SINC_1] = "sinc1", + [SINC_5] = "sinc5", + [SINC_5_COMP] = "sinc5+pf1", +}; + +static const int ad4080_dec_rate_avail[] = { + 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, +}; + +static const int ad4080_dec_rate_none[] = { 1 }; + +static const char * const ad4080_power_supplies[] = { + "vdd33", "vdd11", "vddldo", "iovdd", "vrefin", +}; + +struct ad4080_chip_info { + const char *name; + unsigned int product_id; + int num_scales; + const unsigned int (*scale_table)[2]; + const struct iio_chan_spec *channels; + unsigned int num_channels; + unsigned int lvds_cnv_clk_cnt_max; +}; + +struct ad4080_state { + struct spi_device *spi[AD4080_MAX_CHANNELS]; + struct regmap *regmap[AD4080_MAX_CHANNELS]; + struct iio_backend *back[AD4080_MAX_CHANNELS]; + const struct ad4080_chip_info *info; + /* + * Synchronize access to members the of driver state, and ensure + * atomicity of consecutive regmap operations. + */ + struct mutex lock; + unsigned int num_lanes; + unsigned long clk_rate; + enum ad4080_filter_type filter_type[AD4080_MAX_CHANNELS]; + bool lvds_cnv_en; +}; + +static const struct regmap_config ad4080_regmap_config = { + .reg_bits = 16, + .val_bits = 8, + .read_flag_mask = BIT(7), + .max_register = 0x29, +}; + +static int ad4080_reg_access(struct iio_dev *indio_dev, unsigned int reg, + unsigned int writeval, unsigned int *readval) +{ + struct ad4080_state *st = iio_priv(indio_dev); + + if (readval) + return regmap_read(st->regmap[0], reg, readval); + + return regmap_write(st->regmap[0], reg, writeval); +} + +static int ad4080_get_scale(struct ad4080_state *st, int *val, int *val2) +{ + unsigned int tmp; + + tmp = (st->info->scale_table[0][0] * 1000000ULL) >> + st->info->channels[0].scan_type.realbits; + *val = tmp / 1000000; + *val2 = tmp % 1000000; + + return IIO_VAL_INT_PLUS_NANO; +} + +static unsigned int ad4080_get_dec_rate(struct iio_dev *dev, + const struct iio_chan_spec *chan) +{ + struct ad4080_state *st = iio_priv(dev); + int ret; + unsigned int data; + unsigned int ch = chan->channel; + + ret = regmap_read(st->regmap[ch], AD4080_REG_FILTER_CONFIG, &data); + if (ret) + return ret; + + return 1 << (FIELD_GET(AD4080_FILTER_CONFIG_SINC_DEC_RATE_MSK, data) + 1); +} + +static int ad4080_set_dec_rate(struct iio_dev *dev, + const struct iio_chan_spec *chan, + unsigned int mode) +{ + struct ad4080_state *st = iio_priv(dev); + unsigned int ch = chan->channel; + + guard(mutex)(&st->lock); + + if ((st->filter_type[ch] >= SINC_5 && mode >= 512) || mode < 2) + return -EINVAL; + + return regmap_update_bits(st->regmap[ch], AD4080_REG_FILTER_CONFIG, + AD4080_FILTER_CONFIG_SINC_DEC_RATE_MSK, + FIELD_PREP(AD4080_FILTER_CONFIG_SINC_DEC_RATE_MSK, + (ilog2(mode) - 1))); +} + +static int ad4080_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long m) +{ + struct ad4080_state *st = iio_priv(indio_dev); + int dec_rate; + + switch (m) { + case IIO_CHAN_INFO_SCALE: + return ad4080_get_scale(st, val, val2); + case IIO_CHAN_INFO_SAMP_FREQ: + dec_rate = ad4080_get_dec_rate(indio_dev, chan); + if (dec_rate < 0) + return dec_rate; + if (st->filter_type[chan->channel] == SINC_5_COMP) + dec_rate *= 2; + if (st->filter_type[chan->channel]) + *val = DIV_ROUND_CLOSEST(st->clk_rate, dec_rate); + else + *val = st->clk_rate; + return IIO_VAL_INT; + case IIO_CHAN_INFO_OVERSAMPLING_RATIO: + if (st->filter_type[chan->channel] == FILTER_NONE) { + *val = 1; + } else { + *val = ad4080_get_dec_rate(indio_dev, chan); + if (*val < 0) + return *val; + } + return IIO_VAL_INT; + default: + return -EINVAL; + } +} + +static int ad4080_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, int val2, long mask) +{ + struct ad4080_state *st = iio_priv(indio_dev); + + switch (mask) { + case IIO_CHAN_INFO_OVERSAMPLING_RATIO: + if (st->filter_type[chan->channel] == FILTER_NONE && val > 1) + return -EINVAL; + + return ad4080_set_dec_rate(indio_dev, chan, val); + default: + return -EINVAL; + } +} + +static int ad4080_lvds_sync_write(struct ad4080_state *st, unsigned int ch) +{ + struct device *dev = regmap_get_device(st->regmap[ch]); + int ret; + + ret = regmap_set_bits(st->regmap[ch], AD4080_REG_ADC_DATA_INTF_CONFIG_A, + AD4080_ADC_DATA_INTF_CONFIG_A_INTF_CHK_EN); + if (ret) + return ret; + + ret = iio_backend_interface_data_align(st->back[ch], 10000); + if (ret) + return dev_err_probe(dev, ret, + "Data alignment process failed\n"); + + dev_dbg(dev, "Success: Pattern correct and Locked!\n"); + return regmap_clear_bits(st->regmap[ch], AD4080_REG_ADC_DATA_INTF_CONFIG_A, + AD4080_ADC_DATA_INTF_CONFIG_A_INTF_CHK_EN); +} + +static int ad4080_get_filter_type(struct iio_dev *dev, + const struct iio_chan_spec *chan) +{ + struct ad4080_state *st = iio_priv(dev); + unsigned int data; + unsigned int ch = chan->channel; + int ret; + + ret = regmap_read(st->regmap[ch], AD4080_REG_FILTER_CONFIG, &data); + if (ret) + return ret; + + return FIELD_GET(AD4080_FILTER_CONFIG_FILTER_SEL_MSK, data); +} + +static int ad4080_set_filter_type(struct iio_dev *dev, + const struct iio_chan_spec *chan, + unsigned int mode) +{ + struct ad4080_state *st = iio_priv(dev); + unsigned int ch = chan->channel; + int dec_rate; + int ret; + + guard(mutex)(&st->lock); + + dec_rate = ad4080_get_dec_rate(dev, chan); + if (dec_rate < 0) + return dec_rate; + + if (mode >= SINC_5 && dec_rate >= 512) + return -EINVAL; + + ret = iio_backend_filter_type_set(st->back[ch], mode); + if (ret) + return ret; + + ret = regmap_update_bits(st->regmap[ch], AD4080_REG_FILTER_CONFIG, + AD4080_FILTER_CONFIG_FILTER_SEL_MSK, + FIELD_PREP(AD4080_FILTER_CONFIG_FILTER_SEL_MSK, + mode)); + if (ret) + return ret; + + st->filter_type[ch] = mode; + + return 0; +} + +static int ad4080_read_avail(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + const int **vals, int *type, int *length, + long mask) +{ + struct ad4080_state *st = iio_priv(indio_dev); + + switch (mask) { + case IIO_CHAN_INFO_OVERSAMPLING_RATIO: + switch (st->filter_type[chan->channel]) { + case FILTER_NONE: + *vals = ad4080_dec_rate_none; + *length = ARRAY_SIZE(ad4080_dec_rate_none); + break; + default: + *vals = ad4080_dec_rate_avail; + *length = st->filter_type[chan->channel] >= SINC_5 ? + (ARRAY_SIZE(ad4080_dec_rate_avail) - 2) : + ARRAY_SIZE(ad4080_dec_rate_avail); + break; + } + *type = IIO_VAL_INT; + return IIO_AVAIL_LIST; + default: + return -EINVAL; + } +} + +static int ad4880_update_scan_mode(struct iio_dev *indio_dev, + const unsigned long *scan_mask) +{ + struct ad4080_state *st = iio_priv(indio_dev); + int ret; + + for (unsigned int ch = 0; ch < st->info->num_channels; ch++) { + /* + * Each backend has a single channel (channel 0 from the + * backend's perspective), so always use channel index 0. + */ + if (test_bit(ch, scan_mask)) + ret = iio_backend_chan_enable(st->back[ch], 0); + else + ret = iio_backend_chan_disable(st->back[ch], 0); + if (ret) + return ret; + } + + return 0; +} + +static const struct iio_info ad4080_iio_info = { + .debugfs_reg_access = ad4080_reg_access, + .read_raw = ad4080_read_raw, + .write_raw = ad4080_write_raw, + .read_avail = ad4080_read_avail, +}; + +/* + * AD4880 needs update_scan_mode to enable/disable individual backend channels. + * Single-channel devices don't need this as their backends may not implement + * chan_enable/chan_disable operations. + */ +static const struct iio_info ad4880_iio_info = { + .debugfs_reg_access = ad4080_reg_access, + .read_raw = ad4080_read_raw, + .write_raw = ad4080_write_raw, + .read_avail = ad4080_read_avail, + .update_scan_mode = ad4880_update_scan_mode, +}; + +static const struct iio_enum ad4080_filter_type_enum = { + .items = ad4080_filter_type_iio_enum, + .num_items = ARRAY_SIZE(ad4080_filter_type_iio_enum), + .set = ad4080_set_filter_type, + .get = ad4080_get_filter_type, +}; + +static struct iio_chan_spec_ext_info ad4080_ext_info[] = { + IIO_ENUM("filter_type", IIO_SHARED_BY_ALL, &ad4080_filter_type_enum), + IIO_ENUM_AVAILABLE("filter_type", IIO_SHARED_BY_ALL, + &ad4080_filter_type_enum), + { } +}; + +/* + * AD4880 needs per-channel filter configuration since each channel has + * its own independent ADC with separate SPI interface. + */ +static struct iio_chan_spec_ext_info ad4880_ext_info[] = { + IIO_ENUM("filter_type", IIO_SEPARATE, &ad4080_filter_type_enum), + IIO_ENUM_AVAILABLE("filter_type", IIO_SEPARATE, + &ad4080_filter_type_enum), + { } +}; + +#define AD4080_CHANNEL_DEFINE(bits, storage, idx) { \ + .type = IIO_VOLTAGE, \ + .indexed = 1, \ + .channel = (idx), \ + .info_mask_separate = BIT(IIO_CHAN_INFO_SCALE), \ + .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ) | \ + BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \ + .info_mask_shared_by_all_available = \ + BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \ + .ext_info = ad4080_ext_info, \ + .scan_index = (idx), \ + .scan_type = { \ + .sign = 's', \ + .realbits = (bits), \ + .storagebits = (storage), \ + }, \ +} + +/* + * AD4880 has per-channel attributes (filter_type, oversampling_ratio, + * sampling_frequency) since each channel has its own independent ADC + * with separate SPI configuration interface. + */ +#define AD4880_CHANNEL_DEFINE(bits, storage, idx) { \ + .type = IIO_VOLTAGE, \ + .indexed = 1, \ + .channel = (idx), \ + .info_mask_separate = BIT(IIO_CHAN_INFO_SCALE) | \ + BIT(IIO_CHAN_INFO_SAMP_FREQ) | \ + BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \ + .info_mask_separate_available = \ + BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \ + .ext_info = ad4880_ext_info, \ + .scan_index = (idx), \ + .scan_type = { \ + .sign = 's', \ + .realbits = (bits), \ + .storagebits = (storage), \ + }, \ +} + +static const struct iio_chan_spec ad4080_channel = AD4080_CHANNEL_DEFINE(20, 32, 0); + +static const struct iio_chan_spec ad4081_channel = AD4080_CHANNEL_DEFINE(20, 32, 0); + +static const struct iio_chan_spec ad4082_channel = AD4080_CHANNEL_DEFINE(20, 32, 0); + +static const struct iio_chan_spec ad4083_channel = AD4080_CHANNEL_DEFINE(16, 16, 0); + +static const struct iio_chan_spec ad4084_channel = AD4080_CHANNEL_DEFINE(16, 16, 0); + +static const struct iio_chan_spec ad4085_channel = AD4080_CHANNEL_DEFINE(16, 16, 0); + +static const struct iio_chan_spec ad4086_channel = AD4080_CHANNEL_DEFINE(14, 16, 0); + +static const struct iio_chan_spec ad4087_channel = AD4080_CHANNEL_DEFINE(14, 16, 0); + +static const struct iio_chan_spec ad4088_channel = AD4080_CHANNEL_DEFINE(14, 16, 0); + +static const struct iio_chan_spec ad4880_channels[] = { + AD4880_CHANNEL_DEFINE(20, 32, 0), + AD4880_CHANNEL_DEFINE(20, 32, 1), +}; + +static const struct iio_chan_spec ad4883_channels[] = { + AD4880_CHANNEL_DEFINE(16, 16, 0), + AD4880_CHANNEL_DEFINE(16, 16, 1), +}; + +static const struct iio_chan_spec ad4884_channels[] = { + AD4880_CHANNEL_DEFINE(16, 16, 0), + AD4880_CHANNEL_DEFINE(16, 16, 1), +}; + +static const struct ad4080_chip_info ad4080_chip_info = { + .name = "ad4080", + .product_id = AD4080_CHIP_ID, + .scale_table = ad4080_scale_table, + .num_scales = ARRAY_SIZE(ad4080_scale_table), + .num_channels = 1, + .channels = &ad4080_channel, + .lvds_cnv_clk_cnt_max = AD4080_LVDS_CNV_CLK_CNT_MAX, +}; + +static const struct ad4080_chip_info ad4081_chip_info = { + .name = "ad4081", + .product_id = AD4081_CHIP_ID, + .scale_table = ad4080_scale_table, + .num_scales = ARRAY_SIZE(ad4080_scale_table), + .num_channels = 1, + .channels = &ad4081_channel, + .lvds_cnv_clk_cnt_max = 2, +}; + +static const struct ad4080_chip_info ad4082_chip_info = { + .name = "ad4082", + .product_id = AD4082_CHIP_ID, + .scale_table = ad4080_scale_table, + .num_scales = ARRAY_SIZE(ad4080_scale_table), + .num_channels = 1, + .channels = &ad4082_channel, + .lvds_cnv_clk_cnt_max = 8, +}; + +static const struct ad4080_chip_info ad4083_chip_info = { + .name = "ad4083", + .product_id = AD4083_CHIP_ID, + .scale_table = ad4080_scale_table, + .num_scales = ARRAY_SIZE(ad4080_scale_table), + .num_channels = 1, + .channels = &ad4083_channel, + .lvds_cnv_clk_cnt_max = 5, +}; + +static const struct ad4080_chip_info ad4084_chip_info = { + .name = "ad4084", + .product_id = AD4084_CHIP_ID, + .scale_table = ad4080_scale_table, + .num_scales = ARRAY_SIZE(ad4080_scale_table), + .num_channels = 1, + .channels = &ad4084_channel, + .lvds_cnv_clk_cnt_max = 2, +}; + +static const struct ad4080_chip_info ad4085_chip_info = { + .name = "ad4085", + .product_id = AD4085_CHIP_ID, + .scale_table = ad4080_scale_table, + .num_scales = ARRAY_SIZE(ad4080_scale_table), + .num_channels = 1, + .channels = &ad4085_channel, + .lvds_cnv_clk_cnt_max = 8, +}; + +static const struct ad4080_chip_info ad4086_chip_info = { + .name = "ad4086", + .product_id = AD4086_CHIP_ID, + .scale_table = ad4080_scale_table, + .num_scales = ARRAY_SIZE(ad4080_scale_table), + .num_channels = 1, + .channels = &ad4086_channel, + .lvds_cnv_clk_cnt_max = 4, +}; + +static const struct ad4080_chip_info ad4087_chip_info = { + .name = "ad4087", + .product_id = AD4087_CHIP_ID, + .scale_table = ad4080_scale_table, + .num_scales = ARRAY_SIZE(ad4080_scale_table), + .num_channels = 1, + .channels = &ad4087_channel, + .lvds_cnv_clk_cnt_max = 1, +}; + +static const struct ad4080_chip_info ad4088_chip_info = { + .name = "ad4088", + .product_id = AD4088_CHIP_ID, + .scale_table = ad4080_scale_table, + .num_scales = ARRAY_SIZE(ad4080_scale_table), + .num_channels = 1, + .channels = &ad4088_channel, + .lvds_cnv_clk_cnt_max = 8, +}; + +static const struct ad4080_chip_info ad4880_chip_info = { + .name = "ad4880", + .product_id = AD4880_CHIP_ID, + .scale_table = ad4080_scale_table, + .num_scales = ARRAY_SIZE(ad4080_scale_table), + .num_channels = 2, + .channels = ad4880_channels, + .lvds_cnv_clk_cnt_max = AD4080_LVDS_CNV_CLK_CNT_MAX, +}; + +static const struct ad4080_chip_info ad4883_chip_info = { + .name = "ad4883", + .product_id = AD4883_CHIP_ID, + .scale_table = ad4080_scale_table, + .num_scales = ARRAY_SIZE(ad4080_scale_table), + .num_channels = 2, + .channels = ad4883_channels, + .lvds_cnv_clk_cnt_max = 5, +}; + +static const struct ad4080_chip_info ad4884_chip_info = { + .name = "ad4884", + .product_id = AD4884_CHIP_ID, + .scale_table = ad4080_scale_table, + .num_scales = ARRAY_SIZE(ad4080_scale_table), + .num_channels = 2, + .channels = ad4884_channels, + .lvds_cnv_clk_cnt_max = 2, +}; + +static int ad4080_setup_channel(struct ad4080_state *st, unsigned int ch) +{ + struct device *dev = regmap_get_device(st->regmap[ch]); + __le16 id_le; + u16 id; + int ret; + + ret = regmap_write(st->regmap[ch], AD4080_REG_INTERFACE_CONFIG_A, + AD4080_INTERFACE_CONFIG_A_SW_RESET); + if (ret) + return ret; + + ret = regmap_write(st->regmap[ch], AD4080_REG_INTERFACE_CONFIG_A, + AD4080_INTERFACE_CONFIG_A_SDO_ENABLE); + if (ret) + return ret; + + ret = regmap_bulk_read(st->regmap[ch], AD4080_REG_PRODUCT_ID_L, &id_le, + sizeof(id_le)); + if (ret) + return ret; + + id = le16_to_cpu(id_le); + if (id != st->info->product_id) + dev_info(dev, "Unrecognized CHIP_ID 0x%X\n", id); + + ret = regmap_set_bits(st->regmap[ch], AD4080_REG_GPIO_CONFIG_A, + AD4080_GPIO_CONFIG_A_GPO_1_EN); + if (ret) + return ret; + + ret = regmap_write(st->regmap[ch], AD4080_REG_GPIO_CONFIG_B, + FIELD_PREP(AD4080_GPIO_CONFIG_B_GPIO_1_SEL_MSK, + AD4080_GPIO_CONFIG_B_GPIO_FILTER_RES_RDY)); + if (ret) + return ret; + + ret = iio_backend_num_lanes_set(st->back[ch], st->num_lanes); + if (ret) + return ret; + + ret = iio_backend_data_size_set(st->back[ch], + st->info->channels[0].scan_type.realbits); + if (ret) + return ret; + + if (!st->lvds_cnv_en) + return 0; + + /* Set maximum LVDS Data Transfer Latency */ + ret = regmap_update_bits(st->regmap[ch], + AD4080_REG_ADC_DATA_INTF_CONFIG_B, + AD4080_ADC_DATA_INTF_CONFIG_B_LVDS_CNV_CLK_CNT_MSK, + FIELD_PREP(AD4080_ADC_DATA_INTF_CONFIG_B_LVDS_CNV_CLK_CNT_MSK, + st->info->lvds_cnv_clk_cnt_max)); + if (ret) + return ret; + + if (st->num_lanes > 1) { + ret = regmap_set_bits(st->regmap[ch], AD4080_REG_ADC_DATA_INTF_CONFIG_A, + AD4080_ADC_DATA_INTF_CONFIG_A_SPI_LVDS_LANES); + if (ret) + return ret; + } + + ret = regmap_set_bits(st->regmap[ch], + AD4080_REG_ADC_DATA_INTF_CONFIG_B, + AD4080_ADC_DATA_INTF_CONFIG_B_LVDS_CNV_EN); + if (ret) + return ret; + + return ad4080_lvds_sync_write(st, ch); +} + +static int ad4080_setup(struct iio_dev *indio_dev) +{ + struct ad4080_state *st = iio_priv(indio_dev); + int ret; + + for (unsigned int ch = 0; ch < st->info->num_channels; ch++) { + ret = ad4080_setup_channel(st, ch); + if (ret) + return ret; + } + + return 0; +} + +static int ad4080_properties_parse(struct ad4080_state *st, + struct device *dev) +{ + + st->lvds_cnv_en = device_property_read_bool(dev, "adi,lvds-cnv-enable"); + + st->num_lanes = 1; + device_property_read_u32(dev, "adi,num-lanes", &st->num_lanes); + if (!st->num_lanes || st->num_lanes > 2) + return dev_err_probe(dev, -EINVAL, + "Invalid 'adi,num-lanes' value: %u", + st->num_lanes); + + return 0; +} + +static int ad4080_probe(struct spi_device *spi) +{ + struct iio_dev *indio_dev; + struct device *dev = &spi->dev; + struct ad4080_state *st; + struct clk *clk; + int ret; + + indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st)); + if (!indio_dev) + return -ENOMEM; + + st = iio_priv(indio_dev); + + ret = devm_regulator_bulk_get_enable(dev, + ARRAY_SIZE(ad4080_power_supplies), + ad4080_power_supplies); + if (ret) + return dev_err_probe(dev, ret, + "failed to get and enable supplies\n"); + + /* Setup primary SPI device (channel 0) */ + st->spi[0] = spi; + st->regmap[0] = devm_regmap_init_spi(spi, &ad4080_regmap_config); + if (IS_ERR(st->regmap[0])) + return PTR_ERR(st->regmap[0]); + + st->info = spi_get_device_match_data(spi); + if (!st->info) + return -ENODEV; + + /* Setup ancillary SPI devices for additional channels */ + for (unsigned int ch = 1; ch < st->info->num_channels; ch++) { + st->spi[ch] = devm_spi_new_ancillary_device(spi, spi_get_chipselect(spi, ch)); + if (IS_ERR(st->spi[ch])) + return dev_err_probe(dev, PTR_ERR(st->spi[ch]), + "failed to register ancillary device\n"); + + st->regmap[ch] = devm_regmap_init_spi(st->spi[ch], &ad4080_regmap_config); + if (IS_ERR(st->regmap[ch])) + return PTR_ERR(st->regmap[ch]); + } + + ret = devm_mutex_init(dev, &st->lock); + if (ret) + return ret; + + indio_dev->name = st->info->name; + indio_dev->channels = st->info->channels; + indio_dev->num_channels = st->info->num_channels; + indio_dev->info = st->info->num_channels > 1 ? + &ad4880_iio_info : &ad4080_iio_info; + + ret = ad4080_properties_parse(st, dev); + if (ret) + return ret; + + clk = devm_clk_get_enabled(&spi->dev, "cnv"); + if (IS_ERR(clk)) + return PTR_ERR(clk); + + st->clk_rate = clk_get_rate(clk); + + /* Get backends for all channels */ + for (unsigned int ch = 0; ch < st->info->num_channels; ch++) { + st->back[ch] = devm_iio_backend_get_by_index(dev, ch); + if (IS_ERR(st->back[ch])) + return PTR_ERR(st->back[ch]); + + ret = devm_iio_backend_enable(dev, st->back[ch]); + if (ret) + return ret; + } + + /* + * Request buffer from the first backend only. For multi-channel + * devices (e.g., AD4880), the FPGA uses two axi_ad408x IP instances + * (one per ADC channel) whose outputs are combined by a packer block + * that interleaves all channel data into a single DMA stream routed + * through the first backend's clock domain. + */ + ret = devm_iio_backend_request_buffer(dev, st->back[0], indio_dev); + if (ret) + return ret; + + ret = ad4080_setup(indio_dev); + if (ret) + return ret; + + return devm_iio_device_register(&spi->dev, indio_dev); +} + +static const struct spi_device_id ad4080_id[] = { + { .name = "ad4080", .driver_data = (kernel_ulong_t)&ad4080_chip_info }, + { .name = "ad4081", .driver_data = (kernel_ulong_t)&ad4081_chip_info }, + { .name = "ad4082", .driver_data = (kernel_ulong_t)&ad4082_chip_info }, + { .name = "ad4083", .driver_data = (kernel_ulong_t)&ad4083_chip_info }, + { .name = "ad4084", .driver_data = (kernel_ulong_t)&ad4084_chip_info }, + { .name = "ad4085", .driver_data = (kernel_ulong_t)&ad4085_chip_info }, + { .name = "ad4086", .driver_data = (kernel_ulong_t)&ad4086_chip_info }, + { .name = "ad4087", .driver_data = (kernel_ulong_t)&ad4087_chip_info }, + { .name = "ad4088", .driver_data = (kernel_ulong_t)&ad4088_chip_info }, + { .name = "ad4880", .driver_data = (kernel_ulong_t)&ad4880_chip_info }, + { .name = "ad4883", .driver_data = (kernel_ulong_t)&ad4883_chip_info }, + { .name = "ad4884", .driver_data = (kernel_ulong_t)&ad4884_chip_info }, + { } +}; +MODULE_DEVICE_TABLE(spi, ad4080_id); + +static const struct of_device_id ad4080_of_match[] = { + { .compatible = "adi,ad4080", &ad4080_chip_info }, + { .compatible = "adi,ad4081", &ad4081_chip_info }, + { .compatible = "adi,ad4082", &ad4082_chip_info }, + { .compatible = "adi,ad4083", &ad4083_chip_info }, + { .compatible = "adi,ad4084", &ad4084_chip_info }, + { .compatible = "adi,ad4085", &ad4085_chip_info }, + { .compatible = "adi,ad4086", &ad4086_chip_info }, + { .compatible = "adi,ad4087", &ad4087_chip_info }, + { .compatible = "adi,ad4088", &ad4088_chip_info }, + { .compatible = "adi,ad4880", &ad4880_chip_info }, + { .compatible = "adi,ad4883", &ad4883_chip_info }, + { .compatible = "adi,ad4884", &ad4884_chip_info }, + { } +}; +MODULE_DEVICE_TABLE(of, ad4080_of_match); + +static struct spi_driver ad4080_driver = { + .driver = { + .name = "ad4080", + .of_match_table = ad4080_of_match, + }, + .probe = ad4080_probe, + .id_table = ad4080_id, +}; +module_spi_driver(ad4080_driver); + +MODULE_AUTHOR("Antoniu Miclaus + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#define AD4130_NAME "ad4130" + +#define AD4130_COMMS_READ_MASK BIT(6) + +#define AD4130_STATUS_REG 0x00 + +#define AD4130_ADC_CONTROL_REG 0x01 +#define AD4130_ADC_CONTROL_BIPOLAR_MASK BIT(14) +#define AD4130_ADC_CONTROL_INT_REF_VAL_MASK BIT(13) +#define AD4130_ADC_CONTROL_CONT_READ_MASK BIT(11) +#define AD4130_INT_REF_2_5V 2500000 +#define AD4130_INT_REF_1_25V 1250000 +#define AD4130_ADC_CONTROL_CSB_EN_MASK BIT(9) +#define AD4130_ADC_CONTROL_INT_REF_EN_MASK BIT(8) +#define AD4130_ADC_CONTROL_MODE_MASK GENMASK(5, 2) +#define AD4130_ADC_CONTROL_MCLK_SEL_MASK GENMASK(1, 0) +#define AD4130_MCLK_FREQ_76_8KHZ 76800 +#define AD4130_MCLK_FREQ_153_6KHZ 153600 + +#define AD4130_DATA_REG 0x02 + +#define AD4130_IO_CONTROL_REG 0x03 +#define AD4130_IO_CONTROL_INT_PIN_SEL_MASK GENMASK(9, 8) +#define AD4130_IO_CONTROL_GPIO_DATA_MASK GENMASK(7, 4) +#define AD4130_4_IO_CONTROL_GPIO_DATA_MASK GENMASK(7, 6) +#define AD4130_IO_CONTROL_GPIO_CTRL_MASK GENMASK(3, 0) +#define AD4130_4_IO_CONTROL_GPIO_CTRL_MASK GENMASK(3, 2) + +#define AD4130_VBIAS_REG 0x04 + +#define AD4130_ID_REG 0x05 + +#define AD4130_ERROR_REG 0x06 + +#define AD4130_ERROR_EN_REG 0x07 + +#define AD4130_MCLK_COUNT_REG 0x08 + +#define AD4130_CHANNEL_X_REG(x) (0x09 + (x)) +#define AD4130_CHANNEL_EN_MASK BIT(23) +#define AD4130_CHANNEL_SETUP_MASK GENMASK(22, 20) +#define AD4130_CHANNEL_AINP_MASK GENMASK(17, 13) +#define AD4130_CHANNEL_AINM_MASK GENMASK(12, 8) +#define AD4130_CHANNEL_IOUT1_MASK GENMASK(7, 4) +#define AD4130_CHANNEL_IOUT2_MASK GENMASK(3, 0) + +#define AD4130_CONFIG_X_REG(x) (0x19 + (x)) +#define AD4130_CONFIG_IOUT1_VAL_MASK GENMASK(15, 13) +#define AD4130_CONFIG_IOUT2_VAL_MASK GENMASK(12, 10) +#define AD4130_CONFIG_BURNOUT_MASK GENMASK(9, 8) +#define AD4130_CONFIG_REF_BUFP_MASK BIT(7) +#define AD4130_CONFIG_REF_BUFM_MASK BIT(6) +#define AD4130_CONFIG_REF_SEL_MASK GENMASK(5, 4) +#define AD4130_CONFIG_PGA_MASK GENMASK(3, 1) + +#define AD4130_FILTER_X_REG(x) (0x21 + (x)) +#define AD4130_FILTER_MODE_MASK GENMASK(15, 12) +#define AD4130_FILTER_SELECT_MASK GENMASK(10, 0) +#define AD4130_FILTER_SELECT_MIN 1 + +#define AD4130_OFFSET_X_REG(x) (0x29 + (x)) + +#define AD4130_GAIN_X_REG(x) (0x31 + (x)) + +#define AD4130_MISC_REG 0x39 + +#define AD4130_FIFO_CONTROL_REG 0x3a +#define AD4130_FIFO_CONTROL_HEADER_MASK BIT(18) +#define AD4130_FIFO_CONTROL_MODE_MASK GENMASK(17, 16) +#define AD4130_FIFO_CONTROL_WM_INT_EN_MASK BIT(9) +#define AD4130_FIFO_CONTROL_WM_MASK GENMASK(7, 0) +#define AD4130_WATERMARK_256 0 + +#define AD4130_FIFO_STATUS_REG 0x3b + +#define AD4130_FIFO_THRESHOLD_REG 0x3c + +#define AD4130_FIFO_DATA_REG 0x3d +#define AD4130_FIFO_SIZE 256 +#define AD4130_FIFO_MAX_SAMPLE_SIZE 3 + +#define AD4130_MAX_ANALOG_PINS 16 +#define AD4130_MAX_CHANNELS 16 +#define AD4130_MAX_DIFF_INPUTS 30 +#define AD4130_MAX_GPIOS 4 +#define AD4130_MAX_ODR 2400 +#define AD4130_MAX_PGA 8 +#define AD4130_MAX_SETUPS 8 + +#define AD4130_AIN2_P1 0x2 +#define AD4130_AIN3_P2 0x3 + +#define AD4130_RESET_BUF_SIZE 8 +#define AD4130_RESET_SLEEP_US (160 * MICRO / AD4130_MCLK_FREQ_76_8KHZ) + +#define AD4130_INVALID_SLOT -1 + +static const unsigned int ad4129_reg_size[] = { + [AD4130_STATUS_REG] = 1, + [AD4130_ADC_CONTROL_REG] = 2, + [AD4130_DATA_REG] = 2, + [AD4130_IO_CONTROL_REG] = 2, + [AD4130_VBIAS_REG] = 2, + [AD4130_ID_REG] = 1, + [AD4130_ERROR_REG] = 2, + [AD4130_ERROR_EN_REG] = 2, + [AD4130_MCLK_COUNT_REG] = 1, + [AD4130_CHANNEL_X_REG(0) ... AD4130_CHANNEL_X_REG(AD4130_MAX_CHANNELS - 1)] = 3, + [AD4130_CONFIG_X_REG(0) ... AD4130_CONFIG_X_REG(AD4130_MAX_SETUPS - 1)] = 2, + [AD4130_FILTER_X_REG(0) ... AD4130_FILTER_X_REG(AD4130_MAX_SETUPS - 1)] = 3, + [AD4130_OFFSET_X_REG(0) ... AD4130_OFFSET_X_REG(AD4130_MAX_SETUPS - 1)] = 2, + [AD4130_GAIN_X_REG(0) ... AD4130_GAIN_X_REG(AD4130_MAX_SETUPS - 1)] = 2, + [AD4130_MISC_REG] = 2, + [AD4130_FIFO_CONTROL_REG] = 3, + [AD4130_FIFO_STATUS_REG] = 1, + [AD4130_FIFO_THRESHOLD_REG] = 3, + [AD4130_FIFO_DATA_REG] = 2, +}; + +static const unsigned int ad4130_reg_size[] = { + [AD4130_STATUS_REG] = 1, + [AD4130_ADC_CONTROL_REG] = 2, + [AD4130_DATA_REG] = 3, + [AD4130_IO_CONTROL_REG] = 2, + [AD4130_VBIAS_REG] = 2, + [AD4130_ID_REG] = 1, + [AD4130_ERROR_REG] = 2, + [AD4130_ERROR_EN_REG] = 2, + [AD4130_MCLK_COUNT_REG] = 1, + [AD4130_CHANNEL_X_REG(0) ... AD4130_CHANNEL_X_REG(AD4130_MAX_CHANNELS - 1)] = 3, + [AD4130_CONFIG_X_REG(0) ... AD4130_CONFIG_X_REG(AD4130_MAX_SETUPS - 1)] = 2, + [AD4130_FILTER_X_REG(0) ... AD4130_FILTER_X_REG(AD4130_MAX_SETUPS - 1)] = 3, + [AD4130_OFFSET_X_REG(0) ... AD4130_OFFSET_X_REG(AD4130_MAX_SETUPS - 1)] = 3, + [AD4130_GAIN_X_REG(0) ... AD4130_GAIN_X_REG(AD4130_MAX_SETUPS - 1)] = 3, + [AD4130_MISC_REG] = 2, + [AD4130_FIFO_CONTROL_REG] = 3, + [AD4130_FIFO_STATUS_REG] = 1, + [AD4130_FIFO_THRESHOLD_REG] = 3, + [AD4130_FIFO_DATA_REG] = 3, +}; + +static const unsigned int ad4131_reg_size[] = { + [AD4130_STATUS_REG] = 1, + [AD4130_ADC_CONTROL_REG] = 2, + [AD4130_DATA_REG] = 2, + [AD4130_IO_CONTROL_REG] = 2, + [AD4130_VBIAS_REG] = 2, + [AD4130_ID_REG] = 1, + [AD4130_ERROR_REG] = 2, + [AD4130_ERROR_EN_REG] = 2, + [AD4130_MCLK_COUNT_REG] = 1, + [AD4130_CHANNEL_X_REG(0) ... AD4130_CHANNEL_X_REG(AD4130_MAX_CHANNELS - 1)] = 3, + [AD4130_CONFIG_X_REG(0) ... AD4130_CONFIG_X_REG(AD4130_MAX_SETUPS - 1)] = 2, + [AD4130_FILTER_X_REG(0) ... AD4130_FILTER_X_REG(AD4130_MAX_SETUPS - 1)] = 3, + [AD4130_OFFSET_X_REG(0) ... AD4130_OFFSET_X_REG(AD4130_MAX_SETUPS - 1)] = 2, + [AD4130_GAIN_X_REG(0) ... AD4130_GAIN_X_REG(AD4130_MAX_SETUPS - 1)] = 2, + [AD4130_MISC_REG] = 2, +}; + +enum ad4130_int_ref_val { + AD4130_INT_REF_VAL_2_5V, + AD4130_INT_REF_VAL_1_25V, +}; + +enum ad4130_mclk_sel { + AD4130_MCLK_76_8KHZ, + AD4130_MCLK_76_8KHZ_OUT, + AD4130_MCLK_76_8KHZ_EXT, + AD4130_MCLK_153_6KHZ_EXT, +}; + +enum ad4130_int_pin_sel { + AD4130_INT_PIN_INT, + AD4130_INT_PIN_CLK, + AD4130_INT_PIN_P2, + AD4130_INT_PIN_DOUT, +}; + +enum ad4130_iout { + AD4130_IOUT_OFF, + AD4130_IOUT_10000NA, + AD4130_IOUT_20000NA, + AD4130_IOUT_50000NA, + AD4130_IOUT_100000NA, + AD4130_IOUT_150000NA, + AD4130_IOUT_200000NA, + AD4130_IOUT_100NA, + AD4130_IOUT_MAX +}; + +enum ad4130_burnout { + AD4130_BURNOUT_OFF, + AD4130_BURNOUT_500NA, + AD4130_BURNOUT_2000NA, + AD4130_BURNOUT_4000NA, + AD4130_BURNOUT_MAX +}; + +enum ad4130_ref_sel { + AD4130_REF_REFIN1, + AD4130_REF_REFIN2, + AD4130_REF_REFOUT_AVSS, + AD4130_REF_AVDD_AVSS, + AD4130_REF_SEL_MAX +}; + +enum ad4130_fifo_mode { + AD4130_FIFO_MODE_DISABLED = 0b00, + AD4130_FIFO_MODE_WM = 0b01, +}; + +enum ad4130_mode { + AD4130_MODE_CONTINUOUS = 0b0000, + AD4130_MODE_IDLE = 0b0100, +}; + +enum ad4130_filter_type { + AD4130_FILTER_SINC4, + AD4130_FILTER_SINC4_SINC1, + AD4130_FILTER_SINC3, + AD4130_FILTER_SINC3_REJ60, + AD4130_FILTER_SINC3_SINC1, + AD4130_FILTER_SINC3_PF1, + AD4130_FILTER_SINC3_PF2, + AD4130_FILTER_SINC3_PF3, + AD4130_FILTER_SINC3_PF4, +}; + +enum ad4130_pin_function { + AD4130_PIN_FN_NONE, + AD4130_PIN_FN_SPECIAL = BIT(0), + AD4130_PIN_FN_DIFF = BIT(1), + AD4130_PIN_FN_EXCITATION = BIT(2), + AD4130_PIN_FN_VBIAS = BIT(3), +}; + +/* Pin mapping for AIN0..AIN7, VBIAS_0..VBIAS_7 */ +static const u8 ad4130_4_pin_map[] = { + 0x00, 0x01, 0x04, 0x05, 0x0A, 0x0B, 0x0E, 0x0F, /* 0 - 7 */ +}; + +/* Pin mapping for AIN0..AIN15, VBIAS_0..VBIAS_15 */ +static const u8 ad4130_8_pin_map[] = { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* 0 - 7 */ + 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, /* 8 - 15 */ +}; + +struct ad4130_chip_info { + const char *name; + unsigned int max_analog_pins; + unsigned int num_gpios; + const struct iio_info *info; + const unsigned int *reg_size; + const unsigned int reg_size_length; + const u8 *pin_map; + bool has_fifo; +}; + +/* + * If you make adaptations in this struct, you most likely also have to adapt + * ad4130_setup_info_eq(), too. + */ +struct ad4130_setup_info { + unsigned int iout0_val; + unsigned int iout1_val; + unsigned int burnout; + unsigned int pga; + unsigned int fs; + u32 ref_sel; + enum ad4130_filter_type filter_type; + bool ref_bufp; + bool ref_bufm; +}; + +struct ad4130_slot_info { + struct ad4130_setup_info setup; + unsigned int enabled_channels; + unsigned int channels; +}; + +struct ad4130_chan_info { + struct ad4130_setup_info setup; + u32 iout0; + u32 iout1; + int slot; + bool enabled; + bool initialized; +}; + +struct ad4130_filter_config { + enum ad4130_filter_type filter_type; + unsigned int odr_div; + unsigned int fs_max; + enum iio_available_type samp_freq_avail_type; + int samp_freq_avail_len; + int samp_freq_avail[3][2]; +}; + +struct ad4130_state { + struct regmap *regmap; + struct spi_device *spi; + struct clk *mclk; + const struct ad4130_chip_info *chip_info; + struct regulator_bulk_data regulators[4]; + u32 irq_trigger; + u32 inv_irq_trigger; + + /* + * Synchronize access to members the of driver state, and ensure + * atomicity of consecutive regmap operations. + */ + struct mutex lock; + struct completion completion; + + struct iio_chan_spec chans[AD4130_MAX_CHANNELS]; + struct ad4130_chan_info chans_info[AD4130_MAX_CHANNELS]; + struct ad4130_slot_info slots_info[AD4130_MAX_SETUPS]; + enum ad4130_pin_function pins_fn[AD4130_MAX_ANALOG_PINS]; + u32 vbias_pins[AD4130_MAX_ANALOG_PINS]; + u32 num_vbias_pins; + int scale_tbls[AD4130_REF_SEL_MAX][AD4130_MAX_PGA][2]; + struct gpio_chip gc; + struct clk_hw int_clk_hw; + + u32 int_pin_sel; + u32 int_ref_uv; + u32 mclk_sel; + bool int_ref_en; + bool bipolar; + bool buffer_wait_for_irq; + + unsigned int num_enabled_channels; + unsigned int effective_watermark; + unsigned int watermark; + + struct spi_message fifo_msg; + struct spi_transfer fifo_xfer[2]; + struct iio_trigger *trig; + + /* + * DMA (thus cache coherency maintenance) requires any transfer + * buffers to live in their own cache lines. As the use of these + * buffers is synchronous, all of the buffers used for DMA in this + * driver may share a cache line. + */ + u8 reset_buf[AD4130_RESET_BUF_SIZE] __aligned(IIO_DMA_MINALIGN); + u8 reg_write_tx_buf[4]; + u8 reg_read_tx_buf[1]; + u8 reg_read_rx_buf[3]; + union { + struct { + u8 fifo_tx_buf[2]; + u8 fifo_rx_buf[AD4130_FIFO_SIZE * AD4130_FIFO_MAX_SAMPLE_SIZE]; + }; + IIO_DECLARE_BUFFER_WITH_TS(u32, scan_channels, AD4130_MAX_CHANNELS); + }; +}; + +static const char * const ad4130_int_pin_names[] = { + [AD4130_INT_PIN_INT] = "int", + [AD4130_INT_PIN_CLK] = "clk", + [AD4130_INT_PIN_P2] = "p2", + [AD4130_INT_PIN_DOUT] = "dout", +}; + +static const unsigned int ad4130_iout_current_na_tbl[AD4130_IOUT_MAX] = { + [AD4130_IOUT_OFF] = 0, + [AD4130_IOUT_100NA] = 100, + [AD4130_IOUT_10000NA] = 10000, + [AD4130_IOUT_20000NA] = 20000, + [AD4130_IOUT_50000NA] = 50000, + [AD4130_IOUT_100000NA] = 100000, + [AD4130_IOUT_150000NA] = 150000, + [AD4130_IOUT_200000NA] = 200000, +}; + +static const unsigned int ad4130_burnout_current_na_tbl[AD4130_BURNOUT_MAX] = { + [AD4130_BURNOUT_OFF] = 0, + [AD4130_BURNOUT_500NA] = 500, + [AD4130_BURNOUT_2000NA] = 2000, + [AD4130_BURNOUT_4000NA] = 4000, +}; + +#define AD4130_VARIABLE_ODR_CONFIG(_filter_type, _odr_div, _fs_max) \ +{ \ + .filter_type = (_filter_type), \ + .odr_div = (_odr_div), \ + .fs_max = (_fs_max), \ + .samp_freq_avail_type = IIO_AVAIL_RANGE, \ + .samp_freq_avail = { \ + { AD4130_MAX_ODR, (_odr_div) * (_fs_max) }, \ + { AD4130_MAX_ODR, (_odr_div) * (_fs_max) }, \ + { AD4130_MAX_ODR, (_odr_div) }, \ + }, \ +} + +#define AD4130_FIXED_ODR_CONFIG(_filter_type, _odr_div) \ +{ \ + .filter_type = (_filter_type), \ + .odr_div = (_odr_div), \ + .fs_max = AD4130_FILTER_SELECT_MIN, \ + .samp_freq_avail_type = IIO_AVAIL_LIST, \ + .samp_freq_avail_len = 1, \ + .samp_freq_avail = { \ + { AD4130_MAX_ODR, (_odr_div) }, \ + }, \ +} + +static const struct ad4130_filter_config ad4130_filter_configs[] = { + AD4130_VARIABLE_ODR_CONFIG(AD4130_FILTER_SINC4, 1, 10), + AD4130_VARIABLE_ODR_CONFIG(AD4130_FILTER_SINC4_SINC1, 11, 10), + AD4130_VARIABLE_ODR_CONFIG(AD4130_FILTER_SINC3, 1, 2047), + AD4130_VARIABLE_ODR_CONFIG(AD4130_FILTER_SINC3_REJ60, 1, 2047), + AD4130_VARIABLE_ODR_CONFIG(AD4130_FILTER_SINC3_SINC1, 10, 2047), + AD4130_FIXED_ODR_CONFIG(AD4130_FILTER_SINC3_PF1, 92), + AD4130_FIXED_ODR_CONFIG(AD4130_FILTER_SINC3_PF2, 100), + AD4130_FIXED_ODR_CONFIG(AD4130_FILTER_SINC3_PF3, 124), + AD4130_FIXED_ODR_CONFIG(AD4130_FILTER_SINC3_PF4, 148), +}; + +static const char * const ad4130_filter_types_str[] = { + [AD4130_FILTER_SINC4] = "sinc4", + [AD4130_FILTER_SINC4_SINC1] = "sinc4+sinc1", + [AD4130_FILTER_SINC3] = "sinc3", + [AD4130_FILTER_SINC3_REJ60] = "sinc3+rej60", + [AD4130_FILTER_SINC3_SINC1] = "sinc3+sinc1", + [AD4130_FILTER_SINC3_PF1] = "sinc3+pf1", + [AD4130_FILTER_SINC3_PF2] = "sinc3+pf2", + [AD4130_FILTER_SINC3_PF3] = "sinc3+pf3", + [AD4130_FILTER_SINC3_PF4] = "sinc3+pf4", +}; + +static int ad4130_get_reg_size(struct ad4130_state *st, unsigned int reg, + unsigned int *size) +{ + if (reg >= st->chip_info->reg_size_length) + return -EINVAL; + + *size = st->chip_info->reg_size[reg]; + + return 0; +} + +static unsigned int ad4130_data_reg_size(struct ad4130_state *st) +{ + unsigned int data_reg_size; + int ret; + + ret = ad4130_get_reg_size(st, AD4130_DATA_REG, &data_reg_size); + if (ret) + return 0; + + return data_reg_size; +} + +static unsigned int ad4130_resolution(struct ad4130_state *st) +{ + return ad4130_data_reg_size(st) * BITS_PER_BYTE; +} + +static int ad4130_reg_write(void *context, unsigned int reg, unsigned int val) +{ + struct ad4130_state *st = context; + unsigned int size; + int ret; + + ret = ad4130_get_reg_size(st, reg, &size); + if (ret) + return ret; + + st->reg_write_tx_buf[0] = reg; + + switch (size) { + case 3: + put_unaligned_be24(val, &st->reg_write_tx_buf[1]); + break; + case 2: + put_unaligned_be16(val, &st->reg_write_tx_buf[1]); + break; + case 1: + st->reg_write_tx_buf[1] = val; + break; + default: + return -EINVAL; + } + + return spi_write(st->spi, st->reg_write_tx_buf, size + 1); +} + +static int ad4130_reg_read(void *context, unsigned int reg, unsigned int *val) +{ + struct ad4130_state *st = context; + struct spi_transfer t[] = { + { + .tx_buf = st->reg_read_tx_buf, + .len = sizeof(st->reg_read_tx_buf), + }, + { + .rx_buf = st->reg_read_rx_buf, + }, + }; + unsigned int size; + int ret; + + ret = ad4130_get_reg_size(st, reg, &size); + if (ret) + return ret; + + st->reg_read_tx_buf[0] = AD4130_COMMS_READ_MASK | reg; + t[1].len = size; + + ret = spi_sync_transfer(st->spi, t, ARRAY_SIZE(t)); + if (ret) + return ret; + + switch (size) { + case 3: + *val = get_unaligned_be24(st->reg_read_rx_buf); + break; + case 2: + *val = get_unaligned_be16(st->reg_read_rx_buf); + break; + case 1: + *val = st->reg_read_rx_buf[0]; + break; + default: + return -EINVAL; + } + + return 0; +} + +static const struct regmap_config ad4130_regmap_config = { + .reg_read = ad4130_reg_read, + .reg_write = ad4130_reg_write, +}; + +static int ad4130_gpio_init_valid_mask(struct gpio_chip *gc, + unsigned long *valid_mask, + unsigned int ngpios) +{ + struct ad4130_state *st = gpiochip_get_data(gc); + unsigned int i; + + /* + * Output-only GPIO functionality is available on pins AIN2 through + * AIN5 for some parts and AIN2 through AIN3 for others. If these pins + * are used for anything else, do not expose them. + */ + for (i = 0; i < ngpios; i++) { + unsigned int pin = i + AD4130_AIN2_P1; + bool valid = st->pins_fn[pin] == AD4130_PIN_FN_NONE; + + __assign_bit(i, valid_mask, valid); + } + + return 0; +} + +static int ad4130_gpio_get_direction(struct gpio_chip *gc, unsigned int offset) +{ + return GPIO_LINE_DIRECTION_OUT; +} + +static int ad4130_gpio_set(struct gpio_chip *gc, unsigned int offset, + int value) +{ + struct ad4130_state *st = gpiochip_get_data(gc); + unsigned int mask; + + if (st->chip_info->num_gpios == AD4130_MAX_GPIOS) + mask = FIELD_PREP(AD4130_IO_CONTROL_GPIO_DATA_MASK, + BIT(offset)); + else + mask = FIELD_PREP(AD4130_4_IO_CONTROL_GPIO_DATA_MASK, + BIT(offset)); + + return regmap_update_bits(st->regmap, AD4130_IO_CONTROL_REG, mask, + value ? mask : 0); +} + +static int ad4130_set_mode(struct ad4130_state *st, enum ad4130_mode mode) +{ + return regmap_update_bits(st->regmap, AD4130_ADC_CONTROL_REG, + AD4130_ADC_CONTROL_MODE_MASK, + FIELD_PREP(AD4130_ADC_CONTROL_MODE_MASK, mode)); +} + +static int ad4130_set_watermark_interrupt_en(struct ad4130_state *st, bool en) +{ + return regmap_update_bits(st->regmap, AD4130_FIFO_CONTROL_REG, + AD4130_FIFO_CONTROL_WM_INT_EN_MASK, + FIELD_PREP(AD4130_FIFO_CONTROL_WM_INT_EN_MASK, en)); +} + +static unsigned int ad4130_watermark_reg_val(unsigned int val) +{ + if (val == AD4130_FIFO_SIZE) + val = AD4130_WATERMARK_256; + + return val; +} + +static int ad4130_set_fifo_mode(struct ad4130_state *st, + enum ad4130_fifo_mode mode) +{ + return regmap_update_bits(st->regmap, AD4130_FIFO_CONTROL_REG, + AD4130_FIFO_CONTROL_MODE_MASK, + FIELD_PREP(AD4130_FIFO_CONTROL_MODE_MASK, mode)); +} + +static void ad4130_push_fifo_data(struct iio_dev *indio_dev) +{ + struct ad4130_state *st = iio_priv(indio_dev); + unsigned int data_reg_size = ad4130_data_reg_size(st); + unsigned int transfer_len = st->effective_watermark * data_reg_size; + unsigned int set_size = st->num_enabled_channels * data_reg_size; + unsigned int i; + int ret; + + st->fifo_tx_buf[1] = ad4130_watermark_reg_val(st->effective_watermark); + st->fifo_xfer[1].len = transfer_len; + + ret = spi_sync(st->spi, &st->fifo_msg); + if (ret) + return; + + for (i = 0; i < transfer_len; i += set_size) + iio_push_to_buffers(indio_dev, &st->fifo_rx_buf[i]); +} + +static irqreturn_t ad4130_irq_handler(int irq, void *private) +{ + struct iio_dev *indio_dev = private; + struct ad4130_state *st = iio_priv(indio_dev); + + if (iio_buffer_enabled(indio_dev)) { + if (st->chip_info->has_fifo) + ad4130_push_fifo_data(indio_dev); + else if (st->buffer_wait_for_irq) + complete(&st->completion); + else + iio_trigger_poll(st->trig); + } else { + complete(&st->completion); + } + + return IRQ_HANDLED; +} + +static irqreturn_t ad4130_trigger_handler(int irq, void *p) +{ + struct iio_poll_func *pf = p; + struct iio_dev *indio_dev = pf->indio_dev; + struct ad4130_state *st = iio_priv(indio_dev); + unsigned int data_reg_size = ad4130_data_reg_size(st); + struct spi_transfer xfer = { }; + unsigned int num_en_chn; + int ret; + + num_en_chn = bitmap_weight(indio_dev->active_scan_mask, + iio_get_masklength(indio_dev)); + xfer.rx_buf = st->scan_channels; + xfer.len = data_reg_size * num_en_chn; + ret = spi_sync_transfer(st->spi, &xfer, 1); + if (ret < 0) + goto err_out; + + iio_push_to_buffers_with_timestamp(indio_dev, &st->scan_channels, + iio_get_time_ns(indio_dev)); + +err_out: + iio_trigger_notify_done(indio_dev->trig); + + return IRQ_HANDLED; +} + +static bool ad4130_setup_info_eq(struct ad4130_setup_info *a, + struct ad4130_setup_info *b) +{ + /* + * This is just to make sure that the comparison is adapted after + * struct ad4130_setup_info was changed. + */ + static_assert(sizeof(*a) == + sizeof(struct { + unsigned int iout0_val; + unsigned int iout1_val; + unsigned int burnout; + unsigned int pga; + unsigned int fs; + u32 ref_sel; + enum ad4130_filter_type filter_type; + bool ref_bufp; + bool ref_bufm; + })); + + if (a->iout0_val != b->iout0_val || + a->iout1_val != b->iout1_val || + a->burnout != b->burnout || + a->pga != b->pga || + a->fs != b->fs || + a->ref_sel != b->ref_sel || + a->filter_type != b->filter_type || + a->ref_bufp != b->ref_bufp || + a->ref_bufm != b->ref_bufm) + return false; + + return true; +} + +static int ad4130_find_slot(struct ad4130_state *st, + struct ad4130_setup_info *target_setup_info, + unsigned int *slot, bool *overwrite) +{ + unsigned int i; + + *slot = AD4130_INVALID_SLOT; + *overwrite = false; + + for (i = 0; i < AD4130_MAX_SETUPS; i++) { + struct ad4130_slot_info *slot_info = &st->slots_info[i]; + + /* Immediately accept a matching setup info. */ + if (ad4130_setup_info_eq(target_setup_info, &slot_info->setup)) { + *slot = i; + return 0; + } + + /* Ignore all setups which are used by enabled channels. */ + if (slot_info->enabled_channels) + continue; + + /* Find the least used slot. */ + if (*slot == AD4130_INVALID_SLOT || + slot_info->channels < st->slots_info[*slot].channels) + *slot = i; + } + + if (*slot == AD4130_INVALID_SLOT) + return -EINVAL; + + *overwrite = true; + + return 0; +} + +static void ad4130_unlink_channel(struct ad4130_state *st, unsigned int channel) +{ + struct ad4130_chan_info *chan_info = &st->chans_info[channel]; + struct ad4130_slot_info *slot_info = &st->slots_info[chan_info->slot]; + + chan_info->slot = AD4130_INVALID_SLOT; + slot_info->channels--; +} + +static int ad4130_unlink_slot(struct ad4130_state *st, unsigned int slot) +{ + unsigned int i; + + for (i = 0; i < AD4130_MAX_CHANNELS; i++) { + struct ad4130_chan_info *chan_info = &st->chans_info[i]; + + if (!chan_info->initialized || chan_info->slot != slot) + continue; + + ad4130_unlink_channel(st, i); + } + + return 0; +} + +static int ad4130_link_channel_slot(struct ad4130_state *st, + unsigned int channel, unsigned int slot) +{ + struct ad4130_slot_info *slot_info = &st->slots_info[slot]; + struct ad4130_chan_info *chan_info = &st->chans_info[channel]; + int ret; + + ret = regmap_update_bits(st->regmap, AD4130_CHANNEL_X_REG(channel), + AD4130_CHANNEL_SETUP_MASK, + FIELD_PREP(AD4130_CHANNEL_SETUP_MASK, slot)); + if (ret) + return ret; + + chan_info->slot = slot; + slot_info->channels++; + + return 0; +} + +static int ad4130_write_slot_setup(struct ad4130_state *st, + unsigned int slot, + struct ad4130_setup_info *setup_info) +{ + unsigned int val; + int ret; + + val = FIELD_PREP(AD4130_CONFIG_IOUT1_VAL_MASK, setup_info->iout0_val) | + FIELD_PREP(AD4130_CONFIG_IOUT1_VAL_MASK, setup_info->iout1_val) | + FIELD_PREP(AD4130_CONFIG_BURNOUT_MASK, setup_info->burnout) | + FIELD_PREP(AD4130_CONFIG_REF_BUFP_MASK, setup_info->ref_bufp) | + FIELD_PREP(AD4130_CONFIG_REF_BUFM_MASK, setup_info->ref_bufm) | + FIELD_PREP(AD4130_CONFIG_REF_SEL_MASK, setup_info->ref_sel) | + FIELD_PREP(AD4130_CONFIG_PGA_MASK, setup_info->pga); + + ret = regmap_write(st->regmap, AD4130_CONFIG_X_REG(slot), val); + if (ret) + return ret; + + val = FIELD_PREP(AD4130_FILTER_MODE_MASK, setup_info->filter_type) | + FIELD_PREP(AD4130_FILTER_SELECT_MASK, setup_info->fs); + + ret = regmap_write(st->regmap, AD4130_FILTER_X_REG(slot), val); + if (ret) + return ret; + + memcpy(&st->slots_info[slot].setup, setup_info, sizeof(*setup_info)); + + return 0; +} + +static int ad4130_write_channel_setup(struct ad4130_state *st, + unsigned int channel, bool on_enable) +{ + struct ad4130_chan_info *chan_info = &st->chans_info[channel]; + struct ad4130_setup_info *setup_info = &chan_info->setup; + bool overwrite; + int slot; + int ret; + + /* + * The following cases need to be handled. + * + * 1. Enabled and linked channel with setup changes: + * - Find a slot. If not possible, return error. + * - Unlink channel from current slot. + * - If the slot has channels linked to it, unlink all channels, and + * write the new setup to it. + * - Link channel to new slot. + * + * 2. Soon to be enabled and unlinked channel: + * - Find a slot. If not possible, return error. + * - If the slot has channels linked to it, unlink all channels, and + * write the new setup to it. + * - Link channel to the slot. + * + * 3. Disabled and linked channel with setup changes: + * - Unlink channel from current slot. + * + * 4. Soon to be enabled and linked channel: + * 5. Disabled and unlinked channel with setup changes: + * - Do nothing. + */ + + /* Case 4 */ + if (on_enable && chan_info->slot != AD4130_INVALID_SLOT) + return 0; + + if (!on_enable && !chan_info->enabled) { + if (chan_info->slot != AD4130_INVALID_SLOT) + /* Case 3 */ + ad4130_unlink_channel(st, channel); + + /* Cases 3 & 5 */ + return 0; + } + + /* Cases 1 & 2 */ + ret = ad4130_find_slot(st, setup_info, &slot, &overwrite); + if (ret) + return ret; + + if (chan_info->slot != AD4130_INVALID_SLOT) + /* Case 1 */ + ad4130_unlink_channel(st, channel); + + if (overwrite) { + ret = ad4130_unlink_slot(st, slot); + if (ret) + return ret; + + ret = ad4130_write_slot_setup(st, slot, setup_info); + if (ret) + return ret; + } + + return ad4130_link_channel_slot(st, channel, slot); +} + +static int ad4130_set_channel_enable(struct ad4130_state *st, + unsigned int channel, bool status) +{ + struct ad4130_chan_info *chan_info = &st->chans_info[channel]; + struct ad4130_slot_info *slot_info; + int ret; + + if (chan_info->enabled == status) + return 0; + + if (status) { + ret = ad4130_write_channel_setup(st, channel, true); + if (ret) + return ret; + } + + slot_info = &st->slots_info[chan_info->slot]; + + ret = regmap_update_bits(st->regmap, AD4130_CHANNEL_X_REG(channel), + AD4130_CHANNEL_EN_MASK, + FIELD_PREP(AD4130_CHANNEL_EN_MASK, status)); + if (ret) + return ret; + + slot_info->enabled_channels += status ? 1 : -1; + chan_info->enabled = status; + + return 0; +} + +/* + * Table 58. FILTER_MODE_n bits and Filter Types of the datasheet describes + * the relation between filter mode, ODR and FS. + * + * Notice that the max ODR of each filter mode is not necessarily the + * absolute max ODR supported by the chip. + * + * The ODR divider is not explicitly specified, but it can be deduced based + * on the ODR range of each filter mode. + * + * For example, for Sinc4+Sinc1, max ODR is 218.18. That means that the + * absolute max ODR is divided by 11 to achieve the max ODR of this filter + * mode. + * + * The formulas for converting between ODR and FS for a specific filter + * mode can be deduced from the same table. + * + * Notice that FS = 1 actually means max ODR, and that ODR decreases by + * (maximum ODR / maximum FS) for each increment of FS. + * + * odr = MAX_ODR / odr_div * (1 - (fs - 1) / fs_max) <=> + * odr = MAX_ODR * (1 - (fs - 1) / fs_max) / odr_div <=> + * odr = MAX_ODR * (1 - (fs - 1) / fs_max) / odr_div <=> + * odr = MAX_ODR * (fs_max - fs + 1) / (fs_max * odr_div) + * (used in ad4130_fs_to_freq) + * + * For the opposite formula, FS can be extracted from the last one. + * + * MAX_ODR * (fs_max - fs + 1) = fs_max * odr_div * odr <=> + * fs_max - fs + 1 = fs_max * odr_div * odr / MAX_ODR <=> + * fs = 1 + fs_max - fs_max * odr_div * odr / MAX_ODR + * (used in ad4130_fs_to_freq) + */ + +static void ad4130_freq_to_fs(enum ad4130_filter_type filter_type, + int val, int val2, unsigned int *fs) +{ + const struct ad4130_filter_config *filter_config = + &ad4130_filter_configs[filter_type]; + u64 dividend, divisor; + int temp; + + dividend = filter_config->fs_max * filter_config->odr_div * + ((u64)val * NANO + val2); + divisor = (u64)AD4130_MAX_ODR * NANO; + + temp = AD4130_FILTER_SELECT_MIN + filter_config->fs_max - + DIV64_U64_ROUND_CLOSEST(dividend, divisor); + + if (temp < AD4130_FILTER_SELECT_MIN) + temp = AD4130_FILTER_SELECT_MIN; + else if (temp > filter_config->fs_max) + temp = filter_config->fs_max; + + *fs = temp; +} + +static void ad4130_fs_to_freq(enum ad4130_filter_type filter_type, + unsigned int fs, int *val, int *val2) +{ + const struct ad4130_filter_config *filter_config = + &ad4130_filter_configs[filter_type]; + unsigned int dividend, divisor; + u64 temp; + + dividend = (filter_config->fs_max - fs + AD4130_FILTER_SELECT_MIN) * + AD4130_MAX_ODR; + divisor = filter_config->fs_max * filter_config->odr_div; + + temp = div_u64((u64)dividend * NANO, divisor); + *val = div_u64_rem(temp, NANO, val2); +} + +static int ad4130_set_filter_type(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + unsigned int val) +{ + struct ad4130_state *st = iio_priv(indio_dev); + unsigned int channel = chan->scan_index; + struct ad4130_chan_info *chan_info = &st->chans_info[channel]; + struct ad4130_setup_info *setup_info = &chan_info->setup; + enum ad4130_filter_type old_filter_type; + int freq_val, freq_val2; + unsigned int old_fs; + int ret = 0; + + guard(mutex)(&st->lock); + if (setup_info->filter_type == val) + return 0; + + old_fs = setup_info->fs; + old_filter_type = setup_info->filter_type; + + /* + * When switching between filter modes, try to match the ODR as + * close as possible. To do this, convert the current FS into ODR + * using the old filter mode, then convert it back into FS using + * the new filter mode. + */ + ad4130_fs_to_freq(setup_info->filter_type, setup_info->fs, + &freq_val, &freq_val2); + + ad4130_freq_to_fs(val, freq_val, freq_val2, &setup_info->fs); + + setup_info->filter_type = val; + + ret = ad4130_write_channel_setup(st, channel, false); + if (ret) { + setup_info->fs = old_fs; + setup_info->filter_type = old_filter_type; + return ret; + } + + return 0; +} + +static int ad4130_get_filter_type(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan) +{ + struct ad4130_state *st = iio_priv(indio_dev); + unsigned int channel = chan->scan_index; + struct ad4130_setup_info *setup_info = &st->chans_info[channel].setup; + enum ad4130_filter_type filter_type; + + guard(mutex)(&st->lock); + filter_type = setup_info->filter_type; + + return filter_type; +} + +static const struct iio_enum ad4130_filter_type_enum = { + .items = ad4130_filter_types_str, + .num_items = ARRAY_SIZE(ad4130_filter_types_str), + .set = ad4130_set_filter_type, + .get = ad4130_get_filter_type, +}; + +static const struct iio_chan_spec_ext_info ad4130_ext_info[] = { + /* + * `filter_type` is the standardized IIO ABI for digital filtering. + * `filter_mode` is just kept for backwards compatibility. + */ + IIO_ENUM("filter_mode", IIO_SEPARATE, &ad4130_filter_type_enum), + IIO_ENUM_AVAILABLE("filter_mode", IIO_SHARED_BY_TYPE, + &ad4130_filter_type_enum), + IIO_ENUM("filter_type", IIO_SEPARATE, &ad4130_filter_type_enum), + IIO_ENUM_AVAILABLE("filter_type", IIO_SHARED_BY_TYPE, + &ad4130_filter_type_enum), + { } +}; + +static const struct iio_chan_spec ad4130_channel_template = { + .type = IIO_VOLTAGE, + .indexed = 1, + .differential = 1, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SCALE) | + BIT(IIO_CHAN_INFO_OFFSET) | + BIT(IIO_CHAN_INFO_SAMP_FREQ), + .info_mask_separate_available = BIT(IIO_CHAN_INFO_SCALE) | + BIT(IIO_CHAN_INFO_SAMP_FREQ), + .ext_info = ad4130_ext_info, + .scan_type = { + .sign = 'u', + .endianness = IIO_BE, + }, +}; + +static int ad4130_set_channel_pga(struct ad4130_state *st, unsigned int channel, + int val, int val2) +{ + struct ad4130_chan_info *chan_info = &st->chans_info[channel]; + struct ad4130_setup_info *setup_info = &chan_info->setup; + unsigned int pga, old_pga; + int ret; + + for (pga = 0; pga < AD4130_MAX_PGA; pga++) + if (val == st->scale_tbls[setup_info->ref_sel][pga][0] && + val2 == st->scale_tbls[setup_info->ref_sel][pga][1]) + break; + + if (pga == AD4130_MAX_PGA) + return -EINVAL; + + guard(mutex)(&st->lock); + if (pga == setup_info->pga) + return 0; + + old_pga = setup_info->pga; + setup_info->pga = pga; + + ret = ad4130_write_channel_setup(st, channel, false); + if (ret) { + setup_info->pga = old_pga; + return ret; + } + + return 0; +} + +static int ad4130_set_channel_freq(struct ad4130_state *st, + unsigned int channel, int val, int val2) +{ + struct ad4130_chan_info *chan_info = &st->chans_info[channel]; + struct ad4130_setup_info *setup_info = &chan_info->setup; + unsigned int fs, old_fs; + int ret; + + guard(mutex)(&st->lock); + old_fs = setup_info->fs; + + ad4130_freq_to_fs(setup_info->filter_type, val, val2, &fs); + + if (fs == setup_info->fs) + return 0; + + setup_info->fs = fs; + + ret = ad4130_write_channel_setup(st, channel, false); + if (ret) { + setup_info->fs = old_fs; + return ret; + } + + return 0; +} + +static int _ad4130_read_sample(struct iio_dev *indio_dev, unsigned int channel, + int *val) +{ + struct ad4130_state *st = iio_priv(indio_dev); + int ret; + + ret = ad4130_set_channel_enable(st, channel, true); + if (ret) + return ret; + + reinit_completion(&st->completion); + + ret = ad4130_set_mode(st, AD4130_MODE_CONTINUOUS); + if (ret) + return ret; + + ret = wait_for_completion_timeout(&st->completion, + msecs_to_jiffies(1000)); + if (!ret) + return -ETIMEDOUT; + + ret = ad4130_set_mode(st, AD4130_MODE_IDLE); + if (ret) + return ret; + + ret = regmap_read(st->regmap, AD4130_DATA_REG, val); + if (ret) + return ret; + + ret = ad4130_set_channel_enable(st, channel, false); + if (ret) + return ret; + + return IIO_VAL_INT; +} + +static int ad4130_read_sample(struct iio_dev *indio_dev, unsigned int channel, + int *val) +{ + struct ad4130_state *st = iio_priv(indio_dev); + + guard(mutex)(&st->lock); + + return _ad4130_read_sample(indio_dev, channel, val); +} + +static int ad4130_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long info) +{ + struct ad4130_state *st = iio_priv(indio_dev); + unsigned int channel = chan->scan_index; + struct ad4130_setup_info *setup_info = &st->chans_info[channel].setup; + int ret; + + switch (info) { + case IIO_CHAN_INFO_RAW: + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; + + ret = ad4130_read_sample(indio_dev, channel, val); + iio_device_release_direct(indio_dev); + return ret; + case IIO_CHAN_INFO_SCALE: { + guard(mutex)(&st->lock); + *val = st->scale_tbls[setup_info->ref_sel][setup_info->pga][0]; + *val2 = st->scale_tbls[setup_info->ref_sel][setup_info->pga][1]; + + return IIO_VAL_INT_PLUS_NANO; + } + case IIO_CHAN_INFO_OFFSET: + *val = st->bipolar ? -BIT(chan->scan_type.realbits - 1) : 0; + + return IIO_VAL_INT; + case IIO_CHAN_INFO_SAMP_FREQ: { + guard(mutex)(&st->lock); + ad4130_fs_to_freq(setup_info->filter_type, setup_info->fs, + val, val2); + + return IIO_VAL_INT_PLUS_NANO; + } + default: + return -EINVAL; + } +} + +static int ad4130_read_avail(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + const int **vals, int *type, int *length, + long info) +{ + struct ad4130_state *st = iio_priv(indio_dev); + unsigned int channel = chan->scan_index; + struct ad4130_setup_info *setup_info = &st->chans_info[channel].setup; + const struct ad4130_filter_config *filter_config; + + switch (info) { + case IIO_CHAN_INFO_SCALE: + *vals = (int *)st->scale_tbls[setup_info->ref_sel]; + *length = ARRAY_SIZE(st->scale_tbls[setup_info->ref_sel]) * 2; + + *type = IIO_VAL_INT_PLUS_NANO; + + return IIO_AVAIL_LIST; + case IIO_CHAN_INFO_SAMP_FREQ: + scoped_guard(mutex, &st->lock) { + filter_config = &ad4130_filter_configs[setup_info->filter_type]; + } + + *vals = (int *)filter_config->samp_freq_avail; + *length = filter_config->samp_freq_avail_len * 2; + *type = IIO_VAL_FRACTIONAL; + + return filter_config->samp_freq_avail_type; + default: + return -EINVAL; + } +} + +static int ad4130_write_raw_get_fmt(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + long info) +{ + switch (info) { + case IIO_CHAN_INFO_SCALE: + case IIO_CHAN_INFO_SAMP_FREQ: + return IIO_VAL_INT_PLUS_NANO; + default: + return -EINVAL; + } +} + +static int ad4130_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, int val2, long info) +{ + struct ad4130_state *st = iio_priv(indio_dev); + unsigned int channel = chan->scan_index; + + switch (info) { + case IIO_CHAN_INFO_SCALE: + return ad4130_set_channel_pga(st, channel, val, val2); + case IIO_CHAN_INFO_SAMP_FREQ: + return ad4130_set_channel_freq(st, channel, val, val2); + default: + return -EINVAL; + } +} + +static int ad4130_reg_access(struct iio_dev *indio_dev, unsigned int reg, + unsigned int writeval, unsigned int *readval) +{ + struct ad4130_state *st = iio_priv(indio_dev); + + if (readval) + return regmap_read(st->regmap, reg, readval); + + return regmap_write(st->regmap, reg, writeval); +} + +static int ad4130_update_scan_mode(struct iio_dev *indio_dev, + const unsigned long *scan_mask) +{ + struct ad4130_state *st = iio_priv(indio_dev); + unsigned int channel; + unsigned int val = 0; + int ret; + + guard(mutex)(&st->lock); + + for_each_set_bit(channel, scan_mask, indio_dev->num_channels) { + ret = ad4130_set_channel_enable(st, channel, true); + if (ret) + return ret; + + val++; + } + + st->num_enabled_channels = val; + + return 0; +} + +static int ad4130_set_fifo_watermark(struct iio_dev *indio_dev, unsigned int val) +{ + struct ad4130_state *st = iio_priv(indio_dev); + unsigned int eff; + int ret; + + if (val > AD4130_FIFO_SIZE) + return -EINVAL; + + eff = val * st->num_enabled_channels; + if (eff > AD4130_FIFO_SIZE) + /* + * Always set watermark to a multiple of the number of + * enabled channels to avoid making the FIFO unaligned. + */ + eff = rounddown(AD4130_FIFO_SIZE, st->num_enabled_channels); + + guard(mutex)(&st->lock); + + ret = regmap_update_bits(st->regmap, AD4130_FIFO_CONTROL_REG, + AD4130_FIFO_CONTROL_WM_MASK, + FIELD_PREP(AD4130_FIFO_CONTROL_WM_MASK, + ad4130_watermark_reg_val(eff))); + if (ret) + return ret; + + st->effective_watermark = eff; + st->watermark = val; + + return 0; +} + +static const struct iio_info ad4130_info = { + .read_raw = ad4130_read_raw, + .read_avail = ad4130_read_avail, + .write_raw_get_fmt = ad4130_write_raw_get_fmt, + .write_raw = ad4130_write_raw, + .update_scan_mode = ad4130_update_scan_mode, + .hwfifo_set_watermark = ad4130_set_fifo_watermark, + .debugfs_reg_access = ad4130_reg_access, +}; + +static const struct iio_info ad4131_info = { + .read_raw = ad4130_read_raw, + .read_avail = ad4130_read_avail, + .write_raw_get_fmt = ad4130_write_raw_get_fmt, + .write_raw = ad4130_write_raw, + .update_scan_mode = ad4130_update_scan_mode, + .debugfs_reg_access = ad4130_reg_access, +}; + +static const struct ad4130_chip_info ad4129_4_chip_info = { + .name = "ad4129-4", + .max_analog_pins = 8, + .num_gpios = 2, + .info = &ad4130_info, + .reg_size = ad4129_reg_size, + .reg_size_length = ARRAY_SIZE(ad4129_reg_size), + .has_fifo = true, + .pin_map = ad4130_4_pin_map, +}; + +static const struct ad4130_chip_info ad4129_8_chip_info = { + .name = "ad4129-8", + .max_analog_pins = 16, + .num_gpios = 4, + .info = &ad4130_info, + .reg_size = ad4129_reg_size, + .reg_size_length = ARRAY_SIZE(ad4129_reg_size), + .has_fifo = true, + .pin_map = ad4130_8_pin_map, +}; + +static const struct ad4130_chip_info ad4130_4_chip_info = { + .name = "ad4130-4", + .max_analog_pins = 16, + .num_gpios = 2, + .info = &ad4130_info, + .reg_size = ad4130_reg_size, + .reg_size_length = ARRAY_SIZE(ad4130_reg_size), + .has_fifo = true, + .pin_map = ad4130_4_pin_map, +}; + +static const struct ad4130_chip_info ad4130_8_chip_info = { + .name = "ad4130-8", + .max_analog_pins = 16, + .num_gpios = 4, + .info = &ad4130_info, + .reg_size = ad4130_reg_size, + .reg_size_length = ARRAY_SIZE(ad4130_reg_size), + .has_fifo = true, + .pin_map = ad4130_8_pin_map, +}; + +static const struct ad4130_chip_info ad4131_4_chip_info = { + .name = "ad4131-4", + .max_analog_pins = 8, + .num_gpios = 2, + .info = &ad4131_info, + .reg_size = ad4131_reg_size, + .reg_size_length = ARRAY_SIZE(ad4131_reg_size), + .pin_map = ad4130_4_pin_map, +}; + +static const struct ad4130_chip_info ad4131_8_chip_info = { + .name = "ad4131-8", + .max_analog_pins = 16, + .num_gpios = 4, + .info = &ad4131_info, + .reg_size = ad4131_reg_size, + .reg_size_length = ARRAY_SIZE(ad4131_reg_size), + .pin_map = ad4130_8_pin_map, +}; + +static int ad4130_buffer_postenable(struct iio_dev *indio_dev) +{ + struct ad4130_state *st = iio_priv(indio_dev); + int ret; + + guard(mutex)(&st->lock); + + if (st->chip_info->has_fifo) { + ret = ad4130_set_watermark_interrupt_en(st, true); + if (ret) + return ret; + + ret = irq_set_irq_type(st->spi->irq, st->inv_irq_trigger); + if (ret) + return ret; + + ret = ad4130_set_fifo_mode(st, AD4130_FIFO_MODE_WM); + if (ret) + return ret; + } + + ret = ad4130_set_mode(st, AD4130_MODE_CONTINUOUS); + if (ret) + return ret; + + /* + * When using triggered buffer, Entering continuous read mode must + * be the last command sent. No configuration changes are allowed until + * exiting this mode. + */ + if (!st->chip_info->has_fifo) { + ret = regmap_update_bits(st->regmap, AD4130_ADC_CONTROL_REG, + AD4130_ADC_CONTROL_CONT_READ_MASK, + FIELD_PREP(AD4130_ADC_CONTROL_CONT_READ_MASK, 1)); + if (ret) + return ret; + } + + return 0; +} + +static int ad4130_buffer_predisable(struct iio_dev *indio_dev) +{ + struct ad4130_state *st = iio_priv(indio_dev); + unsigned int i; + u32 temp; + int ret; + + guard(mutex)(&st->lock); + + if (!st->chip_info->has_fifo) { + temp = 0x42; + reinit_completion(&st->completion); + + /* + * In continuous read mode, when all samples are read, the data + * ready signal returns high until the next conversion result is + * ready. To exit this mode, the command must be sent when data + * ready is low. In order to ensure that condition, wait for the + * next interrupt (when the new conversion is finished), allowing + * data ready to return low before sending the exit command. + */ + st->buffer_wait_for_irq = true; + if (!wait_for_completion_timeout(&st->completion, msecs_to_jiffies(1000))) + dev_warn(&st->spi->dev, "Conversion timed out\n"); + st->buffer_wait_for_irq = false; + + /* Perform a read data command to exit continuous read mode (0x42) */ + ret = spi_write(st->spi, &temp, 1); + if (ret) + return ret; + } + + ret = ad4130_set_mode(st, AD4130_MODE_IDLE); + if (ret) + return ret; + + if (st->chip_info->has_fifo) { + ret = irq_set_irq_type(st->spi->irq, st->irq_trigger); + if (ret) + return ret; + + ret = ad4130_set_fifo_mode(st, AD4130_FIFO_MODE_DISABLED); + if (ret) + return ret; + + ret = ad4130_set_watermark_interrupt_en(st, false); + if (ret) + return ret; + } + + /* + * update_scan_mode() is not called in the disable path, disable all + * channels here. + */ + for (i = 0; i < indio_dev->num_channels; i++) { + ret = ad4130_set_channel_enable(st, i, false); + if (ret) + return ret; + } + + return 0; +} + +static const struct iio_buffer_setup_ops ad4130_buffer_ops = { + .postenable = ad4130_buffer_postenable, + .predisable = ad4130_buffer_predisable, +}; + +static ssize_t hwfifo_watermark_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct ad4130_state *st = iio_priv(dev_to_iio_dev(dev)); + unsigned int val; + + guard(mutex)(&st->lock); + val = st->watermark; + + return sysfs_emit(buf, "%d\n", val); +} + +static ssize_t hwfifo_enabled_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct ad4130_state *st = iio_priv(dev_to_iio_dev(dev)); + unsigned int val; + int ret; + + ret = regmap_read(st->regmap, AD4130_FIFO_CONTROL_REG, &val); + if (ret) + return ret; + + val = FIELD_GET(AD4130_FIFO_CONTROL_MODE_MASK, val); + + return sysfs_emit(buf, "%d\n", val != AD4130_FIFO_MODE_DISABLED); +} + +static ssize_t hwfifo_watermark_min_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + return sysfs_emit(buf, "%s\n", "1"); +} + +static ssize_t hwfifo_watermark_max_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + return sysfs_emit(buf, "%s\n", __stringify(AD4130_FIFO_SIZE)); +} + +static IIO_DEVICE_ATTR_RO(hwfifo_watermark_min, 0); +static IIO_DEVICE_ATTR_RO(hwfifo_watermark_max, 0); +static IIO_DEVICE_ATTR_RO(hwfifo_watermark, 0); +static IIO_DEVICE_ATTR_RO(hwfifo_enabled, 0); + +static const struct iio_dev_attr *ad4130_fifo_attributes[] = { + &iio_dev_attr_hwfifo_watermark_min, + &iio_dev_attr_hwfifo_watermark_max, + &iio_dev_attr_hwfifo_watermark, + &iio_dev_attr_hwfifo_enabled, + NULL +}; + +static const struct iio_trigger_ops ad4130_trigger_ops = { + .validate_device = iio_trigger_validate_own_device, +}; + +static int ad4130_triggered_buffer_setup(struct iio_dev *indio_dev) +{ + struct ad4130_state *st = iio_priv(indio_dev); + int ret; + + st->trig = devm_iio_trigger_alloc(indio_dev->dev.parent, "%s-dev%d", + indio_dev->name, iio_device_id(indio_dev)); + if (!st->trig) + return -ENOMEM; + + st->trig->ops = &ad4130_trigger_ops; + iio_trigger_set_drvdata(st->trig, indio_dev); + ret = devm_iio_trigger_register(indio_dev->dev.parent, st->trig); + if (ret) + return ret; + + indio_dev->trig = iio_trigger_get(st->trig); + + return devm_iio_triggered_buffer_setup(indio_dev->dev.parent, indio_dev, + &iio_pollfunc_store_time, + &ad4130_trigger_handler, + &ad4130_buffer_ops); +} + +static int _ad4130_find_table_index(const unsigned int *tbl, size_t len, + unsigned int val) +{ + unsigned int i; + + for (i = 0; i < len; i++) + if (tbl[i] == val) + return i; + + return -EINVAL; +} + +#define ad4130_find_table_index(table, val) \ + _ad4130_find_table_index(table, ARRAY_SIZE(table), val) + +static int ad4130_get_ref_voltage(struct ad4130_state *st, + enum ad4130_ref_sel ref_sel) +{ + switch (ref_sel) { + case AD4130_REF_REFIN1: + return regulator_get_voltage(st->regulators[2].consumer); + case AD4130_REF_REFIN2: + return regulator_get_voltage(st->regulators[3].consumer); + case AD4130_REF_AVDD_AVSS: + return regulator_get_voltage(st->regulators[0].consumer); + case AD4130_REF_REFOUT_AVSS: + return st->int_ref_uv; + default: + return -EINVAL; + } +} + +static int ad4130_parse_fw_setup(struct ad4130_state *st, + struct fwnode_handle *child, + struct ad4130_setup_info *setup_info) +{ + struct device *dev = &st->spi->dev; + u32 tmp; + int ret; + + tmp = 0; + fwnode_property_read_u32(child, "adi,excitation-current-0-nanoamp", &tmp); + ret = ad4130_find_table_index(ad4130_iout_current_na_tbl, tmp); + if (ret < 0) + return dev_err_probe(dev, ret, + "Invalid excitation current %unA\n", tmp); + setup_info->iout0_val = ret; + + tmp = 0; + fwnode_property_read_u32(child, "adi,excitation-current-1-nanoamp", &tmp); + ret = ad4130_find_table_index(ad4130_iout_current_na_tbl, tmp); + if (ret < 0) + return dev_err_probe(dev, ret, + "Invalid excitation current %unA\n", tmp); + setup_info->iout1_val = ret; + + tmp = 0; + fwnode_property_read_u32(child, "adi,burnout-current-nanoamp", &tmp); + ret = ad4130_find_table_index(ad4130_burnout_current_na_tbl, tmp); + if (ret < 0) + return dev_err_probe(dev, ret, + "Invalid burnout current %unA\n", tmp); + setup_info->burnout = ret; + + setup_info->ref_bufp = fwnode_property_read_bool(child, "adi,buffered-positive"); + setup_info->ref_bufm = fwnode_property_read_bool(child, "adi,buffered-negative"); + + setup_info->ref_sel = AD4130_REF_REFIN1; + fwnode_property_read_u32(child, "adi,reference-select", + &setup_info->ref_sel); + if (setup_info->ref_sel >= AD4130_REF_SEL_MAX) + return dev_err_probe(dev, -EINVAL, + "Invalid reference selected %u\n", + setup_info->ref_sel); + + if (setup_info->ref_sel == AD4130_REF_REFOUT_AVSS) + st->int_ref_en = true; + + ret = ad4130_get_ref_voltage(st, setup_info->ref_sel); + if (ret < 0) + return dev_err_probe(dev, ret, "Cannot use reference %u\n", + setup_info->ref_sel); + + return 0; +} + +static unsigned int ad4130_translate_pin(struct ad4130_state *st, + unsigned int logical_pin) +{ + /* For analog input pins, use the chip-specific pin mapping */ + if (logical_pin < st->chip_info->max_analog_pins) + return st->chip_info->pin_map[logical_pin]; + + /* For internal channels, pass through unchanged */ + return logical_pin; +} + +static int ad4130_validate_diff_channel(struct ad4130_state *st, u32 pin) +{ + struct device *dev = &st->spi->dev; + + if (pin >= AD4130_MAX_DIFF_INPUTS) + return dev_err_probe(dev, -EINVAL, + "Invalid differential channel %u\n", pin); + + if (pin >= st->chip_info->max_analog_pins) + return 0; + + if (st->pins_fn[pin] == AD4130_PIN_FN_SPECIAL) + return dev_err_probe(dev, -EINVAL, + "Pin %u already used with fn %u\n", pin, + st->pins_fn[pin]); + + st->pins_fn[pin] |= AD4130_PIN_FN_DIFF; + + return 0; +} + +static int ad4130_validate_diff_channels(struct ad4130_state *st, + u32 *pins, unsigned int len) +{ + unsigned int i; + int ret; + + for (i = 0; i < len; i++) { + ret = ad4130_validate_diff_channel(st, pins[i]); + if (ret) + return ret; + } + + return 0; +} + +static int ad4130_validate_excitation_pin(struct ad4130_state *st, u32 pin) +{ + struct device *dev = &st->spi->dev; + + if (pin >= st->chip_info->max_analog_pins) + return dev_err_probe(dev, -EINVAL, + "Invalid excitation pin %u\n", pin); + + if (st->pins_fn[pin] == AD4130_PIN_FN_SPECIAL) + return dev_err_probe(dev, -EINVAL, + "Pin %u already used with fn %u\n", pin, + st->pins_fn[pin]); + + st->pins_fn[pin] |= AD4130_PIN_FN_EXCITATION; + + return 0; +} + +static int ad4130_validate_vbias_pin(struct ad4130_state *st, u32 pin) +{ + struct device *dev = &st->spi->dev; + + if (pin >= st->chip_info->max_analog_pins) + return dev_err_probe(dev, -EINVAL, "Invalid vbias pin %u\n", + pin); + + if (st->pins_fn[pin] == AD4130_PIN_FN_SPECIAL) + return dev_err_probe(dev, -EINVAL, + "Pin %u already used with fn %u\n", pin, + st->pins_fn[pin]); + + st->pins_fn[pin] |= AD4130_PIN_FN_VBIAS; + + return 0; +} + +static int ad4130_validate_vbias_pins(struct ad4130_state *st, + u32 *pins, unsigned int len) +{ + unsigned int i; + int ret; + + for (i = 0; i < st->num_vbias_pins; i++) { + ret = ad4130_validate_vbias_pin(st, pins[i]); + if (ret) + return ret; + } + + return 0; +} + +static int ad4130_parse_fw_channel(struct iio_dev *indio_dev, + struct fwnode_handle *child) +{ + struct ad4130_state *st = iio_priv(indio_dev); + unsigned int resolution = ad4130_resolution(st); + unsigned int index = indio_dev->num_channels++; + struct device *dev = &st->spi->dev; + struct ad4130_chan_info *chan_info; + struct iio_chan_spec *chan; + u32 pins[2]; + int ret; + + if (index >= AD4130_MAX_CHANNELS) + return dev_err_probe(dev, -EINVAL, "Too many channels\n"); + + chan = &st->chans[index]; + chan_info = &st->chans_info[index]; + + *chan = ad4130_channel_template; + chan->scan_type.realbits = resolution; + chan->scan_type.storagebits = resolution; + chan->scan_index = index; + + chan_info->slot = AD4130_INVALID_SLOT; + chan_info->setup.fs = AD4130_FILTER_SELECT_MIN; + chan_info->initialized = true; + + ret = fwnode_property_read_u32_array(child, "diff-channels", pins, + ARRAY_SIZE(pins)); + if (ret) + return ret; + + ret = ad4130_validate_diff_channels(st, pins, ARRAY_SIZE(pins)); + if (ret) + return ret; + + chan->channel = pins[0]; + chan->channel2 = pins[1]; + + ret = ad4130_parse_fw_setup(st, child, &chan_info->setup); + if (ret) + return ret; + + fwnode_property_read_u32(child, "adi,excitation-pin-0", + &chan_info->iout0); + if (chan_info->setup.iout0_val != AD4130_IOUT_OFF) { + ret = ad4130_validate_excitation_pin(st, chan_info->iout0); + if (ret) + return ret; + } + + fwnode_property_read_u32(child, "adi,excitation-pin-1", + &chan_info->iout1); + if (chan_info->setup.iout1_val != AD4130_IOUT_OFF) { + ret = ad4130_validate_excitation_pin(st, chan_info->iout1); + if (ret) + return ret; + } + + return 0; +} + +static int ad4130_parse_fw_children(struct iio_dev *indio_dev) +{ + struct ad4130_state *st = iio_priv(indio_dev); + struct device *dev = &st->spi->dev; + int ret; + + indio_dev->channels = st->chans; + + device_for_each_child_node_scoped(dev, child) { + ret = ad4130_parse_fw_channel(indio_dev, child); + if (ret) + return ret; + } + + return 0; +} + +static int ad4310_parse_fw(struct iio_dev *indio_dev) +{ + struct ad4130_state *st = iio_priv(indio_dev); + struct device *dev = &st->spi->dev; + u32 ext_clk_freq = AD4130_MCLK_FREQ_76_8KHZ; + unsigned int i; + int avdd_uv; + int irq; + int ret; + + st->mclk = devm_clk_get_optional(dev, "mclk"); + if (IS_ERR(st->mclk)) + return dev_err_probe(dev, PTR_ERR(st->mclk), + "Failed to get mclk\n"); + + st->int_pin_sel = AD4130_INT_PIN_INT; + + for (i = 0; i < ARRAY_SIZE(ad4130_int_pin_names); i++) { + irq = fwnode_irq_get_byname(dev_fwnode(dev), + ad4130_int_pin_names[i]); + if (irq > 0) { + st->int_pin_sel = i; + break; + } + } + + if (st->int_pin_sel == AD4130_INT_PIN_DOUT) + return dev_err_probe(dev, -EINVAL, + "Cannot use DOUT as interrupt pin\n"); + + if (st->int_pin_sel == AD4130_INT_PIN_P2) + st->pins_fn[AD4130_AIN3_P2] = AD4130_PIN_FN_SPECIAL; + + device_property_read_u32(dev, "adi,ext-clk-freq-hz", &ext_clk_freq); + if (ext_clk_freq != AD4130_MCLK_FREQ_153_6KHZ && + ext_clk_freq != AD4130_MCLK_FREQ_76_8KHZ) + return dev_err_probe(dev, -EINVAL, + "Invalid external clock frequency %u\n", + ext_clk_freq); + + if (st->mclk && ext_clk_freq == AD4130_MCLK_FREQ_153_6KHZ) + st->mclk_sel = AD4130_MCLK_153_6KHZ_EXT; + else if (st->mclk) + st->mclk_sel = AD4130_MCLK_76_8KHZ_EXT; + else + st->mclk_sel = AD4130_MCLK_76_8KHZ; + + if (st->int_pin_sel == AD4130_INT_PIN_CLK && + st->mclk_sel != AD4130_MCLK_76_8KHZ) + return dev_err_probe(dev, -EINVAL, + "Invalid clock %u for interrupt pin %u\n", + st->mclk_sel, st->int_pin_sel); + + st->int_ref_uv = AD4130_INT_REF_2_5V; + + /* + * When the AVDD supply is set to below 2.5V the internal reference of + * 1.25V should be selected. + * See datasheet page 37, section ADC REFERENCE. + */ + avdd_uv = regulator_get_voltage(st->regulators[0].consumer); + if (avdd_uv > 0 && avdd_uv < AD4130_INT_REF_2_5V) + st->int_ref_uv = AD4130_INT_REF_1_25V; + + st->bipolar = device_property_read_bool(dev, "adi,bipolar"); + + ret = device_property_count_u32(dev, "adi,vbias-pins"); + if (ret > 0) { + if (ret > st->chip_info->max_analog_pins) + return dev_err_probe(dev, -EINVAL, + "Too many vbias pins %u\n", ret); + + st->num_vbias_pins = ret; + + ret = device_property_read_u32_array(dev, "adi,vbias-pins", + st->vbias_pins, + st->num_vbias_pins); + if (ret) + return dev_err_probe(dev, ret, + "Failed to read vbias pins\n"); + + ret = ad4130_validate_vbias_pins(st, st->vbias_pins, + st->num_vbias_pins); + if (ret) + return ret; + } + + ret = ad4130_parse_fw_children(indio_dev); + if (ret) + return ret; + + return 0; +} + +static void ad4130_fill_scale_tbls(struct ad4130_state *st) +{ + unsigned int pow = ad4130_resolution(st) - st->bipolar; + unsigned int i, j; + + for (i = 0; i < AD4130_REF_SEL_MAX; i++) { + int ret; + u64 nv; + + ret = ad4130_get_ref_voltage(st, i); + if (ret < 0) + continue; + + nv = (u64)ret * NANO; + + for (j = 0; j < AD4130_MAX_PGA; j++) + st->scale_tbls[i][j][1] = div_u64(nv >> (pow + j), MILLI); + } +} + +static void ad4130_clk_disable_unprepare(void *clk) +{ + clk_disable_unprepare(clk); +} + +static int ad4130_set_mclk_sel(struct ad4130_state *st, + enum ad4130_mclk_sel mclk_sel) +{ + return regmap_update_bits(st->regmap, AD4130_ADC_CONTROL_REG, + AD4130_ADC_CONTROL_MCLK_SEL_MASK, + FIELD_PREP(AD4130_ADC_CONTROL_MCLK_SEL_MASK, + mclk_sel)); +} + +static unsigned long ad4130_int_clk_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + return AD4130_MCLK_FREQ_76_8KHZ; +} + +static int ad4130_int_clk_is_enabled(struct clk_hw *hw) +{ + struct ad4130_state *st = container_of(hw, struct ad4130_state, int_clk_hw); + + return st->mclk_sel == AD4130_MCLK_76_8KHZ_OUT; +} + +static int ad4130_int_clk_prepare(struct clk_hw *hw) +{ + struct ad4130_state *st = container_of(hw, struct ad4130_state, int_clk_hw); + int ret; + + ret = ad4130_set_mclk_sel(st, AD4130_MCLK_76_8KHZ_OUT); + if (ret) + return ret; + + st->mclk_sel = AD4130_MCLK_76_8KHZ_OUT; + + return 0; +} + +static void ad4130_int_clk_unprepare(struct clk_hw *hw) +{ + struct ad4130_state *st = container_of(hw, struct ad4130_state, int_clk_hw); + int ret; + + ret = ad4130_set_mclk_sel(st, AD4130_MCLK_76_8KHZ); + if (ret) + return; + + st->mclk_sel = AD4130_MCLK_76_8KHZ; +} + +static const struct clk_ops ad4130_int_clk_ops = { + .recalc_rate = ad4130_int_clk_recalc_rate, + .is_enabled = ad4130_int_clk_is_enabled, + .prepare = ad4130_int_clk_prepare, + .unprepare = ad4130_int_clk_unprepare, +}; + +static int ad4130_setup_int_clk(struct ad4130_state *st) +{ + struct device *dev = &st->spi->dev; + struct device_node *of_node = dev_of_node(dev); + struct clk_init_data init = {}; + const char *clk_name; + int ret; + + if (st->int_pin_sel == AD4130_INT_PIN_CLK || + st->mclk_sel != AD4130_MCLK_76_8KHZ) + return 0; + + if (!of_node) + return 0; + + clk_name = of_node->name; + of_property_read_string(of_node, "clock-output-names", &clk_name); + + init.name = clk_name; + init.ops = &ad4130_int_clk_ops; + + st->int_clk_hw.init = &init; + ret = devm_clk_hw_register(dev, &st->int_clk_hw); + if (ret) + return ret; + + return devm_of_clk_add_hw_provider(dev, of_clk_hw_simple_get, + &st->int_clk_hw); +} + +static int ad4130_setup(struct iio_dev *indio_dev) +{ + struct ad4130_state *st = iio_priv(indio_dev); + struct device *dev = &st->spi->dev; + unsigned int int_ref_val; + unsigned long rate = AD4130_MCLK_FREQ_76_8KHZ; + unsigned int val; + unsigned int i; + int ret; + + if (st->mclk_sel == AD4130_MCLK_153_6KHZ_EXT) + rate = AD4130_MCLK_FREQ_153_6KHZ; + + ret = clk_set_rate(st->mclk, rate); + if (ret) + return ret; + + ret = clk_prepare_enable(st->mclk); + if (ret) + return ret; + + ret = devm_add_action_or_reset(dev, ad4130_clk_disable_unprepare, + st->mclk); + if (ret) + return ret; + + if (st->int_ref_uv == AD4130_INT_REF_2_5V) + int_ref_val = AD4130_INT_REF_VAL_2_5V; + else + int_ref_val = AD4130_INT_REF_VAL_1_25V; + + /* Switch to SPI 4-wire mode. */ + val = FIELD_PREP(AD4130_ADC_CONTROL_CSB_EN_MASK, 1); + val |= FIELD_PREP(AD4130_ADC_CONTROL_BIPOLAR_MASK, st->bipolar); + val |= FIELD_PREP(AD4130_ADC_CONTROL_INT_REF_EN_MASK, st->int_ref_en); + val |= FIELD_PREP(AD4130_ADC_CONTROL_MODE_MASK, AD4130_MODE_IDLE); + val |= FIELD_PREP(AD4130_ADC_CONTROL_MCLK_SEL_MASK, st->mclk_sel); + val |= FIELD_PREP(AD4130_ADC_CONTROL_INT_REF_VAL_MASK, int_ref_val); + + ret = regmap_write(st->regmap, AD4130_ADC_CONTROL_REG, val); + if (ret) + return ret; + + /* + * Configure unused GPIOs for output. If configured, the interrupt + * function of P2 takes priority over the GPIO out function. + */ + val = 0; + for (i = 0; i < st->chip_info->num_gpios; i++) { + if (st->pins_fn[i + AD4130_AIN2_P1] == AD4130_PIN_FN_NONE) { + if (st->chip_info->num_gpios == 2) + val |= FIELD_PREP(AD4130_4_IO_CONTROL_GPIO_CTRL_MASK, BIT(i)); + else + val |= FIELD_PREP(AD4130_IO_CONTROL_GPIO_CTRL_MASK, BIT(i)); + } + } + + val |= FIELD_PREP(AD4130_IO_CONTROL_INT_PIN_SEL_MASK, st->int_pin_sel); + + ret = regmap_write(st->regmap, AD4130_IO_CONTROL_REG, val); + if (ret) + return ret; + + val = 0; + for (i = 0; i < st->num_vbias_pins; i++) + val |= BIT(ad4130_translate_pin(st, st->vbias_pins[i])); + + ret = regmap_write(st->regmap, AD4130_VBIAS_REG, val); + if (ret) + return ret; + + if (st->chip_info->has_fifo) { + ret = regmap_clear_bits(st->regmap, AD4130_FIFO_CONTROL_REG, + AD4130_FIFO_CONTROL_HEADER_MASK); + if (ret) + return ret; + + /* FIFO watermark interrupt starts out as enabled, disable it. */ + ret = ad4130_set_watermark_interrupt_en(st, false); + if (ret) + return ret; + } + + /* Setup channels. */ + for (i = 0; i < indio_dev->num_channels; i++) { + struct ad4130_chan_info *chan_info = &st->chans_info[i]; + struct iio_chan_spec *chan = &st->chans[i]; + unsigned int val; + + val = FIELD_PREP(AD4130_CHANNEL_AINP_MASK, + ad4130_translate_pin(st, chan->channel)) | + FIELD_PREP(AD4130_CHANNEL_AINM_MASK, + ad4130_translate_pin(st, chan->channel2)) | + FIELD_PREP(AD4130_CHANNEL_IOUT1_MASK, + ad4130_translate_pin(st, chan_info->iout0)) | + FIELD_PREP(AD4130_CHANNEL_IOUT2_MASK, + ad4130_translate_pin(st, chan_info->iout1)); + + ret = regmap_write(st->regmap, AD4130_CHANNEL_X_REG(i), val); + if (ret) + return ret; + } + + return 0; +} + +static int ad4130_soft_reset(struct ad4130_state *st) +{ + int ret; + + ret = spi_write(st->spi, st->reset_buf, sizeof(st->reset_buf)); + if (ret) + return ret; + + fsleep(AD4130_RESET_SLEEP_US); + + return 0; +} + +static void ad4130_disable_regulators(void *data) +{ + struct ad4130_state *st = data; + + regulator_bulk_disable(ARRAY_SIZE(st->regulators), st->regulators); +} + +static int ad4130_probe(struct spi_device *spi) +{ + struct device *dev = &spi->dev; + struct iio_dev *indio_dev; + struct ad4130_state *st; + int ret; + + indio_dev = devm_iio_device_alloc(dev, sizeof(*st)); + if (!indio_dev) + return -ENOMEM; + + st = iio_priv(indio_dev); + + st->chip_info = device_get_match_data(dev); + + memset(st->reset_buf, 0xff, sizeof(st->reset_buf)); + init_completion(&st->completion); + mutex_init(&st->lock); + st->spi = spi; + + if (st->chip_info->has_fifo) { + /* + * Xfer: [ XFR1 ] [ XFR2 ] + * Master: 0x7D N ...................... + * Slave: ...... DATA1 DATA2 ... DATAN + */ + st->fifo_tx_buf[0] = AD4130_COMMS_READ_MASK | AD4130_FIFO_DATA_REG; + st->fifo_xfer[0].tx_buf = st->fifo_tx_buf; + st->fifo_xfer[0].len = sizeof(st->fifo_tx_buf); + st->fifo_xfer[1].rx_buf = st->fifo_rx_buf; + spi_message_init_with_transfers(&st->fifo_msg, st->fifo_xfer, + ARRAY_SIZE(st->fifo_xfer)); + } + + indio_dev->name = st->chip_info->name; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->info = st->chip_info->info; + + st->regmap = devm_regmap_init(dev, NULL, st, &ad4130_regmap_config); + if (IS_ERR(st->regmap)) + return PTR_ERR(st->regmap); + + st->regulators[0].supply = "avdd"; + st->regulators[1].supply = "iovdd"; + st->regulators[2].supply = "refin1"; + st->regulators[3].supply = "refin2"; + + ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(st->regulators), + st->regulators); + if (ret) + return dev_err_probe(dev, ret, "Failed to get regulators\n"); + + ret = regulator_bulk_enable(ARRAY_SIZE(st->regulators), st->regulators); + if (ret) + return dev_err_probe(dev, ret, "Failed to enable regulators\n"); + + ret = devm_add_action_or_reset(dev, ad4130_disable_regulators, st); + if (ret) + return ret; + + ret = ad4130_soft_reset(st); + if (ret) + return ret; + + ret = ad4310_parse_fw(indio_dev); + if (ret) + return ret; + + ret = ad4130_setup(indio_dev); + if (ret) + return ret; + + ret = ad4130_setup_int_clk(st); + if (ret) + return ret; + + ad4130_fill_scale_tbls(st); + + st->gc.owner = THIS_MODULE; + st->gc.label = st->chip_info->name; + st->gc.base = -1; + st->gc.ngpio = st->chip_info->num_gpios; + st->gc.parent = dev; + st->gc.can_sleep = true; + st->gc.init_valid_mask = ad4130_gpio_init_valid_mask; + st->gc.get_direction = ad4130_gpio_get_direction; + st->gc.set = ad4130_gpio_set; + + ret = devm_gpiochip_add_data(dev, &st->gc, st); + if (ret) + return ret; + + if (st->chip_info->has_fifo) + ret = devm_iio_kfifo_buffer_setup_ext(dev, indio_dev, + &ad4130_buffer_ops, + ad4130_fifo_attributes); + else + ret = ad4130_triggered_buffer_setup(indio_dev); + if (ret) + return ret; + + ret = devm_request_threaded_irq(dev, spi->irq, NULL, + ad4130_irq_handler, IRQF_ONESHOT, + indio_dev->name, indio_dev); + if (ret) + return dev_err_probe(dev, ret, "Failed to request irq\n"); + + if (st->chip_info->has_fifo) { + /* + * When the chip enters FIFO mode, IRQ polarity is inverted. + * When the chip exits FIFO mode, IRQ polarity returns to normal. + * See datasheet pages: 65, FIFO Watermark Interrupt section, + * and 71, Bit Descriptions for STATUS Register, RDYB. + * Cache the normal and inverted IRQ triggers to set them when + * entering and exiting FIFO mode. + */ + st->irq_trigger = irq_get_trigger_type(spi->irq); + if (st->irq_trigger & IRQF_TRIGGER_RISING) + st->inv_irq_trigger = IRQF_TRIGGER_FALLING; + else if (st->irq_trigger & IRQF_TRIGGER_FALLING) + st->inv_irq_trigger = IRQF_TRIGGER_RISING; + else + return dev_err_probe(dev, -EINVAL, "Invalid irq flags: %u\n", + st->irq_trigger); + } + + return devm_iio_device_register(dev, indio_dev); +} + +static const struct of_device_id ad4130_of_match[] = { + { + .compatible = "adi,ad4129-4", + .data = &ad4129_4_chip_info + }, + { + .compatible = "adi,ad4129-8", + .data = &ad4129_8_chip_info + }, + { + .compatible = "adi,ad4130-4", + .data = &ad4130_4_chip_info + }, + { + .compatible = "adi,ad4130", + .data = &ad4130_8_chip_info + }, + { + .compatible = "adi,ad4131-4", + .data = &ad4131_4_chip_info + }, + { + .compatible = "adi,ad4131-8", + .data = &ad4131_8_chip_info + }, + { } +}; +MODULE_DEVICE_TABLE(of, ad4130_of_match); + +static const struct spi_device_id ad4130_id_table[] = { + { .name = "ad4129-4", .driver_data = (kernel_ulong_t)&ad4129_4_chip_info }, + { .name = "ad4129-8", .driver_data = (kernel_ulong_t)&ad4129_8_chip_info }, + { .name = "ad4130-4", .driver_data = (kernel_ulong_t)&ad4130_4_chip_info }, + { .name = "ad4130", .driver_data = (kernel_ulong_t)&ad4130_8_chip_info }, + { .name = "ad4131-4", .driver_data = (kernel_ulong_t)&ad4131_4_chip_info }, + { .name = "ad4131-8", .driver_data = (kernel_ulong_t)&ad4131_8_chip_info }, + { } +}; +MODULE_DEVICE_TABLE(spi, ad4130_id_table); + +static struct spi_driver ad4130_driver = { + .driver = { + .name = AD4130_NAME, + .of_match_table = ad4130_of_match, + }, + .probe = ad4130_probe, + .id_table = ad4130_id_table, +}; +module_spi_driver(ad4130_driver); + +MODULE_AUTHOR("Cosmin Tanislav "); +MODULE_DESCRIPTION("Analog Devices AD4130 SPI driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/iio/adc/ad4134.c b/drivers/iio/adc/ad4134.c new file mode 100644 index 000000000..0490218bb --- /dev/null +++ b/drivers/iio/adc/ad4134.c @@ -0,0 +1,499 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2026 Analog Devices, Inc. + * Author: Marcelo Schmitt + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define AD4134_RESET_TIME_US (10 * USEC_PER_SEC) + +#define AD4134_REG_READ_MASK BIT(7) +#define AD4134_SPI_MAX_XFER_LEN 3 + +#define AD4134_EXT_CLOCK_MHZ (48 * HZ_PER_MHZ) + +#define AD4134_NUM_CHANNELS 4 +#define AD4134_CHAN_PRECISION_BITS 24 + +#define AD4134_IFACE_CONFIG_A_REG 0x00 +#define AD4134_IFACE_CONFIG_B_REG 0x01 +#define AD4134_IFACE_CONFIG_B_SINGLE_INSTR BIT(7) + +#define AD4134_DEVICE_CONFIG_REG 0x02 +#define AD4134_DEVICE_CONFIG_POWER_MODE_MASK BIT(0) +#define AD4134_POWER_MODE_HIGH_PERF 0x1 + +#define AD4134_SILICON_REV_REG 0x07 +#define AD4134_SCRATCH_PAD_REG 0x0A +#define AD4134_STREAM_MODE_REG 0x0E +#define AD4134_SDO_PIN_SRC_SEL_REG 0x10 +#define AD4134_SDO_PIN_SRC_SEL_SDO_SEL_MASK BIT(2) + +#define AD4134_DATA_PACKET_CONFIG_REG 0x11 +#define AD4134_DATA_PACKET_CONFIG_FRAME_MASK GENMASK(5, 4) +#define AD4134_DATA_PACKET_24BIT_FRAME 0x2 + +#define AD4134_DIG_IF_CFG_REG 0x12 +#define AD4134_DIF_IF_CFG_FORMAT_MASK GENMASK(1, 0) +#define AD4134_DATA_FORMAT_SINGLE_CH_MODE 0x0 + +#define AD4134_PW_DOWN_CTRL_REG 0x13 +#define AD4134_DEVICE_STATUS_REG 0x15 +#define AD4134_ODR_VAL_INT_LSB_REG 0x16 +#define AD4134_CH3_OFFSET_MSB_REG 0x3E +#define AD4134_AIN_OR_ERROR_REG 0x48 + +/* + * AD4134 register map ends at address 0x48 and there is no register for + * retrieving ADC sample data. Though, to make use of Linux regmap API both + * for register access and sample read, we define one virtual register for each + * ADC channel. AD4134_CH_VREG(x) maps a channel number to it's virtual register + * address while AD4134_VREG_CH(x) tells which channel given the address. + */ +#define AD4134_CH_VREG(x) ((x) + 0x50) +#define AD4134_VREG_CH(x) ((x) - 0x50) + +#define AD4134_SPI_CRC_POLYNOM 0x07 +#define AD4134_SPI_CRC_INIT_VALUE 0xA5 +static unsigned char ad4134_spi_crc_table[CRC8_TABLE_SIZE]; + +#define AD4134_CHANNEL(_index) { \ + .type = IIO_VOLTAGE, \ + .indexed = 1, \ + .channel = (_index), \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ +} + +static const struct iio_chan_spec ad4134_chan_set[] = { + AD4134_CHANNEL(0), + AD4134_CHANNEL(1), + AD4134_CHANNEL(2), + AD4134_CHANNEL(3), +}; + +struct ad4134_state { + struct spi_device *spi; + struct regmap *regmap; + unsigned long sys_clk_hz; + struct gpio_desc *odr_gpio; + int refin_mv; + /* + * DMA (thus cache coherency maintenance) requires the transfer buffers + * to live in their own cache lines. + */ + u8 rx_buf[AD4134_SPI_MAX_XFER_LEN] __aligned(IIO_DMA_MINALIGN); + u8 tx_buf[AD4134_SPI_MAX_XFER_LEN]; +}; + +static const struct regmap_range ad4134_regmap_rd_range[] = { + regmap_reg_range(AD4134_IFACE_CONFIG_A_REG, AD4134_SILICON_REV_REG), + regmap_reg_range(AD4134_SCRATCH_PAD_REG, AD4134_PW_DOWN_CTRL_REG), + regmap_reg_range(AD4134_DEVICE_STATUS_REG, AD4134_AIN_OR_ERROR_REG), + regmap_reg_range(AD4134_CH_VREG(0), AD4134_CH_VREG(AD4134_NUM_CHANNELS)), +}; + +static const struct regmap_range ad4134_regmap_wr_range[] = { + regmap_reg_range(AD4134_IFACE_CONFIG_A_REG, AD4134_DEVICE_CONFIG_REG), + regmap_reg_range(AD4134_SCRATCH_PAD_REG, AD4134_SCRATCH_PAD_REG), + regmap_reg_range(AD4134_STREAM_MODE_REG, AD4134_PW_DOWN_CTRL_REG), + regmap_reg_range(AD4134_ODR_VAL_INT_LSB_REG, AD4134_CH3_OFFSET_MSB_REG), +}; + +static const struct regmap_access_table ad4134_regmap_rd_table = { + .yes_ranges = ad4134_regmap_rd_range, + .n_yes_ranges = ARRAY_SIZE(ad4134_regmap_rd_range), +}; + +static const struct regmap_access_table ad4134_regmap_wr_table = { + .yes_ranges = ad4134_regmap_wr_range, + .n_yes_ranges = ARRAY_SIZE(ad4134_regmap_wr_range), +}; + +static int ad4134_calc_spi_crc(u8 inst, u8 data) +{ + u8 buf[] = { inst, data }; + + return crc8(ad4134_spi_crc_table, buf, ARRAY_SIZE(buf), + AD4134_SPI_CRC_INIT_VALUE); +} + +static void ad4134_prepare_spi_tx_buf(u8 inst, u8 data, u8 *buf) +{ + buf[0] = inst; + buf[1] = data; + buf[2] = ad4134_calc_spi_crc(inst, data); +} + +static int ad4134_reg_write(void *context, unsigned int reg, unsigned int val) +{ + struct ad4134_state *st = context; + struct spi_transfer xfer = { + .tx_buf = st->tx_buf, + .rx_buf = st->rx_buf, + .len = AD4134_SPI_MAX_XFER_LEN, + }; + int ret; + + ad4134_prepare_spi_tx_buf(reg, val, st->tx_buf); + + ret = spi_sync_transfer(st->spi, &xfer, 1); + if (ret) + return ret; + + if (st->rx_buf[2] != st->tx_buf[2]) + dev_dbg(&st->spi->dev, "reg write CRC check failed\n"); + + return 0; +} + +static int ad4134_data_read(struct ad4134_state *st, unsigned int reg, + unsigned int *val) +{ + unsigned int i; + int ret; + + /* + * To be able to read data from all 4 channels through a single line, we + * set DOUTx output format to 0 in the digital interface config register + * (0x12). With that, data from all four channels is serialized and + * output on DOUT0. During the probe, we also set SDO_PIN_SRC_SEL in + * DEVICE_CONFIG_1 register to duplicate DOUT0 on the SDO pin. Combined, + * those configurations enable ADC data read through a conventional SPI + * interface. Now we read data from all channels but keep only the bits + * from the requested one. + */ + for (i = 0; i < ARRAY_SIZE(ad4134_chan_set); i++) { + ret = spi_write_then_read(st->spi, NULL, 0, st->rx_buf, + BITS_TO_BYTES(AD4134_CHAN_PRECISION_BITS)); + if (ret) + return ret; + + /* + * AD4134 has a built-in feature that flags when data transfers + * don't run enough clock cycles to read the entire data frame. + * Clock out data from all channels to avoid that. + */ + if (i == AD4134_VREG_CH(reg)) + *val = get_unaligned_be24(st->rx_buf); + } + + return 0; +} + +static int ad4134_register_read(struct ad4134_state *st, unsigned int reg, + unsigned int *val) +{ + struct spi_transfer xfer = { + .tx_buf = st->tx_buf, + .rx_buf = st->rx_buf, + .len = AD4134_SPI_MAX_XFER_LEN, + }; + unsigned int inst; + int ret; + + inst = AD4134_REG_READ_MASK | reg; + ad4134_prepare_spi_tx_buf(inst, 0, st->tx_buf); + + ret = spi_sync_transfer(st->spi, &xfer, 1); + if (ret) + return ret; + + *val = st->rx_buf[1]; + + /* Check CRC */ + if (st->rx_buf[2] != st->tx_buf[2]) + dev_dbg(&st->spi->dev, "reg read CRC check failed\n"); + + return 0; +} + +static int ad4134_reg_read(void *context, unsigned int reg, unsigned int *val) +{ + struct ad4134_state *st = context; + + if (reg >= AD4134_CH_VREG(0)) + return ad4134_data_read(st, reg, val); + + return ad4134_register_read(st, reg, val); +} + +static const struct regmap_config ad4134_regmap_config = { + .reg_read = ad4134_reg_read, + .reg_write = ad4134_reg_write, + .rd_table = &ad4134_regmap_rd_table, + .wr_table = &ad4134_regmap_wr_table, + .max_register = AD4134_CH_VREG(ARRAY_SIZE(ad4134_chan_set)), +}; + +static int ad4134_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long info) +{ + struct ad4134_state *st = iio_priv(indio_dev); + int ret; + + switch (info) { + case IIO_CHAN_INFO_RAW: + gpiod_set_value_cansleep(st->odr_gpio, 1); + /* + * For slave mode gated DCLK (data sheet page 11), the minimum + * ODR high time is 3 * tDIGCLK. The internal digital clock + * period is tDIGCLK = 1/fDIGCLK = 2/fSYSCLK. + * The System clock frequency (fSYSCLK) is typically 48 MHz. + * Thus, ODR high time = 3 * (2 / (48 * HZ_PER_MHZ)) + * ODR high time = 0.000000125 s = 125 ns + * 1 micro second should be more than enough. Not worth it + * tweaking for shorter dealy since this is not a fast data path. + */ + fsleep(1); + gpiod_set_value_cansleep(st->odr_gpio, 0); + ret = regmap_read(st->regmap, AD4134_CH_VREG(chan->channel), val); + if (ret) + return ret; + + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + *val = st->refin_mv; + *val2 = AD4134_CHAN_PRECISION_BITS - 1; + + return IIO_VAL_FRACTIONAL_LOG2; + default: + return -EINVAL; + } +} + +static int ad4134_debugfs_reg_access(struct iio_dev *indio_dev, + unsigned int reg, unsigned int writeval, + unsigned int *readval) +{ + struct ad4134_state *st = iio_priv(indio_dev); + + if (readval) + return regmap_read(st->regmap, reg, readval); + + return regmap_write(st->regmap, reg, writeval); +} + +static int ad4134_min_io_mode_setup(struct ad4134_state *st) +{ + struct device *dev = &st->spi->dev; + int ret; + + st->odr_gpio = devm_gpiod_get(dev, "odr", GPIOD_OUT_LOW); + if (IS_ERR(st->odr_gpio)) + return dev_err_probe(dev, PTR_ERR(st->odr_gpio), + "failed to get ODR GPIO\n"); + + ret = regmap_update_bits(st->regmap, AD4134_DIG_IF_CFG_REG, + AD4134_DIF_IF_CFG_FORMAT_MASK, + FIELD_PREP(AD4134_DIF_IF_CFG_FORMAT_MASK, + AD4134_DATA_FORMAT_SINGLE_CH_MODE)); + if (ret) + return dev_err_probe(dev, ret, + "failed to set single channel mode\n"); + + ret = regmap_set_bits(st->regmap, AD4134_SDO_PIN_SRC_SEL_REG, + AD4134_SDO_PIN_SRC_SEL_SDO_SEL_MASK); + if (ret) + return dev_err_probe(dev, ret, + "failed to set SDO source selection\n"); + + return regmap_set_bits(st->regmap, AD4134_IFACE_CONFIG_B_REG, + AD4134_IFACE_CONFIG_B_SINGLE_INSTR); +} + +static const struct iio_info ad4134_info = { + .read_raw = ad4134_read_raw, + .debugfs_reg_access = ad4134_debugfs_reg_access, +}; + +static const char * const ad4143_required_regulators[] = { + "avdd5", "dvdd5", "iovdd", +}; + +static const char * const ad4143_optional_regulators[] = { + "avdd1v8", "dvdd1v8", "clkvdd", +}; + +static int ad4134_regulator_setup(struct ad4134_state *st) +{ + struct device *dev = &st->spi->dev; + int ret; + + ret = devm_regulator_bulk_get_enable(dev, ARRAY_SIZE(ad4143_required_regulators), + ad4143_required_regulators); + if (ret) + return dev_err_probe(dev, ret, "failed to enable power supplies\n"); + + /* Required regulator that we need to read the voltage */ + ret = devm_regulator_get_enable_read_voltage(dev, "refin"); + if (ret < 0) + return dev_err_probe(dev, ret, "failed to get REFIN voltage.\n"); + + st->refin_mv = ret / (MICRO / MILLI); + + ret = devm_regulator_get_enable_optional(dev, "ldoin"); + if (ret < 0 && ret != -ENODEV) + return dev_err_probe(dev, ret, "failed to enable ldoin supply\n"); + + /* If ldoin was provided, then use the use the internal LDO regulators */ + if (ret == 0) + return 0; + + /* + * If ldoin is not provided, then avdd1v8, dvdd1v8, and clkvdd are + * required. + */ + ret = devm_regulator_bulk_get_enable(dev, ARRAY_SIZE(ad4143_optional_regulators), + ad4143_optional_regulators); + if (ret) + return dev_err_probe(dev, ret, "failed to enable 1V8 power supplies\n"); + + return 0; +} + +static int ad4134_clock_select(struct ad4134_state *st) +{ + struct device *dev = &st->spi->dev; + struct clk *xtal_clk, *clkin_clk; + + /* + * AD4134 requires one external clock source and only one external clock + * source can be provided at a time. Try to get a crystal provided clock. + * If that fails, try to get a CMOS clock. + */ + xtal_clk = devm_clk_get_optional_enabled(dev, "xtal"); + if (!xtal_clk) + xtal_clk = devm_clk_get_optional_enabled(dev, "xtal"); + if (IS_ERR(xtal_clk)) + return dev_err_probe(dev, PTR_ERR(xtal_clk), + "failed to get xtal\n"); + + clkin_clk = devm_clk_get_optional_enabled(dev, "clkin"); + if (!clkin_clk) + clkin_clk = devm_clk_get_optional_enabled(dev, "clkin"); + if (IS_ERR(clkin_clk)) + return dev_err_probe(dev, PTR_ERR(clkin_clk), + "failed to get clkin\n"); + + st->sys_clk_hz = clk_get_rate(xtal_clk) | clk_get_rate(clkin_clk); + if (st->sys_clk_hz != AD4134_EXT_CLOCK_MHZ) + dev_warn(dev, "invalid external clock frequency %lu\n", + st->sys_clk_hz); + + return 0; +} + +static int ad4134_probe(struct spi_device *spi) +{ + struct device *dev = &spi->dev; + struct reset_control *rst; + struct iio_dev *indio_dev; + struct ad4134_state *st; + int ret; + + indio_dev = devm_iio_device_alloc(dev, sizeof(*st)); + if (!indio_dev) + return -ENOMEM; + + st = iio_priv(indio_dev); + st->spi = spi; + + indio_dev->name = "ad4134"; + indio_dev->channels = ad4134_chan_set; + indio_dev->num_channels = ARRAY_SIZE(ad4134_chan_set); + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->info = &ad4134_info; + + ret = ad4134_regulator_setup(st); + if (ret) + return ret; + + ret = ad4134_clock_select(st); + if (ret) + return ret; + + rst = devm_reset_control_get_optional_exclusive_deasserted(dev, NULL); + if (IS_ERR(rst)) + return dev_err_probe(dev, PTR_ERR(rst), + "failed to get and deassert reset\n"); + + crc8_populate_msb(ad4134_spi_crc_table, AD4134_SPI_CRC_POLYNOM); + + st->regmap = devm_regmap_init(dev, NULL, st, &ad4134_regmap_config); + if (IS_ERR(st->regmap)) + return dev_err_probe(dev, PTR_ERR(st->regmap), + "failed to initialize regmap"); + + ret = ad4134_min_io_mode_setup(st); + if (ret) + return dev_err_probe(dev, ret, + "failed to setup minimum I/O mode\n"); + + /* Bump precision to 24-bit */ + ret = regmap_update_bits(st->regmap, AD4134_DATA_PACKET_CONFIG_REG, + AD4134_DATA_PACKET_CONFIG_FRAME_MASK, + FIELD_PREP(AD4134_DATA_PACKET_CONFIG_FRAME_MASK, + AD4134_DATA_PACKET_24BIT_FRAME)); + if (ret) + return ret; + + /* Set high performance power mode */ + ret = regmap_update_bits(st->regmap, AD4134_DEVICE_CONFIG_REG, + AD4134_DEVICE_CONFIG_POWER_MODE_MASK, + FIELD_PREP(AD4134_DEVICE_CONFIG_POWER_MODE_MASK, + AD4134_POWER_MODE_HIGH_PERF)); + if (ret) + return ret; + + return devm_iio_device_register(dev, indio_dev); +} + +static const struct spi_device_id ad4134_id[] = { + { .name = "ad4134" }, + { } +}; +MODULE_DEVICE_TABLE(spi, ad4134_id); + +static const struct of_device_id ad4134_of_match[] = { + { .compatible = "adi,ad4134" }, + { } +}; +MODULE_DEVICE_TABLE(of, ad4134_of_match); + +static struct spi_driver ad4134_driver = { + .driver = { + .name = "ad4134", + .of_match_table = ad4134_of_match, + }, + .probe = ad4134_probe, + .id_table = ad4134_id, +}; +module_spi_driver(ad4134_driver); + +MODULE_AUTHOR("Marcelo Schmitt "); +MODULE_DESCRIPTION("Analog Devices AD4134 SPI driver"); +MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS("IIO_AD4134"); diff --git a/drivers/iio/adc/ad4170-4.c b/drivers/iio/adc/ad4170-4.c new file mode 100644 index 000000000..f7757b3a9 --- /dev/null +++ b/drivers/iio/adc/ad4170-4.c @@ -0,0 +1,3010 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Analog Devices AD4170-4 ADC driver + * + * Copyright (C) 2025 Analog Devices, Inc. + * Author: Ana-Maria Cusco + * Author: Marcelo Schmitt + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * AD4170 registers + * Multibyte register addresses point to the most significant byte which is the + * address to use to get the most significant byte first (address accessed is + * decremented by one for each data byte) + * + * Each register address define follows the AD4170__REG format. + * Each mask follows the AD4170__ format. + * E.g. AD4170_PIN_MUXING_DIG_AUX1_CTRL_MSK is for accessing DIG_AUX1_CTRL field + * of PIN_MUXING_REG. + * Each constant follows the AD4170___ format. + * E.g. AD4170_PIN_MUXING_DIG_AUX1_DISABLED is the value written to + * DIG_AUX1_CTRL field of PIN_MUXING register to disable DIG_AUX1 pin. + * Some register names and register field names are shortened versions of + * their datasheet counterpart names to provide better code readability. + */ +#define AD4170_CONFIG_A_REG 0x00 +#define AD4170_DATA_24B_REG 0x1E +#define AD4170_PIN_MUXING_REG 0x69 +#define AD4170_CLOCK_CTRL_REG 0x6B +#define AD4170_ADC_CTRL_REG 0x71 +#define AD4170_CHAN_EN_REG 0x79 +#define AD4170_CHAN_SETUP_REG(x) (0x81 + 4 * (x)) +#define AD4170_CHAN_MAP_REG(x) (0x83 + 4 * (x)) +#define AD4170_MISC_REG(x) (0xC1 + 14 * (x)) +#define AD4170_AFE_REG(x) (0xC3 + 14 * (x)) +#define AD4170_FILTER_REG(x) (0xC5 + 14 * (x)) +#define AD4170_FILTER_FS_REG(x) (0xC7 + 14 * (x)) +#define AD4170_OFFSET_REG(x) (0xCA + 14 * (x)) +#define AD4170_GAIN_REG(x) (0xCD + 14 * (x)) +#define AD4170_V_BIAS_REG 0x135 +#define AD4170_CURRENT_SRC_REG(x) (0x139 + 2 * (x)) +#define AD4170_GPIO_MODE_REG 0x191 +#define AD4170_GPIO_OUTPUT_REG 0x193 +#define AD4170_GPIO_INPUT_REG 0x195 +#define AD4170_ADC_CTRL_CONT_READ_EXIT_REG 0x200 /* virtual reg */ + +#define AD4170_REG_READ_MASK BIT(14) + +/* AD4170_CONFIG_A_REG - INTERFACE_CONFIG_A REGISTER */ +#define AD4170_SW_RESET_MSK (BIT(7) | BIT(0)) + +/* AD4170_PIN_MUXING_REG */ +#define AD4170_PIN_MUXING_DIG_AUX1_CTRL_MSK GENMASK(5, 4) + +/* AD4170_CLOCK_CTRL_REG */ +#define AD4170_CLOCK_CTRL_CLOCKSEL_MSK GENMASK(1, 0) + +/* AD4170_ADC_CTRL_REG */ +#define AD4170_ADC_CTRL_MULTI_DATA_REG_SEL_MSK BIT(7) +#define AD4170_ADC_CTRL_CONT_READ_MSK GENMASK(5, 4) +#define AD4170_ADC_CTRL_MODE_MSK GENMASK(3, 0) + +/* AD4170_CHAN_EN_REG */ +#define AD4170_CHAN_EN(ch) BIT(ch) + +/* AD4170_CHAN_SETUP_REG */ +#define AD4170_CHAN_SETUP_SETUP_MSK GENMASK(2, 0) + +/* AD4170_CHAN_MAP_REG */ +#define AD4170_CHAN_MAP_AINP_MSK GENMASK(12, 8) +#define AD4170_CHAN_MAP_AINM_MSK GENMASK(4, 0) + +/* AD4170_MISC_REG */ +#define AD4170_MISC_CHOP_IEXC_MSK GENMASK(15, 14) +#define AD4170_MISC_CHOP_ADC_MSK GENMASK(9, 8) + +/* AD4170_AFE_REG */ +#define AD4170_AFE_REF_BUF_M_MSK GENMASK(11, 10) +#define AD4170_AFE_REF_BUF_P_MSK GENMASK(9, 8) +#define AD4170_AFE_REF_SELECT_MSK GENMASK(6, 5) +#define AD4170_AFE_BIPOLAR_MSK BIT(4) +#define AD4170_AFE_PGA_GAIN_MSK GENMASK(3, 0) + +/* AD4170_FILTER_REG */ +#define AD4170_FILTER_FILTER_TYPE_MSK GENMASK(3, 0) + +/* AD4170_CURRENT_SRC_REG */ +#define AD4170_CURRENT_SRC_I_OUT_PIN_MSK GENMASK(12, 8) +#define AD4170_CURRENT_SRC_I_OUT_VAL_MSK GENMASK(2, 0) + +/* AD4170_GPIO_MODE_REG */ +#define AD4170_GPIO_MODE_GPIO0_MSK GENMASK(1, 0) +#define AD4170_GPIO_MODE_GPIO1_MSK GENMASK(3, 2) +#define AD4170_GPIO_MODE_GPIO2_MSK GENMASK(5, 4) +#define AD4170_GPIO_MODE_GPIO3_MSK GENMASK(7, 6) + +/* AD4170_GPIO_OUTPUT_REG */ +#define AD4170_GPIO_OUTPUT_GPIO_MSK(x) BIT(x) + +/* AD4170 register constants */ + +/* AD4170_CLOCK_CTRL_REG constants */ +#define AD4170_CLOCK_CTRL_CLOCKSEL_INT 0x0 +#define AD4170_CLOCK_CTRL_CLOCKSEL_INT_OUT 0x1 +#define AD4170_CLOCK_CTRL_CLOCKSEL_EXT 0x2 +#define AD4170_CLOCK_CTRL_CLOCKSEL_EXT_XTAL 0x3 + +/* AD4170_CHAN_MAP_REG constants */ +#define AD4170_CHAN_MAP_AIN(x) (x) +#define AD4170_CHAN_MAP_TEMP_SENSOR 17 +#define AD4170_CHAN_MAP_AVDD_AVSS_P 18 +#define AD4170_CHAN_MAP_AVDD_AVSS_N 18 +#define AD4170_CHAN_MAP_IOVDD_DGND_P 19 +#define AD4170_CHAN_MAP_IOVDD_DGND_N 19 +#define AD4170_CHAN_MAP_AVSS 23 +#define AD4170_CHAN_MAP_DGND 24 +#define AD4170_CHAN_MAP_REFIN1_P 25 +#define AD4170_CHAN_MAP_REFIN1_N 26 +#define AD4170_CHAN_MAP_REFIN2_P 27 +#define AD4170_CHAN_MAP_REFIN2_N 28 +#define AD4170_CHAN_MAP_REFOUT 29 + +/* AD4170_MISC_REG constants */ +#define AD4170_MISC_CHOP_IEXC_PAIR1 0x1 +#define AD4170_MISC_CHOP_IEXC_PAIR2 0x2 +#define AD4170_MISC_CHOP_IEXC_BOTH 0x3 + +/* AD4170_PIN_MUXING_REG constants */ +#define AD4170_PIN_MUXING_DIG_AUX1_DISABLED 0x0 +#define AD4170_PIN_MUXING_DIG_AUX1_RDY 0x1 + +/* AD4170_ADC_CTRL_REG constants */ +#define AD4170_ADC_CTRL_MODE_CONT 0x0 +#define AD4170_ADC_CTRL_MODE_SINGLE 0x4 +#define AD4170_ADC_CTRL_MODE_IDLE 0x7 + +#define AD4170_ADC_CTRL_CONT_READ_DISABLE 0x0 +#define AD4170_ADC_CTRL_CONT_READ_ENABLE 0x1 + +/* AD4170_FILTER_REG constants */ +#define AD4170_FILTER_FILTER_TYPE_SINC5_AVG 0x0 +#define AD4170_FILTER_FILTER_TYPE_SINC5 0x4 +#define AD4170_FILTER_FILTER_TYPE_SINC3 0x6 + +/* AD4170_CURRENT_SRC_REG constants */ +#define AD4170_CURRENT_SRC_I_OUT_PIN_AIN(x) (x) +#define AD4170_CURRENT_SRC_I_OUT_PIN_GPIO(x) ((x) + 17) + +/* AD4170_GPIO_MODE_REG constants */ +#define AD4170_GPIO_MODE_GPIO_INPUT 1 +#define AD4170_GPIO_MODE_GPIO_OUTPUT 2 + +/* Device properties and auxiliary constants */ + +#define AD4170_NUM_ANALOG_PINS 9 +#define AD4170_NUM_GPIO_PINS 4 +#define AD4170_MAX_ADC_CHANNELS 16 +#define AD4170_MAX_IIO_CHANNELS (AD4170_MAX_ADC_CHANNELS + 1) +#define AD4170_MAX_ANALOG_PINS 8 +#define AD4170_MAX_SETUPS 8 +#define AD4170_INVALID_SETUP 9 +#define AD4170_SPI_INST_PHASE_LEN 2 +#define AD4170_SPI_MAX_XFER_LEN 6 +#define AD4170_NUM_CURRENT_SRC 4 +#define AD4170_DEFAULT_SAMP_RATE (125 * HZ_PER_KHZ) + +#define AD4170_INT_REF_2_5V 2500000 + +/* Internal and external clock properties */ +#define AD4170_INT_CLOCK_16MHZ (16 * HZ_PER_MHZ) +#define AD4170_EXT_CLOCK_MHZ_MIN (1 * HZ_PER_MHZ) +#define AD4170_EXT_CLOCK_MHZ_MAX (17 * HZ_PER_MHZ) + +#define AD4170_NUM_PGA_OPTIONS 10 + +/* Digital filter properties */ +#define AD4170_SINC3_MIN_FS 4 +#define AD4170_SINC3_MAX_FS 65532 +#define AD4170_SINC5_MIN_FS 1 +#define AD4170_SINC5_MAX_FS 256 + +#define AD4170_GAIN_REG_DEFAULT 0x555555 + +#define AD4170_ADC_CTRL_CONT_READ_EXIT 0xA5 + +/* Analog pin functions */ +#define AD4170_PIN_UNASSIGNED 0x00 +#define AD4170_PIN_ANALOG_IN 0x01 +#define AD4170_PIN_CURRENT_OUT 0x02 +#define AD4170_PIN_VBIAS 0x04 + +/* GPIO pin functions */ +#define AD4170_GPIO_UNASSIGNED 0x00 +#define AD4170_GPIO_AC_EXCITATION 0x02 +#define AD4170_GPIO_OUTPUT 0x04 + +/* Current source */ +#define AD4170_CURRENT_SRC_DISABLED 0xFF + +static const unsigned int ad4170_reg_size[] = { + [AD4170_CONFIG_A_REG] = 1, + [AD4170_DATA_24B_REG] = 3, + [AD4170_PIN_MUXING_REG] = 2, + [AD4170_CLOCK_CTRL_REG] = 2, + [AD4170_ADC_CTRL_REG] = 2, + [AD4170_CHAN_EN_REG] = 2, + /* + * CHANNEL_SETUP and CHANNEL_MAP register are all 2 byte size each and + * their addresses are interleaved such that we have CHANNEL_SETUP0 + * address followed by CHANNEL_MAP0 address, followed by CHANNEL_SETUP1, + * and so on until CHANNEL_MAP15. + * Thus, initialize the register size for them only once. + */ + [AD4170_CHAN_SETUP_REG(0) ... AD4170_CHAN_MAP_REG(AD4170_MAX_ADC_CHANNELS - 1)] = 2, + /* + * MISC, AFE, FILTER, FILTER_FS, OFFSET, and GAIN register addresses are + * also interleaved but MISC, AFE, FILTER, FILTER_FS, OFFSET are 16-bit + * while OFFSET, GAIN are 24-bit registers so we can't init them all to + * the same size. + */ + [AD4170_MISC_REG(0) ... AD4170_FILTER_FS_REG(0)] = 2, + [AD4170_MISC_REG(1) ... AD4170_FILTER_FS_REG(1)] = 2, + [AD4170_MISC_REG(2) ... AD4170_FILTER_FS_REG(2)] = 2, + [AD4170_MISC_REG(3) ... AD4170_FILTER_FS_REG(3)] = 2, + [AD4170_MISC_REG(4) ... AD4170_FILTER_FS_REG(4)] = 2, + [AD4170_MISC_REG(5) ... AD4170_FILTER_FS_REG(5)] = 2, + [AD4170_MISC_REG(6) ... AD4170_FILTER_FS_REG(6)] = 2, + [AD4170_MISC_REG(7) ... AD4170_FILTER_FS_REG(7)] = 2, + [AD4170_OFFSET_REG(0) ... AD4170_GAIN_REG(0)] = 3, + [AD4170_OFFSET_REG(1) ... AD4170_GAIN_REG(1)] = 3, + [AD4170_OFFSET_REG(2) ... AD4170_GAIN_REG(2)] = 3, + [AD4170_OFFSET_REG(3) ... AD4170_GAIN_REG(3)] = 3, + [AD4170_OFFSET_REG(4) ... AD4170_GAIN_REG(4)] = 3, + [AD4170_OFFSET_REG(5) ... AD4170_GAIN_REG(5)] = 3, + [AD4170_OFFSET_REG(6) ... AD4170_GAIN_REG(6)] = 3, + [AD4170_OFFSET_REG(7) ... AD4170_GAIN_REG(7)] = 3, + [AD4170_V_BIAS_REG] = 2, + [AD4170_CURRENT_SRC_REG(0) ... AD4170_CURRENT_SRC_REG(3)] = 2, + [AD4170_GPIO_MODE_REG] = 2, + [AD4170_GPIO_OUTPUT_REG] = 2, + [AD4170_GPIO_INPUT_REG] = 2, + [AD4170_ADC_CTRL_CONT_READ_EXIT_REG] = 0, +}; + +enum ad4170_ref_buf { + AD4170_REF_BUF_PRE, /* Pre-charge reference buffer */ + AD4170_REF_BUF_FULL, /* Full reference buffering */ + AD4170_REF_BUF_BYPASS, /* Bypass reference buffering */ +}; + +/* maps adi,positive/negative-reference-buffer property values to enum */ +static const char * const ad4170_ref_buf_str[] = { + [AD4170_REF_BUF_PRE] = "precharge", + [AD4170_REF_BUF_FULL] = "full", + [AD4170_REF_BUF_BYPASS] = "disabled", +}; + +enum ad4170_ref_select { + AD4170_REF_REFIN1, + AD4170_REF_REFIN2, + AD4170_REF_REFOUT, + AD4170_REF_AVDD, +}; + +enum ad4170_filter_type { + AD4170_SINC5_AVG, + AD4170_SINC5, + AD4170_SINC3, +}; + +enum ad4170_regulator { + AD4170_AVDD_SUP, + AD4170_AVSS_SUP, + AD4170_IOVDD_SUP, + AD4170_REFIN1P_SUP, + AD4170_REFIN1N_SUP, + AD4170_REFIN2P_SUP, + AD4170_REFIN2N_SUP, + AD4170_MAX_SUP, +}; + +static const char *const ad4170_clk_sel[] = { + "ext-clk", "xtal", +}; + +enum ad4170_int_pin_sel { + AD4170_INT_PIN_SDO, + AD4170_INT_PIN_DIG_AUX1, +}; + +static const char * const ad4170_int_pin_names[] = { + [AD4170_INT_PIN_SDO] = "sdo", + [AD4170_INT_PIN_DIG_AUX1] = "dig_aux1", +}; + +static const unsigned int ad4170_sinc3_filt_fs_tbl[] = { + 4, 8, 12, 16, 20, 40, 48, 80, /* 0 - 7 */ + 100, 256, 500, 1000, 5000, 8332, 10000, 25000, /* 8 - 15 */ + 50000, 65532, /* 16 - 17 */ +}; + +#define AD4170_MAX_FS_TBL_SIZE ARRAY_SIZE(ad4170_sinc3_filt_fs_tbl) + +static const unsigned int ad4170_sinc5_filt_fs_tbl[] = { + 1, 2, 4, 8, 12, 16, 20, 40, 48, 80, 100, 256, +}; + +static const unsigned int ad4170_iout_pin_tbl[] = { + AD4170_CURRENT_SRC_I_OUT_PIN_AIN(0), + AD4170_CURRENT_SRC_I_OUT_PIN_AIN(1), + AD4170_CURRENT_SRC_I_OUT_PIN_AIN(2), + AD4170_CURRENT_SRC_I_OUT_PIN_AIN(3), + AD4170_CURRENT_SRC_I_OUT_PIN_AIN(4), + AD4170_CURRENT_SRC_I_OUT_PIN_AIN(5), + AD4170_CURRENT_SRC_I_OUT_PIN_AIN(6), + AD4170_CURRENT_SRC_I_OUT_PIN_AIN(7), + AD4170_CURRENT_SRC_I_OUT_PIN_AIN(8), + AD4170_CURRENT_SRC_I_OUT_PIN_GPIO(0), + AD4170_CURRENT_SRC_I_OUT_PIN_GPIO(1), + AD4170_CURRENT_SRC_I_OUT_PIN_GPIO(2), + AD4170_CURRENT_SRC_I_OUT_PIN_GPIO(3), +}; + +static const unsigned int ad4170_iout_current_ua_tbl[] = { + 0, 10, 50, 100, 250, 500, 1000, 1500, +}; + +enum ad4170_sensor_enum { + AD4170_ADC_SENSOR = 0, + AD4170_WEIGH_SCALE_SENSOR = 1, + AD4170_RTD_SENSOR = 2, + AD4170_THERMOCOUPLE_SENSOR = 3, +}; + +/* maps adi,sensor-type property value to enum */ +static const char * const ad4170_sensor_type[] = { + [AD4170_ADC_SENSOR] = "adc", + [AD4170_WEIGH_SCALE_SENSOR] = "weighscale", + [AD4170_RTD_SENSOR] = "rtd", + [AD4170_THERMOCOUPLE_SENSOR] = "thermocouple", +}; + +struct ad4170_chip_info { + const char *name; +}; + +static const struct ad4170_chip_info ad4170_chip_info = { + .name = "ad4170-4", +}; + +static const struct ad4170_chip_info ad4190_chip_info = { + .name = "ad4190-4", +}; + +static const struct ad4170_chip_info ad4195_chip_info = { + .name = "ad4195-4", +}; + +/* + * There are 8 of each MISC, AFE, FILTER, FILTER_FS, OFFSET, and GAIN + * configuration registers. That is, there are 8 miscellaneous registers, MISC0 + * to MISC7. Each MISC register is associated with a setup; MISCN is associated + * with setup number N. The other 5 above mentioned types of registers have + * analogous structure. A setup is a set of those registers. For example, + * setup 1 comprises of MISC1, AFE1, FILTER1, FILTER_FS1, OFFSET1, and GAIN1 + * registers. Also, there are 16 CHANNEL_SETUP registers (CHANNEL_SETUP0 to + * CHANNEL_SETUP15). Each channel setup is associated with one of the 8 possible + * setups. Thus, AD4170 can support up to 16 channels but, since there are only + * 8 available setups, channels must share settings if more than 8 channels are + * configured. + * + * If this struct is modified, ad4170_setup_eq() will probably need to be + * updated too. + */ +struct ad4170_setup { + u16 misc; + u16 afe; + u16 filter; + u16 filter_fs; + u32 offset; /* For calibration purposes */ + u32 gain; /* For calibration purposes */ +}; + +struct ad4170_setup_info { + struct ad4170_setup setup; + unsigned int enabled_channels; + unsigned int channels; +}; + +struct ad4170_chan_info { + unsigned int input_range_uv; + unsigned int setup_num; /* Index to access state setup_infos array */ + struct ad4170_setup setup; /* cached setup */ + int offset_tbl[10]; + u32 scale_tbl[10][2]; + bool initialized; + bool enabled; +}; + +static const char * const ad4170_filt_names[] = { + [AD4170_SINC5_AVG] = "sinc5+avg", + [AD4170_SINC5] = "sinc5", + [AD4170_SINC3] = "sinc3", +}; + +struct ad4170_state { + struct mutex lock; /* Protect read-modify-write and multi write sequences */ + int vrefs_uv[AD4170_MAX_SUP]; + u32 mclk_hz; + struct ad4170_setup_info setup_infos[AD4170_MAX_SETUPS]; + struct ad4170_chan_info chan_infos[AD4170_MAX_ADC_CHANNELS]; + struct completion completion; + struct iio_chan_spec chans[AD4170_MAX_IIO_CHANNELS]; + struct spi_device *spi; + struct regmap *regmap; + int sps_tbl[ARRAY_SIZE(ad4170_filt_names)][AD4170_MAX_FS_TBL_SIZE][2]; + __be32 bounce_buffer[AD4170_MAX_ADC_CHANNELS]; + struct spi_message msg; + struct spi_transfer xfer; + struct iio_trigger *trig; + struct clk_hw int_clk_hw; + unsigned int clock_ctrl; + unsigned int pins_fn[AD4170_NUM_ANALOG_PINS]; + int gpio_fn[AD4170_NUM_GPIO_PINS]; + unsigned int cur_src_pins[AD4170_NUM_CURRENT_SRC]; + struct gpio_chip gpiochip; + /* + * DMA (thus cache coherency maintenance) requires the transfer buffers + * to live in their own cache lines. + */ + u8 rx_buf[4] __aligned(IIO_DMA_MINALIGN); +}; + +static void ad4170_fill_sps_tbl(struct ad4170_state *st) +{ + unsigned int tmp0, tmp1, i; + + /* + * The ODR can be calculated the same way for sinc5+avg, sinc5, and + * sinc3 filter types with the exception that sinc5 filter has a + * narrowed range of allowed FILTER_FS values. + */ + for (i = 0; i < ARRAY_SIZE(ad4170_sinc3_filt_fs_tbl); i++) { + tmp0 = div_u64_rem(st->mclk_hz, 32 * ad4170_sinc3_filt_fs_tbl[i], + &tmp1); + tmp1 = mult_frac(tmp1, MICRO, 32 * ad4170_sinc3_filt_fs_tbl[i]); + /* Fill sinc5+avg filter SPS table */ + st->sps_tbl[AD4170_SINC5_AVG][i][0] = tmp0; /* Integer part */ + st->sps_tbl[AD4170_SINC5_AVG][i][1] = tmp1; /* Fractional part */ + + /* Fill sinc3 filter SPS table */ + st->sps_tbl[AD4170_SINC3][i][0] = tmp0; /* Integer part */ + st->sps_tbl[AD4170_SINC3][i][1] = tmp1; /* Fractional part */ + } + /* Sinc5 filter ODR doesn't use all FILTER_FS bits */ + for (i = 0; i < ARRAY_SIZE(ad4170_sinc5_filt_fs_tbl); i++) { + tmp0 = div_u64_rem(st->mclk_hz, 32 * ad4170_sinc5_filt_fs_tbl[i], + &tmp1); + tmp1 = mult_frac(tmp1, MICRO, 32 * ad4170_sinc5_filt_fs_tbl[i]); + /* Fill sinc5 filter SPS table */ + st->sps_tbl[AD4170_SINC5][i][0] = tmp0; /* Integer part */ + st->sps_tbl[AD4170_SINC5][i][1] = tmp1; /* Fractional part */ + } +} + +static int ad4170_debugfs_reg_access(struct iio_dev *indio_dev, + unsigned int reg, unsigned int writeval, + unsigned int *readval) +{ + struct ad4170_state *st = iio_priv(indio_dev); + + if (readval) + return regmap_read(st->regmap, reg, readval); + + return regmap_write(st->regmap, reg, writeval); +} + +static int ad4170_get_reg_size(struct ad4170_state *st, unsigned int reg, + unsigned int *size) +{ + if (reg >= ARRAY_SIZE(ad4170_reg_size)) + return -EINVAL; + + *size = ad4170_reg_size[reg]; + + return 0; +} + +static int ad4170_reg_write(void *context, unsigned int reg, unsigned int val) +{ + struct ad4170_state *st = context; + u8 tx_buf[AD4170_SPI_MAX_XFER_LEN]; + unsigned int size; + int ret; + + ret = ad4170_get_reg_size(st, reg, &size); + if (ret) + return ret; + + put_unaligned_be16(reg, tx_buf); + switch (size) { + case 3: + put_unaligned_be24(val, &tx_buf[AD4170_SPI_INST_PHASE_LEN]); + break; + case 2: + put_unaligned_be16(val, &tx_buf[AD4170_SPI_INST_PHASE_LEN]); + break; + case 1: + tx_buf[AD4170_SPI_INST_PHASE_LEN] = val; + break; + case 0: + /* Write continuous read exit code */ + tx_buf[0] = AD4170_ADC_CTRL_CONT_READ_EXIT; + return spi_write_then_read(st->spi, tx_buf, 1, NULL, 0); + default: + return -EINVAL; + } + + return spi_write_then_read(st->spi, tx_buf, + AD4170_SPI_INST_PHASE_LEN + size, NULL, 0); +} + +static int ad4170_reg_read(void *context, unsigned int reg, unsigned int *val) +{ + struct ad4170_state *st = context; + u8 tx_buf[AD4170_SPI_INST_PHASE_LEN]; + unsigned int size; + int ret; + + put_unaligned_be16(AD4170_REG_READ_MASK | reg, tx_buf); + + ret = ad4170_get_reg_size(st, reg, &size); + if (ret) + return ret; + + ret = spi_write_then_read(st->spi, tx_buf, ARRAY_SIZE(tx_buf), + st->rx_buf, size); + if (ret) + return ret; + + switch (size) { + case 3: + *val = get_unaligned_be24(st->rx_buf); + return 0; + case 2: + *val = get_unaligned_be16(st->rx_buf); + return 0; + case 1: + *val = st->rx_buf[0]; + return 0; + default: + return -EINVAL; + } +} + +static const struct regmap_config ad4170_regmap_config = { + .reg_read = ad4170_reg_read, + .reg_write = ad4170_reg_write, +}; + +static bool ad4170_setup_eq(struct ad4170_setup *a, struct ad4170_setup *b) +{ + if (a->misc != b->misc || + a->afe != b->afe || + a->filter != b->filter || + a->filter_fs != b->filter_fs || + a->offset != b->offset || + a->gain != b->gain) + return false; + + return true; +} + +static int ad4170_find_setup(struct ad4170_state *st, + struct ad4170_setup *target_setup, + unsigned int *setup_num, bool *overwrite) +{ + unsigned int i; + + *setup_num = AD4170_INVALID_SETUP; + *overwrite = false; + + for (i = 0; i < AD4170_MAX_SETUPS; i++) { + struct ad4170_setup_info *setup_info = &st->setup_infos[i]; + + /* Immediately accept a matching setup. */ + if (ad4170_setup_eq(target_setup, &setup_info->setup)) { + *setup_num = i; + return 0; + } + + /* Ignore all setups which are used by enabled channels. */ + if (setup_info->enabled_channels) + continue; + + /* Find the least used slot. */ + if (*setup_num == AD4170_INVALID_SETUP || + setup_info->channels < st->setup_infos[*setup_num].channels) + *setup_num = i; + } + + if (*setup_num == AD4170_INVALID_SETUP) + return -EINVAL; + + *overwrite = true; + return 0; +} + +static void ad4170_unlink_channel(struct ad4170_state *st, unsigned int channel) +{ + struct ad4170_chan_info *chan_info = &st->chan_infos[channel]; + struct ad4170_setup_info *setup_info = &st->setup_infos[chan_info->setup_num]; + + chan_info->setup_num = AD4170_INVALID_SETUP; + setup_info->channels--; +} + +static int ad4170_unlink_setup(struct ad4170_state *st, unsigned int setup_num) +{ + unsigned int i; + + for (i = 0; i < AD4170_MAX_ADC_CHANNELS; i++) { + struct ad4170_chan_info *chan_info = &st->chan_infos[i]; + + if (!chan_info->initialized || chan_info->setup_num != setup_num) + continue; + + ad4170_unlink_channel(st, i); + } + return 0; +} + +static int ad4170_link_channel_setup(struct ad4170_state *st, + unsigned int chan_addr, + unsigned int setup_num) +{ + struct ad4170_setup_info *setup_info = &st->setup_infos[setup_num]; + struct ad4170_chan_info *chan_info = &st->chan_infos[chan_addr]; + int ret; + + ret = regmap_update_bits(st->regmap, AD4170_CHAN_SETUP_REG(chan_addr), + AD4170_CHAN_SETUP_SETUP_MSK, + FIELD_PREP(AD4170_CHAN_SETUP_SETUP_MSK, setup_num)); + if (ret) + return ret; + + chan_info->setup_num = setup_num; + setup_info->channels++; + return 0; +} + +static int ad4170_write_setup(struct ad4170_state *st, unsigned int setup_num, + struct ad4170_setup *setup) +{ + int ret; + + /* + * It is recommended to place the ADC in standby mode or idle mode to + * write to OFFSET and GAIN registers. + */ + ret = regmap_update_bits(st->regmap, AD4170_ADC_CTRL_REG, + AD4170_ADC_CTRL_MODE_MSK, + FIELD_PREP(AD4170_ADC_CTRL_MODE_MSK, + AD4170_ADC_CTRL_MODE_IDLE)); + if (ret) + return ret; + + ret = regmap_write(st->regmap, AD4170_MISC_REG(setup_num), setup->misc); + if (ret) + return ret; + + ret = regmap_write(st->regmap, AD4170_AFE_REG(setup_num), setup->afe); + if (ret) + return ret; + + ret = regmap_write(st->regmap, AD4170_FILTER_REG(setup_num), + setup->filter); + if (ret) + return ret; + + ret = regmap_write(st->regmap, AD4170_FILTER_FS_REG(setup_num), + setup->filter_fs); + if (ret) + return ret; + + ret = regmap_write(st->regmap, AD4170_OFFSET_REG(setup_num), + setup->offset); + if (ret) + return ret; + + ret = regmap_write(st->regmap, AD4170_GAIN_REG(setup_num), setup->gain); + if (ret) + return ret; + + memcpy(&st->setup_infos[setup_num].setup, setup, sizeof(*setup)); + return 0; +} + +static int ad4170_write_channel_setup(struct ad4170_state *st, + unsigned int chan_addr, bool on_enable) +{ + struct ad4170_chan_info *chan_info = &st->chan_infos[chan_addr]; + bool overwrite; + int setup_num; + int ret; + + /* + * Similar to AD4130 driver, the following cases need to be handled. + * + * 1. Enabled and linked channel with setup changes: + * - Find a setup. If not possible, return error. + * - Unlink channel from current setup. + * - If the setup found has only disabled channels linked to it, + * unlink all channels, and write the new setup to it. + * - Link channel to new setup. + * + * 2. Soon to be enabled and unlinked channel: + * - Find a setup. If not possible, return error. + * - If the setup found has only disabled channels linked to it, + * unlink all channels, and write the new setup to it. + * - Link channel to the setup. + * + * 3. Disabled and linked channel with setup changes: + * - Unlink channel from current setup. + * + * 4. Soon to be enabled and linked channel: + * 5. Disabled and unlinked channel with setup changes: + * - Do nothing. + */ + + /* Cases 3, 4, and 5 */ + if (chan_info->setup_num != AD4170_INVALID_SETUP) { + /* Case 4 */ + if (on_enable) + return 0; + + /* Case 3 */ + if (!chan_info->enabled) { + ad4170_unlink_channel(st, chan_addr); + return 0; + } + } else if (!on_enable && !chan_info->enabled) { + /* Case 5 */ + return 0; + } + + /* Cases 1 & 2 */ + ret = ad4170_find_setup(st, &chan_info->setup, &setup_num, &overwrite); + if (ret) + return ret; + + if (chan_info->setup_num != AD4170_INVALID_SETUP) + /* Case 1 */ + ad4170_unlink_channel(st, chan_addr); + + if (overwrite) { + ret = ad4170_unlink_setup(st, setup_num); + if (ret) + return ret; + + ret = ad4170_write_setup(st, setup_num, &chan_info->setup); + if (ret) + return ret; + } + + return ad4170_link_channel_setup(st, chan_addr, setup_num); +} + +static int ad4170_set_channel_enable(struct ad4170_state *st, + unsigned int chan_addr, bool status) +{ + struct ad4170_chan_info *chan_info = &st->chan_infos[chan_addr]; + struct ad4170_setup_info *setup_info; + int ret; + + if (chan_info->enabled == status) + return 0; + + if (status) { + ret = ad4170_write_channel_setup(st, chan_addr, true); + if (ret) + return ret; + } + + setup_info = &st->setup_infos[chan_info->setup_num]; + + ret = regmap_update_bits(st->regmap, AD4170_CHAN_EN_REG, + AD4170_CHAN_EN(chan_addr), + status ? AD4170_CHAN_EN(chan_addr) : 0); + if (ret) + return ret; + + setup_info->enabled_channels += status ? 1 : -1; + chan_info->enabled = status; + return 0; +} + +static int __ad4170_get_filter_type(unsigned int filter) +{ + u16 f_conf = FIELD_GET(AD4170_FILTER_FILTER_TYPE_MSK, filter); + + switch (f_conf) { + case AD4170_FILTER_FILTER_TYPE_SINC5_AVG: + return AD4170_SINC5_AVG; + case AD4170_FILTER_FILTER_TYPE_SINC5: + return AD4170_SINC5; + case AD4170_FILTER_FILTER_TYPE_SINC3: + return AD4170_SINC3; + default: + return -EINVAL; + } +} + +static int ad4170_set_filter_type(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + unsigned int val) +{ + struct ad4170_state *st = iio_priv(indio_dev); + struct ad4170_chan_info *chan_info = &st->chan_infos[chan->address]; + struct ad4170_setup *setup = &chan_info->setup; + unsigned int filter_type_conf; + int ret; + + switch (val) { + case AD4170_SINC5_AVG: + filter_type_conf = AD4170_FILTER_FILTER_TYPE_SINC5_AVG; + break; + case AD4170_SINC5: + filter_type_conf = AD4170_FILTER_FILTER_TYPE_SINC5; + break; + case AD4170_SINC3: + filter_type_conf = AD4170_FILTER_FILTER_TYPE_SINC3; + break; + default: + return -EINVAL; + } + + /* + * The filters provide the same ODR for a given filter_fs value but + * there are different minimum and maximum filter_fs limits for each + * filter. The filter_fs value will be adjusted if the current filter_fs + * is out of the limits of the just requested filter. Since the + * filter_fs value affects the ODR (sampling_frequency), changing the + * filter may lead to a change in the sampling frequency. + */ + scoped_guard(mutex, &st->lock) { + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; + + if (val == AD4170_SINC5_AVG || val == AD4170_SINC3) + setup->filter_fs = clamp(val, AD4170_SINC3_MIN_FS, + AD4170_SINC3_MAX_FS); + else + setup->filter_fs = clamp(val, AD4170_SINC5_MIN_FS, + AD4170_SINC5_MAX_FS); + + setup->filter &= ~AD4170_FILTER_FILTER_TYPE_MSK; + setup->filter |= FIELD_PREP(AD4170_FILTER_FILTER_TYPE_MSK, + filter_type_conf); + + ret = ad4170_write_channel_setup(st, chan->address, false); + iio_device_release_direct(indio_dev); + } + + return ret; +} + +static int ad4170_get_filter_type(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan) +{ + struct ad4170_state *st = iio_priv(indio_dev); + struct ad4170_chan_info *chan_info = &st->chan_infos[chan->address]; + struct ad4170_setup *setup = &chan_info->setup; + + return __ad4170_get_filter_type(setup->filter); +} + +static const struct iio_enum ad4170_filter_type_enum = { + .items = ad4170_filt_names, + .num_items = ARRAY_SIZE(ad4170_filt_names), + .get = ad4170_get_filter_type, + .set = ad4170_set_filter_type, +}; + +static const struct iio_chan_spec_ext_info ad4170_filter_type_ext_info[] = { + IIO_ENUM("filter_type", IIO_SEPARATE, &ad4170_filter_type_enum), + IIO_ENUM_AVAILABLE("filter_type", IIO_SHARED_BY_TYPE, + &ad4170_filter_type_enum), + { } +}; + +static const struct iio_chan_spec ad4170_channel_template = { + .type = IIO_VOLTAGE, + .indexed = 1, + .differential = 1, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SCALE) | + BIT(IIO_CHAN_INFO_CALIBBIAS) | + BIT(IIO_CHAN_INFO_CALIBSCALE) | + BIT(IIO_CHAN_INFO_SAMP_FREQ) | + BIT(IIO_CHAN_INFO_OFFSET), + .info_mask_separate_available = BIT(IIO_CHAN_INFO_SCALE) | + BIT(IIO_CHAN_INFO_SAMP_FREQ), + .ext_info = ad4170_filter_type_ext_info, + .scan_type = { + .realbits = 24, + .storagebits = 32, + .shift = 8, + .endianness = IIO_BE, + }, +}; + +static const struct iio_chan_spec ad4170_temp_channel_template = { + .type = IIO_TEMP, + .indexed = 0, + .channel = 17, + .channel2 = 17, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SCALE) | + BIT(IIO_CHAN_INFO_OFFSET) | + BIT(IIO_CHAN_INFO_CALIBSCALE) | + BIT(IIO_CHAN_INFO_CALIBBIAS) | + BIT(IIO_CHAN_INFO_SAMP_FREQ), + .info_mask_separate_available = BIT(IIO_CHAN_INFO_SAMP_FREQ), + .scan_type = { + .sign = 's', + .realbits = 24, + .storagebits = 32, + .shift = 8, + .endianness = IIO_BE, + }, +}; + +/* + * Receives the number of a multiplexed AD4170 input (ain_n), and stores the + * voltage (in µV) of the specified input into ain_voltage. If the input number + * is a ordinary analog input (AIN0 to AIN8), stores zero into ain_voltage. + * If a voltage regulator required by a special input is unavailable, return + * error code. Return 0 on success. + */ +static int ad4170_get_ain_voltage_uv(struct ad4170_state *st, int ain_n, + int *ain_voltage) +{ + struct device *dev = &st->spi->dev; + int v_diff; + + *ain_voltage = 0; + /* + * The voltage bias (vbias) sets the common-mode voltage of the channel + * to (AVDD + AVSS)/2. If provided, AVSS supply provides the magnitude + * (absolute value) of the negative voltage supplied to the AVSS pin. + * So, we do AVDD - AVSS to compute the DC voltage generated by the bias + * voltage generator. + */ + if (st->pins_fn[ain_n] & AD4170_PIN_VBIAS) { + int v_diff = st->vrefs_uv[AD4170_AVDD_SUP] - st->vrefs_uv[AD4170_AVSS_SUP]; + *ain_voltage = v_diff / 2; + return 0; + } + + if (ain_n <= AD4170_CHAN_MAP_TEMP_SENSOR) + return 0; + + switch (ain_n) { + case AD4170_CHAN_MAP_AVDD_AVSS_N: + v_diff = st->vrefs_uv[AD4170_AVDD_SUP] - st->vrefs_uv[AD4170_AVSS_SUP]; + *ain_voltage = v_diff / 5; + return 0; + case AD4170_CHAN_MAP_IOVDD_DGND_N: + *ain_voltage = st->vrefs_uv[AD4170_IOVDD_SUP] / 5; + return 0; + case AD4170_CHAN_MAP_AVSS: + *ain_voltage = st->vrefs_uv[AD4170_AVSS_SUP]; + return 0; + case AD4170_CHAN_MAP_DGND: + *ain_voltage = 0; + return 0; + case AD4170_CHAN_MAP_REFIN1_P: + if (st->vrefs_uv[AD4170_REFIN1P_SUP] == -ENODEV) + return dev_err_probe(dev, -ENODEV, + "input set to REFIN+ but ref not provided\n"); + + *ain_voltage = st->vrefs_uv[AD4170_REFIN1P_SUP]; + return 0; + case AD4170_CHAN_MAP_REFIN1_N: + if (st->vrefs_uv[AD4170_REFIN1N_SUP] == -ENODEV) + return dev_err_probe(dev, -ENODEV, + "input set to REFIN- but ref not provided\n"); + + *ain_voltage = st->vrefs_uv[AD4170_REFIN1N_SUP]; + return 0; + case AD4170_CHAN_MAP_REFIN2_P: + if (st->vrefs_uv[AD4170_REFIN2P_SUP] == -ENODEV) + return dev_err_probe(dev, -ENODEV, + "input set to REFIN2+ but ref not provided\n"); + + *ain_voltage = st->vrefs_uv[AD4170_REFIN2P_SUP]; + return 0; + case AD4170_CHAN_MAP_REFIN2_N: + if (st->vrefs_uv[AD4170_REFIN2N_SUP] == -ENODEV) + return dev_err_probe(dev, -ENODEV, + "input set to REFIN2- but ref not provided\n"); + + *ain_voltage = st->vrefs_uv[AD4170_REFIN2N_SUP]; + return 0; + case AD4170_CHAN_MAP_REFOUT: + /* REFOUT is 2.5V relative to AVSS so take that into account */ + *ain_voltage = st->vrefs_uv[AD4170_AVSS_SUP] + AD4170_INT_REF_2_5V; + return 0; + default: + return -EINVAL; + } +} + +static int ad4170_validate_analog_input(struct ad4170_state *st, int pin) +{ + if (pin <= AD4170_MAX_ANALOG_PINS) { + if (st->pins_fn[pin] & AD4170_PIN_CURRENT_OUT) + return dev_err_probe(&st->spi->dev, -EINVAL, + "Pin %d already used with fn %u.\n", + pin, st->pins_fn[pin]); + + st->pins_fn[pin] |= AD4170_PIN_ANALOG_IN; + } + return 0; +} + +static int ad4170_validate_channel_input(struct ad4170_state *st, int pin, bool com) +{ + /* Check common-mode input pin is mapped to a special input. */ + if (com && (pin < AD4170_CHAN_MAP_AVDD_AVSS_P || pin > AD4170_CHAN_MAP_REFOUT)) + return dev_err_probe(&st->spi->dev, -EINVAL, + "Invalid common-mode input pin number. %d\n", + pin); + + /* Check differential input pin is mapped to a analog input pin. */ + if (!com && pin > AD4170_MAX_ANALOG_PINS) + return dev_err_probe(&st->spi->dev, -EINVAL, + "Invalid analog input pin number. %d\n", + pin); + + return ad4170_validate_analog_input(st, pin); +} + +/* + * Verifies whether the channel input configuration is valid by checking the + * input numbers. + * Returns 0 on valid channel input configuration. -EINVAL otherwise. + */ +static int ad4170_validate_channel(struct ad4170_state *st, + struct iio_chan_spec const *chan) +{ + int ret; + + ret = ad4170_validate_channel_input(st, chan->channel, false); + if (ret) + return ret; + + return ad4170_validate_channel_input(st, chan->channel2, + !chan->differential); +} + +/* + * Verifies whether the channel configuration is valid by checking the provided + * input type, polarity, and voltage references result in a sane input range. + * Returns negative error code on failure. + */ +static int ad4170_get_input_range(struct ad4170_state *st, + struct iio_chan_spec const *chan, + unsigned int ch_reg, unsigned int ref_sel) +{ + bool bipolar = chan->scan_type.sign == 's'; + struct device *dev = &st->spi->dev; + int refp, refn, ain_voltage, ret; + + switch (ref_sel) { + case AD4170_REF_REFIN1: + if (st->vrefs_uv[AD4170_REFIN1P_SUP] == -ENODEV || + st->vrefs_uv[AD4170_REFIN1N_SUP] == -ENODEV) + return dev_err_probe(dev, -ENODEV, + "REFIN± selected but not provided\n"); + + refp = st->vrefs_uv[AD4170_REFIN1P_SUP]; + refn = st->vrefs_uv[AD4170_REFIN1N_SUP]; + break; + case AD4170_REF_REFIN2: + if (st->vrefs_uv[AD4170_REFIN2P_SUP] == -ENODEV || + st->vrefs_uv[AD4170_REFIN2N_SUP] == -ENODEV) + return dev_err_probe(dev, -ENODEV, + "REFIN2± selected but not provided\n"); + + refp = st->vrefs_uv[AD4170_REFIN2P_SUP]; + refn = st->vrefs_uv[AD4170_REFIN2N_SUP]; + break; + case AD4170_REF_AVDD: + refp = st->vrefs_uv[AD4170_AVDD_SUP]; + refn = st->vrefs_uv[AD4170_AVSS_SUP]; + break; + case AD4170_REF_REFOUT: + /* REFOUT is 2.5 V relative to AVSS */ + refp = st->vrefs_uv[AD4170_AVSS_SUP] + AD4170_INT_REF_2_5V; + refn = st->vrefs_uv[AD4170_AVSS_SUP]; + break; + default: + return -EINVAL; + } + + /* + * Find out the analog input range from the channel type, polarity, and + * voltage reference selection. + * AD4170 channels are either differential or pseudo-differential. + * Diff input voltage range: −VREF/gain to +VREF/gain (datasheet page 6) + * Pseudo-diff input voltage range: 0 to VREF/gain (datasheet page 6) + */ + if (chan->differential) { + if (!bipolar) + return dev_err_probe(dev, -EINVAL, + "Channel %u differential unipolar\n", + ch_reg); + + /* + * Differential bipolar channel. + * avss-supply is never above 0V. + * Assuming refin1n-supply not above 0V. + * Assuming refin2n-supply not above 0V. + */ + return refp + abs(refn); + } + /* + * Some configurations can lead to invalid setups. + * For example, if AVSS = -2.5V, REF_SELECT set to REFOUT (REFOUT/AVSS), + * and pseudo-diff channel configuration set, then the input range + * should go from 0V to +VREF (single-ended - datasheet pg 10), but + * REFOUT/AVSS range would be -2.5V to 0V. + * Check the positive reference is higher than 0V for pseudo-diff + * channels. + * Note that at this point in the code, refp can only be >= 0 since all + * error codes from reading the regulator voltage have been checked + * either at ad4170_regulator_setup() or above in this function. + */ + if (refp == 0) + return dev_err_probe(dev, -EINVAL, + "REF+ == GND for pseudo-diff chan %u\n", + ch_reg); + + if (bipolar) + return refp; + + /* + * Pseudo-differential unipolar channel. + * Input expected to swing from IN- to +VREF. + */ + ret = ad4170_get_ain_voltage_uv(st, chan->channel2, &ain_voltage); + if (ret) + return ret; + + if (refp - ain_voltage <= 0) + return dev_err_probe(dev, -EINVAL, + "Negative input >= REF+ for pseudo-diff chan %u\n", + ch_reg); + + return refp - ain_voltage; +} + +static int __ad4170_read_sample(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int *val) +{ + struct ad4170_state *st = iio_priv(indio_dev); + unsigned long settling_time_ms; + int ret; + + reinit_completion(&st->completion); + ret = regmap_update_bits(st->regmap, AD4170_ADC_CTRL_REG, + AD4170_ADC_CTRL_MODE_MSK, + FIELD_PREP(AD4170_ADC_CTRL_MODE_MSK, + AD4170_ADC_CTRL_MODE_SINGLE)); + if (ret) + return ret; + + /* + * When a channel is manually selected by the user, the ADC needs an + * extra time to provide the first stable conversion. The ADC settling + * time depends on the filter type, filter frequency, and ADC clock + * frequency (see datasheet page 53). The maximum settling time among + * all filter configurations is 6291164 / fCLK. Use that formula to wait + * for sufficient time whatever the filter configuration may be. + */ + settling_time_ms = DIV_ROUND_UP(6291164 * MILLI, st->mclk_hz); + ret = wait_for_completion_timeout(&st->completion, + msecs_to_jiffies(settling_time_ms)); + if (!ret) + dev_dbg(&st->spi->dev, + "No Data Ready signal. Reading after delay.\n"); + + ret = regmap_read(st->regmap, AD4170_DATA_24B_REG, val); + if (ret) + return ret; + + if (chan->scan_type.sign == 's') + *val = sign_extend32(*val, chan->scan_type.realbits - 1); + + return 0; +} + +static int ad4170_read_sample(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int *val) +{ + struct ad4170_state *st = iio_priv(indio_dev); + struct device *dev = &st->spi->dev; + int ret, ret2; + + /* + * The ADC sequences through all enabled channels. That can lead to + * incorrect channel being sampled if a previous read would have left a + * different channel enabled. Thus, always enable and disable the + * channel on single-shot read. + */ + ret = ad4170_set_channel_enable(st, chan->address, true); + if (ret) + return ret; + + ret = __ad4170_read_sample(indio_dev, chan, val); + if (ret) { + dev_err(dev, "failed to read sample: %d\n", ret); + + ret2 = ad4170_set_channel_enable(st, chan->address, false); + if (ret2) + dev_err(dev, "failed to disable channel: %d\n", ret2); + + return ret; + } + + ret = ad4170_set_channel_enable(st, chan->address, false); + if (ret) + return ret; + + return IIO_VAL_INT; +} + +static int ad4170_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long info) +{ + struct ad4170_state *st = iio_priv(indio_dev); + struct ad4170_chan_info *chan_info = &st->chan_infos[chan->address]; + struct ad4170_setup *setup = &chan_info->setup; + enum ad4170_filter_type f_type; + unsigned int pga, fs_idx; + int ret; + + guard(mutex)(&st->lock); + switch (info) { + case IIO_CHAN_INFO_RAW: + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; + + ret = ad4170_read_sample(indio_dev, chan, val); + iio_device_release_direct(indio_dev); + return ret; + case IIO_CHAN_INFO_SCALE: + pga = FIELD_GET(AD4170_AFE_PGA_GAIN_MSK, setup->afe); + switch (chan->type) { + case IIO_VOLTAGE: + *val = chan_info->scale_tbl[pga][0]; + *val2 = chan_info->scale_tbl[pga][1]; + return IIO_VAL_INT_PLUS_NANO; + case IIO_TEMP: + /* + * The scale_tbl converts output codes to mV units so + * multiply by MILLI to make the factor convert to µV. + * Then, apply the temperature sensor change sensitivity + * of 477 μV/K. Finally, multiply the result by MILLI + * again to comply with milli degrees Celsius IIO ABI. + */ + *val = 0; + *val2 = DIV_ROUND_CLOSEST(chan_info->scale_tbl[pga][1] * MILLI, 477) * + MILLI; + return IIO_VAL_INT_PLUS_NANO; + default: + return -EINVAL; + } + case IIO_CHAN_INFO_OFFSET: + pga = FIELD_GET(AD4170_AFE_PGA_GAIN_MSK, setup->afe); + *val = chan_info->offset_tbl[pga]; + return IIO_VAL_INT; + case IIO_CHAN_INFO_SAMP_FREQ: + f_type = __ad4170_get_filter_type(setup->filter); + switch (f_type) { + case AD4170_SINC5_AVG: + case AD4170_SINC3: + fs_idx = find_closest(setup->filter_fs, + ad4170_sinc3_filt_fs_tbl, + ARRAY_SIZE(ad4170_sinc3_filt_fs_tbl)); + *val = st->sps_tbl[f_type][fs_idx][0]; + *val2 = st->sps_tbl[f_type][fs_idx][1]; + return IIO_VAL_INT_PLUS_MICRO; + case AD4170_SINC5: + fs_idx = find_closest(setup->filter_fs, + ad4170_sinc5_filt_fs_tbl, + ARRAY_SIZE(ad4170_sinc5_filt_fs_tbl)); + *val = st->sps_tbl[f_type][fs_idx][0]; + *val2 = st->sps_tbl[f_type][fs_idx][1]; + return IIO_VAL_INT_PLUS_MICRO; + default: + return -EINVAL; + } + case IIO_CHAN_INFO_CALIBBIAS: + *val = setup->offset; + return IIO_VAL_INT; + case IIO_CHAN_INFO_CALIBSCALE: + *val = setup->gain; + return IIO_VAL_INT; + default: + return -EINVAL; + } +} + +static int ad4170_fill_scale_tbl(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan) +{ + struct ad4170_state *st = iio_priv(indio_dev); + struct ad4170_chan_info *chan_info = &st->chan_infos[chan->address]; + struct device *dev = &st->spi->dev; + int bipolar = chan->scan_type.sign == 's' ? 1 : 0; + int precision_bits = chan->scan_type.realbits; + int pga, ainm_voltage, ret; + unsigned long long offset; + + ainm_voltage = 0; + ret = ad4170_get_ain_voltage_uv(st, chan->channel2, &ainm_voltage); + if (ret < 0) + return dev_err_probe(dev, ret, "Failed to fill scale table\n"); + + for (pga = 0; pga < AD4170_NUM_PGA_OPTIONS; pga++) { + u64 nv; + unsigned int lshift, rshift; + + /* + * The PGA options are numbered from 0 to 9, with option 0 being + * a gain of 2^0 (no actual gain), and 7 meaning a gain of 2^7. + * Option 8, though, sets a gain of 0.5, so the input signal can + * be attenuated by 2 rather than amplified. Option 9, allows + * the signal to bypass the PGA circuitry (no gain). + * + * The scale factor to get ADC output codes to values in mV + * units is given by: + * _scale = (input_range / gain) / 2^precision + * AD4170 gain is a power of 2 so the above can be written as + * _scale = input_range / 2^(precision + gain) + * Keep the input range in µV to avoid truncating the less + * significant bits when right shifting it so to preserve scale + * precision. + */ + nv = (u64)chan_info->input_range_uv * NANO; + lshift = !!(pga & BIT(3)); /* handle PGA options 8 and 9 */ + rshift = precision_bits - bipolar + (pga & GENMASK(2, 0)) - lshift; + chan_info->scale_tbl[pga][0] = 0; + chan_info->scale_tbl[pga][1] = div_u64(nv >> rshift, MILLI); + + /* + * If the negative input is not at GND, the conversion result + * (which is relative to IN-) will be offset by the level at IN-. + * Use the scale factor the other way around to go from a known + * voltage to the corresponding ADC output code. + * With that, we are able to get to what would be the output + * code for the voltage at the negative input. + * If the negative input is not fixed, there is no offset. + */ + offset = ((unsigned long long)abs(ainm_voltage)) * MICRO; + offset = DIV_ROUND_CLOSEST_ULL(offset, chan_info->scale_tbl[pga][1]); + + /* + * After divided by the scale, offset will always fit into 31 + * bits. For _raw + _offset to be relative to GND, the value + * provided as _offset is of opposite sign than the real offset. + */ + if (ainm_voltage > 0) + chan_info->offset_tbl[pga] = -(int)(offset); + else + chan_info->offset_tbl[pga] = (int)(offset); + } + return 0; +} + +static int ad4170_read_avail(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + const int **vals, int *type, int *length, + long info) +{ + struct ad4170_state *st = iio_priv(indio_dev); + struct ad4170_chan_info *chan_info = &st->chan_infos[chan->address]; + enum ad4170_filter_type f_type; + + switch (info) { + case IIO_CHAN_INFO_SCALE: + *vals = (int *)chan_info->scale_tbl; + *length = ARRAY_SIZE(chan_info->scale_tbl) * 2; + *type = IIO_VAL_INT_PLUS_NANO; + return IIO_AVAIL_LIST; + case IIO_CHAN_INFO_SAMP_FREQ: + *type = IIO_VAL_INT_PLUS_MICRO; + f_type = ad4170_get_filter_type(indio_dev, chan); + switch (f_type) { + case AD4170_SINC5_AVG: + case AD4170_SINC3: + /* Read sps_tbl here to ensure in bounds array access */ + *vals = (int *)st->sps_tbl[f_type]; + *length = ARRAY_SIZE(ad4170_sinc3_filt_fs_tbl) * 2; + return IIO_AVAIL_LIST; + case AD4170_SINC5: + /* Read sps_tbl here to ensure in bounds array access */ + *vals = (int *)st->sps_tbl[f_type]; + *length = ARRAY_SIZE(ad4170_sinc5_filt_fs_tbl) * 2; + return IIO_AVAIL_LIST; + default: + return -EINVAL; + } + default: + return -EINVAL; + } +} + +static int ad4170_set_pga(struct ad4170_state *st, + struct iio_chan_spec const *chan, int val, int val2) +{ + struct ad4170_chan_info *chan_info = &st->chan_infos[chan->address]; + struct ad4170_setup *setup = &chan_info->setup; + unsigned int pga; + + for (pga = 0; pga < AD4170_NUM_PGA_OPTIONS; pga++) { + if (val == chan_info->scale_tbl[pga][0] && + val2 == chan_info->scale_tbl[pga][1]) + break; + } + + if (pga == AD4170_NUM_PGA_OPTIONS) + return -EINVAL; + + guard(mutex)(&st->lock); + setup->afe &= ~AD4170_AFE_PGA_GAIN_MSK; + setup->afe |= FIELD_PREP(AD4170_AFE_PGA_GAIN_MSK, pga); + + return ad4170_write_channel_setup(st, chan->address, false); +} + +static int ad4170_set_channel_freq(struct ad4170_state *st, + struct iio_chan_spec const *chan, int val, + int val2) +{ + struct ad4170_chan_info *chan_info = &st->chan_infos[chan->address]; + struct ad4170_setup *setup = &chan_info->setup; + enum ad4170_filter_type f_type = __ad4170_get_filter_type(setup->filter); + unsigned int filt_fs_tbl_size, i; + + switch (f_type) { + case AD4170_SINC5_AVG: + case AD4170_SINC3: + filt_fs_tbl_size = ARRAY_SIZE(ad4170_sinc3_filt_fs_tbl); + break; + case AD4170_SINC5: + filt_fs_tbl_size = ARRAY_SIZE(ad4170_sinc5_filt_fs_tbl); + break; + } + + for (i = 0; i < filt_fs_tbl_size; i++) { + if (st->sps_tbl[f_type][i][0] == val && + st->sps_tbl[f_type][i][1] == val2) + break; + } + if (i == filt_fs_tbl_size) + return -EINVAL; + + guard(mutex)(&st->lock); + if (f_type == AD4170_SINC5) + setup->filter_fs = ad4170_sinc5_filt_fs_tbl[i]; + else + setup->filter_fs = ad4170_sinc3_filt_fs_tbl[i]; + + return ad4170_write_channel_setup(st, chan->address, false); +} + +static int ad4170_set_calib_offset(struct ad4170_state *st, + struct iio_chan_spec const *chan, int val) +{ + struct ad4170_chan_info *chan_info = &st->chan_infos[chan->address]; + struct ad4170_setup *setup = &chan_info->setup; + + guard(mutex)(&st->lock); + setup->offset = val; + + return ad4170_write_channel_setup(st, chan->address, false); +} + +static int ad4170_set_calib_gain(struct ad4170_state *st, + struct iio_chan_spec const *chan, int val) +{ + struct ad4170_chan_info *chan_info = &st->chan_infos[chan->address]; + struct ad4170_setup *setup = &chan_info->setup; + + guard(mutex)(&st->lock); + setup->gain = val; + + return ad4170_write_channel_setup(st, chan->address, false); +} + +static int __ad4170_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int val, + int val2, long info) +{ + struct ad4170_state *st = iio_priv(indio_dev); + + switch (info) { + case IIO_CHAN_INFO_SCALE: + return ad4170_set_pga(st, chan, val, val2); + case IIO_CHAN_INFO_SAMP_FREQ: + return ad4170_set_channel_freq(st, chan, val, val2); + case IIO_CHAN_INFO_CALIBBIAS: + return ad4170_set_calib_offset(st, chan, val); + case IIO_CHAN_INFO_CALIBSCALE: + return ad4170_set_calib_gain(st, chan, val); + default: + return -EINVAL; + } +} + +static int ad4170_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int val, + int val2, long info) +{ + int ret; + + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; + + ret = __ad4170_write_raw(indio_dev, chan, val, val2, info); + iio_device_release_direct(indio_dev); + return ret; +} + +static int ad4170_write_raw_get_fmt(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + long info) +{ + switch (info) { + case IIO_CHAN_INFO_SCALE: + return IIO_VAL_INT_PLUS_NANO; + case IIO_CHAN_INFO_SAMP_FREQ: + return IIO_VAL_INT_PLUS_MICRO; + case IIO_CHAN_INFO_CALIBBIAS: + case IIO_CHAN_INFO_CALIBSCALE: + return IIO_VAL_INT; + default: + return -EINVAL; + } +} + +static int ad4170_update_scan_mode(struct iio_dev *indio_dev, + const unsigned long *active_scan_mask) +{ + struct ad4170_state *st = iio_priv(indio_dev); + unsigned int chan_index; + int ret; + + iio_for_each_active_channel(indio_dev, chan_index) { + ret = ad4170_set_channel_enable(st, chan_index, true); + if (ret) + return ret; + } + return 0; +} + +static const struct iio_info ad4170_info = { + .read_raw = ad4170_read_raw, + .read_avail = ad4170_read_avail, + .write_raw = ad4170_write_raw, + .write_raw_get_fmt = ad4170_write_raw_get_fmt, + .update_scan_mode = ad4170_update_scan_mode, + .debugfs_reg_access = ad4170_debugfs_reg_access, +}; + +static int ad4170_soft_reset(struct ad4170_state *st) +{ + int ret; + + ret = regmap_write(st->regmap, AD4170_CONFIG_A_REG, + AD4170_SW_RESET_MSK); + if (ret) + return ret; + + /* AD4170-4 requires 1 ms between reset and any register access. */ + fsleep(1 * USEC_PER_MSEC); + + return 0; +} + +static int ad4170_gpio_get(struct gpio_chip *gc, unsigned int offset) +{ + struct iio_dev *indio_dev = gpiochip_get_data(gc); + struct ad4170_state *st = iio_priv(indio_dev); + unsigned int val; + int ret; + + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; + + ret = regmap_read(st->regmap, AD4170_GPIO_MODE_REG, &val); + if (ret) + goto err_release; + + /* + * If the GPIO is configured as an input, read the current value from + * AD4170_GPIO_INPUT_REG. Otherwise, read the input value from + * AD4170_GPIO_OUTPUT_REG. + */ + if (val & BIT(offset * 2)) + ret = regmap_read(st->regmap, AD4170_GPIO_INPUT_REG, &val); + else + ret = regmap_read(st->regmap, AD4170_GPIO_OUTPUT_REG, &val); + if (ret) + goto err_release; + + ret = !!(val & BIT(offset)); +err_release: + iio_device_release_direct(indio_dev); + + return ret; +} + +static int ad4170_gpio_set(struct gpio_chip *gc, unsigned int offset, int value) +{ + struct iio_dev *indio_dev = gpiochip_get_data(gc); + struct ad4170_state *st = iio_priv(indio_dev); + int ret; + + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; + + ret = regmap_assign_bits(st->regmap, AD4170_GPIO_OUTPUT_REG, + BIT(offset), !!value); + + iio_device_release_direct(indio_dev); + return ret; +} + +static int ad4170_gpio_get_direction(struct gpio_chip *gc, unsigned int offset) +{ + struct iio_dev *indio_dev = gpiochip_get_data(gc); + struct ad4170_state *st = iio_priv(indio_dev); + unsigned int val; + int ret; + + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; + + ret = regmap_read(st->regmap, AD4170_GPIO_MODE_REG, &val); + if (ret) + goto err_release; + + if (val & BIT(offset * 2 + 1)) + ret = GPIO_LINE_DIRECTION_OUT; + else + ret = GPIO_LINE_DIRECTION_IN; + +err_release: + iio_device_release_direct(indio_dev); + + return ret; +} + +static const unsigned long gpio_masks[] = { + AD4170_GPIO_MODE_GPIO0_MSK, + AD4170_GPIO_MODE_GPIO1_MSK, + AD4170_GPIO_MODE_GPIO2_MSK, + AD4170_GPIO_MODE_GPIO3_MSK, +}; + +static int ad4170_gpio_direction_input(struct gpio_chip *gc, unsigned int offset) +{ + struct iio_dev *indio_dev = gpiochip_get_data(gc); + struct ad4170_state *st = iio_priv(indio_dev); + int ret; + + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; + + if (offset >= ARRAY_SIZE(gpio_masks)) { + ret = -EINVAL; + goto err_release; + } + + ret = regmap_update_bits(st->regmap, AD4170_GPIO_MODE_REG, + gpio_masks[offset], + AD4170_GPIO_MODE_GPIO_INPUT << (2 * offset)); + +err_release: + iio_device_release_direct(indio_dev); + + return ret; +} + +static int ad4170_gpio_direction_output(struct gpio_chip *gc, + unsigned int offset, int value) +{ + struct iio_dev *indio_dev = gpiochip_get_data(gc); + struct ad4170_state *st = iio_priv(indio_dev); + int ret; + + ret = ad4170_gpio_set(gc, offset, value); + if (ret) + return ret; + + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; + + if (offset >= ARRAY_SIZE(gpio_masks)) { + ret = -EINVAL; + goto err_release; + } + + ret = regmap_update_bits(st->regmap, AD4170_GPIO_MODE_REG, + gpio_masks[offset], + AD4170_GPIO_MODE_GPIO_OUTPUT << (2 * offset)); + +err_release: + iio_device_release_direct(indio_dev); + + return ret; +} + +static int ad4170_gpio_init_valid_mask(struct gpio_chip *gc, + unsigned long *valid_mask, + unsigned int ngpios) +{ + struct ad4170_state *st = gpiochip_get_data(gc); + unsigned int i; + + /* Only expose GPIOs that were not assigned any other function. */ + for (i = 0; i < ngpios; i++) { + bool valid = st->gpio_fn[i] == AD4170_GPIO_UNASSIGNED; + + __assign_bit(i, valid_mask, valid); + } + + return 0; +} + +static int ad4170_gpio_init(struct iio_dev *indio_dev) +{ + struct ad4170_state *st = iio_priv(indio_dev); + + st->gpiochip.label = "ad4170_gpios"; + st->gpiochip.base = -1; + st->gpiochip.ngpio = AD4170_NUM_GPIO_PINS; + st->gpiochip.parent = &st->spi->dev; + st->gpiochip.can_sleep = true; + st->gpiochip.init_valid_mask = ad4170_gpio_init_valid_mask; + st->gpiochip.get_direction = ad4170_gpio_get_direction; + st->gpiochip.direction_input = ad4170_gpio_direction_input; + st->gpiochip.direction_output = ad4170_gpio_direction_output; + st->gpiochip.get = ad4170_gpio_get; + st->gpiochip.set = ad4170_gpio_set; + st->gpiochip.owner = THIS_MODULE; + + return devm_gpiochip_add_data(&st->spi->dev, &st->gpiochip, indio_dev); +} + +static int ad4170_validate_excitation_pin(struct ad4170_state *st, u32 pin) +{ + struct device *dev = &st->spi->dev; + unsigned int i; + + /* Check the pin number is valid */ + for (i = 0; i < ARRAY_SIZE(ad4170_iout_pin_tbl); i++) + if (ad4170_iout_pin_tbl[i] == pin) + break; + + if (i == ARRAY_SIZE(ad4170_iout_pin_tbl)) + return dev_err_probe(dev, -EINVAL, + "Invalid excitation pin: %u\n", + pin); + + /* Check the pin is available */ + if (pin <= AD4170_MAX_ANALOG_PINS) { + if (st->pins_fn[pin] != AD4170_PIN_UNASSIGNED) + return dev_err_probe(dev, -EINVAL, + "Pin %u already used with fn %u\n", + pin, st->pins_fn[pin]); + + st->pins_fn[pin] |= AD4170_PIN_CURRENT_OUT; + } else { + unsigned int gpio = pin - AD4170_CURRENT_SRC_I_OUT_PIN_GPIO(0); + + if (st->gpio_fn[gpio] != AD4170_GPIO_UNASSIGNED) + return dev_err_probe(dev, -EINVAL, + "GPIO %u already used with fn %u\n", + gpio, st->gpio_fn[gpio]); + + st->gpio_fn[gpio] |= AD4170_GPIO_AC_EXCITATION; + } + + return 0; +} + +static int ad4170_validate_excitation_pins(struct ad4170_state *st, + u32 *exc_pins, int num_exc_pins) +{ + unsigned int i; + int ret; + + for (i = 0; i < num_exc_pins; i++) { + ret = ad4170_validate_excitation_pin(st, exc_pins[i]); + if (ret) + return ret; + } + return 0; +} + +static const char *const ad4170_i_out_pin_dt_props[] = { + "adi,excitation-pin-0", + "adi,excitation-pin-1", + "adi,excitation-pin-2", + "adi,excitation-pin-3", +}; + +static const char *const ad4170_i_out_val_dt_props[] = { + "adi,excitation-current-0-microamp", + "adi,excitation-current-1-microamp", + "adi,excitation-current-2-microamp", + "adi,excitation-current-3-microamp", +}; + +/* + * Parses firmware data describing output current source setup. There are 4 + * excitation currents (IOUT0 to IOUT3) that can be configured independently. + * Excitation currents are added if they are output on the same pin. + */ +static int ad4170_parse_exc_current(struct ad4170_state *st, + struct fwnode_handle *child, + unsigned int *exc_pins, + unsigned int *exc_curs, + unsigned int *num_exc_pins) +{ + struct device *dev = &st->spi->dev; + unsigned int num_pins, i, j; + u32 pin, val; + int ret; + + num_pins = 0; + for (i = 0; i < AD4170_NUM_CURRENT_SRC; i++) { + /* Parse excitation current output pin properties. */ + pin = AD4170_CURRENT_SRC_I_OUT_PIN_AIN(0); + ret = fwnode_property_read_u32(child, ad4170_i_out_pin_dt_props[i], + &pin); + if (ret) + continue; + + exc_pins[num_pins] = pin; + + /* Parse excitation current value properties. */ + val = ad4170_iout_current_ua_tbl[0]; + fwnode_property_read_u32(child, + ad4170_i_out_val_dt_props[i], &val); + + for (j = 0; j < ARRAY_SIZE(ad4170_iout_current_ua_tbl); j++) + if (ad4170_iout_current_ua_tbl[j] == val) + break; + + if (j == ARRAY_SIZE(ad4170_iout_current_ua_tbl)) + return dev_err_probe(dev, -EINVAL, "Invalid %s: %uuA\n", + ad4170_i_out_val_dt_props[i], val); + + exc_curs[num_pins] = j; + num_pins++; + } + *num_exc_pins = num_pins; + + return 0; +} + +static int ad4170_setup_current_src(struct ad4170_state *st, + struct fwnode_handle *child, + struct ad4170_setup *setup, u32 *exc_pins, + unsigned int *exc_curs, int num_exc_pins, + bool ac_excited) +{ + unsigned int exc_cur_pair, i, j; + int ret; + + for (i = 0; i < num_exc_pins; i++) { + unsigned int exc_cur = exc_curs[i]; + unsigned int pin = exc_pins[i]; + unsigned int current_src = 0; + + for (j = 0; j < AD4170_NUM_CURRENT_SRC; j++) + if (st->cur_src_pins[j] == AD4170_CURRENT_SRC_DISABLED) + break; + + if (j == AD4170_NUM_CURRENT_SRC) + return dev_err_probe(&st->spi->dev, -EINVAL, + "Too many excitation current sources\n"); + + current_src |= FIELD_PREP(AD4170_CURRENT_SRC_I_OUT_PIN_MSK, pin); + current_src |= FIELD_PREP(AD4170_CURRENT_SRC_I_OUT_VAL_MSK, exc_cur); + st->cur_src_pins[j] = pin; + ret = regmap_write(st->regmap, AD4170_CURRENT_SRC_REG(j), + current_src); + if (ret) + return ret; + } + + if (!ac_excited) + return 0; + + if (num_exc_pins < 2) + return dev_err_probe(&st->spi->dev, -EINVAL, + "Current chopping requested but only one pin provided: %u\n", + exc_pins[0]); + + /* + * Two use cases to handle here: + * - 2 pairs of excitation currents; + * - 1 pair of excitation currents. + */ + if (num_exc_pins == 4) { + for (i = 0; i < AD4170_NUM_CURRENT_SRC; i++) + if (st->cur_src_pins[i] != exc_pins[i]) + return dev_err_probe(&st->spi->dev, -EINVAL, + "Unable to use 4 exc pins\n"); + } else { + /* + * Excitation current chopping is configured in pairs. Current + * sources IOUT0 and IOUT1 form pair 1, IOUT2 and IOUT3 make up + * pair 2. So, if current chopping was requested, check if the + * first end of the first pair of excitation currents is + * available. Try the next pair if IOUT0 has already been + * configured for another channel. + */ + i = st->cur_src_pins[0] == exc_pins[0] ? 0 : 2; + + if (st->cur_src_pins[i] != exc_pins[0] || + st->cur_src_pins[i + 1] != exc_pins[1]) + return dev_err_probe(&st->spi->dev, -EINVAL, + "Failed to setup current chopping\n"); + + st->cur_src_pins[i] = exc_pins[0]; + st->cur_src_pins[i + 1] = exc_pins[1]; + + if (i == 0) + exc_cur_pair = AD4170_MISC_CHOP_IEXC_PAIR1; + else + exc_cur_pair = AD4170_MISC_CHOP_IEXC_PAIR2; + } + + /* + * Configure excitation current chopping. + * Chop both pairs if using four excitation pins. + */ + setup->misc |= FIELD_PREP(AD4170_MISC_CHOP_IEXC_MSK, + num_exc_pins == 2 ? + exc_cur_pair : + AD4170_MISC_CHOP_IEXC_BOTH); + + return 0; +} + +static int ad4170_setup_bridge(struct ad4170_state *st, + struct fwnode_handle *child, + struct ad4170_setup *setup, u32 *exc_pins, + unsigned int *exc_curs, int num_exc_pins, + bool ac_excited) +{ + unsigned long gpio_mask; + unsigned int i; + int ret; + + /* + * If a specific current is provided through + * adi,excitation-current-n-microamp, set excitation pins provided + * through adi,excitation-pin-n to excite the bridge circuit. + */ + for (i = 0; i < num_exc_pins; i++) + if (exc_curs[i] > 0) + return ad4170_setup_current_src(st, child, setup, exc_pins, + exc_curs, num_exc_pins, + ac_excited); + + /* + * Else, use predefined ACX1, ACX1 negated, ACX2, ACX2 negated signals + * to AC excite the bridge. Those signals are output on GPIO2, GPIO0, + * GPIO3, and GPIO1, respectively. If only two pins are specified for AC + * excitation, use ACX1 and ACX2 (GPIO2 and GPIO3). + * + * Also, to avoid any short-circuit condition when more than one channel + * is enabled, set GPIO2 and GPIO0 high, and set GPIO1 and GPIO3 low to + * DC excite the bridge whenever a channel without AC excitation is + * selected. That is needed because GPIO pins are controlled by the next + * highest priority GPIO function when a channel doesn't enable AC + * excitation. See datasheet Figure 113 Weigh Scale (AC Excitation) for + * the reference circuit diagram. + */ + if (num_exc_pins == 2) { + setup->misc |= FIELD_PREP(AD4170_MISC_CHOP_ADC_MSK, 0x3); + + gpio_mask = AD4170_GPIO_MODE_GPIO3_MSK | AD4170_GPIO_MODE_GPIO2_MSK; + ret = regmap_update_bits(st->regmap, AD4170_GPIO_MODE_REG, gpio_mask, + FIELD_PREP(AD4170_GPIO_MODE_GPIO3_MSK, + AD4170_GPIO_MODE_GPIO_OUTPUT) | + FIELD_PREP(AD4170_GPIO_MODE_GPIO2_MSK, + AD4170_GPIO_MODE_GPIO_OUTPUT)); + if (ret) + return ret; + + /* + * Set GPIO2 high and GPIO3 low to DC excite the bridge when + * a different channel is selected. + */ + gpio_mask = AD4170_GPIO_OUTPUT_GPIO_MSK(3) | + AD4170_GPIO_OUTPUT_GPIO_MSK(2); + ret = regmap_update_bits(st->regmap, AD4170_GPIO_OUTPUT_REG, gpio_mask, + FIELD_PREP(AD4170_GPIO_OUTPUT_GPIO_MSK(3), 0) | + FIELD_PREP(AD4170_GPIO_OUTPUT_GPIO_MSK(2), 1)); + if (ret) + return ret; + + st->gpio_fn[3] |= AD4170_GPIO_OUTPUT; + st->gpio_fn[2] |= AD4170_GPIO_OUTPUT; + } else { + setup->misc |= FIELD_PREP(AD4170_MISC_CHOP_ADC_MSK, 0x2); + + gpio_mask = AD4170_GPIO_MODE_GPIO3_MSK | AD4170_GPIO_MODE_GPIO2_MSK | + AD4170_GPIO_MODE_GPIO1_MSK | AD4170_GPIO_MODE_GPIO0_MSK; + ret = regmap_update_bits(st->regmap, AD4170_GPIO_MODE_REG, gpio_mask, + FIELD_PREP(AD4170_GPIO_MODE_GPIO3_MSK, + AD4170_GPIO_MODE_GPIO_OUTPUT) | + FIELD_PREP(AD4170_GPIO_MODE_GPIO2_MSK, + AD4170_GPIO_MODE_GPIO_OUTPUT) | + FIELD_PREP(AD4170_GPIO_MODE_GPIO1_MSK, + AD4170_GPIO_MODE_GPIO_OUTPUT) | + FIELD_PREP(AD4170_GPIO_MODE_GPIO0_MSK, + AD4170_GPIO_MODE_GPIO_OUTPUT)); + if (ret) + return ret; + + /* + * Set GPIO2 and GPIO0 high, and set GPIO1 and GPIO3 low to DC + * excite the bridge when a different channel is selected. + */ + gpio_mask = AD4170_GPIO_OUTPUT_GPIO_MSK(3) | + AD4170_GPIO_OUTPUT_GPIO_MSK(2) | + AD4170_GPIO_OUTPUT_GPIO_MSK(1) | + AD4170_GPIO_OUTPUT_GPIO_MSK(0); + ret = regmap_update_bits(st->regmap, AD4170_GPIO_OUTPUT_REG, gpio_mask, + FIELD_PREP(AD4170_GPIO_OUTPUT_GPIO_MSK(3), 0) | + FIELD_PREP(AD4170_GPIO_OUTPUT_GPIO_MSK(2), 1) | + FIELD_PREP(AD4170_GPIO_OUTPUT_GPIO_MSK(1), 0) | + FIELD_PREP(AD4170_GPIO_OUTPUT_GPIO_MSK(0), 1)); + if (ret) + return ret; + + st->gpio_fn[3] |= AD4170_GPIO_OUTPUT; + st->gpio_fn[2] |= AD4170_GPIO_OUTPUT; + st->gpio_fn[1] |= AD4170_GPIO_OUTPUT; + st->gpio_fn[0] |= AD4170_GPIO_OUTPUT; + } + + return 0; +} + +static int ad4170_setup_rtd(struct ad4170_state *st, + struct fwnode_handle *child, + struct ad4170_setup *setup, u32 *exc_pins, + unsigned int *exc_curs, int num_exc_pins, bool ac_excited) +{ + return ad4170_setup_current_src(st, child, setup, exc_pins, + exc_curs, num_exc_pins, ac_excited); +} + +static int ad4170_parse_external_sensor(struct ad4170_state *st, + struct fwnode_handle *child, + struct ad4170_setup *setup, + struct iio_chan_spec *chan, + unsigned int s_type) +{ + unsigned int num_exc_pins, reg_val; + struct device *dev = &st->spi->dev; + u32 pins[2], exc_pins[4], exc_curs[4]; + bool ac_excited; + int ret; + + ret = fwnode_property_read_u32_array(child, "diff-channels", pins, + ARRAY_SIZE(pins)); + if (ret) + return dev_err_probe(dev, ret, + "Failed to read sensor diff-channels\n"); + + chan->differential = true; + chan->channel = pins[0]; + chan->channel2 = pins[1]; + + ret = ad4170_parse_exc_current(st, child, exc_pins, exc_curs, &num_exc_pins); + if (ret) + return ret; + + /* The external sensor may not need excitation from the ADC chip. */ + if (num_exc_pins == 0) + return 0; + + ret = ad4170_validate_excitation_pins(st, exc_pins, num_exc_pins); + if (ret) + return ret; + + ac_excited = fwnode_property_read_bool(child, "adi,excitation-ac"); + + if (s_type == AD4170_THERMOCOUPLE_SENSOR) { + if (st->pins_fn[chan->channel2] & AD4170_PIN_VBIAS) { + reg_val = BIT(chan->channel2); + ret = regmap_write(st->regmap, AD4170_V_BIAS_REG, reg_val); + if (ret) + dev_err_probe(dev, ret, "Failed to set vbias\n"); + } + } + if (s_type == AD4170_WEIGH_SCALE_SENSOR) + ret = ad4170_setup_bridge(st, child, setup, exc_pins, exc_curs, + num_exc_pins, ac_excited); + else + ret = ad4170_setup_rtd(st, child, setup, exc_pins, exc_curs, + num_exc_pins, ac_excited); + + return ret; +} + +static int ad4170_parse_reference(struct ad4170_state *st, + struct fwnode_handle *child, + struct ad4170_setup *setup) +{ + struct device *dev = &st->spi->dev; + const char *propname; + u32 aux; + int ret; + + /* Optional positive reference buffering */ + propname = "adi,positive-reference-buffer"; + ret = device_property_match_property_string(dev, propname, + ad4170_ref_buf_str, + ARRAY_SIZE(ad4170_ref_buf_str)); + + /* Default to full precharge buffer enabled. */ + setup->afe |= FIELD_PREP(AD4170_AFE_REF_BUF_P_MSK, + ret >= 0 ? ret : AD4170_REF_BUF_FULL); + + /* Optional negative reference buffering */ + propname = "adi,negative-reference-buffer"; + ret = device_property_match_property_string(dev, propname, + ad4170_ref_buf_str, + ARRAY_SIZE(ad4170_ref_buf_str)); + + /* Default to full precharge buffer enabled. */ + setup->afe |= FIELD_PREP(AD4170_AFE_REF_BUF_M_MSK, + ret >= 0 ? ret : AD4170_REF_BUF_FULL); + + /* Optional voltage reference selection */ + propname = "adi,reference-select"; + aux = AD4170_REF_REFOUT; /* Default reference selection. */ + fwnode_property_read_u32(child, propname, &aux); + if (aux > AD4170_REF_AVDD) + return dev_err_probe(dev, -EINVAL, "Invalid %s: %u\n", + propname, aux); + + setup->afe |= FIELD_PREP(AD4170_AFE_REF_SELECT_MSK, aux); + + return 0; +} + +static int ad4170_parse_adc_channel_type(struct device *dev, + struct fwnode_handle *child, + struct iio_chan_spec *chan) +{ + const char *propname, *propname2; + int ret, ret2; + u32 pins[2]; + + propname = "single-channel"; + propname2 = "diff-channels"; + if (!fwnode_property_present(child, propname) && + !fwnode_property_present(child, propname2)) + return dev_err_probe(dev, -EINVAL, + "Channel must define one of %s or %s.\n", + propname, propname2); + + /* Parse differential channel configuration */ + ret = fwnode_property_read_u32_array(child, propname2, pins, + ARRAY_SIZE(pins)); + if (!ret) { + chan->differential = true; + chan->channel = pins[0]; + chan->channel2 = pins[1]; + return 0; + } + /* Failed to parse diff chan so try pseudo-diff chan props */ + + propname2 = "common-mode-channel"; + if (fwnode_property_present(child, propname) && + !fwnode_property_present(child, propname2)) + return dev_err_probe(dev, -EINVAL, + "When %s is defined, %s must be defined too\n", + propname, propname2); + + /* Parse pseudo-differential channel configuration */ + ret = fwnode_property_read_u32(child, propname, &pins[0]); + ret2 = fwnode_property_read_u32(child, propname2, &pins[1]); + + if (!ret && !ret2) { + chan->differential = false; + chan->channel = pins[0]; + chan->channel2 = pins[1]; + return 0; + } + return dev_err_probe(dev, -EINVAL, + "Failed to parse channel %lu input. %d, %d\n", + chan->address, ret, ret2); +} + +static int ad4170_parse_channel_node(struct iio_dev *indio_dev, + struct fwnode_handle *child, + unsigned int chan_num) +{ + struct ad4170_state *st = iio_priv(indio_dev); + unsigned int s_type = AD4170_ADC_SENSOR; + struct device *dev = &st->spi->dev; + struct ad4170_chan_info *chan_info; + struct ad4170_setup *setup; + struct iio_chan_spec *chan; + unsigned int ref_select; + unsigned int ch_reg; + bool bipolar; + int ret; + + ret = fwnode_property_read_u32(child, "reg", &ch_reg); + if (ret) + return dev_err_probe(dev, ret, "Failed to read channel reg\n"); + + if (ch_reg >= AD4170_MAX_ADC_CHANNELS) + return dev_err_probe(dev, -EINVAL, + "Channel idx greater than no of channels\n"); + + chan = &st->chans[chan_num]; + *chan = ad4170_channel_template; + + chan->address = ch_reg; + chan->scan_index = ch_reg; + chan_info = &st->chan_infos[chan->address]; + + chan_info->setup_num = AD4170_INVALID_SETUP; + chan_info->initialized = true; + + setup = &chan_info->setup; + ret = ad4170_parse_reference(st, child, setup); + if (ret) + return ret; + + ret = fwnode_property_match_property_string(child, "adi,sensor-type", + ad4170_sensor_type, + ARRAY_SIZE(ad4170_sensor_type)); + + /* Default to conventional ADC channel if sensor type not present */ + s_type = ret < 0 ? AD4170_ADC_SENSOR : ret; + switch (s_type) { + case AD4170_ADC_SENSOR: + ret = ad4170_parse_adc_channel_type(dev, child, chan); + if (ret) + return ret; + + break; + case AD4170_WEIGH_SCALE_SENSOR: + case AD4170_THERMOCOUPLE_SENSOR: + case AD4170_RTD_SENSOR: + ret = ad4170_parse_external_sensor(st, child, setup, chan, s_type); + if (ret) + return ret; + + break; + default: + return -EINVAL; + } + + bipolar = fwnode_property_read_bool(child, "bipolar"); + setup->afe |= FIELD_PREP(AD4170_AFE_BIPOLAR_MSK, bipolar); + if (bipolar) + chan->scan_type.sign = 's'; + else + chan->scan_type.sign = 'u'; + + ret = ad4170_validate_channel(st, chan); + if (ret) + return ret; + + ref_select = FIELD_GET(AD4170_AFE_REF_SELECT_MSK, setup->afe); + ret = ad4170_get_input_range(st, chan, ch_reg, ref_select); + if (ret < 0) + return dev_err_probe(dev, ret, "Invalid input config\n"); + + chan_info->input_range_uv = ret; + return 0; +} + +static int ad4170_parse_channels(struct iio_dev *indio_dev) +{ + struct ad4170_state *st = iio_priv(indio_dev); + struct device *dev = &st->spi->dev; + unsigned int num_channels; + unsigned int chan_num; + int ret; + + num_channels = device_get_child_node_count(dev); + + if (num_channels > AD4170_MAX_ADC_CHANNELS) + return dev_err_probe(dev, -EINVAL, "Too many channels\n"); + + /* Add one for temperature */ + num_channels = min(num_channels + 1, AD4170_MAX_ADC_CHANNELS); + + chan_num = 0; + device_for_each_child_node_scoped(dev, child) { + ret = ad4170_parse_channel_node(indio_dev, child, chan_num++); + if (ret) + return ret; + } + + /* + * Add internal temperature sensor channel if the maximum number of + * channels has not been reached. + */ + if (num_channels < AD4170_MAX_ADC_CHANNELS) { + struct ad4170_setup *setup = &st->chan_infos[chan_num].setup; + + st->chans[chan_num] = ad4170_temp_channel_template; + st->chans[chan_num].address = chan_num; + st->chans[chan_num].scan_index = chan_num; + + st->chan_infos[chan_num].setup_num = AD4170_INVALID_SETUP; + st->chan_infos[chan_num].initialized = true; + + setup->afe |= FIELD_PREP(AD4170_AFE_REF_SELECT_MSK, + AD4170_REF_AVDD); + + ret = ad4170_get_input_range(st, &st->chans[chan_num], chan_num, + AD4170_REF_AVDD); + if (ret < 0) + return dev_err_probe(dev, ret, "Invalid input config\n"); + + st->chan_infos[chan_num].input_range_uv = ret; + chan_num++; + } + + /* Add timestamp channel */ + struct iio_chan_spec ts_chan = IIO_CHAN_SOFT_TIMESTAMP(chan_num); + + st->chans[chan_num] = ts_chan; + num_channels = num_channels + 1; + + indio_dev->num_channels = num_channels; + indio_dev->channels = st->chans; + + return 0; +} + +static struct ad4170_state *clk_hw_to_ad4170(struct clk_hw *hw) +{ + return container_of(hw, struct ad4170_state, int_clk_hw); +} + +static unsigned long ad4170_sel_clk(struct ad4170_state *st, + unsigned int clk_sel) +{ + st->clock_ctrl &= ~AD4170_CLOCK_CTRL_CLOCKSEL_MSK; + st->clock_ctrl |= FIELD_PREP(AD4170_CLOCK_CTRL_CLOCKSEL_MSK, clk_sel); + return regmap_write(st->regmap, AD4170_CLOCK_CTRL_REG, st->clock_ctrl); +} + +static unsigned long ad4170_clk_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + return AD4170_INT_CLOCK_16MHZ; +} + +static int ad4170_clk_output_is_enabled(struct clk_hw *hw) +{ + struct ad4170_state *st = clk_hw_to_ad4170(hw); + u32 clk_sel; + + clk_sel = FIELD_GET(AD4170_CLOCK_CTRL_CLOCKSEL_MSK, st->clock_ctrl); + return clk_sel == AD4170_CLOCK_CTRL_CLOCKSEL_INT_OUT; +} + +static int ad4170_clk_output_prepare(struct clk_hw *hw) +{ + struct ad4170_state *st = clk_hw_to_ad4170(hw); + + return ad4170_sel_clk(st, AD4170_CLOCK_CTRL_CLOCKSEL_INT_OUT); +} + +static void ad4170_clk_output_unprepare(struct clk_hw *hw) +{ + struct ad4170_state *st = clk_hw_to_ad4170(hw); + + ad4170_sel_clk(st, AD4170_CLOCK_CTRL_CLOCKSEL_INT); +} + +static const struct clk_ops ad4170_int_clk_ops = { + .recalc_rate = ad4170_clk_recalc_rate, + .is_enabled = ad4170_clk_output_is_enabled, + .prepare = ad4170_clk_output_prepare, + .unprepare = ad4170_clk_output_unprepare, +}; + +static int ad4170_register_clk_provider(struct iio_dev *indio_dev) +{ + struct ad4170_state *st = iio_priv(indio_dev); + struct device *dev = indio_dev->dev.parent; + struct clk_init_data init = {}; + int ret; + + if (device_property_read_string(dev, "clock-output-names", &init.name)) { + init.name = devm_kasprintf(dev, GFP_KERNEL, "%pfw", + dev_fwnode(dev)); + if (!init.name) + return -ENOMEM; + } + + init.ops = &ad4170_int_clk_ops; + + st->int_clk_hw.init = &init; + ret = devm_clk_hw_register(dev, &st->int_clk_hw); + if (ret) + return ret; + + return devm_of_clk_add_hw_provider(dev, of_clk_hw_simple_get, + &st->int_clk_hw); +} + +static int ad4170_clock_select(struct iio_dev *indio_dev) +{ + struct ad4170_state *st = iio_priv(indio_dev); + struct device *dev = &st->spi->dev; + struct clk *ext_clk; + int ret; + + ext_clk = devm_clk_get_optional_enabled(dev, NULL); + if (IS_ERR(ext_clk)) + return dev_err_probe(dev, PTR_ERR(ext_clk), + "Failed to get external clock\n"); + + if (!ext_clk) { + /* Use internal clock reference */ + st->mclk_hz = AD4170_INT_CLOCK_16MHZ; + st->clock_ctrl |= FIELD_PREP(AD4170_CLOCK_CTRL_CLOCKSEL_MSK, + AD4170_CLOCK_CTRL_CLOCKSEL_INT_OUT); + + if (!device_property_present(&st->spi->dev, "#clock-cells")) + return 0; + + return ad4170_register_clk_provider(indio_dev); + } + + /* Read optional clock-names prop to specify the external clock type */ + ret = device_property_match_property_string(dev, "clock-names", + ad4170_clk_sel, + ARRAY_SIZE(ad4170_clk_sel)); + + ret = ret < 0 ? 0 : ret; /* Default to external clock if no clock-names */ + st->clock_ctrl |= FIELD_PREP(AD4170_CLOCK_CTRL_CLOCKSEL_MSK, + AD4170_CLOCK_CTRL_CLOCKSEL_EXT + ret); + + st->mclk_hz = clk_get_rate(ext_clk); + if (st->mclk_hz < AD4170_EXT_CLOCK_MHZ_MIN || + st->mclk_hz > AD4170_EXT_CLOCK_MHZ_MAX) { + return dev_err_probe(dev, -EINVAL, + "Invalid external clock frequency %u\n", + st->mclk_hz); + } + + return 0; +} + +static int ad4170_parse_firmware(struct iio_dev *indio_dev) +{ + unsigned int vbias_pins[AD4170_MAX_ANALOG_PINS]; + struct ad4170_state *st = iio_priv(indio_dev); + struct device *dev = &st->spi->dev; + unsigned int num_vbias_pins; + int reg_data, ret; + u32 int_pin_sel; + unsigned int i; + + ret = ad4170_clock_select(indio_dev); + if (ret) + return dev_err_probe(dev, ret, "Failed to setup device clock\n"); + + ret = regmap_write(st->regmap, AD4170_CLOCK_CTRL_REG, st->clock_ctrl); + if (ret) + return ret; + + for (i = 0; i < AD4170_NUM_CURRENT_SRC; i++) + st->cur_src_pins[i] = AD4170_CURRENT_SRC_DISABLED; + + /* On power on, device defaults to using SDO pin for data ready signal */ + int_pin_sel = AD4170_INT_PIN_SDO; + ret = device_property_match_property_string(dev, "interrupt-names", + ad4170_int_pin_names, + ARRAY_SIZE(ad4170_int_pin_names)); + if (ret >= 0) + int_pin_sel = ret; + + reg_data = FIELD_PREP(AD4170_PIN_MUXING_DIG_AUX1_CTRL_MSK, + int_pin_sel == AD4170_INT_PIN_DIG_AUX1 ? + AD4170_PIN_MUXING_DIG_AUX1_RDY : + AD4170_PIN_MUXING_DIG_AUX1_DISABLED); + + ret = regmap_update_bits(st->regmap, AD4170_PIN_MUXING_REG, + AD4170_PIN_MUXING_DIG_AUX1_CTRL_MSK, reg_data); + if (ret) + return ret; + + ret = device_property_count_u32(dev, "adi,vbias-pins"); + if (ret > 0) { + if (ret > AD4170_MAX_ANALOG_PINS) + return dev_err_probe(dev, -EINVAL, + "Too many vbias pins %u\n", ret); + + num_vbias_pins = ret; + + ret = device_property_read_u32_array(dev, "adi,vbias-pins", + vbias_pins, + num_vbias_pins); + if (ret) + return dev_err_probe(dev, ret, + "Failed to read vbias pins\n"); + + for (i = 0; i < num_vbias_pins; i++) + st->pins_fn[vbias_pins[i]] |= AD4170_PIN_VBIAS; + } + + ret = ad4170_parse_channels(indio_dev); + if (ret) + return ret; + + /* Only create a GPIO chip if flagged for it */ + if (device_property_read_bool(dev, "gpio-controller")) { + ret = ad4170_gpio_init(indio_dev); + if (ret) + return ret; + } + + return 0; +} + +static int ad4170_initial_config(struct iio_dev *indio_dev) +{ + struct ad4170_state *st = iio_priv(indio_dev); + struct device *dev = &st->spi->dev; + unsigned int i; + int ret; + + ad4170_fill_sps_tbl(st); + + ret = regmap_update_bits(st->regmap, AD4170_ADC_CTRL_REG, + AD4170_ADC_CTRL_MODE_MSK, + FIELD_PREP(AD4170_ADC_CTRL_MODE_MSK, + AD4170_ADC_CTRL_MODE_IDLE)); + if (ret) + return dev_err_probe(dev, ret, + "Failed to set ADC mode to idle\n"); + + for (i = 0; i < indio_dev->num_channels; i++) { + struct ad4170_chan_info *chan_info; + struct iio_chan_spec const *chan; + struct ad4170_setup *setup; + unsigned int val; + + chan = &indio_dev->channels[i]; + if (chan->type == IIO_TIMESTAMP) + continue; + + chan_info = &st->chan_infos[chan->address]; + + setup = &chan_info->setup; + setup->gain = AD4170_GAIN_REG_DEFAULT; + ret = ad4170_write_channel_setup(st, chan->address, false); + if (ret) + return dev_err_probe(dev, ret, + "Failed to write channel setup\n"); + + val = FIELD_PREP(AD4170_CHAN_MAP_AINP_MSK, chan->channel) | + FIELD_PREP(AD4170_CHAN_MAP_AINM_MSK, chan->channel2); + + ret = regmap_write(st->regmap, AD4170_CHAN_MAP_REG(i), val); + if (ret) + return dev_err_probe(dev, ret, + "Failed to write CHAN_MAP_REG\n"); + + ret = ad4170_set_channel_freq(st, chan, + AD4170_DEFAULT_SAMP_RATE, 0); + if (ret) + return dev_err_probe(dev, ret, + "Failed to set channel freq\n"); + + ret = ad4170_fill_scale_tbl(indio_dev, chan); + if (ret) + return dev_err_probe(dev, ret, + "Failed to fill scale tbl\n"); + } + + /* Disable all channels to avoid reading from unexpected channel */ + ret = regmap_write(st->regmap, AD4170_CHAN_EN_REG, 0); + if (ret) + return dev_err_probe(dev, ret, + "Failed to disable channels\n"); + + /* + * Configure channels to share the same data output register, i.e. data + * can be read from the same register address regardless of channel + * number. + */ + return regmap_update_bits(st->regmap, AD4170_ADC_CTRL_REG, + AD4170_ADC_CTRL_MULTI_DATA_REG_SEL_MSK, + AD4170_ADC_CTRL_MULTI_DATA_REG_SEL_MSK); +} + +static int ad4170_prepare_spi_message(struct ad4170_state *st) +{ + /* + * Continuous data register read is enabled on buffer postenable so + * no instruction phase is needed meaning we don't need to send the + * register address to read data. Transfer only needs the read buffer. + */ + st->xfer.rx_buf = &st->rx_buf; + st->xfer.len = BITS_TO_BYTES(ad4170_channel_template.scan_type.realbits); + + spi_message_init_with_transfers(&st->msg, &st->xfer, 1); + + return devm_spi_optimize_message(&st->spi->dev, st->spi, &st->msg); +} + +static int ad4170_buffer_postenable(struct iio_dev *indio_dev) +{ + struct ad4170_state *st = iio_priv(indio_dev); + int ret; + + ret = regmap_update_bits(st->regmap, AD4170_ADC_CTRL_REG, + AD4170_ADC_CTRL_MODE_MSK, + FIELD_PREP(AD4170_ADC_CTRL_MODE_MSK, + AD4170_ADC_CTRL_MODE_CONT)); + if (ret) + return ret; + + /* + * This enables continuous read of the ADC data register. The ADC must + * be in continuous conversion mode. + */ + return regmap_update_bits(st->regmap, AD4170_ADC_CTRL_REG, + AD4170_ADC_CTRL_CONT_READ_MSK, + FIELD_PREP(AD4170_ADC_CTRL_CONT_READ_MSK, + AD4170_ADC_CTRL_CONT_READ_ENABLE)); +} + +static int ad4170_buffer_predisable(struct iio_dev *indio_dev) +{ + struct ad4170_state *st = iio_priv(indio_dev); + unsigned int i; + int ret; + + /* + * Use a high register address (virtual register) to request a write of + * 0xA5 to the ADC during the first 8 SCLKs of the ADC data read cycle, + * thus exiting continuous read. + */ + ret = regmap_write(st->regmap, AD4170_ADC_CTRL_CONT_READ_EXIT_REG, 0); + if (ret) + return ret; + + ret = regmap_update_bits(st->regmap, AD4170_ADC_CTRL_REG, + AD4170_ADC_CTRL_CONT_READ_MSK, + FIELD_PREP(AD4170_ADC_CTRL_CONT_READ_MSK, + AD4170_ADC_CTRL_CONT_READ_DISABLE)); + if (ret) + return ret; + + ret = regmap_update_bits(st->regmap, AD4170_ADC_CTRL_REG, + AD4170_ADC_CTRL_MODE_MSK, + FIELD_PREP(AD4170_ADC_CTRL_MODE_MSK, + AD4170_ADC_CTRL_MODE_IDLE)); + if (ret) + return ret; + + /* + * The ADC sequences through all the enabled channels (see datasheet + * page 95). That can lead to incorrect channel being read if a + * single-shot read (or buffered read with different active_scan_mask) + * is done after buffer disable. Disable all channels so only requested + * channels will be read. + */ + for (i = 0; i < indio_dev->num_channels; i++) { + if (indio_dev->channels[i].type == IIO_TIMESTAMP) + continue; + + ret = ad4170_set_channel_enable(st, i, false); + if (ret) + return ret; + } + + return 0; +} + +static bool ad4170_validate_scan_mask(struct iio_dev *indio_dev, + const unsigned long *scan_mask) +{ + unsigned int masklength = iio_get_masklength(indio_dev); + unsigned int enabled; + + /* + * The channel sequencer cycles through the enabled channels in + * sequential order, from channel 0 to channel 15, bypassing disabled + * channels. When more than one channel is enabled, channel 0 must + * always be enabled. See datasheet channel_en register description at + * page 95. + */ + enabled = bitmap_weight(scan_mask, masklength); + if (enabled > 1) + return test_bit(0, scan_mask); + + return enabled == 1; +} + +static const struct iio_buffer_setup_ops ad4170_buffer_ops = { + .postenable = ad4170_buffer_postenable, + .predisable = ad4170_buffer_predisable, + .validate_scan_mask = ad4170_validate_scan_mask, +}; + +static irqreturn_t ad4170_trigger_handler(int irq, void *p) +{ + struct iio_poll_func *pf = p; + struct iio_dev *indio_dev = pf->indio_dev; + struct ad4170_state *st = iio_priv(indio_dev); + unsigned int chan_index; + unsigned int i = 0; + int ret; + + iio_for_each_active_channel(indio_dev, chan_index) { + ret = spi_sync(st->spi, &st->msg); + if (ret) + goto err_out; + + memcpy(&st->bounce_buffer[i++], st->rx_buf, ARRAY_SIZE(st->rx_buf)); + } + + iio_push_to_buffers_with_ts(indio_dev, st->bounce_buffer, + sizeof(st->bounce_buffer), + iio_get_time_ns(indio_dev)); +err_out: + iio_trigger_notify_done(indio_dev->trig); + return IRQ_HANDLED; +} + +static const struct iio_trigger_ops ad4170_trigger_ops = { + .validate_device = iio_trigger_validate_own_device, +}; + +static irqreturn_t ad4170_irq_handler(int irq, void *dev_id) +{ + struct iio_dev *indio_dev = dev_id; + struct ad4170_state *st = iio_priv(indio_dev); + + if (iio_buffer_enabled(indio_dev)) + iio_trigger_poll(st->trig); + else + complete(&st->completion); + + return IRQ_HANDLED; +}; + +static int ad4170_trigger_setup(struct iio_dev *indio_dev) +{ + struct ad4170_state *st = iio_priv(indio_dev); + struct device *dev = &st->spi->dev; + int ret; + + st->trig = devm_iio_trigger_alloc(dev, "%s-trig%d", + indio_dev->name, + iio_device_id(indio_dev)); + if (!st->trig) + return -ENOMEM; + + st->trig->ops = &ad4170_trigger_ops; + + iio_trigger_set_drvdata(st->trig, indio_dev); + ret = devm_iio_trigger_register(dev, st->trig); + if (ret) + return dev_err_probe(dev, ret, "Failed to register trigger\n"); + + indio_dev->trig = iio_trigger_get(st->trig); + + return 0; +} + +static int ad4170_regulator_setup(struct ad4170_state *st) +{ + struct device *dev = &st->spi->dev; + int ret; + + /* Required regulators */ + ret = devm_regulator_get_enable_read_voltage(dev, "avdd"); + if (ret < 0) + return dev_err_probe(dev, ret, "Failed to get AVDD voltage.\n"); + + st->vrefs_uv[AD4170_AVDD_SUP] = ret; + + ret = devm_regulator_get_enable_read_voltage(dev, "iovdd"); + if (ret < 0) + return dev_err_probe(dev, ret, "Failed to get IOVDD voltage.\n"); + + st->vrefs_uv[AD4170_IOVDD_SUP] = ret; + + /* Optional regulators */ + ret = devm_regulator_get_enable_read_voltage(dev, "avss"); + if (ret < 0 && ret != -ENODEV) + return dev_err_probe(dev, ret, "Failed to get AVSS voltage.\n"); + + /* + * Assume AVSS at GND (0V) if not provided. + * REVISIT: AVSS is never above system ground level (i.e. AVSS is either + * GND or a negative voltage). But we currently don't have support for + * reading negative voltages with the regulator framework. So, the + * current AD4170 support reads a positive value from the regulator, + * then inverts sign to make that negative. + */ + st->vrefs_uv[AD4170_AVSS_SUP] = ret == -ENODEV ? 0 : -ret; + + ret = devm_regulator_get_enable_read_voltage(dev, "refin1p"); + if (ret < 0 && ret != -ENODEV) + return dev_err_probe(dev, ret, "Failed to get REFIN+ voltage.\n"); + + st->vrefs_uv[AD4170_REFIN1P_SUP] = ret; + + ret = devm_regulator_get_enable_read_voltage(dev, "refin1n"); + if (ret < 0 && ret != -ENODEV) + return dev_err_probe(dev, ret, "Failed to get REFIN- voltage.\n"); + + /* + * Negative supplies are assumed to provide negative voltage. + * REVISIT when support for negative regulator voltage read be available + * in the regulator framework. + */ + st->vrefs_uv[AD4170_REFIN1N_SUP] = ret == -ENODEV ? -ENODEV : -ret; + + ret = devm_regulator_get_enable_read_voltage(dev, "refin2p"); + if (ret < 0 && ret != -ENODEV) + return dev_err_probe(dev, ret, "Failed to get REFIN2+ voltage.\n"); + + st->vrefs_uv[AD4170_REFIN2P_SUP] = ret; + + ret = devm_regulator_get_enable_read_voltage(dev, "refin2n"); + if (ret < 0 && ret != -ENODEV) + return dev_err_probe(dev, ret, "Failed to get REFIN2- voltage.\n"); + + /* + * Negative supplies are assumed to provide negative voltage. + * REVISIT when support for negative regulator voltage read be available + * in the regulator framework. + */ + st->vrefs_uv[AD4170_REFIN2N_SUP] = ret == -ENODEV ? -ENODEV : -ret; + + return 0; +} + +static int ad4170_probe(struct spi_device *spi) +{ + const struct ad4170_chip_info *chip; + struct device *dev = &spi->dev; + struct iio_dev *indio_dev; + struct ad4170_state *st; + int ret; + + indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st)); + if (!indio_dev) + return -ENOMEM; + + st = iio_priv(indio_dev); + st->spi = spi; + + ret = devm_mutex_init(dev, &st->lock); + if (ret) + return ret; + + chip = spi_get_device_match_data(spi); + if (!chip) + return -EINVAL; + + indio_dev->name = chip->name; + indio_dev->info = &ad4170_info; + + st->regmap = devm_regmap_init(dev, NULL, st, &ad4170_regmap_config); + if (IS_ERR(st->regmap)) + return dev_err_probe(dev, PTR_ERR(st->regmap), + "Failed to initialize regmap\n"); + + ret = ad4170_regulator_setup(st); + if (ret) + return ret; + + ret = ad4170_soft_reset(st); + if (ret) + return ret; + + ret = ad4170_parse_firmware(indio_dev); + if (ret) + return dev_err_probe(dev, ret, "Failed to parse firmware\n"); + + ret = ad4170_initial_config(indio_dev); + if (ret) + return dev_err_probe(dev, ret, "Failed to setup device\n"); + + init_completion(&st->completion); + + if (spi->irq) { + ret = devm_request_irq(dev, spi->irq, &ad4170_irq_handler, + IRQF_NO_THREAD, indio_dev->name, indio_dev); + if (ret) + return ret; + + ret = ad4170_trigger_setup(indio_dev); + if (ret) + return ret; + } + + ret = ad4170_prepare_spi_message(st); + if (ret) + return dev_err_probe(dev, ret, "Failed to prepare SPI message\n"); + + ret = devm_iio_triggered_buffer_setup(dev, indio_dev, NULL, + &ad4170_trigger_handler, + &ad4170_buffer_ops); + if (ret) + return dev_err_probe(dev, ret, "Failed to setup read buffer\n"); + + return devm_iio_device_register(dev, indio_dev); +} + +static const struct spi_device_id ad4170_id_table[] = { + { .name = "ad4170-4", .driver_data = (kernel_ulong_t)&ad4170_chip_info }, + { .name = "ad4190-4", .driver_data = (kernel_ulong_t)&ad4190_chip_info }, + { .name = "ad4195-4", .driver_data = (kernel_ulong_t)&ad4195_chip_info }, + { } +}; +MODULE_DEVICE_TABLE(spi, ad4170_id_table); + +static const struct of_device_id ad4170_of_match[] = { + { .compatible = "adi,ad4170-4", .data = &ad4170_chip_info }, + { .compatible = "adi,ad4190-4", .data = &ad4190_chip_info }, + { .compatible = "adi,ad4195-4", .data = &ad4195_chip_info }, + { } +}; +MODULE_DEVICE_TABLE(of, ad4170_of_match); + +static struct spi_driver ad4170_driver = { + .driver = { + .name = "ad4170-4", + .of_match_table = ad4170_of_match, + }, + .probe = ad4170_probe, + .id_table = ad4170_id_table, +}; +module_spi_driver(ad4170_driver); + +MODULE_AUTHOR("Ana-Maria Cusco "); +MODULE_AUTHOR("Marcelo Schmitt "); +MODULE_DESCRIPTION("Analog Devices AD4170 SPI driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/iio/adc/ad4691.c b/drivers/iio/adc/ad4691.c new file mode 100644 index 000000000..f2f7c4c64 --- /dev/null +++ b/drivers/iio/adc/ad4691.c @@ -0,0 +1,2083 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2024-2026 Analog Devices, Inc. + * Author: Radu Sabau + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#define AD4691_VREF_uV_MIN 2400000 +#define AD4691_VREF_uV_MAX 5250000 +#define AD4691_VREF_2P5_uV_MAX 2750000 +#define AD4691_VREF_3P0_uV_MAX 3250000 +#define AD4691_VREF_3P3_uV_MAX 3750000 +#define AD4691_VREF_4P096_uV_MAX 4500000 + +#define AD4691_CNV_DUTY_CYCLE_NS 380 +#define AD4691_CNV_HIGH_TIME_NS 430 +/* + * Conservative default for the manual offload periodic trigger. Low enough + * to work safely out of the box across all OSR and channel count combinations. + */ +#define AD4691_OFFLOAD_INITIAL_TRIGGER_HZ (100 * HZ_PER_KHZ) + +#define AD4691_SPI_CONFIG_A_REG 0x000 +#define AD4691_SW_RESET (BIT(7) | BIT(0)) + +#define AD4691_STATUS_REG 0x014 +#define AD4691_CLAMP_STATUS1_REG 0x01A +#define AD4691_CLAMP_STATUS2_REG 0x01B +#define AD4691_DEVICE_SETUP 0x020 +#define AD4691_MANUAL_MODE BIT(2) +#define AD4691_LDO_EN BIT(4) +#define AD4691_REF_CTRL 0x021 +#define AD4691_REF_CTRL_MASK GENMASK(4, 2) +#define AD4691_REFBUF_EN BIT(0) +#define AD4691_OSC_FREQ_REG 0x023 +#define AD4691_OSC_FREQ_MASK GENMASK(3, 0) +#define AD4691_STD_SEQ_CONFIG 0x025 +#define AD4691_SEQ_ALL_CHANNELS_OFF 0x00 +#define AD4691_SPARE_CONTROL 0x02A + +#define AD4691_MAX_CHANNELS 16 + +#define AD4691_NOOP 0x00 +#define AD4691_ADC_CHAN(ch) ((0x10 + (ch)) << 3) +#define AD4691_EXIT_COMMAND 0x5000 + +#define AD4691_OSC_EN_REG 0x180 +#define AD4691_STATE_RESET_REG 0x181 +#define AD4691_STATE_RESET_ALL BIT(0) +#define AD4691_ADC_SETUP 0x182 +#define AD4691_ADC_MODE_MASK GENMASK(1, 0) +#define AD4691_CNV_BURST_MODE 0x01 +#define AD4691_AUTONOMOUS_MODE 0x02 +/* + * ACC_MASK_REG covers both mask bytes via ADDR_DESCENDING SPI: writing a + * 16-bit BE value to 0x185 auto-decrements to 0x184 for the second byte. + */ +#define AD4691_ACC_MASK_REG 0x185 +#define AD4691_ACC_DEPTH_IN(n) (0x186 + (n)) +#define AD4691_GPIO_MODE1_REG 0x196 +#define AD4691_GPIO_MODE2_REG 0x197 +#define AD4691_GP_MODE_MASK GENMASK(3, 0) +#define AD4691_GP_MODE_DATA_READY 0x06 +#define AD4691_GPIO_READ 0x1A0 +#define AD4691_ACC_STATUS_FULL1_REG 0x1B0 +#define AD4691_ACC_STATUS_FULL2_REG 0x1B1 +#define AD4691_ACC_STATUS_OVERRUN1_REG 0x1B2 +#define AD4691_ACC_STATUS_OVERRUN2_REG 0x1B3 +#define AD4691_ACC_STATUS_SAT1_REG 0x1B4 +#define AD4691_ACC_STATUS_SAT2_REG 0x1BE +#define AD4691_ACC_SAT_OVR_REG(n) (0x1C0 + (n)) +#define AD4691_AVG_IN(n) (0x201 + (2 * (n))) +#define AD4691_AVG_STS_IN(n) (0x222 + (3 * (n))) +#define AD4691_ACC_IN(n) (0x252 + (3 * (n))) +#define AD4691_ACC_STS_DATA(n) (0x283 + (4 * (n))) + + +static const char * const ad4691_supplies[] = { "avdd", "vio" }; + +enum ad4691_ref_ctrl { + AD4691_VREF_2P5, + AD4691_VREF_3P0, + AD4691_VREF_3P3, + AD4691_VREF_4P096, + AD4691_VREF_5P0 +}; + +struct ad4691_channel_info { + const struct iio_chan_spec *channels __counted_by_ptr(num_channels); + const struct iio_chan_spec *manual_channels __counted_by_ptr(num_channels); + unsigned int num_channels; +}; + +struct ad4691_chip_info { + const char *name; + unsigned int max_rate; + const struct ad4691_channel_info *sw_info; + const struct ad4691_channel_info *offload_info; +}; + +/* CNV burst mode channel — exposes oversampling ratio. */ +#define AD4691_CHANNEL(ch) \ + { \ + .type = IIO_VOLTAGE, \ + .indexed = 1, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SCALE) \ + | BIT(IIO_CHAN_INFO_SAMP_FREQ) \ + | BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \ + .info_mask_shared_by_all_available = \ + BIT(IIO_CHAN_INFO_SAMP_FREQ) \ + | BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \ + .channel = ch, \ + .scan_index = ch, \ + .scan_type = { \ + .format = 'u', \ + .realbits = 16, \ + .storagebits = 16, \ + .endianness = IIO_BE, \ + }, \ + } + +/* + * Manual mode channel — no oversampling ratio attribute. OSR is not + * supported in manual mode; ACC_DEPTH_IN is not configured during manual + * buffer enable. + */ +#define AD4691_MANUAL_CHANNEL(ch) \ + { \ + .type = IIO_VOLTAGE, \ + .indexed = 1, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SCALE) \ + | BIT(IIO_CHAN_INFO_SAMP_FREQ), \ + .info_mask_shared_by_all_available = \ + BIT(IIO_CHAN_INFO_SAMP_FREQ), \ + .channel = ch, \ + .scan_index = ch, \ + .scan_type = { \ + .format = 'u', \ + .realbits = 16, \ + .storagebits = 16, \ + .endianness = IIO_BE, \ + }, \ + } + +/* + * Offload path (bits_per_word=16): the SPI Engine assembles received + * bits into native 16-bit words before DMA, so samples are in + * CPU-native byte order (IIO_CPU). storagebits=16 matches the 16-bit + * DMA word size. + * + * CNV burst offload configures ACC_DEPTH_IN per channel, so the + * oversampling_ratio attribute is exposed. Manual offload does not; + * use AD4691_OFFLOAD_MANUAL_CHANNEL for that path. + */ +#define AD4691_OFFLOAD_CHANNEL(ch) \ + { \ + .type = IIO_VOLTAGE, \ + .indexed = 1, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SCALE) \ + | BIT(IIO_CHAN_INFO_SAMP_FREQ) \ + | BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \ + .info_mask_shared_by_all_available = \ + BIT(IIO_CHAN_INFO_SAMP_FREQ) \ + | BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \ + .channel = ch, \ + .scan_index = ch, \ + .scan_type = { \ + .format = 'u', \ + .realbits = 16, \ + .storagebits = 16, \ + }, \ + } + +/* Manual offload — same IIO_CPU layout but no oversampling_ratio attribute. */ +#define AD4691_OFFLOAD_MANUAL_CHANNEL(ch) \ + { \ + .type = IIO_VOLTAGE, \ + .indexed = 1, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SCALE) \ + | BIT(IIO_CHAN_INFO_SAMP_FREQ), \ + .info_mask_shared_by_all_available = \ + BIT(IIO_CHAN_INFO_SAMP_FREQ), \ + .channel = ch, \ + .scan_index = ch, \ + .scan_type = { \ + .format = 'u', \ + .realbits = 16, \ + .storagebits = 16, \ + }, \ + } + +static const struct iio_chan_spec ad4691_channels[] = { + AD4691_CHANNEL(0), + AD4691_CHANNEL(1), + AD4691_CHANNEL(2), + AD4691_CHANNEL(3), + AD4691_CHANNEL(4), + AD4691_CHANNEL(5), + AD4691_CHANNEL(6), + AD4691_CHANNEL(7), + AD4691_CHANNEL(8), + AD4691_CHANNEL(9), + AD4691_CHANNEL(10), + AD4691_CHANNEL(11), + AD4691_CHANNEL(12), + AD4691_CHANNEL(13), + AD4691_CHANNEL(14), + AD4691_CHANNEL(15), + IIO_CHAN_SOFT_TIMESTAMP(16), +}; + +static const struct iio_chan_spec ad4693_channels[] = { + AD4691_CHANNEL(0), + AD4691_CHANNEL(1), + AD4691_CHANNEL(2), + AD4691_CHANNEL(3), + AD4691_CHANNEL(4), + AD4691_CHANNEL(5), + AD4691_CHANNEL(6), + AD4691_CHANNEL(7), + IIO_CHAN_SOFT_TIMESTAMP(8), +}; + +/* + * Offload channel arrays: no IIO_CHAN_SOFT_TIMESTAMP because DMA delivers + * data directly to userspace without a software timestamp. + */ +static const struct iio_chan_spec ad4691_offload_channels[] = { + AD4691_OFFLOAD_CHANNEL(0), + AD4691_OFFLOAD_CHANNEL(1), + AD4691_OFFLOAD_CHANNEL(2), + AD4691_OFFLOAD_CHANNEL(3), + AD4691_OFFLOAD_CHANNEL(4), + AD4691_OFFLOAD_CHANNEL(5), + AD4691_OFFLOAD_CHANNEL(6), + AD4691_OFFLOAD_CHANNEL(7), + AD4691_OFFLOAD_CHANNEL(8), + AD4691_OFFLOAD_CHANNEL(9), + AD4691_OFFLOAD_CHANNEL(10), + AD4691_OFFLOAD_CHANNEL(11), + AD4691_OFFLOAD_CHANNEL(12), + AD4691_OFFLOAD_CHANNEL(13), + AD4691_OFFLOAD_CHANNEL(14), + AD4691_OFFLOAD_CHANNEL(15), +}; + +static const struct iio_chan_spec ad4693_offload_channels[] = { + AD4691_OFFLOAD_CHANNEL(0), + AD4691_OFFLOAD_CHANNEL(1), + AD4691_OFFLOAD_CHANNEL(2), + AD4691_OFFLOAD_CHANNEL(3), + AD4691_OFFLOAD_CHANNEL(4), + AD4691_OFFLOAD_CHANNEL(5), + AD4691_OFFLOAD_CHANNEL(6), + AD4691_OFFLOAD_CHANNEL(7), +}; + +static const struct iio_chan_spec ad4691_manual_channels[] = { + AD4691_MANUAL_CHANNEL(0), + AD4691_MANUAL_CHANNEL(1), + AD4691_MANUAL_CHANNEL(2), + AD4691_MANUAL_CHANNEL(3), + AD4691_MANUAL_CHANNEL(4), + AD4691_MANUAL_CHANNEL(5), + AD4691_MANUAL_CHANNEL(6), + AD4691_MANUAL_CHANNEL(7), + AD4691_MANUAL_CHANNEL(8), + AD4691_MANUAL_CHANNEL(9), + AD4691_MANUAL_CHANNEL(10), + AD4691_MANUAL_CHANNEL(11), + AD4691_MANUAL_CHANNEL(12), + AD4691_MANUAL_CHANNEL(13), + AD4691_MANUAL_CHANNEL(14), + AD4691_MANUAL_CHANNEL(15), + IIO_CHAN_SOFT_TIMESTAMP(16), +}; + +static const struct iio_chan_spec ad4693_manual_channels[] = { + AD4691_MANUAL_CHANNEL(0), + AD4691_MANUAL_CHANNEL(1), + AD4691_MANUAL_CHANNEL(2), + AD4691_MANUAL_CHANNEL(3), + AD4691_MANUAL_CHANNEL(4), + AD4691_MANUAL_CHANNEL(5), + AD4691_MANUAL_CHANNEL(6), + AD4691_MANUAL_CHANNEL(7), + IIO_CHAN_SOFT_TIMESTAMP(8), +}; + +static const struct iio_chan_spec ad4691_offload_manual_channels[] = { + AD4691_OFFLOAD_MANUAL_CHANNEL(0), + AD4691_OFFLOAD_MANUAL_CHANNEL(1), + AD4691_OFFLOAD_MANUAL_CHANNEL(2), + AD4691_OFFLOAD_MANUAL_CHANNEL(3), + AD4691_OFFLOAD_MANUAL_CHANNEL(4), + AD4691_OFFLOAD_MANUAL_CHANNEL(5), + AD4691_OFFLOAD_MANUAL_CHANNEL(6), + AD4691_OFFLOAD_MANUAL_CHANNEL(7), + AD4691_OFFLOAD_MANUAL_CHANNEL(8), + AD4691_OFFLOAD_MANUAL_CHANNEL(9), + AD4691_OFFLOAD_MANUAL_CHANNEL(10), + AD4691_OFFLOAD_MANUAL_CHANNEL(11), + AD4691_OFFLOAD_MANUAL_CHANNEL(12), + AD4691_OFFLOAD_MANUAL_CHANNEL(13), + AD4691_OFFLOAD_MANUAL_CHANNEL(14), + AD4691_OFFLOAD_MANUAL_CHANNEL(15), +}; + +static const struct iio_chan_spec ad4693_offload_manual_channels[] = { + AD4691_OFFLOAD_MANUAL_CHANNEL(0), + AD4691_OFFLOAD_MANUAL_CHANNEL(1), + AD4691_OFFLOAD_MANUAL_CHANNEL(2), + AD4691_OFFLOAD_MANUAL_CHANNEL(3), + AD4691_OFFLOAD_MANUAL_CHANNEL(4), + AD4691_OFFLOAD_MANUAL_CHANNEL(5), + AD4691_OFFLOAD_MANUAL_CHANNEL(6), + AD4691_OFFLOAD_MANUAL_CHANNEL(7), +}; + +static const int ad4691_oversampling_ratios[] = { 1, 2, 4, 8, 16, 32 }; + +static const struct ad4691_channel_info ad4691_sw_info = { + .channels = ad4691_channels, + .manual_channels = ad4691_manual_channels, + .num_channels = ARRAY_SIZE(ad4691_channels), +}; + +static const struct ad4691_channel_info ad4693_sw_info = { + .channels = ad4693_channels, + .manual_channels = ad4693_manual_channels, + .num_channels = ARRAY_SIZE(ad4693_channels), +}; + +static const struct ad4691_channel_info ad4691_offload_info = { + .channels = ad4691_offload_channels, + .manual_channels = ad4691_offload_manual_channels, + .num_channels = ARRAY_SIZE(ad4691_offload_channels), +}; + +static const struct ad4691_channel_info ad4693_offload_info = { + .channels = ad4693_offload_channels, + .manual_channels = ad4693_offload_manual_channels, + .num_channels = ARRAY_SIZE(ad4693_offload_channels), +}; + +/* + * Internal oscillator frequency table. Index is the OSC_FREQ_REG[3:0] value. + * Index 0 (1 MHz) is only valid for AD4692/AD4694; AD4691/AD4693 support + * up to 500 kHz and use index 1 as their highest valid rate. + */ +static const int ad4691_osc_freqs_Hz[] = { + [0x0] = 1000000, + [0x1] = 500000, + [0x2] = 400000, + [0x3] = 250000, + [0x4] = 200000, + [0x5] = 167000, + [0x6] = 133000, + [0x7] = 125000, + [0x8] = 100000, + [0x9] = 50000, + [0xA] = 25000, + [0xB] = 12500, + [0xC] = 10000, + [0xD] = 5000, + [0xE] = 2500, + [0xF] = 1250, +}; + +static const char * const ad4691_gp_names[] = { "gp0", "gp1", "gp2", "gp3" }; + +static const struct ad4691_chip_info ad4691_chip_info = { + .name = "ad4691", + .max_rate = 500 * HZ_PER_KHZ, + .sw_info = &ad4691_sw_info, + .offload_info = &ad4691_offload_info, +}; + +static const struct ad4691_chip_info ad4692_chip_info = { + .name = "ad4692", + .max_rate = 1 * HZ_PER_MHZ, + .sw_info = &ad4691_sw_info, + .offload_info = &ad4691_offload_info, +}; + +static const struct ad4691_chip_info ad4693_chip_info = { + .name = "ad4693", + .max_rate = 500 * HZ_PER_KHZ, + .sw_info = &ad4693_sw_info, + .offload_info = &ad4693_offload_info, +}; + +static const struct ad4691_chip_info ad4694_chip_info = { + .name = "ad4694", + .max_rate = 1 * HZ_PER_MHZ, + .sw_info = &ad4693_sw_info, + .offload_info = &ad4693_offload_info, +}; + +struct ad4691_state { + const struct ad4691_chip_info *info; + struct regmap *regmap; + struct spi_device *spi; + + struct pwm_device *conv_trigger; + int irq; + int vref_uV; + u32 cnv_period_ns; + /* + * Snapped oscillator frequency (Hz) shared by all channels. Set when + * sampling_frequency or oversampling_ratio is written; written to + * OSC_FREQ_REG at buffer enable and single-shot time so both attributes + * can be set in any order. Reading in_voltage_sampling_frequency + * returns target_osc_freq_Hz / osr — the effective rate given the + * shared oversampling ratio. + */ + u32 target_osc_freq_Hz; + /* Shared oversampling ratio across all channels; always 1 in manual mode. */ + unsigned int osr; + /* + * Precomputed effective-rate lists, one row per entry in + * ad4691_oversampling_ratios[]. Populated at probe; read_avail picks + * the row for the current shared OSR. The tables are stable after + * probe so returning a pointer into them from read_avail is race-free. + */ + int samp_freq_avail[ARRAY_SIZE(ad4691_oversampling_ratios)][ARRAY_SIZE(ad4691_osc_freqs_Hz)]; + int samp_freq_avail_len[ARRAY_SIZE(ad4691_oversampling_ratios)]; + + bool manual_mode; + bool irq_enabled; + bool refbuf_en; + bool ldo_en; + /* + * Synchronize access to members of the driver state, and ensure + * atomicity of consecutive SPI operations. + */ + struct mutex lock; + /* NULL when no SPI offload hardware is present. */ + struct spi_offload *offload; + struct spi_offload_trigger *offload_trigger; + u64 trigger_hz; + /* + * Per-buffer-enable lifetime resources: + * Manual Mode - a pre-built SPI message that clocks out N+1 + * transfers in one go. + * CNV Burst Mode - a pre-built SPI message that clocks out 2*N + * transfers in one go. + */ + struct spi_message scan_msg; + /* + * max 16 + 1 NOOP (manual) or 2*16 + 1 state-reset (CNV burst). + */ + struct spi_transfer scan_xfers[34]; + /* + * CNV burst: 16 AVG_IN addresses = 16. Manual: 16 channel cmds + + * 1 NOOP = 17. Stored as native u16. The non-offload path fills slots + * with put_unaligned_be16() (bits_per_word=8, bytes go out in memory + * order). The offload path assigns native values directly + * (bits_per_word=bpw, SPI reads each slot as a native 16-bit word and + * shifts it out MSB-first). + */ + u16 scan_tx[17] __aligned(IIO_DMA_MINALIGN); + /* + * CNV burst state-reset: 4-byte write [addr_hi, addr_lo, + * STATE_RESET_ALL, OSC_EN=1]. CS is asserted throughout, so + * ADDR_DESCENDING writes byte[3]=1 to OSC_EN_REG (0x180) as a + * deliberate side-write, keeping the oscillator enabled. Shared + * with the offload path (mutually exclusive at probe). + */ + u8 scan_tx_reset[4] __aligned(IIO_DMA_MINALIGN); + /* + * Scan buffer: one BE16 slot per active channel, plus timestamp. + * DMA-aligned because scan_xfers point rx_buf directly into vals[]. + */ + IIO_DECLARE_DMA_BUFFER_WITH_TS(__be16, vals, 16); +}; + +/* + * Configure the given GP pin (0-3) as DATA_READY output. + * GP0/GP1 → GPIO_MODE1_REG, GP2/GP3 → GPIO_MODE2_REG. + * Even pins occupy bits [3:0], odd pins bits [7:4]. + */ +static int ad4691_gpio_setup(struct ad4691_state *st, unsigned int gp_num) +{ + unsigned int bit_off = gp_num % 2; + unsigned int reg_off = gp_num / 2; + unsigned int shift = 4 * bit_off; + + return regmap_update_bits(st->regmap, + AD4691_GPIO_MODE1_REG + reg_off, + AD4691_GP_MODE_MASK << shift, + AD4691_GP_MODE_DATA_READY << shift); +} + +static const struct spi_offload_config ad4691_offload_config = { + .capability_flags = SPI_OFFLOAD_CAP_TRIGGER | + SPI_OFFLOAD_CAP_RX_STREAM_DMA, +}; + +static bool ad4691_offload_trigger_match(struct spi_offload_trigger *trigger, + enum spi_offload_trigger_type type, + u64 *args, u32 nargs) +{ + return type == SPI_OFFLOAD_TRIGGER_DATA_READY && nargs == 1 && args[0] <= 3; +} + +static int ad4691_offload_trigger_request(struct spi_offload_trigger *trigger, + enum spi_offload_trigger_type type, + u64 *args, u32 nargs) +{ + struct ad4691_state *st = spi_offload_trigger_get_priv(trigger); + + if (nargs != 1 || args[0] > 3) + return -EINVAL; + + return ad4691_gpio_setup(st, args[0]); +} + +static int ad4691_offload_trigger_validate(struct spi_offload_trigger *trigger, + struct spi_offload_trigger_config *config) +{ + if (config->type != SPI_OFFLOAD_TRIGGER_DATA_READY) + return -EINVAL; + + return 0; +} + +static const struct spi_offload_trigger_ops ad4691_offload_trigger_ops = { + .match = ad4691_offload_trigger_match, + .request = ad4691_offload_trigger_request, + .validate = ad4691_offload_trigger_validate, +}; + +static int ad4691_reg_read(void *context, unsigned int reg, unsigned int *val) +{ + struct spi_device *spi = context; + u8 tx[2], rx[4]; + int ret; + + /* Set bit 15 to mark the operation as READ. */ + put_unaligned_be16(0x8000 | reg, tx); + + switch (reg) { + case 0 ... AD4691_OSC_FREQ_REG: + case AD4691_SPARE_CONTROL ... AD4691_ACC_MASK_REG - 1: + case AD4691_ACC_MASK_REG + 1 ... AD4691_ACC_SAT_OVR_REG(15): + ret = spi_write_then_read(spi, tx, sizeof(tx), rx, 1); + if (ret) + return ret; + *val = rx[0]; + return 0; + case AD4691_ACC_MASK_REG: + case AD4691_STD_SEQ_CONFIG: + case AD4691_AVG_IN(0) ... AD4691_AVG_IN(15): + ret = spi_write_then_read(spi, tx, sizeof(tx), rx, 2); + if (ret) + return ret; + *val = get_unaligned_be16(rx); + return 0; + case AD4691_AVG_STS_IN(0) ... AD4691_AVG_STS_IN(15): + case AD4691_ACC_IN(0) ... AD4691_ACC_IN(15): + ret = spi_write_then_read(spi, tx, sizeof(tx), rx, 3); + if (ret) + return ret; + *val = get_unaligned_be24(rx); + return 0; + case AD4691_ACC_STS_DATA(0) ... AD4691_ACC_STS_DATA(15): + ret = spi_write_then_read(spi, tx, sizeof(tx), rx, 4); + if (ret) + return ret; + *val = get_unaligned_be32(rx); + return 0; + default: + return -EINVAL; + } +} + +static int ad4691_reg_write(void *context, unsigned int reg, unsigned int val) +{ + struct spi_device *spi = context; + u8 tx[4]; + + put_unaligned_be16(reg, tx); + + switch (reg) { + case 0 ... AD4691_OSC_FREQ_REG: + case AD4691_SPARE_CONTROL ... AD4691_ACC_MASK_REG - 1: + case AD4691_ACC_MASK_REG + 1 ... AD4691_GPIO_MODE2_REG: + if (val > U8_MAX) + return -EINVAL; + tx[2] = val; + return spi_write_then_read(spi, tx, 3, NULL, 0); + case AD4691_ACC_MASK_REG: + case AD4691_STD_SEQ_CONFIG: + if (val > U16_MAX) + return -EINVAL; + put_unaligned_be16(val, &tx[2]); + return spi_write_then_read(spi, tx, 4, NULL, 0); + default: + return -EINVAL; + } +} + +static bool ad4691_volatile_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case AD4691_STATUS_REG: + case AD4691_CLAMP_STATUS1_REG: + case AD4691_CLAMP_STATUS2_REG: + case AD4691_GPIO_READ: + case AD4691_ACC_STATUS_FULL1_REG ... AD4691_ACC_STATUS_SAT2_REG: + case AD4691_ACC_SAT_OVR_REG(0) ... AD4691_ACC_SAT_OVR_REG(15): + case AD4691_AVG_IN(0) ... AD4691_AVG_IN(15): + case AD4691_AVG_STS_IN(0) ... AD4691_AVG_STS_IN(15): + case AD4691_ACC_IN(0) ... AD4691_ACC_IN(15): + case AD4691_ACC_STS_DATA(0) ... AD4691_ACC_STS_DATA(15): + return true; + default: + return false; + } +} + +static bool ad4691_readable_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case 0 ... AD4691_OSC_FREQ_REG: + case AD4691_SPARE_CONTROL ... AD4691_ACC_SAT_OVR_REG(15): + case AD4691_STD_SEQ_CONFIG: + return true; + default: + break; + } + + /* + * Multi-byte result registers have non-unit strides; only the base + * address of each entry is a valid single-register read. + */ + if (reg >= AD4691_AVG_IN(0) && reg <= AD4691_AVG_IN(15)) + return (reg - AD4691_AVG_IN(0)) % 2 == 0; + if (reg >= AD4691_AVG_STS_IN(0) && reg <= AD4691_AVG_STS_IN(15)) + return (reg - AD4691_AVG_STS_IN(0)) % 3 == 0; + if (reg >= AD4691_ACC_IN(0) && reg <= AD4691_ACC_IN(15)) + return (reg - AD4691_ACC_IN(0)) % 3 == 0; + if (reg >= AD4691_ACC_STS_DATA(0) && reg <= AD4691_ACC_STS_DATA(15)) + return (reg - AD4691_ACC_STS_DATA(0)) % 4 == 0; + + return false; +} + +static bool ad4691_writeable_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case 0 ... AD4691_OSC_FREQ_REG: + case AD4691_STD_SEQ_CONFIG: + case AD4691_SPARE_CONTROL ... AD4691_GPIO_MODE2_REG: + return true; + default: + return false; + } +} + +static const struct regmap_config ad4691_regmap_config = { + .reg_bits = 16, + .val_bits = 32, + .reg_read = ad4691_reg_read, + .reg_write = ad4691_reg_write, + .volatile_reg = ad4691_volatile_reg, + .readable_reg = ad4691_readable_reg, + .writeable_reg = ad4691_writeable_reg, + .max_register = AD4691_ACC_STS_DATA(15), + .cache_type = REGCACHE_MAPLE, +}; + +/* + * Index 0 in ad4691_osc_freqs_Hz is 1 MHz — valid only for AD4692/AD4694 + * (max_rate == 1 MHz). AD4691/AD4693 cap at 500 kHz so their valid range + * starts at index 1. + */ +static unsigned int ad4691_samp_freq_start(const struct ad4691_chip_info *info) +{ + return (info->max_rate == 1 * HZ_PER_MHZ) ? 0 : 1; +} + +/* + * Find the largest oscillator table entry that is both <= needed_osc and + * evenly divisible by osr (guaranteeing an integer effective rate on + * read-back). Returns 0 if no such entry exists in the chip's valid range. + */ +static unsigned int ad4691_find_osc_freq(struct ad4691_state *st, + unsigned int needed_osc, + unsigned int osr) +{ + unsigned int start = ad4691_samp_freq_start(st->info); + + for (unsigned int i = start; i < ARRAY_SIZE(ad4691_osc_freqs_Hz); i++) { + if ((unsigned int)ad4691_osc_freqs_Hz[i] > needed_osc) + continue; + if (ad4691_osc_freqs_Hz[i] % osr) + continue; + return ad4691_osc_freqs_Hz[i]; + } + return 0; +} + +/* Write target_osc_freq_Hz to OSC_FREQ_REG. Called at use time. */ +static int ad4691_write_osc_freq(struct ad4691_state *st) +{ + for (unsigned int i = 0; i < ARRAY_SIZE(ad4691_osc_freqs_Hz); i++) { + if (ad4691_osc_freqs_Hz[i] == st->target_osc_freq_Hz) + return regmap_write(st->regmap, AD4691_OSC_FREQ_REG, i); + } + return -EINVAL; +} + +/* Return the index of osr in ad4691_oversampling_ratios[], defaulting to 0. */ +static unsigned int ad4691_osr_index(unsigned int osr) +{ + for (unsigned int i = 0; i < ARRAY_SIZE(ad4691_oversampling_ratios) - 1; i++) { + if ((unsigned int)ad4691_oversampling_ratios[i] == osr) + return i; + } + return ARRAY_SIZE(ad4691_oversampling_ratios) - 1; +} + +/* + * Precompute samp_freq_avail[][]: for each OSR value, list the oscillator + * table entries that divide evenly by that OSR, expressed as effective rates + * (osc_freq / osr). Called once at probe after st->info is set. + */ +static void ad4691_precompute_samp_freq_avail(struct ad4691_state *st) +{ + unsigned int start = ad4691_samp_freq_start(st->info); + + for (unsigned int i = 0; i < ARRAY_SIZE(ad4691_oversampling_ratios); i++) { + unsigned int osr = ad4691_oversampling_ratios[i]; + int n = 0; + + for (unsigned int j = start; j < ARRAY_SIZE(ad4691_osc_freqs_Hz); j++) { + if (ad4691_osc_freqs_Hz[j] % osr) + continue; + st->samp_freq_avail[i][n++] = ad4691_osc_freqs_Hz[j] / osr; + } + st->samp_freq_avail_len[i] = n; + } +} + +static int ad4691_set_sampling_freq(struct ad4691_state *st, int freq) +{ + unsigned int osr, found; + + /* + * Read osr under st->lock: osr and target_osc_freq_Hz are modified + * together under the lock; reading after acquiring it ensures we see + * a consistent snapshot with no concurrent write racing us. + */ + guard(mutex)(&st->lock); + osr = st->osr; + + if (freq <= 0 || (unsigned int)freq > st->info->max_rate / osr) + return -EINVAL; + + found = ad4691_find_osc_freq(st, (unsigned int)freq * osr, osr); + if (!found) + return -EINVAL; + + /* + * Store the snapped oscillator frequency; OSC_FREQ_REG is written at + * buffer enable and single-shot time so that sampling_frequency and + * oversampling_ratio can be set in any order. + */ + st->target_osc_freq_Hz = found; + return 0; +} + +static int ad4691_read_avail(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + const int **vals, int *type, + int *length, long mask) +{ + struct ad4691_state *st = iio_priv(indio_dev); + + switch (mask) { + case IIO_CHAN_INFO_SAMP_FREQ: { + unsigned int osr_idx; + + /* + * The precomputed tables are stable after probe; only the + * current OSR needs to be read under the lock to pick the + * right row atomically. + */ + guard(mutex)(&st->lock); + osr_idx = ad4691_osr_index(st->osr); + *vals = st->samp_freq_avail[osr_idx]; + *type = IIO_VAL_INT; + *length = st->samp_freq_avail_len[osr_idx]; + return IIO_AVAIL_LIST; + } + case IIO_CHAN_INFO_OVERSAMPLING_RATIO: + *vals = ad4691_oversampling_ratios; + *type = IIO_VAL_INT; + *length = ARRAY_SIZE(ad4691_oversampling_ratios); + return IIO_AVAIL_LIST; + default: + return -EINVAL; + } +} + +static int ad4691_single_shot_read(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int *val) +{ + struct ad4691_state *st = iio_priv(indio_dev); + unsigned int reg_val, period_us; + int ret; + + guard(mutex)(&st->lock); + + /* Use AUTONOMOUS mode for single-shot reads. */ + ret = regmap_write(st->regmap, AD4691_STATE_RESET_REG, AD4691_STATE_RESET_ALL); + if (ret) + return ret; + + ret = regmap_write(st->regmap, AD4691_STD_SEQ_CONFIG, + BIT(chan->channel)); + if (ret) + return ret; + + ret = regmap_write(st->regmap, AD4691_ACC_MASK_REG, + ~BIT(chan->channel) & GENMASK(15, 0)); + if (ret) + return ret; + + ret = regmap_write(st->regmap, AD4691_ACC_DEPTH_IN(0), st->osr); + if (ret) + return ret; + + ret = ad4691_write_osc_freq(st); + if (ret) + return ret; + + ret = regmap_write(st->regmap, AD4691_OSC_EN_REG, 1); + if (ret) + return ret; + + /* + * Wait osr + 1 oscillator periods: osr for accumulation, +1 for the + * pipeline margin (one extra period ensures the final result is ready). + */ + period_us = DIV_ROUND_UP((st->osr + 1) * USEC_PER_SEC, + st->target_osc_freq_Hz); + fsleep(period_us); + + ret = regmap_write(st->regmap, AD4691_OSC_EN_REG, 0); + if (ret) + return ret; + + ret = regmap_read(st->regmap, AD4691_AVG_IN(chan->channel), ®_val); + if (ret) + return ret; + + *val = reg_val; + + ret = regmap_write(st->regmap, AD4691_STATE_RESET_REG, AD4691_STATE_RESET_ALL); + if (ret) + return ret; + + return IIO_VAL_INT; +} + +static int ad4691_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int *val, + int *val2, long info) +{ + struct ad4691_state *st = iio_priv(indio_dev); + + switch (info) { + case IIO_CHAN_INFO_RAW: { + IIO_DEV_ACQUIRE_DIRECT_MODE(indio_dev, claim); + if (IIO_DEV_ACQUIRE_FAILED(claim)) + return -EBUSY; + + return ad4691_single_shot_read(indio_dev, chan, val); + } + case IIO_CHAN_INFO_SAMP_FREQ: { + /* + * Read target_osc_freq_Hz and osr under st->lock to get a + * consistent snapshot: write_raw for SAMP_FREQ or OSR modifies + * both fields under the lock, so a concurrent read without the + * lock could observe a new oscillator frequency with the old OSR. + */ + guard(mutex)(&st->lock); + *val = st->target_osc_freq_Hz / st->osr; + return IIO_VAL_INT; + } + case IIO_CHAN_INFO_OVERSAMPLING_RATIO: { + guard(mutex)(&st->lock); + *val = st->osr; + return IIO_VAL_INT; + } + case IIO_CHAN_INFO_SCALE: + *val = st->vref_uV / (MICRO / MILLI); + *val2 = chan->scan_type.realbits; + return IIO_VAL_FRACTIONAL_LOG2; + default: + return -EINVAL; + } +} + +static int ad4691_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, int val2, long mask) +{ + struct ad4691_state *st = iio_priv(indio_dev); + + IIO_DEV_ACQUIRE_DIRECT_MODE(indio_dev, claim); + if (IIO_DEV_ACQUIRE_FAILED(claim)) + return -EBUSY; + + switch (mask) { + case IIO_CHAN_INFO_SAMP_FREQ: + return ad4691_set_sampling_freq(st, val); + case IIO_CHAN_INFO_OVERSAMPLING_RATIO: { + unsigned int old_effective, found, osr_idx; + + osr_idx = ad4691_osr_index(val); + if (ad4691_oversampling_ratios[osr_idx] != val) + return -EINVAL; + + /* + * Hold st->lock while computing the new oscillator frequency + * and updating both target_osc_freq_Hz and osr atomically: + * read_raw for SAMP_FREQ reads both fields under the lock and + * must see a consistent pair (new osc ↔ new osr). + * + * Snap target_osc_freq_Hz to the largest table entry that is + * both <= old_effective * new_osr and evenly divisible by + * new_osr, preserving an integer read-back of + * in_voltage_sampling_frequency after the OSR change. + */ + guard(mutex)(&st->lock); + old_effective = st->target_osc_freq_Hz / st->osr; + found = ad4691_find_osc_freq(st, old_effective * (unsigned int)val, val); + if (!found) + return -EINVAL; + st->target_osc_freq_Hz = found; + st->osr = val; + return 0; + } + default: + return -EINVAL; + } +} + +static int ad4691_reg_access(struct iio_dev *indio_dev, unsigned int reg, + unsigned int writeval, unsigned int *readval) +{ + struct ad4691_state *st = iio_priv(indio_dev); + + guard(mutex)(&st->lock); + + if (readval) + return regmap_read(st->regmap, reg, readval); + + return regmap_write(st->regmap, reg, writeval); +} + +static int ad4691_set_pwm_freq(struct ad4691_state *st, unsigned int freq) +{ + if (!freq) + return -EINVAL; + + st->cnv_period_ns = DIV_ROUND_UP(NSEC_PER_SEC, freq); + return 0; +} + +static int ad4691_sampling_enable(struct ad4691_state *st, bool enable) +{ + struct pwm_state conv_state = { + .period = st->cnv_period_ns, + .duty_cycle = AD4691_CNV_DUTY_CYCLE_NS, + .polarity = PWM_POLARITY_NORMAL, + .enabled = enable, + }; + + return pwm_apply_might_sleep(st->conv_trigger, &conv_state); +} + +/* + * ad4691_enter_conversion_mode - Switch the chip to its buffer conversion mode. + * + * Configures the ADC hardware registers for the mode selected at probe + * (CNV_BURST or MANUAL). Called from buffer preenable before starting + * sampling. The chip is in AUTONOMOUS mode during idle (for read_raw). + */ +static int ad4691_enter_conversion_mode(struct ad4691_state *st) +{ + int ret; + + if (st->manual_mode) + return regmap_update_bits(st->regmap, AD4691_DEVICE_SETUP, + AD4691_MANUAL_MODE, AD4691_MANUAL_MODE); + + ret = ad4691_write_osc_freq(st); + if (ret) + return ret; + + ret = regmap_update_bits(st->regmap, AD4691_ADC_SETUP, + AD4691_ADC_MODE_MASK, AD4691_CNV_BURST_MODE); + if (ret) + return ret; + + return regmap_write(st->regmap, AD4691_STATE_RESET_REG, + AD4691_STATE_RESET_ALL); +} + +static int ad4691_transfer(struct ad4691_state *st, u16 cmd) +{ + u8 buf[2]; + + put_unaligned_be16(cmd, buf); + + return spi_write_then_read(st->spi, buf, sizeof(buf), NULL, 0); +} + +/* + * ad4691_exit_conversion_mode - Return the chip to AUTONOMOUS mode. + * + * Called from buffer postdisable to restore the chip to the + * idle state used by read_raw. Clears the sequencer and resets state. + */ +static int ad4691_exit_conversion_mode(struct ad4691_state *st) +{ + if (st->manual_mode) + return ad4691_transfer(st, AD4691_EXIT_COMMAND); + + return regmap_update_bits(st->regmap, AD4691_ADC_SETUP, + AD4691_ADC_MODE_MASK, AD4691_AUTONOMOUS_MODE); +} + +static int ad4691_manual_buffer_preenable(struct iio_dev *indio_dev) +{ + struct ad4691_state *st = iio_priv(indio_dev); + unsigned int k, i; + int ret; + + memset(st->scan_xfers, 0, sizeof(st->scan_xfers)); + memset(st->scan_tx, 0, sizeof(st->scan_tx)); + + spi_message_init(&st->scan_msg); + + k = 0; + iio_for_each_active_channel(indio_dev, i) { + /* + * Channel-select command occupies the first (high) byte of the + * 16-bit DIN frame; the second byte is a don't-care zero pad. + * put_unaligned_be16() writes [cmd, 0x00] in memory so the + * SPI controller sends the command byte first on the wire. + */ + put_unaligned_be16((u16)(AD4691_ADC_CHAN(i) << 8), &st->scan_tx[k]); + st->scan_xfers[k].tx_buf = &st->scan_tx[k]; + /* + * The pipeline means xfer[0] receives the residual from the + * previous sequence, not a valid sample. Discard it (rx_buf=NULL) + * to avoid aliasing vals[0] across two concurrent DMA mappings. + * xfer[1] (or the NOOP when only one channel is active) writes + * the real ch[0] result to vals[0]. Subsequent transfers write + * into vals[k-1] so each result lands at the next dense slot. + */ + st->scan_xfers[k].rx_buf = (k == 0) ? NULL : &st->vals[k - 1]; + st->scan_xfers[k].len = sizeof(*st->scan_tx); + st->scan_xfers[k].cs_change = 1; + st->scan_xfers[k].cs_change_delay.value = AD4691_CNV_HIGH_TIME_NS; + st->scan_xfers[k].cs_change_delay.unit = SPI_DELAY_UNIT_NSECS; + spi_message_add_tail(&st->scan_xfers[k], &st->scan_msg); + k++; + } + + /* Final NOOP transfer retrieves the last channel's result. */ + st->scan_xfers[k].tx_buf = &st->scan_tx[k]; /* scan_tx[k] == 0 == NOOP */ + st->scan_xfers[k].rx_buf = &st->vals[k - 1]; + st->scan_xfers[k].len = sizeof(*st->scan_tx); + spi_message_add_tail(&st->scan_xfers[k], &st->scan_msg); + + ret = spi_optimize_message(st->spi, &st->scan_msg); + if (ret) + return ret; + + ret = ad4691_enter_conversion_mode(st); + if (ret) { + spi_unoptimize_message(&st->scan_msg); + return ret; + } + + return 0; +} + +static int ad4691_manual_buffer_postdisable(struct iio_dev *indio_dev) +{ + struct ad4691_state *st = iio_priv(indio_dev); + int ret; + + ret = ad4691_exit_conversion_mode(st); + spi_unoptimize_message(&st->scan_msg); + return ret; +} + +static const struct iio_buffer_setup_ops ad4691_manual_buffer_setup_ops = { + .preenable = ad4691_manual_buffer_preenable, + .postdisable = ad4691_manual_buffer_postdisable, +}; + +static int ad4691_cnv_burst_buffer_preenable(struct iio_dev *indio_dev) +{ + struct ad4691_state *st = iio_priv(indio_dev); + unsigned int acc_mask, std_seq_config; + unsigned int k, i; + int ret; + + memset(st->scan_xfers, 0, sizeof(st->scan_xfers)); + memset(st->scan_tx, 0, sizeof(st->scan_tx)); + + spi_message_init(&st->scan_msg); + + /* + * Each AVG_IN read needs two transfers: a 2-byte address write phase + * followed by a 2-byte data read phase. CS toggles between channels + * (cs_change=1 on the read phase of all but the last channel). + */ + k = 0; + iio_for_each_active_channel(indio_dev, i) { + put_unaligned_be16(0x8000 | AD4691_AVG_IN(i), &st->scan_tx[k]); + st->scan_xfers[2 * k].tx_buf = &st->scan_tx[k]; + st->scan_xfers[2 * k].len = sizeof(*st->scan_tx); + spi_message_add_tail(&st->scan_xfers[2 * k], &st->scan_msg); + st->scan_xfers[2 * k + 1].rx_buf = &st->vals[k]; + st->scan_xfers[2 * k + 1].len = sizeof(*st->scan_tx); + st->scan_xfers[2 * k + 1].cs_change = 1; + spi_message_add_tail(&st->scan_xfers[2 * k + 1], &st->scan_msg); + k++; + } + + /* + * Append a 4-byte state-reset transfer [addr_hi, addr_lo, + * STATE_RESET_ALL, OSC_EN=1]. CS is asserted throughout, so + * ADDR_DESCENDING writes byte[3]=1 to OSC_EN_REG (0x180) as a + * deliberate side-write, keeping the oscillator enabled. + * STATE_RESET_ALL starts the next burst; the hardware does not + * accumulate new conversions until after a STATE_RESET pulse, so + * no in-progress data is lost. No cs_change here — CS must + * deassert normally at end of message to frame the next command. + */ + put_unaligned_be16(AD4691_STATE_RESET_REG, st->scan_tx_reset); + st->scan_tx_reset[2] = AD4691_STATE_RESET_ALL; + st->scan_tx_reset[3] = 1; + st->scan_xfers[2 * k].tx_buf = st->scan_tx_reset; + st->scan_xfers[2 * k].len = sizeof(st->scan_tx_reset); + spi_message_add_tail(&st->scan_xfers[2 * k], &st->scan_msg); + + ret = spi_optimize_message(st->spi, &st->scan_msg); + if (ret) + return ret; + + std_seq_config = bitmap_read(indio_dev->active_scan_mask, 0, + iio_get_masklength(indio_dev)) & GENMASK(15, 0); + ret = regmap_write(st->regmap, AD4691_STD_SEQ_CONFIG, std_seq_config); + if (ret) + goto err_unoptimize; + + acc_mask = ~std_seq_config & GENMASK(15, 0); + ret = regmap_write(st->regmap, AD4691_ACC_MASK_REG, acc_mask); + if (ret) + goto err_unoptimize; + + ret = regmap_write(st->regmap, AD4691_ACC_DEPTH_IN(0), st->osr); + if (ret) + goto err_unoptimize; + + ret = ad4691_enter_conversion_mode(st); + if (ret) + goto err_unoptimize; + + return 0; + +err_unoptimize: + spi_unoptimize_message(&st->scan_msg); + return ret; +} + +static int ad4691_cnv_burst_buffer_postenable(struct iio_dev *indio_dev) +{ + struct ad4691_state *st = iio_priv(indio_dev); + int ret; + + /* + * Start the PWM and unmask the IRQ here in postenable, not in + * preenable. The IIO core attaches the trigger poll function between + * preenable and postenable; enabling sampling or unmasking the IRQ + * before that point risks a DATA_READY assertion landing before the + * poll function is registered. iio_trigger_poll() would drop the + * event, disable_irq_nosync() would fire, and enable_irq() would + * never be called, leaving the IRQ permanently masked. + */ + ret = ad4691_sampling_enable(st, true); + if (ret) + return ret; + + enable_irq(st->irq); + st->irq_enabled = true; + return 0; +} + +static int ad4691_cnv_burst_buffer_predisable(struct iio_dev *indio_dev) +{ + struct ad4691_state *st = iio_priv(indio_dev); + + if (st->irq_enabled) { + disable_irq(st->irq); + st->irq_enabled = false; + } + return ad4691_sampling_enable(st, false); +} + +static int ad4691_cnv_burst_buffer_postdisable(struct iio_dev *indio_dev) +{ + struct ad4691_state *st = iio_priv(indio_dev); + int ret; + + ret = ad4691_exit_conversion_mode(st); + spi_unoptimize_message(&st->scan_msg); + return ret; +} + +static const struct iio_buffer_setup_ops ad4691_cnv_burst_buffer_setup_ops = { + .preenable = ad4691_cnv_burst_buffer_preenable, + .postenable = ad4691_cnv_burst_buffer_postenable, + .predisable = ad4691_cnv_burst_buffer_predisable, + .postdisable = ad4691_cnv_burst_buffer_postdisable, +}; + +static int ad4691_manual_offload_buffer_postenable(struct iio_dev *indio_dev) +{ + struct ad4691_state *st = iio_priv(indio_dev); + struct device *dev = regmap_get_device(st->regmap); + struct spi_device *spi = to_spi_device(dev); + struct spi_offload_trigger_config config = { + .type = SPI_OFFLOAD_TRIGGER_PERIODIC, + }; + unsigned int bpw = indio_dev->channels[0].scan_type.realbits; + unsigned int bit, k; + int ret; + + ret = ad4691_enter_conversion_mode(st); + if (ret) + return ret; + + memset(st->scan_xfers, 0, sizeof(st->scan_xfers)); + memset(st->scan_tx, 0, sizeof(st->scan_tx)); + + /* + * N+1 transfers for N channels. Each CS-low period triggers + * a conversion AND returns the previous result (pipelined). + * TX: [AD4691_ADC_CHAN(n), 0x00] + * RX: [data_hi, data_lo] (storagebits=16, shift=0) + * Transfer 0 RX is garbage; transfers 1..N carry real data. + * scan_tx is reused for TX commands (mutually exclusive with the + * non-offload triggered-buffer path). + * + * bits_per_word=bpw: the SPI controller reads tx_buf as a native + * 16-bit word and shifts it out MSB-first. Store the exact 16-bit + * value we want on the wire as a plain native u16 — no endianness + * macro — so the wire bytes are correct on both LE and BE hosts. + * The channel-select command is a single byte; shift it to the MSB + * position so SPI sends it first, with a zero pad in the LSB. + */ + k = 0; + iio_for_each_active_channel(indio_dev, bit) { + st->scan_tx[k] = AD4691_ADC_CHAN(bit) << 8; + st->scan_xfers[k].tx_buf = &st->scan_tx[k]; + st->scan_xfers[k].len = sizeof(*st->scan_tx); + st->scan_xfers[k].bits_per_word = bpw; + st->scan_xfers[k].cs_change = 1; + st->scan_xfers[k].cs_change_delay.value = AD4691_CNV_HIGH_TIME_NS; + st->scan_xfers[k].cs_change_delay.unit = SPI_DELAY_UNIT_NSECS; + /* First transfer RX is garbage — skip it. */ + if (k > 0) + st->scan_xfers[k].offload_flags = SPI_OFFLOAD_XFER_RX_STREAM; + k++; + } + + /* Final NOOP transfer retrieves the last channel's result. */ + st->scan_xfers[k].tx_buf = &st->scan_tx[k]; /* scan_tx[k] == 0 == NOOP */ + st->scan_xfers[k].len = sizeof(*st->scan_tx); + st->scan_xfers[k].bits_per_word = bpw; + st->scan_xfers[k].offload_flags = SPI_OFFLOAD_XFER_RX_STREAM; + k++; + + spi_message_init_with_transfers(&st->scan_msg, st->scan_xfers, k); + st->scan_msg.offload = st->offload; + + ret = spi_optimize_message(spi, &st->scan_msg); + if (ret) + goto err_exit_conversion; + + config.periodic.frequency_hz = st->trigger_hz; + ret = spi_offload_trigger_enable(st->offload, st->offload_trigger, &config); + if (ret) + goto err_unoptimize; + + return 0; + +err_unoptimize: + spi_unoptimize_message(&st->scan_msg); +err_exit_conversion: + ad4691_exit_conversion_mode(st); + return ret; +} + +static int ad4691_manual_offload_buffer_predisable(struct iio_dev *indio_dev) +{ + struct ad4691_state *st = iio_priv(indio_dev); + + spi_offload_trigger_disable(st->offload, st->offload_trigger); + spi_unoptimize_message(&st->scan_msg); + + return ad4691_exit_conversion_mode(st); +} + +static const struct iio_buffer_setup_ops ad4691_manual_offload_buffer_setup_ops = { + .postenable = ad4691_manual_offload_buffer_postenable, + .predisable = ad4691_manual_offload_buffer_predisable, +}; + +static int ad4691_cnv_burst_offload_buffer_postenable(struct iio_dev *indio_dev) +{ + struct ad4691_state *st = iio_priv(indio_dev); + struct device *dev = regmap_get_device(st->regmap); + struct spi_device *spi = to_spi_device(dev); + struct spi_offload_trigger_config config = { + .type = SPI_OFFLOAD_TRIGGER_DATA_READY, + }; + unsigned int bpw = indio_dev->channels[0].scan_type.realbits; + unsigned int acc_mask, std_seq_config; + unsigned int bit, k; + int ret; + + std_seq_config = bitmap_read(indio_dev->active_scan_mask, 0, + iio_get_masklength(indio_dev)) & GENMASK(15, 0); + ret = regmap_write(st->regmap, AD4691_STD_SEQ_CONFIG, std_seq_config); + if (ret) + return ret; + + acc_mask = ~std_seq_config & GENMASK(15, 0); + ret = regmap_write(st->regmap, AD4691_ACC_MASK_REG, acc_mask); + if (ret) + return ret; + + ret = regmap_write(st->regmap, AD4691_ACC_DEPTH_IN(0), st->osr); + if (ret) + return ret; + + ret = ad4691_enter_conversion_mode(st); + if (ret) + return ret; + + memset(st->scan_xfers, 0, sizeof(st->scan_xfers)); + memset(st->scan_tx, 0, sizeof(st->scan_tx)); + + /* + * Each AVG_IN register read uses two transfers: + * TX: [reg_hi | 0x80, reg_lo] (address phase, CS stays asserted) + * RX: [data_hi, data_lo] (bpw-wide data phase, storagebits=16) + * Both TX and RX use bits_per_word=bpw: the SPI controller reads tx_buf + * as a native 16-bit word and shifts it out MSB-first. Store the exact + * 16-bit wire value as a plain native u16 — no endianness macro — so the + * wire bytes are correct on both LE and BE hosts. The read-address + * (0x8000 | reg) is already the 16-bit value we want on the wire. + * scan_tx is reused for TX addresses (mutually exclusive with the + * non-offload triggered-buffer path). + */ + k = 0; + iio_for_each_active_channel(indio_dev, bit) { + st->scan_tx[k] = 0x8000 | AD4691_AVG_IN(bit); + + /* TX: address phase, CS stays asserted into data phase */ + st->scan_xfers[2 * k].tx_buf = &st->scan_tx[k]; + st->scan_xfers[2 * k].len = sizeof(*st->scan_tx); + st->scan_xfers[2 * k].bits_per_word = bpw; + + /* RX: data phase, CS toggles after to delimit the next register op */ + st->scan_xfers[2 * k + 1].len = sizeof(*st->scan_tx); + st->scan_xfers[2 * k + 1].bits_per_word = bpw; + st->scan_xfers[2 * k + 1].offload_flags = SPI_OFFLOAD_XFER_RX_STREAM; + st->scan_xfers[2 * k + 1].cs_change = 1; + k++; + } + + /* + * State reset: single 4-byte write [addr_hi, addr_lo, STATE_RESET_ALL, + * OSC_EN=1]. ADDR_DESCENDING writes byte[3]=1 to OSC_EN_REG (0x180) as + * a deliberate side-write, keeping the oscillator enabled. + * scan_tx_reset is shared with the non-offload path (len=4 here vs + * len=3 there) since the two paths are mutually exclusive at probe. + */ + put_unaligned_be16(AD4691_STATE_RESET_REG, st->scan_tx_reset); + st->scan_tx_reset[2] = AD4691_STATE_RESET_ALL; + st->scan_tx_reset[3] = 1; + st->scan_xfers[2 * k].tx_buf = st->scan_tx_reset; + st->scan_xfers[2 * k].len = sizeof(st->scan_tx_reset); + /* + * 4-byte u8 buffer assembled with put_unaligned_be16(); leave + * bits_per_word at the default (8) so bytes go out in memory order. + */ + + spi_message_init_with_transfers(&st->scan_msg, st->scan_xfers, 2 * k + 1); + st->scan_msg.offload = st->offload; + + ret = spi_optimize_message(spi, &st->scan_msg); + if (ret) + goto err_exit_conversion; + + ret = spi_offload_trigger_enable(st->offload, st->offload_trigger, &config); + if (ret) + goto err_unoptimize; + + ret = ad4691_sampling_enable(st, true); + if (ret) + goto err_disable_trigger; + + return 0; + +err_disable_trigger: + spi_offload_trigger_disable(st->offload, st->offload_trigger); +err_unoptimize: + spi_unoptimize_message(&st->scan_msg); +err_exit_conversion: + ad4691_exit_conversion_mode(st); + return ret; +} + +static int ad4691_cnv_burst_offload_buffer_predisable(struct iio_dev *indio_dev) +{ + struct ad4691_state *st = iio_priv(indio_dev); + + ad4691_sampling_enable(st, false); + spi_offload_trigger_disable(st->offload, st->offload_trigger); + spi_unoptimize_message(&st->scan_msg); + + return ad4691_exit_conversion_mode(st); +} + +static const struct iio_buffer_setup_ops ad4691_cnv_burst_offload_buffer_setup_ops = { + .postenable = ad4691_cnv_burst_offload_buffer_postenable, + .predisable = ad4691_cnv_burst_offload_buffer_predisable, +}; + +static ssize_t sampling_frequency_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct iio_dev *indio_dev = dev_to_iio_dev(dev); + struct ad4691_state *st = iio_priv(indio_dev); + + if (st->manual_mode && st->offload) + return sysfs_emit(buf, "%llu\n", READ_ONCE(st->trigger_hz)); + + return sysfs_emit(buf, "%lu\n", NSEC_PER_SEC / st->cnv_period_ns); +} + +static ssize_t sampling_frequency_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t len) +{ + struct iio_dev *indio_dev = dev_to_iio_dev(dev); + struct ad4691_state *st = iio_priv(indio_dev); + unsigned int freq; + int ret; + + ret = kstrtouint(buf, 10, &freq); + if (ret) + return ret; + + IIO_DEV_ACQUIRE_DIRECT_MODE(indio_dev, claim); + if (IIO_DEV_ACQUIRE_FAILED(claim)) + return -EBUSY; + + if (st->manual_mode && st->offload) { + struct spi_offload_trigger_config config = { + .type = SPI_OFFLOAD_TRIGGER_PERIODIC, + .periodic = { .frequency_hz = freq }, + }; + + ret = spi_offload_trigger_validate(st->offload_trigger, &config); + if (ret) + return ret; + + WRITE_ONCE(st->trigger_hz, config.periodic.frequency_hz); + return len; + } + + ret = ad4691_set_pwm_freq(st, freq); + if (ret) + return ret; + + return len; +} + +static IIO_DEVICE_ATTR_RW(sampling_frequency, 0); + +static const struct iio_dev_attr *ad4691_buffer_attrs[] = { + &iio_dev_attr_sampling_frequency, + NULL +}; + +static irqreturn_t ad4691_irq(int irq, void *private) +{ + struct iio_dev *indio_dev = private; + struct ad4691_state *st = iio_priv(indio_dev); + + /* + * Disable the IRQ before calling iio_trigger_poll(). The IRQ is + * re-enabled via the trigger .reenable callback, which the IIO core + * calls inside iio_trigger_notify_done() once use_count reaches zero. + * Re-enabling here (before notify_done) would race: a DATA_READY + * between enable_irq() and notify_done() calls iio_trigger_poll() + * while use_count > 0, dropping the event and permanently masking + * the IRQ. + */ + disable_irq_nosync(st->irq); + iio_trigger_poll(indio_dev->trig); + + return IRQ_HANDLED; +} + +static void ad4691_trigger_reenable(struct iio_trigger *trig) +{ + struct ad4691_state *st = iio_trigger_get_drvdata(trig); + + enable_irq(st->irq); +} + +static const struct iio_trigger_ops ad4691_trigger_ops = { + .reenable = ad4691_trigger_reenable, + .validate_device = iio_trigger_validate_own_device, +}; + +static void ad4691_read_scan(struct iio_dev *indio_dev, s64 ts) +{ + struct ad4691_state *st = iio_priv(indio_dev); + int ret; + + guard(mutex)(&st->lock); + + ret = spi_sync(st->spi, &st->scan_msg); + if (ret) { + dev_err_ratelimited(regmap_get_device(st->regmap), + "SPI scan failed: %d\n", ret); + return; + } + + /* + * rx_buf pointers in scan_xfers point directly into scan.vals, so no + * copy is needed. The scan_msg already includes a STATE_RESET at the + * end (appended in preenable), so no explicit reset is needed here. + */ + iio_push_to_buffers_with_ts(indio_dev, st->vals, sizeof(st->vals), ts); +} + +static irqreturn_t ad4691_trigger_handler(int irq, void *p) +{ + struct iio_poll_func *pf = p; + struct iio_dev *indio_dev = pf->indio_dev; + + ad4691_read_scan(indio_dev, pf->timestamp); + iio_trigger_notify_done(indio_dev->trig); + return IRQ_HANDLED; +} + +/* + * CNV burst mode: only allow our own trigger (driven by DATA_READY IRQ). + * Manual mode: external triggers (e.g. iio-trig-hrtimer) must be allowed + * because manual mode has no DATA_READY IRQ to fire the internal trigger. + * iio_trigger_ops.validate_device = iio_trigger_validate_own_device is + * correct in both modes — it prevents other devices from hijacking our + * internal trigger; the distinction here is only for iio_info.validate_trigger. + */ +static const struct iio_info ad4691_cnv_burst_info = { + .read_raw = ad4691_read_raw, + .write_raw = ad4691_write_raw, + .read_avail = ad4691_read_avail, + .debugfs_reg_access = ad4691_reg_access, + .validate_trigger = iio_validate_own_trigger, +}; + +static const struct iio_info ad4691_manual_info = { + .read_raw = ad4691_read_raw, + .write_raw = ad4691_write_raw, + .read_avail = ad4691_read_avail, + .debugfs_reg_access = ad4691_reg_access, +}; + +static int ad4691_pwm_setup(struct ad4691_state *st) +{ + struct device *dev = regmap_get_device(st->regmap); + + st->conv_trigger = devm_pwm_get(dev, "cnv"); + if (IS_ERR(st->conv_trigger)) + return dev_err_probe(dev, PTR_ERR(st->conv_trigger), + "Failed to get CNV PWM\n"); + + return ad4691_set_pwm_freq(st, st->info->max_rate); +} + +static int ad4691_regulator_setup(struct ad4691_state *st) +{ + struct device *dev = regmap_get_device(st->regmap); + int ret; + + ret = devm_regulator_bulk_get_enable(dev, ARRAY_SIZE(ad4691_supplies), + ad4691_supplies); + if (ret) + return dev_err_probe(dev, ret, "Failed to get and enable supplies\n"); + + /* + * vdd-supply and ldo-in-supply are mutually exclusive: + * vdd-supply present → external 1.8V VDD; disable internal LDO. + * vdd-supply absent → enable internal LDO fed from ldo-in-supply. + * Having both simultaneously is strongly inadvisable per the datasheet. + */ + if (device_property_present(dev, "vdd-supply")) { + ret = devm_regulator_get_enable(dev, "vdd"); + if (ret) + return dev_err_probe(dev, ret, + "Failed to get and enable VDD\n"); + } else if (device_property_present(dev, "ldo-in-supply")) { + ret = devm_regulator_get_enable(dev, "ldo-in"); + if (ret) + return dev_err_probe(dev, ret, + "Failed to get and enable LDO-IN\n"); + st->ldo_en = true; + } else { + return dev_err_probe(dev, -EINVAL, + "missing one of vdd-supply, ldo-in-supply\n"); + } + + if (device_property_present(dev, "ref-supply")) { + st->vref_uV = devm_regulator_get_enable_read_voltage(dev, "ref"); + if (st->vref_uV < 0) + return dev_err_probe(dev, st->vref_uV, + "Failed to get REF supply voltage\n"); + } else if (device_property_present(dev, "refin-supply")) { + st->vref_uV = devm_regulator_get_enable_read_voltage(dev, "refin"); + if (st->vref_uV < 0) + return dev_err_probe(dev, st->vref_uV, + "Failed to get REFIN supply voltage\n"); + st->refbuf_en = true; + } else { + return dev_err_probe(dev, -EINVAL, + "missing one of ref-supply, refin-supply\n"); + } + + if (st->vref_uV < AD4691_VREF_uV_MIN || st->vref_uV > AD4691_VREF_uV_MAX) + return dev_err_probe(dev, -EINVAL, + "vref(%d) must be in the range [%u...%u]\n", + st->vref_uV, AD4691_VREF_uV_MIN, + AD4691_VREF_uV_MAX); + + return 0; +} + +static int ad4691_reset(struct ad4691_state *st) +{ + struct device *dev = regmap_get_device(st->regmap); + struct reset_control *rst; + int ret; + + rst = devm_reset_control_get_optional_exclusive(dev, NULL); + if (IS_ERR(rst)) + return dev_err_probe(dev, PTR_ERR(rst), "Failed to get reset\n"); + + if (rst) { + /* + * Assert the reset line to guarantee a clean reset pulse on + * every probe, including driver reloads where the line may + * already be deasserted (reset_control_put() does not + * re-assert on release). tRESETL (minimum pulse width) = 10 ns + * (Table 5); kernel function-call overhead alone exceeds this, + * so no explicit delay is needed between assert and deassert. + */ + reset_control_assert(rst); + ret = reset_control_deassert(rst); + if (ret) + return ret; + } else { + /* No hardware reset available, fall back to software reset. */ + ret = regmap_write(st->regmap, AD4691_SPI_CONFIG_A_REG, + AD4691_SW_RESET); + if (ret) + return ret; + } + + /* + * Wait 300 µs (Table 5) for the device to complete its internal reset + * sequence before accepting SPI commands. + */ + fsleep(300); + return 0; +} + +static int ad4691_config(struct ad4691_state *st) +{ + struct device *dev = regmap_get_device(st->regmap); + enum ad4691_ref_ctrl ref_val; + unsigned int val; + int ret; + + /* + * Determine buffer conversion mode from DT: if a PWM is provided it + * drives the CNV pin (CNV_BURST_MODE); otherwise CNV is tied to CS + * and each SPI transfer triggers a conversion (MANUAL_MODE). + * Both modes idle in AUTONOMOUS mode so that read_raw can use the + * internal oscillator without disturbing the hardware configuration. + */ + if (device_property_present(dev, "pwms")) { + st->manual_mode = false; + ret = ad4691_pwm_setup(st); + if (ret) + return ret; + } else { + st->manual_mode = true; + } + + switch (st->vref_uV) { + case AD4691_VREF_uV_MIN ... AD4691_VREF_2P5_uV_MAX: + ref_val = AD4691_VREF_2P5; + break; + case AD4691_VREF_2P5_uV_MAX + 1 ... AD4691_VREF_3P0_uV_MAX: + ref_val = AD4691_VREF_3P0; + break; + case AD4691_VREF_3P0_uV_MAX + 1 ... AD4691_VREF_3P3_uV_MAX: + ref_val = AD4691_VREF_3P3; + break; + case AD4691_VREF_3P3_uV_MAX + 1 ... AD4691_VREF_4P096_uV_MAX: + ref_val = AD4691_VREF_4P096; + break; + case AD4691_VREF_4P096_uV_MAX + 1 ... AD4691_VREF_uV_MAX: + ref_val = AD4691_VREF_5P0; + break; + default: + return dev_err_probe(dev, -EINVAL, + "Unsupported vref voltage: %d uV\n", + st->vref_uV); + } + + val = FIELD_PREP(AD4691_REF_CTRL_MASK, ref_val); + if (st->refbuf_en) + val |= AD4691_REFBUF_EN; + + ret = regmap_write(st->regmap, AD4691_REF_CTRL, val); + if (ret) + return dev_err_probe(dev, ret, "Failed to write REF_CTRL\n"); + + ret = regmap_assign_bits(st->regmap, AD4691_DEVICE_SETUP, + AD4691_LDO_EN, st->ldo_en); + if (ret) + return dev_err_probe(dev, ret, "Failed to write DEVICE_SETUP\n"); + + /* + * Set the internal oscillator to the highest rate this chip supports. + * Index 0 (1 MHz) exceeds the 500 kHz max of AD4691/AD4693, so those + * chips start at index 1 (500 kHz). + */ + ret = regmap_write(st->regmap, AD4691_OSC_FREQ_REG, + ad4691_samp_freq_start(st->info)); + if (ret) + return dev_err_probe(dev, ret, "Failed to write OSC_FREQ\n"); + + st->target_osc_freq_Hz = ad4691_osc_freqs_Hz[ad4691_samp_freq_start(st->info)]; + + ret = regmap_update_bits(st->regmap, AD4691_ADC_SETUP, + AD4691_ADC_MODE_MASK, AD4691_AUTONOMOUS_MODE); + if (ret) + return dev_err_probe(dev, ret, "Failed to write ADC_SETUP\n"); + + ad4691_precompute_samp_freq_avail(st); + + return 0; +} + +static int ad4691_setup_triggered_buffer(struct iio_dev *indio_dev, + struct ad4691_state *st) +{ + struct device *dev = regmap_get_device(st->regmap); + struct iio_trigger *trig; + unsigned int i; + int irq, ret; + + /* + * Manual mode exposes channels without the oversampling_ratio attribute + * because ACC_DEPTH_IN is not configured in manual mode. + */ + if (st->manual_mode) + indio_dev->channels = st->info->sw_info->manual_channels; + else + indio_dev->channels = st->info->sw_info->channels; + indio_dev->num_channels = st->info->sw_info->num_channels; + indio_dev->info = st->manual_mode ? &ad4691_manual_info : &ad4691_cnv_burst_info; + + /* + * Manual mode relies on an external trigger (e.g. iio-trig-hrtimer); + * no internal trigger is needed or registered. + */ + if (st->manual_mode) + return devm_iio_triggered_buffer_setup(dev, indio_dev, + iio_pollfunc_store_time, + ad4691_trigger_handler, + &ad4691_manual_buffer_setup_ops); + + /* + * CNV burst mode: allocate an internal trigger driven by the + * DATA_READY IRQ on the GP pin. + */ + trig = devm_iio_trigger_alloc(dev, "%s-dev%d", indio_dev->name, + iio_device_id(indio_dev)); + if (!trig) + return -ENOMEM; + + trig->ops = &ad4691_trigger_ops; + iio_trigger_set_drvdata(trig, st); + + ret = devm_iio_trigger_register(dev, trig); + if (ret) + return dev_err_probe(dev, ret, "IIO trigger register failed\n"); + + indio_dev->trig = iio_trigger_get(trig); + + /* + * The GP pin named in interrupt-names asserts at end-of-conversion. + * The IRQ handler fires the IIO trigger so the trigger handler can + * read and push the sample to the buffer. The IRQ is kept disabled + * until the buffer is enabled. + */ + irq = -ENXIO; + for (i = 0; i < ARRAY_SIZE(ad4691_gp_names); i++) { + irq = fwnode_irq_get_byname(dev_fwnode(dev), + ad4691_gp_names[i]); + if (irq > 0 || irq == -EPROBE_DEFER) + break; + } + if (irq < 0) + return dev_err_probe(dev, irq, "failed to get GP interrupt\n"); + + st->irq = irq; + + ret = ad4691_gpio_setup(st, i); + if (ret) + return ret; + + /* + * The handler only calls disable_irq_nosync() and iio_trigger_poll(), + * both safe in hardirq context, so register as a hard IRQ handler. + * IRQF_NO_AUTOEN keeps it disabled until the buffer is enabled. + */ + ret = devm_request_irq(dev, irq, ad4691_irq, IRQF_NO_AUTOEN, + indio_dev->name, indio_dev); + if (ret) + return ret; + + return devm_iio_triggered_buffer_setup_ext(dev, indio_dev, + iio_pollfunc_store_time, + ad4691_trigger_handler, + IIO_BUFFER_DIRECTION_IN, + &ad4691_cnv_burst_buffer_setup_ops, + ad4691_buffer_attrs); +} + +static int ad4691_setup_offload(struct iio_dev *indio_dev, + struct ad4691_state *st, + struct spi_offload *spi_offload) +{ + struct device *dev = regmap_get_device(st->regmap); + struct dma_chan *rx_dma; + int ret; + + st->offload = spi_offload; + + /* + * CNV burst offload exposes oversampling_ratio (ACC_DEPTH_IN is + * configured per channel at buffer enable). Manual offload does not + * configure ACC_DEPTH_IN, so it uses a separate channel array + * without the oversampling_ratio attribute. Both paths use IIO_CPU + * (no .endianness annotation) because bits_per_word=16 causes the + * SPI Engine to produce native 16-bit DMA words. + */ + if (st->manual_mode) + indio_dev->channels = st->info->offload_info->manual_channels; + else + indio_dev->channels = st->info->offload_info->channels; + indio_dev->num_channels = st->info->offload_info->num_channels; + /* + * Offload path uses DMA directly; no IIO trigger is involved, so + * external triggers are not restricted (no validate_trigger). + */ + indio_dev->info = &ad4691_manual_info; + + if (st->manual_mode) { + st->offload_trigger = + devm_spi_offload_trigger_get(dev, st->offload, + SPI_OFFLOAD_TRIGGER_PERIODIC); + if (IS_ERR(st->offload_trigger)) + return dev_err_probe(dev, PTR_ERR(st->offload_trigger), + "Failed to get periodic offload trigger\n"); + + st->trigger_hz = AD4691_OFFLOAD_INITIAL_TRIGGER_HZ; + } else { + struct spi_offload_trigger_info trigger_info = { + .fwnode = dev_fwnode(dev), + .ops = &ad4691_offload_trigger_ops, + .priv = st, + }; + + ret = devm_spi_offload_trigger_register(dev, &trigger_info); + if (ret) + return dev_err_probe(dev, ret, + "Failed to register offload trigger\n"); + + st->offload_trigger = + devm_spi_offload_trigger_get(dev, st->offload, + SPI_OFFLOAD_TRIGGER_DATA_READY); + if (IS_ERR(st->offload_trigger)) + return dev_err_probe(dev, PTR_ERR(st->offload_trigger), + "Failed to get DATA_READY offload trigger\n"); + } + + rx_dma = devm_spi_offload_rx_stream_request_dma_chan(dev, st->offload); + if (IS_ERR(rx_dma)) + return dev_err_probe(dev, PTR_ERR(rx_dma), + "Failed to get offload RX DMA channel\n"); + + if (st->manual_mode) + indio_dev->setup_ops = &ad4691_manual_offload_buffer_setup_ops; + else + indio_dev->setup_ops = &ad4691_cnv_burst_offload_buffer_setup_ops; + + ret = devm_iio_dmaengine_buffer_setup_with_handle(dev, indio_dev, rx_dma, + IIO_BUFFER_DIRECTION_IN); + if (ret) + return ret; + + indio_dev->buffer->attrs = ad4691_buffer_attrs; + + return 0; +} + +static int ad4691_probe(struct spi_device *spi) +{ + struct device *dev = &spi->dev; + struct spi_offload *spi_offload; + struct iio_dev *indio_dev; + struct ad4691_state *st; + int ret; + + indio_dev = devm_iio_device_alloc(dev, sizeof(*st)); + if (!indio_dev) + return -ENOMEM; + + st = iio_priv(indio_dev); + st->spi = spi; + st->info = spi_get_device_match_data(spi); + if (!st->info) + return -ENODEV; + st->osr = 1; + + ret = devm_mutex_init(dev, &st->lock); + if (ret) + return ret; + + st->regmap = devm_regmap_init(dev, NULL, spi, &ad4691_regmap_config); + if (IS_ERR(st->regmap)) + return dev_err_probe(dev, PTR_ERR(st->regmap), + "Failed to initialize regmap\n"); + + ret = ad4691_regulator_setup(st); + if (ret) + return ret; + + ret = ad4691_reset(st); + if (ret) + return ret; + + ret = ad4691_config(st); + if (ret) + return ret; + + spi_offload = devm_spi_offload_get(dev, spi, &ad4691_offload_config); + ret = PTR_ERR_OR_ZERO(spi_offload); + if (ret == -ENODEV) + spi_offload = NULL; + else if (ret) + return dev_err_probe(dev, ret, "Failed to get SPI offload\n"); + + indio_dev->name = st->info->name; + indio_dev->modes = INDIO_DIRECT_MODE; + + if (spi_offload) + ret = ad4691_setup_offload(indio_dev, st, spi_offload); + else + ret = ad4691_setup_triggered_buffer(indio_dev, st); + if (ret) + return ret; + + return devm_iio_device_register(dev, indio_dev); +} + +static const struct of_device_id ad4691_of_match[] = { + { .compatible = "adi,ad4691", .data = &ad4691_chip_info }, + { .compatible = "adi,ad4692", .data = &ad4692_chip_info }, + { .compatible = "adi,ad4693", .data = &ad4693_chip_info }, + { .compatible = "adi,ad4694", .data = &ad4694_chip_info }, + { } +}; +MODULE_DEVICE_TABLE(of, ad4691_of_match); + +static const struct spi_device_id ad4691_id[] = { + { .name = "ad4691", .driver_data = (kernel_ulong_t)&ad4691_chip_info }, + { .name = "ad4692", .driver_data = (kernel_ulong_t)&ad4692_chip_info }, + { .name = "ad4693", .driver_data = (kernel_ulong_t)&ad4693_chip_info }, + { .name = "ad4694", .driver_data = (kernel_ulong_t)&ad4694_chip_info }, + { } +}; +MODULE_DEVICE_TABLE(spi, ad4691_id); + +static struct spi_driver ad4691_driver = { + .driver = { + .name = "ad4691", + .of_match_table = ad4691_of_match, + }, + .probe = ad4691_probe, + .id_table = ad4691_id, +}; +module_spi_driver(ad4691_driver); + +MODULE_AUTHOR("Radu Sabau "); +MODULE_DESCRIPTION("Analog Devices AD4691 Family ADC Driver"); +MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS("IIO_DMA_BUFFER"); +MODULE_IMPORT_NS("IIO_DMAENGINE_BUFFER"); diff --git a/drivers/iio/adc/ad4695.c b/drivers/iio/adc/ad4695.c new file mode 100644 index 000000000..53642de73 --- /dev/null +++ b/drivers/iio/adc/ad4695.c @@ -0,0 +1,2019 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * SPI ADC driver for Analog Devices Inc. AD4695 and similar chips + * + * https://www.analog.com/en/products/ad4695.html + * https://www.analog.com/en/products/ad4696.html + * https://www.analog.com/en/products/ad4697.html + * https://www.analog.com/en/products/ad4698.html + * + * Copyright 2024 Analog Devices Inc. + * Copyright 2024 BayLibre, SAS + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +/* AD4695 registers */ +#define AD4695_REG_SPI_CONFIG_A 0x0000 +#define AD4695_REG_SPI_CONFIG_A_SW_RST (BIT(7) | BIT(0)) +#define AD4695_REG_SPI_CONFIG_A_ADDR_DIR BIT(5) +#define AD4695_REG_SPI_CONFIG_B 0x0001 +#define AD4695_REG_SPI_CONFIG_B_INST_MODE BIT(7) +#define AD4695_REG_DEVICE_TYPE 0x0003 +#define AD4695_REG_SCRATCH_PAD 0x000A +#define AD4695_REG_VENDOR_L 0x000C +#define AD4695_REG_VENDOR_H 0x000D +#define AD4695_REG_LOOP_MODE 0x000E +#define AD4695_REG_SPI_CONFIG_C 0x0010 +#define AD4695_REG_SPI_CONFIG_C_MB_STRICT BIT(7) +#define AD4695_REG_SPI_STATUS 0x0011 +#define AD4695_REG_STATUS 0x0014 +#define AD4695_REG_ALERT_STATUS1 0x0015 +#define AD4695_REG_ALERT_STATUS2 0x0016 +#define AD4695_REG_CLAMP_STATUS 0x001A +#define AD4695_REG_SETUP 0x0020 +#define AD4695_REG_SETUP_LDO_EN BIT(4) +#define AD4695_REG_SETUP_SPI_MODE BIT(2) +#define AD4695_REG_SETUP_SPI_CYC_CTRL BIT(1) +#define AD4695_REG_REF_CTRL 0x0021 +#define AD4695_REG_REF_CTRL_OV_MODE BIT(7) +#define AD4695_REG_REF_CTRL_VREF_SET GENMASK(4, 2) +#define AD4695_REG_REF_CTRL_REFHIZ_EN BIT(1) +#define AD4695_REG_REF_CTRL_REFBUF_EN BIT(0) +#define AD4695_REG_SEQ_CTRL 0x0022 +#define AD4695_REG_SEQ_CTRL_STD_SEQ_EN BIT(7) +#define AD4695_REG_SEQ_CTRL_NUM_SLOTS_AS GENMASK(6, 0) +#define AD4695_REG_AC_CTRL 0x0023 +#define AD4695_REG_STD_SEQ_CONFIG 0x0024 +#define AD4695_REG_GPIO_CTRL 0x0026 +#define AD4695_REG_GP_MODE 0x0027 +#define AD4695_REG_GP_MODE_BUSY_GP_SEL BIT(5) +#define AD4695_REG_GP_MODE_BUSY_GP_EN BIT(1) +#define AD4695_REG_TEMP_CTRL 0x0029 +#define AD4695_REG_TEMP_CTRL_TEMP_EN BIT(0) +#define AD4695_REG_CONFIG_IN(n) (0x0030 | (n)) +#define AD4695_REG_CONFIG_IN_MODE BIT(6) +#define AD4695_REG_CONFIG_IN_PAIR GENMASK(5, 4) +#define AD4695_REG_CONFIG_IN_AINHIGHZ_EN BIT(3) +#define AD4695_REG_CONFIG_IN_OSR_SET GENMASK(1, 0) +#define AD4695_REG_UPPER_IN(n) (0x0040 | (2 * (n))) +#define AD4695_REG_LOWER_IN(n) (0x0060 | (2 * (n))) +#define AD4695_REG_HYST_IN(n) (0x0080 | (2 * (n))) +#define AD4695_REG_OFFSET_IN(n) (0x00A0 | (2 * (n))) +#define AD4695_REG_GAIN_IN(n) (0x00C0 | (2 * (n))) +#define AD4695_REG_AS_SLOT(n) (0x0100 | (n)) +#define AD4695_REG_AS_SLOT_INX GENMASK(3, 0) + +/* Conversion mode commands */ +#define AD4695_CMD_EXIT_CNV_MODE 0x0A +#define AD4695_CMD_TEMP_CHAN 0x0F +#define AD4695_CMD_VOLTAGE_CHAN(n) (0x10 | (n)) + +/* timing specs */ +#define AD4695_T_CONVERT_NS 415 +#define AD4695_T_WAKEUP_HW_MS 3 +#define AD4695_T_WAKEUP_SW_MS 3 +#define AD4695_T_REFBUF_MS 100 +#define AD4695_T_REGCONFIG_NS 20 +#define AD4695_T_SCK_CNV_DELAY_NS 80 +#define AD4695_T_CNVL_NS 80 +#define AD4695_T_CNVH_NS 10 +#define AD4695_REG_ACCESS_SCLK_HZ (10 * MEGA) + +/* Max number of voltage input channels. */ +#define AD4695_MAX_VIN_CHANNELS 16 + +enum ad4695_in_pair { + AD4695_IN_PAIR_REFGND, + AD4695_IN_PAIR_COM, + AD4695_IN_PAIR_EVEN_ODD, +}; + +struct ad4695_chip_info { + const char *name; + int max_sample_rate; + u32 t_acq_ns; + u8 num_voltage_inputs; +}; + +struct ad4695_channel_config { + unsigned int channel; + bool highz_en; + bool bipolar; + enum ad4695_in_pair pin_pairing; + unsigned int common_mode_mv; + unsigned int oversampling_ratio; +}; + +struct ad4695_state { + struct spi_device *spi; + struct spi_offload *offload; + struct spi_offload_trigger *offload_trigger; + struct regmap *regmap; + struct regmap *regmap16; + struct gpio_desc *reset_gpio; + /* currently PWM CNV only supported with SPI offload use */ + struct pwm_device *cnv_pwm; + /* protects against concurrent use of cnv_pwm */ + struct mutex cnv_pwm_lock; + /* offload also requires separate gpio to manually control CNV */ + struct gpio_desc *cnv_gpio; + /* voltages channels plus temperature and timestamp */ + struct iio_chan_spec iio_chan[AD4695_MAX_VIN_CHANNELS + 2]; + struct ad4695_channel_config channels_cfg[AD4695_MAX_VIN_CHANNELS]; + const struct ad4695_chip_info *chip_info; + int sample_freq_range[3]; + /* Reference voltage. */ + unsigned int vref_mv; + /* Common mode input pin voltage. */ + unsigned int com_mv; + /* + * 2 per voltage and temperature chan plus 1 xfer to trigger 1st + * CNV. Excluding the trigger xfer, every 2nd xfer only serves + * to control CS and add a delay between the last SCLK and next + * CNV rising edges. + */ + struct spi_transfer buf_read_xfer[AD4695_MAX_VIN_CHANNELS * 2 + 3]; + struct spi_message buf_read_msg; + /* Raw conversion data received. */ + IIO_DECLARE_DMA_BUFFER_WITH_TS(u16, buf, AD4695_MAX_VIN_CHANNELS + 1); + u16 raw_data; + /* Commands to send for single conversion. */ + u16 cnv_cmd; + u8 cnv_cmd2; + /* Buffer for storing data from regmap bus reads/writes */ + u8 regmap_bus_data[4]; +}; + +static const struct regmap_range ad4695_regmap_rd_ranges[] = { + regmap_reg_range(AD4695_REG_SPI_CONFIG_A, AD4695_REG_SPI_CONFIG_B), + regmap_reg_range(AD4695_REG_DEVICE_TYPE, AD4695_REG_DEVICE_TYPE), + regmap_reg_range(AD4695_REG_SCRATCH_PAD, AD4695_REG_SCRATCH_PAD), + regmap_reg_range(AD4695_REG_VENDOR_L, AD4695_REG_LOOP_MODE), + regmap_reg_range(AD4695_REG_SPI_CONFIG_C, AD4695_REG_SPI_STATUS), + regmap_reg_range(AD4695_REG_STATUS, AD4695_REG_ALERT_STATUS2), + regmap_reg_range(AD4695_REG_CLAMP_STATUS, AD4695_REG_CLAMP_STATUS), + regmap_reg_range(AD4695_REG_SETUP, AD4695_REG_AC_CTRL), + regmap_reg_range(AD4695_REG_GPIO_CTRL, AD4695_REG_TEMP_CTRL), + regmap_reg_range(AD4695_REG_CONFIG_IN(0), AD4695_REG_CONFIG_IN(15)), + regmap_reg_range(AD4695_REG_AS_SLOT(0), AD4695_REG_AS_SLOT(127)), +}; + +static const struct regmap_access_table ad4695_regmap_rd_table = { + .yes_ranges = ad4695_regmap_rd_ranges, + .n_yes_ranges = ARRAY_SIZE(ad4695_regmap_rd_ranges), +}; + +static const struct regmap_range ad4695_regmap_wr_ranges[] = { + regmap_reg_range(AD4695_REG_SPI_CONFIG_A, AD4695_REG_SPI_CONFIG_B), + regmap_reg_range(AD4695_REG_SCRATCH_PAD, AD4695_REG_SCRATCH_PAD), + regmap_reg_range(AD4695_REG_LOOP_MODE, AD4695_REG_LOOP_MODE), + regmap_reg_range(AD4695_REG_SPI_CONFIG_C, AD4695_REG_SPI_STATUS), + regmap_reg_range(AD4695_REG_SETUP, AD4695_REG_AC_CTRL), + regmap_reg_range(AD4695_REG_GPIO_CTRL, AD4695_REG_TEMP_CTRL), + regmap_reg_range(AD4695_REG_CONFIG_IN(0), AD4695_REG_CONFIG_IN(15)), + regmap_reg_range(AD4695_REG_AS_SLOT(0), AD4695_REG_AS_SLOT(127)), +}; + +static const struct regmap_access_table ad4695_regmap_wr_table = { + .yes_ranges = ad4695_regmap_wr_ranges, + .n_yes_ranges = ARRAY_SIZE(ad4695_regmap_wr_ranges), +}; + +static const struct regmap_config ad4695_regmap_config = { + .name = "ad4695-8", + .reg_bits = 16, + .val_bits = 8, + .max_register = AD4695_REG_AS_SLOT(127), + .rd_table = &ad4695_regmap_rd_table, + .wr_table = &ad4695_regmap_wr_table, +}; + +static const struct regmap_range ad4695_regmap16_rd_ranges[] = { + regmap_reg_range(AD4695_REG_STD_SEQ_CONFIG, AD4695_REG_STD_SEQ_CONFIG), + regmap_reg_range(AD4695_REG_UPPER_IN(0), AD4695_REG_GAIN_IN(15)), +}; + +static const struct regmap_access_table ad4695_regmap16_rd_table = { + .yes_ranges = ad4695_regmap16_rd_ranges, + .n_yes_ranges = ARRAY_SIZE(ad4695_regmap16_rd_ranges), +}; + +static const struct regmap_range ad4695_regmap16_wr_ranges[] = { + regmap_reg_range(AD4695_REG_STD_SEQ_CONFIG, AD4695_REG_STD_SEQ_CONFIG), + regmap_reg_range(AD4695_REG_UPPER_IN(0), AD4695_REG_GAIN_IN(15)), +}; + +static const struct regmap_access_table ad4695_regmap16_wr_table = { + .yes_ranges = ad4695_regmap16_wr_ranges, + .n_yes_ranges = ARRAY_SIZE(ad4695_regmap16_wr_ranges), +}; + +static const struct regmap_config ad4695_regmap16_config = { + .name = "ad4695-16", + .reg_bits = 16, + .reg_stride = 2, + .val_bits = 16, + .val_format_endian = REGMAP_ENDIAN_LITTLE, + .max_register = AD4695_REG_GAIN_IN(15), + .rd_table = &ad4695_regmap16_rd_table, + .wr_table = &ad4695_regmap16_wr_table, +}; + +static int ad4695_regmap_bus_reg_write(void *context, const void *data, + size_t count) +{ + struct ad4695_state *st = context; + struct spi_transfer xfer = { + .speed_hz = AD4695_REG_ACCESS_SCLK_HZ, + .len = count, + .tx_buf = st->regmap_bus_data, + }; + + if (count > ARRAY_SIZE(st->regmap_bus_data)) + return -EINVAL; + + memcpy(st->regmap_bus_data, data, count); + + return spi_sync_transfer(st->spi, &xfer, 1); +} + +static int ad4695_regmap_bus_reg_read(void *context, const void *reg, + size_t reg_size, void *val, + size_t val_size) +{ + struct ad4695_state *st = context; + struct spi_transfer xfers[] = { + { + .speed_hz = AD4695_REG_ACCESS_SCLK_HZ, + .len = reg_size, + .tx_buf = &st->regmap_bus_data[0], + }, { + .speed_hz = AD4695_REG_ACCESS_SCLK_HZ, + .len = val_size, + .rx_buf = &st->regmap_bus_data[2], + }, + }; + int ret; + + if (reg_size > 2) + return -EINVAL; + + if (val_size > 2) + return -EINVAL; + + memcpy(&st->regmap_bus_data[0], reg, reg_size); + + ret = spi_sync_transfer(st->spi, xfers, ARRAY_SIZE(xfers)); + if (ret) + return ret; + + memcpy(val, &st->regmap_bus_data[2], val_size); + + return 0; +} + +static const struct regmap_bus ad4695_regmap_bus = { + .write = ad4695_regmap_bus_reg_write, + .read = ad4695_regmap_bus_reg_read, + .read_flag_mask = 0x80, + .reg_format_endian_default = REGMAP_ENDIAN_BIG, + .val_format_endian_default = REGMAP_ENDIAN_BIG, +}; + +enum { + AD4695_SCAN_TYPE_OSR_1, + AD4695_SCAN_TYPE_OSR_4, + AD4695_SCAN_TYPE_OSR_16, + AD4695_SCAN_TYPE_OSR_64, +}; + +static const struct iio_scan_type ad4695_scan_type_offload_u[] = { + [AD4695_SCAN_TYPE_OSR_1] = { + .sign = 'u', + .realbits = 16, + .shift = 3, + .storagebits = 32, + }, + [AD4695_SCAN_TYPE_OSR_4] = { + .sign = 'u', + .realbits = 17, + .shift = 2, + .storagebits = 32, + }, + [AD4695_SCAN_TYPE_OSR_16] = { + .sign = 'u', + .realbits = 18, + .shift = 1, + .storagebits = 32, + }, + [AD4695_SCAN_TYPE_OSR_64] = { + .sign = 'u', + .realbits = 19, + .storagebits = 32, + }, +}; + +static const struct iio_scan_type ad4695_scan_type_offload_s[] = { + [AD4695_SCAN_TYPE_OSR_1] = { + .sign = 's', + .realbits = 16, + .shift = 3, + .storagebits = 32, + }, + [AD4695_SCAN_TYPE_OSR_4] = { + .sign = 's', + .realbits = 17, + .shift = 2, + .storagebits = 32, + }, + [AD4695_SCAN_TYPE_OSR_16] = { + .sign = 's', + .realbits = 18, + .shift = 1, + .storagebits = 32, + }, + [AD4695_SCAN_TYPE_OSR_64] = { + .sign = 's', + .realbits = 19, + .storagebits = 32, + }, +}; + +static const struct iio_chan_spec ad4695_channel_template = { + .type = IIO_VOLTAGE, + .indexed = 1, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SCALE) | + BIT(IIO_CHAN_INFO_OFFSET) | + BIT(IIO_CHAN_INFO_CALIBSCALE) | + BIT(IIO_CHAN_INFO_CALIBBIAS), + .info_mask_separate_available = BIT(IIO_CHAN_INFO_CALIBSCALE) | + BIT(IIO_CHAN_INFO_CALIBBIAS), + .scan_type = { + .sign = 'u', + .realbits = 16, + .storagebits = 16, + }, +}; + +static const struct iio_chan_spec ad4695_temp_channel_template = { + .address = AD4695_CMD_TEMP_CHAN, + .type = IIO_TEMP, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SCALE) | + BIT(IIO_CHAN_INFO_OFFSET), + .scan_type = { + .sign = 's', + .realbits = 16, + .storagebits = 16, + }, +}; + +static const struct iio_chan_spec ad4695_soft_timestamp_channel_template = + IIO_CHAN_SOFT_TIMESTAMP(0); + +static const char * const ad4695_power_supplies[] = { + "avdd", "vio" +}; + +static const int ad4695_oversampling_ratios[] = { + 1, 4, 16, 64, +}; + +static const struct ad4695_chip_info ad4695_chip_info = { + .name = "ad4695", + .max_sample_rate = 500 * KILO, + .t_acq_ns = 1715, + .num_voltage_inputs = 16, +}; + +static const struct ad4695_chip_info ad4696_chip_info = { + .name = "ad4696", + .max_sample_rate = 1 * MEGA, + .t_acq_ns = 715, + .num_voltage_inputs = 16, +}; + +static const struct ad4695_chip_info ad4697_chip_info = { + .name = "ad4697", + .max_sample_rate = 500 * KILO, + .t_acq_ns = 1715, + .num_voltage_inputs = 8, +}; + +static const struct ad4695_chip_info ad4698_chip_info = { + .name = "ad4698", + .max_sample_rate = 1 * MEGA, + .t_acq_ns = 715, + .num_voltage_inputs = 8, +}; + +static void ad4695_cnv_manual_trigger(struct ad4695_state *st) +{ + gpiod_set_value_cansleep(st->cnv_gpio, 1); + ndelay(10); + gpiod_set_value_cansleep(st->cnv_gpio, 0); +} + +/** + * ad4695_set_single_cycle_mode - Set the device in single cycle mode + * @st: The AD4695 state + * @channel: The first channel to read + * + * As per the datasheet, to enable single cycle mode, we need to set + * STD_SEQ_EN=0, NUM_SLOTS_AS=0 and CYC_CTRL=1 (Table 15). Setting SPI_MODE=1 + * triggers the first conversion using the channel in AS_SLOT0. + * + * Context: can sleep, must be called with iio_device_claim_direct held + * Return: 0 on success, a negative error code on failure + */ +static int ad4695_set_single_cycle_mode(struct ad4695_state *st, + unsigned int channel) +{ + int ret; + + ret = regmap_clear_bits(st->regmap, AD4695_REG_SEQ_CTRL, + AD4695_REG_SEQ_CTRL_STD_SEQ_EN | + AD4695_REG_SEQ_CTRL_NUM_SLOTS_AS); + if (ret) + return ret; + + ret = regmap_write(st->regmap, AD4695_REG_AS_SLOT(0), + FIELD_PREP(AD4695_REG_AS_SLOT_INX, channel)); + if (ret) + return ret; + + return regmap_set_bits(st->regmap, AD4695_REG_SETUP, + AD4695_REG_SETUP_SPI_MODE | + AD4695_REG_SETUP_SPI_CYC_CTRL); +} + +/** + * ad4695_enter_advanced_sequencer_mode - Put the ADC in advanced sequencer mode + * @st: The driver state + * @n: The number of slots to use - must be >= 2, <= 128 + * + * As per the datasheet, to enable advanced sequencer, we need to set + * STD_SEQ_EN=0, NUM_SLOTS_AS=n-1 and CYC_CTRL=0 (Table 15). Setting SPI_MODE=1 + * triggers the first conversion using the channel in AS_SLOT0. + * + * Return: 0 on success, a negative error code on failure + */ +static int ad4695_enter_advanced_sequencer_mode(struct ad4695_state *st, u32 n) +{ + int ret; + + ret = regmap_update_bits(st->regmap, AD4695_REG_SEQ_CTRL, + AD4695_REG_SEQ_CTRL_STD_SEQ_EN | + AD4695_REG_SEQ_CTRL_NUM_SLOTS_AS, + FIELD_PREP(AD4695_REG_SEQ_CTRL_STD_SEQ_EN, 0) | + FIELD_PREP(AD4695_REG_SEQ_CTRL_NUM_SLOTS_AS, n - 1)); + if (ret) + return ret; + + return regmap_update_bits(st->regmap, AD4695_REG_SETUP, + AD4695_REG_SETUP_SPI_MODE | AD4695_REG_SETUP_SPI_CYC_CTRL, + FIELD_PREP(AD4695_REG_SETUP_SPI_MODE, 1) | + FIELD_PREP(AD4695_REG_SETUP_SPI_CYC_CTRL, 0)); +} + +/** + * ad4695_exit_conversion_mode - Exit conversion mode + * @st: The AD4695 state + * + * Sends SPI command to exit conversion mode. + * + * Return: 0 on success, a negative error code on failure + */ +static int ad4695_exit_conversion_mode(struct ad4695_state *st) +{ + /* + * An extra transfer is needed to trigger a conversion here so + * that we can be 100% sure the command will be processed by the + * ADC, rather than relying on it to be in the correct state + * when this function is called (this chip has a quirk where the + * command only works when reading a conversion, and if the + * previous conversion was already read then it won't work). The + * actual conversion command is then run at the slower + * AD4695_REG_ACCESS_SCLK_HZ speed to guarantee this works. + */ + struct spi_transfer xfers[] = { + { + .delay.value = AD4695_T_CNVL_NS, + .delay.unit = SPI_DELAY_UNIT_NSECS, + .cs_change = 1, + .cs_change_delay.value = AD4695_T_CNVH_NS, + .cs_change_delay.unit = SPI_DELAY_UNIT_NSECS, + }, + { + .speed_hz = AD4695_REG_ACCESS_SCLK_HZ, + .tx_buf = &st->cnv_cmd2, + .len = 1, + .delay.value = AD4695_T_REGCONFIG_NS, + .delay.unit = SPI_DELAY_UNIT_NSECS, + }, + }; + + /* + * Technically, could do a 5-bit transfer, but shifting to start of + * 8 bits instead for better SPI controller support. + */ + st->cnv_cmd2 = AD4695_CMD_EXIT_CNV_MODE << 3; + + if (st->cnv_gpio) { + ad4695_cnv_manual_trigger(st); + + /* + * In this case, CNV is not connected to CS, so we don't need + * the extra CS toggle to trigger the conversion and toggling + * CS would have no effect. + */ + return spi_sync_transfer(st->spi, &xfers[1], 1); + } + + return spi_sync_transfer(st->spi, xfers, ARRAY_SIZE(xfers)); +} + +static int ad4695_set_ref_voltage(struct ad4695_state *st, int vref_mv) +{ + u8 val; + + if (vref_mv >= 2400 && vref_mv <= 2750) + val = 0; + else if (vref_mv > 2750 && vref_mv <= 3250) + val = 1; + else if (vref_mv > 3250 && vref_mv <= 3750) + val = 2; + else if (vref_mv > 3750 && vref_mv <= 4500) + val = 3; + else if (vref_mv > 4500 && vref_mv <= 5100) + val = 4; + else + return -EINVAL; + + return regmap_update_bits(st->regmap, AD4695_REG_REF_CTRL, + AD4695_REG_REF_CTRL_VREF_SET, + FIELD_PREP(AD4695_REG_REF_CTRL_VREF_SET, val)); +} + +/** + * ad4695_osr_to_regval - convert ratio to OSR register value + * @ratio: ratio to check + * + * Check if ratio is present in the list of available ratios and return + * the corresponding value that needs to be written to the register to + * select that ratio. + * + * Returns: register value (0 to 3) or -EINVAL if there is not an exact + * match + */ +static int ad4695_osr_to_regval(int ratio) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(ad4695_oversampling_ratios); i++) { + if (ratio == ad4695_oversampling_ratios[i]) + return i; + } + + return -EINVAL; +} + +static int ad4695_write_chn_cfg(struct ad4695_state *st, + struct ad4695_channel_config *cfg) +{ + u32 mask, val; + + mask = AD4695_REG_CONFIG_IN_MODE; + val = FIELD_PREP(AD4695_REG_CONFIG_IN_MODE, cfg->bipolar ? 1 : 0); + + mask |= AD4695_REG_CONFIG_IN_PAIR; + val |= FIELD_PREP(AD4695_REG_CONFIG_IN_PAIR, cfg->pin_pairing); + + mask |= AD4695_REG_CONFIG_IN_AINHIGHZ_EN; + val |= FIELD_PREP(AD4695_REG_CONFIG_IN_AINHIGHZ_EN, + cfg->highz_en ? 1 : 0); + + return regmap_update_bits(st->regmap, + AD4695_REG_CONFIG_IN(cfg->channel), + mask, val); +} + +static int ad4695_buffer_preenable(struct iio_dev *indio_dev) +{ + struct ad4695_state *st = iio_priv(indio_dev); + struct spi_transfer *xfer; + u8 temp_chan_bit = st->chip_info->num_voltage_inputs; + u32 bit, num_xfer, num_slots; + u32 temp_en = 0; + int ret, rx_buf_offset = 0; + + /* + * We are using the advanced sequencer since it is the only way to read + * multiple channels that allows individual configuration of each + * voltage input channel. Slot 0 in the advanced sequencer is used to + * account for the gap between trigger polls - we don't read data from + * this slot. Each enabled voltage channel is assigned a slot starting + * with slot 1. + */ + num_slots = 1; + + memset(st->buf_read_xfer, 0, sizeof(st->buf_read_xfer)); + + /* First xfer is only to trigger conversion of slot 1, so no rx. */ + xfer = &st->buf_read_xfer[0]; + xfer->cs_change = 1; + xfer->delay.value = st->chip_info->t_acq_ns; + xfer->delay.unit = SPI_DELAY_UNIT_NSECS; + xfer->cs_change_delay.value = AD4695_T_CONVERT_NS; + xfer->cs_change_delay.unit = SPI_DELAY_UNIT_NSECS; + num_xfer = 1; + + iio_for_each_active_channel(indio_dev, bit) { + xfer = &st->buf_read_xfer[num_xfer]; + xfer->bits_per_word = 16; + xfer->rx_buf = &st->buf[rx_buf_offset++]; + xfer->len = 2; + + if (bit == temp_chan_bit) { + temp_en = 1; + } else { + ret = regmap_write(st->regmap, + AD4695_REG_AS_SLOT(num_slots), + FIELD_PREP(AD4695_REG_AS_SLOT_INX, bit)); + if (ret) + return ret; + + num_slots++; + } + + num_xfer++; + + /* + * We need to add a blank xfer in data reads, to meet the timing + * requirement of a minimum delay between the last SCLK rising + * edge and the CS deassert. + */ + xfer = &st->buf_read_xfer[num_xfer]; + xfer->delay.value = AD4695_T_SCK_CNV_DELAY_NS; + xfer->delay.unit = SPI_DELAY_UNIT_NSECS; + xfer->cs_change = 1; + xfer->cs_change_delay.value = AD4695_T_CONVERT_NS; + xfer->cs_change_delay.unit = SPI_DELAY_UNIT_NSECS; + + num_xfer++; + } + + /* + * The advanced sequencer requires that at least 2 slots are enabled. + * Since slot 0 is always used for other purposes, we need only 1 + * enabled voltage channel to meet this requirement. If the temperature + * channel is the only enabled channel, we need to add one more slot in + * the sequence but not read from it. This is because the temperature + * sensor is sampled at the end of the channel sequence in advanced + * sequencer mode (see datasheet page 38). + * + * From the iio_for_each_active_channel() block above, we now have an + * xfer with data followed by a blank xfer to allow us to meet the + * timing spec, so move both of those up before adding an extra to + * handle the temperature-only case. + */ + if (num_slots < 2) { + /* Move last two xfers */ + st->buf_read_xfer[num_xfer] = st->buf_read_xfer[num_xfer - 1]; + st->buf_read_xfer[num_xfer - 1] = st->buf_read_xfer[num_xfer - 2]; + num_xfer++; + + /* Modify inserted xfer for extra slot. */ + xfer = &st->buf_read_xfer[num_xfer - 3]; + memset(xfer, 0, sizeof(*xfer)); + xfer->cs_change = 1; + xfer->delay.value = st->chip_info->t_acq_ns; + xfer->delay.unit = SPI_DELAY_UNIT_NSECS; + xfer->cs_change_delay.value = AD4695_T_CONVERT_NS; + xfer->cs_change_delay.unit = SPI_DELAY_UNIT_NSECS; + xfer++; + + /* and add the extra slot in the sequencer */ + ret = regmap_write(st->regmap, + AD4695_REG_AS_SLOT(num_slots), + FIELD_PREP(AD4695_REG_AS_SLOT_INX, 0)); + if (ret) + return ret; + + num_slots++; + + /* + * We still want to point at the last xfer when finished, so + * update the pointer. + */ + xfer = &st->buf_read_xfer[num_xfer - 1]; + } + + /* + * Don't keep CS asserted after last xfer. Also triggers conversion of + * slot 0. + */ + xfer->cs_change = 0; + + /* + * Temperature channel isn't included in the sequence, but rather + * controlled by setting a bit in the TEMP_CTRL register. + */ + + ret = regmap_update_bits(st->regmap, AD4695_REG_TEMP_CTRL, + AD4695_REG_TEMP_CTRL_TEMP_EN, + FIELD_PREP(AD4695_REG_TEMP_CTRL_TEMP_EN, temp_en)); + if (ret) + return ret; + + spi_message_init_with_transfers(&st->buf_read_msg, st->buf_read_xfer, + num_xfer); + + ret = spi_optimize_message(st->spi, &st->buf_read_msg); + if (ret) + return ret; + + /* This triggers conversion of slot 0. */ + ret = ad4695_enter_advanced_sequencer_mode(st, num_slots); + if (ret) + spi_unoptimize_message(&st->buf_read_msg); + + return ret; +} + +static int ad4695_buffer_postdisable(struct iio_dev *indio_dev) +{ + struct ad4695_state *st = iio_priv(indio_dev); + int ret; + + ret = ad4695_exit_conversion_mode(st); + if (ret) + return ret; + + spi_unoptimize_message(&st->buf_read_msg); + + return 0; +} + +static const struct iio_buffer_setup_ops ad4695_buffer_setup_ops = { + .preenable = ad4695_buffer_preenable, + .postdisable = ad4695_buffer_postdisable, +}; + +static irqreturn_t ad4695_trigger_handler(int irq, void *p) +{ + struct iio_poll_func *pf = p; + struct iio_dev *indio_dev = pf->indio_dev; + struct ad4695_state *st = iio_priv(indio_dev); + int ret; + + ret = spi_sync(st->spi, &st->buf_read_msg); + if (ret) + goto out; + + iio_push_to_buffers_with_ts(indio_dev, st->buf, sizeof(st->buf), + pf->timestamp); + +out: + iio_trigger_notify_done(indio_dev->trig); + + return IRQ_HANDLED; +} + +static int ad4695_offload_buffer_postenable(struct iio_dev *indio_dev) +{ + struct ad4695_state *st = iio_priv(indio_dev); + struct spi_offload_trigger_config config = { + .type = SPI_OFFLOAD_TRIGGER_DATA_READY, + }; + struct spi_transfer *xfer = &st->buf_read_xfer[0]; + struct pwm_state state; + u8 temp_chan_bit = st->chip_info->num_voltage_inputs; + u8 num_slots = 0; + u8 temp_en = 0; + unsigned int bit; + int ret; + + iio_for_each_active_channel(indio_dev, bit) { + if (bit == temp_chan_bit) { + temp_en = 1; + continue; + } + + ret = regmap_write(st->regmap, AD4695_REG_AS_SLOT(num_slots), + FIELD_PREP(AD4695_REG_AS_SLOT_INX, bit)); + if (ret) + return ret; + + num_slots++; + } + + /* + * For non-offload, we could discard data to work around this + * restriction, but with offload, that is not possible. + */ + if (num_slots < 2) { + dev_err(&st->spi->dev, + "At least two voltage channels must be enabled.\n"); + return -EINVAL; + } + + ret = regmap_update_bits(st->regmap, AD4695_REG_TEMP_CTRL, + AD4695_REG_TEMP_CTRL_TEMP_EN, + FIELD_PREP(AD4695_REG_TEMP_CTRL_TEMP_EN, + temp_en)); + if (ret) + return ret; + + /* Each BUSY event means just one sample for one channel is ready. */ + memset(xfer, 0, sizeof(*xfer)); + xfer->offload_flags = SPI_OFFLOAD_XFER_RX_STREAM; + /* Using 19 bits per word to allow for possible oversampling */ + xfer->bits_per_word = 19; + xfer->len = 4; + + spi_message_init_with_transfers(&st->buf_read_msg, xfer, 1); + st->buf_read_msg.offload = st->offload; + + ret = spi_optimize_message(st->spi, &st->buf_read_msg); + if (ret) + return ret; + + /* + * NB: technically, this is part the SPI offload trigger enable, but it + * doesn't work to call it from the offload trigger enable callback + * because it requires accessing the SPI bus. Calling it from the + * trigger enable callback could cause a deadlock. + */ + ret = regmap_set_bits(st->regmap, AD4695_REG_GP_MODE, + AD4695_REG_GP_MODE_BUSY_GP_EN); + if (ret) + goto err_unoptimize_message; + + ret = ad4695_enter_advanced_sequencer_mode(st, num_slots); + if (ret) + goto err_disable_busy_output; + + ret = spi_offload_trigger_enable(st->offload, st->offload_trigger, + &config); + if (ret) + goto err_exit_conversion_mode; + + mutex_lock(&st->cnv_pwm_lock); + pwm_get_state(st->cnv_pwm, &state); + /* + * PWM subsystem generally rounds down, so requesting 2x minimum high + * time ensures that we meet the minimum high time in any case. + */ + state.duty_cycle = AD4695_T_CNVH_NS * 2; + ret = pwm_apply_might_sleep(st->cnv_pwm, &state); + mutex_unlock(&st->cnv_pwm_lock); + if (ret) + goto err_offload_trigger_disable; + + return 0; + +err_offload_trigger_disable: + spi_offload_trigger_disable(st->offload, st->offload_trigger); + +err_exit_conversion_mode: + ad4695_exit_conversion_mode(st); + +err_disable_busy_output: + regmap_clear_bits(st->regmap, AD4695_REG_GP_MODE, + AD4695_REG_GP_MODE_BUSY_GP_EN); + +err_unoptimize_message: + spi_unoptimize_message(&st->buf_read_msg); + + return ret; +} + +static int ad4695_offload_buffer_predisable(struct iio_dev *indio_dev) +{ + struct ad4695_state *st = iio_priv(indio_dev); + struct pwm_state state; + int ret; + + scoped_guard(mutex, &st->cnv_pwm_lock) { + pwm_get_state(st->cnv_pwm, &state); + state.duty_cycle = 0; + ret = pwm_apply_might_sleep(st->cnv_pwm, &state); + if (ret) + return ret; + } + + spi_offload_trigger_disable(st->offload, st->offload_trigger); + + /* + * ad4695_exit_conversion_mode() triggers a conversion, so it has to be + * done after spi_offload_trigger_disable(). + */ + ret = ad4695_exit_conversion_mode(st); + if (ret) + return ret; + + ret = regmap_clear_bits(st->regmap, AD4695_REG_GP_MODE, + AD4695_REG_GP_MODE_BUSY_GP_EN); + if (ret) + return ret; + + spi_unoptimize_message(&st->buf_read_msg); + + return 0; +} + +static const struct iio_buffer_setup_ops ad4695_offload_buffer_setup_ops = { + .postenable = ad4695_offload_buffer_postenable, + .predisable = ad4695_offload_buffer_predisable, +}; + +/** + * ad4695_read_one_sample - Read a single sample using single-cycle mode + * @st: The AD4695 state + * @address: The address of the channel to read + * + * Upon successful return, the sample will be stored in `st->raw_data`. + * + * Context: can sleep, must be called with iio_device_claim_direct held + * Return: 0 on success, a negative error code on failure + */ +static int ad4695_read_one_sample(struct ad4695_state *st, unsigned int address) +{ + struct spi_transfer xfers[2] = { + { + .speed_hz = AD4695_REG_ACCESS_SCLK_HZ, + .bits_per_word = 16, + .tx_buf = &st->cnv_cmd, + .len = 2, + }, + { + /* Required delay between last SCLK and CNV/CS */ + .delay.value = AD4695_T_SCK_CNV_DELAY_NS, + .delay.unit = SPI_DELAY_UNIT_NSECS, + } + }; + int ret; + + ret = ad4695_set_single_cycle_mode(st, address); + if (ret) + return ret; + + /* + * If CNV is connected to CS, the previous function will have triggered + * the conversion, otherwise, we do it manually. + */ + if (st->cnv_gpio) + ad4695_cnv_manual_trigger(st); + + /* + * Setting the first channel to the temperature channel isn't supported + * in single-cycle mode, so we have to do an extra conversion to read + * the temperature. + */ + if (address == AD4695_CMD_TEMP_CHAN) { + st->cnv_cmd = AD4695_CMD_TEMP_CHAN << 11; + + ret = spi_sync_transfer(st->spi, xfers, ARRAY_SIZE(xfers)); + if (ret) + return ret; + + /* + * If CNV is connected to CS, the previous function will have + * triggered the conversion, otherwise, we do it manually. + */ + if (st->cnv_gpio) + ad4695_cnv_manual_trigger(st); + } + + /* Then read the result and exit conversion mode. */ + st->cnv_cmd = AD4695_CMD_EXIT_CNV_MODE << 11; + xfers[0].rx_buf = &st->raw_data; + + return spi_sync_transfer(st->spi, xfers, ARRAY_SIZE(xfers)); +} + +static int __ad4695_read_info_raw(struct ad4695_state *st, + struct iio_chan_spec const *chan, + int *val) +{ + u8 realbits = chan->scan_type.realbits; + int ret; + + ret = ad4695_read_one_sample(st, chan->address); + if (ret) + return ret; + + if (chan->scan_type.sign == 's') + *val = sign_extend32(st->raw_data, realbits - 1); + else + *val = st->raw_data; + + return IIO_VAL_INT; +} + +static int ad4695_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct ad4695_state *st = iio_priv(indio_dev); + const struct iio_scan_type *scan_type; + struct ad4695_channel_config *cfg; + unsigned int reg_val; + int ret, tmp; + u8 realbits; + + if (chan->type == IIO_VOLTAGE) + cfg = &st->channels_cfg[chan->scan_index]; + + scan_type = iio_get_current_scan_type(indio_dev, chan); + if (IS_ERR(scan_type)) + return PTR_ERR(scan_type); + + realbits = scan_type->realbits; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; + + ret = __ad4695_read_info_raw(st, chan, val); + iio_device_release_direct(indio_dev); + return ret; + case IIO_CHAN_INFO_SCALE: + switch (chan->type) { + case IIO_VOLTAGE: + *val = st->vref_mv; + *val2 = realbits; + return IIO_VAL_FRACTIONAL_LOG2; + case IIO_TEMP: + /* T_scale (°C) = raw * V_REF (mV) / (-1.8 mV/°C * 2^16) */ + *val = st->vref_mv * -556; + *val2 = 16; + return IIO_VAL_FRACTIONAL_LOG2; + default: + return -EINVAL; + } + case IIO_CHAN_INFO_OFFSET: + switch (chan->type) { + case IIO_VOLTAGE: + if (cfg->pin_pairing == AD4695_IN_PAIR_COM) + *val = st->com_mv * (1 << realbits) / st->vref_mv; + else if (cfg->pin_pairing == AD4695_IN_PAIR_EVEN_ODD) + *val = cfg->common_mode_mv * (1 << realbits) / st->vref_mv; + else + *val = 0; + + return IIO_VAL_INT; + case IIO_TEMP: + /* T_offset (°C) = -725 mV / (-1.8 mV/°C) */ + /* T_offset (raw) = T_offset (°C) * (-1.8 mV/°C) * 2^16 / V_REF (mV) */ + *val = -47513600; + *val2 = st->vref_mv; + return IIO_VAL_FRACTIONAL; + default: + return -EINVAL; + } + case IIO_CHAN_INFO_CALIBSCALE: + switch (chan->type) { + case IIO_VOLTAGE: + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; + ret = regmap_read(st->regmap16, + AD4695_REG_GAIN_IN(chan->scan_index), + ®_val); + iio_device_release_direct(indio_dev); + if (ret) + return ret; + *val = reg_val; + *val2 = 15; + + return IIO_VAL_FRACTIONAL_LOG2; + default: + return -EINVAL; + } + case IIO_CHAN_INFO_CALIBBIAS: + switch (chan->type) + case IIO_VOLTAGE: { + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; + ret = regmap_read(st->regmap16, + AD4695_REG_OFFSET_IN(chan->scan_index), + ®_val); + iio_device_release_direct(indio_dev); + if (ret) + return ret; + + tmp = sign_extend32(reg_val, 15); + + switch (cfg->oversampling_ratio) { + case 1: + *val = tmp / 4; + *val2 = abs(tmp) % 4 * MICRO / 4; + break; + case 4: + *val = tmp / 2; + *val2 = abs(tmp) % 2 * MICRO / 2; + break; + case 16: + *val = tmp; + *val2 = 0; + break; + case 64: + *val = tmp * 2; + *val2 = 0; + break; + default: + return -EINVAL; + } + + if (tmp < 0 && *val2) { + *val *= -1; + *val2 *= -1; + } + + return IIO_VAL_INT_PLUS_MICRO; + default: + return -EINVAL; + } + case IIO_CHAN_INFO_OVERSAMPLING_RATIO: + switch (chan->type) { + case IIO_VOLTAGE: + *val = cfg->oversampling_ratio; + return IIO_VAL_INT; + default: + return -EINVAL; + } + case IIO_CHAN_INFO_SAMP_FREQ: { + struct pwm_state state; + unsigned int osr = 1; + + if (chan->type == IIO_VOLTAGE) + osr = cfg->oversampling_ratio; + + ret = pwm_get_state_hw(st->cnv_pwm, &state); + if (ret) + return ret; + + /* + * The effective sampling frequency for a channel is the input + * frequency divided by the channel's OSR value. + */ + *val = DIV_ROUND_UP_ULL(NSEC_PER_SEC, state.period * osr); + + return IIO_VAL_INT; + } + default: + return -EINVAL; + } +} + +static int ad4695_write_raw_get_fmt(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + long mask) +{ + switch (mask) { + case IIO_CHAN_INFO_SAMP_FREQ: + return IIO_VAL_INT; + default: + return IIO_VAL_INT_PLUS_MICRO; + } +} + +static int ad4695_set_osr_val(struct ad4695_state *st, + struct iio_chan_spec const *chan, + int val) +{ + int osr = ad4695_osr_to_regval(val); + + if (osr < 0) + return osr; + + switch (chan->type) { + case IIO_VOLTAGE: + st->channels_cfg[chan->scan_index].oversampling_ratio = val; + return regmap_update_bits(st->regmap, + AD4695_REG_CONFIG_IN(chan->scan_index), + AD4695_REG_CONFIG_IN_OSR_SET, + FIELD_PREP(AD4695_REG_CONFIG_IN_OSR_SET, osr)); + default: + return -EINVAL; + } +} + +static unsigned int ad4695_get_calibbias(int val, int val2, int osr) +{ + int val_calc, scale; + + switch (osr) { + case 4: + scale = 4; + break; + case 16: + scale = 2; + break; + case 64: + scale = 1; + break; + default: + scale = 8; + break; + } + + val = clamp_t(int, val, S32_MIN / 8, S32_MAX / 8); + + /* val2 range is (-MICRO, MICRO) if val == 0, otherwise [0, MICRO) */ + if (val < 0) + val_calc = val * scale - val2 * scale / MICRO; + else if (val2 < 0) + /* if val2 < 0 then val == 0 */ + val_calc = val2 * scale / (int)MICRO; + else + val_calc = val * scale + val2 * scale / MICRO; + + val_calc /= 2; + + return clamp_t(int, val_calc, S16_MIN, S16_MAX); +} + +static int __ad4695_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, int val2, long mask) +{ + struct ad4695_state *st = iio_priv(indio_dev); + unsigned int reg_val; + unsigned int osr = 1; + + if (chan->type == IIO_VOLTAGE) + osr = st->channels_cfg[chan->scan_index].oversampling_ratio; + + switch (mask) { + case IIO_CHAN_INFO_CALIBSCALE: + switch (chan->type) { + case IIO_VOLTAGE: + if (val < 0 || val2 < 0) + reg_val = 0; + else if (val > 1) + reg_val = U16_MAX; + else + reg_val = (val * (1 << 16) + + mul_u64_u32_div(val2, 1 << 16, + MICRO)) / 2; + + return regmap_write(st->regmap16, + AD4695_REG_GAIN_IN(chan->scan_index), + reg_val); + default: + return -EINVAL; + } + case IIO_CHAN_INFO_CALIBBIAS: + switch (chan->type) { + case IIO_VOLTAGE: + reg_val = ad4695_get_calibbias(val, val2, osr); + return regmap_write(st->regmap16, + AD4695_REG_OFFSET_IN(chan->scan_index), + reg_val); + default: + return -EINVAL; + } + case IIO_CHAN_INFO_SAMP_FREQ: { + struct pwm_state state; + /* + * Limit the maximum acceptable sample rate according to + * the channel's oversampling ratio. + */ + u64 max_osr_rate = DIV_ROUND_UP_ULL(st->chip_info->max_sample_rate, + osr); + + if (val <= 0 || val > max_osr_rate) + return -EINVAL; + + guard(mutex)(&st->cnv_pwm_lock); + pwm_get_state(st->cnv_pwm, &state); + /* + * The required sample frequency for a given OSR is the + * input frequency multiplied by it. + */ + state.period = DIV_ROUND_UP_ULL(NSEC_PER_SEC, val * osr); + return pwm_apply_might_sleep(st->cnv_pwm, &state); + } + case IIO_CHAN_INFO_OVERSAMPLING_RATIO: + return ad4695_set_osr_val(st, chan, val); + default: + return -EINVAL; + } +} + +static int ad4695_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, int val2, long mask) +{ + int ret; + + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; + ret = __ad4695_write_raw(indio_dev, chan, val, val2, mask); + iio_device_release_direct(indio_dev); + + return ret; +} + +static int ad4695_read_avail(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + const int **vals, int *type, int *length, + long mask) +{ + int ret; + static const int ad4695_calibscale_available[6] = { + /* Range of 0 (inclusive) to 2 (exclusive) */ + 0, 15, 1, 15, U16_MAX, 15 + }; + static const int ad4695_calibbias_available[4][6] = { + /* + * Datasheet says FSR/8 which translates to signed/4. The step + * depends on oversampling ratio, so we need four different + * ranges to select from. + */ + { + S16_MIN / 4, 0, + 0, MICRO / 4, + S16_MAX / 4, S16_MAX % 4 * MICRO / 4 + }, + { + S16_MIN / 2, 0, + 0, MICRO / 2, + S16_MAX / 2, S16_MAX % 2 * MICRO / 2, + }, + { + S16_MIN, 0, + 1, 0, + S16_MAX, 0, + }, + { + S16_MIN * 2, 0, + 2, 0, + S16_MAX * 2, 0, + }, + }; + struct ad4695_state *st = iio_priv(indio_dev); + unsigned int osr = 1; + + if (chan->type == IIO_VOLTAGE) + osr = st->channels_cfg[chan->scan_index].oversampling_ratio; + + switch (mask) { + case IIO_CHAN_INFO_CALIBSCALE: + switch (chan->type) { + case IIO_VOLTAGE: + *vals = ad4695_calibscale_available; + *type = IIO_VAL_FRACTIONAL_LOG2; + return IIO_AVAIL_RANGE; + default: + return -EINVAL; + } + case IIO_CHAN_INFO_CALIBBIAS: + switch (chan->type) { + case IIO_VOLTAGE: + ret = ad4695_osr_to_regval(osr); + if (ret < 0) + return ret; + /* + * Select the appropriate calibbias array based on the + * OSR value in the register. + */ + *vals = ad4695_calibbias_available[ret]; + *type = IIO_VAL_INT_PLUS_MICRO; + return IIO_AVAIL_RANGE; + default: + return -EINVAL; + } + case IIO_CHAN_INFO_SAMP_FREQ: + /* Max sample rate for the channel depends on OSR */ + st->sample_freq_range[2] = + DIV_ROUND_UP_ULL(st->chip_info->max_sample_rate, osr); + *vals = st->sample_freq_range; + *type = IIO_VAL_INT; + return IIO_AVAIL_RANGE; + case IIO_CHAN_INFO_OVERSAMPLING_RATIO: + switch (chan->type) { + case IIO_VOLTAGE: + *vals = ad4695_oversampling_ratios; + *length = ARRAY_SIZE(ad4695_oversampling_ratios); + *type = IIO_VAL_INT; + return IIO_AVAIL_LIST; + default: + return -EINVAL; + } + default: + return -EINVAL; + } +} + +static int ad4695_debugfs_reg_access(struct iio_dev *indio_dev, + unsigned int reg, + unsigned int writeval, + unsigned int *readval) +{ + struct ad4695_state *st = iio_priv(indio_dev); + int ret = -EINVAL; + + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; + + if (readval) { + if (regmap_check_range_table(st->regmap, reg, + &ad4695_regmap_rd_table)) + ret = regmap_read(st->regmap, reg, readval); + if (regmap_check_range_table(st->regmap16, reg, + &ad4695_regmap16_rd_table)) + ret = regmap_read(st->regmap16, reg, readval); + } else { + if (regmap_check_range_table(st->regmap, reg, + &ad4695_regmap_wr_table)) + ret = regmap_write(st->regmap, reg, writeval); + if (regmap_check_range_table(st->regmap16, reg, + &ad4695_regmap16_wr_table)) + ret = regmap_write(st->regmap16, reg, writeval); + } + iio_device_release_direct(indio_dev); + + return ret; +} + +static int ad4695_get_current_scan_type(const struct iio_dev *indio_dev, + const struct iio_chan_spec *chan) +{ + struct ad4695_state *st = iio_priv(indio_dev); + unsigned int osr = st->channels_cfg[chan->scan_index].oversampling_ratio; + + switch (osr) { + case 1: + return AD4695_SCAN_TYPE_OSR_1; + case 4: + return AD4695_SCAN_TYPE_OSR_4; + case 16: + return AD4695_SCAN_TYPE_OSR_16; + case 64: + return AD4695_SCAN_TYPE_OSR_64; + default: + return -EINVAL; + } +} + +static const struct iio_info ad4695_info = { + .read_raw = &ad4695_read_raw, + .write_raw_get_fmt = &ad4695_write_raw_get_fmt, + .write_raw = &ad4695_write_raw, + .read_avail = &ad4695_read_avail, + .debugfs_reg_access = &ad4695_debugfs_reg_access, +}; + +static const struct iio_info ad4695_offload_info = { + .read_raw = &ad4695_read_raw, + .write_raw_get_fmt = &ad4695_write_raw_get_fmt, + .write_raw = &ad4695_write_raw, + .get_current_scan_type = &ad4695_get_current_scan_type, + .read_avail = &ad4695_read_avail, + .debugfs_reg_access = &ad4695_debugfs_reg_access, +}; + +static int ad4695_parse_channel_cfg(struct ad4695_state *st) +{ + struct device *dev = &st->spi->dev; + struct ad4695_channel_config *chan_cfg; + struct iio_chan_spec *iio_chan; + int ret, i; + + /* populate defaults */ + for (i = 0; i < st->chip_info->num_voltage_inputs; i++) { + chan_cfg = &st->channels_cfg[i]; + iio_chan = &st->iio_chan[i]; + + chan_cfg->highz_en = true; + chan_cfg->channel = i; + + /* This is the default OSR after reset */ + chan_cfg->oversampling_ratio = 1; + + *iio_chan = ad4695_channel_template; + iio_chan->channel = i; + iio_chan->scan_index = i; + iio_chan->address = AD4695_CMD_VOLTAGE_CHAN(i); + } + + /* modify based on firmware description */ + device_for_each_child_node_scoped(dev, child) { + u32 reg, val; + + ret = fwnode_property_read_u32(child, "reg", ®); + if (ret) + return dev_err_probe(dev, ret, + "failed to read reg property (%s)\n", + fwnode_get_name(child)); + + if (reg >= st->chip_info->num_voltage_inputs) + return dev_err_probe(dev, -EINVAL, + "reg out of range (%s)\n", + fwnode_get_name(child)); + + iio_chan = &st->iio_chan[reg]; + chan_cfg = &st->channels_cfg[reg]; + + chan_cfg->highz_en = + !fwnode_property_read_bool(child, "adi,no-high-z"); + chan_cfg->bipolar = fwnode_property_read_bool(child, "bipolar"); + + ret = fwnode_property_read_u32(child, "common-mode-channel", + &val); + if (ret && ret != -EINVAL) + return dev_err_probe(dev, ret, + "failed to read common-mode-channel (%s)\n", + fwnode_get_name(child)); + + if (ret == -EINVAL || val == AD4695_COMMON_MODE_REFGND) + chan_cfg->pin_pairing = AD4695_IN_PAIR_REFGND; + else if (val == AD4695_COMMON_MODE_COM) + chan_cfg->pin_pairing = AD4695_IN_PAIR_COM; + else + chan_cfg->pin_pairing = AD4695_IN_PAIR_EVEN_ODD; + + if (chan_cfg->pin_pairing == AD4695_IN_PAIR_EVEN_ODD && + val % 2 == 0) + return dev_err_probe(dev, -EINVAL, + "common-mode-channel must be odd number (%s)\n", + fwnode_get_name(child)); + + if (chan_cfg->pin_pairing == AD4695_IN_PAIR_EVEN_ODD && + val != reg + 1) + return dev_err_probe(dev, -EINVAL, + "common-mode-channel must be next consecutive channel (%s)\n", + fwnode_get_name(child)); + + if (chan_cfg->pin_pairing == AD4695_IN_PAIR_EVEN_ODD) { + char name[5]; + + snprintf(name, sizeof(name), "in%d", reg + 1); + + ret = devm_regulator_get_enable_read_voltage(dev, name); + if (ret < 0) + return dev_err_probe(dev, ret, + "failed to get %s voltage (%s)\n", + name, fwnode_get_name(child)); + + chan_cfg->common_mode_mv = ret / 1000; + } + + if (chan_cfg->bipolar && + chan_cfg->pin_pairing == AD4695_IN_PAIR_REFGND) + return dev_err_probe(dev, -EINVAL, + "bipolar mode is not available for inputs paired with REFGND (%s).\n", + fwnode_get_name(child)); + + if (chan_cfg->bipolar) + iio_chan->scan_type.sign = 's'; + + ret = ad4695_write_chn_cfg(st, chan_cfg); + if (ret) + return ret; + } + + /* Temperature channel must be next scan index after voltage channels. */ + st->iio_chan[i] = ad4695_temp_channel_template; + st->iio_chan[i].scan_index = i; + i++; + + st->iio_chan[i] = ad4695_soft_timestamp_channel_template; + st->iio_chan[i].scan_index = i; + + return 0; +} + +static bool ad4695_offload_trigger_match(struct spi_offload_trigger *trigger, + enum spi_offload_trigger_type type, + u64 *args, u32 nargs) +{ + if (type != SPI_OFFLOAD_TRIGGER_DATA_READY) + return false; + + /* + * Requires 2 args: + * args[0] is the trigger event. + * args[1] is the GPIO pin number. + */ + if (nargs != 2 || args[0] != AD4695_TRIGGER_EVENT_BUSY) + return false; + + return true; +} + +static int ad4695_offload_trigger_request(struct spi_offload_trigger *trigger, + enum spi_offload_trigger_type type, + u64 *args, u32 nargs) +{ + struct ad4695_state *st = spi_offload_trigger_get_priv(trigger); + + /* Should already be validated by match, but just in case. */ + if (nargs != 2) + return -EINVAL; + + /* DT tells us if BUSY event uses GP0 or GP3. */ + if (args[1] == AD4695_TRIGGER_PIN_GP3) + return regmap_set_bits(st->regmap, AD4695_REG_GP_MODE, + AD4695_REG_GP_MODE_BUSY_GP_SEL); + + return regmap_clear_bits(st->regmap, AD4695_REG_GP_MODE, + AD4695_REG_GP_MODE_BUSY_GP_SEL); +} + +static int +ad4695_offload_trigger_validate(struct spi_offload_trigger *trigger, + struct spi_offload_trigger_config *config) +{ + if (config->type != SPI_OFFLOAD_TRIGGER_DATA_READY) + return -EINVAL; + + return 0; +} + +/* + * NB: There are no enable/disable callbacks here due to requiring a SPI + * message to enable or disable the BUSY output on the ADC. + */ +static const struct spi_offload_trigger_ops ad4695_offload_trigger_ops = { + .match = ad4695_offload_trigger_match, + .request = ad4695_offload_trigger_request, + .validate = ad4695_offload_trigger_validate, +}; + +static void ad4695_pwm_disable(void *pwm) +{ + pwm_disable(pwm); +} + +static int ad4695_probe_spi_offload(struct iio_dev *indio_dev, + struct ad4695_state *st) +{ + struct device *dev = &st->spi->dev; + struct spi_offload_trigger_info trigger_info = { + .fwnode = dev_fwnode(dev), + .ops = &ad4695_offload_trigger_ops, + .priv = st, + }; + struct pwm_state pwm_state; + struct dma_chan *rx_dma; + int ret, i; + + indio_dev->info = &ad4695_offload_info; + indio_dev->num_channels = st->chip_info->num_voltage_inputs + 1; + indio_dev->setup_ops = &ad4695_offload_buffer_setup_ops; + + if (!st->cnv_gpio) + return dev_err_probe(dev, -ENODEV, + "CNV GPIO is required for SPI offload\n"); + + ret = devm_spi_offload_trigger_register(dev, &trigger_info); + if (ret) + return dev_err_probe(dev, ret, + "failed to register offload trigger\n"); + + st->offload_trigger = devm_spi_offload_trigger_get(dev, st->offload, + SPI_OFFLOAD_TRIGGER_DATA_READY); + if (IS_ERR(st->offload_trigger)) + return dev_err_probe(dev, PTR_ERR(st->offload_trigger), + "failed to get offload trigger\n"); + + ret = devm_mutex_init(dev, &st->cnv_pwm_lock); + if (ret) + return ret; + + st->cnv_pwm = devm_pwm_get(dev, NULL); + if (IS_ERR(st->cnv_pwm)) + return dev_err_probe(dev, PTR_ERR(st->cnv_pwm), + "failed to get CNV PWM\n"); + + pwm_init_state(st->cnv_pwm, &pwm_state); + + /* If firmware didn't provide default rate, use 10kHz (arbitrary). */ + if (pwm_state.period == 0) + pwm_state.period = 100 * MILLI; + + pwm_state.enabled = true; + + ret = pwm_apply_might_sleep(st->cnv_pwm, &pwm_state); + if (ret) + return dev_err_probe(dev, ret, "failed to apply CNV PWM\n"); + + ret = devm_add_action_or_reset(dev, ad4695_pwm_disable, st->cnv_pwm); + if (ret) + return ret; + + rx_dma = devm_spi_offload_rx_stream_request_dma_chan(dev, st->offload); + if (IS_ERR(rx_dma)) + return dev_err_probe(dev, PTR_ERR(rx_dma), + "failed to get offload RX DMA\n"); + + for (i = 0; i < indio_dev->num_channels; i++) { + struct iio_chan_spec *chan = &st->iio_chan[i]; + struct ad4695_channel_config *cfg; + + /* + * NB: When using offload support, all channels need to have the + * same bits_per_word because they all use the same SPI message + * for reading one sample. In order to prevent breaking + * userspace in the future when oversampling support is added, + * all channels are set read 19 bits with a shift of 3 to mask + * out the extra bits even though we currently only support 16 + * bit samples (oversampling ratio == 1). + */ + chan->scan_type.shift = 3; + chan->scan_type.storagebits = 32; + /* add sample frequency for PWM CNV trigger */ + chan->info_mask_separate |= BIT(IIO_CHAN_INFO_SAMP_FREQ); + chan->info_mask_separate_available |= BIT(IIO_CHAN_INFO_SAMP_FREQ); + + /* Add the oversampling properties only for voltage channels */ + if (chan->type != IIO_VOLTAGE) + continue; + + cfg = &st->channels_cfg[i]; + + chan->info_mask_separate |= BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO); + chan->info_mask_separate_available |= + BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO); + chan->has_ext_scan_type = 1; + if (cfg->bipolar) { + chan->ext_scan_type = ad4695_scan_type_offload_s; + chan->num_ext_scan_type = + ARRAY_SIZE(ad4695_scan_type_offload_s); + } else { + chan->ext_scan_type = ad4695_scan_type_offload_u; + chan->num_ext_scan_type = + ARRAY_SIZE(ad4695_scan_type_offload_u); + } + } + + return devm_iio_dmaengine_buffer_setup_with_handle(dev, indio_dev, + rx_dma, IIO_BUFFER_DIRECTION_IN); +} + +static const struct spi_offload_config ad4695_spi_offload_config = { + .capability_flags = SPI_OFFLOAD_CAP_TRIGGER | + SPI_OFFLOAD_CAP_RX_STREAM_DMA, +}; + +static int ad4695_probe(struct spi_device *spi) +{ + struct device *dev = &spi->dev; + struct ad4695_state *st; + struct iio_dev *indio_dev; + bool use_internal_ldo_supply; + bool use_internal_ref_buffer; + int ret; + + indio_dev = devm_iio_device_alloc(dev, sizeof(*st)); + if (!indio_dev) + return -ENOMEM; + + st = iio_priv(indio_dev); + st->spi = spi; + + st->chip_info = spi_get_device_match_data(spi); + if (!st->chip_info) + return -EINVAL; + + st->sample_freq_range[0] = 1; /* min */ + st->sample_freq_range[1] = 1; /* step */ + st->sample_freq_range[2] = st->chip_info->max_sample_rate; /* max */ + + st->regmap = devm_regmap_init(dev, &ad4695_regmap_bus, st, + &ad4695_regmap_config); + if (IS_ERR(st->regmap)) + return dev_err_probe(dev, PTR_ERR(st->regmap), + "Failed to initialize regmap\n"); + + st->regmap16 = devm_regmap_init(dev, &ad4695_regmap_bus, st, + &ad4695_regmap16_config); + if (IS_ERR(st->regmap16)) + return dev_err_probe(dev, PTR_ERR(st->regmap16), + "Failed to initialize regmap16\n"); + + st->cnv_gpio = devm_gpiod_get_optional(dev, "cnv", GPIOD_OUT_LOW); + if (IS_ERR(st->cnv_gpio)) + return dev_err_probe(dev, PTR_ERR(st->cnv_gpio), + "Failed to get CNV GPIO\n"); + + ret = devm_regulator_bulk_get_enable(dev, + ARRAY_SIZE(ad4695_power_supplies), + ad4695_power_supplies); + if (ret) + return dev_err_probe(dev, ret, + "Failed to enable power supplies\n"); + + /* If LDO_IN supply is present, then we are using internal LDO. */ + ret = devm_regulator_get_enable_optional(dev, "ldo-in"); + if (ret < 0 && ret != -ENODEV) + return dev_err_probe(dev, ret, + "Failed to enable LDO_IN supply\n"); + + use_internal_ldo_supply = ret == 0; + + if (!use_internal_ldo_supply) { + /* Otherwise we need an external VDD supply. */ + ret = devm_regulator_get_enable(dev, "vdd"); + if (ret < 0) + return dev_err_probe(dev, ret, + "Failed to enable VDD supply\n"); + } + + /* If REFIN supply is given, then we are using internal buffer */ + ret = devm_regulator_get_enable_read_voltage(dev, "refin"); + if (ret < 0 && ret != -ENODEV) + return dev_err_probe(dev, ret, "Failed to get REFIN voltage\n"); + + if (ret != -ENODEV) { + st->vref_mv = ret / 1000; + use_internal_ref_buffer = true; + } else { + /* Otherwise, we need an external reference. */ + ret = devm_regulator_get_enable_read_voltage(dev, "ref"); + if (ret < 0) + return dev_err_probe(dev, ret, + "Failed to get REF voltage\n"); + + st->vref_mv = ret / 1000; + use_internal_ref_buffer = false; + } + + ret = devm_regulator_get_enable_read_voltage(dev, "com"); + if (ret < 0 && ret != -ENODEV) + return dev_err_probe(dev, ret, "Failed to get COM voltage\n"); + + st->com_mv = ret == -ENODEV ? 0 : ret / 1000; + + /* + * Reset the device using hardware reset if available or fall back to + * software reset. + */ + + st->reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH); + if (IS_ERR(st->reset_gpio)) + return PTR_ERR(st->reset_gpio); + + if (st->reset_gpio) { + gpiod_set_value(st->reset_gpio, 0); + msleep(AD4695_T_WAKEUP_HW_MS); + } else { + ret = regmap_write(st->regmap, AD4695_REG_SPI_CONFIG_A, + AD4695_REG_SPI_CONFIG_A_SW_RST); + if (ret) + return ret; + + msleep(AD4695_T_WAKEUP_SW_MS); + } + + /* Needed for regmap16 to be able to work correctly. */ + ret = regmap_set_bits(st->regmap, AD4695_REG_SPI_CONFIG_A, + AD4695_REG_SPI_CONFIG_A_ADDR_DIR); + if (ret) + return ret; + + /* Disable internal LDO if it isn't needed. */ + ret = regmap_update_bits(st->regmap, AD4695_REG_SETUP, + AD4695_REG_SETUP_LDO_EN, + FIELD_PREP(AD4695_REG_SETUP_LDO_EN, + use_internal_ldo_supply ? 1 : 0)); + if (ret) + return ret; + + /* configure reference supply */ + + if (device_property_present(dev, "adi,no-ref-current-limit")) { + ret = regmap_set_bits(st->regmap, AD4695_REG_REF_CTRL, + AD4695_REG_REF_CTRL_OV_MODE); + if (ret) + return ret; + } + + if (device_property_present(dev, "adi,no-ref-high-z")) { + if (use_internal_ref_buffer) + return dev_err_probe(dev, -EINVAL, + "Cannot disable high-Z mode for internal reference buffer\n"); + + ret = regmap_clear_bits(st->regmap, AD4695_REG_REF_CTRL, + AD4695_REG_REF_CTRL_REFHIZ_EN); + if (ret) + return ret; + } + + ret = ad4695_set_ref_voltage(st, st->vref_mv); + if (ret) + return ret; + + if (use_internal_ref_buffer) { + ret = regmap_set_bits(st->regmap, AD4695_REG_REF_CTRL, + AD4695_REG_REF_CTRL_REFBUF_EN); + if (ret) + return ret; + + /* Give the capacitor some time to charge up. */ + msleep(AD4695_T_REFBUF_MS); + } + + ret = ad4695_parse_channel_cfg(st); + if (ret) + return ret; + + indio_dev->name = st->chip_info->name; + indio_dev->info = &ad4695_info; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = st->iio_chan; + indio_dev->num_channels = st->chip_info->num_voltage_inputs + 2; + + st->offload = devm_spi_offload_get(dev, spi, &ad4695_spi_offload_config); + ret = PTR_ERR_OR_ZERO(st->offload); + if (ret && ret != -ENODEV) + return dev_err_probe(dev, ret, "failed to get SPI offload\n"); + + /* If no SPI offload, fall back to low speed usage. */ + if (ret == -ENODEV) { + /* Driver currently requires CNV pin to be connected to SPI CS */ + if (st->cnv_gpio) + return dev_err_probe(dev, -EINVAL, + "CNV GPIO is not supported\n"); + + indio_dev->num_channels = st->chip_info->num_voltage_inputs + 2; + + ret = devm_iio_triggered_buffer_setup(dev, indio_dev, + iio_pollfunc_store_time, + ad4695_trigger_handler, + &ad4695_buffer_setup_ops); + if (ret) + return ret; + } else { + ret = ad4695_probe_spi_offload(indio_dev, st); + if (ret) + return ret; + } + + return devm_iio_device_register(dev, indio_dev); +} + +static const struct spi_device_id ad4695_spi_id_table[] = { + { .name = "ad4695", .driver_data = (kernel_ulong_t)&ad4695_chip_info }, + { .name = "ad4696", .driver_data = (kernel_ulong_t)&ad4696_chip_info }, + { .name = "ad4697", .driver_data = (kernel_ulong_t)&ad4697_chip_info }, + { .name = "ad4698", .driver_data = (kernel_ulong_t)&ad4698_chip_info }, + { } +}; +MODULE_DEVICE_TABLE(spi, ad4695_spi_id_table); + +static const struct of_device_id ad4695_of_match_table[] = { + { .compatible = "adi,ad4695", .data = &ad4695_chip_info, }, + { .compatible = "adi,ad4696", .data = &ad4696_chip_info, }, + { .compatible = "adi,ad4697", .data = &ad4697_chip_info, }, + { .compatible = "adi,ad4698", .data = &ad4698_chip_info, }, + { } +}; +MODULE_DEVICE_TABLE(of, ad4695_of_match_table); + +static struct spi_driver ad4695_driver = { + .driver = { + .name = "ad4695", + .of_match_table = ad4695_of_match_table, + }, + .probe = ad4695_probe, + .id_table = ad4695_spi_id_table, +}; +module_spi_driver(ad4695_driver); + +MODULE_AUTHOR("Ramona Gradinariu "); +MODULE_AUTHOR("David Lechner "); +MODULE_DESCRIPTION("Analog Devices AD4695 ADC driver"); +MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS("IIO_DMAENGINE_BUFFER"); diff --git a/drivers/iio/adc/ad4851.c b/drivers/iio/adc/ad4851.c new file mode 100644 index 000000000..480d1798f --- /dev/null +++ b/drivers/iio/adc/ad4851.c @@ -0,0 +1,1316 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Analog Devices AD4851 DAS driver + * + * Copyright 2024 Analog Devices Inc. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#define AD4851_REG_INTERFACE_CONFIG_A 0x00 +#define AD4851_REG_INTERFACE_CONFIG_B 0x01 +#define AD4851_REG_PRODUCT_ID_L 0x04 +#define AD4851_REG_PRODUCT_ID_H 0x05 +#define AD4851_REG_DEVICE_CTRL 0x25 +#define AD4851_REG_PACKET 0x26 +#define AD4851_REG_OVERSAMPLE 0x27 + +#define AD4851_REG_CH_CONFIG_BASE 0x2A +#define AD4851_REG_CHX_SOFTSPAN(ch) ((0x12 * (ch)) + AD4851_REG_CH_CONFIG_BASE) +#define AD4851_REG_CHX_OFFSET(ch) (AD4851_REG_CHX_SOFTSPAN(ch) + 0x01) +#define AD4851_REG_CHX_OFFSET_LSB(ch) AD4851_REG_CHX_OFFSET(ch) +#define AD4851_REG_CHX_OFFSET_MID(ch) (AD4851_REG_CHX_OFFSET_LSB(ch) + 0x01) +#define AD4851_REG_CHX_OFFSET_MSB(ch) (AD4851_REG_CHX_OFFSET_MID(ch) + 0x01) +#define AD4851_REG_CHX_GAIN(ch) (AD4851_REG_CHX_OFFSET(ch) + 0x03) +#define AD4851_REG_CHX_GAIN_LSB(ch) AD4851_REG_CHX_GAIN(ch) +#define AD4851_REG_CHX_GAIN_MSB(ch) (AD4851_REG_CHX_GAIN(ch) + 0x01) +#define AD4851_REG_CHX_PHASE(ch) (AD4851_REG_CHX_GAIN(ch) + 0x02) +#define AD4851_REG_CHX_PHASE_LSB(ch) AD4851_REG_CHX_PHASE(ch) +#define AD4851_REG_CHX_PHASE_MSB(ch) (AD4851_REG_CHX_PHASE_LSB(ch) + 0x01) + +#define AD4851_REG_TESTPAT_0(c) (0x38 + (c) * 0x12) +#define AD4851_REG_TESTPAT_1(c) (0x39 + (c) * 0x12) +#define AD4851_REG_TESTPAT_2(c) (0x3A + (c) * 0x12) +#define AD4851_REG_TESTPAT_3(c) (0x3B + (c) * 0x12) + +#define AD4851_SW_RESET (BIT(7) | BIT(0)) +#define AD4851_SDO_ENABLE BIT(4) +#define AD4851_SINGLE_INSTRUCTION BIT(7) +#define AD4851_REFBUF BIT(2) +#define AD4851_REFSEL BIT(1) +#define AD4851_ECHO_CLOCK_MODE BIT(0) + +#define AD4851_PACKET_FORMAT_0 0 +#define AD4851_PACKET_FORMAT_1 1 +#define AD4851_PACKET_FORMAT_MASK GENMASK(1, 0) + +#define AD4851_OS_EN_MSK BIT(7) +#define AD4851_OS_RATIO_MSK GENMASK(3, 0) + +#define AD4851_TEST_PAT BIT(2) + +#define AD4858_PACKET_SIZE_20 0 +#define AD4858_PACKET_SIZE_24 1 +#define AD4858_PACKET_SIZE_32 2 + +#define AD4857_PACKET_SIZE_16 0 +#define AD4857_PACKET_SIZE_24 1 + +#define AD4851_TESTPAT_0_DEFAULT 0x2A +#define AD4851_TESTPAT_1_DEFAULT 0x3C +#define AD4851_TESTPAT_2_DEFAULT 0xCE +#define AD4851_TESTPAT_3_DEFAULT(c) (0x0A + (0x10 * (c))) + +#define AD4851_SOFTSPAN_0V_2V5 0 +#define AD4851_SOFTSPAN_N2V5_2V5 1 +#define AD4851_SOFTSPAN_0V_5V 2 +#define AD4851_SOFTSPAN_N5V_5V 3 +#define AD4851_SOFTSPAN_0V_6V25 4 +#define AD4851_SOFTSPAN_N6V25_6V25 5 +#define AD4851_SOFTSPAN_0V_10V 6 +#define AD4851_SOFTSPAN_N10V_10V 7 +#define AD4851_SOFTSPAN_0V_12V5 8 +#define AD4851_SOFTSPAN_N12V5_12V5 9 +#define AD4851_SOFTSPAN_0V_20V 10 +#define AD4851_SOFTSPAN_N20V_20V 11 +#define AD4851_SOFTSPAN_0V_25V 12 +#define AD4851_SOFTSPAN_N25V_25V 13 +#define AD4851_SOFTSPAN_0V_40V 14 +#define AD4851_SOFTSPAN_N40V_40V 15 + +#define AD4851_MAX_LANES 8 +#define AD4851_MAX_IODELAY 32 + +#define AD4851_T_CNVH_NS 40 +#define AD4851_T_CNVH_NS_MARGIN 10 + +#define AD4841_MAX_SCALE_AVAIL 8 + +#define AD4851_MAX_CH_NR 8 +#define AD4851_CH_START 0 + +struct ad4851_scale { + unsigned int scale_val; + u8 reg_val; +}; + +static const struct ad4851_scale ad4851_scale_table_unipolar[] = { + { 2500, 0x0 }, + { 5000, 0x2 }, + { 6250, 0x4 }, + { 10000, 0x6 }, + { 12500, 0x8 }, + { 20000, 0xA }, + { 25000, 0xC }, + { 40000, 0xE }, +}; + +static const struct ad4851_scale ad4851_scale_table_bipolar[] = { + { 5000, 0x1 }, + { 10000, 0x3 }, + { 12500, 0x5 }, + { 20000, 0x7 }, + { 25000, 0x9 }, + { 40000, 0xB }, + { 50000, 0xD }, + { 80000, 0xF }, +}; + +static const unsigned int ad4851_scale_avail_unipolar[] = { + 2500, + 5000, + 6250, + 10000, + 12500, + 20000, + 25000, + 40000, +}; + +static const unsigned int ad4851_scale_avail_bipolar[] = { + 5000, + 10000, + 12500, + 20000, + 25000, + 40000, + 50000, + 80000, +}; + +struct ad4851_chip_info { + const char *name; + unsigned int product_id; + int num_scales; + unsigned long max_sample_rate_hz; + unsigned int resolution; + unsigned int max_channels; + int (*parse_channels)(struct iio_dev *indio_dev); +}; + +enum { + AD4851_SCAN_TYPE_NORMAL, + AD4851_SCAN_TYPE_RESOLUTION_BOOST, +}; + +struct ad4851_state { + struct spi_device *spi; + struct pwm_device *cnv; + struct iio_backend *back; + /* + * Synchronize access to members the of driver state, and ensure + * atomicity of consecutive regmap operations. + */ + struct mutex lock; + struct regmap *regmap; + const struct ad4851_chip_info *info; + struct gpio_desc *pd_gpio; + bool resolution_boost_enabled; + unsigned long cnv_trigger_rate_hz; + unsigned int osr; + bool vrefbuf_en; + bool vrefio_en; + bool bipolar_ch[AD4851_MAX_CH_NR]; + unsigned int scales_unipolar[AD4841_MAX_SCALE_AVAIL][2]; + unsigned int scales_bipolar[AD4841_MAX_SCALE_AVAIL][2]; +}; + +static int ad4851_reg_access(struct iio_dev *indio_dev, + unsigned int reg, + unsigned int writeval, + unsigned int *readval) +{ + struct ad4851_state *st = iio_priv(indio_dev); + + if (readval) + return regmap_read(st->regmap, reg, readval); + + return regmap_write(st->regmap, reg, writeval); +} + +static int ad4851_set_sampling_freq(struct ad4851_state *st, unsigned int freq) +{ + struct pwm_state cnv_state = { + .duty_cycle = AD4851_T_CNVH_NS + AD4851_T_CNVH_NS_MARGIN, + .enabled = true, + }; + int ret; + + freq = clamp(freq, 1, st->info->max_sample_rate_hz); + + cnv_state.period = DIV_ROUND_UP_ULL(NSEC_PER_SEC, freq); + + ret = pwm_apply_might_sleep(st->cnv, &cnv_state); + if (ret) + return ret; + + st->cnv_trigger_rate_hz = freq; + + return 0; +} + +static const int ad4851_oversampling_ratios[] = { + 1, 2, 4, 8, 16, 32, 64, 128, + 256, 512, 1024, 2048, 4096, 8192, 16384, 32768, + 65536, +}; + +static int ad4851_osr_to_regval(unsigned int ratio) +{ + int i; + + for (i = 1; i < ARRAY_SIZE(ad4851_oversampling_ratios); i++) + if (ratio == ad4851_oversampling_ratios[i]) + return i - 1; + + return -EINVAL; +} + +static int __ad4851_get_scale(struct iio_dev *indio_dev, int scale_tbl, + unsigned int *val, unsigned int *val2) +{ + const struct iio_scan_type *scan_type; + unsigned int tmp; + + scan_type = iio_get_current_scan_type(indio_dev, &indio_dev->channels[0]); + if (IS_ERR(scan_type)) + return PTR_ERR(scan_type); + + tmp = ((u64)scale_tbl * MICRO) >> scan_type->realbits; + *val = tmp / MICRO; + *val2 = tmp % MICRO; + + return 0; +} + +static int ad4851_scale_fill(struct iio_dev *indio_dev) +{ + struct ad4851_state *st = iio_priv(indio_dev); + unsigned int i, val1, val2; + int ret; + + for (i = 0; i < ARRAY_SIZE(ad4851_scale_avail_unipolar); i++) { + ret = __ad4851_get_scale(indio_dev, + ad4851_scale_avail_unipolar[i], + &val1, &val2); + if (ret) + return ret; + + st->scales_unipolar[i][0] = val1; + st->scales_unipolar[i][1] = val2; + } + + for (i = 0; i < ARRAY_SIZE(ad4851_scale_avail_bipolar); i++) { + ret = __ad4851_get_scale(indio_dev, + ad4851_scale_avail_bipolar[i], + &val1, &val2); + if (ret) + return ret; + + st->scales_bipolar[i][0] = val1; + st->scales_bipolar[i][1] = val2; + } + + return 0; +} + +static int ad4851_set_oversampling_ratio(struct iio_dev *indio_dev, + unsigned int osr) +{ + struct ad4851_state *st = iio_priv(indio_dev); + int val, ret; + + guard(mutex)(&st->lock); + + if (osr == 1) { + ret = regmap_clear_bits(st->regmap, AD4851_REG_OVERSAMPLE, + AD4851_OS_EN_MSK); + if (ret) + return ret; + } else { + val = ad4851_osr_to_regval(osr); + if (val < 0) + return -EINVAL; + + ret = regmap_update_bits(st->regmap, AD4851_REG_OVERSAMPLE, + AD4851_OS_EN_MSK | + AD4851_OS_RATIO_MSK, + FIELD_PREP(AD4851_OS_EN_MSK, 1) | + FIELD_PREP(AD4851_OS_RATIO_MSK, val)); + if (ret) + return ret; + } + + /* Channel is ignored by the backend being used here */ + ret = iio_backend_oversampling_ratio_set(st->back, 0, osr); + if (ret) + return ret; + + switch (st->info->resolution) { + case 20: + switch (osr) { + case 0: + return -EINVAL; + case 1: + val = 20; + break; + default: + val = 24; + break; + } + break; + case 16: + val = 16; + break; + default: + return -EINVAL; + } + + ret = iio_backend_data_size_set(st->back, val); + if (ret) + return ret; + + if (osr == 1 || st->info->resolution == 16) { + ret = regmap_clear_bits(st->regmap, AD4851_REG_PACKET, + AD4851_PACKET_FORMAT_MASK); + if (ret) + return ret; + + st->resolution_boost_enabled = false; + } else { + ret = regmap_update_bits(st->regmap, AD4851_REG_PACKET, + AD4851_PACKET_FORMAT_MASK, + FIELD_PREP(AD4851_PACKET_FORMAT_MASK, 1)); + if (ret) + return ret; + + st->resolution_boost_enabled = true; + } + + if (st->osr != osr) { + ret = ad4851_scale_fill(indio_dev); + if (ret) + return ret; + + st->osr = osr; + } + + return 0; +} + +static int ad4851_get_oversampling_ratio(struct ad4851_state *st, unsigned int *val) +{ + unsigned int osr; + int ret; + + guard(mutex)(&st->lock); + + ret = regmap_read(st->regmap, AD4851_REG_OVERSAMPLE, &osr); + if (ret) + return ret; + + if (!FIELD_GET(AD4851_OS_EN_MSK, osr)) + *val = 1; + else + *val = ad4851_oversampling_ratios[FIELD_GET(AD4851_OS_RATIO_MSK, osr) + 1]; + + st->osr = *val; + + return IIO_VAL_INT; +} + +static void ad4851_pwm_disable(void *data) +{ + pwm_disable(data); +} + +static int ad4851_setup(struct ad4851_state *st) +{ + unsigned int product_id; + int ret; + + if (st->pd_gpio) { + /* To initiate a global reset, bring the PD pin high twice */ + gpiod_set_value(st->pd_gpio, 1); + fsleep(1); + gpiod_set_value(st->pd_gpio, 0); + fsleep(1); + gpiod_set_value(st->pd_gpio, 1); + fsleep(1); + gpiod_set_value(st->pd_gpio, 0); + fsleep(1000); + } else { + ret = regmap_set_bits(st->regmap, AD4851_REG_INTERFACE_CONFIG_A, + AD4851_SW_RESET); + if (ret) + return ret; + } + + if (st->vrefbuf_en) { + ret = regmap_set_bits(st->regmap, AD4851_REG_DEVICE_CTRL, + AD4851_REFBUF); + if (ret) + return ret; + } + + if (st->vrefio_en) { + ret = regmap_set_bits(st->regmap, AD4851_REG_DEVICE_CTRL, + AD4851_REFSEL); + if (ret) + return ret; + } + + ret = regmap_write(st->regmap, AD4851_REG_INTERFACE_CONFIG_B, + AD4851_SINGLE_INSTRUCTION); + if (ret) + return ret; + + if (!(st->spi->mode & SPI_3WIRE)) { + ret = regmap_write(st->regmap, AD4851_REG_INTERFACE_CONFIG_A, + AD4851_SDO_ENABLE); + if (ret) + return ret; + } + + ret = regmap_read(st->regmap, AD4851_REG_PRODUCT_ID_L, &product_id); + if (ret) + return ret; + + if (product_id != st->info->product_id) + dev_info(&st->spi->dev, "Unknown product ID: 0x%02X\n", + product_id); + + ret = regmap_set_bits(st->regmap, AD4851_REG_DEVICE_CTRL, + AD4851_ECHO_CLOCK_MODE); + if (ret) + return ret; + + return regmap_write(st->regmap, AD4851_REG_PACKET, 0); +} + +/* + * Find the longest consecutive sequence of false values from field + * and return starting index. + */ +static int ad4851_find_opt(const unsigned long *field, unsigned int start, + unsigned int nbits, unsigned int *val) +{ + unsigned int bit = start, end, start_cnt, cnt = 0; + + for_each_clear_bitrange_from(bit, end, field, start + nbits) { + if (end - bit > cnt) { + cnt = end - bit; + start_cnt = bit - start; + } + } + + if (!cnt) + return -ENOENT; + + *val = start_cnt; + + return cnt; +} + +static int ad4851_calibrate(struct iio_dev *indio_dev) +{ + struct ad4851_state *st = iio_priv(indio_dev); + unsigned int opt_delay, num_lanes, delay, i, s; + enum iio_backend_interface_type interface_type; + DECLARE_BITMAP(pn_status, AD4851_MAX_LANES * AD4851_MAX_IODELAY); + bool status; + int c, ret; + + ret = iio_backend_interface_type_get(st->back, &interface_type); + if (ret) + return ret; + + switch (interface_type) { + case IIO_BACKEND_INTERFACE_SERIAL_CMOS: + num_lanes = indio_dev->num_channels; + break; + case IIO_BACKEND_INTERFACE_SERIAL_LVDS: + num_lanes = 1; + break; + default: + return -EINVAL; + } + + if (st->info->resolution == 16) { + ret = iio_backend_data_size_set(st->back, 24); + if (ret) + return ret; + + ret = regmap_write(st->regmap, AD4851_REG_PACKET, + AD4851_TEST_PAT | AD4857_PACKET_SIZE_24); + if (ret) + return ret; + } else { + ret = iio_backend_data_size_set(st->back, 32); + if (ret) + return ret; + + ret = regmap_write(st->regmap, AD4851_REG_PACKET, + AD4851_TEST_PAT | AD4858_PACKET_SIZE_32); + if (ret) + return ret; + } + + for (i = 0; i < indio_dev->num_channels; i++) { + ret = regmap_write(st->regmap, AD4851_REG_TESTPAT_0(i), + AD4851_TESTPAT_0_DEFAULT); + if (ret) + return ret; + + ret = regmap_write(st->regmap, AD4851_REG_TESTPAT_1(i), + AD4851_TESTPAT_1_DEFAULT); + if (ret) + return ret; + + ret = regmap_write(st->regmap, AD4851_REG_TESTPAT_2(i), + AD4851_TESTPAT_2_DEFAULT); + if (ret) + return ret; + + ret = regmap_write(st->regmap, AD4851_REG_TESTPAT_3(i), + AD4851_TESTPAT_3_DEFAULT(i)); + if (ret) + return ret; + + ret = iio_backend_chan_enable(st->back, + indio_dev->channels[i].channel); + if (ret) + return ret; + } + + for (i = 0; i < num_lanes; i++) { + for (delay = 0; delay < AD4851_MAX_IODELAY; delay++) { + ret = iio_backend_iodelay_set(st->back, i, delay); + if (ret) + return ret; + + ret = iio_backend_chan_status(st->back, i, &status); + if (ret) + return ret; + + __assign_bit(i * AD4851_MAX_IODELAY + delay, pn_status, + status); + } + } + + for (i = 0; i < num_lanes; i++) { + c = ad4851_find_opt(pn_status, i * AD4851_MAX_IODELAY, + AD4851_MAX_IODELAY, &s); + if (c < 0) + return c; + + opt_delay = s + c / 2; + ret = iio_backend_iodelay_set(st->back, i, opt_delay); + if (ret) + return ret; + } + + for (i = 0; i < indio_dev->num_channels; i++) { + ret = iio_backend_chan_disable(st->back, i); + if (ret) + return ret; + } + + ret = iio_backend_data_size_set(st->back, 20); + if (ret) + return ret; + + return regmap_write(st->regmap, AD4851_REG_PACKET, 0); +} + +static int ad4851_get_calibscale(struct ad4851_state *st, int ch, int *val, int *val2) +{ + unsigned int reg_val; + int gain; + int ret; + + guard(mutex)(&st->lock); + + ret = regmap_read(st->regmap, AD4851_REG_CHX_GAIN_MSB(ch), ®_val); + if (ret) + return ret; + + gain = reg_val << 8; + + ret = regmap_read(st->regmap, AD4851_REG_CHX_GAIN_LSB(ch), ®_val); + if (ret) + return ret; + + gain |= reg_val; + + *val = gain; + *val2 = 15; + + return IIO_VAL_FRACTIONAL_LOG2; +} + +static int ad4851_set_calibscale(struct ad4851_state *st, int ch, int val, + int val2) +{ + u64 gain; + u8 buf[2]; + int ret; + + if (val < 0 || val2 < 0) + return -EINVAL; + + gain = val * MICRO + val2; + gain = DIV_U64_ROUND_CLOSEST(gain * 32768, MICRO); + + put_unaligned_be16(gain, buf); + + guard(mutex)(&st->lock); + + ret = regmap_write(st->regmap, AD4851_REG_CHX_GAIN_MSB(ch), buf[0]); + if (ret) + return ret; + + return regmap_write(st->regmap, AD4851_REG_CHX_GAIN_LSB(ch), buf[1]); +} + +static int ad4851_get_calibbias(struct ad4851_state *st, int ch, int *val) +{ + unsigned int lsb, mid, msb; + int ret; + + guard(mutex)(&st->lock); + /* + * After testing, the bulk_write operations doesn't work as expected + * here since the cs needs to be raised after each byte transaction. + */ + ret = regmap_read(st->regmap, AD4851_REG_CHX_OFFSET_MSB(ch), &msb); + if (ret) + return ret; + + ret = regmap_read(st->regmap, AD4851_REG_CHX_OFFSET_MID(ch), &mid); + if (ret) + return ret; + + ret = regmap_read(st->regmap, AD4851_REG_CHX_OFFSET_LSB(ch), &lsb); + if (ret) + return ret; + + if (st->info->resolution == 16) { + *val = msb << 8; + *val |= mid; + *val = sign_extend32(*val, 15); + } else { + *val = msb << 12; + *val |= mid << 4; + *val |= lsb >> 4; + *val = sign_extend32(*val, 19); + } + + return IIO_VAL_INT; +} + +static int ad4851_set_calibbias(struct ad4851_state *st, int ch, int val) +{ + u8 buf[3]; + int ret; + + if (val < 0) + return -EINVAL; + + if (st->info->resolution == 16) + put_unaligned_be16(val, buf); + else + put_unaligned_be24(val << 4, buf); + + guard(mutex)(&st->lock); + /* + * After testing, the bulk_write operations doesn't work as expected + * here since the cs needs to be raised after each byte transaction. + */ + ret = regmap_write(st->regmap, AD4851_REG_CHX_OFFSET_LSB(ch), buf[2]); + if (ret) + return ret; + + ret = regmap_write(st->regmap, AD4851_REG_CHX_OFFSET_MID(ch), buf[1]); + if (ret) + return ret; + + return regmap_write(st->regmap, AD4851_REG_CHX_OFFSET_MSB(ch), buf[0]); +} + +static int ad4851_set_scale(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, int val, int val2) +{ + struct ad4851_state *st = iio_priv(indio_dev); + unsigned int scale_val[2]; + unsigned int i; + const struct ad4851_scale *scale_table; + size_t table_size; + int ret; + + if (st->bipolar_ch[chan->channel]) { + scale_table = ad4851_scale_table_bipolar; + table_size = ARRAY_SIZE(ad4851_scale_table_bipolar); + } else { + scale_table = ad4851_scale_table_unipolar; + table_size = ARRAY_SIZE(ad4851_scale_table_unipolar); + } + + for (i = 0; i < table_size; i++) { + ret = __ad4851_get_scale(indio_dev, scale_table[i].scale_val, + &scale_val[0], &scale_val[1]); + if (ret) + return ret; + + if (scale_val[0] != val || scale_val[1] != val2) + continue; + + return regmap_write(st->regmap, + AD4851_REG_CHX_SOFTSPAN(chan->channel), + scale_table[i].reg_val); + } + + return -EINVAL; +} + +static int ad4851_get_scale(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, int *val, + int *val2) +{ + struct ad4851_state *st = iio_priv(indio_dev); + const struct ad4851_scale *scale_table; + size_t table_size; + u32 softspan_val; + int i, ret; + + if (st->bipolar_ch[chan->channel]) { + scale_table = ad4851_scale_table_bipolar; + table_size = ARRAY_SIZE(ad4851_scale_table_bipolar); + } else { + scale_table = ad4851_scale_table_unipolar; + table_size = ARRAY_SIZE(ad4851_scale_table_unipolar); + } + + ret = regmap_read(st->regmap, AD4851_REG_CHX_SOFTSPAN(chan->channel), + &softspan_val); + if (ret) + return ret; + + for (i = 0; i < table_size; i++) { + if (softspan_val == scale_table[i].reg_val) + break; + } + + if (i == table_size) + return -EIO; + + ret = __ad4851_get_scale(indio_dev, scale_table[i].scale_val, val, + val2); + if (ret) + return ret; + + return IIO_VAL_INT_PLUS_MICRO; +} + +static int ad4851_read_raw(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + int *val, int *val2, long info) +{ + struct ad4851_state *st = iio_priv(indio_dev); + + switch (info) { + case IIO_CHAN_INFO_SAMP_FREQ: + *val = st->cnv_trigger_rate_hz; + *val2 = st->osr; + return IIO_VAL_FRACTIONAL; + case IIO_CHAN_INFO_CALIBSCALE: + return ad4851_get_calibscale(st, chan->channel, val, val2); + case IIO_CHAN_INFO_SCALE: + return ad4851_get_scale(indio_dev, chan, val, val2); + case IIO_CHAN_INFO_CALIBBIAS: + return ad4851_get_calibbias(st, chan->channel, val); + case IIO_CHAN_INFO_OVERSAMPLING_RATIO: + return ad4851_get_oversampling_ratio(st, val); + default: + return -EINVAL; + } +} + +static int ad4851_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, int val2, long info) +{ + struct ad4851_state *st = iio_priv(indio_dev); + + switch (info) { + case IIO_CHAN_INFO_SAMP_FREQ: + if (val < 0 || val2 < 0) + return -EINVAL; + return ad4851_set_sampling_freq(st, val * st->osr + val2 * st->osr / MICRO); + case IIO_CHAN_INFO_SCALE: + return ad4851_set_scale(indio_dev, chan, val, val2); + case IIO_CHAN_INFO_CALIBSCALE: + return ad4851_set_calibscale(st, chan->channel, val, val2); + case IIO_CHAN_INFO_CALIBBIAS: + return ad4851_set_calibbias(st, chan->channel, val); + case IIO_CHAN_INFO_OVERSAMPLING_RATIO: + return ad4851_set_oversampling_ratio(indio_dev, val); + default: + return -EINVAL; + } +} + +static int ad4851_update_scan_mode(struct iio_dev *indio_dev, + const unsigned long *scan_mask) +{ + struct ad4851_state *st = iio_priv(indio_dev); + unsigned int c; + int ret; + + for (c = 0; c < indio_dev->num_channels; c++) { + if (test_bit(c, scan_mask)) + ret = iio_backend_chan_enable(st->back, c); + else + ret = iio_backend_chan_disable(st->back, c); + if (ret) + return ret; + } + + return 0; +} + +static int ad4851_read_avail(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + const int **vals, int *type, int *length, + long mask) +{ + struct ad4851_state *st = iio_priv(indio_dev); + + switch (mask) { + case IIO_CHAN_INFO_SCALE: + if (st->bipolar_ch[chan->channel]) { + *vals = (const int *)st->scales_bipolar; + *type = IIO_VAL_INT_PLUS_MICRO; + /* Values are stored in a 2D matrix */ + *length = ARRAY_SIZE(ad4851_scale_avail_bipolar) * 2; + } else { + *vals = (const int *)st->scales_unipolar; + *type = IIO_VAL_INT_PLUS_MICRO; + /* Values are stored in a 2D matrix */ + *length = ARRAY_SIZE(ad4851_scale_avail_unipolar) * 2; + } + return IIO_AVAIL_LIST; + case IIO_CHAN_INFO_OVERSAMPLING_RATIO: + *vals = ad4851_oversampling_ratios; + *length = ARRAY_SIZE(ad4851_oversampling_ratios); + *type = IIO_VAL_INT; + return IIO_AVAIL_LIST; + default: + return -EINVAL; + } +} + +static const struct iio_scan_type ad4851_scan_type_20_u[] = { + [AD4851_SCAN_TYPE_NORMAL] = { + .sign = 'u', + .realbits = 20, + .storagebits = 32, + }, + [AD4851_SCAN_TYPE_RESOLUTION_BOOST] = { + .sign = 'u', + .realbits = 24, + .storagebits = 32, + }, +}; + +static const struct iio_scan_type ad4851_scan_type_20_b[] = { + [AD4851_SCAN_TYPE_NORMAL] = { + .sign = 's', + .realbits = 20, + .storagebits = 32, + }, + [AD4851_SCAN_TYPE_RESOLUTION_BOOST] = { + .sign = 's', + .realbits = 24, + .storagebits = 32, + }, +}; + +static int ad4851_get_current_scan_type(const struct iio_dev *indio_dev, + const struct iio_chan_spec *chan) +{ + struct ad4851_state *st = iio_priv(indio_dev); + + return st->resolution_boost_enabled ? AD4851_SCAN_TYPE_RESOLUTION_BOOST + : AD4851_SCAN_TYPE_NORMAL; +} + +#define AD4851_IIO_CHANNEL \ + .type = IIO_VOLTAGE, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_CALIBSCALE) | \ + BIT(IIO_CHAN_INFO_CALIBBIAS) | \ + BIT(IIO_CHAN_INFO_SCALE), \ + .info_mask_separate_available = BIT(IIO_CHAN_INFO_SCALE), \ + .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ) | \ + BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \ + .info_mask_shared_by_all_available = \ + BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \ + .indexed = 1 + +/* + * In case of AD4858_IIO_CHANNEL the scan_type is handled dynamically during the + * parse_channels function. + */ +#define AD4858_IIO_CHANNEL \ +{ \ + AD4851_IIO_CHANNEL \ +} + +#define AD4857_IIO_CHANNEL \ +{ \ + AD4851_IIO_CHANNEL, \ + .scan_type = { \ + .sign = 'u', \ + .realbits = 16, \ + .storagebits = 16, \ + }, \ +} + +static int ad4851_parse_channels_common(struct iio_dev *indio_dev, + struct iio_chan_spec **chans, + const struct iio_chan_spec ad4851_chan) +{ + struct ad4851_state *st = iio_priv(indio_dev); + struct device *dev = &st->spi->dev; + struct iio_chan_spec *channels, *chan_start; + unsigned int num_channels, reg; + unsigned int index = 0; + int ret; + + num_channels = device_get_child_node_count(dev); + if (num_channels > AD4851_MAX_CH_NR) + return dev_err_probe(dev, -EINVAL, "Too many channels: %u\n", + num_channels); + + channels = devm_kcalloc(dev, num_channels, sizeof(*channels), GFP_KERNEL); + if (!channels) + return -ENOMEM; + + chan_start = channels; + + device_for_each_child_node_scoped(dev, child) { + ret = fwnode_property_read_u32(child, "reg", ®); + if (ret) + return dev_err_probe(dev, ret, + "Missing channel number\n"); + if (reg >= AD4851_MAX_CH_NR) + return dev_err_probe(dev, -EINVAL, + "Invalid channel number\n"); + *channels = ad4851_chan; + channels->scan_index = index++; + channels->channel = reg; + + if (fwnode_property_present(child, "diff-channels")) { + channels->channel2 = reg + st->info->max_channels; + channels->differential = 1; + } + + st->bipolar_ch[reg] = fwnode_property_read_bool(child, "bipolar"); + + if (st->bipolar_ch[reg]) { + channels->scan_type.sign = 's'; + } else { + ret = regmap_write(st->regmap, AD4851_REG_CHX_SOFTSPAN(reg), + AD4851_SOFTSPAN_0V_40V); + if (ret) + return ret; + } + + channels++; + } + + *chans = chan_start; + + return num_channels; +} + +static int ad4857_parse_channels(struct iio_dev *indio_dev) +{ + struct iio_chan_spec *ad4851_channels; + const struct iio_chan_spec ad4851_chan = AD4857_IIO_CHANNEL; + int ret; + + ret = ad4851_parse_channels_common(indio_dev, &ad4851_channels, + ad4851_chan); + if (ret < 0) + return ret; + + indio_dev->channels = ad4851_channels; + indio_dev->num_channels = ret; + + return 0; +} + +static int ad4858_parse_channels(struct iio_dev *indio_dev) +{ + struct ad4851_state *st = iio_priv(indio_dev); + struct device *dev = &st->spi->dev; + struct iio_chan_spec *ad4851_channels; + const struct iio_chan_spec ad4851_chan = AD4858_IIO_CHANNEL; + int ret, i = 0; + + ret = ad4851_parse_channels_common(indio_dev, &ad4851_channels, + ad4851_chan); + if (ret < 0) + return ret; + + device_for_each_child_node_scoped(dev, child) { + ad4851_channels[i].has_ext_scan_type = 1; + if (fwnode_property_read_bool(child, "bipolar")) { + ad4851_channels[i].ext_scan_type = ad4851_scan_type_20_b; + ad4851_channels[i].num_ext_scan_type = ARRAY_SIZE(ad4851_scan_type_20_b); + } else { + ad4851_channels[i].ext_scan_type = ad4851_scan_type_20_u; + ad4851_channels[i].num_ext_scan_type = ARRAY_SIZE(ad4851_scan_type_20_u); + } + i++; + } + + indio_dev->channels = ad4851_channels; + indio_dev->num_channels = ret; + + return 0; +} + +/* + * parse_channels() function handles the rest of the channel related attributes + * that are usually are stored in the chip info structure. + */ +static const struct ad4851_chip_info ad4851_info = { + .name = "ad4851", + .product_id = 0x67, + .max_sample_rate_hz = 250 * KILO, + .resolution = 16, + .max_channels = AD4851_MAX_CH_NR, + .parse_channels = ad4857_parse_channels, +}; + +static const struct ad4851_chip_info ad4852_info = { + .name = "ad4852", + .product_id = 0x66, + .max_sample_rate_hz = 250 * KILO, + .resolution = 20, + .max_channels = AD4851_MAX_CH_NR, + .parse_channels = ad4858_parse_channels, +}; + +static const struct ad4851_chip_info ad4853_info = { + .name = "ad4853", + .product_id = 0x65, + .max_sample_rate_hz = 1 * MEGA, + .resolution = 16, + .max_channels = AD4851_MAX_CH_NR, + .parse_channels = ad4857_parse_channels, +}; + +static const struct ad4851_chip_info ad4854_info = { + .name = "ad4854", + .product_id = 0x64, + .max_sample_rate_hz = 1 * MEGA, + .resolution = 20, + .max_channels = AD4851_MAX_CH_NR, + .parse_channels = ad4858_parse_channels, +}; + +static const struct ad4851_chip_info ad4855_info = { + .name = "ad4855", + .product_id = 0x63, + .max_sample_rate_hz = 250 * KILO, + .resolution = 16, + .max_channels = AD4851_MAX_CH_NR, + .parse_channels = ad4857_parse_channels, +}; + +static const struct ad4851_chip_info ad4856_info = { + .name = "ad4856", + .product_id = 0x62, + .max_sample_rate_hz = 250 * KILO, + .resolution = 20, + .max_channels = AD4851_MAX_CH_NR, + .parse_channels = ad4858_parse_channels, +}; + +static const struct ad4851_chip_info ad4857_info = { + .name = "ad4857", + .product_id = 0x61, + .max_sample_rate_hz = 1 * MEGA, + .resolution = 16, + .max_channels = AD4851_MAX_CH_NR, + .parse_channels = ad4857_parse_channels, +}; + +static const struct ad4851_chip_info ad4858_info = { + .name = "ad4858", + .product_id = 0x60, + .max_sample_rate_hz = 1 * MEGA, + .resolution = 20, + .max_channels = AD4851_MAX_CH_NR, + .parse_channels = ad4858_parse_channels, +}; + +static const struct ad4851_chip_info ad4858i_info = { + .name = "ad4858i", + .product_id = 0x6F, + .max_sample_rate_hz = 1 * MEGA, + .resolution = 20, + .max_channels = AD4851_MAX_CH_NR, + .parse_channels = ad4858_parse_channels, +}; + +static const struct iio_info ad4851_iio_info = { + .debugfs_reg_access = ad4851_reg_access, + .read_raw = ad4851_read_raw, + .write_raw = ad4851_write_raw, + .update_scan_mode = ad4851_update_scan_mode, + .get_current_scan_type = ad4851_get_current_scan_type, + .read_avail = ad4851_read_avail, +}; + +static const struct regmap_config regmap_config = { + .reg_bits = 16, + .val_bits = 8, + .read_flag_mask = BIT(7), +}; + +static const char * const ad4851_power_supplies[] = { + "vcc", "vdd", "vee", "vio", +}; + +static int ad4851_probe(struct spi_device *spi) +{ + struct iio_dev *indio_dev; + struct device *dev = &spi->dev; + struct ad4851_state *st; + int ret; + + indio_dev = devm_iio_device_alloc(dev, sizeof(*st)); + if (!indio_dev) + return -ENOMEM; + + st = iio_priv(indio_dev); + st->spi = spi; + + ret = devm_mutex_init(dev, &st->lock); + if (ret) + return ret; + + ret = devm_regulator_bulk_get_enable(dev, + ARRAY_SIZE(ad4851_power_supplies), + ad4851_power_supplies); + if (ret) + return dev_err_probe(dev, ret, + "failed to get and enable supplies\n"); + + ret = devm_regulator_get_enable_optional(dev, "vddh"); + if (ret < 0 && ret != -ENODEV) + return dev_err_probe(dev, ret, "failed to enable vddh voltage\n"); + + ret = devm_regulator_get_enable_optional(dev, "vddl"); + if (ret < 0 && ret != -ENODEV) + return dev_err_probe(dev, ret, "failed to enable vddl voltage\n"); + + ret = devm_regulator_get_enable_optional(dev, "vrefbuf"); + if (ret < 0 && ret != -ENODEV) + return dev_err_probe(dev, ret, "failed to enable vrefbuf voltage\n"); + + st->vrefbuf_en = ret != -ENODEV; + + ret = devm_regulator_get_enable_optional(dev, "vrefio"); + if (ret < 0 && ret != -ENODEV) + return dev_err_probe(dev, ret, "failed to enable vrefio voltage\n"); + + st->vrefio_en = ret != -ENODEV; + + st->pd_gpio = devm_gpiod_get_optional(dev, "pd", GPIOD_OUT_LOW); + if (IS_ERR(st->pd_gpio)) + return dev_err_probe(dev, PTR_ERR(st->pd_gpio), + "Error on requesting pd GPIO\n"); + + st->cnv = devm_pwm_get(dev, NULL); + if (IS_ERR(st->cnv)) + return dev_err_probe(dev, PTR_ERR(st->cnv), + "Error on requesting pwm\n"); + + st->info = spi_get_device_match_data(spi); + if (!st->info) + return -ENODEV; + + st->regmap = devm_regmap_init_spi(spi, ®map_config); + if (IS_ERR(st->regmap)) + return PTR_ERR(st->regmap); + + ret = ad4851_set_sampling_freq(st, HZ_PER_MHZ); + if (ret) + return ret; + + ret = devm_add_action_or_reset(&st->spi->dev, ad4851_pwm_disable, + st->cnv); + if (ret) + return ret; + + ret = ad4851_setup(st); + if (ret) + return ret; + + indio_dev->name = st->info->name; + indio_dev->info = &ad4851_iio_info; + indio_dev->modes = INDIO_DIRECT_MODE; + + ret = st->info->parse_channels(indio_dev); + if (ret) + return ret; + + ret = ad4851_scale_fill(indio_dev); + if (ret) + return ret; + + st->back = devm_iio_backend_get(dev, NULL); + if (IS_ERR(st->back)) + return PTR_ERR(st->back); + + ret = devm_iio_backend_request_buffer(dev, st->back, indio_dev); + if (ret) + return ret; + + ret = devm_iio_backend_enable(dev, st->back); + if (ret) + return ret; + + ret = ad4851_calibrate(indio_dev); + if (ret) + return ret; + + return devm_iio_device_register(dev, indio_dev); +} + +static const struct of_device_id ad4851_of_match[] = { + { .compatible = "adi,ad4851", .data = &ad4851_info, }, + { .compatible = "adi,ad4852", .data = &ad4852_info, }, + { .compatible = "adi,ad4853", .data = &ad4853_info, }, + { .compatible = "adi,ad4854", .data = &ad4854_info, }, + { .compatible = "adi,ad4855", .data = &ad4855_info, }, + { .compatible = "adi,ad4856", .data = &ad4856_info, }, + { .compatible = "adi,ad4857", .data = &ad4857_info, }, + { .compatible = "adi,ad4858", .data = &ad4858_info, }, + { .compatible = "adi,ad4858i", .data = &ad4858i_info, }, + { } +}; + +static const struct spi_device_id ad4851_spi_id[] = { + { .name = "ad4851", .driver_data = (kernel_ulong_t)&ad4851_info }, + { .name = "ad4852", .driver_data = (kernel_ulong_t)&ad4852_info }, + { .name = "ad4853", .driver_data = (kernel_ulong_t)&ad4853_info }, + { .name = "ad4854", .driver_data = (kernel_ulong_t)&ad4854_info }, + { .name = "ad4855", .driver_data = (kernel_ulong_t)&ad4855_info }, + { .name = "ad4856", .driver_data = (kernel_ulong_t)&ad4856_info }, + { .name = "ad4857", .driver_data = (kernel_ulong_t)&ad4857_info }, + { .name = "ad4858", .driver_data = (kernel_ulong_t)&ad4858_info }, + { .name = "ad4858i", .driver_data = (kernel_ulong_t)&ad4858i_info }, + { } +}; +MODULE_DEVICE_TABLE(spi, ad4851_spi_id); + +static struct spi_driver ad4851_driver = { + .probe = ad4851_probe, + .driver = { + .name = "ad4851", + .of_match_table = ad4851_of_match, + }, + .id_table = ad4851_spi_id, +}; +module_spi_driver(ad4851_driver); + +MODULE_AUTHOR("Sergiu Cuciurean "); +MODULE_AUTHOR("Dragos Bogdan "); +MODULE_AUTHOR("Antoniu Miclaus "); +MODULE_DESCRIPTION("Analog Devices AD4851 DAS driver"); +MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS("IIO_BACKEND"); diff --git a/drivers/iio/adc/ad7091r-base.c b/drivers/iio/adc/ad7091r-base.c new file mode 100644 index 000000000..647a7852d --- /dev/null +++ b/drivers/iio/adc/ad7091r-base.c @@ -0,0 +1,397 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * AD7091RX Analog to Digital converter driver + * + * Copyright 2014-2019 Analog Devices Inc. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ad7091r-base.h" + +const struct iio_event_spec ad7091r_events[] = { + { + .type = IIO_EV_TYPE_THRESH, + .dir = IIO_EV_DIR_RISING, + .mask_separate = BIT(IIO_EV_INFO_VALUE) | + BIT(IIO_EV_INFO_ENABLE), + }, + { + .type = IIO_EV_TYPE_THRESH, + .dir = IIO_EV_DIR_FALLING, + .mask_separate = BIT(IIO_EV_INFO_VALUE) | + BIT(IIO_EV_INFO_ENABLE), + }, + { + .type = IIO_EV_TYPE_THRESH, + .dir = IIO_EV_DIR_EITHER, + .mask_separate = BIT(IIO_EV_INFO_HYSTERESIS), + }, +}; +EXPORT_SYMBOL_NS_GPL(ad7091r_events, "IIO_AD7091R"); + +static int ad7091r_set_channel(struct ad7091r_state *st, unsigned int channel) +{ + unsigned int dummy; + int ret; + + /* AD7091R_REG_CHANNEL specified which channels to be converted */ + ret = regmap_write(st->map, AD7091R_REG_CHANNEL, + BIT(channel) | (BIT(channel) << 8)); + if (ret) + return ret; + + /* + * There is a latency of one conversion before the channel conversion + * sequence is updated + */ + return regmap_read(st->map, AD7091R_REG_RESULT, &dummy); +} + +static int ad7091r_read_one(struct iio_dev *iio_dev, + unsigned int channel, unsigned int *read_val) +{ + struct ad7091r_state *st = iio_priv(iio_dev); + unsigned int val; + int ret; + + ret = ad7091r_set_channel(st, channel); + if (ret) + return ret; + + ret = regmap_read(st->map, AD7091R_REG_RESULT, &val); + if (ret) + return ret; + + if (st->chip_info->reg_result_chan_id(val) != channel) + return -EIO; + + *read_val = AD7091R_REG_RESULT_CONV_RESULT(val); + + return 0; +} + +static int ad7091r_read_raw(struct iio_dev *iio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long m) +{ + struct ad7091r_state *st = iio_priv(iio_dev); + unsigned int read_val; + int ret; + + guard(mutex)(&st->lock); + + switch (m) { + case IIO_CHAN_INFO_RAW: + if (st->mode != AD7091R_MODE_COMMAND) + return -EBUSY; + + ret = ad7091r_read_one(iio_dev, chan->channel, &read_val); + if (ret) + return ret; + + *val = read_val; + return IIO_VAL_INT; + + case IIO_CHAN_INFO_SCALE: + if (st->vref) { + ret = regulator_get_voltage(st->vref); + if (ret < 0) + return ret; + + *val = ret / 1000; + } else { + *val = st->chip_info->vref_mV; + } + + *val2 = chan->scan_type.realbits; + return IIO_VAL_FRACTIONAL_LOG2; + + default: + return -EINVAL; + } +} + +static int ad7091r_read_event_config(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir) +{ + struct ad7091r_state *st = iio_priv(indio_dev); + int val, ret; + + switch (dir) { + case IIO_EV_DIR_RISING: + ret = regmap_read(st->map, + AD7091R_REG_CH_HIGH_LIMIT(chan->channel), + &val); + if (ret) + return ret; + return val != AD7091R_HIGH_LIMIT; + case IIO_EV_DIR_FALLING: + ret = regmap_read(st->map, + AD7091R_REG_CH_LOW_LIMIT(chan->channel), + &val); + if (ret) + return ret; + return val != AD7091R_LOW_LIMIT; + default: + return -EINVAL; + } +} + +static int ad7091r_write_event_config(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir, + bool state) +{ + struct ad7091r_state *st = iio_priv(indio_dev); + + if (state) { + return regmap_set_bits(st->map, AD7091R_REG_CONF, + AD7091R_REG_CONF_ALERT_EN); + } else { + /* + * Set thresholds either to 0 or to 2^12 - 1 as appropriate to + * prevent alerts and thus disable event generation. + */ + switch (dir) { + case IIO_EV_DIR_RISING: + return regmap_write(st->map, + AD7091R_REG_CH_HIGH_LIMIT(chan->channel), + AD7091R_HIGH_LIMIT); + case IIO_EV_DIR_FALLING: + return regmap_write(st->map, + AD7091R_REG_CH_LOW_LIMIT(chan->channel), + AD7091R_LOW_LIMIT); + default: + return -EINVAL; + } + } +} + +static int ad7091r_read_event_value(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir, + enum iio_event_info info, int *val, int *val2) +{ + struct ad7091r_state *st = iio_priv(indio_dev); + int ret; + + switch (info) { + case IIO_EV_INFO_VALUE: + switch (dir) { + case IIO_EV_DIR_RISING: + ret = regmap_read(st->map, + AD7091R_REG_CH_HIGH_LIMIT(chan->channel), + val); + if (ret) + return ret; + return IIO_VAL_INT; + case IIO_EV_DIR_FALLING: + ret = regmap_read(st->map, + AD7091R_REG_CH_LOW_LIMIT(chan->channel), + val); + if (ret) + return ret; + return IIO_VAL_INT; + default: + return -EINVAL; + } + case IIO_EV_INFO_HYSTERESIS: + ret = regmap_read(st->map, + AD7091R_REG_CH_HYSTERESIS(chan->channel), + val); + if (ret) + return ret; + return IIO_VAL_INT; + default: + return -EINVAL; + } +} + +static int ad7091r_write_event_value(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir, + enum iio_event_info info, int val, int val2) +{ + struct ad7091r_state *st = iio_priv(indio_dev); + + switch (info) { + case IIO_EV_INFO_VALUE: + switch (dir) { + case IIO_EV_DIR_RISING: + return regmap_write(st->map, + AD7091R_REG_CH_HIGH_LIMIT(chan->channel), + val); + case IIO_EV_DIR_FALLING: + return regmap_write(st->map, + AD7091R_REG_CH_LOW_LIMIT(chan->channel), + val); + default: + return -EINVAL; + } + case IIO_EV_INFO_HYSTERESIS: + return regmap_write(st->map, + AD7091R_REG_CH_HYSTERESIS(chan->channel), + val); + default: + return -EINVAL; + } +} + +static const struct iio_info ad7091r_info = { + .read_raw = ad7091r_read_raw, + .read_event_config = &ad7091r_read_event_config, + .write_event_config = &ad7091r_write_event_config, + .read_event_value = &ad7091r_read_event_value, + .write_event_value = &ad7091r_write_event_value, +}; + +static irqreturn_t ad7091r_event_handler(int irq, void *private) +{ + struct iio_dev *iio_dev = private; + struct ad7091r_state *st = iio_priv(iio_dev); + unsigned int i, read_val; + int ret; + s64 timestamp = iio_get_time_ns(iio_dev); + + ret = regmap_read(st->map, AD7091R_REG_ALERT, &read_val); + if (ret) + return IRQ_HANDLED; + + for (i = 0; i < st->chip_info->num_channels; i++) { + if (read_val & BIT(i * 2)) + iio_push_event(iio_dev, + IIO_UNMOD_EVENT_CODE(IIO_VOLTAGE, i, + IIO_EV_TYPE_THRESH, + IIO_EV_DIR_RISING), timestamp); + if (read_val & BIT(i * 2 + 1)) + iio_push_event(iio_dev, + IIO_UNMOD_EVENT_CODE(IIO_VOLTAGE, i, + IIO_EV_TYPE_THRESH, + IIO_EV_DIR_FALLING), timestamp); + } + + return IRQ_HANDLED; +} + +static void ad7091r_remove(void *data) +{ + struct ad7091r_state *st = data; + + regulator_disable(st->vref); +} + +int ad7091r_probe(struct device *dev, const struct ad7091r_init_info *init_info, + int irq) +{ + struct iio_dev *iio_dev; + struct ad7091r_state *st; + int ret; + + iio_dev = devm_iio_device_alloc(dev, sizeof(*st)); + if (!iio_dev) + return -ENOMEM; + + st = iio_priv(iio_dev); + st->dev = dev; + init_info->init_adc_regmap(st, init_info->regmap_config); + if (IS_ERR(st->map)) + return dev_err_probe(st->dev, PTR_ERR(st->map), + "Error initializing regmap\n"); + + iio_dev->info = &ad7091r_info; + iio_dev->modes = INDIO_DIRECT_MODE; + + if (init_info->setup) { + ret = init_info->setup(st); + if (ret < 0) + return ret; + } + + if (irq) { + st->chip_info = init_info->info_irq; + ret = regmap_update_bits(st->map, AD7091R_REG_CONF, + AD7091R_REG_CONF_ALERT_EN, BIT(4)); + if (ret) + return ret; + + ret = devm_request_threaded_irq(dev, irq, NULL, + ad7091r_event_handler, + IRQF_TRIGGER_FALLING | + IRQF_ONESHOT, + st->chip_info->name, iio_dev); + if (ret) + return ret; + } else { + st->chip_info = init_info->info_no_irq; + } + + iio_dev->name = st->chip_info->name; + iio_dev->num_channels = st->chip_info->num_channels; + iio_dev->channels = st->chip_info->channels; + + st->vref = devm_regulator_get_optional(dev, "vref"); + if (IS_ERR(st->vref)) { + if (PTR_ERR(st->vref) == -EPROBE_DEFER) + return -EPROBE_DEFER; + + st->vref = NULL; + /* Enable internal vref */ + ret = regmap_set_bits(st->map, AD7091R_REG_CONF, + AD7091R_REG_CONF_INT_VREF); + if (ret) + return dev_err_probe(st->dev, ret, + "Error on enable internal reference\n"); + } else { + ret = regulator_enable(st->vref); + if (ret) + return ret; + ret = devm_add_action_or_reset(dev, ad7091r_remove, st); + if (ret) + return ret; + } + + /* Use command mode by default to convert only desired channels*/ + ret = st->chip_info->set_mode(st, AD7091R_MODE_COMMAND); + if (ret) + return ret; + + return devm_iio_device_register(dev, iio_dev); +} +EXPORT_SYMBOL_NS_GPL(ad7091r_probe, "IIO_AD7091R"); + +bool ad7091r_writeable_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case AD7091R_REG_RESULT: + case AD7091R_REG_ALERT: + return false; + default: + return true; + } +} +EXPORT_SYMBOL_NS_GPL(ad7091r_writeable_reg, "IIO_AD7091R"); + +bool ad7091r_volatile_reg(struct device *dev, unsigned int reg) +{ + /* The volatile ad7091r registers are also the only RO ones. */ + return !ad7091r_writeable_reg(dev, reg); +} +EXPORT_SYMBOL_NS_GPL(ad7091r_volatile_reg, "IIO_AD7091R"); + +MODULE_AUTHOR("Beniamin Bia "); +MODULE_DESCRIPTION("Analog Devices AD7091Rx multi-channel converters"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/adc/ad7091r-base.h b/drivers/iio/adc/ad7091r-base.h new file mode 100644 index 000000000..092ddea0f --- /dev/null +++ b/drivers/iio/adc/ad7091r-base.h @@ -0,0 +1,99 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * AD7091RX Analog to Digital converter driver + * + * Copyright 2014-2019 Analog Devices Inc. + */ + +#ifndef __DRIVERS_IIO_ADC_AD7091R_BASE_H__ +#define __DRIVERS_IIO_ADC_AD7091R_BASE_H__ + +#include + +#define AD7091R_REG_RESULT 0 +#define AD7091R_REG_CHANNEL 1 +#define AD7091R_REG_CONF 2 +#define AD7091R_REG_ALERT 3 +#define AD7091R_REG_CH_LOW_LIMIT(ch) ((ch) * 3 + 4) +#define AD7091R_REG_CH_HIGH_LIMIT(ch) ((ch) * 3 + 5) +#define AD7091R_REG_CH_HYSTERESIS(ch) ((ch) * 3 + 6) + +/* AD7091R_REG_RESULT */ +#define AD7091R5_REG_RESULT_CH_ID(x) (((x) >> 13) & 0x3) +#define AD7091R8_REG_RESULT_CH_ID(x) (((x) >> 13) & 0x7) +#define AD7091R_REG_RESULT_CONV_RESULT(x) ((x) & 0xfff) + +/* AD7091R_REG_CONF */ +#define AD7091R_REG_CONF_INT_VREF BIT(0) +#define AD7091R_REG_CONF_ALERT_EN BIT(4) +#define AD7091R_REG_CONF_AUTO BIT(8) +#define AD7091R_REG_CONF_CMD BIT(10) + +#define AD7091R_REG_CONF_MODE_MASK \ + (AD7091R_REG_CONF_AUTO | AD7091R_REG_CONF_CMD) + +/* AD7091R_REG_CH_LIMIT */ +#define AD7091R_HIGH_LIMIT 0xFFF +#define AD7091R_LOW_LIMIT 0x0 + +#define AD7091R_CHANNEL(idx, bits, ev, num_ev) { \ + .type = IIO_VOLTAGE, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ + .indexed = 1, \ + .channel = idx, \ + .event_spec = ev, \ + .num_event_specs = num_ev, \ + .scan_type.storagebits = 16, \ + .scan_type.realbits = bits, \ +} + +struct device; +struct gpio_desc; + +enum ad7091r_mode { + AD7091R_MODE_SAMPLE, + AD7091R_MODE_COMMAND, + AD7091R_MODE_AUTOCYCLE, +}; + +struct ad7091r_state { + struct device *dev; + struct regmap *map; + struct gpio_desc *convst_gpio; + struct gpio_desc *reset_gpio; + struct regulator *vref; + const struct ad7091r_chip_info *chip_info; + enum ad7091r_mode mode; + struct mutex lock; /*lock to prevent concurrent reads */ + __be16 tx_buf __aligned(IIO_DMA_MINALIGN); + __be16 rx_buf; +}; + +struct ad7091r_chip_info { + const char *name; + unsigned int num_channels; + const struct iio_chan_spec *channels; + unsigned int vref_mV; + unsigned int (*reg_result_chan_id)(unsigned int val); + int (*set_mode)(struct ad7091r_state *st, enum ad7091r_mode mode); +}; + +struct ad7091r_init_info { + const struct ad7091r_chip_info *info_irq; + const struct ad7091r_chip_info *info_no_irq; + const struct regmap_config *regmap_config; + void (*init_adc_regmap)(struct ad7091r_state *st, + const struct regmap_config *regmap_conf); + int (*setup)(struct ad7091r_state *st); +}; + +extern const struct iio_event_spec ad7091r_events[3]; + +int ad7091r_probe(struct device *dev, const struct ad7091r_init_info *init_info, + int irq); + +bool ad7091r_volatile_reg(struct device *dev, unsigned int reg); +bool ad7091r_writeable_reg(struct device *dev, unsigned int reg); + +#endif /* __DRIVERS_IIO_ADC_AD7091R_BASE_H__ */ diff --git a/drivers/iio/adc/ad7091r5.c b/drivers/iio/adc/ad7091r5.c new file mode 100644 index 000000000..7bf7d538a --- /dev/null +++ b/drivers/iio/adc/ad7091r5.c @@ -0,0 +1,132 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * AD7091R5 Analog to Digital converter driver + * + * Copyright 2014-2019 Analog Devices Inc. + */ + +#include +#include +#include +#include + +#include "ad7091r-base.h" + +static const struct iio_chan_spec ad7091r5_channels_irq[] = { + AD7091R_CHANNEL(0, 12, ad7091r_events, ARRAY_SIZE(ad7091r_events)), + AD7091R_CHANNEL(1, 12, ad7091r_events, ARRAY_SIZE(ad7091r_events)), + AD7091R_CHANNEL(2, 12, ad7091r_events, ARRAY_SIZE(ad7091r_events)), + AD7091R_CHANNEL(3, 12, ad7091r_events, ARRAY_SIZE(ad7091r_events)), +}; + +static const struct iio_chan_spec ad7091r5_channels_noirq[] = { + AD7091R_CHANNEL(0, 12, NULL, 0), + AD7091R_CHANNEL(1, 12, NULL, 0), + AD7091R_CHANNEL(2, 12, NULL, 0), + AD7091R_CHANNEL(3, 12, NULL, 0), +}; + +static int ad7091r5_set_mode(struct ad7091r_state *st, enum ad7091r_mode mode) +{ + int ret, conf; + + switch (mode) { + case AD7091R_MODE_SAMPLE: + conf = 0; + break; + case AD7091R_MODE_COMMAND: + conf = AD7091R_REG_CONF_CMD; + break; + case AD7091R_MODE_AUTOCYCLE: + conf = AD7091R_REG_CONF_AUTO; + break; + default: + return -EINVAL; + } + + ret = regmap_update_bits(st->map, AD7091R_REG_CONF, + AD7091R_REG_CONF_MODE_MASK, conf); + if (ret) + return ret; + + st->mode = mode; + + return 0; +} + +static unsigned int ad7091r5_reg_result_chan_id(unsigned int val) +{ + return AD7091R5_REG_RESULT_CH_ID(val); +} + +static const struct ad7091r_chip_info ad7091r5_chip_info_irq = { + .name = "ad7091r-5", + .channels = ad7091r5_channels_irq, + .num_channels = ARRAY_SIZE(ad7091r5_channels_irq), + .vref_mV = 2500, + .reg_result_chan_id = &ad7091r5_reg_result_chan_id, + .set_mode = &ad7091r5_set_mode, +}; + +static const struct ad7091r_chip_info ad7091r5_chip_info_noirq = { + .name = "ad7091r-5", + .channels = ad7091r5_channels_noirq, + .num_channels = ARRAY_SIZE(ad7091r5_channels_noirq), + .vref_mV = 2500, + .reg_result_chan_id = &ad7091r5_reg_result_chan_id, + .set_mode = &ad7091r5_set_mode, +}; + +static const struct regmap_config ad7091r_regmap_config = { + .reg_bits = 8, + .val_bits = 16, + .writeable_reg = ad7091r_writeable_reg, + .volatile_reg = ad7091r_volatile_reg, +}; + +static void ad7091r5_regmap_init(struct ad7091r_state *st, + const struct regmap_config *regmap_conf) +{ + struct i2c_client *i2c = container_of(st->dev, struct i2c_client, dev); + + st->map = devm_regmap_init_i2c(i2c, regmap_conf); +} + +static const struct ad7091r_init_info ad7091r5_init_info = { + .info_irq = &ad7091r5_chip_info_irq, + .info_no_irq = &ad7091r5_chip_info_noirq, + .regmap_config = &ad7091r_regmap_config, + .init_adc_regmap = &ad7091r5_regmap_init +}; + +static int ad7091r5_i2c_probe(struct i2c_client *i2c) +{ + return ad7091r_probe(&i2c->dev, &ad7091r5_init_info, i2c->irq); +} + +static const struct of_device_id ad7091r5_dt_ids[] = { + { .compatible = "adi,ad7091r5" }, + { } +}; +MODULE_DEVICE_TABLE(of, ad7091r5_dt_ids); + +static const struct i2c_device_id ad7091r5_i2c_ids[] = { + { .name = "ad7091r5" }, + { } +}; +MODULE_DEVICE_TABLE(i2c, ad7091r5_i2c_ids); + +static struct i2c_driver ad7091r5_driver = { + .driver = { + .name = "ad7091r5", + .of_match_table = ad7091r5_dt_ids, + }, + .probe = ad7091r5_i2c_probe, + .id_table = ad7091r5_i2c_ids, +}; +module_i2c_driver(ad7091r5_driver); + +MODULE_AUTHOR("Beniamin Bia "); +MODULE_DESCRIPTION("Analog Devices AD7091R5 multi-channel ADC driver"); +MODULE_LICENSE("GPL v2"); +MODULE_IMPORT_NS("IIO_AD7091R"); diff --git a/drivers/iio/adc/ad7091r8.c b/drivers/iio/adc/ad7091r8.c new file mode 100644 index 000000000..465a8ba01 --- /dev/null +++ b/drivers/iio/adc/ad7091r8.c @@ -0,0 +1,272 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Analog Devices AD7091R8 12-bit SAR ADC driver + * + * Copyright 2023 Analog Devices Inc. + */ + +#include +#include +#include +#include +#include +#include + +#include "ad7091r-base.h" + +#define AD7091R8_REG_ADDR_MSK GENMASK(15, 11) +#define AD7091R8_RD_WR_FLAG_MSK BIT(10) +#define AD7091R8_REG_DATA_MSK GENMASK(9, 0) + +#define AD7091R_SPI_REGMAP_CONFIG(n) { \ + .reg_bits = 8, \ + .val_bits = 16, \ + .volatile_reg = ad7091r_volatile_reg, \ + .writeable_reg = ad7091r_writeable_reg, \ + .max_register = AD7091R_REG_CH_HYSTERESIS(n), \ +} + +static int ad7091r8_set_mode(struct ad7091r_state *st, enum ad7091r_mode mode) +{ + /* AD7091R-2/-4/-8 don't set sample/command/autocycle mode in conf reg */ + st->mode = mode; + return 0; +} + +static unsigned int ad7091r8_reg_result_chan_id(unsigned int val) +{ + return AD7091R8_REG_RESULT_CH_ID(val); +} + +#define AD7091R_SPI_CHIP_INFO(_n, _name) { \ + .name = _name, \ + .channels = ad7091r##_n##_channels, \ + .num_channels = ARRAY_SIZE(ad7091r##_n##_channels), \ + .vref_mV = 2500, \ + .reg_result_chan_id = &ad7091r8_reg_result_chan_id, \ + .set_mode = &ad7091r8_set_mode, \ +} + +#define AD7091R_SPI_CHIP_INFO_IRQ(_n, _name) { \ + .name = _name, \ + .channels = ad7091r##_n##_channels_irq, \ + .num_channels = ARRAY_SIZE(ad7091r##_n##_channels_irq), \ + .vref_mV = 2500, \ + .reg_result_chan_id = &ad7091r8_reg_result_chan_id, \ + .set_mode = &ad7091r8_set_mode, \ +} + +enum ad7091r8_info_ids { + AD7091R2_INFO, + AD7091R4_INFO, + AD7091R4_INFO_IRQ, + AD7091R8_INFO, + AD7091R8_INFO_IRQ, +}; + +static const struct iio_chan_spec ad7091r2_channels[] = { + AD7091R_CHANNEL(0, 12, NULL, 0), + AD7091R_CHANNEL(1, 12, NULL, 0), +}; + +static const struct iio_chan_spec ad7091r4_channels[] = { + AD7091R_CHANNEL(0, 12, NULL, 0), + AD7091R_CHANNEL(1, 12, NULL, 0), + AD7091R_CHANNEL(2, 12, NULL, 0), + AD7091R_CHANNEL(3, 12, NULL, 0), +}; + +static const struct iio_chan_spec ad7091r4_channels_irq[] = { + AD7091R_CHANNEL(0, 12, ad7091r_events, ARRAY_SIZE(ad7091r_events)), + AD7091R_CHANNEL(1, 12, ad7091r_events, ARRAY_SIZE(ad7091r_events)), + AD7091R_CHANNEL(2, 12, ad7091r_events, ARRAY_SIZE(ad7091r_events)), + AD7091R_CHANNEL(3, 12, ad7091r_events, ARRAY_SIZE(ad7091r_events)), +}; + +static const struct iio_chan_spec ad7091r8_channels[] = { + AD7091R_CHANNEL(0, 12, NULL, 0), + AD7091R_CHANNEL(1, 12, NULL, 0), + AD7091R_CHANNEL(2, 12, NULL, 0), + AD7091R_CHANNEL(3, 12, NULL, 0), + AD7091R_CHANNEL(4, 12, NULL, 0), + AD7091R_CHANNEL(5, 12, NULL, 0), + AD7091R_CHANNEL(6, 12, NULL, 0), + AD7091R_CHANNEL(7, 12, NULL, 0), +}; + +static const struct iio_chan_spec ad7091r8_channels_irq[] = { + AD7091R_CHANNEL(0, 12, ad7091r_events, ARRAY_SIZE(ad7091r_events)), + AD7091R_CHANNEL(1, 12, ad7091r_events, ARRAY_SIZE(ad7091r_events)), + AD7091R_CHANNEL(2, 12, ad7091r_events, ARRAY_SIZE(ad7091r_events)), + AD7091R_CHANNEL(3, 12, ad7091r_events, ARRAY_SIZE(ad7091r_events)), + AD7091R_CHANNEL(4, 12, ad7091r_events, ARRAY_SIZE(ad7091r_events)), + AD7091R_CHANNEL(5, 12, ad7091r_events, ARRAY_SIZE(ad7091r_events)), + AD7091R_CHANNEL(6, 12, ad7091r_events, ARRAY_SIZE(ad7091r_events)), + AD7091R_CHANNEL(7, 12, ad7091r_events, ARRAY_SIZE(ad7091r_events)), +}; + +static void ad7091r_pulse_convst(struct ad7091r_state *st) +{ + gpiod_set_value_cansleep(st->convst_gpio, 1); + gpiod_set_value_cansleep(st->convst_gpio, 0); +} + +static int ad7091r_regmap_bus_reg_read(void *context, unsigned int reg, + unsigned int *val) +{ + struct ad7091r_state *st = context; + struct spi_device *spi = container_of(st->dev, struct spi_device, dev); + int ret; + + struct spi_transfer t[] = { + { + .tx_buf = &st->tx_buf, + .len = 2, + .cs_change = 1, + }, { + .rx_buf = &st->rx_buf, + .len = 2, + } + }; + + if (reg == AD7091R_REG_RESULT) + ad7091r_pulse_convst(st); + + st->tx_buf = cpu_to_be16(reg << 11); + + ret = spi_sync_transfer(spi, t, ARRAY_SIZE(t)); + if (ret < 0) + return ret; + + *val = be16_to_cpu(st->rx_buf); + return 0; +} + +static int ad7091r_regmap_bus_reg_write(void *context, unsigned int reg, + unsigned int val) +{ + struct ad7091r_state *st = context; + struct spi_device *spi = container_of(st->dev, struct spi_device, dev); + + /* + * AD7091R-2/-4/-8 protocol (datasheet page 31) is to do a single SPI + * transfer with reg address set in bits B15:B11 and value set in B9:B0. + */ + st->tx_buf = cpu_to_be16(FIELD_PREP(AD7091R8_REG_DATA_MSK, val) | + FIELD_PREP(AD7091R8_RD_WR_FLAG_MSK, 1) | + FIELD_PREP(AD7091R8_REG_ADDR_MSK, reg)); + + return spi_write(spi, &st->tx_buf, 2); +} + +static const struct regmap_bus ad7091r8_regmap_bus = { + .reg_read = ad7091r_regmap_bus_reg_read, + .reg_write = ad7091r_regmap_bus_reg_write, + .reg_format_endian_default = REGMAP_ENDIAN_BIG, + .val_format_endian_default = REGMAP_ENDIAN_BIG, +}; + +static const struct ad7091r_chip_info ad7091r8_infos[] = { + [AD7091R2_INFO] = AD7091R_SPI_CHIP_INFO(2, "ad7091r-2"), + [AD7091R4_INFO] = AD7091R_SPI_CHIP_INFO(4, "ad7091r-4"), + [AD7091R4_INFO_IRQ] = AD7091R_SPI_CHIP_INFO_IRQ(4, "ad7091r-4"), + [AD7091R8_INFO] = AD7091R_SPI_CHIP_INFO(8, "ad7091r-8"), + [AD7091R8_INFO_IRQ] = AD7091R_SPI_CHIP_INFO_IRQ(8, "ad7091r-8") +}; + +static const struct regmap_config ad7091r2_reg_conf = AD7091R_SPI_REGMAP_CONFIG(2); +static const struct regmap_config ad7091r4_reg_conf = AD7091R_SPI_REGMAP_CONFIG(4); +static const struct regmap_config ad7091r8_reg_conf = AD7091R_SPI_REGMAP_CONFIG(8); + +static void ad7091r8_regmap_init(struct ad7091r_state *st, + const struct regmap_config *regmap_conf) +{ + st->map = devm_regmap_init(st->dev, &ad7091r8_regmap_bus, st, + regmap_conf); +} + +static int ad7091r8_gpio_setup(struct ad7091r_state *st) +{ + st->convst_gpio = devm_gpiod_get(st->dev, "convst", GPIOD_OUT_LOW); + if (IS_ERR(st->convst_gpio)) + return dev_err_probe(st->dev, PTR_ERR(st->convst_gpio), + "Error getting convst GPIO\n"); + + st->reset_gpio = devm_gpiod_get_optional(st->dev, "reset", + GPIOD_OUT_HIGH); + if (IS_ERR(st->reset_gpio)) + return dev_err_probe(st->dev, PTR_ERR(st->reset_gpio), + "Error on requesting reset GPIO\n"); + + if (st->reset_gpio) { + fsleep(20); + gpiod_set_value_cansleep(st->reset_gpio, 0); + } + + return 0; +} + +static const struct ad7091r_init_info ad7091r2_init_info = { + .info_no_irq = &ad7091r8_infos[AD7091R2_INFO], + .regmap_config = &ad7091r2_reg_conf, + .init_adc_regmap = &ad7091r8_regmap_init, + .setup = &ad7091r8_gpio_setup +}; + +static const struct ad7091r_init_info ad7091r4_init_info = { + .info_no_irq = &ad7091r8_infos[AD7091R4_INFO], + .info_irq = &ad7091r8_infos[AD7091R4_INFO_IRQ], + .regmap_config = &ad7091r4_reg_conf, + .init_adc_regmap = &ad7091r8_regmap_init, + .setup = &ad7091r8_gpio_setup +}; + +static const struct ad7091r_init_info ad7091r8_init_info = { + .info_no_irq = &ad7091r8_infos[AD7091R8_INFO], + .info_irq = &ad7091r8_infos[AD7091R8_INFO_IRQ], + .regmap_config = &ad7091r8_reg_conf, + .init_adc_regmap = &ad7091r8_regmap_init, + .setup = &ad7091r8_gpio_setup +}; + +static int ad7091r8_spi_probe(struct spi_device *spi) +{ + const struct ad7091r_init_info *init_info; + + init_info = spi_get_device_match_data(spi); + if (!init_info) + return -EINVAL; + + return ad7091r_probe(&spi->dev, init_info, spi->irq); +} + +static const struct of_device_id ad7091r8_of_match[] = { + { .compatible = "adi,ad7091r2", .data = &ad7091r2_init_info }, + { .compatible = "adi,ad7091r4", .data = &ad7091r4_init_info }, + { .compatible = "adi,ad7091r8", .data = &ad7091r8_init_info }, + { } +}; +MODULE_DEVICE_TABLE(of, ad7091r8_of_match); + +static const struct spi_device_id ad7091r8_spi_id[] = { + { .name = "ad7091r2", .driver_data = (kernel_ulong_t)&ad7091r2_init_info }, + { .name = "ad7091r4", .driver_data = (kernel_ulong_t)&ad7091r4_init_info }, + { .name = "ad7091r8", .driver_data = (kernel_ulong_t)&ad7091r8_init_info }, + { } +}; +MODULE_DEVICE_TABLE(spi, ad7091r8_spi_id); + +static struct spi_driver ad7091r8_driver = { + .driver = { + .name = "ad7091r8", + .of_match_table = ad7091r8_of_match, + }, + .probe = ad7091r8_spi_probe, + .id_table = ad7091r8_spi_id, +}; +module_spi_driver(ad7091r8_driver); + +MODULE_AUTHOR("Marcelo Schmitt "); +MODULE_DESCRIPTION("Analog Devices AD7091R8 ADC driver"); +MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS("IIO_AD7091R"); diff --git a/drivers/iio/adc/ad7124.c b/drivers/iio/adc/ad7124.c new file mode 100644 index 000000000..fc5dbc624 --- /dev/null +++ b/drivers/iio/adc/ad7124.c @@ -0,0 +1,1705 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * AD7124 SPI ADC driver + * + * Copyright 2018 Analog Devices Inc. + * Copyright 2025 BayLibre, SAS + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +/* AD7124 registers */ +#define AD7124_COMMS 0x00 +#define AD7124_STATUS 0x00 +#define AD7124_ADC_CONTROL 0x01 +#define AD7124_DATA 0x02 +#define AD7124_IO_CONTROL_1 0x03 +#define AD7124_IO_CONTROL_2 0x04 +#define AD7124_ID 0x05 +#define AD7124_ERROR 0x06 +#define AD7124_ERROR_EN 0x07 +#define AD7124_MCLK_COUNT 0x08 +#define AD7124_CHANNEL(x) (0x09 + (x)) +#define AD7124_CONFIG(x) (0x19 + (x)) +#define AD7124_FILTER(x) (0x21 + (x)) +#define AD7124_OFFSET(x) (0x29 + (x)) +#define AD7124_GAIN(x) (0x31 + (x)) + +/* AD7124_STATUS */ +#define AD7124_STATUS_POR_FLAG BIT(4) + +/* AD7124_ADC_CONTROL */ +#define AD7124_ADC_CONTROL_CLK_SEL GENMASK(1, 0) +#define AD7124_ADC_CONTROL_CLK_SEL_INT 0 +#define AD7124_ADC_CONTROL_CLK_SEL_INT_OUT 1 +#define AD7124_ADC_CONTROL_CLK_SEL_EXT 2 +#define AD7124_ADC_CONTROL_CLK_SEL_EXT_DIV4 3 +#define AD7124_ADC_CONTROL_MODE GENMASK(5, 2) +#define AD7124_ADC_CONTROL_MODE_CONTINUOUS 0 +#define AD7124_ADC_CONTROL_MODE_SINGLE 1 +#define AD7124_ADC_CONTROL_MODE_STANDBY 2 +#define AD7124_ADC_CONTROL_MODE_POWERDOWN 3 +#define AD7124_ADC_CONTROL_MODE_IDLE 4 +#define AD7124_ADC_CONTROL_MODE_INT_OFFSET_CALIB 5 /* Internal Zero-Scale Calibration */ +#define AD7124_ADC_CONTROL_MODE_INT_GAIN_CALIB 6 /* Internal Full-Scale Calibration */ +#define AD7124_ADC_CONTROL_MODE_SYS_OFFSET_CALIB 7 /* System Zero-Scale Calibration */ +#define AD7124_ADC_CONTROL_MODE_SYS_GAIN_CALIB 8 /* System Full-Scale Calibration */ +#define AD7124_ADC_CONTROL_POWER_MODE GENMASK(7, 6) +#define AD7124_ADC_CONTROL_POWER_MODE_LOW 0 +#define AD7124_ADC_CONTROL_POWER_MODE_MID 1 +#define AD7124_ADC_CONTROL_POWER_MODE_FULL 2 +#define AD7124_ADC_CONTROL_REF_EN BIT(8) +#define AD7124_ADC_CONTROL_DATA_STATUS BIT(10) + +/* AD7124_ID */ +#define AD7124_ID_SILICON_REVISION GENMASK(3, 0) +#define AD7124_ID_DEVICE_ID GENMASK(7, 4) +#define AD7124_ID_DEVICE_ID_AD7124_4 0x0 +#define AD7124_ID_DEVICE_ID_AD7124_8 0x1 + +/* AD7124_CHANNEL_X */ +#define AD7124_CHANNEL_ENABLE BIT(15) +#define AD7124_CHANNEL_SETUP GENMASK(14, 12) +#define AD7124_CHANNEL_AINP GENMASK(9, 5) +#define AD7124_CHANNEL_AINM GENMASK(4, 0) +#define AD7124_CHANNEL_AINx_TEMPSENSOR 16 +#define AD7124_CHANNEL_AINx_AVSS 17 + +/* AD7124_CONFIG_X */ +#define AD7124_CONFIG_BIPOLAR BIT(11) +#define AD7124_CONFIG_IN_BUFF GENMASK(6, 5) +#define AD7124_CONFIG_AIN_BUFP BIT(6) +#define AD7124_CONFIG_AIN_BUFM BIT(5) +#define AD7124_CONFIG_REF_SEL GENMASK(4, 3) +#define AD7124_CONFIG_PGA GENMASK(2, 0) + +/* AD7124_FILTER_X */ +#define AD7124_FILTER_FILTER GENMASK(23, 21) +#define AD7124_FILTER_FILTER_SINC4 0 +#define AD7124_FILTER_FILTER_SINC3 2 +#define AD7124_FILTER_FILTER_SINC4_SINC1 4 +#define AD7124_FILTER_FILTER_SINC3_SINC1 5 +#define AD7124_FILTER_FILTER_SINC3_PF 7 +#define AD7124_FILTER_REJ60 BIT(20) +#define AD7124_FILTER_POST_FILTER GENMASK(19, 17) +#define AD7124_FILTER_POST_FILTER_47dB 2 +#define AD7124_FILTER_POST_FILTER_62dB 3 +#define AD7124_FILTER_POST_FILTER_86dB 5 +#define AD7124_FILTER_POST_FILTER_92dB 6 +#define AD7124_FILTER_SINGLE_CYCLE BIT(16) +#define AD7124_FILTER_FS GENMASK(10, 0) + +#define AD7124_CFG_SLOT_UNASSIGNED ~0U + +#define AD7124_MAX_CONFIGS 8 +#define AD7124_MAX_CHANNELS 16 + +#define AD7124_INT_CLK_HZ 614400 + +/* AD7124 input sources */ + +enum ad7124_ref_sel { + AD7124_REFIN1, + AD7124_REFIN2, + AD7124_INT_REF, + AD7124_AVDD_REF, +}; + +enum ad7124_power_mode { + AD7124_LOW_POWER, + AD7124_MID_POWER, + AD7124_FULL_POWER, +}; + +static const unsigned int ad7124_gain[8] = { + 1, 2, 4, 8, 16, 32, 64, 128 +}; + +static const unsigned int ad7124_reg_size[] = { + 1, 2, 3, 3, 2, 1, 3, 3, 1, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3 +}; + +static const int ad7124_master_clk_freq_hz[3] = { + [AD7124_LOW_POWER] = AD7124_INT_CLK_HZ / 8, + [AD7124_MID_POWER] = AD7124_INT_CLK_HZ / 4, + [AD7124_FULL_POWER] = AD7124_INT_CLK_HZ, +}; + +static const char * const ad7124_ref_names[] = { + [AD7124_REFIN1] = "refin1", + [AD7124_REFIN2] = "refin2", + [AD7124_INT_REF] = "int", + [AD7124_AVDD_REF] = "avdd", +}; + +struct ad7124_chip_info { + const char *name; + unsigned int chip_id; + unsigned int num_inputs; +}; + +enum ad7124_filter_type { + AD7124_FILTER_TYPE_SINC3, + AD7124_FILTER_TYPE_SINC3_PF1, + AD7124_FILTER_TYPE_SINC3_PF2, + AD7124_FILTER_TYPE_SINC3_PF3, + AD7124_FILTER_TYPE_SINC3_PF4, + AD7124_FILTER_TYPE_SINC3_REJ60, + AD7124_FILTER_TYPE_SINC3_SINC1, + AD7124_FILTER_TYPE_SINC4, + AD7124_FILTER_TYPE_SINC4_REJ60, + AD7124_FILTER_TYPE_SINC4_SINC1, +}; + +struct ad7124_channel_config { + unsigned int cfg_slot; + unsigned int requested_odr; + unsigned int requested_odr_micro; + /* + * Following fields are used to compare for equality. If you + * make adaptations in it, you most likely also have to adapt + * ad7124_config_equal(), too. + */ + struct_group(config_props, + enum ad7124_ref_sel refsel; + bool bipolar; + bool buf_positive; + bool buf_negative; + unsigned int vref_mv; + unsigned int pga_bits; + unsigned int odr_sel_bits; + enum ad7124_filter_type filter_type; + unsigned int calibration_offset; + unsigned int calibration_gain; + ); +}; + +struct ad7124_channel { + struct ad7124_channel_config cfg; + unsigned int ain; + unsigned int slot; + u8 syscalib_mode; +}; + +struct ad7124_state { + const struct ad7124_chip_info *chip_info; + struct ad_sigma_delta sd; + struct ad7124_channel *channels; + struct regulator *vref[4]; + u32 clk_hz; + unsigned int adc_control; + unsigned int num_channels; + struct mutex cfgs_lock; /* lock for configs access */ + u8 cfg_slot_use_count[AD7124_MAX_CONFIGS]; + + /* + * Stores the power-on reset value for the GAIN(x) registers which are + * needed for measurements at gain 1 (i.e. CONFIG(x).PGA == 0) + */ + unsigned int gain_default; + bool enable_single_cycle; +}; + +static const struct ad7124_chip_info ad7124_4_chip_info = { + .name = "ad7124-4", + .chip_id = AD7124_ID_DEVICE_ID_AD7124_4, + .num_inputs = 8, +}; + +static const struct ad7124_chip_info ad7124_8_chip_info = { + .name = "ad7124-8", + .chip_id = AD7124_ID_DEVICE_ID_AD7124_8, + .num_inputs = 16, +}; + +static int ad7124_find_closest_match(const int *array, + unsigned int size, int val) +{ + int i, idx; + unsigned int diff_new, diff_old; + + diff_old = U32_MAX; + idx = 0; + + for (i = 0; i < size; i++) { + diff_new = abs(val - array[i]); + if (diff_new < diff_old) { + diff_old = diff_new; + idx = i; + } + } + + return idx; +} + +static int ad7124_spi_write_mask(struct ad7124_state *st, + unsigned int addr, + unsigned long mask, + unsigned int val, + unsigned int bytes) +{ + unsigned int readval; + int ret; + + ret = ad_sd_read_reg(&st->sd, addr, bytes, &readval); + if (ret < 0) + return ret; + + readval &= ~mask; + readval |= val; + + return ad_sd_write_reg(&st->sd, addr, bytes, readval); +} + +static int ad7124_set_mode(struct ad_sigma_delta *sd, + enum ad_sigma_delta_mode mode) +{ + struct ad7124_state *st = container_of(sd, struct ad7124_state, sd); + + st->adc_control &= ~AD7124_ADC_CONTROL_MODE; + st->adc_control |= FIELD_PREP(AD7124_ADC_CONTROL_MODE, mode); + + return ad_sd_write_reg(&st->sd, AD7124_ADC_CONTROL, 2, st->adc_control); +} + +static u32 ad7124_get_fclk_hz(struct ad7124_state *st) +{ + enum ad7124_power_mode power_mode; + u32 fclk_hz; + + power_mode = FIELD_GET(AD7124_ADC_CONTROL_POWER_MODE, st->adc_control); + fclk_hz = st->clk_hz; + + switch (power_mode) { + case AD7124_LOW_POWER: + fclk_hz /= 8; + break; + case AD7124_MID_POWER: + fclk_hz /= 4; + break; + default: + break; + } + + return fclk_hz; +} + +static u32 ad7124_get_fs_factor(struct ad7124_state *st, unsigned int channel) +{ + enum ad7124_power_mode power_mode = + FIELD_GET(AD7124_ADC_CONTROL_POWER_MODE, st->adc_control); + u32 avg = power_mode == AD7124_LOW_POWER ? 8 : 16; + + /* + * These are the "zero-latency" factors from the data sheet. For the + * sinc1 filters, these aren't documented, but derived by taking the + * single-channel formula from the sinc1 section of the data sheet and + * multiplying that by the sinc3/4 factor from the corresponding zero- + * latency sections. + */ + switch (st->channels[channel].cfg.filter_type) { + case AD7124_FILTER_TYPE_SINC4: + case AD7124_FILTER_TYPE_SINC4_REJ60: + return 4 * 32; + case AD7124_FILTER_TYPE_SINC4_SINC1: + return 4 * avg * 32; + case AD7124_FILTER_TYPE_SINC3_SINC1: + return 3 * avg * 32; + default: + return 3 * 32; + } +} + +static u32 ad7124_get_fadc_divisor(struct ad7124_state *st, unsigned int channel) +{ + u32 factor = ad7124_get_fs_factor(st, channel); + + /* + * The output data rate (f_ADC) is f_CLK / divisor. We are returning + * the divisor. + */ + return st->channels[channel].cfg.odr_sel_bits * factor; +} + +static void ad7124_set_channel_odr(struct ad7124_state *st, unsigned int channel) +{ + struct ad7124_channel_config *cfg = &st->channels[channel].cfg; + unsigned int fclk, factor, divisor, odr_sel_bits; + + fclk = ad7124_get_fclk_hz(st); + factor = ad7124_get_fs_factor(st, channel); + + /* + * FS[10:0] = fCLK / (fADC x 32 * N) where: + * fADC is the output data rate + * fCLK is the master clock frequency + * N is number of conversions per sample (depends on filter type) + * FS[10:0] are the bits in the filter register + * FS[10:0] can have a value from 1 to 2047 + */ + divisor = cfg->requested_odr * factor + + cfg->requested_odr_micro * factor / MICRO; + odr_sel_bits = clamp(DIV_ROUND_CLOSEST(fclk, divisor), 1, 2047); + + st->channels[channel].cfg.odr_sel_bits = odr_sel_bits; +} + +static int ad7124_get_3db_filter_factor(struct ad7124_state *st, + unsigned int channel) +{ + struct ad7124_channel_config *cfg = &st->channels[channel].cfg; + + /* + * 3dB point is the f_CLK rate times some factor. This functions returns + * the factor times 1000. + */ + switch (cfg->filter_type) { + case AD7124_FILTER_TYPE_SINC3: + case AD7124_FILTER_TYPE_SINC3_REJ60: + case AD7124_FILTER_TYPE_SINC3_SINC1: + return 272; + case AD7124_FILTER_TYPE_SINC4: + case AD7124_FILTER_TYPE_SINC4_REJ60: + case AD7124_FILTER_TYPE_SINC4_SINC1: + return 230; + case AD7124_FILTER_TYPE_SINC3_PF1: + return 633; + case AD7124_FILTER_TYPE_SINC3_PF2: + return 605; + case AD7124_FILTER_TYPE_SINC3_PF3: + return 669; + case AD7124_FILTER_TYPE_SINC3_PF4: + return 759; + default: + return -EINVAL; + } +} + +/* Only called during probe, so dev_err_probe() can be used */ +static int ad7124_init_config_vref(struct ad7124_state *st, struct ad7124_channel_config *cfg) +{ + struct device *dev = &st->sd.spi->dev; + unsigned int refsel = cfg->refsel; + + switch (refsel) { + case AD7124_REFIN1: + case AD7124_REFIN2: + case AD7124_AVDD_REF: + if (IS_ERR(st->vref[refsel])) + return dev_err_probe(dev, PTR_ERR(st->vref[refsel]), + "Error, trying to use external voltage reference without a %s regulator.\n", + ad7124_ref_names[refsel]); + + cfg->vref_mv = regulator_get_voltage(st->vref[refsel]); + /* Conversion from uV to mV */ + cfg->vref_mv /= 1000; + return 0; + case AD7124_INT_REF: + cfg->vref_mv = 2500; + st->adc_control |= AD7124_ADC_CONTROL_REF_EN; + return 0; + default: + return dev_err_probe(dev, -EINVAL, "Invalid reference %d\n", refsel); + } +} + +static bool ad7124_config_equal(struct ad7124_channel_config *a, + struct ad7124_channel_config *b) +{ + return a->refsel == b->refsel && + a->bipolar == b->bipolar && + a->buf_positive == b->buf_positive && + a->buf_negative == b->buf_negative && + a->vref_mv == b->vref_mv && + a->pga_bits == b->pga_bits && + a->odr_sel_bits == b->odr_sel_bits && + a->filter_type == b->filter_type && + a->calibration_offset == b->calibration_offset && + a->calibration_gain == b->calibration_gain; +} + +static int ad7124_write_config(struct ad7124_state *st, struct ad7124_channel_config *cfg, + unsigned int cfg_slot) +{ + unsigned int val, filter; + unsigned int rej60 = 0; + unsigned int post = 0; + int ret; + + ret = ad_sd_write_reg(&st->sd, AD7124_OFFSET(cfg_slot), 3, + cfg->calibration_offset); + if (ret) + return ret; + + ret = ad_sd_write_reg(&st->sd, AD7124_GAIN(cfg_slot), 3, + cfg->calibration_gain); + if (ret) + return ret; + + val = FIELD_PREP(AD7124_CONFIG_BIPOLAR, cfg->bipolar) | + FIELD_PREP(AD7124_CONFIG_REF_SEL, cfg->refsel) | + (cfg->buf_positive ? AD7124_CONFIG_AIN_BUFP : 0) | + (cfg->buf_negative ? AD7124_CONFIG_AIN_BUFM : 0) | + FIELD_PREP(AD7124_CONFIG_PGA, cfg->pga_bits); + + ret = ad_sd_write_reg(&st->sd, AD7124_CONFIG(cfg_slot), 2, val); + if (ret < 0) + return ret; + + switch (cfg->filter_type) { + case AD7124_FILTER_TYPE_SINC3: + filter = AD7124_FILTER_FILTER_SINC3; + break; + case AD7124_FILTER_TYPE_SINC3_PF1: + filter = AD7124_FILTER_FILTER_SINC3_PF; + post = AD7124_FILTER_POST_FILTER_47dB; + break; + case AD7124_FILTER_TYPE_SINC3_PF2: + filter = AD7124_FILTER_FILTER_SINC3_PF; + post = AD7124_FILTER_POST_FILTER_62dB; + break; + case AD7124_FILTER_TYPE_SINC3_PF3: + filter = AD7124_FILTER_FILTER_SINC3_PF; + post = AD7124_FILTER_POST_FILTER_86dB; + break; + case AD7124_FILTER_TYPE_SINC3_PF4: + filter = AD7124_FILTER_FILTER_SINC3_PF; + post = AD7124_FILTER_POST_FILTER_92dB; + break; + case AD7124_FILTER_TYPE_SINC3_REJ60: + filter = AD7124_FILTER_FILTER_SINC3; + rej60 = 1; + break; + case AD7124_FILTER_TYPE_SINC3_SINC1: + filter = AD7124_FILTER_FILTER_SINC3_SINC1; + break; + case AD7124_FILTER_TYPE_SINC4: + filter = AD7124_FILTER_FILTER_SINC4; + break; + case AD7124_FILTER_TYPE_SINC4_REJ60: + filter = AD7124_FILTER_FILTER_SINC4; + rej60 = 1; + break; + case AD7124_FILTER_TYPE_SINC4_SINC1: + filter = AD7124_FILTER_FILTER_SINC4_SINC1; + break; + default: + return -EINVAL; + } + + /* + * NB: AD7124_FILTER_SINGLE_CYCLE is always set so that we get the same + * sampling frequency even when only one channel is enabled in a + * buffered read. If it was not set, the N in ad7124_set_channel_odr() + * would be 1 and we would get a faster sampling frequency than what + * was requested. It may only be disabled through debugfs for testing + * purposes. + */ + return ad_sd_write_reg(&st->sd, AD7124_FILTER(cfg_slot), 3, + FIELD_PREP(AD7124_FILTER_FILTER, filter) | + FIELD_PREP(AD7124_FILTER_REJ60, rej60) | + FIELD_PREP(AD7124_FILTER_POST_FILTER, post) | + FIELD_PREP(AD7124_FILTER_SINGLE_CYCLE, + st->enable_single_cycle) | + FIELD_PREP(AD7124_FILTER_FS, cfg->odr_sel_bits)); +} + +/** + * ad7124_request_config_slot() - Request a config slot for a given config + * @st: Driver instance + * @channel: Channel to request a slot for + * + * Tries to find a matching config already in use, otherwise finds a free + * slot. If this function returns successfully, the use count for the slot is + * increased and the slot number is stored in cfg->cfg_slot. + * + * The slot must be released again with ad7124_release_config_slot() when no + * longer needed. + * + * Returns: 0 if a slot was successfully assigned, -EUSERS if no slot is + * available or other error if SPI communication fails. + */ +static int ad7124_request_config_slot(struct ad7124_state *st, u8 channel) +{ + unsigned int other, slot; + int last_used_slot = -1; + + /* Find another channel with a matching config, if any. */ + for (other = 0; other < st->num_channels; other++) { + if (other == channel) + continue; + + if (st->channels[other].cfg.cfg_slot == AD7124_CFG_SLOT_UNASSIGNED) + continue; + + last_used_slot = max_t(int, last_used_slot, + st->channels[other].cfg.cfg_slot); + + if (!ad7124_config_equal(&st->channels[other].cfg, + &st->channels[channel].cfg)) + continue; + + /* Found a match, re-use that slot. */ + slot = st->channels[other].cfg.cfg_slot; + st->cfg_slot_use_count[slot]++; + st->channels[channel].cfg.cfg_slot = slot; + + return 0; + } + + /* No match, use next free slot. */ + slot = last_used_slot + 1; + if (slot >= AD7124_MAX_CONFIGS) + return -EUSERS; + + st->cfg_slot_use_count[slot]++; + st->channels[channel].cfg.cfg_slot = slot; + + return ad7124_write_config(st, &st->channels[channel].cfg, slot); +} + +static void ad7124_release_config_slot(struct ad7124_state *st, u8 channel) +{ + unsigned int slot; + + /* + * All of these early return conditions can happen at probe when all + * channels are disabled. Otherwise, they should not happen normally. + */ + if (channel >= st->num_channels) + return; + + slot = st->channels[channel].cfg.cfg_slot; + + if (slot == AD7124_CFG_SLOT_UNASSIGNED || + st->cfg_slot_use_count[slot] == 0) + return; + + st->cfg_slot_use_count[slot]--; + st->channels[channel].cfg.cfg_slot = AD7124_CFG_SLOT_UNASSIGNED; +} + +static int ad7124_prepare_read(struct ad7124_state *st, int address) +{ + struct ad7124_channel_config *cfg = &st->channels[address].cfg; + int ret; + + ret = ad7124_request_config_slot(st, address); + if (ret) + return ret; + + /* point channel to the config slot and enable */ + return ad_sd_write_reg(&st->sd, AD7124_CHANNEL(address), 2, + st->channels[address].ain | + FIELD_PREP(AD7124_CHANNEL_SETUP, cfg->cfg_slot) | + AD7124_CHANNEL_ENABLE); +} + +static int ad7124_set_channel(struct ad_sigma_delta *sd, unsigned int channel) +{ + struct ad7124_state *st = container_of(sd, struct ad7124_state, sd); + int ret; + + mutex_lock(&st->cfgs_lock); + ret = ad7124_prepare_read(st, channel); + mutex_unlock(&st->cfgs_lock); + + return ret; +} + +static int ad7124_append_status(struct ad_sigma_delta *sd, bool append) +{ + struct ad7124_state *st = container_of(sd, struct ad7124_state, sd); + unsigned int adc_control = st->adc_control; + int ret; + + if (append) + adc_control |= AD7124_ADC_CONTROL_DATA_STATUS; + else + adc_control &= ~AD7124_ADC_CONTROL_DATA_STATUS; + + ret = ad_sd_write_reg(&st->sd, AD7124_ADC_CONTROL, 2, adc_control); + if (ret < 0) + return ret; + + st->adc_control = adc_control; + + return 0; +} + +static int ad7124_disable_one(struct ad_sigma_delta *sd, unsigned int chan) +{ + struct ad7124_state *st = container_of(sd, struct ad7124_state, sd); + + ad7124_release_config_slot(st, chan); + + /* The relevant thing here is that AD7124_CHANNEL_ENABLE is cleared. */ + return ad_sd_write_reg(&st->sd, AD7124_CHANNEL(chan), 2, 0); +} + +static int ad7124_disable_all(struct ad_sigma_delta *sd) +{ + int ret; + int i; + + for (i = 0; i < AD7124_MAX_CHANNELS; i++) { + ret = ad7124_disable_one(sd, i); + if (ret < 0) + return ret; + } + + return 0; +} + +static const struct ad_sigma_delta_info ad7124_sigma_delta_info = { + .set_channel = ad7124_set_channel, + .append_status = ad7124_append_status, + .disable_all = ad7124_disable_all, + .disable_one = ad7124_disable_one, + .set_mode = ad7124_set_mode, + .has_registers = true, + .addr_shift = 0, + .read_mask = BIT(6), + .status_ch_mask = GENMASK(3, 0), + .data_reg = AD7124_DATA, + .num_slots = 8, + .irq_flags = IRQF_TRIGGER_FALLING, + .num_resetclks = 64, +}; + +static const int ad7124_voltage_scales[][2] = { + { 0, 1164 }, + { 0, 2328 }, + { 0, 4656 }, + { 0, 9313 }, + { 0, 18626 }, + { 0, 37252 }, + { 0, 74505 }, + { 0, 149011 }, + { 0, 298023 }, +}; + +static int ad7124_read_avail(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + const int **vals, int *type, int *length, long info) +{ + switch (info) { + case IIO_CHAN_INFO_SCALE: + *vals = (const int *)ad7124_voltage_scales; + *type = IIO_VAL_INT_PLUS_NANO; + *length = ARRAY_SIZE(ad7124_voltage_scales) * 2; + return IIO_AVAIL_LIST; + default: + return -EINVAL; + } +} + +static int ad7124_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long info) +{ + struct ad7124_state *st = iio_priv(indio_dev); + int idx, ret; + + switch (info) { + case IIO_CHAN_INFO_RAW: + ret = ad_sigma_delta_single_conversion(indio_dev, chan, val); + if (ret < 0) + return ret; + + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + switch (chan->type) { + case IIO_VOLTAGE: + mutex_lock(&st->cfgs_lock); + + idx = st->channels[chan->address].cfg.pga_bits; + *val = st->channels[chan->address].cfg.vref_mv; + if (st->channels[chan->address].cfg.bipolar) + *val2 = chan->scan_type.realbits - 1 + idx; + else + *val2 = chan->scan_type.realbits + idx; + + mutex_unlock(&st->cfgs_lock); + return IIO_VAL_FRACTIONAL_LOG2; + + case IIO_TEMP: + /* + * According to the data sheet + * Temperature (°C) + * = ((Conversion − 0x800000)/13584) − 272.5 + * = (Conversion − 0x800000 - 13584 * 272.5) / 13584 + * = (Conversion − 12090248) / 13584 + * So scale with 1000/13584 to yield °mC. Reduce by 8 to + * 125/1698. + */ + *val = 125; + *val2 = 1698; + return IIO_VAL_FRACTIONAL; + + default: + return -EINVAL; + } + + case IIO_CHAN_INFO_OFFSET: + switch (chan->type) { + case IIO_VOLTAGE: + mutex_lock(&st->cfgs_lock); + if (st->channels[chan->address].cfg.bipolar) + *val = -(1 << (chan->scan_type.realbits - 1)); + else + *val = 0; + + mutex_unlock(&st->cfgs_lock); + return IIO_VAL_INT; + + case IIO_TEMP: + /* see calculation above */ + *val = -12090248; + return IIO_VAL_INT; + + default: + return -EINVAL; + } + + case IIO_CHAN_INFO_SAMP_FREQ: { + struct ad7124_channel_config *cfg = &st->channels[chan->address].cfg; + + guard(mutex)(&st->cfgs_lock); + + switch (cfg->filter_type) { + case AD7124_FILTER_TYPE_SINC3: + case AD7124_FILTER_TYPE_SINC3_REJ60: + case AD7124_FILTER_TYPE_SINC3_SINC1: + case AD7124_FILTER_TYPE_SINC4: + case AD7124_FILTER_TYPE_SINC4_REJ60: + case AD7124_FILTER_TYPE_SINC4_SINC1: + *val = ad7124_get_fclk_hz(st); + *val2 = ad7124_get_fadc_divisor(st, chan->address); + return IIO_VAL_FRACTIONAL; + /* + * Post filters force the chip to a fixed rate. These are the + * single-channel rates from the data sheet divided by 3 for + * the multi-channel case (data sheet doesn't explicitly state + * this but confirmed through testing). + */ + case AD7124_FILTER_TYPE_SINC3_PF1: + *val = 300; + *val2 = 33; + return IIO_VAL_FRACTIONAL; + case AD7124_FILTER_TYPE_SINC3_PF2: + *val = 25; + *val2 = 3; + return IIO_VAL_FRACTIONAL; + case AD7124_FILTER_TYPE_SINC3_PF3: + *val = 20; + *val2 = 3; + return IIO_VAL_FRACTIONAL; + case AD7124_FILTER_TYPE_SINC3_PF4: + *val = 50; + *val2 = 9; + return IIO_VAL_FRACTIONAL; + default: + return -EINVAL; + } + } + case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY: { + guard(mutex)(&st->cfgs_lock); + + ret = ad7124_get_3db_filter_factor(st, chan->address); + if (ret < 0) + return ret; + + /* 3dB point is the f_CLK rate times a fractional value */ + *val = ret * ad7124_get_fclk_hz(st); + *val2 = MILLI * ad7124_get_fadc_divisor(st, chan->address); + return IIO_VAL_FRACTIONAL; + } + default: + return -EINVAL; + } +} + +static int ad7124_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, int val2, long info) +{ + struct ad7124_state *st = iio_priv(indio_dev); + struct ad7124_channel_config *cfg = &st->channels[chan->address].cfg; + unsigned int res, gain, full_scale, vref; + + guard(mutex)(&st->cfgs_lock); + + switch (info) { + case IIO_CHAN_INFO_SAMP_FREQ: + if (val2 < 0 || val < 0 || (val2 == 0 && val == 0)) + return -EINVAL; + + cfg->requested_odr = val; + cfg->requested_odr_micro = val2; + ad7124_set_channel_odr(st, chan->address); + + return 0; + case IIO_CHAN_INFO_SCALE: + if (val != 0) + return -EINVAL; + + if (st->channels[chan->address].cfg.bipolar) + full_scale = 1 << (chan->scan_type.realbits - 1); + else + full_scale = 1 << chan->scan_type.realbits; + + vref = st->channels[chan->address].cfg.vref_mv * 1000000LL; + res = DIV_ROUND_CLOSEST(vref, full_scale); + gain = DIV_ROUND_CLOSEST(res, val2); + res = ad7124_find_closest_match(ad7124_gain, ARRAY_SIZE(ad7124_gain), gain); + + st->channels[chan->address].cfg.pga_bits = res; + return 0; + default: + return -EINVAL; + } +} + +static int ad7124_reg_access(struct iio_dev *indio_dev, + unsigned int reg, + unsigned int writeval, + unsigned int *readval) +{ + struct ad7124_state *st = iio_priv(indio_dev); + int ret; + + if (reg >= ARRAY_SIZE(ad7124_reg_size)) + return -EINVAL; + + if (readval) + ret = ad_sd_read_reg(&st->sd, reg, ad7124_reg_size[reg], + readval); + else + ret = ad_sd_write_reg(&st->sd, reg, ad7124_reg_size[reg], + writeval); + + return ret; +} + +static int ad7124_update_scan_mode(struct iio_dev *indio_dev, + const unsigned long *scan_mask) +{ + struct ad7124_state *st = iio_priv(indio_dev); + bool bit_set; + int ret; + int i; + + guard(mutex)(&st->cfgs_lock); + + for (i = 0; i < st->num_channels; i++) { + bit_set = test_bit(i, scan_mask); + if (bit_set) + ret = ad7124_prepare_read(st, i); + else + ret = ad7124_spi_write_mask(st, AD7124_CHANNEL(i), AD7124_CHANNEL_ENABLE, + 0, 2); + if (ret < 0) + return ret; + } + + return 0; +} + +static const struct iio_info ad7124_info = { + .read_avail = ad7124_read_avail, + .read_raw = ad7124_read_raw, + .write_raw = ad7124_write_raw, + .debugfs_reg_access = &ad7124_reg_access, + .validate_trigger = ad_sd_validate_trigger, + .update_scan_mode = ad7124_update_scan_mode, +}; + +/* Only called during probe, so dev_err_probe() can be used */ +static int ad7124_soft_reset(struct ad7124_state *st) +{ + struct device *dev = &st->sd.spi->dev; + unsigned int readval, timeout; + int ret; + + ret = ad_sd_reset(&st->sd); + if (ret < 0) + return ret; + + fsleep(200); + timeout = 100; + do { + ret = ad_sd_read_reg(&st->sd, AD7124_STATUS, 1, &readval); + if (ret < 0) + return dev_err_probe(dev, ret, "Error reading status register\n"); + + if (!(readval & AD7124_STATUS_POR_FLAG)) + break; + + /* The AD7124 requires typically 2ms to power up and settle */ + usleep_range(100, 2000); + } while (--timeout); + + if (readval & AD7124_STATUS_POR_FLAG) + return dev_err_probe(dev, -EIO, "Soft reset failed\n"); + + ret = ad_sd_read_reg(&st->sd, AD7124_GAIN(0), 3, &st->gain_default); + if (ret < 0) + return dev_err_probe(dev, ret, "Error reading gain register\n"); + + dev_dbg(dev, "Reset value of GAIN register is 0x%x\n", st->gain_default); + + return 0; +} + +static int ad7124_check_chip_id(struct ad7124_state *st) +{ + struct device *dev = &st->sd.spi->dev; + unsigned int readval, chip_id, silicon_rev; + int ret; + + ret = ad_sd_read_reg(&st->sd, AD7124_ID, 1, &readval); + if (ret < 0) + return dev_err_probe(dev, ret, "Failure to read ID register\n"); + + chip_id = FIELD_GET(AD7124_ID_DEVICE_ID, readval); + silicon_rev = FIELD_GET(AD7124_ID_SILICON_REVISION, readval); + + if (chip_id != st->chip_info->chip_id) + return dev_err_probe(dev, -ENODEV, + "Chip ID mismatch: expected %u, got %u\n", + st->chip_info->chip_id, chip_id); + + if (silicon_rev == 0) + return dev_err_probe(dev, -ENODEV, + "Silicon revision empty. Chip may not be present\n"); + + return 0; +} + +enum { + AD7124_SYSCALIB_ZERO_SCALE, + AD7124_SYSCALIB_FULL_SCALE, +}; + +static int ad7124_syscalib_locked(struct ad7124_state *st, const struct iio_chan_spec *chan) +{ + struct device *dev = &st->sd.spi->dev; + struct ad7124_channel *ch = &st->channels[chan->address]; + int ret; + + if (ch->syscalib_mode == AD7124_SYSCALIB_ZERO_SCALE) { + ch->cfg.calibration_offset = 0x800000; + + ret = ad_sd_calibrate(&st->sd, AD7124_ADC_CONTROL_MODE_SYS_OFFSET_CALIB, + chan->address); + if (ret < 0) + return ret; + + /* + * Making the assumption that a single conversion will always + * use configuration slot 0 for the OFFSET/GAIN registers. + */ + ret = ad_sd_read_reg(&st->sd, AD7124_OFFSET(0), 3, + &ch->cfg.calibration_offset); + if (ret < 0) + return ret; + + dev_dbg(dev, "offset for channel %lu after zero-scale calibration: 0x%x\n", + chan->address, ch->cfg.calibration_offset); + } else { + ch->cfg.calibration_gain = st->gain_default; + + ret = ad_sd_calibrate(&st->sd, AD7124_ADC_CONTROL_MODE_SYS_GAIN_CALIB, + chan->address); + if (ret < 0) + return ret; + + ret = ad_sd_read_reg(&st->sd, AD7124_GAIN(0), 3, + &ch->cfg.calibration_gain); + if (ret < 0) + return ret; + + dev_dbg(dev, "gain for channel %lu after full-scale calibration: 0x%x\n", + chan->address, ch->cfg.calibration_gain); + } + + return 0; +} + +static ssize_t ad7124_write_syscalib(struct iio_dev *indio_dev, + uintptr_t private, + const struct iio_chan_spec *chan, + const char *buf, size_t len) +{ + struct ad7124_state *st = iio_priv(indio_dev); + bool sys_calib; + int ret; + + ret = kstrtobool(buf, &sys_calib); + if (ret) + return ret; + + if (!sys_calib) + return len; + + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; + + ret = ad7124_syscalib_locked(st, chan); + + iio_device_release_direct(indio_dev); + + return ret ?: len; +} + +static const char * const ad7124_syscalib_modes[] = { + [AD7124_SYSCALIB_ZERO_SCALE] = "zero_scale", + [AD7124_SYSCALIB_FULL_SCALE] = "full_scale", +}; + +static int ad7124_set_syscalib_mode(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + unsigned int mode) +{ + struct ad7124_state *st = iio_priv(indio_dev); + + st->channels[chan->address].syscalib_mode = mode; + + return 0; +} + +static int ad7124_get_syscalib_mode(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan) +{ + struct ad7124_state *st = iio_priv(indio_dev); + + return st->channels[chan->address].syscalib_mode; +} + +static const struct iio_enum ad7124_syscalib_mode_enum = { + .items = ad7124_syscalib_modes, + .num_items = ARRAY_SIZE(ad7124_syscalib_modes), + .set = ad7124_set_syscalib_mode, + .get = ad7124_get_syscalib_mode +}; + +static const char * const ad7124_filter_types[] = { + [AD7124_FILTER_TYPE_SINC3] = "sinc3", + [AD7124_FILTER_TYPE_SINC3_PF1] = "sinc3+pf1", + [AD7124_FILTER_TYPE_SINC3_PF2] = "sinc3+pf2", + [AD7124_FILTER_TYPE_SINC3_PF3] = "sinc3+pf3", + [AD7124_FILTER_TYPE_SINC3_PF4] = "sinc3+pf4", + [AD7124_FILTER_TYPE_SINC3_REJ60] = "sinc3+rej60", + [AD7124_FILTER_TYPE_SINC3_SINC1] = "sinc3+sinc1", + [AD7124_FILTER_TYPE_SINC4] = "sinc4", + [AD7124_FILTER_TYPE_SINC4_REJ60] = "sinc4+rej60", + [AD7124_FILTER_TYPE_SINC4_SINC1] = "sinc4+sinc1", +}; + +static int ad7124_set_filter_type_attr(struct iio_dev *dev, + const struct iio_chan_spec *chan, + unsigned int value) +{ + struct ad7124_state *st = iio_priv(dev); + struct ad7124_channel_config *cfg = &st->channels[chan->address].cfg; + + guard(mutex)(&st->cfgs_lock); + + cfg->filter_type = value; + ad7124_set_channel_odr(st, chan->address); + + return 0; +} + +static int ad7124_get_filter_type_attr(struct iio_dev *dev, + const struct iio_chan_spec *chan) +{ + struct ad7124_state *st = iio_priv(dev); + + guard(mutex)(&st->cfgs_lock); + + return st->channels[chan->address].cfg.filter_type; +} + +static const struct iio_enum ad7124_filter_type_enum = { + .items = ad7124_filter_types, + .num_items = ARRAY_SIZE(ad7124_filter_types), + .set = ad7124_set_filter_type_attr, + .get = ad7124_get_filter_type_attr, +}; + +static const struct iio_chan_spec_ext_info ad7124_calibsys_ext_info[] = { + { + .name = "sys_calibration", + .write = ad7124_write_syscalib, + .shared = IIO_SEPARATE, + }, + IIO_ENUM("sys_calibration_mode", IIO_SEPARATE, + &ad7124_syscalib_mode_enum), + IIO_ENUM_AVAILABLE("sys_calibration_mode", IIO_SHARED_BY_TYPE, + &ad7124_syscalib_mode_enum), + IIO_ENUM("filter_type", IIO_SEPARATE, &ad7124_filter_type_enum), + IIO_ENUM_AVAILABLE("filter_type", IIO_SHARED_BY_TYPE, + &ad7124_filter_type_enum), + { } +}; + +static const struct iio_chan_spec ad7124_channel_template = { + .type = IIO_VOLTAGE, + .indexed = 1, + .differential = 1, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SCALE) | + BIT(IIO_CHAN_INFO_OFFSET) | + BIT(IIO_CHAN_INFO_SAMP_FREQ) | + BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY), + .info_mask_shared_by_type_available = BIT(IIO_CHAN_INFO_SCALE), + .scan_type = { + .sign = 'u', + .realbits = 24, + .storagebits = 32, + .endianness = IIO_BE, + }, + .ext_info = ad7124_calibsys_ext_info, +}; + +/* + * Input specifiers 8 - 15 are explicitly reserved for ad7124-4 + * while they are fine for ad7124-8. Values above 31 don't fit + * into the register field and so are invalid for sure. + */ +static bool ad7124_valid_input_select(unsigned int ain, const struct ad7124_chip_info *info) +{ + if (ain >= info->num_inputs && ain < 16) + return false; + + return ain <= FIELD_MAX(AD7124_CHANNEL_AINM); +} + +static int ad7124_parse_channel_config(struct iio_dev *indio_dev, + struct device *dev) +{ + struct ad7124_state *st = iio_priv(indio_dev); + struct ad7124_channel_config *cfg; + struct ad7124_channel *channels; + struct iio_chan_spec *chan; + unsigned int ain[2], channel = 0, tmp; + unsigned int num_channels; + int ret; + + num_channels = device_get_child_node_count(dev); + + /* + * The driver assigns each logical channel defined in the device tree + * statically one channel register. So only accept 16 such logical + * channels to not treat CONFIG_0 (i.e. the register following + * CHANNEL_15) as an additional channel register. The driver could be + * improved to lift this limitation. + */ + if (num_channels > AD7124_MAX_CHANNELS) + return dev_err_probe(dev, -EINVAL, "Too many channels defined\n"); + + /* Add one for temperature */ + st->num_channels = min(num_channels + 1, AD7124_MAX_CHANNELS); + + chan = devm_kcalloc(dev, st->num_channels, + sizeof(*chan), GFP_KERNEL); + if (!chan) + return -ENOMEM; + + channels = devm_kcalloc(dev, st->num_channels, sizeof(*channels), + GFP_KERNEL); + if (!channels) + return -ENOMEM; + + indio_dev->channels = chan; + indio_dev->num_channels = st->num_channels; + st->channels = channels; + + device_for_each_child_node_scoped(dev, child) { + ret = fwnode_property_read_u32(child, "reg", &channel); + if (ret) + return dev_err_probe(dev, ret, + "Failed to parse reg property of %pfwP\n", child); + + if (channel >= num_channels) + return dev_err_probe(dev, -EINVAL, + "Channel index >= number of channels in %pfwP\n", child); + + ret = fwnode_property_read_u32_array(child, "diff-channels", + ain, 2); + if (ret) + return dev_err_probe(dev, ret, + "Failed to parse diff-channels property of %pfwP\n", child); + + if (!ad7124_valid_input_select(ain[0], st->chip_info) || + !ad7124_valid_input_select(ain[1], st->chip_info)) + return dev_err_probe(dev, -EINVAL, + "diff-channels property of %pfwP contains invalid data\n", child); + + st->channels[channel].ain = FIELD_PREP(AD7124_CHANNEL_AINP, ain[0]) | + FIELD_PREP(AD7124_CHANNEL_AINM, ain[1]); + + cfg = &st->channels[channel].cfg; + cfg->bipolar = fwnode_property_read_bool(child, "bipolar"); + + ret = fwnode_property_read_u32(child, "adi,reference-select", &tmp); + if (ret) + cfg->refsel = AD7124_INT_REF; + else + cfg->refsel = tmp; + + cfg->buf_positive = + fwnode_property_read_bool(child, "adi,buffered-positive"); + cfg->buf_negative = + fwnode_property_read_bool(child, "adi,buffered-negative"); + + chan[channel] = ad7124_channel_template; + chan[channel].address = channel; + chan[channel].scan_index = channel; + chan[channel].channel = ain[0]; + chan[channel].channel2 = ain[1]; + } + + if (num_channels < AD7124_MAX_CHANNELS) { + st->channels[num_channels] = (struct ad7124_channel) { + .ain = FIELD_PREP(AD7124_CHANNEL_AINP, AD7124_CHANNEL_AINx_TEMPSENSOR) | + FIELD_PREP(AD7124_CHANNEL_AINM, AD7124_CHANNEL_AINx_AVSS), + .cfg = { + .bipolar = true, + }, + }; + + chan[num_channels] = (struct iio_chan_spec) { + .type = IIO_TEMP, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SCALE) | BIT(IIO_CHAN_INFO_OFFSET) | + BIT(IIO_CHAN_INFO_SAMP_FREQ), + .scan_type = { + /* + * You might find it strange that a bipolar + * measurement yields an unsigned value, but + * this matches the device's manual. + */ + .sign = 'u', + .realbits = 24, + .storagebits = 32, + .endianness = IIO_BE, + }, + .address = num_channels, + .scan_index = num_channels, + .ext_info = ad7124_calibsys_ext_info, + }; + } + + return 0; +} + +static int ad7124_setup(struct ad7124_state *st) +{ + struct device *dev = &st->sd.spi->dev; + unsigned int power_mode, clk_sel; + struct clk *mclk; + int i, ret; + + /* + * Always use full power mode for max performance. If needed, the driver + * could be adapted to use a dynamic power mode based on the requested + * output data rate. + */ + power_mode = AD7124_ADC_CONTROL_POWER_MODE_FULL; + + /* + * This "mclk" business is needed for backwards compatibility with old + * devicetrees that specified a fake clock named "mclk" to select the + * power mode. + */ + mclk = devm_clk_get_optional_enabled(dev, "mclk"); + if (IS_ERR(mclk)) + return dev_err_probe(dev, PTR_ERR(mclk), "Failed to get mclk\n"); + + if (mclk) { + unsigned long mclk_hz; + + mclk_hz = clk_get_rate(mclk); + if (!mclk_hz) + return dev_err_probe(dev, -EINVAL, + "Failed to get mclk rate\n"); + + /* + * This logic is a bit backwards, which is why it is only here + * for backwards compatibility. The driver should be able to set + * the power mode as it sees fit and the f_clk/mclk rate should + * be dynamic accordingly. But here, we are selecting a fixed + * power mode based on the given "mclk" rate. + */ + power_mode = ad7124_find_closest_match(ad7124_master_clk_freq_hz, + ARRAY_SIZE(ad7124_master_clk_freq_hz), mclk_hz); + + if (mclk_hz != ad7124_master_clk_freq_hz[power_mode]) { + ret = clk_set_rate(mclk, mclk_hz); + if (ret) + return dev_err_probe(dev, ret, + "Failed to set mclk rate\n"); + } + + clk_sel = AD7124_ADC_CONTROL_CLK_SEL_INT; + st->clk_hz = AD7124_INT_CLK_HZ; + } else if (!device_property_present(dev, "clocks") && + device_property_present(dev, "#clock-cells")) { +#ifdef CONFIG_COMMON_CLK + struct clk_hw *clk_hw; + + const char *name __free(kfree) = kasprintf(GFP_KERNEL, "%pfwP-clk", + dev_fwnode(dev)); + if (!name) + return -ENOMEM; + + clk_hw = devm_clk_hw_register_fixed_rate(dev, name, NULL, 0, + AD7124_INT_CLK_HZ); + if (IS_ERR(clk_hw)) + return dev_err_probe(dev, PTR_ERR(clk_hw), + "Failed to register clock provider\n"); + + ret = devm_of_clk_add_hw_provider(dev, of_clk_hw_simple_get, + clk_hw); + if (ret) + return dev_err_probe(dev, ret, + "Failed to add clock provider\n"); +#endif + + /* + * Treat the clock as always on. This way we don't have to deal + * with someone trying to enable/disable the clock while we are + * reading samples. + */ + clk_sel = AD7124_ADC_CONTROL_CLK_SEL_INT_OUT; + st->clk_hz = AD7124_INT_CLK_HZ; + } else { + struct clk *clk; + + clk = devm_clk_get_optional_enabled(dev, NULL); + if (IS_ERR(clk)) + return dev_err_probe(dev, PTR_ERR(clk), + "Failed to get external clock\n"); + + if (clk) { + unsigned long clk_hz; + + clk_hz = clk_get_rate(clk); + if (!clk_hz) + return dev_err_probe(dev, -EINVAL, + "Failed to get external clock rate\n"); + + /* + * The external clock may be 4x the nominal clock rate, + * in which case the ADC needs to be configured to + * divide it by 4. Using MEGA is a bit arbitrary, but + * the expected clock rates are either 614.4 kHz or + * 2.4576 MHz, so this should work. + */ + if (clk_hz > (1 * HZ_PER_MHZ)) { + clk_sel = AD7124_ADC_CONTROL_CLK_SEL_EXT_DIV4; + st->clk_hz = clk_hz / 4; + } else { + clk_sel = AD7124_ADC_CONTROL_CLK_SEL_EXT; + st->clk_hz = clk_hz; + } + } else { + clk_sel = AD7124_ADC_CONTROL_CLK_SEL_INT; + st->clk_hz = AD7124_INT_CLK_HZ; + } + } + + st->adc_control &= ~AD7124_ADC_CONTROL_CLK_SEL; + st->adc_control |= FIELD_PREP(AD7124_ADC_CONTROL_CLK_SEL, clk_sel); + + st->adc_control &= ~AD7124_ADC_CONTROL_POWER_MODE; + st->adc_control |= FIELD_PREP(AD7124_ADC_CONTROL_POWER_MODE, power_mode); + + st->adc_control &= ~AD7124_ADC_CONTROL_MODE; + st->adc_control |= FIELD_PREP(AD7124_ADC_CONTROL_MODE, AD_SD_MODE_IDLE); + + ret = devm_mutex_init(dev, &st->cfgs_lock); + if (ret) + return ret; + + for (i = 0; i < st->num_channels; i++) { + struct ad7124_channel_config *cfg = &st->channels[i].cfg; + + ret = ad7124_init_config_vref(st, cfg); + if (ret < 0) + return ret; + + cfg->cfg_slot = AD7124_CFG_SLOT_UNASSIGNED; + + /* Default filter type on the ADC after reset. */ + cfg->filter_type = AD7124_FILTER_TYPE_SINC4; + + /* + * 9.38 SPS is the minimum output data rate supported + * regardless of the selected power mode. Round it up to 10 and + * set all channels to this default value. + */ + cfg->requested_odr = 10; + ad7124_set_channel_odr(st, i); + } + + ad7124_disable_all(&st->sd); + + ret = ad_sd_write_reg(&st->sd, AD7124_ADC_CONTROL, 2, st->adc_control); + if (ret < 0) + return dev_err_probe(dev, ret, "Failed to setup CONTROL register\n"); + + return ret; +} + +static int __ad7124_calibrate_all(struct ad7124_state *st, struct iio_dev *indio_dev) +{ + struct device *dev = &st->sd.spi->dev; + int ret, i; + + for (i = 0; i < st->num_channels; i++) { + /* + * For calibration the OFFSET register should hold its reset default + * value. For the GAIN register there is no such requirement but + * for gain 1 it should hold the reset default value, too. So to + * simplify matters use the reset default value for both. + */ + st->channels[i].cfg.calibration_offset = 0x800000; + st->channels[i].cfg.calibration_gain = st->gain_default; + + /* + * Only the main voltage input channels are important enough + * to be automatically calibrated here. For everything else, + * just use the default values set above. + */ + if (indio_dev->channels[i].type != IIO_VOLTAGE) + continue; + + /* + * Full-scale calibration isn't supported at gain 1, so skip in + * that case. Note that untypically full-scale calibration has + * to happen before zero-scale calibration. This only applies to + * the internal calibration. For system calibration it's as + * usual: first zero-scale then full-scale calibration. + */ + if (st->channels[i].cfg.pga_bits > 0) { + ret = ad_sd_calibrate(&st->sd, AD7124_ADC_CONTROL_MODE_INT_GAIN_CALIB, i); + if (ret < 0) + return ret; + + /* + * read out the resulting value of GAIN + * after full-scale calibration because the next + * ad_sd_calibrate() call overwrites this via + * ad_sigma_delta_set_channel() -> ad7124_set_channel() + * -> ad7124_prepare_read(). + */ + ret = ad_sd_read_reg(&st->sd, AD7124_GAIN(0), 3, + &st->channels[i].cfg.calibration_gain); + if (ret < 0) + return ret; + } + + ret = ad_sd_calibrate(&st->sd, AD7124_ADC_CONTROL_MODE_INT_OFFSET_CALIB, i); + if (ret < 0) + return ret; + + /* + * Making the assumption that a single conversion will always + * use configuration slot 0 for the OFFSET/GAIN registers. + */ + ret = ad_sd_read_reg(&st->sd, AD7124_OFFSET(0), 3, + &st->channels[i].cfg.calibration_offset); + if (ret < 0) + return ret; + + dev_dbg(dev, "offset and gain for channel %d = 0x%x + 0x%x\n", i, + st->channels[i].cfg.calibration_offset, + st->channels[i].cfg.calibration_gain); + } + + return 0; +} + +static int ad7124_calibrate_all(struct ad7124_state *st, struct iio_dev *indio_dev) +{ + int ret; + unsigned int adc_control = st->adc_control; + + /* + * Calibration isn't supported at full power, so speed down a bit. + * Setting .adc_control is enough here because the control register is + * written as part of ad_sd_calibrate() -> ad_sigma_delta_set_mode(). + * The resulting calibration is then also valid for high-speed, so just + * restore adc_control afterwards. + */ + if (FIELD_GET(AD7124_ADC_CONTROL_POWER_MODE, adc_control) >= AD7124_FULL_POWER) { + st->adc_control &= ~AD7124_ADC_CONTROL_POWER_MODE; + st->adc_control |= FIELD_PREP(AD7124_ADC_CONTROL_POWER_MODE, AD7124_MID_POWER); + } + + ret = __ad7124_calibrate_all(st, indio_dev); + + st->adc_control = adc_control; + + return ret; +} + +static void ad7124_reg_disable(void *r) +{ + regulator_disable(r); +} + +static void ad7124_debugfs_init(struct iio_dev *indio_dev) +{ + struct dentry *dentry = iio_get_debugfs_dentry(indio_dev); + struct ad7124_state *st = iio_priv(indio_dev); + + if (!IS_ENABLED(CONFIG_DEBUG_FS)) + return; + + debugfs_create_bool("enable_single_cycle", 0644, dentry, + &st->enable_single_cycle); +} + +static int ad7124_probe(struct spi_device *spi) +{ + const struct ad7124_chip_info *info; + struct device *dev = &spi->dev; + struct ad7124_state *st; + struct iio_dev *indio_dev; + int i, ret; + + info = spi_get_device_match_data(spi); + if (!info) + return dev_err_probe(dev, -ENODEV, "Failed to get match data\n"); + + indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st)); + if (!indio_dev) + return -ENOMEM; + + st = iio_priv(indio_dev); + + st->chip_info = info; + + /* Only disabled for debug/testing purposes. */ + st->enable_single_cycle = true; + + indio_dev->name = st->chip_info->name; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->info = &ad7124_info; + + ret = ad_sd_init(&st->sd, indio_dev, spi, &ad7124_sigma_delta_info); + if (ret < 0) + return ret; + + ret = ad7124_parse_channel_config(indio_dev, &spi->dev); + if (ret < 0) + return ret; + + for (i = 0; i < ARRAY_SIZE(st->vref); i++) { + if (i == AD7124_INT_REF) + continue; + + st->vref[i] = devm_regulator_get_optional(&spi->dev, + ad7124_ref_names[i]); + if (PTR_ERR(st->vref[i]) == -ENODEV) + continue; + else if (IS_ERR(st->vref[i])) + return PTR_ERR(st->vref[i]); + + ret = regulator_enable(st->vref[i]); + if (ret) + return dev_err_probe(dev, ret, "Failed to enable regulator #%d\n", i); + + ret = devm_add_action_or_reset(&spi->dev, ad7124_reg_disable, + st->vref[i]); + if (ret) + return ret; + } + + ret = ad7124_soft_reset(st); + if (ret < 0) + return ret; + + ret = ad7124_check_chip_id(st); + if (ret) + return ret; + + ret = ad7124_setup(st); + if (ret < 0) + return ret; + + ret = devm_ad_sd_setup_buffer_and_trigger(&spi->dev, indio_dev); + if (ret < 0) + return dev_err_probe(dev, ret, "Failed to setup triggers\n"); + + ret = ad7124_calibrate_all(st, indio_dev); + if (ret) + return ret; + + ret = devm_iio_device_register(&spi->dev, indio_dev); + if (ret < 0) + return dev_err_probe(dev, ret, "Failed to register iio device\n"); + + ad7124_debugfs_init(indio_dev); + + return 0; +} + +static const struct of_device_id ad7124_of_match[] = { + { .compatible = "adi,ad7124-4", .data = &ad7124_4_chip_info }, + { .compatible = "adi,ad7124-8", .data = &ad7124_8_chip_info }, + { } +}; +MODULE_DEVICE_TABLE(of, ad7124_of_match); + +static const struct spi_device_id ad71124_ids[] = { + { .name = "ad7124-4", .driver_data = (kernel_ulong_t)&ad7124_4_chip_info }, + { .name = "ad7124-8", .driver_data = (kernel_ulong_t)&ad7124_8_chip_info }, + { } +}; +MODULE_DEVICE_TABLE(spi, ad71124_ids); + +static struct spi_driver ad71124_driver = { + .driver = { + .name = "ad7124", + .of_match_table = ad7124_of_match, + }, + .probe = ad7124_probe, + .id_table = ad71124_ids, +}; +module_spi_driver(ad71124_driver); + +MODULE_AUTHOR("Stefan Popa "); +MODULE_DESCRIPTION("Analog Devices AD7124 SPI driver"); +MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS("IIO_AD_SIGMA_DELTA"); diff --git a/drivers/iio/adc/ad7173.c b/drivers/iio/adc/ad7173.c new file mode 100644 index 000000000..eb47175a0 --- /dev/null +++ b/drivers/iio/adc/ad7173.c @@ -0,0 +1,2099 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * AD717x and AD411x family SPI ADC driver + * + * Supported devices: + * AD4111/AD4112/AD4113/AD4114/AD4115/AD4116 + * AD7172-2/AD7172-4/AD7173-8/AD7175-2 + * AD7175-8/AD7176-2/AD7177-2 + * + * Copyright (C) 2015, 2024 Analog Devices, Inc. + * Copyright (C) 2025 BayLibre, SAS + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include + +#define AD7173_REG_COMMS 0x00 +#define AD7173_REG_ADC_MODE 0x01 +#define AD7173_REG_INTERFACE_MODE 0x02 +#define AD7173_REG_CRC 0x03 +#define AD7173_REG_DATA 0x04 +#define AD7173_REG_GPIO 0x06 +#define AD7173_REG_ID 0x07 +#define AD7173_REG_CH(x) (0x10 + (x)) +#define AD7173_REG_SETUP(x) (0x20 + (x)) +#define AD7173_REG_FILTER(x) (0x28 + (x)) +#define AD7173_REG_OFFSET(x) (0x30 + (x)) +#define AD7173_REG_GAIN(x) (0x38 + (x)) + +#define AD7173_RESET_LENGTH BITS_TO_BYTES(64) + +#define AD7173_CH_ENABLE BIT(15) +#define AD7173_CH_SETUP_SEL_MASK GENMASK(14, 12) +#define AD7173_CH_SETUP_AINPOS_MASK GENMASK(9, 5) +#define AD7173_CH_SETUP_AINNEG_MASK GENMASK(4, 0) + +#define AD7173_NO_AINS_PER_CHANNEL 2 +#define AD7173_CH_ADDRESS(pos, neg) \ + (FIELD_PREP(AD7173_CH_SETUP_AINPOS_MASK, pos) | \ + FIELD_PREP(AD7173_CH_SETUP_AINNEG_MASK, neg)) +#define AD7173_AIN_TEMP_POS 17 +#define AD7173_AIN_TEMP_NEG 18 +#define AD7173_AIN_POW_MON_POS 19 +#define AD7173_AIN_POW_MON_NEG 20 +#define AD7173_AIN_REF_POS 21 +#define AD7173_AIN_REF_NEG 22 + +#define AD7173_IS_REF_INPUT(x) ((x) == AD7173_AIN_REF_POS || \ + (x) == AD7173_AIN_REF_NEG) + +#define AD7172_2_ID 0x00d0 +#define AD7176_ID 0x0c90 +#define AD7175_ID 0x0cd0 +#define AD7175_2_ID 0x0cd0 +#define AD7172_4_ID 0x2050 +#define AD7173_ID 0x30d0 +#define AD4111_ID AD7173_ID +#define AD4112_ID AD7173_ID +#define AD4114_ID AD7173_ID +#define AD4113_ID 0x31d0 +#define AD4116_ID 0x34d0 +#define AD4115_ID 0x38d0 +#define AD7175_8_ID 0x3cd0 +#define AD7177_ID 0x4fd0 +#define AD7173_ID_MASK GENMASK(15, 4) + +#define AD7173_ADC_MODE_REF_EN BIT(15) +#define AD7173_ADC_MODE_SING_CYC BIT(13) +#define AD7173_ADC_MODE_MODE_MASK GENMASK(6, 4) +#define AD7173_ADC_MODE_CLOCKSEL_MASK GENMASK(3, 2) +#define AD7173_ADC_MODE_CLOCKSEL_INT 0x0 +#define AD7173_ADC_MODE_CLOCKSEL_INT_OUTPUT 0x1 +#define AD7173_ADC_MODE_CLOCKSEL_EXT 0x2 +#define AD7173_ADC_MODE_CLOCKSEL_XTAL 0x3 + +#define AD7173_GPIO_PDSW BIT(14) +#define AD7173_GPIO_OP_EN2_3 BIT(13) +#define AD4111_GPIO_GP_OW_EN BIT(12) +#define AD7173_GPIO_MUX_IO BIT(12) +#define AD7173_GPIO_SYNC_EN BIT(11) +#define AD7173_GPIO_ERR_EN BIT(10) +#define AD7173_GPIO_ERR_DAT BIT(9) +#define AD7173_GPIO_GP_DATA3 BIT(7) +#define AD7173_GPIO_GP_DATA2 BIT(6) +#define AD7173_GPIO_IP_EN1 BIT(5) +#define AD7173_GPIO_IP_EN0 BIT(4) +#define AD7173_GPIO_OP_EN1 BIT(3) +#define AD7173_GPIO_OP_EN0 BIT(2) +#define AD7173_GPIO_GP_DATA1 BIT(1) +#define AD7173_GPIO_GP_DATA0 BIT(0) + +#define AD7173_GPO12_DATA(x) BIT((x) + 0) +#define AD7173_GPO23_DATA(x) BIT((x) + 4) +#define AD4111_GPO01_DATA(x) BIT((x) + 6) +#define AD7173_GPO_DATA(x) ((x) < 2 ? AD7173_GPO12_DATA(x) : AD7173_GPO23_DATA(x)) + +#define AD7173_INTERFACE_DATA_STAT BIT(6) +#define AD7173_INTERFACE_DATA_STAT_EN(x) \ + FIELD_PREP(AD7173_INTERFACE_DATA_STAT, x) + +#define AD7173_SETUP_BIPOLAR BIT(12) +#define AD7173_SETUP_AREF_BUF_MASK GENMASK(11, 10) +#define AD7173_SETUP_AIN_BUF_MASK GENMASK(9, 8) + +#define AD7173_SETUP_REF_SEL_MASK GENMASK(5, 4) +#define AD7173_SETUP_REF_SEL_AVDD1_AVSS 0x3 +#define AD7173_SETUP_REF_SEL_INT_REF 0x2 +#define AD7173_SETUP_REF_SEL_EXT_REF2 0x1 +#define AD7173_SETUP_REF_SEL_EXT_REF 0x0 +#define AD7173_VOLTAGE_INT_REF_uV 2500000 +#define AD7173_TEMP_SENSIIVITY_uV_per_C 477 +#define AD7177_ODR_START_VALUE 0x07 +#define AD4111_SHUNT_RESISTOR_OHM 50 +#define AD4111_DIVIDER_RATIO 10 +#define AD4111_CURRENT_CHAN_CUTOFF 16 +#define AD4111_VINCOM_INPUT 0x10 + +/* pin < num_voltage_in is a normal voltage input */ +/* pin >= num_voltage_in_div is a voltage input without a divider */ +#define AD4111_IS_VINCOM_MISMATCH(pin1, pin2) ((pin1) == AD4111_VINCOM_INPUT && \ + (pin2) < st->info->num_voltage_in && \ + (pin2) >= st->info->num_voltage_in_div) + +#define AD7173_FILTER_SINC3_MAP BIT(15) +#define AD7173_FILTER_SINC3_MAP_DIV GENMASK(14, 0) +#define AD7173_FILTER_ENHFILTEN BIT(11) +#define AD7173_FILTER_ENHFILT_MASK GENMASK(10, 8) +#define AD7173_FILTER_ORDER BIT(6) +#define AD7173_FILTER_ODR_MASK GENMASK(5, 0) +#define AD7173_MAX_CONFIGS 8 +#define AD4111_OW_DET_THRSH_MV 300 + +#define AD7173_MODE_CAL_INT_ZERO 0x4 /* Internal Zero-Scale Calibration */ +#define AD7173_MODE_CAL_INT_FULL 0x5 /* Internal Full-Scale Calibration */ +#define AD7173_MODE_CAL_SYS_ZERO 0x6 /* System Zero-Scale Calibration */ +#define AD7173_MODE_CAL_SYS_FULL 0x7 /* System Full-Scale Calibration */ + +struct ad7173_device_info { + const unsigned int *sinc5_data_rates; + unsigned int num_sinc5_data_rates; + unsigned int odr_start_value; + /* + * AD4116 has both inputs with a voltage divider and without. + * These inputs cannot be mixed in the channel configuration. + * Does not include the VINCOM input. + */ + unsigned int num_voltage_in_div; + unsigned int num_channels; + unsigned int num_configs; + unsigned int num_voltage_in; + unsigned int clock; + unsigned int id; + char *name; + const struct ad_sigma_delta_info *sd_info; + bool has_current_inputs; + bool has_vincom_input; + bool has_temp; + /* ((AVDD1 − AVSS)/5) */ + bool has_pow_supply_monitoring; + bool data_reg_only_16bit; + bool has_input_buf; + bool has_int_ref; + bool has_ref2; + bool has_internal_fs_calibration; + bool has_openwire_det; + bool higher_gpio_bits; + u8 num_gpios; +}; + +enum ad7173_filter_type { + AD7173_FILTER_SINC3, + AD7173_FILTER_SINC5_SINC1, + AD7173_FILTER_SINC5_SINC1_PF1, + AD7173_FILTER_SINC5_SINC1_PF2, + AD7173_FILTER_SINC5_SINC1_PF3, + AD7173_FILTER_SINC5_SINC1_PF4, +}; + +struct ad7173_channel_config { + /* Openwire detection threshold */ + unsigned int openwire_thrsh_raw; + int openwire_comp_chan; + u8 cfg_slot; + bool live; + + /* + * Following fields are used to compare equality. If you + * make adaptations in it, you most likely also have to adapt + * ad7173_is_setup_equal(), too. + */ + struct_group(config_props, + bool bipolar; + bool input_buf; + u16 sinc3_odr_div; + u8 sinc5_odr_index; + u8 ref_sel; + enum ad7173_filter_type filter_type; + ); +}; + +struct ad7173_channel { + unsigned int ain; + struct ad7173_channel_config cfg; + u8 syscalib_mode; + bool openwire_det_en; +}; + +struct ad7173_state { + struct ad_sigma_delta sd; + const struct ad7173_device_info *info; + struct ad7173_channel *channels; + struct regulator_bulk_data regulators[3]; + unsigned int adc_mode; + unsigned int interface_mode; + unsigned int num_channels; + struct ida cfg_slots_status; + unsigned long long config_usage_counter; + unsigned long long *config_cnts; + struct clk_hw int_clk_hw; + struct regmap *reg_gpiocon_regmap; + struct gpio_regmap *gpio_regmap; +}; + +static unsigned int ad4115_sinc5_data_rates[] = { + 24845000, 24845000, 20725000, 20725000, /* 0-3 */ + 15564000, 13841000, 10390000, 10390000, /* 4-7 */ + 4994000, 2499000, 1000000, 500000, /* 8-11 */ + 395500, 200000, 100000, 59890, /* 12-15 */ + 49920, 20000, 16660, 10000, /* 16-19 */ + 5000, 2500, 2500, /* 20-22 */ +}; + +static unsigned int ad4116_sinc5_data_rates[] = { + 12422360, 12422360, 12422360, 12422360, /* 0-3 */ + 10362690, 10362690, 7782100, 6290530, /* 4-7 */ + 5194800, 2496900, 1007600, 499900, /* 8-11 */ + 390600, 200300, 100000, 59750, /* 12-15 */ + 49840, 20000, 16650, 10000, /* 16-19 */ + 5000, 2500, 1250, /* 20-22 */ +}; + +static const unsigned int ad7173_sinc5_data_rates[] = { + 6211000, 6211000, 6211000, 6211000, 6211000, 6211000, 5181000, 4444000, /* 0-7 */ + 3115000, 2597000, 1007000, 503800, 381000, 200300, 100500, 59520, /* 8-15 */ + 49680, 20010, 16333, 10000, 5000, 2500, 1250, /* 16-22 */ +}; + +static const unsigned int ad7175_sinc5_data_rates[] = { + 50000000, 41667000, 31250000, 27778000, /* 0-3 */ + 20833000, 17857000, 12500000, 10000000, /* 4-7 */ + 5000000, 2500000, 1000000, 500000, /* 8-11 */ + 397500, 200000, 100000, 59920, /* 12-15 */ + 49960, 20000, 16666, 10000, /* 16-19 */ + 5000, /* 20 */ +}; + +/** + * ad7173_sinc3_odr_div_from_odr() - Convert ODR to divider value + * @odr_millihz: ODR (sampling_frequency) in milliHz + * Returns: Divider value for SINC3 filter to pass. + */ +static u16 ad7173_sinc3_odr_div_from_odr(u32 odr_millihz) +{ + /* + * Divider is f_MOD (1 MHz) / 32 / ODR. ODR freq is in milliHz, so + * we need to convert f_MOD to the same units. When SING_CYC=1 or + * multiple channels are enabled (currently always the case), there + * is an additional factor of 3. + */ + u32 div = DIV_ROUND_CLOSEST(MEGA * MILLI, odr_millihz * 32 * 3); + /* Avoid divide by 0 and limit to register field size. */ + return clamp(div, 1U, AD7173_FILTER_SINC3_MAP_DIV); +} + +static unsigned int ad4111_current_channel_config[] = { + /* Ain sel: pos neg */ + 0x1E8, /* 15:IIN0+ 8:IIN0− */ + 0x1C9, /* 14:IIN1+ 9:IIN1− */ + 0x1AA, /* 13:IIN2+ 10:IIN2− */ + 0x18B, /* 12:IIN3+ 11:IIN3− */ +}; + +static const char *const ad7173_ref_sel_str[] = { + [AD7173_SETUP_REF_SEL_EXT_REF] = "vref", + [AD7173_SETUP_REF_SEL_EXT_REF2] = "vref2", + [AD7173_SETUP_REF_SEL_INT_REF] = "refout-avss", + [AD7173_SETUP_REF_SEL_AVDD1_AVSS] = "avdd", +}; + +static const char *const ad7173_clk_sel[] = { + "ext-clk", "xtal" +}; + +static const struct regmap_range ad7173_range_gpio[] = { + regmap_reg_range(AD7173_REG_GPIO, AD7173_REG_GPIO), +}; + +static const struct regmap_access_table ad7173_access_table = { + .yes_ranges = ad7173_range_gpio, + .n_yes_ranges = ARRAY_SIZE(ad7173_range_gpio), +}; + +static const struct regmap_config ad7173_regmap_config = { + .reg_bits = 8, + .val_bits = 16, + .rd_table = &ad7173_access_table, + .wr_table = &ad7173_access_table, + .read_flag_mask = BIT(6), +}; + +enum { + AD7173_SYSCALIB_ZERO_SCALE, + AD7173_SYSCALIB_FULL_SCALE, +}; + +static const char * const ad7173_syscalib_modes[] = { + [AD7173_SYSCALIB_ZERO_SCALE] = "zero_scale", + [AD7173_SYSCALIB_FULL_SCALE] = "full_scale", +}; + +static int ad7173_set_syscalib_mode(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + unsigned int mode) +{ + struct ad7173_state *st = iio_priv(indio_dev); + + st->channels[chan->address].syscalib_mode = mode; + + return 0; +} + +static int ad7173_get_syscalib_mode(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan) +{ + struct ad7173_state *st = iio_priv(indio_dev); + + return st->channels[chan->address].syscalib_mode; +} + +static ssize_t ad7173_write_syscalib(struct iio_dev *indio_dev, + uintptr_t private, + const struct iio_chan_spec *chan, + const char *buf, size_t len) +{ + struct ad7173_state *st = iio_priv(indio_dev); + bool sys_calib; + int ret, mode; + + ret = kstrtobool(buf, &sys_calib); + if (ret) + return ret; + + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; + + mode = st->channels[chan->address].syscalib_mode; + if (sys_calib) { + if (mode == AD7173_SYSCALIB_ZERO_SCALE) + ret = ad_sd_calibrate(&st->sd, AD7173_MODE_CAL_SYS_ZERO, + chan->address); + else + ret = ad_sd_calibrate(&st->sd, AD7173_MODE_CAL_SYS_FULL, + chan->address); + } + + iio_device_release_direct(indio_dev); + + return ret ? : len; +} + +static const struct iio_enum ad7173_syscalib_mode_enum = { + .items = ad7173_syscalib_modes, + .num_items = ARRAY_SIZE(ad7173_syscalib_modes), + .set = ad7173_set_syscalib_mode, + .get = ad7173_get_syscalib_mode +}; + +static const char * const ad7173_filter_types_str[] = { + [AD7173_FILTER_SINC3] = "sinc3", + [AD7173_FILTER_SINC5_SINC1] = "sinc5+sinc1", + [AD7173_FILTER_SINC5_SINC1_PF1] = "sinc5+sinc1+pf1", + [AD7173_FILTER_SINC5_SINC1_PF2] = "sinc5+sinc1+pf2", + [AD7173_FILTER_SINC5_SINC1_PF3] = "sinc5+sinc1+pf3", + [AD7173_FILTER_SINC5_SINC1_PF4] = "sinc5+sinc1+pf4", +}; + +static int ad7173_set_filter_type(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + unsigned int val) +{ + struct ad7173_state *st = iio_priv(indio_dev); + + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; + + st->channels[chan->address].cfg.filter_type = val; + st->channels[chan->address].cfg.live = false; + + iio_device_release_direct(indio_dev); + + return 0; +} + +static int ad7173_get_filter_type(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan) +{ + struct ad7173_state *st = iio_priv(indio_dev); + + return st->channels[chan->address].cfg.filter_type; +} + +static const struct iio_enum ad7173_filter_type_enum = { + .items = ad7173_filter_types_str, + .num_items = ARRAY_SIZE(ad7173_filter_types_str), + .set = ad7173_set_filter_type, + .get = ad7173_get_filter_type, +}; + +static const struct iio_chan_spec_ext_info ad7173_chan_spec_ext_info[] = { + { + .name = "sys_calibration", + .write = ad7173_write_syscalib, + .shared = IIO_SEPARATE, + }, + IIO_ENUM("sys_calibration_mode", IIO_SEPARATE, + &ad7173_syscalib_mode_enum), + IIO_ENUM_AVAILABLE("sys_calibration_mode", IIO_SHARED_BY_TYPE, + &ad7173_syscalib_mode_enum), + IIO_ENUM("filter_type", IIO_SEPARATE, &ad7173_filter_type_enum), + IIO_ENUM_AVAILABLE("filter_type", IIO_SHARED_BY_TYPE, + &ad7173_filter_type_enum), + { } +}; + +static const struct iio_chan_spec_ext_info ad7173_temp_chan_spec_ext_info[] = { + IIO_ENUM("filter_type", IIO_SEPARATE, &ad7173_filter_type_enum), + IIO_ENUM_AVAILABLE("filter_type", IIO_SHARED_BY_TYPE, + &ad7173_filter_type_enum), + { } +}; + +static int ad7173_calibrate_all(struct ad7173_state *st, struct iio_dev *indio_dev) +{ + int ret; + int i; + + for (i = 0; i < st->num_channels; i++) { + if (indio_dev->channels[i].type != IIO_VOLTAGE) + continue; + + ret = ad_sd_calibrate(&st->sd, AD7173_MODE_CAL_INT_ZERO, i); + if (ret < 0) + return ret; + + if (st->info->has_internal_fs_calibration) { + ret = ad_sd_calibrate(&st->sd, AD7173_MODE_CAL_INT_FULL, i); + if (ret < 0) + return ret; + } + } + + return 0; +} + +/* + * Associative array of channel pairs for open wire detection + * The array is indexed by ain and gives the associated channel pair + * to perform the open wire detection with + * the channel pair [0] is for non differential and pair [1] + * is for differential inputs + */ +static int openwire_ain_to_channel_pair[][2][2] = { +/* AIN Single Differential */ + [0] = { { 0, 15 }, { 1, 2 } }, + [1] = { { 1, 2 }, { 2, 1 } }, + [2] = { { 3, 4 }, { 5, 6 } }, + [3] = { { 5, 6 }, { 6, 5 } }, + [4] = { { 7, 8 }, { 9, 10 } }, + [5] = { { 9, 10 }, { 10, 9 } }, + [6] = { { 11, 12 }, { 13, 14 } }, + [7] = { { 13, 14 }, { 14, 13 } }, +}; + +/* + * Openwire detection on ad4111 works by running the same input measurement + * on two different channels and compare if the difference between the two + * measurements exceeds a certain value (typical 300mV) + */ +static int ad4111_openwire_event(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan) +{ + struct ad7173_state *st = iio_priv(indio_dev); + struct ad7173_channel *adchan = &st->channels[chan->address]; + struct ad7173_channel_config *cfg = &adchan->cfg; + int ret, val1, val2; + + ret = regmap_set_bits(st->reg_gpiocon_regmap, AD7173_REG_GPIO, + AD4111_GPIO_GP_OW_EN); + if (ret) + return ret; + + adchan->cfg.openwire_comp_chan = + openwire_ain_to_channel_pair[chan->channel][chan->differential][0]; + + ret = ad_sigma_delta_single_conversion(indio_dev, chan, &val1); + if (ret < 0) { + dev_err(&indio_dev->dev, + "Error running ad_sigma_delta single conversion: %d", ret); + goto out; + } + + adchan->cfg.openwire_comp_chan = + openwire_ain_to_channel_pair[chan->channel][chan->differential][1]; + + ret = ad_sigma_delta_single_conversion(indio_dev, chan, &val2); + if (ret < 0) { + dev_err(&indio_dev->dev, + "Error running ad_sigma_delta single conversion: %d", ret); + goto out; + } + + if (abs(val1 - val2) > cfg->openwire_thrsh_raw) + iio_push_event(indio_dev, + IIO_UNMOD_EVENT_CODE(IIO_VOLTAGE, chan->address, + IIO_EV_TYPE_FAULT, IIO_EV_DIR_FAULT_OPENWIRE), + iio_get_time_ns(indio_dev)); + +out: + adchan->cfg.openwire_comp_chan = -1; + regmap_clear_bits(st->reg_gpiocon_regmap, AD7173_REG_GPIO, + AD4111_GPIO_GP_OW_EN); + return ret; +} + +static int ad7173_mask_xlate(struct gpio_regmap *gpio, enum gpio_regmap_operation op, + unsigned int base, unsigned int offset, unsigned int *reg, + unsigned int *mask) +{ + *mask = AD7173_GPO_DATA(offset); + *reg = base; + return 0; +} + +static int ad4111_mask_xlate(struct gpio_regmap *gpio, enum gpio_regmap_operation op, + unsigned int base, unsigned int offset, unsigned int *reg, + unsigned int *mask) +{ + *mask = AD4111_GPO01_DATA(offset); + *reg = base; + return 0; +} + +static void ad7173_gpio_disable(void *data) +{ + struct ad7173_state *st = data; + unsigned int mask; + + mask = AD7173_GPIO_OP_EN0 | AD7173_GPIO_OP_EN1 | AD7173_GPIO_OP_EN2_3; + regmap_update_bits(st->reg_gpiocon_regmap, AD7173_REG_GPIO, mask, ~mask); +} + +static int ad7173_gpio_init(struct ad7173_state *st) +{ + struct gpio_regmap_config gpio_regmap = {}; + struct device *dev = &st->sd.spi->dev; + unsigned int mask; + int ret; + + st->reg_gpiocon_regmap = devm_regmap_init_spi(st->sd.spi, &ad7173_regmap_config); + ret = PTR_ERR_OR_ZERO(st->reg_gpiocon_regmap); + if (ret) + return dev_err_probe(dev, ret, "Unable to init regmap\n"); + + mask = AD7173_GPIO_OP_EN0 | AD7173_GPIO_OP_EN1 | AD7173_GPIO_OP_EN2_3; + regmap_update_bits(st->reg_gpiocon_regmap, AD7173_REG_GPIO, mask, mask); + + ret = devm_add_action_or_reset(dev, ad7173_gpio_disable, st); + if (ret) + return ret; + + gpio_regmap.parent = dev; + gpio_regmap.regmap = st->reg_gpiocon_regmap; + gpio_regmap.ngpio = st->info->num_gpios; + gpio_regmap.reg_set_base = AD7173_REG_GPIO; + if (st->info->higher_gpio_bits) + gpio_regmap.reg_mask_xlate = ad4111_mask_xlate; + else + gpio_regmap.reg_mask_xlate = ad7173_mask_xlate; + + st->gpio_regmap = devm_gpio_regmap_register(dev, &gpio_regmap); + ret = PTR_ERR_OR_ZERO(st->gpio_regmap); + if (ret) + return dev_err_probe(dev, ret, "Unable to init gpio-regmap\n"); + + return 0; +} + +static struct ad7173_state *ad_sigma_delta_to_ad7173(struct ad_sigma_delta *sd) +{ + return container_of(sd, struct ad7173_state, sd); +} + +static struct ad7173_state *clk_hw_to_ad7173(struct clk_hw *hw) +{ + return container_of(hw, struct ad7173_state, int_clk_hw); +} + +static void ad7173_ida_destroy(void *data) +{ + struct ad7173_state *st = data; + + ida_destroy(&st->cfg_slots_status); +} + +static void ad7173_reset_usage_cnts(struct ad7173_state *st) +{ + memset64(st->config_cnts, 0, st->info->num_configs); + st->config_usage_counter = 0; +} + +/** + * ad7173_is_setup_equal - Compare two channel setups + * @cfg1: First channel configuration + * @cfg2: Second channel configuration + * + * Compares all configuration options that affect the registers connected to + * SETUP_SEL, namely CONFIGx, FILTERx, GAINx and OFFSETx. + * + * Returns: true if the setups are identical, false otherwise + */ +static bool ad7173_is_setup_equal(const struct ad7173_channel_config *cfg1, + const struct ad7173_channel_config *cfg2) +{ + /* + * This is just to make sure that the comparison is adapted after + * struct ad7173_channel_config was changed. + */ + static_assert(sizeof_field(struct ad7173_channel_config, config_props) == + sizeof(struct { + bool bipolar; + bool input_buf; + u16 sinc3_odr_div; + u8 sinc5_odr_index; + u8 ref_sel; + enum ad7173_filter_type filter_type; + })); + + return cfg1->bipolar == cfg2->bipolar && + cfg1->input_buf == cfg2->input_buf && + cfg1->sinc3_odr_div == cfg2->sinc3_odr_div && + cfg1->sinc5_odr_index == cfg2->sinc5_odr_index && + cfg1->ref_sel == cfg2->ref_sel && + cfg1->filter_type == cfg2->filter_type; +} + +static struct ad7173_channel_config * +ad7173_find_live_config(struct ad7173_state *st, struct ad7173_channel_config *cfg) +{ + struct ad7173_channel_config *cfg_aux; + int i; + + for (i = 0; i < st->num_channels; i++) { + cfg_aux = &st->channels[i].cfg; + + if (cfg_aux->live && ad7173_is_setup_equal(cfg, cfg_aux)) + return cfg_aux; + } + return NULL; +} + +/* Could be replaced with a generic LRU implementation */ +static int ad7173_free_config_slot_lru(struct ad7173_state *st) +{ + int i, lru_position = 0; + + for (i = 1; i < st->info->num_configs; i++) + if (st->config_cnts[i] < st->config_cnts[lru_position]) + lru_position = i; + + for (i = 0; i < st->num_channels; i++) + if (st->channels[i].cfg.cfg_slot == lru_position) + st->channels[i].cfg.live = false; + + ida_free(&st->cfg_slots_status, lru_position); + return ida_alloc(&st->cfg_slots_status, GFP_KERNEL); +} + +/* Could be replaced with a generic LRU implementation */ +static int ad7173_load_config(struct ad7173_state *st, + struct ad7173_channel_config *cfg) +{ + unsigned int config; + int free_cfg_slot, ret; + u8 post_filter_enable, post_filter_select; + + free_cfg_slot = ida_alloc_range(&st->cfg_slots_status, 0, + st->info->num_configs - 1, GFP_KERNEL); + if (free_cfg_slot < 0) + free_cfg_slot = ad7173_free_config_slot_lru(st); + + cfg->cfg_slot = free_cfg_slot; + config = FIELD_PREP(AD7173_SETUP_REF_SEL_MASK, cfg->ref_sel); + + if (cfg->bipolar) + config |= AD7173_SETUP_BIPOLAR; + + if (cfg->input_buf) + config |= AD7173_SETUP_AIN_BUF_MASK; + + ret = ad_sd_write_reg(&st->sd, AD7173_REG_SETUP(free_cfg_slot), 2, config); + if (ret) + return ret; + + /* + * When SINC3_MAP flag is enabled, the rest of the register has a + * different meaning. We are using this option to allow the most + * possible sampling frequencies with SINC3 filter. + */ + if (cfg->filter_type == AD7173_FILTER_SINC3) + return ad_sd_write_reg(&st->sd, AD7173_REG_FILTER(free_cfg_slot), 2, + FIELD_PREP(AD7173_FILTER_SINC3_MAP, 1) | + FIELD_PREP(AD7173_FILTER_SINC3_MAP_DIV, + cfg->sinc3_odr_div)); + + switch (cfg->filter_type) { + case AD7173_FILTER_SINC5_SINC1_PF1: + post_filter_enable = 1; + post_filter_select = 2; + break; + case AD7173_FILTER_SINC5_SINC1_PF2: + post_filter_enable = 1; + post_filter_select = 3; + break; + case AD7173_FILTER_SINC5_SINC1_PF3: + post_filter_enable = 1; + post_filter_select = 5; + break; + case AD7173_FILTER_SINC5_SINC1_PF4: + post_filter_enable = 1; + post_filter_select = 6; + break; + default: + post_filter_enable = 0; + post_filter_select = 0; + break; + } + + return ad_sd_write_reg(&st->sd, AD7173_REG_FILTER(free_cfg_slot), 2, + FIELD_PREP(AD7173_FILTER_SINC3_MAP, 0) | + FIELD_PREP(AD7173_FILTER_ENHFILT_MASK, + post_filter_enable) | + FIELD_PREP(AD7173_FILTER_ENHFILTEN, + post_filter_select) | + FIELD_PREP(AD7173_FILTER_ORDER, 0) | + FIELD_PREP(AD7173_FILTER_ODR_MASK, + cfg->sinc5_odr_index)); +} + +static int ad7173_config_channel(struct ad7173_state *st, int addr) +{ + struct ad7173_channel_config *cfg = &st->channels[addr].cfg; + struct ad7173_channel_config *live_cfg; + int ret; + + if (!cfg->live) { + live_cfg = ad7173_find_live_config(st, cfg); + if (live_cfg) { + cfg->cfg_slot = live_cfg->cfg_slot; + } else { + ret = ad7173_load_config(st, cfg); + if (ret) + return ret; + cfg->live = true; + } + } + + if (st->config_usage_counter == U64_MAX) + ad7173_reset_usage_cnts(st); + + st->config_usage_counter++; + st->config_cnts[cfg->cfg_slot] = st->config_usage_counter; + + return 0; +} + +static int ad7173_set_channel(struct ad_sigma_delta *sd, unsigned int channel) +{ + struct ad7173_state *st = ad_sigma_delta_to_ad7173(sd); + unsigned int val; + int ret; + + ret = ad7173_config_channel(st, channel); + if (ret) + return ret; + + val = AD7173_CH_ENABLE | + FIELD_PREP(AD7173_CH_SETUP_SEL_MASK, st->channels[channel].cfg.cfg_slot) | + st->channels[channel].ain; + + if (st->channels[channel].cfg.openwire_comp_chan >= 0) + channel = st->channels[channel].cfg.openwire_comp_chan; + + return ad_sd_write_reg(&st->sd, AD7173_REG_CH(channel), 2, val); +} + +static int ad7173_set_mode(struct ad_sigma_delta *sd, + enum ad_sigma_delta_mode mode) +{ + struct ad7173_state *st = ad_sigma_delta_to_ad7173(sd); + + st->adc_mode &= ~AD7173_ADC_MODE_MODE_MASK; + st->adc_mode |= FIELD_PREP(AD7173_ADC_MODE_MODE_MASK, mode); + + return ad_sd_write_reg(&st->sd, AD7173_REG_ADC_MODE, 2, st->adc_mode); +} + +static int ad7173_append_status(struct ad_sigma_delta *sd, bool append) +{ + struct ad7173_state *st = ad_sigma_delta_to_ad7173(sd); + unsigned int interface_mode = st->interface_mode; + int ret; + + interface_mode &= ~AD7173_INTERFACE_DATA_STAT; + interface_mode |= AD7173_INTERFACE_DATA_STAT_EN(append); + ret = ad_sd_write_reg(&st->sd, AD7173_REG_INTERFACE_MODE, 2, interface_mode); + if (ret) + return ret; + + st->interface_mode = interface_mode; + + return 0; +} + +static int ad7173_disable_all(struct ad_sigma_delta *sd) +{ + struct ad7173_state *st = ad_sigma_delta_to_ad7173(sd); + int ret; + int i; + + for (i = 0; i < st->num_channels; i++) { + ret = ad_sd_write_reg(sd, AD7173_REG_CH(i), 2, 0); + if (ret < 0) + return ret; + } + + return 0; +} + +static int ad7173_disable_one(struct ad_sigma_delta *sd, unsigned int chan) +{ + struct ad7173_state *st = ad_sigma_delta_to_ad7173(sd); + + if (st->channels[chan].cfg.openwire_comp_chan >= 0) + chan = st->channels[chan].cfg.openwire_comp_chan; + + return ad_sd_write_reg(sd, AD7173_REG_CH(chan), 2, 0); +} + +static const struct ad_sigma_delta_info ad7173_sigma_delta_info_4_slots = { + .set_channel = ad7173_set_channel, + .append_status = ad7173_append_status, + .disable_all = ad7173_disable_all, + .disable_one = ad7173_disable_one, + .set_mode = ad7173_set_mode, + .has_registers = true, + .has_named_irqs = true, + .supports_spi_offload = true, + .addr_shift = 0, + .read_mask = BIT(6), + .status_ch_mask = GENMASK(3, 0), + .data_reg = AD7173_REG_DATA, + .num_resetclks = 64, + .num_slots = 4, +}; + +static const struct ad_sigma_delta_info ad7173_sigma_delta_info_8_slots = { + .set_channel = ad7173_set_channel, + .append_status = ad7173_append_status, + .disable_all = ad7173_disable_all, + .disable_one = ad7173_disable_one, + .set_mode = ad7173_set_mode, + .has_registers = true, + .has_named_irqs = true, + .supports_spi_offload = true, + .addr_shift = 0, + .read_mask = BIT(6), + .status_ch_mask = GENMASK(3, 0), + .data_reg = AD7173_REG_DATA, + .num_resetclks = 64, + .num_slots = 8, +}; + +static const struct ad_sigma_delta_info ad7173_sigma_delta_info_16_slots = { + .set_channel = ad7173_set_channel, + .append_status = ad7173_append_status, + .disable_all = ad7173_disable_all, + .disable_one = ad7173_disable_one, + .set_mode = ad7173_set_mode, + .has_registers = true, + .has_named_irqs = true, + .supports_spi_offload = true, + .addr_shift = 0, + .read_mask = BIT(6), + .status_ch_mask = GENMASK(3, 0), + .data_reg = AD7173_REG_DATA, + .num_resetclks = 64, + .num_slots = 16, +}; + +static const struct ad7173_device_info ad4111_device_info = { + .name = "ad4111", + .id = AD4111_ID, + .sd_info = &ad7173_sigma_delta_info_16_slots, + .num_voltage_in_div = 8, + .num_channels = 16, + .num_configs = 8, + .num_voltage_in = 8, + .num_gpios = 2, + .higher_gpio_bits = true, + .has_temp = true, + .has_vincom_input = true, + .has_input_buf = true, + .has_current_inputs = true, + .has_int_ref = true, + .has_internal_fs_calibration = true, + .has_openwire_det = true, + .clock = 2 * HZ_PER_MHZ, + .sinc5_data_rates = ad7173_sinc5_data_rates, + .num_sinc5_data_rates = ARRAY_SIZE(ad7173_sinc5_data_rates), +}; + +static const struct ad7173_device_info ad4112_device_info = { + .name = "ad4112", + .id = AD4112_ID, + .sd_info = &ad7173_sigma_delta_info_16_slots, + .num_voltage_in_div = 8, + .num_channels = 16, + .num_configs = 8, + .num_voltage_in = 8, + .num_gpios = 2, + .higher_gpio_bits = true, + .has_vincom_input = true, + .has_temp = true, + .has_input_buf = true, + .has_current_inputs = true, + .has_int_ref = true, + .has_internal_fs_calibration = true, + .clock = 2 * HZ_PER_MHZ, + .sinc5_data_rates = ad7173_sinc5_data_rates, + .num_sinc5_data_rates = ARRAY_SIZE(ad7173_sinc5_data_rates), +}; + +static const struct ad7173_device_info ad4113_device_info = { + .name = "ad4113", + .id = AD4113_ID, + .sd_info = &ad7173_sigma_delta_info_16_slots, + .num_voltage_in_div = 8, + .num_channels = 16, + .num_configs = 8, + .num_voltage_in = 8, + .num_gpios = 2, + .data_reg_only_16bit = true, + .higher_gpio_bits = true, + .has_vincom_input = true, + .has_input_buf = true, + .has_int_ref = true, + .clock = 2 * HZ_PER_MHZ, + .sinc5_data_rates = ad7173_sinc5_data_rates, + .num_sinc5_data_rates = ARRAY_SIZE(ad7173_sinc5_data_rates), +}; + +static const struct ad7173_device_info ad4114_device_info = { + .name = "ad4114", + .id = AD4114_ID, + .sd_info = &ad7173_sigma_delta_info_16_slots, + .num_voltage_in_div = 16, + .num_channels = 16, + .num_configs = 8, + .num_voltage_in = 16, + .num_gpios = 4, + .has_vincom_input = true, + .has_temp = true, + .has_input_buf = true, + .has_int_ref = true, + .has_internal_fs_calibration = true, + .clock = 2 * HZ_PER_MHZ, + .sinc5_data_rates = ad7173_sinc5_data_rates, + .num_sinc5_data_rates = ARRAY_SIZE(ad7173_sinc5_data_rates), +}; + +static const struct ad7173_device_info ad4115_device_info = { + .name = "ad4115", + .id = AD4115_ID, + .sd_info = &ad7173_sigma_delta_info_16_slots, + .num_voltage_in_div = 16, + .num_channels = 16, + .num_configs = 8, + .num_voltage_in = 16, + .num_gpios = 4, + .has_vincom_input = true, + .has_temp = true, + .has_input_buf = true, + .has_int_ref = true, + .has_internal_fs_calibration = true, + .clock = 8 * HZ_PER_MHZ, + .sinc5_data_rates = ad4115_sinc5_data_rates, + .num_sinc5_data_rates = ARRAY_SIZE(ad4115_sinc5_data_rates), +}; + +static const struct ad7173_device_info ad4116_device_info = { + .name = "ad4116", + .id = AD4116_ID, + .sd_info = &ad7173_sigma_delta_info_16_slots, + .num_voltage_in_div = 11, + .num_channels = 16, + .num_configs = 8, + .num_voltage_in = 16, + .num_gpios = 4, + .has_vincom_input = true, + .has_temp = true, + .has_input_buf = true, + .has_int_ref = true, + .has_internal_fs_calibration = true, + .clock = 4 * HZ_PER_MHZ, + .sinc5_data_rates = ad4116_sinc5_data_rates, + .num_sinc5_data_rates = ARRAY_SIZE(ad4116_sinc5_data_rates), +}; + +static const struct ad7173_device_info ad7172_2_device_info = { + .name = "ad7172-2", + .id = AD7172_2_ID, + .sd_info = &ad7173_sigma_delta_info_4_slots, + .num_voltage_in = 5, + .num_channels = 4, + .num_configs = 4, + .num_gpios = 2, + .has_temp = true, + .has_input_buf = true, + .has_int_ref = true, + .has_pow_supply_monitoring = true, + .clock = 2 * HZ_PER_MHZ, + .sinc5_data_rates = ad7173_sinc5_data_rates, + .num_sinc5_data_rates = ARRAY_SIZE(ad7173_sinc5_data_rates), +}; + +static const struct ad7173_device_info ad7172_4_device_info = { + .name = "ad7172-4", + .id = AD7172_4_ID, + .sd_info = &ad7173_sigma_delta_info_8_slots, + .num_voltage_in = 9, + .num_channels = 8, + .num_configs = 8, + .num_gpios = 4, + .has_input_buf = true, + .has_ref2 = true, + .has_pow_supply_monitoring = true, + .clock = 2 * HZ_PER_MHZ, + .sinc5_data_rates = ad7173_sinc5_data_rates, + .num_sinc5_data_rates = ARRAY_SIZE(ad7173_sinc5_data_rates), +}; + +static const struct ad7173_device_info ad7173_8_device_info = { + .name = "ad7173-8", + .id = AD7173_ID, + .sd_info = &ad7173_sigma_delta_info_16_slots, + .num_voltage_in = 17, + .num_channels = 16, + .num_configs = 8, + .num_gpios = 4, + .has_temp = true, + .has_input_buf = true, + .has_int_ref = true, + .has_ref2 = true, + .clock = 2 * HZ_PER_MHZ, + .sinc5_data_rates = ad7173_sinc5_data_rates, + .num_sinc5_data_rates = ARRAY_SIZE(ad7173_sinc5_data_rates), +}; + +static const struct ad7173_device_info ad7175_2_device_info = { + .name = "ad7175-2", + .id = AD7175_2_ID, + .sd_info = &ad7173_sigma_delta_info_4_slots, + .num_voltage_in = 5, + .num_channels = 4, + .num_configs = 4, + .num_gpios = 2, + .has_temp = true, + .has_input_buf = true, + .has_int_ref = true, + .has_pow_supply_monitoring = true, + .clock = 16 * HZ_PER_MHZ, + .sinc5_data_rates = ad7175_sinc5_data_rates, + .num_sinc5_data_rates = ARRAY_SIZE(ad7175_sinc5_data_rates), +}; + +static const struct ad7173_device_info ad7175_8_device_info = { + .name = "ad7175-8", + .id = AD7175_8_ID, + .sd_info = &ad7173_sigma_delta_info_16_slots, + .num_voltage_in = 17, + .num_channels = 16, + .num_configs = 8, + .num_gpios = 4, + .has_temp = true, + .has_input_buf = true, + .has_int_ref = true, + .has_ref2 = true, + .has_pow_supply_monitoring = true, + .clock = 16 * HZ_PER_MHZ, + .sinc5_data_rates = ad7175_sinc5_data_rates, + .num_sinc5_data_rates = ARRAY_SIZE(ad7175_sinc5_data_rates), +}; + +static const struct ad7173_device_info ad7176_2_device_info = { + .name = "ad7176-2", + .id = AD7176_ID, + .sd_info = &ad7173_sigma_delta_info_4_slots, + .num_voltage_in = 5, + .num_channels = 4, + .num_configs = 4, + .num_gpios = 2, + .has_int_ref = true, + .clock = 16 * HZ_PER_MHZ, + .sinc5_data_rates = ad7175_sinc5_data_rates, + .num_sinc5_data_rates = ARRAY_SIZE(ad7175_sinc5_data_rates), +}; + +static const struct ad7173_device_info ad7177_2_device_info = { + .name = "ad7177-2", + .id = AD7177_ID, + .sd_info = &ad7173_sigma_delta_info_4_slots, + .num_voltage_in = 5, + .num_channels = 4, + .num_configs = 4, + .num_gpios = 2, + .has_temp = true, + .has_input_buf = true, + .has_int_ref = true, + .has_pow_supply_monitoring = true, + .clock = 16 * HZ_PER_MHZ, + .odr_start_value = AD7177_ODR_START_VALUE, + .sinc5_data_rates = ad7175_sinc5_data_rates, + .num_sinc5_data_rates = ARRAY_SIZE(ad7175_sinc5_data_rates), +}; + +static int ad7173_setup(struct iio_dev *indio_dev) +{ + struct ad7173_state *st = iio_priv(indio_dev); + struct device *dev = &st->sd.spi->dev; + u8 buf[AD7173_RESET_LENGTH]; + unsigned int id; + int ret; + + /* reset the serial interface */ + memset(buf, 0xff, AD7173_RESET_LENGTH); + ret = spi_write_then_read(st->sd.spi, buf, sizeof(buf), NULL, 0); + if (ret < 0) + return ret; + + /* datasheet recommends a delay of at least 500us after reset */ + fsleep(500); + + ret = ad_sd_read_reg(&st->sd, AD7173_REG_ID, 2, &id); + if (ret) + return ret; + + id &= AD7173_ID_MASK; + if (id != st->info->id) + dev_warn(dev, "Unexpected device id: 0x%04X, expected: 0x%04X\n", + id, st->info->id); + + st->adc_mode |= AD7173_ADC_MODE_SING_CYC; + st->interface_mode = 0x0; + + st->config_usage_counter = 0; + st->config_cnts = devm_kcalloc(dev, st->info->num_configs, + sizeof(*st->config_cnts), GFP_KERNEL); + if (!st->config_cnts) + return -ENOMEM; + + ret = ad7173_calibrate_all(st, indio_dev); + if (ret) + return ret; + + /* All channels are enabled by default after a reset */ + return ad7173_disable_all(&st->sd); +} + +static unsigned int ad7173_get_ref_voltage_milli(struct ad7173_state *st, + u8 reference_select) +{ + int vref; + + switch (reference_select) { + case AD7173_SETUP_REF_SEL_EXT_REF: + vref = regulator_get_voltage(st->regulators[0].consumer); + break; + + case AD7173_SETUP_REF_SEL_EXT_REF2: + vref = regulator_get_voltage(st->regulators[1].consumer); + break; + + case AD7173_SETUP_REF_SEL_INT_REF: + vref = AD7173_VOLTAGE_INT_REF_uV; + break; + + case AD7173_SETUP_REF_SEL_AVDD1_AVSS: + vref = regulator_get_voltage(st->regulators[2].consumer); + break; + + default: + return -EINVAL; + } + + if (vref < 0) + return vref; + + return vref / (MICRO / MILLI); +} + +static int ad7173_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long info) +{ + struct ad7173_state *st = iio_priv(indio_dev); + struct ad7173_channel *ch = &st->channels[chan->address]; + unsigned int reg; + u64 temp; + int ret; + + switch (info) { + case IIO_CHAN_INFO_RAW: + ret = ad_sigma_delta_single_conversion(indio_dev, chan, val); + if (ret < 0) + return ret; + + if (ch->openwire_det_en) { + ret = ad4111_openwire_event(indio_dev, chan); + if (ret < 0) + return ret; + } + + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + + switch (chan->type) { + case IIO_TEMP: + temp = AD7173_VOLTAGE_INT_REF_uV * MILLI; + temp /= AD7173_TEMP_SENSIIVITY_uV_per_C; + *val = temp; + *val2 = chan->scan_type.realbits; + return IIO_VAL_FRACTIONAL_LOG2; + case IIO_VOLTAGE: + *val = ad7173_get_ref_voltage_milli(st, ch->cfg.ref_sel); + *val2 = chan->scan_type.realbits - !!(ch->cfg.bipolar); + + if (chan->channel < st->info->num_voltage_in_div) + *val *= AD4111_DIVIDER_RATIO; + return IIO_VAL_FRACTIONAL_LOG2; + case IIO_CURRENT: + *val = ad7173_get_ref_voltage_milli(st, ch->cfg.ref_sel); + *val /= AD4111_SHUNT_RESISTOR_OHM; + *val2 = chan->scan_type.realbits - ch->cfg.bipolar; + return IIO_VAL_FRACTIONAL_LOG2; + default: + return -EINVAL; + } + case IIO_CHAN_INFO_OFFSET: + + switch (chan->type) { + case IIO_TEMP: + /* 0 Kelvin -> raw sample */ + temp = -ABSOLUTE_ZERO_MILLICELSIUS; + temp *= AD7173_TEMP_SENSIIVITY_uV_per_C; + temp <<= chan->scan_type.realbits; + temp = DIV_U64_ROUND_CLOSEST(temp, + AD7173_VOLTAGE_INT_REF_uV * + MILLI); + *val = -temp; + return IIO_VAL_INT; + case IIO_VOLTAGE: + case IIO_CURRENT: + *val = -BIT(chan->scan_type.realbits - 1); + return IIO_VAL_INT; + default: + return -EINVAL; + } + case IIO_CHAN_INFO_SAMP_FREQ: + if (st->channels[chan->address].cfg.filter_type == AD7173_FILTER_SINC3) { + /* Inverse operation of ad7173_sinc3_odr_div_from_odr() */ + *val = MEGA; + *val2 = 3 * 32 * st->channels[chan->address].cfg.sinc3_odr_div; + return IIO_VAL_FRACTIONAL; + } + + reg = st->channels[chan->address].cfg.sinc5_odr_index; + + *val = st->info->sinc5_data_rates[reg] / MILLI; + *val2 = (st->info->sinc5_data_rates[reg] % MILLI) * (MICRO / MILLI); + + return IIO_VAL_INT_PLUS_MICRO; + default: + return -EINVAL; + } +} + +static int ad7173_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, int val2, long info) +{ + struct ad7173_state *st = iio_priv(indio_dev); + struct ad7173_channel_config *cfg; + unsigned int freq, i; + int ret = 0; + + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; + + switch (info) { + /* + * This attribute sets the sampling frequency for each channel individually. + * There are no issues for raw or buffered reads of an individual channel. + * + * When multiple channels are enabled in buffered mode, the effective + * sampling rate of a channel is lowered in correlation to the number + * of channels enabled and the sampling rate of the other channels. + * + * Example: 3 channels enabled with rates CH1:6211sps CH2,CH3:10sps + * While the reading of CH1 takes only 0.16ms, the reading of CH2 and CH3 + * will take 100ms each. + * + * This will cause the reading of CH1 to be actually done once every + * 200.16ms, an effective rate of 4.99sps. + * + * Both the sinc5 and sinc3 rates are set here so that if the filter + * type is changed, the requested rate will still be set (aside from + * rounding differences). + */ + case IIO_CHAN_INFO_SAMP_FREQ: + freq = val * MILLI + val2 / MILLI; + for (i = st->info->odr_start_value; i < st->info->num_sinc5_data_rates - 1; i++) + if (freq >= st->info->sinc5_data_rates[i]) + break; + + cfg = &st->channels[chan->address].cfg; + cfg->sinc5_odr_index = i; + cfg->sinc3_odr_div = ad7173_sinc3_odr_div_from_odr(freq); + cfg->live = false; + break; + + default: + ret = -EINVAL; + break; + } + + iio_device_release_direct(indio_dev); + return ret; +} + +static int ad7173_update_scan_mode(struct iio_dev *indio_dev, + const unsigned long *scan_mask) +{ + struct ad7173_state *st = iio_priv(indio_dev); + u16 sinc3_count = 0; + u16 sinc3_div = 0; + int i, j, k, ret; + + for (i = 0; i < indio_dev->num_channels; i++) { + const struct ad7173_channel_config *cfg = &st->channels[i].cfg; + + if (test_bit(i, scan_mask)) { + if (cfg->filter_type == AD7173_FILTER_SINC3) { + sinc3_count++; + + if (sinc3_div == 0) { + sinc3_div = cfg->sinc3_odr_div; + } else if (sinc3_div != cfg->sinc3_odr_div) { + dev_err(&st->sd.spi->dev, + "All enabled channels must have the same sampling_frequency for sinc3 filter_type\n"); + return -EINVAL; + } + } + + ret = ad7173_set_channel(&st->sd, i); + } else { + ret = ad_sd_write_reg(&st->sd, AD7173_REG_CH(i), 2, 0); + } + if (ret < 0) + return ret; + } + + if (sinc3_count && sinc3_count < bitmap_weight(scan_mask, indio_dev->num_channels)) { + dev_err(&st->sd.spi->dev, + "All enabled channels must have sinc3 filter_type\n"); + return -EINVAL; + } + + /* + * On some chips, there are more channels that setups, so if there were + * more unique setups requested than the number of available slots, + * ad7173_set_channel() will have written over some of the slots. We + * can detect this by making sure each assigned cfg_slot matches the + * requested configuration. If it doesn't, we know that the slot was + * overwritten by a different channel. + */ + for_each_set_bit(i, scan_mask, indio_dev->num_channels) { + const struct ad7173_channel_config *cfg1, *cfg2; + + cfg1 = &st->channels[i].cfg; + + for_each_set_bit(j, scan_mask, indio_dev->num_channels) { + cfg2 = &st->channels[j].cfg; + + /* + * Only compare configs that are assigned to the same + * SETUP_SEL slot and don't compare channel to itself. + */ + if (i == j || cfg1->cfg_slot != cfg2->cfg_slot) + continue; + + /* + * If we find two different configs trying to use the + * same SETUP_SEL slot, then we know that the that we + * have too many unique configurations requested for + * the available slots and at least one was overwritten. + */ + if (!ad7173_is_setup_equal(cfg1, cfg2)) { + /* + * At this point, there isn't a way to tell + * which setups are actually programmed in the + * ADC anymore, so we could read them back to + * see, but it is simpler to just turn off all + * of the live flags so that everything gets + * reprogramed on the next attempt read a sample. + */ + for (k = 0; k < st->num_channels; k++) + st->channels[k].cfg.live = false; + + dev_err(&st->sd.spi->dev, + "Too many unique channel configurations requested for scan\n"); + return -EINVAL; + } + } + } + + return 0; +} + +static int ad7173_debug_reg_access(struct iio_dev *indio_dev, unsigned int reg, + unsigned int writeval, unsigned int *readval) +{ + struct ad7173_state *st = iio_priv(indio_dev); + u8 reg_size; + + if (reg == AD7173_REG_COMMS) + reg_size = 1; + else if (reg == AD7173_REG_CRC || reg == AD7173_REG_DATA || + reg >= AD7173_REG_OFFSET(0)) + reg_size = 3; + else + reg_size = 2; + + if (readval) + return ad_sd_read_reg(&st->sd, reg, reg_size, readval); + + return ad_sd_write_reg(&st->sd, reg, reg_size, writeval); +} + +static int ad7173_write_event_config(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir, + bool state) +{ + struct ad7173_state *st = iio_priv(indio_dev); + struct ad7173_channel *adchan = &st->channels[chan->address]; + + switch (type) { + case IIO_EV_TYPE_FAULT: + adchan->openwire_det_en = state; + return 0; + default: + return -EINVAL; + } +} + +static int ad7173_read_event_config(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir) +{ + struct ad7173_state *st = iio_priv(indio_dev); + struct ad7173_channel *adchan = &st->channels[chan->address]; + + switch (type) { + case IIO_EV_TYPE_FAULT: + return adchan->openwire_det_en; + default: + return -EINVAL; + } +} + +static const struct iio_event_spec ad4111_events[] = { + { + .type = IIO_EV_TYPE_FAULT, + .dir = IIO_EV_DIR_FAULT_OPENWIRE, + .mask_separate = BIT(IIO_EV_INFO_VALUE), + .mask_shared_by_all = BIT(IIO_EV_INFO_ENABLE), + }, +}; + +static const struct iio_info ad7173_info = { + .read_raw = &ad7173_read_raw, + .write_raw = &ad7173_write_raw, + .debugfs_reg_access = &ad7173_debug_reg_access, + .validate_trigger = ad_sd_validate_trigger, + .update_scan_mode = ad7173_update_scan_mode, + .write_event_config = ad7173_write_event_config, + .read_event_config = ad7173_read_event_config, +}; + +static const struct iio_scan_type ad4113_scan_type = { + .sign = 'u', + .realbits = 16, + .storagebits = 16, + .endianness = IIO_BE, +}; + +static const struct iio_chan_spec ad7173_channel_template = { + .type = IIO_VOLTAGE, + .indexed = 1, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SCALE) | BIT(IIO_CHAN_INFO_SAMP_FREQ), + .scan_type = { + .sign = 'u', + .realbits = 24, + .storagebits = 32, + .endianness = IIO_BE, + }, + .ext_info = ad7173_chan_spec_ext_info, +}; + +static const struct iio_chan_spec ad7173_temp_iio_channel_template = { + .type = IIO_TEMP, + .channel = AD7173_AIN_TEMP_POS, + .channel2 = AD7173_AIN_TEMP_NEG, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SCALE) | BIT(IIO_CHAN_INFO_OFFSET) | + BIT(IIO_CHAN_INFO_SAMP_FREQ), + .scan_type = { + .sign = 'u', + .realbits = 24, + .storagebits = 32, + .endianness = IIO_BE, + }, + .ext_info = ad7173_temp_chan_spec_ext_info, +}; + +static void ad7173_disable_regulators(void *data) +{ + struct ad7173_state *st = data; + + regulator_bulk_disable(ARRAY_SIZE(st->regulators), st->regulators); +} + +static unsigned long ad7173_sel_clk(struct ad7173_state *st, + unsigned int clk_sel) +{ + int ret; + + st->adc_mode &= ~AD7173_ADC_MODE_CLOCKSEL_MASK; + st->adc_mode |= FIELD_PREP(AD7173_ADC_MODE_CLOCKSEL_MASK, clk_sel); + ret = ad_sd_write_reg(&st->sd, AD7173_REG_ADC_MODE, 0x2, st->adc_mode); + + return ret; +} + +static unsigned long ad7173_clk_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct ad7173_state *st = clk_hw_to_ad7173(hw); + + return st->info->clock / HZ_PER_KHZ; +} + +static int ad7173_clk_output_is_enabled(struct clk_hw *hw) +{ + struct ad7173_state *st = clk_hw_to_ad7173(hw); + u32 clk_sel; + + clk_sel = FIELD_GET(AD7173_ADC_MODE_CLOCKSEL_MASK, st->adc_mode); + return clk_sel == AD7173_ADC_MODE_CLOCKSEL_INT_OUTPUT; +} + +static int ad7173_clk_output_prepare(struct clk_hw *hw) +{ + struct ad7173_state *st = clk_hw_to_ad7173(hw); + + return ad7173_sel_clk(st, AD7173_ADC_MODE_CLOCKSEL_INT_OUTPUT); +} + +static void ad7173_clk_output_unprepare(struct clk_hw *hw) +{ + struct ad7173_state *st = clk_hw_to_ad7173(hw); + + ad7173_sel_clk(st, AD7173_ADC_MODE_CLOCKSEL_INT); +} + +static const struct clk_ops ad7173_int_clk_ops = { + .recalc_rate = ad7173_clk_recalc_rate, + .is_enabled = ad7173_clk_output_is_enabled, + .prepare = ad7173_clk_output_prepare, + .unprepare = ad7173_clk_output_unprepare, +}; + +static int ad7173_register_clk_provider(struct iio_dev *indio_dev) +{ + struct ad7173_state *st = iio_priv(indio_dev); + struct device *dev = indio_dev->dev.parent; + struct fwnode_handle *fwnode = dev_fwnode(dev); + struct clk_init_data init = {}; + int ret; + + if (!IS_ENABLED(CONFIG_COMMON_CLK)) + return 0; + + init.name = fwnode_get_name(fwnode); + init.ops = &ad7173_int_clk_ops; + + st->int_clk_hw.init = &init; + ret = devm_clk_hw_register(dev, &st->int_clk_hw); + if (ret) + return ret; + + return devm_of_clk_add_hw_provider(dev, of_clk_hw_simple_get, + &st->int_clk_hw); +} + +static int ad4111_validate_current_ain(struct ad7173_state *st, + const unsigned int ain[AD7173_NO_AINS_PER_CHANNEL]) +{ + struct device *dev = &st->sd.spi->dev; + + if (!st->info->has_current_inputs) + return dev_err_probe(dev, -EINVAL, + "Model %s does not support current channels\n", + st->info->name); + + if (ain[0] >= ARRAY_SIZE(ad4111_current_channel_config)) + return dev_err_probe(dev, -EINVAL, + "For current channels single-channel must be <[0-3]>\n"); + + return 0; +} + +static int ad7173_validate_voltage_ain_inputs(struct ad7173_state *st, + unsigned int ain0, unsigned int ain1) +{ + struct device *dev = &st->sd.spi->dev; + bool special_input0, special_input1; + + /* (AVDD1-AVSS)/5 power supply monitoring */ + if (ain0 == AD7173_AIN_POW_MON_POS && ain1 == AD7173_AIN_POW_MON_NEG && + st->info->has_pow_supply_monitoring) + return 0; + + special_input0 = AD7173_IS_REF_INPUT(ain0) || + (ain0 == AD4111_VINCOM_INPUT && st->info->has_vincom_input); + special_input1 = AD7173_IS_REF_INPUT(ain1) || + (ain1 == AD4111_VINCOM_INPUT && st->info->has_vincom_input); + + if ((ain0 >= st->info->num_voltage_in && !special_input0) || + (ain1 >= st->info->num_voltage_in && !special_input1)) { + if (ain0 == AD4111_VINCOM_INPUT || ain1 == AD4111_VINCOM_INPUT) + return dev_err_probe(dev, -EINVAL, + "VINCOM not supported for %s\n", st->info->name); + + return dev_err_probe(dev, -EINVAL, + "Input pin number out of range for pair (%d %d).\n", + ain0, ain1); + } + + if (AD4111_IS_VINCOM_MISMATCH(ain0, ain1) || + AD4111_IS_VINCOM_MISMATCH(ain1, ain0)) + return dev_err_probe(dev, -EINVAL, + "VINCOM must be paired with inputs having divider.\n"); + + if (!special_input0 && !special_input1 && + ((ain0 >= st->info->num_voltage_in_div) != + (ain1 >= st->info->num_voltage_in_div))) + return dev_err_probe(dev, -EINVAL, + "Both inputs must either have a voltage divider or not have: (%d %d).\n", + ain0, ain1); + + return 0; +} + +static int ad7173_validate_reference(struct ad7173_state *st, int ref_sel) +{ + struct device *dev = &st->sd.spi->dev; + int ret; + + if (ref_sel == AD7173_SETUP_REF_SEL_INT_REF && !st->info->has_int_ref) + return dev_err_probe(dev, -EINVAL, + "Internal reference is not available on current model.\n"); + + if (ref_sel == AD7173_SETUP_REF_SEL_EXT_REF2 && !st->info->has_ref2) + return dev_err_probe(dev, -EINVAL, + "External reference 2 is not available on current model.\n"); + + ret = ad7173_get_ref_voltage_milli(st, ref_sel); + if (ret < 0) + return dev_err_probe(dev, ret, "Cannot use reference %u\n", + ref_sel); + + return 0; +} + +static int ad7173_validate_openwire_ain_inputs(struct ad7173_state *st, + bool differential, + unsigned int ain0, + unsigned int ain1) +{ + /* + * If the channel is configured as differential, + * the ad4111 requires specific ains to be used together + */ + if (differential) + return (ain0 % 2) ? (ain0 - 1) == ain1 : (ain0 + 1) == ain1; + + return ain1 == AD4111_VINCOM_INPUT; +} + +static unsigned int ad7173_calc_openwire_thrsh_raw(struct ad7173_state *st, + struct iio_chan_spec *chan, + struct ad7173_channel *chan_st_priv, + unsigned int thrsh_mv) +{ + unsigned int thrsh_raw; + + thrsh_raw = + BIT(chan->scan_type.realbits - !!(chan_st_priv->cfg.bipolar)) + * thrsh_mv + / ad7173_get_ref_voltage_milli(st, chan_st_priv->cfg.ref_sel); + if (chan->channel < st->info->num_voltage_in_div) + thrsh_raw /= AD4111_DIVIDER_RATIO; + + return thrsh_raw; +} + +static int ad7173_fw_parse_channel_config(struct iio_dev *indio_dev) +{ + struct ad7173_channel *chans_st_arr, *chan_st_priv; + struct ad7173_state *st = iio_priv(indio_dev); + struct device *dev = indio_dev->dev.parent; + struct iio_chan_spec *chan_arr, *chan; + unsigned int ain[AD7173_NO_AINS_PER_CHANNEL], chan_index = 0; + int ref_sel, ret, num_channels; + + num_channels = device_get_child_node_count(dev); + + if (st->info->has_temp) + num_channels++; + + if (num_channels == 0) + return dev_err_probe(dev, -ENODATA, "No channels specified\n"); + + if (num_channels > st->info->num_channels) + return dev_err_probe(dev, -EINVAL, + "Too many channels specified. Maximum is %d, not including temperature channel if supported.\n", + st->info->num_channels); + + indio_dev->num_channels = num_channels; + st->num_channels = num_channels; + + chan_arr = devm_kcalloc(dev, sizeof(*indio_dev->channels), + st->num_channels, GFP_KERNEL); + if (!chan_arr) + return -ENOMEM; + + chans_st_arr = devm_kcalloc(dev, st->num_channels, sizeof(*st->channels), + GFP_KERNEL); + if (!chans_st_arr) + return -ENOMEM; + + indio_dev->channels = chan_arr; + st->channels = chans_st_arr; + + if (st->info->has_temp) { + chan_arr[chan_index] = ad7173_temp_iio_channel_template; + chan_st_priv = &chans_st_arr[chan_index]; + chan_st_priv->ain = + AD7173_CH_ADDRESS(chan_arr[chan_index].channel, + chan_arr[chan_index].channel2); + chan_st_priv->cfg.bipolar = false; + chan_st_priv->cfg.input_buf = st->info->has_input_buf; + chan_st_priv->cfg.ref_sel = AD7173_SETUP_REF_SEL_INT_REF; + chan_st_priv->cfg.sinc3_odr_div = ad7173_sinc3_odr_div_from_odr( + st->info->sinc5_data_rates[st->info->odr_start_value] + ); + chan_st_priv->cfg.sinc5_odr_index = st->info->odr_start_value; + chan_st_priv->cfg.filter_type = AD7173_FILTER_SINC5_SINC1; + chan_st_priv->cfg.openwire_comp_chan = -1; + st->adc_mode |= AD7173_ADC_MODE_REF_EN; + if (st->info->data_reg_only_16bit) + chan_arr[chan_index].scan_type = ad4113_scan_type; + + if (ad_sigma_delta_has_spi_offload(&st->sd)) { + chan_arr[chan_index].scan_type.storagebits = 32; + chan_arr[chan_index].scan_type.endianness = IIO_CPU; + } + + chan_index++; + } + + device_for_each_child_node_scoped(dev, child) { + bool is_current_chan = false; + + chan = &chan_arr[chan_index]; + *chan = ad7173_channel_template; + chan_st_priv = &chans_st_arr[chan_index]; + ret = fwnode_property_read_u32_array(child, "diff-channels", + ain, ARRAY_SIZE(ain)); + if (ret) { + ret = fwnode_property_read_u32(child, "single-channel", + ain); + if (ret) + return dev_err_probe(dev, ret, + "Channel must define one of diff-channels or single-channel.\n"); + + is_current_chan = fwnode_property_read_bool(child, "adi,current-channel"); + } else { + chan->differential = true; + } + + if (is_current_chan) { + ret = ad4111_validate_current_ain(st, ain); + if (ret) + return ret; + } else { + if (!chan->differential) { + ret = fwnode_property_read_u32(child, + "common-mode-channel", ain + 1); + if (ret) + return dev_err_probe(dev, ret, + "common-mode-channel must be defined for single-ended channels.\n"); + } + ret = ad7173_validate_voltage_ain_inputs(st, ain[0], ain[1]); + if (ret) + return ret; + } + + ret = fwnode_property_match_property_string(child, + "adi,reference-select", + ad7173_ref_sel_str, + ARRAY_SIZE(ad7173_ref_sel_str)); + if (ret < 0) + ref_sel = AD7173_SETUP_REF_SEL_INT_REF; + else + ref_sel = ret; + + ret = ad7173_validate_reference(st, ref_sel); + if (ret) + return ret; + + if (ref_sel == AD7173_SETUP_REF_SEL_INT_REF) + st->adc_mode |= AD7173_ADC_MODE_REF_EN; + chan_st_priv->cfg.ref_sel = ref_sel; + + chan->address = chan_index; + chan->scan_index = chan_index; + chan->channel = ain[0]; + chan_st_priv->cfg.input_buf = st->info->has_input_buf; + chan_st_priv->cfg.sinc3_odr_div = ad7173_sinc3_odr_div_from_odr( + st->info->sinc5_data_rates[st->info->odr_start_value] + ); + chan_st_priv->cfg.sinc5_odr_index = st->info->odr_start_value; + chan_st_priv->cfg.filter_type = AD7173_FILTER_SINC5_SINC1; + chan_st_priv->cfg.openwire_comp_chan = -1; + + chan_st_priv->cfg.bipolar = fwnode_property_read_bool(child, "bipolar"); + if (chan_st_priv->cfg.bipolar) + chan->info_mask_separate |= BIT(IIO_CHAN_INFO_OFFSET); + + if (is_current_chan) { + chan->type = IIO_CURRENT; + chan->differential = false; + chan->channel2 = 0; + chan_st_priv->ain = ad4111_current_channel_config[ain[0]]; + } else { + chan_st_priv->cfg.input_buf = st->info->has_input_buf; + chan->channel2 = ain[1]; + chan_st_priv->ain = AD7173_CH_ADDRESS(ain[0], ain[1]); + if (st->info->has_openwire_det && + ad7173_validate_openwire_ain_inputs(st, chan->differential, ain[0], ain[1])) { + chan->event_spec = ad4111_events; + chan->num_event_specs = ARRAY_SIZE(ad4111_events); + chan_st_priv->cfg.openwire_thrsh_raw = + ad7173_calc_openwire_thrsh_raw(st, chan, chan_st_priv, + AD4111_OW_DET_THRSH_MV); + } + } + + if (st->info->data_reg_only_16bit) + chan_arr[chan_index].scan_type = ad4113_scan_type; + + /* Assuming SPI offload is ad411x_ad717x HDL project. */ + if (ad_sigma_delta_has_spi_offload(&st->sd)) { + chan_arr[chan_index].scan_type.storagebits = 32; + chan_arr[chan_index].scan_type.endianness = IIO_CPU; + } + + chan_index++; + } + return 0; +} + +static int ad7173_fw_parse_device_config(struct iio_dev *indio_dev) +{ + struct ad7173_state *st = iio_priv(indio_dev); + struct device *dev = indio_dev->dev.parent; + int ret; + + st->regulators[0].supply = ad7173_ref_sel_str[AD7173_SETUP_REF_SEL_EXT_REF]; + st->regulators[1].supply = ad7173_ref_sel_str[AD7173_SETUP_REF_SEL_EXT_REF2]; + st->regulators[2].supply = ad7173_ref_sel_str[AD7173_SETUP_REF_SEL_AVDD1_AVSS]; + + /* + * If a regulator is not available, it will be set to a dummy regulator. + * Each channel reference is checked with regulator_get_voltage() before + * setting attributes so if any channel uses a dummy supply the driver + * probe will fail. + */ + ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(st->regulators), + st->regulators); + if (ret) + return dev_err_probe(dev, ret, "Failed to get regulators\n"); + + ret = regulator_bulk_enable(ARRAY_SIZE(st->regulators), st->regulators); + if (ret) + return dev_err_probe(dev, ret, "Failed to enable regulators\n"); + + ret = devm_add_action_or_reset(dev, ad7173_disable_regulators, st); + if (ret) + return ret; + + ret = device_property_match_property_string(dev, "clock-names", + ad7173_clk_sel, + ARRAY_SIZE(ad7173_clk_sel)); + if (ret < 0) { + st->adc_mode |= FIELD_PREP(AD7173_ADC_MODE_CLOCKSEL_MASK, + AD7173_ADC_MODE_CLOCKSEL_INT); + ad7173_register_clk_provider(indio_dev); + } else { + struct clk *clk; + + st->adc_mode |= FIELD_PREP(AD7173_ADC_MODE_CLOCKSEL_MASK, + AD7173_ADC_MODE_CLOCKSEL_EXT + ret); + clk = devm_clk_get_enabled(dev, ad7173_clk_sel[ret]); + if (IS_ERR(clk)) + return dev_err_probe(dev, PTR_ERR(clk), + "Failed to get external clock\n"); + } + + return ad7173_fw_parse_channel_config(indio_dev); +} + +static int ad7173_probe(struct spi_device *spi) +{ + struct device *dev = &spi->dev; + struct ad7173_state *st; + struct iio_dev *indio_dev; + int ret; + + indio_dev = devm_iio_device_alloc(dev, sizeof(*st)); + if (!indio_dev) + return -ENOMEM; + + st = iio_priv(indio_dev); + st->info = spi_get_device_match_data(spi); + if (!st->info) + return -ENODEV; + + ida_init(&st->cfg_slots_status); + ret = devm_add_action_or_reset(dev, ad7173_ida_destroy, st); + if (ret) + return ret; + + indio_dev->name = st->info->name; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->info = &ad7173_info; + + spi->mode = SPI_MODE_3; + ret = spi_setup(spi); + if (ret) + return ret; + + ret = ad_sd_init(&st->sd, indio_dev, spi, st->info->sd_info); + if (ret) + return ret; + + ret = ad7173_fw_parse_device_config(indio_dev); + if (ret) + return ret; + + ret = devm_ad_sd_setup_buffer_and_trigger(dev, indio_dev); + if (ret) + return ret; + + ret = ad7173_setup(indio_dev); + if (ret) + return ret; + + ret = devm_iio_device_register(dev, indio_dev); + if (ret) + return ret; + + return ad7173_gpio_init(st); +} + +static const struct of_device_id ad7173_of_match[] = { + { .compatible = "adi,ad4111", .data = &ad4111_device_info }, + { .compatible = "adi,ad4112", .data = &ad4112_device_info }, + { .compatible = "adi,ad4113", .data = &ad4113_device_info }, + { .compatible = "adi,ad4114", .data = &ad4114_device_info }, + { .compatible = "adi,ad4115", .data = &ad4115_device_info }, + { .compatible = "adi,ad4116", .data = &ad4116_device_info }, + { .compatible = "adi,ad7172-2", .data = &ad7172_2_device_info }, + { .compatible = "adi,ad7172-4", .data = &ad7172_4_device_info }, + { .compatible = "adi,ad7173-8", .data = &ad7173_8_device_info }, + { .compatible = "adi,ad7175-2", .data = &ad7175_2_device_info }, + { .compatible = "adi,ad7175-8", .data = &ad7175_8_device_info }, + { .compatible = "adi,ad7176-2", .data = &ad7176_2_device_info }, + { .compatible = "adi,ad7177-2", .data = &ad7177_2_device_info }, + { } +}; +MODULE_DEVICE_TABLE(of, ad7173_of_match); + +static const struct spi_device_id ad7173_id_table[] = { + { .name = "ad4111", .driver_data = (kernel_ulong_t)&ad4111_device_info }, + { .name = "ad4112", .driver_data = (kernel_ulong_t)&ad4112_device_info }, + { .name = "ad4113", .driver_data = (kernel_ulong_t)&ad4113_device_info }, + { .name = "ad4114", .driver_data = (kernel_ulong_t)&ad4114_device_info }, + { .name = "ad4115", .driver_data = (kernel_ulong_t)&ad4115_device_info }, + { .name = "ad4116", .driver_data = (kernel_ulong_t)&ad4116_device_info }, + { .name = "ad7172-2", .driver_data = (kernel_ulong_t)&ad7172_2_device_info }, + { .name = "ad7172-4", .driver_data = (kernel_ulong_t)&ad7172_4_device_info }, + { .name = "ad7173-8", .driver_data = (kernel_ulong_t)&ad7173_8_device_info }, + { .name = "ad7175-2", .driver_data = (kernel_ulong_t)&ad7175_2_device_info }, + { .name = "ad7175-8", .driver_data = (kernel_ulong_t)&ad7175_8_device_info }, + { .name = "ad7176-2", .driver_data = (kernel_ulong_t)&ad7176_2_device_info }, + { .name = "ad7177-2", .driver_data = (kernel_ulong_t)&ad7177_2_device_info }, + { } +}; +MODULE_DEVICE_TABLE(spi, ad7173_id_table); + +static struct spi_driver ad7173_driver = { + .driver = { + .name = "ad7173", + .of_match_table = ad7173_of_match, + }, + .probe = ad7173_probe, + .id_table = ad7173_id_table, +}; +module_spi_driver(ad7173_driver); + +MODULE_IMPORT_NS("IIO_AD_SIGMA_DELTA"); +MODULE_AUTHOR("Lars-Peter Clausen "); +MODULE_AUTHOR("Dumitru Ceclan "); +MODULE_DESCRIPTION("Analog Devices AD7173 and similar ADC driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/iio/adc/ad7191.c b/drivers/iio/adc/ad7191.c new file mode 100644 index 000000000..071d26191 --- /dev/null +++ b/drivers/iio/adc/ad7191.c @@ -0,0 +1,556 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * AD7191 ADC driver + * + * Copyright 2025 Analog Devices Inc. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#define ad_sigma_delta_to_ad7191(sigmad) \ + container_of((sigmad), struct ad7191_state, sd) + +#define AD7191_TEMP_CODES_PER_DEGREE 2815 + +#define AD7191_CHAN_MASK BIT(0) +#define AD7191_TEMP_MASK BIT(1) + +enum ad7191_channel { + AD7191_CH_AIN1_AIN2, + AD7191_CH_AIN3_AIN4, + AD7191_CH_TEMP, +}; + +/* + * NOTE: + * The AD7191 features a dual-use data out ready DOUT/RDY output. + * In order to avoid contentions on the SPI bus, it's therefore necessary + * to use SPI bus locking. + * + * The DOUT/RDY output must also be wired to an interrupt-capable GPIO. + * + * The SPI controller's chip select must be connected to the PDOWN pin + * of the ADC. When CS (PDOWN) is high, it powers down the device and + * resets the internal circuitry. + */ + +struct ad7191_state { + struct ad_sigma_delta sd; + struct mutex lock; /* Protect device state */ + + struct gpio_descs *odr_gpios; + struct gpio_descs *pga_gpios; + struct gpio_desc *temp_gpio; + struct gpio_desc *chan_gpio; + + u16 int_vref_mv; + const u32 (*scale_avail)[2]; + size_t scale_avail_size; + u32 scale_index; + const u32 *samp_freq_avail; + size_t samp_freq_avail_size; + u32 samp_freq_index; + + struct clk *mclk; +}; + +static int ad7191_set_channel(struct ad_sigma_delta *sd, unsigned int address) +{ + struct ad7191_state *st = ad_sigma_delta_to_ad7191(sd); + u8 temp_gpio_val, chan_gpio_val; + + if (!FIELD_FIT(AD7191_CHAN_MASK | AD7191_TEMP_MASK, address)) + return -EINVAL; + + chan_gpio_val = FIELD_GET(AD7191_CHAN_MASK, address); + temp_gpio_val = FIELD_GET(AD7191_TEMP_MASK, address); + + gpiod_set_value(st->chan_gpio, chan_gpio_val); + gpiod_set_value(st->temp_gpio, temp_gpio_val); + + return 0; +} + +static int ad7191_set_cs(struct ad_sigma_delta *sigma_delta, int assert) +{ + struct spi_transfer t = { + .len = 0, + .cs_change = assert, + }; + struct spi_message m; + + spi_message_init_with_transfers(&m, &t, 1); + + return spi_sync_locked(sigma_delta->spi, &m); +} + +static int ad7191_set_mode(struct ad_sigma_delta *sd, + enum ad_sigma_delta_mode mode) +{ + struct ad7191_state *st = ad_sigma_delta_to_ad7191(sd); + + switch (mode) { + case AD_SD_MODE_CONTINUOUS: + case AD_SD_MODE_SINGLE: + return ad7191_set_cs(&st->sd, 1); + case AD_SD_MODE_IDLE: + return ad7191_set_cs(&st->sd, 0); + default: + return -EINVAL; + } +} + +static const struct ad_sigma_delta_info ad7191_sigma_delta_info = { + .set_channel = ad7191_set_channel, + .set_mode = ad7191_set_mode, + .has_registers = false, +}; + +static int ad7191_init_regulators(struct iio_dev *indio_dev) +{ + struct ad7191_state *st = iio_priv(indio_dev); + struct device *dev = &st->sd.spi->dev; + int ret; + + ret = devm_regulator_get_enable(dev, "avdd"); + if (ret) + return dev_err_probe(dev, ret, "Failed to enable specified AVdd supply\n"); + + ret = devm_regulator_get_enable(dev, "dvdd"); + if (ret) + return dev_err_probe(dev, ret, "Failed to enable specified DVdd supply\n"); + + ret = devm_regulator_get_enable_read_voltage(dev, "vref"); + if (ret < 0) + return dev_err_probe(dev, ret, "Failed to get Vref voltage\n"); + + st->int_vref_mv = ret / 1000; + + return 0; +} + +static int ad7191_config_setup(struct iio_dev *indio_dev) +{ + struct ad7191_state *st = iio_priv(indio_dev); + struct device *dev = &st->sd.spi->dev; + /* Sampling frequencies in Hz, see Table 5 */ + static const u32 samp_freq[4] = { 120, 60, 50, 10 }; + /* Gain options, see Table 7 */ + const u32 gain[4] = { 1, 8, 64, 128 }; + static u32 scale_buffer[4][2]; + int odr_value, odr_index = 0, pga_value, pga_index = 0, i, ret; + const char *propname; + u64 scale_uv; + + st->samp_freq_index = 0; + st->scale_index = 0; + + propname = "adi,odr-value"; + if (device_property_present(dev, propname)) { + ret = device_property_read_u32(dev, propname, &odr_value); + if (ret) + return dev_err_probe(dev, ret, "Failed to get %s.\n", propname); + + for (i = 0; i < ARRAY_SIZE(samp_freq); i++) { + if (odr_value != samp_freq[i]) + continue; + odr_index = i; + break; + } + + st->samp_freq_avail = &samp_freq[odr_index]; + st->samp_freq_avail_size = 1; + + st->odr_gpios = NULL; + } else { + st->odr_gpios = devm_gpiod_get_array(dev, "odr", GPIOD_OUT_LOW); + if (IS_ERR(st->odr_gpios)) + return dev_err_probe(dev, PTR_ERR(st->odr_gpios), + "Failed to get odr gpios.\n"); + + if (st->odr_gpios->ndescs != 2) + return dev_err_probe(dev, -EINVAL, "Expected 2 odr gpio pins.\n"); + + st->samp_freq_avail = samp_freq; + st->samp_freq_avail_size = ARRAY_SIZE(samp_freq); + } + + mutex_lock(&st->lock); + + for (i = 0; i < ARRAY_SIZE(scale_buffer); i++) { + scale_uv = ((u64)st->int_vref_mv * NANO) >> + (indio_dev->channels[0].scan_type.realbits - 1); + do_div(scale_uv, gain[i]); + scale_buffer[i][1] = do_div(scale_uv, NANO); + scale_buffer[i][0] = scale_uv; + } + + mutex_unlock(&st->lock); + + propname = "adi,pga-value"; + if (device_property_present(dev, propname)) { + ret = device_property_read_u32(dev, propname, &pga_value); + if (ret) + return dev_err_probe(dev, ret, "Failed to get %s.\n", propname); + + for (i = 0; i < ARRAY_SIZE(gain); i++) { + if (pga_value != gain[i]) + continue; + pga_index = i; + break; + } + + st->scale_avail = &scale_buffer[pga_index]; + st->scale_avail_size = 1; + + st->pga_gpios = NULL; + } else { + st->pga_gpios = devm_gpiod_get_array(dev, "pga", GPIOD_OUT_LOW); + if (IS_ERR(st->pga_gpios)) + return dev_err_probe(dev, PTR_ERR(st->pga_gpios), + "Failed to get pga gpios.\n"); + + if (st->pga_gpios->ndescs != 2) + return dev_err_probe(dev, -EINVAL, "Expected 2 pga gpio pins.\n"); + + st->scale_avail = scale_buffer; + st->scale_avail_size = ARRAY_SIZE(scale_buffer); + } + + st->temp_gpio = devm_gpiod_get(dev, "temp", GPIOD_OUT_LOW); + if (IS_ERR(st->temp_gpio)) + return dev_err_probe(dev, PTR_ERR(st->temp_gpio), + "Failed to get temp gpio.\n"); + + st->chan_gpio = devm_gpiod_get(dev, "chan", GPIOD_OUT_LOW); + if (IS_ERR(st->chan_gpio)) + return dev_err_probe(dev, PTR_ERR(st->chan_gpio), + "Failed to get chan gpio.\n"); + + return 0; +} + +static int ad7191_clock_setup(struct ad7191_state *st) +{ + struct device *dev = &st->sd.spi->dev; + + st->mclk = devm_clk_get_optional_enabled(dev, "mclk"); + if (IS_ERR(st->mclk)) + return dev_err_probe(dev, PTR_ERR(st->mclk), + "Failed to get mclk.\n"); + + return 0; +} + +static int ad7191_setup(struct iio_dev *indio_dev) +{ + struct ad7191_state *st = iio_priv(indio_dev); + int ret; + + ret = ad7191_init_regulators(indio_dev); + if (ret) + return ret; + + ret = ad7191_config_setup(indio_dev); + if (ret) + return ret; + + return ad7191_clock_setup(st); +} + +static int ad7191_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int *val, + int *val2, long m) +{ + struct ad7191_state *st = iio_priv(indio_dev); + + switch (m) { + case IIO_CHAN_INFO_RAW: + return ad_sigma_delta_single_conversion(indio_dev, chan, val); + case IIO_CHAN_INFO_SCALE: + switch (chan->type) { + case IIO_VOLTAGE: { + guard(mutex)(&st->lock); + *val = st->scale_avail[st->scale_index][0]; + *val2 = st->scale_avail[st->scale_index][1]; + return IIO_VAL_INT_PLUS_NANO; + } + case IIO_TEMP: + *val = 0; + *val2 = NANO / AD7191_TEMP_CODES_PER_DEGREE; + return IIO_VAL_INT_PLUS_NANO; + default: + return -EINVAL; + } + case IIO_CHAN_INFO_OFFSET: + *val = -(1 << (chan->scan_type.realbits - 1)); + switch (chan->type) { + case IIO_VOLTAGE: + return IIO_VAL_INT; + case IIO_TEMP: + *val -= 273 * AD7191_TEMP_CODES_PER_DEGREE; + return IIO_VAL_INT; + default: + return -EINVAL; + } + case IIO_CHAN_INFO_SAMP_FREQ: + *val = st->samp_freq_avail[st->samp_freq_index]; + return IIO_VAL_INT; + default: + return -EINVAL; + } +} + +static int ad7191_set_gain(struct ad7191_state *st, int gain_index) +{ + DECLARE_BITMAP(bitmap, 2) = { }; + + st->scale_index = gain_index; + + bitmap_write(bitmap, gain_index, 0, 2); + + return gpiod_multi_set_value_cansleep(st->pga_gpios, bitmap); +} + +static int ad7191_set_samp_freq(struct ad7191_state *st, int samp_freq_index) +{ + DECLARE_BITMAP(bitmap, 2) = {}; + + st->samp_freq_index = samp_freq_index; + + bitmap_write(bitmap, samp_freq_index, 0, 2); + + return gpiod_multi_set_value_cansleep(st->odr_gpios, bitmap); +} + +static int __ad7191_write_raw(struct ad7191_state *st, + struct iio_chan_spec const *chan, + int val, int val2, long mask) +{ + int i; + + switch (mask) { + case IIO_CHAN_INFO_SCALE: { + if (!st->pga_gpios) + return -EPERM; + guard(mutex)(&st->lock); + for (i = 0; i < st->scale_avail_size; i++) { + if (val2 == st->scale_avail[i][1]) + return ad7191_set_gain(st, i); + } + return -EINVAL; + } + case IIO_CHAN_INFO_SAMP_FREQ: { + if (!st->odr_gpios) + return -EPERM; + guard(mutex)(&st->lock); + for (i = 0; i < st->samp_freq_avail_size; i++) { + if (val == st->samp_freq_avail[i]) + return ad7191_set_samp_freq(st, i); + } + return -EINVAL; + } + default: + return -EINVAL; + } +} + +static int ad7191_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int val, int val2, + long mask) +{ + struct ad7191_state *st = iio_priv(indio_dev); + int ret; + + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; + + ret = __ad7191_write_raw(st, chan, val, val2, mask); + + iio_device_release_direct(indio_dev); + + return ret; +} + +static int ad7191_write_raw_get_fmt(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, long mask) +{ + switch (mask) { + case IIO_CHAN_INFO_SCALE: + return IIO_VAL_INT_PLUS_NANO; + case IIO_CHAN_INFO_SAMP_FREQ: + return IIO_VAL_INT; + default: + return -EINVAL; + } +} + +static int ad7191_read_avail(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, const int **vals, + int *type, int *length, long mask) +{ + struct ad7191_state *st = iio_priv(indio_dev); + + switch (mask) { + case IIO_CHAN_INFO_SCALE: + *vals = (int *)st->scale_avail; + *type = IIO_VAL_INT_PLUS_NANO; + *length = st->scale_avail_size * 2; + return IIO_AVAIL_LIST; + case IIO_CHAN_INFO_SAMP_FREQ: + *vals = (int *)st->samp_freq_avail; + *type = IIO_VAL_INT; + *length = st->samp_freq_avail_size; + return IIO_AVAIL_LIST; + } + + return -EINVAL; +} + +static const struct iio_info ad7191_info = { + .read_raw = ad7191_read_raw, + .write_raw = ad7191_write_raw, + .write_raw_get_fmt = ad7191_write_raw_get_fmt, + .read_avail = ad7191_read_avail, + .validate_trigger = ad_sd_validate_trigger, +}; + +static const struct iio_chan_spec ad7191_channels[] = { + { + .type = IIO_TEMP, + .address = AD7191_CH_TEMP, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_OFFSET) | + BIT(IIO_CHAN_INFO_SAMP_FREQ), + .info_mask_shared_by_all_available = BIT(IIO_CHAN_INFO_SAMP_FREQ), + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), + .scan_type = { + .sign = 'u', + .realbits = 24, + .storagebits = 32, + .endianness = IIO_BE, + }, + }, + { + .type = IIO_VOLTAGE, + .differential = 1, + .indexed = 1, + .channel = 1, + .channel2 = 2, + .address = AD7191_CH_AIN1_AIN2, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_OFFSET) | + BIT(IIO_CHAN_INFO_SAMP_FREQ), + .info_mask_shared_by_all_available = BIT(IIO_CHAN_INFO_SAMP_FREQ), + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), + .info_mask_shared_by_type_available = BIT(IIO_CHAN_INFO_SCALE), + .scan_index = 1, + .scan_type = { + .sign = 'u', + .realbits = 24, + .storagebits = 32, + .endianness = IIO_BE, + }, + }, + { + .type = IIO_VOLTAGE, + .differential = 1, + .indexed = 1, + .channel = 3, + .channel2 = 4, + .address = AD7191_CH_AIN3_AIN4, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_OFFSET) | + BIT(IIO_CHAN_INFO_SAMP_FREQ), + .info_mask_shared_by_all_available = BIT(IIO_CHAN_INFO_SAMP_FREQ), + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), + .info_mask_shared_by_type_available = BIT(IIO_CHAN_INFO_SCALE), + .scan_index = 2, + .scan_type = { + .sign = 'u', + .realbits = 24, + .storagebits = 32, + .endianness = IIO_BE, + }, + }, + IIO_CHAN_SOFT_TIMESTAMP(3), +}; + +static int ad7191_probe(struct spi_device *spi) +{ + struct device *dev = &spi->dev; + struct ad7191_state *st; + struct iio_dev *indio_dev; + int ret; + + indio_dev = devm_iio_device_alloc(dev, sizeof(*st)); + if (!indio_dev) + return -ENOMEM; + + st = iio_priv(indio_dev); + + ret = devm_mutex_init(dev, &st->lock); + if (ret) + return ret; + + indio_dev->name = "ad7191"; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = ad7191_channels; + indio_dev->num_channels = ARRAY_SIZE(ad7191_channels); + indio_dev->info = &ad7191_info; + + ret = ad_sd_init(&st->sd, indio_dev, spi, &ad7191_sigma_delta_info); + if (ret) + return ret; + + ret = devm_ad_sd_setup_buffer_and_trigger(dev, indio_dev); + if (ret) + return ret; + + ret = ad7191_setup(indio_dev); + if (ret) + return ret; + + return devm_iio_device_register(dev, indio_dev); +} + +static const struct of_device_id ad7191_of_match[] = { + { .compatible = "adi,ad7191", }, + { } +}; +MODULE_DEVICE_TABLE(of, ad7191_of_match); + +static const struct spi_device_id ad7191_id_table[] = { + { .name = "ad7191" }, + { } +}; +MODULE_DEVICE_TABLE(spi, ad7191_id_table); + +static struct spi_driver ad7191_driver = { + .driver = { + .name = "ad7191", + .of_match_table = ad7191_of_match, + }, + .probe = ad7191_probe, + .id_table = ad7191_id_table, +}; +module_spi_driver(ad7191_driver); + +MODULE_AUTHOR("Alisa-Dariana Roman "); +MODULE_DESCRIPTION("Analog Devices AD7191 ADC"); +MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS("IIO_AD_SIGMA_DELTA"); diff --git a/drivers/iio/adc/ad7192.c b/drivers/iio/adc/ad7192.c new file mode 100644 index 000000000..d8f5886fc --- /dev/null +++ b/drivers/iio/adc/ad7192.c @@ -0,0 +1,1473 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * AD7192 and similar SPI ADC driver + * + * Copyright 2011-2015 Analog Devices Inc. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +/* Registers */ +#define AD7192_REG_COMM 0 /* Communications Register (WO, 8-bit) */ +#define AD7192_REG_STAT 0 /* Status Register (RO, 8-bit) */ +#define AD7192_REG_MODE 1 /* Mode Register (RW, 24-bit */ +#define AD7192_REG_CONF 2 /* Configuration Register (RW, 24-bit) */ +#define AD7192_REG_DATA 3 /* Data Register (RO, 24/32-bit) */ +#define AD7192_REG_ID 4 /* ID Register (RO, 8-bit) */ +#define AD7192_REG_GPOCON 5 /* GPOCON Register (RW, 8-bit) */ +#define AD7192_REG_OFFSET 6 /* Offset Register (RW, 16-bit */ + /* (AD7792)/24-bit (AD7192)) */ +#define AD7192_REG_FULLSALE 7 /* Full-Scale Register */ + /* (RW, 16-bit (AD7792)/24-bit (AD7192)) */ + +/* Communications Register Bit Designations (AD7192_REG_COMM) */ +#define AD7192_COMM_WEN BIT(7) /* Write Enable */ +#define AD7192_COMM_WRITE 0 /* Write Operation */ +#define AD7192_COMM_READ BIT(6) /* Read Operation */ +#define AD7192_COMM_ADDR_MASK GENMASK(5, 3) /* Register Address Mask */ +#define AD7192_COMM_CREAD BIT(2) /* Continuous Read of Data Register */ + +/* Status Register Bit Designations (AD7192_REG_STAT) */ +#define AD7192_STAT_RDY BIT(7) /* Ready */ +#define AD7192_STAT_ERR BIT(6) /* Error (Overrange, Underrange) */ +#define AD7192_STAT_NOREF BIT(5) /* Error no external reference */ +#define AD7192_STAT_PARITY BIT(4) /* Parity */ +#define AD7192_STAT_CH3 BIT(2) /* Channel 3 */ +#define AD7192_STAT_CH2 BIT(1) /* Channel 2 */ +#define AD7192_STAT_CH1 BIT(0) /* Channel 1 */ + +/* Mode Register Bit Designations (AD7192_REG_MODE) */ +#define AD7192_MODE_SEL_MASK GENMASK(23, 21) /* Operation Mode Select Mask */ +#define AD7192_MODE_STA_MASK BIT(20) /* Status Register transmission Mask */ +#define AD7192_MODE_CLKSRC_MASK GENMASK(19, 18) /* Clock Source Select Mask */ +#define AD7192_MODE_AVG_MASK GENMASK(17, 16) + /* Fast Settling Filter Average Select Mask (AD7193 only) */ +#define AD7192_MODE_SINC3 BIT(15) /* SINC3 Filter Select */ +#define AD7192_MODE_ENPAR BIT(13) /* Parity Enable */ +#define AD7192_MODE_CLKDIV BIT(12) /* Clock divide by 2 (AD7190/2 only)*/ +#define AD7192_MODE_SCYCLE BIT(11) /* Single cycle conversion */ +#define AD7192_MODE_REJ60 BIT(10) /* 50/60Hz notch filter */ + /* Filter Update Rate Select Mask */ +#define AD7192_MODE_RATE_MASK GENMASK(9, 0) + +/* Mode Register: AD7192_MODE_SEL options */ +#define AD7192_MODE_CONT 0 /* Continuous Conversion Mode */ +#define AD7192_MODE_SINGLE 1 /* Single Conversion Mode */ +#define AD7192_MODE_IDLE 2 /* Idle Mode */ +#define AD7192_MODE_PWRDN 3 /* Power-Down Mode */ +#define AD7192_MODE_CAL_INT_ZERO 4 /* Internal Zero-Scale Calibration */ +#define AD7192_MODE_CAL_INT_FULL 5 /* Internal Full-Scale Calibration */ +#define AD7192_MODE_CAL_SYS_ZERO 6 /* System Zero-Scale Calibration */ +#define AD7192_MODE_CAL_SYS_FULL 7 /* System Full-Scale Calibration */ + +/* Mode Register: AD7192_MODE_CLKSRC options */ +#define AD7192_CLK_EXT_MCLK1_2 0 /* External 4.92 MHz Clock connected*/ + /* from MCLK1 to MCLK2 */ +#define AD7192_CLK_EXT_MCLK2 1 /* External Clock applied to MCLK2 */ +#define AD7192_CLK_INT 2 /* Internal 4.92 MHz Clock not */ + /* available at the MCLK2 pin */ +#define AD7192_CLK_INT_CO 3 /* Internal 4.92 MHz Clock available*/ + /* at the MCLK2 pin */ + +/* Configuration Register Bit Designations (AD7192_REG_CONF) */ + +#define AD7192_CONF_CHOP BIT(23) /* CHOP enable */ +#define AD7192_CONF_ACX BIT(22) /* AC excitation enable(AD7195 only) */ +#define AD7192_CONF_REFSEL BIT(20) /* REFIN1/REFIN2 Reference Select */ +#define AD7192_CONF_CHAN_MASK GENMASK(18, 8) /* Channel select mask */ +#define AD7192_CONF_BURN BIT(7) /* Burnout current enable */ +#define AD7192_CONF_REFDET BIT(6) /* Reference detect enable */ +#define AD7192_CONF_BUF BIT(4) /* Buffered Mode Enable */ +#define AD7192_CONF_UNIPOLAR BIT(3) /* Unipolar/Bipolar Enable */ +#define AD7192_CONF_GAIN_MASK GENMASK(2, 0) /* Gain Select */ + +#define AD7192_CH_AIN1P_AIN2M BIT(0) /* AIN1(+) - AIN2(-) */ +#define AD7192_CH_AIN3P_AIN4M BIT(1) /* AIN3(+) - AIN4(-) */ +#define AD7192_CH_TEMP BIT(2) /* Temp Sensor */ +#define AD7192_CH_AIN2P_AIN2M BIT(3) /* AIN2(+) - AIN2(-) */ +#define AD7192_CH_AIN1 BIT(4) /* AIN1 - AINCOM */ +#define AD7192_CH_AIN2 BIT(5) /* AIN2 - AINCOM */ +#define AD7192_CH_AIN3 BIT(6) /* AIN3 - AINCOM */ +#define AD7192_CH_AIN4 BIT(7) /* AIN4 - AINCOM */ + +#define AD7193_CH_AIN1P_AIN2M 0x001 /* AIN1(+) - AIN2(-) */ +#define AD7193_CH_AIN3P_AIN4M 0x002 /* AIN3(+) - AIN4(-) */ +#define AD7193_CH_AIN5P_AIN6M 0x004 /* AIN5(+) - AIN6(-) */ +#define AD7193_CH_AIN7P_AIN8M 0x008 /* AIN7(+) - AIN8(-) */ +#define AD7193_CH_TEMP 0x100 /* Temp senseor */ +#define AD7193_CH_AIN2P_AIN2M 0x200 /* AIN2(+) - AIN2(-) */ +#define AD7193_CH_AIN1 0x401 /* AIN1 - AINCOM */ +#define AD7193_CH_AIN2 0x402 /* AIN2 - AINCOM */ +#define AD7193_CH_AIN3 0x404 /* AIN3 - AINCOM */ +#define AD7193_CH_AIN4 0x408 /* AIN4 - AINCOM */ +#define AD7193_CH_AIN5 0x410 /* AIN5 - AINCOM */ +#define AD7193_CH_AIN6 0x420 /* AIN6 - AINCOM */ +#define AD7193_CH_AIN7 0x440 /* AIN7 - AINCOM */ +#define AD7193_CH_AIN8 0x480 /* AIN7 - AINCOM */ +#define AD7193_CH_AINCOM 0x600 /* AINCOM - AINCOM */ + +#define AD7194_CH_POS(x) (((x) - 1) << 4) +#define AD7194_CH_NEG(x) ((x) - 1) + +/* 10th bit corresponds to CON18(Pseudo) */ +#define AD7194_CH(p) (BIT(10) | AD7194_CH_POS(p)) + +#define AD7194_DIFF_CH(p, n) (AD7194_CH_POS(p) | AD7194_CH_NEG(n)) +#define AD7194_CH_TEMP 0x100 +#define AD7194_CH_BASE_NR 2 +#define AD7194_CH_AIN_START 1 +#define AD7194_CH_AIN_NR 16 +#define AD7194_CH_MAX_NR 272 + +/* ID Register Bit Designations (AD7192_REG_ID) */ +#define CHIPID_AD7190 0x4 +#define CHIPID_AD7192 0x0 +#define CHIPID_AD7193 0x2 +#define CHIPID_AD7194 0x3 +#define CHIPID_AD7195 0x6 +#define AD7192_ID_MASK GENMASK(3, 0) + +/* GPOCON Register Bit Designations (AD7192_REG_GPOCON) */ +#define AD7192_GPOCON_BPDSW BIT(6) /* Bridge power-down switch enable */ +#define AD7192_GPOCON_GP32EN BIT(5) /* Digital Output P3 and P2 enable */ +#define AD7192_GPOCON_GP10EN BIT(4) /* Digital Output P1 and P0 enable */ +#define AD7192_GPOCON_P3DAT BIT(3) /* P3 state */ +#define AD7192_GPOCON_P2DAT BIT(2) /* P2 state */ +#define AD7192_GPOCON_P1DAT BIT(1) /* P1 state */ +#define AD7192_GPOCON_P0DAT BIT(0) /* P0 state */ + +#define AD7192_EXT_FREQ_MHZ_MIN 2457600 +#define AD7192_EXT_FREQ_MHZ_MAX 5120000 +#define AD7192_INT_FREQ_MHZ 4915200 + +#define AD7192_NO_SYNC_FILTER 1 +#define AD7192_SYNC3_FILTER 3 +#define AD7192_SYNC4_FILTER 4 + +/* NOTE: + * The AD7190/2/5 features a dual use data out ready DOUT/RDY output. + * In order to avoid contentions on the SPI bus, it's therefore necessary + * to use spi bus locking. + * + * The DOUT/RDY output must also be wired to an interrupt capable GPIO. + */ + +enum { + AD7192_SYSCALIB_ZERO_SCALE, + AD7192_SYSCALIB_FULL_SCALE, +}; + +enum { + ID_AD7190, + ID_AD7192, + ID_AD7193, + ID_AD7194, + ID_AD7195, +}; + +struct ad7192_chip_info { + unsigned int chip_id; + const char *name; + const struct iio_chan_spec *channels; + u8 num_channels; + const struct ad_sigma_delta_info *sigma_delta_info; + const struct iio_info *info; + int (*parse_channels)(struct iio_dev *indio_dev); +}; + +struct ad7192_state { + const struct ad7192_chip_info *chip_info; + struct clk *mclk; + struct clk_hw int_clk_hw; + u16 int_vref_mv; + u32 aincom_mv; + u32 fclk; + u32 mode; + u32 conf; + u32 scale_avail[8][2]; + u32 filter_freq_avail[4][2]; + u32 oversampling_ratio_avail[4]; + u8 gpocon; + u8 clock_sel; + struct mutex lock; /* protect sensor state */ + u8 syscalib_mode[8]; + + struct ad_sigma_delta sd; +}; + +static const char * const ad7192_syscalib_modes[] = { + [AD7192_SYSCALIB_ZERO_SCALE] = "zero_scale", + [AD7192_SYSCALIB_FULL_SCALE] = "full_scale", +}; + +static int ad7192_set_syscalib_mode(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + unsigned int mode) +{ + struct ad7192_state *st = iio_priv(indio_dev); + + st->syscalib_mode[chan->channel] = mode; + + return 0; +} + +static int ad7192_get_syscalib_mode(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan) +{ + struct ad7192_state *st = iio_priv(indio_dev); + + return st->syscalib_mode[chan->channel]; +} + +static ssize_t ad7192_write_syscalib(struct iio_dev *indio_dev, + uintptr_t private, + const struct iio_chan_spec *chan, + const char *buf, size_t len) +{ + struct ad7192_state *st = iio_priv(indio_dev); + bool sys_calib; + int ret, temp; + + ret = kstrtobool(buf, &sys_calib); + if (ret) + return ret; + + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; + + temp = st->syscalib_mode[chan->channel]; + if (sys_calib) { + if (temp == AD7192_SYSCALIB_ZERO_SCALE) + ret = ad_sd_calibrate(&st->sd, AD7192_MODE_CAL_SYS_ZERO, + chan->address); + else + ret = ad_sd_calibrate(&st->sd, AD7192_MODE_CAL_SYS_FULL, + chan->address); + } + + iio_device_release_direct(indio_dev); + + return ret ? ret : len; +} + +static const struct iio_enum ad7192_syscalib_mode_enum = { + .items = ad7192_syscalib_modes, + .num_items = ARRAY_SIZE(ad7192_syscalib_modes), + .set = ad7192_set_syscalib_mode, + .get = ad7192_get_syscalib_mode +}; + +static const struct iio_chan_spec_ext_info ad7192_calibsys_ext_info[] = { + { + .name = "sys_calibration", + .write = ad7192_write_syscalib, + .shared = IIO_SEPARATE, + }, + IIO_ENUM("sys_calibration_mode", IIO_SEPARATE, + &ad7192_syscalib_mode_enum), + IIO_ENUM_AVAILABLE("sys_calibration_mode", IIO_SHARED_BY_TYPE, + &ad7192_syscalib_mode_enum), + { } +}; + +static struct ad7192_state *ad_sigma_delta_to_ad7192(struct ad_sigma_delta *sd) +{ + return container_of(sd, struct ad7192_state, sd); +} + +static int ad7192_set_channel(struct ad_sigma_delta *sd, unsigned int channel) +{ + struct ad7192_state *st = ad_sigma_delta_to_ad7192(sd); + + st->conf &= ~AD7192_CONF_CHAN_MASK; + st->conf |= FIELD_PREP(AD7192_CONF_CHAN_MASK, channel); + + return ad_sd_write_reg(&st->sd, AD7192_REG_CONF, 3, st->conf); +} + +static int ad7192_set_mode(struct ad_sigma_delta *sd, + enum ad_sigma_delta_mode mode) +{ + struct ad7192_state *st = ad_sigma_delta_to_ad7192(sd); + + st->mode &= ~AD7192_MODE_SEL_MASK; + st->mode |= FIELD_PREP(AD7192_MODE_SEL_MASK, mode); + + return ad_sd_write_reg(&st->sd, AD7192_REG_MODE, 3, st->mode); +} + +static int ad7192_append_status(struct ad_sigma_delta *sd, bool append) +{ + struct ad7192_state *st = ad_sigma_delta_to_ad7192(sd); + unsigned int mode = st->mode; + int ret; + + mode &= ~AD7192_MODE_STA_MASK; + mode |= FIELD_PREP(AD7192_MODE_STA_MASK, append); + + ret = ad_sd_write_reg(&st->sd, AD7192_REG_MODE, 3, mode); + if (ret < 0) + return ret; + + st->mode = mode; + + return 0; +} + +static int ad7192_disable_all(struct ad_sigma_delta *sd) +{ + struct ad7192_state *st = ad_sigma_delta_to_ad7192(sd); + u32 conf = st->conf; + int ret; + + conf &= ~AD7192_CONF_CHAN_MASK; + + ret = ad_sd_write_reg(&st->sd, AD7192_REG_CONF, 3, conf); + if (ret < 0) + return ret; + + st->conf = conf; + + return 0; +} + +static const struct ad_sigma_delta_info ad7192_sigma_delta_info = { + .set_channel = ad7192_set_channel, + .append_status = ad7192_append_status, + .disable_all = ad7192_disable_all, + .set_mode = ad7192_set_mode, + .has_registers = true, + .addr_shift = 3, + .read_mask = BIT(6), + .status_ch_mask = GENMASK(3, 0), + .num_slots = 4, + .irq_flags = IRQF_TRIGGER_FALLING, + .num_resetclks = 40, +}; + +static const struct ad_sigma_delta_info ad7194_sigma_delta_info = { + .set_channel = ad7192_set_channel, + .append_status = ad7192_append_status, + .disable_all = ad7192_disable_all, + .set_mode = ad7192_set_mode, + .has_registers = true, + .addr_shift = 3, + .read_mask = BIT(6), + .status_ch_mask = GENMASK(3, 0), + .irq_flags = IRQF_TRIGGER_FALLING, + .num_resetclks = 40, +}; + +static const struct ad_sd_calib_data ad7192_calib_arr[8] = { + {AD7192_MODE_CAL_INT_ZERO, AD7192_CH_AIN1}, + {AD7192_MODE_CAL_INT_FULL, AD7192_CH_AIN1}, + {AD7192_MODE_CAL_INT_ZERO, AD7192_CH_AIN2}, + {AD7192_MODE_CAL_INT_FULL, AD7192_CH_AIN2}, + {AD7192_MODE_CAL_INT_ZERO, AD7192_CH_AIN3}, + {AD7192_MODE_CAL_INT_FULL, AD7192_CH_AIN3}, + {AD7192_MODE_CAL_INT_ZERO, AD7192_CH_AIN4}, + {AD7192_MODE_CAL_INT_FULL, AD7192_CH_AIN4} +}; + +static int ad7192_calibrate_all(struct ad7192_state *st) +{ + return ad_sd_calibrate_all(&st->sd, ad7192_calib_arr, + ARRAY_SIZE(ad7192_calib_arr)); +} + +static inline bool ad7192_valid_external_frequency(u32 freq) +{ + return (freq >= AD7192_EXT_FREQ_MHZ_MIN && + freq <= AD7192_EXT_FREQ_MHZ_MAX); +} + +/* + * Position 0 of ad7192_clock_names, xtal, corresponds to clock source + * configuration AD7192_CLK_EXT_MCLK1_2 and position 1, mclk, corresponds to + * AD7192_CLK_EXT_MCLK2 + */ +static const char *const ad7192_clock_names[] = { + "xtal", + "mclk" +}; + +static struct ad7192_state *clk_hw_to_ad7192(struct clk_hw *hw) +{ + return container_of(hw, struct ad7192_state, int_clk_hw); +} + +static unsigned long ad7192_clk_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + return AD7192_INT_FREQ_MHZ; +} + +static int ad7192_clk_output_is_enabled(struct clk_hw *hw) +{ + struct ad7192_state *st = clk_hw_to_ad7192(hw); + + return st->clock_sel == AD7192_CLK_INT_CO; +} + +static int ad7192_clk_prepare(struct clk_hw *hw) +{ + struct ad7192_state *st = clk_hw_to_ad7192(hw); + int ret; + + st->mode &= ~AD7192_MODE_CLKSRC_MASK; + st->mode |= AD7192_CLK_INT_CO; + + ret = ad_sd_write_reg(&st->sd, AD7192_REG_MODE, 3, st->mode); + if (ret) + return ret; + + st->clock_sel = AD7192_CLK_INT_CO; + + return 0; +} + +static void ad7192_clk_unprepare(struct clk_hw *hw) +{ + struct ad7192_state *st = clk_hw_to_ad7192(hw); + int ret; + + st->mode &= ~AD7192_MODE_CLKSRC_MASK; + st->mode |= AD7192_CLK_INT; + + ret = ad_sd_write_reg(&st->sd, AD7192_REG_MODE, 3, st->mode); + if (ret) + return; + + st->clock_sel = AD7192_CLK_INT; +} + +static const struct clk_ops ad7192_int_clk_ops = { + .recalc_rate = ad7192_clk_recalc_rate, + .is_enabled = ad7192_clk_output_is_enabled, + .prepare = ad7192_clk_prepare, + .unprepare = ad7192_clk_unprepare, +}; + +static int ad7192_register_clk_provider(struct ad7192_state *st) +{ + struct device *dev = &st->sd.spi->dev; + struct clk_init_data init = {}; + int ret; + + if (!IS_ENABLED(CONFIG_COMMON_CLK)) + return 0; + + if (!device_property_present(dev, "#clock-cells")) + return 0; + + init.name = devm_kasprintf(dev, GFP_KERNEL, "%s-clk", + fwnode_get_name(dev_fwnode(dev))); + if (!init.name) + return -ENOMEM; + + init.ops = &ad7192_int_clk_ops; + + st->int_clk_hw.init = &init; + ret = devm_clk_hw_register(dev, &st->int_clk_hw); + if (ret) + return ret; + + return devm_of_clk_add_hw_provider(dev, of_clk_hw_simple_get, + &st->int_clk_hw); +} + +static int ad7192_clock_setup(struct ad7192_state *st) +{ + struct device *dev = &st->sd.spi->dev; + int ret; + + /* + * The following two if branches are kept for backward compatibility but + * the use of the two devicetree properties is highly discouraged. Clock + * configuration should be done according to the bindings. + */ + + if (device_property_read_bool(dev, "adi,int-clock-output-enable")) { + st->clock_sel = AD7192_CLK_INT_CO; + st->fclk = AD7192_INT_FREQ_MHZ; + dev_warn(dev, "Property adi,int-clock-output-enable is deprecated! Check bindings!\n"); + return 0; + } + + if (device_property_read_bool(dev, "adi,clock-xtal")) { + st->clock_sel = AD7192_CLK_EXT_MCLK1_2; + st->mclk = devm_clk_get_enabled(dev, "mclk"); + if (IS_ERR(st->mclk)) + return dev_err_probe(dev, PTR_ERR(st->mclk), + "Failed to get mclk\n"); + + st->fclk = clk_get_rate(st->mclk); + if (!ad7192_valid_external_frequency(st->fclk)) + return dev_err_probe(dev, -EINVAL, + "External clock frequency out of bounds\n"); + + dev_warn(dev, "Property adi,clock-xtal is deprecated! Check bindings!\n"); + return 0; + } + + ret = device_property_match_property_string(dev, "clock-names", + ad7192_clock_names, + ARRAY_SIZE(ad7192_clock_names)); + if (ret < 0) { + st->clock_sel = AD7192_CLK_INT; + st->fclk = AD7192_INT_FREQ_MHZ; + + ret = ad7192_register_clk_provider(st); + if (ret) + return dev_err_probe(dev, ret, + "Failed to register clock provider\n"); + return 0; + } + + st->clock_sel = AD7192_CLK_EXT_MCLK1_2 + ret; + + st->mclk = devm_clk_get_enabled(dev, ad7192_clock_names[ret]); + if (IS_ERR(st->mclk)) + return dev_err_probe(dev, PTR_ERR(st->mclk), + "Failed to get clock source\n"); + + st->fclk = clk_get_rate(st->mclk); + if (!ad7192_valid_external_frequency(st->fclk)) + return dev_err_probe(dev, -EINVAL, + "External clock frequency out of bounds\n"); + + return 0; +} + +static int ad7192_setup(struct iio_dev *indio_dev, struct device *dev) +{ + struct ad7192_state *st = iio_priv(indio_dev); + bool rej60_en, refin2_en; + bool buf_en, bipolar, burnout_curr_en; + unsigned long long scale_uv; + int i, ret, id; + + /* reset the serial interface */ + ret = ad_sd_reset(&st->sd); + if (ret < 0) + return ret; + + /* + * Per AD7192 datasheet (Rev. A, page 34, RESET section), allow + * 500 us after a reset before accessing on-chip registers. + */ + fsleep(500); + + /* write/read test for device presence */ + ret = ad_sd_read_reg(&st->sd, AD7192_REG_ID, 1, &id); + if (ret) + return ret; + + id = FIELD_GET(AD7192_ID_MASK, id); + + if (id != st->chip_info->chip_id) + dev_warn(dev, "device ID query failed (0x%X != 0x%X)\n", + id, st->chip_info->chip_id); + + st->mode = FIELD_PREP(AD7192_MODE_SEL_MASK, AD7192_MODE_IDLE) | + FIELD_PREP(AD7192_MODE_CLKSRC_MASK, st->clock_sel) | + FIELD_PREP(AD7192_MODE_RATE_MASK, 480); + + st->conf = FIELD_PREP(AD7192_CONF_GAIN_MASK, 0); + + rej60_en = device_property_read_bool(dev, "adi,rejection-60-Hz-enable"); + if (rej60_en) + st->mode |= AD7192_MODE_REJ60; + + refin2_en = device_property_read_bool(dev, "adi,refin2-pins-enable"); + if (refin2_en && st->chip_info->chip_id != CHIPID_AD7195) + st->conf |= AD7192_CONF_REFSEL; + + st->conf &= ~AD7192_CONF_CHOP; + + buf_en = device_property_read_bool(dev, "adi,buffer-enable"); + if (buf_en) + st->conf |= AD7192_CONF_BUF; + + bipolar = device_property_read_bool(dev, "bipolar"); + if (!bipolar) + st->conf |= AD7192_CONF_UNIPOLAR; + + burnout_curr_en = device_property_read_bool(dev, + "adi,burnout-currents-enable"); + if (burnout_curr_en && buf_en) { + st->conf |= AD7192_CONF_BURN; + } else if (burnout_curr_en) { + dev_warn(dev, + "Can't enable burnout currents: see CHOP or buffer\n"); + } + + ret = ad_sd_write_reg(&st->sd, AD7192_REG_MODE, 3, st->mode); + if (ret) + return ret; + + ret = ad_sd_write_reg(&st->sd, AD7192_REG_CONF, 3, st->conf); + if (ret) + return ret; + + ret = ad7192_calibrate_all(st); + if (ret) + return ret; + + /* Populate available ADC input ranges */ + for (i = 0; i < ARRAY_SIZE(st->scale_avail); i++) { + scale_uv = ((u64)st->int_vref_mv * 100000000) + >> (indio_dev->channels[0].scan_type.realbits - + !FIELD_GET(AD7192_CONF_UNIPOLAR, st->conf)); + scale_uv >>= i; + + st->scale_avail[i][1] = do_div(scale_uv, 100000000) * 10; + st->scale_avail[i][0] = scale_uv; + } + + st->oversampling_ratio_avail[0] = 1; + st->oversampling_ratio_avail[1] = 2; + st->oversampling_ratio_avail[2] = 8; + st->oversampling_ratio_avail[3] = 16; + + st->filter_freq_avail[0][0] = 600; + st->filter_freq_avail[1][0] = 800; + st->filter_freq_avail[2][0] = 2300; + st->filter_freq_avail[3][0] = 2720; + + st->filter_freq_avail[0][1] = 1000; + st->filter_freq_avail[1][1] = 1000; + st->filter_freq_avail[2][1] = 1000; + st->filter_freq_avail[3][1] = 1000; + + return 0; +} + +static ssize_t ad7192_show_ac_excitation(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct iio_dev *indio_dev = dev_to_iio_dev(dev); + struct ad7192_state *st = iio_priv(indio_dev); + + return sysfs_emit(buf, "%ld\n", FIELD_GET(AD7192_CONF_ACX, st->conf)); +} + +static ssize_t ad7192_show_bridge_switch(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct iio_dev *indio_dev = dev_to_iio_dev(dev); + struct ad7192_state *st = iio_priv(indio_dev); + + return sysfs_emit(buf, "%ld\n", + FIELD_GET(AD7192_GPOCON_BPDSW, st->gpocon)); +} + +static ssize_t ad7192_set(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t len) +{ + struct iio_dev *indio_dev = dev_to_iio_dev(dev); + struct ad7192_state *st = iio_priv(indio_dev); + struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); + int ret; + bool val; + + ret = kstrtobool(buf, &val); + if (ret < 0) + return ret; + + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; + + switch ((u32)this_attr->address) { + case AD7192_REG_GPOCON: + if (val) + st->gpocon |= AD7192_GPOCON_BPDSW; + else + st->gpocon &= ~AD7192_GPOCON_BPDSW; + + ad_sd_write_reg(&st->sd, AD7192_REG_GPOCON, 1, st->gpocon); + break; + case AD7192_REG_CONF: + if (val) + st->conf |= AD7192_CONF_ACX; + else + st->conf &= ~AD7192_CONF_ACX; + + ad_sd_write_reg(&st->sd, AD7192_REG_CONF, 3, st->conf); + break; + default: + ret = -EINVAL; + } + + iio_device_release_direct(indio_dev); + + return ret ? ret : len; +} + +static int ad7192_compute_f_order(struct ad7192_state *st, bool sinc3_en, bool chop_en) +{ + u8 avg_factor_selected, oversampling_ratio; + + avg_factor_selected = FIELD_GET(AD7192_MODE_AVG_MASK, st->mode); + + if (!avg_factor_selected && !chop_en) + return 1; + + oversampling_ratio = st->oversampling_ratio_avail[avg_factor_selected]; + + if (sinc3_en) + return AD7192_SYNC3_FILTER + oversampling_ratio - 1; + + return AD7192_SYNC4_FILTER + oversampling_ratio - 1; +} + +static int ad7192_get_f_order(struct ad7192_state *st) +{ + bool sinc3_en, chop_en; + + sinc3_en = FIELD_GET(AD7192_MODE_SINC3, st->mode); + chop_en = FIELD_GET(AD7192_CONF_CHOP, st->conf); + + return ad7192_compute_f_order(st, sinc3_en, chop_en); +} + +static int ad7192_compute_f_adc(struct ad7192_state *st, bool sinc3_en, + bool chop_en) +{ + unsigned int f_order = ad7192_compute_f_order(st, sinc3_en, chop_en); + + return DIV_ROUND_CLOSEST(st->fclk, + f_order * FIELD_GET(AD7192_MODE_RATE_MASK, st->mode)); +} + +static int ad7192_get_f_adc(struct ad7192_state *st) +{ + unsigned int f_order = ad7192_get_f_order(st); + + return DIV_ROUND_CLOSEST(st->fclk, + f_order * FIELD_GET(AD7192_MODE_RATE_MASK, st->mode)); +} + +static void ad7192_update_filter_freq_avail(struct ad7192_state *st) +{ + unsigned int fadc; + + /* Formulas for filter at page 25 of the datasheet */ + fadc = ad7192_compute_f_adc(st, false, true); + st->filter_freq_avail[0][0] = DIV_ROUND_CLOSEST(fadc * 240, 1024); + + fadc = ad7192_compute_f_adc(st, true, true); + st->filter_freq_avail[1][0] = DIV_ROUND_CLOSEST(fadc * 240, 1024); + + fadc = ad7192_compute_f_adc(st, false, false); + st->filter_freq_avail[2][0] = DIV_ROUND_CLOSEST(fadc * 230, 1024); + + fadc = ad7192_compute_f_adc(st, true, false); + st->filter_freq_avail[3][0] = DIV_ROUND_CLOSEST(fadc * 272, 1024); +} + +static IIO_DEVICE_ATTR(bridge_switch_en, 0644, + ad7192_show_bridge_switch, ad7192_set, + AD7192_REG_GPOCON); + +static IIO_DEVICE_ATTR(ac_excitation_en, 0644, + ad7192_show_ac_excitation, ad7192_set, + AD7192_REG_CONF); + +static struct attribute *ad7192_attributes[] = { + &iio_dev_attr_bridge_switch_en.dev_attr.attr, + NULL +}; + +static const struct attribute_group ad7192_attribute_group = { + .attrs = ad7192_attributes, +}; + +static struct attribute *ad7195_attributes[] = { + &iio_dev_attr_bridge_switch_en.dev_attr.attr, + &iio_dev_attr_ac_excitation_en.dev_attr.attr, + NULL +}; + +static const struct attribute_group ad7195_attribute_group = { + .attrs = ad7195_attributes, +}; + +static unsigned int ad7192_get_temp_scale(bool unipolar) +{ + return unipolar ? 2815 * 2 : 2815; +} + +static int ad7192_set_3db_filter_freq(struct ad7192_state *st, + int val, int val2) +{ + int i, ret, freq; + unsigned int diff_new, diff_old; + int idx = 0; + + diff_old = U32_MAX; + freq = val * 1000 + val2; + + for (i = 0; i < ARRAY_SIZE(st->filter_freq_avail); i++) { + diff_new = abs(freq - st->filter_freq_avail[i][0]); + if (diff_new < diff_old) { + diff_old = diff_new; + idx = i; + } + } + + switch (idx) { + case 0: + st->mode &= ~AD7192_MODE_SINC3; + + st->conf |= AD7192_CONF_CHOP; + break; + case 1: + st->mode |= AD7192_MODE_SINC3; + + st->conf |= AD7192_CONF_CHOP; + break; + case 2: + st->mode &= ~AD7192_MODE_SINC3; + + st->conf &= ~AD7192_CONF_CHOP; + break; + case 3: + st->mode |= AD7192_MODE_SINC3; + + st->conf &= ~AD7192_CONF_CHOP; + break; + } + + ret = ad_sd_write_reg(&st->sd, AD7192_REG_MODE, 3, st->mode); + if (ret < 0) + return ret; + + return ad_sd_write_reg(&st->sd, AD7192_REG_CONF, 3, st->conf); +} + +static int ad7192_get_3db_filter_freq(struct ad7192_state *st) +{ + unsigned int fadc; + + fadc = ad7192_get_f_adc(st); + + if (FIELD_GET(AD7192_CONF_CHOP, st->conf)) + return DIV_ROUND_CLOSEST(fadc * 240, 1024); + if (FIELD_GET(AD7192_MODE_SINC3, st->mode)) + return DIV_ROUND_CLOSEST(fadc * 272, 1024); + else + return DIV_ROUND_CLOSEST(fadc * 230, 1024); +} + +static int ad7192_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, + int *val2, + long m) +{ + struct ad7192_state *st = iio_priv(indio_dev); + bool unipolar = FIELD_GET(AD7192_CONF_UNIPOLAR, st->conf); + u8 gain = FIELD_GET(AD7192_CONF_GAIN_MASK, st->conf); + + switch (m) { + case IIO_CHAN_INFO_RAW: + return ad_sigma_delta_single_conversion(indio_dev, chan, val); + case IIO_CHAN_INFO_SCALE: + switch (chan->type) { + case IIO_VOLTAGE: + mutex_lock(&st->lock); + *val = st->scale_avail[gain][0]; + *val2 = st->scale_avail[gain][1]; + mutex_unlock(&st->lock); + return IIO_VAL_INT_PLUS_NANO; + case IIO_TEMP: + *val = 0; + *val2 = 1000000000 / ad7192_get_temp_scale(unipolar); + return IIO_VAL_INT_PLUS_NANO; + default: + return -EINVAL; + } + case IIO_CHAN_INFO_OFFSET: + if (!unipolar) + *val = -(1 << (chan->scan_type.realbits - 1)); + else + *val = 0; + + switch (chan->type) { + case IIO_VOLTAGE: + /* + * Only applies to pseudo-differential inputs. + * AINCOM voltage has to be converted to "raw" units. + */ + if (st->aincom_mv && !chan->differential) + *val += DIV_ROUND_CLOSEST_ULL((u64)st->aincom_mv * NANO, + st->scale_avail[gain][1]); + return IIO_VAL_INT; + /* Kelvin to Celsius */ + case IIO_TEMP: + *val -= 273 * ad7192_get_temp_scale(unipolar); + return IIO_VAL_INT; + default: + return -EINVAL; + } + case IIO_CHAN_INFO_SAMP_FREQ: + *val = DIV_ROUND_CLOSEST(ad7192_get_f_adc(st), 1024); + return IIO_VAL_INT; + case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY: + *val = ad7192_get_3db_filter_freq(st); + *val2 = 1000; + return IIO_VAL_FRACTIONAL; + case IIO_CHAN_INFO_OVERSAMPLING_RATIO: + *val = st->oversampling_ratio_avail[FIELD_GET(AD7192_MODE_AVG_MASK, st->mode)]; + return IIO_VAL_INT; + } + + return -EINVAL; +} + +static int __ad7192_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, + int val2, + long mask) +{ + struct ad7192_state *st = iio_priv(indio_dev); + int i, div; + unsigned int tmp; + + guard(mutex)(&st->lock); + + switch (mask) { + case IIO_CHAN_INFO_SCALE: + for (i = 0; i < ARRAY_SIZE(st->scale_avail); i++) { + if (val2 != st->scale_avail[i][1]) + continue; + + tmp = st->conf; + st->conf &= ~AD7192_CONF_GAIN_MASK; + st->conf |= FIELD_PREP(AD7192_CONF_GAIN_MASK, i); + if (tmp == st->conf) + return 0; + ad_sd_write_reg(&st->sd, AD7192_REG_CONF, 3, st->conf); + ad7192_calibrate_all(st); + return 0; + } + return -EINVAL; + case IIO_CHAN_INFO_SAMP_FREQ: + if (!val) + return -EINVAL; + + div = st->fclk / (val * ad7192_get_f_order(st) * 1024); + if (div < 1 || div > 1023) + return -EINVAL; + + st->mode &= ~AD7192_MODE_RATE_MASK; + st->mode |= FIELD_PREP(AD7192_MODE_RATE_MASK, div); + ad_sd_write_reg(&st->sd, AD7192_REG_MODE, 3, st->mode); + ad7192_update_filter_freq_avail(st); + return 0; + case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY: + return ad7192_set_3db_filter_freq(st, val, val2 / 1000); + case IIO_CHAN_INFO_OVERSAMPLING_RATIO: + for (i = 0; i < ARRAY_SIZE(st->oversampling_ratio_avail); i++) { + if (val != st->oversampling_ratio_avail[i]) + continue; + + tmp = st->mode; + st->mode &= ~AD7192_MODE_AVG_MASK; + st->mode |= FIELD_PREP(AD7192_MODE_AVG_MASK, i); + if (tmp == st->mode) + return 0; + ad_sd_write_reg(&st->sd, AD7192_REG_MODE, 3, st->mode); + ad7192_update_filter_freq_avail(st); + return 0; + } + return -EINVAL; + default: + return -EINVAL; + } +} + +static int ad7192_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, + int val2, + long mask) +{ + int ret; + + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; + + ret = __ad7192_write_raw(indio_dev, chan, val, val2, mask); + + iio_device_release_direct(indio_dev); + + return ret; +} + +static int ad7192_write_raw_get_fmt(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + long mask) +{ + switch (mask) { + case IIO_CHAN_INFO_SCALE: + return IIO_VAL_INT_PLUS_NANO; + case IIO_CHAN_INFO_SAMP_FREQ: + return IIO_VAL_INT; + case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY: + return IIO_VAL_INT_PLUS_MICRO; + case IIO_CHAN_INFO_OVERSAMPLING_RATIO: + return IIO_VAL_INT; + default: + return -EINVAL; + } +} + +static int ad7192_read_avail(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + const int **vals, int *type, int *length, + long mask) +{ + struct ad7192_state *st = iio_priv(indio_dev); + + switch (mask) { + case IIO_CHAN_INFO_SCALE: + *vals = (int *)st->scale_avail; + *type = IIO_VAL_INT_PLUS_NANO; + /* Values are stored in a 2D matrix */ + *length = ARRAY_SIZE(st->scale_avail) * 2; + + return IIO_AVAIL_LIST; + case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY: + *vals = (int *)st->filter_freq_avail; + *type = IIO_VAL_FRACTIONAL; + *length = ARRAY_SIZE(st->filter_freq_avail) * 2; + + return IIO_AVAIL_LIST; + case IIO_CHAN_INFO_OVERSAMPLING_RATIO: + *vals = (int *)st->oversampling_ratio_avail; + *type = IIO_VAL_INT; + *length = ARRAY_SIZE(st->oversampling_ratio_avail); + + return IIO_AVAIL_LIST; + } + + return -EINVAL; +} + +static int ad7192_update_scan_mode(struct iio_dev *indio_dev, const unsigned long *scan_mask) +{ + struct ad7192_state *st = iio_priv(indio_dev); + u32 conf = st->conf; + int ret; + int i; + + conf &= ~AD7192_CONF_CHAN_MASK; + for_each_set_bit(i, scan_mask, 8) + conf |= FIELD_PREP(AD7192_CONF_CHAN_MASK, BIT(i)); + + ret = ad_sd_write_reg(&st->sd, AD7192_REG_CONF, 3, conf); + if (ret < 0) + return ret; + + st->conf = conf; + + return 0; +} + +static const struct iio_info ad7192_info = { + .read_raw = ad7192_read_raw, + .write_raw = ad7192_write_raw, + .write_raw_get_fmt = ad7192_write_raw_get_fmt, + .read_avail = ad7192_read_avail, + .attrs = &ad7192_attribute_group, + .validate_trigger = ad_sd_validate_trigger, + .update_scan_mode = ad7192_update_scan_mode, +}; + +static const struct iio_info ad7194_info = { + .read_raw = ad7192_read_raw, + .write_raw = ad7192_write_raw, + .write_raw_get_fmt = ad7192_write_raw_get_fmt, + .read_avail = ad7192_read_avail, + .validate_trigger = ad_sd_validate_trigger, +}; + +static const struct iio_info ad7195_info = { + .read_raw = ad7192_read_raw, + .write_raw = ad7192_write_raw, + .write_raw_get_fmt = ad7192_write_raw_get_fmt, + .read_avail = ad7192_read_avail, + .attrs = &ad7195_attribute_group, + .validate_trigger = ad_sd_validate_trigger, + .update_scan_mode = ad7192_update_scan_mode, +}; + +#define __AD719x_CHANNEL(_si, _channel1, _channel2, _address, _type, \ + _mask_all, _mask_type_av, _mask_all_av, _ext_info) \ + { \ + .type = (_type), \ + .differential = ((_channel2) == -1 ? 0 : 1), \ + .indexed = 1, \ + .channel = (_channel1), \ + .channel2 = (_channel2), \ + .address = (_address), \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ + BIT(IIO_CHAN_INFO_OFFSET), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ + .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ) | \ + BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY) | \ + (_mask_all), \ + .info_mask_shared_by_type_available = (_mask_type_av), \ + .info_mask_shared_by_all_available = \ + BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY) | \ + (_mask_all_av), \ + .ext_info = (_ext_info), \ + .scan_index = (_si), \ + .scan_type = { \ + .sign = 'u', \ + .realbits = 24, \ + .storagebits = 32, \ + .endianness = IIO_BE, \ + }, \ + } + +#define AD719x_DIFF_CHANNEL(_si, _channel1, _channel2, _address) \ + __AD719x_CHANNEL(_si, _channel1, _channel2, _address, IIO_VOLTAGE, 0, \ + BIT(IIO_CHAN_INFO_SCALE), 0, ad7192_calibsys_ext_info) + +#define AD719x_CHANNEL(_si, _channel1, _address) \ + __AD719x_CHANNEL(_si, _channel1, -1, _address, IIO_VOLTAGE, 0, \ + BIT(IIO_CHAN_INFO_SCALE), 0, ad7192_calibsys_ext_info) + +#define AD719x_TEMP_CHANNEL(_si, _address) \ + __AD719x_CHANNEL(_si, 0, -1, _address, IIO_TEMP, 0, 0, 0, NULL) + +#define AD7193_DIFF_CHANNEL(_si, _channel1, _channel2, _address) \ + __AD719x_CHANNEL(_si, _channel1, _channel2, _address, \ + IIO_VOLTAGE, \ + BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \ + BIT(IIO_CHAN_INFO_SCALE), \ + BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \ + ad7192_calibsys_ext_info) + +#define AD7193_CHANNEL(_si, _channel1, _address) \ + AD7193_DIFF_CHANNEL(_si, _channel1, -1, _address) + +static const struct iio_chan_spec ad7192_channels[] = { + AD719x_DIFF_CHANNEL(0, 1, 2, AD7192_CH_AIN1P_AIN2M), + AD719x_DIFF_CHANNEL(1, 3, 4, AD7192_CH_AIN3P_AIN4M), + AD719x_TEMP_CHANNEL(2, AD7192_CH_TEMP), + AD719x_DIFF_CHANNEL(3, 2, 2, AD7192_CH_AIN2P_AIN2M), + AD719x_CHANNEL(4, 1, AD7192_CH_AIN1), + AD719x_CHANNEL(5, 2, AD7192_CH_AIN2), + AD719x_CHANNEL(6, 3, AD7192_CH_AIN3), + AD719x_CHANNEL(7, 4, AD7192_CH_AIN4), + IIO_CHAN_SOFT_TIMESTAMP(8), +}; + +static const struct iio_chan_spec ad7193_channels[] = { + AD7193_DIFF_CHANNEL(0, 1, 2, AD7193_CH_AIN1P_AIN2M), + AD7193_DIFF_CHANNEL(1, 3, 4, AD7193_CH_AIN3P_AIN4M), + AD7193_DIFF_CHANNEL(2, 5, 6, AD7193_CH_AIN5P_AIN6M), + AD7193_DIFF_CHANNEL(3, 7, 8, AD7193_CH_AIN7P_AIN8M), + AD719x_TEMP_CHANNEL(4, AD7193_CH_TEMP), + AD7193_DIFF_CHANNEL(5, 2, 2, AD7193_CH_AIN2P_AIN2M), + AD7193_CHANNEL(6, 1, AD7193_CH_AIN1), + AD7193_CHANNEL(7, 2, AD7193_CH_AIN2), + AD7193_CHANNEL(8, 3, AD7193_CH_AIN3), + AD7193_CHANNEL(9, 4, AD7193_CH_AIN4), + AD7193_CHANNEL(10, 5, AD7193_CH_AIN5), + AD7193_CHANNEL(11, 6, AD7193_CH_AIN6), + AD7193_CHANNEL(12, 7, AD7193_CH_AIN7), + AD7193_CHANNEL(13, 8, AD7193_CH_AIN8), + IIO_CHAN_SOFT_TIMESTAMP(14), +}; + +static bool ad7194_validate_ain_channel(struct device *dev, u32 ain) +{ + return in_range(ain, AD7194_CH_AIN_START, AD7194_CH_AIN_NR); +} + +static int ad7194_parse_channels(struct iio_dev *indio_dev) +{ + struct device *dev = indio_dev->dev.parent; + struct iio_chan_spec *ad7194_channels; + const struct iio_chan_spec ad7194_chan = AD7193_CHANNEL(0, 0, 0); + const struct iio_chan_spec ad7194_chan_diff = AD7193_DIFF_CHANNEL(0, 0, 0, 0); + const struct iio_chan_spec ad7194_chan_temp = AD719x_TEMP_CHANNEL(0, 0); + const struct iio_chan_spec ad7194_chan_timestamp = IIO_CHAN_SOFT_TIMESTAMP(0); + unsigned int num_channels, index = 0; + u32 ain[2]; + int ret; + + num_channels = device_get_child_node_count(dev); + if (num_channels > AD7194_CH_MAX_NR) + return dev_err_probe(dev, -EINVAL, "Too many channels: %u\n", + num_channels); + + num_channels += AD7194_CH_BASE_NR; + + ad7194_channels = devm_kcalloc(dev, num_channels, + sizeof(*ad7194_channels), GFP_KERNEL); + if (!ad7194_channels) + return -ENOMEM; + + indio_dev->channels = ad7194_channels; + indio_dev->num_channels = num_channels; + + device_for_each_child_node_scoped(dev, child) { + ret = fwnode_property_read_u32_array(child, "diff-channels", + ain, ARRAY_SIZE(ain)); + if (ret == 0) { + if (!ad7194_validate_ain_channel(dev, ain[0])) + return dev_err_probe(dev, -EINVAL, + "Invalid AIN channel: %u\n", + ain[0]); + + if (!ad7194_validate_ain_channel(dev, ain[1])) + return dev_err_probe(dev, -EINVAL, + "Invalid AIN channel: %u\n", + ain[1]); + + *ad7194_channels = ad7194_chan_diff; + ad7194_channels->scan_index = index++; + ad7194_channels->channel = ain[0]; + ad7194_channels->channel2 = ain[1]; + ad7194_channels->address = AD7194_DIFF_CH(ain[0], ain[1]); + } else { + ret = fwnode_property_read_u32(child, "single-channel", + &ain[0]); + if (ret) + return dev_err_probe(dev, ret, + "Missing channel property\n"); + + if (!ad7194_validate_ain_channel(dev, ain[0])) + return dev_err_probe(dev, -EINVAL, + "Invalid AIN channel: %u\n", + ain[0]); + + *ad7194_channels = ad7194_chan; + ad7194_channels->scan_index = index++; + ad7194_channels->channel = ain[0]; + ad7194_channels->address = AD7194_CH(ain[0]); + } + ad7194_channels++; + } + + *ad7194_channels = ad7194_chan_temp; + ad7194_channels->scan_index = index++; + ad7194_channels->address = AD7194_CH_TEMP; + ad7194_channels++; + + *ad7194_channels = ad7194_chan_timestamp; + ad7194_channels->scan_index = index; + + return 0; +} + +static const struct ad7192_chip_info ad7192_chip_info_tbl[] = { + [ID_AD7190] = { + .chip_id = CHIPID_AD7190, + .name = "ad7190", + .channels = ad7192_channels, + .num_channels = ARRAY_SIZE(ad7192_channels), + .sigma_delta_info = &ad7192_sigma_delta_info, + .info = &ad7192_info, + }, + [ID_AD7192] = { + .chip_id = CHIPID_AD7192, + .name = "ad7192", + .channels = ad7192_channels, + .num_channels = ARRAY_SIZE(ad7192_channels), + .sigma_delta_info = &ad7192_sigma_delta_info, + .info = &ad7192_info, + }, + [ID_AD7193] = { + .chip_id = CHIPID_AD7193, + .name = "ad7193", + .channels = ad7193_channels, + .num_channels = ARRAY_SIZE(ad7193_channels), + .sigma_delta_info = &ad7192_sigma_delta_info, + .info = &ad7192_info, + }, + [ID_AD7194] = { + .chip_id = CHIPID_AD7194, + .name = "ad7194", + .info = &ad7194_info, + .sigma_delta_info = &ad7194_sigma_delta_info, + .parse_channels = ad7194_parse_channels, + }, + [ID_AD7195] = { + .chip_id = CHIPID_AD7195, + .name = "ad7195", + .channels = ad7192_channels, + .num_channels = ARRAY_SIZE(ad7192_channels), + .sigma_delta_info = &ad7192_sigma_delta_info, + .info = &ad7195_info, + }, +}; + +static int ad7192_probe(struct spi_device *spi) +{ + struct device *dev = &spi->dev; + struct ad7192_state *st; + struct iio_dev *indio_dev; + int ret, avdd_mv; + + if (!spi->irq) + return dev_err_probe(dev, -ENODEV, "Failed to get IRQ\n"); + + indio_dev = devm_iio_device_alloc(dev, sizeof(*st)); + if (!indio_dev) + return -ENOMEM; + + st = iio_priv(indio_dev); + + mutex_init(&st->lock); + + /* + * Regulator aincom is optional to maintain compatibility with older DT. + * Newer firmware should provide a zero volt fixed supply if wired to + * ground. + */ + ret = devm_regulator_get_enable_read_voltage(dev, "aincom"); + if (ret < 0 && ret != -ENODEV) + return dev_err_probe(dev, ret, "Failed to get AINCOM voltage\n"); + + st->aincom_mv = ret == -ENODEV ? 0 : ret / MILLI; + + /* AVDD can optionally be used as reference voltage */ + ret = devm_regulator_get_enable_read_voltage(dev, "avdd"); + if (ret == -ENODEV || ret == -EINVAL) { + int ret2; + + /* + * We get -EINVAL if avdd is a supply with unknown voltage. We + * still need to enable it since it is also a power supply. + */ + ret2 = devm_regulator_get_enable(dev, "avdd"); + if (ret2) + return dev_err_probe(dev, ret2, + "Failed to enable AVDD supply\n"); + } else if (ret < 0) { + return dev_err_probe(dev, ret, "Failed to get AVDD voltage\n"); + } + + avdd_mv = ret == -ENODEV || ret == -EINVAL ? 0 : ret / MILLI; + + ret = devm_regulator_get_enable(dev, "dvdd"); + if (ret) + return dev_err_probe(dev, ret, "Failed to enable specified DVdd supply\n"); + + /* + * This is either REFIN1 or REFIN2 depending on adi,refin2-pins-enable. + * If this supply is not present, fall back to AVDD as reference. + */ + ret = devm_regulator_get_enable_read_voltage(dev, "vref"); + if (ret == -ENODEV) { + if (avdd_mv == 0) + return dev_err_probe(dev, -ENODEV, + "No reference voltage available\n"); + } else if (ret < 0) { + return ret; + } + + st->int_vref_mv = ret == -ENODEV ? avdd_mv : ret / MILLI; + + st->chip_info = spi_get_device_match_data(spi); + indio_dev->name = st->chip_info->name; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->info = st->chip_info->info; + if (st->chip_info->parse_channels) { + ret = st->chip_info->parse_channels(indio_dev); + if (ret) + return ret; + } else { + indio_dev->channels = st->chip_info->channels; + indio_dev->num_channels = st->chip_info->num_channels; + } + + ret = ad_sd_init(&st->sd, indio_dev, spi, st->chip_info->sigma_delta_info); + if (ret) + return ret; + + ret = devm_ad_sd_setup_buffer_and_trigger(dev, indio_dev); + if (ret) + return ret; + + ret = ad7192_clock_setup(st); + if (ret) + return ret; + + ret = ad7192_setup(indio_dev, dev); + if (ret) + return ret; + + return devm_iio_device_register(dev, indio_dev); +} + +static const struct of_device_id ad7192_of_match[] = { + { .compatible = "adi,ad7190", .data = &ad7192_chip_info_tbl[ID_AD7190] }, + { .compatible = "adi,ad7192", .data = &ad7192_chip_info_tbl[ID_AD7192] }, + { .compatible = "adi,ad7193", .data = &ad7192_chip_info_tbl[ID_AD7193] }, + { .compatible = "adi,ad7194", .data = &ad7192_chip_info_tbl[ID_AD7194] }, + { .compatible = "adi,ad7195", .data = &ad7192_chip_info_tbl[ID_AD7195] }, + { } +}; +MODULE_DEVICE_TABLE(of, ad7192_of_match); + +static const struct spi_device_id ad7192_ids[] = { + { .name = "ad7190", .driver_data = (kernel_ulong_t)&ad7192_chip_info_tbl[ID_AD7190] }, + { .name = "ad7192", .driver_data = (kernel_ulong_t)&ad7192_chip_info_tbl[ID_AD7192] }, + { .name = "ad7193", .driver_data = (kernel_ulong_t)&ad7192_chip_info_tbl[ID_AD7193] }, + { .name = "ad7194", .driver_data = (kernel_ulong_t)&ad7192_chip_info_tbl[ID_AD7194] }, + { .name = "ad7195", .driver_data = (kernel_ulong_t)&ad7192_chip_info_tbl[ID_AD7195] }, + { } +}; +MODULE_DEVICE_TABLE(spi, ad7192_ids); + +static struct spi_driver ad7192_driver = { + .driver = { + .name = "ad7192", + .of_match_table = ad7192_of_match, + }, + .probe = ad7192_probe, + .id_table = ad7192_ids, +}; +module_spi_driver(ad7192_driver); + +MODULE_AUTHOR("Michael Hennerich "); +MODULE_DESCRIPTION("Analog Devices AD7192 and similar ADC"); +MODULE_LICENSE("GPL v2"); +MODULE_IMPORT_NS("IIO_AD_SIGMA_DELTA"); diff --git a/drivers/iio/adc/ad7266.c b/drivers/iio/adc/ad7266.c new file mode 100644 index 000000000..594a070c3 --- /dev/null +++ b/drivers/iio/adc/ad7266.c @@ -0,0 +1,473 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * AD7266/65 SPI ADC driver + * + * Copyright 2012 Analog Devices Inc. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include + +#include + +#define AD7266_INTERNAL_REF_MV 2500 + +struct ad7266_state { + struct spi_device *spi; + unsigned long vref_mv; + + struct spi_transfer single_xfer[3]; + struct spi_message single_msg; + + enum ad7266_range range; + enum ad7266_mode mode; + bool fixed_addr; + struct gpio_desc *gpios[3]; + + /* + * DMA (thus cache coherency maintenance) may require the + * transfer buffers to live in their own cache lines. + * The buffer needs to be large enough to hold two samples (4 bytes) and + * the naturally aligned timestamp (8 bytes). + */ + struct { + __be16 sample[2]; + aligned_s64 timestamp; + } data __aligned(IIO_DMA_MINALIGN); +}; + +static int ad7266_wakeup(struct ad7266_state *st) +{ + /* Any read with >= 2 bytes will wake the device */ + return spi_read(st->spi, &st->data.sample[0], 2); +} + +static int ad7266_powerdown(struct ad7266_state *st) +{ + /* Any read with < 2 bytes will powerdown the device */ + return spi_read(st->spi, &st->data.sample[0], 1); +} + +static int ad7266_preenable(struct iio_dev *indio_dev) +{ + struct ad7266_state *st = iio_priv(indio_dev); + return ad7266_wakeup(st); +} + +static int ad7266_postdisable(struct iio_dev *indio_dev) +{ + struct ad7266_state *st = iio_priv(indio_dev); + return ad7266_powerdown(st); +} + +static const struct iio_buffer_setup_ops iio_triggered_buffer_setup_ops = { + .preenable = &ad7266_preenable, + .postdisable = &ad7266_postdisable, +}; + +static irqreturn_t ad7266_trigger_handler(int irq, void *p) +{ + struct iio_poll_func *pf = p; + struct iio_dev *indio_dev = pf->indio_dev; + struct ad7266_state *st = iio_priv(indio_dev); + int ret; + + ret = spi_read(st->spi, st->data.sample, 4); + if (ret == 0) + iio_push_to_buffers_with_ts(indio_dev, &st->data, sizeof(st->data), + pf->timestamp); + + iio_trigger_notify_done(indio_dev->trig); + + return IRQ_HANDLED; +} + +static void ad7266_select_input(struct ad7266_state *st, unsigned int nr) +{ + unsigned int i; + + if (st->fixed_addr) + return; + + switch (st->mode) { + case AD7266_MODE_SINGLE_ENDED: + nr >>= 1; + break; + case AD7266_MODE_PSEUDO_DIFF: + nr |= 1; + break; + case AD7266_MODE_DIFF: + nr &= ~1; + break; + } + + for (i = 0; i < 3; ++i) + gpiod_set_value(st->gpios[i], (bool)(nr & BIT(i))); +} + +static int ad7266_update_scan_mode(struct iio_dev *indio_dev, + const unsigned long *scan_mask) +{ + struct ad7266_state *st = iio_priv(indio_dev); + unsigned int nr = find_first_bit(scan_mask, + iio_get_masklength(indio_dev)); + + ad7266_select_input(st, nr); + + return 0; +} + +static int ad7266_read_single(struct ad7266_state *st, int *val, + unsigned int address) +{ + int ret; + + ad7266_select_input(st, address); + + ret = spi_sync(st->spi, &st->single_msg); + *val = be16_to_cpu(st->data.sample[address % 2]); + + return ret; +} + +static int ad7266_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int *val, int *val2, long m) +{ + struct ad7266_state *st = iio_priv(indio_dev); + unsigned long scale_mv; + int ret; + + switch (m) { + case IIO_CHAN_INFO_RAW: + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; + ret = ad7266_read_single(st, val, chan->address); + iio_device_release_direct(indio_dev); + + if (ret < 0) + return ret; + *val = (*val >> 2) & 0xfff; + if (chan->scan_type.sign == 's') + *val = sign_extend32(*val, + chan->scan_type.realbits - 1); + + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + scale_mv = st->vref_mv; + if (st->mode == AD7266_MODE_DIFF) + scale_mv *= 2; + if (st->range == AD7266_RANGE_2VREF) + scale_mv *= 2; + + *val = scale_mv; + *val2 = chan->scan_type.realbits; + return IIO_VAL_FRACTIONAL_LOG2; + case IIO_CHAN_INFO_OFFSET: + if (st->range == AD7266_RANGE_2VREF && + st->mode != AD7266_MODE_DIFF) + *val = 2048; + else + *val = 0; + return IIO_VAL_INT; + } + return -EINVAL; +} + +#define AD7266_CHAN(_chan, _sign) { \ + .type = IIO_VOLTAGE, \ + .indexed = 1, \ + .channel = (_chan), \ + .address = (_chan), \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) \ + | BIT(IIO_CHAN_INFO_OFFSET), \ + .scan_index = (_chan), \ + .scan_type = { \ + .sign = (_sign), \ + .realbits = 12, \ + .storagebits = 16, \ + .shift = 2, \ + .endianness = IIO_BE, \ + }, \ +} + +#define AD7266_DECLARE_SINGLE_ENDED_CHANNELS(_name, _sign) \ +const struct iio_chan_spec ad7266_channels_##_name[] = { \ + AD7266_CHAN(0, (_sign)), \ + AD7266_CHAN(1, (_sign)), \ + AD7266_CHAN(2, (_sign)), \ + AD7266_CHAN(3, (_sign)), \ + AD7266_CHAN(4, (_sign)), \ + AD7266_CHAN(5, (_sign)), \ + AD7266_CHAN(6, (_sign)), \ + AD7266_CHAN(7, (_sign)), \ + AD7266_CHAN(8, (_sign)), \ + AD7266_CHAN(9, (_sign)), \ + AD7266_CHAN(10, (_sign)), \ + AD7266_CHAN(11, (_sign)), \ + IIO_CHAN_SOFT_TIMESTAMP(13), \ +} + +#define AD7266_DECLARE_SINGLE_ENDED_CHANNELS_FIXED(_name, _sign) \ +const struct iio_chan_spec ad7266_channels_##_name##_fixed[] = { \ + AD7266_CHAN(0, (_sign)), \ + AD7266_CHAN(1, (_sign)), \ + IIO_CHAN_SOFT_TIMESTAMP(2), \ +} + +static AD7266_DECLARE_SINGLE_ENDED_CHANNELS(u, 'u'); +static AD7266_DECLARE_SINGLE_ENDED_CHANNELS(s, 's'); +static AD7266_DECLARE_SINGLE_ENDED_CHANNELS_FIXED(u, 'u'); +static AD7266_DECLARE_SINGLE_ENDED_CHANNELS_FIXED(s, 's'); + +#define AD7266_CHAN_DIFF(_chan, _sign) { \ + .type = IIO_VOLTAGE, \ + .indexed = 1, \ + .channel = (_chan) * 2, \ + .channel2 = (_chan) * 2 + 1, \ + .address = (_chan), \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) \ + | BIT(IIO_CHAN_INFO_OFFSET), \ + .scan_index = (_chan), \ + .scan_type = { \ + .sign = _sign, \ + .realbits = 12, \ + .storagebits = 16, \ + .shift = 2, \ + .endianness = IIO_BE, \ + }, \ + .differential = 1, \ +} + +#define AD7266_DECLARE_DIFF_CHANNELS(_name, _sign) \ +const struct iio_chan_spec ad7266_channels_diff_##_name[] = { \ + AD7266_CHAN_DIFF(0, (_sign)), \ + AD7266_CHAN_DIFF(1, (_sign)), \ + AD7266_CHAN_DIFF(2, (_sign)), \ + AD7266_CHAN_DIFF(3, (_sign)), \ + AD7266_CHAN_DIFF(4, (_sign)), \ + AD7266_CHAN_DIFF(5, (_sign)), \ + IIO_CHAN_SOFT_TIMESTAMP(6), \ +} + +static AD7266_DECLARE_DIFF_CHANNELS(s, 's'); +static AD7266_DECLARE_DIFF_CHANNELS(u, 'u'); + +#define AD7266_DECLARE_DIFF_CHANNELS_FIXED(_name, _sign) \ +const struct iio_chan_spec ad7266_channels_diff_fixed_##_name[] = { \ + AD7266_CHAN_DIFF(0, (_sign)), \ + AD7266_CHAN_DIFF(1, (_sign)), \ + IIO_CHAN_SOFT_TIMESTAMP(2), \ +} + +static AD7266_DECLARE_DIFF_CHANNELS_FIXED(s, 's'); +static AD7266_DECLARE_DIFF_CHANNELS_FIXED(u, 'u'); + +static const struct iio_info ad7266_info = { + .read_raw = &ad7266_read_raw, + .update_scan_mode = &ad7266_update_scan_mode, +}; + +static const unsigned long ad7266_available_scan_masks[] = { + 0x003, + 0x00c, + 0x030, + 0x0c0, + 0x300, + 0xc00, + 0x000, +}; + +static const unsigned long ad7266_available_scan_masks_diff[] = { + 0x003, + 0x00c, + 0x030, + 0x000, +}; + +static const unsigned long ad7266_available_scan_masks_fixed[] = { + 0x003, + 0x000, +}; + +struct ad7266_chan_info { + const struct iio_chan_spec *channels; + unsigned int num_channels; + const unsigned long *scan_masks; +}; + +#define AD7266_CHAN_INFO_INDEX(_differential, _signed, _fixed) \ + (((_differential) << 2) | ((_signed) << 1) | ((_fixed) << 0)) + +static const struct ad7266_chan_info ad7266_chan_infos[] = { + [AD7266_CHAN_INFO_INDEX(0, 0, 0)] = { + .channels = ad7266_channels_u, + .num_channels = ARRAY_SIZE(ad7266_channels_u), + .scan_masks = ad7266_available_scan_masks, + }, + [AD7266_CHAN_INFO_INDEX(0, 0, 1)] = { + .channels = ad7266_channels_u_fixed, + .num_channels = ARRAY_SIZE(ad7266_channels_u_fixed), + .scan_masks = ad7266_available_scan_masks_fixed, + }, + [AD7266_CHAN_INFO_INDEX(0, 1, 0)] = { + .channels = ad7266_channels_s, + .num_channels = ARRAY_SIZE(ad7266_channels_s), + .scan_masks = ad7266_available_scan_masks, + }, + [AD7266_CHAN_INFO_INDEX(0, 1, 1)] = { + .channels = ad7266_channels_s_fixed, + .num_channels = ARRAY_SIZE(ad7266_channels_s_fixed), + .scan_masks = ad7266_available_scan_masks_fixed, + }, + [AD7266_CHAN_INFO_INDEX(1, 0, 0)] = { + .channels = ad7266_channels_diff_u, + .num_channels = ARRAY_SIZE(ad7266_channels_diff_u), + .scan_masks = ad7266_available_scan_masks_diff, + }, + [AD7266_CHAN_INFO_INDEX(1, 0, 1)] = { + .channels = ad7266_channels_diff_fixed_u, + .num_channels = ARRAY_SIZE(ad7266_channels_diff_fixed_u), + .scan_masks = ad7266_available_scan_masks_fixed, + }, + [AD7266_CHAN_INFO_INDEX(1, 1, 0)] = { + .channels = ad7266_channels_diff_s, + .num_channels = ARRAY_SIZE(ad7266_channels_diff_s), + .scan_masks = ad7266_available_scan_masks_diff, + }, + [AD7266_CHAN_INFO_INDEX(1, 1, 1)] = { + .channels = ad7266_channels_diff_fixed_s, + .num_channels = ARRAY_SIZE(ad7266_channels_diff_fixed_s), + .scan_masks = ad7266_available_scan_masks_fixed, + }, +}; + +static void ad7266_init_channels(struct iio_dev *indio_dev) +{ + struct ad7266_state *st = iio_priv(indio_dev); + bool is_differential, is_signed; + const struct ad7266_chan_info *chan_info; + int i; + + is_differential = st->mode != AD7266_MODE_SINGLE_ENDED; + is_signed = (st->range == AD7266_RANGE_2VREF) | + (st->mode == AD7266_MODE_DIFF); + + i = AD7266_CHAN_INFO_INDEX(is_differential, is_signed, st->fixed_addr); + chan_info = &ad7266_chan_infos[i]; + + indio_dev->channels = chan_info->channels; + indio_dev->num_channels = chan_info->num_channels; + indio_dev->available_scan_masks = chan_info->scan_masks; +} + +static const char * const ad7266_gpio_labels[] = { + "ad0", "ad1", "ad2", +}; + +static int ad7266_probe(struct spi_device *spi) +{ + const struct ad7266_platform_data *pdata = dev_get_platdata(&spi->dev); + struct iio_dev *indio_dev; + struct ad7266_state *st; + unsigned int i; + int ret; + + indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st)); + if (indio_dev == NULL) + return -ENOMEM; + + st = iio_priv(indio_dev); + + ret = devm_regulator_get_enable_read_voltage(&spi->dev, "vref"); + if (ret < 0 && ret != -ENODEV) + return ret; + + st->vref_mv = ret == -ENODEV ? AD7266_INTERNAL_REF_MV : ret / 1000; + + if (pdata) { + st->fixed_addr = pdata->fixed_addr; + st->mode = pdata->mode; + st->range = pdata->range; + + if (!st->fixed_addr) { + for (i = 0; i < ARRAY_SIZE(st->gpios); ++i) { + st->gpios[i] = devm_gpiod_get(&spi->dev, + ad7266_gpio_labels[i], + GPIOD_OUT_LOW); + if (IS_ERR(st->gpios[i])) + return PTR_ERR(st->gpios[i]); + } + } + } else { + st->fixed_addr = true; + st->range = AD7266_RANGE_VREF; + st->mode = AD7266_MODE_DIFF; + } + + st->spi = spi; + + indio_dev->name = spi_get_device_id(spi)->name; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->info = &ad7266_info; + + ad7266_init_channels(indio_dev); + + /* wakeup */ + st->single_xfer[0].rx_buf = &st->data.sample[0]; + st->single_xfer[0].len = 2; + st->single_xfer[0].cs_change = 1; + /* conversion */ + st->single_xfer[1].rx_buf = st->data.sample; + st->single_xfer[1].len = 4; + st->single_xfer[1].cs_change = 1; + /* powerdown */ + st->single_xfer[2].tx_buf = &st->data.sample[0]; + st->single_xfer[2].len = 1; + + spi_message_init(&st->single_msg); + spi_message_add_tail(&st->single_xfer[0], &st->single_msg); + spi_message_add_tail(&st->single_xfer[1], &st->single_msg); + spi_message_add_tail(&st->single_xfer[2], &st->single_msg); + + ret = devm_iio_triggered_buffer_setup(&spi->dev, indio_dev, &iio_pollfunc_store_time, + &ad7266_trigger_handler, &iio_triggered_buffer_setup_ops); + if (ret) + return ret; + + return devm_iio_device_register(&spi->dev, indio_dev); +} + +static const struct spi_device_id ad7266_id[] = { + { .name = "ad7265" }, + { .name = "ad7266" }, + { } +}; +MODULE_DEVICE_TABLE(spi, ad7266_id); + +static struct spi_driver ad7266_driver = { + .driver = { + .name = "ad7266", + }, + .probe = ad7266_probe, + .id_table = ad7266_id, +}; +module_spi_driver(ad7266_driver); + +MODULE_AUTHOR("Lars-Peter Clausen "); +MODULE_DESCRIPTION("Analog Devices AD7266/65 ADC"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/adc/ad7280a.c b/drivers/iio/adc/ad7280a.c new file mode 100644 index 000000000..5ae0d694d --- /dev/null +++ b/drivers/iio/adc/ad7280a.c @@ -0,0 +1,1101 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * AD7280A Lithium Ion Battery Monitoring System + * + * Copyright 2011 Analog Devices Inc. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +/* Registers */ + +#define AD7280A_CELL_VOLTAGE_1_REG 0x0 /* D11 to D0, Read only */ +#define AD7280A_CELL_VOLTAGE_2_REG 0x1 /* D11 to D0, Read only */ +#define AD7280A_CELL_VOLTAGE_3_REG 0x2 /* D11 to D0, Read only */ +#define AD7280A_CELL_VOLTAGE_4_REG 0x3 /* D11 to D0, Read only */ +#define AD7280A_CELL_VOLTAGE_5_REG 0x4 /* D11 to D0, Read only */ +#define AD7280A_CELL_VOLTAGE_6_REG 0x5 /* D11 to D0, Read only */ +#define AD7280A_AUX_ADC_1_REG 0x6 /* D11 to D0, Read only */ +#define AD7280A_AUX_ADC_2_REG 0x7 /* D11 to D0, Read only */ +#define AD7280A_AUX_ADC_3_REG 0x8 /* D11 to D0, Read only */ +#define AD7280A_AUX_ADC_4_REG 0x9 /* D11 to D0, Read only */ +#define AD7280A_AUX_ADC_5_REG 0xA /* D11 to D0, Read only */ +#define AD7280A_AUX_ADC_6_REG 0xB /* D11 to D0, Read only */ +#define AD7280A_SELF_TEST_REG 0xC /* D11 to D0, Read only */ + +#define AD7280A_CTRL_HB_REG 0xD /* D15 to D8, Read/write */ +#define AD7280A_CTRL_HB_CONV_INPUT_MSK GENMASK(7, 6) +#define AD7280A_CTRL_HB_CONV_INPUT_ALL 0 +#define AD7280A_CTRL_HB_CONV_INPUT_6CELL_AUX1_3_5 1 +#define AD7280A_CTRL_HB_CONV_INPUT_6CELL 2 +#define AD7280A_CTRL_HB_CONV_INPUT_SELF_TEST 3 +#define AD7280A_CTRL_HB_CONV_RREAD_MSK GENMASK(5, 4) +#define AD7280A_CTRL_HB_CONV_RREAD_ALL 0 +#define AD7280A_CTRL_HB_CONV_RREAD_6CELL_AUX1_3_5 1 +#define AD7280A_CTRL_HB_CONV_RREAD_6CELL 2 +#define AD7280A_CTRL_HB_CONV_RREAD_NO 3 +#define AD7280A_CTRL_HB_CONV_START_MSK BIT(3) +#define AD7280A_CTRL_HB_CONV_START_CNVST 0 +#define AD7280A_CTRL_HB_CONV_START_CS 1 +#define AD7280A_CTRL_HB_CONV_AVG_MSK GENMASK(2, 1) +#define AD7280A_CTRL_HB_CONV_AVG_DIS 0 +#define AD7280A_CTRL_HB_CONV_AVG_2 1 +#define AD7280A_CTRL_HB_CONV_AVG_4 2 +#define AD7280A_CTRL_HB_CONV_AVG_8 3 +#define AD7280A_CTRL_HB_PWRDN_SW BIT(0) + +#define AD7280A_CTRL_LB_REG 0xE /* D7 to D0, Read/write */ +#define AD7280A_CTRL_LB_SWRST_MSK BIT(7) +#define AD7280A_CTRL_LB_ACQ_TIME_MSK GENMASK(6, 5) +#define AD7280A_CTRL_LB_ACQ_TIME_400ns 0 +#define AD7280A_CTRL_LB_ACQ_TIME_800ns 1 +#define AD7280A_CTRL_LB_ACQ_TIME_1200ns 2 +#define AD7280A_CTRL_LB_ACQ_TIME_1600ns 3 +#define AD7280A_CTRL_LB_MUST_SET BIT(4) +#define AD7280A_CTRL_LB_THERMISTOR_MSK BIT(3) +#define AD7280A_CTRL_LB_LOCK_DEV_ADDR_MSK BIT(2) +#define AD7280A_CTRL_LB_INC_DEV_ADDR_MSK BIT(1) +#define AD7280A_CTRL_LB_DAISY_CHAIN_RB_MSK BIT(0) + +#define AD7280A_CELL_OVERVOLTAGE_REG 0xF /* D7 to D0, Read/write */ +#define AD7280A_CELL_UNDERVOLTAGE_REG 0x10 /* D7 to D0, Read/write */ +#define AD7280A_AUX_ADC_OVERVOLTAGE_REG 0x11 /* D7 to D0, Read/write */ +#define AD7280A_AUX_ADC_UNDERVOLTAGE_REG 0x12 /* D7 to D0, Read/write */ + +#define AD7280A_ALERT_REG 0x13 /* D7 to D0, Read/write */ +#define AD7280A_ALERT_REMOVE_MSK GENMASK(3, 0) +#define AD7280A_ALERT_REMOVE_AUX5 BIT(0) +#define AD7280A_ALERT_REMOVE_AUX3_AUX5 BIT(1) +#define AD7280A_ALERT_REMOVE_VIN5 BIT(2) +#define AD7280A_ALERT_REMOVE_VIN4_VIN5 BIT(3) +#define AD7280A_ALERT_GEN_STATIC_HIGH BIT(6) +#define AD7280A_ALERT_RELAY_SIG_CHAIN_DOWN (BIT(7) | BIT(6)) + +#define AD7280A_CELL_BALANCE_REG 0x14 /* D7 to D0, Read/write */ +#define AD7280A_CELL_BALANCE_CHAN_BITMAP_MSK GENMASK(7, 2) +#define AD7280A_CB1_TIMER_REG 0x15 /* D7 to D0, Read/write */ +#define AD7280A_CB_TIMER_VAL_MSK GENMASK(7, 3) +#define AD7280A_CB2_TIMER_REG 0x16 /* D7 to D0, Read/write */ +#define AD7280A_CB3_TIMER_REG 0x17 /* D7 to D0, Read/write */ +#define AD7280A_CB4_TIMER_REG 0x18 /* D7 to D0, Read/write */ +#define AD7280A_CB5_TIMER_REG 0x19 /* D7 to D0, Read/write */ +#define AD7280A_CB6_TIMER_REG 0x1A /* D7 to D0, Read/write */ +#define AD7280A_PD_TIMER_REG 0x1B /* D7 to D0, Read/write */ +#define AD7280A_READ_REG 0x1C /* D7 to D0, Read/write */ +#define AD7280A_READ_ADDR_MSK GENMASK(7, 2) +#define AD7280A_CNVST_CTRL_REG 0x1D /* D7 to D0, Read/write */ + +/* Transfer fields */ +#define AD7280A_TRANS_WRITE_DEVADDR_MSK GENMASK(31, 27) +#define AD7280A_TRANS_WRITE_ADDR_MSK GENMASK(26, 21) +#define AD7280A_TRANS_WRITE_VAL_MSK GENMASK(20, 13) +#define AD7280A_TRANS_WRITE_ALL_MSK BIT(12) +#define AD7280A_TRANS_WRITE_CRC_MSK GENMASK(10, 3) +#define AD7280A_TRANS_WRITE_RES_PATTERN 0x2 + +/* Layouts differ for channel vs other registers */ +#define AD7280A_TRANS_READ_DEVADDR_MSK GENMASK(31, 27) +#define AD7280A_TRANS_READ_CONV_CHANADDR_MSK GENMASK(26, 23) +#define AD7280A_TRANS_READ_CONV_DATA_MSK GENMASK(22, 11) +#define AD7280A_TRANS_READ_REG_REGADDR_MSK GENMASK(26, 21) +#define AD7280A_TRANS_READ_REG_DATA_MSK GENMASK(20, 13) +#define AD7280A_TRANS_READ_WRITE_ACK_MSK BIT(10) +#define AD7280A_TRANS_READ_CRC_MSK GENMASK(9, 2) + +/* Magic value used to indicate this special case */ +#define AD7280A_ALL_CELLS (0xAD << 16) + +#define AD7280A_MAX_SPI_CLK_HZ 700000 /* < 1MHz */ +#define AD7280A_MAX_CHAIN 8 +#define AD7280A_CELLS_PER_DEV 6 +#define AD7280A_BITS 12 +#define AD7280A_NUM_CH (AD7280A_AUX_ADC_6_REG - \ + AD7280A_CELL_VOLTAGE_1_REG + 1) + +#define AD7280A_CALC_VOLTAGE_CHAN_NUM(d, c) (((d) * AD7280A_CELLS_PER_DEV) + \ + (c)) +#define AD7280A_CALC_TEMP_CHAN_NUM(d, c) (((d) * AD7280A_CELLS_PER_DEV) + \ + (c) - AD7280A_CELLS_PER_DEV) + +#define AD7280A_DEVADDR_MASTER 0 +#define AD7280A_DEVADDR_ALL 0x1F + +static const unsigned short ad7280a_n_avg[4] = {1, 2, 4, 8}; +static const unsigned short ad7280a_t_acq_ns[4] = {470, 1030, 1510, 1945}; + +/* 5-bit device address is sent LSB first */ +static unsigned int ad7280a_devaddr(unsigned int addr) +{ + return ((addr & 0x1) << 4) | + ((addr & 0x2) << 2) | + (addr & 0x4) | + ((addr & 0x8) >> 2) | + ((addr & 0x10) >> 4); +} + +/* + * During a read a valid write is mandatory. + * So writing to the highest available address (Address 0x1F) and setting the + * address all parts bit to 0 is recommended. + * So the TXVAL is AD7280A_DEVADDR_ALL + CRC + */ +#define AD7280A_READ_TXVAL 0xF800030A + +/* + * AD7280 CRC + * + * P(x) = x^8 + x^5 + x^3 + x^2 + x^1 + x^0 = 0b100101111 => 0x2F + */ +#define POLYNOM 0x2F + +struct ad7280_state { + struct spi_device *spi; + struct iio_chan_spec *channels; + unsigned int chain_last_alert_ignore; + bool thermistor_term_en; + int slave_num; + int scan_cnt; + int readback_delay_us; + unsigned char crc_tab[CRC8_TABLE_SIZE]; + u8 oversampling_ratio; + u8 acquisition_time; + unsigned char ctrl_lb; + unsigned char cell_threshhigh; + unsigned char cell_threshlow; + unsigned char aux_threshhigh; + unsigned char aux_threshlow; + unsigned char cb_mask[AD7280A_MAX_CHAIN]; + struct mutex lock; /* protect sensor state */ + + __be32 tx __aligned(IIO_DMA_MINALIGN); + __be32 rx; +}; + +static unsigned char ad7280_calc_crc8(unsigned char *crc_tab, unsigned int val) +{ + unsigned char crc; + + crc = crc_tab[val >> 16 & 0xFF]; + crc = crc_tab[crc ^ (val >> 8 & 0xFF)]; + + return crc ^ (val & 0xFF); +} + +static int ad7280_check_crc(struct ad7280_state *st, unsigned int val) +{ + unsigned char crc = ad7280_calc_crc8(st->crc_tab, val >> 10); + + if (crc != ((val >> 2) & 0xFF)) + return -EIO; + + return 0; +} + +/* + * After initiating a conversion sequence we need to wait until the conversion + * is done. The delay is typically in the range of 15..30us however depending on + * the number of devices in the daisy chain, the number of averages taken, + * conversion delays and acquisition time options it may take up to 250us, in + * this case we better sleep instead of busy wait. + */ + +static void ad7280_delay(struct ad7280_state *st) +{ + if (st->readback_delay_us < 50) + udelay(st->readback_delay_us); + else + usleep_range(250, 500); +} + +static int __ad7280_read32(struct ad7280_state *st, unsigned int *val) +{ + int ret; + struct spi_transfer t = { + .tx_buf = &st->tx, + .rx_buf = &st->rx, + .len = sizeof(st->tx), + }; + + st->tx = cpu_to_be32(AD7280A_READ_TXVAL); + + ret = spi_sync_transfer(st->spi, &t, 1); + if (ret) + return ret; + + *val = be32_to_cpu(st->rx); + + return 0; +} + +static int ad7280_write(struct ad7280_state *st, unsigned int devaddr, + unsigned int addr, bool all, unsigned int val) +{ + unsigned int reg = FIELD_PREP(AD7280A_TRANS_WRITE_DEVADDR_MSK, devaddr) | + FIELD_PREP(AD7280A_TRANS_WRITE_ADDR_MSK, addr) | + FIELD_PREP(AD7280A_TRANS_WRITE_VAL_MSK, val) | + FIELD_PREP(AD7280A_TRANS_WRITE_ALL_MSK, all); + + reg |= FIELD_PREP(AD7280A_TRANS_WRITE_CRC_MSK, + ad7280_calc_crc8(st->crc_tab, reg >> 11)); + /* Reserved b010 pattern not included crc calc */ + reg |= AD7280A_TRANS_WRITE_RES_PATTERN; + + st->tx = cpu_to_be32(reg); + + return spi_write(st->spi, &st->tx, sizeof(st->tx)); +} + +static int ad7280_read_reg(struct ad7280_state *st, unsigned int devaddr, + unsigned int addr) +{ + int ret; + unsigned int tmp; + + /* turns off the read operation on all parts */ + ret = ad7280_write(st, AD7280A_DEVADDR_MASTER, AD7280A_CTRL_HB_REG, 1, + FIELD_PREP(AD7280A_CTRL_HB_CONV_INPUT_MSK, + AD7280A_CTRL_HB_CONV_INPUT_ALL) | + FIELD_PREP(AD7280A_CTRL_HB_CONV_RREAD_MSK, + AD7280A_CTRL_HB_CONV_RREAD_NO) | + FIELD_PREP(AD7280A_CTRL_HB_CONV_AVG_MSK, + st->oversampling_ratio)); + if (ret) + return ret; + + /* turns on the read operation on the addressed part */ + ret = ad7280_write(st, devaddr, AD7280A_CTRL_HB_REG, 0, + FIELD_PREP(AD7280A_CTRL_HB_CONV_INPUT_MSK, + AD7280A_CTRL_HB_CONV_INPUT_ALL) | + FIELD_PREP(AD7280A_CTRL_HB_CONV_RREAD_MSK, + AD7280A_CTRL_HB_CONV_RREAD_ALL) | + FIELD_PREP(AD7280A_CTRL_HB_CONV_AVG_MSK, + st->oversampling_ratio)); + if (ret) + return ret; + + /* Set register address on the part to be read from */ + ret = ad7280_write(st, devaddr, AD7280A_READ_REG, 0, + FIELD_PREP(AD7280A_READ_ADDR_MSK, addr)); + if (ret) + return ret; + + ret = __ad7280_read32(st, &tmp); + if (ret) + return ret; + + if (ad7280_check_crc(st, tmp)) + return -EIO; + + if ((FIELD_GET(AD7280A_TRANS_READ_DEVADDR_MSK, tmp) != devaddr) || + (FIELD_GET(AD7280A_TRANS_READ_REG_REGADDR_MSK, tmp) != addr)) + return -EFAULT; + + return FIELD_GET(AD7280A_TRANS_READ_REG_DATA_MSK, tmp); +} + +static int ad7280_read_channel(struct ad7280_state *st, unsigned int devaddr, + unsigned int addr) +{ + int ret; + unsigned int tmp; + + ret = ad7280_write(st, devaddr, AD7280A_READ_REG, 0, + FIELD_PREP(AD7280A_READ_ADDR_MSK, addr)); + if (ret) + return ret; + + ret = ad7280_write(st, AD7280A_DEVADDR_MASTER, AD7280A_CTRL_HB_REG, 1, + FIELD_PREP(AD7280A_CTRL_HB_CONV_INPUT_MSK, + AD7280A_CTRL_HB_CONV_INPUT_ALL) | + FIELD_PREP(AD7280A_CTRL_HB_CONV_RREAD_MSK, + AD7280A_CTRL_HB_CONV_RREAD_NO) | + FIELD_PREP(AD7280A_CTRL_HB_CONV_AVG_MSK, + st->oversampling_ratio)); + if (ret) + return ret; + + ret = ad7280_write(st, devaddr, AD7280A_CTRL_HB_REG, 0, + FIELD_PREP(AD7280A_CTRL_HB_CONV_INPUT_MSK, + AD7280A_CTRL_HB_CONV_INPUT_ALL) | + FIELD_PREP(AD7280A_CTRL_HB_CONV_RREAD_MSK, + AD7280A_CTRL_HB_CONV_RREAD_ALL) | + FIELD_PREP(AD7280A_CTRL_HB_CONV_START_MSK, + AD7280A_CTRL_HB_CONV_START_CS) | + FIELD_PREP(AD7280A_CTRL_HB_CONV_AVG_MSK, + st->oversampling_ratio)); + if (ret) + return ret; + + ad7280_delay(st); + + ret = __ad7280_read32(st, &tmp); + if (ret) + return ret; + + if (ad7280_check_crc(st, tmp)) + return -EIO; + + if ((FIELD_GET(AD7280A_TRANS_READ_DEVADDR_MSK, tmp) != devaddr) || + (FIELD_GET(AD7280A_TRANS_READ_CONV_CHANADDR_MSK, tmp) != addr)) + return -EFAULT; + + return FIELD_GET(AD7280A_TRANS_READ_CONV_DATA_MSK, tmp); +} + +static int ad7280_read_all_channels(struct ad7280_state *st, unsigned int cnt, + unsigned int *array) +{ + int i, ret; + unsigned int tmp, sum = 0; + + ret = ad7280_write(st, AD7280A_DEVADDR_MASTER, AD7280A_READ_REG, 1, + AD7280A_CELL_VOLTAGE_1_REG << 2); + if (ret) + return ret; + + ret = ad7280_write(st, AD7280A_DEVADDR_MASTER, AD7280A_CTRL_HB_REG, 1, + FIELD_PREP(AD7280A_CTRL_HB_CONV_INPUT_MSK, + AD7280A_CTRL_HB_CONV_INPUT_ALL) | + FIELD_PREP(AD7280A_CTRL_HB_CONV_RREAD_MSK, + AD7280A_CTRL_HB_CONV_RREAD_ALL) | + FIELD_PREP(AD7280A_CTRL_HB_CONV_START_MSK, + AD7280A_CTRL_HB_CONV_START_CS) | + FIELD_PREP(AD7280A_CTRL_HB_CONV_AVG_MSK, + st->oversampling_ratio)); + if (ret) + return ret; + + ad7280_delay(st); + + for (i = 0; i < cnt; i++) { + ret = __ad7280_read32(st, &tmp); + if (ret) + return ret; + + if (ad7280_check_crc(st, tmp)) + return -EIO; + + if (array) + array[i] = tmp; + /* only sum cell voltages */ + if (FIELD_GET(AD7280A_TRANS_READ_CONV_CHANADDR_MSK, tmp) <= + AD7280A_CELL_VOLTAGE_6_REG) + sum += FIELD_GET(AD7280A_TRANS_READ_CONV_DATA_MSK, tmp); + } + + return sum; +} + +static void ad7280_sw_power_down(void *data) +{ + struct ad7280_state *st = data; + + ad7280_write(st, AD7280A_DEVADDR_MASTER, AD7280A_CTRL_HB_REG, 1, + AD7280A_CTRL_HB_PWRDN_SW | + FIELD_PREP(AD7280A_CTRL_HB_CONV_AVG_MSK, st->oversampling_ratio)); +} + +static int ad7280_chain_setup(struct ad7280_state *st) +{ + unsigned int val, n; + int ret; + + ret = ad7280_write(st, AD7280A_DEVADDR_MASTER, AD7280A_CTRL_LB_REG, 1, + FIELD_PREP(AD7280A_CTRL_LB_DAISY_CHAIN_RB_MSK, 1) | + FIELD_PREP(AD7280A_CTRL_LB_LOCK_DEV_ADDR_MSK, 1) | + AD7280A_CTRL_LB_MUST_SET | + FIELD_PREP(AD7280A_CTRL_LB_SWRST_MSK, 1) | + st->ctrl_lb); + if (ret) + return ret; + + ret = ad7280_write(st, AD7280A_DEVADDR_MASTER, AD7280A_CTRL_LB_REG, 1, + FIELD_PREP(AD7280A_CTRL_LB_DAISY_CHAIN_RB_MSK, 1) | + FIELD_PREP(AD7280A_CTRL_LB_LOCK_DEV_ADDR_MSK, 1) | + AD7280A_CTRL_LB_MUST_SET | + FIELD_PREP(AD7280A_CTRL_LB_SWRST_MSK, 0) | + st->ctrl_lb); + if (ret) + goto error_power_down; + + ret = ad7280_write(st, AD7280A_DEVADDR_MASTER, AD7280A_READ_REG, 1, + FIELD_PREP(AD7280A_READ_ADDR_MSK, AD7280A_CTRL_LB_REG)); + if (ret) + goto error_power_down; + + for (n = 0; n <= AD7280A_MAX_CHAIN; n++) { + ret = __ad7280_read32(st, &val); + if (ret) + goto error_power_down; + + if (val == 0) + return n - 1; + + if (ad7280_check_crc(st, val)) { + ret = -EIO; + goto error_power_down; + } + + if (n != ad7280a_devaddr(FIELD_GET(AD7280A_TRANS_READ_DEVADDR_MSK, val))) { + ret = -EIO; + goto error_power_down; + } + } + ret = -EFAULT; + +error_power_down: + ad7280_write(st, AD7280A_DEVADDR_MASTER, AD7280A_CTRL_HB_REG, 1, + AD7280A_CTRL_HB_PWRDN_SW | + FIELD_PREP(AD7280A_CTRL_HB_CONV_AVG_MSK, st->oversampling_ratio)); + + return ret; +} + +static ssize_t ad7280_show_balance_sw(struct iio_dev *indio_dev, + uintptr_t private, + const struct iio_chan_spec *chan, char *buf) +{ + struct ad7280_state *st = iio_priv(indio_dev); + + return sysfs_emit(buf, "%d\n", + !!(st->cb_mask[chan->address >> 8] & + BIT(chan->address & 0xFF))); +} + +static ssize_t ad7280_store_balance_sw(struct iio_dev *indio_dev, + uintptr_t private, + const struct iio_chan_spec *chan, + const char *buf, size_t len) +{ + struct ad7280_state *st = iio_priv(indio_dev); + unsigned int devaddr, ch; + bool readin; + int ret; + + ret = kstrtobool(buf, &readin); + if (ret) + return ret; + + devaddr = chan->address >> 8; + ch = chan->address & 0xFF; + + guard(mutex)(&st->lock); + + if (readin) + st->cb_mask[devaddr] |= BIT(ch); + else + st->cb_mask[devaddr] &= ~BIT(ch); + + ret = ad7280_write(st, devaddr, AD7280A_CELL_BALANCE_REG, 0, + FIELD_PREP(AD7280A_CELL_BALANCE_CHAN_BITMAP_MSK, + st->cb_mask[devaddr])); + + return ret ? ret : len; +} + +static ssize_t ad7280_show_balance_timer(struct iio_dev *indio_dev, + uintptr_t private, + const struct iio_chan_spec *chan, + char *buf) +{ + struct ad7280_state *st = iio_priv(indio_dev); + u8 devaddr = chan->address >> 8; + u8 ch = chan->address & 0xFF; + unsigned int msecs; + int ret; + + scoped_guard(mutex, &st->lock) + ret = ad7280_read_reg(st, devaddr, ch + AD7280A_CB1_TIMER_REG); + if (ret < 0) + return ret; + + msecs = FIELD_GET(AD7280A_CB_TIMER_VAL_MSK, ret) * 71500; + + return sysfs_emit(buf, "%u.%u\n", msecs / 1000, msecs % 1000); +} + +static ssize_t ad7280_store_balance_timer(struct iio_dev *indio_dev, + uintptr_t private, + const struct iio_chan_spec *chan, + const char *buf, size_t len) +{ + struct ad7280_state *st = iio_priv(indio_dev); + u8 devaddr = chan->address >> 8; + u8 ch = chan->address & 0xFF; + int val, val2; + int ret; + + ret = iio_str_to_fixpoint(buf, 100, &val, &val2); + if (ret) + return ret; + + val = val * 1000 + val2; + val /= 71500; + + if (val > 31) + return -EINVAL; + + guard(mutex)(&st->lock); + + ret = ad7280_write(st, devaddr, ch + AD7280A_CB1_TIMER_REG, 0, + FIELD_PREP(AD7280A_CB_TIMER_VAL_MSK, val)); + + return ret ? ret : len; +} + +static const struct iio_chan_spec_ext_info ad7280_cell_ext_info[] = { + { + .name = "balance_switch_en", + .read = ad7280_show_balance_sw, + .write = ad7280_store_balance_sw, + .shared = IIO_SEPARATE, + }, { + .name = "balance_switch_timer", + .read = ad7280_show_balance_timer, + .write = ad7280_store_balance_timer, + .shared = IIO_SEPARATE, + }, + { } +}; + +static const struct iio_event_spec ad7280_events[] = { + { + .type = IIO_EV_TYPE_THRESH, + .dir = IIO_EV_DIR_RISING, + .mask_shared_by_type = BIT(IIO_EV_INFO_VALUE), + }, { + .type = IIO_EV_TYPE_THRESH, + .dir = IIO_EV_DIR_FALLING, + .mask_shared_by_type = BIT(IIO_EV_INFO_VALUE), + }, +}; + +static void ad7280_voltage_channel_init(struct iio_chan_spec *chan, int i, + bool irq_present) +{ + chan->type = IIO_VOLTAGE; + chan->differential = 1; + chan->channel = i; + chan->channel2 = chan->channel + 1; + if (irq_present) { + chan->event_spec = ad7280_events; + chan->num_event_specs = ARRAY_SIZE(ad7280_events); + } + chan->ext_info = ad7280_cell_ext_info; +} + +static void ad7280_temp_channel_init(struct iio_chan_spec *chan, int i, + bool irq_present) +{ + chan->type = IIO_TEMP; + chan->channel = i; + if (irq_present) { + chan->event_spec = ad7280_events; + chan->num_event_specs = ARRAY_SIZE(ad7280_events); + } +} + +static void ad7280_common_fields_init(struct iio_chan_spec *chan, int addr, + int cnt) +{ + chan->indexed = 1; + chan->info_mask_separate = BIT(IIO_CHAN_INFO_RAW); + chan->info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE); + chan->info_mask_shared_by_all = BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO); + chan->address = addr; + chan->scan_index = cnt; + chan->scan_type.sign = 'u'; + chan->scan_type.realbits = 12; + chan->scan_type.storagebits = 32; +} + +static void ad7280_total_voltage_channel_init(struct iio_chan_spec *chan, + int cnt, int dev) +{ + chan->type = IIO_VOLTAGE; + chan->differential = 1; + chan->channel = 0; + chan->channel2 = dev * AD7280A_CELLS_PER_DEV; + chan->address = AD7280A_ALL_CELLS; + chan->indexed = 1; + chan->info_mask_separate = BIT(IIO_CHAN_INFO_RAW); + chan->info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE); + chan->scan_index = cnt; + chan->scan_type.sign = 'u'; + chan->scan_type.realbits = 32; + chan->scan_type.storagebits = 32; +} + +static void ad7280_init_dev_channels(struct ad7280_state *st, int dev, int *cnt, + bool irq_present) +{ + int addr, ch, i; + struct iio_chan_spec *chan; + + for (ch = AD7280A_CELL_VOLTAGE_1_REG; ch <= AD7280A_AUX_ADC_6_REG; ch++) { + chan = &st->channels[*cnt]; + + if (ch < AD7280A_AUX_ADC_1_REG) { + i = AD7280A_CALC_VOLTAGE_CHAN_NUM(dev, ch); + ad7280_voltage_channel_init(chan, i, irq_present); + } else { + i = AD7280A_CALC_TEMP_CHAN_NUM(dev, ch); + ad7280_temp_channel_init(chan, i, irq_present); + } + + addr = ad7280a_devaddr(dev) << 8 | ch; + ad7280_common_fields_init(chan, addr, *cnt); + + (*cnt)++; + } +} + +static int ad7280_channel_init(struct ad7280_state *st, bool irq_present) +{ + int dev, cnt = 0; + + st->channels = devm_kcalloc(&st->spi->dev, (st->slave_num + 1) * 12 + 1, + sizeof(*st->channels), GFP_KERNEL); + if (!st->channels) + return -ENOMEM; + + for (dev = 0; dev <= st->slave_num; dev++) + ad7280_init_dev_channels(st, dev, &cnt, irq_present); + + ad7280_total_voltage_channel_init(&st->channels[cnt], cnt, dev); + + return cnt + 1; +} + +static int ad7280a_read_thresh(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir, + enum iio_event_info info, int *val, int *val2) +{ + struct ad7280_state *st = iio_priv(indio_dev); + + switch (chan->type) { + case IIO_VOLTAGE: + switch (dir) { + case IIO_EV_DIR_RISING: + *val = 1000 + (st->cell_threshhigh * 1568L) / 100; + return IIO_VAL_INT; + case IIO_EV_DIR_FALLING: + *val = 1000 + (st->cell_threshlow * 1568L) / 100; + return IIO_VAL_INT; + default: + return -EINVAL; + } + break; + case IIO_TEMP: + switch (dir) { + case IIO_EV_DIR_RISING: + *val = ((st->aux_threshhigh) * 196L) / 10; + return IIO_VAL_INT; + case IIO_EV_DIR_FALLING: + *val = (st->aux_threshlow * 196L) / 10; + return IIO_VAL_INT; + default: + return -EINVAL; + } + break; + default: + return -EINVAL; + } +} + +static int ad7280a_write_thresh(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir, + enum iio_event_info info, + int val, int val2) +{ + struct ad7280_state *st = iio_priv(indio_dev); + unsigned int addr; + long value; + int ret; + + if (val2 != 0) + return -EINVAL; + + guard(mutex)(&st->lock); + + switch (chan->type) { + case IIO_VOLTAGE: + value = ((val - 1000) * 100) / 1568; /* LSB 15.68mV */ + value = clamp(value, 0L, 0xFFL); + switch (dir) { + case IIO_EV_DIR_RISING: + addr = AD7280A_CELL_OVERVOLTAGE_REG; + ret = ad7280_write(st, AD7280A_DEVADDR_MASTER, addr, + 1, value); + if (ret) + return ret; + st->cell_threshhigh = value; + return 0; + case IIO_EV_DIR_FALLING: + addr = AD7280A_CELL_UNDERVOLTAGE_REG; + ret = ad7280_write(st, AD7280A_DEVADDR_MASTER, addr, + 1, value); + if (ret) + return ret; + st->cell_threshlow = value; + return 0; + default: + return -EINVAL; + } + case IIO_TEMP: + value = (val * 10) / 196; /* LSB 19.6mV */ + value = clamp(value, 0L, 0xFFL); + switch (dir) { + case IIO_EV_DIR_RISING: + addr = AD7280A_AUX_ADC_OVERVOLTAGE_REG; + ret = ad7280_write(st, AD7280A_DEVADDR_MASTER, addr, + 1, value); + if (ret) + return ret; + st->aux_threshhigh = value; + return 0; + case IIO_EV_DIR_FALLING: + addr = AD7280A_AUX_ADC_UNDERVOLTAGE_REG; + ret = ad7280_write(st, AD7280A_DEVADDR_MASTER, addr, + 1, value); + if (ret) + return ret; + st->aux_threshlow = value; + return 0; + default: + return -EINVAL; + } + default: + return -EINVAL; + } +} + +static irqreturn_t ad7280_event_handler(int irq, void *private) +{ + struct iio_dev *indio_dev = private; + struct ad7280_state *st = iio_priv(indio_dev); + int i, ret; + + unsigned int *channels __free(kfree) = kzalloc_objs(*channels, + st->scan_cnt); + if (!channels) + return IRQ_HANDLED; + + ret = ad7280_read_all_channels(st, st->scan_cnt, channels); + if (ret < 0) + return IRQ_HANDLED; + + for (i = 0; i < st->scan_cnt; i++) { + unsigned int val; + + val = FIELD_GET(AD7280A_TRANS_READ_CONV_DATA_MSK, channels[i]); + if (FIELD_GET(AD7280A_TRANS_READ_CONV_CHANADDR_MSK, channels[i]) <= + AD7280A_CELL_VOLTAGE_6_REG) { + if (val >= st->cell_threshhigh) { + u64 tmp = IIO_DIFF_EVENT_CODE(IIO_VOLTAGE, 0, 0, + IIO_EV_TYPE_THRESH, + IIO_EV_DIR_RISING); + iio_push_event(indio_dev, tmp, + iio_get_time_ns(indio_dev)); + } else if (val <= st->cell_threshlow) { + u64 tmp = IIO_DIFF_EVENT_CODE(IIO_VOLTAGE, 0, 0, + IIO_EV_TYPE_THRESH, + IIO_EV_DIR_FALLING); + iio_push_event(indio_dev, tmp, + iio_get_time_ns(indio_dev)); + } + } else { + if (val >= st->aux_threshhigh) { + u64 tmp = IIO_UNMOD_EVENT_CODE(IIO_TEMP, 0, + IIO_EV_TYPE_THRESH, + IIO_EV_DIR_RISING); + iio_push_event(indio_dev, tmp, + iio_get_time_ns(indio_dev)); + } else if (val <= st->aux_threshlow) { + u64 tmp = IIO_UNMOD_EVENT_CODE(IIO_TEMP, 0, + IIO_EV_TYPE_THRESH, + IIO_EV_DIR_FALLING); + iio_push_event(indio_dev, tmp, + iio_get_time_ns(indio_dev)); + } + } + } + + return IRQ_HANDLED; +} + +static void ad7280_update_delay(struct ad7280_state *st) +{ + /* + * Total Conversion Time = ((tACQ + tCONV) * + * (Number of Conversions per Part)) − + * tACQ + ((N - 1) * tDELAY) + * + * Readback Delay = Total Conversion Time + tWAIT + */ + + st->readback_delay_us = + ((ad7280a_t_acq_ns[st->acquisition_time & 0x3] + 720) * + (AD7280A_NUM_CH * ad7280a_n_avg[st->oversampling_ratio & 0x3])) - + ad7280a_t_acq_ns[st->acquisition_time & 0x3] + st->slave_num * 250; + + /* Convert to usecs */ + st->readback_delay_us = DIV_ROUND_UP(st->readback_delay_us, 1000); + st->readback_delay_us += 5; /* Add tWAIT */ +} + +static int ad7280_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, + int *val2, + long m) +{ + struct ad7280_state *st = iio_priv(indio_dev); + u8 devaddr = chan->address >> 8; + u8 ch = chan->address & 0xFF; + int ret; + + switch (m) { + case IIO_CHAN_INFO_RAW: { + guard(mutex)(&st->lock); + + if (chan->address == AD7280A_ALL_CELLS) + ret = ad7280_read_all_channels(st, st->scan_cnt, NULL); + else + ret = ad7280_read_channel(st, devaddr, ch); + + if (ret < 0) + return ret; + + *val = ret; + + return IIO_VAL_INT; + } + case IIO_CHAN_INFO_SCALE: + if (ch <= AD7280A_CELL_VOLTAGE_6_REG) + *val = 4000; + else + *val = 5000; + + *val2 = AD7280A_BITS; + return IIO_VAL_FRACTIONAL_LOG2; + case IIO_CHAN_INFO_OVERSAMPLING_RATIO: + *val = ad7280a_n_avg[st->oversampling_ratio]; + return IIO_VAL_INT; + } + return -EINVAL; +} + +static int ad7280_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, int val2, long mask) +{ + struct ad7280_state *st = iio_priv(indio_dev); + int i; + + switch (mask) { + case IIO_CHAN_INFO_OVERSAMPLING_RATIO: + if (val2 != 0) + return -EINVAL; + for (i = 0; i < ARRAY_SIZE(ad7280a_n_avg); i++) { + if (val == ad7280a_n_avg[i]) { + st->oversampling_ratio = i; + ad7280_update_delay(st); + return 0; + } + } + return -EINVAL; + default: + return -EINVAL; + } +} + +static const struct iio_info ad7280_info = { + .read_raw = ad7280_read_raw, + .write_raw = ad7280_write_raw, + .read_event_value = &ad7280a_read_thresh, + .write_event_value = &ad7280a_write_thresh, +}; + +static const struct iio_info ad7280_info_no_irq = { + .read_raw = ad7280_read_raw, + .write_raw = ad7280_write_raw, +}; + +static int ad7280_probe(struct spi_device *spi) +{ + struct device *dev = &spi->dev; + struct ad7280_state *st; + int ret; + struct iio_dev *indio_dev; + + indio_dev = devm_iio_device_alloc(dev, sizeof(*st)); + if (!indio_dev) + return -ENOMEM; + + st = iio_priv(indio_dev); + spi_set_drvdata(spi, indio_dev); + st->spi = spi; + mutex_init(&st->lock); + + st->thermistor_term_en = + device_property_read_bool(dev, "adi,thermistor-termination"); + + if (device_property_present(dev, "adi,acquisition-time-ns")) { + u32 val; + + ret = device_property_read_u32(dev, "adi,acquisition-time-ns", &val); + if (ret) + return ret; + + switch (val) { + case 400: + st->acquisition_time = AD7280A_CTRL_LB_ACQ_TIME_400ns; + break; + case 800: + st->acquisition_time = AD7280A_CTRL_LB_ACQ_TIME_800ns; + break; + case 1200: + st->acquisition_time = AD7280A_CTRL_LB_ACQ_TIME_1200ns; + break; + case 1600: + st->acquisition_time = AD7280A_CTRL_LB_ACQ_TIME_1600ns; + break; + default: + return dev_err_probe(dev, -EINVAL, + "Firmware provided acquisition time is invalid\n"); + } + } else { + st->acquisition_time = AD7280A_CTRL_LB_ACQ_TIME_400ns; + } + + /* Alert masks are intended for when particular inputs are not wired up */ + if (device_property_present(dev, "adi,voltage-alert-last-chan")) { + u32 val; + + ret = device_property_read_u32(dev, "adi,voltage-alert-last-chan", &val); + if (ret) + return ret; + + switch (val) { + case 3: + st->chain_last_alert_ignore |= AD7280A_ALERT_REMOVE_VIN4_VIN5; + break; + case 4: + st->chain_last_alert_ignore |= AD7280A_ALERT_REMOVE_VIN5; + break; + case 5: + break; + default: + dev_err(dev, + "Firmware provided last voltage alert channel invalid\n"); + break; + } + } + crc8_populate_msb(st->crc_tab, POLYNOM); + + st->spi->max_speed_hz = AD7280A_MAX_SPI_CLK_HZ; + st->spi->mode = SPI_MODE_1; + ret = spi_setup(st->spi); + if (ret < 0) + return ret; + + st->ctrl_lb = FIELD_PREP(AD7280A_CTRL_LB_ACQ_TIME_MSK, st->acquisition_time) | + FIELD_PREP(AD7280A_CTRL_LB_THERMISTOR_MSK, st->thermistor_term_en); + st->oversampling_ratio = 0; /* No oversampling */ + + ret = ad7280_chain_setup(st); + if (ret < 0) + return ret; + + st->slave_num = ret; + st->scan_cnt = (st->slave_num + 1) * AD7280A_NUM_CH; + st->cell_threshhigh = 0xFF; + st->aux_threshhigh = 0xFF; + + ret = devm_add_action_or_reset(dev, ad7280_sw_power_down, st); + if (ret) + return ret; + + ad7280_update_delay(st); + + indio_dev->name = spi_get_device_id(spi)->name; + indio_dev->modes = INDIO_DIRECT_MODE; + + ret = ad7280_channel_init(st, spi->irq > 0); + if (ret < 0) + return ret; + + indio_dev->num_channels = ret; + indio_dev->channels = st->channels; + if (spi->irq > 0) { + ret = ad7280_write(st, AD7280A_DEVADDR_MASTER, + AD7280A_ALERT_REG, 1, + AD7280A_ALERT_RELAY_SIG_CHAIN_DOWN); + if (ret) + return ret; + + ret = ad7280_write(st, ad7280a_devaddr(st->slave_num), + AD7280A_ALERT_REG, 0, + AD7280A_ALERT_GEN_STATIC_HIGH | + FIELD_PREP(AD7280A_ALERT_REMOVE_MSK, + st->chain_last_alert_ignore)); + if (ret) + return ret; + + ret = devm_request_threaded_irq(dev, spi->irq, + NULL, + ad7280_event_handler, + IRQF_TRIGGER_FALLING | + IRQF_ONESHOT, + indio_dev->name, + indio_dev); + if (ret) + return ret; + + indio_dev->info = &ad7280_info; + } else { + indio_dev->info = &ad7280_info_no_irq; + } + + return devm_iio_device_register(dev, indio_dev); +} + +static const struct spi_device_id ad7280_id[] = { + { .name = "ad7280a" }, + { } +}; +MODULE_DEVICE_TABLE(spi, ad7280_id); + +static struct spi_driver ad7280_driver = { + .driver = { + .name = "ad7280", + }, + .probe = ad7280_probe, + .id_table = ad7280_id, +}; +module_spi_driver(ad7280_driver); + +MODULE_AUTHOR("Michael Hennerich "); +MODULE_DESCRIPTION("Analog Devices AD7280A"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/adc/ad7291.c b/drivers/iio/adc/ad7291.c new file mode 100644 index 000000000..6a1493e27 --- /dev/null +++ b/drivers/iio/adc/ad7291.c @@ -0,0 +1,563 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * AD7291 8-Channel, I2C, 12-Bit SAR ADC with Temperature Sensor + * + * Copyright 2010-2011 Analog Devices Inc. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +/* + * Simplified handling + * + * If no events enabled - single polled channel read + * If event enabled direct reads disable unless channel + * is in the read mask. + * + * The noise-delayed bit as per datasheet suggestion is always enabled. + */ + +/* + * AD7291 registers definition + */ +#define AD7291_COMMAND 0x00 +#define AD7291_VOLTAGE 0x01 +#define AD7291_T_SENSE 0x02 +#define AD7291_T_AVERAGE 0x03 +#define AD7291_DATA_HIGH(x) ((x) * 3 + 0x4) +#define AD7291_DATA_LOW(x) ((x) * 3 + 0x5) +#define AD7291_HYST(x) ((x) * 3 + 0x6) +#define AD7291_VOLTAGE_ALERT_STATUS 0x1F +#define AD7291_T_ALERT_STATUS 0x20 + +#define AD7291_BITS 12 +#define AD7291_VOLTAGE_LIMIT_COUNT 8 + + +/* + * AD7291 command + */ +#define AD7291_AUTOCYCLE BIT(0) +#define AD7291_RESET BIT(1) +#define AD7291_ALERT_CLEAR BIT(2) +#define AD7291_ALERT_POLARITY BIT(3) +#define AD7291_EXT_REF BIT(4) +#define AD7291_NOISE_DELAY BIT(5) +#define AD7291_T_SENSE_MASK BIT(7) +#define AD7291_VOLTAGE_MASK GENMASK(15, 8) +#define AD7291_VOLTAGE_OFFSET 8 + +/* + * AD7291 value masks + */ +#define AD7291_VALUE_MASK GENMASK(11, 0) + +/* + * AD7291 alert register bits + */ +#define AD7291_T_LOW BIT(0) +#define AD7291_T_HIGH BIT(1) +#define AD7291_T_AVG_LOW BIT(2) +#define AD7291_T_AVG_HIGH BIT(3) +#define AD7291_V_LOW(x) BIT((x) * 2) +#define AD7291_V_HIGH(x) BIT((x) * 2 + 1) + + +struct ad7291_chip_info { + struct i2c_client *client; + struct regulator *reg; + u16 command; + u16 c_mask; /* Active voltage channels for events */ + struct mutex state_lock; +}; + +static int ad7291_i2c_read(struct ad7291_chip_info *chip, u8 reg, u16 *data) +{ + struct i2c_client *client = chip->client; + int ret = 0; + + ret = i2c_smbus_read_word_swapped(client, reg); + if (ret < 0) { + dev_err(&client->dev, "I2C read error\n"); + return ret; + } + + *data = ret; + + return 0; +} + +static int ad7291_i2c_write(struct ad7291_chip_info *chip, u8 reg, u16 data) +{ + return i2c_smbus_write_word_swapped(chip->client, reg, data); +} + +static irqreturn_t ad7291_event_handler(int irq, void *private) +{ + struct iio_dev *indio_dev = private; + struct ad7291_chip_info *chip = iio_priv(private); + u16 t_status, v_status; + u16 command; + int i; + s64 timestamp = iio_get_time_ns(indio_dev); + + if (ad7291_i2c_read(chip, AD7291_T_ALERT_STATUS, &t_status)) + return IRQ_HANDLED; + + if (ad7291_i2c_read(chip, AD7291_VOLTAGE_ALERT_STATUS, &v_status)) + return IRQ_HANDLED; + + if (!(t_status || v_status)) + return IRQ_HANDLED; + + command = chip->command | AD7291_ALERT_CLEAR; + ad7291_i2c_write(chip, AD7291_COMMAND, command); + + command = chip->command & ~AD7291_ALERT_CLEAR; + ad7291_i2c_write(chip, AD7291_COMMAND, command); + + /* For now treat t_sense and t_sense_average the same */ + if ((t_status & AD7291_T_LOW) || (t_status & AD7291_T_AVG_LOW)) + iio_push_event(indio_dev, + IIO_UNMOD_EVENT_CODE(IIO_TEMP, + 0, + IIO_EV_TYPE_THRESH, + IIO_EV_DIR_FALLING), + timestamp); + if ((t_status & AD7291_T_HIGH) || (t_status & AD7291_T_AVG_HIGH)) + iio_push_event(indio_dev, + IIO_UNMOD_EVENT_CODE(IIO_TEMP, + 0, + IIO_EV_TYPE_THRESH, + IIO_EV_DIR_RISING), + timestamp); + + for (i = 0; i < AD7291_VOLTAGE_LIMIT_COUNT; i++) { + if (v_status & AD7291_V_LOW(i)) + iio_push_event(indio_dev, + IIO_UNMOD_EVENT_CODE(IIO_VOLTAGE, + i, + IIO_EV_TYPE_THRESH, + IIO_EV_DIR_FALLING), + timestamp); + if (v_status & AD7291_V_HIGH(i)) + iio_push_event(indio_dev, + IIO_UNMOD_EVENT_CODE(IIO_VOLTAGE, + i, + IIO_EV_TYPE_THRESH, + IIO_EV_DIR_RISING), + timestamp); + } + + return IRQ_HANDLED; +} + +static unsigned int ad7291_threshold_reg(const struct iio_chan_spec *chan, + enum iio_event_direction dir, + enum iio_event_info info) +{ + unsigned int offset; + + switch (chan->type) { + case IIO_VOLTAGE: + offset = chan->channel; + break; + case IIO_TEMP: + offset = AD7291_VOLTAGE_OFFSET; + break; + default: + return 0; + } + + switch (info) { + case IIO_EV_INFO_VALUE: + if (dir == IIO_EV_DIR_FALLING) + return AD7291_DATA_HIGH(offset); + else + return AD7291_DATA_LOW(offset); + case IIO_EV_INFO_HYSTERESIS: + return AD7291_HYST(offset); + default: + break; + } + return 0; +} + +static int ad7291_read_event_value(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir, + enum iio_event_info info, + int *val, int *val2) +{ + struct ad7291_chip_info *chip = iio_priv(indio_dev); + int ret; + u16 uval; + + ret = ad7291_i2c_read(chip, ad7291_threshold_reg(chan, dir, info), + &uval); + if (ret < 0) + return ret; + + if (info == IIO_EV_INFO_HYSTERESIS || chan->type == IIO_VOLTAGE) + *val = uval & AD7291_VALUE_MASK; + + else + *val = sign_extend32(uval, 11); + + return IIO_VAL_INT; +} + +static int ad7291_write_event_value(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir, + enum iio_event_info info, + int val, int val2) +{ + struct ad7291_chip_info *chip = iio_priv(indio_dev); + + if (info == IIO_EV_INFO_HYSTERESIS || chan->type == IIO_VOLTAGE) { + if (val > AD7291_VALUE_MASK || val < 0) + return -EINVAL; + } else { + if (val > 2047 || val < -2048) + return -EINVAL; + } + + return ad7291_i2c_write(chip, ad7291_threshold_reg(chan, dir, info), + val); +} + +static int ad7291_read_event_config(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir) +{ + struct ad7291_chip_info *chip = iio_priv(indio_dev); + /* + * To be enabled the channel must simply be on. If any are enabled + * we are in continuous sampling mode + */ + + switch (chan->type) { + case IIO_VOLTAGE: + return !!(chip->c_mask & BIT(15 - chan->channel)); + case IIO_TEMP: + /* always on */ + return 1; + default: + return -EINVAL; + } + +} + +static int ad7291_write_event_config(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir, + bool state) +{ + int ret = 0; + struct ad7291_chip_info *chip = iio_priv(indio_dev); + unsigned int mask; + u16 regval; + + mutex_lock(&chip->state_lock); + regval = chip->command; + /* + * To be enabled the channel must simply be on. If any are enabled + * use continuous sampling mode. + * Possible to disable temp as well but that makes single read tricky. + */ + + mask = BIT(15 - chan->channel); + + switch (chan->type) { + case IIO_VOLTAGE: + if ((!state) && (chip->c_mask & mask)) + chip->c_mask &= ~mask; + else if (state && (!(chip->c_mask & mask))) + chip->c_mask |= mask; + else + break; + + regval &= ~AD7291_AUTOCYCLE; + regval |= chip->c_mask; + if (chip->c_mask) /* Enable autocycle? */ + regval |= AD7291_AUTOCYCLE; + + ret = ad7291_i2c_write(chip, AD7291_COMMAND, regval); + if (ret < 0) + goto error_ret; + + chip->command = regval; + break; + default: + ret = -EINVAL; + } + +error_ret: + mutex_unlock(&chip->state_lock); + return ret; +} + +static int ad7291_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, + int *val2, + long mask) +{ + int ret; + struct ad7291_chip_info *chip = iio_priv(indio_dev); + u16 regval; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + switch (chan->type) { + case IIO_VOLTAGE: + mutex_lock(&chip->state_lock); + /* If in autocycle mode drop through */ + if (chip->command & AD7291_AUTOCYCLE) { + mutex_unlock(&chip->state_lock); + return -EBUSY; + } + /* Enable this channel alone */ + regval = chip->command & (~AD7291_VOLTAGE_MASK); + regval |= BIT(15 - chan->channel); + ret = ad7291_i2c_write(chip, AD7291_COMMAND, regval); + if (ret < 0) { + mutex_unlock(&chip->state_lock); + return ret; + } + /* Read voltage */ + ret = i2c_smbus_read_word_swapped(chip->client, + AD7291_VOLTAGE); + if (ret < 0) { + mutex_unlock(&chip->state_lock); + return ret; + } + *val = ret & AD7291_VALUE_MASK; + mutex_unlock(&chip->state_lock); + return IIO_VAL_INT; + case IIO_TEMP: + /* Assumes tsense bit of command register always set */ + ret = i2c_smbus_read_word_swapped(chip->client, + AD7291_T_SENSE); + if (ret < 0) + return ret; + *val = sign_extend32(ret, 11); + return IIO_VAL_INT; + default: + return -EINVAL; + } + case IIO_CHAN_INFO_AVERAGE_RAW: + ret = i2c_smbus_read_word_swapped(chip->client, + AD7291_T_AVERAGE); + if (ret < 0) + return ret; + *val = sign_extend32(ret, 11); + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + switch (chan->type) { + case IIO_VOLTAGE: + if (chip->reg) { + int vref; + + vref = regulator_get_voltage(chip->reg); + if (vref < 0) + return vref; + *val = vref / 1000; + } else { + *val = 2500; + } + *val2 = AD7291_BITS; + return IIO_VAL_FRACTIONAL_LOG2; + case IIO_TEMP: + /* + * One LSB of the ADC corresponds to 0.25 deg C. + * The temperature reading is in 12-bit twos + * complement format + */ + *val = 250; + return IIO_VAL_INT; + default: + return -EINVAL; + } + default: + return -EINVAL; + } +} + +static const struct iio_event_spec ad7291_events[] = { + { + .type = IIO_EV_TYPE_THRESH, + .dir = IIO_EV_DIR_RISING, + .mask_separate = BIT(IIO_EV_INFO_VALUE) | + BIT(IIO_EV_INFO_ENABLE), + }, { + .type = IIO_EV_TYPE_THRESH, + .dir = IIO_EV_DIR_FALLING, + .mask_separate = BIT(IIO_EV_INFO_VALUE) | + BIT(IIO_EV_INFO_ENABLE), + }, { + .type = IIO_EV_TYPE_THRESH, + .dir = IIO_EV_DIR_EITHER, + .mask_separate = BIT(IIO_EV_INFO_HYSTERESIS), + }, +}; + +#define AD7291_VOLTAGE_CHAN(_chan) \ +{ \ + .type = IIO_VOLTAGE, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ + .indexed = 1, \ + .channel = _chan, \ + .event_spec = ad7291_events, \ + .num_event_specs = ARRAY_SIZE(ad7291_events), \ +} + +static const struct iio_chan_spec ad7291_channels[] = { + AD7291_VOLTAGE_CHAN(0), + AD7291_VOLTAGE_CHAN(1), + AD7291_VOLTAGE_CHAN(2), + AD7291_VOLTAGE_CHAN(3), + AD7291_VOLTAGE_CHAN(4), + AD7291_VOLTAGE_CHAN(5), + AD7291_VOLTAGE_CHAN(6), + AD7291_VOLTAGE_CHAN(7), + { + .type = IIO_TEMP, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_AVERAGE_RAW) | + BIT(IIO_CHAN_INFO_SCALE), + .indexed = 1, + .channel = 0, + .event_spec = ad7291_events, + .num_event_specs = ARRAY_SIZE(ad7291_events), + } +}; + +static const struct iio_info ad7291_info = { + .read_raw = &ad7291_read_raw, + .read_event_config = &ad7291_read_event_config, + .write_event_config = &ad7291_write_event_config, + .read_event_value = &ad7291_read_event_value, + .write_event_value = &ad7291_write_event_value, +}; + +static void ad7291_reg_disable(void *reg) +{ + regulator_disable(reg); +} + +static int ad7291_probe(struct i2c_client *client) +{ + const struct i2c_device_id *id = i2c_client_get_device_id(client); + struct ad7291_chip_info *chip; + struct iio_dev *indio_dev; + int ret; + + indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*chip)); + if (!indio_dev) + return -ENOMEM; + chip = iio_priv(indio_dev); + + mutex_init(&chip->state_lock); + + chip->client = client; + + chip->command = AD7291_NOISE_DELAY | + AD7291_T_SENSE_MASK | /* Tsense always enabled */ + AD7291_ALERT_POLARITY; /* set irq polarity low level */ + + chip->reg = devm_regulator_get_optional(&client->dev, "vref"); + if (IS_ERR(chip->reg)) { + if (PTR_ERR(chip->reg) != -ENODEV) + return PTR_ERR(chip->reg); + + chip->reg = NULL; + } + + if (chip->reg) { + ret = regulator_enable(chip->reg); + if (ret) + return ret; + + ret = devm_add_action_or_reset(&client->dev, ad7291_reg_disable, + chip->reg); + if (ret) + return ret; + + chip->command |= AD7291_EXT_REF; + } + + indio_dev->name = id->name; + indio_dev->channels = ad7291_channels; + indio_dev->num_channels = ARRAY_SIZE(ad7291_channels); + + indio_dev->info = &ad7291_info; + indio_dev->modes = INDIO_DIRECT_MODE; + + ret = ad7291_i2c_write(chip, AD7291_COMMAND, AD7291_RESET); + if (ret) + return -EIO; + + ret = ad7291_i2c_write(chip, AD7291_COMMAND, chip->command); + if (ret) + return -EIO; + + if (client->irq > 0) { + ret = devm_request_threaded_irq(&client->dev, client->irq, + NULL, + &ad7291_event_handler, + IRQF_TRIGGER_LOW | IRQF_ONESHOT, + id->name, + indio_dev); + if (ret) + return ret; + } + + return devm_iio_device_register(&client->dev, indio_dev); +} + +static const struct i2c_device_id ad7291_id[] = { + { .name = "ad7291" }, + { } +}; + +MODULE_DEVICE_TABLE(i2c, ad7291_id); + +static const struct of_device_id ad7291_of_match[] = { + { .compatible = "adi,ad7291" }, + { } +}; +MODULE_DEVICE_TABLE(of, ad7291_of_match); + +static struct i2c_driver ad7291_driver = { + .driver = { + .name = KBUILD_MODNAME, + .of_match_table = ad7291_of_match, + }, + .probe = ad7291_probe, + .id_table = ad7291_id, +}; +module_i2c_driver(ad7291_driver); + +MODULE_AUTHOR("Sonic Zhang "); +MODULE_DESCRIPTION("Analog Devices AD7291 ADC driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/adc/ad7292.c b/drivers/iio/adc/ad7292.c new file mode 100644 index 000000000..1d7bf46b8 --- /dev/null +++ b/drivers/iio/adc/ad7292.c @@ -0,0 +1,325 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Analog Devices AD7292 SPI ADC driver + * + * Copyright 2019 Analog Devices Inc. + */ + +#include +#include +#include +#include +#include +#include + +#include + +#define ADI_VENDOR_ID 0x0018 + +#define AD7292_INTERNAL_REF_MV 1250 + +/* AD7292 registers definition */ +#define AD7292_REG_VENDOR_ID 0x00 +#define AD7292_REG_CONF_BANK 0x05 +#define AD7292_REG_CONV_COMM 0x0E +#define AD7292_REG_ADC_CH(x) (0x10 + (x)) + +/* AD7292 configuration bank subregisters definition */ +#define AD7292_BANK_REG_VIN_RNG0 0x10 +#define AD7292_BANK_REG_VIN_RNG1 0x11 +#define AD7292_BANK_REG_SAMP_MODE 0x12 + +#define AD7292_RD_FLAG_MSK(x) (BIT(7) | ((x) & 0x3F)) + +/* AD7292_REG_ADC_CONVERSION */ +#define AD7292_ADC_DATA_MASK GENMASK(15, 6) +#define AD7292_ADC_DATA(x) FIELD_GET(AD7292_ADC_DATA_MASK, x) + +/* AD7292_CHANNEL_SAMPLING_MODE */ +#define AD7292_CH_SAMP_MODE(reg, ch) (((reg) >> 8) & BIT(ch)) + +/* AD7292_CHANNEL_VIN_RANGE */ +#define AD7292_CH_VIN_RANGE(reg, ch) ((reg) & BIT(ch)) + +#define AD7292_VOLTAGE_CHAN(_chan) \ +{ \ + .type = IIO_VOLTAGE, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ + BIT(IIO_CHAN_INFO_SCALE), \ + .indexed = 1, \ + .channel = _chan, \ +} + +static const struct iio_chan_spec ad7292_channels[] = { + AD7292_VOLTAGE_CHAN(0), + AD7292_VOLTAGE_CHAN(1), + AD7292_VOLTAGE_CHAN(2), + AD7292_VOLTAGE_CHAN(3), + AD7292_VOLTAGE_CHAN(4), + AD7292_VOLTAGE_CHAN(5), + AD7292_VOLTAGE_CHAN(6), + AD7292_VOLTAGE_CHAN(7) +}; + +static const struct iio_chan_spec ad7292_channels_diff[] = { + { + .type = IIO_VOLTAGE, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), + .indexed = 1, + .differential = 1, + .channel = 0, + .channel2 = 1, + }, + AD7292_VOLTAGE_CHAN(2), + AD7292_VOLTAGE_CHAN(3), + AD7292_VOLTAGE_CHAN(4), + AD7292_VOLTAGE_CHAN(5), + AD7292_VOLTAGE_CHAN(6), + AD7292_VOLTAGE_CHAN(7) +}; + +struct ad7292_state { + struct spi_device *spi; + unsigned short vref_mv; + + __be16 d16 __aligned(IIO_DMA_MINALIGN); + u8 d8[2]; +}; + +static int ad7292_spi_reg_read(struct ad7292_state *st, unsigned int addr) +{ + int ret; + + st->d8[0] = AD7292_RD_FLAG_MSK(addr); + + ret = spi_write_then_read(st->spi, st->d8, 1, &st->d16, 2); + if (ret < 0) + return ret; + + return be16_to_cpu(st->d16); +} + +static int ad7292_spi_subreg_read(struct ad7292_state *st, unsigned int addr, + unsigned int sub_addr, unsigned int len) +{ + unsigned int shift = 16 - (8 * len); + int ret; + + st->d8[0] = AD7292_RD_FLAG_MSK(addr); + st->d8[1] = sub_addr; + + ret = spi_write_then_read(st->spi, st->d8, 2, &st->d16, len); + if (ret < 0) + return ret; + + return (be16_to_cpu(st->d16) >> shift); +} + +static int ad7292_single_conversion(struct ad7292_state *st, + unsigned int chan_addr) +{ + int ret; + + struct spi_transfer t[] = { + { + .tx_buf = &st->d8, + .len = 4, + .delay = { + .value = 6, + .unit = SPI_DELAY_UNIT_USECS + }, + }, { + .rx_buf = &st->d16, + .len = 2, + }, + }; + + st->d8[0] = chan_addr; + st->d8[1] = AD7292_RD_FLAG_MSK(AD7292_REG_CONV_COMM); + + ret = spi_sync_transfer(st->spi, t, ARRAY_SIZE(t)); + + if (ret < 0) + return ret; + + return be16_to_cpu(st->d16); +} + +static int ad7292_vin_range_multiplier(struct ad7292_state *st, int channel) +{ + int samp_mode, range0, range1, factor = 1; + + /* + * Every AD7292 ADC channel may have its input range adjusted according + * to the settings at the ADC sampling mode and VIN range subregisters. + * For a given channel, the minimum input range is equal to Vref, and it + * may be increased by a multiplier factor of 2 or 4 according to the + * following rule: + * If channel is being sampled with respect to AGND: + * factor = 4 if VIN range0 and VIN range1 equal 0 + * factor = 2 if only one of VIN ranges equal 1 + * factor = 1 if both VIN range0 and VIN range1 equal 1 + * If channel is being sampled with respect to AVDD: + * factor = 4 if VIN range0 and VIN range1 equal 0 + * Behavior is undefined if any of VIN range doesn't equal 0 + */ + + samp_mode = ad7292_spi_subreg_read(st, AD7292_REG_CONF_BANK, + AD7292_BANK_REG_SAMP_MODE, 2); + + if (samp_mode < 0) + return samp_mode; + + range0 = ad7292_spi_subreg_read(st, AD7292_REG_CONF_BANK, + AD7292_BANK_REG_VIN_RNG0, 2); + + if (range0 < 0) + return range0; + + range1 = ad7292_spi_subreg_read(st, AD7292_REG_CONF_BANK, + AD7292_BANK_REG_VIN_RNG1, 2); + + if (range1 < 0) + return range1; + + if (AD7292_CH_SAMP_MODE(samp_mode, channel)) { + /* Sampling with respect to AGND */ + if (!AD7292_CH_VIN_RANGE(range0, channel)) + factor *= 2; + + if (!AD7292_CH_VIN_RANGE(range1, channel)) + factor *= 2; + + } else { + /* Sampling with respect to AVDD */ + if (AD7292_CH_VIN_RANGE(range0, channel) || + AD7292_CH_VIN_RANGE(range1, channel)) + return -EPERM; + + factor = 4; + } + + return factor; +} + +static int ad7292_read_raw(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + int *val, int *val2, long info) +{ + struct ad7292_state *st = iio_priv(indio_dev); + unsigned int ch_addr; + int ret; + + switch (info) { + case IIO_CHAN_INFO_RAW: + ch_addr = AD7292_REG_ADC_CH(chan->channel); + ret = ad7292_single_conversion(st, ch_addr); + if (ret < 0) + return ret; + + *val = AD7292_ADC_DATA(ret); + + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + /* + * To convert a raw value to standard units, the IIO defines + * this formula: Scaled value = (raw + offset) * scale. + * For the scale to be a correct multiplier for (raw + offset), + * it must be calculated as the input range divided by the + * number of possible distinct input values. Given the ADC data + * is 10 bit long, it may assume 2^10 distinct values. + * Hence, scale = range / 2^10. The IIO_VAL_FRACTIONAL_LOG2 + * return type indicates to the IIO API to divide *val by 2 to + * the power of *val2 when returning from read_raw. + */ + + ret = ad7292_vin_range_multiplier(st, chan->channel); + if (ret < 0) + return ret; + + *val = st->vref_mv * ret; + *val2 = 10; + return IIO_VAL_FRACTIONAL_LOG2; + default: + break; + } + return -EINVAL; +} + +static const struct iio_info ad7292_info = { + .read_raw = ad7292_read_raw, +}; + +static int ad7292_probe(struct spi_device *spi) +{ + struct device *dev = &spi->dev; + struct ad7292_state *st; + struct iio_dev *indio_dev; + bool diff_channels = false; + int ret; + + indio_dev = devm_iio_device_alloc(dev, sizeof(*st)); + if (!indio_dev) + return -ENOMEM; + + st = iio_priv(indio_dev); + st->spi = spi; + + ret = ad7292_spi_reg_read(st, AD7292_REG_VENDOR_ID); + if (ret != ADI_VENDOR_ID) + return dev_err_probe(dev, -EINVAL, + "Wrong vendor id 0x%x\n", ret); + + ret = devm_regulator_get_enable_read_voltage(dev, "vref"); + if (ret < 0 && ret != -ENODEV) + return ret; + + st->vref_mv = ret == -ENODEV ? AD7292_INTERNAL_REF_MV : ret / 1000; + + indio_dev->name = spi_get_device_id(spi)->name; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->info = &ad7292_info; + + device_for_each_child_node_scoped(dev, child) { + diff_channels = fwnode_property_read_bool(child, + "diff-channels"); + if (diff_channels) + break; + } + + if (diff_channels) { + indio_dev->num_channels = ARRAY_SIZE(ad7292_channels_diff); + indio_dev->channels = ad7292_channels_diff; + } else { + indio_dev->num_channels = ARRAY_SIZE(ad7292_channels); + indio_dev->channels = ad7292_channels; + } + + return devm_iio_device_register(dev, indio_dev); +} + +static const struct spi_device_id ad7292_id_table[] = { + { .name = "ad7292" }, + { } +}; +MODULE_DEVICE_TABLE(spi, ad7292_id_table); + +static const struct of_device_id ad7292_of_match[] = { + { .compatible = "adi,ad7292" }, + { } +}; +MODULE_DEVICE_TABLE(of, ad7292_of_match); + +static struct spi_driver ad7292_driver = { + .driver = { + .name = "ad7292", + .of_match_table = ad7292_of_match, + }, + .probe = ad7292_probe, + .id_table = ad7292_id_table, +}; +module_spi_driver(ad7292_driver); + +MODULE_AUTHOR("Marcelo Schmitt "); +MODULE_DESCRIPTION("Analog Devices AD7292 ADC driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/adc/ad7298.c b/drivers/iio/adc/ad7298.c new file mode 100644 index 000000000..c669022b5 --- /dev/null +++ b/drivers/iio/adc/ad7298.c @@ -0,0 +1,373 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * AD7298 SPI ADC driver + * + * Copyright 2011 Analog Devices Inc. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#define AD7298_WRITE BIT(15) /* write to the control register */ +#define AD7298_REPEAT BIT(14) /* repeated conversion enable */ +#define AD7298_CH(x) BIT(13 - (x)) /* channel select */ +#define AD7298_TSENSE BIT(5) /* temperature conversion enable */ +#define AD7298_EXTREF BIT(2) /* external reference enable */ +#define AD7298_TAVG BIT(1) /* temperature sensor averaging enable */ +#define AD7298_PDD BIT(0) /* partial power down enable */ + +#define AD7298_MAX_CHAN 8 +#define AD7298_INTREF_mV 2500 + +#define AD7298_CH_TEMP 9 + +struct ad7298_state { + struct spi_device *spi; + struct regulator *reg; + unsigned ext_ref; + struct spi_transfer ring_xfer[10]; + struct spi_transfer scan_single_xfer[3]; + struct spi_message ring_msg; + struct spi_message scan_single_msg; + /* + * DMA (thus cache coherency maintenance) requires the + * transfer buffers to live in their own cache lines. + */ + __be16 rx_buf[12] __aligned(IIO_DMA_MINALIGN); + __be16 tx_buf[2]; +}; + +#define AD7298_V_CHAN(index) \ + { \ + .type = IIO_VOLTAGE, \ + .indexed = 1, \ + .channel = index, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ + .address = index, \ + .scan_index = index, \ + .scan_type = { \ + .sign = 'u', \ + .realbits = 12, \ + .storagebits = 16, \ + .endianness = IIO_BE, \ + }, \ + } + +static const struct iio_chan_spec ad7298_channels[] = { + { + .type = IIO_TEMP, + .indexed = 1, + .channel = 0, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SCALE) | + BIT(IIO_CHAN_INFO_OFFSET), + .address = AD7298_CH_TEMP, + .scan_index = -1, + .scan_type = { + .sign = 's', + .realbits = 32, + .storagebits = 32, + }, + }, + AD7298_V_CHAN(0), + AD7298_V_CHAN(1), + AD7298_V_CHAN(2), + AD7298_V_CHAN(3), + AD7298_V_CHAN(4), + AD7298_V_CHAN(5), + AD7298_V_CHAN(6), + AD7298_V_CHAN(7), + IIO_CHAN_SOFT_TIMESTAMP(8), +}; + +/* + * ad7298_update_scan_mode() setup the spi transfer buffer for the new scan mask + */ +static int ad7298_update_scan_mode(struct iio_dev *indio_dev, + const unsigned long *active_scan_mask) +{ + struct ad7298_state *st = iio_priv(indio_dev); + int i, m; + unsigned short command; + int scan_count; + + /* Now compute overall size */ + scan_count = bitmap_weight(active_scan_mask, + iio_get_masklength(indio_dev)); + + command = AD7298_WRITE | st->ext_ref; + + for (i = 0, m = AD7298_CH(0); i < AD7298_MAX_CHAN; i++, m >>= 1) + if (test_bit(i, active_scan_mask)) + command |= m; + + st->tx_buf[0] = cpu_to_be16(command); + + /* build spi ring message */ + st->ring_xfer[0].tx_buf = &st->tx_buf[0]; + st->ring_xfer[0].len = 2; + st->ring_xfer[0].cs_change = 1; + st->ring_xfer[1].tx_buf = &st->tx_buf[1]; + st->ring_xfer[1].len = 2; + st->ring_xfer[1].cs_change = 1; + + spi_message_init(&st->ring_msg); + spi_message_add_tail(&st->ring_xfer[0], &st->ring_msg); + spi_message_add_tail(&st->ring_xfer[1], &st->ring_msg); + + for (i = 0; i < scan_count; i++) { + st->ring_xfer[i + 2].rx_buf = &st->rx_buf[i]; + st->ring_xfer[i + 2].len = 2; + st->ring_xfer[i + 2].cs_change = 1; + spi_message_add_tail(&st->ring_xfer[i + 2], &st->ring_msg); + } + /* make sure last transfer cs_change is not set */ + st->ring_xfer[i + 1].cs_change = 0; + + return 0; +} + +static irqreturn_t ad7298_trigger_handler(int irq, void *p) +{ + struct iio_poll_func *pf = p; + struct iio_dev *indio_dev = pf->indio_dev; + struct ad7298_state *st = iio_priv(indio_dev); + int b_sent; + + b_sent = spi_sync(st->spi, &st->ring_msg); + if (b_sent) + goto done; + + iio_push_to_buffers_with_ts(indio_dev, st->rx_buf, sizeof(st->rx_buf), + iio_get_time_ns(indio_dev)); + +done: + iio_trigger_notify_done(indio_dev->trig); + + return IRQ_HANDLED; +} + +static int ad7298_scan_direct(struct ad7298_state *st, unsigned ch) +{ + int ret; + st->tx_buf[0] = cpu_to_be16(AD7298_WRITE | st->ext_ref | + (AD7298_CH(0) >> ch)); + + ret = spi_sync(st->spi, &st->scan_single_msg); + if (ret) + return ret; + + return be16_to_cpu(st->rx_buf[0]); +} + +static int ad7298_scan_temp(struct ad7298_state *st, int *val) +{ + int ret; + __be16 buf; + + buf = cpu_to_be16(AD7298_WRITE | AD7298_TSENSE | + AD7298_TAVG | st->ext_ref); + + ret = spi_write(st->spi, (u8 *)&buf, 2); + if (ret) + return ret; + + buf = cpu_to_be16(0); + + ret = spi_write(st->spi, (u8 *)&buf, 2); + if (ret) + return ret; + + usleep_range(101, 1000); /* sleep > 100us */ + + ret = spi_read(st->spi, (u8 *)&buf, 2); + if (ret) + return ret; + + *val = sign_extend32(be16_to_cpu(buf), 11); + + return 0; +} + +static int ad7298_get_ref_voltage(struct ad7298_state *st) +{ + int vref; + + if (st->reg) { + vref = regulator_get_voltage(st->reg); + if (vref < 0) + return vref; + + return vref / 1000; + } else { + return AD7298_INTREF_mV; + } +} + +static int ad7298_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, + int *val2, + long m) +{ + int ret; + struct ad7298_state *st = iio_priv(indio_dev); + + switch (m) { + case IIO_CHAN_INFO_RAW: + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; + + if (chan->address == AD7298_CH_TEMP) + ret = ad7298_scan_temp(st, val); + else + ret = ad7298_scan_direct(st, chan->address); + + iio_device_release_direct(indio_dev); + + if (ret < 0) + return ret; + + if (chan->address != AD7298_CH_TEMP) + *val = ret & GENMASK(chan->scan_type.realbits - 1, 0); + + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + switch (chan->type) { + case IIO_VOLTAGE: + *val = ad7298_get_ref_voltage(st); + *val2 = chan->scan_type.realbits; + return IIO_VAL_FRACTIONAL_LOG2; + case IIO_TEMP: + *val = ad7298_get_ref_voltage(st); + *val2 = 10; + return IIO_VAL_FRACTIONAL; + default: + return -EINVAL; + } + case IIO_CHAN_INFO_OFFSET: + *val = 1093 - 2732500 / ad7298_get_ref_voltage(st); + return IIO_VAL_INT; + } + return -EINVAL; +} + +static const struct iio_info ad7298_info = { + .read_raw = &ad7298_read_raw, + .update_scan_mode = ad7298_update_scan_mode, +}; + +static void ad7298_reg_disable(void *data) +{ + struct regulator *reg = data; + + regulator_disable(reg); +} + +static int ad7298_probe(struct spi_device *spi) +{ + struct ad7298_state *st; + struct iio_dev *indio_dev; + int ret; + + indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st)); + if (indio_dev == NULL) + return -ENOMEM; + + st = iio_priv(indio_dev); + + st->reg = devm_regulator_get_optional(&spi->dev, "vref"); + if (!IS_ERR(st->reg)) { + st->ext_ref = AD7298_EXTREF; + } else { + ret = PTR_ERR(st->reg); + if (ret != -ENODEV) + return ret; + + st->reg = NULL; + } + + if (st->reg) { + ret = regulator_enable(st->reg); + if (ret) + return ret; + + ret = devm_add_action_or_reset(&spi->dev, ad7298_reg_disable, + st->reg); + if (ret) + return ret; + } + + st->spi = spi; + + indio_dev->name = spi_get_device_id(spi)->name; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = ad7298_channels; + indio_dev->num_channels = ARRAY_SIZE(ad7298_channels); + indio_dev->info = &ad7298_info; + + /* Setup default message */ + + st->scan_single_xfer[0].tx_buf = &st->tx_buf[0]; + st->scan_single_xfer[0].len = 2; + st->scan_single_xfer[0].cs_change = 1; + st->scan_single_xfer[1].tx_buf = &st->tx_buf[1]; + st->scan_single_xfer[1].len = 2; + st->scan_single_xfer[1].cs_change = 1; + st->scan_single_xfer[2].rx_buf = &st->rx_buf[0]; + st->scan_single_xfer[2].len = 2; + + spi_message_init(&st->scan_single_msg); + spi_message_add_tail(&st->scan_single_xfer[0], &st->scan_single_msg); + spi_message_add_tail(&st->scan_single_xfer[1], &st->scan_single_msg); + spi_message_add_tail(&st->scan_single_xfer[2], &st->scan_single_msg); + + ret = devm_iio_triggered_buffer_setup(&spi->dev, indio_dev, NULL, + &ad7298_trigger_handler, NULL); + if (ret) + return ret; + + return devm_iio_device_register(&spi->dev, indio_dev); +} + +static const struct acpi_device_id ad7298_acpi_ids[] = { + { "INT3494", 0 }, + { } +}; +MODULE_DEVICE_TABLE(acpi, ad7298_acpi_ids); + +static const struct spi_device_id ad7298_id[] = { + { .name = "ad7298" }, + { } +}; +MODULE_DEVICE_TABLE(spi, ad7298_id); + +static struct spi_driver ad7298_driver = { + .driver = { + .name = "ad7298", + .acpi_match_table = ad7298_acpi_ids, + }, + .probe = ad7298_probe, + .id_table = ad7298_id, +}; +module_spi_driver(ad7298_driver); + +MODULE_AUTHOR("Michael Hennerich "); +MODULE_DESCRIPTION("Analog Devices AD7298 ADC"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/adc/ad7380.c b/drivers/iio/adc/ad7380.c new file mode 100644 index 000000000..2e90f125a --- /dev/null +++ b/drivers/iio/adc/ad7380.c @@ -0,0 +1,2154 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Analog Devices AD738x Simultaneous Sampling SAR ADCs + * + * Copyright 2017 Analog Devices Inc. + * Copyright 2024 BayLibre, SAS + * + * Datasheets of supported parts: + * ad7380/1 : https://www.analog.com/media/en/technical-documentation/data-sheets/AD7380-7381.pdf + * ad7383/4 : https://www.analog.com/media/en/technical-documentation/data-sheets/ad7383-7384.pdf + * ad7386/7/8 : https://www.analog.com/media/en/technical-documentation/data-sheets/AD7386-7387-7388.pdf + * ad7380-4 : https://www.analog.com/media/en/technical-documentation/data-sheets/ad7380-4.pdf + * ad7381-4 : https://www.analog.com/media/en/technical-documentation/data-sheets/ad7381-4.pdf + * ad7383/4-4 : https://www.analog.com/media/en/technical-documentation/data-sheets/ad7383-4-ad7384-4.pdf + * ad7386/7/8-4 : https://www.analog.com/media/en/technical-documentation/data-sheets/ad7386-4-7387-4-7388-4.pdf + * ad7389-4 : https://www.analog.com/media/en/technical-documentation/data-sheets/ad7389-4.pdf + * adaq4370-4 : https://www.analog.com/media/en/technical-documentation/data-sheets/adaq4370-4.pdf + * adaq4380-4 : https://www.analog.com/media/en/technical-documentation/data-sheets/adaq4380-4.pdf + * adaq4381-4 : https://www.analog.com/media/en/technical-documentation/data-sheets/adaq4381-4.pdf + * + * HDL ad738x_fmc: https://analogdevicesinc.github.io/hdl/projects/ad738x_fmc/index.html + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#define MAX_NUM_CHANNELS 8 +/* 2.5V internal reference voltage */ +#define AD7380_INTERNAL_REF_MV 2500 +/* 3.3V internal reference voltage for ADAQ */ +#define ADAQ4380_INTERNAL_REF_MV 3300 + +/* reading and writing registers is more reliable at lower than max speed */ +#define AD7380_REG_WR_SPEED_HZ 10000000 + +#define AD7380_REG_WR BIT(15) +#define AD7380_REG_REGADDR GENMASK(14, 12) +#define AD7380_REG_DATA GENMASK(11, 0) + +#define AD7380_REG_ADDR_NOP 0x0 +#define AD7380_REG_ADDR_CONFIG1 0x1 +#define AD7380_REG_ADDR_CONFIG2 0x2 +#define AD7380_REG_ADDR_ALERT 0x3 +#define AD7380_REG_ADDR_ALERT_LOW_TH 0x4 +#define AD7380_REG_ADDR_ALERT_HIGH_TH 0x5 + +#define AD7380_CONFIG1_CH BIT(11) +#define AD7380_CONFIG1_SEQ BIT(10) +#define AD7380_CONFIG1_OS_MODE BIT(9) +#define AD7380_CONFIG1_OSR GENMASK(8, 6) +#define AD7380_CONFIG1_CRC_W BIT(5) +#define AD7380_CONFIG1_CRC_R BIT(4) +#define AD7380_CONFIG1_ALERTEN BIT(3) +#define AD7380_CONFIG1_RES BIT(2) +#define AD7380_CONFIG1_REFSEL BIT(1) +#define AD7380_CONFIG1_PMODE BIT(0) + +#define AD7380_CONFIG2_SDO GENMASK(9, 8) +#define AD7380_CONFIG2_RESET GENMASK(7, 0) + +#define AD7380_CONFIG2_RESET_SOFT 0x3C +#define AD7380_CONFIG2_RESET_HARD 0xFF + +#define AD7380_ALERT_LOW_TH GENMASK(11, 0) +#define AD7380_ALERT_HIGH_TH GENMASK(11, 0) + +#define T_CONVERT_NS 190 /* conversion time */ +#define T_CONVERT_0_NS 10 /* 1st conversion start time (oversampling) */ +#define T_CONVERT_X_NS 500 /* xth conversion start time (oversampling) */ +#define T_POWERUP_US 5000 /* Power up */ + +#define AD7380_DEFAULT_GAIN_MILLI 1000 + +/* + * Using SPI offload, storagebits is always 32, so can't be used to compute struct + * spi_transfer.len. Using realbits instead. + */ +#define AD7380_SPI_BYTES(scan_type) ((scan_type)->realbits > 16 ? 4 : 2) + +struct ad7380_timing_specs { + const unsigned int t_csh_ns; /* CS minimum high time */ +}; + +struct ad7380_chip_info { + const char *name; + const struct iio_chan_spec *channels; + const struct iio_chan_spec *offload_channels; + unsigned int num_channels; + unsigned int num_simult_channels; + bool has_hardware_gain; + bool has_mux; + const char * const *supplies; + unsigned int num_supplies; + bool external_ref_only; + bool internal_ref_only; + unsigned int internal_ref_mv; + const char * const *vcm_supplies; + unsigned int num_vcm_supplies; + const unsigned long *available_scan_masks; + const struct ad7380_timing_specs *timing_specs; + u32 max_conversion_rate_hz; +}; + +static const struct iio_event_spec ad7380_events[] = { + { + .type = IIO_EV_TYPE_THRESH, + .dir = IIO_EV_DIR_RISING, + .mask_shared_by_dir = BIT(IIO_EV_INFO_VALUE), + }, + { + .type = IIO_EV_TYPE_THRESH, + .dir = IIO_EV_DIR_FALLING, + .mask_shared_by_dir = BIT(IIO_EV_INFO_VALUE), + }, + { + .type = IIO_EV_TYPE_THRESH, + .dir = IIO_EV_DIR_EITHER, + .mask_shared_by_all = BIT(IIO_EV_INFO_ENABLE), + }, +}; + +enum { + AD7380_SCAN_TYPE_NORMAL, + AD7380_SCAN_TYPE_RESOLUTION_BOOST, +}; + +/* Extended scan types for 12-bit unsigned chips. */ +static const struct iio_scan_type ad7380_scan_type_12_u[] = { + [AD7380_SCAN_TYPE_NORMAL] = { + .sign = 'u', + .realbits = 12, + .storagebits = 16, + .endianness = IIO_CPU, + }, + [AD7380_SCAN_TYPE_RESOLUTION_BOOST] = { + .sign = 'u', + .realbits = 14, + .storagebits = 16, + .endianness = IIO_CPU, + }, +}; + +/* Extended scan types for 14-bit signed chips. */ +static const struct iio_scan_type ad7380_scan_type_14_s[] = { + [AD7380_SCAN_TYPE_NORMAL] = { + .sign = 's', + .realbits = 14, + .storagebits = 16, + .endianness = IIO_CPU, + }, + [AD7380_SCAN_TYPE_RESOLUTION_BOOST] = { + .sign = 's', + .realbits = 16, + .storagebits = 16, + .endianness = IIO_CPU, + }, +}; + +/* Extended scan types for 14-bit unsigned chips. */ +static const struct iio_scan_type ad7380_scan_type_14_u[] = { + [AD7380_SCAN_TYPE_NORMAL] = { + .sign = 'u', + .realbits = 14, + .storagebits = 16, + .endianness = IIO_CPU, + }, + [AD7380_SCAN_TYPE_RESOLUTION_BOOST] = { + .sign = 'u', + .realbits = 16, + .storagebits = 16, + .endianness = IIO_CPU, + }, +}; + +/* Extended scan types for 16-bit signed_chips. */ +static const struct iio_scan_type ad7380_scan_type_16_s[] = { + [AD7380_SCAN_TYPE_NORMAL] = { + .sign = 's', + .realbits = 16, + .storagebits = 16, + .endianness = IIO_CPU, + }, + [AD7380_SCAN_TYPE_RESOLUTION_BOOST] = { + .sign = 's', + .realbits = 18, + .storagebits = 32, + .endianness = IIO_CPU, + }, +}; + +/* Extended scan types for 16-bit unsigned chips. */ +static const struct iio_scan_type ad7380_scan_type_16_u[] = { + [AD7380_SCAN_TYPE_NORMAL] = { + .sign = 'u', + .realbits = 16, + .storagebits = 16, + .endianness = IIO_CPU, + }, + [AD7380_SCAN_TYPE_RESOLUTION_BOOST] = { + .sign = 'u', + .realbits = 18, + .storagebits = 32, + .endianness = IIO_CPU, + }, +}; + +/* + * Defining here scan types for offload mode, since with current available HDL + * only a value of 32 for storagebits is supported. + */ + +/* Extended scan types for 12-bit unsigned chips, offload support. */ +static const struct iio_scan_type ad7380_scan_type_12_u_offload[] = { + [AD7380_SCAN_TYPE_NORMAL] = { + .sign = 'u', + .realbits = 12, + .storagebits = 32, + .endianness = IIO_CPU, + }, + [AD7380_SCAN_TYPE_RESOLUTION_BOOST] = { + .sign = 'u', + .realbits = 14, + .storagebits = 32, + .endianness = IIO_CPU, + }, +}; + +/* Extended scan types for 14-bit signed chips, offload support. */ +static const struct iio_scan_type ad7380_scan_type_14_s_offload[] = { + [AD7380_SCAN_TYPE_NORMAL] = { + .sign = 's', + .realbits = 14, + .storagebits = 32, + .endianness = IIO_CPU, + }, + [AD7380_SCAN_TYPE_RESOLUTION_BOOST] = { + .sign = 's', + .realbits = 16, + .storagebits = 32, + .endianness = IIO_CPU, + }, +}; + +/* Extended scan types for 14-bit unsigned chips, offload support. */ +static const struct iio_scan_type ad7380_scan_type_14_u_offload[] = { + [AD7380_SCAN_TYPE_NORMAL] = { + .sign = 'u', + .realbits = 14, + .storagebits = 32, + .endianness = IIO_CPU, + }, + [AD7380_SCAN_TYPE_RESOLUTION_BOOST] = { + .sign = 'u', + .realbits = 16, + .storagebits = 32, + .endianness = IIO_CPU, + }, +}; + +/* Extended scan types for 16-bit signed_chips, offload support. */ +static const struct iio_scan_type ad7380_scan_type_16_s_offload[] = { + [AD7380_SCAN_TYPE_NORMAL] = { + .sign = 's', + .realbits = 16, + .storagebits = 32, + .endianness = IIO_CPU, + }, + [AD7380_SCAN_TYPE_RESOLUTION_BOOST] = { + .sign = 's', + .realbits = 18, + .storagebits = 32, + .endianness = IIO_CPU, + }, +}; + +/* Extended scan types for 16-bit unsigned chips, offload support. */ +static const struct iio_scan_type ad7380_scan_type_16_u_offload[] = { + [AD7380_SCAN_TYPE_NORMAL] = { + .sign = 'u', + .realbits = 16, + .storagebits = 32, + .endianness = IIO_CPU, + }, + [AD7380_SCAN_TYPE_RESOLUTION_BOOST] = { + .sign = 'u', + .realbits = 18, + .storagebits = 32, + .endianness = IIO_CPU, + }, +}; + +#define _AD7380_CHANNEL(index, bits, diff, sign, gain) { \ + .type = IIO_VOLTAGE, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ + ((gain) ? BIT(IIO_CHAN_INFO_SCALE) : 0) | \ + ((diff) ? 0 : BIT(IIO_CHAN_INFO_OFFSET)), \ + .info_mask_shared_by_type = ((gain) ? 0 : BIT(IIO_CHAN_INFO_SCALE)) | \ + BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \ + .info_mask_shared_by_type_available = \ + BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \ + .indexed = 1, \ + .differential = (diff), \ + .channel = (diff) ? (2 * (index)) : (index), \ + .channel2 = (diff) ? (2 * (index) + 1) : 0, \ + .scan_index = (index), \ + .has_ext_scan_type = 1, \ + .ext_scan_type = ad7380_scan_type_##bits##_##sign, \ + .num_ext_scan_type = ARRAY_SIZE(ad7380_scan_type_##bits##_##sign), \ + .event_spec = ad7380_events, \ + .num_event_specs = ARRAY_SIZE(ad7380_events), \ +} + +#define _AD7380_OFFLOAD_CHANNEL(index, bits, diff, sign, gain) { \ + .type = IIO_VOLTAGE, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ + ((gain) ? BIT(IIO_CHAN_INFO_SCALE) : 0) | \ + ((diff) ? 0 : BIT(IIO_CHAN_INFO_OFFSET)), \ + .info_mask_shared_by_type = ((gain) ? 0 : BIT(IIO_CHAN_INFO_SCALE)) | \ + BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO) | \ + BIT(IIO_CHAN_INFO_SAMP_FREQ), \ + .info_mask_shared_by_type_available = \ + BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO) | \ + BIT(IIO_CHAN_INFO_SAMP_FREQ), \ + .indexed = 1, \ + .differential = (diff), \ + .channel = (diff) ? (2 * (index)) : (index), \ + .channel2 = (diff) ? (2 * (index) + 1) : 0, \ + .scan_index = (index), \ + .has_ext_scan_type = 1, \ + .ext_scan_type = ad7380_scan_type_##bits##_##sign##_offload, \ + .num_ext_scan_type = \ + ARRAY_SIZE(ad7380_scan_type_##bits##_##sign##_offload), \ + .event_spec = ad7380_events, \ + .num_event_specs = ARRAY_SIZE(ad7380_events), \ +} + +/* + * Notes on the offload channels: + * - There is no soft timestamp since everything is done in hardware. + * - There is a sampling frequency attribute added. This controls the SPI + * offload trigger. + * - The storagebits value depends on the SPI offload provider. Currently there + * is only one supported provider, namely the ADI PULSAR ADC HDL project, + * which always uses 32-bit words for data values, even for <= 16-bit ADCs. + * So the value is just hardcoded to 32 for now. + */ + +#define AD7380_CHANNEL(index, bits, diff, sign) \ + _AD7380_CHANNEL(index, bits, diff, sign, false) + +#define ADAQ4380_CHANNEL(index, bits, diff, sign) \ + _AD7380_CHANNEL(index, bits, diff, sign, true) + +#define DEFINE_AD7380_2_CHANNEL(name, bits, diff, sign) \ +static const struct iio_chan_spec name[] = { \ + AD7380_CHANNEL(0, bits, diff, sign), \ + AD7380_CHANNEL(1, bits, diff, sign), \ + IIO_CHAN_SOFT_TIMESTAMP(2), \ +} + +#define DEFINE_AD7380_4_CHANNEL(name, bits, diff, sign) \ +static const struct iio_chan_spec name[] = { \ + AD7380_CHANNEL(0, bits, diff, sign), \ + AD7380_CHANNEL(1, bits, diff, sign), \ + AD7380_CHANNEL(2, bits, diff, sign), \ + AD7380_CHANNEL(3, bits, diff, sign), \ + IIO_CHAN_SOFT_TIMESTAMP(4), \ +} + +#define DEFINE_ADAQ4380_4_CHANNEL(name, bits, diff, sign) \ +static const struct iio_chan_spec name[] = { \ + ADAQ4380_CHANNEL(0, bits, diff, sign), \ + ADAQ4380_CHANNEL(1, bits, diff, sign), \ + ADAQ4380_CHANNEL(2, bits, diff, sign), \ + ADAQ4380_CHANNEL(3, bits, diff, sign), \ + IIO_CHAN_SOFT_TIMESTAMP(4), \ +} + +#define DEFINE_AD7380_8_CHANNEL(name, bits, diff, sign) \ +static const struct iio_chan_spec name[] = { \ + AD7380_CHANNEL(0, bits, diff, sign), \ + AD7380_CHANNEL(1, bits, diff, sign), \ + AD7380_CHANNEL(2, bits, diff, sign), \ + AD7380_CHANNEL(3, bits, diff, sign), \ + AD7380_CHANNEL(4, bits, diff, sign), \ + AD7380_CHANNEL(5, bits, diff, sign), \ + AD7380_CHANNEL(6, bits, diff, sign), \ + AD7380_CHANNEL(7, bits, diff, sign), \ + IIO_CHAN_SOFT_TIMESTAMP(8), \ +} + +#define AD7380_OFFLOAD_CHANNEL(index, bits, diff, sign) \ +_AD7380_OFFLOAD_CHANNEL(index, bits, diff, sign, false) + +#define ADAQ4380_OFFLOAD_CHANNEL(index, bits, diff, sign) \ +_AD7380_OFFLOAD_CHANNEL(index, bits, diff, sign, true) + +#define DEFINE_AD7380_2_OFFLOAD_CHANNEL(name, bits, diff, sign) \ +static const struct iio_chan_spec name[] = { \ + AD7380_OFFLOAD_CHANNEL(0, bits, diff, sign), \ + AD7380_OFFLOAD_CHANNEL(1, bits, diff, sign), \ +} + +#define DEFINE_AD7380_4_OFFLOAD_CHANNEL(name, bits, diff, sign) \ +static const struct iio_chan_spec name[] = { \ + AD7380_OFFLOAD_CHANNEL(0, bits, diff, sign), \ + AD7380_OFFLOAD_CHANNEL(1, bits, diff, sign), \ + AD7380_OFFLOAD_CHANNEL(2, bits, diff, sign), \ + AD7380_OFFLOAD_CHANNEL(3, bits, diff, sign), \ +} + +#define DEFINE_ADAQ4380_4_OFFLOAD_CHANNEL(name, bits, diff, sign) \ +static const struct iio_chan_spec name[] = { \ + AD7380_OFFLOAD_CHANNEL(0, bits, diff, sign), \ + AD7380_OFFLOAD_CHANNEL(1, bits, diff, sign), \ + AD7380_OFFLOAD_CHANNEL(2, bits, diff, sign), \ + AD7380_OFFLOAD_CHANNEL(3, bits, diff, sign), \ +} + +#define DEFINE_AD7380_8_OFFLOAD_CHANNEL(name, bits, diff, sign) \ +static const struct iio_chan_spec name[] = { \ + AD7380_OFFLOAD_CHANNEL(0, bits, diff, sign), \ + AD7380_OFFLOAD_CHANNEL(1, bits, diff, sign), \ + AD7380_OFFLOAD_CHANNEL(2, bits, diff, sign), \ + AD7380_OFFLOAD_CHANNEL(3, bits, diff, sign), \ + AD7380_OFFLOAD_CHANNEL(4, bits, diff, sign), \ + AD7380_OFFLOAD_CHANNEL(5, bits, diff, sign), \ + AD7380_OFFLOAD_CHANNEL(6, bits, diff, sign), \ + AD7380_OFFLOAD_CHANNEL(7, bits, diff, sign), \ +} + +/* fully differential */ +DEFINE_AD7380_2_CHANNEL(ad7380_channels, 16, 1, s); +DEFINE_AD7380_2_CHANNEL(ad7381_channels, 14, 1, s); +DEFINE_AD7380_4_CHANNEL(ad7380_4_channels, 16, 1, s); +DEFINE_AD7380_4_CHANNEL(ad7381_4_channels, 14, 1, s); +DEFINE_ADAQ4380_4_CHANNEL(adaq4380_4_channels, 16, 1, s); +DEFINE_ADAQ4380_4_CHANNEL(adaq4381_4_channels, 14, 1, s); +/* pseudo differential */ +DEFINE_AD7380_2_CHANNEL(ad7383_channels, 16, 0, s); +DEFINE_AD7380_2_CHANNEL(ad7384_channels, 14, 0, s); +DEFINE_AD7380_4_CHANNEL(ad7383_4_channels, 16, 0, s); +DEFINE_AD7380_4_CHANNEL(ad7384_4_channels, 14, 0, s); + +/* Single ended */ +DEFINE_AD7380_4_CHANNEL(ad7386_channels, 16, 0, u); +DEFINE_AD7380_4_CHANNEL(ad7387_channels, 14, 0, u); +DEFINE_AD7380_4_CHANNEL(ad7388_channels, 12, 0, u); +DEFINE_AD7380_8_CHANNEL(ad7386_4_channels, 16, 0, u); +DEFINE_AD7380_8_CHANNEL(ad7387_4_channels, 14, 0, u); +DEFINE_AD7380_8_CHANNEL(ad7388_4_channels, 12, 0, u); + +/* offload channels */ +DEFINE_AD7380_2_OFFLOAD_CHANNEL(ad7380_offload_channels, 16, 1, s); +DEFINE_AD7380_2_OFFLOAD_CHANNEL(ad7381_offload_channels, 14, 1, s); +DEFINE_AD7380_4_OFFLOAD_CHANNEL(ad7380_4_offload_channels, 16, 1, s); +DEFINE_AD7380_4_OFFLOAD_CHANNEL(ad7381_4_offload_channels, 14, 1, s); +DEFINE_ADAQ4380_4_OFFLOAD_CHANNEL(adaq4380_4_offload_channels, 16, 1, s); +DEFINE_ADAQ4380_4_OFFLOAD_CHANNEL(adaq4381_4_offload_channels, 14, 1, s); + +/* pseudo differential */ +DEFINE_AD7380_2_OFFLOAD_CHANNEL(ad7383_offload_channels, 16, 0, s); +DEFINE_AD7380_2_OFFLOAD_CHANNEL(ad7384_offload_channels, 14, 0, s); +DEFINE_AD7380_4_OFFLOAD_CHANNEL(ad7383_4_offload_channels, 16, 0, s); +DEFINE_AD7380_4_OFFLOAD_CHANNEL(ad7384_4_offload_channels, 14, 0, s); + +/* Single ended */ +DEFINE_AD7380_4_OFFLOAD_CHANNEL(ad7386_offload_channels, 16, 0, u); +DEFINE_AD7380_4_OFFLOAD_CHANNEL(ad7387_offload_channels, 14, 0, u); +DEFINE_AD7380_4_OFFLOAD_CHANNEL(ad7388_offload_channels, 12, 0, u); +DEFINE_AD7380_8_OFFLOAD_CHANNEL(ad7386_4_offload_channels, 16, 0, u); +DEFINE_AD7380_8_OFFLOAD_CHANNEL(ad7387_4_offload_channels, 14, 0, u); +DEFINE_AD7380_8_OFFLOAD_CHANNEL(ad7388_4_offload_channels, 12, 0, u); + +static const char * const ad7380_supplies[] = { + "vcc", "vlogic", +}; + +static const char * const adaq4380_supplies[] = { + "ldo", "vcc", "vlogic", "vs-p", "vs-n", "refin", +}; + +static const char * const ad7380_2_channel_vcm_supplies[] = { + "aina", "ainb", +}; + +static const char * const ad7380_4_channel_vcm_supplies[] = { + "aina", "ainb", "ainc", "aind", +}; + +/* Since this is simultaneous sampling, we don't allow individual channels. */ +static const unsigned long ad7380_2_channel_scan_masks[] = { + GENMASK(1, 0), + 0 +}; + +static const unsigned long ad7380_4_channel_scan_masks[] = { + GENMASK(3, 0), + 0 +}; + +/* + * Single ended parts have a 2:1 multiplexer in front of each ADC. + * + * From an IIO point of view, all inputs are exported, i.e ad7386/7/8 + * export 4 channels and ad7386-4/7-4/8-4 export 8 channels. + * + * Inputs AinX0 of multiplexers correspond to the first half of IIO channels + * (i.e 0-1 or 0-3) and inputs AinX1 correspond to second half (i.e 2-3 or + * 4-7). Example for AD7386/7/8 (2 channels parts): + * + * IIO | AD7386/7/8 + * | +---------------------------- + * | | _____ ______ + * | | | | | | + * voltage0 | AinA0 --|--->| | | | + * | | | mux |----->| ADCA |--- + * voltage2 | AinA1 --|--->| | | | + * | | |_____| |_____ | + * | | _____ ______ + * | | | | | | + * voltage1 | AinB0 --|--->| | | | + * | | | mux |----->| ADCB |--- + * voltage3 | AinB1 --|--->| | | | + * | | |_____| |______| + * | | + * | +---------------------------- + * + * Since this is simultaneous sampling for AinX0 OR AinX1 we have two separate + * scan masks. + * When sequencer mode is enabled, chip automatically cycles through + * AinX0 and AinX1 channels. From an IIO point of view, we ca enable all + * channels, at the cost of an extra read, thus dividing the maximum rate by + * two. + */ +enum { + AD7380_SCAN_MASK_CH_0, + AD7380_SCAN_MASK_CH_1, + AD7380_SCAN_MASK_SEQ, +}; + +static const unsigned long ad7380_2x2_channel_scan_masks[] = { + [AD7380_SCAN_MASK_CH_0] = GENMASK(1, 0), + [AD7380_SCAN_MASK_CH_1] = GENMASK(3, 2), + [AD7380_SCAN_MASK_SEQ] = GENMASK(3, 0), + 0 +}; + +static const unsigned long ad7380_2x4_channel_scan_masks[] = { + [AD7380_SCAN_MASK_CH_0] = GENMASK(3, 0), + [AD7380_SCAN_MASK_CH_1] = GENMASK(7, 4), + [AD7380_SCAN_MASK_SEQ] = GENMASK(7, 0), + 0 +}; + +static const struct ad7380_timing_specs ad7380_timing = { + .t_csh_ns = 10, +}; + +static const struct ad7380_timing_specs ad7380_4_timing = { + .t_csh_ns = 20, +}; + +/* + * Available oversampling ratios. The indices correspond with the bit value + * expected by the chip. The available ratios depend on the averaging mode, + * only normal averaging is supported for now. + */ +static const int ad7380_oversampling_ratios[] = { + 1, 2, 4, 8, 16, 32, +}; + +/* Gains stored as fractions of 1000 so they can be expressed by integers. */ +static const int ad7380_gains[] = { + 300, 600, 1000, 1600, +}; + +static const struct ad7380_chip_info ad7380_chip_info = { + .name = "ad7380", + .channels = ad7380_channels, + .offload_channels = ad7380_offload_channels, + .num_channels = ARRAY_SIZE(ad7380_channels), + .num_simult_channels = 2, + .supplies = ad7380_supplies, + .num_supplies = ARRAY_SIZE(ad7380_supplies), + .internal_ref_mv = AD7380_INTERNAL_REF_MV, + .available_scan_masks = ad7380_2_channel_scan_masks, + .timing_specs = &ad7380_timing, + .max_conversion_rate_hz = 4 * MEGA, +}; + +static const struct ad7380_chip_info ad7381_chip_info = { + .name = "ad7381", + .channels = ad7381_channels, + .offload_channels = ad7381_offload_channels, + .num_channels = ARRAY_SIZE(ad7381_channels), + .num_simult_channels = 2, + .supplies = ad7380_supplies, + .num_supplies = ARRAY_SIZE(ad7380_supplies), + .internal_ref_mv = AD7380_INTERNAL_REF_MV, + .available_scan_masks = ad7380_2_channel_scan_masks, + .timing_specs = &ad7380_timing, + .max_conversion_rate_hz = 4 * MEGA, +}; + +static const struct ad7380_chip_info ad7383_chip_info = { + .name = "ad7383", + .channels = ad7383_channels, + .offload_channels = ad7383_offload_channels, + .num_channels = ARRAY_SIZE(ad7383_channels), + .num_simult_channels = 2, + .supplies = ad7380_supplies, + .num_supplies = ARRAY_SIZE(ad7380_supplies), + .vcm_supplies = ad7380_2_channel_vcm_supplies, + .num_vcm_supplies = ARRAY_SIZE(ad7380_2_channel_vcm_supplies), + .internal_ref_mv = AD7380_INTERNAL_REF_MV, + .available_scan_masks = ad7380_2_channel_scan_masks, + .timing_specs = &ad7380_timing, + .max_conversion_rate_hz = 4 * MEGA, +}; + +static const struct ad7380_chip_info ad7384_chip_info = { + .name = "ad7384", + .channels = ad7384_channels, + .offload_channels = ad7384_offload_channels, + .num_channels = ARRAY_SIZE(ad7384_channels), + .num_simult_channels = 2, + .supplies = ad7380_supplies, + .num_supplies = ARRAY_SIZE(ad7380_supplies), + .vcm_supplies = ad7380_2_channel_vcm_supplies, + .num_vcm_supplies = ARRAY_SIZE(ad7380_2_channel_vcm_supplies), + .internal_ref_mv = AD7380_INTERNAL_REF_MV, + .available_scan_masks = ad7380_2_channel_scan_masks, + .timing_specs = &ad7380_timing, + .max_conversion_rate_hz = 4 * MEGA, +}; + +static const struct ad7380_chip_info ad7386_chip_info = { + .name = "ad7386", + .channels = ad7386_channels, + .offload_channels = ad7386_offload_channels, + .num_channels = ARRAY_SIZE(ad7386_channels), + .num_simult_channels = 2, + .supplies = ad7380_supplies, + .num_supplies = ARRAY_SIZE(ad7380_supplies), + .internal_ref_mv = AD7380_INTERNAL_REF_MV, + .has_mux = true, + .available_scan_masks = ad7380_2x2_channel_scan_masks, + .timing_specs = &ad7380_timing, + .max_conversion_rate_hz = 4 * MEGA, +}; + +static const struct ad7380_chip_info ad7387_chip_info = { + .name = "ad7387", + .channels = ad7387_channels, + .offload_channels = ad7387_offload_channels, + .num_channels = ARRAY_SIZE(ad7387_channels), + .num_simult_channels = 2, + .supplies = ad7380_supplies, + .num_supplies = ARRAY_SIZE(ad7380_supplies), + .internal_ref_mv = AD7380_INTERNAL_REF_MV, + .has_mux = true, + .available_scan_masks = ad7380_2x2_channel_scan_masks, + .timing_specs = &ad7380_timing, + .max_conversion_rate_hz = 4 * MEGA, +}; + +static const struct ad7380_chip_info ad7388_chip_info = { + .name = "ad7388", + .channels = ad7388_channels, + .offload_channels = ad7388_offload_channels, + .num_channels = ARRAY_SIZE(ad7388_channels), + .num_simult_channels = 2, + .supplies = ad7380_supplies, + .num_supplies = ARRAY_SIZE(ad7380_supplies), + .internal_ref_mv = AD7380_INTERNAL_REF_MV, + .has_mux = true, + .available_scan_masks = ad7380_2x2_channel_scan_masks, + .timing_specs = &ad7380_timing, + .max_conversion_rate_hz = 4 * MEGA, +}; + +static const struct ad7380_chip_info ad7380_4_chip_info = { + .name = "ad7380-4", + .channels = ad7380_4_channels, + .offload_channels = ad7380_4_offload_channels, + .num_channels = ARRAY_SIZE(ad7380_4_channels), + .num_simult_channels = 4, + .supplies = ad7380_supplies, + .num_supplies = ARRAY_SIZE(ad7380_supplies), + .external_ref_only = true, + .available_scan_masks = ad7380_4_channel_scan_masks, + .timing_specs = &ad7380_4_timing, + .max_conversion_rate_hz = 4 * MEGA, +}; + +static const struct ad7380_chip_info ad7381_4_chip_info = { + .name = "ad7381-4", + .channels = ad7381_4_channels, + .offload_channels = ad7381_4_offload_channels, + .num_channels = ARRAY_SIZE(ad7381_4_channels), + .num_simult_channels = 4, + .supplies = ad7380_supplies, + .num_supplies = ARRAY_SIZE(ad7380_supplies), + .internal_ref_mv = AD7380_INTERNAL_REF_MV, + .available_scan_masks = ad7380_4_channel_scan_masks, + .timing_specs = &ad7380_4_timing, + .max_conversion_rate_hz = 4 * MEGA, +}; + +static const struct ad7380_chip_info ad7383_4_chip_info = { + .name = "ad7383-4", + .channels = ad7383_4_channels, + .offload_channels = ad7383_4_offload_channels, + .num_channels = ARRAY_SIZE(ad7383_4_channels), + .num_simult_channels = 4, + .supplies = ad7380_supplies, + .num_supplies = ARRAY_SIZE(ad7380_supplies), + .internal_ref_mv = AD7380_INTERNAL_REF_MV, + .vcm_supplies = ad7380_4_channel_vcm_supplies, + .num_vcm_supplies = ARRAY_SIZE(ad7380_4_channel_vcm_supplies), + .available_scan_masks = ad7380_4_channel_scan_masks, + .timing_specs = &ad7380_4_timing, + .max_conversion_rate_hz = 4 * MEGA, +}; + +static const struct ad7380_chip_info ad7384_4_chip_info = { + .name = "ad7384-4", + .channels = ad7384_4_channels, + .offload_channels = ad7384_4_offload_channels, + .num_channels = ARRAY_SIZE(ad7384_4_channels), + .num_simult_channels = 4, + .supplies = ad7380_supplies, + .num_supplies = ARRAY_SIZE(ad7380_supplies), + .internal_ref_mv = AD7380_INTERNAL_REF_MV, + .vcm_supplies = ad7380_4_channel_vcm_supplies, + .num_vcm_supplies = ARRAY_SIZE(ad7380_4_channel_vcm_supplies), + .available_scan_masks = ad7380_4_channel_scan_masks, + .timing_specs = &ad7380_4_timing, + .max_conversion_rate_hz = 4 * MEGA, +}; + +static const struct ad7380_chip_info ad7386_4_chip_info = { + .name = "ad7386-4", + .channels = ad7386_4_channels, + .offload_channels = ad7386_4_offload_channels, + .num_channels = ARRAY_SIZE(ad7386_4_channels), + .num_simult_channels = 4, + .supplies = ad7380_supplies, + .num_supplies = ARRAY_SIZE(ad7380_supplies), + .internal_ref_mv = AD7380_INTERNAL_REF_MV, + .has_mux = true, + .available_scan_masks = ad7380_2x4_channel_scan_masks, + .timing_specs = &ad7380_4_timing, + .max_conversion_rate_hz = 4 * MEGA, +}; + +static const struct ad7380_chip_info ad7387_4_chip_info = { + .name = "ad7387-4", + .channels = ad7387_4_channels, + .offload_channels = ad7387_4_offload_channels, + .num_channels = ARRAY_SIZE(ad7387_4_channels), + .num_simult_channels = 4, + .supplies = ad7380_supplies, + .num_supplies = ARRAY_SIZE(ad7380_supplies), + .internal_ref_mv = AD7380_INTERNAL_REF_MV, + .has_mux = true, + .available_scan_masks = ad7380_2x4_channel_scan_masks, + .timing_specs = &ad7380_4_timing, + .max_conversion_rate_hz = 4 * MEGA, +}; + +static const struct ad7380_chip_info ad7388_4_chip_info = { + .name = "ad7388-4", + .channels = ad7388_4_channels, + .offload_channels = ad7388_4_offload_channels, + .num_channels = ARRAY_SIZE(ad7388_4_channels), + .num_simult_channels = 4, + .supplies = ad7380_supplies, + .num_supplies = ARRAY_SIZE(ad7380_supplies), + .internal_ref_mv = AD7380_INTERNAL_REF_MV, + .has_mux = true, + .available_scan_masks = ad7380_2x4_channel_scan_masks, + .timing_specs = &ad7380_4_timing, + .max_conversion_rate_hz = 4 * MEGA, +}; + +static const struct ad7380_chip_info ad7389_4_chip_info = { + .name = "ad7389-4", + .channels = ad7380_4_channels, + .offload_channels = ad7380_4_offload_channels, + .num_channels = ARRAY_SIZE(ad7380_4_channels), + .num_simult_channels = 4, + .supplies = ad7380_supplies, + .num_supplies = ARRAY_SIZE(ad7380_supplies), + .internal_ref_only = true, + .internal_ref_mv = AD7380_INTERNAL_REF_MV, + .available_scan_masks = ad7380_4_channel_scan_masks, + .timing_specs = &ad7380_4_timing, + .max_conversion_rate_hz = 4 * MEGA, +}; + +static const struct ad7380_chip_info adaq4370_4_chip_info = { + .name = "adaq4370-4", + .channels = adaq4380_4_channels, + .offload_channels = adaq4380_4_offload_channels, + .num_channels = ARRAY_SIZE(adaq4380_4_channels), + .num_simult_channels = 4, + .supplies = adaq4380_supplies, + .num_supplies = ARRAY_SIZE(adaq4380_supplies), + .internal_ref_only = true, + .internal_ref_mv = ADAQ4380_INTERNAL_REF_MV, + .has_hardware_gain = true, + .available_scan_masks = ad7380_4_channel_scan_masks, + .timing_specs = &ad7380_4_timing, + .max_conversion_rate_hz = 2 * MEGA, +}; + +static const struct ad7380_chip_info adaq4380_4_chip_info = { + .name = "adaq4380-4", + .channels = adaq4380_4_channels, + .offload_channels = adaq4380_4_offload_channels, + .num_channels = ARRAY_SIZE(adaq4380_4_channels), + .num_simult_channels = 4, + .supplies = adaq4380_supplies, + .num_supplies = ARRAY_SIZE(adaq4380_supplies), + .internal_ref_only = true, + .internal_ref_mv = ADAQ4380_INTERNAL_REF_MV, + .has_hardware_gain = true, + .available_scan_masks = ad7380_4_channel_scan_masks, + .timing_specs = &ad7380_4_timing, + .max_conversion_rate_hz = 4 * MEGA, +}; + +static const struct ad7380_chip_info adaq4381_4_chip_info = { + .name = "adaq4381-4", + .channels = adaq4381_4_channels, + .offload_channels = adaq4381_4_offload_channels, + .num_channels = ARRAY_SIZE(adaq4381_4_channels), + .num_simult_channels = 4, + .supplies = adaq4380_supplies, + .num_supplies = ARRAY_SIZE(adaq4380_supplies), + .internal_ref_only = true, + .internal_ref_mv = ADAQ4380_INTERNAL_REF_MV, + .has_hardware_gain = true, + .available_scan_masks = ad7380_4_channel_scan_masks, + .timing_specs = &ad7380_4_timing, + .max_conversion_rate_hz = 4 * MEGA, +}; + +static const struct spi_offload_config ad7380_offload_config = { + .capability_flags = SPI_OFFLOAD_CAP_TRIGGER | + SPI_OFFLOAD_CAP_RX_STREAM_DMA, +}; + +struct ad7380_state { + const struct ad7380_chip_info *chip_info; + struct spi_device *spi; + struct regmap *regmap; + bool resolution_boost_enabled; + unsigned int ch; + bool seq; + /* How many SDO lines are wired up. */ + u8 num_sdo_lines; + unsigned int vref_mv; + unsigned int vcm_mv[MAX_NUM_CHANNELS]; + unsigned int gain_milli[MAX_NUM_CHANNELS]; + /* xfers, message an buffer for reading sample data */ + struct spi_transfer normal_xfer[2]; + struct spi_message normal_msg; + struct spi_transfer seq_xfer[4]; + struct spi_message seq_msg; + struct spi_transfer offload_xfer; + struct spi_message offload_msg; + struct spi_offload *offload; + struct spi_offload_trigger *offload_trigger; + unsigned long offload_trigger_hz; + + int sample_freq_range[3]; + /* + * DMA (thus cache coherency maintenance) requires the transfer buffers + * to live in their own cache lines. + * + * Make the buffer large enough for MAX_NUM_CHANNELS 32-bit samples and + * one 64-bit aligned 64-bit timestamp. + */ + IIO_DECLARE_DMA_BUFFER_WITH_TS(u8, scan_data, MAX_NUM_CHANNELS * sizeof(u32)); + /* buffers for reading/writing registers */ + u16 tx; + u16 rx; +}; + +static int ad7380_regmap_reg_write(void *context, unsigned int reg, + unsigned int val) +{ + struct ad7380_state *st = context; + struct spi_transfer xfer = { + .speed_hz = AD7380_REG_WR_SPEED_HZ, + .bits_per_word = 16, + .len = 2, + .tx_buf = &st->tx, + }; + + st->tx = FIELD_PREP(AD7380_REG_WR, 1) | + FIELD_PREP(AD7380_REG_REGADDR, reg) | + FIELD_PREP(AD7380_REG_DATA, val); + + return spi_sync_transfer(st->spi, &xfer, 1); +} + +static int ad7380_regmap_reg_read(void *context, unsigned int reg, + unsigned int *val) +{ + struct ad7380_state *st = context; + struct spi_transfer xfers[] = { + { + .speed_hz = AD7380_REG_WR_SPEED_HZ, + .bits_per_word = 16, + .len = 2, + .tx_buf = &st->tx, + .cs_change = 1, + .cs_change_delay = { + .value = st->chip_info->timing_specs->t_csh_ns, + .unit = SPI_DELAY_UNIT_NSECS, + }, + }, { + .speed_hz = AD7380_REG_WR_SPEED_HZ, + .bits_per_word = 16, + .len = 2, + .rx_buf = &st->rx, + }, + }; + int ret; + + st->tx = FIELD_PREP(AD7380_REG_WR, 0) | + FIELD_PREP(AD7380_REG_REGADDR, reg) | + FIELD_PREP(AD7380_REG_DATA, 0); + + ret = spi_sync_transfer(st->spi, xfers, ARRAY_SIZE(xfers)); + if (ret < 0) + return ret; + + *val = FIELD_GET(AD7380_REG_DATA, st->rx); + + return 0; +} + +static const struct reg_default ad7380_reg_defaults[] = { + { AD7380_REG_ADDR_ALERT_LOW_TH, 0x800 }, + { AD7380_REG_ADDR_ALERT_HIGH_TH, 0x7FF }, +}; + +static const struct regmap_range ad7380_volatile_reg_ranges[] = { + regmap_reg_range(AD7380_REG_ADDR_CONFIG2, AD7380_REG_ADDR_ALERT), +}; + +static const struct regmap_access_table ad7380_volatile_regs = { + .yes_ranges = ad7380_volatile_reg_ranges, + .n_yes_ranges = ARRAY_SIZE(ad7380_volatile_reg_ranges), +}; + +static const struct regmap_config ad7380_regmap_config = { + .reg_bits = 3, + .val_bits = 12, + .reg_read = ad7380_regmap_reg_read, + .reg_write = ad7380_regmap_reg_write, + .max_register = AD7380_REG_ADDR_ALERT_HIGH_TH, + .can_sleep = true, + .reg_defaults = ad7380_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(ad7380_reg_defaults), + .volatile_table = &ad7380_volatile_regs, + .cache_type = REGCACHE_MAPLE, +}; + +static int ad7380_debugfs_reg_access(struct iio_dev *indio_dev, u32 reg, + u32 writeval, u32 *readval) +{ + struct ad7380_state *st = iio_priv(indio_dev); + int ret; + + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; + + if (readval) + ret = regmap_read(st->regmap, reg, readval); + else + ret = regmap_write(st->regmap, reg, writeval); + + iio_device_release_direct(indio_dev); + + return ret; +} + +/** + * ad7380_regval_to_osr - convert OSR register value to ratio + * @regval: register value to check + * + * Returns: the ratio corresponding to the OSR register. If regval is not in + * bound, return 1 (oversampling disabled) + * + */ +static int ad7380_regval_to_osr(unsigned int regval) +{ + if (regval >= ARRAY_SIZE(ad7380_oversampling_ratios)) + return 1; + + return ad7380_oversampling_ratios[regval]; +} + +static int ad7380_get_osr(struct ad7380_state *st, int *val) +{ + u32 tmp; + int ret; + + ret = regmap_read(st->regmap, AD7380_REG_ADDR_CONFIG1, &tmp); + if (ret) + return ret; + + *val = ad7380_regval_to_osr(FIELD_GET(AD7380_CONFIG1_OSR, tmp)); + + return 0; +} + +/* + * When switching channel, the ADC require an additional settling time. + * According to the datasheet, data is value on the third CS low. We already + * have an extra toggle before each read (either direct reads or buffered reads) + * to sample correct data, so we just add a single CS toggle at the end of the + * register write. + */ +static int ad7380_set_ch(struct ad7380_state *st, unsigned int ch) +{ + struct spi_transfer xfer = { + .delay = { + .value = T_CONVERT_NS, + .unit = SPI_DELAY_UNIT_NSECS, + } + }; + int oversampling_ratio, ret; + + if (st->ch == ch) + return 0; + + ret = ad7380_get_osr(st, &oversampling_ratio); + if (ret) + return ret; + + ret = regmap_update_bits(st->regmap, + AD7380_REG_ADDR_CONFIG1, + AD7380_CONFIG1_CH, + FIELD_PREP(AD7380_CONFIG1_CH, ch)); + + if (ret) + return ret; + + st->ch = ch; + + if (oversampling_ratio > 1) + xfer.delay.value = T_CONVERT_0_NS + + T_CONVERT_X_NS * (oversampling_ratio - 1) * + st->chip_info->num_simult_channels / st->num_sdo_lines; + + return spi_sync_transfer(st->spi, &xfer, 1); +} + +/** + * ad7380_update_xfers - update the SPI transfers base on the current scan type + * @st: device instance specific state + * @scan_type: current scan type + */ +static int ad7380_update_xfers(struct ad7380_state *st, + const struct iio_scan_type *scan_type) +{ + struct spi_transfer *xfer = st->seq ? st->seq_xfer : st->normal_xfer; + unsigned int t_convert = T_CONVERT_NS; + int oversampling_ratio, ret; + + /* + * In the case of oversampling, conversion time is higher than in normal + * mode. Technically T_CONVERT_X_NS is lower for some chips, but we use + * the maximum value for simplicity for now. + */ + ret = ad7380_get_osr(st, &oversampling_ratio); + if (ret) + return ret; + + if (oversampling_ratio > 1) + t_convert = T_CONVERT_0_NS + T_CONVERT_X_NS * + (oversampling_ratio - 1) * + st->chip_info->num_simult_channels / st->num_sdo_lines; + + if (st->seq) { + xfer[0].delay.value = xfer[1].delay.value = t_convert; + xfer[0].delay.unit = xfer[1].delay.unit = SPI_DELAY_UNIT_NSECS; + xfer[2].bits_per_word = xfer[3].bits_per_word = + scan_type->realbits; + xfer[2].len = xfer[3].len = + AD7380_SPI_BYTES(scan_type) * + st->chip_info->num_simult_channels; + xfer[3].rx_buf = xfer[2].rx_buf + xfer[2].len; + /* Additional delay required here when oversampling is enabled */ + if (oversampling_ratio > 1) + xfer[2].delay.value = t_convert; + else + xfer[2].delay.value = 0; + xfer[2].delay.unit = SPI_DELAY_UNIT_NSECS; + } else { + xfer[0].delay.value = t_convert; + xfer[0].delay.unit = SPI_DELAY_UNIT_NSECS; + xfer[1].bits_per_word = scan_type->realbits; + xfer[1].len = AD7380_SPI_BYTES(scan_type) * + st->chip_info->num_simult_channels; + } + + return 0; +} + +static int ad7380_set_sample_freq(struct ad7380_state *st, int val) +{ + struct spi_offload_trigger_config config = { + .type = SPI_OFFLOAD_TRIGGER_PERIODIC, + .periodic = { + .frequency_hz = val, + }, + }; + int ret; + + ret = spi_offload_trigger_validate(st->offload_trigger, &config); + if (ret) + return ret; + + st->offload_trigger_hz = config.periodic.frequency_hz; + + return 0; +} + +static int ad7380_init_offload_msg(struct ad7380_state *st, + struct iio_dev *indio_dev) +{ + struct spi_transfer *xfer = &st->offload_xfer; + struct device *dev = &st->spi->dev; + const struct iio_scan_type *scan_type; + int ret; + + scan_type = iio_get_current_scan_type(indio_dev, + &indio_dev->channels[0]); + if (IS_ERR(scan_type)) + return PTR_ERR(scan_type); + + if (st->chip_info->has_mux) { + int index; + + ret = iio_active_scan_mask_index(indio_dev); + if (ret < 0) + return ret; + + index = ret; + if (index == AD7380_SCAN_MASK_SEQ) { + ret = regmap_set_bits(st->regmap, AD7380_REG_ADDR_CONFIG1, + AD7380_CONFIG1_SEQ); + if (ret) + return ret; + + st->seq = true; + } else { + ret = ad7380_set_ch(st, index); + if (ret) + return ret; + } + } + + xfer->bits_per_word = scan_type->realbits; + xfer->offload_flags = SPI_OFFLOAD_XFER_RX_STREAM; + xfer->len = AD7380_SPI_BYTES(scan_type) * st->chip_info->num_simult_channels; + if (st->num_sdo_lines > 1) + xfer->multi_lane_mode = SPI_MULTI_LANE_MODE_STRIPE; + + spi_message_init_with_transfers(&st->offload_msg, xfer, 1); + st->offload_msg.offload = st->offload; + + ret = spi_optimize_message(st->spi, &st->offload_msg); + if (ret) { + dev_err(dev, "failed to prepare offload msg, err: %d\n", + ret); + return ret; + } + + return 0; +} + +static int ad7380_offload_buffer_postenable(struct iio_dev *indio_dev) +{ + struct ad7380_state *st = iio_priv(indio_dev); + struct spi_offload_trigger_config config = { + .type = SPI_OFFLOAD_TRIGGER_PERIODIC, + .periodic = { + .frequency_hz = st->offload_trigger_hz, + }, + }; + int ret; + + ret = ad7380_init_offload_msg(st, indio_dev); + if (ret) + return ret; + + /* + * When the sequencer is required to read all channels, we need to + * trigger twice per sample period in order to read one complete set + * of samples. + */ + if (st->seq) + config.periodic.frequency_hz *= 2; + + ret = spi_offload_trigger_enable(st->offload, st->offload_trigger, &config); + if (ret) + spi_unoptimize_message(&st->offload_msg); + + return ret; +} + +static int ad7380_offload_buffer_predisable(struct iio_dev *indio_dev) +{ + struct ad7380_state *st = iio_priv(indio_dev); + int ret; + + spi_offload_trigger_disable(st->offload, st->offload_trigger); + spi_unoptimize_message(&st->offload_msg); + + if (st->seq) { + ret = regmap_update_bits(st->regmap, + AD7380_REG_ADDR_CONFIG1, + AD7380_CONFIG1_SEQ, + FIELD_PREP(AD7380_CONFIG1_SEQ, 0)); + if (ret) + return ret; + + st->seq = false; + } + + return 0; +} + +static const struct iio_buffer_setup_ops ad7380_offload_buffer_setup_ops = { + .postenable = ad7380_offload_buffer_postenable, + .predisable = ad7380_offload_buffer_predisable, +}; + +static int ad7380_triggered_buffer_preenable(struct iio_dev *indio_dev) +{ + struct ad7380_state *st = iio_priv(indio_dev); + const struct iio_scan_type *scan_type; + struct spi_message *msg = &st->normal_msg; + int ret; + + /* + * Currently, we always read all channels at the same time. The scan_type + * is the same for all channels, so we just pass the first channel. + */ + scan_type = iio_get_current_scan_type(indio_dev, &indio_dev->channels[0]); + if (IS_ERR(scan_type)) + return PTR_ERR(scan_type); + + if (st->chip_info->has_mux) { + unsigned int index; + + /* + * Depending on the requested scan_mask and current state, + * we need to either change CH bit, or enable sequencer mode + * to sample correct data. + * Sequencer mode is enabled if active mask corresponds to all + * IIO channels enabled. Otherwise, CH bit is set. + */ + ret = iio_active_scan_mask_index(indio_dev); + if (ret < 0) + return ret; + + index = ret; + if (index == AD7380_SCAN_MASK_SEQ) { + ret = regmap_update_bits(st->regmap, + AD7380_REG_ADDR_CONFIG1, + AD7380_CONFIG1_SEQ, + FIELD_PREP(AD7380_CONFIG1_SEQ, 1)); + if (ret) + return ret; + msg = &st->seq_msg; + st->seq = true; + } else { + ret = ad7380_set_ch(st, index); + if (ret) + return ret; + } + + } + + ret = ad7380_update_xfers(st, scan_type); + if (ret) + return ret; + + return spi_optimize_message(st->spi, msg); +} + +static int ad7380_triggered_buffer_postdisable(struct iio_dev *indio_dev) +{ + struct ad7380_state *st = iio_priv(indio_dev); + struct spi_message *msg = &st->normal_msg; + int ret; + + if (st->seq) { + ret = regmap_update_bits(st->regmap, + AD7380_REG_ADDR_CONFIG1, + AD7380_CONFIG1_SEQ, + FIELD_PREP(AD7380_CONFIG1_SEQ, 0)); + if (ret) + return ret; + + msg = &st->seq_msg; + st->seq = false; + } + + spi_unoptimize_message(msg); + + return 0; +} + +static const struct iio_buffer_setup_ops ad7380_buffer_setup_ops = { + .preenable = ad7380_triggered_buffer_preenable, + .postdisable = ad7380_triggered_buffer_postdisable, +}; + +static irqreturn_t ad7380_trigger_handler(int irq, void *p) +{ + struct iio_poll_func *pf = p; + struct iio_dev *indio_dev = pf->indio_dev; + struct ad7380_state *st = iio_priv(indio_dev); + struct spi_message *msg = st->seq ? &st->seq_msg : &st->normal_msg; + int ret; + + ret = spi_sync(st->spi, msg); + if (ret) + goto out; + + iio_push_to_buffers_with_ts(indio_dev, &st->scan_data, sizeof(st->scan_data), + pf->timestamp); + +out: + iio_trigger_notify_done(indio_dev->trig); + + return IRQ_HANDLED; +} + +static int ad7380_read_direct(struct ad7380_state *st, unsigned int scan_index, + const struct iio_scan_type *scan_type, int *val) +{ + unsigned int index = scan_index; + int ret; + + if (st->chip_info->has_mux) { + unsigned int ch = 0; + + if (index >= st->chip_info->num_simult_channels) { + index -= st->chip_info->num_simult_channels; + ch = 1; + } + + ret = ad7380_set_ch(st, ch); + if (ret) + return ret; + } + + ret = ad7380_update_xfers(st, scan_type); + if (ret) + return ret; + + ret = spi_sync(st->spi, &st->normal_msg); + if (ret < 0) + return ret; + + if (scan_type->realbits > 16) { + if (scan_type->sign == 's') + *val = sign_extend32(*(u32 *)(st->scan_data + 4 * index), + scan_type->realbits - 1); + else + *val = *(u32 *)(st->scan_data + 4 * index) & + GENMASK(scan_type->realbits - 1, 0); + } else { + if (scan_type->sign == 's') + *val = sign_extend32(*(u16 *)(st->scan_data + 2 * index), + scan_type->realbits - 1); + else + *val = *(u16 *)(st->scan_data + 2 * index) & + GENMASK(scan_type->realbits - 1, 0); + } + + return IIO_VAL_INT; +} + +static int ad7380_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long info) +{ + struct ad7380_state *st = iio_priv(indio_dev); + const struct iio_scan_type *scan_type; + int ret; + + scan_type = iio_get_current_scan_type(indio_dev, chan); + + if (IS_ERR(scan_type)) + return PTR_ERR(scan_type); + + switch (info) { + case IIO_CHAN_INFO_RAW: + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; + + ret = ad7380_read_direct(st, chan->scan_index, + scan_type, val); + + iio_device_release_direct(indio_dev); + + return ret; + case IIO_CHAN_INFO_SCALE: + /* + * According to the datasheet, the LSB size is: + * * (2 × VREF) / 2^N, for differential chips + * * VREF / 2^N, for pseudo-differential chips + * where N is the ADC resolution (i.e realbits) + * + * The gain is stored as a fraction of 1000 and, as we need to + * divide vref_mv by the gain, we invert the gain/1000 fraction. + */ + if (st->chip_info->has_hardware_gain) + *val = mult_frac(st->vref_mv, MILLI, + st->gain_milli[chan->scan_index]); + else + *val = st->vref_mv; + *val2 = scan_type->realbits - chan->differential; + + return IIO_VAL_FRACTIONAL_LOG2; + case IIO_CHAN_INFO_OFFSET: + /* + * According to IIO ABI, offset is applied before scale, + * so offset is: vcm_mv / scale + */ + *val = st->vcm_mv[chan->channel] * (1 << scan_type->realbits) + / st->vref_mv; + + return IIO_VAL_INT; + case IIO_CHAN_INFO_OVERSAMPLING_RATIO: + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; + + ret = ad7380_get_osr(st, val); + + iio_device_release_direct(indio_dev); + + if (ret) + return ret; + + return IIO_VAL_INT; + case IIO_CHAN_INFO_SAMP_FREQ: + *val = st->offload_trigger_hz; + return IIO_VAL_INT; + default: + return -EINVAL; + } +} + +static int ad7380_read_avail(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + const int **vals, int *type, int *length, + long mask) +{ + struct ad7380_state *st = iio_priv(indio_dev); + + switch (mask) { + case IIO_CHAN_INFO_OVERSAMPLING_RATIO: + *vals = ad7380_oversampling_ratios; + *length = ARRAY_SIZE(ad7380_oversampling_ratios); + *type = IIO_VAL_INT; + + return IIO_AVAIL_LIST; + case IIO_CHAN_INFO_SAMP_FREQ: + *vals = st->sample_freq_range; + *type = IIO_VAL_INT; + return IIO_AVAIL_RANGE; + default: + return -EINVAL; + } +} + +/** + * ad7380_osr_to_regval - convert ratio to OSR register value + * @ratio: ratio to check + * + * Check if ratio is present in the list of available ratios and return the + * corresponding value that needs to be written to the register to select that + * ratio. + * + * Returns: register value (0 to 7) or -EINVAL if there is not an exact match + */ +static int ad7380_osr_to_regval(int ratio) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(ad7380_oversampling_ratios); i++) { + if (ratio == ad7380_oversampling_ratios[i]) + return i; + } + + return -EINVAL; +} + +static int ad7380_set_oversampling_ratio(struct ad7380_state *st, int val) +{ + int ret, osr, boost; + + osr = ad7380_osr_to_regval(val); + if (osr < 0) + return osr; + + /* always enable resolution boost when oversampling is enabled */ + boost = osr > 0 ? 1 : 0; + + ret = regmap_update_bits(st->regmap, + AD7380_REG_ADDR_CONFIG1, + AD7380_CONFIG1_OSR | AD7380_CONFIG1_RES, + FIELD_PREP(AD7380_CONFIG1_OSR, osr) | + FIELD_PREP(AD7380_CONFIG1_RES, boost)); + + if (ret) + return ret; + + st->resolution_boost_enabled = boost; + + /* + * Perform a soft reset. This will flush the oversampling + * block and FIFO but will maintain the content of the + * configurable registers. + */ + ret = regmap_update_bits(st->regmap, + AD7380_REG_ADDR_CONFIG2, + AD7380_CONFIG2_RESET, + FIELD_PREP(AD7380_CONFIG2_RESET, + AD7380_CONFIG2_RESET_SOFT)); + return ret; +} +static int ad7380_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int val, + int val2, long mask) +{ + struct ad7380_state *st = iio_priv(indio_dev); + int ret; + + switch (mask) { + case IIO_CHAN_INFO_SAMP_FREQ: + if (val < 1) + return -EINVAL; + return ad7380_set_sample_freq(st, val); + case IIO_CHAN_INFO_OVERSAMPLING_RATIO: + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; + + ret = ad7380_set_oversampling_ratio(st, val); + + iio_device_release_direct(indio_dev); + + return ret; + default: + return -EINVAL; + } +} + +static int ad7380_get_current_scan_type(const struct iio_dev *indio_dev, + const struct iio_chan_spec *chan) +{ + struct ad7380_state *st = iio_priv(indio_dev); + + return st->resolution_boost_enabled ? AD7380_SCAN_TYPE_RESOLUTION_BOOST + : AD7380_SCAN_TYPE_NORMAL; +} + +static int ad7380_read_event_config(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir) +{ + struct ad7380_state *st = iio_priv(indio_dev); + int tmp, ret; + + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; + + ret = regmap_read(st->regmap, AD7380_REG_ADDR_CONFIG1, &tmp); + + iio_device_release_direct(indio_dev); + + if (ret) + return ret; + + return FIELD_GET(AD7380_CONFIG1_ALERTEN, tmp); +} + +static int ad7380_write_event_config(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir, + bool state) +{ + struct ad7380_state *st = iio_priv(indio_dev); + int ret; + + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; + + ret = regmap_update_bits(st->regmap, + AD7380_REG_ADDR_CONFIG1, + AD7380_CONFIG1_ALERTEN, + FIELD_PREP(AD7380_CONFIG1_ALERTEN, state)); + + iio_device_release_direct(indio_dev); + + return ret; +} + +static int ad7380_get_alert_th(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_direction dir, + int *val) +{ + struct ad7380_state *st = iio_priv(indio_dev); + const struct iio_scan_type *scan_type; + int ret, tmp, shift; + + scan_type = iio_get_current_scan_type(indio_dev, chan); + if (IS_ERR(scan_type)) + return PTR_ERR(scan_type); + + /* + * The register value is 12-bits and is compared to the most significant + * bits of raw value, therefore a shift is required to convert this to + * the same scale as the raw value. + */ + shift = scan_type->realbits - 12; + + switch (dir) { + case IIO_EV_DIR_RISING: + ret = regmap_read(st->regmap, + AD7380_REG_ADDR_ALERT_HIGH_TH, + &tmp); + if (ret) + return ret; + + *val = FIELD_GET(AD7380_ALERT_HIGH_TH, tmp) << shift; + return IIO_VAL_INT; + case IIO_EV_DIR_FALLING: + ret = regmap_read(st->regmap, + AD7380_REG_ADDR_ALERT_LOW_TH, + &tmp); + if (ret) + return ret; + + *val = FIELD_GET(AD7380_ALERT_LOW_TH, tmp) << shift; + return IIO_VAL_INT; + default: + return -EINVAL; + } +} + +static int ad7380_read_event_value(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir, + enum iio_event_info info, + int *val, int *val2) +{ + int ret; + + switch (info) { + case IIO_EV_INFO_VALUE: + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; + + ret = ad7380_get_alert_th(indio_dev, chan, dir, val); + + iio_device_release_direct(indio_dev); + return ret; + default: + return -EINVAL; + } +} + +static int ad7380_set_alert_th(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_direction dir, + int val) +{ + struct ad7380_state *st = iio_priv(indio_dev); + const struct iio_scan_type *scan_type; + u16 th; + + /* + * According to the datasheet, + * AD7380_REG_ADDR_ALERT_HIGH_TH[11:0] are the 12 MSB of the + * 16-bits internal alert high register. LSB are set to 0xf. + * AD7380_REG_ADDR_ALERT_LOW_TH[11:0] are the 12 MSB of the + * 16 bits internal alert low register. LSB are set to 0x0. + * + * When alert is enabled the conversion from the adc is compared + * immediately to the alert high/low thresholds, before any + * oversampling. This means that the thresholds are the same for + * normal mode and oversampling mode. + */ + + /* Extract the 12 MSB of val */ + scan_type = iio_get_current_scan_type(indio_dev, chan); + if (IS_ERR(scan_type)) + return PTR_ERR(scan_type); + + th = val >> (scan_type->realbits - 12); + + switch (dir) { + case IIO_EV_DIR_RISING: + return regmap_write(st->regmap, + AD7380_REG_ADDR_ALERT_HIGH_TH, + th); + case IIO_EV_DIR_FALLING: + return regmap_write(st->regmap, + AD7380_REG_ADDR_ALERT_LOW_TH, + th); + default: + return -EINVAL; + } +} + +static int ad7380_write_event_value(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir, + enum iio_event_info info, + int val, int val2) +{ + int ret; + + switch (info) { + case IIO_EV_INFO_VALUE: + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; + + ret = ad7380_set_alert_th(indio_dev, chan, dir, val); + + iio_device_release_direct(indio_dev); + return ret; + default: + return -EINVAL; + } +} + +static const struct iio_info ad7380_info = { + .read_raw = &ad7380_read_raw, + .read_avail = &ad7380_read_avail, + .write_raw = &ad7380_write_raw, + .get_current_scan_type = &ad7380_get_current_scan_type, + .debugfs_reg_access = &ad7380_debugfs_reg_access, + .read_event_config = &ad7380_read_event_config, + .write_event_config = &ad7380_write_event_config, + .read_event_value = &ad7380_read_event_value, + .write_event_value = &ad7380_write_event_value, +}; + +static int ad7380_init(struct ad7380_state *st, bool external_ref_en) +{ + u32 sdo; + int ret; + + /* perform hard reset */ + ret = regmap_update_bits(st->regmap, AD7380_REG_ADDR_CONFIG2, + AD7380_CONFIG2_RESET, + FIELD_PREP(AD7380_CONFIG2_RESET, + AD7380_CONFIG2_RESET_HARD)); + if (ret < 0) + return ret; + + if (external_ref_en) { + /* select external reference voltage */ + ret = regmap_set_bits(st->regmap, AD7380_REG_ADDR_CONFIG1, + AD7380_CONFIG1_REFSEL); + if (ret < 0) + return ret; + } + + /* This is the default value after reset. */ + st->ch = 0; + st->seq = false; + + /* SDO field has an irregular mapping. */ + switch (st->num_sdo_lines) { + case 1: + sdo = 1; + break; + case 2: + sdo = 0; + break; + case 4: + sdo = 2; + break; + default: + return -EINVAL; + } + + return regmap_update_bits(st->regmap, AD7380_REG_ADDR_CONFIG2, + AD7380_CONFIG2_SDO, + FIELD_PREP(AD7380_CONFIG2_SDO, sdo)); +} + +static int ad7380_probe_spi_offload(struct iio_dev *indio_dev, + struct ad7380_state *st) +{ + struct spi_device *spi = st->spi; + struct device *dev = &spi->dev; + struct dma_chan *rx_dma; + int sample_rate, ret; + + indio_dev->setup_ops = &ad7380_offload_buffer_setup_ops; + indio_dev->channels = st->chip_info->offload_channels; + /* Just removing the timestamp channel. */ + indio_dev->num_channels--; + + st->offload_trigger = devm_spi_offload_trigger_get(dev, st->offload, + SPI_OFFLOAD_TRIGGER_PERIODIC); + if (IS_ERR(st->offload_trigger)) + return dev_err_probe(dev, PTR_ERR(st->offload_trigger), + "failed to get offload trigger\n"); + + sample_rate = st->chip_info->max_conversion_rate_hz * + st->num_sdo_lines / st->chip_info->num_simult_channels; + + st->sample_freq_range[0] = 1; /* min */ + st->sample_freq_range[1] = 1; /* step */ + st->sample_freq_range[2] = sample_rate; /* max */ + + /* + * Starting with a quite low frequency, to allow oversampling x32, + * user is then responsible to adjust the frequency for the specific case. + */ + ret = ad7380_set_sample_freq(st, sample_rate / 32); + if (ret) + return ret; + + rx_dma = devm_spi_offload_rx_stream_request_dma_chan(dev, st->offload); + if (IS_ERR(rx_dma)) + return dev_err_probe(dev, PTR_ERR(rx_dma), + "failed to get offload RX DMA\n"); + + ret = devm_iio_dmaengine_buffer_setup_with_handle(dev, indio_dev, + rx_dma, IIO_BUFFER_DIRECTION_IN); + if (ret) + return dev_err_probe(dev, ret, "cannot setup dma buffer\n"); + + return 0; +} + +static int ad7380_probe(struct spi_device *spi) +{ + struct device *dev = &spi->dev; + struct iio_dev *indio_dev; + struct ad7380_state *st; + bool external_ref_en; + int ret, i; + + indio_dev = devm_iio_device_alloc(dev, sizeof(*st)); + if (!indio_dev) + return -ENOMEM; + + st = iio_priv(indio_dev); + st->spi = spi; + st->chip_info = spi_get_device_match_data(spi); + if (!st->chip_info) + return dev_err_probe(dev, -EINVAL, "missing match data\n"); + + st->num_sdo_lines = spi->num_rx_lanes; + + if (st->num_sdo_lines < 1 || st->num_sdo_lines > st->chip_info->num_simult_channels) + return dev_err_probe(dev, -EINVAL, + "invalid number of SDO lines (%d)\n", + st->num_sdo_lines); + + ret = devm_regulator_bulk_get_enable(dev, st->chip_info->num_supplies, + st->chip_info->supplies); + + if (ret) + return dev_err_probe(dev, ret, + "Failed to enable power supplies\n"); + fsleep(T_POWERUP_US); + + if (st->chip_info->internal_ref_only) { + /* + * ADAQ chips use fixed internal reference but still + * require a specific reference supply to power it. + * "refin" is already enabled with other power supplies + * in bulk_get_enable(). + */ + + st->vref_mv = st->chip_info->internal_ref_mv; + + /* these chips don't have a register bit for this */ + external_ref_en = false; + } else if (st->chip_info->external_ref_only) { + ret = devm_regulator_get_enable_read_voltage(dev, "refin"); + if (ret < 0) + return dev_err_probe(dev, ret, + "Failed to get refin regulator\n"); + + st->vref_mv = ret / 1000; + + /* these chips don't have a register bit for this */ + external_ref_en = false; + } else { + /* + * If there is no REFIO supply, then it means that we are using + * the internal reference, otherwise REFIO is reference voltage. + */ + ret = devm_regulator_get_enable_read_voltage(dev, "refio"); + if (ret < 0 && ret != -ENODEV) + return dev_err_probe(dev, ret, + "Failed to get refio regulator\n"); + + external_ref_en = ret != -ENODEV; + st->vref_mv = external_ref_en ? ret / 1000 + : st->chip_info->internal_ref_mv; + } + + if (st->chip_info->num_vcm_supplies > ARRAY_SIZE(st->vcm_mv)) + return dev_err_probe(dev, -EINVAL, + "invalid number of VCM supplies\n"); + + /* + * pseudo-differential chips have common mode supplies for the negative + * input pin. + */ + for (i = 0; i < st->chip_info->num_vcm_supplies; i++) { + const char *vcm = st->chip_info->vcm_supplies[i]; + + ret = devm_regulator_get_enable_read_voltage(dev, vcm); + if (ret < 0) + return dev_err_probe(dev, ret, + "Failed to get %s regulator\n", + vcm); + + st->vcm_mv[i] = ret / 1000; + } + + for (i = 0; i < MAX_NUM_CHANNELS; i++) + st->gain_milli[i] = AD7380_DEFAULT_GAIN_MILLI; + + if (st->chip_info->has_hardware_gain) { + device_for_each_child_node_scoped(dev, node) { + unsigned int channel; + int gain_idx; + u16 gain; + + ret = fwnode_property_read_u32(node, "reg", &channel); + if (ret) + return dev_err_probe(dev, ret, + "Failed to read reg property\n"); + + if (channel >= st->chip_info->num_channels - 1) + return dev_err_probe(dev, -EINVAL, + "Invalid channel number %i\n", + channel); + + ret = fwnode_property_read_u16(node, "adi,gain-milli", + &gain); + if (ret && ret != -EINVAL) + return dev_err_probe(dev, ret, + "Failed to read gain for channel %i\n", + channel); + if (ret != -EINVAL) { + /* + * Match gain value from dt to one of supported + * gains + */ + gain_idx = find_closest(gain, ad7380_gains, + ARRAY_SIZE(ad7380_gains)); + st->gain_milli[channel] = ad7380_gains[gain_idx]; + } + } + } + + st->regmap = devm_regmap_init(dev, NULL, st, &ad7380_regmap_config); + if (IS_ERR(st->regmap)) + return dev_err_probe(dev, PTR_ERR(st->regmap), + "failed to allocate register map\n"); + + /* + * Setting up xfer structures for both normal and sequence mode. These + * struct are used for both direct read and triggered buffer. Additional + * fields will be set up in ad7380_update_xfers() based on the current + * state of the driver at the time of the read. + */ + + /* + * In normal mode a read is composed of two steps: + * - first, toggle CS (no data xfer) to trigger a conversion + * - then, read data + */ + st->normal_xfer[0].cs_change = 1; + st->normal_xfer[0].cs_change_delay.value = st->chip_info->timing_specs->t_csh_ns; + st->normal_xfer[0].cs_change_delay.unit = SPI_DELAY_UNIT_NSECS; + st->normal_xfer[1].rx_buf = st->scan_data; + if (st->num_sdo_lines > 1) + st->normal_xfer[1].multi_lane_mode = SPI_MULTI_LANE_MODE_STRIPE; + + spi_message_init_with_transfers(&st->normal_msg, st->normal_xfer, + ARRAY_SIZE(st->normal_xfer)); + /* + * In sequencer mode a read is composed of four steps: + * - CS toggle (no data xfer) to get the right point in the sequence + * - CS toggle (no data xfer) to trigger a conversion of AinX0 and + * acquisition of AinX1 + * - 2 data reads, to read AinX0 and AinX1 + */ + st->seq_xfer[0].cs_change = 1; + st->seq_xfer[0].cs_change_delay.value = st->chip_info->timing_specs->t_csh_ns; + st->seq_xfer[0].cs_change_delay.unit = SPI_DELAY_UNIT_NSECS; + st->seq_xfer[1].cs_change = 1; + st->seq_xfer[1].cs_change_delay.value = st->chip_info->timing_specs->t_csh_ns; + st->seq_xfer[1].cs_change_delay.unit = SPI_DELAY_UNIT_NSECS; + + st->seq_xfer[2].rx_buf = st->scan_data; + st->seq_xfer[2].cs_change = 1; + st->seq_xfer[2].cs_change_delay.value = st->chip_info->timing_specs->t_csh_ns; + st->seq_xfer[2].cs_change_delay.unit = SPI_DELAY_UNIT_NSECS; + if (st->num_sdo_lines > 1) { + st->seq_xfer[2].multi_lane_mode = SPI_MULTI_LANE_MODE_STRIPE; + st->seq_xfer[3].multi_lane_mode = SPI_MULTI_LANE_MODE_STRIPE; + } + + spi_message_init_with_transfers(&st->seq_msg, st->seq_xfer, + ARRAY_SIZE(st->seq_xfer)); + + indio_dev->channels = st->chip_info->channels; + indio_dev->num_channels = st->chip_info->num_channels; + indio_dev->name = st->chip_info->name; + indio_dev->info = &ad7380_info; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->available_scan_masks = st->chip_info->available_scan_masks; + + st->offload = devm_spi_offload_get(dev, spi, &ad7380_offload_config); + ret = PTR_ERR_OR_ZERO(st->offload); + if (ret && ret != -ENODEV) + return dev_err_probe(dev, ret, "failed to get offload\n"); + + /* If no SPI offload, fall back to low speed usage. */ + if (ret == -ENODEV) { + ret = devm_iio_triggered_buffer_setup(dev, indio_dev, + iio_pollfunc_store_time, + ad7380_trigger_handler, + &ad7380_buffer_setup_ops); + if (ret) + return ret; + } else { + ret = ad7380_probe_spi_offload(indio_dev, st); + if (ret) + return ret; + } + + ret = ad7380_init(st, external_ref_en); + if (ret) + return ret; + + return devm_iio_device_register(dev, indio_dev); +} + +static const struct of_device_id ad7380_of_match_table[] = { + { .compatible = "adi,ad7380", .data = &ad7380_chip_info }, + { .compatible = "adi,ad7381", .data = &ad7381_chip_info }, + { .compatible = "adi,ad7383", .data = &ad7383_chip_info }, + { .compatible = "adi,ad7384", .data = &ad7384_chip_info }, + { .compatible = "adi,ad7386", .data = &ad7386_chip_info }, + { .compatible = "adi,ad7387", .data = &ad7387_chip_info }, + { .compatible = "adi,ad7388", .data = &ad7388_chip_info }, + { .compatible = "adi,ad7380-4", .data = &ad7380_4_chip_info }, + { .compatible = "adi,ad7381-4", .data = &ad7381_4_chip_info }, + { .compatible = "adi,ad7383-4", .data = &ad7383_4_chip_info }, + { .compatible = "adi,ad7384-4", .data = &ad7384_4_chip_info }, + { .compatible = "adi,ad7386-4", .data = &ad7386_4_chip_info }, + { .compatible = "adi,ad7387-4", .data = &ad7387_4_chip_info }, + { .compatible = "adi,ad7388-4", .data = &ad7388_4_chip_info }, + { .compatible = "adi,ad7389-4", .data = &ad7389_4_chip_info }, + { .compatible = "adi,adaq4370-4", .data = &adaq4370_4_chip_info }, + { .compatible = "adi,adaq4380-4", .data = &adaq4380_4_chip_info }, + { .compatible = "adi,adaq4381-4", .data = &adaq4381_4_chip_info }, + { } +}; + +static const struct spi_device_id ad7380_id_table[] = { + { .name = "ad7380", .driver_data = (kernel_ulong_t)&ad7380_chip_info }, + { .name = "ad7381", .driver_data = (kernel_ulong_t)&ad7381_chip_info }, + { .name = "ad7383", .driver_data = (kernel_ulong_t)&ad7383_chip_info }, + { .name = "ad7384", .driver_data = (kernel_ulong_t)&ad7384_chip_info }, + { .name = "ad7386", .driver_data = (kernel_ulong_t)&ad7386_chip_info }, + { .name = "ad7387", .driver_data = (kernel_ulong_t)&ad7387_chip_info }, + { .name = "ad7388", .driver_data = (kernel_ulong_t)&ad7388_chip_info }, + { .name = "ad7380-4", .driver_data = (kernel_ulong_t)&ad7380_4_chip_info }, + { .name = "ad7381-4", .driver_data = (kernel_ulong_t)&ad7381_4_chip_info }, + { .name = "ad7383-4", .driver_data = (kernel_ulong_t)&ad7383_4_chip_info }, + { .name = "ad7384-4", .driver_data = (kernel_ulong_t)&ad7384_4_chip_info }, + { .name = "ad7386-4", .driver_data = (kernel_ulong_t)&ad7386_4_chip_info }, + { .name = "ad7387-4", .driver_data = (kernel_ulong_t)&ad7387_4_chip_info }, + { .name = "ad7388-4", .driver_data = (kernel_ulong_t)&ad7388_4_chip_info }, + { .name = "ad7389-4", .driver_data = (kernel_ulong_t)&ad7389_4_chip_info }, + { .name = "adaq4370-4", .driver_data = (kernel_ulong_t)&adaq4370_4_chip_info }, + { .name = "adaq4380-4", .driver_data = (kernel_ulong_t)&adaq4380_4_chip_info }, + { .name = "adaq4381-4", .driver_data = (kernel_ulong_t)&adaq4381_4_chip_info }, + { } +}; +MODULE_DEVICE_TABLE(spi, ad7380_id_table); + +static struct spi_driver ad7380_driver = { + .driver = { + .name = "ad7380", + .of_match_table = ad7380_of_match_table, + }, + .probe = ad7380_probe, + .id_table = ad7380_id_table, +}; +module_spi_driver(ad7380_driver); + +MODULE_AUTHOR("Stefan Popa "); +MODULE_DESCRIPTION("Analog Devices AD738x ADC driver"); +MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS("IIO_DMAENGINE_BUFFER"); diff --git a/drivers/iio/adc/ad7405.c b/drivers/iio/adc/ad7405.c new file mode 100644 index 000000000..0996f48d7 --- /dev/null +++ b/drivers/iio/adc/ad7405.c @@ -0,0 +1,252 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Analog Devices AD7405 driver + * + * Copyright 2025 Analog Devices Inc. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +static const unsigned int ad7405_dec_rates_range[] = { + 32, 1, 4096, +}; + +struct ad7405_chip_info { + const char *name; + const unsigned int full_scale_mv; +}; + +struct ad7405_state { + struct iio_backend *back; + const struct ad7405_chip_info *info; + unsigned int ref_frequency; + unsigned int dec_rate; +}; + +static int ad7405_set_dec_rate(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + unsigned int dec_rate) +{ + struct ad7405_state *st = iio_priv(indio_dev); + int ret; + + if (dec_rate > 4096 || dec_rate < 32) + return -EINVAL; + + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; + + ret = iio_backend_oversampling_ratio_set(st->back, chan->scan_index, dec_rate); + iio_device_release_direct(indio_dev); + + if (ret < 0) + return ret; + + st->dec_rate = dec_rate; + + return 0; +} + +static int ad7405_read_raw(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, int *val, + int *val2, long info) +{ + struct ad7405_state *st = iio_priv(indio_dev); + + switch (info) { + case IIO_CHAN_INFO_SCALE: + *val = st->info->full_scale_mv; + *val2 = indio_dev->channels[0].scan_type.realbits - 1; + return IIO_VAL_FRACTIONAL_LOG2; + case IIO_CHAN_INFO_OVERSAMPLING_RATIO: + *val = st->dec_rate; + return IIO_VAL_INT; + case IIO_CHAN_INFO_SAMP_FREQ: + *val = DIV_ROUND_CLOSEST_ULL(st->ref_frequency, st->dec_rate); + return IIO_VAL_INT; + case IIO_CHAN_INFO_OFFSET: + *val = -(1 << (indio_dev->channels[0].scan_type.realbits - 1)); + return IIO_VAL_INT; + default: + return -EINVAL; + } +} + +static int ad7405_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int val, + int val2, long info) +{ + switch (info) { + case IIO_CHAN_INFO_OVERSAMPLING_RATIO: + if (val < 0) + return -EINVAL; + return ad7405_set_dec_rate(indio_dev, chan, val); + default: + return -EINVAL; + } +} + +static int ad7405_read_avail(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + const int **vals, int *type, int *length, + long info) +{ + switch (info) { + case IIO_CHAN_INFO_OVERSAMPLING_RATIO: + *vals = ad7405_dec_rates_range; + *type = IIO_VAL_INT; + return IIO_AVAIL_RANGE; + default: + return -EINVAL; + } +} + +static const struct iio_info ad7405_iio_info = { + .read_raw = &ad7405_read_raw, + .write_raw = &ad7405_write_raw, + .read_avail = &ad7405_read_avail, +}; + +static const struct iio_chan_spec ad7405_channel = { + .type = IIO_VOLTAGE, + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | + BIT(IIO_CHAN_INFO_OFFSET), + .info_mask_shared_by_all = IIO_CHAN_INFO_SAMP_FREQ | + BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), + .info_mask_shared_by_all_available = + BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), + .indexed = 1, + .channel = 0, + .channel2 = 1, + .differential = 1, + .scan_index = 0, + .scan_type = { + .sign = 'u', + .realbits = 16, + .storagebits = 16, + }, +}; + +static const struct ad7405_chip_info ad7405_chip_info = { + .name = "ad7405", + .full_scale_mv = 320, +}; + +static const struct ad7405_chip_info adum7701_chip_info = { + .name = "adum7701", + .full_scale_mv = 320, +}; + +static const struct ad7405_chip_info adum7702_chip_info = { + .name = "adum7702", + .full_scale_mv = 64, +}; + +static const struct ad7405_chip_info adum7703_chip_info = { + .name = "adum7703", + .full_scale_mv = 320, +}; + +static const char * const ad7405_power_supplies[] = { + "vdd1", "vdd2", +}; + +static int ad7405_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct iio_dev *indio_dev; + struct ad7405_state *st; + struct clk *clk; + int ret; + + indio_dev = devm_iio_device_alloc(dev, sizeof(*st)); + if (!indio_dev) + return -ENOMEM; + + st = iio_priv(indio_dev); + + st->info = device_get_match_data(dev); + if (!st->info) + return dev_err_probe(dev, -EINVAL, "no chip info\n"); + + ret = devm_regulator_bulk_get_enable(dev, ARRAY_SIZE(ad7405_power_supplies), + ad7405_power_supplies); + if (ret) + return dev_err_probe(dev, ret, "failed to get and enable supplies"); + + clk = devm_clk_get_enabled(dev, NULL); + if (IS_ERR(clk)) + return PTR_ERR(clk); + + st->ref_frequency = clk_get_rate(clk); + if (!st->ref_frequency) + return -EINVAL; + + indio_dev->name = st->info->name; + indio_dev->channels = &ad7405_channel; + indio_dev->num_channels = 1; + indio_dev->info = &ad7405_iio_info; + + st->back = devm_iio_backend_get(dev, NULL); + if (IS_ERR(st->back)) + return dev_err_probe(dev, PTR_ERR(st->back), + "failed to get IIO backend"); + + ret = iio_backend_chan_enable(st->back, 0); + if (ret) + return ret; + + ret = devm_iio_backend_request_buffer(dev, st->back, indio_dev); + if (ret) + return ret; + + ret = devm_iio_backend_enable(dev, st->back); + if (ret) + return ret; + + /* + * Set 256 decimation rate. The default value in the AXI_ADC register + * is 0, so we set the register with a decimation rate value that is + * functional for all parts. + */ + ret = ad7405_set_dec_rate(indio_dev, &indio_dev->channels[0], 256); + if (ret) + return ret; + + return devm_iio_device_register(dev, indio_dev); +} + +static const struct of_device_id ad7405_of_match[] = { + { .compatible = "adi,ad7405", .data = &ad7405_chip_info, }, + { .compatible = "adi,adum7701", .data = &adum7701_chip_info, }, + { .compatible = "adi,adum7702", .data = &adum7702_chip_info, }, + { .compatible = "adi,adum7703", .data = &adum7703_chip_info, }, + { } +}; +MODULE_DEVICE_TABLE(of, ad7405_of_match); + +static struct platform_driver ad7405_driver = { + .driver = { + .name = "ad7405", + .of_match_table = ad7405_of_match, + }, + .probe = ad7405_probe, +}; +module_platform_driver(ad7405_driver); + +MODULE_AUTHOR("Dragos Bogdan "); +MODULE_AUTHOR("Pop Ioan Daniel "); +MODULE_DESCRIPTION("Analog Devices AD7405 driver"); +MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS("IIO_BACKEND"); diff --git a/drivers/iio/adc/ad7476.c b/drivers/iio/adc/ad7476.c new file mode 100644 index 000000000..c007f453a --- /dev/null +++ b/drivers/iio/adc/ad7476.c @@ -0,0 +1,465 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Analog Devices AD7466/7/8 AD7476/5/7/8 (A) SPI ADC driver + * TI ADC081S/ADC101S/ADC121S 8/10/12-bit SPI ADC driver + * + * Copyright 2010 Analog Devices Inc. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +struct ad7476_state; + +struct ad7476_chip_info { + unsigned int int_vref_mv; + struct iio_chan_spec channel[2]; + void (*reset)(struct ad7476_state *); + void (*conversion_pre_op)(struct ad7476_state *st); + void (*conversion_post_op)(struct ad7476_state *st); + bool has_vref; + bool has_vdrive; + bool convstart_required; +}; + +struct ad7476_state { + struct spi_device *spi; + const struct ad7476_chip_info *chip_info; + struct gpio_desc *convst_gpio; + struct spi_transfer xfer; + struct spi_message msg; + struct iio_chan_spec channel[2]; + int scale_mv; + /* + * DMA (thus cache coherency maintenance) may require the + * transfer buffers to live in their own cache lines. + * Make the buffer large enough for one 16 bit sample and one 64 bit + * aligned 64 bit timestamp. + */ + unsigned char data[ALIGN(2, sizeof(s64)) + sizeof(s64)] __aligned(IIO_DMA_MINALIGN); +}; + +static void ad7091_convst(struct ad7476_state *st) +{ + if (!st->convst_gpio) + return; + + gpiod_set_value_cansleep(st->convst_gpio, 0); + udelay(1); /* CONVST pulse width: 10 ns min */ + gpiod_set_value_cansleep(st->convst_gpio, 1); + udelay(1); /* Conversion time: 650 ns max */ +} + +static void bd79105_convst_disable(struct ad7476_state *st) +{ + gpiod_set_value_cansleep(st->convst_gpio, 0); +} + +static void bd79105_convst_enable(struct ad7476_state *st) +{ + gpiod_set_value_cansleep(st->convst_gpio, 1); + /* Worst case, 2790 ns required for conversion */ + ndelay(2790); +} + +static irqreturn_t ad7476_trigger_handler(int irq, void *p) +{ + struct iio_poll_func *pf = p; + struct iio_dev *indio_dev = pf->indio_dev; + struct ad7476_state *st = iio_priv(indio_dev); + int b_sent; + + if (st->chip_info->conversion_pre_op) + st->chip_info->conversion_pre_op(st); + + b_sent = spi_sync(st->spi, &st->msg); + if (b_sent < 0) + goto done; + + iio_push_to_buffers_with_ts(indio_dev, st->data, sizeof(st->data), + iio_get_time_ns(indio_dev)); +done: + if (st->chip_info->conversion_post_op) + st->chip_info->conversion_post_op(st); + iio_trigger_notify_done(indio_dev->trig); + + return IRQ_HANDLED; +} + +static void ad7091_reset(struct ad7476_state *st) +{ + /* Any transfers with 8 scl cycles will reset the device */ + spi_read(st->spi, st->data, 1); +} + +static int ad7476_scan_direct(struct ad7476_state *st) +{ + int ret; + + if (st->chip_info->conversion_pre_op) + st->chip_info->conversion_pre_op(st); + + ret = spi_sync(st->spi, &st->msg); + if (ret) + return ret; + + if (st->chip_info->conversion_post_op) + st->chip_info->conversion_post_op(st); + + return be16_to_cpup((__be16 *)st->data); +} + +static int ad7476_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, + int *val2, + long m) +{ + int ret; + struct ad7476_state *st = iio_priv(indio_dev); + + switch (m) { + case IIO_CHAN_INFO_RAW: + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; + ret = ad7476_scan_direct(st); + iio_device_release_direct(indio_dev); + + if (ret < 0) + return ret; + *val = (ret >> chan->scan_type.shift) & + GENMASK(chan->scan_type.realbits - 1, 0); + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + *val = st->scale_mv; + *val2 = chan->scan_type.realbits; + return IIO_VAL_FRACTIONAL_LOG2; + } + return -EINVAL; +} + +#define _AD7476_CHAN(bits, _shift, _info_mask_sep) \ + { \ + .type = IIO_VOLTAGE, \ + .indexed = 1, \ + .info_mask_separate = _info_mask_sep, \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ + .scan_type = { \ + .sign = 'u', \ + .realbits = (bits), \ + .storagebits = 16, \ + .shift = (_shift), \ + .endianness = IIO_BE, \ + }, \ +} + +#define ADC081S_CHAN(bits) _AD7476_CHAN((bits), 12 - (bits), \ + BIT(IIO_CHAN_INFO_RAW)) +#define AD7476_CHAN(bits) _AD7476_CHAN((bits), 13 - (bits), \ + BIT(IIO_CHAN_INFO_RAW)) +#define AD7940_CHAN(bits) _AD7476_CHAN((bits), 15 - (bits), \ + BIT(IIO_CHAN_INFO_RAW)) +#define AD7091R_CHAN(bits) _AD7476_CHAN((bits), 16 - (bits), 0) +#define ADS786X_CHAN(bits) _AD7476_CHAN((bits), 12 - (bits), \ + BIT(IIO_CHAN_INFO_RAW)) + +static const struct ad7476_chip_info ad7091_chip_info = { + .channel[0] = AD7091R_CHAN(12), + .channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1), + .conversion_pre_op = ad7091_convst, + .reset = ad7091_reset, +}; + +static const struct ad7476_chip_info ad7091r_chip_info = { + .channel[0] = AD7091R_CHAN(12), + .channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1), + .conversion_pre_op = ad7091_convst, + .int_vref_mv = 2500, + .has_vref = true, + .reset = ad7091_reset, +}; + +static const struct ad7476_chip_info ad7273_chip_info = { + .channel[0] = AD7940_CHAN(10), + .channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1), + .has_vref = true, +}; + +static const struct ad7476_chip_info ad7274_chip_info = { + .channel[0] = AD7940_CHAN(12), + .channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1), + .has_vref = true, +}; + +static const struct ad7476_chip_info ad7276_chip_info = { + .channel[0] = AD7940_CHAN(12), + .channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1), +}; + +static const struct ad7476_chip_info ad7277_chip_info = { + .channel[0] = AD7940_CHAN(10), + .channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1), +}; + +static const struct ad7476_chip_info ad7278_chip_info = { + .channel[0] = AD7940_CHAN(8), + .channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1), +}; + +static const struct ad7476_chip_info ad7466_chip_info = { + .channel[0] = AD7476_CHAN(12), + .channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1), +}; + +static const struct ad7476_chip_info ad7467_chip_info = { + .channel[0] = AD7476_CHAN(10), + .channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1), +}; + +static const struct ad7476_chip_info ad7468_chip_info = { + .channel[0] = AD7476_CHAN(8), + .channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1), +}; + +static const struct ad7476_chip_info ad7475_chip_info = { + .channel[0] = AD7476_CHAN(12), + .channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1), + .has_vref = true, + .has_vdrive = true, +}; + +static const struct ad7476_chip_info ad7495_chip_info = { + .channel[0] = AD7476_CHAN(12), + .channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1), + .int_vref_mv = 2500, + .has_vdrive = true, +}; + +static const struct ad7476_chip_info ad7940_chip_info = { + .channel[0] = AD7940_CHAN(14), + .channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1), +}; + +static const struct ad7476_chip_info adc081s_chip_info = { + .channel[0] = ADC081S_CHAN(8), + .channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1), +}; + +static const struct ad7476_chip_info adc101s_chip_info = { + .channel[0] = ADC081S_CHAN(10), + .channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1), +}; + +static const struct ad7476_chip_info adc121s_chip_info = { + .channel[0] = ADC081S_CHAN(12), + .channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1), +}; + +static const struct ad7476_chip_info ads7866_chip_info = { + .channel[0] = ADS786X_CHAN(12), + .channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1), +}; + +static const struct ad7476_chip_info ads7867_chip_info = { + .channel[0] = ADS786X_CHAN(10), + .channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1), +}; + +static const struct ad7476_chip_info ads7868_chip_info = { + .channel[0] = ADS786X_CHAN(8), + .channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1), +}; + +static const struct ad7476_chip_info ltc2314_14_chip_info = { + .channel[0] = AD7940_CHAN(14), + .channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1), + .has_vref = true, +}; + +static const struct ad7476_chip_info bd79105_chip_info = { + .channel[0] = AD7091R_CHAN(16), + .channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1), + /* + * The BD79105 starts ADC data conversion when the CONVSTART line is + * set HIGH. The CONVSTART must be kept HIGH until the data has been + * read from the ADC. + */ + .conversion_pre_op = bd79105_convst_enable, + .conversion_post_op = bd79105_convst_disable, + /* BD79105 won't do conversion without convstart */ + .convstart_required = true, + .has_vref = true, + .has_vdrive = true, +}; + +static const struct iio_info ad7476_info = { + .read_raw = &ad7476_read_raw, +}; + +static int ad7476_probe(struct spi_device *spi) +{ + struct ad7476_state *st; + struct iio_dev *indio_dev; + unsigned int i; + int ret; + + indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st)); + if (!indio_dev) + return -ENOMEM; + + st = iio_priv(indio_dev); + + st->chip_info = spi_get_device_match_data(spi); + if (!st->chip_info) + return -ENODEV; + + /* Use VCC for reference voltage if vref / internal vref aren't used */ + if (!st->chip_info->int_vref_mv && !st->chip_info->has_vref) { + ret = devm_regulator_get_enable_read_voltage(&spi->dev, "vcc"); + if (ret < 0) + return ret; + st->scale_mv = ret / 1000; + } else { + ret = devm_regulator_get_enable(&spi->dev, "vcc"); + if (ret < 0) + return ret; + } + + if (st->chip_info->has_vref) { + ret = devm_regulator_get_enable_read_voltage(&spi->dev, "vref"); + if (ret < 0) { + /* Vref is optional if a device has an internal reference */ + if (!st->chip_info->int_vref_mv || ret != -ENODEV) + return ret; + } else { + st->scale_mv = ret / 1000; + } + } + + if (!st->scale_mv) + st->scale_mv = st->chip_info->int_vref_mv; + + if (st->chip_info->has_vdrive) { + ret = devm_regulator_get_enable(&spi->dev, "vdrive"); + if (ret) + return ret; + } + + st->convst_gpio = devm_gpiod_get_optional(&spi->dev, + "adi,conversion-start", + GPIOD_OUT_LOW); + if (IS_ERR(st->convst_gpio)) + return PTR_ERR(st->convst_gpio); + + if (st->chip_info->convstart_required && !st->convst_gpio) + return dev_err_probe(&spi->dev, -EINVAL, "No convstart GPIO\n"); + + /* + * This will never happen. Unless someone changes the channel specs + * in this driver. And if someone does, without changing the loop + * below, then we'd better immediately produce a big fat error, before + * the change proceeds from that developer's table. + */ + static_assert(ARRAY_SIZE(st->channel) == ARRAY_SIZE(st->chip_info->channel)); + for (i = 0; i < ARRAY_SIZE(st->channel); i++) { + st->channel[i] = st->chip_info->channel[i]; + if (st->convst_gpio) + __set_bit(IIO_CHAN_INFO_RAW, + &st->channel[i].info_mask_separate); + } + + st->spi = spi; + + indio_dev->name = spi_get_device_id(spi)->name; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = st->channel; + indio_dev->num_channels = ARRAY_SIZE(st->channel); + indio_dev->info = &ad7476_info; + + /* Setup default message */ + + st->xfer.rx_buf = &st->data; + st->xfer.len = indio_dev->channels[0].scan_type.storagebits / 8; + + spi_message_init(&st->msg); + spi_message_add_tail(&st->xfer, &st->msg); + + ret = devm_iio_triggered_buffer_setup(&spi->dev, indio_dev, NULL, + &ad7476_trigger_handler, NULL); + if (ret) + return ret; + + if (st->chip_info->reset) + st->chip_info->reset(st); + + return devm_iio_device_register(&spi->dev, indio_dev); +} + +static const struct spi_device_id ad7476_id[] = { + { .name = "ad7091", .driver_data = (kernel_ulong_t)&ad7091_chip_info }, + { .name = "ad7091r", .driver_data = (kernel_ulong_t)&ad7091r_chip_info }, + { .name = "ad7273", .driver_data = (kernel_ulong_t)&ad7273_chip_info }, + { .name = "ad7274", .driver_data = (kernel_ulong_t)&ad7274_chip_info }, + { .name = "ad7276", .driver_data = (kernel_ulong_t)&ad7276_chip_info }, + { .name = "ad7277", .driver_data = (kernel_ulong_t)&ad7277_chip_info }, + { .name = "ad7278", .driver_data = (kernel_ulong_t)&ad7278_chip_info }, + { .name = "ad7466", .driver_data = (kernel_ulong_t)&ad7466_chip_info }, + { .name = "ad7467", .driver_data = (kernel_ulong_t)&ad7467_chip_info }, + { .name = "ad7468", .driver_data = (kernel_ulong_t)&ad7468_chip_info }, + { .name = "ad7475", .driver_data = (kernel_ulong_t)&ad7475_chip_info }, + { .name = "ad7476", .driver_data = (kernel_ulong_t)&ad7466_chip_info }, + { .name = "ad7476a", .driver_data = (kernel_ulong_t)&ad7466_chip_info }, + { .name = "ad7477", .driver_data = (kernel_ulong_t)&ad7467_chip_info }, + { .name = "ad7477a", .driver_data = (kernel_ulong_t)&ad7467_chip_info }, + { .name = "ad7478", .driver_data = (kernel_ulong_t)&ad7468_chip_info }, + { .name = "ad7478a", .driver_data = (kernel_ulong_t)&ad7468_chip_info }, + { .name = "ad7495", .driver_data = (kernel_ulong_t)&ad7495_chip_info }, + { .name = "ad7910", .driver_data = (kernel_ulong_t)&ad7467_chip_info }, + { .name = "ad7920", .driver_data = (kernel_ulong_t)&ad7466_chip_info }, + { .name = "ad7940", .driver_data = (kernel_ulong_t)&ad7940_chip_info }, + { .name = "adc081s", .driver_data = (kernel_ulong_t)&adc081s_chip_info }, + { .name = "adc101s", .driver_data = (kernel_ulong_t)&adc101s_chip_info }, + { .name = "adc121s", .driver_data = (kernel_ulong_t)&adc121s_chip_info }, + { .name = "ads7866", .driver_data = (kernel_ulong_t)&ads7866_chip_info }, + { .name = "ads7867", .driver_data = (kernel_ulong_t)&ads7867_chip_info }, + { .name = "ads7868", .driver_data = (kernel_ulong_t)&ads7868_chip_info }, + { .name = "bd79105", .driver_data = (kernel_ulong_t)&bd79105_chip_info }, + /* + * The ROHM BU79100G is identical to the TI's ADS7866 from the software + * point of view. The binding document mandates the ADS7866 to be + * marked as a fallback for the BU79100G, but we still need the SPI ID + * here to make the module loading work. + */ + { .name = "bu79100g", .driver_data = (kernel_ulong_t)&ads7866_chip_info }, + { .name = "ltc2314-14", .driver_data = (kernel_ulong_t)<c2314_14_chip_info }, + { } +}; +MODULE_DEVICE_TABLE(spi, ad7476_id); + +static struct spi_driver ad7476_driver = { + .driver = { + .name = "ad7476", + }, + .probe = ad7476_probe, + .id_table = ad7476_id, +}; +module_spi_driver(ad7476_driver); + +MODULE_AUTHOR("Michael Hennerich "); +MODULE_DESCRIPTION("Analog Devices AD7476 and similar 1-channel ADCs"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/adc/ad7606.c b/drivers/iio/adc/ad7606.c new file mode 100644 index 000000000..cebb8ed8d --- /dev/null +++ b/drivers/iio/adc/ad7606.c @@ -0,0 +1,1712 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * AD7606 SPI ADC driver + * + * Copyright 2011 Analog Devices Inc. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "ad7606.h" + +#define AD7606_CALIB_GAIN_MIN 0 +#define AD7606_CALIB_GAIN_STEP 1024 +#define AD7606_CALIB_GAIN_MAX (63 * AD7606_CALIB_GAIN_STEP) + +/* + * Scales are computed as 5000/32768 and 10000/32768 respectively, + * so that when applied to the raw values they provide mV values. + * The scale arrays are kept as IIO_VAL_INT_PLUS_MICRO, so index + * X is the integer part and X + 1 is the fractional part. + */ +static const unsigned int ad7606_16bit_hw_scale_avail[2][2] = { + { 0, 152588 }, { 0, 305176 } +}; + +static const unsigned int ad7606_18bit_hw_scale_avail[2][2] = { + { 0, 38147 }, { 0, 76294 } +}; + +static const unsigned int ad7606c_16bit_single_ended_unipolar_scale_avail[3][2] = { + { 0, 76294 }, { 0, 152588 }, { 0, 190735 } +}; + +static const unsigned int ad7606c_16bit_single_ended_bipolar_scale_avail[5][2] = { + { 0, 76294 }, { 0, 152588 }, { 0, 190735 }, { 0, 305176 }, { 0, 381470 } +}; + +static const unsigned int ad7606c_16bit_differential_bipolar_scale_avail[4][2] = { + { 0, 152588 }, { 0, 305176 }, { 0, 381470 }, { 0, 610352 } +}; + +static const unsigned int ad7606c_18bit_single_ended_unipolar_scale_avail[3][2] = { + { 0, 19073 }, { 0, 38147 }, { 0, 47684 } +}; + +static const unsigned int ad7606c_18bit_single_ended_bipolar_scale_avail[5][2] = { + { 0, 19073 }, { 0, 38147 }, { 0, 47684 }, { 0, 76294 }, { 0, 95367 } +}; + +static const unsigned int ad7606c_18bit_differential_bipolar_scale_avail[4][2] = { + { 0, 38147 }, { 0, 76294 }, { 0, 95367 }, { 0, 152588 } +}; + +static const unsigned int ad7606_16bit_sw_scale_avail[3][2] = { + { 0, 76293 }, { 0, 152588 }, { 0, 305176 } +}; + +static const unsigned int ad7607_hw_scale_avail[2][2] = { + { 0, 610352 }, { 1, 220703 } +}; + +static const unsigned int ad7609_hw_scale_avail[2][2] = { + { 0, 152588 }, { 0, 305176 } +}; + +static const unsigned int ad7606_oversampling_avail[7] = { + 1, 2, 4, 8, 16, 32, 64, +}; + +static const unsigned int ad7606b_oversampling_avail[9] = { + 1, 2, 4, 8, 16, 32, 64, 128, 256, +}; + +static const unsigned int ad7616_oversampling_avail[8] = { + 1, 2, 4, 8, 16, 32, 64, 128, +}; + +static const int ad7606_calib_offset_avail[3] = { + -128, 1, 127, +}; + +static const int ad7606c_18bit_calib_offset_avail[3] = { + -512, 4, 508, +}; + +static const int ad7606b_calib_phase_avail[][2] = { + { 0, 0 }, { 0, 1250 }, { 0, 318750 }, +}; + +static const int ad7606c_calib_phase_avail[][2] = { + { 0, 0 }, { 0, 1000 }, { 0, 255000 }, +}; + +static int ad7606c_18bit_chan_scale_setup(struct iio_dev *indio_dev, + struct iio_chan_spec *chan); +static int ad7606c_16bit_chan_scale_setup(struct iio_dev *indio_dev, + struct iio_chan_spec *chan); +static int ad7606_16bit_chan_scale_setup(struct iio_dev *indio_dev, + struct iio_chan_spec *chan); +static int ad7607_chan_scale_setup(struct iio_dev *indio_dev, + struct iio_chan_spec *chan); +static int ad7608_chan_scale_setup(struct iio_dev *indio_dev, + struct iio_chan_spec *chan); +static int ad7609_chan_scale_setup(struct iio_dev *indio_dev, + struct iio_chan_spec *chan); +static int ad7616_sw_mode_setup(struct iio_dev *indio_dev); +static int ad7606b_sw_mode_setup(struct iio_dev *indio_dev); + +const struct ad7606_chip_info ad7605_4_info = { + .max_samplerate = 300 * KILO, + .name = "ad7605-4", + .bits = 16, + .num_adc_channels = 4, + .scale_setup_cb = ad7606_16bit_chan_scale_setup, +}; +EXPORT_SYMBOL_NS_GPL(ad7605_4_info, "IIO_AD7606"); + +const struct ad7606_chip_info ad7606_8_info = { + .max_samplerate = 200 * KILO, + .name = "ad7606-8", + .bits = 16, + .num_adc_channels = 8, + .oversampling_avail = ad7606_oversampling_avail, + .oversampling_num = ARRAY_SIZE(ad7606_oversampling_avail), + .scale_setup_cb = ad7606_16bit_chan_scale_setup, +}; +EXPORT_SYMBOL_NS_GPL(ad7606_8_info, "IIO_AD7606"); + +const struct ad7606_chip_info ad7606_6_info = { + .max_samplerate = 200 * KILO, + .name = "ad7606-6", + .bits = 16, + .num_adc_channels = 6, + .oversampling_avail = ad7606_oversampling_avail, + .oversampling_num = ARRAY_SIZE(ad7606_oversampling_avail), + .scale_setup_cb = ad7606_16bit_chan_scale_setup, + .offload_storagebits = 32, +}; +EXPORT_SYMBOL_NS_GPL(ad7606_6_info, "IIO_AD7606"); + +const struct ad7606_chip_info ad7606_4_info = { + .max_samplerate = 200 * KILO, + .name = "ad7606-4", + .bits = 16, + .num_adc_channels = 4, + .oversampling_avail = ad7606_oversampling_avail, + .oversampling_num = ARRAY_SIZE(ad7606_oversampling_avail), + .scale_setup_cb = ad7606_16bit_chan_scale_setup, + .offload_storagebits = 32, +}; +EXPORT_SYMBOL_NS_GPL(ad7606_4_info, "IIO_AD7606"); + +const struct ad7606_chip_info ad7606b_info = { + .max_samplerate = 800 * KILO, + .name = "ad7606b", + .bits = 16, + .num_adc_channels = 8, + .oversampling_avail = ad7606_oversampling_avail, + .oversampling_num = ARRAY_SIZE(ad7606_oversampling_avail), + .scale_setup_cb = ad7606_16bit_chan_scale_setup, + .sw_setup_cb = ad7606b_sw_mode_setup, + .offload_storagebits = 32, + .calib_gain_avail = true, + .calib_offset_avail = ad7606_calib_offset_avail, + .calib_phase_avail = ad7606b_calib_phase_avail, +}; +EXPORT_SYMBOL_NS_GPL(ad7606b_info, "IIO_AD7606"); + +const struct ad7606_chip_info ad7606c_16_info = { + .max_samplerate = 1 * MEGA, + .name = "ad7606c16", + .bits = 16, + .num_adc_channels = 8, + .oversampling_avail = ad7606_oversampling_avail, + .oversampling_num = ARRAY_SIZE(ad7606_oversampling_avail), + .scale_setup_cb = ad7606c_16bit_chan_scale_setup, + .sw_setup_cb = ad7606b_sw_mode_setup, + .offload_storagebits = 32, + .calib_gain_avail = true, + .calib_offset_avail = ad7606_calib_offset_avail, + .calib_phase_avail = ad7606c_calib_phase_avail, +}; +EXPORT_SYMBOL_NS_GPL(ad7606c_16_info, "IIO_AD7606"); + +const struct ad7606_chip_info ad7607_info = { + .max_samplerate = 200 * KILO, + .name = "ad7607", + .bits = 14, + .num_adc_channels = 8, + .oversampling_avail = ad7606_oversampling_avail, + .oversampling_num = ARRAY_SIZE(ad7606_oversampling_avail), + .scale_setup_cb = ad7607_chan_scale_setup, + .offload_storagebits = 32, +}; +EXPORT_SYMBOL_NS_GPL(ad7607_info, "IIO_AD7606"); + +const struct ad7606_chip_info ad7608_info = { + .max_samplerate = 200 * KILO, + .name = "ad7608", + .bits = 18, + .num_adc_channels = 8, + .oversampling_avail = ad7606_oversampling_avail, + .oversampling_num = ARRAY_SIZE(ad7606_oversampling_avail), + .scale_setup_cb = ad7608_chan_scale_setup, + .offload_storagebits = 32, +}; +EXPORT_SYMBOL_NS_GPL(ad7608_info, "IIO_AD7606"); + +const struct ad7606_chip_info ad7609_info = { + .max_samplerate = 200 * KILO, + .name = "ad7609", + .bits = 18, + .num_adc_channels = 8, + .oversampling_avail = ad7606_oversampling_avail, + .oversampling_num = ARRAY_SIZE(ad7606_oversampling_avail), + .scale_setup_cb = ad7609_chan_scale_setup, + .offload_storagebits = 32, +}; +EXPORT_SYMBOL_NS_GPL(ad7609_info, "IIO_AD7606"); + +const struct ad7606_chip_info ad7606c_18_info = { + .max_samplerate = 1 * MEGA, + .name = "ad7606c18", + .bits = 18, + .num_adc_channels = 8, + .oversampling_avail = ad7606_oversampling_avail, + .oversampling_num = ARRAY_SIZE(ad7606_oversampling_avail), + .scale_setup_cb = ad7606c_18bit_chan_scale_setup, + .sw_setup_cb = ad7606b_sw_mode_setup, + .offload_storagebits = 32, + .calib_gain_avail = true, + .calib_offset_avail = ad7606c_18bit_calib_offset_avail, + .calib_phase_avail = ad7606c_calib_phase_avail, +}; +EXPORT_SYMBOL_NS_GPL(ad7606c_18_info, "IIO_AD7606"); + +const struct ad7606_chip_info ad7616_info = { + .max_samplerate = 1 * MEGA, + .init_delay_ms = 15, + .name = "ad7616", + .bits = 16, + .num_adc_channels = 16, + .oversampling_avail = ad7616_oversampling_avail, + .oversampling_num = ARRAY_SIZE(ad7616_oversampling_avail), + .os_req_reset = true, + .scale_setup_cb = ad7606_16bit_chan_scale_setup, + .sw_setup_cb = ad7616_sw_mode_setup, + .offload_storagebits = 16, +}; +EXPORT_SYMBOL_NS_GPL(ad7616_info, "IIO_AD7606"); + +int ad7606_reset(struct ad7606_state *st) +{ + if (st->gpio_reset) { + gpiod_set_value(st->gpio_reset, 1); + ndelay(100); /* t_reset >= 100ns */ + gpiod_set_value(st->gpio_reset, 0); + return 0; + } + + return -ENODEV; +} +EXPORT_SYMBOL_NS_GPL(ad7606_reset, "IIO_AD7606"); + +static int ad7606_16bit_chan_scale_setup(struct iio_dev *indio_dev, + struct iio_chan_spec *chan) +{ + struct ad7606_state *st = iio_priv(indio_dev); + struct ad7606_chan_info *ci = &st->chan_info[chan->scan_index]; + + if (!st->sw_mode_en) { + /* tied to logic low, analog input range is +/- 5V */ + ci->range = 0; + ci->scale_avail = ad7606_16bit_hw_scale_avail; + ci->num_scales = ARRAY_SIZE(ad7606_16bit_hw_scale_avail); + return 0; + } + + /* Scale of 0.076293 is only available in sw mode */ + /* After reset, in software mode, ±10 V is set by default */ + ci->range = 2; + ci->scale_avail = ad7606_16bit_sw_scale_avail; + ci->num_scales = ARRAY_SIZE(ad7606_16bit_sw_scale_avail); + + return 0; +} + +static int ad7606_get_chan_config(struct iio_dev *indio_dev, int ch, + bool *bipolar, bool *differential) +{ + struct ad7606_state *st = iio_priv(indio_dev); + struct ad7606_chan_info *ci; + unsigned int num_channels = st->chip_info->num_adc_channels; + struct device *dev = st->dev; + int ret; + + *bipolar = false; + *differential = false; + + device_for_each_child_node_scoped(dev, child) { + u32 pins[2]; + int reg; + + ret = fwnode_property_read_u32(child, "reg", ®); + if (ret) + return ret; + + /* channel number (here) is from 1 to num_channels */ + if (reg < 1 || reg > num_channels) + return -EINVAL; + + /* Loop until we are in the right channel. */ + if (reg != (ch + 1)) + continue; + + *bipolar = fwnode_property_read_bool(child, "bipolar"); + + ret = fwnode_property_read_u32_array(child, "diff-channels", + pins, ARRAY_SIZE(pins)); + /* Channel is differential, if pins are the same as 'reg' */ + if (ret == 0 && (pins[0] != reg || pins[1] != reg)) { + dev_err(dev, + "Differential pins must be the same as 'reg'"); + return -EINVAL; + } + + *differential = (ret == 0); + + if (*differential && !*bipolar) { + dev_err(dev, + "'bipolar' must be added for diff channel %d\n", + reg); + return -EINVAL; + } + + ci = &st->chan_info[reg - 1]; + + ci->r_gain = 0; + ret = fwnode_property_read_u32(child, "adi,rfilter-ohms", + &ci->r_gain); + if (ret == 0 && ci->r_gain > AD7606_CALIB_GAIN_MAX) + return -EINVAL; + + return 0; + } + + return 0; +} + +static int ad7606c_18bit_chan_scale_setup(struct iio_dev *indio_dev, + struct iio_chan_spec *chan) +{ + struct ad7606_state *st = iio_priv(indio_dev); + struct ad7606_chan_info *ci = &st->chan_info[chan->scan_index]; + bool bipolar, differential; + int ret; + + if (!st->sw_mode_en) { + ci->range = 0; + ci->scale_avail = ad7606_18bit_hw_scale_avail; + ci->num_scales = ARRAY_SIZE(ad7606_18bit_hw_scale_avail); + return 0; + } + + ret = ad7606_get_chan_config(indio_dev, chan->scan_index, &bipolar, + &differential); + if (ret) + return ret; + + if (differential) { + ci->scale_avail = ad7606c_18bit_differential_bipolar_scale_avail; + ci->num_scales = + ARRAY_SIZE(ad7606c_18bit_differential_bipolar_scale_avail); + /* Bipolar differential ranges start at 8 (b1000) */ + ci->reg_offset = 8; + ci->range = 1; + chan->differential = 1; + chan->channel2 = chan->channel; + + return 0; + } + + chan->differential = 0; + + if (bipolar) { + ci->scale_avail = ad7606c_18bit_single_ended_bipolar_scale_avail; + ci->num_scales = + ARRAY_SIZE(ad7606c_18bit_single_ended_bipolar_scale_avail); + /* Bipolar single-ended ranges start at 0 (b0000) */ + ci->reg_offset = 0; + ci->range = 3; + chan->scan_type.sign = 's'; + + return 0; + } + + ci->scale_avail = ad7606c_18bit_single_ended_unipolar_scale_avail; + ci->num_scales = + ARRAY_SIZE(ad7606c_18bit_single_ended_unipolar_scale_avail); + /* Unipolar single-ended ranges start at 5 (b0101) */ + ci->reg_offset = 5; + ci->range = 1; + chan->scan_type.sign = 'u'; + + return 0; +} + +static int ad7606c_16bit_chan_scale_setup(struct iio_dev *indio_dev, + struct iio_chan_spec *chan) +{ + struct ad7606_state *st = iio_priv(indio_dev); + struct ad7606_chan_info *ci = &st->chan_info[chan->scan_index]; + bool bipolar, differential; + int ret; + + if (!st->sw_mode_en) { + ci->range = 0; + ci->scale_avail = ad7606_16bit_hw_scale_avail; + ci->num_scales = ARRAY_SIZE(ad7606_16bit_hw_scale_avail); + return 0; + } + + ret = ad7606_get_chan_config(indio_dev, chan->scan_index, &bipolar, + &differential); + if (ret) + return ret; + + if (differential) { + ci->scale_avail = ad7606c_16bit_differential_bipolar_scale_avail; + ci->num_scales = + ARRAY_SIZE(ad7606c_16bit_differential_bipolar_scale_avail); + /* Bipolar differential ranges start at 8 (b1000) */ + ci->reg_offset = 8; + ci->range = 1; + chan->differential = 1; + chan->channel2 = chan->channel; + chan->scan_type.sign = 's'; + + return 0; + } + + chan->differential = 0; + + if (bipolar) { + ci->scale_avail = ad7606c_16bit_single_ended_bipolar_scale_avail; + ci->num_scales = + ARRAY_SIZE(ad7606c_16bit_single_ended_bipolar_scale_avail); + /* Bipolar single-ended ranges start at 0 (b0000) */ + ci->reg_offset = 0; + ci->range = 3; + chan->scan_type.sign = 's'; + + return 0; + } + + ci->scale_avail = ad7606c_16bit_single_ended_unipolar_scale_avail; + ci->num_scales = + ARRAY_SIZE(ad7606c_16bit_single_ended_unipolar_scale_avail); + /* Unipolar single-ended ranges start at 5 (b0101) */ + ci->reg_offset = 5; + ci->range = 1; + chan->scan_type.sign = 'u'; + + return 0; +} + +static int ad7607_chan_scale_setup(struct iio_dev *indio_dev, + struct iio_chan_spec *chan) +{ + struct ad7606_state *st = iio_priv(indio_dev); + struct ad7606_chan_info *ci = &st->chan_info[chan->scan_index]; + + ci->range = 0; + ci->scale_avail = ad7607_hw_scale_avail; + ci->num_scales = ARRAY_SIZE(ad7607_hw_scale_avail); + return 0; +} + +static int ad7608_chan_scale_setup(struct iio_dev *indio_dev, + struct iio_chan_spec *chan) +{ + struct ad7606_state *st = iio_priv(indio_dev); + struct ad7606_chan_info *ci = &st->chan_info[chan->scan_index]; + + ci->range = 0; + ci->scale_avail = ad7606_18bit_hw_scale_avail; + ci->num_scales = ARRAY_SIZE(ad7606_18bit_hw_scale_avail); + return 0; +} + +static int ad7609_chan_scale_setup(struct iio_dev *indio_dev, + struct iio_chan_spec *chan) +{ + struct ad7606_state *st = iio_priv(indio_dev); + struct ad7606_chan_info *ci = &st->chan_info[chan->scan_index]; + + ci->range = 0; + ci->scale_avail = ad7609_hw_scale_avail; + ci->num_scales = ARRAY_SIZE(ad7609_hw_scale_avail); + return 0; +} + +static int ad7606_reg_access(struct iio_dev *indio_dev, + unsigned int reg, + unsigned int writeval, + unsigned int *readval) +{ + struct ad7606_state *st = iio_priv(indio_dev); + int ret; + + guard(mutex)(&st->lock); + + if (readval) { + ret = st->bops->reg_read(st, reg); + if (ret < 0) + return ret; + *readval = ret; + return 0; + } else { + return st->bops->reg_write(st, reg, writeval); + } +} + +static int ad7606_pwm_set_high(struct ad7606_state *st) +{ + struct pwm_state cnvst_pwm_state; + int ret; + + pwm_get_state(st->cnvst_pwm, &cnvst_pwm_state); + cnvst_pwm_state.enabled = true; + cnvst_pwm_state.duty_cycle = cnvst_pwm_state.period; + + ret = pwm_apply_might_sleep(st->cnvst_pwm, &cnvst_pwm_state); + + return ret; +} + +int ad7606_pwm_set_low(struct ad7606_state *st) +{ + struct pwm_state cnvst_pwm_state; + int ret; + + pwm_get_state(st->cnvst_pwm, &cnvst_pwm_state); + cnvst_pwm_state.enabled = true; + cnvst_pwm_state.duty_cycle = 0; + + ret = pwm_apply_might_sleep(st->cnvst_pwm, &cnvst_pwm_state); + + return ret; +} +EXPORT_SYMBOL_NS_GPL(ad7606_pwm_set_low, "IIO_AD7606"); + +int ad7606_pwm_set_swing(struct ad7606_state *st) +{ + struct pwm_state cnvst_pwm_state; + + pwm_get_state(st->cnvst_pwm, &cnvst_pwm_state); + cnvst_pwm_state.enabled = true; + cnvst_pwm_state.duty_cycle = cnvst_pwm_state.period / 2; + + return pwm_apply_might_sleep(st->cnvst_pwm, &cnvst_pwm_state); +} +EXPORT_SYMBOL_NS_GPL(ad7606_pwm_set_swing, "IIO_AD7606"); + +static bool ad7606_pwm_is_swinging(struct ad7606_state *st) +{ + struct pwm_state cnvst_pwm_state; + + pwm_get_state(st->cnvst_pwm, &cnvst_pwm_state); + + return cnvst_pwm_state.duty_cycle != cnvst_pwm_state.period && + cnvst_pwm_state.duty_cycle != 0; +} + +static int ad7606_set_sampling_freq(struct ad7606_state *st, unsigned long freq) +{ + struct pwm_state cnvst_pwm_state; + bool is_swinging = ad7606_pwm_is_swinging(st); + bool is_high; + + if (freq == 0) + return -EINVAL; + + /* Retrieve the previous state. */ + pwm_get_state(st->cnvst_pwm, &cnvst_pwm_state); + is_high = cnvst_pwm_state.duty_cycle == cnvst_pwm_state.period; + + cnvst_pwm_state.period = DIV_ROUND_UP_ULL(NSEC_PER_SEC, freq); + cnvst_pwm_state.polarity = PWM_POLARITY_NORMAL; + if (is_high) + cnvst_pwm_state.duty_cycle = cnvst_pwm_state.period; + else if (is_swinging) + cnvst_pwm_state.duty_cycle = cnvst_pwm_state.period / 2; + else + cnvst_pwm_state.duty_cycle = 0; + + return pwm_apply_might_sleep(st->cnvst_pwm, &cnvst_pwm_state); +} + +static int ad7606_read_samples(struct ad7606_state *st) +{ + unsigned int num = st->chip_info->num_adc_channels; + + return st->bops->read_block(st->dev, num, &st->data); +} + +static irqreturn_t ad7606_trigger_handler(int irq, void *p) +{ + struct iio_poll_func *pf = p; + struct iio_dev *indio_dev = pf->indio_dev; + struct ad7606_state *st = iio_priv(indio_dev); + int ret; + + guard(mutex)(&st->lock); + + ret = ad7606_read_samples(st); + if (ret) + goto error_ret; + + iio_push_to_buffers_with_ts(indio_dev, &st->data, sizeof(st->data), + iio_get_time_ns(indio_dev)); +error_ret: + iio_trigger_notify_done(indio_dev->trig); + /* The rising edge of the CONVST signal starts a new conversion. */ + gpiod_set_value(st->gpio_convst, 1); + + return IRQ_HANDLED; +} + +static int ad7606_scan_direct(struct iio_dev *indio_dev, unsigned int ch, + int *val) +{ + struct ad7606_state *st = iio_priv(indio_dev); + const struct iio_chan_spec *chan; + unsigned int realbits; + int ret; + + if (st->gpio_convst) { + gpiod_set_value(st->gpio_convst, 1); + } else { + ret = ad7606_pwm_set_high(st); + if (ret < 0) + return ret; + } + + /* + * If no backend, wait for the interruption on busy pin, otherwise just add + * a delay to leave time for the data to be available. For now, the latter + * will not happen because IIO_CHAN_INFO_RAW is not supported for the backend. + * TODO: Add support for reading a single value when the backend is used. + */ + if (st->trig) { + ret = wait_for_completion_timeout(&st->completion, + msecs_to_jiffies(1000)); + if (!ret) { + ret = -ETIMEDOUT; + goto error_ret; + } + } else { + /* + * If the BUSY interrupt is not available, wait enough time for + * the longest possible conversion (max for the whole family is + * around 350us). + */ + fsleep(400); + } + + ret = ad7606_read_samples(st); + if (ret) + goto error_ret; + + chan = &indio_dev->channels[ch]; + realbits = chan->scan_type.realbits; + + if (realbits > 16) + *val = st->data.buf32[ch]; + else + *val = st->data.buf16[ch]; + + *val &= GENMASK(realbits - 1, 0); + + if (chan->scan_type.sign == 's') + *val = sign_extend32(*val, realbits - 1); + +error_ret: + if (!st->gpio_convst) { + ret = ad7606_pwm_set_low(st); + if (ret < 0) + return ret; + } + gpiod_set_value(st->gpio_convst, 0); + + return ret; +} + +static int ad7606_get_calib_offset(struct ad7606_state *st, int ch, int *val) +{ + int ret; + + ret = st->bops->reg_read(st, AD7606_CALIB_OFFSET(ch)); + if (ret < 0) + return ret; + + *val = st->chip_info->calib_offset_avail[0] + + ret * st->chip_info->calib_offset_avail[1]; + + return 0; +} + +static int ad7606_get_calib_phase(struct ad7606_state *st, int ch, int *val, + int *val2) +{ + int ret; + + ret = st->bops->reg_read(st, AD7606_CALIB_PHASE(ch)); + if (ret < 0) + return ret; + + *val = 0; + + /* + * ad7606b: phase delay from 0 to 318.75 μs in steps of 1.25 μs. + * ad7606c-16/18: phase delay from 0 µs to 255 µs in steps of 1 µs. + */ + *val2 = ret * st->chip_info->calib_phase_avail[1][1]; + + return 0; +} + +static int ad7606_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, + int *val2, + long m) +{ + int ret, ch = 0; + struct ad7606_state *st = iio_priv(indio_dev); + struct ad7606_chan_info *ci; + struct pwm_state cnvst_pwm_state; + + switch (m) { + case IIO_CHAN_INFO_RAW: + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; + ret = ad7606_scan_direct(indio_dev, chan->scan_index, val); + iio_device_release_direct(indio_dev); + if (ret < 0) + return ret; + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + if (st->sw_mode_en) + ch = chan->scan_index; + ci = &st->chan_info[ch]; + *val = ci->scale_avail[ci->range][0]; + *val2 = ci->scale_avail[ci->range][1]; + return IIO_VAL_INT_PLUS_MICRO; + case IIO_CHAN_INFO_OVERSAMPLING_RATIO: + *val = st->oversampling; + return IIO_VAL_INT; + case IIO_CHAN_INFO_SAMP_FREQ: + pwm_get_state(st->cnvst_pwm, &cnvst_pwm_state); + *val = DIV_ROUND_CLOSEST_ULL(NSEC_PER_SEC, cnvst_pwm_state.period); + return IIO_VAL_INT; + case IIO_CHAN_INFO_CALIBBIAS: + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; + ret = ad7606_get_calib_offset(st, chan->scan_index, val); + iio_device_release_direct(indio_dev); + if (ret) + return ret; + return IIO_VAL_INT; + case IIO_CHAN_INFO_CONVDELAY: + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; + ret = ad7606_get_calib_phase(st, chan->scan_index, val, val2); + iio_device_release_direct(indio_dev); + if (ret) + return ret; + return IIO_VAL_INT_PLUS_NANO; + } + return -EINVAL; +} + +static ssize_t in_voltage_scale_available_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct iio_dev *indio_dev = dev_to_iio_dev(dev); + struct ad7606_state *st = iio_priv(indio_dev); + struct ad7606_chan_info *ci = &st->chan_info[0]; + const unsigned int (*vals)[2] = ci->scale_avail; + unsigned int i; + size_t len = 0; + + for (i = 0; i < ci->num_scales; i++) + len += scnprintf(buf + len, PAGE_SIZE - len, "%u.%06u ", + vals[i][0], vals[i][1]); + buf[len - 1] = '\n'; + + return len; +} + +static IIO_DEVICE_ATTR_RO(in_voltage_scale_available, 0); + +static int ad7606_write_scale_hw(struct iio_dev *indio_dev, int ch, int val) +{ + struct ad7606_state *st = iio_priv(indio_dev); + + gpiod_set_value(st->gpio_range, val); + + return 0; +} + +static int ad7606_write_os_hw(struct iio_dev *indio_dev, int val) +{ + struct ad7606_state *st = iio_priv(indio_dev); + DECLARE_BITMAP(values, 3); + + values[0] = val & GENMASK(2, 0); + + gpiod_multi_set_value_cansleep(st->gpio_os, values); + + /* AD7616 requires a reset to update value */ + if (st->chip_info->os_req_reset) + ad7606_reset(st); + + return 0; +} + +static int ad7606_set_calib_offset(struct ad7606_state *st, int ch, int val) +{ + int start_val, step_val, stop_val; + int offset; + + start_val = st->chip_info->calib_offset_avail[0]; + step_val = st->chip_info->calib_offset_avail[1]; + stop_val = st->chip_info->calib_offset_avail[2]; + + if (val < start_val || val > stop_val) + return -EINVAL; + + offset = (val - start_val) / step_val; + + return st->bops->reg_write(st, AD7606_CALIB_OFFSET(ch), offset); +} + +static int ad7606_set_calib_phase(struct ad7606_state *st, int ch, int val, + int val2) +{ + int wreg, start_ns, step_ns, stop_ns; + + if (val != 0) + return -EINVAL; + + start_ns = st->chip_info->calib_phase_avail[0][1]; + step_ns = st->chip_info->calib_phase_avail[1][1]; + stop_ns = st->chip_info->calib_phase_avail[2][1]; + + /* + * ad7606b: phase delay from 0 to 318.75 μs in steps of 1.25 μs. + * ad7606c-16/18: phase delay from 0 µs to 255 µs in steps of 1 µs. + */ + if (val2 < start_ns || val2 > stop_ns) + return -EINVAL; + + wreg = val2 / step_ns; + + return st->bops->reg_write(st, AD7606_CALIB_PHASE(ch), wreg); +} + +static int ad7606_write_raw_get_fmt(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, long info) +{ + switch (info) { + case IIO_CHAN_INFO_SCALE: + return IIO_VAL_INT_PLUS_MICRO; + case IIO_CHAN_INFO_SAMP_FREQ: + case IIO_CHAN_INFO_OVERSAMPLING_RATIO: + case IIO_CHAN_INFO_CALIBBIAS: + return IIO_VAL_INT; + case IIO_CHAN_INFO_CONVDELAY: + return IIO_VAL_INT_PLUS_NANO; + default: + return -EINVAL; + } +} + +static int ad7606_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, + int val2, + long mask) +{ + struct ad7606_state *st = iio_priv(indio_dev); + unsigned int scale_avail_uv[AD760X_MAX_SCALES]; + struct ad7606_chan_info *ci; + int i, ret, ch = 0; + + guard(mutex)(&st->lock); + + switch (mask) { + case IIO_CHAN_INFO_SCALE: + if (st->sw_mode_en) + ch = chan->scan_index; + ci = &st->chan_info[ch]; + for (i = 0; i < ci->num_scales; i++) { + scale_avail_uv[i] = ci->scale_avail[i][0] * MICRO + + ci->scale_avail[i][1]; + } + val = (val * MICRO) + val2; + i = find_closest(val, scale_avail_uv, ci->num_scales); + + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; + ret = st->write_scale(indio_dev, ch, i + ci->reg_offset); + iio_device_release_direct(indio_dev); + if (ret < 0) + return ret; + ci->range = i; + + return 0; + case IIO_CHAN_INFO_OVERSAMPLING_RATIO: + if (val2) + return -EINVAL; + i = find_closest(val, st->oversampling_avail, + st->num_os_ratios); + + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; + ret = st->write_os(indio_dev, i); + iio_device_release_direct(indio_dev); + if (ret < 0) + return ret; + st->oversampling = st->oversampling_avail[i]; + + return 0; + case IIO_CHAN_INFO_SAMP_FREQ: + if (val < 0 && val2 != 0) + return -EINVAL; + return ad7606_set_sampling_freq(st, val); + case IIO_CHAN_INFO_CALIBBIAS: + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; + ret = ad7606_set_calib_offset(st, chan->scan_index, val); + iio_device_release_direct(indio_dev); + return ret; + case IIO_CHAN_INFO_CONVDELAY: + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; + ret = ad7606_set_calib_phase(st, chan->scan_index, val, val2); + iio_device_release_direct(indio_dev); + return ret; + default: + return -EINVAL; + } +} + +static ssize_t ad7606_oversampling_ratio_avail(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct iio_dev *indio_dev = dev_to_iio_dev(dev); + struct ad7606_state *st = iio_priv(indio_dev); + const unsigned int *vals = st->oversampling_avail; + unsigned int i; + size_t len = 0; + + for (i = 0; i < st->num_os_ratios; i++) + len += scnprintf(buf + len, PAGE_SIZE - len, "%u ", vals[i]); + buf[len - 1] = '\n'; + + return len; +} + +static IIO_DEVICE_ATTR(oversampling_ratio_available, 0444, + ad7606_oversampling_ratio_avail, NULL, 0); + +static struct attribute *ad7606_attributes_os_and_range[] = { + &iio_dev_attr_in_voltage_scale_available.dev_attr.attr, + &iio_dev_attr_oversampling_ratio_available.dev_attr.attr, + NULL, +}; + +static const struct attribute_group ad7606_attribute_group_os_and_range = { + .attrs = ad7606_attributes_os_and_range, +}; + +static struct attribute *ad7606_attributes_os[] = { + &iio_dev_attr_oversampling_ratio_available.dev_attr.attr, + NULL, +}; + +static const struct attribute_group ad7606_attribute_group_os = { + .attrs = ad7606_attributes_os, +}; + +static struct attribute *ad7606_attributes_range[] = { + &iio_dev_attr_in_voltage_scale_available.dev_attr.attr, + NULL, +}; + +static const struct attribute_group ad7606_attribute_group_range = { + .attrs = ad7606_attributes_range, +}; + +static int ad7606_request_gpios(struct ad7606_state *st) +{ + struct device *dev = st->dev; + + st->gpio_convst = devm_gpiod_get_optional(dev, "adi,conversion-start", + GPIOD_OUT_LOW); + + if (IS_ERR(st->gpio_convst)) + return PTR_ERR(st->gpio_convst); + + st->gpio_reset = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW); + if (IS_ERR(st->gpio_reset)) + return PTR_ERR(st->gpio_reset); + + st->gpio_range = devm_gpiod_get_optional(dev, "adi,range", + GPIOD_OUT_LOW); + if (IS_ERR(st->gpio_range)) + return PTR_ERR(st->gpio_range); + + st->gpio_standby = devm_gpiod_get_optional(dev, "standby", + GPIOD_OUT_LOW); + if (IS_ERR(st->gpio_standby)) + return PTR_ERR(st->gpio_standby); + + st->gpio_frstdata = devm_gpiod_get_optional(dev, "adi,first-data", + GPIOD_IN); + if (IS_ERR(st->gpio_frstdata)) + return PTR_ERR(st->gpio_frstdata); + + if (!st->chip_info->oversampling_num) + return 0; + + st->gpio_os = devm_gpiod_get_array_optional(dev, + "adi,oversampling-ratio", + GPIOD_OUT_LOW); + return PTR_ERR_OR_ZERO(st->gpio_os); +} + +/* + * The BUSY signal indicates when conversions are in progress, so when a rising + * edge of CONVST is applied, BUSY goes logic high and transitions low at the + * end of the entire conversion process. The falling edge of the BUSY signal + * triggers this interrupt. + */ +static irqreturn_t ad7606_interrupt(int irq, void *dev_id) +{ + struct iio_dev *indio_dev = dev_id; + struct ad7606_state *st = iio_priv(indio_dev); + int ret; + + if (iio_buffer_enabled(indio_dev)) { + if (st->gpio_convst) { + gpiod_set_value(st->gpio_convst, 0); + } else { + ret = ad7606_pwm_set_low(st); + if (ret < 0) { + dev_err(st->dev, "PWM set low failed"); + goto done; + } + } + iio_trigger_poll_nested(st->trig); + } else { + complete(&st->completion); + } + +done: + return IRQ_HANDLED; +}; + +static int ad7606_validate_trigger(struct iio_dev *indio_dev, + struct iio_trigger *trig) +{ + struct ad7606_state *st = iio_priv(indio_dev); + + if (st->trig != trig) + return -EINVAL; + + return 0; +} + +static int ad7606_buffer_postenable(struct iio_dev *indio_dev) +{ + struct ad7606_state *st = iio_priv(indio_dev); + + gpiod_set_value(st->gpio_convst, 1); + + return 0; +} + +static int ad7606_buffer_predisable(struct iio_dev *indio_dev) +{ + struct ad7606_state *st = iio_priv(indio_dev); + + gpiod_set_value(st->gpio_convst, 0); + + return 0; +} + +static int ad7606_read_avail(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + const int **vals, int *type, int *length, + long info) +{ + struct ad7606_state *st = iio_priv(indio_dev); + struct ad7606_chan_info *ci; + unsigned int ch = 0; + + switch (info) { + case IIO_CHAN_INFO_OVERSAMPLING_RATIO: + *vals = st->oversampling_avail; + *length = st->num_os_ratios; + *type = IIO_VAL_INT; + + return IIO_AVAIL_LIST; + + case IIO_CHAN_INFO_SCALE: + if (st->sw_mode_en) + ch = chan->scan_index; + + ci = &st->chan_info[ch]; + *vals = (int *)ci->scale_avail; + *length = ci->num_scales * 2; + *type = IIO_VAL_INT_PLUS_MICRO; + + return IIO_AVAIL_LIST; + case IIO_CHAN_INFO_CALIBBIAS: + *vals = st->chip_info->calib_offset_avail; + *type = IIO_VAL_INT; + return IIO_AVAIL_RANGE; + case IIO_CHAN_INFO_CONVDELAY: + *vals = (const int *)st->chip_info->calib_phase_avail; + *type = IIO_VAL_INT_PLUS_NANO; + return IIO_AVAIL_RANGE; + } + return -EINVAL; +} + +static int ad7606_backend_buffer_postenable(struct iio_dev *indio_dev) +{ + struct ad7606_state *st = iio_priv(indio_dev); + + return ad7606_pwm_set_swing(st); +} + +static int ad7606_backend_buffer_predisable(struct iio_dev *indio_dev) +{ + struct ad7606_state *st = iio_priv(indio_dev); + + return ad7606_pwm_set_low(st); +} + +static int ad7606_update_scan_mode(struct iio_dev *indio_dev, + const unsigned long *scan_mask) +{ + struct ad7606_state *st = iio_priv(indio_dev); + + /* + * The update scan mode is only for iio backend compatible drivers. + * If the specific update_scan_mode is not defined in the bus ops, + * just do nothing and return 0. + */ + if (!st->bops->update_scan_mode) + return 0; + + return st->bops->update_scan_mode(indio_dev, scan_mask); +} + +static const struct iio_buffer_setup_ops ad7606_buffer_ops = { + .postenable = &ad7606_buffer_postenable, + .predisable = &ad7606_buffer_predisable, +}; + +static const struct iio_buffer_setup_ops ad7606_backend_buffer_ops = { + .postenable = &ad7606_backend_buffer_postenable, + .predisable = &ad7606_backend_buffer_predisable, +}; + +static const struct iio_info ad7606_info_no_os_or_range = { + .read_raw = &ad7606_read_raw, + .validate_trigger = &ad7606_validate_trigger, + .update_scan_mode = &ad7606_update_scan_mode, +}; + +static const struct iio_info ad7606_info_os_and_range = { + .read_raw = &ad7606_read_raw, + .write_raw = &ad7606_write_raw, + .attrs = &ad7606_attribute_group_os_and_range, + .validate_trigger = &ad7606_validate_trigger, + .update_scan_mode = &ad7606_update_scan_mode, +}; + +static const struct iio_info ad7606_info_sw_mode = { + .read_raw = &ad7606_read_raw, + .write_raw = &ad7606_write_raw, + .read_avail = &ad7606_read_avail, + .write_raw_get_fmt = ad7606_write_raw_get_fmt, + .debugfs_reg_access = &ad7606_reg_access, + .validate_trigger = &ad7606_validate_trigger, + .update_scan_mode = &ad7606_update_scan_mode, +}; + +static const struct iio_info ad7606_info_os = { + .read_raw = &ad7606_read_raw, + .write_raw = &ad7606_write_raw, + .attrs = &ad7606_attribute_group_os, + .validate_trigger = &ad7606_validate_trigger, + .update_scan_mode = &ad7606_update_scan_mode, +}; + +static const struct iio_info ad7606_info_range = { + .read_raw = &ad7606_read_raw, + .write_raw = &ad7606_write_raw, + .attrs = &ad7606_attribute_group_range, + .validate_trigger = &ad7606_validate_trigger, + .update_scan_mode = &ad7606_update_scan_mode, +}; + +static const struct iio_trigger_ops ad7606_trigger_ops = { + .validate_device = iio_trigger_validate_own_device, +}; + +static int ad7606_write_mask(struct ad7606_state *st, unsigned int addr, + unsigned long mask, unsigned int val) +{ + int readval; + + readval = st->bops->reg_read(st, addr); + if (readval < 0) + return readval; + + readval &= ~mask; + readval |= val; + + return st->bops->reg_write(st, addr, readval); +} + +static int ad7616_write_scale_sw(struct iio_dev *indio_dev, int ch, int val) +{ + struct ad7606_state *st = iio_priv(indio_dev); + unsigned int ch_addr, mode, ch_index; + + /* + * Ad7616 has 16 channels divided in group A and group B. + * The range of channels from A are stored in registers with address 4 + * while channels from B are stored in register with address 6. + * The last bit from channels determines if it is from group A or B + * because the order of channels in iio is 0A, 0B, 1A, 1B... + */ + ch_index = ch >> 1; + + ch_addr = AD7616_RANGE_CH_ADDR(ch_index); + + if ((ch & 0x1) == 0) /* channel A */ + ch_addr += AD7616_RANGE_CH_A_ADDR_OFF; + else /* channel B */ + ch_addr += AD7616_RANGE_CH_B_ADDR_OFF; + + /* 0b01 for 2.5v, 0b10 for 5v and 0b11 for 10v */ + mode = AD7616_RANGE_CH_MODE(ch_index, ((val + 1) & 0b11)); + + return ad7606_write_mask(st, ch_addr, AD7616_RANGE_CH_MSK(ch_index), + mode); +} + +static int ad7616_write_os_sw(struct iio_dev *indio_dev, int val) +{ + struct ad7606_state *st = iio_priv(indio_dev); + + return ad7606_write_mask(st, AD7616_CONFIGURATION_REGISTER, + AD7616_OS_MASK, val << 2); +} + +static int ad7606_write_scale_sw(struct iio_dev *indio_dev, int ch, int val) +{ + struct ad7606_state *st = iio_priv(indio_dev); + + return ad7606_write_mask(st, AD7606_RANGE_CH_ADDR(ch), + AD7606_RANGE_CH_MSK(ch), + AD7606_RANGE_CH_MODE(ch, val)); +} + +static int ad7606_write_os_sw(struct iio_dev *indio_dev, int val) +{ + struct ad7606_state *st = iio_priv(indio_dev); + + return st->bops->reg_write(st, AD7606_OS_MODE, val); +} + +static int ad7616_sw_mode_setup(struct iio_dev *indio_dev) +{ + struct ad7606_state *st = iio_priv(indio_dev); + int ret; + + /* + * Scale can be configured individually for each channel + * in software mode. + */ + + st->write_scale = ad7616_write_scale_sw; + st->write_os = &ad7616_write_os_sw; + + if (st->bops->sw_mode_config) { + ret = st->bops->sw_mode_config(indio_dev); + if (ret) + return ret; + } + + /* Activate Burst mode and SEQEN MODE */ + return ad7606_write_mask(st, AD7616_CONFIGURATION_REGISTER, + AD7616_BURST_MODE | AD7616_SEQEN_MODE, + AD7616_BURST_MODE | AD7616_SEQEN_MODE); +} + +static int ad7606b_sw_mode_setup(struct iio_dev *indio_dev) +{ + struct ad7606_state *st = iio_priv(indio_dev); + DECLARE_BITMAP(os, 3); + + bitmap_fill(os, 3); + /* + * Software mode is enabled when all three oversampling + * pins are set to high. If oversampling gpios are defined + * in the device tree, then they need to be set to high, + * otherwise, they must be hardwired to VDD + */ + if (st->gpio_os) + gpiod_multi_set_value_cansleep(st->gpio_os, os); + + /* OS of 128 and 256 are available only in software mode */ + st->oversampling_avail = ad7606b_oversampling_avail; + st->num_os_ratios = ARRAY_SIZE(ad7606b_oversampling_avail); + + st->write_scale = ad7606_write_scale_sw; + st->write_os = &ad7606_write_os_sw; + + if (!st->bops->sw_mode_config) + return 0; + + return st->bops->sw_mode_config(indio_dev); +} + +static int ad7606_set_gain_calib(struct ad7606_state *st) +{ + struct ad7606_chan_info *ci; + int i, ret; + + for (i = 0; i < st->chip_info->num_adc_channels; i++) { + ci = &st->chan_info[i]; + ret = st->bops->reg_write(st, AD7606_CALIB_GAIN(i), + DIV_ROUND_CLOSEST(ci->r_gain, + AD7606_CALIB_GAIN_STEP)); + if (ret) + return ret; + } + + return 0; +} + +static int ad7606_probe_channels(struct iio_dev *indio_dev) +{ + struct ad7606_state *st = iio_priv(indio_dev); + struct device *dev = indio_dev->dev.parent; + struct iio_chan_spec *channels; + bool slow_bus; + int ret, i; + + slow_bus = !(st->bops->iio_backend_config || st->offload_en); + indio_dev->num_channels = st->chip_info->num_adc_channels; + + /* Slow buses also get 1 more channel for soft timestamp */ + if (slow_bus) + indio_dev->num_channels++; + + channels = devm_kcalloc(dev, indio_dev->num_channels, sizeof(*channels), + GFP_KERNEL); + if (!channels) + return -ENOMEM; + + for (i = 0; i < st->chip_info->num_adc_channels; i++) { + struct iio_chan_spec *chan = &channels[i]; + + chan->type = IIO_VOLTAGE; + chan->indexed = 1; + chan->channel = i; + chan->scan_index = i; + chan->scan_type.sign = 's'; + chan->scan_type.realbits = st->chip_info->bits; + /* + * If in SPI offload mode, storagebits are set based + * on the spi-engine hw implementation. + */ + chan->scan_type.storagebits = st->offload_en ? + st->chip_info->offload_storagebits : + (st->chip_info->bits > 16 ? 32 : 16); + + chan->scan_type.endianness = IIO_CPU; + + if (indio_dev->modes & INDIO_DIRECT_MODE) + chan->info_mask_separate |= BIT(IIO_CHAN_INFO_RAW); + + if (st->sw_mode_en) { + chan->info_mask_separate |= BIT(IIO_CHAN_INFO_SCALE); + chan->info_mask_separate_available |= + BIT(IIO_CHAN_INFO_SCALE); + + if (st->chip_info->calib_offset_avail) { + chan->info_mask_separate |= + BIT(IIO_CHAN_INFO_CALIBBIAS) | + BIT(IIO_CHAN_INFO_CONVDELAY); + chan->info_mask_separate_available |= + BIT(IIO_CHAN_INFO_CALIBBIAS) | + BIT(IIO_CHAN_INFO_CONVDELAY); + } + + /* + * All chips with software mode support oversampling, + * so we skip the oversampling_available check. And the + * shared_by_type instead of shared_by_all on slow + * buses is for backward compatibility. + */ + if (slow_bus) + chan->info_mask_shared_by_type |= + BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO); + else + chan->info_mask_shared_by_all |= + BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO); + + chan->info_mask_shared_by_all_available |= + BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO); + } else { + chan->info_mask_shared_by_type |= + BIT(IIO_CHAN_INFO_SCALE); + + if (st->chip_info->oversampling_avail) + chan->info_mask_shared_by_all |= + BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO); + } + + if (!slow_bus) + chan->info_mask_shared_by_all |= + BIT(IIO_CHAN_INFO_SAMP_FREQ); + + ret = st->chip_info->scale_setup_cb(indio_dev, chan); + if (ret) + return ret; + } + + if (slow_bus) + channels[i] = IIO_CHAN_SOFT_TIMESTAMP(i); + + indio_dev->channels = channels; + + return 0; +} + +static void ad7606_pwm_disable(void *data) +{ + pwm_disable(data); +} + +int ad7606_probe(struct device *dev, int irq, void __iomem *base_address, + const struct ad7606_chip_info *chip_info, + const struct ad7606_bus_ops *bops) +{ + struct ad7606_state *st; + int ret; + struct iio_dev *indio_dev; + + indio_dev = devm_iio_device_alloc(dev, sizeof(*st)); + if (!indio_dev) + return -ENOMEM; + + st = iio_priv(indio_dev); + dev_set_drvdata(dev, indio_dev); + + ret = devm_mutex_init(dev, &st->lock); + if (ret) + return ret; + + st->dev = dev; + st->bops = bops; + st->base_address = base_address; + st->oversampling = 1; + st->sw_mode_en = device_property_read_bool(dev, "adi,sw-mode"); + + if (st->sw_mode_en && !chip_info->sw_setup_cb) + return dev_err_probe(dev, -EINVAL, + "Software mode is not supported for this chip\n"); + + ret = devm_regulator_get_enable(dev, "avcc"); + if (ret) + return dev_err_probe(dev, ret, + "Failed to enable specified AVcc supply\n"); + + ret = devm_regulator_get_enable(dev, "vdrive"); + if (ret) + return dev_err_probe(dev, ret, + "Failed to enable Vdrive supply\n"); + + ret = devm_regulator_get_enable_optional(dev, "refin"); + if (ret && ret != -ENODEV) + return dev_err_probe(dev, ret, + "Failed to enable REFIN supply\n"); + + st->chip_info = chip_info; + + if (st->chip_info->oversampling_num) { + st->oversampling_avail = st->chip_info->oversampling_avail; + st->num_os_ratios = st->chip_info->oversampling_num; + } + + ret = ad7606_request_gpios(st); + if (ret) + return ret; + + if (st->gpio_os) { + if (st->gpio_range) + indio_dev->info = &ad7606_info_os_and_range; + else + indio_dev->info = &ad7606_info_os; + } else { + if (st->gpio_range) + indio_dev->info = &ad7606_info_range; + else + indio_dev->info = &ad7606_info_no_os_or_range; + } + + /* AXI ADC backend doesn't support single read. */ + indio_dev->modes = st->bops->iio_backend_config ? 0 : INDIO_DIRECT_MODE; + indio_dev->name = chip_info->name; + + /* Using spi-engine with offload support ? */ + if (st->bops->offload_config) { + ret = st->bops->offload_config(dev, indio_dev); + if (ret) + return ret; + } + + ret = ad7606_probe_channels(indio_dev); + if (ret) + return ret; + + ret = ad7606_reset(st); + if (ret) + dev_warn(st->dev, "failed to RESET: no RESET GPIO specified\n"); + + /* AD7616 requires al least 15ms to reconfigure after a reset */ + if (st->chip_info->init_delay_ms) { + if (msleep_interruptible(st->chip_info->init_delay_ms)) + return -ERESTARTSYS; + } + + /* If convst pin is not defined, setup PWM. */ + if (!st->gpio_convst || st->offload_en) { + st->cnvst_pwm = devm_pwm_get(dev, NULL); + if (IS_ERR(st->cnvst_pwm)) + return PTR_ERR(st->cnvst_pwm); + + /* The PWM is initialized at 1MHz to have a fast enough GPIO emulation. */ + ret = ad7606_set_sampling_freq(st, 1 * MEGA); + if (ret) + return ret; + + ret = ad7606_pwm_set_low(st); + if (ret) + return ret; + + /* + * PWM is not disabled when sampling stops, but instead its duty cycle is set + * to 0% to be sure we have a "low" state. After we unload the driver, let's + * disable the PWM. + */ + ret = devm_add_action_or_reset(dev, ad7606_pwm_disable, + st->cnvst_pwm); + if (ret) + return ret; + } + + if (st->bops->iio_backend_config) { + /* + * If there is a backend, the PWM should not overpass the maximum sampling + * frequency the chip supports. + */ + ret = ad7606_set_sampling_freq(st, chip_info->max_samplerate); + if (ret) + return ret; + + ret = st->bops->iio_backend_config(dev, indio_dev); + if (ret) + return ret; + + indio_dev->setup_ops = &ad7606_backend_buffer_ops; + } else if (!st->offload_en) { + /* Reserve the PWM use only for backend (force gpio_convst definition) */ + if (!st->gpio_convst) + return dev_err_probe(dev, -EINVAL, + "No backend, connect convst to a GPIO"); + + init_completion(&st->completion); + st->trig = devm_iio_trigger_alloc(dev, "%s-dev%d", + indio_dev->name, + iio_device_id(indio_dev)); + if (!st->trig) + return -ENOMEM; + + st->trig->ops = &ad7606_trigger_ops; + iio_trigger_set_drvdata(st->trig, indio_dev); + ret = devm_iio_trigger_register(dev, st->trig); + if (ret) + return ret; + + indio_dev->trig = iio_trigger_get(st->trig); + + ret = devm_request_threaded_irq(dev, irq, NULL, &ad7606_interrupt, + IRQF_TRIGGER_FALLING | IRQF_ONESHOT, + chip_info->name, indio_dev); + if (ret) + return ret; + + ret = devm_iio_triggered_buffer_setup(dev, indio_dev, + &iio_pollfunc_store_time, + &ad7606_trigger_handler, + &ad7606_buffer_ops); + if (ret) + return ret; + } + + st->write_scale = ad7606_write_scale_hw; + st->write_os = ad7606_write_os_hw; + + /* Offload needs 1 DOUT line, applying this setting in sw_setup_cb. */ + if (st->sw_mode_en || st->offload_en) { + indio_dev->info = &ad7606_info_sw_mode; + st->chip_info->sw_setup_cb(indio_dev); + } + + if (st->sw_mode_en && st->chip_info->calib_gain_avail) { + ret = ad7606_set_gain_calib(st); + if (ret) + return ret; + } + + return devm_iio_device_register(dev, indio_dev); +} +EXPORT_SYMBOL_NS_GPL(ad7606_probe, "IIO_AD7606"); + +#ifdef CONFIG_PM_SLEEP + +static int ad7606_suspend(struct device *dev) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct ad7606_state *st = iio_priv(indio_dev); + + if (st->gpio_standby) { + gpiod_set_value(st->gpio_range, 1); + gpiod_set_value(st->gpio_standby, 1); + } + + return 0; +} + +static int ad7606_resume(struct device *dev) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct ad7606_state *st = iio_priv(indio_dev); + + if (st->gpio_standby) { + gpiod_set_value(st->gpio_range, st->chan_info[0].range); + gpiod_set_value(st->gpio_standby, 1); + ad7606_reset(st); + } + + return 0; +} + +SIMPLE_DEV_PM_OPS(ad7606_pm_ops, ad7606_suspend, ad7606_resume); +EXPORT_SYMBOL_NS_GPL(ad7606_pm_ops, "IIO_AD7606"); + +#endif + +MODULE_AUTHOR("Michael Hennerich "); +MODULE_DESCRIPTION("Analog Devices AD7606 ADC"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/adc/ad7606.h b/drivers/iio/adc/ad7606.h new file mode 100644 index 000000000..2951bb731 --- /dev/null +++ b/drivers/iio/adc/ad7606.h @@ -0,0 +1,251 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * AD7606 ADC driver + * + * Copyright 2011 Analog Devices Inc. + */ + +#ifndef IIO_ADC_AD7606_H_ +#define IIO_ADC_AD7606_H_ + +#define AD760X_MAX_CHANNELS 16 + +#define AD7616_CONFIGURATION_REGISTER 0x02 +#define AD7616_OS_MASK GENMASK(4, 2) +#define AD7616_BURST_MODE BIT(6) +#define AD7616_SEQEN_MODE BIT(5) +#define AD7616_RANGE_CH_A_ADDR_OFF 0x04 +#define AD7616_RANGE_CH_B_ADDR_OFF 0x06 +/* + * Range of channels from a group are stored in 2 registers. + * 0, 1, 2, 3 in a register followed by 4, 5, 6, 7 in second register. + * For channels from second group(8-15) the order is the same, only with + * an offset of 2 for register address. + */ +#define AD7616_RANGE_CH_ADDR(ch) ((ch) >> 2) +/* The range of the channel is stored in 2 bits */ +#define AD7616_RANGE_CH_MSK(ch) (0b11 << (((ch) & 0b11) * 2)) +#define AD7616_RANGE_CH_MODE(ch, mode) ((mode) << ((((ch) & 0b11)) * 2)) + +#define AD7606_CONFIGURATION_REGISTER 0x02 +#define AD7606_SINGLE_DOUT 0x00 + +/* + * Range for AD7606B channels are stored in registers starting with address 0x3. + * Each register stores range for 2 channels(4 bits per channel). + */ +#define AD7606_RANGE_CH_MSK(ch) (GENMASK(3, 0) << (4 * ((ch) & 0x1))) +#define AD7606_RANGE_CH_MODE(ch, mode) \ + ((GENMASK(3, 0) & (mode)) << (4 * ((ch) & 0x1))) +#define AD7606_RANGE_CH_ADDR(ch) (0x03 + ((ch) >> 1)) +#define AD7606_OS_MODE 0x08 + +#define AD7606_CALIB_GAIN(ch) (0x09 + (ch)) +#define AD7606_CALIB_GAIN_MASK GENMASK(5, 0) +#define AD7606_CALIB_OFFSET(ch) (0x11 + (ch)) +#define AD7606_CALIB_PHASE(ch) (0x19 + (ch)) + +struct ad7606_state; + +typedef int (*ad7606_scale_setup_cb_t)(struct iio_dev *indio_dev, + struct iio_chan_spec *chan); +typedef int (*ad7606_sw_setup_cb_t)(struct iio_dev *indio_dev); + +/** + * struct ad7606_chip_info - chip specific information + * @max_samplerate: maximum supported sample rate + * @name: device name + * @bits: data width in bits + * @num_adc_channels: the number of physical voltage inputs + * @scale_setup_cb: callback to setup the scales for each channel + * @sw_setup_cb: callback to setup the software mode if available. + * @oversampling_avail: pointer to the array which stores the available + * oversampling ratios. + * @oversampling_num: number of elements stored in oversampling_avail array + * @os_req_reset: some devices require a reset to update oversampling + * @init_delay_ms: required delay in milliseconds for initialization + * after a restart + * @offload_storagebits: storage bits used by the offload hw implementation + * @calib_gain_avail: chip supports gain calibration + * @calib_offset_avail: pointer to offset calibration range/limits array + * @calib_phase_avail: pointer to phase calibration range/limits array + */ +struct ad7606_chip_info { + unsigned int max_samplerate; + const char *name; + unsigned int bits; + unsigned int num_adc_channels; + ad7606_scale_setup_cb_t scale_setup_cb; + ad7606_sw_setup_cb_t sw_setup_cb; + const unsigned int *oversampling_avail; + unsigned int oversampling_num; + bool os_req_reset; + unsigned long init_delay_ms; + u8 offload_storagebits; + bool calib_gain_avail; + const int *calib_offset_avail; + const int (*calib_phase_avail)[2]; +}; + +/** + * struct ad7606_chan_info - channel configuration + * @scale_avail: pointer to the array which stores the available scales + * @num_scales: number of elements stored in the scale_avail array + * @range: voltage range selection, selects which scale to apply + * @reg_offset: offset for the register value, to be applied when + * writing the value of 'range' to the register value + * @r_gain: gain resistor value in ohms, to be set to match the + * external r_filter value + */ +struct ad7606_chan_info { +#define AD760X_MAX_SCALES 16 + const unsigned int (*scale_avail)[2]; + unsigned int num_scales; + unsigned int range; + unsigned int reg_offset; + unsigned int r_gain; +}; + +/** + * struct ad7606_state - driver instance specific data + * @dev: pointer to kernel device + * @chip_info: entry in the table of chips that describes this device + * @bops: bus operations (SPI or parallel) + * @chan_info: scale configuration for channels + * @oversampling: oversampling selection + * @cnvst_pwm: pointer to the PWM device connected to the cnvst pin + * @base_address: address from where to read data in parallel operation + * @sw_mode_en: software mode enabled + * @oversampling_avail: pointer to the array which stores the available + * oversampling ratios. + * @num_os_ratios: number of elements stored in oversampling_avail array + * @back: pointer to the iio_backend structure, if used + * @write_scale: pointer to the function which writes the scale + * @write_os: pointer to the function which writes the os + * @lock: protect sensor state from concurrent accesses to GPIOs + * @gpio_convst: GPIO descriptor for conversion start signal (CONVST) + * @gpio_reset: GPIO descriptor for device hard-reset + * @gpio_range: GPIO descriptor for range selection + * @gpio_standby: GPIO descriptor for stand-by signal (STBY), + * controls power-down mode of device + * @gpio_frstdata: GPIO descriptor for reading from device when data + * is being read on the first channel + * @gpio_os: GPIO descriptors to control oversampling on the device + * @trig: The IIO trigger associated with the device. + * @completion: completion to indicate end of conversion + * @data: buffer for reading data from the device + * @offload_en: SPI offload enabled + * @bus_data: bus-specific variables + * @d16: be16 buffer for reading data from the device + */ +struct ad7606_state { + struct device *dev; + const struct ad7606_chip_info *chip_info; + const struct ad7606_bus_ops *bops; + struct ad7606_chan_info chan_info[AD760X_MAX_CHANNELS]; + unsigned int oversampling; + struct pwm_device *cnvst_pwm; + void __iomem *base_address; + bool sw_mode_en; + const unsigned int *oversampling_avail; + unsigned int num_os_ratios; + struct iio_backend *back; + int (*write_scale)(struct iio_dev *indio_dev, int ch, int val); + int (*write_os)(struct iio_dev *indio_dev, int val); + + struct mutex lock; /* protect sensor state */ + struct gpio_desc *gpio_convst; + struct gpio_desc *gpio_reset; + struct gpio_desc *gpio_range; + struct gpio_desc *gpio_standby; + struct gpio_desc *gpio_frstdata; + struct gpio_descs *gpio_os; + struct iio_trigger *trig; + struct completion completion; + + bool offload_en; + void *bus_data; + + /* + * DMA (thus cache coherency maintenance) may require the + * transfer buffers to live in their own cache lines. + * 16 * 16-bit samples for AD7616 + * 8 * 32-bit samples for AD7616C-18 (and similar) + */ + struct { + union { + u16 buf16[16]; + u32 buf32[8]; + }; + aligned_s64 timestamp; + } data __aligned(IIO_DMA_MINALIGN); + __be16 d16[2]; +}; + +/** + * struct ad7606_bus_ops - driver bus operations + * @iio_backend_config: function pointer for configuring the iio_backend for + * the compatibles that use it + * @read_block: function pointer for reading blocks of data + * @sw_mode_config: pointer to a function which configured the device + * for software mode + * @offload_config: function pointer for configuring offload support, + * where any + * @reg_read: function pointer for reading spi register + * @reg_write: function pointer for writing spi register + * @update_scan_mode: function pointer for handling the calls to iio_info's + * update_scan mode when enabling/disabling channels. + * @rd_wr_cmd: pointer to the function which calculates the spi address + */ +struct ad7606_bus_ops { + /* more methods added in future? */ + int (*iio_backend_config)(struct device *dev, struct iio_dev *indio_dev); + int (*offload_config)(struct device *dev, struct iio_dev *indio_dev); + int (*read_block)(struct device *dev, int num, void *data); + int (*sw_mode_config)(struct iio_dev *indio_dev); + int (*reg_read)(struct ad7606_state *st, unsigned int addr); + int (*reg_write)(struct ad7606_state *st, + unsigned int addr, + unsigned int val); + int (*update_scan_mode)(struct iio_dev *indio_dev, const unsigned long *scan_mask); + u16 (*rd_wr_cmd)(int addr, char is_write_op); +}; + +/** + * struct ad7606_bus_info - aggregate ad7606_chip_info and ad7606_bus_ops + * @chip_info: entry in the table of chips that describes this device + * @bops: bus operations (SPI or parallel) + */ +struct ad7606_bus_info { + const struct ad7606_chip_info *chip_info; + const struct ad7606_bus_ops *bops; +}; + +int ad7606_probe(struct device *dev, int irq, void __iomem *base_address, + const struct ad7606_chip_info *info, + const struct ad7606_bus_ops *bops); + +int ad7606_reset(struct ad7606_state *st); +int ad7606_pwm_set_swing(struct ad7606_state *st); +int ad7606_pwm_set_low(struct ad7606_state *st); + +extern const struct ad7606_chip_info ad7605_4_info; +extern const struct ad7606_chip_info ad7606_8_info; +extern const struct ad7606_chip_info ad7606_6_info; +extern const struct ad7606_chip_info ad7606_4_info; +extern const struct ad7606_chip_info ad7606b_info; +extern const struct ad7606_chip_info ad7606c_16_info; +extern const struct ad7606_chip_info ad7606c_18_info; +extern const struct ad7606_chip_info ad7607_info; +extern const struct ad7606_chip_info ad7608_info; +extern const struct ad7606_chip_info ad7609_info; +extern const struct ad7606_chip_info ad7616_info; + +#ifdef CONFIG_PM_SLEEP +extern const struct dev_pm_ops ad7606_pm_ops; +#define AD7606_PM_OPS (&ad7606_pm_ops) +#else +#define AD7606_PM_OPS NULL +#endif + +#endif /* IIO_ADC_AD7606_H_ */ diff --git a/drivers/iio/adc/ad7606_bus_iface.h b/drivers/iio/adc/ad7606_bus_iface.h new file mode 100644 index 000000000..f2c979a9b --- /dev/null +++ b/drivers/iio/adc/ad7606_bus_iface.h @@ -0,0 +1,16 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (c) 2010-2024 Analog Devices Inc. + * Copyright (c) 2025 Baylibre, SAS + */ +#ifndef __LINUX_PLATFORM_DATA_AD7606_H__ +#define __LINUX_PLATFORM_DATA_AD7606_H__ + +struct iio_backend; + +struct ad7606_platform_data { + int (*bus_reg_read)(struct iio_backend *back, u32 reg, u32 *val); + int (*bus_reg_write)(struct iio_backend *back, u32 reg, u32 val); +}; + +#endif /* __LINUX_PLATFORM_DATA_AD7606_H__ */ diff --git a/drivers/iio/adc/ad7606_par.c b/drivers/iio/adc/ad7606_par.c new file mode 100644 index 000000000..5b137e947 --- /dev/null +++ b/drivers/iio/adc/ad7606_par.c @@ -0,0 +1,264 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * AD7606 Parallel Interface ADC driver + * + * Copyright 2011 - 2024 Analog Devices Inc. + * Copyright 2024 BayLibre SAS. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "ad7606.h" +#include "ad7606_bus_iface.h" + +static int ad7606_par_bus_update_scan_mode(struct iio_dev *indio_dev, + const unsigned long *scan_mask) +{ + struct ad7606_state *st = iio_priv(indio_dev); + unsigned int c, ret; + + for (c = 0; c < indio_dev->num_channels; c++) { + if (test_bit(c, scan_mask)) + ret = iio_backend_chan_enable(st->back, c); + else + ret = iio_backend_chan_disable(st->back, c); + if (ret) + return ret; + } + + return 0; +} + +static int ad7606_par_bus_setup_iio_backend(struct device *dev, + struct iio_dev *indio_dev) +{ + struct ad7606_state *st = iio_priv(indio_dev); + unsigned int c; + int ret; + struct iio_backend_data_fmt data = { + .sign_extend = true, + .enable = true, + }; + + st->back = devm_iio_backend_get(dev, NULL); + if (IS_ERR(st->back)) + return PTR_ERR(st->back); + + /* If the device is iio_backend powered the PWM is mandatory */ + if (!st->cnvst_pwm) + return dev_err_probe(st->dev, -EINVAL, + "A PWM is mandatory when using backend.\n"); + + ret = devm_iio_backend_request_buffer(dev, st->back, indio_dev); + if (ret) + return ret; + + ret = devm_iio_backend_enable(dev, st->back); + if (ret) + return ret; + + for (c = 0; c < indio_dev->num_channels; c++) { + ret = iio_backend_data_format_set(st->back, c, &data); + if (ret) + return ret; + } + + return 0; +} + +static int ad7606_par_bus_reg_read(struct ad7606_state *st, unsigned int addr) +{ + struct ad7606_platform_data *pdata = st->dev->platform_data; + int val, ret; + + ret = pdata->bus_reg_read(st->back, addr, &val); + if (ret) + return ret; + + return val; +} + +static int ad7606_par_bus_reg_write(struct ad7606_state *st, unsigned int addr, + unsigned int val) +{ + struct ad7606_platform_data *pdata = st->dev->platform_data; + + return pdata->bus_reg_write(st->back, addr, val); +} + +static const struct ad7606_bus_ops ad7606_bi_bops = { + .iio_backend_config = ad7606_par_bus_setup_iio_backend, + .update_scan_mode = ad7606_par_bus_update_scan_mode, + .reg_read = ad7606_par_bus_reg_read, + .reg_write = ad7606_par_bus_reg_write, +}; + +static int ad7606_par16_read_block(struct device *dev, + int count, void *buf) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct ad7606_state *st = iio_priv(indio_dev); + + + /* + * On the parallel interface, the frstdata signal is set to high while + * and after reading the sample of the first channel and low for all + * other channels. This can be used to check that the incoming data is + * correctly aligned. During normal operation the data should never + * become unaligned, but some glitch or electrostatic discharge might + * cause an extra read or clock cycle. Monitoring the frstdata signal + * allows to recover from such failure situations. + */ + int num = count; + u16 *_buf = buf; + + if (st->gpio_frstdata) { + insw((unsigned long)st->base_address, _buf, 1); + if (!gpiod_get_value(st->gpio_frstdata)) { + ad7606_reset(st); + return -EIO; + } + _buf++; + num--; + } + insw((unsigned long)st->base_address, _buf, num); + return 0; +} + +static const struct ad7606_bus_ops ad7606_par16_bops = { + .read_block = ad7606_par16_read_block, +}; + +static int ad7606_par8_read_block(struct device *dev, + int count, void *buf) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct ad7606_state *st = iio_priv(indio_dev); + /* + * On the parallel interface, the frstdata signal is set to high while + * and after reading the sample of the first channel and low for all + * other channels. This can be used to check that the incoming data is + * correctly aligned. During normal operation the data should never + * become unaligned, but some glitch or electrostatic discharge might + * cause an extra read or clock cycle. Monitoring the frstdata signal + * allows to recover from such failure situations. + */ + int num = count; + u16 *_buf = buf; + + if (st->gpio_frstdata) { + insb((unsigned long)st->base_address, _buf, 2); + if (!gpiod_get_value(st->gpio_frstdata)) { + ad7606_reset(st); + return -EIO; + } + _buf++; + num--; + } + insb((unsigned long)st->base_address, _buf, num * 2); + + return 0; +} + +static const struct ad7606_bus_ops ad7606_par8_bops = { + .read_block = ad7606_par8_read_block, +}; + +static int ad7606_par_probe(struct platform_device *pdev) +{ + const struct ad7606_chip_info *chip_info; + const struct platform_device_id *id; + struct resource *res; + void __iomem *addr; + resource_size_t remap_size; + int irq; + + /* + * If a firmware node is available (ACPI or DT), platform_device_id is null + * and we must use get_match_data. + */ + if (dev_fwnode(&pdev->dev)) { + chip_info = device_get_match_data(&pdev->dev); + if (device_property_present(&pdev->dev, "io-backends")) + /* + * If a backend is available ,call the core probe with backend + * bops, otherwise use the former bops. + */ + return ad7606_probe(&pdev->dev, 0, NULL, + chip_info, + &ad7606_bi_bops); + } else { + id = platform_get_device_id(pdev); + chip_info = (const struct ad7606_chip_info *)id->driver_data; + } + + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return irq; + + addr = devm_platform_get_and_ioremap_resource(pdev, 0, &res); + if (IS_ERR(addr)) + return PTR_ERR(addr); + + remap_size = resource_size(res); + + return ad7606_probe(&pdev->dev, irq, addr, chip_info, + remap_size > 1 ? &ad7606_par16_bops : + &ad7606_par8_bops); +} + +static const struct platform_device_id ad7606_driver_ids[] = { + { .name = "ad7605-4", .driver_data = (kernel_ulong_t)&ad7605_4_info, }, + { .name = "ad7606-4", .driver_data = (kernel_ulong_t)&ad7606_4_info, }, + { .name = "ad7606-6", .driver_data = (kernel_ulong_t)&ad7606_6_info, }, + { .name = "ad7606-8", .driver_data = (kernel_ulong_t)&ad7606_8_info, }, + { .name = "ad7606b", .driver_data = (kernel_ulong_t)&ad7606b_info, }, + { .name = "ad7606c-16", .driver_data = (kernel_ulong_t)&ad7606c_16_info }, + { .name = "ad7606c-18", .driver_data = (kernel_ulong_t)&ad7606c_18_info }, + { .name = "ad7607", .driver_data = (kernel_ulong_t)&ad7607_info, }, + { .name = "ad7608", .driver_data = (kernel_ulong_t)&ad7608_info, }, + { .name = "ad7609", .driver_data = (kernel_ulong_t)&ad7609_info, }, + { } +}; +MODULE_DEVICE_TABLE(platform, ad7606_driver_ids); + +static const struct of_device_id ad7606_of_match[] = { + { .compatible = "adi,ad7605-4", .data = &ad7605_4_info }, + { .compatible = "adi,ad7606-4", .data = &ad7606_4_info }, + { .compatible = "adi,ad7606-6", .data = &ad7606_6_info }, + { .compatible = "adi,ad7606-8", .data = &ad7606_8_info }, + { .compatible = "adi,ad7606b", .data = &ad7606b_info }, + { .compatible = "adi,ad7606c-16", .data = &ad7606c_16_info }, + { .compatible = "adi,ad7606c-18", .data = &ad7606c_18_info }, + { .compatible = "adi,ad7607", .data = &ad7607_info }, + { .compatible = "adi,ad7608", .data = &ad7608_info }, + { .compatible = "adi,ad7609", .data = &ad7609_info }, + { } +}; +MODULE_DEVICE_TABLE(of, ad7606_of_match); + +static struct platform_driver ad7606_driver = { + .probe = ad7606_par_probe, + .id_table = ad7606_driver_ids, + .driver = { + .name = "ad7606", + .pm = AD7606_PM_OPS, + .of_match_table = ad7606_of_match, + }, +}; +module_platform_driver(ad7606_driver); + +MODULE_AUTHOR("Michael Hennerich "); +MODULE_DESCRIPTION("Analog Devices AD7606 ADC"); +MODULE_LICENSE("GPL v2"); +MODULE_IMPORT_NS("IIO_AD7606"); +MODULE_IMPORT_NS("IIO_BACKEND"); diff --git a/drivers/iio/adc/ad7606_spi.c b/drivers/iio/adc/ad7606_spi.c new file mode 100644 index 000000000..1ebdfb858 --- /dev/null +++ b/drivers/iio/adc/ad7606_spi.c @@ -0,0 +1,512 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * AD7606 SPI ADC driver + * + * Copyright 2011 Analog Devices Inc. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +#include "ad7606.h" + +#define MAX_SPI_FREQ_HZ 23500000 /* VDRIVE above 4.75 V */ + +struct spi_bus_data { + struct spi_offload *offload; + struct spi_offload_trigger *offload_trigger; + struct spi_transfer offload_xfer; + struct spi_message offload_msg; +}; + +static u16 ad7616_spi_rd_wr_cmd(int addr, char is_write_op) +{ + /* + * The address of register consist of one w/r bit + * 6 bits of address followed by one reserved bit. + */ + return ((addr & 0x7F) << 1) | ((is_write_op & 0x1) << 7); +} + +static u16 ad7606b_spi_rd_wr_cmd(int addr, char is_write_op) +{ + /* + * The address of register consists of one bit which + * specifies a read command placed in bit 6, followed by + * 6 bits of address. + */ + return (addr & 0x3F) | (((~is_write_op) & 0x1) << 6); +} + +static int ad7606_spi_read_block(struct device *dev, + int count, void *buf) +{ + struct spi_device *spi = to_spi_device(dev); + int i, ret; + unsigned short *data = buf; + __be16 *bdata = buf; + + ret = spi_read(spi, buf, count * 2); + if (ret < 0) { + dev_err(&spi->dev, "SPI read error\n"); + return ret; + } + + for (i = 0; i < count; i++) + data[i] = be16_to_cpu(bdata[i]); + + return 0; +} + +static int ad7606_spi_read_block14to16(struct device *dev, + int count, void *buf) +{ + struct spi_device *spi = to_spi_device(dev); + struct spi_transfer xfer = { + .bits_per_word = 14, + .len = count * sizeof(u16), + .rx_buf = buf, + }; + + return spi_sync_transfer(spi, &xfer, 1); +} + +static int ad7606_spi_read_block18to32(struct device *dev, + int count, void *buf) +{ + struct spi_device *spi = to_spi_device(dev); + struct spi_transfer xfer = { + .bits_per_word = 18, + .len = count * sizeof(u32), + .rx_buf = buf, + }; + + return spi_sync_transfer(spi, &xfer, 1); +} + +static int ad7606_spi_reg_read(struct ad7606_state *st, unsigned int addr) +{ + struct spi_device *spi = to_spi_device(st->dev); + struct spi_transfer t[] = { + { + .tx_buf = &st->d16[0], + .len = 2, + .cs_change = 1, + }, { + .rx_buf = &st->d16[1], + .len = 2, + }, + }; + int ret; + + st->d16[0] = cpu_to_be16(st->bops->rd_wr_cmd(addr, 0) << 8); + + ret = spi_sync_transfer(spi, t, ARRAY_SIZE(t)); + if (ret < 0) + return ret; + + return be16_to_cpu(st->d16[1]); +} + +static int ad7606_spi_reg_write(struct ad7606_state *st, + unsigned int addr, + unsigned int val) +{ + struct spi_device *spi = to_spi_device(st->dev); + + st->d16[0] = cpu_to_be16((st->bops->rd_wr_cmd(addr, 1) << 8) | + (val & 0xFF)); + + return spi_write(spi, &st->d16[0], sizeof(st->d16[0])); +} + +static int ad7606b_sw_mode_config(struct iio_dev *indio_dev) +{ + struct ad7606_state *st = iio_priv(indio_dev); + + /* Configure device spi to output on a single channel */ + return st->bops->reg_write(st, AD7606_CONFIGURATION_REGISTER, + AD7606_SINGLE_DOUT); +} + +static const struct spi_offload_config ad7606_spi_offload_config = { + .capability_flags = SPI_OFFLOAD_CAP_TRIGGER | + SPI_OFFLOAD_CAP_RX_STREAM_DMA, +}; + +static int ad7606_spi_offload_buffer_postenable(struct iio_dev *indio_dev) +{ + const struct iio_scan_type *scan_type; + struct ad7606_state *st = iio_priv(indio_dev); + struct spi_bus_data *bus_data = st->bus_data; + struct spi_transfer *xfer = &bus_data->offload_xfer; + struct spi_device *spi = to_spi_device(st->dev); + struct spi_offload_trigger_config config = { + .type = SPI_OFFLOAD_TRIGGER_DATA_READY, + }; + int ret; + + scan_type = &indio_dev->channels[0].scan_type; + + xfer->bits_per_word = scan_type->realbits; + xfer->offload_flags = SPI_OFFLOAD_XFER_RX_STREAM; + /* + * Using SPI offload, storagebits are related to the spi-engine + * hw implementation, can be 16 or 32, so can't be used to compute + * struct spi_transfer.len. Using realbits instead. + */ + xfer->len = (scan_type->realbits > 16 ? 4 : 2) * + st->chip_info->num_adc_channels; + + spi_message_init_with_transfers(&bus_data->offload_msg, xfer, 1); + bus_data->offload_msg.offload = bus_data->offload; + + ret = spi_optimize_message(spi, &bus_data->offload_msg); + if (ret) { + dev_err(st->dev, "failed to prepare offload, err: %d\n", ret); + return ret; + } + + ret = spi_offload_trigger_enable(bus_data->offload, + bus_data->offload_trigger, + &config); + if (ret) + goto err_unoptimize_message; + + ret = ad7606_pwm_set_swing(st); + if (ret) + goto err_offload_exit_conversion_mode; + + return 0; + +err_offload_exit_conversion_mode: + spi_offload_trigger_disable(bus_data->offload, + bus_data->offload_trigger); + +err_unoptimize_message: + spi_unoptimize_message(&bus_data->offload_msg); + + return ret; +} + +static int ad7606_spi_offload_buffer_predisable(struct iio_dev *indio_dev) +{ + struct ad7606_state *st = iio_priv(indio_dev); + struct spi_bus_data *bus_data = st->bus_data; + int ret; + + ret = ad7606_pwm_set_low(st); + if (ret) + return ret; + + spi_offload_trigger_disable(bus_data->offload, + bus_data->offload_trigger); + spi_unoptimize_message(&bus_data->offload_msg); + + return 0; +} + +static const struct iio_buffer_setup_ops ad7606_offload_buffer_setup_ops = { + .postenable = ad7606_spi_offload_buffer_postenable, + .predisable = ad7606_spi_offload_buffer_predisable, +}; + +static bool ad7606_spi_offload_trigger_match( + struct spi_offload_trigger *trigger, + enum spi_offload_trigger_type type, + u64 *args, u32 nargs) +{ + if (type != SPI_OFFLOAD_TRIGGER_DATA_READY) + return false; + + /* + * Requires 1 arg: + * args[0] is the trigger event. + */ + if (nargs != 1 || args[0] != AD7606_TRIGGER_EVENT_BUSY) + return false; + + return true; +} + +static int ad7606_spi_offload_trigger_request( + struct spi_offload_trigger *trigger, + enum spi_offload_trigger_type type, + u64 *args, u32 nargs) +{ + /* Should already be validated by match, but just in case. */ + if (nargs != 1) + return -EINVAL; + + return 0; +} + +static int ad7606_spi_offload_trigger_validate( + struct spi_offload_trigger *trigger, + struct spi_offload_trigger_config *config) +{ + if (config->type != SPI_OFFLOAD_TRIGGER_DATA_READY) + return -EINVAL; + + return 0; +} + +static const struct spi_offload_trigger_ops ad7606_offload_trigger_ops = { + .match = ad7606_spi_offload_trigger_match, + .request = ad7606_spi_offload_trigger_request, + .validate = ad7606_spi_offload_trigger_validate, +}; + +static int ad7606_spi_offload_probe(struct device *dev, + struct iio_dev *indio_dev) +{ + struct ad7606_state *st = iio_priv(indio_dev); + struct spi_device *spi = to_spi_device(dev); + struct spi_bus_data *bus_data; + struct dma_chan *rx_dma; + struct spi_offload_trigger_info trigger_info = { + .fwnode = dev_fwnode(dev), + .ops = &ad7606_offload_trigger_ops, + .priv = st, + }; + int ret; + + bus_data = devm_kzalloc(dev, sizeof(*bus_data), GFP_KERNEL); + if (!bus_data) + return -ENOMEM; + st->bus_data = bus_data; + + bus_data->offload = devm_spi_offload_get(dev, spi, + &ad7606_spi_offload_config); + ret = PTR_ERR_OR_ZERO(bus_data->offload); + if (ret && ret != -ENODEV) + return dev_err_probe(dev, ret, "failed to get SPI offload\n"); + /* Allow main ad7606_probe function to continue. */ + if (ret == -ENODEV) + return 0; + + ret = devm_spi_offload_trigger_register(dev, &trigger_info); + if (ret) + return dev_err_probe(dev, ret, + "failed to register offload trigger\n"); + + bus_data->offload_trigger = devm_spi_offload_trigger_get(dev, + bus_data->offload, SPI_OFFLOAD_TRIGGER_DATA_READY); + if (IS_ERR(bus_data->offload_trigger)) + return dev_err_probe(dev, PTR_ERR(bus_data->offload_trigger), + "failed to get offload trigger\n"); + + /* TODO: PWM setup should be ok, done for the backend. PWM mutex ? */ + rx_dma = devm_spi_offload_rx_stream_request_dma_chan(dev, + bus_data->offload); + if (IS_ERR(rx_dma)) + return dev_err_probe(dev, PTR_ERR(rx_dma), + "failed to get offload RX DMA\n"); + + ret = devm_iio_dmaengine_buffer_setup_with_handle(dev, indio_dev, + rx_dma, IIO_BUFFER_DIRECTION_IN); + if (ret) + return dev_err_probe(dev, ret, + "failed to setup offload RX DMA\n"); + + /* Use offload ops. */ + indio_dev->setup_ops = &ad7606_offload_buffer_setup_ops; + + st->offload_en = true; + + return 0; +} + +static int ad7606_spi_update_scan_mode(struct iio_dev *indio_dev, + const unsigned long *scan_mask) +{ + struct ad7606_state *st = iio_priv(indio_dev); + + if (st->offload_en) { + unsigned int num_adc_ch = st->chip_info->num_adc_channels; + + /* + * SPI offload requires that all channels are enabled since + * there isn't a way to selectively disable channels that get + * read (this is simultaneous sampling ADC) and the DMA buffer + * has no way of demuxing the data to filter out unwanted + * channels. + */ + if (!bitmap_full(scan_mask, num_adc_ch)) + return -EINVAL; + } + + return 0; +} + +static const struct ad7606_bus_ops ad7606_spi_bops = { + .offload_config = ad7606_spi_offload_probe, + .read_block = ad7606_spi_read_block, + .update_scan_mode = ad7606_spi_update_scan_mode, +}; + +static const struct ad7606_bus_ops ad7607_spi_bops = { + .offload_config = ad7606_spi_offload_probe, + .read_block = ad7606_spi_read_block14to16, + .update_scan_mode = ad7606_spi_update_scan_mode, +}; + +static const struct ad7606_bus_ops ad7608_spi_bops = { + .offload_config = ad7606_spi_offload_probe, + .read_block = ad7606_spi_read_block18to32, + .update_scan_mode = ad7606_spi_update_scan_mode, +}; + +static const struct ad7606_bus_ops ad7616_spi_bops = { + .offload_config = ad7606_spi_offload_probe, + .read_block = ad7606_spi_read_block, + .reg_read = ad7606_spi_reg_read, + .reg_write = ad7606_spi_reg_write, + .rd_wr_cmd = ad7616_spi_rd_wr_cmd, + .update_scan_mode = ad7606_spi_update_scan_mode, +}; + +static const struct ad7606_bus_ops ad7606b_spi_bops = { + .offload_config = ad7606_spi_offload_probe, + .read_block = ad7606_spi_read_block, + .reg_read = ad7606_spi_reg_read, + .reg_write = ad7606_spi_reg_write, + .rd_wr_cmd = ad7606b_spi_rd_wr_cmd, + .sw_mode_config = ad7606b_sw_mode_config, + .update_scan_mode = ad7606_spi_update_scan_mode, +}; + +static const struct ad7606_bus_ops ad7606c_18_spi_bops = { + .offload_config = ad7606_spi_offload_probe, + .read_block = ad7606_spi_read_block18to32, + .reg_read = ad7606_spi_reg_read, + .reg_write = ad7606_spi_reg_write, + .rd_wr_cmd = ad7606b_spi_rd_wr_cmd, + .sw_mode_config = ad7606b_sw_mode_config, + .update_scan_mode = ad7606_spi_update_scan_mode, +}; + +static const struct ad7606_bus_info ad7605_4_bus_info = { + .chip_info = &ad7605_4_info, + .bops = &ad7606_spi_bops, +}; + +static const struct ad7606_bus_info ad7606_8_bus_info = { + .chip_info = &ad7606_8_info, + .bops = &ad7606_spi_bops, +}; + +static const struct ad7606_bus_info ad7606_6_bus_info = { + .chip_info = &ad7606_6_info, + .bops = &ad7606_spi_bops, +}; + +static const struct ad7606_bus_info ad7606_4_bus_info = { + .chip_info = &ad7606_4_info, + .bops = &ad7606_spi_bops, +}; + +static const struct ad7606_bus_info ad7606b_bus_info = { + .chip_info = &ad7606b_info, + .bops = &ad7606b_spi_bops, +}; + +static const struct ad7606_bus_info ad7606c_16_bus_info = { + .chip_info = &ad7606c_16_info, + .bops = &ad7606b_spi_bops, +}; + +static const struct ad7606_bus_info ad7606c_18_bus_info = { + .chip_info = &ad7606c_18_info, + .bops = &ad7606c_18_spi_bops, +}; + +static const struct ad7606_bus_info ad7607_bus_info = { + .chip_info = &ad7607_info, + .bops = &ad7607_spi_bops, +}; + +static const struct ad7606_bus_info ad7608_bus_info = { + .chip_info = &ad7608_info, + .bops = &ad7608_spi_bops, +}; + +static const struct ad7606_bus_info ad7609_bus_info = { + .chip_info = &ad7609_info, + .bops = &ad7608_spi_bops, +}; + +static const struct ad7606_bus_info ad7616_bus_info = { + .chip_info = &ad7616_info, + .bops = &ad7616_spi_bops, +}; + +static int ad7606_spi_probe(struct spi_device *spi) +{ + const struct ad7606_bus_info *bus_info = spi_get_device_match_data(spi); + + return ad7606_probe(&spi->dev, spi->irq, NULL, + bus_info->chip_info, bus_info->bops); +} + +static const struct spi_device_id ad7606_id_table[] = { + { .name = "ad7605-4", .driver_data = (kernel_ulong_t)&ad7605_4_bus_info }, + { .name = "ad7606-4", .driver_data = (kernel_ulong_t)&ad7606_4_bus_info }, + { .name = "ad7606-6", .driver_data = (kernel_ulong_t)&ad7606_6_bus_info }, + { .name = "ad7606-8", .driver_data = (kernel_ulong_t)&ad7606_8_bus_info }, + { .name = "ad7606b", .driver_data = (kernel_ulong_t)&ad7606b_bus_info }, + { .name = "ad7606c-16", .driver_data = (kernel_ulong_t)&ad7606c_16_bus_info }, + { .name = "ad7606c-18", .driver_data = (kernel_ulong_t)&ad7606c_18_bus_info }, + { .name = "ad7607", .driver_data = (kernel_ulong_t)&ad7607_bus_info }, + { .name = "ad7608", .driver_data = (kernel_ulong_t)&ad7608_bus_info }, + { .name = "ad7609", .driver_data = (kernel_ulong_t)&ad7609_bus_info }, + { .name = "ad7616", .driver_data = (kernel_ulong_t)&ad7616_bus_info }, + { } +}; +MODULE_DEVICE_TABLE(spi, ad7606_id_table); + +static const struct of_device_id ad7606_of_match[] = { + { .compatible = "adi,ad7605-4", .data = &ad7605_4_bus_info }, + { .compatible = "adi,ad7606-4", .data = &ad7606_4_bus_info }, + { .compatible = "adi,ad7606-6", .data = &ad7606_6_bus_info }, + { .compatible = "adi,ad7606-8", .data = &ad7606_8_bus_info }, + { .compatible = "adi,ad7606b", .data = &ad7606b_bus_info }, + { .compatible = "adi,ad7606c-16", .data = &ad7606c_16_bus_info }, + { .compatible = "adi,ad7606c-18", .data = &ad7606c_18_bus_info }, + { .compatible = "adi,ad7607", .data = &ad7607_bus_info }, + { .compatible = "adi,ad7608", .data = &ad7608_bus_info }, + { .compatible = "adi,ad7609", .data = &ad7609_bus_info }, + { .compatible = "adi,ad7616", .data = &ad7616_bus_info }, + { } +}; +MODULE_DEVICE_TABLE(of, ad7606_of_match); + +static struct spi_driver ad7606_driver = { + .driver = { + .name = "ad7606", + .of_match_table = ad7606_of_match, + .pm = AD7606_PM_OPS, + }, + .probe = ad7606_spi_probe, + .id_table = ad7606_id_table, +}; +module_spi_driver(ad7606_driver); + +MODULE_AUTHOR("Michael Hennerich "); +MODULE_DESCRIPTION("Analog Devices AD7606 ADC"); +MODULE_LICENSE("GPL v2"); +MODULE_IMPORT_NS("IIO_AD7606"); +MODULE_IMPORT_NS("IIO_DMAENGINE_BUFFER"); diff --git a/drivers/iio/adc/ad7625.c b/drivers/iio/adc/ad7625.c new file mode 100644 index 000000000..e73a5c9e7 --- /dev/null +++ b/drivers/iio/adc/ad7625.c @@ -0,0 +1,686 @@ +// SPDX-License-Identifier: (GPL-2.0-only) +/* + * Analog Devices Inc. AD7625 ADC driver + * + * Copyright 2024 Analog Devices Inc. + * Copyright 2024 BayLibre, SAS + * + * Note that this driver requires the AXI ADC IP block configured for + * LVDS to function. See Documentation/iio/ad7625.rst for more + * information. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define AD7625_INTERNAL_REF_MV 4096 +#define AD7960_MAX_NBW_FREQ (2 * MEGA) + +struct ad7625_timing_spec { + /* Max conversion high time (t_{CNVH}). */ + unsigned int conv_high_ns; + /* Max conversion to MSB delay (t_{MSB}). */ + unsigned int conv_msb_ns; +}; + +struct ad7625_chip_info { + const char *name; + const unsigned int max_sample_freq_hz; + const struct ad7625_timing_spec *timing_spec; + const struct iio_chan_spec chan_spec; + const bool has_power_down_state; + const bool has_bandwidth_control; + const bool has_internal_vref; +}; + +/* AD7625_CHAN_SPEC - Define a chan spec structure for a specific chip */ +#define AD7625_CHAN_SPEC(_bits) { \ + .type = IIO_VOLTAGE, \ + .indexed = 1, \ + .differential = 1, \ + .channel = 0, \ + .channel2 = 1, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_SCALE), \ + .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ), \ + .scan_index = 0, \ + .scan_type.sign = 's', \ + .scan_type.storagebits = (_bits) > 16 ? 32 : 16, \ + .scan_type.realbits = (_bits), \ +} + +struct ad7625_state { + const struct ad7625_chip_info *info; + struct iio_backend *back; + /* rate of the clock gated by the "clk_gate" PWM */ + u32 ref_clk_rate_hz; + /* PWM burst signal for transferring acquired data to the host */ + struct pwm_device *clk_gate_pwm; + /* + * PWM control signal for initiating data conversion. Analog + * inputs are sampled beginning on this signal's rising edge. + */ + struct pwm_device *cnv_pwm; + /* + * Waveforms containing the last-requested and rounded + * properties for the clk_gate and cnv PWMs + */ + struct pwm_waveform clk_gate_wf; + struct pwm_waveform cnv_wf; + unsigned int vref_mv; + u32 sampling_freq_hz; + /* + * Optional GPIOs for controlling device state. EN0 and EN1 + * determine voltage reference configuration and on/off state. + * EN2 controls the device -3dB bandwidth (and by extension, max + * sample rate). EN3 controls the VCM reference output. EN2 and + * EN3 are only present for the AD796x devices. + */ + struct gpio_desc *en_gpios[4]; + bool can_power_down; + bool can_refin; + bool can_ref_4v096; + /* + * Indicate whether the bandwidth can be narrow (9MHz). + * When true, device sample rate must also be < 2MSPS. + */ + bool can_narrow_bandwidth; + /* Indicate whether the bandwidth can be wide (28MHz). */ + bool can_wide_bandwidth; + bool can_ref_5v; + bool can_snooze; + bool can_test_pattern; + /* Indicate whether there is a REFIN supply connected */ + bool have_refin; +}; + +static const struct ad7625_timing_spec ad7625_timing_spec = { + .conv_high_ns = 40, + .conv_msb_ns = 145, +}; + +static const struct ad7625_timing_spec ad7626_timing_spec = { + .conv_high_ns = 40, + .conv_msb_ns = 80, +}; + +/* + * conv_msb_ns is set to 0 instead of the datasheet maximum of 200ns to + * avoid exceeding the minimum conversion time, i.e. it is effectively + * modulo 200 and offset by a full period. Values greater than or equal + * to the period would be rejected by the PWM API. + */ +static const struct ad7625_timing_spec ad7960_timing_spec = { + .conv_high_ns = 80, + .conv_msb_ns = 0, +}; + +static const struct ad7625_chip_info ad7625_chip_info = { + .name = "ad7625", + .max_sample_freq_hz = 6 * MEGA, + .timing_spec = &ad7625_timing_spec, + .chan_spec = AD7625_CHAN_SPEC(16), + .has_power_down_state = false, + .has_bandwidth_control = false, + .has_internal_vref = true, +}; + +static const struct ad7625_chip_info ad7626_chip_info = { + .name = "ad7626", + .max_sample_freq_hz = 10 * MEGA, + .timing_spec = &ad7626_timing_spec, + .chan_spec = AD7625_CHAN_SPEC(16), + .has_power_down_state = true, + .has_bandwidth_control = false, + .has_internal_vref = true, +}; + +static const struct ad7625_chip_info ad7960_chip_info = { + .name = "ad7960", + .max_sample_freq_hz = 5 * MEGA, + .timing_spec = &ad7960_timing_spec, + .chan_spec = AD7625_CHAN_SPEC(18), + .has_power_down_state = true, + .has_bandwidth_control = true, + .has_internal_vref = false, +}; + +static const struct ad7625_chip_info ad7961_chip_info = { + .name = "ad7961", + .max_sample_freq_hz = 5 * MEGA, + .timing_spec = &ad7960_timing_spec, + .chan_spec = AD7625_CHAN_SPEC(16), + .has_power_down_state = true, + .has_bandwidth_control = true, + .has_internal_vref = false, +}; + +enum ad7960_mode { + AD7960_MODE_POWER_DOWN, + AD7960_MODE_SNOOZE, + AD7960_MODE_NARROW_BANDWIDTH, + AD7960_MODE_WIDE_BANDWIDTH, + AD7960_MODE_TEST_PATTERN, +}; + +static int ad7625_set_sampling_freq(struct ad7625_state *st, u32 freq) +{ + struct pwm_waveform clk_gate_wf = { }, cnv_wf = { }; + unsigned long target; + int ret; + + target = DIV_ROUND_UP(NSEC_PER_SEC, freq); + cnv_wf.period_length_ns = clamp(target, 100UL, 10UL * NSEC_PER_USEC); + + /* + * Use the maximum conversion time t_CNVH from the datasheet as + * the duty_cycle for ref_clk, cnv, and clk_gate + */ + cnv_wf.duty_length_ns = st->info->timing_spec->conv_high_ns; + + ret = pwm_round_waveform_might_sleep(st->cnv_pwm, &cnv_wf); + if (ret) + return ret; + + /* + * Set up the burst signal for transferring data. period and + * offset should mirror the CNV signal + */ + clk_gate_wf.period_length_ns = cnv_wf.period_length_ns; + + clk_gate_wf.duty_length_ns = DIV_ROUND_UP_ULL((u64)NSEC_PER_SEC * + st->info->chan_spec.scan_type.realbits, + st->ref_clk_rate_hz); + + /* max t_MSB from datasheet */ + clk_gate_wf.duty_offset_ns = st->info->timing_spec->conv_msb_ns; + + ret = pwm_round_waveform_might_sleep(st->clk_gate_pwm, &clk_gate_wf); + if (ret) + return ret; + + st->cnv_wf = cnv_wf; + st->clk_gate_wf = clk_gate_wf; + + /* TODO: Add a rounding API for PWMs that can simplify this */ + target = DIV_ROUND_CLOSEST(st->ref_clk_rate_hz, freq); + st->sampling_freq_hz = DIV_ROUND_CLOSEST(st->ref_clk_rate_hz, + target); + + return 0; +} + +static int ad7625_read_raw(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + int *val, int *val2, long info) +{ + struct ad7625_state *st = iio_priv(indio_dev); + + switch (info) { + case IIO_CHAN_INFO_SAMP_FREQ: + *val = st->sampling_freq_hz; + + return IIO_VAL_INT; + + case IIO_CHAN_INFO_SCALE: + *val = st->vref_mv; + *val2 = chan->scan_type.realbits - 1; + + return IIO_VAL_FRACTIONAL_LOG2; + + default: + return -EINVAL; + } +} + +static int ad7625_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, int val2, long info) +{ + struct ad7625_state *st = iio_priv(indio_dev); + int ret; + + switch (info) { + case IIO_CHAN_INFO_SAMP_FREQ: + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; + ret = ad7625_set_sampling_freq(st, val); + iio_device_release_direct(indio_dev); + return ret; + default: + return -EINVAL; + } +} + +static int ad7625_parse_mode(struct device *dev, struct ad7625_state *st, + int num_gpios) +{ + bool en_always_on[4], en_always_off[4]; + bool en_may_be_on[4], en_may_be_off[4]; + char en_gpio_buf[4]; + char always_on_buf[18]; + int i; + + for (i = 0; i < num_gpios; i++) { + snprintf(en_gpio_buf, sizeof(en_gpio_buf), "en%d", i); + snprintf(always_on_buf, sizeof(always_on_buf), + "adi,en%d-always-on", i); + /* Set the device to 0b0000 (power-down mode) by default */ + st->en_gpios[i] = devm_gpiod_get_optional(dev, en_gpio_buf, + GPIOD_OUT_LOW); + if (IS_ERR(st->en_gpios[i])) + return dev_err_probe(dev, PTR_ERR(st->en_gpios[i]), + "failed to get EN%d GPIO\n", i); + + en_always_on[i] = device_property_read_bool(dev, always_on_buf); + if (st->en_gpios[i] && en_always_on[i]) + return dev_err_probe(dev, -EINVAL, + "cannot have adi,en%d-always-on and en%d-gpios\n", i, i); + + en_may_be_off[i] = !en_always_on[i]; + en_may_be_on[i] = en_always_on[i] || st->en_gpios[i]; + en_always_off[i] = !en_always_on[i] && !st->en_gpios[i]; + } + + /* + * Power down is mode 0bXX00, but not all devices have a valid + * power down state. + */ + st->can_power_down = en_may_be_off[1] && en_may_be_off[0] && + st->info->has_power_down_state; + /* + * The REFIN pin can take a 1.2V (AD762x) or 2.048V (AD796x) + * external reference when the mode is 0bXX01. + */ + st->can_refin = en_may_be_off[1] && en_may_be_on[0]; + /* 4.096V can be applied to REF when the EN mode is 0bXX10. */ + st->can_ref_4v096 = en_may_be_on[1] && en_may_be_off[0]; + + /* Avoid AD796x-specific setup if the part is an AD762x */ + if (num_gpios == 2) + return 0; + + /* mode 0b1100 (AD796x) is invalid */ + if (en_always_on[3] && en_always_on[2] && + en_always_off[1] && en_always_off[0]) + return dev_err_probe(dev, -EINVAL, + "EN GPIOs set to invalid mode 0b1100\n"); + /* + * 5V can be applied to the AD796x REF pin when the EN mode is + * the same (0bX001 or 0bX101) as for can_refin, and REFIN is + * 0V. + */ + st->can_ref_5v = st->can_refin; + /* + * Bandwidth (AD796x) is controlled solely by EN2. If it's + * specified and not hard-wired, then we can configure it to + * change the bandwidth between 28MHz and 9MHz. + */ + st->can_narrow_bandwidth = en_may_be_on[2]; + /* Wide bandwidth mode is possible if EN2 can be 0. */ + st->can_wide_bandwidth = en_may_be_off[2]; + /* Snooze mode (AD796x) is 0bXX11 when REFIN = 0V. */ + st->can_snooze = en_may_be_on[1] && en_may_be_on[0]; + /* Test pattern mode (AD796x) is 0b0100. */ + st->can_test_pattern = en_may_be_off[3] && en_may_be_on[2] && + en_may_be_off[1] && en_may_be_off[0]; + + return 0; +} + +/* Set EN1 and EN0 based on reference voltage source */ +static void ad7625_set_en_gpios_for_vref(struct ad7625_state *st, + bool have_refin, int ref_mv) +{ + if (have_refin || ref_mv == 5000) { + gpiod_set_value_cansleep(st->en_gpios[1], 0); + gpiod_set_value_cansleep(st->en_gpios[0], 1); + } else if (ref_mv == 4096) { + gpiod_set_value_cansleep(st->en_gpios[1], 1); + gpiod_set_value_cansleep(st->en_gpios[0], 0); + } else { + /* + * Unreachable by AD796x, since the driver will error if + * neither REF nor REFIN is provided + */ + gpiod_set_value_cansleep(st->en_gpios[1], 1); + gpiod_set_value_cansleep(st->en_gpios[0], 1); + } +} + +static int ad7960_set_mode(struct ad7625_state *st, enum ad7960_mode mode, + bool have_refin, int ref_mv) +{ + switch (mode) { + case AD7960_MODE_POWER_DOWN: + if (!st->can_power_down) + return -EINVAL; + + gpiod_set_value_cansleep(st->en_gpios[2], 0); + gpiod_set_value_cansleep(st->en_gpios[1], 0); + gpiod_set_value_cansleep(st->en_gpios[0], 0); + + return 0; + + case AD7960_MODE_SNOOZE: + if (!st->can_snooze) + return -EINVAL; + + gpiod_set_value_cansleep(st->en_gpios[1], 1); + gpiod_set_value_cansleep(st->en_gpios[0], 1); + + return 0; + + case AD7960_MODE_NARROW_BANDWIDTH: + if (!st->can_narrow_bandwidth) + return -EINVAL; + + gpiod_set_value_cansleep(st->en_gpios[2], 1); + ad7625_set_en_gpios_for_vref(st, have_refin, ref_mv); + + return 0; + + case AD7960_MODE_WIDE_BANDWIDTH: + if (!st->can_wide_bandwidth) + return -EINVAL; + + gpiod_set_value_cansleep(st->en_gpios[2], 0); + ad7625_set_en_gpios_for_vref(st, have_refin, ref_mv); + + return 0; + + case AD7960_MODE_TEST_PATTERN: + if (!st->can_test_pattern) + return -EINVAL; + + gpiod_set_value_cansleep(st->en_gpios[3], 0); + gpiod_set_value_cansleep(st->en_gpios[2], 1); + gpiod_set_value_cansleep(st->en_gpios[1], 0); + gpiod_set_value_cansleep(st->en_gpios[0], 0); + + return 0; + + default: + return -EINVAL; + } +} + +static int ad7625_buffer_preenable(struct iio_dev *indio_dev) +{ + struct ad7625_state *st = iio_priv(indio_dev); + int ret; + + ret = pwm_set_waveform_might_sleep(st->cnv_pwm, &st->cnv_wf, false); + if (ret) + return ret; + + ret = pwm_set_waveform_might_sleep(st->clk_gate_pwm, + &st->clk_gate_wf, false); + if (ret) { + /* Disable cnv PWM if clk_gate setup failed */ + pwm_disable(st->cnv_pwm); + return ret; + } + + return 0; +} + +static int ad7625_buffer_postdisable(struct iio_dev *indio_dev) +{ + struct ad7625_state *st = iio_priv(indio_dev); + + pwm_disable(st->clk_gate_pwm); + pwm_disable(st->cnv_pwm); + + return 0; +} + +static const struct iio_info ad7625_info = { + .read_raw = ad7625_read_raw, + .write_raw = ad7625_write_raw, +}; + +static const struct iio_buffer_setup_ops ad7625_buffer_setup_ops = { + .preenable = &ad7625_buffer_preenable, + .postdisable = &ad7625_buffer_postdisable, +}; + +static int devm_ad7625_pwm_get(struct device *dev, + struct ad7625_state *st) +{ + struct clk *ref_clk; + u32 ref_clk_rate_hz; + + st->cnv_pwm = devm_pwm_get(dev, "cnv"); + if (IS_ERR(st->cnv_pwm)) + return dev_err_probe(dev, PTR_ERR(st->cnv_pwm), + "failed to get cnv pwm\n"); + + /* Preemptively disable the PWM in case it was enabled at boot */ + pwm_disable(st->cnv_pwm); + + st->clk_gate_pwm = devm_pwm_get(dev, "clk_gate"); + if (IS_ERR(st->clk_gate_pwm)) + return dev_err_probe(dev, PTR_ERR(st->clk_gate_pwm), + "failed to get clk_gate pwm\n"); + + /* Preemptively disable the PWM in case it was enabled at boot */ + pwm_disable(st->clk_gate_pwm); + + ref_clk = devm_clk_get_enabled(dev, NULL); + if (IS_ERR(ref_clk)) + return dev_err_probe(dev, PTR_ERR(ref_clk), + "failed to get ref_clk\n"); + + ref_clk_rate_hz = clk_get_rate(ref_clk); + if (!ref_clk_rate_hz) + return dev_err_probe(dev, -EINVAL, + "failed to get ref_clk rate\n"); + + st->ref_clk_rate_hz = ref_clk_rate_hz; + + return 0; +} + +/* + * There are three required input voltages for each device, plus two + * conditionally-optional (depending on part) REF and REFIN voltages + * where their validity depends upon the EN pin configuration. + * + * Power-up info for the device says to bring up vio, then vdd2, then + * vdd1, so list them in that order in the regulator_names array. + * + * The reference voltage source is determined like so: + * - internal reference: neither REF or REFIN is connected (invalid for + * AD796x) + * - internal buffer, external reference: REF not connected, REFIN + * connected + * - external reference: REF connected, REFIN not connected + */ +static int devm_ad7625_regulator_setup(struct device *dev, + struct ad7625_state *st) +{ + static const char * const regulator_names[] = { "vio", "vdd2", "vdd1" }; + int ret, ref_mv; + + ret = devm_regulator_bulk_get_enable(dev, ARRAY_SIZE(regulator_names), + regulator_names); + if (ret) + return ret; + + ret = devm_regulator_get_enable_read_voltage(dev, "ref"); + if (ret < 0 && ret != -ENODEV) + return dev_err_probe(dev, ret, "failed to get REF voltage\n"); + + ref_mv = ret == -ENODEV ? 0 : ret / 1000; + + ret = devm_regulator_get_enable_optional(dev, "refin"); + if (ret < 0 && ret != -ENODEV) + return dev_err_probe(dev, ret, "failed to get REFIN voltage\n"); + + st->have_refin = ret != -ENODEV; + + if (st->have_refin && !st->can_refin) + return dev_err_probe(dev, -EINVAL, + "REFIN provided in unsupported mode\n"); + + if (!st->info->has_internal_vref && !st->have_refin && !ref_mv) + return dev_err_probe(dev, -EINVAL, + "Need either REFIN or REF\n"); + + if (st->have_refin && ref_mv) + return dev_err_probe(dev, -EINVAL, + "cannot have both REFIN and REF supplies\n"); + + if (ref_mv == 4096 && !st->can_ref_4v096) + return dev_err_probe(dev, -EINVAL, + "REF is 4.096V in unsupported mode\n"); + + if (ref_mv == 5000 && !st->can_ref_5v) + return dev_err_probe(dev, -EINVAL, + "REF is 5V in unsupported mode\n"); + + st->vref_mv = ref_mv ?: AD7625_INTERNAL_REF_MV; + + return 0; +} + +static int ad7625_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct iio_dev *indio_dev; + struct ad7625_state *st; + int ret; + u32 default_sample_freq; + + indio_dev = devm_iio_device_alloc(dev, sizeof(*st)); + if (!indio_dev) + return -ENOMEM; + + st = iio_priv(indio_dev); + + st->info = device_get_match_data(dev); + if (!st->info) + return dev_err_probe(dev, -EINVAL, "no chip info\n"); + + if (device_property_read_bool(dev, "adi,no-dco")) + return dev_err_probe(dev, -EINVAL, + "self-clocked mode not supported\n"); + + if (st->info->has_bandwidth_control) + ret = ad7625_parse_mode(dev, st, 4); + else + ret = ad7625_parse_mode(dev, st, 2); + + if (ret) + return ret; + + ret = devm_ad7625_regulator_setup(dev, st); + if (ret) + return ret; + + /* Set the device mode based on detected EN configuration. */ + if (!st->info->has_bandwidth_control) { + ad7625_set_en_gpios_for_vref(st, st->have_refin, st->vref_mv); + } else { + /* + * If neither sampling mode is available, then report an error, + * since the other modes are not useful defaults. + */ + if (st->can_wide_bandwidth) { + ret = ad7960_set_mode(st, AD7960_MODE_WIDE_BANDWIDTH, + st->have_refin, st->vref_mv); + } else if (st->can_narrow_bandwidth) { + ret = ad7960_set_mode(st, AD7960_MODE_NARROW_BANDWIDTH, + st->have_refin, st->vref_mv); + } else { + return dev_err_probe(dev, -EINVAL, + "couldn't set device to wide or narrow bandwidth modes\n"); + } + + if (ret) + return dev_err_probe(dev, -EINVAL, + "failed to set EN pins\n"); + } + + ret = devm_ad7625_pwm_get(dev, st); + if (ret) + return ret; + + indio_dev->channels = &st->info->chan_spec; + indio_dev->num_channels = 1; + indio_dev->name = st->info->name; + indio_dev->info = &ad7625_info; + indio_dev->setup_ops = &ad7625_buffer_setup_ops; + + st->back = devm_iio_backend_get(dev, NULL); + if (IS_ERR(st->back)) + return dev_err_probe(dev, PTR_ERR(st->back), + "failed to get IIO backend\n"); + + ret = devm_iio_backend_request_buffer(dev, st->back, indio_dev); + if (ret) + return ret; + + ret = devm_iio_backend_enable(dev, st->back); + if (ret) + return ret; + + /* + * Set the initial sampling frequency to the maximum, unless the + * AD796x device is limited to narrow bandwidth by EN2 == 1, in + * which case the sampling frequency should be limited to 2MSPS + */ + default_sample_freq = st->info->max_sample_freq_hz; + if (st->info->has_bandwidth_control && !st->can_wide_bandwidth) + default_sample_freq = AD7960_MAX_NBW_FREQ; + + ret = ad7625_set_sampling_freq(st, default_sample_freq); + if (ret) + dev_err_probe(dev, ret, + "failed to set valid sampling frequency\n"); + + return devm_iio_device_register(dev, indio_dev); +} + +static const struct of_device_id ad7625_of_match[] = { + { .compatible = "adi,ad7625", .data = &ad7625_chip_info }, + { .compatible = "adi,ad7626", .data = &ad7626_chip_info }, + { .compatible = "adi,ad7960", .data = &ad7960_chip_info }, + { .compatible = "adi,ad7961", .data = &ad7961_chip_info }, + { } +}; +MODULE_DEVICE_TABLE(of, ad7625_of_match); + +static const struct platform_device_id ad7625_device_ids[] = { + { .name = "ad7625", .driver_data = (kernel_ulong_t)&ad7625_chip_info }, + { .name = "ad7626", .driver_data = (kernel_ulong_t)&ad7626_chip_info }, + { .name = "ad7960", .driver_data = (kernel_ulong_t)&ad7960_chip_info }, + { .name = "ad7961", .driver_data = (kernel_ulong_t)&ad7961_chip_info }, + { } +}; +MODULE_DEVICE_TABLE(platform, ad7625_device_ids); + +static struct platform_driver ad7625_driver = { + .probe = ad7625_probe, + .driver = { + .name = "ad7625", + .of_match_table = ad7625_of_match, + }, + .id_table = ad7625_device_ids, +}; +module_platform_driver(ad7625_driver); + +MODULE_AUTHOR("Trevor Gamblin "); +MODULE_DESCRIPTION("Analog Devices AD7625 ADC"); +MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS("IIO_BACKEND"); diff --git a/drivers/iio/adc/ad7766.c b/drivers/iio/adc/ad7766.c new file mode 100644 index 000000000..92957196d --- /dev/null +++ b/drivers/iio/adc/ad7766.c @@ -0,0 +1,309 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * AD7766/AD7767 SPI ADC driver + * + * Copyright 2016 Analog Devices Inc. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +struct ad7766_chip_info { + unsigned int decimation_factor; +}; + +enum { + AD7766_SUPPLY_AVDD = 0, + AD7766_SUPPLY_DVDD = 1, + AD7766_SUPPLY_VREF = 2, + AD7766_NUM_SUPPLIES = 3 +}; + +struct ad7766 { + const struct ad7766_chip_info *chip_info; + struct spi_device *spi; + struct clk *mclk; + struct gpio_desc *pd_gpio; + struct regulator_bulk_data reg[AD7766_NUM_SUPPLIES]; + + struct iio_trigger *trig; + + struct spi_transfer xfer; + struct spi_message msg; + + /* + * DMA (thus cache coherency maintenance) may require the + * transfer buffers to live in their own cache lines. + * Make the buffer large enough for one 24 bit sample and one 64 bit + * aligned 64 bit timestamp. + */ + unsigned char data[ALIGN(3, sizeof(s64)) + sizeof(s64)] __aligned(IIO_DMA_MINALIGN); +}; + +/* + * AD7766 and AD7767 variations are interface compatible, the main difference is + * analog performance. Both parts will use the same ID. + */ +enum ad7766_device_ids { + ID_AD7766, + ID_AD7766_1, + ID_AD7766_2, +}; + +static irqreturn_t ad7766_trigger_handler(int irq, void *p) +{ + struct iio_poll_func *pf = p; + struct iio_dev *indio_dev = pf->indio_dev; + struct ad7766 *ad7766 = iio_priv(indio_dev); + int ret; + + ret = spi_sync(ad7766->spi, &ad7766->msg); + if (ret < 0) + goto done; + + iio_push_to_buffers_with_timestamp(indio_dev, ad7766->data, + pf->timestamp); +done: + iio_trigger_notify_done(indio_dev->trig); + + return IRQ_HANDLED; +} + +static int ad7766_preenable(struct iio_dev *indio_dev) +{ + struct ad7766 *ad7766 = iio_priv(indio_dev); + int ret; + + ret = regulator_bulk_enable(ARRAY_SIZE(ad7766->reg), ad7766->reg); + if (ret < 0) { + dev_err(&ad7766->spi->dev, "Failed to enable supplies: %d\n", + ret); + return ret; + } + + ret = clk_prepare_enable(ad7766->mclk); + if (ret < 0) { + dev_err(&ad7766->spi->dev, "Failed to enable MCLK: %d\n", ret); + regulator_bulk_disable(ARRAY_SIZE(ad7766->reg), ad7766->reg); + return ret; + } + + gpiod_set_value(ad7766->pd_gpio, 0); + + return 0; +} + +static int ad7766_postdisable(struct iio_dev *indio_dev) +{ + struct ad7766 *ad7766 = iio_priv(indio_dev); + + gpiod_set_value(ad7766->pd_gpio, 1); + + /* + * The PD pin is synchronous to the clock, so give it some time to + * notice the change before we disable the clock. + */ + msleep(20); + + clk_disable_unprepare(ad7766->mclk); + regulator_bulk_disable(ARRAY_SIZE(ad7766->reg), ad7766->reg); + + return 0; +} + +static int ad7766_read_raw(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, int *val, int *val2, long info) +{ + struct ad7766 *ad7766 = iio_priv(indio_dev); + struct regulator *vref = ad7766->reg[AD7766_SUPPLY_VREF].consumer; + int scale_uv; + + switch (info) { + case IIO_CHAN_INFO_SCALE: + scale_uv = regulator_get_voltage(vref); + if (scale_uv < 0) + return scale_uv; + *val = scale_uv / 1000; + *val2 = chan->scan_type.realbits; + return IIO_VAL_FRACTIONAL_LOG2; + case IIO_CHAN_INFO_SAMP_FREQ: + *val = clk_get_rate(ad7766->mclk) / + ad7766->chip_info->decimation_factor; + return IIO_VAL_INT; + } + return -EINVAL; +} + +static const struct iio_chan_spec ad7766_channels[] = { + { + .type = IIO_VOLTAGE, + .indexed = 1, + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), + .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ), + .scan_type = { + .sign = 's', + .realbits = 24, + .storagebits = 32, + .endianness = IIO_BE, + }, + }, + IIO_CHAN_SOFT_TIMESTAMP(1), +}; + +static const struct ad7766_chip_info ad7766_chip_info[] = { + [ID_AD7766] = { + .decimation_factor = 8, + }, + [ID_AD7766_1] = { + .decimation_factor = 16, + }, + [ID_AD7766_2] = { + .decimation_factor = 32, + }, +}; + +static const struct iio_buffer_setup_ops ad7766_buffer_setup_ops = { + .preenable = &ad7766_preenable, + .postdisable = &ad7766_postdisable, +}; + +static const struct iio_info ad7766_info = { + .read_raw = &ad7766_read_raw, +}; + +static int ad7766_set_trigger_state(struct iio_trigger *trig, bool enable) +{ + struct ad7766 *ad7766 = iio_trigger_get_drvdata(trig); + + if (enable) + enable_irq(ad7766->spi->irq); + else + disable_irq(ad7766->spi->irq); + + return 0; +} + +static const struct iio_trigger_ops ad7766_trigger_ops = { + .set_trigger_state = ad7766_set_trigger_state, + .validate_device = iio_trigger_validate_own_device, +}; + +static int ad7766_probe(struct spi_device *spi) +{ + const struct spi_device_id *id = spi_get_device_id(spi); + struct iio_dev *indio_dev; + struct ad7766 *ad7766; + int ret; + + indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*ad7766)); + if (!indio_dev) + return -ENOMEM; + + ad7766 = iio_priv(indio_dev); + ad7766->chip_info = &ad7766_chip_info[id->driver_data]; + + ad7766->mclk = devm_clk_get(&spi->dev, "mclk"); + if (IS_ERR(ad7766->mclk)) + return PTR_ERR(ad7766->mclk); + + ad7766->reg[AD7766_SUPPLY_AVDD].supply = "avdd"; + ad7766->reg[AD7766_SUPPLY_DVDD].supply = "dvdd"; + ad7766->reg[AD7766_SUPPLY_VREF].supply = "vref"; + + ret = devm_regulator_bulk_get(&spi->dev, ARRAY_SIZE(ad7766->reg), + ad7766->reg); + if (ret) + return ret; + + ad7766->pd_gpio = devm_gpiod_get_optional(&spi->dev, "powerdown", + GPIOD_OUT_HIGH); + if (IS_ERR(ad7766->pd_gpio)) + return PTR_ERR(ad7766->pd_gpio); + + indio_dev->name = spi_get_device_id(spi)->name; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = ad7766_channels; + indio_dev->num_channels = ARRAY_SIZE(ad7766_channels); + indio_dev->info = &ad7766_info; + + if (spi->irq > 0) { + ad7766->trig = devm_iio_trigger_alloc(&spi->dev, "%s-dev%d", + indio_dev->name, + iio_device_id(indio_dev)); + if (!ad7766->trig) + return -ENOMEM; + + ad7766->trig->ops = &ad7766_trigger_ops; + iio_trigger_set_drvdata(ad7766->trig, ad7766); + + /* + * The device generates interrupts as long as it is powered up. + * Some platforms might not allow the option to power it down so + * don't enable the interrupt to avoid extra load on the system + */ + ret = devm_request_irq(&spi->dev, spi->irq, iio_trigger_generic_data_rdy_poll, + IRQF_TRIGGER_FALLING | IRQF_NO_AUTOEN | IRQF_NO_THREAD, + dev_name(&spi->dev), + ad7766->trig); + if (ret < 0) + return ret; + + ret = devm_iio_trigger_register(&spi->dev, ad7766->trig); + if (ret) + return ret; + } + + ad7766->spi = spi; + + /* First byte always 0 */ + ad7766->xfer.rx_buf = &ad7766->data[1]; + ad7766->xfer.len = 3; + + spi_message_init(&ad7766->msg); + spi_message_add_tail(&ad7766->xfer, &ad7766->msg); + + ret = devm_iio_triggered_buffer_setup(&spi->dev, indio_dev, + &iio_pollfunc_store_time, &ad7766_trigger_handler, + &ad7766_buffer_setup_ops); + if (ret) + return ret; + + return devm_iio_device_register(&spi->dev, indio_dev); +} + +static const struct spi_device_id ad7766_id[] = { + { .name = "ad7766", .driver_data = ID_AD7766 }, + { .name = "ad7766-1", .driver_data = ID_AD7766_1 }, + { .name = "ad7766-2", .driver_data = ID_AD7766_2 }, + { .name = "ad7767", .driver_data = ID_AD7766 }, + { .name = "ad7767-1", .driver_data = ID_AD7766_1 }, + { .name = "ad7767-2", .driver_data = ID_AD7766_2 }, + { } +}; +MODULE_DEVICE_TABLE(spi, ad7766_id); + +static struct spi_driver ad7766_driver = { + .driver = { + .name = "ad7766", + }, + .probe = ad7766_probe, + .id_table = ad7766_id, +}; +module_spi_driver(ad7766_driver); + +MODULE_AUTHOR("Lars-Peter Clausen "); +MODULE_DESCRIPTION("Analog Devices AD7766 and AD7767 ADCs driver support"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/adc/ad7768-1.c b/drivers/iio/adc/ad7768-1.c new file mode 100644 index 000000000..e9060c1bb --- /dev/null +++ b/drivers/iio/adc/ad7768-1.c @@ -0,0 +1,1951 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Analog Devices AD7768-1 SPI ADC driver + * + * Copyright 2017 Analog Devices Inc. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +/* AD7768 registers definition */ +#define AD7768_REG_CHIP_TYPE 0x3 +#define AD7768_REG_PROD_ID_L 0x4 +#define AD7768_REG_PROD_ID_H 0x5 +#define AD7768_REG_CHIP_GRADE 0x6 +#define AD7768_REG_SCRATCH_PAD 0x0A +#define AD7768_REG_VENDOR_L 0x0C +#define AD7768_REG_VENDOR_H 0x0D +#define AD7768_REG_INTERFACE_FORMAT 0x14 +#define AD7768_REG_POWER_CLOCK 0x15 +#define AD7768_REG_ANALOG 0x16 +#define AD7768_REG_ANALOG2 0x17 +#define AD7768_REG_CONVERSION 0x18 +#define AD7768_REG_DIGITAL_FILTER 0x19 +#define AD7768_REG_SINC3_DEC_RATE_MSB 0x1A +#define AD7768_REG_SINC3_DEC_RATE_LSB 0x1B +#define AD7768_REG_DUTY_CYCLE_RATIO 0x1C +#define AD7768_REG_SYNC_RESET 0x1D +#define AD7768_REG_GPIO_CONTROL 0x1E +#define AD7768_REG_GPIO_WRITE 0x1F +#define AD7768_REG_GPIO_READ 0x20 +#define AD7768_REG_OFFSET_HI 0x21 +#define AD7768_REG_OFFSET_MID 0x22 +#define AD7768_REG_OFFSET_LO 0x23 +#define AD7768_REG_GAIN_HI 0x24 +#define AD7768_REG_GAIN_MID 0x25 +#define AD7768_REG_GAIN_LO 0x26 +#define AD7768_REG_SPI_DIAG_ENABLE 0x28 +#define AD7768_REG_ADC_DIAG_ENABLE 0x29 +#define AD7768_REG_DIG_DIAG_ENABLE 0x2A +#define AD7768_REG24_ADC_DATA 0x2C +#define AD7768_REG_MASTER_STATUS 0x2D +#define AD7768_REG_SPI_DIAG_STATUS 0x2E +#define AD7768_REG_ADC_DIAG_STATUS 0x2F +#define AD7768_REG_DIG_DIAG_STATUS 0x30 +#define AD7768_REG_MCLK_COUNTER 0x31 +#define AD7768_REG_COEFF_CONTROL 0x32 +#define AD7768_REG24_COEFF_DATA 0x33 +#define AD7768_REG_ACCESS_KEY 0x34 + +/* AD7768_REG_POWER_CLOCK */ +#define AD7768_PWR_MCLK_DIV_MSK GENMASK(5, 4) +#define AD7768_PWR_MCLK_DIV(x) FIELD_PREP(AD7768_PWR_MCLK_DIV_MSK, x) +#define AD7768_PWR_PWRMODE_MSK GENMASK(1, 0) +#define AD7768_PWR_PWRMODE(x) FIELD_PREP(AD7768_PWR_PWRMODE_MSK, x) + +/* AD7768_REG_DIGITAL_FILTER */ +#define AD7768_DIG_FIL_EN_60HZ_REJ BIT(7) +#define AD7768_DIG_FIL_FIL_MSK GENMASK(6, 4) +#define AD7768_DIG_FIL_FIL(x) FIELD_PREP(AD7768_DIG_FIL_FIL_MSK, x) +#define AD7768_DIG_FIL_DEC_MSK GENMASK(2, 0) +#define AD7768_DIG_FIL_DEC_RATE(x) FIELD_PREP(AD7768_DIG_FIL_DEC_MSK, x) + +/* AD7768_REG_CONVERSION */ +#define AD7768_CONV_MODE_MSK GENMASK(2, 0) +#define AD7768_CONV_MODE(x) FIELD_PREP(AD7768_CONV_MODE_MSK, x) + +/* AD7768_REG_ANALOG2 */ +#define AD7768_REG_ANALOG2_VCM_MSK GENMASK(2, 0) +#define AD7768_REG_ANALOG2_VCM(x) FIELD_PREP(AD7768_REG_ANALOG2_VCM_MSK, (x)) + +/* AD7768_REG_GPIO_CONTROL */ +#define AD7768_GPIO_UNIVERSAL_EN BIT(7) +#define AD7768_GPIO_CONTROL_MSK GENMASK(3, 0) + +/* AD7768_REG_GPIO_WRITE */ +#define AD7768_GPIO_WRITE_MSK GENMASK(3, 0) + +/* AD7768_REG_GPIO_READ */ +#define AD7768_GPIO_READ_MSK GENMASK(3, 0) + +#define AD7768_VCM_OFF 0x07 + +#define ADAQ776X_GAIN_MAX_NANO (128 * NANO) +#define ADAQ776X_MAX_GAIN_MODES 8 + +#define AD7768_TRIGGER_SOURCE_SYNC_IDX 0 + +#define AD7768_MAX_CHANNELS 1 + +#define ADAQ7768_PGA_PINS 3 + +enum ad7768_conv_mode { + AD7768_CONTINUOUS, + AD7768_ONE_SHOT, + AD7768_SINGLE, + AD7768_PERIODIC, + AD7768_STANDBY +}; + +enum ad7768_pwrmode { + AD7768_ECO_MODE = 0, + AD7768_MED_MODE = 2, + AD7768_FAST_MODE = 3 +}; + +enum ad7768_mclk_div { + AD7768_MCLK_DIV_16, + AD7768_MCLK_DIV_8, + AD7768_MCLK_DIV_4, + AD7768_MCLK_DIV_2 +}; + +enum ad7768_filter_type { + AD7768_FILTER_SINC5, + AD7768_FILTER_SINC3, + AD7768_FILTER_WIDEBAND, + AD7768_FILTER_SINC3_REJ60, +}; + +enum ad7768_filter_regval { + AD7768_FILTER_REGVAL_SINC5 = 0, + AD7768_FILTER_REGVAL_SINC5_X8 = 1, + AD7768_FILTER_REGVAL_SINC5_X16 = 2, + AD7768_FILTER_REGVAL_SINC3 = 3, + AD7768_FILTER_REGVAL_WIDEBAND = 4, + AD7768_FILTER_REGVAL_SINC3_REJ60 = 11, +}; + +enum ad7768_scan_type { + AD7768_SCAN_TYPE_NORMAL, + AD7768_SCAN_TYPE_HIGH_SPEED, + AD7768_SCAN_TYPE_OFFLOAD_NORMAL, + AD7768_SCAN_TYPE_OFFLOAD_HIGH_SPEED, +}; + +enum { + AD7768_PGA_GAIN_0, + AD7768_PGA_GAIN_1, + AD7768_PGA_GAIN_2, + AD7768_PGA_GAIN_3, + AD7768_PGA_GAIN_4, + AD7768_PGA_GAIN_5, + AD7768_PGA_GAIN_6, + AD7768_PGA_GAIN_7, +}; + +enum { + AD7768_AAF_IN1, + AD7768_AAF_IN2, + AD7768_AAF_IN3, +}; + +/* PGA and AAF gains in V/V */ +static const int adaq7768_gains[] = { + [AD7768_PGA_GAIN_0] = 325, /* 0.325 */ + [AD7768_PGA_GAIN_1] = 650, /* 0.650 */ + [AD7768_PGA_GAIN_2] = 1300, /* 1.300 */ + [AD7768_PGA_GAIN_3] = 2600, /* 2.600 */ + [AD7768_PGA_GAIN_4] = 5200, /* 5.200 */ + [AD7768_PGA_GAIN_5] = 10400, /* 10.400 */ + [AD7768_PGA_GAIN_6] = 20800, /* 20.800 */ +}; + +static const int adaq7769_gains[] = { + [AD7768_PGA_GAIN_0] = 1000, /* 1.000 */ + [AD7768_PGA_GAIN_1] = 2000, /* 2.000 */ + [AD7768_PGA_GAIN_2] = 4000, /* 4.000 */ + [AD7768_PGA_GAIN_3] = 8000, /* 8.000 */ + [AD7768_PGA_GAIN_4] = 16000, /* 16.000 */ + [AD7768_PGA_GAIN_5] = 32000, /* 32.000 */ + [AD7768_PGA_GAIN_6] = 64000, /* 64.000 */ + [AD7768_PGA_GAIN_7] = 128000, /* 128.000 */ +}; + +static const int ad7768_aaf_gains_bp[] = { + [AD7768_AAF_IN1] = 10000, /* 1.000 */ + [AD7768_AAF_IN2] = 3640, /* 0.364 */ + [AD7768_AAF_IN3] = 1430, /* 0.143 */ +}; + +/* -3dB cutoff frequency multipliers (relative to ODR) for each filter type. */ +static const int ad7768_filter_3db_odr_multiplier[] = { + [AD7768_FILTER_SINC5] = 204, /* 0.204 */ + [AD7768_FILTER_SINC3] = 262, /* 0.2617 */ + [AD7768_FILTER_SINC3_REJ60] = 262, /* 0.2617 */ + [AD7768_FILTER_WIDEBAND] = 433, /* 0.433 */ +}; + +static const int ad7768_mclk_div_rates[] = { + 16, 8, 4, 2, +}; + +static const int ad7768_dec_rate_values[8] = { + 8, 16, 32, 64, 128, 256, 512, 1024, +}; + +/* Decimation rate range for sinc3 filter */ +static const int ad7768_sinc3_dec_rate_range[3] = { + 32, 32, 163840, +}; + +/* + * The AD7768-1 supports three primary filter types: + * Sinc5, Sinc3, and Wideband. + * However, the filter register values can also encode additional parameters + * such as decimation rates and 60Hz rejection. This utility array separates + * the filter type from these parameters. + */ +static const int ad7768_filter_regval_to_type[] = { + [AD7768_FILTER_REGVAL_SINC5] = AD7768_FILTER_SINC5, + [AD7768_FILTER_REGVAL_SINC5_X8] = AD7768_FILTER_SINC5, + [AD7768_FILTER_REGVAL_SINC5_X16] = AD7768_FILTER_SINC5, + [AD7768_FILTER_REGVAL_SINC3] = AD7768_FILTER_SINC3, + [AD7768_FILTER_REGVAL_WIDEBAND] = AD7768_FILTER_WIDEBAND, + [AD7768_FILTER_REGVAL_SINC3_REJ60] = AD7768_FILTER_SINC3_REJ60, +}; + +static const char * const ad7768_filter_enum[] = { + [AD7768_FILTER_SINC5] = "sinc5", + [AD7768_FILTER_SINC3] = "sinc3", + [AD7768_FILTER_WIDEBAND] = "wideband", + [AD7768_FILTER_SINC3_REJ60] = "sinc3+rej60", +}; + +static const struct iio_scan_type ad7768_scan_type[] = { + [AD7768_SCAN_TYPE_NORMAL] = { + .sign = 's', + .realbits = 24, + .storagebits = 32, + .shift = 8, + .endianness = IIO_BE, + }, + [AD7768_SCAN_TYPE_HIGH_SPEED] = { + .sign = 's', + .realbits = 16, + .storagebits = 16, + .endianness = IIO_BE, + }, + [AD7768_SCAN_TYPE_OFFLOAD_NORMAL] = { + .sign = 's', + .realbits = 24, + .storagebits = 32, + .endianness = IIO_CPU, + }, + [AD7768_SCAN_TYPE_OFFLOAD_HIGH_SPEED] = { + .sign = 's', + .realbits = 16, + .storagebits = 32, + .endianness = IIO_CPU, + }, +}; + +struct ad7768_chip_info { + const char *name; + const struct iio_chan_spec *channel_spec; + int num_channels; + const int *pga_gains; + int num_pga_modes; + int default_pga_mode; + int pgia_mode2pin_offset; + bool has_pga; + bool has_variable_aaf; + bool has_vcm_regulator; +}; + +struct ad7768_state { + struct spi_device *spi; + struct spi_offload *offload; + struct spi_offload_trigger *offload_trigger; + struct regmap *regmap; + struct regmap *regmap24; + int vref_uv; + struct regulator_dev *vcm_rdev; + unsigned int vcm_output_sel; + struct clk *mclk; + unsigned int mclk_freq; + unsigned int oversampling_ratio; + enum ad7768_filter_type filter_type; + unsigned int samp_freq; + unsigned int samp_freq_avail[ARRAY_SIZE(ad7768_mclk_div_rates)]; + unsigned int samp_freq_avail_len; + unsigned int pga_gain_mode; + unsigned int aaf_gain; + int scale_tbl[ADAQ776X_MAX_GAIN_MODES][2]; + struct completion completion; + struct iio_trigger *trig; + struct gpio_descs *pga_gpios; + struct gpio_desc *gpio_sync_in; + struct gpio_desc *gpio_reset; + const char *labels[AD7768_MAX_CHANNELS]; + struct gpio_chip gpiochip; + struct spi_transfer offload_xfer; + struct spi_message offload_msg; + const struct ad7768_chip_info *chip; + bool en_spi_sync; + struct mutex pga_lock; /* protect device internal state (PGA) */ + /* + * DMA (thus cache coherency maintenance) may require the + * transfer buffers to live in their own cache lines. + */ + union { + struct { + __be32 chan; + aligned_s64 timestamp; + } scan; + __be32 d32; + u8 d8[2]; + } data __aligned(IIO_DMA_MINALIGN); +}; + +static const struct regmap_range ad7768_regmap_rd_ranges[] = { + regmap_reg_range(AD7768_REG_CHIP_TYPE, AD7768_REG_CHIP_GRADE), + regmap_reg_range(AD7768_REG_SCRATCH_PAD, AD7768_REG_SCRATCH_PAD), + regmap_reg_range(AD7768_REG_VENDOR_L, AD7768_REG_VENDOR_H), + regmap_reg_range(AD7768_REG_INTERFACE_FORMAT, AD7768_REG_GAIN_LO), + regmap_reg_range(AD7768_REG_SPI_DIAG_ENABLE, AD7768_REG_DIG_DIAG_ENABLE), + regmap_reg_range(AD7768_REG_MASTER_STATUS, AD7768_REG_COEFF_CONTROL), + regmap_reg_range(AD7768_REG_ACCESS_KEY, AD7768_REG_ACCESS_KEY), +}; + +static const struct regmap_access_table ad7768_regmap_rd_table = { + .yes_ranges = ad7768_regmap_rd_ranges, + .n_yes_ranges = ARRAY_SIZE(ad7768_regmap_rd_ranges), +}; + +static const struct regmap_range ad7768_regmap_wr_ranges[] = { + regmap_reg_range(AD7768_REG_SCRATCH_PAD, AD7768_REG_SCRATCH_PAD), + regmap_reg_range(AD7768_REG_INTERFACE_FORMAT, AD7768_REG_GPIO_WRITE), + regmap_reg_range(AD7768_REG_OFFSET_HI, AD7768_REG_GAIN_LO), + regmap_reg_range(AD7768_REG_SPI_DIAG_ENABLE, AD7768_REG_DIG_DIAG_ENABLE), + regmap_reg_range(AD7768_REG_SPI_DIAG_STATUS, AD7768_REG_SPI_DIAG_STATUS), + regmap_reg_range(AD7768_REG_COEFF_CONTROL, AD7768_REG_COEFF_CONTROL), + regmap_reg_range(AD7768_REG_ACCESS_KEY, AD7768_REG_ACCESS_KEY), +}; + +static const struct regmap_access_table ad7768_regmap_wr_table = { + .yes_ranges = ad7768_regmap_wr_ranges, + .n_yes_ranges = ARRAY_SIZE(ad7768_regmap_wr_ranges), +}; + +static const struct regmap_config ad7768_regmap_config = { + .name = "ad7768-1-8", + .reg_bits = 8, + .val_bits = 8, + .read_flag_mask = BIT(6), + .rd_table = &ad7768_regmap_rd_table, + .wr_table = &ad7768_regmap_wr_table, + .max_register = AD7768_REG_ACCESS_KEY, + .use_single_write = true, + .use_single_read = true, +}; + +static const struct regmap_range ad7768_regmap24_rd_ranges[] = { + regmap_reg_range(AD7768_REG24_ADC_DATA, AD7768_REG24_ADC_DATA), + regmap_reg_range(AD7768_REG24_COEFF_DATA, AD7768_REG24_COEFF_DATA), +}; + +static const struct regmap_access_table ad7768_regmap24_rd_table = { + .yes_ranges = ad7768_regmap24_rd_ranges, + .n_yes_ranges = ARRAY_SIZE(ad7768_regmap24_rd_ranges), +}; + +static const struct regmap_range ad7768_regmap24_wr_ranges[] = { + regmap_reg_range(AD7768_REG24_COEFF_DATA, AD7768_REG24_COEFF_DATA), +}; + +static const struct regmap_access_table ad7768_regmap24_wr_table = { + .yes_ranges = ad7768_regmap24_wr_ranges, + .n_yes_ranges = ARRAY_SIZE(ad7768_regmap24_wr_ranges), +}; + +static const struct regmap_config ad7768_regmap24_config = { + .name = "ad7768-1-24", + .reg_bits = 8, + .val_bits = 24, + .read_flag_mask = BIT(6), + .rd_table = &ad7768_regmap24_rd_table, + .wr_table = &ad7768_regmap24_wr_table, + .max_register = AD7768_REG24_COEFF_DATA, +}; + +static int ad7768_send_sync_pulse(struct ad7768_state *st) +{ + if (st->en_spi_sync) + return regmap_write(st->regmap, AD7768_REG_SYNC_RESET, 0x00); + + /* + * The datasheet specifies a minimum SYNC_IN pulse width of 1.5 × Tmclk, + * where Tmclk is the MCLK period. The supported MCLK frequencies range + * from 0.6 MHz to 17 MHz, which corresponds to a minimum SYNC_IN pulse + * width of approximately 2.5 µs in the worst-case scenario (0.6 MHz). + * + * Add a delay to ensure the pulse width is always sufficient to + * trigger synchronization. + */ + gpiod_set_value_cansleep(st->gpio_sync_in, 1); + fsleep(3); + gpiod_set_value_cansleep(st->gpio_sync_in, 0); + + return 0; +} + +static void ad7768_fill_samp_freq_tbl(struct ad7768_state *st) +{ + unsigned int i, samp_freq_avail, freq_filtered; + unsigned int len = 0; + + freq_filtered = DIV_ROUND_CLOSEST(st->mclk_freq, st->oversampling_ratio); + for (i = 0; i < ARRAY_SIZE(ad7768_mclk_div_rates); i++) { + samp_freq_avail = DIV_ROUND_CLOSEST(freq_filtered, ad7768_mclk_div_rates[i]); + /* Sampling frequency cannot be lower than the minimum of 50 SPS */ + if (samp_freq_avail < 50) + continue; + + st->samp_freq_avail[len++] = samp_freq_avail; + } + + st->samp_freq_avail_len = len; +} + +static int ad7768_set_mclk_div(struct ad7768_state *st, unsigned int mclk_div) +{ + unsigned int mclk_div_value; + + mclk_div_value = AD7768_PWR_MCLK_DIV(mclk_div); + /* + * Set power mode based on mclk_div value. + * ECO_MODE is only recommended for MCLK_DIV = 16. + */ + mclk_div_value |= mclk_div > AD7768_MCLK_DIV_16 ? + AD7768_PWR_PWRMODE(AD7768_FAST_MODE) : + AD7768_PWR_PWRMODE(AD7768_ECO_MODE); + + return regmap_update_bits(st->regmap, AD7768_REG_POWER_CLOCK, + AD7768_PWR_MCLK_DIV_MSK | AD7768_PWR_PWRMODE_MSK, + mclk_div_value); +} + +static int ad7768_set_mode(struct ad7768_state *st, + enum ad7768_conv_mode mode) +{ + return regmap_update_bits(st->regmap, AD7768_REG_CONVERSION, + AD7768_CONV_MODE_MSK, AD7768_CONV_MODE(mode)); +} + +static int ad7768_scan_direct(struct iio_dev *indio_dev) +{ + struct ad7768_state *st = iio_priv(indio_dev); + int readval, ret; + + reinit_completion(&st->completion); + enable_irq(st->spi->irq); + + ret = wait_for_completion_timeout(&st->completion, + msecs_to_jiffies(1000)); + disable_irq(st->spi->irq); + if (!ret) + return -ETIMEDOUT; + + ret = regmap_read(st->regmap24, AD7768_REG24_ADC_DATA, &readval); + if (ret) + return ret; + + /* + * When the decimation rate is set to x8, the ADC data precision is + * reduced from 24 bits to 16 bits. Since the AD7768_REG_ADC_DATA + * register provides 24-bit data, the precision is reduced by + * right-shifting the read value by 8 bits. + */ + if (st->oversampling_ratio == 8) + readval >>= 8; + + return readval; +} + +static int ad7768_reg_access(struct iio_dev *indio_dev, + unsigned int reg, + unsigned int writeval, + unsigned int *readval) +{ + struct ad7768_state *st = iio_priv(indio_dev); + int ret; + + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; + + ret = -EINVAL; + if (readval) { + if (regmap_check_range_table(st->regmap, reg, &ad7768_regmap_rd_table)) + ret = regmap_read(st->regmap, reg, readval); + + if (regmap_check_range_table(st->regmap24, reg, &ad7768_regmap24_rd_table)) + ret = regmap_read(st->regmap24, reg, readval); + + } else { + if (regmap_check_range_table(st->regmap, reg, &ad7768_regmap_wr_table)) + ret = regmap_write(st->regmap, reg, writeval); + + if (regmap_check_range_table(st->regmap24, reg, &ad7768_regmap24_wr_table)) + ret = regmap_write(st->regmap24, reg, writeval); + + } + + iio_device_release_direct(indio_dev); + + return ret; +} + +static int ad7768_fill_scale_tbl(struct iio_dev *dev) +{ + struct ad7768_state *st = iio_priv(dev); + const struct iio_scan_type *scan_type; + int val, val2, tmp0, tmp1, i; + struct u32_fract fract; + unsigned long n, d; + u64 tmp2; + + scan_type = iio_get_current_scan_type(dev, &dev->channels[0]); + if (IS_ERR(scan_type)) { + dev_err(&st->spi->dev, "Failed to get scan type.\n"); + return PTR_ERR(scan_type); + } + + if (scan_type->sign == 's') + val2 = scan_type->realbits - 1; + else + val2 = scan_type->realbits; + + for (i = 0; i < st->chip->num_pga_modes; i++) { + /* Convert gain to a fraction format */ + fract.numerator = st->chip->pga_gains[i]; + fract.denominator = MILLI; + if (st->chip->has_variable_aaf) { + fract.numerator *= ad7768_aaf_gains_bp[st->aaf_gain]; + fract.denominator *= PERMYRIAD; + } + + rational_best_approximation(fract.numerator, fract.denominator, + INT_MAX, INT_MAX, &n, &d); + + val = mult_frac(st->vref_uv, d, n); + /* Would multiply by NANO here, but value is already in milli */ + tmp2 = ((u64)val * MICRO) >> val2; + tmp0 = div_u64_rem(tmp2, NANO, &tmp1); + st->scale_tbl[i][0] = tmp0; /* Integer part */ + st->scale_tbl[i][1] = abs(tmp1); /* Fractional part */ + } + + return 0; +} + +static int ad7768_set_sinc3_dec_rate(struct ad7768_state *st, + unsigned int dec_rate) +{ + unsigned int max_dec_rate; + u8 dec_rate_reg[2]; + u16 regval; + int ret; + + /* + * Maximum dec_rate is limited by the MCLK_DIV value and by the ODR. + * The edge case is for MCLK_DIV = 2, ODR = 50 SPS. + * max_dec_rate <= MCLK / (2 * 50) + */ + max_dec_rate = st->mclk_freq / 100; + dec_rate = clamp(dec_rate, 32, max_dec_rate); + /* + * Calculate the equivalent value to sinc3 decimation ratio + * to be written on the SINC3_DEC_RATE register: + * Value = (DEC_RATE / 32) - 1 + */ + dec_rate = DIV_ROUND_UP(dec_rate, 32) - 1; + + /* + * The SINC3_DEC_RATE value is a 13-bit value split across two + * registers: MSB [12:8] and LSB [7:0]. Prepare the 13-bit value using + * FIELD_PREP() and store it with the right endianness in dec_rate_reg. + */ + regval = FIELD_PREP(GENMASK(12, 0), dec_rate); + put_unaligned_be16(regval, dec_rate_reg); + ret = regmap_bulk_write(st->regmap, AD7768_REG_SINC3_DEC_RATE_MSB, + dec_rate_reg, 2); + if (ret) + return ret; + + st->oversampling_ratio = (dec_rate + 1) * 32; + + return 0; +} + +static int ad7768_configure_dig_fil(struct iio_dev *dev, + enum ad7768_filter_type filter_type, + unsigned int dec_rate) +{ + struct ad7768_state *st = iio_priv(dev); + unsigned int dec_rate_idx, dig_filter_regval; + int ret; + + switch (filter_type) { + case AD7768_FILTER_SINC3: + dig_filter_regval = AD7768_DIG_FIL_FIL(AD7768_FILTER_REGVAL_SINC3); + break; + case AD7768_FILTER_SINC3_REJ60: + dig_filter_regval = AD7768_DIG_FIL_FIL(AD7768_FILTER_REGVAL_SINC3) | + AD7768_DIG_FIL_EN_60HZ_REJ; + break; + case AD7768_FILTER_WIDEBAND: + /* Skip decimations 8 and 16, not supported by the wideband filter */ + dec_rate_idx = find_closest(dec_rate, &ad7768_dec_rate_values[2], + ARRAY_SIZE(ad7768_dec_rate_values) - 2); + dig_filter_regval = AD7768_DIG_FIL_FIL(AD7768_FILTER_REGVAL_WIDEBAND) | + AD7768_DIG_FIL_DEC_RATE(dec_rate_idx); + /* Correct the index offset */ + dec_rate_idx += 2; + break; + case AD7768_FILTER_SINC5: + dec_rate_idx = find_closest(dec_rate, ad7768_dec_rate_values, + ARRAY_SIZE(ad7768_dec_rate_values)); + + /* + * Decimations 8 (idx 0) and 16 (idx 1) are set in the + * FILTER[6:4] field. The other decimations are set in the + * DEC_RATE[2:0] field, and the idx needs to be offsetted by two. + */ + if (dec_rate_idx == 0) + dig_filter_regval = AD7768_DIG_FIL_FIL(AD7768_FILTER_REGVAL_SINC5_X8); + else if (dec_rate_idx == 1) + dig_filter_regval = AD7768_DIG_FIL_FIL(AD7768_FILTER_REGVAL_SINC5_X16); + else + dig_filter_regval = AD7768_DIG_FIL_FIL(AD7768_FILTER_REGVAL_SINC5) | + AD7768_DIG_FIL_DEC_RATE(dec_rate_idx - 2); + break; + } + + ret = regmap_write(st->regmap, AD7768_REG_DIGITAL_FILTER, dig_filter_regval); + if (ret) + return ret; + + st->filter_type = filter_type; + /* + * The decimation for SINC3 filters are configured in different + * registers. + */ + if (filter_type == AD7768_FILTER_SINC3 || + filter_type == AD7768_FILTER_SINC3_REJ60) { + ret = ad7768_set_sinc3_dec_rate(st, dec_rate); + if (ret) + return ret; + } else { + st->oversampling_ratio = ad7768_dec_rate_values[dec_rate_idx]; + } + + /* Update scale table: scale values vary according to the precision */ + ret = ad7768_fill_scale_tbl(dev); + if (ret) + return ret; + + ad7768_fill_samp_freq_tbl(st); + + /* A sync-in pulse is required after every configuration change */ + return ad7768_send_sync_pulse(st); +} + +static int ad7768_setup_pga(struct device *dev, struct ad7768_state *st) +{ + st->pga_gpios = devm_gpiod_get_array(dev, "pga", GPIOD_OUT_LOW); + if (IS_ERR(st->pga_gpios)) + return dev_err_probe(dev, PTR_ERR(st->pga_gpios), + "Failed to get PGA gpios.\n"); + + if (st->pga_gpios->ndescs != ADAQ7768_PGA_PINS) + return dev_err_probe(dev, -EINVAL, + "Expected %d GPIOs for PGA control.\n", + ADAQ7768_PGA_PINS); + return 0; +} + +static int ad7768_calc_pga_gain(struct ad7768_state *st, int gain_int, + int gain_fract, int precision) +{ + u64 gain_nano; + u32 tmp; + + gain_nano = gain_int * NANO + gain_fract; + gain_nano = clamp(gain_nano, 0, ADAQ776X_GAIN_MAX_NANO); + tmp = DIV_ROUND_CLOSEST_ULL(gain_nano << precision, NANO); + gain_nano = DIV_ROUND_CLOSEST(st->vref_uv, tmp); + if (st->chip->has_variable_aaf) + gain_nano = DIV_ROUND_CLOSEST_ULL(gain_nano * PERMYRIAD, + ad7768_aaf_gains_bp[st->aaf_gain]); + + return find_closest(gain_nano, st->chip->pga_gains, + (int)st->chip->num_pga_modes); +} + +static int ad7768_set_pga_gain(struct ad7768_state *st, + int gain_mode) +{ + int pgia_pins_value = abs(gain_mode - st->chip->pgia_mode2pin_offset); + DECLARE_BITMAP(bitmap, ADAQ7768_PGA_PINS) = { }; + int ret; + + guard(mutex)(&st->pga_lock); + + bitmap_write(bitmap, pgia_pins_value, 0, ADAQ7768_PGA_PINS); + ret = gpiod_multi_set_value_cansleep(st->pga_gpios, bitmap); + if (ret) + return ret; + + st->pga_gain_mode = gain_mode; + + return 0; +} + +static int ad7768_gpio_direction_input(struct gpio_chip *chip, unsigned int offset) +{ + struct iio_dev *indio_dev = gpiochip_get_data(chip); + struct ad7768_state *st = iio_priv(indio_dev); + int ret; + + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; + + ret = regmap_clear_bits(st->regmap, AD7768_REG_GPIO_CONTROL, + BIT(offset)); + iio_device_release_direct(indio_dev); + + return ret; +} + +static int ad7768_gpio_direction_output(struct gpio_chip *chip, + unsigned int offset, int value) +{ + struct iio_dev *indio_dev = gpiochip_get_data(chip); + struct ad7768_state *st = iio_priv(indio_dev); + int ret; + + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; + + ret = regmap_set_bits(st->regmap, AD7768_REG_GPIO_CONTROL, + BIT(offset)); + iio_device_release_direct(indio_dev); + + return ret; +} + +static int ad7768_gpio_get(struct gpio_chip *chip, unsigned int offset) +{ + struct iio_dev *indio_dev = gpiochip_get_data(chip); + struct ad7768_state *st = iio_priv(indio_dev); + unsigned int val; + int ret; + + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; + + ret = regmap_read(st->regmap, AD7768_REG_GPIO_CONTROL, &val); + if (ret) + goto err_release; + + /* + * If the GPIO is configured as an output, read the current value from + * AD7768_REG_GPIO_WRITE. Otherwise, read the input value from + * AD7768_REG_GPIO_READ. + */ + if (val & BIT(offset)) + ret = regmap_read(st->regmap, AD7768_REG_GPIO_WRITE, &val); + else + ret = regmap_read(st->regmap, AD7768_REG_GPIO_READ, &val); + if (ret) + goto err_release; + + ret = !!(val & BIT(offset)); +err_release: + iio_device_release_direct(indio_dev); + + return ret; +} + +static int ad7768_gpio_set(struct gpio_chip *chip, unsigned int offset, int value) +{ + struct iio_dev *indio_dev = gpiochip_get_data(chip); + struct ad7768_state *st = iio_priv(indio_dev); + unsigned int val; + int ret; + + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; + + ret = regmap_read(st->regmap, AD7768_REG_GPIO_CONTROL, &val); + if (ret) + goto err_release; + + if (val & BIT(offset)) + ret = regmap_assign_bits(st->regmap, AD7768_REG_GPIO_WRITE, + BIT(offset), value); + +err_release: + iio_device_release_direct(indio_dev); + + return ret; +} + +static int ad7768_gpio_init(struct iio_dev *indio_dev) +{ + struct ad7768_state *st = iio_priv(indio_dev); + int ret; + + ret = regmap_write(st->regmap, AD7768_REG_GPIO_CONTROL, + AD7768_GPIO_UNIVERSAL_EN); + if (ret) + return ret; + + st->gpiochip = (struct gpio_chip) { + .label = "ad7768_1_gpios", + .base = -1, + .ngpio = 4, + .parent = &st->spi->dev, + .can_sleep = true, + .direction_input = ad7768_gpio_direction_input, + .direction_output = ad7768_gpio_direction_output, + .get = ad7768_gpio_get, + .set = ad7768_gpio_set, + .owner = THIS_MODULE, + }; + + return devm_gpiochip_add_data(&st->spi->dev, &st->gpiochip, indio_dev); +} + +static int ad7768_set_freq(struct ad7768_state *st, + unsigned int freq) +{ + unsigned int idx, mclk_div; + int ret; + + freq = clamp(freq, 50, 1024000); + + mclk_div = DIV_ROUND_CLOSEST(st->mclk_freq, freq * st->oversampling_ratio); + /* Find the closest match for the desired sampling frequency */ + idx = find_closest_descending(mclk_div, ad7768_mclk_div_rates, + ARRAY_SIZE(ad7768_mclk_div_rates)); + /* Set both the mclk_div and pwrmode */ + ret = ad7768_set_mclk_div(st, idx); + if (ret) + return ret; + + st->samp_freq = DIV_ROUND_CLOSEST(st->mclk_freq, + ad7768_mclk_div_rates[idx] * st->oversampling_ratio); + + /* A sync-in pulse is required after every configuration change */ + return ad7768_send_sync_pulse(st); +} + +static int ad7768_set_filter_type_attr(struct iio_dev *dev, + const struct iio_chan_spec *chan, + unsigned int filter) +{ + struct ad7768_state *st = iio_priv(dev); + int ret; + + ret = ad7768_configure_dig_fil(dev, filter, st->oversampling_ratio); + if (ret) + return ret; + + /* Update sampling frequency */ + return ad7768_set_freq(st, st->samp_freq); +} + +static int ad7768_get_filter_type_attr(struct iio_dev *dev, + const struct iio_chan_spec *chan) +{ + struct ad7768_state *st = iio_priv(dev); + int ret; + unsigned int mode, mask; + + ret = regmap_read(st->regmap, AD7768_REG_DIGITAL_FILTER, &mode); + if (ret) + return ret; + + mask = AD7768_DIG_FIL_EN_60HZ_REJ | AD7768_DIG_FIL_FIL_MSK; + /* From the register value, get the corresponding filter type */ + return ad7768_filter_regval_to_type[FIELD_GET(mask, mode)]; +} + +static int ad7768_update_dec_rate(struct iio_dev *dev, unsigned int dec_rate) +{ + struct ad7768_state *st = iio_priv(dev); + int ret; + + ret = ad7768_configure_dig_fil(dev, st->filter_type, dec_rate); + if (ret) + return ret; + + /* Update sampling frequency */ + return ad7768_set_freq(st, st->samp_freq); +} + +static const struct iio_enum ad7768_filter_type_iio_enum = { + .items = ad7768_filter_enum, + .num_items = ARRAY_SIZE(ad7768_filter_enum), + .set = ad7768_set_filter_type_attr, + .get = ad7768_get_filter_type_attr, +}; + +static const struct iio_chan_spec_ext_info ad7768_ext_info[] = { + IIO_ENUM("filter_type", IIO_SHARED_BY_ALL, &ad7768_filter_type_iio_enum), + IIO_ENUM_AVAILABLE("filter_type", IIO_SHARED_BY_ALL, &ad7768_filter_type_iio_enum), + { } +}; + +#define AD7768_CHAN(_idx, _msk_avail) \ +{ \ + .type = IIO_VOLTAGE, \ + .info_mask_separate_available = _msk_avail, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | \ + BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY) | \ + BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \ + .info_mask_shared_by_type_available = BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \ + .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ), \ + .info_mask_shared_by_all_available = BIT(IIO_CHAN_INFO_SAMP_FREQ), \ + .ext_info = ad7768_ext_info, \ + .indexed = 1, \ + .channel = _idx, \ + .scan_index = _idx, \ + .has_ext_scan_type = 1, \ + .ext_scan_type = ad7768_scan_type, \ + .num_ext_scan_type = ARRAY_SIZE(ad7768_scan_type), \ +} + +static const struct iio_chan_spec ad7768_channels[] = { + AD7768_CHAN(0, 0), +}; + +static const struct iio_chan_spec adaq776x_channels[] = { + AD7768_CHAN(0, BIT(IIO_CHAN_INFO_SCALE)), +}; + +static int ad7768_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long info) +{ + struct ad7768_state *st = iio_priv(indio_dev); + const struct iio_scan_type *scan_type; + int ret, temp; + + scan_type = iio_get_current_scan_type(indio_dev, chan); + if (IS_ERR(scan_type)) + return PTR_ERR(scan_type); + + switch (info) { + case IIO_CHAN_INFO_RAW: + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; + + ret = ad7768_scan_direct(indio_dev); + + iio_device_release_direct(indio_dev); + if (ret < 0) + return ret; + *val = sign_extend32(ret, scan_type->realbits - 1); + + return IIO_VAL_INT; + + case IIO_CHAN_INFO_SCALE: + if (st->chip->has_pga) { + guard(mutex)(&st->pga_lock); + + *val = st->scale_tbl[st->pga_gain_mode][0]; + *val2 = st->scale_tbl[st->pga_gain_mode][1]; + return IIO_VAL_INT_PLUS_NANO; + } + + temp = (st->vref_uv * 2) / 1000; + if (st->chip->has_variable_aaf) + temp = (temp * PERMYRIAD) / ad7768_aaf_gains_bp[st->aaf_gain]; + + *val = temp; + *val2 = scan_type->realbits; + + return IIO_VAL_FRACTIONAL_LOG2; + + case IIO_CHAN_INFO_SAMP_FREQ: + *val = st->samp_freq; + + return IIO_VAL_INT; + + case IIO_CHAN_INFO_OVERSAMPLING_RATIO: + *val = st->oversampling_ratio; + + return IIO_VAL_INT; + + case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY: + temp = st->samp_freq * ad7768_filter_3db_odr_multiplier[st->filter_type]; + *val = DIV_ROUND_CLOSEST(temp, MILLI); + + return IIO_VAL_INT; + } + + return -EINVAL; +} + +static int ad7768_read_avail(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + const int **vals, int *type, int *length, + long info) +{ + struct ad7768_state *st = iio_priv(indio_dev); + unsigned int shift; + + switch (info) { + case IIO_CHAN_INFO_OVERSAMPLING_RATIO: + /* + * Sinc3 filter allows a wider range of OSR values, so show + * the available values in range format. + */ + if (st->filter_type == AD7768_FILTER_SINC3 || + st->filter_type == AD7768_FILTER_SINC3_REJ60) { + *vals = (int *)ad7768_sinc3_dec_rate_range; + *type = IIO_VAL_INT; + return IIO_AVAIL_RANGE; + } + + shift = st->filter_type == AD7768_FILTER_SINC5 ? 0 : 2; + *vals = (int *)&ad7768_dec_rate_values[shift]; + *length = ARRAY_SIZE(ad7768_dec_rate_values) - shift; + *type = IIO_VAL_INT; + return IIO_AVAIL_LIST; + case IIO_CHAN_INFO_SAMP_FREQ: + *vals = (int *)st->samp_freq_avail; + *length = st->samp_freq_avail_len; + *type = IIO_VAL_INT; + return IIO_AVAIL_LIST; + case IIO_CHAN_INFO_SCALE: + *vals = (int *)st->scale_tbl; + *length = st->chip->num_pga_modes * 2; + *type = IIO_VAL_INT_PLUS_NANO; + return IIO_AVAIL_LIST; + default: + return -EINVAL; + } +} + +static int ad7768_write_raw_get_fmt(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, long mask) +{ + switch (mask) { + case IIO_CHAN_INFO_SCALE: + return IIO_VAL_INT_PLUS_NANO; + default: + return IIO_VAL_INT_PLUS_MICRO; + } +} + +static int ad7768_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, int val2, long info) +{ + struct ad7768_state *st = iio_priv(indio_dev); + const struct iio_scan_type *scan_type; + int ret; + + scan_type = iio_get_current_scan_type(indio_dev, chan); + if (IS_ERR(scan_type)) + return PTR_ERR(scan_type); + + switch (info) { + case IIO_CHAN_INFO_SAMP_FREQ: + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; + + ret = ad7768_set_freq(st, val); + iio_device_release_direct(indio_dev); + return ret; + case IIO_CHAN_INFO_OVERSAMPLING_RATIO: + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; + + ret = ad7768_update_dec_rate(indio_dev, val); + iio_device_release_direct(indio_dev); + return ret; + case IIO_CHAN_INFO_SCALE: { + int gain_mode; + + if (!st->chip->has_pga) + return -EOPNOTSUPP; + + if (scan_type->sign == 's') + gain_mode = ad7768_calc_pga_gain(st, val, val2, + scan_type->realbits - 1); + else + gain_mode = ad7768_calc_pga_gain(st, val, val2, + scan_type->realbits); + + return ad7768_set_pga_gain(st, gain_mode); + } + default: + return -EINVAL; + } +} + +static int ad7768_read_label(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, char *label) +{ + struct ad7768_state *st = iio_priv(indio_dev); + + return sysfs_emit(label, "%s\n", st->labels[chan->channel]); +} + +static int ad7768_get_current_scan_type(const struct iio_dev *indio_dev, + const struct iio_chan_spec *chan) +{ + struct ad7768_state *st = iio_priv(indio_dev); + + if (st->offload) + return st->oversampling_ratio == 8 ? + AD7768_SCAN_TYPE_OFFLOAD_HIGH_SPEED : AD7768_SCAN_TYPE_OFFLOAD_NORMAL; + + return st->oversampling_ratio == 8 ? + AD7768_SCAN_TYPE_HIGH_SPEED : AD7768_SCAN_TYPE_NORMAL; +} + +static const struct iio_info ad7768_info = { + .read_raw = &ad7768_read_raw, + .read_avail = &ad7768_read_avail, + .write_raw = &ad7768_write_raw, + .write_raw_get_fmt = &ad7768_write_raw_get_fmt, + .read_label = ad7768_read_label, + .get_current_scan_type = &ad7768_get_current_scan_type, + .debugfs_reg_access = &ad7768_reg_access, +}; + +static struct fwnode_handle * +ad7768_fwnode_find_reference_args(const struct fwnode_handle *fwnode, + const char *name, const char *nargs_prop, + unsigned int nargs, unsigned int index, + struct fwnode_reference_args *args) +{ + int ret; + + ret = fwnode_property_get_reference_args(fwnode, name, nargs_prop, + nargs, index, args); + return ret ? ERR_PTR(ret) : args->fwnode; +} + +static int ad7768_trigger_sources_sync_setup(struct device *dev, + struct fwnode_handle *fwnode, + struct ad7768_state *st) +{ + struct fwnode_reference_args args; + + struct fwnode_handle *ref __free(fwnode_handle) = + ad7768_fwnode_find_reference_args(fwnode, "trigger-sources", + "#trigger-source-cells", 0, + AD7768_TRIGGER_SOURCE_SYNC_IDX, + &args); + if (IS_ERR(ref)) + return PTR_ERR(ref); + + ref = args.fwnode; + /* First, try getting the GPIO trigger source */ + if (fwnode_device_is_compatible(ref, "gpio-trigger")) { + st->gpio_sync_in = devm_fwnode_gpiod_get_index(dev, ref, NULL, 0, + GPIOD_OUT_LOW, + "sync-in"); + return PTR_ERR_OR_ZERO(st->gpio_sync_in); + } + + /* + * TODO: Support the other cases when we have a trigger subsystem + * to reliably handle other types of devices as trigger sources. + * + * For now, return an error message. For self triggering, omit the + * trigger-sources property. + */ + return dev_err_probe(dev, -EOPNOTSUPP, "Invalid synchronization trigger source\n"); +} + +static int ad7768_trigger_sources_get_sync(struct device *dev, + struct ad7768_state *st) +{ + struct fwnode_handle *fwnode = dev_fwnode(dev); + + /* + * The AD7768-1 allows two primary methods for driving the SYNC_IN pin + * to synchronize one or more devices: + * 1. Using an external GPIO. + * 2. Using a SPI command, where the SYNC_OUT pin generates a + * synchronization pulse that drives the SYNC_IN pin. + */ + if (fwnode_property_present(fwnode, "trigger-sources")) + return ad7768_trigger_sources_sync_setup(dev, fwnode, st); + + /* + * In the absence of trigger-sources property, enable self + * synchronization over SPI (SYNC_OUT). + */ + st->en_spi_sync = true; + + return 0; +} + +static int ad7768_setup(struct iio_dev *indio_dev) +{ + struct ad7768_state *st = iio_priv(indio_dev); + int ret; + + st->gpio_reset = devm_gpiod_get_optional(&st->spi->dev, "reset", + GPIOD_OUT_HIGH); + if (IS_ERR(st->gpio_reset)) + return PTR_ERR(st->gpio_reset); + + if (st->gpio_reset) { + fsleep(10); + gpiod_set_value_cansleep(st->gpio_reset, 0); + fsleep(200); + } else { + /* + * Two writes to the SPI_RESET[1:0] bits are required to initiate + * a software reset. The bits must first be set to 11, and then + * to 10. When the sequence is detected, the reset occurs. + * See the datasheet, page 70. + */ + ret = regmap_write(st->regmap, AD7768_REG_SYNC_RESET, 0x3); + if (ret) + return ret; + + ret = regmap_write(st->regmap, AD7768_REG_SYNC_RESET, 0x2); + if (ret) + return ret; + } + + ret = ad7768_set_mode(st, AD7768_CONTINUOUS); + if (ret) + return ret; + + /* For backwards compatibility, try the adi,sync-in-gpios property */ + st->gpio_sync_in = devm_gpiod_get_optional(&st->spi->dev, "adi,sync-in", + GPIOD_OUT_LOW); + if (IS_ERR(st->gpio_sync_in)) + return PTR_ERR(st->gpio_sync_in); + + /* + * If the synchronization is not defined by adi,sync-in-gpios, try the + * trigger-sources. + */ + if (!st->gpio_sync_in) { + ret = ad7768_trigger_sources_get_sync(&st->spi->dev, st); + if (ret) + return ret; + } + + /* Only create a Chip GPIO if flagged for it */ + if (device_property_read_bool(&st->spi->dev, "gpio-controller")) { + ret = ad7768_gpio_init(indio_dev); + if (ret) + return ret; + } + + /* + * Set Default Digital Filter configuration: + * SINC5 filter with x32 Decimation rate + */ + ret = ad7768_configure_dig_fil(indio_dev, AD7768_FILTER_SINC5, 32); + if (ret) + return ret; + + /* Set the default sampling frequency to 32000 kSPS */ + return ad7768_set_freq(st, 32000); +} + +static irqreturn_t ad7768_trigger_handler(int irq, void *p) +{ + struct iio_poll_func *pf = p; + struct iio_dev *indio_dev = pf->indio_dev; + struct ad7768_state *st = iio_priv(indio_dev); + const struct iio_scan_type *scan_type; + int ret; + + scan_type = iio_get_current_scan_type(indio_dev, &indio_dev->channels[0]); + if (IS_ERR(scan_type)) + goto out; + + ret = spi_read(st->spi, &st->data.scan.chan, + BITS_TO_BYTES(scan_type->realbits)); + if (ret < 0) + goto out; + + iio_push_to_buffers_with_ts(indio_dev, &st->data.scan, + sizeof(st->data.scan), + iio_get_time_ns(indio_dev)); + +out: + iio_trigger_notify_done(indio_dev->trig); + + return IRQ_HANDLED; +} + +static irqreturn_t ad7768_interrupt(int irq, void *dev_id) +{ + struct iio_dev *indio_dev = dev_id; + struct ad7768_state *st = iio_priv(indio_dev); + + if (iio_buffer_enabled(indio_dev)) + iio_trigger_poll(st->trig); + else + complete(&st->completion); + + return IRQ_HANDLED; +}; + +static int ad7768_buffer_postenable(struct iio_dev *indio_dev) +{ + struct ad7768_state *st = iio_priv(indio_dev); + + /* + * Write a 1 to the LSB of the INTERFACE_FORMAT register to enter + * continuous read mode. Subsequent data reads do not require an + * initial 8-bit write to query the ADC_DATA register. + */ + return regmap_write(st->regmap, AD7768_REG_INTERFACE_FORMAT, 0x01); +} + +static int ad7768_buffer_predisable(struct iio_dev *indio_dev) +{ + struct ad7768_state *st = iio_priv(indio_dev); + unsigned int unused; + + /* + * To exit continuous read mode, perform a single read of the ADC_DATA + * reg (0x2C), which allows further configuration of the device. + */ + return regmap_read(st->regmap24, AD7768_REG24_ADC_DATA, &unused); +} + +static const struct iio_buffer_setup_ops ad7768_buffer_ops = { + .postenable = &ad7768_buffer_postenable, + .predisable = &ad7768_buffer_predisable, +}; + +static int ad7768_offload_buffer_postenable(struct iio_dev *indio_dev) +{ + struct ad7768_state *st = iio_priv(indio_dev); + struct spi_offload_trigger_config config = { + .type = SPI_OFFLOAD_TRIGGER_DATA_READY, + }; + const struct iio_scan_type *scan_type; + unsigned int unused; + int ret; + + scan_type = iio_get_current_scan_type(indio_dev, &indio_dev->channels[0]); + if (IS_ERR(scan_type)) + return PTR_ERR(scan_type); + + st->offload_xfer.len = spi_bpw_to_bytes(scan_type->realbits); + st->offload_xfer.bits_per_word = scan_type->realbits; + st->offload_xfer.offload_flags = SPI_OFFLOAD_XFER_RX_STREAM; + + spi_message_init_with_transfers(&st->offload_msg, &st->offload_xfer, 1); + st->offload_msg.offload = st->offload; + + ret = spi_optimize_message(st->spi, &st->offload_msg); + if (ret) { + dev_err(&st->spi->dev, "failed to prepare offload, err: %d\n", ret); + return ret; + } + + /* + * Write a 1 to the LSB of the INTERFACE_FORMAT register to enter + * continuous read mode. Subsequent data reads do not require an + * initial 8-bit write to query the ADC_DATA register. + */ + ret = regmap_write(st->regmap, AD7768_REG_INTERFACE_FORMAT, 0x01); + if (ret) + goto err_unoptimize_message; + + ret = spi_offload_trigger_enable(st->offload, st->offload_trigger, + &config); + if (ret) + goto err_exit_continuous_read_mode; + + return 0; + +err_exit_continuous_read_mode: + regmap_read(st->regmap24, AD7768_REG24_ADC_DATA, &unused); + +err_unoptimize_message: + spi_unoptimize_message(&st->offload_msg); + + return ret; +} + +static int ad7768_offload_buffer_predisable(struct iio_dev *indio_dev) +{ + struct ad7768_state *st = iio_priv(indio_dev); + unsigned int unused; + + spi_offload_trigger_disable(st->offload, st->offload_trigger); + spi_unoptimize_message(&st->offload_msg); + + /* + * To exit continuous read mode, perform a single read of the ADC_DATA + * reg (0x2C), which allows further configuration of the device. + */ + return regmap_read(st->regmap24, AD7768_REG24_ADC_DATA, &unused); +} + +static const struct iio_buffer_setup_ops ad7768_offload_buffer_ops = { + .postenable = ad7768_offload_buffer_postenable, + .predisable = ad7768_offload_buffer_predisable, +}; + +static int ad7768_set_trigger_state(struct iio_trigger *trig, bool enable) +{ + struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig); + struct ad7768_state *st = iio_priv(indio_dev); + + if (enable) + enable_irq(st->spi->irq); + else + disable_irq(st->spi->irq); + + return 0; +} + +static const struct iio_trigger_ops ad7768_trigger_ops = { + .validate_device = iio_trigger_validate_own_device, + .set_trigger_state = ad7768_set_trigger_state, +}; + +static int ad7768_set_channel_label(struct iio_dev *indio_dev, + int num_channels) +{ + struct ad7768_state *st = iio_priv(indio_dev); + struct device *device = indio_dev->dev.parent; + const char *label; + int crt_ch = 0; + + device_for_each_child_node_scoped(device, child) { + if (fwnode_property_read_u32(child, "reg", &crt_ch)) + continue; + + if (crt_ch >= num_channels) + continue; + + if (fwnode_property_read_string(child, "label", &label)) + continue; + + st->labels[crt_ch] = label; + } + + return 0; +} + +static int ad7768_triggered_buffer_alloc(struct iio_dev *indio_dev) +{ + struct ad7768_state *st = iio_priv(indio_dev); + int ret; + + st->trig = devm_iio_trigger_alloc(indio_dev->dev.parent, "%s-dev%d", + indio_dev->name, + iio_device_id(indio_dev)); + if (!st->trig) + return -ENOMEM; + + st->trig->ops = &ad7768_trigger_ops; + iio_trigger_set_drvdata(st->trig, indio_dev); + ret = devm_iio_trigger_register(indio_dev->dev.parent, st->trig); + if (ret) + return ret; + + indio_dev->trig = iio_trigger_get(st->trig); + + return devm_iio_triggered_buffer_setup(indio_dev->dev.parent, indio_dev, + &iio_pollfunc_store_time, + &ad7768_trigger_handler, + &ad7768_buffer_ops); +} + +static int ad7768_vcm_enable(struct regulator_dev *rdev) +{ + struct iio_dev *indio_dev = rdev_get_drvdata(rdev); + struct ad7768_state *st = iio_priv(indio_dev); + int ret, regval; + + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; + + /* To enable, set the last selected output */ + regval = AD7768_REG_ANALOG2_VCM(st->vcm_output_sel + 1); + ret = regmap_update_bits(st->regmap, AD7768_REG_ANALOG2, + AD7768_REG_ANALOG2_VCM_MSK, regval); + iio_device_release_direct(indio_dev); + + return ret; +} + +static int ad7768_vcm_disable(struct regulator_dev *rdev) +{ + struct iio_dev *indio_dev = rdev_get_drvdata(rdev); + struct ad7768_state *st = iio_priv(indio_dev); + int ret; + + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; + + ret = regmap_update_bits(st->regmap, AD7768_REG_ANALOG2, + AD7768_REG_ANALOG2_VCM_MSK, AD7768_VCM_OFF); + iio_device_release_direct(indio_dev); + + return ret; +} + +static int ad7768_vcm_is_enabled(struct regulator_dev *rdev) +{ + struct iio_dev *indio_dev = rdev_get_drvdata(rdev); + struct ad7768_state *st = iio_priv(indio_dev); + int ret, val; + + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; + + ret = regmap_read(st->regmap, AD7768_REG_ANALOG2, &val); + iio_device_release_direct(indio_dev); + if (ret) + return ret; + + return FIELD_GET(AD7768_REG_ANALOG2_VCM_MSK, val) != AD7768_VCM_OFF; +} + +static int ad7768_set_voltage_sel(struct regulator_dev *rdev, + unsigned int selector) +{ + unsigned int regval = AD7768_REG_ANALOG2_VCM(selector + 1); + struct iio_dev *indio_dev = rdev_get_drvdata(rdev); + struct ad7768_state *st = iio_priv(indio_dev); + int ret; + + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; + + ret = regmap_update_bits(st->regmap, AD7768_REG_ANALOG2, + AD7768_REG_ANALOG2_VCM_MSK, regval); + iio_device_release_direct(indio_dev); + if (ret) + return ret; + + st->vcm_output_sel = selector; + + return 0; +} + +static int ad7768_get_voltage_sel(struct regulator_dev *rdev) +{ + struct iio_dev *indio_dev = rdev_get_drvdata(rdev); + struct ad7768_state *st = iio_priv(indio_dev); + int ret, val; + + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; + + ret = regmap_read(st->regmap, AD7768_REG_ANALOG2, &val); + iio_device_release_direct(indio_dev); + if (ret) + return ret; + + val = FIELD_GET(AD7768_REG_ANALOG2_VCM_MSK, val); + + return clamp(val, 1, rdev->desc->n_voltages) - 1; +} + +static const struct regulator_ops vcm_regulator_ops = { + .enable = ad7768_vcm_enable, + .disable = ad7768_vcm_disable, + .is_enabled = ad7768_vcm_is_enabled, + .list_voltage = regulator_list_voltage_table, + .set_voltage_sel = ad7768_set_voltage_sel, + .get_voltage_sel = ad7768_get_voltage_sel, +}; + +static const unsigned int vcm_voltage_table[] = { + 2500000, + 2050000, + 1650000, + 1900000, + 1100000, + 900000, +}; + +static const struct regulator_desc vcm_desc = { + .name = "ad7768-1-vcm", + .of_match = "vcm-output", + .regulators_node = "regulators", + .n_voltages = ARRAY_SIZE(vcm_voltage_table), + .volt_table = vcm_voltage_table, + .ops = &vcm_regulator_ops, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, +}; + +static int ad7768_register_vcm_regulator(struct device *dev, + struct ad7768_state *st, + struct iio_dev *indio_dev) +{ + struct regulator_config config = { + .dev = dev, + .driver_data = indio_dev, + }; + int ret; + + /* Disable the regulator before registering it */ + ret = regmap_update_bits(st->regmap, AD7768_REG_ANALOG2, + AD7768_REG_ANALOG2_VCM_MSK, AD7768_VCM_OFF); + if (ret) + return ret; + + st->vcm_rdev = devm_regulator_register(dev, &vcm_desc, &config); + if (IS_ERR(st->vcm_rdev)) + return dev_err_probe(dev, PTR_ERR(st->vcm_rdev), + "failed to register VCM regulator\n"); + + return 0; +} + +static int ad7768_parse_aaf_gain(struct device *dev, struct ad7768_state *st) +{ + u32 val; + int ret; + + ret = device_property_read_u32(dev, "adi,aaf-gain-bp", &val); + if (ret == -EINVAL) { + /* If controllable, use default */ + if (st->chip->has_variable_aaf) + st->aaf_gain = AD7768_AAF_IN1; + return 0; + } + if (ret) + return dev_err_probe(dev, ret, "Failed to get AAF gain value\n"); + + if (!st->chip->has_variable_aaf) + return dev_err_probe(dev, -EOPNOTSUPP, + "AAF gain provided, but not supported for %s\n", st->chip->name); + + switch (val) { + case 10000: + st->aaf_gain = AD7768_AAF_IN1; + break; + case 3640: + st->aaf_gain = AD7768_AAF_IN2; + break; + case 1430: + st->aaf_gain = AD7768_AAF_IN3; + break; + default: + return dev_err_probe(dev, -EINVAL, "Invalid firmware provided AAF gain\n"); + } + + return 0; +} + +static bool ad7768_offload_trigger_match(struct spi_offload_trigger *trigger, + enum spi_offload_trigger_type type, + u64 *args, u32 nargs) +{ + if (type != SPI_OFFLOAD_TRIGGER_DATA_READY) + return false; + + /* Up to 2 args are allowed, but only 1 is used */ + if (nargs == 0 || nargs > 2 || args[0] != AD7768_TRIGGER_SOURCE_DRDY) + return false; + + return true; +} + +static int ad7768_offload_trigger_request(struct spi_offload_trigger *trigger, + enum spi_offload_trigger_type type, + u64 *args, u32 nargs) +{ + /* Should already be validated by match, but just in case */ + if (nargs == 0 || nargs > 2) + return -EINVAL; + + return 0; +} + +static const struct spi_offload_trigger_ops ad7768_offload_trigger_ops = { + .match = ad7768_offload_trigger_match, + .request = ad7768_offload_trigger_request, +}; + +static const struct ad7768_chip_info ad7768_chip_info = { + .name = "ad7768-1", + .channel_spec = ad7768_channels, + .num_channels = ARRAY_SIZE(ad7768_channels), + .has_vcm_regulator = true, +}; + +static const struct ad7768_chip_info adaq7767_chip_info = { + .name = "adaq7767-1", + .channel_spec = ad7768_channels, + .num_channels = ARRAY_SIZE(ad7768_channels), + .has_variable_aaf = true, +}; + +static const struct ad7768_chip_info adaq7768_chip_info = { + .name = "adaq7768-1", + .channel_spec = adaq776x_channels, + .num_channels = ARRAY_SIZE(adaq776x_channels), + .pga_gains = adaq7768_gains, + .default_pga_mode = AD7768_PGA_GAIN_2, + .num_pga_modes = ARRAY_SIZE(adaq7768_gains), + .pgia_mode2pin_offset = 6, + .has_pga = true, +}; + +static const struct ad7768_chip_info adaq7769_chip_info = { + .name = "adaq7769-1", + .channel_spec = adaq776x_channels, + .num_channels = ARRAY_SIZE(adaq776x_channels), + .pga_gains = adaq7769_gains, + .default_pga_mode = AD7768_PGA_GAIN_0, + .num_pga_modes = ARRAY_SIZE(adaq7769_gains), + .pgia_mode2pin_offset = 0, + .has_pga = true, + .has_variable_aaf = true, +}; + +static const struct spi_offload_config ad7768_spi_offload_config = { + .capability_flags = SPI_OFFLOAD_CAP_TRIGGER | SPI_OFFLOAD_CAP_RX_STREAM_DMA, +}; + +static int ad7768_spi_offload_probe(struct iio_dev *indio_dev, + struct ad7768_state *st) +{ + struct device *dev = &st->spi->dev; + struct spi_offload_trigger_info trigger_info = { + .fwnode = dev_fwnode(dev), + .ops = &ad7768_offload_trigger_ops, + .priv = st, + }; + struct dma_chan *rx_dma; + int ret; + + ret = devm_spi_offload_trigger_register(dev, &trigger_info); + if (ret) + return dev_err_probe(dev, ret, "failed to register offload trigger\n"); + + st->offload_trigger = devm_spi_offload_trigger_get(dev, st->offload, + SPI_OFFLOAD_TRIGGER_DATA_READY); + if (IS_ERR(st->offload_trigger)) + return dev_err_probe(dev, PTR_ERR(st->offload_trigger), + "failed to get offload trigger\n"); + + rx_dma = devm_spi_offload_rx_stream_request_dma_chan(dev, st->offload); + if (IS_ERR(rx_dma)) + return dev_err_probe(dev, PTR_ERR(rx_dma), "failed to get offload RX DMA\n"); + + ret = devm_iio_dmaengine_buffer_setup_with_handle(dev, indio_dev, rx_dma, + IIO_BUFFER_DIRECTION_IN); + if (ret) + return dev_err_probe(dev, ret, "failed to setup offload RX DMA\n"); + + indio_dev->setup_ops = &ad7768_offload_buffer_ops; + + return 0; +} + +static int ad7768_probe(struct spi_device *spi) +{ + struct ad7768_state *st; + struct iio_dev *indio_dev; + struct device *dev = &spi->dev; + int ret; + + indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st)); + if (!indio_dev) + return -ENOMEM; + + st = iio_priv(indio_dev); + /* + * Datasheet recommends SDI line to be kept high when data is not being + * clocked out of the controller and the spi clock is free running, + * to prevent accidental reset. + * Since many controllers do not support the SPI_MOSI_IDLE_HIGH flag + * yet, only request the MOSI idle state to enable if the controller + * supports it. + */ + if (spi->controller->mode_bits & SPI_MOSI_IDLE_HIGH) { + spi->mode |= SPI_MOSI_IDLE_HIGH; + ret = spi_setup(spi); + if (ret < 0) + return ret; + } + + st->chip = spi_get_device_match_data(spi); + st->spi = spi; + + st->regmap = devm_regmap_init_spi(spi, &ad7768_regmap_config); + if (IS_ERR(st->regmap)) + return dev_err_probe(&spi->dev, PTR_ERR(st->regmap), + "Failed to initialize regmap"); + + st->regmap24 = devm_regmap_init_spi(spi, &ad7768_regmap24_config); + if (IS_ERR(st->regmap24)) + return dev_err_probe(&spi->dev, PTR_ERR(st->regmap24), + "Failed to initialize regmap24"); + + ret = devm_regulator_get_enable_read_voltage(&spi->dev, "vref"); + if (ret < 0) + return dev_err_probe(&spi->dev, ret, + "Failed to get VREF voltage\n"); + st->vref_uv = ret; + + st->mclk = devm_clk_get_enabled(&spi->dev, "mclk"); + if (IS_ERR(st->mclk)) + return PTR_ERR(st->mclk); + + st->mclk_freq = clk_get_rate(st->mclk); + + indio_dev->channels = st->chip->channel_spec; + indio_dev->num_channels = st->chip->num_channels; + indio_dev->name = st->chip->name; + indio_dev->info = &ad7768_info; + indio_dev->modes = INDIO_DIRECT_MODE; + + /* Register VCM output regulator */ + if (st->chip->has_vcm_regulator) { + ret = ad7768_register_vcm_regulator(&spi->dev, st, indio_dev); + if (ret) + return ret; + } + + ret = ad7768_parse_aaf_gain(&spi->dev, st); + if (ret) + return ret; + + ret = ad7768_setup(indio_dev); + if (ret < 0) + return dev_err_probe(dev, ret, "AD7768 setup failed\n"); + + init_completion(&st->completion); + ret = devm_mutex_init(&spi->dev, &st->pga_lock); + if (ret) + return ret; + + if (st->chip->has_pga) { + ret = ad7768_setup_pga(&spi->dev, st); + if (ret) + return ret; + + ret = ad7768_set_pga_gain(st, st->chip->default_pga_mode); + if (ret) + return ret; + } + + ret = ad7768_set_channel_label(indio_dev, st->chip->num_channels); + if (ret) + return ret; + + ret = devm_request_irq(&spi->dev, spi->irq, &ad7768_interrupt, + IRQF_TRIGGER_RISING | IRQF_NO_THREAD | IRQF_NO_AUTOEN, + indio_dev->name, indio_dev); + if (ret) + return ret; + + st->offload = devm_spi_offload_get(dev, spi, &ad7768_spi_offload_config); + ret = PTR_ERR_OR_ZERO(st->offload); + if (ret == -ENODEV) { + /* If not using SPI offload, fall back to low speed usage */ + ret = ad7768_triggered_buffer_alloc(indio_dev); + if (ret) + return ret; + } else if (ret) { + return dev_err_probe(dev, ret, "failed to get SPI offload\n"); + } else { + ret = ad7768_spi_offload_probe(indio_dev, st); + if (ret) + return ret; + } + + return devm_iio_device_register(&spi->dev, indio_dev); +} + +static const struct spi_device_id ad7768_id_table[] = { + { .name = "ad7768-1", .driver_data = (kernel_ulong_t)&ad7768_chip_info }, + { .name = "adaq7767-1", .driver_data = (kernel_ulong_t)&adaq7767_chip_info }, + { .name = "adaq7768-1", .driver_data = (kernel_ulong_t)&adaq7768_chip_info }, + { .name = "adaq7769-1", .driver_data = (kernel_ulong_t)&adaq7769_chip_info }, + { } +}; +MODULE_DEVICE_TABLE(spi, ad7768_id_table); + +static const struct of_device_id ad7768_of_match[] = { + { .compatible = "adi,ad7768-1", .data = &ad7768_chip_info }, + { .compatible = "adi,adaq7767-1", .data = &adaq7767_chip_info }, + { .compatible = "adi,adaq7768-1", .data = &adaq7768_chip_info }, + { .compatible = "adi,adaq7769-1", .data = &adaq7769_chip_info }, + { } +}; +MODULE_DEVICE_TABLE(of, ad7768_of_match); + +static struct spi_driver ad7768_driver = { + .driver = { + .name = "ad7768-1", + .of_match_table = ad7768_of_match, + }, + .probe = ad7768_probe, + .id_table = ad7768_id_table, +}; +module_spi_driver(ad7768_driver); + +MODULE_AUTHOR("Stefan Popa "); +MODULE_DESCRIPTION("Analog Devices AD7768-1 ADC driver"); +MODULE_LICENSE("GPL v2"); +MODULE_IMPORT_NS("IIO_DMAENGINE_BUFFER"); diff --git a/drivers/iio/adc/ad7779.c b/drivers/iio/adc/ad7779.c new file mode 100644 index 000000000..79fb38d0f --- /dev/null +++ b/drivers/iio/adc/ad7779.c @@ -0,0 +1,1045 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * AD7770, AD7771, AD7779 ADC + * + * Copyright 2023-2024 Analog Devices Inc. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#define AD7779_SPI_READ_CMD BIT(7) + +#define AD7779_DISABLE_SD BIT(7) + +#define AD7779_REG_CH_DISABLE 0x08 +#define AD7779_REG_CH_SYNC_OFFSET(ch) (0x09 + (ch)) +#define AD7779_REG_CH_CONFIG(ch) (0x00 + (ch)) +#define AD7779_REG_GENERAL_USER_CONFIG_1 0x11 +#define AD7779_REG_GENERAL_USER_CONFIG_2 0x12 +#define AD7779_REG_GENERAL_USER_CONFIG_3 0x13 +#define AD7779_REG_DOUT_FORMAT 0x14 +#define AD7779_REG_ADC_MUX_CONFIG 0x15 +#define AD7779_REG_GPIO_CONFIG 0x17 +#define AD7779_REG_BUFFER_CONFIG_1 0x19 +#define AD7779_REG_GLOBAL_MUX_CONFIG 0x16 +#define AD7779_REG_BUFFER_CONFIG_2 0x1A +#define AD7779_REG_GPIO_DATA 0x18 +#define AD7779_REG_CH_OFFSET_UPPER_BYTE(ch) (0x1C + (ch) * 6) +#define AD7779_REG_CH_OFFSET_LOWER_BYTE(ch) (0x1E + (ch) * 6) +#define AD7779_REG_CH_GAIN_UPPER_BYTE(ch) (0x1F + (ch) * 6) +#define AD7779_REG_CH_OFFSET_MID_BYTE(ch) (0x1D + (ch) * 6) +#define AD7779_REG_CH_GAIN_MID_BYTE(ch) (0x20 + (ch) * 6) +#define AD7779_REG_CH_ERR_REG(ch) (0x4C + (ch)) +#define AD7779_REG_CH0_1_SAT_ERR 0x54 +#define AD7779_REG_CH_GAIN_LOWER_BYTE(ch) (0x21 + (ch) * 6) +#define AD7779_REG_CH2_3_SAT_ERR 0x55 +#define AD7779_REG_CH4_5_SAT_ERR 0x56 +#define AD7779_REG_CH6_7_SAT_ERR 0x57 +#define AD7779_REG_CHX_ERR_REG_EN 0x58 +#define AD7779_REG_GEN_ERR_REG_1 0x59 +#define AD7779_REG_GEN_ERR_REG_1_EN 0x5A +#define AD7779_REG_GEN_ERR_REG_2 0x5B +#define AD7779_REG_GEN_ERR_REG_2_EN 0x5C +#define AD7779_REG_STATUS_REG_1 0x5D +#define AD7779_REG_STATUS_REG_2 0x5E +#define AD7779_REG_STATUS_REG_3 0x5F +#define AD7779_REG_SRC_N_MSB 0x60 +#define AD7779_REG_SRC_N_LSB 0x61 +#define AD7779_REG_SRC_IF_MSB 0x62 +#define AD7779_REG_SRC_IF_LSB 0x63 +#define AD7779_REG_SRC_UPDATE 0x64 + +#define AD7779_FILTER_MSK BIT(6) +#define AD7779_MOD_POWERMODE_MSK BIT(6) +#define AD7779_MOD_PDB_REFOUT_MSK BIT(4) +#define AD7779_MOD_SPI_EN_MSK BIT(4) +#define AD7779_USRMOD_INIT_MSK GENMASK(6, 4) + +/* AD7779_REG_DOUT_FORMAT */ +#define AD7779_DOUT_FORMAT_MSK GENMASK(7, 6) +#define AD7779_DOUT_HEADER_FORMAT BIT(5) +#define AD7779_DCLK_CLK_DIV_MSK GENMASK(3, 1) + +#define AD7779_REFMUX_CTRL_MSK GENMASK(7, 6) +#define AD7779_SPI_CRC_EN_MSK BIT(0) + +#define AD7779_MAXCLK_LOWPOWER (4096 * HZ_PER_KHZ) +#define AD7779_NUM_CHANNELS 8 +#define AD7779_RESET_BUF_SIZE 8 +#define AD7779_CHAN_DATA_SIZE 4 + +#define AD7779_LOWPOWER_DIV 512 +#define AD7779_HIGHPOWER_DIV 2048 + +#define AD7779_SINC3_MAXFREQ (16 * HZ_PER_KHZ) +#define AD7779_SINC5_MAXFREQ (128 * HZ_PER_KHZ) + +#define AD7779_DEFAULT_SAMPLING_FREQ (8 * HZ_PER_KHZ) +#define AD7779_DEFAULT_SAMPLING_2LINE (4 * HZ_PER_KHZ) +#define AD7779_DEFAULT_SAMPLING_1LINE (2 * HZ_PER_KHZ) + +#define AD7779_SPIMODE_MAX_SAMP_FREQ (16 * HZ_PER_KHZ) + +#define GAIN_REL 0x555555 +#define AD7779_FREQ_MSB_MSK GENMASK(15, 8) +#define AD7779_FREQ_LSB_MSK GENMASK(7, 0) +#define AD7779_UPPER GENMASK(23, 16) +#define AD7779_MID GENMASK(15, 8) +#define AD7779_LOWER GENMASK(7, 0) + +#define AD7779_REG_MSK GENMASK(6, 0) + +#define AD7779_CRC8_POLY 0x07 +DECLARE_CRC8_TABLE(ad7779_crc8_table); + +enum ad7779_filter { + AD7779_SINC3, + AD7779_SINC5, +}; + +enum ad7779_variant { + ad7770, + ad7771, + ad7779, +}; + +enum ad7779_power_mode { + AD7779_LOW_POWER, + AD7779_HIGH_POWER, +}; + +struct ad7779_chip_info { + const char *name; + struct iio_chan_spec const *channels; +}; + +struct ad7779_state { + struct spi_device *spi; + const struct ad7779_chip_info *chip_info; + struct clk *mclk; + struct iio_trigger *trig; + unsigned int sampling_freq; + enum ad7779_filter filter_enabled; + struct iio_backend *back; + /* + * DMA (thus cache coherency maintenance) requires the + * transfer buffers to live in their own cache lines. + */ + struct { + u32 chans[8]; + aligned_s64 timestamp; + } data __aligned(IIO_DMA_MINALIGN); + u32 spidata_tx[8]; + u8 reg_rx_buf[3]; + u8 reg_tx_buf[3]; + u8 reset_buf[8]; +}; + +static const char * const ad7779_filter_type[] = { + [AD7779_SINC3] = "sinc3", + [AD7779_SINC5] = "sinc5", +}; + +static const char * const ad7779_power_supplies[] = { + "avdd1", "avdd2", "avdd4", +}; + +static int ad7779_spi_read(struct ad7779_state *st, u8 reg, u8 *rbuf) +{ + int ret; + u8 crc_buf[2]; + u8 exp_crc; + struct spi_transfer t = { + .tx_buf = st->reg_tx_buf, + .rx_buf = st->reg_rx_buf, + }; + + st->reg_tx_buf[0] = AD7779_SPI_READ_CMD | FIELD_GET(AD7779_REG_MSK, reg); + st->reg_tx_buf[1] = 0; + + if (reg == AD7779_REG_GEN_ERR_REG_1_EN) { + t.len = 2; + } else { + t.len = 3; + st->reg_tx_buf[2] = crc8(ad7779_crc8_table, st->reg_tx_buf, + t.len - 1, 0); + } + + ret = spi_sync_transfer(st->spi, &t, 1); + if (ret) + return ret; + + crc_buf[0] = AD7779_SPI_READ_CMD | FIELD_GET(AD7779_REG_MSK, reg); + crc_buf[1] = st->reg_rx_buf[1]; + exp_crc = crc8(ad7779_crc8_table, crc_buf, ARRAY_SIZE(crc_buf), 0); + if (reg != AD7779_REG_GEN_ERR_REG_1_EN && exp_crc != st->reg_rx_buf[2]) { + dev_err(&st->spi->dev, "Bad CRC %x, expected %x", + st->reg_rx_buf[2], exp_crc); + return -EINVAL; + } + *rbuf = st->reg_rx_buf[1]; + + return 0; +} + +static int ad7779_spi_write(struct ad7779_state *st, u8 reg, u8 val) +{ + u8 length = 3; + + st->reg_tx_buf[0] = FIELD_GET(AD7779_REG_MSK, reg); + st->reg_tx_buf[1] = val; + if (reg == AD7779_REG_GEN_ERR_REG_1_EN) + length = 2; + else + st->reg_tx_buf[2] = crc8(ad7779_crc8_table, st->reg_tx_buf, + length - 1, 0); + + return spi_write(st->spi, st->reg_tx_buf, length); +} + +static int ad7779_spi_write_mask(struct ad7779_state *st, u8 reg, u8 mask, + u8 val) +{ + int ret; + u8 regval, data; + + ret = ad7779_spi_read(st, reg, &data); + if (ret) + return ret; + + regval = (data & ~mask) | (val & mask); + + if (regval == data) + return 0; + + return ad7779_spi_write(st, reg, regval); +} + +static int ad7779_reg_access(struct iio_dev *indio_dev, + unsigned int reg, + unsigned int writeval, + unsigned int *readval) +{ + struct ad7779_state *st = iio_priv(indio_dev); + u8 rval; + int ret; + + if (readval) { + ret = ad7779_spi_read(st, reg, &rval); + *readval = rval; + return ret; + } + + return ad7779_spi_write(st, reg, writeval); +} + +static int ad7779_set_sampling_frequency(struct ad7779_state *st, + unsigned int sampling_freq) +{ + int ret; + unsigned int dec; + unsigned int frac; + unsigned int div; + unsigned int decimal; + unsigned int freq_khz; + + if (st->filter_enabled == AD7779_SINC3 && + sampling_freq > AD7779_SINC3_MAXFREQ) + return -EINVAL; + + if (st->filter_enabled == AD7779_SINC5 && + sampling_freq > AD7779_SINC5_MAXFREQ) + return -EINVAL; + + if (sampling_freq > AD7779_SPIMODE_MAX_SAMP_FREQ) + return -EINVAL; + + div = AD7779_HIGHPOWER_DIV; + + freq_khz = sampling_freq / HZ_PER_KHZ; + dec = div / freq_khz; + frac = div % freq_khz; + + ret = ad7779_spi_write(st, AD7779_REG_SRC_N_MSB, + FIELD_GET(AD7779_FREQ_MSB_MSK, dec)); + if (ret) + return ret; + ret = ad7779_spi_write(st, AD7779_REG_SRC_N_LSB, + FIELD_GET(AD7779_FREQ_LSB_MSK, dec)); + if (ret) + return ret; + + if (frac) { + /* + * In order to obtain the first three decimals of the decimation + * the initial number is multiplied with 10^3 prior to the + * division, then the original division result is subtracted and + * the number is divided by 10^3. + */ + decimal = ((mult_frac(div, KILO, freq_khz) - dec * KILO) << 16) + / KILO; + ret = ad7779_spi_write(st, AD7779_REG_SRC_N_MSB, + FIELD_GET(AD7779_FREQ_MSB_MSK, decimal)); + if (ret) + return ret; + ret = ad7779_spi_write(st, AD7779_REG_SRC_N_LSB, + FIELD_GET(AD7779_FREQ_LSB_MSK, decimal)); + if (ret) + return ret; + } else { + ret = ad7779_spi_write(st, AD7779_REG_SRC_N_MSB, + FIELD_GET(AD7779_FREQ_MSB_MSK, 0x0)); + if (ret) + return ret; + ret = ad7779_spi_write(st, AD7779_REG_SRC_N_LSB, + FIELD_GET(AD7779_FREQ_LSB_MSK, 0x0)); + if (ret) + return ret; + } + ret = ad7779_spi_write(st, AD7779_REG_SRC_UPDATE, BIT(0)); + if (ret) + return ret; + + /* SRC update settling time */ + fsleep(15); + + ret = ad7779_spi_write(st, AD7779_REG_SRC_UPDATE, 0x0); + if (ret) + return ret; + + /* SRC update settling time */ + fsleep(15); + + st->sampling_freq = sampling_freq; + + return 0; +} + +static int ad7779_get_filter(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan) +{ + struct ad7779_state *st = iio_priv(indio_dev); + u8 temp; + int ret; + + ret = ad7779_spi_read(st, AD7779_REG_GENERAL_USER_CONFIG_2, &temp); + if (ret) + return ret; + + return FIELD_GET(AD7779_FILTER_MSK, temp); +} + +static int ad7779_set_filter(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + unsigned int mode) +{ + struct ad7779_state *st = iio_priv(indio_dev); + int ret; + + ret = ad7779_spi_write_mask(st, + AD7779_REG_GENERAL_USER_CONFIG_2, + AD7779_FILTER_MSK, + FIELD_PREP(AD7779_FILTER_MSK, mode)); + if (ret) + return ret; + + ret = ad7779_set_sampling_frequency(st, st->sampling_freq); + if (ret) + return ret; + + st->filter_enabled = mode; + + return 0; +} + +static int ad7779_get_calibscale(struct ad7779_state *st, int channel) +{ + int ret; + u8 calibscale[3]; + + ret = ad7779_spi_read(st, AD7779_REG_CH_GAIN_LOWER_BYTE(channel), + &calibscale[0]); + if (ret) + return ret; + + ret = ad7779_spi_read(st, AD7779_REG_CH_GAIN_MID_BYTE(channel), + &calibscale[1]); + if (ret) + return ret; + + ret = ad7779_spi_read(st, AD7779_REG_CH_GAIN_UPPER_BYTE(channel), + &calibscale[2]); + if (ret) + return ret; + + return get_unaligned_be24(calibscale); +} + +static int ad7779_set_calibscale(struct ad7779_state *st, int channel, int val) +{ + int ret; + unsigned int gain; + u8 gain_bytes[3]; + + /* + * The gain value is relative to 0x555555, which represents a gain of 1 + */ + gain = DIV_ROUND_CLOSEST_ULL((u64)val * 5592405LL, MEGA); + put_unaligned_be24(gain, gain_bytes); + ret = ad7779_spi_write(st, AD7779_REG_CH_GAIN_UPPER_BYTE(channel), + gain_bytes[0]); + if (ret) + return ret; + + ret = ad7779_spi_write(st, AD7779_REG_CH_GAIN_MID_BYTE(channel), + gain_bytes[1]); + if (ret) + return ret; + + return ad7779_spi_write(st, AD7779_REG_CH_GAIN_LOWER_BYTE(channel), + gain_bytes[2]); +} + +static int ad7779_get_calibbias(struct ad7779_state *st, int channel) +{ + int ret; + u8 calibbias[3]; + + ret = ad7779_spi_read(st, AD7779_REG_CH_OFFSET_LOWER_BYTE(channel), + &calibbias[0]); + if (ret) + return ret; + + ret = ad7779_spi_read(st, AD7779_REG_CH_OFFSET_MID_BYTE(channel), + &calibbias[1]); + if (ret) + return ret; + + ret = ad7779_spi_read(st, AD7779_REG_CH_OFFSET_UPPER_BYTE(channel), + &calibbias[2]); + if (ret) + return ret; + + return get_unaligned_be24(calibbias); +} + +static int ad7779_set_calibbias(struct ad7779_state *st, int channel, int val) +{ + int ret; + u8 calibbias[3]; + + put_unaligned_be24(val, calibbias); + ret = ad7779_spi_write(st, AD7779_REG_CH_OFFSET_UPPER_BYTE(channel), + calibbias[0]); + if (ret) + return ret; + + ret = ad7779_spi_write(st, AD7779_REG_CH_OFFSET_MID_BYTE(channel), + calibbias[1]); + if (ret) + return ret; + + return ad7779_spi_write(st, AD7779_REG_CH_OFFSET_LOWER_BYTE(channel), + calibbias[2]); +} + +static int __ad7779_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int *val, + int *val2, long mask) +{ + struct ad7779_state *st = iio_priv(indio_dev); + int ret; + + switch (mask) { + case IIO_CHAN_INFO_CALIBSCALE: + ret = ad7779_get_calibscale(st, chan->channel); + if (ret < 0) + return ret; + *val = ret; + *val2 = GAIN_REL; + return IIO_VAL_FRACTIONAL; + case IIO_CHAN_INFO_CALIBBIAS: + ret = ad7779_get_calibbias(st, chan->channel); + if (ret < 0) + return ret; + *val = ret; + return IIO_VAL_INT; + case IIO_CHAN_INFO_SAMP_FREQ: + *val = st->sampling_freq; + if (*val < 0) + return -EINVAL; + return IIO_VAL_INT; + default: + return -EINVAL; + } +} + +static int ad7779_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int *val, + int *val2, long mask) +{ + int ret; + + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; + + ret = __ad7779_read_raw(indio_dev, chan, val, val2, mask); + iio_device_release_direct(indio_dev); + return ret; +} + +static int __ad7779_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, int val2, + long mask) +{ + struct ad7779_state *st = iio_priv(indio_dev); + + switch (mask) { + case IIO_CHAN_INFO_CALIBSCALE: + return ad7779_set_calibscale(st, chan->channel, val2); + case IIO_CHAN_INFO_CALIBBIAS: + return ad7779_set_calibbias(st, chan->channel, val); + case IIO_CHAN_INFO_SAMP_FREQ: + return ad7779_set_sampling_frequency(st, val); + default: + return -EINVAL; + } +} + +static int ad7779_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int val, int val2, + long mask) +{ + int ret; + + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; + + ret = __ad7779_write_raw(indio_dev, chan, val, val2, mask); + iio_device_release_direct(indio_dev); + return ret; +} + +static int ad7779_buffer_preenable(struct iio_dev *indio_dev) +{ + int ret; + struct ad7779_state *st = iio_priv(indio_dev); + + ret = ad7779_spi_write_mask(st, + AD7779_REG_GENERAL_USER_CONFIG_3, + AD7779_MOD_SPI_EN_MSK, + FIELD_PREP(AD7779_MOD_SPI_EN_MSK, 1)); + if (ret) + return ret; + + /* + * DRDY output cannot be disabled at device level therefore we mask + * the irq at host end. + */ + enable_irq(st->spi->irq); + + return 0; +} + +static int ad7779_buffer_postdisable(struct iio_dev *indio_dev) +{ + struct ad7779_state *st = iio_priv(indio_dev); + + disable_irq(st->spi->irq); + + return ad7779_spi_write(st, AD7779_REG_GENERAL_USER_CONFIG_3, + AD7779_DISABLE_SD); +} + +static irqreturn_t ad7779_trigger_handler(int irq, void *p) +{ + struct iio_poll_func *pf = p; + struct iio_dev *indio_dev = pf->indio_dev; + struct ad7779_state *st = iio_priv(indio_dev); + int ret; + struct spi_transfer t = { + .rx_buf = st->data.chans, + .tx_buf = st->spidata_tx, + .len = AD7779_NUM_CHANNELS * AD7779_CHAN_DATA_SIZE, + }; + + st->spidata_tx[0] = AD7779_SPI_READ_CMD; + ret = spi_sync_transfer(st->spi, &t, 1); + if (ret) { + dev_err(&st->spi->dev, "SPI transfer error in IRQ handler"); + goto exit_handler; + } + + iio_push_to_buffers_with_ts(indio_dev, &st->data, sizeof(st->data), + pf->timestamp); + +exit_handler: + iio_trigger_notify_done(indio_dev->trig); + return IRQ_HANDLED; +} + +static int ad7779_reset(struct iio_dev *indio_dev, struct gpio_desc *reset_gpio) +{ + struct ad7779_state *st = iio_priv(indio_dev); + int ret; + struct spi_transfer t = { + .tx_buf = st->reset_buf, + .len = 8, + }; + + if (reset_gpio) { + gpiod_set_value(reset_gpio, 1); + /* Delay for reset to occur is 225 microseconds */ + fsleep(230); + ret = 0; + } else { + memset(st->reset_buf, 0xff, sizeof(st->reset_buf)); + ret = spi_sync_transfer(st->spi, &t, 1); + if (ret) + return ret; + } + + /* Delay for reset to occur is 225 microseconds */ + fsleep(230); + + return ret; +} + +static int ad7779_update_scan_mode(struct iio_dev *indio_dev, + const unsigned long *scan_mask) +{ + struct ad7779_state *st = iio_priv(indio_dev); + unsigned int c; + int ret; + + for (c = 0; c < AD7779_NUM_CHANNELS; c++) { + if (test_bit(c, scan_mask)) + ret = iio_backend_chan_enable(st->back, c); + else + ret = iio_backend_chan_disable(st->back, c); + if (ret) + return ret; + } + + return 0; +} + +static const struct iio_info ad7779_info = { + .read_raw = ad7779_read_raw, + .write_raw = ad7779_write_raw, + .debugfs_reg_access = &ad7779_reg_access, +}; + +static const struct iio_info ad7779_info_data = { + .read_raw = ad7779_read_raw, + .write_raw = ad7779_write_raw, + .debugfs_reg_access = &ad7779_reg_access, + .update_scan_mode = &ad7779_update_scan_mode, +}; + +static const struct iio_enum ad7779_filter_enum = { + .items = ad7779_filter_type, + .num_items = ARRAY_SIZE(ad7779_filter_type), + .get = ad7779_get_filter, + .set = ad7779_set_filter, +}; + +static const struct iio_chan_spec_ext_info ad7779_ext_filter[] = { + IIO_ENUM("filter_type", IIO_SHARED_BY_ALL, &ad7779_filter_enum), + IIO_ENUM_AVAILABLE("filter_type", IIO_SHARED_BY_ALL, + &ad7779_filter_enum), + { } +}; + +#define AD777x_CHAN_S(index, _ext_info) \ + { \ + .type = IIO_VOLTAGE, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_CALIBSCALE) | \ + BIT(IIO_CHAN_INFO_CALIBBIAS), \ + .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ),\ + .address = (index), \ + .indexed = 1, \ + .channel = (index), \ + .scan_index = (index), \ + .ext_info = (_ext_info), \ + .scan_type = { \ + .sign = 's', \ + .realbits = 24, \ + .storagebits = 32, \ + .endianness = IIO_BE, \ + }, \ + } + +#define AD777x_CHAN_NO_FILTER_S(index) \ + AD777x_CHAN_S(index, NULL) + +#define AD777x_CHAN_FILTER_S(index) \ + AD777x_CHAN_S(index, ad7779_ext_filter) +static const struct iio_chan_spec ad7779_channels[] = { + AD777x_CHAN_NO_FILTER_S(0), + AD777x_CHAN_NO_FILTER_S(1), + AD777x_CHAN_NO_FILTER_S(2), + AD777x_CHAN_NO_FILTER_S(3), + AD777x_CHAN_NO_FILTER_S(4), + AD777x_CHAN_NO_FILTER_S(5), + AD777x_CHAN_NO_FILTER_S(6), + AD777x_CHAN_NO_FILTER_S(7), + IIO_CHAN_SOFT_TIMESTAMP(8), +}; + +static const struct iio_chan_spec ad7779_channels_filter[] = { + AD777x_CHAN_FILTER_S(0), + AD777x_CHAN_FILTER_S(1), + AD777x_CHAN_FILTER_S(2), + AD777x_CHAN_FILTER_S(3), + AD777x_CHAN_FILTER_S(4), + AD777x_CHAN_FILTER_S(5), + AD777x_CHAN_FILTER_S(6), + AD777x_CHAN_FILTER_S(7), + IIO_CHAN_SOFT_TIMESTAMP(8), +}; + +static const struct iio_buffer_setup_ops ad7779_buffer_setup_ops = { + .preenable = ad7779_buffer_preenable, + .postdisable = ad7779_buffer_postdisable, +}; + +static const struct iio_trigger_ops ad7779_trigger_ops = { + .validate_device = iio_trigger_validate_own_device, +}; + +static int ad7779_conf(struct ad7779_state *st, struct gpio_desc *start_gpio) +{ + int ret; + + ret = ad7779_spi_write_mask(st, AD7779_REG_GEN_ERR_REG_1_EN, + AD7779_SPI_CRC_EN_MSK, + FIELD_PREP(AD7779_SPI_CRC_EN_MSK, 1)); + if (ret) + return ret; + + ret = ad7779_spi_write_mask(st, AD7779_REG_GENERAL_USER_CONFIG_1, + AD7779_USRMOD_INIT_MSK, + FIELD_PREP(AD7779_USRMOD_INIT_MSK, 5)); + if (ret) + return ret; + + ret = ad7779_spi_write_mask(st, AD7779_REG_DOUT_FORMAT, + AD7779_DCLK_CLK_DIV_MSK, + FIELD_PREP(AD7779_DCLK_CLK_DIV_MSK, 1)); + if (ret) + return ret; + + ret = ad7779_spi_write_mask(st, AD7779_REG_ADC_MUX_CONFIG, + AD7779_REFMUX_CTRL_MSK, + FIELD_PREP(AD7779_REFMUX_CTRL_MSK, 1)); + if (ret) + return ret; + + ret = ad7779_set_sampling_frequency(st, AD7779_DEFAULT_SAMPLING_FREQ); + if (ret) + return ret; + + gpiod_set_value(start_gpio, 0); + /* Start setup time */ + fsleep(15); + gpiod_set_value(start_gpio, 1); + /* Start setup time */ + fsleep(15); + gpiod_set_value(start_gpio, 0); + /* Start setup time */ + fsleep(15); + + return 0; +} + +static int ad7779_set_data_lines(struct iio_dev *indio_dev, u32 num_lanes) +{ + struct ad7779_state *st = iio_priv(indio_dev); + int ret; + + if (num_lanes != 1 && num_lanes != 2 && num_lanes != 4) + return -EINVAL; + + ret = ad7779_set_sampling_frequency(st, num_lanes * AD7779_DEFAULT_SAMPLING_1LINE); + if (ret) + return ret; + + ret = iio_backend_num_lanes_set(st->back, num_lanes); + if (ret) + return ret; + + return ad7779_spi_write_mask(st, AD7779_REG_DOUT_FORMAT, + AD7779_DOUT_FORMAT_MSK, + FIELD_PREP(AD7779_DOUT_FORMAT_MSK, 2 - ilog2(num_lanes))); +} + +static int ad7779_setup_channels(struct iio_dev *indio_dev, const struct ad7779_state *st) +{ + struct iio_chan_spec *channels; + struct device *dev = &st->spi->dev; + + channels = devm_kmemdup_array(dev, st->chip_info->channels, + ARRAY_SIZE(ad7779_channels), + sizeof(*channels), GFP_KERNEL); + if (!channels) + return -ENOMEM; + + for (unsigned int i = 0; i < ARRAY_SIZE(ad7779_channels); i++) + channels[i].scan_type.endianness = IIO_CPU; + + indio_dev->channels = channels; + indio_dev->num_channels = ARRAY_SIZE(ad7779_channels); + + return 0; +} + +static int ad7779_setup_without_backend(struct ad7779_state *st, struct iio_dev *indio_dev) +{ + int ret; + struct device *dev = &st->spi->dev; + + indio_dev->info = &ad7779_info; + indio_dev->channels = st->chip_info->channels; + indio_dev->num_channels = ARRAY_SIZE(ad7779_channels); + + st->trig = devm_iio_trigger_alloc(dev, "%s-dev%d", indio_dev->name, + iio_device_id(indio_dev)); + if (!st->trig) + return -ENOMEM; + + st->trig->ops = &ad7779_trigger_ops; + + iio_trigger_set_drvdata(st->trig, st); + + ret = devm_request_irq(dev, st->spi->irq, iio_trigger_generic_data_rdy_poll, + IRQF_NO_THREAD | IRQF_NO_AUTOEN, indio_dev->name, + st->trig); + if (ret) + return ret; + + ret = devm_iio_trigger_register(dev, st->trig); + if (ret) + return ret; + + indio_dev->trig = iio_trigger_get(st->trig); + + ret = devm_iio_triggered_buffer_setup(dev, indio_dev, + &iio_pollfunc_store_time, + &ad7779_trigger_handler, + &ad7779_buffer_setup_ops); + if (ret) + return ret; + + return ad7779_spi_write_mask(st, AD7779_REG_DOUT_FORMAT, + AD7779_DCLK_CLK_DIV_MSK, + FIELD_PREP(AD7779_DCLK_CLK_DIV_MSK, 7)); +} + +static int ad7779_setup_backend(struct ad7779_state *st, struct iio_dev *indio_dev) +{ + struct device *dev = &st->spi->dev; + int ret; + u32 num_lanes; + + indio_dev->info = &ad7779_info_data; + + ret = ad7779_setup_channels(indio_dev, st); + if (ret) + return ret; + + st->back = devm_iio_backend_get(dev, NULL); + if (IS_ERR(st->back)) + return dev_err_probe(dev, PTR_ERR(st->back), + "failed to get iio backend"); + + ret = devm_iio_backend_request_buffer(dev, st->back, indio_dev); + if (ret) + return ret; + + ret = devm_iio_backend_enable(dev, st->back); + if (ret) + return ret; + + num_lanes = 4; + ret = device_property_read_u32(dev, "adi,num-lanes", &num_lanes); + if (ret && ret != -EINVAL) + return ret; + + return ad7779_set_data_lines(indio_dev, num_lanes); +} + +static int ad7779_probe(struct spi_device *spi) +{ + struct iio_dev *indio_dev; + struct ad7779_state *st; + struct gpio_desc *reset_gpio, *start_gpio; + struct device *dev = &spi->dev; + int ret = -EINVAL; + + indio_dev = devm_iio_device_alloc(dev, sizeof(*st)); + if (!indio_dev) + return -ENOMEM; + + st = iio_priv(indio_dev); + + ret = devm_regulator_bulk_get_enable(dev, + ARRAY_SIZE(ad7779_power_supplies), + ad7779_power_supplies); + if (ret) + return dev_err_probe(dev, ret, + "failed to get and enable supplies\n"); + + st->mclk = devm_clk_get_enabled(dev, "mclk"); + if (IS_ERR(st->mclk)) + return PTR_ERR(st->mclk); + + reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW); + if (IS_ERR(reset_gpio)) + return PTR_ERR(reset_gpio); + + start_gpio = devm_gpiod_get(dev, "start", GPIOD_OUT_HIGH); + if (IS_ERR(start_gpio)) + return PTR_ERR(start_gpio); + + crc8_populate_msb(ad7779_crc8_table, AD7779_CRC8_POLY); + st->spi = spi; + + st->chip_info = spi_get_device_match_data(spi); + if (!st->chip_info) + return -ENODEV; + + ret = ad7779_reset(indio_dev, reset_gpio); + if (ret) + return ret; + + ret = ad7779_conf(st, start_gpio); + if (ret) + return ret; + + indio_dev->name = st->chip_info->name; + indio_dev->modes = INDIO_DIRECT_MODE; + + if (device_property_present(dev, "io-backends")) + ret = ad7779_setup_backend(st, indio_dev); + else + ret = ad7779_setup_without_backend(st, indio_dev); + if (ret) + return ret; + + return devm_iio_device_register(dev, indio_dev); +} + +static int ad7779_suspend(struct device *dev) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct ad7779_state *st = iio_priv(indio_dev); + + return ad7779_spi_write_mask(st, AD7779_REG_GENERAL_USER_CONFIG_1, + AD7779_MOD_POWERMODE_MSK, + FIELD_PREP(AD7779_MOD_POWERMODE_MSK, + AD7779_LOW_POWER)); +} + +static int ad7779_resume(struct device *dev) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct ad7779_state *st = iio_priv(indio_dev); + + return ad7779_spi_write_mask(st, AD7779_REG_GENERAL_USER_CONFIG_1, + AD7779_MOD_POWERMODE_MSK, + FIELD_PREP(AD7779_MOD_POWERMODE_MSK, + AD7779_HIGH_POWER)); +} + +static DEFINE_SIMPLE_DEV_PM_OPS(ad7779_pm_ops, ad7779_suspend, ad7779_resume); + +static const struct ad7779_chip_info ad7770_chip_info = { + .name = "ad7770", + .channels = ad7779_channels, +}; + +static const struct ad7779_chip_info ad7771_chip_info = { + .name = "ad7771", + .channels = ad7779_channels_filter, +}; + +static const struct ad7779_chip_info ad7779_chip_info = { + .name = "ad7779", + .channels = ad7779_channels, +}; + +static const struct spi_device_id ad7779_id[] = { + { + .name = "ad7770", + .driver_data = (kernel_ulong_t)&ad7770_chip_info, + }, + { + .name = "ad7771", + .driver_data = (kernel_ulong_t)&ad7771_chip_info, + }, + { + .name = "ad7779", + .driver_data = (kernel_ulong_t)&ad7779_chip_info, + }, + { } +}; +MODULE_DEVICE_TABLE(spi, ad7779_id); + +static const struct of_device_id ad7779_of_table[] = { + { + .compatible = "adi,ad7770", + .data = &ad7770_chip_info, + }, + { + .compatible = "adi,ad7771", + .data = &ad7771_chip_info, + }, + { + .compatible = "adi,ad7779", + .data = &ad7779_chip_info, + }, + { } +}; +MODULE_DEVICE_TABLE(of, ad7779_of_table); + +static struct spi_driver ad7779_driver = { + .driver = { + .name = "ad7779", + .pm = pm_sleep_ptr(&ad7779_pm_ops), + .of_match_table = ad7779_of_table, + }, + .probe = ad7779_probe, + .id_table = ad7779_id, +}; +module_spi_driver(ad7779_driver); + +MODULE_AUTHOR("Ramona Alexandra Nechita "); +MODULE_DESCRIPTION("Analog Devices AD7779 ADC"); +MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS("IIO_BACKEND"); diff --git a/drivers/iio/adc/ad7780.c b/drivers/iio/adc/ad7780.c new file mode 100644 index 000000000..1cfad7032 --- /dev/null +++ b/drivers/iio/adc/ad7780.c @@ -0,0 +1,370 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * AD7170/AD7171 and AD7780/AD7781 SPI ADC driver + * + * Copyright 2011 Analog Devices Inc. + * Copyright 2019 Renato Lui Geh + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#define AD7780_RDY BIT(7) +#define AD7780_FILTER BIT(6) +#define AD7780_ERR BIT(5) +#define AD7780_ID1 BIT(4) +#define AD7780_ID0 BIT(3) +#define AD7780_GAIN BIT(2) + +#define AD7170_ID 0 +#define AD7171_ID 1 +#define AD7780_ID 1 +#define AD7781_ID 0 + +#define AD7780_ID_MASK (AD7780_ID0 | AD7780_ID1) + +#define AD7780_PATTERN_GOOD 1 +#define AD7780_PATTERN_MASK GENMASK(1, 0) + +#define AD7170_PATTERN_GOOD 5 +#define AD7170_PATTERN_MASK GENMASK(2, 0) + +#define AD7780_GAIN_MIDPOINT 64 +#define AD7780_FILTER_MIDPOINT 13350 + +static const unsigned int ad778x_gain[2] = { 1, 128 }; +static const unsigned int ad778x_odr_avail[2] = { 10000, 16700 }; + +struct ad7780_chip_info { + struct iio_chan_spec channel; + unsigned int pattern_mask; + unsigned int pattern; + bool is_ad778x; +}; + +struct ad7780_state { + const struct ad7780_chip_info *chip_info; + struct regulator *reg; + struct gpio_desc *powerdown_gpio; + struct gpio_desc *gain_gpio; + struct gpio_desc *filter_gpio; + unsigned int gain; + unsigned int odr; + unsigned int int_vref_mv; + + struct ad_sigma_delta sd; +}; + +enum ad7780_supported_device_ids { + ID_AD7170, + ID_AD7171, + ID_AD7780, + ID_AD7781, +}; + +static struct ad7780_state *ad_sigma_delta_to_ad7780(struct ad_sigma_delta *sd) +{ + return container_of(sd, struct ad7780_state, sd); +} + +static int ad7780_set_mode(struct ad_sigma_delta *sigma_delta, + enum ad_sigma_delta_mode mode) +{ + struct ad7780_state *st = ad_sigma_delta_to_ad7780(sigma_delta); + unsigned int val; + + switch (mode) { + case AD_SD_MODE_SINGLE: + case AD_SD_MODE_CONTINUOUS: + val = 1; + break; + default: + val = 0; + break; + } + + gpiod_set_value(st->powerdown_gpio, val); + + return 0; +} + +static int ad7780_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, + int *val2, + long m) +{ + struct ad7780_state *st = iio_priv(indio_dev); + int voltage_uv; + + switch (m) { + case IIO_CHAN_INFO_RAW: + return ad_sigma_delta_single_conversion(indio_dev, chan, val); + case IIO_CHAN_INFO_SCALE: + voltage_uv = regulator_get_voltage(st->reg); + if (voltage_uv < 0) + return voltage_uv; + voltage_uv /= 1000; + *val = voltage_uv * st->gain; + *val2 = chan->scan_type.realbits - 1; + st->int_vref_mv = voltage_uv; + return IIO_VAL_FRACTIONAL_LOG2; + case IIO_CHAN_INFO_OFFSET: + *val = -(1 << (chan->scan_type.realbits - 1)); + return IIO_VAL_INT; + case IIO_CHAN_INFO_SAMP_FREQ: + *val = st->odr; + return IIO_VAL_INT; + default: + break; + } + + return -EINVAL; +} + +static int ad7780_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, + int val2, + long m) +{ + struct ad7780_state *st = iio_priv(indio_dev); + const struct ad7780_chip_info *chip_info = st->chip_info; + unsigned long long vref; + unsigned int full_scale, gain; + + if (!chip_info->is_ad778x) + return -EINVAL; + + switch (m) { + case IIO_CHAN_INFO_SCALE: + if (val != 0 || val2 == 0) + return -EINVAL; + + vref = st->int_vref_mv * 1000000LL; + full_scale = 1 << (chip_info->channel.scan_type.realbits - 1); + gain = DIV_ROUND_CLOSEST_ULL(vref, full_scale); + gain = DIV_ROUND_CLOSEST(gain, val2); + st->gain = gain; + if (gain < AD7780_GAIN_MIDPOINT) + gain = 0; + else + gain = 1; + gpiod_set_value(st->gain_gpio, gain); + break; + case IIO_CHAN_INFO_SAMP_FREQ: + if (1000*val + val2/1000 < AD7780_FILTER_MIDPOINT) + val = 0; + else + val = 1; + st->odr = ad778x_odr_avail[val]; + gpiod_set_value(st->filter_gpio, val); + break; + default: + break; + } + + return 0; +} + +static int ad7780_postprocess_sample(struct ad_sigma_delta *sigma_delta, + unsigned int raw_sample) +{ + struct ad7780_state *st = ad_sigma_delta_to_ad7780(sigma_delta); + const struct ad7780_chip_info *chip_info = st->chip_info; + + if ((raw_sample & AD7780_ERR) || + ((raw_sample & chip_info->pattern_mask) != chip_info->pattern)) + return -EIO; + + if (chip_info->is_ad778x) { + st->gain = ad778x_gain[raw_sample & AD7780_GAIN]; + st->odr = ad778x_odr_avail[raw_sample & AD7780_FILTER]; + } + + return 0; +} + +static const struct ad_sigma_delta_info ad7780_sigma_delta_info = { + .set_mode = ad7780_set_mode, + .postprocess_sample = ad7780_postprocess_sample, + .has_registers = false, + .irq_flags = IRQF_TRIGGER_FALLING, +}; + +#define _AD7780_CHANNEL(_bits, _wordsize, _mask_all) \ +{ \ + .type = IIO_VOLTAGE, \ + .indexed = 1, \ + .channel = 0, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ + BIT(IIO_CHAN_INFO_OFFSET), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ + .info_mask_shared_by_all = _mask_all, \ + .scan_index = 1, \ + .scan_type = { \ + .sign = 'u', \ + .realbits = (_bits), \ + .storagebits = 32, \ + .shift = (_wordsize) - (_bits), \ + .endianness = IIO_BE, \ + }, \ +} + +#define AD7780_CHANNEL(_bits, _wordsize) \ + _AD7780_CHANNEL(_bits, _wordsize, BIT(IIO_CHAN_INFO_SAMP_FREQ)) +#define AD7170_CHANNEL(_bits, _wordsize) \ + _AD7780_CHANNEL(_bits, _wordsize, 0) + +static const struct ad7780_chip_info ad7780_chip_info_tbl[] = { + [ID_AD7170] = { + .channel = AD7170_CHANNEL(12, 24), + .pattern = AD7170_PATTERN_GOOD, + .pattern_mask = AD7170_PATTERN_MASK, + .is_ad778x = false, + }, + [ID_AD7171] = { + .channel = AD7170_CHANNEL(16, 24), + .pattern = AD7170_PATTERN_GOOD, + .pattern_mask = AD7170_PATTERN_MASK, + .is_ad778x = false, + }, + [ID_AD7780] = { + .channel = AD7780_CHANNEL(24, 32), + .pattern = AD7780_PATTERN_GOOD, + .pattern_mask = AD7780_PATTERN_MASK, + .is_ad778x = true, + }, + [ID_AD7781] = { + .channel = AD7780_CHANNEL(20, 32), + .pattern = AD7780_PATTERN_GOOD, + .pattern_mask = AD7780_PATTERN_MASK, + .is_ad778x = true, + }, +}; + +static const struct iio_info ad7780_info = { + .read_raw = ad7780_read_raw, + .write_raw = ad7780_write_raw, +}; + +static int ad7780_init_gpios(struct device *dev, struct ad7780_state *st) +{ + st->powerdown_gpio = devm_gpiod_get_optional(dev, + "powerdown", + GPIOD_OUT_LOW); + if (IS_ERR(st->powerdown_gpio)) + return dev_err_probe(dev, PTR_ERR(st->powerdown_gpio), + "Failed to request powerdown GPIO\n"); + + if (!st->chip_info->is_ad778x) + return 0; + + + st->gain_gpio = devm_gpiod_get_optional(dev, + "adi,gain", + GPIOD_OUT_HIGH); + if (IS_ERR(st->gain_gpio)) + return dev_err_probe(dev, PTR_ERR(st->gain_gpio), + "Failed to request gain GPIO\n"); + + st->filter_gpio = devm_gpiod_get_optional(dev, + "adi,filter", + GPIOD_OUT_HIGH); + if (IS_ERR(st->filter_gpio)) + return dev_err_probe(dev, PTR_ERR(st->filter_gpio), + "Failed to request filter GPIO\n"); + + return 0; +} + +static void ad7780_reg_disable(void *reg) +{ + regulator_disable(reg); +} + +static int ad7780_probe(struct spi_device *spi) +{ + struct device *dev = &spi->dev; + struct ad7780_state *st; + struct iio_dev *indio_dev; + int ret; + + indio_dev = devm_iio_device_alloc(dev, sizeof(*st)); + if (!indio_dev) + return -ENOMEM; + + st = iio_priv(indio_dev); + st->gain = 1; + + ad_sd_init(&st->sd, indio_dev, spi, &ad7780_sigma_delta_info); + + st->chip_info = + &ad7780_chip_info_tbl[spi_get_device_id(spi)->driver_data]; + + indio_dev->name = spi_get_device_id(spi)->name; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = &st->chip_info->channel; + indio_dev->num_channels = 1; + indio_dev->info = &ad7780_info; + + ret = ad7780_init_gpios(dev, st); + if (ret) + return ret; + + st->reg = devm_regulator_get(dev, "avdd"); + if (IS_ERR(st->reg)) + return PTR_ERR(st->reg); + + ret = regulator_enable(st->reg); + if (ret) + return dev_err_probe(dev, ret, + "Failed to enable specified AVdd supply\n"); + + ret = devm_add_action_or_reset(dev, ad7780_reg_disable, st->reg); + if (ret) + return ret; + + ret = devm_ad_sd_setup_buffer_and_trigger(dev, indio_dev); + if (ret) + return ret; + + return devm_iio_device_register(dev, indio_dev); +} + +static const struct spi_device_id ad7780_id[] = { + { .name = "ad7170", .driver_data = ID_AD7170 }, + { .name = "ad7171", .driver_data = ID_AD7171 }, + { .name = "ad7780", .driver_data = ID_AD7780 }, + { .name = "ad7781", .driver_data = ID_AD7781 }, + { } +}; +MODULE_DEVICE_TABLE(spi, ad7780_id); + +static struct spi_driver ad7780_driver = { + .driver = { + .name = "ad7780", + }, + .probe = ad7780_probe, + .id_table = ad7780_id, +}; +module_spi_driver(ad7780_driver); + +MODULE_AUTHOR("Michael Hennerich "); +MODULE_DESCRIPTION("Analog Devices AD7780 and similar ADCs"); +MODULE_LICENSE("GPL v2"); +MODULE_IMPORT_NS("IIO_AD_SIGMA_DELTA"); diff --git a/drivers/iio/adc/ad7791.c b/drivers/iio/adc/ad7791.c new file mode 100644 index 000000000..c2688307b --- /dev/null +++ b/drivers/iio/adc/ad7791.c @@ -0,0 +1,482 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * AD7787/AD7788/AD7789/AD7790/AD7791 SPI ADC driver + * + * Copyright 2012 Analog Devices Inc. + * Author: Lars-Peter Clausen + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +#define AD7791_REG_COMM 0x0 /* For writes */ +#define AD7791_REG_STATUS 0x0 /* For reads */ +#define AD7791_REG_MODE 0x1 +#define AD7791_REG_FILTER 0x2 +#define AD7791_REG_DATA 0x3 + +#define AD7791_MODE_CONTINUOUS 0x00 +#define AD7791_MODE_SINGLE 0x02 +#define AD7791_MODE_POWERDOWN 0x03 + +#define AD7791_CH_AIN1P_AIN1N 0x00 +#define AD7791_CH_AIN2 0x01 +#define AD7791_CH_AIN1N_AIN1N 0x02 +#define AD7791_CH_AVDD_MONITOR 0x03 + +#define AD7791_FILTER_CLK_DIV_1 (0x0 << 4) +#define AD7791_FILTER_CLK_DIV_2 (0x1 << 4) +#define AD7791_FILTER_CLK_DIV_4 (0x2 << 4) +#define AD7791_FILTER_CLK_DIV_8 (0x3 << 4) +#define AD7791_FILTER_CLK_MASK (0x3 << 4) +#define AD7791_FILTER_RATE_120 0x0 +#define AD7791_FILTER_RATE_100 0x1 +#define AD7791_FILTER_RATE_33_3 0x2 +#define AD7791_FILTER_RATE_20 0x3 +#define AD7791_FILTER_RATE_16_6 0x4 +#define AD7791_FILTER_RATE_16_7 0x5 +#define AD7791_FILTER_RATE_13_3 0x6 +#define AD7791_FILTER_RATE_9_5 0x7 +#define AD7791_FILTER_RATE_MASK 0x7 + +#define AD7791_MODE_BUFFER BIT(1) +#define AD7791_MODE_UNIPOLAR BIT(2) +#define AD7791_MODE_BURNOUT BIT(3) +#define AD7791_MODE_SEL_MASK (0x3 << 6) +#define AD7791_MODE_SEL(x) ((x) << 6) + +#define __AD7991_CHANNEL(_si, _channel1, _channel2, _address, _bits, \ + _storagebits, _shift, _extend_name, _type, _mask_all) \ + { \ + .type = (_type), \ + .differential = (_channel2 == -1 ? 0 : 1), \ + .indexed = 1, \ + .channel = (_channel1), \ + .channel2 = (_channel2), \ + .address = (_address), \ + .extend_name = (_extend_name), \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ + BIT(IIO_CHAN_INFO_OFFSET), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ + .info_mask_shared_by_all = _mask_all, \ + .scan_index = (_si), \ + .scan_type = { \ + .sign = 'u', \ + .realbits = (_bits), \ + .storagebits = (_storagebits), \ + .shift = (_shift), \ + .endianness = IIO_BE, \ + }, \ + } + +#define AD7991_SHORTED_CHANNEL(_si, _channel, _address, _bits, \ + _storagebits, _shift) \ + __AD7991_CHANNEL(_si, _channel, _channel, _address, _bits, \ + _storagebits, _shift, "shorted", IIO_VOLTAGE, \ + BIT(IIO_CHAN_INFO_SAMP_FREQ)) + +#define AD7991_CHANNEL(_si, _channel, _address, _bits, \ + _storagebits, _shift) \ + __AD7991_CHANNEL(_si, _channel, -1, _address, _bits, \ + _storagebits, _shift, NULL, IIO_VOLTAGE, \ + BIT(IIO_CHAN_INFO_SAMP_FREQ)) + +#define AD7991_DIFF_CHANNEL(_si, _channel1, _channel2, _address, _bits, \ + _storagebits, _shift) \ + __AD7991_CHANNEL(_si, _channel1, _channel2, _address, _bits, \ + _storagebits, _shift, NULL, IIO_VOLTAGE, \ + BIT(IIO_CHAN_INFO_SAMP_FREQ)) + +#define AD7991_SUPPLY_CHANNEL(_si, _channel, _address, _bits, _storagebits, \ + _shift) \ + __AD7991_CHANNEL(_si, _channel, -1, _address, _bits, \ + _storagebits, _shift, "supply", IIO_VOLTAGE, \ + BIT(IIO_CHAN_INFO_SAMP_FREQ)) + +#define DECLARE_AD7787_CHANNELS(name, bits, storagebits) \ +const struct iio_chan_spec name[] = { \ + AD7991_DIFF_CHANNEL(0, 0, 0, AD7791_CH_AIN1P_AIN1N, \ + (bits), (storagebits), 0), \ + AD7991_CHANNEL(1, 1, AD7791_CH_AIN2, (bits), (storagebits), 0), \ + AD7991_SHORTED_CHANNEL(2, 0, AD7791_CH_AIN1N_AIN1N, \ + (bits), (storagebits), 0), \ + AD7991_SUPPLY_CHANNEL(3, 2, AD7791_CH_AVDD_MONITOR, \ + (bits), (storagebits), 0), \ + IIO_CHAN_SOFT_TIMESTAMP(4), \ +} + +#define DECLARE_AD7791_CHANNELS(name, bits, storagebits) \ +const struct iio_chan_spec name[] = { \ + AD7991_DIFF_CHANNEL(0, 0, 0, AD7791_CH_AIN1P_AIN1N, \ + (bits), (storagebits), 0), \ + AD7991_SHORTED_CHANNEL(1, 0, AD7791_CH_AIN1N_AIN1N, \ + (bits), (storagebits), 0), \ + AD7991_SUPPLY_CHANNEL(2, 1, AD7791_CH_AVDD_MONITOR, \ + (bits), (storagebits), 0), \ + IIO_CHAN_SOFT_TIMESTAMP(3), \ +} + +static DECLARE_AD7787_CHANNELS(ad7787_channels, 24, 32); +static DECLARE_AD7791_CHANNELS(ad7790_channels, 16, 16); +static DECLARE_AD7791_CHANNELS(ad7791_channels, 24, 32); + +enum { + AD7787, + AD7788, + AD7789, + AD7790, + AD7791, +}; + +enum ad7791_chip_info_flags { + AD7791_FLAG_HAS_FILTER = (1 << 0), + AD7791_FLAG_HAS_BUFFER = (1 << 1), + AD7791_FLAG_HAS_UNIPOLAR = (1 << 2), + AD7791_FLAG_HAS_BURNOUT = (1 << 3), +}; + +struct ad7791_chip_info { + const struct iio_chan_spec *channels; + unsigned int num_channels; + enum ad7791_chip_info_flags flags; +}; + +static const struct ad7791_chip_info ad7791_chip_infos[] = { + [AD7787] = { + .channels = ad7787_channels, + .num_channels = ARRAY_SIZE(ad7787_channels), + .flags = AD7791_FLAG_HAS_FILTER | AD7791_FLAG_HAS_BUFFER | + AD7791_FLAG_HAS_UNIPOLAR | AD7791_FLAG_HAS_BURNOUT, + }, + [AD7788] = { + .channels = ad7790_channels, + .num_channels = ARRAY_SIZE(ad7790_channels), + .flags = AD7791_FLAG_HAS_UNIPOLAR, + }, + [AD7789] = { + .channels = ad7791_channels, + .num_channels = ARRAY_SIZE(ad7791_channels), + .flags = AD7791_FLAG_HAS_UNIPOLAR, + }, + [AD7790] = { + .channels = ad7790_channels, + .num_channels = ARRAY_SIZE(ad7790_channels), + .flags = AD7791_FLAG_HAS_FILTER | AD7791_FLAG_HAS_BUFFER | + AD7791_FLAG_HAS_BURNOUT, + }, + [AD7791] = { + .channels = ad7791_channels, + .num_channels = ARRAY_SIZE(ad7791_channels), + .flags = AD7791_FLAG_HAS_FILTER | AD7791_FLAG_HAS_BUFFER | + AD7791_FLAG_HAS_UNIPOLAR | AD7791_FLAG_HAS_BURNOUT, + }, +}; + +struct ad7791_state { + struct ad_sigma_delta sd; + uint8_t mode; + uint8_t filter; + + struct regulator *reg; + const struct ad7791_chip_info *info; +}; + +static const int ad7791_sample_freq_avail[8][2] = { + [AD7791_FILTER_RATE_120] = { 120, 0 }, + [AD7791_FILTER_RATE_100] = { 100, 0 }, + [AD7791_FILTER_RATE_33_3] = { 33, 300000 }, + [AD7791_FILTER_RATE_20] = { 20, 0 }, + [AD7791_FILTER_RATE_16_6] = { 16, 600000 }, + [AD7791_FILTER_RATE_16_7] = { 16, 700000 }, + [AD7791_FILTER_RATE_13_3] = { 13, 300000 }, + [AD7791_FILTER_RATE_9_5] = { 9, 500000 }, +}; + +static struct ad7791_state *ad_sigma_delta_to_ad7791(struct ad_sigma_delta *sd) +{ + return container_of(sd, struct ad7791_state, sd); +} + +static int ad7791_set_channel(struct ad_sigma_delta *sd, unsigned int channel) +{ + ad_sd_set_comm(sd, channel); + + return 0; +} + +static int ad7791_set_mode(struct ad_sigma_delta *sd, + enum ad_sigma_delta_mode mode) +{ + struct ad7791_state *st = ad_sigma_delta_to_ad7791(sd); + + switch (mode) { + case AD_SD_MODE_CONTINUOUS: + mode = AD7791_MODE_CONTINUOUS; + break; + case AD_SD_MODE_SINGLE: + mode = AD7791_MODE_SINGLE; + break; + case AD_SD_MODE_IDLE: + case AD_SD_MODE_POWERDOWN: + mode = AD7791_MODE_POWERDOWN; + break; + } + + st->mode &= ~AD7791_MODE_SEL_MASK; + st->mode |= AD7791_MODE_SEL(mode); + + return ad_sd_write_reg(sd, AD7791_REG_MODE, sizeof(st->mode), st->mode); +} + +static const struct ad_sigma_delta_info ad7791_sigma_delta_info = { + .set_channel = ad7791_set_channel, + .set_mode = ad7791_set_mode, + .has_registers = true, + .addr_shift = 4, + .read_mask = BIT(3), + .irq_flags = IRQF_TRIGGER_FALLING, + .num_resetclks = 32, +}; + +static int ad7791_read_raw(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, int *val, int *val2, long info) +{ + struct ad7791_state *st = iio_priv(indio_dev); + bool unipolar = !!(st->mode & AD7791_MODE_UNIPOLAR); + unsigned int rate; + + switch (info) { + case IIO_CHAN_INFO_RAW: + return ad_sigma_delta_single_conversion(indio_dev, chan, val); + case IIO_CHAN_INFO_OFFSET: + /** + * Unipolar: 0 to VREF + * Bipolar -VREF to VREF + **/ + if (unipolar) + *val = 0; + else + *val = -(1 << (chan->scan_type.realbits - 1)); + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + /* The monitor channel uses an internal reference. */ + if (chan->address == AD7791_CH_AVDD_MONITOR) { + /* + * The signal is attenuated by a factor of 5 and + * compared against a 1.17V internal reference. + */ + *val = 1170 * 5; + } else { + int voltage_uv; + + voltage_uv = regulator_get_voltage(st->reg); + if (voltage_uv < 0) + return voltage_uv; + + *val = voltage_uv / 1000; + } + if (unipolar) + *val2 = chan->scan_type.realbits; + else + *val2 = chan->scan_type.realbits - 1; + + return IIO_VAL_FRACTIONAL_LOG2; + case IIO_CHAN_INFO_SAMP_FREQ: + rate = st->filter & AD7791_FILTER_RATE_MASK; + *val = ad7791_sample_freq_avail[rate][0]; + *val2 = ad7791_sample_freq_avail[rate][1]; + return IIO_VAL_INT_PLUS_MICRO; + } + + return -EINVAL; +} + +static int __ad7791_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int val, int val2, long mask) +{ + struct ad7791_state *st = iio_priv(indio_dev); + int i; + + switch (mask) { + case IIO_CHAN_INFO_SAMP_FREQ: + for (i = 0; i < ARRAY_SIZE(ad7791_sample_freq_avail); i++) { + if (ad7791_sample_freq_avail[i][0] == val && + ad7791_sample_freq_avail[i][1] == val2) + break; + } + + if (i == ARRAY_SIZE(ad7791_sample_freq_avail)) + return -EINVAL; + + st->filter &= ~AD7791_FILTER_RATE_MASK; + st->filter |= i; + ad_sd_write_reg(&st->sd, AD7791_REG_FILTER, + sizeof(st->filter), + st->filter); + return 0; + default: + return -EINVAL; + } +} + +static int ad7791_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int val, int val2, long mask) +{ + int ret; + + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; + + ret = __ad7791_write_raw(indio_dev, chan, val, val2, mask); + + iio_device_release_direct(indio_dev); + return ret; +} + +static IIO_CONST_ATTR_SAMP_FREQ_AVAIL("120 100 33.3 20 16.7 16.6 13.3 9.5"); + +static struct attribute *ad7791_attributes[] = { + &iio_const_attr_sampling_frequency_available.dev_attr.attr, + NULL +}; + +static const struct attribute_group ad7791_attribute_group = { + .attrs = ad7791_attributes, +}; + +static const struct iio_info ad7791_info = { + .read_raw = &ad7791_read_raw, + .write_raw = &ad7791_write_raw, + .attrs = &ad7791_attribute_group, + .validate_trigger = ad_sd_validate_trigger, +}; + +static const struct iio_info ad7791_no_filter_info = { + .read_raw = &ad7791_read_raw, + .write_raw = &ad7791_write_raw, + .validate_trigger = ad_sd_validate_trigger, +}; + +static int ad7791_setup(struct ad7791_state *st, + const struct ad7791_platform_data *pdata) +{ + /* Set to poweron-reset default values */ + st->mode = AD7791_MODE_BUFFER; + st->filter = AD7791_FILTER_RATE_16_6; + + if (!pdata) + return 0; + + if ((st->info->flags & AD7791_FLAG_HAS_BUFFER) && !pdata->buffered) + st->mode &= ~AD7791_MODE_BUFFER; + + if ((st->info->flags & AD7791_FLAG_HAS_BURNOUT) && + pdata->burnout_current) + st->mode |= AD7791_MODE_BURNOUT; + + if ((st->info->flags & AD7791_FLAG_HAS_UNIPOLAR) && pdata->unipolar) + st->mode |= AD7791_MODE_UNIPOLAR; + + return ad_sd_write_reg(&st->sd, AD7791_REG_MODE, sizeof(st->mode), + st->mode); +} + +static void ad7791_reg_disable(void *reg) +{ + regulator_disable(reg); +} + +static int ad7791_probe(struct spi_device *spi) +{ + struct device *dev = &spi->dev; + const struct ad7791_platform_data *pdata = dev_get_platdata(dev); + struct iio_dev *indio_dev; + struct ad7791_state *st; + int ret; + + if (!spi->irq) + return dev_err_probe(dev, -ENXIO, "Missing IRQ.\n"); + + indio_dev = devm_iio_device_alloc(dev, sizeof(*st)); + if (!indio_dev) + return -ENOMEM; + + st = iio_priv(indio_dev); + + st->reg = devm_regulator_get(dev, "refin"); + if (IS_ERR(st->reg)) + return PTR_ERR(st->reg); + + ret = regulator_enable(st->reg); + if (ret) + return ret; + + ret = devm_add_action_or_reset(dev, ad7791_reg_disable, st->reg); + if (ret) + return ret; + + st->info = &ad7791_chip_infos[spi_get_device_id(spi)->driver_data]; + ad_sd_init(&st->sd, indio_dev, spi, &ad7791_sigma_delta_info); + + indio_dev->name = spi_get_device_id(spi)->name; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = st->info->channels; + indio_dev->num_channels = st->info->num_channels; + if (st->info->flags & AD7791_FLAG_HAS_FILTER) + indio_dev->info = &ad7791_info; + else + indio_dev->info = &ad7791_no_filter_info; + + ret = devm_ad_sd_setup_buffer_and_trigger(dev, indio_dev); + if (ret) + return ret; + + ret = ad7791_setup(st, pdata); + if (ret) + return ret; + + return devm_iio_device_register(dev, indio_dev); +} + +static const struct spi_device_id ad7791_spi_ids[] = { + { .name = "ad7787", .driver_data = AD7787 }, + { .name = "ad7788", .driver_data = AD7788 }, + { .name = "ad7789", .driver_data = AD7789 }, + { .name = "ad7790", .driver_data = AD7790 }, + { .name = "ad7791", .driver_data = AD7791 }, + { } +}; +MODULE_DEVICE_TABLE(spi, ad7791_spi_ids); + +static struct spi_driver ad7791_driver = { + .driver = { + .name = "ad7791", + }, + .probe = ad7791_probe, + .id_table = ad7791_spi_ids, +}; +module_spi_driver(ad7791_driver); + +MODULE_AUTHOR("Lars-Peter Clausen "); +MODULE_DESCRIPTION("Analog Devices AD7787/AD7788/AD7789/AD7790/AD7791 ADC driver"); +MODULE_LICENSE("GPL v2"); +MODULE_IMPORT_NS("IIO_AD_SIGMA_DELTA"); diff --git a/drivers/iio/adc/ad7793.c b/drivers/iio/adc/ad7793.c new file mode 100644 index 000000000..b0f44c144 --- /dev/null +++ b/drivers/iio/adc/ad7793.c @@ -0,0 +1,857 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * AD7785/AD7792/AD7793/AD7794/AD7795 SPI ADC driver + * + * Copyright 2011-2012 Analog Devices Inc. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +/* Registers */ +#define AD7793_REG_COMM 0 /* Communications Register (WO, 8-bit) */ +#define AD7793_REG_STAT 0 /* Status Register (RO, 8-bit) */ +#define AD7793_REG_MODE 1 /* Mode Register (RW, 16-bit */ +#define AD7793_REG_CONF 2 /* Configuration Register (RW, 16-bit) */ +#define AD7793_REG_DATA 3 /* Data Register (RO, 16-/24-bit) */ +#define AD7793_REG_ID 4 /* ID Register (RO, 8-bit) */ +#define AD7793_REG_IO 5 /* IO Register (RO, 8-bit) */ +#define AD7793_REG_OFFSET 6 /* Offset Register (RW, 16-bit + * (AD7792)/24-bit (AD7793)) */ +#define AD7793_REG_FULLSALE 7 /* Full-Scale Register + * (RW, 16-bit (AD7792)/24-bit (AD7793)) */ + +/* Communications Register Bit Designations (AD7793_REG_COMM) */ +#define AD7793_COMM_WEN (1 << 7) /* Write Enable */ +#define AD7793_COMM_WRITE (0 << 6) /* Write Operation */ +#define AD7793_COMM_READ (1 << 6) /* Read Operation */ +#define AD7793_COMM_ADDR(x) (((x) & 0x7) << 3) /* Register Address */ +#define AD7793_COMM_CREAD (1 << 2) /* Continuous Read of Data Register */ + +/* Status Register Bit Designations (AD7793_REG_STAT) */ +#define AD7793_STAT_RDY (1 << 7) /* Ready */ +#define AD7793_STAT_ERR (1 << 6) /* Error (Overrange, Underrange) */ +#define AD7793_STAT_CH3 (1 << 2) /* Channel 3 */ +#define AD7793_STAT_CH2 (1 << 1) /* Channel 2 */ +#define AD7793_STAT_CH1 (1 << 0) /* Channel 1 */ + +/* Mode Register Bit Designations (AD7793_REG_MODE) */ +#define AD7793_MODE_SEL(x) (((x) & 0x7) << 13) /* Operation Mode Select */ +#define AD7793_MODE_SEL_MASK (0x7 << 13) /* Operation Mode Select mask */ +#define AD7793_MODE_CLKSRC(x) (((x) & 0x3) << 6) /* ADC Clock Source Select */ +#define AD7793_MODE_RATE(x) ((x) & 0xF) /* Filter Update Rate Select */ + +#define AD7793_MODE_CONT 0 /* Continuous Conversion Mode */ +#define AD7793_MODE_SINGLE 1 /* Single Conversion Mode */ +#define AD7793_MODE_IDLE 2 /* Idle Mode */ +#define AD7793_MODE_PWRDN 3 /* Power-Down Mode */ +#define AD7793_MODE_CAL_INT_ZERO 4 /* Internal Zero-Scale Calibration */ +#define AD7793_MODE_CAL_INT_FULL 5 /* Internal Full-Scale Calibration */ +#define AD7793_MODE_CAL_SYS_ZERO 6 /* System Zero-Scale Calibration */ +#define AD7793_MODE_CAL_SYS_FULL 7 /* System Full-Scale Calibration */ + +#define AD7793_CLK_INT 0 /* Internal 64 kHz Clock not + * available at the CLK pin */ +#define AD7793_CLK_INT_CO 1 /* Internal 64 kHz Clock available + * at the CLK pin */ +#define AD7793_CLK_EXT 2 /* External 64 kHz Clock */ +#define AD7793_CLK_EXT_DIV2 3 /* External Clock divided by 2 */ + +/* Configuration Register Bit Designations (AD7793_REG_CONF) */ +#define AD7793_CONF_VBIAS(x) (((x) & 0x3) << 14) /* Bias Voltage + * Generator Enable */ +#define AD7793_CONF_BO_EN (1 << 13) /* Burnout Current Enable */ +#define AD7793_CONF_UNIPOLAR (1 << 12) /* Unipolar/Bipolar Enable */ +#define AD7793_CONF_BOOST (1 << 11) /* Boost Enable */ +#define AD7793_CONF_GAIN(x) (((x) & 0x7) << 8) /* Gain Select */ +#define AD7793_CONF_REFSEL(x) ((x) << 6) /* INT/EXT Reference Select */ +#define AD7793_CONF_BUF (1 << 4) /* Buffered Mode Enable */ +#define AD7793_CONF_CHAN(x) ((x) & 0xf) /* Channel select */ +#define AD7793_CONF_CHAN_MASK 0xf /* Channel select mask */ + +#define AD7793_CH_AIN1P_AIN1M 0 /* AIN1(+) - AIN1(-) */ +#define AD7793_CH_AIN2P_AIN2M 1 /* AIN2(+) - AIN2(-) */ +#define AD7793_CH_AIN3P_AIN3M 2 /* AIN3(+) - AIN3(-) */ +#define AD7793_CH_AIN1M_AIN1M 3 /* AIN1(-) - AIN1(-) */ +#define AD7793_CH_TEMP 6 /* Temp Sensor */ +#define AD7793_CH_AVDD_MONITOR 7 /* AVDD Monitor */ + +#define AD7795_CH_AIN4P_AIN4M 4 /* AIN4(+) - AIN4(-) */ +#define AD7795_CH_AIN5P_AIN5M 5 /* AIN5(+) - AIN5(-) */ +#define AD7795_CH_AIN6P_AIN6M 6 /* AIN6(+) - AIN6(-) */ +#define AD7795_CH_AIN1M_AIN1M 8 /* AIN1(-) - AIN1(-) */ + +/* ID Register Bit Designations (AD7793_REG_ID) */ +#define AD7785_ID 0x3 +#define AD7792_ID 0xA +#define AD7793_ID 0xB +#define AD7794_ID 0xF +#define AD7795_ID 0xF +#define AD7796_ID 0xA +#define AD7797_ID 0xB +#define AD7798_ID 0x8 +#define AD7799_ID 0x9 +#define AD7793_ID_MASK 0xF + +/* IO (Excitation Current Sources) Register Bit Designations (AD7793_REG_IO) */ +#define AD7793_IO_IEXC1_IOUT1_IEXC2_IOUT2 0 /* IEXC1 connect to IOUT1, + * IEXC2 connect to IOUT2 */ +#define AD7793_IO_IEXC1_IOUT2_IEXC2_IOUT1 1 /* IEXC1 connect to IOUT2, + * IEXC2 connect to IOUT1 */ +#define AD7793_IO_IEXC1_IEXC2_IOUT1 2 /* Both current sources + * IEXC1,2 connect to IOUT1 */ +#define AD7793_IO_IEXC1_IEXC2_IOUT2 3 /* Both current sources + * IEXC1,2 connect to IOUT2 */ + +#define AD7793_IO_IXCEN_10uA (1 << 0) /* Excitation Current 10uA */ +#define AD7793_IO_IXCEN_210uA (2 << 0) /* Excitation Current 210uA */ +#define AD7793_IO_IXCEN_1mA (3 << 0) /* Excitation Current 1mA */ + +/* NOTE: + * The AD7792/AD7793 features a dual use data out ready DOUT/RDY output. + * In order to avoid contentions on the SPI bus, it's therefore necessary + * to use spi bus locking. + * + * The DOUT/RDY output must also be wired to an interrupt capable GPIO. + */ + +#define AD7793_FLAG_HAS_CLKSEL BIT(0) +#define AD7793_FLAG_HAS_REFSEL BIT(1) +#define AD7793_FLAG_HAS_VBIAS BIT(2) +#define AD7793_HAS_EXITATION_CURRENT BIT(3) +#define AD7793_FLAG_HAS_GAIN BIT(4) +#define AD7793_FLAG_HAS_BUFFER BIT(5) + +struct ad7793_chip_info { + unsigned int id; + const struct iio_chan_spec *channels; + unsigned int num_channels; + unsigned int flags; + + const struct iio_info *iio_info; + const u16 *sample_freq_avail; +}; + +struct ad7793_state { + const struct ad7793_chip_info *chip_info; + u16 mode; + u16 conf; + u32 scale_avail[8][2]; + + struct ad_sigma_delta sd; + +}; + +enum ad7793_supported_device_ids { + ID_AD7785, + ID_AD7792, + ID_AD7793, + ID_AD7794, + ID_AD7795, + ID_AD7796, + ID_AD7797, + ID_AD7798, + ID_AD7799, +}; + +static struct ad7793_state *ad_sigma_delta_to_ad7793(struct ad_sigma_delta *sd) +{ + return container_of(sd, struct ad7793_state, sd); +} + +static int ad7793_set_channel(struct ad_sigma_delta *sd, unsigned int channel) +{ + struct ad7793_state *st = ad_sigma_delta_to_ad7793(sd); + + st->conf &= ~AD7793_CONF_CHAN_MASK; + st->conf |= AD7793_CONF_CHAN(channel); + + return ad_sd_write_reg(&st->sd, AD7793_REG_CONF, 2, st->conf); +} + +static int ad7793_set_mode(struct ad_sigma_delta *sd, + enum ad_sigma_delta_mode mode) +{ + struct ad7793_state *st = ad_sigma_delta_to_ad7793(sd); + + st->mode &= ~AD7793_MODE_SEL_MASK; + st->mode |= AD7793_MODE_SEL(mode); + + return ad_sd_write_reg(&st->sd, AD7793_REG_MODE, 2, st->mode); +} + +static const struct ad_sigma_delta_info ad7793_sigma_delta_info = { + .set_channel = ad7793_set_channel, + .set_mode = ad7793_set_mode, + .has_registers = true, + .addr_shift = 3, + .read_mask = BIT(6), + .irq_flags = IRQF_TRIGGER_FALLING, + .num_resetclks = 32, +}; + +static const struct ad_sd_calib_data ad7793_calib_arr[6] = { + {AD7793_MODE_CAL_INT_ZERO, AD7793_CH_AIN1P_AIN1M}, + {AD7793_MODE_CAL_INT_FULL, AD7793_CH_AIN1P_AIN1M}, + {AD7793_MODE_CAL_INT_ZERO, AD7793_CH_AIN2P_AIN2M}, + {AD7793_MODE_CAL_INT_FULL, AD7793_CH_AIN2P_AIN2M}, + {AD7793_MODE_CAL_INT_ZERO, AD7793_CH_AIN3P_AIN3M}, + {AD7793_MODE_CAL_INT_FULL, AD7793_CH_AIN3P_AIN3M} +}; + +static int ad7793_calibrate_all(struct ad7793_state *st) +{ + return ad_sd_calibrate_all(&st->sd, ad7793_calib_arr, + ARRAY_SIZE(ad7793_calib_arr)); +} + +static int ad7793_check_platform_data(struct ad7793_state *st, + const struct ad7793_platform_data *pdata) +{ + if ((pdata->current_source_direction == AD7793_IEXEC1_IEXEC2_IOUT1 || + pdata->current_source_direction == AD7793_IEXEC1_IEXEC2_IOUT2) && + ((pdata->exitation_current != AD7793_IX_10uA) && + (pdata->exitation_current != AD7793_IX_210uA))) + return -EINVAL; + + if (!(st->chip_info->flags & AD7793_FLAG_HAS_CLKSEL) && + pdata->clock_src != AD7793_CLK_SRC_INT) + return -EINVAL; + + if (!(st->chip_info->flags & AD7793_FLAG_HAS_REFSEL) && + pdata->refsel != AD7793_REFSEL_REFIN1) + return -EINVAL; + + if (!(st->chip_info->flags & AD7793_FLAG_HAS_VBIAS) && + pdata->bias_voltage != AD7793_BIAS_VOLTAGE_DISABLED) + return -EINVAL; + + if (!(st->chip_info->flags & AD7793_HAS_EXITATION_CURRENT) && + pdata->exitation_current != AD7793_IX_DISABLED) + return -EINVAL; + + return 0; +} + +static int ad7793_setup(struct iio_dev *indio_dev, + const struct ad7793_platform_data *pdata, + unsigned int vref_mv) +{ + struct ad7793_state *st = iio_priv(indio_dev); + int i, ret; + unsigned long long scale_uv; + u32 id; + + ret = ad7793_check_platform_data(st, pdata); + if (ret) + return ret; + + /* reset the serial interface */ + ret = ad_sd_reset(&st->sd); + if (ret < 0) + goto out; + + /* + * Per AD7792/AD7793 datasheet (Rev. B, page 25, RESET section), + * allow 500 us after a reset before accessing on-chip registers. + */ + fsleep(500); + + /* write/read test for device presence */ + ret = ad_sd_read_reg(&st->sd, AD7793_REG_ID, 1, &id); + if (ret) + goto out; + + id &= AD7793_ID_MASK; + + if (id != st->chip_info->id) { + ret = dev_err_probe(&st->sd.spi->dev, -ENODEV, + "device ID query failed\n"); + goto out; + } + + st->mode = AD7793_MODE_RATE(1); + st->conf = 0; + + if (st->chip_info->flags & AD7793_FLAG_HAS_CLKSEL) + st->mode |= AD7793_MODE_CLKSRC(pdata->clock_src); + if (st->chip_info->flags & AD7793_FLAG_HAS_REFSEL) + st->conf |= AD7793_CONF_REFSEL(pdata->refsel); + if (st->chip_info->flags & AD7793_FLAG_HAS_VBIAS) + st->conf |= AD7793_CONF_VBIAS(pdata->bias_voltage); + if (pdata->buffered || !(st->chip_info->flags & AD7793_FLAG_HAS_BUFFER)) + st->conf |= AD7793_CONF_BUF; + if (pdata->boost_enable && + (st->chip_info->flags & AD7793_FLAG_HAS_VBIAS)) + st->conf |= AD7793_CONF_BOOST; + if (pdata->burnout_current) + st->conf |= AD7793_CONF_BO_EN; + if (pdata->unipolar) + st->conf |= AD7793_CONF_UNIPOLAR; + + if (!(st->chip_info->flags & AD7793_FLAG_HAS_GAIN)) + st->conf |= AD7793_CONF_GAIN(7); + + ret = ad7793_set_mode(&st->sd, AD_SD_MODE_IDLE); + if (ret) + goto out; + + ret = ad7793_set_channel(&st->sd, 0); + if (ret) + goto out; + + if (st->chip_info->flags & AD7793_HAS_EXITATION_CURRENT) { + ret = ad_sd_write_reg(&st->sd, AD7793_REG_IO, 1, + pdata->exitation_current | + (pdata->current_source_direction << 2)); + if (ret) + goto out; + } + + ret = ad7793_calibrate_all(st); + if (ret) + goto out; + + /* Populate available ADC input ranges */ + for (i = 0; i < ARRAY_SIZE(st->scale_avail); i++) { + scale_uv = ((u64)vref_mv * 100000000) + >> (st->chip_info->channels[0].scan_type.realbits - + (!!(st->conf & AD7793_CONF_UNIPOLAR) ? 0 : 1)); + scale_uv >>= i; + + st->scale_avail[i][1] = do_div(scale_uv, 100000000) * 10; + st->scale_avail[i][0] = scale_uv; + } + + return 0; +out: + return dev_err_probe(&st->sd.spi->dev, ret, "setup failed\n"); +} + +static const u16 ad7793_sample_freq_avail[16] = {0, 470, 242, 123, 62, 50, 39, + 33, 19, 17, 16, 12, 10, 8, 6, 4}; + +static const u16 ad7797_sample_freq_avail[16] = {0, 0, 0, 123, 62, 50, 0, + 33, 0, 17, 16, 12, 10, 8, 6, 4}; + +static IIO_CONST_ATTR_SAMP_FREQ_AVAIL( + "470 242 123 62 50 39 33 19 17 16 12 10 8 6 4"); + +static IIO_CONST_ATTR_NAMED(sampling_frequency_available_ad7797, + sampling_frequency_available, "123 62 50 33 17 16 12 10 8 6 4"); + +static int ad7793_read_avail(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + const int **vals, int *type, int *length, + long mask) +{ + struct ad7793_state *st = iio_priv(indio_dev); + + switch (mask) { + case IIO_CHAN_INFO_SCALE: + *vals = (int *)st->scale_avail; + *type = IIO_VAL_INT_PLUS_NANO; + /* Values are stored in a 2D matrix */ + *length = ARRAY_SIZE(st->scale_avail) * 2; + + return IIO_AVAIL_LIST; + default: + return -EINVAL; + } +} + +static struct attribute *ad7793_attributes[] = { + &iio_const_attr_sampling_frequency_available.dev_attr.attr, + NULL +}; + +static const struct attribute_group ad7793_attribute_group = { + .attrs = ad7793_attributes, +}; + +static struct attribute *ad7797_attributes[] = { + &iio_const_attr_sampling_frequency_available_ad7797.dev_attr.attr, + NULL +}; + +static const struct attribute_group ad7797_attribute_group = { + .attrs = ad7797_attributes, +}; + +static int ad7793_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, + int *val2, + long m) +{ + struct ad7793_state *st = iio_priv(indio_dev); + int ret; + unsigned long long scale_uv; + bool unipolar = !!(st->conf & AD7793_CONF_UNIPOLAR); + + switch (m) { + case IIO_CHAN_INFO_RAW: + ret = ad_sigma_delta_single_conversion(indio_dev, chan, val); + if (ret < 0) + return ret; + + return IIO_VAL_INT; + + case IIO_CHAN_INFO_SCALE: + switch (chan->type) { + case IIO_VOLTAGE: + if (chan->differential) { + *val = st-> + scale_avail[(st->conf >> 8) & 0x7][0]; + *val2 = st-> + scale_avail[(st->conf >> 8) & 0x7][1]; + return IIO_VAL_INT_PLUS_NANO; + } + /* 1170mV / 2^23 * 6 */ + scale_uv = (1170ULL * 1000000000ULL * 6ULL); + break; + case IIO_TEMP: + /* 1170mV / 0.81 mV/C / 2^23 */ + scale_uv = 1444444444444444ULL; + break; + default: + return -EINVAL; + } + + scale_uv >>= (chan->scan_type.realbits - (unipolar ? 0 : 1)); + *val = 0; + *val2 = scale_uv; + return IIO_VAL_INT_PLUS_NANO; + case IIO_CHAN_INFO_OFFSET: + if (!unipolar) + *val = -(1 << (chan->scan_type.realbits - 1)); + else + *val = 0; + + /* Kelvin to Celsius */ + if (chan->type == IIO_TEMP) { + unsigned long long offset; + unsigned int shift; + + shift = chan->scan_type.realbits - (unipolar ? 0 : 1); + offset = 273ULL << shift; + do_div(offset, 1444); + *val -= offset; + } + return IIO_VAL_INT; + case IIO_CHAN_INFO_SAMP_FREQ: + *val = st->chip_info + ->sample_freq_avail[AD7793_MODE_RATE(st->mode)]; + return IIO_VAL_INT; + } + return -EINVAL; +} + +static int __ad7793_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, int val2, long mask) +{ + struct ad7793_state *st = iio_priv(indio_dev); + int i; + unsigned int tmp; + + switch (mask) { + case IIO_CHAN_INFO_SCALE: + for (i = 0; i < ARRAY_SIZE(st->scale_avail); i++) { + if (val2 != st->scale_avail[i][1]) + continue; + + tmp = st->conf; + st->conf &= ~AD7793_CONF_GAIN(-1); + st->conf |= AD7793_CONF_GAIN(i); + + if (tmp == st->conf) + return 0; + + ad_sd_write_reg(&st->sd, AD7793_REG_CONF, + sizeof(st->conf), st->conf); + ad7793_calibrate_all(st); + + return 0; + } + return -EINVAL; + case IIO_CHAN_INFO_SAMP_FREQ: + if (!val) + return -EINVAL; + + for (i = 0; i < 16; i++) + if (val == st->chip_info->sample_freq_avail[i]) + break; + + if (i == 16) + return -EINVAL; + + st->mode &= ~AD7793_MODE_RATE(-1); + st->mode |= AD7793_MODE_RATE(i); + ad_sd_write_reg(&st->sd, AD7793_REG_MODE, sizeof(st->mode), + st->mode); + return 0; + default: + return -EINVAL; + } +} + +static int ad7793_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, int val2, long mask) +{ + int ret; + + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; + + ret = __ad7793_write_raw(indio_dev, chan, val, val2, mask); + + iio_device_release_direct(indio_dev); + + return ret; +} + +static int ad7793_write_raw_get_fmt(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + long mask) +{ + return IIO_VAL_INT_PLUS_NANO; +} + +static const struct iio_info ad7793_info = { + .read_raw = &ad7793_read_raw, + .write_raw = &ad7793_write_raw, + .write_raw_get_fmt = &ad7793_write_raw_get_fmt, + .read_avail = ad7793_read_avail, + .attrs = &ad7793_attribute_group, + .validate_trigger = ad_sd_validate_trigger, +}; + +static const struct iio_info ad7797_info = { + .read_raw = &ad7793_read_raw, + .write_raw = &ad7793_write_raw, + .write_raw_get_fmt = &ad7793_write_raw_get_fmt, + .attrs = &ad7797_attribute_group, + .validate_trigger = ad_sd_validate_trigger, +}; + +#define __AD7793_CHANNEL(_si, _channel1, _channel2, _address, _bits, \ + _storagebits, _shift, _extend_name, _type, _mask_type_av, _mask_all) \ + { \ + .type = (_type), \ + .differential = (_channel2 == -1 ? 0 : 1), \ + .indexed = 1, \ + .channel = (_channel1), \ + .channel2 = (_channel2), \ + .address = (_address), \ + .extend_name = (_extend_name), \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ + BIT(IIO_CHAN_INFO_OFFSET), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ + .info_mask_shared_by_type_available = (_mask_type_av), \ + .info_mask_shared_by_all = _mask_all, \ + .scan_index = (_si), \ + .scan_type = { \ + .sign = 'u', \ + .realbits = (_bits), \ + .storagebits = (_storagebits), \ + .shift = (_shift), \ + .endianness = IIO_BE, \ + }, \ + } + +#define AD7793_DIFF_CHANNEL(_si, _channel1, _channel2, _address, _bits, \ + _storagebits, _shift) \ + __AD7793_CHANNEL(_si, _channel1, _channel2, _address, _bits, \ + _storagebits, _shift, NULL, IIO_VOLTAGE, \ + BIT(IIO_CHAN_INFO_SCALE), \ + BIT(IIO_CHAN_INFO_SAMP_FREQ)) + +#define AD7793_SHORTED_CHANNEL(_si, _channel, _address, _bits, \ + _storagebits, _shift) \ + __AD7793_CHANNEL(_si, _channel, _channel, _address, _bits, \ + _storagebits, _shift, "shorted", IIO_VOLTAGE, \ + BIT(IIO_CHAN_INFO_SCALE), \ + BIT(IIO_CHAN_INFO_SAMP_FREQ)) + +#define AD7793_TEMP_CHANNEL(_si, _address, _bits, _storagebits, _shift) \ + __AD7793_CHANNEL(_si, 0, -1, _address, _bits, \ + _storagebits, _shift, NULL, IIO_TEMP, \ + 0, \ + BIT(IIO_CHAN_INFO_SAMP_FREQ)) + +#define AD7793_SUPPLY_CHANNEL(_si, _channel, _address, _bits, _storagebits, \ + _shift) \ + __AD7793_CHANNEL(_si, _channel, -1, _address, _bits, \ + _storagebits, _shift, "supply", IIO_VOLTAGE, \ + 0, \ + BIT(IIO_CHAN_INFO_SAMP_FREQ)) + +#define AD7797_DIFF_CHANNEL(_si, _channel1, _channel2, _address, _bits, \ + _storagebits, _shift) \ + __AD7793_CHANNEL(_si, _channel1, _channel2, _address, _bits, \ + _storagebits, _shift, NULL, IIO_VOLTAGE, \ + 0, \ + BIT(IIO_CHAN_INFO_SAMP_FREQ)) + +#define AD7797_SHORTED_CHANNEL(_si, _channel, _address, _bits, \ + _storagebits, _shift) \ + __AD7793_CHANNEL(_si, _channel, _channel, _address, _bits, \ + _storagebits, _shift, "shorted", IIO_VOLTAGE, \ + 0, \ + BIT(IIO_CHAN_INFO_SAMP_FREQ)) + +#define DECLARE_AD7793_CHANNELS(_name, _b, _sb, _s) \ +const struct iio_chan_spec _name##_channels[] = { \ + AD7793_DIFF_CHANNEL(0, 0, 0, AD7793_CH_AIN1P_AIN1M, (_b), (_sb), (_s)), \ + AD7793_DIFF_CHANNEL(1, 1, 1, AD7793_CH_AIN2P_AIN2M, (_b), (_sb), (_s)), \ + AD7793_DIFF_CHANNEL(2, 2, 2, AD7793_CH_AIN3P_AIN3M, (_b), (_sb), (_s)), \ + AD7793_SHORTED_CHANNEL(3, 0, AD7793_CH_AIN1M_AIN1M, (_b), (_sb), (_s)), \ + AD7793_TEMP_CHANNEL(4, AD7793_CH_TEMP, (_b), (_sb), (_s)), \ + AD7793_SUPPLY_CHANNEL(5, 3, AD7793_CH_AVDD_MONITOR, (_b), (_sb), (_s)), \ + IIO_CHAN_SOFT_TIMESTAMP(6), \ +} + +#define DECLARE_AD7795_CHANNELS(_name, _b, _sb) \ +const struct iio_chan_spec _name##_channels[] = { \ + AD7793_DIFF_CHANNEL(0, 0, 0, AD7793_CH_AIN1P_AIN1M, (_b), (_sb), 0), \ + AD7793_DIFF_CHANNEL(1, 1, 1, AD7793_CH_AIN2P_AIN2M, (_b), (_sb), 0), \ + AD7793_DIFF_CHANNEL(2, 2, 2, AD7793_CH_AIN3P_AIN3M, (_b), (_sb), 0), \ + AD7793_DIFF_CHANNEL(3, 3, 3, AD7795_CH_AIN4P_AIN4M, (_b), (_sb), 0), \ + AD7793_DIFF_CHANNEL(4, 4, 4, AD7795_CH_AIN5P_AIN5M, (_b), (_sb), 0), \ + AD7793_DIFF_CHANNEL(5, 5, 5, AD7795_CH_AIN6P_AIN6M, (_b), (_sb), 0), \ + AD7793_SHORTED_CHANNEL(6, 0, AD7795_CH_AIN1M_AIN1M, (_b), (_sb), 0), \ + AD7793_TEMP_CHANNEL(7, AD7793_CH_TEMP, (_b), (_sb), 0), \ + AD7793_SUPPLY_CHANNEL(8, 3, AD7793_CH_AVDD_MONITOR, (_b), (_sb), 0), \ + IIO_CHAN_SOFT_TIMESTAMP(9), \ +} + +#define DECLARE_AD7797_CHANNELS(_name, _b, _sb) \ +const struct iio_chan_spec _name##_channels[] = { \ + AD7797_DIFF_CHANNEL(0, 0, 0, AD7793_CH_AIN1P_AIN1M, (_b), (_sb), 0), \ + AD7797_SHORTED_CHANNEL(1, 0, AD7793_CH_AIN1M_AIN1M, (_b), (_sb), 0), \ + AD7793_TEMP_CHANNEL(2, AD7793_CH_TEMP, (_b), (_sb), 0), \ + AD7793_SUPPLY_CHANNEL(3, 3, AD7793_CH_AVDD_MONITOR, (_b), (_sb), 0), \ + IIO_CHAN_SOFT_TIMESTAMP(4), \ +} + +#define DECLARE_AD7799_CHANNELS(_name, _b, _sb) \ +const struct iio_chan_spec _name##_channels[] = { \ + AD7793_DIFF_CHANNEL(0, 0, 0, AD7793_CH_AIN1P_AIN1M, (_b), (_sb), 0), \ + AD7793_DIFF_CHANNEL(1, 1, 1, AD7793_CH_AIN2P_AIN2M, (_b), (_sb), 0), \ + AD7793_DIFF_CHANNEL(2, 2, 2, AD7793_CH_AIN3P_AIN3M, (_b), (_sb), 0), \ + AD7793_SHORTED_CHANNEL(3, 0, AD7793_CH_AIN1M_AIN1M, (_b), (_sb), 0), \ + AD7793_SUPPLY_CHANNEL(4, 3, AD7793_CH_AVDD_MONITOR, (_b), (_sb), 0), \ + IIO_CHAN_SOFT_TIMESTAMP(5), \ +} + +static DECLARE_AD7793_CHANNELS(ad7785, 20, 32, 4); +static DECLARE_AD7793_CHANNELS(ad7792, 16, 32, 0); +static DECLARE_AD7793_CHANNELS(ad7793, 24, 32, 0); +static DECLARE_AD7795_CHANNELS(ad7794, 16, 32); +static DECLARE_AD7795_CHANNELS(ad7795, 24, 32); +static DECLARE_AD7797_CHANNELS(ad7796, 16, 16); +static DECLARE_AD7797_CHANNELS(ad7797, 24, 32); +static DECLARE_AD7799_CHANNELS(ad7798, 16, 16); +static DECLARE_AD7799_CHANNELS(ad7799, 24, 32); + +static const struct ad7793_chip_info ad7793_chip_info_tbl[] = { + [ID_AD7785] = { + .id = AD7785_ID, + .channels = ad7785_channels, + .num_channels = ARRAY_SIZE(ad7785_channels), + .iio_info = &ad7793_info, + .sample_freq_avail = ad7793_sample_freq_avail, + .flags = AD7793_FLAG_HAS_CLKSEL | + AD7793_FLAG_HAS_REFSEL | + AD7793_FLAG_HAS_VBIAS | + AD7793_HAS_EXITATION_CURRENT | + AD7793_FLAG_HAS_GAIN | + AD7793_FLAG_HAS_BUFFER, + }, + [ID_AD7792] = { + .id = AD7792_ID, + .channels = ad7792_channels, + .num_channels = ARRAY_SIZE(ad7792_channels), + .iio_info = &ad7793_info, + .sample_freq_avail = ad7793_sample_freq_avail, + .flags = AD7793_FLAG_HAS_CLKSEL | + AD7793_FLAG_HAS_REFSEL | + AD7793_FLAG_HAS_VBIAS | + AD7793_HAS_EXITATION_CURRENT | + AD7793_FLAG_HAS_GAIN | + AD7793_FLAG_HAS_BUFFER, + }, + [ID_AD7793] = { + .id = AD7793_ID, + .channels = ad7793_channels, + .num_channels = ARRAY_SIZE(ad7793_channels), + .iio_info = &ad7793_info, + .sample_freq_avail = ad7793_sample_freq_avail, + .flags = AD7793_FLAG_HAS_CLKSEL | + AD7793_FLAG_HAS_REFSEL | + AD7793_FLAG_HAS_VBIAS | + AD7793_HAS_EXITATION_CURRENT | + AD7793_FLAG_HAS_GAIN | + AD7793_FLAG_HAS_BUFFER, + }, + [ID_AD7794] = { + .id = AD7794_ID, + .channels = ad7794_channels, + .num_channels = ARRAY_SIZE(ad7794_channels), + .iio_info = &ad7793_info, + .sample_freq_avail = ad7793_sample_freq_avail, + .flags = AD7793_FLAG_HAS_CLKSEL | + AD7793_FLAG_HAS_REFSEL | + AD7793_FLAG_HAS_VBIAS | + AD7793_HAS_EXITATION_CURRENT | + AD7793_FLAG_HAS_GAIN | + AD7793_FLAG_HAS_BUFFER, + }, + [ID_AD7795] = { + .id = AD7795_ID, + .channels = ad7795_channels, + .num_channels = ARRAY_SIZE(ad7795_channels), + .iio_info = &ad7793_info, + .sample_freq_avail = ad7793_sample_freq_avail, + .flags = AD7793_FLAG_HAS_CLKSEL | + AD7793_FLAG_HAS_REFSEL | + AD7793_FLAG_HAS_VBIAS | + AD7793_HAS_EXITATION_CURRENT | + AD7793_FLAG_HAS_GAIN | + AD7793_FLAG_HAS_BUFFER, + }, + [ID_AD7796] = { + .id = AD7796_ID, + .channels = ad7796_channels, + .num_channels = ARRAY_SIZE(ad7796_channels), + .iio_info = &ad7797_info, + .sample_freq_avail = ad7797_sample_freq_avail, + .flags = AD7793_FLAG_HAS_CLKSEL, + }, + [ID_AD7797] = { + .id = AD7797_ID, + .channels = ad7797_channels, + .num_channels = ARRAY_SIZE(ad7797_channels), + .iio_info = &ad7797_info, + .sample_freq_avail = ad7797_sample_freq_avail, + .flags = AD7793_FLAG_HAS_CLKSEL, + }, + [ID_AD7798] = { + .id = AD7798_ID, + .channels = ad7798_channels, + .num_channels = ARRAY_SIZE(ad7798_channels), + .iio_info = &ad7793_info, + .sample_freq_avail = ad7793_sample_freq_avail, + .flags = AD7793_FLAG_HAS_GAIN | + AD7793_FLAG_HAS_BUFFER, + }, + [ID_AD7799] = { + .id = AD7799_ID, + .channels = ad7799_channels, + .num_channels = ARRAY_SIZE(ad7799_channels), + .iio_info = &ad7793_info, + .sample_freq_avail = ad7793_sample_freq_avail, + .flags = AD7793_FLAG_HAS_GAIN | + AD7793_FLAG_HAS_BUFFER, + }, +}; + +static int ad7793_probe(struct spi_device *spi) +{ + struct device *dev = &spi->dev; + const struct ad7793_platform_data *pdata = dev_get_platdata(dev); + struct ad7793_state *st; + struct iio_dev *indio_dev; + int ret, vref_mv = 0; + + if (!pdata) + return dev_err_probe(dev, -ENODEV, "no platform data?\n"); + + if (!spi->irq) + return dev_err_probe(dev, -ENODEV, "no IRQ?\n"); + + indio_dev = devm_iio_device_alloc(dev, sizeof(*st)); + if (indio_dev == NULL) + return -ENOMEM; + + st = iio_priv(indio_dev); + + ad_sd_init(&st->sd, indio_dev, spi, &ad7793_sigma_delta_info); + + if (pdata->refsel != AD7793_REFSEL_INTERNAL) { + ret = devm_regulator_get_enable_read_voltage(dev, "refin"); + if (ret < 0) + return ret; + + vref_mv = ret / 1000; + } else { + vref_mv = 1170; /* Built-in ref */ + } + + st->chip_info = + &ad7793_chip_info_tbl[spi_get_device_id(spi)->driver_data]; + + indio_dev->name = spi_get_device_id(spi)->name; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = st->chip_info->channels; + indio_dev->num_channels = st->chip_info->num_channels; + indio_dev->info = st->chip_info->iio_info; + + ret = devm_ad_sd_setup_buffer_and_trigger(dev, indio_dev); + if (ret) + return ret; + + ret = ad7793_setup(indio_dev, pdata, vref_mv); + if (ret) + return ret; + + return devm_iio_device_register(dev, indio_dev); +} + +static const struct spi_device_id ad7793_id[] = { + { .name = "ad7785", .driver_data = ID_AD7785 }, + { .name = "ad7792", .driver_data = ID_AD7792 }, + { .name = "ad7793", .driver_data = ID_AD7793 }, + { .name = "ad7794", .driver_data = ID_AD7794 }, + { .name = "ad7795", .driver_data = ID_AD7795 }, + { .name = "ad7796", .driver_data = ID_AD7796 }, + { .name = "ad7797", .driver_data = ID_AD7797 }, + { .name = "ad7798", .driver_data = ID_AD7798 }, + { .name = "ad7799", .driver_data = ID_AD7799 }, + { } +}; +MODULE_DEVICE_TABLE(spi, ad7793_id); + +static struct spi_driver ad7793_driver = { + .driver = { + .name = "ad7793", + }, + .probe = ad7793_probe, + .id_table = ad7793_id, +}; +module_spi_driver(ad7793_driver); + +MODULE_AUTHOR("Michael Hennerich "); +MODULE_DESCRIPTION("Analog Devices AD7793 and similar ADCs"); +MODULE_LICENSE("GPL v2"); +MODULE_IMPORT_NS("IIO_AD_SIGMA_DELTA"); diff --git a/drivers/iio/adc/ad7887.c b/drivers/iio/adc/ad7887.c new file mode 100644 index 000000000..62aaedbce --- /dev/null +++ b/drivers/iio/adc/ad7887.c @@ -0,0 +1,347 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * AD7887 SPI ADC driver + * + * Copyright 2010-2011 Analog Devices Inc. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +#include + +#define AD7887_REF_DIS BIT(5) /* on-chip reference disable */ +#define AD7887_DUAL BIT(4) /* dual-channel mode */ +#define AD7887_CH_AIN1 BIT(3) /* convert on channel 1, DUAL=1 */ +#define AD7887_CH_AIN0 0 /* convert on channel 0, DUAL=0,1 */ +#define AD7887_PM_MODE1 0 /* CS based shutdown */ +#define AD7887_PM_MODE2 1 /* full on */ +#define AD7887_PM_MODE3 2 /* auto shutdown after conversion */ +#define AD7887_PM_MODE4 3 /* standby mode */ + +enum ad7887_channels { + AD7887_CH0, + AD7887_CH0_CH1, + AD7887_CH1, +}; + +/** + * struct ad7887_chip_info - chip specific information + * @int_vref_mv: the internal reference voltage + * @channels: channels specification + * @num_channels: number of channels + * @dual_channels: channels specification in dual mode + * @num_dual_channels: number of channels in dual mode + */ +struct ad7887_chip_info { + u16 int_vref_mv; + const struct iio_chan_spec *channels; + unsigned int num_channels; + const struct iio_chan_spec *dual_channels; + unsigned int num_dual_channels; +}; + +struct ad7887_state { + struct spi_device *spi; + const struct ad7887_chip_info *chip_info; + struct regulator *reg; + struct spi_transfer xfer[4]; + struct spi_message msg[3]; + struct spi_message *ring_msg; + unsigned char tx_cmd_buf[4]; + + /* + * DMA (thus cache coherency maintenance) may require the + * transfer buffers to live in their own cache lines. + * Buffer needs to be large enough to hold two 16 bit samples and a + * 64 bit aligned 64 bit timestamp. + */ + unsigned char data[ALIGN(4, sizeof(s64)) + sizeof(s64)] __aligned(IIO_DMA_MINALIGN); +}; + +enum ad7887_supported_device_ids { + ID_AD7887 +}; + +static int ad7887_ring_preenable(struct iio_dev *indio_dev) +{ + struct ad7887_state *st = iio_priv(indio_dev); + + /* We know this is a single long so can 'cheat' */ + switch (*indio_dev->active_scan_mask) { + case (1 << 0): + st->ring_msg = &st->msg[AD7887_CH0]; + break; + case (1 << 1): + st->ring_msg = &st->msg[AD7887_CH1]; + /* Dummy read: push CH1 setting down to hardware */ + spi_sync(st->spi, st->ring_msg); + break; + case ((1 << 1) | (1 << 0)): + st->ring_msg = &st->msg[AD7887_CH0_CH1]; + break; + } + + return 0; +} + +static int ad7887_ring_postdisable(struct iio_dev *indio_dev) +{ + struct ad7887_state *st = iio_priv(indio_dev); + + /* dummy read: restore default CH0 settings */ + return spi_sync(st->spi, &st->msg[AD7887_CH0]); +} + +static irqreturn_t ad7887_trigger_handler(int irq, void *p) +{ + struct iio_poll_func *pf = p; + struct iio_dev *indio_dev = pf->indio_dev; + struct ad7887_state *st = iio_priv(indio_dev); + int b_sent; + + b_sent = spi_sync(st->spi, st->ring_msg); + if (b_sent) + goto done; + + iio_push_to_buffers_with_timestamp(indio_dev, st->data, + iio_get_time_ns(indio_dev)); +done: + iio_trigger_notify_done(indio_dev->trig); + + return IRQ_HANDLED; +} + +static const struct iio_buffer_setup_ops ad7887_ring_setup_ops = { + .preenable = &ad7887_ring_preenable, + .postdisable = &ad7887_ring_postdisable, +}; + +static int ad7887_scan_direct(struct ad7887_state *st, unsigned ch) +{ + int ret = spi_sync(st->spi, &st->msg[ch]); + if (ret) + return ret; + + return (st->data[(ch * 2)] << 8) | st->data[(ch * 2) + 1]; +} + +static int ad7887_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, + int *val2, + long m) +{ + int ret; + struct ad7887_state *st = iio_priv(indio_dev); + + switch (m) { + case IIO_CHAN_INFO_RAW: + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; + ret = ad7887_scan_direct(st, chan->address); + iio_device_release_direct(indio_dev); + + if (ret < 0) + return ret; + *val = ret >> chan->scan_type.shift; + *val &= GENMASK(chan->scan_type.realbits - 1, 0); + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + if (st->reg) { + *val = regulator_get_voltage(st->reg); + if (*val < 0) + return *val; + *val /= 1000; + } else { + *val = st->chip_info->int_vref_mv; + } + + *val2 = chan->scan_type.realbits; + + return IIO_VAL_FRACTIONAL_LOG2; + } + return -EINVAL; +} + +#define AD7887_CHANNEL(x) { \ + .type = IIO_VOLTAGE, \ + .indexed = 1, \ + .channel = (x), \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ + .address = (x), \ + .scan_index = (x), \ + .scan_type = { \ + .sign = 'u', \ + .realbits = 12, \ + .storagebits = 16, \ + .shift = 0, \ + .endianness = IIO_BE, \ + }, \ +} + +static const struct iio_chan_spec ad7887_channels[] = { + AD7887_CHANNEL(0), + IIO_CHAN_SOFT_TIMESTAMP(1), +}; + +static const struct iio_chan_spec ad7887_dual_channels[] = { + AD7887_CHANNEL(0), + AD7887_CHANNEL(1), + IIO_CHAN_SOFT_TIMESTAMP(2), +}; + +static const struct ad7887_chip_info ad7887_chip_info_tbl[] = { + /* + * More devices added in future + */ + [ID_AD7887] = { + .channels = ad7887_channels, + .num_channels = ARRAY_SIZE(ad7887_channels), + .dual_channels = ad7887_dual_channels, + .num_dual_channels = ARRAY_SIZE(ad7887_dual_channels), + .int_vref_mv = 2500, + }, +}; + +static const struct iio_info ad7887_info = { + .read_raw = &ad7887_read_raw, +}; + +static void ad7887_reg_disable(void *data) +{ + struct regulator *reg = data; + + regulator_disable(reg); +} + +static int ad7887_probe(struct spi_device *spi) +{ + const struct ad7887_platform_data *pdata = dev_get_platdata(&spi->dev); + struct ad7887_state *st; + struct iio_dev *indio_dev; + uint8_t mode; + int ret; + + indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st)); + if (indio_dev == NULL) + return -ENOMEM; + + st = iio_priv(indio_dev); + + st->reg = devm_regulator_get_optional(&spi->dev, "vref"); + if (IS_ERR(st->reg)) { + if (PTR_ERR(st->reg) != -ENODEV) + return PTR_ERR(st->reg); + + st->reg = NULL; + } + + if (st->reg) { + ret = regulator_enable(st->reg); + if (ret) + return ret; + + ret = devm_add_action_or_reset(&spi->dev, ad7887_reg_disable, st->reg); + if (ret) + return ret; + } + + st->chip_info = + &ad7887_chip_info_tbl[spi_get_device_id(spi)->driver_data]; + + st->spi = spi; + + indio_dev->name = spi_get_device_id(spi)->name; + indio_dev->info = &ad7887_info; + indio_dev->modes = INDIO_DIRECT_MODE; + + /* Setup default message */ + + mode = AD7887_PM_MODE4; + if (!st->reg) + mode |= AD7887_REF_DIS; + if (pdata && pdata->en_dual) + mode |= AD7887_DUAL; + + st->tx_cmd_buf[0] = AD7887_CH_AIN0 | mode; + + st->xfer[0].rx_buf = &st->data[0]; + st->xfer[0].tx_buf = &st->tx_cmd_buf[0]; + st->xfer[0].len = 2; + + spi_message_init(&st->msg[AD7887_CH0]); + spi_message_add_tail(&st->xfer[0], &st->msg[AD7887_CH0]); + + if (pdata && pdata->en_dual) { + st->tx_cmd_buf[2] = AD7887_CH_AIN1 | mode; + + st->xfer[1].rx_buf = &st->data[0]; + st->xfer[1].tx_buf = &st->tx_cmd_buf[2]; + st->xfer[1].len = 2; + + st->xfer[2].rx_buf = &st->data[2]; + st->xfer[2].tx_buf = &st->tx_cmd_buf[0]; + st->xfer[2].len = 2; + + spi_message_init(&st->msg[AD7887_CH0_CH1]); + spi_message_add_tail(&st->xfer[1], &st->msg[AD7887_CH0_CH1]); + spi_message_add_tail(&st->xfer[2], &st->msg[AD7887_CH0_CH1]); + + st->xfer[3].rx_buf = &st->data[2]; + st->xfer[3].tx_buf = &st->tx_cmd_buf[2]; + st->xfer[3].len = 2; + + spi_message_init(&st->msg[AD7887_CH1]); + spi_message_add_tail(&st->xfer[3], &st->msg[AD7887_CH1]); + + indio_dev->channels = st->chip_info->dual_channels; + indio_dev->num_channels = st->chip_info->num_dual_channels; + } else { + indio_dev->channels = st->chip_info->channels; + indio_dev->num_channels = st->chip_info->num_channels; + } + + ret = devm_iio_triggered_buffer_setup(&spi->dev, indio_dev, + &iio_pollfunc_store_time, + &ad7887_trigger_handler, &ad7887_ring_setup_ops); + if (ret) + return ret; + + return devm_iio_device_register(&spi->dev, indio_dev); +} + +static const struct spi_device_id ad7887_id[] = { + { .name = "ad7887", .driver_data = ID_AD7887 }, + { } +}; +MODULE_DEVICE_TABLE(spi, ad7887_id); + +static struct spi_driver ad7887_driver = { + .driver = { + .name = "ad7887", + }, + .probe = ad7887_probe, + .id_table = ad7887_id, +}; +module_spi_driver(ad7887_driver); + +MODULE_AUTHOR("Michael Hennerich "); +MODULE_DESCRIPTION("Analog Devices AD7887 ADC"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/adc/ad7923.c b/drivers/iio/adc/ad7923.c new file mode 100644 index 000000000..44e014da9 --- /dev/null +++ b/drivers/iio/adc/ad7923.c @@ -0,0 +1,399 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * AD7904/AD7914/AD7923/AD7924/AD7908/AD7918/AD7928 SPI ADC driver + * + * Copyright 2011 Analog Devices Inc (from AD7923 Driver) + * Copyright 2012 CS Systemes d'Information + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#define AD7923_WRITE_CR BIT(11) /* write control register */ +#define AD7923_RANGE BIT(1) /* range to REFin */ +#define AD7923_CODING BIT(0) /* coding is straight binary */ +#define AD7923_PM_MODE_AS (1) /* auto shutdown */ +#define AD7923_PM_MODE_FS (2) /* full shutdown */ +#define AD7923_PM_MODE_OPS (3) /* normal operation */ +#define AD7923_SEQUENCE_OFF (0) /* no sequence function */ +#define AD7923_SEQUENCE_PROTECT (2) /* no interrupt write cycle */ +#define AD7923_SEQUENCE_ON (3) /* continuous sequence */ + + +#define AD7923_PM_MODE_WRITE(mode) ((mode) << 4) /* write mode */ +#define AD7923_CHANNEL_WRITE(channel) ((channel) << 6) /* write channel */ +#define AD7923_SEQUENCE_WRITE(sequence) ((((sequence) & 1) << 3) \ + + (((sequence) & 2) << 9)) + /* write sequence function */ +/* left shift for CR : bit 11 transmit in first */ +#define AD7923_SHIFT_REGISTER 4 + +/* val = value, dec = left shift, bits = number of bits of the mask */ +#define EXTRACT(val, dec, bits) (((val) >> (dec)) & ((1 << (bits)) - 1)) + +struct ad7923_state { + struct spi_device *spi; + struct spi_transfer ring_xfer[9]; + struct spi_transfer scan_single_xfer[2]; + struct spi_message ring_msg; + struct spi_message scan_single_msg; + + struct regulator *reg; + + unsigned int settings; + + /* + * DMA (thus cache coherency maintenance) may require the + * transfer buffers to live in their own cache lines. + * Ensure rx_buf can be directly used in iio_push_to_buffers_with_timetamp + * Length = 8 channels + 4 extra for 8 byte timestamp + */ + __be16 rx_buf[12] __aligned(IIO_DMA_MINALIGN); + __be16 tx_buf[8]; +}; + +struct ad7923_chip_info { + const struct iio_chan_spec *channels; + unsigned int num_channels; +}; + +enum ad7923_id { + AD7904, + AD7914, + AD7924, + AD7908, + AD7918, + AD7928 +}; + +#define AD7923_V_CHAN(index, bits) \ + { \ + .type = IIO_VOLTAGE, \ + .indexed = 1, \ + .channel = index, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ + .address = index, \ + .scan_index = index, \ + .scan_type = { \ + .sign = 'u', \ + .realbits = (bits), \ + .storagebits = 16, \ + .shift = 12 - (bits), \ + .endianness = IIO_BE, \ + }, \ + } + +#define DECLARE_AD7923_CHANNELS(name, bits) \ +const struct iio_chan_spec name ## _channels[] = { \ + AD7923_V_CHAN(0, bits), \ + AD7923_V_CHAN(1, bits), \ + AD7923_V_CHAN(2, bits), \ + AD7923_V_CHAN(3, bits), \ + IIO_CHAN_SOFT_TIMESTAMP(4), \ +} + +#define DECLARE_AD7908_CHANNELS(name, bits) \ +const struct iio_chan_spec name ## _channels[] = { \ + AD7923_V_CHAN(0, bits), \ + AD7923_V_CHAN(1, bits), \ + AD7923_V_CHAN(2, bits), \ + AD7923_V_CHAN(3, bits), \ + AD7923_V_CHAN(4, bits), \ + AD7923_V_CHAN(5, bits), \ + AD7923_V_CHAN(6, bits), \ + AD7923_V_CHAN(7, bits), \ + IIO_CHAN_SOFT_TIMESTAMP(8), \ +} + +static DECLARE_AD7923_CHANNELS(ad7904, 8); +static DECLARE_AD7923_CHANNELS(ad7914, 10); +static DECLARE_AD7923_CHANNELS(ad7924, 12); +static DECLARE_AD7908_CHANNELS(ad7908, 8); +static DECLARE_AD7908_CHANNELS(ad7918, 10); +static DECLARE_AD7908_CHANNELS(ad7928, 12); + +static const struct ad7923_chip_info ad7923_chip_info[] = { + [AD7904] = { + .channels = ad7904_channels, + .num_channels = ARRAY_SIZE(ad7904_channels), + }, + [AD7914] = { + .channels = ad7914_channels, + .num_channels = ARRAY_SIZE(ad7914_channels), + }, + [AD7924] = { + .channels = ad7924_channels, + .num_channels = ARRAY_SIZE(ad7924_channels), + }, + [AD7908] = { + .channels = ad7908_channels, + .num_channels = ARRAY_SIZE(ad7908_channels), + }, + [AD7918] = { + .channels = ad7918_channels, + .num_channels = ARRAY_SIZE(ad7918_channels), + }, + [AD7928] = { + .channels = ad7928_channels, + .num_channels = ARRAY_SIZE(ad7928_channels), + }, +}; + +/* + * ad7923_update_scan_mode() setup the spi transfer buffer for the new scan mask + */ +static int ad7923_update_scan_mode(struct iio_dev *indio_dev, + const unsigned long *active_scan_mask) +{ + struct ad7923_state *st = iio_priv(indio_dev); + int i, cmd, len; + + len = 0; + /* + * For this driver the last channel is always the software timestamp so + * skip that one. + */ + for_each_set_bit(i, active_scan_mask, indio_dev->num_channels - 1) { + cmd = AD7923_WRITE_CR | AD7923_CHANNEL_WRITE(i) | + AD7923_SEQUENCE_WRITE(AD7923_SEQUENCE_OFF) | + st->settings; + cmd <<= AD7923_SHIFT_REGISTER; + st->tx_buf[len++] = cpu_to_be16(cmd); + } + /* build spi ring message */ + st->ring_xfer[0].tx_buf = &st->tx_buf[0]; + st->ring_xfer[0].len = len; + st->ring_xfer[0].cs_change = 1; + + spi_message_init(&st->ring_msg); + spi_message_add_tail(&st->ring_xfer[0], &st->ring_msg); + + for (i = 0; i < len; i++) { + st->ring_xfer[i + 1].rx_buf = &st->rx_buf[i]; + st->ring_xfer[i + 1].len = 2; + st->ring_xfer[i + 1].cs_change = 1; + spi_message_add_tail(&st->ring_xfer[i + 1], &st->ring_msg); + } + /* make sure last transfer cs_change is not set */ + st->ring_xfer[i + 1].cs_change = 0; + + return 0; +} + +static irqreturn_t ad7923_trigger_handler(int irq, void *p) +{ + struct iio_poll_func *pf = p; + struct iio_dev *indio_dev = pf->indio_dev; + struct ad7923_state *st = iio_priv(indio_dev); + int b_sent; + + b_sent = spi_sync(st->spi, &st->ring_msg); + if (b_sent) + goto done; + + iio_push_to_buffers_with_ts(indio_dev, st->rx_buf, sizeof(st->rx_buf), + iio_get_time_ns(indio_dev)); + +done: + iio_trigger_notify_done(indio_dev->trig); + + return IRQ_HANDLED; +} + +static int ad7923_scan_direct(struct ad7923_state *st, unsigned int ch) +{ + int ret, cmd; + + cmd = AD7923_WRITE_CR | AD7923_CHANNEL_WRITE(ch) | + AD7923_SEQUENCE_WRITE(AD7923_SEQUENCE_OFF) | + st->settings; + cmd <<= AD7923_SHIFT_REGISTER; + st->tx_buf[0] = cpu_to_be16(cmd); + + ret = spi_sync(st->spi, &st->scan_single_msg); + if (ret) + return ret; + + return be16_to_cpu(st->rx_buf[0]); +} + +static int ad7923_get_range(struct ad7923_state *st) +{ + int vref; + + vref = regulator_get_voltage(st->reg); + if (vref < 0) + return vref; + + vref /= 1000; + + if (!(st->settings & AD7923_RANGE)) + vref *= 2; + + return vref; +} + +static int ad7923_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, + int *val2, + long m) +{ + int ret; + struct ad7923_state *st = iio_priv(indio_dev); + + switch (m) { + case IIO_CHAN_INFO_RAW: + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; + ret = ad7923_scan_direct(st, chan->address); + iio_device_release_direct(indio_dev); + + if (ret < 0) + return ret; + + if (chan->address == EXTRACT(ret, 12, 4)) + *val = EXTRACT(ret, chan->scan_type.shift, + chan->scan_type.realbits); + else + return -EIO; + + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + ret = ad7923_get_range(st); + if (ret < 0) + return ret; + *val = ret; + *val2 = chan->scan_type.realbits; + return IIO_VAL_FRACTIONAL_LOG2; + } + return -EINVAL; +} + +static const struct iio_info ad7923_info = { + .read_raw = &ad7923_read_raw, + .update_scan_mode = ad7923_update_scan_mode, +}; + +static void ad7923_regulator_disable(void *data) +{ + struct ad7923_state *st = data; + + regulator_disable(st->reg); +} + +static int ad7923_probe(struct spi_device *spi) +{ + u32 ad7923_range = AD7923_RANGE; + struct ad7923_state *st; + struct iio_dev *indio_dev; + const struct ad7923_chip_info *info; + int ret; + + indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st)); + if (!indio_dev) + return -ENOMEM; + + st = iio_priv(indio_dev); + + if (device_property_read_bool(&spi->dev, "adi,range-double")) + ad7923_range = 0; + + st->spi = spi; + st->settings = AD7923_CODING | ad7923_range | + AD7923_PM_MODE_WRITE(AD7923_PM_MODE_OPS); + + info = &ad7923_chip_info[spi_get_device_id(spi)->driver_data]; + + indio_dev->name = spi_get_device_id(spi)->name; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = info->channels; + indio_dev->num_channels = info->num_channels; + indio_dev->info = &ad7923_info; + + /* Setup default message */ + + st->scan_single_xfer[0].tx_buf = &st->tx_buf[0]; + st->scan_single_xfer[0].len = 2; + st->scan_single_xfer[0].cs_change = 1; + st->scan_single_xfer[1].rx_buf = &st->rx_buf[0]; + st->scan_single_xfer[1].len = 2; + + spi_message_init(&st->scan_single_msg); + spi_message_add_tail(&st->scan_single_xfer[0], &st->scan_single_msg); + spi_message_add_tail(&st->scan_single_xfer[1], &st->scan_single_msg); + + st->reg = devm_regulator_get(&spi->dev, "refin"); + if (IS_ERR(st->reg)) + return PTR_ERR(st->reg); + + ret = regulator_enable(st->reg); + if (ret) + return ret; + + ret = devm_add_action_or_reset(&spi->dev, ad7923_regulator_disable, st); + if (ret) + return ret; + + ret = devm_iio_triggered_buffer_setup(&spi->dev, indio_dev, NULL, + &ad7923_trigger_handler, NULL); + if (ret) + return ret; + + return devm_iio_device_register(&spi->dev, indio_dev); +} + +static const struct spi_device_id ad7923_id[] = { + { .name = "ad7904", .driver_data = AD7904 }, + { .name = "ad7914", .driver_data = AD7914 }, + { .name = "ad7923", .driver_data = AD7924 }, + { .name = "ad7924", .driver_data = AD7924 }, + { .name = "ad7908", .driver_data = AD7908 }, + { .name = "ad7918", .driver_data = AD7918 }, + { .name = "ad7928", .driver_data = AD7928 }, + { } +}; +MODULE_DEVICE_TABLE(spi, ad7923_id); + +static const struct of_device_id ad7923_of_match[] = { + { .compatible = "adi,ad7904", }, + { .compatible = "adi,ad7914", }, + { .compatible = "adi,ad7923", }, + { .compatible = "adi,ad7924", }, + { .compatible = "adi,ad7908", }, + { .compatible = "adi,ad7918", }, + { .compatible = "adi,ad7928", }, + { } +}; +MODULE_DEVICE_TABLE(of, ad7923_of_match); + +static struct spi_driver ad7923_driver = { + .driver = { + .name = "ad7923", + .of_match_table = ad7923_of_match, + }, + .probe = ad7923_probe, + .id_table = ad7923_id, +}; +module_spi_driver(ad7923_driver); + +MODULE_AUTHOR("Michael Hennerich "); +MODULE_AUTHOR("Patrick Vasseur "); +MODULE_DESCRIPTION("Analog Devices AD7923 and similar ADC"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/adc/ad7944.c b/drivers/iio/adc/ad7944.c new file mode 100644 index 000000000..12656d3cd --- /dev/null +++ b/drivers/iio/adc/ad7944.c @@ -0,0 +1,889 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Analog Devices AD7944/85/86 PulSAR ADC family driver. + * + * Copyright 2024 Analog Devices, Inc. + * Copyright 2024 BayLibre, SAS + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#define AD7944_INTERNAL_REF_MV 4096 + +struct ad7944_timing_spec { + /* Normal mode max conversion time (t_{CONV}). */ + unsigned int conv_ns; + /* TURBO mode max conversion time (t_{CONV}). */ + unsigned int turbo_conv_ns; +}; + +enum ad7944_spi_mode { + /* datasheet calls this "4-wire mode" */ + AD7944_SPI_MODE_DEFAULT, + /* datasheet calls this "3-wire mode" (not related to SPI_3WIRE!) */ + AD7944_SPI_MODE_SINGLE, + /* datasheet calls this "chain mode" */ + AD7944_SPI_MODE_CHAIN, +}; + +/* maps adi,spi-mode property value to enum */ +static const char * const ad7944_spi_modes[] = { + [AD7944_SPI_MODE_DEFAULT] = "", + [AD7944_SPI_MODE_SINGLE] = "single", + [AD7944_SPI_MODE_CHAIN] = "chain", +}; + +struct ad7944_adc { + struct spi_device *spi; + enum ad7944_spi_mode spi_mode; + struct spi_transfer xfers[3]; + struct spi_message msg; + struct spi_transfer offload_xfers[2]; + struct spi_message offload_msg; + struct spi_offload *offload; + struct spi_offload_trigger *offload_trigger; + unsigned long offload_trigger_hz; + int sample_freq_range[3]; + void *chain_mode_buf; + /* Chip-specific timing specifications. */ + const struct ad7944_timing_spec *timing_spec; + /* GPIO connected to CNV pin. */ + struct gpio_desc *cnv; + /* Optional GPIO to enable turbo mode. */ + struct gpio_desc *turbo; + /* Indicates TURBO is hard-wired to be always enabled. */ + bool always_turbo; + /* Reference voltage (millivolts). */ + unsigned int ref_mv; + + /* + * DMA (thus cache coherency maintenance) requires the + * transfer buffers to live in their own cache lines. + */ + struct { + union { + u16 u16; + u32 u32; + } raw; + aligned_s64 timestamp; + } sample __aligned(IIO_DMA_MINALIGN); +}; + +/* quite time before CNV rising edge */ +#define AD7944_T_QUIET_NS 20 +/* minimum CNV high time to trigger conversion */ +#define AD7944_T_CNVH_NS 10 + +static const struct ad7944_timing_spec ad7944_timing_spec = { + .conv_ns = 420, + .turbo_conv_ns = 320, +}; + +static const struct ad7944_timing_spec ad7986_timing_spec = { + .conv_ns = 500, + .turbo_conv_ns = 400, +}; + +struct ad7944_chip_info { + const char *name; + const struct ad7944_timing_spec *timing_spec; + u32 max_sample_rate_hz; + const struct iio_chan_spec channels[2]; + const struct iio_chan_spec offload_channels[1]; +}; + +/* get number of bytes for SPI xfer */ +#define AD7944_SPI_BYTES(scan_type) ((scan_type).realbits > 16 ? 4 : 2) + +/* + * AD7944_DEFINE_CHIP_INFO - Define a chip info structure for a specific chip + * @_name: The name of the chip + * @_ts: The timing specification for the chip + * @_max: The maximum sample rate in Hz + * @_bits: The number of bits in the conversion result + * @_diff: Whether the chip is true differential or not + */ +#define AD7944_DEFINE_CHIP_INFO(_name, _ts, _max, _bits, _diff) \ +static const struct ad7944_chip_info _name##_chip_info = { \ + .name = #_name, \ + .timing_spec = &_ts##_timing_spec, \ + .max_sample_rate_hz = _max, \ + .channels = { \ + { \ + .type = IIO_VOLTAGE, \ + .indexed = 1, \ + .differential = _diff, \ + .channel = 0, \ + .channel2 = _diff ? 1 : 0, \ + .scan_index = 0, \ + .scan_type.sign = _diff ? 's' : 'u', \ + .scan_type.realbits = _bits, \ + .scan_type.storagebits = _bits > 16 ? 32 : 16, \ + .scan_type.endianness = IIO_CPU, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) \ + | BIT(IIO_CHAN_INFO_SCALE), \ + }, \ + IIO_CHAN_SOFT_TIMESTAMP(1), \ + }, \ + .offload_channels = { \ + { \ + .type = IIO_VOLTAGE, \ + .indexed = 1, \ + .differential = _diff, \ + .channel = 0, \ + .channel2 = _diff ? 1 : 0, \ + .scan_index = 0, \ + .scan_type.sign = _diff ? 's' : 'u', \ + .scan_type.realbits = _bits, \ + .scan_type.storagebits = 32, \ + .scan_type.endianness = IIO_CPU, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) \ + | BIT(IIO_CHAN_INFO_SCALE) \ + | BIT(IIO_CHAN_INFO_SAMP_FREQ), \ + .info_mask_separate_available = \ + BIT(IIO_CHAN_INFO_SAMP_FREQ), \ + }, \ + }, \ +} + +/* + * Notes on the offload channels: + * - There is no soft timestamp since everything is done in hardware. + * - There is a sampling frequency attribute added. This controls the SPI + * offload trigger. + * - The storagebits value depends on the SPI offload provider. Currently there + * is only one supported provider, namely the ADI PULSAR ADC HDL project, + * which always uses 32-bit words for data values, even for <= 16-bit ADCs. + * So the value is just hardcoded to 32 for now. + */ + +/* pseudo-differential with ground sense */ +AD7944_DEFINE_CHIP_INFO(ad7944, ad7944, 2.5 * MEGA, 14, 0); +AD7944_DEFINE_CHIP_INFO(ad7985, ad7944, 2.5 * MEGA, 16, 0); +/* fully differential */ +AD7944_DEFINE_CHIP_INFO(ad7986, ad7986, 2 * MEGA, 18, 1); + +static int ad7944_3wire_cs_mode_init_msg(struct device *dev, struct ad7944_adc *adc, + const struct iio_chan_spec *chan) +{ + unsigned int t_conv_ns = adc->always_turbo ? adc->timing_spec->turbo_conv_ns + : adc->timing_spec->conv_ns; + struct spi_transfer *xfers = adc->xfers; + + /* + * CS is tied to CNV and we need a low to high transition to start the + * conversion, so place CNV low for t_QUIET to prepare for this. + */ + xfers[0].delay.value = AD7944_T_QUIET_NS; + xfers[0].delay.unit = SPI_DELAY_UNIT_NSECS; + + /* + * CS has to be high for full conversion time to avoid triggering the + * busy indication. + */ + xfers[1].cs_off = 1; + xfers[1].delay.value = t_conv_ns; + xfers[1].delay.unit = SPI_DELAY_UNIT_NSECS; + + /* Then we can read the data during the acquisition phase */ + xfers[2].rx_buf = &adc->sample.raw; + xfers[2].len = AD7944_SPI_BYTES(chan->scan_type); + xfers[2].bits_per_word = chan->scan_type.realbits; + + spi_message_init_with_transfers(&adc->msg, xfers, 3); + + return devm_spi_optimize_message(dev, adc->spi, &adc->msg); +} + +static int ad7944_4wire_mode_init_msg(struct device *dev, struct ad7944_adc *adc, + const struct iio_chan_spec *chan) +{ + unsigned int t_conv_ns = adc->always_turbo ? adc->timing_spec->turbo_conv_ns + : adc->timing_spec->conv_ns; + struct spi_transfer *xfers = adc->xfers; + + /* + * CS has to be high for full conversion time to avoid triggering the + * busy indication. + */ + xfers[0].cs_off = 1; + xfers[0].delay.value = t_conv_ns; + xfers[0].delay.unit = SPI_DELAY_UNIT_NSECS; + + xfers[1].rx_buf = &adc->sample.raw; + xfers[1].len = AD7944_SPI_BYTES(chan->scan_type); + xfers[1].bits_per_word = chan->scan_type.realbits; + + spi_message_init_with_transfers(&adc->msg, xfers, 2); + + return devm_spi_optimize_message(dev, adc->spi, &adc->msg); +} + +static int ad7944_chain_mode_init_msg(struct device *dev, struct ad7944_adc *adc, + const struct iio_chan_spec *chan, + u32 n_chain_dev) +{ + struct spi_transfer *xfers = adc->xfers; + + /* + * NB: SCLK has to be low before we toggle CS to avoid triggering the + * busy indication. + */ + if (adc->spi->mode & SPI_CPOL) + return dev_err_probe(dev, -EINVAL, + "chain mode requires ~SPI_CPOL\n"); + + /* + * We only support CNV connected to CS in chain mode and we need CNV + * to be high during the transfer to trigger the conversion. + */ + if (!(adc->spi->mode & SPI_CS_HIGH)) + return dev_err_probe(dev, -EINVAL, + "chain mode requires SPI_CS_HIGH\n"); + + /* CNV has to be high for full conversion time before reading data. */ + xfers[0].delay.value = adc->timing_spec->conv_ns; + xfers[0].delay.unit = SPI_DELAY_UNIT_NSECS; + + xfers[1].rx_buf = adc->chain_mode_buf; + xfers[1].len = AD7944_SPI_BYTES(chan->scan_type) * n_chain_dev; + xfers[1].bits_per_word = chan->scan_type.realbits; + + spi_message_init_with_transfers(&adc->msg, xfers, 2); + + return devm_spi_optimize_message(dev, adc->spi, &adc->msg); +} + +/* + * Unlike ad7944_3wire_cs_mode_init_msg(), this creates a message that reads + * during the conversion phase instead of the acquisition phase when reading + * a sample from the ADC. This is needed to be able to read at the maximum + * sample rate. It requires the SPI controller to have offload support and a + * high enough SCLK rate to read the sample during the conversion phase. + */ +static int ad7944_3wire_cs_mode_init_offload_msg(struct device *dev, + struct ad7944_adc *adc, + const struct iio_chan_spec *chan) +{ + struct spi_transfer *xfers = adc->offload_xfers; + int ret; + + /* + * CS is tied to CNV and we need a low to high transition to start the + * conversion, so place CNV low for t_QUIET to prepare for this. + */ + xfers[0].delay.value = AD7944_T_QUIET_NS; + xfers[0].delay.unit = SPI_DELAY_UNIT_NSECS; + /* CNV has to be high for a minimum time to trigger conversion. */ + xfers[0].cs_change = 1; + xfers[0].cs_change_delay.value = AD7944_T_CNVH_NS; + xfers[0].cs_change_delay.unit = SPI_DELAY_UNIT_NSECS; + + /* Then we can read the previous sample during the conversion phase */ + xfers[1].offload_flags = SPI_OFFLOAD_XFER_RX_STREAM; + xfers[1].len = AD7944_SPI_BYTES(chan->scan_type); + xfers[1].bits_per_word = chan->scan_type.realbits; + + spi_message_init_with_transfers(&adc->offload_msg, xfers, + ARRAY_SIZE(adc->offload_xfers)); + + adc->offload_msg.offload = adc->offload; + + ret = devm_spi_optimize_message(dev, adc->spi, &adc->offload_msg); + if (ret) + return dev_err_probe(dev, ret, "failed to prepare offload msg\n"); + + return 0; +} + +/** + * ad7944_convert_and_acquire - Perform a single conversion and acquisition + * @adc: The ADC device structure + * Return: 0 on success, a negative error code on failure + * + * Perform a conversion and acquisition of a single sample using the + * pre-optimized adc->msg. + * + * Upon successful return adc->sample.raw will contain the conversion result + * (or adc->chain_mode_buf if the device is using chain mode). + */ +static int ad7944_convert_and_acquire(struct ad7944_adc *adc) +{ + int ret; + + /* + * In 4-wire mode, the CNV line is held high for the entire conversion + * and acquisition process. In other modes adc->cnv is NULL and is + * ignored (CS is wired to CNV in those cases). + */ + gpiod_set_value_cansleep(adc->cnv, 1); + ret = spi_sync(adc->spi, &adc->msg); + gpiod_set_value_cansleep(adc->cnv, 0); + + return ret; +} + +static int ad7944_single_conversion(struct ad7944_adc *adc, + const struct iio_chan_spec *chan, + int *val) +{ + int ret; + + ret = ad7944_convert_and_acquire(adc); + if (ret) + return ret; + + if (adc->spi_mode == AD7944_SPI_MODE_CHAIN) { + if (chan->scan_type.realbits > 16) + *val = ((u32 *)adc->chain_mode_buf)[chan->scan_index]; + else + *val = ((u16 *)adc->chain_mode_buf)[chan->scan_index]; + } else { + if (chan->scan_type.realbits > 16) + *val = adc->sample.raw.u32; + else + *val = adc->sample.raw.u16; + } + + if (chan->scan_type.sign == 's') + *val = sign_extend32(*val, chan->scan_type.realbits - 1); + else + *val &= GENMASK(chan->scan_type.realbits - 1, 0); + + return IIO_VAL_INT; +} + +static int ad7944_read_avail(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + const int **vals, int *type, int *length, + long mask) +{ + struct ad7944_adc *adc = iio_priv(indio_dev); + + switch (mask) { + case IIO_CHAN_INFO_SAMP_FREQ: + *vals = adc->sample_freq_range; + *type = IIO_VAL_INT; + return IIO_AVAIL_RANGE; + default: + return -EINVAL; + } +} + +static int ad7944_read_raw(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + int *val, int *val2, long info) +{ + struct ad7944_adc *adc = iio_priv(indio_dev); + int ret; + + switch (info) { + case IIO_CHAN_INFO_RAW: + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; + + ret = ad7944_single_conversion(adc, chan, val); + iio_device_release_direct(indio_dev); + return ret; + + case IIO_CHAN_INFO_SCALE: + switch (chan->type) { + case IIO_VOLTAGE: + *val = adc->ref_mv; + + if (chan->scan_type.sign == 's') + *val2 = chan->scan_type.realbits - 1; + else + *val2 = chan->scan_type.realbits; + + return IIO_VAL_FRACTIONAL_LOG2; + default: + return -EINVAL; + } + + case IIO_CHAN_INFO_SAMP_FREQ: + *val = adc->offload_trigger_hz; + return IIO_VAL_INT; + + default: + return -EINVAL; + } +} + +static int ad7944_set_sample_freq(struct ad7944_adc *adc, int val) +{ + struct spi_offload_trigger_config config = { + .type = SPI_OFFLOAD_TRIGGER_PERIODIC, + .periodic = { + .frequency_hz = val, + }, + }; + int ret; + + ret = spi_offload_trigger_validate(adc->offload_trigger, &config); + if (ret) + return ret; + + adc->offload_trigger_hz = config.periodic.frequency_hz; + + return 0; +} + +static int ad7944_write_raw(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + int val, int val2, long info) +{ + struct ad7944_adc *adc = iio_priv(indio_dev); + + switch (info) { + case IIO_CHAN_INFO_SAMP_FREQ: + if (val < 1 || val > adc->sample_freq_range[2]) + return -EINVAL; + + return ad7944_set_sample_freq(adc, val); + default: + return -EINVAL; + } +} + +static int ad7944_write_raw_get_fmt(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + long mask) +{ + switch (mask) { + case IIO_CHAN_INFO_SAMP_FREQ: + return IIO_VAL_INT; + default: + return IIO_VAL_INT_PLUS_MICRO; + } +} + +static const struct iio_info ad7944_iio_info = { + .read_avail = &ad7944_read_avail, + .read_raw = &ad7944_read_raw, + .write_raw = &ad7944_write_raw, + .write_raw_get_fmt = &ad7944_write_raw_get_fmt, +}; + +static int ad7944_offload_buffer_postenable(struct iio_dev *indio_dev) +{ + struct ad7944_adc *adc = iio_priv(indio_dev); + struct spi_offload_trigger_config config = { + .type = SPI_OFFLOAD_TRIGGER_PERIODIC, + .periodic = { + .frequency_hz = adc->offload_trigger_hz, + }, + }; + int ret; + + gpiod_set_value_cansleep(adc->turbo, 1); + + ret = spi_offload_trigger_enable(adc->offload, adc->offload_trigger, + &config); + if (ret) + gpiod_set_value_cansleep(adc->turbo, 0); + + return ret; +} + +static int ad7944_offload_buffer_predisable(struct iio_dev *indio_dev) +{ + struct ad7944_adc *adc = iio_priv(indio_dev); + + spi_offload_trigger_disable(adc->offload, adc->offload_trigger); + gpiod_set_value_cansleep(adc->turbo, 0); + + return 0; +} + +static const struct iio_buffer_setup_ops ad7944_offload_buffer_setup_ops = { + .postenable = &ad7944_offload_buffer_postenable, + .predisable = &ad7944_offload_buffer_predisable, +}; + +static irqreturn_t ad7944_trigger_handler(int irq, void *p) +{ + struct iio_poll_func *pf = p; + struct iio_dev *indio_dev = pf->indio_dev; + struct ad7944_adc *adc = iio_priv(indio_dev); + int ret; + + ret = ad7944_convert_and_acquire(adc); + if (ret) + goto out; + + if (adc->spi_mode == AD7944_SPI_MODE_CHAIN) + iio_push_to_buffers_with_timestamp(indio_dev, adc->chain_mode_buf, + pf->timestamp); + else + iio_push_to_buffers_with_timestamp(indio_dev, &adc->sample.raw, + pf->timestamp); + +out: + iio_trigger_notify_done(indio_dev->trig); + + return IRQ_HANDLED; +} + +/** + * ad7944_chain_mode_alloc - allocate and initialize channel specs and buffers + * for daisy-chained devices + * @dev: The device for devm_ functions + * @chan_template: The channel template for the devices (array of 2 channels + * voltage and timestamp) + * @n_chain_dev: The number of devices in the chain + * @chain_chan: Pointer to receive the allocated channel specs + * @chain_mode_buf: Pointer to receive the allocated rx buffer + * @chain_scan_masks: Pointer to receive the allocated scan masks + * Return: 0 on success, a negative error code on failure + */ +static int ad7944_chain_mode_alloc(struct device *dev, + const struct iio_chan_spec *chan_template, + u32 n_chain_dev, + struct iio_chan_spec **chain_chan, + void **chain_mode_buf, + unsigned long **chain_scan_masks) +{ + struct iio_chan_spec *chan; + size_t chain_mode_buf_size; + unsigned long *scan_masks; + void *buf; + int i; + + /* 1 channel for each device in chain plus 1 for soft timestamp */ + + chan = devm_kcalloc(dev, n_chain_dev + 1, sizeof(*chan), GFP_KERNEL); + if (!chan) + return -ENOMEM; + + for (i = 0; i < n_chain_dev; i++) { + chan[i] = chan_template[0]; + + if (chan_template[0].differential) { + chan[i].channel = 2 * i; + chan[i].channel2 = 2 * i + 1; + } else { + chan[i].channel = i; + } + + chan[i].scan_index = i; + } + + /* soft timestamp */ + chan[i] = chan_template[1]; + chan[i].scan_index = i; + + *chain_chan = chan; + + /* 1 word for each voltage channel + aligned u64 for timestamp */ + + chain_mode_buf_size = ALIGN(n_chain_dev * + AD7944_SPI_BYTES(chan[0].scan_type), sizeof(u64)) + sizeof(u64); + buf = devm_kzalloc(dev, chain_mode_buf_size, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + *chain_mode_buf = buf; + + /* + * Have to limit n_chain_dev due to current implementation of + * available_scan_masks. + */ + if (n_chain_dev > BITS_PER_LONG) + return dev_err_probe(dev, -EINVAL, + "chain is limited to 32 devices\n"); + + scan_masks = devm_kcalloc(dev, 2, sizeof(*scan_masks), GFP_KERNEL); + if (!scan_masks) + return -ENOMEM; + + /* + * Scan mask is needed since we always have to read all devices in the + * chain in one SPI transfer. + */ + scan_masks[0] = GENMASK(n_chain_dev - 1, 0); + + *chain_scan_masks = scan_masks; + + return 0; +} + +static const char * const ad7944_power_supplies[] = { + "avdd", "dvdd", "bvdd", "vio" +}; + +static const struct spi_offload_config ad7944_offload_config = { + .capability_flags = SPI_OFFLOAD_CAP_TRIGGER | + SPI_OFFLOAD_CAP_RX_STREAM_DMA, +}; + +static int ad7944_probe(struct spi_device *spi) +{ + const struct ad7944_chip_info *chip_info; + struct device *dev = &spi->dev; + struct iio_dev *indio_dev; + struct ad7944_adc *adc; + bool have_refin; + struct iio_chan_spec *chain_chan; + unsigned long *chain_scan_masks; + u32 n_chain_dev; + int ret, ref_mv; + + indio_dev = devm_iio_device_alloc(dev, sizeof(*adc)); + if (!indio_dev) + return -ENOMEM; + + adc = iio_priv(indio_dev); + adc->spi = spi; + + chip_info = spi_get_device_match_data(spi); + if (!chip_info) + return dev_err_probe(dev, -EINVAL, "no chip info\n"); + + adc->timing_spec = chip_info->timing_spec; + + adc->sample_freq_range[0] = 1; /* min */ + adc->sample_freq_range[1] = 1; /* step */ + adc->sample_freq_range[2] = chip_info->max_sample_rate_hz; /* max */ + + ret = device_property_match_property_string(dev, "adi,spi-mode", + ad7944_spi_modes, + ARRAY_SIZE(ad7944_spi_modes)); + /* absence of adi,spi-mode property means default mode */ + if (ret == -EINVAL) + adc->spi_mode = AD7944_SPI_MODE_DEFAULT; + else if (ret < 0) + return dev_err_probe(dev, ret, + "getting adi,spi-mode property failed\n"); + else + adc->spi_mode = ret; + + /* + * Some chips use unusual word sizes, so check now instead of waiting + * for the first xfer. + */ + if (!spi_is_bpw_supported(spi, chip_info->channels[0].scan_type.realbits)) + return dev_err_probe(dev, -EINVAL, + "SPI host does not support %d bits per word\n", + chip_info->channels[0].scan_type.realbits); + + ret = devm_regulator_bulk_get_enable(dev, + ARRAY_SIZE(ad7944_power_supplies), + ad7944_power_supplies); + if (ret) + return dev_err_probe(dev, ret, + "failed to get and enable supplies\n"); + + /* + * Sort out what is being used for the reference voltage. Options are: + * - internal reference: neither REF or REFIN is connected + * - internal reference with external buffer: REF not connected, REFIN + * is connected + * - external reference: REF is connected, REFIN is not connected + */ + + ret = devm_regulator_get_enable_read_voltage(dev, "ref"); + if (ret < 0 && ret != -ENODEV) + return dev_err_probe(dev, ret, "failed to get REF voltage\n"); + + ref_mv = ret == -ENODEV ? 0 : ret / 1000; + + ret = devm_regulator_get_enable_optional(dev, "refin"); + if (ret < 0 && ret != -ENODEV) + return dev_err_probe(dev, ret, "failed to get REFIN voltage\n"); + + have_refin = ret != -ENODEV; + + if (have_refin && ref_mv) + return dev_err_probe(dev, -EINVAL, + "cannot have both refin and ref supplies\n"); + + adc->ref_mv = ref_mv ?: AD7944_INTERNAL_REF_MV; + + adc->cnv = devm_gpiod_get_optional(dev, "cnv", GPIOD_OUT_LOW); + if (IS_ERR(adc->cnv)) + return dev_err_probe(dev, PTR_ERR(adc->cnv), + "failed to get CNV GPIO\n"); + + if (!adc->cnv && adc->spi_mode == AD7944_SPI_MODE_DEFAULT) + return dev_err_probe(&spi->dev, -EINVAL, "CNV GPIO is required\n"); + if (adc->cnv && adc->spi_mode != AD7944_SPI_MODE_DEFAULT) + return dev_err_probe(&spi->dev, -EINVAL, + "CNV GPIO in single and chain mode is not currently supported\n"); + + adc->turbo = devm_gpiod_get_optional(dev, "turbo", GPIOD_OUT_LOW); + if (IS_ERR(adc->turbo)) + return dev_err_probe(dev, PTR_ERR(adc->turbo), + "failed to get TURBO GPIO\n"); + + adc->always_turbo = device_property_present(dev, "adi,always-turbo"); + + if (adc->turbo && adc->always_turbo) + return dev_err_probe(dev, -EINVAL, + "cannot have both turbo-gpios and adi,always-turbo\n"); + + if (adc->spi_mode == AD7944_SPI_MODE_CHAIN && adc->always_turbo) + return dev_err_probe(dev, -EINVAL, + "cannot have both chain mode and always turbo\n"); + + switch (adc->spi_mode) { + case AD7944_SPI_MODE_DEFAULT: + ret = ad7944_4wire_mode_init_msg(dev, adc, &chip_info->channels[0]); + if (ret) + return ret; + + break; + case AD7944_SPI_MODE_SINGLE: + ret = ad7944_3wire_cs_mode_init_msg(dev, adc, &chip_info->channels[0]); + if (ret) + return ret; + + break; + case AD7944_SPI_MODE_CHAIN: + ret = device_property_read_u32(dev, "#daisy-chained-devices", + &n_chain_dev); + if (ret) + return dev_err_probe(dev, ret, + "failed to get #daisy-chained-devices\n"); + + ret = ad7944_chain_mode_alloc(dev, chip_info->channels, + n_chain_dev, &chain_chan, + &adc->chain_mode_buf, + &chain_scan_masks); + if (ret) + return ret; + + ret = ad7944_chain_mode_init_msg(dev, adc, &chain_chan[0], + n_chain_dev); + if (ret) + return ret; + + break; + } + + indio_dev->name = chip_info->name; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->info = &ad7944_iio_info; + + adc->offload = devm_spi_offload_get(dev, spi, &ad7944_offload_config); + ret = PTR_ERR_OR_ZERO(adc->offload); + if (ret && ret != -ENODEV) + return dev_err_probe(dev, ret, "failed to get offload\n"); + + /* Fall back to low speed usage when no SPI offload available. */ + if (ret == -ENODEV) { + if (adc->spi_mode == AD7944_SPI_MODE_CHAIN) { + indio_dev->available_scan_masks = chain_scan_masks; + indio_dev->channels = chain_chan; + indio_dev->num_channels = n_chain_dev + 1; + } else { + indio_dev->channels = chip_info->channels; + indio_dev->num_channels = ARRAY_SIZE(chip_info->channels); + } + + ret = devm_iio_triggered_buffer_setup(dev, indio_dev, + iio_pollfunc_store_time, + ad7944_trigger_handler, + NULL); + if (ret) + return ret; + } else { + struct dma_chan *rx_dma; + + if (adc->spi_mode != AD7944_SPI_MODE_SINGLE) + return dev_err_probe(dev, -EINVAL, + "offload only supported in single mode\n"); + + indio_dev->setup_ops = &ad7944_offload_buffer_setup_ops; + indio_dev->channels = chip_info->offload_channels; + indio_dev->num_channels = ARRAY_SIZE(chip_info->offload_channels); + + adc->offload_trigger = devm_spi_offload_trigger_get(dev, + adc->offload, SPI_OFFLOAD_TRIGGER_PERIODIC); + if (IS_ERR(adc->offload_trigger)) + return dev_err_probe(dev, PTR_ERR(adc->offload_trigger), + "failed to get offload trigger\n"); + + ret = ad7944_set_sample_freq(adc, 2 * MEGA); + if (ret) + return dev_err_probe(dev, ret, + "failed to init sample rate\n"); + + rx_dma = devm_spi_offload_rx_stream_request_dma_chan(dev, + adc->offload); + if (IS_ERR(rx_dma)) + return dev_err_probe(dev, PTR_ERR(rx_dma), + "failed to get offload RX DMA\n"); + + /* + * REVISIT: ideally, we would confirm that the offload RX DMA + * buffer layout is the same as what is hard-coded in + * offload_channels. Right now, the only supported offload + * is the pulsar_adc project which always uses 32-bit word + * size for data values, regardless of the SPI bits per word. + */ + + ret = devm_iio_dmaengine_buffer_setup_with_handle(dev, + indio_dev, rx_dma, IIO_BUFFER_DIRECTION_IN); + if (ret) + return ret; + + ret = ad7944_3wire_cs_mode_init_offload_msg(dev, adc, + &chip_info->offload_channels[0]); + if (ret) + return ret; + } + + return devm_iio_device_register(dev, indio_dev); +} + +static const struct of_device_id ad7944_of_match[] = { + { .compatible = "adi,ad7944", .data = &ad7944_chip_info }, + { .compatible = "adi,ad7985", .data = &ad7985_chip_info }, + { .compatible = "adi,ad7986", .data = &ad7986_chip_info }, + { } +}; +MODULE_DEVICE_TABLE(of, ad7944_of_match); + +static const struct spi_device_id ad7944_spi_id[] = { + { .name = "ad7944", .driver_data = (kernel_ulong_t)&ad7944_chip_info }, + { .name = "ad7985", .driver_data = (kernel_ulong_t)&ad7985_chip_info }, + { .name = "ad7986", .driver_data = (kernel_ulong_t)&ad7986_chip_info }, + { } + +}; +MODULE_DEVICE_TABLE(spi, ad7944_spi_id); + +static struct spi_driver ad7944_driver = { + .driver = { + .name = "ad7944", + .of_match_table = ad7944_of_match, + }, + .probe = ad7944_probe, + .id_table = ad7944_spi_id, +}; +module_spi_driver(ad7944_driver); + +MODULE_AUTHOR("David Lechner "); +MODULE_DESCRIPTION("Analog Devices AD7944 PulSAR ADC family driver"); +MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS("IIO_DMAENGINE_BUFFER"); diff --git a/drivers/iio/adc/ad7949.c b/drivers/iio/adc/ad7949.c new file mode 100644 index 000000000..93a57a3d5 --- /dev/null +++ b/drivers/iio/adc/ad7949.c @@ -0,0 +1,432 @@ +// SPDX-License-Identifier: GPL-2.0 +/* ad7949.c - Analog Devices ADC driver 14/16 bits 4/8 channels + * + * Copyright (C) 2018 CMC NV + * + * https://www.analog.com/media/en/technical-documentation/data-sheets/AD7949.pdf + */ + +#include +#include +#include +#include +#include +#include + +#define AD7949_CFG_MASK_TOTAL GENMASK(13, 0) + +/* CFG: Configuration Update */ +#define AD7949_CFG_MASK_OVERWRITE BIT(13) + +/* INCC: Input Channel Configuration */ +#define AD7949_CFG_MASK_INCC GENMASK(12, 10) +#define AD7949_CFG_VAL_INCC_UNIPOLAR_GND 7 +#define AD7949_CFG_VAL_INCC_UNIPOLAR_COMM 6 +#define AD7949_CFG_VAL_INCC_UNIPOLAR_DIFF 4 +#define AD7949_CFG_VAL_INCC_TEMP 3 +#define AD7949_CFG_VAL_INCC_BIPOLAR 2 +#define AD7949_CFG_VAL_INCC_BIPOLAR_DIFF 0 + +/* INX: Input channel Selection in a binary fashion */ +#define AD7949_CFG_MASK_INX GENMASK(9, 7) + +/* BW: select bandwidth for low-pass filter. Full or Quarter */ +#define AD7949_CFG_MASK_BW_FULL BIT(6) + +/* REF: reference/buffer selection */ +#define AD7949_CFG_MASK_REF GENMASK(5, 3) +#define AD7949_CFG_VAL_REF_EXT_TEMP_BUF 3 +#define AD7949_CFG_VAL_REF_EXT_TEMP 2 +#define AD7949_CFG_VAL_REF_INT_4096 1 +#define AD7949_CFG_VAL_REF_INT_2500 0 +#define AD7949_CFG_VAL_REF_EXTERNAL BIT(1) + +/* SEQ: channel sequencer. Allows for scanning channels */ +#define AD7949_CFG_MASK_SEQ GENMASK(2, 1) + +/* RB: Read back the CFG register */ +#define AD7949_CFG_MASK_RBN BIT(0) + +enum { + ID_AD7949 = 0, + ID_AD7682, + ID_AD7689, +}; + +struct ad7949_adc_spec { + u8 num_channels; + u8 resolution; +}; + +static const struct ad7949_adc_spec ad7949_adc_spec[] = { + [ID_AD7949] = { .num_channels = 8, .resolution = 14 }, + [ID_AD7682] = { .num_channels = 4, .resolution = 16 }, + [ID_AD7689] = { .num_channels = 8, .resolution = 16 }, +}; + +/** + * struct ad7949_adc_chip - AD ADC chip + * @lock: protects write sequences + * @vref: regulator generating Vref + * @indio_dev: reference to iio structure + * @spi: reference to spi structure + * @refsel: reference selection + * @resolution: resolution of the chip + * @cfg: copy of the configuration register + * @current_channel: current channel in use + * @buffer: buffer to send / receive data to / from device + * @buf8b: be16 buffer to exchange data with the device in 8-bit transfers + */ +struct ad7949_adc_chip { + struct mutex lock; + struct regulator *vref; + struct iio_dev *indio_dev; + struct spi_device *spi; + u32 refsel; + u8 resolution; + u16 cfg; + unsigned int current_channel; + u16 buffer __aligned(IIO_DMA_MINALIGN); + __be16 buf8b; +}; + +static int ad7949_spi_write_cfg(struct ad7949_adc_chip *ad7949_adc, u16 val, + u16 mask) +{ + int ret; + + ad7949_adc->cfg = (val & mask) | (ad7949_adc->cfg & ~mask); + + switch (ad7949_adc->spi->bits_per_word) { + case 16: + ad7949_adc->buffer = ad7949_adc->cfg << 2; + ret = spi_write(ad7949_adc->spi, &ad7949_adc->buffer, 2); + break; + case 14: + ad7949_adc->buffer = ad7949_adc->cfg; + ret = spi_write(ad7949_adc->spi, &ad7949_adc->buffer, 2); + break; + case 8: + /* Here, type is big endian as it must be sent in two transfers */ + ad7949_adc->buf8b = cpu_to_be16(ad7949_adc->cfg << 2); + ret = spi_write(ad7949_adc->spi, &ad7949_adc->buf8b, 2); + break; + default: + dev_err(&ad7949_adc->indio_dev->dev, "unsupported BPW\n"); + return -EINVAL; + } + + /* + * This delay is to avoid a new request before the required time to + * send a new command to the device + */ + udelay(2); + return ret; +} + +static int ad7949_spi_read_channel(struct ad7949_adc_chip *ad7949_adc, int *val, + unsigned int channel) +{ + int ret; + int i; + + /* + * 1: write CFG for sample N and read old data (sample N-2) + * 2: if CFG was not changed since sample N-1 then we'll get good data + * at the next xfer, so we bail out now, otherwise we write something + * and we read garbage (sample N-1 configuration). + */ + for (i = 0; i < 2; i++) { + ret = ad7949_spi_write_cfg(ad7949_adc, + FIELD_PREP(AD7949_CFG_MASK_INX, channel), + AD7949_CFG_MASK_INX); + if (ret) + return ret; + if (channel == ad7949_adc->current_channel) + break; + } + + /* 3: write something and read actual data */ + if (ad7949_adc->spi->bits_per_word == 8) + ret = spi_read(ad7949_adc->spi, &ad7949_adc->buf8b, 2); + else + ret = spi_read(ad7949_adc->spi, &ad7949_adc->buffer, 2); + + if (ret) + return ret; + + /* + * This delay is to avoid a new request before the required time to + * send a new command to the device + */ + udelay(2); + + ad7949_adc->current_channel = channel; + + switch (ad7949_adc->spi->bits_per_word) { + case 16: + *val = ad7949_adc->buffer; + /* Shift-out padding bits */ + *val >>= 16 - ad7949_adc->resolution; + break; + case 14: + *val = ad7949_adc->buffer & GENMASK(13, 0); + break; + case 8: + /* Here, type is big endian as data was sent in two transfers */ + *val = be16_to_cpu(ad7949_adc->buf8b); + /* Shift-out padding bits */ + *val >>= 16 - ad7949_adc->resolution; + break; + default: + dev_err(&ad7949_adc->indio_dev->dev, "unsupported BPW\n"); + return -EINVAL; + } + + return 0; +} + +#define AD7949_ADC_CHANNEL(chan) { \ + .type = IIO_VOLTAGE, \ + .indexed = 1, \ + .channel = (chan), \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ +} + +static const struct iio_chan_spec ad7949_adc_channels[] = { + AD7949_ADC_CHANNEL(0), + AD7949_ADC_CHANNEL(1), + AD7949_ADC_CHANNEL(2), + AD7949_ADC_CHANNEL(3), + AD7949_ADC_CHANNEL(4), + AD7949_ADC_CHANNEL(5), + AD7949_ADC_CHANNEL(6), + AD7949_ADC_CHANNEL(7), +}; + +static int ad7949_spi_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct ad7949_adc_chip *ad7949_adc = iio_priv(indio_dev); + int ret; + + if (!val) + return -EINVAL; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + mutex_lock(&ad7949_adc->lock); + ret = ad7949_spi_read_channel(ad7949_adc, val, chan->channel); + mutex_unlock(&ad7949_adc->lock); + + if (ret < 0) + return ret; + + return IIO_VAL_INT; + + case IIO_CHAN_INFO_SCALE: + switch (ad7949_adc->refsel) { + case AD7949_CFG_VAL_REF_INT_2500: + *val = 2500; + break; + case AD7949_CFG_VAL_REF_INT_4096: + *val = 4096; + break; + case AD7949_CFG_VAL_REF_EXT_TEMP: + case AD7949_CFG_VAL_REF_EXT_TEMP_BUF: + ret = regulator_get_voltage(ad7949_adc->vref); + if (ret < 0) + return ret; + + /* convert value back to mV */ + *val = ret / 1000; + break; + } + + *val2 = (1 << ad7949_adc->resolution) - 1; + return IIO_VAL_FRACTIONAL; + } + + return -EINVAL; +} + +static int ad7949_spi_reg_access(struct iio_dev *indio_dev, + unsigned int reg, unsigned int writeval, + unsigned int *readval) +{ + struct ad7949_adc_chip *ad7949_adc = iio_priv(indio_dev); + int ret = 0; + + if (readval) + *readval = ad7949_adc->cfg; + else + ret = ad7949_spi_write_cfg(ad7949_adc, writeval, + AD7949_CFG_MASK_TOTAL); + + return ret; +} + +static const struct iio_info ad7949_spi_info = { + .read_raw = ad7949_spi_read_raw, + .debugfs_reg_access = ad7949_spi_reg_access, +}; + +static int ad7949_spi_init(struct ad7949_adc_chip *ad7949_adc) +{ + int ret; + int val; + u16 cfg; + + ad7949_adc->current_channel = 0; + + cfg = FIELD_PREP(AD7949_CFG_MASK_OVERWRITE, 1) | + FIELD_PREP(AD7949_CFG_MASK_INCC, AD7949_CFG_VAL_INCC_UNIPOLAR_GND) | + FIELD_PREP(AD7949_CFG_MASK_INX, ad7949_adc->current_channel) | + FIELD_PREP(AD7949_CFG_MASK_BW_FULL, 1) | + FIELD_PREP(AD7949_CFG_MASK_REF, ad7949_adc->refsel) | + FIELD_PREP(AD7949_CFG_MASK_SEQ, 0x0) | + FIELD_PREP(AD7949_CFG_MASK_RBN, 1); + + ret = ad7949_spi_write_cfg(ad7949_adc, cfg, AD7949_CFG_MASK_TOTAL); + + /* + * Do two dummy conversions to apply the first configuration setting. + * Required only after the start up of the device. + */ + ad7949_spi_read_channel(ad7949_adc, &val, ad7949_adc->current_channel); + ad7949_spi_read_channel(ad7949_adc, &val, ad7949_adc->current_channel); + + return ret; +} + +static void ad7949_disable_reg(void *reg) +{ + regulator_disable(reg); +} + +static int ad7949_spi_probe(struct spi_device *spi) +{ + struct device *dev = &spi->dev; + const struct ad7949_adc_spec *spec; + struct ad7949_adc_chip *ad7949_adc; + struct iio_dev *indio_dev; + u32 tmp; + int ret; + + indio_dev = devm_iio_device_alloc(dev, sizeof(*ad7949_adc)); + if (!indio_dev) + return -ENOMEM; + + indio_dev->info = &ad7949_spi_info; + indio_dev->name = spi_get_device_id(spi)->name; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = ad7949_adc_channels; + spi_set_drvdata(spi, indio_dev); + + ad7949_adc = iio_priv(indio_dev); + ad7949_adc->indio_dev = indio_dev; + ad7949_adc->spi = spi; + + spec = &ad7949_adc_spec[spi_get_device_id(spi)->driver_data]; + indio_dev->num_channels = spec->num_channels; + ad7949_adc->resolution = spec->resolution; + + /* Set SPI bits per word */ + if (spi_is_bpw_supported(spi, ad7949_adc->resolution)) { + spi->bits_per_word = ad7949_adc->resolution; + } else if (spi_is_bpw_supported(spi, 16)) { + spi->bits_per_word = 16; + } else if (spi_is_bpw_supported(spi, 8)) { + spi->bits_per_word = 8; + } else { + return dev_err_probe(dev, -EINVAL, + "unable to find common BPW with spi controller\n"); + } + + /* Setup internal voltage reference */ + tmp = 4096000; + device_property_read_u32(dev, "adi,internal-ref-microvolt", &tmp); + + switch (tmp) { + case 2500000: + ad7949_adc->refsel = AD7949_CFG_VAL_REF_INT_2500; + break; + case 4096000: + ad7949_adc->refsel = AD7949_CFG_VAL_REF_INT_4096; + break; + default: + return dev_err_probe(dev, -EINVAL, + "unsupported internal voltage reference\n"); + } + + /* Setup external voltage reference, buffered? */ + ad7949_adc->vref = devm_regulator_get_optional(dev, "vrefin"); + if (IS_ERR(ad7949_adc->vref)) { + ret = PTR_ERR(ad7949_adc->vref); + if (ret != -ENODEV) + return ret; + /* unbuffered? */ + ad7949_adc->vref = devm_regulator_get_optional(dev, "vref"); + if (IS_ERR(ad7949_adc->vref)) { + ret = PTR_ERR(ad7949_adc->vref); + if (ret != -ENODEV) + return ret; + } else { + ad7949_adc->refsel = AD7949_CFG_VAL_REF_EXT_TEMP; + } + } else { + ad7949_adc->refsel = AD7949_CFG_VAL_REF_EXT_TEMP_BUF; + } + + if (ad7949_adc->refsel & AD7949_CFG_VAL_REF_EXTERNAL) { + ret = regulator_enable(ad7949_adc->vref); + if (ret < 0) + return dev_err_probe(dev, ret, + "fail to enable regulator\n"); + + ret = devm_add_action_or_reset(dev, ad7949_disable_reg, + ad7949_adc->vref); + if (ret) + return ret; + } + + mutex_init(&ad7949_adc->lock); + + ret = ad7949_spi_init(ad7949_adc); + if (ret) + return dev_err_probe(dev, ret, "fail to init this device\n"); + + return devm_iio_device_register(dev, indio_dev); +} + +static const struct of_device_id ad7949_spi_of_id[] = { + { .compatible = "adi,ad7949" }, + { .compatible = "adi,ad7682" }, + { .compatible = "adi,ad7689" }, + { } +}; +MODULE_DEVICE_TABLE(of, ad7949_spi_of_id); + +static const struct spi_device_id ad7949_spi_id[] = { + { .name = "ad7949", .driver_data = ID_AD7949 }, + { .name = "ad7682", .driver_data = ID_AD7682 }, + { .name = "ad7689", .driver_data = ID_AD7689 }, + { } +}; +MODULE_DEVICE_TABLE(spi, ad7949_spi_id); + +static struct spi_driver ad7949_spi_driver = { + .driver = { + .name = "ad7949", + .of_match_table = ad7949_spi_of_id, + }, + .probe = ad7949_spi_probe, + .id_table = ad7949_spi_id, +}; +module_spi_driver(ad7949_spi_driver); + +MODULE_AUTHOR("Charles-Antoine Couret "); +MODULE_DESCRIPTION("Analog Devices 14/16-bit 8-channel ADC driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/adc/ad799x.c b/drivers/iio/adc/ad799x.c new file mode 100644 index 000000000..bb076d8f5 --- /dev/null +++ b/drivers/iio/adc/ad799x.c @@ -0,0 +1,968 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * iio/adc/ad799x.c + * Copyright (C) 2010-2011 Michael Hennerich, Analog Devices Inc. + * + * based on iio/adc/max1363 + * Copyright (C) 2008-2010 Jonathan Cameron + * + * based on linux/drivers/i2c/chips/max123x + * Copyright (C) 2002-2004 Stefan Eletzhofer + * + * based on linux/drivers/acron/char/pcf8583.c + * Copyright (C) 2000 Russell King + * + * ad799x.c + * + * Support for ad7991, ad7995, ad7999, ad7992, ad7993, ad7994, ad7997, + * ad7998 and similar chips. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#define AD799X_CHANNEL_SHIFT 4 +#define AD799X_MAX_CHANNELS 8 + +/* + * AD7991, AD7995 and AD7999 defines + */ + +#define AD7991_REF_SEL 0x08 +#define AD7991_FLTR 0x04 +#define AD7991_BIT_TRIAL_DELAY 0x02 +#define AD7991_SAMPLE_DELAY 0x01 + +/* + * AD7992, AD7993, AD7994, AD7997 and AD7998 defines + */ + +#define AD7998_FLTR BIT(3) +#define AD7998_ALERT_EN BIT(2) +#define AD7998_BUSY_ALERT BIT(1) +#define AD7998_BUSY_ALERT_POL BIT(0) + +#define AD7998_CONV_RES_REG 0x0 +#define AD7998_ALERT_STAT_REG 0x1 +#define AD7998_CONF_REG 0x2 +#define AD7998_CYCLE_TMR_REG 0x3 + +#define AD7998_DATALOW_REG(x) ((x) * 3 + 0x4) +#define AD7998_DATAHIGH_REG(x) ((x) * 3 + 0x5) +#define AD7998_HYST_REG(x) ((x) * 3 + 0x6) + +#define AD7998_CYC_MASK GENMASK(2, 0) +#define AD7998_CYC_DIS 0x0 +#define AD7998_CYC_TCONF_32 0x1 +#define AD7998_CYC_TCONF_64 0x2 +#define AD7998_CYC_TCONF_128 0x3 +#define AD7998_CYC_TCONF_256 0x4 +#define AD7998_CYC_TCONF_512 0x5 +#define AD7998_CYC_TCONF_1024 0x6 +#define AD7998_CYC_TCONF_2048 0x7 + +#define AD7998_ALERT_STAT_CLEAR 0xFF + +/* + * AD7997 and AD7997 defines + */ + +#define AD7997_8_READ_SINGLE BIT(7) +#define AD7997_8_READ_SEQUENCE (BIT(6) | BIT(5) | BIT(4)) + +enum { + ad7991, + ad7995, + ad7999, + ad7992, + ad7993, + ad7994, + ad7997, + ad7998 +}; + +/** + * struct ad799x_chip_config - chip specific information + * @channel: channel specification + * @default_config: device default configuration + * @info: pointer to iio_info struct + */ +struct ad799x_chip_config { + const struct iio_chan_spec channel[9]; + u16 default_config; + const struct iio_info *info; +}; + +/** + * struct ad799x_chip_info - chip specific information + * @num_channels: number of channels + * @noirq_config: device configuration w/o IRQ + * @irq_config: device configuration w/IRQ + * @has_vref: device supports external reference voltage + */ +struct ad799x_chip_info { + int num_channels; + const struct ad799x_chip_config noirq_config; + const struct ad799x_chip_config irq_config; + bool has_vref; +}; + +struct ad799x_state { + struct i2c_client *client; + const struct ad799x_chip_config *chip_config; + struct regulator *reg; + struct regulator *vref; + /* lock to protect against multiple access to the device */ + struct mutex lock; + unsigned int id; + u16 config; + + unsigned int transfer_size; + + int vref_uV; + + IIO_DECLARE_BUFFER_WITH_TS(__be16, rx_buf, AD799X_MAX_CHANNELS); +}; + +static int ad799x_write_config(struct ad799x_state *st, u16 val) +{ + switch (st->id) { + case ad7997: + case ad7998: + return i2c_smbus_write_word_swapped(st->client, AD7998_CONF_REG, + val); + case ad7992: + case ad7993: + case ad7994: + return i2c_smbus_write_byte_data(st->client, AD7998_CONF_REG, + val); + default: + /* Will be written when doing a conversion */ + st->config = val; + return 0; + } +} + +static int ad799x_read_config(struct ad799x_state *st) +{ + switch (st->id) { + case ad7997: + case ad7998: + return i2c_smbus_read_word_swapped(st->client, AD7998_CONF_REG); + case ad7992: + case ad7993: + case ad7994: + return i2c_smbus_read_byte_data(st->client, AD7998_CONF_REG); + default: + /* No readback support */ + return st->config; + } +} + +static int ad799x_update_config(struct ad799x_state *st, u16 config) +{ + int ret; + + ret = ad799x_write_config(st, config); + if (ret < 0) + return ret; + ret = ad799x_read_config(st); + if (ret < 0) + return ret; + st->config = ret; + + return 0; +} + +static irqreturn_t ad799x_trigger_handler(int irq, void *p) +{ + struct iio_poll_func *pf = p; + struct iio_dev *indio_dev = pf->indio_dev; + struct ad799x_state *st = iio_priv(indio_dev); + int b_sent; + u8 cmd; + + switch (st->id) { + case ad7991: + case ad7995: + case ad7999: + cmd = st->config | + (*indio_dev->active_scan_mask << AD799X_CHANNEL_SHIFT); + break; + case ad7992: + case ad7993: + case ad7994: + cmd = (*indio_dev->active_scan_mask << AD799X_CHANNEL_SHIFT) | + AD7998_CONV_RES_REG; + break; + case ad7997: + case ad7998: + cmd = AD7997_8_READ_SEQUENCE | AD7998_CONV_RES_REG; + break; + default: + cmd = 0; + } + + b_sent = i2c_smbus_read_i2c_block_data(st->client, + cmd, st->transfer_size, + (u8 *)st->rx_buf); + if (b_sent < 0) + goto out; + + iio_push_to_buffers_with_timestamp(indio_dev, st->rx_buf, + iio_get_time_ns(indio_dev)); +out: + iio_trigger_notify_done(indio_dev->trig); + + return IRQ_HANDLED; +} + +static int ad799x_update_scan_mode(struct iio_dev *indio_dev, + const unsigned long *scan_mask) +{ + struct ad799x_state *st = iio_priv(indio_dev); + + st->transfer_size = bitmap_weight(scan_mask, + iio_get_masklength(indio_dev)) * 2; + + switch (st->id) { + case ad7992: + case ad7993: + case ad7994: + case ad7997: + case ad7998: + st->config &= ~(GENMASK(7, 0) << AD799X_CHANNEL_SHIFT); + st->config |= (*scan_mask << AD799X_CHANNEL_SHIFT); + return ad799x_write_config(st, st->config); + default: + return 0; + } +} + +static int ad799x_scan_direct(struct ad799x_state *st, unsigned int ch) +{ + u8 cmd; + + switch (st->id) { + case ad7991: + case ad7995: + case ad7999: + cmd = st->config | (BIT(ch) << AD799X_CHANNEL_SHIFT); + break; + case ad7992: + case ad7993: + case ad7994: + cmd = BIT(ch) << AD799X_CHANNEL_SHIFT; + break; + case ad7997: + case ad7998: + cmd = (ch << AD799X_CHANNEL_SHIFT) | AD7997_8_READ_SINGLE; + break; + default: + return -EINVAL; + } + + return i2c_smbus_read_word_swapped(st->client, cmd); +} + +static int ad799x_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, + int *val2, + long m) +{ + int ret; + struct ad799x_state *st = iio_priv(indio_dev); + + switch (m) { + case IIO_CHAN_INFO_RAW: + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; + mutex_lock(&st->lock); + ret = ad799x_scan_direct(st, chan->scan_index); + mutex_unlock(&st->lock); + iio_device_release_direct(indio_dev); + + if (ret < 0) + return ret; + *val = (ret >> chan->scan_type.shift) & + GENMASK(chan->scan_type.realbits - 1, 0); + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + *val = st->vref_uV / (MICRO / MILLI); + *val2 = chan->scan_type.realbits; + return IIO_VAL_FRACTIONAL_LOG2; + } + return -EINVAL; +} +static const unsigned int ad7998_frequencies[] = { + [AD7998_CYC_DIS] = 0, + [AD7998_CYC_TCONF_32] = 15625, + [AD7998_CYC_TCONF_64] = 7812, + [AD7998_CYC_TCONF_128] = 3906, + [AD7998_CYC_TCONF_512] = 976, + [AD7998_CYC_TCONF_1024] = 488, + [AD7998_CYC_TCONF_2048] = 244, +}; + +static ssize_t ad799x_read_frequency(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct iio_dev *indio_dev = dev_to_iio_dev(dev); + struct ad799x_state *st = iio_priv(indio_dev); + + int ret = i2c_smbus_read_byte_data(st->client, AD7998_CYCLE_TMR_REG); + + if (ret < 0) + return ret; + + return sprintf(buf, "%u\n", ad7998_frequencies[ret & AD7998_CYC_MASK]); +} + +static ssize_t ad799x_write_frequency(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t len) +{ + struct iio_dev *indio_dev = dev_to_iio_dev(dev); + struct ad799x_state *st = iio_priv(indio_dev); + + long val; + int ret, i; + + ret = kstrtol(buf, 10, &val); + if (ret) + return ret; + + mutex_lock(&st->lock); + + ret = i2c_smbus_read_byte_data(st->client, AD7998_CYCLE_TMR_REG); + if (ret < 0) + goto error_ret_mutex; + /* Wipe the bits clean */ + ret &= ~AD7998_CYC_MASK; + + for (i = 0; i < ARRAY_SIZE(ad7998_frequencies); i++) + if (val == ad7998_frequencies[i]) + break; + if (i == ARRAY_SIZE(ad7998_frequencies)) { + ret = -EINVAL; + goto error_ret_mutex; + } + + ret = i2c_smbus_write_byte_data(st->client, AD7998_CYCLE_TMR_REG, + ret | i); + if (ret < 0) + goto error_ret_mutex; + ret = len; + +error_ret_mutex: + mutex_unlock(&st->lock); + + return ret; +} + +static int ad799x_read_event_config(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir) +{ + struct ad799x_state *st = iio_priv(indio_dev); + + if (!(st->config & AD7998_ALERT_EN)) + return 0; + + if ((st->config >> AD799X_CHANNEL_SHIFT) & BIT(chan->scan_index)) + return 1; + + return 0; +} + +static int ad799x_write_event_config(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir, + bool state) +{ + struct ad799x_state *st = iio_priv(indio_dev); + int ret; + + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; + + mutex_lock(&st->lock); + + if (state) + st->config |= BIT(chan->scan_index) << AD799X_CHANNEL_SHIFT; + else + st->config &= ~(BIT(chan->scan_index) << AD799X_CHANNEL_SHIFT); + + if (st->config >> AD799X_CHANNEL_SHIFT) + st->config |= AD7998_ALERT_EN; + else + st->config &= ~AD7998_ALERT_EN; + + ret = ad799x_write_config(st, st->config); + mutex_unlock(&st->lock); + iio_device_release_direct(indio_dev); + return ret; +} + +static unsigned int ad799x_threshold_reg(const struct iio_chan_spec *chan, + enum iio_event_direction dir, + enum iio_event_info info) +{ + switch (info) { + case IIO_EV_INFO_VALUE: + if (dir == IIO_EV_DIR_FALLING) + return AD7998_DATALOW_REG(chan->channel); + else + return AD7998_DATAHIGH_REG(chan->channel); + case IIO_EV_INFO_HYSTERESIS: + return AD7998_HYST_REG(chan->channel); + default: + return -EINVAL; + } + + return 0; +} + +static int ad799x_write_event_value(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir, + enum iio_event_info info, + int val, int val2) +{ + int ret; + struct ad799x_state *st = iio_priv(indio_dev); + + if (val < 0 || val > GENMASK(chan->scan_type.realbits - 1, 0)) + return -EINVAL; + + ret = i2c_smbus_write_word_swapped(st->client, + ad799x_threshold_reg(chan, dir, info), + val << chan->scan_type.shift); + + return ret; +} + +static int ad799x_read_event_value(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir, + enum iio_event_info info, + int *val, int *val2) +{ + int ret; + struct ad799x_state *st = iio_priv(indio_dev); + + ret = i2c_smbus_read_word_swapped(st->client, + ad799x_threshold_reg(chan, dir, info)); + if (ret < 0) + return ret; + *val = (ret >> chan->scan_type.shift) & + GENMASK(chan->scan_type.realbits - 1, 0); + + return IIO_VAL_INT; +} + +static irqreturn_t ad799x_event_handler(int irq, void *private) +{ + struct iio_dev *indio_dev = private; + struct ad799x_state *st = iio_priv(private); + int i, ret; + + ret = i2c_smbus_read_byte_data(st->client, AD7998_ALERT_STAT_REG); + if (ret <= 0) + goto done; + + if (i2c_smbus_write_byte_data(st->client, AD7998_ALERT_STAT_REG, + AD7998_ALERT_STAT_CLEAR) < 0) + goto done; + + for (i = 0; i < 8; i++) { + if (ret & BIT(i)) + iio_push_event(indio_dev, + i & 0x1 ? + IIO_UNMOD_EVENT_CODE(IIO_VOLTAGE, + (i >> 1), + IIO_EV_TYPE_THRESH, + IIO_EV_DIR_RISING) : + IIO_UNMOD_EVENT_CODE(IIO_VOLTAGE, + (i >> 1), + IIO_EV_TYPE_THRESH, + IIO_EV_DIR_FALLING), + iio_get_time_ns(indio_dev)); + } + +done: + return IRQ_HANDLED; +} + +static IIO_DEV_ATTR_SAMP_FREQ(0644, + ad799x_read_frequency, + ad799x_write_frequency); +static IIO_CONST_ATTR_SAMP_FREQ_AVAIL("15625 7812 3906 1953 976 488 244 0"); + +static struct attribute *ad799x_event_attributes[] = { + &iio_dev_attr_sampling_frequency.dev_attr.attr, + &iio_const_attr_sampling_frequency_available.dev_attr.attr, + NULL, +}; + +static const struct attribute_group ad799x_event_attrs_group = { + .attrs = ad799x_event_attributes, +}; + +static const struct iio_info ad7991_info = { + .read_raw = &ad799x_read_raw, + .update_scan_mode = ad799x_update_scan_mode, +}; + +static const struct iio_info ad7993_4_7_8_noirq_info = { + .read_raw = &ad799x_read_raw, + .update_scan_mode = ad799x_update_scan_mode, +}; + +static const struct iio_info ad7993_4_7_8_irq_info = { + .read_raw = &ad799x_read_raw, + .event_attrs = &ad799x_event_attrs_group, + .read_event_config = &ad799x_read_event_config, + .write_event_config = &ad799x_write_event_config, + .read_event_value = &ad799x_read_event_value, + .write_event_value = &ad799x_write_event_value, + .update_scan_mode = ad799x_update_scan_mode, +}; + +static const struct iio_event_spec ad799x_events[] = { + { + .type = IIO_EV_TYPE_THRESH, + .dir = IIO_EV_DIR_RISING, + .mask_separate = BIT(IIO_EV_INFO_VALUE) | + BIT(IIO_EV_INFO_ENABLE), + }, { + .type = IIO_EV_TYPE_THRESH, + .dir = IIO_EV_DIR_FALLING, + .mask_separate = BIT(IIO_EV_INFO_VALUE) | + BIT(IIO_EV_INFO_ENABLE), + }, { + .type = IIO_EV_TYPE_THRESH, + .dir = IIO_EV_DIR_EITHER, + .mask_separate = BIT(IIO_EV_INFO_HYSTERESIS), + }, +}; + +#define _AD799X_CHANNEL(_index, _realbits, _ev_spec, _num_ev_spec) { \ + .type = IIO_VOLTAGE, \ + .indexed = 1, \ + .channel = (_index), \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ + .scan_index = (_index), \ + .scan_type = { \ + .sign = 'u', \ + .realbits = (_realbits), \ + .storagebits = 16, \ + .shift = 12 - (_realbits), \ + .endianness = IIO_BE, \ + }, \ + .event_spec = _ev_spec, \ + .num_event_specs = _num_ev_spec, \ +} + +#define AD799X_CHANNEL(_index, _realbits) \ + _AD799X_CHANNEL(_index, _realbits, NULL, 0) + +#define AD799X_CHANNEL_WITH_EVENTS(_index, _realbits) \ + _AD799X_CHANNEL(_index, _realbits, ad799x_events, \ + ARRAY_SIZE(ad799x_events)) + +static const struct ad799x_chip_info ad799x_chip_info_tbl[] = { + [ad7991] = { + .num_channels = 5, + .has_vref = true, + .noirq_config = { + .channel = { + AD799X_CHANNEL(0, 12), + AD799X_CHANNEL(1, 12), + AD799X_CHANNEL(2, 12), + AD799X_CHANNEL(3, 12), + IIO_CHAN_SOFT_TIMESTAMP(4), + }, + .info = &ad7991_info, + }, + }, + [ad7995] = { + .num_channels = 5, + .has_vref = true, + .noirq_config = { + .channel = { + AD799X_CHANNEL(0, 10), + AD799X_CHANNEL(1, 10), + AD799X_CHANNEL(2, 10), + AD799X_CHANNEL(3, 10), + IIO_CHAN_SOFT_TIMESTAMP(4), + }, + .info = &ad7991_info, + }, + }, + [ad7999] = { + .num_channels = 5, + .has_vref = true, + .noirq_config = { + .channel = { + AD799X_CHANNEL(0, 8), + AD799X_CHANNEL(1, 8), + AD799X_CHANNEL(2, 8), + AD799X_CHANNEL(3, 8), + IIO_CHAN_SOFT_TIMESTAMP(4), + }, + .info = &ad7991_info, + }, + }, + [ad7992] = { + .num_channels = 3, + .noirq_config = { + .channel = { + AD799X_CHANNEL(0, 12), + AD799X_CHANNEL(1, 12), + IIO_CHAN_SOFT_TIMESTAMP(3), + }, + .info = &ad7993_4_7_8_noirq_info, + }, + .irq_config = { + .channel = { + AD799X_CHANNEL_WITH_EVENTS(0, 12), + AD799X_CHANNEL_WITH_EVENTS(1, 12), + IIO_CHAN_SOFT_TIMESTAMP(3), + }, + .default_config = AD7998_ALERT_EN | AD7998_BUSY_ALERT, + .info = &ad7993_4_7_8_irq_info, + }, + }, + [ad7993] = { + .num_channels = 5, + .noirq_config = { + .channel = { + AD799X_CHANNEL(0, 10), + AD799X_CHANNEL(1, 10), + AD799X_CHANNEL(2, 10), + AD799X_CHANNEL(3, 10), + IIO_CHAN_SOFT_TIMESTAMP(4), + }, + .info = &ad7993_4_7_8_noirq_info, + }, + .irq_config = { + .channel = { + AD799X_CHANNEL_WITH_EVENTS(0, 10), + AD799X_CHANNEL_WITH_EVENTS(1, 10), + AD799X_CHANNEL_WITH_EVENTS(2, 10), + AD799X_CHANNEL_WITH_EVENTS(3, 10), + IIO_CHAN_SOFT_TIMESTAMP(4), + }, + .default_config = AD7998_ALERT_EN | AD7998_BUSY_ALERT, + .info = &ad7993_4_7_8_irq_info, + }, + }, + [ad7994] = { + .num_channels = 5, + .has_vref = true, + .noirq_config = { + .channel = { + AD799X_CHANNEL(0, 12), + AD799X_CHANNEL(1, 12), + AD799X_CHANNEL(2, 12), + AD799X_CHANNEL(3, 12), + IIO_CHAN_SOFT_TIMESTAMP(4), + }, + .info = &ad7993_4_7_8_noirq_info, + }, + .irq_config = { + .channel = { + AD799X_CHANNEL_WITH_EVENTS(0, 12), + AD799X_CHANNEL_WITH_EVENTS(1, 12), + AD799X_CHANNEL_WITH_EVENTS(2, 12), + AD799X_CHANNEL_WITH_EVENTS(3, 12), + IIO_CHAN_SOFT_TIMESTAMP(4), + }, + .default_config = AD7998_ALERT_EN | AD7998_BUSY_ALERT, + .info = &ad7993_4_7_8_irq_info, + }, + }, + [ad7997] = { + .num_channels = 9, + .noirq_config = { + .channel = { + AD799X_CHANNEL(0, 10), + AD799X_CHANNEL(1, 10), + AD799X_CHANNEL(2, 10), + AD799X_CHANNEL(3, 10), + AD799X_CHANNEL(4, 10), + AD799X_CHANNEL(5, 10), + AD799X_CHANNEL(6, 10), + AD799X_CHANNEL(7, 10), + IIO_CHAN_SOFT_TIMESTAMP(8), + }, + .info = &ad7993_4_7_8_noirq_info, + }, + .irq_config = { + .channel = { + AD799X_CHANNEL_WITH_EVENTS(0, 10), + AD799X_CHANNEL_WITH_EVENTS(1, 10), + AD799X_CHANNEL_WITH_EVENTS(2, 10), + AD799X_CHANNEL_WITH_EVENTS(3, 10), + AD799X_CHANNEL(4, 10), + AD799X_CHANNEL(5, 10), + AD799X_CHANNEL(6, 10), + AD799X_CHANNEL(7, 10), + IIO_CHAN_SOFT_TIMESTAMP(8), + }, + .default_config = AD7998_ALERT_EN | AD7998_BUSY_ALERT, + .info = &ad7993_4_7_8_irq_info, + }, + }, + [ad7998] = { + .num_channels = 9, + .noirq_config = { + .channel = { + AD799X_CHANNEL(0, 12), + AD799X_CHANNEL(1, 12), + AD799X_CHANNEL(2, 12), + AD799X_CHANNEL(3, 12), + AD799X_CHANNEL(4, 12), + AD799X_CHANNEL(5, 12), + AD799X_CHANNEL(6, 12), + AD799X_CHANNEL(7, 12), + IIO_CHAN_SOFT_TIMESTAMP(8), + }, + .info = &ad7993_4_7_8_noirq_info, + }, + .irq_config = { + .channel = { + AD799X_CHANNEL_WITH_EVENTS(0, 12), + AD799X_CHANNEL_WITH_EVENTS(1, 12), + AD799X_CHANNEL_WITH_EVENTS(2, 12), + AD799X_CHANNEL_WITH_EVENTS(3, 12), + AD799X_CHANNEL(4, 12), + AD799X_CHANNEL(5, 12), + AD799X_CHANNEL(6, 12), + AD799X_CHANNEL(7, 12), + IIO_CHAN_SOFT_TIMESTAMP(8), + }, + .default_config = AD7998_ALERT_EN | AD7998_BUSY_ALERT, + .info = &ad7993_4_7_8_irq_info, + }, + }, +}; + +static void ad799x_reg_disable(void *reg) +{ + regulator_disable(reg); +} + +static int ad799x_probe(struct i2c_client *client) +{ + struct device *dev = &client->dev; + const struct i2c_device_id *id = i2c_client_get_device_id(client); + int ret; + int extra_config = 0; + struct ad799x_state *st; + struct iio_dev *indio_dev; + const struct ad799x_chip_info *chip_info = + &ad799x_chip_info_tbl[id->driver_data]; + + indio_dev = devm_iio_device_alloc(dev, sizeof(*st)); + if (indio_dev == NULL) + return -ENOMEM; + + st = iio_priv(indio_dev); + /* this is only used for device removal purposes */ + i2c_set_clientdata(client, indio_dev); + + ret = devm_mutex_init(dev, &st->lock); + if (ret) + return ret; + + st->id = id->driver_data; + if (client->irq > 0 && chip_info->irq_config.info) + st->chip_config = &chip_info->irq_config; + else + st->chip_config = &chip_info->noirq_config; + + /* TODO: Add pdata options for filtering and bit delay */ + + st->reg = devm_regulator_get(dev, "vcc"); + if (IS_ERR(st->reg)) + return PTR_ERR(st->reg); + ret = regulator_enable(st->reg); + if (ret) + return ret; + + ret = devm_add_action_or_reset(dev, ad799x_reg_disable, st->reg); + if (ret) + return ret; + + /* check if an external reference is supplied */ + if (chip_info->has_vref) { + st->vref = devm_regulator_get_optional(dev, "vref"); + ret = PTR_ERR_OR_ZERO(st->vref); + if (ret == -ENODEV) { + st->vref = NULL; + dev_info(dev, "Using VCC reference voltage\n"); + } else if (ret) { + return ret; + } + + if (st->vref) { + dev_info(dev, "Using external reference voltage\n"); + extra_config |= AD7991_REF_SEL; + ret = regulator_enable(st->vref); + if (ret) + return ret; + + ret = devm_add_action_or_reset(dev, ad799x_reg_disable, st->vref); + if (ret) + return ret; + + ret = regulator_get_voltage(st->vref); + if (ret < 0) + return ret; + st->vref_uV = ret; + } + } + + if (!st->vref) { + ret = regulator_get_voltage(st->reg); + if (ret < 0) + return ret; + st->vref_uV = ret; + } + + st->client = client; + + indio_dev->name = id->name; + indio_dev->info = st->chip_config->info; + + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = st->chip_config->channel; + indio_dev->num_channels = chip_info->num_channels; + + ret = ad799x_update_config(st, st->chip_config->default_config | extra_config); + if (ret) + return ret; + + ret = devm_iio_triggered_buffer_setup(dev, indio_dev, NULL, + &ad799x_trigger_handler, NULL); + if (ret) + return ret; + + if (client->irq > 0) { + ret = devm_request_threaded_irq(dev, + client->irq, + NULL, + ad799x_event_handler, + IRQF_TRIGGER_FALLING | + IRQF_ONESHOT, + client->name, + indio_dev); + if (ret) + return ret; + } + + return devm_iio_device_register(dev, indio_dev); +} + +static int ad799x_suspend(struct device *dev) +{ + struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev)); + struct ad799x_state *st = iio_priv(indio_dev); + + if (st->vref) + regulator_disable(st->vref); + regulator_disable(st->reg); + + return 0; +} + +static int ad799x_resume(struct device *dev) +{ + struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev)); + struct ad799x_state *st = iio_priv(indio_dev); + int ret; + + ret = regulator_enable(st->reg); + if (ret) { + dev_err(dev, "Unable to enable vcc regulator\n"); + return ret; + } + + if (st->vref) { + ret = regulator_enable(st->vref); + if (ret) { + regulator_disable(st->reg); + dev_err(dev, "Unable to enable vref regulator\n"); + return ret; + } + } + + /* resync config */ + ret = ad799x_update_config(st, st->config); + if (ret) { + if (st->vref) + regulator_disable(st->vref); + regulator_disable(st->reg); + return ret; + } + + return 0; +} + +static DEFINE_SIMPLE_DEV_PM_OPS(ad799x_pm_ops, ad799x_suspend, ad799x_resume); + +static const struct i2c_device_id ad799x_id[] = { + { .name = "ad7991", .driver_data = ad7991 }, + { .name = "ad7995", .driver_data = ad7995 }, + { .name = "ad7999", .driver_data = ad7999 }, + { .name = "ad7992", .driver_data = ad7992 }, + { .name = "ad7993", .driver_data = ad7993 }, + { .name = "ad7994", .driver_data = ad7994 }, + { .name = "ad7997", .driver_data = ad7997 }, + { .name = "ad7998", .driver_data = ad7998 }, + { } +}; + +MODULE_DEVICE_TABLE(i2c, ad799x_id); + +static struct i2c_driver ad799x_driver = { + .driver = { + .name = "ad799x", + .pm = pm_sleep_ptr(&ad799x_pm_ops), + }, + .probe = ad799x_probe, + .id_table = ad799x_id, +}; +module_i2c_driver(ad799x_driver); + +MODULE_AUTHOR("Michael Hennerich "); +MODULE_DESCRIPTION("Analog Devices AD799x ADC"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/adc/ad9467.c b/drivers/iio/adc/ad9467.c new file mode 100644 index 000000000..34b7b1256 --- /dev/null +++ b/drivers/iio/adc/ad9467.c @@ -0,0 +1,1433 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Analog Devices AD9467 SPI ADC driver + * + * Copyright 2012-2020 Analog Devices Inc. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +/* + * ADI High-Speed ADC common spi interface registers + * See Application-Note AN-877: + * https://www.analog.com/media/en/technical-documentation/application-notes/AN-877.pdf + */ + +#define AN877_ADC_REG_CHIP_PORT_CONF 0x00 +#define AN877_ADC_REG_CHIP_ID 0x01 +#define AN877_ADC_REG_CHIP_GRADE 0x02 +#define AN877_ADC_REG_CHAN_INDEX 0x05 +#define AN877_ADC_REG_TRANSFER 0xFF +#define AN877_ADC_REG_MODES 0x08 +#define AN877_ADC_REG_TEST_IO 0x0D +#define AN877_ADC_REG_ADC_INPUT 0x0F +#define AN877_ADC_REG_OFFSET 0x10 +#define AN877_ADC_REG_OUTPUT_MODE 0x14 +#define AN877_ADC_REG_OUTPUT_ADJUST 0x15 +#define AN877_ADC_REG_OUTPUT_PHASE 0x16 +#define AN877_ADC_REG_OUTPUT_DELAY 0x17 +#define AN877_ADC_REG_VREF 0x18 +#define AN877_ADC_REG_ANALOG_INPUT 0x2C + +/* AN877_ADC_REG_TEST_IO */ +#define AN877_ADC_TESTMODE_OFF 0x0 +#define AN877_ADC_TESTMODE_MIDSCALE_SHORT 0x1 +#define AN877_ADC_TESTMODE_POS_FULLSCALE 0x2 +#define AN877_ADC_TESTMODE_NEG_FULLSCALE 0x3 +#define AN877_ADC_TESTMODE_ALT_CHECKERBOARD 0x4 +#define AN877_ADC_TESTMODE_PN23_SEQ 0x5 +#define AN877_ADC_TESTMODE_PN9_SEQ 0x6 +#define AN877_ADC_TESTMODE_ONE_ZERO_TOGGLE 0x7 +#define AN877_ADC_TESTMODE_USER 0x8 +#define AN877_ADC_TESTMODE_BIT_TOGGLE 0x9 +#define AN877_ADC_TESTMODE_SYNC 0xA +#define AN877_ADC_TESTMODE_ONE_BIT_HIGH 0xB +#define AN877_ADC_TESTMODE_MIXED_BIT_FREQUENCY 0xC +#define AN877_ADC_TESTMODE_RAMP 0xF + +/* AN877_ADC_REG_TRANSFER */ +#define AN877_ADC_TRANSFER_SYNC 0x1 + +/* AN877_ADC_REG_OUTPUT_MODE */ +#define AN877_ADC_OUTPUT_MODE_OFFSET_BINARY 0x0 +#define AN877_ADC_OUTPUT_MODE_TWOS_COMPLEMENT 0x1 +#define AN877_ADC_OUTPUT_MODE_GRAY_CODE 0x2 +#define AN877_ADC_OUTPUT_MODE_MASK GENMASK(1, 0) + +/* AN877_ADC_REG_OUTPUT_PHASE */ +#define AN877_ADC_OUTPUT_EVEN_ODD_MODE_EN 0x20 +#define AN877_ADC_INVERT_DCO_CLK 0x80 + +/* AN877_ADC_REG_OUTPUT_DELAY */ +#define AN877_ADC_DCO_DELAY_ENABLE 0x80 + +/* + * Analog Devices AD9211 10-Bit, 200/250/300 MSPS ADC + */ + +#define CHIPID_AD9211 0x06 +#define AD9211_DEF_OUTPUT_MODE 0x01 +#define AD9211_REG_VREF_MASK GENMASK(4, 0) + +/* + * Analog Devices AD9265 16-Bit, 125/105/80 MSPS ADC + */ + +#define CHIPID_AD9265 0x64 +#define AD9265_DEF_OUTPUT_MODE 0x41 +#define AD9265_REG_VREF_MASK 0xC0 + +/* + * Analog Devices AD9434 12-Bit, 370/500 MSPS ADC + */ + +#define CHIPID_AD9434 0x6A +#define AD9434_DEF_OUTPUT_MODE 0x01 +#define AD9434_REG_VREF_MASK GENMASK(4, 0) + +/* + * Analog Devices AD9467 16-Bit, 200/250 MSPS ADC + */ + +#define CHIPID_AD9467 0x50 +#define AD9467_DEF_OUTPUT_MODE 0x09 +#define AD9467_REG_VREF_MASK 0x0F + +/* + * Analog Devices AD9643 14-Bit, 170/210/250 MSPS ADC + */ + +#define CHIPID_AD9643 0x82 +#define AD9643_DEF_OUTPUT_MODE 0x01 +#define AD9643_REG_VREF_MASK 0x1F + +/* + * Analog Devices AD9652 16-bit 310 MSPS ADC + */ + +#define CHIPID_AD9652 0xC1 +#define AD9652_DEF_OUTPUT_MODE 0x01 +#define AD9652_REG_VREF_MASK 0xC0 + +/* + * Analog Devices AD9649 14-bit 20/40/65/80 MSPS ADC + */ + +#define CHIPID_AD9649 0x6F +#define AD9649_DEF_OUTPUT_MODE 0x01 +#define AD9649_TEST_POINTS 8 + +#define AD9647_MAX_TEST_POINTS 32 +#define AD9467_CAN_INVERT(st) \ + (!(st)->info->has_dco || (st)->info->has_dco_invert) + +struct ad9467_chip_info { + const char *name; + unsigned int id; + const struct iio_chan_spec *channels; + unsigned int num_channels; + const unsigned int (*scale_table)[2]; + int num_scales; + unsigned long test_mask; + unsigned int test_mask_len; + unsigned long max_rate; + unsigned int default_output_mode; + unsigned int vref_mask; + unsigned int num_lanes; + unsigned int dco_en; + unsigned int test_points; + const int *offset_range; + /* data clock output */ + bool has_dco; + bool has_dco_invert; +}; + +struct ad9467_chan_test_mode { + struct ad9467_state *st; + unsigned int idx; + u8 mode; +}; + +struct ad9467_state { + const struct ad9467_chip_info *info; + struct iio_backend *back; + struct spi_device *spi; + struct clk *clk; + /* used for debugfs */ + struct ad9467_chan_test_mode *chan_test; + unsigned int (*scales)[2]; + /* + * Times 2 because we may also invert the signal polarity and run the + * calibration again. For some reference on the test points (ad9265) see: + * https://www.analog.com/media/en/technical-documentation/data-sheets/ad9265.pdf + * at page 38 for the dco output delay. On devices as ad9467, the + * calibration is done at the backend level. For the ADI axi-adc: + * https://wiki.analog.com/resources/fpga/docs/axi_adc_ip + * at the io delay control section. + */ + DECLARE_BITMAP(calib_map, AD9647_MAX_TEST_POINTS * 2); + /* number of bits of the map */ + unsigned int calib_map_size; + struct gpio_desc *pwrdown_gpio; + /* ensure consistent state obtained on multiple related accesses */ + struct mutex lock; + u8 buf[3] __aligned(IIO_DMA_MINALIGN); +}; + +static int ad9467_spi_read(struct ad9467_state *st, unsigned int reg) +{ + unsigned char tbuf[2], rbuf[1]; + int ret; + + tbuf[0] = 0x80 | (reg >> 8); + tbuf[1] = reg & 0xFF; + + ret = spi_write_then_read(st->spi, + tbuf, ARRAY_SIZE(tbuf), + rbuf, ARRAY_SIZE(rbuf)); + + if (ret < 0) + return ret; + + return rbuf[0]; +} + +static int ad9467_spi_write(struct ad9467_state *st, unsigned int reg, + unsigned int val) +{ + st->buf[0] = reg >> 8; + st->buf[1] = reg & 0xFF; + st->buf[2] = val; + + return spi_write(st->spi, st->buf, ARRAY_SIZE(st->buf)); +} + +static int ad9467_reg_access(struct iio_dev *indio_dev, unsigned int reg, + unsigned int writeval, unsigned int *readval) +{ + struct ad9467_state *st = iio_priv(indio_dev); + int ret; + + if (!readval) { + guard(mutex)(&st->lock); + ret = ad9467_spi_write(st, reg, writeval); + if (ret) + return ret; + return ad9467_spi_write(st, AN877_ADC_REG_TRANSFER, + AN877_ADC_TRANSFER_SYNC); + } + + ret = ad9467_spi_read(st, reg); + if (ret < 0) + return ret; + *readval = ret; + + return 0; +} + +static const int ad9434_offset_range[] = { + -128, 1, 127, +}; + +static const unsigned int ad9211_scale_table[][2] = { + {980, 0x10}, {1000, 0x11}, {1020, 0x12}, {1040, 0x13}, + {1060, 0x14}, {1080, 0x15}, {1100, 0x16}, {1120, 0x17}, + {1140, 0x18}, {1160, 0x19}, {1180, 0x1A}, {1190, 0x1B}, + {1200, 0x1C}, {1210, 0x1D}, {1220, 0x1E}, {1230, 0x1F}, + {1250, 0x0}, {1270, 0x1}, {1290, 0x2}, {1310, 0x3}, + {1330, 0x4}, {1350, 0x5}, {1370, 0x6}, {1390, 0x7}, + {1410, 0x8}, {1430, 0x9}, {1450, 0xA}, {1460, 0xB}, + {1470, 0xC}, {1480, 0xD}, {1490, 0xE}, {1500, 0xF}, +}; + +static const unsigned int ad9265_scale_table[][2] = { + {1250, 0x00}, {1500, 0x40}, {1750, 0x80}, {2000, 0xC0}, +}; + +static const unsigned int ad9434_scale_table[][2] = { + {1600, 0x1C}, {1580, 0x1D}, {1550, 0x1E}, {1520, 0x1F}, {1500, 0x00}, + {1470, 0x01}, {1440, 0x02}, {1420, 0x03}, {1390, 0x04}, {1360, 0x05}, + {1340, 0x06}, {1310, 0x07}, {1280, 0x08}, {1260, 0x09}, {1230, 0x0A}, + {1200, 0x0B}, {1180, 0x0C}, +}; + +static const unsigned int ad9467_scale_table[][2] = { + {2000, 0}, {2100, 6}, {2200, 7}, + {2300, 8}, {2400, 9}, {2500, 10}, +}; + +static const unsigned int ad9643_scale_table[][2] = { + {2087, 0x0F}, {2065, 0x0E}, {2042, 0x0D}, {2020, 0x0C}, {1997, 0x0B}, + {1975, 0x0A}, {1952, 0x09}, {1930, 0x08}, {1907, 0x07}, {1885, 0x06}, + {1862, 0x05}, {1840, 0x04}, {1817, 0x03}, {1795, 0x02}, {1772, 0x01}, + {1750, 0x00}, {1727, 0x1F}, {1704, 0x1E}, {1681, 0x1D}, {1658, 0x1C}, + {1635, 0x1B}, {1612, 0x1A}, {1589, 0x19}, {1567, 0x18}, {1544, 0x17}, + {1521, 0x16}, {1498, 0x15}, {1475, 0x14}, {1452, 0x13}, {1429, 0x12}, + {1406, 0x11}, {1383, 0x10}, +}; + +static const unsigned int ad9649_scale_table[][2] = { + {2000, 0}, +}; + +static const unsigned int ad9652_scale_table[][2] = { + {1250, 0}, {1125, 1}, {1200, 2}, {1250, 3}, {1000, 5}, +}; + +static void __ad9467_get_scale(struct ad9467_state *st, int index, + unsigned int *val, unsigned int *val2) +{ + const struct ad9467_chip_info *info = st->info; + const struct iio_chan_spec *chan = &info->channels[0]; + unsigned int tmp; + + tmp = (info->scale_table[index][0] * 1000000ULL) >> + chan->scan_type.realbits; + *val = tmp / 1000000; + *val2 = tmp % 1000000; +} + +#define AD9467_CHAN(_chan, avai_mask, _si, _bits, _sign) \ +{ \ + .type = IIO_VOLTAGE, \ + .indexed = 1, \ + .channel = _chan, \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | \ + BIT(IIO_CHAN_INFO_SAMP_FREQ), \ + .info_mask_shared_by_type_available = avai_mask, \ + .scan_index = _si, \ + .scan_type = { \ + .sign = _sign, \ + .realbits = _bits, \ + .storagebits = 16, \ + }, \ +} + +static const struct iio_chan_spec ad9211_channels[] = { + AD9467_CHAN(0, BIT(IIO_CHAN_INFO_SCALE), 0, 10, 's'), +}; + +static const struct iio_chan_spec ad9434_channels[] = { + { + .type = IIO_VOLTAGE, + .indexed = 1, + .channel = 0, + .info_mask_shared_by_type = + BIT(IIO_CHAN_INFO_SCALE) | + BIT(IIO_CHAN_INFO_SAMP_FREQ) | + BIT(IIO_CHAN_INFO_CALIBBIAS), + .info_mask_shared_by_type_available = + BIT(IIO_CHAN_INFO_SCALE) | + BIT(IIO_CHAN_INFO_CALIBBIAS), + .scan_index = 0, + .scan_type = { + .sign = 's', + .realbits = 12, + .storagebits = 16, + }, + }, +}; + +static const struct iio_chan_spec ad9467_channels[] = { + AD9467_CHAN(0, BIT(IIO_CHAN_INFO_SCALE), 0, 16, 's'), +}; + +static const struct iio_chan_spec ad9643_channels[] = { + AD9467_CHAN(0, BIT(IIO_CHAN_INFO_SCALE), 0, 14, 's'), + AD9467_CHAN(1, BIT(IIO_CHAN_INFO_SCALE), 1, 14, 's'), +}; + +static const struct iio_chan_spec ad9649_channels[] = { + AD9467_CHAN(0, 0, 0, 14, 's'), +}; + +static const struct iio_chan_spec ad9652_channels[] = { + AD9467_CHAN(0, BIT(IIO_CHAN_INFO_SCALE), 0, 16, 's'), + AD9467_CHAN(1, BIT(IIO_CHAN_INFO_SCALE), 1, 16, 's'), +}; + +static const char * const ad9467_test_modes[] = { + [AN877_ADC_TESTMODE_OFF] = "off", + [AN877_ADC_TESTMODE_MIDSCALE_SHORT] = "midscale_short", + [AN877_ADC_TESTMODE_POS_FULLSCALE] = "pos_fullscale", + [AN877_ADC_TESTMODE_NEG_FULLSCALE] = "neg_fullscale", + [AN877_ADC_TESTMODE_ALT_CHECKERBOARD] = "checkerboard", + [AN877_ADC_TESTMODE_PN23_SEQ] = "prbs23", + [AN877_ADC_TESTMODE_PN9_SEQ] = "prbs9", + [AN877_ADC_TESTMODE_ONE_ZERO_TOGGLE] = "one_zero_toggle", + [AN877_ADC_TESTMODE_USER] = "user", + [AN877_ADC_TESTMODE_BIT_TOGGLE] = "bit_toggle", + [AN877_ADC_TESTMODE_SYNC] = "sync", + [AN877_ADC_TESTMODE_ONE_BIT_HIGH] = "one_bit_high", + [AN877_ADC_TESTMODE_MIXED_BIT_FREQUENCY] = "mixed_bit_frequency", + [AN877_ADC_TESTMODE_RAMP] = "ramp", +}; + +static const struct ad9467_chip_info ad9467_chip_tbl = { + .name = "ad9467", + .id = CHIPID_AD9467, + .max_rate = 250000000UL, + .scale_table = ad9467_scale_table, + .num_scales = ARRAY_SIZE(ad9467_scale_table), + .channels = ad9467_channels, + .num_channels = ARRAY_SIZE(ad9467_channels), + .test_points = AD9647_MAX_TEST_POINTS, + .test_mask = GENMASK(AN877_ADC_TESTMODE_ONE_ZERO_TOGGLE, + AN877_ADC_TESTMODE_OFF), + .test_mask_len = AN877_ADC_TESTMODE_ONE_ZERO_TOGGLE + 1, + .default_output_mode = AD9467_DEF_OUTPUT_MODE, + .vref_mask = AD9467_REG_VREF_MASK, + .num_lanes = 8, +}; + +static const struct ad9467_chip_info ad9434_chip_tbl = { + .name = "ad9434", + .id = CHIPID_AD9434, + .max_rate = 500000000UL, + .scale_table = ad9434_scale_table, + .num_scales = ARRAY_SIZE(ad9434_scale_table), + .channels = ad9434_channels, + .num_channels = ARRAY_SIZE(ad9434_channels), + .test_points = AD9647_MAX_TEST_POINTS, + .test_mask = GENMASK(AN877_ADC_TESTMODE_USER, AN877_ADC_TESTMODE_OFF), + .test_mask_len = AN877_ADC_TESTMODE_USER + 1, + .default_output_mode = AD9434_DEF_OUTPUT_MODE, + .vref_mask = AD9434_REG_VREF_MASK, + .num_lanes = 6, + .offset_range = ad9434_offset_range, +}; + +static const struct ad9467_chip_info ad9211_chip_tbl = { + .name = "ad9211", + .id = CHIPID_AD9211, + .max_rate = 300 * HZ_PER_MHZ, + .scale_table = ad9211_scale_table, + .num_scales = ARRAY_SIZE(ad9211_scale_table), + .channels = ad9211_channels, + .num_channels = ARRAY_SIZE(ad9211_channels), + .test_points = AD9647_MAX_TEST_POINTS, + .test_mask = GENMASK(AN877_ADC_TESTMODE_ONE_ZERO_TOGGLE, + AN877_ADC_TESTMODE_OFF), + .test_mask_len = AN877_ADC_TESTMODE_ONE_ZERO_TOGGLE + 1, + .default_output_mode = AD9211_DEF_OUTPUT_MODE, + .vref_mask = AD9211_REG_VREF_MASK, + .has_dco = true, +}; + +static const struct ad9467_chip_info ad9265_chip_tbl = { + .name = "ad9265", + .id = CHIPID_AD9265, + .max_rate = 125000000UL, + .scale_table = ad9265_scale_table, + .num_scales = ARRAY_SIZE(ad9265_scale_table), + .channels = ad9467_channels, + .num_channels = ARRAY_SIZE(ad9467_channels), + .test_points = AD9647_MAX_TEST_POINTS, + .test_mask = GENMASK(AN877_ADC_TESTMODE_ONE_ZERO_TOGGLE, + AN877_ADC_TESTMODE_OFF), + .test_mask_len = AN877_ADC_TESTMODE_ONE_ZERO_TOGGLE + 1, + .default_output_mode = AD9265_DEF_OUTPUT_MODE, + .vref_mask = AD9265_REG_VREF_MASK, + .has_dco = true, + .has_dco_invert = true, +}; + +static const struct ad9467_chip_info ad9643_chip_tbl = { + .name = "ad9643", + .id = CHIPID_AD9643, + .max_rate = 250000000UL, + .scale_table = ad9643_scale_table, + .num_scales = ARRAY_SIZE(ad9643_scale_table), + .channels = ad9643_channels, + .num_channels = ARRAY_SIZE(ad9643_channels), + .test_points = AD9647_MAX_TEST_POINTS, + .test_mask = BIT(AN877_ADC_TESTMODE_RAMP) | + GENMASK(AN877_ADC_TESTMODE_MIXED_BIT_FREQUENCY, AN877_ADC_TESTMODE_OFF), + .test_mask_len = AN877_ADC_TESTMODE_RAMP + 1, + .default_output_mode = AD9643_DEF_OUTPUT_MODE, + .vref_mask = AD9643_REG_VREF_MASK, + .has_dco = true, + .has_dco_invert = true, + .dco_en = AN877_ADC_DCO_DELAY_ENABLE, +}; + +static const struct ad9467_chip_info ad9649_chip_tbl = { + .name = "ad9649", + .id = CHIPID_AD9649, + .max_rate = 80000000UL, + .scale_table = ad9649_scale_table, + .num_scales = ARRAY_SIZE(ad9649_scale_table), + .channels = ad9649_channels, + .num_channels = ARRAY_SIZE(ad9649_channels), + .test_points = AD9649_TEST_POINTS, + .test_mask = GENMASK(AN877_ADC_TESTMODE_MIXED_BIT_FREQUENCY, + AN877_ADC_TESTMODE_OFF), + .test_mask_len = AN877_ADC_TESTMODE_MIXED_BIT_FREQUENCY + 1, + .default_output_mode = AD9649_DEF_OUTPUT_MODE, + .has_dco = true, + .has_dco_invert = true, + .dco_en = AN877_ADC_DCO_DELAY_ENABLE, +}; + +static const struct ad9467_chip_info ad9652_chip_tbl = { + .name = "ad9652", + .id = CHIPID_AD9652, + .max_rate = 310000000UL, + .scale_table = ad9652_scale_table, + .num_scales = ARRAY_SIZE(ad9652_scale_table), + .channels = ad9652_channels, + .num_channels = ARRAY_SIZE(ad9652_channels), + .test_points = AD9647_MAX_TEST_POINTS, + .test_mask = GENMASK(AN877_ADC_TESTMODE_ONE_ZERO_TOGGLE, + AN877_ADC_TESTMODE_OFF), + .test_mask_len = AN877_ADC_TESTMODE_ONE_ZERO_TOGGLE + 1, + .default_output_mode = AD9652_DEF_OUTPUT_MODE, + .vref_mask = AD9652_REG_VREF_MASK, + .has_dco = true, +}; + +static int ad9467_get_scale(struct ad9467_state *st, int *val, int *val2) +{ + const struct ad9467_chip_info *info = st->info; + unsigned int vref_val; + unsigned int i = 0; + int ret; + + /* nothing to read if we only have one possible scale */ + if (info->num_scales == 1) + goto out_get_scale; + + ret = ad9467_spi_read(st, AN877_ADC_REG_VREF); + if (ret < 0) + return ret; + + vref_val = ret & info->vref_mask; + + for (i = 0; i < info->num_scales; i++) { + if (vref_val == info->scale_table[i][1]) + break; + } + + if (i == info->num_scales) + return -ERANGE; + +out_get_scale: + __ad9467_get_scale(st, i, val, val2); + + return IIO_VAL_INT_PLUS_MICRO; +} + +static int ad9467_set_scale(struct ad9467_state *st, int val, int val2) +{ + const struct ad9467_chip_info *info = st->info; + unsigned int scale_val[2]; + unsigned int i; + int ret; + + if (val != 0) + return -EINVAL; + if (info->num_scales == 1) + return -EOPNOTSUPP; + + for (i = 0; i < info->num_scales; i++) { + __ad9467_get_scale(st, i, &scale_val[0], &scale_val[1]); + if (scale_val[0] != val || scale_val[1] != val2) + continue; + + guard(mutex)(&st->lock); + ret = ad9467_spi_write(st, AN877_ADC_REG_VREF, + info->scale_table[i][1]); + if (ret < 0) + return ret; + + return ad9467_spi_write(st, AN877_ADC_REG_TRANSFER, + AN877_ADC_TRANSFER_SYNC); + } + + return -EINVAL; +} + +static int ad9467_get_offset(struct ad9467_state *st, int *val) +{ + int ret; + + ret = ad9467_spi_read(st, AN877_ADC_REG_OFFSET); + if (ret < 0) + return ret; + *val = ret; + + return IIO_VAL_INT; +} + +static int ad9467_set_offset(struct ad9467_state *st, int val) +{ + int ret; + + if (val < st->info->offset_range[0] || val > st->info->offset_range[2]) + return -EINVAL; + + ret = ad9467_spi_write(st, AN877_ADC_REG_OFFSET, val); + if (ret < 0) + return ret; + + return ad9467_spi_write(st, AN877_ADC_REG_TRANSFER, + AN877_ADC_TRANSFER_SYNC); +} + +static int ad9467_outputmode_set(struct ad9467_state *st, unsigned int mode) +{ + int ret; + + ret = ad9467_spi_write(st, AN877_ADC_REG_OUTPUT_MODE, mode); + if (ret < 0) + return ret; + + return ad9467_spi_write(st, AN877_ADC_REG_TRANSFER, + AN877_ADC_TRANSFER_SYNC); +} + +static int ad9467_testmode_set(struct ad9467_state *st, unsigned int chan, + unsigned int test_mode) +{ + int ret; + + if (st->info->num_channels > 1) { + /* so that the test mode is only applied to one channel */ + ret = ad9467_spi_write(st, AN877_ADC_REG_CHAN_INDEX, BIT(chan)); + if (ret) + return ret; + } + + ret = ad9467_spi_write(st, AN877_ADC_REG_TEST_IO, test_mode); + if (ret) + return ret; + + if (st->info->num_channels > 1) { + /* go to default state where all channels get write commands */ + ret = ad9467_spi_write(st, AN877_ADC_REG_CHAN_INDEX, + GENMASK(st->info->num_channels - 1, 0)); + if (ret) + return ret; + } + + return ad9467_spi_write(st, AN877_ADC_REG_TRANSFER, + AN877_ADC_TRANSFER_SYNC); +} + +static int ad9467_backend_testmode_on(struct ad9467_state *st, + unsigned int chan, + enum iio_backend_test_pattern pattern) +{ + struct iio_backend_data_fmt data = { + .enable = false, + }; + int ret; + + ret = iio_backend_data_format_set(st->back, chan, &data); + if (ret) + return ret; + + ret = iio_backend_test_pattern_set(st->back, chan, pattern); + if (ret) + return ret; + + return iio_backend_chan_enable(st->back, chan); +} + +static int ad9467_backend_testmode_off(struct ad9467_state *st, + unsigned int chan) +{ + struct iio_backend_data_fmt data = { + .enable = true, + .sign_extend = true, + }; + int ret; + + ret = iio_backend_chan_disable(st->back, chan); + if (ret) + return ret; + + ret = iio_backend_test_pattern_set(st->back, chan, + IIO_BACKEND_NO_TEST_PATTERN); + if (ret) + return ret; + + return iio_backend_data_format_set(st->back, chan, &data); +} + +static int ad9647_calibrate_prepare(struct ad9467_state *st) +{ + unsigned int cmode; + unsigned int c; + int ret; + + cmode = st->info->default_output_mode; + FIELD_MODIFY(AN877_ADC_OUTPUT_MODE_MASK, &cmode, + AN877_ADC_OUTPUT_MODE_OFFSET_BINARY); + ret = ad9467_outputmode_set(st, cmode); + if (ret) + return ret; + + for (c = 0; c < st->info->num_channels; c++) { + ret = ad9467_testmode_set(st, c, AN877_ADC_TESTMODE_PN9_SEQ); + if (ret) + return ret; + + ret = ad9467_backend_testmode_on(st, c, + IIO_BACKEND_ADI_PRBS_9A); + if (ret) + return ret; + } + + return 0; +} + +static int ad9647_calibrate_polarity_set(struct ad9467_state *st, + bool invert) +{ + enum iio_backend_sample_trigger trigger; + + if (st->info->has_dco) { + unsigned int phase = AN877_ADC_OUTPUT_EVEN_ODD_MODE_EN; + + if (invert) + phase |= AN877_ADC_INVERT_DCO_CLK; + + return ad9467_spi_write(st, AN877_ADC_REG_OUTPUT_PHASE, + phase); + } + + if (invert) + trigger = IIO_BACKEND_SAMPLE_TRIGGER_EDGE_FALLING; + else + trigger = IIO_BACKEND_SAMPLE_TRIGGER_EDGE_RISING; + + return iio_backend_data_sample_trigger(st->back, trigger); +} + +/* + * The idea is pretty simple. Find the max number of successful points in a row + * and get the one in the middle. + */ +static unsigned int ad9467_find_optimal_point(const unsigned long *calib_map, + unsigned int start, + unsigned int nbits, + unsigned int *val) +{ + unsigned int bit = start, end, start_cnt, cnt = 0; + + for_each_clear_bitrange_from(bit, end, calib_map, nbits + start) { + if (end - bit > cnt) { + cnt = end - bit; + start_cnt = bit; + } + } + + if (cnt) + *val = start_cnt + cnt / 2; + + return cnt; +} + +static int ad9467_calibrate_apply(struct ad9467_state *st, unsigned int val) +{ + unsigned int lane; + int ret; + + if (st->info->has_dco) { + ret = ad9467_spi_write(st, AN877_ADC_REG_OUTPUT_DELAY, + val | st->info->dco_en); + if (ret) + return ret; + + return ad9467_spi_write(st, AN877_ADC_REG_TRANSFER, + AN877_ADC_TRANSFER_SYNC); + } + + for (lane = 0; lane < st->info->num_lanes; lane++) { + ret = iio_backend_iodelay_set(st->back, lane, val); + if (ret) + return ret; + } + + return 0; +} + +static int ad9647_calibrate_stop(struct ad9467_state *st) +{ + unsigned int c, mode; + int ret; + + for (c = 0; c < st->info->num_channels; c++) { + ret = ad9467_backend_testmode_off(st, c); + if (ret) + return ret; + + ret = ad9467_testmode_set(st, c, AN877_ADC_TESTMODE_OFF); + if (ret) + return ret; + } + + mode = st->info->default_output_mode; + return ad9467_outputmode_set(st, mode); +} + +static int ad9467_calibrate(struct ad9467_state *st) +{ + unsigned int point, val, inv_val, cnt, inv_cnt = 0, c; + /* + * Half of the bitmap is for the inverted signal. The number of test + * points is the same though... + */ + unsigned int test_points = st->info->test_points; + unsigned long sample_rate = clk_get_rate(st->clk); + struct device *dev = &st->spi->dev; + bool invert = false, stat; + int ret; + + /* all points invalid */ + bitmap_fill(st->calib_map, st->calib_map_size); + + ret = ad9647_calibrate_prepare(st); + if (ret) + return ret; +retune: + ret = ad9647_calibrate_polarity_set(st, invert); + if (ret) + return ret; + + for (point = 0; point < st->info->test_points; point++) { + ret = ad9467_calibrate_apply(st, point); + if (ret) + return ret; + + for (c = 0; c < st->info->num_channels; c++) { + ret = iio_backend_chan_status(st->back, c, &stat); + if (ret) + return ret; + + /* + * A point is considered valid if all channels report no + * error. If one reports an error, then we consider the + * point as invalid and we can break the loop right away. + */ + if (stat) { + dev_dbg(dev, "Invalid point(%u, inv:%u) for CH:%u\n", + point, invert, c); + break; + } + + if (c == st->info->num_channels - 1) + __clear_bit(point + invert * test_points, + st->calib_map); + } + } + + if (!invert) { + cnt = ad9467_find_optimal_point(st->calib_map, 0, test_points, + &val); + /* + * We're happy if we find, at least, three good test points in + * a row. + */ + if (cnt < 3) { + if (AD9467_CAN_INVERT(st)) { + invert = true; + goto retune; + } + + if (!cnt) + return -EIO; + } + } else { + inv_cnt = ad9467_find_optimal_point(st->calib_map, test_points, + test_points, &inv_val); + if (!inv_cnt && !cnt) + return -EIO; + } + + if (inv_cnt < cnt) { + ret = ad9647_calibrate_polarity_set(st, false); + if (ret) + return ret; + } else { + /* + * polarity inverted is the last test to run. Hence, there's no + * need to re-do any configuration. We just need to "normalize" + * the selected value. + */ + val = inv_val - test_points; + } + + if (st->info->has_dco) + dev_dbg(dev, "%sDCO 0x%X CLK %lu Hz\n", inv_cnt >= cnt ? "INVERT " : "", + val, sample_rate); + else + dev_dbg(dev, "%sIDELAY 0x%x\n", inv_cnt >= cnt ? "INVERT " : "", + val); + + ret = ad9467_calibrate_apply(st, val); + if (ret) + return ret; + + /* finally apply the optimal value */ + return ad9647_calibrate_stop(st); +} + +static int ad9467_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long m) +{ + struct ad9467_state *st = iio_priv(indio_dev); + + switch (m) { + case IIO_CHAN_INFO_CALIBBIAS: + return ad9467_get_offset(st, val); + case IIO_CHAN_INFO_SCALE: + return ad9467_get_scale(st, val, val2); + case IIO_CHAN_INFO_SAMP_FREQ: + *val = clk_get_rate(st->clk); + + return IIO_VAL_INT; + default: + return -EINVAL; + } +} + +static int __ad9467_update_clock(struct ad9467_state *st, long r_clk) +{ + int ret; + + ret = clk_set_rate(st->clk, r_clk); + if (ret) + return ret; + + guard(mutex)(&st->lock); + + if (iio_backend_has_caps(st->back, IIO_BACKEND_CAP_CALIBRATION)) + return ad9467_calibrate(st); + + return 0; +} + +static int ad9467_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, int val2, long mask) +{ + struct ad9467_state *st = iio_priv(indio_dev); + const struct ad9467_chip_info *info = st->info; + unsigned long sample_rate; + long r_clk; + int ret; + + switch (mask) { + case IIO_CHAN_INFO_CALIBBIAS: + return ad9467_set_offset(st, val); + case IIO_CHAN_INFO_SCALE: + return ad9467_set_scale(st, val, val2); + case IIO_CHAN_INFO_SAMP_FREQ: + r_clk = clk_round_rate(st->clk, val); + if (r_clk < 0 || r_clk > info->max_rate) { + dev_warn(&st->spi->dev, + "Error setting ADC sample rate %ld", r_clk); + return -EINVAL; + } + + sample_rate = clk_get_rate(st->clk); + /* + * clk_set_rate() would also do this but since we would still + * need it for avoiding an unnecessary calibration, do it now. + */ + if (sample_rate == r_clk) + return 0; + + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; + + ret = __ad9467_update_clock(st, r_clk); + iio_device_release_direct(indio_dev); + return ret; + default: + return -EINVAL; + } +} + +static int ad9467_read_avail(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + const int **vals, int *type, int *length, + long mask) +{ + struct ad9467_state *st = iio_priv(indio_dev); + const struct ad9467_chip_info *info = st->info; + + switch (mask) { + case IIO_CHAN_INFO_CALIBBIAS: + *type = IIO_VAL_INT; + *vals = info->offset_range; + return IIO_AVAIL_RANGE; + case IIO_CHAN_INFO_SCALE: + *vals = (const int *)st->scales; + *type = IIO_VAL_INT_PLUS_MICRO; + /* Values are stored in a 2D matrix */ + *length = info->num_scales * 2; + return IIO_AVAIL_LIST; + default: + return -EINVAL; + } +} + +static int ad9467_update_scan_mode(struct iio_dev *indio_dev, + const unsigned long *scan_mask) +{ + struct ad9467_state *st = iio_priv(indio_dev); + unsigned int c; + int ret; + + for (c = 0; c < st->info->num_channels; c++) { + if (test_bit(c, scan_mask)) + ret = iio_backend_chan_enable(st->back, c); + else + ret = iio_backend_chan_disable(st->back, c); + if (ret) + return ret; + } + + return 0; +} + +static const struct iio_info ad9467_info = { + .read_raw = ad9467_read_raw, + .write_raw = ad9467_write_raw, + .update_scan_mode = ad9467_update_scan_mode, + .debugfs_reg_access = ad9467_reg_access, + .read_avail = ad9467_read_avail, +}; + +/* Same as above, but without .read_avail */ +static const struct iio_info ad9467_info_no_read_avail = { + .read_raw = ad9467_read_raw, + .write_raw = ad9467_write_raw, + .update_scan_mode = ad9467_update_scan_mode, + .debugfs_reg_access = ad9467_reg_access, +}; + +static int ad9467_scale_fill(struct ad9467_state *st) +{ + const struct ad9467_chip_info *info = st->info; + unsigned int i, val1, val2; + + st->scales = devm_kmalloc_array(&st->spi->dev, info->num_scales, + sizeof(*st->scales), GFP_KERNEL); + if (!st->scales) + return -ENOMEM; + + for (i = 0; i < info->num_scales; i++) { + __ad9467_get_scale(st, i, &val1, &val2); + st->scales[i][0] = val1; + st->scales[i][1] = val2; + } + + return 0; +} + +static int ad9467_reset(struct device *dev) +{ + struct gpio_desc *gpio; + + gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH); + if (IS_ERR_OR_NULL(gpio)) + return PTR_ERR_OR_ZERO(gpio); + + fsleep(1); + gpiod_set_value_cansleep(gpio, 0); + fsleep(10 * USEC_PER_MSEC); + + return 0; +} + +static int ad9467_iio_backend_get(struct ad9467_state *st) +{ + struct device *dev = &st->spi->dev; + struct device_node *__back; + + st->back = devm_iio_backend_get(dev, NULL); + if (!IS_ERR(st->back)) + return 0; + /* If not found, don't error out as we might have legacy DT property */ + if (PTR_ERR(st->back) != -ENOENT) + return PTR_ERR(st->back); + + /* + * if we don't get the backend using the normal API's, use the legacy + * 'adi,adc-dev' property. So we get all nodes with that property, and + * look for the one pointing at us. Then we directly lookup that fwnode + * on the backend list of registered devices. This is done so we don't + * make io-backends mandatory which would break DT ABI. + */ + for_each_node_with_property(__back, "adi,adc-dev") { + struct device_node *__me; + + __me = of_parse_phandle(__back, "adi,adc-dev", 0); + if (!__me) + continue; + + if (!device_match_of_node(dev, __me)) { + of_node_put(__me); + continue; + } + + of_node_put(__me); + st->back = __devm_iio_backend_get_from_fwnode_lookup(dev, + of_fwnode_handle(__back)); + of_node_put(__back); + return PTR_ERR_OR_ZERO(st->back); + } + + return -ENODEV; +} + +static int ad9467_test_mode_available_show(struct seq_file *s, void *ignored) +{ + struct ad9467_state *st = s->private; + unsigned int bit; + + for_each_set_bit(bit, &st->info->test_mask, st->info->test_mask_len) + seq_printf(s, "%s\n", ad9467_test_modes[bit]); + + return 0; +} +DEFINE_SHOW_ATTRIBUTE(ad9467_test_mode_available); + +static ssize_t ad9467_chan_test_mode_read(struct file *file, + char __user *userbuf, size_t count, + loff_t *ppos) +{ + struct ad9467_chan_test_mode *chan = file->private_data; + struct ad9467_state *st = chan->st; + char buf[128] = {0}; + size_t len; + int ret; + + if (chan->mode == AN877_ADC_TESTMODE_PN9_SEQ || + chan->mode == AN877_ADC_TESTMODE_PN23_SEQ) { + len = scnprintf(buf, sizeof(buf), "Running \"%s\" Test:\n\t", + ad9467_test_modes[chan->mode]); + + if (iio_backend_has_caps(st->back, IIO_BACKEND_CAP_CALIBRATION)) { + ret = iio_backend_debugfs_print_chan_status(st->back, + chan->idx, + buf + len, + sizeof(buf) - len); + if (ret < 0) + return ret; + len += ret; + } + } else if (chan->mode == AN877_ADC_TESTMODE_OFF) { + len = scnprintf(buf, sizeof(buf), "No test Running...\n"); + } else { + len = scnprintf(buf, sizeof(buf), "Running \"%s\" Test on CH:%u\n", + ad9467_test_modes[chan->mode], chan->idx); + } + + return simple_read_from_buffer(userbuf, count, ppos, buf, len); +} + +static ssize_t ad9467_chan_test_mode_write(struct file *file, + const char __user *userbuf, + size_t count, loff_t *ppos) +{ + struct ad9467_chan_test_mode *chan = file->private_data; + struct ad9467_state *st = chan->st; + char test_mode[32] = {0}; + unsigned int mode; + int ret; + + ret = simple_write_to_buffer(test_mode, sizeof(test_mode) - 1, ppos, + userbuf, count); + if (ret < 0) + return ret; + + for_each_set_bit(mode, &st->info->test_mask, st->info->test_mask_len) { + if (sysfs_streq(test_mode, ad9467_test_modes[mode])) + break; + } + + if (mode == st->info->test_mask_len) + return -EINVAL; + + guard(mutex)(&st->lock); + + if (mode == AN877_ADC_TESTMODE_OFF) { + unsigned int out_mode; + + if (iio_backend_has_caps(st->back, IIO_BACKEND_CAP_CALIBRATION)) { + if (chan->mode == AN877_ADC_TESTMODE_PN9_SEQ || + chan->mode == AN877_ADC_TESTMODE_PN23_SEQ) { + ret = ad9467_backend_testmode_off(st, chan->idx); + if (ret) + return ret; + } + } + + ret = ad9467_testmode_set(st, chan->idx, mode); + if (ret) + return ret; + + out_mode = st->info->default_output_mode; + ret = ad9467_outputmode_set(st, out_mode); + if (ret) + return ret; + } else { + unsigned int cmode; + + cmode = st->info->default_output_mode; + FIELD_MODIFY(AN877_ADC_OUTPUT_MODE_MASK, &cmode, + AN877_ADC_OUTPUT_MODE_OFFSET_BINARY); + ret = ad9467_outputmode_set(st, cmode); + if (ret) + return ret; + + ret = ad9467_testmode_set(st, chan->idx, mode); + if (ret) + return ret; + + /* some patterns have a backend matching monitoring block */ + if (iio_backend_has_caps(st->back, IIO_BACKEND_CAP_CALIBRATION)) { + if (mode == AN877_ADC_TESTMODE_PN9_SEQ) { + ret = ad9467_backend_testmode_on(st, chan->idx, + IIO_BACKEND_ADI_PRBS_9A); + if (ret) + return ret; + } else if (mode == AN877_ADC_TESTMODE_PN23_SEQ) { + ret = ad9467_backend_testmode_on(st, chan->idx, + IIO_BACKEND_ADI_PRBS_23A); + if (ret) + return ret; + } + } + } + + chan->mode = mode; + + return count; +} + +static const struct file_operations ad9467_chan_test_mode_fops = { + .open = simple_open, + .read = ad9467_chan_test_mode_read, + .write = ad9467_chan_test_mode_write, + .llseek = default_llseek, + .owner = THIS_MODULE, +}; + +static ssize_t ad9467_dump_calib_table(struct file *file, + char __user *userbuf, + size_t count, loff_t *ppos) +{ + struct ad9467_state *st = file->private_data; + unsigned int bit; + /* +2 for the newline and +1 for the string termination */ + unsigned char map[AD9647_MAX_TEST_POINTS * 2 + 3]; + ssize_t len = 0; + + guard(mutex)(&st->lock); + if (*ppos) + goto out_read; + + for (bit = 0; bit < st->calib_map_size; bit++) { + if (AD9467_CAN_INVERT(st) && bit == st->calib_map_size / 2) + len += scnprintf(map + len, sizeof(map) - len, "\n"); + + len += scnprintf(map + len, sizeof(map) - len, "%c", + test_bit(bit, st->calib_map) ? 'x' : 'o'); + } + + len += scnprintf(map + len, sizeof(map) - len, "\n"); +out_read: + return simple_read_from_buffer(userbuf, count, ppos, map, len); +} + +static const struct file_operations ad9467_calib_table_fops = { + .open = simple_open, + .read = ad9467_dump_calib_table, + .llseek = default_llseek, + .owner = THIS_MODULE, +}; + +static void ad9467_debugfs_init(struct iio_dev *indio_dev) +{ + struct dentry *d = iio_get_debugfs_dentry(indio_dev); + struct ad9467_state *st = iio_priv(indio_dev); + char attr_name[32]; + unsigned int chan; + + if (!IS_ENABLED(CONFIG_DEBUG_FS)) + return; + + st->chan_test = devm_kcalloc(&st->spi->dev, st->info->num_channels, + sizeof(*st->chan_test), GFP_KERNEL); + if (!st->chan_test) + return; + + if (iio_backend_has_caps(st->back, IIO_BACKEND_CAP_CALIBRATION)) + debugfs_create_file("calibration_table_dump", 0400, d, st, + &ad9467_calib_table_fops); + + for (chan = 0; chan < st->info->num_channels; chan++) { + snprintf(attr_name, sizeof(attr_name), "in_voltage%u_test_mode", + chan); + st->chan_test[chan].idx = chan; + st->chan_test[chan].st = st; + debugfs_create_file(attr_name, 0600, d, &st->chan_test[chan], + &ad9467_chan_test_mode_fops); + } + + debugfs_create_file("in_voltage_test_mode_available", 0400, d, st, + &ad9467_test_mode_available_fops); + + iio_backend_debugfs_add(st->back, indio_dev); +} + +static int ad9467_probe(struct spi_device *spi) +{ + struct device *dev = &spi->dev; + struct iio_dev *indio_dev; + struct ad9467_state *st; + unsigned int id; + int ret; + + indio_dev = devm_iio_device_alloc(dev, sizeof(*st)); + if (!indio_dev) + return -ENOMEM; + + st = iio_priv(indio_dev); + st->spi = spi; + + st->info = spi_get_device_match_data(spi); + if (!st->info) + return -ENODEV; + + st->calib_map_size = st->info->test_points; + if (AD9467_CAN_INVERT(st)) + st->calib_map_size *= 2; + + st->clk = devm_clk_get_enabled(dev, "adc-clk"); + if (IS_ERR(st->clk)) + return PTR_ERR(st->clk); + + st->pwrdown_gpio = devm_gpiod_get_optional(dev, "powerdown", GPIOD_OUT_LOW); + if (IS_ERR(st->pwrdown_gpio)) + return PTR_ERR(st->pwrdown_gpio); + + ret = ad9467_reset(dev); + if (ret) + return ret; + + ret = ad9467_scale_fill(st); + if (ret) + return ret; + + id = ad9467_spi_read(st, AN877_ADC_REG_CHIP_ID); + if (id != st->info->id) + return dev_err_probe(dev, -ENODEV, + "Mismatch CHIP_ID, got 0x%X, expected 0x%X\n", + id, st->info->id); + + if (st->info->num_scales > 1) + indio_dev->info = &ad9467_info; + else + indio_dev->info = &ad9467_info_no_read_avail; + indio_dev->name = st->info->name; + indio_dev->channels = st->info->channels; + indio_dev->num_channels = st->info->num_channels; + + ret = ad9467_iio_backend_get(st); + if (ret) + return ret; + + if (iio_backend_has_caps(st->back, IIO_BACKEND_CAP_BUFFER)) { + ret = devm_iio_backend_request_buffer(dev, st->back, indio_dev); + if (ret) + return ret; + } + + if (iio_backend_has_caps(st->back, IIO_BACKEND_CAP_ENABLE)) { + ret = devm_iio_backend_enable(dev, st->back); + if (ret) + return ret; + } + + if (iio_backend_has_caps(st->back, IIO_BACKEND_CAP_CALIBRATION)) { + ret = ad9467_calibrate(st); + if (ret) + return ret; + } + + ret = devm_iio_device_register(dev, indio_dev); + if (ret) + return ret; + + ad9467_debugfs_init(indio_dev); + + return 0; +} + +static const struct of_device_id ad9467_of_match[] = { + { .compatible = "adi,ad9211", .data = &ad9211_chip_tbl, }, + { .compatible = "adi,ad9265", .data = &ad9265_chip_tbl, }, + { .compatible = "adi,ad9434", .data = &ad9434_chip_tbl, }, + { .compatible = "adi,ad9467", .data = &ad9467_chip_tbl, }, + { .compatible = "adi,ad9643", .data = &ad9643_chip_tbl, }, + { .compatible = "adi,ad9649", .data = &ad9649_chip_tbl, }, + { .compatible = "adi,ad9652", .data = &ad9652_chip_tbl, }, + { } +}; +MODULE_DEVICE_TABLE(of, ad9467_of_match); + +static const struct spi_device_id ad9467_ids[] = { + { .name = "ad9211", .driver_data = (kernel_ulong_t)&ad9211_chip_tbl }, + { .name = "ad9265", .driver_data = (kernel_ulong_t)&ad9265_chip_tbl }, + { .name = "ad9434", .driver_data = (kernel_ulong_t)&ad9434_chip_tbl }, + { .name = "ad9467", .driver_data = (kernel_ulong_t)&ad9467_chip_tbl }, + { .name = "ad9643", .driver_data = (kernel_ulong_t)&ad9643_chip_tbl }, + { .name = "ad9649", .driver_data = (kernel_ulong_t)&ad9649_chip_tbl }, + { .name = "ad9652", .driver_data = (kernel_ulong_t)&ad9652_chip_tbl }, + { } +}; +MODULE_DEVICE_TABLE(spi, ad9467_ids); + +static struct spi_driver ad9467_driver = { + .driver = { + .name = "ad9467", + .of_match_table = ad9467_of_match, + }, + .probe = ad9467_probe, + .id_table = ad9467_ids, +}; +module_spi_driver(ad9467_driver); + +MODULE_AUTHOR("Michael Hennerich "); +MODULE_DESCRIPTION("Analog Devices AD9467 ADC driver"); +MODULE_LICENSE("GPL v2"); +MODULE_IMPORT_NS("IIO_BACKEND"); diff --git a/drivers/iio/adc/ad_sigma_delta.c b/drivers/iio/adc/ad_sigma_delta.c new file mode 100644 index 000000000..1b410291d --- /dev/null +++ b/drivers/iio/adc/ad_sigma_delta.c @@ -0,0 +1,950 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Support code for Analog Devices Sigma-Delta ADCs + * + * Copyright 2012 Analog Devices Inc. + * Author: Lars-Peter Clausen + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#define AD_SD_COMM_CHAN_MASK 0x3 + +#define AD_SD_REG_COMM 0x00 +#define AD_SD_REG_STATUS 0x00 +#define AD_SD_REG_DATA 0x03 + +#define AD_SD_REG_STATUS_RDY 0x80 + +/** + * ad_sd_set_comm() - Set communications register + * + * @sigma_delta: The sigma delta device + * @comm: New value for the communications register + */ +void ad_sd_set_comm(struct ad_sigma_delta *sigma_delta, u8 comm) +{ + /* + * Some variants use the lower two bits of the communications register + * to select the channel. + */ + sigma_delta->comm = comm & AD_SD_COMM_CHAN_MASK; +} +EXPORT_SYMBOL_NS_GPL(ad_sd_set_comm, "IIO_AD_SIGMA_DELTA"); + +/** + * ad_sd_write_reg() - Write a register + * + * @sigma_delta: The sigma delta device + * @reg: Address of the register + * @size: Size of the register (0-3) + * @val: Value to write to the register + * + * Returns 0 on success, an error code otherwise. + **/ +int ad_sd_write_reg(struct ad_sigma_delta *sigma_delta, unsigned int reg, + unsigned int size, unsigned int val) +{ + u8 *data = sigma_delta->tx_buf; + struct spi_transfer t = { + .tx_buf = data, + .len = size + 1, + .cs_change = sigma_delta->keep_cs_asserted, + }; + struct spi_message m; + int ret; + + data[0] = (reg << sigma_delta->info->addr_shift) | sigma_delta->comm; + + switch (size) { + case 3: + put_unaligned_be24(val, &data[1]); + break; + case 2: + put_unaligned_be16(val, &data[1]); + break; + case 1: + data[1] = val; + break; + case 0: + break; + default: + return -EINVAL; + } + + spi_message_init(&m); + spi_message_add_tail(&t, &m); + + if (sigma_delta->bus_locked) + ret = spi_sync_locked(sigma_delta->spi, &m); + else + ret = spi_sync(sigma_delta->spi, &m); + + return ret; +} +EXPORT_SYMBOL_NS_GPL(ad_sd_write_reg, "IIO_AD_SIGMA_DELTA"); + +static void ad_sd_set_read_reg_addr(struct ad_sigma_delta *sigma_delta, u8 reg, + u8 *data) +{ + data[0] = reg << sigma_delta->info->addr_shift; + data[0] |= sigma_delta->info->read_mask; + data[0] |= sigma_delta->comm; +} + +static int ad_sd_read_reg_raw(struct ad_sigma_delta *sigma_delta, + unsigned int reg, unsigned int size, u8 *val) +{ + u8 *data = sigma_delta->tx_buf; + int ret; + struct spi_transfer t[] = { + { + .tx_buf = data, + .len = 1, + }, { + .rx_buf = val, + .len = size, + .cs_change = sigma_delta->keep_cs_asserted, + }, + }; + struct spi_message m; + + spi_message_init(&m); + + if (sigma_delta->info->has_registers) { + ad_sd_set_read_reg_addr(sigma_delta, reg, data); + spi_message_add_tail(&t[0], &m); + } + spi_message_add_tail(&t[1], &m); + + if (sigma_delta->bus_locked) + ret = spi_sync_locked(sigma_delta->spi, &m); + else + ret = spi_sync(sigma_delta->spi, &m); + + return ret; +} + +/** + * ad_sd_read_reg() - Read a register + * + * @sigma_delta: The sigma delta device + * @reg: Address of the register + * @size: Size of the register (1-4) + * @val: Read value + * + * Returns 0 on success, an error code otherwise. + **/ +int ad_sd_read_reg(struct ad_sigma_delta *sigma_delta, + unsigned int reg, unsigned int size, unsigned int *val) +{ + int ret; + + ret = ad_sd_read_reg_raw(sigma_delta, reg, size, sigma_delta->rx_buf); + if (ret < 0) + goto out; + + switch (size) { + case 4: + *val = get_unaligned_be32(sigma_delta->rx_buf); + break; + case 3: + *val = get_unaligned_be24(sigma_delta->rx_buf); + break; + case 2: + *val = get_unaligned_be16(sigma_delta->rx_buf); + break; + case 1: + *val = sigma_delta->rx_buf[0]; + break; + default: + ret = -EINVAL; + break; + } + +out: + return ret; +} +EXPORT_SYMBOL_NS_GPL(ad_sd_read_reg, "IIO_AD_SIGMA_DELTA"); + +/** + * ad_sd_reset() - Reset the serial interface + * + * @sigma_delta: The sigma delta device + * + * Returns 0 on success, an error code otherwise. + **/ +int ad_sd_reset(struct ad_sigma_delta *sigma_delta) +{ + unsigned int reset_length = sigma_delta->info->num_resetclks; + unsigned int size; + u8 *buf; + int ret; + + size = BITS_TO_BYTES(reset_length); + buf = kcalloc(size, sizeof(*buf), GFP_KERNEL); + if (!buf) + return -ENOMEM; + + memset(buf, 0xff, size); + ret = spi_write(sigma_delta->spi, buf, size); + kfree(buf); + + return ret; +} +EXPORT_SYMBOL_NS_GPL(ad_sd_reset, "IIO_AD_SIGMA_DELTA"); + +static bool ad_sd_disable_irq(struct ad_sigma_delta *sigma_delta) +{ + guard(spinlock_irqsave)(&sigma_delta->irq_lock); + + /* It's already off, return false to indicate nothing was changed */ + if (sigma_delta->irq_dis) + return false; + + sigma_delta->irq_dis = true; + disable_irq_nosync(sigma_delta->irq_line); + return true; +} + +static void ad_sd_enable_irq(struct ad_sigma_delta *sigma_delta) +{ + guard(spinlock_irqsave)(&sigma_delta->irq_lock); + + sigma_delta->irq_dis = false; + enable_irq(sigma_delta->irq_line); +} + +#define AD_SD_CLEAR_DATA_BUFLEN 9 + +/* Called with `sigma_delta->bus_locked == true` only. */ +static int ad_sigma_delta_clear_pending_event(struct ad_sigma_delta *sigma_delta) +{ + bool pending_event; + unsigned int data_read_len = BITS_TO_BYTES(sigma_delta->info->num_resetclks); + u8 *data; + struct spi_transfer t[] = { + { + .len = 1, + }, { + .len = data_read_len, + } + }; + struct spi_message m; + int ret; + + /* + * Read R̅D̅Y̅ pin (if possible) or status register to check if there is an + * old event. For devices with neither an RDY GPIO nor registers, + * ad_sd_read_reg() transmits no address byte and clocks raw MISO bytes, + * which is indistinguishable from reading conversion data and would + * partially consume a pending result. Skip the check for such devices. + * + * This is safe for all current registerless devices: ad7191 and ad7780 + * (with powerdown GPIO) are reset between conversions by CS deassertion, + * so there is no stale result to drain; ad7780 (without powerdown GPIO) + * and max11205 are continuously-converting and cycle ~DRDY at the output + * data rate regardless of whether the previous result was read, so the + * next falling edge fires naturally. + * + * A future registerless device that holds ~DRDY asserted until data is + * read would be broken by this early return and would need either + * num_resetclks set or a rdy-gpio. + */ + if (sigma_delta->rdy_gpiod) { + pending_event = gpiod_get_value(sigma_delta->rdy_gpiod); + } else if (sigma_delta->info->has_registers) { + unsigned int status_reg; + + ret = ad_sd_read_reg(sigma_delta, AD_SD_REG_STATUS, 1, &status_reg); + if (ret) + return ret; + + pending_event = !(status_reg & AD_SD_REG_STATUS_RDY); + } else { + return 0; + } + + if (!pending_event) + return 0; + + /* + * With num_resetclks = 0, data_read_len is 0 and the drain sequence + * below would compute memset(data + 2, 0xff, 0 - 1), underflowing to + * SIZE_MAX and corrupting the heap. There is no safe way to drain the + * stale result without knowing the data register size; it will be + * consumed by the first ad_sd_read_reg() call in + * ad_sigma_delta_single_conversion(). + */ + if (!data_read_len) + return 0; + + /* + * In general the size of the data register is unknown. It varies from + * device to device, might be one byte longer if CONTROL.DATA_STATUS is + * set and even varies on some devices depending on which input is + * selected. So send one byte to start reading the data register and + * then just clock for some bytes with DIN (aka MOSI) high to not + * confuse the register access state machine after the data register was + * completely read. Note however that the sequence length must be + * shorter than the reset procedure. + */ + + data = kzalloc(data_read_len + 1, GFP_KERNEL); + if (!data) + return -ENOMEM; + + spi_message_init(&m); + if (sigma_delta->info->has_registers) { + unsigned int data_reg = sigma_delta->info->data_reg ?: AD_SD_REG_DATA; + + ad_sd_set_read_reg_addr(sigma_delta, data_reg, data); + t[0].tx_buf = data; + spi_message_add_tail(&t[0], &m); + } + + /* + * The first transferred byte is part of the real data register, + * so this doesn't need to be 0xff. In the remaining + * `data_read_len - 1` bytes are less than $num_resetclks ones. + */ + t[1].tx_buf = data + 1; + data[1] = 0x00; + memset(data + 2, 0xff, data_read_len - 1); + spi_message_add_tail(&t[1], &m); + + ret = spi_sync_locked(sigma_delta->spi, &m); + + kfree(data); + + return ret; +} + +int ad_sd_calibrate(struct ad_sigma_delta *sigma_delta, + unsigned int mode, unsigned int channel) +{ + int ret; + unsigned long time_left; + + ret = ad_sigma_delta_set_channel(sigma_delta, channel); + if (ret) + return ret; + + spi_bus_lock(sigma_delta->spi->controller); + sigma_delta->bus_locked = true; + sigma_delta->keep_cs_asserted = true; + reinit_completion(&sigma_delta->completion); + + ret = ad_sigma_delta_clear_pending_event(sigma_delta); + if (ret) + goto out; + + ret = ad_sigma_delta_set_mode(sigma_delta, mode); + if (ret < 0) + goto out; + + ad_sd_enable_irq(sigma_delta); + time_left = wait_for_completion_timeout(&sigma_delta->completion, 2 * HZ); + if (time_left == 0) { + ad_sd_disable_irq(sigma_delta); + ret = -EIO; + } else { + ret = 0; + } +out: + sigma_delta->keep_cs_asserted = false; + ad_sigma_delta_set_mode(sigma_delta, AD_SD_MODE_IDLE); + ad_sigma_delta_disable_one(sigma_delta, channel); + sigma_delta->bus_locked = false; + spi_bus_unlock(sigma_delta->spi->controller); + + return ret; +} +EXPORT_SYMBOL_NS_GPL(ad_sd_calibrate, "IIO_AD_SIGMA_DELTA"); + +/** + * ad_sd_calibrate_all() - Performs channel calibration + * @sigma_delta: The sigma delta device + * @cb: Array of channels and calibration type to perform + * @n: Number of items in cb + * + * Returns 0 on success, an error code otherwise. + **/ +int ad_sd_calibrate_all(struct ad_sigma_delta *sigma_delta, + const struct ad_sd_calib_data *cb, unsigned int n) +{ + unsigned int i; + int ret; + + for (i = 0; i < n; i++) { + ret = ad_sd_calibrate(sigma_delta, cb[i].mode, cb[i].channel); + if (ret) + return ret; + } + + return 0; +} +EXPORT_SYMBOL_NS_GPL(ad_sd_calibrate_all, "IIO_AD_SIGMA_DELTA"); + +/** + * ad_sigma_delta_single_conversion() - Performs a single data conversion + * @indio_dev: The IIO device + * @chan: The conversion is done for this channel + * @val: Pointer to the location where to store the read value + * + * Returns: 0 on success, an error value otherwise. + */ +int ad_sigma_delta_single_conversion(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, int *val) +{ + struct ad_sigma_delta *sigma_delta = iio_device_get_drvdata(indio_dev); + unsigned int sample, raw_sample; + unsigned int data_reg; + int ret = 0; + + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; + + ret = ad_sigma_delta_set_channel(sigma_delta, chan->address); + if (ret) + goto out_release; + + spi_bus_lock(sigma_delta->spi->controller); + sigma_delta->bus_locked = true; + sigma_delta->keep_cs_asserted = true; + reinit_completion(&sigma_delta->completion); + + ret = ad_sigma_delta_clear_pending_event(sigma_delta); + if (ret) + goto out_unlock; + + ad_sigma_delta_set_mode(sigma_delta, AD_SD_MODE_SINGLE); + + ad_sd_enable_irq(sigma_delta); + ret = wait_for_completion_interruptible_timeout( + &sigma_delta->completion, HZ); + + if (ret == 0) + ret = -EIO; + if (ret < 0) + goto out; + + if (sigma_delta->info->data_reg != 0) + data_reg = sigma_delta->info->data_reg; + else + data_reg = AD_SD_REG_DATA; + + ret = ad_sd_read_reg(sigma_delta, data_reg, + BITS_TO_BYTES(chan->scan_type.realbits + chan->scan_type.shift), + &raw_sample); + +out: + ad_sd_disable_irq(sigma_delta); + +out_unlock: + sigma_delta->keep_cs_asserted = false; + ad_sigma_delta_set_mode(sigma_delta, AD_SD_MODE_IDLE); + ad_sigma_delta_disable_one(sigma_delta, chan->address); + sigma_delta->bus_locked = false; + spi_bus_unlock(sigma_delta->spi->controller); +out_release: + iio_device_release_direct(indio_dev); + + if (ret) + return ret; + + sample = raw_sample >> chan->scan_type.shift; + sample &= (1 << chan->scan_type.realbits) - 1; + *val = sample; + + ret = ad_sigma_delta_postprocess_sample(sigma_delta, raw_sample); + if (ret) + return ret; + + return IIO_VAL_INT; +} +EXPORT_SYMBOL_NS_GPL(ad_sigma_delta_single_conversion, "IIO_AD_SIGMA_DELTA"); + +static int ad_sd_buffer_postenable(struct iio_dev *indio_dev) +{ + struct ad_sigma_delta *sigma_delta = iio_device_get_drvdata(indio_dev); + const struct iio_scan_type *scan_type = &indio_dev->channels[0].scan_type; + struct spi_transfer *xfer = sigma_delta->sample_xfer; + unsigned int i, slot, channel; + u8 *samples_buf; + int ret; + + if (sigma_delta->num_slots == 1) { + channel = find_first_bit(indio_dev->active_scan_mask, + iio_get_masklength(indio_dev)); + ret = ad_sigma_delta_set_channel(sigma_delta, + indio_dev->channels[channel].address); + if (ret) + return ret; + slot = 1; + } else { + /* + * At this point update_scan_mode already enabled the required channels. + * For sigma-delta sequencer drivers with multiple slots, an update_scan_mode + * implementation is mandatory. + */ + slot = 0; + iio_for_each_active_channel(indio_dev, i) { + sigma_delta->slots[slot] = indio_dev->channels[i].address; + slot++; + } + } + + sigma_delta->active_slots = slot; + sigma_delta->current_slot = 0; + + if (ad_sigma_delta_has_spi_offload(sigma_delta)) { + xfer[1].offload_flags = SPI_OFFLOAD_XFER_RX_STREAM; + xfer[1].bits_per_word = scan_type->realbits; + xfer[1].len = spi_bpw_to_bytes(scan_type->realbits); + } else { + unsigned int samples_buf_size, scan_size; + + if (sigma_delta->active_slots > 1) { + ret = ad_sigma_delta_append_status(sigma_delta, true); + if (ret) + return ret; + } + + samples_buf_size = + ALIGN(slot * BITS_TO_BYTES(scan_type->storagebits), + sizeof(s64)); + samples_buf_size += sizeof(s64); + samples_buf = devm_krealloc(&sigma_delta->spi->dev, + sigma_delta->samples_buf, + samples_buf_size, GFP_KERNEL); + if (!samples_buf) + return -ENOMEM; + + sigma_delta->samples_buf = samples_buf; + scan_size = BITS_TO_BYTES(scan_type->realbits + scan_type->shift); + /* For 24-bit data, there is an extra byte of padding. */ + xfer[1].rx_buf = &sigma_delta->rx_buf[scan_size == 3 ? 1 : 0]; + xfer[1].len = scan_size + (sigma_delta->status_appended ? 1 : 0); + } + xfer[1].cs_change = 1; + + if (sigma_delta->info->has_registers) { + xfer[0].tx_buf = &sigma_delta->sample_addr; + xfer[0].len = 1; + + ad_sd_set_read_reg_addr(sigma_delta, + sigma_delta->info->data_reg ?: AD_SD_REG_DATA, + &sigma_delta->sample_addr); + spi_message_init_with_transfers(&sigma_delta->sample_msg, xfer, 2); + } else { + spi_message_init_with_transfers(&sigma_delta->sample_msg, + &xfer[1], 1); + } + + sigma_delta->sample_msg.offload = sigma_delta->offload; + + ret = spi_optimize_message(sigma_delta->spi, &sigma_delta->sample_msg); + if (ret) + return ret; + + spi_bus_lock(sigma_delta->spi->controller); + sigma_delta->bus_locked = true; + sigma_delta->keep_cs_asserted = true; + + ret = ad_sigma_delta_clear_pending_event(sigma_delta); + if (ret) + goto err_unlock; + + ret = ad_sigma_delta_set_mode(sigma_delta, AD_SD_MODE_CONTINUOUS); + if (ret) + goto err_unlock; + + if (ad_sigma_delta_has_spi_offload(sigma_delta)) { + struct spi_offload_trigger_config config = { + .type = SPI_OFFLOAD_TRIGGER_DATA_READY, + }; + + ret = spi_offload_trigger_enable(sigma_delta->offload, + sigma_delta->offload_trigger, + &config); + if (ret) + goto err_unlock; + } else { + ad_sd_enable_irq(sigma_delta); + } + + return 0; + +err_unlock: + sigma_delta->keep_cs_asserted = false; + ad_sigma_delta_set_mode(sigma_delta, AD_SD_MODE_IDLE); + sigma_delta->bus_locked = false; + spi_bus_unlock(sigma_delta->spi->controller); + spi_unoptimize_message(&sigma_delta->sample_msg); + + return ret; +} + +static int ad_sd_buffer_predisable(struct iio_dev *indio_dev) +{ + struct ad_sigma_delta *sigma_delta = iio_device_get_drvdata(indio_dev); + + if (ad_sigma_delta_has_spi_offload(sigma_delta)) { + spi_offload_trigger_disable(sigma_delta->offload, + sigma_delta->offload_trigger); + } else { + reinit_completion(&sigma_delta->completion); + wait_for_completion_timeout(&sigma_delta->completion, HZ); + + ad_sd_disable_irq(sigma_delta); + } + + sigma_delta->keep_cs_asserted = false; + ad_sigma_delta_set_mode(sigma_delta, AD_SD_MODE_IDLE); + + if (sigma_delta->status_appended) + ad_sigma_delta_append_status(sigma_delta, false); + + ad_sigma_delta_disable_all(sigma_delta); + sigma_delta->bus_locked = false; + spi_bus_unlock(sigma_delta->spi->controller); + spi_unoptimize_message(&sigma_delta->sample_msg); + + return 0; +} + +static irqreturn_t ad_sd_trigger_handler(int irq, void *p) +{ + struct iio_poll_func *pf = p; + struct iio_dev *indio_dev = pf->indio_dev; + const struct iio_scan_type *scan_type = &indio_dev->channels[0].scan_type; + struct ad_sigma_delta *sigma_delta = iio_device_get_drvdata(indio_dev); + u8 *data = sigma_delta->rx_buf; + unsigned int sample_size; + unsigned int sample_pos; + unsigned int status_pos; + unsigned int reg_size; + int ret; + + reg_size = BITS_TO_BYTES(scan_type->realbits + scan_type->shift); + /* For 24-bit data, there is an extra byte of padding. */ + status_pos = reg_size + (reg_size == 3 ? 1 : 0); + + ret = spi_sync_locked(sigma_delta->spi, &sigma_delta->sample_msg); + if (ret) + goto irq_handled; + + /* + * For devices sampling only one channel at + * once, there is no need for sample number tracking. + */ + if (sigma_delta->active_slots == 1) { + iio_push_to_buffers_with_timestamp(indio_dev, data, pf->timestamp); + goto irq_handled; + } + + if (sigma_delta->status_appended) { + u8 converted_channel; + + converted_channel = data[status_pos] & sigma_delta->info->status_ch_mask; + if (converted_channel != sigma_delta->slots[sigma_delta->current_slot]) { + /* + * Desync occurred during continuous sampling of multiple channels. + * Drop this incomplete sample and start from first channel again. + */ + + sigma_delta->current_slot = 0; + goto irq_handled; + } + } + + sample_size = BITS_TO_BYTES(scan_type->storagebits); + sample_pos = sample_size * sigma_delta->current_slot; + memcpy(&sigma_delta->samples_buf[sample_pos], data, sample_size); + sigma_delta->current_slot++; + + if (sigma_delta->current_slot == sigma_delta->active_slots) { + sigma_delta->current_slot = 0; + iio_push_to_buffers_with_timestamp(indio_dev, sigma_delta->samples_buf, + pf->timestamp); + } + +irq_handled: + iio_trigger_notify_done(indio_dev->trig); + ad_sd_enable_irq(sigma_delta); + + return IRQ_HANDLED; +} + +static bool ad_sd_validate_scan_mask(struct iio_dev *indio_dev, const unsigned long *mask) +{ + struct ad_sigma_delta *sigma_delta = iio_device_get_drvdata(indio_dev); + + return bitmap_weight(mask, iio_get_masklength(indio_dev)) <= sigma_delta->num_slots; +} + +static const struct iio_buffer_setup_ops ad_sd_buffer_setup_ops = { + .postenable = &ad_sd_buffer_postenable, + .predisable = &ad_sd_buffer_predisable, + .validate_scan_mask = &ad_sd_validate_scan_mask, +}; + +static irqreturn_t ad_sd_data_rdy_trig_poll(int irq, void *private) +{ + struct ad_sigma_delta *sigma_delta = private; + + /* + * AD7124 and a few others use the same physical line for interrupt + * reporting (R̅D̅Y̅) and MISO. + * As MISO toggles when reading a register, this likely results in a + * pending interrupt. This has two consequences: a) The irq might + * trigger immediately after it's enabled even though the conversion + * isn't done yet; and b) checking the STATUS register's R̅D̅Y̅ flag is + * off-limits as reading that would trigger another irq event. + * + * So read the MOSI line as GPIO (if available) and only trigger the irq + * if the line is active. Without such a GPIO assume this is a valid + * interrupt. + * + * Also as disable_irq_nosync() is used to disable the irq, only act if + * the irq wasn't disabled before. + */ + if ((!sigma_delta->rdy_gpiod || gpiod_get_value(sigma_delta->rdy_gpiod)) && + ad_sd_disable_irq(sigma_delta)) { + complete(&sigma_delta->completion); + if (sigma_delta->trig) + iio_trigger_poll(sigma_delta->trig); + + return IRQ_HANDLED; + } + + return IRQ_NONE; +} + +/** + * ad_sd_validate_trigger() - validate_trigger callback for ad_sigma_delta devices + * @indio_dev: The IIO device + * @trig: The new trigger + * + * Returns: 0 if the 'trig' matches the trigger registered by the ad_sigma_delta + * device, -EINVAL otherwise. + */ +int ad_sd_validate_trigger(struct iio_dev *indio_dev, struct iio_trigger *trig) +{ + struct ad_sigma_delta *sigma_delta = iio_device_get_drvdata(indio_dev); + + if (sigma_delta->trig != trig) + return -EINVAL; + + return 0; +} +EXPORT_SYMBOL_NS_GPL(ad_sd_validate_trigger, "IIO_AD_SIGMA_DELTA"); + +static int devm_ad_sd_probe_trigger(struct device *dev, struct iio_dev *indio_dev) +{ + struct ad_sigma_delta *sigma_delta = iio_device_get_drvdata(indio_dev); + unsigned long irq_flags = irq_get_trigger_type(sigma_delta->irq_line); + int ret; + + init_completion(&sigma_delta->completion); + + sigma_delta->irq_dis = true; + + /* the IRQ core clears IRQ_DISABLE_UNLAZY flag when freeing an IRQ */ + irq_set_status_flags(sigma_delta->irq_line, IRQ_DISABLE_UNLAZY); + + /* Allow overwriting the flags from firmware */ + if (!irq_flags) + irq_flags = sigma_delta->info->irq_flags; + + ret = devm_request_irq(dev, sigma_delta->irq_line, + ad_sd_data_rdy_trig_poll, + irq_flags | IRQF_NO_AUTOEN, + indio_dev->name, + sigma_delta); + if (ret) + return ret; + + if (ad_sigma_delta_has_spi_offload(sigma_delta)) { + sigma_delta->offload_trigger = + devm_spi_offload_trigger_get(dev, sigma_delta->offload, + SPI_OFFLOAD_TRIGGER_DATA_READY); + if (IS_ERR(sigma_delta->offload_trigger)) + return dev_err_probe(dev, PTR_ERR(sigma_delta->offload_trigger), + "Failed to get SPI offload trigger\n"); + } else { + if (dev != &sigma_delta->spi->dev) + return dev_err_probe(dev, -EFAULT, + "Trigger parent should be '%s', got '%s'\n", + dev_name(dev), dev_name(&sigma_delta->spi->dev)); + + sigma_delta->trig = devm_iio_trigger_alloc(dev, "%s-dev%d", + indio_dev->name, iio_device_id(indio_dev)); + if (!sigma_delta->trig) + return -ENOMEM; + + iio_trigger_set_drvdata(sigma_delta->trig, sigma_delta); + + ret = devm_iio_trigger_register(dev, sigma_delta->trig); + if (ret) + return ret; + + /* select default trigger */ + indio_dev->trig = iio_trigger_get(sigma_delta->trig); + } + + return 0; +} + +/** + * devm_ad_sd_setup_buffer_and_trigger() - Device-managed buffer & trigger setup + * @dev: Device object to which to bind the life-time of the resources attached + * @indio_dev: The IIO device + */ +int devm_ad_sd_setup_buffer_and_trigger(struct device *dev, struct iio_dev *indio_dev) +{ + struct ad_sigma_delta *sigma_delta = iio_device_get_drvdata(indio_dev); + int ret; + + sigma_delta->slots = devm_kcalloc(dev, sigma_delta->num_slots, + sizeof(*sigma_delta->slots), GFP_KERNEL); + if (!sigma_delta->slots) + return -ENOMEM; + + if (ad_sigma_delta_has_spi_offload(sigma_delta)) { + struct dma_chan *rx_dma; + + rx_dma = devm_spi_offload_rx_stream_request_dma_chan(dev, + sigma_delta->offload); + if (IS_ERR(rx_dma)) + return dev_err_probe(dev, PTR_ERR(rx_dma), + "Failed to get RX DMA channel\n"); + + ret = devm_iio_dmaengine_buffer_setup_with_handle(dev, indio_dev, + rx_dma, IIO_BUFFER_DIRECTION_IN); + if (ret) + return dev_err_probe(dev, ret, "Cannot setup DMA buffer\n"); + + indio_dev->setup_ops = &ad_sd_buffer_setup_ops; + } else { + ret = devm_iio_triggered_buffer_setup(dev, indio_dev, + &iio_pollfunc_store_time, + &ad_sd_trigger_handler, + &ad_sd_buffer_setup_ops); + if (ret) + return ret; + } + + return devm_ad_sd_probe_trigger(dev, indio_dev); +} +EXPORT_SYMBOL_NS_GPL(devm_ad_sd_setup_buffer_and_trigger, "IIO_AD_SIGMA_DELTA"); + +/** + * ad_sd_init() - Initializes a ad_sigma_delta struct + * @sigma_delta: The ad_sigma_delta device + * @indio_dev: The IIO device which the Sigma Delta device is used for + * @spi: The SPI device for the ad_sigma_delta device + * @info: Device specific callbacks and options + * + * This function needs to be called before any other operations are performed on + * the ad_sigma_delta struct. + */ +int ad_sd_init(struct ad_sigma_delta *sigma_delta, struct iio_dev *indio_dev, + struct spi_device *spi, const struct ad_sigma_delta_info *info) +{ + sigma_delta->spi = spi; + sigma_delta->info = info; + + /* If the field is unset in ad_sigma_delta_info, assume there can only be 1 slot. */ + if (!info->num_slots) + sigma_delta->num_slots = 1; + else + sigma_delta->num_slots = info->num_slots; + + if (sigma_delta->num_slots > 1) { + if (!indio_dev->info->update_scan_mode) { + dev_err(&spi->dev, "iio_dev lacks update_scan_mode().\n"); + return -EINVAL; + } + + if (!info->disable_all) { + dev_err(&spi->dev, "ad_sigma_delta_info lacks disable_all().\n"); + return -EINVAL; + } + } + + spin_lock_init(&sigma_delta->irq_lock); + + if (info->has_named_irqs) { + sigma_delta->irq_line = fwnode_irq_get_byname(dev_fwnode(&spi->dev), + "rdy"); + if (sigma_delta->irq_line < 0) + return dev_err_probe(&spi->dev, sigma_delta->irq_line, + "Interrupt 'rdy' is required\n"); + } else { + sigma_delta->irq_line = spi->irq; + } + + sigma_delta->rdy_gpiod = devm_gpiod_get_optional(&spi->dev, "rdy", GPIOD_IN); + if (IS_ERR(sigma_delta->rdy_gpiod)) + return dev_err_probe(&spi->dev, PTR_ERR(sigma_delta->rdy_gpiod), + "Failed to find rdy gpio\n"); + + if (sigma_delta->rdy_gpiod && !sigma_delta->irq_line) { + sigma_delta->irq_line = gpiod_to_irq(sigma_delta->rdy_gpiod); + if (sigma_delta->irq_line < 0) + return sigma_delta->irq_line; + } + + if (info->supports_spi_offload) { + struct spi_offload_config offload_config = { + .capability_flags = SPI_OFFLOAD_CAP_TRIGGER | + SPI_OFFLOAD_CAP_RX_STREAM_DMA, + }; + int ret; + + sigma_delta->offload = devm_spi_offload_get(&spi->dev, spi, + &offload_config); + ret = PTR_ERR_OR_ZERO(sigma_delta->offload); + if (ret && ret != -ENODEV) + return dev_err_probe(&spi->dev, ret, "Failed to get SPI offload\n"); + } + + iio_device_set_drvdata(indio_dev, sigma_delta); + + return 0; +} +EXPORT_SYMBOL_NS_GPL(ad_sd_init, "IIO_AD_SIGMA_DELTA"); + +MODULE_AUTHOR("Lars-Peter Clausen "); +MODULE_DESCRIPTION("Analog Devices Sigma-Delta ADCs"); +MODULE_LICENSE("GPL v2"); +MODULE_IMPORT_NS("IIO_DMAENGINE_BUFFER"); diff --git a/drivers/iio/adc/ade9000.c b/drivers/iio/adc/ade9000.c new file mode 100644 index 000000000..c6c3ea953 --- /dev/null +++ b/drivers/iio/adc/ade9000.c @@ -0,0 +1,1794 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * ADE9000 driver + * + * Copyright 2025 Analog Devices Inc. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Address of ADE9000 registers */ +#define ADE9000_REG_AIGAIN 0x000 +#define ADE9000_REG_AVGAIN 0x00B +#define ADE9000_REG_AIRMSOS 0x00C +#define ADE9000_REG_AVRMSOS 0x00D +#define ADE9000_REG_APGAIN 0x00E +#define ADE9000_REG_AWATTOS 0x00F +#define ADE9000_REG_AVAROS 0x010 +#define ADE9000_REG_AFVAROS 0x012 +#define ADE9000_REG_CONFIG0 0x060 +#define ADE9000_REG_DICOEFF 0x072 +#define ADE9000_REG_AI_PCF 0x20A +#define ADE9000_REG_AV_PCF 0x20B +#define ADE9000_REG_AIRMS 0x20C +#define ADE9000_REG_AVRMS 0x20D +#define ADE9000_REG_AWATT 0x210 +#define ADE9000_REG_AVAR 0x211 +#define ADE9000_REG_AVA 0x212 +#define ADE9000_REG_AFVAR 0x214 +#define ADE9000_REG_APF 0x216 +#define ADE9000_REG_BI_PCF 0x22A +#define ADE9000_REG_BV_PCF 0x22B +#define ADE9000_REG_BIRMS 0x22C +#define ADE9000_REG_BVRMS 0x22D +#define ADE9000_REG_CI_PCF 0x24A +#define ADE9000_REG_CV_PCF 0x24B +#define ADE9000_REG_CIRMS 0x24C +#define ADE9000_REG_CVRMS 0x24D +#define ADE9000_REG_AWATT_ACC 0x2E5 +#define ADE9000_REG_AWATTHR_LO 0x2E6 +#define ADE9000_REG_AVAHR_LO 0x2FA +#define ADE9000_REG_AFVARHR_LO 0x30E +#define ADE9000_REG_BWATTHR_LO 0x322 +#define ADE9000_REG_BVAHR_LO 0x336 +#define ADE9000_REG_BFVARHR_LO 0x34A +#define ADE9000_REG_CWATTHR_LO 0x35E +#define ADE9000_REG_CVAHR_LO 0x372 +#define ADE9000_REG_CFVARHR_LO 0x386 +#define ADE9000_REG_STATUS0 0x402 +#define ADE9000_REG_STATUS1 0x403 +#define ADE9000_REG_MASK0 0x405 +#define ADE9000_REG_MASK1 0x406 +#define ADE9000_REG_EVENT_MASK 0x407 +#define ADE9000_REG_VLEVEL 0x40F +#define ADE9000_REG_DIP_LVL 0x410 +#define ADE9000_REG_DIPA 0x411 +#define ADE9000_REG_DIPB 0x412 +#define ADE9000_REG_DIPC 0x413 +#define ADE9000_REG_SWELL_LVL 0x414 +#define ADE9000_REG_SWELLA 0x415 +#define ADE9000_REG_SWELLB 0x416 +#define ADE9000_REG_SWELLC 0x417 +#define ADE9000_REG_APERIOD 0x418 +#define ADE9000_REG_BPERIOD 0x419 +#define ADE9000_REG_CPERIOD 0x41A +#define ADE9000_REG_RUN 0x480 +#define ADE9000_REG_CONFIG1 0x481 +#define ADE9000_REG_ACCMODE 0x492 +#define ADE9000_REG_CONFIG3 0x493 +#define ADE9000_REG_ZXTOUT 0x498 +#define ADE9000_REG_ZX_LP_SEL 0x49A +#define ADE9000_REG_WFB_CFG 0x4A0 +#define ADE9000_REG_WFB_PG_IRQEN 0x4A1 +#define ADE9000_REG_WFB_TRG_CFG 0x4A2 +#define ADE9000_REG_WFB_TRG_STAT 0x4A3 +#define ADE9000_REG_CONFIG2 0x4AF +#define ADE9000_REG_EP_CFG 0x4B0 +#define ADE9000_REG_EGY_TIME 0x4B2 +#define ADE9000_REG_PGA_GAIN 0x4B9 +#define ADE9000_REG_VERSION 0x4FE +#define ADE9000_REG_WF_BUFF 0x800 +#define ADE9000_REG_WF_HALF_BUFF 0xC00 + +#define ADE9000_REG_ADDR_MASK GENMASK(15, 4) +#define ADE9000_REG_READ_BIT_MASK BIT(3) + +#define ADE9000_WF_CAP_EN_MASK BIT(4) +#define ADE9000_WF_CAP_SEL_MASK BIT(5) +#define ADE9000_WF_MODE_MASK GENMASK(7, 6) +#define ADE9000_WF_SRC_MASK GENMASK(9, 8) +#define ADE9000_WF_IN_EN_MASK BIT(12) + +/* External reference selection bit in CONFIG1 */ +#define ADE9000_EXT_REF_MASK BIT(15) + +/* + * Configuration registers + */ +#define ADE9000_PGA_GAIN 0x0000 + +/* Default configuration */ + +#define ADE9000_CONFIG0 0x00000000 + +/* CF3/ZX pin outputs Zero crossing, CF4 = DREADY */ +#define ADE9000_CONFIG1 0x000E + +/* Default High pass corner frequency of 1.25Hz */ +#define ADE9000_CONFIG2 0x0A00 + +/* Peak and overcurrent detection disabled */ +#define ADE9000_CONFIG3 0x0000 + +/* + * 50Hz operation, 3P4W Wye configuration, signed accumulation + * 3P4W Wye = 3-Phase 4-Wire star configuration (3 phases + neutral wire) + * Clear bit 8 i.e. ACCMODE=0x00xx for 50Hz operation + * ACCMODE=0x0x9x for 3Wire delta when phase B is used as reference + * 3Wire delta = 3-Phase 3-Wire triangle configuration (3 phases, no neutral) + */ +#define ADE9000_ACCMODE 0x0000 +#define ADE9000_ACCMODE_60HZ 0x0100 + +/*Line period and zero crossing obtained from VA */ +#define ADE9000_ZX_LP_SEL 0x0000 + +/* Interrupt mask values for initialization */ +#define ADE9000_MASK0_ALL_INT_DIS 0 +#define ADE9000_MASK1_ALL_INT_DIS 0x00000000 + +/* Events disabled */ +#define ADE9000_EVENT_DISABLE 0x00000000 + +/* + * Assuming Vnom=1/2 of full scale. + * Refer to Technical reference manual for detailed calculations. + */ +#define ADE9000_VLEVEL 0x0022EA28 + +/* Set DICOEFF= 0xFFFFE000 when integrator is enabled */ +#define ADE9000_DICOEFF 0x00000000 + +/* DSP ON */ +#define ADE9000_RUN_ON 0xFFFFFFFF + +/* + * Energy Accumulation Settings + * Enable energy accumulation, accumulate samples at 8ksps + * latch energy accumulation after EGYRDY + * If accumulation is changed to half line cycle mode, change EGY_TIME + */ +#define ADE9000_EP_CFG 0x0011 + +/* Accumulate 4000 samples */ +#define ADE9000_EGY_TIME 7999 + +/* + * Constant Definitions + * ADE9000 FDSP: 8000sps, ADE9000 FDSP: 4000sps + */ +#define ADE9000_FDSP 4000 +#define ADE9000_DEFAULT_CLK_FREQ_HZ 24576000 +#define ADE9000_WFB_CFG 0x03E9 +#define ADE9000_WFB_PAGE_SIZE 128 +#define ADE9000_WFB_NR_OF_PAGES 16 +#define ADE9000_WFB_MAX_CHANNELS 8 +#define ADE9000_WFB_BYTES_IN_SAMPLE 4 +#define ADE9000_WFB_SAMPLES_IN_PAGE \ + (ADE9000_WFB_PAGE_SIZE / ADE9000_WFB_MAX_CHANNELS) +#define ADE9000_WFB_MAX_SAMPLES_CHAN \ + (ADE9000_WFB_SAMPLES_IN_PAGE * ADE9000_WFB_NR_OF_PAGES) +#define ADE9000_WFB_FULL_BUFF_NR_SAMPLES \ + (ADE9000_WFB_PAGE_SIZE * ADE9000_WFB_NR_OF_PAGES) +#define ADE9000_WFB_FULL_BUFF_SIZE \ + (ADE9000_WFB_FULL_BUFF_NR_SAMPLES * ADE9000_WFB_BYTES_IN_SAMPLE) + +#define ADE9000_SWRST_BIT BIT(0) + +/* Status and Mask register bits*/ +#define ADE9000_ST0_WFB_TRIG_BIT BIT(16) +#define ADE9000_ST0_PAGE_FULL_BIT BIT(17) +#define ADE9000_ST0_EGYRDY BIT(0) + +#define ADE9000_ST1_ZXTOVA_BIT BIT(6) +#define ADE9000_ST1_ZXTOVB_BIT BIT(7) +#define ADE9000_ST1_ZXTOVC_BIT BIT(8) +#define ADE9000_ST1_ZXVA_BIT BIT(9) +#define ADE9000_ST1_ZXVB_BIT BIT(10) +#define ADE9000_ST1_ZXVC_BIT BIT(11) +#define ADE9000_ST1_ZXIA_BIT BIT(13) +#define ADE9000_ST1_ZXIB_BIT BIT(14) +#define ADE9000_ST1_ZXIC_BIT BIT(15) +#define ADE9000_ST1_RSTDONE_BIT BIT(16) +#define ADE9000_ST1_SEQERR_BIT BIT(18) +#define ADE9000_ST1_SWELLA_BIT BIT(20) +#define ADE9000_ST1_SWELLB_BIT BIT(21) +#define ADE9000_ST1_SWELLC_BIT BIT(22) +#define ADE9000_ST1_DIPA_BIT BIT(23) +#define ADE9000_ST1_DIPB_BIT BIT(24) +#define ADE9000_ST1_DIPC_BIT BIT(25) +#define ADE9000_ST1_ERROR0_BIT BIT(28) +#define ADE9000_ST1_ERROR1_BIT BIT(29) +#define ADE9000_ST1_ERROR2_BIT BIT(30) +#define ADE9000_ST1_ERROR3_BIT BIT(31) +#define ADE9000_ST1_CROSSING_FIRST 6 +#define ADE9000_ST1_CROSSING_DEPTH 25 + +#define ADE9000_WFB_TRG_DIP_BIT BIT(0) +#define ADE9000_WFB_TRG_SWELL_BIT BIT(1) +#define ADE9000_WFB_TRG_ZXIA_BIT BIT(3) +#define ADE9000_WFB_TRG_ZXIB_BIT BIT(4) +#define ADE9000_WFB_TRG_ZXIC_BIT BIT(5) +#define ADE9000_WFB_TRG_ZXVA_BIT BIT(6) +#define ADE9000_WFB_TRG_ZXVB_BIT BIT(7) +#define ADE9000_WFB_TRG_ZXVC_BIT BIT(8) + +/* Stop when waveform buffer is full */ +#define ADE9000_WFB_FULL_MODE 0x0 +/* Continuous fill—stop only on enabled trigger events */ +#define ADE9000_WFB_EN_TRIG_MODE 0x1 +/* Continuous filling—center capture around enabled trigger events */ +#define ADE9000_WFB_C_EN_TRIG_MODE 0x2 +/* Continuous fill—used as streaming mode for continuous data output */ +#define ADE9000_WFB_STREAMING_MODE 0x3 + +#define ADE9000_LAST_PAGE_BIT BIT(15) +#define ADE9000_MIDDLE_PAGE_BIT BIT(7) + +/* + * Full scale Codes referred from Datasheet. Respective digital codes are + * produced when ADC inputs are at full scale. + */ +#define ADE9000_RMS_FULL_SCALE_CODES 52866837 +#define ADE9000_WATT_FULL_SCALE_CODES 20694066 +#define ADE9000_PCF_FULL_SCALE_CODES 74770000 + +/* Phase and channel definitions */ +#define ADE9000_PHASE_A_NR 0 +#define ADE9000_PHASE_B_NR 1 +#define ADE9000_PHASE_C_NR 2 + +#define ADE9000_SCAN_POS_IA BIT(0) +#define ADE9000_SCAN_POS_VA BIT(1) +#define ADE9000_SCAN_POS_IB BIT(2) +#define ADE9000_SCAN_POS_VB BIT(3) +#define ADE9000_SCAN_POS_IC BIT(4) +#define ADE9000_SCAN_POS_VC BIT(5) + +/* Waveform buffer configuration values */ +enum ade9000_wfb_cfg { + ADE9000_WFB_CFG_ALL_CHAN = 0x0, + ADE9000_WFB_CFG_IA_VA = 0x1, + ADE9000_WFB_CFG_IB_VB = 0x2, + ADE9000_WFB_CFG_IC_VC = 0x3, + ADE9000_WFB_CFG_IA = 0x8, + ADE9000_WFB_CFG_VA = 0x9, + ADE9000_WFB_CFG_IB = 0xA, + ADE9000_WFB_CFG_VB = 0xB, + ADE9000_WFB_CFG_IC = 0xC, + ADE9000_WFB_CFG_VC = 0xD, +}; + +#define ADE9000_PHASE_B_POS_BIT BIT(5) +#define ADE9000_PHASE_C_POS_BIT BIT(6) + +#define ADE9000_MAX_PHASE_NR 3 + +/* + * Calculate register address for multi-phase device. + * Phase A (chan 0): base address + 0x00 + * Phase B (chan 1): base address + 0x20 + * Phase C (chan 2): base address + 0x40 + */ +#define ADE9000_ADDR_ADJUST(addr, chan) \ + (((chan) == 0 ? 0 : (chan) == 1 ? 2 : 4) << 4 | (addr)) + +struct ade9000_state { + struct completion reset_completion; + struct mutex lock; /* Protects SPI transactions */ + u8 wf_src; + u32 wfb_trg; + u8 wfb_nr_activ_chan; + u32 wfb_nr_samples; + struct spi_device *spi; + struct clk *clkin; + struct spi_transfer xfer[2]; + struct spi_message spi_msg; + struct regmap *regmap; + union{ + u8 byte[ADE9000_WFB_FULL_BUFF_SIZE]; + __be32 word[ADE9000_WFB_FULL_BUFF_NR_SAMPLES]; + } rx_buff __aligned(IIO_DMA_MINALIGN); + u8 tx_buff[2] __aligned(IIO_DMA_MINALIGN); + unsigned int bulk_read_buf[2]; +}; + +struct ade9000_irq1_event { + u32 bit_mask; + enum iio_chan_type chan_type; + u32 channel; + enum iio_event_type event_type; + enum iio_event_direction event_dir; +}; + +static const struct ade9000_irq1_event ade9000_irq1_events[] = { + { ADE9000_ST1_ZXVA_BIT, IIO_VOLTAGE, ADE9000_PHASE_A_NR, IIO_EV_TYPE_THRESH, IIO_EV_DIR_EITHER }, + { ADE9000_ST1_ZXIA_BIT, IIO_CURRENT, ADE9000_PHASE_A_NR, IIO_EV_TYPE_THRESH, IIO_EV_DIR_EITHER }, + { ADE9000_ST1_ZXVB_BIT, IIO_VOLTAGE, ADE9000_PHASE_B_NR, IIO_EV_TYPE_THRESH, IIO_EV_DIR_EITHER }, + { ADE9000_ST1_ZXIB_BIT, IIO_CURRENT, ADE9000_PHASE_B_NR, IIO_EV_TYPE_THRESH, IIO_EV_DIR_EITHER }, + { ADE9000_ST1_ZXVC_BIT, IIO_VOLTAGE, ADE9000_PHASE_C_NR, IIO_EV_TYPE_THRESH, IIO_EV_DIR_EITHER }, + { ADE9000_ST1_ZXIC_BIT, IIO_CURRENT, ADE9000_PHASE_C_NR, IIO_EV_TYPE_THRESH, IIO_EV_DIR_EITHER }, + { ADE9000_ST1_SWELLA_BIT, IIO_ALTVOLTAGE, ADE9000_PHASE_A_NR, IIO_EV_TYPE_THRESH, IIO_EV_DIR_RISING }, + { ADE9000_ST1_SWELLB_BIT, IIO_ALTVOLTAGE, ADE9000_PHASE_B_NR, IIO_EV_TYPE_THRESH, IIO_EV_DIR_RISING }, + { ADE9000_ST1_SWELLC_BIT, IIO_ALTVOLTAGE, ADE9000_PHASE_C_NR, IIO_EV_TYPE_THRESH, IIO_EV_DIR_RISING }, + { ADE9000_ST1_DIPA_BIT, IIO_ALTVOLTAGE, ADE9000_PHASE_A_NR, IIO_EV_TYPE_THRESH, IIO_EV_DIR_FALLING }, + { ADE9000_ST1_DIPB_BIT, IIO_ALTVOLTAGE, ADE9000_PHASE_B_NR, IIO_EV_TYPE_THRESH, IIO_EV_DIR_FALLING }, + { ADE9000_ST1_DIPC_BIT, IIO_ALTVOLTAGE, ADE9000_PHASE_C_NR, IIO_EV_TYPE_THRESH, IIO_EV_DIR_FALLING }, +}; + +/* Voltage events (zero crossing on instantaneous voltage) */ +static const struct iio_event_spec ade9000_voltage_events[] = { + { + /* Zero crossing detection - datasheet: ZXV interrupts */ + .type = IIO_EV_TYPE_THRESH, + .dir = IIO_EV_DIR_EITHER, + .mask_separate = BIT(IIO_EV_INFO_ENABLE), + }, +}; + +/* Current events (zero crossing on instantaneous current) */ +static const struct iio_event_spec ade9000_current_events[] = { + { + /* Zero crossing detection - datasheet: ZXI interrupts */ + .type = IIO_EV_TYPE_THRESH, + .dir = IIO_EV_DIR_EITHER, + .mask_separate = BIT(IIO_EV_INFO_ENABLE), + }, +}; + +/* RMS voltage events (swell/sag detection on RMS values) */ +static const struct iio_event_spec ade9000_rms_voltage_events[] = { + { + .type = IIO_EV_TYPE_THRESH, + .dir = IIO_EV_DIR_RISING, /* RMS swell detection */ + .mask_separate = BIT(IIO_EV_INFO_ENABLE) | BIT(IIO_EV_INFO_VALUE), + }, + { + .type = IIO_EV_TYPE_THRESH, + .dir = IIO_EV_DIR_FALLING, /* RMS sag/dip detection */ + .mask_separate = BIT(IIO_EV_INFO_ENABLE) | BIT(IIO_EV_INFO_VALUE), + }, +}; + +static const char * const ade9000_filter_type_items[] = { + "sinc4", "sinc4+lp", +}; + +static const int ade9000_filter_type_values[] = { + 0, 2, +}; + +static int ade9000_filter_type_get(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan) +{ + struct ade9000_state *st = iio_priv(indio_dev); + u32 val; + int ret; + unsigned int i; + + ret = regmap_read(st->regmap, ADE9000_REG_WFB_CFG, &val); + if (ret) + return ret; + + val = FIELD_GET(ADE9000_WF_SRC_MASK, val); + + for (i = 0; i < ARRAY_SIZE(ade9000_filter_type_values); i++) { + if (ade9000_filter_type_values[i] == val) + return i; + } + + return -EINVAL; +} + +static int ade9000_filter_type_set(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + unsigned int index) +{ + struct ade9000_state *st = iio_priv(indio_dev); + int ret, val; + + if (index >= ARRAY_SIZE(ade9000_filter_type_values)) + return -EINVAL; + + val = ade9000_filter_type_values[index]; + + /* Update the WFB_CFG register with the new filter type */ + ret = regmap_update_bits(st->regmap, ADE9000_REG_WFB_CFG, + ADE9000_WF_SRC_MASK, + FIELD_PREP(ADE9000_WF_SRC_MASK, val)); + if (ret) + return ret; + + /* Update cached value */ + st->wf_src = val; + + return 0; +} + +static const struct iio_enum ade9000_filter_type_enum = { + .items = ade9000_filter_type_items, + .num_items = ARRAY_SIZE(ade9000_filter_type_items), + .get = ade9000_filter_type_get, + .set = ade9000_filter_type_set, +}; + +static const struct iio_chan_spec_ext_info ade9000_ext_info[] = { + IIO_ENUM("filter_type", IIO_SHARED_BY_ALL, &ade9000_filter_type_enum), + IIO_ENUM_AVAILABLE("filter_type", IIO_SHARED_BY_ALL, &ade9000_filter_type_enum), + { } +}; + +#define ADE9000_CURRENT_CHANNEL(num) { \ + .type = IIO_CURRENT, \ + .channel = num, \ + .address = ADE9000_ADDR_ADJUST(ADE9000_REG_AI_PCF, num), \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ + BIT(IIO_CHAN_INFO_SCALE) | \ + BIT(IIO_CHAN_INFO_CALIBSCALE), \ + .event_spec = ade9000_current_events, \ + .num_event_specs = ARRAY_SIZE(ade9000_current_events), \ + .scan_index = num, \ + .indexed = 1, \ + .scan_type = { \ + .sign = 's', \ + .realbits = 32, \ + .storagebits = 32, \ + .endianness = IIO_BE, \ + }, \ +} + +#define ADE9000_VOLTAGE_CHANNEL(num) { \ + .type = IIO_VOLTAGE, \ + .channel = num, \ + .address = ADE9000_ADDR_ADJUST(ADE9000_REG_AV_PCF, num), \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ + BIT(IIO_CHAN_INFO_SCALE) | \ + BIT(IIO_CHAN_INFO_CALIBSCALE) | \ + BIT(IIO_CHAN_INFO_FREQUENCY), \ + .event_spec = ade9000_voltage_events, \ + .num_event_specs = ARRAY_SIZE(ade9000_voltage_events), \ + .scan_index = num + 1, /* interleave with current channels */ \ + .indexed = 1, \ + .scan_type = { \ + .sign = 's', \ + .realbits = 32, \ + .storagebits = 32, \ + .endianness = IIO_BE, \ + }, \ + .ext_info = ade9000_ext_info, \ +} + +#define ADE9000_ALTCURRENT_RMS_CHANNEL(num) { \ + .type = IIO_ALTCURRENT, \ + .channel = num, \ + .address = ADE9000_ADDR_ADJUST(ADE9000_REG_AIRMS, num), \ + .channel2 = IIO_MOD_RMS, \ + .modified = 1, \ + .indexed = 1, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ + BIT(IIO_CHAN_INFO_SCALE) | \ + BIT(IIO_CHAN_INFO_CALIBBIAS), \ + .scan_index = -1 \ +} + +#define ADE9000_ALTVOLTAGE_RMS_CHANNEL(num) { \ + .type = IIO_ALTVOLTAGE, \ + .channel = num, \ + .address = ADE9000_ADDR_ADJUST(ADE9000_REG_AVRMS, num), \ + .channel2 = IIO_MOD_RMS, \ + .modified = 1, \ + .indexed = 1, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ + BIT(IIO_CHAN_INFO_SCALE) | \ + BIT(IIO_CHAN_INFO_CALIBBIAS), \ + .event_spec = ade9000_rms_voltage_events, \ + .num_event_specs = ARRAY_SIZE(ade9000_rms_voltage_events), \ + .scan_index = -1 \ +} + +#define ADE9000_POWER_ACTIVE_CHANNEL(num) { \ + .type = IIO_POWER, \ + .channel = num, \ + .address = ADE9000_ADDR_ADJUST(ADE9000_REG_AWATT, num), \ + .channel2 = IIO_MOD_ACTIVE, \ + .modified = 1, \ + .indexed = 1, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ + BIT(IIO_CHAN_INFO_SCALE) | \ + BIT(IIO_CHAN_INFO_CALIBBIAS) | \ + BIT(IIO_CHAN_INFO_CALIBSCALE), \ + .scan_index = -1 \ +} + +#define ADE9000_POWER_REACTIVE_CHANNEL(num) { \ + .type = IIO_POWER, \ + .channel = num, \ + .address = ADE9000_ADDR_ADJUST(ADE9000_REG_AVAR, num), \ + .channel2 = IIO_MOD_REACTIVE, \ + .modified = 1, \ + .indexed = 1, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ + BIT(IIO_CHAN_INFO_SCALE) | \ + BIT(IIO_CHAN_INFO_CALIBBIAS), \ + .scan_index = -1 \ +} + +#define ADE9000_POWER_APPARENT_CHANNEL(num) { \ + .type = IIO_POWER, \ + .channel = num, \ + .address = ADE9000_ADDR_ADJUST(ADE9000_REG_AVA, num), \ + .channel2 = IIO_MOD_APPARENT, \ + .modified = 1, \ + .indexed = 1, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ + BIT(IIO_CHAN_INFO_SCALE), \ + .scan_index = -1 \ +} + + #define ADE9000_ENERGY_ACTIVE_CHANNEL(num, addr) { \ + .type = IIO_ENERGY, \ + .channel = num, \ + .address = addr, \ + .channel2 = IIO_MOD_ACTIVE, \ + .modified = 1, \ + .indexed = 1, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .scan_index = -1 \ +} + +#define ADE9000_ENERGY_APPARENT_CHANNEL(num, addr) { \ + .type = IIO_ENERGY, \ + .channel = num, \ + .address = addr, \ + .channel2 = IIO_MOD_APPARENT, \ + .modified = 1, \ + .indexed = 1, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .scan_index = -1 \ +} + +#define ADE9000_ENERGY_REACTIVE_CHANNEL(num, addr) { \ + .type = IIO_ENERGY, \ + .channel = num, \ + .address = addr, \ + .channel2 = IIO_MOD_REACTIVE, \ + .modified = 1, \ + .indexed = 1, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .scan_index = -1 \ +} + +#define ADE9000_POWER_FACTOR_CHANNEL(num) { \ + .type = IIO_POWER, \ + .channel = num, \ + .address = ADE9000_ADDR_ADJUST(ADE9000_REG_APF, num), \ + .indexed = 1, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_POWERFACTOR), \ + .scan_index = -1 \ +} + +static const struct iio_chan_spec ade9000_channels[] = { + /* Phase A channels */ + ADE9000_CURRENT_CHANNEL(ADE9000_PHASE_A_NR), + ADE9000_VOLTAGE_CHANNEL(ADE9000_PHASE_A_NR), + ADE9000_ALTCURRENT_RMS_CHANNEL(ADE9000_PHASE_A_NR), + ADE9000_ALTVOLTAGE_RMS_CHANNEL(ADE9000_PHASE_A_NR), + ADE9000_POWER_ACTIVE_CHANNEL(ADE9000_PHASE_A_NR), + ADE9000_POWER_REACTIVE_CHANNEL(ADE9000_PHASE_A_NR), + ADE9000_POWER_APPARENT_CHANNEL(ADE9000_PHASE_A_NR), + ADE9000_ENERGY_ACTIVE_CHANNEL(ADE9000_PHASE_A_NR, ADE9000_REG_AWATTHR_LO), + ADE9000_ENERGY_APPARENT_CHANNEL(ADE9000_PHASE_A_NR, ADE9000_REG_AVAHR_LO), + ADE9000_ENERGY_REACTIVE_CHANNEL(ADE9000_PHASE_A_NR, ADE9000_REG_AFVARHR_LO), + ADE9000_POWER_FACTOR_CHANNEL(ADE9000_PHASE_A_NR), + /* Phase B channels */ + ADE9000_CURRENT_CHANNEL(ADE9000_PHASE_B_NR), + ADE9000_VOLTAGE_CHANNEL(ADE9000_PHASE_B_NR), + ADE9000_ALTCURRENT_RMS_CHANNEL(ADE9000_PHASE_B_NR), + ADE9000_ALTVOLTAGE_RMS_CHANNEL(ADE9000_PHASE_B_NR), + ADE9000_POWER_ACTIVE_CHANNEL(ADE9000_PHASE_B_NR), + ADE9000_POWER_REACTIVE_CHANNEL(ADE9000_PHASE_B_NR), + ADE9000_POWER_APPARENT_CHANNEL(ADE9000_PHASE_B_NR), + ADE9000_ENERGY_ACTIVE_CHANNEL(ADE9000_PHASE_B_NR, ADE9000_REG_BWATTHR_LO), + ADE9000_ENERGY_APPARENT_CHANNEL(ADE9000_PHASE_B_NR, ADE9000_REG_BVAHR_LO), + ADE9000_ENERGY_REACTIVE_CHANNEL(ADE9000_PHASE_B_NR, ADE9000_REG_BFVARHR_LO), + ADE9000_POWER_FACTOR_CHANNEL(ADE9000_PHASE_B_NR), + /* Phase C channels */ + ADE9000_CURRENT_CHANNEL(ADE9000_PHASE_C_NR), + ADE9000_VOLTAGE_CHANNEL(ADE9000_PHASE_C_NR), + ADE9000_ALTCURRENT_RMS_CHANNEL(ADE9000_PHASE_C_NR), + ADE9000_ALTVOLTAGE_RMS_CHANNEL(ADE9000_PHASE_C_NR), + ADE9000_POWER_ACTIVE_CHANNEL(ADE9000_PHASE_C_NR), + ADE9000_POWER_REACTIVE_CHANNEL(ADE9000_PHASE_C_NR), + ADE9000_POWER_APPARENT_CHANNEL(ADE9000_PHASE_C_NR), + ADE9000_ENERGY_ACTIVE_CHANNEL(ADE9000_PHASE_C_NR, ADE9000_REG_CWATTHR_LO), + ADE9000_ENERGY_APPARENT_CHANNEL(ADE9000_PHASE_C_NR, ADE9000_REG_CVAHR_LO), + ADE9000_ENERGY_REACTIVE_CHANNEL(ADE9000_PHASE_C_NR, ADE9000_REG_CFVARHR_LO), + ADE9000_POWER_FACTOR_CHANNEL(ADE9000_PHASE_C_NR), +}; + +static const struct reg_sequence ade9000_initialization_sequence[] = { + { ADE9000_REG_PGA_GAIN, ADE9000_PGA_GAIN }, + { ADE9000_REG_CONFIG0, ADE9000_CONFIG0 }, + { ADE9000_REG_CONFIG1, ADE9000_CONFIG1 }, + { ADE9000_REG_CONFIG2, ADE9000_CONFIG2 }, + { ADE9000_REG_CONFIG3, ADE9000_CONFIG3 }, + { ADE9000_REG_ACCMODE, ADE9000_ACCMODE }, + { ADE9000_REG_ZX_LP_SEL, ADE9000_ZX_LP_SEL }, + { ADE9000_REG_MASK0, ADE9000_MASK0_ALL_INT_DIS }, + { ADE9000_REG_MASK1, ADE9000_MASK1_ALL_INT_DIS }, + { ADE9000_REG_EVENT_MASK, ADE9000_EVENT_DISABLE }, + { ADE9000_REG_WFB_CFG, ADE9000_WFB_CFG }, + { ADE9000_REG_VLEVEL, ADE9000_VLEVEL }, + { ADE9000_REG_DICOEFF, ADE9000_DICOEFF }, + { ADE9000_REG_EGY_TIME, ADE9000_EGY_TIME }, + { ADE9000_REG_EP_CFG, ADE9000_EP_CFG }, + /* Clear all pending status bits by writing 1s */ + { ADE9000_REG_STATUS0, GENMASK(31, 0) }, + { ADE9000_REG_STATUS1, GENMASK(31, 0) }, + { ADE9000_REG_RUN, ADE9000_RUN_ON } +}; + +static int ade9000_spi_write_reg(void *context, unsigned int reg, + unsigned int val) +{ + struct ade9000_state *st = context; + u8 tx_buf[6]; + u16 addr; + int ret, len; + + guard(mutex)(&st->lock); + + addr = FIELD_PREP(ADE9000_REG_ADDR_MASK, reg); + put_unaligned_be16(addr, tx_buf); + + if (reg > ADE9000_REG_RUN && reg < ADE9000_REG_VERSION) { + put_unaligned_be16(val, &tx_buf[2]); + len = 4; + } else { + put_unaligned_be32(val, &tx_buf[2]); + len = 6; + } + + ret = spi_write_then_read(st->spi, tx_buf, len, NULL, 0); + if (ret) + dev_err(&st->spi->dev, "problem when writing register 0x%x\n", reg); + + return ret; +} + +static int ade9000_spi_read_reg(void *context, unsigned int reg, + unsigned int *val) +{ + struct ade9000_state *st = context; + u8 tx_buf[2]; + u8 rx_buf[4]; + u16 addr; + int ret, rx_len; + + guard(mutex)(&st->lock); + + addr = FIELD_PREP(ADE9000_REG_ADDR_MASK, reg) | + ADE9000_REG_READ_BIT_MASK; + + put_unaligned_be16(addr, tx_buf); + + /* Skip CRC bytes - only read actual data */ + if (reg > ADE9000_REG_RUN && reg < ADE9000_REG_VERSION) + rx_len = 2; + else + rx_len = 4; + + ret = spi_write_then_read(st->spi, tx_buf, 2, rx_buf, rx_len); + if (ret) { + dev_err(&st->spi->dev, "error reading register 0x%x\n", reg); + return ret; + } + + if (reg > ADE9000_REG_RUN && reg < ADE9000_REG_VERSION) + *val = get_unaligned_be16(rx_buf); + else + *val = get_unaligned_be32(rx_buf); + + return 0; +} + +static bool ade9000_is_volatile_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + /* Interrupt/error status registers - volatile */ + case ADE9000_REG_STATUS0: + case ADE9000_REG_STATUS1: + return true; + default: + /* All other registers are non-volatile */ + return false; + } +} + +static void ade9000_configure_scan(struct iio_dev *indio_dev, u32 wfb_addr) +{ + struct ade9000_state *st = iio_priv(indio_dev); + u16 addr; + + addr = FIELD_PREP(ADE9000_REG_ADDR_MASK, wfb_addr) | + ADE9000_REG_READ_BIT_MASK; + + put_unaligned_be16(addr, st->tx_buff); + + st->xfer[0].tx_buf = &st->tx_buff[0]; + st->xfer[0].len = 2; + + st->xfer[1].rx_buf = st->rx_buff.byte; + + /* Always use streaming mode */ + st->xfer[1].len = (st->wfb_nr_samples / 2) * 4; + + spi_message_init_with_transfers(&st->spi_msg, st->xfer, ARRAY_SIZE(st->xfer)); +} + +static int ade9000_iio_push_streaming(struct iio_dev *indio_dev) +{ + struct ade9000_state *st = iio_priv(indio_dev); + struct device *dev = &st->spi->dev; + u32 current_page, i; + int ret; + + guard(mutex)(&st->lock); + + ret = spi_sync(st->spi, &st->spi_msg); + if (ret) { + dev_err_ratelimited(dev, "SPI fail in trigger handler\n"); + return ret; + } + + /* In streaming mode, only half the buffer is filled per interrupt */ + for (i = 0; i < st->wfb_nr_samples / 2; i += st->wfb_nr_activ_chan) + iio_push_to_buffers(indio_dev, &st->rx_buff.word[i]); + + ret = regmap_read(st->regmap, ADE9000_REG_WFB_PG_IRQEN, ¤t_page); + if (ret) { + dev_err_ratelimited(dev, "IRQ0 WFB read fail\n"); + return ret; + } + + if (current_page & ADE9000_MIDDLE_PAGE_BIT) { + ret = regmap_write(st->regmap, ADE9000_REG_WFB_PG_IRQEN, + ADE9000_LAST_PAGE_BIT); + if (ret) { + dev_err_ratelimited(dev, "IRQ0 WFB write fail\n"); + return ret; + } + + ade9000_configure_scan(indio_dev, + ADE9000_REG_WF_HALF_BUFF); + } else { + ret = regmap_write(st->regmap, ADE9000_REG_WFB_PG_IRQEN, + ADE9000_MIDDLE_PAGE_BIT); + if (ret) { + dev_err_ratelimited(dev, "IRQ0 WFB write fail"); + return ret; + } + + ade9000_configure_scan(indio_dev, ADE9000_REG_WF_BUFF); + } + + return 0; +} + +static int ade9000_iio_push_buffer(struct iio_dev *indio_dev) +{ + struct ade9000_state *st = iio_priv(indio_dev); + int ret; + u32 i; + + guard(mutex)(&st->lock); + + ret = spi_sync(st->spi, &st->spi_msg); + if (ret) { + dev_err_ratelimited(&st->spi->dev, + "SPI fail in trigger handler\n"); + return ret; + } + + for (i = 0; i < st->wfb_nr_samples; i += st->wfb_nr_activ_chan) + iio_push_to_buffers(indio_dev, &st->rx_buff.word[i]); + + return 0; +} + +static irqreturn_t ade9000_irq0_thread(int irq, void *data) +{ + struct iio_dev *indio_dev = data; + struct ade9000_state *st = iio_priv(indio_dev); + struct device *dev = &st->spi->dev; + u32 handled_irq = 0; + u32 interrupts, status; + int ret; + + ret = regmap_read(st->regmap, ADE9000_REG_STATUS0, &status); + if (ret) { + dev_err_ratelimited(dev, "IRQ0 read status fail\n"); + return IRQ_HANDLED; + } + + ret = regmap_read(st->regmap, ADE9000_REG_MASK0, &interrupts); + if (ret) { + dev_err_ratelimited(dev, "IRQ0 read mask fail\n"); + return IRQ_HANDLED; + } + + if ((status & ADE9000_ST0_PAGE_FULL_BIT) && + (interrupts & ADE9000_ST0_PAGE_FULL_BIT)) { + /* Always use streaming mode */ + ret = ade9000_iio_push_streaming(indio_dev); + if (ret) { + dev_err_ratelimited(dev, "IRQ0 IIO push fail\n"); + return IRQ_HANDLED; + } + + handled_irq |= ADE9000_ST0_PAGE_FULL_BIT; + } + + if ((status & ADE9000_ST0_WFB_TRIG_BIT) && + (interrupts & ADE9000_ST0_WFB_TRIG_BIT)) { + ret = regmap_update_bits(st->regmap, ADE9000_REG_WFB_CFG, + ADE9000_WF_CAP_EN_MASK, 0); + if (ret) { + dev_err_ratelimited(dev, "IRQ0 WFB fail\n"); + return IRQ_HANDLED; + } + + if (iio_buffer_enabled(indio_dev)) { + ret = ade9000_iio_push_buffer(indio_dev); + if (ret) { + dev_err_ratelimited(dev, + "IRQ0 IIO push fail @ WFB TRIG\n"); + return IRQ_HANDLED; + } + } + + handled_irq |= ADE9000_ST0_WFB_TRIG_BIT; + } + + ret = regmap_write(st->regmap, ADE9000_REG_STATUS0, handled_irq); + if (ret) + dev_err_ratelimited(dev, "IRQ0 write status fail\n"); + + return IRQ_HANDLED; +} + +static irqreturn_t ade9000_irq1_thread(int irq, void *data) +{ + struct iio_dev *indio_dev = data; + struct ade9000_state *st = iio_priv(indio_dev); + unsigned int bit = ADE9000_ST1_CROSSING_FIRST; + s64 timestamp = iio_get_time_ns(indio_dev); + u32 handled_irq = 0; + u32 interrupts, result, status, tmp; + DECLARE_BITMAP(interrupt_bits, ADE9000_ST1_CROSSING_DEPTH); + const struct ade9000_irq1_event *event; + int ret, i; + + if (!completion_done(&st->reset_completion)) { + ret = regmap_read(st->regmap, ADE9000_REG_STATUS1, &result); + if (ret) { + dev_err_ratelimited(&st->spi->dev, "IRQ1 read status fail\n"); + return IRQ_HANDLED; + } + + if (result & ADE9000_ST1_RSTDONE_BIT) { + complete(&st->reset_completion); + /* Clear the reset done status bit */ + ret = regmap_write(st->regmap, ADE9000_REG_STATUS1, ADE9000_ST1_RSTDONE_BIT); + if (ret) + dev_err_ratelimited(&st->spi->dev, + "IRQ1 clear reset status fail\n"); + } else { + dev_err_ratelimited(&st->spi->dev, + "Error testing reset done\n"); + } + + return IRQ_HANDLED; + } + + ret = regmap_read(st->regmap, ADE9000_REG_STATUS1, &status); + if (ret) { + dev_err_ratelimited(&st->spi->dev, "IRQ1 read status fail\n"); + return IRQ_HANDLED; + } + + ret = regmap_read(st->regmap, ADE9000_REG_MASK1, &interrupts); + if (ret) { + dev_err_ratelimited(&st->spi->dev, "IRQ1 read mask fail\n"); + return IRQ_HANDLED; + } + + bitmap_from_arr32(interrupt_bits, &interrupts, ADE9000_ST1_CROSSING_DEPTH); + for_each_set_bit_from(bit, interrupt_bits, + ADE9000_ST1_CROSSING_DEPTH) { + tmp = status & BIT(bit); + if (!tmp) + continue; + + event = NULL; + + /* Find corresponding event in lookup table */ + for (i = 0; i < ARRAY_SIZE(ade9000_irq1_events); i++) { + if (ade9000_irq1_events[i].bit_mask == tmp) { + event = &ade9000_irq1_events[i]; + break; + } + } + + if (event) { + iio_push_event(indio_dev, + IIO_UNMOD_EVENT_CODE(event->chan_type, + event->channel, + event->event_type, + event->event_dir), + timestamp); + } + handled_irq |= tmp; + } + + ret = regmap_write(st->regmap, ADE9000_REG_STATUS1, handled_irq); + if (ret) + dev_err_ratelimited(&st->spi->dev, "IRQ1 write status fail\n"); + + return IRQ_HANDLED; +} + +static irqreturn_t ade9000_dready_thread(int irq, void *data) +{ + struct iio_dev *indio_dev = data; + + /* Handle data ready interrupt from C4/EVENT/DREADY pin */ + if (iio_device_try_claim_buffer_mode(indio_dev)) { + ade9000_iio_push_buffer(indio_dev); + iio_device_release_buffer_mode(indio_dev); + } + + return IRQ_HANDLED; +} + +static int ade9000_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, + int *val2, + long mask) +{ + struct ade9000_state *st = iio_priv(indio_dev); + unsigned int measured; + int ret; + + switch (mask) { + case IIO_CHAN_INFO_FREQUENCY: + if (chan->type == IIO_VOLTAGE) { + int period_reg; + int period; + + switch (chan->channel) { + case ADE9000_PHASE_A_NR: + period_reg = ADE9000_REG_APERIOD; + break; + case ADE9000_PHASE_B_NR: + period_reg = ADE9000_REG_BPERIOD; + break; + case ADE9000_PHASE_C_NR: + period_reg = ADE9000_REG_CPERIOD; + break; + default: + return -EINVAL; + } + ret = regmap_read(st->regmap, period_reg, &period); + if (ret) + return ret; + /* + * Frequency = (4MHz * 65536) / (PERIOD + 1) + * 4MHz = ADC sample rate, 65536 = 2^16 period register scaling + * See ADE9000 datasheet section on period measurement + */ + *val = 4000 * 65536; + *val2 = period + 1; + return IIO_VAL_FRACTIONAL; + } + + return -EINVAL; + case IIO_CHAN_INFO_RAW: + if (chan->type == IIO_ENERGY) { + u16 lo_reg = chan->address; + + ret = regmap_bulk_read(st->regmap, lo_reg, + st->bulk_read_buf, 2); + if (ret) + return ret; + + *val = st->bulk_read_buf[0]; /* Lower 32 bits */ + *val2 = st->bulk_read_buf[1]; /* Upper 32 bits */ + return IIO_VAL_INT_64; + } + + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; + + ret = regmap_read(st->regmap, chan->address, &measured); + iio_device_release_direct(indio_dev); + if (ret) + return ret; + + *val = measured; + + return IIO_VAL_INT; + + case IIO_CHAN_INFO_POWERFACTOR: + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; + + ret = regmap_read(st->regmap, chan->address, &measured); + iio_device_release_direct(indio_dev); + if (ret) + return ret; + + *val = measured; + + return IIO_VAL_INT; + + case IIO_CHAN_INFO_SCALE: + switch (chan->type) { + case IIO_CURRENT: + case IIO_VOLTAGE: + case IIO_ALTVOLTAGE: + case IIO_ALTCURRENT: + switch (chan->address) { + case ADE9000_REG_AI_PCF: + case ADE9000_REG_AV_PCF: + case ADE9000_REG_BI_PCF: + case ADE9000_REG_BV_PCF: + case ADE9000_REG_CI_PCF: + case ADE9000_REG_CV_PCF: + *val = 1; + *val2 = ADE9000_PCF_FULL_SCALE_CODES; + return IIO_VAL_FRACTIONAL; + case ADE9000_REG_AIRMS: + case ADE9000_REG_AVRMS: + case ADE9000_REG_BIRMS: + case ADE9000_REG_BVRMS: + case ADE9000_REG_CIRMS: + case ADE9000_REG_CVRMS: + *val = 1; + *val2 = ADE9000_RMS_FULL_SCALE_CODES; + return IIO_VAL_FRACTIONAL; + default: + return -EINVAL; + } + case IIO_POWER: + *val = 1; + *val2 = ADE9000_WATT_FULL_SCALE_CODES; + return IIO_VAL_FRACTIONAL; + default: + break; + } + + return -EINVAL; + default: + return -EINVAL; + } +} + +static int ade9000_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, + int val2, + long mask) +{ + struct ade9000_state *st = iio_priv(indio_dev); + u32 tmp; + + switch (mask) { + case IIO_CHAN_INFO_CALIBBIAS: + switch (chan->type) { + case IIO_CURRENT: + return regmap_write(st->regmap, + ADE9000_ADDR_ADJUST(ADE9000_REG_AIRMSOS, + chan->channel), val); + case IIO_VOLTAGE: + case IIO_ALTVOLTAGE: + return regmap_write(st->regmap, + ADE9000_ADDR_ADJUST(ADE9000_REG_AVRMSOS, + chan->channel), val); + case IIO_POWER: + tmp = chan->address; + tmp &= ~ADE9000_PHASE_B_POS_BIT; + tmp &= ~ADE9000_PHASE_C_POS_BIT; + + switch (tmp) { + case ADE9000_REG_AWATT: + return regmap_write(st->regmap, + ADE9000_ADDR_ADJUST(ADE9000_REG_AWATTOS, + chan->channel), val); + case ADE9000_REG_AVAR: + return regmap_write(st->regmap, + ADE9000_ADDR_ADJUST(ADE9000_REG_AVAROS, + chan->channel), val); + case ADE9000_REG_AFVAR: + return regmap_write(st->regmap, + ADE9000_ADDR_ADJUST(ADE9000_REG_AFVAROS, + chan->channel), val); + default: + return -EINVAL; + } + default: + return -EINVAL; + } + case IIO_CHAN_INFO_CALIBSCALE: + /* + * Calibration gain registers for fine-tuning measurements. + * These are separate from PGA gain and applied in the digital domain. + */ + switch (chan->type) { + case IIO_CURRENT: + return regmap_write(st->regmap, + ADE9000_ADDR_ADJUST(ADE9000_REG_AIGAIN, + chan->channel), val); + case IIO_VOLTAGE: + return regmap_write(st->regmap, + ADE9000_ADDR_ADJUST(ADE9000_REG_AVGAIN, + chan->channel), val); + case IIO_POWER: + return regmap_write(st->regmap, + ADE9000_ADDR_ADJUST(ADE9000_REG_APGAIN, + chan->channel), val); + default: + return -EINVAL; + } + case IIO_CHAN_INFO_SCALE: + /* Per-channel scales are read-only */ + return -EINVAL; + default: + return -EINVAL; + } +} + +static int ade9000_reg_access(struct iio_dev *indio_dev, + unsigned int reg, + unsigned int tx_val, + unsigned int *rx_val) +{ + struct ade9000_state *st = iio_priv(indio_dev); + + if (rx_val) + return regmap_read(st->regmap, reg, rx_val); + + return regmap_write(st->regmap, reg, tx_val); +} + +static int ade9000_read_event_config(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir) +{ + struct ade9000_state *st = iio_priv(indio_dev); + u32 interrupts1; + int ret; + + /* All events use MASK1 register */ + ret = regmap_read(st->regmap, ADE9000_REG_MASK1, &interrupts1); + if (ret) + return ret; + + switch (chan->channel) { + case ADE9000_PHASE_A_NR: + if (chan->type == IIO_VOLTAGE && dir == IIO_EV_DIR_EITHER) + return !!(interrupts1 & ADE9000_ST1_ZXVA_BIT); + else if (chan->type == IIO_CURRENT && dir == IIO_EV_DIR_EITHER) + return !!(interrupts1 & ADE9000_ST1_ZXIA_BIT); + else if (chan->type == IIO_ALTVOLTAGE && dir == IIO_EV_DIR_RISING) + return !!(interrupts1 & ADE9000_ST1_SWELLA_BIT); + else if (chan->type == IIO_ALTVOLTAGE && dir == IIO_EV_DIR_FALLING) + return !!(interrupts1 & ADE9000_ST1_DIPA_BIT); + dev_err_ratelimited(&indio_dev->dev, + "Invalid channel type %d or direction %d for phase A\n", chan->type, dir); + return -EINVAL; + case ADE9000_PHASE_B_NR: + if (chan->type == IIO_VOLTAGE && dir == IIO_EV_DIR_EITHER) + return !!(interrupts1 & ADE9000_ST1_ZXVB_BIT); + else if (chan->type == IIO_CURRENT && dir == IIO_EV_DIR_EITHER) + return !!(interrupts1 & ADE9000_ST1_ZXIB_BIT); + else if (chan->type == IIO_ALTVOLTAGE && dir == IIO_EV_DIR_RISING) + return !!(interrupts1 & ADE9000_ST1_SWELLB_BIT); + else if (chan->type == IIO_ALTVOLTAGE && dir == IIO_EV_DIR_FALLING) + return !!(interrupts1 & ADE9000_ST1_DIPB_BIT); + dev_err_ratelimited(&indio_dev->dev, + "Invalid channel type %d or direction %d for phase B\n", chan->type, dir); + return -EINVAL; + case ADE9000_PHASE_C_NR: + if (chan->type == IIO_VOLTAGE && dir == IIO_EV_DIR_EITHER) + return !!(interrupts1 & ADE9000_ST1_ZXVC_BIT); + else if (chan->type == IIO_CURRENT && dir == IIO_EV_DIR_EITHER) + return !!(interrupts1 & ADE9000_ST1_ZXIC_BIT); + else if (chan->type == IIO_ALTVOLTAGE && dir == IIO_EV_DIR_RISING) + return !!(interrupts1 & ADE9000_ST1_SWELLC_BIT); + else if (chan->type == IIO_ALTVOLTAGE && dir == IIO_EV_DIR_FALLING) + return !!(interrupts1 & ADE9000_ST1_DIPC_BIT); + dev_err_ratelimited(&indio_dev->dev, + "Invalid channel type %d or direction %d for phase C\n", chan->type, dir); + return -EINVAL; + default: + return -EINVAL; + } +} + +static int ade9000_write_event_config(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir, + bool state) +{ + struct ade9000_state *st = iio_priv(indio_dev); + u32 bit_mask; + int ret; + + /* Clear all pending events in STATUS1 register (write 1 to clear) */ + ret = regmap_write(st->regmap, ADE9000_REG_STATUS1, GENMASK(31, 0)); + if (ret) + return ret; + + /* Determine which interrupt bit to enable/disable */ + switch (chan->channel) { + case ADE9000_PHASE_A_NR: + if (chan->type == IIO_VOLTAGE && dir == IIO_EV_DIR_EITHER) { + bit_mask = ADE9000_ST1_ZXVA_BIT; + if (state) + st->wfb_trg |= ADE9000_WFB_TRG_ZXVA_BIT; + else + st->wfb_trg &= ~ADE9000_WFB_TRG_ZXVA_BIT; + } else if (chan->type == IIO_CURRENT && dir == IIO_EV_DIR_EITHER) { + bit_mask = ADE9000_ST1_ZXIA_BIT; + if (state) + st->wfb_trg |= ADE9000_WFB_TRG_ZXIA_BIT; + else + st->wfb_trg &= ~ADE9000_WFB_TRG_ZXIA_BIT; + } else if (chan->type == IIO_ALTVOLTAGE && dir == IIO_EV_DIR_RISING) { + bit_mask = ADE9000_ST1_SWELLA_BIT; + if (state) + st->wfb_trg |= ADE9000_WFB_TRG_SWELL_BIT; + else + st->wfb_trg &= ~ADE9000_WFB_TRG_SWELL_BIT; + } else if (chan->type == IIO_ALTVOLTAGE && dir == IIO_EV_DIR_FALLING) { + bit_mask = ADE9000_ST1_DIPA_BIT; + if (state) + st->wfb_trg |= ADE9000_WFB_TRG_DIP_BIT; + else + st->wfb_trg &= ~ADE9000_WFB_TRG_DIP_BIT; + } else { + dev_err_ratelimited(&indio_dev->dev, "Invalid channel type %d or direction %d for phase A\n", + chan->type, dir); + return -EINVAL; + } + break; + case ADE9000_PHASE_B_NR: + if (chan->type == IIO_VOLTAGE && dir == IIO_EV_DIR_EITHER) { + bit_mask = ADE9000_ST1_ZXVB_BIT; + if (state) + st->wfb_trg |= ADE9000_WFB_TRG_ZXVB_BIT; + else + st->wfb_trg &= ~ADE9000_WFB_TRG_ZXVB_BIT; + } else if (chan->type == IIO_CURRENT && dir == IIO_EV_DIR_EITHER) { + bit_mask = ADE9000_ST1_ZXIB_BIT; + if (state) + st->wfb_trg |= ADE9000_WFB_TRG_ZXIB_BIT; + else + st->wfb_trg &= ~ADE9000_WFB_TRG_ZXIB_BIT; + } else if (chan->type == IIO_ALTVOLTAGE && dir == IIO_EV_DIR_RISING) { + bit_mask = ADE9000_ST1_SWELLB_BIT; + if (state) + st->wfb_trg |= ADE9000_WFB_TRG_SWELL_BIT; + else + st->wfb_trg &= ~ADE9000_WFB_TRG_SWELL_BIT; + } else if (chan->type == IIO_ALTVOLTAGE && dir == IIO_EV_DIR_FALLING) { + bit_mask = ADE9000_ST1_DIPB_BIT; + if (state) + st->wfb_trg |= ADE9000_WFB_TRG_DIP_BIT; + else + st->wfb_trg &= ~ADE9000_WFB_TRG_DIP_BIT; + } else { + dev_err_ratelimited(&indio_dev->dev, + "Invalid channel type %d or direction %d for phase B\n", + chan->type, dir); + return -EINVAL; + } + break; + case ADE9000_PHASE_C_NR: + if (chan->type == IIO_VOLTAGE && dir == IIO_EV_DIR_EITHER) { + bit_mask = ADE9000_ST1_ZXVC_BIT; + if (state) + st->wfb_trg |= ADE9000_WFB_TRG_ZXVC_BIT; + else + st->wfb_trg &= ~ADE9000_WFB_TRG_ZXVC_BIT; + } else if (chan->type == IIO_CURRENT && dir == IIO_EV_DIR_EITHER) { + bit_mask = ADE9000_ST1_ZXIC_BIT; + if (state) + st->wfb_trg |= ADE9000_WFB_TRG_ZXIC_BIT; + else + st->wfb_trg &= ~ADE9000_WFB_TRG_ZXIC_BIT; + } else if (chan->type == IIO_ALTVOLTAGE && dir == IIO_EV_DIR_RISING) { + bit_mask = ADE9000_ST1_SWELLC_BIT; + if (state) + st->wfb_trg |= ADE9000_WFB_TRG_SWELL_BIT; + else + st->wfb_trg &= ~ADE9000_WFB_TRG_SWELL_BIT; + } else if (chan->type == IIO_ALTVOLTAGE && dir == IIO_EV_DIR_FALLING) { + bit_mask = ADE9000_ST1_DIPC_BIT; + if (state) + st->wfb_trg |= ADE9000_WFB_TRG_DIP_BIT; + else + st->wfb_trg &= ~ADE9000_WFB_TRG_DIP_BIT; + } else { + dev_err_ratelimited(&indio_dev->dev, + "Invalid channel type %d or direction %d for phase C\n", + chan->type, dir); + return -EINVAL; + } + break; + default: + return -EINVAL; + } + + /* Set bits if enabling event, clear bits if disabling */ + return regmap_assign_bits(st->regmap, ADE9000_REG_MASK1, bit_mask, state ? bit_mask : 0); +} + +static int ade9000_write_event_value(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir, + enum iio_event_info info, + int val, int val2) +{ + struct ade9000_state *st = iio_priv(indio_dev); + + switch (info) { + case IIO_EV_INFO_VALUE: + switch (dir) { + case IIO_EV_DIR_FALLING: + return regmap_write(st->regmap, ADE9000_REG_DIP_LVL, val); + case IIO_EV_DIR_RISING: + return regmap_write(st->regmap, ADE9000_REG_SWELL_LVL, val); + default: + return -EINVAL; + } + default: + return -EINVAL; + } +} + +static int ade9000_read_event_value(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir, + enum iio_event_info info, + int *val, int *val2) +{ + struct ade9000_state *st = iio_priv(indio_dev); + unsigned int data; + int ret; + + switch (info) { + case IIO_EV_INFO_VALUE: + switch (dir) { + case IIO_EV_DIR_FALLING: + ret = regmap_read(st->regmap, ADE9000_REG_DIP_LVL, &data); + if (ret) + return ret; + *val = data; + return IIO_VAL_INT; + case IIO_EV_DIR_RISING: + ret = regmap_read(st->regmap, ADE9000_REG_SWELL_LVL, &data); + if (ret) + return ret; + *val = data; + return IIO_VAL_INT; + default: + return -EINVAL; + } + default: + return -EINVAL; + } +} + +static int ade9000_waveform_buffer_config(struct iio_dev *indio_dev) +{ + struct ade9000_state *st = iio_priv(indio_dev); + u32 wfb_cfg_val; + u32 active_scans; + + bitmap_to_arr32(&active_scans, indio_dev->active_scan_mask, + iio_get_masklength(indio_dev)); + + switch (active_scans) { + case ADE9000_SCAN_POS_IA | ADE9000_SCAN_POS_VA: + wfb_cfg_val = ADE9000_WFB_CFG_IA_VA; + st->wfb_nr_activ_chan = 2; + break; + case ADE9000_SCAN_POS_IB | ADE9000_SCAN_POS_VB: + wfb_cfg_val = ADE9000_WFB_CFG_IB_VB; + st->wfb_nr_activ_chan = 2; + break; + case ADE9000_SCAN_POS_IC | ADE9000_SCAN_POS_VC: + wfb_cfg_val = ADE9000_WFB_CFG_IC_VC; + st->wfb_nr_activ_chan = 2; + break; + case ADE9000_SCAN_POS_IA: + wfb_cfg_val = ADE9000_WFB_CFG_IA; + st->wfb_nr_activ_chan = 1; + break; + case ADE9000_SCAN_POS_VA: + wfb_cfg_val = ADE9000_WFB_CFG_VA; + st->wfb_nr_activ_chan = 1; + break; + case ADE9000_SCAN_POS_IB: + wfb_cfg_val = ADE9000_WFB_CFG_IB; + st->wfb_nr_activ_chan = 1; + break; + case ADE9000_SCAN_POS_VB: + wfb_cfg_val = ADE9000_WFB_CFG_VB; + st->wfb_nr_activ_chan = 1; + break; + case ADE9000_SCAN_POS_IC: + wfb_cfg_val = ADE9000_WFB_CFG_IC; + st->wfb_nr_activ_chan = 1; + break; + case ADE9000_SCAN_POS_VC: + wfb_cfg_val = ADE9000_WFB_CFG_VC; + st->wfb_nr_activ_chan = 1; + break; + case (ADE9000_SCAN_POS_IA | ADE9000_SCAN_POS_VA | ADE9000_SCAN_POS_IB | + ADE9000_SCAN_POS_VB | ADE9000_SCAN_POS_IC | ADE9000_SCAN_POS_VC): + wfb_cfg_val = ADE9000_WFB_CFG_ALL_CHAN; + st->wfb_nr_activ_chan = 6; + break; + default: + dev_err(&st->spi->dev, "Unsupported combination of scans\n"); + return -EINVAL; + } + + wfb_cfg_val |= FIELD_PREP(ADE9000_WF_SRC_MASK, st->wf_src); + + return regmap_write(st->regmap, ADE9000_REG_WFB_CFG, wfb_cfg_val); +} + +static int ade9000_waveform_buffer_interrupt_setup(struct ade9000_state *st) +{ + int ret; + + ret = regmap_write(st->regmap, ADE9000_REG_WFB_TRG_CFG, 0x0); + if (ret) + return ret; + + /* Always use streaming mode setup */ + ret = regmap_write(st->regmap, ADE9000_REG_WFB_PG_IRQEN, + ADE9000_MIDDLE_PAGE_BIT); + if (ret) + return ret; + + ret = regmap_write(st->regmap, ADE9000_REG_STATUS0, GENMASK(31, 0)); + if (ret) + return ret; + + return regmap_set_bits(st->regmap, ADE9000_REG_MASK0, + ADE9000_ST0_PAGE_FULL_BIT); +} + +static int ade9000_buffer_preenable(struct iio_dev *indio_dev) +{ + struct ade9000_state *st = iio_priv(indio_dev); + int ret; + + ret = ade9000_waveform_buffer_config(indio_dev); + if (ret) + return ret; + + st->wfb_nr_samples = ADE9000_WFB_MAX_SAMPLES_CHAN * st->wfb_nr_activ_chan; + + ade9000_configure_scan(indio_dev, ADE9000_REG_WF_BUFF); + + ret = ade9000_waveform_buffer_interrupt_setup(st); + if (ret) + return ret; + + ret = regmap_set_bits(st->regmap, ADE9000_REG_WFB_CFG, + ADE9000_WF_CAP_EN_MASK); + if (ret) { + dev_err(&st->spi->dev, "Post-enable waveform buffer enable fail\n"); + return ret; + } + + return 0; +} + +static int ade9000_buffer_postdisable(struct iio_dev *indio_dev) +{ + struct ade9000_state *st = iio_priv(indio_dev); + struct device *dev = &st->spi->dev; + u32 interrupts; + int ret; + + ret = regmap_clear_bits(st->regmap, ADE9000_REG_WFB_CFG, + ADE9000_WF_CAP_EN_MASK); + if (ret) { + dev_err(dev, "Post-disable waveform buffer disable fail\n"); + return ret; + } + + ret = regmap_write(st->regmap, ADE9000_REG_WFB_TRG_CFG, 0x0); + if (ret) + return ret; + + interrupts = ADE9000_ST0_WFB_TRIG_BIT | ADE9000_ST0_PAGE_FULL_BIT; + + ret = regmap_clear_bits(st->regmap, ADE9000_REG_MASK0, interrupts); + if (ret) { + dev_err(dev, "Post-disable update mask0 fail\n"); + return ret; + } + + return regmap_write(st->regmap, ADE9000_REG_STATUS0, GENMASK(31, 0)); +} + +static const struct iio_buffer_setup_ops ade9000_buffer_ops = { + .preenable = &ade9000_buffer_preenable, + .postdisable = &ade9000_buffer_postdisable, +}; + +static int ade9000_reset(struct ade9000_state *st) +{ + struct device *dev = &st->spi->dev; + struct gpio_desc *gpio_reset; + int ret; + + gpio_reset = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH); + if (IS_ERR(gpio_reset)) + return PTR_ERR(gpio_reset); + + /* Software reset via register if no GPIO available */ + if (!gpio_reset) { + ret = regmap_set_bits(st->regmap, ADE9000_REG_CONFIG1, + ADE9000_SWRST_BIT); + if (ret) + return ret; + fsleep(90); + return 0; + } + + /* Hardware reset via GPIO */ + fsleep(10); + gpiod_set_value_cansleep(gpio_reset, 0); + fsleep(50000); + + /* Only wait for completion if IRQ1 is available to signal reset done */ + if (fwnode_irq_get_byname(dev_fwnode(dev), "irq1") >= 0) { + if (!wait_for_completion_timeout(&st->reset_completion, + msecs_to_jiffies(1000))) + return dev_err_probe(dev, -ETIMEDOUT, + "Reset timeout after 1s\n"); + } + /* If no IRQ available, reset is already complete after the 50ms delay above */ + + return 0; +} + +static int ade9000_setup(struct ade9000_state *st) +{ + struct device *dev = &st->spi->dev; + int ret; + + ret = regmap_multi_reg_write(st->regmap, ade9000_initialization_sequence, + ARRAY_SIZE(ade9000_initialization_sequence)); + if (ret) + return dev_err_probe(dev, ret, "Failed to write register sequence"); + + fsleep(2000); + + return 0; +} + +static const struct iio_info ade9000_info = { + .read_raw = ade9000_read_raw, + .write_raw = ade9000_write_raw, + .debugfs_reg_access = ade9000_reg_access, + .write_event_config = ade9000_write_event_config, + .read_event_config = ade9000_read_event_config, + .write_event_value = ade9000_write_event_value, + .read_event_value = ade9000_read_event_value, +}; + +static const struct regmap_config ade9000_regmap_config = { + .reg_bits = 16, + .val_bits = 32, + .max_register = 0x6bc, + .zero_flag_mask = true, + .cache_type = REGCACHE_MAPLE, + .reg_read = ade9000_spi_read_reg, + .reg_write = ade9000_spi_write_reg, + .volatile_reg = ade9000_is_volatile_reg, +}; + +static int ade9000_setup_clkout(struct device *dev, struct ade9000_state *st) +{ + struct clk_hw *clkout_hw; + int ret; + + if (!IS_ENABLED(CONFIG_COMMON_CLK)) + return 0; + + /* + * Only provide clock output when using external CMOS clock. + * When using crystal, CLKOUT is connected to crystal and shouldn't + * be used as clock provider for other devices. + */ + if (!device_property_present(dev, "#clock-cells") || !st->clkin) + return 0; + + /* CLKOUT passes through CLKIN with divider of 1 */ + clkout_hw = devm_clk_hw_register_divider(dev, "clkout", __clk_get_name(st->clkin), + CLK_SET_RATE_PARENT, NULL, 0, 1, 0, NULL); + if (IS_ERR(clkout_hw)) + return dev_err_probe(dev, PTR_ERR(clkout_hw), "Failed to register clkout"); + + ret = devm_of_clk_add_hw_provider(dev, of_clk_hw_simple_get, clkout_hw); + if (ret) + return dev_err_probe(dev, ret, "Failed to add clock provider"); + + return 0; +} + +static int ade9000_request_irq(struct device *dev, const char *name, + irq_handler_t handler, void *dev_id) +{ + int irq, ret; + + irq = fwnode_irq_get_byname(dev_fwnode(dev), name); + if (irq == -EINVAL) + return 0; /* interrupts are optional */ + if (irq < 0) + return dev_err_probe(dev, irq, "Failed to get %s irq", name); + + ret = devm_request_threaded_irq(dev, irq, NULL, handler, + IRQF_ONESHOT, KBUILD_MODNAME, dev_id); + if (ret) + return dev_err_probe(dev, ret, "Failed to request %s irq", name); + + return 0; +} + +static int ade9000_probe(struct spi_device *spi) +{ + struct device *dev = &spi->dev; + struct iio_dev *indio_dev; + struct ade9000_state *st; + struct regmap *regmap; + int ret; + + indio_dev = devm_iio_device_alloc(dev, sizeof(*st)); + if (!indio_dev) + return -ENOMEM; + + st = iio_priv(indio_dev); + + regmap = devm_regmap_init(dev, NULL, st, &ade9000_regmap_config); + if (IS_ERR(regmap)) + return dev_err_probe(dev, PTR_ERR(regmap), "Unable to allocate ADE9000 regmap"); + + st->regmap = regmap; + st->spi = spi; + + init_completion(&st->reset_completion); + + ret = devm_mutex_init(dev, &st->lock); + if (ret) + return ret; + + ret = ade9000_request_irq(dev, "irq0", ade9000_irq0_thread, indio_dev); + if (ret) + return ret; + + ret = ade9000_request_irq(dev, "irq1", ade9000_irq1_thread, indio_dev); + if (ret) + return ret; + + ret = ade9000_request_irq(dev, "dready", ade9000_dready_thread, indio_dev); + if (ret) + return ret; + + /* External CMOS clock input (optional - crystal can be used instead) */ + st->clkin = devm_clk_get_optional_enabled(dev, NULL); + if (IS_ERR(st->clkin)) + return dev_err_probe(dev, PTR_ERR(st->clkin), "Failed to get and enable clkin"); + + ret = ade9000_setup_clkout(dev, st); + if (ret) + return ret; + + indio_dev->name = "ade9000"; + indio_dev->info = &ade9000_info; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->setup_ops = &ade9000_buffer_ops; + + ret = devm_regulator_get_enable(&spi->dev, "vdd"); + if (ret) + return dev_err_probe(&spi->dev, ret, + "Failed to get and enable vdd regulator\n"); + + indio_dev->channels = ade9000_channels; + indio_dev->num_channels = ARRAY_SIZE(ade9000_channels); + + ret = devm_iio_kfifo_buffer_setup(dev, indio_dev, + &ade9000_buffer_ops); + if (ret) + return dev_err_probe(dev, ret, "Failed to setup IIO buffer"); + + ret = ade9000_reset(st); + if (ret) + return ret; + + /* Configure reference selection if vref regulator is available */ + ret = devm_regulator_get_enable_optional(dev, "vref"); + if (ret != -ENODEV && ret >= 0) { + ret = regmap_set_bits(st->regmap, ADE9000_REG_CONFIG1, + ADE9000_EXT_REF_MASK); + if (ret) + return ret; + } else if (ret < 0 && ret != -ENODEV) { + return dev_err_probe(dev, ret, + "Failed to get and enable vref regulator\n"); + } + + ret = ade9000_setup(st); + if (ret) + return ret; + + return devm_iio_device_register(dev, indio_dev); +}; + +static const struct spi_device_id ade9000_id[] = { + { .name = "ade9000" }, + { } +}; +MODULE_DEVICE_TABLE(spi, ade9000_id); + +static const struct of_device_id ade9000_of_match[] = { + { .compatible = "adi,ade9000" }, + { } +}; +MODULE_DEVICE_TABLE(of, ade9000_of_match); + +static struct spi_driver ade9000_driver = { + .driver = { + .name = "ade9000", + .of_match_table = ade9000_of_match, + }, + .probe = ade9000_probe, + .id_table = ade9000_id, +}; +module_spi_driver(ade9000_driver); + +MODULE_AUTHOR("Antoniu Miclaus "); +MODULE_DESCRIPTION("Analog Devices ADE9000"); +MODULE_LICENSE("GPL"); diff --git a/drivers/iio/adc/adi-axi-adc.c b/drivers/iio/adc/adi-axi-adc.c new file mode 100644 index 000000000..589f03618 --- /dev/null +++ b/drivers/iio/adc/adi-axi-adc.c @@ -0,0 +1,842 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Analog Devices Generic AXI ADC IP core + * Link: https://wiki.analog.com/resources/fpga/docs/axi_adc_ip + * + * Copyright 2012-2020 Analog Devices Inc. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "ad7606_bus_iface.h" +/* + * Register definitions: + * https://wiki.analog.com/resources/fpga/docs/axi_adc_ip#register_map + */ + +/* ADC controls */ + +#define ADI_AXI_REG_RSTN 0x0040 +#define ADI_AXI_REG_RSTN_CE_N BIT(2) +#define ADI_AXI_REG_RSTN_MMCM_RSTN BIT(1) +#define ADI_AXI_REG_RSTN_RSTN BIT(0) + +#define ADI_AXI_ADC_REG_CONFIG 0x000c +#define ADI_AXI_ADC_REG_CONFIG_CMOS_OR_LVDS_N BIT(7) + +#define ADI_AXI_ADC_REG_CTRL 0x0044 +#define ADI_AXI_ADC_CTRL_NUM_LANES_MSK GENMASK(12, 8) +#define ADI_AXI_ADC_CTRL_SYNC_MSK BIT(3) +#define ADI_AXI_ADC_CTRL_DDR_EDGESEL_MASK BIT(1) + +#define ADI_AXI_ADC_REG_CNTRL_3 0x004c +#define AXI_AD485X_CNTRL_3_OS_EN_MSK BIT(2) +#define AXI_AD485X_CNTRL_3_PACKET_FORMAT_MSK GENMASK(1, 0) +#define AXI_AD485X_PACKET_FORMAT_20BIT 0x0 +#define AXI_AD485X_PACKET_FORMAT_24BIT 0x1 +#define AXI_AD485X_PACKET_FORMAT_32BIT 0x2 +#define AXI_AD408X_CNTRL_3_FILTER_EN_MSK BIT(0) +#define AXI_AD408X_CNTRL_3_PACKET_FORMAT_MSK GENMASK(3, 2) +#define AXI_AD408X_PACKET_FORMAT_20BIT 0x0 +#define AXI_AD408X_PACKET_FORMAT_16BIT 0x1 +#define AXI_AD408X_PACKET_FORMAT_14BIT 0x2 + +#define ADI_AXI_ADC_REG_SYNC_STATUS 0x0068 +#define ADI_AXI_ADC_SYNC_STATUS_ADC_SYNC_MSK BIT(0) + +#define ADI_AXI_ADC_REG_DRP_STATUS 0x0074 +#define ADI_AXI_ADC_DRP_LOCKED BIT(17) + +/* ADC Channel controls */ + +#define ADI_AXI_REG_CHAN_CTRL(c) (0x0400 + (c) * 0x40) +#define ADI_AXI_REG_CHAN_CTRL_LB_OWR BIT(11) +#define ADI_AXI_REG_CHAN_CTRL_PN_SEL_OWR BIT(10) +#define ADI_AXI_REG_CHAN_CTRL_IQCOR_EN BIT(9) +#define ADI_AXI_REG_CHAN_CTRL_DCFILT_EN BIT(8) +#define ADI_AXI_REG_CHAN_CTRL_FMT_MASK GENMASK(6, 4) +#define ADI_AXI_REG_CHAN_CTRL_FMT_SIGNEXT BIT(6) +#define ADI_AXI_REG_CHAN_CTRL_FMT_TYPE BIT(5) +#define ADI_AXI_REG_CHAN_CTRL_FMT_EN BIT(4) +#define ADI_AXI_REG_CHAN_CTRL_PN_TYPE_OWR BIT(1) +#define ADI_AXI_REG_CHAN_CTRL_ENABLE BIT(0) + +#define ADI_AXI_ADC_REG_CHAN_STATUS(c) (0x0404 + (c) * 0x40) +#define ADI_AXI_ADC_CHAN_STAT_PN_MASK GENMASK(2, 1) +/* out of sync */ +#define ADI_AXI_ADC_CHAN_STAT_PN_OOS BIT(1) +/* spurious out of sync */ +#define ADI_AXI_ADC_CHAN_STAT_PN_ERR BIT(2) + +#define ADI_AXI_ADC_REG_CHAN_CTRL_3(c) (0x0418 + (c) * 0x40) +#define ADI_AXI_ADC_CHAN_PN_SEL_MASK GENMASK(19, 16) + +#define ADI_AXI_ADC_REG_CHAN_USR_CTRL_2(c) (0x0424 + (c) * 0x40) +#define ADI_AXI_ADC_CHAN_USR_CTRL_2_DEC_RATE_N_MASK GENMASK(15, 0) + +/* IO Delays */ +#define ADI_AXI_ADC_REG_DELAY(l) (0x0800 + (l) * 0x4) +#define AXI_ADC_DELAY_CTRL_MASK GENMASK(4, 0) + +#define ADI_AXI_REG_CONFIG_WR 0x0080 +#define ADI_AXI_REG_CONFIG_RD 0x0084 +#define ADI_AXI_REG_CONFIG_CTRL 0x008c +#define ADI_AXI_REG_CONFIG_CTRL_READ 0x03 +#define ADI_AXI_REG_CONFIG_CTRL_WRITE 0x01 + +#define ADI_AXI_ADC_MAX_IO_NUM_LANES 15 + +#define ADI_AXI_REG_CHAN_CTRL_DEFAULTS \ + (ADI_AXI_REG_CHAN_CTRL_FMT_SIGNEXT | \ + ADI_AXI_REG_CHAN_CTRL_FMT_EN | \ + ADI_AXI_REG_CHAN_CTRL_ENABLE) + +#define ADI_AXI_REG_READ_BIT 0x8000 +#define ADI_AXI_REG_ADDRESS_MASK 0xff00 +#define ADI_AXI_REG_VALUE_MASK 0x00ff + +struct axi_adc_info { + unsigned int version; + const struct iio_backend_info *backend_info; + bool has_child_nodes; + const void *pdata; + unsigned int pdata_sz; +}; + +struct adi_axi_adc_state { + const struct axi_adc_info *info; + struct regmap *regmap; + struct device *dev; + /* lock to protect multiple accesses to the device registers */ + struct mutex lock; +}; + +static int axi_adc_enable(struct iio_backend *back) +{ + struct adi_axi_adc_state *st = iio_backend_get_priv(back); + unsigned int __val; + int ret; + + guard(mutex)(&st->lock); + ret = regmap_set_bits(st->regmap, ADI_AXI_REG_RSTN, + ADI_AXI_REG_RSTN_MMCM_RSTN); + if (ret) + return ret; + + /* + * Make sure the DRP (Dynamic Reconfiguration Port) is locked. Not all + * designs really use it but if they don't we still get the lock bit + * set. So let's do it all the time so the code is generic. + */ + ret = regmap_read_poll_timeout(st->regmap, ADI_AXI_ADC_REG_DRP_STATUS, + __val, __val & ADI_AXI_ADC_DRP_LOCKED, + 100, 1000); + if (ret) + return ret; + + return regmap_set_bits(st->regmap, ADI_AXI_REG_RSTN, + ADI_AXI_REG_RSTN_RSTN | ADI_AXI_REG_RSTN_MMCM_RSTN); +} + +static void axi_adc_disable(struct iio_backend *back) +{ + struct adi_axi_adc_state *st = iio_backend_get_priv(back); + + guard(mutex)(&st->lock); + regmap_write(st->regmap, ADI_AXI_REG_RSTN, 0); +} + +static int axi_adc_data_format_set(struct iio_backend *back, unsigned int chan, + const struct iio_backend_data_fmt *data) +{ + struct adi_axi_adc_state *st = iio_backend_get_priv(back); + u32 val; + + if (!data->enable) + return regmap_clear_bits(st->regmap, + ADI_AXI_REG_CHAN_CTRL(chan), + ADI_AXI_REG_CHAN_CTRL_FMT_EN); + + val = FIELD_PREP(ADI_AXI_REG_CHAN_CTRL_FMT_EN, true); + if (data->sign_extend) + val |= FIELD_PREP(ADI_AXI_REG_CHAN_CTRL_FMT_SIGNEXT, true); + if (data->type == IIO_BACKEND_OFFSET_BINARY) + val |= FIELD_PREP(ADI_AXI_REG_CHAN_CTRL_FMT_TYPE, true); + + return regmap_update_bits(st->regmap, ADI_AXI_REG_CHAN_CTRL(chan), + ADI_AXI_REG_CHAN_CTRL_FMT_MASK, val); +} + +static int axi_adc_data_sample_trigger(struct iio_backend *back, + enum iio_backend_sample_trigger trigger) +{ + struct adi_axi_adc_state *st = iio_backend_get_priv(back); + + switch (trigger) { + case IIO_BACKEND_SAMPLE_TRIGGER_EDGE_RISING: + return regmap_clear_bits(st->regmap, ADI_AXI_ADC_REG_CTRL, + ADI_AXI_ADC_CTRL_DDR_EDGESEL_MASK); + case IIO_BACKEND_SAMPLE_TRIGGER_EDGE_FALLING: + return regmap_set_bits(st->regmap, ADI_AXI_ADC_REG_CTRL, + ADI_AXI_ADC_CTRL_DDR_EDGESEL_MASK); + default: + return -EINVAL; + } +} + +static int axi_adc_iodelays_set(struct iio_backend *back, unsigned int lane, + unsigned int tap) +{ + struct adi_axi_adc_state *st = iio_backend_get_priv(back); + int ret; + u32 val; + + if (tap > FIELD_MAX(AXI_ADC_DELAY_CTRL_MASK)) + return -EINVAL; + if (lane > ADI_AXI_ADC_MAX_IO_NUM_LANES) + return -EINVAL; + + guard(mutex)(&st->lock); + ret = regmap_write(st->regmap, ADI_AXI_ADC_REG_DELAY(lane), tap); + if (ret) + return ret; + /* + * If readback is ~0, that means there are issues with the + * delay_clk. + */ + ret = regmap_read(st->regmap, ADI_AXI_ADC_REG_DELAY(lane), &val); + if (ret) + return ret; + if (val == U32_MAX) + return -EIO; + + return 0; +} + +static int axi_adc_test_pattern_set(struct iio_backend *back, + unsigned int chan, + enum iio_backend_test_pattern pattern) +{ + struct adi_axi_adc_state *st = iio_backend_get_priv(back); + + switch (pattern) { + case IIO_BACKEND_NO_TEST_PATTERN: + /* nothing to do */ + return 0; + case IIO_BACKEND_ADI_PRBS_9A: + return regmap_update_bits(st->regmap, ADI_AXI_ADC_REG_CHAN_CTRL_3(chan), + ADI_AXI_ADC_CHAN_PN_SEL_MASK, + FIELD_PREP(ADI_AXI_ADC_CHAN_PN_SEL_MASK, 0)); + case IIO_BACKEND_ADI_PRBS_23A: + return regmap_update_bits(st->regmap, ADI_AXI_ADC_REG_CHAN_CTRL_3(chan), + ADI_AXI_ADC_CHAN_PN_SEL_MASK, + FIELD_PREP(ADI_AXI_ADC_CHAN_PN_SEL_MASK, 1)); + default: + return -EINVAL; + } +} + +static int axi_adc_oversampling_ratio_set(struct iio_backend *back, + unsigned int chan, + unsigned int rate) +{ + struct adi_axi_adc_state *st = iio_backend_get_priv(back); + + return regmap_update_bits(st->regmap, + ADI_AXI_ADC_REG_CHAN_USR_CTRL_2(chan), + ADI_AXI_ADC_CHAN_USR_CTRL_2_DEC_RATE_N_MASK, + FIELD_PREP(ADI_AXI_ADC_CHAN_USR_CTRL_2_DEC_RATE_N_MASK, + rate)); +} + +static int axi_adc_read_chan_status(struct adi_axi_adc_state *st, unsigned int chan, + unsigned int *status) +{ + int ret; + + guard(mutex)(&st->lock); + /* reset test bits by setting them */ + ret = regmap_write(st->regmap, ADI_AXI_ADC_REG_CHAN_STATUS(chan), + ADI_AXI_ADC_CHAN_STAT_PN_MASK); + if (ret) + return ret; + + /* let's give enough time to validate or erroring the incoming pattern */ + fsleep(1000); + + return regmap_read(st->regmap, ADI_AXI_ADC_REG_CHAN_STATUS(chan), + status); +} + +static int axi_adc_chan_status(struct iio_backend *back, unsigned int chan, + bool *error) +{ + struct adi_axi_adc_state *st = iio_backend_get_priv(back); + u32 val; + int ret; + + ret = axi_adc_read_chan_status(st, chan, &val); + if (ret) + return ret; + + if (ADI_AXI_ADC_CHAN_STAT_PN_MASK & val) + *error = true; + else + *error = false; + + return 0; +} + +static int axi_adc_debugfs_print_chan_status(struct iio_backend *back, + unsigned int chan, char *buf, + size_t len) +{ + struct adi_axi_adc_state *st = iio_backend_get_priv(back); + u32 val; + int ret; + + ret = axi_adc_read_chan_status(st, chan, &val); + if (ret) + return ret; + + /* + * PN_ERR is cleared in case out of sync is set. Hence, no point in + * checking both bits. + */ + if (val & ADI_AXI_ADC_CHAN_STAT_PN_OOS) + return scnprintf(buf, len, "CH%u: Out of Sync.\n", chan); + if (val & ADI_AXI_ADC_CHAN_STAT_PN_ERR) + return scnprintf(buf, len, "CH%u: Spurious Out of Sync.\n", chan); + + return scnprintf(buf, len, "CH%u: OK.\n", chan); +} + +static int axi_adc_chan_enable(struct iio_backend *back, unsigned int chan) +{ + struct adi_axi_adc_state *st = iio_backend_get_priv(back); + + return regmap_set_bits(st->regmap, ADI_AXI_REG_CHAN_CTRL(chan), + ADI_AXI_REG_CHAN_CTRL_ENABLE); +} + +static int axi_adc_chan_disable(struct iio_backend *back, unsigned int chan) +{ + struct adi_axi_adc_state *st = iio_backend_get_priv(back); + + return regmap_clear_bits(st->regmap, ADI_AXI_REG_CHAN_CTRL(chan), + ADI_AXI_REG_CHAN_CTRL_ENABLE); +} + +static int axi_adc_interface_type_get(struct iio_backend *back, + enum iio_backend_interface_type *type) +{ + struct adi_axi_adc_state *st = iio_backend_get_priv(back); + unsigned int val; + int ret; + + ret = regmap_read(st->regmap, ADI_AXI_ADC_REG_CONFIG, &val); + if (ret) + return ret; + + if (val & ADI_AXI_ADC_REG_CONFIG_CMOS_OR_LVDS_N) + *type = IIO_BACKEND_INTERFACE_SERIAL_CMOS; + else + *type = IIO_BACKEND_INTERFACE_SERIAL_LVDS; + + return 0; +} + +static int axi_adc_ad485x_data_size_set(struct iio_backend *back, + unsigned int size) +{ + struct adi_axi_adc_state *st = iio_backend_get_priv(back); + unsigned int val; + + switch (size) { + /* + * There are two different variants of the AXI AXI_AD485X IP block, a + * 16-bit and a 20-bit variant. + * The 0x0 value (AXI_AD485X_PACKET_FORMAT_20BIT) is corresponding also + * to the 16-bit variant of the IP block. + */ + case 16: + case 20: + val = AXI_AD485X_PACKET_FORMAT_20BIT; + break; + case 24: + val = AXI_AD485X_PACKET_FORMAT_24BIT; + break; + /* + * The 0x2 (AXI_AD485X_PACKET_FORMAT_32BIT) corresponds only to the + * 20-bit variant of the IP block. Setting this value properly is + * ensured by the upper layers of the drivers calling the axi-adc + * functions. + * Also, for 16-bit IP block, the 0x2 (AXI_AD485X_PACKET_FORMAT_32BIT) + * value is handled as maximum size available which is 24-bit for this + * configuration. + */ + case 32: + val = AXI_AD485X_PACKET_FORMAT_32BIT; + break; + default: + return -EINVAL; + } + + return regmap_update_bits(st->regmap, ADI_AXI_ADC_REG_CNTRL_3, + AXI_AD485X_CNTRL_3_PACKET_FORMAT_MSK, + FIELD_PREP(AXI_AD485X_CNTRL_3_PACKET_FORMAT_MSK, val)); +} + +static int axi_adc_ad485x_oversampling_ratio_set(struct iio_backend *back, + unsigned int chan, + unsigned int ratio) +{ + struct adi_axi_adc_state *st = iio_backend_get_priv(back); + + /* The current state of the function enables or disables the + * oversampling in REG_CNTRL_3 register. A ratio equal to 1 implies no + * oversampling, while a value greater than 1 implies oversampling being + * enabled. + */ + switch (ratio) { + case 0: + return -EINVAL; + case 1: + return regmap_clear_bits(st->regmap, ADI_AXI_ADC_REG_CNTRL_3, + AXI_AD485X_CNTRL_3_OS_EN_MSK); + default: + return regmap_set_bits(st->regmap, ADI_AXI_ADC_REG_CNTRL_3, + AXI_AD485X_CNTRL_3_OS_EN_MSK); + } +} + +static int axi_adc_ad408x_filter_type_set(struct iio_backend *back, + enum iio_backend_filter_type type) +{ + struct adi_axi_adc_state *st = iio_backend_get_priv(back); + + if (type) + return regmap_set_bits(st->regmap, ADI_AXI_ADC_REG_CNTRL_3, + AXI_AD408X_CNTRL_3_FILTER_EN_MSK); + + return regmap_clear_bits(st->regmap, ADI_AXI_ADC_REG_CNTRL_3, + AXI_AD408X_CNTRL_3_FILTER_EN_MSK); +} + +static int axi_adc_ad408x_data_size_set(struct iio_backend *back, + unsigned int size) +{ + struct adi_axi_adc_state *st = iio_backend_get_priv(back); + unsigned int val; + + switch (size) { + case 20: + val = AXI_AD408X_PACKET_FORMAT_20BIT; + break; + case 16: + val = AXI_AD408X_PACKET_FORMAT_16BIT; + break; + case 14: + val = AXI_AD408X_PACKET_FORMAT_14BIT; + break; + default: + return -EINVAL; + } + + return regmap_update_bits(st->regmap, ADI_AXI_ADC_REG_CNTRL_3, + AXI_AD408X_CNTRL_3_PACKET_FORMAT_MSK, + FIELD_PREP(AXI_AD408X_CNTRL_3_PACKET_FORMAT_MSK, val)); +} + +static int axi_adc_ad408x_interface_data_align(struct iio_backend *back, + u32 timeout_us) +{ + struct adi_axi_adc_state *st = iio_backend_get_priv(back); + u32 val; + int ret; + + ret = regmap_set_bits(st->regmap, ADI_AXI_ADC_REG_CTRL, + ADI_AXI_ADC_CTRL_SYNC_MSK); + if (ret) + return ret; + + return regmap_read_poll_timeout(st->regmap, ADI_AXI_ADC_REG_SYNC_STATUS, + val, + FIELD_GET(ADI_AXI_ADC_SYNC_STATUS_ADC_SYNC_MSK, val), + 1, timeout_us); +} + +static int axi_adc_num_lanes_set(struct iio_backend *back, + unsigned int num_lanes) +{ + struct adi_axi_adc_state *st = iio_backend_get_priv(back); + + if (!num_lanes) + return -EINVAL; + + return regmap_update_bits(st->regmap, ADI_AXI_ADC_REG_CTRL, + ADI_AXI_ADC_CTRL_NUM_LANES_MSK, + FIELD_PREP(ADI_AXI_ADC_CTRL_NUM_LANES_MSK, num_lanes)); +} + +static struct iio_buffer *axi_adc_request_buffer(struct iio_backend *back, + struct iio_dev *indio_dev) +{ + struct adi_axi_adc_state *st = iio_backend_get_priv(back); + const char *dma_name; + + if (device_property_read_string(st->dev, "dma-names", &dma_name)) + dma_name = "rx"; + + return iio_dmaengine_buffer_setup(st->dev, indio_dev, dma_name); +} + +static int axi_adc_raw_write(struct iio_backend *back, u32 val) +{ + struct adi_axi_adc_state *st = iio_backend_get_priv(back); + + regmap_write(st->regmap, ADI_AXI_REG_CONFIG_WR, val); + regmap_write(st->regmap, ADI_AXI_REG_CONFIG_CTRL, + ADI_AXI_REG_CONFIG_CTRL_WRITE); + fsleep(100); + regmap_write(st->regmap, ADI_AXI_REG_CONFIG_CTRL, 0x00); + fsleep(100); + + return 0; +} + +static int axi_adc_raw_read(struct iio_backend *back, u32 *val) +{ + struct adi_axi_adc_state *st = iio_backend_get_priv(back); + + regmap_write(st->regmap, ADI_AXI_REG_CONFIG_CTRL, + ADI_AXI_REG_CONFIG_CTRL_READ); + fsleep(100); + regmap_read(st->regmap, ADI_AXI_REG_CONFIG_RD, val); + regmap_write(st->regmap, ADI_AXI_REG_CONFIG_CTRL, 0x00); + fsleep(100); + + return 0; +} + +static int ad7606_bus_reg_read(struct iio_backend *back, u32 reg, u32 *val) +{ + struct adi_axi_adc_state *st = iio_backend_get_priv(back); + u32 addr, reg_val; + + guard(mutex)(&st->lock); + + /* + * The address is written on the highest weight byte, and the MSB set + * at 1 indicates a read operation. + */ + addr = FIELD_PREP(ADI_AXI_REG_ADDRESS_MASK, reg) | ADI_AXI_REG_READ_BIT; + axi_adc_raw_write(back, addr); + axi_adc_raw_read(back, ®_val); + + *val = FIELD_GET(ADI_AXI_REG_VALUE_MASK, reg_val); + + /* Write 0x0 on the bus to get back to ADC mode */ + axi_adc_raw_write(back, 0); + + return 0; +} + +static int ad7606_bus_reg_write(struct iio_backend *back, u32 reg, u32 val) +{ + struct adi_axi_adc_state *st = iio_backend_get_priv(back); + u32 buf; + + guard(mutex)(&st->lock); + + /* Write any register to switch to register mode */ + axi_adc_raw_write(back, 0xaf00); + + buf = FIELD_PREP(ADI_AXI_REG_ADDRESS_MASK, reg) | + FIELD_PREP(ADI_AXI_REG_VALUE_MASK, val); + axi_adc_raw_write(back, buf); + + /* Write 0x0 on the bus to get back to ADC mode */ + axi_adc_raw_write(back, 0); + + return 0; +} + +static void axi_adc_free_buffer(struct iio_backend *back, + struct iio_buffer *buffer) +{ + iio_dmaengine_buffer_teardown(buffer); +} + +static int axi_adc_reg_access(struct iio_backend *back, unsigned int reg, + unsigned int writeval, unsigned int *readval) +{ + struct adi_axi_adc_state *st = iio_backend_get_priv(back); + + if (readval) + return regmap_read(st->regmap, reg, readval); + + return regmap_write(st->regmap, reg, writeval); +} + +static const struct regmap_config axi_adc_regmap_config = { + .val_bits = 32, + .reg_bits = 32, + .reg_stride = 4, +}; + +static void axi_adc_child_remove(void *data) +{ + platform_device_unregister(data); +} + +static int axi_adc_create_platform_device(struct adi_axi_adc_state *st, + struct fwnode_handle *child) +{ + struct platform_device_info pi = { + .parent = st->dev, + .name = fwnode_get_name(child), + .id = PLATFORM_DEVID_AUTO, + .fwnode = child, + .data = st->info->pdata, + .size_data = st->info->pdata_sz, + }; + struct platform_device *pdev; + + pdev = platform_device_register_full(&pi); + if (IS_ERR(pdev)) + return PTR_ERR(pdev); + + return devm_add_action_or_reset(st->dev, axi_adc_child_remove, pdev); +} + +static const struct iio_backend_ops adi_axi_adc_ops = { + .enable = axi_adc_enable, + .disable = axi_adc_disable, + .data_format_set = axi_adc_data_format_set, + .chan_enable = axi_adc_chan_enable, + .chan_disable = axi_adc_chan_disable, + .request_buffer = axi_adc_request_buffer, + .free_buffer = axi_adc_free_buffer, + .data_sample_trigger = axi_adc_data_sample_trigger, + .iodelay_set = axi_adc_iodelays_set, + .test_pattern_set = axi_adc_test_pattern_set, + .chan_status = axi_adc_chan_status, + .interface_type_get = axi_adc_interface_type_get, + .oversampling_ratio_set = axi_adc_oversampling_ratio_set, + .num_lanes_set = axi_adc_num_lanes_set, + .debugfs_reg_access = iio_backend_debugfs_ptr(axi_adc_reg_access), + .debugfs_print_chan_status = iio_backend_debugfs_ptr(axi_adc_debugfs_print_chan_status), +}; + +static const struct iio_backend_info adi_axi_adc_generic = { + .name = "axi-adc", + .ops = &adi_axi_adc_ops, + .caps = IIO_BACKEND_CAP_CALIBRATION | IIO_BACKEND_CAP_BUFFER | + IIO_BACKEND_CAP_ENABLE, +}; + +static const struct iio_backend_ops adi_ad485x_ops = { + .enable = axi_adc_enable, + .disable = axi_adc_disable, + .data_format_set = axi_adc_data_format_set, + .chan_enable = axi_adc_chan_enable, + .chan_disable = axi_adc_chan_disable, + .request_buffer = axi_adc_request_buffer, + .free_buffer = axi_adc_free_buffer, + .data_sample_trigger = axi_adc_data_sample_trigger, + .iodelay_set = axi_adc_iodelays_set, + .chan_status = axi_adc_chan_status, + .interface_type_get = axi_adc_interface_type_get, + .data_size_set = axi_adc_ad485x_data_size_set, + .oversampling_ratio_set = axi_adc_ad485x_oversampling_ratio_set, + .debugfs_reg_access = iio_backend_debugfs_ptr(axi_adc_reg_access), + .debugfs_print_chan_status = + iio_backend_debugfs_ptr(axi_adc_debugfs_print_chan_status), +}; + +static const struct iio_backend_info axi_ad485x = { + .name = "axi-ad485x", + .ops = &adi_ad485x_ops, + .caps = IIO_BACKEND_CAP_CALIBRATION | IIO_BACKEND_CAP_BUFFER | + IIO_BACKEND_CAP_ENABLE, +}; + +static const struct iio_backend_ops adi_ad408x_ops = { + .enable = axi_adc_enable, + .disable = axi_adc_disable, + .chan_enable = axi_adc_chan_enable, + .chan_disable = axi_adc_chan_disable, + .request_buffer = axi_adc_request_buffer, + .free_buffer = axi_adc_free_buffer, + .data_sample_trigger = axi_adc_data_sample_trigger, + .filter_type_set = axi_adc_ad408x_filter_type_set, + .data_size_set = axi_adc_ad408x_data_size_set, + .interface_data_align = axi_adc_ad408x_interface_data_align, + .num_lanes_set = axi_adc_num_lanes_set, + .debugfs_reg_access = iio_backend_debugfs_ptr(axi_adc_reg_access), + .debugfs_print_chan_status = iio_backend_debugfs_ptr(axi_adc_debugfs_print_chan_status), +}; + +static const struct iio_backend_info axi_ad408x = { + .name = "axi-ad408x", + .ops = &adi_ad408x_ops, + .caps = IIO_BACKEND_CAP_BUFFER | IIO_BACKEND_CAP_ENABLE, +}; + +static int adi_axi_adc_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct adi_axi_adc_state *st; + void __iomem *base; + unsigned int ver; + struct clk *clk; + int ret; + + st = devm_kzalloc(dev, sizeof(*st), GFP_KERNEL); + if (!st) + return -ENOMEM; + + base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(base)) + return PTR_ERR(base); + + st->dev = dev; + st->regmap = devm_regmap_init_mmio(dev, base, &axi_adc_regmap_config); + if (IS_ERR(st->regmap)) + return dev_err_probe(dev, PTR_ERR(st->regmap), + "failed to init register map\n"); + + st->info = device_get_match_data(dev); + if (!st->info) + return -ENODEV; + + clk = devm_clk_get_enabled(dev, NULL); + if (IS_ERR(clk)) + return dev_err_probe(dev, PTR_ERR(clk), + "failed to get clock\n"); + + /* + * Force disable the core. Up to the frontend to enable us. And we can + * still read/write registers... + */ + ret = regmap_write(st->regmap, ADI_AXI_REG_RSTN, 0); + if (ret) + return ret; + + ret = regmap_read(st->regmap, ADI_AXI_REG_VERSION, &ver); + if (ret) + return ret; + + if (ADI_AXI_PCORE_VER_MAJOR(ver) != ADI_AXI_PCORE_VER_MAJOR(st->info->version)) + return dev_err_probe(dev, -ENODEV, + "Major version mismatch. Expected %d.%.2d.%c, Reported %d.%.2d.%c\n", + ADI_AXI_PCORE_VER_MAJOR(st->info->version), + ADI_AXI_PCORE_VER_MINOR(st->info->version), + ADI_AXI_PCORE_VER_PATCH(st->info->version), + ADI_AXI_PCORE_VER_MAJOR(ver), + ADI_AXI_PCORE_VER_MINOR(ver), + ADI_AXI_PCORE_VER_PATCH(ver)); + + ret = devm_iio_backend_register(dev, st->info->backend_info, st); + if (ret) + return dev_err_probe(dev, ret, "failed to register iio backend\n"); + + device_for_each_child_node_scoped(dev, child) { + int val; + + if (!st->info->has_child_nodes) + return dev_err_probe(dev, -EINVAL, + "invalid fdt axi-dac compatible."); + + /* Processing only reg 0 node */ + ret = fwnode_property_read_u32(child, "reg", &val); + if (ret) + return dev_err_probe(dev, ret, "invalid reg property."); + if (val != 0) + return dev_err_probe(dev, -EINVAL, + "invalid node address."); + + ret = axi_adc_create_platform_device(st, child); + if (ret) + return dev_err_probe(dev, -EINVAL, + "cannot create device."); + } + + dev_info(dev, "AXI ADC IP core (%d.%.2d.%c) probed\n", + ADI_AXI_PCORE_VER_MAJOR(ver), + ADI_AXI_PCORE_VER_MINOR(ver), + ADI_AXI_PCORE_VER_PATCH(ver)); + + return 0; +} + +static const struct axi_adc_info adc_generic = { + .version = ADI_AXI_PCORE_VER(10, 0, 'a'), + .backend_info = &adi_axi_adc_generic, +}; + +static const struct axi_adc_info adi_axi_ad485x = { + .version = ADI_AXI_PCORE_VER(10, 0, 'a'), + .backend_info = &axi_ad485x, +}; + +static const struct ad7606_platform_data ad7606_pdata = { + .bus_reg_read = ad7606_bus_reg_read, + .bus_reg_write = ad7606_bus_reg_write, +}; + +static const struct axi_adc_info adc_ad7606 = { + .version = ADI_AXI_PCORE_VER(10, 0, 'a'), + .backend_info = &adi_axi_adc_generic, + .pdata = &ad7606_pdata, + .pdata_sz = sizeof(ad7606_pdata), + .has_child_nodes = true, +}; + +static const struct axi_adc_info adi_axi_ad408x = { + .version = ADI_AXI_PCORE_VER(10, 0, 'a'), + .backend_info = &axi_ad408x, +}; + +/* Match table for of_platform binding */ +static const struct of_device_id adi_axi_adc_of_match[] = { + { .compatible = "adi,axi-adc-10.0.a", .data = &adc_generic }, + { .compatible = "adi,axi-ad408x", .data = &adi_axi_ad408x }, + { .compatible = "adi,axi-ad485x", .data = &adi_axi_ad485x }, + { .compatible = "adi,axi-ad7606x", .data = &adc_ad7606 }, + { } +}; +MODULE_DEVICE_TABLE(of, adi_axi_adc_of_match); + +static struct platform_driver adi_axi_adc_driver = { + .driver = { + .name = KBUILD_MODNAME, + .of_match_table = adi_axi_adc_of_match, + }, + .probe = adi_axi_adc_probe, +}; +module_platform_driver(adi_axi_adc_driver); + +MODULE_AUTHOR("Michael Hennerich "); +MODULE_DESCRIPTION("Analog Devices Generic AXI ADC IP core driver"); +MODULE_LICENSE("GPL v2"); +MODULE_IMPORT_NS("IIO_DMAENGINE_BUFFER"); +MODULE_IMPORT_NS("IIO_BACKEND"); diff --git a/drivers/iio/adc/aspeed_adc.c b/drivers/iio/adc/aspeed_adc.c new file mode 100644 index 000000000..6169ce282 --- /dev/null +++ b/drivers/iio/adc/aspeed_adc.c @@ -0,0 +1,789 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Aspeed AST2400/2500/2600 ADC + * + * Copyright (C) 2017 Google, Inc. + * Copyright (C) 2021 Aspeed Technology Inc. + * + * ADC clock formula: + * Ast2400/Ast2500: + * clock period = period of PCLK * 2 * (ADC0C[31:17] + 1) * (ADC0C[9:0] + 1) + * Ast2600: + * clock period = period of PCLK * 2 * (ADC0C[15:0] + 1) + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#define ASPEED_RESOLUTION_BITS 10 +#define ASPEED_CLOCKS_PER_SAMPLE 12 + +#define ASPEED_REG_ENGINE_CONTROL 0x00 +#define ASPEED_REG_INTERRUPT_CONTROL 0x04 +#define ASPEED_REG_VGA_DETECT_CONTROL 0x08 +#define ASPEED_REG_CLOCK_CONTROL 0x0C +#define ASPEED_REG_COMPENSATION_TRIM 0xC4 +/* + * The register offset between 0xC8~0xCC can be read and won't affect the + * hardware logic in each version of ADC. + */ +#define ASPEED_REG_MAX 0xD0 + +#define ASPEED_ADC_ENGINE_ENABLE BIT(0) +#define ASPEED_ADC_OP_MODE GENMASK(3, 1) +#define ASPEED_ADC_OP_MODE_PWR_DOWN 0 +#define ASPEED_ADC_OP_MODE_STANDBY 1 +#define ASPEED_ADC_OP_MODE_NORMAL 7 +#define ASPEED_ADC_CTRL_COMPENSATION BIT(4) +#define ASPEED_ADC_AUTO_COMPENSATION BIT(5) +/* + * Bit 6 determines not only the reference voltage range but also the dividing + * circuit for battery sensing. + */ +#define ASPEED_ADC_REF_VOLTAGE GENMASK(7, 6) +#define ASPEED_ADC_REF_VOLTAGE_2500mV 0 +#define ASPEED_ADC_REF_VOLTAGE_1200mV 1 +#define ASPEED_ADC_REF_VOLTAGE_EXT_HIGH 2 +#define ASPEED_ADC_REF_VOLTAGE_EXT_LOW 3 +#define ASPEED_ADC_BAT_SENSING_DIV BIT(6) +#define ASPEED_ADC_BAT_SENSING_DIV_2_3 0 +#define ASPEED_ADC_BAT_SENSING_DIV_1_3 1 +#define ASPEED_ADC_CTRL_INIT_RDY BIT(8) +#define ASPEED_ADC_CH7_MODE BIT(12) +#define ASPEED_ADC_CH7_NORMAL 0 +#define ASPEED_ADC_CH7_BAT 1 +#define ASPEED_ADC_BAT_SENSING_ENABLE BIT(13) +#define ASPEED_ADC_CTRL_CHANNEL GENMASK(31, 16) +#define ASPEED_ADC_CTRL_CHANNEL_ENABLE(ch) FIELD_PREP(ASPEED_ADC_CTRL_CHANNEL, BIT(ch)) + +#define ASPEED_ADC_INIT_POLLING_TIME 500 +#define ASPEED_ADC_INIT_TIMEOUT 500000 +/* Battery sensing is typically on the last channel */ +#define ASPEED_ADC_BATTERY_CHANNEL 7 +/* + * When the sampling rate is too high, the ADC may not have enough charging + * time, resulting in a low voltage value. Thus, the default uses a slow + * sampling rate for most use cases. + */ +#define ASPEED_ADC_DEF_SAMPLING_RATE 65000 + +struct aspeed_adc_trim_locate { + const unsigned int offset; + const unsigned int field; +}; + +struct aspeed_adc_model_data { + const char *model_name; + unsigned int min_sampling_rate; // Hz + unsigned int max_sampling_rate; // Hz + unsigned int vref_fixed_mv; + bool wait_init_sequence; + bool need_prescaler; + bool bat_sense_sup; + u8 scaler_bit_width; + unsigned int num_channels; + const struct aspeed_adc_trim_locate *trim_locate; +}; + +struct adc_gain { + u8 mult; + u8 div; +}; + +struct aspeed_adc_data { + struct device *dev; + const struct aspeed_adc_model_data *model_data; + void __iomem *base; + spinlock_t clk_lock; + struct clk_hw *fixed_div_clk; + struct clk_hw *clk_prescaler; + struct clk_hw *clk_scaler; + struct reset_control *rst; + int vref_mv; + u32 sample_period_ns; + int cv; + bool battery_sensing; + struct adc_gain battery_mode_gain; +}; + +/* + * Enable multiple consecutive channels starting from channel 0. + * This creates a bitmask for channels 0 to (num_channels - 1). + * For example: num_channels=3 creates mask 0x0007 (channels 0,1,2) + */ +static inline u32 aspeed_adc_channels_mask(unsigned int num_channels) +{ + if (num_channels > 16) + return GENMASK(15, 0); + + return BIT(num_channels) - 1; +} + +static inline unsigned int aspeed_adc_get_active_channels(const struct aspeed_adc_data *data) +{ + /* + * For controllers with battery sensing capability, the last channel + * is reserved for battery sensing and should not be included in + * normal channel operations. + */ + if (data->model_data->bat_sense_sup) + return data->model_data->num_channels - 1; + return data->model_data->num_channels; +} + +#define ASPEED_CHAN(_idx, _data_reg_addr) { \ + .type = IIO_VOLTAGE, \ + .indexed = 1, \ + .channel = (_idx), \ + .address = (_data_reg_addr), \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | \ + BIT(IIO_CHAN_INFO_SAMP_FREQ) | \ + BIT(IIO_CHAN_INFO_OFFSET), \ +} + +static const struct iio_chan_spec aspeed_adc_iio_channels[] = { + ASPEED_CHAN(0, 0x10), + ASPEED_CHAN(1, 0x12), + ASPEED_CHAN(2, 0x14), + ASPEED_CHAN(3, 0x16), + ASPEED_CHAN(4, 0x18), + ASPEED_CHAN(5, 0x1A), + ASPEED_CHAN(6, 0x1C), + ASPEED_CHAN(7, 0x1E), + ASPEED_CHAN(8, 0x20), + ASPEED_CHAN(9, 0x22), + ASPEED_CHAN(10, 0x24), + ASPEED_CHAN(11, 0x26), + ASPEED_CHAN(12, 0x28), + ASPEED_CHAN(13, 0x2A), + ASPEED_CHAN(14, 0x2C), + ASPEED_CHAN(15, 0x2E), +}; + +#define ASPEED_BAT_CHAN(_idx, _data_reg_addr) { \ + .type = IIO_VOLTAGE, \ + .indexed = 1, \ + .channel = (_idx), \ + .address = (_data_reg_addr), \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ + BIT(IIO_CHAN_INFO_OFFSET), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | \ + BIT(IIO_CHAN_INFO_SAMP_FREQ), \ +} +static const struct iio_chan_spec aspeed_adc_iio_bat_channels[] = { + ASPEED_CHAN(0, 0x10), + ASPEED_CHAN(1, 0x12), + ASPEED_CHAN(2, 0x14), + ASPEED_CHAN(3, 0x16), + ASPEED_CHAN(4, 0x18), + ASPEED_CHAN(5, 0x1A), + ASPEED_CHAN(6, 0x1C), + ASPEED_BAT_CHAN(7, 0x1E), +}; + +static int aspeed_adc_set_trim_data(struct iio_dev *indio_dev) +{ + struct device_node *syscon; + struct regmap *scu; + u32 scu_otp, trimming_val; + struct aspeed_adc_data *data = iio_priv(indio_dev); + + syscon = of_find_node_by_name(NULL, "syscon"); + if (syscon == NULL) { + dev_warn(data->dev, "Couldn't find syscon node\n"); + return -EOPNOTSUPP; + } + scu = syscon_node_to_regmap(syscon); + of_node_put(syscon); + if (IS_ERR(scu)) { + dev_warn(data->dev, "Failed to get syscon regmap\n"); + return -EOPNOTSUPP; + } + if (data->model_data->trim_locate) { + if (regmap_read(scu, data->model_data->trim_locate->offset, + &scu_otp)) { + dev_warn(data->dev, + "Failed to get adc trimming data\n"); + trimming_val = 0x8; + } else { + trimming_val = + ((scu_otp) & + (data->model_data->trim_locate->field)) >> + __ffs(data->model_data->trim_locate->field); + if (!trimming_val) + trimming_val = 0x8; + } + dev_dbg(data->dev, + "trimming val = %d, offset = %08x, fields = %08x\n", + trimming_val, data->model_data->trim_locate->offset, + data->model_data->trim_locate->field); + writel(trimming_val, data->base + ASPEED_REG_COMPENSATION_TRIM); + } + return 0; +} + +static int aspeed_adc_compensation(struct iio_dev *indio_dev) +{ + struct aspeed_adc_data *data = iio_priv(indio_dev); + u32 index, adc_raw = 0; + u32 adc_engine_control_reg_val; + + adc_engine_control_reg_val = + readl(data->base + ASPEED_REG_ENGINE_CONTROL); + adc_engine_control_reg_val &= ~ASPEED_ADC_OP_MODE; + adc_engine_control_reg_val |= + (FIELD_PREP(ASPEED_ADC_OP_MODE, ASPEED_ADC_OP_MODE_NORMAL) | + ASPEED_ADC_ENGINE_ENABLE); + /* + * Enable compensating sensing: + * After that, the input voltage of ADC will force to half of the reference + * voltage. So the expected reading raw data will become half of the max + * value. We can get compensating value = 0x200 - ADC read raw value. + * It is recommended to average at least 10 samples to get a final CV. + */ + writel(adc_engine_control_reg_val | ASPEED_ADC_CTRL_COMPENSATION | + ASPEED_ADC_CTRL_CHANNEL_ENABLE(0), + data->base + ASPEED_REG_ENGINE_CONTROL); + /* + * After enable compensating sensing mode need to wait some time for the + * ADC stablize. Experiment result is 1ms. + */ + fsleep(1000); + + for (index = 0; index < 16; index++) { + /* + * Waiting for the sampling period ensures that the value acquired + * is fresh each time. + */ + ndelay(data->sample_period_ns); + adc_raw += readw(data->base + aspeed_adc_iio_channels[0].address); + } + adc_raw >>= 4; + data->cv = BIT(ASPEED_RESOLUTION_BITS - 1) - adc_raw; + writel(adc_engine_control_reg_val, + data->base + ASPEED_REG_ENGINE_CONTROL); + dev_dbg(data->dev, "Compensating value = %d\n", data->cv); + + return 0; +} + +static int aspeed_adc_set_sampling_rate(struct iio_dev *indio_dev, u32 rate) +{ + struct aspeed_adc_data *data = iio_priv(indio_dev); + + if (rate < data->model_data->min_sampling_rate || + rate > data->model_data->max_sampling_rate) + return -EINVAL; + /* Each sampling needs 12 clocks to convert.*/ + clk_set_rate(data->clk_scaler->clk, rate * ASPEED_CLOCKS_PER_SAMPLE); + rate = clk_get_rate(data->clk_scaler->clk); + data->sample_period_ns = DIV_ROUND_UP_ULL( + (u64)NSEC_PER_SEC * ASPEED_CLOCKS_PER_SAMPLE, rate); + dev_dbg(data->dev, "Adc clock = %d sample period = %d ns", rate, + data->sample_period_ns); + + return 0; +} + +static int aspeed_adc_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct aspeed_adc_data *data = iio_priv(indio_dev); + u32 adc_engine_control_reg_val; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + adc_engine_control_reg_val = readl(data->base + ASPEED_REG_ENGINE_CONTROL); + /* + * For battery sensing capable controllers, we need to enable + * the specific channel before reading. This is required because + * the battery channel may not be enabled by default. + */ + if (data->model_data->bat_sense_sup && + chan->channel == ASPEED_ADC_BATTERY_CHANNEL) { + u32 ctrl_reg = adc_engine_control_reg_val & ~ASPEED_ADC_CTRL_CHANNEL; + + ctrl_reg |= ASPEED_ADC_CTRL_CHANNEL_ENABLE(chan->channel); + writel(ctrl_reg, data->base + ASPEED_REG_ENGINE_CONTROL); + /* + * After enable a new channel need to wait some time for ADC stable + * Experiment result is 1ms. + */ + fsleep(1000); + } + + if (data->battery_sensing && chan->channel == ASPEED_ADC_BATTERY_CHANNEL) { + writel(adc_engine_control_reg_val | + FIELD_PREP(ASPEED_ADC_CH7_MODE, + ASPEED_ADC_CH7_BAT) | + ASPEED_ADC_BAT_SENSING_ENABLE, + data->base + ASPEED_REG_ENGINE_CONTROL); + /* + * After enable battery sensing mode need to wait some time for ADC stable + * Experiment result is 1ms. + */ + fsleep(1000); + *val = readw(data->base + chan->address); + *val = (*val * data->battery_mode_gain.mult) / + data->battery_mode_gain.div; + } else + *val = readw(data->base + chan->address); + /* Restore control register value */ + writel(adc_engine_control_reg_val, + data->base + ASPEED_REG_ENGINE_CONTROL); + return IIO_VAL_INT; + + case IIO_CHAN_INFO_OFFSET: + if (data->battery_sensing && chan->channel == ASPEED_ADC_BATTERY_CHANNEL) + *val = (data->cv * data->battery_mode_gain.mult) / + data->battery_mode_gain.div; + else + *val = data->cv; + return IIO_VAL_INT; + + case IIO_CHAN_INFO_SCALE: + *val = data->vref_mv; + *val2 = ASPEED_RESOLUTION_BITS; + return IIO_VAL_FRACTIONAL_LOG2; + + case IIO_CHAN_INFO_SAMP_FREQ: + *val = clk_get_rate(data->clk_scaler->clk) / + ASPEED_CLOCKS_PER_SAMPLE; + return IIO_VAL_INT; + + default: + return -EINVAL; + } +} + +static int aspeed_adc_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, int val2, long mask) +{ + switch (mask) { + case IIO_CHAN_INFO_SAMP_FREQ: + return aspeed_adc_set_sampling_rate(indio_dev, val); + + case IIO_CHAN_INFO_SCALE: + case IIO_CHAN_INFO_RAW: + /* + * Technically, these could be written but the only reasons + * for doing so seem better handled in userspace. EPERM is + * returned to signal this is a policy choice rather than a + * hardware limitation. + */ + return -EPERM; + + default: + return -EINVAL; + } +} + +static int aspeed_adc_reg_access(struct iio_dev *indio_dev, + unsigned int reg, unsigned int writeval, + unsigned int *readval) +{ + struct aspeed_adc_data *data = iio_priv(indio_dev); + + if (!readval || reg % 4 || reg > ASPEED_REG_MAX) + return -EINVAL; + + *readval = readl(data->base + reg); + + return 0; +} + +static const struct iio_info aspeed_adc_iio_info = { + .read_raw = aspeed_adc_read_raw, + .write_raw = aspeed_adc_write_raw, + .debugfs_reg_access = aspeed_adc_reg_access, +}; + +static void aspeed_adc_unregister_fixed_divider(void *data) +{ + struct clk_hw *clk = data; + + clk_hw_unregister_fixed_factor(clk); +} + +static void aspeed_adc_reset_assert(void *data) +{ + struct reset_control *rst = data; + + reset_control_assert(rst); +} + +static void aspeed_adc_clk_disable_unprepare(void *data) +{ + struct clk *clk = data; + + clk_disable_unprepare(clk); +} + +static void aspeed_adc_power_down(void *data) +{ + struct aspeed_adc_data *priv_data = data; + + writel(FIELD_PREP(ASPEED_ADC_OP_MODE, ASPEED_ADC_OP_MODE_PWR_DOWN), + priv_data->base + ASPEED_REG_ENGINE_CONTROL); +} + +static int aspeed_adc_vref_config(struct iio_dev *indio_dev) +{ + struct aspeed_adc_data *data = iio_priv(indio_dev); + int ret; + u32 adc_engine_control_reg_val; + + if (data->model_data->vref_fixed_mv) { + data->vref_mv = data->model_data->vref_fixed_mv; + return 0; + } + adc_engine_control_reg_val = + readl(data->base + ASPEED_REG_ENGINE_CONTROL); + adc_engine_control_reg_val &= ~ASPEED_ADC_REF_VOLTAGE; + + ret = devm_regulator_get_enable_read_voltage(data->dev, "vref"); + if (ret < 0 && ret != -ENODEV) + return ret; + + if (ret != -ENODEV) { + data->vref_mv = ret / 1000; + + if ((data->vref_mv >= 1550) && (data->vref_mv <= 2700)) + writel(adc_engine_control_reg_val | + FIELD_PREP( + ASPEED_ADC_REF_VOLTAGE, + ASPEED_ADC_REF_VOLTAGE_EXT_HIGH), + data->base + ASPEED_REG_ENGINE_CONTROL); + else if ((data->vref_mv >= 900) && (data->vref_mv <= 1650)) + writel(adc_engine_control_reg_val | + FIELD_PREP( + ASPEED_ADC_REF_VOLTAGE, + ASPEED_ADC_REF_VOLTAGE_EXT_LOW), + data->base + ASPEED_REG_ENGINE_CONTROL); + else { + dev_err(data->dev, "Regulator voltage %d not support", + data->vref_mv); + return -EOPNOTSUPP; + } + } else { + data->vref_mv = 2500000; + of_property_read_u32(data->dev->of_node, + "aspeed,int-vref-microvolt", + &data->vref_mv); + /* Conversion from uV to mV */ + data->vref_mv /= 1000; + if (data->vref_mv == 2500) + writel(adc_engine_control_reg_val | + FIELD_PREP(ASPEED_ADC_REF_VOLTAGE, + ASPEED_ADC_REF_VOLTAGE_2500mV), + data->base + ASPEED_REG_ENGINE_CONTROL); + else if (data->vref_mv == 1200) + writel(adc_engine_control_reg_val | + FIELD_PREP(ASPEED_ADC_REF_VOLTAGE, + ASPEED_ADC_REF_VOLTAGE_1200mV), + data->base + ASPEED_REG_ENGINE_CONTROL); + else { + dev_err(data->dev, "Voltage %d not support", data->vref_mv); + return -EOPNOTSUPP; + } + } + + return 0; +} + +static int aspeed_adc_probe(struct platform_device *pdev) +{ + struct iio_dev *indio_dev; + struct aspeed_adc_data *data; + int ret; + u32 adc_engine_control_reg_val; + struct device *dev = &pdev->dev; + struct device_node *np = dev_of_node(dev); + unsigned long scaler_flags = 0; + char clk_name[32], clk_parent_name[32]; + + indio_dev = devm_iio_device_alloc(dev, sizeof(*data)); + if (!indio_dev) + return -ENOMEM; + + data = iio_priv(indio_dev); + data->dev = dev; + data->model_data = of_device_get_match_data(dev); + platform_set_drvdata(pdev, indio_dev); + + data->base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(data->base)) + return PTR_ERR(data->base); + + /* Register ADC clock prescaler with source specified by device tree. */ + spin_lock_init(&data->clk_lock); + snprintf(clk_parent_name, ARRAY_SIZE(clk_parent_name), "%s", + of_clk_get_parent_name(np, 0)); + snprintf(clk_name, ARRAY_SIZE(clk_name), "%s-fixed-div", + data->model_data->model_name); + data->fixed_div_clk = clk_hw_register_fixed_factor(dev, clk_name, + clk_parent_name, 0, 1, 2); + if (IS_ERR(data->fixed_div_clk)) + return PTR_ERR(data->fixed_div_clk); + + ret = devm_add_action_or_reset(dev, aspeed_adc_unregister_fixed_divider, + data->fixed_div_clk); + if (ret) + return ret; + snprintf(clk_parent_name, ARRAY_SIZE(clk_parent_name), clk_name); + + if (data->model_data->need_prescaler) { + snprintf(clk_name, ARRAY_SIZE(clk_name), "%s-prescaler", + data->model_data->model_name); + data->clk_prescaler = devm_clk_hw_register_divider( + dev, clk_name, clk_parent_name, 0, + data->base + ASPEED_REG_CLOCK_CONTROL, 17, 15, 0, + &data->clk_lock); + if (IS_ERR(data->clk_prescaler)) + return PTR_ERR(data->clk_prescaler); + snprintf(clk_parent_name, ARRAY_SIZE(clk_parent_name), + clk_name); + scaler_flags = CLK_SET_RATE_PARENT; + } + /* + * Register ADC clock scaler downstream from the prescaler. Allow rate + * setting to adjust the prescaler as well. + */ + snprintf(clk_name, ARRAY_SIZE(clk_name), "%s-scaler", + data->model_data->model_name); + data->clk_scaler = devm_clk_hw_register_divider( + dev, clk_name, clk_parent_name, scaler_flags, + data->base + ASPEED_REG_CLOCK_CONTROL, 0, + data->model_data->scaler_bit_width, + data->model_data->need_prescaler ? CLK_DIVIDER_ONE_BASED : 0, + &data->clk_lock); + if (IS_ERR(data->clk_scaler)) + return PTR_ERR(data->clk_scaler); + + data->rst = devm_reset_control_get_shared(dev, NULL); + if (IS_ERR(data->rst)) + return dev_err_probe(dev, PTR_ERR(data->rst), + "invalid or missing reset controller device tree entry"); + + reset_control_deassert(data->rst); + + ret = devm_add_action_or_reset(dev, aspeed_adc_reset_assert, data->rst); + if (ret) + return ret; + + ret = aspeed_adc_vref_config(indio_dev); + if (ret) + return ret; + + ret = aspeed_adc_set_trim_data(indio_dev); + if (ret) + return ret; + + if (of_property_present(np, "aspeed,battery-sensing")) { + if (data->model_data->bat_sense_sup) { + data->battery_sensing = 1; + if (readl(data->base + ASPEED_REG_ENGINE_CONTROL) & + ASPEED_ADC_BAT_SENSING_DIV) { + data->battery_mode_gain.mult = 3; + data->battery_mode_gain.div = 1; + } else { + data->battery_mode_gain.mult = 3; + data->battery_mode_gain.div = 2; + } + } else + dev_warn(dev, "Failed to enable battery-sensing mode\n"); + } + + ret = clk_prepare_enable(data->clk_scaler->clk); + if (ret) + return ret; + ret = devm_add_action_or_reset(dev, aspeed_adc_clk_disable_unprepare, + data->clk_scaler->clk); + if (ret) + return ret; + ret = aspeed_adc_set_sampling_rate(indio_dev, + ASPEED_ADC_DEF_SAMPLING_RATE); + if (ret) + return ret; + + adc_engine_control_reg_val = + readl(data->base + ASPEED_REG_ENGINE_CONTROL); + adc_engine_control_reg_val |= + FIELD_PREP(ASPEED_ADC_OP_MODE, ASPEED_ADC_OP_MODE_NORMAL) | + ASPEED_ADC_ENGINE_ENABLE; + /* Enable engine in normal mode. */ + writel(adc_engine_control_reg_val, + data->base + ASPEED_REG_ENGINE_CONTROL); + + ret = devm_add_action_or_reset(dev, aspeed_adc_power_down, data); + if (ret) + return ret; + + if (data->model_data->wait_init_sequence) { + /* Wait for initial sequence complete. */ + ret = readl_poll_timeout(data->base + ASPEED_REG_ENGINE_CONTROL, + adc_engine_control_reg_val, + adc_engine_control_reg_val & + ASPEED_ADC_CTRL_INIT_RDY, + ASPEED_ADC_INIT_POLLING_TIME, + ASPEED_ADC_INIT_TIMEOUT); + if (ret) + return ret; + } + + aspeed_adc_compensation(indio_dev); + /* Start all channels in normal mode. */ + adc_engine_control_reg_val = + readl(data->base + ASPEED_REG_ENGINE_CONTROL); + FIELD_MODIFY(ASPEED_ADC_CTRL_CHANNEL, &adc_engine_control_reg_val, + aspeed_adc_channels_mask(aspeed_adc_get_active_channels(data))); + + writel(adc_engine_control_reg_val, + data->base + ASPEED_REG_ENGINE_CONTROL); + + indio_dev->name = data->model_data->model_name; + indio_dev->info = &aspeed_adc_iio_info; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = data->battery_sensing ? + aspeed_adc_iio_bat_channels : + aspeed_adc_iio_channels; + indio_dev->num_channels = data->model_data->num_channels; + + return devm_iio_device_register(dev, indio_dev); +} + +static const struct aspeed_adc_trim_locate ast2500_adc_trim = { + .offset = 0x154, + .field = GENMASK(31, 28), +}; + +static const struct aspeed_adc_trim_locate ast2600_adc0_trim = { + .offset = 0x5d0, + .field = GENMASK(3, 0), +}; + +static const struct aspeed_adc_trim_locate ast2600_adc1_trim = { + .offset = 0x5d0, + .field = GENMASK(7, 4), +}; + +static const struct aspeed_adc_trim_locate ast2700_adc0_trim = { + .offset = 0x820, + .field = GENMASK(3, 0), +}; + +static const struct aspeed_adc_trim_locate ast2700_adc1_trim = { + .offset = 0x820, + .field = GENMASK(7, 4), +}; + +static const struct aspeed_adc_model_data ast2400_model_data = { + .model_name = "ast2400-adc", + .vref_fixed_mv = 2500, + .min_sampling_rate = 10000, + .max_sampling_rate = 500000, + .need_prescaler = true, + .scaler_bit_width = 10, + .num_channels = 16, +}; + +static const struct aspeed_adc_model_data ast2500_model_data = { + .model_name = "ast2500-adc", + .vref_fixed_mv = 1800, + .min_sampling_rate = 1, + .max_sampling_rate = 1000000, + .wait_init_sequence = true, + .need_prescaler = true, + .scaler_bit_width = 10, + .num_channels = 16, + .trim_locate = &ast2500_adc_trim, +}; + +static const struct aspeed_adc_model_data ast2600_adc0_model_data = { + .model_name = "ast2600-adc0", + .min_sampling_rate = 10000, + .max_sampling_rate = 500000, + .wait_init_sequence = true, + .bat_sense_sup = true, + .scaler_bit_width = 16, + .num_channels = 8, + .trim_locate = &ast2600_adc0_trim, +}; + +static const struct aspeed_adc_model_data ast2600_adc1_model_data = { + .model_name = "ast2600-adc1", + .min_sampling_rate = 10000, + .max_sampling_rate = 500000, + .wait_init_sequence = true, + .bat_sense_sup = true, + .scaler_bit_width = 16, + .num_channels = 8, + .trim_locate = &ast2600_adc1_trim, +}; + +static const struct aspeed_adc_model_data ast2700_adc0_model_data = { + .model_name = "ast2700-adc0", + .min_sampling_rate = 10000, + .max_sampling_rate = 500000, + .wait_init_sequence = true, + .bat_sense_sup = true, + .scaler_bit_width = 16, + .num_channels = 8, + .trim_locate = &ast2700_adc0_trim, +}; + +static const struct aspeed_adc_model_data ast2700_adc1_model_data = { + .model_name = "ast2700-adc1", + .min_sampling_rate = 10000, + .max_sampling_rate = 500000, + .wait_init_sequence = true, + .bat_sense_sup = true, + .scaler_bit_width = 16, + .num_channels = 8, + .trim_locate = &ast2700_adc1_trim, +}; + +static const struct of_device_id aspeed_adc_matches[] = { + { .compatible = "aspeed,ast2400-adc", .data = &ast2400_model_data }, + { .compatible = "aspeed,ast2500-adc", .data = &ast2500_model_data }, + { .compatible = "aspeed,ast2600-adc0", .data = &ast2600_adc0_model_data }, + { .compatible = "aspeed,ast2600-adc1", .data = &ast2600_adc1_model_data }, + { .compatible = "aspeed,ast2700-adc0", .data = &ast2700_adc0_model_data }, + { .compatible = "aspeed,ast2700-adc1", .data = &ast2700_adc1_model_data }, + { } +}; +MODULE_DEVICE_TABLE(of, aspeed_adc_matches); + +static struct platform_driver aspeed_adc_driver = { + .probe = aspeed_adc_probe, + .driver = { + .name = KBUILD_MODNAME, + .of_match_table = aspeed_adc_matches, + } +}; + +module_platform_driver(aspeed_adc_driver); + +MODULE_AUTHOR("Rick Altherr "); +MODULE_DESCRIPTION("Aspeed AST2400/2500/2600 ADC Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/iio/adc/at91-sama5d2_adc.c b/drivers/iio/adc/at91-sama5d2_adc.c new file mode 100644 index 000000000..e8a5285bb --- /dev/null +++ b/drivers/iio/adc/at91-sama5d2_adc.c @@ -0,0 +1,2629 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Atmel ADC driver for SAMA5D2 devices and compatible. + * + * Copyright (C) 2015 Atmel, + * 2015 Ludovic Desroches + * 2021 Microchip Technology, Inc. and its subsidiaries + * 2021 Eugen Hristev + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +struct at91_adc_reg_layout { +/* Control Register */ + u16 CR; +/* Software Reset */ +#define AT91_SAMA5D2_CR_SWRST BIT(0) +/* Start Conversion */ +#define AT91_SAMA5D2_CR_START BIT(1) +/* Touchscreen Calibration */ +#define AT91_SAMA5D2_CR_TSCALIB BIT(2) +/* Comparison Restart */ +#define AT91_SAMA5D2_CR_CMPRST BIT(4) + +/* Mode Register */ + u16 MR; +/* Trigger Selection */ +#define AT91_SAMA5D2_MR_TRGSEL(v) ((v) << 1) +/* ADTRG */ +#define AT91_SAMA5D2_MR_TRGSEL_TRIG0 0 +/* TIOA0 */ +#define AT91_SAMA5D2_MR_TRGSEL_TRIG1 1 +/* TIOA1 */ +#define AT91_SAMA5D2_MR_TRGSEL_TRIG2 2 +/* TIOA2 */ +#define AT91_SAMA5D2_MR_TRGSEL_TRIG3 3 +/* PWM event line 0 */ +#define AT91_SAMA5D2_MR_TRGSEL_TRIG4 4 +/* PWM event line 1 */ +#define AT91_SAMA5D2_MR_TRGSEL_TRIG5 5 +/* TIOA3 */ +#define AT91_SAMA5D2_MR_TRGSEL_TRIG6 6 +/* RTCOUT0 */ +#define AT91_SAMA5D2_MR_TRGSEL_TRIG7 7 +/* Sleep Mode */ +#define AT91_SAMA5D2_MR_SLEEP BIT(5) +/* Fast Wake Up */ +#define AT91_SAMA5D2_MR_FWUP BIT(6) +/* Prescaler Rate Selection */ +#define AT91_SAMA5D2_MR_PRESCAL(v) ((v) << AT91_SAMA5D2_MR_PRESCAL_OFFSET) +#define AT91_SAMA5D2_MR_PRESCAL_OFFSET 8 +#define AT91_SAMA5D2_MR_PRESCAL_MAX 0xff +#define AT91_SAMA5D2_MR_PRESCAL_MASK GENMASK(15, 8) +/* Startup Time */ +#define AT91_SAMA5D2_MR_STARTUP(v) ((v) << 16) +#define AT91_SAMA5D2_MR_STARTUP_MASK GENMASK(19, 16) +/* Minimum startup time for temperature sensor */ +#define AT91_SAMA5D2_MR_STARTUP_TS_MIN (50) +/* Analog Change */ +#define AT91_SAMA5D2_MR_ANACH BIT(23) +/* Tracking Time */ +#define AT91_SAMA5D2_MR_TRACKTIM(v) ((v) << 24) +#define AT91_SAMA5D2_MR_TRACKTIM_TS 6 +#define AT91_SAMA5D2_MR_TRACKTIM_MAX 0xf +/* Transfer Time */ +#define AT91_SAMA5D2_MR_TRANSFER(v) ((v) << 28) +#define AT91_SAMA5D2_MR_TRANSFER_MAX 0x3 +/* Use Sequence Enable */ +#define AT91_SAMA5D2_MR_USEQ BIT(31) + +/* Channel Sequence Register 1 */ + u16 SEQR1; +/* Channel Sequence Register 2 */ + u16 SEQR2; +/* Channel Enable Register */ + u16 CHER; +/* Channel Disable Register */ + u16 CHDR; +/* Channel Status Register */ + u16 CHSR; +/* Last Converted Data Register */ + u16 LCDR; +/* Interrupt Enable Register */ + u16 IER; +/* Interrupt Enable Register - TS X measurement ready */ +#define AT91_SAMA5D2_IER_XRDY BIT(20) +/* Interrupt Enable Register - TS Y measurement ready */ +#define AT91_SAMA5D2_IER_YRDY BIT(21) +/* Interrupt Enable Register - TS pressure measurement ready */ +#define AT91_SAMA5D2_IER_PRDY BIT(22) +/* Interrupt Enable Register - Data ready */ +#define AT91_SAMA5D2_IER_DRDY BIT(24) +/* Interrupt Enable Register - general overrun error */ +#define AT91_SAMA5D2_IER_GOVRE BIT(25) +/* Interrupt Enable Register - Pen detect */ +#define AT91_SAMA5D2_IER_PEN BIT(29) +/* Interrupt Enable Register - No pen detect */ +#define AT91_SAMA5D2_IER_NOPEN BIT(30) + +/* Interrupt Disable Register */ + u16 IDR; +/* Interrupt Mask Register */ + u16 IMR; +/* Interrupt Status Register */ + u16 ISR; +/* End of Conversion Interrupt Enable Register */ + u16 EOC_IER; +/* End of Conversion Interrupt Disable Register */ + u16 EOC_IDR; +/* End of Conversion Interrupt Mask Register */ + u16 EOC_IMR; +/* End of Conversion Interrupt Status Register */ + u16 EOC_ISR; +/* Interrupt Status Register - Pen touching sense status */ +#define AT91_SAMA5D2_ISR_PENS BIT(31) +/* Last Channel Trigger Mode Register */ + u16 LCTMR; +/* Last Channel Compare Window Register */ + u16 LCCWR; +/* Overrun Status Register */ + u16 OVER; +/* Extended Mode Register */ + u16 EMR; +/* Extended Mode Register - Oversampling rate */ +#define AT91_SAMA5D2_EMR_OSR(V, M) (((V) << 16) & (M)) +#define AT91_SAMA5D2_EMR_OSR_1SAMPLES 0 +#define AT91_SAMA5D2_EMR_OSR_4SAMPLES 1 +#define AT91_SAMA5D2_EMR_OSR_16SAMPLES 2 +#define AT91_SAMA5D2_EMR_OSR_64SAMPLES 3 +#define AT91_SAMA5D2_EMR_OSR_256SAMPLES 4 + +/* Extended Mode Register - TRACKX */ +#define AT91_SAMA5D2_TRACKX_MASK GENMASK(23, 22) +#define AT91_SAMA5D2_TRACKX(x) (((x) << 22) & \ + AT91_SAMA5D2_TRACKX_MASK) +/* TRACKX for temperature sensor. */ +#define AT91_SAMA5D2_TRACKX_TS (1) + +/* Extended Mode Register - Averaging on single trigger event */ +#define AT91_SAMA5D2_EMR_ASTE(V) ((V) << 20) + +/* Compare Window Register */ + u16 CWR; +/* Channel Gain Register */ + u16 CGR; +/* Channel Offset Register */ + u16 COR; +/* Channel Offset Register differential offset - constant, not a register */ + u16 COR_diff_offset; +/* Analog Control Register */ + u16 ACR; +/* Analog Control Register - Pen detect sensitivity mask */ +#define AT91_SAMA5D2_ACR_PENDETSENS_MASK GENMASK(1, 0) +/* Analog Control Register - Source last channel */ +#define AT91_SAMA5D2_ACR_SRCLCH BIT(16) + +/* Touchscreen Mode Register */ + u16 TSMR; +/* Touchscreen Mode Register - No touch mode */ +#define AT91_SAMA5D2_TSMR_TSMODE_NONE 0 +/* Touchscreen Mode Register - 4 wire screen, no pressure measurement */ +#define AT91_SAMA5D2_TSMR_TSMODE_4WIRE_NO_PRESS 1 +/* Touchscreen Mode Register - 4 wire screen, pressure measurement */ +#define AT91_SAMA5D2_TSMR_TSMODE_4WIRE_PRESS 2 +/* Touchscreen Mode Register - 5 wire screen */ +#define AT91_SAMA5D2_TSMR_TSMODE_5WIRE 3 +/* Touchscreen Mode Register - Average samples mask */ +#define AT91_SAMA5D2_TSMR_TSAV_MASK GENMASK(5, 4) +/* Touchscreen Mode Register - Average samples */ +#define AT91_SAMA5D2_TSMR_TSAV(x) ((x) << 4) +/* Touchscreen Mode Register - Touch/trigger frequency ratio mask */ +#define AT91_SAMA5D2_TSMR_TSFREQ_MASK GENMASK(11, 8) +/* Touchscreen Mode Register - Touch/trigger frequency ratio */ +#define AT91_SAMA5D2_TSMR_TSFREQ(x) ((x) << 8) +/* Touchscreen Mode Register - Pen Debounce Time mask */ +#define AT91_SAMA5D2_TSMR_PENDBC_MASK GENMASK(31, 28) +/* Touchscreen Mode Register - Pen Debounce Time */ +#define AT91_SAMA5D2_TSMR_PENDBC(x) ((x) << 28) +/* Touchscreen Mode Register - No DMA for touch measurements */ +#define AT91_SAMA5D2_TSMR_NOTSDMA BIT(22) +/* Touchscreen Mode Register - Disable pen detection */ +#define AT91_SAMA5D2_TSMR_PENDET_DIS (0 << 24) +/* Touchscreen Mode Register - Enable pen detection */ +#define AT91_SAMA5D2_TSMR_PENDET_ENA BIT(24) + +/* Touchscreen X Position Register */ + u16 XPOSR; +/* Touchscreen Y Position Register */ + u16 YPOSR; +/* Touchscreen Pressure Register */ + u16 PRESSR; +/* Trigger Register */ + u16 TRGR; +/* Mask for TRGMOD field of TRGR register */ +#define AT91_SAMA5D2_TRGR_TRGMOD_MASK GENMASK(2, 0) +/* No trigger, only software trigger can start conversions */ +#define AT91_SAMA5D2_TRGR_TRGMOD_NO_TRIGGER 0 +/* Trigger Mode external trigger rising edge */ +#define AT91_SAMA5D2_TRGR_TRGMOD_EXT_TRIG_RISE 1 +/* Trigger Mode external trigger falling edge */ +#define AT91_SAMA5D2_TRGR_TRGMOD_EXT_TRIG_FALL 2 +/* Trigger Mode external trigger any edge */ +#define AT91_SAMA5D2_TRGR_TRGMOD_EXT_TRIG_ANY 3 +/* Trigger Mode internal periodic */ +#define AT91_SAMA5D2_TRGR_TRGMOD_PERIODIC 5 +/* Trigger Mode - trigger period mask */ +#define AT91_SAMA5D2_TRGR_TRGPER_MASK GENMASK(31, 16) +/* Trigger Mode - trigger period */ +#define AT91_SAMA5D2_TRGR_TRGPER(x) ((x) << 16) + +/* Correction Select Register */ + u16 COSR; +/* Correction Value Register */ + u16 CVR; +/* Channel Error Correction Register */ + u16 CECR; +/* Write Protection Mode Register */ + u16 WPMR; +/* Write Protection Status Register */ + u16 WPSR; +/* Version Register */ + u16 VERSION; +/* Temperature Sensor Mode Register */ + u16 TEMPMR; +/* Temperature Sensor Mode - Temperature sensor on */ +#define AT91_SAMA5D2_TEMPMR_TEMPON BIT(0) +}; + +static const struct at91_adc_reg_layout sama5d2_layout = { + .CR = 0x00, + .MR = 0x04, + .SEQR1 = 0x08, + .SEQR2 = 0x0c, + .CHER = 0x10, + .CHDR = 0x14, + .CHSR = 0x18, + .LCDR = 0x20, + .IER = 0x24, + .IDR = 0x28, + .IMR = 0x2c, + .ISR = 0x30, + .LCTMR = 0x34, + .LCCWR = 0x38, + .OVER = 0x3c, + .EMR = 0x40, + .CWR = 0x44, + .CGR = 0x48, + .COR = 0x4c, + .COR_diff_offset = 16, + .ACR = 0x94, + .TSMR = 0xb0, + .XPOSR = 0xb4, + .YPOSR = 0xb8, + .PRESSR = 0xbc, + .TRGR = 0xc0, + .COSR = 0xd0, + .CVR = 0xd4, + .CECR = 0xd8, + .WPMR = 0xe4, + .WPSR = 0xe8, + .VERSION = 0xfc, +}; + +static const struct at91_adc_reg_layout sama7g5_layout = { + .CR = 0x00, + .MR = 0x04, + .SEQR1 = 0x08, + .SEQR2 = 0x0c, + .CHER = 0x10, + .CHDR = 0x14, + .CHSR = 0x18, + .LCDR = 0x20, + .IER = 0x24, + .IDR = 0x28, + .IMR = 0x2c, + .ISR = 0x30, + .EOC_IER = 0x34, + .EOC_IDR = 0x38, + .EOC_IMR = 0x3c, + .EOC_ISR = 0x40, + .TEMPMR = 0x44, + .OVER = 0x4c, + .EMR = 0x50, + .CWR = 0x54, + .COR = 0x5c, + .COR_diff_offset = 0, + .ACR = 0xe0, + .TRGR = 0x100, + .COSR = 0x104, + .CVR = 0x108, + .CECR = 0x10c, + .WPMR = 0x118, + .WPSR = 0x11c, + .VERSION = 0x130, +}; + +#define AT91_SAMA5D2_TOUCH_SAMPLE_PERIOD_US 2000 /* 2ms */ +#define AT91_SAMA5D2_TOUCH_PEN_DETECT_DEBOUNCE_US 200 + +#define AT91_SAMA5D2_XYZ_MASK GENMASK(11, 0) + +#define AT91_SAMA5D2_MAX_POS_BITS 12 + +#define AT91_HWFIFO_MAX_SIZE_STR "128" +#define AT91_HWFIFO_MAX_SIZE 128 + +#define AT91_SAMA_CHAN_SINGLE(index, num, addr, rbits) \ + { \ + .type = IIO_VOLTAGE, \ + .channel = num, \ + .address = addr, \ + .scan_index = index, \ + .scan_type = { \ + .sign = 'u', \ + .realbits = rbits, \ + .storagebits = 16, \ + }, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ + .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ)|\ + BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \ + .info_mask_shared_by_all_available = \ + BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \ + .datasheet_name = "CH"#num, \ + .indexed = 1, \ + } + +#define AT91_SAMA5D2_CHAN_SINGLE(index, num, addr) \ + AT91_SAMA_CHAN_SINGLE(index, num, addr, 14) + +#define AT91_SAMA7G5_CHAN_SINGLE(index, num, addr) \ + AT91_SAMA_CHAN_SINGLE(index, num, addr, 16) + +#define AT91_SAMA_CHAN_DIFF(index, num, num2, addr, rbits) \ + { \ + .type = IIO_VOLTAGE, \ + .differential = 1, \ + .channel = num, \ + .channel2 = num2, \ + .address = addr, \ + .scan_index = index, \ + .scan_type = { \ + .sign = 's', \ + .realbits = rbits, \ + .storagebits = 16, \ + }, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ + .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ)|\ + BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \ + .info_mask_shared_by_all_available = \ + BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \ + .datasheet_name = "CH"#num"-CH"#num2, \ + .indexed = 1, \ + } + +#define AT91_SAMA5D2_CHAN_DIFF(index, num, num2, addr) \ + AT91_SAMA_CHAN_DIFF(index, num, num2, addr, 14) + +#define AT91_SAMA7G5_CHAN_DIFF(index, num, num2, addr) \ + AT91_SAMA_CHAN_DIFF(index, num, num2, addr, 16) + +#define AT91_SAMA5D2_CHAN_TOUCH(num, name, mod) \ + { \ + .type = IIO_POSITIONRELATIVE, \ + .modified = 1, \ + .channel = num, \ + .channel2 = mod, \ + .scan_index = num, \ + .scan_type = { \ + .sign = 'u', \ + .realbits = 12, \ + .storagebits = 16, \ + }, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ)|\ + BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \ + .info_mask_shared_by_all_available = \ + BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \ + .datasheet_name = name, \ + } +#define AT91_SAMA5D2_CHAN_PRESSURE(num, name) \ + { \ + .type = IIO_PRESSURE, \ + .channel = num, \ + .scan_index = num, \ + .scan_type = { \ + .sign = 'u', \ + .realbits = 12, \ + .storagebits = 16, \ + }, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ)|\ + BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \ + .info_mask_shared_by_all_available = \ + BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \ + .datasheet_name = name, \ + } + +#define AT91_SAMA5D2_CHAN_TEMP(num, name, addr) \ + { \ + .type = IIO_TEMP, \ + .channel = num, \ + .address = addr, \ + .scan_index = num, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED), \ + .info_mask_shared_by_all = \ + BIT(IIO_CHAN_INFO_PROCESSED) | \ + BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \ + .info_mask_shared_by_all_available = \ + BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \ + .datasheet_name = name, \ + } + +#define at91_adc_readl(st, reg) \ + readl_relaxed((st)->base + (st)->soc_info.platform->layout->reg) +#define at91_adc_read_chan(st, reg) \ + readl_relaxed((st)->base + reg) +#define at91_adc_writel(st, reg, val) \ + writel_relaxed(val, (st)->base + (st)->soc_info.platform->layout->reg) + +/** + * struct at91_adc_platform - at91-sama5d2 platform information struct + * @layout: pointer to the reg layout struct + * @adc_channels: pointer to an array of channels for registering in + * the iio subsystem + * @nr_channels: number of physical channels available + * @touch_chan_x: index of the touchscreen X channel + * @touch_chan_y: index of the touchscreen Y channel + * @touch_chan_p: index of the touchscreen P channel + * @max_channels: number of total channels + * @max_index: highest channel index (highest index may be higher + * than the total channel number) + * @hw_trig_cnt: number of possible hardware triggers + * @osr_mask: oversampling ratio bitmask on EMR register + * @oversampling_avail: available oversampling values + * @oversampling_avail_no: number of available oversampling values + * @chan_realbits: realbits for registered channels + * @temp_chan: temperature channel index + * @temp_sensor: temperature sensor supported + */ +struct at91_adc_platform { + const struct at91_adc_reg_layout *layout; + const struct iio_chan_spec (*adc_channels)[]; + unsigned int nr_channels; + unsigned int touch_chan_x; + unsigned int touch_chan_y; + unsigned int touch_chan_p; + unsigned int max_channels; + unsigned int max_index; + unsigned int hw_trig_cnt; + unsigned int osr_mask; + unsigned int oversampling_avail[5]; + unsigned int oversampling_avail_no; + unsigned int chan_realbits; + unsigned int temp_chan; + bool temp_sensor; +}; + +/** + * struct at91_adc_temp_sensor_clb - at91-sama5d2 temperature sensor + * calibration data structure + * @p1: P1 calibration temperature + * @p4: P4 calibration voltage + * @p6: P6 calibration voltage + */ +struct at91_adc_temp_sensor_clb { + u32 p1; + u32 p4; + u32 p6; +}; + +/** + * enum at91_adc_ts_clb_idx - calibration indexes in NVMEM buffer + * @AT91_ADC_TS_CLB_IDX_P1: index for P1 + * @AT91_ADC_TS_CLB_IDX_P4: index for P4 + * @AT91_ADC_TS_CLB_IDX_P6: index for P6 + * @AT91_ADC_TS_CLB_IDX_MAX: max index for temperature calibration packet in OTP + */ +enum at91_adc_ts_clb_idx { + AT91_ADC_TS_CLB_IDX_P1 = 2, + AT91_ADC_TS_CLB_IDX_P4 = 5, + AT91_ADC_TS_CLB_IDX_P6 = 7, + AT91_ADC_TS_CLB_IDX_MAX = 19, +}; + +/* Temperature sensor calibration - Vtemp voltage sensitivity to temperature. */ +#define AT91_ADC_TS_VTEMP_DT (2080U) + +/** + * struct at91_adc_soc_info - at91-sama5d2 soc information struct + * @startup_time: device startup time + * @min_sample_rate: minimum sample rate in Hz + * @max_sample_rate: maximum sample rate in Hz + * @platform: pointer to the platform structure + * @temp_sensor_clb: temperature sensor calibration data structure + */ +struct at91_adc_soc_info { + unsigned startup_time; + unsigned min_sample_rate; + unsigned max_sample_rate; + const struct at91_adc_platform *platform; + struct at91_adc_temp_sensor_clb temp_sensor_clb; +}; + +struct at91_adc_trigger { + char *name; + unsigned int trgmod_value; + unsigned int edge_type; + bool hw_trig; +}; + +/** + * struct at91_adc_dma - at91-sama5d2 dma information struct + * @dma_chan: the dma channel acquired + * @rx_buf: dma coherent allocated area + * @rx_dma_buf: dma handler for the buffer + * @phys_addr: physical address of the ADC base register + * @buf_idx: index inside the dma buffer where reading was last done + * @rx_buf_sz: size of buffer used by DMA operation + * @watermark: number of conversions to copy before DMA triggers irq + * @dma_ts: hold the start timestamp of dma operation + */ +struct at91_adc_dma { + struct dma_chan *dma_chan; + u8 *rx_buf; + dma_addr_t rx_dma_buf; + phys_addr_t phys_addr; + int buf_idx; + int rx_buf_sz; + int watermark; + s64 dma_ts; +}; + +/** + * struct at91_adc_touch - at91-sama5d2 touchscreen information struct + * @sample_period_val: the value for periodic trigger interval + * @touching: is the pen touching the screen or not + * @x_pos: temporary placeholder for pressure computation + * @channels_bitmask: bitmask with the touchscreen channels enabled + * @workq: workqueue for buffer data pushing + */ +struct at91_adc_touch { + u16 sample_period_val; + bool touching; + u16 x_pos; + unsigned long channels_bitmask; + struct work_struct workq; +}; + +/** + * struct at91_adc_temp - at91-sama5d2 temperature information structure + * @sample_period_val: sample period value + * @saved_sample_rate: saved sample rate + * @saved_oversampling: saved oversampling + */ +struct at91_adc_temp { + u16 sample_period_val; + u16 saved_sample_rate; + u16 saved_oversampling; +}; + +struct at91_adc_state { + void __iomem *base; + int irq; + struct clk *per_clk; + struct regulator *reg; + struct regulator *vref; + int vref_uv; + unsigned int current_sample_rate; + struct iio_trigger *trig; + const struct at91_adc_trigger *selected_trig; + const struct iio_chan_spec *chan; + bool conversion_done; + u32 conversion_value; + unsigned int oversampling_ratio; + struct at91_adc_soc_info soc_info; + wait_queue_head_t wq_data_available; + struct at91_adc_dma dma_st; + struct at91_adc_touch touch_st; + struct at91_adc_temp temp_st; + struct iio_dev *indio_dev; + struct device *dev; + /* We assume 32 channels for now, has to be increased if needed. */ + IIO_DECLARE_BUFFER_WITH_TS(u16, buffer, 32); + /* + * lock to prevent concurrent 'single conversion' requests through + * sysfs. + */ + struct mutex lock; +}; + +static const struct at91_adc_trigger at91_adc_trigger_list[] = { + { + .name = "external_rising", + .trgmod_value = AT91_SAMA5D2_TRGR_TRGMOD_EXT_TRIG_RISE, + .edge_type = IRQ_TYPE_EDGE_RISING, + .hw_trig = true, + }, + { + .name = "external_falling", + .trgmod_value = AT91_SAMA5D2_TRGR_TRGMOD_EXT_TRIG_FALL, + .edge_type = IRQ_TYPE_EDGE_FALLING, + .hw_trig = true, + }, + { + .name = "external_any", + .trgmod_value = AT91_SAMA5D2_TRGR_TRGMOD_EXT_TRIG_ANY, + .edge_type = IRQ_TYPE_EDGE_BOTH, + .hw_trig = true, + }, + { + .name = "software", + .trgmod_value = AT91_SAMA5D2_TRGR_TRGMOD_NO_TRIGGER, + .edge_type = IRQ_TYPE_NONE, + .hw_trig = false, + }, +}; + +static const struct iio_chan_spec at91_sama5d2_adc_channels[] = { + AT91_SAMA5D2_CHAN_SINGLE(0, 0, 0x50), + AT91_SAMA5D2_CHAN_SINGLE(1, 1, 0x54), + AT91_SAMA5D2_CHAN_SINGLE(2, 2, 0x58), + AT91_SAMA5D2_CHAN_SINGLE(3, 3, 0x5c), + AT91_SAMA5D2_CHAN_SINGLE(4, 4, 0x60), + AT91_SAMA5D2_CHAN_SINGLE(5, 5, 0x64), + AT91_SAMA5D2_CHAN_SINGLE(6, 6, 0x68), + AT91_SAMA5D2_CHAN_SINGLE(7, 7, 0x6c), + AT91_SAMA5D2_CHAN_SINGLE(8, 8, 0x70), + AT91_SAMA5D2_CHAN_SINGLE(9, 9, 0x74), + AT91_SAMA5D2_CHAN_SINGLE(10, 10, 0x78), + AT91_SAMA5D2_CHAN_SINGLE(11, 11, 0x7c), + /* original ABI has the differential channels with a gap in between */ + AT91_SAMA5D2_CHAN_DIFF(12, 0, 1, 0x50), + AT91_SAMA5D2_CHAN_DIFF(14, 2, 3, 0x58), + AT91_SAMA5D2_CHAN_DIFF(16, 4, 5, 0x60), + AT91_SAMA5D2_CHAN_DIFF(18, 6, 7, 0x68), + AT91_SAMA5D2_CHAN_DIFF(20, 8, 9, 0x70), + AT91_SAMA5D2_CHAN_DIFF(22, 10, 11, 0x78), + IIO_CHAN_SOFT_TIMESTAMP(23), + AT91_SAMA5D2_CHAN_TOUCH(24, "x", IIO_MOD_X), + AT91_SAMA5D2_CHAN_TOUCH(25, "y", IIO_MOD_Y), + AT91_SAMA5D2_CHAN_PRESSURE(26, "pressure"), +}; + +static const struct iio_chan_spec at91_sama7g5_adc_channels[] = { + AT91_SAMA7G5_CHAN_SINGLE(0, 0, 0x60), + AT91_SAMA7G5_CHAN_SINGLE(1, 1, 0x64), + AT91_SAMA7G5_CHAN_SINGLE(2, 2, 0x68), + AT91_SAMA7G5_CHAN_SINGLE(3, 3, 0x6c), + AT91_SAMA7G5_CHAN_SINGLE(4, 4, 0x70), + AT91_SAMA7G5_CHAN_SINGLE(5, 5, 0x74), + AT91_SAMA7G5_CHAN_SINGLE(6, 6, 0x78), + AT91_SAMA7G5_CHAN_SINGLE(7, 7, 0x7c), + AT91_SAMA7G5_CHAN_SINGLE(8, 8, 0x80), + AT91_SAMA7G5_CHAN_SINGLE(9, 9, 0x84), + AT91_SAMA7G5_CHAN_SINGLE(10, 10, 0x88), + AT91_SAMA7G5_CHAN_SINGLE(11, 11, 0x8c), + AT91_SAMA7G5_CHAN_SINGLE(12, 12, 0x90), + AT91_SAMA7G5_CHAN_SINGLE(13, 13, 0x94), + AT91_SAMA7G5_CHAN_SINGLE(14, 14, 0x98), + AT91_SAMA7G5_CHAN_SINGLE(15, 15, 0x9c), + AT91_SAMA7G5_CHAN_DIFF(16, 0, 1, 0x60), + AT91_SAMA7G5_CHAN_DIFF(17, 2, 3, 0x68), + AT91_SAMA7G5_CHAN_DIFF(18, 4, 5, 0x70), + AT91_SAMA7G5_CHAN_DIFF(19, 6, 7, 0x78), + AT91_SAMA7G5_CHAN_DIFF(20, 8, 9, 0x80), + AT91_SAMA7G5_CHAN_DIFF(21, 10, 11, 0x88), + AT91_SAMA7G5_CHAN_DIFF(22, 12, 13, 0x90), + AT91_SAMA7G5_CHAN_DIFF(23, 14, 15, 0x98), + IIO_CHAN_SOFT_TIMESTAMP(24), + AT91_SAMA5D2_CHAN_TEMP(AT91_SAMA7G5_ADC_TEMP_CHANNEL, "temp", 0xdc), +}; + +static const struct at91_adc_platform sama5d2_platform = { + .layout = &sama5d2_layout, + .adc_channels = &at91_sama5d2_adc_channels, +#define AT91_SAMA5D2_SINGLE_CHAN_CNT 12 +#define AT91_SAMA5D2_DIFF_CHAN_CNT 6 + .nr_channels = AT91_SAMA5D2_SINGLE_CHAN_CNT + + AT91_SAMA5D2_DIFF_CHAN_CNT, +#define AT91_SAMA5D2_TOUCH_X_CHAN_IDX (AT91_SAMA5D2_SINGLE_CHAN_CNT + \ + AT91_SAMA5D2_DIFF_CHAN_CNT * 2) + .touch_chan_x = AT91_SAMA5D2_TOUCH_X_CHAN_IDX, +#define AT91_SAMA5D2_TOUCH_Y_CHAN_IDX (AT91_SAMA5D2_TOUCH_X_CHAN_IDX + 1) + .touch_chan_y = AT91_SAMA5D2_TOUCH_Y_CHAN_IDX, +#define AT91_SAMA5D2_TOUCH_P_CHAN_IDX (AT91_SAMA5D2_TOUCH_Y_CHAN_IDX + 1) + .touch_chan_p = AT91_SAMA5D2_TOUCH_P_CHAN_IDX, +#define AT91_SAMA5D2_MAX_CHAN_IDX AT91_SAMA5D2_TOUCH_P_CHAN_IDX + .max_channels = ARRAY_SIZE(at91_sama5d2_adc_channels), + .max_index = AT91_SAMA5D2_MAX_CHAN_IDX, +#define AT91_SAMA5D2_HW_TRIG_CNT 3 + .hw_trig_cnt = AT91_SAMA5D2_HW_TRIG_CNT, + .osr_mask = GENMASK(17, 16), + .oversampling_avail = { 1, 4, 16, }, + .oversampling_avail_no = 3, + .chan_realbits = 14, +}; + +static const struct at91_adc_platform sama7g5_platform = { + .layout = &sama7g5_layout, + .adc_channels = &at91_sama7g5_adc_channels, +#define AT91_SAMA7G5_SINGLE_CHAN_CNT 16 +#define AT91_SAMA7G5_DIFF_CHAN_CNT 8 +#define AT91_SAMA7G5_TEMP_CHAN_CNT 1 + .nr_channels = AT91_SAMA7G5_SINGLE_CHAN_CNT + + AT91_SAMA7G5_DIFF_CHAN_CNT + + AT91_SAMA7G5_TEMP_CHAN_CNT, +#define AT91_SAMA7G5_MAX_CHAN_IDX (AT91_SAMA7G5_SINGLE_CHAN_CNT + \ + AT91_SAMA7G5_DIFF_CHAN_CNT + \ + AT91_SAMA7G5_TEMP_CHAN_CNT) + .max_channels = ARRAY_SIZE(at91_sama7g5_adc_channels), + .max_index = AT91_SAMA7G5_MAX_CHAN_IDX, +#define AT91_SAMA7G5_HW_TRIG_CNT 3 + .hw_trig_cnt = AT91_SAMA7G5_HW_TRIG_CNT, + .osr_mask = GENMASK(18, 16), + .oversampling_avail = { 1, 4, 16, 64, 256, }, + .oversampling_avail_no = 5, + .chan_realbits = 16, + .temp_sensor = true, + .temp_chan = AT91_SAMA7G5_ADC_TEMP_CHANNEL, +}; + +static int at91_adc_chan_xlate(struct iio_dev *indio_dev, int chan) +{ + int i; + + for (i = 0; i < indio_dev->num_channels; i++) { + if (indio_dev->channels[i].scan_index == chan) + return i; + } + return -EINVAL; +} + +static inline struct iio_chan_spec const * +at91_adc_chan_get(struct iio_dev *indio_dev, int chan) +{ + int index = at91_adc_chan_xlate(indio_dev, chan); + + if (index < 0) + return NULL; + return indio_dev->channels + index; +} + +static inline int at91_adc_fwnode_xlate(struct iio_dev *indio_dev, + const struct fwnode_reference_args *iiospec) +{ + return at91_adc_chan_xlate(indio_dev, iiospec->args[0]); +} + +static unsigned int at91_adc_active_scan_mask_to_reg(struct iio_dev *indio_dev) +{ + u32 mask = 0; + u8 bit; + struct at91_adc_state *st = iio_priv(indio_dev); + + for_each_set_bit(bit, indio_dev->active_scan_mask, + indio_dev->num_channels) { + struct iio_chan_spec const *chan = + at91_adc_chan_get(indio_dev, bit); + mask |= BIT(chan->channel); + } + + return mask & GENMASK(st->soc_info.platform->nr_channels, 0); +} + +static void at91_adc_cor(struct at91_adc_state *st, + struct iio_chan_spec const *chan) +{ + u32 cor, cur_cor; + + cor = BIT(chan->channel) | BIT(chan->channel2); + + cur_cor = at91_adc_readl(st, COR); + cor <<= st->soc_info.platform->layout->COR_diff_offset; + if (chan->differential) + at91_adc_writel(st, COR, cur_cor | cor); + else + at91_adc_writel(st, COR, cur_cor & ~cor); +} + +static void at91_adc_irq_status(struct at91_adc_state *st, u32 *status, + u32 *eoc) +{ + *status = at91_adc_readl(st, ISR); + if (st->soc_info.platform->layout->EOC_ISR) + *eoc = at91_adc_readl(st, EOC_ISR); + else + *eoc = *status; +} + +static void at91_adc_irq_mask(struct at91_adc_state *st, u32 *status, u32 *eoc) +{ + *status = at91_adc_readl(st, IMR); + if (st->soc_info.platform->layout->EOC_IMR) + *eoc = at91_adc_readl(st, EOC_IMR); + else + *eoc = *status; +} + +static void at91_adc_eoc_dis(struct at91_adc_state *st, unsigned int channel) +{ + /* + * On some products having the EOC bits in a separate register, + * errata recommends not writing this register (EOC_IDR). + * On products having the EOC bits in the IDR register, it's fine to write it. + */ + if (!st->soc_info.platform->layout->EOC_IDR) + at91_adc_writel(st, IDR, BIT(channel)); +} + +static void at91_adc_eoc_ena(struct at91_adc_state *st, unsigned int channel) +{ + if (!st->soc_info.platform->layout->EOC_IDR) + at91_adc_writel(st, IER, BIT(channel)); + else + at91_adc_writel(st, EOC_IER, BIT(channel)); +} + +static int at91_adc_config_emr(struct at91_adc_state *st, + u32 oversampling_ratio, u32 trackx) +{ + /* configure the extended mode register */ + unsigned int emr, osr; + unsigned int osr_mask = st->soc_info.platform->osr_mask; + int i, ret; + + /* Check against supported oversampling values. */ + for (i = 0; i < st->soc_info.platform->oversampling_avail_no; i++) { + if (oversampling_ratio == st->soc_info.platform->oversampling_avail[i]) + break; + } + if (i == st->soc_info.platform->oversampling_avail_no) + return -EINVAL; + + /* select oversampling ratio from configuration */ + switch (oversampling_ratio) { + case 1: + osr = AT91_SAMA5D2_EMR_OSR(AT91_SAMA5D2_EMR_OSR_1SAMPLES, + osr_mask); + break; + case 4: + osr = AT91_SAMA5D2_EMR_OSR(AT91_SAMA5D2_EMR_OSR_4SAMPLES, + osr_mask); + break; + case 16: + osr = AT91_SAMA5D2_EMR_OSR(AT91_SAMA5D2_EMR_OSR_16SAMPLES, + osr_mask); + break; + case 64: + osr = AT91_SAMA5D2_EMR_OSR(AT91_SAMA5D2_EMR_OSR_64SAMPLES, + osr_mask); + break; + case 256: + osr = AT91_SAMA5D2_EMR_OSR(AT91_SAMA5D2_EMR_OSR_256SAMPLES, + osr_mask); + break; + } + + ret = pm_runtime_resume_and_get(st->dev); + if (ret < 0) + return ret; + + emr = at91_adc_readl(st, EMR); + /* select oversampling per single trigger event */ + emr |= AT91_SAMA5D2_EMR_ASTE(1); + /* delete leftover content if it's the case */ + emr &= ~(osr_mask | AT91_SAMA5D2_TRACKX_MASK); + /* Update osr and trackx. */ + emr |= osr | AT91_SAMA5D2_TRACKX(trackx); + at91_adc_writel(st, EMR, emr); + + pm_runtime_put_autosuspend(st->dev); + + st->oversampling_ratio = oversampling_ratio; + + return 0; +} + +static int at91_adc_adjust_val_osr(struct at91_adc_state *st, int *val) +{ + int nbits, diff; + + if (st->oversampling_ratio == 1) + nbits = 12; + else if (st->oversampling_ratio == 4) + nbits = 13; + else if (st->oversampling_ratio == 16) + nbits = 14; + else if (st->oversampling_ratio == 64) + nbits = 15; + else if (st->oversampling_ratio == 256) + nbits = 16; + else + /* Should not happen. */ + return -EINVAL; + + /* + * We have nbits of real data and channel is registered as + * st->soc_info.platform->chan_realbits, so shift left diff bits. + */ + diff = st->soc_info.platform->chan_realbits - nbits; + *val <<= diff; + + return IIO_VAL_INT; +} + +static void at91_adc_adjust_val_osr_array(struct at91_adc_state *st, void *buf, + int len) +{ + int i = 0, val; + u16 *buf_u16 = (u16 *) buf; + + /* + * We are converting each two bytes (each sample). + * First convert the byte based array to u16, and convert each sample + * separately. + * Each value is two bytes in an array of chars, so to not shift + * more than we need, save the value separately. + * len is in bytes, so divide by two to get number of samples. + */ + while (i < len / 2) { + val = buf_u16[i]; + at91_adc_adjust_val_osr(st, &val); + buf_u16[i] = val; + i++; + } +} + +static int at91_adc_configure_touch(struct at91_adc_state *st, bool state) +{ + u32 clk_khz = st->current_sample_rate / 1000; + int i = 0, ret; + u16 pendbc; + u32 tsmr, acr; + + if (state) { + ret = pm_runtime_resume_and_get(st->dev); + if (ret < 0) + return ret; + } else { + /* disabling touch IRQs and setting mode to no touch enabled */ + at91_adc_writel(st, IDR, + AT91_SAMA5D2_IER_PEN | AT91_SAMA5D2_IER_NOPEN); + at91_adc_writel(st, TSMR, 0); + + pm_runtime_put_autosuspend(st->dev); + return 0; + } + /* + * debounce time is in microseconds, we need it in milliseconds to + * multiply with kilohertz, so, divide by 1000, but after the multiply. + * round up to make sure pendbc is at least 1 + */ + pendbc = round_up(AT91_SAMA5D2_TOUCH_PEN_DETECT_DEBOUNCE_US * + clk_khz / 1000, 1); + + /* get the required exponent */ + while (pendbc >> i++) + ; + + pendbc = i; + + tsmr = AT91_SAMA5D2_TSMR_TSMODE_4WIRE_PRESS; + + tsmr |= AT91_SAMA5D2_TSMR_TSAV(2) & AT91_SAMA5D2_TSMR_TSAV_MASK; + tsmr |= AT91_SAMA5D2_TSMR_PENDBC(pendbc) & + AT91_SAMA5D2_TSMR_PENDBC_MASK; + tsmr |= AT91_SAMA5D2_TSMR_NOTSDMA; + tsmr |= AT91_SAMA5D2_TSMR_PENDET_ENA; + tsmr |= AT91_SAMA5D2_TSMR_TSFREQ(2) & AT91_SAMA5D2_TSMR_TSFREQ_MASK; + + at91_adc_writel(st, TSMR, tsmr); + + acr = at91_adc_readl(st, ACR); + acr &= ~AT91_SAMA5D2_ACR_PENDETSENS_MASK; + acr |= 0x02 & AT91_SAMA5D2_ACR_PENDETSENS_MASK; + at91_adc_writel(st, ACR, acr); + + /* Sample Period Time = (TRGPER + 1) / ADCClock */ + st->touch_st.sample_period_val = + round_up((AT91_SAMA5D2_TOUCH_SAMPLE_PERIOD_US * + clk_khz / 1000) - 1, 1); + /* enable pen detect IRQ */ + at91_adc_writel(st, IER, AT91_SAMA5D2_IER_PEN); + + return 0; +} + +static u16 at91_adc_touch_pos(struct at91_adc_state *st, int reg) +{ + u32 val = 0; + u32 scale, result, pos; + + /* + * to obtain the actual position we must divide by scale + * and multiply with max, where + * max = 2^AT91_SAMA5D2_MAX_POS_BITS - 1 + */ + /* first half of register is the x or y, second half is the scale */ + if (reg == st->soc_info.platform->layout->XPOSR) + val = at91_adc_readl(st, XPOSR); + else if (reg == st->soc_info.platform->layout->YPOSR) + val = at91_adc_readl(st, YPOSR); + + if (!val) + dev_dbg(&st->indio_dev->dev, "pos is 0\n"); + + pos = val & AT91_SAMA5D2_XYZ_MASK; + result = (pos << AT91_SAMA5D2_MAX_POS_BITS) - pos; + scale = (val >> 16) & AT91_SAMA5D2_XYZ_MASK; + if (scale == 0) { + dev_err(&st->indio_dev->dev, "scale is 0\n"); + return 0; + } + result /= scale; + + return result; +} + +static u16 at91_adc_touch_x_pos(struct at91_adc_state *st) +{ + st->touch_st.x_pos = at91_adc_touch_pos(st, st->soc_info.platform->layout->XPOSR); + return st->touch_st.x_pos; +} + +static u16 at91_adc_touch_y_pos(struct at91_adc_state *st) +{ + return at91_adc_touch_pos(st, st->soc_info.platform->layout->YPOSR); +} + +static u16 at91_adc_touch_pressure(struct at91_adc_state *st) +{ + u32 val; + u32 z1, z2; + u32 pres; + u32 rxp = 1; + u32 factor = 1000; + + /* calculate the pressure */ + val = at91_adc_readl(st, PRESSR); + z1 = val & AT91_SAMA5D2_XYZ_MASK; + z2 = (val >> 16) & AT91_SAMA5D2_XYZ_MASK; + + if (z1 != 0) + pres = rxp * (st->touch_st.x_pos * factor / 1024) * + (z2 * factor / z1 - factor) / + factor; + else + pres = 0xFFFF; /* no pen contact */ + + /* + * The pressure from device grows down, minimum is 0xFFFF, maximum 0x0. + * We compute it this way, but let's return it in the expected way, + * growing from 0 to 0xFFFF. + */ + return 0xFFFF - pres; +} + +static int at91_adc_read_position(struct at91_adc_state *st, int chan, u16 *val) +{ + *val = 0; + if (!st->touch_st.touching) + return -ENODATA; + if (chan == st->soc_info.platform->touch_chan_x) + *val = at91_adc_touch_x_pos(st); + else if (chan == st->soc_info.platform->touch_chan_y) + *val = at91_adc_touch_y_pos(st); + else + return -ENODATA; + + return IIO_VAL_INT; +} + +static int at91_adc_read_pressure(struct at91_adc_state *st, int chan, u16 *val) +{ + *val = 0; + if (!st->touch_st.touching) + return -ENODATA; + if (chan == st->soc_info.platform->touch_chan_p) + *val = at91_adc_touch_pressure(st); + else + return -ENODATA; + + return IIO_VAL_INT; +} + +static void at91_adc_configure_trigger_registers(struct at91_adc_state *st, + bool state) +{ + u32 status = at91_adc_readl(st, TRGR); + + /* clear TRGMOD */ + status &= ~AT91_SAMA5D2_TRGR_TRGMOD_MASK; + + if (state) + status |= st->selected_trig->trgmod_value; + + /* set/unset hw trigger */ + at91_adc_writel(st, TRGR, status); +} + +static int at91_adc_configure_trigger(struct iio_trigger *trig, bool state) +{ + struct iio_dev *indio = iio_trigger_get_drvdata(trig); + struct at91_adc_state *st = iio_priv(indio); + int ret; + + if (state) { + ret = pm_runtime_resume_and_get(st->dev); + if (ret < 0) + return ret; + } + + at91_adc_configure_trigger_registers(st, state); + + if (!state) + pm_runtime_put_autosuspend(st->dev); + + return 0; +} + +static void at91_adc_reenable_trigger(struct iio_trigger *trig) +{ + struct iio_dev *indio = iio_trigger_get_drvdata(trig); + struct at91_adc_state *st = iio_priv(indio); + + /* if we are using DMA, we must not reenable irq after each trigger */ + if (st->dma_st.dma_chan) + return; + + enable_irq(st->irq); + + /* Needed to ACK the DRDY interruption */ + at91_adc_readl(st, LCDR); +} + +static const struct iio_trigger_ops at91_adc_trigger_ops = { + .set_trigger_state = &at91_adc_configure_trigger, + .reenable = &at91_adc_reenable_trigger, + .validate_device = iio_trigger_validate_own_device, +}; + +static int at91_adc_dma_size_done(struct at91_adc_state *st) +{ + struct dma_tx_state state; + enum dma_status status; + int i, size; + + status = dmaengine_tx_status(st->dma_st.dma_chan, + st->dma_st.dma_chan->cookie, + &state); + if (status != DMA_IN_PROGRESS) + return 0; + + /* Transferred length is size in bytes from end of buffer */ + i = st->dma_st.rx_buf_sz - state.residue; + + /* Return available bytes */ + if (i >= st->dma_st.buf_idx) + size = i - st->dma_st.buf_idx; + else + size = st->dma_st.rx_buf_sz + i - st->dma_st.buf_idx; + return size; +} + +static void at91_dma_buffer_done(void *data) +{ + struct iio_dev *indio_dev = data; + + iio_trigger_poll_nested(indio_dev->trig); +} + +static int at91_adc_dma_start(struct iio_dev *indio_dev) +{ + struct at91_adc_state *st = iio_priv(indio_dev); + struct dma_async_tx_descriptor *desc; + dma_cookie_t cookie; + int ret; + u8 bit; + + if (!st->dma_st.dma_chan) + return 0; + + /* we start a new DMA, so set buffer index to start */ + st->dma_st.buf_idx = 0; + + /* + * compute buffer size w.r.t. watermark and enabled channels. + * scan_bytes is aligned so we need an exact size for DMA + */ + st->dma_st.rx_buf_sz = 0; + + for_each_set_bit(bit, indio_dev->active_scan_mask, + indio_dev->num_channels) { + struct iio_chan_spec const *chan = + at91_adc_chan_get(indio_dev, bit); + + if (!chan) + continue; + + st->dma_st.rx_buf_sz += chan->scan_type.storagebits / 8; + } + st->dma_st.rx_buf_sz *= st->dma_st.watermark; + + /* Prepare a DMA cyclic transaction */ + desc = dmaengine_prep_dma_cyclic(st->dma_st.dma_chan, + st->dma_st.rx_dma_buf, + st->dma_st.rx_buf_sz, + st->dma_st.rx_buf_sz / 2, + DMA_DEV_TO_MEM, DMA_PREP_INTERRUPT); + + if (!desc) { + dev_err(&indio_dev->dev, "cannot prepare DMA cyclic\n"); + return -EBUSY; + } + + desc->callback = at91_dma_buffer_done; + desc->callback_param = indio_dev; + + cookie = dmaengine_submit(desc); + ret = dma_submit_error(cookie); + if (ret) { + dev_err(&indio_dev->dev, "cannot submit DMA cyclic\n"); + dmaengine_terminate_async(st->dma_st.dma_chan); + return ret; + } + + /* enable general overrun error signaling */ + at91_adc_writel(st, IER, AT91_SAMA5D2_IER_GOVRE); + /* Issue pending DMA requests */ + dma_async_issue_pending(st->dma_st.dma_chan); + + /* consider current time as DMA start time for timestamps */ + st->dma_st.dma_ts = iio_get_time_ns(indio_dev); + + dev_dbg(&indio_dev->dev, "DMA cyclic started\n"); + + return 0; +} + +static bool at91_adc_buffer_check_use_irq(struct iio_dev *indio, + struct at91_adc_state *st) +{ + /* if using DMA, we do not use our own IRQ (we use DMA-controller) */ + if (st->dma_st.dma_chan) + return false; + /* if the trigger is not ours, then it has its own IRQ */ + if (iio_trigger_validate_own_device(indio->trig, indio)) + return false; + return true; +} + +static bool at91_adc_current_chan_is_touch(struct iio_dev *indio_dev) +{ + struct at91_adc_state *st = iio_priv(indio_dev); + + return !!bitmap_subset(indio_dev->active_scan_mask, + &st->touch_st.channels_bitmask, + st->soc_info.platform->max_index + 1); +} + +static int at91_adc_buffer_prepare(struct iio_dev *indio_dev) +{ + int ret; + u8 bit; + struct at91_adc_state *st = iio_priv(indio_dev); + + /* check if we are enabling triggered buffer or the touchscreen */ + if (at91_adc_current_chan_is_touch(indio_dev)) + return at91_adc_configure_touch(st, true); + + /* if we are not in triggered mode, we cannot enable the buffer. */ + if (!(iio_device_get_current_mode(indio_dev) & INDIO_ALL_TRIGGERED_MODES)) + return -EINVAL; + + ret = pm_runtime_resume_and_get(st->dev); + if (ret < 0) + return ret; + + /* we continue with the triggered buffer */ + ret = at91_adc_dma_start(indio_dev); + if (ret) { + dev_err(&indio_dev->dev, "buffer prepare failed\n"); + goto pm_runtime_put; + } + + for_each_set_bit(bit, indio_dev->active_scan_mask, + indio_dev->num_channels) { + struct iio_chan_spec const *chan = + at91_adc_chan_get(indio_dev, bit); + if (!chan) + continue; + /* these channel types cannot be handled by this trigger */ + if (chan->type == IIO_POSITIONRELATIVE || + chan->type == IIO_PRESSURE || + chan->type == IIO_TEMP) + continue; + + at91_adc_cor(st, chan); + + at91_adc_writel(st, CHER, BIT(chan->channel)); + } + + if (at91_adc_buffer_check_use_irq(indio_dev, st)) + at91_adc_writel(st, IER, AT91_SAMA5D2_IER_DRDY); + +pm_runtime_put: + pm_runtime_put_autosuspend(st->dev); + return ret; +} + +static int at91_adc_buffer_postdisable(struct iio_dev *indio_dev) +{ + struct at91_adc_state *st = iio_priv(indio_dev); + int ret; + u8 bit; + + /* check if we are disabling triggered buffer or the touchscreen */ + if (at91_adc_current_chan_is_touch(indio_dev)) + return at91_adc_configure_touch(st, false); + + /* if we are not in triggered mode, nothing to do here */ + if (!(iio_device_get_current_mode(indio_dev) & INDIO_ALL_TRIGGERED_MODES)) + return -EINVAL; + + ret = pm_runtime_resume_and_get(st->dev); + if (ret < 0) + return ret; + + /* + * For each enable channel we must disable it in hardware. + * In the case of DMA, we must read the last converted value + * to clear EOC status and not get a possible interrupt later. + * This value is being read by DMA from LCDR anyway, so it's not lost. + */ + for_each_set_bit(bit, indio_dev->active_scan_mask, + indio_dev->num_channels) { + struct iio_chan_spec const *chan = + at91_adc_chan_get(indio_dev, bit); + + if (!chan) + continue; + /* these channel types are virtual, no need to do anything */ + if (chan->type == IIO_POSITIONRELATIVE || + chan->type == IIO_PRESSURE || + chan->type == IIO_TEMP) + continue; + + at91_adc_writel(st, CHDR, BIT(chan->channel)); + + if (st->dma_st.dma_chan) + at91_adc_read_chan(st, chan->address); + } + + if (at91_adc_buffer_check_use_irq(indio_dev, st)) + at91_adc_writel(st, IDR, AT91_SAMA5D2_IER_DRDY); + + /* read overflow register to clear possible overflow status */ + at91_adc_readl(st, OVER); + + /* if we are using DMA we must clear registers and end DMA */ + if (st->dma_st.dma_chan) + dmaengine_terminate_sync(st->dma_st.dma_chan); + + pm_runtime_put_autosuspend(st->dev); + + return 0; +} + +static const struct iio_buffer_setup_ops at91_buffer_setup_ops = { + .postdisable = &at91_adc_buffer_postdisable, +}; + +static struct iio_trigger *at91_adc_allocate_trigger(struct iio_dev *indio, + char *trigger_name) +{ + struct iio_trigger *trig; + int ret; + + trig = devm_iio_trigger_alloc(&indio->dev, "%s-dev%d-%s", indio->name, + iio_device_id(indio), trigger_name); + if (!trig) + return ERR_PTR(-ENOMEM); + + trig->dev.parent = indio->dev.parent; + iio_trigger_set_drvdata(trig, indio); + trig->ops = &at91_adc_trigger_ops; + + ret = devm_iio_trigger_register(&indio->dev, trig); + if (ret) + return ERR_PTR(ret); + + return trig; +} + +static void at91_adc_trigger_handler_nodma(struct iio_dev *indio_dev, + struct iio_poll_func *pf) +{ + struct at91_adc_state *st = iio_priv(indio_dev); + int i = 0; + int val; + u8 bit; + u32 mask = at91_adc_active_scan_mask_to_reg(indio_dev); + unsigned int timeout = 50; + u32 status, imr, eoc = 0, eoc_imr; + + /* + * Check if the conversion is ready. If not, wait a little bit, and + * in case of timeout exit with an error. + */ + while (((eoc & mask) != mask) && timeout) { + at91_adc_irq_status(st, &status, &eoc); + at91_adc_irq_mask(st, &imr, &eoc_imr); + usleep_range(50, 100); + timeout--; + } + + /* Cannot read data, not ready. Continue without reporting data */ + if (!timeout) + return; + + for_each_set_bit(bit, indio_dev->active_scan_mask, + indio_dev->num_channels) { + struct iio_chan_spec const *chan = + at91_adc_chan_get(indio_dev, bit); + + if (!chan) + continue; + /* + * Our external trigger only supports the voltage channels. + * In case someone requested a different type of channel + * just put zeroes to buffer. + * This should not happen because we check the scan mode + * and scan mask when we enable the buffer, and we don't allow + * the buffer to start with a mixed mask (voltage and something + * else). + * Thus, emit a warning. + */ + if (chan->type == IIO_VOLTAGE) { + val = at91_adc_read_chan(st, chan->address); + at91_adc_adjust_val_osr(st, &val); + st->buffer[i] = val; + } else { + st->buffer[i] = 0; + WARN(true, "This trigger cannot handle this type of channel"); + } + i++; + } + iio_push_to_buffers_with_timestamp(indio_dev, st->buffer, + pf->timestamp); +} + +static void at91_adc_trigger_handler_dma(struct iio_dev *indio_dev) +{ + struct at91_adc_state *st = iio_priv(indio_dev); + int transferred_len = at91_adc_dma_size_done(st); + s64 ns = iio_get_time_ns(indio_dev); + s64 interval; + int sample_index = 0, sample_count, sample_size; + + u32 status = at91_adc_readl(st, ISR); + /* if we reached this point, we cannot sample faster */ + if (status & AT91_SAMA5D2_IER_GOVRE) + pr_info_ratelimited("%s: conversion overrun detected\n", + indio_dev->name); + + sample_size = div_s64(st->dma_st.rx_buf_sz, st->dma_st.watermark); + + sample_count = div_s64(transferred_len, sample_size); + + /* + * interval between samples is total time since last transfer handling + * divided by the number of samples (total size divided by sample size) + */ + interval = div_s64((ns - st->dma_st.dma_ts), sample_count); + + while (transferred_len >= sample_size) { + /* + * for all the values in the current sample, + * adjust the values inside the buffer for oversampling + */ + at91_adc_adjust_val_osr_array(st, + &st->dma_st.rx_buf[st->dma_st.buf_idx], + sample_size); + + iio_push_to_buffers_with_timestamp(indio_dev, + (st->dma_st.rx_buf + st->dma_st.buf_idx), + (st->dma_st.dma_ts + interval * sample_index)); + /* adjust remaining length */ + transferred_len -= sample_size; + /* adjust buffer index */ + st->dma_st.buf_idx += sample_size; + /* in case of reaching end of buffer, reset index */ + if (st->dma_st.buf_idx >= st->dma_st.rx_buf_sz) + st->dma_st.buf_idx = 0; + sample_index++; + } + /* adjust saved time for next transfer handling */ + st->dma_st.dma_ts = iio_get_time_ns(indio_dev); +} + +static irqreturn_t at91_adc_trigger_handler(int irq, void *p) +{ + struct iio_poll_func *pf = p; + struct iio_dev *indio_dev = pf->indio_dev; + struct at91_adc_state *st = iio_priv(indio_dev); + + /* + * If it's not our trigger, start a conversion now, as we are + * actually polling the trigger now. + */ + if (iio_trigger_validate_own_device(indio_dev->trig, indio_dev)) + at91_adc_writel(st, CR, AT91_SAMA5D2_CR_START); + + if (st->dma_st.dma_chan) + at91_adc_trigger_handler_dma(indio_dev); + else + at91_adc_trigger_handler_nodma(indio_dev, pf); + + iio_trigger_notify_done(indio_dev->trig); + + return IRQ_HANDLED; +} + +static unsigned at91_adc_startup_time(unsigned startup_time_min, + unsigned adc_clk_khz) +{ + static const unsigned int startup_lookup[] = { + 0, 8, 16, 24, + 64, 80, 96, 112, + 512, 576, 640, 704, + 768, 832, 896, 960 + }; + unsigned ticks_min, i; + + /* + * Since the adc frequency is checked before, there is no reason + * to not meet the startup time constraint. + */ + + ticks_min = startup_time_min * adc_clk_khz / 1000; + for (i = 0; i < ARRAY_SIZE(startup_lookup); i++) + if (startup_lookup[i] > ticks_min) + break; + + return i; +} + +static void at91_adc_setup_samp_freq(struct iio_dev *indio_dev, unsigned freq, + unsigned int startup_time, + unsigned int tracktim) +{ + struct at91_adc_state *st = iio_priv(indio_dev); + unsigned f_per, prescal, startup, mr; + int ret; + + f_per = clk_get_rate(st->per_clk); + prescal = (f_per / (2 * freq)) - 1; + + startup = at91_adc_startup_time(startup_time, freq / 1000); + + ret = pm_runtime_resume_and_get(st->dev); + if (ret < 0) + return; + + mr = at91_adc_readl(st, MR); + mr &= ~(AT91_SAMA5D2_MR_STARTUP_MASK | AT91_SAMA5D2_MR_PRESCAL_MASK); + mr |= AT91_SAMA5D2_MR_STARTUP(startup); + mr |= AT91_SAMA5D2_MR_PRESCAL(prescal); + mr |= AT91_SAMA5D2_MR_TRACKTIM(tracktim); + at91_adc_writel(st, MR, mr); + + pm_runtime_put_autosuspend(st->dev); + + dev_dbg(&indio_dev->dev, "freq: %u, startup: %u, prescal: %u, tracktim=%u\n", + freq, startup, prescal, tracktim); + st->current_sample_rate = freq; +} + +static inline unsigned at91_adc_get_sample_freq(struct at91_adc_state *st) +{ + return st->current_sample_rate; +} + +static void at91_adc_touch_data_handler(struct iio_dev *indio_dev) +{ + struct at91_adc_state *st = iio_priv(indio_dev); + u8 bit; + u16 val; + int i = 0; + + for_each_set_bit(bit, indio_dev->active_scan_mask, + st->soc_info.platform->max_index + 1) { + struct iio_chan_spec const *chan = + at91_adc_chan_get(indio_dev, bit); + + if (chan->type == IIO_POSITIONRELATIVE) + at91_adc_read_position(st, chan->channel, &val); + else if (chan->type == IIO_PRESSURE) + at91_adc_read_pressure(st, chan->channel, &val); + else + continue; + st->buffer[i] = val; + i++; + } + /* + * Schedule work to push to buffers. + * This is intended to push to the callback buffer that another driver + * registered. We are still in a handler from our IRQ. If we push + * directly, it means the other driver has it's callback called + * from our IRQ context. Which is something we better avoid. + * Let's schedule it after our IRQ is completed. + */ + schedule_work(&st->touch_st.workq); +} + +static void at91_adc_pen_detect_interrupt(struct at91_adc_state *st) +{ + at91_adc_writel(st, IDR, AT91_SAMA5D2_IER_PEN); + at91_adc_writel(st, IER, AT91_SAMA5D2_IER_NOPEN | + AT91_SAMA5D2_IER_XRDY | AT91_SAMA5D2_IER_YRDY | + AT91_SAMA5D2_IER_PRDY); + at91_adc_writel(st, TRGR, AT91_SAMA5D2_TRGR_TRGMOD_PERIODIC | + AT91_SAMA5D2_TRGR_TRGPER(st->touch_st.sample_period_val)); + st->touch_st.touching = true; +} + +static void at91_adc_no_pen_detect_interrupt(struct iio_dev *indio_dev) +{ + struct at91_adc_state *st = iio_priv(indio_dev); + + at91_adc_writel(st, TRGR, AT91_SAMA5D2_TRGR_TRGMOD_NO_TRIGGER); + at91_adc_writel(st, IDR, AT91_SAMA5D2_IER_NOPEN | + AT91_SAMA5D2_IER_XRDY | AT91_SAMA5D2_IER_YRDY | + AT91_SAMA5D2_IER_PRDY); + st->touch_st.touching = false; + + at91_adc_touch_data_handler(indio_dev); + + at91_adc_writel(st, IER, AT91_SAMA5D2_IER_PEN); +} + +static void at91_adc_workq_handler(struct work_struct *workq) +{ + struct at91_adc_touch *touch_st = container_of(workq, + struct at91_adc_touch, workq); + struct at91_adc_state *st = container_of(touch_st, + struct at91_adc_state, touch_st); + struct iio_dev *indio_dev = st->indio_dev; + + iio_push_to_buffers(indio_dev, st->buffer); +} + +static irqreturn_t at91_adc_interrupt(int irq, void *private) +{ + struct iio_dev *indio = private; + struct at91_adc_state *st = iio_priv(indio); + u32 status, eoc, imr, eoc_imr; + u32 rdy_mask = AT91_SAMA5D2_IER_XRDY | AT91_SAMA5D2_IER_YRDY | + AT91_SAMA5D2_IER_PRDY; + + at91_adc_irq_status(st, &status, &eoc); + at91_adc_irq_mask(st, &imr, &eoc_imr); + + if (!(status & imr) && !(eoc & eoc_imr)) + return IRQ_NONE; + if (status & AT91_SAMA5D2_IER_PEN) { + /* pen detected IRQ */ + at91_adc_pen_detect_interrupt(st); + } else if ((status & AT91_SAMA5D2_IER_NOPEN)) { + /* nopen detected IRQ */ + at91_adc_no_pen_detect_interrupt(indio); + } else if ((status & AT91_SAMA5D2_ISR_PENS) && + ((status & rdy_mask) == rdy_mask)) { + /* periodic trigger IRQ - during pen sense */ + at91_adc_touch_data_handler(indio); + } else if (status & AT91_SAMA5D2_ISR_PENS) { + /* + * touching, but the measurements are not ready yet. + * read and ignore. + */ + status = at91_adc_readl(st, XPOSR); + status = at91_adc_readl(st, YPOSR); + status = at91_adc_readl(st, PRESSR); + } else if (iio_buffer_enabled(indio) && + (status & AT91_SAMA5D2_IER_DRDY)) { + /* triggered buffer without DMA */ + disable_irq_nosync(irq); + iio_trigger_poll(indio->trig); + } else if (iio_buffer_enabled(indio) && st->dma_st.dma_chan) { + /* triggered buffer with DMA - should not happen */ + disable_irq_nosync(irq); + WARN(true, "Unexpected irq occurred\n"); + } else if (!iio_buffer_enabled(indio)) { + /* software requested conversion */ + st->conversion_value = at91_adc_read_chan(st, st->chan->address); + st->conversion_done = true; + wake_up_interruptible(&st->wq_data_available); + } + return IRQ_HANDLED; +} + +/* This needs to be called with direct mode claimed and st->lock locked. */ +static int at91_adc_read_info_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int *val) +{ + struct at91_adc_state *st = iio_priv(indio_dev); + u16 tmp_val; + int ret; + + ret = pm_runtime_resume_and_get(st->dev); + if (ret < 0) + return ret; + + /* + * Keep in mind that we cannot use software trigger or touchscreen + * if external trigger is enabled + */ + if (chan->type == IIO_POSITIONRELATIVE) { + ret = at91_adc_read_position(st, chan->channel, + &tmp_val); + *val = tmp_val; + if (ret > 0) + ret = at91_adc_adjust_val_osr(st, val); + + goto pm_runtime_put; + } + if (chan->type == IIO_PRESSURE) { + ret = at91_adc_read_pressure(st, chan->channel, + &tmp_val); + *val = tmp_val; + if (ret > 0) + ret = at91_adc_adjust_val_osr(st, val); + + goto pm_runtime_put; + } + + /* in this case we have a voltage or temperature channel */ + + st->chan = chan; + + at91_adc_cor(st, chan); + at91_adc_writel(st, CHER, BIT(chan->channel)); + /* + * TEMPMR.TEMPON needs to update after CHER otherwise if none + * of the channels are enabled and TEMPMR.TEMPON = 1 will + * trigger DRDY interruption while preparing for temperature read. + */ + if (chan->type == IIO_TEMP) + at91_adc_writel(st, TEMPMR, AT91_SAMA5D2_TEMPMR_TEMPON); + at91_adc_eoc_ena(st, chan->channel); + at91_adc_writel(st, CR, AT91_SAMA5D2_CR_START); + + ret = wait_event_interruptible_timeout(st->wq_data_available, + st->conversion_done, + msecs_to_jiffies(1000)); + if (ret == 0) + ret = -ETIMEDOUT; + + if (ret > 0) { + *val = st->conversion_value; + ret = at91_adc_adjust_val_osr(st, val); + if (chan->scan_type.sign == 's') + *val = sign_extend32(*val, + chan->scan_type.realbits - 1); + st->conversion_done = false; + } + + at91_adc_eoc_dis(st, st->chan->channel); + if (chan->type == IIO_TEMP) + at91_adc_writel(st, TEMPMR, 0U); + at91_adc_writel(st, CHDR, BIT(chan->channel)); + + /* Needed to ACK the DRDY interruption */ + at91_adc_readl(st, LCDR); + +pm_runtime_put: + pm_runtime_put_autosuspend(st->dev); + return ret; +} + +static int at91_adc_read_info_locked(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int *val) +{ + struct at91_adc_state *st = iio_priv(indio_dev); + + guard(mutex)(&st->lock); + + return at91_adc_read_info_raw(indio_dev, chan, val); +} + +static void at91_adc_temp_sensor_configure(struct at91_adc_state *st, + bool start) +{ + u32 sample_rate, oversampling_ratio; + u32 startup_time, tracktim, trackx; + + if (start) { + /* + * Configure the sensor for best accuracy: 10MHz frequency, + * oversampling rate of 256, tracktim=0xf and trackx=1. + */ + sample_rate = 10 * MEGA; + oversampling_ratio = 256; + startup_time = AT91_SAMA5D2_MR_STARTUP_TS_MIN; + tracktim = AT91_SAMA5D2_MR_TRACKTIM_TS; + trackx = AT91_SAMA5D2_TRACKX_TS; + + st->temp_st.saved_sample_rate = st->current_sample_rate; + st->temp_st.saved_oversampling = st->oversampling_ratio; + } else { + /* Go back to previous settings. */ + sample_rate = st->temp_st.saved_sample_rate; + oversampling_ratio = st->temp_st.saved_oversampling; + startup_time = st->soc_info.startup_time; + tracktim = 0; + trackx = 0; + } + + at91_adc_setup_samp_freq(st->indio_dev, sample_rate, startup_time, + tracktim); + at91_adc_config_emr(st, oversampling_ratio, trackx); +} + +static int at91_adc_read_temp(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int *val) +{ + struct at91_adc_state *st = iio_priv(indio_dev); + struct at91_adc_temp_sensor_clb *clb = &st->soc_info.temp_sensor_clb; + u64 div1, div2; + u32 tmp; + int ret, vbg, vtemp; + + guard(mutex)(&st->lock); + + ret = pm_runtime_resume_and_get(st->dev); + if (ret < 0) + return ret; + + at91_adc_temp_sensor_configure(st, true); + + /* Read VBG. */ + tmp = at91_adc_readl(st, ACR); + tmp |= AT91_SAMA5D2_ACR_SRCLCH; + at91_adc_writel(st, ACR, tmp); + ret = at91_adc_read_info_raw(indio_dev, chan, &vbg); + if (ret < 0) + goto restore_config; + + /* Read VTEMP. */ + tmp &= ~AT91_SAMA5D2_ACR_SRCLCH; + at91_adc_writel(st, ACR, tmp); + ret = at91_adc_read_info_raw(indio_dev, chan, &vtemp); + +restore_config: + /* Revert previous settings. */ + at91_adc_temp_sensor_configure(st, false); + pm_runtime_put_autosuspend(st->dev); + if (ret < 0) + return ret; + + /* + * Temp[milli] = p1[milli] + (vtemp * clb->p6 - clb->p4 * vbg)/ + * (vbg * AT91_ADC_TS_VTEMP_DT) + */ + div1 = DIV_ROUND_CLOSEST_ULL(((u64)vtemp * clb->p6), vbg); + div1 = DIV_ROUND_CLOSEST_ULL((div1 * 1000), AT91_ADC_TS_VTEMP_DT); + div2 = DIV_ROUND_CLOSEST_ULL((u64)clb->p4, AT91_ADC_TS_VTEMP_DT); + div2 *= 1000; + *val = clb->p1 + (int)div1 - (int)div2; + + return ret; +} + +static int at91_adc_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct at91_adc_state *st = iio_priv(indio_dev); + int ret; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; + + ret = at91_adc_read_info_locked(indio_dev, chan, val); + iio_device_release_direct(indio_dev); + return ret; + + case IIO_CHAN_INFO_SCALE: + *val = st->vref_uv / 1000; + if (chan->differential) + *val *= 2; + *val2 = chan->scan_type.realbits; + return IIO_VAL_FRACTIONAL_LOG2; + + case IIO_CHAN_INFO_PROCESSED: + if (chan->type != IIO_TEMP) + return -EINVAL; + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; + + ret = at91_adc_read_temp(indio_dev, chan, val); + iio_device_release_direct(indio_dev); + + return ret; + + case IIO_CHAN_INFO_SAMP_FREQ: + *val = at91_adc_get_sample_freq(st); + return IIO_VAL_INT; + + case IIO_CHAN_INFO_OVERSAMPLING_RATIO: + *val = st->oversampling_ratio; + return IIO_VAL_INT; + + default: + return -EINVAL; + } +} + +static int at91_adc_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, int val2, long mask) +{ + struct at91_adc_state *st = iio_priv(indio_dev); + int ret; + + switch (mask) { + case IIO_CHAN_INFO_OVERSAMPLING_RATIO: + /* if no change, optimize out */ + if (val == st->oversampling_ratio) + return 0; + + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; + mutex_lock(&st->lock); + /* update ratio */ + ret = at91_adc_config_emr(st, val, 0); + mutex_unlock(&st->lock); + iio_device_release_direct(indio_dev); + return ret; + case IIO_CHAN_INFO_SAMP_FREQ: + if (val < st->soc_info.min_sample_rate || + val > st->soc_info.max_sample_rate) + return -EINVAL; + + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; + mutex_lock(&st->lock); + at91_adc_setup_samp_freq(indio_dev, val, + st->soc_info.startup_time, 0); + mutex_unlock(&st->lock); + iio_device_release_direct(indio_dev); + return 0; + default: + return -EINVAL; + } +} + +static int at91_adc_read_avail(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + const int **vals, int *type, int *length, + long mask) +{ + struct at91_adc_state *st = iio_priv(indio_dev); + + switch (mask) { + case IIO_CHAN_INFO_OVERSAMPLING_RATIO: + *vals = (int *)st->soc_info.platform->oversampling_avail; + *type = IIO_VAL_INT; + *length = st->soc_info.platform->oversampling_avail_no; + return IIO_AVAIL_LIST; + default: + return -EINVAL; + } +} + +static void at91_adc_dma_init(struct at91_adc_state *st) +{ + struct device *dev = &st->indio_dev->dev; + struct dma_slave_config config = {0}; + /* we have 2 bytes for each channel */ + unsigned int sample_size = st->soc_info.platform->nr_channels * 2; + /* + * We make the buffer double the size of the fifo, + * such that DMA uses one half of the buffer (full fifo size) + * and the software uses the other half to read/write. + */ + unsigned int pages = DIV_ROUND_UP(AT91_HWFIFO_MAX_SIZE * + sample_size * 2, PAGE_SIZE); + + if (st->dma_st.dma_chan) + return; + + st->dma_st.dma_chan = dma_request_chan(dev, "rx"); + if (IS_ERR(st->dma_st.dma_chan)) { + dev_info(dev, "can't get DMA channel\n"); + st->dma_st.dma_chan = NULL; + goto dma_exit; + } + + st->dma_st.rx_buf = dma_alloc_coherent(st->dma_st.dma_chan->device->dev, + pages * PAGE_SIZE, + &st->dma_st.rx_dma_buf, + GFP_KERNEL); + if (!st->dma_st.rx_buf) { + dev_info(dev, "can't allocate coherent DMA area\n"); + goto dma_chan_disable; + } + + /* Configure DMA channel to read data register */ + config.direction = DMA_DEV_TO_MEM; + config.src_addr = (phys_addr_t)(st->dma_st.phys_addr + + st->soc_info.platform->layout->LCDR); + config.src_addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES; + config.src_maxburst = 1; + config.dst_maxburst = 1; + + if (dmaengine_slave_config(st->dma_st.dma_chan, &config)) { + dev_info(dev, "can't configure DMA slave\n"); + goto dma_free_area; + } + + dev_info(dev, "using %s for rx DMA transfers\n", + dma_chan_name(st->dma_st.dma_chan)); + + return; + +dma_free_area: + dma_free_coherent(st->dma_st.dma_chan->device->dev, pages * PAGE_SIZE, + st->dma_st.rx_buf, st->dma_st.rx_dma_buf); +dma_chan_disable: + dma_release_channel(st->dma_st.dma_chan); + st->dma_st.dma_chan = NULL; +dma_exit: + dev_info(dev, "continuing without DMA support\n"); +} + +static void at91_adc_dma_disable(struct at91_adc_state *st) +{ + struct device *dev = &st->indio_dev->dev; + /* we have 2 bytes for each channel */ + unsigned int sample_size = st->soc_info.platform->nr_channels * 2; + unsigned int pages = DIV_ROUND_UP(AT91_HWFIFO_MAX_SIZE * + sample_size * 2, PAGE_SIZE); + + /* if we are not using DMA, just return */ + if (!st->dma_st.dma_chan) + return; + + /* wait for all transactions to be terminated first*/ + dmaengine_terminate_sync(st->dma_st.dma_chan); + + dma_free_coherent(st->dma_st.dma_chan->device->dev, pages * PAGE_SIZE, + st->dma_st.rx_buf, st->dma_st.rx_dma_buf); + dma_release_channel(st->dma_st.dma_chan); + st->dma_st.dma_chan = NULL; + + dev_info(dev, "continuing without DMA support\n"); +} + +static int at91_adc_set_watermark(struct iio_dev *indio_dev, unsigned int val) +{ + struct at91_adc_state *st = iio_priv(indio_dev); + int ret; + + if (val > AT91_HWFIFO_MAX_SIZE) + val = AT91_HWFIFO_MAX_SIZE; + + if (!st->selected_trig->hw_trig) { + dev_dbg(&indio_dev->dev, "we need hw trigger for DMA\n"); + return 0; + } + + dev_dbg(&indio_dev->dev, "new watermark is %u\n", val); + st->dma_st.watermark = val; + + /* + * The logic here is: if we have watermark 1, it means we do + * each conversion with it's own IRQ, thus we don't need DMA. + * If the watermark is higher, we do DMA to do all the transfers in bulk + */ + + if (val == 1) + at91_adc_dma_disable(st); + else if (val > 1) + at91_adc_dma_init(st); + + /* + * We can start the DMA only after setting the watermark and + * having the DMA initialization completed + */ + ret = at91_adc_buffer_prepare(indio_dev); + if (ret) + at91_adc_dma_disable(st); + + return ret; +} + +static int at91_adc_update_scan_mode(struct iio_dev *indio_dev, + const unsigned long *scan_mask) +{ + struct at91_adc_state *st = iio_priv(indio_dev); + + if (bitmap_subset(scan_mask, &st->touch_st.channels_bitmask, + st->soc_info.platform->max_index + 1)) + return 0; + /* + * if the new bitmap is a combination of touchscreen and regular + * channels, then we are not fine + */ + if (bitmap_intersects(&st->touch_st.channels_bitmask, scan_mask, + st->soc_info.platform->max_index + 1)) + return -EINVAL; + return 0; +} + +static void at91_adc_hw_init(struct iio_dev *indio_dev) +{ + struct at91_adc_state *st = iio_priv(indio_dev); + + at91_adc_writel(st, CR, AT91_SAMA5D2_CR_SWRST); + if (st->soc_info.platform->layout->EOC_IDR) + at91_adc_writel(st, EOC_IDR, 0xffffffff); + at91_adc_writel(st, IDR, 0xffffffff); + /* + * Transfer field must be set to 2 according to the datasheet and + * allows different analog settings for each channel. + */ + at91_adc_writel(st, MR, + AT91_SAMA5D2_MR_TRANSFER(2) | AT91_SAMA5D2_MR_ANACH); + + at91_adc_setup_samp_freq(indio_dev, st->soc_info.min_sample_rate, + st->soc_info.startup_time, 0); + + /* configure extended mode register */ + at91_adc_config_emr(st, st->oversampling_ratio, 0); +} + +static ssize_t at91_adc_get_fifo_state(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct iio_dev *indio_dev = dev_to_iio_dev(dev); + struct at91_adc_state *st = iio_priv(indio_dev); + + return sysfs_emit(buf, "%d\n", !!st->dma_st.dma_chan); +} + +static ssize_t at91_adc_get_watermark(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct iio_dev *indio_dev = dev_to_iio_dev(dev); + struct at91_adc_state *st = iio_priv(indio_dev); + + return sysfs_emit(buf, "%d\n", st->dma_st.watermark); +} + +static IIO_DEVICE_ATTR(hwfifo_enabled, 0444, + at91_adc_get_fifo_state, NULL, 0); +static IIO_DEVICE_ATTR(hwfifo_watermark, 0444, + at91_adc_get_watermark, NULL, 0); + +IIO_STATIC_CONST_DEVICE_ATTR(hwfifo_watermark_min, "2"); +IIO_STATIC_CONST_DEVICE_ATTR(hwfifo_watermark_max, AT91_HWFIFO_MAX_SIZE_STR); + +static const struct iio_dev_attr *at91_adc_fifo_attributes[] = { + &iio_dev_attr_hwfifo_watermark_min, + &iio_dev_attr_hwfifo_watermark_max, + &iio_dev_attr_hwfifo_watermark, + &iio_dev_attr_hwfifo_enabled, + NULL, +}; + +static const struct iio_info at91_adc_info = { + .read_avail = &at91_adc_read_avail, + .read_raw = &at91_adc_read_raw, + .write_raw = &at91_adc_write_raw, + .update_scan_mode = &at91_adc_update_scan_mode, + .fwnode_xlate = &at91_adc_fwnode_xlate, + .hwfifo_set_watermark = &at91_adc_set_watermark, +}; + +static int at91_adc_buffer_and_trigger_init(struct device *dev, + struct iio_dev *indio) +{ + struct at91_adc_state *st = iio_priv(indio); + const struct iio_dev_attr **fifo_attrs; + int ret; + + if (st->selected_trig->hw_trig) + fifo_attrs = at91_adc_fifo_attributes; + else + fifo_attrs = NULL; + + ret = devm_iio_triggered_buffer_setup_ext(&indio->dev, indio, + &iio_pollfunc_store_time, &at91_adc_trigger_handler, + IIO_BUFFER_DIRECTION_IN, &at91_buffer_setup_ops, fifo_attrs); + if (ret < 0) { + dev_err(dev, "couldn't initialize the buffer.\n"); + return ret; + } + + if (!st->selected_trig->hw_trig) + return 0; + + st->trig = at91_adc_allocate_trigger(indio, st->selected_trig->name); + if (IS_ERR(st->trig)) { + dev_err(dev, "could not allocate trigger\n"); + return PTR_ERR(st->trig); + } + + /* + * Initially the iio buffer has a length of 2 and + * a watermark of 1 + */ + st->dma_st.watermark = 1; + + return 0; +} + +static int at91_adc_temp_sensor_init(struct at91_adc_state *st, + struct device *dev) +{ + struct at91_adc_temp_sensor_clb *clb = &st->soc_info.temp_sensor_clb; + struct nvmem_cell *temp_calib; + u32 *buf; + size_t len; + int ret = 0; + + if (!st->soc_info.platform->temp_sensor) + return 0; + + /* Get the calibration data from NVMEM. */ + temp_calib = nvmem_cell_get(dev, "temperature_calib"); + if (IS_ERR(temp_calib)) { + ret = PTR_ERR(temp_calib); + if (ret != -ENOENT) + dev_err(dev, "Failed to get temperature_calib cell!\n"); + return ret; + } + + buf = nvmem_cell_read(temp_calib, &len); + nvmem_cell_put(temp_calib); + if (IS_ERR(buf)) { + dev_err(dev, "Failed to read calibration data!\n"); + return PTR_ERR(buf); + } + if (len < AT91_ADC_TS_CLB_IDX_MAX * 4) { + dev_err(dev, "Invalid calibration data!\n"); + ret = -EINVAL; + goto free_buf; + } + + /* Store calibration data for later use. */ + clb->p1 = buf[AT91_ADC_TS_CLB_IDX_P1]; + clb->p4 = buf[AT91_ADC_TS_CLB_IDX_P4]; + clb->p6 = buf[AT91_ADC_TS_CLB_IDX_P6]; + + /* + * We prepare here the conversion to milli to avoid doing it on hotpath. + */ + clb->p1 = clb->p1 * 1000; + +free_buf: + kfree(buf); + return ret; +} + +static int at91_adc_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct iio_dev *indio_dev; + struct at91_adc_state *st; + struct resource *res; + int ret, i, num_channels; + u32 edge_type = IRQ_TYPE_NONE; + + indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*st)); + if (!indio_dev) + return -ENOMEM; + + st = iio_priv(indio_dev); + st->indio_dev = indio_dev; + + st->soc_info.platform = device_get_match_data(dev); + + ret = at91_adc_temp_sensor_init(st, &pdev->dev); + /* Don't register temperature channel if initialization failed. */ + if (ret) + num_channels = st->soc_info.platform->max_channels - 1; + else + num_channels = st->soc_info.platform->max_channels; + + indio_dev->name = dev_name(&pdev->dev); + indio_dev->modes = INDIO_DIRECT_MODE | INDIO_BUFFER_SOFTWARE; + indio_dev->info = &at91_adc_info; + indio_dev->channels = *st->soc_info.platform->adc_channels; + indio_dev->num_channels = num_channels; + + bitmap_set(&st->touch_st.channels_bitmask, + st->soc_info.platform->touch_chan_x, 1); + bitmap_set(&st->touch_st.channels_bitmask, + st->soc_info.platform->touch_chan_y, 1); + bitmap_set(&st->touch_st.channels_bitmask, + st->soc_info.platform->touch_chan_p, 1); + + st->oversampling_ratio = 1; + + ret = device_property_read_u32(dev, "atmel,min-sample-rate-hz", + &st->soc_info.min_sample_rate); + if (ret) { + dev_err(&pdev->dev, + "invalid or missing value for atmel,min-sample-rate-hz\n"); + return ret; + } + + ret = device_property_read_u32(dev, "atmel,max-sample-rate-hz", + &st->soc_info.max_sample_rate); + if (ret) { + dev_err(&pdev->dev, + "invalid or missing value for atmel,max-sample-rate-hz\n"); + return ret; + } + + ret = device_property_read_u32(dev, "atmel,startup-time-ms", + &st->soc_info.startup_time); + if (ret) { + dev_err(&pdev->dev, + "invalid or missing value for atmel,startup-time-ms\n"); + return ret; + } + + ret = device_property_read_u32(dev, "atmel,trigger-edge-type", + &edge_type); + if (ret) { + dev_dbg(&pdev->dev, + "atmel,trigger-edge-type not specified, only software trigger available\n"); + } + + st->selected_trig = NULL; + + /* find the right trigger, or no trigger at all */ + for (i = 0; i < st->soc_info.platform->hw_trig_cnt + 1; i++) + if (at91_adc_trigger_list[i].edge_type == edge_type) { + st->selected_trig = &at91_adc_trigger_list[i]; + break; + } + + if (!st->selected_trig) { + dev_err(&pdev->dev, "invalid external trigger edge value\n"); + return -EINVAL; + } + + init_waitqueue_head(&st->wq_data_available); + mutex_init(&st->lock); + INIT_WORK(&st->touch_st.workq, at91_adc_workq_handler); + + st->base = devm_platform_get_and_ioremap_resource(pdev, 0, &res); + if (IS_ERR(st->base)) + return PTR_ERR(st->base); + + /* if we plan to use DMA, we need the physical address of the regs */ + st->dma_st.phys_addr = res->start; + + st->irq = platform_get_irq(pdev, 0); + if (st->irq < 0) + return st->irq; + + st->per_clk = devm_clk_get(&pdev->dev, "adc_clk"); + if (IS_ERR(st->per_clk)) + return PTR_ERR(st->per_clk); + + st->reg = devm_regulator_get(&pdev->dev, "vddana"); + if (IS_ERR(st->reg)) + return PTR_ERR(st->reg); + + st->vref = devm_regulator_get(&pdev->dev, "vref"); + if (IS_ERR(st->vref)) + return PTR_ERR(st->vref); + + ret = devm_request_irq(&pdev->dev, st->irq, at91_adc_interrupt, 0, + pdev->dev.driver->name, indio_dev); + if (ret) + return ret; + + ret = regulator_enable(st->reg); + if (ret) + return ret; + + ret = regulator_enable(st->vref); + if (ret) + goto reg_disable; + + st->vref_uv = regulator_get_voltage(st->vref); + if (st->vref_uv <= 0) { + ret = -EINVAL; + goto vref_disable; + } + + ret = clk_prepare_enable(st->per_clk); + if (ret) + goto vref_disable; + + platform_set_drvdata(pdev, indio_dev); + st->dev = &pdev->dev; + pm_runtime_set_autosuspend_delay(st->dev, 500); + pm_runtime_use_autosuspend(st->dev); + pm_runtime_set_active(st->dev); + pm_runtime_enable(st->dev); + pm_runtime_get_noresume(st->dev); + + at91_adc_hw_init(indio_dev); + + ret = at91_adc_buffer_and_trigger_init(&pdev->dev, indio_dev); + if (ret < 0) + goto err_pm_disable; + + if (dma_coerce_mask_and_coherent(&indio_dev->dev, DMA_BIT_MASK(32))) + dev_info(&pdev->dev, "cannot set DMA mask to 32-bit\n"); + + ret = iio_device_register(indio_dev); + if (ret < 0) + goto dma_disable; + + if (st->selected_trig->hw_trig) + dev_info(&pdev->dev, "setting up trigger as %s\n", + st->selected_trig->name); + + dev_info(&pdev->dev, "version: %x\n", + readl_relaxed(st->base + st->soc_info.platform->layout->VERSION)); + + pm_runtime_put_autosuspend(st->dev); + + return 0; + +dma_disable: + at91_adc_dma_disable(st); +err_pm_disable: + pm_runtime_put_noidle(st->dev); + pm_runtime_disable(st->dev); + pm_runtime_set_suspended(st->dev); + pm_runtime_dont_use_autosuspend(st->dev); + clk_disable_unprepare(st->per_clk); +vref_disable: + regulator_disable(st->vref); +reg_disable: + regulator_disable(st->reg); + return ret; +} + +static void at91_adc_remove(struct platform_device *pdev) +{ + struct iio_dev *indio_dev = platform_get_drvdata(pdev); + struct at91_adc_state *st = iio_priv(indio_dev); + + iio_device_unregister(indio_dev); + cancel_work_sync(&st->touch_st.workq); + + at91_adc_dma_disable(st); + + pm_runtime_disable(st->dev); + pm_runtime_set_suspended(st->dev); + clk_disable_unprepare(st->per_clk); + + regulator_disable(st->vref); + regulator_disable(st->reg); +} + +static int at91_adc_suspend(struct device *dev) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct at91_adc_state *st = iio_priv(indio_dev); + int ret; + + ret = pm_runtime_resume_and_get(st->dev); + if (ret < 0) + return ret; + + if (iio_buffer_enabled(indio_dev)) + at91_adc_buffer_postdisable(indio_dev); + + /* + * Do a software reset of the ADC before we go to suspend. + * this will ensure that all pins are free from being muxed by the ADC + * and can be used by for other devices. + * Otherwise, ADC will hog them and we can't go to suspend mode. + */ + at91_adc_writel(st, CR, AT91_SAMA5D2_CR_SWRST); + + pm_runtime_mark_last_busy(st->dev); + pm_runtime_put_noidle(st->dev); + clk_disable_unprepare(st->per_clk); + regulator_disable(st->vref); + regulator_disable(st->reg); + + return pinctrl_pm_select_sleep_state(dev); +} + +static int at91_adc_resume(struct device *dev) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct at91_adc_state *st = iio_priv(indio_dev); + int ret; + + ret = pinctrl_pm_select_default_state(dev); + if (ret) + goto resume_failed; + + ret = regulator_enable(st->reg); + if (ret) + goto resume_failed; + + ret = regulator_enable(st->vref); + if (ret) + goto reg_disable_resume; + + ret = clk_prepare_enable(st->per_clk); + if (ret) + goto vref_disable_resume; + + pm_runtime_get_noresume(st->dev); + + at91_adc_hw_init(indio_dev); + + /* reconfiguring trigger hardware state */ + if (iio_buffer_enabled(indio_dev)) { + ret = at91_adc_buffer_prepare(indio_dev); + if (ret) + goto pm_runtime_put; + + at91_adc_configure_trigger_registers(st, true); + } + + pm_runtime_put_autosuspend(st->dev); + + return 0; + +pm_runtime_put: + pm_runtime_mark_last_busy(st->dev); + pm_runtime_put_noidle(st->dev); + clk_disable_unprepare(st->per_clk); +vref_disable_resume: + regulator_disable(st->vref); +reg_disable_resume: + regulator_disable(st->reg); +resume_failed: + dev_err(&indio_dev->dev, "failed to resume\n"); + return ret; +} + +static int at91_adc_runtime_suspend(struct device *dev) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct at91_adc_state *st = iio_priv(indio_dev); + + clk_disable(st->per_clk); + + return 0; +} + +static int at91_adc_runtime_resume(struct device *dev) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct at91_adc_state *st = iio_priv(indio_dev); + + return clk_enable(st->per_clk); +} + +static const struct dev_pm_ops at91_adc_pm_ops = { + SYSTEM_SLEEP_PM_OPS(at91_adc_suspend, at91_adc_resume) + RUNTIME_PM_OPS(at91_adc_runtime_suspend, at91_adc_runtime_resume, + NULL) +}; + +static const struct of_device_id at91_adc_dt_match[] = { + { + .compatible = "atmel,sama5d2-adc", + .data = (const void *)&sama5d2_platform, + }, { + .compatible = "microchip,sama7g5-adc", + .data = (const void *)&sama7g5_platform, + }, { + /* sentinel */ + } +}; +MODULE_DEVICE_TABLE(of, at91_adc_dt_match); + +static struct platform_driver at91_adc_driver = { + .probe = at91_adc_probe, + .remove = at91_adc_remove, + .driver = { + .name = "at91-sama5d2_adc", + .of_match_table = at91_adc_dt_match, + .pm = pm_ptr(&at91_adc_pm_ops), + }, +}; +module_platform_driver(at91_adc_driver) + +MODULE_AUTHOR("Ludovic Desroches "); +MODULE_AUTHOR("Eugen Hristev +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +/* Registers */ +#define AT91_ADC_CR 0x00 /* Control Register */ +#define AT91_ADC_SWRST (1 << 0) /* Software Reset */ +#define AT91_ADC_START (1 << 1) /* Start Conversion */ + +#define AT91_ADC_MR 0x04 /* Mode Register */ +#define AT91_ADC_TSAMOD (3 << 0) /* ADC mode */ +#define AT91_ADC_TSAMOD_ADC_ONLY_MODE (0 << 0) /* ADC Mode */ +#define AT91_ADC_TSAMOD_TS_ONLY_MODE (1 << 0) /* Touch Screen Only Mode */ +#define AT91_ADC_TRGEN (1 << 0) /* Trigger Enable */ +#define AT91_ADC_TRGSEL (7 << 1) /* Trigger Selection */ +#define AT91_ADC_TRGSEL_TC0 (0 << 1) +#define AT91_ADC_TRGSEL_TC1 (1 << 1) +#define AT91_ADC_TRGSEL_TC2 (2 << 1) +#define AT91_ADC_TRGSEL_EXTERNAL (6 << 1) +#define AT91_ADC_LOWRES (1 << 4) /* Low Resolution */ +#define AT91_ADC_SLEEP (1 << 5) /* Sleep Mode */ +#define AT91_ADC_PENDET (1 << 6) /* Pen contact detection enable */ +#define AT91_ADC_PRESCAL_9260 (0x3f << 8) /* Prescalar Rate Selection */ +#define AT91_ADC_PRESCAL_9G45 (0xff << 8) +#define AT91_ADC_PRESCAL_(x) ((x) << 8) +#define AT91_ADC_STARTUP_9260 (0x1f << 16) /* Startup Up Time */ +#define AT91_ADC_STARTUP_9G45 (0x7f << 16) +#define AT91_ADC_STARTUP_9X5 (0xf << 16) +#define AT91_ADC_STARTUP_(x) ((x) << 16) +#define AT91_ADC_SHTIM (0xf << 24) /* Sample & Hold Time */ +#define AT91_ADC_SHTIM_(x) ((x) << 24) +#define AT91_ADC_PENDBC (0x0f << 28) /* Pen Debounce time */ +#define AT91_ADC_PENDBC_(x) ((x) << 28) + +#define AT91_ADC_TSR 0x0C +#define AT91_ADC_TSR_SHTIM (0xf << 24) /* Sample & Hold Time */ +#define AT91_ADC_TSR_SHTIM_(x) ((x) << 24) + +#define AT91_ADC_CHER 0x10 /* Channel Enable Register */ +#define AT91_ADC_CHDR 0x14 /* Channel Disable Register */ +#define AT91_ADC_CHSR 0x18 /* Channel Status Register */ +#define AT91_ADC_CH(n) (1 << (n)) /* Channel Number */ + +#define AT91_ADC_SR 0x1C /* Status Register */ +#define AT91_ADC_EOC(n) (1 << (n)) /* End of Conversion on Channel N */ +#define AT91_ADC_OVRE(n) (1 << ((n) + 8))/* Overrun Error on Channel N */ +#define AT91_ADC_DRDY (1 << 16) /* Data Ready */ +#define AT91_ADC_GOVRE (1 << 17) /* General Overrun Error */ +#define AT91_ADC_ENDRX (1 << 18) /* End of RX Buffer */ +#define AT91_ADC_RXFUFF (1 << 19) /* RX Buffer Full */ + +#define AT91_ADC_SR_9X5 0x30 /* Status Register for 9x5 */ +#define AT91_ADC_SR_DRDY_9X5 (1 << 24) /* Data Ready */ + +#define AT91_ADC_LCDR 0x20 /* Last Converted Data Register */ +#define AT91_ADC_LDATA (0x3ff) + +#define AT91_ADC_IER 0x24 /* Interrupt Enable Register */ +#define AT91_ADC_IDR 0x28 /* Interrupt Disable Register */ +#define AT91_ADC_IMR 0x2C /* Interrupt Mask Register */ +#define AT91RL_ADC_IER_PEN (1 << 20) +#define AT91RL_ADC_IER_NOPEN (1 << 21) +#define AT91_ADC_IER_PEN (1 << 29) +#define AT91_ADC_IER_NOPEN (1 << 30) +#define AT91_ADC_IER_XRDY (1 << 20) +#define AT91_ADC_IER_YRDY (1 << 21) +#define AT91_ADC_IER_PRDY (1 << 22) +#define AT91_ADC_ISR_PENS (1 << 31) + +#define AT91_ADC_CHR(n) (0x30 + ((n) * 4)) /* Channel Data Register N */ +#define AT91_ADC_DATA (0x3ff) + +#define AT91_ADC_CDR0_9X5 (0x50) /* Channel Data Register 0 for 9X5 */ + +#define AT91_ADC_ACR 0x94 /* Analog Control Register */ +#define AT91_ADC_ACR_PENDETSENS (0x3 << 0) /* pull-up resistor */ + +#define AT91_ADC_TSMR 0xB0 +#define AT91_ADC_TSMR_TSMODE (3 << 0) /* Touch Screen Mode */ +#define AT91_ADC_TSMR_TSMODE_NONE (0 << 0) +#define AT91_ADC_TSMR_TSMODE_4WIRE_NO_PRESS (1 << 0) +#define AT91_ADC_TSMR_TSMODE_4WIRE_PRESS (2 << 0) +#define AT91_ADC_TSMR_TSMODE_5WIRE (3 << 0) +#define AT91_ADC_TSMR_TSAV (3 << 4) /* Averages samples */ +#define AT91_ADC_TSMR_TSAV_(x) ((x) << 4) +#define AT91_ADC_TSMR_SCTIM (0x0f << 16) /* Switch closure time */ +#define AT91_ADC_TSMR_SCTIM_(x) ((x) << 16) +#define AT91_ADC_TSMR_PENDBC (0x0f << 28) /* Pen Debounce time */ +#define AT91_ADC_TSMR_PENDBC_(x) ((x) << 28) +#define AT91_ADC_TSMR_NOTSDMA (1 << 22) /* No Touchscreen DMA */ +#define AT91_ADC_TSMR_PENDET_DIS (0 << 24) /* Pen contact detection disable */ +#define AT91_ADC_TSMR_PENDET_ENA (1 << 24) /* Pen contact detection enable */ + +#define AT91_ADC_TSXPOSR 0xB4 +#define AT91_ADC_TSYPOSR 0xB8 +#define AT91_ADC_TSPRESSR 0xBC + +#define AT91_ADC_TRGR_9260 AT91_ADC_MR +#define AT91_ADC_TRGR_9G45 0x08 +#define AT91_ADC_TRGR_9X5 0xC0 + +/* Trigger Register bit field */ +#define AT91_ADC_TRGR_TRGPER (0xffff << 16) +#define AT91_ADC_TRGR_TRGPER_(x) ((x) << 16) +#define AT91_ADC_TRGR_TRGMOD (0x7 << 0) +#define AT91_ADC_TRGR_NONE (0 << 0) +#define AT91_ADC_TRGR_MOD_PERIOD_TRIG (5 << 0) + +#define AT91_ADC_CHAN(st, ch) \ + (st->registers->channel_base + (ch * 4)) +#define at91_adc_readl(st, reg) \ + (readl_relaxed(st->reg_base + reg)) +#define at91_adc_writel(st, reg, val) \ + (writel_relaxed(val, st->reg_base + reg)) + +#define DRIVER_NAME "at91_adc" +#define MAX_POS_BITS 12 + +#define TOUCH_SAMPLE_PERIOD_US 2000 /* 2ms */ +#define TOUCH_PEN_DETECT_DEBOUNCE_US 200 + +#define MAX_RLPOS_BITS 10 +#define TOUCH_SAMPLE_PERIOD_US_RL 10000 /* 10ms, the SoC can't keep up with 2ms */ +#define TOUCH_SHTIM 0xa +#define TOUCH_SCTIM_US 10 /* 10us for the Touchscreen Switches Closure Time */ + +enum atmel_adc_ts_type { + ATMEL_ADC_TOUCHSCREEN_NONE = 0, + ATMEL_ADC_TOUCHSCREEN_4WIRE = 4, + ATMEL_ADC_TOUCHSCREEN_5WIRE = 5, +}; + +/** + * struct at91_adc_trigger - description of triggers + * @name: name of the trigger advertised to the user + * @value: value to set in the ADC's trigger setup register + * to enable the trigger + * @is_external: Does the trigger rely on an external pin? + */ +struct at91_adc_trigger { + const char *name; + u8 value; + bool is_external; +}; + +/** + * struct at91_adc_reg_desc - Various information relative to registers + * @channel_base: Base offset for the channel data registers + * @drdy_mask: Mask of the DRDY field in the relevant registers + * (Interruptions registers mostly) + * @status_register: Offset of the Interrupt Status Register + * @trigger_register: Offset of the Trigger setup register + * @mr_prescal_mask: Mask of the PRESCAL field in the adc MR register + * @mr_startup_mask: Mask of the STARTUP field in the adc MR register + */ +struct at91_adc_reg_desc { + u8 channel_base; + u32 drdy_mask; + u8 status_register; + u8 trigger_register; + u32 mr_prescal_mask; + u32 mr_startup_mask; +}; + +struct at91_adc_caps { + bool has_ts; /* Support touch screen */ + bool has_tsmr; /* only at91sam9x5, sama5d3 have TSMR reg */ + /* + * Numbers of sampling data will be averaged. Can be 0~3. + * Hardware can average (2 ^ ts_filter_average) sample data. + */ + u8 ts_filter_average; + /* Pen Detection input pull-up resistor, can be 0~3 */ + u8 ts_pen_detect_sensitivity; + + /* startup time calculate function */ + u32 (*calc_startup_ticks)(u32 startup_time, u32 adc_clk_khz); + + u8 num_channels; + + u8 low_res_bits; + u8 high_res_bits; + u32 trigger_number; + const struct at91_adc_trigger *triggers; + struct at91_adc_reg_desc registers; +}; + +struct at91_adc_state { + struct clk *adc_clk; + u16 *buffer; + unsigned long channels_mask; + struct clk *clk; + bool done; + int irq; + u16 last_value; + int chnb; + struct mutex lock; + u8 num_channels; + void __iomem *reg_base; + const struct at91_adc_reg_desc *registers; + u32 startup_time; + u8 sample_hold_time; + bool sleep_mode; + struct iio_trigger **trig; + bool use_external; + u32 vref_mv; + u32 res; /* resolution used for conversions */ + wait_queue_head_t wq_data_avail; + const struct at91_adc_caps *caps; + + /* + * Following ADC channels are shared by touchscreen: + * + * CH0 -- Touch screen XP/UL + * CH1 -- Touch screen XM/UR + * CH2 -- Touch screen YP/LL + * CH3 -- Touch screen YM/Sense + * CH4 -- Touch screen LR(5-wire only) + * + * The bitfields below represents the reserved channel in the + * touchscreen mode. + */ +#define CHAN_MASK_TOUCHSCREEN_4WIRE (0xf << 0) +#define CHAN_MASK_TOUCHSCREEN_5WIRE (0x1f << 0) + enum atmel_adc_ts_type touchscreen_type; + struct input_dev *ts_input; + + u16 ts_sample_period_val; + u32 ts_pressure_threshold; + u16 ts_pendbc; + + bool ts_bufferedmeasure; + u32 ts_prev_absx; + u32 ts_prev_absy; +}; + +static irqreturn_t at91_adc_trigger_handler(int irq, void *p) +{ + struct iio_poll_func *pf = p; + struct iio_dev *idev = pf->indio_dev; + struct at91_adc_state *st = iio_priv(idev); + struct iio_chan_spec const *chan; + int i, j = 0; + + iio_for_each_active_channel(idev, i) { + chan = idev->channels + i; + st->buffer[j] = at91_adc_readl(st, AT91_ADC_CHAN(st, chan->channel)); + j++; + } + + iio_push_to_buffers_with_timestamp(idev, st->buffer, pf->timestamp); + + iio_trigger_notify_done(idev->trig); + + /* Needed to ACK the DRDY interruption */ + at91_adc_readl(st, AT91_ADC_LCDR); + + enable_irq(st->irq); + + return IRQ_HANDLED; +} + +/* Handler for classic adc channel eoc trigger */ +static void handle_adc_eoc_trigger(int irq, struct iio_dev *idev) +{ + struct at91_adc_state *st = iio_priv(idev); + + if (iio_buffer_enabled(idev)) { + disable_irq_nosync(irq); + iio_trigger_poll(idev->trig); + } else { + st->last_value = at91_adc_readl(st, AT91_ADC_CHAN(st, st->chnb)); + /* Needed to ACK the DRDY interruption */ + at91_adc_readl(st, AT91_ADC_LCDR); + st->done = true; + wake_up_interruptible(&st->wq_data_avail); + } +} + +static void at91_ts_sample(struct iio_dev *idev) +{ + struct at91_adc_state *st = iio_priv(idev); + unsigned int xscale, yscale, reg, z1, z2; + unsigned int x, y, pres, xpos, ypos; + unsigned int rxp = 1; + unsigned int factor = 1000; + + unsigned int xyz_mask_bits = st->res; + unsigned int xyz_mask = (1 << xyz_mask_bits) - 1; + + /* calculate position */ + /* x position = (x / xscale) * max, max = 2^MAX_POS_BITS - 1 */ + reg = at91_adc_readl(st, AT91_ADC_TSXPOSR); + xpos = reg & xyz_mask; + x = (xpos << MAX_POS_BITS) - xpos; + xscale = (reg >> 16) & xyz_mask; + if (xscale == 0) { + dev_err(&idev->dev, "Error: xscale == 0!\n"); + return; + } + x /= xscale; + + /* y position = (y / yscale) * max, max = 2^MAX_POS_BITS - 1 */ + reg = at91_adc_readl(st, AT91_ADC_TSYPOSR); + ypos = reg & xyz_mask; + y = (ypos << MAX_POS_BITS) - ypos; + yscale = (reg >> 16) & xyz_mask; + if (yscale == 0) { + dev_err(&idev->dev, "Error: yscale == 0!\n"); + return; + } + y /= yscale; + + /* calculate the pressure */ + reg = at91_adc_readl(st, AT91_ADC_TSPRESSR); + z1 = reg & xyz_mask; + z2 = (reg >> 16) & xyz_mask; + + if (z1 != 0) + pres = rxp * (x * factor / 1024) * (z2 * factor / z1 - factor) + / factor; + else + pres = st->ts_pressure_threshold; /* no pen contacted */ + + dev_dbg(&idev->dev, "xpos = %d, xscale = %d, ypos = %d, yscale = %d, z1 = %d, z2 = %d, press = %d\n", + xpos, xscale, ypos, yscale, z1, z2, pres); + + if (pres < st->ts_pressure_threshold) { + dev_dbg(&idev->dev, "x = %d, y = %d, pressure = %d\n", + x, y, pres / factor); + input_report_abs(st->ts_input, ABS_X, x); + input_report_abs(st->ts_input, ABS_Y, y); + input_report_abs(st->ts_input, ABS_PRESSURE, pres); + input_report_key(st->ts_input, BTN_TOUCH, 1); + input_sync(st->ts_input); + } else { + dev_dbg(&idev->dev, "pressure too low: not reporting\n"); + } +} + +static irqreturn_t at91_adc_rl_interrupt(int irq, void *private) +{ + struct iio_dev *idev = private; + struct at91_adc_state *st = iio_priv(idev); + u32 status = at91_adc_readl(st, st->registers->status_register); + unsigned int reg; + + status &= at91_adc_readl(st, AT91_ADC_IMR); + if (status & GENMASK(st->num_channels - 1, 0)) + handle_adc_eoc_trigger(irq, idev); + + if (status & AT91RL_ADC_IER_PEN) { + /* Disabling pen debounce is required to get a NOPEN irq */ + reg = at91_adc_readl(st, AT91_ADC_MR); + reg &= ~AT91_ADC_PENDBC; + at91_adc_writel(st, AT91_ADC_MR, reg); + + at91_adc_writel(st, AT91_ADC_IDR, AT91RL_ADC_IER_PEN); + at91_adc_writel(st, AT91_ADC_IER, AT91RL_ADC_IER_NOPEN + | AT91_ADC_EOC(3)); + /* Set up period trigger for sampling */ + at91_adc_writel(st, st->registers->trigger_register, + AT91_ADC_TRGR_MOD_PERIOD_TRIG | + AT91_ADC_TRGR_TRGPER_(st->ts_sample_period_val)); + } else if (status & AT91RL_ADC_IER_NOPEN) { + reg = at91_adc_readl(st, AT91_ADC_MR); + reg |= AT91_ADC_PENDBC_(st->ts_pendbc) & AT91_ADC_PENDBC; + at91_adc_writel(st, AT91_ADC_MR, reg); + at91_adc_writel(st, st->registers->trigger_register, + AT91_ADC_TRGR_NONE); + + at91_adc_writel(st, AT91_ADC_IDR, AT91RL_ADC_IER_NOPEN + | AT91_ADC_EOC(3)); + at91_adc_writel(st, AT91_ADC_IER, AT91RL_ADC_IER_PEN); + st->ts_bufferedmeasure = false; + input_report_key(st->ts_input, BTN_TOUCH, 0); + input_sync(st->ts_input); + } else if (status & AT91_ADC_EOC(3) && st->ts_input) { + /* Conversion finished and we've a touchscreen */ + if (st->ts_bufferedmeasure) { + /* + * Last measurement is always discarded, since it can + * be erroneous. + * Always report previous measurement + */ + input_report_abs(st->ts_input, ABS_X, st->ts_prev_absx); + input_report_abs(st->ts_input, ABS_Y, st->ts_prev_absy); + input_report_key(st->ts_input, BTN_TOUCH, 1); + input_sync(st->ts_input); + } else + st->ts_bufferedmeasure = true; + + /* Now make new measurement */ + st->ts_prev_absx = at91_adc_readl(st, AT91_ADC_CHAN(st, 3)) + << MAX_RLPOS_BITS; + st->ts_prev_absx /= at91_adc_readl(st, AT91_ADC_CHAN(st, 2)); + + st->ts_prev_absy = at91_adc_readl(st, AT91_ADC_CHAN(st, 1)) + << MAX_RLPOS_BITS; + st->ts_prev_absy /= at91_adc_readl(st, AT91_ADC_CHAN(st, 0)); + } + + return IRQ_HANDLED; +} + +static irqreturn_t at91_adc_9x5_interrupt(int irq, void *private) +{ + struct iio_dev *idev = private; + struct at91_adc_state *st = iio_priv(idev); + u32 status = at91_adc_readl(st, st->registers->status_register); + const uint32_t ts_data_irq_mask = + AT91_ADC_IER_XRDY | + AT91_ADC_IER_YRDY | + AT91_ADC_IER_PRDY; + + if (status & GENMASK(st->num_channels - 1, 0)) + handle_adc_eoc_trigger(irq, idev); + + if (status & AT91_ADC_IER_PEN) { + at91_adc_writel(st, AT91_ADC_IDR, AT91_ADC_IER_PEN); + at91_adc_writel(st, AT91_ADC_IER, AT91_ADC_IER_NOPEN | + ts_data_irq_mask); + /* Set up period trigger for sampling */ + at91_adc_writel(st, st->registers->trigger_register, + AT91_ADC_TRGR_MOD_PERIOD_TRIG | + AT91_ADC_TRGR_TRGPER_(st->ts_sample_period_val)); + } else if (status & AT91_ADC_IER_NOPEN) { + at91_adc_writel(st, st->registers->trigger_register, 0); + at91_adc_writel(st, AT91_ADC_IDR, AT91_ADC_IER_NOPEN | + ts_data_irq_mask); + at91_adc_writel(st, AT91_ADC_IER, AT91_ADC_IER_PEN); + + input_report_key(st->ts_input, BTN_TOUCH, 0); + input_sync(st->ts_input); + } else if ((status & ts_data_irq_mask) == ts_data_irq_mask) { + /* Now all touchscreen data is ready */ + + if (status & AT91_ADC_ISR_PENS) { + /* validate data by pen contact */ + at91_ts_sample(idev); + } else { + /* triggered by event that is no pen contact, just read + * them to clean the interrupt and discard all. + */ + at91_adc_readl(st, AT91_ADC_TSXPOSR); + at91_adc_readl(st, AT91_ADC_TSYPOSR); + at91_adc_readl(st, AT91_ADC_TSPRESSR); + } + } + + return IRQ_HANDLED; +} + +static int at91_adc_channel_init(struct iio_dev *idev) +{ + struct at91_adc_state *st = iio_priv(idev); + struct iio_chan_spec *chan_array; + int bit, idx = 0; + unsigned long rsvd_mask = 0; + + /* If touchscreen is enable, then reserve the adc channels */ + if (st->touchscreen_type == ATMEL_ADC_TOUCHSCREEN_4WIRE) + rsvd_mask = CHAN_MASK_TOUCHSCREEN_4WIRE; + else if (st->touchscreen_type == ATMEL_ADC_TOUCHSCREEN_5WIRE) + rsvd_mask = CHAN_MASK_TOUCHSCREEN_5WIRE; + + /* set up the channel mask to reserve touchscreen channels */ + st->channels_mask &= ~rsvd_mask; + + idev->num_channels = bitmap_weight(&st->channels_mask, + st->num_channels) + 1; + + chan_array = devm_kzalloc(&idev->dev, + ((idev->num_channels + 1) * + sizeof(struct iio_chan_spec)), + GFP_KERNEL); + + if (!chan_array) + return -ENOMEM; + + for_each_set_bit(bit, &st->channels_mask, st->num_channels) { + struct iio_chan_spec *chan = chan_array + idx; + + chan->type = IIO_VOLTAGE; + chan->indexed = 1; + chan->channel = bit; + chan->scan_index = idx; + chan->scan_type.sign = 'u'; + chan->scan_type.realbits = st->res; + chan->scan_type.storagebits = 16; + chan->info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE); + chan->info_mask_separate = BIT(IIO_CHAN_INFO_RAW); + idx++; + } + + chan_array[idx] = IIO_CHAN_SOFT_TIMESTAMP(idx); + + idev->channels = chan_array; + return idev->num_channels; +} + +static int at91_adc_get_trigger_value_by_name(struct iio_dev *idev, + const struct at91_adc_trigger *triggers, + const char *trigger_name) +{ + struct at91_adc_state *st = iio_priv(idev); + int i; + + for (i = 0; i < st->caps->trigger_number; i++) { + char *name __free(kfree) = kasprintf(GFP_KERNEL, "%s-dev%d-%s", + idev->name, + iio_device_id(idev), + triggers[i].name); + if (!name) + return -ENOMEM; + + if (strcmp(trigger_name, name) == 0) { + if (triggers[i].value == 0) + return -EINVAL; + return triggers[i].value; + } + } + + return -EINVAL; +} + +static int at91_adc_configure_trigger(struct iio_trigger *trig, bool state) +{ + struct iio_dev *idev = iio_trigger_get_drvdata(trig); + struct at91_adc_state *st = iio_priv(idev); + const struct at91_adc_reg_desc *reg = st->registers; + u32 status = at91_adc_readl(st, reg->trigger_register); + int value; + u8 bit; + + value = at91_adc_get_trigger_value_by_name(idev, + st->caps->triggers, + idev->trig->name); + if (value < 0) + return value; + + if (state) { + st->buffer = kmalloc(idev->scan_bytes, GFP_KERNEL); + if (st->buffer == NULL) + return -ENOMEM; + + at91_adc_writel(st, reg->trigger_register, + status | value); + + for_each_set_bit(bit, idev->active_scan_mask, + st->num_channels) { + struct iio_chan_spec const *chan = idev->channels + bit; + at91_adc_writel(st, AT91_ADC_CHER, + AT91_ADC_CH(chan->channel)); + } + + at91_adc_writel(st, AT91_ADC_IER, reg->drdy_mask); + + } else { + at91_adc_writel(st, AT91_ADC_IDR, reg->drdy_mask); + + at91_adc_writel(st, reg->trigger_register, + status & ~value); + + for_each_set_bit(bit, idev->active_scan_mask, + st->num_channels) { + struct iio_chan_spec const *chan = idev->channels + bit; + at91_adc_writel(st, AT91_ADC_CHDR, + AT91_ADC_CH(chan->channel)); + } + kfree(st->buffer); + } + + return 0; +} + +static const struct iio_trigger_ops at91_adc_trigger_ops = { + .set_trigger_state = &at91_adc_configure_trigger, +}; + +static struct iio_trigger *at91_adc_allocate_trigger(struct iio_dev *idev, + const struct at91_adc_trigger *trigger) +{ + struct iio_trigger *trig; + int ret; + + trig = iio_trigger_alloc(idev->dev.parent, "%s-dev%d-%s", idev->name, + iio_device_id(idev), trigger->name); + if (trig == NULL) + return NULL; + + iio_trigger_set_drvdata(trig, idev); + trig->ops = &at91_adc_trigger_ops; + + ret = iio_trigger_register(trig); + if (ret) { + iio_trigger_free(trig); + return NULL; + } + + return trig; +} + +static int at91_adc_trigger_init(struct iio_dev *idev) +{ + struct at91_adc_state *st = iio_priv(idev); + int i, ret; + + st->trig = devm_kcalloc(&idev->dev, + st->caps->trigger_number, sizeof(*st->trig), + GFP_KERNEL); + + if (st->trig == NULL) { + ret = -ENOMEM; + goto error_ret; + } + + for (i = 0; i < st->caps->trigger_number; i++) { + if (st->caps->triggers[i].is_external && !(st->use_external)) + continue; + + st->trig[i] = at91_adc_allocate_trigger(idev, + st->caps->triggers + i); + if (st->trig[i] == NULL) { + dev_err(&idev->dev, + "Could not allocate trigger %d\n", i); + ret = -ENOMEM; + goto error_trigger; + } + } + + return 0; + +error_trigger: + for (i--; i >= 0; i--) { + iio_trigger_unregister(st->trig[i]); + iio_trigger_free(st->trig[i]); + } +error_ret: + return ret; +} + +static void at91_adc_trigger_remove(struct iio_dev *idev) +{ + struct at91_adc_state *st = iio_priv(idev); + int i; + + for (i = 0; i < st->caps->trigger_number; i++) { + iio_trigger_unregister(st->trig[i]); + iio_trigger_free(st->trig[i]); + } +} + +static int at91_adc_buffer_init(struct iio_dev *idev) +{ + return iio_triggered_buffer_setup(idev, &iio_pollfunc_store_time, + &at91_adc_trigger_handler, NULL); +} + +static void at91_adc_buffer_remove(struct iio_dev *idev) +{ + iio_triggered_buffer_cleanup(idev); +} + +static int at91_adc_read_raw(struct iio_dev *idev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct at91_adc_state *st = iio_priv(idev); + int ret; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + mutex_lock(&st->lock); + + st->chnb = chan->channel; + at91_adc_writel(st, AT91_ADC_CHER, + AT91_ADC_CH(chan->channel)); + at91_adc_writel(st, AT91_ADC_IER, BIT(chan->channel)); + at91_adc_writel(st, AT91_ADC_CR, AT91_ADC_START); + + ret = wait_event_interruptible_timeout(st->wq_data_avail, + st->done, + msecs_to_jiffies(1000)); + + /* Disable interrupts, regardless if adc conversion was + * successful or not + */ + at91_adc_writel(st, AT91_ADC_CHDR, + AT91_ADC_CH(chan->channel)); + at91_adc_writel(st, AT91_ADC_IDR, BIT(chan->channel)); + + if (ret > 0) { + /* a valid conversion took place */ + *val = st->last_value; + st->last_value = 0; + st->done = false; + ret = IIO_VAL_INT; + } else if (ret == 0) { + /* conversion timeout */ + dev_err(&idev->dev, "ADC Channel %d timeout.\n", + chan->channel); + ret = -ETIMEDOUT; + } + + mutex_unlock(&st->lock); + return ret; + + case IIO_CHAN_INFO_SCALE: + *val = st->vref_mv; + *val2 = chan->scan_type.realbits; + return IIO_VAL_FRACTIONAL_LOG2; + default: + break; + } + return -EINVAL; +} + + +static u32 calc_startup_ticks_9260(u32 startup_time, u32 adc_clk_khz) +{ + /* + * Number of ticks needed to cover the startup time of the ADC + * as defined in the electrical characteristics of the board, + * divided by 8. The formula thus is : + * Startup Time = (ticks + 1) * 8 / ADC Clock + */ + return round_up((startup_time * adc_clk_khz / 1000) - 1, 8) / 8; +} + +static u32 calc_startup_ticks_9x5(u32 startup_time, u32 adc_clk_khz) +{ + /* + * For sama5d3x and at91sam9x5, the formula changes to: + * Startup Time = / ADC Clock + */ + static const int startup_lookup[] = { + 0, 8, 16, 24, + 64, 80, 96, 112, + 512, 576, 640, 704, + 768, 832, 896, 960 + }; + int i, size = ARRAY_SIZE(startup_lookup); + unsigned int ticks; + + ticks = startup_time * adc_clk_khz / 1000; + for (i = 0; i < size; i++) + if (ticks < startup_lookup[i]) + break; + + ticks = i; + if (ticks == size) + /* Reach the end of lookup table */ + ticks = size - 1; + + return ticks; +} + +static int at91_adc_probe_dt_ts(struct device_node *node, + struct at91_adc_state *st, struct device *dev) +{ + int ret; + u32 prop; + + ret = of_property_read_u32(node, "atmel,adc-ts-wires", &prop); + if (ret) { + dev_info(dev, "ADC Touch screen is disabled.\n"); + return 0; + } + + switch (prop) { + case 4: + case 5: + st->touchscreen_type = prop; + break; + default: + dev_err(dev, "Unsupported number of touchscreen wires (%d). Should be 4 or 5.\n", prop); + return -EINVAL; + } + + if (!st->caps->has_tsmr) + return 0; + prop = 0; + of_property_read_u32(node, "atmel,adc-ts-pressure-threshold", &prop); + st->ts_pressure_threshold = prop; + if (st->ts_pressure_threshold) { + return 0; + } else { + dev_err(dev, "Invalid pressure threshold for the touchscreen\n"); + return -EINVAL; + } +} + +static const struct iio_info at91_adc_info = { + .read_raw = &at91_adc_read_raw, +}; + +/* Touchscreen related functions */ +static int atmel_ts_open(struct input_dev *dev) +{ + struct at91_adc_state *st = input_get_drvdata(dev); + + if (st->caps->has_tsmr) + at91_adc_writel(st, AT91_ADC_IER, AT91_ADC_IER_PEN); + else + at91_adc_writel(st, AT91_ADC_IER, AT91RL_ADC_IER_PEN); + return 0; +} + +static void atmel_ts_close(struct input_dev *dev) +{ + struct at91_adc_state *st = input_get_drvdata(dev); + + if (st->caps->has_tsmr) + at91_adc_writel(st, AT91_ADC_IDR, AT91_ADC_IER_PEN); + else + at91_adc_writel(st, AT91_ADC_IDR, AT91RL_ADC_IER_PEN); +} + +static int at91_ts_hw_init(struct iio_dev *idev, u32 adc_clk_khz) +{ + struct at91_adc_state *st = iio_priv(idev); + u32 reg = 0; + u32 tssctim = 0; + int i = 0; + + /* a Pen Detect Debounce Time is necessary for the ADC Touch to avoid + * pen detect noise. + * The formula is : Pen Detect Debounce Time = (2 ^ pendbc) / ADCClock + */ + st->ts_pendbc = round_up(TOUCH_PEN_DETECT_DEBOUNCE_US * adc_clk_khz / + 1000, 1); + + while (st->ts_pendbc >> ++i) + ; /* Empty! Find the shift offset */ + if (abs(st->ts_pendbc - (1 << i)) < abs(st->ts_pendbc - (1 << (i - 1)))) + st->ts_pendbc = i; + else + st->ts_pendbc = i - 1; + + if (!st->caps->has_tsmr) { + reg = at91_adc_readl(st, AT91_ADC_MR); + reg |= AT91_ADC_TSAMOD_TS_ONLY_MODE | AT91_ADC_PENDET; + + reg |= AT91_ADC_PENDBC_(st->ts_pendbc) & AT91_ADC_PENDBC; + at91_adc_writel(st, AT91_ADC_MR, reg); + + reg = AT91_ADC_TSR_SHTIM_(TOUCH_SHTIM) & AT91_ADC_TSR_SHTIM; + at91_adc_writel(st, AT91_ADC_TSR, reg); + + st->ts_sample_period_val = round_up((TOUCH_SAMPLE_PERIOD_US_RL * + adc_clk_khz / 1000) - 1, 1); + + return 0; + } + + /* Touchscreen Switches Closure time needed for allowing the value to + * stabilize. + * Switch Closure Time = (TSSCTIM * 4) ADCClock periods + */ + tssctim = DIV_ROUND_UP(TOUCH_SCTIM_US * adc_clk_khz / 1000, 4); + dev_dbg(&idev->dev, "adc_clk at: %d KHz, tssctim at: %d\n", + adc_clk_khz, tssctim); + + if (st->touchscreen_type == ATMEL_ADC_TOUCHSCREEN_4WIRE) + reg = AT91_ADC_TSMR_TSMODE_4WIRE_PRESS; + else + reg = AT91_ADC_TSMR_TSMODE_5WIRE; + + reg |= AT91_ADC_TSMR_SCTIM_(tssctim) & AT91_ADC_TSMR_SCTIM; + reg |= AT91_ADC_TSMR_TSAV_(st->caps->ts_filter_average) + & AT91_ADC_TSMR_TSAV; + reg |= AT91_ADC_TSMR_PENDBC_(st->ts_pendbc) & AT91_ADC_TSMR_PENDBC; + reg |= AT91_ADC_TSMR_NOTSDMA; + reg |= AT91_ADC_TSMR_PENDET_ENA; + reg |= 0x03 << 8; /* TSFREQ, needs to be bigger than TSAV */ + + at91_adc_writel(st, AT91_ADC_TSMR, reg); + + /* Change adc internal resistor value for better pen detection, + * default value is 100 kOhm. + * 0 = 200 kOhm, 1 = 150 kOhm, 2 = 100 kOhm, 3 = 50 kOhm + * option only available on ES2 and higher + */ + at91_adc_writel(st, AT91_ADC_ACR, st->caps->ts_pen_detect_sensitivity + & AT91_ADC_ACR_PENDETSENS); + + /* Sample Period Time = (TRGPER + 1) / ADCClock */ + st->ts_sample_period_val = round_up((TOUCH_SAMPLE_PERIOD_US * + adc_clk_khz / 1000) - 1, 1); + + return 0; +} + +static int at91_ts_register(struct iio_dev *idev, + struct platform_device *pdev) +{ + struct at91_adc_state *st = iio_priv(idev); + struct input_dev *input; + int ret; + + input = input_allocate_device(); + if (!input) { + dev_err(&idev->dev, "Failed to allocate TS device!\n"); + return -ENOMEM; + } + + input->name = DRIVER_NAME; + input->id.bustype = BUS_HOST; + input->dev.parent = &pdev->dev; + input->open = atmel_ts_open; + input->close = atmel_ts_close; + + __set_bit(EV_ABS, input->evbit); + __set_bit(EV_KEY, input->evbit); + __set_bit(BTN_TOUCH, input->keybit); + if (st->caps->has_tsmr) { + input_set_abs_params(input, ABS_X, 0, (1 << MAX_POS_BITS) - 1, + 0, 0); + input_set_abs_params(input, ABS_Y, 0, (1 << MAX_POS_BITS) - 1, + 0, 0); + input_set_abs_params(input, ABS_PRESSURE, 0, 0xffffff, 0, 0); + } else { + if (st->touchscreen_type != ATMEL_ADC_TOUCHSCREEN_4WIRE) { + dev_err(&pdev->dev, + "This touchscreen controller only support 4 wires\n"); + ret = -EINVAL; + goto err; + } + + input_set_abs_params(input, ABS_X, 0, (1 << MAX_RLPOS_BITS) - 1, + 0, 0); + input_set_abs_params(input, ABS_Y, 0, (1 << MAX_RLPOS_BITS) - 1, + 0, 0); + } + + st->ts_input = input; + input_set_drvdata(input, st); + + ret = input_register_device(input); + if (ret) + goto err; + + return ret; + +err: + input_free_device(input); + return ret; +} + +static void at91_ts_unregister(struct at91_adc_state *st) +{ + input_unregister_device(st->ts_input); +} + +static int at91_adc_probe(struct platform_device *pdev) +{ + unsigned int prsc, mstrclk, ticks, adc_clk, adc_clk_khz, shtim; + struct device_node *node = pdev->dev.of_node; + int ret; + struct iio_dev *idev; + struct at91_adc_state *st; + u32 reg, prop; + const char *s; + + idev = devm_iio_device_alloc(&pdev->dev, sizeof(struct at91_adc_state)); + if (!idev) + return -ENOMEM; + + st = iio_priv(idev); + + st->caps = of_device_get_match_data(&pdev->dev); + + st->use_external = of_property_read_bool(node, "atmel,adc-use-external-triggers"); + + if (of_property_read_u32(node, "atmel,adc-channels-used", &prop)) + return dev_err_probe(&idev->dev, -EINVAL, + "Missing adc-channels-used property in the DT.\n"); + st->channels_mask = prop; + + st->sleep_mode = of_property_read_bool(node, "atmel,adc-sleep-mode"); + + if (of_property_read_u32(node, "atmel,adc-startup-time", &prop)) + return dev_err_probe(&idev->dev, -EINVAL, + "Missing adc-startup-time property in the DT.\n"); + st->startup_time = prop; + + prop = 0; + of_property_read_u32(node, "atmel,adc-sample-hold-time", &prop); + st->sample_hold_time = prop; + + if (of_property_read_u32(node, "atmel,adc-vref", &prop)) + return dev_err_probe(&idev->dev, -EINVAL, + "Missing adc-vref property in the DT.\n"); + st->vref_mv = prop; + + st->res = st->caps->high_res_bits; + if (st->caps->low_res_bits && + !of_property_read_string(node, "atmel,adc-use-res", &s) + && !strcmp(s, "lowres")) + st->res = st->caps->low_res_bits; + + dev_info(&idev->dev, "Resolution used: %u bits\n", st->res); + + st->registers = &st->caps->registers; + st->num_channels = st->caps->num_channels; + + /* Check if touchscreen is supported. */ + if (st->caps->has_ts) { + ret = at91_adc_probe_dt_ts(node, st, &idev->dev); + if (ret) + return ret; + } + + platform_set_drvdata(pdev, idev); + + idev->name = dev_name(&pdev->dev); + idev->modes = INDIO_DIRECT_MODE; + idev->info = &at91_adc_info; + + st->irq = platform_get_irq(pdev, 0); + if (st->irq < 0) + return -ENODEV; + + st->reg_base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(st->reg_base)) + return PTR_ERR(st->reg_base); + + /* + * Disable all IRQs before setting up the handler + */ + at91_adc_writel(st, AT91_ADC_CR, AT91_ADC_SWRST); + at91_adc_writel(st, AT91_ADC_IDR, 0xFFFFFFFF); + + if (st->caps->has_tsmr) + ret = devm_request_irq(&pdev->dev, st->irq, + at91_adc_9x5_interrupt, 0, + pdev->dev.driver->name, idev); + else + ret = devm_request_irq(&pdev->dev, st->irq, + at91_adc_rl_interrupt, 0, + pdev->dev.driver->name, idev); + if (ret) + return dev_err_probe(&pdev->dev, ret, + "Failed to allocate IRQ.\n"); + + st->clk = devm_clk_get_enabled(&pdev->dev, "adc_clk"); + if (IS_ERR(st->clk)) + return dev_err_probe(&pdev->dev, PTR_ERR(st->clk), + "Could not prepare or enable the clock.\n"); + + st->adc_clk = devm_clk_get_enabled(&pdev->dev, "adc_op_clk"); + if (IS_ERR(st->adc_clk)) + return dev_err_probe(&pdev->dev, PTR_ERR(st->adc_clk), + "Could not prepare or enable the ADC clock.\n"); + + /* + * Prescaler rate computation using the formula from the Atmel's + * datasheet : ADC Clock = MCK / ((Prescaler + 1) * 2), ADC Clock being + * specified by the electrical characteristics of the board. + */ + mstrclk = clk_get_rate(st->clk); + adc_clk = clk_get_rate(st->adc_clk); + adc_clk_khz = adc_clk / 1000; + + dev_dbg(&pdev->dev, "Master clock is set as: %d Hz, adc_clk should set as: %d Hz\n", + mstrclk, adc_clk); + + prsc = (mstrclk / (2 * adc_clk)) - 1; + + if (!st->startup_time) + return dev_err_probe(&pdev->dev, -EINVAL, + "No startup time available.\n"); + ticks = (*st->caps->calc_startup_ticks)(st->startup_time, adc_clk_khz); + + /* + * a minimal Sample and Hold Time is necessary for the ADC to guarantee + * the best converted final value between two channels selection + * The formula thus is : Sample and Hold Time = (shtim + 1) / ADCClock + */ + if (st->sample_hold_time > 0) + shtim = round_up((st->sample_hold_time * adc_clk_khz / 1000) + - 1, 1); + else + shtim = 0; + + reg = AT91_ADC_PRESCAL_(prsc) & st->registers->mr_prescal_mask; + reg |= AT91_ADC_STARTUP_(ticks) & st->registers->mr_startup_mask; + if (st->res == st->caps->low_res_bits) + reg |= AT91_ADC_LOWRES; + if (st->sleep_mode) + reg |= AT91_ADC_SLEEP; + reg |= AT91_ADC_SHTIM_(shtim) & AT91_ADC_SHTIM; + at91_adc_writel(st, AT91_ADC_MR, reg); + + /* Setup the ADC channels available on the board */ + ret = at91_adc_channel_init(idev); + if (ret < 0) + return dev_err_probe(&pdev->dev, ret, + "Couldn't initialize the channels.\n"); + + init_waitqueue_head(&st->wq_data_avail); + mutex_init(&st->lock); + + /* + * Since touch screen will set trigger register as period trigger. So + * when touch screen is enabled, then we have to disable hardware + * trigger for classic adc. + */ + if (!st->touchscreen_type) { + ret = at91_adc_buffer_init(idev); + if (ret < 0) + return dev_err_probe(&pdev->dev, ret, + "Couldn't initialize the buffer.\n"); + + ret = at91_adc_trigger_init(idev); + if (ret < 0) { + dev_err(&pdev->dev, "Couldn't setup the triggers.\n"); + at91_adc_buffer_remove(idev); + return ret; + } + } else { + ret = at91_ts_register(idev, pdev); + if (ret) + return ret; + + at91_ts_hw_init(idev, adc_clk_khz); + } + + ret = iio_device_register(idev); + if (ret < 0) { + dev_err(&pdev->dev, "Couldn't register the device.\n"); + goto error_iio_device_register; + } + + return 0; + +error_iio_device_register: + if (!st->touchscreen_type) { + at91_adc_trigger_remove(idev); + at91_adc_buffer_remove(idev); + } else { + at91_ts_unregister(st); + } + return ret; +} + +static void at91_adc_remove(struct platform_device *pdev) +{ + struct iio_dev *idev = platform_get_drvdata(pdev); + struct at91_adc_state *st = iio_priv(idev); + + iio_device_unregister(idev); + if (!st->touchscreen_type) { + at91_adc_trigger_remove(idev); + at91_adc_buffer_remove(idev); + } else { + at91_ts_unregister(st); + } +} + +static int at91_adc_suspend(struct device *dev) +{ + struct iio_dev *idev = dev_get_drvdata(dev); + struct at91_adc_state *st = iio_priv(idev); + + pinctrl_pm_select_sleep_state(dev); + clk_disable_unprepare(st->clk); + + return 0; +} + +static int at91_adc_resume(struct device *dev) +{ + struct iio_dev *idev = dev_get_drvdata(dev); + struct at91_adc_state *st = iio_priv(idev); + + clk_prepare_enable(st->clk); + pinctrl_pm_select_default_state(dev); + + return 0; +} + +static DEFINE_SIMPLE_DEV_PM_OPS(at91_adc_pm_ops, at91_adc_suspend, + at91_adc_resume); + +static const struct at91_adc_trigger at91sam9260_triggers[] = { + { .name = "timer-counter-0", .value = 0x1 }, + { .name = "timer-counter-1", .value = 0x3 }, + { .name = "timer-counter-2", .value = 0x5 }, + { .name = "external", .value = 0xd, .is_external = true }, +}; + +static const struct at91_adc_caps at91sam9260_caps = { + .calc_startup_ticks = calc_startup_ticks_9260, + .num_channels = 4, + .low_res_bits = 8, + .high_res_bits = 10, + .registers = { + .channel_base = AT91_ADC_CHR(0), + .drdy_mask = AT91_ADC_DRDY, + .status_register = AT91_ADC_SR, + .trigger_register = AT91_ADC_TRGR_9260, + .mr_prescal_mask = AT91_ADC_PRESCAL_9260, + .mr_startup_mask = AT91_ADC_STARTUP_9260, + }, + .triggers = at91sam9260_triggers, + .trigger_number = ARRAY_SIZE(at91sam9260_triggers), +}; + +static const struct at91_adc_trigger at91sam9x5_triggers[] = { + { .name = "external-rising", .value = 0x1, .is_external = true }, + { .name = "external-falling", .value = 0x2, .is_external = true }, + { .name = "external-any", .value = 0x3, .is_external = true }, + { .name = "continuous", .value = 0x6 }, +}; + +static const struct at91_adc_caps at91sam9rl_caps = { + .has_ts = true, + .calc_startup_ticks = calc_startup_ticks_9260, /* same as 9260 */ + .num_channels = 6, + .low_res_bits = 8, + .high_res_bits = 10, + .registers = { + .channel_base = AT91_ADC_CHR(0), + .drdy_mask = AT91_ADC_DRDY, + .status_register = AT91_ADC_SR, + .trigger_register = AT91_ADC_TRGR_9G45, + .mr_prescal_mask = AT91_ADC_PRESCAL_9260, + .mr_startup_mask = AT91_ADC_STARTUP_9G45, + }, + .triggers = at91sam9x5_triggers, + .trigger_number = ARRAY_SIZE(at91sam9x5_triggers), +}; + +static const struct at91_adc_caps at91sam9g45_caps = { + .has_ts = true, + .calc_startup_ticks = calc_startup_ticks_9260, /* same as 9260 */ + .num_channels = 8, + .low_res_bits = 8, + .high_res_bits = 10, + .registers = { + .channel_base = AT91_ADC_CHR(0), + .drdy_mask = AT91_ADC_DRDY, + .status_register = AT91_ADC_SR, + .trigger_register = AT91_ADC_TRGR_9G45, + .mr_prescal_mask = AT91_ADC_PRESCAL_9G45, + .mr_startup_mask = AT91_ADC_STARTUP_9G45, + }, + .triggers = at91sam9x5_triggers, + .trigger_number = ARRAY_SIZE(at91sam9x5_triggers), +}; + +static const struct at91_adc_caps at91sam9x5_caps = { + .has_ts = true, + .has_tsmr = true, + .ts_filter_average = 3, + .ts_pen_detect_sensitivity = 2, + .calc_startup_ticks = calc_startup_ticks_9x5, + .num_channels = 12, + .low_res_bits = 8, + .high_res_bits = 10, + .registers = { + .channel_base = AT91_ADC_CDR0_9X5, + .drdy_mask = AT91_ADC_SR_DRDY_9X5, + .status_register = AT91_ADC_SR_9X5, + .trigger_register = AT91_ADC_TRGR_9X5, + /* prescal mask is same as 9G45 */ + .mr_prescal_mask = AT91_ADC_PRESCAL_9G45, + .mr_startup_mask = AT91_ADC_STARTUP_9X5, + }, + .triggers = at91sam9x5_triggers, + .trigger_number = ARRAY_SIZE(at91sam9x5_triggers), +}; + +static const struct at91_adc_caps sama5d3_caps = { + .has_ts = true, + .has_tsmr = true, + .ts_filter_average = 3, + .ts_pen_detect_sensitivity = 2, + .calc_startup_ticks = calc_startup_ticks_9x5, + .num_channels = 12, + .low_res_bits = 0, + .high_res_bits = 12, + .registers = { + .channel_base = AT91_ADC_CDR0_9X5, + .drdy_mask = AT91_ADC_SR_DRDY_9X5, + .status_register = AT91_ADC_SR_9X5, + .trigger_register = AT91_ADC_TRGR_9X5, + .mr_prescal_mask = AT91_ADC_PRESCAL_9G45, + .mr_startup_mask = AT91_ADC_STARTUP_9X5, + }, + .triggers = at91sam9x5_triggers, + .trigger_number = ARRAY_SIZE(at91sam9x5_triggers), +}; + +static const struct of_device_id at91_adc_dt_ids[] = { + { .compatible = "atmel,at91sam9260-adc", .data = &at91sam9260_caps }, + { .compatible = "atmel,at91sam9rl-adc", .data = &at91sam9rl_caps }, + { .compatible = "atmel,at91sam9g45-adc", .data = &at91sam9g45_caps }, + { .compatible = "atmel,at91sam9x5-adc", .data = &at91sam9x5_caps }, + { .compatible = "atmel,sama5d3-adc", .data = &sama5d3_caps }, + { } +}; +MODULE_DEVICE_TABLE(of, at91_adc_dt_ids); + +static struct platform_driver at91_adc_driver = { + .probe = at91_adc_probe, + .remove = at91_adc_remove, + .driver = { + .name = DRIVER_NAME, + .of_match_table = at91_adc_dt_ids, + .pm = pm_sleep_ptr(&at91_adc_pm_ops), + }, +}; + +module_platform_driver(at91_adc_driver); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Atmel AT91 ADC Driver"); +MODULE_AUTHOR("Maxime Ripard "); diff --git a/drivers/iio/adc/axp20x_adc.c b/drivers/iio/adc/axp20x_adc.c new file mode 100644 index 000000000..d9016fe1a --- /dev/null +++ b/drivers/iio/adc/axp20x_adc.c @@ -0,0 +1,1192 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* ADC driver for AXP20X and AXP22X PMICs + * + * Copyright (c) 2016 Free Electrons NextThing Co. + * Quentin Schulz + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#define AXP192_ADC_EN1_MASK GENMASK(7, 0) +#define AXP192_ADC_EN2_MASK (GENMASK(3, 0) | BIT(7)) + +#define AXP20X_ADC_EN1_MASK GENMASK(7, 0) +#define AXP20X_ADC_EN2_MASK (GENMASK(3, 2) | BIT(7)) + +#define AXP22X_ADC_EN1_MASK (GENMASK(7, 5) | BIT(0)) + +#define AXP717_ADC_EN1_MASK GENMASK(7, 0) + +#define AXP192_GPIO30_IN_RANGE_GPIO0 BIT(0) +#define AXP192_GPIO30_IN_RANGE_GPIO1 BIT(1) +#define AXP192_GPIO30_IN_RANGE_GPIO2 BIT(2) +#define AXP192_GPIO30_IN_RANGE_GPIO3 BIT(3) + +#define AXP20X_GPIO10_IN_RANGE_GPIO0 BIT(0) +#define AXP20X_GPIO10_IN_RANGE_GPIO1 BIT(1) + +#define AXP20X_ADC_RATE_MASK GENMASK(7, 6) +#define AXP20X_ADC_RATE_HZ(x) ((ilog2((x) / 25) << 6) & AXP20X_ADC_RATE_MASK) + +#define AXP22X_ADC_RATE_HZ(x) ((ilog2((x) / 100) << 6) & AXP20X_ADC_RATE_MASK) + +#define AXP717_ADC_DATA_TS 0x00 +#define AXP717_ADC_DATA_TEMP 0x01 +#define AXP717_ADC_DATA_VMID 0x02 +#define AXP717_ADC_DATA_BKUP_BATT 0x03 + +#define AXP717_ADC_DATA_MASK GENMASK(13, 0) + +#define AXP813_V_I_ADC_RATE_MASK GENMASK(5, 4) +#define AXP813_ADC_RATE_MASK (AXP20X_ADC_RATE_MASK | AXP813_V_I_ADC_RATE_MASK) +#define AXP813_TS_GPIO0_ADC_RATE_HZ(x) AXP20X_ADC_RATE_HZ(x) +#define AXP813_V_I_ADC_RATE_HZ(x) ((ilog2((x) / 100) << 4) & AXP813_V_I_ADC_RATE_MASK) +#define AXP813_ADC_RATE_HZ(x) (AXP20X_ADC_RATE_HZ(x) | AXP813_V_I_ADC_RATE_HZ(x)) + +#define AXP20X_ADC_CHANNEL(_channel, _name, _type, _reg) \ + { \ + .type = _type, \ + .indexed = 1, \ + .channel = _channel, \ + .address = _reg, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ + BIT(IIO_CHAN_INFO_SCALE), \ + .datasheet_name = _name, \ + } + +#define AXP20X_ADC_CHANNEL_OFFSET(_channel, _name, _type, _reg) \ + { \ + .type = _type, \ + .indexed = 1, \ + .channel = _channel, \ + .address = _reg, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ + BIT(IIO_CHAN_INFO_SCALE) |\ + BIT(IIO_CHAN_INFO_OFFSET),\ + .datasheet_name = _name, \ + } + +struct axp_data; + +struct axp20x_adc_iio { + struct regmap *regmap; + const struct axp_data *data; +}; + +enum axp192_adc_channel_v { + AXP192_ACIN_V = 0, + AXP192_VBUS_V, + AXP192_TS_IN, + AXP192_GPIO0_V, + AXP192_GPIO1_V, + AXP192_GPIO2_V, + AXP192_GPIO3_V, + AXP192_IPSOUT_V, + AXP192_BATT_V, +}; + +enum axp192_adc_channel_i { + AXP192_ACIN_I = 0, + AXP192_VBUS_I, + AXP192_BATT_CHRG_I, + AXP192_BATT_DISCHRG_I, +}; + +enum axp20x_adc_channel_v { + AXP20X_ACIN_V = 0, + AXP20X_VBUS_V, + AXP20X_TS_IN, + AXP20X_GPIO0_V, + AXP20X_GPIO1_V, + AXP20X_IPSOUT_V, + AXP20X_BATT_V, +}; + +enum axp20x_adc_channel_i { + AXP20X_ACIN_I = 0, + AXP20X_VBUS_I, + AXP20X_BATT_CHRG_I, + AXP20X_BATT_DISCHRG_I, +}; + +enum axp22x_adc_channel_v { + AXP22X_TS_IN = 0, + AXP22X_BATT_V, +}; + +enum axp22x_adc_channel_i { + AXP22X_BATT_CHRG_I = 1, + AXP22X_BATT_DISCHRG_I, +}; + +enum axp717_adc_channel_v { + AXP717_BATT_V = 0, + AXP717_TS_IN, + AXP717_VBUS_V, + AXP717_VSYS_V, + AXP717_DIE_TEMP_V, + AXP717_VMID_V = 6, + AXP717_BKUP_BATT_V, +}; + +enum axp717_adc_channel_i { + AXP717_BATT_CHRG_I = 5, +}; + +enum axp813_adc_channel_v { + AXP813_TS_IN = 0, + AXP813_GPIO0_V, + AXP813_BATT_V, +}; + +static const struct iio_map axp20x_maps[] = { + IIO_MAP("vbus_v", "axp20x-usb-power-supply", "vbus_v"), + IIO_MAP("vbus_i", "axp20x-usb-power-supply", "vbus_i"), + IIO_MAP("acin_v", "axp20x-ac-power-supply", "acin_v"), + IIO_MAP("acin_i", "axp20x-ac-power-supply", "acin_i"), + IIO_MAP("batt_v", "axp20x-battery-power-supply", "batt_v"), + IIO_MAP("batt_chrg_i", "axp20x-battery-power-supply", "batt_chrg_i"), + IIO_MAP("batt_dischrg_i", "axp20x-battery-power-supply", "batt_dischrg_i"), + { } +}; + +static const struct iio_map axp22x_maps[] = { + IIO_MAP("batt_v", "axp20x-battery-power-supply", "batt_v"), + IIO_MAP("batt_chrg_i", "axp20x-battery-power-supply", "batt_chrg_i"), + IIO_MAP("batt_dischrg_i", "axp20x-battery-power-supply", "batt_dischrg_i"), + { } +}; + +static const struct iio_map axp717_maps[] = { + { + .consumer_dev_name = "axp20x-usb-power-supply", + .consumer_channel = "vbus_v", + .adc_channel_label = "vbus_v", + }, { + .consumer_dev_name = "axp20x-battery-power-supply", + .consumer_channel = "batt_v", + .adc_channel_label = "batt_v", + }, { + .consumer_dev_name = "axp20x-battery-power-supply", + .consumer_channel = "batt_chrg_i", + .adc_channel_label = "batt_chrg_i", + }, + { } +}; + +/* + * Channels are mapped by physical system. Their channels share the same index. + * i.e. acin_i is in_current0_raw and acin_v is in_voltage0_raw. + * The only exception is for the battery. batt_v will be in_voltage6_raw and + * charge current in_current6_raw and discharge current will be in_current7_raw. + */ +static const struct iio_chan_spec axp192_adc_channels[] = { + AXP20X_ADC_CHANNEL(AXP192_ACIN_V, "acin_v", IIO_VOLTAGE, + AXP20X_ACIN_V_ADC_H), + AXP20X_ADC_CHANNEL(AXP192_ACIN_I, "acin_i", IIO_CURRENT, + AXP20X_ACIN_I_ADC_H), + AXP20X_ADC_CHANNEL(AXP192_VBUS_V, "vbus_v", IIO_VOLTAGE, + AXP20X_VBUS_V_ADC_H), + AXP20X_ADC_CHANNEL(AXP192_VBUS_I, "vbus_i", IIO_CURRENT, + AXP20X_VBUS_I_ADC_H), + { + .type = IIO_TEMP, + .address = AXP20X_TEMP_ADC_H, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SCALE) | + BIT(IIO_CHAN_INFO_OFFSET), + .datasheet_name = "pmic_temp", + }, + AXP20X_ADC_CHANNEL_OFFSET(AXP192_GPIO0_V, "gpio0_v", IIO_VOLTAGE, + AXP20X_GPIO0_V_ADC_H), + AXP20X_ADC_CHANNEL_OFFSET(AXP192_GPIO1_V, "gpio1_v", IIO_VOLTAGE, + AXP20X_GPIO1_V_ADC_H), + AXP20X_ADC_CHANNEL_OFFSET(AXP192_GPIO2_V, "gpio2_v", IIO_VOLTAGE, + AXP192_GPIO2_V_ADC_H), + AXP20X_ADC_CHANNEL_OFFSET(AXP192_GPIO3_V, "gpio3_v", IIO_VOLTAGE, + AXP192_GPIO3_V_ADC_H), + AXP20X_ADC_CHANNEL(AXP192_IPSOUT_V, "ipsout_v", IIO_VOLTAGE, + AXP20X_IPSOUT_V_HIGH_H), + AXP20X_ADC_CHANNEL(AXP192_BATT_V, "batt_v", IIO_VOLTAGE, + AXP20X_BATT_V_H), + AXP20X_ADC_CHANNEL(AXP192_BATT_CHRG_I, "batt_chrg_i", IIO_CURRENT, + AXP20X_BATT_CHRG_I_H), + AXP20X_ADC_CHANNEL(AXP192_BATT_DISCHRG_I, "batt_dischrg_i", IIO_CURRENT, + AXP20X_BATT_DISCHRG_I_H), + AXP20X_ADC_CHANNEL(AXP192_TS_IN, "ts_v", IIO_VOLTAGE, + AXP20X_TS_IN_H), +}; + +static const struct iio_chan_spec axp20x_adc_channels[] = { + AXP20X_ADC_CHANNEL(AXP20X_ACIN_V, "acin_v", IIO_VOLTAGE, + AXP20X_ACIN_V_ADC_H), + AXP20X_ADC_CHANNEL(AXP20X_ACIN_I, "acin_i", IIO_CURRENT, + AXP20X_ACIN_I_ADC_H), + AXP20X_ADC_CHANNEL(AXP20X_VBUS_V, "vbus_v", IIO_VOLTAGE, + AXP20X_VBUS_V_ADC_H), + AXP20X_ADC_CHANNEL(AXP20X_VBUS_I, "vbus_i", IIO_CURRENT, + AXP20X_VBUS_I_ADC_H), + { + .type = IIO_TEMP, + .address = AXP20X_TEMP_ADC_H, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SCALE) | + BIT(IIO_CHAN_INFO_OFFSET), + .datasheet_name = "pmic_temp", + }, + AXP20X_ADC_CHANNEL_OFFSET(AXP20X_GPIO0_V, "gpio0_v", IIO_VOLTAGE, + AXP20X_GPIO0_V_ADC_H), + AXP20X_ADC_CHANNEL_OFFSET(AXP20X_GPIO1_V, "gpio1_v", IIO_VOLTAGE, + AXP20X_GPIO1_V_ADC_H), + AXP20X_ADC_CHANNEL(AXP20X_IPSOUT_V, "ipsout_v", IIO_VOLTAGE, + AXP20X_IPSOUT_V_HIGH_H), + AXP20X_ADC_CHANNEL(AXP20X_BATT_V, "batt_v", IIO_VOLTAGE, + AXP20X_BATT_V_H), + AXP20X_ADC_CHANNEL(AXP20X_BATT_CHRG_I, "batt_chrg_i", IIO_CURRENT, + AXP20X_BATT_CHRG_I_H), + AXP20X_ADC_CHANNEL(AXP20X_BATT_DISCHRG_I, "batt_dischrg_i", IIO_CURRENT, + AXP20X_BATT_DISCHRG_I_H), + AXP20X_ADC_CHANNEL(AXP20X_TS_IN, "ts_v", IIO_VOLTAGE, + AXP20X_TS_IN_H), +}; + +static const struct iio_chan_spec axp22x_adc_channels[] = { + { + .type = IIO_TEMP, + .address = AXP22X_PMIC_TEMP_H, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SCALE) | + BIT(IIO_CHAN_INFO_OFFSET), + .datasheet_name = "pmic_temp", + }, + AXP20X_ADC_CHANNEL(AXP22X_BATT_V, "batt_v", IIO_VOLTAGE, + AXP20X_BATT_V_H), + AXP20X_ADC_CHANNEL(AXP22X_BATT_CHRG_I, "batt_chrg_i", IIO_CURRENT, + AXP20X_BATT_CHRG_I_H), + AXP20X_ADC_CHANNEL(AXP22X_BATT_DISCHRG_I, "batt_dischrg_i", IIO_CURRENT, + AXP20X_BATT_DISCHRG_I_H), + AXP20X_ADC_CHANNEL(AXP22X_TS_IN, "ts_v", IIO_VOLTAGE, + AXP22X_TS_ADC_H), +}; + +/* + * Scale and offset is unknown for temp, ts, batt_chrg_i, vmid_v, and + * bkup_batt_v channels. Leaving scale and offset undefined for now. + */ +static const struct iio_chan_spec axp717_adc_channels[] = { + AXP20X_ADC_CHANNEL(AXP717_BATT_V, "batt_v", IIO_VOLTAGE, + AXP717_BATT_V_H), + AXP20X_ADC_CHANNEL(AXP717_TS_IN, "ts_v", IIO_VOLTAGE, + AXP717_ADC_DATA_H), + AXP20X_ADC_CHANNEL(AXP717_VBUS_V, "vbus_v", IIO_VOLTAGE, + AXP717_VBUS_V_H), + AXP20X_ADC_CHANNEL(AXP717_VSYS_V, "vsys_v", IIO_VOLTAGE, + AXP717_VSYS_V_H), + AXP20X_ADC_CHANNEL(AXP717_DIE_TEMP_V, "pmic_temp", IIO_TEMP, + AXP717_ADC_DATA_H), + AXP20X_ADC_CHANNEL(AXP717_BATT_CHRG_I, "batt_chrg_i", IIO_CURRENT, + AXP717_BATT_CHRG_I_H), + AXP20X_ADC_CHANNEL(AXP717_VMID_V, "vmid_v", IIO_VOLTAGE, + AXP717_ADC_DATA_H), + AXP20X_ADC_CHANNEL(AXP717_BKUP_BATT_V, "bkup_batt_v", IIO_VOLTAGE, + AXP717_ADC_DATA_H), +}; + +static const struct iio_chan_spec axp813_adc_channels[] = { + { + .type = IIO_TEMP, + .address = AXP22X_PMIC_TEMP_H, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SCALE) | + BIT(IIO_CHAN_INFO_OFFSET), + .datasheet_name = "pmic_temp", + }, + AXP20X_ADC_CHANNEL(AXP813_GPIO0_V, "gpio0_v", IIO_VOLTAGE, + AXP288_GP_ADC_H), + AXP20X_ADC_CHANNEL(AXP813_BATT_V, "batt_v", IIO_VOLTAGE, + AXP20X_BATT_V_H), + AXP20X_ADC_CHANNEL(AXP22X_BATT_CHRG_I, "batt_chrg_i", IIO_CURRENT, + AXP20X_BATT_CHRG_I_H), + AXP20X_ADC_CHANNEL(AXP22X_BATT_DISCHRG_I, "batt_dischrg_i", IIO_CURRENT, + AXP20X_BATT_DISCHRG_I_H), + AXP20X_ADC_CHANNEL(AXP813_TS_IN, "ts_v", IIO_VOLTAGE, + AXP288_TS_ADC_H), +}; + +static int axp192_adc_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int *val) +{ + struct axp20x_adc_iio *info = iio_priv(indio_dev); + int ret, size; + + if (chan->type == IIO_CURRENT && + (chan->channel == AXP192_BATT_CHRG_I || + chan->channel == AXP192_BATT_DISCHRG_I)) + size = 13; + else + size = 12; + + ret = axp20x_read_variable_width(info->regmap, chan->address, size); + if (ret < 0) + return ret; + + *val = ret; + return IIO_VAL_INT; +} + +static int axp20x_adc_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int *val) +{ + struct axp20x_adc_iio *info = iio_priv(indio_dev); + int ret, size; + + /* + * N.B.: Unlike the Chinese datasheets tell, the charging current is + * stored on 12 bits, not 13 bits. Only discharging current is on 13 + * bits. + */ + if (chan->type == IIO_CURRENT && chan->channel == AXP20X_BATT_DISCHRG_I) + size = 13; + else + size = 12; + + ret = axp20x_read_variable_width(info->regmap, chan->address, size); + if (ret < 0) + return ret; + + *val = ret; + return IIO_VAL_INT; +} + +static int axp22x_adc_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int *val) +{ + struct axp20x_adc_iio *info = iio_priv(indio_dev); + int ret; + + ret = axp20x_read_variable_width(info->regmap, chan->address, 12); + if (ret < 0) + return ret; + + *val = ret; + return IIO_VAL_INT; +} + +static int axp717_adc_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int *val) +{ + struct axp20x_adc_iio *info = iio_priv(indio_dev); + u8 bulk_reg[2]; + int ret; + + /* + * A generic "ADC data" channel is used for TS, tdie, vmid, + * and vbackup. This channel must both first be enabled and + * also selected before it can be read. + */ + switch (chan->channel) { + case AXP717_TS_IN: + regmap_write(info->regmap, AXP717_ADC_DATA_SEL, + AXP717_ADC_DATA_TS); + break; + case AXP717_DIE_TEMP_V: + regmap_write(info->regmap, AXP717_ADC_DATA_SEL, + AXP717_ADC_DATA_TEMP); + break; + case AXP717_VMID_V: + regmap_write(info->regmap, AXP717_ADC_DATA_SEL, + AXP717_ADC_DATA_VMID); + break; + case AXP717_BKUP_BATT_V: + regmap_write(info->regmap, AXP717_ADC_DATA_SEL, + AXP717_ADC_DATA_BKUP_BATT); + break; + default: + break; + } + + /* + * All channels are 14 bits, with the first 2 bits on the high + * register reserved and the remaining bits as the ADC value. + */ + ret = regmap_bulk_read(info->regmap, chan->address, bulk_reg, 2); + if (ret < 0) + return ret; + + *val = FIELD_GET(AXP717_ADC_DATA_MASK, get_unaligned_be16(bulk_reg)); + return IIO_VAL_INT; +} + +static int axp813_adc_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int *val) +{ + struct axp20x_adc_iio *info = iio_priv(indio_dev); + int ret; + + ret = axp20x_read_variable_width(info->regmap, chan->address, 12); + if (ret < 0) + return ret; + + *val = ret; + return IIO_VAL_INT; +} + +static int axp192_adc_scale_voltage(int channel, int *val, int *val2) +{ + switch (channel) { + case AXP192_ACIN_V: + case AXP192_VBUS_V: + *val = 1; + *val2 = 700000; + return IIO_VAL_INT_PLUS_MICRO; + + case AXP192_GPIO0_V: + case AXP192_GPIO1_V: + case AXP192_GPIO2_V: + case AXP192_GPIO3_V: + *val = 0; + *val2 = 500000; + return IIO_VAL_INT_PLUS_MICRO; + + case AXP192_BATT_V: + *val = 1; + *val2 = 100000; + return IIO_VAL_INT_PLUS_MICRO; + + case AXP192_IPSOUT_V: + *val = 1; + *val2 = 400000; + return IIO_VAL_INT_PLUS_MICRO; + + case AXP192_TS_IN: + /* 0.8 mV per LSB */ + *val = 0; + *val2 = 800000; + return IIO_VAL_INT_PLUS_MICRO; + + default: + return -EINVAL; + } +} + +static int axp20x_adc_scale_voltage(int channel, int *val, int *val2) +{ + switch (channel) { + case AXP20X_ACIN_V: + case AXP20X_VBUS_V: + *val = 1; + *val2 = 700000; + return IIO_VAL_INT_PLUS_MICRO; + + case AXP20X_GPIO0_V: + case AXP20X_GPIO1_V: + *val = 0; + *val2 = 500000; + return IIO_VAL_INT_PLUS_MICRO; + + case AXP20X_BATT_V: + *val = 1; + *val2 = 100000; + return IIO_VAL_INT_PLUS_MICRO; + + case AXP20X_IPSOUT_V: + *val = 1; + *val2 = 400000; + return IIO_VAL_INT_PLUS_MICRO; + + case AXP20X_TS_IN: + /* 0.8 mV per LSB */ + *val = 0; + *val2 = 800000; + return IIO_VAL_INT_PLUS_MICRO; + + default: + return -EINVAL; + } +} + +static int axp22x_adc_scale_voltage(int channel, int *val, int *val2) +{ + switch (channel) { + case AXP22X_BATT_V: + /* 1.1 mV per LSB */ + *val = 1; + *val2 = 100000; + return IIO_VAL_INT_PLUS_MICRO; + + case AXP22X_TS_IN: + /* 0.8 mV per LSB */ + *val = 0; + *val2 = 800000; + return IIO_VAL_INT_PLUS_MICRO; + + default: + return -EINVAL; + } +} +static int axp813_adc_scale_voltage(int channel, int *val, int *val2) +{ + switch (channel) { + case AXP813_GPIO0_V: + *val = 0; + *val2 = 800000; + return IIO_VAL_INT_PLUS_MICRO; + + case AXP813_BATT_V: + *val = 1; + *val2 = 100000; + return IIO_VAL_INT_PLUS_MICRO; + + case AXP813_TS_IN: + /* 0.8 mV per LSB */ + *val = 0; + *val2 = 800000; + return IIO_VAL_INT_PLUS_MICRO; + + default: + return -EINVAL; + } +} + +static int axp20x_adc_scale_current(int channel, int *val, int *val2) +{ + switch (channel) { + case AXP20X_ACIN_I: + *val = 0; + *val2 = 625000; + return IIO_VAL_INT_PLUS_MICRO; + + case AXP20X_VBUS_I: + *val = 0; + *val2 = 375000; + return IIO_VAL_INT_PLUS_MICRO; + + case AXP20X_BATT_DISCHRG_I: + case AXP20X_BATT_CHRG_I: + *val = 0; + *val2 = 500000; + return IIO_VAL_INT_PLUS_MICRO; + + default: + return -EINVAL; + } +} + +static int axp192_adc_scale(struct iio_chan_spec const *chan, int *val, + int *val2) +{ + switch (chan->type) { + case IIO_VOLTAGE: + return axp192_adc_scale_voltage(chan->channel, val, val2); + + case IIO_CURRENT: + /* + * AXP192 current channels are identical to the AXP20x, + * therefore we can re-use the scaling function. + */ + return axp20x_adc_scale_current(chan->channel, val, val2); + + case IIO_TEMP: + *val = 100; + return IIO_VAL_INT; + + default: + return -EINVAL; + } +} + +static int axp20x_adc_scale(struct iio_chan_spec const *chan, int *val, + int *val2) +{ + switch (chan->type) { + case IIO_VOLTAGE: + return axp20x_adc_scale_voltage(chan->channel, val, val2); + + case IIO_CURRENT: + return axp20x_adc_scale_current(chan->channel, val, val2); + + case IIO_TEMP: + *val = 100; + return IIO_VAL_INT; + + default: + return -EINVAL; + } +} + +static int axp22x_adc_scale(struct iio_chan_spec const *chan, int *val, + int *val2) +{ + switch (chan->type) { + case IIO_VOLTAGE: + return axp22x_adc_scale_voltage(chan->channel, val, val2); + + case IIO_CURRENT: + *val = 1; + return IIO_VAL_INT; + + case IIO_TEMP: + *val = 100; + return IIO_VAL_INT; + + default: + return -EINVAL; + } +} + +static int axp717_adc_scale(struct iio_chan_spec const *chan, int *val, + int *val2) +{ + switch (chan->type) { + case IIO_VOLTAGE: + *val = 1; + return IIO_VAL_INT; + + case IIO_CURRENT: + *val = 1; + return IIO_VAL_INT; + + case IIO_TEMP: + *val = 100; + return IIO_VAL_INT; + + default: + return -EINVAL; + } +} + +static int axp813_adc_scale(struct iio_chan_spec const *chan, int *val, + int *val2) +{ + switch (chan->type) { + case IIO_VOLTAGE: + return axp813_adc_scale_voltage(chan->channel, val, val2); + + case IIO_CURRENT: + *val = 1; + return IIO_VAL_INT; + + case IIO_TEMP: + *val = 100; + return IIO_VAL_INT; + + default: + return -EINVAL; + } +} + +static int axp192_adc_offset_voltage(struct iio_dev *indio_dev, int channel, + int *val) +{ + struct axp20x_adc_iio *info = iio_priv(indio_dev); + unsigned int regval; + int ret; + + ret = regmap_read(info->regmap, AXP192_GPIO30_IN_RANGE, ®val); + if (ret < 0) + return ret; + + switch (channel) { + case AXP192_GPIO0_V: + regval = FIELD_GET(AXP192_GPIO30_IN_RANGE_GPIO0, regval); + break; + + case AXP192_GPIO1_V: + regval = FIELD_GET(AXP192_GPIO30_IN_RANGE_GPIO1, regval); + break; + + case AXP192_GPIO2_V: + regval = FIELD_GET(AXP192_GPIO30_IN_RANGE_GPIO2, regval); + break; + + case AXP192_GPIO3_V: + regval = FIELD_GET(AXP192_GPIO30_IN_RANGE_GPIO3, regval); + break; + + default: + return -EINVAL; + } + + *val = regval ? 700000 : 0; + return IIO_VAL_INT; +} + +static int axp20x_adc_offset_voltage(struct iio_dev *indio_dev, int channel, + int *val) +{ + struct axp20x_adc_iio *info = iio_priv(indio_dev); + unsigned int regval; + int ret; + + ret = regmap_read(info->regmap, AXP20X_GPIO10_IN_RANGE, ®val); + if (ret < 0) + return ret; + + switch (channel) { + case AXP20X_GPIO0_V: + regval = FIELD_GET(AXP20X_GPIO10_IN_RANGE_GPIO0, regval); + break; + + case AXP20X_GPIO1_V: + regval = FIELD_GET(AXP20X_GPIO10_IN_RANGE_GPIO1, regval); + break; + + default: + return -EINVAL; + } + + *val = regval ? 700000 : 0; + return IIO_VAL_INT; +} + +static int axp192_adc_offset(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int *val) +{ + switch (chan->type) { + case IIO_VOLTAGE: + return axp192_adc_offset_voltage(indio_dev, chan->channel, val); + + case IIO_TEMP: + *val = -1447; + return IIO_VAL_INT; + + default: + return -EINVAL; + } +} + +static int axp20x_adc_offset(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int *val) +{ + switch (chan->type) { + case IIO_VOLTAGE: + return axp20x_adc_offset_voltage(indio_dev, chan->channel, val); + + case IIO_TEMP: + *val = -1447; + return IIO_VAL_INT; + + default: + return -EINVAL; + } +} + +static int axp192_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int *val, + int *val2, long mask) +{ + switch (mask) { + case IIO_CHAN_INFO_OFFSET: + return axp192_adc_offset(indio_dev, chan, val); + + case IIO_CHAN_INFO_SCALE: + return axp192_adc_scale(chan, val, val2); + + case IIO_CHAN_INFO_RAW: + return axp192_adc_raw(indio_dev, chan, val); + + default: + return -EINVAL; + } +} + +static int axp20x_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int *val, + int *val2, long mask) +{ + switch (mask) { + case IIO_CHAN_INFO_OFFSET: + return axp20x_adc_offset(indio_dev, chan, val); + + case IIO_CHAN_INFO_SCALE: + return axp20x_adc_scale(chan, val, val2); + + case IIO_CHAN_INFO_RAW: + return axp20x_adc_raw(indio_dev, chan, val); + + default: + return -EINVAL; + } +} + +static int axp22x_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int *val, + int *val2, long mask) +{ + switch (mask) { + case IIO_CHAN_INFO_OFFSET: + /* For PMIC temp only */ + *val = -2677; + return IIO_VAL_INT; + + case IIO_CHAN_INFO_SCALE: + return axp22x_adc_scale(chan, val, val2); + + case IIO_CHAN_INFO_RAW: + return axp22x_adc_raw(indio_dev, chan, val); + + default: + return -EINVAL; + } +} + +static int axp717_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int *val, + int *val2, long mask) +{ + switch (mask) { + case IIO_CHAN_INFO_SCALE: + return axp717_adc_scale(chan, val, val2); + + case IIO_CHAN_INFO_RAW: + return axp717_adc_raw(indio_dev, chan, val); + + default: + return -EINVAL; + } +} + +static int axp813_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int *val, + int *val2, long mask) +{ + switch (mask) { + case IIO_CHAN_INFO_OFFSET: + *val = -2667; + return IIO_VAL_INT; + + case IIO_CHAN_INFO_SCALE: + return axp813_adc_scale(chan, val, val2); + + case IIO_CHAN_INFO_RAW: + return axp813_adc_raw(indio_dev, chan, val); + + default: + return -EINVAL; + } +} + +static int axp192_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int val, int val2, + long mask) +{ + struct axp20x_adc_iio *info = iio_priv(indio_dev); + unsigned int regmask, regval; + + /* + * The AXP192 PMIC allows the user to choose between 0V and 0.7V offsets + * for (independently) GPIO0-3 when in ADC mode. + */ + if (mask != IIO_CHAN_INFO_OFFSET) + return -EINVAL; + + if (val != 0 && val != 700000) + return -EINVAL; + + switch (chan->channel) { + case AXP192_GPIO0_V: + regmask = AXP192_GPIO30_IN_RANGE_GPIO0; + regval = FIELD_PREP(AXP192_GPIO30_IN_RANGE_GPIO0, !!val); + break; + + case AXP192_GPIO1_V: + regmask = AXP192_GPIO30_IN_RANGE_GPIO1; + regval = FIELD_PREP(AXP192_GPIO30_IN_RANGE_GPIO1, !!val); + break; + + case AXP192_GPIO2_V: + regmask = AXP192_GPIO30_IN_RANGE_GPIO2; + regval = FIELD_PREP(AXP192_GPIO30_IN_RANGE_GPIO2, !!val); + break; + + case AXP192_GPIO3_V: + regmask = AXP192_GPIO30_IN_RANGE_GPIO3; + regval = FIELD_PREP(AXP192_GPIO30_IN_RANGE_GPIO3, !!val); + break; + + default: + return -EINVAL; + } + + return regmap_update_bits(info->regmap, AXP192_GPIO30_IN_RANGE, regmask, regval); +} + +static int axp20x_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int val, int val2, + long mask) +{ + struct axp20x_adc_iio *info = iio_priv(indio_dev); + unsigned int regmask, regval; + + /* + * The AXP20X PMIC allows the user to choose between 0V and 0.7V offsets + * for (independently) GPIO0 and GPIO1 when in ADC mode. + */ + if (mask != IIO_CHAN_INFO_OFFSET) + return -EINVAL; + + if (val != 0 && val != 700000) + return -EINVAL; + + switch (chan->channel) { + case AXP20X_GPIO0_V: + regmask = AXP20X_GPIO10_IN_RANGE_GPIO0; + regval = FIELD_PREP(AXP20X_GPIO10_IN_RANGE_GPIO0, !!val); + break; + + case AXP20X_GPIO1_V: + regmask = AXP20X_GPIO10_IN_RANGE_GPIO1; + regval = FIELD_PREP(AXP20X_GPIO10_IN_RANGE_GPIO1, !!val); + break; + + default: + return -EINVAL; + } + + return regmap_update_bits(info->regmap, AXP20X_GPIO10_IN_RANGE, regmask, regval); +} + +static const struct iio_info axp192_adc_iio_info = { + .read_raw = axp192_read_raw, + .write_raw = axp192_write_raw, +}; + +static const struct iio_info axp20x_adc_iio_info = { + .read_raw = axp20x_read_raw, + .write_raw = axp20x_write_raw, +}; + +static const struct iio_info axp22x_adc_iio_info = { + .read_raw = axp22x_read_raw, +}; + +static const struct iio_info axp717_adc_iio_info = { + .read_raw = axp717_read_raw, +}; + +static const struct iio_info axp813_adc_iio_info = { + .read_raw = axp813_read_raw, +}; + +static int axp20x_adc_rate(struct axp20x_adc_iio *info, int rate) +{ + return regmap_update_bits(info->regmap, AXP20X_ADC_RATE, + AXP20X_ADC_RATE_MASK, + AXP20X_ADC_RATE_HZ(rate)); +} + +static int axp22x_adc_rate(struct axp20x_adc_iio *info, int rate) +{ + return regmap_update_bits(info->regmap, AXP20X_ADC_RATE, + AXP20X_ADC_RATE_MASK, + AXP22X_ADC_RATE_HZ(rate)); +} + +static int axp813_adc_rate(struct axp20x_adc_iio *info, int rate) +{ + return regmap_update_bits(info->regmap, AXP813_ADC_RATE, + AXP813_ADC_RATE_MASK, + AXP813_ADC_RATE_HZ(rate)); +} + +struct axp_data { + const struct iio_info *iio_info; + int num_channels; + struct iio_chan_spec const *channels; + unsigned long adc_en1; + unsigned long adc_en1_mask; + unsigned long adc_en2; + unsigned long adc_en2_mask; + int (*adc_rate)(struct axp20x_adc_iio *info, + int rate); + const struct iio_map *maps; +}; + +static const struct axp_data axp192_data = { + .iio_info = &axp192_adc_iio_info, + .num_channels = ARRAY_SIZE(axp192_adc_channels), + .channels = axp192_adc_channels, + .adc_en1_mask = AXP192_ADC_EN1_MASK, + .adc_en2_mask = AXP192_ADC_EN2_MASK, + .adc_rate = axp20x_adc_rate, + .maps = axp20x_maps, +}; + +static const struct axp_data axp20x_data = { + .iio_info = &axp20x_adc_iio_info, + .num_channels = ARRAY_SIZE(axp20x_adc_channels), + .channels = axp20x_adc_channels, + .adc_en1 = AXP20X_ADC_EN1, + .adc_en1_mask = AXP20X_ADC_EN1_MASK, + .adc_en2 = AXP20X_ADC_EN2, + .adc_en2_mask = AXP20X_ADC_EN2_MASK, + .adc_rate = axp20x_adc_rate, + .maps = axp20x_maps, +}; + +static const struct axp_data axp22x_data = { + .iio_info = &axp22x_adc_iio_info, + .num_channels = ARRAY_SIZE(axp22x_adc_channels), + .channels = axp22x_adc_channels, + .adc_en1 = AXP20X_ADC_EN1, + .adc_en1_mask = AXP22X_ADC_EN1_MASK, + .adc_rate = axp22x_adc_rate, + .maps = axp22x_maps, +}; + +static const struct axp_data axp717_data = { + .iio_info = &axp717_adc_iio_info, + .num_channels = ARRAY_SIZE(axp717_adc_channels), + .channels = axp717_adc_channels, + .adc_en1 = AXP717_ADC_CH_EN_CONTROL, + .adc_en1_mask = AXP717_ADC_EN1_MASK, + .maps = axp717_maps, +}; + +static const struct axp_data axp813_data = { + .iio_info = &axp813_adc_iio_info, + .num_channels = ARRAY_SIZE(axp813_adc_channels), + .channels = axp813_adc_channels, + .adc_en1 = AXP20X_ADC_EN1, + .adc_en1_mask = AXP22X_ADC_EN1_MASK, + .adc_rate = axp813_adc_rate, + .maps = axp22x_maps, +}; + +static const struct of_device_id axp20x_adc_of_match[] = { + { .compatible = "x-powers,axp192-adc", .data = (void *)&axp192_data, }, + { .compatible = "x-powers,axp209-adc", .data = (void *)&axp20x_data, }, + { .compatible = "x-powers,axp221-adc", .data = (void *)&axp22x_data, }, + { .compatible = "x-powers,axp717-adc", .data = (void *)&axp717_data, }, + { .compatible = "x-powers,axp813-adc", .data = (void *)&axp813_data, }, + { } +}; +MODULE_DEVICE_TABLE(of, axp20x_adc_of_match); + +static const struct platform_device_id axp20x_adc_id_match[] = { + { .name = "axp192-adc", .driver_data = (kernel_ulong_t)&axp192_data, }, + { .name = "axp20x-adc", .driver_data = (kernel_ulong_t)&axp20x_data, }, + { .name = "axp22x-adc", .driver_data = (kernel_ulong_t)&axp22x_data, }, + { .name = "axp717-adc", .driver_data = (kernel_ulong_t)&axp717_data, }, + { .name = "axp813-adc", .driver_data = (kernel_ulong_t)&axp813_data, }, + { } +}; +MODULE_DEVICE_TABLE(platform, axp20x_adc_id_match); + +static int axp20x_probe(struct platform_device *pdev) +{ + struct axp20x_adc_iio *info; + struct iio_dev *indio_dev; + struct axp20x_dev *axp20x_dev; + int ret; + + axp20x_dev = dev_get_drvdata(pdev->dev.parent); + + indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*info)); + if (!indio_dev) + return -ENOMEM; + + info = iio_priv(indio_dev); + platform_set_drvdata(pdev, indio_dev); + + info->regmap = axp20x_dev->regmap; + indio_dev->modes = INDIO_DIRECT_MODE; + + if (!dev_fwnode(&pdev->dev)) { + const struct platform_device_id *id; + + id = platform_get_device_id(pdev); + info->data = (const struct axp_data *)id->driver_data; + } else { + struct device *dev = &pdev->dev; + + info->data = device_get_match_data(dev); + } + + indio_dev->name = platform_get_device_id(pdev)->name; + indio_dev->info = info->data->iio_info; + indio_dev->num_channels = info->data->num_channels; + indio_dev->channels = info->data->channels; + + /* Enable the ADCs on IP */ + regmap_write(info->regmap, info->data->adc_en1, + info->data->adc_en1_mask); + + if (info->data->adc_en2_mask) + regmap_set_bits(info->regmap, info->data->adc_en2, + info->data->adc_en2_mask); + + /* Configure ADCs rate */ + if (info->data->adc_rate) + info->data->adc_rate(info, 100); + + ret = iio_map_array_register(indio_dev, info->data->maps); + if (ret < 0) { + dev_err(&pdev->dev, "failed to register IIO maps: %d\n", ret); + goto fail_map; + } + + ret = iio_device_register(indio_dev); + if (ret < 0) { + dev_err(&pdev->dev, "could not register the device\n"); + goto fail_register; + } + + return 0; + +fail_register: + iio_map_array_unregister(indio_dev); + +fail_map: + regmap_write(info->regmap, info->data->adc_en1, 0); + + if (info->data->adc_en2_mask) + regmap_write(info->regmap, info->data->adc_en2, 0); + + return ret; +} + +static void axp20x_remove(struct platform_device *pdev) +{ + struct iio_dev *indio_dev = platform_get_drvdata(pdev); + struct axp20x_adc_iio *info = iio_priv(indio_dev); + + iio_device_unregister(indio_dev); + iio_map_array_unregister(indio_dev); + + regmap_write(info->regmap, info->data->adc_en1, 0); + + if (info->data->adc_en2_mask) + regmap_write(info->regmap, info->data->adc_en2, 0); +} + +static struct platform_driver axp20x_adc_driver = { + .driver = { + .name = "axp20x-adc", + .of_match_table = axp20x_adc_of_match, + }, + .id_table = axp20x_adc_id_match, + .probe = axp20x_probe, + .remove = axp20x_remove, +}; + +module_platform_driver(axp20x_adc_driver); + +MODULE_DESCRIPTION("ADC driver for AXP20X and AXP22X PMICs"); +MODULE_AUTHOR("Quentin Schulz "); +MODULE_LICENSE("GPL"); diff --git a/drivers/iio/adc/axp288_adc.c b/drivers/iio/adc/axp288_adc.c new file mode 100644 index 000000000..c8283279c --- /dev/null +++ b/drivers/iio/adc/axp288_adc.c @@ -0,0 +1,319 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * axp288_adc.c - X-Powers AXP288 PMIC ADC Driver + * + * Copyright (C) 2014 Intel Corporation + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +/* + * This mask enables all ADCs except for the battery temp-sensor (TS), that is + * left as-is to avoid breaking charging on devices without a temp-sensor. + */ +#define AXP288_ADC_EN_MASK 0xF0 +#define AXP288_ADC_TS_ENABLE 0x01 + +#define AXP288_ADC_TS_BIAS_MASK GENMASK(5, 4) +#define AXP288_ADC_TS_BIAS_20UA (0 << 4) +#define AXP288_ADC_TS_BIAS_40UA (1 << 4) +#define AXP288_ADC_TS_BIAS_60UA (2 << 4) +#define AXP288_ADC_TS_BIAS_80UA (3 << 4) +#define AXP288_ADC_TS_CURRENT_ON_OFF_MASK GENMASK(1, 0) +#define AXP288_ADC_TS_CURRENT_OFF (0 << 0) +#define AXP288_ADC_TS_CURRENT_ON_WHEN_CHARGING (1 << 0) +#define AXP288_ADC_TS_CURRENT_ON_ONDEMAND (2 << 0) +#define AXP288_ADC_TS_CURRENT_ON (3 << 0) + +enum axp288_adc_id { + AXP288_ADC_TS, + AXP288_ADC_PMIC, + AXP288_ADC_GP, + AXP288_ADC_BATT_CHRG_I, + AXP288_ADC_BATT_DISCHRG_I, + AXP288_ADC_BATT_V, + AXP288_ADC_NR_CHAN, +}; + +struct axp288_adc_info { + int irq; + struct regmap *regmap; + /* lock to protect against multiple access to the device */ + struct mutex lock; + bool ts_enabled; +}; + +static const struct iio_chan_spec axp288_adc_channels[] = { + { + .indexed = 1, + .type = IIO_TEMP, + .channel = 0, + .address = AXP288_TS_ADC_H, + .datasheet_name = "TS_PIN", + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), + }, { + .indexed = 1, + .type = IIO_TEMP, + .channel = 1, + .address = AXP288_PMIC_ADC_H, + .datasheet_name = "PMIC_TEMP", + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), + }, { + .indexed = 1, + .type = IIO_TEMP, + .channel = 2, + .address = AXP288_GP_ADC_H, + .datasheet_name = "GPADC", + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), + }, { + .indexed = 1, + .type = IIO_CURRENT, + .channel = 3, + .address = AXP20X_BATT_CHRG_I_H, + .datasheet_name = "BATT_CHG_I", + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), + }, { + .indexed = 1, + .type = IIO_CURRENT, + .channel = 4, + .address = AXP20X_BATT_DISCHRG_I_H, + .datasheet_name = "BATT_DISCHRG_I", + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), + }, { + .indexed = 1, + .type = IIO_VOLTAGE, + .channel = 5, + .address = AXP20X_BATT_V_H, + .datasheet_name = "BATT_V", + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), + }, +}; + +/* for consumer drivers */ +static const struct iio_map axp288_adc_default_maps[] = { + IIO_MAP("TS_PIN", "axp288-batt", "axp288-batt-temp"), + IIO_MAP("PMIC_TEMP", "axp288-pmic", "axp288-pmic-temp"), + IIO_MAP("GPADC", "axp288-gpadc", "axp288-system-temp"), + IIO_MAP("BATT_CHG_I", "axp288-chrg", "axp288-chrg-curr"), + IIO_MAP("BATT_DISCHRG_I", "axp288-chrg", "axp288-chrg-d-curr"), + IIO_MAP("BATT_V", "axp288-batt", "axp288-batt-volt"), + { } +}; + +static int axp288_adc_read_channel(int *val, unsigned long address, + struct regmap *regmap) +{ + u8 buf[2]; + + if (regmap_bulk_read(regmap, address, buf, 2)) + return -EIO; + *val = (buf[0] << 4) + ((buf[1] >> 4) & 0x0F); + + return IIO_VAL_INT; +} + +/* + * The current-source used for the battery temp-sensor (TS) is shared + * with the GPADC. For proper fuel-gauge and charger operation the TS + * current-source needs to be permanently on. But to read the GPADC we + * need to temporary switch the TS current-source to ondemand, so that + * the GPADC can use it, otherwise we will always read an all 0 value. + */ +static int axp288_adc_set_ts(struct axp288_adc_info *info, + unsigned int mode, unsigned long address) +{ + int ret; + + /* No need to switch the current-source if the TS pin is disabled */ + if (!info->ts_enabled) + return 0; + + /* Channels other than GPADC do not need the current source */ + if (address != AXP288_GP_ADC_H) + return 0; + + ret = regmap_update_bits(info->regmap, AXP288_ADC_TS_PIN_CTRL, + AXP288_ADC_TS_CURRENT_ON_OFF_MASK, mode); + if (ret) + return ret; + + /* When switching to the GPADC pin give things some time to settle */ + if (mode == AXP288_ADC_TS_CURRENT_ON_ONDEMAND) + usleep_range(6000, 10000); + + return 0; +} + +static int axp288_adc_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + int ret; + struct axp288_adc_info *info = iio_priv(indio_dev); + + mutex_lock(&info->lock); + switch (mask) { + case IIO_CHAN_INFO_RAW: + if (axp288_adc_set_ts(info, AXP288_ADC_TS_CURRENT_ON_ONDEMAND, + chan->address)) { + dev_err(&indio_dev->dev, "GPADC mode\n"); + ret = -EINVAL; + break; + } + ret = axp288_adc_read_channel(val, chan->address, info->regmap); + if (axp288_adc_set_ts(info, AXP288_ADC_TS_CURRENT_ON, + chan->address)) + dev_err(&indio_dev->dev, "TS pin restore\n"); + break; + default: + ret = -EINVAL; + } + mutex_unlock(&info->lock); + + return ret; +} + +/* + * We rely on the machine's firmware to correctly setup the TS pin bias current + * at boot. This lists systems with broken fw where we need to set it ourselves. + */ +static const struct dmi_system_id axp288_adc_ts_bias_override[] = { + { + /* Lenovo Ideapad 100S (11 inch) */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), + DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo ideapad 100S-11IBY"), + }, + .driver_data = (void *)(uintptr_t)AXP288_ADC_TS_BIAS_80UA, + }, + { + /* Nuvision Solo 10 Draw */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "TMAX"), + DMI_MATCH(DMI_PRODUCT_NAME, "TM101W610L"), + }, + .driver_data = (void *)(uintptr_t)AXP288_ADC_TS_BIAS_80UA, + }, + { } +}; + +static int axp288_adc_initialize(struct axp288_adc_info *info) +{ + const struct dmi_system_id *bias_override; + int ret, adc_enable_val; + + bias_override = dmi_first_match(axp288_adc_ts_bias_override); + if (bias_override) { + ret = regmap_update_bits(info->regmap, AXP288_ADC_TS_PIN_CTRL, + AXP288_ADC_TS_BIAS_MASK, + (uintptr_t)bias_override->driver_data); + if (ret) + return ret; + } + + /* + * Determine if the TS pin is enabled and set the TS current-source + * accordingly. + */ + ret = regmap_read(info->regmap, AXP20X_ADC_EN1, &adc_enable_val); + if (ret) + return ret; + + if (adc_enable_val & AXP288_ADC_TS_ENABLE) { + info->ts_enabled = true; + ret = regmap_update_bits(info->regmap, AXP288_ADC_TS_PIN_CTRL, + AXP288_ADC_TS_CURRENT_ON_OFF_MASK, + AXP288_ADC_TS_CURRENT_ON); + } else { + info->ts_enabled = false; + ret = regmap_update_bits(info->regmap, AXP288_ADC_TS_PIN_CTRL, + AXP288_ADC_TS_CURRENT_ON_OFF_MASK, + AXP288_ADC_TS_CURRENT_OFF); + } + if (ret) + return ret; + + /* Turn on the ADC for all channels except TS, leave TS as is */ + return regmap_set_bits(info->regmap, AXP20X_ADC_EN1, + AXP288_ADC_EN_MASK); +} + +static const struct iio_info axp288_adc_iio_info = { + .read_raw = &axp288_adc_read_raw, +}; + +static int axp288_adc_probe(struct platform_device *pdev) +{ + int ret; + struct axp288_adc_info *info; + struct iio_dev *indio_dev; + struct axp20x_dev *axp20x = dev_get_drvdata(pdev->dev.parent); + + indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*info)); + if (!indio_dev) + return -ENOMEM; + + info = iio_priv(indio_dev); + info->irq = platform_get_irq(pdev, 0); + if (info->irq < 0) + return info->irq; + + info->regmap = axp20x->regmap; + /* + * Set ADC to enabled state at all time, including system suspend. + * otherwise internal fuel gauge functionality may be affected. + */ + ret = axp288_adc_initialize(info); + if (ret) { + dev_err(&pdev->dev, "unable to enable ADC device\n"); + return ret; + } + + indio_dev->name = pdev->name; + indio_dev->channels = axp288_adc_channels; + indio_dev->num_channels = ARRAY_SIZE(axp288_adc_channels); + indio_dev->info = &axp288_adc_iio_info; + indio_dev->modes = INDIO_DIRECT_MODE; + + ret = devm_iio_map_array_register(&pdev->dev, indio_dev, axp288_adc_default_maps); + if (ret < 0) + return ret; + + mutex_init(&info->lock); + + return devm_iio_device_register(&pdev->dev, indio_dev); +} + +static const struct platform_device_id axp288_adc_id_table[] = { + { .name = "axp288_adc" }, + { } +}; + +static struct platform_driver axp288_adc_driver = { + .probe = axp288_adc_probe, + .id_table = axp288_adc_id_table, + .driver = { + .name = "axp288_adc", + }, +}; + +MODULE_DEVICE_TABLE(platform, axp288_adc_id_table); + +module_platform_driver(axp288_adc_driver); + +MODULE_AUTHOR("Jacob Pan "); +MODULE_DESCRIPTION("X-Powers AXP288 ADC Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/iio/adc/bcm_iproc_adc.c b/drivers/iio/adc/bcm_iproc_adc.c new file mode 100644 index 000000000..cf4738b16 --- /dev/null +++ b/drivers/iio/adc/bcm_iproc_adc.c @@ -0,0 +1,622 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright 2016 Broadcom + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +/* Below Register's are common to IPROC ADC and Touchscreen IP */ +#define IPROC_REGCTL1 0x00 +#define IPROC_REGCTL2 0x04 +#define IPROC_INTERRUPT_THRES 0x08 +#define IPROC_INTERRUPT_MASK 0x0c +#define IPROC_INTERRUPT_STATUS 0x10 +#define IPROC_ANALOG_CONTROL 0x1c +#define IPROC_CONTROLLER_STATUS 0x14 +#define IPROC_AUX_DATA 0x20 +#define IPROC_SOFT_BYPASS_CONTROL 0x38 +#define IPROC_SOFT_BYPASS_DATA 0x3C + +/* IPROC ADC Channel register offsets */ +#define IPROC_ADC_CHANNEL_REGCTL1 0x800 +#define IPROC_ADC_CHANNEL_REGCTL2 0x804 +#define IPROC_ADC_CHANNEL_STATUS 0x808 +#define IPROC_ADC_CHANNEL_INTERRUPT_STATUS 0x80c +#define IPROC_ADC_CHANNEL_INTERRUPT_MASK 0x810 +#define IPROC_ADC_CHANNEL_DATA 0x814 +#define IPROC_ADC_CHANNEL_OFFSET 0x20 + +/* Bit definitions for IPROC_REGCTL2 */ +#define IPROC_ADC_AUXIN_SCAN_ENA BIT(0) +#define IPROC_ADC_PWR_LDO BIT(5) +#define IPROC_ADC_PWR_ADC BIT(4) +#define IPROC_ADC_PWR_BG BIT(3) +#define IPROC_ADC_CONTROLLER_EN BIT(17) + +/* Bit definitions for IPROC_INTERRUPT_MASK and IPROC_INTERRUPT_STATUS */ +#define IPROC_ADC_AUXDATA_RDY_INTR BIT(3) +#define IPROC_ADC_INTR 9 +#define IPROC_ADC_INTR_MASK (0xFF << IPROC_ADC_INTR) + +/* Bit definitions for IPROC_ANALOG_CONTROL */ +#define IPROC_ADC_CHANNEL_SEL 11 +#define IPROC_ADC_CHANNEL_SEL_MASK (0x7 << IPROC_ADC_CHANNEL_SEL) + +/* Bit definitions for IPROC_ADC_CHANNEL_REGCTL1 */ +#define IPROC_ADC_CHANNEL_ROUNDS 0x2 +#define IPROC_ADC_CHANNEL_ROUNDS_MASK (0x3F << IPROC_ADC_CHANNEL_ROUNDS) +#define IPROC_ADC_CHANNEL_MODE 0x1 +#define IPROC_ADC_CHANNEL_MODE_MASK (0x1 << IPROC_ADC_CHANNEL_MODE) +#define IPROC_ADC_CHANNEL_MODE_TDM 0x1 +#define IPROC_ADC_CHANNEL_MODE_SNAPSHOT 0x0 +#define IPROC_ADC_CHANNEL_ENABLE 0x0 +#define IPROC_ADC_CHANNEL_ENABLE_MASK 0x1 + +/* Bit definitions for IPROC_ADC_CHANNEL_REGCTL2 */ +#define IPROC_ADC_CHANNEL_WATERMARK 0x0 +#define IPROC_ADC_CHANNEL_WATERMARK_MASK \ + (0x3F << IPROC_ADC_CHANNEL_WATERMARK) + +#define IPROC_ADC_WATER_MARK_LEVEL 0x1 + +/* Bit definitions for IPROC_ADC_CHANNEL_STATUS */ +#define IPROC_ADC_CHANNEL_DATA_LOST 0x0 +#define IPROC_ADC_CHANNEL_DATA_LOST_MASK \ + (0x0 << IPROC_ADC_CHANNEL_DATA_LOST) +#define IPROC_ADC_CHANNEL_VALID_ENTERIES 0x1 +#define IPROC_ADC_CHANNEL_VALID_ENTERIES_MASK \ + (0xFF << IPROC_ADC_CHANNEL_VALID_ENTERIES) +#define IPROC_ADC_CHANNEL_TOTAL_ENTERIES 0x9 +#define IPROC_ADC_CHANNEL_TOTAL_ENTERIES_MASK \ + (0xFF << IPROC_ADC_CHANNEL_TOTAL_ENTERIES) + +/* Bit definitions for IPROC_ADC_CHANNEL_INTERRUPT_MASK */ +#define IPROC_ADC_CHANNEL_WTRMRK_INTR 0x0 +#define IPROC_ADC_CHANNEL_WTRMRK_INTR_MASK \ + (0x1 << IPROC_ADC_CHANNEL_WTRMRK_INTR) +#define IPROC_ADC_CHANNEL_FULL_INTR 0x1 +#define IPROC_ADC_CHANNEL_FULL_INTR_MASK \ + (0x1 << IPROC_ADC_IPROC_ADC_CHANNEL_FULL_INTR) +#define IPROC_ADC_CHANNEL_EMPTY_INTR 0x2 +#define IPROC_ADC_CHANNEL_EMPTY_INTR_MASK \ + (0x1 << IPROC_ADC_CHANNEL_EMPTY_INTR) + +#define IPROC_ADC_WATER_MARK_INTR_ENABLE 0x1 + +/* Number of time to retry a set of the interrupt mask reg */ +#define IPROC_ADC_INTMASK_RETRY_ATTEMPTS 10 + +#define IPROC_ADC_READ_TIMEOUT (HZ*2) + +#define iproc_adc_dbg_reg(dev, priv, reg) \ +do { \ + u32 val; \ + regmap_read(priv->regmap, reg, &val); \ + dev_dbg(dev, "%20s= 0x%08x\n", #reg, val); \ +} while (0) + +struct iproc_adc_priv { + struct regmap *regmap; + struct clk *adc_clk; + struct mutex mutex; + int irqno; + int chan_val; + int chan_id; + struct completion completion; +}; + +static void iproc_adc_reg_dump(struct iio_dev *indio_dev) +{ + struct device *dev = &indio_dev->dev; + struct iproc_adc_priv *adc_priv = iio_priv(indio_dev); + + iproc_adc_dbg_reg(dev, adc_priv, IPROC_REGCTL1); + iproc_adc_dbg_reg(dev, adc_priv, IPROC_REGCTL2); + iproc_adc_dbg_reg(dev, adc_priv, IPROC_INTERRUPT_THRES); + iproc_adc_dbg_reg(dev, adc_priv, IPROC_INTERRUPT_MASK); + iproc_adc_dbg_reg(dev, adc_priv, IPROC_INTERRUPT_STATUS); + iproc_adc_dbg_reg(dev, adc_priv, IPROC_CONTROLLER_STATUS); + iproc_adc_dbg_reg(dev, adc_priv, IPROC_ANALOG_CONTROL); + iproc_adc_dbg_reg(dev, adc_priv, IPROC_AUX_DATA); + iproc_adc_dbg_reg(dev, adc_priv, IPROC_SOFT_BYPASS_CONTROL); + iproc_adc_dbg_reg(dev, adc_priv, IPROC_SOFT_BYPASS_DATA); +} + +static irqreturn_t iproc_adc_interrupt_thread(int irq, void *data) +{ + u32 channel_intr_status; + u32 intr_status; + u32 intr_mask; + struct iio_dev *indio_dev = data; + struct iproc_adc_priv *adc_priv = iio_priv(indio_dev); + + /* + * This interrupt is shared with the touchscreen driver. + * Make sure this interrupt is intended for us. + * Handle only ADC channel specific interrupts. + */ + regmap_read(adc_priv->regmap, IPROC_INTERRUPT_STATUS, &intr_status); + regmap_read(adc_priv->regmap, IPROC_INTERRUPT_MASK, &intr_mask); + intr_status = intr_status & intr_mask; + channel_intr_status = (intr_status & IPROC_ADC_INTR_MASK) >> + IPROC_ADC_INTR; + if (channel_intr_status) + return IRQ_WAKE_THREAD; + + return IRQ_NONE; +} + +static irqreturn_t iproc_adc_interrupt_handler(int irq, void *data) +{ + irqreturn_t retval = IRQ_NONE; + struct iproc_adc_priv *adc_priv; + struct iio_dev *indio_dev = data; + unsigned int valid_entries; + u32 intr_status; + u32 intr_channels; + u32 channel_status; + u32 ch_intr_status; + + adc_priv = iio_priv(indio_dev); + + regmap_read(adc_priv->regmap, IPROC_INTERRUPT_STATUS, &intr_status); + dev_dbg(&indio_dev->dev, "iproc_adc_interrupt_handler(),INTRPT_STS:%x\n", + intr_status); + + intr_channels = (intr_status & IPROC_ADC_INTR_MASK) >> IPROC_ADC_INTR; + if (intr_channels) { + regmap_read(adc_priv->regmap, + IPROC_ADC_CHANNEL_INTERRUPT_STATUS + + IPROC_ADC_CHANNEL_OFFSET * adc_priv->chan_id, + &ch_intr_status); + + if (ch_intr_status & IPROC_ADC_CHANNEL_WTRMRK_INTR_MASK) { + regmap_read(adc_priv->regmap, + IPROC_ADC_CHANNEL_STATUS + + IPROC_ADC_CHANNEL_OFFSET * + adc_priv->chan_id, + &channel_status); + + valid_entries = ((channel_status & + IPROC_ADC_CHANNEL_VALID_ENTERIES_MASK) >> + IPROC_ADC_CHANNEL_VALID_ENTERIES); + if (valid_entries >= 1) { + regmap_read(adc_priv->regmap, + IPROC_ADC_CHANNEL_DATA + + IPROC_ADC_CHANNEL_OFFSET * + adc_priv->chan_id, + &adc_priv->chan_val); + complete(&adc_priv->completion); + } else { + dev_err(&indio_dev->dev, + "No data rcvd on channel %d\n", + adc_priv->chan_id); + } + regmap_write(adc_priv->regmap, + IPROC_ADC_CHANNEL_INTERRUPT_MASK + + IPROC_ADC_CHANNEL_OFFSET * + adc_priv->chan_id, + (ch_intr_status & + ~(IPROC_ADC_CHANNEL_WTRMRK_INTR_MASK))); + } + regmap_write(adc_priv->regmap, + IPROC_ADC_CHANNEL_INTERRUPT_STATUS + + IPROC_ADC_CHANNEL_OFFSET * adc_priv->chan_id, + ch_intr_status); + regmap_write(adc_priv->regmap, IPROC_INTERRUPT_STATUS, + intr_channels); + retval = IRQ_HANDLED; + } + + return retval; +} + +static int iproc_adc_do_read(struct iio_dev *indio_dev, + int channel, + u16 *p_adc_data) +{ + int read_len = 0; + u32 val; + u32 mask; + u32 val_check; + int failed_cnt = 0; + struct iproc_adc_priv *adc_priv = iio_priv(indio_dev); + + mutex_lock(&adc_priv->mutex); + + /* + * After a read is complete the ADC interrupts will be disabled so + * we can assume this section of code is safe from interrupts. + */ + adc_priv->chan_val = -1; + adc_priv->chan_id = channel; + + reinit_completion(&adc_priv->completion); + /* Clear any pending interrupt */ + regmap_update_bits(adc_priv->regmap, IPROC_INTERRUPT_STATUS, + IPROC_ADC_INTR_MASK | IPROC_ADC_AUXDATA_RDY_INTR, + ((0x0 << channel) << IPROC_ADC_INTR) | + IPROC_ADC_AUXDATA_RDY_INTR); + + /* Configure channel for snapshot mode and enable */ + val = (BIT(IPROC_ADC_CHANNEL_ROUNDS) | + (IPROC_ADC_CHANNEL_MODE_SNAPSHOT << IPROC_ADC_CHANNEL_MODE) | + (0x1 << IPROC_ADC_CHANNEL_ENABLE)); + + mask = IPROC_ADC_CHANNEL_ROUNDS_MASK | IPROC_ADC_CHANNEL_MODE_MASK | + IPROC_ADC_CHANNEL_ENABLE_MASK; + regmap_update_bits(adc_priv->regmap, (IPROC_ADC_CHANNEL_REGCTL1 + + IPROC_ADC_CHANNEL_OFFSET * channel), + mask, val); + + /* Set the Watermark for a channel */ + regmap_update_bits(adc_priv->regmap, (IPROC_ADC_CHANNEL_REGCTL2 + + IPROC_ADC_CHANNEL_OFFSET * channel), + IPROC_ADC_CHANNEL_WATERMARK_MASK, + 0x1); + + /* Enable water mark interrupt */ + regmap_update_bits(adc_priv->regmap, (IPROC_ADC_CHANNEL_INTERRUPT_MASK + + IPROC_ADC_CHANNEL_OFFSET * + channel), + IPROC_ADC_CHANNEL_WTRMRK_INTR_MASK, + IPROC_ADC_WATER_MARK_INTR_ENABLE); + regmap_read(adc_priv->regmap, IPROC_INTERRUPT_MASK, &val); + + /* Enable ADC interrupt for a channel */ + val |= (BIT(channel) << IPROC_ADC_INTR); + regmap_write(adc_priv->regmap, IPROC_INTERRUPT_MASK, val); + + /* + * There seems to be a very rare issue where writing to this register + * does not take effect. To work around the issue we will try multiple + * writes. In total we will spend about 10*10 = 100 us attempting this. + * Testing has shown that this may loop a few time, but we have never + * hit the full count. + */ + regmap_read(adc_priv->regmap, IPROC_INTERRUPT_MASK, &val_check); + while (val_check != val) { + failed_cnt++; + + if (failed_cnt > IPROC_ADC_INTMASK_RETRY_ATTEMPTS) + break; + + udelay(10); + regmap_update_bits(adc_priv->regmap, IPROC_INTERRUPT_MASK, + IPROC_ADC_INTR_MASK, + ((0x1 << channel) << + IPROC_ADC_INTR)); + + regmap_read(adc_priv->regmap, IPROC_INTERRUPT_MASK, &val_check); + } + + if (failed_cnt) { + dev_dbg(&indio_dev->dev, + "IntMask failed (%d times)", failed_cnt); + if (failed_cnt > IPROC_ADC_INTMASK_RETRY_ATTEMPTS) { + dev_err(&indio_dev->dev, + "IntMask set failed. Read will likely fail."); + read_len = -EIO; + goto adc_err; + } + } + regmap_read(adc_priv->regmap, IPROC_INTERRUPT_MASK, &val_check); + + if (wait_for_completion_timeout(&adc_priv->completion, + IPROC_ADC_READ_TIMEOUT) > 0) { + + /* Only the lower 16 bits are relevant */ + *p_adc_data = adc_priv->chan_val & 0xFFFF; + read_len = sizeof(*p_adc_data); + + } else { + /* + * We never got the interrupt, something went wrong. + * Perhaps the interrupt may still be coming, we do not want + * that now. Lets disable the ADC interrupt, and clear the + * status to put it back in to normal state. + */ + read_len = -ETIMEDOUT; + goto adc_err; + } + mutex_unlock(&adc_priv->mutex); + + return read_len; + +adc_err: + regmap_update_bits(adc_priv->regmap, IPROC_INTERRUPT_MASK, + IPROC_ADC_INTR_MASK, + ((0x0 << channel) << IPROC_ADC_INTR)); + + regmap_update_bits(adc_priv->regmap, IPROC_INTERRUPT_STATUS, + IPROC_ADC_INTR_MASK, + ((0x0 << channel) << IPROC_ADC_INTR)); + + dev_err(&indio_dev->dev, "Timed out waiting for ADC data!\n"); + iproc_adc_reg_dump(indio_dev); + mutex_unlock(&adc_priv->mutex); + + return read_len; +} + +static int iproc_adc_enable(struct iio_dev *indio_dev) +{ + u32 val; + u32 channel_id; + struct iproc_adc_priv *adc_priv = iio_priv(indio_dev); + int ret; + + /* Set i_amux = 3b'000, select channel 0 */ + ret = regmap_clear_bits(adc_priv->regmap, IPROC_ANALOG_CONTROL, + IPROC_ADC_CHANNEL_SEL_MASK); + if (ret) { + dev_err(&indio_dev->dev, + "failed to write IPROC_ANALOG_CONTROL %d\n", ret); + return ret; + } + adc_priv->chan_val = -1; + + /* + * PWR up LDO, ADC, and Band Gap (0 to enable) + * Also enable ADC controller (set high) + */ + ret = regmap_read(adc_priv->regmap, IPROC_REGCTL2, &val); + if (ret) { + dev_err(&indio_dev->dev, + "failed to read IPROC_REGCTL2 %d\n", ret); + return ret; + } + + val &= ~(IPROC_ADC_PWR_LDO | IPROC_ADC_PWR_ADC | IPROC_ADC_PWR_BG); + + ret = regmap_write(adc_priv->regmap, IPROC_REGCTL2, val); + if (ret) { + dev_err(&indio_dev->dev, + "failed to write IPROC_REGCTL2 %d\n", ret); + return ret; + } + + ret = regmap_read(adc_priv->regmap, IPROC_REGCTL2, &val); + if (ret) { + dev_err(&indio_dev->dev, + "failed to read IPROC_REGCTL2 %d\n", ret); + return ret; + } + + val |= IPROC_ADC_CONTROLLER_EN; + ret = regmap_write(adc_priv->regmap, IPROC_REGCTL2, val); + if (ret) { + dev_err(&indio_dev->dev, + "failed to write IPROC_REGCTL2 %d\n", ret); + return ret; + } + + for (channel_id = 0; channel_id < indio_dev->num_channels; + channel_id++) { + ret = regmap_write(adc_priv->regmap, + IPROC_ADC_CHANNEL_INTERRUPT_MASK + + IPROC_ADC_CHANNEL_OFFSET * channel_id, 0); + if (ret) { + dev_err(&indio_dev->dev, + "failed to write ADC_CHANNEL_INTERRUPT_MASK %d\n", + ret); + return ret; + } + + ret = regmap_write(adc_priv->regmap, + IPROC_ADC_CHANNEL_INTERRUPT_STATUS + + IPROC_ADC_CHANNEL_OFFSET * channel_id, 0); + if (ret) { + dev_err(&indio_dev->dev, + "failed to write ADC_CHANNEL_INTERRUPT_STATUS %d\n", + ret); + return ret; + } + } + + return 0; +} + +static void iproc_adc_disable(struct iio_dev *indio_dev) +{ + u32 val; + int ret; + struct iproc_adc_priv *adc_priv = iio_priv(indio_dev); + + ret = regmap_read(adc_priv->regmap, IPROC_REGCTL2, &val); + if (ret) { + dev_err(&indio_dev->dev, + "failed to read IPROC_REGCTL2 %d\n", ret); + return; + } + + val &= ~IPROC_ADC_CONTROLLER_EN; + ret = regmap_write(adc_priv->regmap, IPROC_REGCTL2, val); + if (ret) { + dev_err(&indio_dev->dev, + "failed to write IPROC_REGCTL2 %d\n", ret); + return; + } +} + +static int iproc_adc_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, + int *val2, + long mask) +{ + u16 adc_data; + int err; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + err = iproc_adc_do_read(indio_dev, chan->channel, &adc_data); + if (err < 0) + return err; + *val = adc_data; + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + switch (chan->type) { + case IIO_VOLTAGE: + *val = 1800; + *val2 = 10; + return IIO_VAL_FRACTIONAL_LOG2; + default: + return -EINVAL; + } + default: + return -EINVAL; + } +} + +static const struct iio_info iproc_adc_iio_info = { + .read_raw = &iproc_adc_read_raw, +}; + +#define IPROC_ADC_CHANNEL(_index, _id) { \ + .type = IIO_VOLTAGE, \ + .indexed = 1, \ + .channel = _index, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ + .datasheet_name = _id, \ +} + +static const struct iio_chan_spec iproc_adc_iio_channels[] = { + IPROC_ADC_CHANNEL(0, "adc0"), + IPROC_ADC_CHANNEL(1, "adc1"), + IPROC_ADC_CHANNEL(2, "adc2"), + IPROC_ADC_CHANNEL(3, "adc3"), + IPROC_ADC_CHANNEL(4, "adc4"), + IPROC_ADC_CHANNEL(5, "adc5"), + IPROC_ADC_CHANNEL(6, "adc6"), + IPROC_ADC_CHANNEL(7, "adc7"), +}; + +static int iproc_adc_probe(struct platform_device *pdev) +{ + struct iproc_adc_priv *adc_priv; + struct iio_dev *indio_dev = NULL; + int ret; + + indio_dev = devm_iio_device_alloc(&pdev->dev, + sizeof(*adc_priv)); + if (!indio_dev) + return -ENOMEM; + + adc_priv = iio_priv(indio_dev); + platform_set_drvdata(pdev, indio_dev); + + mutex_init(&adc_priv->mutex); + + init_completion(&adc_priv->completion); + + adc_priv->regmap = syscon_regmap_lookup_by_phandle(pdev->dev.of_node, + "adc-syscon"); + if (IS_ERR(adc_priv->regmap)) { + dev_err(&pdev->dev, "failed to get handle for tsc syscon\n"); + ret = PTR_ERR(adc_priv->regmap); + return ret; + } + + adc_priv->adc_clk = devm_clk_get(&pdev->dev, "tsc_clk"); + if (IS_ERR(adc_priv->adc_clk)) { + dev_err(&pdev->dev, + "failed getting clock tsc_clk\n"); + ret = PTR_ERR(adc_priv->adc_clk); + return ret; + } + + adc_priv->irqno = platform_get_irq(pdev, 0); + if (adc_priv->irqno < 0) + return adc_priv->irqno; + + ret = regmap_clear_bits(adc_priv->regmap, IPROC_REGCTL2, + IPROC_ADC_AUXIN_SCAN_ENA); + if (ret) { + dev_err(&pdev->dev, "failed to write IPROC_REGCTL2 %d\n", ret); + return ret; + } + + ret = devm_request_threaded_irq(&pdev->dev, adc_priv->irqno, + iproc_adc_interrupt_handler, + iproc_adc_interrupt_thread, + IRQF_SHARED, "iproc-adc", indio_dev); + if (ret) { + dev_err(&pdev->dev, "request_irq error %d\n", ret); + return ret; + } + + ret = clk_prepare_enable(adc_priv->adc_clk); + if (ret) { + dev_err(&pdev->dev, + "clk_prepare_enable failed %d\n", ret); + return ret; + } + + ret = iproc_adc_enable(indio_dev); + if (ret) { + dev_err(&pdev->dev, "failed to enable adc %d\n", ret); + goto err_adc_enable; + } + + indio_dev->name = "iproc-static-adc"; + indio_dev->info = &iproc_adc_iio_info; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = iproc_adc_iio_channels; + indio_dev->num_channels = ARRAY_SIZE(iproc_adc_iio_channels); + + ret = iio_device_register(indio_dev); + if (ret) { + dev_err(&pdev->dev, "iio_device_register failed:err %d\n", ret); + goto err_clk; + } + + return 0; + +err_clk: + iproc_adc_disable(indio_dev); +err_adc_enable: + clk_disable_unprepare(adc_priv->adc_clk); + + return ret; +} + +static void iproc_adc_remove(struct platform_device *pdev) +{ + struct iio_dev *indio_dev = platform_get_drvdata(pdev); + struct iproc_adc_priv *adc_priv = iio_priv(indio_dev); + + iio_device_unregister(indio_dev); + iproc_adc_disable(indio_dev); + clk_disable_unprepare(adc_priv->adc_clk); +} + +static const struct of_device_id iproc_adc_of_match[] = { + {.compatible = "brcm,iproc-static-adc", }, + { } +}; +MODULE_DEVICE_TABLE(of, iproc_adc_of_match); + +static struct platform_driver iproc_adc_driver = { + .probe = iproc_adc_probe, + .remove = iproc_adc_remove, + .driver = { + .name = "iproc-static-adc", + .of_match_table = iproc_adc_of_match, + }, +}; +module_platform_driver(iproc_adc_driver); + +MODULE_DESCRIPTION("Broadcom iProc ADC controller driver"); +MODULE_AUTHOR("Raveendra Padasalagi "); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/adc/berlin2-adc.c b/drivers/iio/adc/berlin2-adc.c new file mode 100644 index 000000000..a67edf0bd --- /dev/null +++ b/drivers/iio/adc/berlin2-adc.c @@ -0,0 +1,368 @@ +/* + * Marvell Berlin2 ADC driver + * + * Copyright (C) 2015 Marvell Technology Group Ltd. + * + * Antoine Tenart + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define BERLIN2_SM_CTRL 0x14 +#define BERLIN2_SM_CTRL_SM_SOC_INT BIT(1) +#define BERLIN2_SM_CTRL_SOC_SM_INT BIT(2) +#define BERLIN2_SM_CTRL_ADC_SEL(x) ((x) << 5) /* 0-15 */ +#define BERLIN2_SM_CTRL_ADC_SEL_MASK GENMASK(8, 5) +#define BERLIN2_SM_CTRL_ADC_POWER BIT(9) +#define BERLIN2_SM_CTRL_ADC_CLKSEL_DIV2 (0x0 << 10) +#define BERLIN2_SM_CTRL_ADC_CLKSEL_DIV3 (0x1 << 10) +#define BERLIN2_SM_CTRL_ADC_CLKSEL_DIV4 (0x2 << 10) +#define BERLIN2_SM_CTRL_ADC_CLKSEL_DIV8 (0x3 << 10) +#define BERLIN2_SM_CTRL_ADC_CLKSEL_MASK GENMASK(11, 10) +#define BERLIN2_SM_CTRL_ADC_START BIT(12) +#define BERLIN2_SM_CTRL_ADC_RESET BIT(13) +#define BERLIN2_SM_CTRL_ADC_BANDGAP_RDY BIT(14) +#define BERLIN2_SM_CTRL_ADC_CONT_SINGLE (0x0 << 15) +#define BERLIN2_SM_CTRL_ADC_CONT_CONTINUOUS (0x1 << 15) +#define BERLIN2_SM_CTRL_ADC_BUFFER_EN BIT(16) +#define BERLIN2_SM_CTRL_ADC_VREF_EXT (0x0 << 17) +#define BERLIN2_SM_CTRL_ADC_VREF_INT (0x1 << 17) +#define BERLIN2_SM_CTRL_ADC_ROTATE BIT(19) +#define BERLIN2_SM_CTRL_TSEN_EN BIT(20) +#define BERLIN2_SM_CTRL_TSEN_CLK_SEL_125 (0x0 << 21) /* 1.25 MHz */ +#define BERLIN2_SM_CTRL_TSEN_CLK_SEL_250 (0x1 << 21) /* 2.5 MHz */ +#define BERLIN2_SM_CTRL_TSEN_MODE_0_125 (0x0 << 22) /* 0-125 C */ +#define BERLIN2_SM_CTRL_TSEN_MODE_10_50 (0x1 << 22) /* 10-50 C */ +#define BERLIN2_SM_CTRL_TSEN_RESET BIT(29) +#define BERLIN2_SM_ADC_DATA 0x20 +#define BERLIN2_SM_ADC_MASK GENMASK(9, 0) +#define BERLIN2_SM_ADC_STATUS 0x1c +#define BERLIN2_SM_ADC_STATUS_DATA_RDY(x) BIT(x) /* 0-15 */ +#define BERLIN2_SM_ADC_STATUS_DATA_RDY_MASK GENMASK(15, 0) +#define BERLIN2_SM_ADC_STATUS_INT_EN(x) (BIT(x) << 16) /* 0-15 */ +#define BERLIN2_SM_ADC_STATUS_INT_EN_MASK GENMASK(31, 16) +#define BERLIN2_SM_TSEN_STATUS 0x24 +#define BERLIN2_SM_TSEN_STATUS_DATA_RDY BIT(0) +#define BERLIN2_SM_TSEN_STATUS_INT_EN BIT(1) +#define BERLIN2_SM_TSEN_DATA 0x28 +#define BERLIN2_SM_TSEN_MASK GENMASK(9, 0) +#define BERLIN2_SM_TSEN_CTRL 0x74 +#define BERLIN2_SM_TSEN_CTRL_START BIT(8) +#define BERLIN2_SM_TSEN_CTRL_SETTLING_4 (0x0 << 21) /* 4 us */ +#define BERLIN2_SM_TSEN_CTRL_SETTLING_12 (0x1 << 21) /* 12 us */ +#define BERLIN2_SM_TSEN_CTRL_SETTLING_MASK BIT(21) +#define BERLIN2_SM_TSEN_CTRL_TRIM(x) ((x) << 22) +#define BERLIN2_SM_TSEN_CTRL_TRIM_MASK GENMASK(25, 22) + +struct berlin2_adc_priv { + struct regmap *regmap; + struct mutex lock; + wait_queue_head_t wq; + bool data_available; + int data; +}; + +#define BERLIN2_ADC_CHANNEL(n, t) \ + { \ + .channel = n, \ + .datasheet_name = "channel"#n, \ + .type = t, \ + .indexed = 1, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + } + +static const struct iio_chan_spec berlin2_adc_channels[] = { + BERLIN2_ADC_CHANNEL(0, IIO_VOLTAGE), /* external input */ + BERLIN2_ADC_CHANNEL(1, IIO_VOLTAGE), /* external input */ + BERLIN2_ADC_CHANNEL(2, IIO_VOLTAGE), /* external input */ + BERLIN2_ADC_CHANNEL(3, IIO_VOLTAGE), /* external input */ + BERLIN2_ADC_CHANNEL(4, IIO_VOLTAGE), /* reserved */ + BERLIN2_ADC_CHANNEL(5, IIO_VOLTAGE), /* reserved */ + { /* temperature sensor */ + .channel = 6, + .datasheet_name = "channel6", + .type = IIO_TEMP, + .indexed = 0, + .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED), + }, + BERLIN2_ADC_CHANNEL(7, IIO_VOLTAGE), /* reserved */ + IIO_CHAN_SOFT_TIMESTAMP(8), /* timestamp */ +}; + +static int berlin2_adc_read(struct iio_dev *indio_dev, int channel) +{ + struct berlin2_adc_priv *priv = iio_priv(indio_dev); + int data, ret; + + mutex_lock(&priv->lock); + + /* Enable the interrupts */ + regmap_write(priv->regmap, BERLIN2_SM_ADC_STATUS, + BERLIN2_SM_ADC_STATUS_INT_EN(channel)); + + /* Configure the ADC */ + regmap_update_bits(priv->regmap, BERLIN2_SM_CTRL, + BERLIN2_SM_CTRL_ADC_RESET | + BERLIN2_SM_CTRL_ADC_SEL_MASK | + BERLIN2_SM_CTRL_ADC_START, + BERLIN2_SM_CTRL_ADC_SEL(channel) | + BERLIN2_SM_CTRL_ADC_START); + + ret = wait_event_interruptible_timeout(priv->wq, priv->data_available, + msecs_to_jiffies(1000)); + + /* Disable the interrupts */ + regmap_clear_bits(priv->regmap, BERLIN2_SM_ADC_STATUS, + BERLIN2_SM_ADC_STATUS_INT_EN(channel)); + + if (ret == 0) + ret = -ETIMEDOUT; + if (ret < 0) { + mutex_unlock(&priv->lock); + return ret; + } + + regmap_clear_bits(priv->regmap, BERLIN2_SM_CTRL, + BERLIN2_SM_CTRL_ADC_START); + + data = priv->data; + priv->data_available = false; + + mutex_unlock(&priv->lock); + + return data; +} + +static int berlin2_adc_tsen_read(struct iio_dev *indio_dev) +{ + struct berlin2_adc_priv *priv = iio_priv(indio_dev); + int data, ret; + + mutex_lock(&priv->lock); + + /* Enable interrupts */ + regmap_write(priv->regmap, BERLIN2_SM_TSEN_STATUS, + BERLIN2_SM_TSEN_STATUS_INT_EN); + + /* Configure the ADC */ + regmap_update_bits(priv->regmap, BERLIN2_SM_CTRL, + BERLIN2_SM_CTRL_TSEN_RESET | + BERLIN2_SM_CTRL_ADC_ROTATE, + BERLIN2_SM_CTRL_ADC_ROTATE); + + /* Configure the temperature sensor */ + regmap_update_bits(priv->regmap, BERLIN2_SM_TSEN_CTRL, + BERLIN2_SM_TSEN_CTRL_TRIM_MASK | + BERLIN2_SM_TSEN_CTRL_SETTLING_MASK | + BERLIN2_SM_TSEN_CTRL_START, + BERLIN2_SM_TSEN_CTRL_TRIM(3) | + BERLIN2_SM_TSEN_CTRL_SETTLING_12 | + BERLIN2_SM_TSEN_CTRL_START); + + ret = wait_event_interruptible_timeout(priv->wq, priv->data_available, + msecs_to_jiffies(1000)); + + /* Disable interrupts */ + regmap_clear_bits(priv->regmap, BERLIN2_SM_TSEN_STATUS, + BERLIN2_SM_TSEN_STATUS_INT_EN); + + if (ret == 0) + ret = -ETIMEDOUT; + if (ret < 0) { + mutex_unlock(&priv->lock); + return ret; + } + + regmap_clear_bits(priv->regmap, BERLIN2_SM_TSEN_CTRL, + BERLIN2_SM_TSEN_CTRL_START); + + data = priv->data; + priv->data_available = false; + + mutex_unlock(&priv->lock); + + return data; +} + +static int berlin2_adc_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int *val, + int *val2, long mask) +{ + int temp; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + if (chan->type != IIO_VOLTAGE) + return -EINVAL; + + *val = berlin2_adc_read(indio_dev, chan->channel); + if (*val < 0) + return *val; + + return IIO_VAL_INT; + case IIO_CHAN_INFO_PROCESSED: + if (chan->type != IIO_TEMP) + return -EINVAL; + + temp = berlin2_adc_tsen_read(indio_dev); + if (temp < 0) + return temp; + + if (temp > 2047) + temp -= 4096; + + /* Convert to milli Celsius */ + *val = ((temp * 100000) / 264 - 270000); + return IIO_VAL_INT; + default: + break; + } + + return -EINVAL; +} + +static irqreturn_t berlin2_adc_irq(int irq, void *private) +{ + struct berlin2_adc_priv *priv = iio_priv(private); + unsigned val; + + regmap_read(priv->regmap, BERLIN2_SM_ADC_STATUS, &val); + if (val & BERLIN2_SM_ADC_STATUS_DATA_RDY_MASK) { + regmap_read(priv->regmap, BERLIN2_SM_ADC_DATA, &priv->data); + priv->data &= BERLIN2_SM_ADC_MASK; + + val &= ~BERLIN2_SM_ADC_STATUS_DATA_RDY_MASK; + regmap_write(priv->regmap, BERLIN2_SM_ADC_STATUS, val); + + priv->data_available = true; + wake_up_interruptible(&priv->wq); + } + + return IRQ_HANDLED; +} + +static irqreturn_t berlin2_adc_tsen_irq(int irq, void *private) +{ + struct berlin2_adc_priv *priv = iio_priv(private); + unsigned val; + + regmap_read(priv->regmap, BERLIN2_SM_TSEN_STATUS, &val); + if (val & BERLIN2_SM_TSEN_STATUS_DATA_RDY) { + regmap_read(priv->regmap, BERLIN2_SM_TSEN_DATA, &priv->data); + priv->data &= BERLIN2_SM_TSEN_MASK; + + val &= ~BERLIN2_SM_TSEN_STATUS_DATA_RDY; + regmap_write(priv->regmap, BERLIN2_SM_TSEN_STATUS, val); + + priv->data_available = true; + wake_up_interruptible(&priv->wq); + } + + return IRQ_HANDLED; +} + +static const struct iio_info berlin2_adc_info = { + .read_raw = berlin2_adc_read_raw, +}; + +static void berlin2_adc_powerdown(void *regmap) +{ + regmap_clear_bits(regmap, BERLIN2_SM_CTRL, BERLIN2_SM_CTRL_ADC_POWER); + +} + +static int berlin2_adc_probe(struct platform_device *pdev) +{ + struct iio_dev *indio_dev; + struct berlin2_adc_priv *priv; + struct device_node *parent_np = of_get_parent(pdev->dev.of_node); + int irq, tsen_irq; + int ret; + + indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*priv)); + if (!indio_dev) { + of_node_put(parent_np); + return -ENOMEM; + } + + priv = iio_priv(indio_dev); + + priv->regmap = syscon_node_to_regmap(parent_np); + of_node_put(parent_np); + if (IS_ERR(priv->regmap)) + return PTR_ERR(priv->regmap); + + irq = platform_get_irq_byname(pdev, "adc"); + if (irq < 0) + return irq; + + tsen_irq = platform_get_irq_byname(pdev, "tsen"); + if (tsen_irq < 0) + return tsen_irq; + + ret = devm_request_irq(&pdev->dev, irq, berlin2_adc_irq, 0, + pdev->dev.driver->name, indio_dev); + if (ret) + return ret; + + ret = devm_request_irq(&pdev->dev, tsen_irq, berlin2_adc_tsen_irq, + 0, pdev->dev.driver->name, indio_dev); + if (ret) + return ret; + + init_waitqueue_head(&priv->wq); + mutex_init(&priv->lock); + + indio_dev->name = dev_name(&pdev->dev); + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->info = &berlin2_adc_info; + + indio_dev->channels = berlin2_adc_channels; + indio_dev->num_channels = ARRAY_SIZE(berlin2_adc_channels); + + /* Power up the ADC */ + regmap_set_bits(priv->regmap, BERLIN2_SM_CTRL, + BERLIN2_SM_CTRL_ADC_POWER); + + ret = devm_add_action_or_reset(&pdev->dev, berlin2_adc_powerdown, + priv->regmap); + if (ret) + return ret; + + return devm_iio_device_register(&pdev->dev, indio_dev); +} + +static const struct of_device_id berlin2_adc_match[] = { + { .compatible = "marvell,berlin2-adc", }, + { } +}; +MODULE_DEVICE_TABLE(of, berlin2_adc_match); + +static struct platform_driver berlin2_adc_driver = { + .driver = { + .name = "berlin2-adc", + .of_match_table = berlin2_adc_match, + }, + .probe = berlin2_adc_probe, +}; +module_platform_driver(berlin2_adc_driver); + +MODULE_AUTHOR("Antoine Tenart "); +MODULE_DESCRIPTION("Marvell Berlin2 ADC driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/adc/cc10001_adc.c b/drivers/iio/adc/cc10001_adc.c new file mode 100644 index 000000000..d42b74732 --- /dev/null +++ b/drivers/iio/adc/cc10001_adc.c @@ -0,0 +1,409 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2014-2015 Imagination Technologies Ltd. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +/* Registers */ +#define CC10001_ADC_CONFIG 0x00 +#define CC10001_ADC_START_CONV BIT(4) +#define CC10001_ADC_MODE_SINGLE_CONV BIT(5) + +#define CC10001_ADC_DDATA_OUT 0x04 +#define CC10001_ADC_EOC 0x08 +#define CC10001_ADC_EOC_SET BIT(0) + +#define CC10001_ADC_CHSEL_SAMPLED 0x0c +#define CC10001_ADC_POWER_DOWN 0x10 +#define CC10001_ADC_POWER_DOWN_SET BIT(0) + +#define CC10001_ADC_DEBUG 0x14 +#define CC10001_ADC_DATA_COUNT 0x20 + +#define CC10001_ADC_DATA_MASK GENMASK(9, 0) +#define CC10001_ADC_NUM_CHANNELS 8 +#define CC10001_ADC_CH_MASK GENMASK(2, 0) + +#define CC10001_INVALID_SAMPLED 0xffff +#define CC10001_MAX_POLL_COUNT 20 + +/* + * As per device specification, wait six clock cycles after power-up to + * activate START. Since adding two more clock cycles delay does not + * impact the performance too much, we are adding two additional cycles delay + * intentionally here. + */ +#define CC10001_WAIT_CYCLES 8 + +struct cc10001_adc_device { + void __iomem *reg_base; + struct clk *adc_clk; + struct regulator *reg; + u16 *buf; + + bool shared; + struct mutex lock; + unsigned int start_delay_ns; + unsigned int eoc_delay_ns; +}; + +static inline void cc10001_adc_write_reg(struct cc10001_adc_device *adc_dev, + u32 reg, u32 val) +{ + writel(val, adc_dev->reg_base + reg); +} + +static inline u32 cc10001_adc_read_reg(struct cc10001_adc_device *adc_dev, + u32 reg) +{ + return readl(adc_dev->reg_base + reg); +} + +static void cc10001_adc_power_up(struct cc10001_adc_device *adc_dev) +{ + cc10001_adc_write_reg(adc_dev, CC10001_ADC_POWER_DOWN, 0); + ndelay(adc_dev->start_delay_ns); +} + +static void cc10001_adc_power_down(struct cc10001_adc_device *adc_dev) +{ + cc10001_adc_write_reg(adc_dev, CC10001_ADC_POWER_DOWN, + CC10001_ADC_POWER_DOWN_SET); +} + +static void cc10001_adc_start(struct cc10001_adc_device *adc_dev, + unsigned int channel) +{ + u32 val; + + /* Channel selection and mode of operation */ + val = (channel & CC10001_ADC_CH_MASK) | CC10001_ADC_MODE_SINGLE_CONV; + cc10001_adc_write_reg(adc_dev, CC10001_ADC_CONFIG, val); + + udelay(1); + val = cc10001_adc_read_reg(adc_dev, CC10001_ADC_CONFIG); + val = val | CC10001_ADC_START_CONV; + cc10001_adc_write_reg(adc_dev, CC10001_ADC_CONFIG, val); +} + +static u16 cc10001_adc_poll_done(struct iio_dev *indio_dev, + unsigned int channel, + unsigned int delay) +{ + struct cc10001_adc_device *adc_dev = iio_priv(indio_dev); + unsigned int poll_count = 0; + + while (!(cc10001_adc_read_reg(adc_dev, CC10001_ADC_EOC) & + CC10001_ADC_EOC_SET)) { + + ndelay(delay); + if (poll_count++ == CC10001_MAX_POLL_COUNT) + return CC10001_INVALID_SAMPLED; + } + + poll_count = 0; + while ((cc10001_adc_read_reg(adc_dev, CC10001_ADC_CHSEL_SAMPLED) & + CC10001_ADC_CH_MASK) != channel) { + + ndelay(delay); + if (poll_count++ == CC10001_MAX_POLL_COUNT) + return CC10001_INVALID_SAMPLED; + } + + /* Read the 10 bit output register */ + return cc10001_adc_read_reg(adc_dev, CC10001_ADC_DDATA_OUT) & + CC10001_ADC_DATA_MASK; +} + +static irqreturn_t cc10001_adc_trigger_h(int irq, void *p) +{ + struct cc10001_adc_device *adc_dev; + struct iio_poll_func *pf = p; + struct iio_dev *indio_dev; + unsigned int delay_ns; + unsigned int channel; + unsigned int scan_idx; + bool sample_invalid; + u16 *data; + int i; + + indio_dev = pf->indio_dev; + adc_dev = iio_priv(indio_dev); + data = adc_dev->buf; + + mutex_lock(&adc_dev->lock); + + if (!adc_dev->shared) + cc10001_adc_power_up(adc_dev); + + /* Calculate delay step for eoc and sampled data */ + delay_ns = adc_dev->eoc_delay_ns / CC10001_MAX_POLL_COUNT; + + i = 0; + sample_invalid = false; + iio_for_each_active_channel(indio_dev, scan_idx) { + channel = indio_dev->channels[scan_idx].channel; + cc10001_adc_start(adc_dev, channel); + + data[i] = cc10001_adc_poll_done(indio_dev, channel, delay_ns); + if (data[i] == CC10001_INVALID_SAMPLED) { + dev_warn(&indio_dev->dev, + "invalid sample on channel %d\n", channel); + sample_invalid = true; + goto done; + } + i++; + } + +done: + if (!adc_dev->shared) + cc10001_adc_power_down(adc_dev); + + mutex_unlock(&adc_dev->lock); + + if (!sample_invalid) + iio_push_to_buffers_with_timestamp(indio_dev, data, + iio_get_time_ns(indio_dev)); + iio_trigger_notify_done(indio_dev->trig); + + return IRQ_HANDLED; +} + +static u16 cc10001_adc_read_raw_voltage(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan) +{ + struct cc10001_adc_device *adc_dev = iio_priv(indio_dev); + unsigned int delay_ns; + u16 val; + + if (!adc_dev->shared) + cc10001_adc_power_up(adc_dev); + + /* Calculate delay step for eoc and sampled data */ + delay_ns = adc_dev->eoc_delay_ns / CC10001_MAX_POLL_COUNT; + + cc10001_adc_start(adc_dev, chan->channel); + + val = cc10001_adc_poll_done(indio_dev, chan->channel, delay_ns); + + if (!adc_dev->shared) + cc10001_adc_power_down(adc_dev); + + return val; +} + +static int cc10001_adc_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct cc10001_adc_device *adc_dev = iio_priv(indio_dev); + int ret; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + if (iio_buffer_enabled(indio_dev)) + return -EBUSY; + mutex_lock(&adc_dev->lock); + *val = cc10001_adc_read_raw_voltage(indio_dev, chan); + mutex_unlock(&adc_dev->lock); + + if (*val == CC10001_INVALID_SAMPLED) + return -EIO; + return IIO_VAL_INT; + + case IIO_CHAN_INFO_SCALE: + ret = regulator_get_voltage(adc_dev->reg); + if (ret < 0) + return ret; + + *val = ret / 1000; + *val2 = chan->scan_type.realbits; + return IIO_VAL_FRACTIONAL_LOG2; + + default: + return -EINVAL; + } +} + +static int cc10001_update_scan_mode(struct iio_dev *indio_dev, + const unsigned long *scan_mask) +{ + struct cc10001_adc_device *adc_dev = iio_priv(indio_dev); + + kfree(adc_dev->buf); + adc_dev->buf = kmalloc(indio_dev->scan_bytes, GFP_KERNEL); + if (!adc_dev->buf) + return -ENOMEM; + + return 0; +} + +static const struct iio_info cc10001_adc_info = { + .read_raw = &cc10001_adc_read_raw, + .update_scan_mode = &cc10001_update_scan_mode, +}; + +static int cc10001_adc_channel_init(struct iio_dev *indio_dev, + unsigned long channel_map) +{ + struct iio_chan_spec *chan_array; + unsigned int bit, idx = 0; + + indio_dev->num_channels = bitmap_weight(&channel_map, + CC10001_ADC_NUM_CHANNELS) + 1; + + chan_array = devm_kcalloc(&indio_dev->dev, indio_dev->num_channels, + sizeof(struct iio_chan_spec), + GFP_KERNEL); + if (!chan_array) + return -ENOMEM; + + for_each_set_bit(bit, &channel_map, CC10001_ADC_NUM_CHANNELS) { + struct iio_chan_spec *chan = &chan_array[idx]; + + chan->type = IIO_VOLTAGE; + chan->indexed = 1; + chan->channel = bit; + chan->scan_index = idx; + chan->scan_type.sign = 'u'; + chan->scan_type.realbits = 10; + chan->scan_type.storagebits = 16; + chan->info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE); + chan->info_mask_separate = BIT(IIO_CHAN_INFO_RAW); + idx++; + } + + chan_array[idx] = IIO_CHAN_SOFT_TIMESTAMP(idx); + + indio_dev->channels = chan_array; + + return 0; +} + +static void cc10001_reg_disable(void *priv) +{ + regulator_disable(priv); +} + +static void cc10001_pd_cb(void *priv) +{ + cc10001_adc_power_down(priv); +} + +static int cc10001_adc_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct device_node *node = dev->of_node; + struct cc10001_adc_device *adc_dev; + unsigned long adc_clk_rate; + struct iio_dev *indio_dev; + unsigned long channel_map; + int ret; + + indio_dev = devm_iio_device_alloc(dev, sizeof(*adc_dev)); + if (indio_dev == NULL) + return -ENOMEM; + + adc_dev = iio_priv(indio_dev); + + channel_map = GENMASK(CC10001_ADC_NUM_CHANNELS - 1, 0); + if (!of_property_read_u32(node, "adc-reserved-channels", &ret)) { + adc_dev->shared = true; + channel_map &= ~ret; + } + + adc_dev->reg = devm_regulator_get(dev, "vref"); + if (IS_ERR(adc_dev->reg)) + return PTR_ERR(adc_dev->reg); + + ret = regulator_enable(adc_dev->reg); + if (ret) + return ret; + + ret = devm_add_action_or_reset(dev, cc10001_reg_disable, adc_dev->reg); + if (ret) + return ret; + + indio_dev->name = dev_name(dev); + indio_dev->info = &cc10001_adc_info; + indio_dev->modes = INDIO_DIRECT_MODE; + + adc_dev->reg_base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(adc_dev->reg_base)) + return PTR_ERR(adc_dev->reg_base); + + adc_dev->adc_clk = devm_clk_get_enabled(dev, "adc"); + if (IS_ERR(adc_dev->adc_clk)) { + dev_err(dev, "failed to get/enable the clock\n"); + return PTR_ERR(adc_dev->adc_clk); + } + + adc_clk_rate = clk_get_rate(adc_dev->adc_clk); + if (!adc_clk_rate) { + dev_err(dev, "null clock rate!\n"); + return -EINVAL; + } + + adc_dev->eoc_delay_ns = NSEC_PER_SEC / adc_clk_rate; + adc_dev->start_delay_ns = adc_dev->eoc_delay_ns * CC10001_WAIT_CYCLES; + + /* + * There is only one register to power-up/power-down the AUX ADC. + * If the ADC is shared among multiple CPUs, always power it up here. + * If the ADC is used only by the MIPS, power-up/power-down at runtime. + */ + if (adc_dev->shared) + cc10001_adc_power_up(adc_dev); + + ret = devm_add_action_or_reset(dev, cc10001_pd_cb, adc_dev); + if (ret) + return ret; + /* Setup the ADC channels available on the device */ + ret = cc10001_adc_channel_init(indio_dev, channel_map); + if (ret < 0) + return ret; + + mutex_init(&adc_dev->lock); + + ret = devm_iio_triggered_buffer_setup(dev, indio_dev, NULL, + &cc10001_adc_trigger_h, NULL); + if (ret < 0) + return ret; + + return devm_iio_device_register(dev, indio_dev); +} + +static const struct of_device_id cc10001_adc_dt_ids[] = { + { .compatible = "cosmic,10001-adc", }, + { } +}; +MODULE_DEVICE_TABLE(of, cc10001_adc_dt_ids); + +static struct platform_driver cc10001_adc_driver = { + .driver = { + .name = "cc10001-adc", + .of_match_table = cc10001_adc_dt_ids, + }, + .probe = cc10001_adc_probe, +}; +module_platform_driver(cc10001_adc_driver); + +MODULE_AUTHOR("Phani Movva "); +MODULE_DESCRIPTION("Cosmic Circuits ADC driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/adc/cpcap-adc.c b/drivers/iio/adc/cpcap-adc.c new file mode 100644 index 000000000..223e2737c --- /dev/null +++ b/drivers/iio/adc/cpcap-adc.c @@ -0,0 +1,1035 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2017 Tony Lindgren + * + * Rewritten for Linux IIO framework with some code based on + * earlier driver found in the Motorola Linux kernel: + * + * Copyright (C) 2009-2010 Motorola, Inc. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +/* Register CPCAP_REG_ADCC1 bits */ +#define CPCAP_BIT_ADEN_AUTO_CLR BIT(15) /* Currently unused */ +#define CPCAP_BIT_CAL_MODE BIT(14) /* Set with BIT_RAND0 */ +#define CPCAP_BIT_ADC_CLK_SEL1 BIT(13) /* Currently unused */ +#define CPCAP_BIT_ADC_CLK_SEL0 BIT(12) /* Currently unused */ +#define CPCAP_BIT_ATOX BIT(11) +#define CPCAP_BIT_ATO3 BIT(10) +#define CPCAP_BIT_ATO2 BIT(9) +#define CPCAP_BIT_ATO1 BIT(8) +#define CPCAP_BIT_ATO0 BIT(7) +#define CPCAP_BIT_ADA2 BIT(6) +#define CPCAP_BIT_ADA1 BIT(5) +#define CPCAP_BIT_ADA0 BIT(4) +#define CPCAP_BIT_AD_SEL1 BIT(3) /* Set for bank1 */ +#define CPCAP_BIT_RAND1 BIT(2) /* Set for channel 16 & 17 */ +#define CPCAP_BIT_RAND0 BIT(1) /* Set with CAL_MODE */ +#define CPCAP_BIT_ADEN BIT(0) /* Currently unused */ + +#define CPCAP_REG_ADCC1_DEFAULTS (CPCAP_BIT_ADEN_AUTO_CLR | \ + CPCAP_BIT_ADC_CLK_SEL0 | \ + CPCAP_BIT_RAND1) + +/* Register CPCAP_REG_ADCC2 bits */ +#define CPCAP_BIT_CAL_FACTOR_ENABLE BIT(15) /* Currently unused */ +#define CPCAP_BIT_BATDETB_EN BIT(14) /* Currently unused */ +#define CPCAP_BIT_ADTRIG_ONESHOT BIT(13) /* Set for !TIMING_IMM */ +#define CPCAP_BIT_ASC BIT(12) /* Set for TIMING_IMM */ +#define CPCAP_BIT_ATOX_PS_FACTOR BIT(11) +#define CPCAP_BIT_ADC_PS_FACTOR1 BIT(10) +#define CPCAP_BIT_ADC_PS_FACTOR0 BIT(9) +#define CPCAP_BIT_AD4_SELECT BIT(8) /* Currently unused */ +#define CPCAP_BIT_ADC_BUSY BIT(7) /* Currently unused */ +#define CPCAP_BIT_THERMBIAS_EN BIT(6) /* Bias for AD0_BATTDETB */ +#define CPCAP_BIT_ADTRIG_DIS BIT(5) /* Disable interrupt */ +#define CPCAP_BIT_LIADC BIT(4) /* Currently unused */ +#define CPCAP_BIT_TS_REFEN BIT(3) /* Currently unused */ +#define CPCAP_BIT_TS_M2 BIT(2) /* Currently unused */ +#define CPCAP_BIT_TS_M1 BIT(1) /* Currently unused */ +#define CPCAP_BIT_TS_M0 BIT(0) /* Currently unused */ + +#define CPCAP_REG_ADCC2_DEFAULTS (CPCAP_BIT_AD4_SELECT | \ + CPCAP_BIT_ADTRIG_DIS | \ + CPCAP_BIT_LIADC | \ + CPCAP_BIT_TS_M2 | \ + CPCAP_BIT_TS_M1) + +#define CPCAP_MAX_TEMP_LVL 27 +#define CPCAP_FOUR_POINT_TWO_ADC 801 +#define ST_ADC_CAL_CHRGI_HIGH_THRESHOLD 530 +#define ST_ADC_CAL_CHRGI_LOW_THRESHOLD 494 +#define ST_ADC_CAL_BATTI_HIGH_THRESHOLD 530 +#define ST_ADC_CAL_BATTI_LOW_THRESHOLD 494 +#define ST_ADC_CALIBRATE_DIFF_THRESHOLD 3 + +#define CPCAP_ADC_MAX_RETRIES 5 /* Calibration */ + +/* + * struct cpcap_adc_ato - timing settings for cpcap adc + * + * Unfortunately no cpcap documentation available, please document when + * using these. + */ +struct cpcap_adc_ato { + unsigned short ato_in; + unsigned short atox_in; + unsigned short adc_ps_factor_in; + unsigned short atox_ps_factor_in; + unsigned short ato_out; + unsigned short atox_out; + unsigned short adc_ps_factor_out; + unsigned short atox_ps_factor_out; +}; + +/** + * struct cpcap_adc - cpcap adc device driver data + * @reg: cpcap regmap + * @dev: struct device + * @vendor: cpcap vendor + * @irq: interrupt + * @lock: mutex + * @ato: request timings + * @wq_data_avail: work queue + * @done: work done + */ +struct cpcap_adc { + struct regmap *reg; + struct device *dev; + u16 vendor; + int irq; + struct mutex lock; /* ADC register access lock */ + const struct cpcap_adc_ato *ato; + wait_queue_head_t wq_data_avail; + bool done; +}; + +/* + * enum cpcap_adc_channel - cpcap adc channels + */ +enum cpcap_adc_channel { + /* Bank0 channels */ + CPCAP_ADC_AD0, /* Battery temperature */ + CPCAP_ADC_BATTP, /* Battery voltage */ + CPCAP_ADC_VBUS, /* USB VBUS voltage */ + CPCAP_ADC_AD3, /* Die temperature when charging */ + CPCAP_ADC_BPLUS_AD4, /* Another battery or system voltage */ + CPCAP_ADC_CHG_ISENSE, /* Calibrated charge current */ + CPCAP_ADC_BATTI, /* Calibrated system current */ + CPCAP_ADC_USB_ID, /* USB OTG ID, unused on droid 4? */ + + /* Bank1 channels */ + CPCAP_ADC_AD8, /* Seems unused */ + CPCAP_ADC_AD9, /* Seems unused */ + CPCAP_ADC_LICELL, /* Maybe system voltage? Always 3V */ + CPCAP_ADC_HV_BATTP, /* Another battery detection? */ + CPCAP_ADC_TSX1_AD12, /* Seems unused, for touchscreen? */ + CPCAP_ADC_TSX2_AD13, /* Seems unused, for touchscreen? */ + CPCAP_ADC_TSY1_AD14, /* Seems unused, for touchscreen? */ + CPCAP_ADC_TSY2_AD15, /* Seems unused, for touchscreen? */ + + /* Remuxed channels using bank0 entries */ + CPCAP_ADC_BATTP_PI16, /* Alternative mux mode for BATTP */ + CPCAP_ADC_BATTI_PI17, /* Alternative mux mode for BATTI */ + + CPCAP_ADC_CHANNEL_NUM, +}; + +/* + * enum cpcap_adc_timing - cpcap adc timing options + * + * CPCAP_ADC_TIMING_IMM seems to be immediate with no timings. + * Please document when using. + */ +enum cpcap_adc_timing { + CPCAP_ADC_TIMING_IMM, + CPCAP_ADC_TIMING_IN, + CPCAP_ADC_TIMING_OUT, +}; + +/** + * struct cpcap_adc_phasing_tbl - cpcap phasing table + * @offset: offset in the phasing table + * @multiplier: multiplier in the phasing table + * @divider: divider in the phasing table + * @min: minimum value + * @max: maximum value + */ +struct cpcap_adc_phasing_tbl { + short offset; + unsigned short multiplier; + unsigned short divider; + short min; + short max; +}; + +/** + * struct cpcap_adc_conversion_tbl - cpcap conversion table + * @conv_type: conversion type + * @align_offset: align offset + * @conv_offset: conversion offset + * @cal_offset: calibration offset + * @multiplier: conversion multiplier + * @divider: conversion divider + */ +struct cpcap_adc_conversion_tbl { + enum iio_chan_info_enum conv_type; + int align_offset; + int conv_offset; + int cal_offset; + int multiplier; + int divider; +}; + +/** + * struct cpcap_adc_request - cpcap adc request + * @channel: request channel + * @phase_tbl: channel phasing table + * @conv_tbl: channel conversion table + * @bank_index: channel index within the bank + * @timing: timing settings + * @result: result + */ +struct cpcap_adc_request { + int channel; + const struct cpcap_adc_phasing_tbl *phase_tbl; + const struct cpcap_adc_conversion_tbl *conv_tbl; + int bank_index; + enum cpcap_adc_timing timing; + int result; +}; + +/* Phasing table for channels. Note that channels 16 & 17 use BATTP and BATTI */ +static const struct cpcap_adc_phasing_tbl bank_phasing[] = { + /* Bank0 */ + [CPCAP_ADC_AD0] = {0, 0x80, 0x80, 0, 1023}, + [CPCAP_ADC_BATTP] = {0, 0x80, 0x80, 0, 1023}, + [CPCAP_ADC_VBUS] = {0, 0x80, 0x80, 0, 1023}, + [CPCAP_ADC_AD3] = {0, 0x80, 0x80, 0, 1023}, + [CPCAP_ADC_BPLUS_AD4] = {0, 0x80, 0x80, 0, 1023}, + [CPCAP_ADC_CHG_ISENSE] = {0, 0x80, 0x80, -512, 511}, + [CPCAP_ADC_BATTI] = {0, 0x80, 0x80, -512, 511}, + [CPCAP_ADC_USB_ID] = {0, 0x80, 0x80, 0, 1023}, + + /* Bank1 */ + [CPCAP_ADC_AD8] = {0, 0x80, 0x80, 0, 1023}, + [CPCAP_ADC_AD9] = {0, 0x80, 0x80, 0, 1023}, + [CPCAP_ADC_LICELL] = {0, 0x80, 0x80, 0, 1023}, + [CPCAP_ADC_HV_BATTP] = {0, 0x80, 0x80, 0, 1023}, + [CPCAP_ADC_TSX1_AD12] = {0, 0x80, 0x80, 0, 1023}, + [CPCAP_ADC_TSX2_AD13] = {0, 0x80, 0x80, 0, 1023}, + [CPCAP_ADC_TSY1_AD14] = {0, 0x80, 0x80, 0, 1023}, + [CPCAP_ADC_TSY2_AD15] = {0, 0x80, 0x80, 0, 1023}, +}; + +/* + * Conversion table for channels. Updated during init based on calibration. + * Here too channels 16 & 17 use BATTP and BATTI. + */ +static struct cpcap_adc_conversion_tbl bank_conversion[] = { + /* Bank0 */ + [CPCAP_ADC_AD0] = { + IIO_CHAN_INFO_PROCESSED, 0, 0, 0, 1, 1, + }, + [CPCAP_ADC_BATTP] = { + IIO_CHAN_INFO_PROCESSED, 0, 2400, 0, 2300, 1023, + }, + [CPCAP_ADC_VBUS] = { + IIO_CHAN_INFO_PROCESSED, 0, 0, 0, 10000, 1023, + }, + [CPCAP_ADC_AD3] = { + IIO_CHAN_INFO_PROCESSED, 0, 0, 0, 1, 1, + }, + [CPCAP_ADC_BPLUS_AD4] = { + IIO_CHAN_INFO_PROCESSED, 0, 2400, 0, 2300, 1023, + }, + [CPCAP_ADC_CHG_ISENSE] = { + IIO_CHAN_INFO_PROCESSED, -512, 2, 0, 5000, 1023, + }, + [CPCAP_ADC_BATTI] = { + IIO_CHAN_INFO_PROCESSED, -512, 2, 0, 5000, 1023, + }, + [CPCAP_ADC_USB_ID] = { + IIO_CHAN_INFO_RAW, 0, 0, 0, 1, 1, + }, + + /* Bank1 */ + [CPCAP_ADC_AD8] = { + IIO_CHAN_INFO_RAW, 0, 0, 0, 1, 1, + }, + [CPCAP_ADC_AD9] = { + IIO_CHAN_INFO_RAW, 0, 0, 0, 1, 1, + }, + [CPCAP_ADC_LICELL] = { + IIO_CHAN_INFO_PROCESSED, 0, 0, 0, 3400, 1023, + }, + [CPCAP_ADC_HV_BATTP] = { + IIO_CHAN_INFO_RAW, 0, 0, 0, 1, 1, + }, + [CPCAP_ADC_TSX1_AD12] = { + IIO_CHAN_INFO_RAW, 0, 0, 0, 1, 1, + }, + [CPCAP_ADC_TSX2_AD13] = { + IIO_CHAN_INFO_RAW, 0, 0, 0, 1, 1, + }, + [CPCAP_ADC_TSY1_AD14] = { + IIO_CHAN_INFO_RAW, 0, 0, 0, 1, 1, + }, + [CPCAP_ADC_TSY2_AD15] = { + IIO_CHAN_INFO_RAW, 0, 0, 0, 1, 1, + }, +}; + +/* + * Temperature lookup table of register values to milliCelcius. + * REVISIT: Check the duplicate 0x3ff entry in a freezer + */ +static const int temp_map[CPCAP_MAX_TEMP_LVL][2] = { + { 0x03ff, -40000 }, + { 0x03ff, -35000 }, + { 0x03ef, -30000 }, + { 0x03b2, -25000 }, + { 0x036c, -20000 }, + { 0x0320, -15000 }, + { 0x02d0, -10000 }, + { 0x027f, -5000 }, + { 0x022f, 0 }, + { 0x01e4, 5000 }, + { 0x019f, 10000 }, + { 0x0161, 15000 }, + { 0x012b, 20000 }, + { 0x00fc, 25000 }, + { 0x00d4, 30000 }, + { 0x00b2, 35000 }, + { 0x0095, 40000 }, + { 0x007d, 45000 }, + { 0x0069, 50000 }, + { 0x0059, 55000 }, + { 0x004b, 60000 }, + { 0x003f, 65000 }, + { 0x0036, 70000 }, + { 0x002e, 75000 }, + { 0x0027, 80000 }, + { 0x0022, 85000 }, + { 0x001d, 90000 }, +}; + +#define CPCAP_CHAN(_type, _index, _address, _datasheet_name) { \ + .type = (_type), \ + .address = (_address), \ + .indexed = 1, \ + .channel = (_index), \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ + BIT(IIO_CHAN_INFO_PROCESSED), \ + .scan_index = (_index), \ + .scan_type = { \ + .sign = 'u', \ + .realbits = 10, \ + .storagebits = 16, \ + .endianness = IIO_CPU, \ + }, \ + .datasheet_name = (_datasheet_name), \ +} + +/* + * The datasheet names are from Motorola mapphone Linux kernel except + * for the last two which might be uncalibrated charge voltage and + * current. + */ +static const struct iio_chan_spec cpcap_adc_channels[] = { + /* Bank0 */ + CPCAP_CHAN(IIO_TEMP, 0, CPCAP_REG_ADCD0, "battdetb"), + CPCAP_CHAN(IIO_VOLTAGE, 1, CPCAP_REG_ADCD1, "battp"), + CPCAP_CHAN(IIO_VOLTAGE, 2, CPCAP_REG_ADCD2, "vbus"), + CPCAP_CHAN(IIO_TEMP, 3, CPCAP_REG_ADCD3, "ad3"), + CPCAP_CHAN(IIO_VOLTAGE, 4, CPCAP_REG_ADCD4, "ad4"), + CPCAP_CHAN(IIO_CURRENT, 5, CPCAP_REG_ADCD5, "chg_isense"), + CPCAP_CHAN(IIO_CURRENT, 6, CPCAP_REG_ADCD6, "batti"), + CPCAP_CHAN(IIO_VOLTAGE, 7, CPCAP_REG_ADCD7, "usb_id"), + + /* Bank1 */ + CPCAP_CHAN(IIO_CURRENT, 8, CPCAP_REG_ADCD0, "ad8"), + CPCAP_CHAN(IIO_VOLTAGE, 9, CPCAP_REG_ADCD1, "ad9"), + CPCAP_CHAN(IIO_VOLTAGE, 10, CPCAP_REG_ADCD2, "licell"), + CPCAP_CHAN(IIO_VOLTAGE, 11, CPCAP_REG_ADCD3, "hv_battp"), + CPCAP_CHAN(IIO_VOLTAGE, 12, CPCAP_REG_ADCD4, "tsx1_ad12"), + CPCAP_CHAN(IIO_VOLTAGE, 13, CPCAP_REG_ADCD5, "tsx2_ad13"), + CPCAP_CHAN(IIO_VOLTAGE, 14, CPCAP_REG_ADCD6, "tsy1_ad14"), + CPCAP_CHAN(IIO_VOLTAGE, 15, CPCAP_REG_ADCD7, "tsy2_ad15"), + + /* There are two registers with multiplexed functionality */ + CPCAP_CHAN(IIO_VOLTAGE, 16, CPCAP_REG_ADCD0, "chg_vsense"), + CPCAP_CHAN(IIO_CURRENT, 17, CPCAP_REG_ADCD1, "batti2"), +}; + +static irqreturn_t cpcap_adc_irq_thread(int irq, void *data) +{ + struct iio_dev *indio_dev = data; + struct cpcap_adc *ddata = iio_priv(indio_dev); + int error; + + error = regmap_set_bits(ddata->reg, CPCAP_REG_ADCC2, + CPCAP_BIT_ADTRIG_DIS); + if (error) + return IRQ_NONE; + + ddata->done = true; + wake_up_interruptible(&ddata->wq_data_avail); + + return IRQ_HANDLED; +} + +/* ADC calibration functions */ +static void cpcap_adc_setup_calibrate(struct cpcap_adc *ddata, + enum cpcap_adc_channel chan) +{ + unsigned int value = 0; + unsigned long timeout = jiffies + msecs_to_jiffies(3000); + int error; + + if ((chan != CPCAP_ADC_CHG_ISENSE) && + (chan != CPCAP_ADC_BATTI)) + return; + + value |= CPCAP_BIT_CAL_MODE | CPCAP_BIT_RAND0; + value |= ((chan << 4) & + (CPCAP_BIT_ADA2 | CPCAP_BIT_ADA1 | CPCAP_BIT_ADA0)); + + error = regmap_update_bits(ddata->reg, CPCAP_REG_ADCC1, + CPCAP_BIT_CAL_MODE | CPCAP_BIT_ATOX | + CPCAP_BIT_ATO3 | CPCAP_BIT_ATO2 | + CPCAP_BIT_ATO1 | CPCAP_BIT_ATO0 | + CPCAP_BIT_ADA2 | CPCAP_BIT_ADA1 | + CPCAP_BIT_ADA0 | CPCAP_BIT_AD_SEL1 | + CPCAP_BIT_RAND1 | CPCAP_BIT_RAND0, + value); + if (error) + return; + + error = regmap_clear_bits(ddata->reg, CPCAP_REG_ADCC2, + CPCAP_BIT_ATOX_PS_FACTOR | + CPCAP_BIT_ADC_PS_FACTOR1 | + CPCAP_BIT_ADC_PS_FACTOR0); + if (error) + return; + + error = regmap_set_bits(ddata->reg, CPCAP_REG_ADCC2, + CPCAP_BIT_ADTRIG_DIS); + if (error) + return; + + error = regmap_set_bits(ddata->reg, CPCAP_REG_ADCC2, CPCAP_BIT_ASC); + if (error) + return; + + do { + schedule_timeout_uninterruptible(1); + error = regmap_read(ddata->reg, CPCAP_REG_ADCC2, &value); + if (error) + return; + } while ((value & CPCAP_BIT_ASC) && time_before(jiffies, timeout)); + + if (value & CPCAP_BIT_ASC) + dev_err(ddata->dev, + "Timeout waiting for calibration to complete\n"); + + error = regmap_clear_bits(ddata->reg, CPCAP_REG_ADCC1, + CPCAP_BIT_CAL_MODE); + if (error) + return; +} + +static int cpcap_adc_calibrate_one(struct cpcap_adc *ddata, + int channel, + u16 calibration_register, + int lower_threshold, + int upper_threshold) +{ + unsigned int calibration_data[2]; + unsigned short cal_data_diff; + int i, error; + + for (i = 0; i < CPCAP_ADC_MAX_RETRIES; i++) { + calibration_data[0] = 0; + calibration_data[1] = 0; + + cpcap_adc_setup_calibrate(ddata, channel); + error = regmap_read(ddata->reg, calibration_register, + &calibration_data[0]); + if (error) + return error; + cpcap_adc_setup_calibrate(ddata, channel); + error = regmap_read(ddata->reg, calibration_register, + &calibration_data[1]); + if (error) + return error; + + if (calibration_data[0] > calibration_data[1]) + cal_data_diff = + calibration_data[0] - calibration_data[1]; + else + cal_data_diff = + calibration_data[1] - calibration_data[0]; + + if (((calibration_data[1] >= lower_threshold) && + (calibration_data[1] <= upper_threshold) && + (cal_data_diff <= ST_ADC_CALIBRATE_DIFF_THRESHOLD)) || + (ddata->vendor == CPCAP_VENDOR_TI)) { + bank_conversion[channel].cal_offset = + ((short)calibration_data[1] * -1) + 512; + dev_dbg(ddata->dev, "ch%i calibration complete: %i\n", + channel, bank_conversion[channel].cal_offset); + break; + } + usleep_range(5000, 10000); + } + + return 0; +} + +static int cpcap_adc_calibrate(struct cpcap_adc *ddata) +{ + int error; + + error = cpcap_adc_calibrate_one(ddata, CPCAP_ADC_CHG_ISENSE, + CPCAP_REG_ADCAL1, + ST_ADC_CAL_CHRGI_LOW_THRESHOLD, + ST_ADC_CAL_CHRGI_HIGH_THRESHOLD); + if (error) + return error; + + error = cpcap_adc_calibrate_one(ddata, CPCAP_ADC_BATTI, + CPCAP_REG_ADCAL2, + ST_ADC_CAL_BATTI_LOW_THRESHOLD, + ST_ADC_CAL_BATTI_HIGH_THRESHOLD); + if (error) + return error; + + return 0; +} + +/* ADC setup, read and scale functions */ +static void cpcap_adc_setup_bank(struct cpcap_adc *ddata, + struct cpcap_adc_request *req) +{ + const struct cpcap_adc_ato *ato = ddata->ato; + unsigned short value1 = 0; + unsigned short value2 = 0; + int error; + + if (!ato) + return; + + switch (req->channel) { + case CPCAP_ADC_AD0: + value2 |= CPCAP_BIT_THERMBIAS_EN; + error = regmap_update_bits(ddata->reg, CPCAP_REG_ADCC2, + CPCAP_BIT_THERMBIAS_EN, + value2); + if (error) + return; + usleep_range(800, 1000); + break; + case CPCAP_ADC_AD8 ... CPCAP_ADC_TSY2_AD15: + value1 |= CPCAP_BIT_AD_SEL1; + break; + case CPCAP_ADC_BATTP_PI16 ... CPCAP_ADC_BATTI_PI17: + value1 |= CPCAP_BIT_RAND1; + break; + default: + break; + } + + switch (req->timing) { + case CPCAP_ADC_TIMING_IN: + value1 |= ato->ato_in; + value1 |= ato->atox_in; + value2 |= ato->adc_ps_factor_in; + value2 |= ato->atox_ps_factor_in; + break; + case CPCAP_ADC_TIMING_OUT: + value1 |= ato->ato_out; + value1 |= ato->atox_out; + value2 |= ato->adc_ps_factor_out; + value2 |= ato->atox_ps_factor_out; + break; + + case CPCAP_ADC_TIMING_IMM: + default: + break; + } + + error = regmap_update_bits(ddata->reg, CPCAP_REG_ADCC1, + CPCAP_BIT_CAL_MODE | CPCAP_BIT_ATOX | + CPCAP_BIT_ATO3 | CPCAP_BIT_ATO2 | + CPCAP_BIT_ATO1 | CPCAP_BIT_ATO0 | + CPCAP_BIT_ADA2 | CPCAP_BIT_ADA1 | + CPCAP_BIT_ADA0 | CPCAP_BIT_AD_SEL1 | + CPCAP_BIT_RAND1 | CPCAP_BIT_RAND0, + value1); + if (error) + return; + + error = regmap_update_bits(ddata->reg, CPCAP_REG_ADCC2, + CPCAP_BIT_ATOX_PS_FACTOR | + CPCAP_BIT_ADC_PS_FACTOR1 | + CPCAP_BIT_ADC_PS_FACTOR0 | + CPCAP_BIT_THERMBIAS_EN, + value2); + if (error) + return; + + if (req->timing == CPCAP_ADC_TIMING_IMM) { + error = regmap_set_bits(ddata->reg, CPCAP_REG_ADCC2, + CPCAP_BIT_ADTRIG_DIS); + if (error) + return; + + error = regmap_set_bits(ddata->reg, CPCAP_REG_ADCC2, + CPCAP_BIT_ASC); + if (error) + return; + } else { + error = regmap_set_bits(ddata->reg, CPCAP_REG_ADCC2, + CPCAP_BIT_ADTRIG_ONESHOT); + if (error) + return; + + error = regmap_clear_bits(ddata->reg, CPCAP_REG_ADCC2, + CPCAP_BIT_ADTRIG_DIS); + if (error) + return; + } +} + +static int cpcap_adc_start_bank(struct cpcap_adc *ddata, + struct cpcap_adc_request *req) +{ + int i, error; + + req->timing = CPCAP_ADC_TIMING_IMM; + ddata->done = false; + + for (i = 0; i < CPCAP_ADC_MAX_RETRIES; i++) { + cpcap_adc_setup_bank(ddata, req); + error = wait_event_interruptible_timeout(ddata->wq_data_avail, + ddata->done, + msecs_to_jiffies(50)); + if (error > 0) + return 0; + + if (error == 0) { + error = -ETIMEDOUT; + continue; + } + + if (error < 0) + return error; + } + + return error; +} + +static int cpcap_adc_stop_bank(struct cpcap_adc *ddata) +{ + int error; + + error = regmap_update_bits(ddata->reg, CPCAP_REG_ADCC1, + 0xffff, + CPCAP_REG_ADCC1_DEFAULTS); + if (error) + return error; + + return regmap_update_bits(ddata->reg, CPCAP_REG_ADCC2, + 0xffff, + CPCAP_REG_ADCC2_DEFAULTS); +} + +static void cpcap_adc_phase(struct cpcap_adc_request *req) +{ + const struct cpcap_adc_conversion_tbl *conv_tbl = req->conv_tbl; + const struct cpcap_adc_phasing_tbl *phase_tbl = req->phase_tbl; + int index = req->channel; + + /* Remuxed channels 16 and 17 use BATTP and BATTI entries */ + switch (req->channel) { + case CPCAP_ADC_BATTP: + case CPCAP_ADC_BATTP_PI16: + index = req->bank_index; + req->result -= phase_tbl[index].offset; + req->result -= CPCAP_FOUR_POINT_TWO_ADC; + req->result *= phase_tbl[index].multiplier; + if (phase_tbl[index].divider == 0) + return; + req->result /= phase_tbl[index].divider; + req->result += CPCAP_FOUR_POINT_TWO_ADC; + break; + case CPCAP_ADC_BATTI_PI17: + index = req->bank_index; + fallthrough; + default: + req->result += conv_tbl[index].cal_offset; + req->result += conv_tbl[index].align_offset; + req->result *= phase_tbl[index].multiplier; + if (phase_tbl[index].divider == 0) + return; + req->result /= phase_tbl[index].divider; + req->result += phase_tbl[index].offset; + break; + } + + if (req->result < phase_tbl[index].min) + req->result = phase_tbl[index].min; + else if (req->result > phase_tbl[index].max) + req->result = phase_tbl[index].max; +} + +/* Looks up temperatures in a table and calculates averages if needed */ +static int cpcap_adc_table_to_millicelcius(unsigned short value) +{ + int i, result = 0, alpha; + + if (value <= temp_map[CPCAP_MAX_TEMP_LVL - 1][0]) + return temp_map[CPCAP_MAX_TEMP_LVL - 1][1]; + + if (value >= temp_map[0][0]) + return temp_map[0][1]; + + for (i = 0; i < CPCAP_MAX_TEMP_LVL - 1; i++) { + if ((value <= temp_map[i][0]) && + (value >= temp_map[i + 1][0])) { + if (value == temp_map[i][0]) { + result = temp_map[i][1]; + } else if (value == temp_map[i + 1][0]) { + result = temp_map[i + 1][1]; + } else { + alpha = ((value - temp_map[i][0]) * 1000) / + (temp_map[i + 1][0] - temp_map[i][0]); + + result = temp_map[i][1] + + ((alpha * (temp_map[i + 1][1] - + temp_map[i][1])) / 1000); + } + break; + } + } + + return result; +} + +static void cpcap_adc_convert(struct cpcap_adc_request *req) +{ + const struct cpcap_adc_conversion_tbl *conv_tbl = req->conv_tbl; + int index = req->channel; + + /* Remuxed channels 16 and 17 use BATTP and BATTI entries */ + switch (req->channel) { + case CPCAP_ADC_BATTP_PI16: + index = CPCAP_ADC_BATTP; + break; + case CPCAP_ADC_BATTI_PI17: + index = CPCAP_ADC_BATTI; + break; + default: + break; + } + + /* No conversion for raw channels */ + if (conv_tbl[index].conv_type == IIO_CHAN_INFO_RAW) + return; + + /* Temperatures use a lookup table instead of conversion table */ + if ((req->channel == CPCAP_ADC_AD0) || + (req->channel == CPCAP_ADC_AD3)) { + req->result = + cpcap_adc_table_to_millicelcius(req->result); + + return; + } + + /* All processed channels use a conversion table */ + req->result *= conv_tbl[index].multiplier; + if (conv_tbl[index].divider == 0) + return; + req->result /= conv_tbl[index].divider; + req->result += conv_tbl[index].conv_offset; +} + +/* + * REVISIT: Check if timed sampling can use multiple channels at the + * same time. If not, replace channel_mask with just channel. + */ +static int cpcap_adc_read_bank_scaled(struct cpcap_adc *ddata, + struct cpcap_adc_request *req) +{ + int calibration_data, error, addr; + + if (ddata->vendor == CPCAP_VENDOR_TI) { + error = regmap_read(ddata->reg, CPCAP_REG_ADCAL1, + &calibration_data); + if (error) + return error; + bank_conversion[CPCAP_ADC_CHG_ISENSE].cal_offset = + ((short)calibration_data * -1) + 512; + + error = regmap_read(ddata->reg, CPCAP_REG_ADCAL2, + &calibration_data); + if (error) + return error; + bank_conversion[CPCAP_ADC_BATTI].cal_offset = + ((short)calibration_data * -1) + 512; + } + + addr = CPCAP_REG_ADCD0 + req->bank_index * 4; + + error = regmap_read(ddata->reg, addr, &req->result); + if (error) + return error; + + req->result &= 0x3ff; + cpcap_adc_phase(req); + cpcap_adc_convert(req); + + return 0; +} + +static int cpcap_adc_init_request(struct cpcap_adc_request *req, + int channel) +{ + req->channel = channel; + req->phase_tbl = bank_phasing; + req->conv_tbl = bank_conversion; + + switch (channel) { + case CPCAP_ADC_AD0 ... CPCAP_ADC_USB_ID: + req->bank_index = channel; + break; + case CPCAP_ADC_AD8 ... CPCAP_ADC_TSY2_AD15: + req->bank_index = channel - 8; + break; + case CPCAP_ADC_BATTP_PI16: + req->bank_index = CPCAP_ADC_BATTP; + break; + case CPCAP_ADC_BATTI_PI17: + req->bank_index = CPCAP_ADC_BATTI; + break; + default: + return -EINVAL; + } + + return 0; +} + +static int cpcap_adc_read_st_die_temp(struct cpcap_adc *ddata, + int addr, int *val) +{ + int error; + + error = regmap_read(ddata->reg, addr, val); + if (error) + return error; + + *val -= 282; + *val *= 114; + *val += 25000; + + return 0; +} + +static int cpcap_adc_read(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct cpcap_adc *ddata = iio_priv(indio_dev); + struct cpcap_adc_request req; + int error; + + error = cpcap_adc_init_request(&req, chan->channel); + if (error) + return error; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + mutex_lock(&ddata->lock); + error = cpcap_adc_start_bank(ddata, &req); + if (error) + goto err_unlock; + error = regmap_read(ddata->reg, chan->address, val); + if (error) + goto err_unlock; + error = cpcap_adc_stop_bank(ddata); + if (error) + goto err_unlock; + mutex_unlock(&ddata->lock); + break; + case IIO_CHAN_INFO_PROCESSED: + mutex_lock(&ddata->lock); + error = cpcap_adc_start_bank(ddata, &req); + if (error) + goto err_unlock; + if ((ddata->vendor == CPCAP_VENDOR_ST) && + (chan->channel == CPCAP_ADC_AD3)) { + error = cpcap_adc_read_st_die_temp(ddata, + chan->address, + &req.result); + if (error) + goto err_unlock; + } else { + error = cpcap_adc_read_bank_scaled(ddata, &req); + if (error) + goto err_unlock; + } + error = cpcap_adc_stop_bank(ddata); + if (error) + goto err_unlock; + mutex_unlock(&ddata->lock); + *val = req.result; + break; + default: + return -EINVAL; + } + + return IIO_VAL_INT; + +err_unlock: + mutex_unlock(&ddata->lock); + dev_err(ddata->dev, "error reading ADC: %i\n", error); + + return error; +} + +static const struct iio_info cpcap_adc_info = { + .read_raw = &cpcap_adc_read, +}; + +/* + * Configuration for Motorola mapphone series such as droid 4. + * Copied from the Motorola mapphone kernel tree. + */ +static const struct cpcap_adc_ato mapphone_adc = { + .ato_in = 0x0480, + .atox_in = 0, + .adc_ps_factor_in = 0x0200, + .atox_ps_factor_in = 0, + .ato_out = 0, + .atox_out = 0, + .adc_ps_factor_out = 0, + .atox_ps_factor_out = 0, +}; + +static const struct cpcap_adc_ato mot_adc = { + .ato_in = 0x0300, + .atox_in = 0, + .adc_ps_factor_in = 0x0200, + .atox_ps_factor_in = 0, + .ato_out = 0x0780, + .atox_out = 0, + .adc_ps_factor_out = 0x0600, + .atox_ps_factor_out = 0, +}; + +static const struct of_device_id cpcap_adc_id_table[] = { + { + .compatible = "motorola,cpcap-adc", + }, + { + .compatible = "motorola,mapphone-cpcap-adc", + .data = &mapphone_adc, + }, + { + .compatible = "motorola,mot-cpcap-adc", + .data = &mot_adc, + }, + { } +}; +MODULE_DEVICE_TABLE(of, cpcap_adc_id_table); + +static int cpcap_adc_probe(struct platform_device *pdev) +{ + struct cpcap_adc *ddata; + struct iio_dev *indio_dev; + int error; + + indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*ddata)); + if (!indio_dev) + return -ENOMEM; + + ddata = iio_priv(indio_dev); + ddata->ato = device_get_match_data(&pdev->dev); + if (!ddata->ato) + return -ENODEV; + ddata->dev = &pdev->dev; + + mutex_init(&ddata->lock); + init_waitqueue_head(&ddata->wq_data_avail); + + indio_dev->modes = INDIO_DIRECT_MODE | INDIO_BUFFER_SOFTWARE; + indio_dev->channels = cpcap_adc_channels; + indio_dev->num_channels = ARRAY_SIZE(cpcap_adc_channels); + indio_dev->name = dev_name(&pdev->dev); + indio_dev->info = &cpcap_adc_info; + + ddata->reg = dev_get_regmap(pdev->dev.parent, NULL); + if (!ddata->reg) + return -ENODEV; + + error = cpcap_get_vendor(ddata->dev, ddata->reg, &ddata->vendor); + if (error) + return error; + + platform_set_drvdata(pdev, indio_dev); + + ddata->irq = platform_get_irq_byname(pdev, "adcdone"); + if (ddata->irq < 0) + return -ENODEV; + + error = devm_request_threaded_irq(&pdev->dev, ddata->irq, NULL, + cpcap_adc_irq_thread, + IRQF_TRIGGER_NONE | IRQF_ONESHOT, + "cpcap-adc", indio_dev); + if (error) { + dev_err(&pdev->dev, "could not get irq: %i\n", + error); + + return error; + } + + error = cpcap_adc_calibrate(ddata); + if (error) + return error; + + dev_info(&pdev->dev, "CPCAP ADC device probed\n"); + + return devm_iio_device_register(&pdev->dev, indio_dev); +} + +static struct platform_driver cpcap_adc_driver = { + .driver = { + .name = "cpcap_adc", + .of_match_table = cpcap_adc_id_table, + }, + .probe = cpcap_adc_probe, +}; + +module_platform_driver(cpcap_adc_driver); + +MODULE_ALIAS("platform:cpcap_adc"); +MODULE_DESCRIPTION("CPCAP ADC driver"); +MODULE_AUTHOR("Tony Lindgren + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Channels */ +enum da9150_gpadc_hw_channel { + DA9150_GPADC_HW_CHAN_GPIOA_2V = 0, + DA9150_GPADC_HW_CHAN_GPIOA_2V_, + DA9150_GPADC_HW_CHAN_GPIOB_2V, + DA9150_GPADC_HW_CHAN_GPIOB_2V_, + DA9150_GPADC_HW_CHAN_GPIOC_2V, + DA9150_GPADC_HW_CHAN_GPIOC_2V_, + DA9150_GPADC_HW_CHAN_GPIOD_2V, + DA9150_GPADC_HW_CHAN_GPIOD_2V_, + DA9150_GPADC_HW_CHAN_IBUS_SENSE, + DA9150_GPADC_HW_CHAN_IBUS_SENSE_, + DA9150_GPADC_HW_CHAN_VBUS_DIV, + DA9150_GPADC_HW_CHAN_VBUS_DIV_, + DA9150_GPADC_HW_CHAN_ID, + DA9150_GPADC_HW_CHAN_ID_, + DA9150_GPADC_HW_CHAN_VSYS, + DA9150_GPADC_HW_CHAN_VSYS_, + DA9150_GPADC_HW_CHAN_GPIOA_6V, + DA9150_GPADC_HW_CHAN_GPIOA_6V_, + DA9150_GPADC_HW_CHAN_GPIOB_6V, + DA9150_GPADC_HW_CHAN_GPIOB_6V_, + DA9150_GPADC_HW_CHAN_GPIOC_6V, + DA9150_GPADC_HW_CHAN_GPIOC_6V_, + DA9150_GPADC_HW_CHAN_GPIOD_6V, + DA9150_GPADC_HW_CHAN_GPIOD_6V_, + DA9150_GPADC_HW_CHAN_VBAT, + DA9150_GPADC_HW_CHAN_VBAT_, + DA9150_GPADC_HW_CHAN_TBAT, + DA9150_GPADC_HW_CHAN_TBAT_, + DA9150_GPADC_HW_CHAN_TJUNC_CORE, + DA9150_GPADC_HW_CHAN_TJUNC_CORE_, + DA9150_GPADC_HW_CHAN_TJUNC_OVP, + DA9150_GPADC_HW_CHAN_TJUNC_OVP_, +}; + +enum da9150_gpadc_channel { + DA9150_GPADC_CHAN_GPIOA = 0, + DA9150_GPADC_CHAN_GPIOB, + DA9150_GPADC_CHAN_GPIOC, + DA9150_GPADC_CHAN_GPIOD, + DA9150_GPADC_CHAN_IBUS, + DA9150_GPADC_CHAN_VBUS, + DA9150_GPADC_CHAN_VSYS, + DA9150_GPADC_CHAN_VBAT, + DA9150_GPADC_CHAN_TBAT, + DA9150_GPADC_CHAN_TJUNC_CORE, + DA9150_GPADC_CHAN_TJUNC_OVP, +}; + +/* Private data */ +struct da9150_gpadc { + struct da9150 *da9150; + struct device *dev; + + struct mutex lock; + struct completion complete; +}; + + +static irqreturn_t da9150_gpadc_irq(int irq, void *data) +{ + + struct da9150_gpadc *gpadc = data; + + complete(&gpadc->complete); + + return IRQ_HANDLED; +} + +static int da9150_gpadc_read_adc(struct da9150_gpadc *gpadc, int hw_chan) +{ + u8 result_regs[2]; + int result; + + mutex_lock(&gpadc->lock); + + /* Set channel & enable measurement */ + da9150_reg_write(gpadc->da9150, DA9150_GPADC_MAN, + (DA9150_GPADC_EN_MASK | + hw_chan << DA9150_GPADC_MUX_SHIFT)); + + /* Consume left-over completion from a previous timeout */ + try_wait_for_completion(&gpadc->complete); + + /* Check for actual completion */ + wait_for_completion_timeout(&gpadc->complete, msecs_to_jiffies(5)); + + /* Read result and status from device */ + da9150_bulk_read(gpadc->da9150, DA9150_GPADC_RES_A, 2, result_regs); + + mutex_unlock(&gpadc->lock); + + /* Check to make sure device really has completed reading */ + if (result_regs[1] & DA9150_GPADC_RUN_MASK) { + dev_err(gpadc->dev, "Timeout on channel %d of GPADC\n", + hw_chan); + return -ETIMEDOUT; + } + + /* LSBs - 2 bits */ + result = (result_regs[1] & DA9150_GPADC_RES_L_MASK) >> + DA9150_GPADC_RES_L_SHIFT; + /* MSBs - 8 bits */ + result |= result_regs[0] << DA9150_GPADC_RES_L_BITS; + + return result; +} + +static inline int da9150_gpadc_gpio_6v_voltage_now(int raw_val) +{ + /* Convert to mV */ + return (6 * ((raw_val * 1000) + 500)) / 1024; +} + +static inline int da9150_gpadc_ibus_current_avg(int raw_val) +{ + /* Convert to mA */ + return (4 * ((raw_val * 1000) + 500)) / 2048; +} + +static inline int da9150_gpadc_vbus_21v_voltage_now(int raw_val) +{ + /* Convert to mV */ + return (21 * ((raw_val * 1000) + 500)) / 1024; +} + +static inline int da9150_gpadc_vsys_6v_voltage_now(int raw_val) +{ + /* Convert to mV */ + return (3 * ((raw_val * 1000) + 500)) / 512; +} + +static int da9150_gpadc_read_processed(struct da9150_gpadc *gpadc, int channel, + int hw_chan, int *val) +{ + int raw_val; + + raw_val = da9150_gpadc_read_adc(gpadc, hw_chan); + if (raw_val < 0) + return raw_val; + + switch (channel) { + case DA9150_GPADC_CHAN_GPIOA: + case DA9150_GPADC_CHAN_GPIOB: + case DA9150_GPADC_CHAN_GPIOC: + case DA9150_GPADC_CHAN_GPIOD: + *val = da9150_gpadc_gpio_6v_voltage_now(raw_val); + break; + case DA9150_GPADC_CHAN_IBUS: + *val = da9150_gpadc_ibus_current_avg(raw_val); + break; + case DA9150_GPADC_CHAN_VBUS: + *val = da9150_gpadc_vbus_21v_voltage_now(raw_val); + break; + case DA9150_GPADC_CHAN_VSYS: + *val = da9150_gpadc_vsys_6v_voltage_now(raw_val); + break; + default: + /* No processing for other channels so return raw value */ + *val = raw_val; + break; + } + + return IIO_VAL_INT; +} + +static int da9150_gpadc_read_scale(int channel, int *val, int *val2) +{ + switch (channel) { + case DA9150_GPADC_CHAN_VBAT: + *val = 2932; + *val2 = 1000; + return IIO_VAL_FRACTIONAL; + case DA9150_GPADC_CHAN_TJUNC_CORE: + case DA9150_GPADC_CHAN_TJUNC_OVP: + *val = 1000000; + *val2 = 4420; + return IIO_VAL_FRACTIONAL; + default: + return -EINVAL; + } +} + +static int da9150_gpadc_read_offset(int channel, int *val) +{ + switch (channel) { + case DA9150_GPADC_CHAN_VBAT: + *val = 1500000 / 2932; + return IIO_VAL_INT; + case DA9150_GPADC_CHAN_TJUNC_CORE: + case DA9150_GPADC_CHAN_TJUNC_OVP: + *val = -144; + return IIO_VAL_INT; + default: + return -EINVAL; + } +} + +static int da9150_gpadc_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct da9150_gpadc *gpadc = iio_priv(indio_dev); + + if ((chan->channel < DA9150_GPADC_CHAN_GPIOA) || + (chan->channel > DA9150_GPADC_CHAN_TJUNC_OVP)) + return -EINVAL; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + case IIO_CHAN_INFO_PROCESSED: + return da9150_gpadc_read_processed(gpadc, chan->channel, + chan->address, val); + case IIO_CHAN_INFO_SCALE: + return da9150_gpadc_read_scale(chan->channel, val, val2); + case IIO_CHAN_INFO_OFFSET: + return da9150_gpadc_read_offset(chan->channel, val); + default: + return -EINVAL; + } +} + +static const struct iio_info da9150_gpadc_info = { + .read_raw = &da9150_gpadc_read_raw, +}; + +#define DA9150_GPADC_CHANNEL(_id, _hw_id, _type, chan_info, \ + _ext_name) { \ + .type = _type, \ + .indexed = 1, \ + .channel = DA9150_GPADC_CHAN_##_id, \ + .address = DA9150_GPADC_HW_CHAN_##_hw_id, \ + .info_mask_separate = chan_info, \ + .extend_name = _ext_name, \ + .datasheet_name = #_id, \ +} + +#define DA9150_GPADC_CHANNEL_RAW(_id, _hw_id, _type, _ext_name) \ + DA9150_GPADC_CHANNEL(_id, _hw_id, _type, \ + BIT(IIO_CHAN_INFO_RAW), _ext_name) + +#define DA9150_GPADC_CHANNEL_SCALED(_id, _hw_id, _type, _ext_name) \ + DA9150_GPADC_CHANNEL(_id, _hw_id, _type, \ + BIT(IIO_CHAN_INFO_RAW) | \ + BIT(IIO_CHAN_INFO_SCALE) | \ + BIT(IIO_CHAN_INFO_OFFSET), \ + _ext_name) + +#define DA9150_GPADC_CHANNEL_PROCESSED(_id, _hw_id, _type, _ext_name) \ + DA9150_GPADC_CHANNEL(_id, _hw_id, _type, \ + BIT(IIO_CHAN_INFO_PROCESSED), _ext_name) + +/* Supported channels */ +static const struct iio_chan_spec da9150_gpadc_channels[] = { + DA9150_GPADC_CHANNEL_PROCESSED(GPIOA, GPIOA_6V, IIO_VOLTAGE, NULL), + DA9150_GPADC_CHANNEL_PROCESSED(GPIOB, GPIOB_6V, IIO_VOLTAGE, NULL), + DA9150_GPADC_CHANNEL_PROCESSED(GPIOC, GPIOC_6V, IIO_VOLTAGE, NULL), + DA9150_GPADC_CHANNEL_PROCESSED(GPIOD, GPIOD_6V, IIO_VOLTAGE, NULL), + DA9150_GPADC_CHANNEL_PROCESSED(IBUS, IBUS_SENSE, IIO_CURRENT, "ibus"), + DA9150_GPADC_CHANNEL_PROCESSED(VBUS, VBUS_DIV_, IIO_VOLTAGE, "vbus"), + DA9150_GPADC_CHANNEL_PROCESSED(VSYS, VSYS, IIO_VOLTAGE, "vsys"), + DA9150_GPADC_CHANNEL_SCALED(VBAT, VBAT, IIO_VOLTAGE, "vbat"), + DA9150_GPADC_CHANNEL_RAW(TBAT, TBAT, IIO_VOLTAGE, "tbat"), + DA9150_GPADC_CHANNEL_SCALED(TJUNC_CORE, TJUNC_CORE, IIO_TEMP, + "tjunc_core"), + DA9150_GPADC_CHANNEL_SCALED(TJUNC_OVP, TJUNC_OVP, IIO_TEMP, + "tjunc_ovp"), +}; + +/* Default maps used by da9150-charger */ +static const struct iio_map da9150_gpadc_default_maps[] = { + IIO_MAP("IBUS", "da9150-charger", "CHAN_IBUS"), + IIO_MAP("VBUS", "da9150-charger", "CHAN_VBUS"), + IIO_MAP("TJUNC_CORE", "da9150-charger", "CHAN_TJUNC"), + IIO_MAP("VBAT", "da9150-charger", "CHAN_VBAT"), + { } +}; + +static int da9150_gpadc_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct da9150 *da9150 = dev_get_drvdata(dev->parent); + struct da9150_gpadc *gpadc; + struct iio_dev *indio_dev; + int irq, ret; + + indio_dev = devm_iio_device_alloc(dev, sizeof(*gpadc)); + if (!indio_dev) + return -ENOMEM; + + gpadc = iio_priv(indio_dev); + + gpadc->da9150 = da9150; + gpadc->dev = dev; + mutex_init(&gpadc->lock); + init_completion(&gpadc->complete); + + irq = platform_get_irq_byname(pdev, "GPADC"); + if (irq < 0) + return irq; + + ret = devm_request_threaded_irq(dev, irq, NULL, da9150_gpadc_irq, + IRQF_ONESHOT, "GPADC", gpadc); + if (ret) { + dev_err(dev, "Failed to request IRQ %d: %d\n", irq, ret); + return ret; + } + + ret = devm_iio_map_array_register(&pdev->dev, indio_dev, da9150_gpadc_default_maps); + if (ret) { + dev_err(dev, "Failed to register IIO maps: %d\n", ret); + return ret; + } + + indio_dev->name = dev_name(dev); + indio_dev->info = &da9150_gpadc_info; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = da9150_gpadc_channels; + indio_dev->num_channels = ARRAY_SIZE(da9150_gpadc_channels); + + return devm_iio_device_register(&pdev->dev, indio_dev); +} + +static struct platform_driver da9150_gpadc_driver = { + .driver = { + .name = "da9150-gpadc", + }, + .probe = da9150_gpadc_probe, +}; + +module_platform_driver(da9150_gpadc_driver); + +MODULE_DESCRIPTION("GPADC Driver for DA9150"); +MODULE_AUTHOR("Adam Thomson "); +MODULE_LICENSE("GPL"); diff --git a/drivers/iio/adc/dln2-adc.c b/drivers/iio/adc/dln2-adc.c new file mode 100644 index 000000000..b01c6f5a7 --- /dev/null +++ b/drivers/iio/adc/dln2-adc.c @@ -0,0 +1,678 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Driver for the Diolan DLN-2 USB-ADC adapter + * + * Copyright (c) 2017 Jack Andersen + */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#define DLN2_ADC_MOD_NAME "dln2-adc" + +#define DLN2_ADC_ID 0x06 + +#define DLN2_ADC_GET_CHANNEL_COUNT DLN2_CMD(0x01, DLN2_ADC_ID) +#define DLN2_ADC_ENABLE DLN2_CMD(0x02, DLN2_ADC_ID) +#define DLN2_ADC_DISABLE DLN2_CMD(0x03, DLN2_ADC_ID) +#define DLN2_ADC_CHANNEL_ENABLE DLN2_CMD(0x05, DLN2_ADC_ID) +#define DLN2_ADC_CHANNEL_DISABLE DLN2_CMD(0x06, DLN2_ADC_ID) +#define DLN2_ADC_SET_RESOLUTION DLN2_CMD(0x08, DLN2_ADC_ID) +#define DLN2_ADC_CHANNEL_GET_VAL DLN2_CMD(0x0A, DLN2_ADC_ID) +#define DLN2_ADC_CHANNEL_GET_ALL_VAL DLN2_CMD(0x0B, DLN2_ADC_ID) +#define DLN2_ADC_CHANNEL_SET_CFG DLN2_CMD(0x0C, DLN2_ADC_ID) +#define DLN2_ADC_CHANNEL_GET_CFG DLN2_CMD(0x0D, DLN2_ADC_ID) +#define DLN2_ADC_CONDITION_MET_EV DLN2_CMD(0x10, DLN2_ADC_ID) + +#define DLN2_ADC_EVENT_NONE 0 +#define DLN2_ADC_EVENT_BELOW 1 +#define DLN2_ADC_EVENT_LEVEL_ABOVE 2 +#define DLN2_ADC_EVENT_OUTSIDE 3 +#define DLN2_ADC_EVENT_INSIDE 4 +#define DLN2_ADC_EVENT_ALWAYS 5 + +#define DLN2_ADC_MAX_CHANNELS 8 +#define DLN2_ADC_DATA_BITS 10 + +/* + * Plays similar role to iio_demux_table in subsystem core; except allocated + * in a fixed 8-element array. + */ +struct dln2_adc_demux_table { + unsigned int from; + unsigned int to; + unsigned int length; +}; + +struct dln2_adc { + struct platform_device *pdev; + struct iio_chan_spec iio_channels[DLN2_ADC_MAX_CHANNELS + 1]; + int port, trigger_chan; + struct iio_trigger *trig; + struct mutex mutex; + /* Cached sample period in milliseconds */ + unsigned int sample_period; + /* Demux table */ + unsigned int demux_count; + struct dln2_adc_demux_table demux[DLN2_ADC_MAX_CHANNELS]; +}; + +struct dln2_adc_port_chan { + u8 port; + u8 chan; +}; + +struct dln2_adc_get_all_vals { + __le16 channel_mask; + __le16 values[DLN2_ADC_MAX_CHANNELS]; +}; + +static void dln2_adc_add_demux(struct dln2_adc *dln2, + unsigned int in_loc, unsigned int out_loc, + unsigned int length) +{ + struct dln2_adc_demux_table *p = dln2->demux_count ? + &dln2->demux[dln2->demux_count - 1] : NULL; + + if (p && p->from + p->length == in_loc && + p->to + p->length == out_loc) { + p->length += length; + } else if (dln2->demux_count < DLN2_ADC_MAX_CHANNELS) { + p = &dln2->demux[dln2->demux_count++]; + p->from = in_loc; + p->to = out_loc; + p->length = length; + } +} + +static void dln2_adc_update_demux(struct dln2_adc *dln2) +{ + int in_ind = -1, out_ind; + unsigned int in_loc = 0, out_loc = 0; + struct iio_dev *indio_dev = platform_get_drvdata(dln2->pdev); + + /* Clear out any old demux */ + dln2->demux_count = 0; + + /* Optimize all 8-channels case */ + if (iio_get_masklength(indio_dev) && + (*indio_dev->active_scan_mask & 0xff) == 0xff) { + dln2_adc_add_demux(dln2, 0, 0, 16); + return; + } + + /* Build demux table from fixed 8-channels to active_scan_mask */ + iio_for_each_active_channel(indio_dev, out_ind) { + /* Handle timestamp separately */ + if (out_ind == DLN2_ADC_MAX_CHANNELS) + break; + for (++in_ind; in_ind != out_ind; ++in_ind) + in_loc += 2; + dln2_adc_add_demux(dln2, in_loc, out_loc, 2); + out_loc += 2; + in_loc += 2; + } +} + +static int dln2_adc_get_chan_count(struct dln2_adc *dln2) +{ + int ret; + u8 port = dln2->port; + u8 count; + int olen = sizeof(count); + + ret = dln2_transfer(dln2->pdev, DLN2_ADC_GET_CHANNEL_COUNT, + &port, sizeof(port), &count, &olen); + if (ret < 0) { + dev_dbg(&dln2->pdev->dev, "Problem in %s\n", __func__); + return ret; + } + if (olen < sizeof(count)) + return -EPROTO; + + return count; +} + +static int dln2_adc_set_port_resolution(struct dln2_adc *dln2) +{ + int ret; + struct dln2_adc_port_chan port_chan = { + .port = dln2->port, + .chan = DLN2_ADC_DATA_BITS, + }; + + ret = dln2_transfer_tx(dln2->pdev, DLN2_ADC_SET_RESOLUTION, + &port_chan, sizeof(port_chan)); + if (ret < 0) + dev_dbg(&dln2->pdev->dev, "Problem in %s\n", __func__); + + return ret; +} + +static int dln2_adc_set_chan_enabled(struct dln2_adc *dln2, + int channel, bool enable) +{ + int ret; + struct dln2_adc_port_chan port_chan = { + .port = dln2->port, + .chan = channel, + }; + u16 cmd = enable ? DLN2_ADC_CHANNEL_ENABLE : DLN2_ADC_CHANNEL_DISABLE; + + ret = dln2_transfer_tx(dln2->pdev, cmd, &port_chan, sizeof(port_chan)); + if (ret < 0) + dev_dbg(&dln2->pdev->dev, "Problem in %s\n", __func__); + + return ret; +} + +static int dln2_adc_set_port_enabled(struct dln2_adc *dln2, bool enable, + u16 *conflict_out) +{ + int ret; + u8 port = dln2->port; + __le16 conflict; + int olen = sizeof(conflict); + u16 cmd = enable ? DLN2_ADC_ENABLE : DLN2_ADC_DISABLE; + + if (conflict_out) + *conflict_out = 0; + + ret = dln2_transfer(dln2->pdev, cmd, &port, sizeof(port), + &conflict, &olen); + if (ret < 0) { + dev_dbg(&dln2->pdev->dev, "Problem in %s(%d)\n", + __func__, (int)enable); + if (conflict_out && enable && olen >= sizeof(conflict)) + *conflict_out = le16_to_cpu(conflict); + return ret; + } + if (enable && olen < sizeof(conflict)) + return -EPROTO; + + return ret; +} + +static int dln2_adc_set_chan_period(struct dln2_adc *dln2, + unsigned int channel, unsigned int period) +{ + int ret; + struct { + struct dln2_adc_port_chan port_chan; + __u8 type; + __le16 period; + __le16 low; + __le16 high; + } __packed set_cfg = { + .port_chan.port = dln2->port, + .port_chan.chan = channel, + .type = period ? DLN2_ADC_EVENT_ALWAYS : DLN2_ADC_EVENT_NONE, + .period = cpu_to_le16(period) + }; + + ret = dln2_transfer_tx(dln2->pdev, DLN2_ADC_CHANNEL_SET_CFG, + &set_cfg, sizeof(set_cfg)); + if (ret < 0) + dev_dbg(&dln2->pdev->dev, "Problem in %s\n", __func__); + + return ret; +} + +static int dln2_adc_read(struct dln2_adc *dln2, unsigned int channel) +{ + int ret, i; + u16 conflict; + __le16 value; + int olen = sizeof(value); + struct dln2_adc_port_chan port_chan = { + .port = dln2->port, + .chan = channel, + }; + + ret = dln2_adc_set_chan_enabled(dln2, channel, true); + if (ret < 0) + return ret; + + ret = dln2_adc_set_port_enabled(dln2, true, &conflict); + if (ret < 0) { + if (conflict) { + dev_err(&dln2->pdev->dev, + "ADC pins conflict with mask %04X\n", + (int)conflict); + ret = -EBUSY; + } + goto disable_chan; + } + + /* + * Call GET_VAL twice due to initial zero-return immediately after + * enabling channel. + */ + for (i = 0; i < 2; ++i) { + ret = dln2_transfer(dln2->pdev, DLN2_ADC_CHANNEL_GET_VAL, + &port_chan, sizeof(port_chan), + &value, &olen); + if (ret < 0) { + dev_dbg(&dln2->pdev->dev, "Problem in %s\n", __func__); + goto disable_port; + } + if (olen < sizeof(value)) { + ret = -EPROTO; + goto disable_port; + } + } + + ret = le16_to_cpu(value); + +disable_port: + dln2_adc_set_port_enabled(dln2, false, NULL); +disable_chan: + dln2_adc_set_chan_enabled(dln2, channel, false); + + return ret; +} + +static int dln2_adc_read_all(struct dln2_adc *dln2, + struct dln2_adc_get_all_vals *get_all_vals) +{ + int ret; + __u8 port = dln2->port; + int olen = sizeof(*get_all_vals); + + ret = dln2_transfer(dln2->pdev, DLN2_ADC_CHANNEL_GET_ALL_VAL, + &port, sizeof(port), get_all_vals, &olen); + if (ret < 0) { + dev_dbg(&dln2->pdev->dev, "Problem in %s\n", __func__); + return ret; + } + if (olen < sizeof(*get_all_vals)) + return -EPROTO; + + return ret; +} + +static int dln2_adc_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, + int *val2, + long mask) +{ + int ret; + unsigned int microhertz; + struct dln2_adc *dln2 = iio_priv(indio_dev); + + switch (mask) { + case IIO_CHAN_INFO_RAW: + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; + + mutex_lock(&dln2->mutex); + ret = dln2_adc_read(dln2, chan->channel); + mutex_unlock(&dln2->mutex); + + iio_device_release_direct(indio_dev); + + if (ret < 0) + return ret; + + *val = ret; + return IIO_VAL_INT; + + case IIO_CHAN_INFO_SCALE: + /* + * Voltage reference is fixed at 3.3v + * 3.3 / (1 << 10) * 1000000000 + */ + *val = 0; + *val2 = 3222656; + return IIO_VAL_INT_PLUS_NANO; + + case IIO_CHAN_INFO_SAMP_FREQ: + if (dln2->sample_period) { + microhertz = 1000000000 / dln2->sample_period; + *val = microhertz / 1000000; + *val2 = microhertz % 1000000; + } else { + *val = 0; + *val2 = 0; + } + + return IIO_VAL_INT_PLUS_MICRO; + + default: + return -EINVAL; + } +} + +static int dln2_adc_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, + int val2, + long mask) +{ + int ret; + unsigned int microhertz; + struct dln2_adc *dln2 = iio_priv(indio_dev); + + switch (mask) { + case IIO_CHAN_INFO_SAMP_FREQ: + microhertz = 1000000 * val + val2; + + mutex_lock(&dln2->mutex); + + dln2->sample_period = + microhertz ? 1000000000 / microhertz : UINT_MAX; + if (dln2->sample_period > 65535) { + dln2->sample_period = 65535; + dev_warn(&dln2->pdev->dev, + "clamping period to 65535ms\n"); + } + + /* + * The first requested channel is arbitrated as a shared + * trigger source, so only one event is registered with the + * DLN. The event handler will then read all enabled channel + * values using DLN2_ADC_CHANNEL_GET_ALL_VAL to maintain + * synchronization between ADC readings. + */ + if (dln2->trigger_chan != -1) + ret = dln2_adc_set_chan_period(dln2, + dln2->trigger_chan, dln2->sample_period); + else + ret = 0; + + mutex_unlock(&dln2->mutex); + + return ret; + + default: + return -EINVAL; + } +} + +static int dln2_update_scan_mode(struct iio_dev *indio_dev, + const unsigned long *scan_mask) +{ + struct dln2_adc *dln2 = iio_priv(indio_dev); + int chan_count = indio_dev->num_channels - 1; + int ret, i, j; + + mutex_lock(&dln2->mutex); + + for (i = 0; i < chan_count; ++i) { + ret = dln2_adc_set_chan_enabled(dln2, i, + test_bit(i, scan_mask)); + if (ret < 0) { + for (j = 0; j < i; ++j) + dln2_adc_set_chan_enabled(dln2, j, false); + mutex_unlock(&dln2->mutex); + dev_err(&dln2->pdev->dev, + "Unable to enable ADC channel %d\n", i); + return -EBUSY; + } + } + + dln2_adc_update_demux(dln2); + + mutex_unlock(&dln2->mutex); + + return 0; +} + +#define DLN2_ADC_CHAN(lval, idx) { \ + lval.type = IIO_VOLTAGE; \ + lval.channel = idx; \ + lval.indexed = 1; \ + lval.info_mask_separate = BIT(IIO_CHAN_INFO_RAW); \ + lval.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SCALE) | \ + BIT(IIO_CHAN_INFO_SAMP_FREQ); \ + lval.scan_index = idx; \ + lval.scan_type.sign = 'u'; \ + lval.scan_type.realbits = DLN2_ADC_DATA_BITS; \ + lval.scan_type.storagebits = 16; \ + lval.scan_type.endianness = IIO_LE; \ +} + +static const struct iio_info dln2_adc_info = { + .read_raw = dln2_adc_read_raw, + .write_raw = dln2_adc_write_raw, + .update_scan_mode = dln2_update_scan_mode, +}; + +static irqreturn_t dln2_adc_trigger_h(int irq, void *p) +{ + struct iio_poll_func *pf = p; + struct iio_dev *indio_dev = pf->indio_dev; + struct { + __le16 values[DLN2_ADC_MAX_CHANNELS]; + aligned_s64 timestamp_space; + } data = { }; + struct dln2_adc_get_all_vals dev_data; + struct dln2_adc *dln2 = iio_priv(indio_dev); + const struct dln2_adc_demux_table *t; + int ret, i; + + mutex_lock(&dln2->mutex); + ret = dln2_adc_read_all(dln2, &dev_data); + mutex_unlock(&dln2->mutex); + if (ret < 0) + goto done; + + /* Demux operation */ + for (i = 0; i < dln2->demux_count; ++i) { + t = &dln2->demux[i]; + memcpy((void *)data.values + t->to, + (void *)dev_data.values + t->from, t->length); + } + + iio_push_to_buffers_with_ts(indio_dev, &data, sizeof(data), + iio_get_time_ns(indio_dev)); + +done: + iio_trigger_notify_done(indio_dev->trig); + return IRQ_HANDLED; +} + +static int dln2_adc_triggered_buffer_postenable(struct iio_dev *indio_dev) +{ + int ret; + struct dln2_adc *dln2 = iio_priv(indio_dev); + u16 conflict; + unsigned int trigger_chan; + + mutex_lock(&dln2->mutex); + + /* Enable ADC */ + ret = dln2_adc_set_port_enabled(dln2, true, &conflict); + if (ret < 0) { + mutex_unlock(&dln2->mutex); + dev_dbg(&dln2->pdev->dev, "Problem in %s\n", __func__); + if (conflict) { + dev_err(&dln2->pdev->dev, + "ADC pins conflict with mask %04X\n", + (int)conflict); + ret = -EBUSY; + } + return ret; + } + + /* Assign trigger channel based on first enabled channel */ + trigger_chan = find_first_bit(indio_dev->active_scan_mask, + iio_get_masklength(indio_dev)); + if (trigger_chan < DLN2_ADC_MAX_CHANNELS) { + dln2->trigger_chan = trigger_chan; + ret = dln2_adc_set_chan_period(dln2, dln2->trigger_chan, + dln2->sample_period); + mutex_unlock(&dln2->mutex); + if (ret < 0) { + dev_dbg(&dln2->pdev->dev, "Problem in %s\n", __func__); + return ret; + } + } else { + dln2->trigger_chan = -1; + mutex_unlock(&dln2->mutex); + } + + return 0; +} + +static int dln2_adc_triggered_buffer_predisable(struct iio_dev *indio_dev) +{ + int ret; + struct dln2_adc *dln2 = iio_priv(indio_dev); + + mutex_lock(&dln2->mutex); + + /* Disable trigger channel */ + if (dln2->trigger_chan != -1) { + dln2_adc_set_chan_period(dln2, dln2->trigger_chan, 0); + dln2->trigger_chan = -1; + } + + /* Disable ADC */ + ret = dln2_adc_set_port_enabled(dln2, false, NULL); + + mutex_unlock(&dln2->mutex); + if (ret < 0) + dev_dbg(&dln2->pdev->dev, "Problem in %s\n", __func__); + + return ret; +} + +static const struct iio_buffer_setup_ops dln2_adc_buffer_setup_ops = { + .postenable = dln2_adc_triggered_buffer_postenable, + .predisable = dln2_adc_triggered_buffer_predisable, +}; + +static void dln2_adc_event(struct platform_device *pdev, u16 echo, + const void *data, int len) +{ + struct iio_dev *indio_dev = platform_get_drvdata(pdev); + struct dln2_adc *dln2 = iio_priv(indio_dev); + + /* Called via URB completion handler */ + iio_trigger_poll(dln2->trig); +} + +static int dln2_adc_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct dln2_adc *dln2; + struct dln2_platform_data *pdata = dev_get_platdata(&pdev->dev); + struct iio_dev *indio_dev; + int i, ret, chans; + + indio_dev = devm_iio_device_alloc(dev, sizeof(*dln2)); + if (!indio_dev) + return -ENOMEM; + + dln2 = iio_priv(indio_dev); + dln2->pdev = pdev; + dln2->port = pdata->port; + dln2->trigger_chan = -1; + mutex_init(&dln2->mutex); + + platform_set_drvdata(pdev, indio_dev); + + ret = dln2_adc_set_port_resolution(dln2); + if (ret < 0) { + dev_err(dev, "failed to set ADC resolution to 10 bits\n"); + return ret; + } + + chans = dln2_adc_get_chan_count(dln2); + if (chans < 0) { + dev_err(dev, "failed to get channel count: %d\n", chans); + return chans; + } + if (chans > DLN2_ADC_MAX_CHANNELS) { + chans = DLN2_ADC_MAX_CHANNELS; + dev_warn(dev, "clamping channels to %d\n", + DLN2_ADC_MAX_CHANNELS); + } + + for (i = 0; i < chans; ++i) + DLN2_ADC_CHAN(dln2->iio_channels[i], i) + dln2->iio_channels[i] = IIO_CHAN_SOFT_TIMESTAMP(i); + + indio_dev->name = DLN2_ADC_MOD_NAME; + indio_dev->info = &dln2_adc_info; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = dln2->iio_channels; + indio_dev->num_channels = chans + 1; + indio_dev->setup_ops = &dln2_adc_buffer_setup_ops; + + dln2->trig = devm_iio_trigger_alloc(dev, "%s-dev%d", + indio_dev->name, + iio_device_id(indio_dev)); + if (!dln2->trig) + return -ENOMEM; + + iio_trigger_set_drvdata(dln2->trig, dln2); + ret = devm_iio_trigger_register(dev, dln2->trig); + if (ret) { + dev_err(dev, "failed to register trigger: %d\n", ret); + return ret; + } + iio_trigger_set_immutable(indio_dev, dln2->trig); + + ret = devm_iio_triggered_buffer_setup(dev, indio_dev, NULL, + dln2_adc_trigger_h, + &dln2_adc_buffer_setup_ops); + if (ret) { + dev_err(dev, "failed to allocate triggered buffer: %d\n", ret); + return ret; + } + + ret = dln2_register_event_cb(pdev, DLN2_ADC_CONDITION_MET_EV, + dln2_adc_event); + if (ret) { + dev_err(dev, "failed to setup DLN2 periodic event: %d\n", ret); + return ret; + } + + ret = iio_device_register(indio_dev); + if (ret) { + dev_err(dev, "failed to register iio device: %d\n", ret); + goto unregister_event; + } + + return ret; + +unregister_event: + dln2_unregister_event_cb(pdev, DLN2_ADC_CONDITION_MET_EV); + + return ret; +} + +static void dln2_adc_remove(struct platform_device *pdev) +{ + struct iio_dev *indio_dev = platform_get_drvdata(pdev); + + iio_device_unregister(indio_dev); + dln2_unregister_event_cb(pdev, DLN2_ADC_CONDITION_MET_EV); +} + +static struct platform_driver dln2_adc_driver = { + .driver.name = DLN2_ADC_MOD_NAME, + .probe = dln2_adc_probe, + .remove = dln2_adc_remove, +}; + +module_platform_driver(dln2_adc_driver); + +MODULE_AUTHOR("Jack Andersen + */ + +/* + * The DAC is used to find the peak level of an alternating voltage input + * signal by a binary search using the output of a comparator wired to + * an interrupt pin. Like so: + * _ + * | \ + * input +------>-------|+ \ + * | \ + * .-------. | }---. + * | | | / | + * | dac|-->--|- / | + * | | |_/ | + * | | | + * | | | + * | irq|------<-------' + * | | + * '-------' + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct envelope { + spinlock_t comp_lock; /* protects comp */ + int comp; + + struct mutex read_lock; /* protects everything else */ + + int comp_irq; + u32 comp_irq_trigger; + u32 comp_irq_trigger_inv; + + struct iio_channel *dac; + struct delayed_work comp_timeout; + + unsigned int comp_interval; + bool invert; + u32 dac_max; + + int high; + int level; + int low; + + struct completion done; +}; + +/* + * The envelope_detector_comp_latch function works together with the compare + * interrupt service routine below (envelope_detector_comp_isr) as a latch + * (one-bit memory) for if the interrupt has triggered since last calling + * this function. + * The ..._comp_isr function disables the interrupt so that the cpu does not + * need to service a possible interrupt flood from the comparator when no-one + * cares anyway, and this ..._comp_latch function reenables them again if + * needed. + */ +static int envelope_detector_comp_latch(struct envelope *env) +{ + int comp; + + spin_lock_irq(&env->comp_lock); + comp = env->comp; + env->comp = 0; + spin_unlock_irq(&env->comp_lock); + + if (!comp) + return 0; + + /* + * The irq was disabled, and is reenabled just now. + * But there might have been a pending irq that + * happened while the irq was disabled that fires + * just as the irq is reenabled. That is not what + * is desired. + */ + enable_irq(env->comp_irq); + + /* So, synchronize this possibly pending irq... */ + synchronize_irq(env->comp_irq); + + /* ...and redo the whole dance. */ + spin_lock_irq(&env->comp_lock); + comp = env->comp; + env->comp = 0; + spin_unlock_irq(&env->comp_lock); + + if (comp) + enable_irq(env->comp_irq); + + return 1; +} + +static irqreturn_t envelope_detector_comp_isr(int irq, void *ctx) +{ + struct envelope *env = ctx; + + spin_lock(&env->comp_lock); + env->comp = 1; + disable_irq_nosync(env->comp_irq); + spin_unlock(&env->comp_lock); + + return IRQ_HANDLED; +} + +static void envelope_detector_setup_compare(struct envelope *env) +{ + int ret; + + /* + * Do a binary search for the peak input level, and stop + * when that level is "trapped" between two adjacent DAC + * values. + * When invert is active, use the midpoint floor so that + * env->level ends up as env->low when the termination + * criteria below is fulfilled, and use the midpoint + * ceiling when invert is not active so that env->level + * ends up as env->high in that case. + */ + env->level = (env->high + env->low + !env->invert) / 2; + + if (env->high == env->low + 1) { + complete(&env->done); + return; + } + + /* Set a "safe" DAC level (if there is such a thing)... */ + ret = iio_write_channel_raw(env->dac, env->invert ? 0 : env->dac_max); + if (ret < 0) + goto err; + + /* ...clear the comparison result... */ + envelope_detector_comp_latch(env); + + /* ...set the real DAC level... */ + ret = iio_write_channel_raw(env->dac, env->level); + if (ret < 0) + goto err; + + /* ...and wait for a bit to see if the latch catches anything. */ + schedule_delayed_work(&env->comp_timeout, + msecs_to_jiffies(env->comp_interval)); + return; + +err: + env->level = ret; + complete(&env->done); +} + +static void envelope_detector_timeout(struct work_struct *work) +{ + struct envelope *env = container_of(work, struct envelope, + comp_timeout.work); + + /* Adjust low/high depending on the latch content... */ + if (!envelope_detector_comp_latch(env) ^ !env->invert) + env->low = env->level; + else + env->high = env->level; + + /* ...and continue the search. */ + envelope_detector_setup_compare(env); +} + +static int envelope_detector_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct envelope *env = iio_priv(indio_dev); + int ret; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + /* + * When invert is active, start with high=max+1 and low=0 + * since we will end up with the low value when the + * termination criteria is fulfilled (rounding down). And + * start with high=max and low=-1 when invert is not active + * since we will end up with the high value in that case. + * This ensures that the returned value in both cases are + * in the same range as the DAC and is a value that has not + * triggered the comparator. + */ + mutex_lock(&env->read_lock); + env->high = env->dac_max + env->invert; + env->low = -1 + env->invert; + envelope_detector_setup_compare(env); + wait_for_completion(&env->done); + if (env->level < 0) { + ret = env->level; + goto err_unlock; + } + *val = env->invert ? env->dac_max - env->level : env->level; + mutex_unlock(&env->read_lock); + + return IIO_VAL_INT; + + case IIO_CHAN_INFO_SCALE: + return iio_read_channel_scale(env->dac, val, val2); + } + + return -EINVAL; + +err_unlock: + mutex_unlock(&env->read_lock); + return ret; +} + +static ssize_t envelope_show_invert(struct iio_dev *indio_dev, + uintptr_t private, + struct iio_chan_spec const *ch, char *buf) +{ + struct envelope *env = iio_priv(indio_dev); + + return sprintf(buf, "%u\n", env->invert); +} + +static ssize_t envelope_store_invert(struct iio_dev *indio_dev, + uintptr_t private, + struct iio_chan_spec const *ch, + const char *buf, size_t len) +{ + struct envelope *env = iio_priv(indio_dev); + unsigned long invert; + int ret; + u32 trigger; + + ret = kstrtoul(buf, 0, &invert); + if (ret < 0) + return ret; + if (invert > 1) + return -EINVAL; + + trigger = invert ? env->comp_irq_trigger_inv : env->comp_irq_trigger; + + mutex_lock(&env->read_lock); + if (invert != env->invert) + ret = irq_set_irq_type(env->comp_irq, trigger); + if (!ret) { + env->invert = invert; + ret = len; + } + mutex_unlock(&env->read_lock); + + return ret; +} + +static ssize_t envelope_show_comp_interval(struct iio_dev *indio_dev, + uintptr_t private, + struct iio_chan_spec const *ch, + char *buf) +{ + struct envelope *env = iio_priv(indio_dev); + + return sprintf(buf, "%u\n", env->comp_interval); +} + +static ssize_t envelope_store_comp_interval(struct iio_dev *indio_dev, + uintptr_t private, + struct iio_chan_spec const *ch, + const char *buf, size_t len) +{ + struct envelope *env = iio_priv(indio_dev); + unsigned long interval; + int ret; + + ret = kstrtoul(buf, 0, &interval); + if (ret < 0) + return ret; + if (interval > 1000) + return -EINVAL; + + mutex_lock(&env->read_lock); + env->comp_interval = interval; + mutex_unlock(&env->read_lock); + + return len; +} + +static const struct iio_chan_spec_ext_info envelope_detector_ext_info[] = { + { .name = "invert", + .read = envelope_show_invert, + .write = envelope_store_invert, }, + { .name = "compare_interval", + .read = envelope_show_comp_interval, + .write = envelope_store_comp_interval, }, + { } +}; + +static const struct iio_chan_spec envelope_detector_iio_channel = { + .type = IIO_ALTVOLTAGE, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) + | BIT(IIO_CHAN_INFO_SCALE), + .ext_info = envelope_detector_ext_info, + .indexed = 1, +}; + +static const struct iio_info envelope_detector_info = { + .read_raw = &envelope_detector_read_raw, +}; + +static int envelope_detector_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct iio_dev *indio_dev; + struct envelope *env; + enum iio_chan_type type; + int ret; + + indio_dev = devm_iio_device_alloc(dev, sizeof(*env)); + if (!indio_dev) + return -ENOMEM; + + platform_set_drvdata(pdev, indio_dev); + env = iio_priv(indio_dev); + env->comp_interval = 50; /* some sensible default? */ + + spin_lock_init(&env->comp_lock); + mutex_init(&env->read_lock); + init_completion(&env->done); + INIT_DELAYED_WORK(&env->comp_timeout, envelope_detector_timeout); + + indio_dev->name = dev_name(dev); + indio_dev->info = &envelope_detector_info; + indio_dev->channels = &envelope_detector_iio_channel; + indio_dev->num_channels = 1; + + env->dac = devm_iio_channel_get(dev, "dac"); + if (IS_ERR(env->dac)) + return dev_err_probe(dev, PTR_ERR(env->dac), + "failed to get dac input channel\n"); + + env->comp_irq = platform_get_irq_byname(pdev, "comp"); + if (env->comp_irq < 0) + return env->comp_irq; + + ret = devm_request_irq(dev, env->comp_irq, envelope_detector_comp_isr, + 0, "envelope-detector", env); + if (ret) + return dev_err_probe(dev, ret, "failed to request interrupt\n"); + + env->comp_irq_trigger = irq_get_trigger_type(env->comp_irq); + if (env->comp_irq_trigger & IRQF_TRIGGER_RISING) + env->comp_irq_trigger_inv |= IRQF_TRIGGER_FALLING; + if (env->comp_irq_trigger & IRQF_TRIGGER_FALLING) + env->comp_irq_trigger_inv |= IRQF_TRIGGER_RISING; + if (env->comp_irq_trigger & IRQF_TRIGGER_HIGH) + env->comp_irq_trigger_inv |= IRQF_TRIGGER_LOW; + if (env->comp_irq_trigger & IRQF_TRIGGER_LOW) + env->comp_irq_trigger_inv |= IRQF_TRIGGER_HIGH; + + ret = iio_get_channel_type(env->dac, &type); + if (ret < 0) + return ret; + + if (type != IIO_VOLTAGE) { + dev_err(dev, "dac is of the wrong type\n"); + return -EINVAL; + } + + ret = iio_read_max_channel_raw(env->dac, &env->dac_max); + if (ret < 0) { + dev_err(dev, "dac does not indicate its raw maximum value\n"); + return ret; + } + + return devm_iio_device_register(dev, indio_dev); +} + +static const struct of_device_id envelope_detector_match[] = { + { .compatible = "axentia,tse850-envelope-detector", }, + { } +}; +MODULE_DEVICE_TABLE(of, envelope_detector_match); + +static struct platform_driver envelope_detector_driver = { + .probe = envelope_detector_probe, + .driver = { + .name = "iio-envelope-detector", + .of_match_table = envelope_detector_match, + }, +}; +module_platform_driver(envelope_detector_driver); + +MODULE_DESCRIPTION("Envelope detector using a DAC and a comparator"); +MODULE_AUTHOR("Peter Rosin "); +MODULE_LICENSE("GPL v2"); +MODULE_IMPORT_NS("IIO_CONSUMER"); diff --git a/drivers/iio/adc/ep93xx_adc.c b/drivers/iio/adc/ep93xx_adc.c new file mode 100644 index 000000000..a3e9c697e --- /dev/null +++ b/drivers/iio/adc/ep93xx_adc.c @@ -0,0 +1,248 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Driver for ADC module on the Cirrus Logic EP93xx series of SoCs + * + * Copyright (C) 2015 Alexander Sverdlin + * + * The driver uses polling to get the conversion status. According to EP93xx + * datasheets, reading ADCResult register starts the conversion, but user is also + * responsible for ensuring that delay between adjacent conversion triggers is + * long enough so that maximum allowed conversion rate is not exceeded. This + * basically renders IRQ mode unusable. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * This code could benefit from real HR Timers, but jiffy granularity would + * lower ADC conversion rate down to CONFIG_HZ, so we fallback to busy wait + * in such case. + * + * HR Timers-based version loads CPU only up to 10% during back to back ADC + * conversion, while busy wait-based version consumes whole CPU power. + */ +#ifdef CONFIG_HIGH_RES_TIMERS +#define ep93xx_adc_delay(usmin, usmax) usleep_range(usmin, usmax) +#else +#define ep93xx_adc_delay(usmin, usmax) udelay(usmin) +#endif + +#define EP93XX_ADC_RESULT 0x08 +#define EP93XX_ADC_SDR BIT(31) +#define EP93XX_ADC_SWITCH 0x18 +#define EP93XX_ADC_SW_LOCK 0x20 + +struct ep93xx_adc_priv { + struct clk *clk; + void __iomem *base; + int lastch; + struct mutex lock; +}; + +#define EP93XX_ADC_CH(index, dname, swcfg) { \ + .type = IIO_VOLTAGE, \ + .indexed = 1, \ + .channel = index, \ + .address = swcfg, \ + .datasheet_name = dname, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SCALE) | \ + BIT(IIO_CHAN_INFO_OFFSET), \ +} + +/* + * Numbering scheme for channels 0..4 is defined in EP9301 and EP9302 datasheets. + * EP9307, EP9312 and EP9312 have 3 channels more (total 8), but the numbering is + * not defined. So the last three are numbered randomly, let's say. + */ +static const struct iio_chan_spec ep93xx_adc_channels[8] = { + EP93XX_ADC_CH(0, "YM", 0x608), + EP93XX_ADC_CH(1, "SXP", 0x680), + EP93XX_ADC_CH(2, "SXM", 0x640), + EP93XX_ADC_CH(3, "SYP", 0x620), + EP93XX_ADC_CH(4, "SYM", 0x610), + EP93XX_ADC_CH(5, "XP", 0x601), + EP93XX_ADC_CH(6, "XM", 0x602), + EP93XX_ADC_CH(7, "YP", 0x604), +}; + +static int ep93xx_read_raw(struct iio_dev *iiodev, + struct iio_chan_spec const *channel, int *value, + int *shift, long mask) +{ + struct ep93xx_adc_priv *priv = iio_priv(iiodev); + unsigned long timeout; + int ret; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + mutex_lock(&priv->lock); + if (priv->lastch != channel->channel) { + priv->lastch = channel->channel; + /* + * Switch register is software-locked, unlocking must be + * immediately followed by write + */ + local_irq_disable(); + writel_relaxed(0xAA, priv->base + EP93XX_ADC_SW_LOCK); + writel_relaxed(channel->address, + priv->base + EP93XX_ADC_SWITCH); + local_irq_enable(); + /* + * Settling delay depends on module clock and could be + * 2ms or 500us + */ + ep93xx_adc_delay(2000, 2000); + } + /* Start the conversion, eventually discarding old result */ + readl_relaxed(priv->base + EP93XX_ADC_RESULT); + /* Ensure maximum conversion rate is not exceeded */ + ep93xx_adc_delay(DIV_ROUND_UP(1000000, 925), + DIV_ROUND_UP(1000000, 925)); + /* At this point conversion must be completed, but anyway... */ + ret = IIO_VAL_INT; + timeout = jiffies + msecs_to_jiffies(1) + 1; + while (1) { + u32 t; + + t = readl_relaxed(priv->base + EP93XX_ADC_RESULT); + if (t & EP93XX_ADC_SDR) { + *value = sign_extend32(t, 15); + break; + } + + if (time_after(jiffies, timeout)) { + dev_err(&iiodev->dev, "Conversion timeout\n"); + ret = -ETIMEDOUT; + break; + } + + cpu_relax(); + } + mutex_unlock(&priv->lock); + return ret; + + case IIO_CHAN_INFO_OFFSET: + /* According to datasheet, range is -25000..25000 */ + *value = 25000; + return IIO_VAL_INT; + + case IIO_CHAN_INFO_SCALE: + /* Typical supply voltage is 3.3v */ + *value = (1ULL << 32) * 3300 / 50000; + *shift = 32; + return IIO_VAL_FRACTIONAL_LOG2; + } + + return -EINVAL; +} + +static const struct iio_info ep93xx_adc_info = { + .read_raw = ep93xx_read_raw, +}; + +static int ep93xx_adc_probe(struct platform_device *pdev) +{ + int ret; + struct iio_dev *iiodev; + struct ep93xx_adc_priv *priv; + struct clk *pclk; + + iiodev = devm_iio_device_alloc(&pdev->dev, sizeof(*priv)); + if (!iiodev) + return -ENOMEM; + priv = iio_priv(iiodev); + + priv->base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(priv->base)) + return PTR_ERR(priv->base); + + iiodev->name = dev_name(&pdev->dev); + iiodev->modes = INDIO_DIRECT_MODE; + iiodev->info = &ep93xx_adc_info; + iiodev->num_channels = ARRAY_SIZE(ep93xx_adc_channels); + iiodev->channels = ep93xx_adc_channels; + + priv->lastch = -1; + mutex_init(&priv->lock); + + platform_set_drvdata(pdev, iiodev); + + priv->clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(priv->clk)) { + dev_err(&pdev->dev, "Cannot obtain clock\n"); + return PTR_ERR(priv->clk); + } + + pclk = clk_get_parent(priv->clk); + if (!pclk) { + dev_warn(&pdev->dev, "Cannot obtain parent clock\n"); + } else { + /* + * This is actually a place for improvement: + * EP93xx ADC supports two clock divisors -- 4 and 16, + * resulting in conversion rates 3750 and 925 samples per second + * with 500us or 2ms settling time respectively. + * One might find this interesting enough to be configurable. + */ + ret = clk_set_rate(priv->clk, clk_get_rate(pclk) / 16); + if (ret) + dev_warn(&pdev->dev, "Cannot set clock rate\n"); + /* + * We can tolerate rate setting failure because the module should + * work in any case. + */ + } + + ret = clk_prepare_enable(priv->clk); + if (ret) { + dev_err(&pdev->dev, "Cannot enable clock\n"); + return ret; + } + + ret = iio_device_register(iiodev); + if (ret) + clk_disable_unprepare(priv->clk); + + return ret; +} + +static void ep93xx_adc_remove(struct platform_device *pdev) +{ + struct iio_dev *iiodev = platform_get_drvdata(pdev); + struct ep93xx_adc_priv *priv = iio_priv(iiodev); + + iio_device_unregister(iiodev); + clk_disable_unprepare(priv->clk); +} + +static const struct of_device_id ep93xx_adc_of_ids[] = { + { .compatible = "cirrus,ep9301-adc" }, + { } +}; +MODULE_DEVICE_TABLE(of, ep93xx_adc_of_ids); + +static struct platform_driver ep93xx_adc_driver = { + .driver = { + .name = "ep93xx-adc", + .of_match_table = ep93xx_adc_of_ids, + }, + .probe = ep93xx_adc_probe, + .remove = ep93xx_adc_remove, +}; +module_platform_driver(ep93xx_adc_driver); + +MODULE_AUTHOR("Alexander Sverdlin "); +MODULE_DESCRIPTION("Cirrus Logic EP93XX ADC driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:ep93xx-adc"); diff --git a/drivers/iio/adc/exynos_adc.c b/drivers/iio/adc/exynos_adc.c new file mode 100644 index 000000000..c1a0061b1 --- /dev/null +++ b/drivers/iio/adc/exynos_adc.c @@ -0,0 +1,721 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * exynos_adc.c - Support for ADC in EXYNOS SoCs + * + * 8 ~ 10 channel, 10/12-bit ADC + * + * Copyright (C) 2013 Naveen Krishna Chatradhi + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +/* S3C/EXYNOS4412/5250 ADC_V1 registers definitions */ +#define ADC_V1_CON(x) ((x) + 0x00) +#define ADC_V1_DLY(x) ((x) + 0x08) +#define ADC_V1_DATX(x) ((x) + 0x0C) +#define ADC_V1_DATY(x) ((x) + 0x10) +#define ADC_V1_UPDN(x) ((x) + 0x14) +#define ADC_V1_INTCLR(x) ((x) + 0x18) +#define ADC_V1_MUX(x) ((x) + 0x1c) + +/* Future ADC_V2 registers definitions */ +#define ADC_V2_CON1(x) ((x) + 0x00) +#define ADC_V2_CON2(x) ((x) + 0x04) +#define ADC_V2_STAT(x) ((x) + 0x08) +#define ADC_V2_INT_EN(x) ((x) + 0x10) +#define ADC_V2_INT_ST(x) ((x) + 0x14) +#define ADC_V2_VER(x) ((x) + 0x20) + +/* Bit definitions for ADC_V1 */ +#define ADC_V1_CON_RES (1u << 16) +#define ADC_V1_CON_PRSCEN (1u << 14) +#define ADC_V1_CON_PRSCLV(x) (((x) & 0xFF) << 6) +#define ADC_V1_CON_STANDBY (1u << 2) + +/* Bit definitions for S3C2410 / S3C6410 ADC */ +#define ADC_S3C2410_CON_SELMUX(x) (((x) & 7) << 3) + +/* ADCTSC Register Bits */ +#define ADC_S3C2443_TSC_UD_SEN (1u << 8) +#define ADC_S3C2410_TSC_YM_SEN (1u << 7) +#define ADC_S3C2410_TSC_YP_SEN (1u << 6) +#define ADC_S3C2410_TSC_XM_SEN (1u << 5) +#define ADC_S3C2410_TSC_XP_SEN (1u << 4) +#define ADC_S3C2410_TSC_XY_PST(x) (((x) & 0x3) << 0) + +#define ADC_TSC_WAIT4INT (ADC_S3C2410_TSC_YM_SEN | \ + ADC_S3C2410_TSC_YP_SEN | \ + ADC_S3C2410_TSC_XP_SEN | \ + ADC_S3C2410_TSC_XY_PST(3)) + +/* Bit definitions for ADC_V2 */ +#define ADC_V2_CON1_SOFT_RESET (1u << 2) + +#define ADC_V2_CON2_OSEL (1u << 10) +#define ADC_V2_CON2_ESEL (1u << 9) +#define ADC_V2_CON2_HIGHF (1u << 8) +#define ADC_V2_CON2_C_TIME(x) (((x) & 7) << 4) +#define ADC_V2_CON2_ACH_SEL(x) (((x) & 0xF) << 0) +#define ADC_V2_CON2_ACH_MASK 0xF + +#define MAX_ADC_V2_CHANNELS 10 +#define MAX_ADC_V1_CHANNELS 8 +#define MAX_EXYNOS3250_ADC_CHANNELS 2 +#define MAX_EXYNOS4212_ADC_CHANNELS 4 +#define MAX_S5PV210_ADC_CHANNELS 10 + +/* Bit definitions common for ADC_V1 and ADC_V2 */ +#define ADC_CON_EN_START (1u << 0) +#define ADC_CON_EN_START_MASK (0x3 << 0) +#define ADC_DATX_PRESSED (1u << 15) +#define ADC_DATX_MASK 0xFFF +#define ADC_DATY_MASK 0xFFF + +#define EXYNOS_ADC_TIMEOUT (msecs_to_jiffies(100)) + +#define EXYNOS_ADCV1_PHY_OFFSET 0x0718 +#define EXYNOS_ADCV2_PHY_OFFSET 0x0720 + +struct exynos_adc { + struct exynos_adc_data *data; + struct device *dev; + void __iomem *regs; + struct regmap *pmu_map; + struct clk *clk; + struct clk *sclk; + unsigned int irq; + struct regulator *vdd; + + struct completion completion; + + u32 value; + unsigned int version; + + /* + * Lock to protect from potential concurrent access to the + * completion callback during a manual conversion. For this driver + * a wait-callback is used to wait for the conversion result, + * so in the meantime no other read request (or conversion start) + * must be performed, otherwise it would interfere with the + * current conversion result. + */ + struct mutex lock; +}; + +struct exynos_adc_data { + int num_channels; + bool needs_sclk; + bool needs_adc_phy; + int phy_offset; + u32 mask; + + void (*init_hw)(struct exynos_adc *info); + void (*exit_hw)(struct exynos_adc *info); + void (*clear_irq)(struct exynos_adc *info); + void (*start_conv)(struct exynos_adc *info, unsigned long addr); +}; + +static void exynos_adc_unprepare_clk(struct exynos_adc *info) +{ + if (info->data->needs_sclk) + clk_unprepare(info->sclk); + clk_unprepare(info->clk); +} + +static int exynos_adc_prepare_clk(struct exynos_adc *info) +{ + int ret; + + ret = clk_prepare(info->clk); + if (ret) { + dev_err(info->dev, "failed preparing adc clock: %d\n", ret); + return ret; + } + + if (info->data->needs_sclk) { + ret = clk_prepare(info->sclk); + if (ret) { + clk_unprepare(info->clk); + dev_err(info->dev, + "failed preparing sclk_adc clock: %d\n", ret); + return ret; + } + } + + return 0; +} + +static void exynos_adc_disable_clk(struct exynos_adc *info) +{ + if (info->data->needs_sclk) + clk_disable(info->sclk); + clk_disable(info->clk); +} + +static int exynos_adc_enable_clk(struct exynos_adc *info) +{ + int ret; + + ret = clk_enable(info->clk); + if (ret) { + dev_err(info->dev, "failed enabling adc clock: %d\n", ret); + return ret; + } + + if (info->data->needs_sclk) { + ret = clk_enable(info->sclk); + if (ret) { + clk_disable(info->clk); + dev_err(info->dev, + "failed enabling sclk_adc clock: %d\n", ret); + return ret; + } + } + + return 0; +} + +static void exynos_adc_v1_init_hw(struct exynos_adc *info) +{ + u32 con1; + + if (info->data->needs_adc_phy) + regmap_write(info->pmu_map, info->data->phy_offset, 1); + + /* set default prescaler values and Enable prescaler */ + con1 = ADC_V1_CON_PRSCLV(49) | ADC_V1_CON_PRSCEN; + + /* Enable 12-bit ADC resolution */ + con1 |= ADC_V1_CON_RES; + writel(con1, ADC_V1_CON(info->regs)); + + /* set touchscreen delay */ + writel(10000, ADC_V1_DLY(info->regs)); +} + +static void exynos_adc_v1_exit_hw(struct exynos_adc *info) +{ + u32 con; + + if (info->data->needs_adc_phy) + regmap_write(info->pmu_map, info->data->phy_offset, 0); + + con = readl(ADC_V1_CON(info->regs)); + con |= ADC_V1_CON_STANDBY; + writel(con, ADC_V1_CON(info->regs)); +} + +static void exynos_adc_v1_clear_irq(struct exynos_adc *info) +{ + writel(1, ADC_V1_INTCLR(info->regs)); +} + +static void exynos_adc_v1_start_conv(struct exynos_adc *info, + unsigned long addr) +{ + u32 con1; + + writel(addr, ADC_V1_MUX(info->regs)); + + con1 = readl(ADC_V1_CON(info->regs)); + writel(con1 | ADC_CON_EN_START, ADC_V1_CON(info->regs)); +} + +/* Exynos4212 and 4412 is like ADCv1 but with four channels only */ +static const struct exynos_adc_data exynos4212_adc_data = { + .num_channels = MAX_EXYNOS4212_ADC_CHANNELS, + .mask = ADC_DATX_MASK, /* 12 bit ADC resolution */ + .needs_adc_phy = true, + .phy_offset = EXYNOS_ADCV1_PHY_OFFSET, + + .init_hw = exynos_adc_v1_init_hw, + .exit_hw = exynos_adc_v1_exit_hw, + .clear_irq = exynos_adc_v1_clear_irq, + .start_conv = exynos_adc_v1_start_conv, +}; + +static const struct exynos_adc_data exynos_adc_v1_data = { + .num_channels = MAX_ADC_V1_CHANNELS, + .mask = ADC_DATX_MASK, /* 12 bit ADC resolution */ + .needs_adc_phy = true, + .phy_offset = EXYNOS_ADCV1_PHY_OFFSET, + + .init_hw = exynos_adc_v1_init_hw, + .exit_hw = exynos_adc_v1_exit_hw, + .clear_irq = exynos_adc_v1_clear_irq, + .start_conv = exynos_adc_v1_start_conv, +}; + +static const struct exynos_adc_data exynos_adc_s5pv210_data = { + .num_channels = MAX_S5PV210_ADC_CHANNELS, + .mask = ADC_DATX_MASK, /* 12 bit ADC resolution */ + + .init_hw = exynos_adc_v1_init_hw, + .exit_hw = exynos_adc_v1_exit_hw, + .clear_irq = exynos_adc_v1_clear_irq, + .start_conv = exynos_adc_v1_start_conv, +}; + +static void exynos_adc_s3c64xx_start_conv(struct exynos_adc *info, + unsigned long addr) +{ + u32 con1; + + con1 = readl(ADC_V1_CON(info->regs)); + con1 &= ~ADC_S3C2410_CON_SELMUX(0x7); + con1 |= ADC_S3C2410_CON_SELMUX(addr); + writel(con1 | ADC_CON_EN_START, ADC_V1_CON(info->regs)); +} + +static struct exynos_adc_data const exynos_adc_s3c64xx_data = { + .num_channels = MAX_ADC_V1_CHANNELS, + .mask = ADC_DATX_MASK, /* 12 bit ADC resolution */ + + .init_hw = exynos_adc_v1_init_hw, + .exit_hw = exynos_adc_v1_exit_hw, + .clear_irq = exynos_adc_v1_clear_irq, + .start_conv = exynos_adc_s3c64xx_start_conv, +}; + +static void exynos_adc_v2_init_hw(struct exynos_adc *info) +{ + u32 con1, con2; + + if (info->data->needs_adc_phy) + regmap_write(info->pmu_map, info->data->phy_offset, 1); + + con1 = ADC_V2_CON1_SOFT_RESET; + writel(con1, ADC_V2_CON1(info->regs)); + + con2 = ADC_V2_CON2_OSEL | ADC_V2_CON2_ESEL | + ADC_V2_CON2_HIGHF | ADC_V2_CON2_C_TIME(0); + writel(con2, ADC_V2_CON2(info->regs)); + + /* Enable interrupts */ + writel(1, ADC_V2_INT_EN(info->regs)); +} + +static void exynos_adc_v2_exit_hw(struct exynos_adc *info) +{ + u32 con; + + if (info->data->needs_adc_phy) + regmap_write(info->pmu_map, info->data->phy_offset, 0); + + con = readl(ADC_V2_CON1(info->regs)); + con &= ~ADC_CON_EN_START; + writel(con, ADC_V2_CON1(info->regs)); +} + +static void exynos_adc_v2_clear_irq(struct exynos_adc *info) +{ + writel(1, ADC_V2_INT_ST(info->regs)); +} + +static void exynos_adc_v2_start_conv(struct exynos_adc *info, + unsigned long addr) +{ + u32 con1, con2; + + con2 = readl(ADC_V2_CON2(info->regs)); + con2 &= ~ADC_V2_CON2_ACH_MASK; + con2 |= ADC_V2_CON2_ACH_SEL(addr); + writel(con2, ADC_V2_CON2(info->regs)); + + con1 = readl(ADC_V2_CON1(info->regs)); + writel(con1 | ADC_CON_EN_START, ADC_V2_CON1(info->regs)); +} + +static const struct exynos_adc_data exynos_adc_v2_data = { + .num_channels = MAX_ADC_V2_CHANNELS, + .mask = ADC_DATX_MASK, /* 12 bit ADC resolution */ + .needs_adc_phy = true, + .phy_offset = EXYNOS_ADCV2_PHY_OFFSET, + + .init_hw = exynos_adc_v2_init_hw, + .exit_hw = exynos_adc_v2_exit_hw, + .clear_irq = exynos_adc_v2_clear_irq, + .start_conv = exynos_adc_v2_start_conv, +}; + +static const struct exynos_adc_data exynos3250_adc_data = { + .num_channels = MAX_EXYNOS3250_ADC_CHANNELS, + .mask = ADC_DATX_MASK, /* 12 bit ADC resolution */ + .needs_sclk = true, + .needs_adc_phy = true, + .phy_offset = EXYNOS_ADCV1_PHY_OFFSET, + + .init_hw = exynos_adc_v2_init_hw, + .exit_hw = exynos_adc_v2_exit_hw, + .clear_irq = exynos_adc_v2_clear_irq, + .start_conv = exynos_adc_v2_start_conv, +}; + +static void exynos_adc_exynos7_init_hw(struct exynos_adc *info) +{ + u32 con1, con2; + + con1 = ADC_V2_CON1_SOFT_RESET; + writel(con1, ADC_V2_CON1(info->regs)); + + con2 = readl(ADC_V2_CON2(info->regs)); + con2 &= ~ADC_V2_CON2_C_TIME(7); + con2 |= ADC_V2_CON2_C_TIME(0); + writel(con2, ADC_V2_CON2(info->regs)); + + /* Enable interrupts */ + writel(1, ADC_V2_INT_EN(info->regs)); +} + +static const struct exynos_adc_data exynos7_adc_data = { + .num_channels = MAX_ADC_V1_CHANNELS, + .mask = ADC_DATX_MASK, /* 12 bit ADC resolution */ + + .init_hw = exynos_adc_exynos7_init_hw, + .exit_hw = exynos_adc_v2_exit_hw, + .clear_irq = exynos_adc_v2_clear_irq, + .start_conv = exynos_adc_v2_start_conv, +}; + +static const struct of_device_id exynos_adc_match[] = { + { + .compatible = "samsung,s3c6410-adc", + .data = &exynos_adc_s3c64xx_data, + }, { + .compatible = "samsung,s5pv210-adc", + .data = &exynos_adc_s5pv210_data, + }, { + .compatible = "samsung,exynos4212-adc", + .data = &exynos4212_adc_data, + }, { + .compatible = "samsung,exynos-adc-v1", + .data = &exynos_adc_v1_data, + }, { + .compatible = "samsung,exynos-adc-v2", + .data = &exynos_adc_v2_data, + }, { + .compatible = "samsung,exynos3250-adc", + .data = &exynos3250_adc_data, + }, { + .compatible = "samsung,exynos7-adc", + .data = &exynos7_adc_data, + }, + { } +}; +MODULE_DEVICE_TABLE(of, exynos_adc_match); + +static struct exynos_adc_data *exynos_adc_get_data(struct platform_device *pdev) +{ + const struct of_device_id *match; + + match = of_match_node(exynos_adc_match, pdev->dev.of_node); + return (struct exynos_adc_data *)match->data; +} + +static int exynos_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, + int *val2, + long mask) +{ + struct exynos_adc *info = iio_priv(indio_dev); + unsigned long time_left; + int ret; + + if (mask == IIO_CHAN_INFO_SCALE) { + ret = regulator_get_voltage(info->vdd); + if (ret < 0) + return ret; + + /* Regulator voltage is in uV, but need mV */ + *val = ret / 1000; + *val2 = info->data->mask; + + return IIO_VAL_FRACTIONAL; + } else if (mask != IIO_CHAN_INFO_RAW) { + return -EINVAL; + } + + mutex_lock(&info->lock); + reinit_completion(&info->completion); + + /* Select the channel to be used and Trigger conversion */ + if (info->data->start_conv) + info->data->start_conv(info, chan->address); + + time_left = wait_for_completion_timeout(&info->completion, + EXYNOS_ADC_TIMEOUT); + if (time_left == 0) { + dev_warn(&indio_dev->dev, "Conversion timed out! Resetting\n"); + if (info->data->init_hw) + info->data->init_hw(info); + ret = -ETIMEDOUT; + } else { + *val = info->value; + *val2 = 0; + ret = IIO_VAL_INT; + } + + mutex_unlock(&info->lock); + + return ret; +} + +static irqreturn_t exynos_adc_isr(int irq, void *dev_id) +{ + struct exynos_adc *info = dev_id; + u32 mask = info->data->mask; + + /* Read value */ + info->value = readl(ADC_V1_DATX(info->regs)) & mask; + + /* clear irq */ + if (info->data->clear_irq) + info->data->clear_irq(info); + + complete(&info->completion); + + return IRQ_HANDLED; +} + +static int exynos_adc_reg_access(struct iio_dev *indio_dev, + unsigned reg, unsigned writeval, + unsigned *readval) +{ + struct exynos_adc *info = iio_priv(indio_dev); + + if (readval == NULL) + return -EINVAL; + + *readval = readl(info->regs + reg); + + return 0; +} + +static const struct iio_info exynos_adc_iio_info = { + .read_raw = &exynos_read_raw, + .debugfs_reg_access = &exynos_adc_reg_access, +}; + +#define ADC_CHANNEL(_index, _id) { \ + .type = IIO_VOLTAGE, \ + .indexed = 1, \ + .channel = _index, \ + .address = _index, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SCALE), \ + .datasheet_name = _id, \ +} + +static const struct iio_chan_spec exynos_adc_iio_channels[] = { + ADC_CHANNEL(0, "adc0"), + ADC_CHANNEL(1, "adc1"), + ADC_CHANNEL(2, "adc2"), + ADC_CHANNEL(3, "adc3"), + ADC_CHANNEL(4, "adc4"), + ADC_CHANNEL(5, "adc5"), + ADC_CHANNEL(6, "adc6"), + ADC_CHANNEL(7, "adc7"), + ADC_CHANNEL(8, "adc8"), + ADC_CHANNEL(9, "adc9"), +}; + +static int exynos_adc_probe(struct platform_device *pdev) +{ + struct exynos_adc *info = NULL; + struct device *dev = &pdev->dev; + struct device_node *np = pdev->dev.of_node; + struct iio_dev *indio_dev = NULL; + int ret; + int irq; + + indio_dev = devm_iio_device_alloc(dev, sizeof(struct exynos_adc)); + if (!indio_dev) + return -ENOMEM; + + info = iio_priv(indio_dev); + + info->data = exynos_adc_get_data(pdev); + if (!info->data) + return dev_err_probe(dev, -EINVAL, "failed getting exynos_adc_data\n"); + + info->regs = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(info->regs)) + return PTR_ERR(info->regs); + + + if (info->data->needs_adc_phy) { + info->pmu_map = syscon_regmap_lookup_by_phandle(np, "samsung,syscon-phandle"); + if (IS_ERR(info->pmu_map)) + return dev_err_probe(dev, PTR_ERR(info->pmu_map), + "syscon regmap lookup failed.\n"); + } + + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return irq; + info->irq = irq; + info->dev = dev; + + init_completion(&info->completion); + + info->clk = devm_clk_get(dev, "adc"); + if (IS_ERR(info->clk)) + return dev_err_probe(dev, PTR_ERR(info->clk), "failed getting clock\n"); + + if (info->data->needs_sclk) { + info->sclk = devm_clk_get(dev, "sclk"); + if (IS_ERR(info->sclk)) + return dev_err_probe(dev, PTR_ERR(info->sclk), + "failed getting sclk clock\n"); + } + + info->vdd = devm_regulator_get(dev, "vdd"); + if (IS_ERR(info->vdd)) + return dev_err_probe(dev, PTR_ERR(info->vdd), "failed getting regulator"); + + ret = regulator_enable(info->vdd); + if (ret) + return ret; + + ret = exynos_adc_prepare_clk(info); + if (ret) + goto err_disable_reg; + + ret = exynos_adc_enable_clk(info); + if (ret) + goto err_unprepare_clk; + + platform_set_drvdata(pdev, indio_dev); + + indio_dev->name = dev_name(dev); + indio_dev->info = &exynos_adc_iio_info; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = exynos_adc_iio_channels; + indio_dev->num_channels = info->data->num_channels; + + mutex_init(&info->lock); + + ret = request_irq(info->irq, exynos_adc_isr, 0, dev_name(dev), info); + if (ret < 0) { + dev_err(dev, "failed requesting irq, irq = %d\n", info->irq); + goto err_disable_clk; + } + + ret = iio_device_register(indio_dev); + if (ret) + goto err_irq; + + if (info->data->init_hw) + info->data->init_hw(info); + + ret = of_platform_populate(np, exynos_adc_match, NULL, &indio_dev->dev); + if (ret < 0) { + dev_err(dev, "failed adding child nodes\n"); + goto err_of_populate; + } + + return 0; + +err_of_populate: + of_platform_depopulate(&indio_dev->dev); + iio_device_unregister(indio_dev); +err_irq: + free_irq(info->irq, info); +err_disable_clk: + if (info->data->exit_hw) + info->data->exit_hw(info); + exynos_adc_disable_clk(info); +err_unprepare_clk: + exynos_adc_unprepare_clk(info); +err_disable_reg: + regulator_disable(info->vdd); + return ret; +} + +static void exynos_adc_remove(struct platform_device *pdev) +{ + struct iio_dev *indio_dev = platform_get_drvdata(pdev); + struct exynos_adc *info = iio_priv(indio_dev); + + of_platform_depopulate(&indio_dev->dev); + iio_device_unregister(indio_dev); + free_irq(info->irq, info); + if (info->data->exit_hw) + info->data->exit_hw(info); + exynos_adc_disable_clk(info); + exynos_adc_unprepare_clk(info); + regulator_disable(info->vdd); +} + +static int exynos_adc_suspend(struct device *dev) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct exynos_adc *info = iio_priv(indio_dev); + + if (info->data->exit_hw) + info->data->exit_hw(info); + exynos_adc_disable_clk(info); + regulator_disable(info->vdd); + + return 0; +} + +static int exynos_adc_resume(struct device *dev) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct exynos_adc *info = iio_priv(indio_dev); + int ret; + + ret = regulator_enable(info->vdd); + if (ret) + return ret; + + ret = exynos_adc_enable_clk(info); + if (ret) + return ret; + + if (info->data->init_hw) + info->data->init_hw(info); + + return 0; +} + +static DEFINE_SIMPLE_DEV_PM_OPS(exynos_adc_pm_ops, exynos_adc_suspend, + exynos_adc_resume); + +static struct platform_driver exynos_adc_driver = { + .probe = exynos_adc_probe, + .remove = exynos_adc_remove, + .driver = { + .name = "exynos-adc", + .of_match_table = exynos_adc_match, + .pm = pm_sleep_ptr(&exynos_adc_pm_ops), + }, +}; + +module_platform_driver(exynos_adc_driver); + +MODULE_AUTHOR("Naveen Krishna Chatradhi "); +MODULE_DESCRIPTION("Samsung EXYNOS5 ADC driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/adc/fsl-imx25-gcq.c b/drivers/iio/adc/fsl-imx25-gcq.c new file mode 100644 index 000000000..dc310ed61 --- /dev/null +++ b/drivers/iio/adc/fsl-imx25-gcq.c @@ -0,0 +1,389 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2014-2015 Pengutronix, Markus Pargmann + * + * This is the driver for the imx25 GCQ (Generic Conversion Queue) + * connected to the imx25 ADC. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define MX25_GCQ_TIMEOUT (msecs_to_jiffies(2000)) + +static const char * const driver_name = "mx25-gcq"; + +enum mx25_gcq_cfgs { + MX25_CFG_XP = 0, + MX25_CFG_YP, + MX25_CFG_XN, + MX25_CFG_YN, + MX25_CFG_WIPER, + MX25_CFG_INAUX0, + MX25_CFG_INAUX1, + MX25_CFG_INAUX2, + MX25_NUM_CFGS, +}; + +struct mx25_gcq_priv { + struct regmap *regs; + struct completion completed; + struct clk *clk; + int irq; + struct regulator *vref[4]; + u32 channel_vref_mv[MX25_NUM_CFGS]; + /* + * Lock to protect the device state during a potential concurrent + * read access from userspace. Reading a raw value requires a sequence + * of register writes, then a wait for a completion callback, + * and finally a register read, during which userspace could issue + * another read request. This lock protects a read access from + * occurring before another one has finished. + */ + struct mutex lock; +}; + +#define MX25_CQG_CHAN(chan, id) {\ + .type = IIO_VOLTAGE,\ + .indexed = 1,\ + .channel = chan,\ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ + BIT(IIO_CHAN_INFO_SCALE),\ + .datasheet_name = id,\ +} + +static const struct iio_chan_spec mx25_gcq_channels[MX25_NUM_CFGS] = { + MX25_CQG_CHAN(MX25_CFG_XP, "xp"), + MX25_CQG_CHAN(MX25_CFG_YP, "yp"), + MX25_CQG_CHAN(MX25_CFG_XN, "xn"), + MX25_CQG_CHAN(MX25_CFG_YN, "yn"), + MX25_CQG_CHAN(MX25_CFG_WIPER, "wiper"), + MX25_CQG_CHAN(MX25_CFG_INAUX0, "inaux0"), + MX25_CQG_CHAN(MX25_CFG_INAUX1, "inaux1"), + MX25_CQG_CHAN(MX25_CFG_INAUX2, "inaux2"), +}; + +static const char * const mx25_gcq_refp_names[] = { + [MX25_ADC_REFP_YP] = "yp", + [MX25_ADC_REFP_XP] = "xp", + [MX25_ADC_REFP_INT] = "int", + [MX25_ADC_REFP_EXT] = "ext", +}; + +static irqreturn_t mx25_gcq_irq(int irq, void *data) +{ + struct mx25_gcq_priv *priv = data; + u32 stats; + + regmap_read(priv->regs, MX25_ADCQ_SR, &stats); + + if (stats & MX25_ADCQ_SR_EOQ) { + regmap_set_bits(priv->regs, MX25_ADCQ_MR, + MX25_ADCQ_MR_EOQ_IRQ); + complete(&priv->completed); + } + + /* Disable conversion queue run */ + regmap_clear_bits(priv->regs, MX25_ADCQ_CR, MX25_ADCQ_CR_FQS); + + /* Acknowledge all possible irqs */ + regmap_write(priv->regs, MX25_ADCQ_SR, MX25_ADCQ_SR_FRR | + MX25_ADCQ_SR_FUR | MX25_ADCQ_SR_FOR | + MX25_ADCQ_SR_EOQ | MX25_ADCQ_SR_PD); + + return IRQ_HANDLED; +} + +static int mx25_gcq_get_raw_value(struct device *dev, + struct iio_chan_spec const *chan, + struct mx25_gcq_priv *priv, + int *val) +{ + long time_left; + u32 data; + + /* Setup the configuration we want to use */ + regmap_write(priv->regs, MX25_ADCQ_ITEM_7_0, + MX25_ADCQ_ITEM(0, chan->channel)); + + regmap_clear_bits(priv->regs, MX25_ADCQ_MR, MX25_ADCQ_MR_EOQ_IRQ); + + /* Trigger queue for one run */ + regmap_set_bits(priv->regs, MX25_ADCQ_CR, MX25_ADCQ_CR_FQS); + + time_left = wait_for_completion_interruptible_timeout( + &priv->completed, MX25_GCQ_TIMEOUT); + if (time_left < 0) { + dev_err(dev, "ADC wait for measurement failed\n"); + return time_left; + } else if (time_left == 0) { + dev_err(dev, "ADC timed out\n"); + return -ETIMEDOUT; + } + + regmap_read(priv->regs, MX25_ADCQ_FIFO, &data); + + *val = MX25_ADCQ_FIFO_DATA(data); + + return IIO_VAL_INT; +} + +static int mx25_gcq_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int *val, + int *val2, long mask) +{ + struct mx25_gcq_priv *priv = iio_priv(indio_dev); + int ret; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + mutex_lock(&priv->lock); + ret = mx25_gcq_get_raw_value(&indio_dev->dev, chan, priv, val); + mutex_unlock(&priv->lock); + return ret; + + case IIO_CHAN_INFO_SCALE: + *val = priv->channel_vref_mv[chan->channel]; + *val2 = 12; + return IIO_VAL_FRACTIONAL_LOG2; + + default: + return -EINVAL; + } +} + +static const struct iio_info mx25_gcq_iio_info = { + .read_raw = mx25_gcq_read_raw, +}; + +static const struct regmap_config mx25_gcq_regconfig = { + .max_register = 0x5c, + .reg_bits = 32, + .val_bits = 32, + .reg_stride = 4, +}; + +static int mx25_gcq_ext_regulator_setup(struct device *dev, + struct mx25_gcq_priv *priv, u32 refp) +{ + char reg_name[12]; + int ret; + + if (priv->vref[refp]) + return 0; + + ret = snprintf(reg_name, sizeof(reg_name), "vref-%s", + mx25_gcq_refp_names[refp]); + if (ret < 0) + return ret; + + priv->vref[refp] = devm_regulator_get_optional(dev, reg_name); + if (IS_ERR(priv->vref[refp])) + return dev_err_probe(dev, PTR_ERR(priv->vref[refp]), + "Error, trying to use external voltage reference without a %s regulator.", + reg_name); + + return 0; +} + +static int mx25_gcq_setup_cfgs(struct platform_device *pdev, + struct mx25_gcq_priv *priv) +{ + struct device *dev = &pdev->dev; + int ret, i; + + /* + * Setup all configurations registers with a default conversion + * configuration for each input + */ + for (i = 0; i < MX25_NUM_CFGS; ++i) + regmap_write(priv->regs, MX25_ADCQ_CFG(i), + MX25_ADCQ_CFG_YPLL_OFF | + MX25_ADCQ_CFG_XNUR_OFF | + MX25_ADCQ_CFG_XPUL_OFF | + MX25_ADCQ_CFG_REFP_INT | + MX25_ADCQ_CFG_IN(i) | + MX25_ADCQ_CFG_REFN_NGND2); + + device_for_each_child_node_scoped(dev, child) { + u32 reg; + u32 refp = MX25_ADCQ_CFG_REFP_INT; + u32 refn = MX25_ADCQ_CFG_REFN_NGND2; + + ret = fwnode_property_read_u32(child, "reg", ®); + if (ret) + return dev_err_probe(dev, ret, + "Failed to get reg property\n"); + + if (reg >= MX25_NUM_CFGS) + return dev_err_probe(dev, -EINVAL, + "reg value is greater than the number of available configuration registers\n"); + + fwnode_property_read_u32(child, "fsl,adc-refp", &refp); + fwnode_property_read_u32(child, "fsl,adc-refn", &refn); + + switch (refp) { + case MX25_ADC_REFP_EXT: + case MX25_ADC_REFP_XP: + case MX25_ADC_REFP_YP: + ret = mx25_gcq_ext_regulator_setup(&pdev->dev, priv, refp); + if (ret) + return ret; + priv->channel_vref_mv[reg] = + regulator_get_voltage(priv->vref[refp]); + /* Conversion from uV to mV */ + priv->channel_vref_mv[reg] /= 1000; + break; + case MX25_ADC_REFP_INT: + priv->channel_vref_mv[reg] = 2500; + break; + default: + return dev_err_probe(dev, -EINVAL, + "Invalid positive reference %d\n", refp); + } + + /* + * Shift the read values to the correct positions within the + * register. + */ + refp = MX25_ADCQ_CFG_REFP(refp); + refn = MX25_ADCQ_CFG_REFN(refn); + + if ((refp & MX25_ADCQ_CFG_REFP_MASK) != refp) + return dev_err_probe(dev, -EINVAL, + "Invalid fsl,adc-refp property value\n"); + + if ((refn & MX25_ADCQ_CFG_REFN_MASK) != refn) + return dev_err_probe(dev, -EINVAL, + "Invalid fsl,adc-refn property value\n"); + + regmap_update_bits(priv->regs, MX25_ADCQ_CFG(reg), + MX25_ADCQ_CFG_REFP_MASK | + MX25_ADCQ_CFG_REFN_MASK, + refp | refn); + } + regmap_set_bits(priv->regs, MX25_ADCQ_CR, + MX25_ADCQ_CR_FRST | MX25_ADCQ_CR_QRST); + + regmap_write(priv->regs, MX25_ADCQ_CR, + MX25_ADCQ_CR_PDMSK | MX25_ADCQ_CR_QSM_FQS); + + return 0; +} + +static void mx25_gcq_reg_disable(void *reg) +{ + regulator_disable(reg); +} + +/* Custom handling needed as this driver doesn't own the clock */ +static void mx25_gcq_clk_disable(void *clk) +{ + clk_disable_unprepare(clk); +} + +static int mx25_gcq_probe(struct platform_device *pdev) +{ + struct iio_dev *indio_dev; + struct mx25_gcq_priv *priv; + struct mx25_tsadc *tsadc = dev_get_drvdata(pdev->dev.parent); + struct device *dev = &pdev->dev; + void __iomem *mem; + int ret; + int i; + + indio_dev = devm_iio_device_alloc(dev, sizeof(*priv)); + if (!indio_dev) + return -ENOMEM; + + priv = iio_priv(indio_dev); + + mem = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(mem)) + return PTR_ERR(mem); + + priv->regs = devm_regmap_init_mmio(dev, mem, &mx25_gcq_regconfig); + if (IS_ERR(priv->regs)) + return dev_err_probe(dev, PTR_ERR(priv->regs), + "Failed to initialize regmap\n"); + + mutex_init(&priv->lock); + + init_completion(&priv->completed); + + ret = mx25_gcq_setup_cfgs(pdev, priv); + if (ret) + return ret; + + for (i = 0; i != 4; ++i) { + if (!priv->vref[i]) + continue; + + ret = regulator_enable(priv->vref[i]); + if (ret) + return ret; + + ret = devm_add_action_or_reset(dev, mx25_gcq_reg_disable, + priv->vref[i]); + if (ret) + return ret; + } + + priv->clk = tsadc->clk; + ret = clk_prepare_enable(priv->clk); + if (ret) + return dev_err_probe(dev, ret, "Failed to enable clock\n"); + + ret = devm_add_action_or_reset(dev, mx25_gcq_clk_disable, + priv->clk); + if (ret) + return ret; + + ret = platform_get_irq(pdev, 0); + if (ret < 0) + return ret; + + priv->irq = ret; + ret = devm_request_irq(dev, priv->irq, mx25_gcq_irq, 0, pdev->name, + priv); + if (ret) + return dev_err_probe(dev, ret, "Failed requesting IRQ\n"); + + indio_dev->channels = mx25_gcq_channels; + indio_dev->num_channels = ARRAY_SIZE(mx25_gcq_channels); + indio_dev->info = &mx25_gcq_iio_info; + indio_dev->name = driver_name; + + ret = devm_iio_device_register(dev, indio_dev); + if (ret) + return dev_err_probe(dev, ret, "Failed to register iio device\n"); + + return 0; +} + +static const struct of_device_id mx25_gcq_ids[] = { + { .compatible = "fsl,imx25-gcq", }, + { } +}; +MODULE_DEVICE_TABLE(of, mx25_gcq_ids); + +static struct platform_driver mx25_gcq_driver = { + .driver = { + .name = "mx25-gcq", + .of_match_table = mx25_gcq_ids, + }, + .probe = mx25_gcq_probe, +}; +module_platform_driver(mx25_gcq_driver); + +MODULE_DESCRIPTION("ADC driver for Freescale mx25"); +MODULE_AUTHOR("Markus Pargmann "); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/adc/gehc-pmc-adc.c b/drivers/iio/adc/gehc-pmc-adc.c new file mode 100644 index 000000000..24193e941 --- /dev/null +++ b/drivers/iio/adc/gehc-pmc-adc.c @@ -0,0 +1,228 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * The GE HealthCare PMC ADC is a 16-Channel (Voltage and current), 16-Bit + * ADC with an I2C Interface. + * + * Copyright (C) 2024, GE HealthCare + * + * Authors: + * Herve Codina + */ +#include +#include +#include +#include +#include +#include +#include +#include + +struct pmc_adc { + struct i2c_client *client; +}; + +#define PMC_ADC_CMD_REQUEST_PROTOCOL_VERSION 0x01 +#define PMC_ADC_CMD_READ_VOLTAGE(_ch) (0x10 | (_ch)) +#define PMC_ADC_CMD_READ_CURRENT(_ch) (0x20 | (_ch)) + +#define PMC_ADC_VOLTAGE_CHANNEL(_ch, _ds_name) { \ + .type = IIO_VOLTAGE, \ + .indexed = 1, \ + .channel = (_ch), \ + .address = PMC_ADC_CMD_READ_VOLTAGE(_ch), \ + .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED), \ + .datasheet_name = (_ds_name), \ +} + +#define PMC_ADC_CURRENT_CHANNEL(_ch, _ds_name) { \ + .type = IIO_CURRENT, \ + .indexed = 1, \ + .channel = (_ch), \ + .address = PMC_ADC_CMD_READ_CURRENT(_ch), \ + .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED), \ + .datasheet_name = (_ds_name), \ +} + +static const struct iio_chan_spec pmc_adc_channels[] = { + PMC_ADC_VOLTAGE_CHANNEL(0, "CH0_V"), + PMC_ADC_VOLTAGE_CHANNEL(1, "CH1_V"), + PMC_ADC_VOLTAGE_CHANNEL(2, "CH2_V"), + PMC_ADC_VOLTAGE_CHANNEL(3, "CH3_V"), + PMC_ADC_VOLTAGE_CHANNEL(4, "CH4_V"), + PMC_ADC_VOLTAGE_CHANNEL(5, "CH5_V"), + PMC_ADC_VOLTAGE_CHANNEL(6, "CH6_V"), + PMC_ADC_VOLTAGE_CHANNEL(7, "CH7_V"), + PMC_ADC_VOLTAGE_CHANNEL(8, "CH8_V"), + PMC_ADC_VOLTAGE_CHANNEL(9, "CH9_V"), + PMC_ADC_VOLTAGE_CHANNEL(10, "CH10_V"), + PMC_ADC_VOLTAGE_CHANNEL(11, "CH11_V"), + PMC_ADC_VOLTAGE_CHANNEL(12, "CH12_V"), + PMC_ADC_VOLTAGE_CHANNEL(13, "CH13_V"), + PMC_ADC_VOLTAGE_CHANNEL(14, "CH14_V"), + PMC_ADC_VOLTAGE_CHANNEL(15, "CH15_V"), + + PMC_ADC_CURRENT_CHANNEL(0, "CH0_I"), + PMC_ADC_CURRENT_CHANNEL(1, "CH1_I"), + PMC_ADC_CURRENT_CHANNEL(2, "CH2_I"), + PMC_ADC_CURRENT_CHANNEL(3, "CH3_I"), + PMC_ADC_CURRENT_CHANNEL(4, "CH4_I"), + PMC_ADC_CURRENT_CHANNEL(5, "CH5_I"), + PMC_ADC_CURRENT_CHANNEL(6, "CH6_I"), + PMC_ADC_CURRENT_CHANNEL(7, "CH7_I"), + PMC_ADC_CURRENT_CHANNEL(8, "CH8_I"), + PMC_ADC_CURRENT_CHANNEL(9, "CH9_I"), + PMC_ADC_CURRENT_CHANNEL(10, "CH10_I"), + PMC_ADC_CURRENT_CHANNEL(11, "CH11_I"), + PMC_ADC_CURRENT_CHANNEL(12, "CH12_I"), + PMC_ADC_CURRENT_CHANNEL(13, "CH13_I"), + PMC_ADC_CURRENT_CHANNEL(14, "CH14_I"), + PMC_ADC_CURRENT_CHANNEL(15, "CH15_I"), +}; + +static int pmc_adc_read_raw_ch(struct pmc_adc *pmc_adc, u8 cmd, int *val) +{ + s32 ret; + + ret = i2c_smbus_read_word_swapped(pmc_adc->client, cmd); + if (ret < 0) { + dev_err(&pmc_adc->client->dev, "i2c read word failed (%d)\n", ret); + return ret; + } + + *val = sign_extend32(ret, 15); + return 0; +} + +static int pmc_adc_read_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct pmc_adc *pmc_adc = iio_priv(indio_dev); + int ret; + + switch (mask) { + case IIO_CHAN_INFO_PROCESSED: + /* Values are directly read in mV or mA */ + ret = pmc_adc_read_raw_ch(pmc_adc, chan->address, val); + if (ret) + return ret; + return IIO_VAL_INT; + } + + return -EINVAL; +} + +static int pmc_adc_fwnode_xlate(struct iio_dev *indio_dev, + const struct fwnode_reference_args *iiospec) +{ + enum iio_chan_type expected_type; + unsigned int i; + + /* + * args[0]: Acquisition type (i.e. voltage or current) + * args[1]: PMC ADC channel number + */ + if (iiospec->nargs != 2) + return -EINVAL; + + switch (iiospec->args[0]) { + case GEHC_PMC_ADC_VOLTAGE: + expected_type = IIO_VOLTAGE; + break; + case GEHC_PMC_ADC_CURRENT: + expected_type = IIO_CURRENT; + break; + default: + dev_err(&indio_dev->dev, "Invalid channel type %llu\n", + iiospec->args[0]); + return -EINVAL; + } + + for (i = 0; i < indio_dev->num_channels; i++) + if (indio_dev->channels[i].type == expected_type && + indio_dev->channels[i].channel == iiospec->args[1]) + return i; + + dev_err(&indio_dev->dev, "Invalid channel type %llu number %llu\n", + iiospec->args[0], iiospec->args[1]); + return -EINVAL; +} + +static const struct iio_info pmc_adc_info = { + .read_raw = pmc_adc_read_raw, + .fwnode_xlate = pmc_adc_fwnode_xlate, +}; + +static const char *const pmc_adc_regulator_names[] = { + "vdd", + "vdda", + "vddio", + "vref", +}; + +static int pmc_adc_probe(struct i2c_client *client) +{ + struct iio_dev *indio_dev; + struct pmc_adc *pmc_adc; + struct clk *clk; + s32 val; + int ret; + + ret = devm_regulator_bulk_get_enable(&client->dev, ARRAY_SIZE(pmc_adc_regulator_names), + pmc_adc_regulator_names); + if (ret) + return dev_err_probe(&client->dev, ret, "Failed to get regulators\n"); + + clk = devm_clk_get_optional_enabled(&client->dev, "osc"); + if (IS_ERR(clk)) + return dev_err_probe(&client->dev, PTR_ERR(clk), "Failed to get osc clock\n"); + + indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*pmc_adc)); + if (!indio_dev) + return -ENOMEM; + + pmc_adc = iio_priv(indio_dev); + pmc_adc->client = client; + + val = i2c_smbus_read_byte_data(pmc_adc->client, PMC_ADC_CMD_REQUEST_PROTOCOL_VERSION); + if (val < 0) + return dev_err_probe(&client->dev, val, "Failed to get protocol version\n"); + + if (val != 0x01) + return dev_err_probe(&client->dev, -EINVAL, + "Unsupported protocol version 0x%02x\n", val); + + indio_dev->name = "pmc_adc"; + indio_dev->info = &pmc_adc_info; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = pmc_adc_channels; + indio_dev->num_channels = ARRAY_SIZE(pmc_adc_channels); + + return devm_iio_device_register(&client->dev, indio_dev); +} + +static const struct of_device_id pmc_adc_of_match[] = { + { .compatible = "gehc,pmc-adc"}, + { } +}; +MODULE_DEVICE_TABLE(of, pmc_adc_of_match); + +static const struct i2c_device_id pmc_adc_id_table[] = { + { .name = "pmc-adc" }, + { } +}; +MODULE_DEVICE_TABLE(i2c, pmc_adc_id_table); + +static struct i2c_driver pmc_adc_i2c_driver = { + .driver = { + .name = "pmc-adc", + .of_match_table = pmc_adc_of_match, + }, + .id_table = pmc_adc_id_table, + .probe = pmc_adc_probe, +}; + +module_i2c_driver(pmc_adc_i2c_driver); + +MODULE_AUTHOR("Herve Codina "); +MODULE_DESCRIPTION("GE HealthCare PMC ADC driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/iio/adc/hi8435.c b/drivers/iio/adc/hi8435.c new file mode 100644 index 000000000..9fa63de4b --- /dev/null +++ b/drivers/iio/adc/hi8435.c @@ -0,0 +1,546 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Holt Integrated Circuits HI-8435 threshold detector driver + * + * Copyright (C) 2015 Zodiac Inflight Innovations + * Copyright (C) 2015 Cogent Embedded, Inc. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Register offsets for HI-8435 */ +#define HI8435_CTRL_REG 0x02 +#define HI8435_PSEN_REG 0x04 +#define HI8435_TMDATA_REG 0x1E +#define HI8435_GOCENHYS_REG 0x3A +#define HI8435_SOCENHYS_REG 0x3C +#define HI8435_SO7_0_REG 0x10 +#define HI8435_SO15_8_REG 0x12 +#define HI8435_SO23_16_REG 0x14 +#define HI8435_SO31_24_REG 0x16 +#define HI8435_SO31_0_REG 0x78 + +#define HI8435_WRITE_OPCODE 0x00 +#define HI8435_READ_OPCODE 0x80 + +/* CTRL register bits */ +#define HI8435_CTRL_TEST 0x01 +#define HI8435_CTRL_SRST 0x02 + +struct hi8435_priv { + struct spi_device *spi; + struct mutex lock; + + unsigned long event_scan_mask; /* soft mask/unmask channels events */ + unsigned int event_prev_val; + + unsigned threshold_lo[2]; /* GND-Open and Supply-Open thresholds */ + unsigned threshold_hi[2]; /* GND-Open and Supply-Open thresholds */ + u8 reg_buffer[3] __aligned(IIO_DMA_MINALIGN); +}; + +static int hi8435_readb(struct hi8435_priv *priv, u8 reg, u8 *val) +{ + reg |= HI8435_READ_OPCODE; + return spi_write_then_read(priv->spi, ®, 1, val, 1); +} + +static int hi8435_readw(struct hi8435_priv *priv, u8 reg, u16 *val) +{ + int ret; + __be16 be_val; + + reg |= HI8435_READ_OPCODE; + ret = spi_write_then_read(priv->spi, ®, 1, &be_val, 2); + *val = be16_to_cpu(be_val); + + return ret; +} + +static int hi8435_readl(struct hi8435_priv *priv, u8 reg, u32 *val) +{ + int ret; + __be32 be_val; + + reg |= HI8435_READ_OPCODE; + ret = spi_write_then_read(priv->spi, ®, 1, &be_val, 4); + *val = be32_to_cpu(be_val); + + return ret; +} + +static int hi8435_writeb(struct hi8435_priv *priv, u8 reg, u8 val) +{ + priv->reg_buffer[0] = reg | HI8435_WRITE_OPCODE; + priv->reg_buffer[1] = val; + + return spi_write(priv->spi, priv->reg_buffer, 2); +} + +static int hi8435_writew(struct hi8435_priv *priv, u8 reg, u16 val) +{ + priv->reg_buffer[0] = reg | HI8435_WRITE_OPCODE; + priv->reg_buffer[1] = (val >> 8) & 0xff; + priv->reg_buffer[2] = val & 0xff; + + return spi_write(priv->spi, priv->reg_buffer, 3); +} + +static int hi8435_read_raw(struct iio_dev *idev, + const struct iio_chan_spec *chan, + int *val, int *val2, long mask) +{ + struct hi8435_priv *priv = iio_priv(idev); + u32 tmp; + int ret; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + ret = hi8435_readl(priv, HI8435_SO31_0_REG, &tmp); + if (ret < 0) + return ret; + *val = !!(tmp & BIT(chan->channel)); + return IIO_VAL_INT; + default: + return -EINVAL; + } +} + +static int hi8435_read_event_config(struct iio_dev *idev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir) +{ + struct hi8435_priv *priv = iio_priv(idev); + + return !!(priv->event_scan_mask & BIT(chan->channel)); +} + +static int hi8435_write_event_config(struct iio_dev *idev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir, bool state) +{ + struct hi8435_priv *priv = iio_priv(idev); + int ret; + u32 tmp; + + if (state) { + ret = hi8435_readl(priv, HI8435_SO31_0_REG, &tmp); + if (ret < 0) + return ret; + if (tmp & BIT(chan->channel)) + priv->event_prev_val |= BIT(chan->channel); + else + priv->event_prev_val &= ~BIT(chan->channel); + + priv->event_scan_mask |= BIT(chan->channel); + } else + priv->event_scan_mask &= ~BIT(chan->channel); + + return 0; +} + +static int hi8435_read_event_value(struct iio_dev *idev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir, + enum iio_event_info info, + int *val, int *val2) +{ + struct hi8435_priv *priv = iio_priv(idev); + int ret; + u8 mode, psen; + u16 reg; + + ret = hi8435_readb(priv, HI8435_PSEN_REG, &psen); + if (ret < 0) + return ret; + + /* Supply-Open or GND-Open sensing mode */ + mode = !!(psen & BIT(chan->channel / 8)); + + ret = hi8435_readw(priv, mode ? HI8435_SOCENHYS_REG : + HI8435_GOCENHYS_REG, ®); + if (ret < 0) + return ret; + + if (dir == IIO_EV_DIR_FALLING) + *val = ((reg & 0xff) - (reg >> 8)) / 2; + else if (dir == IIO_EV_DIR_RISING) + *val = ((reg & 0xff) + (reg >> 8)) / 2; + + return IIO_VAL_INT; +} + +static int hi8435_write_event_value(struct iio_dev *idev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir, + enum iio_event_info info, + int val, int val2) +{ + struct hi8435_priv *priv = iio_priv(idev); + int ret; + u8 mode, psen; + u16 reg; + + ret = hi8435_readb(priv, HI8435_PSEN_REG, &psen); + if (ret < 0) + return ret; + + /* Supply-Open or GND-Open sensing mode */ + mode = !!(psen & BIT(chan->channel / 8)); + + ret = hi8435_readw(priv, mode ? HI8435_SOCENHYS_REG : + HI8435_GOCENHYS_REG, ®); + if (ret < 0) + return ret; + + if (dir == IIO_EV_DIR_FALLING) { + /* falling threshold range 2..21V, hysteresis minimum 2V */ + if (val < 2 || val > 21 || (val + 2) > priv->threshold_hi[mode]) + return -EINVAL; + + if (val == priv->threshold_lo[mode]) + return 0; + + priv->threshold_lo[mode] = val; + + /* hysteresis must not be odd */ + if ((priv->threshold_hi[mode] - priv->threshold_lo[mode]) % 2) + priv->threshold_hi[mode]--; + } else if (dir == IIO_EV_DIR_RISING) { + /* rising threshold range 3..22V, hysteresis minimum 2V */ + if (val < 3 || val > 22 || val < (priv->threshold_lo[mode] + 2)) + return -EINVAL; + + if (val == priv->threshold_hi[mode]) + return 0; + + priv->threshold_hi[mode] = val; + + /* hysteresis must not be odd */ + if ((priv->threshold_hi[mode] - priv->threshold_lo[mode]) % 2) + priv->threshold_lo[mode]++; + } + + /* program thresholds */ + mutex_lock(&priv->lock); + + ret = hi8435_readw(priv, mode ? HI8435_SOCENHYS_REG : + HI8435_GOCENHYS_REG, ®); + if (ret < 0) { + mutex_unlock(&priv->lock); + return ret; + } + + /* hysteresis */ + reg = priv->threshold_hi[mode] - priv->threshold_lo[mode]; + reg <<= 8; + /* threshold center */ + reg |= (priv->threshold_hi[mode] + priv->threshold_lo[mode]); + + ret = hi8435_writew(priv, mode ? HI8435_SOCENHYS_REG : + HI8435_GOCENHYS_REG, reg); + + mutex_unlock(&priv->lock); + + return ret; +} + +static int hi8435_debugfs_reg_access(struct iio_dev *idev, + unsigned reg, unsigned writeval, + unsigned *readval) +{ + struct hi8435_priv *priv = iio_priv(idev); + int ret; + u8 val; + + if (readval != NULL) { + ret = hi8435_readb(priv, reg, &val); + *readval = val; + } else { + val = (u8)writeval; + ret = hi8435_writeb(priv, reg, val); + } + + return ret; +} + +static const struct iio_event_spec hi8435_events[] = { + { + .type = IIO_EV_TYPE_THRESH, + .dir = IIO_EV_DIR_RISING, + .mask_separate = BIT(IIO_EV_INFO_VALUE), + }, { + .type = IIO_EV_TYPE_THRESH, + .dir = IIO_EV_DIR_FALLING, + .mask_separate = BIT(IIO_EV_INFO_VALUE), + }, { + .type = IIO_EV_TYPE_THRESH, + .dir = IIO_EV_DIR_EITHER, + .mask_separate = BIT(IIO_EV_INFO_ENABLE), + }, +}; + +static int hi8435_get_sensing_mode(struct iio_dev *idev, + const struct iio_chan_spec *chan) +{ + struct hi8435_priv *priv = iio_priv(idev); + int ret; + u8 reg; + + ret = hi8435_readb(priv, HI8435_PSEN_REG, ®); + if (ret < 0) + return ret; + + return !!(reg & BIT(chan->channel / 8)); +} + +static int hi8435_set_sensing_mode(struct iio_dev *idev, + const struct iio_chan_spec *chan, + unsigned int mode) +{ + struct hi8435_priv *priv = iio_priv(idev); + int ret; + u8 reg; + + mutex_lock(&priv->lock); + + ret = hi8435_readb(priv, HI8435_PSEN_REG, ®); + if (ret < 0) { + mutex_unlock(&priv->lock); + return ret; + } + + reg &= ~BIT(chan->channel / 8); + if (mode) + reg |= BIT(chan->channel / 8); + + ret = hi8435_writeb(priv, HI8435_PSEN_REG, reg); + + mutex_unlock(&priv->lock); + + return ret; +} + +static const char * const hi8435_sensing_modes[] = { "GND-Open", + "Supply-Open" }; + +static const struct iio_enum hi8435_sensing_mode = { + .items = hi8435_sensing_modes, + .num_items = ARRAY_SIZE(hi8435_sensing_modes), + .get = hi8435_get_sensing_mode, + .set = hi8435_set_sensing_mode, +}; + +static const struct iio_chan_spec_ext_info hi8435_ext_info[] = { + IIO_ENUM("sensing_mode", IIO_SEPARATE, &hi8435_sensing_mode), + IIO_ENUM_AVAILABLE("sensing_mode", IIO_SHARED_BY_TYPE, &hi8435_sensing_mode), + { } +}; + +#define HI8435_VOLTAGE_CHANNEL(num) \ +{ \ + .type = IIO_VOLTAGE, \ + .indexed = 1, \ + .channel = num, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .event_spec = hi8435_events, \ + .num_event_specs = ARRAY_SIZE(hi8435_events), \ + .ext_info = hi8435_ext_info, \ +} + +static const struct iio_chan_spec hi8435_channels[] = { + HI8435_VOLTAGE_CHANNEL(0), + HI8435_VOLTAGE_CHANNEL(1), + HI8435_VOLTAGE_CHANNEL(2), + HI8435_VOLTAGE_CHANNEL(3), + HI8435_VOLTAGE_CHANNEL(4), + HI8435_VOLTAGE_CHANNEL(5), + HI8435_VOLTAGE_CHANNEL(6), + HI8435_VOLTAGE_CHANNEL(7), + HI8435_VOLTAGE_CHANNEL(8), + HI8435_VOLTAGE_CHANNEL(9), + HI8435_VOLTAGE_CHANNEL(10), + HI8435_VOLTAGE_CHANNEL(11), + HI8435_VOLTAGE_CHANNEL(12), + HI8435_VOLTAGE_CHANNEL(13), + HI8435_VOLTAGE_CHANNEL(14), + HI8435_VOLTAGE_CHANNEL(15), + HI8435_VOLTAGE_CHANNEL(16), + HI8435_VOLTAGE_CHANNEL(17), + HI8435_VOLTAGE_CHANNEL(18), + HI8435_VOLTAGE_CHANNEL(19), + HI8435_VOLTAGE_CHANNEL(20), + HI8435_VOLTAGE_CHANNEL(21), + HI8435_VOLTAGE_CHANNEL(22), + HI8435_VOLTAGE_CHANNEL(23), + HI8435_VOLTAGE_CHANNEL(24), + HI8435_VOLTAGE_CHANNEL(25), + HI8435_VOLTAGE_CHANNEL(26), + HI8435_VOLTAGE_CHANNEL(27), + HI8435_VOLTAGE_CHANNEL(28), + HI8435_VOLTAGE_CHANNEL(29), + HI8435_VOLTAGE_CHANNEL(30), + HI8435_VOLTAGE_CHANNEL(31), + IIO_CHAN_SOFT_TIMESTAMP(32), +}; + +static const struct iio_info hi8435_info = { + .read_raw = hi8435_read_raw, + .read_event_config = hi8435_read_event_config, + .write_event_config = hi8435_write_event_config, + .read_event_value = hi8435_read_event_value, + .write_event_value = hi8435_write_event_value, + .debugfs_reg_access = hi8435_debugfs_reg_access, +}; + +static void hi8435_iio_push_event(struct iio_dev *idev, unsigned int val) +{ + struct hi8435_priv *priv = iio_priv(idev); + enum iio_event_direction dir; + unsigned int i; + unsigned int status = priv->event_prev_val ^ val; + + if (!status) + return; + + for_each_set_bit(i, &priv->event_scan_mask, 32) { + if (status & BIT(i)) { + dir = val & BIT(i) ? IIO_EV_DIR_RISING : + IIO_EV_DIR_FALLING; + iio_push_event(idev, + IIO_UNMOD_EVENT_CODE(IIO_VOLTAGE, i, + IIO_EV_TYPE_THRESH, dir), + iio_get_time_ns(idev)); + } + } + + priv->event_prev_val = val; +} + +static irqreturn_t hi8435_trigger_handler(int irq, void *private) +{ + struct iio_poll_func *pf = private; + struct iio_dev *idev = pf->indio_dev; + struct hi8435_priv *priv = iio_priv(idev); + u32 val; + int ret; + + ret = hi8435_readl(priv, HI8435_SO31_0_REG, &val); + if (ret < 0) + goto err_read; + + hi8435_iio_push_event(idev, val); + +err_read: + iio_trigger_notify_done(idev->trig); + + return IRQ_HANDLED; +} + +static void hi8435_triggered_event_cleanup(void *data) +{ + iio_triggered_event_cleanup(data); +} + +static int hi8435_probe(struct spi_device *spi) +{ + struct iio_dev *idev; + struct hi8435_priv *priv; + struct gpio_desc *reset_gpio; + int ret; + + idev = devm_iio_device_alloc(&spi->dev, sizeof(*priv)); + if (!idev) + return -ENOMEM; + + priv = iio_priv(idev); + priv->spi = spi; + + reset_gpio = devm_gpiod_get(&spi->dev, NULL, GPIOD_OUT_LOW); + if (IS_ERR(reset_gpio)) { + /* chip s/w reset if h/w reset failed */ + hi8435_writeb(priv, HI8435_CTRL_REG, HI8435_CTRL_SRST); + hi8435_writeb(priv, HI8435_CTRL_REG, 0); + } else { + udelay(5); + gpiod_set_value_cansleep(reset_gpio, 1); + } + + mutex_init(&priv->lock); + + idev->name = spi_get_device_id(spi)->name; + idev->modes = INDIO_DIRECT_MODE; + idev->info = &hi8435_info; + idev->channels = hi8435_channels; + idev->num_channels = ARRAY_SIZE(hi8435_channels); + + /* unmask all events */ + priv->event_scan_mask = ~(0); + /* + * There is a restriction in the chip - the hysteresis can not be odd. + * If the hysteresis is set to odd value then chip gets into lock state + * and not functional anymore. + * After chip reset the thresholds are in undefined state, so we need to + * initialize thresholds to some initial values and then prevent + * userspace setting odd hysteresis. + * + * Set threshold low voltage to 2V, threshold high voltage to 4V + * for both GND-Open and Supply-Open sensing modes. + */ + priv->threshold_lo[0] = priv->threshold_lo[1] = 2; + priv->threshold_hi[0] = priv->threshold_hi[1] = 4; + hi8435_writew(priv, HI8435_GOCENHYS_REG, 0x206); + hi8435_writew(priv, HI8435_SOCENHYS_REG, 0x206); + + ret = iio_triggered_event_setup(idev, NULL, hi8435_trigger_handler); + if (ret) + return ret; + + ret = devm_add_action_or_reset(&spi->dev, + hi8435_triggered_event_cleanup, + idev); + if (ret) + return ret; + + return devm_iio_device_register(&spi->dev, idev); +} + +static const struct of_device_id hi8435_dt_ids[] = { + { .compatible = "holt,hi8435" }, + { } +}; +MODULE_DEVICE_TABLE(of, hi8435_dt_ids); + +static const struct spi_device_id hi8435_id[] = { + { .name = "hi8435" }, + { } +}; +MODULE_DEVICE_TABLE(spi, hi8435_id); + +static struct spi_driver hi8435_driver = { + .driver = { + .name = "hi8435", + .of_match_table = hi8435_dt_ids, + }, + .probe = hi8435_probe, + .id_table = hi8435_id, +}; +module_spi_driver(hi8435_driver); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Vladimir Barinov"); +MODULE_DESCRIPTION("HI-8435 threshold detector"); diff --git a/drivers/iio/adc/hx711.c b/drivers/iio/adc/hx711.c new file mode 100644 index 000000000..00adafa7c --- /dev/null +++ b/drivers/iio/adc/hx711.c @@ -0,0 +1,629 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * HX711: analog to digital converter for weight sensor module + * + * Copyright (c) 2016 Andreas Klinger + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define HX711_DATA_BITS 24 + +/* gain to pulse and scale conversion */ +#define HX711_GAIN_MAX 3 +#define HX711_RESET_GAIN 128 + +struct hx711_gain_to_scale { + int gain; + int gain_pulse; + int channel; +}; + +/* + * .scale depends on AVDD which in turn is known as soon as the regulator + * is available; it is stored per device in hx711_data.gain_scale[] + * + * channel A in documentation is channel 0 in source code + * channel B in documentation is channel 1 in source code + */ +static const struct hx711_gain_to_scale hx711_gain_to_scale[HX711_GAIN_MAX] = { + { 128, 1, 0 }, + { 32, 2, 1 }, + { 64, 3, 0 }, +}; + +static int hx711_get_gain_to_pulse(int gain) +{ + int i; + + for (i = 0; i < HX711_GAIN_MAX; i++) + if (hx711_gain_to_scale[i].gain == gain) + return hx711_gain_to_scale[i].gain_pulse; + return 1; +} + +static int hx711_get_gain_to_scale(const int *gain_scale, int gain) +{ + int i; + + for (i = 0; i < HX711_GAIN_MAX; i++) + if (hx711_gain_to_scale[i].gain == gain) + return gain_scale[i]; + return 0; +} + +static int hx711_get_scale_to_gain(const int *gain_scale, int scale) +{ + int i; + + for (i = 0; i < HX711_GAIN_MAX; i++) + if (gain_scale[i] == scale) + return hx711_gain_to_scale[i].gain; + return -EINVAL; +} + +/** + * struct hx711_chip_info - per-variant static configuration + * @name: IIO device name + * @channels: channel specification array + * @num_channels: number of entries in @channels + * @iio_info: IIO info ops for this variant + */ +struct hx711_chip_info { + const char *name; + const struct iio_chan_spec *channels __counted_by_ptr(num_channels); + unsigned int num_channels; + const struct iio_info *iio_info; +}; + +struct hx711_data { + struct device *dev; + struct gpio_desc *gpiod_pd_sck; + struct gpio_desc *gpiod_dout; + int gain_set; /* gain set on device */ + int gain_chan_a; /* gain for channel A */ + int gain_scale[HX711_GAIN_MAX]; + const struct hx711_chip_info *chip_info; + struct mutex lock; + /* + * triggered buffer + * 2x32-bit channel + 64-bit naturally aligned timestamp + */ + struct { + u32 channel[2]; + aligned_s64 timestamp; + } buffer; + /* + * delay after a rising edge on SCK until the data is ready DOUT + * this is dependent on the hx711 where the datasheet tells a + * maximum value of 100 ns + * but also on potential parasitic capacities on the wiring + */ + u32 data_ready_delay_ns; + u32 clock_frequency; +}; + +static int hx711_cycle(struct hx711_data *hx711_data) +{ + unsigned long flags; + + /* + * if preempted for more then 60us while PD_SCK is high: + * hx711 is going in reset + * ==> measuring is false + */ + local_irq_save(flags); + gpiod_set_value(hx711_data->gpiod_pd_sck, 1); + + /* + * wait until DOUT is ready + * it turned out that parasitic capacities are extending the time + * until DOUT has reached it's value + */ + ndelay(hx711_data->data_ready_delay_ns); + + /* + * here we are not waiting for 0.2 us as suggested by the datasheet, + * because the oscilloscope showed in a test scenario + * at least 1.15 us for PD_SCK high (T3 in datasheet) + * and 0.56 us for PD_SCK low on TI Sitara with 800 MHz + */ + gpiod_set_value(hx711_data->gpiod_pd_sck, 0); + local_irq_restore(flags); + + /* + * make it a square wave for addressing cases with capacitance on + * PC_SCK + */ + ndelay(hx711_data->data_ready_delay_ns); + + /* sample as late as possible */ + return gpiod_get_value(hx711_data->gpiod_dout); +} + +static int hx711_read(struct hx711_data *hx711_data, int trailing_pulses) +{ + int value = 0; + int val; + int ret; + + /* we double check if it's really down */ + val = gpiod_get_value(hx711_data->gpiod_dout); + if (val) + return -EIO; + + for (int i = 0; i < HX711_DATA_BITS; i++) { + value <<= 1; + ret = hx711_cycle(hx711_data); + if (ret) + value++; + } + + value ^= 0x800000; + + for (int i = 0; i < trailing_pulses; i++) + hx711_cycle(hx711_data); + + return value; +} + +static int hx711_wait_for_ready(struct hx711_data *hx711_data) +{ + int i, val; + + /* + * in some rare cases the reset takes quite a long time + * especially when the channel is changed. + * Allow up to one second for it + */ + for (i = 0; i < 100; i++) { + val = gpiod_get_value(hx711_data->gpiod_dout); + if (!val) + break; + /* sleep at least 10 ms */ + msleep(10); + } + if (val) + return -EIO; + + return 0; +} + +static int hx711_reset(struct hx711_data *hx711_data) +{ + int val; + + val = hx711_wait_for_ready(hx711_data); + if (val) { + /* + * an examination with the oszilloscope indicated + * that the first value read after the reset is not stable + * if we reset too short; + * the shorter the reset cycle + * the less reliable the first value after reset is; + * there were no problems encountered with a value + * of 10 ms or higher + */ + gpiod_set_value(hx711_data->gpiod_pd_sck, 1); + msleep(10); + gpiod_set_value(hx711_data->gpiod_pd_sck, 0); + + val = hx711_wait_for_ready(hx711_data); + + /* after a reset the gain is 128 */ + hx711_data->gain_set = HX711_RESET_GAIN; + } + + return val; +} + +static int hx711_set_gain_for_channel(struct hx711_data *hx711_data, int chan) +{ + int ret; + + if (chan == 0) { + if (hx711_data->gain_set == 32) { + hx711_data->gain_set = hx711_data->gain_chan_a; + + ret = hx711_read(hx711_data, + hx711_get_gain_to_pulse(hx711_data->gain_set)); + if (ret < 0) + return ret; + + ret = hx711_wait_for_ready(hx711_data); + if (ret) + return ret; + } + } else { + if (hx711_data->gain_set != 32) { + hx711_data->gain_set = 32; + + ret = hx711_read(hx711_data, + hx711_get_gain_to_pulse(hx711_data->gain_set)); + if (ret < 0) + return ret; + + ret = hx711_wait_for_ready(hx711_data); + if (ret) + return ret; + } + } + + return 0; +} + +static int hx711_set_hx711_channel(struct hx711_data *hx711_data, + const struct iio_chan_spec *chan, + int *trailing_pulses) +{ + int ret; + + ret = hx711_set_gain_for_channel(hx711_data, chan->channel); + if (ret < 0) + return ret; + + *trailing_pulses = hx711_get_gain_to_pulse(hx711_data->gain_set); + + return 0; +} + +static int hx711_reset_read(struct hx711_data *hx711_data, + const struct iio_chan_spec *chan) +{ + int trailing_pulses; + int ret; + + /* + * hx711_reset() must be called from here + * because it could be calling hx711_read() by itself + */ + if (hx711_reset(hx711_data)) { + dev_err(hx711_data->dev, "reset failed!"); + return -EIO; + } + + ret = hx711_set_hx711_channel(hx711_data, chan, &trailing_pulses); + if (ret < 0) + return ret; + + return hx711_read(hx711_data, trailing_pulses); +} + +static int hx711_read_raw(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + int *val, int *val2, long mask) +{ + struct hx711_data *hx711_data = iio_priv(indio_dev); + + switch (mask) { + case IIO_CHAN_INFO_RAW: + mutex_lock(&hx711_data->lock); + + *val = hx711_reset_read(hx711_data, chan); + + mutex_unlock(&hx711_data->lock); + + if (*val < 0) + return *val; + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + *val = 0; + mutex_lock(&hx711_data->lock); + + *val2 = hx711_get_gain_to_scale(hx711_data->gain_scale, + hx711_data->gain_set); + + mutex_unlock(&hx711_data->lock); + + return IIO_VAL_INT_PLUS_NANO; + default: + return -EINVAL; + } +} + +static int hx711_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, + int val2, + long mask) +{ + struct hx711_data *hx711_data = iio_priv(indio_dev); + int ret; + int gain; + + switch (mask) { + case IIO_CHAN_INFO_SCALE: + /* + * a scale greater than 1 mV per LSB is not possible + * with the HX711, therefore val must be 0 + */ + if (val != 0) + return -EINVAL; + + mutex_lock(&hx711_data->lock); + + gain = hx711_get_scale_to_gain(hx711_data->gain_scale, val2); + if (gain < 0) { + mutex_unlock(&hx711_data->lock); + return gain; + } + + if (gain != hx711_data->gain_set) { + hx711_data->gain_set = gain; + if (gain != 32) + hx711_data->gain_chan_a = gain; + + ret = hx711_read(hx711_data, + hx711_get_gain_to_pulse(hx711_data->gain_set)); + if (ret < 0) { + mutex_unlock(&hx711_data->lock); + return ret; + } + } + + mutex_unlock(&hx711_data->lock); + return 0; + default: + return -EINVAL; + } + + return 0; +} + +static int hx711_write_raw_get_fmt(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + long mask) +{ + return IIO_VAL_INT_PLUS_NANO; +} + +static irqreturn_t hx711_trigger(int irq, void *p) +{ + struct iio_poll_func *pf = p; + struct iio_dev *indio_dev = pf->indio_dev; + struct hx711_data *hx711_data = iio_priv(indio_dev); + int i, j = 0; + + mutex_lock(&hx711_data->lock); + + memset(&hx711_data->buffer, 0, sizeof(hx711_data->buffer)); + + iio_for_each_active_channel(indio_dev, i) { + hx711_data->buffer.channel[j] = + hx711_reset_read(hx711_data, &indio_dev->channels[i]); + j++; + } + + iio_push_to_buffers_with_timestamp(indio_dev, &hx711_data->buffer, + pf->timestamp); + + mutex_unlock(&hx711_data->lock); + + iio_trigger_notify_done(indio_dev->trig); + + return IRQ_HANDLED; +} + +static ssize_t hx711_scale_available_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct hx711_data *hx711_data = iio_priv(dev_to_iio_dev(dev)); + struct iio_dev_attr *iio_attr = to_iio_dev_attr(attr); + int channel = iio_attr->address; + int i, len = 0; + + for (i = 0; i < HX711_GAIN_MAX; i++) + if (hx711_gain_to_scale[i].channel == channel) + len += sprintf(buf + len, "0.%09d ", + hx711_data->gain_scale[i]); + + len += sprintf(buf + len, "\n"); + + return len; +} + +static IIO_DEVICE_ATTR(in_voltage0_scale_available, S_IRUGO, + hx711_scale_available_show, NULL, 0); + +static IIO_DEVICE_ATTR(in_voltage1_scale_available, S_IRUGO, + hx711_scale_available_show, NULL, 1); + +static struct attribute *hx711_attributes[] = { + &iio_dev_attr_in_voltage0_scale_available.dev_attr.attr, + &iio_dev_attr_in_voltage1_scale_available.dev_attr.attr, + NULL, +}; + +static const struct attribute_group hx711_attribute_group = { + .attrs = hx711_attributes, +}; + +static const struct iio_info hx711_iio_info = { + .read_raw = hx711_read_raw, + .write_raw = hx711_write_raw, + .write_raw_get_fmt = hx711_write_raw_get_fmt, + .attrs = &hx711_attribute_group, +}; + +static const struct iio_chan_spec hx711_chan_spec[] = { + { + .type = IIO_VOLTAGE, + .channel = 0, + .indexed = 1, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), + .scan_index = 0, + .scan_type = { + .sign = 'u', + .realbits = 24, + .storagebits = 32, + .endianness = IIO_CPU, + }, + }, + { + .type = IIO_VOLTAGE, + .channel = 1, + .indexed = 1, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), + .scan_index = 1, + .scan_type = { + .sign = 'u', + .realbits = 24, + .storagebits = 32, + .endianness = IIO_CPU, + }, + }, + IIO_CHAN_SOFT_TIMESTAMP(2), +}; + +static const struct hx711_chip_info hx711_chip = { + .name = "hx711", + .channels = hx711_chan_spec, + .iio_info = &hx711_iio_info, + .num_channels = ARRAY_SIZE(hx711_chan_spec), +}; + +static int hx711_probe(struct platform_device *pdev) +{ + const struct hx711_chip_info *chip_info; + struct device *dev = &pdev->dev; + struct hx711_data *hx711_data; + struct iio_dev *indio_dev; + int ret; + int i; + + indio_dev = devm_iio_device_alloc(dev, sizeof(struct hx711_data)); + if (!indio_dev) + return -ENOMEM; + + hx711_data = iio_priv(indio_dev); + hx711_data->dev = dev; + + mutex_init(&hx711_data->lock); + + chip_info = device_get_match_data(dev); + if (!chip_info) + return dev_err_probe(dev, -ENODEV, "missing driver data\n"); + + hx711_data->chip_info = chip_info; + + /* + * PD_SCK stands for power down and serial clock input of HX711 + * in the driver it is an output + */ + hx711_data->gpiod_pd_sck = devm_gpiod_get(dev, "sck", GPIOD_OUT_LOW); + if (IS_ERR(hx711_data->gpiod_pd_sck)) + return dev_err_probe(dev, PTR_ERR(hx711_data->gpiod_pd_sck), + "failed to get sck-gpiod\n"); + + /* + * DOUT stands for serial data output of HX711 + * for the driver it is an input + */ + hx711_data->gpiod_dout = devm_gpiod_get(dev, "dout", GPIOD_IN); + if (IS_ERR(hx711_data->gpiod_dout)) + return dev_err_probe(dev, PTR_ERR(hx711_data->gpiod_dout), + "failed to get dout-gpiod\n"); + + ret = devm_regulator_get_enable_read_voltage(dev, "avdd"); + if (ret < 0) + return ret; + + /* + * with + * full scale differential input range: AVDD / GAIN + * full scale output data: 2^24 + * we can say: + * AVDD / GAIN = 2^24 + * therefore: + * 1 LSB = AVDD / GAIN / 2^24 + * AVDD is in uV, but we need 10^-9 mV + * approximately to fit into a 32 bit number: + * 1 LSB = (AVDD * 100) / GAIN / 1678 [10^-9 mV] + */ + + /* we need 10^-9 mV */ + ret *= 100; + + for (i = 0; i < HX711_GAIN_MAX; i++) + hx711_data->gain_scale[i] = + ret / hx711_gain_to_scale[i].gain / 1678; + + hx711_data->gain_set = 128; + hx711_data->gain_chan_a = 128; + + hx711_data->clock_frequency = 400000; + ret = device_property_read_u32(&pdev->dev, "clock-frequency", + &hx711_data->clock_frequency); + + /* + * datasheet says the high level of PD_SCK has a maximum duration + * of 50 microseconds + */ + if (hx711_data->clock_frequency < 20000) { + dev_warn(dev, "clock-frequency too low - assuming 400 kHz\n"); + hx711_data->clock_frequency = 400000; + } + + hx711_data->data_ready_delay_ns = + 1000000000 / hx711_data->clock_frequency; + + indio_dev->name = chip_info->name; + indio_dev->info = chip_info->iio_info; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = chip_info->channels; + indio_dev->num_channels = chip_info->num_channels; + + ret = devm_iio_triggered_buffer_setup(dev, indio_dev, + iio_pollfunc_store_time, + hx711_trigger, NULL); + if (ret < 0) + return dev_err_probe(dev, ret, + "setup of iio triggered buffer failed\n"); + + ret = devm_iio_device_register(dev, indio_dev); + if (ret < 0) + return dev_err_probe(dev, ret, "Couldn't register the device\n"); + + return 0; +} + +static const struct of_device_id of_hx711_match[] = { + { .compatible = "avia,hx711", .data = &hx711_chip }, + { } +}; + +MODULE_DEVICE_TABLE(of, of_hx711_match); + +static struct platform_driver hx711_driver = { + .probe = hx711_probe, + .driver = { + .name = "hx711-gpio", + .of_match_table = of_hx711_match, + }, +}; + +module_platform_driver(hx711_driver); + +MODULE_AUTHOR("Andreas Klinger "); +MODULE_DESCRIPTION("HX711 bitbanging driver - ADC for weight cells"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:hx711-gpio"); + diff --git a/drivers/iio/adc/imx7d_adc.c b/drivers/iio/adc/imx7d_adc.c new file mode 100644 index 000000000..e9a509dc1 --- /dev/null +++ b/drivers/iio/adc/imx7d_adc.c @@ -0,0 +1,560 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Freescale i.MX7D ADC driver + * + * Copyright (C) 2015 Freescale Semiconductor, Inc. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +/* ADC register */ +#define IMX7D_REG_ADC_CH_A_CFG1 0x00 +#define IMX7D_REG_ADC_CH_A_CFG2 0x10 +#define IMX7D_REG_ADC_CH_B_CFG1 0x20 +#define IMX7D_REG_ADC_CH_B_CFG2 0x30 +#define IMX7D_REG_ADC_CH_C_CFG1 0x40 +#define IMX7D_REG_ADC_CH_C_CFG2 0x50 +#define IMX7D_REG_ADC_CH_D_CFG1 0x60 +#define IMX7D_REG_ADC_CH_D_CFG2 0x70 +#define IMX7D_REG_ADC_CH_SW_CFG 0x80 +#define IMX7D_REG_ADC_TIMER_UNIT 0x90 +#define IMX7D_REG_ADC_DMA_FIFO 0xa0 +#define IMX7D_REG_ADC_FIFO_STATUS 0xb0 +#define IMX7D_REG_ADC_INT_SIG_EN 0xc0 +#define IMX7D_REG_ADC_INT_EN 0xd0 +#define IMX7D_REG_ADC_INT_STATUS 0xe0 +#define IMX7D_REG_ADC_CHA_B_CNV_RSLT 0xf0 +#define IMX7D_REG_ADC_CHC_D_CNV_RSLT 0x100 +#define IMX7D_REG_ADC_CH_SW_CNV_RSLT 0x110 +#define IMX7D_REG_ADC_DMA_FIFO_DAT 0x120 +#define IMX7D_REG_ADC_ADC_CFG 0x130 + +#define IMX7D_REG_ADC_CHANNEL_CFG2_BASE 0x10 +#define IMX7D_EACH_CHANNEL_REG_OFFSET 0x20 + +#define IMX7D_REG_ADC_CH_CFG1_CHANNEL_EN (0x1 << 31) +#define IMX7D_REG_ADC_CH_CFG1_CHANNEL_SINGLE BIT(30) +#define IMX7D_REG_ADC_CH_CFG1_CHANNEL_AVG_EN BIT(29) +#define IMX7D_REG_ADC_CH_CFG1_CHANNEL_SEL(x) ((x) << 24) + +#define IMX7D_REG_ADC_CH_CFG2_AVG_NUM_4 (0x0 << 12) +#define IMX7D_REG_ADC_CH_CFG2_AVG_NUM_8 (0x1 << 12) +#define IMX7D_REG_ADC_CH_CFG2_AVG_NUM_16 (0x2 << 12) +#define IMX7D_REG_ADC_CH_CFG2_AVG_NUM_32 (0x3 << 12) + +#define IMX7D_REG_ADC_TIMER_UNIT_PRE_DIV_4 (0x0 << 29) +#define IMX7D_REG_ADC_TIMER_UNIT_PRE_DIV_8 (0x1 << 29) +#define IMX7D_REG_ADC_TIMER_UNIT_PRE_DIV_16 (0x2 << 29) +#define IMX7D_REG_ADC_TIMER_UNIT_PRE_DIV_32 (0x3 << 29) +#define IMX7D_REG_ADC_TIMER_UNIT_PRE_DIV_64 (0x4 << 29) +#define IMX7D_REG_ADC_TIMER_UNIT_PRE_DIV_128 (0x5 << 29) + +#define IMX7D_REG_ADC_ADC_CFG_ADC_CLK_DOWN BIT(31) +#define IMX7D_REG_ADC_ADC_CFG_ADC_POWER_DOWN BIT(1) +#define IMX7D_REG_ADC_ADC_CFG_ADC_EN BIT(0) + +#define IMX7D_REG_ADC_INT_CHA_COV_INT_EN BIT(8) +#define IMX7D_REG_ADC_INT_CHB_COV_INT_EN BIT(9) +#define IMX7D_REG_ADC_INT_CHC_COV_INT_EN BIT(10) +#define IMX7D_REG_ADC_INT_CHD_COV_INT_EN BIT(11) +#define IMX7D_REG_ADC_INT_CHANNEL_INT_EN \ + (IMX7D_REG_ADC_INT_CHA_COV_INT_EN | \ + IMX7D_REG_ADC_INT_CHB_COV_INT_EN | \ + IMX7D_REG_ADC_INT_CHC_COV_INT_EN | \ + IMX7D_REG_ADC_INT_CHD_COV_INT_EN) +#define IMX7D_REG_ADC_INT_STATUS_CHANNEL_INT_STATUS 0xf00 +#define IMX7D_REG_ADC_INT_STATUS_CHANNEL_CONV_TIME_OUT 0xf0000 + +#define IMX7D_ADC_TIMEOUT msecs_to_jiffies(100) +#define IMX7D_ADC_INPUT_CLK 24000000 + +enum imx7d_adc_clk_pre_div { + IMX7D_ADC_ANALOG_CLK_PRE_DIV_4, + IMX7D_ADC_ANALOG_CLK_PRE_DIV_8, + IMX7D_ADC_ANALOG_CLK_PRE_DIV_16, + IMX7D_ADC_ANALOG_CLK_PRE_DIV_32, + IMX7D_ADC_ANALOG_CLK_PRE_DIV_64, + IMX7D_ADC_ANALOG_CLK_PRE_DIV_128, +}; + +enum imx7d_adc_average_num { + IMX7D_ADC_AVERAGE_NUM_4, + IMX7D_ADC_AVERAGE_NUM_8, + IMX7D_ADC_AVERAGE_NUM_16, + IMX7D_ADC_AVERAGE_NUM_32, +}; + +struct imx7d_adc_feature { + enum imx7d_adc_clk_pre_div clk_pre_div; + enum imx7d_adc_average_num avg_num; + + u32 core_time_unit; /* impact the sample rate */ +}; + +struct imx7d_adc { + struct device *dev; + void __iomem *regs; + struct clk *clk; + /* lock to protect against multiple access to the device */ + struct mutex lock; + u32 vref_uv; + u32 value; + u32 channel; + u32 pre_div_num; + + struct regulator *vref; + struct imx7d_adc_feature adc_feature; + + struct completion completion; +}; + +struct imx7d_adc_analogue_core_clk { + u32 pre_div; + u32 reg_config; +}; + +#define IMX7D_ADC_ANALOGUE_CLK_CONFIG(_pre_div, _reg_conf) { \ + .pre_div = (_pre_div), \ + .reg_config = (_reg_conf), \ +} + +static const struct imx7d_adc_analogue_core_clk imx7d_adc_analogue_clk[] = { + IMX7D_ADC_ANALOGUE_CLK_CONFIG(4, IMX7D_REG_ADC_TIMER_UNIT_PRE_DIV_4), + IMX7D_ADC_ANALOGUE_CLK_CONFIG(8, IMX7D_REG_ADC_TIMER_UNIT_PRE_DIV_8), + IMX7D_ADC_ANALOGUE_CLK_CONFIG(16, IMX7D_REG_ADC_TIMER_UNIT_PRE_DIV_16), + IMX7D_ADC_ANALOGUE_CLK_CONFIG(32, IMX7D_REG_ADC_TIMER_UNIT_PRE_DIV_32), + IMX7D_ADC_ANALOGUE_CLK_CONFIG(64, IMX7D_REG_ADC_TIMER_UNIT_PRE_DIV_64), + IMX7D_ADC_ANALOGUE_CLK_CONFIG(128, IMX7D_REG_ADC_TIMER_UNIT_PRE_DIV_128), +}; + +#define IMX7D_ADC_CHAN(_idx) { \ + .type = IIO_VOLTAGE, \ + .indexed = 1, \ + .channel = (_idx), \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | \ + BIT(IIO_CHAN_INFO_SAMP_FREQ), \ +} + +static const struct iio_chan_spec imx7d_adc_iio_channels[] = { + IMX7D_ADC_CHAN(0), + IMX7D_ADC_CHAN(1), + IMX7D_ADC_CHAN(2), + IMX7D_ADC_CHAN(3), + IMX7D_ADC_CHAN(4), + IMX7D_ADC_CHAN(5), + IMX7D_ADC_CHAN(6), + IMX7D_ADC_CHAN(7), + IMX7D_ADC_CHAN(8), + IMX7D_ADC_CHAN(9), + IMX7D_ADC_CHAN(10), + IMX7D_ADC_CHAN(11), + IMX7D_ADC_CHAN(12), + IMX7D_ADC_CHAN(13), + IMX7D_ADC_CHAN(14), + IMX7D_ADC_CHAN(15), +}; + +static const u32 imx7d_adc_average_num[] = { + IMX7D_REG_ADC_CH_CFG2_AVG_NUM_4, + IMX7D_REG_ADC_CH_CFG2_AVG_NUM_8, + IMX7D_REG_ADC_CH_CFG2_AVG_NUM_16, + IMX7D_REG_ADC_CH_CFG2_AVG_NUM_32, +}; + +static void imx7d_adc_feature_config(struct imx7d_adc *info) +{ + info->adc_feature.clk_pre_div = IMX7D_ADC_ANALOG_CLK_PRE_DIV_4; + info->adc_feature.avg_num = IMX7D_ADC_AVERAGE_NUM_32; + info->adc_feature.core_time_unit = 1; +} + +static void imx7d_adc_sample_rate_set(struct imx7d_adc *info) +{ + struct imx7d_adc_feature *adc_feature = &info->adc_feature; + struct imx7d_adc_analogue_core_clk adc_analogure_clk; + u32 i; + u32 tmp_cfg1; + u32 sample_rate = 0; + + /* + * Before sample set, disable channel A,B,C,D. Here we + * clear the bit 31 of register REG_ADC_CH_A\B\C\D_CFG1. + */ + for (i = 0; i < 4; i++) { + tmp_cfg1 = + readl(info->regs + i * IMX7D_EACH_CHANNEL_REG_OFFSET); + tmp_cfg1 &= ~IMX7D_REG_ADC_CH_CFG1_CHANNEL_EN; + writel(tmp_cfg1, + info->regs + i * IMX7D_EACH_CHANNEL_REG_OFFSET); + } + + adc_analogure_clk = imx7d_adc_analogue_clk[adc_feature->clk_pre_div]; + sample_rate |= adc_analogure_clk.reg_config; + info->pre_div_num = adc_analogure_clk.pre_div; + + sample_rate |= adc_feature->core_time_unit; + writel(sample_rate, info->regs + IMX7D_REG_ADC_TIMER_UNIT); +} + +static void imx7d_adc_hw_init(struct imx7d_adc *info) +{ + u32 cfg; + + /* power up and enable adc analogue core */ + cfg = readl(info->regs + IMX7D_REG_ADC_ADC_CFG); + cfg &= ~(IMX7D_REG_ADC_ADC_CFG_ADC_CLK_DOWN | + IMX7D_REG_ADC_ADC_CFG_ADC_POWER_DOWN); + cfg |= IMX7D_REG_ADC_ADC_CFG_ADC_EN; + writel(cfg, info->regs + IMX7D_REG_ADC_ADC_CFG); + + /* enable channel A,B,C,D interrupt */ + writel(IMX7D_REG_ADC_INT_CHANNEL_INT_EN, + info->regs + IMX7D_REG_ADC_INT_SIG_EN); + writel(IMX7D_REG_ADC_INT_CHANNEL_INT_EN, + info->regs + IMX7D_REG_ADC_INT_EN); + + imx7d_adc_sample_rate_set(info); +} + +static void imx7d_adc_channel_set(struct imx7d_adc *info) +{ + u32 cfg1 = 0; + u32 cfg2; + u32 channel; + + channel = info->channel; + + /* the channel choose single conversion, and enable average mode */ + cfg1 |= (IMX7D_REG_ADC_CH_CFG1_CHANNEL_EN | + IMX7D_REG_ADC_CH_CFG1_CHANNEL_SINGLE | + IMX7D_REG_ADC_CH_CFG1_CHANNEL_AVG_EN); + + /* + * physical channel 0 chose logical channel A + * physical channel 1 chose logical channel B + * physical channel 2 chose logical channel C + * physical channel 3 chose logical channel D + */ + cfg1 |= IMX7D_REG_ADC_CH_CFG1_CHANNEL_SEL(channel); + + /* + * read register REG_ADC_CH_A\B\C\D_CFG2, according to the + * channel chosen + */ + cfg2 = readl(info->regs + IMX7D_EACH_CHANNEL_REG_OFFSET * channel + + IMX7D_REG_ADC_CHANNEL_CFG2_BASE); + + cfg2 |= imx7d_adc_average_num[info->adc_feature.avg_num]; + + /* + * write the register REG_ADC_CH_A\B\C\D_CFG2, according to + * the channel chosen + */ + writel(cfg2, info->regs + IMX7D_EACH_CHANNEL_REG_OFFSET * channel + + IMX7D_REG_ADC_CHANNEL_CFG2_BASE); + writel(cfg1, info->regs + IMX7D_EACH_CHANNEL_REG_OFFSET * channel); +} + +static u32 imx7d_adc_get_sample_rate(struct imx7d_adc *info) +{ + u32 analogue_core_clk; + u32 core_time_unit = info->adc_feature.core_time_unit; + u32 tmp; + + analogue_core_clk = IMX7D_ADC_INPUT_CLK / info->pre_div_num; + tmp = (core_time_unit + 1) * 6; + + return analogue_core_clk / tmp; +} + +static int imx7d_adc_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, + int *val2, + long mask) +{ + struct imx7d_adc *info = iio_priv(indio_dev); + + u32 channel; + long ret; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + mutex_lock(&info->lock); + reinit_completion(&info->completion); + + channel = chan->channel & 0x03; + info->channel = channel; + imx7d_adc_channel_set(info); + + ret = wait_for_completion_interruptible_timeout + (&info->completion, IMX7D_ADC_TIMEOUT); + if (ret == 0) { + mutex_unlock(&info->lock); + return -ETIMEDOUT; + } + if (ret < 0) { + mutex_unlock(&info->lock); + return ret; + } + + *val = info->value; + mutex_unlock(&info->lock); + return IIO_VAL_INT; + + case IIO_CHAN_INFO_SCALE: + info->vref_uv = regulator_get_voltage(info->vref); + *val = info->vref_uv / 1000; + *val2 = 12; + return IIO_VAL_FRACTIONAL_LOG2; + + case IIO_CHAN_INFO_SAMP_FREQ: + *val = imx7d_adc_get_sample_rate(info); + return IIO_VAL_INT; + + default: + return -EINVAL; + } +} + +static int imx7d_adc_read_data(struct imx7d_adc *info) +{ + u32 channel; + u32 value; + + channel = info->channel & 0x03; + + /* + * channel A and B conversion result share one register, + * bit[27~16] is the channel B conversion result, + * bit[11~0] is the channel A conversion result. + * channel C and D is the same. + */ + if (channel < 2) + value = readl(info->regs + IMX7D_REG_ADC_CHA_B_CNV_RSLT); + else + value = readl(info->regs + IMX7D_REG_ADC_CHC_D_CNV_RSLT); + if (channel & 0x1) /* channel B or D */ + value = (value >> 16) & 0xFFF; + else /* channel A or C */ + value &= 0xFFF; + + return value; +} + +static irqreturn_t imx7d_adc_isr(int irq, void *dev_id) +{ + struct imx7d_adc *info = dev_id; + int status; + + status = readl(info->regs + IMX7D_REG_ADC_INT_STATUS); + if (status & IMX7D_REG_ADC_INT_STATUS_CHANNEL_INT_STATUS) { + info->value = imx7d_adc_read_data(info); + complete(&info->completion); + + /* + * The register IMX7D_REG_ADC_INT_STATUS can't clear + * itself after read operation, need software to write + * 0 to the related bit. Here we clear the channel A/B/C/D + * conversion finished flag. + */ + status &= ~IMX7D_REG_ADC_INT_STATUS_CHANNEL_INT_STATUS; + writel(status, info->regs + IMX7D_REG_ADC_INT_STATUS); + } + + /* + * If the channel A/B/C/D conversion timeout, report it and clear these + * timeout flags. + */ + if (status & IMX7D_REG_ADC_INT_STATUS_CHANNEL_CONV_TIME_OUT) { + dev_err(info->dev, + "ADC got conversion time out interrupt: 0x%08x\n", + status); + status &= ~IMX7D_REG_ADC_INT_STATUS_CHANNEL_CONV_TIME_OUT; + writel(status, info->regs + IMX7D_REG_ADC_INT_STATUS); + } + + return IRQ_HANDLED; +} + +static int imx7d_adc_reg_access(struct iio_dev *indio_dev, + unsigned reg, unsigned writeval, + unsigned *readval) +{ + struct imx7d_adc *info = iio_priv(indio_dev); + + if (!readval || reg % 4 || reg > IMX7D_REG_ADC_ADC_CFG) + return -EINVAL; + + *readval = readl(info->regs + reg); + + return 0; +} + +static const struct iio_info imx7d_adc_iio_info = { + .read_raw = &imx7d_adc_read_raw, + .debugfs_reg_access = &imx7d_adc_reg_access, +}; + +static const struct of_device_id imx7d_adc_match[] = { + { .compatible = "fsl,imx7d-adc", }, + { } +}; +MODULE_DEVICE_TABLE(of, imx7d_adc_match); + +static void imx7d_adc_power_down(struct imx7d_adc *info) +{ + u32 adc_cfg; + + adc_cfg = readl(info->regs + IMX7D_REG_ADC_ADC_CFG); + adc_cfg |= IMX7D_REG_ADC_ADC_CFG_ADC_CLK_DOWN | + IMX7D_REG_ADC_ADC_CFG_ADC_POWER_DOWN; + adc_cfg &= ~IMX7D_REG_ADC_ADC_CFG_ADC_EN; + writel(adc_cfg, info->regs + IMX7D_REG_ADC_ADC_CFG); +} + +static int imx7d_adc_enable(struct device *dev) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct imx7d_adc *info = iio_priv(indio_dev); + int ret; + + ret = regulator_enable(info->vref); + if (ret) { + dev_err(info->dev, + "Can't enable adc reference top voltage, err = %d\n", + ret); + return ret; + } + + ret = clk_prepare_enable(info->clk); + if (ret) { + dev_err(info->dev, + "Could not prepare or enable clock.\n"); + regulator_disable(info->vref); + return ret; + } + + imx7d_adc_hw_init(info); + + return 0; +} + +static int imx7d_adc_disable(struct device *dev) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct imx7d_adc *info = iio_priv(indio_dev); + + imx7d_adc_power_down(info); + + clk_disable_unprepare(info->clk); + regulator_disable(info->vref); + + return 0; +} + +static void __imx7d_adc_disable(void *data) +{ + imx7d_adc_disable(data); +} + +static int imx7d_adc_probe(struct platform_device *pdev) +{ + struct imx7d_adc *info; + struct iio_dev *indio_dev; + struct device *dev = &pdev->dev; + int irq; + int ret; + + indio_dev = devm_iio_device_alloc(dev, sizeof(*info)); + if (!indio_dev) + return -ENOMEM; + + info = iio_priv(indio_dev); + info->dev = dev; + + info->regs = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(info->regs)) + return PTR_ERR(info->regs); + + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return irq; + + info->clk = devm_clk_get(dev, "adc"); + if (IS_ERR(info->clk)) + return dev_err_probe(dev, PTR_ERR(info->clk), "Failed getting clock\n"); + + info->vref = devm_regulator_get(dev, "vref"); + if (IS_ERR(info->vref)) + return dev_err_probe(dev, PTR_ERR(info->vref), + "Failed getting reference voltage\n"); + + platform_set_drvdata(pdev, indio_dev); + + init_completion(&info->completion); + + indio_dev->name = dev_name(dev); + indio_dev->info = &imx7d_adc_iio_info; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = imx7d_adc_iio_channels; + indio_dev->num_channels = ARRAY_SIZE(imx7d_adc_iio_channels); + + ret = devm_request_irq(dev, irq, imx7d_adc_isr, 0, dev_name(dev), info); + if (ret < 0) { + dev_err(dev, "Failed requesting irq, irq = %d\n", irq); + return ret; + } + + imx7d_adc_feature_config(info); + + ret = imx7d_adc_enable(dev); + if (ret) + return ret; + + ret = devm_add_action_or_reset(dev, __imx7d_adc_disable, dev); + if (ret) + return ret; + + mutex_init(&info->lock); + + ret = devm_iio_device_register(dev, indio_dev); + if (ret) { + dev_err(&pdev->dev, "Couldn't register the device.\n"); + return ret; + } + + return 0; +} + +static DEFINE_SIMPLE_DEV_PM_OPS(imx7d_adc_pm_ops, imx7d_adc_disable, + imx7d_adc_enable); + +static struct platform_driver imx7d_adc_driver = { + .probe = imx7d_adc_probe, + .driver = { + .name = "imx7d_adc", + .of_match_table = imx7d_adc_match, + .pm = pm_sleep_ptr(&imx7d_adc_pm_ops), + }, +}; + +module_platform_driver(imx7d_adc_driver); + +MODULE_AUTHOR("Haibo Chen "); +MODULE_DESCRIPTION("Freescale IMX7D ADC driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/adc/imx8qxp-adc.c b/drivers/iio/adc/imx8qxp-adc.c new file mode 100644 index 000000000..d7cc97743 --- /dev/null +++ b/drivers/iio/adc/imx8qxp-adc.c @@ -0,0 +1,496 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * NXP i.MX8QXP ADC driver + * + * Based on the work of Haibo Chen + * The initial developer of the original code is Haibo Chen. + * Portions created by Haibo Chen are Copyright (C) 2018 NXP. + * All Rights Reserved. + * + * Copyright (C) 2018 NXP + * Copyright (C) 2021 Cai Huoqing + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define ADC_DRIVER_NAME "imx8qxp-adc" + +/* Register map definition */ +#define IMX8QXP_ADR_ADC_CTRL 0x10 +#define IMX8QXP_ADR_ADC_STAT 0x14 +#define IMX8QXP_ADR_ADC_IE 0x18 +#define IMX8QXP_ADR_ADC_DE 0x1c +#define IMX8QXP_ADR_ADC_CFG 0x20 +#define IMX8QXP_ADR_ADC_FCTRL 0x30 +#define IMX8QXP_ADR_ADC_SWTRIG 0x34 +#define IMX8QXP_ADR_ADC_TCTRL(tid) (0xc0 + (tid) * 4) +#define IMX8QXP_ADR_ADC_CMDL(cid) (0x100 + (cid) * 8) +#define IMX8QXP_ADR_ADC_CMDH(cid) (0x104 + (cid) * 8) +#define IMX8QXP_ADR_ADC_RESFIFO 0x300 +#define IMX8QXP_ADR_ADC_TST 0xffc + +/* ADC bit shift */ +#define IMX8QXP_ADC_IE_FWMIE_MASK GENMASK(1, 0) +#define IMX8QXP_ADC_CTRL_FIFO_RESET_MASK BIT(8) +#define IMX8QXP_ADC_CTRL_SOFTWARE_RESET_MASK BIT(1) +#define IMX8QXP_ADC_CTRL_ADC_EN_MASK BIT(0) +#define IMX8QXP_ADC_TCTRL_TCMD_MASK GENMASK(31, 24) +#define IMX8QXP_ADC_TCTRL_TDLY_MASK GENMASK(23, 16) +#define IMX8QXP_ADC_TCTRL_TPRI_MASK GENMASK(15, 8) +#define IMX8QXP_ADC_TCTRL_HTEN_MASK GENMASK(7, 0) +#define IMX8QXP_ADC_CMDL_CSCALE_MASK GENMASK(13, 8) +#define IMX8QXP_ADC_CMDL_MODE_MASK BIT(7) +#define IMX8QXP_ADC_CMDL_DIFF_MASK BIT(6) +#define IMX8QXP_ADC_CMDL_ABSEL_MASK BIT(5) +#define IMX8QXP_ADC_CMDL_ADCH_MASK GENMASK(2, 0) +#define IMX8QXP_ADC_CMDH_NEXT_MASK GENMASK(31, 24) +#define IMX8QXP_ADC_CMDH_LOOP_MASK GENMASK(23, 16) +#define IMX8QXP_ADC_CMDH_AVGS_MASK GENMASK(15, 12) +#define IMX8QXP_ADC_CMDH_STS_MASK BIT(8) +#define IMX8QXP_ADC_CMDH_LWI_MASK GENMASK(7, 7) +#define IMX8QXP_ADC_CMDH_CMPEN_MASK GENMASK(0, 0) +#define IMX8QXP_ADC_CFG_PWREN_MASK BIT(28) +#define IMX8QXP_ADC_CFG_PUDLY_MASK GENMASK(23, 16) +#define IMX8QXP_ADC_CFG_REFSEL_MASK GENMASK(7, 6) +#define IMX8QXP_ADC_CFG_PWRSEL_MASK GENMASK(5, 4) +#define IMX8QXP_ADC_CFG_TPRICTRL_MASK GENMASK(3, 0) +#define IMX8QXP_ADC_FCTRL_FWMARK_MASK GENMASK(20, 16) +#define IMX8QXP_ADC_FCTRL_FCOUNT_MASK GENMASK(4, 0) +#define IMX8QXP_ADC_RESFIFO_VAL_MASK GENMASK(18, 3) + +/* ADC PARAMETER*/ +#define IMX8QXP_ADC_CMDL_CHANNEL_SCALE_FULL GENMASK(5, 0) +#define IMX8QXP_ADC_CMDL_SEL_A_A_B_CHANNEL 0 +#define IMX8QXP_ADC_CMDL_STANDARD_RESOLUTION 0 +#define IMX8QXP_ADC_CMDL_MODE_SINGLE 0 +#define IMX8QXP_ADC_CMDH_LWI_INCREMENT_DIS 0 +#define IMX8QXP_ADC_CMDH_CMPEN_DIS 0 +#define IMX8QXP_ADC_PAUSE_EN BIT(31) +#define IMX8QXP_ADC_TCTRL_TPRI_PRIORITY_HIGH 0 + +#define IMX8QXP_ADC_TCTRL_HTEN_HW_TIRG_DIS 0 + +#define IMX8QXP_ADC_TIMEOUT msecs_to_jiffies(100) + +#define IMX8QXP_ADC_MAX_FIFO_SIZE 16 + +struct imx8qxp_adc { + struct device *dev; + void __iomem *regs; + struct clk *clk; + struct clk *ipg_clk; + struct regulator *vref; + /* Serialise ADC channel reads */ + struct mutex lock; + struct completion completion; + u32 fifo[IMX8QXP_ADC_MAX_FIFO_SIZE]; +}; + +#define IMX8QXP_ADC_CHAN(_idx) { \ + .type = IIO_VOLTAGE, \ + .indexed = 1, \ + .channel = (_idx), \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | \ + BIT(IIO_CHAN_INFO_SAMP_FREQ), \ +} + +static const struct iio_chan_spec imx8qxp_adc_iio_channels[] = { + IMX8QXP_ADC_CHAN(0), + IMX8QXP_ADC_CHAN(1), + IMX8QXP_ADC_CHAN(2), + IMX8QXP_ADC_CHAN(3), + IMX8QXP_ADC_CHAN(4), + IMX8QXP_ADC_CHAN(5), + IMX8QXP_ADC_CHAN(6), + IMX8QXP_ADC_CHAN(7), +}; + +static void imx8qxp_adc_reset(struct imx8qxp_adc *adc) +{ + u32 ctrl; + + /*software reset, need to clear the set bit*/ + ctrl = readl(adc->regs + IMX8QXP_ADR_ADC_CTRL); + ctrl |= FIELD_PREP(IMX8QXP_ADC_CTRL_SOFTWARE_RESET_MASK, 1); + writel(ctrl, adc->regs + IMX8QXP_ADR_ADC_CTRL); + udelay(10); + ctrl &= ~FIELD_PREP(IMX8QXP_ADC_CTRL_SOFTWARE_RESET_MASK, 1); + writel(ctrl, adc->regs + IMX8QXP_ADR_ADC_CTRL); + + /* reset the fifo */ + ctrl |= FIELD_PREP(IMX8QXP_ADC_CTRL_FIFO_RESET_MASK, 1); + writel(ctrl, adc->regs + IMX8QXP_ADR_ADC_CTRL); +} + +static void imx8qxp_adc_reg_config(struct imx8qxp_adc *adc, int channel) +{ + u32 adc_cfg, adc_tctrl, adc_cmdl, adc_cmdh; + + /* ADC configuration */ + adc_cfg = FIELD_PREP(IMX8QXP_ADC_CFG_PWREN_MASK, 1) | + FIELD_PREP(IMX8QXP_ADC_CFG_PUDLY_MASK, 0x80)| + FIELD_PREP(IMX8QXP_ADC_CFG_REFSEL_MASK, 0) | + FIELD_PREP(IMX8QXP_ADC_CFG_PWRSEL_MASK, 3) | + FIELD_PREP(IMX8QXP_ADC_CFG_TPRICTRL_MASK, 0); + writel(adc_cfg, adc->regs + IMX8QXP_ADR_ADC_CFG); + + /* config the trigger control */ + adc_tctrl = FIELD_PREP(IMX8QXP_ADC_TCTRL_TCMD_MASK, 1) | + FIELD_PREP(IMX8QXP_ADC_TCTRL_TDLY_MASK, 0) | + FIELD_PREP(IMX8QXP_ADC_TCTRL_TPRI_MASK, IMX8QXP_ADC_TCTRL_TPRI_PRIORITY_HIGH) | + FIELD_PREP(IMX8QXP_ADC_TCTRL_HTEN_MASK, IMX8QXP_ADC_TCTRL_HTEN_HW_TIRG_DIS); + writel(adc_tctrl, adc->regs + IMX8QXP_ADR_ADC_TCTRL(0)); + + /* config the cmd */ + adc_cmdl = FIELD_PREP(IMX8QXP_ADC_CMDL_CSCALE_MASK, IMX8QXP_ADC_CMDL_CHANNEL_SCALE_FULL) | + FIELD_PREP(IMX8QXP_ADC_CMDL_MODE_MASK, IMX8QXP_ADC_CMDL_STANDARD_RESOLUTION) | + FIELD_PREP(IMX8QXP_ADC_CMDL_DIFF_MASK, IMX8QXP_ADC_CMDL_MODE_SINGLE) | + FIELD_PREP(IMX8QXP_ADC_CMDL_ABSEL_MASK, IMX8QXP_ADC_CMDL_SEL_A_A_B_CHANNEL) | + FIELD_PREP(IMX8QXP_ADC_CMDL_ADCH_MASK, channel); + writel(adc_cmdl, adc->regs + IMX8QXP_ADR_ADC_CMDL(0)); + + adc_cmdh = FIELD_PREP(IMX8QXP_ADC_CMDH_NEXT_MASK, 0) | + FIELD_PREP(IMX8QXP_ADC_CMDH_LOOP_MASK, 0) | + FIELD_PREP(IMX8QXP_ADC_CMDH_AVGS_MASK, 7) | + FIELD_PREP(IMX8QXP_ADC_CMDH_STS_MASK, 0) | + FIELD_PREP(IMX8QXP_ADC_CMDH_LWI_MASK, IMX8QXP_ADC_CMDH_LWI_INCREMENT_DIS) | + FIELD_PREP(IMX8QXP_ADC_CMDH_CMPEN_MASK, IMX8QXP_ADC_CMDH_CMPEN_DIS); + writel(adc_cmdh, adc->regs + IMX8QXP_ADR_ADC_CMDH(0)); +} + +static void imx8qxp_adc_fifo_config(struct imx8qxp_adc *adc) +{ + u32 fifo_ctrl, interrupt_en; + + fifo_ctrl = readl(adc->regs + IMX8QXP_ADR_ADC_FCTRL); + fifo_ctrl &= ~IMX8QXP_ADC_FCTRL_FWMARK_MASK; + /* set the watermark level to 1 */ + fifo_ctrl |= FIELD_PREP(IMX8QXP_ADC_FCTRL_FWMARK_MASK, 0); + writel(fifo_ctrl, adc->regs + IMX8QXP_ADR_ADC_FCTRL); + + /* FIFO Watermark Interrupt Enable */ + interrupt_en = readl(adc->regs + IMX8QXP_ADR_ADC_IE); + interrupt_en |= FIELD_PREP(IMX8QXP_ADC_IE_FWMIE_MASK, 1); + writel(interrupt_en, adc->regs + IMX8QXP_ADR_ADC_IE); +} + +static void imx8qxp_adc_disable(struct imx8qxp_adc *adc) +{ + u32 ctrl; + + ctrl = readl(adc->regs + IMX8QXP_ADR_ADC_CTRL); + ctrl &= ~FIELD_PREP(IMX8QXP_ADC_CTRL_ADC_EN_MASK, 1); + writel(ctrl, adc->regs + IMX8QXP_ADR_ADC_CTRL); +} + +static int imx8qxp_adc_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct imx8qxp_adc *adc = iio_priv(indio_dev); + struct device *dev = adc->dev; + + u32 ctrl; + long ret; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + pm_runtime_get_sync(dev); + + mutex_lock(&adc->lock); + reinit_completion(&adc->completion); + + imx8qxp_adc_reg_config(adc, chan->channel); + + imx8qxp_adc_fifo_config(adc); + + /* adc enable */ + ctrl = readl(adc->regs + IMX8QXP_ADR_ADC_CTRL); + ctrl |= FIELD_PREP(IMX8QXP_ADC_CTRL_ADC_EN_MASK, 1); + writel(ctrl, adc->regs + IMX8QXP_ADR_ADC_CTRL); + /* adc start */ + writel(1, adc->regs + IMX8QXP_ADR_ADC_SWTRIG); + + ret = wait_for_completion_interruptible_timeout(&adc->completion, + IMX8QXP_ADC_TIMEOUT); + + pm_runtime_put_sync_autosuspend(dev); + + if (ret == 0) { + mutex_unlock(&adc->lock); + return -ETIMEDOUT; + } + if (ret < 0) { + mutex_unlock(&adc->lock); + return ret; + } + + *val = adc->fifo[0]; + + mutex_unlock(&adc->lock); + return IIO_VAL_INT; + + case IIO_CHAN_INFO_SCALE: + ret = regulator_get_voltage(adc->vref); + if (ret < 0) + return ret; + *val = ret / 1000; + *val2 = 12; + return IIO_VAL_FRACTIONAL_LOG2; + + case IIO_CHAN_INFO_SAMP_FREQ: + *val = clk_get_rate(adc->clk) / 3; + return IIO_VAL_INT; + + default: + return -EINVAL; + } +} + +static irqreturn_t imx8qxp_adc_isr(int irq, void *dev_id) +{ + struct imx8qxp_adc *adc = dev_id; + u32 fifo_count; + int i; + + fifo_count = FIELD_GET(IMX8QXP_ADC_FCTRL_FCOUNT_MASK, + readl(adc->regs + IMX8QXP_ADR_ADC_FCTRL)); + + for (i = 0; i < fifo_count; i++) + adc->fifo[i] = FIELD_GET(IMX8QXP_ADC_RESFIFO_VAL_MASK, + readl_relaxed(adc->regs + IMX8QXP_ADR_ADC_RESFIFO)); + + if (fifo_count) + complete(&adc->completion); + + return IRQ_HANDLED; +} + +static int imx8qxp_adc_reg_access(struct iio_dev *indio_dev, unsigned int reg, + unsigned int writeval, unsigned int *readval) +{ + struct imx8qxp_adc *adc = iio_priv(indio_dev); + struct device *dev = adc->dev; + + if (!readval || reg % 4 || reg > IMX8QXP_ADR_ADC_TST) + return -EINVAL; + + pm_runtime_get_sync(dev); + + *readval = readl(adc->regs + reg); + + pm_runtime_put_sync_autosuspend(dev); + + return 0; +} + +static const struct iio_info imx8qxp_adc_iio_info = { + .read_raw = &imx8qxp_adc_read_raw, + .debugfs_reg_access = &imx8qxp_adc_reg_access, +}; + +static int imx8qxp_adc_probe(struct platform_device *pdev) +{ + struct imx8qxp_adc *adc; + struct iio_dev *indio_dev; + struct device *dev = &pdev->dev; + int irq; + int ret; + + indio_dev = devm_iio_device_alloc(dev, sizeof(*adc)); + if (!indio_dev) + return -ENOMEM; + + adc = iio_priv(indio_dev); + adc->dev = dev; + + mutex_init(&adc->lock); + adc->regs = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(adc->regs)) + return PTR_ERR(adc->regs); + + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return irq; + + adc->clk = devm_clk_get(dev, "per"); + if (IS_ERR(adc->clk)) + return dev_err_probe(dev, PTR_ERR(adc->clk), "Failed getting clock\n"); + + adc->ipg_clk = devm_clk_get(dev, "ipg"); + if (IS_ERR(adc->ipg_clk)) + return dev_err_probe(dev, PTR_ERR(adc->ipg_clk), "Failed getting clock\n"); + + adc->vref = devm_regulator_get(dev, "vref"); + if (IS_ERR(adc->vref)) + return dev_err_probe(dev, PTR_ERR(adc->vref), "Failed getting reference voltage\n"); + + ret = regulator_enable(adc->vref); + if (ret) { + dev_err(dev, "Can't enable adc reference top voltage\n"); + return ret; + } + + platform_set_drvdata(pdev, indio_dev); + + init_completion(&adc->completion); + + indio_dev->name = ADC_DRIVER_NAME; + indio_dev->info = &imx8qxp_adc_iio_info; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = imx8qxp_adc_iio_channels; + indio_dev->num_channels = ARRAY_SIZE(imx8qxp_adc_iio_channels); + + ret = clk_prepare_enable(adc->clk); + if (ret) { + dev_err(&pdev->dev, "Could not prepare or enable the clock.\n"); + goto error_regulator_disable; + } + + ret = clk_prepare_enable(adc->ipg_clk); + if (ret) { + dev_err(&pdev->dev, "Could not prepare or enable the clock.\n"); + goto error_adc_clk_disable; + } + + ret = devm_request_irq(dev, irq, imx8qxp_adc_isr, 0, ADC_DRIVER_NAME, adc); + if (ret < 0) { + dev_err(dev, "Failed requesting irq, irq = %d\n", irq); + goto error_ipg_clk_disable; + } + + imx8qxp_adc_reset(adc); + + ret = iio_device_register(indio_dev); + if (ret) { + imx8qxp_adc_disable(adc); + dev_err(dev, "Couldn't register the device.\n"); + goto error_ipg_clk_disable; + } + + pm_runtime_set_active(dev); + pm_runtime_set_autosuspend_delay(dev, 50); + pm_runtime_use_autosuspend(dev); + pm_runtime_enable(dev); + + return 0; + +error_ipg_clk_disable: + clk_disable_unprepare(adc->ipg_clk); +error_adc_clk_disable: + clk_disable_unprepare(adc->clk); +error_regulator_disable: + regulator_disable(adc->vref); + + return ret; +} + +static void imx8qxp_adc_remove(struct platform_device *pdev) +{ + struct iio_dev *indio_dev = platform_get_drvdata(pdev); + struct imx8qxp_adc *adc = iio_priv(indio_dev); + struct device *dev = adc->dev; + + pm_runtime_get_sync(dev); + + iio_device_unregister(indio_dev); + + imx8qxp_adc_disable(adc); + + clk_disable_unprepare(adc->clk); + clk_disable_unprepare(adc->ipg_clk); + regulator_disable(adc->vref); + + pm_runtime_disable(dev); + pm_runtime_put_noidle(dev); +} + +static int imx8qxp_adc_runtime_suspend(struct device *dev) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct imx8qxp_adc *adc = iio_priv(indio_dev); + + imx8qxp_adc_disable(adc); + + clk_disable_unprepare(adc->clk); + clk_disable_unprepare(adc->ipg_clk); + regulator_disable(adc->vref); + + return 0; +} + +static int imx8qxp_adc_runtime_resume(struct device *dev) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct imx8qxp_adc *adc = iio_priv(indio_dev); + int ret; + + ret = regulator_enable(adc->vref); + if (ret) { + dev_err(dev, "Can't enable adc reference top voltage, err = %d\n", ret); + return ret; + } + + ret = clk_prepare_enable(adc->clk); + if (ret) { + dev_err(dev, "Could not prepare or enable clock.\n"); + goto err_disable_reg; + } + + ret = clk_prepare_enable(adc->ipg_clk); + if (ret) { + dev_err(dev, "Could not prepare or enable clock.\n"); + goto err_unprepare_clk; + } + + imx8qxp_adc_reset(adc); + + return 0; + +err_unprepare_clk: + clk_disable_unprepare(adc->clk); + +err_disable_reg: + regulator_disable(adc->vref); + + return ret; +} + +static DEFINE_RUNTIME_DEV_PM_OPS(imx8qxp_adc_pm_ops, + imx8qxp_adc_runtime_suspend, + imx8qxp_adc_runtime_resume, NULL); + +static const struct of_device_id imx8qxp_adc_match[] = { + { .compatible = "nxp,imx8qxp-adc", }, + { } +}; +MODULE_DEVICE_TABLE(of, imx8qxp_adc_match); + +static struct platform_driver imx8qxp_adc_driver = { + .probe = imx8qxp_adc_probe, + .remove = imx8qxp_adc_remove, + .driver = { + .name = ADC_DRIVER_NAME, + .of_match_table = imx8qxp_adc_match, + .pm = pm_ptr(&imx8qxp_adc_pm_ops), + }, +}; + +module_platform_driver(imx8qxp_adc_driver); + +MODULE_DESCRIPTION("i.MX8QuadXPlus ADC driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/adc/imx93_adc.c b/drivers/iio/adc/imx93_adc.c new file mode 100644 index 000000000..797adaf37 --- /dev/null +++ b/drivers/iio/adc/imx93_adc.c @@ -0,0 +1,494 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * NXP i.MX93 ADC driver + * + * Copyright 2023 NXP + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define IMX93_ADC_DRIVER_NAME "imx93-adc" + +/* Register map definition */ +#define IMX93_ADC_MCR 0x00 +#define IMX93_ADC_MSR 0x04 +#define IMX93_ADC_ISR 0x10 +#define IMX93_ADC_IMR 0x20 +#define IMX93_ADC_CIMR0 0x24 +#define IMX93_ADC_CTR0 0x94 +#define IMX93_ADC_NCMR0 0xA4 +#define IMX93_ADC_PCDR0 0x100 +#define IMX93_ADC_PCDR1 0x104 +#define IMX93_ADC_PCDR2 0x108 +#define IMX93_ADC_PCDR3 0x10C +#define IMX93_ADC_PCDR4 0x110 +#define IMX93_ADC_PCDR5 0x114 +#define IMX93_ADC_PCDR6 0x118 +#define IMX93_ADC_PCDR7 0x11C +#define IMX93_ADC_CALSTAT 0x39C +#define IMX93_ADC_CALCFG0 0x3A0 + +/* ADC bit shift */ +#define IMX93_ADC_MCR_MODE_MASK BIT(29) +#define IMX93_ADC_MCR_NSTART_MASK BIT(24) +#define IMX93_ADC_MCR_CALSTART_MASK BIT(14) +#define IMX93_ADC_MCR_ADCLKSE_MASK BIT(8) +#define IMX93_ADC_MCR_PWDN_MASK BIT(0) +#define IMX93_ADC_MSR_CALFAIL_MASK BIT(30) +#define IMX93_ADC_MSR_CALBUSY_MASK BIT(29) +#define IMX93_ADC_MSR_ADCSTATUS_MASK GENMASK(2, 0) +#define IMX93_ADC_ISR_ECH_MASK BIT(0) +#define IMX93_ADC_ISR_EOC_MASK BIT(1) +#define IMX93_ADC_ISR_EOC_ECH_MASK (IMX93_ADC_ISR_EOC_MASK | \ + IMX93_ADC_ISR_ECH_MASK) +#define IMX93_ADC_IMR_JEOC_MASK BIT(3) +#define IMX93_ADC_IMR_JECH_MASK BIT(2) +#define IMX93_ADC_IMR_EOC_MASK BIT(1) +#define IMX93_ADC_IMR_ECH_MASK BIT(0) +#define IMX93_ADC_PCDR_CDATA_MASK GENMASK(11, 0) + +#define IMX93_ADC_CALCFG0_LDFAIL_MASK BIT(4) + +/* ADC status */ +#define IMX93_ADC_MSR_ADCSTATUS_IDLE 0 +#define IMX93_ADC_MSR_ADCSTATUS_POWER_DOWN 1 +#define IMX93_ADC_MSR_ADCSTATUS_WAIT_STATE 2 +#define IMX93_ADC_MSR_ADCSTATUS_BUSY_IN_CALIBRATION 3 +#define IMX93_ADC_MSR_ADCSTATUS_SAMPLE 4 +#define IMX93_ADC_MSR_ADCSTATUS_CONVERSION 6 + +#define IMX93_ADC_TIMEOUT msecs_to_jiffies(100) + +struct imx93_adc { + struct device *dev; + void __iomem *regs; + struct clk *ipg_clk; + int irq; + struct regulator *vref; + /* lock to protect against multiple access to the device */ + struct mutex lock; + struct completion completion; +}; + +#define IMX93_ADC_CHAN(_idx) { \ + .type = IIO_VOLTAGE, \ + .indexed = 1, \ + .channel = (_idx), \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | \ + BIT(IIO_CHAN_INFO_SAMP_FREQ), \ +} + +static const struct iio_chan_spec imx93_adc_iio_channels[] = { + IMX93_ADC_CHAN(0), + IMX93_ADC_CHAN(1), + IMX93_ADC_CHAN(2), + IMX93_ADC_CHAN(3), + IMX93_ADC_CHAN(4), + IMX93_ADC_CHAN(5), + IMX93_ADC_CHAN(6), + IMX93_ADC_CHAN(7), +}; + +static void imx93_adc_power_down(struct imx93_adc *adc) +{ + u32 mcr, msr; + int ret; + + mcr = readl(adc->regs + IMX93_ADC_MCR); + mcr |= FIELD_PREP(IMX93_ADC_MCR_PWDN_MASK, 1); + writel(mcr, adc->regs + IMX93_ADC_MCR); + + ret = readl_poll_timeout(adc->regs + IMX93_ADC_MSR, msr, + ((msr & IMX93_ADC_MSR_ADCSTATUS_MASK) == + IMX93_ADC_MSR_ADCSTATUS_POWER_DOWN), + 1, 50); + if (ret == -ETIMEDOUT) + dev_warn(adc->dev, + "ADC do not in power down mode, current MSR is %x\n", + msr); +} + +static void imx93_adc_power_up(struct imx93_adc *adc) +{ + u32 mcr; + + /* bring ADC out of power down state, in idle state */ + mcr = readl(adc->regs + IMX93_ADC_MCR); + mcr &= ~FIELD_PREP(IMX93_ADC_MCR_PWDN_MASK, 1); + writel(mcr, adc->regs + IMX93_ADC_MCR); +} + +static void imx93_adc_config_ad_clk(struct imx93_adc *adc) +{ + u32 mcr; + + /* put adc in power down mode */ + imx93_adc_power_down(adc); + + /* config the AD_CLK equal to bus clock */ + mcr = readl(adc->regs + IMX93_ADC_MCR); + mcr |= FIELD_PREP(IMX93_ADC_MCR_ADCLKSE_MASK, 1); + writel(mcr, adc->regs + IMX93_ADC_MCR); + + imx93_adc_power_up(adc); +} + +static int imx93_adc_calibration(struct imx93_adc *adc) +{ + u32 mcr, msr, calcfg; + int ret; + + /* make sure ADC in power down mode */ + imx93_adc_power_down(adc); + + /* config SAR controller operating clock */ + mcr = readl(adc->regs + IMX93_ADC_MCR); + mcr &= ~FIELD_PREP(IMX93_ADC_MCR_ADCLKSE_MASK, 1); + writel(mcr, adc->regs + IMX93_ADC_MCR); + + imx93_adc_power_up(adc); + + /* Enable loading of calibrated values even in fail condition */ + calcfg = readl(adc->regs + IMX93_ADC_CALCFG0); + calcfg |= IMX93_ADC_CALCFG0_LDFAIL_MASK; + writel(calcfg, adc->regs + IMX93_ADC_CALCFG0); + + /* + * TODO: we use the default TSAMP/NRSMPL/AVGEN in MCR, + * can add the setting of these bit if need in future. + */ + + /* run calibration */ + mcr = readl(adc->regs + IMX93_ADC_MCR); + mcr |= FIELD_PREP(IMX93_ADC_MCR_CALSTART_MASK, 1); + writel(mcr, adc->regs + IMX93_ADC_MCR); + + /* wait calibration to be finished */ + ret = readl_poll_timeout(adc->regs + IMX93_ADC_MSR, msr, + !(msr & IMX93_ADC_MSR_CALBUSY_MASK), 1000, 2000000); + if (ret == -ETIMEDOUT) { + dev_warn(adc->dev, "ADC do not finish calibration in 2 min!\n"); + imx93_adc_power_down(adc); + return ret; + } + + /* check whether calbration is success or not */ + msr = readl(adc->regs + IMX93_ADC_MSR); + if (msr & IMX93_ADC_MSR_CALFAIL_MASK) { + /* + * Only give warning here, this means the noise of the + * reference voltage do not meet the requirement: + * ADC reference voltage Noise < 1.8V * 1/2^ENOB + * And the resault of ADC is not that accurate. + */ + dev_warn(adc->dev, "ADC calibration failed!\n"); + } + + return 0; +} + +static int imx93_adc_read_channel_conversion(struct imx93_adc *adc, + int channel_number, + int *result) +{ + u32 channel; + u32 imr, mcr, pcda; + long ret; + + reinit_completion(&adc->completion); + + /* config channel mask register */ + channel = 1 << channel_number; + writel(channel, adc->regs + IMX93_ADC_NCMR0); + + /* TODO: can config desired sample time in CTRn if need */ + + /* config interrupt mask */ + imr = FIELD_PREP(IMX93_ADC_IMR_EOC_MASK, 1); + writel(imr, adc->regs + IMX93_ADC_IMR); + writel(channel, adc->regs + IMX93_ADC_CIMR0); + + /* config one-shot mode */ + mcr = readl(adc->regs + IMX93_ADC_MCR); + mcr &= ~FIELD_PREP(IMX93_ADC_MCR_MODE_MASK, 1); + writel(mcr, adc->regs + IMX93_ADC_MCR); + + /* start normal conversion */ + mcr = readl(adc->regs + IMX93_ADC_MCR); + mcr |= FIELD_PREP(IMX93_ADC_MCR_NSTART_MASK, 1); + writel(mcr, adc->regs + IMX93_ADC_MCR); + + ret = wait_for_completion_interruptible_timeout(&adc->completion, + IMX93_ADC_TIMEOUT); + if (ret == 0) + return -ETIMEDOUT; + + if (ret < 0) + return ret; + + pcda = readl(adc->regs + IMX93_ADC_PCDR0 + channel_number * 4); + + *result = FIELD_GET(IMX93_ADC_PCDR_CDATA_MASK, pcda); + + return ret; +} + +static int imx93_adc_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct imx93_adc *adc = iio_priv(indio_dev); + struct device *dev = adc->dev; + int ret; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + pm_runtime_get_sync(dev); + mutex_lock(&adc->lock); + ret = imx93_adc_read_channel_conversion(adc, chan->channel, val); + mutex_unlock(&adc->lock); + pm_runtime_put_sync_autosuspend(dev); + if (ret < 0) + return ret; + + return IIO_VAL_INT; + + case IIO_CHAN_INFO_SCALE: + ret = regulator_get_voltage(adc->vref); + if (ret < 0) + return ret; + *val = ret / 1000; + *val2 = 12; + return IIO_VAL_FRACTIONAL_LOG2; + + case IIO_CHAN_INFO_SAMP_FREQ: + *val = clk_get_rate(adc->ipg_clk); + return IIO_VAL_INT; + + default: + return -EINVAL; + } +} + +static irqreturn_t imx93_adc_isr(int irq, void *dev_id) +{ + struct imx93_adc *adc = dev_id; + u32 isr, eoc, unexpected; + + isr = readl(adc->regs + IMX93_ADC_ISR); + + if (FIELD_GET(IMX93_ADC_ISR_EOC_ECH_MASK, isr)) { + eoc = isr & IMX93_ADC_ISR_EOC_ECH_MASK; + writel(eoc, adc->regs + IMX93_ADC_ISR); + complete(&adc->completion); + } + + unexpected = isr & ~IMX93_ADC_ISR_EOC_ECH_MASK; + if (unexpected) { + writel(unexpected, adc->regs + IMX93_ADC_ISR); + dev_err(adc->dev, "Unexpected interrupt 0x%08x.\n", unexpected); + return IRQ_NONE; + } + + return IRQ_HANDLED; +} + +static const struct iio_info imx93_adc_iio_info = { + .read_raw = &imx93_adc_read_raw, +}; + +static int imx93_adc_probe(struct platform_device *pdev) +{ + struct imx93_adc *adc; + struct iio_dev *indio_dev; + struct device *dev = &pdev->dev; + int ret; + + indio_dev = devm_iio_device_alloc(dev, sizeof(*adc)); + if (!indio_dev) + return -ENOMEM; + + adc = iio_priv(indio_dev); + adc->dev = dev; + + mutex_init(&adc->lock); + adc->regs = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(adc->regs)) + return dev_err_probe(dev, PTR_ERR(adc->regs), + "Failed getting ioremap resource\n"); + + /* The third irq is for ADC conversion usage */ + adc->irq = platform_get_irq(pdev, 2); + if (adc->irq < 0) + return adc->irq; + + adc->ipg_clk = devm_clk_get(dev, "ipg"); + if (IS_ERR(adc->ipg_clk)) + return dev_err_probe(dev, PTR_ERR(adc->ipg_clk), + "Failed getting clock.\n"); + + adc->vref = devm_regulator_get(dev, "vref"); + if (IS_ERR(adc->vref)) + return dev_err_probe(dev, PTR_ERR(adc->vref), + "Failed getting reference voltage.\n"); + + ret = regulator_enable(adc->vref); + if (ret) + return dev_err_probe(dev, ret, + "Failed to enable reference voltage.\n"); + + platform_set_drvdata(pdev, indio_dev); + + init_completion(&adc->completion); + + indio_dev->name = "imx93-adc"; + indio_dev->info = &imx93_adc_iio_info; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = imx93_adc_iio_channels; + indio_dev->num_channels = ARRAY_SIZE(imx93_adc_iio_channels); + + ret = clk_prepare_enable(adc->ipg_clk); + if (ret) { + dev_err_probe(dev, ret, + "Failed to enable ipg clock.\n"); + goto error_regulator_disable; + } + + ret = request_irq(adc->irq, imx93_adc_isr, 0, IMX93_ADC_DRIVER_NAME, adc); + if (ret < 0) { + dev_err_probe(dev, ret, + "Failed requesting irq, irq = %d\n", adc->irq); + goto error_ipg_clk_disable; + } + + ret = imx93_adc_calibration(adc); + if (ret < 0) + goto error_free_adc_irq; + + imx93_adc_config_ad_clk(adc); + + ret = iio_device_register(indio_dev); + if (ret) { + dev_err_probe(dev, ret, + "Failed to register this iio device.\n"); + goto error_adc_power_down; + } + + pm_runtime_set_active(dev); + pm_runtime_set_autosuspend_delay(dev, 50); + pm_runtime_use_autosuspend(dev); + pm_runtime_enable(dev); + + return 0; + +error_adc_power_down: + imx93_adc_power_down(adc); +error_free_adc_irq: + free_irq(adc->irq, adc); +error_ipg_clk_disable: + clk_disable_unprepare(adc->ipg_clk); +error_regulator_disable: + regulator_disable(adc->vref); + + return ret; +} + +static void imx93_adc_remove(struct platform_device *pdev) +{ + struct iio_dev *indio_dev = platform_get_drvdata(pdev); + struct imx93_adc *adc = iio_priv(indio_dev); + struct device *dev = adc->dev; + + /* adc power down need clock on */ + pm_runtime_get_sync(dev); + + pm_runtime_disable(dev); + pm_runtime_dont_use_autosuspend(dev); + pm_runtime_put_noidle(dev); + + iio_device_unregister(indio_dev); + imx93_adc_power_down(adc); + free_irq(adc->irq, adc); + clk_disable_unprepare(adc->ipg_clk); + regulator_disable(adc->vref); +} + +static int imx93_adc_runtime_suspend(struct device *dev) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct imx93_adc *adc = iio_priv(indio_dev); + + imx93_adc_power_down(adc); + clk_disable_unprepare(adc->ipg_clk); + regulator_disable(adc->vref); + + return 0; +} + +static int imx93_adc_runtime_resume(struct device *dev) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct imx93_adc *adc = iio_priv(indio_dev); + int ret; + + ret = regulator_enable(adc->vref); + if (ret) { + dev_err(dev, + "Can't enable adc reference top voltage, err = %d\n", + ret); + return ret; + } + + ret = clk_prepare_enable(adc->ipg_clk); + if (ret) { + dev_err(dev, "Could not prepare or enable clock.\n"); + goto err_disable_reg; + } + + imx93_adc_power_up(adc); + + return 0; + +err_disable_reg: + regulator_disable(adc->vref); + + return ret; +} + +static DEFINE_RUNTIME_DEV_PM_OPS(imx93_adc_pm_ops, + imx93_adc_runtime_suspend, + imx93_adc_runtime_resume, NULL); + +static const struct of_device_id imx93_adc_match[] = { + { .compatible = "nxp,imx93-adc", }, + { } +}; +MODULE_DEVICE_TABLE(of, imx93_adc_match); + +static struct platform_driver imx93_adc_driver = { + .probe = imx93_adc_probe, + .remove = imx93_adc_remove, + .driver = { + .name = IMX93_ADC_DRIVER_NAME, + .of_match_table = imx93_adc_match, + .pm = pm_ptr(&imx93_adc_pm_ops), + }, +}; + +module_platform_driver(imx93_adc_driver); + +MODULE_DESCRIPTION("NXP i.MX93 ADC driver"); +MODULE_AUTHOR("Haibo Chen "); +MODULE_LICENSE("GPL"); diff --git a/drivers/iio/adc/ina2xx-adc.c b/drivers/iio/adc/ina2xx-adc.c new file mode 100644 index 000000000..c412f5ed4 --- /dev/null +++ b/drivers/iio/adc/ina2xx-adc.c @@ -0,0 +1,1133 @@ +/* + * INA2XX Current and Power Monitors + * + * Copyright 2015 Baylibre SAS. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Based on linux/drivers/iio/adc/ad7291.c + * Copyright 2010-2011 Analog Devices Inc. + * + * Based on linux/drivers/hwmon/ina2xx.c + * Copyright 2012 Lothar Felten + * + * Licensed under the GPL-2 or later. + * + * IIO driver for INA219-220-226-230-231 + * + * Configurable 7-bit I2C slave address from 0x40 to 0x4F + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* INA2XX registers definition */ +#define INA2XX_CONFIG 0x00 +#define INA2XX_SHUNT_VOLTAGE 0x01 /* readonly */ +#define INA2XX_BUS_VOLTAGE 0x02 /* readonly */ +#define INA2XX_POWER 0x03 /* readonly */ +#define INA2XX_CURRENT 0x04 /* readonly */ +#define INA2XX_CALIBRATION 0x05 + +#define INA226_MASK_ENABLE 0x06 +#define INA226_CVRF BIT(3) + +#define INA2XX_MAX_REGISTERS 8 + +/* settings - depend on use case */ +#define INA219_CONFIG_DEFAULT 0x399F /* PGA=1/8, BRNG=32V */ +#define INA219_DEFAULT_IT 532 +#define INA219_DEFAULT_BRNG 1 /* 32V */ +#define INA219_DEFAULT_PGA 125 /* 1000/8 */ +#define INA226_CONFIG_DEFAULT 0x4327 +#define INA226_DEFAULT_AVG 4 +#define INA226_DEFAULT_IT 1110 + +#define INA2XX_RSHUNT_DEFAULT 10000 + +/* + * bit masks for reading the settings in the configuration register + * FIXME: use regmap_fields. + */ +#define INA2XX_MODE_MASK GENMASK(3, 0) + +/* Gain for VShunt: 1/8 (default), 1/4, 1/2, 1 */ +#define INA219_PGA_MASK GENMASK(12, 11) +#define INA219_SHIFT_PGA(val) ((val) << 11) + +/* VBus range: 32V (default), 16V */ +#define INA219_BRNG_MASK BIT(13) +#define INA219_SHIFT_BRNG(val) ((val) << 13) + +/* Averaging for VBus/VShunt/Power */ +#define INA226_AVG_MASK GENMASK(11, 9) +#define INA226_SHIFT_AVG(val) ((val) << 9) + +/* Integration time for VBus */ +#define INA219_ITB_MASK GENMASK(10, 7) +#define INA219_SHIFT_ITB(val) ((val) << 7) +#define INA226_ITB_MASK GENMASK(8, 6) +#define INA226_SHIFT_ITB(val) ((val) << 6) + +/* Integration time for VShunt */ +#define INA219_ITS_MASK GENMASK(6, 3) +#define INA219_SHIFT_ITS(val) ((val) << 3) +#define INA226_ITS_MASK GENMASK(5, 3) +#define INA226_SHIFT_ITS(val) ((val) << 3) + +/* INA219 Bus voltage register, low bits are flags */ +#define INA219_OVF BIT(0) +#define INA219_CNVR BIT(1) +#define INA219_BUS_VOLTAGE_SHIFT 3 + +/* Cosmetic macro giving the sampling period for a full P=UxI cycle */ +#define SAMPLING_PERIOD(c) ((c->int_time_vbus + c->int_time_vshunt) \ + * c->avg) + +static bool ina2xx_is_writeable_reg(struct device *dev, unsigned int reg) +{ + return (reg == INA2XX_CONFIG) || (reg > INA2XX_CURRENT); +} + +static bool ina2xx_is_volatile_reg(struct device *dev, unsigned int reg) +{ + return (reg != INA2XX_CONFIG); +} + +static inline bool is_signed_reg(unsigned int reg) +{ + return (reg == INA2XX_SHUNT_VOLTAGE) || (reg == INA2XX_CURRENT); +} + +static const struct regmap_config ina2xx_regmap_config = { + .reg_bits = 8, + .val_bits = 16, + .max_register = INA2XX_MAX_REGISTERS, + .writeable_reg = ina2xx_is_writeable_reg, + .volatile_reg = ina2xx_is_volatile_reg, +}; + +enum ina2xx_ids { ina219, ina226, ina236 }; + +struct ina2xx_config { + const char *name; + u16 config_default; + int calibration_value; + int shunt_voltage_lsb; /* nV */ + int bus_voltage_shift; /* position of lsb */ + int bus_voltage_lsb; /* uV */ + /* fixed relation between current and power lsb, uW/uA */ + int power_lsb_factor; + enum ina2xx_ids chip_id; +}; + +struct ina2xx_chip_info { + struct regmap *regmap; + struct task_struct *task; + const struct ina2xx_config *config; + struct mutex state_lock; + unsigned int shunt_resistor_uohm; + int avg; + int int_time_vbus; /* Bus voltage integration time uS */ + int int_time_vshunt; /* Shunt voltage integration time uS */ + int range_vbus; /* Bus voltage maximum in V */ + int pga_gain_vshunt; /* Shunt voltage PGA gain */ + bool allow_async_readout; + /* data buffer needs space for channel data and timestamp */ + struct { + u16 chan[4]; + aligned_s64 ts; + } scan; +}; + +static const struct ina2xx_config ina2xx_config[] = { + [ina219] = { + .name = "ina219", + .config_default = INA219_CONFIG_DEFAULT, + .calibration_value = 4096, + .shunt_voltage_lsb = 10000, + .bus_voltage_shift = INA219_BUS_VOLTAGE_SHIFT, + .bus_voltage_lsb = 4000, + .power_lsb_factor = 20, + .chip_id = ina219, + }, + [ina226] = { + .name = "ina226", + .config_default = INA226_CONFIG_DEFAULT, + .calibration_value = 2048, + .shunt_voltage_lsb = 2500, + .bus_voltage_shift = 0, + .bus_voltage_lsb = 1250, + .power_lsb_factor = 25, + .chip_id = ina226, + }, + [ina236] = { + .name = "ina236", + .config_default = INA226_CONFIG_DEFAULT, + .calibration_value = 2048, + .shunt_voltage_lsb = 2500, + .bus_voltage_shift = 0, + .bus_voltage_lsb = 1600, + .power_lsb_factor = 32, + .chip_id = ina236, + }, +}; + +static int ina2xx_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + int ret; + struct ina2xx_chip_info *chip = iio_priv(indio_dev); + unsigned int regval; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + ret = regmap_read(chip->regmap, chan->address, ®val); + if (ret) + return ret; + + if (is_signed_reg(chan->address)) + *val = (s16) regval; + else + *val = regval; + + if (chan->address == INA2XX_BUS_VOLTAGE) + *val >>= chip->config->bus_voltage_shift; + + return IIO_VAL_INT; + + case IIO_CHAN_INFO_OVERSAMPLING_RATIO: + *val = chip->avg; + return IIO_VAL_INT; + + case IIO_CHAN_INFO_INT_TIME: + *val = 0; + if (chan->address == INA2XX_SHUNT_VOLTAGE) + *val2 = chip->int_time_vshunt; + else + *val2 = chip->int_time_vbus; + + return IIO_VAL_INT_PLUS_MICRO; + + case IIO_CHAN_INFO_SAMP_FREQ: + /* + * Sample freq is read only, it is a consequence of + * 1/AVG*(CT_bus+CT_shunt). + */ + *val = DIV_ROUND_CLOSEST(1000000, SAMPLING_PERIOD(chip)); + + return IIO_VAL_INT; + + case IIO_CHAN_INFO_SCALE: + switch (chan->address) { + case INA2XX_SHUNT_VOLTAGE: + /* processed (mV) = raw * lsb(nV) / 1000000 */ + *val = chip->config->shunt_voltage_lsb; + *val2 = 1000000; + return IIO_VAL_FRACTIONAL; + + case INA2XX_BUS_VOLTAGE: + /* processed (mV) = raw * lsb (uV) / 1000 */ + *val = chip->config->bus_voltage_lsb; + *val2 = 1000; + return IIO_VAL_FRACTIONAL; + + case INA2XX_CURRENT: + /* + * processed (mA) = raw * current_lsb (mA) + * current_lsb (mA) = shunt_voltage_lsb (nV) / + * shunt_resistor (uOhm) + */ + *val = chip->config->shunt_voltage_lsb; + *val2 = chip->shunt_resistor_uohm; + return IIO_VAL_FRACTIONAL; + + case INA2XX_POWER: + /* + * processed (mW) = raw * power_lsb (mW) + * power_lsb (mW) = power_lsb_factor (mW/mA) * + * current_lsb (mA) + */ + *val = chip->config->power_lsb_factor * + chip->config->shunt_voltage_lsb; + *val2 = chip->shunt_resistor_uohm; + return IIO_VAL_FRACTIONAL; + } + return -EINVAL; + + case IIO_CHAN_INFO_HARDWAREGAIN: + switch (chan->address) { + case INA2XX_SHUNT_VOLTAGE: + *val = chip->pga_gain_vshunt; + *val2 = 1000; + return IIO_VAL_FRACTIONAL; + + case INA2XX_BUS_VOLTAGE: + *val = chip->range_vbus == 32 ? 1 : 2; + return IIO_VAL_INT; + } + return -EINVAL; + } + + return -EINVAL; +} + +/* + * Available averaging rates for ina226. The indices correspond with + * the bit values expected by the chip (according to the ina226 datasheet, + * table 3 AVG bit settings, found at + * https://www.ti.com/lit/ds/symlink/ina226.pdf. + */ +static const int ina226_avg_tab[] = { 1, 4, 16, 64, 128, 256, 512, 1024 }; + +static int ina226_set_average(struct ina2xx_chip_info *chip, unsigned int val, + unsigned int *config) +{ + int bits; + + if (val > 1024 || val < 1) + return -EINVAL; + + bits = find_closest(val, ina226_avg_tab, + ARRAY_SIZE(ina226_avg_tab)); + + chip->avg = ina226_avg_tab[bits]; + + *config &= ~INA226_AVG_MASK; + *config |= INA226_SHIFT_AVG(bits) & INA226_AVG_MASK; + + return 0; +} + +/* Conversion times in uS */ +static const int ina226_conv_time_tab[] = { 140, 204, 332, 588, 1100, + 2116, 4156, 8244 }; + +static int ina226_set_int_time_vbus(struct ina2xx_chip_info *chip, + unsigned int val_us, unsigned int *config) +{ + int bits; + + if (val_us > 8244 || val_us < 140) + return -EINVAL; + + bits = find_closest(val_us, ina226_conv_time_tab, + ARRAY_SIZE(ina226_conv_time_tab)); + + chip->int_time_vbus = ina226_conv_time_tab[bits]; + + *config &= ~INA226_ITB_MASK; + *config |= INA226_SHIFT_ITB(bits) & INA226_ITB_MASK; + + return 0; +} + +static int ina226_set_int_time_vshunt(struct ina2xx_chip_info *chip, + unsigned int val_us, unsigned int *config) +{ + int bits; + + if (val_us > 8244 || val_us < 140) + return -EINVAL; + + bits = find_closest(val_us, ina226_conv_time_tab, + ARRAY_SIZE(ina226_conv_time_tab)); + + chip->int_time_vshunt = ina226_conv_time_tab[bits]; + + *config &= ~INA226_ITS_MASK; + *config |= INA226_SHIFT_ITS(bits) & INA226_ITS_MASK; + + return 0; +} + +/* Conversion times in uS. */ +static const int ina219_conv_time_tab_subsample[] = { 84, 148, 276, 532 }; +static const int ina219_conv_time_tab_average[] = { 532, 1060, 2130, 4260, + 8510, 17020, 34050, 68100}; + +static int ina219_lookup_int_time(unsigned int *val_us, int *bits) +{ + if (*val_us > 68100 || *val_us < 84) + return -EINVAL; + + if (*val_us <= 532) { + *bits = find_closest(*val_us, ina219_conv_time_tab_subsample, + ARRAY_SIZE(ina219_conv_time_tab_subsample)); + *val_us = ina219_conv_time_tab_subsample[*bits]; + } else { + *bits = find_closest(*val_us, ina219_conv_time_tab_average, + ARRAY_SIZE(ina219_conv_time_tab_average)); + *val_us = ina219_conv_time_tab_average[*bits]; + *bits |= 0x8; + } + + return 0; +} + +static int ina219_set_int_time_vbus(struct ina2xx_chip_info *chip, + unsigned int val_us, unsigned int *config) +{ + int bits, ret; + unsigned int val_us_best = val_us; + + ret = ina219_lookup_int_time(&val_us_best, &bits); + if (ret) + return ret; + + chip->int_time_vbus = val_us_best; + + *config &= ~INA219_ITB_MASK; + *config |= INA219_SHIFT_ITB(bits) & INA219_ITB_MASK; + + return 0; +} + +static int ina219_set_int_time_vshunt(struct ina2xx_chip_info *chip, + unsigned int val_us, unsigned int *config) +{ + int bits, ret; + unsigned int val_us_best = val_us; + + ret = ina219_lookup_int_time(&val_us_best, &bits); + if (ret) + return ret; + + chip->int_time_vshunt = val_us_best; + + *config &= ~INA219_ITS_MASK; + *config |= INA219_SHIFT_ITS(bits) & INA219_ITS_MASK; + + return 0; +} + +static const int ina219_vbus_range_tab[] = { 1, 2 }; +static int ina219_set_vbus_range_denom(struct ina2xx_chip_info *chip, + unsigned int range, + unsigned int *config) +{ + if (range == 1) + chip->range_vbus = 32; + else if (range == 2) + chip->range_vbus = 16; + else + return -EINVAL; + + *config &= ~INA219_BRNG_MASK; + *config |= INA219_SHIFT_BRNG(range == 1 ? 1 : 0) & INA219_BRNG_MASK; + + return 0; +} + +static const int ina219_vshunt_gain_tab[] = { 125, 250, 500, 1000 }; +static const int ina219_vshunt_gain_frac[] = { + 125, 1000, 250, 1000, 500, 1000, 1000, 1000 }; + +static int ina219_set_vshunt_pga_gain(struct ina2xx_chip_info *chip, + unsigned int gain, + unsigned int *config) +{ + int bits; + + if (gain < 125 || gain > 1000) + return -EINVAL; + + bits = find_closest(gain, ina219_vshunt_gain_tab, + ARRAY_SIZE(ina219_vshunt_gain_tab)); + + chip->pga_gain_vshunt = ina219_vshunt_gain_tab[bits]; + bits = 3 - bits; + + *config &= ~INA219_PGA_MASK; + *config |= INA219_SHIFT_PGA(bits) & INA219_PGA_MASK; + + return 0; +} + +static int ina2xx_read_avail(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + const int **vals, int *type, int *length, + long mask) +{ + switch (mask) { + case IIO_CHAN_INFO_HARDWAREGAIN: + switch (chan->address) { + case INA2XX_SHUNT_VOLTAGE: + *type = IIO_VAL_FRACTIONAL; + *length = sizeof(ina219_vshunt_gain_frac) / sizeof(int); + *vals = ina219_vshunt_gain_frac; + return IIO_AVAIL_LIST; + + case INA2XX_BUS_VOLTAGE: + *type = IIO_VAL_INT; + *length = sizeof(ina219_vbus_range_tab) / sizeof(int); + *vals = ina219_vbus_range_tab; + return IIO_AVAIL_LIST; + } + } + + return -EINVAL; +} + +static int ina2xx_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, int val2, long mask) +{ + struct ina2xx_chip_info *chip = iio_priv(indio_dev); + unsigned int config, tmp; + int ret; + + if (iio_buffer_enabled(indio_dev)) + return -EBUSY; + + mutex_lock(&chip->state_lock); + + ret = regmap_read(chip->regmap, INA2XX_CONFIG, &config); + if (ret) + goto err; + + tmp = config; + + switch (mask) { + case IIO_CHAN_INFO_OVERSAMPLING_RATIO: + ret = ina226_set_average(chip, val, &tmp); + break; + + case IIO_CHAN_INFO_INT_TIME: + switch (chip->config->chip_id) { + case ina226: + case ina236: + if (chan->address == INA2XX_SHUNT_VOLTAGE) + ret = ina226_set_int_time_vshunt(chip, val2, + &tmp); + else + ret = ina226_set_int_time_vbus(chip, val2, + &tmp); + break; + case ina219: + if (chan->address == INA2XX_SHUNT_VOLTAGE) + ret = ina219_set_int_time_vshunt(chip, val2, + &tmp); + else + ret = ina219_set_int_time_vbus(chip, val2, + &tmp); + break; + default: + ret = -EINVAL; + } + break; + + case IIO_CHAN_INFO_HARDWAREGAIN: + if (chan->address == INA2XX_SHUNT_VOLTAGE) + ret = ina219_set_vshunt_pga_gain(chip, val * 1000 + + val2 / 1000, &tmp); + else + ret = ina219_set_vbus_range_denom(chip, val, &tmp); + break; + + default: + ret = -EINVAL; + } + + if (!ret && (tmp != config)) + ret = regmap_write(chip->regmap, INA2XX_CONFIG, tmp); +err: + mutex_unlock(&chip->state_lock); + + return ret; +} + +static ssize_t ina2xx_allow_async_readout_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct ina2xx_chip_info *chip = iio_priv(dev_to_iio_dev(dev)); + + return sysfs_emit(buf, "%d\n", chip->allow_async_readout); +} + +static ssize_t ina2xx_allow_async_readout_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t len) +{ + struct ina2xx_chip_info *chip = iio_priv(dev_to_iio_dev(dev)); + bool val; + int ret; + + ret = kstrtobool(buf, &val); + if (ret) + return ret; + + chip->allow_async_readout = val; + + return len; +} + +/* + * Calibration register is set to the best value, which eliminates + * truncation errors on calculating current register in hardware. + * According to datasheet (INA 226: eq. 3, INA219: eq. 4) the best values + * are 2048 for ina226 and 4096 for ina219. They are hardcoded as + * calibration_value. + */ +static int ina2xx_set_calibration(struct ina2xx_chip_info *chip) +{ + return regmap_write(chip->regmap, INA2XX_CALIBRATION, + chip->config->calibration_value); +} + +static int set_shunt_resistor(struct ina2xx_chip_info *chip, unsigned int val) +{ + if (val == 0 || val > INT_MAX) + return -EINVAL; + + chip->shunt_resistor_uohm = val; + + return 0; +} + +static ssize_t ina2xx_shunt_resistor_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct ina2xx_chip_info *chip = iio_priv(dev_to_iio_dev(dev)); + int vals[2] = { chip->shunt_resistor_uohm, 1000000 }; + + return iio_format_value(buf, IIO_VAL_FRACTIONAL, 1, vals); +} + +static ssize_t ina2xx_shunt_resistor_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t len) +{ + struct ina2xx_chip_info *chip = iio_priv(dev_to_iio_dev(dev)); + int val, val_fract, ret; + + ret = iio_str_to_fixpoint(buf, 100000, &val, &val_fract); + if (ret) + return ret; + + ret = set_shunt_resistor(chip, val * 1000000 + val_fract); + if (ret) + return ret; + + return len; +} + +#define INA219_CHAN(_type, _index, _address) { \ + .type = (_type), \ + .address = (_address), \ + .indexed = 1, \ + .channel = (_index), \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ + BIT(IIO_CHAN_INFO_SCALE), \ + .info_mask_shared_by_dir = BIT(IIO_CHAN_INFO_SAMP_FREQ), \ + .scan_index = (_index), \ + .scan_type = { \ + .sign = 'u', \ + .realbits = 16, \ + .storagebits = 16, \ + .endianness = IIO_CPU, \ + } \ +} + +#define INA226_CHAN(_type, _index, _address) { \ + .type = (_type), \ + .address = (_address), \ + .indexed = 1, \ + .channel = (_index), \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ + BIT(IIO_CHAN_INFO_SCALE), \ + .info_mask_shared_by_dir = BIT(IIO_CHAN_INFO_SAMP_FREQ) | \ + BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \ + .scan_index = (_index), \ + .scan_type = { \ + .sign = 'u', \ + .realbits = 16, \ + .storagebits = 16, \ + .endianness = IIO_CPU, \ + } \ +} + +/* + * Sampling Freq is a consequence of the integration times of + * the Voltage channels. + */ +#define INA219_CHAN_VOLTAGE(_index, _address, _shift) { \ + .type = IIO_VOLTAGE, \ + .address = (_address), \ + .indexed = 1, \ + .channel = (_index), \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ + BIT(IIO_CHAN_INFO_SCALE) | \ + BIT(IIO_CHAN_INFO_INT_TIME) | \ + BIT(IIO_CHAN_INFO_HARDWAREGAIN), \ + .info_mask_separate_available = \ + BIT(IIO_CHAN_INFO_HARDWAREGAIN), \ + .info_mask_shared_by_dir = BIT(IIO_CHAN_INFO_SAMP_FREQ), \ + .scan_index = (_index), \ + .scan_type = { \ + .sign = 'u', \ + .shift = _shift, \ + .realbits = 16 - _shift, \ + .storagebits = 16, \ + .endianness = IIO_LE, \ + } \ +} + +#define INA226_CHAN_VOLTAGE(_index, _address) { \ + .type = IIO_VOLTAGE, \ + .address = (_address), \ + .indexed = 1, \ + .channel = (_index), \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ + BIT(IIO_CHAN_INFO_SCALE) | \ + BIT(IIO_CHAN_INFO_INT_TIME), \ + .info_mask_shared_by_dir = BIT(IIO_CHAN_INFO_SAMP_FREQ) | \ + BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \ + .scan_index = (_index), \ + .scan_type = { \ + .sign = 'u', \ + .realbits = 16, \ + .storagebits = 16, \ + .endianness = IIO_LE, \ + } \ +} + + +static const struct iio_chan_spec ina226_channels[] = { + INA226_CHAN_VOLTAGE(0, INA2XX_SHUNT_VOLTAGE), + INA226_CHAN_VOLTAGE(1, INA2XX_BUS_VOLTAGE), + INA226_CHAN(IIO_POWER, 2, INA2XX_POWER), + INA226_CHAN(IIO_CURRENT, 3, INA2XX_CURRENT), + IIO_CHAN_SOFT_TIMESTAMP(4), +}; + +static const struct iio_chan_spec ina219_channels[] = { + INA219_CHAN_VOLTAGE(0, INA2XX_SHUNT_VOLTAGE, 0), + INA219_CHAN_VOLTAGE(1, INA2XX_BUS_VOLTAGE, INA219_BUS_VOLTAGE_SHIFT), + INA219_CHAN(IIO_POWER, 2, INA2XX_POWER), + INA219_CHAN(IIO_CURRENT, 3, INA2XX_CURRENT), + IIO_CHAN_SOFT_TIMESTAMP(4), +}; + +static int ina2xx_conversion_ready(struct iio_dev *indio_dev) +{ + struct ina2xx_chip_info *chip = iio_priv(indio_dev); + int ret; + unsigned int alert; + + /* + * Because the timer thread and the chip conversion clock + * are asynchronous, the period difference will eventually + * result in reading V[k-1] again, or skip V[k] at time Tk. + * In order to resync the timer with the conversion process + * we check the ConVersionReadyFlag. + * On hardware that supports using the ALERT pin to toggle a + * GPIO a triggered buffer could be used instead. + * For now, we do an extra read of the MASK_ENABLE register (INA226) + * resp. the BUS_VOLTAGE register (INA219). + */ + switch (chip->config->chip_id) { + case ina226: + case ina236: + ret = regmap_read(chip->regmap, + INA226_MASK_ENABLE, &alert); + if (ret < 0) + return ret; + + alert &= INA226_CVRF; + break; + case ina219: + ret = regmap_read(chip->regmap, + INA2XX_BUS_VOLTAGE, &alert); + if (ret < 0) + return ret; + alert &= INA219_CNVR; + break; + default: + return -EINVAL; + } + + return !!alert; +} + +static int ina2xx_work_buffer(struct iio_dev *indio_dev) +{ + struct ina2xx_chip_info *chip = iio_priv(indio_dev); + int bit, ret, i = 0; + s64 time; + + time = iio_get_time_ns(indio_dev); + + /* + * Single register reads: bulk_read will not work with ina226/219 + * as there is no auto-increment of the register pointer. + */ + iio_for_each_active_channel(indio_dev, bit) { + unsigned int val; + + ret = regmap_read(chip->regmap, + INA2XX_SHUNT_VOLTAGE + bit, &val); + if (ret < 0) + return ret; + + chip->scan.chan[i++] = val; + } + + iio_push_to_buffers_with_ts(indio_dev, &chip->scan, sizeof(chip->scan), time); + + return 0; +}; + +static int ina2xx_capture_thread(void *data) +{ + struct iio_dev *indio_dev = data; + struct ina2xx_chip_info *chip = iio_priv(indio_dev); + int sampling_us = SAMPLING_PERIOD(chip); + int ret; + struct timespec64 next, now, delta; + s64 delay_us; + + /* + * Poll a bit faster than the chip internal Fs, in case + * we wish to sync with the conversion ready flag. + */ + if (!chip->allow_async_readout) + sampling_us -= 200; + + ktime_get_ts64(&next); + + do { + while (!chip->allow_async_readout) { + ret = ina2xx_conversion_ready(indio_dev); + if (ret < 0) + return ret; + + /* + * If the conversion was not yet finished, + * reset the reference timestamp. + */ + if (ret == 0) + ktime_get_ts64(&next); + else + break; + } + + ret = ina2xx_work_buffer(indio_dev); + if (ret < 0) + return ret; + + ktime_get_ts64(&now); + + /* + * Advance the timestamp for the next poll by one sampling + * interval, and sleep for the remainder (next - now) + * In case "next" has already passed, the interval is added + * multiple times, i.e. samples are dropped. + */ + do { + timespec64_add_ns(&next, 1000 * sampling_us); + delta = timespec64_sub(next, now); + delay_us = div_s64(timespec64_to_ns(&delta), 1000); + } while (delay_us <= 0); + + usleep_range(delay_us, (delay_us * 3) >> 1); + + } while (!kthread_should_stop()); + + return 0; +} + +static int ina2xx_buffer_enable(struct iio_dev *indio_dev) +{ + struct ina2xx_chip_info *chip = iio_priv(indio_dev); + unsigned int sampling_us = SAMPLING_PERIOD(chip); + struct task_struct *task; + + dev_dbg(&indio_dev->dev, "Enabling buffer w/ scan_mask %02x, freq = %d, avg =%u\n", + (unsigned int)(*indio_dev->active_scan_mask), + 1000000 / sampling_us, chip->avg); + + dev_dbg(&indio_dev->dev, "Expected work period: %u us\n", sampling_us); + dev_dbg(&indio_dev->dev, "Async readout mode: %d\n", + chip->allow_async_readout); + + task = kthread_run(ina2xx_capture_thread, (void *)indio_dev, + "%s:%d-%uus", indio_dev->name, + iio_device_id(indio_dev), + sampling_us); + if (IS_ERR(task)) + return PTR_ERR(task); + + chip->task = task; + + return 0; +} + +static int ina2xx_buffer_disable(struct iio_dev *indio_dev) +{ + struct ina2xx_chip_info *chip = iio_priv(indio_dev); + + if (chip->task) { + kthread_stop(chip->task); + chip->task = NULL; + } + + return 0; +} + +static const struct iio_buffer_setup_ops ina2xx_setup_ops = { + .postenable = &ina2xx_buffer_enable, + .predisable = &ina2xx_buffer_disable, +}; + +static int ina2xx_debug_reg(struct iio_dev *indio_dev, + unsigned reg, unsigned writeval, unsigned *readval) +{ + struct ina2xx_chip_info *chip = iio_priv(indio_dev); + + if (!readval) + return regmap_write(chip->regmap, reg, writeval); + + return regmap_read(chip->regmap, reg, readval); +} + +/* Possible integration times for vshunt and vbus */ +static IIO_CONST_ATTR_NAMED(ina219_integration_time_available, + integration_time_available, + "0.000084 0.000148 0.000276 0.000532 0.001060 0.002130 0.004260 0.008510 0.017020 0.034050 0.068100"); + +static IIO_CONST_ATTR_NAMED(ina226_integration_time_available, + integration_time_available, + "0.000140 0.000204 0.000332 0.000588 0.001100 0.002116 0.004156 0.008244"); + +static IIO_DEVICE_ATTR(in_allow_async_readout, S_IRUGO | S_IWUSR, + ina2xx_allow_async_readout_show, + ina2xx_allow_async_readout_store, 0); + +static IIO_DEVICE_ATTR(in_shunt_resistor, S_IRUGO | S_IWUSR, + ina2xx_shunt_resistor_show, + ina2xx_shunt_resistor_store, 0); + +static struct attribute *ina219_attributes[] = { + &iio_dev_attr_in_allow_async_readout.dev_attr.attr, + &iio_const_attr_ina219_integration_time_available.dev_attr.attr, + &iio_dev_attr_in_shunt_resistor.dev_attr.attr, + NULL, +}; + +static struct attribute *ina226_attributes[] = { + &iio_dev_attr_in_allow_async_readout.dev_attr.attr, + &iio_const_attr_ina226_integration_time_available.dev_attr.attr, + &iio_dev_attr_in_shunt_resistor.dev_attr.attr, + NULL, +}; + +static const struct attribute_group ina219_attribute_group = { + .attrs = ina219_attributes, +}; + +static const struct attribute_group ina226_attribute_group = { + .attrs = ina226_attributes, +}; + +static const struct iio_info ina219_info = { + .attrs = &ina219_attribute_group, + .read_raw = ina2xx_read_raw, + .read_avail = ina2xx_read_avail, + .write_raw = ina2xx_write_raw, + .debugfs_reg_access = ina2xx_debug_reg, +}; + +static const struct iio_info ina226_info = { + .attrs = &ina226_attribute_group, + .read_raw = ina2xx_read_raw, + .write_raw = ina2xx_write_raw, + .debugfs_reg_access = ina2xx_debug_reg, +}; + +/* Initialize the configuration and calibration registers. */ +static int ina2xx_init(struct ina2xx_chip_info *chip, unsigned int config) +{ + int ret = regmap_write(chip->regmap, INA2XX_CONFIG, config); + if (ret) + return ret; + + return ina2xx_set_calibration(chip); +} + +static int ina2xx_probe(struct i2c_client *client) +{ + const struct i2c_device_id *id = i2c_client_get_device_id(client); + struct ina2xx_chip_info *chip; + struct iio_dev *indio_dev; + unsigned int val; + enum ina2xx_ids type; + int ret; + + indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*chip)); + if (!indio_dev) + return -ENOMEM; + + chip = iio_priv(indio_dev); + + /* This is only used for device removal purposes. */ + i2c_set_clientdata(client, indio_dev); + + chip->regmap = devm_regmap_init_i2c(client, &ina2xx_regmap_config); + if (IS_ERR(chip->regmap)) { + dev_err(&client->dev, "failed to allocate register map\n"); + return PTR_ERR(chip->regmap); + } + + if (client->dev.of_node) + type = (uintptr_t)of_device_get_match_data(&client->dev); + else + type = id->driver_data; + chip->config = &ina2xx_config[type]; + + mutex_init(&chip->state_lock); + + if (of_property_read_u32(client->dev.of_node, "shunt-resistor", &val) < 0) + val = INA2XX_RSHUNT_DEFAULT; + + ret = set_shunt_resistor(chip, val); + if (ret) + return ret; + + /* Patch the current config register with default. */ + val = chip->config->config_default; + + switch (type) { + case ina226: + case ina236: + ina226_set_average(chip, INA226_DEFAULT_AVG, &val); + ina226_set_int_time_vbus(chip, INA226_DEFAULT_IT, &val); + ina226_set_int_time_vshunt(chip, INA226_DEFAULT_IT, &val); + break; + case ina219: + chip->avg = 1; + ina219_set_int_time_vbus(chip, INA219_DEFAULT_IT, &val); + ina219_set_int_time_vshunt(chip, INA219_DEFAULT_IT, &val); + ina219_set_vbus_range_denom(chip, INA219_DEFAULT_BRNG, &val); + ina219_set_vshunt_pga_gain(chip, INA219_DEFAULT_PGA, &val); + break; + default: + return -EINVAL; + } + + ret = ina2xx_init(chip, val); + if (ret) { + dev_err(&client->dev, "error configuring the device\n"); + return ret; + } + + indio_dev->modes = INDIO_DIRECT_MODE; + switch (type) { + case ina226: + case ina236: + indio_dev->channels = ina226_channels; + indio_dev->num_channels = ARRAY_SIZE(ina226_channels); + indio_dev->info = &ina226_info; + break; + case ina219: + indio_dev->channels = ina219_channels; + indio_dev->num_channels = ARRAY_SIZE(ina219_channels); + indio_dev->info = &ina219_info; + break; + default: + return -EINVAL; + } + indio_dev->name = id ? id->name : chip->config->name; + + ret = devm_iio_kfifo_buffer_setup(&client->dev, indio_dev, + &ina2xx_setup_ops); + if (ret) + return ret; + + return iio_device_register(indio_dev); +} + +static void ina2xx_remove(struct i2c_client *client) +{ + struct iio_dev *indio_dev = i2c_get_clientdata(client); + struct ina2xx_chip_info *chip = iio_priv(indio_dev); + int ret; + + iio_device_unregister(indio_dev); + + /* Powerdown */ + ret = regmap_clear_bits(chip->regmap, INA2XX_CONFIG, INA2XX_MODE_MASK); + if (ret) + dev_warn(&client->dev, "Failed to power down device (%pe)\n", + ERR_PTR(ret)); +} + +static const struct i2c_device_id ina2xx_id[] = { + { .name = "ina219", .driver_data = ina219 }, + { .name = "ina220", .driver_data = ina219 }, + { .name = "ina226", .driver_data = ina226 }, + { .name = "ina230", .driver_data = ina226 }, + { .name = "ina231", .driver_data = ina226 }, + { .name = "ina236", .driver_data = ina236 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, ina2xx_id); + +static const struct of_device_id ina2xx_of_match[] = { + { + .compatible = "ti,ina219", + .data = (void *)ina219 + }, + { + .compatible = "ti,ina220", + .data = (void *)ina219 + }, + { + .compatible = "ti,ina226", + .data = (void *)ina226 + }, + { + .compatible = "ti,ina230", + .data = (void *)ina226 + }, + { + .compatible = "ti,ina231", + .data = (void *)ina226 + }, + { + .compatible = "ti,ina236", + .data = (void *)ina236 + }, + { } +}; +MODULE_DEVICE_TABLE(of, ina2xx_of_match); + +static struct i2c_driver ina2xx_driver = { + .driver = { + .name = KBUILD_MODNAME, + .of_match_table = ina2xx_of_match, + }, + .probe = ina2xx_probe, + .remove = ina2xx_remove, + .id_table = ina2xx_id, +}; +module_i2c_driver(ina2xx_driver); + +MODULE_AUTHOR("Marc Titinger "); +MODULE_DESCRIPTION("Texas Instruments INA2XX ADC driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/adc/industrialio-adc.c b/drivers/iio/adc/industrialio-adc.c new file mode 100644 index 000000000..b4057230e --- /dev/null +++ b/drivers/iio/adc/industrialio-adc.c @@ -0,0 +1,82 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Helpers for parsing common ADC information from a firmware node. + * + * Copyright (c) 2025 Matti Vaittinen + */ + +#include +#include +#include +#include +#include +#include + +#include +#include + +/** + * devm_iio_adc_device_alloc_chaninfo_se - allocate and fill iio_chan_spec for ADC + * + * Scan the device node for single-ended ADC channel information. Channel ID is + * expected to be found from the "reg" property. Allocate and populate the + * iio_chan_spec structure corresponding to channels that are found. The memory + * for iio_chan_spec structure will be freed upon device detach. + * + * @dev: Pointer to the ADC device. + * @template: Template iio_chan_spec from which the fields of all + * found and allocated channels are initialized. + * @max_chan_id: Maximum value of a channel ID. Use negative value if no + * checking is required. + * @cs: Location where pointer to allocated iio_chan_spec + * should be stored. + * + * Return: Number of found channels on success. Negative value to indicate + * failure. Specifically, -ENOENT if no channel nodes were found. + */ +int devm_iio_adc_device_alloc_chaninfo_se(struct device *dev, + const struct iio_chan_spec *template, + int max_chan_id, + struct iio_chan_spec **cs) +{ + struct iio_chan_spec *chan_array, *chan; + int num_chan, ret; + + num_chan = iio_adc_device_num_channels(dev); + if (num_chan < 0) + return num_chan; + + if (!num_chan) + return -ENOENT; + + chan_array = devm_kcalloc(dev, num_chan, sizeof(*chan_array), + GFP_KERNEL); + if (!chan_array) + return -ENOMEM; + + chan = &chan_array[0]; + + device_for_each_named_child_node_scoped(dev, child, "channel") { + u32 ch; + + ret = fwnode_property_read_u32(child, "reg", &ch); + if (ret) + return ret; + + if (max_chan_id >= 0 && ch > max_chan_id) + return -ERANGE; + + *chan = *template; + chan->channel = ch; + chan++; + } + + *cs = chan_array; + + return num_chan; +} +EXPORT_SYMBOL_NS_GPL(devm_iio_adc_device_alloc_chaninfo_se, "IIO_DRIVER"); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Matti Vaittinen "); +MODULE_DESCRIPTION("IIO ADC fwnode parsing helpers"); diff --git a/drivers/iio/adc/ingenic-adc.c b/drivers/iio/adc/ingenic-adc.c new file mode 100644 index 000000000..71fcdfedb --- /dev/null +++ b/drivers/iio/adc/ingenic-adc.c @@ -0,0 +1,925 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * ADC driver for the Ingenic JZ47xx SoCs + * Copyright (c) 2019 Artur Rojek + * + * based on drivers/mfd/jz4740-adc.c + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define JZ_ADC_REG_ENABLE 0x00 +#define JZ_ADC_REG_CFG 0x04 +#define JZ_ADC_REG_CTRL 0x08 +#define JZ_ADC_REG_STATUS 0x0c +#define JZ_ADC_REG_ADSAME 0x10 +#define JZ_ADC_REG_ADWAIT 0x14 +#define JZ_ADC_REG_ADTCH 0x18 +#define JZ_ADC_REG_ADBDAT 0x1c +#define JZ_ADC_REG_ADSDAT 0x20 +#define JZ_ADC_REG_ADCMD 0x24 +#define JZ_ADC_REG_ADCLK 0x28 + +#define JZ_ADC_REG_ENABLE_PD BIT(7) +#define JZ_ADC_REG_CFG_AUX_MD (BIT(0) | BIT(1)) +#define JZ_ADC_REG_CFG_BAT_MD BIT(4) +#define JZ_ADC_REG_CFG_SAMPLE_NUM(n) ((n) << 10) +#define JZ_ADC_REG_CFG_PULL_UP(n) ((n) << 16) +#define JZ_ADC_REG_CFG_CMD_SEL BIT(22) +#define JZ_ADC_REG_CFG_VBAT_SEL BIT(30) +#define JZ_ADC_REG_CFG_TOUCH_OPS_MASK (BIT(31) | GENMASK(23, 10)) +#define JZ_ADC_REG_ADCLK_CLKDIV_LSB 0 +#define JZ4725B_ADC_REG_ADCLK_CLKDIV10US_LSB 16 +#define JZ4770_ADC_REG_ADCLK_CLKDIV10US_LSB 8 +#define JZ4770_ADC_REG_ADCLK_CLKDIVMS_LSB 16 + +#define JZ_ADC_REG_ADCMD_YNADC BIT(7) +#define JZ_ADC_REG_ADCMD_YPADC BIT(8) +#define JZ_ADC_REG_ADCMD_XNADC BIT(9) +#define JZ_ADC_REG_ADCMD_XPADC BIT(10) +#define JZ_ADC_REG_ADCMD_VREFPYP BIT(11) +#define JZ_ADC_REG_ADCMD_VREFPXP BIT(12) +#define JZ_ADC_REG_ADCMD_VREFPXN BIT(13) +#define JZ_ADC_REG_ADCMD_VREFPAUX BIT(14) +#define JZ_ADC_REG_ADCMD_VREFPVDD33 BIT(15) +#define JZ_ADC_REG_ADCMD_VREFNYN BIT(16) +#define JZ_ADC_REG_ADCMD_VREFNXP BIT(17) +#define JZ_ADC_REG_ADCMD_VREFNXN BIT(18) +#define JZ_ADC_REG_ADCMD_VREFAUX BIT(19) +#define JZ_ADC_REG_ADCMD_YNGRU BIT(20) +#define JZ_ADC_REG_ADCMD_XNGRU BIT(21) +#define JZ_ADC_REG_ADCMD_XPGRU BIT(22) +#define JZ_ADC_REG_ADCMD_YPSUP BIT(23) +#define JZ_ADC_REG_ADCMD_XNSUP BIT(24) +#define JZ_ADC_REG_ADCMD_XPSUP BIT(25) + +#define JZ_ADC_AUX_VREF 3300 +#define JZ_ADC_AUX_VREF_BITS 12 +#define JZ_ADC_BATTERY_LOW_VREF 2500 +#define JZ_ADC_BATTERY_LOW_VREF_BITS 12 +#define JZ4725B_ADC_BATTERY_HIGH_VREF 7500 +#define JZ4725B_ADC_BATTERY_HIGH_VREF_BITS 10 +#define JZ4740_ADC_BATTERY_HIGH_VREF (7500 * 0.986) +#define JZ4740_ADC_BATTERY_HIGH_VREF_BITS 12 +#define JZ4760_ADC_BATTERY_VREF 2500 +#define JZ4770_ADC_BATTERY_VREF 1200 +#define JZ4770_ADC_BATTERY_VREF_BITS 12 + +#define JZ_ADC_IRQ_AUX BIT(0) +#define JZ_ADC_IRQ_BATTERY BIT(1) +#define JZ_ADC_IRQ_TOUCH BIT(2) +#define JZ_ADC_IRQ_PEN_DOWN BIT(3) +#define JZ_ADC_IRQ_PEN_UP BIT(4) +#define JZ_ADC_IRQ_PEN_DOWN_SLEEP BIT(5) +#define JZ_ADC_IRQ_SLEEP BIT(7) + +struct ingenic_adc; + +struct ingenic_adc_soc_data { + unsigned int battery_high_vref; + unsigned int battery_high_vref_bits; + const int *battery_raw_avail; + size_t battery_raw_avail_size; + const int *battery_scale_avail; + size_t battery_scale_avail_size; + unsigned int battery_vref_mode: 1; + unsigned int has_aux_md: 1; + const struct iio_chan_spec *channels; + unsigned int num_channels; + int (*init_clk_div)(struct device *dev, struct ingenic_adc *adc); +}; + +struct ingenic_adc { + void __iomem *base; + struct clk *clk; + struct mutex lock; + struct mutex aux_lock; + const struct ingenic_adc_soc_data *soc_data; + bool low_vref_mode; +}; + +static void ingenic_adc_set_adcmd(struct iio_dev *iio_dev, unsigned long mask) +{ + struct ingenic_adc *adc = iio_priv(iio_dev); + + guard(mutex)(&adc->lock); + + /* Init ADCMD */ + readl(adc->base + JZ_ADC_REG_ADCMD); + + if (mask & 0x3) { + /* Second channel (INGENIC_ADC_TOUCH_YP): sample YP vs. GND */ + writel(JZ_ADC_REG_ADCMD_XNGRU + | JZ_ADC_REG_ADCMD_VREFNXN | JZ_ADC_REG_ADCMD_VREFPVDD33 + | JZ_ADC_REG_ADCMD_YPADC, + adc->base + JZ_ADC_REG_ADCMD); + + /* First channel (INGENIC_ADC_TOUCH_XP): sample XP vs. GND */ + writel(JZ_ADC_REG_ADCMD_YNGRU + | JZ_ADC_REG_ADCMD_VREFNYN | JZ_ADC_REG_ADCMD_VREFPVDD33 + | JZ_ADC_REG_ADCMD_XPADC, + adc->base + JZ_ADC_REG_ADCMD); + } + + if (mask & 0xc) { + /* Fourth channel (INGENIC_ADC_TOUCH_YN): sample YN vs. GND */ + writel(JZ_ADC_REG_ADCMD_XNGRU + | JZ_ADC_REG_ADCMD_VREFNXN | JZ_ADC_REG_ADCMD_VREFPVDD33 + | JZ_ADC_REG_ADCMD_YNADC, + adc->base + JZ_ADC_REG_ADCMD); + + /* Third channel (INGENIC_ADC_TOUCH_XN): sample XN vs. GND */ + writel(JZ_ADC_REG_ADCMD_YNGRU + | JZ_ADC_REG_ADCMD_VREFNYN | JZ_ADC_REG_ADCMD_VREFPVDD33 + | JZ_ADC_REG_ADCMD_XNADC, + adc->base + JZ_ADC_REG_ADCMD); + } + + if (mask & 0x30) { + /* Sixth channel (INGENIC_ADC_TOUCH_YD): sample YP vs. YN */ + writel(JZ_ADC_REG_ADCMD_VREFNYN | JZ_ADC_REG_ADCMD_VREFPVDD33 + | JZ_ADC_REG_ADCMD_YPADC, + adc->base + JZ_ADC_REG_ADCMD); + + /* Fifth channel (INGENIC_ADC_TOUCH_XD): sample XP vs. XN */ + writel(JZ_ADC_REG_ADCMD_VREFNXN | JZ_ADC_REG_ADCMD_VREFPVDD33 + | JZ_ADC_REG_ADCMD_XPADC, + adc->base + JZ_ADC_REG_ADCMD); + } + + /* We're done */ + writel(0, adc->base + JZ_ADC_REG_ADCMD); +} + +static void ingenic_adc_set_config(struct ingenic_adc *adc, + uint32_t mask, + uint32_t val) +{ + uint32_t cfg; + + guard(mutex)(&adc->lock); + + cfg = readl(adc->base + JZ_ADC_REG_CFG) & ~mask; + cfg |= val; + writel(cfg, adc->base + JZ_ADC_REG_CFG); +} + +static void __ingenic_adc_enable(struct ingenic_adc *adc, int engine, + bool enabled) +{ + u8 val; + + val = readb(adc->base + JZ_ADC_REG_ENABLE); + + if (enabled) + val |= BIT(engine); + else + val &= ~BIT(engine); + + writeb(val, adc->base + JZ_ADC_REG_ENABLE); +} + +static void ingenic_adc_enable(struct ingenic_adc *adc, + int engine, + bool enabled) +{ + guard(mutex)(&adc->lock); + __ingenic_adc_enable(adc, engine, enabled); +} + +static int ingenic_adc_capture(struct ingenic_adc *adc, + int engine) +{ + u32 cfg; + u8 val; + int ret; + + /* + * Disable CMD_SEL temporarily, because it causes wrong VBAT readings, + * probably due to the switch of VREF. We must keep the lock here to + * avoid races with the buffer enable/disable functions. + */ + guard(mutex)(&adc->lock); + cfg = readl(adc->base + JZ_ADC_REG_CFG); + writel(cfg & ~JZ_ADC_REG_CFG_CMD_SEL, adc->base + JZ_ADC_REG_CFG); + + __ingenic_adc_enable(adc, engine, true); + ret = readb_poll_timeout(adc->base + JZ_ADC_REG_ENABLE, val, + !(val & BIT(engine)), 250, 1000); + if (ret) + __ingenic_adc_enable(adc, engine, false); + + writel(cfg, adc->base + JZ_ADC_REG_CFG); + + return ret; +} + +static int ingenic_adc_write_raw(struct iio_dev *iio_dev, + struct iio_chan_spec const *chan, + int val, + int val2, + long m) +{ + struct ingenic_adc *adc = iio_priv(iio_dev); + struct device *dev = iio_dev->dev.parent; + int ret; + + switch (m) { + case IIO_CHAN_INFO_SCALE: + switch (chan->channel) { + case INGENIC_ADC_BATTERY: + if (!adc->soc_data->battery_vref_mode) + return -EINVAL; + + ret = clk_enable(adc->clk); + if (ret) { + dev_err(dev, "Failed to enable clock: %d\n", + ret); + return ret; + } + + if (val > JZ_ADC_BATTERY_LOW_VREF) { + ingenic_adc_set_config(adc, + JZ_ADC_REG_CFG_BAT_MD, + 0); + adc->low_vref_mode = false; + } else { + ingenic_adc_set_config(adc, + JZ_ADC_REG_CFG_BAT_MD, + JZ_ADC_REG_CFG_BAT_MD); + adc->low_vref_mode = true; + } + + clk_disable(adc->clk); + + return 0; + default: + return -EINVAL; + } + default: + return -EINVAL; + } +} + +static const int jz4725b_adc_battery_raw_avail[] = { + 0, 1, (1 << JZ_ADC_BATTERY_LOW_VREF_BITS) - 1, +}; + +static const int jz4725b_adc_battery_scale_avail[] = { + JZ4725B_ADC_BATTERY_HIGH_VREF, JZ4725B_ADC_BATTERY_HIGH_VREF_BITS, + JZ_ADC_BATTERY_LOW_VREF, JZ_ADC_BATTERY_LOW_VREF_BITS, +}; + +static const int jz4740_adc_battery_raw_avail[] = { + 0, 1, (1 << JZ_ADC_BATTERY_LOW_VREF_BITS) - 1, +}; + +static const int jz4740_adc_battery_scale_avail[] = { + JZ4740_ADC_BATTERY_HIGH_VREF, JZ4740_ADC_BATTERY_HIGH_VREF_BITS, + JZ_ADC_BATTERY_LOW_VREF, JZ_ADC_BATTERY_LOW_VREF_BITS, +}; + +static const int jz4760_adc_battery_scale_avail[] = { + JZ4760_ADC_BATTERY_VREF, JZ4770_ADC_BATTERY_VREF_BITS, +}; + +static const int jz4770_adc_battery_raw_avail[] = { + 0, 1, (1 << JZ4770_ADC_BATTERY_VREF_BITS) - 1, +}; + +static const int jz4770_adc_battery_scale_avail[] = { + JZ4770_ADC_BATTERY_VREF, JZ4770_ADC_BATTERY_VREF_BITS, +}; + +static int jz4725b_adc_init_clk_div(struct device *dev, struct ingenic_adc *adc) +{ + struct clk *parent_clk; + unsigned long parent_rate, rate; + unsigned int div_main, div_10us; + + parent_clk = clk_get_parent(adc->clk); + if (!parent_clk) { + dev_err(dev, "ADC clock has no parent\n"); + return -ENODEV; + } + parent_rate = clk_get_rate(parent_clk); + + /* + * The JZ4725B ADC works at 500 kHz to 8 MHz. + * We pick the highest rate possible. + * In practice we typically get 6 MHz, half of the 12 MHz EXT clock. + */ + div_main = DIV_ROUND_UP(parent_rate, 8000000); + div_main = clamp(div_main, 1u, 64u); + rate = parent_rate / div_main; + if (rate < 500000 || rate > 8000000) { + dev_err(dev, "No valid divider for ADC main clock\n"); + return -EINVAL; + } + + /* We also need a divider that produces a 10us clock. */ + div_10us = DIV_ROUND_UP(rate, 100000); + + writel(((div_10us - 1) << JZ4725B_ADC_REG_ADCLK_CLKDIV10US_LSB) | + (div_main - 1) << JZ_ADC_REG_ADCLK_CLKDIV_LSB, + adc->base + JZ_ADC_REG_ADCLK); + + return 0; +} + +static int jz4770_adc_init_clk_div(struct device *dev, struct ingenic_adc *adc) +{ + struct clk *parent_clk; + unsigned long parent_rate, rate; + unsigned int div_main, div_ms, div_10us; + + parent_clk = clk_get_parent(adc->clk); + if (!parent_clk) { + dev_err(dev, "ADC clock has no parent\n"); + return -ENODEV; + } + parent_rate = clk_get_rate(parent_clk); + + /* + * The JZ4770 ADC works at 20 kHz to 200 kHz. + * We pick the highest rate possible. + */ + div_main = DIV_ROUND_UP(parent_rate, 200000); + div_main = clamp(div_main, 1u, 256u); + rate = parent_rate / div_main; + if (rate < 20000 || rate > 200000) { + dev_err(dev, "No valid divider for ADC main clock\n"); + return -EINVAL; + } + + /* We also need a divider that produces a 10us clock. */ + div_10us = DIV_ROUND_UP(rate, 10000); + /* And another, which produces a 1ms clock. */ + div_ms = DIV_ROUND_UP(rate, 1000); + + writel(((div_ms - 1) << JZ4770_ADC_REG_ADCLK_CLKDIVMS_LSB) | + ((div_10us - 1) << JZ4770_ADC_REG_ADCLK_CLKDIV10US_LSB) | + (div_main - 1) << JZ_ADC_REG_ADCLK_CLKDIV_LSB, + adc->base + JZ_ADC_REG_ADCLK); + + return 0; +} + +static const struct iio_chan_spec jz4740_channels[] = { + { + .extend_name = "aux", + .type = IIO_VOLTAGE, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SCALE), + .indexed = 1, + .channel = INGENIC_ADC_AUX, + .scan_index = -1, + }, + { + .extend_name = "battery", + .type = IIO_VOLTAGE, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SCALE), + .info_mask_separate_available = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SCALE), + .indexed = 1, + .channel = INGENIC_ADC_BATTERY, + .scan_index = -1, + }, +}; + +static const struct iio_chan_spec jz4760_channels[] = { + { + .extend_name = "aux", + .type = IIO_VOLTAGE, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SCALE), + .indexed = 1, + .channel = INGENIC_ADC_AUX0, + .scan_index = -1, + }, + { + .extend_name = "aux1", + .type = IIO_VOLTAGE, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SCALE), + .indexed = 1, + .channel = INGENIC_ADC_AUX, + .scan_index = -1, + }, + { + .extend_name = "aux2", + .type = IIO_VOLTAGE, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SCALE), + .indexed = 1, + .channel = INGENIC_ADC_AUX2, + .scan_index = -1, + }, + { + .extend_name = "battery", + .type = IIO_VOLTAGE, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SCALE), + .info_mask_separate_available = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SCALE), + .indexed = 1, + .channel = INGENIC_ADC_BATTERY, + .scan_index = -1, + }, +}; + +static const struct iio_chan_spec jz4770_channels[] = { + { + .type = IIO_VOLTAGE, + .indexed = 1, + .channel = INGENIC_ADC_TOUCH_XP, + .scan_index = 0, + .scan_type = { + .sign = 'u', + .realbits = 12, + .storagebits = 16, + }, + }, + { + .type = IIO_VOLTAGE, + .indexed = 1, + .channel = INGENIC_ADC_TOUCH_YP, + .scan_index = 1, + .scan_type = { + .sign = 'u', + .realbits = 12, + .storagebits = 16, + }, + }, + { + .type = IIO_VOLTAGE, + .indexed = 1, + .channel = INGENIC_ADC_TOUCH_XN, + .scan_index = 2, + .scan_type = { + .sign = 'u', + .realbits = 12, + .storagebits = 16, + }, + }, + { + .type = IIO_VOLTAGE, + .indexed = 1, + .channel = INGENIC_ADC_TOUCH_YN, + .scan_index = 3, + .scan_type = { + .sign = 'u', + .realbits = 12, + .storagebits = 16, + }, + }, + { + .type = IIO_VOLTAGE, + .indexed = 1, + .channel = INGENIC_ADC_TOUCH_XD, + .scan_index = 4, + .scan_type = { + .sign = 'u', + .realbits = 12, + .storagebits = 16, + }, + }, + { + .type = IIO_VOLTAGE, + .indexed = 1, + .channel = INGENIC_ADC_TOUCH_YD, + .scan_index = 5, + .scan_type = { + .sign = 'u', + .realbits = 12, + .storagebits = 16, + }, + }, + { + .extend_name = "aux", + .type = IIO_VOLTAGE, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SCALE), + .indexed = 1, + .channel = INGENIC_ADC_AUX, + .scan_index = -1, + }, + { + .extend_name = "battery", + .type = IIO_VOLTAGE, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SCALE), + .info_mask_separate_available = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SCALE), + .indexed = 1, + .channel = INGENIC_ADC_BATTERY, + .scan_index = -1, + }, + { + .extend_name = "aux2", + .type = IIO_VOLTAGE, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SCALE), + .indexed = 1, + .channel = INGENIC_ADC_AUX2, + .scan_index = -1, + }, +}; + +static const struct ingenic_adc_soc_data jz4725b_adc_soc_data = { + .battery_high_vref = JZ4725B_ADC_BATTERY_HIGH_VREF, + .battery_high_vref_bits = JZ4725B_ADC_BATTERY_HIGH_VREF_BITS, + .battery_raw_avail = jz4725b_adc_battery_raw_avail, + .battery_raw_avail_size = ARRAY_SIZE(jz4725b_adc_battery_raw_avail), + .battery_scale_avail = jz4725b_adc_battery_scale_avail, + .battery_scale_avail_size = ARRAY_SIZE(jz4725b_adc_battery_scale_avail), + .battery_vref_mode = true, + .has_aux_md = false, + .channels = jz4740_channels, + .num_channels = ARRAY_SIZE(jz4740_channels), + .init_clk_div = jz4725b_adc_init_clk_div, +}; + +static const struct ingenic_adc_soc_data jz4740_adc_soc_data = { + .battery_high_vref = JZ4740_ADC_BATTERY_HIGH_VREF, + .battery_high_vref_bits = JZ4740_ADC_BATTERY_HIGH_VREF_BITS, + .battery_raw_avail = jz4740_adc_battery_raw_avail, + .battery_raw_avail_size = ARRAY_SIZE(jz4740_adc_battery_raw_avail), + .battery_scale_avail = jz4740_adc_battery_scale_avail, + .battery_scale_avail_size = ARRAY_SIZE(jz4740_adc_battery_scale_avail), + .battery_vref_mode = true, + .has_aux_md = false, + .channels = jz4740_channels, + .num_channels = ARRAY_SIZE(jz4740_channels), + .init_clk_div = NULL, /* no ADCLK register on JZ4740 */ +}; + +static const struct ingenic_adc_soc_data jz4760_adc_soc_data = { + .battery_high_vref = JZ4760_ADC_BATTERY_VREF, + .battery_high_vref_bits = JZ4770_ADC_BATTERY_VREF_BITS, + .battery_raw_avail = jz4770_adc_battery_raw_avail, + .battery_raw_avail_size = ARRAY_SIZE(jz4770_adc_battery_raw_avail), + .battery_scale_avail = jz4760_adc_battery_scale_avail, + .battery_scale_avail_size = ARRAY_SIZE(jz4760_adc_battery_scale_avail), + .battery_vref_mode = false, + .has_aux_md = true, + .channels = jz4760_channels, + .num_channels = ARRAY_SIZE(jz4760_channels), + .init_clk_div = jz4770_adc_init_clk_div, +}; + +static const struct ingenic_adc_soc_data jz4770_adc_soc_data = { + .battery_high_vref = JZ4770_ADC_BATTERY_VREF, + .battery_high_vref_bits = JZ4770_ADC_BATTERY_VREF_BITS, + .battery_raw_avail = jz4770_adc_battery_raw_avail, + .battery_raw_avail_size = ARRAY_SIZE(jz4770_adc_battery_raw_avail), + .battery_scale_avail = jz4770_adc_battery_scale_avail, + .battery_scale_avail_size = ARRAY_SIZE(jz4770_adc_battery_scale_avail), + .battery_vref_mode = false, + .has_aux_md = true, + .channels = jz4770_channels, + .num_channels = ARRAY_SIZE(jz4770_channels), + .init_clk_div = jz4770_adc_init_clk_div, +}; + +static int ingenic_adc_read_avail(struct iio_dev *iio_dev, + struct iio_chan_spec const *chan, + const int **vals, + int *type, + int *length, + long m) +{ + struct ingenic_adc *adc = iio_priv(iio_dev); + + switch (m) { + case IIO_CHAN_INFO_RAW: + *type = IIO_VAL_INT; + *length = adc->soc_data->battery_raw_avail_size; + *vals = adc->soc_data->battery_raw_avail; + return IIO_AVAIL_RANGE; + case IIO_CHAN_INFO_SCALE: + *type = IIO_VAL_FRACTIONAL_LOG2; + *length = adc->soc_data->battery_scale_avail_size; + *vals = adc->soc_data->battery_scale_avail; + return IIO_AVAIL_LIST; + default: + return -EINVAL; + } +} + +static int __ingenic_adc_read_chan(struct ingenic_adc *adc, + struct iio_chan_spec const *chan, + int *val) +{ + int cmd, ret, engine = (chan->channel == INGENIC_ADC_BATTERY); + + if (adc->soc_data->has_aux_md && engine == 0) { + switch (chan->channel) { + case INGENIC_ADC_AUX0: + cmd = 0; + break; + case INGENIC_ADC_AUX: + cmd = 1; + break; + case INGENIC_ADC_AUX2: + cmd = 2; + break; + } + + ingenic_adc_set_config(adc, JZ_ADC_REG_CFG_AUX_MD, cmd); + } + + ret = ingenic_adc_capture(adc, engine); + if (ret) + return ret; + + switch (chan->channel) { + case INGENIC_ADC_AUX0: + case INGENIC_ADC_AUX: + case INGENIC_ADC_AUX2: + *val = readw(adc->base + JZ_ADC_REG_ADSDAT); + break; + case INGENIC_ADC_BATTERY: + *val = readw(adc->base + JZ_ADC_REG_ADBDAT); + break; + } + + return IIO_VAL_INT; +} + +static int ingenic_adc_read_chan_info_raw(struct iio_dev *iio_dev, + struct iio_chan_spec const *chan, + int *val) +{ + struct ingenic_adc *adc = iio_priv(iio_dev); + int ret; + + ret = clk_enable(adc->clk); + if (ret) { + dev_err(iio_dev->dev.parent, "Failed to enable clock: %d\n", ret); + return ret; + } + + /* We cannot sample the aux channels in parallel. */ + scoped_guard(mutex, &adc->aux_lock) + ret = __ingenic_adc_read_chan(adc, chan, val); + + clk_disable(adc->clk); + + return ret; +} + +static int ingenic_adc_read_raw(struct iio_dev *iio_dev, + struct iio_chan_spec const *chan, + int *val, + int *val2, + long m) +{ + struct ingenic_adc *adc = iio_priv(iio_dev); + + switch (m) { + case IIO_CHAN_INFO_RAW: + return ingenic_adc_read_chan_info_raw(iio_dev, chan, val); + case IIO_CHAN_INFO_SCALE: + switch (chan->channel) { + case INGENIC_ADC_AUX0: + case INGENIC_ADC_AUX: + case INGENIC_ADC_AUX2: + *val = JZ_ADC_AUX_VREF; + *val2 = JZ_ADC_AUX_VREF_BITS; + break; + case INGENIC_ADC_BATTERY: + if (adc->low_vref_mode) { + *val = JZ_ADC_BATTERY_LOW_VREF; + *val2 = JZ_ADC_BATTERY_LOW_VREF_BITS; + } else { + *val = adc->soc_data->battery_high_vref; + *val2 = adc->soc_data->battery_high_vref_bits; + } + break; + } + + return IIO_VAL_FRACTIONAL_LOG2; + default: + return -EINVAL; + } +} + +static int ingenic_adc_fwnode_xlate(struct iio_dev *iio_dev, + const struct fwnode_reference_args *iiospec) +{ + int i; + + if (!iiospec->nargs) + return -EINVAL; + + for (i = 0; i < iio_dev->num_channels; ++i) + if (iio_dev->channels[i].channel == iiospec->args[0]) + return i; + + return -EINVAL; +} + +static const struct iio_info ingenic_adc_info = { + .write_raw = ingenic_adc_write_raw, + .read_raw = ingenic_adc_read_raw, + .read_avail = ingenic_adc_read_avail, + .fwnode_xlate = ingenic_adc_fwnode_xlate, +}; + +static int ingenic_adc_buffer_enable(struct iio_dev *iio_dev) +{ + struct ingenic_adc *adc = iio_priv(iio_dev); + int ret; + + ret = clk_enable(adc->clk); + if (ret) { + dev_err(iio_dev->dev.parent, "Failed to enable clock: %d\n", + ret); + return ret; + } + + /* It takes significant time for the touchscreen hw to stabilize. */ + msleep(50); + ingenic_adc_set_config(adc, JZ_ADC_REG_CFG_TOUCH_OPS_MASK, + JZ_ADC_REG_CFG_SAMPLE_NUM(4) | + JZ_ADC_REG_CFG_PULL_UP(4)); + + writew(80, adc->base + JZ_ADC_REG_ADWAIT); + writew(2, adc->base + JZ_ADC_REG_ADSAME); + writeb((u8)~JZ_ADC_IRQ_TOUCH, adc->base + JZ_ADC_REG_CTRL); + writel(0, adc->base + JZ_ADC_REG_ADTCH); + + ingenic_adc_set_config(adc, JZ_ADC_REG_CFG_CMD_SEL, + JZ_ADC_REG_CFG_CMD_SEL); + ingenic_adc_set_adcmd(iio_dev, iio_dev->active_scan_mask[0]); + + ingenic_adc_enable(adc, 2, true); + + return 0; +} + +static int ingenic_adc_buffer_disable(struct iio_dev *iio_dev) +{ + struct ingenic_adc *adc = iio_priv(iio_dev); + + ingenic_adc_enable(adc, 2, false); + + ingenic_adc_set_config(adc, JZ_ADC_REG_CFG_CMD_SEL, 0); + + writeb(0xff, adc->base + JZ_ADC_REG_CTRL); + writeb(0xff, adc->base + JZ_ADC_REG_STATUS); + ingenic_adc_set_config(adc, JZ_ADC_REG_CFG_TOUCH_OPS_MASK, 0); + writew(0, adc->base + JZ_ADC_REG_ADSAME); + writew(0, adc->base + JZ_ADC_REG_ADWAIT); + clk_disable(adc->clk); + + return 0; +} + +static const struct iio_buffer_setup_ops ingenic_buffer_setup_ops = { + .postenable = &ingenic_adc_buffer_enable, + .predisable = &ingenic_adc_buffer_disable +}; + +static irqreturn_t ingenic_adc_irq(int irq, void *data) +{ + struct iio_dev *iio_dev = data; + struct ingenic_adc *adc = iio_priv(iio_dev); + unsigned long mask = iio_dev->active_scan_mask[0]; + unsigned int i; + u32 tdat[3]; + + for (i = 0; i < ARRAY_SIZE(tdat); mask >>= 2, i++) { + if (mask & 0x3) + tdat[i] = readl(adc->base + JZ_ADC_REG_ADTCH); + else + tdat[i] = 0; + } + + iio_push_to_buffers(iio_dev, tdat); + writeb(JZ_ADC_IRQ_TOUCH, adc->base + JZ_ADC_REG_STATUS); + + return IRQ_HANDLED; +} + +static int ingenic_adc_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct iio_dev *iio_dev; + struct ingenic_adc *adc; + const struct ingenic_adc_soc_data *soc_data; + int irq, ret; + + soc_data = device_get_match_data(dev); + if (!soc_data) + return -EINVAL; + + iio_dev = devm_iio_device_alloc(dev, sizeof(*adc)); + if (!iio_dev) + return -ENOMEM; + + adc = iio_priv(iio_dev); + mutex_init(&adc->lock); + mutex_init(&adc->aux_lock); + adc->soc_data = soc_data; + + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return irq; + + ret = devm_request_irq(dev, irq, ingenic_adc_irq, 0, + dev_name(dev), iio_dev); + if (ret < 0) { + dev_err(dev, "Failed to request irq: %d\n", ret); + return ret; + } + + adc->base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(adc->base)) + return PTR_ERR(adc->base); + + adc->clk = devm_clk_get_prepared(dev, "adc"); + if (IS_ERR(adc->clk)) { + dev_err(dev, "Unable to get clock\n"); + return PTR_ERR(adc->clk); + } + + ret = clk_enable(adc->clk); + if (ret) { + dev_err(dev, "Failed to enable clock\n"); + return ret; + } + + /* Set clock dividers. */ + if (soc_data->init_clk_div) { + ret = soc_data->init_clk_div(dev, adc); + if (ret) { + clk_disable_unprepare(adc->clk); + return ret; + } + } + + /* Put hardware in a known passive state. */ + writeb(0x00, adc->base + JZ_ADC_REG_ENABLE); + writeb(0xff, adc->base + JZ_ADC_REG_CTRL); + + /* JZ4760B specific */ + if (device_property_present(dev, "ingenic,use-internal-divider")) + ingenic_adc_set_config(adc, JZ_ADC_REG_CFG_VBAT_SEL, + JZ_ADC_REG_CFG_VBAT_SEL); + else + ingenic_adc_set_config(adc, JZ_ADC_REG_CFG_VBAT_SEL, 0); + + usleep_range(2000, 3000); /* Must wait at least 2ms. */ + clk_disable(adc->clk); + + iio_dev->name = "jz-adc"; + iio_dev->modes = INDIO_DIRECT_MODE | INDIO_BUFFER_SOFTWARE; + iio_dev->setup_ops = &ingenic_buffer_setup_ops; + iio_dev->channels = soc_data->channels; + iio_dev->num_channels = soc_data->num_channels; + iio_dev->info = &ingenic_adc_info; + + ret = devm_iio_device_register(dev, iio_dev); + if (ret) + dev_err(dev, "Unable to register IIO device\n"); + + return ret; +} + +static const struct of_device_id ingenic_adc_of_match[] = { + { .compatible = "ingenic,jz4725b-adc", .data = &jz4725b_adc_soc_data, }, + { .compatible = "ingenic,jz4740-adc", .data = &jz4740_adc_soc_data, }, + { .compatible = "ingenic,jz4760-adc", .data = &jz4760_adc_soc_data, }, + { .compatible = "ingenic,jz4760b-adc", .data = &jz4760_adc_soc_data, }, + { .compatible = "ingenic,jz4770-adc", .data = &jz4770_adc_soc_data, }, + { } +}; +MODULE_DEVICE_TABLE(of, ingenic_adc_of_match); + +static struct platform_driver ingenic_adc_driver = { + .driver = { + .name = "ingenic-adc", + .of_match_table = ingenic_adc_of_match, + }, + .probe = ingenic_adc_probe, +}; +module_platform_driver(ingenic_adc_driver); +MODULE_DESCRIPTION("ADC driver for the Ingenic JZ47xx SoCs"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/adc/intel_dc_ti_adc.c b/drivers/iio/adc/intel_dc_ti_adc.c new file mode 100644 index 000000000..698a2a304 --- /dev/null +++ b/drivers/iio/adc/intel_dc_ti_adc.c @@ -0,0 +1,327 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Intel Dollar Cove TI PMIC GPADC Driver + * + * Copyright (C) 2014 Intel Corporation (Ramakrishna Pallala ) + * Copyright (C) 2024 - 2025 Hans de Goede + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#define DC_TI_ADC_CNTL_REG 0x50 +#define DC_TI_ADC_START BIT(0) +#define DC_TI_ADC_CH_SEL GENMASK(2, 1) +#define DC_TI_ADC_EN BIT(5) +#define DC_TI_ADC_EN_EXT_BPTH_BIAS BIT(6) + +#define DC_TI_VBAT_ZSE_GE_REG 0x53 +#define DC_TI_VBAT_GE GENMASK(3, 0) +#define DC_TI_VBAT_ZSE GENMASK(7, 4) + +/* VBAT GE gain correction is in 0.0015 increments, ZSE is in 1.0 increments */ +#define DC_TI_VBAT_GE_STEP 15 +#define DC_TI_VBAT_GE_DIV 10000 + +#define DC_TI_ADC_DATA_REG_CH(x) (0x54 + 2 * (x)) + +enum dc_ti_adc_id { + DC_TI_ADC_VBAT, + DC_TI_ADC_PMICTEMP, + DC_TI_ADC_BATTEMP, + DC_TI_ADC_SYSTEMP0, +}; + +struct dc_ti_adc_info { + struct mutex lock; /* Protects against concurrent accesses to the ADC */ + wait_queue_head_t wait; + struct device *dev; + struct regmap *regmap; + int vbat_zse; + int vbat_ge; + bool conversion_done; +}; + +static const struct iio_chan_spec dc_ti_adc_channels[] = { + { + .indexed = 1, + .type = IIO_VOLTAGE, + .channel = DC_TI_ADC_VBAT, + .address = DC_TI_ADC_DATA_REG_CH(0), + .datasheet_name = "CH0", + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SCALE) | + BIT(IIO_CHAN_INFO_PROCESSED), + }, { + .indexed = 1, + .type = IIO_TEMP, + .channel = DC_TI_ADC_PMICTEMP, + .address = DC_TI_ADC_DATA_REG_CH(1), + .datasheet_name = "CH1", + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), + }, { + .indexed = 1, + .type = IIO_TEMP, + .channel = DC_TI_ADC_BATTEMP, + .address = DC_TI_ADC_DATA_REG_CH(2), + .datasheet_name = "CH2", + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), + }, { + .indexed = 1, + .type = IIO_TEMP, + .channel = DC_TI_ADC_SYSTEMP0, + .address = DC_TI_ADC_DATA_REG_CH(3), + .datasheet_name = "CH3", + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), + } +}; + +static struct iio_map dc_ti_adc_default_maps[] = { + IIO_MAP("CH0", "chtdc_ti_battery", "VBAT"), + IIO_MAP("CH1", "chtdc_ti_battery", "PMICTEMP"), + IIO_MAP("CH2", "chtdc_ti_battery", "BATTEMP"), + IIO_MAP("CH3", "chtdc_ti_battery", "SYSTEMP0"), + { } +}; + +static irqreturn_t dc_ti_adc_isr(int irq, void *data) +{ + struct dc_ti_adc_info *info = data; + + info->conversion_done = true; + wake_up(&info->wait); + return IRQ_HANDLED; +} + +static int dc_ti_adc_scale(struct dc_ti_adc_info *info, + struct iio_chan_spec const *chan, + int *val, int *val2) +{ + if (chan->channel != DC_TI_ADC_VBAT) + return -EINVAL; + + /* Vbat ADC scale is 4.6875 mV / unit */ + *val = 4; + *val2 = 687500; + + return IIO_VAL_INT_PLUS_MICRO; +} + +static int dc_ti_adc_raw_to_processed(struct dc_ti_adc_info *info, + struct iio_chan_spec const *chan, + int raw, int *val, int *val2) +{ + if (chan->channel != DC_TI_ADC_VBAT) + return -EINVAL; + + /* Apply calibration */ + raw -= info->vbat_zse; + raw = raw * (DC_TI_VBAT_GE_DIV - info->vbat_ge * DC_TI_VBAT_GE_STEP) / + DC_TI_VBAT_GE_DIV; + /* Vbat ADC scale is 4.6875 mV / unit */ + raw *= 46875; + + /* raw is now in 10000 units / mV, convert to milli + milli/1e6 */ + *val = raw / 10000; + *val2 = (raw % 10000) * 100; + + return IIO_VAL_INT_PLUS_MICRO; +} + +static int dc_ti_adc_sample(struct dc_ti_adc_info *info, + struct iio_chan_spec const *chan, int *val) +{ + int ret, ch = chan->channel; + __be16 buf; + + info->conversion_done = false; + + /* + * As per TI (PMIC Vendor), the ADC enable and ADC start commands should + * not be sent together. Hence send the commands separately. + */ + ret = regmap_set_bits(info->regmap, DC_TI_ADC_CNTL_REG, DC_TI_ADC_EN); + if (ret) + return ret; + + ret = regmap_update_bits(info->regmap, DC_TI_ADC_CNTL_REG, + DC_TI_ADC_CH_SEL, + FIELD_PREP(DC_TI_ADC_CH_SEL, ch)); + if (ret) + return ret; + + /* + * As per PMIC Vendor, a minimum of 50 ųs delay is required between ADC + * Enable and ADC START commands. This is also recommended by Intel + * Hardware team after the timing analysis of GPADC signals. Since the + * I2C Write transaction to set the channel number also imparts 25 ųs + * delay, we need to wait for another 25 ųs before issuing ADC START. + */ + fsleep(25); + + ret = regmap_set_bits(info->regmap, DC_TI_ADC_CNTL_REG, + DC_TI_ADC_START); + if (ret) + return ret; + + /* TI (PMIC Vendor) recommends 5 s timeout for conversion */ + ret = wait_event_timeout(info->wait, info->conversion_done, 5 * HZ); + if (ret == 0) { + ret = -ETIMEDOUT; + goto disable_adc; + } + + ret = regmap_bulk_read(info->regmap, chan->address, &buf, sizeof(buf)); + if (ret) + goto disable_adc; + + /* The ADC values are 10 bits wide */ + *val = be16_to_cpu(buf) & GENMASK(9, 0); + +disable_adc: + regmap_clear_bits(info->regmap, DC_TI_ADC_CNTL_REG, + DC_TI_ADC_START | DC_TI_ADC_EN); + return ret; +} + +static int dc_ti_adc_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct dc_ti_adc_info *info = iio_priv(indio_dev); + int ret; + + if (mask == IIO_CHAN_INFO_SCALE) + return dc_ti_adc_scale(info, chan, val, val2); + + guard(mutex)(&info->lock); + + /* + * If channel BPTHERM has been selected, first enable the BPTHERM BIAS + * which provides the VREF Voltage reference to convert BPTHERM Input + * voltage to temperature. + */ + if (chan->channel == DC_TI_ADC_BATTEMP) { + ret = regmap_set_bits(info->regmap, DC_TI_ADC_CNTL_REG, + DC_TI_ADC_EN_EXT_BPTH_BIAS); + if (ret) + return ret; + /* + * As per PMIC Vendor specifications, BPTHERM BIAS should be + * enabled 35 ms before ADC_EN command. + */ + msleep(35); + } + + ret = dc_ti_adc_sample(info, chan, val); + + if (chan->channel == DC_TI_ADC_BATTEMP) + regmap_clear_bits(info->regmap, DC_TI_ADC_CNTL_REG, + DC_TI_ADC_EN_EXT_BPTH_BIAS); + + if (ret) + return ret; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + return IIO_VAL_INT; + case IIO_CHAN_INFO_PROCESSED: + return dc_ti_adc_raw_to_processed(info, chan, *val, val, val2); + } + + return -EINVAL; +} + +static const struct iio_info dc_ti_adc_iio_info = { + .read_raw = dc_ti_adc_read_raw, +}; + +static int dc_ti_adc_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct intel_soc_pmic *pmic = dev_get_drvdata(dev->parent); + struct dc_ti_adc_info *info; + struct iio_dev *indio_dev; + unsigned int val; + int irq, ret; + + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return irq; + + indio_dev = devm_iio_device_alloc(dev, sizeof(*info)); + if (!indio_dev) + return -ENOMEM; + + info = iio_priv(indio_dev); + + ret = devm_mutex_init(dev, &info->lock); + if (ret) + return ret; + + init_waitqueue_head(&info->wait); + + info->dev = dev; + info->regmap = pmic->regmap; + + indio_dev->name = "dc_ti_adc"; + indio_dev->channels = dc_ti_adc_channels; + indio_dev->num_channels = ARRAY_SIZE(dc_ti_adc_channels); + indio_dev->info = &dc_ti_adc_iio_info; + indio_dev->modes = INDIO_DIRECT_MODE; + + ret = regmap_read(info->regmap, DC_TI_VBAT_ZSE_GE_REG, &val); + if (ret) + return ret; + + info->vbat_zse = FIELD_GET_SIGNED(DC_TI_VBAT_ZSE, val); + info->vbat_ge = FIELD_GET_SIGNED(DC_TI_VBAT_GE, val); + + dev_dbg(dev, "vbat-zse %d vbat-ge %d\n", info->vbat_zse, info->vbat_ge); + + ret = devm_iio_map_array_register(dev, indio_dev, dc_ti_adc_default_maps); + if (ret) + return ret; + + ret = devm_request_threaded_irq(dev, irq, NULL, dc_ti_adc_isr, + IRQF_ONESHOT, indio_dev->name, info); + if (ret) + return ret; + + return devm_iio_device_register(dev, indio_dev); +} + +static const struct platform_device_id dc_ti_adc_ids[] = { + { .name = "chtdc_ti_adc" }, + { } +}; +MODULE_DEVICE_TABLE(platform, dc_ti_adc_ids); + +static struct platform_driver dc_ti_adc_driver = { + .driver = { + .name = "dc_ti_adc", + }, + .probe = dc_ti_adc_probe, + .id_table = dc_ti_adc_ids, +}; +module_platform_driver(dc_ti_adc_driver); + +MODULE_AUTHOR("Ramakrishna Pallala (Intel)"); +MODULE_AUTHOR("Hans de Goede "); +MODULE_DESCRIPTION("Intel Dollar Cove (TI) GPADC Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/iio/adc/intel_mrfld_adc.c b/drivers/iio/adc/intel_mrfld_adc.c new file mode 100644 index 000000000..ff34e5979 --- /dev/null +++ b/drivers/iio/adc/intel_mrfld_adc.c @@ -0,0 +1,241 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * ADC driver for Basin Cove PMIC + * + * Copyright (C) 2012 Intel Corporation + * Author: Bin Yang + * + * Rewritten for upstream by: + * Vincent Pelletier + * Andy Shevchenko + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +#define BCOVE_GPADCREQ 0xDC +#define BCOVE_GPADCREQ_BUSY BIT(0) +#define BCOVE_GPADCREQ_IRQEN BIT(1) + +#define BCOVE_ADCIRQ_ALL ( \ + BCOVE_ADCIRQ_BATTEMP | \ + BCOVE_ADCIRQ_SYSTEMP | \ + BCOVE_ADCIRQ_BATTID | \ + BCOVE_ADCIRQ_VIBATT | \ + BCOVE_ADCIRQ_CCTICK) + +#define BCOVE_ADC_TIMEOUT msecs_to_jiffies(1000) + +static const u8 mrfld_adc_requests[] = { + BCOVE_ADCIRQ_VIBATT, + BCOVE_ADCIRQ_BATTID, + BCOVE_ADCIRQ_VIBATT, + BCOVE_ADCIRQ_SYSTEMP, + BCOVE_ADCIRQ_BATTEMP, + BCOVE_ADCIRQ_BATTEMP, + BCOVE_ADCIRQ_SYSTEMP, + BCOVE_ADCIRQ_SYSTEMP, + BCOVE_ADCIRQ_SYSTEMP, +}; + +struct mrfld_adc { + struct regmap *regmap; + struct completion completion; + /* Lock to protect the IPC transfers */ + struct mutex lock; +}; + +static irqreturn_t mrfld_adc_thread_isr(int irq, void *data) +{ + struct iio_dev *indio_dev = data; + struct mrfld_adc *adc = iio_priv(indio_dev); + + complete(&adc->completion); + return IRQ_HANDLED; +} + +static int mrfld_adc_single_conv(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *result) +{ + struct mrfld_adc *adc = iio_priv(indio_dev); + struct regmap *regmap = adc->regmap; + unsigned int req; + long time_left; + __be16 value; + int ret; + + reinit_completion(&adc->completion); + + regmap_clear_bits(regmap, BCOVE_MADCIRQ, BCOVE_ADCIRQ_ALL); + regmap_clear_bits(regmap, BCOVE_MIRQLVL1, BCOVE_LVL1_ADC); + + ret = regmap_read_poll_timeout(regmap, BCOVE_GPADCREQ, req, + !(req & BCOVE_GPADCREQ_BUSY), + 2000, 1000000); + if (ret) + goto done; + + req = mrfld_adc_requests[chan->channel]; + ret = regmap_write(regmap, BCOVE_GPADCREQ, BCOVE_GPADCREQ_IRQEN | req); + if (ret) + goto done; + + time_left = wait_for_completion_interruptible_timeout(&adc->completion, + BCOVE_ADC_TIMEOUT); + if (time_left < 0) { + ret = time_left; + goto done; + } + if (time_left == 0) { + ret = -ETIMEDOUT; + goto done; + } + + ret = regmap_bulk_read(regmap, chan->address, &value, sizeof(value)); + if (ret) + goto done; + + *result = be16_to_cpu(value); + ret = IIO_VAL_INT; + +done: + regmap_update_bits(regmap, BCOVE_MIRQLVL1, BCOVE_LVL1_ADC, 0xff); + regmap_update_bits(regmap, BCOVE_MADCIRQ, BCOVE_ADCIRQ_ALL, 0xff); + + return ret; +} + +static int mrfld_adc_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct mrfld_adc *adc = iio_priv(indio_dev); + int ret; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + mutex_lock(&adc->lock); + ret = mrfld_adc_single_conv(indio_dev, chan, val); + mutex_unlock(&adc->lock); + return ret; + default: + return -EINVAL; + } +} + +static const struct iio_info mrfld_adc_iio_info = { + .read_raw = &mrfld_adc_read_raw, +}; + +#define BCOVE_ADC_CHANNEL(_type, _channel, _datasheet_name, _address) \ + { \ + .indexed = 1, \ + .type = _type, \ + .channel = _channel, \ + .address = _address, \ + .datasheet_name = _datasheet_name, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + } + +static const struct iio_chan_spec mrfld_adc_channels[] = { + BCOVE_ADC_CHANNEL(IIO_VOLTAGE, 0, "CH0", 0xE9), + BCOVE_ADC_CHANNEL(IIO_RESISTANCE, 1, "CH1", 0xEB), + BCOVE_ADC_CHANNEL(IIO_CURRENT, 2, "CH2", 0xED), + BCOVE_ADC_CHANNEL(IIO_TEMP, 3, "CH3", 0xCC), + BCOVE_ADC_CHANNEL(IIO_TEMP, 4, "CH4", 0xC8), + BCOVE_ADC_CHANNEL(IIO_TEMP, 5, "CH5", 0xCA), + BCOVE_ADC_CHANNEL(IIO_TEMP, 6, "CH6", 0xC2), + BCOVE_ADC_CHANNEL(IIO_TEMP, 7, "CH7", 0xC4), + BCOVE_ADC_CHANNEL(IIO_TEMP, 8, "CH8", 0xC6), +}; + +static const struct iio_map iio_maps[] = { + IIO_MAP("CH0", "bcove-battery", "VBATRSLT"), + IIO_MAP("CH1", "bcove-battery", "BATTID"), + IIO_MAP("CH2", "bcove-battery", "IBATRSLT"), + IIO_MAP("CH3", "bcove-temp", "PMICTEMP"), + IIO_MAP("CH4", "bcove-temp", "BATTEMP0"), + IIO_MAP("CH5", "bcove-temp", "BATTEMP1"), + IIO_MAP("CH6", "bcove-temp", "SYSTEMP0"), + IIO_MAP("CH7", "bcove-temp", "SYSTEMP1"), + IIO_MAP("CH8", "bcove-temp", "SYSTEMP2"), + { } +}; + +static int mrfld_adc_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct intel_soc_pmic *pmic = dev_get_drvdata(dev->parent); + struct iio_dev *indio_dev; + struct mrfld_adc *adc; + int irq; + int ret; + + indio_dev = devm_iio_device_alloc(dev, sizeof(struct mrfld_adc)); + if (!indio_dev) + return -ENOMEM; + + adc = iio_priv(indio_dev); + + mutex_init(&adc->lock); + init_completion(&adc->completion); + adc->regmap = pmic->regmap; + + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return irq; + + ret = devm_request_threaded_irq(dev, irq, NULL, mrfld_adc_thread_isr, + IRQF_ONESHOT | IRQF_SHARED, pdev->name, + indio_dev); + if (ret) + return ret; + + indio_dev->name = pdev->name; + + indio_dev->channels = mrfld_adc_channels; + indio_dev->num_channels = ARRAY_SIZE(mrfld_adc_channels); + indio_dev->info = &mrfld_adc_iio_info; + indio_dev->modes = INDIO_DIRECT_MODE; + + ret = devm_iio_map_array_register(dev, indio_dev, iio_maps); + if (ret) + return ret; + + return devm_iio_device_register(dev, indio_dev); +} + +static const struct platform_device_id mrfld_adc_id_table[] = { + { .name = "mrfld_bcove_adc" }, + { } +}; +MODULE_DEVICE_TABLE(platform, mrfld_adc_id_table); + +static struct platform_driver mrfld_adc_driver = { + .driver = { + .name = "mrfld_bcove_adc", + }, + .probe = mrfld_adc_probe, + .id_table = mrfld_adc_id_table, +}; +module_platform_driver(mrfld_adc_driver); + +MODULE_AUTHOR("Bin Yang "); +MODULE_AUTHOR("Vincent Pelletier "); +MODULE_AUTHOR("Andy Shevchenko "); +MODULE_DESCRIPTION("ADC driver for Basin Cove PMIC"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/adc/lp8788_adc.c b/drivers/iio/adc/lp8788_adc.c new file mode 100644 index 000000000..33bf8aef7 --- /dev/null +++ b/drivers/iio/adc/lp8788_adc.c @@ -0,0 +1,219 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * TI LP8788 MFD - ADC driver + * + * Copyright 2012 Texas Instruments + * + * Author: Milo(Woogyom) Kim + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* register address */ +#define LP8788_ADC_CONF 0x60 +#define LP8788_ADC_RAW 0x61 +#define LP8788_ADC_DONE 0x63 + +#define ADC_CONV_START 1 + +struct lp8788_adc { + struct lp8788 *lp; + const struct iio_map *map; + struct mutex lock; +}; + +static const int lp8788_scale[LPADC_MAX] = { + [LPADC_VBATT_5P5] = 1343101, + [LPADC_VIN_CHG] = 3052503, + [LPADC_IBATT] = 610500, + [LPADC_IC_TEMP] = 61050, + [LPADC_VBATT_6P0] = 1465201, + [LPADC_VBATT_5P0] = 1221001, + [LPADC_ADC1] = 610500, + [LPADC_ADC2] = 610500, + [LPADC_VDD] = 1025641, + [LPADC_VCOIN] = 757020, + [LPADC_ADC3] = 610500, + [LPADC_ADC4] = 610500, +}; + +static int lp8788_get_adc_result(struct lp8788_adc *adc, enum lp8788_adc_id id, + int *val) +{ + unsigned int msb; + unsigned int lsb; + unsigned int result; + u8 data; + u8 rawdata[2]; + int size = ARRAY_SIZE(rawdata); + int retry = 5; + int ret; + + data = (id << 1) | ADC_CONV_START; + ret = lp8788_write_byte(adc->lp, LP8788_ADC_CONF, data); + if (ret) + goto err_io; + + /* retry until adc conversion is done */ + data = 0; + while (retry--) { + usleep_range(100, 200); + + ret = lp8788_read_byte(adc->lp, LP8788_ADC_DONE, &data); + if (ret) + goto err_io; + + /* conversion done */ + if (data) + break; + } + + ret = lp8788_read_multi_bytes(adc->lp, LP8788_ADC_RAW, rawdata, size); + if (ret) + goto err_io; + + msb = (rawdata[0] << 4) & 0x00000ff0; + lsb = (rawdata[1] >> 4) & 0x0000000f; + result = msb | lsb; + *val = result; + + return 0; + +err_io: + return ret; +} + +static int lp8788_adc_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct lp8788_adc *adc = iio_priv(indio_dev); + enum lp8788_adc_id id = chan->channel; + int ret; + + mutex_lock(&adc->lock); + + switch (mask) { + case IIO_CHAN_INFO_RAW: + ret = lp8788_get_adc_result(adc, id, val) ? -EIO : IIO_VAL_INT; + break; + case IIO_CHAN_INFO_SCALE: + *val = lp8788_scale[id] / 1000000; + *val2 = lp8788_scale[id] % 1000000; + ret = IIO_VAL_INT_PLUS_MICRO; + break; + default: + ret = -EINVAL; + break; + } + + mutex_unlock(&adc->lock); + + return ret; +} + +static const struct iio_info lp8788_adc_info = { + .read_raw = &lp8788_adc_read_raw, +}; + +#define LP8788_CHAN(_id, _type) { \ + .type = _type, \ + .indexed = 1, \ + .channel = LPADC_##_id, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ + BIT(IIO_CHAN_INFO_SCALE), \ + .datasheet_name = #_id, \ +} + +static const struct iio_chan_spec lp8788_adc_channels[] = { + [LPADC_VBATT_5P5] = LP8788_CHAN(VBATT_5P5, IIO_VOLTAGE), + [LPADC_VIN_CHG] = LP8788_CHAN(VIN_CHG, IIO_VOLTAGE), + [LPADC_IBATT] = LP8788_CHAN(IBATT, IIO_CURRENT), + [LPADC_IC_TEMP] = LP8788_CHAN(IC_TEMP, IIO_TEMP), + [LPADC_VBATT_6P0] = LP8788_CHAN(VBATT_6P0, IIO_VOLTAGE), + [LPADC_VBATT_5P0] = LP8788_CHAN(VBATT_5P0, IIO_VOLTAGE), + [LPADC_ADC1] = LP8788_CHAN(ADC1, IIO_VOLTAGE), + [LPADC_ADC2] = LP8788_CHAN(ADC2, IIO_VOLTAGE), + [LPADC_VDD] = LP8788_CHAN(VDD, IIO_VOLTAGE), + [LPADC_VCOIN] = LP8788_CHAN(VCOIN, IIO_VOLTAGE), + [LPADC_ADC3] = LP8788_CHAN(ADC3, IIO_VOLTAGE), + [LPADC_ADC4] = LP8788_CHAN(ADC4, IIO_VOLTAGE), +}; + +/* default maps used by iio consumer (lp8788-charger driver) */ +static const struct iio_map lp8788_default_iio_maps[] = { + IIO_MAP("VBATT_5P0", "lp8788-charger", "lp8788_vbatt_5p0"), + IIO_MAP("ADC1", "lp8788-charger", "lp8788_adc1"), + { } +}; + +static int lp8788_iio_map_register(struct device *dev, + struct iio_dev *indio_dev, + struct lp8788_platform_data *pdata, + struct lp8788_adc *adc) +{ + const struct iio_map *map; + int ret; + + map = (!pdata || !pdata->adc_pdata) ? + lp8788_default_iio_maps : pdata->adc_pdata; + + ret = devm_iio_map_array_register(dev, indio_dev, map); + if (ret) { + dev_err(&indio_dev->dev, "iio map err: %d\n", ret); + return ret; + } + + adc->map = map; + return 0; +} + +static int lp8788_adc_probe(struct platform_device *pdev) +{ + struct lp8788 *lp = dev_get_drvdata(pdev->dev.parent); + struct iio_dev *indio_dev; + struct lp8788_adc *adc; + int ret; + + indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*adc)); + if (!indio_dev) + return -ENOMEM; + + adc = iio_priv(indio_dev); + adc->lp = lp; + + ret = lp8788_iio_map_register(&pdev->dev, indio_dev, lp->pdata, adc); + if (ret) + return ret; + + mutex_init(&adc->lock); + + indio_dev->name = pdev->name; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->info = &lp8788_adc_info; + indio_dev->channels = lp8788_adc_channels; + indio_dev->num_channels = ARRAY_SIZE(lp8788_adc_channels); + + return devm_iio_device_register(&pdev->dev, indio_dev); +} + +static struct platform_driver lp8788_adc_driver = { + .probe = lp8788_adc_probe, + .driver = { + .name = LP8788_DEV_ADC, + }, +}; +module_platform_driver(lp8788_adc_driver); + +MODULE_DESCRIPTION("Texas Instruments LP8788 ADC Driver"); +MODULE_AUTHOR("Milo Kim"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:lp8788-adc"); diff --git a/drivers/iio/adc/lpc18xx_adc.c b/drivers/iio/adc/lpc18xx_adc.c new file mode 100644 index 000000000..2cf2519f5 --- /dev/null +++ b/drivers/iio/adc/lpc18xx_adc.c @@ -0,0 +1,205 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * IIO ADC driver for NXP LPC18xx ADC + * + * Copyright (C) 2016 Joachim Eastwood + * + * UNSUPPORTED hardware features: + * - Hardware triggers + * - Burst mode + * - Interrupts + * - DMA + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* LPC18XX ADC registers and bits */ +#define LPC18XX_ADC_CR 0x000 +#define LPC18XX_ADC_CR_CLKDIV_SHIFT 8 +#define LPC18XX_ADC_CR_PDN BIT(21) +#define LPC18XX_ADC_CR_START_NOW (0x1 << 24) +#define LPC18XX_ADC_GDR 0x004 + +/* Data register bits */ +#define LPC18XX_ADC_SAMPLE_SHIFT 6 +#define LPC18XX_ADC_SAMPLE_MASK 0x3ff +#define LPC18XX_ADC_CONV_DONE BIT(31) + +/* Clock should be 4.5 MHz or less */ +#define LPC18XX_ADC_CLK_TARGET 4500000 + +struct lpc18xx_adc { + struct regulator *vref; + void __iomem *base; + struct device *dev; + struct mutex lock; + struct clk *clk; + u32 cr_reg; +}; + +#define LPC18XX_ADC_CHAN(_idx) { \ + .type = IIO_VOLTAGE, \ + .indexed = 1, \ + .channel = _idx, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ +} + +static const struct iio_chan_spec lpc18xx_adc_iio_channels[] = { + LPC18XX_ADC_CHAN(0), + LPC18XX_ADC_CHAN(1), + LPC18XX_ADC_CHAN(2), + LPC18XX_ADC_CHAN(3), + LPC18XX_ADC_CHAN(4), + LPC18XX_ADC_CHAN(5), + LPC18XX_ADC_CHAN(6), + LPC18XX_ADC_CHAN(7), +}; + +static int lpc18xx_adc_read_chan(struct lpc18xx_adc *adc, unsigned int ch) +{ + int ret; + u32 reg; + + reg = adc->cr_reg | BIT(ch) | LPC18XX_ADC_CR_START_NOW; + writel(reg, adc->base + LPC18XX_ADC_CR); + + ret = readl_poll_timeout(adc->base + LPC18XX_ADC_GDR, reg, + reg & LPC18XX_ADC_CONV_DONE, 3, 9); + if (ret) { + dev_warn(adc->dev, "adc read timed out\n"); + return ret; + } + + return (reg >> LPC18XX_ADC_SAMPLE_SHIFT) & LPC18XX_ADC_SAMPLE_MASK; +} + +static int lpc18xx_adc_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct lpc18xx_adc *adc = iio_priv(indio_dev); + + switch (mask) { + case IIO_CHAN_INFO_RAW: + mutex_lock(&adc->lock); + *val = lpc18xx_adc_read_chan(adc, chan->channel); + mutex_unlock(&adc->lock); + if (*val < 0) + return *val; + + return IIO_VAL_INT; + + case IIO_CHAN_INFO_SCALE: + *val = regulator_get_voltage(adc->vref) / 1000; + *val2 = 10; + + return IIO_VAL_FRACTIONAL_LOG2; + } + + return -EINVAL; +} + +static const struct iio_info lpc18xx_adc_info = { + .read_raw = lpc18xx_adc_read_raw, +}; + +static void lpc18xx_clear_cr_reg(void *data) +{ + struct lpc18xx_adc *adc = data; + + writel(0, adc->base + LPC18XX_ADC_CR); +} + +static void lpc18xx_regulator_disable(void *vref) +{ + regulator_disable(vref); +} + +static int lpc18xx_adc_probe(struct platform_device *pdev) +{ + struct iio_dev *indio_dev; + struct lpc18xx_adc *adc; + unsigned int clkdiv; + unsigned long rate; + int ret; + + indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*adc)); + if (!indio_dev) + return -ENOMEM; + + adc = iio_priv(indio_dev); + adc->dev = &pdev->dev; + mutex_init(&adc->lock); + + adc->base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(adc->base)) + return PTR_ERR(adc->base); + + adc->clk = devm_clk_get_enabled(&pdev->dev, NULL); + if (IS_ERR(adc->clk)) + return dev_err_probe(&pdev->dev, PTR_ERR(adc->clk), + "error getting clock\n"); + + adc->vref = devm_regulator_get(&pdev->dev, "vref"); + if (IS_ERR(adc->vref)) + return dev_err_probe(&pdev->dev, PTR_ERR(adc->vref), + "error getting regulator\n"); + + indio_dev->name = dev_name(&pdev->dev); + indio_dev->info = &lpc18xx_adc_info; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = lpc18xx_adc_iio_channels; + indio_dev->num_channels = ARRAY_SIZE(lpc18xx_adc_iio_channels); + + ret = regulator_enable(adc->vref); + if (ret) { + dev_err(&pdev->dev, "unable to enable regulator\n"); + return ret; + } + + ret = devm_add_action_or_reset(&pdev->dev, lpc18xx_regulator_disable, adc->vref); + if (ret) + return ret; + + rate = clk_get_rate(adc->clk); + clkdiv = DIV_ROUND_UP(rate, LPC18XX_ADC_CLK_TARGET); + + adc->cr_reg = (clkdiv << LPC18XX_ADC_CR_CLKDIV_SHIFT) | + LPC18XX_ADC_CR_PDN; + writel(adc->cr_reg, adc->base + LPC18XX_ADC_CR); + + ret = devm_add_action_or_reset(&pdev->dev, lpc18xx_clear_cr_reg, adc); + if (ret) + return ret; + + return devm_iio_device_register(&pdev->dev, indio_dev); +} + +static const struct of_device_id lpc18xx_adc_match[] = { + { .compatible = "nxp,lpc1850-adc" }, + { } +}; +MODULE_DEVICE_TABLE(of, lpc18xx_adc_match); + +static struct platform_driver lpc18xx_adc_driver = { + .probe = lpc18xx_adc_probe, + .driver = { + .name = "lpc18xx-adc", + .of_match_table = lpc18xx_adc_match, + }, +}; +module_platform_driver(lpc18xx_adc_driver); + +MODULE_DESCRIPTION("LPC18xx ADC driver"); +MODULE_AUTHOR("Joachim Eastwood "); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/adc/lpc32xx_adc.c b/drivers/iio/adc/lpc32xx_adc.c new file mode 100644 index 000000000..32a15d193 --- /dev/null +++ b/drivers/iio/adc/lpc32xx_adc.c @@ -0,0 +1,235 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * lpc32xx_adc.c - Support for ADC in LPC32XX + * + * 3-channel, 10-bit ADC + * + * Copyright (C) 2011, 2012 Roland Stigge + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * LPC32XX registers definitions + */ +#define LPC32XXAD_SELECT(x) ((x) + 0x04) +#define LPC32XXAD_CTRL(x) ((x) + 0x08) +#define LPC32XXAD_VALUE(x) ((x) + 0x48) + +/* Bit definitions for LPC32XXAD_SELECT: */ +/* constant, always write this value! */ +#define LPC32XXAD_REFm 0x00000200 +/* constant, always write this value! */ +#define LPC32XXAD_REFp 0x00000080 + /* multiple of this is the channel number: 0, 1, 2 */ +#define LPC32XXAD_IN 0x00000010 +/* constant, always write this value! */ +#define LPC32XXAD_INTERNAL 0x00000004 + +/* Bit definitions for LPC32XXAD_CTRL: */ +#define LPC32XXAD_STROBE 0x00000002 +#define LPC32XXAD_PDN_CTRL 0x00000004 + +/* Bit definitions for LPC32XXAD_VALUE: */ +#define LPC32XXAD_VALUE_MASK 0x000003FF + +#define LPC32XXAD_NAME "lpc32xx-adc" + +struct lpc32xx_adc_state { + void __iomem *adc_base; + struct clk *clk; + struct completion completion; + struct regulator *vref; + /* lock to protect against multiple access to the device */ + struct mutex lock; + + u32 value; +}; + +static int lpc32xx_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, + int *val2, + long mask) +{ + struct lpc32xx_adc_state *st = iio_priv(indio_dev); + int ret; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + mutex_lock(&st->lock); + ret = clk_prepare_enable(st->clk); + if (ret) { + mutex_unlock(&st->lock); + return ret; + } + /* Measurement setup */ + __raw_writel(LPC32XXAD_INTERNAL | (chan->address) | + LPC32XXAD_REFp | LPC32XXAD_REFm, + LPC32XXAD_SELECT(st->adc_base)); + /* Trigger conversion */ + __raw_writel(LPC32XXAD_PDN_CTRL | LPC32XXAD_STROBE, + LPC32XXAD_CTRL(st->adc_base)); + wait_for_completion(&st->completion); /* set by ISR */ + clk_disable_unprepare(st->clk); + *val = st->value; + mutex_unlock(&st->lock); + + return IIO_VAL_INT; + + case IIO_CHAN_INFO_SCALE: + *val = regulator_get_voltage(st->vref) / 1000; + *val2 = 10; + + return IIO_VAL_FRACTIONAL_LOG2; + default: + return -EINVAL; + } +} + +static const struct iio_info lpc32xx_adc_iio_info = { + .read_raw = &lpc32xx_read_raw, +}; + +#define LPC32XX_ADC_CHANNEL_BASE(_index) \ + .type = IIO_VOLTAGE, \ + .indexed = 1, \ + .channel = _index, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .address = LPC32XXAD_IN * _index, \ + .scan_index = _index, + +#define LPC32XX_ADC_CHANNEL(_index) { \ + LPC32XX_ADC_CHANNEL_BASE(_index) \ +} + +#define LPC32XX_ADC_SCALE_CHANNEL(_index) { \ + LPC32XX_ADC_CHANNEL_BASE(_index) \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) \ +} + +static const struct iio_chan_spec lpc32xx_adc_iio_channels[] = { + LPC32XX_ADC_CHANNEL(0), + LPC32XX_ADC_CHANNEL(1), + LPC32XX_ADC_CHANNEL(2), +}; + +static const struct iio_chan_spec lpc32xx_adc_iio_scale_channels[] = { + LPC32XX_ADC_SCALE_CHANNEL(0), + LPC32XX_ADC_SCALE_CHANNEL(1), + LPC32XX_ADC_SCALE_CHANNEL(2), +}; + +static irqreturn_t lpc32xx_adc_isr(int irq, void *dev_id) +{ + struct lpc32xx_adc_state *st = dev_id; + + /* Read value and clear irq */ + st->value = __raw_readl(LPC32XXAD_VALUE(st->adc_base)) & + LPC32XXAD_VALUE_MASK; + complete(&st->completion); + + return IRQ_HANDLED; +} + +static int lpc32xx_adc_probe(struct platform_device *pdev) +{ + struct lpc32xx_adc_state *st = NULL; + struct resource *res; + int retval = -ENODEV; + struct iio_dev *iodev = NULL; + int irq; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(&pdev->dev, "failed to get platform I/O memory\n"); + return -ENXIO; + } + + iodev = devm_iio_device_alloc(&pdev->dev, sizeof(*st)); + if (!iodev) + return -ENOMEM; + + st = iio_priv(iodev); + + st->adc_base = devm_ioremap(&pdev->dev, res->start, + resource_size(res)); + if (!st->adc_base) { + dev_err(&pdev->dev, "failed mapping memory\n"); + return -EBUSY; + } + + st->clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(st->clk)) { + dev_err(&pdev->dev, "failed getting clock\n"); + return PTR_ERR(st->clk); + } + + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return irq; + + init_completion(&st->completion); + + retval = devm_request_irq(&pdev->dev, irq, lpc32xx_adc_isr, 0, + LPC32XXAD_NAME, st); + if (retval < 0) { + dev_err(&pdev->dev, "failed requesting interrupt\n"); + return retval; + } + + st->vref = devm_regulator_get(&pdev->dev, "vref"); + if (IS_ERR(st->vref)) { + iodev->channels = lpc32xx_adc_iio_channels; + dev_info(&pdev->dev, + "Missing vref regulator: No scaling available\n"); + } else { + iodev->channels = lpc32xx_adc_iio_scale_channels; + } + + platform_set_drvdata(pdev, iodev); + + iodev->name = LPC32XXAD_NAME; + iodev->info = &lpc32xx_adc_iio_info; + iodev->modes = INDIO_DIRECT_MODE; + iodev->num_channels = ARRAY_SIZE(lpc32xx_adc_iio_channels); + + mutex_init(&st->lock); + + retval = devm_iio_device_register(&pdev->dev, iodev); + if (retval) + return retval; + + dev_info(&pdev->dev, "LPC32XX ADC driver loaded, IRQ %d\n", irq); + + return 0; +} + +static const struct of_device_id lpc32xx_adc_match[] = { + { .compatible = "nxp,lpc3220-adc" }, + { } +}; +MODULE_DEVICE_TABLE(of, lpc32xx_adc_match); + +static struct platform_driver lpc32xx_adc_driver = { + .probe = lpc32xx_adc_probe, + .driver = { + .name = LPC32XXAD_NAME, + .of_match_table = lpc32xx_adc_match, + }, +}; + +module_platform_driver(lpc32xx_adc_driver); + +MODULE_AUTHOR("Roland Stigge "); +MODULE_DESCRIPTION("LPC32XX ADC driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/iio/adc/ltc2309.c b/drivers/iio/adc/ltc2309.c new file mode 100644 index 000000000..1ad533270 --- /dev/null +++ b/drivers/iio/adc/ltc2309.c @@ -0,0 +1,264 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * The LTC2305 is a 2-Channel, 12-Bit SAR ADC with an I2C Interface. + * The LTC2309 is an 8-Channel, 12-Bit SAR ADC with an I2C Interface. + * + * Datasheet: + * https://www.analog.com/media/en/technical-documentation/data-sheets/23015fb.pdf + * https://www.analog.com/media/en/technical-documentation/data-sheets/2309fd.pdf + * + * Copyright (c) 2023, Liam Beguin + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define LTC2309_ADC_RESOLUTION 12 +#define LTC2309_INTERNAL_REF_MV 4096 + +#define LTC2309_DIN_CH_MASK GENMASK(7, 4) +#define LTC2309_DIN_SDN BIT(7) +#define LTC2309_DIN_OSN BIT(6) +#define LTC2309_DIN_S1 BIT(5) +#define LTC2309_DIN_S0 BIT(4) +#define LTC2309_DIN_UNI BIT(3) +#define LTC2309_DIN_SLEEP BIT(2) + +/** + * struct ltc2309 - internal device data structure + * @dev: Device reference + * @client: I2C reference + * @lock: Lock to serialize data access + * @vref_mv: Internal voltage reference + * @read_delay_us: Chip-specific read delay in microseconds + */ +struct ltc2309 { + struct device *dev; + struct i2c_client *client; + struct mutex lock; /* serialize data access */ + int vref_mv; + unsigned int read_delay_us; +}; + +/* Order matches expected channel address, See datasheet Table 1. */ +enum ltc2305_channels { + LTC2305_CH0_CH1 = 0x0, + LTC2305_CH1_CH0 = 0x4, + LTC2305_CH0 = 0x8, + LTC2305_CH1 = 0xc, +}; + +enum ltc2309_channels { + LTC2309_CH0_CH1 = 0x0, + LTC2309_CH2_CH3 = 0x1, + LTC2309_CH4_CH5 = 0x2, + LTC2309_CH6_CH7 = 0x3, + LTC2309_CH1_CH0 = 0x4, + LTC2309_CH3_CH2 = 0x5, + LTC2309_CH5_CH4 = 0x6, + LTC2309_CH7_CH6 = 0x7, + LTC2309_CH0 = 0x8, + LTC2309_CH2 = 0x9, + LTC2309_CH4 = 0xa, + LTC2309_CH6 = 0xb, + LTC2309_CH1 = 0xc, + LTC2309_CH3 = 0xd, + LTC2309_CH5 = 0xe, + LTC2309_CH7 = 0xf, +}; + +#define LTC2309_CHAN(_chan, _addr) { \ + .type = IIO_VOLTAGE, \ + .indexed = 1, \ + .address = _addr, \ + .channel = _chan, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ +} + +#define LTC2309_DIFF_CHAN(_chan, _chan2, _addr) { \ + .type = IIO_VOLTAGE, \ + .differential = 1, \ + .indexed = 1, \ + .address = _addr, \ + .channel = _chan, \ + .channel2 = _chan2, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ +} + +static const struct iio_chan_spec ltc2305_channels[] = { + LTC2309_CHAN(0, LTC2305_CH0), + LTC2309_CHAN(1, LTC2305_CH1), + LTC2309_DIFF_CHAN(0, 1, LTC2305_CH0_CH1), + LTC2309_DIFF_CHAN(1, 0, LTC2305_CH1_CH0), +}; + +static const struct iio_chan_spec ltc2309_channels[] = { + LTC2309_CHAN(0, LTC2309_CH0), + LTC2309_CHAN(1, LTC2309_CH1), + LTC2309_CHAN(2, LTC2309_CH2), + LTC2309_CHAN(3, LTC2309_CH3), + LTC2309_CHAN(4, LTC2309_CH4), + LTC2309_CHAN(5, LTC2309_CH5), + LTC2309_CHAN(6, LTC2309_CH6), + LTC2309_CHAN(7, LTC2309_CH7), + LTC2309_DIFF_CHAN(0, 1, LTC2309_CH0_CH1), + LTC2309_DIFF_CHAN(2, 3, LTC2309_CH2_CH3), + LTC2309_DIFF_CHAN(4, 5, LTC2309_CH4_CH5), + LTC2309_DIFF_CHAN(6, 7, LTC2309_CH6_CH7), + LTC2309_DIFF_CHAN(1, 0, LTC2309_CH1_CH0), + LTC2309_DIFF_CHAN(3, 2, LTC2309_CH3_CH2), + LTC2309_DIFF_CHAN(5, 4, LTC2309_CH5_CH4), + LTC2309_DIFF_CHAN(7, 6, LTC2309_CH7_CH6), +}; + +struct ltc2309_chip_info { + const char *name; + unsigned int read_delay_us; + int num_channels; + const struct iio_chan_spec *channels __counted_by_ptr(num_channels); +}; + +static const struct ltc2309_chip_info ltc2305_chip_info = { + .name = "ltc2305", + .read_delay_us = 2, + .num_channels = ARRAY_SIZE(ltc2305_channels), + .channels = ltc2305_channels, +}; + +static const struct ltc2309_chip_info ltc2309_chip_info = { + .name = "ltc2309", + .num_channels = ARRAY_SIZE(ltc2309_channels), + .channels = ltc2309_channels, +}; + +static int ltc2309_read_raw_channel(struct ltc2309 *ltc2309, + unsigned long address, int *val) +{ + int ret; + __be16 buf; + u8 din; + + din = FIELD_PREP(LTC2309_DIN_CH_MASK, address & 0x0f) | + FIELD_PREP(LTC2309_DIN_UNI, 1) | + FIELD_PREP(LTC2309_DIN_SLEEP, 0); + + ret = i2c_smbus_write_byte(ltc2309->client, din); + if (ret < 0) { + dev_err(ltc2309->dev, "i2c command failed: %pe\n", + ERR_PTR(ret)); + return ret; + } + + if (ltc2309->read_delay_us) + fsleep(ltc2309->read_delay_us); + + ret = i2c_master_recv(ltc2309->client, (char *)&buf, 2); + if (ret < 0) { + dev_err(ltc2309->dev, "i2c read failed: %pe\n", ERR_PTR(ret)); + return ret; + } + + *val = be16_to_cpu(buf) >> 4; + + return ret; +} + +static int ltc2309_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int *val, + int *val2, long mask) +{ + struct ltc2309 *ltc2309 = iio_priv(indio_dev); + int ret; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + mutex_lock(<c2309->lock); + ret = ltc2309_read_raw_channel(ltc2309, chan->address, val); + mutex_unlock(<c2309->lock); + if (ret < 0) + return -EINVAL; + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + *val = ltc2309->vref_mv; + *val2 = LTC2309_ADC_RESOLUTION; + return IIO_VAL_FRACTIONAL_LOG2; + default: + return -EINVAL; + } +} + +static const struct iio_info ltc2309_info = { + .read_raw = ltc2309_read_raw, +}; + +static int ltc2309_probe(struct i2c_client *client) +{ + const struct ltc2309_chip_info *chip_info; + struct iio_dev *indio_dev; + struct ltc2309 *ltc2309; + int ret; + + indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*ltc2309)); + if (!indio_dev) + return -ENOMEM; + + ltc2309 = iio_priv(indio_dev); + chip_info = i2c_get_match_data(client); + + ltc2309->dev = &indio_dev->dev; + ltc2309->client = client; + ltc2309->read_delay_us = chip_info->read_delay_us; + + indio_dev->name = chip_info->name; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = chip_info->channels; + indio_dev->num_channels = chip_info->num_channels; + indio_dev->info = <c2309_info; + + ret = devm_regulator_get_enable_read_voltage(&client->dev, "vref"); + if (ret < 0 && ret != -ENODEV) + return dev_err_probe(ltc2309->dev, ret, + "failed to get vref voltage\n"); + + ltc2309->vref_mv = ret == -ENODEV ? LTC2309_INTERNAL_REF_MV : ret / 1000; + + mutex_init(<c2309->lock); + + return devm_iio_device_register(&client->dev, indio_dev); +} + +static const struct of_device_id ltc2309_of_match[] = { + { .compatible = "lltc,ltc2305", .data = <c2305_chip_info }, + { .compatible = "lltc,ltc2309", .data = <c2309_chip_info }, + { } +}; +MODULE_DEVICE_TABLE(of, ltc2309_of_match); + +static const struct i2c_device_id ltc2309_id[] = { + { .name = "ltc2305", .driver_data = (kernel_ulong_t)<c2305_chip_info }, + { .name = "ltc2309", .driver_data = (kernel_ulong_t)<c2309_chip_info }, + { } +}; +MODULE_DEVICE_TABLE(i2c, ltc2309_id); + +static struct i2c_driver ltc2309_driver = { + .driver = { + .name = "ltc2309", + .of_match_table = ltc2309_of_match, + }, + .probe = ltc2309_probe, + .id_table = ltc2309_id, +}; +module_i2c_driver(ltc2309_driver); + +MODULE_AUTHOR("Liam Beguin "); +MODULE_DESCRIPTION("Linear Technology LTC2309 ADC"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/adc/ltc2378.c b/drivers/iio/adc/ltc2378.c new file mode 100644 index 000000000..b0ff2be1a --- /dev/null +++ b/drivers/iio/adc/ltc2378.c @@ -0,0 +1,857 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Analog Devices LTC2378 ADC series driver + * + * Copyright (C) 2026 Analog Devices Inc. + * Author: Marcelo Schmitt + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#define LTC2378_TDSDOBUSYL_NS 5 +#define LTC2378_TBUSYLH_NS 13 +#define LTC2378_TCNV_HIGH_NS 20 +#define LTC2378_MAX_DATA_WAIT_US 4 /* max(TBUSYLH + TCONV + TDSDOBUSYL) */ + +#define LTC2378_CHANNEL(_sign, _real_bits, _storage_bits) \ +{ \ + .type = IIO_VOLTAGE, \ + .indexed = 1, \ + .differential = _sign, \ + .channel = 0, \ + .channel2 = _sign ? 1 : 0, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ + BIT(IIO_CHAN_INFO_SCALE), \ + .scan_index = 0, \ + .scan_type = { \ + .format = _sign ? IIO_SCAN_FORMAT_SIGNED_INT : \ + IIO_SCAN_FORMAT_UNSIGNED_INT, \ + .realbits = _real_bits, \ + .storagebits = _storage_bits, \ + .shift = _storage_bits - _real_bits, \ + .endianness = IIO_BE, \ + }, \ +} + +#define LTC2378_DIFF_CHANNEL(_real_bits) \ + LTC2378_CHANNEL(1, _real_bits, (((_real_bits) > 16) ? 32 : 16)) + +#define LTC2378_PSEUDO_DIFF_CHANNEL(_real_bits) \ + LTC2378_CHANNEL(0, _real_bits, (((_real_bits) > 16) ? 32 : 16)) + +#define LTC2378_OFFLOAD_CHANNEL(_sign, _real_bits, _storage_bits) \ +{ \ + .type = IIO_VOLTAGE, \ + .indexed = 1, \ + .differential = _sign, \ + .channel = 0, \ + .channel2 = _sign ? 1 : 0, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ + BIT(IIO_CHAN_INFO_SCALE) | \ + BIT(IIO_CHAN_INFO_SAMP_FREQ), \ + .info_mask_separate_available = BIT(IIO_CHAN_INFO_SAMP_FREQ), \ + .scan_index = 0, \ + .scan_type = { \ + .format = _sign ? IIO_SCAN_FORMAT_SIGNED_INT : \ + IIO_SCAN_FORMAT_UNSIGNED_INT, \ + .realbits = _real_bits, \ + .storagebits = _storage_bits, \ + .shift = 0, \ + .endianness = IIO_CPU, \ + }, \ +} + +/* + * Currently, the available offload hardware + DMA configuration only supports + * pushing 32-bit data elements to DMA IIO buffers in CPU endianness. For 16-bit + * precision parts, those 32-bit elements (in CPU endianness) contain 2 bytes + * with data and 2 bytes always zeroed out. Nevertheless, for the offload use + * case, the IIO buffer is configured for 32 storage bits in CPU endianness so + * data is correctly aligned in user space despite 2 out of the 4 bytes being + * zeros. + */ +#define LTC2378_OFFLOAD_DIFF_CHANNEL(_real_bits) \ + LTC2378_OFFLOAD_CHANNEL(1, (_real_bits), 32) + +#define LTC2378_OFFLOAD_PSEUDO_DIFF_CHANNEL(_real_bits) \ + LTC2378_OFFLOAD_CHANNEL(0, (_real_bits), 32) + +struct ltc2378_chip_info { + const char *name; + unsigned int internal_ref_uV; + struct u32_fract internal_div; + struct iio_chan_spec chan[2]; /* 1 physical chan + 1 timestamp chan */ + struct iio_chan_spec offload_chan; + unsigned int max_sample_rate_Hz; + unsigned int tconv_ns; +}; + +struct ltc2378_state { + const struct ltc2378_chip_info *info; + struct gpio_desc *cnv_gpio; + struct spi_device *spi; + struct mutex lock; /* Protect data acquisition cycle */ + int ref_uV; + struct spi_transfer xfer; + struct spi_transfer offload_xfer; + struct spi_offload *offload; + struct spi_offload_trigger *offload_trigger; + struct pwm_waveform cnv_wf; + struct spi_message offload_msg; + struct spi_offload_trigger_config offload_trigger_config; + struct pwm_device *cnv_trigger; + unsigned int cnv_Hz; + unsigned int sample_freq_range[3]; + + /* + * DMA (thus cache coherency maintenance) requires the transfer buffers + * to live in their own cache lines. + */ + struct { + union { + __be16 sample_buf16_be; + __be32 sample_buf32_be; + u16 sample_buf16; + u32 sample_buf32; + } data; + aligned_s64 timestamp; + } scan __aligned(IIO_DMA_MINALIGN); +}; + +static const struct ltc2378_chip_info ltc2338_18_chip_info = { + .name = "ltc2338-18", + .internal_ref_uV = 2048000, + .internal_div = { .numerator = 5, .denominator = 2 }, + .chan = { LTC2378_DIFF_CHANNEL(18), IIO_CHAN_SOFT_TIMESTAMP(1) }, + .offload_chan = LTC2378_OFFLOAD_DIFF_CHANNEL(18), + .max_sample_rate_Hz = 1 * HZ_PER_MHZ, + .tconv_ns = 527, +}; + +static const struct ltc2378_chip_info ltc2364_16_chip_info = { + .name = "ltc2364-16", + .chan = { LTC2378_PSEUDO_DIFF_CHANNEL(16), IIO_CHAN_SOFT_TIMESTAMP(1) }, + .offload_chan = LTC2378_OFFLOAD_PSEUDO_DIFF_CHANNEL(16), + .max_sample_rate_Hz = 250 * HZ_PER_KHZ, + .tconv_ns = 3000, +}; + +static const struct ltc2378_chip_info ltc2364_18_chip_info = { + .name = "ltc2364-18", + .chan = { LTC2378_PSEUDO_DIFF_CHANNEL(18), IIO_CHAN_SOFT_TIMESTAMP(1) }, + .offload_chan = LTC2378_OFFLOAD_PSEUDO_DIFF_CHANNEL(18), + .max_sample_rate_Hz = 250 * HZ_PER_KHZ, + .tconv_ns = 3000, +}; + +static const struct ltc2378_chip_info ltc2367_16_chip_info = { + .name = "ltc2367-16", + .chan = { LTC2378_PSEUDO_DIFF_CHANNEL(16), IIO_CHAN_SOFT_TIMESTAMP(1) }, + .offload_chan = LTC2378_OFFLOAD_PSEUDO_DIFF_CHANNEL(16), + .max_sample_rate_Hz = 500 * HZ_PER_KHZ, + .tconv_ns = 1500, +}; + +static const struct ltc2378_chip_info ltc2367_18_chip_info = { + .name = "ltc2367-18", + .chan = { LTC2378_PSEUDO_DIFF_CHANNEL(18), IIO_CHAN_SOFT_TIMESTAMP(1) }, + .offload_chan = LTC2378_OFFLOAD_PSEUDO_DIFF_CHANNEL(18), + .max_sample_rate_Hz = 500 * HZ_PER_KHZ, + .tconv_ns = 1500, +}; + +static const struct ltc2378_chip_info ltc2368_16_chip_info = { + .name = "ltc2368-16", + .chan = { LTC2378_PSEUDO_DIFF_CHANNEL(16), IIO_CHAN_SOFT_TIMESTAMP(1) }, + .offload_chan = LTC2378_OFFLOAD_PSEUDO_DIFF_CHANNEL(16), + .max_sample_rate_Hz = 1 * HZ_PER_MHZ, + .tconv_ns = 527, +}; + +static const struct ltc2378_chip_info ltc2368_18_chip_info = { + .name = "ltc2368-18", + .chan = { LTC2378_PSEUDO_DIFF_CHANNEL(18), IIO_CHAN_SOFT_TIMESTAMP(1) }, + .offload_chan = LTC2378_OFFLOAD_PSEUDO_DIFF_CHANNEL(18), + .max_sample_rate_Hz = 1 * HZ_PER_MHZ, + .tconv_ns = 527, +}; + +static const struct ltc2378_chip_info ltc2369_18_chip_info = { + .name = "ltc2369-18", + .chan = { LTC2378_PSEUDO_DIFF_CHANNEL(18), IIO_CHAN_SOFT_TIMESTAMP(1) }, + .offload_chan = LTC2378_OFFLOAD_PSEUDO_DIFF_CHANNEL(18), + .max_sample_rate_Hz = 1600 * HZ_PER_KHZ, + .tconv_ns = 412, +}; + +static const struct ltc2378_chip_info ltc2370_16_chip_info = { + .name = "ltc2370-16", + .chan = { LTC2378_PSEUDO_DIFF_CHANNEL(16), IIO_CHAN_SOFT_TIMESTAMP(1) }, + .offload_chan = LTC2378_OFFLOAD_PSEUDO_DIFF_CHANNEL(16), + .max_sample_rate_Hz = 2 * HZ_PER_MHZ, + .tconv_ns = 322, +}; + +static const struct ltc2378_chip_info ltc2376_16_chip_info = { + .name = "ltc2376-16", + .chan = { LTC2378_DIFF_CHANNEL(16), IIO_CHAN_SOFT_TIMESTAMP(1) }, + .offload_chan = LTC2378_OFFLOAD_DIFF_CHANNEL(16), + .max_sample_rate_Hz = 250 * HZ_PER_KHZ, + .tconv_ns = 3000, +}; + +static const struct ltc2378_chip_info ltc2376_18_chip_info = { + .name = "ltc2376-18", + .chan = { LTC2378_DIFF_CHANNEL(18), IIO_CHAN_SOFT_TIMESTAMP(1) }, + .offload_chan = LTC2378_OFFLOAD_DIFF_CHANNEL(18), + .max_sample_rate_Hz = 250 * HZ_PER_KHZ, + .tconv_ns = 3000, +}; + +static const struct ltc2378_chip_info ltc2376_20_chip_info = { + .name = "ltc2376-20", + .chan = { LTC2378_DIFF_CHANNEL(20), IIO_CHAN_SOFT_TIMESTAMP(1) }, + .offload_chan = LTC2378_OFFLOAD_DIFF_CHANNEL(20), + .max_sample_rate_Hz = 250 * HZ_PER_KHZ, + .tconv_ns = 3000, +}; + +static const struct ltc2378_chip_info ltc2377_16_chip_info = { + .name = "ltc2377-16", + .chan = { LTC2378_DIFF_CHANNEL(16), IIO_CHAN_SOFT_TIMESTAMP(1) }, + .offload_chan = LTC2378_OFFLOAD_DIFF_CHANNEL(16), + .max_sample_rate_Hz = 500 * HZ_PER_KHZ, + .tconv_ns = 1500, +}; + +static const struct ltc2378_chip_info ltc2377_18_chip_info = { + .name = "ltc2377-18", + .chan = { LTC2378_DIFF_CHANNEL(18), IIO_CHAN_SOFT_TIMESTAMP(1) }, + .offload_chan = LTC2378_OFFLOAD_DIFF_CHANNEL(18), + .max_sample_rate_Hz = 500 * HZ_PER_KHZ, + .tconv_ns = 1500, +}; + +static const struct ltc2378_chip_info ltc2377_20_chip_info = { + .name = "ltc2377-20", + .chan = { LTC2378_DIFF_CHANNEL(20), IIO_CHAN_SOFT_TIMESTAMP(1) }, + .offload_chan = LTC2378_OFFLOAD_DIFF_CHANNEL(20), + .max_sample_rate_Hz = 500 * HZ_PER_KHZ, + .tconv_ns = 1500, +}; + +static const struct ltc2378_chip_info ltc2378_16_chip_info = { + .name = "ltc2378-16", + .chan = { LTC2378_DIFF_CHANNEL(16), IIO_CHAN_SOFT_TIMESTAMP(1) }, + .offload_chan = LTC2378_OFFLOAD_DIFF_CHANNEL(16), + .max_sample_rate_Hz = 1 * HZ_PER_MHZ, + .tconv_ns = 527, +}; + +static const struct ltc2378_chip_info ltc2378_18_chip_info = { + .name = "ltc2378-18", + .chan = { LTC2378_DIFF_CHANNEL(18), IIO_CHAN_SOFT_TIMESTAMP(1) }, + .offload_chan = LTC2378_OFFLOAD_DIFF_CHANNEL(18), + .max_sample_rate_Hz = 1 * HZ_PER_MHZ, + .tconv_ns = 527, +}; + +static const struct ltc2378_chip_info ltc2378_20_chip_info = { + .name = "ltc2378-20", + .chan = { LTC2378_DIFF_CHANNEL(20), IIO_CHAN_SOFT_TIMESTAMP(1) }, + .offload_chan = LTC2378_OFFLOAD_DIFF_CHANNEL(20), + .max_sample_rate_Hz = 1 * HZ_PER_MHZ, + .tconv_ns = 675, +}; + +static const struct ltc2378_chip_info ltc2379_18_chip_info = { + .name = "ltc2379-18", + .chan = { LTC2378_DIFF_CHANNEL(18), IIO_CHAN_SOFT_TIMESTAMP(1) }, + .offload_chan = LTC2378_OFFLOAD_DIFF_CHANNEL(18), + .max_sample_rate_Hz = 1600 * HZ_PER_KHZ, + .tconv_ns = 412, +}; + +static const struct ltc2378_chip_info ltc2380_16_chip_info = { + .name = "ltc2380-16", + .chan = { LTC2378_DIFF_CHANNEL(16), IIO_CHAN_SOFT_TIMESTAMP(1) }, + .offload_chan = LTC2378_OFFLOAD_DIFF_CHANNEL(16), + .max_sample_rate_Hz = 2 * HZ_PER_MHZ, + .tconv_ns = 322, +}; + +static int ltc2378_convert_and_acquire(struct ltc2378_state *st) +{ + int ret; + + /* Cause a rising edge of CNV to initiate a new ADC conversion */ + gpiod_set_value_cansleep(st->cnv_gpio, 1); + fsleep(LTC2378_MAX_DATA_WAIT_US); + ret = spi_sync_transfer(st->spi, &st->xfer, 1); + gpiod_set_value_cansleep(st->cnv_gpio, 0); + + return ret; +} + +static irqreturn_t ltc2378_trigger_handler(int irq, void *p) +{ + struct iio_poll_func *pf = p; + struct iio_dev *indio_dev = pf->indio_dev; + struct ltc2378_state *st = iio_priv(indio_dev); + int ret; + + ret = ltc2378_convert_and_acquire(st); + if (ret < 0) + goto err_out; + + iio_push_to_buffers_with_ts(indio_dev, &st->scan, sizeof(st->scan), + pf->timestamp); + +err_out: + iio_trigger_notify_done(indio_dev->trig); + return IRQ_HANDLED; +} + +static int ltc2378_channel_single_read(const struct iio_chan_spec *chan, + struct ltc2378_state *st, int *val) +{ + const struct iio_scan_type *scan_type = &chan->scan_type; + u32 sample; + int ret; + + guard(mutex)(&st->lock); + ret = ltc2378_convert_and_acquire(st); + if (ret) + return ret; + + if (chan->scan_type.endianness == IIO_BE) { + if (chan->scan_type.realbits > 16) + sample = be32_to_cpu(st->scan.data.sample_buf32_be); + else + sample = be16_to_cpu(st->scan.data.sample_buf16_be); + } else { /* IIO_CPU */ + if (chan->scan_type.realbits > 16) + sample = st->scan.data.sample_buf32; + else + sample = st->scan.data.sample_buf16; + } + + sample >>= chan->scan_type.shift; + + if (scan_type->format == IIO_SCAN_FORMAT_SIGNED_INT) + *val = sign_extend32(sample, scan_type->realbits - 1); + else + *val = sample; + + return 0; +} + +static int ltc2378_read_raw(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + int *val, int *val2, long mask) +{ + struct ltc2378_state *st = iio_priv(indio_dev); + int ret; + + switch (mask) { + case IIO_CHAN_INFO_RAW: { + IIO_DEV_ACQUIRE_DIRECT_MODE(indio_dev, claim); + if (IIO_DEV_ACQUIRE_FAILED(claim)) + return -EBUSY; + + ret = ltc2378_channel_single_read(chan, st, val); + if (ret) + return ret; + + return IIO_VAL_INT; + } + case IIO_CHAN_INFO_SCALE: { + struct u32_fract fract = st->info->internal_div; + *val = st->ref_uV / MILLI; + if (fract.numerator && fract.denominator) + *val = mult_frac(*val, fract.numerator, fract.denominator); + /* + * For all LTC2378-like devices, the amount of bits that express + * voltage magnitude depend on the polarity / output code format: + * - straight binary: All precision/resolution bits are used. + * - 2's complement: One of the precision bits is used for sign. + */ + if (chan->scan_type.format == IIO_SCAN_FORMAT_SIGNED_INT) + *val2 = chan->scan_type.realbits - 1; + else + *val2 = chan->scan_type.realbits; + + return IIO_VAL_FRACTIONAL_LOG2; + } + case IIO_CHAN_INFO_SAMP_FREQ: + *val = st->cnv_Hz; + return IIO_VAL_INT; + default: + return -EINVAL; + } +} + +static int ltc2378_read_avail(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + const int **vals, int *type, int *length, long mask) +{ + struct ltc2378_state *st = iio_priv(indio_dev); + + switch (mask) { + case IIO_CHAN_INFO_SAMP_FREQ: + *vals = st->sample_freq_range; + *type = IIO_VAL_INT; + return IIO_AVAIL_RANGE; + default: + return -EINVAL; + } +} + +/* + * SPI offload wiring schema + * + * +-------------+ +-------------+ + * | CNV |<-----+--| GPIO | + * | | +--| PWM0 | + * | | | | + * | | +--| PWM1 | + * | | | +-------------+ + * | | +->| TRIGGER | + * | | | | + * | ADC | | SPI | + * | | | controller | + * | | | | + * | SDI |<--------| SDO | + * | SDO |-------->| SDI | + * | SCLK |<--------| SCLK | + * +-------------+ +-------------+ + * + */ +static int ltc2378_update_conversion_rate(struct ltc2378_state *st, int freq_Hz) +{ + struct spi_offload_trigger_config config = st->offload_trigger_config; + unsigned int min_read_offset, offload_period_ns; + struct pwm_waveform cnv_wf = { }; + u64 target = LTC2378_TCNV_HIGH_NS; + unsigned int count; + u64 offload_offset_ns; + int ret; + + if (freq_Hz == 0) + return -EINVAL; + + if (!in_range(freq_Hz, 1, st->info->max_sample_rate_Hz)) + return -ERANGE; + + /* Configure CNV PWM waveform */ + cnv_wf.period_length_ns = DIV_ROUND_CLOSEST(NSEC_PER_SEC, freq_Hz); + + /* + * Ensure CNV high time meets minimum requirement (20ns). The PWM + * hardware may round the duty cycle, so iterate until we get at least + * the minimum required high time (or reach a try count limit). + */ + count = 100; + do { + cnv_wf.duty_length_ns = target; + ret = pwm_round_waveform_might_sleep(st->cnv_trigger, &cnv_wf); + if (ret) + return ret; + target += 10; /* Increment by PWM duty cycle period */ + } while (count-- && cnv_wf.duty_length_ns < LTC2378_TCNV_HIGH_NS); + + /* Check the minimum CNV high time is met */ + if (cnv_wf.duty_length_ns < LTC2378_TCNV_HIGH_NS) + return -EDOM; + + /* + * Configure SPI offload PWM trigger. + * The trigger should fire after tBUSYLH + tCONV + tDSDOBUSYL. + * Minimum time needed: TBUSYLH (13ns) + TCONV (part-specific) + TDSDOBUSYL (5ns) + * + * Use the same period as CNV PWM to avoid timing issues. + * Convert back from period to frequency for the SPI offload API. + */ + offload_period_ns = cnv_wf.period_length_ns; + config.periodic.frequency_hz = div_u64(HZ_PER_GHZ, offload_period_ns); + min_read_offset = LTC2378_TBUSYLH_NS + st->info->tconv_ns + LTC2378_TDSDOBUSYL_NS; + offload_offset_ns = min_read_offset; + count = 100; + do { + config.periodic.offset_ns = offload_offset_ns; + ret = spi_offload_trigger_validate(st->offload_trigger, &config); + if (ret) + return ret; + offload_offset_ns += 10; + } while (count-- && config.periodic.offset_ns < min_read_offset); + + /* Check the minimum CNV to SCLK delay is met */ + if (config.periodic.offset_ns < min_read_offset) + return -EDOM; + + /* Check the PWM periods remain the same */ + offload_period_ns = div64_u64(HZ_PER_GHZ, config.periodic.frequency_hz); + if (cnv_wf.period_length_ns != offload_period_ns) + return -EDOM; + + st->offload_trigger_config = config; + st->cnv_wf = cnv_wf; + st->cnv_Hz = DIV_ROUND_CLOSEST_ULL(HZ_PER_GHZ, cnv_wf.period_length_ns); + + return 0; +} + +static int ltc2378_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, int val2, long mask) +{ + struct ltc2378_state *st = iio_priv(indio_dev); + + IIO_DEV_ACQUIRE_DIRECT_MODE(indio_dev, claim); + if (IIO_DEV_ACQUIRE_FAILED(claim)) + return -EBUSY; + + switch (mask) { + case IIO_CHAN_INFO_SAMP_FREQ: + return ltc2378_update_conversion_rate(st, val); + default: + return -EINVAL; + } +} + +static const struct iio_info ltc2378_iio_info = { + .read_raw = <c2378_read_raw, +}; + +static const struct iio_info ltc2378_offload_iio_info = { + .read_raw = <c2378_read_raw, + .read_avail = <c2378_read_avail, + .write_raw = <c2378_write_raw, +}; + +static int ltc2378_offload_buffer_postenable(struct iio_dev *indio_dev) +{ + struct ltc2378_state *st = iio_priv(indio_dev); + int ret; + + ret = pwm_set_waveform_might_sleep(st->cnv_trigger, &st->cnv_wf, true); + if (ret) + return ret; + + ret = spi_offload_trigger_enable(st->offload, st->offload_trigger, + &st->offload_trigger_config); + if (ret) + goto out_pwm_disable; + + return 0; + +out_pwm_disable: + pwm_disable(st->cnv_trigger); + return ret; +} + +static int ltc2378_offload_buffer_predisable(struct iio_dev *indio_dev) +{ + struct ltc2378_state *st = iio_priv(indio_dev); + + spi_offload_trigger_disable(st->offload, st->offload_trigger); + pwm_disable(st->cnv_trigger); + + return 0; +} + +static const struct iio_buffer_setup_ops ltc2378_offload_buffer_ops = { + .postenable = <c2378_offload_buffer_postenable, + .predisable = <c2378_offload_buffer_predisable, +}; + +static int ltc2378_prepare_offload_message(struct device *dev, + struct ltc2378_state *st) +{ + unsigned int resolution = st->info->offload_chan.scan_type.realbits; + + st->offload_xfer.bits_per_word = resolution; + st->offload_xfer.len = spi_bpw_to_bytes(resolution); + st->offload_xfer.offload_flags = SPI_OFFLOAD_XFER_RX_STREAM; + + /* Initialize message with offload */ + spi_message_init_with_transfers(&st->offload_msg, &st->offload_xfer, 1); + st->offload_msg.offload = st->offload; + + return devm_spi_optimize_message(dev, st->spi, &st->offload_msg); +} + +static int ltc2378_spi_offload_setup(struct iio_dev *indio_dev, + struct ltc2378_state *st) +{ + struct device *dev = &st->spi->dev; + struct dma_chan *rx_dma; + + indio_dev->setup_ops = <c2378_offload_buffer_ops; + + st->offload_trigger = devm_spi_offload_trigger_get(dev, st->offload, + SPI_OFFLOAD_TRIGGER_PERIODIC); + if (IS_ERR(st->offload_trigger)) + return dev_err_probe(dev, PTR_ERR(st->offload_trigger), + "failed to get offload trigger\n"); + + st->offload_trigger_config.type = SPI_OFFLOAD_TRIGGER_PERIODIC; + + rx_dma = devm_spi_offload_rx_stream_request_dma_chan(dev, st->offload); + if (IS_ERR(rx_dma)) + return dev_err_probe(dev, PTR_ERR(rx_dma), "failed to get offload RX DMA\n"); + + return devm_iio_dmaengine_buffer_setup_with_handle(dev, indio_dev, rx_dma, + IIO_BUFFER_DIRECTION_IN); +} + +static int ltc2378_pwm_get(struct ltc2378_state *st) +{ + struct device *dev = &st->spi->dev; + + st->cnv_trigger = devm_pwm_get(dev, NULL); + if (IS_ERR(st->cnv_trigger)) + return dev_err_probe(dev, PTR_ERR(st->cnv_trigger), + "failed to get cnv pwm\n"); + + /* + * Disable the PWM connected to CNV in case it was left running by + * something else. + */ + pwm_disable(st->cnv_trigger); + + return 0; +} + +static const struct spi_offload_config ltc2378_offload_config = { + .capability_flags = SPI_OFFLOAD_CAP_TRIGGER | + SPI_OFFLOAD_CAP_RX_STREAM_DMA, +}; + +static int ltc2378_refin_setup(struct device *dev, struct ltc2378_state *st) +{ + int ret; + + /* + * The internal reference buffer amplifies both the internal reference + * and REFIN by a factor of 2. + */ + ret = devm_regulator_get_enable_read_voltage(dev, "refin"); + if (ret == -ENODEV) { /* refin is optional */ + st->ref_uV = st->info->internal_ref_uV * 2; + return 0; + } + + if (ret < 0) + return dev_err_probe(dev, ret, "failed to read refin regulator\n"); + + st->ref_uV = ret * 2; + + return 0; +} + +static int ltc2378_ref_setup(struct device *dev, struct ltc2378_state *st) +{ + int ret; + + ret = devm_regulator_get_enable_read_voltage(dev, "ref"); + if (ret < 0) + return dev_err_probe(dev, ret, "failed to read ref regulator\n"); + + st->ref_uV = ret; + + return 0; +} + +static int ltc2378_probe(struct spi_device *spi) +{ + struct device *dev = &spi->dev; + struct iio_dev *indio_dev; + struct ltc2378_state *st; + int ret; + + indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st)); + if (!indio_dev) + return -ENOMEM; + + st = iio_priv(indio_dev); + st->spi = spi; + + ret = devm_mutex_init(dev, &st->lock); + if (ret) + return ret; + + st->info = spi_get_device_match_data(spi); + if (!st->info) + return -EINVAL; + + if (st->info->internal_ref_uV) + ret = ltc2378_refin_setup(dev, st); + else + ret = ltc2378_ref_setup(dev, st); + if (ret) + return ret; + + indio_dev->name = st->info->name; + indio_dev->modes = INDIO_DIRECT_MODE; + + st->cnv_gpio = devm_gpiod_get(dev, "cnv", GPIOD_OUT_LOW); + if (IS_ERR(st->cnv_gpio)) + return dev_err_probe(dev, PTR_ERR(st->cnv_gpio), + "failed to get CNV GPIO"); + + st->offload = devm_spi_offload_get(dev, spi, <c2378_offload_config); + ret = PTR_ERR_OR_ZERO(st->offload); + /* Fall back to low speed usage when no SPI offload is available. */ + if (ret == -ENODEV) { + indio_dev->info = <c2378_iio_info; + indio_dev->channels = st->info->chan; + indio_dev->num_channels = ARRAY_SIZE(st->info->chan); + + ret = devm_iio_triggered_buffer_setup(dev, indio_dev, + iio_pollfunc_store_time, + ltc2378_trigger_handler, + NULL); + if (ret) + return ret; + } else if (ret) { + return dev_err_probe(dev, ret, "failed to get offload\n"); + } else { + indio_dev->info = <c2378_offload_iio_info; + indio_dev->channels = &st->info->offload_chan; + indio_dev->num_channels = 1; + ret = ltc2378_spi_offload_setup(indio_dev, st); + if (ret) + return dev_err_probe(dev, ret, + "failed to setup SPI offload\n"); + + ret = ltc2378_pwm_get(st); + if (ret) + return dev_err_probe(dev, ret, "failed to get PWM\n"); + + st->sample_freq_range[0] = 1; /* min */ + st->sample_freq_range[1] = 1; /* step */ + st->sample_freq_range[2] = st->info->max_sample_rate_Hz; /* max */ + + /* + * Start with a slower sampling rate so there is some room for + * adjusting the sample averaging and the sampling frequency + * without hitting the maximum conversion rate. + */ + ret = ltc2378_update_conversion_rate(st, st->info->max_sample_rate_Hz >> 4); + if (ret) + return dev_err_probe(dev, ret, + "failed to set offload samp freq\n"); + + ret = ltc2378_prepare_offload_message(&spi->dev, st); + if (ret) + return dev_err_probe(dev, ret, "failed to optimize SPI message\n"); + + /* + * Set single-read transfer bits_per_word so the SPI subsystem + * rearranges data to CPU endianness, enabling us to reuse + * offload_chan specifications for single-shot reads. + */ + st->xfer.bits_per_word = st->info->offload_chan.scan_type.realbits; + } + + st->xfer.rx_buf = &st->scan.data; + st->xfer.len = spi_bpw_to_bytes(indio_dev->channels[0].scan_type.realbits); + + return devm_iio_device_register(&spi->dev, indio_dev); +} + +static const struct of_device_id ltc2378_of_match[] = { + { .compatible = "adi,ltc2338-18", .data = <c2338_18_chip_info }, + { .compatible = "adi,ltc2364-16", .data = <c2364_16_chip_info }, + { .compatible = "adi,ltc2364-18", .data = <c2364_18_chip_info }, + { .compatible = "adi,ltc2367-16", .data = <c2367_16_chip_info }, + { .compatible = "adi,ltc2367-18", .data = <c2367_18_chip_info }, + { .compatible = "adi,ltc2368-16", .data = <c2368_16_chip_info }, + { .compatible = "adi,ltc2368-18", .data = <c2368_18_chip_info }, + { .compatible = "adi,ltc2369-18", .data = <c2369_18_chip_info }, + { .compatible = "adi,ltc2370-16", .data = <c2370_16_chip_info }, + { .compatible = "adi,ltc2376-16", .data = <c2376_16_chip_info }, + { .compatible = "adi,ltc2376-18", .data = <c2376_18_chip_info }, + { .compatible = "adi,ltc2376-20", .data = <c2376_20_chip_info }, + { .compatible = "adi,ltc2377-16", .data = <c2377_16_chip_info }, + { .compatible = "adi,ltc2377-18", .data = <c2377_18_chip_info }, + { .compatible = "adi,ltc2377-20", .data = <c2377_20_chip_info }, + { .compatible = "adi,ltc2378-16", .data = <c2378_16_chip_info }, + { .compatible = "adi,ltc2378-18", .data = <c2378_18_chip_info }, + { .compatible = "adi,ltc2378-20", .data = <c2378_20_chip_info }, + { .compatible = "adi,ltc2379-18", .data = <c2379_18_chip_info }, + { .compatible = "adi,ltc2380-16", .data = <c2380_16_chip_info }, + { } +}; +MODULE_DEVICE_TABLE(of, ltc2378_of_match); + +static const struct spi_device_id ltc2378_spi_id[] = { + { .name = "ltc2338-18", .driver_data = (kernel_ulong_t)<c2338_18_chip_info }, + { .name = "ltc2364-16", .driver_data = (kernel_ulong_t)<c2364_16_chip_info }, + { .name = "ltc2364-18", .driver_data = (kernel_ulong_t)<c2364_18_chip_info }, + { .name = "ltc2367-16", .driver_data = (kernel_ulong_t)<c2367_16_chip_info }, + { .name = "ltc2367-18", .driver_data = (kernel_ulong_t)<c2367_18_chip_info }, + { .name = "ltc2368-16", .driver_data = (kernel_ulong_t)<c2368_16_chip_info }, + { .name = "ltc2368-18", .driver_data = (kernel_ulong_t)<c2368_18_chip_info }, + { .name = "ltc2369-18", .driver_data = (kernel_ulong_t)<c2369_18_chip_info }, + { .name = "ltc2370-16", .driver_data = (kernel_ulong_t)<c2370_16_chip_info }, + { .name = "ltc2376-16", .driver_data = (kernel_ulong_t)<c2376_16_chip_info }, + { .name = "ltc2376-18", .driver_data = (kernel_ulong_t)<c2376_18_chip_info }, + { .name = "ltc2376-20", .driver_data = (kernel_ulong_t)<c2376_20_chip_info }, + { .name = "ltc2377-16", .driver_data = (kernel_ulong_t)<c2377_16_chip_info }, + { .name = "ltc2377-18", .driver_data = (kernel_ulong_t)<c2377_18_chip_info }, + { .name = "ltc2377-20", .driver_data = (kernel_ulong_t)<c2377_20_chip_info }, + { .name = "ltc2378-16", .driver_data = (kernel_ulong_t)<c2378_16_chip_info }, + { .name = "ltc2378-18", .driver_data = (kernel_ulong_t)<c2378_18_chip_info }, + { .name = "ltc2378-20", .driver_data = (kernel_ulong_t)<c2378_20_chip_info }, + { .name = "ltc2379-18", .driver_data = (kernel_ulong_t)<c2379_18_chip_info }, + { .name = "ltc2380-16", .driver_data = (kernel_ulong_t)<c2380_16_chip_info }, + { } +}; +MODULE_DEVICE_TABLE(spi, ltc2378_spi_id); + +static struct spi_driver ltc2378_driver = { + .driver = { + .name = "ltc2378", + .of_match_table = ltc2378_of_match + }, + .probe = ltc2378_probe, + .id_table = ltc2378_spi_id, +}; +module_spi_driver(ltc2378_driver); + +MODULE_AUTHOR("Marcelo Schmitt "); +MODULE_DESCRIPTION("Analog Devices LTC2378 ADC series driver"); +MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS("IIO_DMAENGINE_BUFFER"); +MODULE_IMPORT_NS("SPI_OFFLOAD"); diff --git a/drivers/iio/adc/ltc2471.c b/drivers/iio/adc/ltc2471.c new file mode 100644 index 000000000..369af7009 --- /dev/null +++ b/drivers/iio/adc/ltc2471.c @@ -0,0 +1,157 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Driver for Linear Technology LTC2471 and LTC2473 voltage monitors + * The LTC2473 is identical to the 2471, but reports a differential signal. + * + * Copyright (C) 2017 Topic Embedded Products + * Author: Mike Looijmans + */ + +#include +#include +#include +#include +#include +#include + +enum ltc2471_chips { + ltc2471, + ltc2473, +}; + +struct ltc2471_data { + struct i2c_client *client; +}; + +/* Reference voltage is 1.25V */ +#define LTC2471_VREF 1250 + +/* Read two bytes from the I2C bus to obtain the ADC result */ +static int ltc2471_get_value(struct i2c_client *client) +{ + int ret; + __be16 buf; + + ret = i2c_master_recv(client, (char *)&buf, sizeof(buf)); + if (ret < 0) + return ret; + if (ret != sizeof(buf)) + return -EIO; + + /* MSB first */ + return be16_to_cpu(buf); +} + +static int ltc2471_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long info) +{ + struct ltc2471_data *data = iio_priv(indio_dev); + int ret; + + switch (info) { + case IIO_CHAN_INFO_RAW: + ret = ltc2471_get_value(data->client); + if (ret < 0) + return ret; + *val = ret; + return IIO_VAL_INT; + + case IIO_CHAN_INFO_SCALE: + if (chan->differential) + /* Output ranges from -VREF to +VREF */ + *val = 2 * LTC2471_VREF; + else + /* Output ranges from 0 to VREF */ + *val = LTC2471_VREF; + *val2 = 16; /* 16 data bits */ + return IIO_VAL_FRACTIONAL_LOG2; + + case IIO_CHAN_INFO_OFFSET: + /* Only differential chip has this property */ + *val = -LTC2471_VREF; + return IIO_VAL_INT; + + default: + return -EINVAL; + } +} + +static const struct iio_chan_spec ltc2471_channel[] = { + { + .type = IIO_VOLTAGE, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), + }, +}; + +static const struct iio_chan_spec ltc2473_channel[] = { + { + .type = IIO_VOLTAGE, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | + BIT(IIO_CHAN_INFO_OFFSET), + .differential = 1, + }, +}; + +static const struct iio_info ltc2471_info = { + .read_raw = ltc2471_read_raw, +}; + +static int ltc2471_i2c_probe(struct i2c_client *client) +{ + const struct i2c_device_id *id = i2c_client_get_device_id(client); + struct iio_dev *indio_dev; + struct ltc2471_data *data; + int ret; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) + return -EOPNOTSUPP; + + indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data)); + if (!indio_dev) + return -ENOMEM; + + data = iio_priv(indio_dev); + data->client = client; + + indio_dev->name = id->name; + indio_dev->info = <c2471_info; + indio_dev->modes = INDIO_DIRECT_MODE; + if (id->driver_data == ltc2473) + indio_dev->channels = ltc2473_channel; + else + indio_dev->channels = ltc2471_channel; + indio_dev->num_channels = 1; + + /* Trigger once to start conversion and check if chip is there */ + ret = ltc2471_get_value(client); + if (ret < 0) { + dev_err(&client->dev, "Cannot read from device.\n"); + return ret; + } + + return devm_iio_device_register(&client->dev, indio_dev); +} + +static const struct i2c_device_id ltc2471_i2c_id[] = { + { .name = "ltc2471", .driver_data = ltc2471 }, + { .name = "ltc2473", .driver_data = ltc2473 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, ltc2471_i2c_id); + +static struct i2c_driver ltc2471_i2c_driver = { + .driver = { + .name = "ltc2471", + }, + .probe = ltc2471_i2c_probe, + .id_table = ltc2471_i2c_id, +}; + +module_i2c_driver(ltc2471_i2c_driver); + +MODULE_DESCRIPTION("LTC2471/LTC2473 ADC driver"); +MODULE_AUTHOR("Topic Embedded Products"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/adc/ltc2485.c b/drivers/iio/adc/ltc2485.c new file mode 100644 index 000000000..8c3806e06 --- /dev/null +++ b/drivers/iio/adc/ltc2485.c @@ -0,0 +1,143 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * ltc2485.c - Driver for Linear Technology LTC2485 ADC + * + * Copyright (C) 2016 Alison Schofield + * + * Datasheet: http://cds.linear.com/docs/en/datasheet/2485fd.pdf + */ + +#include +#include +#include + +#include +#include + +/* Power-on configuration: rejects both 50/60Hz, operates at 1x speed */ +#define LTC2485_CONFIG_DEFAULT 0 + +struct ltc2485_data { + struct i2c_client *client; + ktime_t time_prev; /* last conversion */ +}; + +static void ltc2485_wait_conv(struct ltc2485_data *data) +{ + const unsigned int conv_time = 147; /* conversion time ms */ + unsigned int time_elapsed; + + /* delay if conversion time not passed since last read or write */ + time_elapsed = ktime_ms_delta(ktime_get(), data->time_prev); + + if (time_elapsed < conv_time) + msleep(conv_time - time_elapsed); +} + +static int ltc2485_read(struct ltc2485_data *data, int *val) +{ + struct i2c_client *client = data->client; + __be32 buf = 0; + int ret; + + ltc2485_wait_conv(data); + + ret = i2c_master_recv(client, (char *)&buf, 4); + if (ret < 0) { + dev_err(&client->dev, "i2c_master_recv failed\n"); + return ret; + } + data->time_prev = ktime_get(); + *val = sign_extend32(be32_to_cpu(buf) >> 6, 24); + + return ret; +} + +static int ltc2485_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct ltc2485_data *data = iio_priv(indio_dev); + int ret; + + if (mask == IIO_CHAN_INFO_RAW) { + ret = ltc2485_read(data, val); + if (ret < 0) + return ret; + + return IIO_VAL_INT; + + } else if (mask == IIO_CHAN_INFO_SCALE) { + *val = 5000; /* on board vref millivolts */ + *val2 = 25; /* 25 (24 + sign) data bits */ + return IIO_VAL_FRACTIONAL_LOG2; + + } else { + return -EINVAL; + } +} + +static const struct iio_chan_spec ltc2485_channel[] = { + { + .type = IIO_VOLTAGE, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) + }, +}; + +static const struct iio_info ltc2485_info = { + .read_raw = ltc2485_read_raw, +}; + +static int ltc2485_probe(struct i2c_client *client) +{ + const struct i2c_device_id *id = i2c_client_get_device_id(client); + struct iio_dev *indio_dev; + struct ltc2485_data *data; + int ret; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C | + I2C_FUNC_SMBUS_WRITE_BYTE)) + return -EOPNOTSUPP; + + indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data)); + if (!indio_dev) + return -ENOMEM; + + data = iio_priv(indio_dev); + i2c_set_clientdata(client, indio_dev); + data->client = client; + + indio_dev->name = id->name; + indio_dev->info = <c2485_info; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = ltc2485_channel; + indio_dev->num_channels = ARRAY_SIZE(ltc2485_channel); + + ret = i2c_smbus_write_byte(data->client, LTC2485_CONFIG_DEFAULT); + if (ret < 0) + return ret; + + data->time_prev = ktime_get(); + + return devm_iio_device_register(&client->dev, indio_dev); +} + +static const struct i2c_device_id ltc2485_id[] = { + { .name = "ltc2485" }, + { } +}; +MODULE_DEVICE_TABLE(i2c, ltc2485_id); + +static struct i2c_driver ltc2485_driver = { + .driver = { + .name = "ltc2485", + }, + .probe = ltc2485_probe, + .id_table = ltc2485_id, +}; +module_i2c_driver(ltc2485_driver); + +MODULE_AUTHOR("Alison Schofield "); +MODULE_DESCRIPTION("Linear Technology LTC2485 ADC driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/adc/ltc2496.c b/drivers/iio/adc/ltc2496.c new file mode 100644 index 000000000..5b5b6ab28 --- /dev/null +++ b/drivers/iio/adc/ltc2496.c @@ -0,0 +1,112 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * ltc2496.c - Driver for Analog Devices/Linear Technology LTC2496 ADC + * + * Based on ltc2497.c which has + * Copyright (C) 2017 Analog Devices Inc. + * + * Licensed under the GPL-2. + * + * Datasheet: https://www.analog.com/media/en/technical-documentation/data-sheets/2496fc.pdf + */ + +#include +#include +#include +#include +#include + +#include "ltc2497.h" + +struct ltc2496_driverdata { + /* this must be the first member */ + struct ltc2497core_driverdata common_ddata; + struct spi_device *spi; + + /* + * DMA (thus cache coherency maintenance) may require the + * transfer buffers to live in their own cache lines. + */ + unsigned char rxbuf[3] __aligned(IIO_DMA_MINALIGN); + unsigned char txbuf[3]; +}; + +static int ltc2496_result_and_measure(struct ltc2497core_driverdata *ddata, + u8 address, int *val) +{ + struct ltc2496_driverdata *st = + container_of(ddata, struct ltc2496_driverdata, common_ddata); + struct spi_transfer t = { + .tx_buf = st->txbuf, + .rx_buf = st->rxbuf, + .len = sizeof(st->txbuf), + }; + int ret; + + st->txbuf[0] = LTC2497_ENABLE | address; + + ret = spi_sync_transfer(st->spi, &t, 1); + if (ret < 0) { + dev_err(&st->spi->dev, "spi_sync_transfer failed: %pe\n", + ERR_PTR(ret)); + return ret; + } + + if (val) + *val = ((st->rxbuf[0] & 0x3f) << 12 | + st->rxbuf[1] << 4 | st->rxbuf[2] >> 4) - + (1 << 17); + + return 0; +} + +static int ltc2496_probe(struct spi_device *spi) +{ + struct iio_dev *indio_dev; + struct ltc2496_driverdata *st; + struct device *dev = &spi->dev; + + indio_dev = devm_iio_device_alloc(dev, sizeof(*st)); + if (!indio_dev) + return -ENOMEM; + + st = iio_priv(indio_dev); + spi_set_drvdata(spi, indio_dev); + st->spi = spi; + st->common_ddata.result_and_measure = ltc2496_result_and_measure; + st->common_ddata.chip_info = device_get_match_data(dev); + + return ltc2497core_probe(dev, indio_dev); +} + +static void ltc2496_remove(struct spi_device *spi) +{ + struct iio_dev *indio_dev = spi_get_drvdata(spi); + + ltc2497core_remove(indio_dev); +} + +static const struct ltc2497_chip_info ltc2496_info = { + .resolution = 16, + .name = NULL, +}; + +static const struct of_device_id ltc2496_of_match[] = { + { .compatible = "lltc,ltc2496", .data = <c2496_info, }, + { } +}; +MODULE_DEVICE_TABLE(of, ltc2496_of_match); + +static struct spi_driver ltc2496_driver = { + .driver = { + .name = "ltc2496", + .of_match_table = ltc2496_of_match, + }, + .probe = ltc2496_probe, + .remove = ltc2496_remove, +}; +module_spi_driver(ltc2496_driver); + +MODULE_AUTHOR("Uwe Kleine-König "); +MODULE_DESCRIPTION("Linear Technology LTC2496 ADC driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/adc/ltc2497-core.c b/drivers/iio/adc/ltc2497-core.c new file mode 100644 index 000000000..2dc5c7044 --- /dev/null +++ b/drivers/iio/adc/ltc2497-core.c @@ -0,0 +1,244 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * ltc2497-core.c - Common code for Analog Devices/Linear Technology + * LTC2496 and LTC2497 ADCs + * + * Copyright (C) 2017 Analog Devices Inc. + */ + +#include +#include +#include +#include +#include +#include + +#include "ltc2497.h" + +#define LTC2497_SGL BIT(4) +#define LTC2497_DIFF 0 +#define LTC2497_SIGN BIT(3) + +static int ltc2497core_wait_conv(struct ltc2497core_driverdata *ddata) +{ + s64 time_elapsed; + + time_elapsed = ktime_ms_delta(ktime_get(), ddata->time_prev); + + if (time_elapsed < LTC2497_CONVERSION_TIME_MS) { + /* delay if conversion time not passed + * since last read or write + */ + if (msleep_interruptible( + LTC2497_CONVERSION_TIME_MS - time_elapsed)) + return -ERESTARTSYS; + + return 0; + } + + if (time_elapsed - LTC2497_CONVERSION_TIME_MS <= 0) { + /* We're in automatic mode - + * so the last reading is still not outdated + */ + return 0; + } + + return 1; +} + +static int ltc2497core_read(struct ltc2497core_driverdata *ddata, u8 address, int *val) +{ + int ret; + + ret = ltc2497core_wait_conv(ddata); + if (ret < 0) + return ret; + + if (ret || ddata->addr_prev != address) { + ret = ddata->result_and_measure(ddata, address, NULL); + if (ret < 0) + return ret; + ddata->addr_prev = address; + + if (msleep_interruptible(LTC2497_CONVERSION_TIME_MS)) + return -ERESTARTSYS; + } + + ret = ddata->result_and_measure(ddata, address, val); + if (ret < 0) + return ret; + + ddata->time_prev = ktime_get(); + + return ret; +} + +static int ltc2497core_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct ltc2497core_driverdata *ddata = iio_priv(indio_dev); + int ret; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + mutex_lock(&ddata->lock); + ret = ltc2497core_read(ddata, chan->address, val); + mutex_unlock(&ddata->lock); + if (ret < 0) + return ret; + + return IIO_VAL_INT; + + case IIO_CHAN_INFO_SCALE: + ret = regulator_get_voltage(ddata->ref); + if (ret < 0) + return ret; + + *val = ret / 1000; + *val2 = ddata->chip_info->resolution + 1; + + return IIO_VAL_FRACTIONAL_LOG2; + + default: + return -EINVAL; + } +} + +#define LTC2497_CHAN(_chan, _addr, _ds_name) { \ + .type = IIO_VOLTAGE, \ + .indexed = 1, \ + .channel = (_chan), \ + .address = (_addr | (_chan / 2) | ((_chan & 1) ? LTC2497_SIGN : 0)), \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ + .datasheet_name = (_ds_name), \ +} + +#define LTC2497_CHAN_DIFF(_chan, _addr) { \ + .type = IIO_VOLTAGE, \ + .indexed = 1, \ + .channel = (_chan) * 2 + ((_addr) & LTC2497_SIGN ? 1 : 0), \ + .channel2 = (_chan) * 2 + ((_addr) & LTC2497_SIGN ? 0 : 1),\ + .address = (_addr | _chan), \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ + .differential = 1, \ +} + +static const struct iio_chan_spec ltc2497core_channel[] = { + LTC2497_CHAN(0, LTC2497_SGL, "CH0"), + LTC2497_CHAN(1, LTC2497_SGL, "CH1"), + LTC2497_CHAN(2, LTC2497_SGL, "CH2"), + LTC2497_CHAN(3, LTC2497_SGL, "CH3"), + LTC2497_CHAN(4, LTC2497_SGL, "CH4"), + LTC2497_CHAN(5, LTC2497_SGL, "CH5"), + LTC2497_CHAN(6, LTC2497_SGL, "CH6"), + LTC2497_CHAN(7, LTC2497_SGL, "CH7"), + LTC2497_CHAN(8, LTC2497_SGL, "CH8"), + LTC2497_CHAN(9, LTC2497_SGL, "CH9"), + LTC2497_CHAN(10, LTC2497_SGL, "CH10"), + LTC2497_CHAN(11, LTC2497_SGL, "CH11"), + LTC2497_CHAN(12, LTC2497_SGL, "CH12"), + LTC2497_CHAN(13, LTC2497_SGL, "CH13"), + LTC2497_CHAN(14, LTC2497_SGL, "CH14"), + LTC2497_CHAN(15, LTC2497_SGL, "CH15"), + LTC2497_CHAN_DIFF(0, LTC2497_DIFF), + LTC2497_CHAN_DIFF(1, LTC2497_DIFF), + LTC2497_CHAN_DIFF(2, LTC2497_DIFF), + LTC2497_CHAN_DIFF(3, LTC2497_DIFF), + LTC2497_CHAN_DIFF(4, LTC2497_DIFF), + LTC2497_CHAN_DIFF(5, LTC2497_DIFF), + LTC2497_CHAN_DIFF(6, LTC2497_DIFF), + LTC2497_CHAN_DIFF(7, LTC2497_DIFF), + LTC2497_CHAN_DIFF(0, LTC2497_DIFF | LTC2497_SIGN), + LTC2497_CHAN_DIFF(1, LTC2497_DIFF | LTC2497_SIGN), + LTC2497_CHAN_DIFF(2, LTC2497_DIFF | LTC2497_SIGN), + LTC2497_CHAN_DIFF(3, LTC2497_DIFF | LTC2497_SIGN), + LTC2497_CHAN_DIFF(4, LTC2497_DIFF | LTC2497_SIGN), + LTC2497_CHAN_DIFF(5, LTC2497_DIFF | LTC2497_SIGN), + LTC2497_CHAN_DIFF(6, LTC2497_DIFF | LTC2497_SIGN), + LTC2497_CHAN_DIFF(7, LTC2497_DIFF | LTC2497_SIGN), +}; + +static const struct iio_info ltc2497core_info = { + .read_raw = ltc2497core_read_raw, +}; + +int ltc2497core_probe(struct device *dev, struct iio_dev *indio_dev) +{ + struct ltc2497core_driverdata *ddata = iio_priv(indio_dev); + struct iio_map *plat_data = dev_get_platdata(dev); + int ret; + + /* + * Keep using dev_name() for the iio_dev's name on some of the parts, + * since updating it would result in a ABI breakage. + */ + if (ddata->chip_info->name) + indio_dev->name = ddata->chip_info->name; + else + indio_dev->name = dev_name(dev); + + indio_dev->info = <c2497core_info; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = ltc2497core_channel; + indio_dev->num_channels = ARRAY_SIZE(ltc2497core_channel); + + ret = ddata->result_and_measure(ddata, LTC2497_CONFIG_DEFAULT, NULL); + if (ret < 0) + return ret; + + ddata->ref = devm_regulator_get(dev, "vref"); + if (IS_ERR(ddata->ref)) + return dev_err_probe(dev, PTR_ERR(ddata->ref), + "Failed to get vref regulator\n"); + + ret = regulator_enable(ddata->ref); + if (ret < 0) { + dev_err(dev, "Failed to enable vref regulator: %pe\n", + ERR_PTR(ret)); + return ret; + } + + ret = iio_map_array_register(indio_dev, plat_data); + if (ret) { + dev_err(&indio_dev->dev, "iio map err: %d\n", ret); + goto err_regulator_disable; + } + + ddata->addr_prev = LTC2497_CONFIG_DEFAULT; + ddata->time_prev = ktime_get(); + + mutex_init(&ddata->lock); + + ret = iio_device_register(indio_dev); + if (ret < 0) + goto err_array_unregister; + + return 0; + +err_array_unregister: + iio_map_array_unregister(indio_dev); + +err_regulator_disable: + regulator_disable(ddata->ref); + + return ret; +} +EXPORT_SYMBOL_NS(ltc2497core_probe, "LTC2497"); + +void ltc2497core_remove(struct iio_dev *indio_dev) +{ + struct ltc2497core_driverdata *ddata = iio_priv(indio_dev); + + iio_device_unregister(indio_dev); + + iio_map_array_unregister(indio_dev); + + regulator_disable(ddata->ref); +} +EXPORT_SYMBOL_NS(ltc2497core_remove, "LTC2497"); + +MODULE_DESCRIPTION("common code for LTC2496/LTC2497 drivers"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/adc/ltc2497.c b/drivers/iio/adc/ltc2497.c new file mode 100644 index 000000000..c1668b5a3 --- /dev/null +++ b/drivers/iio/adc/ltc2497.c @@ -0,0 +1,170 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * ltc2497.c - Driver for Analog Devices/Linear Technology LTC2497 ADC + * + * Copyright (C) 2017 Analog Devices Inc. + * + * Datasheet: http://cds.linear.com/docs/en/datasheet/2497fd.pdf + */ + +#include +#include +#include +#include +#include + +#include + +#include "ltc2497.h" + +enum ltc2497_chip_type { + TYPE_LTC2497, + TYPE_LTC2499, +}; + +struct ltc2497_driverdata { + /* this must be the first member */ + struct ltc2497core_driverdata common_ddata; + struct i2c_client *client; + u32 recv_size; + /* + * DMA (thus cache coherency maintenance) may require the + * transfer buffers to live in their own cache lines. + */ + union { + __be32 d32; + u8 d8[3]; + } data __aligned(IIO_DMA_MINALIGN); +}; + +static int ltc2497_result_and_measure(struct ltc2497core_driverdata *ddata, + u8 address, int *val) +{ + struct ltc2497_driverdata *st = + container_of(ddata, struct ltc2497_driverdata, common_ddata); + int ret; + + if (val) { + if (st->recv_size == 3) + ret = i2c_master_recv(st->client, (char *)&st->data.d8, + st->recv_size); + else + ret = i2c_master_recv(st->client, (char *)&st->data.d32, + st->recv_size); + if (ret < 0) { + dev_err(&st->client->dev, "i2c_master_recv failed\n"); + return ret; + } + + /* + * The data format is 16/24 bit 2s complement, but with an upper sign bit on the + * resolution + 1 position, which is set for positive values only. Given this + * bit's value, subtracting BIT(resolution + 1) from the ADC's result is + * equivalent to a sign extension. + */ + if (st->recv_size == 3) { + *val = (get_unaligned_be24(st->data.d8) >> 6) + - BIT(ddata->chip_info->resolution + 1); + } else { + *val = (be32_to_cpu(st->data.d32) >> 6) + - BIT(ddata->chip_info->resolution + 1); + } + + /* + * The part started a new conversion at the end of the above i2c + * transfer, so if the address didn't change since the last call + * everything is fine and we can return early. + * If not (which should only happen when some sort of bulk + * conversion is implemented) we have to program the new + * address. Note that this probably fails as the conversion that + * was triggered above is like not complete yet and the two + * operations have to be done in a single transfer. + */ + if (ddata->addr_prev == address) + return 0; + } + + ret = i2c_smbus_write_byte(st->client, + LTC2497_ENABLE | address); + if (ret) + dev_err(&st->client->dev, "i2c transfer failed: %pe\n", + ERR_PTR(ret)); + return ret; +} + +static int ltc2497_probe(struct i2c_client *client) +{ + const struct ltc2497_chip_info *chip_info; + struct iio_dev *indio_dev; + struct ltc2497_driverdata *st; + struct device *dev = &client->dev; + u32 resolution; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C | + I2C_FUNC_SMBUS_WRITE_BYTE)) + return -EOPNOTSUPP; + + indio_dev = devm_iio_device_alloc(dev, sizeof(*st)); + if (!indio_dev) + return -ENOMEM; + + st = iio_priv(indio_dev); + i2c_set_clientdata(client, indio_dev); + st->client = client; + st->common_ddata.result_and_measure = ltc2497_result_and_measure; + + chip_info = i2c_get_match_data(client); + st->common_ddata.chip_info = chip_info; + + resolution = chip_info->resolution; + st->recv_size = BITS_TO_BYTES(resolution) + 1; + + return ltc2497core_probe(dev, indio_dev); +} + +static void ltc2497_remove(struct i2c_client *client) +{ + struct iio_dev *indio_dev = i2c_get_clientdata(client); + + ltc2497core_remove(indio_dev); +} + +static const struct ltc2497_chip_info ltc2497_info[] = { + [TYPE_LTC2497] = { + .resolution = 16, + .name = NULL, + }, + [TYPE_LTC2499] = { + .resolution = 24, + .name = "ltc2499", + }, +}; + +static const struct i2c_device_id ltc2497_id[] = { + { .name = "ltc2497", .driver_data = (kernel_ulong_t)<c2497_info[TYPE_LTC2497] }, + { .name = "ltc2499", .driver_data = (kernel_ulong_t)<c2497_info[TYPE_LTC2499] }, + { } +}; +MODULE_DEVICE_TABLE(i2c, ltc2497_id); + +static const struct of_device_id ltc2497_of_match[] = { + { .compatible = "lltc,ltc2497", .data = <c2497_info[TYPE_LTC2497] }, + { .compatible = "lltc,ltc2499", .data = <c2497_info[TYPE_LTC2499] }, + { } +}; +MODULE_DEVICE_TABLE(of, ltc2497_of_match); + +static struct i2c_driver ltc2497_driver = { + .driver = { + .name = "ltc2497", + .of_match_table = ltc2497_of_match, + }, + .probe = ltc2497_probe, + .remove = ltc2497_remove, + .id_table = ltc2497_id, +}; +module_i2c_driver(ltc2497_driver); + +MODULE_AUTHOR("Michael Hennerich "); +MODULE_DESCRIPTION("Linear Technology LTC2497 ADC driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/adc/ltc2497.h b/drivers/iio/adc/ltc2497.h new file mode 100644 index 000000000..64e81c95a --- /dev/null +++ b/drivers/iio/adc/ltc2497.h @@ -0,0 +1,26 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#define LTC2497_ENABLE 0xA0 +#define LTC2497_CONFIG_DEFAULT LTC2497_ENABLE +#define LTC2497_CONVERSION_TIME_MS 150ULL + +struct ltc2497_chip_info { + u32 resolution; + const char *name; +}; + +struct ltc2497core_driverdata { + struct regulator *ref; + ktime_t time_prev; + /* lock to protect against multiple access to the device */ + struct mutex lock; + const struct ltc2497_chip_info *chip_info; + u8 addr_prev; + int (*result_and_measure)(struct ltc2497core_driverdata *ddata, + u8 address, int *val); +}; + +int ltc2497core_probe(struct device *dev, struct iio_dev *indio_dev); +void ltc2497core_remove(struct iio_dev *indio_dev); + +MODULE_IMPORT_NS("LTC2497"); diff --git a/drivers/iio/adc/max1027.c b/drivers/iio/adc/max1027.c new file mode 100644 index 000000000..fb6f12316 --- /dev/null +++ b/drivers/iio/adc/max1027.c @@ -0,0 +1,635 @@ +// SPDX-License-Identifier: GPL-2.0-only + /* + * iio/adc/max1027.c + * Copyright (C) 2014 Philippe Reynes + * + * based on linux/drivers/iio/ad7923.c + * Copyright 2011 Analog Devices Inc (from AD7923 Driver) + * Copyright 2012 CS Systemes d'Information + * + * max1027.c + * + * Partial support for max1027 and similar chips. + */ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#define MAX1027_CONV_REG BIT(7) +#define MAX1027_SETUP_REG BIT(6) +#define MAX1027_AVG_REG BIT(5) +#define MAX1027_RST_REG BIT(4) + +/* conversion register */ +#define MAX1027_TEMP BIT(0) +#define MAX1027_SCAN_0_N (0x00 << 1) +#define MAX1027_SCAN_N_M (0x01 << 1) +#define MAX1027_SCAN_N (0x02 << 1) +#define MAX1027_NOSCAN (0x03 << 1) +#define MAX1027_CHAN(n) ((n) << 3) + +/* setup register */ +#define MAX1027_UNIPOLAR 0x02 +#define MAX1027_BIPOLAR 0x03 +#define MAX1027_REF_MODE0 (0x00 << 2) +#define MAX1027_REF_MODE1 (0x01 << 2) +#define MAX1027_REF_MODE2 (0x02 << 2) +#define MAX1027_REF_MODE3 (0x03 << 2) +#define MAX1027_CKS_MODE0 (0x00 << 4) +#define MAX1027_CKS_MODE1 (0x01 << 4) +#define MAX1027_CKS_MODE2 (0x02 << 4) +#define MAX1027_CKS_MODE3 (0x03 << 4) + +/* averaging register */ +#define MAX1027_NSCAN_4 0x00 +#define MAX1027_NSCAN_8 0x01 +#define MAX1027_NSCAN_12 0x02 +#define MAX1027_NSCAN_16 0x03 +#define MAX1027_NAVG_4 (0x00 << 2) +#define MAX1027_NAVG_8 (0x01 << 2) +#define MAX1027_NAVG_16 (0x02 << 2) +#define MAX1027_NAVG_32 (0x03 << 2) +#define MAX1027_AVG_EN BIT(4) + +/* Device can achieve 300ksps so we assume a 3.33us conversion delay */ +#define MAX1027_CONVERSION_UDELAY 4 + +enum max1027_id { + max1027, + max1029, + max1031, + max1227, + max1229, + max1231, +}; + +static const struct spi_device_id max1027_id[] = { + { .name = "max1027", .driver_data = max1027 }, + { .name = "max1029", .driver_data = max1029 }, + { .name = "max1031", .driver_data = max1031 }, + { .name = "max1227", .driver_data = max1227 }, + { .name = "max1229", .driver_data = max1229 }, + { .name = "max1231", .driver_data = max1231 }, + { } +}; +MODULE_DEVICE_TABLE(spi, max1027_id); + +static const struct of_device_id max1027_adc_dt_ids[] = { + { .compatible = "maxim,max1027" }, + { .compatible = "maxim,max1029" }, + { .compatible = "maxim,max1031" }, + { .compatible = "maxim,max1227" }, + { .compatible = "maxim,max1229" }, + { .compatible = "maxim,max1231" }, + { } +}; +MODULE_DEVICE_TABLE(of, max1027_adc_dt_ids); + +#define MAX1027_V_CHAN(index, depth) \ + { \ + .type = IIO_VOLTAGE, \ + .indexed = 1, \ + .channel = index, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ + .scan_index = index + 1, \ + .scan_type = { \ + .sign = 'u', \ + .realbits = depth, \ + .storagebits = 16, \ + .shift = (depth == 10) ? 2 : 0, \ + .endianness = IIO_BE, \ + }, \ + } + +#define MAX1027_T_CHAN \ + { \ + .type = IIO_TEMP, \ + .channel = 0, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ + .scan_index = 0, \ + .scan_type = { \ + .sign = 'u', \ + .realbits = 12, \ + .storagebits = 16, \ + .endianness = IIO_BE, \ + }, \ + } + +#define MAX1X27_CHANNELS(depth) \ + MAX1027_T_CHAN, \ + MAX1027_V_CHAN(0, depth), \ + MAX1027_V_CHAN(1, depth), \ + MAX1027_V_CHAN(2, depth), \ + MAX1027_V_CHAN(3, depth), \ + MAX1027_V_CHAN(4, depth), \ + MAX1027_V_CHAN(5, depth), \ + MAX1027_V_CHAN(6, depth), \ + MAX1027_V_CHAN(7, depth) + +#define MAX1X29_CHANNELS(depth) \ + MAX1X27_CHANNELS(depth), \ + MAX1027_V_CHAN(8, depth), \ + MAX1027_V_CHAN(9, depth), \ + MAX1027_V_CHAN(10, depth), \ + MAX1027_V_CHAN(11, depth) + +#define MAX1X31_CHANNELS(depth) \ + MAX1X29_CHANNELS(depth), \ + MAX1027_V_CHAN(12, depth), \ + MAX1027_V_CHAN(13, depth), \ + MAX1027_V_CHAN(14, depth), \ + MAX1027_V_CHAN(15, depth) + +static const struct iio_chan_spec max1027_channels[] = { + MAX1X27_CHANNELS(10), +}; + +static const struct iio_chan_spec max1029_channels[] = { + MAX1X29_CHANNELS(10), +}; + +static const struct iio_chan_spec max1031_channels[] = { + MAX1X31_CHANNELS(10), +}; + +static const struct iio_chan_spec max1227_channels[] = { + MAX1X27_CHANNELS(12), +}; + +static const struct iio_chan_spec max1229_channels[] = { + MAX1X29_CHANNELS(12), +}; + +static const struct iio_chan_spec max1231_channels[] = { + MAX1X31_CHANNELS(12), +}; + +/* + * These devices are able to scan from 0 to N, N being the highest voltage + * channel requested by the user. The temperature can be included or not, + * but cannot be retrieved alone. Based on the below + * ->available_scan_masks, the core will select the most appropriate + * ->active_scan_mask and the "minimum" number of channels will be + * scanned and pushed to the buffers. + * + * For example, if the user wants channels 1, 4 and 5, all channels from + * 0 to 5 will be scanned and pushed to the IIO buffers. The core will then + * filter out the unneeded samples based on the ->active_scan_mask that has + * been selected and only channels 1, 4 and 5 will be available to the user + * in the shared buffer. + */ +#define MAX1X27_SCAN_MASK_TEMP BIT(0) + +#define MAX1X27_SCAN_MASKS(temp) \ + GENMASK(1, 1 - (temp)), GENMASK(2, 1 - (temp)), \ + GENMASK(3, 1 - (temp)), GENMASK(4, 1 - (temp)), \ + GENMASK(5, 1 - (temp)), GENMASK(6, 1 - (temp)), \ + GENMASK(7, 1 - (temp)), GENMASK(8, 1 - (temp)) + +#define MAX1X29_SCAN_MASKS(temp) \ + MAX1X27_SCAN_MASKS(temp), \ + GENMASK(9, 1 - (temp)), GENMASK(10, 1 - (temp)), \ + GENMASK(11, 1 - (temp)), GENMASK(12, 1 - (temp)) + +#define MAX1X31_SCAN_MASKS(temp) \ + MAX1X29_SCAN_MASKS(temp), \ + GENMASK(13, 1 - (temp)), GENMASK(14, 1 - (temp)), \ + GENMASK(15, 1 - (temp)), GENMASK(16, 1 - (temp)) + +static const unsigned long max1027_available_scan_masks[] = { + MAX1X27_SCAN_MASKS(0), + MAX1X27_SCAN_MASKS(1), + 0x00000000, +}; + +static const unsigned long max1029_available_scan_masks[] = { + MAX1X29_SCAN_MASKS(0), + MAX1X29_SCAN_MASKS(1), + 0x00000000, +}; + +static const unsigned long max1031_available_scan_masks[] = { + MAX1X31_SCAN_MASKS(0), + MAX1X31_SCAN_MASKS(1), + 0x00000000, +}; + +struct max1027_chip_info { + const struct iio_chan_spec *channels; + unsigned int num_channels; + const unsigned long *available_scan_masks; +}; + +static const struct max1027_chip_info max1027_chip_info_tbl[] = { + [max1027] = { + .channels = max1027_channels, + .num_channels = ARRAY_SIZE(max1027_channels), + .available_scan_masks = max1027_available_scan_masks, + }, + [max1029] = { + .channels = max1029_channels, + .num_channels = ARRAY_SIZE(max1029_channels), + .available_scan_masks = max1029_available_scan_masks, + }, + [max1031] = { + .channels = max1031_channels, + .num_channels = ARRAY_SIZE(max1031_channels), + .available_scan_masks = max1031_available_scan_masks, + }, + [max1227] = { + .channels = max1227_channels, + .num_channels = ARRAY_SIZE(max1227_channels), + .available_scan_masks = max1027_available_scan_masks, + }, + [max1229] = { + .channels = max1229_channels, + .num_channels = ARRAY_SIZE(max1229_channels), + .available_scan_masks = max1029_available_scan_masks, + }, + [max1231] = { + .channels = max1231_channels, + .num_channels = ARRAY_SIZE(max1231_channels), + .available_scan_masks = max1031_available_scan_masks, + }, +}; + +struct max1027_state { + const struct max1027_chip_info *info; + struct spi_device *spi; + struct iio_trigger *trig; + __be16 *buffer; + struct mutex lock; + struct completion complete; + + u8 reg __aligned(IIO_DMA_MINALIGN); +}; + +static int max1027_wait_eoc(struct iio_dev *indio_dev) +{ + struct max1027_state *st = iio_priv(indio_dev); + unsigned int conversion_time = MAX1027_CONVERSION_UDELAY; + int ret; + + if (st->spi->irq) { + ret = wait_for_completion_timeout(&st->complete, + msecs_to_jiffies(1000)); + reinit_completion(&st->complete); + if (!ret) + return -ETIMEDOUT; + } else { + if (indio_dev->active_scan_mask) + conversion_time *= hweight32(*indio_dev->active_scan_mask); + + usleep_range(conversion_time, conversion_time * 2); + } + + return 0; +} + +/* Scan from chan 0 to the highest requested channel. Include temperature on demand. */ +static int max1027_configure_chans_and_start(struct iio_dev *indio_dev) +{ + struct max1027_state *st = iio_priv(indio_dev); + + st->reg = MAX1027_CONV_REG | MAX1027_SCAN_0_N; + st->reg |= MAX1027_CHAN(fls(*indio_dev->active_scan_mask) - 2); + if (*indio_dev->active_scan_mask & MAX1X27_SCAN_MASK_TEMP) + st->reg |= MAX1027_TEMP; + + return spi_write(st->spi, &st->reg, 1); +} + +static int max1027_enable_trigger(struct iio_dev *indio_dev, bool enable) +{ + struct max1027_state *st = iio_priv(indio_dev); + + st->reg = MAX1027_SETUP_REG | MAX1027_REF_MODE2; + + /* + * Start acquisition on: + * MODE0: external hardware trigger wired to the cnvst input pin + * MODE2: conversion register write + */ + if (enable) + st->reg |= MAX1027_CKS_MODE0; + else + st->reg |= MAX1027_CKS_MODE2; + + return spi_write(st->spi, &st->reg, 1); +} + +static int max1027_read_single_value(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val) +{ + int ret; + struct max1027_state *st = iio_priv(indio_dev); + + /* Configure conversion register with the requested chan */ + st->reg = MAX1027_CONV_REG | MAX1027_CHAN(chan->channel) | + MAX1027_NOSCAN; + if (chan->type == IIO_TEMP) + st->reg |= MAX1027_TEMP; + ret = spi_write(st->spi, &st->reg, 1); + if (ret < 0) { + dev_err(&indio_dev->dev, + "Failed to configure conversion register\n"); + return ret; + } + + /* + * For an unknown reason, when we use the mode "10" (write + * conversion register), the interrupt doesn't occur every time. + * So we just wait the maximum conversion time and deliver the value. + */ + ret = max1027_wait_eoc(indio_dev); + if (ret) + return ret; + + /* Read result */ + ret = spi_read(st->spi, st->buffer, (chan->type == IIO_TEMP) ? 4 : 2); + if (ret < 0) + return ret; + + *val = be16_to_cpu(st->buffer[0]); + + return IIO_VAL_INT; +} + +static int max1027_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + int ret = 0; + struct max1027_state *st = iio_priv(indio_dev); + + guard(mutex)(&st->lock); + + switch (mask) { + case IIO_CHAN_INFO_RAW: + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; + + ret = max1027_read_single_value(indio_dev, chan, val); + iio_device_release_direct(indio_dev); + return ret; + case IIO_CHAN_INFO_SCALE: + switch (chan->type) { + case IIO_TEMP: + *val = 1; + *val2 = 8; + return IIO_VAL_FRACTIONAL; + case IIO_VOLTAGE: + *val = 2500; + *val2 = chan->scan_type.realbits; + return IIO_VAL_FRACTIONAL_LOG2; + default: + return -EINVAL; + } + default: + return -EINVAL; + } +} + +static int max1027_debugfs_reg_access(struct iio_dev *indio_dev, + unsigned int reg, unsigned int writeval, + unsigned int *readval) +{ + struct max1027_state *st = iio_priv(indio_dev); + u8 *val = (u8 *)st->buffer; + + if (readval) { + int ret = spi_read(st->spi, val, 2); + *readval = be16_to_cpu(st->buffer[0]); + return ret; + } + + *val = (u8)writeval; + return spi_write(st->spi, val, 1); +} + +static int max1027_set_cnvst_trigger_state(struct iio_trigger *trig, bool state) +{ + struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig); + int ret; + + /* + * In order to disable the convst trigger, start acquisition on + * conversion register write, which basically disables triggering + * conversions upon cnvst changes and thus has the effect of disabling + * the external hardware trigger. + */ + ret = max1027_enable_trigger(indio_dev, state); + if (ret) + return ret; + + if (state) { + ret = max1027_configure_chans_and_start(indio_dev); + if (ret) + return ret; + } + + return 0; +} + +static int max1027_read_scan(struct iio_dev *indio_dev) +{ + struct max1027_state *st = iio_priv(indio_dev); + unsigned int scanned_chans; + int ret; + + scanned_chans = fls(*indio_dev->active_scan_mask) - 1; + if (*indio_dev->active_scan_mask & MAX1X27_SCAN_MASK_TEMP) + scanned_chans++; + + /* fill buffer with all channel */ + ret = spi_read(st->spi, st->buffer, scanned_chans * 2); + if (ret < 0) + return ret; + + iio_push_to_buffers(indio_dev, st->buffer); + + return 0; +} + +static irqreturn_t max1027_handler(int irq, void *private) +{ + struct iio_dev *indio_dev = private; + struct max1027_state *st = iio_priv(indio_dev); + + /* + * If buffers are disabled (raw read) or when using external triggers, + * we just need to unlock the waiters which will then handle the data. + * + * When using the internal trigger, we must hand-off the choice of the + * handler to the core which will then lookup through the interrupt tree + * for the right handler registered with iio_triggered_buffer_setup() + * to execute, as this trigger might very well be used in conjunction + * with another device. The core will then call the relevant handler to + * perform the data processing step. + */ + if (!iio_buffer_enabled(indio_dev)) + complete(&st->complete); + else + iio_trigger_poll(indio_dev->trig); + + return IRQ_HANDLED; +} + +static irqreturn_t max1027_trigger_handler(int irq, void *private) +{ + struct iio_poll_func *pf = private; + struct iio_dev *indio_dev = pf->indio_dev; + int ret; + + if (!iio_trigger_using_own(indio_dev)) { + ret = max1027_configure_chans_and_start(indio_dev); + if (ret) + goto out; + + /* This is a threaded handler, it is fine to wait for an IRQ */ + ret = max1027_wait_eoc(indio_dev); + if (ret) + goto out; + } + + ret = max1027_read_scan(indio_dev); +out: + if (ret) + dev_err(&indio_dev->dev, + "Cannot read scanned values (%d)\n", ret); + + iio_trigger_notify_done(indio_dev->trig); + + return IRQ_HANDLED; +} + +static const struct iio_trigger_ops max1027_trigger_ops = { + .validate_device = &iio_trigger_validate_own_device, + .set_trigger_state = &max1027_set_cnvst_trigger_state, +}; + +static const struct iio_info max1027_info = { + .read_raw = &max1027_read_raw, + .debugfs_reg_access = &max1027_debugfs_reg_access, +}; + +static int max1027_probe(struct spi_device *spi) +{ + int ret; + struct iio_dev *indio_dev; + struct max1027_state *st; + + indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st)); + if (!indio_dev) { + pr_err("Can't allocate iio device\n"); + return -ENOMEM; + } + + st = iio_priv(indio_dev); + st->spi = spi; + st->info = &max1027_chip_info_tbl[spi_get_device_id(spi)->driver_data]; + + mutex_init(&st->lock); + init_completion(&st->complete); + + indio_dev->name = spi_get_device_id(spi)->name; + indio_dev->info = &max1027_info; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = st->info->channels; + indio_dev->num_channels = st->info->num_channels; + indio_dev->available_scan_masks = st->info->available_scan_masks; + + st->buffer = devm_kmalloc_array(&indio_dev->dev, + indio_dev->num_channels, 2, + GFP_KERNEL); + if (!st->buffer) + return -ENOMEM; + + /* Enable triggered buffers */ + ret = devm_iio_triggered_buffer_setup(&spi->dev, indio_dev, + &iio_pollfunc_store_time, + &max1027_trigger_handler, + NULL); + if (ret < 0) { + dev_err(&indio_dev->dev, "Failed to setup buffer\n"); + return ret; + } + + /* If there is an EOC interrupt, register the cnvst hardware trigger */ + if (spi->irq) { + st->trig = devm_iio_trigger_alloc(&spi->dev, "%s-trigger", + indio_dev->name); + if (!st->trig) { + ret = -ENOMEM; + dev_err(&indio_dev->dev, + "Failed to allocate iio trigger\n"); + return ret; + } + + st->trig->ops = &max1027_trigger_ops; + iio_trigger_set_drvdata(st->trig, indio_dev); + ret = devm_iio_trigger_register(&indio_dev->dev, + st->trig); + if (ret < 0) { + dev_err(&indio_dev->dev, + "Failed to register iio trigger\n"); + return ret; + } + + ret = devm_request_irq(&spi->dev, spi->irq, max1027_handler, + IRQF_TRIGGER_FALLING, + spi->dev.driver->name, indio_dev); + if (ret < 0) { + dev_err(&indio_dev->dev, "Failed to allocate IRQ.\n"); + return ret; + } + } + + /* Internal reset */ + st->reg = MAX1027_RST_REG; + ret = spi_write(st->spi, &st->reg, 1); + if (ret < 0) { + dev_err(&indio_dev->dev, "Failed to reset the ADC\n"); + return ret; + } + + /* Disable averaging */ + st->reg = MAX1027_AVG_REG; + ret = spi_write(st->spi, &st->reg, 1); + if (ret < 0) { + dev_err(&indio_dev->dev, "Failed to configure averaging register\n"); + return ret; + } + + /* Assume conversion on register write for now */ + ret = max1027_enable_trigger(indio_dev, false); + if (ret) + return ret; + + return devm_iio_device_register(&spi->dev, indio_dev); +} + +static struct spi_driver max1027_driver = { + .driver = { + .name = "max1027", + .of_match_table = max1027_adc_dt_ids, + }, + .probe = max1027_probe, + .id_table = max1027_id, +}; +module_spi_driver(max1027_driver); + +MODULE_AUTHOR("Philippe Reynes "); +MODULE_DESCRIPTION("MAX1X27/MAX1X29/MAX1X31 ADC"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/adc/max11100.c b/drivers/iio/adc/max11100.c new file mode 100644 index 000000000..549dea6bf --- /dev/null +++ b/drivers/iio/adc/max11100.c @@ -0,0 +1,162 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * iio/adc/max11100.c + * Maxim max11100 ADC Driver with IIO interface + * + * Copyright (C) 2016-17 Renesas Electronics Corporation + * Copyright (C) 2016-17 Jacopo Mondi + */ +#include +#include +#include +#include +#include +#include + +#include +#include + +/* + * LSB is the ADC single digital step + * 1 LSB = (vref_mv / 2 ^ 16) + * + * LSB is used to calculate analog voltage value + * from the number of ADC steps count + * + * Ain = (count * LSB) + */ +#define MAX11100_LSB_DIV (1 << 16) + +struct max11100_state { + struct regulator *vref_reg; + struct spi_device *spi; + + /* + * DMA (thus cache coherency maintenance) may require the + * transfer buffers to live in their own cache lines. + */ + u8 buffer[3] __aligned(IIO_DMA_MINALIGN); +}; + +static const struct iio_chan_spec max11100_channels[] = { + { /* [0] */ + .type = IIO_VOLTAGE, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SCALE), + }, +}; + +static int max11100_read_single(struct iio_dev *indio_dev, int *val) +{ + int ret; + struct max11100_state *state = iio_priv(indio_dev); + + ret = spi_read(state->spi, state->buffer, sizeof(state->buffer)); + if (ret) { + dev_err(&indio_dev->dev, "SPI transfer failed\n"); + return ret; + } + + /* the first 8 bits sent out from ADC must be 0s */ + if (state->buffer[0]) { + dev_err(&indio_dev->dev, "Invalid value: buffer[0] != 0\n"); + return -EINVAL; + } + + *val = get_unaligned_be16(&state->buffer[1]); + + return 0; +} + +static int max11100_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long info) +{ + int ret, vref_uv; + struct max11100_state *state = iio_priv(indio_dev); + + switch (info) { + case IIO_CHAN_INFO_RAW: + ret = max11100_read_single(indio_dev, val); + if (ret) + return ret; + + return IIO_VAL_INT; + + case IIO_CHAN_INFO_SCALE: + vref_uv = regulator_get_voltage(state->vref_reg); + if (vref_uv < 0) + /* dummy regulator "get_voltage" returns -EINVAL */ + return -EINVAL; + + *val = vref_uv / 1000; + *val2 = MAX11100_LSB_DIV; + return IIO_VAL_FRACTIONAL; + } + + return -EINVAL; +} + +static const struct iio_info max11100_info = { + .read_raw = max11100_read_raw, +}; + +static void max11100_regulator_disable(void *reg) +{ + regulator_disable(reg); +} + +static int max11100_probe(struct spi_device *spi) +{ + int ret; + struct iio_dev *indio_dev; + struct max11100_state *state; + + indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*state)); + if (!indio_dev) + return -ENOMEM; + + state = iio_priv(indio_dev); + state->spi = spi; + + indio_dev->name = "max11100"; + indio_dev->info = &max11100_info; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = max11100_channels; + indio_dev->num_channels = ARRAY_SIZE(max11100_channels); + + state->vref_reg = devm_regulator_get(&spi->dev, "vref"); + if (IS_ERR(state->vref_reg)) + return PTR_ERR(state->vref_reg); + + ret = regulator_enable(state->vref_reg); + if (ret) + return ret; + + ret = devm_add_action_or_reset(&spi->dev, max11100_regulator_disable, + state->vref_reg); + if (ret) + return ret; + + return devm_iio_device_register(&spi->dev, indio_dev); +} + +static const struct of_device_id max11100_ids[] = { + { .compatible = "maxim,max11100" }, + { } +}; +MODULE_DEVICE_TABLE(of, max11100_ids); + +static struct spi_driver max11100_driver = { + .driver = { + .name = "max11100", + .of_match_table = max11100_ids, + }, + .probe = max11100_probe, +}; + +module_spi_driver(max11100_driver); + +MODULE_AUTHOR("Jacopo Mondi "); +MODULE_DESCRIPTION("Maxim max11100 ADC Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/adc/max1118.c b/drivers/iio/adc/max1118.c new file mode 100644 index 000000000..75e51ce30 --- /dev/null +++ b/drivers/iio/adc/max1118.c @@ -0,0 +1,286 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * MAX1117/MAX1118/MAX1119 8-bit, dual-channel ADCs driver + * + * Copyright (c) 2017 Akinobu Mita + * + * Datasheet: https://datasheets.maximintegrated.com/en/ds/MAX1117-MAX1119.pdf + * + * SPI interface connections + * + * SPI MAXIM + * Master Direction MAX1117/8/9 + * ------ --------- ----------- + * nCS --> CNVST + * SCK --> SCLK + * MISO <-- DOUT + * ------ --------- ----------- + */ + +#include +#include +#include +#include +#include +#include +#include + +enum max1118_id { + max1117, + max1118, + max1119, +}; + +struct max1118 { + struct spi_device *spi; + struct mutex lock; + struct regulator *reg; + /* Ensure natural alignment of buffer elements */ + struct { + u8 channels[2]; + aligned_s64 ts; + } scan; + + u8 data __aligned(IIO_DMA_MINALIGN); +}; + +#define MAX1118_CHANNEL(ch) \ + { \ + .type = IIO_VOLTAGE, \ + .indexed = 1, \ + .channel = (ch), \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ + .scan_index = ch, \ + .scan_type = { \ + .sign = 'u', \ + .realbits = 8, \ + .storagebits = 8, \ + }, \ + } + +static const struct iio_chan_spec max1118_channels[] = { + MAX1118_CHANNEL(0), + MAX1118_CHANNEL(1), + IIO_CHAN_SOFT_TIMESTAMP(2), +}; + +static int max1118_read(struct iio_dev *indio_dev, int channel) +{ + struct max1118 *adc = iio_priv(indio_dev); + struct spi_transfer xfers[] = { + /* + * To select CH1 for conversion, CNVST pin must be brought high + * and low for a second time. + */ + { + .len = 0, + .delay = { /* > CNVST Low Time 100 ns */ + .value = 1, + .unit = SPI_DELAY_UNIT_USECS + }, + .cs_change = 1, + }, + /* + * The acquisition interval begins with the falling edge of + * CNVST. The total acquisition and conversion process takes + * <7.5us. + */ + { + .len = 0, + .delay = { + .value = 8, + .unit = SPI_DELAY_UNIT_USECS + }, + }, + { + .rx_buf = &adc->data, + .len = 1, + }, + }; + int ret; + + if (channel == 0) + ret = spi_sync_transfer(adc->spi, xfers + 1, 2); + else + ret = spi_sync_transfer(adc->spi, xfers, 3); + + if (ret) + return ret; + + return adc->data; +} + +static int max1118_get_vref_mV(struct iio_dev *indio_dev) +{ + struct max1118 *adc = iio_priv(indio_dev); + const struct spi_device_id *id = spi_get_device_id(adc->spi); + int vref_uV; + + switch (id->driver_data) { + case max1117: + return 2048; + case max1119: + return 4096; + case max1118: + vref_uV = regulator_get_voltage(adc->reg); + if (vref_uV < 0) + return vref_uV; + return vref_uV / 1000; + } + + return -ENODEV; +} + +static int max1118_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct max1118 *adc = iio_priv(indio_dev); + + switch (mask) { + case IIO_CHAN_INFO_RAW: + mutex_lock(&adc->lock); + *val = max1118_read(indio_dev, chan->channel); + mutex_unlock(&adc->lock); + if (*val < 0) + return *val; + + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + *val = max1118_get_vref_mV(indio_dev); + if (*val < 0) + return *val; + *val2 = 8; + + return IIO_VAL_FRACTIONAL_LOG2; + } + + return -EINVAL; +} + +static const struct iio_info max1118_info = { + .read_raw = max1118_read_raw, +}; + +static irqreturn_t max1118_trigger_handler(int irq, void *p) +{ + struct iio_poll_func *pf = p; + struct iio_dev *indio_dev = pf->indio_dev; + struct max1118 *adc = iio_priv(indio_dev); + int scan_index; + int i = 0; + + mutex_lock(&adc->lock); + + iio_for_each_active_channel(indio_dev, scan_index) { + const struct iio_chan_spec *scan_chan = + &indio_dev->channels[scan_index]; + int ret = max1118_read(indio_dev, scan_chan->channel); + + if (ret < 0) { + dev_warn(&adc->spi->dev, + "failed to get conversion data\n"); + goto out; + } + + adc->scan.channels[i] = ret; + i++; + } + iio_push_to_buffers_with_ts(indio_dev, &adc->scan, sizeof(adc->scan), + iio_get_time_ns(indio_dev)); +out: + mutex_unlock(&adc->lock); + + iio_trigger_notify_done(indio_dev->trig); + + return IRQ_HANDLED; +} + +static void max1118_reg_disable(void *reg) +{ + regulator_disable(reg); +} + +static int max1118_probe(struct spi_device *spi) +{ + struct iio_dev *indio_dev; + struct max1118 *adc; + const struct spi_device_id *id = spi_get_device_id(spi); + int ret; + + indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*adc)); + if (!indio_dev) + return -ENOMEM; + + adc = iio_priv(indio_dev); + adc->spi = spi; + mutex_init(&adc->lock); + + if (id->driver_data == max1118) { + adc->reg = devm_regulator_get(&spi->dev, "vref"); + if (IS_ERR(adc->reg)) + return dev_err_probe(&spi->dev, PTR_ERR(adc->reg), + "failed to get vref regulator\n"); + ret = regulator_enable(adc->reg); + if (ret) + return ret; + + ret = devm_add_action_or_reset(&spi->dev, max1118_reg_disable, + adc->reg); + if (ret) + return ret; + + } + + indio_dev->name = spi_get_device_id(spi)->name; + indio_dev->info = &max1118_info; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = max1118_channels; + indio_dev->num_channels = ARRAY_SIZE(max1118_channels); + + /* + * To reinitiate a conversion on CH0, it is necessary to allow for a + * conversion to be complete and all of the data to be read out. Once + * a conversion has been completed, the MAX1117/MAX1118/MAX1119 will go + * into AutoShutdown mode until the next conversion is initiated. + */ + max1118_read(indio_dev, 0); + + ret = devm_iio_triggered_buffer_setup(&spi->dev, indio_dev, NULL, + max1118_trigger_handler, NULL); + if (ret) + return ret; + + return devm_iio_device_register(&spi->dev, indio_dev); +} + +static const struct spi_device_id max1118_id[] = { + { .name = "max1117", .driver_data = max1117 }, + { .name = "max1118", .driver_data = max1118 }, + { .name = "max1119", .driver_data = max1119 }, + { } +}; +MODULE_DEVICE_TABLE(spi, max1118_id); + +static const struct of_device_id max1118_dt_ids[] = { + { .compatible = "maxim,max1117" }, + { .compatible = "maxim,max1118" }, + { .compatible = "maxim,max1119" }, + { } +}; +MODULE_DEVICE_TABLE(of, max1118_dt_ids); + +static struct spi_driver max1118_spi_driver = { + .driver = { + .name = "max1118", + .of_match_table = max1118_dt_ids, + }, + .probe = max1118_probe, + .id_table = max1118_id, +}; +module_spi_driver(max1118_spi_driver); + +MODULE_AUTHOR("Akinobu Mita "); +MODULE_DESCRIPTION("MAXIM MAX1117/MAX1118/MAX1119 ADCs driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/adc/max11205.c b/drivers/iio/adc/max11205.c new file mode 100644 index 000000000..63b6561dc --- /dev/null +++ b/drivers/iio/adc/max11205.c @@ -0,0 +1,180 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Maxim MAX11205 16-Bit Delta-Sigma ADC + * + * Datasheet: https://datasheets.maximintegrated.com/en/ds/MAX1240-max11205.pdf + * Copyright (C) 2022 Analog Devices, Inc. + * Author: Ramona Bolboaca + */ + +#include +#include +#include +#include + +#include +#include + +#define MAX11205_BIT_SCALE 15 +#define MAX11205A_OUT_DATA_RATE 116 +#define MAX11205B_OUT_DATA_RATE 13 + +enum max11205_chip_type { + TYPE_MAX11205A, + TYPE_MAX11205B, +}; + +struct max11205_chip_info { + unsigned int out_data_rate; + const char *name; +}; + +struct max11205_state { + const struct max11205_chip_info *chip_info; + struct regulator *vref; + struct ad_sigma_delta sd; +}; + +static const struct ad_sigma_delta_info max11205_sigma_delta_info = { + .has_registers = false, +}; + +static int max11205_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct max11205_state *st = iio_priv(indio_dev); + int reg_mv; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + return ad_sigma_delta_single_conversion(indio_dev, chan, val); + case IIO_CHAN_INFO_SCALE: + reg_mv = regulator_get_voltage(st->vref); + if (reg_mv < 0) + return reg_mv; + reg_mv /= 1000; + *val = reg_mv; + *val2 = MAX11205_BIT_SCALE; + return IIO_VAL_FRACTIONAL_LOG2; + case IIO_CHAN_INFO_SAMP_FREQ: + *val = st->chip_info->out_data_rate; + return IIO_VAL_INT; + default: + return -EINVAL; + } +} + +static const struct iio_info max11205_iio_info = { + .read_raw = max11205_read_raw, + .validate_trigger = ad_sd_validate_trigger, +}; + +static const struct iio_chan_spec max11205_channels[] = { + { + .type = IIO_VOLTAGE, + .indexed = 1, + .scan_type = { + .sign = 's', + .realbits = 16, + .storagebits = 16, + .endianness = IIO_BE, + }, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SAMP_FREQ) | + BIT(IIO_CHAN_INFO_SCALE), + }, +}; + +static const struct max11205_chip_info max11205_chip_info[] = { + [TYPE_MAX11205A] = { + .out_data_rate = MAX11205A_OUT_DATA_RATE, + .name = "max11205a", + }, + [TYPE_MAX11205B] = { + .out_data_rate = MAX11205B_OUT_DATA_RATE, + .name = "max11205b", + }, +}; + +static void max11205_reg_disable(void *reg) +{ + regulator_disable(reg); +} + +static int max11205_probe(struct spi_device *spi) +{ + struct max11205_state *st; + struct iio_dev *indio_dev; + int ret; + + indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st)); + if (!indio_dev) + return -ENOMEM; + + st = iio_priv(indio_dev); + + ad_sd_init(&st->sd, indio_dev, spi, &max11205_sigma_delta_info); + + st->chip_info = spi_get_device_match_data(spi); + + indio_dev->name = st->chip_info->name; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = max11205_channels; + indio_dev->num_channels = 1; + indio_dev->info = &max11205_iio_info; + + st->vref = devm_regulator_get(&spi->dev, "vref"); + if (IS_ERR(st->vref)) + return dev_err_probe(&spi->dev, PTR_ERR(st->vref), + "Failed to get vref regulator\n"); + + ret = regulator_enable(st->vref); + if (ret) + return ret; + + ret = devm_add_action_or_reset(&spi->dev, max11205_reg_disable, st->vref); + if (ret) + return ret; + + ret = devm_ad_sd_setup_buffer_and_trigger(&spi->dev, indio_dev); + if (ret) + return ret; + + return devm_iio_device_register(&spi->dev, indio_dev); +} + +static const struct spi_device_id max11205_spi_ids[] = { + { .name = "max11205a", .driver_data = (kernel_ulong_t)&max11205_chip_info[TYPE_MAX11205A] }, + { .name = "max11205b", .driver_data = (kernel_ulong_t)&max11205_chip_info[TYPE_MAX11205B] }, + { } +}; +MODULE_DEVICE_TABLE(spi, max11205_spi_ids); + +static const struct of_device_id max11205_dt_ids[] = { + { + .compatible = "maxim,max11205a", + .data = &max11205_chip_info[TYPE_MAX11205A], + }, + { + .compatible = "maxim,max11205b", + .data = &max11205_chip_info[TYPE_MAX11205B], + }, + { } +}; +MODULE_DEVICE_TABLE(of, max11205_dt_ids); + +static struct spi_driver max11205_spi_driver = { + .driver = { + .name = "max11205", + .of_match_table = max11205_dt_ids, + }, + .probe = max11205_probe, + .id_table = max11205_spi_ids, +}; +module_spi_driver(max11205_spi_driver); + +MODULE_AUTHOR("Ramona Bolboaca "); +MODULE_DESCRIPTION("MAX11205 ADC driver"); +MODULE_LICENSE("GPL v2"); +MODULE_IMPORT_NS("IIO_AD_SIGMA_DELTA"); diff --git a/drivers/iio/adc/max11410.c b/drivers/iio/adc/max11410.c new file mode 100644 index 000000000..23ae684cf --- /dev/null +++ b/drivers/iio/adc/max11410.c @@ -0,0 +1,1046 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * MAX11410 SPI ADC driver + * + * Copyright 2022 Analog Devices Inc. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include + +#define MAX11410_REG_CONV_START 0x01 +#define MAX11410_CONV_TYPE_SINGLE 0x00 +#define MAX11410_CONV_TYPE_CONTINUOUS 0x01 +#define MAX11410_REG_CAL_START 0x03 +#define MAX11410_CAL_START_SELF 0x00 +#define MAX11410_CAL_START_PGA 0x01 +#define MAX11410_REG_GPIO_CTRL(ch) ((ch) ? 0x05 : 0x04) +#define MAX11410_GPIO_INTRB 0xC1 +#define MAX11410_REG_FILTER 0x08 +#define MAX11410_FILTER_RATE_MASK GENMASK(3, 0) +#define MAX11410_FILTER_RATE_MAX 0x0F +#define MAX11410_FILTER_LINEF_MASK GENMASK(5, 4) +#define MAX11410_FILTER_50HZ BIT(5) +#define MAX11410_FILTER_60HZ BIT(4) +#define MAX11410_REG_CTRL 0x09 +#define MAX11410_CTRL_REFSEL_MASK GENMASK(2, 0) +#define MAX11410_CTRL_VREFN_BUF_BIT BIT(3) +#define MAX11410_CTRL_VREFP_BUF_BIT BIT(4) +#define MAX11410_CTRL_FORMAT_BIT BIT(5) +#define MAX11410_CTRL_UNIPOLAR_BIT BIT(6) +#define MAX11410_REG_MUX_CTRL0 0x0B +#define MAX11410_REG_PGA 0x0E +#define MAX11410_PGA_GAIN_MASK GENMASK(2, 0) +#define MAX11410_PGA_SIG_PATH_MASK GENMASK(5, 4) +#define MAX11410_PGA_SIG_PATH_BUFFERED 0x00 +#define MAX11410_PGA_SIG_PATH_BYPASS 0x01 +#define MAX11410_PGA_SIG_PATH_PGA 0x02 +#define MAX11410_REG_DATA0 0x30 +#define MAX11410_REG_STATUS 0x38 +#define MAX11410_STATUS_CONV_READY_BIT BIT(0) +#define MAX11410_STATUS_CAL_READY_BIT BIT(2) + +#define MAX11410_REFSEL_AVDD_AGND 0x03 +#define MAX11410_REFSEL_MAX 0x06 +#define MAX11410_SIG_PATH_MAX 0x02 +#define MAX11410_CHANNEL_INDEX_MAX 0x0A +#define MAX11410_AINP_AVDD 0x0A +#define MAX11410_AINN_GND 0x0A + +#define MAX11410_CONVERSION_TIMEOUT_MS 2000 +#define MAX11410_CALIB_TIMEOUT_MS 2000 + +#define MAX11410_SCALE_AVAIL_SIZE 8 + +enum max11410_filter { + MAX11410_FILTER_FIR5060, + MAX11410_FILTER_FIR50, + MAX11410_FILTER_FIR60, + MAX11410_FILTER_SINC4, +}; + +static const u8 max11410_sampling_len[] = { + [MAX11410_FILTER_FIR5060] = 5, + [MAX11410_FILTER_FIR50] = 6, + [MAX11410_FILTER_FIR60] = 6, + [MAX11410_FILTER_SINC4] = 10, +}; + +static const int max11410_sampling_rates[4][10][2] = { + [MAX11410_FILTER_FIR5060] = { + { 1, 100000 }, + { 2, 100000 }, + { 4, 200000 }, + { 8, 400000 }, + { 16, 800000 } + }, + [MAX11410_FILTER_FIR50] = { + { 1, 300000 }, + { 2, 700000 }, + { 5, 300000 }, + { 10, 700000 }, + { 21, 300000 }, + { 40 } + }, + [MAX11410_FILTER_FIR60] = { + { 1, 300000 }, + { 2, 700000 }, + { 5, 300000 }, + { 10, 700000 }, + { 21, 300000 }, + { 40 } + }, + [MAX11410_FILTER_SINC4] = { + { 4 }, + { 10 }, + { 20 }, + { 40 }, + { 60 }, + { 120 }, + { 240 }, + { 480 }, + { 960 }, + { 1920 } + } +}; + +struct max11410_channel_config { + u32 settling_time_us; + u32 *scale_avail; + u8 refsel; + u8 sig_path; + u8 gain; + bool bipolar; + bool buffered_vrefp; + bool buffered_vrefn; +}; + +struct max11410_state { + struct spi_device *spi_dev; + struct iio_trigger *trig; + struct completion completion; + struct mutex lock; /* Prevent changing channel config during sampling */ + struct regmap *regmap; + struct regulator *avdd; + struct regulator *vrefp[3]; + struct regulator *vrefn[3]; + struct max11410_channel_config *channels; + int irq; + struct { + u32 data __aligned(IIO_DMA_MINALIGN); + aligned_s64 ts; + } scan; +}; + +static const struct iio_chan_spec chanspec_template = { + .type = IIO_VOLTAGE, + .indexed = 1, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SCALE) | + BIT(IIO_CHAN_INFO_OFFSET), + .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ), + .info_mask_shared_by_all_available = BIT(IIO_CHAN_INFO_SAMP_FREQ), + .scan_type = { + .sign = 's', + .realbits = 24, + .storagebits = 32, + .endianness = IIO_LE, + }, +}; + +static unsigned int max11410_reg_size(unsigned int reg) +{ + /* Registers from 0x00 to 0x10 are 1 byte, the rest are 3 bytes long. */ + return reg <= 0x10 ? 1 : 3; +} + +static int max11410_write_reg(struct max11410_state *st, unsigned int reg, + unsigned int val) +{ + /* This driver only needs to write 8-bit registers */ + if (max11410_reg_size(reg) != 1) + return -EINVAL; + + return regmap_write(st->regmap, reg, val); +} + +static int max11410_read_reg(struct max11410_state *st, unsigned int reg, + int *val) +{ + int ret; + + if (max11410_reg_size(reg) == 3) { + ret = regmap_bulk_read(st->regmap, reg, &st->scan.data, 3); + if (ret) + return ret; + + *val = get_unaligned_be24(&st->scan.data); + return 0; + } + + return regmap_read(st->regmap, reg, val); +} + +static struct regulator *max11410_get_vrefp(struct max11410_state *st, + u8 refsel) +{ + refsel = refsel % 4; + if (refsel == 3) + return st->avdd; + + return st->vrefp[refsel]; +} + +static struct regulator *max11410_get_vrefn(struct max11410_state *st, + u8 refsel) +{ + if (refsel > 2) + return NULL; + + return st->vrefn[refsel]; +} + +static const struct regmap_config regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .max_register = 0x39, +}; + +static ssize_t max11410_notch_en_show(struct device *dev, + struct device_attribute *devattr, + char *buf) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct max11410_state *state = iio_priv(indio_dev); + struct iio_dev_attr *iio_attr = to_iio_dev_attr(devattr); + unsigned int val; + int ret; + + ret = max11410_read_reg(state, MAX11410_REG_FILTER, &val); + if (ret) + return ret; + + switch (iio_attr->address) { + case 0: + val = !FIELD_GET(MAX11410_FILTER_50HZ, val); + break; + case 1: + val = !FIELD_GET(MAX11410_FILTER_60HZ, val); + break; + case 2: + val = FIELD_GET(MAX11410_FILTER_LINEF_MASK, val) == 3; + break; + default: + return -EINVAL; + } + + return sysfs_emit(buf, "%d\n", val); +} + +static ssize_t max11410_notch_en_store(struct device *dev, + struct device_attribute *devattr, + const char *buf, size_t count) +{ + struct iio_dev_attr *iio_attr = to_iio_dev_attr(devattr); + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct max11410_state *state = iio_priv(indio_dev); + unsigned int filter_bits; + bool enable; + int ret; + + ret = kstrtobool(buf, &enable); + if (ret) + return ret; + + switch (iio_attr->address) { + case 0: + filter_bits = MAX11410_FILTER_50HZ; + break; + case 1: + filter_bits = MAX11410_FILTER_60HZ; + break; + case 2: + default: + filter_bits = MAX11410_FILTER_50HZ | MAX11410_FILTER_60HZ; + enable = !enable; + break; + } + + if (enable) + ret = regmap_clear_bits(state->regmap, MAX11410_REG_FILTER, + filter_bits); + else + ret = regmap_set_bits(state->regmap, MAX11410_REG_FILTER, + filter_bits); + + if (ret) + return ret; + + return count; +} + +static ssize_t in_voltage_filter2_notch_center_show(struct device *dev, + struct device_attribute *devattr, + char *buf) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct max11410_state *state = iio_priv(indio_dev); + int ret, reg, rate, filter; + + ret = regmap_read(state->regmap, MAX11410_REG_FILTER, ®); + if (ret) + return ret; + + rate = FIELD_GET(MAX11410_FILTER_RATE_MASK, reg); + rate = clamp_val(rate, 0, + max11410_sampling_len[MAX11410_FILTER_SINC4] - 1); + filter = max11410_sampling_rates[MAX11410_FILTER_SINC4][rate][0]; + + return sysfs_emit(buf, "%d\n", filter); +} + +static IIO_CONST_ATTR(in_voltage_filter0_notch_center, "50"); +static IIO_CONST_ATTR(in_voltage_filter1_notch_center, "60"); +static IIO_DEVICE_ATTR_RO(in_voltage_filter2_notch_center, 2); + +static IIO_DEVICE_ATTR(in_voltage_filter0_notch_en, 0644, + max11410_notch_en_show, max11410_notch_en_store, 0); +static IIO_DEVICE_ATTR(in_voltage_filter1_notch_en, 0644, + max11410_notch_en_show, max11410_notch_en_store, 1); +static IIO_DEVICE_ATTR(in_voltage_filter2_notch_en, 0644, + max11410_notch_en_show, max11410_notch_en_store, 2); + +static struct attribute *max11410_attributes[] = { + &iio_const_attr_in_voltage_filter0_notch_center.dev_attr.attr, + &iio_const_attr_in_voltage_filter1_notch_center.dev_attr.attr, + &iio_dev_attr_in_voltage_filter2_notch_center.dev_attr.attr, + &iio_dev_attr_in_voltage_filter0_notch_en.dev_attr.attr, + &iio_dev_attr_in_voltage_filter1_notch_en.dev_attr.attr, + &iio_dev_attr_in_voltage_filter2_notch_en.dev_attr.attr, + NULL +}; + +static const struct attribute_group max11410_attribute_group = { + .attrs = max11410_attributes, +}; + +static int max11410_set_input_mux(struct max11410_state *st, u8 ainp, u8 ainn) +{ + if (ainp > MAX11410_CHANNEL_INDEX_MAX || + ainn > MAX11410_CHANNEL_INDEX_MAX) + return -EINVAL; + + return max11410_write_reg(st, MAX11410_REG_MUX_CTRL0, + (ainp << 4) | ainn); +} + +static int max11410_configure_channel(struct max11410_state *st, + struct iio_chan_spec const *chan) +{ + struct max11410_channel_config cfg = st->channels[chan->address]; + unsigned int regval; + int ret; + + if (chan->differential) + ret = max11410_set_input_mux(st, chan->channel, chan->channel2); + else + ret = max11410_set_input_mux(st, chan->channel, + MAX11410_AINN_GND); + + if (ret) + return ret; + + regval = FIELD_PREP(MAX11410_CTRL_VREFP_BUF_BIT, cfg.buffered_vrefp) | + FIELD_PREP(MAX11410_CTRL_VREFN_BUF_BIT, cfg.buffered_vrefn) | + FIELD_PREP(MAX11410_CTRL_REFSEL_MASK, cfg.refsel) | + FIELD_PREP(MAX11410_CTRL_UNIPOLAR_BIT, cfg.bipolar ? 0 : 1); + ret = regmap_update_bits(st->regmap, MAX11410_REG_CTRL, + MAX11410_CTRL_REFSEL_MASK | + MAX11410_CTRL_VREFP_BUF_BIT | + MAX11410_CTRL_VREFN_BUF_BIT | + MAX11410_CTRL_UNIPOLAR_BIT, regval); + if (ret) + return ret; + + regval = FIELD_PREP(MAX11410_PGA_SIG_PATH_MASK, cfg.sig_path) | + FIELD_PREP(MAX11410_PGA_GAIN_MASK, cfg.gain); + ret = regmap_write(st->regmap, MAX11410_REG_PGA, regval); + if (ret) + return ret; + + if (cfg.settling_time_us) + fsleep(cfg.settling_time_us); + + return 0; +} + +static int max11410_sample(struct max11410_state *st, int *sample_raw, + struct iio_chan_spec const *chan) +{ + int val, ret; + + ret = max11410_configure_channel(st, chan); + if (ret) + return ret; + + if (st->irq > 0) + reinit_completion(&st->completion); + + /* Start Conversion */ + ret = max11410_write_reg(st, MAX11410_REG_CONV_START, + MAX11410_CONV_TYPE_SINGLE); + if (ret) + return ret; + + if (st->irq > 0) { + /* Wait for an interrupt. */ + ret = wait_for_completion_timeout(&st->completion, + msecs_to_jiffies(MAX11410_CONVERSION_TIMEOUT_MS)); + if (!ret) + return -ETIMEDOUT; + } else { + int ret2; + + /* Wait for status register Conversion Ready flag */ + ret = read_poll_timeout(max11410_read_reg, ret2, + ret2 || (val & MAX11410_STATUS_CONV_READY_BIT), + 5000, MAX11410_CONVERSION_TIMEOUT_MS * 1000, + true, st, MAX11410_REG_STATUS, &val); + if (ret) + return ret; + if (ret2) + return ret2; + } + + /* Read ADC Data */ + return max11410_read_reg(st, MAX11410_REG_DATA0, sample_raw); +} + +static int max11410_get_scale(struct max11410_state *state, + struct max11410_channel_config cfg) +{ + struct regulator *vrefp, *vrefn; + int scale; + + vrefp = max11410_get_vrefp(state, cfg.refsel); + + scale = regulator_get_voltage(vrefp) / 1000; + vrefn = max11410_get_vrefn(state, cfg.refsel); + if (vrefn) + scale -= regulator_get_voltage(vrefn) / 1000; + + if (cfg.bipolar) + scale *= 2; + + return scale >> cfg.gain; +} + +static int max11410_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long info) +{ + struct max11410_state *state = iio_priv(indio_dev); + struct max11410_channel_config cfg = state->channels[chan->address]; + int ret, reg_val, filter, rate; + + switch (info) { + case IIO_CHAN_INFO_SCALE: + *val = max11410_get_scale(state, cfg); + *val2 = chan->scan_type.realbits; + return IIO_VAL_FRACTIONAL_LOG2; + case IIO_CHAN_INFO_OFFSET: + if (cfg.bipolar) + *val = -BIT(chan->scan_type.realbits - 1); + else + *val = 0; + + return IIO_VAL_INT; + case IIO_CHAN_INFO_RAW: + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; + + mutex_lock(&state->lock); + + ret = max11410_sample(state, ®_val, chan); + + mutex_unlock(&state->lock); + + iio_device_release_direct(indio_dev); + + if (ret) + return ret; + + *val = reg_val; + + return IIO_VAL_INT; + case IIO_CHAN_INFO_SAMP_FREQ: + ret = regmap_read(state->regmap, MAX11410_REG_FILTER, ®_val); + if (ret) + return ret; + + filter = FIELD_GET(MAX11410_FILTER_LINEF_MASK, reg_val); + rate = reg_val & MAX11410_FILTER_RATE_MASK; + if (rate >= max11410_sampling_len[filter]) + rate = max11410_sampling_len[filter] - 1; + + *val = max11410_sampling_rates[filter][rate][0]; + *val2 = max11410_sampling_rates[filter][rate][1]; + + return IIO_VAL_INT_PLUS_MICRO; + } + return -EINVAL; +} + +static int __max11410_write_samp_freq(struct max11410_state *st, + int val, int val2) +{ + int ret, i, reg_val, filter; + + guard(mutex)(&st->lock); + + ret = regmap_read(st->regmap, MAX11410_REG_FILTER, ®_val); + if (ret) + return ret; + + filter = FIELD_GET(MAX11410_FILTER_LINEF_MASK, reg_val); + + for (i = 0; i < max11410_sampling_len[filter]; ++i) { + if (val == max11410_sampling_rates[filter][i][0] && + val2 == max11410_sampling_rates[filter][i][1]) + break; + } + if (i == max11410_sampling_len[filter]) + return -EINVAL; + + return regmap_write_bits(st->regmap, MAX11410_REG_FILTER, + MAX11410_FILTER_RATE_MASK, i); +} + +static int max11410_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, int val2, long mask) +{ + struct max11410_state *st = iio_priv(indio_dev); + int ret, gain; + u32 *scale_avail; + + switch (mask) { + case IIO_CHAN_INFO_SCALE: + scale_avail = st->channels[chan->address].scale_avail; + if (!scale_avail) + return -EOPNOTSUPP; + + /* Accept values in range 0.000001 <= scale < 1.000000 */ + if (val != 0 || val2 == 0) + return -EINVAL; + + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; + + /* Convert from INT_PLUS_MICRO to FRACTIONAL_LOG2 */ + val2 = val2 * DIV_ROUND_CLOSEST(BIT(24), 1000000); + val2 = DIV_ROUND_CLOSEST(scale_avail[0], val2); + gain = order_base_2(val2); + + st->channels[chan->address].gain = clamp_val(gain, 0, 7); + + iio_device_release_direct(indio_dev); + + return 0; + case IIO_CHAN_INFO_SAMP_FREQ: + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; + + ret = __max11410_write_samp_freq(st, val, val2); + iio_device_release_direct(indio_dev); + + return ret; + default: + return -EINVAL; + } +} + +static int max11410_read_avail(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + const int **vals, int *type, int *length, + long info) +{ + struct max11410_state *st = iio_priv(indio_dev); + struct max11410_channel_config cfg; + int ret, reg_val, filter; + + switch (info) { + case IIO_CHAN_INFO_SAMP_FREQ: + ret = regmap_read(st->regmap, MAX11410_REG_FILTER, ®_val); + if (ret) + return ret; + + filter = FIELD_GET(MAX11410_FILTER_LINEF_MASK, reg_val); + + *vals = (const int *)max11410_sampling_rates[filter]; + *length = max11410_sampling_len[filter] * 2; + *type = IIO_VAL_INT_PLUS_MICRO; + + return IIO_AVAIL_LIST; + case IIO_CHAN_INFO_SCALE: + cfg = st->channels[chan->address]; + + if (!cfg.scale_avail) + return -EINVAL; + + *vals = cfg.scale_avail; + *length = MAX11410_SCALE_AVAIL_SIZE * 2; + *type = IIO_VAL_FRACTIONAL_LOG2; + + return IIO_AVAIL_LIST; + } + return -EINVAL; +} + +static const struct iio_info max11410_info = { + .read_raw = max11410_read_raw, + .write_raw = max11410_write_raw, + .read_avail = max11410_read_avail, + .attrs = &max11410_attribute_group, +}; + +static irqreturn_t max11410_trigger_handler(int irq, void *p) +{ + struct iio_poll_func *pf = p; + struct iio_dev *indio_dev = pf->indio_dev; + struct max11410_state *st = iio_priv(indio_dev); + int ret; + + ret = max11410_read_reg(st, MAX11410_REG_DATA0, &st->scan.data); + if (ret) { + dev_err(&indio_dev->dev, "cannot read data\n"); + goto out; + } + + iio_push_to_buffers_with_ts(indio_dev, &st->scan, sizeof(st->scan), + iio_get_time_ns(indio_dev)); + +out: + iio_trigger_notify_done(indio_dev->trig); + + return IRQ_HANDLED; +} + +static int max11410_buffer_postenable(struct iio_dev *indio_dev) +{ + struct max11410_state *st = iio_priv(indio_dev); + int scan_ch, ret; + + scan_ch = ffs(*indio_dev->active_scan_mask) - 1; + + ret = max11410_configure_channel(st, &indio_dev->channels[scan_ch]); + if (ret) + return ret; + + /* Start continuous conversion. */ + return max11410_write_reg(st, MAX11410_REG_CONV_START, + MAX11410_CONV_TYPE_CONTINUOUS); +} + +static int max11410_buffer_predisable(struct iio_dev *indio_dev) +{ + struct max11410_state *st = iio_priv(indio_dev); + + /* Stop continuous conversion. */ + return max11410_write_reg(st, MAX11410_REG_CONV_START, + MAX11410_CONV_TYPE_SINGLE); +} + +static const struct iio_buffer_setup_ops max11410_buffer_ops = { + .postenable = &max11410_buffer_postenable, + .predisable = &max11410_buffer_predisable, + .validate_scan_mask = &iio_validate_scan_mask_onehot, +}; + +static const struct iio_trigger_ops max11410_trigger_ops = { + .validate_device = iio_trigger_validate_own_device, +}; + +static irqreturn_t max11410_interrupt(int irq, void *dev_id) +{ + struct iio_dev *indio_dev = dev_id; + struct max11410_state *st = iio_priv(indio_dev); + + if (iio_buffer_enabled(indio_dev)) + iio_trigger_poll_nested(st->trig); + else + complete(&st->completion); + + return IRQ_HANDLED; +}; + +static int max11410_parse_channels(struct max11410_state *st, + struct iio_dev *indio_dev) +{ + struct iio_chan_spec chanspec = chanspec_template; + struct device *dev = &st->spi_dev->dev; + struct max11410_channel_config *cfg; + struct iio_chan_spec *channels; + u32 reference, sig_path; + const char *node_name; + u32 inputs[2], scale; + unsigned int num_ch; + int chan_idx = 0; + int ret, i; + + num_ch = device_get_child_node_count(dev); + if (num_ch == 0) + return dev_err_probe(&indio_dev->dev, -ENODEV, + "FW has no channels defined\n"); + + /* Reserve space for soft timestamp channel */ + num_ch++; + channels = devm_kcalloc(dev, num_ch, sizeof(*channels), GFP_KERNEL); + if (!channels) + return -ENOMEM; + + st->channels = devm_kcalloc(dev, num_ch, sizeof(*st->channels), + GFP_KERNEL); + if (!st->channels) + return -ENOMEM; + + device_for_each_child_node_scoped(dev, child) { + node_name = fwnode_get_name(child); + if (fwnode_property_present(child, "diff-channels")) { + ret = fwnode_property_read_u32_array(child, + "diff-channels", + inputs, + ARRAY_SIZE(inputs)); + + chanspec.differential = 1; + } else { + ret = fwnode_property_read_u32(child, "reg", &inputs[0]); + + inputs[1] = 0; + chanspec.differential = 0; + } + if (ret) + return ret; + + if (inputs[0] > MAX11410_CHANNEL_INDEX_MAX || + inputs[1] > MAX11410_CHANNEL_INDEX_MAX) + return dev_err_probe(&indio_dev->dev, -EINVAL, + "Invalid channel index for %s, should be less than %d\n", + node_name, + MAX11410_CHANNEL_INDEX_MAX + 1); + + cfg = &st->channels[chan_idx]; + + reference = MAX11410_REFSEL_AVDD_AGND; + fwnode_property_read_u32(child, "adi,reference", &reference); + if (reference > MAX11410_REFSEL_MAX) + return dev_err_probe(&indio_dev->dev, -EINVAL, + "Invalid adi,reference value for %s, should be less than %d.\n", + node_name, MAX11410_REFSEL_MAX + 1); + + if (!max11410_get_vrefp(st, reference) || + (!max11410_get_vrefn(st, reference) && reference <= 2)) + return dev_err_probe(&indio_dev->dev, -EINVAL, + "Invalid VREF configuration for %s, either specify corresponding VREF regulators or change adi,reference property.\n", + node_name); + + sig_path = MAX11410_PGA_SIG_PATH_BUFFERED; + fwnode_property_read_u32(child, "adi,input-mode", &sig_path); + if (sig_path > MAX11410_SIG_PATH_MAX) + return dev_err_probe(&indio_dev->dev, -EINVAL, + "Invalid adi,input-mode value for %s, should be less than %d.\n", + node_name, MAX11410_SIG_PATH_MAX + 1); + + fwnode_property_read_u32(child, "settling-time-us", + &cfg->settling_time_us); + cfg->bipolar = fwnode_property_read_bool(child, "bipolar"); + cfg->buffered_vrefp = fwnode_property_read_bool(child, "adi,buffered-vrefp"); + cfg->buffered_vrefn = fwnode_property_read_bool(child, "adi,buffered-vrefn"); + cfg->refsel = reference; + cfg->sig_path = sig_path; + cfg->gain = 0; + + /* Enable scale_available property if input mode is PGA */ + if (sig_path == MAX11410_PGA_SIG_PATH_PGA) { + __set_bit(IIO_CHAN_INFO_SCALE, + &chanspec.info_mask_separate_available); + cfg->scale_avail = devm_kcalloc(dev, MAX11410_SCALE_AVAIL_SIZE * 2, + sizeof(*cfg->scale_avail), + GFP_KERNEL); + if (!cfg->scale_avail) + return -ENOMEM; + + scale = max11410_get_scale(st, *cfg); + for (i = 0; i < MAX11410_SCALE_AVAIL_SIZE; i++) { + cfg->scale_avail[2 * i] = scale >> i; + cfg->scale_avail[2 * i + 1] = chanspec.scan_type.realbits; + } + } else { + __clear_bit(IIO_CHAN_INFO_SCALE, + &chanspec.info_mask_separate_available); + } + + chanspec.address = chan_idx; + chanspec.scan_index = chan_idx; + chanspec.channel = inputs[0]; + chanspec.channel2 = inputs[1]; + + channels[chan_idx] = chanspec; + chan_idx++; + } + + channels[chan_idx] = IIO_CHAN_SOFT_TIMESTAMP(chan_idx); + + indio_dev->num_channels = chan_idx + 1; + indio_dev->channels = channels; + + return 0; +} + +static void max11410_disable_reg(void *reg) +{ + regulator_disable(reg); +} + +static int max11410_init_vref(struct device *dev, + struct regulator **vref, + const char *id) +{ + struct regulator *reg; + int ret; + + reg = devm_regulator_get_optional(dev, id); + if (PTR_ERR(reg) == -ENODEV) { + *vref = NULL; + return 0; + } else if (IS_ERR(reg)) { + return PTR_ERR(reg); + } + ret = regulator_enable(reg); + if (ret) + return dev_err_probe(dev, ret, + "Failed to enable regulator %s\n", id); + + *vref = reg; + return devm_add_action_or_reset(dev, max11410_disable_reg, reg); +} + +static int max11410_calibrate(struct max11410_state *st, u32 cal_type) +{ + int ret, ret2, val; + + ret = max11410_write_reg(st, MAX11410_REG_CAL_START, cal_type); + if (ret) + return ret; + + /* Wait for status register Calibration Ready flag */ + ret = read_poll_timeout(max11410_read_reg, ret2, + ret2 || (val & MAX11410_STATUS_CAL_READY_BIT), + 50000, MAX11410_CALIB_TIMEOUT_MS * 1000, true, + st, MAX11410_REG_STATUS, &val); + if (ret) + return ret; + + return ret2; +} + +static int max11410_self_calibrate(struct max11410_state *st) +{ + int ret, i; + + ret = regmap_write_bits(st->regmap, MAX11410_REG_FILTER, + MAX11410_FILTER_RATE_MASK, + FIELD_PREP(MAX11410_FILTER_RATE_MASK, + MAX11410_FILTER_RATE_MAX)); + if (ret) + return ret; + + ret = max11410_calibrate(st, MAX11410_CAL_START_SELF); + if (ret) + return ret; + + ret = regmap_write_bits(st->regmap, MAX11410_REG_PGA, + MAX11410_PGA_SIG_PATH_MASK, + FIELD_PREP(MAX11410_PGA_SIG_PATH_MASK, + MAX11410_PGA_SIG_PATH_PGA)); + if (ret) + return ret; + + /* PGA calibrations */ + for (i = 1; i < 8; ++i) { + ret = regmap_write_bits(st->regmap, MAX11410_REG_PGA, + MAX11410_PGA_GAIN_MASK, i); + if (ret) + return ret; + + ret = max11410_calibrate(st, MAX11410_CAL_START_PGA); + if (ret) + return ret; + } + + /* Cleanup */ + ret = regmap_write_bits(st->regmap, MAX11410_REG_PGA, + MAX11410_PGA_GAIN_MASK, 0); + if (ret) + return ret; + + ret = regmap_write_bits(st->regmap, MAX11410_REG_FILTER, + MAX11410_FILTER_RATE_MASK, 0); + if (ret) + return ret; + + return regmap_write_bits(st->regmap, MAX11410_REG_PGA, + MAX11410_PGA_SIG_PATH_MASK, + FIELD_PREP(MAX11410_PGA_SIG_PATH_MASK, + MAX11410_PGA_SIG_PATH_BUFFERED)); +} + +static int max11410_probe(struct spi_device *spi) +{ + static const char * const vrefp_regs[] = { "vref0p", "vref1p", "vref2p" }; + static const char * const vrefn_regs[] = { "vref0n", "vref1n", "vref2n" }; + struct device *dev = &spi->dev; + struct max11410_state *st; + struct iio_dev *indio_dev; + int ret, irqs[2]; + int i; + + indio_dev = devm_iio_device_alloc(dev, sizeof(*st)); + if (!indio_dev) + return -ENOMEM; + + st = iio_priv(indio_dev); + st->spi_dev = spi; + init_completion(&st->completion); + mutex_init(&st->lock); + + indio_dev->name = "max11410"; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->info = &max11410_info; + + st->regmap = devm_regmap_init_spi(spi, ®map_config); + if (IS_ERR(st->regmap)) + return dev_err_probe(dev, PTR_ERR(st->regmap), + "regmap initialization failed\n"); + + ret = max11410_init_vref(dev, &st->avdd, "avdd"); + if (ret) + return ret; + + for (i = 0; i < ARRAY_SIZE(vrefp_regs); i++) { + ret = max11410_init_vref(dev, &st->vrefp[i], vrefp_regs[i]); + if (ret) + return ret; + + ret = max11410_init_vref(dev, &st->vrefn[i], vrefn_regs[i]); + if (ret) + return ret; + } + + /* + * Regulators must be configured before parsing channels for + * validating "adi,reference" property of each channel. + */ + ret = max11410_parse_channels(st, indio_dev); + if (ret) + return ret; + + irqs[0] = fwnode_irq_get_byname(dev_fwnode(dev), "gpio0"); + irqs[1] = fwnode_irq_get_byname(dev_fwnode(dev), "gpio1"); + + if (irqs[0] > 0) { + st->irq = irqs[0]; + ret = regmap_write(st->regmap, MAX11410_REG_GPIO_CTRL(0), + MAX11410_GPIO_INTRB); + } else if (irqs[1] > 0) { + st->irq = irqs[1]; + ret = regmap_write(st->regmap, MAX11410_REG_GPIO_CTRL(1), + MAX11410_GPIO_INTRB); + } else if (spi->irq > 0) { + return dev_err_probe(dev, -ENODEV, + "no interrupt name specified"); + } + + if (ret) + return ret; + + ret = regmap_set_bits(st->regmap, MAX11410_REG_CTRL, + MAX11410_CTRL_FORMAT_BIT); + if (ret) + return ret; + + ret = devm_iio_triggered_buffer_setup(dev, indio_dev, NULL, + &max11410_trigger_handler, + &max11410_buffer_ops); + if (ret) + return ret; + + if (st->irq > 0) { + st->trig = devm_iio_trigger_alloc(dev, "%s-dev%d", + indio_dev->name, + iio_device_id(indio_dev)); + if (!st->trig) + return -ENOMEM; + + st->trig->ops = &max11410_trigger_ops; + ret = devm_iio_trigger_register(dev, st->trig); + if (ret) + return ret; + + ret = devm_request_threaded_irq(dev, st->irq, NULL, + &max11410_interrupt, + IRQF_ONESHOT, "max11410", + indio_dev); + if (ret) + return ret; + } + + ret = max11410_self_calibrate(st); + if (ret) + return dev_err_probe(dev, ret, + "cannot perform device self calibration\n"); + + return devm_iio_device_register(dev, indio_dev); +} + +static const struct of_device_id max11410_spi_of_id[] = { + { .compatible = "adi,max11410" }, + { } +}; +MODULE_DEVICE_TABLE(of, max11410_spi_of_id); + +static const struct spi_device_id max11410_id[] = { + { .name = "max11410" }, + { } +}; +MODULE_DEVICE_TABLE(spi, max11410_id); + +static struct spi_driver max11410_driver = { + .driver = { + .name = "max11410", + .of_match_table = max11410_spi_of_id, + }, + .probe = max11410_probe, + .id_table = max11410_id, +}; +module_spi_driver(max11410_driver); + +MODULE_AUTHOR("David Jung "); +MODULE_AUTHOR("Ibrahim Tilki "); +MODULE_DESCRIPTION("Analog Devices MAX11410 ADC"); +MODULE_LICENSE("GPL"); diff --git a/drivers/iio/adc/max1241.c b/drivers/iio/adc/max1241.c new file mode 100644 index 000000000..b3cecbf9a --- /dev/null +++ b/drivers/iio/adc/max1241.c @@ -0,0 +1,198 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * MAX1241 low-power, 12-bit serial ADC + * + * Datasheet: https://datasheets.maximintegrated.com/en/ds/MAX1240-MAX1241.pdf + */ + +#include +#include +#include +#include +#include +#include + +#define MAX1241_VAL_MASK GENMASK(11, 0) +#define MAX1241_SHUTDOWN_DELAY_USEC 4 + +struct max1241 { + struct spi_device *spi; + struct mutex lock; + struct regulator *vref; + struct gpio_desc *shutdown; + + __be16 data __aligned(IIO_DMA_MINALIGN); +}; + +static const struct iio_chan_spec max1241_channels[] = { + { + .type = IIO_VOLTAGE, + .indexed = 1, + .channel = 0, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SCALE), + }, +}; + +static int max1241_read(struct max1241 *adc) +{ + struct spi_transfer xfers[] = { + /* + * Begin conversion by bringing /CS low for at least + * tconv us. + */ + { + .len = 0, + .delay.value = 8, + .delay.unit = SPI_DELAY_UNIT_USECS, + }, + /* + * Then read two bytes of data in our RX buffer. + */ + { + .rx_buf = &adc->data, + .len = 2, + }, + }; + + return spi_sync_transfer(adc->spi, xfers, ARRAY_SIZE(xfers)); +} + +static int max1241_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + int ret, vref_uV; + struct max1241 *adc = iio_priv(indio_dev); + + switch (mask) { + case IIO_CHAN_INFO_RAW: + mutex_lock(&adc->lock); + + if (adc->shutdown) { + gpiod_set_value(adc->shutdown, 0); + udelay(MAX1241_SHUTDOWN_DELAY_USEC); + ret = max1241_read(adc); + gpiod_set_value(adc->shutdown, 1); + } else + ret = max1241_read(adc); + + if (ret) { + mutex_unlock(&adc->lock); + return ret; + } + + *val = (be16_to_cpu(adc->data) >> 3) & MAX1241_VAL_MASK; + + mutex_unlock(&adc->lock); + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + vref_uV = regulator_get_voltage(adc->vref); + + if (vref_uV < 0) + return vref_uV; + + *val = vref_uV / 1000; + *val2 = 12; + + return IIO_VAL_FRACTIONAL_LOG2; + default: + return -EINVAL; + } +} + +static const struct iio_info max1241_info = { + .read_raw = max1241_read_raw, +}; + +static void max1241_disable_vref_action(void *data) +{ + struct max1241 *adc = data; + struct device *dev = &adc->spi->dev; + int err; + + err = regulator_disable(adc->vref); + if (err) + dev_err(dev, "could not disable vref regulator.\n"); +} + +static int max1241_probe(struct spi_device *spi) +{ + struct device *dev = &spi->dev; + struct iio_dev *indio_dev; + struct max1241 *adc; + int ret; + + indio_dev = devm_iio_device_alloc(dev, sizeof(*adc)); + if (!indio_dev) + return -ENOMEM; + + adc = iio_priv(indio_dev); + adc->spi = spi; + mutex_init(&adc->lock); + + ret = devm_regulator_get_enable(dev, "vdd"); + if (ret) + return dev_err_probe(dev, ret, + "failed to get/enable vdd regulator\n"); + + adc->vref = devm_regulator_get(dev, "vref"); + if (IS_ERR(adc->vref)) + return dev_err_probe(dev, PTR_ERR(adc->vref), + "failed to get vref regulator\n"); + + ret = regulator_enable(adc->vref); + if (ret) + return ret; + + ret = devm_add_action_or_reset(dev, max1241_disable_vref_action, adc); + if (ret) { + dev_err(dev, "could not set up vref regulator cleanup action\n"); + return ret; + } + + adc->shutdown = devm_gpiod_get_optional(dev, "shutdown", + GPIOD_OUT_HIGH); + if (IS_ERR(adc->shutdown)) + return dev_err_probe(dev, PTR_ERR(adc->shutdown), + "cannot get shutdown gpio\n"); + + if (adc->shutdown) + dev_dbg(dev, "shutdown pin passed, low-power mode enabled"); + else + dev_dbg(dev, "no shutdown pin passed, low-power mode disabled"); + + indio_dev->name = "max1241"; + indio_dev->info = &max1241_info; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = max1241_channels; + indio_dev->num_channels = ARRAY_SIZE(max1241_channels); + + return devm_iio_device_register(dev, indio_dev); +} + +static const struct spi_device_id max1241_id[] = { + { .name = "max1241" }, + { } +}; +MODULE_DEVICE_TABLE(spi, max1241_id); + +static const struct of_device_id max1241_dt_ids[] = { + { .compatible = "maxim,max1241" }, + { } +}; +MODULE_DEVICE_TABLE(of, max1241_dt_ids); + +static struct spi_driver max1241_spi_driver = { + .driver = { + .name = "max1241", + .of_match_table = max1241_dt_ids, + }, + .probe = max1241_probe, + .id_table = max1241_id, +}; +module_spi_driver(max1241_spi_driver); + +MODULE_AUTHOR("Alexandru Lazar "); +MODULE_DESCRIPTION("MAX1241 ADC driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/adc/max1363.c b/drivers/iio/adc/max1363.c new file mode 100644 index 000000000..65a2d92bb --- /dev/null +++ b/drivers/iio/adc/max1363.c @@ -0,0 +1,1691 @@ +// SPDX-License-Identifier: GPL-2.0-only + /* + * iio/adc/max1363.c + * Copyright (C) 2008-2010 Jonathan Cameron + * + * based on linux/drivers/i2c/chips/max123x + * Copyright (C) 2002-2004 Stefan Eletzhofer + * + * based on linux/drivers/acron/char/pcf8583.c + * Copyright (C) 2000 Russell King + * + * Driver for max1363 and similar chips. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#define MAX1363_SETUP_BYTE(a) ((a) | 0x80) + +/* There is a fair bit more defined here than currently + * used, but the intention is to support everything these + * chips do in the long run */ + +/* see data sheets */ +/* max1363 and max1236, max1237, max1238, max1239 */ +#define MAX1363_SETUP_AIN3_IS_AIN3_REF_IS_VDD 0x00 +#define MAX1363_SETUP_AIN3_IS_REF_EXT_TO_REF 0x20 +#define MAX1363_SETUP_AIN3_IS_AIN3_REF_IS_INT 0x40 +#define MAX1363_SETUP_AIN3_IS_REF_REF_IS_INT 0x60 +#define MAX1363_SETUP_POWER_UP_INT_REF 0x10 +#define MAX1363_SETUP_POWER_DOWN_INT_REF 0x00 + +/* think about including max11600 etc - more settings */ +#define MAX1363_SETUP_EXT_CLOCK 0x08 +#define MAX1363_SETUP_INT_CLOCK 0x00 +#define MAX1363_SETUP_UNIPOLAR 0x00 +#define MAX1363_SETUP_BIPOLAR 0x04 +#define MAX1363_SETUP_RESET 0x00 +#define MAX1363_SETUP_NORESET 0x02 +/* max1363 only - though don't care on others. + * For now monitor modes are not implemented as the relevant + * line is not connected on my test board. + * The definitions are here as I intend to add this soon. + */ +#define MAX1363_SETUP_MONITOR_SETUP 0x01 + +/* Specific to the max1363 */ +#define MAX1363_MON_RESET_CHAN(a) (1 << ((a) + 4)) +#define MAX1363_MON_INT_ENABLE 0x01 + +/* defined for readability reasons */ +/* All chips */ +#define MAX1363_CONFIG_BYTE(a) ((a)) + +#define MAX1363_CONFIG_SE 0x01 +#define MAX1363_CONFIG_DE 0x00 +#define MAX1363_CONFIG_SCAN_TO_CS 0x00 +#define MAX1363_CONFIG_SCAN_SINGLE_8 0x20 +#define MAX1363_CONFIG_SCAN_MONITOR_MODE 0x40 +#define MAX1363_CONFIG_SCAN_SINGLE_1 0x60 +/* max123{6-9} only */ +#define MAX1236_SCAN_MID_TO_CHANNEL 0x40 + +/* max1363 only - merely part of channel selects or don't care for others */ +#define MAX1363_CONFIG_EN_MON_MODE_READ 0x18 + +#define MAX1363_CHANNEL_SEL(a) ((a) << 1) + +/* max1363 strictly 0x06 - but doesn't matter */ +#define MAX1363_CHANNEL_SEL_MASK 0x1E +#define MAX1363_SCAN_MASK 0x60 +#define MAX1363_SE_DE_MASK 0x01 + +#define MAX1363_MAX_CHANNELS 25 +/** + * struct max1363_mode - scan mode information + * @conf: The corresponding value of the configuration register + * @modemask: Bit mask corresponding to channels enabled in this mode + */ +struct max1363_mode { + int8_t conf; + DECLARE_BITMAP(modemask, MAX1363_MAX_CHANNELS); +}; + +/* This must be maintained along side the max1363_mode_table in max1363_core */ +enum max1363_modes { + /* Single read of a single channel */ + _s0, _s1, _s2, _s3, _s4, _s5, _s6, _s7, _s8, _s9, _s10, _s11, + /* Differential single read */ + d0m1, d2m3, d4m5, d6m7, d8m9, d10m11, + d1m0, d3m2, d5m4, d7m6, d9m8, d11m10, + /* Scan to channel and mid to channel where overlapping */ + s0to1, s0to2, s2to3, s0to3, s0to4, s0to5, s0to6, + s6to7, s0to7, s6to8, s0to8, s6to9, + s0to9, s6to10, s0to10, s6to11, s0to11, + /* Differential scan to channel and mid to channel where overlapping */ + d0m1to2m3, d0m1to4m5, d0m1to6m7, d6m7to8m9, + d0m1to8m9, d6m7to10m11, d0m1to10m11, d1m0to3m2, + d1m0to5m4, d1m0to7m6, d7m6to9m8, d1m0to9m8, + d7m6to11m10, d1m0to11m10, +}; + +/** + * struct max1363_chip_info - chip specific information + * @info: iio core function callbacks structure + * @channels: channel specification + * @num_channels: number of channels + * @mode_list: array of available scan modes + * @default_mode: the scan mode in which the chip starts up + * @int_vref_mv: the internal reference voltage + * @num_modes: number of modes + * @bits: accuracy of the adc in bits + */ +struct max1363_chip_info { + const struct iio_info *info; + const struct iio_chan_spec *channels; + int num_channels; + const enum max1363_modes *mode_list; + enum max1363_modes default_mode; + u16 int_vref_mv; + u8 num_modes; + u8 bits; +}; + +/** + * struct max1363_state - driver instance specific data + * @client: i2c_client + * @setupbyte: cache of current device setup byte + * @configbyte: cache of current device config byte + * @chip_info: chip model specific constants, available modes, etc. + * @current_mode: the scan mode of this chip + * @lock: lock to ensure state is consistent + * @monitor_on: whether monitor mode is enabled + * @monitor_speed: parameter corresponding to device monitor speed setting + * @mask_high: bitmask for enabled high thresholds + * @mask_low: bitmask for enabled low thresholds + * @thresh_high: high threshold values + * @thresh_low: low threshold values + * @vref: Reference voltage regulator + * @vref_uv: Actual (external or internal) reference voltage + * @send: function used to send data to the chip + * @recv: function used to receive data from the chip + * @data: buffer to store channel data and timestamp + */ +struct max1363_state { + struct i2c_client *client; + u8 setupbyte; + u8 configbyte; + const struct max1363_chip_info *chip_info; + const struct max1363_mode *current_mode; + struct mutex lock; + + /* Using monitor modes and buffer at the same time is + currently not supported */ + bool monitor_on; + unsigned int monitor_speed:3; + u8 mask_high; + u8 mask_low; + /* 4x unipolar first then the fours bipolar ones */ + s16 thresh_high[8]; + s16 thresh_low[8]; + struct regulator *vref; + u32 vref_uv; + int (*send)(const struct i2c_client *client, + const char *buf, int count); + int (*recv)(const struct i2c_client *client, + char *buf, int count); + struct { + u8 buf[MAX1363_MAX_CHANNELS * 2]; + aligned_s64 ts; + } data; +}; + +#define MAX1363_MODE_SINGLE(_num, _mask) { \ + .conf = MAX1363_CHANNEL_SEL(_num) \ + | MAX1363_CONFIG_SCAN_SINGLE_1 \ + | MAX1363_CONFIG_SE, \ + .modemask[0] = _mask, \ + } + +#define MAX1363_MODE_SCAN_TO_CHANNEL(_num, _mask) { \ + .conf = MAX1363_CHANNEL_SEL(_num) \ + | MAX1363_CONFIG_SCAN_TO_CS \ + | MAX1363_CONFIG_SE, \ + .modemask[0] = _mask, \ + } + +/* note not available for max1363 hence naming */ +#define MAX1236_MODE_SCAN_MID_TO_CHANNEL(_mid, _num, _mask) { \ + .conf = MAX1363_CHANNEL_SEL(_num) \ + | MAX1236_SCAN_MID_TO_CHANNEL \ + | MAX1363_CONFIG_SE, \ + .modemask[0] = _mask \ +} + +#define MAX1363_MODE_DIFF_SINGLE(_nump, _numm, _mask) { \ + .conf = MAX1363_CHANNEL_SEL(_nump) \ + | MAX1363_CONFIG_SCAN_SINGLE_1 \ + | MAX1363_CONFIG_DE, \ + .modemask[0] = _mask \ + } + +/* Can't think how to automate naming so specify for now */ +#define MAX1363_MODE_DIFF_SCAN_TO_CHANNEL(_num, _numvals, _mask) { \ + .conf = MAX1363_CHANNEL_SEL(_num) \ + | MAX1363_CONFIG_SCAN_TO_CS \ + | MAX1363_CONFIG_DE, \ + .modemask[0] = _mask \ + } + +/* note only available for max1363 hence naming */ +#define MAX1236_MODE_DIFF_SCAN_MID_TO_CHANNEL(_num, _numvals, _mask) { \ + .conf = MAX1363_CHANNEL_SEL(_num) \ + | MAX1236_SCAN_MID_TO_CHANNEL \ + | MAX1363_CONFIG_SE, \ + .modemask[0] = _mask \ +} + +static const struct max1363_mode max1363_mode_table[] = { + /* All of the single channel options first */ + MAX1363_MODE_SINGLE(0, 1 << 0), + MAX1363_MODE_SINGLE(1, 1 << 1), + MAX1363_MODE_SINGLE(2, 1 << 2), + MAX1363_MODE_SINGLE(3, 1 << 3), + MAX1363_MODE_SINGLE(4, 1 << 4), + MAX1363_MODE_SINGLE(5, 1 << 5), + MAX1363_MODE_SINGLE(6, 1 << 6), + MAX1363_MODE_SINGLE(7, 1 << 7), + MAX1363_MODE_SINGLE(8, 1 << 8), + MAX1363_MODE_SINGLE(9, 1 << 9), + MAX1363_MODE_SINGLE(10, 1 << 10), + MAX1363_MODE_SINGLE(11, 1 << 11), + + MAX1363_MODE_DIFF_SINGLE(0, 1, 1 << 12), + MAX1363_MODE_DIFF_SINGLE(2, 3, 1 << 13), + MAX1363_MODE_DIFF_SINGLE(4, 5, 1 << 14), + MAX1363_MODE_DIFF_SINGLE(6, 7, 1 << 15), + MAX1363_MODE_DIFF_SINGLE(8, 9, 1 << 16), + MAX1363_MODE_DIFF_SINGLE(10, 11, 1 << 17), + MAX1363_MODE_DIFF_SINGLE(1, 0, 1 << 18), + MAX1363_MODE_DIFF_SINGLE(3, 2, 1 << 19), + MAX1363_MODE_DIFF_SINGLE(5, 4, 1 << 20), + MAX1363_MODE_DIFF_SINGLE(7, 6, 1 << 21), + MAX1363_MODE_DIFF_SINGLE(9, 8, 1 << 22), + MAX1363_MODE_DIFF_SINGLE(11, 10, 1 << 23), + + /* The multichannel scans next */ + MAX1363_MODE_SCAN_TO_CHANNEL(1, 0x003), + MAX1363_MODE_SCAN_TO_CHANNEL(2, 0x007), + MAX1236_MODE_SCAN_MID_TO_CHANNEL(2, 3, 0x00C), + MAX1363_MODE_SCAN_TO_CHANNEL(3, 0x00F), + MAX1363_MODE_SCAN_TO_CHANNEL(4, 0x01F), + MAX1363_MODE_SCAN_TO_CHANNEL(5, 0x03F), + MAX1363_MODE_SCAN_TO_CHANNEL(6, 0x07F), + MAX1236_MODE_SCAN_MID_TO_CHANNEL(6, 7, 0x0C0), + MAX1363_MODE_SCAN_TO_CHANNEL(7, 0x0FF), + MAX1236_MODE_SCAN_MID_TO_CHANNEL(6, 8, 0x1C0), + MAX1363_MODE_SCAN_TO_CHANNEL(8, 0x1FF), + MAX1236_MODE_SCAN_MID_TO_CHANNEL(6, 9, 0x3C0), + MAX1363_MODE_SCAN_TO_CHANNEL(9, 0x3FF), + MAX1236_MODE_SCAN_MID_TO_CHANNEL(6, 10, 0x7C0), + MAX1363_MODE_SCAN_TO_CHANNEL(10, 0x7FF), + MAX1236_MODE_SCAN_MID_TO_CHANNEL(6, 11, 0xFC0), + MAX1363_MODE_SCAN_TO_CHANNEL(11, 0xFFF), + + MAX1363_MODE_DIFF_SCAN_TO_CHANNEL(2, 2, 0x003000), + MAX1363_MODE_DIFF_SCAN_TO_CHANNEL(4, 3, 0x007000), + MAX1363_MODE_DIFF_SCAN_TO_CHANNEL(6, 4, 0x00F000), + MAX1236_MODE_DIFF_SCAN_MID_TO_CHANNEL(8, 2, 0x018000), + MAX1363_MODE_DIFF_SCAN_TO_CHANNEL(8, 5, 0x01F000), + MAX1236_MODE_DIFF_SCAN_MID_TO_CHANNEL(10, 3, 0x038000), + MAX1363_MODE_DIFF_SCAN_TO_CHANNEL(10, 6, 0x3F000), + MAX1363_MODE_DIFF_SCAN_TO_CHANNEL(3, 2, 0x0C0000), + MAX1363_MODE_DIFF_SCAN_TO_CHANNEL(5, 3, 0x1C0000), + MAX1363_MODE_DIFF_SCAN_TO_CHANNEL(7, 4, 0x3C0000), + MAX1236_MODE_DIFF_SCAN_MID_TO_CHANNEL(9, 2, 0x600000), + MAX1363_MODE_DIFF_SCAN_TO_CHANNEL(9, 5, 0x7C0000), + MAX1236_MODE_DIFF_SCAN_MID_TO_CHANNEL(11, 3, 0xE00000), + MAX1363_MODE_DIFF_SCAN_TO_CHANNEL(11, 6, 0xFC0000), +}; + +static const struct max1363_mode +*max1363_match_mode(const unsigned long *mask, + const struct max1363_chip_info *ci) +{ + int i; + if (mask) + for (i = 0; i < ci->num_modes; i++) + if (bitmap_subset(mask, + max1363_mode_table[ci->mode_list[i]]. + modemask, + MAX1363_MAX_CHANNELS)) + return &max1363_mode_table[ci->mode_list[i]]; + return NULL; +} + +static int max1363_smbus_send(const struct i2c_client *client, const char *buf, + int count) +{ + int i, err; + + for (i = err = 0; err == 0 && i < count; ++i) + err = i2c_smbus_write_byte(client, buf[i]); + + return err ? err : count; +} + +static int max1363_smbus_recv(const struct i2c_client *client, char *buf, + int count) +{ + int i, ret; + + for (i = 0; i < count; ++i) { + ret = i2c_smbus_read_byte(client); + if (ret < 0) + return ret; + buf[i] = ret; + } + + return count; +} + +static int max1363_write_basic_config(struct max1363_state *st) +{ + u8 tx_buf[2] = { st->setupbyte, st->configbyte }; + + return st->send(st->client, tx_buf, 2); +} + +static int max1363_set_scan_mode(struct max1363_state *st) +{ + st->configbyte &= ~(MAX1363_CHANNEL_SEL_MASK + | MAX1363_SCAN_MASK + | MAX1363_SE_DE_MASK); + st->configbyte |= st->current_mode->conf; + + return max1363_write_basic_config(st); +} + +static int max1363_read_single_chan(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, + long m) +{ + s32 data; + u8 rxbuf[2]; + struct max1363_state *st = iio_priv(indio_dev); + struct i2c_client *client = st->client; + + guard(mutex)(&st->lock); + + /* + * If monitor mode is enabled, the method for reading a single + * channel will have to be rather different and has not yet + * been implemented. + * + * Also, cannot read directly if buffered capture enabled. + */ + if (st->monitor_on) + return -EBUSY; + + /* Check to see if current scan mode is correct */ + if (st->current_mode != &max1363_mode_table[chan->address]) { + int ret; + + /* Update scan mode if needed */ + st->current_mode = &max1363_mode_table[chan->address]; + ret = max1363_set_scan_mode(st); + if (ret < 0) + return ret; + } + if (st->chip_info->bits != 8) { + /* Get reading */ + data = st->recv(client, rxbuf, 2); + if (data < 0) + return data; + + data = get_unaligned_be16(rxbuf) & + ((1 << st->chip_info->bits) - 1); + } else { + /* Get reading */ + data = st->recv(client, rxbuf, 1); + if (data < 0) + return data; + + data = rxbuf[0]; + } + *val = data; + + return 0; +} + +static int max1363_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, + int *val2, + long m) +{ + struct max1363_state *st = iio_priv(indio_dev); + int ret; + + switch (m) { + case IIO_CHAN_INFO_RAW: + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; + + ret = max1363_read_single_chan(indio_dev, chan, val, m); + iio_device_release_direct(indio_dev); + if (ret < 0) + return ret; + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + *val = st->vref_uv / 1000; + *val2 = st->chip_info->bits; + return IIO_VAL_FRACTIONAL_LOG2; + default: + return -EINVAL; + } + return 0; +} + +/* Applies to max1363 */ +static const enum max1363_modes max1363_mode_list[] = { + _s0, _s1, _s2, _s3, + s0to1, s0to2, s0to3, + d0m1, d2m3, d1m0, d3m2, + d0m1to2m3, d1m0to3m2, +}; + +static const struct iio_event_spec max1363_events[] = { + { + .type = IIO_EV_TYPE_THRESH, + .dir = IIO_EV_DIR_RISING, + .mask_separate = BIT(IIO_EV_INFO_VALUE) | + BIT(IIO_EV_INFO_ENABLE), + }, { + .type = IIO_EV_TYPE_THRESH, + .dir = IIO_EV_DIR_FALLING, + .mask_separate = BIT(IIO_EV_INFO_VALUE) | + BIT(IIO_EV_INFO_ENABLE), + }, +}; + +#define MAX1363_CHAN_U(num, addr, si, bits, ev_spec, num_ev_spec) \ + { \ + .type = IIO_VOLTAGE, \ + .indexed = 1, \ + .channel = num, \ + .address = addr, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ + .datasheet_name = "AIN"#num, \ + .scan_type = { \ + .sign = 'u', \ + .realbits = bits, \ + .storagebits = (bits > 8) ? 16 : 8, \ + .endianness = IIO_BE, \ + }, \ + .scan_index = si, \ + .event_spec = ev_spec, \ + .num_event_specs = num_ev_spec, \ + } + +/* bipolar channel */ +#define MAX1363_CHAN_B(num, num2, addr, si, bits, ev_spec, num_ev_spec) \ + { \ + .type = IIO_VOLTAGE, \ + .differential = 1, \ + .indexed = 1, \ + .channel = num, \ + .channel2 = num2, \ + .address = addr, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ + .datasheet_name = "AIN"#num"-AIN"#num2, \ + .scan_type = { \ + .sign = 's', \ + .realbits = bits, \ + .storagebits = (bits > 8) ? 16 : 8, \ + .endianness = IIO_BE, \ + }, \ + .scan_index = si, \ + .event_spec = ev_spec, \ + .num_event_specs = num_ev_spec, \ + } + +#define MAX1363_4X_CHANS(bits, ev_spec, num_ev_spec) { \ + MAX1363_CHAN_U(0, _s0, 0, bits, ev_spec, num_ev_spec), \ + MAX1363_CHAN_U(1, _s1, 1, bits, ev_spec, num_ev_spec), \ + MAX1363_CHAN_U(2, _s2, 2, bits, ev_spec, num_ev_spec), \ + MAX1363_CHAN_U(3, _s3, 3, bits, ev_spec, num_ev_spec), \ + MAX1363_CHAN_B(0, 1, d0m1, 12, bits, ev_spec, num_ev_spec), \ + MAX1363_CHAN_B(2, 3, d2m3, 13, bits, ev_spec, num_ev_spec), \ + MAX1363_CHAN_B(1, 0, d1m0, 18, bits, ev_spec, num_ev_spec), \ + MAX1363_CHAN_B(3, 2, d3m2, 19, bits, ev_spec, num_ev_spec), \ + IIO_CHAN_SOFT_TIMESTAMP(8) \ + } + +static const struct iio_chan_spec max1036_channels[] = + MAX1363_4X_CHANS(8, NULL, 0); +static const struct iio_chan_spec max1136_channels[] = + MAX1363_4X_CHANS(10, NULL, 0); +static const struct iio_chan_spec max1236_channels[] = + MAX1363_4X_CHANS(12, NULL, 0); +static const struct iio_chan_spec max1361_channels[] = + MAX1363_4X_CHANS(10, max1363_events, ARRAY_SIZE(max1363_events)); +static const struct iio_chan_spec max1363_channels[] = + MAX1363_4X_CHANS(12, max1363_events, ARRAY_SIZE(max1363_events)); + +/* Applies to max1236, max1237 */ +static const enum max1363_modes max1236_mode_list[] = { + _s0, _s1, _s2, _s3, + s0to1, s0to2, s2to3, s0to3, + d0m1, d2m3, d1m0, d3m2, + d0m1to2m3, d1m0to3m2, +}; + +/* Applies to max1238, max1239 */ +static const enum max1363_modes max1238_mode_list[] = { + _s0, _s1, _s2, _s3, _s4, _s5, _s6, _s7, _s8, _s9, _s10, _s11, + s0to1, s0to2, s0to3, s0to4, s0to5, s0to6, + s6to7, s6to8, s6to9, s6to10, s6to11, + s0to7, s0to8, s0to9, s0to10, s0to11, + d0m1, d2m3, d4m5, d6m7, d8m9, d10m11, + d1m0, d3m2, d5m4, d7m6, d9m8, d11m10, + d0m1to2m3, d0m1to4m5, d0m1to6m7, d6m7to8m9, + d0m1to8m9, d6m7to10m11, d0m1to10m11, d1m0to3m2, + d1m0to5m4, d1m0to7m6, d7m6to9m8, d1m0to9m8, + d7m6to11m10, d1m0to11m10, +}; + +#define MAX1363_12X_CHANS(bits) { \ + MAX1363_CHAN_U(0, _s0, 0, bits, NULL, 0), \ + MAX1363_CHAN_U(1, _s1, 1, bits, NULL, 0), \ + MAX1363_CHAN_U(2, _s2, 2, bits, NULL, 0), \ + MAX1363_CHAN_U(3, _s3, 3, bits, NULL, 0), \ + MAX1363_CHAN_U(4, _s4, 4, bits, NULL, 0), \ + MAX1363_CHAN_U(5, _s5, 5, bits, NULL, 0), \ + MAX1363_CHAN_U(6, _s6, 6, bits, NULL, 0), \ + MAX1363_CHAN_U(7, _s7, 7, bits, NULL, 0), \ + MAX1363_CHAN_U(8, _s8, 8, bits, NULL, 0), \ + MAX1363_CHAN_U(9, _s9, 9, bits, NULL, 0), \ + MAX1363_CHAN_U(10, _s10, 10, bits, NULL, 0), \ + MAX1363_CHAN_U(11, _s11, 11, bits, NULL, 0), \ + MAX1363_CHAN_B(0, 1, d0m1, 12, bits, NULL, 0), \ + MAX1363_CHAN_B(2, 3, d2m3, 13, bits, NULL, 0), \ + MAX1363_CHAN_B(4, 5, d4m5, 14, bits, NULL, 0), \ + MAX1363_CHAN_B(6, 7, d6m7, 15, bits, NULL, 0), \ + MAX1363_CHAN_B(8, 9, d8m9, 16, bits, NULL, 0), \ + MAX1363_CHAN_B(10, 11, d10m11, 17, bits, NULL, 0), \ + MAX1363_CHAN_B(1, 0, d1m0, 18, bits, NULL, 0), \ + MAX1363_CHAN_B(3, 2, d3m2, 19, bits, NULL, 0), \ + MAX1363_CHAN_B(5, 4, d5m4, 20, bits, NULL, 0), \ + MAX1363_CHAN_B(7, 6, d7m6, 21, bits, NULL, 0), \ + MAX1363_CHAN_B(9, 8, d9m8, 22, bits, NULL, 0), \ + MAX1363_CHAN_B(11, 10, d11m10, 23, bits, NULL, 0), \ + IIO_CHAN_SOFT_TIMESTAMP(24) \ + } +static const struct iio_chan_spec max1038_channels[] = MAX1363_12X_CHANS(8); +static const struct iio_chan_spec max1138_channels[] = MAX1363_12X_CHANS(10); +static const struct iio_chan_spec max1238_channels[] = MAX1363_12X_CHANS(12); + +static const enum max1363_modes max11607_mode_list[] = { + _s0, _s1, _s2, _s3, + s0to1, s0to2, s2to3, + s0to3, + d0m1, d2m3, d1m0, d3m2, + d0m1to2m3, d1m0to3m2, +}; + +static const enum max1363_modes max11608_mode_list[] = { + _s0, _s1, _s2, _s3, _s4, _s5, _s6, _s7, + s0to1, s0to2, s0to3, s0to4, s0to5, s0to6, s6to7, s0to7, + d0m1, d2m3, d4m5, d6m7, + d1m0, d3m2, d5m4, d7m6, + d0m1to2m3, d0m1to4m5, d0m1to6m7, + d1m0to3m2, d1m0to5m4, d1m0to7m6, +}; + +#define MAX1363_8X_CHANS(bits) { \ + MAX1363_CHAN_U(0, _s0, 0, bits, NULL, 0), \ + MAX1363_CHAN_U(1, _s1, 1, bits, NULL, 0), \ + MAX1363_CHAN_U(2, _s2, 2, bits, NULL, 0), \ + MAX1363_CHAN_U(3, _s3, 3, bits, NULL, 0), \ + MAX1363_CHAN_U(4, _s4, 4, bits, NULL, 0), \ + MAX1363_CHAN_U(5, _s5, 5, bits, NULL, 0), \ + MAX1363_CHAN_U(6, _s6, 6, bits, NULL, 0), \ + MAX1363_CHAN_U(7, _s7, 7, bits, NULL, 0), \ + MAX1363_CHAN_B(0, 1, d0m1, 12, bits, NULL, 0), \ + MAX1363_CHAN_B(2, 3, d2m3, 13, bits, NULL, 0), \ + MAX1363_CHAN_B(4, 5, d4m5, 14, bits, NULL, 0), \ + MAX1363_CHAN_B(6, 7, d6m7, 15, bits, NULL, 0), \ + MAX1363_CHAN_B(1, 0, d1m0, 18, bits, NULL, 0), \ + MAX1363_CHAN_B(3, 2, d3m2, 19, bits, NULL, 0), \ + MAX1363_CHAN_B(5, 4, d5m4, 20, bits, NULL, 0), \ + MAX1363_CHAN_B(7, 6, d7m6, 21, bits, NULL, 0), \ + IIO_CHAN_SOFT_TIMESTAMP(16) \ +} +static const struct iio_chan_spec max11602_channels[] = MAX1363_8X_CHANS(8); +static const struct iio_chan_spec max11608_channels[] = MAX1363_8X_CHANS(10); +static const struct iio_chan_spec max11614_channels[] = MAX1363_8X_CHANS(12); + +static const enum max1363_modes max11644_mode_list[] = { + _s0, _s1, s0to1, d0m1, d1m0, +}; + +#define MAX1363_2X_CHANS(bits) { \ + MAX1363_CHAN_U(0, _s0, 0, bits, NULL, 0), \ + MAX1363_CHAN_U(1, _s1, 1, bits, NULL, 0), \ + MAX1363_CHAN_B(0, 1, d0m1, 2, bits, NULL, 0), \ + MAX1363_CHAN_B(1, 0, d1m0, 3, bits, NULL, 0), \ + IIO_CHAN_SOFT_TIMESTAMP(4) \ + } + +static const struct iio_chan_spec max11646_channels[] = MAX1363_2X_CHANS(10); +static const struct iio_chan_spec max11644_channels[] = MAX1363_2X_CHANS(12); + +enum { + max1361, + max1362, + max1363, + max1364, + max1036, + max1037, + max1038, + max1039, + max1136, + max1137, + max1138, + max1139, + max1236, + max1237, + max1238, + max1239, + max11600, + max11601, + max11602, + max11603, + max11604, + max11605, + max11606, + max11607, + max11608, + max11609, + max11610, + max11611, + max11612, + max11613, + max11614, + max11615, + max11616, + max11617, + max11644, + max11645, + max11646, + max11647, +}; + +static const int max1363_monitor_speeds[] = { + 133000, 665000, 33300, 16600, + 8300, 4200, 2000, 1000, +}; + +static ssize_t max1363_monitor_show_freq(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct max1363_state *st = iio_priv(dev_to_iio_dev(dev)); + return sprintf(buf, "%d\n", max1363_monitor_speeds[st->monitor_speed]); +} + +static ssize_t max1363_monitor_store_freq(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t len) +{ + struct iio_dev *indio_dev = dev_to_iio_dev(dev); + struct max1363_state *st = iio_priv(indio_dev); + int i, ret; + unsigned long val; + bool found = false; + + ret = kstrtoul(buf, 10, &val); + if (ret) + return -EINVAL; + for (i = 0; i < ARRAY_SIZE(max1363_monitor_speeds); i++) + if (val == max1363_monitor_speeds[i]) { + found = true; + break; + } + if (!found) + return -EINVAL; + + scoped_guard(mutex, &st->lock) + st->monitor_speed = i; + + return 0; +} + +static IIO_DEV_ATTR_SAMP_FREQ(S_IRUGO | S_IWUSR, + max1363_monitor_show_freq, + max1363_monitor_store_freq); + +static IIO_CONST_ATTR(sampling_frequency_available, + "133000 665000 33300 16600 8300 4200 2000 1000"); + +static int max1363_read_thresh(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, enum iio_event_type type, + enum iio_event_direction dir, enum iio_event_info info, int *val, + int *val2) +{ + struct max1363_state *st = iio_priv(indio_dev); + if (dir == IIO_EV_DIR_FALLING) + *val = st->thresh_low[chan->channel]; + else + *val = st->thresh_high[chan->channel]; + return IIO_VAL_INT; +} + +static int max1363_write_thresh(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, enum iio_event_type type, + enum iio_event_direction dir, enum iio_event_info info, int val, + int val2) +{ + struct max1363_state *st = iio_priv(indio_dev); + /* make it handle signed correctly as well */ + switch (st->chip_info->bits) { + case 10: + if (val > 0x3FF) + return -EINVAL; + break; + case 12: + if (val > 0xFFF) + return -EINVAL; + break; + } + + switch (dir) { + case IIO_EV_DIR_FALLING: + st->thresh_low[chan->channel] = val; + break; + case IIO_EV_DIR_RISING: + st->thresh_high[chan->channel] = val; + break; + default: + return -EINVAL; + } + + return 0; +} + +static const u64 max1363_event_codes[] = { + IIO_UNMOD_EVENT_CODE(IIO_VOLTAGE, 0, + IIO_EV_TYPE_THRESH, IIO_EV_DIR_FALLING), + IIO_UNMOD_EVENT_CODE(IIO_VOLTAGE, 1, + IIO_EV_TYPE_THRESH, IIO_EV_DIR_FALLING), + IIO_UNMOD_EVENT_CODE(IIO_VOLTAGE, 2, + IIO_EV_TYPE_THRESH, IIO_EV_DIR_FALLING), + IIO_UNMOD_EVENT_CODE(IIO_VOLTAGE, 3, + IIO_EV_TYPE_THRESH, IIO_EV_DIR_FALLING), + IIO_UNMOD_EVENT_CODE(IIO_VOLTAGE, 0, + IIO_EV_TYPE_THRESH, IIO_EV_DIR_RISING), + IIO_UNMOD_EVENT_CODE(IIO_VOLTAGE, 1, + IIO_EV_TYPE_THRESH, IIO_EV_DIR_RISING), + IIO_UNMOD_EVENT_CODE(IIO_VOLTAGE, 2, + IIO_EV_TYPE_THRESH, IIO_EV_DIR_RISING), + IIO_UNMOD_EVENT_CODE(IIO_VOLTAGE, 3, + IIO_EV_TYPE_THRESH, IIO_EV_DIR_RISING), +}; + +static irqreturn_t max1363_event_handler(int irq, void *private) +{ + struct iio_dev *indio_dev = private; + struct max1363_state *st = iio_priv(indio_dev); + s64 timestamp = iio_get_time_ns(indio_dev); + unsigned long mask, loc; + u8 rx; + u8 tx[2] = { st->setupbyte, + MAX1363_MON_INT_ENABLE | (st->monitor_speed << 1) | 0xF0 }; + + st->recv(st->client, &rx, 1); + mask = rx; + for_each_set_bit(loc, &mask, 8) + iio_push_event(indio_dev, max1363_event_codes[loc], timestamp); + st->send(st->client, tx, 2); + + return IRQ_HANDLED; +} + +static int max1363_read_event_config(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, enum iio_event_type type, + enum iio_event_direction dir) +{ + struct max1363_state *st = iio_priv(indio_dev); + int val; + int number = chan->channel; + + guard(mutex)(&st->lock); + if (dir == IIO_EV_DIR_FALLING) + val = (1 << number) & st->mask_low; + else + val = (1 << number) & st->mask_high; + + return val; +} + +static int max1363_monitor_mode_update(struct max1363_state *st, int enabled) +{ + int ret, i = 3, j; + unsigned long numelements; + int len; + const long *modemask; + + if (!enabled) { + /* transition to buffered capture is not currently supported */ + st->setupbyte &= ~MAX1363_SETUP_MONITOR_SETUP; + st->configbyte &= ~MAX1363_SCAN_MASK; + st->monitor_on = false; + return max1363_write_basic_config(st); + } + + /* Ensure we are in the relevant mode */ + st->setupbyte |= MAX1363_SETUP_MONITOR_SETUP; + st->configbyte &= ~(MAX1363_CHANNEL_SEL_MASK + | MAX1363_SCAN_MASK + | MAX1363_SE_DE_MASK); + st->configbyte |= MAX1363_CONFIG_SCAN_MONITOR_MODE; + if ((st->mask_low | st->mask_high) & 0x0F) { + st->configbyte |= max1363_mode_table[s0to3].conf; + modemask = max1363_mode_table[s0to3].modemask; + } else if ((st->mask_low | st->mask_high) & 0x30) { + st->configbyte |= max1363_mode_table[d0m1to2m3].conf; + modemask = max1363_mode_table[d0m1to2m3].modemask; + } else { + st->configbyte |= max1363_mode_table[d1m0to3m2].conf; + modemask = max1363_mode_table[d1m0to3m2].modemask; + } + numelements = bitmap_weight(modemask, MAX1363_MAX_CHANNELS); + len = 3 * numelements + 3; + u8 *tx_buf __free(kfree) = kmalloc(len, GFP_KERNEL); + if (!tx_buf) + return -ENOMEM; + + tx_buf[0] = st->configbyte; + tx_buf[1] = st->setupbyte; + tx_buf[2] = (st->monitor_speed << 1); + + /* + * So we need to do yet another bit of nefarious scan mode + * setup to match what we need. + */ + for (j = 0; j < 8; j++) + if (test_bit(j, modemask)) { + /* Establish the mode is in the scan */ + if (st->mask_low & (1 << j)) { + tx_buf[i] = (st->thresh_low[j] >> 4) & 0xFF; + tx_buf[i + 1] = (st->thresh_low[j] << 4) & 0xF0; + } else if (j < 4) { + tx_buf[i] = 0; + tx_buf[i + 1] = 0; + } else { + tx_buf[i] = 0x80; + tx_buf[i + 1] = 0; + } + if (st->mask_high & (1 << j)) { + tx_buf[i + 1] |= + (st->thresh_high[j] >> 8) & 0x0F; + tx_buf[i + 2] = st->thresh_high[j] & 0xFF; + } else if (j < 4) { + tx_buf[i + 1] |= 0x0F; + tx_buf[i + 2] = 0xFF; + } else { + tx_buf[i + 1] |= 0x07; + tx_buf[i + 2] = 0xFF; + } + i += 3; + } + + + ret = st->send(st->client, tx_buf, len); + if (ret < 0) + return ret; + if (ret != len) + return -EIO; + + /* + * Now that we hopefully have sensible thresholds in place it is + * time to turn the interrupts on. + * It is unclear from the data sheet if this should be necessary + * (i.e. whether monitor mode setup is atomic) but it appears to + * be in practice. + */ + tx_buf[0] = st->setupbyte; + tx_buf[1] = MAX1363_MON_INT_ENABLE | (st->monitor_speed << 1) | 0xF0; + ret = st->send(st->client, tx_buf, 2); + if (ret < 0) + return ret; + if (ret != 2) + return -EIO; + + st->monitor_on = true; + + return 0; +} + +/* + * To keep this manageable we always use one of 3 scan modes. + * Scan 0...3, 0-1,2-3 and 1-0,3-2 + */ + +static inline int __max1363_check_event_mask(int thismask, int checkmask) +{ + int ret = 0; + /* Is it unipolar */ + if (thismask < 4) { + if (checkmask & ~0x0F) { + ret = -EBUSY; + goto error_ret; + } + } else if (thismask < 6) { + if (checkmask & ~0x30) { + ret = -EBUSY; + goto error_ret; + } + } else if (checkmask & ~0xC0) + ret = -EBUSY; +error_ret: + return ret; +} + +static int __max1363_write_event_config(struct max1363_state *st, + const struct iio_chan_spec *chan, + enum iio_event_direction dir, bool state) +{ + int number = chan->channel; + u16 unifiedmask; + int ret; + + guard(mutex)(&st->lock); + + unifiedmask = st->mask_low | st->mask_high; + if (dir == IIO_EV_DIR_FALLING) { + + if (state == 0) + st->mask_low &= ~(1 << number); + else { + ret = __max1363_check_event_mask((1 << number), + unifiedmask); + if (ret) + return ret; + st->mask_low |= (1 << number); + } + } else { + if (state == 0) + st->mask_high &= ~(1 << number); + else { + ret = __max1363_check_event_mask((1 << number), + unifiedmask); + if (ret) + return ret; + st->mask_high |= (1 << number); + } + } + + return 0; + +} +static int max1363_write_event_config(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, enum iio_event_type type, + enum iio_event_direction dir, bool state) +{ + struct max1363_state *st = iio_priv(indio_dev); + int ret; + + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; + + ret = __max1363_write_event_config(st, chan, dir, state); + iio_device_release_direct(indio_dev); + max1363_monitor_mode_update(st, !!(st->mask_high | st->mask_low)); + + return ret; +} + +/* + * As with scan_elements, only certain sets of these can + * be combined. + */ +static struct attribute *max1363_event_attributes[] = { + &iio_dev_attr_sampling_frequency.dev_attr.attr, + &iio_const_attr_sampling_frequency_available.dev_attr.attr, + NULL, +}; + +static const struct attribute_group max1363_event_attribute_group = { + .attrs = max1363_event_attributes, +}; + +static int max1363_update_scan_mode(struct iio_dev *indio_dev, + const unsigned long *scan_mask) +{ + struct max1363_state *st = iio_priv(indio_dev); + + /* + * Need to figure out the current mode based upon the requested + * scan mask in iio_dev + */ + st->current_mode = max1363_match_mode(scan_mask, st->chip_info); + if (!st->current_mode) + return -EINVAL; + max1363_set_scan_mode(st); + return 0; +} + +static const struct iio_info max1238_info = { + .read_raw = &max1363_read_raw, + .update_scan_mode = &max1363_update_scan_mode, +}; + +static const struct iio_info max1363_info = { + .read_event_value = &max1363_read_thresh, + .write_event_value = &max1363_write_thresh, + .read_event_config = &max1363_read_event_config, + .write_event_config = &max1363_write_event_config, + .read_raw = &max1363_read_raw, + .update_scan_mode = &max1363_update_scan_mode, + .event_attrs = &max1363_event_attribute_group, +}; + +/* max1363 and max1368 tested - rest from data sheet */ +static const struct max1363_chip_info max1363_chip_info_tbl[] = { + [max1361] = { + .bits = 10, + .int_vref_mv = 2048, + .mode_list = max1363_mode_list, + .num_modes = ARRAY_SIZE(max1363_mode_list), + .default_mode = s0to3, + .channels = max1361_channels, + .num_channels = ARRAY_SIZE(max1361_channels), + .info = &max1363_info, + }, + [max1362] = { + .bits = 10, + .int_vref_mv = 4096, + .mode_list = max1363_mode_list, + .num_modes = ARRAY_SIZE(max1363_mode_list), + .default_mode = s0to3, + .channels = max1361_channels, + .num_channels = ARRAY_SIZE(max1361_channels), + .info = &max1363_info, + }, + [max1363] = { + .bits = 12, + .int_vref_mv = 2048, + .mode_list = max1363_mode_list, + .num_modes = ARRAY_SIZE(max1363_mode_list), + .default_mode = s0to3, + .channels = max1363_channels, + .num_channels = ARRAY_SIZE(max1363_channels), + .info = &max1363_info, + }, + [max1364] = { + .bits = 12, + .int_vref_mv = 4096, + .mode_list = max1363_mode_list, + .num_modes = ARRAY_SIZE(max1363_mode_list), + .default_mode = s0to3, + .channels = max1363_channels, + .num_channels = ARRAY_SIZE(max1363_channels), + .info = &max1363_info, + }, + [max1036] = { + .bits = 8, + .int_vref_mv = 4096, + .mode_list = max1236_mode_list, + .num_modes = ARRAY_SIZE(max1236_mode_list), + .default_mode = s0to3, + .info = &max1238_info, + .channels = max1036_channels, + .num_channels = ARRAY_SIZE(max1036_channels), + }, + [max1037] = { + .bits = 8, + .int_vref_mv = 2048, + .mode_list = max1236_mode_list, + .num_modes = ARRAY_SIZE(max1236_mode_list), + .default_mode = s0to3, + .info = &max1238_info, + .channels = max1036_channels, + .num_channels = ARRAY_SIZE(max1036_channels), + }, + [max1038] = { + .bits = 8, + .int_vref_mv = 4096, + .mode_list = max1238_mode_list, + .num_modes = ARRAY_SIZE(max1238_mode_list), + .default_mode = s0to11, + .info = &max1238_info, + .channels = max1038_channels, + .num_channels = ARRAY_SIZE(max1038_channels), + }, + [max1039] = { + .bits = 8, + .int_vref_mv = 2048, + .mode_list = max1238_mode_list, + .num_modes = ARRAY_SIZE(max1238_mode_list), + .default_mode = s0to11, + .info = &max1238_info, + .channels = max1038_channels, + .num_channels = ARRAY_SIZE(max1038_channels), + }, + [max1136] = { + .bits = 10, + .int_vref_mv = 4096, + .mode_list = max1236_mode_list, + .num_modes = ARRAY_SIZE(max1236_mode_list), + .default_mode = s0to3, + .info = &max1238_info, + .channels = max1136_channels, + .num_channels = ARRAY_SIZE(max1136_channels), + }, + [max1137] = { + .bits = 10, + .int_vref_mv = 2048, + .mode_list = max1236_mode_list, + .num_modes = ARRAY_SIZE(max1236_mode_list), + .default_mode = s0to3, + .info = &max1238_info, + .channels = max1136_channels, + .num_channels = ARRAY_SIZE(max1136_channels), + }, + [max1138] = { + .bits = 10, + .int_vref_mv = 4096, + .mode_list = max1238_mode_list, + .num_modes = ARRAY_SIZE(max1238_mode_list), + .default_mode = s0to11, + .info = &max1238_info, + .channels = max1138_channels, + .num_channels = ARRAY_SIZE(max1138_channels), + }, + [max1139] = { + .bits = 10, + .int_vref_mv = 2048, + .mode_list = max1238_mode_list, + .num_modes = ARRAY_SIZE(max1238_mode_list), + .default_mode = s0to11, + .info = &max1238_info, + .channels = max1138_channels, + .num_channels = ARRAY_SIZE(max1138_channels), + }, + [max1236] = { + .bits = 12, + .int_vref_mv = 4096, + .mode_list = max1236_mode_list, + .num_modes = ARRAY_SIZE(max1236_mode_list), + .default_mode = s0to3, + .info = &max1238_info, + .channels = max1236_channels, + .num_channels = ARRAY_SIZE(max1236_channels), + }, + [max1237] = { + .bits = 12, + .int_vref_mv = 2048, + .mode_list = max1236_mode_list, + .num_modes = ARRAY_SIZE(max1236_mode_list), + .default_mode = s0to3, + .info = &max1238_info, + .channels = max1236_channels, + .num_channels = ARRAY_SIZE(max1236_channels), + }, + [max1238] = { + .bits = 12, + .int_vref_mv = 4096, + .mode_list = max1238_mode_list, + .num_modes = ARRAY_SIZE(max1238_mode_list), + .default_mode = s0to11, + .info = &max1238_info, + .channels = max1238_channels, + .num_channels = ARRAY_SIZE(max1238_channels), + }, + [max1239] = { + .bits = 12, + .int_vref_mv = 2048, + .mode_list = max1238_mode_list, + .num_modes = ARRAY_SIZE(max1238_mode_list), + .default_mode = s0to11, + .info = &max1238_info, + .channels = max1238_channels, + .num_channels = ARRAY_SIZE(max1238_channels), + }, + [max11600] = { + .bits = 8, + .int_vref_mv = 4096, + .mode_list = max11607_mode_list, + .num_modes = ARRAY_SIZE(max11607_mode_list), + .default_mode = s0to3, + .info = &max1238_info, + .channels = max1036_channels, + .num_channels = ARRAY_SIZE(max1036_channels), + }, + [max11601] = { + .bits = 8, + .int_vref_mv = 2048, + .mode_list = max11607_mode_list, + .num_modes = ARRAY_SIZE(max11607_mode_list), + .default_mode = s0to3, + .info = &max1238_info, + .channels = max1036_channels, + .num_channels = ARRAY_SIZE(max1036_channels), + }, + [max11602] = { + .bits = 8, + .int_vref_mv = 4096, + .mode_list = max11608_mode_list, + .num_modes = ARRAY_SIZE(max11608_mode_list), + .default_mode = s0to7, + .info = &max1238_info, + .channels = max11602_channels, + .num_channels = ARRAY_SIZE(max11602_channels), + }, + [max11603] = { + .bits = 8, + .int_vref_mv = 2048, + .mode_list = max11608_mode_list, + .num_modes = ARRAY_SIZE(max11608_mode_list), + .default_mode = s0to7, + .info = &max1238_info, + .channels = max11602_channels, + .num_channels = ARRAY_SIZE(max11602_channels), + }, + [max11604] = { + .bits = 8, + .int_vref_mv = 4096, + .mode_list = max1238_mode_list, + .num_modes = ARRAY_SIZE(max1238_mode_list), + .default_mode = s0to11, + .info = &max1238_info, + .channels = max1038_channels, + .num_channels = ARRAY_SIZE(max1038_channels), + }, + [max11605] = { + .bits = 8, + .int_vref_mv = 2048, + .mode_list = max1238_mode_list, + .num_modes = ARRAY_SIZE(max1238_mode_list), + .default_mode = s0to11, + .info = &max1238_info, + .channels = max1038_channels, + .num_channels = ARRAY_SIZE(max1038_channels), + }, + [max11606] = { + .bits = 10, + .int_vref_mv = 4096, + .mode_list = max11607_mode_list, + .num_modes = ARRAY_SIZE(max11607_mode_list), + .default_mode = s0to3, + .info = &max1238_info, + .channels = max1136_channels, + .num_channels = ARRAY_SIZE(max1136_channels), + }, + [max11607] = { + .bits = 10, + .int_vref_mv = 2048, + .mode_list = max11607_mode_list, + .num_modes = ARRAY_SIZE(max11607_mode_list), + .default_mode = s0to3, + .info = &max1238_info, + .channels = max1136_channels, + .num_channels = ARRAY_SIZE(max1136_channels), + }, + [max11608] = { + .bits = 10, + .int_vref_mv = 4096, + .mode_list = max11608_mode_list, + .num_modes = ARRAY_SIZE(max11608_mode_list), + .default_mode = s0to7, + .info = &max1238_info, + .channels = max11608_channels, + .num_channels = ARRAY_SIZE(max11608_channels), + }, + [max11609] = { + .bits = 10, + .int_vref_mv = 2048, + .mode_list = max11608_mode_list, + .num_modes = ARRAY_SIZE(max11608_mode_list), + .default_mode = s0to7, + .info = &max1238_info, + .channels = max11608_channels, + .num_channels = ARRAY_SIZE(max11608_channels), + }, + [max11610] = { + .bits = 10, + .int_vref_mv = 4096, + .mode_list = max1238_mode_list, + .num_modes = ARRAY_SIZE(max1238_mode_list), + .default_mode = s0to11, + .info = &max1238_info, + .channels = max1138_channels, + .num_channels = ARRAY_SIZE(max1138_channels), + }, + [max11611] = { + .bits = 10, + .int_vref_mv = 2048, + .mode_list = max1238_mode_list, + .num_modes = ARRAY_SIZE(max1238_mode_list), + .default_mode = s0to11, + .info = &max1238_info, + .channels = max1138_channels, + .num_channels = ARRAY_SIZE(max1138_channels), + }, + [max11612] = { + .bits = 12, + .int_vref_mv = 4096, + .mode_list = max11607_mode_list, + .num_modes = ARRAY_SIZE(max11607_mode_list), + .default_mode = s0to3, + .info = &max1238_info, + .channels = max1363_channels, + .num_channels = ARRAY_SIZE(max1363_channels), + }, + [max11613] = { + .bits = 12, + .int_vref_mv = 2048, + .mode_list = max11607_mode_list, + .num_modes = ARRAY_SIZE(max11607_mode_list), + .default_mode = s0to3, + .info = &max1238_info, + .channels = max1363_channels, + .num_channels = ARRAY_SIZE(max1363_channels), + }, + [max11614] = { + .bits = 12, + .int_vref_mv = 4096, + .mode_list = max11608_mode_list, + .num_modes = ARRAY_SIZE(max11608_mode_list), + .default_mode = s0to7, + .info = &max1238_info, + .channels = max11614_channels, + .num_channels = ARRAY_SIZE(max11614_channels), + }, + [max11615] = { + .bits = 12, + .int_vref_mv = 2048, + .mode_list = max11608_mode_list, + .num_modes = ARRAY_SIZE(max11608_mode_list), + .default_mode = s0to7, + .info = &max1238_info, + .channels = max11614_channels, + .num_channels = ARRAY_SIZE(max11614_channels), + }, + [max11616] = { + .bits = 12, + .int_vref_mv = 4096, + .mode_list = max1238_mode_list, + .num_modes = ARRAY_SIZE(max1238_mode_list), + .default_mode = s0to11, + .info = &max1238_info, + .channels = max1238_channels, + .num_channels = ARRAY_SIZE(max1238_channels), + }, + [max11617] = { + .bits = 12, + .int_vref_mv = 2048, + .mode_list = max1238_mode_list, + .num_modes = ARRAY_SIZE(max1238_mode_list), + .default_mode = s0to11, + .info = &max1238_info, + .channels = max1238_channels, + .num_channels = ARRAY_SIZE(max1238_channels), + }, + [max11644] = { + .bits = 12, + .int_vref_mv = 4096, + .mode_list = max11644_mode_list, + .num_modes = ARRAY_SIZE(max11644_mode_list), + .default_mode = s0to1, + .info = &max1238_info, + .channels = max11644_channels, + .num_channels = ARRAY_SIZE(max11644_channels), + }, + [max11645] = { + .bits = 12, + .int_vref_mv = 2048, + .mode_list = max11644_mode_list, + .num_modes = ARRAY_SIZE(max11644_mode_list), + .default_mode = s0to1, + .info = &max1238_info, + .channels = max11644_channels, + .num_channels = ARRAY_SIZE(max11644_channels), + }, + [max11646] = { + .bits = 10, + .int_vref_mv = 4096, + .mode_list = max11644_mode_list, + .num_modes = ARRAY_SIZE(max11644_mode_list), + .default_mode = s0to1, + .info = &max1238_info, + .channels = max11646_channels, + .num_channels = ARRAY_SIZE(max11646_channels), + }, + [max11647] = { + .bits = 10, + .int_vref_mv = 2048, + .mode_list = max11644_mode_list, + .num_modes = ARRAY_SIZE(max11644_mode_list), + .default_mode = s0to1, + .info = &max1238_info, + .channels = max11646_channels, + .num_channels = ARRAY_SIZE(max11646_channels), + }, +}; + +static int max1363_initial_setup(struct max1363_state *st) +{ + st->setupbyte = MAX1363_SETUP_INT_CLOCK + | MAX1363_SETUP_UNIPOLAR + | MAX1363_SETUP_NORESET; + + if (st->vref) + st->setupbyte |= MAX1363_SETUP_AIN3_IS_REF_EXT_TO_REF; + else + st->setupbyte |= MAX1363_SETUP_POWER_UP_INT_REF + | MAX1363_SETUP_AIN3_IS_AIN3_REF_IS_INT; + + /* Set scan mode writes the config anyway so wait until then */ + st->setupbyte = MAX1363_SETUP_BYTE(st->setupbyte); + st->current_mode = &max1363_mode_table[st->chip_info->default_mode]; + st->configbyte = MAX1363_CONFIG_BYTE(st->configbyte); + + return max1363_set_scan_mode(st); +} + +static int max1363_alloc_scan_masks(struct iio_dev *indio_dev) +{ + struct max1363_state *st = iio_priv(indio_dev); + unsigned long *masks; + int i; + + masks = devm_kzalloc(&indio_dev->dev, + array3_size(BITS_TO_LONGS(MAX1363_MAX_CHANNELS), + sizeof(long), + st->chip_info->num_modes + 1), + GFP_KERNEL); + if (!masks) + return -ENOMEM; + + for (i = 0; i < st->chip_info->num_modes; i++) + bitmap_copy(masks + BITS_TO_LONGS(MAX1363_MAX_CHANNELS)*i, + max1363_mode_table[st->chip_info->mode_list[i]] + .modemask, MAX1363_MAX_CHANNELS); + + indio_dev->available_scan_masks = masks; + + return 0; +} + +static irqreturn_t max1363_trigger_handler(int irq, void *p) +{ + struct iio_poll_func *pf = p; + struct iio_dev *indio_dev = pf->indio_dev; + struct max1363_state *st = iio_priv(indio_dev); + int b_sent; + unsigned long numvals = bitmap_weight(st->current_mode->modemask, + MAX1363_MAX_CHANNELS); + + /* Monitor mode prevents reading. Whilst not currently implemented + * might as well have this test in here in the meantime as it does + * no harm. + */ + if (numvals == 0) + goto done; + + if (st->chip_info->bits != 8) + b_sent = st->recv(st->client, st->data.buf, numvals * 2); + else + b_sent = st->recv(st->client, st->data.buf, numvals); + if (b_sent < 0) + goto done; + + iio_push_to_buffers_with_ts(indio_dev, &st->data, sizeof(st->data), + iio_get_time_ns(indio_dev)); + +done: + iio_trigger_notify_done(indio_dev->trig); + + return IRQ_HANDLED; +} + +#define MAX1363_COMPATIBLE(of_compatible, cfg) { \ + .compatible = of_compatible, \ + .data = &max1363_chip_info_tbl[cfg], \ +} + +static const struct of_device_id max1363_of_match[] = { + MAX1363_COMPATIBLE("maxim,max1361", max1361), + MAX1363_COMPATIBLE("maxim,max1362", max1362), + MAX1363_COMPATIBLE("maxim,max1363", max1363), + MAX1363_COMPATIBLE("maxim,max1364", max1364), + MAX1363_COMPATIBLE("maxim,max1036", max1036), + MAX1363_COMPATIBLE("maxim,max1037", max1037), + MAX1363_COMPATIBLE("maxim,max1038", max1038), + MAX1363_COMPATIBLE("maxim,max1039", max1039), + MAX1363_COMPATIBLE("maxim,max1136", max1136), + MAX1363_COMPATIBLE("maxim,max1137", max1137), + MAX1363_COMPATIBLE("maxim,max1138", max1138), + MAX1363_COMPATIBLE("maxim,max1139", max1139), + MAX1363_COMPATIBLE("maxim,max1236", max1236), + MAX1363_COMPATIBLE("maxim,max1237", max1237), + MAX1363_COMPATIBLE("maxim,max1238", max1238), + MAX1363_COMPATIBLE("maxim,max1239", max1239), + MAX1363_COMPATIBLE("maxim,max11600", max11600), + MAX1363_COMPATIBLE("maxim,max11601", max11601), + MAX1363_COMPATIBLE("maxim,max11602", max11602), + MAX1363_COMPATIBLE("maxim,max11603", max11603), + MAX1363_COMPATIBLE("maxim,max11604", max11604), + MAX1363_COMPATIBLE("maxim,max11605", max11605), + MAX1363_COMPATIBLE("maxim,max11606", max11606), + MAX1363_COMPATIBLE("maxim,max11607", max11607), + MAX1363_COMPATIBLE("maxim,max11608", max11608), + MAX1363_COMPATIBLE("maxim,max11609", max11609), + MAX1363_COMPATIBLE("maxim,max11610", max11610), + MAX1363_COMPATIBLE("maxim,max11611", max11611), + MAX1363_COMPATIBLE("maxim,max11612", max11612), + MAX1363_COMPATIBLE("maxim,max11613", max11613), + MAX1363_COMPATIBLE("maxim,max11614", max11614), + MAX1363_COMPATIBLE("maxim,max11615", max11615), + MAX1363_COMPATIBLE("maxim,max11616", max11616), + MAX1363_COMPATIBLE("maxim,max11617", max11617), + MAX1363_COMPATIBLE("maxim,max11644", max11644), + MAX1363_COMPATIBLE("maxim,max11645", max11645), + MAX1363_COMPATIBLE("maxim,max11646", max11646), + MAX1363_COMPATIBLE("maxim,max11647", max11647), + { } +}; +MODULE_DEVICE_TABLE(of, max1363_of_match); + +static int max1363_probe(struct i2c_client *client) +{ + const struct i2c_device_id *id = i2c_client_get_device_id(client); + int ret; + struct max1363_state *st; + struct iio_dev *indio_dev; + + indio_dev = devm_iio_device_alloc(&client->dev, + sizeof(struct max1363_state)); + if (!indio_dev) + return -ENOMEM; + + st = iio_priv(indio_dev); + + mutex_init(&st->lock); + ret = devm_regulator_get_enable(&client->dev, "vcc"); + if (ret) + return ret; + + st->chip_info = i2c_get_match_data(client); + st->client = client; + + ret = devm_regulator_get_enable_read_voltage(&client->dev, "vref"); + if (ret < 0 && ret != -ENODEV) + return ret; + + + st->vref_uv = ret == -ENODEV ? st->chip_info->int_vref_mv * 1000 : ret; + + if (i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { + st->send = i2c_master_send; + st->recv = i2c_master_recv; + } else if (i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE) + && st->chip_info->bits == 8) { + st->send = max1363_smbus_send; + st->recv = max1363_smbus_recv; + } else { + return -EOPNOTSUPP; + } + + ret = max1363_alloc_scan_masks(indio_dev); + if (ret) + return ret; + + indio_dev->name = id->name; + indio_dev->channels = st->chip_info->channels; + indio_dev->num_channels = st->chip_info->num_channels; + indio_dev->info = st->chip_info->info; + indio_dev->modes = INDIO_DIRECT_MODE; + ret = max1363_initial_setup(st); + if (ret < 0) + return ret; + + ret = devm_iio_triggered_buffer_setup(&client->dev, indio_dev, NULL, + &max1363_trigger_handler, NULL); + if (ret) + return ret; + + if (client->irq) { + ret = devm_request_threaded_irq(&client->dev, st->client->irq, + NULL, + &max1363_event_handler, + IRQF_TRIGGER_RISING | IRQF_ONESHOT, + "max1363_event", + indio_dev); + + if (ret) + return ret; + } + + return devm_iio_device_register(&client->dev, indio_dev); +} + +#define MAX1363_ID_TABLE(_name, cfg) { \ + .name = _name, \ + .driver_data = (kernel_ulong_t)&max1363_chip_info_tbl[cfg], \ +} + +static const struct i2c_device_id max1363_id[] = { + MAX1363_ID_TABLE("max1361", max1361), + MAX1363_ID_TABLE("max1362", max1362), + MAX1363_ID_TABLE("max1363", max1363), + MAX1363_ID_TABLE("max1364", max1364), + MAX1363_ID_TABLE("max1036", max1036), + MAX1363_ID_TABLE("max1037", max1037), + MAX1363_ID_TABLE("max1038", max1038), + MAX1363_ID_TABLE("max1039", max1039), + MAX1363_ID_TABLE("max1136", max1136), + MAX1363_ID_TABLE("max1137", max1137), + MAX1363_ID_TABLE("max1138", max1138), + MAX1363_ID_TABLE("max1139", max1139), + MAX1363_ID_TABLE("max1236", max1236), + MAX1363_ID_TABLE("max1237", max1237), + MAX1363_ID_TABLE("max1238", max1238), + MAX1363_ID_TABLE("max1239", max1239), + MAX1363_ID_TABLE("max11600", max11600), + MAX1363_ID_TABLE("max11601", max11601), + MAX1363_ID_TABLE("max11602", max11602), + MAX1363_ID_TABLE("max11603", max11603), + MAX1363_ID_TABLE("max11604", max11604), + MAX1363_ID_TABLE("max11605", max11605), + MAX1363_ID_TABLE("max11606", max11606), + MAX1363_ID_TABLE("max11607", max11607), + MAX1363_ID_TABLE("max11608", max11608), + MAX1363_ID_TABLE("max11609", max11609), + MAX1363_ID_TABLE("max11610", max11610), + MAX1363_ID_TABLE("max11611", max11611), + MAX1363_ID_TABLE("max11612", max11612), + MAX1363_ID_TABLE("max11613", max11613), + MAX1363_ID_TABLE("max11614", max11614), + MAX1363_ID_TABLE("max11615", max11615), + MAX1363_ID_TABLE("max11616", max11616), + MAX1363_ID_TABLE("max11617", max11617), + MAX1363_ID_TABLE("max11644", max11644), + MAX1363_ID_TABLE("max11645", max11645), + MAX1363_ID_TABLE("max11646", max11646), + MAX1363_ID_TABLE("max11647", max11647), + { } +}; + +MODULE_DEVICE_TABLE(i2c, max1363_id); + +static struct i2c_driver max1363_driver = { + .driver = { + .name = "max1363", + .of_match_table = max1363_of_match, + }, + .probe = max1363_probe, + .id_table = max1363_id, +}; +module_i2c_driver(max1363_driver); + +MODULE_AUTHOR("Jonathan Cameron "); +MODULE_DESCRIPTION("Maxim 1363 ADC"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/adc/max14001.c b/drivers/iio/adc/max14001.c new file mode 100644 index 000000000..58adeac62 --- /dev/null +++ b/drivers/iio/adc/max14001.c @@ -0,0 +1,390 @@ +// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) +/* + * Analog Devices MAX14001/MAX14002 ADC driver + * + * Copyright (C) 2023-2025 Analog Devices Inc. + * Copyright (C) 2023 Kim Seer Paller + * Copyright (c) 2025 Marilene Andrade Garcia + * + * Datasheet: https://www.analog.com/media/en/technical-documentation/data-sheets/MAX14001-MAX14002.pdf + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +/* MAX14001 Registers Address */ +#define MAX14001_REG_ADC 0x00 +#define MAX14001_REG_FADC 0x01 +#define MAX14001_REG_FLAGS 0x02 +#define MAX14001_REG_FLTEN 0x03 +#define MAX14001_REG_THL 0x04 +#define MAX14001_REG_THU 0x05 +#define MAX14001_REG_INRR 0x06 +#define MAX14001_REG_INRT 0x07 +#define MAX14001_REG_INRP 0x08 +#define MAX14001_REG_CFG 0x09 +#define MAX14001_REG_ENBL 0x0A +#define MAX14001_REG_ACT 0x0B +#define MAX14001_REG_WEN 0x0C + +#define MAX14001_REG_VERIFICATION(x) ((x) + 0x10) + +#define MAX14001_REG_CFG_BIT_EXRF BIT(5) + +#define MAX14001_REG_WEN_VALUE_WRITE 0x294 + +#define MAX14001_MASK_ADDR GENMASK(15, 11) +#define MAX14001_MASK_WR BIT(10) +#define MAX14001_MASK_DATA GENMASK(9, 0) + +struct max14001_state { + const struct max14001_chip_info *chip_info; + struct spi_device *spi; + struct regmap *regmap; + int vref_mV; + bool spi_hw_has_lsb_first; + + /* + * The following buffers will be bit-reversed during device + * communication, because the device transmits and receives data + * LSB-first. + * DMA (thus cache coherency maintenance) requires the transfer + * buffers to live in their own cache lines. + */ + union { + __be16 be; + __le16 le; + } spi_tx_buffer __aligned(IIO_DMA_MINALIGN); + + union { + __be16 be; + __le16 le; + } spi_rx_buffer; +}; + +struct max14001_chip_info { + const char *name; +}; + +static int max14001_read(void *context, unsigned int reg, unsigned int *val) +{ + struct max14001_state *st = context; + struct spi_transfer xfers[] = { + { + .tx_buf = &st->spi_tx_buffer, + .len = sizeof(st->spi_tx_buffer), + .cs_change = 1, + }, { + .rx_buf = &st->spi_rx_buffer, + .len = sizeof(st->spi_rx_buffer), + }, + }; + int ret; + unsigned int addr, data; + + /* + * Prepare SPI transmit buffer 16 bit-value and reverse bit order + * to align with the LSB-first input on SDI port in order to meet + * the device communication requirements. If the controller supports + * SPI_LSB_FIRST, this step will be handled by the SPI controller. + */ + addr = FIELD_PREP(MAX14001_MASK_ADDR, reg); + + if (st->spi_hw_has_lsb_first) + st->spi_tx_buffer.le = cpu_to_le16(addr); + else + st->spi_tx_buffer.be = cpu_to_be16(bitrev16(addr)); + + ret = spi_sync_transfer(st->spi, xfers, ARRAY_SIZE(xfers)); + if (ret) + return ret; + + /* + * Convert received 16-bit value to cpu-endian format and reverse + * bit order. If the controller supports SPI_LSB_FIRST, this step + * will be handled by the SPI controller. + */ + if (st->spi_hw_has_lsb_first) + data = le16_to_cpu(st->spi_rx_buffer.le); + else + data = bitrev16(be16_to_cpu(st->spi_rx_buffer.be)); + + *val = FIELD_GET(MAX14001_MASK_DATA, data); + + return 0; +} + +static int max14001_write(struct max14001_state *st, unsigned int reg, unsigned int val) +{ + unsigned int addr; + + /* + * Prepare SPI transmit buffer 16 bit-value and reverse bit order + * to align with the LSB-first input on SDI port in order to meet + * the device communication requirements. If the controller supports + * SPI_LSB_FIRST, this step will be handled by the SPI controller. + */ + addr = FIELD_PREP(MAX14001_MASK_ADDR, reg) | + FIELD_PREP(MAX14001_MASK_WR, 1) | + FIELD_PREP(MAX14001_MASK_DATA, val); + + if (st->spi_hw_has_lsb_first) + st->spi_tx_buffer.le = cpu_to_le16(addr); + else + st->spi_tx_buffer.be = cpu_to_be16(bitrev16(addr)); + + return spi_write(st->spi, &st->spi_tx_buffer, sizeof(st->spi_tx_buffer)); +} + +static int max14001_write_single_reg(void *context, unsigned int reg, unsigned int val) +{ + struct max14001_state *st = context; + int ret; + + /* Enable writing to the SPI register. */ + ret = max14001_write(st, MAX14001_REG_WEN, MAX14001_REG_WEN_VALUE_WRITE); + if (ret) + return ret; + + /* Writing data into SPI register. */ + ret = max14001_write(st, reg, val); + if (ret) + return ret; + + /* Disable writing to the SPI register. */ + return max14001_write(st, MAX14001_REG_WEN, 0); +} + +static int max14001_write_verification_reg(struct max14001_state *st, unsigned int reg) +{ + unsigned int val; + int ret; + + ret = regmap_read(st->regmap, reg, &val); + if (ret) + return ret; + + return max14001_write(st, MAX14001_REG_VERIFICATION(reg), val); +} + +static int max14001_disable_mv_fault(struct max14001_state *st) +{ + unsigned int reg; + int ret; + + /* Enable writing to the SPI registers. */ + ret = max14001_write(st, MAX14001_REG_WEN, MAX14001_REG_WEN_VALUE_WRITE); + if (ret) + return ret; + + /* + * Reads all registers and writes the values to their appropriate + * verification registers to clear the Memory Validation fault. + */ + for (reg = MAX14001_REG_FLTEN; reg <= MAX14001_REG_ENBL; reg++) { + ret = max14001_write_verification_reg(st, reg); + if (ret) + return ret; + } + + /* Disable writing to the SPI registers. */ + return max14001_write(st, MAX14001_REG_WEN, 0); +} + +static int max14001_debugfs_reg_access(struct iio_dev *indio_dev, + unsigned int reg, unsigned int writeval, + unsigned int *readval) +{ + struct max14001_state *st = iio_priv(indio_dev); + + if (readval) + return regmap_read(st->regmap, reg, readval); + + return regmap_write(st->regmap, reg, writeval); +} + +static int max14001_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct max14001_state *st = iio_priv(indio_dev); + int ret; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + ret = regmap_read(st->regmap, MAX14001_REG_ADC, val); + if (ret) + return ret; + + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + *val = st->vref_mV; + *val2 = 10; + + return IIO_VAL_FRACTIONAL_LOG2; + default: + return -EINVAL; + } +} + +static const struct regmap_range max14001_regmap_rd_range[] = { + regmap_reg_range(MAX14001_REG_ADC, MAX14001_REG_ENBL), + regmap_reg_range(MAX14001_REG_WEN, MAX14001_REG_WEN), + regmap_reg_range(MAX14001_REG_VERIFICATION(MAX14001_REG_FLTEN), + MAX14001_REG_VERIFICATION(MAX14001_REG_ENBL)), +}; + +static const struct regmap_access_table max14001_regmap_rd_table = { + .yes_ranges = max14001_regmap_rd_range, + .n_yes_ranges = ARRAY_SIZE(max14001_regmap_rd_range), +}; + +static const struct regmap_range max14001_regmap_wr_range[] = { + regmap_reg_range(MAX14001_REG_FLTEN, MAX14001_REG_WEN), + regmap_reg_range(MAX14001_REG_VERIFICATION(MAX14001_REG_FLTEN), + MAX14001_REG_VERIFICATION(MAX14001_REG_ENBL)), +}; + +static const struct regmap_access_table max14001_regmap_wr_table = { + .yes_ranges = max14001_regmap_wr_range, + .n_yes_ranges = ARRAY_SIZE(max14001_regmap_wr_range), +}; + +static const struct regmap_config max14001_regmap_config = { + .reg_read = max14001_read, + .reg_write = max14001_write_single_reg, + .max_register = MAX14001_REG_VERIFICATION(MAX14001_REG_ENBL), + .rd_table = &max14001_regmap_rd_table, + .wr_table = &max14001_regmap_wr_table, +}; + +static const struct iio_info max14001_info = { + .read_raw = max14001_read_raw, + .debugfs_reg_access = max14001_debugfs_reg_access, +}; + +static const struct iio_chan_spec max14001_channel[] = { + { + .type = IIO_VOLTAGE, + .indexed = 1, + .channel = 0, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SCALE), + }, +}; + +static int max14001_probe(struct spi_device *spi) +{ + struct device *dev = &spi->dev; + struct iio_dev *indio_dev; + struct max14001_state *st; + int ret; + bool use_ext_vrefin = false; + + indio_dev = devm_iio_device_alloc(dev, sizeof(*st)); + if (!indio_dev) + return -ENOMEM; + + st = iio_priv(indio_dev); + st->spi = spi; + st->spi_hw_has_lsb_first = spi->mode & SPI_LSB_FIRST; + st->chip_info = spi_get_device_match_data(spi); + if (!st->chip_info) + return -EINVAL; + + indio_dev->name = st->chip_info->name; + indio_dev->info = &max14001_info; + indio_dev->channels = max14001_channel; + indio_dev->num_channels = ARRAY_SIZE(max14001_channel); + indio_dev->modes = INDIO_DIRECT_MODE; + + st->regmap = devm_regmap_init(dev, NULL, st, &max14001_regmap_config); + if (IS_ERR(st->regmap)) + return dev_err_probe(dev, PTR_ERR(st->regmap), "Failed to initialize regmap\n"); + + ret = devm_regulator_get_enable(dev, "vdd"); + if (ret) + return dev_err_probe(dev, ret, "Failed to enable Vdd supply\n"); + + ret = devm_regulator_get_enable(dev, "vddl"); + if (ret) + return dev_err_probe(dev, ret, "Failed to enable Vddl supply\n"); + + ret = devm_regulator_get_enable_read_voltage(dev, "refin"); + if (ret < 0 && ret != -ENODEV) + return dev_err_probe(dev, ret, "Failed to get REFIN voltage\n"); + + if (ret == -ENODEV) + ret = 1250000; + else + use_ext_vrefin = true; + st->vref_mV = ret / (MICRO / MILLI); + + if (use_ext_vrefin) { + /* + * Configure the MAX14001/MAX14002 to use an external voltage + * reference source by setting the bit 5 of the configuration register. + */ + ret = regmap_set_bits(st->regmap, MAX14001_REG_CFG, + MAX14001_REG_CFG_BIT_EXRF); + if (ret) + return dev_err_probe(dev, ret, + "Failed to set External REFIN in Configuration Register\n"); + } + + ret = max14001_disable_mv_fault(st); + if (ret) + return dev_err_probe(dev, ret, "Failed to disable MV Fault\n"); + + return devm_iio_device_register(dev, indio_dev); +} + +static struct max14001_chip_info max14001_chip_info = { + .name = "max14001", +}; + +static struct max14001_chip_info max14002_chip_info = { + .name = "max14002", +}; + +static const struct spi_device_id max14001_id_table[] = { + { .name = "max14001", .driver_data = (kernel_ulong_t)&max14001_chip_info }, + { .name = "max14002", .driver_data = (kernel_ulong_t)&max14002_chip_info }, + { } +}; + +static const struct of_device_id max14001_of_match[] = { + { .compatible = "adi,max14001", .data = &max14001_chip_info }, + { .compatible = "adi,max14002", .data = &max14002_chip_info }, + { } +}; +MODULE_DEVICE_TABLE(of, max14001_of_match); + +static struct spi_driver max14001_driver = { + .driver = { + .name = "max14001", + .of_match_table = max14001_of_match, + }, + .probe = max14001_probe, + .id_table = max14001_id_table, +}; +module_spi_driver(max14001_driver); + +MODULE_AUTHOR("Kim Seer Paller "); +MODULE_AUTHOR("Marilene Andrade Garcia "); +MODULE_DESCRIPTION("Analog Devices MAX14001/MAX14002 ADCs driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/iio/adc/max34408.c b/drivers/iio/adc/max34408.c new file mode 100644 index 000000000..c96dfed63 --- /dev/null +++ b/drivers/iio/adc/max34408.c @@ -0,0 +1,276 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * IIO driver for Maxim MAX34409/34408 ADC, 4-Channels/2-Channels, 8bits, I2C + * + * Datasheet: https://www.analog.com/media/en/technical-documentation/data-sheets/MAX34408-MAX34409.pdf + * + * TODO: ALERT interrupt, Overcurrent delay, Shutdown delay + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#define MAX34408_STATUS_REG 0x0 +#define MAX34408_CONTROL_REG 0x1 +#define MAX34408_OCDELAY_REG 0x2 +#define MAX34408_SDDELAY_REG 0x3 + +#define MAX34408_ADC1_REG 0x4 +#define MAX34408_ADC2_REG 0x5 +/* ADC3 & ADC4 always returns 0x0 on 34408 */ +#define MAX34409_ADC3_REG 0x6 +#define MAX34409_ADC4_REG 0x7 + +#define MAX34408_OCT1_REG 0x8 +#define MAX34408_OCT2_REG 0x9 +#define MAX34409_OCT3_REG 0xA +#define MAX34409_OCT4_REG 0xB + +#define MAX34408_DID_REG 0xC +#define MAX34408_DCYY_REG 0xD +#define MAX34408_DCWW_REG 0xE + +/* Bit masks for status register */ +#define MAX34408_STATUS_OC_MSK GENMASK(1, 0) +#define MAX34409_STATUS_OC_MSK GENMASK(3, 0) +#define MAX34408_STATUS_SHTDN BIT(4) +#define MAX34408_STATUS_ENA BIT(5) + +/* Bit masks for control register */ +#define MAX34408_CONTROL_AVG0 BIT(0) +#define MAX34408_CONTROL_AVG1 BIT(1) +#define MAX34408_CONTROL_AVG2 BIT(2) +#define MAX34408_CONTROL_ALERT BIT(3) + +#define MAX34408_DEFAULT_AVG 0x4 + +/* Bit masks for over current delay */ +#define MAX34408_OCDELAY_OCD_MSK GENMASK(6, 0) +#define MAX34408_OCDELAY_RESET BIT(7) + +/* Bit masks for shutdown delay */ +#define MAX34408_SDDELAY_SHD_MSK GENMASK(6, 0) +#define MAX34408_SDDELAY_RESET BIT(7) + +#define MAX34408_DEFAULT_RSENSE 1000 + +/** + * struct max34408_data - max34408/max34409 specific data. + * @regmap: device register map. + * @dev: max34408 device. + * @lock: lock for protecting access to device hardware registers, mostly + * for read modify write cycles for control registers. + * @input_rsense: Rsense values in uOhm, will be overwritten by + * values from channel nodes. + */ +struct max34408_data { + struct regmap *regmap; + struct device *dev; + struct mutex lock; + u32 input_rsense[4]; +}; + +static const struct regmap_config max34408_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .max_register = MAX34408_DCWW_REG, +}; + +struct max34408_adc_model_data { + const char *model_name; + const struct iio_chan_spec *channels; + const int num_channels; +}; + +#define MAX34008_CHANNEL(_index, _address) \ + { \ + .type = IIO_CURRENT, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ + BIT(IIO_CHAN_INFO_SCALE) | \ + BIT(IIO_CHAN_INFO_OFFSET), \ + .channel = (_index), \ + .address = (_address), \ + .indexed = 1, \ + } + +static const struct iio_chan_spec max34408_channels[] = { + MAX34008_CHANNEL(0, MAX34408_ADC1_REG), + MAX34008_CHANNEL(1, MAX34408_ADC2_REG), +}; + +static const struct iio_chan_spec max34409_channels[] = { + MAX34008_CHANNEL(0, MAX34408_ADC1_REG), + MAX34008_CHANNEL(1, MAX34408_ADC2_REG), + MAX34008_CHANNEL(2, MAX34409_ADC3_REG), + MAX34008_CHANNEL(3, MAX34409_ADC4_REG), +}; + +static int max34408_read_adc_avg(struct max34408_data *max34408, + const struct iio_chan_spec *chan, int *val) +{ + unsigned int ctrl; + int rc; + + guard(mutex)(&max34408->lock); + rc = regmap_read(max34408->regmap, MAX34408_CONTROL_REG, (u32 *)&ctrl); + if (rc) + return rc; + + /* set averaging (0b100) default values*/ + rc = regmap_write(max34408->regmap, MAX34408_CONTROL_REG, + MAX34408_DEFAULT_AVG); + if (rc) { + dev_err(max34408->dev, + "Error (%d) writing control register\n", rc); + return rc; + } + + rc = regmap_read(max34408->regmap, chan->address, val); + if (rc) + return rc; + + /* back to old values */ + rc = regmap_write(max34408->regmap, MAX34408_CONTROL_REG, ctrl); + if (rc) + dev_err(max34408->dev, + "Error (%d) writing control register\n", rc); + + return rc; +} + +static int max34408_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct max34408_data *max34408 = iio_priv(indio_dev); + int rc; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + rc = max34408_read_adc_avg(max34408, chan, val); + if (rc) + return rc; + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + /* + * calculate current for 8bit ADC with Rsense + * value. + * 10 mV * 1000 / Rsense uOhm = max current + * (max current * adc val * 1000) / (2^8 - 1) mA + */ + *val = 10000 / max34408->input_rsense[chan->channel]; + *val2 = 8; + return IIO_VAL_FRACTIONAL_LOG2; + default: + return -EINVAL; + } +} + +static const struct iio_info max34408_info = { + .read_raw = max34408_read_raw, +}; + +static const struct max34408_adc_model_data max34408_model_data = { + .model_name = "max34408", + .channels = max34408_channels, + .num_channels = 2, +}; + +static const struct max34408_adc_model_data max34409_model_data = { + .model_name = "max34409", + .channels = max34409_channels, + .num_channels = 4, +}; + +static int max34408_probe(struct i2c_client *client) +{ + const struct max34408_adc_model_data *model_data; + struct device *dev = &client->dev; + struct max34408_data *max34408; + struct fwnode_handle *node; + struct iio_dev *indio_dev; + struct regmap *regmap; + int rc, i = 0; + + model_data = i2c_get_match_data(client); + if (!model_data) + return -EINVAL; + + regmap = devm_regmap_init_i2c(client, &max34408_regmap_config); + if (IS_ERR(regmap)) { + dev_err_probe(dev, PTR_ERR(regmap), + "regmap_init failed\n"); + return PTR_ERR(regmap); + } + + indio_dev = devm_iio_device_alloc(dev, sizeof(*max34408)); + if (!indio_dev) + return -ENOMEM; + + max34408 = iio_priv(indio_dev); + max34408->regmap = regmap; + max34408->dev = dev; + mutex_init(&max34408->lock); + + device_for_each_child_node(dev, node) { + fwnode_property_read_u32(node, "maxim,rsense-val-micro-ohms", + &max34408->input_rsense[i]); + i++; + } + + /* disable ALERT and averaging */ + rc = regmap_write(max34408->regmap, MAX34408_CONTROL_REG, 0x0); + if (rc) + return rc; + + indio_dev->channels = model_data->channels; + indio_dev->num_channels = model_data->num_channels; + indio_dev->name = model_data->model_name; + + indio_dev->info = &max34408_info; + indio_dev->modes = INDIO_DIRECT_MODE; + + return devm_iio_device_register(dev, indio_dev); +} + +static const struct of_device_id max34408_of_match[] = { + { + .compatible = "maxim,max34408", + .data = &max34408_model_data, + }, + { + .compatible = "maxim,max34409", + .data = &max34409_model_data, + }, + { } +}; +MODULE_DEVICE_TABLE(of, max34408_of_match); + +static const struct i2c_device_id max34408_id[] = { + { .name = "max34408", .driver_data = (kernel_ulong_t)&max34408_model_data }, + { .name = "max34409", .driver_data = (kernel_ulong_t)&max34409_model_data }, + { } +}; +MODULE_DEVICE_TABLE(i2c, max34408_id); + +static struct i2c_driver max34408_driver = { + .driver = { + .name = "max34408", + .of_match_table = max34408_of_match, + }, + .probe = max34408_probe, + .id_table = max34408_id, +}; +module_i2c_driver(max34408_driver); + +MODULE_AUTHOR("Ivan Mikhaylov "); +MODULE_DESCRIPTION("Maxim MAX34408/34409 ADC driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/iio/adc/max77541-adc.c b/drivers/iio/adc/max77541-adc.c new file mode 100644 index 000000000..6e68cad5b --- /dev/null +++ b/drivers/iio/adc/max77541-adc.c @@ -0,0 +1,193 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) 2022 Analog Devices, Inc. + * ADI MAX77541 ADC Driver with IIO interface + */ + +#include +#include +#include +#include +#include + +#include + +enum max77541_adc_range { + LOW_RANGE, + MID_RANGE, + HIGH_RANGE, +}; + +enum max77541_adc_channel { + MAX77541_ADC_VSYS_V, + MAX77541_ADC_VOUT1_V, + MAX77541_ADC_VOUT2_V, + MAX77541_ADC_TEMP, +}; + +static int max77541_adc_offset(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2) +{ + switch (chan->channel) { + case MAX77541_ADC_TEMP: + *val = DIV_ROUND_CLOSEST(ABSOLUTE_ZERO_MILLICELSIUS, 1725); + return IIO_VAL_INT; + default: + return -EINVAL; + } +} + +static int max77541_adc_scale(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2) +{ + struct regmap **regmap = iio_priv(indio_dev); + unsigned int reg_val; + int ret; + + switch (chan->channel) { + case MAX77541_ADC_VSYS_V: + *val = 25; + return IIO_VAL_INT; + case MAX77541_ADC_VOUT1_V: + case MAX77541_ADC_VOUT2_V: + ret = regmap_read(*regmap, MAX77541_REG_M2_CFG1, ®_val); + if (ret) + return ret; + + reg_val = FIELD_GET(MAX77541_BITS_MX_CFG1_RNG, reg_val); + switch (reg_val) { + case LOW_RANGE: + *val = 6; + *val2 = 250000; + break; + case MID_RANGE: + *val = 12; + *val2 = 500000; + break; + case HIGH_RANGE: + *val = 25; + return IIO_VAL_INT; + default: + return -EINVAL; + } + + return IIO_VAL_INT_PLUS_MICRO; + case MAX77541_ADC_TEMP: + *val = 1725; + return IIO_VAL_INT; + default: + return -EINVAL; + } +} + +static int max77541_adc_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val) +{ + struct regmap **regmap = iio_priv(indio_dev); + int ret; + + ret = regmap_read(*regmap, chan->address, val); + if (ret) + return ret; + + return IIO_VAL_INT; +} + +#define MAX77541_ADC_CHANNEL_V(_channel, _name, _type, _reg) \ + { \ + .type = _type, \ + .indexed = 1, \ + .channel = _channel, \ + .address = _reg, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ + BIT(IIO_CHAN_INFO_SCALE), \ + .datasheet_name = _name, \ + } + +#define MAX77541_ADC_CHANNEL_TEMP(_channel, _name, _type, _reg) \ + { \ + .type = _type, \ + .indexed = 1, \ + .channel = _channel, \ + .address = _reg, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ + BIT(IIO_CHAN_INFO_SCALE) |\ + BIT(IIO_CHAN_INFO_OFFSET),\ + .datasheet_name = _name, \ + } + +static const struct iio_chan_spec max77541_adc_channels[] = { + MAX77541_ADC_CHANNEL_V(MAX77541_ADC_VSYS_V, "vsys_v", IIO_VOLTAGE, + MAX77541_REG_ADC_DATA_CH1), + MAX77541_ADC_CHANNEL_V(MAX77541_ADC_VOUT1_V, "vout1_v", IIO_VOLTAGE, + MAX77541_REG_ADC_DATA_CH2), + MAX77541_ADC_CHANNEL_V(MAX77541_ADC_VOUT2_V, "vout2_v", IIO_VOLTAGE, + MAX77541_REG_ADC_DATA_CH3), + MAX77541_ADC_CHANNEL_TEMP(MAX77541_ADC_TEMP, "temp", IIO_TEMP, + MAX77541_REG_ADC_DATA_CH6), +}; + +static int max77541_adc_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + switch (mask) { + case IIO_CHAN_INFO_OFFSET: + return max77541_adc_offset(indio_dev, chan, val, val2); + case IIO_CHAN_INFO_SCALE: + return max77541_adc_scale(indio_dev, chan, val, val2); + case IIO_CHAN_INFO_RAW: + return max77541_adc_raw(indio_dev, chan, val); + default: + return -EINVAL; + } +} + +static const struct iio_info max77541_adc_info = { + .read_raw = max77541_adc_read_raw, +}; + +static int max77541_adc_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct iio_dev *indio_dev; + struct regmap **regmap; + + indio_dev = devm_iio_device_alloc(dev, sizeof(*regmap)); + if (!indio_dev) + return -ENOMEM; + + regmap = iio_priv(indio_dev); + + *regmap = dev_get_regmap(dev->parent, NULL); + indio_dev->modes = INDIO_DIRECT_MODE; + + indio_dev->name = "max77541"; + indio_dev->info = &max77541_adc_info; + indio_dev->channels = max77541_adc_channels; + indio_dev->num_channels = ARRAY_SIZE(max77541_adc_channels); + + return devm_iio_device_register(dev, indio_dev); +} + +static const struct platform_device_id max77541_adc_platform_id[] = { + { .name = "max77541-adc" }, + { } +}; +MODULE_DEVICE_TABLE(platform, max77541_adc_platform_id); + +static struct platform_driver max77541_adc_driver = { + .driver = { + .name = "max77541-adc", + }, + .probe = max77541_adc_probe, + .id_table = max77541_adc_platform_id, +}; +module_platform_driver(max77541_adc_driver); + +MODULE_AUTHOR("Okan Sahin "); +MODULE_DESCRIPTION("MAX77541 ADC driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/iio/adc/max9611.c b/drivers/iio/adc/max9611.c new file mode 100644 index 000000000..45cea84c5 --- /dev/null +++ b/drivers/iio/adc/max9611.c @@ -0,0 +1,562 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * iio/adc/max9611.c + * + * Maxim max9611/max9612 high side current sense amplifier with + * 12-bit ADC interface. + * + * Copyright (C) 2017 Jacopo Mondi + */ + +/* + * This driver supports input common-mode voltage, current-sense + * amplifier with programmable gains and die temperature reading from + * Maxim max9611/max9612. + * + * Op-amp, analog comparator, and watchdog functionalities are not + * supported by this driver. + */ + +#include +#include +#include +#include +#include +#include + +/* max9611 register addresses */ +#define MAX9611_REG_CSA_DATA 0x00 +#define MAX9611_REG_RS_DATA 0x02 +#define MAX9611_REG_TEMP_DATA 0x08 +#define MAX9611_REG_CTRL1 0x0a +#define MAX9611_REG_CTRL2 0x0b + +/* max9611 REG1 mux configuration options */ +#define MAX9611_MUX_MASK GENMASK(3, 0) +#define MAX9611_MUX_SENSE_1x 0x00 +#define MAX9611_MUX_SENSE_4x 0x01 +#define MAX9611_MUX_SENSE_8x 0x02 +#define MAX9611_INPUT_VOLT 0x03 +#define MAX9611_MUX_TEMP 0x06 + +/* max9611 voltage (both csa and input) helper macros */ +#define MAX9611_VOLTAGE_SHIFT 0x04 +#define MAX9611_VOLTAGE_RAW(_r) ((_r) >> MAX9611_VOLTAGE_SHIFT) + +/* + * max9611 current sense amplifier voltage output: + * LSB and offset values depends on selected gain (1x, 4x, 8x) + * + * GAIN LSB (nV) OFFSET (LSB steps) + * 1x 107500 1 + * 4x 26880 1 + * 8x 13440 3 + * + * The complete formula to calculate current sense voltage is: + * (((adc_read >> 4) - offset) / ((1 / LSB) * 10^-3) + */ +#define MAX9611_CSA_1X_LSB_nV 107500 +#define MAX9611_CSA_4X_LSB_nV 26880 +#define MAX9611_CSA_8X_LSB_nV 13440 + +#define MAX9611_CSA_1X_OFFS_RAW 1 +#define MAX9611_CSA_4X_OFFS_RAW 1 +#define MAX9611_CSA_8X_OFFS_RAW 3 + +/* + * max9611 common input mode (CIM): LSB is 14mV, with 14mV offset at 25 C + * + * The complete formula to calculate input common voltage is: + * (((adc_read >> 4) * 1000) - offset) / (1 / 14 * 1000) + */ +#define MAX9611_CIM_LSB_mV 14 +#define MAX9611_CIM_OFFSET_RAW 1 + +/* + * max9611 temperature reading: LSB is 480 milli degrees Celsius + * + * The complete formula to calculate temperature is: + * ((adc_read >> 7) * 1000) / (1 / 480 * 1000) + */ +#define MAX9611_TEMP_MAX_POS 0x7f80 +#define MAX9611_TEMP_MAX_NEG 0xff80 +#define MAX9611_TEMP_MIN_NEG 0xd980 +#define MAX9611_TEMP_MASK GENMASK(15, 7) +#define MAX9611_TEMP_SHIFT 0x07 +#define MAX9611_TEMP_RAW(_r) ((_r) >> MAX9611_TEMP_SHIFT) +#define MAX9611_TEMP_SCALE_NUM 1000000 +#define MAX9611_TEMP_SCALE_DIV 2083 + +/* + * Conversion time is 2 ms (typically) at Ta=25 degreeC + * No maximum value is known, so play it safe. + */ +#define MAX9611_CONV_TIME_US_RANGE 3000, 3300 + +struct max9611_dev { + struct device *dev; + struct i2c_client *i2c_client; + struct mutex lock; + unsigned int shunt_resistor_uohm; +}; + +enum max9611_conf_ids { + CONF_SENSE_1x, + CONF_SENSE_4x, + CONF_SENSE_8x, + CONF_IN_VOLT, + CONF_TEMP, +}; + +/* + * max9611_mux_conf - associate ADC mux configuration with register address + * where data shall be read from + */ +static const unsigned int max9611_mux_conf[][2] = { + [CONF_SENSE_1x] = { MAX9611_MUX_SENSE_1x, MAX9611_REG_CSA_DATA }, + [CONF_SENSE_4x] = { MAX9611_MUX_SENSE_4x, MAX9611_REG_CSA_DATA }, + [CONF_SENSE_8x] = { MAX9611_MUX_SENSE_8x, MAX9611_REG_CSA_DATA }, + [CONF_IN_VOLT] = { MAX9611_INPUT_VOLT, MAX9611_REG_RS_DATA }, + [CONF_TEMP] = { MAX9611_MUX_TEMP, MAX9611_REG_TEMP_DATA }, +}; + +enum max9611_csa_gain { + CSA_GAIN_1x = CONF_SENSE_1x, + CSA_GAIN_4x = CONF_SENSE_4x, + CSA_GAIN_8x = CONF_SENSE_8x, +}; + +enum max9611_csa_gain_params { + CSA_GAIN_LSB_nV, + CSA_GAIN_OFFS_RAW, +}; + +/* + * max9611_csa_gain_conf - associate gain multiplier with LSB and + * offset values. + * + * Group together parameters associated with configurable gain + * on current sense amplifier path to ADC interface. + * Current sense read routine adjusts gain until it gets a meaningful + * value; use this structure to retrieve the correct LSB and offset values. + */ +static const unsigned int max9611_gain_conf[][2] = { + [CSA_GAIN_1x] = { MAX9611_CSA_1X_LSB_nV, MAX9611_CSA_1X_OFFS_RAW, }, + [CSA_GAIN_4x] = { MAX9611_CSA_4X_LSB_nV, MAX9611_CSA_4X_OFFS_RAW, }, + [CSA_GAIN_8x] = { MAX9611_CSA_8X_LSB_nV, MAX9611_CSA_8X_OFFS_RAW, }, +}; + +enum max9611_chan_addrs { + MAX9611_CHAN_VOLTAGE_INPUT, + MAX9611_CHAN_VOLTAGE_SENSE, + MAX9611_CHAN_TEMPERATURE, + MAX9611_CHAN_CURRENT_LOAD, + MAX9611_CHAN_POWER_LOAD, +}; + +static const struct iio_chan_spec max9611_channels[] = { + { + .type = IIO_TEMP, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SCALE), + .address = MAX9611_CHAN_TEMPERATURE, + }, + { + .type = IIO_VOLTAGE, + .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED), + .address = MAX9611_CHAN_VOLTAGE_SENSE, + .indexed = 1, + .channel = 0, + }, + { + .type = IIO_VOLTAGE, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SCALE) | + BIT(IIO_CHAN_INFO_OFFSET), + .address = MAX9611_CHAN_VOLTAGE_INPUT, + .indexed = 1, + .channel = 1, + }, + { + .type = IIO_CURRENT, + .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED), + .address = MAX9611_CHAN_CURRENT_LOAD, + }, + { + .type = IIO_POWER, + .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED), + .address = MAX9611_CHAN_POWER_LOAD + }, +}; + +/** + * max9611_read_single() - read a single value from ADC interface + * + * Data registers are 16 bit long, spread between two 8 bit registers + * with consecutive addresses. + * Configure ADC mux first, then read register at address "reg_addr". + * The smbus_read_word routine asks for 16 bits and the ADC is kind enough + * to return values from "reg_addr" and "reg_addr + 1" consecutively. + * Data are transmitted with big-endian ordering: MSB arrives first. + * + * @max9611: max9611 device + * @selector: index for mux and register configuration + * @raw_val: the value returned from ADC + */ +static int max9611_read_single(struct max9611_dev *max9611, + enum max9611_conf_ids selector, + u16 *raw_val) +{ + int ret; + + u8 mux_conf = max9611_mux_conf[selector][0] & MAX9611_MUX_MASK; + u8 reg_addr = max9611_mux_conf[selector][1]; + + /* + * Keep mutex lock held during read-write to avoid mux register + * (CTRL1) re-configuration. + */ + mutex_lock(&max9611->lock); + ret = i2c_smbus_write_byte_data(max9611->i2c_client, + MAX9611_REG_CTRL1, mux_conf); + if (ret) { + dev_err(max9611->dev, "i2c write byte failed: 0x%2x - 0x%2x\n", + MAX9611_REG_CTRL1, mux_conf); + mutex_unlock(&max9611->lock); + return ret; + } + + /* need a delay here to make register configuration stabilize. */ + + usleep_range(MAX9611_CONV_TIME_US_RANGE); + + ret = i2c_smbus_read_word_swapped(max9611->i2c_client, reg_addr); + if (ret < 0) { + dev_err(max9611->dev, "i2c read word from 0x%2x failed\n", + reg_addr); + mutex_unlock(&max9611->lock); + return ret; + } + + *raw_val = ret; + mutex_unlock(&max9611->lock); + + return 0; +} + +/** + * max9611_read_csa_voltage() - read current sense amplifier output voltage + * + * Current sense amplifier output voltage is read through a configurable + * 1x, 4x or 8x gain. + * Start with plain 1x gain, and adjust gain control properly until a + * meaningful value is read from ADC output. + * + * @max9611: max9611 device + * @adc_raw: raw value read from ADC output + * @csa_gain: gain configuration option selector + */ +static int max9611_read_csa_voltage(struct max9611_dev *max9611, + u16 *adc_raw, + enum max9611_csa_gain *csa_gain) +{ + enum max9611_conf_ids gain_selectors[] = { + CONF_SENSE_1x, + CONF_SENSE_4x, + CONF_SENSE_8x + }; + unsigned int i; + int ret; + + for (i = 0; i < ARRAY_SIZE(gain_selectors); ++i) { + ret = max9611_read_single(max9611, gain_selectors[i], adc_raw); + if (ret) + return ret; + + if (*adc_raw > 0) { + *csa_gain = (enum max9611_csa_gain)gain_selectors[i]; + return 0; + } + } + + return -EIO; +} + +static int max9611_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct max9611_dev *dev = iio_priv(indio_dev); + enum max9611_csa_gain gain_selector; + const unsigned int *csa_gain; + u16 adc_data; + int ret; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + + switch (chan->address) { + case MAX9611_CHAN_TEMPERATURE: + ret = max9611_read_single(dev, CONF_TEMP, + &adc_data); + if (ret) + return -EINVAL; + + *val = MAX9611_TEMP_RAW(adc_data); + return IIO_VAL_INT; + + case MAX9611_CHAN_VOLTAGE_INPUT: + ret = max9611_read_single(dev, CONF_IN_VOLT, + &adc_data); + if (ret) + return -EINVAL; + + *val = MAX9611_VOLTAGE_RAW(adc_data); + return IIO_VAL_INT; + } + + break; + + case IIO_CHAN_INFO_OFFSET: + /* MAX9611_CHAN_VOLTAGE_INPUT */ + *val = MAX9611_CIM_OFFSET_RAW; + + return IIO_VAL_INT; + + case IIO_CHAN_INFO_SCALE: + + switch (chan->address) { + case MAX9611_CHAN_TEMPERATURE: + *val = MAX9611_TEMP_SCALE_NUM; + *val2 = MAX9611_TEMP_SCALE_DIV; + + return IIO_VAL_FRACTIONAL; + + case MAX9611_CHAN_VOLTAGE_INPUT: + *val = MAX9611_CIM_LSB_mV; + + return IIO_VAL_INT; + } + + break; + + case IIO_CHAN_INFO_PROCESSED: + + switch (chan->address) { + case MAX9611_CHAN_VOLTAGE_SENSE: + /* + * processed (mV): (raw - offset) * LSB (nV) / 10^6 + * + * Even if max9611 can output raw csa voltage readings, + * use a produced value as scale depends on gain. + */ + ret = max9611_read_csa_voltage(dev, &adc_data, + &gain_selector); + if (ret) + return -EINVAL; + + csa_gain = max9611_gain_conf[gain_selector]; + + adc_data -= csa_gain[CSA_GAIN_OFFS_RAW]; + *val = MAX9611_VOLTAGE_RAW(adc_data) * + csa_gain[CSA_GAIN_LSB_nV]; + *val2 = 1000000; + + return IIO_VAL_FRACTIONAL; + + case MAX9611_CHAN_CURRENT_LOAD: + /* processed (mA): Vcsa (nV) / Rshunt (uOhm) */ + ret = max9611_read_csa_voltage(dev, &adc_data, + &gain_selector); + if (ret) + return -EINVAL; + + csa_gain = max9611_gain_conf[gain_selector]; + + adc_data -= csa_gain[CSA_GAIN_OFFS_RAW]; + *val = MAX9611_VOLTAGE_RAW(adc_data) * + csa_gain[CSA_GAIN_LSB_nV]; + *val2 = dev->shunt_resistor_uohm; + + return IIO_VAL_FRACTIONAL; + + case MAX9611_CHAN_POWER_LOAD: + /* + * processed (mW): Vin (mV) * Vcsa (uV) / + * Rshunt (uOhm) + */ + ret = max9611_read_single(dev, CONF_IN_VOLT, + &adc_data); + if (ret) + return -EINVAL; + + adc_data -= MAX9611_CIM_OFFSET_RAW; + *val = MAX9611_VOLTAGE_RAW(adc_data) * + MAX9611_CIM_LSB_mV; + + ret = max9611_read_csa_voltage(dev, &adc_data, + &gain_selector); + if (ret) + return -EINVAL; + + csa_gain = max9611_gain_conf[gain_selector]; + + /* divide by 10^3 here to avoid 32bit overflow */ + adc_data -= csa_gain[CSA_GAIN_OFFS_RAW]; + *val *= MAX9611_VOLTAGE_RAW(adc_data) * + csa_gain[CSA_GAIN_LSB_nV] / 1000; + *val2 = dev->shunt_resistor_uohm; + + return IIO_VAL_FRACTIONAL; + } + + break; + } + + return -EINVAL; +} + +static ssize_t max9611_shunt_resistor_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct max9611_dev *max9611 = iio_priv(dev_to_iio_dev(dev)); + unsigned int i, r; + + i = max9611->shunt_resistor_uohm / 1000000; + r = max9611->shunt_resistor_uohm % 1000000; + + return sysfs_emit(buf, "%u.%06u\n", i, r); +} + +static IIO_DEVICE_ATTR(in_power_shunt_resistor, 0444, + max9611_shunt_resistor_show, NULL, 0); +static IIO_DEVICE_ATTR(in_current_shunt_resistor, 0444, + max9611_shunt_resistor_show, NULL, 0); + +static struct attribute *max9611_attributes[] = { + &iio_dev_attr_in_power_shunt_resistor.dev_attr.attr, + &iio_dev_attr_in_current_shunt_resistor.dev_attr.attr, + NULL, +}; + +static const struct attribute_group max9611_attribute_group = { + .attrs = max9611_attributes, +}; + +static const struct iio_info indio_info = { + .read_raw = max9611_read_raw, + .attrs = &max9611_attribute_group, +}; + +static int max9611_init(struct max9611_dev *max9611) +{ + struct i2c_client *client = max9611->i2c_client; + u16 regval; + int ret; + + if (!i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_WRITE_BYTE | + I2C_FUNC_SMBUS_READ_WORD_DATA)) { + dev_err(max9611->dev, + "I2c adapter does not support smbus write_byte or read_word functionalities: aborting probe.\n"); + return -EINVAL; + } + + /* Make sure die temperature is in range to test communications. */ + ret = max9611_read_single(max9611, CONF_TEMP, ®val); + if (ret) + return ret; + + regval &= MAX9611_TEMP_MASK; + + if ((regval > MAX9611_TEMP_MAX_POS && + regval < MAX9611_TEMP_MIN_NEG) || + regval > MAX9611_TEMP_MAX_NEG) { + dev_err(max9611->dev, + "Invalid value received from ADC 0x%4x: aborting\n", + regval); + return -EIO; + } + + /* Mux shall be zeroed back before applying other configurations */ + ret = i2c_smbus_write_byte_data(max9611->i2c_client, + MAX9611_REG_CTRL1, 0); + if (ret) { + dev_err(max9611->dev, "i2c write byte failed: 0x%2x - 0x%2x\n", + MAX9611_REG_CTRL1, 0); + return ret; + } + + ret = i2c_smbus_write_byte_data(max9611->i2c_client, + MAX9611_REG_CTRL2, 0); + if (ret) { + dev_err(max9611->dev, "i2c write byte failed: 0x%2x - 0x%2x\n", + MAX9611_REG_CTRL2, 0); + return ret; + } + usleep_range(MAX9611_CONV_TIME_US_RANGE); + + return 0; +} + +static const struct of_device_id max9611_of_table[] = { + { .compatible = "maxim,max9611", .data = "max9611" }, + { .compatible = "maxim,max9612", .data = "max9612" }, + { } +}; + +MODULE_DEVICE_TABLE(of, max9611_of_table); +static int max9611_probe(struct i2c_client *client) +{ + const char * const shunt_res_prop = "shunt-resistor-micro-ohms"; + struct max9611_dev *max9611; + struct iio_dev *indio_dev; + struct device *dev = &client->dev; + unsigned int of_shunt; + int ret; + + indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*max9611)); + if (!indio_dev) + return -ENOMEM; + + i2c_set_clientdata(client, indio_dev); + + max9611 = iio_priv(indio_dev); + max9611->dev = dev; + max9611->i2c_client = client; + mutex_init(&max9611->lock); + + ret = device_property_read_u32(dev, shunt_res_prop, &of_shunt); + if (ret) { + dev_err(dev, "Missing %s property for %pfw node\n", + shunt_res_prop, dev_fwnode(dev)); + return ret; + } + max9611->shunt_resistor_uohm = of_shunt; + + ret = max9611_init(max9611); + if (ret) + return ret; + + indio_dev->name = device_get_match_data(dev); + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->info = &indio_info; + indio_dev->channels = max9611_channels; + indio_dev->num_channels = ARRAY_SIZE(max9611_channels); + + return devm_iio_device_register(dev, indio_dev); +} + +static struct i2c_driver max9611_driver = { + .driver = { + .name = "max9611", + .of_match_table = max9611_of_table, + }, + .probe = max9611_probe, +}; +module_i2c_driver(max9611_driver); + +MODULE_AUTHOR("Jacopo Mondi "); +MODULE_DESCRIPTION("Maxim max9611/12 current sense amplifier with 12bit ADC"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/adc/mcp320x.c b/drivers/iio/adc/mcp320x.c new file mode 100644 index 000000000..18cb957e7 --- /dev/null +++ b/drivers/iio/adc/mcp320x.c @@ -0,0 +1,508 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2013 Oskar Andero + * Copyright (C) 2014 Rose Technology + * Allan Bendorff Jensen + * Soren Andersen + * + * Driver for following ADC chips from Microchip Technology's: + * 10 Bit converter + * MCP3001 + * MCP3002 + * MCP3004 + * MCP3008 + * ------------ + * 12 bit converter + * MCP3201 + * MCP3202 + * MCP3204 + * MCP3208 + * ------------ + * 13 bit converter + * MCP3301 + * ------------ + * 22 bit converter + * MCP3550 + * MCP3551 + * MCP3553 + * + * Datasheet can be found here: + * https://ww1.microchip.com/downloads/en/DeviceDoc/21293C.pdf mcp3001 + * https://ww1.microchip.com/downloads/en/DeviceDoc/21294E.pdf mcp3002 + * https://ww1.microchip.com/downloads/en/DeviceDoc/21295d.pdf mcp3004/08 + * http://ww1.microchip.com/downloads/en/DeviceDoc/21290D.pdf mcp3201 + * http://ww1.microchip.com/downloads/en/DeviceDoc/21034D.pdf mcp3202 + * http://ww1.microchip.com/downloads/en/DeviceDoc/21298c.pdf mcp3204/08 + * https://ww1.microchip.com/downloads/en/DeviceDoc/21700E.pdf mcp3301 + * http://ww1.microchip.com/downloads/en/DeviceDoc/21950D.pdf mcp3550/1/3 + */ + +#include +#include +#include +#include +#include +#include + +enum { + mcp3001, + mcp3002, + mcp3004, + mcp3008, + mcp3201, + mcp3202, + mcp3204, + mcp3208, + mcp3301, + mcp3550_50, + mcp3550_60, + mcp3551, + mcp3553, +}; + +struct mcp320x_chip_info { + const struct iio_chan_spec *channels; + unsigned int num_channels; + unsigned int resolution; + unsigned int conv_time; /* usec */ +}; + +/** + * struct mcp320x - Microchip SPI ADC instance + * @spi: SPI slave (parent of the IIO device) + * @msg: SPI message to select a channel and receive a value from the ADC + * @transfer: SPI transfers used by @msg + * @start_conv_msg: SPI message to start a conversion by briefly asserting CS + * @start_conv_transfer: SPI transfer used by @start_conv_msg + * @reg: regulator generating Vref + * @lock: protects read sequences + * @chip_info: ADC properties + * @tx_buf: buffer for @transfer[0] (not used on single-channel converters) + * @rx_buf: buffer for @transfer[1] + */ +struct mcp320x { + struct spi_device *spi; + struct spi_message msg; + struct spi_transfer transfer[2]; + struct spi_message start_conv_msg; + struct spi_transfer start_conv_transfer; + + struct regulator *reg; + struct mutex lock; + const struct mcp320x_chip_info *chip_info; + + u8 tx_buf __aligned(IIO_DMA_MINALIGN); + u8 rx_buf[4]; +}; + +static int mcp320x_channel_to_tx_data(int device_index, + const unsigned int channel, bool differential) +{ + int start_bit = 1; + + switch (device_index) { + case mcp3002: + case mcp3202: + return ((start_bit << 4) | (!differential << 3) | + (channel << 2)); + case mcp3004: + case mcp3204: + case mcp3008: + case mcp3208: + return ((start_bit << 6) | (!differential << 5) | + (channel << 2)); + default: + return -EINVAL; + } +} + +static int mcp320x_adc_conversion(struct mcp320x *adc, u8 channel, + bool differential, int device_index, int *val) +{ + int ret; + + if (adc->chip_info->conv_time) { + ret = spi_sync(adc->spi, &adc->start_conv_msg); + if (ret < 0) + return ret; + + usleep_range(adc->chip_info->conv_time, + adc->chip_info->conv_time + 100); + } + + memset(&adc->rx_buf, 0, sizeof(adc->rx_buf)); + if (adc->chip_info->num_channels > 1) + adc->tx_buf = mcp320x_channel_to_tx_data(device_index, channel, + differential); + + ret = spi_sync(adc->spi, &adc->msg); + if (ret < 0) + return ret; + + switch (device_index) { + case mcp3001: + *val = (adc->rx_buf[0] << 5 | adc->rx_buf[1] >> 3); + return 0; + case mcp3002: + case mcp3004: + case mcp3008: + *val = (adc->rx_buf[0] << 2 | adc->rx_buf[1] >> 6); + return 0; + case mcp3201: + *val = (adc->rx_buf[0] << 7 | adc->rx_buf[1] >> 1); + return 0; + case mcp3202: + case mcp3204: + case mcp3208: + *val = (adc->rx_buf[0] << 4 | adc->rx_buf[1] >> 4); + return 0; + case mcp3301: + *val = sign_extend32((adc->rx_buf[0] & 0x1f) << 8 + | adc->rx_buf[1], 12); + return 0; + case mcp3550_50: + case mcp3550_60: + case mcp3551: + case mcp3553: { + u32 raw = be32_to_cpup((__be32 *)adc->rx_buf); + + if (!(adc->spi->mode & SPI_CPOL)) + raw <<= 1; /* strip Data Ready bit in SPI mode 0,0 */ + + /* + * If the input is within -vref and vref, bit 21 is the sign. + * Up to 12% overrange or underrange are allowed, in which case + * bit 23 is the sign and bit 0 to 21 is the value. + */ + raw >>= 8; + if (raw & BIT(22) && raw & BIT(23)) + return -EIO; /* cannot have overrange AND underrange */ + else if (raw & BIT(22)) + raw &= ~BIT(22); /* overrange */ + else if (raw & BIT(23) || raw & BIT(21)) + raw |= GENMASK(31, 22); /* underrange or negative */ + + *val = (s32)raw; + return 0; + } + default: + return -EINVAL; + } +} + +static int mcp320x_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *channel, int *val, + int *val2, long mask) +{ + struct mcp320x *adc = iio_priv(indio_dev); + int ret = -EINVAL; + int device_index = 0; + + mutex_lock(&adc->lock); + + device_index = spi_get_device_id(adc->spi)->driver_data; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + ret = mcp320x_adc_conversion(adc, channel->address, + channel->differential, device_index, val); + if (ret < 0) + goto out; + + ret = IIO_VAL_INT; + break; + + case IIO_CHAN_INFO_SCALE: + ret = regulator_get_voltage(adc->reg); + if (ret < 0) + goto out; + + /* convert regulator output voltage to mV */ + *val = ret / 1000; + *val2 = adc->chip_info->resolution; + ret = IIO_VAL_FRACTIONAL_LOG2; + break; + } + +out: + mutex_unlock(&adc->lock); + + return ret; +} + +#define MCP320X_VOLTAGE_CHANNEL(num) \ + { \ + .type = IIO_VOLTAGE, \ + .indexed = 1, \ + .channel = (num), \ + .address = (num), \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) \ + } + +#define MCP320X_VOLTAGE_CHANNEL_DIFF(chan1, chan2) \ + { \ + .type = IIO_VOLTAGE, \ + .indexed = 1, \ + .channel = (chan1), \ + .channel2 = (chan2), \ + .address = (chan1), \ + .differential = 1, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) \ + } + +static const struct iio_chan_spec mcp3201_channels[] = { + MCP320X_VOLTAGE_CHANNEL_DIFF(0, 1), +}; + +static const struct iio_chan_spec mcp3202_channels[] = { + MCP320X_VOLTAGE_CHANNEL(0), + MCP320X_VOLTAGE_CHANNEL(1), + MCP320X_VOLTAGE_CHANNEL_DIFF(0, 1), + MCP320X_VOLTAGE_CHANNEL_DIFF(1, 0), +}; + +static const struct iio_chan_spec mcp3204_channels[] = { + MCP320X_VOLTAGE_CHANNEL(0), + MCP320X_VOLTAGE_CHANNEL(1), + MCP320X_VOLTAGE_CHANNEL(2), + MCP320X_VOLTAGE_CHANNEL(3), + MCP320X_VOLTAGE_CHANNEL_DIFF(0, 1), + MCP320X_VOLTAGE_CHANNEL_DIFF(1, 0), + MCP320X_VOLTAGE_CHANNEL_DIFF(2, 3), + MCP320X_VOLTAGE_CHANNEL_DIFF(3, 2), +}; + +static const struct iio_chan_spec mcp3208_channels[] = { + MCP320X_VOLTAGE_CHANNEL(0), + MCP320X_VOLTAGE_CHANNEL(1), + MCP320X_VOLTAGE_CHANNEL(2), + MCP320X_VOLTAGE_CHANNEL(3), + MCP320X_VOLTAGE_CHANNEL(4), + MCP320X_VOLTAGE_CHANNEL(5), + MCP320X_VOLTAGE_CHANNEL(6), + MCP320X_VOLTAGE_CHANNEL(7), + MCP320X_VOLTAGE_CHANNEL_DIFF(0, 1), + MCP320X_VOLTAGE_CHANNEL_DIFF(1, 0), + MCP320X_VOLTAGE_CHANNEL_DIFF(2, 3), + MCP320X_VOLTAGE_CHANNEL_DIFF(3, 2), + MCP320X_VOLTAGE_CHANNEL_DIFF(4, 5), + MCP320X_VOLTAGE_CHANNEL_DIFF(5, 4), + MCP320X_VOLTAGE_CHANNEL_DIFF(6, 7), + MCP320X_VOLTAGE_CHANNEL_DIFF(7, 6), +}; + +static const struct iio_info mcp320x_info = { + .read_raw = mcp320x_read_raw, +}; + +static const struct mcp320x_chip_info mcp320x_chip_infos[] = { + [mcp3001] = { + .channels = mcp3201_channels, + .num_channels = ARRAY_SIZE(mcp3201_channels), + .resolution = 10 + }, + [mcp3002] = { + .channels = mcp3202_channels, + .num_channels = ARRAY_SIZE(mcp3202_channels), + .resolution = 10 + }, + [mcp3004] = { + .channels = mcp3204_channels, + .num_channels = ARRAY_SIZE(mcp3204_channels), + .resolution = 10 + }, + [mcp3008] = { + .channels = mcp3208_channels, + .num_channels = ARRAY_SIZE(mcp3208_channels), + .resolution = 10 + }, + [mcp3201] = { + .channels = mcp3201_channels, + .num_channels = ARRAY_SIZE(mcp3201_channels), + .resolution = 12 + }, + [mcp3202] = { + .channels = mcp3202_channels, + .num_channels = ARRAY_SIZE(mcp3202_channels), + .resolution = 12 + }, + [mcp3204] = { + .channels = mcp3204_channels, + .num_channels = ARRAY_SIZE(mcp3204_channels), + .resolution = 12 + }, + [mcp3208] = { + .channels = mcp3208_channels, + .num_channels = ARRAY_SIZE(mcp3208_channels), + .resolution = 12 + }, + [mcp3301] = { + .channels = mcp3201_channels, + .num_channels = ARRAY_SIZE(mcp3201_channels), + .resolution = 13 + }, + [mcp3550_50] = { + .channels = mcp3201_channels, + .num_channels = ARRAY_SIZE(mcp3201_channels), + .resolution = 21, + /* 2% max deviation + 144 clock periods to exit shutdown */ + .conv_time = 80000 * 1.02 + 144000 / 102.4, + }, + [mcp3550_60] = { + .channels = mcp3201_channels, + .num_channels = ARRAY_SIZE(mcp3201_channels), + .resolution = 21, + .conv_time = 66670 * 1.02 + 144000 / 122.88, + }, + [mcp3551] = { + .channels = mcp3201_channels, + .num_channels = ARRAY_SIZE(mcp3201_channels), + .resolution = 21, + .conv_time = 73100 * 1.02 + 144000 / 112.64, + }, + [mcp3553] = { + .channels = mcp3201_channels, + .num_channels = ARRAY_SIZE(mcp3201_channels), + .resolution = 21, + .conv_time = 16670 * 1.02 + 144000 / 122.88, + }, +}; + +static void mcp320x_regulator_disable(void *reg) +{ + regulator_disable(reg); +} + +static int mcp320x_probe(struct spi_device *spi) +{ + struct iio_dev *indio_dev; + struct mcp320x *adc; + const struct mcp320x_chip_info *chip_info; + int ret, device_index; + + indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*adc)); + if (!indio_dev) + return -ENOMEM; + + adc = iio_priv(indio_dev); + adc->spi = spi; + + indio_dev->name = spi_get_device_id(spi)->name; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->info = &mcp320x_info; + + device_index = spi_get_device_id(spi)->driver_data; + chip_info = &mcp320x_chip_infos[device_index]; + indio_dev->channels = chip_info->channels; + indio_dev->num_channels = chip_info->num_channels; + + adc->chip_info = chip_info; + + adc->transfer[0].tx_buf = &adc->tx_buf; + adc->transfer[0].len = sizeof(adc->tx_buf); + adc->transfer[1].rx_buf = adc->rx_buf; + adc->transfer[1].len = DIV_ROUND_UP(chip_info->resolution, 8); + + if (chip_info->num_channels == 1) + /* single-channel converters are rx only (no MOSI pin) */ + spi_message_init_with_transfers(&adc->msg, + &adc->transfer[1], 1); + else + spi_message_init_with_transfers(&adc->msg, adc->transfer, + ARRAY_SIZE(adc->transfer)); + + switch (device_index) { + case mcp3550_50: + case mcp3550_60: + case mcp3551: + case mcp3553: + /* rx len increases from 24 to 25 bit in SPI mode 0,0 */ + if (!(spi->mode & SPI_CPOL)) + adc->transfer[1].len++; + + /* conversions are started by asserting CS pin for 8 usec */ + adc->start_conv_transfer.delay.value = 8; + adc->start_conv_transfer.delay.unit = SPI_DELAY_UNIT_USECS; + spi_message_init_with_transfers(&adc->start_conv_msg, + &adc->start_conv_transfer, 1); + + /* + * If CS was previously kept low (continuous conversion mode) + * and then changed to high, the chip is in shutdown. + * Sometimes it fails to wake from shutdown and clocks out + * only 0xffffff. The magic sequence of performing two + * conversions without delay between them resets the chip + * and ensures all subsequent conversions succeed. + */ + mcp320x_adc_conversion(adc, 0, 1, device_index, &ret); + mcp320x_adc_conversion(adc, 0, 1, device_index, &ret); + } + + adc->reg = devm_regulator_get(&spi->dev, "vref"); + if (IS_ERR(adc->reg)) + return PTR_ERR(adc->reg); + + ret = regulator_enable(adc->reg); + if (ret < 0) + return ret; + + ret = devm_add_action_or_reset(&spi->dev, mcp320x_regulator_disable, adc->reg); + if (ret < 0) + return ret; + + mutex_init(&adc->lock); + + return devm_iio_device_register(&spi->dev, indio_dev); +} + +static const struct of_device_id mcp320x_dt_ids[] = { + { .compatible = "microchip,mcp3001" }, + { .compatible = "microchip,mcp3002" }, + { .compatible = "microchip,mcp3004" }, + { .compatible = "microchip,mcp3008" }, + { .compatible = "microchip,mcp3201" }, + { .compatible = "microchip,mcp3202" }, + { .compatible = "microchip,mcp3204" }, + { .compatible = "microchip,mcp3208" }, + { .compatible = "microchip,mcp3301" }, + { .compatible = "microchip,mcp3550-50" }, + { .compatible = "microchip,mcp3550-60" }, + { .compatible = "microchip,mcp3551" }, + { .compatible = "microchip,mcp3553" }, + { } +}; +MODULE_DEVICE_TABLE(of, mcp320x_dt_ids); + +static const struct spi_device_id mcp320x_id[] = { + { .name = "mcp3001", .driver_data = mcp3001 }, + { .name = "mcp3002", .driver_data = mcp3002 }, + { .name = "mcp3004", .driver_data = mcp3004 }, + { .name = "mcp3008", .driver_data = mcp3008 }, + { .name = "mcp3201", .driver_data = mcp3201 }, + { .name = "mcp3202", .driver_data = mcp3202 }, + { .name = "mcp3204", .driver_data = mcp3204 }, + { .name = "mcp3208", .driver_data = mcp3208 }, + { .name = "mcp3301", .driver_data = mcp3301 }, + { .name = "mcp3550-50", .driver_data = mcp3550_50 }, + { .name = "mcp3550-60", .driver_data = mcp3550_60 }, + { .name = "mcp3551", .driver_data = mcp3551 }, + { .name = "mcp3553", .driver_data = mcp3553 }, + { } +}; +MODULE_DEVICE_TABLE(spi, mcp320x_id); + +static struct spi_driver mcp320x_driver = { + .driver = { + .name = "mcp320x", + .of_match_table = mcp320x_dt_ids, + }, + .probe = mcp320x_probe, + .id_table = mcp320x_id, +}; +module_spi_driver(mcp320x_driver); + +MODULE_AUTHOR("Oskar Andero "); +MODULE_DESCRIPTION("Microchip Technology MCP3x01/02/04/08 and MCP3550/1/3"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/adc/mcp3422.c b/drivers/iio/adc/mcp3422.c new file mode 100644 index 000000000..36ba00edf --- /dev/null +++ b/drivers/iio/adc/mcp3422.c @@ -0,0 +1,415 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * mcp3422.c - driver for the Microchip mcp3421/2/3/4/5/6/7/8 chip family + * + * Copyright (C) 2013, Angelo Compagnucci + * Author: Angelo Compagnucci + * + * Datasheet: http://ww1.microchip.com/downloads/en/devicedoc/22088b.pdf + * https://ww1.microchip.com/downloads/en/DeviceDoc/22226a.pdf + * https://ww1.microchip.com/downloads/en/DeviceDoc/22072b.pdf + * + * This driver exports the value of analog input voltage to sysfs, the + * voltage unit is nV. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#define MCP3422_CHANNEL_MASK GENMASK(6, 5) +#define MCP3422_SRATE_MASK GENMASK(3, 2) +#define MCP3422_PGA_MASK GENMASK(1, 0) + +#define MCP3422_SRATE_240 0x0 +#define MCP3422_SRATE_60 0x1 +#define MCP3422_SRATE_15 0x2 +#define MCP3422_SRATE_3 0x3 +#define MCP3422_PGA_1 0 +#define MCP3422_PGA_2 1 +#define MCP3422_PGA_4 2 +#define MCP3422_PGA_8 3 +#define MCP3422_CONT_SAMPLING BIT(4) + +#define MCP3422_CHAN(_index) \ + { \ + .type = IIO_VOLTAGE, \ + .indexed = 1, \ + .channel = _index, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) \ + | BIT(IIO_CHAN_INFO_SCALE), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SAMP_FREQ), \ + } + +static const int mcp3422_scales[4][4] = { + { 1000000, 500000, 250000, 125000 }, + { 250000, 125000, 62500, 31250 }, + { 62500, 31250, 15625, 7812 }, + { 15625, 7812, 3906, 1953 } }; + +/* Constant msleep times for data acquisitions */ +static const int mcp3422_read_times[4] = { + [MCP3422_SRATE_240] = 1000 / 240, + [MCP3422_SRATE_60] = 1000 / 60, + [MCP3422_SRATE_15] = 1000 / 15, + [MCP3422_SRATE_3] = 1000 / 3 }; + +/* sample rates to integer conversion table */ +static const int mcp3422_sample_rates[4] = { + [MCP3422_SRATE_240] = 240, + [MCP3422_SRATE_60] = 60, + [MCP3422_SRATE_15] = 15, + [MCP3422_SRATE_3] = 3 }; + +/* sample rates to sign extension table */ +static const int mcp3422_sign_extend[4] = { + [MCP3422_SRATE_240] = 11, + [MCP3422_SRATE_60] = 13, + [MCP3422_SRATE_15] = 15, + [MCP3422_SRATE_3] = 17 }; + +/* Client data (each client gets its own) */ +struct mcp3422 { + struct i2c_client *i2c; + u8 id; + u8 config; + u8 pga[4]; + struct mutex lock; +}; + +static int mcp3422_update_config(struct mcp3422 *adc, u8 newconfig) +{ + int ret; + + ret = i2c_master_send(adc->i2c, &newconfig, 1); + if (ret > 0) { + adc->config = newconfig; + ret = 0; + } + + return ret; +} + +static int mcp3422_read(struct mcp3422 *adc, int *value, u8 *config) +{ + int ret = 0; + u8 sample_rate = FIELD_GET(MCP3422_SRATE_MASK, adc->config); + u8 buf[4] = {0, 0, 0, 0}; + u32 temp; + + if (sample_rate == MCP3422_SRATE_3) { + ret = i2c_master_recv(adc->i2c, buf, 4); + temp = get_unaligned_be24(&buf[0]); + *config = buf[3]; + } else { + ret = i2c_master_recv(adc->i2c, buf, 3); + temp = get_unaligned_be16(&buf[0]); + *config = buf[2]; + } + + *value = sign_extend32(temp, mcp3422_sign_extend[sample_rate]); + + return ret; +} + +static int mcp3422_read_channel(struct mcp3422 *adc, + struct iio_chan_spec const *channel, int *value) +{ + int ret; + u8 config; + u8 req_channel = channel->channel; + + mutex_lock(&adc->lock); + + if (req_channel != FIELD_GET(MCP3422_CHANNEL_MASK, adc->config)) { + config = adc->config; + + FIELD_MODIFY(MCP3422_CHANNEL_MASK, &config, req_channel); + FIELD_MODIFY(MCP3422_PGA_MASK, &config, adc->pga[req_channel]); + + ret = mcp3422_update_config(adc, config); + if (ret < 0) { + mutex_unlock(&adc->lock); + return ret; + } + msleep(mcp3422_read_times[FIELD_GET(MCP3422_SRATE_MASK, adc->config)]); + } + + ret = mcp3422_read(adc, value, &config); + + mutex_unlock(&adc->lock); + + return ret; +} + +static int mcp3422_read_raw(struct iio_dev *iio, + struct iio_chan_spec const *channel, int *val1, + int *val2, long mask) +{ + struct mcp3422 *adc = iio_priv(iio); + int err; + u8 sample_rate = FIELD_GET(MCP3422_SRATE_MASK, adc->config); + u8 pga = FIELD_GET(MCP3422_PGA_MASK, adc->config); + + switch (mask) { + case IIO_CHAN_INFO_RAW: + err = mcp3422_read_channel(adc, channel, val1); + if (err < 0) + return -EINVAL; + return IIO_VAL_INT; + + case IIO_CHAN_INFO_SCALE: + + *val1 = 0; + *val2 = mcp3422_scales[sample_rate][pga]; + return IIO_VAL_INT_PLUS_NANO; + + case IIO_CHAN_INFO_SAMP_FREQ: + *val1 = mcp3422_sample_rates[FIELD_GET(MCP3422_SRATE_MASK, adc->config)]; + return IIO_VAL_INT; + + default: + break; + } + + return -EINVAL; +} + +static int mcp3422_write_raw(struct iio_dev *iio, + struct iio_chan_spec const *channel, int val1, + int val2, long mask) +{ + struct mcp3422 *adc = iio_priv(iio); + u8 temp; + u8 config = adc->config; + u8 req_channel = channel->channel; + u8 sample_rate = FIELD_GET(MCP3422_SRATE_MASK, config); + u8 i; + + switch (mask) { + case IIO_CHAN_INFO_SCALE: + if (val1 != 0) + return -EINVAL; + + for (i = 0; i < ARRAY_SIZE(mcp3422_scales[0]); i++) { + if (val2 == mcp3422_scales[sample_rate][i]) { + adc->pga[req_channel] = i; + + FIELD_MODIFY(MCP3422_CHANNEL_MASK, &config, req_channel); + FIELD_MODIFY(MCP3422_PGA_MASK, &config, adc->pga[req_channel]); + + return mcp3422_update_config(adc, config); + } + } + return -EINVAL; + + case IIO_CHAN_INFO_SAMP_FREQ: + switch (val1) { + case 240: + temp = MCP3422_SRATE_240; + break; + case 60: + temp = MCP3422_SRATE_60; + break; + case 15: + temp = MCP3422_SRATE_15; + break; + case 3: + if (adc->id > 4) + return -EINVAL; + temp = MCP3422_SRATE_3; + break; + default: + return -EINVAL; + } + + FIELD_MODIFY(MCP3422_CHANNEL_MASK, &config, req_channel); + FIELD_MODIFY(MCP3422_SRATE_MASK, &config, temp); + + return mcp3422_update_config(adc, config); + + default: + break; + } + + return -EINVAL; +} + +static int mcp3422_write_raw_get_fmt(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, long mask) +{ + switch (mask) { + case IIO_CHAN_INFO_SCALE: + return IIO_VAL_INT_PLUS_NANO; + case IIO_CHAN_INFO_SAMP_FREQ: + return IIO_VAL_INT_PLUS_MICRO; + default: + return -EINVAL; + } +} + +static ssize_t mcp3422_show_samp_freqs(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct mcp3422 *adc = iio_priv(dev_to_iio_dev(dev)); + + if (adc->id > 4) + return sprintf(buf, "240 60 15\n"); + + return sprintf(buf, "240 60 15 3\n"); +} + +static ssize_t mcp3422_show_scales(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct mcp3422 *adc = iio_priv(dev_to_iio_dev(dev)); + u8 sample_rate = FIELD_GET(MCP3422_SRATE_MASK, adc->config); + + return sprintf(buf, "0.%09u 0.%09u 0.%09u 0.%09u\n", + mcp3422_scales[sample_rate][0], + mcp3422_scales[sample_rate][1], + mcp3422_scales[sample_rate][2], + mcp3422_scales[sample_rate][3]); +} + +static IIO_DEVICE_ATTR(sampling_frequency_available, S_IRUGO, + mcp3422_show_samp_freqs, NULL, 0); +static IIO_DEVICE_ATTR(in_voltage_scale_available, S_IRUGO, + mcp3422_show_scales, NULL, 0); + +static struct attribute *mcp3422_attributes[] = { + &iio_dev_attr_sampling_frequency_available.dev_attr.attr, + &iio_dev_attr_in_voltage_scale_available.dev_attr.attr, + NULL, +}; + +static const struct attribute_group mcp3422_attribute_group = { + .attrs = mcp3422_attributes, +}; + +static const struct iio_chan_spec mcp3421_channels[] = { + MCP3422_CHAN(0), +}; + +static const struct iio_chan_spec mcp3422_channels[] = { + MCP3422_CHAN(0), + MCP3422_CHAN(1), +}; + +static const struct iio_chan_spec mcp3424_channels[] = { + MCP3422_CHAN(0), + MCP3422_CHAN(1), + MCP3422_CHAN(2), + MCP3422_CHAN(3), +}; + +static const struct iio_info mcp3422_info = { + .read_raw = mcp3422_read_raw, + .write_raw = mcp3422_write_raw, + .write_raw_get_fmt = mcp3422_write_raw_get_fmt, + .attrs = &mcp3422_attribute_group, +}; + +static int mcp3422_probe(struct i2c_client *client) +{ + const struct i2c_device_id *id = i2c_client_get_device_id(client); + struct iio_dev *indio_dev; + struct mcp3422 *adc; + int err; + u8 config; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) + return -EOPNOTSUPP; + + indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*adc)); + if (!indio_dev) + return -ENOMEM; + + adc = iio_priv(indio_dev); + adc->i2c = client; + adc->id = (u8)(id->driver_data); + + mutex_init(&adc->lock); + + indio_dev->name = dev_name(&client->dev); + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->info = &mcp3422_info; + + switch (adc->id) { + case 1: + case 5: + indio_dev->channels = mcp3421_channels; + indio_dev->num_channels = ARRAY_SIZE(mcp3421_channels); + break; + case 2: + case 3: + case 6: + case 7: + indio_dev->channels = mcp3422_channels; + indio_dev->num_channels = ARRAY_SIZE(mcp3422_channels); + break; + case 4: + case 8: + indio_dev->channels = mcp3424_channels; + indio_dev->num_channels = ARRAY_SIZE(mcp3424_channels); + break; + } + + /* meaningful default configuration */ + config = MCP3422_CONT_SAMPLING | + FIELD_PREP(MCP3422_CHANNEL_MASK, 0) | + FIELD_PREP(MCP3422_PGA_MASK, MCP3422_PGA_1) | + FIELD_PREP(MCP3422_SRATE_MASK, MCP3422_SRATE_240); + err = mcp3422_update_config(adc, config); + if (err < 0) + return err; + + err = devm_iio_device_register(&client->dev, indio_dev); + if (err < 0) + return err; + + i2c_set_clientdata(client, indio_dev); + + return 0; +} + +static const struct i2c_device_id mcp3422_id[] = { + { .name = "mcp3421", .driver_data = 1 }, + { .name = "mcp3422", .driver_data = 2 }, + { .name = "mcp3423", .driver_data = 3 }, + { .name = "mcp3424", .driver_data = 4 }, + { .name = "mcp3425", .driver_data = 5 }, + { .name = "mcp3426", .driver_data = 6 }, + { .name = "mcp3427", .driver_data = 7 }, + { .name = "mcp3428", .driver_data = 8 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, mcp3422_id); + +static const struct of_device_id mcp3422_of_match[] = { + { .compatible = "mcp3422" }, + { } +}; +MODULE_DEVICE_TABLE(of, mcp3422_of_match); + +static struct i2c_driver mcp3422_driver = { + .driver = { + .name = "mcp3422", + .of_match_table = mcp3422_of_match, + }, + .probe = mcp3422_probe, + .id_table = mcp3422_id, +}; +module_i2c_driver(mcp3422_driver); + +MODULE_AUTHOR("Angelo Compagnucci "); +MODULE_DESCRIPTION("Microchip mcp3421/2/3/4/5/6/7/8 driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/adc/mcp3564.c b/drivers/iio/adc/mcp3564.c new file mode 100644 index 000000000..259ac4ebd --- /dev/null +++ b/drivers/iio/adc/mcp3564.c @@ -0,0 +1,1483 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * IIO driver for MCP356X/MCP356XR and MCP346X/MCP346XR series ADC chip family + * + * Copyright (C) 2022-2023 Microchip Technology Inc. and its subsidiaries + * + * Author: Marius Cristea + * + * Datasheet for MCP3561, MCP3562, MCP3564 can be found here: + * https://ww1.microchip.com/downloads/aemDocuments/documents/MSLD/ProductDocuments/DataSheets/MCP3561-2-4-Family-Data-Sheet-DS20006181C.pdf + * Datasheet for MCP3561R, MCP3562R, MCP3564R can be found here: + * https://ww1.microchip.com/downloads/aemDocuments/documents/APID/ProductDocuments/DataSheets/MCP3561_2_4R-Data-Sheet-DS200006391C.pdf + * Datasheet for MCP3461, MCP3462, MCP3464 can be found here: + * https://ww1.microchip.com/downloads/aemDocuments/documents/APID/ProductDocuments/DataSheets/MCP3461-2-4-Two-Four-Eight-Channel-153.6-ksps-Low-Noise-16-Bit-Delta-Sigma-ADC-Data-Sheet-20006180D.pdf + * Datasheet for MCP3461R, MCP3462R, MCP3464R can be found here: + * https://ww1.microchip.com/downloads/aemDocuments/documents/APID/ProductDocuments/DataSheets/MCP3461-2-4R-Family-Data-Sheet-DS20006404C.pdf + */ + +#include +#include +#include +#include +#include +#include + +#include +#include + +#define MCP3564_ADCDATA_REG 0x00 + +#define MCP3564_CONFIG0_REG 0x01 +#define MCP3564_CONFIG0_ADC_MODE_MASK GENMASK(1, 0) +/* Current Source/Sink Selection Bits for Sensor Bias */ +#define MCP3564_CONFIG0_CS_SEL_MASK GENMASK(3, 2) +/* Internal clock is selected and AMCLK is present on the analog master clock output pin */ +#define MCP3564_CONFIG0_USE_INT_CLK_OUTPUT_EN 0x03 +/* Internal clock is selected and no clock output is present on the CLK pin */ +#define MCP3564_CONFIG0_USE_INT_CLK 0x02 +/* External digital clock */ +#define MCP3564_CONFIG0_USE_EXT_CLK 0x01 +/* External digital clock (default) */ +#define MCP3564_CONFIG0_USE_EXT_CLK_DEFAULT 0x00 +#define MCP3564_CONFIG0_CLK_SEL_MASK GENMASK(5, 4) +#define MCP3456_CONFIG0_BIT6_DEFAULT BIT(6) +#define MCP3456_CONFIG0_VREF_MASK BIT(7) + +#define MCP3564_CONFIG1_REG 0x02 +#define MCP3564_CONFIG1_OVERSPL_RATIO_MASK GENMASK(5, 2) + +#define MCP3564_CONFIG2_REG 0x03 +#define MCP3564_CONFIG2_AZ_REF_MASK BIT(1) +#define MCP3564_CONFIG2_AZ_MUX_MASK BIT(2) + +#define MCP3564_CONFIG2_HARDWARE_GAIN_MASK GENMASK(5, 3) +#define MCP3564_DEFAULT_HARDWARE_GAIN 0x01 +#define MCP3564_CONFIG2_BOOST_CURRENT_MASK GENMASK(7, 6) + +#define MCP3564_CONFIG3_REG 0x04 +#define MCP3464_CONFIG3_EN_GAINCAL_MASK BIT(0) +#define MCP3464_CONFIG3_EN_OFFCAL_MASK BIT(1) +#define MCP3464_CONFIG3_EN_CRCCOM_MASK BIT(2) +#define MCP3464_CONFIG3_CRC_FORMAT_MASK BIT(3) +/* + * ADC Output Data Format 32-bit (25-bit right justified data + Channel ID): + * CHID[3:0] + SGN extension (4 bits) + 24-bit ADC data. + * It allows overrange with the SGN extension. + */ +#define MCP3464_CONFIG3_DATA_FMT_32B_WITH_CH_ID 3 +/* + * ADC Output Data Format 32-bit (25-bit right justified data): + * SGN extension (8-bit) + 24-bit ADC data. + * It allows overrange with the SGN extension. + */ +#define MCP3464_CONFIG3_DATA_FMT_32B_SGN_EXT 2 +/* + * ADC Output Data Format 32-bit (24-bit left justified data): + * 24-bit ADC data + 0x00 (8-bit). + * It does not allow overrange (ADC code locked to 0xFFFFFF or 0x800000). + */ +#define MCP3464_CONFIG3_DATA_FMT_32B_LEFT_JUSTIFIED 1 +/* + * ADC Output Data Format 24-bit (default ADC coding): + * 24-bit ADC data. + * It does not allow overrange (ADC code locked to 0xFFFFFF or 0x800000). + */ +#define MCP3464_CONFIG3_DATA_FMT_24B 0 +#define MCP3464_CONFIG3_DATA_FORMAT_MASK GENMASK(5, 4) + +/* Continuous Conversion mode or continuous conversion cycle in SCAN mode. */ +#define MCP3464_CONFIG3_CONV_MODE_CONTINUOUS 3 +/* + * One-shot conversion or one-shot cycle in SCAN mode. It sets ADC_MODE[1:0] to ‘10’ + * (standby) at the end of the conversion or at the end of the conversion cycle in SCAN mode. + */ +#define MCP3464_CONFIG3_CONV_MODE_ONE_SHOT_STANDBY 2 +/* + * One-shot conversion or one-shot cycle in SCAN mode. It sets ADC_MODE[1:0] to ‘0x’ (ADC + * Shutdown) at the end of the conversion or at the end of the conversion cycle in SCAN + * mode (default). + */ +#define MCP3464_CONFIG3_CONV_MODE_ONE_SHOT_SHUTDOWN 0 +#define MCP3464_CONFIG3_CONV_MODE_MASK GENMASK(7, 6) + +#define MCP3564_IRQ_REG 0x05 +#define MCP3464_EN_STP_MASK BIT(0) +#define MCP3464_EN_FASTCMD_MASK BIT(1) +#define MCP3464_IRQ_MODE_0_MASK BIT(2) +#define MCP3464_IRQ_MODE_1_MASK BIT(3) +#define MCP3564_POR_STATUS_MASK BIT(4) +#define MCP3564_CRCCFG_STATUS_MASK BIT(5) +#define MCP3564_DATA_READY_MASK BIT(6) + +#define MCP3564_MUX_REG 0x06 +#define MCP3564_MUX_VIN_P_MASK GENMASK(7, 4) +#define MCP3564_MUX_VIN_N_MASK GENMASK(3, 0) +#define MCP3564_MUX_SET(x, y) (FIELD_PREP(MCP3564_MUX_VIN_P_MASK, (x)) | \ + FIELD_PREP(MCP3564_MUX_VIN_N_MASK, (y))) + +#define MCP3564_SCAN_REG 0x07 +#define MCP3564_SCAN_CH_SEL_MASK GENMASK(15, 0) +#define MCP3564_SCAN_CH_SEL_SET(x) FIELD_PREP(MCP3564_SCAN_CH_SEL_MASK, (x)) +#define MCP3564_SCAN_DELAY_TIME_MASK GENMASK(23, 21) +#define MCP3564_SCAN_DELAY_TIME_SET(x) FIELD_PREP(MCP3564_SCAN_DELAY_TIME_MASK, (x)) +#define MCP3564_SCAN_DEFAULT_VALUE 0 + +#define MCP3564_TIMER_REG 0x08 +#define MCP3564_TIMER_DEFAULT_VALUE 0 + +#define MCP3564_OFFSETCAL_REG 0x09 +#define MCP3564_DEFAULT_OFFSETCAL 0 + +#define MCP3564_GAINCAL_REG 0x0A +#define MCP3564_DEFAULT_GAINCAL 0x00800000 + +#define MCP3564_RESERVED_B_REG 0x0B + +#define MCP3564_RESERVED_C_REG 0x0C +#define MCP3564_C_REG_DEFAULT 0x50 +#define MCP3564R_C_REG_DEFAULT 0x30 + +#define MCP3564_LOCK_REG 0x0D +#define MCP3564_LOCK_WRITE_ACCESS_PASSWORD 0xA5 +#define MCP3564_RESERVED_E_REG 0x0E +#define MCP3564_CRCCFG_REG 0x0F + +#define MCP3564_CMD_HW_ADDR_MASK GENMASK(7, 6) +#define MCP3564_CMD_ADDR_MASK GENMASK(5, 2) + +#define MCP3564_HW_ADDR_MASK GENMASK(1, 0) + +#define MCP3564_FASTCMD_START 0x0A +#define MCP3564_FASTCMD_RESET 0x0E + +#define MCP3461_HW_ID 0x0008 +#define MCP3462_HW_ID 0x0009 +#define MCP3464_HW_ID 0x000B + +#define MCP3561_HW_ID 0x000C +#define MCP3562_HW_ID 0x000D +#define MCP3564_HW_ID 0x000F +#define MCP3564_HW_ID_MASK GENMASK(3, 0) + +#define MCP3564R_INT_VREF_MV 2400 + +#define MCP3564_DATA_READY_TIMEOUT_MS 2000 + +#define MCP3564_MAX_PGA 8 +#define MCP3564_MAX_BURNOUT_IDX 4 +#define MCP3564_MAX_CHANNELS 66 + +enum mcp3564_ids { + mcp3461, + mcp3462, + mcp3464, + mcp3561, + mcp3562, + mcp3564, + mcp3461r, + mcp3462r, + mcp3464r, + mcp3561r, + mcp3562r, + mcp3564r, +}; + +enum mcp3564_delay_time { + MCP3564_NO_DELAY, + MCP3564_DELAY_8_DMCLK, + MCP3564_DELAY_16_DMCLK, + MCP3564_DELAY_32_DMCLK, + MCP3564_DELAY_64_DMCLK, + MCP3564_DELAY_128_DMCLK, + MCP3564_DELAY_256_DMCLK, + MCP3564_DELAY_512_DMCLK +}; + +enum mcp3564_adc_conversion_mode { + MCP3564_ADC_MODE_DEFAULT, + MCP3564_ADC_MODE_SHUTDOWN, + MCP3564_ADC_MODE_STANDBY, + MCP3564_ADC_MODE_CONVERSION +}; + +enum mcp3564_adc_bias_current { + MCP3564_BOOST_CURRENT_x0_50, + MCP3564_BOOST_CURRENT_x0_66, + MCP3564_BOOST_CURRENT_x1_00, + MCP3564_BOOST_CURRENT_x2_00 +}; + +enum mcp3564_burnout { + MCP3564_CONFIG0_CS_SEL_0_0_uA, + MCP3564_CONFIG0_CS_SEL_0_9_uA, + MCP3564_CONFIG0_CS_SEL_3_7_uA, + MCP3564_CONFIG0_CS_SEL_15_uA +}; + +enum mcp3564_channel_names { + MCP3564_CH0, + MCP3564_CH1, + MCP3564_CH2, + MCP3564_CH3, + MCP3564_CH4, + MCP3564_CH5, + MCP3564_CH6, + MCP3564_CH7, + MCP3564_AGND, + MCP3564_AVDD, + MCP3564_RESERVED, /* do not use */ + MCP3564_REFIN_POZ, + MCP3564_REFIN_NEG, + MCP3564_TEMP_DIODE_P, + MCP3564_TEMP_DIODE_M, + MCP3564_INTERNAL_VCM, +}; + +enum mcp3564_oversampling { + MCP3564_OVERSAMPLING_RATIO_32, + MCP3564_OVERSAMPLING_RATIO_64, + MCP3564_OVERSAMPLING_RATIO_128, + MCP3564_OVERSAMPLING_RATIO_256, + MCP3564_OVERSAMPLING_RATIO_512, + MCP3564_OVERSAMPLING_RATIO_1024, + MCP3564_OVERSAMPLING_RATIO_2048, + MCP3564_OVERSAMPLING_RATIO_4096, + MCP3564_OVERSAMPLING_RATIO_8192, + MCP3564_OVERSAMPLING_RATIO_16384, + MCP3564_OVERSAMPLING_RATIO_20480, + MCP3564_OVERSAMPLING_RATIO_24576, + MCP3564_OVERSAMPLING_RATIO_40960, + MCP3564_OVERSAMPLING_RATIO_49152, + MCP3564_OVERSAMPLING_RATIO_81920, + MCP3564_OVERSAMPLING_RATIO_98304 +}; + +static const unsigned int mcp3564_oversampling_avail[] = { + [MCP3564_OVERSAMPLING_RATIO_32] = 32, + [MCP3564_OVERSAMPLING_RATIO_64] = 64, + [MCP3564_OVERSAMPLING_RATIO_128] = 128, + [MCP3564_OVERSAMPLING_RATIO_256] = 256, + [MCP3564_OVERSAMPLING_RATIO_512] = 512, + [MCP3564_OVERSAMPLING_RATIO_1024] = 1024, + [MCP3564_OVERSAMPLING_RATIO_2048] = 2048, + [MCP3564_OVERSAMPLING_RATIO_4096] = 4096, + [MCP3564_OVERSAMPLING_RATIO_8192] = 8192, + [MCP3564_OVERSAMPLING_RATIO_16384] = 16384, + [MCP3564_OVERSAMPLING_RATIO_20480] = 20480, + [MCP3564_OVERSAMPLING_RATIO_24576] = 24576, + [MCP3564_OVERSAMPLING_RATIO_40960] = 40960, + [MCP3564_OVERSAMPLING_RATIO_49152] = 49152, + [MCP3564_OVERSAMPLING_RATIO_81920] = 81920, + [MCP3564_OVERSAMPLING_RATIO_98304] = 98304 +}; + +/* + * Current Source/Sink Selection Bits for Sensor Bias (source on VIN+/sink on VIN-) + */ +static const int mcp3564_burnout_avail[][2] = { + [MCP3564_CONFIG0_CS_SEL_0_0_uA] = { 0, 0 }, + [MCP3564_CONFIG0_CS_SEL_0_9_uA] = { 0, 900 }, + [MCP3564_CONFIG0_CS_SEL_3_7_uA] = { 0, 3700 }, + [MCP3564_CONFIG0_CS_SEL_15_uA] = { 0, 15000 } +}; + +/* + * BOOST[1:0]: ADC Bias Current Selection + */ +static const char * const mcp3564_boost_current_avail[] = { + [MCP3564_BOOST_CURRENT_x0_50] = "0.5", + [MCP3564_BOOST_CURRENT_x0_66] = "0.66", + [MCP3564_BOOST_CURRENT_x1_00] = "1", + [MCP3564_BOOST_CURRENT_x2_00] = "2", +}; + +/* + * Calibration bias values + */ +static const int mcp3564_calib_bias[] = { + -8388608, /* min: -2^23 */ + 1, /* step: 1 */ + 8388607 /* max: 2^23 - 1 */ +}; + +/* + * Calibration scale values + * The Gain Error Calibration register (GAINCAL) is an + * unsigned 24-bit register that holds the digital gain error + * calibration value, GAINCAL which could be calculated by + * GAINCAL (V/V) = (GAINCAL[23:0])/8388608 + * The gain error calibration value range in equivalent voltage is [0; 2-2^(-23)] + */ +static const unsigned int mcp3564_calib_scale[] = { + 0, /* min: 0 */ + 1, /* step: 1/8388608 */ + 16777215 /* max: 2 - 2^(-23) */ +}; + +/* Programmable hardware gain x1/3, x1, x2, x4, x8, x16, x32, x64 */ +static const int mcp3564_hwgain_frac[] = { + 3, 10, + 1, 1, + 2, 1, + 4, 1, + 8, 1, + 16, 1, + 32, 1, + 64, 1 +}; + +static const char *mcp3564_channel_labels[2] = { + "burnout_current", "temperature", +}; + +/** + * struct mcp3564_chip_info - chip specific data + * @name: device name + * @num_channels: number of channels + * @resolution: ADC resolution + * @have_vref: does the hardware have an internal voltage reference? + */ +struct mcp3564_chip_info { + const char *name; + unsigned int num_channels; + unsigned int resolution; + bool have_vref; +}; + +/** + * struct mcp3564_state - working data for a ADC device + * @chip_info: chip specific data + * @spi: SPI device structure + * @vref_mv: voltage reference value in millivolts + * @lock: synchronize access to driver's state members + * @dev_addr: hardware device address + * @oversampling: the index inside oversampling list of the ADC + * @hwgain: the index inside hardware gain list of the ADC + * @scale_tbls: table with precalculated scale + * @calib_bias: calibration bias value + * @calib_scale: calibration scale value + * @current_boost_mode: the index inside current boost list of the ADC + * @burnout_mode: the index inside current bias list of the ADC + * @auto_zeroing_mux: set if ADC auto-zeroing algorithm is enabled + * @auto_zeroing_ref: set if ADC auto-Zeroing Reference Buffer Setting is enabled + * @have_vref: does the ADC have an internal voltage reference? + * @labels: table with channels labels + */ +struct mcp3564_state { + const struct mcp3564_chip_info *chip_info; + struct spi_device *spi; + unsigned short vref_mv; + struct mutex lock; /* Synchronize access to driver's state members */ + u8 dev_addr; + enum mcp3564_oversampling oversampling; + unsigned int hwgain; + unsigned int scale_tbls[MCP3564_MAX_PGA][2]; + int calib_bias; + int calib_scale; + unsigned int current_boost_mode; + enum mcp3564_burnout burnout_mode; + bool auto_zeroing_mux; + bool auto_zeroing_ref; + bool have_vref; + const char *labels[MCP3564_MAX_CHANNELS]; +}; + +static inline u8 mcp3564_cmd_write(u8 chip_addr, u8 reg) +{ + return FIELD_PREP(MCP3564_CMD_HW_ADDR_MASK, chip_addr) | + FIELD_PREP(MCP3564_CMD_ADDR_MASK, reg) | + BIT(1); +} + +static inline u8 mcp3564_cmd_read(u8 chip_addr, u8 reg) +{ + return FIELD_PREP(MCP3564_CMD_HW_ADDR_MASK, chip_addr) | + FIELD_PREP(MCP3564_CMD_ADDR_MASK, reg) | + BIT(0); +} + +static int mcp3564_read_8bits(struct mcp3564_state *adc, u8 reg, u8 *val) +{ + int ret; + u8 tx_buf; + u8 rx_buf; + + tx_buf = mcp3564_cmd_read(adc->dev_addr, reg); + + ret = spi_write_then_read(adc->spi, &tx_buf, sizeof(tx_buf), + &rx_buf, sizeof(rx_buf)); + *val = rx_buf; + + return ret; +} + +static int mcp3564_read_16bits(struct mcp3564_state *adc, u8 reg, u16 *val) +{ + int ret; + u8 tx_buf; + __be16 rx_buf; + + tx_buf = mcp3564_cmd_read(adc->dev_addr, reg); + + ret = spi_write_then_read(adc->spi, &tx_buf, sizeof(tx_buf), + &rx_buf, sizeof(rx_buf)); + *val = be16_to_cpu(rx_buf); + + return ret; +} + +static int mcp3564_read_32bits(struct mcp3564_state *adc, u8 reg, u32 *val) +{ + int ret; + u8 tx_buf; + __be32 rx_buf; + + tx_buf = mcp3564_cmd_read(adc->dev_addr, reg); + + ret = spi_write_then_read(adc->spi, &tx_buf, sizeof(tx_buf), + &rx_buf, sizeof(rx_buf)); + *val = be32_to_cpu(rx_buf); + + return ret; +} + +static int mcp3564_write_8bits(struct mcp3564_state *adc, u8 reg, u8 val) +{ + u8 tx_buf[2]; + + tx_buf[0] = mcp3564_cmd_write(adc->dev_addr, reg); + tx_buf[1] = val; + + return spi_write_then_read(adc->spi, tx_buf, sizeof(tx_buf), NULL, 0); +} + +static int mcp3564_write_24bits(struct mcp3564_state *adc, u8 reg, u32 val) +{ + __be32 val_be; + + val |= (mcp3564_cmd_write(adc->dev_addr, reg) << 24); + val_be = cpu_to_be32(val); + + return spi_write_then_read(adc->spi, &val_be, sizeof(val_be), NULL, 0); +} + +static int mcp3564_fast_cmd(struct mcp3564_state *adc, u8 fast_cmd) +{ + u8 val; + + val = FIELD_PREP(MCP3564_CMD_HW_ADDR_MASK, adc->dev_addr) | + FIELD_PREP(MCP3564_CMD_ADDR_MASK, fast_cmd); + + return spi_write_then_read(adc->spi, &val, 1, NULL, 0); +} + +static int mcp3564_update_8bits(struct mcp3564_state *adc, u8 reg, u32 mask, u8 val) +{ + u8 tmp; + int ret; + + val &= mask; + + ret = mcp3564_read_8bits(adc, reg, &tmp); + if (ret < 0) + return ret; + + tmp &= ~mask; + tmp |= val; + + return mcp3564_write_8bits(adc, reg, tmp); +} + +static int mcp3564_set_current_boost_mode(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + unsigned int mode) +{ + struct mcp3564_state *adc = iio_priv(indio_dev); + int ret; + + dev_dbg(&indio_dev->dev, "%s: %d\n", __func__, mode); + + mutex_lock(&adc->lock); + ret = mcp3564_update_8bits(adc, MCP3564_CONFIG2_REG, MCP3564_CONFIG2_BOOST_CURRENT_MASK, + FIELD_PREP(MCP3564_CONFIG2_BOOST_CURRENT_MASK, mode)); + + if (ret) + dev_err(&indio_dev->dev, "Failed to configure CONFIG2 register\n"); + else + adc->current_boost_mode = mode; + + mutex_unlock(&adc->lock); + + return ret; +} + +static int mcp3564_get_current_boost_mode(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan) +{ + struct mcp3564_state *adc = iio_priv(indio_dev); + + return adc->current_boost_mode; +} + +static const struct iio_enum mcp3564_current_boost_mode_enum = { + .items = mcp3564_boost_current_avail, + .num_items = ARRAY_SIZE(mcp3564_boost_current_avail), + .set = mcp3564_set_current_boost_mode, + .get = mcp3564_get_current_boost_mode, +}; + +static const struct iio_chan_spec_ext_info mcp3564_ext_info[] = { + IIO_ENUM("boost_current_gain", IIO_SHARED_BY_ALL, &mcp3564_current_boost_mode_enum), + { + .name = "boost_current_gain_available", + .shared = IIO_SHARED_BY_ALL, + .read = iio_enum_available_read, + .private = (uintptr_t)&mcp3564_current_boost_mode_enum, + }, + { } +}; + +static ssize_t mcp3564_auto_zeroing_mux_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct iio_dev *indio_dev = dev_to_iio_dev(dev); + struct mcp3564_state *adc = iio_priv(indio_dev); + + return sysfs_emit(buf, "%d\n", adc->auto_zeroing_mux); +} + +static ssize_t mcp3564_auto_zeroing_mux_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t len) +{ + struct iio_dev *indio_dev = dev_to_iio_dev(dev); + struct mcp3564_state *adc = iio_priv(indio_dev); + bool auto_zero; + int ret; + + ret = kstrtobool(buf, &auto_zero); + if (ret) + return ret; + + mutex_lock(&adc->lock); + ret = mcp3564_update_8bits(adc, MCP3564_CONFIG2_REG, MCP3564_CONFIG2_AZ_MUX_MASK, + FIELD_PREP(MCP3564_CONFIG2_AZ_MUX_MASK, auto_zero)); + + if (ret) + dev_err(&indio_dev->dev, "Failed to update CONFIG2 register\n"); + else + adc->auto_zeroing_mux = auto_zero; + + mutex_unlock(&adc->lock); + + return ret ? ret : len; +} + +static ssize_t mcp3564_auto_zeroing_ref_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct iio_dev *indio_dev = dev_to_iio_dev(dev); + struct mcp3564_state *adc = iio_priv(indio_dev); + + return sysfs_emit(buf, "%d\n", adc->auto_zeroing_ref); +} + +static ssize_t mcp3564_auto_zeroing_ref_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t len) +{ + struct iio_dev *indio_dev = dev_to_iio_dev(dev); + struct mcp3564_state *adc = iio_priv(indio_dev); + bool auto_zero; + int ret; + + ret = kstrtobool(buf, &auto_zero); + if (ret) + return ret; + + mutex_lock(&adc->lock); + ret = mcp3564_update_8bits(adc, MCP3564_CONFIG2_REG, MCP3564_CONFIG2_AZ_REF_MASK, + FIELD_PREP(MCP3564_CONFIG2_AZ_REF_MASK, auto_zero)); + + if (ret) + dev_err(&indio_dev->dev, "Failed to update CONFIG2 register\n"); + else + adc->auto_zeroing_ref = auto_zero; + + mutex_unlock(&adc->lock); + + return ret ? ret : len; +} + +static const struct iio_chan_spec mcp3564_channel_template = { + .type = IIO_VOLTAGE, + .indexed = 1, + .differential = 1, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), + .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SCALE) | + BIT(IIO_CHAN_INFO_CALIBSCALE) | + BIT(IIO_CHAN_INFO_CALIBBIAS) | + BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), + .info_mask_shared_by_all_available = BIT(IIO_CHAN_INFO_SCALE) | + BIT(IIO_CHAN_INFO_CALIBSCALE) | + BIT(IIO_CHAN_INFO_CALIBBIAS) | + BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), + .ext_info = mcp3564_ext_info, +}; + +static const struct iio_chan_spec mcp3564_temp_channel_template = { + .type = IIO_TEMP, + .channel = 0, + .address = ((MCP3564_TEMP_DIODE_P << 4) | MCP3564_TEMP_DIODE_M), + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), + .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SCALE) | + BIT(IIO_CHAN_INFO_CALIBSCALE) | + BIT(IIO_CHAN_INFO_CALIBBIAS) | + BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), + .info_mask_shared_by_all_available = BIT(IIO_CHAN_INFO_SCALE) | + BIT(IIO_CHAN_INFO_CALIBSCALE) | + BIT(IIO_CHAN_INFO_CALIBBIAS) | + BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), +}; + +static const struct iio_chan_spec mcp3564_burnout_channel_template = { + .type = IIO_CURRENT, + .output = true, + .channel = 0, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), + .info_mask_separate_available = BIT(IIO_CHAN_INFO_RAW), +}; + +/* + * Number of channels could be calculated: + * num_channels = single_ended_input + differential_input + temperature + burnout + * Eg. for MCP3561 (only 2 channels available: CH0 and CH1) + * single_ended_input = (CH0 - GND), (CH1 - GND) = 2 + * differential_input = (CH0 - CH1), (CH0 - CH0) = 2 + * num_channels = 2 + 2 + 2 + * Generic formula is: + * num_channels = P^R(Number_of_single_ended_channels, 2) + 2 (temperature + burnout channels) + * P^R(Number_of_single_ended_channels, 2) is Permutations with Replacement of + * Number_of_single_ended_channels taken by 2 + */ +static const struct mcp3564_chip_info mcp3564_chip_infos_tbl[] = { + [mcp3461] = { + .name = "mcp3461", + .num_channels = 6, + .resolution = 16, + .have_vref = false, + }, + [mcp3462] = { + .name = "mcp3462", + .num_channels = 18, + .resolution = 16, + .have_vref = false, + }, + [mcp3464] = { + .name = "mcp3464", + .num_channels = 66, + .resolution = 16, + .have_vref = false, + }, + [mcp3561] = { + .name = "mcp3561", + .num_channels = 6, + .resolution = 24, + .have_vref = false, + }, + [mcp3562] = { + .name = "mcp3562", + .num_channels = 18, + .resolution = 24, + .have_vref = false, + }, + [mcp3564] = { + .name = "mcp3564", + .num_channels = 66, + .resolution = 24, + .have_vref = false, + }, + [mcp3461r] = { + .name = "mcp3461r", + .num_channels = 6, + .resolution = 16, + .have_vref = false, + }, + [mcp3462r] = { + .name = "mcp3462r", + .num_channels = 18, + .resolution = 16, + .have_vref = true, + }, + [mcp3464r] = { + .name = "mcp3464r", + .num_channels = 66, + .resolution = 16, + .have_vref = true, + }, + [mcp3561r] = { + .name = "mcp3561r", + .num_channels = 6, + .resolution = 24, + .have_vref = true, + }, + [mcp3562r] = { + .name = "mcp3562r", + .num_channels = 18, + .resolution = 24, + .have_vref = true, + }, + [mcp3564r] = { + .name = "mcp3564r", + .num_channels = 66, + .resolution = 24, + .have_vref = true, + }, +}; + +static int mcp3564_read_single_value(struct iio_dev *indio_dev, + struct iio_chan_spec const *channel, + int *val) +{ + struct mcp3564_state *adc = iio_priv(indio_dev); + int ret; + u8 tmp; + int ret_read = 0; + + ret = mcp3564_write_8bits(adc, MCP3564_MUX_REG, channel->address); + if (ret) + return ret; + + /* Start ADC Conversion using fast command (overwrites ADC_MODE[1:0] = 11) */ + ret = mcp3564_fast_cmd(adc, MCP3564_FASTCMD_START); + if (ret) + return ret; + + /* + * Check if the conversion is ready. If not, wait a little bit, and + * in case of timeout exit with an error. + */ + ret = read_poll_timeout(mcp3564_read_8bits, ret_read, + ret_read || !(tmp & MCP3564_DATA_READY_MASK), + 20000, MCP3564_DATA_READY_TIMEOUT_MS * 1000, true, + adc, MCP3564_IRQ_REG, &tmp); + + /* failed to read status register */ + if (ret_read) + return ret_read; + + if (ret) + return ret; + + if (tmp & MCP3564_DATA_READY_MASK) + /* failing to finish conversion */ + return -EBUSY; + + return mcp3564_read_32bits(adc, MCP3564_ADCDATA_REG, val); +} + +static int mcp3564_read_avail(struct iio_dev *indio_dev, + struct iio_chan_spec const *channel, + const int **vals, int *type, + int *length, long mask) +{ + struct mcp3564_state *adc = iio_priv(indio_dev); + + switch (mask) { + case IIO_CHAN_INFO_RAW: + if (!channel->output) + return -EINVAL; + + *vals = mcp3564_burnout_avail[0]; + *length = ARRAY_SIZE(mcp3564_burnout_avail) * 2; + *type = IIO_VAL_INT_PLUS_MICRO; + return IIO_AVAIL_LIST; + case IIO_CHAN_INFO_OVERSAMPLING_RATIO: + *vals = mcp3564_oversampling_avail; + *length = ARRAY_SIZE(mcp3564_oversampling_avail); + *type = IIO_VAL_INT; + return IIO_AVAIL_LIST; + case IIO_CHAN_INFO_SCALE: + *vals = (int *)adc->scale_tbls; + *length = ARRAY_SIZE(adc->scale_tbls) * 2; + *type = IIO_VAL_INT_PLUS_NANO; + return IIO_AVAIL_LIST; + case IIO_CHAN_INFO_CALIBBIAS: + *vals = mcp3564_calib_bias; + *type = IIO_VAL_INT; + return IIO_AVAIL_RANGE; + case IIO_CHAN_INFO_CALIBSCALE: + *vals = mcp3564_calib_scale; + *type = IIO_VAL_INT; + return IIO_AVAIL_RANGE; + default: + return -EINVAL; + } +} + +static int mcp3564_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *channel, + int *val, int *val2, long mask) +{ + struct mcp3564_state *adc = iio_priv(indio_dev); + int ret; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + if (channel->output) { + mutex_lock(&adc->lock); + *val = mcp3564_burnout_avail[adc->burnout_mode][0]; + *val2 = mcp3564_burnout_avail[adc->burnout_mode][1]; + mutex_unlock(&adc->lock); + return IIO_VAL_INT_PLUS_MICRO; + } + + ret = mcp3564_read_single_value(indio_dev, channel, val); + if (ret) + return -EINVAL; + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + mutex_lock(&adc->lock); + *val = adc->scale_tbls[adc->hwgain][0]; + *val2 = adc->scale_tbls[adc->hwgain][1]; + mutex_unlock(&adc->lock); + return IIO_VAL_INT_PLUS_NANO; + case IIO_CHAN_INFO_OVERSAMPLING_RATIO: + *val = mcp3564_oversampling_avail[adc->oversampling]; + return IIO_VAL_INT; + case IIO_CHAN_INFO_CALIBBIAS: + *val = adc->calib_bias; + return IIO_VAL_INT; + case IIO_CHAN_INFO_CALIBSCALE: + *val = adc->calib_scale; + return IIO_VAL_INT; + default: + return -EINVAL; + } +} + +static int mcp3564_write_raw_get_fmt(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + long info) +{ + switch (info) { + case IIO_CHAN_INFO_RAW: + return IIO_VAL_INT_PLUS_MICRO; + case IIO_CHAN_INFO_CALIBBIAS: + case IIO_CHAN_INFO_CALIBSCALE: + case IIO_CHAN_INFO_OVERSAMPLING_RATIO: + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + return IIO_VAL_INT_PLUS_NANO; + default: + return -EINVAL; + } +} + +static int mcp3564_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *channel, int val, + int val2, long mask) +{ + struct mcp3564_state *adc = iio_priv(indio_dev); + int tmp; + unsigned int hwgain; + enum mcp3564_burnout burnout; + int ret = 0; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + if (!channel->output) + return -EINVAL; + + for (burnout = 0; burnout < MCP3564_MAX_BURNOUT_IDX; burnout++) + if (val == mcp3564_burnout_avail[burnout][0] && + val2 == mcp3564_burnout_avail[burnout][1]) + break; + + if (burnout == MCP3564_MAX_BURNOUT_IDX) + return -EINVAL; + + if (burnout == adc->burnout_mode) + return ret; + + mutex_lock(&adc->lock); + ret = mcp3564_update_8bits(adc, MCP3564_CONFIG0_REG, + MCP3564_CONFIG0_CS_SEL_MASK, + FIELD_PREP(MCP3564_CONFIG0_CS_SEL_MASK, burnout)); + + if (ret) + dev_err(&indio_dev->dev, "Failed to configure burnout current\n"); + else + adc->burnout_mode = burnout; + mutex_unlock(&adc->lock); + return ret; + case IIO_CHAN_INFO_CALIBBIAS: + if (val < mcp3564_calib_bias[0] || val > mcp3564_calib_bias[2]) + return -EINVAL; + + mutex_lock(&adc->lock); + ret = mcp3564_write_24bits(adc, MCP3564_OFFSETCAL_REG, val); + if (!ret) + adc->calib_bias = val; + mutex_unlock(&adc->lock); + return ret; + case IIO_CHAN_INFO_CALIBSCALE: + if (val < mcp3564_calib_scale[0] || val > mcp3564_calib_scale[2]) + return -EINVAL; + + if (adc->calib_scale == val) + return ret; + + mutex_lock(&adc->lock); + ret = mcp3564_write_24bits(adc, MCP3564_GAINCAL_REG, val); + if (!ret) + adc->calib_scale = val; + mutex_unlock(&adc->lock); + return ret; + case IIO_CHAN_INFO_OVERSAMPLING_RATIO: + if (val < 0) + return -EINVAL; + + tmp = find_closest(val, mcp3564_oversampling_avail, + ARRAY_SIZE(mcp3564_oversampling_avail)); + + if (adc->oversampling == tmp) + return ret; + + mutex_lock(&adc->lock); + ret = mcp3564_update_8bits(adc, MCP3564_CONFIG1_REG, + MCP3564_CONFIG1_OVERSPL_RATIO_MASK, + FIELD_PREP(MCP3564_CONFIG1_OVERSPL_RATIO_MASK, + adc->oversampling)); + if (!ret) + adc->oversampling = tmp; + mutex_unlock(&adc->lock); + return ret; + case IIO_CHAN_INFO_SCALE: + for (hwgain = 0; hwgain < MCP3564_MAX_PGA; hwgain++) + if (val == adc->scale_tbls[hwgain][0] && + val2 == adc->scale_tbls[hwgain][1]) + break; + + if (hwgain == MCP3564_MAX_PGA) + return -EINVAL; + + if (hwgain == adc->hwgain) + return ret; + + mutex_lock(&adc->lock); + ret = mcp3564_update_8bits(adc, MCP3564_CONFIG2_REG, + MCP3564_CONFIG2_HARDWARE_GAIN_MASK, + FIELD_PREP(MCP3564_CONFIG2_HARDWARE_GAIN_MASK, hwgain)); + if (!ret) + adc->hwgain = hwgain; + + mutex_unlock(&adc->lock); + return ret; + default: + return -EINVAL; + } +} + +static int mcp3564_read_label(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, char *label) +{ + struct mcp3564_state *adc = iio_priv(indio_dev); + + return sysfs_emit(label, "%s\n", adc->labels[chan->scan_index]); +} + +static int mcp3564_parse_fw_children(struct iio_dev *indio_dev) +{ + struct mcp3564_state *adc = iio_priv(indio_dev); + struct device *dev = &adc->spi->dev; + struct iio_chan_spec *channels; + struct iio_chan_spec chanspec = mcp3564_channel_template; + struct iio_chan_spec temp_chanspec = mcp3564_temp_channel_template; + struct iio_chan_spec burnout_chanspec = mcp3564_burnout_channel_template; + int chan_idx = 0; + unsigned int num_ch; + u32 inputs[2]; + const char *node_name; + const char *label; + int ret; + + num_ch = device_get_child_node_count(dev); + if (num_ch == 0) + return dev_err_probe(&indio_dev->dev, -ENODEV, + "FW has no channels defined\n"); + + /* Reserve space for burnout and temperature channel */ + num_ch += 2; + + if (num_ch > adc->chip_info->num_channels) + return dev_err_probe(dev, -EINVAL, "Too many channels %d > %d\n", + num_ch, adc->chip_info->num_channels); + + channels = devm_kcalloc(dev, num_ch, sizeof(*channels), GFP_KERNEL); + if (!channels) + return -ENOMEM; + + device_for_each_child_node_scoped(dev, child) { + node_name = fwnode_get_name(child); + + if (fwnode_property_present(child, "diff-channels")) { + ret = fwnode_property_read_u32_array(child, + "diff-channels", + inputs, + ARRAY_SIZE(inputs)); + if (ret) + return ret; + + chanspec.differential = 1; + } else { + ret = fwnode_property_read_u32(child, "reg", &inputs[0]); + if (ret) + return ret; + + chanspec.differential = 0; + inputs[1] = MCP3564_AGND; + } + + if (inputs[0] > MCP3564_INTERNAL_VCM || + inputs[1] > MCP3564_INTERNAL_VCM) + return dev_err_probe(&indio_dev->dev, -EINVAL, + "Channel index > %d, for %s\n", + MCP3564_INTERNAL_VCM + 1, + node_name); + + chanspec.address = (inputs[0] << 4) | inputs[1]; + chanspec.channel = inputs[0]; + chanspec.channel2 = inputs[1]; + chanspec.scan_index = chan_idx; + + if (fwnode_property_present(child, "label")) { + fwnode_property_read_string(child, "label", &label); + adc->labels[chan_idx] = label; + } + + channels[chan_idx] = chanspec; + chan_idx++; + } + + /* Add burnout current channel */ + burnout_chanspec.scan_index = chan_idx; + channels[chan_idx] = burnout_chanspec; + adc->labels[chan_idx] = mcp3564_channel_labels[0]; + chanspec.scan_index = chan_idx; + chan_idx++; + + /* Add temperature channel */ + temp_chanspec.scan_index = chan_idx; + channels[chan_idx] = temp_chanspec; + adc->labels[chan_idx] = mcp3564_channel_labels[1]; + chan_idx++; + + indio_dev->num_channels = chan_idx; + indio_dev->channels = channels; + + return 0; +} + +static void mcp3564_fill_scale_tbls(struct mcp3564_state *adc) +{ + unsigned int pow = adc->chip_info->resolution - 1; + int ref; + unsigned int i; + int tmp0; + u64 tmp1; + + for (i = 0; i < MCP3564_MAX_PGA; i++) { + ref = adc->vref_mv; + tmp1 = ((u64)ref * NANO) >> pow; + div_u64_rem(tmp1, NANO, &tmp0); + + tmp1 = tmp1 * mcp3564_hwgain_frac[(2 * i) + 1]; + tmp0 = (int)div_u64(tmp1, mcp3564_hwgain_frac[2 * i]); + + adc->scale_tbls[i][1] = tmp0; + } +} + +static int mcp3564_config(struct iio_dev *indio_dev, bool *use_internal_vref_attr) +{ + struct mcp3564_state *adc = iio_priv(indio_dev); + struct device *dev = &adc->spi->dev; + u8 tmp_reg; + u16 tmp_u16; + enum mcp3564_ids ids; + int ret = 0; + unsigned int tmp = 0x01; + bool internal_vref; + bool err = false; + + /* + * The address is set on a per-device basis by fuses in the factory, + * configured on request. If not requested, the fuses are set for 0x1. + * The device address is part of the device markings to avoid + * potential confusion. This address is coded on two bits, so four possible + * addresses are available when multiple devices are present on the same + * SPI bus with only one Chip Select line for all devices. + */ + device_property_read_u32(dev, "microchip,hw-device-address", &tmp); + + if (tmp > 3) { + dev_err_probe(dev, tmp, + "invalid device address. Must be in range 0-3.\n"); + return -EINVAL; + } + + adc->dev_addr = FIELD_GET(MCP3564_HW_ADDR_MASK, tmp); + + dev_dbg(dev, "use HW device address %i\n", adc->dev_addr); + + ret = mcp3564_read_8bits(adc, MCP3564_RESERVED_C_REG, &tmp_reg); + if (ret < 0) + return ret; + + switch (tmp_reg) { + case MCP3564_C_REG_DEFAULT: + adc->have_vref = false; + break; + case MCP3564R_C_REG_DEFAULT: + adc->have_vref = true; + break; + default: + dev_info(dev, "Unknown chip found: %d\n", tmp_reg); + err = true; + } + + if (!err) { + ret = mcp3564_read_16bits(adc, MCP3564_RESERVED_E_REG, &tmp_u16); + if (ret < 0) + return ret; + + switch (tmp_u16 & MCP3564_HW_ID_MASK) { + case MCP3461_HW_ID: + if (adc->have_vref) + ids = mcp3461r; + else + ids = mcp3461; + break; + case MCP3462_HW_ID: + if (adc->have_vref) + ids = mcp3462r; + else + ids = mcp3462; + break; + case MCP3464_HW_ID: + if (adc->have_vref) + ids = mcp3464r; + else + ids = mcp3464; + break; + case MCP3561_HW_ID: + if (adc->have_vref) + ids = mcp3561r; + else + ids = mcp3561; + break; + case MCP3562_HW_ID: + if (adc->have_vref) + ids = mcp3562r; + else + ids = mcp3562; + break; + case MCP3564_HW_ID: + if (adc->have_vref) + ids = mcp3564r; + else + ids = mcp3564; + break; + default: + dev_info(dev, "Unknown chip found: %d\n", tmp_u16); + err = true; + } + } + + if (err) { + /* + * If failed to identify the hardware based on internal registers, + * try using fallback compatible in device tree to deal with some newer part number. + */ + adc->chip_info = spi_get_device_match_data(adc->spi); + adc->have_vref = adc->chip_info->have_vref; + } else { + adc->chip_info = &mcp3564_chip_infos_tbl[ids]; + } + + dev_dbg(dev, "Found %s chip\n", adc->chip_info->name); + + ret = devm_regulator_get_enable_read_voltage(dev, "vref"); + if (ret < 0 && ret != -ENODEV) + return dev_err_probe(dev, ret, "Failed to get vref voltage\n"); + + internal_vref = ret == -ENODEV; + adc->vref_mv = internal_vref ? MCP3564R_INT_VREF_MV : ret / MILLI; + *use_internal_vref_attr = internal_vref; + + if (internal_vref) { + /* Check if chip has internal vref */ + if (!adc->have_vref) + return dev_err_probe(dev, -ENODEV, "Unknown Vref\n"); + + dev_dbg(dev, "%s: Using internal Vref\n", __func__); + } else { + dev_dbg(dev, "%s: Using External Vref\n", __func__); + } + + ret = mcp3564_parse_fw_children(indio_dev); + if (ret) + return ret; + + /* + * Command sequence that ensures a recovery with the desired settings + * in any cases of loss-of-power scenario (Full Chip Reset): + * - Write LOCK register to 0xA5 + * - Write IRQ register to 0x03 + * - Send "Device Full Reset" fast command + * - Wait 1ms for "Full Reset" to complete + */ + ret = mcp3564_write_8bits(adc, MCP3564_LOCK_REG, MCP3564_LOCK_WRITE_ACCESS_PASSWORD); + if (ret) + return ret; + + ret = mcp3564_write_8bits(adc, MCP3564_IRQ_REG, 0x03); + if (ret) + return ret; + + ret = mcp3564_fast_cmd(adc, MCP3564_FASTCMD_RESET); + if (ret) + return ret; + + /* + * After Full reset wait some time to be able to fully reset the part and place + * it back in a default configuration. + * From datasheet: POR (Power On Reset Time) is ~1us + * 1ms should be enough. + */ + mdelay(1); + + /* set a gain of 1x for GAINCAL */ + ret = mcp3564_write_24bits(adc, MCP3564_GAINCAL_REG, MCP3564_DEFAULT_GAINCAL); + if (ret) + return ret; + + adc->calib_scale = MCP3564_DEFAULT_GAINCAL; + + ret = mcp3564_write_24bits(adc, MCP3564_OFFSETCAL_REG, MCP3564_DEFAULT_OFFSETCAL); + if (ret) + return ret; + + ret = mcp3564_write_24bits(adc, MCP3564_TIMER_REG, MCP3564_TIMER_DEFAULT_VALUE); + if (ret) + return ret; + + ret = mcp3564_write_24bits(adc, MCP3564_SCAN_REG, + MCP3564_SCAN_DELAY_TIME_SET(MCP3564_NO_DELAY) | + MCP3564_SCAN_CH_SEL_SET(MCP3564_SCAN_DEFAULT_VALUE)); + if (ret) + return ret; + + ret = mcp3564_write_8bits(adc, MCP3564_MUX_REG, MCP3564_MUX_SET(MCP3564_CH0, MCP3564_CH1)); + if (ret) + return ret; + + ret = mcp3564_write_8bits(adc, MCP3564_IRQ_REG, + FIELD_PREP(MCP3464_EN_FASTCMD_MASK, 1) | + FIELD_PREP(MCP3464_EN_STP_MASK, 1)); + if (ret) + return ret; + + tmp_reg = FIELD_PREP(MCP3464_CONFIG3_CONV_MODE_MASK, + MCP3464_CONFIG3_CONV_MODE_ONE_SHOT_STANDBY); + tmp_reg |= FIELD_PREP(MCP3464_CONFIG3_DATA_FORMAT_MASK, + MCP3464_CONFIG3_DATA_FMT_32B_SGN_EXT); + tmp_reg |= MCP3464_CONFIG3_EN_OFFCAL_MASK; + tmp_reg |= MCP3464_CONFIG3_EN_GAINCAL_MASK; + + ret = mcp3564_write_8bits(adc, MCP3564_CONFIG3_REG, tmp_reg); + if (ret) + return ret; + + tmp_reg = FIELD_PREP(MCP3564_CONFIG2_BOOST_CURRENT_MASK, MCP3564_BOOST_CURRENT_x1_00); + tmp_reg |= FIELD_PREP(MCP3564_CONFIG2_HARDWARE_GAIN_MASK, 0x01); + tmp_reg |= FIELD_PREP(MCP3564_CONFIG2_AZ_MUX_MASK, 1); + + ret = mcp3564_write_8bits(adc, MCP3564_CONFIG2_REG, tmp_reg); + if (ret) + return ret; + + adc->hwgain = 0x01; + adc->auto_zeroing_mux = true; + adc->auto_zeroing_ref = false; + adc->current_boost_mode = MCP3564_BOOST_CURRENT_x1_00; + + tmp_reg = FIELD_PREP(MCP3564_CONFIG1_OVERSPL_RATIO_MASK, MCP3564_OVERSAMPLING_RATIO_98304); + + ret = mcp3564_write_8bits(adc, MCP3564_CONFIG1_REG, tmp_reg); + if (ret) + return ret; + + adc->oversampling = MCP3564_OVERSAMPLING_RATIO_98304; + + tmp_reg = FIELD_PREP(MCP3564_CONFIG0_ADC_MODE_MASK, MCP3564_ADC_MODE_STANDBY); + tmp_reg |= FIELD_PREP(MCP3564_CONFIG0_CS_SEL_MASK, MCP3564_CONFIG0_CS_SEL_0_0_uA); + tmp_reg |= FIELD_PREP(MCP3564_CONFIG0_CLK_SEL_MASK, MCP3564_CONFIG0_USE_INT_CLK); + tmp_reg |= MCP3456_CONFIG0_BIT6_DEFAULT; + + if (internal_vref) + tmp_reg |= FIELD_PREP(MCP3456_CONFIG0_VREF_MASK, 1); + + ret = mcp3564_write_8bits(adc, MCP3564_CONFIG0_REG, tmp_reg); + + adc->burnout_mode = MCP3564_CONFIG0_CS_SEL_0_0_uA; + + return ret; +} + +static IIO_DEVICE_ATTR(auto_zeroing_ref_enable, 0644, + mcp3564_auto_zeroing_ref_show, + mcp3564_auto_zeroing_ref_store, 0); + +static IIO_DEVICE_ATTR(auto_zeroing_mux_enable, 0644, + mcp3564_auto_zeroing_mux_show, + mcp3564_auto_zeroing_mux_store, 0); + +static struct attribute *mcp3564_attributes[] = { + &iio_dev_attr_auto_zeroing_mux_enable.dev_attr.attr, + NULL +}; + +static struct attribute *mcp3564r_attributes[] = { + &iio_dev_attr_auto_zeroing_mux_enable.dev_attr.attr, + &iio_dev_attr_auto_zeroing_ref_enable.dev_attr.attr, + NULL +}; + +static struct attribute_group mcp3564_attribute_group = { + .attrs = mcp3564_attributes, +}; + +static struct attribute_group mcp3564r_attribute_group = { + .attrs = mcp3564r_attributes, +}; + +static const struct iio_info mcp3564_info = { + .read_raw = mcp3564_read_raw, + .read_avail = mcp3564_read_avail, + .write_raw = mcp3564_write_raw, + .write_raw_get_fmt = mcp3564_write_raw_get_fmt, + .read_label = mcp3564_read_label, + .attrs = &mcp3564_attribute_group, +}; + +static const struct iio_info mcp3564r_info = { + .read_raw = mcp3564_read_raw, + .read_avail = mcp3564_read_avail, + .write_raw = mcp3564_write_raw, + .write_raw_get_fmt = mcp3564_write_raw_get_fmt, + .read_label = mcp3564_read_label, + .attrs = &mcp3564r_attribute_group, +}; + +static int mcp3564_probe(struct spi_device *spi) +{ + int ret; + struct iio_dev *indio_dev; + struct mcp3564_state *adc; + bool use_internal_vref_attr; + + indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*adc)); + if (!indio_dev) + return -ENOMEM; + + adc = iio_priv(indio_dev); + adc->spi = spi; + + dev_dbg(&spi->dev, "%s: probe(spi = 0x%p)\n", __func__, spi); + + /* + * Do any chip specific initialization, e.g: + * read/write some registers + * enable/disable certain channels + * change the sampling rate to the requested value + */ + ret = mcp3564_config(indio_dev, &use_internal_vref_attr); + if (ret) + return dev_err_probe(&spi->dev, ret, + "Can't configure MCP356X device\n"); + + dev_dbg(&spi->dev, "%s: Vref (mV): %d\n", __func__, adc->vref_mv); + + mcp3564_fill_scale_tbls(adc); + + indio_dev->name = adc->chip_info->name; + indio_dev->modes = INDIO_DIRECT_MODE; + + if (use_internal_vref_attr) + indio_dev->info = &mcp3564r_info; + else + indio_dev->info = &mcp3564_info; + + mutex_init(&adc->lock); + + ret = devm_iio_device_register(&spi->dev, indio_dev); + if (ret) + return dev_err_probe(&spi->dev, ret, + "Can't register IIO device\n"); + + return 0; +} + +static const struct of_device_id mcp3564_dt_ids[] = { + { .compatible = "microchip,mcp3461", .data = &mcp3564_chip_infos_tbl[mcp3461] }, + { .compatible = "microchip,mcp3462", .data = &mcp3564_chip_infos_tbl[mcp3462] }, + { .compatible = "microchip,mcp3464", .data = &mcp3564_chip_infos_tbl[mcp3464] }, + { .compatible = "microchip,mcp3561", .data = &mcp3564_chip_infos_tbl[mcp3561] }, + { .compatible = "microchip,mcp3562", .data = &mcp3564_chip_infos_tbl[mcp3562] }, + { .compatible = "microchip,mcp3564", .data = &mcp3564_chip_infos_tbl[mcp3564] }, + { .compatible = "microchip,mcp3461r", .data = &mcp3564_chip_infos_tbl[mcp3461r] }, + { .compatible = "microchip,mcp3462r", .data = &mcp3564_chip_infos_tbl[mcp3462r] }, + { .compatible = "microchip,mcp3464r", .data = &mcp3564_chip_infos_tbl[mcp3464r] }, + { .compatible = "microchip,mcp3561r", .data = &mcp3564_chip_infos_tbl[mcp3561r] }, + { .compatible = "microchip,mcp3562r", .data = &mcp3564_chip_infos_tbl[mcp3562r] }, + { .compatible = "microchip,mcp3564r", .data = &mcp3564_chip_infos_tbl[mcp3564r] }, + { } +}; +MODULE_DEVICE_TABLE(of, mcp3564_dt_ids); + +static const struct spi_device_id mcp3564_id[] = { + { .name = "mcp3461", .driver_data = (kernel_ulong_t)&mcp3564_chip_infos_tbl[mcp3461] }, + { .name = "mcp3462", .driver_data = (kernel_ulong_t)&mcp3564_chip_infos_tbl[mcp3462] }, + { .name = "mcp3464", .driver_data = (kernel_ulong_t)&mcp3564_chip_infos_tbl[mcp3464] }, + { .name = "mcp3561", .driver_data = (kernel_ulong_t)&mcp3564_chip_infos_tbl[mcp3561] }, + { .name = "mcp3562", .driver_data = (kernel_ulong_t)&mcp3564_chip_infos_tbl[mcp3562] }, + { .name = "mcp3564", .driver_data = (kernel_ulong_t)&mcp3564_chip_infos_tbl[mcp3564] }, + { .name = "mcp3461r", .driver_data = (kernel_ulong_t)&mcp3564_chip_infos_tbl[mcp3461r] }, + { .name = "mcp3462r", .driver_data = (kernel_ulong_t)&mcp3564_chip_infos_tbl[mcp3462r] }, + { .name = "mcp3464r", .driver_data = (kernel_ulong_t)&mcp3564_chip_infos_tbl[mcp3464r] }, + { .name = "mcp3561r", .driver_data = (kernel_ulong_t)&mcp3564_chip_infos_tbl[mcp3561r] }, + { .name = "mcp3562r", .driver_data = (kernel_ulong_t)&mcp3564_chip_infos_tbl[mcp3562r] }, + { .name = "mcp3564r", .driver_data = (kernel_ulong_t)&mcp3564_chip_infos_tbl[mcp3564r] }, + { } +}; +MODULE_DEVICE_TABLE(spi, mcp3564_id); + +static struct spi_driver mcp3564_driver = { + .driver = { + .name = "mcp3564", + .of_match_table = mcp3564_dt_ids, + }, + .probe = mcp3564_probe, + .id_table = mcp3564_id, +}; + +module_spi_driver(mcp3564_driver); + +MODULE_AUTHOR("Marius Cristea "); +MODULE_DESCRIPTION("Microchip MCP346x/MCP346xR and MCP356x/MCP356xR ADCs"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/adc/mcp3911.c b/drivers/iio/adc/mcp3911.c new file mode 100644 index 000000000..797424f59 --- /dev/null +++ b/drivers/iio/adc/mcp3911.c @@ -0,0 +1,954 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Driver for Microchip MCP3911, Two-channel Analog Front End + * + * Copyright (C) 2018 Marcus Folkesson + * Copyright (C) 2018 Kent Gustavsson + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include + +#define MCP3911_REG_CHANNEL0 0x00 +#define MCP3911_REG_CHANNEL1 0x03 +#define MCP3911_REG_MOD 0x06 +#define MCP3911_REG_PHASE 0x07 +#define MCP3911_REG_GAIN 0x09 +#define MCP3911_GAIN_MASK(ch) (GENMASK(2, 0) << 3 * (ch)) +#define MCP3911_GAIN_VAL(ch, val) ((val << 3 * (ch)) & MCP3911_GAIN_MASK(ch)) + +#define MCP3911_REG_STATUSCOM 0x0a +#define MCP3911_STATUSCOM_DRHIZ BIT(12) +#define MCP3911_STATUSCOM_READ GENMASK(7, 6) +#define MCP3911_STATUSCOM_CH1_24WIDTH BIT(4) +#define MCP3911_STATUSCOM_CH0_24WIDTH BIT(3) +#define MCP3911_STATUSCOM_EN_OFFCAL BIT(2) +#define MCP3911_STATUSCOM_EN_GAINCAL BIT(1) + +#define MCP3911_REG_CONFIG 0x0c +#define MCP3911_CONFIG_CLKEXT BIT(1) +#define MCP3911_CONFIG_VREFEXT BIT(2) +#define MCP3911_CONFIG_OSR GENMASK(13, 11) + +#define MCP3911_REG_OFFCAL_CH0 0x0e +#define MCP3911_REG_GAINCAL_CH0 0x11 +#define MCP3911_REG_OFFCAL_CH1 0x14 +#define MCP3911_REG_GAINCAL_CH1 0x17 +#define MCP3911_REG_VREFCAL 0x1a + +#define MCP3911_CHANNEL(ch) (MCP3911_REG_CHANNEL0 + (ch) * 3) +#define MCP3911_OFFCAL(ch) (MCP3911_REG_OFFCAL_CH0 + (ch) * 6) + +/* Internal voltage reference in mV */ +#define MCP3911_INT_VREF_MV 1200 + +#define MCP3911_REG_READ(reg, id) ((((reg) << 1) | ((id) << 6) | (1 << 0)) & 0xff) +#define MCP3911_REG_WRITE(reg, id) ((((reg) << 1) | ((id) << 6) | (0 << 0)) & 0xff) +#define MCP3911_REG_MASK GENMASK(4, 1) + +#define MCP3911_NUM_SCALES 6 + +/* Registers compatible with MCP3910 */ +#define MCP3910_REG_STATUSCOM 0x0c +#define MCP3910_STATUSCOM_READ GENMASK(23, 22) +#define MCP3910_STATUSCOM_DRHIZ BIT(20) + +#define MCP3910_REG_GAIN 0x0b + +#define MCP3910_REG_CONFIG0 0x0d +#define MCP3910_CONFIG0_EN_OFFCAL BIT(23) +#define MCP3910_CONFIG0_OSR GENMASK(15, 13) + +#define MCP3910_REG_CONFIG1 0x0e +#define MCP3910_CONFIG1_CLKEXT BIT(6) +#define MCP3910_CONFIG1_VREFEXT BIT(7) + +#define MCP3910_CHANNEL(ch) (MCP3911_REG_CHANNEL0 + (ch)) + +#define MCP3910_REG_OFFCAL_CH0 0x0f +#define MCP3910_OFFCAL(ch) (MCP3910_REG_OFFCAL_CH0 + (ch) * 6) + +/* Maximal number of channels used by the MCP39XX family */ +#define MCP39XX_MAX_NUM_CHANNELS 8 + +static const int mcp3911_osr_table[] = { 32, 64, 128, 256, 512, 1024, 2048, 4096 }; +static u32 mcp3911_scale_table[MCP3911_NUM_SCALES][2]; + +enum mcp3911_id { + MCP3910, + MCP3911, + MCP3912, + MCP3913, + MCP3914, + MCP3918, + MCP3919, +}; + +struct mcp3911; +struct mcp3911_chip_info { + const struct iio_chan_spec *channels; + unsigned int num_channels; + + int (*config)(struct mcp3911 *adc, bool external_vref); + int (*get_osr)(struct mcp3911 *adc, u32 *val); + int (*set_osr)(struct mcp3911 *adc, u32 val); + int (*enable_offset)(struct mcp3911 *adc, bool enable); + int (*get_offset)(struct mcp3911 *adc, int channel, int *val); + int (*set_offset)(struct mcp3911 *adc, int channel, int val); + int (*set_scale)(struct mcp3911 *adc, int channel, u32 val); + int (*get_raw)(struct mcp3911 *adc, int channel, int *val); +}; + +struct mcp3911 { + struct spi_device *spi; + struct mutex lock; + struct clk *clki; + u32 dev_addr; + struct iio_trigger *trig; + u32 gain[MCP39XX_MAX_NUM_CHANNELS]; + const struct mcp3911_chip_info *chip; + struct { + u32 channels[MCP39XX_MAX_NUM_CHANNELS]; + aligned_s64 ts; + } scan; + + u8 tx_buf __aligned(IIO_DMA_MINALIGN); + u8 rx_buf[MCP39XX_MAX_NUM_CHANNELS * 3]; +}; + +static int mcp3911_read(struct mcp3911 *adc, u8 reg, u32 *val, u8 len) +{ + int ret; + + reg = MCP3911_REG_READ(reg, adc->dev_addr); + ret = spi_write_then_read(adc->spi, ®, 1, val, len); + if (ret < 0) + return ret; + + be32_to_cpus(val); + *val >>= ((4 - len) * 8); + dev_dbg(&adc->spi->dev, "reading 0x%x from register 0x%lx\n", *val, + FIELD_GET(MCP3911_REG_MASK, reg)); + return ret; +} + +static int mcp3911_write(struct mcp3911 *adc, u8 reg, u32 val, u8 len) +{ + dev_dbg(&adc->spi->dev, "writing 0x%x to register 0x%x\n", val, reg); + + val <<= (3 - len) * 8; + cpu_to_be32s(&val); + val |= MCP3911_REG_WRITE(reg, adc->dev_addr); + + return spi_write(adc->spi, &val, len + 1); +} + +static int mcp3911_update(struct mcp3911 *adc, u8 reg, u32 mask, u32 val, u8 len) +{ + u32 tmp; + int ret; + + ret = mcp3911_read(adc, reg, &tmp, len); + if (ret) + return ret; + + val &= mask; + val |= tmp & ~mask; + return mcp3911_write(adc, reg, val, len); +} + +static int mcp3911_read_s24(struct mcp3911 *const adc, u8 const reg, s32 *const val) +{ + u32 uval; + int const ret = mcp3911_read(adc, reg, &uval, 3); + + if (ret) + return ret; + + *val = sign_extend32(uval, 23); + return ret; +} + +static int mcp3910_enable_offset(struct mcp3911 *adc, bool enable) +{ + unsigned int mask = MCP3910_CONFIG0_EN_OFFCAL; + unsigned int value = enable ? mask : 0; + + return mcp3911_update(adc, MCP3910_REG_CONFIG0, mask, value, 3); +} + +static int mcp3910_get_offset(struct mcp3911 *adc, int channel, int *val) +{ + return mcp3911_read(adc, MCP3910_OFFCAL(channel), val, 3); +} + +static int mcp3910_set_offset(struct mcp3911 *adc, int channel, int val) +{ + int ret; + + ret = mcp3911_write(adc, MCP3910_OFFCAL(channel), val, 3); + if (ret) + return ret; + + return adc->chip->enable_offset(adc, 1); +} + +static int mcp3910_get_raw(struct mcp3911 *adc, int channel, s32 *val) +{ + return mcp3911_read_s24(adc, MCP3910_CHANNEL(channel), val); +} + +static int mcp3911_enable_offset(struct mcp3911 *adc, bool enable) +{ + unsigned int mask = MCP3911_STATUSCOM_EN_OFFCAL; + unsigned int value = enable ? mask : 0; + + return mcp3911_update(adc, MCP3911_REG_STATUSCOM, mask, value, 2); +} + +static int mcp3911_get_offset(struct mcp3911 *adc, int channel, int *val) +{ + return mcp3911_read(adc, MCP3911_OFFCAL(channel), val, 3); +} + +static int mcp3911_set_offset(struct mcp3911 *adc, int channel, int val) +{ + int ret; + + ret = mcp3911_write(adc, MCP3911_OFFCAL(channel), val, 3); + if (ret) + return ret; + + return adc->chip->enable_offset(adc, 1); +} + +static int mcp3911_get_raw(struct mcp3911 *adc, int channel, s32 *val) +{ + return mcp3911_read_s24(adc, MCP3911_CHANNEL(channel), val); +} + +static int mcp3910_get_osr(struct mcp3911 *adc, u32 *val) +{ + int ret; + unsigned int osr; + + ret = mcp3911_read(adc, MCP3910_REG_CONFIG0, val, 3); + if (ret) + return ret; + + osr = FIELD_GET(MCP3910_CONFIG0_OSR, *val); + *val = 32 << osr; + return 0; +} + +static int mcp3910_set_osr(struct mcp3911 *adc, u32 val) +{ + unsigned int osr = FIELD_PREP(MCP3910_CONFIG0_OSR, val); + unsigned int mask = MCP3910_CONFIG0_OSR; + + return mcp3911_update(adc, MCP3910_REG_CONFIG0, mask, osr, 3); +} + +static int mcp3911_set_osr(struct mcp3911 *adc, u32 val) +{ + unsigned int osr = FIELD_PREP(MCP3911_CONFIG_OSR, val); + unsigned int mask = MCP3911_CONFIG_OSR; + + return mcp3911_update(adc, MCP3911_REG_CONFIG, mask, osr, 2); +} + +static int mcp3911_get_osr(struct mcp3911 *adc, u32 *val) +{ + int ret; + unsigned int osr; + + ret = mcp3911_read(adc, MCP3911_REG_CONFIG, val, 2); + if (ret) + return ret; + + osr = FIELD_GET(MCP3911_CONFIG_OSR, *val); + *val = 32 << osr; + return ret; +} + +static int mcp3910_set_scale(struct mcp3911 *adc, int channel, u32 val) +{ + return mcp3911_update(adc, MCP3910_REG_GAIN, + MCP3911_GAIN_MASK(channel), + MCP3911_GAIN_VAL(channel, val), 3); +} + +static int mcp3911_set_scale(struct mcp3911 *adc, int channel, u32 val) +{ + return mcp3911_update(adc, MCP3911_REG_GAIN, + MCP3911_GAIN_MASK(channel), + MCP3911_GAIN_VAL(channel, val), 1); +} + +static int mcp3911_write_raw_get_fmt(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + long mask) +{ + switch (mask) { + case IIO_CHAN_INFO_SCALE: + return IIO_VAL_INT_PLUS_NANO; + case IIO_CHAN_INFO_OVERSAMPLING_RATIO: + return IIO_VAL_INT; + default: + return IIO_VAL_INT_PLUS_NANO; + } +} + +static int mcp3911_read_avail(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + const int **vals, int *type, int *length, + long info) +{ + switch (info) { + case IIO_CHAN_INFO_OVERSAMPLING_RATIO: + *type = IIO_VAL_INT; + *vals = mcp3911_osr_table; + *length = ARRAY_SIZE(mcp3911_osr_table); + return IIO_AVAIL_LIST; + case IIO_CHAN_INFO_SCALE: + *type = IIO_VAL_INT_PLUS_NANO; + *vals = (int *)mcp3911_scale_table; + *length = ARRAY_SIZE(mcp3911_scale_table) * 2; + return IIO_AVAIL_LIST; + default: + return -EINVAL; + } +} + +static int mcp3911_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *channel, int *val, + int *val2, long mask) +{ + struct mcp3911 *adc = iio_priv(indio_dev); + int ret; + + guard(mutex)(&adc->lock); + switch (mask) { + case IIO_CHAN_INFO_RAW: + ret = adc->chip->get_raw(adc, channel->channel, val); + if (ret) + return ret; + return IIO_VAL_INT; + case IIO_CHAN_INFO_OFFSET: + ret = adc->chip->get_offset(adc, channel->channel, val); + if (ret) + return ret; + + return IIO_VAL_INT; + case IIO_CHAN_INFO_OVERSAMPLING_RATIO: + ret = adc->chip->get_osr(adc, val); + if (ret) + return ret; + + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + *val = mcp3911_scale_table[ilog2(adc->gain[channel->channel])][0]; + *val2 = mcp3911_scale_table[ilog2(adc->gain[channel->channel])][1]; + return IIO_VAL_INT_PLUS_NANO; + default: + return -EINVAL; + } +} + +static int mcp3911_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *channel, int val, + int val2, long mask) +{ + struct mcp3911 *adc = iio_priv(indio_dev); + + guard(mutex)(&adc->lock); + switch (mask) { + case IIO_CHAN_INFO_SCALE: + for (int i = 0; i < MCP3911_NUM_SCALES; i++) { + if (val == mcp3911_scale_table[i][0] && + val2 == mcp3911_scale_table[i][1]) { + + adc->gain[channel->channel] = BIT(i); + return adc->chip->set_scale(adc, channel->channel, i); + } + } + return -EINVAL; + case IIO_CHAN_INFO_OFFSET: + if (val2 != 0) + return -EINVAL; + + return adc->chip->set_offset(adc, channel->channel, val); + case IIO_CHAN_INFO_OVERSAMPLING_RATIO: + for (int i = 0; i < ARRAY_SIZE(mcp3911_osr_table); i++) { + if (val == mcp3911_osr_table[i]) { + return adc->chip->set_osr(adc, i); + } + } + return -EINVAL; + default: + return -EINVAL; + } +} + +static int mcp3911_calc_scale_table(u32 vref_mv) +{ + u32 div; + u64 tmp; + + /* + * For 24-bit Conversion + * Raw = ((Voltage)/(Vref) * 2^23 * Gain * 1.5 + * Voltage = Raw * (Vref)/(2^23 * Gain * 1.5) + * + * ref = Reference voltage + * div = (2^23 * 1.5 * gain) = 12582912 * gain + */ + for (int i = 0; i < MCP3911_NUM_SCALES; i++) { + div = 12582912 * BIT(i); + tmp = div_s64((s64)vref_mv * 1000000000LL, div); + + mcp3911_scale_table[i][0] = 0; + mcp3911_scale_table[i][1] = tmp; + } + + return 0; +} + +#define MCP3911_CHAN(idx) { \ + .type = IIO_VOLTAGE, \ + .indexed = 1, \ + .channel = idx, \ + .scan_index = idx, \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ + BIT(IIO_CHAN_INFO_OFFSET) | \ + BIT(IIO_CHAN_INFO_SCALE), \ + .info_mask_shared_by_type_available = \ + BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \ + .info_mask_separate_available = \ + BIT(IIO_CHAN_INFO_SCALE), \ + .scan_type = { \ + .sign = 's', \ + .realbits = 24, \ + .storagebits = 32, \ + .endianness = IIO_BE, \ + }, \ +} + +static const struct iio_chan_spec mcp3910_channels[] = { + MCP3911_CHAN(0), + MCP3911_CHAN(1), + IIO_CHAN_SOFT_TIMESTAMP(2), +}; + +static const struct iio_chan_spec mcp3911_channels[] = { + MCP3911_CHAN(0), + MCP3911_CHAN(1), + IIO_CHAN_SOFT_TIMESTAMP(2), +}; + +static const struct iio_chan_spec mcp3912_channels[] = { + MCP3911_CHAN(0), + MCP3911_CHAN(1), + MCP3911_CHAN(2), + MCP3911_CHAN(3), + IIO_CHAN_SOFT_TIMESTAMP(4), +}; + +static const struct iio_chan_spec mcp3913_channels[] = { + MCP3911_CHAN(0), + MCP3911_CHAN(1), + MCP3911_CHAN(2), + MCP3911_CHAN(3), + MCP3911_CHAN(4), + MCP3911_CHAN(5), + IIO_CHAN_SOFT_TIMESTAMP(6), +}; + +static const struct iio_chan_spec mcp3914_channels[] = { + MCP3911_CHAN(0), + MCP3911_CHAN(1), + MCP3911_CHAN(2), + MCP3911_CHAN(3), + MCP3911_CHAN(4), + MCP3911_CHAN(5), + MCP3911_CHAN(6), + MCP3911_CHAN(7), + IIO_CHAN_SOFT_TIMESTAMP(8), +}; + +static const struct iio_chan_spec mcp3918_channels[] = { + MCP3911_CHAN(0), + IIO_CHAN_SOFT_TIMESTAMP(1), +}; + +static const struct iio_chan_spec mcp3919_channels[] = { + MCP3911_CHAN(0), + MCP3911_CHAN(1), + MCP3911_CHAN(2), + IIO_CHAN_SOFT_TIMESTAMP(3), +}; + +static irqreturn_t mcp3911_trigger_handler(int irq, void *p) +{ + struct iio_poll_func *pf = p; + struct iio_dev *indio_dev = pf->indio_dev; + struct mcp3911 *adc = iio_priv(indio_dev); + struct device *dev = &adc->spi->dev; + struct spi_transfer xfer[] = { + { + .tx_buf = &adc->tx_buf, + .len = 1, + }, { + .rx_buf = adc->rx_buf, + .len = (adc->chip->num_channels - 1) * 3, + }, + }; + int scan_index; + int i = 0; + int ret; + + guard(mutex)(&adc->lock); + adc->tx_buf = MCP3911_REG_READ(MCP3911_CHANNEL(0), adc->dev_addr); + ret = spi_sync_transfer(adc->spi, xfer, ARRAY_SIZE(xfer)); + if (ret < 0) { + dev_warn(dev, "failed to get conversion data\n"); + goto out; + } + + iio_for_each_active_channel(indio_dev, scan_index) { + const struct iio_chan_spec *scan_chan = &indio_dev->channels[scan_index]; + + adc->scan.channels[i] = get_unaligned_be24(&adc->rx_buf[scan_chan->channel * 3]); + i++; + } + iio_push_to_buffers_with_ts(indio_dev, &adc->scan, sizeof(adc->scan), + iio_get_time_ns(indio_dev)); +out: + iio_trigger_notify_done(indio_dev->trig); + + return IRQ_HANDLED; +} + +static const struct iio_info mcp3911_info = { + .read_raw = mcp3911_read_raw, + .write_raw = mcp3911_write_raw, + .read_avail = mcp3911_read_avail, + .write_raw_get_fmt = mcp3911_write_raw_get_fmt, +}; + +static int mcp3911_config(struct mcp3911 *adc, bool external_vref) +{ + struct device *dev = &adc->spi->dev; + u32 regval; + int ret; + + ret = mcp3911_read(adc, MCP3911_REG_CONFIG, ®val, 2); + if (ret) + return ret; + + regval &= ~MCP3911_CONFIG_VREFEXT; + if (external_vref) { + dev_dbg(dev, "use external voltage reference\n"); + regval |= FIELD_PREP(MCP3911_CONFIG_VREFEXT, 1); + } else { + dev_dbg(dev, "use internal voltage reference (1.2V)\n"); + regval |= FIELD_PREP(MCP3911_CONFIG_VREFEXT, 0); + } + + regval &= ~MCP3911_CONFIG_CLKEXT; + if (adc->clki) { + dev_dbg(dev, "use external clock as clocksource\n"); + regval |= FIELD_PREP(MCP3911_CONFIG_CLKEXT, 1); + } else { + dev_dbg(dev, "use crystal oscillator as clocksource\n"); + regval |= FIELD_PREP(MCP3911_CONFIG_CLKEXT, 0); + } + + ret = mcp3911_write(adc, MCP3911_REG_CONFIG, regval, 2); + if (ret) + return ret; + + ret = mcp3911_read(adc, MCP3911_REG_STATUSCOM, ®val, 2); + if (ret) + return ret; + + /* Address counter incremented, cycle through register types */ + regval &= ~MCP3911_STATUSCOM_READ; + regval |= FIELD_PREP(MCP3911_STATUSCOM_READ, 0x02); + + regval &= ~MCP3911_STATUSCOM_DRHIZ; + if (device_property_read_bool(dev, "microchip,data-ready-hiz")) + regval |= FIELD_PREP(MCP3911_STATUSCOM_DRHIZ, 0); + else + regval |= FIELD_PREP(MCP3911_STATUSCOM_DRHIZ, 1); + + /* Disable offset to ignore any old values in offset register */ + regval &= ~MCP3911_STATUSCOM_EN_OFFCAL; + + ret = mcp3911_write(adc, MCP3911_REG_STATUSCOM, regval, 2); + if (ret) + return ret; + + /* Set gain to 1 for all channels */ + ret = mcp3911_read(adc, MCP3911_REG_GAIN, ®val, 1); + if (ret) + return ret; + + for (int i = 0; i < adc->chip->num_channels - 1; i++) { + adc->gain[i] = 1; + regval &= ~MCP3911_GAIN_MASK(i); + } + + return mcp3911_write(adc, MCP3911_REG_GAIN, regval, 1); +} + +static int mcp3910_config(struct mcp3911 *adc, bool external_vref) +{ + struct device *dev = &adc->spi->dev; + u32 regval; + int ret; + + ret = mcp3911_read(adc, MCP3910_REG_CONFIG1, ®val, 3); + if (ret) + return ret; + + regval &= ~MCP3910_CONFIG1_VREFEXT; + if (external_vref) { + dev_dbg(dev, "use external voltage reference\n"); + regval |= FIELD_PREP(MCP3910_CONFIG1_VREFEXT, 1); + } else { + dev_dbg(dev, "use internal voltage reference (1.2V)\n"); + regval |= FIELD_PREP(MCP3910_CONFIG1_VREFEXT, 0); + } + + regval &= ~MCP3910_CONFIG1_CLKEXT; + if (adc->clki) { + dev_dbg(dev, "use external clock as clocksource\n"); + regval |= FIELD_PREP(MCP3910_CONFIG1_CLKEXT, 1); + } else { + dev_dbg(dev, "use crystal oscillator as clocksource\n"); + regval |= FIELD_PREP(MCP3910_CONFIG1_CLKEXT, 0); + } + + ret = mcp3911_write(adc, MCP3910_REG_CONFIG1, regval, 3); + if (ret) + return ret; + + ret = mcp3911_read(adc, MCP3910_REG_STATUSCOM, ®val, 3); + if (ret) + return ret; + + /* Address counter incremented, cycle through register types */ + regval &= ~MCP3910_STATUSCOM_READ; + regval |= FIELD_PREP(MCP3910_STATUSCOM_READ, 0x02); + + regval &= ~MCP3910_STATUSCOM_DRHIZ; + if (device_property_read_bool(dev, "microchip,data-ready-hiz")) + regval |= FIELD_PREP(MCP3910_STATUSCOM_DRHIZ, 0); + else + regval |= FIELD_PREP(MCP3910_STATUSCOM_DRHIZ, 1); + + ret = mcp3911_write(adc, MCP3910_REG_STATUSCOM, regval, 3); + if (ret) + return ret; + + /* Set gain to 1 for all channels */ + ret = mcp3911_read(adc, MCP3910_REG_GAIN, ®val, 3); + if (ret) + return ret; + + for (int i = 0; i < adc->chip->num_channels - 1; i++) { + adc->gain[i] = 1; + regval &= ~MCP3911_GAIN_MASK(i); + } + ret = mcp3911_write(adc, MCP3910_REG_GAIN, regval, 3); + if (ret) + return ret; + + /* Disable offset to ignore any old values in offset register */ + return adc->chip->enable_offset(adc, 0); +} + +static int mcp3911_set_trigger_state(struct iio_trigger *trig, bool enable) +{ + struct mcp3911 *adc = iio_trigger_get_drvdata(trig); + + if (enable) + enable_irq(adc->spi->irq); + else + disable_irq(adc->spi->irq); + + return 0; +} + +static const struct iio_trigger_ops mcp3911_trigger_ops = { + .validate_device = iio_trigger_validate_own_device, + .set_trigger_state = mcp3911_set_trigger_state, +}; + +static int mcp3911_probe(struct spi_device *spi) +{ + struct device *dev = &spi->dev; + struct gpio_desc *gpio_reset; + struct iio_dev *indio_dev; + struct mcp3911 *adc; + bool external_vref; + u32 vref_mv; + int ret; + + indio_dev = devm_iio_device_alloc(dev, sizeof(*adc)); + if (!indio_dev) + return -ENOMEM; + + adc = iio_priv(indio_dev); + adc->spi = spi; + adc->chip = spi_get_device_match_data(spi); + + ret = devm_regulator_get_enable_read_voltage(dev, "vref"); + if (ret < 0 && ret != -ENODEV) + return dev_err_probe(dev, ret, "failed to get vref voltage\n"); + + external_vref = ret != -ENODEV; + vref_mv = external_vref ? ret / 1000 : MCP3911_INT_VREF_MV; + + adc->clki = devm_clk_get_enabled(dev, NULL); + if (IS_ERR(adc->clki)) { + if (PTR_ERR(adc->clki) == -ENOENT) { + adc->clki = NULL; + } else { + return dev_err_probe(dev, PTR_ERR(adc->clki), "failed to get adc clk\n"); + } + } + + /* + * Fallback to "device-addr" due to historical mismatch between + * dt-bindings and implementation. + */ + ret = device_property_read_u32(dev, "microchip,device-addr", &adc->dev_addr); + if (ret) + device_property_read_u32(dev, "device-addr", &adc->dev_addr); + if (adc->dev_addr > 3) { + return dev_err_probe(dev, -EINVAL, + "invalid device address (%i). Must be in range 0-3.\n", + adc->dev_addr); + } + dev_dbg(dev, "use device address %i\n", adc->dev_addr); + + gpio_reset = devm_gpiod_get_optional(&spi->dev, "reset", GPIOD_OUT_HIGH); + if (IS_ERR(gpio_reset)) + return dev_err_probe(dev, PTR_ERR(gpio_reset), + "Cannot get reset GPIO\n"); + + if (gpio_reset) { + gpiod_set_value_cansleep(gpio_reset, 0); + + /* + * Settling time after Hard Reset Mode (determined experimentally): + * 330 micro-seconds are too few; 470 micro-seconds are sufficient. + * Just in case, we add some safety factor... + */ + fsleep(600); + } + + ret = adc->chip->config(adc, external_vref); + if (ret) + return ret; + + ret = mcp3911_calc_scale_table(vref_mv); + if (ret) + return ret; + + /* Set gain to 1 for all channels */ + for (int i = 0; i < adc->chip->num_channels - 1; i++) { + adc->gain[i] = 1; + ret = mcp3911_update(adc, MCP3911_REG_GAIN, + MCP3911_GAIN_MASK(i), + MCP3911_GAIN_VAL(i, 0), 1); + if (ret) + return ret; + } + + indio_dev->name = spi_get_device_id(spi)->name; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->info = &mcp3911_info; + spi_set_drvdata(spi, indio_dev); + + indio_dev->channels = adc->chip->channels; + indio_dev->num_channels = adc->chip->num_channels; + + mutex_init(&adc->lock); + + if (spi->irq > 0) { + adc->trig = devm_iio_trigger_alloc(dev, "%s-dev%d", indio_dev->name, + iio_device_id(indio_dev)); + if (!adc->trig) + return -ENOMEM; + + adc->trig->ops = &mcp3911_trigger_ops; + iio_trigger_set_drvdata(adc->trig, adc); + ret = devm_iio_trigger_register(dev, adc->trig); + if (ret) + return ret; + + /* + * The device generates interrupts as long as it is powered up. + * Some platforms might not allow the option to power it down so + * don't enable the interrupt to avoid extra load on the system. + */ + ret = devm_request_irq(dev, spi->irq, &iio_trigger_generic_data_rdy_poll, + IRQF_NO_AUTOEN | IRQF_NO_THREAD, + indio_dev->name, adc->trig); + if (ret) + return ret; + } + + ret = devm_iio_triggered_buffer_setup(dev, indio_dev, NULL, + mcp3911_trigger_handler, NULL); + if (ret) + return ret; + + return devm_iio_device_register(dev, indio_dev); +} + +static const struct mcp3911_chip_info mcp3911_chip_info[] = { + [MCP3910] = { + .channels = mcp3910_channels, + .num_channels = ARRAY_SIZE(mcp3910_channels), + .config = mcp3910_config, + .get_osr = mcp3910_get_osr, + .set_osr = mcp3910_set_osr, + .enable_offset = mcp3910_enable_offset, + .get_offset = mcp3910_get_offset, + .set_offset = mcp3910_set_offset, + .set_scale = mcp3910_set_scale, + .get_raw = mcp3910_get_raw, + }, + [MCP3911] = { + .channels = mcp3911_channels, + .num_channels = ARRAY_SIZE(mcp3911_channels), + .config = mcp3911_config, + .get_osr = mcp3911_get_osr, + .set_osr = mcp3911_set_osr, + .enable_offset = mcp3911_enable_offset, + .get_offset = mcp3911_get_offset, + .set_offset = mcp3911_set_offset, + .set_scale = mcp3911_set_scale, + .get_raw = mcp3911_get_raw, + }, + [MCP3912] = { + .channels = mcp3912_channels, + .num_channels = ARRAY_SIZE(mcp3912_channels), + .config = mcp3910_config, + .get_osr = mcp3910_get_osr, + .set_osr = mcp3910_set_osr, + .enable_offset = mcp3910_enable_offset, + .get_offset = mcp3910_get_offset, + .set_offset = mcp3910_set_offset, + .set_scale = mcp3910_set_scale, + .get_raw = mcp3910_get_raw, + }, + [MCP3913] = { + .channels = mcp3913_channels, + .num_channels = ARRAY_SIZE(mcp3913_channels), + .config = mcp3910_config, + .get_osr = mcp3910_get_osr, + .set_osr = mcp3910_set_osr, + .enable_offset = mcp3910_enable_offset, + .get_offset = mcp3910_get_offset, + .set_offset = mcp3910_set_offset, + .set_scale = mcp3910_set_scale, + .get_raw = mcp3910_get_raw, + }, + [MCP3914] = { + .channels = mcp3914_channels, + .num_channels = ARRAY_SIZE(mcp3914_channels), + .config = mcp3910_config, + .get_osr = mcp3910_get_osr, + .set_osr = mcp3910_set_osr, + .enable_offset = mcp3910_enable_offset, + .get_offset = mcp3910_get_offset, + .set_offset = mcp3910_set_offset, + .set_scale = mcp3910_set_scale, + .get_raw = mcp3910_get_raw, + }, + [MCP3918] = { + .channels = mcp3918_channels, + .num_channels = ARRAY_SIZE(mcp3918_channels), + .config = mcp3910_config, + .get_osr = mcp3910_get_osr, + .set_osr = mcp3910_set_osr, + .enable_offset = mcp3910_enable_offset, + .get_offset = mcp3910_get_offset, + .set_offset = mcp3910_set_offset, + .set_scale = mcp3910_set_scale, + .get_raw = mcp3910_get_raw, + }, + [MCP3919] = { + .channels = mcp3919_channels, + .num_channels = ARRAY_SIZE(mcp3919_channels), + .config = mcp3910_config, + .get_osr = mcp3910_get_osr, + .set_osr = mcp3910_set_osr, + .enable_offset = mcp3910_enable_offset, + .get_offset = mcp3910_get_offset, + .set_offset = mcp3910_set_offset, + .set_scale = mcp3910_set_scale, + .get_raw = mcp3910_get_raw, + }, +}; +static const struct of_device_id mcp3911_dt_ids[] = { + { .compatible = "microchip,mcp3910", .data = &mcp3911_chip_info[MCP3910] }, + { .compatible = "microchip,mcp3911", .data = &mcp3911_chip_info[MCP3911] }, + { .compatible = "microchip,mcp3912", .data = &mcp3911_chip_info[MCP3912] }, + { .compatible = "microchip,mcp3913", .data = &mcp3911_chip_info[MCP3913] }, + { .compatible = "microchip,mcp3914", .data = &mcp3911_chip_info[MCP3914] }, + { .compatible = "microchip,mcp3918", .data = &mcp3911_chip_info[MCP3918] }, + { .compatible = "microchip,mcp3919", .data = &mcp3911_chip_info[MCP3919] }, + { } +}; +MODULE_DEVICE_TABLE(of, mcp3911_dt_ids); + +static const struct spi_device_id mcp3911_id[] = { + { .name = "mcp3910", .driver_data = (kernel_ulong_t)&mcp3911_chip_info[MCP3910] }, + { .name = "mcp3911", .driver_data = (kernel_ulong_t)&mcp3911_chip_info[MCP3911] }, + { .name = "mcp3912", .driver_data = (kernel_ulong_t)&mcp3911_chip_info[MCP3912] }, + { .name = "mcp3913", .driver_data = (kernel_ulong_t)&mcp3911_chip_info[MCP3913] }, + { .name = "mcp3914", .driver_data = (kernel_ulong_t)&mcp3911_chip_info[MCP3914] }, + { .name = "mcp3918", .driver_data = (kernel_ulong_t)&mcp3911_chip_info[MCP3918] }, + { .name = "mcp3919", .driver_data = (kernel_ulong_t)&mcp3911_chip_info[MCP3919] }, + { } +}; +MODULE_DEVICE_TABLE(spi, mcp3911_id); + +static struct spi_driver mcp3911_driver = { + .driver = { + .name = "mcp3911", + .of_match_table = mcp3911_dt_ids, + }, + .probe = mcp3911_probe, + .id_table = mcp3911_id, +}; +module_spi_driver(mcp3911_driver); + +MODULE_AUTHOR("Marcus Folkesson "); +MODULE_AUTHOR("Kent Gustavsson "); +MODULE_DESCRIPTION("Microchip Technology MCP3911"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/adc/men_z188_adc.c b/drivers/iio/adc/men_z188_adc.c new file mode 100644 index 000000000..5bd334ec5 --- /dev/null +++ b/drivers/iio/adc/men_z188_adc.c @@ -0,0 +1,174 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * MEN 16z188 Analog to Digital Converter + * + * Copyright (C) 2014 MEN Mikroelektronik GmbH (www.men.de) + * Author: Johannes Thumshirn + */ + +#include +#include +#include +#include +#include + +#define Z188_ADC_MAX_CHAN 8 +#define Z188_ADC_GAIN 0x0700000 +#define Z188_MODE_VOLTAGE BIT(27) +#define Z188_CFG_AUTO 0x1 +#define Z188_CTRL_REG 0x40 + +#define ADC_DATA(x) (((x) >> 2) & 0x7ffffc) +#define ADC_OVR(x) ((x) & 0x1) + +struct z188_adc { + struct resource *mem; + void __iomem *base; +}; + +#define Z188_ADC_CHANNEL(idx) { \ + .type = IIO_VOLTAGE, \ + .indexed = 1, \ + .channel = (idx), \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ +} + +static const struct iio_chan_spec z188_adc_iio_channels[] = { + Z188_ADC_CHANNEL(0), + Z188_ADC_CHANNEL(1), + Z188_ADC_CHANNEL(2), + Z188_ADC_CHANNEL(3), + Z188_ADC_CHANNEL(4), + Z188_ADC_CHANNEL(5), + Z188_ADC_CHANNEL(6), + Z188_ADC_CHANNEL(7), +}; + +static int z188_iio_read_raw(struct iio_dev *iio_dev, + struct iio_chan_spec const *chan, + int *val, + int *val2, + long info) +{ + struct z188_adc *adc = iio_priv(iio_dev); + int ret; + u16 tmp; + + switch (info) { + case IIO_CHAN_INFO_RAW: + tmp = readw(adc->base + chan->channel * 4); + + if (ADC_OVR(tmp)) { + dev_info(&iio_dev->dev, + "Oversampling error on ADC channel %d\n", + chan->channel); + return -EIO; + } + *val = ADC_DATA(tmp); + ret = IIO_VAL_INT; + break; + default: + ret = -EINVAL; + break; + } + + return ret; +} + +static const struct iio_info z188_adc_info = { + .read_raw = &z188_iio_read_raw, +}; + +static void men_z188_config_channels(void __iomem *addr) +{ + int i; + u32 cfg; + u32 ctl; + + ctl = readl(addr + Z188_CTRL_REG); + ctl |= Z188_CFG_AUTO; + writel(ctl, addr + Z188_CTRL_REG); + + for (i = 0; i < Z188_ADC_MAX_CHAN; i++) { + cfg = readl(addr + i); + cfg &= ~Z188_ADC_GAIN; + cfg |= Z188_MODE_VOLTAGE; + writel(cfg, addr + i); + } +} + +static int men_z188_probe(struct mcb_device *dev, + const struct mcb_device_id *id) +{ + struct z188_adc *adc; + struct iio_dev *indio_dev; + struct resource *mem; + int ret; + + indio_dev = devm_iio_device_alloc(&dev->dev, sizeof(struct z188_adc)); + if (!indio_dev) + return -ENOMEM; + + adc = iio_priv(indio_dev); + indio_dev->name = "z188-adc"; + indio_dev->info = &z188_adc_info; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = z188_adc_iio_channels; + indio_dev->num_channels = ARRAY_SIZE(z188_adc_iio_channels); + + mem = mcb_request_mem(dev, "z188-adc"); + if (IS_ERR(mem)) + return PTR_ERR(mem); + + adc->base = ioremap(mem->start, resource_size(mem)); + if (adc->base == NULL) + goto err; + + men_z188_config_channels(adc->base); + + adc->mem = mem; + mcb_set_drvdata(dev, indio_dev); + + ret = iio_device_register(indio_dev); + if (ret) + goto err_unmap; + + return 0; + +err_unmap: + iounmap(adc->base); +err: + mcb_release_mem(mem); + return -ENXIO; +} + +static void men_z188_remove(struct mcb_device *dev) +{ + struct iio_dev *indio_dev = mcb_get_drvdata(dev); + struct z188_adc *adc = iio_priv(indio_dev); + + iio_device_unregister(indio_dev); + iounmap(adc->base); + mcb_release_mem(adc->mem); +} + +static const struct mcb_device_id men_z188_ids[] = { + { .device = 0xbc }, + { } +}; +MODULE_DEVICE_TABLE(mcb, men_z188_ids); + +static struct mcb_driver men_z188_driver = { + .driver = { + .name = "z188-adc", + }, + .probe = men_z188_probe, + .remove = men_z188_remove, + .id_table = men_z188_ids, +}; +module_mcb_driver(men_z188_driver); + +MODULE_AUTHOR("Johannes Thumshirn "); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("IIO ADC driver for MEN 16z188 ADC Core"); +MODULE_IMPORT_NS("MCB"); diff --git a/drivers/iio/adc/meson_saradc.c b/drivers/iio/adc/meson_saradc.c new file mode 100644 index 000000000..000e39ca5 --- /dev/null +++ b/drivers/iio/adc/meson_saradc.c @@ -0,0 +1,1530 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Amlogic Meson Successive Approximation Register (SAR) A/D Converter + * + * Copyright (C) 2017 Martin Blumenstingl + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define MESON_SAR_ADC_REG0 0x00 + #define MESON_SAR_ADC_REG0_PANEL_DETECT BIT(31) + #define MESON_SAR_ADC_REG0_BUSY_MASK GENMASK(30, 28) + #define MESON_SAR_ADC_REG0_DELTA_BUSY BIT(30) + #define MESON_SAR_ADC_REG0_AVG_BUSY BIT(29) + #define MESON_SAR_ADC_REG0_SAMPLE_BUSY BIT(28) + #define MESON_SAR_ADC_REG0_FIFO_FULL BIT(27) + #define MESON_SAR_ADC_REG0_FIFO_EMPTY BIT(26) + #define MESON_SAR_ADC_REG0_FIFO_COUNT_MASK GENMASK(25, 21) + #define MESON_SAR_ADC_REG0_ADC_BIAS_CTRL_MASK GENMASK(20, 19) + #define MESON_SAR_ADC_REG0_CURR_CHAN_ID_MASK GENMASK(18, 16) + #define MESON_SAR_ADC_REG0_ADC_TEMP_SEN_SEL BIT(15) + #define MESON_SAR_ADC_REG0_SAMPLING_STOP BIT(14) + #define MESON_SAR_ADC_REG0_CHAN_DELTA_EN_MASK GENMASK(13, 12) + #define MESON_SAR_ADC_REG0_DETECT_IRQ_POL BIT(10) + #define MESON_SAR_ADC_REG0_DETECT_IRQ_EN BIT(9) + #define MESON_SAR_ADC_REG0_FIFO_CNT_IRQ_MASK GENMASK(8, 4) + #define MESON_SAR_ADC_REG0_FIFO_IRQ_EN BIT(3) + #define MESON_SAR_ADC_REG0_SAMPLING_START BIT(2) + #define MESON_SAR_ADC_REG0_CONTINUOUS_EN BIT(1) + #define MESON_SAR_ADC_REG0_SAMPLE_ENGINE_ENABLE BIT(0) + +#define MESON_SAR_ADC_CHAN_LIST 0x04 + #define MESON_SAR_ADC_CHAN_LIST_MAX_INDEX_MASK GENMASK(26, 24) + #define MESON_SAR_ADC_CHAN_LIST_ENTRY_MASK(_chan) \ + (GENMASK(2, 0) << ((_chan) * 3)) + +#define MESON_SAR_ADC_AVG_CNTL 0x08 + #define MESON_SAR_ADC_AVG_CNTL_AVG_MODE_SHIFT(_chan) \ + (16 + ((_chan) * 2)) + #define MESON_SAR_ADC_AVG_CNTL_AVG_MODE_MASK(_chan) \ + (GENMASK(17, 16) << ((_chan) * 2)) + #define MESON_SAR_ADC_AVG_CNTL_NUM_SAMPLES_SHIFT(_chan) \ + (0 + ((_chan) * 2)) + #define MESON_SAR_ADC_AVG_CNTL_NUM_SAMPLES_MASK(_chan) \ + (GENMASK(1, 0) << ((_chan) * 2)) + +#define MESON_SAR_ADC_REG3 0x0c + #define MESON_SAR_ADC_REG3_CNTL_USE_SC_DLY BIT(31) + #define MESON_SAR_ADC_REG3_CLK_EN BIT(30) + #define MESON_SAR_ADC_REG3_BL30_INITIALIZED BIT(28) + #define MESON_SAR_ADC_REG3_CTRL_CONT_RING_COUNTER_EN BIT(27) + #define MESON_SAR_ADC_REG3_CTRL_SAMPLING_CLOCK_PHASE BIT(26) + #define MESON_SAR_ADC_REG3_CTRL_CHAN7_MUX_SEL_MASK GENMASK(25, 23) + #define MESON_SAR_ADC_REG3_DETECT_EN BIT(22) + #define MESON_SAR_ADC_REG3_ADC_EN BIT(21) + #define MESON_SAR_ADC_REG3_PANEL_DETECT_COUNT_MASK GENMASK(20, 18) + #define MESON_SAR_ADC_REG3_PANEL_DETECT_FILTER_TB_MASK GENMASK(17, 16) + #define MESON_SAR_ADC_REG3_ADC_CLK_DIV_SHIFT 10 + #define MESON_SAR_ADC_REG3_ADC_CLK_DIV_WIDTH 6 + #define MESON_SAR_ADC_REG3_BLOCK_DLY_SEL_MASK GENMASK(9, 8) + #define MESON_SAR_ADC_REG3_BLOCK_DLY_MASK GENMASK(7, 0) + +#define MESON_SAR_ADC_DELAY 0x10 + #define MESON_SAR_ADC_DELAY_INPUT_DLY_SEL_MASK GENMASK(25, 24) + #define MESON_SAR_ADC_DELAY_BL30_BUSY BIT(15) + #define MESON_SAR_ADC_DELAY_KERNEL_BUSY BIT(14) + #define MESON_SAR_ADC_DELAY_INPUT_DLY_CNT_MASK GENMASK(23, 16) + #define MESON_SAR_ADC_DELAY_SAMPLE_DLY_SEL_MASK GENMASK(9, 8) + #define MESON_SAR_ADC_DELAY_SAMPLE_DLY_CNT_MASK GENMASK(7, 0) + +#define MESON_SAR_ADC_LAST_RD 0x14 + #define MESON_SAR_ADC_LAST_RD_LAST_CHANNEL1_MASK GENMASK(23, 16) + #define MESON_SAR_ADC_LAST_RD_LAST_CHANNEL0_MASK GENMASK(9, 0) + +#define MESON_SAR_ADC_FIFO_RD 0x18 + #define MESON_SAR_ADC_FIFO_RD_CHAN_ID_MASK GENMASK(14, 12) + #define MESON_SAR_ADC_FIFO_RD_SAMPLE_VALUE_MASK GENMASK(11, 0) + +#define MESON_SAR_ADC_AUX_SW 0x1c + #define MESON_SAR_ADC_AUX_SW_MUX_SEL_CHAN_SHIFT(_chan) \ + (8 + (((_chan) - 2) * 3)) + #define MESON_SAR_ADC_AUX_SW_VREF_P_MUX BIT(6) + #define MESON_SAR_ADC_AUX_SW_VREF_N_MUX BIT(5) + #define MESON_SAR_ADC_AUX_SW_MODE_SEL BIT(4) + #define MESON_SAR_ADC_AUX_SW_YP_DRIVE_SW BIT(3) + #define MESON_SAR_ADC_AUX_SW_XP_DRIVE_SW BIT(2) + #define MESON_SAR_ADC_AUX_SW_YM_DRIVE_SW BIT(1) + #define MESON_SAR_ADC_AUX_SW_XM_DRIVE_SW BIT(0) + +#define MESON_SAR_ADC_CHAN_10_SW 0x20 + #define MESON_SAR_ADC_CHAN_10_SW_CHAN1_MUX_SEL_MASK GENMASK(25, 23) + #define MESON_SAR_ADC_CHAN_10_SW_CHAN1_VREF_P_MUX BIT(22) + #define MESON_SAR_ADC_CHAN_10_SW_CHAN1_VREF_N_MUX BIT(21) + #define MESON_SAR_ADC_CHAN_10_SW_CHAN1_MODE_SEL BIT(20) + #define MESON_SAR_ADC_CHAN_10_SW_CHAN1_YP_DRIVE_SW BIT(19) + #define MESON_SAR_ADC_CHAN_10_SW_CHAN1_XP_DRIVE_SW BIT(18) + #define MESON_SAR_ADC_CHAN_10_SW_CHAN1_YM_DRIVE_SW BIT(17) + #define MESON_SAR_ADC_CHAN_10_SW_CHAN1_XM_DRIVE_SW BIT(16) + #define MESON_SAR_ADC_CHAN_10_SW_CHAN0_MUX_SEL_MASK GENMASK(9, 7) + #define MESON_SAR_ADC_CHAN_10_SW_CHAN0_VREF_P_MUX BIT(6) + #define MESON_SAR_ADC_CHAN_10_SW_CHAN0_VREF_N_MUX BIT(5) + #define MESON_SAR_ADC_CHAN_10_SW_CHAN0_MODE_SEL BIT(4) + #define MESON_SAR_ADC_CHAN_10_SW_CHAN0_YP_DRIVE_SW BIT(3) + #define MESON_SAR_ADC_CHAN_10_SW_CHAN0_XP_DRIVE_SW BIT(2) + #define MESON_SAR_ADC_CHAN_10_SW_CHAN0_YM_DRIVE_SW BIT(1) + #define MESON_SAR_ADC_CHAN_10_SW_CHAN0_XM_DRIVE_SW BIT(0) + +#define MESON_SAR_ADC_DETECT_IDLE_SW 0x24 + #define MESON_SAR_ADC_DETECT_IDLE_SW_DETECT_SW_EN BIT(26) + #define MESON_SAR_ADC_DETECT_IDLE_SW_DETECT_MUX_MASK GENMASK(25, 23) + #define MESON_SAR_ADC_DETECT_IDLE_SW_DETECT_VREF_P_MUX BIT(22) + #define MESON_SAR_ADC_DETECT_IDLE_SW_DETECT_VREF_N_MUX BIT(21) + #define MESON_SAR_ADC_DETECT_IDLE_SW_DETECT_MODE_SEL BIT(20) + #define MESON_SAR_ADC_DETECT_IDLE_SW_DETECT_YP_DRIVE_SW BIT(19) + #define MESON_SAR_ADC_DETECT_IDLE_SW_DETECT_XP_DRIVE_SW BIT(18) + #define MESON_SAR_ADC_DETECT_IDLE_SW_DETECT_YM_DRIVE_SW BIT(17) + #define MESON_SAR_ADC_DETECT_IDLE_SW_DETECT_XM_DRIVE_SW BIT(16) + #define MESON_SAR_ADC_DETECT_IDLE_SW_IDLE_MUX_SEL_MASK GENMASK(9, 7) + #define MESON_SAR_ADC_DETECT_IDLE_SW_IDLE_VREF_P_MUX BIT(6) + #define MESON_SAR_ADC_DETECT_IDLE_SW_IDLE_VREF_N_MUX BIT(5) + #define MESON_SAR_ADC_DETECT_IDLE_SW_IDLE_MODE_SEL BIT(4) + #define MESON_SAR_ADC_DETECT_IDLE_SW_IDLE_YP_DRIVE_SW BIT(3) + #define MESON_SAR_ADC_DETECT_IDLE_SW_IDLE_XP_DRIVE_SW BIT(2) + #define MESON_SAR_ADC_DETECT_IDLE_SW_IDLE_YM_DRIVE_SW BIT(1) + #define MESON_SAR_ADC_DETECT_IDLE_SW_IDLE_XM_DRIVE_SW BIT(0) + +#define MESON_SAR_ADC_DELTA_10 0x28 + #define MESON_SAR_ADC_DELTA_10_TEMP_SEL BIT(27) + #define MESON_SAR_ADC_DELTA_10_TS_REVE1 BIT(26) + #define MESON_SAR_ADC_DELTA_10_CHAN1_DELTA_VALUE_MASK GENMASK(25, 16) + #define MESON_SAR_ADC_DELTA_10_TS_REVE0 BIT(15) + #define MESON_SAR_ADC_DELTA_10_TS_C_MASK GENMASK(14, 11) + #define MESON_SAR_ADC_DELTA_10_TS_VBG_EN BIT(10) + #define MESON_SAR_ADC_DELTA_10_CHAN0_DELTA_VALUE_MASK GENMASK(9, 0) + +/* + * NOTE: registers from here are undocumented (the vendor Linux kernel driver + * and u-boot source served as reference). These only seem to be relevant on + * GXBB and newer. + */ +#define MESON_SAR_ADC_REG11 0x2c + #define MESON_SAR_ADC_REG11_BANDGAP_EN BIT(13) + #define MESON_SAR_ADC_REG11_CMV_SEL BIT(6) + #define MESON_SAR_ADC_REG11_VREF_VOLTAGE BIT(5) + #define MESON_SAR_ADC_REG11_EOC BIT(1) + #define MESON_SAR_ADC_REG11_VREF_SEL BIT(0) + +#define MESON_SAR_ADC_REG12 0x30 + #define MESON_SAR_ADC_REG12_MPLL0_UNKNOWN BIT(0) + #define MESON_SAR_ADC_REG12_MPLL1_UNKNOWN BIT(1) + #define MESON_SAR_ADC_REG12_MPLL2_UNKNOWN BIT(2) + +#define MESON_SAR_ADC_REG13 0x34 + #define MESON_SAR_ADC_REG13_12BIT_CALIBRATION_MASK GENMASK(13, 8) + +#define MESON_SAR_ADC_MAX_FIFO_SIZE 32 +#define MESON_SAR_ADC_TIMEOUT 100 /* ms */ +#define MESON_SAR_ADC_VOLTAGE_AND_TEMP_CHANNEL 6 +#define MESON_SAR_ADC_VOLTAGE_AND_MUX_CHANNEL 7 +#define MESON_SAR_ADC_TEMP_OFFSET 27 + +/* temperature sensor calibration information in eFuse */ +#define MESON_SAR_ADC_EFUSE_BYTES 4 +#define MESON_SAR_ADC_EFUSE_BYTE3_UPPER_ADC_VAL GENMASK(6, 0) +#define MESON_SAR_ADC_EFUSE_BYTE3_IS_CALIBRATED BIT(7) + +#define MESON_HHI_DPLL_TOP_0 0x318 +#define MESON_HHI_DPLL_TOP_0_TSC_BIT4 BIT(9) + +/* for use with IIO_VAL_INT_PLUS_MICRO */ +#define MILLION 1000000 + +#define MESON_SAR_ADC_CHAN(_chan) { \ + .type = IIO_VOLTAGE, \ + .indexed = 1, \ + .channel = _chan, \ + .address = _chan, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ + BIT(IIO_CHAN_INFO_AVERAGE_RAW), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ + .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_CALIBBIAS) | \ + BIT(IIO_CHAN_INFO_CALIBSCALE), \ + .datasheet_name = "SAR_ADC_CH"#_chan, \ +} + +#define MESON_SAR_ADC_TEMP_CHAN(_chan) { \ + .type = IIO_TEMP, \ + .channel = _chan, \ + .address = MESON_SAR_ADC_VOLTAGE_AND_TEMP_CHANNEL, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ + BIT(IIO_CHAN_INFO_AVERAGE_RAW), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_OFFSET) | \ + BIT(IIO_CHAN_INFO_SCALE), \ + .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_CALIBBIAS) | \ + BIT(IIO_CHAN_INFO_CALIBSCALE), \ + .datasheet_name = "TEMP_SENSOR", \ +} + +#define MESON_SAR_ADC_MUX(_chan, _sel) { \ + .type = IIO_VOLTAGE, \ + .channel = _chan, \ + .indexed = 1, \ + .address = MESON_SAR_ADC_VOLTAGE_AND_MUX_CHANNEL, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ + BIT(IIO_CHAN_INFO_AVERAGE_RAW), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ + .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_CALIBBIAS) | \ + BIT(IIO_CHAN_INFO_CALIBSCALE), \ + .datasheet_name = "SAR_ADC_MUX_"#_sel, \ +} + +enum meson_sar_adc_vref_sel { + VREF_CALIBATION_VOLTAGE = 0, + VREF_VDDA = 1, +}; + +enum meson_sar_adc_avg_mode { + NO_AVERAGING = 0x0, + MEAN_AVERAGING = 0x1, + MEDIAN_AVERAGING = 0x2, +}; + +enum meson_sar_adc_num_samples { + ONE_SAMPLE = 0x0, + TWO_SAMPLES = 0x1, + FOUR_SAMPLES = 0x2, + EIGHT_SAMPLES = 0x3, +}; + +enum meson_sar_adc_chan7_mux_sel { + CHAN7_MUX_VSS = 0x0, + CHAN7_MUX_VDD_DIV4 = 0x1, + CHAN7_MUX_VDD_DIV2 = 0x2, + CHAN7_MUX_VDD_MUL3_DIV4 = 0x3, + CHAN7_MUX_VDD = 0x4, + CHAN7_MUX_CH7_INPUT = 0x7, +}; + +enum meson_sar_adc_channel_index { + NUM_CHAN_0, + NUM_CHAN_1, + NUM_CHAN_2, + NUM_CHAN_3, + NUM_CHAN_4, + NUM_CHAN_5, + NUM_CHAN_6, + NUM_CHAN_7, + NUM_CHAN_TEMP, + NUM_MUX_0_VSS, + NUM_MUX_1_VDD_DIV4, + NUM_MUX_2_VDD_DIV2, + NUM_MUX_3_VDD_MUL3_DIV4, + NUM_MUX_4_VDD, +}; + +static enum meson_sar_adc_chan7_mux_sel chan7_mux_values[] = { + CHAN7_MUX_VSS, + CHAN7_MUX_VDD_DIV4, + CHAN7_MUX_VDD_DIV2, + CHAN7_MUX_VDD_MUL3_DIV4, + CHAN7_MUX_VDD, +}; + +static const char * const chan7_mux_names[] = { + [CHAN7_MUX_VSS] = "gnd", + [CHAN7_MUX_VDD_DIV4] = "0.25vdd", + [CHAN7_MUX_VDD_DIV2] = "0.5vdd", + [CHAN7_MUX_VDD_MUL3_DIV4] = "0.75vdd", + [CHAN7_MUX_VDD] = "vdd", +}; + +static const struct iio_chan_spec meson_sar_adc_iio_channels[] = { + MESON_SAR_ADC_CHAN(NUM_CHAN_0), + MESON_SAR_ADC_CHAN(NUM_CHAN_1), + MESON_SAR_ADC_CHAN(NUM_CHAN_2), + MESON_SAR_ADC_CHAN(NUM_CHAN_3), + MESON_SAR_ADC_CHAN(NUM_CHAN_4), + MESON_SAR_ADC_CHAN(NUM_CHAN_5), + MESON_SAR_ADC_CHAN(NUM_CHAN_6), + MESON_SAR_ADC_CHAN(NUM_CHAN_7), + MESON_SAR_ADC_MUX(NUM_MUX_0_VSS, 0), + MESON_SAR_ADC_MUX(NUM_MUX_1_VDD_DIV4, 1), + MESON_SAR_ADC_MUX(NUM_MUX_2_VDD_DIV2, 2), + MESON_SAR_ADC_MUX(NUM_MUX_3_VDD_MUL3_DIV4, 3), + MESON_SAR_ADC_MUX(NUM_MUX_4_VDD, 4), +}; + +static const struct iio_chan_spec meson_sar_adc_and_temp_iio_channels[] = { + MESON_SAR_ADC_CHAN(NUM_CHAN_0), + MESON_SAR_ADC_CHAN(NUM_CHAN_1), + MESON_SAR_ADC_CHAN(NUM_CHAN_2), + MESON_SAR_ADC_CHAN(NUM_CHAN_3), + MESON_SAR_ADC_CHAN(NUM_CHAN_4), + MESON_SAR_ADC_CHAN(NUM_CHAN_5), + MESON_SAR_ADC_CHAN(NUM_CHAN_6), + MESON_SAR_ADC_CHAN(NUM_CHAN_7), + MESON_SAR_ADC_TEMP_CHAN(NUM_CHAN_TEMP), + MESON_SAR_ADC_MUX(NUM_MUX_0_VSS, 0), + MESON_SAR_ADC_MUX(NUM_MUX_1_VDD_DIV4, 1), + MESON_SAR_ADC_MUX(NUM_MUX_2_VDD_DIV2, 2), + MESON_SAR_ADC_MUX(NUM_MUX_3_VDD_MUL3_DIV4, 3), + MESON_SAR_ADC_MUX(NUM_MUX_4_VDD, 4), +}; + +struct meson_sar_adc_param { + bool has_bl30_integration; + unsigned long clock_rate; + unsigned int resolution; + const struct regmap_config *regmap_config; + u8 temperature_trimming_bits; + unsigned int temperature_multiplier; + unsigned int temperature_divider; + u8 disable_ring_counter; + bool has_vref_select; + u8 vref_select; + u8 cmv_select; + u8 adc_eoc; + enum meson_sar_adc_vref_sel vref_voltage; + bool enable_mpll_clock_workaround; +}; + +struct meson_sar_adc_data { + const struct meson_sar_adc_param *param; + const char *name; +}; + +struct meson_sar_adc_priv { + struct regmap *regmap; + struct regulator *vref; + const struct meson_sar_adc_param *param; + struct clk *clkin; + struct clk *core_clk; + struct clk *adc_sel_clk; + struct clk *adc_clk; + struct clk_gate clk_gate; + struct clk *adc_div_clk; + struct clk_divider clk_div; + struct completion done; + /* lock to protect against multiple access to the device */ + struct mutex lock; + int calibbias; + int calibscale; + struct regmap *tsc_regmap; + bool temperature_sensor_calibrated; + u8 temperature_sensor_coefficient; + u16 temperature_sensor_adc_val; + enum meson_sar_adc_chan7_mux_sel chan7_mux_sel; +}; + +static const struct regmap_config meson_sar_adc_regmap_config_gxbb = { + .reg_bits = 8, + .val_bits = 32, + .reg_stride = 4, + .max_register = MESON_SAR_ADC_REG13, +}; + +static const struct regmap_config meson_sar_adc_regmap_config_meson8 = { + .reg_bits = 8, + .val_bits = 32, + .reg_stride = 4, + .max_register = MESON_SAR_ADC_DELTA_10, +}; + +static const struct iio_chan_spec * +find_channel_by_num(struct iio_dev *indio_dev, int num) +{ + int i; + + for (i = 0; i < indio_dev->num_channels; i++) + if (indio_dev->channels[i].channel == num) + return &indio_dev->channels[i]; + return NULL; +} + +static unsigned int meson_sar_adc_get_fifo_count(struct iio_dev *indio_dev) +{ + struct meson_sar_adc_priv *priv = iio_priv(indio_dev); + u32 regval; + + regmap_read(priv->regmap, MESON_SAR_ADC_REG0, ®val); + + return FIELD_GET(MESON_SAR_ADC_REG0_FIFO_COUNT_MASK, regval); +} + +static int meson_sar_adc_calib_val(struct iio_dev *indio_dev, int val) +{ + struct meson_sar_adc_priv *priv = iio_priv(indio_dev); + int tmp; + + /* use val_calib = scale * val_raw + offset calibration function */ + tmp = div_s64((s64)val * priv->calibscale, MILLION) + priv->calibbias; + + return clamp(tmp, 0, (1 << priv->param->resolution) - 1); +} + +static int meson_sar_adc_wait_busy_clear(struct iio_dev *indio_dev) +{ + struct meson_sar_adc_priv *priv = iio_priv(indio_dev); + int val; + + /* + * NOTE: we need a small delay before reading the status, otherwise + * the sample engine may not have started internally (which would + * seem to us that sampling is already finished). + */ + udelay(1); + return regmap_read_poll_timeout_atomic(priv->regmap, MESON_SAR_ADC_REG0, val, + !FIELD_GET(MESON_SAR_ADC_REG0_BUSY_MASK, val), + 1, 10000); +} + +static void meson_sar_adc_set_chan7_mux(struct iio_dev *indio_dev, + enum meson_sar_adc_chan7_mux_sel sel) +{ + struct meson_sar_adc_priv *priv = iio_priv(indio_dev); + u32 regval; + + regval = FIELD_PREP(MESON_SAR_ADC_REG3_CTRL_CHAN7_MUX_SEL_MASK, sel); + regmap_update_bits(priv->regmap, MESON_SAR_ADC_REG3, + MESON_SAR_ADC_REG3_CTRL_CHAN7_MUX_SEL_MASK, regval); + + usleep_range(10, 20); + + priv->chan7_mux_sel = sel; +} + +static int meson_sar_adc_read_raw_sample(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + int *val) +{ + struct meson_sar_adc_priv *priv = iio_priv(indio_dev); + struct device *dev = indio_dev->dev.parent; + int regval, fifo_chan, fifo_val, count; + + if (!wait_for_completion_timeout(&priv->done, + msecs_to_jiffies(MESON_SAR_ADC_TIMEOUT))) + return -ETIMEDOUT; + + count = meson_sar_adc_get_fifo_count(indio_dev); + if (count != 1) { + dev_err(dev, "ADC FIFO has %d element(s) instead of one\n", count); + return -EINVAL; + } + + regmap_read(priv->regmap, MESON_SAR_ADC_FIFO_RD, ®val); + fifo_chan = FIELD_GET(MESON_SAR_ADC_FIFO_RD_CHAN_ID_MASK, regval); + if (fifo_chan != chan->address) { + dev_err(dev, "ADC FIFO entry belongs to channel %d instead of %lu\n", + fifo_chan, chan->address); + return -EINVAL; + } + + fifo_val = FIELD_GET(MESON_SAR_ADC_FIFO_RD_SAMPLE_VALUE_MASK, regval); + fifo_val &= GENMASK(priv->param->resolution - 1, 0); + *val = meson_sar_adc_calib_val(indio_dev, fifo_val); + + return 0; +} + +static void meson_sar_adc_set_averaging(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum meson_sar_adc_avg_mode mode, + enum meson_sar_adc_num_samples samples) +{ + struct meson_sar_adc_priv *priv = iio_priv(indio_dev); + int val, address = chan->address; + + val = samples << MESON_SAR_ADC_AVG_CNTL_NUM_SAMPLES_SHIFT(address); + regmap_update_bits(priv->regmap, MESON_SAR_ADC_AVG_CNTL, + MESON_SAR_ADC_AVG_CNTL_NUM_SAMPLES_MASK(address), + val); + + val = mode << MESON_SAR_ADC_AVG_CNTL_AVG_MODE_SHIFT(address); + regmap_update_bits(priv->regmap, MESON_SAR_ADC_AVG_CNTL, + MESON_SAR_ADC_AVG_CNTL_AVG_MODE_MASK(address), val); +} + +static void meson_sar_adc_enable_channel(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan) +{ + struct meson_sar_adc_priv *priv = iio_priv(indio_dev); + u32 regval; + + /* + * the SAR ADC engine allows sampling multiple channels at the same + * time. to keep it simple we're only working with one *internal* + * channel, which starts counting at index 0 (which means: count = 1). + */ + regval = FIELD_PREP(MESON_SAR_ADC_CHAN_LIST_MAX_INDEX_MASK, 0); + regmap_update_bits(priv->regmap, MESON_SAR_ADC_CHAN_LIST, + MESON_SAR_ADC_CHAN_LIST_MAX_INDEX_MASK, regval); + + /* map channel index 0 to the channel which we want to read */ + regval = FIELD_PREP(MESON_SAR_ADC_CHAN_LIST_ENTRY_MASK(0), + chan->address); + regmap_update_bits(priv->regmap, MESON_SAR_ADC_CHAN_LIST, + MESON_SAR_ADC_CHAN_LIST_ENTRY_MASK(0), regval); + + regval = FIELD_PREP(MESON_SAR_ADC_DETECT_IDLE_SW_DETECT_MUX_MASK, + chan->address); + regmap_update_bits(priv->regmap, MESON_SAR_ADC_DETECT_IDLE_SW, + MESON_SAR_ADC_DETECT_IDLE_SW_DETECT_MUX_MASK, + regval); + + regval = FIELD_PREP(MESON_SAR_ADC_DETECT_IDLE_SW_IDLE_MUX_SEL_MASK, + chan->address); + regmap_update_bits(priv->regmap, MESON_SAR_ADC_DETECT_IDLE_SW, + MESON_SAR_ADC_DETECT_IDLE_SW_IDLE_MUX_SEL_MASK, + regval); + + if (chan->address == MESON_SAR_ADC_VOLTAGE_AND_TEMP_CHANNEL) { + if (chan->type == IIO_TEMP) + regval = MESON_SAR_ADC_DELTA_10_TEMP_SEL; + else + regval = 0; + + regmap_update_bits(priv->regmap, + MESON_SAR_ADC_DELTA_10, + MESON_SAR_ADC_DELTA_10_TEMP_SEL, regval); + } else if (chan->address == MESON_SAR_ADC_VOLTAGE_AND_MUX_CHANNEL) { + enum meson_sar_adc_chan7_mux_sel sel; + + if (chan->channel == NUM_CHAN_7) + sel = CHAN7_MUX_CH7_INPUT; + else + sel = chan7_mux_values[chan->channel - NUM_MUX_0_VSS]; + if (sel != priv->chan7_mux_sel) + meson_sar_adc_set_chan7_mux(indio_dev, sel); + } +} + +static void meson_sar_adc_start_sample_engine(struct iio_dev *indio_dev) +{ + struct meson_sar_adc_priv *priv = iio_priv(indio_dev); + + reinit_completion(&priv->done); + + regmap_set_bits(priv->regmap, MESON_SAR_ADC_REG0, + MESON_SAR_ADC_REG0_FIFO_IRQ_EN); + + regmap_set_bits(priv->regmap, MESON_SAR_ADC_REG0, + MESON_SAR_ADC_REG0_SAMPLE_ENGINE_ENABLE); + + regmap_set_bits(priv->regmap, MESON_SAR_ADC_REG0, + MESON_SAR_ADC_REG0_SAMPLING_START); +} + +static void meson_sar_adc_stop_sample_engine(struct iio_dev *indio_dev) +{ + struct meson_sar_adc_priv *priv = iio_priv(indio_dev); + + regmap_clear_bits(priv->regmap, MESON_SAR_ADC_REG0, + MESON_SAR_ADC_REG0_FIFO_IRQ_EN); + + regmap_set_bits(priv->regmap, MESON_SAR_ADC_REG0, + MESON_SAR_ADC_REG0_SAMPLING_STOP); + + /* wait until all modules are stopped */ + meson_sar_adc_wait_busy_clear(indio_dev); + + regmap_clear_bits(priv->regmap, MESON_SAR_ADC_REG0, + MESON_SAR_ADC_REG0_SAMPLE_ENGINE_ENABLE); +} + +static int meson_sar_adc_lock(struct iio_dev *indio_dev) +{ + struct meson_sar_adc_priv *priv = iio_priv(indio_dev); + int val, ret; + + mutex_lock(&priv->lock); + + if (priv->param->has_bl30_integration) { + /* prevent BL30 from using the SAR ADC while we are using it */ + regmap_set_bits(priv->regmap, MESON_SAR_ADC_DELAY, + MESON_SAR_ADC_DELAY_KERNEL_BUSY); + + udelay(1); + + /* + * wait until BL30 releases it's lock (so we can use the SAR + * ADC) + */ + ret = regmap_read_poll_timeout_atomic(priv->regmap, MESON_SAR_ADC_DELAY, val, + !(val & MESON_SAR_ADC_DELAY_BL30_BUSY), + 1, 10000); + if (ret) { + mutex_unlock(&priv->lock); + return ret; + } + } + + return 0; +} + +static void meson_sar_adc_unlock(struct iio_dev *indio_dev) +{ + struct meson_sar_adc_priv *priv = iio_priv(indio_dev); + + if (priv->param->has_bl30_integration) + /* allow BL30 to use the SAR ADC again */ + regmap_clear_bits(priv->regmap, MESON_SAR_ADC_DELAY, + MESON_SAR_ADC_DELAY_KERNEL_BUSY); + + mutex_unlock(&priv->lock); +} + +static void meson_sar_adc_clear_fifo(struct iio_dev *indio_dev) +{ + struct meson_sar_adc_priv *priv = iio_priv(indio_dev); + unsigned int count, tmp; + + for (count = 0; count < MESON_SAR_ADC_MAX_FIFO_SIZE; count++) { + if (!meson_sar_adc_get_fifo_count(indio_dev)) + break; + + regmap_read(priv->regmap, MESON_SAR_ADC_FIFO_RD, &tmp); + } +} + +static int meson_sar_adc_get_sample(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum meson_sar_adc_avg_mode avg_mode, + enum meson_sar_adc_num_samples avg_samples, + int *val) +{ + struct meson_sar_adc_priv *priv = iio_priv(indio_dev); + struct device *dev = indio_dev->dev.parent; + int ret; + + if (chan->type == IIO_TEMP && !priv->temperature_sensor_calibrated) + return -ENOTSUPP; + + ret = meson_sar_adc_lock(indio_dev); + if (ret) + return ret; + + /* clear the FIFO to make sure we're not reading old values */ + meson_sar_adc_clear_fifo(indio_dev); + + meson_sar_adc_set_averaging(indio_dev, chan, avg_mode, avg_samples); + + meson_sar_adc_enable_channel(indio_dev, chan); + + meson_sar_adc_start_sample_engine(indio_dev); + ret = meson_sar_adc_read_raw_sample(indio_dev, chan, val); + meson_sar_adc_stop_sample_engine(indio_dev); + + meson_sar_adc_unlock(indio_dev); + + if (ret) { + dev_warn(dev, "failed to read sample for channel %lu: %d\n", + chan->address, ret); + return ret; + } + + return IIO_VAL_INT; +} + +static int meson_sar_adc_iio_info_read_raw(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + int *val, int *val2, long mask) +{ + struct meson_sar_adc_priv *priv = iio_priv(indio_dev); + struct device *dev = indio_dev->dev.parent; + int ret; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + return meson_sar_adc_get_sample(indio_dev, chan, NO_AVERAGING, + ONE_SAMPLE, val); + + case IIO_CHAN_INFO_AVERAGE_RAW: + return meson_sar_adc_get_sample(indio_dev, chan, + MEAN_AVERAGING, EIGHT_SAMPLES, + val); + + case IIO_CHAN_INFO_SCALE: + if (chan->type == IIO_VOLTAGE) { + ret = regulator_get_voltage(priv->vref); + if (ret < 0) { + dev_err(dev, "failed to get vref voltage: %d\n", ret); + return ret; + } + + *val = ret / 1000; + *val2 = priv->param->resolution; + return IIO_VAL_FRACTIONAL_LOG2; + } else if (chan->type == IIO_TEMP) { + /* SoC specific multiplier and divider */ + *val = priv->param->temperature_multiplier; + *val2 = priv->param->temperature_divider; + + /* celsius to millicelsius */ + *val *= 1000; + + return IIO_VAL_FRACTIONAL; + } else { + return -EINVAL; + } + + case IIO_CHAN_INFO_CALIBBIAS: + *val = priv->calibbias; + return IIO_VAL_INT; + + case IIO_CHAN_INFO_CALIBSCALE: + *val = priv->calibscale / MILLION; + *val2 = priv->calibscale % MILLION; + return IIO_VAL_INT_PLUS_MICRO; + + case IIO_CHAN_INFO_OFFSET: + *val = DIV_ROUND_CLOSEST(MESON_SAR_ADC_TEMP_OFFSET * + priv->param->temperature_divider, + priv->param->temperature_multiplier); + *val -= priv->temperature_sensor_adc_val; + return IIO_VAL_INT; + + default: + return -EINVAL; + } +} + +static int meson_sar_adc_clk_init(struct iio_dev *indio_dev, + void __iomem *base) +{ + struct meson_sar_adc_priv *priv = iio_priv(indio_dev); + struct device *dev = indio_dev->dev.parent; + struct clk_init_data init; + const char *clk_parents[1]; + + init.name = devm_kasprintf(dev, GFP_KERNEL, "%s#adc_div", dev_name(dev)); + if (!init.name) + return -ENOMEM; + + init.flags = 0; + init.ops = &clk_divider_ops; + clk_parents[0] = __clk_get_name(priv->clkin); + init.parent_names = clk_parents; + init.num_parents = 1; + + priv->clk_div.reg = base + MESON_SAR_ADC_REG3; + priv->clk_div.shift = MESON_SAR_ADC_REG3_ADC_CLK_DIV_SHIFT; + priv->clk_div.width = MESON_SAR_ADC_REG3_ADC_CLK_DIV_WIDTH; + priv->clk_div.hw.init = &init; + priv->clk_div.flags = 0; + + priv->adc_div_clk = devm_clk_register(dev, &priv->clk_div.hw); + if (WARN_ON(IS_ERR(priv->adc_div_clk))) + return PTR_ERR(priv->adc_div_clk); + + init.name = devm_kasprintf(dev, GFP_KERNEL, "%s#adc_en", dev_name(dev)); + if (!init.name) + return -ENOMEM; + + init.flags = CLK_SET_RATE_PARENT; + init.ops = &clk_gate_ops; + clk_parents[0] = __clk_get_name(priv->adc_div_clk); + init.parent_names = clk_parents; + init.num_parents = 1; + + priv->clk_gate.reg = base + MESON_SAR_ADC_REG3; + priv->clk_gate.bit_idx = __ffs(MESON_SAR_ADC_REG3_CLK_EN); + priv->clk_gate.hw.init = &init; + + priv->adc_clk = devm_clk_register(dev, &priv->clk_gate.hw); + if (WARN_ON(IS_ERR(priv->adc_clk))) + return PTR_ERR(priv->adc_clk); + + return 0; +} + +static int meson_sar_adc_temp_sensor_init(struct iio_dev *indio_dev) +{ + struct meson_sar_adc_priv *priv = iio_priv(indio_dev); + u8 *buf, trimming_bits, trimming_mask, upper_adc_val; + struct device *dev = indio_dev->dev.parent; + struct nvmem_cell *temperature_calib; + size_t read_len; + int ret; + + temperature_calib = nvmem_cell_get(dev, "temperature_calib"); + if (IS_ERR(temperature_calib)) { + ret = PTR_ERR(temperature_calib); + + /* + * leave the temperature sensor disabled if no calibration data + * was passed via nvmem-cells. + */ + if (ret == -ENODEV) + return 0; + + return dev_err_probe(dev, ret, "failed to get temperature_calib cell\n"); + } + + read_len = MESON_SAR_ADC_EFUSE_BYTES; + buf = nvmem_cell_read(temperature_calib, &read_len); + nvmem_cell_put(temperature_calib); + if (IS_ERR(buf)) + return dev_err_probe(dev, PTR_ERR(buf), "failed to read temperature_calib cell\n"); + if (read_len != MESON_SAR_ADC_EFUSE_BYTES) { + kfree(buf); + return dev_err_probe(dev, -EINVAL, "invalid read size of temperature_calib cell\n"); + } + + priv->tsc_regmap = syscon_regmap_lookup_by_phandle(dev->of_node, "amlogic,hhi-sysctrl"); + if (IS_ERR(priv->tsc_regmap)) { + kfree(buf); + return dev_err_probe(dev, PTR_ERR(priv->tsc_regmap), + "failed to get amlogic,hhi-sysctrl regmap\n"); + } + + trimming_bits = priv->param->temperature_trimming_bits; + trimming_mask = BIT(trimming_bits) - 1; + + priv->temperature_sensor_calibrated = + buf[3] & MESON_SAR_ADC_EFUSE_BYTE3_IS_CALIBRATED; + priv->temperature_sensor_coefficient = buf[2] & trimming_mask; + + upper_adc_val = FIELD_GET(MESON_SAR_ADC_EFUSE_BYTE3_UPPER_ADC_VAL, + buf[3]); + + priv->temperature_sensor_adc_val = buf[2]; + priv->temperature_sensor_adc_val |= upper_adc_val << BITS_PER_BYTE; + priv->temperature_sensor_adc_val >>= trimming_bits; + + kfree(buf); + + return 0; +} + +static int meson_sar_adc_init(struct iio_dev *indio_dev) +{ + struct meson_sar_adc_priv *priv = iio_priv(indio_dev); + struct device *dev = indio_dev->dev.parent; + int regval, i, ret; + + /* + * make sure we start at CH7 input since the other muxes are only used + * for internal calibration. + */ + meson_sar_adc_set_chan7_mux(indio_dev, CHAN7_MUX_CH7_INPUT); + + if (priv->param->has_bl30_integration) { + /* + * leave sampling delay and the input clocks as configured by + * BL30 to make sure BL30 gets the values it expects when + * reading the temperature sensor. + */ + regmap_read(priv->regmap, MESON_SAR_ADC_REG3, ®val); + if (regval & MESON_SAR_ADC_REG3_BL30_INITIALIZED) + return 0; + } + + meson_sar_adc_stop_sample_engine(indio_dev); + + /* + * disable this bit as seems to be only relevant for Meson6 (based + * on the vendor driver), which we don't support at the moment. + */ + regmap_clear_bits(priv->regmap, MESON_SAR_ADC_REG0, + MESON_SAR_ADC_REG0_ADC_TEMP_SEN_SEL); + + /* disable all channels by default */ + regmap_write(priv->regmap, MESON_SAR_ADC_CHAN_LIST, 0x0); + + regmap_clear_bits(priv->regmap, MESON_SAR_ADC_REG3, + MESON_SAR_ADC_REG3_CTRL_SAMPLING_CLOCK_PHASE); + regmap_set_bits(priv->regmap, MESON_SAR_ADC_REG3, + MESON_SAR_ADC_REG3_CNTL_USE_SC_DLY); + + /* delay between two samples = (10+1) * 1uS */ + regmap_update_bits(priv->regmap, MESON_SAR_ADC_DELAY, + MESON_SAR_ADC_DELAY_INPUT_DLY_CNT_MASK, + FIELD_PREP(MESON_SAR_ADC_DELAY_SAMPLE_DLY_CNT_MASK, + 10)); + regmap_update_bits(priv->regmap, MESON_SAR_ADC_DELAY, + MESON_SAR_ADC_DELAY_SAMPLE_DLY_SEL_MASK, + FIELD_PREP(MESON_SAR_ADC_DELAY_SAMPLE_DLY_SEL_MASK, + 0)); + + /* delay between two samples = (10+1) * 1uS */ + regmap_update_bits(priv->regmap, MESON_SAR_ADC_DELAY, + MESON_SAR_ADC_DELAY_INPUT_DLY_CNT_MASK, + FIELD_PREP(MESON_SAR_ADC_DELAY_INPUT_DLY_CNT_MASK, + 10)); + regmap_update_bits(priv->regmap, MESON_SAR_ADC_DELAY, + MESON_SAR_ADC_DELAY_INPUT_DLY_SEL_MASK, + FIELD_PREP(MESON_SAR_ADC_DELAY_INPUT_DLY_SEL_MASK, + 1)); + + /* + * set up the input channel muxes in MESON_SAR_ADC_CHAN_10_SW + * (0 = SAR_ADC_CH0, 1 = SAR_ADC_CH1) + */ + regval = FIELD_PREP(MESON_SAR_ADC_CHAN_10_SW_CHAN0_MUX_SEL_MASK, 0); + regmap_update_bits(priv->regmap, MESON_SAR_ADC_CHAN_10_SW, + MESON_SAR_ADC_CHAN_10_SW_CHAN0_MUX_SEL_MASK, + regval); + regval = FIELD_PREP(MESON_SAR_ADC_CHAN_10_SW_CHAN1_MUX_SEL_MASK, 1); + regmap_update_bits(priv->regmap, MESON_SAR_ADC_CHAN_10_SW, + MESON_SAR_ADC_CHAN_10_SW_CHAN1_MUX_SEL_MASK, + regval); + + regmap_set_bits(priv->regmap, MESON_SAR_ADC_CHAN_10_SW, + MESON_SAR_ADC_CHAN_10_SW_CHAN0_XP_DRIVE_SW); + + regmap_set_bits(priv->regmap, MESON_SAR_ADC_CHAN_10_SW, + MESON_SAR_ADC_CHAN_10_SW_CHAN0_YP_DRIVE_SW); + + regmap_set_bits(priv->regmap, MESON_SAR_ADC_CHAN_10_SW, + MESON_SAR_ADC_CHAN_10_SW_CHAN1_XP_DRIVE_SW); + + regmap_set_bits(priv->regmap, MESON_SAR_ADC_CHAN_10_SW, + MESON_SAR_ADC_CHAN_10_SW_CHAN1_YP_DRIVE_SW); + + /* + * set up the input channel muxes in MESON_SAR_ADC_AUX_SW + * (2 = SAR_ADC_CH2, 3 = SAR_ADC_CH3, ...) and enable + * MESON_SAR_ADC_AUX_SW_YP_DRIVE_SW and + * MESON_SAR_ADC_AUX_SW_XP_DRIVE_SW like the vendor driver. + */ + regval = 0; + for (i = 2; i <= 7; i++) + regval |= i << MESON_SAR_ADC_AUX_SW_MUX_SEL_CHAN_SHIFT(i); + regval |= MESON_SAR_ADC_AUX_SW_YP_DRIVE_SW; + regval |= MESON_SAR_ADC_AUX_SW_XP_DRIVE_SW; + regmap_write(priv->regmap, MESON_SAR_ADC_AUX_SW, regval); + + if (priv->temperature_sensor_calibrated) { + regmap_set_bits(priv->regmap, MESON_SAR_ADC_DELTA_10, + MESON_SAR_ADC_DELTA_10_TS_REVE1); + regmap_set_bits(priv->regmap, MESON_SAR_ADC_DELTA_10, + MESON_SAR_ADC_DELTA_10_TS_REVE0); + + /* + * set bits [3:0] of the TSC (temperature sensor coefficient) + * to get the correct values when reading the temperature. + */ + regval = FIELD_PREP(MESON_SAR_ADC_DELTA_10_TS_C_MASK, + priv->temperature_sensor_coefficient); + regmap_update_bits(priv->regmap, MESON_SAR_ADC_DELTA_10, + MESON_SAR_ADC_DELTA_10_TS_C_MASK, regval); + + if (priv->param->temperature_trimming_bits == 5) { + if (priv->temperature_sensor_coefficient & BIT(4)) + regval = MESON_HHI_DPLL_TOP_0_TSC_BIT4; + else + regval = 0; + + /* + * bit [4] (the 5th bit when starting to count at 1) + * of the TSC is located in the HHI register area. + */ + regmap_update_bits(priv->tsc_regmap, + MESON_HHI_DPLL_TOP_0, + MESON_HHI_DPLL_TOP_0_TSC_BIT4, + regval); + } + } else { + regmap_clear_bits(priv->regmap, MESON_SAR_ADC_DELTA_10, + MESON_SAR_ADC_DELTA_10_TS_REVE1); + regmap_clear_bits(priv->regmap, MESON_SAR_ADC_DELTA_10, + MESON_SAR_ADC_DELTA_10_TS_REVE0); + } + + regval = FIELD_PREP(MESON_SAR_ADC_REG3_CTRL_CONT_RING_COUNTER_EN, + priv->param->disable_ring_counter); + regmap_update_bits(priv->regmap, MESON_SAR_ADC_REG3, + MESON_SAR_ADC_REG3_CTRL_CONT_RING_COUNTER_EN, + regval); + + if (priv->param->regmap_config->max_register >= MESON_SAR_ADC_REG11) { + regval = FIELD_PREP(MESON_SAR_ADC_REG11_EOC, priv->param->adc_eoc); + regmap_update_bits(priv->regmap, MESON_SAR_ADC_REG11, + MESON_SAR_ADC_REG11_EOC, regval); + + if (priv->param->has_vref_select) { + regval = FIELD_PREP(MESON_SAR_ADC_REG11_VREF_SEL, + priv->param->vref_select); + regmap_update_bits(priv->regmap, MESON_SAR_ADC_REG11, + MESON_SAR_ADC_REG11_VREF_SEL, regval); + } + + regval = FIELD_PREP(MESON_SAR_ADC_REG11_VREF_VOLTAGE, + priv->param->vref_voltage); + regmap_update_bits(priv->regmap, MESON_SAR_ADC_REG11, + MESON_SAR_ADC_REG11_VREF_VOLTAGE, regval); + + regval = FIELD_PREP(MESON_SAR_ADC_REG11_CMV_SEL, + priv->param->cmv_select); + regmap_update_bits(priv->regmap, MESON_SAR_ADC_REG11, + MESON_SAR_ADC_REG11_CMV_SEL, regval); + + if (priv->param->enable_mpll_clock_workaround) { + dev_warn(dev, + "Enabling unknown bits to make the MPLL clocks work. This may change so always update dtbs and kernel together\n"); + regmap_write(priv->regmap, MESON_SAR_ADC_REG12, + MESON_SAR_ADC_REG12_MPLL0_UNKNOWN | + MESON_SAR_ADC_REG12_MPLL1_UNKNOWN | + MESON_SAR_ADC_REG12_MPLL2_UNKNOWN); + } + } + + ret = clk_set_parent(priv->adc_sel_clk, priv->clkin); + if (ret) + return dev_err_probe(dev, ret, "failed to set adc parent to clkin\n"); + + ret = clk_set_rate(priv->adc_clk, priv->param->clock_rate); + if (ret) + return dev_err_probe(dev, ret, "failed to set adc clock rate\n"); + + return 0; +} + +static void meson_sar_adc_set_bandgap(struct iio_dev *indio_dev, bool on_off) +{ + struct meson_sar_adc_priv *priv = iio_priv(indio_dev); + + if (priv->param->regmap_config->max_register >= MESON_SAR_ADC_REG11) + regmap_update_bits(priv->regmap, MESON_SAR_ADC_REG11, + MESON_SAR_ADC_REG11_BANDGAP_EN, + on_off ? MESON_SAR_ADC_REG11_BANDGAP_EN : 0); + else + regmap_update_bits(priv->regmap, MESON_SAR_ADC_DELTA_10, + MESON_SAR_ADC_DELTA_10_TS_VBG_EN, + on_off ? MESON_SAR_ADC_DELTA_10_TS_VBG_EN : 0); +} + +static int meson_sar_adc_hw_enable(struct iio_dev *indio_dev) +{ + struct meson_sar_adc_priv *priv = iio_priv(indio_dev); + struct device *dev = indio_dev->dev.parent; + int ret; + u32 regval; + + ret = meson_sar_adc_lock(indio_dev); + if (ret) { + dev_err(dev, "failed to lock adc\n"); + goto err_lock; + } + + ret = regulator_enable(priv->vref); + if (ret < 0) { + dev_err(dev, "failed to enable vref regulator\n"); + goto err_vref; + } + + regval = FIELD_PREP(MESON_SAR_ADC_REG0_FIFO_CNT_IRQ_MASK, 1); + regmap_update_bits(priv->regmap, MESON_SAR_ADC_REG0, + MESON_SAR_ADC_REG0_FIFO_CNT_IRQ_MASK, regval); + + meson_sar_adc_set_bandgap(indio_dev, true); + + regmap_set_bits(priv->regmap, MESON_SAR_ADC_REG3, + MESON_SAR_ADC_REG3_ADC_EN); + + udelay(5); + + ret = clk_prepare_enable(priv->adc_clk); + if (ret) { + dev_err(dev, "failed to enable adc clk\n"); + goto err_adc_clk; + } + + meson_sar_adc_unlock(indio_dev); + + return 0; + +err_adc_clk: + regmap_clear_bits(priv->regmap, MESON_SAR_ADC_REG3, + MESON_SAR_ADC_REG3_ADC_EN); + meson_sar_adc_set_bandgap(indio_dev, false); + regulator_disable(priv->vref); +err_vref: + meson_sar_adc_unlock(indio_dev); +err_lock: + return ret; +} + +static void meson_sar_adc_hw_disable(struct iio_dev *indio_dev) +{ + struct meson_sar_adc_priv *priv = iio_priv(indio_dev); + int ret; + + /* + * If taking the lock fails we have to assume that BL30 is broken. The + * best we can do then is to release the resources anyhow. + */ + ret = meson_sar_adc_lock(indio_dev); + if (ret) + dev_err(indio_dev->dev.parent, "Failed to lock ADC (%pE)\n", ERR_PTR(ret)); + + clk_disable_unprepare(priv->adc_clk); + + regmap_clear_bits(priv->regmap, MESON_SAR_ADC_REG3, + MESON_SAR_ADC_REG3_ADC_EN); + + meson_sar_adc_set_bandgap(indio_dev, false); + + regulator_disable(priv->vref); + + if (!ret) + meson_sar_adc_unlock(indio_dev); +} + +static irqreturn_t meson_sar_adc_irq(int irq, void *data) +{ + struct iio_dev *indio_dev = data; + struct meson_sar_adc_priv *priv = iio_priv(indio_dev); + unsigned int cnt, threshold; + u32 regval; + + regmap_read(priv->regmap, MESON_SAR_ADC_REG0, ®val); + cnt = FIELD_GET(MESON_SAR_ADC_REG0_FIFO_COUNT_MASK, regval); + threshold = FIELD_GET(MESON_SAR_ADC_REG0_FIFO_CNT_IRQ_MASK, regval); + + if (cnt < threshold) + return IRQ_NONE; + + complete(&priv->done); + + return IRQ_HANDLED; +} + +static int meson_sar_adc_calib(struct iio_dev *indio_dev) +{ + struct meson_sar_adc_priv *priv = iio_priv(indio_dev); + int ret, nominal0, nominal1, value0, value1; + + /* use points 25% and 75% for calibration */ + nominal0 = (1 << priv->param->resolution) / 4; + nominal1 = (1 << priv->param->resolution) * 3 / 4; + + meson_sar_adc_set_chan7_mux(indio_dev, CHAN7_MUX_VDD_DIV4); + usleep_range(10, 20); + ret = meson_sar_adc_get_sample(indio_dev, + find_channel_by_num(indio_dev, + NUM_MUX_1_VDD_DIV4), + MEAN_AVERAGING, EIGHT_SAMPLES, &value0); + if (ret < 0) + goto out; + + meson_sar_adc_set_chan7_mux(indio_dev, CHAN7_MUX_VDD_MUL3_DIV4); + usleep_range(10, 20); + ret = meson_sar_adc_get_sample(indio_dev, + find_channel_by_num(indio_dev, + NUM_MUX_3_VDD_MUL3_DIV4), + MEAN_AVERAGING, EIGHT_SAMPLES, &value1); + if (ret < 0) + goto out; + + if (value1 <= value0) { + ret = -EINVAL; + goto out; + } + + priv->calibscale = div_s64((nominal1 - nominal0) * (s64)MILLION, + value1 - value0); + priv->calibbias = nominal0 - div_s64((s64)value0 * priv->calibscale, + MILLION); + ret = 0; +out: + meson_sar_adc_set_chan7_mux(indio_dev, CHAN7_MUX_CH7_INPUT); + + return ret; +} + +static int read_label(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + char *label) +{ + if (chan->type == IIO_TEMP) + return sysfs_emit(label, "temp-sensor\n"); + if (chan->type == IIO_VOLTAGE && chan->channel >= NUM_MUX_0_VSS) + return sysfs_emit(label, "%s\n", + chan7_mux_names[chan->channel - NUM_MUX_0_VSS]); + if (chan->type == IIO_VOLTAGE) + return sysfs_emit(label, "channel-%d\n", chan->channel); + return 0; +} + +static const struct iio_info meson_sar_adc_iio_info = { + .read_raw = meson_sar_adc_iio_info_read_raw, + .read_label = read_label, +}; + +static const struct meson_sar_adc_param meson_sar_adc_meson8_param = { + .has_bl30_integration = false, + .clock_rate = 1150000, + .regmap_config = &meson_sar_adc_regmap_config_meson8, + .resolution = 10, + .temperature_trimming_bits = 4, + .temperature_multiplier = 18 * 10000, + .temperature_divider = 1024 * 10 * 85, +}; + +static const struct meson_sar_adc_param meson_sar_adc_meson8b_param = { + .has_bl30_integration = false, + .clock_rate = 1150000, + .regmap_config = &meson_sar_adc_regmap_config_meson8, + .resolution = 10, + .temperature_trimming_bits = 5, + .temperature_multiplier = 10, + .temperature_divider = 32, +}; + +static const struct meson_sar_adc_param meson_sar_adc_gxbb_param = { + .has_bl30_integration = true, + .clock_rate = 1200000, + .regmap_config = &meson_sar_adc_regmap_config_gxbb, + .resolution = 10, + .vref_voltage = 1, + .cmv_select = 1, +}; + +static const struct meson_sar_adc_param meson_sar_adc_gxl_param = { + .has_bl30_integration = true, + .clock_rate = 1200000, + .regmap_config = &meson_sar_adc_regmap_config_gxbb, + .resolution = 12, + .disable_ring_counter = 1, + .vref_voltage = 1, + .cmv_select = 1, +}; + +static const struct meson_sar_adc_param meson_sar_adc_gxlx_param = { + .has_bl30_integration = true, + .clock_rate = 1200000, + .regmap_config = &meson_sar_adc_regmap_config_gxbb, + .resolution = 12, + .disable_ring_counter = 1, + .vref_voltage = 1, + .cmv_select = true, + .enable_mpll_clock_workaround = true, +}; + +static const struct meson_sar_adc_param meson_sar_adc_axg_param = { + .has_bl30_integration = true, + .clock_rate = 1200000, + .regmap_config = &meson_sar_adc_regmap_config_gxbb, + .resolution = 12, + .disable_ring_counter = 1, + .vref_voltage = 1, + .has_vref_select = true, + .vref_select = VREF_VDDA, + .cmv_select = 1, +}; + +static const struct meson_sar_adc_param meson_sar_adc_g12a_param = { + .has_bl30_integration = false, + .clock_rate = 1200000, + .regmap_config = &meson_sar_adc_regmap_config_gxbb, + .resolution = 12, + .disable_ring_counter = 1, + .adc_eoc = 1, + .has_vref_select = true, + .vref_select = VREF_VDDA, +}; + +static const struct meson_sar_adc_data meson_sar_adc_meson8_data = { + .param = &meson_sar_adc_meson8_param, + .name = "meson-meson8-saradc", +}; + +static const struct meson_sar_adc_data meson_sar_adc_meson8b_data = { + .param = &meson_sar_adc_meson8b_param, + .name = "meson-meson8b-saradc", +}; + +static const struct meson_sar_adc_data meson_sar_adc_meson8m2_data = { + .param = &meson_sar_adc_meson8b_param, + .name = "meson-meson8m2-saradc", +}; + +static const struct meson_sar_adc_data meson_sar_adc_gxbb_data = { + .param = &meson_sar_adc_gxbb_param, + .name = "meson-gxbb-saradc", +}; + +static const struct meson_sar_adc_data meson_sar_adc_gxl_data = { + .param = &meson_sar_adc_gxl_param, + .name = "meson-gxl-saradc", +}; + +static const struct meson_sar_adc_data meson_sar_adc_gxlx_data = { + .param = &meson_sar_adc_gxlx_param, + .name = "meson-gxlx-saradc", +}; + +static const struct meson_sar_adc_data meson_sar_adc_gxm_data = { + .param = &meson_sar_adc_gxl_param, + .name = "meson-gxm-saradc", +}; + +static const struct meson_sar_adc_data meson_sar_adc_axg_data = { + .param = &meson_sar_adc_axg_param, + .name = "meson-axg-saradc", +}; + +static const struct meson_sar_adc_data meson_sar_adc_g12a_data = { + .param = &meson_sar_adc_g12a_param, + .name = "meson-g12a-saradc", +}; + +static const struct meson_sar_adc_data meson_sar_adc_s4_data = { + .param = &meson_sar_adc_g12a_param, + .name = "meson-s4-saradc", +}; + +static const struct of_device_id meson_sar_adc_of_match[] = { + { + .compatible = "amlogic,meson8-saradc", + .data = &meson_sar_adc_meson8_data, + }, { + .compatible = "amlogic,meson8b-saradc", + .data = &meson_sar_adc_meson8b_data, + }, { + .compatible = "amlogic,meson8m2-saradc", + .data = &meson_sar_adc_meson8m2_data, + }, { + .compatible = "amlogic,meson-gxbb-saradc", + .data = &meson_sar_adc_gxbb_data, + }, { + .compatible = "amlogic,meson-gxl-saradc", + .data = &meson_sar_adc_gxl_data, + }, { + .compatible = "amlogic,meson-gxlx-saradc", + .data = &meson_sar_adc_gxlx_data, + }, { + .compatible = "amlogic,meson-gxm-saradc", + .data = &meson_sar_adc_gxm_data, + }, { + .compatible = "amlogic,meson-axg-saradc", + .data = &meson_sar_adc_axg_data, + }, { + .compatible = "amlogic,meson-g12a-saradc", + .data = &meson_sar_adc_g12a_data, + }, { + .compatible = "amlogic,meson-s4-saradc", + .data = &meson_sar_adc_s4_data, + }, + { } +}; +MODULE_DEVICE_TABLE(of, meson_sar_adc_of_match); + +static int meson_sar_adc_probe(struct platform_device *pdev) +{ + const struct meson_sar_adc_data *match_data; + struct meson_sar_adc_priv *priv; + struct device *dev = &pdev->dev; + struct iio_dev *indio_dev; + void __iomem *base; + int irq, ret; + + indio_dev = devm_iio_device_alloc(dev, sizeof(*priv)); + if (!indio_dev) + return -ENOMEM; + + priv = iio_priv(indio_dev); + init_completion(&priv->done); + + match_data = of_device_get_match_data(dev); + if (!match_data) + return dev_err_probe(dev, -ENODEV, "failed to get match data\n"); + + priv->param = match_data->param; + + indio_dev->name = match_data->name; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->info = &meson_sar_adc_iio_info; + + base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(base)) + return PTR_ERR(base); + + priv->regmap = devm_regmap_init_mmio(dev, base, priv->param->regmap_config); + if (IS_ERR(priv->regmap)) + return dev_err_probe(dev, PTR_ERR(priv->regmap), "failed to init regmap\n"); + + irq = irq_of_parse_and_map(dev->of_node, 0); + if (!irq) + return dev_err_probe(dev, -EINVAL, "failed to get irq\n"); + + ret = devm_request_irq(dev, irq, meson_sar_adc_irq, IRQF_SHARED, dev_name(dev), indio_dev); + if (ret) + return dev_err_probe(dev, ret, "failed to request irq\n"); + + priv->clkin = devm_clk_get(dev, "clkin"); + if (IS_ERR(priv->clkin)) + return dev_err_probe(dev, PTR_ERR(priv->clkin), "failed to get clkin\n"); + + priv->core_clk = devm_clk_get_enabled(dev, "core"); + if (IS_ERR(priv->core_clk)) + return dev_err_probe(dev, PTR_ERR(priv->core_clk), "failed to get core clk\n"); + + priv->adc_clk = devm_clk_get_optional(dev, "adc_clk"); + if (IS_ERR(priv->adc_clk)) + return dev_err_probe(dev, PTR_ERR(priv->adc_clk), "failed to get adc clk\n"); + + priv->adc_sel_clk = devm_clk_get_optional(dev, "adc_sel"); + if (IS_ERR(priv->adc_sel_clk)) + return dev_err_probe(dev, PTR_ERR(priv->adc_sel_clk), "failed to get adc_sel clk\n"); + + /* on pre-GXBB SoCs the SAR ADC itself provides the ADC clock: */ + if (!priv->adc_clk) { + ret = meson_sar_adc_clk_init(indio_dev, base); + if (ret) + return dev_err_probe(dev, ret, "failed to init internal clk\n"); + } + + priv->vref = devm_regulator_get(dev, "vref"); + if (IS_ERR(priv->vref)) + return dev_err_probe(dev, PTR_ERR(priv->vref), "failed to get vref regulator\n"); + + priv->calibscale = MILLION; + + if (priv->param->temperature_trimming_bits) { + ret = meson_sar_adc_temp_sensor_init(indio_dev); + if (ret) + return ret; + } + + if (priv->temperature_sensor_calibrated) { + indio_dev->channels = meson_sar_adc_and_temp_iio_channels; + indio_dev->num_channels = + ARRAY_SIZE(meson_sar_adc_and_temp_iio_channels); + } else { + indio_dev->channels = meson_sar_adc_iio_channels; + indio_dev->num_channels = + ARRAY_SIZE(meson_sar_adc_iio_channels); + } + + ret = meson_sar_adc_init(indio_dev); + if (ret) + goto err; + + mutex_init(&priv->lock); + + ret = meson_sar_adc_hw_enable(indio_dev); + if (ret) + goto err; + + ret = meson_sar_adc_calib(indio_dev); + if (ret) + dev_warn(dev, "calibration failed\n"); + + platform_set_drvdata(pdev, indio_dev); + + ret = iio_device_register(indio_dev); + if (ret) { + dev_err_probe(dev, ret, "failed to register iio device\n"); + goto err_hw; + } + + return 0; + +err_hw: + meson_sar_adc_hw_disable(indio_dev); +err: + return ret; +} + +static void meson_sar_adc_remove(struct platform_device *pdev) +{ + struct iio_dev *indio_dev = platform_get_drvdata(pdev); + + iio_device_unregister(indio_dev); + + meson_sar_adc_hw_disable(indio_dev); +} + +static int meson_sar_adc_suspend(struct device *dev) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct meson_sar_adc_priv *priv = iio_priv(indio_dev); + + meson_sar_adc_hw_disable(indio_dev); + + clk_disable_unprepare(priv->core_clk); + + return 0; +} + +static int meson_sar_adc_resume(struct device *dev) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct meson_sar_adc_priv *priv = iio_priv(indio_dev); + int ret; + + ret = clk_prepare_enable(priv->core_clk); + if (ret) { + dev_err(dev, "failed to enable core clk\n"); + return ret; + } + + return meson_sar_adc_hw_enable(indio_dev); +} + +static DEFINE_SIMPLE_DEV_PM_OPS(meson_sar_adc_pm_ops, + meson_sar_adc_suspend, meson_sar_adc_resume); + +static struct platform_driver meson_sar_adc_driver = { + .probe = meson_sar_adc_probe, + .remove = meson_sar_adc_remove, + .driver = { + .name = "meson-saradc", + .of_match_table = meson_sar_adc_of_match, + .pm = pm_sleep_ptr(&meson_sar_adc_pm_ops), + }, +}; + +module_platform_driver(meson_sar_adc_driver); + +MODULE_AUTHOR("Martin Blumenstingl "); +MODULE_DESCRIPTION("Amlogic Meson SAR ADC driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/adc/mp2629_adc.c b/drivers/iio/adc/mp2629_adc.c new file mode 100644 index 000000000..c03f89ddb --- /dev/null +++ b/drivers/iio/adc/mp2629_adc.c @@ -0,0 +1,203 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * MP2629 Driver for ADC + * + * Copyright 2020 Monolithic Power Systems, Inc + * + * Author: Saravanan Sekar + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define MP2629_REG_ADC_CTRL 0x03 +#define MP2629_REG_BATT_VOLT 0x0e +#define MP2629_REG_SYSTEM_VOLT 0x0f +#define MP2629_REG_INPUT_VOLT 0x11 +#define MP2629_REG_BATT_CURRENT 0x12 +#define MP2629_REG_INPUT_CURRENT 0x13 + +#define MP2629_ADC_START BIT(7) +#define MP2629_ADC_CONTINUOUS BIT(6) + +#define MP2629_MAP(_mp, _mpc) IIO_MAP(#_mp, "mp2629_charger", "mp2629-"_mpc) + +#define MP2629_ADC_CHAN(_ch, _type) { \ + .type = _type, \ + .indexed = 1, \ + .datasheet_name = #_ch, \ + .channel = MP2629_ ## _ch, \ + .address = MP2629_REG_ ## _ch, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ +} + +struct mp2629_adc { + struct regmap *regmap; + struct device *dev; +}; + +static const struct iio_chan_spec mp2629_channels[] = { + MP2629_ADC_CHAN(BATT_VOLT, IIO_VOLTAGE), + MP2629_ADC_CHAN(SYSTEM_VOLT, IIO_VOLTAGE), + MP2629_ADC_CHAN(INPUT_VOLT, IIO_VOLTAGE), + MP2629_ADC_CHAN(BATT_CURRENT, IIO_CURRENT), + MP2629_ADC_CHAN(INPUT_CURRENT, IIO_CURRENT) +}; + +static const struct iio_map mp2629_adc_maps[] = { + MP2629_MAP(BATT_VOLT, "batt-volt"), + MP2629_MAP(SYSTEM_VOLT, "system-volt"), + MP2629_MAP(INPUT_VOLT, "input-volt"), + MP2629_MAP(BATT_CURRENT, "batt-current"), + MP2629_MAP(INPUT_CURRENT, "input-current"), + { } +}; + +static int mp2629_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct mp2629_adc *info = iio_priv(indio_dev); + unsigned int rval; + int ret; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + ret = regmap_read(info->regmap, chan->address, &rval); + if (ret) + return ret; + + if (chan->channel == MP2629_INPUT_VOLT) + rval &= GENMASK(6, 0); + *val = rval; + return IIO_VAL_INT; + + case IIO_CHAN_INFO_SCALE: + switch (chan->channel) { + case MP2629_BATT_VOLT: + case MP2629_SYSTEM_VOLT: + *val = 20; + return IIO_VAL_INT; + + case MP2629_INPUT_VOLT: + *val = 60; + return IIO_VAL_INT; + + case MP2629_BATT_CURRENT: + *val = 175; + *val2 = 10; + return IIO_VAL_FRACTIONAL; + + case MP2629_INPUT_CURRENT: + *val = 133; + *val2 = 10; + return IIO_VAL_FRACTIONAL; + + default: + return -EINVAL; + } + + default: + return -EINVAL; + } +} + +static const struct iio_info mp2629_adc_info = { + .read_raw = &mp2629_read_raw, +}; + +static int mp2629_adc_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct mp2629_data *ddata = dev_get_drvdata(dev->parent); + struct mp2629_adc *info; + struct iio_dev *indio_dev; + int ret; + + indio_dev = devm_iio_device_alloc(dev, sizeof(*info)); + if (!indio_dev) + return -ENOMEM; + + info = iio_priv(indio_dev); + info->regmap = ddata->regmap; + info->dev = dev; + platform_set_drvdata(pdev, indio_dev); + + ret = regmap_set_bits(info->regmap, MP2629_REG_ADC_CTRL, + MP2629_ADC_START | MP2629_ADC_CONTINUOUS); + if (ret) { + dev_err(dev, "adc enable fail: %d\n", ret); + return ret; + } + + ret = iio_map_array_register(indio_dev, mp2629_adc_maps); + if (ret) { + dev_err(dev, "IIO maps register fail: %d\n", ret); + goto fail_disable; + } + + indio_dev->name = "mp2629-adc"; + indio_dev->channels = mp2629_channels; + indio_dev->num_channels = ARRAY_SIZE(mp2629_channels); + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->info = &mp2629_adc_info; + + ret = iio_device_register(indio_dev); + if (ret) { + dev_err(dev, "IIO device register fail: %d\n", ret); + goto fail_map_unregister; + } + + return 0; + +fail_map_unregister: + iio_map_array_unregister(indio_dev); + +fail_disable: + regmap_clear_bits(info->regmap, MP2629_REG_ADC_CTRL, + MP2629_ADC_CONTINUOUS); + regmap_clear_bits(info->regmap, MP2629_REG_ADC_CTRL, MP2629_ADC_START); + + return ret; +} + +static void mp2629_adc_remove(struct platform_device *pdev) +{ + struct iio_dev *indio_dev = platform_get_drvdata(pdev); + struct mp2629_adc *info = iio_priv(indio_dev); + + iio_device_unregister(indio_dev); + + iio_map_array_unregister(indio_dev); + + regmap_clear_bits(info->regmap, MP2629_REG_ADC_CTRL, + MP2629_ADC_CONTINUOUS); + regmap_clear_bits(info->regmap, MP2629_REG_ADC_CTRL, MP2629_ADC_START); +} + +static const struct of_device_id mp2629_adc_of_match[] = { + { .compatible = "mps,mp2629_adc" }, + { } +}; +MODULE_DEVICE_TABLE(of, mp2629_adc_of_match); + +static struct platform_driver mp2629_adc_driver = { + .driver = { + .name = "mp2629_adc", + .of_match_table = mp2629_adc_of_match, + }, + .probe = mp2629_adc_probe, + .remove = mp2629_adc_remove, +}; +module_platform_driver(mp2629_adc_driver); + +MODULE_AUTHOR("Saravanan Sekar "); +MODULE_DESCRIPTION("MP2629 ADC driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/iio/adc/mt6323-auxadc.c b/drivers/iio/adc/mt6323-auxadc.c new file mode 100644 index 000000000..4f73ac42a --- /dev/null +++ b/drivers/iio/adc/mt6323-auxadc.c @@ -0,0 +1,313 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2026 Roman Vivchar + * + * Based on drivers/iio/adc/mt6359-auxadc.c + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +#define AUXADC_STRUP_CON10_RSTB_SEL BIT(7) +#define AUXADC_STRUP_CON10_RSTB_SW BIT(5) + +#define AUXADC_TOP_CKPDN2_CTL_CK BIT(5) + +#define AUXADC_TRIM_CH2_MASK GENMASK(11, 10) +#define AUXADC_TRIM_CH4_MASK GENMASK(9, 8) +#define AUXADC_TRIM_CH5_MASK GENMASK(5, 4) +#define AUXADC_TRIM_CH6_MASK GENMASK(3, 2) + +#define AUXADC_CON27_VREF18_ENB_MD BIT(15) +#define AUXADC_CON27_MD_STATUS BIT(0) + +#define AUXADC_CON19_GPS_STATUS BIT(1) + +#define AUXADC_CON26_VREF18_SELB BIT(1) +#define AUXADC_CON26_DECI_GDLY_SEL BIT(0) + +#define AUXADC_CON11_VBUF_EN BIT(4) + +#define AUXADC_CON19_DECI_GDLY_MASK GENMASK(15, 14) +#define AUXADC_ADC19_BUSY_MASK GENMASK(15, 1) +#define AUXADC_READY_MASK BIT(15) +#define AUXADC_DATA_MASK GENMASK(14, 0) + +#define AUXADC_CON9_OSR_MASK GENMASK(12, 10) +#define AUXADC_DEFAULT_OSR 3 + +#define MTK_PMIC_IIO_CHAN(_name, _chan, _addr) \ +{ \ + .type = IIO_VOLTAGE, \ + .indexed = 1, \ + .channel = _chan, \ + .address = _addr, \ + .datasheet_name = __stringify(_name), \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ + BIT(IIO_CHAN_INFO_SCALE), \ +} + +/* + * AUXADC reports everything in mV, including temperature and + * current channels. Channel macros are mapped such that their + * ID matches their respective hardware bit position in CON22. + */ +static const struct iio_chan_spec mt6323_auxadc_channels[] = { + MTK_PMIC_IIO_CHAN(baton2, MT6323_AUXADC_BATON2, MT6323_AUXADC_ADC6), + MTK_PMIC_IIO_CHAN(ch6, MT6323_AUXADC_CH6, MT6323_AUXADC_ADC11), + MTK_PMIC_IIO_CHAN(bat_temp, MT6323_AUXADC_BAT_TEMP, MT6323_AUXADC_ADC5), + MTK_PMIC_IIO_CHAN(chip_temp, MT6323_AUXADC_CHIP_TEMP, MT6323_AUXADC_ADC4), + MTK_PMIC_IIO_CHAN(vcdt, MT6323_AUXADC_VCDT, MT6323_AUXADC_ADC2), + MTK_PMIC_IIO_CHAN(baton1, MT6323_AUXADC_BATON1, MT6323_AUXADC_ADC3), + MTK_PMIC_IIO_CHAN(isense, MT6323_AUXADC_ISENSE, MT6323_AUXADC_ADC1), + MTK_PMIC_IIO_CHAN(batsns, MT6323_AUXADC_BATSNS, MT6323_AUXADC_ADC0), + MTK_PMIC_IIO_CHAN(accdet, MT6323_AUXADC_ACCDET, MT6323_AUXADC_ADC7), +}; + +/* + * The MediaTek MT6323 (as well as a lot of other PMICs) has the following hierarchy: + * PMIC AUXADC <- PMIC MFD <- SoC PWRAP (wrapper for PWRAP FSM) + * + * Therefore, PWRAP regmap should be obtained using dev->parent->parent. + */ +struct mt6323_auxadc { + struct regmap *regmap; + /* AUXADC doesn't support reading multiple channels simultaneously. */ + struct mutex lock; +}; + +static int mt6323_auxadc_prepare_channel(struct mt6323_auxadc *auxadc) +{ + struct regmap *map = auxadc->regmap; + u32 val; + int ret; + + ret = regmap_read(map, MT6323_AUXADC_CON19, &val); + if (ret) + return ret; + + /* The ADC is idle. */ + if (!(val & AUXADC_CON19_DECI_GDLY_MASK)) + return 0; + + ret = regmap_read_poll_timeout(map, MT6323_AUXADC_ADC19, + val, !(val & AUXADC_ADC19_BUSY_MASK), + 10, 500); + if (ret) + return ret; + + return regmap_clear_bits(map, MT6323_AUXADC_CON19, + AUXADC_CON19_DECI_GDLY_MASK); +} + +static int mt6323_auxadc_request(struct mt6323_auxadc *auxadc, + unsigned long channel) +{ + struct regmap *map = auxadc->regmap; + int ret; + + ret = regmap_set_bits(map, MT6323_AUXADC_CON11, AUXADC_CON11_VBUF_EN); + if (ret) + return ret; + + return regmap_set_bits(map, MT6323_AUXADC_CON22, BIT(channel)); +} + +static int mt6323_auxadc_release(struct mt6323_auxadc *auxadc, + unsigned long channel) +{ + struct regmap *map = auxadc->regmap; + int ret; + + ret = regmap_clear_bits(map, MT6323_AUXADC_CON22, BIT(channel)); + if (ret) + return ret; + + return regmap_clear_bits(map, MT6323_AUXADC_CON11, AUXADC_CON11_VBUF_EN); +} + +static int mt6323_auxadc_read(struct mt6323_auxadc *auxadc, + const struct iio_chan_spec *chan, int *out) +{ + struct regmap *map = auxadc->regmap; + u32 val; + int ret; + + ret = regmap_read_poll_timeout(map, chan->address, + val, (val & AUXADC_READY_MASK), + 1 * USEC_PER_MSEC, 100 * USEC_PER_MSEC); + if (ret) + return ret; + + *out = FIELD_GET(AUXADC_DATA_MASK, val); + + return 0; +} + +static int mt6323_auxadc_read_raw(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + int *val, int *val2, long mask) +{ + struct mt6323_auxadc *auxadc = iio_priv(indio_dev); + int ret, mult; + + switch (mask) { + case IIO_CHAN_INFO_SCALE: + if (chan->channel == MT6323_AUXADC_ISENSE || + chan->channel == MT6323_AUXADC_BATSNS) + mult = 4; + else + mult = 1; + + /* 1800mV full range with 15-bit resolution. */ + *val = mult * 1800; + *val2 = 15; + + return IIO_VAL_FRACTIONAL_LOG2; + case IIO_CHAN_INFO_RAW: { + guard(mutex)(&auxadc->lock); + + ret = mt6323_auxadc_prepare_channel(auxadc); + if (ret) + return ret; + + ret = mt6323_auxadc_request(auxadc, chan->channel); + if (ret) + return ret; + + /* Hardware limitation: the AUXADC needs a delay to become ready. */ + fsleep(300); + + ret = mt6323_auxadc_read(auxadc, chan, val); + + if (mt6323_auxadc_release(auxadc, chan->channel)) + dev_err(&indio_dev->dev, + "failed to release channel %d\n", chan->channel); + + if (ret) + return ret; + + return IIO_VAL_INT; + } + default: + return -EINVAL; + } +} + +static int mt6323_auxadc_init(struct mt6323_auxadc *auxadc) +{ + struct regmap *map = auxadc->regmap; + int ret; + + ret = regmap_set_bits(map, MT6323_STRUP_CON10, + AUXADC_STRUP_CON10_RSTB_SW | + AUXADC_STRUP_CON10_RSTB_SEL); + if (ret) + return ret; + + ret = regmap_set_bits(map, MT6323_TOP_CKPDN2, AUXADC_TOP_CKPDN2_CTL_CK); + if (ret) + return ret; + + ret = regmap_update_bits(map, MT6323_AUXADC_CON10, + AUXADC_TRIM_CH2_MASK | AUXADC_TRIM_CH4_MASK | + AUXADC_TRIM_CH5_MASK | AUXADC_TRIM_CH6_MASK, + FIELD_PREP(AUXADC_TRIM_CH2_MASK, 1) | + FIELD_PREP(AUXADC_TRIM_CH4_MASK, 1) | + FIELD_PREP(AUXADC_TRIM_CH5_MASK, 1) | + FIELD_PREP(AUXADC_TRIM_CH6_MASK, 1)); + if (ret) + return ret; + + ret = regmap_set_bits(map, MT6323_AUXADC_CON27, + AUXADC_CON27_VREF18_ENB_MD | + AUXADC_CON27_MD_STATUS); + if (ret) + return ret; + + ret = regmap_set_bits(map, MT6323_AUXADC_CON19, AUXADC_CON19_GPS_STATUS); + if (ret) + return ret; + + ret = regmap_set_bits(map, MT6323_AUXADC_CON26, + AUXADC_CON26_VREF18_SELB | + AUXADC_CON26_DECI_GDLY_SEL); + if (ret) + return ret; + + return regmap_update_bits(map, MT6323_AUXADC_CON9, AUXADC_CON9_OSR_MASK, + FIELD_PREP(AUXADC_CON9_OSR_MASK, AUXADC_DEFAULT_OSR)); +} + +static const struct iio_info mt6323_auxadc_iio_info = { + .read_raw = mt6323_auxadc_read_raw, +}; + +static int mt6323_auxadc_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct mt6323_auxadc *auxadc; + struct regmap *regmap; + struct iio_dev *iio; + int ret; + + regmap = dev_get_regmap(dev->parent->parent, NULL); + if (!regmap) + return dev_err_probe(dev, -ENODEV, "failed to get regmap\n"); + + iio = devm_iio_device_alloc(dev, sizeof(*auxadc)); + if (!iio) + return -ENOMEM; + + auxadc = iio_priv(iio); + auxadc->regmap = regmap; + + ret = devm_mutex_init(dev, &auxadc->lock); + if (ret) + return ret; + + ret = mt6323_auxadc_init(auxadc); + if (ret) + return dev_err_probe(dev, ret, "failed to initialize auxadc\n"); + + iio->name = "mt6323-auxadc"; + iio->info = &mt6323_auxadc_iio_info; + iio->modes = INDIO_DIRECT_MODE; + iio->channels = mt6323_auxadc_channels; + iio->num_channels = ARRAY_SIZE(mt6323_auxadc_channels); + + return devm_iio_device_register(dev, iio); +} + +static const struct of_device_id mt6323_auxadc_of_match[] = { + { .compatible = "mediatek,mt6323-auxadc" }, + { } +}; +MODULE_DEVICE_TABLE(of, mt6323_auxadc_of_match); + +static struct platform_driver mt6323_auxadc_driver = { + .driver = { + .name = "mt6323-auxadc", + .of_match_table = mt6323_auxadc_of_match, + }, + .probe = mt6323_auxadc_probe, +}; +module_platform_driver(mt6323_auxadc_driver); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("MediaTek MT6323 PMIC AUXADC Driver"); diff --git a/drivers/iio/adc/mt6359-auxadc.c b/drivers/iio/adc/mt6359-auxadc.c new file mode 100644 index 000000000..88b0e2630 --- /dev/null +++ b/drivers/iio/adc/mt6359-auxadc.c @@ -0,0 +1,908 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * MediaTek MT6359 PMIC AUXADC IIO driver + * + * Copyright (c) 2021 MediaTek Inc. + * Copyright (c) 2024 Collabora Ltd + * Author: AngeloGioacchino Del Regno + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +#include +#include +#include +#include + +#define AUXADC_AVG_TIME_US 10 +#define AUXADC_POLL_DELAY_US 100 +#define AUXADC_TIMEOUT_US 32000 +#define IMP_STOP_DELAY_US 150 +#define IMP_POLL_DELAY_US 1000 + +/* For PMIC_RG_RESET_VAL and MT6358_IMP0_CLEAR, the bits specific purpose is unknown. */ +#define PMIC_RG_RESET_VAL (BIT(0) | BIT(3)) +#define PMIC_AUXADC_RDY_BIT BIT(15) +#define MT6357_IMP_ADC_NUM 30 +#define MT6358_IMP_ADC_NUM 28 + +#define MT6358_DCM_CK_SW_EN GENMASK(1, 0) +#define MT6358_IMP0_CLEAR (BIT(14) | BIT(7)) +#define MT6358_IMP0_IRQ_RDY BIT(8) +#define MT6358_IMP1_AUTOREPEAT_EN BIT(15) + +#define MT6359_IMP0_CONV_EN BIT(0) +#define MT6359_IMP1_IRQ_RDY BIT(15) + +#define MT6363_EXT_CHAN_MASK GENMASK(2, 0) +#define MT6363_EXT_PURES_MASK GENMASK(4, 3) + #define MT6363_PULLUP_RES_100K 0 + #define MT6363_PULLUP_RES_30K 1 + #define MT6363_PULLUP_RES_OPEN 3 + +enum mtk_pmic_auxadc_regs { + PMIC_AUXADC_ADC0, + PMIC_AUXADC_DCM_CON, + PMIC_AUXADC_IMP0, + PMIC_AUXADC_IMP1, + PMIC_AUXADC_IMP3, + PMIC_AUXADC_RQST0, + PMIC_AUXADC_RQST1, + PMIC_AUXADC_RQST3, + PMIC_AUXADC_SDMADC_CON0, + PMIC_HK_TOP_WKEY, + PMIC_HK_TOP_RST_CON0, + PMIC_FGADC_R_CON0, + PMIC_AUXADC_REGS_MAX +}; + +enum mtk_pmic_auxadc_channels { + PMIC_AUXADC_CHAN_BATADC, + PMIC_AUXADC_CHAN_ISENSE, + PMIC_AUXADC_CHAN_VCDT, + PMIC_AUXADC_CHAN_BAT_TEMP, + PMIC_AUXADC_CHAN_BATID, + PMIC_AUXADC_CHAN_CHIP_TEMP, + PMIC_AUXADC_CHAN_VCORE_TEMP, + PMIC_AUXADC_CHAN_VPROC_TEMP, + PMIC_AUXADC_CHAN_VGPU_TEMP, + PMIC_AUXADC_CHAN_ACCDET, + PMIC_AUXADC_CHAN_VDCXO, + PMIC_AUXADC_CHAN_TSX_TEMP, + PMIC_AUXADC_CHAN_HPOFS_CAL, + PMIC_AUXADC_CHAN_DCXO_TEMP, + PMIC_AUXADC_CHAN_VTREF, + PMIC_AUXADC_CHAN_VBIF, + PMIC_AUXADC_CHAN_VSYSSNS, + PMIC_AUXADC_CHAN_VIN1, + PMIC_AUXADC_CHAN_VIN2, + PMIC_AUXADC_CHAN_VIN3, + PMIC_AUXADC_CHAN_VIN4, + PMIC_AUXADC_CHAN_VIN5, + PMIC_AUXADC_CHAN_VIN6, + PMIC_AUXADC_CHAN_VIN7, + PMIC_AUXADC_CHAN_IBAT, + PMIC_AUXADC_CHAN_VBAT, + PMIC_AUXADC_CHAN_MAX +}; + +/** + * struct mt6359_auxadc - Main driver structure + * @dev: Device pointer + * @regmap: Regmap from SoC PMIC Wrapper + * @chip_info: PMIC specific chip info + * @lock: Mutex to serialize AUXADC reading vs configuration + * @timed_out: Signals whether the last read timed out + */ +struct mt6359_auxadc { + struct device *dev; + struct regmap *regmap; + const struct mtk_pmic_auxadc_info *chip_info; + struct mutex lock; + bool timed_out; +}; + +/** + * struct mtk_pmic_auxadc_chan - PMIC AUXADC channel data + * @req_idx: Request register number + * @req_mask: Bitmask to activate a channel + * @rdy_idx: Readiness register number + * @rdy_mask: Bitmask to determine channel readiness + * @ext_sel_idx: PMIC GPIO channel register number + * @ext_sel_ch: PMIC GPIO number + * @ext_sel_pu: PMIC GPIO channel pullup resistor selector + * @num_samples: Number of AUXADC samples for averaging + * @r_ratio: Resistance ratio fractional + */ +struct mtk_pmic_auxadc_chan { + u8 req_idx; + u16 req_mask; + u8 rdy_idx; + u16 rdy_mask; + s8 ext_sel_idx; + u8 ext_sel_ch; + u8 ext_sel_pu; + u16 num_samples; + struct u8_fract r_ratio; +}; + +/** + * struct mtk_pmic_auxadc_info - PMIC specific chip info + * @model_name: PMIC model name + * @channels: IIO specification of ADC channels + * @num_channels: Number of ADC channels + * @desc: PMIC AUXADC channel data + * @regs: List of PMIC specific registers + * @sec_unlock_key: Security unlock key for HK_TOP writes + * @vref_mV: AUXADC Reference Voltage (VREF) in millivolts + * @imp_adc_num: ADC channel for battery impedance readings + * @is_spmi: Defines whether this PMIC communicates over SPMI + * @no_reset: If true, this PMIC does not support ADC reset + * @read_imp: Callback to read impedance channels + */ +struct mtk_pmic_auxadc_info { + const char *model_name; + const struct iio_chan_spec *channels; + u8 num_channels; + const struct mtk_pmic_auxadc_chan *desc; + const u16 *regs; + u16 sec_unlock_key; + u32 vref_mV; + u8 imp_adc_num; + bool is_spmi; + bool no_reset; + int (*read_imp)(struct mt6359_auxadc *adc_dev, + const struct iio_chan_spec *chan, int *vbat, int *ibat); +}; + +#define MTK_PMIC_ADC_EXT_CHAN(_ch_idx, _req_idx, _req_bit, _rdy_idx, _rdy_bit, \ + _ext_sel_idx, _ext_sel_ch, _ext_sel_pu, \ + _samples, _rnum, _rdiv) \ + [PMIC_AUXADC_CHAN_##_ch_idx] = { \ + .req_idx = _req_idx, \ + .req_mask = BIT(_req_bit), \ + .rdy_idx = _rdy_idx, \ + .rdy_mask = BIT(_rdy_bit), \ + .ext_sel_idx = _ext_sel_idx, \ + .ext_sel_ch = _ext_sel_ch, \ + .ext_sel_pu = _ext_sel_pu, \ + .num_samples = _samples, \ + .r_ratio = { _rnum, _rdiv } \ + } + +#define MTK_PMIC_ADC_CHAN(_ch_idx, _req_idx, _req_bit, _rdy_idx, _rdy_bit, \ + _samples, _rnum, _rdiv) \ + MTK_PMIC_ADC_EXT_CHAN(_ch_idx, _req_idx, _req_bit, _rdy_idx, _rdy_bit, \ + -1, 0, 0, _samples, _rnum, _rdiv) + +#define MTK_PMIC_IIO_CHAN(_model, _name, _ch_idx, _adc_idx, _nbits, _ch_type) \ +{ \ + .type = _ch_type, \ + .channel = _model##_AUXADC_##_ch_idx, \ + .address = _adc_idx, \ + .scan_index = PMIC_AUXADC_CHAN_##_ch_idx, \ + .datasheet_name = __stringify(_name), \ + .scan_type = { \ + .sign = 'u', \ + .realbits = _nbits, \ + .storagebits = 16, \ + .endianness = IIO_CPU \ + }, \ + .indexed = 1, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE) \ +} + +static const struct iio_chan_spec mt6357_auxadc_channels[] = { + MTK_PMIC_IIO_CHAN(MT6357, bat_adc, BATADC, 0, 15, IIO_RESISTANCE), + MTK_PMIC_IIO_CHAN(MT6357, isense, ISENSE, 1, 12, IIO_CURRENT), + MTK_PMIC_IIO_CHAN(MT6357, cdt_v, VCDT, 2, 12, IIO_TEMP), + MTK_PMIC_IIO_CHAN(MT6357, batt_temp, BAT_TEMP, 3, 12, IIO_TEMP), + MTK_PMIC_IIO_CHAN(MT6357, chip_temp, CHIP_TEMP, 4, 12, IIO_TEMP), + MTK_PMIC_IIO_CHAN(MT6357, acc_det, ACCDET, 5, 12, IIO_RESISTANCE), + MTK_PMIC_IIO_CHAN(MT6357, dcxo_v, VDCXO, 6, 12, IIO_VOLTAGE), + MTK_PMIC_IIO_CHAN(MT6357, tsx_temp, TSX_TEMP, 7, 15, IIO_TEMP), + MTK_PMIC_IIO_CHAN(MT6357, hp_ofs_cal, HPOFS_CAL, 9, 15, IIO_RESISTANCE), + MTK_PMIC_IIO_CHAN(MT6357, dcxo_temp, DCXO_TEMP, 36, 15, IIO_TEMP), + MTK_PMIC_IIO_CHAN(MT6357, vcore_temp, VCORE_TEMP, 40, 12, IIO_TEMP), + MTK_PMIC_IIO_CHAN(MT6357, vproc_temp, VPROC_TEMP, 41, 12, IIO_TEMP), + + /* Battery impedance channels */ + MTK_PMIC_IIO_CHAN(MT6357, batt_v, VBAT, 0, 15, IIO_VOLTAGE), +}; + +static const struct mtk_pmic_auxadc_chan mt6357_auxadc_ch_desc[] = { + MTK_PMIC_ADC_CHAN(BATADC, PMIC_AUXADC_RQST0, 0, PMIC_AUXADC_IMP0, 8, 128, 3, 1), + MTK_PMIC_ADC_CHAN(ISENSE, PMIC_AUXADC_RQST0, 0, PMIC_AUXADC_IMP0, 8, 128, 3, 1), + MTK_PMIC_ADC_CHAN(VCDT, PMIC_AUXADC_RQST0, 0, PMIC_AUXADC_IMP0, 8, 8, 1, 1), + MTK_PMIC_ADC_CHAN(BAT_TEMP, PMIC_AUXADC_RQST0, 3, PMIC_AUXADC_IMP0, 8, 8, 1, 1), + MTK_PMIC_ADC_CHAN(CHIP_TEMP, PMIC_AUXADC_RQST0, 4, PMIC_AUXADC_IMP0, 8, 8, 1, 1), + MTK_PMIC_ADC_CHAN(ACCDET, PMIC_AUXADC_RQST0, 5, PMIC_AUXADC_IMP0, 8, 8, 1, 1), + MTK_PMIC_ADC_CHAN(TSX_TEMP, PMIC_AUXADC_RQST0, 7, PMIC_AUXADC_IMP0, 8, 128, 1, 1), + MTK_PMIC_ADC_CHAN(HPOFS_CAL, PMIC_AUXADC_RQST0, 9, PMIC_AUXADC_IMP0, 8, 256, 1, 1), + MTK_PMIC_ADC_CHAN(DCXO_TEMP, PMIC_AUXADC_RQST0, 10, PMIC_AUXADC_IMP0, 8, 16, 1, 1), + MTK_PMIC_ADC_CHAN(VBIF, PMIC_AUXADC_RQST0, 11, PMIC_AUXADC_IMP0, 8, 8, 1, 1), + MTK_PMIC_ADC_CHAN(VCORE_TEMP, PMIC_AUXADC_RQST1, 5, PMIC_AUXADC_IMP0, 8, 8, 1, 1), + MTK_PMIC_ADC_CHAN(VPROC_TEMP, PMIC_AUXADC_RQST1, 6, PMIC_AUXADC_IMP0, 8, 8, 1, 1), + + /* Battery impedance channels */ + MTK_PMIC_ADC_CHAN(VBAT, 0, 0, PMIC_AUXADC_IMP0, 8, 128, 3, 1), +}; + +static const u16 mt6357_auxadc_regs[] = { + [PMIC_HK_TOP_RST_CON0] = 0x0f90, + [PMIC_AUXADC_DCM_CON] = 0x122e, + [PMIC_AUXADC_ADC0] = 0x1088, + [PMIC_AUXADC_IMP0] = 0x119c, + [PMIC_AUXADC_IMP1] = 0x119e, + [PMIC_AUXADC_RQST0] = 0x110e, + [PMIC_AUXADC_RQST1] = 0x1114, +}; + +static const struct iio_chan_spec mt6358_auxadc_channels[] = { + MTK_PMIC_IIO_CHAN(MT6358, bat_adc, BATADC, 0, 15, IIO_RESISTANCE), + MTK_PMIC_IIO_CHAN(MT6358, cdt_v, VCDT, 2, 12, IIO_TEMP), + MTK_PMIC_IIO_CHAN(MT6358, batt_temp, BAT_TEMP, 3, 12, IIO_TEMP), + MTK_PMIC_IIO_CHAN(MT6358, chip_temp, CHIP_TEMP, 4, 12, IIO_TEMP), + MTK_PMIC_IIO_CHAN(MT6358, acc_det, ACCDET, 5, 12, IIO_RESISTANCE), + MTK_PMIC_IIO_CHAN(MT6358, dcxo_v, VDCXO, 6, 12, IIO_VOLTAGE), + MTK_PMIC_IIO_CHAN(MT6358, tsx_temp, TSX_TEMP, 7, 15, IIO_TEMP), + MTK_PMIC_IIO_CHAN(MT6358, hp_ofs_cal, HPOFS_CAL, 9, 15, IIO_RESISTANCE), + MTK_PMIC_IIO_CHAN(MT6358, dcxo_temp, DCXO_TEMP, 10, 15, IIO_TEMP), + MTK_PMIC_IIO_CHAN(MT6358, bif_v, VBIF, 11, 12, IIO_VOLTAGE), + MTK_PMIC_IIO_CHAN(MT6358, vcore_temp, VCORE_TEMP, 38, 12, IIO_TEMP), + MTK_PMIC_IIO_CHAN(MT6358, vproc_temp, VPROC_TEMP, 39, 12, IIO_TEMP), + MTK_PMIC_IIO_CHAN(MT6358, vgpu_temp, VGPU_TEMP, 40, 12, IIO_TEMP), + + /* Battery impedance channels */ + MTK_PMIC_IIO_CHAN(MT6358, batt_v, VBAT, 0, 15, IIO_VOLTAGE), +}; + +static const struct mtk_pmic_auxadc_chan mt6358_auxadc_ch_desc[] = { + MTK_PMIC_ADC_CHAN(BATADC, PMIC_AUXADC_RQST0, 0, PMIC_AUXADC_IMP0, 8, 128, 3, 1), + MTK_PMIC_ADC_CHAN(VCDT, PMIC_AUXADC_RQST0, 0, PMIC_AUXADC_IMP0, 8, 8, 1, 1), + MTK_PMIC_ADC_CHAN(BAT_TEMP, PMIC_AUXADC_RQST0, 3, PMIC_AUXADC_IMP0, 8, 8, 2, 1), + MTK_PMIC_ADC_CHAN(CHIP_TEMP, PMIC_AUXADC_RQST0, 4, PMIC_AUXADC_IMP0, 8, 8, 1, 1), + MTK_PMIC_ADC_CHAN(ACCDET, PMIC_AUXADC_RQST0, 5, PMIC_AUXADC_IMP0, 8, 8, 1, 1), + MTK_PMIC_ADC_CHAN(VDCXO, PMIC_AUXADC_RQST0, 6, PMIC_AUXADC_IMP0, 8, 8, 3, 2), + MTK_PMIC_ADC_CHAN(TSX_TEMP, PMIC_AUXADC_RQST0, 7, PMIC_AUXADC_IMP0, 8, 128, 1, 1), + MTK_PMIC_ADC_CHAN(HPOFS_CAL, PMIC_AUXADC_RQST0, 9, PMIC_AUXADC_IMP0, 8, 256, 1, 1), + MTK_PMIC_ADC_CHAN(DCXO_TEMP, PMIC_AUXADC_RQST0, 10, PMIC_AUXADC_IMP0, 8, 16, 1, 1), + MTK_PMIC_ADC_CHAN(VBIF, PMIC_AUXADC_RQST0, 11, PMIC_AUXADC_IMP0, 8, 8, 2, 1), + MTK_PMIC_ADC_CHAN(VCORE_TEMP, PMIC_AUXADC_RQST1, 8, PMIC_AUXADC_IMP0, 8, 8, 1, 1), + MTK_PMIC_ADC_CHAN(VPROC_TEMP, PMIC_AUXADC_RQST1, 9, PMIC_AUXADC_IMP0, 8, 8, 1, 1), + MTK_PMIC_ADC_CHAN(VGPU_TEMP, PMIC_AUXADC_RQST1, 10, PMIC_AUXADC_IMP0, 8, 8, 1, 1), + + /* Battery impedance channels */ + MTK_PMIC_ADC_CHAN(VBAT, 0, 0, PMIC_AUXADC_IMP0, 8, 128, 7, 2), +}; + +static const u16 mt6358_auxadc_regs[] = { + [PMIC_HK_TOP_RST_CON0] = 0x0f90, + [PMIC_AUXADC_DCM_CON] = 0x1260, + [PMIC_AUXADC_ADC0] = 0x1088, + [PMIC_AUXADC_IMP0] = 0x1208, + [PMIC_AUXADC_IMP1] = 0x120a, + [PMIC_AUXADC_RQST0] = 0x1108, + [PMIC_AUXADC_RQST1] = 0x110a, +}; + +static const struct iio_chan_spec mt6359_auxadc_channels[] = { + MTK_PMIC_IIO_CHAN(MT6359, bat_adc, BATADC, 0, 15, IIO_RESISTANCE), + MTK_PMIC_IIO_CHAN(MT6359, batt_temp, BAT_TEMP, 3, 12, IIO_TEMP), + MTK_PMIC_IIO_CHAN(MT6359, chip_temp, CHIP_TEMP, 4, 12, IIO_TEMP), + MTK_PMIC_IIO_CHAN(MT6359, acc_det, ACCDET, 5, 12, IIO_RESISTANCE), + MTK_PMIC_IIO_CHAN(MT6359, dcxo_v, VDCXO, 6, 12, IIO_VOLTAGE), + MTK_PMIC_IIO_CHAN(MT6359, tsx_temp, TSX_TEMP, 7, 15, IIO_TEMP), + MTK_PMIC_IIO_CHAN(MT6359, hp_ofs_cal, HPOFS_CAL, 9, 15, IIO_RESISTANCE), + MTK_PMIC_IIO_CHAN(MT6359, dcxo_temp, DCXO_TEMP, 10, 15, IIO_TEMP), + MTK_PMIC_IIO_CHAN(MT6359, bif_v, VBIF, 11, 12, IIO_VOLTAGE), + MTK_PMIC_IIO_CHAN(MT6359, vcore_temp, VCORE_TEMP, 30, 12, IIO_TEMP), + MTK_PMIC_IIO_CHAN(MT6359, vproc_temp, VPROC_TEMP, 31, 12, IIO_TEMP), + MTK_PMIC_IIO_CHAN(MT6359, vgpu_temp, VGPU_TEMP, 32, 12, IIO_TEMP), + + /* Battery impedance channels */ + MTK_PMIC_IIO_CHAN(MT6359, batt_v, VBAT, 0, 15, IIO_VOLTAGE), + MTK_PMIC_IIO_CHAN(MT6359, batt_i, IBAT, 0, 15, IIO_CURRENT), +}; + +static const struct mtk_pmic_auxadc_chan mt6359_auxadc_ch_desc[] = { + MTK_PMIC_ADC_CHAN(BATADC, PMIC_AUXADC_RQST0, 0, PMIC_AUXADC_IMP1, 15, 128, 7, 2), + MTK_PMIC_ADC_CHAN(BAT_TEMP, PMIC_AUXADC_RQST0, 3, PMIC_AUXADC_IMP1, 15, 8, 5, 2), + MTK_PMIC_ADC_CHAN(CHIP_TEMP, PMIC_AUXADC_RQST0, 4, PMIC_AUXADC_IMP1, 15, 8, 1, 1), + MTK_PMIC_ADC_CHAN(ACCDET, PMIC_AUXADC_RQST0, 5, PMIC_AUXADC_IMP1, 15, 8, 1, 1), + MTK_PMIC_ADC_CHAN(VDCXO, PMIC_AUXADC_RQST0, 6, PMIC_AUXADC_IMP1, 15, 8, 3, 2), + MTK_PMIC_ADC_CHAN(TSX_TEMP, PMIC_AUXADC_RQST0, 7, PMIC_AUXADC_IMP1, 15, 128, 1, 1), + MTK_PMIC_ADC_CHAN(HPOFS_CAL, PMIC_AUXADC_RQST0, 9, PMIC_AUXADC_IMP1, 15, 256, 1, 1), + MTK_PMIC_ADC_CHAN(DCXO_TEMP, PMIC_AUXADC_RQST0, 10, PMIC_AUXADC_IMP1, 15, 16, 1, 1), + MTK_PMIC_ADC_CHAN(VBIF, PMIC_AUXADC_RQST0, 11, PMIC_AUXADC_IMP1, 15, 8, 5, 2), + MTK_PMIC_ADC_CHAN(VCORE_TEMP, PMIC_AUXADC_RQST1, 8, PMIC_AUXADC_IMP1, 15, 8, 1, 1), + MTK_PMIC_ADC_CHAN(VPROC_TEMP, PMIC_AUXADC_RQST1, 9, PMIC_AUXADC_IMP1, 15, 8, 1, 1), + MTK_PMIC_ADC_CHAN(VGPU_TEMP, PMIC_AUXADC_RQST1, 10, PMIC_AUXADC_IMP1, 15, 8, 1, 1), + + /* Battery impedance channels */ + MTK_PMIC_ADC_CHAN(VBAT, 0, 0, PMIC_AUXADC_IMP1, 15, 128, 7, 2), + MTK_PMIC_ADC_CHAN(IBAT, 0, 0, PMIC_AUXADC_IMP1, 15, 128, 7, 2), +}; + +static const u16 mt6359_auxadc_regs[] = { + [PMIC_FGADC_R_CON0] = 0x0d88, + [PMIC_HK_TOP_WKEY] = 0x0fb4, + [PMIC_HK_TOP_RST_CON0] = 0x0f90, + [PMIC_AUXADC_RQST0] = 0x1108, + [PMIC_AUXADC_RQST1] = 0x110a, + [PMIC_AUXADC_ADC0] = 0x1088, + [PMIC_AUXADC_IMP0] = 0x1208, + [PMIC_AUXADC_IMP1] = 0x120a, + [PMIC_AUXADC_IMP3] = 0x120e, +}; + +static const struct iio_chan_spec mt6363_auxadc_channels[] = { + MTK_PMIC_IIO_CHAN(MT6363, bat_adc, BATADC, 0, 15, IIO_RESISTANCE), + MTK_PMIC_IIO_CHAN(MT6363, cdt_v, VCDT, 2, 12, IIO_TEMP), + MTK_PMIC_IIO_CHAN(MT6363, batt_temp, BAT_TEMP, 3, 12, IIO_TEMP), + MTK_PMIC_IIO_CHAN(MT6363, chip_temp, CHIP_TEMP, 4, 12, IIO_TEMP), + MTK_PMIC_IIO_CHAN(MT6363, sys_sns_v, VSYSSNS, 6, 15, IIO_VOLTAGE), + MTK_PMIC_IIO_CHAN(MT6363, tref_v, VTREF, 11, 12, IIO_VOLTAGE), + MTK_PMIC_IIO_CHAN(MT6363, vcore_temp, VCORE_TEMP, 38, 12, IIO_TEMP), + MTK_PMIC_IIO_CHAN(MT6363, vproc_temp, VPROC_TEMP, 39, 12, IIO_TEMP), + MTK_PMIC_IIO_CHAN(MT6363, vgpu_temp, VGPU_TEMP, 40, 12, IIO_TEMP), + + /* For VIN, ADC12 holds the result depending on which GPIO was activated */ + MTK_PMIC_IIO_CHAN(MT6363, in1_v, VIN1, 45, 15, IIO_VOLTAGE), + MTK_PMIC_IIO_CHAN(MT6363, in2_v, VIN2, 45, 15, IIO_VOLTAGE), + MTK_PMIC_IIO_CHAN(MT6363, in3_v, VIN3, 45, 15, IIO_VOLTAGE), + MTK_PMIC_IIO_CHAN(MT6363, in4_v, VIN4, 45, 15, IIO_VOLTAGE), + MTK_PMIC_IIO_CHAN(MT6363, in5_v, VIN5, 45, 15, IIO_VOLTAGE), + MTK_PMIC_IIO_CHAN(MT6363, in6_v, VIN6, 45, 15, IIO_VOLTAGE), + MTK_PMIC_IIO_CHAN(MT6363, in7_v, VIN7, 45, 15, IIO_VOLTAGE), +}; + +static const struct mtk_pmic_auxadc_chan mt6363_auxadc_ch_desc[] = { + MTK_PMIC_ADC_CHAN(BATADC, PMIC_AUXADC_RQST0, 0, PMIC_AUXADC_ADC0, 15, 64, 4, 1), + MTK_PMIC_ADC_CHAN(VCDT, PMIC_AUXADC_RQST0, 2, PMIC_AUXADC_ADC0, 15, 32, 1, 1), + MTK_PMIC_ADC_CHAN(BAT_TEMP, PMIC_AUXADC_RQST0, 3, PMIC_AUXADC_ADC0, 15, 32, 3, 2), + MTK_PMIC_ADC_CHAN(CHIP_TEMP, PMIC_AUXADC_RQST0, 4, PMIC_AUXADC_ADC0, 15, 32, 1, 1), + MTK_PMIC_ADC_CHAN(VSYSSNS, PMIC_AUXADC_RQST1, 6, PMIC_AUXADC_ADC0, 15, 64, 3, 1), + MTK_PMIC_ADC_CHAN(VTREF, PMIC_AUXADC_RQST1, 3, PMIC_AUXADC_ADC0, 15, 32, 3, 2), + MTK_PMIC_ADC_CHAN(VCORE_TEMP, PMIC_AUXADC_RQST3, 0, PMIC_AUXADC_ADC0, 15, 32, 1, 1), + MTK_PMIC_ADC_CHAN(VPROC_TEMP, PMIC_AUXADC_RQST3, 1, PMIC_AUXADC_ADC0, 15, 32, 1, 1), + MTK_PMIC_ADC_CHAN(VGPU_TEMP, PMIC_AUXADC_RQST3, 2, PMIC_AUXADC_ADC0, 15, 32, 1, 1), + + MTK_PMIC_ADC_EXT_CHAN(VIN1, + PMIC_AUXADC_RQST1, 4, PMIC_AUXADC_ADC0, 15, + PMIC_AUXADC_SDMADC_CON0, 1, MT6363_PULLUP_RES_100K, 32, 1, 1), + MTK_PMIC_ADC_EXT_CHAN(VIN2, + PMIC_AUXADC_RQST1, 4, PMIC_AUXADC_ADC0, 15, + PMIC_AUXADC_SDMADC_CON0, 2, MT6363_PULLUP_RES_100K, 32, 1, 1), + MTK_PMIC_ADC_EXT_CHAN(VIN3, + PMIC_AUXADC_RQST1, 4, PMIC_AUXADC_ADC0, 15, + PMIC_AUXADC_SDMADC_CON0, 3, MT6363_PULLUP_RES_100K, 32, 1, 1), + MTK_PMIC_ADC_EXT_CHAN(VIN4, + PMIC_AUXADC_RQST1, 4, PMIC_AUXADC_ADC0, 15, + PMIC_AUXADC_SDMADC_CON0, 4, MT6363_PULLUP_RES_100K, 32, 1, 1), + MTK_PMIC_ADC_EXT_CHAN(VIN5, + PMIC_AUXADC_RQST1, 4, PMIC_AUXADC_ADC0, 15, + PMIC_AUXADC_SDMADC_CON0, 5, MT6363_PULLUP_RES_100K, 32, 1, 1), + MTK_PMIC_ADC_EXT_CHAN(VIN6, + PMIC_AUXADC_RQST1, 4, PMIC_AUXADC_ADC0, 15, + PMIC_AUXADC_SDMADC_CON0, 6, MT6363_PULLUP_RES_100K, 32, 1, 1), + MTK_PMIC_ADC_EXT_CHAN(VIN7, + PMIC_AUXADC_RQST1, 4, PMIC_AUXADC_ADC0, 15, + PMIC_AUXADC_SDMADC_CON0, 7, MT6363_PULLUP_RES_100K, 32, 1, 1), +}; + +static const u16 mt6363_auxadc_regs[] = { + [PMIC_AUXADC_RQST0] = 0x1108, + [PMIC_AUXADC_RQST1] = 0x1109, + [PMIC_AUXADC_RQST3] = 0x110c, + [PMIC_AUXADC_ADC0] = 0x1088, + [PMIC_AUXADC_IMP0] = 0x1208, + [PMIC_AUXADC_IMP1] = 0x1209, +}; + +static const struct iio_chan_spec mt6373_auxadc_channels[] = { + MTK_PMIC_IIO_CHAN(MT6363, chip_temp, CHIP_TEMP, 4, 12, IIO_TEMP), + MTK_PMIC_IIO_CHAN(MT6363, vcore_temp, VCORE_TEMP, 38, 12, IIO_TEMP), + MTK_PMIC_IIO_CHAN(MT6363, vproc_temp, VPROC_TEMP, 39, 12, IIO_TEMP), + MTK_PMIC_IIO_CHAN(MT6363, vgpu_temp, VGPU_TEMP, 40, 12, IIO_TEMP), + + /* For VIN, ADC12 holds the result depending on which GPIO was activated */ + MTK_PMIC_IIO_CHAN(MT6363, in1_v, VIN1, 45, 15, IIO_VOLTAGE), + MTK_PMIC_IIO_CHAN(MT6363, in2_v, VIN2, 45, 15, IIO_VOLTAGE), + MTK_PMIC_IIO_CHAN(MT6363, in3_v, VIN3, 45, 15, IIO_VOLTAGE), + MTK_PMIC_IIO_CHAN(MT6363, in4_v, VIN4, 45, 15, IIO_VOLTAGE), + MTK_PMIC_IIO_CHAN(MT6363, in5_v, VIN5, 45, 15, IIO_VOLTAGE), +}; + +static const struct mtk_pmic_auxadc_chan mt6373_auxadc_ch_desc[] = { + MTK_PMIC_ADC_CHAN(CHIP_TEMP, PMIC_AUXADC_RQST0, 4, PMIC_AUXADC_ADC0, 15, 32, 1, 1), + MTK_PMIC_ADC_CHAN(VCORE_TEMP, PMIC_AUXADC_RQST3, 0, PMIC_AUXADC_ADC0, 15, 32, 1, 1), + MTK_PMIC_ADC_CHAN(VPROC_TEMP, PMIC_AUXADC_RQST3, 1, PMIC_AUXADC_ADC0, 15, 32, 1, 1), + MTK_PMIC_ADC_CHAN(VGPU_TEMP, PMIC_AUXADC_RQST3, 2, PMIC_AUXADC_ADC0, 15, 32, 1, 1), + + MTK_PMIC_ADC_EXT_CHAN(VIN1, + PMIC_AUXADC_RQST1, 4, PMIC_AUXADC_ADC0, 15, + PMIC_AUXADC_SDMADC_CON0, 1, MT6363_PULLUP_RES_30K, 32, 1, 1), + MTK_PMIC_ADC_EXT_CHAN(VIN2, + PMIC_AUXADC_RQST1, 4, PMIC_AUXADC_ADC0, 15, + PMIC_AUXADC_SDMADC_CON0, 2, MT6363_PULLUP_RES_OPEN, 32, 1, 1), + MTK_PMIC_ADC_EXT_CHAN(VIN3, + PMIC_AUXADC_RQST1, 4, PMIC_AUXADC_ADC0, 15, + PMIC_AUXADC_SDMADC_CON0, 3, MT6363_PULLUP_RES_OPEN, 32, 1, 1), + MTK_PMIC_ADC_EXT_CHAN(VIN4, + PMIC_AUXADC_RQST1, 4, PMIC_AUXADC_ADC0, 15, + PMIC_AUXADC_SDMADC_CON0, 4, MT6363_PULLUP_RES_OPEN, 32, 1, 1), + MTK_PMIC_ADC_EXT_CHAN(VIN5, + PMIC_AUXADC_RQST1, 4, PMIC_AUXADC_ADC0, 15, + PMIC_AUXADC_SDMADC_CON0, 5, MT6363_PULLUP_RES_OPEN, 32, 1, 1), +}; + +static void mt6358_stop_imp_conv(struct mt6359_auxadc *adc_dev) +{ + const struct mtk_pmic_auxadc_info *cinfo = adc_dev->chip_info; + struct regmap *regmap = adc_dev->regmap; + + regmap_set_bits(regmap, cinfo->regs[PMIC_AUXADC_IMP0], MT6358_IMP0_CLEAR); + regmap_clear_bits(regmap, cinfo->regs[PMIC_AUXADC_IMP0], MT6358_IMP0_CLEAR); + regmap_clear_bits(regmap, cinfo->regs[PMIC_AUXADC_IMP1], MT6358_IMP1_AUTOREPEAT_EN); + regmap_clear_bits(regmap, cinfo->regs[PMIC_AUXADC_DCM_CON], MT6358_DCM_CK_SW_EN); +} + +static int mt6358_start_imp_conv(struct mt6359_auxadc *adc_dev, const struct iio_chan_spec *chan) +{ + const struct mtk_pmic_auxadc_info *cinfo = adc_dev->chip_info; + const struct mtk_pmic_auxadc_chan *desc = &cinfo->desc[chan->scan_index]; + struct regmap *regmap = adc_dev->regmap; + u32 val; + int ret; + + regmap_set_bits(regmap, cinfo->regs[PMIC_AUXADC_DCM_CON], MT6358_DCM_CK_SW_EN); + regmap_set_bits(regmap, cinfo->regs[PMIC_AUXADC_IMP1], MT6358_IMP1_AUTOREPEAT_EN); + + ret = regmap_read_poll_timeout(regmap, cinfo->regs[desc->rdy_idx], + val, val & desc->rdy_mask, + IMP_POLL_DELAY_US, AUXADC_TIMEOUT_US); + if (ret) { + mt6358_stop_imp_conv(adc_dev); + return ret; + } + + return 0; +} + +static int mt6358_read_imp(struct mt6359_auxadc *adc_dev, + const struct iio_chan_spec *chan, int *vbat, int *ibat) +{ + const struct mtk_pmic_auxadc_info *cinfo = adc_dev->chip_info; + struct regmap *regmap = adc_dev->regmap; + u16 reg_adc0 = cinfo->regs[PMIC_AUXADC_ADC0]; + u32 val_v; + int ret; + + ret = mt6358_start_imp_conv(adc_dev, chan); + if (ret) + return ret; + + /* Read the params before stopping */ + val_v = 0; + regmap_read(regmap, reg_adc0 + (cinfo->imp_adc_num << 1), &val_v); + + mt6358_stop_imp_conv(adc_dev); + + if (vbat) + *vbat = val_v; + if (ibat) + *ibat = 0; + + return 0; +} + +static int mt6359_read_imp(struct mt6359_auxadc *adc_dev, + const struct iio_chan_spec *chan, int *vbat, int *ibat) +{ + const struct mtk_pmic_auxadc_info *cinfo = adc_dev->chip_info; + const struct mtk_pmic_auxadc_chan *desc = &cinfo->desc[chan->scan_index]; + struct regmap *regmap = adc_dev->regmap; + u32 val, val_v, val_i; + int ret; + + /* Start conversion */ + regmap_write(regmap, cinfo->regs[PMIC_AUXADC_IMP0], MT6359_IMP0_CONV_EN); + ret = regmap_read_poll_timeout(regmap, cinfo->regs[desc->rdy_idx], + val, val & desc->rdy_mask, + IMP_POLL_DELAY_US, AUXADC_TIMEOUT_US); + + /* Stop conversion regardless of the result */ + regmap_write(regmap, cinfo->regs[PMIC_AUXADC_IMP0], 0); + if (ret) + return ret; + + /* If it succeeded, wait for the registers to be populated */ + fsleep(IMP_STOP_DELAY_US); + + ret = regmap_read(regmap, cinfo->regs[PMIC_AUXADC_IMP3], &val_v); + if (ret) + return ret; + + ret = regmap_read(regmap, cinfo->regs[PMIC_FGADC_R_CON0], &val_i); + if (ret) + return ret; + + if (vbat) + *vbat = val_v; + if (ibat) + *ibat = val_i; + + return 0; +} + +static const struct mtk_pmic_auxadc_info mt6357_chip_info = { + .model_name = "MT6357", + .channels = mt6357_auxadc_channels, + .num_channels = ARRAY_SIZE(mt6357_auxadc_channels), + .desc = mt6357_auxadc_ch_desc, + .regs = mt6357_auxadc_regs, + .imp_adc_num = MT6357_IMP_ADC_NUM, + .read_imp = mt6358_read_imp, + .vref_mV = 1800, +}; + +static const struct mtk_pmic_auxadc_info mt6358_chip_info = { + .model_name = "MT6358", + .channels = mt6358_auxadc_channels, + .num_channels = ARRAY_SIZE(mt6358_auxadc_channels), + .desc = mt6358_auxadc_ch_desc, + .regs = mt6358_auxadc_regs, + .imp_adc_num = MT6358_IMP_ADC_NUM, + .read_imp = mt6358_read_imp, + .vref_mV = 1800, +}; + +static const struct mtk_pmic_auxadc_info mt6359_chip_info = { + .model_name = "MT6359", + .channels = mt6359_auxadc_channels, + .num_channels = ARRAY_SIZE(mt6359_auxadc_channels), + .desc = mt6359_auxadc_ch_desc, + .regs = mt6359_auxadc_regs, + .sec_unlock_key = 0x6359, + .read_imp = mt6359_read_imp, + .vref_mV = 1800, +}; + +static const struct mtk_pmic_auxadc_info mt6363_chip_info = { + .model_name = "MT6363", + .channels = mt6363_auxadc_channels, + .num_channels = ARRAY_SIZE(mt6363_auxadc_channels), + .desc = mt6363_auxadc_ch_desc, + .regs = mt6363_auxadc_regs, + .is_spmi = true, + .no_reset = true, + .vref_mV = 1840, +}; + +static const struct mtk_pmic_auxadc_info mt6373_chip_info = { + .model_name = "MT6373", + .channels = mt6373_auxadc_channels, + .num_channels = ARRAY_SIZE(mt6373_auxadc_channels), + .desc = mt6373_auxadc_ch_desc, + .regs = mt6363_auxadc_regs, + .is_spmi = true, + .no_reset = true, + .vref_mV = 1840, +}; + +static void mt6359_auxadc_reset(struct mt6359_auxadc *adc_dev) +{ + const struct mtk_pmic_auxadc_info *cinfo = adc_dev->chip_info; + struct regmap *regmap = adc_dev->regmap; + + /* Some PMICs do not support reset */ + if (cinfo->no_reset) + return; + + /* Unlock HK_TOP writes */ + if (cinfo->sec_unlock_key) + regmap_write(regmap, cinfo->regs[PMIC_HK_TOP_WKEY], cinfo->sec_unlock_key); + + /* Assert ADC reset */ + regmap_set_bits(regmap, cinfo->regs[PMIC_HK_TOP_RST_CON0], PMIC_RG_RESET_VAL); + + /* De-assert ADC reset. No wait required, as pwrap takes care of that for us. */ + regmap_clear_bits(regmap, cinfo->regs[PMIC_HK_TOP_RST_CON0], PMIC_RG_RESET_VAL); + + /* Lock HK_TOP writes again */ + if (cinfo->sec_unlock_key) + regmap_write(regmap, cinfo->regs[PMIC_HK_TOP_WKEY], 0); +} + +/** + * mt6359_auxadc_sample_adc_val() - Start ADC channel sampling and read value + * @adc_dev: Main driver structure + * @chan: IIO Channel spec for requested ADC + * @out: Preallocated variable to store the value read from HW + * + * This function starts the sampling for an ADC channel, waits until all + * of the samples are averaged and then reads the value from the HW. + * + * Note that the caller must stop the ADC sampling on its own, as this + * function *never* stops it. + * + * Return: + * Negative number for error; + * Upon success returns zero and writes the read value to *out. + */ +static int mt6359_auxadc_sample_adc_val(struct mt6359_auxadc *adc_dev, + const struct iio_chan_spec *chan, u32 *out) +{ + const struct mtk_pmic_auxadc_info *cinfo = adc_dev->chip_info; + const struct mtk_pmic_auxadc_chan *desc = &cinfo->desc[chan->scan_index]; + struct regmap *regmap = adc_dev->regmap; + u32 reg, rdy_mask, val, lval; + int ret; + + /* Request to start sampling for ADC channel */ + ret = regmap_write(regmap, cinfo->regs[desc->req_idx], desc->req_mask); + if (ret) + return ret; + + /* Wait until all samples are averaged */ + fsleep(desc->num_samples * AUXADC_AVG_TIME_US); + + reg = cinfo->regs[PMIC_AUXADC_ADC0] + (chan->address << 1); + rdy_mask = PMIC_AUXADC_RDY_BIT; + + /* + * Even though for both PWRAP and SPMI cases the ADC HW signals that + * the data is ready by setting AUXADC_RDY_BIT, for SPMI the register + * read is only 8 bits long: for this case, the check has to be done + * on the ADC(x)_H register (high bits) and the rdy_mask needs to be + * shifted to the right by the same 8 bits. + */ + if (cinfo->is_spmi) { + rdy_mask >>= 8; + reg += 1; + } + + ret = regmap_read_poll_timeout(regmap, reg, val, val & rdy_mask, + AUXADC_POLL_DELAY_US, AUXADC_TIMEOUT_US); + if (ret) { + dev_dbg(adc_dev->dev, "ADC read timeout for chan %lu\n", chan->address); + return ret; + } + + if (cinfo->is_spmi) { + ret = regmap_read(regmap, reg - 1, &lval); + if (ret) + return ret; + + val = (val << 8) | lval; + } + + *out = val; + return 0; +} + +static int mt6359_auxadc_read_adc(struct mt6359_auxadc *adc_dev, + const struct iio_chan_spec *chan, int *out) +{ + const struct mtk_pmic_auxadc_info *cinfo = adc_dev->chip_info; + const struct mtk_pmic_auxadc_chan *desc = &cinfo->desc[chan->scan_index]; + struct regmap *regmap = adc_dev->regmap; + int ret, adc_stop_err; + u8 ext_sel; + u32 val; + + if (desc->ext_sel_idx >= 0) { + ext_sel = FIELD_PREP(MT6363_EXT_PURES_MASK, desc->ext_sel_pu); + ext_sel |= FIELD_PREP(MT6363_EXT_CHAN_MASK, desc->ext_sel_ch); + + ret = regmap_update_bits(regmap, cinfo->regs[desc->ext_sel_idx], + MT6363_EXT_PURES_MASK | MT6363_EXT_CHAN_MASK, + ext_sel); + if (ret) + return ret; + } + + /* + * Get sampled value, then stop sampling unconditionally; the gathered + * value is good regardless of if the ADC could be stopped. + * + * Note that if the ADC cannot be stopped but sampling was ok, this + * function will not return any error, but will set the timed_out + * status: this is not critical, as the ADC may auto recover and auto + * stop after some time (depending on the PMIC model); if not, the next + * read attempt will return -ETIMEDOUT and, for models that support it, + * reset will be triggered. + */ + ret = mt6359_auxadc_sample_adc_val(adc_dev, chan, &val); + + adc_stop_err = regmap_write(regmap, cinfo->regs[desc->req_idx], 0); + if (adc_stop_err) { + dev_warn(adc_dev->dev, "Could not stop the ADC: %d\n,", adc_stop_err); + adc_dev->timed_out = true; + } + + /* If any sampling error occurred, the retrieved value is invalid */ + if (ret) + return ret; + + /* ...and deactivate the ADC GPIO if previously done */ + if (desc->ext_sel_idx >= 0) { + ext_sel = FIELD_PREP(MT6363_EXT_PURES_MASK, MT6363_PULLUP_RES_OPEN); + + ret = regmap_update_bits(regmap, cinfo->regs[desc->ext_sel_idx], + MT6363_EXT_PURES_MASK, ext_sel); + if (ret) + return ret; + } + + /* Everything went fine, give back the ADC reading */ + *out = val & GENMASK(chan->scan_type.realbits - 1, 0); + return 0; +} + +static int mt6359_auxadc_read_label(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, char *label) +{ + return sysfs_emit(label, "%s\n", chan->datasheet_name); +} + +static int mt6359_auxadc_read_raw(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + int *val, int *val2, long mask) +{ + struct mt6359_auxadc *adc_dev = iio_priv(indio_dev); + const struct mtk_pmic_auxadc_info *cinfo = adc_dev->chip_info; + const struct mtk_pmic_auxadc_chan *desc = &cinfo->desc[chan->scan_index]; + int ret; + + if (mask == IIO_CHAN_INFO_SCALE) { + *val = desc->r_ratio.numerator * cinfo->vref_mV; + + if (desc->r_ratio.denominator > 1) { + *val2 = desc->r_ratio.denominator; + return IIO_VAL_FRACTIONAL; + } + + return IIO_VAL_INT; + } + + scoped_guard(mutex, &adc_dev->lock) { + switch (chan->scan_index) { + case PMIC_AUXADC_CHAN_IBAT: + if (!adc_dev->chip_info->read_imp) + return -EOPNOTSUPP; + + ret = adc_dev->chip_info->read_imp(adc_dev, chan, NULL, val); + break; + case PMIC_AUXADC_CHAN_VBAT: + if (!adc_dev->chip_info->read_imp) + return -EOPNOTSUPP; + + ret = adc_dev->chip_info->read_imp(adc_dev, chan, val, NULL); + break; + default: + ret = mt6359_auxadc_read_adc(adc_dev, chan, val); + break; + } + } + + if (ret) { + /* + * If we get more than one timeout, it's possible that the + * AUXADC is stuck: perform a full reset to recover it. + */ + if (ret == -ETIMEDOUT) { + if (adc_dev->timed_out) { + dev_warn(adc_dev->dev, "Resetting stuck ADC!\r\n"); + mt6359_auxadc_reset(adc_dev); + } + adc_dev->timed_out = true; + } + return ret; + } + adc_dev->timed_out = false; + + return IIO_VAL_INT; +} + +static const struct iio_info mt6359_auxadc_iio_info = { + .read_label = mt6359_auxadc_read_label, + .read_raw = mt6359_auxadc_read_raw, +}; + +static int mt6359_auxadc_probe(struct platform_device *pdev) +{ + const struct mtk_pmic_auxadc_info *chip_info; + struct device *dev = &pdev->dev; + struct device *mfd_dev = dev->parent; + struct mt6359_auxadc *adc_dev; + struct iio_dev *indio_dev; + struct device *regmap_dev; + struct regmap *regmap; + int ret; + + chip_info = device_get_match_data(dev); + if (!chip_info) + return -EINVAL; + /* + * The regmap for this device has to be acquired differently for + * SoC PMIC Wrapper and SPMI PMIC cases: + * + * If this is under SPMI, the regmap comes from the direct parent of + * this driver: this_device->parent(mfd). + * ... or ... + * If this is under the SoC PMIC Wrapper, the regmap comes from the + * parent of the MT6397 MFD: this_device->parent(mfd)->parent(pwrap) + */ + if (chip_info->is_spmi) + regmap_dev = mfd_dev; + else + regmap_dev = mfd_dev->parent; + + + /* Regmap is from SoC PMIC Wrapper, parent of the mt6397 MFD */ + regmap = dev_get_regmap(regmap_dev, NULL); + if (!regmap) + return dev_err_probe(dev, -ENODEV, "Failed to get regmap\n"); + + indio_dev = devm_iio_device_alloc(dev, sizeof(*adc_dev)); + if (!indio_dev) + return -ENOMEM; + + adc_dev = iio_priv(indio_dev); + adc_dev->regmap = regmap; + adc_dev->dev = dev; + adc_dev->chip_info = chip_info; + + mutex_init(&adc_dev->lock); + + mt6359_auxadc_reset(adc_dev); + + indio_dev->name = adc_dev->chip_info->model_name; + indio_dev->info = &mt6359_auxadc_iio_info; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = adc_dev->chip_info->channels; + indio_dev->num_channels = adc_dev->chip_info->num_channels; + + ret = devm_iio_device_register(dev, indio_dev); + if (ret) + return dev_err_probe(dev, ret, "failed to register iio device\n"); + + return 0; +} + +static const struct of_device_id mt6359_auxadc_of_match[] = { + { .compatible = "mediatek,mt6357-auxadc", .data = &mt6357_chip_info }, + { .compatible = "mediatek,mt6358-auxadc", .data = &mt6358_chip_info }, + { .compatible = "mediatek,mt6359-auxadc", .data = &mt6359_chip_info }, + { .compatible = "mediatek,mt6363-auxadc", .data = &mt6363_chip_info }, + { .compatible = "mediatek,mt6373-auxadc", .data = &mt6373_chip_info }, + { } +}; +MODULE_DEVICE_TABLE(of, mt6359_auxadc_of_match); + +static struct platform_driver mt6359_auxadc_driver = { + .driver = { + .name = "mt6359-auxadc", + .of_match_table = mt6359_auxadc_of_match, + }, + .probe = mt6359_auxadc_probe, +}; +module_platform_driver(mt6359_auxadc_driver); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("AngeloGioacchino Del Regno "); +MODULE_DESCRIPTION("MediaTek MT6359 PMIC AUXADC Driver"); diff --git a/drivers/iio/adc/mt6360-adc.c b/drivers/iio/adc/mt6360-adc.c new file mode 100644 index 000000000..e59b13e88 --- /dev/null +++ b/drivers/iio/adc/mt6360-adc.c @@ -0,0 +1,371 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +#define MT6360_REG_PMUCHGCTRL3 0x313 +#define MT6360_REG_PMUADCCFG 0x356 +#define MT6360_REG_PMUADCIDLET 0x358 +#define MT6360_REG_PMUADCRPT1 0x35A + +/* PMUCHGCTRL3 0x313 */ +#define MT6360_AICR_MASK GENMASK(7, 2) +#define MT6360_AICR_SHFT 2 +#define MT6360_AICR_400MA 0x6 +/* PMUADCCFG 0x356 */ +#define MT6360_ADCEN_MASK BIT(15) +/* PMUADCRPT1 0x35A */ +#define MT6360_PREFERCH_MASK GENMASK(7, 4) +#define MT6360_PREFERCH_SHFT 4 +#define MT6360_RPTCH_MASK GENMASK(3, 0) +#define MT6360_NO_PREFER 15 + +/* Time in ms */ +#define ADC_WAIT_TIME_MS 25 +#define ADC_CONV_TIMEOUT_MS 100 +#define ADC_LOOP_TIME_US 2000 + +enum { + MT6360_CHAN_USBID = 0, + MT6360_CHAN_VBUSDIV5, + MT6360_CHAN_VBUSDIV2, + MT6360_CHAN_VSYS, + MT6360_CHAN_VBAT, + MT6360_CHAN_IBUS, + MT6360_CHAN_IBAT, + MT6360_CHAN_CHG_VDDP, + MT6360_CHAN_TEMP_JC, + MT6360_CHAN_VREF_TS, + MT6360_CHAN_TS, + MT6360_CHAN_MAX +}; + +struct mt6360_adc_data { + struct device *dev; + struct regmap *regmap; + /* Due to only one set of ADC control, this lock is used to prevent the race condition */ + struct mutex adc_lock; + ktime_t last_off_timestamps[MT6360_CHAN_MAX]; +}; + +static int mt6360_adc_read_channel(struct mt6360_adc_data *mad, int channel, int *val) +{ + __be16 adc_enable; + u8 rpt[3]; + ktime_t predict_end_t, timeout; + unsigned int pre_wait_time; + int ret; + + mutex_lock(&mad->adc_lock); + + /* Select the preferred ADC channel */ + ret = regmap_update_bits(mad->regmap, MT6360_REG_PMUADCRPT1, MT6360_PREFERCH_MASK, + channel << MT6360_PREFERCH_SHFT); + if (ret) + goto out_adc_lock; + + adc_enable = cpu_to_be16(MT6360_ADCEN_MASK | BIT(channel)); + ret = regmap_raw_write(mad->regmap, MT6360_REG_PMUADCCFG, &adc_enable, sizeof(adc_enable)); + if (ret) + goto out_adc_lock; + + predict_end_t = ktime_add_ms(mad->last_off_timestamps[channel], 2 * ADC_WAIT_TIME_MS); + + if (ktime_after(ktime_get(), predict_end_t)) + pre_wait_time = ADC_WAIT_TIME_MS; + else + pre_wait_time = 3 * ADC_WAIT_TIME_MS; + + if (msleep_interruptible(pre_wait_time)) { + ret = -ERESTARTSYS; + goto out_adc_conv; + } + + timeout = ktime_add_ms(ktime_get(), ADC_CONV_TIMEOUT_MS); + while (true) { + ret = regmap_raw_read(mad->regmap, MT6360_REG_PMUADCRPT1, rpt, sizeof(rpt)); + if (ret) + goto out_adc_conv; + + /* + * There are two functions, ZCV and TypeC OTP, running ADC VBAT and TS in + * background, and ADC samples are taken on a fixed frequency no matter read the + * previous one or not. + * To avoid conflict, We set minimum time threshold after enable ADC and + * check report channel is the same. + * The worst case is run the same ADC twice and background function is also running, + * ADC conversion sequence is desire channel before start ADC, background ADC, + * desire channel after start ADC. + * So the minimum correct data is three times of typical conversion time. + */ + if ((rpt[0] & MT6360_RPTCH_MASK) == channel) + break; + + if (ktime_compare(ktime_get(), timeout) > 0) { + ret = -ETIMEDOUT; + goto out_adc_conv; + } + + usleep_range(ADC_LOOP_TIME_US / 2, ADC_LOOP_TIME_US); + } + + *val = get_unaligned_be16(&rpt[1]); + ret = IIO_VAL_INT; + +out_adc_conv: + /* Only keep ADC enable */ + adc_enable = cpu_to_be16(MT6360_ADCEN_MASK); + regmap_raw_write(mad->regmap, MT6360_REG_PMUADCCFG, &adc_enable, sizeof(adc_enable)); + mad->last_off_timestamps[channel] = ktime_get(); + /* Config prefer channel to NO_PREFER */ + regmap_update_bits(mad->regmap, MT6360_REG_PMUADCRPT1, MT6360_PREFERCH_MASK, + MT6360_NO_PREFER << MT6360_PREFERCH_SHFT); +out_adc_lock: + mutex_unlock(&mad->adc_lock); + + return ret; +} + +static int mt6360_adc_read_scale(struct mt6360_adc_data *mad, int channel, int *val, int *val2) +{ + unsigned int regval; + int ret; + + switch (channel) { + case MT6360_CHAN_USBID: + case MT6360_CHAN_VSYS: + case MT6360_CHAN_VBAT: + case MT6360_CHAN_CHG_VDDP: + case MT6360_CHAN_VREF_TS: + case MT6360_CHAN_TS: + *val = 1250; + return IIO_VAL_INT; + case MT6360_CHAN_VBUSDIV5: + *val = 6250; + return IIO_VAL_INT; + case MT6360_CHAN_VBUSDIV2: + case MT6360_CHAN_IBUS: + case MT6360_CHAN_IBAT: + *val = 2500; + + if (channel == MT6360_CHAN_IBUS) { + /* IBUS will be affected by input current limit for the different Ron */ + /* Check whether the config is <400mA or not */ + ret = regmap_read(mad->regmap, MT6360_REG_PMUCHGCTRL3, ®val); + if (ret) + return ret; + + regval = (regval & MT6360_AICR_MASK) >> MT6360_AICR_SHFT; + if (regval < MT6360_AICR_400MA) + *val = 1900; + } + + return IIO_VAL_INT; + case MT6360_CHAN_TEMP_JC: + *val = 105; + *val2 = 100; + return IIO_VAL_FRACTIONAL; + } + + return -EINVAL; +} + +static int mt6360_adc_read_offset(struct mt6360_adc_data *mad, int channel, int *val) +{ + *val = (channel == MT6360_CHAN_TEMP_JC) ? -80 : 0; + return IIO_VAL_INT; +} + +static int mt6360_adc_read_raw(struct iio_dev *iio_dev, const struct iio_chan_spec *chan, + int *val, int *val2, long mask) +{ + struct mt6360_adc_data *mad = iio_priv(iio_dev); + + switch (mask) { + case IIO_CHAN_INFO_RAW: + return mt6360_adc_read_channel(mad, chan->channel, val); + case IIO_CHAN_INFO_SCALE: + return mt6360_adc_read_scale(mad, chan->channel, val, val2); + case IIO_CHAN_INFO_OFFSET: + return mt6360_adc_read_offset(mad, chan->channel, val); + } + + return -EINVAL; +} + +static const char *mt6360_channel_labels[MT6360_CHAN_MAX] = { + "usbid", "vbusdiv5", "vbusdiv2", "vsys", "vbat", "ibus", "ibat", "chg_vddp", + "temp_jc", "vref_ts", "ts", +}; + +static int mt6360_adc_read_label(struct iio_dev *iio_dev, const struct iio_chan_spec *chan, + char *label) +{ + return sysfs_emit(label, "%s\n", mt6360_channel_labels[chan->channel]); +} + +static const struct iio_info mt6360_adc_iio_info = { + .read_raw = mt6360_adc_read_raw, + .read_label = mt6360_adc_read_label, +}; + +#define MT6360_ADC_CHAN(_idx, _type) { \ + .type = _type, \ + .channel = MT6360_CHAN_##_idx, \ + .scan_index = MT6360_CHAN_##_idx, \ + .datasheet_name = #_idx, \ + .scan_type = { \ + .sign = 'u', \ + .realbits = 16, \ + .storagebits = 16, \ + .endianness = IIO_CPU, \ + }, \ + .indexed = 1, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ + BIT(IIO_CHAN_INFO_SCALE) | \ + BIT(IIO_CHAN_INFO_OFFSET), \ +} + +static const struct iio_chan_spec mt6360_adc_channels[] = { + MT6360_ADC_CHAN(USBID, IIO_VOLTAGE), + MT6360_ADC_CHAN(VBUSDIV5, IIO_VOLTAGE), + MT6360_ADC_CHAN(VBUSDIV2, IIO_VOLTAGE), + MT6360_ADC_CHAN(VSYS, IIO_VOLTAGE), + MT6360_ADC_CHAN(VBAT, IIO_VOLTAGE), + MT6360_ADC_CHAN(IBUS, IIO_CURRENT), + MT6360_ADC_CHAN(IBAT, IIO_CURRENT), + MT6360_ADC_CHAN(CHG_VDDP, IIO_VOLTAGE), + MT6360_ADC_CHAN(TEMP_JC, IIO_TEMP), + MT6360_ADC_CHAN(VREF_TS, IIO_VOLTAGE), + MT6360_ADC_CHAN(TS, IIO_VOLTAGE), + IIO_CHAN_SOFT_TIMESTAMP(MT6360_CHAN_MAX), +}; + +static irqreturn_t mt6360_adc_trigger_handler(int irq, void *p) +{ + struct iio_poll_func *pf = p; + struct iio_dev *indio_dev = pf->indio_dev; + struct mt6360_adc_data *mad = iio_priv(indio_dev); + struct { + u16 values[MT6360_CHAN_MAX]; + aligned_s64 timestamp; + } data = { }; + int i = 0, bit, val, ret; + + iio_for_each_active_channel(indio_dev, bit) { + ret = mt6360_adc_read_channel(mad, bit, &val); + if (ret < 0) { + dev_warn(&indio_dev->dev, "Failed to get channel %d conversion val\n", bit); + goto out; + } + + data.values[i++] = val; + } + iio_push_to_buffers_with_timestamp(indio_dev, &data, iio_get_time_ns(indio_dev)); +out: + iio_trigger_notify_done(indio_dev->trig); + + return IRQ_HANDLED; +} + +static inline int mt6360_adc_reset(struct mt6360_adc_data *info) +{ + __be16 adc_enable; + ktime_t all_off_time; + int i, ret; + + /* Clear ADC idle wait time to 0 */ + ret = regmap_write(info->regmap, MT6360_REG_PMUADCIDLET, 0); + if (ret) + return ret; + + /* Only keep ADC enable, but keep all channels off */ + adc_enable = cpu_to_be16(MT6360_ADCEN_MASK); + ret = regmap_raw_write(info->regmap, MT6360_REG_PMUADCCFG, &adc_enable, sizeof(adc_enable)); + if (ret) + return ret; + + /* Reset all channel off time to the current one */ + all_off_time = ktime_get(); + for (i = 0; i < MT6360_CHAN_MAX; i++) + info->last_off_timestamps[i] = all_off_time; + + return 0; +} + +static int mt6360_adc_probe(struct platform_device *pdev) +{ + struct mt6360_adc_data *mad; + struct regmap *regmap; + struct iio_dev *indio_dev; + int ret; + + regmap = dev_get_regmap(pdev->dev.parent, NULL); + if (!regmap) { + dev_err(&pdev->dev, "Failed to get parent regmap\n"); + return -ENODEV; + } + + indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*mad)); + if (!indio_dev) + return -ENOMEM; + + mad = iio_priv(indio_dev); + mad->dev = &pdev->dev; + mad->regmap = regmap; + mutex_init(&mad->adc_lock); + + ret = mt6360_adc_reset(mad); + if (ret < 0) { + dev_err(&pdev->dev, "Failed to reset adc\n"); + return ret; + } + + indio_dev->name = dev_name(&pdev->dev); + indio_dev->info = &mt6360_adc_iio_info; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = mt6360_adc_channels; + indio_dev->num_channels = ARRAY_SIZE(mt6360_adc_channels); + + ret = devm_iio_triggered_buffer_setup(&pdev->dev, indio_dev, NULL, + mt6360_adc_trigger_handler, NULL); + if (ret) { + dev_err(&pdev->dev, "Failed to allocate iio trigger buffer\n"); + return ret; + } + + return devm_iio_device_register(&pdev->dev, indio_dev); +} + +static const struct of_device_id mt6360_adc_of_id[] = { + { .compatible = "mediatek,mt6360-adc", }, + { } +}; +MODULE_DEVICE_TABLE(of, mt6360_adc_of_id); + +static struct platform_driver mt6360_adc_driver = { + .driver = { + .name = "mt6360-adc", + .of_match_table = mt6360_adc_of_id, + }, + .probe = mt6360_adc_probe, +}; +module_platform_driver(mt6360_adc_driver); + +MODULE_AUTHOR("Gene Chen "); +MODULE_DESCRIPTION("MT6360 ADC Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/adc/mt6370-adc.c b/drivers/iio/adc/mt6370-adc.c new file mode 100644 index 000000000..c250385f9 --- /dev/null +++ b/drivers/iio/adc/mt6370-adc.c @@ -0,0 +1,353 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2022 Richtek Technology Corp. + * + * Author: ChiaEn Wu + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define MT6370_REG_DEV_INFO 0x100 +#define MT6370_REG_CHG_CTRL3 0x113 +#define MT6370_REG_CHG_CTRL7 0x117 +#define MT6370_REG_CHG_ADC 0x121 +#define MT6370_REG_ADC_DATA_H 0x14C + +#define MT6370_ADC_START_MASK BIT(0) +#define MT6370_ADC_IN_SEL_MASK GENMASK(7, 4) +#define MT6370_AICR_ICHG_MASK GENMASK(7, 2) +#define MT6370_VENID_MASK GENMASK(7, 4) + +#define MT6370_AICR_100_mA 0x0 +#define MT6370_AICR_150_mA 0x1 +#define MT6370_AICR_200_mA 0x2 +#define MT6370_AICR_250_mA 0x3 +#define MT6370_AICR_300_mA 0x4 +#define MT6370_AICR_350_mA 0x5 + +#define MT6370_ICHG_100_mA 0x0 +#define MT6370_ICHG_200_mA 0x1 +#define MT6370_ICHG_300_mA 0x2 +#define MT6370_ICHG_400_mA 0x3 +#define MT6370_ICHG_500_mA 0x4 +#define MT6370_ICHG_600_mA 0x5 +#define MT6370_ICHG_700_mA 0x6 +#define MT6370_ICHG_800_mA 0x7 + +#define ADC_CONV_TIME_MS 35 +#define ADC_CONV_POLLING_TIME_US 1000 + +#define MT6370_VID_RT5081 0x8 +#define MT6370_VID_RT5081A 0xA +#define MT6370_VID_MT6370 0xE + +struct mt6370_adc_data { + struct device *dev; + struct regmap *regmap; + /* + * This mutex lock is for preventing the different ADC channels + * from being read at the same time. + */ + struct mutex adc_lock; + unsigned int vid; +}; + +static int mt6370_adc_read_channel(struct mt6370_adc_data *priv, int chan, + unsigned long addr, int *val) +{ + unsigned int reg_val; + __be16 be_val; + int ret; + + mutex_lock(&priv->adc_lock); + + reg_val = MT6370_ADC_START_MASK | + FIELD_PREP(MT6370_ADC_IN_SEL_MASK, addr); + ret = regmap_write(priv->regmap, MT6370_REG_CHG_ADC, reg_val); + if (ret) + goto adc_unlock; + + msleep(ADC_CONV_TIME_MS); + + ret = regmap_read_poll_timeout(priv->regmap, + MT6370_REG_CHG_ADC, reg_val, + !(reg_val & MT6370_ADC_START_MASK), + ADC_CONV_POLLING_TIME_US, + ADC_CONV_TIME_MS * MILLI * 3); + if (ret) { + dev_err(priv->dev, "Failed to read ADC register (%d)\n", ret); + goto adc_unlock; + } + + ret = regmap_raw_read(priv->regmap, MT6370_REG_ADC_DATA_H, + &be_val, sizeof(be_val)); + if (ret) + goto adc_unlock; + + *val = be16_to_cpu(be_val); + ret = IIO_VAL_INT; + +adc_unlock: + mutex_unlock(&priv->adc_lock); + + return ret; +} + +static int mt6370_adc_get_ibus_scale(struct mt6370_adc_data *priv) +{ + switch (priv->vid) { + case MT6370_VID_RT5081: + case MT6370_VID_RT5081A: + case MT6370_VID_MT6370: + return 3350; + default: + return 3875; + } +} + +static int mt6370_adc_get_ibat_scale(struct mt6370_adc_data *priv) +{ + switch (priv->vid) { + case MT6370_VID_RT5081: + case MT6370_VID_RT5081A: + case MT6370_VID_MT6370: + return 2680; + default: + return 3870; + } +} + +static int mt6370_adc_read_scale(struct mt6370_adc_data *priv, + int chan, int *val1, int *val2) +{ + unsigned int reg_val; + int ret; + + switch (chan) { + case MT6370_CHAN_VBAT: + case MT6370_CHAN_VSYS: + case MT6370_CHAN_CHG_VDDP: + *val1 = 5; + return IIO_VAL_INT; + case MT6370_CHAN_IBUS: + ret = regmap_read(priv->regmap, MT6370_REG_CHG_CTRL3, ®_val); + if (ret) + return ret; + + reg_val = FIELD_GET(MT6370_AICR_ICHG_MASK, reg_val); + switch (reg_val) { + case MT6370_AICR_100_mA: + case MT6370_AICR_150_mA: + case MT6370_AICR_200_mA: + case MT6370_AICR_250_mA: + case MT6370_AICR_300_mA: + case MT6370_AICR_350_mA: + *val1 = mt6370_adc_get_ibus_scale(priv); + break; + default: + *val1 = 5000; + break; + } + + *val2 = 100; + + return IIO_VAL_FRACTIONAL; + case MT6370_CHAN_IBAT: + ret = regmap_read(priv->regmap, MT6370_REG_CHG_CTRL7, ®_val); + if (ret) + return ret; + + reg_val = FIELD_GET(MT6370_AICR_ICHG_MASK, reg_val); + switch (reg_val) { + case MT6370_ICHG_100_mA: + case MT6370_ICHG_200_mA: + case MT6370_ICHG_300_mA: + case MT6370_ICHG_400_mA: + *val1 = 2375; + break; + case MT6370_ICHG_500_mA: + case MT6370_ICHG_600_mA: + case MT6370_ICHG_700_mA: + case MT6370_ICHG_800_mA: + *val1 = mt6370_adc_get_ibat_scale(priv); + break; + default: + *val1 = 5000; + break; + } + + *val2 = 100; + + return IIO_VAL_FRACTIONAL; + case MT6370_CHAN_VBUSDIV5: + *val1 = 25; + return IIO_VAL_INT; + case MT6370_CHAN_VBUSDIV2: + *val1 = 10; + return IIO_VAL_INT; + case MT6370_CHAN_TS_BAT: + *val1 = 25; + *val2 = 10000; + return IIO_VAL_FRACTIONAL; + case MT6370_CHAN_TEMP_JC: + *val1 = 2000; + return IIO_VAL_INT; + default: + return -EINVAL; + } +} + +static int mt6370_adc_read_offset(struct mt6370_adc_data *priv, + int chan, int *val) +{ + *val = -20; + + return IIO_VAL_INT; +} + +static int mt6370_adc_read_raw(struct iio_dev *iio_dev, + const struct iio_chan_spec *chan, + int *val, int *val2, long mask) +{ + struct mt6370_adc_data *priv = iio_priv(iio_dev); + + switch (mask) { + case IIO_CHAN_INFO_RAW: + return mt6370_adc_read_channel(priv, chan->channel, + chan->address, val); + case IIO_CHAN_INFO_SCALE: + return mt6370_adc_read_scale(priv, chan->channel, val, val2); + case IIO_CHAN_INFO_OFFSET: + return mt6370_adc_read_offset(priv, chan->channel, val); + default: + return -EINVAL; + } +} + +static const char * const mt6370_channel_labels[MT6370_CHAN_MAX] = { + [MT6370_CHAN_VBUSDIV5] = "vbusdiv5", + [MT6370_CHAN_VBUSDIV2] = "vbusdiv2", + [MT6370_CHAN_VSYS] = "vsys", + [MT6370_CHAN_VBAT] = "vbat", + [MT6370_CHAN_TS_BAT] = "ts_bat", + [MT6370_CHAN_IBUS] = "ibus", + [MT6370_CHAN_IBAT] = "ibat", + [MT6370_CHAN_CHG_VDDP] = "chg_vddp", + [MT6370_CHAN_TEMP_JC] = "temp_jc", +}; + +static int mt6370_adc_read_label(struct iio_dev *iio_dev, + struct iio_chan_spec const *chan, char *label) +{ + return sysfs_emit(label, "%s\n", mt6370_channel_labels[chan->channel]); +} + +static const struct iio_info mt6370_adc_iio_info = { + .read_raw = mt6370_adc_read_raw, + .read_label = mt6370_adc_read_label, +}; + +#define MT6370_ADC_CHAN(_idx, _type, _addr, _extra_info) { \ + .type = _type, \ + .channel = MT6370_CHAN_##_idx, \ + .address = _addr, \ + .scan_index = MT6370_CHAN_##_idx, \ + .indexed = 1, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ + BIT(IIO_CHAN_INFO_SCALE) | \ + _extra_info, \ +} + +static const struct iio_chan_spec mt6370_adc_channels[] = { + MT6370_ADC_CHAN(VBUSDIV5, IIO_VOLTAGE, 1, 0), + MT6370_ADC_CHAN(VBUSDIV2, IIO_VOLTAGE, 2, 0), + MT6370_ADC_CHAN(VSYS, IIO_VOLTAGE, 3, 0), + MT6370_ADC_CHAN(VBAT, IIO_VOLTAGE, 4, 0), + MT6370_ADC_CHAN(TS_BAT, IIO_VOLTAGE, 6, 0), + MT6370_ADC_CHAN(IBUS, IIO_CURRENT, 8, 0), + MT6370_ADC_CHAN(IBAT, IIO_CURRENT, 9, 0), + MT6370_ADC_CHAN(CHG_VDDP, IIO_VOLTAGE, 11, 0), + MT6370_ADC_CHAN(TEMP_JC, IIO_TEMP, 12, BIT(IIO_CHAN_INFO_OFFSET)), +}; + +static int mt6370_get_vendor_info(struct mt6370_adc_data *priv) +{ + unsigned int dev_info; + int ret; + + ret = regmap_read(priv->regmap, MT6370_REG_DEV_INFO, &dev_info); + if (ret) + return ret; + + priv->vid = FIELD_GET(MT6370_VENID_MASK, dev_info); + + return 0; +} + +static int mt6370_adc_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct mt6370_adc_data *priv; + struct iio_dev *indio_dev; + struct regmap *regmap; + int ret; + + regmap = dev_get_regmap(pdev->dev.parent, NULL); + if (!regmap) + return dev_err_probe(dev, -ENODEV, "Failed to get regmap\n"); + + indio_dev = devm_iio_device_alloc(dev, sizeof(*priv)); + if (!indio_dev) + return -ENOMEM; + + priv = iio_priv(indio_dev); + priv->dev = dev; + priv->regmap = regmap; + mutex_init(&priv->adc_lock); + + ret = mt6370_get_vendor_info(priv); + if (ret) + return dev_err_probe(dev, ret, "Failed to get vid\n"); + + ret = regmap_write(priv->regmap, MT6370_REG_CHG_ADC, 0); + if (ret) + return dev_err_probe(dev, ret, "Failed to reset ADC\n"); + + indio_dev->name = "mt6370-adc"; + indio_dev->info = &mt6370_adc_iio_info; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = mt6370_adc_channels; + indio_dev->num_channels = ARRAY_SIZE(mt6370_adc_channels); + + return devm_iio_device_register(dev, indio_dev); +} + +static const struct of_device_id mt6370_adc_of_id[] = { + { .compatible = "mediatek,mt6370-adc", }, + { } +}; +MODULE_DEVICE_TABLE(of, mt6370_adc_of_id); + +static struct platform_driver mt6370_adc_driver = { + .driver = { + .name = "mt6370-adc", + .of_match_table = mt6370_adc_of_id, + }, + .probe = mt6370_adc_probe, +}; +module_platform_driver(mt6370_adc_driver); + +MODULE_AUTHOR("ChiaEn Wu "); +MODULE_DESCRIPTION("MT6370 ADC Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/adc/mt6577_auxadc.c b/drivers/iio/adc/mt6577_auxadc.c new file mode 100644 index 000000000..ecbae90ac --- /dev/null +++ b/drivers/iio/adc/mt6577_auxadc.c @@ -0,0 +1,335 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2016 MediaTek Inc. + * Author: Zhiyong Tao + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Register definitions */ +#define MT6577_AUXADC_CON0 0x00 +#define MT6577_AUXADC_CON1 0x04 +#define MT6577_AUXADC_CON2 0x10 +#define MT6577_AUXADC_STA BIT(0) + +#define MT6577_AUXADC_DAT0 0x14 +#define MT6577_AUXADC_RDY0 BIT(12) + +#define MT6577_AUXADC_MISC 0x94 +#define MT6577_AUXADC_PDN_EN BIT(14) + +#define MT6577_AUXADC_DAT_MASK 0xfff +#define MT6577_AUXADC_SLEEP_US 1000 +#define MT6577_AUXADC_TIMEOUT_US 10000 +#define MT6577_AUXADC_POWER_READY_MS 1 +#define MT6577_AUXADC_SAMPLE_READY_US 25 + +struct mtk_auxadc_compatible { + bool sample_data_cali; + bool check_global_idle; +}; + +struct mt6577_auxadc_device { + void __iomem *reg_base; + struct clk *adc_clk; + struct mutex lock; + const struct mtk_auxadc_compatible *dev_comp; +}; + +static const struct mtk_auxadc_compatible mt8186_compat = { + .sample_data_cali = false, + .check_global_idle = false, +}; + +static const struct mtk_auxadc_compatible mt8173_compat = { + .sample_data_cali = false, + .check_global_idle = true, +}; + +static const struct mtk_auxadc_compatible mt6765_compat = { + .sample_data_cali = true, + .check_global_idle = false, +}; + +#define MT6577_AUXADC_CHANNEL(idx) { \ + .type = IIO_VOLTAGE, \ + .indexed = 1, \ + .channel = (idx), \ + .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED), \ +} + +static const struct iio_chan_spec mt6577_auxadc_iio_channels[] = { + MT6577_AUXADC_CHANNEL(0), + MT6577_AUXADC_CHANNEL(1), + MT6577_AUXADC_CHANNEL(2), + MT6577_AUXADC_CHANNEL(3), + MT6577_AUXADC_CHANNEL(4), + MT6577_AUXADC_CHANNEL(5), + MT6577_AUXADC_CHANNEL(6), + MT6577_AUXADC_CHANNEL(7), + MT6577_AUXADC_CHANNEL(8), + MT6577_AUXADC_CHANNEL(9), + MT6577_AUXADC_CHANNEL(10), + MT6577_AUXADC_CHANNEL(11), + MT6577_AUXADC_CHANNEL(12), + MT6577_AUXADC_CHANNEL(13), + MT6577_AUXADC_CHANNEL(14), + MT6577_AUXADC_CHANNEL(15), +}; + +/* For Voltage calculation */ +#define VOLTAGE_FULL_RANGE 1500 /* VA voltage */ +#define AUXADC_PRECISE 4096 /* 12 bits */ + +static int mt_auxadc_get_cali_data(int rawdata, bool enable_cali) +{ + return rawdata; +} + +static inline void mt6577_auxadc_mod_reg(void __iomem *reg, + u32 or_mask, u32 and_mask) +{ + u32 val; + + val = readl(reg); + val |= or_mask; + val &= ~and_mask; + writel(val, reg); +} + +static int mt6577_auxadc_read(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan) +{ + u32 val; + void __iomem *reg_channel; + int ret; + struct mt6577_auxadc_device *adc_dev = iio_priv(indio_dev); + + reg_channel = adc_dev->reg_base + MT6577_AUXADC_DAT0 + + chan->channel * 0x04; + + mutex_lock(&adc_dev->lock); + + mt6577_auxadc_mod_reg(adc_dev->reg_base + MT6577_AUXADC_CON1, + 0, 1 << chan->channel); + + /* read channel and make sure old ready bit == 0 */ + ret = readl_poll_timeout(reg_channel, val, + ((val & MT6577_AUXADC_RDY0) == 0), + MT6577_AUXADC_SLEEP_US, + MT6577_AUXADC_TIMEOUT_US); + if (ret < 0) { + dev_err(indio_dev->dev.parent, + "wait for channel[%d] ready bit clear time out\n", + chan->channel); + goto err_timeout; + } + + /* set bit to trigger sample */ + mt6577_auxadc_mod_reg(adc_dev->reg_base + MT6577_AUXADC_CON1, + 1 << chan->channel, 0); + + /* we must delay here for hardware sample channel data */ + udelay(MT6577_AUXADC_SAMPLE_READY_US); + + if (adc_dev->dev_comp->check_global_idle) { + /* check MTK_AUXADC_CON2 if auxadc is idle */ + ret = readl_poll_timeout(adc_dev->reg_base + MT6577_AUXADC_CON2, + val, ((val & MT6577_AUXADC_STA) == 0), + MT6577_AUXADC_SLEEP_US, + MT6577_AUXADC_TIMEOUT_US); + if (ret < 0) { + dev_err(indio_dev->dev.parent, + "wait for auxadc idle time out\n"); + goto err_timeout; + } + } + + /* read channel and make sure ready bit == 1 */ + ret = readl_poll_timeout(reg_channel, val, + ((val & MT6577_AUXADC_RDY0) != 0), + MT6577_AUXADC_SLEEP_US, + MT6577_AUXADC_TIMEOUT_US); + if (ret < 0) { + dev_err(indio_dev->dev.parent, + "wait for channel[%d] data ready time out\n", + chan->channel); + goto err_timeout; + } + + /* read data */ + val = readl(reg_channel) & MT6577_AUXADC_DAT_MASK; + + mutex_unlock(&adc_dev->lock); + + return val; + +err_timeout: + + mutex_unlock(&adc_dev->lock); + + return -ETIMEDOUT; +} + +static int mt6577_auxadc_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, + int *val2, + long info) +{ + struct mt6577_auxadc_device *adc_dev = iio_priv(indio_dev); + + switch (info) { + case IIO_CHAN_INFO_PROCESSED: + *val = mt6577_auxadc_read(indio_dev, chan); + if (*val < 0) { + dev_err(indio_dev->dev.parent, + "failed to sample data on channel[%d]\n", + chan->channel); + return *val; + } + if (adc_dev->dev_comp->sample_data_cali) + *val = mt_auxadc_get_cali_data(*val, true); + + /* Convert adc raw data to voltage: 0 - 1500 mV */ + *val = *val * VOLTAGE_FULL_RANGE / AUXADC_PRECISE; + + return IIO_VAL_INT; + + default: + return -EINVAL; + } +} + +static const struct iio_info mt6577_auxadc_info = { + .read_raw = &mt6577_auxadc_read_raw, +}; + +static int mt6577_auxadc_resume(struct device *dev) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct mt6577_auxadc_device *adc_dev = iio_priv(indio_dev); + int ret; + + ret = clk_prepare_enable(adc_dev->adc_clk); + if (ret) { + pr_err("failed to enable auxadc clock\n"); + return ret; + } + + mt6577_auxadc_mod_reg(adc_dev->reg_base + MT6577_AUXADC_MISC, + MT6577_AUXADC_PDN_EN, 0); + mdelay(MT6577_AUXADC_POWER_READY_MS); + + return 0; +} + +static int mt6577_auxadc_suspend(struct device *dev) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct mt6577_auxadc_device *adc_dev = iio_priv(indio_dev); + + mt6577_auxadc_mod_reg(adc_dev->reg_base + MT6577_AUXADC_MISC, + 0, MT6577_AUXADC_PDN_EN); + clk_disable_unprepare(adc_dev->adc_clk); + + return 0; +} + +static void mt6577_power_off(void *data) +{ + struct mt6577_auxadc_device *adc_dev = data; + + mt6577_auxadc_mod_reg(adc_dev->reg_base + MT6577_AUXADC_MISC, + 0, MT6577_AUXADC_PDN_EN); +} + +static int mt6577_auxadc_probe(struct platform_device *pdev) +{ + struct mt6577_auxadc_device *adc_dev; + unsigned long adc_clk_rate; + struct iio_dev *indio_dev; + int ret; + + indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*adc_dev)); + if (!indio_dev) + return -ENOMEM; + + adc_dev = iio_priv(indio_dev); + indio_dev->name = dev_name(&pdev->dev); + indio_dev->info = &mt6577_auxadc_info; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = mt6577_auxadc_iio_channels; + indio_dev->num_channels = ARRAY_SIZE(mt6577_auxadc_iio_channels); + + adc_dev->reg_base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(adc_dev->reg_base)) + return dev_err_probe(&pdev->dev, PTR_ERR(adc_dev->reg_base), + "failed to get auxadc base address\n"); + + adc_dev->adc_clk = devm_clk_get_enabled(&pdev->dev, "main"); + if (IS_ERR(adc_dev->adc_clk)) + return dev_err_probe(&pdev->dev, PTR_ERR(adc_dev->adc_clk), + "failed to enable auxadc clock\n"); + + adc_clk_rate = clk_get_rate(adc_dev->adc_clk); + if (!adc_clk_rate) + return dev_err_probe(&pdev->dev, -EINVAL, "null clock rate\n"); + + adc_dev->dev_comp = device_get_match_data(&pdev->dev); + + mutex_init(&adc_dev->lock); + + mt6577_auxadc_mod_reg(adc_dev->reg_base + MT6577_AUXADC_MISC, + MT6577_AUXADC_PDN_EN, 0); + mdelay(MT6577_AUXADC_POWER_READY_MS); + platform_set_drvdata(pdev, indio_dev); + + ret = devm_add_action_or_reset(&pdev->dev, mt6577_power_off, adc_dev); + if (ret) + return ret; + + ret = devm_iio_device_register(&pdev->dev, indio_dev); + if (ret < 0) + return dev_err_probe(&pdev->dev, ret, "failed to register iio device\n"); + + return 0; +} + +static DEFINE_SIMPLE_DEV_PM_OPS(mt6577_auxadc_pm_ops, + mt6577_auxadc_suspend, + mt6577_auxadc_resume); + +static const struct of_device_id mt6577_auxadc_of_match[] = { + { .compatible = "mediatek,mt2701-auxadc", .data = &mt8173_compat }, + { .compatible = "mediatek,mt2712-auxadc", .data = &mt8173_compat }, + { .compatible = "mediatek,mt7622-auxadc", .data = &mt8173_compat }, + { .compatible = "mediatek,mt8173-auxadc", .data = &mt8173_compat }, + { .compatible = "mediatek,mt8186-auxadc", .data = &mt8186_compat }, + { .compatible = "mediatek,mt6765-auxadc", .data = &mt6765_compat }, + { } +}; +MODULE_DEVICE_TABLE(of, mt6577_auxadc_of_match); + +static struct platform_driver mt6577_auxadc_driver = { + .driver = { + .name = "mt6577-auxadc", + .of_match_table = mt6577_auxadc_of_match, + .pm = pm_sleep_ptr(&mt6577_auxadc_pm_ops), + }, + .probe = mt6577_auxadc_probe, +}; +module_platform_driver(mt6577_auxadc_driver); + +MODULE_AUTHOR("Zhiyong Tao "); +MODULE_DESCRIPTION("MTK AUXADC Device Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/adc/mxs-lradc-adc.c b/drivers/iio/adc/mxs-lradc-adc.c new file mode 100644 index 000000000..dda5182a5 --- /dev/null +++ b/drivers/iio/adc/mxs-lradc-adc.c @@ -0,0 +1,829 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Freescale MXS LRADC ADC driver + * + * Copyright (c) 2012 DENX Software Engineering, GmbH. + * Copyright (c) 2017 Ksenija Stanojevic + * + * Authors: + * Marek Vasut + * Ksenija Stanojevic + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +/* + * Make this runtime configurable if necessary. Currently, if the buffered mode + * is enabled, the LRADC takes LRADC_DELAY_TIMER_LOOP samples of data before + * triggering IRQ. The sampling happens every (LRADC_DELAY_TIMER_PER / 2000) + * seconds. The result is that the samples arrive every 500mS. + */ +#define LRADC_DELAY_TIMER_PER 200 +#define LRADC_DELAY_TIMER_LOOP 5 + +#define VREF_MV_BASE 1850 + +static const char *mx23_lradc_adc_irq_names[] = { + "mxs-lradc-channel0", + "mxs-lradc-channel1", + "mxs-lradc-channel2", + "mxs-lradc-channel3", + "mxs-lradc-channel4", + "mxs-lradc-channel5", +}; + +static const char *mx28_lradc_adc_irq_names[] = { + "mxs-lradc-thresh0", + "mxs-lradc-thresh1", + "mxs-lradc-channel0", + "mxs-lradc-channel1", + "mxs-lradc-channel2", + "mxs-lradc-channel3", + "mxs-lradc-channel4", + "mxs-lradc-channel5", + "mxs-lradc-button0", + "mxs-lradc-button1", +}; + +static const u32 mxs_lradc_adc_vref_mv[][LRADC_MAX_TOTAL_CHANS] = { + [IMX23_LRADC] = { + VREF_MV_BASE, /* CH0 */ + VREF_MV_BASE, /* CH1 */ + VREF_MV_BASE, /* CH2 */ + VREF_MV_BASE, /* CH3 */ + VREF_MV_BASE, /* CH4 */ + VREF_MV_BASE, /* CH5 */ + VREF_MV_BASE * 2, /* CH6 VDDIO */ + VREF_MV_BASE * 4, /* CH7 VBATT */ + VREF_MV_BASE, /* CH8 Temp sense 0 */ + VREF_MV_BASE, /* CH9 Temp sense 1 */ + VREF_MV_BASE, /* CH10 */ + VREF_MV_BASE, /* CH11 */ + VREF_MV_BASE, /* CH12 USB_DP */ + VREF_MV_BASE, /* CH13 USB_DN */ + VREF_MV_BASE, /* CH14 VBG */ + VREF_MV_BASE * 4, /* CH15 VDD5V */ + }, + [IMX28_LRADC] = { + VREF_MV_BASE, /* CH0 */ + VREF_MV_BASE, /* CH1 */ + VREF_MV_BASE, /* CH2 */ + VREF_MV_BASE, /* CH3 */ + VREF_MV_BASE, /* CH4 */ + VREF_MV_BASE, /* CH5 */ + VREF_MV_BASE, /* CH6 */ + VREF_MV_BASE * 4, /* CH7 VBATT */ + VREF_MV_BASE, /* CH8 Temp sense 0 */ + VREF_MV_BASE, /* CH9 Temp sense 1 */ + VREF_MV_BASE * 2, /* CH10 VDDIO */ + VREF_MV_BASE, /* CH11 VTH */ + VREF_MV_BASE * 2, /* CH12 VDDA */ + VREF_MV_BASE, /* CH13 VDDD */ + VREF_MV_BASE, /* CH14 VBG */ + VREF_MV_BASE * 4, /* CH15 VDD5V */ + }, +}; + +enum mxs_lradc_divbytwo { + MXS_LRADC_DIV_DISABLED = 0, + MXS_LRADC_DIV_ENABLED, +}; + +struct mxs_lradc_scale { + unsigned int integer; + unsigned int nano; +}; + +struct mxs_lradc_adc { + struct mxs_lradc *lradc; + struct device *dev; + + void __iomem *base; + /* Maximum of 8 channels + 8 byte ts */ + u32 buffer[10] __aligned(8); + struct iio_trigger *trig; + struct completion completion; + spinlock_t lock; + + const u32 *vref_mv; + struct mxs_lradc_scale scale_avail[LRADC_MAX_TOTAL_CHANS][2]; + unsigned long is_divided; +}; + + +/* Raw I/O operations */ +static int mxs_lradc_adc_read_single(struct iio_dev *iio_dev, int chan, + int *val) +{ + struct mxs_lradc_adc *adc = iio_priv(iio_dev); + struct mxs_lradc *lradc = adc->lradc; + int ret; + + /* + * See if there is no buffered operation in progress. If there is simply + * bail out. This can be improved to support both buffered and raw IO at + * the same time, yet the code becomes horribly complicated. Therefore I + * applied KISS principle here. + */ + if (!iio_device_claim_direct(iio_dev)) + return -EBUSY; + + reinit_completion(&adc->completion); + + /* + * No buffered operation in progress, map the channel and trigger it. + * Virtual channel 0 is always used here as the others are always not + * used if doing raw sampling. + */ + if (lradc->soc == IMX28_LRADC) + writel(LRADC_CTRL1_LRADC_IRQ_EN(0), + adc->base + LRADC_CTRL1 + STMP_OFFSET_REG_CLR); + writel(0x1, adc->base + LRADC_CTRL0 + STMP_OFFSET_REG_CLR); + + /* Enable / disable the divider per requirement */ + if (test_bit(chan, &adc->is_divided)) + writel(1 << LRADC_CTRL2_DIVIDE_BY_TWO_OFFSET, + adc->base + LRADC_CTRL2 + STMP_OFFSET_REG_SET); + else + writel(1 << LRADC_CTRL2_DIVIDE_BY_TWO_OFFSET, + adc->base + LRADC_CTRL2 + STMP_OFFSET_REG_CLR); + + /* Clean the slot's previous content, then set new one. */ + writel(LRADC_CTRL4_LRADCSELECT_MASK(0), + adc->base + LRADC_CTRL4 + STMP_OFFSET_REG_CLR); + writel(chan, adc->base + LRADC_CTRL4 + STMP_OFFSET_REG_SET); + + writel(0, adc->base + LRADC_CH(0)); + + /* Enable the IRQ and start sampling the channel. */ + writel(LRADC_CTRL1_LRADC_IRQ_EN(0), + adc->base + LRADC_CTRL1 + STMP_OFFSET_REG_SET); + writel(BIT(0), adc->base + LRADC_CTRL0 + STMP_OFFSET_REG_SET); + + /* Wait for completion on the channel, 1 second max. */ + ret = wait_for_completion_killable_timeout(&adc->completion, HZ); + if (!ret) + ret = -ETIMEDOUT; + if (ret < 0) + goto err; + + /* Read the data. */ + *val = readl(adc->base + LRADC_CH(0)) & LRADC_CH_VALUE_MASK; + ret = IIO_VAL_INT; + +err: + writel(LRADC_CTRL1_LRADC_IRQ_EN(0), + adc->base + LRADC_CTRL1 + STMP_OFFSET_REG_CLR); + + iio_device_release_direct(iio_dev); + + return ret; +} + +static int mxs_lradc_adc_read_temp(struct iio_dev *iio_dev, int *val) +{ + int ret, min, max; + + ret = mxs_lradc_adc_read_single(iio_dev, 8, &min); + if (ret != IIO_VAL_INT) + return ret; + + ret = mxs_lradc_adc_read_single(iio_dev, 9, &max); + if (ret != IIO_VAL_INT) + return ret; + + *val = max - min; + + return IIO_VAL_INT; +} + +static int mxs_lradc_adc_read_raw(struct iio_dev *iio_dev, + const struct iio_chan_spec *chan, + int *val, int *val2, long m) +{ + struct mxs_lradc_adc *adc = iio_priv(iio_dev); + + switch (m) { + case IIO_CHAN_INFO_RAW: + if (chan->type == IIO_TEMP) + return mxs_lradc_adc_read_temp(iio_dev, val); + + return mxs_lradc_adc_read_single(iio_dev, chan->channel, val); + + case IIO_CHAN_INFO_SCALE: + if (chan->type == IIO_TEMP) { + /* + * From the datasheet, we have to multiply by 1.012 and + * divide by 4 + */ + *val = 0; + *val2 = 253000; + return IIO_VAL_INT_PLUS_MICRO; + } + + *val = adc->vref_mv[chan->channel]; + *val2 = chan->scan_type.realbits - + test_bit(chan->channel, &adc->is_divided); + return IIO_VAL_FRACTIONAL_LOG2; + + case IIO_CHAN_INFO_OFFSET: + if (chan->type == IIO_TEMP) { + /* + * The calculated value from the ADC is in Kelvin, we + * want Celsius for hwmon so the offset is -273.15 + * The offset is applied before scaling so it is + * actually -213.15 * 4 / 1.012 = -1079.644268 + */ + *val = -1079; + *val2 = 644268; + + return IIO_VAL_INT_PLUS_MICRO; + } + + return -EINVAL; + + default: + break; + } + + return -EINVAL; +} + +static int mxs_lradc_adc_write_raw(struct iio_dev *iio_dev, + const struct iio_chan_spec *chan, + int val, int val2, long m) +{ + struct mxs_lradc_adc *adc = iio_priv(iio_dev); + struct mxs_lradc_scale *scale_avail = + adc->scale_avail[chan->channel]; + int ret; + + if (!iio_device_claim_direct(iio_dev)) + return -EBUSY; + + switch (m) { + case IIO_CHAN_INFO_SCALE: + ret = -EINVAL; + if (val == scale_avail[MXS_LRADC_DIV_DISABLED].integer && + val2 == scale_avail[MXS_LRADC_DIV_DISABLED].nano) { + /* divider by two disabled */ + clear_bit(chan->channel, &adc->is_divided); + ret = 0; + } else if (val == scale_avail[MXS_LRADC_DIV_ENABLED].integer && + val2 == scale_avail[MXS_LRADC_DIV_ENABLED].nano) { + /* divider by two enabled */ + set_bit(chan->channel, &adc->is_divided); + ret = 0; + } + + break; + default: + ret = -EINVAL; + break; + } + + iio_device_release_direct(iio_dev); + + return ret; +} + +static int mxs_lradc_adc_write_raw_get_fmt(struct iio_dev *iio_dev, + const struct iio_chan_spec *chan, + long m) +{ + return IIO_VAL_INT_PLUS_NANO; +} + +static ssize_t mxs_lradc_adc_show_scale_avail(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct iio_dev *iio = dev_to_iio_dev(dev); + struct mxs_lradc_adc *adc = iio_priv(iio); + struct iio_dev_attr *iio_attr = to_iio_dev_attr(attr); + int i, ch, len = 0; + + ch = iio_attr->address; + for (i = 0; i < ARRAY_SIZE(adc->scale_avail[ch]); i++) + len += sprintf(buf + len, "%u.%09u ", + adc->scale_avail[ch][i].integer, + adc->scale_avail[ch][i].nano); + + len += sprintf(buf + len, "\n"); + + return len; +} + +#define SHOW_SCALE_AVAILABLE_ATTR(ch)\ + IIO_DEVICE_ATTR(in_voltage##ch##_scale_available, 0444,\ + mxs_lradc_adc_show_scale_avail, NULL, ch) + +static SHOW_SCALE_AVAILABLE_ATTR(0); +static SHOW_SCALE_AVAILABLE_ATTR(1); +static SHOW_SCALE_AVAILABLE_ATTR(2); +static SHOW_SCALE_AVAILABLE_ATTR(3); +static SHOW_SCALE_AVAILABLE_ATTR(4); +static SHOW_SCALE_AVAILABLE_ATTR(5); +static SHOW_SCALE_AVAILABLE_ATTR(6); +static SHOW_SCALE_AVAILABLE_ATTR(7); +static SHOW_SCALE_AVAILABLE_ATTR(10); +static SHOW_SCALE_AVAILABLE_ATTR(11); +static SHOW_SCALE_AVAILABLE_ATTR(12); +static SHOW_SCALE_AVAILABLE_ATTR(13); +static SHOW_SCALE_AVAILABLE_ATTR(14); +static SHOW_SCALE_AVAILABLE_ATTR(15); + +static struct attribute *mxs_lradc_adc_attributes[] = { + &iio_dev_attr_in_voltage0_scale_available.dev_attr.attr, + &iio_dev_attr_in_voltage1_scale_available.dev_attr.attr, + &iio_dev_attr_in_voltage2_scale_available.dev_attr.attr, + &iio_dev_attr_in_voltage3_scale_available.dev_attr.attr, + &iio_dev_attr_in_voltage4_scale_available.dev_attr.attr, + &iio_dev_attr_in_voltage5_scale_available.dev_attr.attr, + &iio_dev_attr_in_voltage6_scale_available.dev_attr.attr, + &iio_dev_attr_in_voltage7_scale_available.dev_attr.attr, + &iio_dev_attr_in_voltage10_scale_available.dev_attr.attr, + &iio_dev_attr_in_voltage11_scale_available.dev_attr.attr, + &iio_dev_attr_in_voltage12_scale_available.dev_attr.attr, + &iio_dev_attr_in_voltage13_scale_available.dev_attr.attr, + &iio_dev_attr_in_voltage14_scale_available.dev_attr.attr, + &iio_dev_attr_in_voltage15_scale_available.dev_attr.attr, + NULL +}; + +static const struct attribute_group mxs_lradc_adc_attribute_group = { + .attrs = mxs_lradc_adc_attributes, +}; + +static const struct iio_info mxs_lradc_adc_iio_info = { + .read_raw = mxs_lradc_adc_read_raw, + .write_raw = mxs_lradc_adc_write_raw, + .write_raw_get_fmt = mxs_lradc_adc_write_raw_get_fmt, + .attrs = &mxs_lradc_adc_attribute_group, +}; + +/* IRQ Handling */ +static irqreturn_t mxs_lradc_adc_handle_irq(int irq, void *data) +{ + struct iio_dev *iio = data; + struct mxs_lradc_adc *adc = iio_priv(iio); + struct mxs_lradc *lradc = adc->lradc; + unsigned long reg = readl(adc->base + LRADC_CTRL1); + unsigned long flags; + + if (!(reg & mxs_lradc_irq_mask(lradc))) + return IRQ_NONE; + + if (iio_buffer_enabled(iio)) { + if (reg & lradc->buffer_vchans) { + spin_lock_irqsave(&adc->lock, flags); + iio_trigger_poll(iio->trig); + spin_unlock_irqrestore(&adc->lock, flags); + } + } else if (reg & LRADC_CTRL1_LRADC_IRQ(0)) { + complete(&adc->completion); + } + + writel(reg & mxs_lradc_irq_mask(lradc), + adc->base + LRADC_CTRL1 + STMP_OFFSET_REG_CLR); + + return IRQ_HANDLED; +} + + +/* Trigger handling */ +static irqreturn_t mxs_lradc_adc_trigger_handler(int irq, void *p) +{ + struct iio_poll_func *pf = p; + struct iio_dev *iio = pf->indio_dev; + struct mxs_lradc_adc *adc = iio_priv(iio); + const u32 chan_value = LRADC_CH_ACCUMULATE | + ((LRADC_DELAY_TIMER_LOOP - 1) << LRADC_CH_NUM_SAMPLES_OFFSET); + unsigned int i, j = 0; + + for_each_set_bit(i, iio->active_scan_mask, LRADC_MAX_TOTAL_CHANS) { + adc->buffer[j] = readl(adc->base + LRADC_CH(j)); + writel(chan_value, adc->base + LRADC_CH(j)); + adc->buffer[j] &= LRADC_CH_VALUE_MASK; + adc->buffer[j] /= LRADC_DELAY_TIMER_LOOP; + j++; + } + + iio_push_to_buffers_with_ts(iio, adc->buffer, sizeof(adc->buffer), + pf->timestamp); + + iio_trigger_notify_done(iio->trig); + + return IRQ_HANDLED; +} + +static int mxs_lradc_adc_configure_trigger(struct iio_trigger *trig, bool state) +{ + struct iio_dev *iio = iio_trigger_get_drvdata(trig); + struct mxs_lradc_adc *adc = iio_priv(iio); + const u32 st = state ? STMP_OFFSET_REG_SET : STMP_OFFSET_REG_CLR; + + writel(LRADC_DELAY_KICK, adc->base + (LRADC_DELAY(0) + st)); + + return 0; +} + +static const struct iio_trigger_ops mxs_lradc_adc_trigger_ops = { + .set_trigger_state = &mxs_lradc_adc_configure_trigger, +}; + +static int mxs_lradc_adc_trigger_init(struct iio_dev *iio) +{ + int ret; + struct iio_trigger *trig; + struct mxs_lradc_adc *adc = iio_priv(iio); + + trig = devm_iio_trigger_alloc(&iio->dev, "%s-dev%i", iio->name, + iio_device_id(iio)); + if (!trig) + return -ENOMEM; + + trig->dev.parent = adc->dev; + iio_trigger_set_drvdata(trig, iio); + trig->ops = &mxs_lradc_adc_trigger_ops; + + ret = iio_trigger_register(trig); + if (ret) + return ret; + + adc->trig = trig; + + return 0; +} + +static void mxs_lradc_adc_trigger_remove(struct iio_dev *iio) +{ + struct mxs_lradc_adc *adc = iio_priv(iio); + + iio_trigger_unregister(adc->trig); +} + +static int mxs_lradc_adc_buffer_preenable(struct iio_dev *iio) +{ + struct mxs_lradc_adc *adc = iio_priv(iio); + struct mxs_lradc *lradc = adc->lradc; + int chan, ofs = 0; + unsigned long enable = 0; + u32 ctrl4_set = 0; + u32 ctrl4_clr = 0; + u32 ctrl1_irq = 0; + const u32 chan_value = LRADC_CH_ACCUMULATE | + ((LRADC_DELAY_TIMER_LOOP - 1) << LRADC_CH_NUM_SAMPLES_OFFSET); + + if (lradc->soc == IMX28_LRADC) + writel(lradc->buffer_vchans << LRADC_CTRL1_LRADC_IRQ_EN_OFFSET, + adc->base + LRADC_CTRL1 + STMP_OFFSET_REG_CLR); + writel(lradc->buffer_vchans, + adc->base + LRADC_CTRL0 + STMP_OFFSET_REG_CLR); + + for_each_set_bit(chan, iio->active_scan_mask, LRADC_MAX_TOTAL_CHANS) { + ctrl4_set |= chan << LRADC_CTRL4_LRADCSELECT_OFFSET(ofs); + ctrl4_clr |= LRADC_CTRL4_LRADCSELECT_MASK(ofs); + ctrl1_irq |= LRADC_CTRL1_LRADC_IRQ_EN(ofs); + writel(chan_value, adc->base + LRADC_CH(ofs)); + bitmap_set(&enable, ofs, 1); + ofs++; + } + + writel(LRADC_DELAY_TRIGGER_LRADCS_MASK | LRADC_DELAY_KICK, + adc->base + LRADC_DELAY(0) + STMP_OFFSET_REG_CLR); + writel(ctrl4_clr, adc->base + LRADC_CTRL4 + STMP_OFFSET_REG_CLR); + writel(ctrl4_set, adc->base + LRADC_CTRL4 + STMP_OFFSET_REG_SET); + writel(ctrl1_irq, adc->base + LRADC_CTRL1 + STMP_OFFSET_REG_SET); + writel(enable << LRADC_DELAY_TRIGGER_LRADCS_OFFSET, + adc->base + LRADC_DELAY(0) + STMP_OFFSET_REG_SET); + + return 0; +} + +static int mxs_lradc_adc_buffer_postdisable(struct iio_dev *iio) +{ + struct mxs_lradc_adc *adc = iio_priv(iio); + struct mxs_lradc *lradc = adc->lradc; + + writel(LRADC_DELAY_TRIGGER_LRADCS_MASK | LRADC_DELAY_KICK, + adc->base + LRADC_DELAY(0) + STMP_OFFSET_REG_CLR); + + writel(lradc->buffer_vchans, + adc->base + LRADC_CTRL0 + STMP_OFFSET_REG_CLR); + if (lradc->soc == IMX28_LRADC) + writel(lradc->buffer_vchans << LRADC_CTRL1_LRADC_IRQ_EN_OFFSET, + adc->base + LRADC_CTRL1 + STMP_OFFSET_REG_CLR); + + return 0; +} + +static bool mxs_lradc_adc_validate_scan_mask(struct iio_dev *iio, + const unsigned long *mask) +{ + struct mxs_lradc_adc *adc = iio_priv(iio); + struct mxs_lradc *lradc = adc->lradc; + const int map_chans = bitmap_weight(mask, LRADC_MAX_TOTAL_CHANS); + int rsvd_chans = 0; + unsigned long rsvd_mask = 0; + + if (lradc->use_touchbutton) + rsvd_mask |= CHAN_MASK_TOUCHBUTTON; + if (lradc->touchscreen_wire == MXS_LRADC_TOUCHSCREEN_4WIRE) + rsvd_mask |= CHAN_MASK_TOUCHSCREEN_4WIRE; + if (lradc->touchscreen_wire == MXS_LRADC_TOUCHSCREEN_5WIRE) + rsvd_mask |= CHAN_MASK_TOUCHSCREEN_5WIRE; + + if (lradc->use_touchbutton) + rsvd_chans++; + if (lradc->touchscreen_wire) + rsvd_chans += 2; + + /* Test for attempts to map channels with special mode of operation. */ + if (bitmap_intersects(mask, &rsvd_mask, LRADC_MAX_TOTAL_CHANS)) + return false; + + /* Test for attempts to map more channels then available slots. */ + if (map_chans + rsvd_chans > LRADC_MAX_MAPPED_CHANS) + return false; + + return true; +} + +static const struct iio_buffer_setup_ops mxs_lradc_adc_buffer_ops = { + .preenable = &mxs_lradc_adc_buffer_preenable, + .postdisable = &mxs_lradc_adc_buffer_postdisable, + .validate_scan_mask = &mxs_lradc_adc_validate_scan_mask, +}; + +/* Driver initialization */ +#define MXS_ADC_CHAN(idx, chan_type, name) { \ + .type = (chan_type), \ + .indexed = 1, \ + .scan_index = (idx), \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ + BIT(IIO_CHAN_INFO_SCALE), \ + .channel = (idx), \ + .address = (idx), \ + .scan_type = { \ + .sign = 'u', \ + .realbits = LRADC_RESOLUTION, \ + .storagebits = 32, \ + }, \ + .datasheet_name = (name), \ +} + +static const struct iio_chan_spec mx23_lradc_chan_spec[] = { + MXS_ADC_CHAN(0, IIO_VOLTAGE, "LRADC0"), + MXS_ADC_CHAN(1, IIO_VOLTAGE, "LRADC1"), + MXS_ADC_CHAN(2, IIO_VOLTAGE, "LRADC2"), + MXS_ADC_CHAN(3, IIO_VOLTAGE, "LRADC3"), + MXS_ADC_CHAN(4, IIO_VOLTAGE, "LRADC4"), + MXS_ADC_CHAN(5, IIO_VOLTAGE, "LRADC5"), + MXS_ADC_CHAN(6, IIO_VOLTAGE, "VDDIO"), + MXS_ADC_CHAN(7, IIO_VOLTAGE, "VBATT"), + /* Combined Temperature sensors */ + { + .type = IIO_TEMP, + .indexed = 1, + .scan_index = 8, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_OFFSET) | + BIT(IIO_CHAN_INFO_SCALE), + .channel = 8, + .scan_type = {.sign = 'u', .realbits = 18, .storagebits = 32,}, + .datasheet_name = "TEMP_DIE", + }, + /* Hidden channel to keep indexes */ + { + .type = IIO_TEMP, + .indexed = 1, + .scan_index = -1, + .channel = 9, + }, + MXS_ADC_CHAN(10, IIO_VOLTAGE, NULL), + MXS_ADC_CHAN(11, IIO_VOLTAGE, NULL), + MXS_ADC_CHAN(12, IIO_VOLTAGE, "USB_DP"), + MXS_ADC_CHAN(13, IIO_VOLTAGE, "USB_DN"), + MXS_ADC_CHAN(14, IIO_VOLTAGE, "VBG"), + MXS_ADC_CHAN(15, IIO_VOLTAGE, "VDD5V"), +}; + +static const struct iio_chan_spec mx28_lradc_chan_spec[] = { + MXS_ADC_CHAN(0, IIO_VOLTAGE, "LRADC0"), + MXS_ADC_CHAN(1, IIO_VOLTAGE, "LRADC1"), + MXS_ADC_CHAN(2, IIO_VOLTAGE, "LRADC2"), + MXS_ADC_CHAN(3, IIO_VOLTAGE, "LRADC3"), + MXS_ADC_CHAN(4, IIO_VOLTAGE, "LRADC4"), + MXS_ADC_CHAN(5, IIO_VOLTAGE, "LRADC5"), + MXS_ADC_CHAN(6, IIO_VOLTAGE, "LRADC6"), + MXS_ADC_CHAN(7, IIO_VOLTAGE, "VBATT"), + /* Combined Temperature sensors */ + { + .type = IIO_TEMP, + .indexed = 1, + .scan_index = 8, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_OFFSET) | + BIT(IIO_CHAN_INFO_SCALE), + .channel = 8, + .scan_type = {.sign = 'u', .realbits = 18, .storagebits = 32,}, + .datasheet_name = "TEMP_DIE", + }, + /* Hidden channel to keep indexes */ + { + .type = IIO_TEMP, + .indexed = 1, + .scan_index = -1, + .channel = 9, + }, + MXS_ADC_CHAN(10, IIO_VOLTAGE, "VDDIO"), + MXS_ADC_CHAN(11, IIO_VOLTAGE, "VTH"), + MXS_ADC_CHAN(12, IIO_VOLTAGE, "VDDA"), + MXS_ADC_CHAN(13, IIO_VOLTAGE, "VDDD"), + MXS_ADC_CHAN(14, IIO_VOLTAGE, "VBG"), + MXS_ADC_CHAN(15, IIO_VOLTAGE, "VDD5V"), +}; + +static void mxs_lradc_adc_hw_init(struct mxs_lradc_adc *adc) +{ + /* The ADC always uses DELAY CHANNEL 0. */ + const u32 adc_cfg = + (1 << (LRADC_DELAY_TRIGGER_DELAYS_OFFSET + 0)) | + (LRADC_DELAY_TIMER_PER << LRADC_DELAY_DELAY_OFFSET); + + /* Configure DELAY CHANNEL 0 for generic ADC sampling. */ + writel(adc_cfg, adc->base + LRADC_DELAY(0)); + + /* + * Start internal temperature sensing by clearing bit + * HW_LRADC_CTRL2_TEMPSENSE_PWD. This bit can be left cleared + * after power up. + */ + writel(0, adc->base + LRADC_CTRL2); +} + +static void mxs_lradc_adc_hw_stop(struct mxs_lradc_adc *adc) +{ + writel(0, adc->base + LRADC_DELAY(0)); +} + +static int mxs_lradc_adc_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct mxs_lradc *lradc = dev_get_drvdata(dev->parent); + struct mxs_lradc_adc *adc; + struct iio_dev *iio; + struct resource *iores; + int ret, irq, virq, i, s, n; + u64 scale_uv; + const char **irq_name; + + /* Allocate the IIO device. */ + iio = devm_iio_device_alloc(dev, sizeof(*adc)); + if (!iio) + return -ENOMEM; + + adc = iio_priv(iio); + adc->lradc = lradc; + adc->dev = dev; + + iores = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!iores) + return -EINVAL; + + adc->base = devm_ioremap(dev, iores->start, resource_size(iores)); + if (!adc->base) + return -ENOMEM; + + init_completion(&adc->completion); + spin_lock_init(&adc->lock); + + platform_set_drvdata(pdev, iio); + + iio->name = pdev->name; + iio->dev.of_node = dev->parent->of_node; + iio->info = &mxs_lradc_adc_iio_info; + iio->modes = INDIO_DIRECT_MODE; + + if (lradc->soc == IMX23_LRADC) { + iio->channels = mx23_lradc_chan_spec; + iio->num_channels = ARRAY_SIZE(mx23_lradc_chan_spec); + irq_name = mx23_lradc_adc_irq_names; + n = ARRAY_SIZE(mx23_lradc_adc_irq_names); + } else { + iio->channels = mx28_lradc_chan_spec; + iio->num_channels = ARRAY_SIZE(mx28_lradc_chan_spec); + irq_name = mx28_lradc_adc_irq_names; + n = ARRAY_SIZE(mx28_lradc_adc_irq_names); + } + + ret = stmp_reset_block(adc->base); + if (ret) + return ret; + + for (i = 0; i < n; i++) { + irq = platform_get_irq_byname(pdev, irq_name[i]); + if (irq < 0) + return irq; + + virq = irq_of_parse_and_map(dev->parent->of_node, irq); + + ret = devm_request_irq(dev, virq, mxs_lradc_adc_handle_irq, + 0, irq_name[i], iio); + if (ret) + return ret; + } + + ret = mxs_lradc_adc_trigger_init(iio); + if (ret) + return ret; + + ret = iio_triggered_buffer_setup(iio, &iio_pollfunc_store_time, + &mxs_lradc_adc_trigger_handler, + &mxs_lradc_adc_buffer_ops); + if (ret) + goto err_trig; + + adc->vref_mv = mxs_lradc_adc_vref_mv[lradc->soc]; + + /* Populate available ADC input ranges */ + for (i = 0; i < LRADC_MAX_TOTAL_CHANS; i++) { + for (s = 0; s < ARRAY_SIZE(adc->scale_avail[i]); s++) { + /* + * [s=0] = optional divider by two disabled (default) + * [s=1] = optional divider by two enabled + * + * The scale is calculated by doing: + * Vref >> (realbits - s) + * which multiplies by two on the second component + * of the array. + */ + scale_uv = ((u64)adc->vref_mv[i] * 100000000) >> + (LRADC_RESOLUTION - s); + adc->scale_avail[i][s].nano = + do_div(scale_uv, 100000000) * 10; + adc->scale_avail[i][s].integer = scale_uv; + } + } + + /* Configure the hardware. */ + mxs_lradc_adc_hw_init(adc); + + /* Register IIO device. */ + ret = iio_device_register(iio); + if (ret) { + dev_err(dev, "Failed to register IIO device\n"); + goto err_dev; + } + + return 0; + +err_dev: + mxs_lradc_adc_hw_stop(adc); + iio_triggered_buffer_cleanup(iio); +err_trig: + mxs_lradc_adc_trigger_remove(iio); + return ret; +} + +static void mxs_lradc_adc_remove(struct platform_device *pdev) +{ + struct iio_dev *iio = platform_get_drvdata(pdev); + struct mxs_lradc_adc *adc = iio_priv(iio); + + iio_device_unregister(iio); + mxs_lradc_adc_hw_stop(adc); + iio_triggered_buffer_cleanup(iio); + mxs_lradc_adc_trigger_remove(iio); +} + +static struct platform_driver mxs_lradc_adc_driver = { + .driver = { + .name = "mxs-lradc-adc", + }, + .probe = mxs_lradc_adc_probe, + .remove = mxs_lradc_adc_remove, +}; +module_platform_driver(mxs_lradc_adc_driver); + +MODULE_AUTHOR("Marek Vasut "); +MODULE_DESCRIPTION("Freescale MXS LRADC driver general purpose ADC driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:mxs-lradc-adc"); diff --git a/drivers/iio/adc/nau7802.c b/drivers/iio/adc/nau7802.c new file mode 100644 index 000000000..dccf11fbf --- /dev/null +++ b/drivers/iio/adc/nau7802.c @@ -0,0 +1,558 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Driver for the Nuvoton NAU7802 ADC + * + * Copyright 2013 Free Electrons + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#define NAU7802_REG_PUCTRL 0x00 +#define NAU7802_PUCTRL_RR(x) (x << 0) +#define NAU7802_PUCTRL_RR_BIT NAU7802_PUCTRL_RR(1) +#define NAU7802_PUCTRL_PUD(x) (x << 1) +#define NAU7802_PUCTRL_PUD_BIT NAU7802_PUCTRL_PUD(1) +#define NAU7802_PUCTRL_PUA(x) (x << 2) +#define NAU7802_PUCTRL_PUA_BIT NAU7802_PUCTRL_PUA(1) +#define NAU7802_PUCTRL_PUR(x) (x << 3) +#define NAU7802_PUCTRL_PUR_BIT NAU7802_PUCTRL_PUR(1) +#define NAU7802_PUCTRL_CS(x) (x << 4) +#define NAU7802_PUCTRL_CS_BIT NAU7802_PUCTRL_CS(1) +#define NAU7802_PUCTRL_CR(x) (x << 5) +#define NAU7802_PUCTRL_CR_BIT NAU7802_PUCTRL_CR(1) +#define NAU7802_PUCTRL_AVDDS(x) (x << 7) +#define NAU7802_PUCTRL_AVDDS_BIT NAU7802_PUCTRL_AVDDS(1) +#define NAU7802_REG_CTRL1 0x01 +#define NAU7802_CTRL1_VLDO(x) (x << 3) +#define NAU7802_CTRL1_GAINS(x) (x) +#define NAU7802_CTRL1_GAINS_BITS 0x07 +#define NAU7802_REG_CTRL2 0x02 +#define NAU7802_CTRL2_CHS(x) (x << 7) +#define NAU7802_CTRL2_CRS(x) (x << 4) +#define NAU7802_SAMP_FREQ_320 0x07 +#define NAU7802_CTRL2_CHS_BIT NAU7802_CTRL2_CHS(1) +#define NAU7802_REG_ADC_B2 0x12 +#define NAU7802_REG_ADC_B1 0x13 +#define NAU7802_REG_ADC_B0 0x14 +#define NAU7802_REG_ADC_CTRL 0x15 + +#define NAU7802_MIN_CONVERSIONS 6 + +struct nau7802_state { + struct i2c_client *client; + s32 last_value; + struct mutex lock; + struct mutex data_lock; + u32 vref_mv; + u32 conversion_count; + u8 sample_rate; + u32 scale_avail[8]; + struct completion value_ok; +}; + +#define NAU7802_CHANNEL(chan) { \ + .type = IIO_VOLTAGE, \ + .indexed = 1, \ + .channel = (chan), \ + .scan_index = (chan), \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | \ + BIT(IIO_CHAN_INFO_SAMP_FREQ) \ +} + +static const struct iio_chan_spec nau7802_chan_array[] = { + NAU7802_CHANNEL(0), + NAU7802_CHANNEL(1), +}; + +static const u16 nau7802_sample_freq_avail[] = {10, 20, 40, 80, + 10, 10, 10, 320}; + +static ssize_t nau7802_show_scales(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct nau7802_state *st = iio_priv(dev_to_iio_dev(dev)); + int i, len = 0; + + for (i = 0; i < ARRAY_SIZE(st->scale_avail); i++) + len += scnprintf(buf + len, PAGE_SIZE - len, "0.%09d ", + st->scale_avail[i]); + + buf[len-1] = '\n'; + + return len; +} + +static IIO_CONST_ATTR_SAMP_FREQ_AVAIL("10 40 80 320"); + +static IIO_DEVICE_ATTR(in_voltage_scale_available, S_IRUGO, nau7802_show_scales, + NULL, 0); + +static struct attribute *nau7802_attributes[] = { + &iio_const_attr_sampling_frequency_available.dev_attr.attr, + &iio_dev_attr_in_voltage_scale_available.dev_attr.attr, + NULL +}; + +static const struct attribute_group nau7802_attribute_group = { + .attrs = nau7802_attributes, +}; + +static int nau7802_set_gain(struct nau7802_state *st, int gain) +{ + int ret; + + mutex_lock(&st->lock); + st->conversion_count = 0; + + ret = i2c_smbus_read_byte_data(st->client, NAU7802_REG_CTRL1); + if (ret < 0) + goto nau7802_sysfs_set_gain_out; + ret = i2c_smbus_write_byte_data(st->client, NAU7802_REG_CTRL1, + (ret & (~NAU7802_CTRL1_GAINS_BITS)) | + gain); + +nau7802_sysfs_set_gain_out: + mutex_unlock(&st->lock); + + return ret; +} + +static int nau7802_read_conversion(struct nau7802_state *st) +{ + int data; + + mutex_lock(&st->data_lock); + data = i2c_smbus_read_byte_data(st->client, NAU7802_REG_ADC_B2); + if (data < 0) + goto nau7802_read_conversion_out; + st->last_value = data << 16; + + data = i2c_smbus_read_byte_data(st->client, NAU7802_REG_ADC_B1); + if (data < 0) + goto nau7802_read_conversion_out; + st->last_value |= data << 8; + + data = i2c_smbus_read_byte_data(st->client, NAU7802_REG_ADC_B0); + if (data < 0) + goto nau7802_read_conversion_out; + st->last_value |= data; + + st->last_value = sign_extend32(st->last_value, 23); + +nau7802_read_conversion_out: + mutex_unlock(&st->data_lock); + + return data; +} + +/* + * Conversions are synchronised on the rising edge of NAU7802_PUCTRL_CS_BIT + */ +static int nau7802_sync(struct nau7802_state *st) +{ + int ret; + + ret = i2c_smbus_read_byte_data(st->client, NAU7802_REG_PUCTRL); + if (ret < 0) + return ret; + ret = i2c_smbus_write_byte_data(st->client, NAU7802_REG_PUCTRL, + ret | NAU7802_PUCTRL_CS_BIT); + + return ret; +} + +static irqreturn_t nau7802_eoc_trigger(int irq, void *private) +{ + struct iio_dev *indio_dev = private; + struct nau7802_state *st = iio_priv(indio_dev); + int status; + + status = i2c_smbus_read_byte_data(st->client, NAU7802_REG_PUCTRL); + if (status < 0) + return IRQ_HANDLED; + + if (!(status & NAU7802_PUCTRL_CR_BIT)) + return IRQ_NONE; + + if (nau7802_read_conversion(st) < 0) + return IRQ_HANDLED; + + /* + * Because there is actually only one ADC for both channels, we have to + * wait for enough conversions to happen before getting a significant + * value when changing channels and the values are far apart. + */ + if (st->conversion_count < NAU7802_MIN_CONVERSIONS) + st->conversion_count++; + if (st->conversion_count >= NAU7802_MIN_CONVERSIONS) + complete(&st->value_ok); + + return IRQ_HANDLED; +} + +static int nau7802_read_irq(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val) +{ + struct nau7802_state *st = iio_priv(indio_dev); + int ret; + + reinit_completion(&st->value_ok); + enable_irq(st->client->irq); + + nau7802_sync(st); + + /* read registers to ensure we flush everything */ + ret = nau7802_read_conversion(st); + if (ret < 0) + goto read_chan_info_failure; + + /* Wait for a conversion to finish */ + ret = wait_for_completion_interruptible_timeout(&st->value_ok, + msecs_to_jiffies(1000)); + if (ret == 0) + ret = -ETIMEDOUT; + + if (ret < 0) + goto read_chan_info_failure; + + disable_irq(st->client->irq); + + *val = st->last_value; + + return IIO_VAL_INT; + +read_chan_info_failure: + disable_irq(st->client->irq); + + return ret; +} + +static int nau7802_read_poll(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val) +{ + struct nau7802_state *st = iio_priv(indio_dev); + int ret; + + nau7802_sync(st); + + /* read registers to ensure we flush everything */ + ret = nau7802_read_conversion(st); + if (ret < 0) + return ret; + + /* + * Because there is actually only one ADC for both channels, we have to + * wait for enough conversions to happen before getting a significant + * value when changing channels and the values are far apart. + */ + do { + ret = i2c_smbus_read_byte_data(st->client, NAU7802_REG_PUCTRL); + if (ret < 0) + return ret; + + while (!(ret & NAU7802_PUCTRL_CR_BIT)) { + if (st->sample_rate != NAU7802_SAMP_FREQ_320) + msleep(20); + else + mdelay(4); + ret = i2c_smbus_read_byte_data(st->client, + NAU7802_REG_PUCTRL); + if (ret < 0) + return ret; + } + + ret = nau7802_read_conversion(st); + if (ret < 0) + return ret; + if (st->conversion_count < NAU7802_MIN_CONVERSIONS) + st->conversion_count++; + } while (st->conversion_count < NAU7802_MIN_CONVERSIONS); + + *val = st->last_value; + + return IIO_VAL_INT; +} + +static int nau7802_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct nau7802_state *st = iio_priv(indio_dev); + int ret; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + mutex_lock(&st->lock); + /* + * Select the channel to use + * - Channel 1 is value 0 in the CHS register + * - Channel 2 is value 1 in the CHS register + */ + ret = i2c_smbus_read_byte_data(st->client, NAU7802_REG_CTRL2); + if (ret < 0) { + mutex_unlock(&st->lock); + return ret; + } + + if (((ret & NAU7802_CTRL2_CHS_BIT) && !chan->channel) || + (!(ret & NAU7802_CTRL2_CHS_BIT) && + chan->channel)) { + st->conversion_count = 0; + ret = i2c_smbus_write_byte_data(st->client, + NAU7802_REG_CTRL2, + NAU7802_CTRL2_CHS(chan->channel) | + NAU7802_CTRL2_CRS(st->sample_rate)); + + if (ret < 0) { + mutex_unlock(&st->lock); + return ret; + } + } + + if (st->client->irq) + ret = nau7802_read_irq(indio_dev, chan, val); + else + ret = nau7802_read_poll(indio_dev, chan, val); + + mutex_unlock(&st->lock); + return ret; + + case IIO_CHAN_INFO_SCALE: + ret = i2c_smbus_read_byte_data(st->client, NAU7802_REG_CTRL1); + if (ret < 0) + return ret; + + /* + * We have 24 bits of signed data, that means 23 bits of data + * plus the sign bit + */ + *val = st->vref_mv; + *val2 = 23 + (ret & NAU7802_CTRL1_GAINS_BITS); + + return IIO_VAL_FRACTIONAL_LOG2; + + case IIO_CHAN_INFO_SAMP_FREQ: + *val = nau7802_sample_freq_avail[st->sample_rate]; + *val2 = 0; + return IIO_VAL_INT; + + default: + break; + } + + return -EINVAL; +} + +static int nau7802_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, int val2, long mask) +{ + struct nau7802_state *st = iio_priv(indio_dev); + int i, ret; + + switch (mask) { + case IIO_CHAN_INFO_SCALE: + for (i = 0; i < ARRAY_SIZE(st->scale_avail); i++) + if (val2 == st->scale_avail[i]) + return nau7802_set_gain(st, i); + + break; + + case IIO_CHAN_INFO_SAMP_FREQ: + for (i = 0; i < ARRAY_SIZE(nau7802_sample_freq_avail); i++) + if (val == nau7802_sample_freq_avail[i]) { + mutex_lock(&st->lock); + st->sample_rate = i; + st->conversion_count = 0; + ret = i2c_smbus_write_byte_data(st->client, + NAU7802_REG_CTRL2, + NAU7802_CTRL2_CRS(st->sample_rate)); + mutex_unlock(&st->lock); + return ret; + } + + break; + + default: + break; + } + + return -EINVAL; +} + +static int nau7802_write_raw_get_fmt(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + long mask) +{ + return IIO_VAL_INT_PLUS_NANO; +} + +static const struct iio_info nau7802_info = { + .read_raw = &nau7802_read_raw, + .write_raw = &nau7802_write_raw, + .write_raw_get_fmt = nau7802_write_raw_get_fmt, + .attrs = &nau7802_attribute_group, +}; + +static int nau7802_probe(struct i2c_client *client) +{ + struct iio_dev *indio_dev; + struct nau7802_state *st; + int i, ret; + u8 data; + u32 tmp = 0; + + indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*st)); + if (indio_dev == NULL) + return -ENOMEM; + + st = iio_priv(indio_dev); + + indio_dev->name = dev_name(&client->dev); + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->info = &nau7802_info; + + st->client = client; + + /* Reset the device */ + ret = i2c_smbus_write_byte_data(st->client, NAU7802_REG_PUCTRL, + NAU7802_PUCTRL_RR_BIT); + if (ret < 0) + return ret; + + /* Enter normal operation mode */ + ret = i2c_smbus_write_byte_data(st->client, NAU7802_REG_PUCTRL, + NAU7802_PUCTRL_PUD_BIT); + if (ret < 0) + return ret; + + /* + * After about 200 usecs, the device should be ready and then + * the Power Up bit will be set to 1. If not, wait for it. + */ + udelay(210); + ret = i2c_smbus_read_byte_data(st->client, NAU7802_REG_PUCTRL); + if (ret < 0) + return ret; + if (!(ret & NAU7802_PUCTRL_PUR_BIT)) + return ret; + + device_property_read_u32(&client->dev, "nuvoton,vldo", &tmp); + st->vref_mv = tmp; + + data = NAU7802_PUCTRL_PUD_BIT | NAU7802_PUCTRL_PUA_BIT | + NAU7802_PUCTRL_CS_BIT; + if (tmp >= 2400) + data |= NAU7802_PUCTRL_AVDDS_BIT; + + ret = i2c_smbus_write_byte_data(st->client, NAU7802_REG_PUCTRL, data); + if (ret < 0) + return ret; + ret = i2c_smbus_write_byte_data(st->client, NAU7802_REG_ADC_CTRL, 0x30); + if (ret < 0) + return ret; + + if (tmp >= 2400) { + data = NAU7802_CTRL1_VLDO((4500 - tmp) / 300); + ret = i2c_smbus_write_byte_data(st->client, NAU7802_REG_CTRL1, + data); + if (ret < 0) + return ret; + } + + /* Populate available ADC input ranges */ + for (i = 0; i < ARRAY_SIZE(st->scale_avail); i++) + st->scale_avail[i] = (((u64)st->vref_mv) * 1000000000ULL) + >> (23 + i); + + init_completion(&st->value_ok); + + /* + * The ADC fires continuously and we can't do anything about + * it. So we need to have the IRQ disabled by default, and we + * will enable them back when we will need them.. + */ + if (client->irq) { + ret = devm_request_threaded_irq(&client->dev, client->irq, + NULL, + nau7802_eoc_trigger, + IRQF_TRIGGER_HIGH | IRQF_ONESHOT | + IRQF_NO_AUTOEN, + client->dev.driver->name, + indio_dev); + if (ret) { + /* + * What may happen here is that our IRQ controller is + * not able to get level interrupt but this is required + * by this ADC as when going over 40 sample per second, + * the interrupt line may stay high between conversions. + * So, we continue no matter what but we switch to + * polling mode. + */ + dev_info(&client->dev, + "Failed to allocate IRQ, using polling mode\n"); + client->irq = 0; + } + } + + if (!client->irq) { + /* + * We are polling, use the fastest sample rate by + * default + */ + st->sample_rate = NAU7802_SAMP_FREQ_320; + ret = i2c_smbus_write_byte_data(st->client, NAU7802_REG_CTRL2, + NAU7802_CTRL2_CRS(st->sample_rate)); + if (ret) + return ret; + } + + /* Setup the ADC channels available on the board */ + indio_dev->num_channels = ARRAY_SIZE(nau7802_chan_array); + indio_dev->channels = nau7802_chan_array; + + mutex_init(&st->lock); + mutex_init(&st->data_lock); + + return devm_iio_device_register(&client->dev, indio_dev); +} + +static const struct i2c_device_id nau7802_i2c_id[] = { + { .name = "nau7802" }, + { } +}; +MODULE_DEVICE_TABLE(i2c, nau7802_i2c_id); + +static const struct of_device_id nau7802_dt_ids[] = { + { .compatible = "nuvoton,nau7802" }, + { } +}; +MODULE_DEVICE_TABLE(of, nau7802_dt_ids); + +static struct i2c_driver nau7802_driver = { + .probe = nau7802_probe, + .id_table = nau7802_i2c_id, + .driver = { + .name = "nau7802", + .of_match_table = nau7802_dt_ids, + }, +}; + +module_i2c_driver(nau7802_driver); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Nuvoton NAU7802 ADC Driver"); +MODULE_AUTHOR("Maxime Ripard "); +MODULE_AUTHOR("Alexandre Belloni "); diff --git a/drivers/iio/adc/nct7201.c b/drivers/iio/adc/nct7201.c new file mode 100644 index 000000000..bea88a9b9 --- /dev/null +++ b/drivers/iio/adc/nct7201.c @@ -0,0 +1,500 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Driver for Nuvoton nct7201 and nct7202 power monitor chips. + * + * Copyright (c) 2024-2025 Nuvoton Technology corporation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#define NCT7201_REG_INTERRUPT_STATUS 0x0C +#define NCT7201_REG_VOLT_LOW_BYTE 0x0F +#define NCT7201_REG_CONFIGURATION 0x10 +#define NCT7201_BIT_CONFIGURATION_START BIT(0) +#define NCT7201_BIT_CONFIGURATION_ALERT_MSK BIT(1) +#define NCT7201_BIT_CONFIGURATION_CONV_RATE BIT(2) +#define NCT7201_BIT_CONFIGURATION_RESET BIT(7) + +#define NCT7201_REG_ADVANCED_CONFIGURATION 0x11 +#define NCT7201_BIT_ADVANCED_CONF_MOD_ALERT BIT(0) +#define NCT7201_BIT_ADVANCED_CONF_MOD_STS BIT(1) +#define NCT7201_BIT_ADVANCED_CONF_FAULT_QUEUE BIT(2) +#define NCT7201_BIT_ADVANCED_CONF_EN_DEEP_SHUTDOWN BIT(4) +#define NCT7201_BIT_ADVANCED_CONF_EN_SMB_TIMEOUT BIT(5) +#define NCT7201_BIT_ADVANCED_CONF_MOD_RSTIN BIT(7) + +#define NCT7201_REG_CHANNEL_INPUT_MODE 0x12 +#define NCT7201_REG_CHANNEL_ENABLE 0x13 +#define NCT7201_REG_INTERRUPT_MASK_1 0x15 +#define NCT7201_REG_INTERRUPT_MASK_2 0x16 +#define NCT7201_REG_BUSY_STATUS 0x1E +#define NCT7201_BIT_BUSY BIT(0) +#define NCT7201_BIT_PWR_UP BIT(1) +#define NCT7201_REG_ONE_SHOT 0x1F +#define NCT7201_REG_SMUS_ADDRESS 0xFC +#define NCT7201_REG_VIN_MASK GENMASK(15, 3) + +#define NCT7201_REG_VIN(i) (0x00 + i) +#define NCT7201_REG_VIN_HIGH_LIMIT(i) (0x20 + (i) * 2) +#define NCT7201_REG_VIN_LOW_LIMIT(i) (0x21 + (i) * 2) +#define NCT7201_MAX_CHANNEL 12 + +static const struct regmap_range nct7201_read_reg_range[] = { + regmap_reg_range(NCT7201_REG_INTERRUPT_STATUS, NCT7201_REG_BUSY_STATUS), + regmap_reg_range(NCT7201_REG_SMUS_ADDRESS, NCT7201_REG_SMUS_ADDRESS), +}; + +static const struct regmap_access_table nct7201_readable_regs_tbl = { + .yes_ranges = nct7201_read_reg_range, + .n_yes_ranges = ARRAY_SIZE(nct7201_read_reg_range), +}; + +static const struct regmap_range nct7201_write_reg_range[] = { + regmap_reg_range(NCT7201_REG_CONFIGURATION, NCT7201_REG_INTERRUPT_MASK_2), + regmap_reg_range(NCT7201_REG_ONE_SHOT, NCT7201_REG_ONE_SHOT), +}; + +static const struct regmap_access_table nct7201_writeable_regs_tbl = { + .yes_ranges = nct7201_write_reg_range, + .n_yes_ranges = ARRAY_SIZE(nct7201_write_reg_range), +}; + +static const struct regmap_range nct7201_read_vin_reg_range[] = { + regmap_reg_range(NCT7201_REG_VIN(0), NCT7201_REG_VIN(NCT7201_MAX_CHANNEL - 1)), + regmap_reg_range(NCT7201_REG_VIN_HIGH_LIMIT(0), + NCT7201_REG_VIN_LOW_LIMIT(NCT7201_MAX_CHANNEL - 1)), +}; + +static const struct regmap_access_table nct7201_readable_vin_regs_tbl = { + .yes_ranges = nct7201_read_vin_reg_range, + .n_yes_ranges = ARRAY_SIZE(nct7201_read_vin_reg_range), +}; + +static const struct regmap_range nct7201_write_vin_reg_range[] = { + regmap_reg_range(NCT7201_REG_VIN_HIGH_LIMIT(0), + NCT7201_REG_VIN_LOW_LIMIT(NCT7201_MAX_CHANNEL - 1)), +}; + +static const struct regmap_access_table nct7201_writeable_vin_regs_tbl = { + .yes_ranges = nct7201_write_vin_reg_range, + .n_yes_ranges = ARRAY_SIZE(nct7201_write_vin_reg_range), +}; + +static const struct regmap_config nct7201_regmap8_config = { + .name = "vin-data-read-byte", + .reg_bits = 8, + .val_bits = 8, + .use_single_read = true, + .use_single_write = true, + .max_register = 0xff, + .rd_table = &nct7201_readable_regs_tbl, + .wr_table = &nct7201_writeable_regs_tbl, +}; + +static const struct regmap_config nct7201_regmap16_config = { + .name = "vin-data-read-word", + .reg_bits = 8, + .val_bits = 16, + .max_register = 0xff, + .rd_table = &nct7201_readable_vin_regs_tbl, + .wr_table = &nct7201_writeable_vin_regs_tbl, +}; + +struct nct7201_chip_info { + struct regmap *regmap; + struct regmap *regmap16; + int num_vin_channels; + __le16 vin_mask; +}; + +struct nct7201_adc_model_data { + const char *model_name; + const struct iio_chan_spec *channels; + unsigned int num_channels; + int num_vin_channels; +}; + +static const struct iio_event_spec nct7201_events[] = { + { + .type = IIO_EV_TYPE_THRESH, + .dir = IIO_EV_DIR_RISING, + .mask_separate = BIT(IIO_EV_INFO_VALUE) | + BIT(IIO_EV_INFO_ENABLE), + }, { + .type = IIO_EV_TYPE_THRESH, + .dir = IIO_EV_DIR_FALLING, + .mask_separate = BIT(IIO_EV_INFO_VALUE) | + BIT(IIO_EV_INFO_ENABLE), + }, +}; + +#define NCT7201_VOLTAGE_CHANNEL(num) \ + { \ + .type = IIO_VOLTAGE, \ + .indexed = 1, \ + .channel = num + 1, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ + .address = num, \ + .event_spec = nct7201_events, \ + .num_event_specs = ARRAY_SIZE(nct7201_events), \ + } + +static const struct iio_chan_spec nct7201_channels[] = { + NCT7201_VOLTAGE_CHANNEL(0), + NCT7201_VOLTAGE_CHANNEL(1), + NCT7201_VOLTAGE_CHANNEL(2), + NCT7201_VOLTAGE_CHANNEL(3), + NCT7201_VOLTAGE_CHANNEL(4), + NCT7201_VOLTAGE_CHANNEL(5), + NCT7201_VOLTAGE_CHANNEL(6), + NCT7201_VOLTAGE_CHANNEL(7), +}; + +static const struct iio_chan_spec nct7202_channels[] = { + NCT7201_VOLTAGE_CHANNEL(0), + NCT7201_VOLTAGE_CHANNEL(1), + NCT7201_VOLTAGE_CHANNEL(2), + NCT7201_VOLTAGE_CHANNEL(3), + NCT7201_VOLTAGE_CHANNEL(4), + NCT7201_VOLTAGE_CHANNEL(5), + NCT7201_VOLTAGE_CHANNEL(6), + NCT7201_VOLTAGE_CHANNEL(7), + NCT7201_VOLTAGE_CHANNEL(8), + NCT7201_VOLTAGE_CHANNEL(9), + NCT7201_VOLTAGE_CHANNEL(10), + NCT7201_VOLTAGE_CHANNEL(11), +}; + +static int nct7201_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct nct7201_chip_info *chip = iio_priv(indio_dev); + unsigned int value; + int err; + + if (chan->type != IIO_VOLTAGE) + return -EOPNOTSUPP; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + err = regmap_read(chip->regmap16, NCT7201_REG_VIN(chan->address), &value); + if (err) + return err; + *val = FIELD_GET(NCT7201_REG_VIN_MASK, value); + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + /* From the datasheet, we have to multiply by 0.0004995 */ + *val = 0; + *val2 = 499500; + return IIO_VAL_INT_PLUS_NANO; + default: + return -EINVAL; + } +} + +static int nct7201_read_event_value(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir, + enum iio_event_info info, + int *val, int *val2) +{ + struct nct7201_chip_info *chip = iio_priv(indio_dev); + unsigned int value; + int err; + + if (chan->type != IIO_VOLTAGE) + return -EOPNOTSUPP; + + if (info != IIO_EV_INFO_VALUE) + return -EINVAL; + + if (dir == IIO_EV_DIR_FALLING) + err = regmap_read(chip->regmap16, NCT7201_REG_VIN_LOW_LIMIT(chan->address), + &value); + else + err = regmap_read(chip->regmap16, NCT7201_REG_VIN_HIGH_LIMIT(chan->address), + &value); + if (err) + return err; + + *val = FIELD_GET(NCT7201_REG_VIN_MASK, value); + + return IIO_VAL_INT; +} + +static int nct7201_write_event_value(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir, + enum iio_event_info info, + int val, int val2) +{ + struct nct7201_chip_info *chip = iio_priv(indio_dev); + int err; + + if (chan->type != IIO_VOLTAGE) + return -EOPNOTSUPP; + + if (info != IIO_EV_INFO_VALUE) + return -EOPNOTSUPP; + + if (dir == IIO_EV_DIR_FALLING) + err = regmap_write(chip->regmap16, NCT7201_REG_VIN_LOW_LIMIT(chan->address), + FIELD_PREP(NCT7201_REG_VIN_MASK, val)); + else + err = regmap_write(chip->regmap16, NCT7201_REG_VIN_HIGH_LIMIT(chan->address), + FIELD_PREP(NCT7201_REG_VIN_MASK, val)); + + return err; +} + +static int nct7201_read_event_config(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir) +{ + struct nct7201_chip_info *chip = iio_priv(indio_dev); + + if (chan->type != IIO_VOLTAGE) + return -EOPNOTSUPP; + + return !!(le16_to_cpu(chip->vin_mask) & BIT(chan->address)); +} + +static int nct7201_write_event_config(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir, + bool state) +{ + struct nct7201_chip_info *chip = iio_priv(indio_dev); + __le16 mask = cpu_to_le16(BIT(chan->address)); + int err; + + if (chan->type != IIO_VOLTAGE) + return -EOPNOTSUPP; + + if (state) + chip->vin_mask |= mask; + else + chip->vin_mask &= ~mask; + + if (chip->num_vin_channels <= 8) + err = regmap_write(chip->regmap, NCT7201_REG_CHANNEL_ENABLE, + le16_to_cpu(chip->vin_mask)); + else + err = regmap_bulk_write(chip->regmap, NCT7201_REG_CHANNEL_ENABLE, + &chip->vin_mask, sizeof(chip->vin_mask)); + + return err; +} + +static const struct iio_info nct7201_info = { + .read_raw = nct7201_read_raw, + .read_event_config = nct7201_read_event_config, + .write_event_config = nct7201_write_event_config, + .read_event_value = nct7201_read_event_value, + .write_event_value = nct7201_write_event_value, +}; + +static const struct iio_info nct7201_info_no_irq = { + .read_raw = nct7201_read_raw, +}; + +static const struct nct7201_adc_model_data nct7201_model_data = { + .model_name = "nct7201", + .channels = nct7201_channels, + .num_channels = ARRAY_SIZE(nct7201_channels), + .num_vin_channels = 8, +}; + +static const struct nct7201_adc_model_data nct7202_model_data = { + .model_name = "nct7202", + .channels = nct7202_channels, + .num_channels = ARRAY_SIZE(nct7202_channels), + .num_vin_channels = 12, +}; + +static int nct7201_init_chip(struct nct7201_chip_info *chip) +{ + struct device *dev = regmap_get_device(chip->regmap); + __le16 data = cpu_to_le16(GENMASK(chip->num_vin_channels - 1, 0)); + unsigned int value; + int err; + + err = regmap_write(chip->regmap, NCT7201_REG_CONFIGURATION, + NCT7201_BIT_CONFIGURATION_RESET); + if (err) + return dev_err_probe(dev, err, "Failed to reset chip\n"); + + /* + * After about 25 msecs, the device should be ready and then the power-up + * bit will be set to 1. + */ + fsleep(25 * USEC_PER_MSEC); + + err = regmap_read(chip->regmap, NCT7201_REG_BUSY_STATUS, &value); + if (err) + return dev_err_probe(dev, err, "Failed to read busy status\n"); + if (!(value & NCT7201_BIT_PWR_UP)) + return dev_err_probe(dev, -EIO, "Failed to power up after reset\n"); + + /* Enable Channels */ + if (chip->num_vin_channels <= 8) + err = regmap_write(chip->regmap, NCT7201_REG_CHANNEL_ENABLE, + le16_to_cpu(data)); + else + err = regmap_bulk_write(chip->regmap, NCT7201_REG_CHANNEL_ENABLE, + &data, sizeof(data)); + if (err) + return dev_err_probe(dev, err, "Failed to enable channels\n"); + + err = regmap_bulk_read(chip->regmap, NCT7201_REG_CHANNEL_ENABLE, + &chip->vin_mask, sizeof(chip->vin_mask)); + if (err) + return dev_err_probe(dev, err, + "Failed to read channel enable register\n"); + + /* Start monitoring if needed */ + err = regmap_set_bits(chip->regmap, NCT7201_REG_CONFIGURATION, + NCT7201_BIT_CONFIGURATION_START); + if (err) + return dev_err_probe(dev, err, "Failed to start monitoring\n"); + + return 0; +} + +static irqreturn_t nct7201_irq_handler(int irq, void *private) +{ + struct iio_dev *indio_dev = private; + struct nct7201_chip_info *chip = iio_priv(indio_dev); + __le16 data; + int err; + + err = regmap_bulk_read(chip->regmap, NCT7201_REG_INTERRUPT_STATUS, + &data, sizeof(data)); + if (err) + return IRQ_NONE; + + if (data) + iio_push_event(indio_dev, + IIO_UNMOD_EVENT_CODE(IIO_VOLTAGE, + 0, + IIO_EV_TYPE_THRESH, + IIO_EV_DIR_EITHER), + iio_get_time_ns(indio_dev)); + + return IRQ_HANDLED; +} + +static int nct7201_probe(struct i2c_client *client) +{ + const struct nct7201_adc_model_data *model_data; + struct device *dev = &client->dev; + struct nct7201_chip_info *chip; + struct iio_dev *indio_dev; + int ret; + + model_data = i2c_get_match_data(client); + if (!model_data) + return -ENODEV; + + indio_dev = devm_iio_device_alloc(dev, sizeof(*chip)); + if (!indio_dev) + return -ENOMEM; + chip = iio_priv(indio_dev); + + chip->regmap = devm_regmap_init_i2c(client, &nct7201_regmap8_config); + if (IS_ERR(chip->regmap)) + return dev_err_probe(dev, PTR_ERR(chip->regmap), + "Failed to init regmap\n"); + + chip->regmap16 = devm_regmap_init_i2c(client, &nct7201_regmap16_config); + if (IS_ERR(chip->regmap16)) + return dev_err_probe(dev, PTR_ERR(chip->regmap16), + "Failed to init regmap16\n"); + + chip->num_vin_channels = model_data->num_vin_channels; + + ret = nct7201_init_chip(chip); + if (ret) + return ret; + + indio_dev->name = model_data->model_name; + indio_dev->channels = model_data->channels; + indio_dev->num_channels = model_data->num_channels; + if (client->irq) { + /* Enable alert function */ + ret = regmap_clear_bits(chip->regmap, NCT7201_REG_CONFIGURATION, + NCT7201_BIT_CONFIGURATION_ALERT_MSK); + if (ret) + return dev_err_probe(dev, ret, + "Failed to enable alert function\n"); + + ret = devm_request_threaded_irq(dev, client->irq, + NULL, nct7201_irq_handler, + IRQF_TRIGGER_LOW | IRQF_ONESHOT, + client->name, indio_dev); + if (ret) + return dev_err_probe(dev, ret, + "Failed to assign interrupt.\n"); + + indio_dev->info = &nct7201_info; + } else { + indio_dev->info = &nct7201_info_no_irq; + } + indio_dev->modes = INDIO_DIRECT_MODE; + + return devm_iio_device_register(dev, indio_dev); +} + +static const struct i2c_device_id nct7201_id[] = { + { .name = "nct7201", .driver_data = (kernel_ulong_t)&nct7201_model_data }, + { .name = "nct7202", .driver_data = (kernel_ulong_t)&nct7202_model_data }, + { } +}; +MODULE_DEVICE_TABLE(i2c, nct7201_id); + +static const struct of_device_id nct7201_of_match[] = { + { + .compatible = "nuvoton,nct7201", + .data = &nct7201_model_data, + }, + { + .compatible = "nuvoton,nct7202", + .data = &nct7202_model_data, + }, + { } +}; +MODULE_DEVICE_TABLE(of, nct7201_of_match); + +static struct i2c_driver nct7201_driver = { + .driver = { + .name = "nct7201", + .of_match_table = nct7201_of_match, + }, + .probe = nct7201_probe, + .id_table = nct7201_id, +}; +module_i2c_driver(nct7201_driver); + +MODULE_AUTHOR("Eason Yang "); +MODULE_DESCRIPTION("Nuvoton NCT7201 voltage monitor driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/iio/adc/npcm_adc.c b/drivers/iio/adc/npcm_adc.c new file mode 100644 index 000000000..a25c15b38 --- /dev/null +++ b/drivers/iio/adc/npcm_adc.c @@ -0,0 +1,341 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (c) 2019 Nuvoton Technology corporation. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct npcm_adc_info { + u32 data_mask; + u32 internal_vref; + u32 res_bits; +}; + +struct npcm_adc { + bool int_status; + u32 adc_sample_hz; + struct device *dev; + void __iomem *regs; + struct clk *adc_clk; + wait_queue_head_t wq; + struct regulator *vref; + struct reset_control *reset; + /* + * Lock to protect the device state during a potential concurrent + * read access from userspace. Reading a raw value requires a sequence + * of register writes, then a wait for a event and finally a register + * read, during which userspace could issue another read request. + * This lock protects a read access from occurring before another one + * has finished. + */ + struct mutex lock; + const struct npcm_adc_info *data; +}; + +/* ADC registers */ +#define NPCM_ADCCON 0x00 +#define NPCM_ADCDATA 0x04 + +/* ADCCON Register Bits */ +#define NPCM_ADCCON_ADC_INT_EN BIT(21) +#define NPCM_ADCCON_REFSEL BIT(19) +#define NPCM_ADCCON_ADC_INT_ST BIT(18) +#define NPCM_ADCCON_ADC_EN BIT(17) +#define NPCM_ADCCON_ADC_RST BIT(16) +#define NPCM_ADCCON_ADC_CONV BIT(13) + +#define NPCM_ADCCON_CH_MASK GENMASK(27, 24) +#define NPCM_ADCCON_CH(x) ((x) << 24) +#define NPCM_ADCCON_DIV_SHIFT 1 +#define NPCM_ADCCON_DIV_MASK GENMASK(8, 1) + +#define NPCM_ADC_ENABLE (NPCM_ADCCON_ADC_EN | NPCM_ADCCON_ADC_INT_EN) + +/* ADC General Definition */ +static const struct npcm_adc_info npxm7xx_adc_info = { + .data_mask = GENMASK(9, 0), + .internal_vref = 2048, + .res_bits = 10, +}; + +static const struct npcm_adc_info npxm8xx_adc_info = { + .data_mask = GENMASK(11, 0), + .internal_vref = 1229, + .res_bits = 12, +}; + +#define NPCM_ADC_CHAN(ch) { \ + .type = IIO_VOLTAGE, \ + .indexed = 1, \ + .channel = ch, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | \ + BIT(IIO_CHAN_INFO_SAMP_FREQ), \ +} + +static const struct iio_chan_spec npcm_adc_iio_channels[] = { + NPCM_ADC_CHAN(0), + NPCM_ADC_CHAN(1), + NPCM_ADC_CHAN(2), + NPCM_ADC_CHAN(3), + NPCM_ADC_CHAN(4), + NPCM_ADC_CHAN(5), + NPCM_ADC_CHAN(6), + NPCM_ADC_CHAN(7), +}; + +static irqreturn_t npcm_adc_isr(int irq, void *data) +{ + u32 regtemp; + struct iio_dev *indio_dev = data; + struct npcm_adc *info = iio_priv(indio_dev); + + regtemp = ioread32(info->regs + NPCM_ADCCON); + if (regtemp & NPCM_ADCCON_ADC_INT_ST) { + iowrite32(regtemp, info->regs + NPCM_ADCCON); + wake_up_interruptible(&info->wq); + info->int_status = true; + } + + return IRQ_HANDLED; +} + +static int npcm_adc_read(struct npcm_adc *info, int *val, u8 channel) +{ + int ret; + u32 regtemp; + + /* Select ADC channel */ + regtemp = ioread32(info->regs + NPCM_ADCCON); + regtemp &= ~NPCM_ADCCON_CH_MASK; + info->int_status = false; + iowrite32(regtemp | NPCM_ADCCON_CH(channel) | + NPCM_ADCCON_ADC_CONV, info->regs + NPCM_ADCCON); + + ret = wait_event_interruptible_timeout(info->wq, info->int_status, + msecs_to_jiffies(10)); + if (ret == 0) { + regtemp = ioread32(info->regs + NPCM_ADCCON); + if (regtemp & NPCM_ADCCON_ADC_CONV) { + /* if conversion failed - reset ADC module */ + reset_control_assert(info->reset); + msleep(100); + reset_control_deassert(info->reset); + msleep(100); + + /* Enable ADC and start conversion module */ + iowrite32(NPCM_ADC_ENABLE | NPCM_ADCCON_ADC_CONV, + info->regs + NPCM_ADCCON); + dev_err(info->dev, "RESET ADC Complete\n"); + } + return -ETIMEDOUT; + } + if (ret < 0) + return ret; + + *val = ioread32(info->regs + NPCM_ADCDATA); + *val &= info->data->data_mask; + + return 0; +} + +static int npcm_adc_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int *val, + int *val2, long mask) +{ + int ret; + int vref_uv; + struct npcm_adc *info = iio_priv(indio_dev); + + switch (mask) { + case IIO_CHAN_INFO_RAW: + mutex_lock(&info->lock); + ret = npcm_adc_read(info, val, chan->channel); + mutex_unlock(&info->lock); + if (ret) { + dev_err(info->dev, "NPCM ADC read failed\n"); + return ret; + } + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + if (!IS_ERR(info->vref)) { + vref_uv = regulator_get_voltage(info->vref); + *val = vref_uv / 1000; + } else { + *val = info->data->internal_vref; + } + *val2 = info->data->res_bits; + return IIO_VAL_FRACTIONAL_LOG2; + case IIO_CHAN_INFO_SAMP_FREQ: + *val = info->adc_sample_hz; + return IIO_VAL_INT; + default: + return -EINVAL; + } + + return 0; +} + +static const struct iio_info npcm_adc_iio_info = { + .read_raw = &npcm_adc_read_raw, +}; + +static const struct of_device_id npcm_adc_match[] = { + { .compatible = "nuvoton,npcm750-adc", .data = &npxm7xx_adc_info}, + { .compatible = "nuvoton,npcm845-adc", .data = &npxm8xx_adc_info}, + { } +}; +MODULE_DEVICE_TABLE(of, npcm_adc_match); + +static int npcm_adc_probe(struct platform_device *pdev) +{ + int ret; + int irq; + u32 div; + u32 reg_con; + struct npcm_adc *info; + struct iio_dev *indio_dev; + struct device *dev = &pdev->dev; + + indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*info)); + if (!indio_dev) + return -ENOMEM; + info = iio_priv(indio_dev); + + info->data = device_get_match_data(dev); + if (!info->data) + return -EINVAL; + + mutex_init(&info->lock); + + info->dev = &pdev->dev; + + info->regs = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(info->regs)) + return PTR_ERR(info->regs); + + info->reset = devm_reset_control_get(&pdev->dev, NULL); + if (IS_ERR(info->reset)) + return PTR_ERR(info->reset); + + info->adc_clk = devm_clk_get_enabled(&pdev->dev, NULL); + if (IS_ERR(info->adc_clk)) { + dev_warn(&pdev->dev, "ADC clock failed: can't read clk\n"); + return PTR_ERR(info->adc_clk); + } + + /* calculate ADC clock sample rate */ + reg_con = ioread32(info->regs + NPCM_ADCCON); + div = reg_con & NPCM_ADCCON_DIV_MASK; + div = div >> NPCM_ADCCON_DIV_SHIFT; + info->adc_sample_hz = clk_get_rate(info->adc_clk) / ((div + 1) * 2); + + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return irq; + + ret = devm_request_irq(&pdev->dev, irq, npcm_adc_isr, 0, + "NPCM_ADC", indio_dev); + if (ret < 0) + return ret; + + reg_con = ioread32(info->regs + NPCM_ADCCON); + info->vref = devm_regulator_get_optional(&pdev->dev, "vref"); + if (!IS_ERR(info->vref)) { + ret = regulator_enable(info->vref); + if (ret) { + dev_err(&pdev->dev, "Can't enable ADC reference voltage\n"); + return ret; + } + + iowrite32(reg_con & ~NPCM_ADCCON_REFSEL, + info->regs + NPCM_ADCCON); + } else { + /* + * Any error which is not ENODEV indicates the regulator + * has been specified and so is a failure case. + */ + if (PTR_ERR(info->vref) != -ENODEV) + return PTR_ERR(info->vref); + + /* Use internal reference */ + iowrite32(reg_con | NPCM_ADCCON_REFSEL, + info->regs + NPCM_ADCCON); + } + + init_waitqueue_head(&info->wq); + + reg_con = ioread32(info->regs + NPCM_ADCCON); + reg_con |= NPCM_ADC_ENABLE; + + /* Enable the ADC Module */ + iowrite32(reg_con, info->regs + NPCM_ADCCON); + + /* Start ADC conversion */ + iowrite32(reg_con | NPCM_ADCCON_ADC_CONV, info->regs + NPCM_ADCCON); + + platform_set_drvdata(pdev, indio_dev); + indio_dev->name = dev_name(&pdev->dev); + indio_dev->info = &npcm_adc_iio_info; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = npcm_adc_iio_channels; + indio_dev->num_channels = ARRAY_SIZE(npcm_adc_iio_channels); + + ret = iio_device_register(indio_dev); + if (ret) { + dev_err(&pdev->dev, "Couldn't register the device.\n"); + goto err_iio_register; + } + + pr_info("NPCM ADC driver probed\n"); + + return 0; + +err_iio_register: + iowrite32(reg_con & ~NPCM_ADCCON_ADC_EN, info->regs + NPCM_ADCCON); + if (!IS_ERR(info->vref)) + regulator_disable(info->vref); + + return ret; +} + +static void npcm_adc_remove(struct platform_device *pdev) +{ + struct iio_dev *indio_dev = platform_get_drvdata(pdev); + struct npcm_adc *info = iio_priv(indio_dev); + u32 regtemp; + + iio_device_unregister(indio_dev); + + regtemp = ioread32(info->regs + NPCM_ADCCON); + iowrite32(regtemp & ~NPCM_ADCCON_ADC_EN, info->regs + NPCM_ADCCON); + if (!IS_ERR(info->vref)) + regulator_disable(info->vref); +} + +static struct platform_driver npcm_adc_driver = { + .probe = npcm_adc_probe, + .remove = npcm_adc_remove, + .driver = { + .name = "npcm_adc", + .of_match_table = npcm_adc_match, + }, +}; + +module_platform_driver(npcm_adc_driver); + +MODULE_DESCRIPTION("Nuvoton NPCM ADC Driver"); +MODULE_AUTHOR("Tomer Maimon "); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/adc/nxp-sar-adc.c b/drivers/iio/adc/nxp-sar-adc.c new file mode 100644 index 000000000..894d3211b --- /dev/null +++ b/drivers/iio/adc/nxp-sar-adc.c @@ -0,0 +1,1030 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * NXP SAR-ADC driver (adapted from Freescale Vybrid vf610 ADC driver + * by Fugang Duan ) + * + * Copyright 2013 Freescale Semiconductor, Inc. + * Copyright 2017, 2020-2025 NXP + * Copyright 2025, Linaro Ltd + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +/* SAR ADC registers. */ +#define NXP_SAR_ADC_CDR(__base, __channel) (((__base) + 0x100) + ((__channel) * 0x4)) + +#define NXP_SAR_ADC_CDR_CDATA_MASK GENMASK(11, 0) +#define NXP_SAR_ADC_CDR_VALID BIT(19) + +/* Main Configuration Register */ +#define NXP_SAR_ADC_MCR(__base) ((__base) + 0x00) + +#define NXP_SAR_ADC_MCR_PWDN BIT(0) +#define NXP_SAR_ADC_MCR_ACKO BIT(5) +#define NXP_SAR_ADC_MCR_ADCLKSEL BIT(8) +#define NXP_SAR_ADC_MCR_TSAMP_MASK GENMASK(10, 9) +#define NXP_SAR_ADC_MCR_NRSMPL_MASK GENMASK(12, 11) +#define NXP_SAR_ADC_MCR_AVGEN BIT(13) +#define NXP_SAR_ADC_MCR_CALSTART BIT(14) +#define NXP_SAR_ADC_MCR_NSTART BIT(24) +#define NXP_SAR_ADC_MCR_MODE BIT(29) +#define NXP_SAR_ADC_MCR_OWREN BIT(31) + +/* Main Status Register */ +#define NXP_SAR_ADC_MSR(__base) ((__base) + 0x04) + +#define NXP_SAR_ADC_MSR_CALBUSY BIT(29) +#define NXP_SAR_ADC_MSR_CALFAIL BIT(30) + +/* Interrupt Status Register */ +#define NXP_SAR_ADC_ISR(__base) ((__base) + 0x10) + +#define NXP_SAR_ADC_ISR_ECH BIT(0) + +/* Channel Pending Register */ +#define NXP_SAR_ADC_CEOCFR0(__base) ((__base) + 0x14) +#define NXP_SAR_ADC_CEOCFR1(__base) ((__base) + 0x18) + +#define NXP_SAR_ADC_EOC_CH(c) BIT(c) + +/* Interrupt Mask Register */ +#define NXP_SAR_ADC_IMR(__base) ((__base) + 0x20) + +/* Channel Interrupt Mask Register */ +#define NXP_SAR_ADC_CIMR0(__base) ((__base) + 0x24) +#define NXP_SAR_ADC_CIMR1(__base) ((__base) + 0x28) + +/* DMA Setting Register */ +#define NXP_SAR_ADC_DMAE(__base) ((__base) + 0x40) + +#define NXP_SAR_ADC_DMAE_DMAEN BIT(0) +#define NXP_SAR_ADC_DMAE_DCLR BIT(1) + +/* DMA Control register */ +#define NXP_SAR_ADC_DMAR0(__base) ((__base) + 0x44) +#define NXP_SAR_ADC_DMAR1(__base) ((__base) + 0x48) + +/* Conversion Timing Register */ +#define NXP_SAR_ADC_CTR0(__base) ((__base) + 0x94) +#define NXP_SAR_ADC_CTR1(__base) ((__base) + 0x98) + +#define NXP_SAR_ADC_CTR_INPSAMP_MIN 0x08 +#define NXP_SAR_ADC_CTR_INPSAMP_MAX 0xff + +/* Normal Conversion Mask Register */ +#define NXP_SAR_ADC_NCMR0(__base) ((__base) + 0xa4) +#define NXP_SAR_ADC_NCMR1(__base) ((__base) + 0xa8) + +/* Normal Conversion Mask Register field define */ +#define NXP_SAR_ADC_CH_MASK GENMASK(7, 0) + +/* Other field define */ +#define NXP_SAR_ADC_CONV_TIMEOUT (msecs_to_jiffies(100)) +#define NXP_SAR_ADC_CAL_TIMEOUT_US (100 * USEC_PER_MSEC) +#define NXP_SAR_ADC_WAIT_US (2 * USEC_PER_MSEC) +#define NXP_SAR_ADC_RESOLUTION 12 + +/* Duration of conversion phases */ +#define NXP_SAR_ADC_TPT 2 +#define NXP_SAR_ADC_DP 2 +#define NXP_SAR_ADC_CT ((NXP_SAR_ADC_RESOLUTION + 2) * 4) +#define NXP_SAR_ADC_CONV_TIME (NXP_SAR_ADC_TPT + NXP_SAR_ADC_CT + NXP_SAR_ADC_DP) + +#define NXP_SAR_ADC_NR_CHANNELS 8 + +#define NXP_PAGE_SIZE SZ_4K +#define NXP_SAR_ADC_DMA_SAMPLE_SZ DMA_SLAVE_BUSWIDTH_4_BYTES +#define NXP_SAR_ADC_DMA_BUFF_SZ (NXP_PAGE_SIZE * NXP_SAR_ADC_DMA_SAMPLE_SZ) +#define NXP_SAR_ADC_DMA_SAMPLE_CNT (NXP_SAR_ADC_DMA_BUFF_SZ / NXP_SAR_ADC_DMA_SAMPLE_SZ) + +struct nxp_sar_adc { + void __iomem *regs; + phys_addr_t regs_phys; + u8 current_channel; + u8 channels_used; + u16 value; + u32 vref_mV; + + /* Save and restore context. */ + u32 inpsamp; + u32 pwdn; + + struct clk *clk; + struct dma_chan *dma_chan; + struct completion completion; + struct circ_buf dma_buf; + + dma_addr_t rx_dma_buf; + dma_cookie_t cookie; + + /* Protect circular buffers access. */ + spinlock_t lock; + + /* Array of enabled channels. */ + u16 buffered_chan[NXP_SAR_ADC_NR_CHANNELS]; + + /* Buffer to be filled by the DMA. */ + IIO_DECLARE_BUFFER_WITH_TS(u16, buffer, NXP_SAR_ADC_NR_CHANNELS); +}; + +struct nxp_sar_adc_data { + u32 vref_mV; + const char *model; +}; + +#define ADC_CHAN(_idx, _chan_type) { \ + .type = (_chan_type), \ + .indexed = 1, \ + .channel = (_idx), \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | \ + BIT(IIO_CHAN_INFO_SAMP_FREQ), \ + .scan_index = (_idx), \ + .scan_type = { \ + .sign = 'u', \ + .realbits = 12, \ + .storagebits = 16, \ + }, \ +} + +static const struct iio_chan_spec nxp_sar_adc_iio_channels[] = { + ADC_CHAN(0, IIO_VOLTAGE), + ADC_CHAN(1, IIO_VOLTAGE), + ADC_CHAN(2, IIO_VOLTAGE), + ADC_CHAN(3, IIO_VOLTAGE), + ADC_CHAN(4, IIO_VOLTAGE), + ADC_CHAN(5, IIO_VOLTAGE), + ADC_CHAN(6, IIO_VOLTAGE), + ADC_CHAN(7, IIO_VOLTAGE), + /* + * The NXP SAR ADC documentation marks the channels 8 to 31 as + * "Reserved". Reflect the same in the driver in case new ADC + * variants comes with more channels. + */ + IIO_CHAN_SOFT_TIMESTAMP(32), +}; + +static void nxp_sar_adc_irq_cfg(struct nxp_sar_adc *info, bool enable) +{ + if (enable) + writel(NXP_SAR_ADC_ISR_ECH, NXP_SAR_ADC_IMR(info->regs)); + else + writel(0, NXP_SAR_ADC_IMR(info->regs)); +} + +static void nxp_sar_adc_wait_for(struct nxp_sar_adc *info, u64 cycles) +{ + u64 rate; + + rate = clk_get_rate(info->clk); + if (rate) + ndelay(div64_u64(NSEC_PER_SEC * cycles, rate)); +} + +static bool nxp_sar_adc_set_enabled(struct nxp_sar_adc *info, bool enable) +{ + u32 mcr; + bool pwdn; + + mcr = readl(NXP_SAR_ADC_MCR(info->regs)); + + /* + * Get the current state and return it later. This is used for + * suspend/resume to get the power state + */ + pwdn = FIELD_GET(NXP_SAR_ADC_MCR_PWDN, mcr); + + /* When the enabled flag is not set, we set the power down bit */ + FIELD_MODIFY(NXP_SAR_ADC_MCR_PWDN, &mcr, !enable); + + writel(mcr, NXP_SAR_ADC_MCR(info->regs)); + + /* + * Ensure there are at least three cycles between the + * configuration of NCMR and the setting of NSTART. + */ + if (enable) + nxp_sar_adc_wait_for(info, 3); + + return pwdn; +} + +static inline bool nxp_sar_adc_enable(struct nxp_sar_adc *info) +{ + return nxp_sar_adc_set_enabled(info, true); +} + +static inline bool nxp_sar_adc_disable(struct nxp_sar_adc *info) +{ + return nxp_sar_adc_set_enabled(info, false); +} + +static inline void nxp_sar_adc_calibration_start(void __iomem *base) +{ + u32 mcr = readl(NXP_SAR_ADC_MCR(base)); + + FIELD_MODIFY(NXP_SAR_ADC_MCR_CALSTART, &mcr, 0x1); + + writel(mcr, NXP_SAR_ADC_MCR(base)); +} + +static inline int nxp_sar_adc_calibration_wait(void __iomem *base) +{ + u32 msr; + int ret; + + ret = readl_poll_timeout(NXP_SAR_ADC_MSR(base), msr, + !FIELD_GET(NXP_SAR_ADC_MSR_CALBUSY, msr), + NXP_SAR_ADC_WAIT_US, + NXP_SAR_ADC_CAL_TIMEOUT_US); + if (ret) + return ret; + + if (FIELD_GET(NXP_SAR_ADC_MSR_CALFAIL, msr)) { + /* + * If the calibration fails, the status register bit must be + * cleared. + */ + FIELD_MODIFY(NXP_SAR_ADC_MSR_CALFAIL, &msr, 0x0); + writel(msr, NXP_SAR_ADC_MSR(base)); + + return -EAGAIN; + } + + return 0; +} + +static int nxp_sar_adc_calibration(struct nxp_sar_adc *info) +{ + int ret; + + /* Calibration works only if the ADC is powered up. */ + nxp_sar_adc_enable(info); + + /* The calibration operation starts. */ + nxp_sar_adc_calibration_start(info->regs); + + ret = nxp_sar_adc_calibration_wait(info->regs); + + /* + * Calibration works only if the ADC is powered up. However + * the calibration is called from the probe function where the + * iio is not enabled, so we disable after the calibration. + */ + nxp_sar_adc_disable(info); + + return ret; +} + +static void nxp_sar_adc_conversion_timing_set(struct nxp_sar_adc *info, u32 inpsamp) +{ + inpsamp = clamp(inpsamp, NXP_SAR_ADC_CTR_INPSAMP_MIN, NXP_SAR_ADC_CTR_INPSAMP_MAX); + + writel(inpsamp, NXP_SAR_ADC_CTR0(info->regs)); +} + +static u32 nxp_sar_adc_conversion_timing_get(struct nxp_sar_adc *info) +{ + return readl(NXP_SAR_ADC_CTR0(info->regs)); +} + +static void nxp_sar_adc_read_notify(struct nxp_sar_adc *info) +{ + writel(NXP_SAR_ADC_CH_MASK, NXP_SAR_ADC_CEOCFR0(info->regs)); + writel(NXP_SAR_ADC_CH_MASK, NXP_SAR_ADC_CEOCFR1(info->regs)); +} + +static int nxp_sar_adc_read_data(struct nxp_sar_adc *info, unsigned int chan) +{ + u32 ceocfr, cdr; + + ceocfr = readl(NXP_SAR_ADC_CEOCFR0(info->regs)); + + if (!field_get(NXP_SAR_ADC_EOC_CH(chan), ceocfr)) + return -EIO; + + cdr = readl(NXP_SAR_ADC_CDR(info->regs, chan)); + if (!(FIELD_GET(NXP_SAR_ADC_CDR_VALID, cdr))) + return -EIO; + + return FIELD_GET(NXP_SAR_ADC_CDR_CDATA_MASK, cdr); +} + +static void nxp_sar_adc_isr_buffer(struct iio_dev *indio_dev) +{ + struct nxp_sar_adc *info = iio_priv(indio_dev); + unsigned int i; + int ret; + + for (i = 0; i < info->channels_used; i++) { + ret = nxp_sar_adc_read_data(info, info->buffered_chan[i]); + if (ret < 0) { + nxp_sar_adc_read_notify(info); + iio_trigger_notify_done(indio_dev->trig); + return; + } + + info->buffer[i] = ret; + } + + nxp_sar_adc_read_notify(info); + + iio_push_to_buffers_with_ts(indio_dev, info->buffer, sizeof(info->buffer), + iio_get_time_ns(indio_dev)); + + iio_trigger_notify_done(indio_dev->trig); +} + +static void nxp_sar_adc_isr_read_raw(struct iio_dev *indio_dev) +{ + struct nxp_sar_adc *info = iio_priv(indio_dev); + int ret; + + ret = nxp_sar_adc_read_data(info, info->current_channel); + nxp_sar_adc_read_notify(info); + if (ret < 0) + return; + + info->value = ret; + complete(&info->completion); +} + +static irqreturn_t nxp_sar_adc_isr(int irq, void *dev_id) +{ + struct iio_dev *indio_dev = dev_id; + struct nxp_sar_adc *info = iio_priv(indio_dev); + int isr; + + isr = readl(NXP_SAR_ADC_ISR(info->regs)); + if (!(FIELD_GET(NXP_SAR_ADC_ISR_ECH, isr))) + return IRQ_NONE; + + if (iio_buffer_enabled(indio_dev)) + nxp_sar_adc_isr_buffer(indio_dev); + else + nxp_sar_adc_isr_read_raw(indio_dev); + + writel(NXP_SAR_ADC_ISR_ECH, NXP_SAR_ADC_ISR(info->regs)); + + return IRQ_HANDLED; +} + +static void nxp_sar_adc_channels_disable(struct nxp_sar_adc *info, u32 mask) +{ + u32 ncmr, cimr; + + ncmr = readl(NXP_SAR_ADC_NCMR0(info->regs)); + cimr = readl(NXP_SAR_ADC_CIMR0(info->regs)); + + /* FIELD_MODIFY() can not be used because the mask is not constant */ + ncmr &= ~mask; + cimr &= ~mask; + + writel(ncmr, NXP_SAR_ADC_NCMR0(info->regs)); + writel(cimr, NXP_SAR_ADC_CIMR0(info->regs)); +} + +static void nxp_sar_adc_channels_enable(struct nxp_sar_adc *info, u32 mask) +{ + u32 ncmr, cimr; + + ncmr = readl(NXP_SAR_ADC_NCMR0(info->regs)); + cimr = readl(NXP_SAR_ADC_CIMR0(info->regs)); + + ncmr |= mask; + cimr |= mask; + + writel(ncmr, NXP_SAR_ADC_NCMR0(info->regs)); + writel(cimr, NXP_SAR_ADC_CIMR0(info->regs)); +} + +static void nxp_sar_adc_dma_channels_enable(struct nxp_sar_adc *info, u32 mask) +{ + u32 dmar; + + dmar = readl(NXP_SAR_ADC_DMAR0(info->regs)); + + dmar |= mask; + + writel(dmar, NXP_SAR_ADC_DMAR0(info->regs)); +} + +static void nxp_sar_adc_dma_channels_disable(struct nxp_sar_adc *info, u32 mask) +{ + u32 dmar; + + dmar = readl(NXP_SAR_ADC_DMAR0(info->regs)); + + dmar &= ~mask; + + writel(dmar, NXP_SAR_ADC_DMAR0(info->regs)); +} + +static void nxp_sar_adc_dma_cfg(struct nxp_sar_adc *info, bool enable) +{ + u32 dmae; + + dmae = readl(NXP_SAR_ADC_DMAE(info->regs)); + + FIELD_MODIFY(NXP_SAR_ADC_DMAE_DMAEN, &dmae, enable); + + writel(dmae, NXP_SAR_ADC_DMAE(info->regs)); +} + +static void nxp_sar_adc_stop_conversion(struct nxp_sar_adc *info) +{ + u32 mcr; + + mcr = readl(NXP_SAR_ADC_MCR(info->regs)); + + FIELD_MODIFY(NXP_SAR_ADC_MCR_NSTART, &mcr, 0x0); + + writel(mcr, NXP_SAR_ADC_MCR(info->regs)); + + /* + * On disable, we have to wait for the transaction to finish. + * ADC does not abort the transaction if a chain conversion is + * in progress. Wait for the worst case scenario - 80 ADC clk + * cycles. The clock rate is 80MHz, this routine is called + * only when the capture finishes. The delay will be very + * short, usec-ish, which is acceptable in the atomic context. + */ + nxp_sar_adc_wait_for(info, 80); +} + +static int nxp_sar_adc_start_conversion(struct nxp_sar_adc *info, bool raw) +{ + u32 mcr; + + mcr = readl(NXP_SAR_ADC_MCR(info->regs)); + + FIELD_MODIFY(NXP_SAR_ADC_MCR_NSTART, &mcr, 0x1); + FIELD_MODIFY(NXP_SAR_ADC_MCR_MODE, &mcr, raw ? 0 : 1); + + writel(mcr, NXP_SAR_ADC_MCR(info->regs)); + + return 0; +} + +static int nxp_sar_adc_read_channel(struct nxp_sar_adc *info, int channel) +{ + int ret; + + info->current_channel = channel; + nxp_sar_adc_channels_enable(info, BIT(channel)); + nxp_sar_adc_irq_cfg(info, true); + nxp_sar_adc_enable(info); + + reinit_completion(&info->completion); + ret = nxp_sar_adc_start_conversion(info, true); + if (ret < 0) + goto out_disable; + + if (!wait_for_completion_interruptible_timeout(&info->completion, + NXP_SAR_ADC_CONV_TIMEOUT)) + ret = -ETIMEDOUT; + + nxp_sar_adc_stop_conversion(info); + +out_disable: + nxp_sar_adc_channels_disable(info, BIT(channel)); + nxp_sar_adc_irq_cfg(info, false); + nxp_sar_adc_disable(info); + + return ret; +} + +static int nxp_sar_adc_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int *val, + int *val2, long mask) +{ + struct nxp_sar_adc *info = iio_priv(indio_dev); + u32 inpsamp; + int ret; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; + + ret = nxp_sar_adc_read_channel(info, chan->channel); + + iio_device_release_direct(indio_dev); + + if (ret) + return ret; + + *val = info->value; + return IIO_VAL_INT; + + case IIO_CHAN_INFO_SCALE: + *val = info->vref_mV; + *val2 = NXP_SAR_ADC_RESOLUTION; + return IIO_VAL_FRACTIONAL_LOG2; + + case IIO_CHAN_INFO_SAMP_FREQ: + inpsamp = nxp_sar_adc_conversion_timing_get(info); + *val = clk_get_rate(info->clk) / (inpsamp + NXP_SAR_ADC_CONV_TIME); + return IIO_VAL_INT; + + default: + return -EINVAL; + } +} + +static int nxp_sar_adc_write_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, + int val, int val2, long mask) +{ + struct nxp_sar_adc *info = iio_priv(indio_dev); + u32 inpsamp; + + switch (mask) { + case IIO_CHAN_INFO_SAMP_FREQ: + if (val <= 0) + return -EINVAL; + + /* + * Configures the sample period duration in terms of the SAR + * controller clock. The minimum acceptable value is 8. + * Configuring it to a value lower than 8 sets the sample period + * to 8 cycles. We read the clock value and divide by the + * sampling timing which gives us the number of cycles expected. + * The value is 8-bit wide, consequently the max value is 0xFF. + */ + inpsamp = clk_get_rate(info->clk) / val; + if (inpsamp < NXP_SAR_ADC_CONV_TIME) + return -EINVAL; + + inpsamp -= NXP_SAR_ADC_CONV_TIME; + nxp_sar_adc_conversion_timing_set(info, inpsamp); + return 0; + + default: + return -EINVAL; + } +} + +static void nxp_sar_adc_dma_cb(void *data) +{ + struct iio_dev *indio_dev = data; + struct nxp_sar_adc *info = iio_priv(indio_dev); + struct dma_tx_state state; + struct circ_buf *dma_buf; + struct device *dev_dma; + u32 *dma_samples; + s64 timestamp; + int idx, ret; + + guard(spinlock_irqsave)(&info->lock); + + dma_buf = &info->dma_buf; + dma_samples = (u32 *)dma_buf->buf; + dev_dma = info->dma_chan->device->dev; + + /* + * DMA in some corner cases might have already be charged for + * the next transfer. Potentially there can be a race where + * the residue changes while the dma engine updates the + * buffer. That could be handled by using the + * callback_result() instead of callback() because the residue + * will be passed as a parameter to the function. However this + * new callback is pretty new and the backend does not update + * the residue. So let's stick to the version other drivers do + * which has proven running well in production since several + * years. + */ + dmaengine_tx_status(info->dma_chan, info->cookie, &state); + + dma_sync_single_for_cpu(dev_dma, info->rx_dma_buf, + NXP_SAR_ADC_DMA_BUFF_SZ, DMA_FROM_DEVICE); + + /* Current head position. */ + dma_buf->head = (NXP_SAR_ADC_DMA_BUFF_SZ - state.residue) / + NXP_SAR_ADC_DMA_SAMPLE_SZ; + + /* If everything was transferred, avoid an off by one error. */ + if (!state.residue) + dma_buf->head--; + + /* Something went wrong and nothing transferred. */ + if (state.residue != NXP_SAR_ADC_DMA_BUFF_SZ) { + /* Make sure that head is multiple of info->channels_used. */ + dma_buf->head -= dma_buf->head % info->channels_used; + + /* + * dma_buf->tail != dma_buf->head condition will become false + * because dma_buf->tail will be incremented with 1. + */ + while (dma_buf->tail != dma_buf->head) { + idx = dma_buf->tail % info->channels_used; + info->buffer[idx] = dma_samples[dma_buf->tail]; + dma_buf->tail = (dma_buf->tail + 1) % NXP_SAR_ADC_DMA_SAMPLE_CNT; + if (idx != info->channels_used - 1) + continue; + + /* + * iio_push_to_buffers_with_ts() should not be + * called with dma_samples as parameter. The samples + * will be smashed if timestamp is enabled. + */ + timestamp = iio_get_time_ns(indio_dev); + ret = iio_push_to_buffers_with_ts(indio_dev, info->buffer, + sizeof(info->buffer), + timestamp); + if (ret < 0 && ret != -EBUSY) + dev_err_ratelimited(&indio_dev->dev, + "failed to push iio buffer: %d", + ret); + } + + dma_buf->tail = dma_buf->head; + } + + dma_sync_single_for_device(dev_dma, info->rx_dma_buf, + NXP_SAR_ADC_DMA_BUFF_SZ, DMA_FROM_DEVICE); +} + +static int nxp_sar_adc_start_cyclic_dma(struct iio_dev *indio_dev) +{ + struct nxp_sar_adc *info = iio_priv(indio_dev); + struct dma_slave_config config = { }; + struct dma_async_tx_descriptor *desc; + int ret; + + info->dma_buf.head = 0; + info->dma_buf.tail = 0; + + config.direction = DMA_DEV_TO_MEM; + config.src_addr_width = NXP_SAR_ADC_DMA_SAMPLE_SZ; + config.src_addr = NXP_SAR_ADC_CDR(info->regs_phys, info->buffered_chan[0]); + config.src_port_window_size = info->channels_used; + config.src_maxburst = info->channels_used; + ret = dmaengine_slave_config(info->dma_chan, &config); + if (ret < 0) + return ret; + + desc = dmaengine_prep_dma_cyclic(info->dma_chan, + info->rx_dma_buf, + NXP_SAR_ADC_DMA_BUFF_SZ, + NXP_SAR_ADC_DMA_BUFF_SZ / 2, + DMA_DEV_TO_MEM, DMA_PREP_INTERRUPT); + if (!desc) + return -EINVAL; + + desc->callback = nxp_sar_adc_dma_cb; + desc->callback_param = indio_dev; + info->cookie = dmaengine_submit(desc); + ret = dma_submit_error(info->cookie); + if (ret) { + dmaengine_terminate_async(info->dma_chan); + return ret; + } + + dma_async_issue_pending(info->dma_chan); + + return 0; +} + +static void nxp_sar_adc_buffer_software_do_predisable(struct iio_dev *indio_dev) +{ + struct nxp_sar_adc *info = iio_priv(indio_dev); + + /* + * The ADC DMAEN bit should be cleared before DMA transaction + * is canceled. + */ + nxp_sar_adc_stop_conversion(info); + dmaengine_terminate_sync(info->dma_chan); + nxp_sar_adc_dma_cfg(info, false); + nxp_sar_adc_dma_channels_disable(info, *indio_dev->active_scan_mask); + + dma_release_channel(info->dma_chan); +} + +static int nxp_sar_adc_buffer_software_do_postenable(struct iio_dev *indio_dev) +{ + struct nxp_sar_adc *info = iio_priv(indio_dev); + int ret; + + info->dma_chan = dma_request_chan(indio_dev->dev.parent, "rx"); + if (IS_ERR(info->dma_chan)) + return PTR_ERR(info->dma_chan); + + nxp_sar_adc_dma_channels_enable(info, *indio_dev->active_scan_mask); + + nxp_sar_adc_dma_cfg(info, true); + + ret = nxp_sar_adc_start_cyclic_dma(indio_dev); + if (ret) + goto out_dma_channels_disable; + + ret = nxp_sar_adc_start_conversion(info, false); + if (ret) + goto out_stop_cyclic_dma; + + return 0; + +out_stop_cyclic_dma: + dmaengine_terminate_sync(info->dma_chan); + +out_dma_channels_disable: + nxp_sar_adc_dma_cfg(info, false); + nxp_sar_adc_dma_channels_disable(info, *indio_dev->active_scan_mask); + dma_release_channel(info->dma_chan); + + return ret; +} + +static void nxp_sar_adc_buffer_trigger_do_predisable(struct iio_dev *indio_dev) +{ + struct nxp_sar_adc *info = iio_priv(indio_dev); + + nxp_sar_adc_irq_cfg(info, false); +} + +static int nxp_sar_adc_buffer_trigger_do_postenable(struct iio_dev *indio_dev) +{ + struct nxp_sar_adc *info = iio_priv(indio_dev); + + nxp_sar_adc_irq_cfg(info, true); + + return 0; +} + +static int nxp_sar_adc_buffer_postenable(struct iio_dev *indio_dev) +{ + struct nxp_sar_adc *info = iio_priv(indio_dev); + int current_mode = iio_device_get_current_mode(indio_dev); + unsigned long channel; + int ret; + + info->channels_used = 0; + + /* + * The SAR-ADC has two groups of channels. + * + * - Group #0: + * * bit 0-7 : channel 0 -> channel 7 + * * bit 8-31 : reserved + * + * - Group #32: + * * bit 0-7 : Internal + * * bit 8-31 : reserved + * + * The 8 channels from group #0 are used in this driver for + * ADC as described when declaring the IIO device and the + * mapping is the same. That means the active_scan_mask can be + * used directly to write the channel interrupt mask. + */ + nxp_sar_adc_channels_enable(info, *indio_dev->active_scan_mask); + + for_each_set_bit(channel, indio_dev->active_scan_mask, NXP_SAR_ADC_NR_CHANNELS) + info->buffered_chan[info->channels_used++] = channel; + + nxp_sar_adc_enable(info); + + if (current_mode == INDIO_BUFFER_SOFTWARE) + ret = nxp_sar_adc_buffer_software_do_postenable(indio_dev); + else + ret = nxp_sar_adc_buffer_trigger_do_postenable(indio_dev); + if (ret) + goto out_postenable; + + return 0; + +out_postenable: + nxp_sar_adc_disable(info); + nxp_sar_adc_channels_disable(info, *indio_dev->active_scan_mask); + + return ret; +} + +static int nxp_sar_adc_buffer_predisable(struct iio_dev *indio_dev) +{ + struct nxp_sar_adc *info = iio_priv(indio_dev); + int currentmode = iio_device_get_current_mode(indio_dev); + + if (currentmode == INDIO_BUFFER_SOFTWARE) + nxp_sar_adc_buffer_software_do_predisable(indio_dev); + else + nxp_sar_adc_buffer_trigger_do_predisable(indio_dev); + + nxp_sar_adc_disable(info); + + nxp_sar_adc_channels_disable(info, *indio_dev->active_scan_mask); + + return 0; +} + +static irqreturn_t nxp_sar_adc_trigger_handler(int irq, void *p) +{ + struct iio_poll_func *pf = p; + struct iio_dev *indio_dev = pf->indio_dev; + struct nxp_sar_adc *info = iio_priv(indio_dev); + int ret; + + ret = nxp_sar_adc_start_conversion(info, true); + if (ret < 0) + dev_dbg(&indio_dev->dev, "Failed to start conversion\n"); + + return IRQ_HANDLED; +} + +static const struct iio_buffer_setup_ops iio_triggered_buffer_setup_ops = { + .postenable = nxp_sar_adc_buffer_postenable, + .predisable = nxp_sar_adc_buffer_predisable, +}; + +static const struct iio_info nxp_sar_adc_iio_info = { + .read_raw = nxp_sar_adc_read_raw, + .write_raw = nxp_sar_adc_write_raw, +}; + +static int nxp_sar_adc_dma_probe(struct device *dev, struct nxp_sar_adc *info) +{ + u8 *rx_buf; + + rx_buf = dmam_alloc_coherent(dev, NXP_SAR_ADC_DMA_BUFF_SZ, + &info->rx_dma_buf, GFP_KERNEL); + if (!rx_buf) + return -ENOMEM; + + info->dma_buf.buf = rx_buf; + + return 0; +} + +/* + * The documentation describes the reset values for the registers. + * However some registers do not have these values after a reset. It + * is not a desirable situation. In some other SoC family + * documentation NXP recommends not assuming the default values are + * set and to initialize the registers conforming to the documentation + * reset information to prevent this situation. Assume the same rule + * applies here as there is a discrepancy between what is read from + * the registers at reset time and the documentation. + */ +static void nxp_sar_adc_set_default_values(struct nxp_sar_adc *info) +{ + writel(0x00003901, NXP_SAR_ADC_MCR(info->regs)); + writel(0x00000001, NXP_SAR_ADC_MSR(info->regs)); + writel(0x00000014, NXP_SAR_ADC_CTR0(info->regs)); + writel(0x00000014, NXP_SAR_ADC_CTR1(info->regs)); + writel(0x00000000, NXP_SAR_ADC_CIMR0(info->regs)); + writel(0x00000000, NXP_SAR_ADC_CIMR1(info->regs)); + writel(0x00000000, NXP_SAR_ADC_NCMR0(info->regs)); + writel(0x00000000, NXP_SAR_ADC_NCMR1(info->regs)); +} + +static int nxp_sar_adc_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + const struct nxp_sar_adc_data *data = device_get_match_data(dev); + struct nxp_sar_adc *info; + struct iio_dev *indio_dev; + struct resource *mem; + int irq, ret; + + indio_dev = devm_iio_device_alloc(dev, sizeof(*info)); + if (!indio_dev) + return -ENOMEM; + + info = iio_priv(indio_dev); + info->vref_mV = data->vref_mV; + spin_lock_init(&info->lock); + info->regs = devm_platform_get_and_ioremap_resource(pdev, 0, &mem); + if (IS_ERR(info->regs)) + return dev_err_probe(dev, PTR_ERR(info->regs), + "Failed to get and remap resource"); + + info->regs_phys = mem->start; + + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return irq; + + ret = devm_request_irq(dev, irq, nxp_sar_adc_isr, 0, dev_name(dev), + indio_dev); + if (ret < 0) + return ret; + + info->clk = devm_clk_get_enabled(dev, NULL); + if (IS_ERR(info->clk)) + return dev_err_probe(dev, PTR_ERR(info->clk), + "Failed to get the clock\n"); + + platform_set_drvdata(pdev, indio_dev); + + init_completion(&info->completion); + + indio_dev->name = data->model; + indio_dev->info = &nxp_sar_adc_iio_info; + indio_dev->modes = INDIO_DIRECT_MODE | INDIO_BUFFER_SOFTWARE; + indio_dev->channels = nxp_sar_adc_iio_channels; + indio_dev->num_channels = ARRAY_SIZE(nxp_sar_adc_iio_channels); + + nxp_sar_adc_set_default_values(info); + + ret = nxp_sar_adc_calibration(info); + if (ret) + dev_err_probe(dev, ret, "Calibration failed\n"); + + ret = nxp_sar_adc_dma_probe(dev, info); + if (ret) + return dev_err_probe(dev, ret, "Failed to initialize the DMA\n"); + + ret = devm_iio_triggered_buffer_setup(dev, indio_dev, + &iio_pollfunc_store_time, + &nxp_sar_adc_trigger_handler, + &iio_triggered_buffer_setup_ops); + if (ret < 0) + return dev_err_probe(dev, ret, "Couldn't initialise the buffer\n"); + + ret = devm_iio_device_register(dev, indio_dev); + if (ret) + return dev_err_probe(dev, ret, "Couldn't register the device\n"); + + return 0; +} + +static int nxp_sar_adc_suspend(struct device *dev) +{ + struct nxp_sar_adc *info = iio_priv(dev_get_drvdata(dev)); + + info->pwdn = nxp_sar_adc_disable(info); + info->inpsamp = nxp_sar_adc_conversion_timing_get(info); + + clk_disable_unprepare(info->clk); + + return 0; +} + +static int nxp_sar_adc_resume(struct device *dev) +{ + struct nxp_sar_adc *info = iio_priv(dev_get_drvdata(dev)); + int ret; + + ret = clk_prepare_enable(info->clk); + if (ret) + return ret; + + nxp_sar_adc_conversion_timing_set(info, info->inpsamp); + + if (!info->pwdn) + nxp_sar_adc_enable(info); + + return 0; +} + +static DEFINE_SIMPLE_DEV_PM_OPS(nxp_sar_adc_pm_ops, nxp_sar_adc_suspend, + nxp_sar_adc_resume); + +static const struct nxp_sar_adc_data s32g2_sar_adc_data = { + .vref_mV = 1800, + .model = "s32g2-sar-adc", +}; + +static const struct of_device_id nxp_sar_adc_match[] = { + { .compatible = "nxp,s32g2-sar-adc", .data = &s32g2_sar_adc_data }, + { } +}; +MODULE_DEVICE_TABLE(of, nxp_sar_adc_match); + +static struct platform_driver nxp_sar_adc_driver = { + .probe = nxp_sar_adc_probe, + .driver = { + .name = "nxp-sar-adc", + .of_match_table = nxp_sar_adc_match, + .pm = pm_sleep_ptr(&nxp_sar_adc_pm_ops), + }, +}; +module_platform_driver(nxp_sar_adc_driver); + +MODULE_AUTHOR("NXP"); +MODULE_DESCRIPTION("NXP SAR-ADC driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/iio/adc/pac1921.c b/drivers/iio/adc/pac1921.c new file mode 100644 index 000000000..f0bab9446 --- /dev/null +++ b/drivers/iio/adc/pac1921.c @@ -0,0 +1,1345 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * IIO driver for PAC1921 High-Side Power/Current Monitor + * + * Copyright (C) 2024 Matteo Martelli + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* pac1921 registers */ +#define PAC1921_REG_GAIN_CFG 0x00 +#define PAC1921_REG_INT_CFG 0x01 +#define PAC1921_REG_CONTROL 0x02 +#define PAC1921_REG_VBUS 0x10 +#define PAC1921_REG_VSENSE 0x12 +#define PAC1921_REG_OVERFLOW_STS 0x1C +#define PAC1921_REG_VPOWER 0x1D + +/* pac1921 gain configuration bits */ +#define PAC1921_GAIN_DI_GAIN_MASK GENMASK(5, 3) +#define PAC1921_GAIN_DV_GAIN_MASK GENMASK(2, 0) + +/* pac1921 integration configuration bits */ +#define PAC1921_INT_CFG_SMPL_MASK GENMASK(7, 4) +#define PAC1921_INT_CFG_VSFEN BIT(3) +#define PAC1921_INT_CFG_VBFEN BIT(2) +#define PAC1921_INT_CFG_RIOV BIT(1) +#define PAC1921_INT_CFG_INTEN BIT(0) + +/* pac1921 control bits */ +#define PAC1921_CONTROL_MXSL_MASK GENMASK(7, 6) +enum pac1921_mxsl { + PAC1921_MXSL_VPOWER_PIN = 0, + PAC1921_MXSL_VSENSE_FREE_RUN = 1, + PAC1921_MXSL_VBUS_FREE_RUN = 2, + PAC1921_MXSL_VPOWER_FREE_RUN = 3, +}; +#define PAC1921_CONTROL_SLEEP BIT(2) + +/* pac1921 result registers mask and resolution */ +#define PAC1921_RES_MASK GENMASK(15, 6) +#define PAC1921_RES_RESOLUTION 1023 + +/* pac1921 overflow status bits */ +#define PAC1921_OVERFLOW_VSOV BIT(2) +#define PAC1921_OVERFLOW_VBOV BIT(1) +#define PAC1921_OVERFLOW_VPOV BIT(0) + +/* pac1921 constants */ +#define PAC1921_MAX_VSENSE_MV 100 +#define PAC1921_MAX_VBUS_V 32 +/* Time to first communication after power up (tINT_T) */ +#define PAC1921_POWERUP_TIME_MS 20 +/* Time from Sleep State to Start of Integration Period (tSLEEP_TO_INT) */ +#define PAC1921_SLEEP_TO_INT_TIME_US 86 + +/* pac1921 defaults */ +#define PAC1921_DEFAULT_DV_GAIN 0 /* 2^(value): 1x gain (HW default) */ +#define PAC1921_DEFAULT_DI_GAIN 0 /* 2^(value): 1x gain (HW default) */ +#define PAC1921_DEFAULT_NUM_SAMPLES 0 /* 2^(value): 1 sample (HW default) */ + +#define PAC1921_ACPI_GET_uOHMS_VALS 0 +#define PAC1921_ACPI_GET_LABEL 1 + +/* f7bb9932-86ee-4516-a236-7a7a742e55cb */ +static const guid_t pac1921_guid = + GUID_INIT(0xf7bb9932, 0x86ee, 0x4516, 0xa2, + 0x36, 0x7a, 0x7a, 0x74, 0x2e, 0x55, 0xcb); + +/* + * Pre-computed scale factors for BUS voltage + * format: IIO_VAL_INT_PLUS_NANO + * unit: mV + * + * Vbus scale (mV) = max_vbus (mV) / dv_gain / resolution + */ +static const int pac1921_vbus_scales[][2] = { + { 31, 280547409 }, /* dv_gain x1 */ + { 15, 640273704 }, /* dv_gain x2 */ + { 7, 820136852 }, /* dv_gain x4 */ + { 3, 910068426 }, /* dv_gain x8 */ + { 1, 955034213 }, /* dv_gain x16 */ + { 0, 977517106 }, /* dv_gain x32 */ +}; + +/* + * Pre-computed scales for SENSE voltage + * format: IIO_VAL_INT_PLUS_NANO + * unit: mV + * + * Vsense scale (mV) = max_vsense (mV) / di_gain / resolution + */ +static const int pac1921_vsense_scales[][2] = { + { 0, 97751710 }, /* di_gain x1 */ + { 0, 48875855 }, /* di_gain x2 */ + { 0, 24437927 }, /* di_gain x4 */ + { 0, 12218963 }, /* di_gain x8 */ + { 0, 6109481 }, /* di_gain x16 */ + { 0, 3054740 }, /* di_gain x32 */ + { 0, 1527370 }, /* di_gain x64 */ + { 0, 763685 }, /* di_gain x128 */ +}; + +/* + * Numbers of samples used to integrate measurements at the end of an + * integration period. + * + * Changing the number of samples affects the integration period: higher the + * number of samples, longer the integration period. + * + * These correspond to the oversampling ratios available exposed to userspace. + */ +static const int pac1921_int_num_samples[] = { + 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048 +}; + +/* + * The integration period depends on the configuration of number of integration + * samples, measurement resolution and post filters. The following array + * contains integration periods, in microsecs unit, based on table 4-5 from + * datasheet considering power integration mode, 14-Bit resolution and post + * filters on. Each index corresponds to a specific number of samples from 1 + * to 2048. + */ +static const unsigned int pac1921_int_periods_usecs[] = { + 2720, /* 1 sample */ + 4050, /* 2 samples */ + 6790, /* 4 samples */ + 12200, /* 8 samples */ + 23000, /* 16 samples */ + 46000, /* 32 samples */ + 92000, /* 64 samples */ + 184000, /* 128 samples */ + 368000, /* 256 samples */ + 736000, /* 512 samples */ + 1471000, /* 1024 samples */ + 2941000 /* 2048 samples */ +}; + +/* pac1921 regmap configuration */ +static const struct regmap_range pac1921_regmap_wr_ranges[] = { + regmap_reg_range(PAC1921_REG_GAIN_CFG, PAC1921_REG_CONTROL), +}; + +static const struct regmap_access_table pac1921_regmap_wr_table = { + .yes_ranges = pac1921_regmap_wr_ranges, + .n_yes_ranges = ARRAY_SIZE(pac1921_regmap_wr_ranges), +}; + +static const struct regmap_range pac1921_regmap_rd_ranges[] = { + regmap_reg_range(PAC1921_REG_GAIN_CFG, PAC1921_REG_CONTROL), + regmap_reg_range(PAC1921_REG_VBUS, PAC1921_REG_VPOWER + 1), +}; + +static const struct regmap_access_table pac1921_regmap_rd_table = { + .yes_ranges = pac1921_regmap_rd_ranges, + .n_yes_ranges = ARRAY_SIZE(pac1921_regmap_rd_ranges), +}; + +static const struct regmap_config pac1921_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .rd_table = &pac1921_regmap_rd_table, + .wr_table = &pac1921_regmap_wr_table, +}; + +enum pac1921_channels { + PAC1921_CHAN_VBUS = 0, + PAC1921_CHAN_VSENSE = 1, + PAC1921_CHAN_CURRENT = 2, + PAC1921_CHAN_POWER = 3, +}; +#define PAC1921_NUM_MEAS_CHANS 4 + +struct pac1921_priv { + struct i2c_client *client; + struct regmap *regmap; + struct regulator *vdd; + struct iio_info iio_info; + + /* + * Synchronize access to private members, and ensure atomicity of + * consecutive regmap operations. + */ + struct mutex lock; + + u32 rshunt_uohm; /* uOhm */ + u8 dv_gain; + u8 di_gain; + u8 n_samples; + u8 prev_ovf_flags; + u8 ovf_enabled_events; + + bool first_integr_started; + bool first_integr_done; + unsigned long integr_started_time_jiffies; + unsigned int integr_period_usecs; + + int current_scales[ARRAY_SIZE(pac1921_vsense_scales)][2]; + + struct { + u16 chan[PAC1921_NUM_MEAS_CHANS]; + aligned_s64 timestamp; + } scan; +}; + +/* + * Check if first integration after configuration update has completed. + * + * Must be called with lock held. + */ +static bool pac1921_data_ready(struct pac1921_priv *priv) +{ + if (!priv->first_integr_started) + return false; + + if (!priv->first_integr_done) { + unsigned long t_ready; + + /* + * Data valid after the device entered into integration state, + * considering worst case where the device was in sleep state, + * and completed the first integration period. + */ + t_ready = priv->integr_started_time_jiffies + + usecs_to_jiffies(PAC1921_SLEEP_TO_INT_TIME_US) + + usecs_to_jiffies(priv->integr_period_usecs); + + if (time_before(jiffies, t_ready)) + return false; + + priv->first_integr_done = true; + } + + return true; +} + +static inline void pac1921_calc_scale(int dividend, int divisor, int *val, + int *val2) +{ + s64 tmp; + + tmp = div_s64(dividend * (s64)NANO, divisor); + *val = div_s64_rem(tmp, NANO, val2); +} + +/* + * Fill the table of scale factors for current + * format: IIO_VAL_INT_PLUS_NANO + * unit: mA + * + * Vsense LSB (nV) = max_vsense (nV) * di_gain / resolution + * Current scale (mA) = Vsense LSB (nV) / shunt (uOhm) + * + * Must be called with held lock when updating after first initialization. + */ +static void pac1921_calc_current_scales(struct pac1921_priv *priv) +{ + for (unsigned int i = 0; i < ARRAY_SIZE(priv->current_scales); i++) { + int max = (PAC1921_MAX_VSENSE_MV * MICRO) >> i; + int vsense_lsb = DIV_ROUND_CLOSEST(max, PAC1921_RES_RESOLUTION); + + pac1921_calc_scale(vsense_lsb, priv->rshunt_uohm, + &priv->current_scales[i][0], + &priv->current_scales[i][1]); + } +} + +/* + * Check if overflow occurred and if so, push the corresponding events. + * + * Must be called with lock held. + */ +static int pac1921_check_push_overflow(struct iio_dev *indio_dev, s64 timestamp) +{ + struct pac1921_priv *priv = iio_priv(indio_dev); + unsigned int flags; + int ret; + + ret = regmap_read(priv->regmap, PAC1921_REG_OVERFLOW_STS, &flags); + if (ret) + return ret; + + if (flags & PAC1921_OVERFLOW_VBOV && + !(priv->prev_ovf_flags & PAC1921_OVERFLOW_VBOV) && + priv->ovf_enabled_events & PAC1921_OVERFLOW_VBOV) { + iio_push_event(indio_dev, + IIO_UNMOD_EVENT_CODE( + IIO_VOLTAGE, PAC1921_CHAN_VBUS, + IIO_EV_TYPE_THRESH, IIO_EV_DIR_RISING), + timestamp); + } + if (flags & PAC1921_OVERFLOW_VSOV && + !(priv->prev_ovf_flags & PAC1921_OVERFLOW_VSOV) && + priv->ovf_enabled_events & PAC1921_OVERFLOW_VSOV) { + iio_push_event(indio_dev, + IIO_UNMOD_EVENT_CODE( + IIO_VOLTAGE, PAC1921_CHAN_VSENSE, + IIO_EV_TYPE_THRESH, IIO_EV_DIR_RISING), + timestamp); + iio_push_event(indio_dev, + IIO_UNMOD_EVENT_CODE( + IIO_CURRENT, PAC1921_CHAN_CURRENT, + IIO_EV_TYPE_THRESH, IIO_EV_DIR_RISING), + timestamp); + } + if (flags & PAC1921_OVERFLOW_VPOV && + !(priv->prev_ovf_flags & PAC1921_OVERFLOW_VPOV) && + priv->ovf_enabled_events & PAC1921_OVERFLOW_VPOV) { + iio_push_event(indio_dev, + IIO_UNMOD_EVENT_CODE( + IIO_POWER, PAC1921_CHAN_POWER, + IIO_EV_TYPE_THRESH, IIO_EV_DIR_RISING), + timestamp); + } + + priv->prev_ovf_flags = flags; + + return 0; +} + +/* + * Read the value from a result register + * + * Result registers contain the most recent averaged values of Vbus, Vsense and + * Vpower. Each value is 10 bits wide and spread across two consecutive 8 bit + * registers, with 6 bit LSB zero padding. + */ +static int pac1921_read_res(struct pac1921_priv *priv, unsigned long reg, + u16 *val) +{ + int ret = regmap_bulk_read(priv->regmap, reg, val, sizeof(*val)); + if (ret) + return ret; + + *val = FIELD_GET(PAC1921_RES_MASK, get_unaligned_be16(val)); + + return 0; +} + +static int pac1921_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int *val, + int *val2, long mask) +{ + struct pac1921_priv *priv = iio_priv(indio_dev); + + guard(mutex)(&priv->lock); + + switch (mask) { + case IIO_CHAN_INFO_RAW: { + s64 ts; + u16 res_val; + int ret; + + if (!pac1921_data_ready(priv)) + return -EBUSY; + + ts = iio_get_time_ns(indio_dev); + + ret = pac1921_check_push_overflow(indio_dev, ts); + if (ret) + return ret; + + ret = pac1921_read_res(priv, chan->address, &res_val); + if (ret) + return ret; + + *val = res_val; + + return IIO_VAL_INT; + } + case IIO_CHAN_INFO_SCALE: + switch (chan->channel) { + case PAC1921_CHAN_VBUS: + *val = pac1921_vbus_scales[priv->dv_gain][0]; + *val2 = pac1921_vbus_scales[priv->dv_gain][1]; + return IIO_VAL_INT_PLUS_NANO; + + case PAC1921_CHAN_VSENSE: + *val = pac1921_vsense_scales[priv->di_gain][0]; + *val2 = pac1921_vsense_scales[priv->di_gain][1]; + return IIO_VAL_INT_PLUS_NANO; + + case PAC1921_CHAN_CURRENT: + *val = priv->current_scales[priv->di_gain][0]; + *val2 = priv->current_scales[priv->di_gain][1]; + return IIO_VAL_INT_PLUS_NANO; + + case PAC1921_CHAN_POWER: { + /* + * Power scale factor in mW: + * Current scale (mA) * max_vbus (V) / dv_gain + */ + + /* Get current scale based on di_gain */ + int *curr_scale = priv->current_scales[priv->di_gain]; + + /* Convert current_scale from INT_PLUS_NANO to INT */ + s64 tmp = curr_scale[0] * (s64)NANO + curr_scale[1]; + + /* Multiply by max_vbus (V) / dv_gain */ + tmp *= PAC1921_MAX_VBUS_V >> priv->dv_gain; + + /* Convert back to INT_PLUS_NANO */ + *val = div_s64_rem(tmp, NANO, val2); + + return IIO_VAL_INT_PLUS_NANO; + } + default: + return -EINVAL; + } + + case IIO_CHAN_INFO_OVERSAMPLING_RATIO: + *val = pac1921_int_num_samples[priv->n_samples]; + return IIO_VAL_INT; + + case IIO_CHAN_INFO_SAMP_FREQ: + /* + * The sampling frequency (Hz) is read-only and corresponds to + * how often the device provides integrated measurements into + * the result registers, thus it's 1/integration_period. + * The integration period depends on the number of integration + * samples, measurement resolution and post filters. + * + * 1/(integr_period_usecs/MICRO) = MICRO/integr_period_usecs + */ + *val = MICRO; + *val2 = priv->integr_period_usecs; + return IIO_VAL_FRACTIONAL; + + default: + return -EINVAL; + } +} + +static int pac1921_read_avail(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + const int **vals, int *type, int *length, + long mask) +{ + switch (mask) { + case IIO_CHAN_INFO_OVERSAMPLING_RATIO: + *type = IIO_VAL_INT; + *vals = pac1921_int_num_samples; + *length = ARRAY_SIZE(pac1921_int_num_samples); + return IIO_AVAIL_LIST; + default: + return -EINVAL; + } +} + +/* + * Perform configuration update sequence: set the device into read state, then + * write the config register and set the device back into integration state. + * Also reset integration start time and mark first integration to be yet + * completed. + * + * Must be called with lock held. + */ +static int pac1921_update_cfg_reg(struct pac1921_priv *priv, unsigned int reg, + unsigned int mask, unsigned int val) +{ + /* Enter READ state before configuration */ + int ret = regmap_update_bits(priv->regmap, PAC1921_REG_INT_CFG, + PAC1921_INT_CFG_INTEN, 0); + if (ret) + return ret; + + /* Update configuration value */ + ret = regmap_update_bits(priv->regmap, reg, mask, val); + if (ret) + return ret; + + /* Re-enable integration */ + ret = regmap_update_bits(priv->regmap, PAC1921_REG_INT_CFG, + PAC1921_INT_CFG_INTEN, PAC1921_INT_CFG_INTEN); + if (ret) + return ret; + + /* + * Reset integration started time and mark this integration period as + * the first one so that new measurements will be considered as valid + * only at the end of this integration period. + */ + priv->integr_started_time_jiffies = jiffies; + priv->first_integr_done = false; + + return 0; +} + +/* + * Retrieve the index of the given scale (represented by scale_val and + * scale_val2) from scales_tbl. The returned index (if found) is the log2 of + * the gain corresponding to the given scale. + * + * Must be called with lock held if the scales_tbl can change runtime (e.g. for + * the current scales table) + */ +static int pac1921_lookup_scale(const int (*const scales_tbl)[2], size_t size, + int scale_val, int scale_val2) +{ + for (unsigned int i = 0; i < size; i++) + if (scales_tbl[i][0] == scale_val && + scales_tbl[i][1] == scale_val2) + return i; + + return -EINVAL; +} + +/* + * Configure device with the given gain (only if changed) + * + * Must be called with lock held. + */ +static int pac1921_update_gain(struct pac1921_priv *priv, u8 *priv_val, u8 gain, + unsigned int mask) +{ + unsigned int reg_val; + int ret; + + if (*priv_val == gain) + return 0; + + reg_val = (gain << __ffs(mask)) & mask; + ret = pac1921_update_cfg_reg(priv, PAC1921_REG_GAIN_CFG, mask, reg_val); + if (ret) + return ret; + + *priv_val = gain; + + return 0; +} + +/* + * Given a scale factor represented by scale_val and scale_val2 with format + * IIO_VAL_INT_PLUS_NANO, find the corresponding gain value and write it to the + * device. + * + * Must be called with lock held. + */ +static int pac1921_update_gain_from_scale(struct pac1921_priv *priv, + struct iio_chan_spec const *chan, + int scale_val, int scale_val2) +{ + int ret; + + switch (chan->channel) { + case PAC1921_CHAN_VBUS: + ret = pac1921_lookup_scale(pac1921_vbus_scales, + ARRAY_SIZE(pac1921_vbus_scales), + scale_val, scale_val2); + if (ret < 0) + return ret; + + return pac1921_update_gain(priv, &priv->dv_gain, ret, + PAC1921_GAIN_DV_GAIN_MASK); + case PAC1921_CHAN_VSENSE: + ret = pac1921_lookup_scale(pac1921_vsense_scales, + ARRAY_SIZE(pac1921_vsense_scales), + scale_val, scale_val2); + if (ret < 0) + return ret; + + return pac1921_update_gain(priv, &priv->di_gain, ret, + PAC1921_GAIN_DI_GAIN_MASK); + case PAC1921_CHAN_CURRENT: + ret = pac1921_lookup_scale(priv->current_scales, + ARRAY_SIZE(priv->current_scales), + scale_val, scale_val2); + if (ret < 0) + return ret; + + return pac1921_update_gain(priv, &priv->di_gain, ret, + PAC1921_GAIN_DI_GAIN_MASK); + default: + return -EINVAL; + } +} + +/* + * Retrieve the index of the given number of samples from the constant table. + * The returned index (if found) is the log2 of the given num_samples. + */ +static int pac1921_lookup_int_num_samples(int num_samples) +{ + for (unsigned int i = 0; i < ARRAY_SIZE(pac1921_int_num_samples); i++) + if (pac1921_int_num_samples[i] == num_samples) + return i; + + return -EINVAL; +} + +/* + * Update the device with the given number of integration samples. + * + * Must be called with lock held. + */ +static int pac1921_update_int_num_samples(struct pac1921_priv *priv, + int num_samples) +{ + unsigned int reg_val; + u8 n_samples; + int ret; + + ret = pac1921_lookup_int_num_samples(num_samples); + if (ret < 0) + return ret; + + n_samples = ret; + + if (priv->n_samples == n_samples) + return 0; + + reg_val = FIELD_PREP(PAC1921_INT_CFG_SMPL_MASK, n_samples); + + ret = pac1921_update_cfg_reg(priv, PAC1921_REG_INT_CFG, + PAC1921_INT_CFG_SMPL_MASK, reg_val); + if (ret) + return ret; + + priv->n_samples = n_samples; + + priv->integr_period_usecs = pac1921_int_periods_usecs[priv->n_samples]; + + return 0; +} + +static int pac1921_write_raw_get_fmt(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + long info) +{ + switch (info) { + case IIO_CHAN_INFO_SCALE: + return IIO_VAL_INT_PLUS_NANO; + case IIO_CHAN_INFO_OVERSAMPLING_RATIO: + return IIO_VAL_INT; + default: + return -EINVAL; + } +} + +static int pac1921_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int val, + int val2, long mask) +{ + struct pac1921_priv *priv = iio_priv(indio_dev); + + guard(mutex)(&priv->lock); + + switch (mask) { + case IIO_CHAN_INFO_SCALE: + return pac1921_update_gain_from_scale(priv, chan, val, val2); + case IIO_CHAN_INFO_OVERSAMPLING_RATIO: + return pac1921_update_int_num_samples(priv, val); + default: + return -EINVAL; + } +} + +static int pac1921_read_label(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, char *label) +{ + switch (chan->channel) { + case PAC1921_CHAN_VBUS: + return sysfs_emit(label, "vbus\n"); + case PAC1921_CHAN_VSENSE: + return sysfs_emit(label, "vsense\n"); + case PAC1921_CHAN_CURRENT: + return sysfs_emit(label, "current\n"); + case PAC1921_CHAN_POWER: + return sysfs_emit(label, "power\n"); + default: + return -EINVAL; + } +} + +static int pac1921_read_event_config(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir) +{ + struct pac1921_priv *priv = iio_priv(indio_dev); + + guard(mutex)(&priv->lock); + + switch (chan->channel) { + case PAC1921_CHAN_VBUS: + return !!(priv->ovf_enabled_events & PAC1921_OVERFLOW_VBOV); + case PAC1921_CHAN_VSENSE: + case PAC1921_CHAN_CURRENT: + return !!(priv->ovf_enabled_events & PAC1921_OVERFLOW_VSOV); + case PAC1921_CHAN_POWER: + return !!(priv->ovf_enabled_events & PAC1921_OVERFLOW_VPOV); + default: + return -EINVAL; + } +} + +static int pac1921_write_event_config(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir, + bool state) +{ + struct pac1921_priv *priv = iio_priv(indio_dev); + u8 ovf_bit; + + guard(mutex)(&priv->lock); + + switch (chan->channel) { + case PAC1921_CHAN_VBUS: + ovf_bit = PAC1921_OVERFLOW_VBOV; + break; + case PAC1921_CHAN_VSENSE: + case PAC1921_CHAN_CURRENT: + ovf_bit = PAC1921_OVERFLOW_VSOV; + break; + case PAC1921_CHAN_POWER: + ovf_bit = PAC1921_OVERFLOW_VPOV; + break; + default: + return -EINVAL; + } + + if (state) + priv->ovf_enabled_events |= ovf_bit; + else + priv->ovf_enabled_events &= ~ovf_bit; + + return 0; +} + +static int pac1921_read_event_value(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir, + enum iio_event_info info, int *val, + int *val2) +{ + switch (info) { + case IIO_EV_INFO_VALUE: + *val = PAC1921_RES_RESOLUTION; + return IIO_VAL_INT; + default: + return -EINVAL; + } +} + +static const struct iio_info pac1921_iio = { + .read_raw = pac1921_read_raw, + .read_avail = pac1921_read_avail, + .write_raw = pac1921_write_raw, + .write_raw_get_fmt = pac1921_write_raw_get_fmt, + .read_label = pac1921_read_label, + .read_event_config = pac1921_read_event_config, + .write_event_config = pac1921_write_event_config, + .read_event_value = pac1921_read_event_value, +}; + +static ssize_t pac1921_read_shunt_resistor(struct iio_dev *indio_dev, + uintptr_t private, + const struct iio_chan_spec *chan, + char *buf) +{ + struct pac1921_priv *priv = iio_priv(indio_dev); + int vals[2]; + + if (chan->channel != PAC1921_CHAN_CURRENT) + return -EINVAL; + + guard(mutex)(&priv->lock); + + vals[0] = priv->rshunt_uohm; + vals[1] = MICRO; + + return iio_format_value(buf, IIO_VAL_FRACTIONAL, 1, vals); +} + +static ssize_t pac1921_write_shunt_resistor(struct iio_dev *indio_dev, + uintptr_t private, + const struct iio_chan_spec *chan, + const char *buf, size_t len) +{ + struct pac1921_priv *priv = iio_priv(indio_dev); + u32 rshunt_uohm; + int val, val_fract; + int ret; + + if (chan->channel != PAC1921_CHAN_CURRENT) + return -EINVAL; + + ret = iio_str_to_fixpoint(buf, 100000, &val, &val_fract); + if (ret) + return ret; + + /* + * This check validates the shunt is not zero and does not surpass + * INT_MAX. The check is done before calculating in order to avoid + * val * MICRO overflowing. + */ + if ((!val && !val_fract) || val > INT_MAX / MICRO || + (val == INT_MAX / MICRO && val_fract > INT_MAX % MICRO)) + return -EINVAL; + + rshunt_uohm = val * MICRO + val_fract; + + guard(mutex)(&priv->lock); + + priv->rshunt_uohm = rshunt_uohm; + + pac1921_calc_current_scales(priv); + + return len; +} + +/* + * Emit on sysfs the list of available scales contained in scales_tbl + * + * TODO:: this function can be replaced with iio_format_avail_list() if the + * latter will ever be exported. + * + * Must be called with lock held if the scales_tbl can change runtime (e.g. for + * the current scales table) + */ +static ssize_t pac1921_format_scale_avail(const int (*const scales_tbl)[2], + size_t size, char *buf) +{ + ssize_t len = 0; + + for (unsigned int i = 0; i < size; i++) { + if (i != 0) { + len += sysfs_emit_at(buf, len, " "); + if (len >= PAGE_SIZE) + return -EFBIG; + } + len += sysfs_emit_at(buf, len, "%d.%09d", scales_tbl[i][0], + scales_tbl[i][1]); + if (len >= PAGE_SIZE) + return -EFBIG; + } + + len += sysfs_emit_at(buf, len, "\n"); + return len; +} + +/* + * Read available scales for a specific channel + * + * NOTE: using extended info instead of iio.read_avail() because access to + * current scales must be locked as they depend on shunt resistor which may + * change runtime. Caller of iio.read_avail() would access the table unlocked + * instead. + */ +static ssize_t pac1921_read_scale_avail(struct iio_dev *indio_dev, + uintptr_t private, + const struct iio_chan_spec *chan, + char *buf) +{ + struct pac1921_priv *priv = iio_priv(indio_dev); + const int (*scales_tbl)[2]; + size_t size; + + switch (chan->channel) { + case PAC1921_CHAN_VBUS: + scales_tbl = pac1921_vbus_scales; + size = ARRAY_SIZE(pac1921_vbus_scales); + return pac1921_format_scale_avail(scales_tbl, size, buf); + + case PAC1921_CHAN_VSENSE: + scales_tbl = pac1921_vsense_scales; + size = ARRAY_SIZE(pac1921_vsense_scales); + return pac1921_format_scale_avail(scales_tbl, size, buf); + + case PAC1921_CHAN_CURRENT: { + guard(mutex)(&priv->lock); + scales_tbl = priv->current_scales; + size = ARRAY_SIZE(priv->current_scales); + return pac1921_format_scale_avail(scales_tbl, size, buf); + } + default: + return -EINVAL; + } +} + +#define PAC1921_EXT_INFO_SCALE_AVAIL { \ + .name = "scale_available", \ + .read = pac1921_read_scale_avail, \ + .shared = IIO_SEPARATE, \ +} + +static const struct iio_chan_spec_ext_info pac1921_ext_info_voltage[] = { + PAC1921_EXT_INFO_SCALE_AVAIL, + { } +}; + +static const struct iio_chan_spec_ext_info pac1921_ext_info_current[] = { + PAC1921_EXT_INFO_SCALE_AVAIL, + { + .name = "shunt_resistor", + .read = pac1921_read_shunt_resistor, + .write = pac1921_write_shunt_resistor, + .shared = IIO_SEPARATE, + }, + { } +}; + +static const struct iio_event_spec pac1921_overflow_event[] = { + { + .type = IIO_EV_TYPE_THRESH, + .dir = IIO_EV_DIR_RISING, + .mask_shared_by_all = BIT(IIO_EV_INFO_VALUE), + .mask_separate = BIT(IIO_EV_INFO_ENABLE), + }, +}; + +static const struct iio_chan_spec pac1921_channels[] = { + { + .type = IIO_VOLTAGE, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SCALE), + .info_mask_shared_by_all = + BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO) | + BIT(IIO_CHAN_INFO_SAMP_FREQ), + .info_mask_shared_by_all_available = + BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), + .channel = PAC1921_CHAN_VBUS, + .address = PAC1921_REG_VBUS, + .scan_index = PAC1921_CHAN_VBUS, + .scan_type = { + .sign = 'u', + .realbits = 10, + .storagebits = 16, + .endianness = IIO_CPU + }, + .indexed = 1, + .event_spec = pac1921_overflow_event, + .num_event_specs = ARRAY_SIZE(pac1921_overflow_event), + .ext_info = pac1921_ext_info_voltage, + }, + { + .type = IIO_VOLTAGE, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SCALE), + .info_mask_shared_by_all = + BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO) | + BIT(IIO_CHAN_INFO_SAMP_FREQ), + .info_mask_shared_by_all_available = + BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), + .channel = PAC1921_CHAN_VSENSE, + .address = PAC1921_REG_VSENSE, + .scan_index = PAC1921_CHAN_VSENSE, + .scan_type = { + .sign = 'u', + .realbits = 10, + .storagebits = 16, + .endianness = IIO_CPU + }, + .indexed = 1, + .event_spec = pac1921_overflow_event, + .num_event_specs = ARRAY_SIZE(pac1921_overflow_event), + .ext_info = pac1921_ext_info_voltage, + }, + { + .type = IIO_CURRENT, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SCALE), + .info_mask_shared_by_all = + BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO) | + BIT(IIO_CHAN_INFO_SAMP_FREQ), + .info_mask_shared_by_all_available = + BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), + .channel = PAC1921_CHAN_CURRENT, + .address = PAC1921_REG_VSENSE, + .scan_index = PAC1921_CHAN_CURRENT, + .scan_type = { + .sign = 'u', + .realbits = 10, + .storagebits = 16, + .endianness = IIO_CPU + }, + .event_spec = pac1921_overflow_event, + .num_event_specs = ARRAY_SIZE(pac1921_overflow_event), + .ext_info = pac1921_ext_info_current, + }, + { + .type = IIO_POWER, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SCALE), + .info_mask_shared_by_all = + BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO) | + BIT(IIO_CHAN_INFO_SAMP_FREQ), + .info_mask_shared_by_all_available = + BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), + .channel = PAC1921_CHAN_POWER, + .address = PAC1921_REG_VPOWER, + .scan_index = PAC1921_CHAN_POWER, + .scan_type = { + .sign = 'u', + .realbits = 10, + .storagebits = 16, + .endianness = IIO_CPU + }, + .event_spec = pac1921_overflow_event, + .num_event_specs = ARRAY_SIZE(pac1921_overflow_event), + }, + IIO_CHAN_SOFT_TIMESTAMP(PAC1921_NUM_MEAS_CHANS), +}; + +static irqreturn_t pac1921_trigger_handler(int irq, void *p) +{ + struct iio_poll_func *pf = p; + struct iio_dev *idev = pf->indio_dev; + struct pac1921_priv *priv = iio_priv(idev); + int ret; + int bit; + int ch = 0; + + guard(mutex)(&priv->lock); + + if (!pac1921_data_ready(priv)) + goto done; + + ret = pac1921_check_push_overflow(idev, pf->timestamp); + if (ret) + goto done; + + iio_for_each_active_channel(idev, bit) { + u16 val; + + ret = pac1921_read_res(priv, idev->channels[bit].address, &val); + if (ret) + goto done; + + priv->scan.chan[ch++] = val; + } + + iio_push_to_buffers_with_ts(idev, &priv->scan, sizeof(priv->scan), + pf->timestamp); + +done: + iio_trigger_notify_done(idev->trig); + + return IRQ_HANDLED; +} + +/* + * Initialize device by writing initial configuration and putting it into + * integration state. + * + * Must be called with lock held when called after first initialization + * (e.g. from pm resume) + */ +static int pac1921_init(struct pac1921_priv *priv) +{ + unsigned int val; + int ret; + + /* Enter READ state before configuration */ + ret = regmap_update_bits(priv->regmap, PAC1921_REG_INT_CFG, + PAC1921_INT_CFG_INTEN, 0); + if (ret) + return ret; + + /* Configure gains, use 14-bits measurement resolution (HW default) */ + val = FIELD_PREP(PAC1921_GAIN_DI_GAIN_MASK, priv->di_gain) | + FIELD_PREP(PAC1921_GAIN_DV_GAIN_MASK, priv->dv_gain); + ret = regmap_write(priv->regmap, PAC1921_REG_GAIN_CFG, val); + if (ret) + return ret; + + /* + * Configure integration: + * - num of integration samples + * - filters enabled (HW default) + * - set READ/INT pin override (RIOV) to control operation mode via + * register instead of pin + */ + val = FIELD_PREP(PAC1921_INT_CFG_SMPL_MASK, priv->n_samples) | + PAC1921_INT_CFG_VSFEN | PAC1921_INT_CFG_VBFEN | + PAC1921_INT_CFG_RIOV; + ret = regmap_write(priv->regmap, PAC1921_REG_INT_CFG, val); + if (ret) + return ret; + + /* + * Init control register: + * - VPower free run integration mode + * - OUT pin full scale range: 3V (HW default) + * - no timeout, no sleep, no sleep override, no recalc (HW defaults) + */ + val = FIELD_PREP(PAC1921_CONTROL_MXSL_MASK, + PAC1921_MXSL_VPOWER_FREE_RUN); + ret = regmap_write(priv->regmap, PAC1921_REG_CONTROL, val); + if (ret) + return ret; + + /* Enable integration */ + ret = regmap_update_bits(priv->regmap, PAC1921_REG_INT_CFG, + PAC1921_INT_CFG_INTEN, PAC1921_INT_CFG_INTEN); + if (ret) + return ret; + + priv->first_integr_started = true; + priv->integr_started_time_jiffies = jiffies; + priv->integr_period_usecs = pac1921_int_periods_usecs[priv->n_samples]; + + return 0; +} + +static int pac1921_suspend(struct device *dev) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct pac1921_priv *priv = iio_priv(indio_dev); + int ret; + + guard(mutex)(&priv->lock); + + priv->first_integr_started = false; + priv->first_integr_done = false; + + ret = regmap_update_bits(priv->regmap, PAC1921_REG_INT_CFG, + PAC1921_INT_CFG_INTEN, 0); + if (ret) + return ret; + + ret = regmap_update_bits(priv->regmap, PAC1921_REG_CONTROL, + PAC1921_CONTROL_SLEEP, PAC1921_CONTROL_SLEEP); + if (ret) + return ret; + + return regulator_disable(priv->vdd); + +} + +static int pac1921_resume(struct device *dev) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct pac1921_priv *priv = iio_priv(indio_dev); + int ret; + + guard(mutex)(&priv->lock); + + ret = regulator_enable(priv->vdd); + if (ret) + return ret; + + msleep(PAC1921_POWERUP_TIME_MS); + + return pac1921_init(priv); +} + +static DEFINE_SIMPLE_DEV_PM_OPS(pac1921_pm_ops, pac1921_suspend, + pac1921_resume); + +static void pac1921_regulator_disable(void *data) +{ + struct regulator *regulator = data; + + regulator_disable(regulator); +} + +/* + * Documentation related to the ACPI device definition + * https://ww1.microchip.com/downloads/aemDocuments/documents/OTH/ApplicationNotes/ApplicationNotes/PAC193X-Integration-Notes-for-Microsoft-Windows-10-and-Windows-11-Driver-Support-DS00002534.pdf + */ +static int pac1921_match_acpi_device(struct iio_dev *indio_dev) +{ + acpi_handle handle; + union acpi_object *status; + char *label; + struct pac1921_priv *priv = iio_priv(indio_dev); + struct device *dev = &priv->client->dev; + + handle = ACPI_HANDLE(dev); + + status = acpi_evaluate_dsm(handle, &pac1921_guid, 1, + PAC1921_ACPI_GET_uOHMS_VALS, NULL); + if (!status) + return dev_err_probe(dev, -EINVAL, + "Could not read shunt from ACPI table\n"); + + priv->rshunt_uohm = status->package.elements[0].integer.value; + ACPI_FREE(status); + + status = acpi_evaluate_dsm(handle, &pac1921_guid, 1, + PAC1921_ACPI_GET_LABEL, NULL); + if (!status) + return dev_err_probe(dev, -EINVAL, + "Could not read label from ACPI table\n"); + + label = devm_kstrdup(dev, status->package.elements[0].string.pointer, + GFP_KERNEL); + ACPI_FREE(status); + if (!label) + return -ENOMEM; + + indio_dev->label = label; + + return 0; +} + +static int pac1921_parse_of_fw(struct iio_dev *indio_dev) +{ + int ret; + struct pac1921_priv *priv = iio_priv(indio_dev); + struct device *dev = &priv->client->dev; + + ret = device_property_read_u32(dev, "shunt-resistor-micro-ohms", + &priv->rshunt_uohm); + if (ret) + return dev_err_probe(dev, ret, + "Cannot read shunt resistor property\n"); + + return 0; +} + +static int pac1921_probe(struct i2c_client *client) +{ + struct device *dev = &client->dev; + struct pac1921_priv *priv; + struct iio_dev *indio_dev; + int ret; + + indio_dev = devm_iio_device_alloc(dev, sizeof(*priv)); + if (!indio_dev) + return -ENOMEM; + + priv = iio_priv(indio_dev); + priv->client = client; + i2c_set_clientdata(client, indio_dev); + + priv->regmap = devm_regmap_init_i2c(client, &pac1921_regmap_config); + if (IS_ERR(priv->regmap)) + return dev_err_probe(dev, PTR_ERR(priv->regmap), + "Cannot initialize register map\n"); + + ret = devm_mutex_init(dev, &priv->lock); + if (ret) + return ret; + + priv->dv_gain = PAC1921_DEFAULT_DV_GAIN; + priv->di_gain = PAC1921_DEFAULT_DI_GAIN; + priv->n_samples = PAC1921_DEFAULT_NUM_SAMPLES; + + if (is_acpi_device_node(dev->fwnode)) + ret = pac1921_match_acpi_device(indio_dev); + else + ret = pac1921_parse_of_fw(indio_dev); + if (ret) + return dev_err_probe(dev, ret, + "Parameter parsing error\n"); + + if (priv->rshunt_uohm == 0 || priv->rshunt_uohm > INT_MAX) + return dev_err_probe(dev, -EINVAL, + "Invalid shunt resistor: %u\n", + priv->rshunt_uohm); + + pac1921_calc_current_scales(priv); + + priv->vdd = devm_regulator_get(dev, "vdd"); + if (IS_ERR(priv->vdd)) + return dev_err_probe(dev, PTR_ERR(priv->vdd), + "Cannot get vdd regulator\n"); + + ret = regulator_enable(priv->vdd); + if (ret) + return dev_err_probe(dev, ret, "Cannot enable vdd regulator\n"); + + ret = devm_add_action_or_reset(dev, pac1921_regulator_disable, + priv->vdd); + if (ret) + return ret; + + msleep(PAC1921_POWERUP_TIME_MS); + + ret = pac1921_init(priv); + if (ret) + return dev_err_probe(dev, ret, "Cannot initialize device\n"); + + priv->iio_info = pac1921_iio; + + indio_dev->name = "pac1921"; + indio_dev->info = &priv->iio_info; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = pac1921_channels; + indio_dev->num_channels = ARRAY_SIZE(pac1921_channels); + + ret = devm_iio_triggered_buffer_setup(dev, indio_dev, + &iio_pollfunc_store_time, + &pac1921_trigger_handler, NULL); + if (ret) + return dev_err_probe(dev, ret, + "Cannot setup IIO triggered buffer\n"); + + ret = devm_iio_device_register(dev, indio_dev); + if (ret) + return dev_err_probe(dev, ret, "Cannot register IIO device\n"); + + return 0; +} + +static const struct i2c_device_id pac1921_id[] = { + { .name = "pac1921" }, + { } +}; +MODULE_DEVICE_TABLE(i2c, pac1921_id); + +static const struct of_device_id pac1921_of_match[] = { + { .compatible = "microchip,pac1921" }, + { } +}; +MODULE_DEVICE_TABLE(of, pac1921_of_match); + +static const struct acpi_device_id pac1921_acpi_match[] = { + { "MCHP1921" }, + { } +}; +MODULE_DEVICE_TABLE(acpi, pac1921_acpi_match); + +static struct i2c_driver pac1921_driver = { + .driver = { + .name = "pac1921", + .pm = pm_sleep_ptr(&pac1921_pm_ops), + .of_match_table = pac1921_of_match, + .acpi_match_table = pac1921_acpi_match, + }, + .probe = pac1921_probe, + .id_table = pac1921_id, +}; + +module_i2c_driver(pac1921_driver); + +MODULE_AUTHOR("Matteo Martelli "); +MODULE_DESCRIPTION("IIO driver for PAC1921 High-Side Power/Current Monitor"); +MODULE_LICENSE("GPL"); diff --git a/drivers/iio/adc/pac1934.c b/drivers/iio/adc/pac1934.c new file mode 100644 index 000000000..23055405a --- /dev/null +++ b/drivers/iio/adc/pac1934.c @@ -0,0 +1,1632 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * IIO driver for PAC1934 Multi-Channel DC Power/Energy Monitor + * + * Copyright (C) 2017-2024 Microchip Technology Inc. and its subsidiaries + * + * Author: Bogdan Bolocan + * Author: Victor Tudose + * Author: Marius Cristea + * + * Datasheet for PAC1931, PAC1932, PAC1933 and PAC1934 can be found here: + * https://ww1.microchip.com/downloads/aemDocuments/documents/OTH/ProductDocuments/DataSheets/PAC1931-Family-Data-Sheet-DS20005850E.pdf + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * maximum accumulation time should be (17 * 60 * 1000) around 17 minutes@1024 sps + * till PAC1934 accumulation registers starts to saturate + */ +#define PAC1934_MAX_RFSH_LIMIT_MS 60000 +/* 50msec is the timeout for validity of the cached registers */ +#define PAC1934_MIN_POLLING_TIME_MS 50 +/* + * 1000usec is the minimum wait time for normal conversions when sample + * rate doesn't change + */ +#define PAC1934_MIN_UPDATE_WAIT_TIME_US 1000 + +/* 32000mV */ +#define PAC1934_VOLTAGE_MILLIVOLTS_MAX 32000 +/* voltage bits resolution when set for unsigned values */ +#define PAC1934_VOLTAGE_U_RES 16 +/* voltage bits resolution when set for signed values */ +#define PAC1934_VOLTAGE_S_RES 15 + +/* + * max signed value that can be stored on 32 bits and 8 digits fractional value + * (2^31 - 1) * 10^8 + 99999999 + */ +#define PAC_193X_MAX_POWER_ACC 214748364799999999LL +/* + * min signed value that can be stored on 32 bits and 8 digits fractional value + * -(2^31) * 10^8 - 99999999 + */ +#define PAC_193X_MIN_POWER_ACC -214748364899999999LL + +#define PAC1934_MAX_NUM_CHANNELS 4 + +#define PAC1934_MEAS_REG_LEN 76 +#define PAC1934_CTRL_REG_LEN 12 + +#define PAC1934_DEFAULT_CHIP_SAMP_SPEED_HZ 1024 + +/* I2C address map */ +#define PAC1934_REFRESH_REG_ADDR 0x00 +#define PAC1934_CTRL_REG_ADDR 0x01 +#define PAC1934_ACC_COUNT_REG_ADDR 0x02 +#define PAC1934_VPOWER_ACC_1_ADDR 0x03 +#define PAC1934_VPOWER_ACC_2_ADDR 0x04 +#define PAC1934_VPOWER_ACC_3_ADDR 0x05 +#define PAC1934_VPOWER_ACC_4_ADDR 0x06 +#define PAC1934_VBUS_1_ADDR 0x07 +#define PAC1934_VBUS_2_ADDR 0x08 +#define PAC1934_VBUS_3_ADDR 0x09 +#define PAC1934_VBUS_4_ADDR 0x0A +#define PAC1934_VSENSE_1_ADDR 0x0B +#define PAC1934_VSENSE_2_ADDR 0x0C +#define PAC1934_VSENSE_3_ADDR 0x0D +#define PAC1934_VSENSE_4_ADDR 0x0E +#define PAC1934_VBUS_AVG_1_ADDR 0x0F +#define PAC1934_VBUS_AVG_2_ADDR 0x10 +#define PAC1934_VBUS_AVG_3_ADDR 0x11 +#define PAC1934_VBUS_AVG_4_ADDR 0x12 +#define PAC1934_VSENSE_AVG_1_ADDR 0x13 +#define PAC1934_VSENSE_AVG_2_ADDR 0x14 +#define PAC1934_VSENSE_AVG_3_ADDR 0x15 +#define PAC1934_VSENSE_AVG_4_ADDR 0x16 +#define PAC1934_VPOWER_1_ADDR 0x17 +#define PAC1934_VPOWER_2_ADDR 0x18 +#define PAC1934_VPOWER_3_ADDR 0x19 +#define PAC1934_VPOWER_4_ADDR 0x1A +#define PAC1934_REFRESH_V_REG_ADDR 0x1F +#define PAC1934_SLOW_REG_ADDR 0x20 +#define PAC1934_CTRL_STAT_REGS_ADDR 0x1C +#define PAC1934_PID_REG_ADDR 0xFD +#define PAC1934_MID_REG_ADDR 0xFE +#define PAC1934_RID_REG_ADDR 0xFF + +/* PRODUCT ID REGISTER + MANUFACTURER ID REGISTER + REVISION ID REGISTER */ +#define PAC1934_ID_REG_LEN 3 +#define PAC1934_PID_IDX 0 +#define PAC1934_MID_IDX 1 +#define PAC1934_RID_IDX 2 + +#define PAC1934_ACPI_GET_NAMES_AND_MOHMS_VALS 1 +#define PAC1934_ACPI_GET_UOHMS_VALS 2 +#define PAC1934_ACPI_GET_BIPOLAR_SETTINGS 4 +#define PAC1934_ACPI_GET_SAMP 5 + +#define PAC1934_SAMPLE_RATE_SHIFT 6 + +#define PAC1934_VBUS_SENSE_REG_LEN 2 +#define PAC1934_ACC_REG_LEN 3 +#define PAC1934_VPOWER_REG_LEN 4 +#define PAC1934_VPOWER_ACC_REG_LEN 6 +#define PAC1934_MAX_REGISTER_LENGTH 6 + +#define PAC1934_CUSTOM_ATTR_FOR_CHANNEL 1 + +/* + * relative offsets when using multi-byte reads/writes even though these + * bytes are read one after the other, they are not at adjacent memory + * locations within the I2C memory map. The chip can skip some addresses + */ +#define PAC1934_CHANNEL_DIS_REG_OFF 0 +#define PAC1934_NEG_PWR_REG_OFF 1 + +/* + * when reading/writing multiple bytes from offset PAC1934_CHANNEL_DIS_REG_OFF, + * the chip jumps over the 0x1E (REFRESH_G) and 0x1F (REFRESH_V) offsets + */ +#define PAC1934_SLOW_REG_OFF 2 +#define PAC1934_CTRL_ACT_REG_OFF 3 +#define PAC1934_CHANNEL_DIS_ACT_REG_OFF 4 +#define PAC1934_NEG_PWR_ACT_REG_OFF 5 +#define PAC1934_CTRL_LAT_REG_OFF 6 +#define PAC1934_CHANNEL_DIS_LAT_REG_OFF 7 +#define PAC1934_NEG_PWR_LAT_REG_OFF 8 +#define PAC1934_PID_REG_OFF 9 +#define PAC1934_MID_REG_OFF 10 +#define PAC1934_REV_REG_OFF 11 +#define PAC1934_CTRL_STATUS_INFO_LEN 12 + +#define PAC1934_MID 0x5D +#define PAC1931_PID 0x58 +#define PAC1932_PID 0x59 +#define PAC1933_PID 0x5A +#define PAC1934_PID 0x5B + +/* Scale constant = (10^3 * 3.2 * 10^9 / 2^28) for mili Watt-second */ +#define PAC1934_SCALE_CONSTANT 11921 + +#define PAC1934_MAX_VPOWER_RSHIFTED_BY_28B 11921 +#define PAC1934_MAX_VSENSE_RSHIFTED_BY_16B 1525 + +#define PAC1934_DEV_ATTR(name) (&iio_dev_attr_##name.dev_attr.attr) + +#define PAC1934_CRTL_SAMPLE_RATE_MASK GENMASK(7, 6) +#define PAC1934_CHAN_SLEEP_MASK BIT(5) +#define PAC1934_CHAN_SLEEP_SET BIT(5) +#define PAC1934_CHAN_SINGLE_MASK BIT(4) +#define PAC1934_CHAN_SINGLE_SHOT_SET BIT(4) +#define PAC1934_CHAN_ALERT_MASK BIT(3) +#define PAC1934_CHAN_ALERT_EN BIT(3) +#define PAC1934_CHAN_ALERT_CC_MASK BIT(2) +#define PAC1934_CHAN_ALERT_CC_EN BIT(2) +#define PAC1934_CHAN_OVF_ALERT_MASK BIT(1) +#define PAC1934_CHAN_OVF_ALERT_EN BIT(1) +#define PAC1934_CHAN_OVF_MASK BIT(0) + +#define PAC1934_CHAN_DIS_CH1_OFF_MASK BIT(7) +#define PAC1934_CHAN_DIS_CH2_OFF_MASK BIT(6) +#define PAC1934_CHAN_DIS_CH3_OFF_MASK BIT(5) +#define PAC1934_CHAN_DIS_CH4_OFF_MASK BIT(4) +#define PAC1934_SMBUS_TIMEOUT_MASK BIT(3) +#define PAC1934_SMBUS_BYTECOUNT_MASK BIT(2) +#define PAC1934_SMBUS_NO_SKIP_MASK BIT(1) + +#define PAC1934_NEG_PWR_CH1_BIDI_MASK BIT(7) +#define PAC1934_NEG_PWR_CH2_BIDI_MASK BIT(6) +#define PAC1934_NEG_PWR_CH3_BIDI_MASK BIT(5) +#define PAC1934_NEG_PWR_CH4_BIDI_MASK BIT(4) +#define PAC1934_NEG_PWR_CH1_BIDV_MASK BIT(3) +#define PAC1934_NEG_PWR_CH2_BIDV_MASK BIT(2) +#define PAC1934_NEG_PWR_CH3_BIDV_MASK BIT(1) +#define PAC1934_NEG_PWR_CH4_BIDV_MASK BIT(0) + +/* + * Universal Unique Identifier (UUID), + * 033771E0-1705-47B4-9535-D1BBE14D9A09, + * is reserved to Microchip for the PAC1934. + */ +#define PAC1934_DSM_UUID "033771E0-1705-47B4-9535-D1BBE14D9A09" + +enum pac1934_ids { + PAC1931, + PAC1932, + PAC1933, + PAC1934 +}; + +enum pac1934_samps { + PAC1934_SAMP_1024SPS, + PAC1934_SAMP_256SPS, + PAC1934_SAMP_64SPS, + PAC1934_SAMP_8SPS +}; + +/* + * these indexes are exactly describing the element order within a single + * PAC1934 phys channel IIO channel descriptor; see the static const struct + * iio_chan_spec pac1934_single_channel[] declaration + */ +enum pac1934_ch_idx { + PAC1934_CH_ENERGY, + PAC1934_CH_POWER, + PAC1934_CH_VOLTAGE, + PAC1934_CH_CURRENT, + PAC1934_CH_VOLTAGE_AVERAGE, + PAC1934_CH_CURRENT_AVERAGE +}; + +/** + * struct pac1934_features - features of a pac1934 instance + * @phys_channels: number of physical channels supported by the chip + * @name: chip's name + */ +struct pac1934_features { + u8 phys_channels; + const char *name; +}; + +static const unsigned int samp_rate_map_tbl[] = { + [PAC1934_SAMP_1024SPS] = 1024, + [PAC1934_SAMP_256SPS] = 256, + [PAC1934_SAMP_64SPS] = 64, + [PAC1934_SAMP_8SPS] = 8, +}; + +static const struct pac1934_features pac1934_chip_config[] = { + [PAC1931] = { + .phys_channels = 1, + .name = "pac1931", + }, + [PAC1932] = { + .phys_channels = 2, + .name = "pac1932", + }, + [PAC1933] = { + .phys_channels = 3, + .name = "pac1933", + }, + [PAC1934] = { + .phys_channels = 4, + .name = "pac1934", + }, +}; + +/** + * struct reg_data - data from the registers + * @meas_regs: snapshot of raw measurements registers + * @ctrl_regs: snapshot of control registers + * @energy_sec_acc: snapshot of energy values + * @vpower_acc: accumulated vpower values + * @vpower: snapshot of vpower registers + * @vbus: snapshot of vbus registers + * @vbus_avg: averages of vbus registers + * @vsense: snapshot of vsense registers + * @vsense_avg: averages of vsense registers + * @num_enabled_channels: count of how many chip channels are currently enabled + */ +struct reg_data { + u8 meas_regs[PAC1934_MEAS_REG_LEN]; + u8 ctrl_regs[PAC1934_CTRL_REG_LEN]; + s64 energy_sec_acc[PAC1934_MAX_NUM_CHANNELS]; + s64 vpower_acc[PAC1934_MAX_NUM_CHANNELS]; + s32 vpower[PAC1934_MAX_NUM_CHANNELS]; + s32 vbus[PAC1934_MAX_NUM_CHANNELS]; + s32 vbus_avg[PAC1934_MAX_NUM_CHANNELS]; + s32 vsense[PAC1934_MAX_NUM_CHANNELS]; + s32 vsense_avg[PAC1934_MAX_NUM_CHANNELS]; + u8 num_enabled_channels; +}; + +/** + * struct pac1934_chip_info - information about the chip + * @client: the i2c-client attached to the device + * @lock: synchronize access to driver's state members + * @work_chip_rfsh: work queue used for refresh commands + * @phys_channels: phys channels count + * @active_channels: array of values, true means that channel is active + * @enable_energy: array of values, true means that channel energy is measured + * @bi_dir: array of bools, true means that channel is bidirectional + * @chip_variant: chip variant + * @chip_revision: chip revision + * @shunts: shunts + * @chip_reg_data: chip reg data + * @sample_rate_value: sampling frequency + * @labels: table with channels labels + * @iio_info: iio_info + * @tstamp: chip's uptime + */ +struct pac1934_chip_info { + struct i2c_client *client; + struct mutex lock; /* synchronize access to driver's state members */ + struct delayed_work work_chip_rfsh; + u8 phys_channels; + bool active_channels[PAC1934_MAX_NUM_CHANNELS]; + bool enable_energy[PAC1934_MAX_NUM_CHANNELS]; + bool bi_dir[PAC1934_MAX_NUM_CHANNELS]; + u8 chip_variant; + u8 chip_revision; + u32 shunts[PAC1934_MAX_NUM_CHANNELS]; + struct reg_data chip_reg_data; + s32 sample_rate_value; + char *labels[PAC1934_MAX_NUM_CHANNELS]; + struct iio_info iio_info; + unsigned long tstamp; +}; + +#define TO_PAC1934_CHIP_INFO(d) container_of(d, struct pac1934_chip_info, work_chip_rfsh) + +#define PAC1934_VPOWER_ACC_CHANNEL(_index, _si, _address) { \ + .type = IIO_ENERGY, \ + .address = (_address), \ + .indexed = 1, \ + .channel = (_index), \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ + BIT(IIO_CHAN_INFO_SCALE) | \ + BIT(IIO_CHAN_INFO_ENABLE), \ + .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ), \ + .info_mask_shared_by_all_available = BIT(IIO_CHAN_INFO_SAMP_FREQ), \ + .scan_index = (_si), \ + .scan_type = { \ + .sign = 'u', \ + .realbits = 48, \ + .storagebits = 64, \ + .endianness = IIO_CPU, \ + } \ +} + +#define PAC1934_VBUS_CHANNEL(_index, _si, _address) { \ + .type = IIO_VOLTAGE, \ + .address = (_address), \ + .indexed = 1, \ + .channel = (_index), \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ + BIT(IIO_CHAN_INFO_SCALE), \ + .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ), \ + .info_mask_shared_by_all_available = BIT(IIO_CHAN_INFO_SAMP_FREQ), \ + .scan_index = (_si), \ + .scan_type = { \ + .sign = 'u', \ + .realbits = 16, \ + .storagebits = 16, \ + .endianness = IIO_CPU, \ + } \ +} + +#define PAC1934_VBUS_AVG_CHANNEL(_index, _si, _address) { \ + .type = IIO_VOLTAGE, \ + .address = (_address), \ + .indexed = 1, \ + .channel = (_index), \ + .info_mask_separate = BIT(IIO_CHAN_INFO_AVERAGE_RAW) | \ + BIT(IIO_CHAN_INFO_SCALE), \ + .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ), \ + .info_mask_shared_by_all_available = BIT(IIO_CHAN_INFO_SAMP_FREQ), \ + .scan_index = (_si), \ + .scan_type = { \ + .sign = 'u', \ + .realbits = 16, \ + .storagebits = 16, \ + .endianness = IIO_CPU, \ + } \ +} + +#define PAC1934_VSENSE_CHANNEL(_index, _si, _address) { \ + .type = IIO_CURRENT, \ + .address = (_address), \ + .indexed = 1, \ + .channel = (_index), \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ + BIT(IIO_CHAN_INFO_SCALE), \ + .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ), \ + .info_mask_shared_by_all_available = BIT(IIO_CHAN_INFO_SAMP_FREQ), \ + .scan_index = (_si), \ + .scan_type = { \ + .sign = 'u', \ + .realbits = 16, \ + .storagebits = 16, \ + .endianness = IIO_CPU, \ + } \ +} + +#define PAC1934_VSENSE_AVG_CHANNEL(_index, _si, _address) { \ + .type = IIO_CURRENT, \ + .address = (_address), \ + .indexed = 1, \ + .channel = (_index), \ + .info_mask_separate = BIT(IIO_CHAN_INFO_AVERAGE_RAW) | \ + BIT(IIO_CHAN_INFO_SCALE), \ + .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ), \ + .info_mask_shared_by_all_available = BIT(IIO_CHAN_INFO_SAMP_FREQ), \ + .scan_index = (_si), \ + .scan_type = { \ + .sign = 'u', \ + .realbits = 16, \ + .storagebits = 16, \ + .endianness = IIO_CPU, \ + } \ +} + +#define PAC1934_VPOWER_CHANNEL(_index, _si, _address) { \ + .type = IIO_POWER, \ + .address = (_address), \ + .indexed = 1, \ + .channel = (_index), \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ + BIT(IIO_CHAN_INFO_SCALE), \ + .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ), \ + .info_mask_shared_by_all_available = BIT(IIO_CHAN_INFO_SAMP_FREQ), \ + .scan_index = (_si), \ + .scan_type = { \ + .sign = 'u', \ + .realbits = 28, \ + .storagebits = 32, \ + .shift = 4, \ + .endianness = IIO_CPU, \ + } \ +} + +static const struct iio_chan_spec pac1934_single_channel[] = { + PAC1934_VPOWER_ACC_CHANNEL(0, 0, PAC1934_VPOWER_ACC_1_ADDR), + PAC1934_VPOWER_CHANNEL(0, 0, PAC1934_VPOWER_1_ADDR), + PAC1934_VBUS_CHANNEL(0, 0, PAC1934_VBUS_1_ADDR), + PAC1934_VSENSE_CHANNEL(0, 0, PAC1934_VSENSE_1_ADDR), + PAC1934_VBUS_AVG_CHANNEL(0, 0, PAC1934_VBUS_AVG_1_ADDR), + PAC1934_VSENSE_AVG_CHANNEL(0, 0, PAC1934_VSENSE_AVG_1_ADDR), +}; + +/* Low-level I2c functions used to transfer up to 76 bytes at once */ +static int pac1934_i2c_read(struct i2c_client *client, u8 reg_addr, + void *databuf, u8 len) +{ + int ret; + struct i2c_msg msgs[2] = { + { + .addr = client->addr, + .len = 1, + .buf = (u8 *)®_addr, + }, + { + .addr = client->addr, + .len = len, + .buf = databuf, + .flags = I2C_M_RD + } + }; + + ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); + if (ret < 0) + return ret; + + return 0; +} + +static int pac1934_get_samp_rate_idx(struct pac1934_chip_info *info, + u32 new_samp_rate) +{ + int cnt; + + for (cnt = 0; cnt < ARRAY_SIZE(samp_rate_map_tbl); cnt++) + if (new_samp_rate == samp_rate_map_tbl[cnt]) + return cnt; + + /* not a valid sample rate value */ + return -EINVAL; +} + +static ssize_t pac1934_shunt_value_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct iio_dev *indio_dev = dev_to_iio_dev(dev); + struct pac1934_chip_info *info = iio_priv(indio_dev); + struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); + + return sysfs_emit(buf, "%u\n", info->shunts[this_attr->address]); +} + +static ssize_t pac1934_shunt_value_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct iio_dev *indio_dev = dev_to_iio_dev(dev); + struct pac1934_chip_info *info = iio_priv(indio_dev); + struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); + int sh_val; + + if (kstrtouint(buf, 10, &sh_val)) { + dev_err(dev, "Shunt value is not valid\n"); + return -EINVAL; + } + + scoped_guard(mutex, &info->lock) + info->shunts[this_attr->address] = sh_val; + + return count; +} + +static int pac1934_read_avail(struct iio_dev *indio_dev, + struct iio_chan_spec const *channel, + const int **vals, int *type, int *length, long mask) +{ + switch (mask) { + case IIO_CHAN_INFO_SAMP_FREQ: + *type = IIO_VAL_INT; + *vals = samp_rate_map_tbl; + *length = ARRAY_SIZE(samp_rate_map_tbl); + return IIO_AVAIL_LIST; + } + + return -EINVAL; +} + +static int pac1934_send_refresh(struct pac1934_chip_info *info, + u8 refresh_cmd, u32 wait_time) +{ + /* this function only sends REFRESH or REFRESH_V */ + struct i2c_client *client = info->client; + int ret; + u8 bidir_reg; + bool revision_bug = false; + + if (info->chip_revision == 2 || info->chip_revision == 3) { + /* + * chip rev 2 and 3 bug workaround + * see: PAC1934 Family Data Sheet Errata DS80000836A.pdf + */ + revision_bug = true; + + bidir_reg = + FIELD_PREP(PAC1934_NEG_PWR_CH1_BIDI_MASK, info->bi_dir[0]) | + FIELD_PREP(PAC1934_NEG_PWR_CH2_BIDI_MASK, info->bi_dir[1]) | + FIELD_PREP(PAC1934_NEG_PWR_CH3_BIDI_MASK, info->bi_dir[2]) | + FIELD_PREP(PAC1934_NEG_PWR_CH4_BIDI_MASK, info->bi_dir[3]) | + FIELD_PREP(PAC1934_NEG_PWR_CH1_BIDV_MASK, info->bi_dir[0]) | + FIELD_PREP(PAC1934_NEG_PWR_CH2_BIDV_MASK, info->bi_dir[1]) | + FIELD_PREP(PAC1934_NEG_PWR_CH3_BIDV_MASK, info->bi_dir[2]) | + FIELD_PREP(PAC1934_NEG_PWR_CH4_BIDV_MASK, info->bi_dir[3]); + + ret = i2c_smbus_write_byte_data(client, + PAC1934_CTRL_STAT_REGS_ADDR + + PAC1934_NEG_PWR_REG_OFF, + bidir_reg); + if (ret) + return ret; + } + + ret = i2c_smbus_write_byte(client, refresh_cmd); + if (ret) { + dev_err(&client->dev, "%s - cannot send 0x%02X\n", + __func__, refresh_cmd); + return ret; + } + + if (revision_bug) { + /* + * chip rev 2 and 3 bug workaround - write again the same + * register write the updated registers back + */ + ret = i2c_smbus_write_byte_data(client, + PAC1934_CTRL_STAT_REGS_ADDR + + PAC1934_NEG_PWR_REG_OFF, bidir_reg); + if (ret) + return ret; + } + + /* register data retrieval timestamp */ + info->tstamp = jiffies; + + /* wait till the data is available */ + usleep_range(wait_time, wait_time + 100); + + return ret; +} + +static int pac1934_reg_snapshot(struct pac1934_chip_info *info, + bool do_refresh, u8 refresh_cmd, u32 wait_time) +{ + int ret; + struct i2c_client *client = info->client; + u8 samp_shift, ctrl_regs_tmp; + u8 *offset_reg_data_p; + u16 tmp_value; + u32 samp_rate, cnt, tmp; + s64 curr_energy, inc; + u64 tmp_energy; + struct reg_data *reg_data; + + guard(mutex)(&info->lock); + + if (do_refresh) { + ret = pac1934_send_refresh(info, refresh_cmd, wait_time); + if (ret < 0) { + dev_err(&client->dev, + "%s - cannot send refresh\n", + __func__); + return ret; + } + } + + ret = i2c_smbus_read_i2c_block_data(client, PAC1934_CTRL_STAT_REGS_ADDR, + PAC1934_CTRL_REG_LEN, + (u8 *)info->chip_reg_data.ctrl_regs); + if (ret < 0) { + dev_err(&client->dev, + "%s - cannot read ctrl/status registers\n", + __func__); + return ret; + } + + reg_data = &info->chip_reg_data; + + /* read the data registers */ + ret = pac1934_i2c_read(client, PAC1934_ACC_COUNT_REG_ADDR, + (u8 *)reg_data->meas_regs, PAC1934_MEAS_REG_LEN); + if (ret) { + dev_err(&client->dev, + "%s - cannot read ACC_COUNT register: %d:%d\n", + __func__, ret, PAC1934_MEAS_REG_LEN); + return ret; + } + + /* see how much shift is required by the sample rate */ + samp_rate = samp_rate_map_tbl[((reg_data->ctrl_regs[PAC1934_CTRL_LAT_REG_OFF]) >> 6)]; + samp_shift = get_count_order(samp_rate); + + ctrl_regs_tmp = reg_data->ctrl_regs[PAC1934_CHANNEL_DIS_LAT_REG_OFF]; + offset_reg_data_p = ®_data->meas_regs[PAC1934_ACC_REG_LEN]; + + /* start with VPOWER_ACC */ + for (cnt = 0; cnt < info->phys_channels; cnt++) { + /* check if the channel is active, skip all fields if disabled */ + if ((ctrl_regs_tmp << cnt) & 0x80) + continue; + + /* skip if the energy accumulation is disabled */ + if (info->enable_energy[cnt]) { + curr_energy = info->chip_reg_data.energy_sec_acc[cnt]; + + tmp_energy = get_unaligned_be48(offset_reg_data_p); + + if (info->bi_dir[cnt]) + reg_data->vpower_acc[cnt] = sign_extend64(tmp_energy, 47); + else + reg_data->vpower_acc[cnt] = tmp_energy; + + /* + * compute the scaled to 1 second accumulated energy value; + * energy accumulator scaled to 1sec = VPOWER_ACC/2^samp_shift + * the chip's sampling rate is 2^samp_shift samples/sec + */ + inc = (reg_data->vpower_acc[cnt] >> samp_shift); + + /* add the power_acc field */ + curr_energy += inc; + + reg_data->energy_sec_acc[cnt] = clamp(curr_energy, + PAC_193X_MIN_POWER_ACC, + PAC_193X_MAX_POWER_ACC); + } + + offset_reg_data_p += PAC1934_VPOWER_ACC_REG_LEN; + } + + /* continue with VBUS */ + for (cnt = 0; cnt < info->phys_channels; cnt++) { + if ((ctrl_regs_tmp << cnt) & 0x80) + continue; + + tmp_value = get_unaligned_be16(offset_reg_data_p); + + if (info->bi_dir[cnt]) + reg_data->vbus[cnt] = sign_extend32((u32)(tmp_value), 15); + else + reg_data->vbus[cnt] = tmp_value; + + offset_reg_data_p += PAC1934_VBUS_SENSE_REG_LEN; + } + + /* VSENSE */ + for (cnt = 0; cnt < info->phys_channels; cnt++) { + if ((ctrl_regs_tmp << cnt) & 0x80) + continue; + + tmp_value = get_unaligned_be16(offset_reg_data_p); + + if (info->bi_dir[cnt]) + reg_data->vsense[cnt] = sign_extend32((u32)(tmp_value), 15); + else + reg_data->vsense[cnt] = tmp_value; + + offset_reg_data_p += PAC1934_VBUS_SENSE_REG_LEN; + } + + /* VBUS_AVG */ + for (cnt = 0; cnt < info->phys_channels; cnt++) { + if ((ctrl_regs_tmp << cnt) & 0x80) + continue; + + tmp_value = get_unaligned_be16(offset_reg_data_p); + + if (info->bi_dir[cnt]) + reg_data->vbus_avg[cnt] = sign_extend32((u32)(tmp_value), 15); + else + reg_data->vbus_avg[cnt] = tmp_value; + + offset_reg_data_p += PAC1934_VBUS_SENSE_REG_LEN; + } + + /* VSENSE_AVG */ + for (cnt = 0; cnt < info->phys_channels; cnt++) { + if ((ctrl_regs_tmp << cnt) & 0x80) + continue; + + tmp_value = get_unaligned_be16(offset_reg_data_p); + + if (info->bi_dir[cnt]) + reg_data->vsense_avg[cnt] = sign_extend32((u32)(tmp_value), 15); + else + reg_data->vsense_avg[cnt] = tmp_value; + + offset_reg_data_p += PAC1934_VBUS_SENSE_REG_LEN; + } + + /* VPOWER */ + for (cnt = 0; cnt < info->phys_channels; cnt++) { + if ((ctrl_regs_tmp << cnt) & 0x80) + continue; + + tmp = get_unaligned_be32(offset_reg_data_p) >> 4; + + if (info->bi_dir[cnt]) + reg_data->vpower[cnt] = sign_extend32(tmp, 27); + else + reg_data->vpower[cnt] = tmp; + + offset_reg_data_p += PAC1934_VPOWER_REG_LEN; + } + + return 0; +} + +static int pac1934_retrieve_data(struct pac1934_chip_info *info, + u32 wait_time) +{ + int ret = 0; + + /* + * check if the minimal elapsed time has passed and if so, + * re-read the chip, otherwise the cached info is just fine + */ + if (time_after(jiffies, info->tstamp + msecs_to_jiffies(PAC1934_MIN_POLLING_TIME_MS))) { + ret = pac1934_reg_snapshot(info, true, PAC1934_REFRESH_REG_ADDR, + wait_time); + + /* + * Re-schedule the work for the read registers on timeout + * (to prevent chip registers saturation) + */ + mod_delayed_work(system_percpu_wq, &info->work_chip_rfsh, + msecs_to_jiffies(PAC1934_MAX_RFSH_LIMIT_MS)); + } + + return ret; +} + +static int pac1934_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int *val, + int *val2, long mask) +{ + struct pac1934_chip_info *info = iio_priv(indio_dev); + s64 curr_energy; + int ret, channel = chan->channel - 1; + + /* + * For AVG the index should be between 5 to 8. + * To calculate PAC1934_CH_VOLTAGE_AVERAGE, + * respectively PAC1934_CH_CURRENT real index, we need + * to remove the added offset (PAC1934_MAX_NUM_CHANNELS). + */ + if (channel >= PAC1934_MAX_NUM_CHANNELS) + channel = channel - PAC1934_MAX_NUM_CHANNELS; + + ret = pac1934_retrieve_data(info, PAC1934_MIN_UPDATE_WAIT_TIME_US); + if (ret < 0) + return ret; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + switch (chan->type) { + case IIO_VOLTAGE: + *val = info->chip_reg_data.vbus[channel]; + return IIO_VAL_INT; + case IIO_CURRENT: + *val = info->chip_reg_data.vsense[channel]; + return IIO_VAL_INT; + case IIO_POWER: + *val = info->chip_reg_data.vpower[channel]; + return IIO_VAL_INT; + case IIO_ENERGY: + curr_energy = info->chip_reg_data.energy_sec_acc[channel]; + *val = (u32)curr_energy; + *val2 = (u32)(curr_energy >> 32); + return IIO_VAL_INT_64; + default: + return -EINVAL; + } + case IIO_CHAN_INFO_AVERAGE_RAW: + switch (chan->type) { + case IIO_VOLTAGE: + *val = info->chip_reg_data.vbus_avg[channel]; + return IIO_VAL_INT; + case IIO_CURRENT: + *val = info->chip_reg_data.vsense_avg[channel]; + return IIO_VAL_INT; + default: + return -EINVAL; + } + case IIO_CHAN_INFO_SCALE: + switch (chan->address) { + /* Voltages - scale for millivolts */ + case PAC1934_VBUS_1_ADDR: + case PAC1934_VBUS_2_ADDR: + case PAC1934_VBUS_3_ADDR: + case PAC1934_VBUS_4_ADDR: + case PAC1934_VBUS_AVG_1_ADDR: + case PAC1934_VBUS_AVG_2_ADDR: + case PAC1934_VBUS_AVG_3_ADDR: + case PAC1934_VBUS_AVG_4_ADDR: + *val = PAC1934_VOLTAGE_MILLIVOLTS_MAX; + if (chan->scan_type.sign == 'u') + *val2 = PAC1934_VOLTAGE_U_RES; + else + *val2 = PAC1934_VOLTAGE_S_RES; + return IIO_VAL_FRACTIONAL_LOG2; + /* + * Currents - scale for mA - depends on the + * channel's shunt value + * (100mV * 1000000) / (2^16 * shunt(uohm)) + */ + case PAC1934_VSENSE_1_ADDR: + case PAC1934_VSENSE_2_ADDR: + case PAC1934_VSENSE_3_ADDR: + case PAC1934_VSENSE_4_ADDR: + case PAC1934_VSENSE_AVG_1_ADDR: + case PAC1934_VSENSE_AVG_2_ADDR: + case PAC1934_VSENSE_AVG_3_ADDR: + case PAC1934_VSENSE_AVG_4_ADDR: + *val = PAC1934_MAX_VSENSE_RSHIFTED_BY_16B; + if (chan->scan_type.sign == 'u') + *val2 = info->shunts[channel]; + else + *val2 = info->shunts[channel] >> 1; + return IIO_VAL_FRACTIONAL; + /* + * Power - uW - it will use the combined scale + * for current and voltage + * current(mA) * voltage(mV) = power (uW) + */ + case PAC1934_VPOWER_1_ADDR: + case PAC1934_VPOWER_2_ADDR: + case PAC1934_VPOWER_3_ADDR: + case PAC1934_VPOWER_4_ADDR: + *val = PAC1934_MAX_VPOWER_RSHIFTED_BY_28B; + if (chan->scan_type.sign == 'u') + *val2 = info->shunts[channel]; + else + *val2 = info->shunts[channel] >> 1; + return IIO_VAL_FRACTIONAL; + case PAC1934_VPOWER_ACC_1_ADDR: + case PAC1934_VPOWER_ACC_2_ADDR: + case PAC1934_VPOWER_ACC_3_ADDR: + case PAC1934_VPOWER_ACC_4_ADDR: + /* + * expresses the 32 bit scale value here compute + * the scale for energy (miliWatt-second or miliJoule) + */ + *val = PAC1934_SCALE_CONSTANT; + + if (chan->scan_type.sign == 'u') + *val2 = info->shunts[channel]; + else + *val2 = info->shunts[channel] >> 1; + return IIO_VAL_FRACTIONAL; + default: + return -EINVAL; + } + case IIO_CHAN_INFO_SAMP_FREQ: + *val = info->sample_rate_value; + return IIO_VAL_INT; + case IIO_CHAN_INFO_ENABLE: + *val = info->enable_energy[channel]; + return IIO_VAL_INT; + default: + return -EINVAL; + } +} + +static int pac1934_write_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, + int val, int val2, long mask) +{ + struct pac1934_chip_info *info = iio_priv(indio_dev); + struct i2c_client *client = info->client; + int ret = -EINVAL; + s32 old_samp_rate; + u8 ctrl_reg; + + switch (mask) { + case IIO_CHAN_INFO_SAMP_FREQ: + ret = pac1934_get_samp_rate_idx(info, val); + if (ret < 0) + return ret; + + /* write the new sampling value and trigger a snapshot(incl refresh) */ + scoped_guard(mutex, &info->lock) { + ctrl_reg = FIELD_PREP(PAC1934_CRTL_SAMPLE_RATE_MASK, ret); + ret = i2c_smbus_write_byte_data(client, PAC1934_CTRL_REG_ADDR, ctrl_reg); + if (ret) { + dev_err(&client->dev, + "%s - can't update sample rate\n", + __func__); + return ret; + } + } + + old_samp_rate = info->sample_rate_value; + info->sample_rate_value = val; + + /* + * now, force a snapshot with refresh - call retrieve + * data in order to update the refresh timer + * alter the timestamp in order to force trigger a + * register snapshot and a timestamp update + */ + info->tstamp -= msecs_to_jiffies(PAC1934_MIN_POLLING_TIME_MS); + ret = pac1934_retrieve_data(info, (1024 / old_samp_rate) * 1000); + if (ret < 0) { + dev_err(&client->dev, + "%s - cannot snapshot ctrl and measurement regs\n", + __func__); + return ret; + } + + return 0; + case IIO_CHAN_INFO_ENABLE: + scoped_guard(mutex, &info->lock) { + info->enable_energy[chan->channel - 1] = val ? true : false; + if (!val) + info->chip_reg_data.energy_sec_acc[chan->channel - 1] = 0; + } + + return 0; + default: + return -EINVAL; + } +} + +static int pac1934_read_label(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, char *label) +{ + struct pac1934_chip_info *info = iio_priv(indio_dev); + + switch (chan->address) { + case PAC1934_VBUS_1_ADDR: + case PAC1934_VBUS_2_ADDR: + case PAC1934_VBUS_3_ADDR: + case PAC1934_VBUS_4_ADDR: + return sysfs_emit(label, "%s_VBUS_%d\n", + info->labels[chan->scan_index], + chan->scan_index + 1); + case PAC1934_VBUS_AVG_1_ADDR: + case PAC1934_VBUS_AVG_2_ADDR: + case PAC1934_VBUS_AVG_3_ADDR: + case PAC1934_VBUS_AVG_4_ADDR: + return sysfs_emit(label, "%s_VBUS_AVG_%d\n", + info->labels[chan->scan_index], + chan->scan_index + 1); + case PAC1934_VSENSE_1_ADDR: + case PAC1934_VSENSE_2_ADDR: + case PAC1934_VSENSE_3_ADDR: + case PAC1934_VSENSE_4_ADDR: + return sysfs_emit(label, "%s_IBUS_%d\n", + info->labels[chan->scan_index], + chan->scan_index + 1); + case PAC1934_VSENSE_AVG_1_ADDR: + case PAC1934_VSENSE_AVG_2_ADDR: + case PAC1934_VSENSE_AVG_3_ADDR: + case PAC1934_VSENSE_AVG_4_ADDR: + return sysfs_emit(label, "%s_IBUS_AVG_%d\n", + info->labels[chan->scan_index], + chan->scan_index + 1); + case PAC1934_VPOWER_1_ADDR: + case PAC1934_VPOWER_2_ADDR: + case PAC1934_VPOWER_3_ADDR: + case PAC1934_VPOWER_4_ADDR: + return sysfs_emit(label, "%s_POWER_%d\n", + info->labels[chan->scan_index], + chan->scan_index + 1); + case PAC1934_VPOWER_ACC_1_ADDR: + case PAC1934_VPOWER_ACC_2_ADDR: + case PAC1934_VPOWER_ACC_3_ADDR: + case PAC1934_VPOWER_ACC_4_ADDR: + return sysfs_emit(label, "%s_ENERGY_%d\n", + info->labels[chan->scan_index], + chan->scan_index + 1); + } + + return 0; +} + +static void pac1934_work_periodic_rfsh(struct work_struct *work) +{ + struct pac1934_chip_info *info = TO_PAC1934_CHIP_INFO((struct delayed_work *)work); + struct device *dev = &info->client->dev; + + dev_dbg(dev, "%s - Periodic refresh\n", __func__); + + /* do a REFRESH, then read */ + pac1934_reg_snapshot(info, true, PAC1934_REFRESH_REG_ADDR, + PAC1934_MIN_UPDATE_WAIT_TIME_US); + + schedule_delayed_work(&info->work_chip_rfsh, + msecs_to_jiffies(PAC1934_MAX_RFSH_LIMIT_MS)); +} + +static int pac1934_read_revision(struct pac1934_chip_info *info, u8 *buf) +{ + int ret; + struct i2c_client *client = info->client; + + ret = i2c_smbus_read_i2c_block_data(client, PAC1934_PID_REG_ADDR, + PAC1934_ID_REG_LEN, + buf); + if (ret < 0) { + dev_err(&client->dev, "cannot read revision\n"); + return ret; + } + + return 0; +} + +static int pac1934_chip_identify(struct pac1934_chip_info *info) +{ + u8 rev_info[PAC1934_ID_REG_LEN]; + struct device *dev = &info->client->dev; + int ret = 0; + + ret = pac1934_read_revision(info, (u8 *)rev_info); + if (ret) + return ret; + + info->chip_variant = rev_info[PAC1934_PID_IDX]; + info->chip_revision = rev_info[PAC1934_RID_IDX]; + + dev_dbg(dev, "Chip variant: 0x%02X\n", info->chip_variant); + dev_dbg(dev, "Chip revision: 0x%02X\n", info->chip_revision); + + switch (info->chip_variant) { + case PAC1934_PID: + return PAC1934; + case PAC1933_PID: + return PAC1933; + case PAC1932_PID: + return PAC1932; + case PAC1931_PID: + return PAC1931; + default: + return -EINVAL; + } +} + +/* + * documentation related to the ACPI device definition + * https://ww1.microchip.com/downloads/aemDocuments/documents/OTH/ApplicationNotes/ApplicationNotes/PAC193X-Integration-Notes-for-Microsoft-Windows-10-and-Windows-11-Driver-Support-DS00002534.pdf + */ +static int pac1934_acpi_parse_channel_config(struct i2c_client *client, + struct pac1934_chip_info *info) +{ + acpi_handle handle; + union acpi_object *rez; + struct device *dev = &client->dev; + unsigned short bi_dir_mask; + int idx, i; + guid_t guid; + + handle = ACPI_HANDLE(dev); + + guid_parse(PAC1934_DSM_UUID, &guid); + + rez = acpi_evaluate_dsm(handle, &guid, 0, PAC1934_ACPI_GET_NAMES_AND_MOHMS_VALS, NULL); + if (!rez) + return -EINVAL; + + for (i = 0; i < rez->package.count; i += 2) { + idx = i / 2; + info->labels[idx] = + devm_kmemdup(dev, rez->package.elements[i].string.pointer, + (size_t)rez->package.elements[i].string.length + 1, + GFP_KERNEL); + info->labels[idx][rez->package.elements[i].string.length] = '\0'; + info->shunts[idx] = rez->package.elements[i + 1].integer.value * 1000; + info->active_channels[idx] = (info->shunts[idx] != 0); + } + + ACPI_FREE(rez); + + rez = acpi_evaluate_dsm(handle, &guid, 1, PAC1934_ACPI_GET_UOHMS_VALS, NULL); + if (!rez) { + /* + * initializing with default values + * we assume all channels are unidirectional(the mask is zero) + * and assign the default sampling rate + */ + info->sample_rate_value = PAC1934_DEFAULT_CHIP_SAMP_SPEED_HZ; + return 0; + } + + for (i = 0; i < rez->package.count; i++) { + idx = i; + info->shunts[idx] = rez->package.elements[i].integer.value; + info->active_channels[idx] = (info->shunts[idx] != 0); + } + + ACPI_FREE(rez); + + rez = acpi_evaluate_dsm(handle, &guid, 1, PAC1934_ACPI_GET_BIPOLAR_SETTINGS, NULL); + if (!rez) + return -EINVAL; + + bi_dir_mask = rez->package.elements[0].integer.value; + info->bi_dir[0] = ((bi_dir_mask & (1 << 3)) | (bi_dir_mask & (1 << 7))) != 0; + info->bi_dir[1] = ((bi_dir_mask & (1 << 2)) | (bi_dir_mask & (1 << 6))) != 0; + info->bi_dir[2] = ((bi_dir_mask & (1 << 1)) | (bi_dir_mask & (1 << 5))) != 0; + info->bi_dir[3] = ((bi_dir_mask & (1 << 0)) | (bi_dir_mask & (1 << 4))) != 0; + + ACPI_FREE(rez); + + rez = acpi_evaluate_dsm(handle, &guid, 1, PAC1934_ACPI_GET_SAMP, NULL); + if (!rez) + return -EINVAL; + + info->sample_rate_value = rez->package.elements[0].integer.value; + + ACPI_FREE(rez); + + return 0; +} + +static int pac1934_fw_parse_channel_config(struct i2c_client *client, + struct pac1934_chip_info *info) +{ + struct device *dev = &client->dev; + unsigned int current_channel; + int idx, ret; + + info->sample_rate_value = 1024; + current_channel = 1; + + device_for_each_child_node_scoped(dev, node) { + ret = fwnode_property_read_u32(node, "reg", &idx); + if (ret) + return dev_err_probe(dev, ret, + "reading invalid channel index\n"); + + /* adjust idx to match channel index (1 to 4) from the datasheet */ + idx--; + + if (current_channel >= (info->phys_channels + 1) || + idx >= info->phys_channels || idx < 0) + return dev_err_probe(dev, -EINVAL, + "%s: invalid channel_index %d value\n", + fwnode_get_name(node), idx); + + /* enable channel */ + info->active_channels[idx] = true; + + ret = fwnode_property_read_u32(node, "shunt-resistor-micro-ohms", + &info->shunts[idx]); + if (ret) + return dev_err_probe(dev, ret, + "%s: invalid shunt-resistor value: %d\n", + fwnode_get_name(node), info->shunts[idx]); + + if (fwnode_property_present(node, "label")) { + ret = fwnode_property_read_string(node, "label", + (const char **)&info->labels[idx]); + if (ret) + return dev_err_probe(dev, ret, + "%s: invalid rail-name value\n", + fwnode_get_name(node)); + } + + info->bi_dir[idx] = fwnode_property_read_bool(node, "bipolar"); + + current_channel++; + } + + return 0; +} + +static void pac1934_cancel_delayed_work(void *dwork) +{ + cancel_delayed_work_sync(dwork); +} + +static int pac1934_chip_configure(struct pac1934_chip_info *info) +{ + int cnt, ret; + struct i2c_client *client = info->client; + u8 regs[PAC1934_CTRL_STATUS_INFO_LEN], idx, ctrl_reg; + u32 wait_time; + + info->chip_reg_data.num_enabled_channels = 0; + for (cnt = 0; cnt < info->phys_channels; cnt++) { + if (info->active_channels[cnt]) + info->chip_reg_data.num_enabled_channels++; + } + + /* + * read whatever information was gathered before the driver was loaded + * establish which channels are enabled/disabled and then establish the + * information retrieval mode (using SKIP or no). + * Read the chip ID values + */ + ret = i2c_smbus_read_i2c_block_data(client, PAC1934_CTRL_STAT_REGS_ADDR, + ARRAY_SIZE(regs), + (u8 *)regs); + if (ret < 0) { + dev_err_probe(&client->dev, ret, + "%s - cannot read regs from 0x%02X\n", + __func__, PAC1934_CTRL_STAT_REGS_ADDR); + return ret; + } + + /* write the CHANNEL_DIS and the NEG_PWR registers */ + regs[PAC1934_CHANNEL_DIS_REG_OFF] = + FIELD_PREP(PAC1934_CHAN_DIS_CH1_OFF_MASK, info->active_channels[0] ? 0 : 1) | + FIELD_PREP(PAC1934_CHAN_DIS_CH2_OFF_MASK, info->active_channels[1] ? 0 : 1) | + FIELD_PREP(PAC1934_CHAN_DIS_CH3_OFF_MASK, info->active_channels[2] ? 0 : 1) | + FIELD_PREP(PAC1934_CHAN_DIS_CH4_OFF_MASK, info->active_channels[3] ? 0 : 1) | + FIELD_PREP(PAC1934_SMBUS_TIMEOUT_MASK, 0) | + FIELD_PREP(PAC1934_SMBUS_BYTECOUNT_MASK, 0) | + FIELD_PREP(PAC1934_SMBUS_NO_SKIP_MASK, 0); + + regs[PAC1934_NEG_PWR_REG_OFF] = + FIELD_PREP(PAC1934_NEG_PWR_CH1_BIDI_MASK, info->bi_dir[0]) | + FIELD_PREP(PAC1934_NEG_PWR_CH2_BIDI_MASK, info->bi_dir[1]) | + FIELD_PREP(PAC1934_NEG_PWR_CH3_BIDI_MASK, info->bi_dir[2]) | + FIELD_PREP(PAC1934_NEG_PWR_CH4_BIDI_MASK, info->bi_dir[3]) | + FIELD_PREP(PAC1934_NEG_PWR_CH1_BIDV_MASK, info->bi_dir[0]) | + FIELD_PREP(PAC1934_NEG_PWR_CH2_BIDV_MASK, info->bi_dir[1]) | + FIELD_PREP(PAC1934_NEG_PWR_CH3_BIDV_MASK, info->bi_dir[2]) | + FIELD_PREP(PAC1934_NEG_PWR_CH4_BIDV_MASK, info->bi_dir[3]); + + /* no SLOW triggered REFRESH, clear POR */ + regs[PAC1934_SLOW_REG_OFF] = 0; + + /* + * Write the three bytes sequentially, as the device does not support + * block write. + */ + ret = i2c_smbus_write_byte_data(client, PAC1934_CTRL_STAT_REGS_ADDR, + regs[PAC1934_CHANNEL_DIS_REG_OFF]); + if (ret) + return ret; + + ret = i2c_smbus_write_byte_data(client, + PAC1934_CTRL_STAT_REGS_ADDR + PAC1934_NEG_PWR_REG_OFF, + regs[PAC1934_NEG_PWR_REG_OFF]); + if (ret) + return ret; + + ret = i2c_smbus_write_byte_data(client, PAC1934_SLOW_REG_ADDR, + regs[PAC1934_SLOW_REG_OFF]); + if (ret) + return ret; + + /* Default sampling rate */ + ctrl_reg = FIELD_PREP(PAC1934_CRTL_SAMPLE_RATE_MASK, PAC1934_SAMP_1024SPS); + + ret = i2c_smbus_write_byte_data(client, PAC1934_CTRL_REG_ADDR, ctrl_reg); + if (ret) + return ret; + + /* + * send a REFRESH to the chip, so the new settings take place + * as well as resetting the accumulators + */ + ret = i2c_smbus_write_byte(client, PAC1934_REFRESH_REG_ADDR); + if (ret) { + dev_err(&client->dev, + "%s - cannot send 0x%02X\n", + __func__, PAC1934_REFRESH_REG_ADDR); + return ret; + } + + /* + * get the current(in the chip) sampling speed and compute the + * required timeout based on its value + * the timeout is 1/sampling_speed + */ + idx = regs[PAC1934_CTRL_ACT_REG_OFF] >> PAC1934_SAMPLE_RATE_SHIFT; + wait_time = (1024 / samp_rate_map_tbl[idx]) * 1000; + + /* + * wait the maximum amount of time to be on the safe side + * the maximum wait time is for 8sps + */ + usleep_range(wait_time, wait_time + 100); + + INIT_DELAYED_WORK(&info->work_chip_rfsh, pac1934_work_periodic_rfsh); + /* Setup the latest moment for reading the regs before saturation */ + schedule_delayed_work(&info->work_chip_rfsh, + msecs_to_jiffies(PAC1934_MAX_RFSH_LIMIT_MS)); + + return devm_add_action_or_reset(&client->dev, pac1934_cancel_delayed_work, + &info->work_chip_rfsh); +} + +static int pac1934_prep_iio_channels(struct pac1934_chip_info *info, struct iio_dev *indio_dev) +{ + struct iio_chan_spec *ch_sp; + int channel_size, attribute_count, cnt; + void *dyn_ch_struct, *tmp_data; + struct device *dev = &info->client->dev; + + /* find out dynamically how many IIO channels we need */ + attribute_count = 0; + channel_size = 0; + for (cnt = 0; cnt < info->phys_channels; cnt++) { + if (!info->active_channels[cnt]) + continue; + + /* add the size of the properties of one chip physical channel */ + channel_size += sizeof(pac1934_single_channel); + /* count how many enabled channels we have */ + attribute_count += ARRAY_SIZE(pac1934_single_channel); + dev_dbg(dev, ":%s: Channel %d active\n", __func__, cnt + 1); + } + + dyn_ch_struct = devm_kzalloc(dev, channel_size, GFP_KERNEL); + if (!dyn_ch_struct) + return -ENOMEM; + + tmp_data = dyn_ch_struct; + + /* populate the dynamic channels and make all the adjustments */ + for (cnt = 0; cnt < info->phys_channels; cnt++) { + if (!info->active_channels[cnt]) + continue; + + memcpy(tmp_data, pac1934_single_channel, sizeof(pac1934_single_channel)); + ch_sp = (struct iio_chan_spec *)tmp_data; + ch_sp[PAC1934_CH_ENERGY].channel = cnt + 1; + ch_sp[PAC1934_CH_ENERGY].scan_index = cnt; + ch_sp[PAC1934_CH_ENERGY].address = cnt + PAC1934_VPOWER_ACC_1_ADDR; + ch_sp[PAC1934_CH_POWER].channel = cnt + 1; + ch_sp[PAC1934_CH_POWER].scan_index = cnt; + ch_sp[PAC1934_CH_POWER].address = cnt + PAC1934_VPOWER_1_ADDR; + ch_sp[PAC1934_CH_VOLTAGE].channel = cnt + 1; + ch_sp[PAC1934_CH_VOLTAGE].scan_index = cnt; + ch_sp[PAC1934_CH_VOLTAGE].address = cnt + PAC1934_VBUS_1_ADDR; + ch_sp[PAC1934_CH_CURRENT].channel = cnt + 1; + ch_sp[PAC1934_CH_CURRENT].scan_index = cnt; + ch_sp[PAC1934_CH_CURRENT].address = cnt + PAC1934_VSENSE_1_ADDR; + + /* + * In order to be able to use labels for PAC1934_CH_VOLTAGE, and + * PAC1934_CH_VOLTAGE_AVERAGE,respectively PAC1934_CH_CURRENT + * and PAC1934_CH_CURRENT_AVERAGE we need to use different + * channel numbers. We will add +5 (+1 to maximum PAC channels). + */ + ch_sp[PAC1934_CH_VOLTAGE_AVERAGE].channel = cnt + 5; + ch_sp[PAC1934_CH_VOLTAGE_AVERAGE].scan_index = cnt; + ch_sp[PAC1934_CH_VOLTAGE_AVERAGE].address = cnt + PAC1934_VBUS_AVG_1_ADDR; + ch_sp[PAC1934_CH_CURRENT_AVERAGE].channel = cnt + 5; + ch_sp[PAC1934_CH_CURRENT_AVERAGE].scan_index = cnt; + ch_sp[PAC1934_CH_CURRENT_AVERAGE].address = cnt + PAC1934_VSENSE_AVG_1_ADDR; + + /* + * now modify the parameters in all channels if the + * whole chip rail(channel) is bi-directional + */ + if (info->bi_dir[cnt]) { + ch_sp[PAC1934_CH_ENERGY].scan_type.sign = 's'; + ch_sp[PAC1934_CH_ENERGY].scan_type.realbits = 47; + ch_sp[PAC1934_CH_POWER].scan_type.sign = 's'; + ch_sp[PAC1934_CH_POWER].scan_type.realbits = 27; + ch_sp[PAC1934_CH_VOLTAGE].scan_type.sign = 's'; + ch_sp[PAC1934_CH_VOLTAGE].scan_type.realbits = 15; + ch_sp[PAC1934_CH_CURRENT].scan_type.sign = 's'; + ch_sp[PAC1934_CH_CURRENT].scan_type.realbits = 15; + ch_sp[PAC1934_CH_VOLTAGE_AVERAGE].scan_type.sign = 's'; + ch_sp[PAC1934_CH_VOLTAGE_AVERAGE].scan_type.realbits = 15; + ch_sp[PAC1934_CH_CURRENT_AVERAGE].scan_type.sign = 's'; + ch_sp[PAC1934_CH_CURRENT_AVERAGE].scan_type.realbits = 15; + } + tmp_data += sizeof(pac1934_single_channel); + } + + /* + * send the updated dynamic channel structure information towards IIO + * prepare the required field for IIO class registration + */ + indio_dev->num_channels = attribute_count; + indio_dev->channels = (const struct iio_chan_spec *)dyn_ch_struct; + + return 0; +} + +static IIO_DEVICE_ATTR(in_shunt_resistor1, 0644, + pac1934_shunt_value_show, pac1934_shunt_value_store, 0); +static IIO_DEVICE_ATTR(in_shunt_resistor2, 0644, + pac1934_shunt_value_show, pac1934_shunt_value_store, 1); +static IIO_DEVICE_ATTR(in_shunt_resistor3, 0644, + pac1934_shunt_value_show, pac1934_shunt_value_store, 2); +static IIO_DEVICE_ATTR(in_shunt_resistor4, 0644, + pac1934_shunt_value_show, pac1934_shunt_value_store, 3); + +static int pac1934_prep_custom_attributes(struct pac1934_chip_info *info, + struct iio_dev *indio_dev) +{ + int i, active_channels_count = 0; + struct attribute **pac1934_custom_attr; + struct attribute_group *pac1934_group; + struct device *dev = &info->client->dev; + + for (i = 0 ; i < info->phys_channels; i++) + if (info->active_channels[i]) + active_channels_count++; + + pac1934_group = devm_kzalloc(dev, sizeof(*pac1934_group), GFP_KERNEL); + if (!pac1934_group) + return -ENOMEM; + + pac1934_custom_attr = devm_kzalloc(dev, + (PAC1934_CUSTOM_ATTR_FOR_CHANNEL * + active_channels_count) + * sizeof(*pac1934_group) + 1, + GFP_KERNEL); + if (!pac1934_custom_attr) + return -ENOMEM; + + i = 0; + if (info->active_channels[0]) + pac1934_custom_attr[i++] = PAC1934_DEV_ATTR(in_shunt_resistor1); + + if (info->active_channels[1]) + pac1934_custom_attr[i++] = PAC1934_DEV_ATTR(in_shunt_resistor2); + + if (info->active_channels[2]) + pac1934_custom_attr[i++] = PAC1934_DEV_ATTR(in_shunt_resistor3); + + if (info->active_channels[3]) + pac1934_custom_attr[i] = PAC1934_DEV_ATTR(in_shunt_resistor4); + + pac1934_group->attrs = pac1934_custom_attr; + info->iio_info.attrs = pac1934_group; + + return 0; +} + +static const struct iio_info pac1934_info = { + .read_raw = pac1934_read_raw, + .write_raw = pac1934_write_raw, + .read_avail = pac1934_read_avail, + .read_label = pac1934_read_label, +}; + +static int pac1934_probe(struct i2c_client *client) +{ + struct pac1934_chip_info *info; + const struct pac1934_features *chip; + struct iio_dev *indio_dev; + int cnt, ret; + struct device *dev = &client->dev; + + indio_dev = devm_iio_device_alloc(dev, sizeof(*info)); + if (!indio_dev) + return -ENOMEM; + + info = iio_priv(indio_dev); + + info->client = client; + + /* always start with energy accumulation enabled */ + for (cnt = 0; cnt < PAC1934_MAX_NUM_CHANNELS; cnt++) + info->enable_energy[cnt] = true; + + ret = pac1934_chip_identify(info); + if (ret < 0) { + /* + * If failed to identify the hardware based on internal + * registers, try using fallback compatible in device tree + * to deal with some newer part number. + */ + chip = i2c_get_match_data(client); + if (!chip) + return -EINVAL; + + info->phys_channels = chip->phys_channels; + indio_dev->name = chip->name; + } else { + info->phys_channels = pac1934_chip_config[ret].phys_channels; + indio_dev->name = pac1934_chip_config[ret].name; + } + + if (is_acpi_device_node(dev_fwnode(dev))) + ret = pac1934_acpi_parse_channel_config(client, info); + else + /* + * This makes it possible to use also ACPI PRP0001 for + * registering the device using device tree properties. + */ + ret = pac1934_fw_parse_channel_config(client, info); + + if (ret) + return dev_err_probe(dev, ret, + "parameter parsing returned an error\n"); + + ret = devm_mutex_init(dev, &info->lock); + if (ret < 0) + return ret; + + /* + * do now any chip specific initialization (e.g. read/write + * some registers), enable/disable certain channels, change the sampling + * rate to the requested value + */ + ret = pac1934_chip_configure(info); + if (ret < 0) + return ret; + + /* prepare the channel information */ + ret = pac1934_prep_iio_channels(info, indio_dev); + if (ret < 0) + return ret; + + info->iio_info = pac1934_info; + indio_dev->info = &info->iio_info; + indio_dev->modes = INDIO_DIRECT_MODE; + + ret = pac1934_prep_custom_attributes(info, indio_dev); + if (ret < 0) + return dev_err_probe(dev, ret, + "Can't configure custom attributes for PAC1934 device\n"); + + /* + * read whatever has been accumulated in the chip so far + * and reset the accumulators + */ + ret = pac1934_reg_snapshot(info, true, PAC1934_REFRESH_REG_ADDR, + PAC1934_MIN_UPDATE_WAIT_TIME_US); + if (ret < 0) + return ret; + + ret = devm_iio_device_register(dev, indio_dev); + if (ret < 0) + return dev_err_probe(dev, ret, + "Can't register IIO device\n"); + + return 0; +} + +static const struct i2c_device_id pac1934_id[] = { + { .name = "pac1931", .driver_data = (kernel_ulong_t)&pac1934_chip_config[PAC1931] }, + { .name = "pac1932", .driver_data = (kernel_ulong_t)&pac1934_chip_config[PAC1932] }, + { .name = "pac1933", .driver_data = (kernel_ulong_t)&pac1934_chip_config[PAC1933] }, + { .name = "pac1934", .driver_data = (kernel_ulong_t)&pac1934_chip_config[PAC1934] }, + { } +}; +MODULE_DEVICE_TABLE(i2c, pac1934_id); + +static const struct of_device_id pac1934_of_match[] = { + { + .compatible = "microchip,pac1931", + .data = &pac1934_chip_config[PAC1931] + }, + { + .compatible = "microchip,pac1932", + .data = &pac1934_chip_config[PAC1932] + }, + { + .compatible = "microchip,pac1933", + .data = &pac1934_chip_config[PAC1933] + }, + { + .compatible = "microchip,pac1934", + .data = &pac1934_chip_config[PAC1934] + }, + { } +}; +MODULE_DEVICE_TABLE(of, pac1934_of_match); + +/* + * using MCHP1930 to be compatible with BIOS ACPI. See example: + * https://ww1.microchip.com/downloads/aemDocuments/documents/OTH/ApplicationNotes/ApplicationNotes/PAC1934-Integration-Notes-for-Microsoft-Windows-10-and-Windows-11-Driver-Support-DS00002534.pdf + */ +static const struct acpi_device_id pac1934_acpi_match[] = { + { "MCHP1930", .driver_data = (kernel_ulong_t)&pac1934_chip_config[PAC1934] }, + { } +}; +MODULE_DEVICE_TABLE(acpi, pac1934_acpi_match); + +static struct i2c_driver pac1934_driver = { + .driver = { + .name = "pac1934", + .of_match_table = pac1934_of_match, + .acpi_match_table = pac1934_acpi_match + }, + .probe = pac1934_probe, + .id_table = pac1934_id, +}; + +module_i2c_driver(pac1934_driver); + +MODULE_AUTHOR("Bogdan Bolocan "); +MODULE_AUTHOR("Victor Tudose"); +MODULE_AUTHOR("Marius Cristea "); +MODULE_DESCRIPTION("IIO driver for PAC1934 Multi-Channel DC Power/Energy Monitor"); +MODULE_LICENSE("GPL"); diff --git a/drivers/iio/adc/palmas_gpadc.c b/drivers/iio/adc/palmas_gpadc.c new file mode 100644 index 000000000..f777986a6 --- /dev/null +++ b/drivers/iio/adc/palmas_gpadc.c @@ -0,0 +1,1182 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * palmas-adc.c -- TI PALMAS GPADC. + * + * Copyright (c) 2013, NVIDIA Corporation. All rights reserved. + * + * Author: Pradeep Goudagunta + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define MOD_NAME "palmas-gpadc" +#define PALMAS_ADC_CONVERSION_TIMEOUT (msecs_to_jiffies(5000)) +#define PALMAS_TO_BE_CALCULATED 0 +#define PALMAS_GPADC_TRIMINVALID -1 + +struct palmas_gpadc_info { +/* calibration codes and regs */ + int x1; /* lower ideal code */ + int x2; /* higher ideal code */ + int v1; /* expected lower volt reading */ + int v2; /* expected higher volt reading */ + u8 trim1_reg; /* register number for lower trim */ + u8 trim2_reg; /* register number for upper trim */ + int gain; /* calculated from above (after reading trim regs) */ + int offset; /* calculated from above (after reading trim regs) */ + int gain_error; /* calculated from above (after reading trim regs) */ + bool is_uncalibrated; /* if channel has calibration data */ +}; + +#define PALMAS_ADC_INFO(_chan, _x1, _x2, _v1, _v2, _t1, _t2, _is_uncalibrated) \ + [PALMAS_ADC_CH_##_chan] = { \ + .x1 = _x1, \ + .x2 = _x2, \ + .v1 = _v1, \ + .v2 = _v2, \ + .gain = PALMAS_TO_BE_CALCULATED, \ + .offset = PALMAS_TO_BE_CALCULATED, \ + .gain_error = PALMAS_TO_BE_CALCULATED, \ + .trim1_reg = PALMAS_GPADC_TRIM##_t1, \ + .trim2_reg = PALMAS_GPADC_TRIM##_t2, \ + .is_uncalibrated = _is_uncalibrated \ + } + +static struct palmas_gpadc_info palmas_gpadc_info[] = { + PALMAS_ADC_INFO(IN0, 2064, 3112, 630, 950, 1, 2, false), + PALMAS_ADC_INFO(IN1, 2064, 3112, 630, 950, 1, 2, false), + PALMAS_ADC_INFO(IN2, 2064, 3112, 1260, 1900, 3, 4, false), + PALMAS_ADC_INFO(IN3, 2064, 3112, 630, 950, 1, 2, false), + PALMAS_ADC_INFO(IN4, 2064, 3112, 630, 950, 1, 2, false), + PALMAS_ADC_INFO(IN5, 2064, 3112, 630, 950, 1, 2, false), + PALMAS_ADC_INFO(IN6, 2064, 3112, 2520, 3800, 5, 6, false), + PALMAS_ADC_INFO(IN7, 2064, 3112, 2520, 3800, 7, 8, false), + PALMAS_ADC_INFO(IN8, 2064, 3112, 3150, 4750, 9, 10, false), + PALMAS_ADC_INFO(IN9, 2064, 3112, 5670, 8550, 11, 12, false), + PALMAS_ADC_INFO(IN10, 2064, 3112, 3465, 5225, 13, 14, false), + PALMAS_ADC_INFO(IN11, 0, 0, 0, 0, INVALID, INVALID, true), + PALMAS_ADC_INFO(IN12, 0, 0, 0, 0, INVALID, INVALID, true), + PALMAS_ADC_INFO(IN13, 0, 0, 0, 0, INVALID, INVALID, true), + PALMAS_ADC_INFO(IN14, 2064, 3112, 3645, 5225, 15, 16, false), + PALMAS_ADC_INFO(IN15, 0, 0, 0, 0, INVALID, INVALID, true), +}; + +struct palmas_adc_event { + bool enabled; + int channel; + enum iio_event_direction direction; +}; + +struct palmas_gpadc_thresholds { + int high; + int low; +}; + +/* + * struct palmas_gpadc - the palmas_gpadc structure + * @ch0_current: channel 0 current source setting + * 0: 0 uA + * 1: 5 uA + * 2: 15 uA + * 3: 20 uA + * @ch3_current: channel 0 current source setting + * 0: 0 uA + * 1: 10 uA + * 2: 400 uA + * 3: 800 uA + * @extended_delay: enable the gpadc extended delay mode + * @auto_conversion_period: define the auto_conversion_period + * @lock: Lock to protect the device state during a potential concurrent + * read access from userspace. Reading a raw value requires a sequence + * of register writes, then a wait for a completion callback, + * and finally a register read, during which userspace could issue + * another read request. This lock protects a read access from + * occurring before another one has finished. + * + * This is the palmas_gpadc structure to store run-time information + * and pointers for this driver instance. + */ +struct palmas_gpadc { + struct device *dev; + struct palmas *palmas; + u8 ch0_current; + u8 ch3_current; + bool extended_delay; + int irq; + int irq_auto_0; + int irq_auto_1; + struct palmas_gpadc_info *adc_info; + struct completion conv_completion; + struct palmas_adc_event event0; + struct palmas_adc_event event1; + struct palmas_gpadc_thresholds thresholds[PALMAS_ADC_CH_MAX]; + int auto_conversion_period; + struct mutex lock; +}; + +static struct palmas_adc_event *palmas_gpadc_get_event(struct palmas_gpadc *adc, + int adc_chan, + enum iio_event_direction dir) +{ + if (adc_chan == adc->event0.channel && dir == adc->event0.direction) + return &adc->event0; + + if (adc_chan == adc->event1.channel && dir == adc->event1.direction) + return &adc->event1; + + return NULL; +} + +static bool palmas_gpadc_channel_is_freerunning(struct palmas_gpadc *adc, + int adc_chan) +{ + return palmas_gpadc_get_event(adc, adc_chan, IIO_EV_DIR_RISING) || + palmas_gpadc_get_event(adc, adc_chan, IIO_EV_DIR_FALLING); +} + +/* + * GPADC lock issue in AUTO mode. + * Impact: In AUTO mode, GPADC conversion can be locked after disabling AUTO + * mode feature. + * Details: + * When the AUTO mode is the only conversion mode enabled, if the AUTO + * mode feature is disabled with bit GPADC_AUTO_CTRL. AUTO_CONV1_EN = 0 + * or bit GPADC_AUTO_CTRL. AUTO_CONV0_EN = 0 during a conversion, the + * conversion mechanism can be seen as locked meaning that all following + * conversion will give 0 as a result. Bit GPADC_STATUS.GPADC_AVAILABLE + * will stay at 0 meaning that GPADC is busy. An RT conversion can unlock + * the GPADC. + * + * Workaround(s): + * To avoid the lock mechanism, the workaround to follow before any stop + * conversion request is: + * Force the GPADC state machine to be ON by using the GPADC_CTRL1. + * GPADC_FORCE bit = 1 + * Shutdown the GPADC AUTO conversion using + * GPADC_AUTO_CTRL.SHUTDOWN_CONV[01] = 0. + * After 100us, force the GPADC state machine to be OFF by using the + * GPADC_CTRL1. GPADC_FORCE bit = 0 + */ + +static int palmas_disable_auto_conversion(struct palmas_gpadc *adc) +{ + int ret; + + ret = palmas_update_bits(adc->palmas, PALMAS_GPADC_BASE, + PALMAS_GPADC_CTRL1, + PALMAS_GPADC_CTRL1_GPADC_FORCE, + PALMAS_GPADC_CTRL1_GPADC_FORCE); + if (ret < 0) { + dev_err(adc->dev, "GPADC_CTRL1 update failed: %d\n", ret); + return ret; + } + + ret = palmas_update_bits(adc->palmas, PALMAS_GPADC_BASE, + PALMAS_GPADC_AUTO_CTRL, + PALMAS_GPADC_AUTO_CTRL_SHUTDOWN_CONV1 | + PALMAS_GPADC_AUTO_CTRL_SHUTDOWN_CONV0, + 0); + if (ret < 0) { + dev_err(adc->dev, "AUTO_CTRL update failed: %d\n", ret); + return ret; + } + + udelay(100); + + ret = palmas_update_bits(adc->palmas, PALMAS_GPADC_BASE, + PALMAS_GPADC_CTRL1, + PALMAS_GPADC_CTRL1_GPADC_FORCE, 0); + if (ret < 0) + dev_err(adc->dev, "GPADC_CTRL1 update failed: %d\n", ret); + + return ret; +} + +static irqreturn_t palmas_gpadc_irq(int irq, void *data) +{ + struct palmas_gpadc *adc = data; + + complete(&adc->conv_completion); + + return IRQ_HANDLED; +} + +static irqreturn_t palmas_gpadc_irq_auto(int irq, void *data) +{ + struct iio_dev *indio_dev = data; + struct palmas_gpadc *adc = iio_priv(indio_dev); + struct palmas_adc_event *ev; + + dev_dbg(adc->dev, "Threshold interrupt %d occurs\n", irq); + palmas_disable_auto_conversion(adc); + + ev = (irq == adc->irq_auto_0) ? &adc->event0 : &adc->event1; + if (ev->channel != -1) { + enum iio_event_direction dir; + u64 code; + + dir = ev->direction; + code = IIO_UNMOD_EVENT_CODE(IIO_VOLTAGE, ev->channel, + IIO_EV_TYPE_THRESH, dir); + iio_push_event(indio_dev, code, iio_get_time_ns(indio_dev)); + } + + return IRQ_HANDLED; +} + +static int palmas_gpadc_start_mask_interrupt(struct palmas_gpadc *adc, + bool mask) +{ + int ret; + + if (!mask) + ret = palmas_update_bits(adc->palmas, PALMAS_INTERRUPT_BASE, + PALMAS_INT3_MASK, + PALMAS_INT3_MASK_GPADC_EOC_SW, 0); + else + ret = palmas_update_bits(adc->palmas, PALMAS_INTERRUPT_BASE, + PALMAS_INT3_MASK, + PALMAS_INT3_MASK_GPADC_EOC_SW, + PALMAS_INT3_MASK_GPADC_EOC_SW); + if (ret < 0) + dev_err(adc->dev, "GPADC INT MASK update failed: %d\n", ret); + + return ret; +} + +static int palmas_gpadc_enable(struct palmas_gpadc *adc, int adc_chan, + int enable) +{ + unsigned int mask, val; + int ret; + + if (enable) { + val = (adc->extended_delay + << PALMAS_GPADC_RT_CTRL_EXTEND_DELAY_SHIFT); + ret = palmas_update_bits(adc->palmas, PALMAS_GPADC_BASE, + PALMAS_GPADC_RT_CTRL, + PALMAS_GPADC_RT_CTRL_EXTEND_DELAY, val); + if (ret < 0) { + dev_err(adc->dev, "RT_CTRL update failed: %d\n", ret); + return ret; + } + + mask = (PALMAS_GPADC_CTRL1_CURRENT_SRC_CH0_MASK | + PALMAS_GPADC_CTRL1_CURRENT_SRC_CH3_MASK | + PALMAS_GPADC_CTRL1_GPADC_FORCE); + val = (adc->ch0_current + << PALMAS_GPADC_CTRL1_CURRENT_SRC_CH0_SHIFT); + val |= (adc->ch3_current + << PALMAS_GPADC_CTRL1_CURRENT_SRC_CH3_SHIFT); + val |= PALMAS_GPADC_CTRL1_GPADC_FORCE; + ret = palmas_update_bits(adc->palmas, PALMAS_GPADC_BASE, + PALMAS_GPADC_CTRL1, mask, val); + if (ret < 0) { + dev_err(adc->dev, + "Failed to update current setting: %d\n", ret); + return ret; + } + + mask = (PALMAS_GPADC_SW_SELECT_SW_CONV0_SEL_MASK | + PALMAS_GPADC_SW_SELECT_SW_CONV_EN); + val = (adc_chan | PALMAS_GPADC_SW_SELECT_SW_CONV_EN); + ret = palmas_update_bits(adc->palmas, PALMAS_GPADC_BASE, + PALMAS_GPADC_SW_SELECT, mask, val); + if (ret < 0) { + dev_err(adc->dev, "SW_SELECT update failed: %d\n", ret); + return ret; + } + } else { + ret = palmas_write(adc->palmas, PALMAS_GPADC_BASE, + PALMAS_GPADC_SW_SELECT, 0); + if (ret < 0) + dev_err(adc->dev, "SW_SELECT write failed: %d\n", ret); + + ret = palmas_update_bits(adc->palmas, PALMAS_GPADC_BASE, + PALMAS_GPADC_CTRL1, + PALMAS_GPADC_CTRL1_GPADC_FORCE, 0); + if (ret < 0) { + dev_err(adc->dev, "CTRL1 update failed: %d\n", ret); + return ret; + } + } + + return ret; +} + +static int palmas_gpadc_read_prepare(struct palmas_gpadc *adc, int adc_chan) +{ + int ret; + + if (palmas_gpadc_channel_is_freerunning(adc, adc_chan)) + return 0; /* ADC already running */ + + ret = palmas_gpadc_enable(adc, adc_chan, true); + if (ret < 0) + return ret; + + return palmas_gpadc_start_mask_interrupt(adc, 0); +} + +static void palmas_gpadc_read_done(struct palmas_gpadc *adc, int adc_chan) +{ + palmas_gpadc_start_mask_interrupt(adc, 1); + palmas_gpadc_enable(adc, adc_chan, false); +} + +static int palmas_gpadc_calibrate(struct palmas_gpadc *adc, int adc_chan) +{ + int k; + int d1; + int d2; + int ret; + int gain; + int x1 = adc->adc_info[adc_chan].x1; + int x2 = adc->adc_info[adc_chan].x2; + int v1 = adc->adc_info[adc_chan].v1; + int v2 = adc->adc_info[adc_chan].v2; + + ret = palmas_read(adc->palmas, PALMAS_TRIM_GPADC_BASE, + adc->adc_info[adc_chan].trim1_reg, &d1); + if (ret < 0) { + dev_err(adc->dev, "TRIM read failed: %d\n", ret); + goto scrub; + } + + ret = palmas_read(adc->palmas, PALMAS_TRIM_GPADC_BASE, + adc->adc_info[adc_chan].trim2_reg, &d2); + if (ret < 0) { + dev_err(adc->dev, "TRIM read failed: %d\n", ret); + goto scrub; + } + + /* gain error calculation */ + k = (1000 + (1000 * (d2 - d1)) / (x2 - x1)); + + /* gain calculation */ + gain = ((v2 - v1) * 1000) / (x2 - x1); + + adc->adc_info[adc_chan].gain_error = k; + adc->adc_info[adc_chan].gain = gain; + /* offset Calculation */ + adc->adc_info[adc_chan].offset = (d1 * 1000) - ((k - 1000) * x1); + +scrub: + return ret; +} + +static int palmas_gpadc_start_conversion(struct palmas_gpadc *adc, int adc_chan) +{ + unsigned int val; + int ret; + + if (palmas_gpadc_channel_is_freerunning(adc, adc_chan)) { + int event = (adc_chan == adc->event0.channel) ? 0 : 1; + unsigned int reg = (event == 0) ? + PALMAS_GPADC_AUTO_CONV0_LSB : + PALMAS_GPADC_AUTO_CONV1_LSB; + + ret = palmas_bulk_read(adc->palmas, PALMAS_GPADC_BASE, + reg, &val, 2); + if (ret < 0) { + dev_err(adc->dev, "AUTO_CONV%x_LSB read failed: %d\n", + event, ret); + return ret; + } + } else { + init_completion(&adc->conv_completion); + ret = palmas_update_bits(adc->palmas, PALMAS_GPADC_BASE, + PALMAS_GPADC_SW_SELECT, + PALMAS_GPADC_SW_SELECT_SW_START_CONV0, + PALMAS_GPADC_SW_SELECT_SW_START_CONV0); + if (ret < 0) { + dev_err(adc->dev, "SELECT_SW_START write failed: %d\n", ret); + return ret; + } + + ret = wait_for_completion_timeout(&adc->conv_completion, + PALMAS_ADC_CONVERSION_TIMEOUT); + if (ret == 0) { + dev_err(adc->dev, "conversion not completed\n"); + return -ETIMEDOUT; + } + + ret = palmas_bulk_read(adc->palmas, PALMAS_GPADC_BASE, + PALMAS_GPADC_SW_CONV0_LSB, &val, 2); + if (ret < 0) { + dev_err(adc->dev, "SW_CONV0_LSB read failed: %d\n", ret); + return ret; + } + } + + ret = val & 0xFFF; + + return ret; +} + +static int palmas_gpadc_get_calibrated_code(struct palmas_gpadc *adc, + int adc_chan, int val) +{ + if (!adc->adc_info[adc_chan].is_uncalibrated) + val = (val*1000 - adc->adc_info[adc_chan].offset) / + adc->adc_info[adc_chan].gain_error; + + if (val < 0) { + if (val < -10) + dev_err(adc->dev, "Mismatch with calibration var = %d\n", val); + return 0; + } + + val = (val * adc->adc_info[adc_chan].gain) / 1000; + + return val; +} + +/* + * The high and low threshold values are calculated based on the advice given + * in TI Application Report SLIA087A, "Guide to Using the GPADC in PS65903x, + * TPS65917-Q1, TPS65919-Q1, and TPS65916 Devices". This document recommend + * taking ADC tolerances into account and is based on the device integral non- + * linearity (INL), offset error and gain error: + * + * raw high threshold = (ideal threshold + INL) * gain error + offset error + * + * The gain error include both gain error, as specified in the datasheet, and + * the gain error drift. These parameters vary depending on device and whether + * the channel is calibrated (trimmed) or not. + */ +static int palmas_gpadc_threshold_with_tolerance(int val, const int INL, + const int gain_error, + const int offset_error) +{ + val = ((val + INL) * (1000 + gain_error)) / 1000 + offset_error; + + return clamp(val, 0, 0xFFF); +} + +/* + * The values below are taken from the datasheet of TWL6035, TWL6037. + * todo: get max INL, gain error, and offset error from OF. + */ +static int palmas_gpadc_get_high_threshold_raw(struct palmas_gpadc *adc, + struct palmas_adc_event *ev) +{ + const int adc_chan = ev->channel; + int val = adc->thresholds[adc_chan].high; + /* integral nonlinearity, measured in LSB */ + const int max_INL = 2; + /* measured in LSB */ + int max_offset_error; + /* 0.2% when calibrated */ + int max_gain_error = 2; + + val = (val * 1000) / adc->adc_info[adc_chan].gain; + + if (adc->adc_info[adc_chan].is_uncalibrated) { + /* 2% worse */ + max_gain_error += 20; + max_offset_error = 36; + } else { + val = (val * adc->adc_info[adc_chan].gain_error + + adc->adc_info[adc_chan].offset) / + 1000; + max_offset_error = 2; + } + + return palmas_gpadc_threshold_with_tolerance(val, + max_INL, + max_gain_error, + max_offset_error); +} + +/* + * The values below are taken from the datasheet of TWL6035, TWL6037. + * todo: get min INL, gain error, and offset error from OF. + */ +static int palmas_gpadc_get_low_threshold_raw(struct palmas_gpadc *adc, + struct palmas_adc_event *ev) +{ + const int adc_chan = ev->channel; + int val = adc->thresholds[adc_chan].low; + /* integral nonlinearity, measured in LSB */ + const int min_INL = -2; + /* measured in LSB */ + int min_offset_error; + /* -0.6% when calibrated */ + int min_gain_error = -6; + + val = (val * 1000) / adc->adc_info[adc_chan].gain; + + if (adc->adc_info[adc_chan].is_uncalibrated) { + /* 2% worse */ + min_gain_error -= 20; + min_offset_error = -36; + } else { + val = (val * adc->adc_info[adc_chan].gain_error - + adc->adc_info[adc_chan].offset) / + 1000; + min_offset_error = -2; + } + + return palmas_gpadc_threshold_with_tolerance(val, + min_INL, + min_gain_error, + min_offset_error); +} + +static int palmas_gpadc_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int *val, int *val2, long mask) +{ + struct palmas_gpadc *adc = iio_priv(indio_dev); + int adc_chan = chan->channel; + int ret = 0; + + if (adc_chan >= PALMAS_ADC_CH_MAX) + return -EINVAL; + + mutex_lock(&adc->lock); + + switch (mask) { + case IIO_CHAN_INFO_RAW: + case IIO_CHAN_INFO_PROCESSED: + ret = palmas_gpadc_read_prepare(adc, adc_chan); + if (ret < 0) + goto out; + + ret = palmas_gpadc_start_conversion(adc, adc_chan); + if (ret < 0) { + dev_err(adc->dev, + "ADC start conversion failed\n"); + goto out; + } + + if (mask == IIO_CHAN_INFO_PROCESSED) + ret = palmas_gpadc_get_calibrated_code( + adc, adc_chan, ret); + + *val = ret; + + ret = IIO_VAL_INT; + goto out; + } + + mutex_unlock(&adc->lock); + return ret; + +out: + palmas_gpadc_read_done(adc, adc_chan); + mutex_unlock(&adc->lock); + + return ret; +} + +static int palmas_gpadc_read_event_config(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir) +{ + struct palmas_gpadc *adc = iio_priv(indio_dev); + int adc_chan = chan->channel; + int ret = 0; + + if (adc_chan >= PALMAS_ADC_CH_MAX || type != IIO_EV_TYPE_THRESH) + return -EINVAL; + + mutex_lock(&adc->lock); + + if (palmas_gpadc_get_event(adc, adc_chan, dir)) + ret = 1; + + mutex_unlock(&adc->lock); + + return ret; +} + +static int palmas_adc_configure_events(struct palmas_gpadc *adc); +static int palmas_adc_reset_events(struct palmas_gpadc *adc); + +static int palmas_gpadc_reconfigure_event_channels(struct palmas_gpadc *adc) +{ + return (adc->event0.enabled || adc->event1.enabled) ? + palmas_adc_configure_events(adc) : + palmas_adc_reset_events(adc); +} + +static int palmas_gpadc_enable_event_config(struct palmas_gpadc *adc, + const struct iio_chan_spec *chan, + enum iio_event_direction dir) +{ + struct palmas_adc_event *ev; + int adc_chan = chan->channel; + + if (palmas_gpadc_get_event(adc, adc_chan, dir)) + /* already enabled */ + return 0; + + if (adc->event0.channel == -1) { + ev = &adc->event0; + } else if (adc->event1.channel == -1) { + /* event0 has to be the lowest channel */ + if (adc_chan < adc->event0.channel) { + adc->event1 = adc->event0; + ev = &adc->event0; + } else { + ev = &adc->event1; + } + } else { /* both AUTO channels already in use */ + dev_warn(adc->dev, "event0 - %d, event1 - %d\n", + adc->event0.channel, adc->event1.channel); + return -EBUSY; + } + + ev->enabled = true; + ev->channel = adc_chan; + ev->direction = dir; + + return palmas_gpadc_reconfigure_event_channels(adc); +} + +static int palmas_gpadc_disable_event_config(struct palmas_gpadc *adc, + const struct iio_chan_spec *chan, + enum iio_event_direction dir) +{ + int adc_chan = chan->channel; + struct palmas_adc_event *ev = palmas_gpadc_get_event(adc, adc_chan, dir); + + if (!ev) + return 0; + + if (ev == &adc->event0) { + adc->event0 = adc->event1; + ev = &adc->event1; + } + + ev->enabled = false; + ev->channel = -1; + ev->direction = IIO_EV_DIR_NONE; + + return palmas_gpadc_reconfigure_event_channels(adc); +} + +static int palmas_gpadc_write_event_config(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir, + bool state) +{ + struct palmas_gpadc *adc = iio_priv(indio_dev); + int adc_chan = chan->channel; + int ret; + + if (adc_chan >= PALMAS_ADC_CH_MAX || type != IIO_EV_TYPE_THRESH) + return -EINVAL; + + mutex_lock(&adc->lock); + + if (state) + ret = palmas_gpadc_enable_event_config(adc, chan, dir); + else + ret = palmas_gpadc_disable_event_config(adc, chan, dir); + + mutex_unlock(&adc->lock); + + return ret; +} + +static int palmas_gpadc_read_event_value(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir, + enum iio_event_info info, + int *val, int *val2) +{ + struct palmas_gpadc *adc = iio_priv(indio_dev); + int adc_chan = chan->channel; + int ret; + + if (adc_chan >= PALMAS_ADC_CH_MAX || type != IIO_EV_TYPE_THRESH) + return -EINVAL; + + mutex_lock(&adc->lock); + + switch (info) { + case IIO_EV_INFO_VALUE: + *val = (dir == IIO_EV_DIR_RISING) ? + adc->thresholds[adc_chan].high : + adc->thresholds[adc_chan].low; + ret = IIO_VAL_INT; + break; + default: + ret = -EINVAL; + break; + } + + mutex_unlock(&adc->lock); + + return ret; +} + +static int palmas_gpadc_write_event_value(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir, + enum iio_event_info info, + int val, int val2) +{ + struct palmas_gpadc *adc = iio_priv(indio_dev); + int adc_chan = chan->channel; + int old; + int ret; + + if (adc_chan >= PALMAS_ADC_CH_MAX || type != IIO_EV_TYPE_THRESH) + return -EINVAL; + + mutex_lock(&adc->lock); + switch (info) { + case IIO_EV_INFO_VALUE: + if (val < 0 || val > 0xFFF) { + ret = -EINVAL; + goto out_unlock; + } + if (dir == IIO_EV_DIR_RISING) { + old = adc->thresholds[adc_chan].high; + adc->thresholds[adc_chan].high = val; + } else { + old = adc->thresholds[adc_chan].low; + adc->thresholds[adc_chan].low = val; + } + ret = 0; + break; + default: + ret = -EINVAL; + goto out_unlock; + } + + if (val != old && palmas_gpadc_get_event(adc, adc_chan, dir)) + ret = palmas_gpadc_reconfigure_event_channels(adc); + +out_unlock: + mutex_unlock(&adc->lock); + + return ret; +} + +static const struct iio_info palmas_gpadc_iio_info = { + .read_raw = palmas_gpadc_read_raw, + .read_event_config = palmas_gpadc_read_event_config, + .write_event_config = palmas_gpadc_write_event_config, + .read_event_value = palmas_gpadc_read_event_value, + .write_event_value = palmas_gpadc_write_event_value, +}; + +static const struct iio_event_spec palmas_gpadc_events[] = { + { + .type = IIO_EV_TYPE_THRESH, + .dir = IIO_EV_DIR_RISING, + .mask_separate = BIT(IIO_EV_INFO_VALUE) | + BIT(IIO_EV_INFO_ENABLE), + }, { + .type = IIO_EV_TYPE_THRESH, + .dir = IIO_EV_DIR_FALLING, + .mask_separate = BIT(IIO_EV_INFO_VALUE) | + BIT(IIO_EV_INFO_ENABLE), + }, +}; + +#define PALMAS_ADC_CHAN_IIO(chan, _type, chan_info) \ +{ \ + .datasheet_name = PALMAS_DATASHEET_NAME(chan), \ + .type = _type, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ + BIT(chan_info), \ + .indexed = 1, \ + .channel = PALMAS_ADC_CH_##chan, \ + .event_spec = palmas_gpadc_events, \ + .num_event_specs = ARRAY_SIZE(palmas_gpadc_events) \ +} + +static const struct iio_chan_spec palmas_gpadc_iio_channel[] = { + PALMAS_ADC_CHAN_IIO(IN0, IIO_VOLTAGE, IIO_CHAN_INFO_PROCESSED), + PALMAS_ADC_CHAN_IIO(IN1, IIO_TEMP, IIO_CHAN_INFO_RAW), + PALMAS_ADC_CHAN_IIO(IN2, IIO_VOLTAGE, IIO_CHAN_INFO_PROCESSED), + PALMAS_ADC_CHAN_IIO(IN3, IIO_TEMP, IIO_CHAN_INFO_RAW), + PALMAS_ADC_CHAN_IIO(IN4, IIO_VOLTAGE, IIO_CHAN_INFO_PROCESSED), + PALMAS_ADC_CHAN_IIO(IN5, IIO_VOLTAGE, IIO_CHAN_INFO_PROCESSED), + PALMAS_ADC_CHAN_IIO(IN6, IIO_VOLTAGE, IIO_CHAN_INFO_PROCESSED), + PALMAS_ADC_CHAN_IIO(IN7, IIO_VOLTAGE, IIO_CHAN_INFO_PROCESSED), + PALMAS_ADC_CHAN_IIO(IN8, IIO_VOLTAGE, IIO_CHAN_INFO_PROCESSED), + PALMAS_ADC_CHAN_IIO(IN9, IIO_VOLTAGE, IIO_CHAN_INFO_PROCESSED), + PALMAS_ADC_CHAN_IIO(IN10, IIO_VOLTAGE, IIO_CHAN_INFO_PROCESSED), + PALMAS_ADC_CHAN_IIO(IN11, IIO_VOLTAGE, IIO_CHAN_INFO_PROCESSED), + PALMAS_ADC_CHAN_IIO(IN12, IIO_TEMP, IIO_CHAN_INFO_RAW), + PALMAS_ADC_CHAN_IIO(IN13, IIO_TEMP, IIO_CHAN_INFO_RAW), + PALMAS_ADC_CHAN_IIO(IN14, IIO_VOLTAGE, IIO_CHAN_INFO_PROCESSED), + PALMAS_ADC_CHAN_IIO(IN15, IIO_VOLTAGE, IIO_CHAN_INFO_PROCESSED), +}; + +static int palmas_gpadc_get_adc_dt_data(struct platform_device *pdev, + struct palmas_gpadc_platform_data **gpadc_pdata) +{ + struct device_node *np = pdev->dev.of_node; + struct palmas_gpadc_platform_data *gp_data; + int ret; + u32 pval; + + gp_data = devm_kzalloc(&pdev->dev, sizeof(*gp_data), GFP_KERNEL); + if (!gp_data) + return -ENOMEM; + + ret = of_property_read_u32(np, "ti,channel0-current-microamp", &pval); + if (!ret) + gp_data->ch0_current = pval; + + ret = of_property_read_u32(np, "ti,channel3-current-microamp", &pval); + if (!ret) + gp_data->ch3_current = pval; + + gp_data->extended_delay = of_property_read_bool(np, + "ti,enable-extended-delay"); + + *gpadc_pdata = gp_data; + + return 0; +} + +static void palmas_gpadc_reset(void *data) +{ + struct palmas_gpadc *adc = data; + if (adc->event0.enabled || adc->event1.enabled) + palmas_adc_reset_events(adc); +} + +static int palmas_gpadc_probe(struct platform_device *pdev) +{ + struct palmas_gpadc *adc; + struct palmas_platform_data *pdata; + struct palmas_gpadc_platform_data *gpadc_pdata = NULL; + struct iio_dev *indio_dev; + int ret, i; + + pdata = dev_get_platdata(pdev->dev.parent); + + if (pdata && pdata->gpadc_pdata) + gpadc_pdata = pdata->gpadc_pdata; + + if (!gpadc_pdata && pdev->dev.of_node) { + ret = palmas_gpadc_get_adc_dt_data(pdev, &gpadc_pdata); + if (ret < 0) + return ret; + } + if (!gpadc_pdata) + return -EINVAL; + + indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*adc)); + if (!indio_dev) + return -ENOMEM; + + adc = iio_priv(indio_dev); + adc->dev = &pdev->dev; + adc->palmas = dev_get_drvdata(pdev->dev.parent); + adc->adc_info = palmas_gpadc_info; + + mutex_init(&adc->lock); + + init_completion(&adc->conv_completion); + platform_set_drvdata(pdev, indio_dev); + + adc->auto_conversion_period = gpadc_pdata->auto_conversion_period_ms; + adc->irq = palmas_irq_get_virq(adc->palmas, PALMAS_GPADC_EOC_SW_IRQ); + if (adc->irq < 0) + return dev_err_probe(adc->dev, adc->irq, "get virq failed\n"); + + ret = devm_request_threaded_irq(&pdev->dev, adc->irq, NULL, + palmas_gpadc_irq, + IRQF_ONESHOT, dev_name(adc->dev), + adc); + if (ret < 0) + return dev_err_probe(adc->dev, ret, + "request irq %d failed\n", adc->irq); + + adc->irq_auto_0 = platform_get_irq(pdev, 1); + if (adc->irq_auto_0 < 0) + return adc->irq_auto_0; + + ret = devm_request_threaded_irq(&pdev->dev, adc->irq_auto_0, NULL, + palmas_gpadc_irq_auto, IRQF_ONESHOT, + "palmas-adc-auto-0", indio_dev); + if (ret < 0) + return dev_err_probe(adc->dev, ret, + "request auto0 irq %d failed\n", + adc->irq_auto_0); + + adc->irq_auto_1 = platform_get_irq(pdev, 2); + if (adc->irq_auto_1 < 0) + return adc->irq_auto_1; + + ret = devm_request_threaded_irq(&pdev->dev, adc->irq_auto_1, NULL, + palmas_gpadc_irq_auto, IRQF_ONESHOT, + "palmas-adc-auto-1", indio_dev); + if (ret < 0) + return dev_err_probe(adc->dev, ret, + "request auto1 irq %d failed\n", + adc->irq_auto_1); + + adc->event0.enabled = false; + adc->event0.channel = -1; + adc->event0.direction = IIO_EV_DIR_NONE; + adc->event1.enabled = false; + adc->event1.channel = -1; + adc->event1.direction = IIO_EV_DIR_NONE; + + /* set the current source 0 (value 0/5/15/20 uA => 0..3) */ + if (gpadc_pdata->ch0_current <= 1) + adc->ch0_current = PALMAS_ADC_CH0_CURRENT_SRC_0; + else if (gpadc_pdata->ch0_current <= 5) + adc->ch0_current = PALMAS_ADC_CH0_CURRENT_SRC_5; + else if (gpadc_pdata->ch0_current <= 15) + adc->ch0_current = PALMAS_ADC_CH0_CURRENT_SRC_15; + else + adc->ch0_current = PALMAS_ADC_CH0_CURRENT_SRC_20; + + /* set the current source 3 (value 0/10/400/800 uA => 0..3) */ + if (gpadc_pdata->ch3_current <= 1) + adc->ch3_current = PALMAS_ADC_CH3_CURRENT_SRC_0; + else if (gpadc_pdata->ch3_current <= 10) + adc->ch3_current = PALMAS_ADC_CH3_CURRENT_SRC_10; + else if (gpadc_pdata->ch3_current <= 400) + adc->ch3_current = PALMAS_ADC_CH3_CURRENT_SRC_400; + else + adc->ch3_current = PALMAS_ADC_CH3_CURRENT_SRC_800; + + adc->extended_delay = gpadc_pdata->extended_delay; + + indio_dev->name = MOD_NAME; + indio_dev->info = &palmas_gpadc_iio_info; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = palmas_gpadc_iio_channel; + indio_dev->num_channels = ARRAY_SIZE(palmas_gpadc_iio_channel); + + ret = devm_iio_device_register(&pdev->dev, indio_dev); + if (ret < 0) + return dev_err_probe(adc->dev, ret, + "iio_device_register() failed\n"); + + device_set_wakeup_capable(&pdev->dev, 1); + for (i = 0; i < PALMAS_ADC_CH_MAX; i++) { + if (!(adc->adc_info[i].is_uncalibrated)) + palmas_gpadc_calibrate(adc, i); + } + + ret = devm_add_action(&pdev->dev, palmas_gpadc_reset, adc); + if (ret) + return ret; + + return 0; +} + +static int palmas_adc_configure_events(struct palmas_gpadc *adc) +{ + int adc_period, conv; + int i; + int ch0 = 0, ch1 = 0; + int thres; + int ret; + + adc_period = adc->auto_conversion_period; + for (i = 0; i < 16; ++i) { + if (((1000 * (1 << i)) / 32) >= adc_period) + break; + } + if (i > 0) + i--; + adc_period = i; + ret = palmas_update_bits(adc->palmas, PALMAS_GPADC_BASE, + PALMAS_GPADC_AUTO_CTRL, + PALMAS_GPADC_AUTO_CTRL_COUNTER_CONV_MASK, + adc_period); + if (ret < 0) { + dev_err(adc->dev, "AUTO_CTRL write failed: %d\n", ret); + return ret; + } + + conv = 0; + if (adc->event0.enabled) { + struct palmas_adc_event *ev = &adc->event0; + int polarity; + + ch0 = ev->channel; + conv |= PALMAS_GPADC_AUTO_CTRL_AUTO_CONV0_EN; + switch (ev->direction) { + case IIO_EV_DIR_RISING: + thres = palmas_gpadc_get_high_threshold_raw(adc, ev); + polarity = 0; + break; + case IIO_EV_DIR_FALLING: + thres = palmas_gpadc_get_low_threshold_raw(adc, ev); + polarity = PALMAS_GPADC_THRES_CONV0_MSB_THRES_CONV0_POL; + break; + default: + return -EINVAL; + } + + ret = palmas_write(adc->palmas, PALMAS_GPADC_BASE, + PALMAS_GPADC_THRES_CONV0_LSB, thres & 0xFF); + if (ret < 0) { + dev_err(adc->dev, + "THRES_CONV0_LSB write failed: %d\n", ret); + return ret; + } + + ret = palmas_write(adc->palmas, PALMAS_GPADC_BASE, + PALMAS_GPADC_THRES_CONV0_MSB, + ((thres >> 8) & 0xF) | polarity); + if (ret < 0) { + dev_err(adc->dev, + "THRES_CONV0_MSB write failed: %d\n", ret); + return ret; + } + } + + if (adc->event1.enabled) { + struct palmas_adc_event *ev = &adc->event1; + int polarity; + + ch1 = ev->channel; + conv |= PALMAS_GPADC_AUTO_CTRL_AUTO_CONV1_EN; + switch (ev->direction) { + case IIO_EV_DIR_RISING: + thres = palmas_gpadc_get_high_threshold_raw(adc, ev); + polarity = 0; + break; + case IIO_EV_DIR_FALLING: + thres = palmas_gpadc_get_low_threshold_raw(adc, ev); + polarity = PALMAS_GPADC_THRES_CONV1_MSB_THRES_CONV1_POL; + break; + default: + return -EINVAL; + } + + ret = palmas_write(adc->palmas, PALMAS_GPADC_BASE, + PALMAS_GPADC_THRES_CONV1_LSB, thres & 0xFF); + if (ret < 0) { + dev_err(adc->dev, + "THRES_CONV1_LSB write failed: %d\n", ret); + return ret; + } + + ret = palmas_write(adc->palmas, PALMAS_GPADC_BASE, + PALMAS_GPADC_THRES_CONV1_MSB, + ((thres >> 8) & 0xF) | polarity); + if (ret < 0) { + dev_err(adc->dev, + "THRES_CONV1_MSB write failed: %d\n", ret); + return ret; + } + } + + ret = palmas_write(adc->palmas, PALMAS_GPADC_BASE, + PALMAS_GPADC_AUTO_SELECT, (ch1 << 4) | ch0); + if (ret < 0) { + dev_err(adc->dev, "AUTO_SELECT write failed: %d\n", ret); + return ret; + } + + ret = palmas_update_bits(adc->palmas, PALMAS_GPADC_BASE, + PALMAS_GPADC_AUTO_CTRL, + PALMAS_GPADC_AUTO_CTRL_AUTO_CONV1_EN | + PALMAS_GPADC_AUTO_CTRL_AUTO_CONV0_EN, conv); + if (ret < 0) + dev_err(adc->dev, "AUTO_CTRL write failed: %d\n", ret); + + return ret; +} + +static int palmas_adc_reset_events(struct palmas_gpadc *adc) +{ + int ret; + + ret = palmas_write(adc->palmas, PALMAS_GPADC_BASE, + PALMAS_GPADC_AUTO_SELECT, 0); + if (ret < 0) { + dev_err(adc->dev, "AUTO_SELECT write failed: %d\n", ret); + return ret; + } + + ret = palmas_disable_auto_conversion(adc); + if (ret < 0) + dev_err(adc->dev, "Disable auto conversion failed: %d\n", ret); + + return ret; +} + +static int palmas_gpadc_suspend(struct device *dev) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct palmas_gpadc *adc = iio_priv(indio_dev); + + if (!device_may_wakeup(dev)) + return 0; + + if (adc->event0.enabled) + enable_irq_wake(adc->irq_auto_0); + + if (adc->event1.enabled) + enable_irq_wake(adc->irq_auto_1); + + return 0; +} + +static int palmas_gpadc_resume(struct device *dev) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct palmas_gpadc *adc = iio_priv(indio_dev); + + if (!device_may_wakeup(dev)) + return 0; + + if (adc->event0.enabled) + disable_irq_wake(adc->irq_auto_0); + + if (adc->event1.enabled) + disable_irq_wake(adc->irq_auto_1); + + return 0; +}; + +static DEFINE_SIMPLE_DEV_PM_OPS(palmas_pm_ops, palmas_gpadc_suspend, + palmas_gpadc_resume); + +static const struct of_device_id of_palmas_gpadc_match_tbl[] = { + { .compatible = "ti,palmas-gpadc", }, + { } +}; +MODULE_DEVICE_TABLE(of, of_palmas_gpadc_match_tbl); + +static struct platform_driver palmas_gpadc_driver = { + .probe = palmas_gpadc_probe, + .driver = { + .name = MOD_NAME, + .pm = pm_sleep_ptr(&palmas_pm_ops), + .of_match_table = of_palmas_gpadc_match_tbl, + }, +}; +module_platform_driver(palmas_gpadc_driver); + +MODULE_DESCRIPTION("palmas GPADC driver"); +MODULE_AUTHOR("Pradeep Goudagunta"); +MODULE_ALIAS("platform:palmas-gpadc"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/adc/qcom-pm8xxx-xoadc.c b/drivers/iio/adc/qcom-pm8xxx-xoadc.c new file mode 100644 index 000000000..3cdf90c44 --- /dev/null +++ b/drivers/iio/adc/qcom-pm8xxx-xoadc.c @@ -0,0 +1,1027 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Qualcomm PM8xxx PMIC XOADC driver + * + * These ADCs are known as HK/XO (house keeping / chrystal oscillator) + * "XO" in "XOADC" means Chrystal Oscillator. It's a bunch of + * specific-purpose and general purpose ADC converters and channels. + * + * Copyright (C) 2017 Linaro Ltd. + * Author: Linus Walleij + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * Definitions for the "user processor" registers lifted from the v3.4 + * Qualcomm tree. Their kernel has two out-of-tree drivers for the ADC: + * drivers/misc/pmic8058-xoadc.c + * drivers/hwmon/pm8xxx-adc.c + * None of them contain any complete register specification, so this is + * a best effort of combining the information. + */ + +/* These appear to be "battery monitor" registers */ +#define ADC_ARB_BTM_CNTRL1 0x17e +#define ADC_ARB_BTM_CNTRL1_EN_BTM BIT(0) +#define ADC_ARB_BTM_CNTRL1_SEL_OP_MODE BIT(1) +#define ADC_ARB_BTM_CNTRL1_MEAS_INTERVAL1 BIT(2) +#define ADC_ARB_BTM_CNTRL1_MEAS_INTERVAL2 BIT(3) +#define ADC_ARB_BTM_CNTRL1_MEAS_INTERVAL3 BIT(4) +#define ADC_ARB_BTM_CNTRL1_MEAS_INTERVAL4 BIT(5) +#define ADC_ARB_BTM_CNTRL1_EOC BIT(6) +#define ADC_ARB_BTM_CNTRL1_REQ BIT(7) + +#define ADC_ARB_BTM_AMUX_CNTRL 0x17f +#define ADC_ARB_BTM_ANA_PARAM 0x180 +#define ADC_ARB_BTM_DIG_PARAM 0x181 +#define ADC_ARB_BTM_RSV 0x182 +#define ADC_ARB_BTM_DATA1 0x183 +#define ADC_ARB_BTM_DATA0 0x184 +#define ADC_ARB_BTM_BAT_COOL_THR1 0x185 +#define ADC_ARB_BTM_BAT_COOL_THR0 0x186 +#define ADC_ARB_BTM_BAT_WARM_THR1 0x187 +#define ADC_ARB_BTM_BAT_WARM_THR0 0x188 +#define ADC_ARB_BTM_CNTRL2 0x18c + +/* Proper ADC registers */ + +#define ADC_ARB_USRP_CNTRL 0x197 +#define ADC_ARB_USRP_CNTRL_EN_ARB BIT(0) +#define ADC_ARB_USRP_CNTRL_RSV1 BIT(1) +#define ADC_ARB_USRP_CNTRL_RSV2 BIT(2) +#define ADC_ARB_USRP_CNTRL_RSV3 BIT(3) +#define ADC_ARB_USRP_CNTRL_RSV4 BIT(4) +#define ADC_ARB_USRP_CNTRL_RSV5 BIT(5) +#define ADC_ARB_USRP_CNTRL_EOC BIT(6) +#define ADC_ARB_USRP_CNTRL_REQ BIT(7) + +#define ADC_ARB_USRP_AMUX_CNTRL 0x198 +/* + * The channel mask includes the bits selecting channel mux and prescaler + * on PM8058, or channel mux and premux on PM8921. + */ +#define ADC_ARB_USRP_AMUX_CNTRL_CHAN_MASK 0xfc +#define ADC_ARB_USRP_AMUX_CNTRL_RSV0 BIT(0) +#define ADC_ARB_USRP_AMUX_CNTRL_RSV1 BIT(1) +/* On PM8058 this is prescaling, on PM8921 this is premux */ +#define ADC_ARB_USRP_AMUX_CNTRL_PRESCALEMUX0 BIT(2) +#define ADC_ARB_USRP_AMUX_CNTRL_PRESCALEMUX1 BIT(3) +#define ADC_ARB_USRP_AMUX_CNTRL_SEL0 BIT(4) +#define ADC_ARB_USRP_AMUX_CNTRL_SEL1 BIT(5) +#define ADC_ARB_USRP_AMUX_CNTRL_SEL2 BIT(6) +#define ADC_ARB_USRP_AMUX_CNTRL_SEL3 BIT(7) +#define ADC_AMUX_PREMUX_SHIFT 2 +#define ADC_AMUX_SEL_SHIFT 4 + +/* We know very little about the bits in this register */ +#define ADC_ARB_USRP_ANA_PARAM 0x199 +#define ADC_ARB_USRP_ANA_PARAM_DIS 0xFE +#define ADC_ARB_USRP_ANA_PARAM_EN 0xFF + +#define ADC_ARB_USRP_DIG_PARAM 0x19A +#define ADC_ARB_USRP_DIG_PARAM_SEL_SHIFT0 BIT(0) +#define ADC_ARB_USRP_DIG_PARAM_SEL_SHIFT1 BIT(1) +#define ADC_ARB_USRP_DIG_PARAM_CLK_RATE0 BIT(2) +#define ADC_ARB_USRP_DIG_PARAM_CLK_RATE1 BIT(3) +#define ADC_ARB_USRP_DIG_PARAM_EOC BIT(4) +/* + * On a later ADC the decimation factors are defined as + * 00 = 512, 01 = 1024, 10 = 2048, 11 = 4096 so assume this + * holds also for this older XOADC. + */ +#define ADC_ARB_USRP_DIG_PARAM_DEC_RATE0 BIT(5) +#define ADC_ARB_USRP_DIG_PARAM_DEC_RATE1 BIT(6) +#define ADC_ARB_USRP_DIG_PARAM_EN BIT(7) +#define ADC_DIG_PARAM_DEC_SHIFT 5 + +#define ADC_ARB_USRP_RSV 0x19B +#define ADC_ARB_USRP_RSV_RST BIT(0) +#define ADC_ARB_USRP_RSV_DTEST0 BIT(1) +#define ADC_ARB_USRP_RSV_DTEST1 BIT(2) +#define ADC_ARB_USRP_RSV_OP BIT(3) +#define ADC_ARB_USRP_RSV_IP_SEL0 BIT(4) +#define ADC_ARB_USRP_RSV_IP_SEL1 BIT(5) +#define ADC_ARB_USRP_RSV_IP_SEL2 BIT(6) +#define ADC_ARB_USRP_RSV_TRM BIT(7) +#define ADC_RSV_IP_SEL_SHIFT 4 + +#define ADC_ARB_USRP_DATA0 0x19D +#define ADC_ARB_USRP_DATA1 0x19C + +/* + * Physical channels which MUST exist on all PM variants in order to provide + * proper reference points for calibration. + * + * @PM8XXX_CHANNEL_INTERNAL: 625mV reference channel + * @PM8XXX_CHANNEL_125V: 1250mV reference channel + * @PM8XXX_CHANNEL_INTERNAL_2: 325mV reference channel + * @PM8XXX_CHANNEL_MUXOFF: channel to reduce input load on mux, apparently also + * measures XO temperature + */ +#define PM8XXX_CHANNEL_INTERNAL 0x0c +#define PM8XXX_CHANNEL_125V 0x0d +#define PM8XXX_CHANNEL_INTERNAL_2 0x0e +#define PM8XXX_CHANNEL_MUXOFF 0x0f + +/* + * PM8058 AMUX premux scaling, two bits. This is done of the channel before + * reaching the AMUX. + */ +#define PM8058_AMUX_PRESCALE_0 0x0 /* No scaling on the signal */ +#define PM8058_AMUX_PRESCALE_1 0x1 /* Unity scaling selected by the user */ +#define PM8058_AMUX_PRESCALE_1_DIV3 0x2 /* 1/3 prescaler on the input */ + +/* Defines reference voltage for the XOADC */ +#define AMUX_RSV0 0x0 /* XO_IN/XOADC_GND, special selection to read XO temp */ +#define AMUX_RSV1 0x1 /* PMIC_IN/XOADC_GND */ +#define AMUX_RSV2 0x2 /* PMIC_IN/BMS_CSP */ +#define AMUX_RSV3 0x3 /* not used */ +#define AMUX_RSV4 0x4 /* XOADC_GND/XOADC_GND */ +#define AMUX_RSV5 0x5 /* XOADC_VREF/XOADC_GND */ +#define XOADC_RSV_MAX 5 /* 3 bits 0..7, 3 and 6,7 are invalid */ + +/** + * struct xoadc_channel - encodes channel properties and defaults + * @datasheet_name: the hardwarename of this channel + * @pre_scale_mux: prescale (PM8058) or premux (PM8921) for selecting + * this channel. Both this and the amux channel is needed to uniquely + * identify a channel. Values 0..3. + * @amux_channel: value of the ADC_ARB_USRP_AMUX_CNTRL register for this + * channel, bits 4..7, selects the amux, values 0..f + * @prescale: the channels have hard-coded prescale ratios defined + * by the hardware, this tells us what it is + * @type: corresponding IIO channel type, usually IIO_VOLTAGE or + * IIO_TEMP + * @scale_fn_type: the liner interpolation etc to convert the + * ADC code to the value that IIO expects, in uV or millicelsius + * etc. This scale function can be pretty elaborate if different + * thermistors are connected or other hardware characteristics are + * deployed. + * @amux_ip_rsv: ratiometric scale value used by the analog muxer: this + * selects the reference voltage for ratiometric scaling + */ +struct xoadc_channel { + const char *datasheet_name; + u8 pre_scale_mux:2; + u8 amux_channel:4; + const struct u32_fract prescale; + enum iio_chan_type type; + enum vadc_scale_fn_type scale_fn_type; + u8 amux_ip_rsv:3; +}; + +/** + * struct xoadc_variant - encodes the XOADC variant characteristics + * @name: name of this PMIC variant + * @channels: the hardware channels and respective settings and defaults + * @broken_ratiometric: if the PMIC has broken ratiometric scaling (this + * is a known problem on PM8058) + * @prescaling: this variant uses AMUX bits 2 & 3 for prescaling (PM8058) + * @second_level_mux: this variant uses AMUX bits 2 & 3 for a second level + * mux + */ +struct xoadc_variant { + const char name[16]; + const struct xoadc_channel *channels; + bool broken_ratiometric; + bool prescaling; + bool second_level_mux; +}; + +/* + * XOADC_CHAN macro parameters: + * _dname: the name of the channel + * _presmux: prescaler (PM8058) or premux (PM8921) setting for this channel + * _amux: the value in bits 2..7 of the ADC_ARB_USRP_AMUX_CNTRL register + * for this channel. On some PMICs some of the bits select a prescaler, and + * on some PMICs some of the bits select various complex multiplex settings. + * _type: IIO channel type + * _prenum: prescaler numerator (dividend) + * _preden: prescaler denominator (divisor) + * _scale: scaling function type, this selects how the raw valued is mangled + * to output the actual processed measurement + * _amip: analog mux input parent when using ratiometric measurements + */ +#define XOADC_CHAN(_dname, _presmux, _amux, _type, _prenum, _preden, _scale, _amip) \ + { \ + .datasheet_name = __stringify(_dname), \ + .pre_scale_mux = _presmux, \ + .amux_channel = _amux, \ + .prescale = { \ + .numerator = _prenum, .denominator = _preden, \ + }, \ + .type = _type, \ + .scale_fn_type = _scale, \ + .amux_ip_rsv = _amip, \ + } + +/* + * Taken from arch/arm/mach-msm/board-9615.c in the vendor tree: + * TODO: incomplete, needs testing. + */ +static const struct xoadc_channel pm8018_xoadc_channels[] = { + XOADC_CHAN(VCOIN, 0x00, 0x00, IIO_VOLTAGE, 1, 3, SCALE_DEFAULT, AMUX_RSV1), + XOADC_CHAN(VBAT, 0x00, 0x01, IIO_VOLTAGE, 1, 3, SCALE_DEFAULT, AMUX_RSV1), + XOADC_CHAN(VPH_PWR, 0x00, 0x02, IIO_VOLTAGE, 1, 3, SCALE_DEFAULT, AMUX_RSV1), + XOADC_CHAN(DIE_TEMP, 0x00, 0x0b, IIO_TEMP, 1, 1, SCALE_PMIC_THERM, AMUX_RSV1), + /* Used for battery ID or battery temperature */ + XOADC_CHAN(AMUX8, 0x00, 0x08, IIO_VOLTAGE, 1, 1, SCALE_DEFAULT, AMUX_RSV2), + XOADC_CHAN(INTERNAL, 0x00, 0x0c, IIO_VOLTAGE, 1, 1, SCALE_DEFAULT, AMUX_RSV1), + XOADC_CHAN(125V, 0x00, 0x0d, IIO_VOLTAGE, 1, 1, SCALE_DEFAULT, AMUX_RSV1), + XOADC_CHAN(MUXOFF, 0x00, 0x0f, IIO_TEMP, 1, 1, SCALE_XOTHERM, AMUX_RSV0), + { }, /* Sentinel */ +}; + +/* + * Taken from arch/arm/mach-msm/board-8930-pmic.c in the vendor tree: + * TODO: needs testing. + */ +static const struct xoadc_channel pm8038_xoadc_channels[] = { + XOADC_CHAN(VCOIN, 0x00, 0x00, IIO_VOLTAGE, 1, 3, SCALE_DEFAULT, AMUX_RSV1), + XOADC_CHAN(VBAT, 0x00, 0x01, IIO_VOLTAGE, 1, 3, SCALE_DEFAULT, AMUX_RSV1), + XOADC_CHAN(DCIN, 0x00, 0x02, IIO_VOLTAGE, 1, 6, SCALE_DEFAULT, AMUX_RSV1), + XOADC_CHAN(ICHG, 0x00, 0x03, IIO_VOLTAGE, 1, 1, SCALE_DEFAULT, AMUX_RSV1), + XOADC_CHAN(VPH_PWR, 0x00, 0x04, IIO_VOLTAGE, 1, 3, SCALE_DEFAULT, AMUX_RSV1), + XOADC_CHAN(AMUX5, 0x00, 0x05, IIO_VOLTAGE, 1, 1, SCALE_DEFAULT, AMUX_RSV1), + XOADC_CHAN(AMUX6, 0x00, 0x06, IIO_VOLTAGE, 1, 1, SCALE_DEFAULT, AMUX_RSV1), + XOADC_CHAN(AMUX7, 0x00, 0x07, IIO_VOLTAGE, 1, 1, SCALE_DEFAULT, AMUX_RSV1), + /* AMUX8 used for battery temperature in most cases */ + XOADC_CHAN(AMUX8, 0x00, 0x08, IIO_TEMP, 1, 1, SCALE_THERM_100K_PULLUP, AMUX_RSV2), + XOADC_CHAN(AMUX9, 0x00, 0x09, IIO_VOLTAGE, 1, 1, SCALE_DEFAULT, AMUX_RSV1), + XOADC_CHAN(USB_VBUS, 0x00, 0x0a, IIO_VOLTAGE, 1, 4, SCALE_DEFAULT, AMUX_RSV1), + XOADC_CHAN(DIE_TEMP, 0x00, 0x0b, IIO_TEMP, 1, 1, SCALE_PMIC_THERM, AMUX_RSV1), + XOADC_CHAN(INTERNAL, 0x00, 0x0c, IIO_VOLTAGE, 1, 1, SCALE_DEFAULT, AMUX_RSV1), + XOADC_CHAN(125V, 0x00, 0x0d, IIO_VOLTAGE, 1, 1, SCALE_DEFAULT, AMUX_RSV1), + XOADC_CHAN(INTERNAL_2, 0x00, 0x0e, IIO_VOLTAGE, 1, 1, SCALE_DEFAULT, AMUX_RSV1), + XOADC_CHAN(MUXOFF, 0x00, 0x0f, IIO_TEMP, 1, 1, SCALE_XOTHERM, AMUX_RSV0), + { }, /* Sentinel */ +}; + +/* + * This was created by cross-referencing the vendor tree + * arch/arm/mach-msm/board-msm8x60.c msm_adc_channels_data[] + * with the "channel types" (first field) to find the right + * configuration for these channels on an MSM8x60 i.e. PM8058 + * setup. + */ +static const struct xoadc_channel pm8058_xoadc_channels[] = { + XOADC_CHAN(VCOIN, 0x00, 0x00, IIO_VOLTAGE, 1, 2, SCALE_DEFAULT, AMUX_RSV1), + XOADC_CHAN(VBAT, 0x00, 0x01, IIO_VOLTAGE, 1, 3, SCALE_DEFAULT, AMUX_RSV1), + XOADC_CHAN(DCIN, 0x00, 0x02, IIO_VOLTAGE, 1, 10, SCALE_DEFAULT, AMUX_RSV1), + XOADC_CHAN(ICHG, 0x00, 0x03, IIO_VOLTAGE, 1, 1, SCALE_DEFAULT, AMUX_RSV1), + XOADC_CHAN(VPH_PWR, 0x00, 0x04, IIO_VOLTAGE, 1, 3, SCALE_DEFAULT, AMUX_RSV1), + /* + * AMUX channels 5 thru 9 are referred to as MPP5 thru MPP9 in + * some code and documentation. But they are really just 5 + * channels just like any other. They are connected to a switching + * matrix where they can be routed to any of the MPPs, not just + * 1-to-1 onto MPP5 thru 9, so naming them MPP5 thru MPP9 is + * very confusing. + */ + XOADC_CHAN(AMUX5, 0x00, 0x05, IIO_VOLTAGE, 1, 1, SCALE_DEFAULT, AMUX_RSV1), + XOADC_CHAN(AMUX6, 0x00, 0x06, IIO_VOLTAGE, 1, 1, SCALE_DEFAULT, AMUX_RSV1), + XOADC_CHAN(AMUX7, 0x00, 0x07, IIO_VOLTAGE, 1, 2, SCALE_DEFAULT, AMUX_RSV1), + XOADC_CHAN(AMUX8, 0x00, 0x08, IIO_VOLTAGE, 1, 2, SCALE_DEFAULT, AMUX_RSV1), + XOADC_CHAN(AMUX9, 0x00, 0x09, IIO_VOLTAGE, 1, 3, SCALE_DEFAULT, AMUX_RSV1), + XOADC_CHAN(USB_VBUS, 0x00, 0x0a, IIO_VOLTAGE, 1, 3, SCALE_DEFAULT, AMUX_RSV1), + XOADC_CHAN(DIE_TEMP, 0x00, 0x0b, IIO_TEMP, 1, 1, SCALE_PMIC_THERM, AMUX_RSV1), + XOADC_CHAN(INTERNAL, 0x00, 0x0c, IIO_VOLTAGE, 1, 1, SCALE_DEFAULT, AMUX_RSV1), + XOADC_CHAN(125V, 0x00, 0x0d, IIO_VOLTAGE, 1, 1, SCALE_DEFAULT, AMUX_RSV1), + XOADC_CHAN(INTERNAL_2, 0x00, 0x0e, IIO_VOLTAGE, 1, 1, SCALE_DEFAULT, AMUX_RSV1), + XOADC_CHAN(MUXOFF, 0x00, 0x0f, IIO_TEMP, 1, 1, SCALE_XOTHERM, AMUX_RSV0), + /* There are also "unity" and divided by 3 channels (prescaler) but noone is using them */ + { }, /* Sentinel */ +}; + +/* + * The PM8921 has some pre-muxing on its channels, this comes from the vendor tree + * include/linux/mfd/pm8xxx/pm8xxx-adc.h + * board-flo-pmic.c (Nexus 7) and board-8064-pmic.c + */ +static const struct xoadc_channel pm8921_xoadc_channels[] = { + XOADC_CHAN(VCOIN, 0x00, 0x00, IIO_VOLTAGE, 1, 3, SCALE_DEFAULT, AMUX_RSV1), + XOADC_CHAN(VBAT, 0x00, 0x01, IIO_VOLTAGE, 1, 3, SCALE_DEFAULT, AMUX_RSV1), + XOADC_CHAN(DCIN, 0x00, 0x02, IIO_VOLTAGE, 1, 6, SCALE_DEFAULT, AMUX_RSV1), + /* channel "ICHG" is reserved and not used on PM8921 */ + XOADC_CHAN(VPH_PWR, 0x00, 0x04, IIO_VOLTAGE, 1, 3, SCALE_DEFAULT, AMUX_RSV1), + XOADC_CHAN(IBAT, 0x00, 0x05, IIO_VOLTAGE, 1, 1, SCALE_DEFAULT, AMUX_RSV1), + /* CHAN 6 & 7 (MPP1 & MPP2) are reserved for MPP channels on PM8921 */ + XOADC_CHAN(BATT_THERM, 0x00, 0x08, IIO_TEMP, 1, 1, SCALE_THERM_100K_PULLUP, AMUX_RSV1), + XOADC_CHAN(BATT_ID, 0x00, 0x09, IIO_VOLTAGE, 1, 1, SCALE_DEFAULT, AMUX_RSV1), + XOADC_CHAN(USB_VBUS, 0x00, 0x0a, IIO_VOLTAGE, 1, 4, SCALE_DEFAULT, AMUX_RSV1), + XOADC_CHAN(DIE_TEMP, 0x00, 0x0b, IIO_TEMP, 1, 1, SCALE_PMIC_THERM, AMUX_RSV1), + XOADC_CHAN(INTERNAL, 0x00, 0x0c, IIO_VOLTAGE, 1, 1, SCALE_DEFAULT, AMUX_RSV1), + XOADC_CHAN(125V, 0x00, 0x0d, IIO_VOLTAGE, 1, 1, SCALE_DEFAULT, AMUX_RSV1), + /* FIXME: look into the scaling of this temperature */ + XOADC_CHAN(CHG_TEMP, 0x00, 0x0e, IIO_TEMP, 1, 1, SCALE_DEFAULT, AMUX_RSV1), + XOADC_CHAN(MUXOFF, 0x00, 0x0f, IIO_TEMP, 1, 1, SCALE_XOTHERM, AMUX_RSV0), + /* The following channels have premux bit 0 set to 1 (all end in 4) */ + XOADC_CHAN(ATEST_8, 0x01, 0x00, IIO_VOLTAGE, 1, 1, SCALE_DEFAULT, AMUX_RSV1), + /* Set scaling to 1/2 based on the name for these two */ + XOADC_CHAN(USB_SNS_DIV20, 0x01, 0x01, IIO_VOLTAGE, 1, 2, SCALE_DEFAULT, AMUX_RSV1), + XOADC_CHAN(DCIN_SNS_DIV20, 0x01, 0x02, IIO_VOLTAGE, 1, 2, SCALE_DEFAULT, AMUX_RSV1), + XOADC_CHAN(AMUX3, 0x01, 0x03, IIO_VOLTAGE, 1, 1, SCALE_DEFAULT, AMUX_RSV1), + XOADC_CHAN(AMUX4, 0x01, 0x04, IIO_VOLTAGE, 1, 1, SCALE_DEFAULT, AMUX_RSV1), + XOADC_CHAN(AMUX5, 0x01, 0x05, IIO_VOLTAGE, 1, 1, SCALE_DEFAULT, AMUX_RSV1), + XOADC_CHAN(AMUX6, 0x01, 0x06, IIO_VOLTAGE, 1, 1, SCALE_DEFAULT, AMUX_RSV1), + XOADC_CHAN(AMUX7, 0x01, 0x07, IIO_VOLTAGE, 1, 1, SCALE_DEFAULT, AMUX_RSV1), + XOADC_CHAN(AMUX8, 0x01, 0x08, IIO_VOLTAGE, 1, 1, SCALE_DEFAULT, AMUX_RSV1), + /* Internal test signals, I think */ + XOADC_CHAN(ATEST_1, 0x01, 0x09, IIO_VOLTAGE, 1, 1, SCALE_DEFAULT, AMUX_RSV1), + XOADC_CHAN(ATEST_2, 0x01, 0x0a, IIO_VOLTAGE, 1, 1, SCALE_DEFAULT, AMUX_RSV1), + XOADC_CHAN(ATEST_3, 0x01, 0x0b, IIO_VOLTAGE, 1, 1, SCALE_DEFAULT, AMUX_RSV1), + XOADC_CHAN(ATEST_4, 0x01, 0x0c, IIO_VOLTAGE, 1, 1, SCALE_DEFAULT, AMUX_RSV1), + XOADC_CHAN(ATEST_5, 0x01, 0x0d, IIO_VOLTAGE, 1, 1, SCALE_DEFAULT, AMUX_RSV1), + XOADC_CHAN(ATEST_6, 0x01, 0x0e, IIO_VOLTAGE, 1, 1, SCALE_DEFAULT, AMUX_RSV1), + XOADC_CHAN(ATEST_7, 0x01, 0x0f, IIO_VOLTAGE, 1, 1, SCALE_DEFAULT, AMUX_RSV1), + /* The following channels have premux bit 1 set to 1 (all end in 8) */ + /* I guess even ATEST8 will be divided by 3 here */ + XOADC_CHAN(ATEST_8, 0x02, 0x00, IIO_VOLTAGE, 1, 3, SCALE_DEFAULT, AMUX_RSV1), + /* I guess div 2 div 3 becomes div 6 */ + XOADC_CHAN(USB_SNS_DIV20_DIV3, 0x02, 0x01, IIO_VOLTAGE, 1, 6, SCALE_DEFAULT, AMUX_RSV1), + XOADC_CHAN(DCIN_SNS_DIV20_DIV3, 0x02, 0x02, IIO_VOLTAGE, 1, 6, SCALE_DEFAULT, AMUX_RSV1), + XOADC_CHAN(AMUX3_DIV3, 0x02, 0x03, IIO_VOLTAGE, 1, 3, SCALE_DEFAULT, AMUX_RSV1), + XOADC_CHAN(AMUX4_DIV3, 0x02, 0x04, IIO_VOLTAGE, 1, 3, SCALE_DEFAULT, AMUX_RSV1), + XOADC_CHAN(AMUX5_DIV3, 0x02, 0x05, IIO_VOLTAGE, 1, 3, SCALE_DEFAULT, AMUX_RSV1), + XOADC_CHAN(AMUX6_DIV3, 0x02, 0x06, IIO_VOLTAGE, 1, 3, SCALE_DEFAULT, AMUX_RSV1), + XOADC_CHAN(AMUX7_DIV3, 0x02, 0x07, IIO_VOLTAGE, 1, 3, SCALE_DEFAULT, AMUX_RSV1), + XOADC_CHAN(AMUX8_DIV3, 0x02, 0x08, IIO_VOLTAGE, 1, 3, SCALE_DEFAULT, AMUX_RSV1), + XOADC_CHAN(ATEST_1_DIV3, 0x02, 0x09, IIO_VOLTAGE, 1, 3, SCALE_DEFAULT, AMUX_RSV1), + XOADC_CHAN(ATEST_2_DIV3, 0x02, 0x0a, IIO_VOLTAGE, 1, 3, SCALE_DEFAULT, AMUX_RSV1), + XOADC_CHAN(ATEST_3_DIV3, 0x02, 0x0b, IIO_VOLTAGE, 1, 3, SCALE_DEFAULT, AMUX_RSV1), + XOADC_CHAN(ATEST_4_DIV3, 0x02, 0x0c, IIO_VOLTAGE, 1, 3, SCALE_DEFAULT, AMUX_RSV1), + XOADC_CHAN(ATEST_5_DIV3, 0x02, 0x0d, IIO_VOLTAGE, 1, 3, SCALE_DEFAULT, AMUX_RSV1), + XOADC_CHAN(ATEST_6_DIV3, 0x02, 0x0e, IIO_VOLTAGE, 1, 3, SCALE_DEFAULT, AMUX_RSV1), + XOADC_CHAN(ATEST_7_DIV3, 0x02, 0x0f, IIO_VOLTAGE, 1, 3, SCALE_DEFAULT, AMUX_RSV1), + { }, /* Sentinel */ +}; + +/** + * struct pm8xxx_chan_info - ADC channel information + * @label: label of this channel from device tree (defaults to datasheet name if not specified) + * @hwchan: pointer to hardware channel information (muxing & scaling settings) + * @calibration: whether to use absolute or ratiometric calibration + * @decimation: 0,1,2,3 + * @amux_ip_rsv: ratiometric scale value if using ratiometric + * calibration: 0, 1, 2, 4, 5. + */ +struct pm8xxx_chan_info { + const char *label; + const struct xoadc_channel *hwchan; + enum vadc_calibration calibration; + u8 decimation:2; + u8 amux_ip_rsv:3; +}; + +/** + * struct pm8xxx_xoadc - state container for the XOADC + * @dev: pointer to device + * @map: regmap to access registers + * @variant: XOADC variant characteristics + * @vref: reference voltage regulator + * characteristics of the channels, and sensible default settings + * @nchans: number of channels, configured by the device tree + * @chans: the channel information per-channel, configured by the device tree + * @iio_chans: IIO channel specifiers + * @graph: linear calibration parameters for absolute and + * ratiometric measurements + * @complete: completion to indicate end of conversion + * @lock: lock to restrict access to the hardware to one client at the time + */ +struct pm8xxx_xoadc { + struct device *dev; + struct regmap *map; + const struct xoadc_variant *variant; + struct regulator *vref; + unsigned int nchans; + struct pm8xxx_chan_info *chans; + struct iio_chan_spec *iio_chans; + struct vadc_linear_graph graph[2]; + struct completion complete; + struct mutex lock; +}; + +static irqreturn_t pm8xxx_eoc_irq(int irq, void *d) +{ + struct iio_dev *indio_dev = d; + struct pm8xxx_xoadc *adc = iio_priv(indio_dev); + + complete(&adc->complete); + + return IRQ_HANDLED; +} + +static struct pm8xxx_chan_info * +pm8xxx_get_channel(struct pm8xxx_xoadc *adc, u8 chan) +{ + int i; + + for (i = 0; i < adc->nchans; i++) { + struct pm8xxx_chan_info *ch = &adc->chans[i]; + if (ch->hwchan->amux_channel == chan) + return ch; + } + return NULL; +} + +static int pm8xxx_read_channel_rsv(struct pm8xxx_xoadc *adc, + const struct pm8xxx_chan_info *ch, + u8 rsv, u16 *adc_code, + bool force_ratiometric) +{ + int ret; + unsigned int val; + u8 rsvmask, rsvval; + u8 lsb, msb; + + dev_dbg(adc->dev, "read channel \"%s\", amux %d, prescale/mux: %d, rsv %d\n", + ch->label, ch->hwchan->amux_channel, ch->hwchan->pre_scale_mux, rsv); + + mutex_lock(&adc->lock); + + /* Mux in this channel */ + val = ch->hwchan->amux_channel << ADC_AMUX_SEL_SHIFT; + val |= ch->hwchan->pre_scale_mux << ADC_AMUX_PREMUX_SHIFT; + ret = regmap_write(adc->map, ADC_ARB_USRP_AMUX_CNTRL, val); + if (ret) + goto unlock; + + /* Set up ratiometric scale value, mask off all bits except these */ + rsvmask = (ADC_ARB_USRP_RSV_RST | ADC_ARB_USRP_RSV_DTEST0 | + ADC_ARB_USRP_RSV_DTEST1 | ADC_ARB_USRP_RSV_OP); + if (adc->variant->broken_ratiometric && !force_ratiometric) { + /* + * Apparently the PM8058 has some kind of bug which is + * reflected in the vendor tree drivers/misc/pmix8058-xoadc.c + * which just hardcodes the RSV selector to SEL1 (0x20) for + * most cases and SEL0 (0x10) for the MUXOFF channel only. + * If we force ratiometric (currently only done when attempting + * to do ratiometric calibration) this doesn't seem to work + * very well and I suspect ratiometric conversion is simply + * broken or not supported on the PM8058. + * + * Maybe IO_SEL2 doesn't exist on PM8058 and bits 4 & 5 select + * the mode alone. + * + * Some PM8058 register documentation would be nice to get + * this right. + */ + if (ch->hwchan->amux_channel == PM8XXX_CHANNEL_MUXOFF) + rsvval = ADC_ARB_USRP_RSV_IP_SEL0; + else + rsvval = ADC_ARB_USRP_RSV_IP_SEL1; + } else { + if (rsv == 0xff) + rsvval = (ch->amux_ip_rsv << ADC_RSV_IP_SEL_SHIFT) | + ADC_ARB_USRP_RSV_TRM; + else + rsvval = (rsv << ADC_RSV_IP_SEL_SHIFT) | + ADC_ARB_USRP_RSV_TRM; + } + + ret = regmap_update_bits(adc->map, + ADC_ARB_USRP_RSV, + ~rsvmask, + rsvval); + if (ret) + goto unlock; + + ret = regmap_write(adc->map, ADC_ARB_USRP_ANA_PARAM, + ADC_ARB_USRP_ANA_PARAM_DIS); + if (ret) + goto unlock; + + /* Decimation factor */ + ret = regmap_write(adc->map, ADC_ARB_USRP_DIG_PARAM, + ADC_ARB_USRP_DIG_PARAM_SEL_SHIFT0 | + ADC_ARB_USRP_DIG_PARAM_SEL_SHIFT1 | + ch->decimation << ADC_DIG_PARAM_DEC_SHIFT); + if (ret) + goto unlock; + + ret = regmap_write(adc->map, ADC_ARB_USRP_ANA_PARAM, + ADC_ARB_USRP_ANA_PARAM_EN); + if (ret) + goto unlock; + + /* Enable the arbiter, the Qualcomm code does it twice like this */ + ret = regmap_write(adc->map, ADC_ARB_USRP_CNTRL, + ADC_ARB_USRP_CNTRL_EN_ARB); + if (ret) + goto unlock; + ret = regmap_write(adc->map, ADC_ARB_USRP_CNTRL, + ADC_ARB_USRP_CNTRL_EN_ARB); + if (ret) + goto unlock; + + + /* Fire a request! */ + reinit_completion(&adc->complete); + ret = regmap_write(adc->map, ADC_ARB_USRP_CNTRL, + ADC_ARB_USRP_CNTRL_EN_ARB | + ADC_ARB_USRP_CNTRL_REQ); + if (ret) + goto unlock; + + /* Next the interrupt occurs */ + if (!wait_for_completion_timeout(&adc->complete, VADC_CONV_TIME_MAX_US)) { + ret = -ETIMEDOUT; + goto unlock; + } + + ret = regmap_read(adc->map, ADC_ARB_USRP_DATA0, &val); + if (ret) + goto unlock; + lsb = val; + ret = regmap_read(adc->map, ADC_ARB_USRP_DATA1, &val); + if (ret) + goto unlock; + msb = val; + *adc_code = (msb << 8) | lsb; + + /* Turn off the ADC by setting the arbiter to 0 twice */ + ret = regmap_write(adc->map, ADC_ARB_USRP_CNTRL, 0); + if (ret) + goto unlock; + ret = regmap_write(adc->map, ADC_ARB_USRP_CNTRL, 0); + if (ret) + goto unlock; + +unlock: + mutex_unlock(&adc->lock); + return ret; +} + +static int pm8xxx_read_channel(struct pm8xxx_xoadc *adc, + const struct pm8xxx_chan_info *ch, + u16 *adc_code) +{ + /* + * Normally we just use the ratiometric scale value (RSV) predefined + * for the channel, but during calibration we need to modify this + * so this wrapper is a helper hiding the more complex version. + */ + return pm8xxx_read_channel_rsv(adc, ch, 0xff, adc_code, false); +} + +static int pm8xxx_calibrate_device(struct pm8xxx_xoadc *adc) +{ + const struct pm8xxx_chan_info *ch; + u16 read_1250v; + u16 read_0625v; + u16 read_nomux_rsv5; + u16 read_nomux_rsv4; + int ret; + + adc->graph[VADC_CALIB_ABSOLUTE].dx = VADC_ABSOLUTE_RANGE_UV; + adc->graph[VADC_CALIB_RATIOMETRIC].dx = VADC_RATIOMETRIC_RANGE; + + /* Common reference channel calibration */ + ch = pm8xxx_get_channel(adc, PM8XXX_CHANNEL_125V); + if (!ch) + return -ENODEV; + ret = pm8xxx_read_channel(adc, ch, &read_1250v); + if (ret) { + dev_err(adc->dev, "could not read 1.25V reference channel\n"); + return -ENODEV; + } + ch = pm8xxx_get_channel(adc, PM8XXX_CHANNEL_INTERNAL); + if (!ch) + return -ENODEV; + ret = pm8xxx_read_channel(adc, ch, &read_0625v); + if (ret) { + dev_err(adc->dev, "could not read 0.625V reference channel\n"); + return -ENODEV; + } + if (read_1250v == read_0625v) { + dev_err(adc->dev, "read same ADC code for 1.25V and 0.625V\n"); + return -ENODEV; + } + + adc->graph[VADC_CALIB_ABSOLUTE].dy = read_1250v - read_0625v; + adc->graph[VADC_CALIB_ABSOLUTE].gnd = read_0625v; + + dev_info(adc->dev, "absolute calibration dx = %d uV, dy = %d units\n", + VADC_ABSOLUTE_RANGE_UV, adc->graph[VADC_CALIB_ABSOLUTE].dy); + + /* Ratiometric calibration */ + ch = pm8xxx_get_channel(adc, PM8XXX_CHANNEL_MUXOFF); + if (!ch) + return -ENODEV; + ret = pm8xxx_read_channel_rsv(adc, ch, AMUX_RSV5, + &read_nomux_rsv5, true); + if (ret) { + dev_err(adc->dev, "could not read MUXOFF reference channel\n"); + return -ENODEV; + } + ret = pm8xxx_read_channel_rsv(adc, ch, AMUX_RSV4, + &read_nomux_rsv4, true); + if (ret) { + dev_err(adc->dev, "could not read MUXOFF reference channel\n"); + return -ENODEV; + } + adc->graph[VADC_CALIB_RATIOMETRIC].dy = + read_nomux_rsv5 - read_nomux_rsv4; + adc->graph[VADC_CALIB_RATIOMETRIC].gnd = read_nomux_rsv4; + + dev_info(adc->dev, "ratiometric calibration dx = %d, dy = %d units\n", + VADC_RATIOMETRIC_RANGE, + adc->graph[VADC_CALIB_RATIOMETRIC].dy); + + return 0; +} + +static int pm8xxx_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct pm8xxx_xoadc *adc = iio_priv(indio_dev); + const struct pm8xxx_chan_info *ch; + u16 adc_code; + int ret; + + switch (mask) { + case IIO_CHAN_INFO_PROCESSED: + ch = pm8xxx_get_channel(adc, chan->address); + if (!ch) + return -EINVAL; + ret = pm8xxx_read_channel(adc, ch, &adc_code); + if (ret) + return ret; + + ret = qcom_vadc_scale(ch->hwchan->scale_fn_type, + &adc->graph[ch->calibration], + &ch->hwchan->prescale, + (ch->calibration == VADC_CALIB_ABSOLUTE), + adc_code, val); + if (ret) + return ret; + + return IIO_VAL_INT; + case IIO_CHAN_INFO_RAW: + ch = pm8xxx_get_channel(adc, chan->address); + if (!ch) + return -EINVAL; + ret = pm8xxx_read_channel(adc, ch, &adc_code); + if (ret) + return ret; + + *val = (int)adc_code; + return IIO_VAL_INT; + default: + return -EINVAL; + } +} + +static int pm8xxx_fwnode_xlate(struct iio_dev *indio_dev, + const struct fwnode_reference_args *iiospec) +{ + struct pm8xxx_xoadc *adc = iio_priv(indio_dev); + u8 pre_scale_mux; + u8 amux_channel; + unsigned int i; + + /* + * First cell is prescaler or premux, second cell is analog + * mux. + */ + if (iiospec->nargs != 2) { + dev_err(&indio_dev->dev, "wrong number of arguments for %pfwP need 2 got %d\n", + iiospec->fwnode, + iiospec->nargs); + return -EINVAL; + } + pre_scale_mux = (u8)iiospec->args[0]; + amux_channel = (u8)iiospec->args[1]; + dev_dbg(&indio_dev->dev, "pre scale/mux: %02x, amux: %02x\n", + pre_scale_mux, amux_channel); + + /* We need to match exactly on the prescale/premux and channel */ + for (i = 0; i < adc->nchans; i++) + if (adc->chans[i].hwchan->pre_scale_mux == pre_scale_mux && + adc->chans[i].hwchan->amux_channel == amux_channel) + return i; + + return -EINVAL; +} + +static int pm8xxx_read_label(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, char *label) +{ + struct pm8xxx_xoadc *adc = iio_priv(indio_dev); + const struct pm8xxx_chan_info *ch; + + ch = pm8xxx_get_channel(adc, chan->address); + if (!ch) + return -EINVAL; + return sysfs_emit(label, "%s\n", ch->label); +} + +static const struct iio_info pm8xxx_xoadc_info = { + .fwnode_xlate = pm8xxx_fwnode_xlate, + .read_label = pm8xxx_read_label, + .read_raw = pm8xxx_read_raw, +}; + +static int pm8xxx_xoadc_parse_channel(struct device *dev, + struct fwnode_handle *fwnode, + const struct xoadc_channel *hw_channels, + struct iio_chan_spec *iio_chan, + struct pm8xxx_chan_info *ch) +{ + const char *name = fwnode_get_name(fwnode); + const struct xoadc_channel *hwchan; + u32 pre_scale_mux, amux_channel, reg[2]; + u32 rsv, dec; + int ret; + int chid; + + ret = fwnode_property_read_u32_array(fwnode, "reg", reg, + ARRAY_SIZE(reg)); + if (ret) { + dev_err(dev, "invalid pre scale/mux or amux channel number %s\n", + name); + return ret; + } + + pre_scale_mux = reg[0]; + amux_channel = reg[1]; + + /* Find the right channel setting */ + chid = 0; + hwchan = &hw_channels[0]; + while (hwchan->datasheet_name) { + if (hwchan->pre_scale_mux == pre_scale_mux && + hwchan->amux_channel == amux_channel) + break; + hwchan++; + chid++; + } + /* The sentinel does not have a name assigned */ + if (!hwchan->datasheet_name) { + dev_err(dev, "could not locate channel %02x/%02x\n", + pre_scale_mux, amux_channel); + return -EINVAL; + } + ch->label = hwchan->datasheet_name; + fwnode_property_read_string(fwnode, "label", &ch->label); + ch->hwchan = hwchan; + /* Everyone seems to use absolute calibration except in special cases */ + ch->calibration = VADC_CALIB_ABSOLUTE; + /* Everyone seems to use default ("type 2") decimation */ + ch->decimation = VADC_DEF_DECIMATION; + + if (!fwnode_property_read_u32(fwnode, "qcom,ratiometric", &rsv)) { + ch->calibration = VADC_CALIB_RATIOMETRIC; + if (rsv > XOADC_RSV_MAX) { + dev_err(dev, "%s too large RSV value %d\n", name, rsv); + return -EINVAL; + } + if (rsv == AMUX_RSV3) { + dev_err(dev, "%s invalid RSV value %d\n", name, rsv); + return -EINVAL; + } + } + + /* Optional decimation, if omitted we use the default */ + ret = fwnode_property_read_u32(fwnode, "qcom,decimation", &dec); + if (!ret) { + ret = qcom_vadc_decimation_from_dt(dec); + if (ret < 0) { + dev_err(dev, "%s invalid decimation %d\n", + name, dec); + return ret; + } + ch->decimation = ret; + } + + iio_chan->channel = chid; + iio_chan->address = hwchan->amux_channel; + iio_chan->datasheet_name = hwchan->datasheet_name; + iio_chan->type = hwchan->type; + /* All channels are raw or processed */ + iio_chan->info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_PROCESSED); + iio_chan->indexed = 1; + + dev_dbg(dev, + "channel [PRESCALE/MUX: %02x AMUX: %02x] \"%s\" ref voltage: %d, decimation %d prescale %d/%d, scale function %d\n", + hwchan->pre_scale_mux, hwchan->amux_channel, ch->label, + ch->amux_ip_rsv, ch->decimation, hwchan->prescale.numerator, + hwchan->prescale.denominator, hwchan->scale_fn_type); + + return 0; +} + +static int pm8xxx_xoadc_parse_channels(struct pm8xxx_xoadc *adc) +{ + struct pm8xxx_chan_info *ch; + int ret; + int i; + + adc->nchans = device_get_child_node_count(adc->dev); + if (!adc->nchans) { + dev_err(adc->dev, "no channel children\n"); + return -ENODEV; + } + dev_dbg(adc->dev, "found %d ADC channels\n", adc->nchans); + + adc->iio_chans = devm_kcalloc(adc->dev, adc->nchans, + sizeof(*adc->iio_chans), GFP_KERNEL); + if (!adc->iio_chans) + return -ENOMEM; + + adc->chans = devm_kcalloc(adc->dev, adc->nchans, + sizeof(*adc->chans), GFP_KERNEL); + if (!adc->chans) + return -ENOMEM; + + i = 0; + device_for_each_child_node_scoped(adc->dev, child) { + ch = &adc->chans[i]; + ret = pm8xxx_xoadc_parse_channel(adc->dev, child, + adc->variant->channels, + &adc->iio_chans[i], + ch); + if (ret) + return ret; + + i++; + } + + /* Check for required channels */ + ch = pm8xxx_get_channel(adc, PM8XXX_CHANNEL_125V); + if (!ch) { + dev_err(adc->dev, "missing 1.25V reference channel\n"); + return -ENODEV; + } + ch = pm8xxx_get_channel(adc, PM8XXX_CHANNEL_INTERNAL); + if (!ch) { + dev_err(adc->dev, "missing 0.625V reference channel\n"); + return -ENODEV; + } + ch = pm8xxx_get_channel(adc, PM8XXX_CHANNEL_MUXOFF); + if (!ch) { + dev_err(adc->dev, "missing MUXOFF reference channel\n"); + return -ENODEV; + } + + return 0; +} + +static int pm8xxx_xoadc_probe(struct platform_device *pdev) +{ + const struct xoadc_variant *variant; + struct pm8xxx_xoadc *adc; + struct iio_dev *indio_dev; + struct regmap *map; + struct device *dev = &pdev->dev; + int ret; + + variant = device_get_match_data(dev); + if (!variant) + return -ENODEV; + + indio_dev = devm_iio_device_alloc(dev, sizeof(*adc)); + if (!indio_dev) + return -ENOMEM; + platform_set_drvdata(pdev, indio_dev); + + adc = iio_priv(indio_dev); + adc->dev = dev; + adc->variant = variant; + init_completion(&adc->complete); + mutex_init(&adc->lock); + + ret = pm8xxx_xoadc_parse_channels(adc); + if (ret) + return ret; + + map = dev_get_regmap(dev->parent, NULL); + if (!map) { + dev_err(dev, "parent regmap unavailable.\n"); + return -ENODEV; + } + adc->map = map; + + /* Bring up regulator */ + adc->vref = devm_regulator_get(dev, "xoadc-ref"); + if (IS_ERR(adc->vref)) + return dev_err_probe(dev, PTR_ERR(adc->vref), + "failed to get XOADC VREF regulator\n"); + ret = regulator_enable(adc->vref); + if (ret) { + dev_err(dev, "failed to enable XOADC VREF regulator\n"); + return ret; + } + + ret = devm_request_threaded_irq(dev, platform_get_irq(pdev, 0), + pm8xxx_eoc_irq, NULL, 0, variant->name, indio_dev); + if (ret) { + dev_err(dev, "unable to request IRQ\n"); + goto out_disable_vref; + } + + indio_dev->name = variant->name; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->info = &pm8xxx_xoadc_info; + indio_dev->channels = adc->iio_chans; + indio_dev->num_channels = adc->nchans; + + ret = iio_device_register(indio_dev); + if (ret) + goto out_disable_vref; + + ret = pm8xxx_calibrate_device(adc); + if (ret) + goto out_unreg_device; + + dev_info(dev, "%s XOADC driver enabled\n", variant->name); + + return 0; + +out_unreg_device: + iio_device_unregister(indio_dev); +out_disable_vref: + regulator_disable(adc->vref); + + return ret; +} + +static void pm8xxx_xoadc_remove(struct platform_device *pdev) +{ + struct iio_dev *indio_dev = platform_get_drvdata(pdev); + struct pm8xxx_xoadc *adc = iio_priv(indio_dev); + + iio_device_unregister(indio_dev); + + regulator_disable(adc->vref); +} + +static const struct xoadc_variant pm8018_variant = { + .name = "PM8018-XOADC", + .channels = pm8018_xoadc_channels, +}; + +static const struct xoadc_variant pm8038_variant = { + .name = "PM8038-XOADC", + .channels = pm8038_xoadc_channels, +}; + +static const struct xoadc_variant pm8058_variant = { + .name = "PM8058-XOADC", + .channels = pm8058_xoadc_channels, + .broken_ratiometric = true, + .prescaling = true, +}; + +static const struct xoadc_variant pm8921_variant = { + .name = "PM8921-XOADC", + .channels = pm8921_xoadc_channels, + .second_level_mux = true, +}; + +static const struct of_device_id pm8xxx_xoadc_id_table[] = { + { + .compatible = "qcom,pm8018-adc", + .data = &pm8018_variant, + }, + { + .compatible = "qcom,pm8038-adc", + .data = &pm8038_variant, + }, + { + .compatible = "qcom,pm8058-adc", + .data = &pm8058_variant, + }, + { + .compatible = "qcom,pm8921-adc", + .data = &pm8921_variant, + }, + { } +}; +MODULE_DEVICE_TABLE(of, pm8xxx_xoadc_id_table); + +static struct platform_driver pm8xxx_xoadc_driver = { + .driver = { + .name = "pm8xxx-adc", + .of_match_table = pm8xxx_xoadc_id_table, + }, + .probe = pm8xxx_xoadc_probe, + .remove = pm8xxx_xoadc_remove, +}; +module_platform_driver(pm8xxx_xoadc_driver); + +MODULE_DESCRIPTION("PM8xxx XOADC driver"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:pm8xxx-xoadc"); diff --git a/drivers/iio/adc/qcom-spmi-adc5-gen3.c b/drivers/iio/adc/qcom-spmi-adc5-gen3.c new file mode 100644 index 000000000..c68c6c5f6 --- /dev/null +++ b/drivers/iio/adc/qcom-spmi-adc5-gen3.c @@ -0,0 +1,828 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define ADC5_GEN3_VADC_SDAM 0x0 + +struct adc5_chip; + +/** + * struct adc5_channel_prop - ADC channel structure + * @common_props: structure with ADC channel properties (common to TM usage). + * @adc_tm: indicates TM type if the channel is used for TM measurements. + * @chip: pointer to top-level ADC device structure. + */ +struct adc5_channel_prop { + struct adc5_channel_common_prop common_props; + int adc_tm; + struct adc5_chip *chip; +}; + +/** + * struct adc5_chip - ADC private structure. + * @dev: SPMI ADC5 Gen3 device. + * @dev_data: Top-level ADC device data. + * @nchannels: number of ADC channels. + * @chan_props: array of ADC channel properties. + * @iio_chans: array of IIO channels specification. + * @complete: ADC result notification after interrupt is received. + * @lock: ADC lock for access to the peripheral, to prevent concurrent + * requests from multiple clients. + * @data: software configuration data. + * @n_tm_channels: number of ADC channels used for TM measurements. + */ +struct adc5_chip { + struct device *dev; + struct adc5_device_data dev_data; + unsigned int nchannels; + struct adc5_channel_prop *chan_props; + struct iio_chan_spec *iio_chans; + struct completion complete; + struct mutex lock; + const struct adc5_data *data; + unsigned int n_tm_channels; +}; + +int adc5_gen3_read(struct adc5_device_data *adc, unsigned int sdam_index, + u16 offset, u8 *data, int len) +{ + return regmap_bulk_read(adc->regmap, + adc->base[sdam_index].base_addr + offset, + data, len); +} +EXPORT_SYMBOL_NS_GPL(adc5_gen3_read, "QCOM_SPMI_ADC5_GEN3"); + +int adc5_gen3_write(struct adc5_device_data *adc, unsigned int sdam_index, + u16 offset, u8 *data, int len) +{ + return regmap_bulk_write(adc->regmap, + adc->base[sdam_index].base_addr + offset, + data, len); +} +EXPORT_SYMBOL_NS_GPL(adc5_gen3_write, "QCOM_SPMI_ADC5_GEN3"); + +static int adc5_gen3_read_voltage_data(struct adc5_chip *adc, u16 *data) +{ + u8 rslt[2]; + int ret; + + ret = adc5_gen3_read(&adc->dev_data, ADC5_GEN3_VADC_SDAM, + ADC5_GEN3_CH_DATA0(0), rslt, sizeof(rslt)); + if (ret) + return ret; + + *data = get_unaligned_le16(rslt); + + if (*data == ADC5_USR_DATA_CHECK) { + dev_err(adc->dev, "Invalid data:%#x\n", *data); + return -EINVAL; + } + + dev_dbg(adc->dev, "voltage raw code:%#x\n", *data); + + return 0; +} + +void adc5_gen3_update_dig_param(struct adc5_channel_common_prop *prop, u8 *data) +{ + /* Update calibration select and decimation ratio select */ + *data &= ~(ADC5_GEN3_DIG_PARAM_CAL_SEL_MASK | ADC5_GEN3_DIG_PARAM_DEC_RATIO_SEL_MASK); + *data |= FIELD_PREP(ADC5_GEN3_DIG_PARAM_CAL_SEL_MASK, prop->cal_method); + *data |= FIELD_PREP(ADC5_GEN3_DIG_PARAM_DEC_RATIO_SEL_MASK, prop->decimation); +} +EXPORT_SYMBOL_NS_GPL(adc5_gen3_update_dig_param, "QCOM_SPMI_ADC5_GEN3"); + +#define ADC5_GEN3_READ_CONFIG_REGS 7 + +static int adc5_gen3_configure(struct adc5_chip *adc, + struct adc5_channel_common_prop *prop) +{ + u8 buf[ADC5_GEN3_READ_CONFIG_REGS]; + u8 conv_req = 0; + int ret; + + ret = adc5_gen3_read(&adc->dev_data, ADC5_GEN3_VADC_SDAM, ADC5_GEN3_SID, + buf, sizeof(buf)); + if (ret) + return ret; + + /* Write SID */ + buf[0] = FIELD_PREP(ADC5_GEN3_SID_MASK, prop->sid); + + /* + * Use channel 0 by default for immediate conversion and to indicate + * there is an actual conversion request + */ + buf[1] = ADC5_GEN3_CHAN_CONV_REQ | 0; + + buf[2] = ADC5_GEN3_TIME_IMMEDIATE; + + /* Digital param selection */ + adc5_gen3_update_dig_param(prop, &buf[3]); + + /* Update fast average sample value */ + buf[4] = FIELD_PREP(ADC5_GEN3_FAST_AVG_CTL_SAMPLES_MASK, + prop->avg_samples) | ADC5_GEN3_FAST_AVG_CTL_EN; + + /* Select ADC channel */ + buf[5] = prop->channel; + + /* Select HW settle delay for channel */ + buf[6] = FIELD_PREP(ADC5_GEN3_HW_SETTLE_DELAY_MASK, + prop->hw_settle_time_us); + + reinit_completion(&adc->complete); + + ret = adc5_gen3_write(&adc->dev_data, ADC5_GEN3_VADC_SDAM, ADC5_GEN3_SID, + buf, sizeof(buf)); + if (ret) + return ret; + + conv_req = ADC5_GEN3_CONV_REQ_REQ; + return adc5_gen3_write(&adc->dev_data, ADC5_GEN3_VADC_SDAM, + ADC5_GEN3_CONV_REQ, &conv_req, sizeof(conv_req)); +} + +/* + * Worst case delay from PBS in readying handshake bit can be up to 15ms, when + * PBS is busy running other simultaneous transactions, while in the best case, + * it is already ready at this point. Assigning polling delay and retry count + * accordingly. + */ + +#define ADC5_GEN3_HS_DELAY_US 100 +#define ADC5_GEN3_HS_RETRY_COUNT 150 + +int adc5_gen3_poll_wait_hs(struct adc5_device_data *adc, + unsigned int sdam_index) +{ + u8 conv_req = ADC5_GEN3_CONV_REQ_REQ; + int ret, count; + u8 status = 0; + + for (count = 0; count < ADC5_GEN3_HS_RETRY_COUNT; count++) { + ret = adc5_gen3_read(adc, sdam_index, ADC5_GEN3_HS, &status, sizeof(status)); + if (ret) + return ret; + + if (status == ADC5_GEN3_HS_READY) { + ret = adc5_gen3_read(adc, sdam_index, ADC5_GEN3_CONV_REQ, + &conv_req, sizeof(conv_req)); + if (ret) + return ret; + + if (!conv_req) + return 0; + } + + fsleep(ADC5_GEN3_HS_DELAY_US); + } + + pr_err("Setting HS ready bit timed out, sdam_index:%d, status:%#x\n", + sdam_index, status); + return -ETIMEDOUT; +} +EXPORT_SYMBOL_NS_GPL(adc5_gen3_poll_wait_hs, "QCOM_SPMI_ADC5_GEN3"); + +int adc5_gen3_status_clear(struct adc5_device_data *adc, + int sdam_index, u16 offset, u8 *val, int len) +{ + u8 value; + int ret; + + ret = adc5_gen3_write(adc, sdam_index, offset, val, len); + if (ret) + return ret; + + /* To indicate conversion request is only to clear a status */ + value = 0; + ret = adc5_gen3_write(adc, sdam_index, ADC5_GEN3_PERPH_CH, &value, + sizeof(value)); + if (ret) + return ret; + + value = ADC5_GEN3_CONV_REQ_REQ; + return adc5_gen3_write(adc, sdam_index, ADC5_GEN3_CONV_REQ, &value, + sizeof(value)); +} +EXPORT_SYMBOL_NS_GPL(adc5_gen3_status_clear, "QCOM_SPMI_ADC5_GEN3"); + +/* + * Worst case delay from PBS for conversion time can be up to 500ms, when PBS + * has timed out twice, once for the initial attempt and once for a retry of + * the same transaction. + */ + +#define ADC5_GEN3_CONV_TIMEOUT_MS 501 + +static int adc5_gen3_do_conversion(struct adc5_chip *adc, + struct adc5_channel_common_prop *prop, + u16 *data_volt) +{ + unsigned long rc; + int ret; + u8 val; + + guard(mutex)(&adc->lock); + ret = adc5_gen3_poll_wait_hs(&adc->dev_data, ADC5_GEN3_VADC_SDAM); + if (ret) + return ret; + + ret = adc5_gen3_configure(adc, prop); + if (ret) { + dev_err(adc->dev, "ADC configure failed with %d\n", ret); + return ret; + } + + /* No support for polling mode at present */ + rc = wait_for_completion_timeout(&adc->complete, + msecs_to_jiffies(ADC5_GEN3_CONV_TIMEOUT_MS)); + if (!rc) { + dev_err(adc->dev, "Reading ADC channel %s timed out\n", + prop->label); + return -ETIMEDOUT; + } + + ret = adc5_gen3_read_voltage_data(adc, data_volt); + if (ret) + return ret; + + val = BIT(0); + return adc5_gen3_status_clear(&adc->dev_data, ADC5_GEN3_VADC_SDAM, + ADC5_GEN3_EOC_CLR, &val, 1); +} + +static irqreturn_t adc5_gen3_isr(int irq, void *dev_id) +{ + struct adc5_chip *adc = dev_id; + struct device *dev = adc->dev; + u8 status, eoc_status, val; + int ret; + + ret = adc5_gen3_read(&adc->dev_data, ADC5_GEN3_VADC_SDAM, + ADC5_GEN3_STATUS1, &status, sizeof(status)); + if (ret) { + dev_err(dev, "adc read status1 failed with %d\n", ret); + return IRQ_NONE; + } + + ret = adc5_gen3_read(&adc->dev_data, ADC5_GEN3_VADC_SDAM, + ADC5_GEN3_EOC_STS, &eoc_status, sizeof(eoc_status)); + if (ret) { + dev_err(dev, "adc read eoc status failed with %d\n", ret); + return IRQ_NONE; + } + + if (status & ADC5_GEN3_STATUS1_CONV_FAULT) { + dev_err_ratelimited(dev, + "Unexpected conversion fault, status:%#x, eoc_status:%#x\n", + status, eoc_status); + val = ADC5_GEN3_CONV_ERR_CLR_REQ; + adc5_gen3_status_clear(&adc->dev_data, ADC5_GEN3_VADC_SDAM, + ADC5_GEN3_CONV_ERR_CLR, &val, 1); + return IRQ_HANDLED; + } + + dev_dbg(dev, "Interrupt status:%#x, EOC status:%#x\n", status, eoc_status); + + /* CHAN0 is the preconfigured channel for immediate conversion */ + if (!(eoc_status & ADC5_GEN3_EOC_CHAN_0)) + return IRQ_NONE; + + complete(&adc->complete); + return IRQ_HANDLED; +} + +static int adc5_gen3_fwnode_xlate(struct iio_dev *indio_dev, + const struct fwnode_reference_args *iiospec) +{ + struct adc5_chip *adc = iio_priv(indio_dev); + int i, v_channel; + + for (i = 0; i < adc->nchannels; i++) { + v_channel = ADC5_GEN3_V_CHAN(adc->chan_props[i].common_props); + if (v_channel == iiospec->args[0]) + return i; + } + + return -ENOENT; +} + +static int adc5_gen3_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int *val, + int *val2, long mask) +{ + struct adc5_chip *adc = iio_priv(indio_dev); + struct adc5_channel_common_prop *prop; + u16 adc_code_volt; + int ret; + + prop = &adc->chan_props[chan->address].common_props; + + switch (mask) { + case IIO_CHAN_INFO_PROCESSED: + ret = adc5_gen3_do_conversion(adc, prop, &adc_code_volt); + if (ret) + return ret; + + ret = qcom_adc5_hw_scale(prop->scale_fn_type, prop->prescale, + adc->data, adc_code_volt, val); + if (ret) + return ret; + + return IIO_VAL_INT; + default: + return -EINVAL; + } +} + +static int adc5_gen3_read_label(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, char *label) +{ + struct adc5_chip *adc = iio_priv(indio_dev); + struct adc5_channel_prop *prop; + + prop = &adc->chan_props[chan->address]; + return sprintf(label, "%s\n", prop->common_props.label); +} + +static const struct iio_info adc5_gen3_info = { + .read_raw = adc5_gen3_read_raw, + .read_label = adc5_gen3_read_label, + .fwnode_xlate = adc5_gen3_fwnode_xlate, +}; + +struct adc5_channels { + unsigned int prescale_index; + enum iio_chan_type type; + long info_mask; + enum vadc_scale_fn_type scale_fn_type; +}; + +/* In these definitions, _pre refers to an index into adc5_prescale_ratios. */ +#define ADC5_CHAN(_type, _mask, _pre, _scale) \ + { \ + .prescale_index = _pre, \ + .type = _type, \ + .info_mask = _mask, \ + .scale_fn_type = _scale, \ + }, \ + +#define ADC5_CHAN_TEMP(_pre, _scale) \ + ADC5_CHAN(IIO_TEMP, BIT(IIO_CHAN_INFO_PROCESSED), _pre, _scale) \ + +#define ADC5_CHAN_VOLT(_pre, _scale) \ + ADC5_CHAN(IIO_VOLTAGE, BIT(IIO_CHAN_INFO_PROCESSED), _pre, _scale) \ + +#define ADC5_CHAN_CUR(_pre, _scale) \ + ADC5_CHAN(IIO_CURRENT, BIT(IIO_CHAN_INFO_PROCESSED), _pre, _scale) \ + +static const struct adc5_channels adc5_gen3_chans_pmic[ADC5_MAX_CHANNEL] = { + [ADC5_GEN3_REF_GND] = ADC5_CHAN_VOLT(0, SCALE_HW_CALIB_DEFAULT) + [ADC5_GEN3_1P25VREF] = ADC5_CHAN_VOLT(0, SCALE_HW_CALIB_DEFAULT) + [ADC5_GEN3_VPH_PWR] = ADC5_CHAN_VOLT(1, SCALE_HW_CALIB_DEFAULT) + [ADC5_GEN3_VBAT_SNS_QBG] = ADC5_CHAN_VOLT(1, SCALE_HW_CALIB_DEFAULT) + [ADC5_GEN3_USB_SNS_V_16] = ADC5_CHAN_TEMP(8, SCALE_HW_CALIB_DEFAULT) + [ADC5_GEN3_VIN_DIV16_MUX] = ADC5_CHAN_TEMP(8, SCALE_HW_CALIB_DEFAULT) + [ADC5_GEN3_DIE_TEMP] = ADC5_CHAN_TEMP(0, + SCALE_HW_CALIB_PMIC_THERM_PM7) + [ADC5_GEN3_AMUX1_THM_100K_PU] = ADC5_CHAN_TEMP(0, + SCALE_HW_CALIB_THERM_100K_PU_PM7) + [ADC5_GEN3_AMUX2_THM_100K_PU] = ADC5_CHAN_TEMP(0, + SCALE_HW_CALIB_THERM_100K_PU_PM7) + [ADC5_GEN3_AMUX3_THM_100K_PU] = ADC5_CHAN_TEMP(0, + SCALE_HW_CALIB_THERM_100K_PU_PM7) + [ADC5_GEN3_AMUX4_THM_100K_PU] = ADC5_CHAN_TEMP(0, + SCALE_HW_CALIB_THERM_100K_PU_PM7) + [ADC5_GEN3_AMUX5_THM_100K_PU] = ADC5_CHAN_TEMP(0, + SCALE_HW_CALIB_THERM_100K_PU_PM7) + [ADC5_GEN3_AMUX6_THM_100K_PU] = ADC5_CHAN_TEMP(0, + SCALE_HW_CALIB_THERM_100K_PU_PM7) + [ADC5_GEN3_AMUX1_GPIO_100K_PU] = ADC5_CHAN_TEMP(0, + SCALE_HW_CALIB_THERM_100K_PU_PM7) + [ADC5_GEN3_AMUX2_GPIO_100K_PU] = ADC5_CHAN_TEMP(0, + SCALE_HW_CALIB_THERM_100K_PU_PM7) + [ADC5_GEN3_AMUX3_GPIO_100K_PU] = ADC5_CHAN_TEMP(0, + SCALE_HW_CALIB_THERM_100K_PU_PM7) + [ADC5_GEN3_AMUX4_GPIO_100K_PU] = ADC5_CHAN_TEMP(0, + SCALE_HW_CALIB_THERM_100K_PU_PM7) +}; + +static int adc5_gen3_get_fw_channel_data(struct adc5_chip *adc, + struct adc5_channel_prop *prop, + struct fwnode_handle *fwnode) +{ + const char *name = fwnode_get_name(fwnode); + const struct adc5_data *data = adc->data; + struct device *dev = adc->dev; + const char *channel_name; + u32 chan, value, sid; + u32 varr[2]; + int ret; + + ret = fwnode_property_read_u32(fwnode, "reg", &chan); + if (ret < 0) + return dev_err_probe(dev, ret, "invalid channel number %s\n", + name); + + /* + * Value read from "reg" is virtual channel number + * virtual channel number = sid << 8 | channel number + */ + sid = FIELD_GET(ADC5_GEN3_VIRTUAL_SID_MASK, chan); + chan = FIELD_GET(ADC5_GEN3_CHANNEL_MASK, chan); + + if (chan >= ADC5_MAX_CHANNEL) + return dev_err_probe(dev, -EINVAL, + "%s invalid channel number %d\n", + name, chan); + + prop->common_props.channel = chan; + prop->common_props.sid = sid; + + if (!adc->data->adc_chans[chan].info_mask) + return dev_err_probe(dev, -EINVAL, "Channel %#x not supported\n", chan); + + channel_name = name; + fwnode_property_read_string(fwnode, "label", &channel_name); + prop->common_props.label = channel_name; + + value = data->decimation[ADC5_DECIMATION_DEFAULT]; + fwnode_property_read_u32(fwnode, "qcom,decimation", &value); + ret = qcom_adc5_decimation_from_dt(value, data->decimation); + if (ret < 0) + return dev_err_probe(dev, ret, "%#x invalid decimation %d\n", + chan, value); + prop->common_props.decimation = ret; + + prop->common_props.prescale = adc->data->adc_chans[chan].prescale_index; + ret = fwnode_property_read_u32_array(fwnode, "qcom,pre-scaling", varr, 2); + if (!ret) { + ret = qcom_adc5_prescaling_from_dt(varr[0], varr[1]); + if (ret < 0) + return dev_err_probe(dev, ret, + "%#x invalid pre-scaling <%d %d>\n", + chan, varr[0], varr[1]); + prop->common_props.prescale = ret; + } + + value = data->hw_settle_1[VADC_DEF_HW_SETTLE_TIME]; + fwnode_property_read_u32(fwnode, "qcom,hw-settle-time", &value); + ret = qcom_adc5_hw_settle_time_from_dt(value, data->hw_settle_1); + if (ret < 0) + return dev_err_probe(dev, ret, + "%#x invalid hw-settle-time %d us\n", + chan, value); + prop->common_props.hw_settle_time_us = ret; + + value = BIT(VADC_DEF_AVG_SAMPLES); + fwnode_property_read_u32(fwnode, "qcom,avg-samples", &value); + ret = qcom_adc5_avg_samples_from_dt(value); + if (ret < 0) + return dev_err_probe(dev, ret, "%#x invalid avg-samples %d\n", + chan, value); + prop->common_props.avg_samples = ret; + + if (fwnode_property_read_bool(fwnode, "qcom,ratiometric")) + prop->common_props.cal_method = ADC5_RATIOMETRIC_CAL; + else + prop->common_props.cal_method = ADC5_ABSOLUTE_CAL; + + prop->adc_tm = fwnode_property_read_bool(fwnode, "qcom,adc-tm"); + if (prop->adc_tm) { + adc->n_tm_channels++; + if (adc->n_tm_channels > (adc->dev_data.num_sdams * 8 - 1)) + return dev_err_probe(dev, -EINVAL, + "Number of TM nodes %u greater than channels supported:%u\n", + adc->n_tm_channels, + adc->dev_data.num_sdams * 8 - 1); + } + + return 0; +} + +static const struct adc5_data adc5_gen3_data_pmic = { + .full_scale_code_volt = 0x70e4, + .adc_chans = adc5_gen3_chans_pmic, + .info = &adc5_gen3_info, + .decimation = (unsigned int [ADC5_DECIMATION_SAMPLES_MAX]) + { 85, 340, 1360 }, + .hw_settle_1 = (unsigned int [VADC_HW_SETTLE_SAMPLES_MAX]) + { 15, 100, 200, 300, + 400, 500, 600, 700, + 1000, 2000, 4000, 8000, + 16000, 32000, 64000, 128000 }, +}; + +static const struct of_device_id adc5_match_table[] = { + { + .compatible = "qcom,spmi-adc5-gen3", + .data = &adc5_gen3_data_pmic, + }, + { } +}; +MODULE_DEVICE_TABLE(of, adc5_match_table); + +static int adc5_get_fw_data(struct adc5_chip *adc) +{ + const struct adc5_channels *adc_chan; + struct adc5_channel_prop *chan_props; + struct iio_chan_spec *iio_chan; + struct device *dev = adc->dev; + unsigned int index = 0; + int ret; + + adc->nchannels = device_get_child_node_count(dev); + if (!adc->nchannels) + return dev_err_probe(dev, -EINVAL, "No ADC channels found\n"); + + adc->iio_chans = devm_kcalloc(dev, adc->nchannels, + sizeof(*adc->iio_chans), GFP_KERNEL); + if (!adc->iio_chans) + return -ENOMEM; + + adc->chan_props = devm_kcalloc(dev, adc->nchannels, + sizeof(*adc->chan_props), GFP_KERNEL); + if (!adc->chan_props) + return -ENOMEM; + + chan_props = adc->chan_props; + adc->n_tm_channels = 0; + iio_chan = adc->iio_chans; + adc->data = device_get_match_data(dev); + + device_for_each_child_node_scoped(dev, child) { + ret = adc5_gen3_get_fw_channel_data(adc, chan_props, child); + if (ret) + return ret; + + chan_props->chip = adc; + adc_chan = &adc->data->adc_chans[chan_props->common_props.channel]; + chan_props->common_props.scale_fn_type = adc_chan->scale_fn_type; + + iio_chan->channel = ADC5_GEN3_V_CHAN(chan_props->common_props); + iio_chan->info_mask_separate = adc_chan->info_mask; + iio_chan->type = adc_chan->type; + iio_chan->address = index; + iio_chan->indexed = 1; + iio_chan++; + chan_props++; + index++; + } + + return 0; +} + +static void adc5_gen3_uninit_aux(void *data) +{ + auxiliary_device_uninit(data); +} + +static void adc5_gen3_delete_aux(void *data) +{ + auxiliary_device_delete(data); +} + +static void adc5_gen3_aux_device_release(struct device *dev) {} + +static int adc5_gen3_add_aux_tm_device(struct adc5_chip *adc) +{ + struct tm5_aux_dev_wrapper *aux_device; + int i, ret, i_tm = 0; + + aux_device = devm_kzalloc(adc->dev, sizeof(*aux_device), GFP_KERNEL); + if (!aux_device) + return -ENOMEM; + + aux_device->aux_dev.name = "adc5_tm_gen3"; + aux_device->aux_dev.dev.parent = adc->dev; + aux_device->aux_dev.dev.release = adc5_gen3_aux_device_release; + + aux_device->tm_props = devm_kcalloc(adc->dev, adc->n_tm_channels, + sizeof(*aux_device->tm_props), + GFP_KERNEL); + if (!aux_device->tm_props) + return -ENOMEM; + + aux_device->dev_data = &adc->dev_data; + + for (i = 0; i < adc->nchannels; i++) { + if (!adc->chan_props[i].adc_tm) + continue; + aux_device->tm_props[i_tm] = adc->chan_props[i].common_props; + i_tm++; + } + + device_set_of_node_from_dev(&aux_device->aux_dev.dev, adc->dev); + + aux_device->n_tm_channels = adc->n_tm_channels; + + ret = auxiliary_device_init(&aux_device->aux_dev); + if (ret) + return ret; + + ret = devm_add_action_or_reset(adc->dev, adc5_gen3_uninit_aux, + &aux_device->aux_dev); + if (ret) + return ret; + + ret = auxiliary_device_add(&aux_device->aux_dev); + if (ret) + return ret; + ret = devm_add_action_or_reset(adc->dev, adc5_gen3_delete_aux, + &aux_device->aux_dev); + if (ret) + return ret; + + return 0; +} + +void adc5_gen3_mutex_lock(struct device *dev) + __acquires(&adc->lock) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev->parent); + struct adc5_chip *adc = iio_priv(indio_dev); + + mutex_lock(&adc->lock); +} +EXPORT_SYMBOL_NS_GPL(adc5_gen3_mutex_lock, "QCOM_SPMI_ADC5_GEN3"); + +void adc5_gen3_mutex_unlock(struct device *dev) + __releases(&adc->lock) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev->parent); + struct adc5_chip *adc = iio_priv(indio_dev); + + mutex_unlock(&adc->lock); +} +EXPORT_SYMBOL_NS_GPL(adc5_gen3_mutex_unlock, "QCOM_SPMI_ADC5_GEN3"); + +int adc5_gen3_get_scaled_reading(struct device *dev, + struct adc5_channel_common_prop *common_props, + int *val) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev->parent); + struct adc5_chip *adc = iio_priv(indio_dev); + u16 adc_code_volt; + int ret; + + ret = adc5_gen3_do_conversion(adc, common_props, &adc_code_volt); + if (ret) + return ret; + + return qcom_adc5_hw_scale(common_props->scale_fn_type, + common_props->prescale, + adc->data, adc_code_volt, val); +} +EXPORT_SYMBOL_NS_GPL(adc5_gen3_get_scaled_reading, "QCOM_SPMI_ADC5_GEN3"); + +int adc5_gen3_therm_code_to_temp(struct device *dev, + struct adc5_channel_common_prop *common_props, + u16 code, int *val) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev->parent); + struct adc5_chip *adc = iio_priv(indio_dev); + + return qcom_adc5_hw_scale(common_props->scale_fn_type, + common_props->prescale, + adc->data, code, val); +} +EXPORT_SYMBOL_NS_GPL(adc5_gen3_therm_code_to_temp, "QCOM_SPMI_ADC5_GEN3"); + +static int adc5_gen3_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct iio_dev *indio_dev; + struct adc5_chip *adc; + struct regmap *regmap; + int ret, i; + u32 *reg; + + regmap = dev_get_regmap(dev->parent, NULL); + if (!regmap) + return -ENODEV; + + indio_dev = devm_iio_device_alloc(dev, sizeof(*adc)); + if (!indio_dev) + return -ENOMEM; + + adc = iio_priv(indio_dev); + adc->dev_data.regmap = regmap; + adc->dev = dev; + + ret = device_property_count_u32(dev, "reg"); + if (ret < 0) + return ret; + + adc->dev_data.num_sdams = ret; + + reg = devm_kcalloc(dev, adc->dev_data.num_sdams, sizeof(u32), + GFP_KERNEL); + if (!reg) + return -ENOMEM; + + ret = device_property_read_u32_array(dev, "reg", reg, + adc->dev_data.num_sdams); + if (ret) + return dev_err_probe(dev, ret, + "Failed to read reg property\n"); + + adc->dev_data.base = devm_kcalloc(dev, adc->dev_data.num_sdams, + sizeof(*adc->dev_data.base), + GFP_KERNEL); + if (!adc->dev_data.base) + return -ENOMEM; + + platform_set_drvdata(pdev, indio_dev); + init_completion(&adc->complete); + ret = devm_mutex_init(dev, &adc->lock); + if (ret) + return ret; + + for (i = 0; i < adc->dev_data.num_sdams; i++) { + adc->dev_data.base[i].base_addr = reg[i]; + + ret = platform_get_irq(pdev, i); + if (ret < 0) + return dev_err_probe(dev, ret, + "Getting IRQ %d failed\n", i); + + adc->dev_data.base[i].irq = ret; + + adc->dev_data.base[i].irq_name = devm_kasprintf(dev, GFP_KERNEL, + "sdam%d", i); + if (!adc->dev_data.base[i].irq_name) + return -ENOMEM; + } + + /* + * This interrupt is shared with the ADC_TM auxiliary driver, which + * is threaded and uses IRQF_ONESHOT. Since shared interrupts need + * to agree on IRQF_ONESHOT configuration and there is a kernel + * warning for using IRQF_ONESHOT with non-threaded interrupts, + * make this also a threaded IRQ. + */ + ret = devm_request_threaded_irq(dev, adc->dev_data.base[ADC5_GEN3_VADC_SDAM].irq, + NULL, adc5_gen3_isr, IRQF_ONESHOT | IRQF_SHARED, + adc->dev_data.base[ADC5_GEN3_VADC_SDAM].irq_name, + adc); + if (ret) + return ret; + + ret = adc5_get_fw_data(adc); + if (ret) + return ret; + + if (adc->n_tm_channels > 0) { + ret = adc5_gen3_add_aux_tm_device(adc); + if (ret) + dev_err_probe(dev, ret, + "Failed to add auxiliary TM device\n"); + } + + indio_dev->name = "spmi-adc5-gen3"; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->info = &adc5_gen3_info; + indio_dev->channels = adc->iio_chans; + indio_dev->num_channels = adc->nchannels; + + return devm_iio_device_register(dev, indio_dev); +} + +static struct platform_driver adc5_gen3_driver = { + .driver = { + .name = "qcom-spmi-adc5-gen3", + .of_match_table = adc5_match_table, + }, + .probe = adc5_gen3_probe, +}; +module_platform_driver(adc5_gen3_driver); + +MODULE_DESCRIPTION("Qualcomm Technologies Inc. PMIC5 Gen3 ADC driver"); +MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS("QCOM_SPMI_ADC5_GEN3"); diff --git a/drivers/iio/adc/qcom-spmi-adc5.c b/drivers/iio/adc/qcom-spmi-adc5.c new file mode 100644 index 000000000..83ecd3adf --- /dev/null +++ b/drivers/iio/adc/qcom-spmi-adc5.c @@ -0,0 +1,939 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2018, 2020, The Linux Foundation. All rights reserved. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define ADC5_USR_REVISION1 0x0 +#define ADC5_USR_STATUS1 0x8 +#define ADC5_USR_STATUS1_CONV_FAULT BIT(7) +#define ADC5_USR_STATUS1_REQ_STS BIT(1) +#define ADC5_USR_STATUS1_EOC BIT(0) +#define ADC5_USR_STATUS1_REQ_STS_EOC_MASK 0x3 + +#define ADC5_USR_STATUS2 0x9 +#define ADC5_USR_STATUS2_CONV_SEQ_MASK 0x70 +#define ADC5_USR_STATUS2_CONV_SEQ_MASK_SHIFT 0x5 + +#define ADC5_USR_IBAT_MEAS 0xf +#define ADC5_USR_IBAT_MEAS_SUPPORTED BIT(0) + +#define ADC5_USR_DIG_PARAM 0x42 +#define ADC5_USR_DIG_PARAM_CAL_VAL BIT(6) +#define ADC5_USR_DIG_PARAM_CAL_VAL_SHIFT 6 +#define ADC5_USR_DIG_PARAM_CAL_SEL 0x30 +#define ADC5_USR_DIG_PARAM_CAL_SEL_SHIFT 4 +#define ADC5_USR_DIG_PARAM_DEC_RATIO_SEL 0xc +#define ADC5_USR_DIG_PARAM_DEC_RATIO_SEL_SHIFT 2 + +#define ADC5_USR_FAST_AVG_CTL 0x43 +#define ADC5_USR_FAST_AVG_CTL_EN BIT(7) +#define ADC5_USR_FAST_AVG_CTL_SAMPLES_MASK 0x7 + +#define ADC5_USR_CH_SEL_CTL 0x44 + +#define ADC5_USR_DELAY_CTL 0x45 +#define ADC5_USR_HW_SETTLE_DELAY_MASK 0xf + +#define ADC5_USR_EN_CTL1 0x46 +#define ADC5_USR_EN_CTL1_ADC_EN BIT(7) + +#define ADC5_USR_CONV_REQ 0x47 +#define ADC5_USR_CONV_REQ_REQ BIT(7) + +#define ADC5_USR_DATA0 0x50 + +#define ADC5_USR_DATA1 0x51 + +#define ADC5_USR_IBAT_DATA0 0x52 + +#define ADC5_USR_IBAT_DATA1 0x53 + +#define ADC_CHANNEL_OFFSET 0x8 +#define ADC_CHANNEL_MASK GENMASK(7, 0) + +/* + * Conversion time varies based on the decimation, clock rate, fast average + * samples and measurements queued across different VADC peripherals. + * Set the timeout to a max of 100ms. + */ +#define ADC5_CONV_TIME_MIN_US 263 +#define ADC5_CONV_TIME_MAX_US 264 +#define ADC5_CONV_TIME_RETRY 400 +#define ADC5_CONV_TIMEOUT msecs_to_jiffies(100) + +/* Digital version >= 5.3 supports hw_settle_2 */ +#define ADC5_HW_SETTLE_DIFF_MINOR 3 +#define ADC5_HW_SETTLE_DIFF_MAJOR 5 + +/* For PMIC7 */ +#define ADC_APP_SID 0x40 +#define ADC_APP_SID_MASK GENMASK(3, 0) +#define ADC7_CONV_TIMEOUT msecs_to_jiffies(10) + +enum adc5_cal_method { + ADC5_NO_CAL = 0, + ADC5_RATIOMETRIC_CAL, + ADC5_ABSOLUTE_CAL +}; + +enum adc5_cal_val { + ADC5_TIMER_CAL = 0, + ADC5_NEW_CAL +}; + +/** + * struct adc5_channel_prop - ADC channel property. + * @channel: channel number, refer to the channel list. + * @cal_method: calibration method. + * @cal_val: calibration value + * @decimation: sampling rate supported for the channel. + * @sid: slave id of PMIC owning the channel, for PMIC7. + * @prescale: channel scaling performed on the input signal. + * @hw_settle_time: the time between AMUX being configured and the + * start of conversion. + * @avg_samples: ability to provide single result from the ADC + * that is an average of multiple measurements. + * @scale_fn_type: Represents the scaling function to convert voltage + * physical units desired by the client for the channel. + * @channel_name: Channel name used in device tree. + */ +struct adc5_channel_prop { + unsigned int channel; + enum adc5_cal_method cal_method; + enum adc5_cal_val cal_val; + unsigned int decimation; + unsigned int sid; + unsigned int prescale; + unsigned int hw_settle_time; + unsigned int avg_samples; + enum vadc_scale_fn_type scale_fn_type; + const char *channel_name; +}; + +/** + * struct adc5_chip - ADC private structure. + * @regmap: SPMI ADC5 peripheral register map field. + * @dev: SPMI ADC5 device. + * @base: base address for the ADC peripheral. + * @nchannels: number of ADC channels. + * @chan_props: array of ADC channel properties. + * @iio_chans: array of IIO channels specification. + * @poll_eoc: use polling instead of interrupt. + * @complete: ADC result notification after interrupt is received. + * @lock: ADC lock for access to the peripheral. + * @data: software configuration data. + */ +struct adc5_chip { + struct regmap *regmap; + struct device *dev; + u16 base; + unsigned int nchannels; + struct adc5_channel_prop *chan_props; + struct iio_chan_spec *iio_chans; + bool poll_eoc; + struct completion complete; + struct mutex lock; + const struct adc5_data *data; +}; + +static int adc5_read(struct adc5_chip *adc, u16 offset, u8 *data, int len) +{ + return regmap_bulk_read(adc->regmap, adc->base + offset, data, len); +} + +static int adc5_write(struct adc5_chip *adc, u16 offset, u8 *data, int len) +{ + return regmap_bulk_write(adc->regmap, adc->base + offset, data, len); +} + +static int adc5_masked_write(struct adc5_chip *adc, u16 offset, u8 mask, u8 val) +{ + return regmap_update_bits(adc->regmap, adc->base + offset, mask, val); +} + +static int adc5_read_voltage_data(struct adc5_chip *adc, u16 *data) +{ + int ret; + u8 rslt_lsb, rslt_msb; + + ret = adc5_read(adc, ADC5_USR_DATA0, &rslt_lsb, sizeof(rslt_lsb)); + if (ret) + return ret; + + ret = adc5_read(adc, ADC5_USR_DATA1, &rslt_msb, sizeof(rslt_lsb)); + if (ret) + return ret; + + *data = (rslt_msb << 8) | rslt_lsb; + + if (*data == ADC5_USR_DATA_CHECK) { + dev_err(adc->dev, "Invalid data:0x%x\n", *data); + return -EINVAL; + } + + dev_dbg(adc->dev, "voltage raw code:0x%x\n", *data); + + return 0; +} + +static int adc5_poll_wait_eoc(struct adc5_chip *adc) +{ + unsigned int count, retry = ADC5_CONV_TIME_RETRY; + u8 status1; + int ret; + + for (count = 0; count < retry; count++) { + ret = adc5_read(adc, ADC5_USR_STATUS1, &status1, + sizeof(status1)); + if (ret) + return ret; + + status1 &= ADC5_USR_STATUS1_REQ_STS_EOC_MASK; + if (status1 == ADC5_USR_STATUS1_EOC) + return 0; + + usleep_range(ADC5_CONV_TIME_MIN_US, ADC5_CONV_TIME_MAX_US); + } + + return -ETIMEDOUT; +} + +static void adc5_update_dig_param(struct adc5_chip *adc, + struct adc5_channel_prop *prop, u8 *data) +{ + /* Update calibration value */ + *data &= ~ADC5_USR_DIG_PARAM_CAL_VAL; + *data |= (prop->cal_val << ADC5_USR_DIG_PARAM_CAL_VAL_SHIFT); + + /* Update calibration select */ + *data &= ~ADC5_USR_DIG_PARAM_CAL_SEL; + *data |= (prop->cal_method << ADC5_USR_DIG_PARAM_CAL_SEL_SHIFT); + + /* Update decimation ratio select */ + *data &= ~ADC5_USR_DIG_PARAM_DEC_RATIO_SEL; + *data |= (prop->decimation << ADC5_USR_DIG_PARAM_DEC_RATIO_SEL_SHIFT); +} + +static int adc5_configure(struct adc5_chip *adc, + struct adc5_channel_prop *prop) +{ + int ret; + u8 buf[6]; + + /* Read registers 0x42 through 0x46 */ + ret = adc5_read(adc, ADC5_USR_DIG_PARAM, buf, sizeof(buf)); + if (ret) + return ret; + + /* Digital param selection */ + adc5_update_dig_param(adc, prop, &buf[0]); + + /* Update fast average sample value */ + buf[1] &= (u8) ~ADC5_USR_FAST_AVG_CTL_SAMPLES_MASK; + buf[1] |= prop->avg_samples; + + /* Select ADC channel */ + buf[2] = prop->channel; + + /* Select HW settle delay for channel */ + buf[3] &= (u8) ~ADC5_USR_HW_SETTLE_DELAY_MASK; + buf[3] |= prop->hw_settle_time; + + /* Select ADC enable */ + buf[4] |= ADC5_USR_EN_CTL1_ADC_EN; + + /* Select CONV request */ + buf[5] |= ADC5_USR_CONV_REQ_REQ; + + if (!adc->poll_eoc) + reinit_completion(&adc->complete); + + return adc5_write(adc, ADC5_USR_DIG_PARAM, buf, sizeof(buf)); +} + +static int adc7_configure(struct adc5_chip *adc, + struct adc5_channel_prop *prop) +{ + int ret; + u8 conv_req = 0, buf[4]; + + ret = adc5_masked_write(adc, ADC_APP_SID, ADC_APP_SID_MASK, prop->sid); + if (ret) + return ret; + + ret = adc5_read(adc, ADC5_USR_DIG_PARAM, buf, sizeof(buf)); + if (ret) + return ret; + + /* Digital param selection */ + adc5_update_dig_param(adc, prop, &buf[0]); + + /* Update fast average sample value */ + buf[1] &= ~ADC5_USR_FAST_AVG_CTL_SAMPLES_MASK; + buf[1] |= prop->avg_samples; + + /* Select ADC channel */ + buf[2] = prop->channel; + + /* Select HW settle delay for channel */ + buf[3] &= ~ADC5_USR_HW_SETTLE_DELAY_MASK; + buf[3] |= prop->hw_settle_time; + + /* Select CONV request */ + conv_req = ADC5_USR_CONV_REQ_REQ; + + if (!adc->poll_eoc) + reinit_completion(&adc->complete); + + ret = adc5_write(adc, ADC5_USR_DIG_PARAM, buf, sizeof(buf)); + if (ret) + return ret; + + return adc5_write(adc, ADC5_USR_CONV_REQ, &conv_req, 1); +} + +static int adc5_do_conversion(struct adc5_chip *adc, + struct adc5_channel_prop *prop, + struct iio_chan_spec const *chan, + u16 *data_volt, u16 *data_cur) +{ + int ret; + + mutex_lock(&adc->lock); + + ret = adc5_configure(adc, prop); + if (ret) { + dev_err(adc->dev, "ADC configure failed with %d\n", ret); + goto unlock; + } + + if (adc->poll_eoc) { + ret = adc5_poll_wait_eoc(adc); + if (ret) { + dev_err(adc->dev, "EOC bit not set\n"); + goto unlock; + } + } else { + ret = wait_for_completion_timeout(&adc->complete, + ADC5_CONV_TIMEOUT); + if (!ret) { + dev_dbg(adc->dev, "Did not get completion timeout.\n"); + ret = adc5_poll_wait_eoc(adc); + if (ret) { + dev_err(adc->dev, "EOC bit not set\n"); + goto unlock; + } + } + } + + ret = adc5_read_voltage_data(adc, data_volt); +unlock: + mutex_unlock(&adc->lock); + + return ret; +} + +static int adc7_do_conversion(struct adc5_chip *adc, + struct adc5_channel_prop *prop, + struct iio_chan_spec const *chan, + u16 *data_volt, u16 *data_cur) +{ + int ret; + u8 status; + + mutex_lock(&adc->lock); + + ret = adc7_configure(adc, prop); + if (ret) { + dev_err(adc->dev, "ADC configure failed with %d\n", ret); + goto unlock; + } + + /* No support for polling mode at present */ + wait_for_completion_timeout(&adc->complete, ADC7_CONV_TIMEOUT); + + ret = adc5_read(adc, ADC5_USR_STATUS1, &status, 1); + if (ret) + goto unlock; + + if (status & ADC5_USR_STATUS1_CONV_FAULT) { + dev_err(adc->dev, "Unexpected conversion fault\n"); + ret = -EIO; + goto unlock; + } + + ret = adc5_read_voltage_data(adc, data_volt); + +unlock: + mutex_unlock(&adc->lock); + + return ret; +} + +typedef int (*adc_do_conversion)(struct adc5_chip *adc, + struct adc5_channel_prop *prop, + struct iio_chan_spec const *chan, + u16 *data_volt, u16 *data_cur); + +static irqreturn_t adc5_isr(int irq, void *dev_id) +{ + struct adc5_chip *adc = dev_id; + + complete(&adc->complete); + + return IRQ_HANDLED; +} + +static int adc5_fwnode_xlate(struct iio_dev *indio_dev, + const struct fwnode_reference_args *iiospec) +{ + struct adc5_chip *adc = iio_priv(indio_dev); + int i; + + for (i = 0; i < adc->nchannels; i++) + if (adc->chan_props[i].channel == iiospec->args[0]) + return i; + + return -EINVAL; +} + +static int adc7_fwnode_xlate(struct iio_dev *indio_dev, + const struct fwnode_reference_args *iiospec) +{ + struct adc5_chip *adc = iio_priv(indio_dev); + int i, v_channel; + + for (i = 0; i < adc->nchannels; i++) { + v_channel = (adc->chan_props[i].sid << ADC_CHANNEL_OFFSET) | + adc->chan_props[i].channel; + if (v_channel == iiospec->args[0]) + return i; + } + + return -EINVAL; +} + +static int adc_read_raw_common(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int *val, int *val2, + long mask, adc_do_conversion do_conv) +{ + struct adc5_chip *adc = iio_priv(indio_dev); + struct adc5_channel_prop *prop; + u16 adc_code_volt, adc_code_cur; + int ret; + + prop = &adc->chan_props[chan->address]; + + switch (mask) { + case IIO_CHAN_INFO_PROCESSED: + ret = do_conv(adc, prop, chan, + &adc_code_volt, &adc_code_cur); + if (ret) + return ret; + + ret = qcom_adc5_hw_scale(prop->scale_fn_type, + prop->prescale, + adc->data, + adc_code_volt, val); + if (ret) + return ret; + + return IIO_VAL_INT; + default: + return -EINVAL; + } +} + +static int adc5_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int *val, int *val2, + long mask) +{ + return adc_read_raw_common(indio_dev, chan, val, val2, + mask, adc5_do_conversion); +} + +static int adc7_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int *val, int *val2, + long mask) +{ + return adc_read_raw_common(indio_dev, chan, val, val2, + mask, adc7_do_conversion); +} + +static const struct iio_info adc5_info = { + .read_raw = adc5_read_raw, + .fwnode_xlate = adc5_fwnode_xlate, +}; + +static const struct iio_info adc7_info = { + .read_raw = adc7_read_raw, + .fwnode_xlate = adc7_fwnode_xlate, +}; + +struct adc5_channels { + const char *datasheet_name; + unsigned int prescale_index; + enum iio_chan_type type; + long info_mask; + enum vadc_scale_fn_type scale_fn_type; +}; + +/* In these definitions, _pre refers to an index into adc5_prescale_ratios. */ +#define ADC5_CHAN(_dname, _type, _mask, _pre, _scale) \ + { \ + .datasheet_name = _dname, \ + .prescale_index = _pre, \ + .type = _type, \ + .info_mask = _mask, \ + .scale_fn_type = _scale, \ + }, \ + +#define ADC5_CHAN_TEMP(_dname, _pre, _scale) \ + ADC5_CHAN(_dname, IIO_TEMP, \ + BIT(IIO_CHAN_INFO_PROCESSED), \ + _pre, _scale) \ + +#define ADC5_CHAN_VOLT(_dname, _pre, _scale) \ + ADC5_CHAN(_dname, IIO_VOLTAGE, \ + BIT(IIO_CHAN_INFO_PROCESSED), \ + _pre, _scale) \ + +static const struct adc5_channels adc5_chans_pmic[ADC5_MAX_CHANNEL] = { + [ADC5_REF_GND] = ADC5_CHAN_VOLT("ref_gnd", 0, + SCALE_HW_CALIB_DEFAULT) + [ADC5_1P25VREF] = ADC5_CHAN_VOLT("vref_1p25", 0, + SCALE_HW_CALIB_DEFAULT) + [ADC5_VPH_PWR] = ADC5_CHAN_VOLT("vph_pwr", 1, + SCALE_HW_CALIB_DEFAULT) + [ADC5_VBAT_SNS] = ADC5_CHAN_VOLT("vbat_sns", 1, + SCALE_HW_CALIB_DEFAULT) + [ADC5_VCOIN] = ADC5_CHAN_VOLT("vcoin", 1, + SCALE_HW_CALIB_DEFAULT) + [ADC5_DIE_TEMP] = ADC5_CHAN_TEMP("die_temp", 0, + SCALE_HW_CALIB_PMIC_THERM) + [ADC5_USB_IN_I] = ADC5_CHAN_VOLT("usb_in_i_uv", 0, + SCALE_HW_CALIB_DEFAULT) + [ADC5_USB_IN_V_16] = ADC5_CHAN_VOLT("usb_in_v_div_16", 8, + SCALE_HW_CALIB_DEFAULT) + [ADC5_CHG_TEMP] = ADC5_CHAN_TEMP("chg_temp", 0, + SCALE_HW_CALIB_PM5_CHG_TEMP) + /* Charger prescales SBUx and MID_CHG to fit within 1.8V upper unit */ + [ADC5_SBUx] = ADC5_CHAN_VOLT("chg_sbux", 1, + SCALE_HW_CALIB_DEFAULT) + [ADC5_MID_CHG_DIV6] = ADC5_CHAN_VOLT("chg_mid_chg", 3, + SCALE_HW_CALIB_DEFAULT) + [ADC5_XO_THERM_100K_PU] = ADC5_CHAN_TEMP("xo_therm", 0, + SCALE_HW_CALIB_XOTHERM) + [ADC5_BAT_ID_100K_PU] = ADC5_CHAN_TEMP("bat_id", 0, + SCALE_HW_CALIB_DEFAULT) + [ADC5_AMUX_THM1_100K_PU] = ADC5_CHAN_TEMP("amux_thm1_100k_pu", 0, + SCALE_HW_CALIB_THERM_100K_PULLUP) + [ADC5_AMUX_THM2_100K_PU] = ADC5_CHAN_TEMP("amux_thm2_100k_pu", 0, + SCALE_HW_CALIB_THERM_100K_PULLUP) + [ADC5_AMUX_THM3_100K_PU] = ADC5_CHAN_TEMP("amux_thm3_100k_pu", 0, + SCALE_HW_CALIB_THERM_100K_PULLUP) + [ADC5_AMUX_THM2] = ADC5_CHAN_TEMP("amux_thm2", 0, + SCALE_HW_CALIB_PM5_SMB_TEMP) + [ADC5_GPIO1_100K_PU] = ADC5_CHAN_TEMP("gpio1_100k_pu", 0, + SCALE_HW_CALIB_THERM_100K_PULLUP) + [ADC5_GPIO2_100K_PU] = ADC5_CHAN_TEMP("gpio2_100k_pu", 0, + SCALE_HW_CALIB_THERM_100K_PULLUP) + [ADC5_GPIO3_100K_PU] = ADC5_CHAN_TEMP("gpio3_100k_pu", 0, + SCALE_HW_CALIB_THERM_100K_PULLUP) + [ADC5_GPIO4_100K_PU] = ADC5_CHAN_TEMP("gpio4_100k_pu", 0, + SCALE_HW_CALIB_THERM_100K_PULLUP) +}; + +static const struct adc5_channels adc7_chans_pmic[ADC5_MAX_CHANNEL] = { + [ADC7_REF_GND] = ADC5_CHAN_VOLT("ref_gnd", 0, + SCALE_HW_CALIB_DEFAULT) + [ADC7_1P25VREF] = ADC5_CHAN_VOLT("vref_1p25", 0, + SCALE_HW_CALIB_DEFAULT) + [ADC7_VPH_PWR] = ADC5_CHAN_VOLT("vph_pwr", 1, + SCALE_HW_CALIB_DEFAULT) + [ADC7_VBAT_SNS] = ADC5_CHAN_VOLT("vbat_sns", 3, + SCALE_HW_CALIB_DEFAULT) + [ADC7_DIE_TEMP] = ADC5_CHAN_TEMP("die_temp", 0, + SCALE_HW_CALIB_PMIC_THERM_PM7) + [ADC7_AMUX_THM1_100K_PU] = ADC5_CHAN_TEMP("amux_thm1_pu2", 0, + SCALE_HW_CALIB_THERM_100K_PU_PM7) + [ADC7_AMUX_THM2_100K_PU] = ADC5_CHAN_TEMP("amux_thm2_pu2", 0, + SCALE_HW_CALIB_THERM_100K_PU_PM7) + [ADC7_AMUX_THM3_100K_PU] = ADC5_CHAN_TEMP("amux_thm3_pu2", 0, + SCALE_HW_CALIB_THERM_100K_PU_PM7) + [ADC7_AMUX_THM4_100K_PU] = ADC5_CHAN_TEMP("amux_thm4_pu2", 0, + SCALE_HW_CALIB_THERM_100K_PU_PM7) + [ADC7_AMUX_THM5_100K_PU] = ADC5_CHAN_TEMP("amux_thm5_pu2", 0, + SCALE_HW_CALIB_THERM_100K_PU_PM7) + [ADC7_AMUX_THM6_100K_PU] = ADC5_CHAN_TEMP("amux_thm6_pu2", 0, + SCALE_HW_CALIB_THERM_100K_PU_PM7) + [ADC7_GPIO1_100K_PU] = ADC5_CHAN_TEMP("gpio1_pu2", 0, + SCALE_HW_CALIB_THERM_100K_PU_PM7) + [ADC7_GPIO2_100K_PU] = ADC5_CHAN_TEMP("gpio2_pu2", 0, + SCALE_HW_CALIB_THERM_100K_PU_PM7) + [ADC7_GPIO3_100K_PU] = ADC5_CHAN_TEMP("gpio3_pu2", 0, + SCALE_HW_CALIB_THERM_100K_PU_PM7) + [ADC7_GPIO4_100K_PU] = ADC5_CHAN_TEMP("gpio4_pu2", 0, + SCALE_HW_CALIB_THERM_100K_PU_PM7) +}; + +static const struct adc5_channels adc5_chans_rev2[ADC5_MAX_CHANNEL] = { + [ADC5_REF_GND] = ADC5_CHAN_VOLT("ref_gnd", 0, + SCALE_HW_CALIB_DEFAULT) + [ADC5_1P25VREF] = ADC5_CHAN_VOLT("vref_1p25", 0, + SCALE_HW_CALIB_DEFAULT) + [ADC5_VREF_VADC] = ADC5_CHAN_VOLT("vref_vadc", 0, + SCALE_HW_CALIB_DEFAULT) + [ADC5_VPH_PWR] = ADC5_CHAN_VOLT("vph_pwr", 1, + SCALE_HW_CALIB_DEFAULT) + [ADC5_VBAT_SNS] = ADC5_CHAN_VOLT("vbat_sns", 1, + SCALE_HW_CALIB_DEFAULT) + [ADC5_VCOIN] = ADC5_CHAN_VOLT("vcoin", 1, + SCALE_HW_CALIB_DEFAULT) + [ADC5_DIE_TEMP] = ADC5_CHAN_TEMP("die_temp", 0, + SCALE_HW_CALIB_PMIC_THERM) + [ADC5_AMUX_THM1_100K_PU] = ADC5_CHAN_TEMP("amux_thm1_100k_pu", 0, + SCALE_HW_CALIB_THERM_100K_PULLUP) + [ADC5_AMUX_THM2_100K_PU] = ADC5_CHAN_TEMP("amux_thm2_100k_pu", 0, + SCALE_HW_CALIB_THERM_100K_PULLUP) + [ADC5_AMUX_THM3_100K_PU] = ADC5_CHAN_TEMP("amux_thm3_100k_pu", 0, + SCALE_HW_CALIB_THERM_100K_PULLUP) + [ADC5_AMUX_THM4_100K_PU] = ADC5_CHAN_TEMP("amux_thm4_100k_pu", 0, + SCALE_HW_CALIB_THERM_100K_PULLUP) + [ADC5_AMUX_THM5_100K_PU] = ADC5_CHAN_TEMP("amux_thm5_100k_pu", 0, + SCALE_HW_CALIB_THERM_100K_PULLUP) + [ADC5_XO_THERM_100K_PU] = ADC5_CHAN_TEMP("xo_therm_100k_pu", 0, + SCALE_HW_CALIB_THERM_100K_PULLUP) +}; + +static int adc5_get_fw_channel_data(struct adc5_chip *adc, + struct adc5_channel_prop *prop, + struct fwnode_handle *fwnode, + const struct adc5_data *data) +{ + const char *channel_name; + char *name; + u32 chan, value, varr[2]; + u32 sid = 0; + int ret; + struct device *dev = adc->dev; + + name = devm_kasprintf(dev, GFP_KERNEL, "%pfwP", fwnode); + if (!name) + return -ENOMEM; + + /* Cut the address part */ + name[strchrnul(name, '@') - name] = '\0'; + + ret = fwnode_property_read_u32(fwnode, "reg", &chan); + if (ret) { + dev_err(dev, "invalid channel number %s\n", name); + return ret; + } + + /* Value read from "reg" is virtual channel number */ + + /* virtual channel number = sid << 8 | channel number */ + + if (adc->data->info == &adc7_info) { + sid = chan >> ADC_CHANNEL_OFFSET; + chan = chan & ADC_CHANNEL_MASK; + } + + if (chan > ADC5_PARALLEL_ISENSE_VBAT_IDATA) { + dev_err(dev, "%s invalid channel number %d\n", name, chan); + return -EINVAL; + } + + /* the channel has DT description */ + prop->channel = chan; + prop->sid = sid; + + ret = fwnode_property_read_string(fwnode, "label", &channel_name); + if (ret) + channel_name = data->adc_chans[chan].datasheet_name; + + prop->channel_name = channel_name; + + ret = fwnode_property_read_u32(fwnode, "qcom,decimation", &value); + if (!ret) { + ret = qcom_adc5_decimation_from_dt(value, data->decimation); + if (ret < 0) { + dev_err(dev, "%02x invalid decimation %d\n", + chan, value); + return ret; + } + prop->decimation = ret; + } else { + prop->decimation = ADC5_DECIMATION_DEFAULT; + } + + ret = fwnode_property_read_u32_array(fwnode, "qcom,pre-scaling", varr, 2); + if (!ret) { + ret = qcom_adc5_prescaling_from_dt(varr[0], varr[1]); + if (ret < 0) { + dev_err(dev, "%02x invalid pre-scaling <%d %d>\n", + chan, varr[0], varr[1]); + return ret; + } + prop->prescale = ret; + } else { + prop->prescale = + adc->data->adc_chans[prop->channel].prescale_index; + } + + ret = fwnode_property_read_u32(fwnode, "qcom,hw-settle-time", &value); + if (!ret) { + u8 dig_version[2]; + + ret = adc5_read(adc, ADC5_USR_REVISION1, dig_version, + sizeof(dig_version)); + if (ret) { + dev_err(dev, "Invalid dig version read %d\n", ret); + return ret; + } + + dev_dbg(dev, "dig_ver:minor:%d, major:%d\n", dig_version[0], + dig_version[1]); + /* Digital controller >= 5.3 have hw_settle_2 option */ + if ((dig_version[0] >= ADC5_HW_SETTLE_DIFF_MINOR && + dig_version[1] >= ADC5_HW_SETTLE_DIFF_MAJOR) || + adc->data->info == &adc7_info) + ret = qcom_adc5_hw_settle_time_from_dt(value, data->hw_settle_2); + else + ret = qcom_adc5_hw_settle_time_from_dt(value, data->hw_settle_1); + + if (ret < 0) { + dev_err(dev, "%02x invalid hw-settle-time %d us\n", + chan, value); + return ret; + } + prop->hw_settle_time = ret; + } else { + prop->hw_settle_time = VADC_DEF_HW_SETTLE_TIME; + } + + ret = fwnode_property_read_u32(fwnode, "qcom,avg-samples", &value); + if (!ret) { + ret = qcom_adc5_avg_samples_from_dt(value); + if (ret < 0) { + dev_err(dev, "%02x invalid avg-samples %d\n", + chan, value); + return ret; + } + prop->avg_samples = ret; + } else { + prop->avg_samples = VADC_DEF_AVG_SAMPLES; + } + + if (fwnode_property_read_bool(fwnode, "qcom,ratiometric")) + prop->cal_method = ADC5_RATIOMETRIC_CAL; + else + prop->cal_method = ADC5_ABSOLUTE_CAL; + + /* + * Default to using timer calibration. Using a fresh calibration value + * for every conversion will increase the overall time for a request. + */ + prop->cal_val = ADC5_TIMER_CAL; + + dev_dbg(dev, "%02x name %s\n", chan, name); + + return 0; +} + +static const struct adc5_data adc5_data_pmic = { + .full_scale_code_volt = 0x70e4, + .full_scale_code_cur = 0x2710, + .adc_chans = adc5_chans_pmic, + .info = &adc5_info, + .decimation = (unsigned int [ADC5_DECIMATION_SAMPLES_MAX]) + {250, 420, 840}, + .hw_settle_1 = (unsigned int [VADC_HW_SETTLE_SAMPLES_MAX]) + {15, 100, 200, 300, 400, 500, 600, 700, + 800, 900, 1, 2, 4, 6, 8, 10}, + .hw_settle_2 = (unsigned int [VADC_HW_SETTLE_SAMPLES_MAX]) + {15, 100, 200, 300, 400, 500, 600, 700, + 1, 2, 4, 8, 16, 32, 64, 128}, +}; + +static const struct adc5_data adc7_data_pmic = { + .full_scale_code_volt = 0x70e4, + .adc_chans = adc7_chans_pmic, + .info = &adc7_info, + .decimation = (unsigned int [ADC5_DECIMATION_SAMPLES_MAX]) + {85, 340, 1360}, + .hw_settle_2 = (unsigned int [VADC_HW_SETTLE_SAMPLES_MAX]) + {15, 100, 200, 300, 400, 500, 600, 700, + 1000, 2000, 4000, 8000, 16000, 32000, + 64000, 128000}, +}; + +static const struct adc5_data adc5_data_pmic_rev2 = { + .full_scale_code_volt = 0x4000, + .full_scale_code_cur = 0x1800, + .adc_chans = adc5_chans_rev2, + .info = &adc5_info, + .decimation = (unsigned int [ADC5_DECIMATION_SAMPLES_MAX]) + {256, 512, 1024}, + .hw_settle_1 = (unsigned int [VADC_HW_SETTLE_SAMPLES_MAX]) + {0, 100, 200, 300, 400, 500, 600, 700, + 800, 900, 1, 2, 4, 6, 8, 10}, + .hw_settle_2 = (unsigned int [VADC_HW_SETTLE_SAMPLES_MAX]) + {15, 100, 200, 300, 400, 500, 600, 700, + 1, 2, 4, 8, 16, 32, 64, 128}, +}; + +static const struct of_device_id adc5_match_table[] = { + { + .compatible = "qcom,spmi-adc5", + .data = &adc5_data_pmic, + }, + { + .compatible = "qcom,spmi-adc7", + .data = &adc7_data_pmic, + }, + { + .compatible = "qcom,spmi-adc-rev2", + .data = &adc5_data_pmic_rev2, + }, + { } +}; +MODULE_DEVICE_TABLE(of, adc5_match_table); + +static int adc5_get_fw_data(struct adc5_chip *adc) +{ + const struct adc5_channels *adc_chan; + struct iio_chan_spec *iio_chan; + struct adc5_channel_prop prop, *chan_props; + unsigned int index = 0; + int ret; + + adc->nchannels = device_get_child_node_count(adc->dev); + if (!adc->nchannels) + return dev_err_probe(adc->dev, -EINVAL, "no channels defined\n"); + + adc->iio_chans = devm_kcalloc(adc->dev, adc->nchannels, + sizeof(*adc->iio_chans), GFP_KERNEL); + if (!adc->iio_chans) + return -ENOMEM; + + adc->chan_props = devm_kcalloc(adc->dev, adc->nchannels, + sizeof(*adc->chan_props), GFP_KERNEL); + if (!adc->chan_props) + return -ENOMEM; + + chan_props = adc->chan_props; + iio_chan = adc->iio_chans; + adc->data = device_get_match_data(adc->dev); + if (!adc->data) + adc->data = &adc5_data_pmic; + + device_for_each_child_node_scoped(adc->dev, child) { + ret = adc5_get_fw_channel_data(adc, &prop, child, adc->data); + if (ret) + return ret; + + prop.scale_fn_type = + adc->data->adc_chans[prop.channel].scale_fn_type; + *chan_props = prop; + adc_chan = &adc->data->adc_chans[prop.channel]; + + iio_chan->channel = prop.channel; + iio_chan->datasheet_name = adc_chan->datasheet_name; + iio_chan->extend_name = prop.channel_name; + iio_chan->info_mask_separate = adc_chan->info_mask; + iio_chan->type = adc_chan->type; + iio_chan->address = index; + iio_chan++; + chan_props++; + index++; + } + + return 0; +} + +static int adc5_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct iio_dev *indio_dev; + struct adc5_chip *adc; + struct regmap *regmap; + int ret, irq_eoc; + u32 reg; + + regmap = dev_get_regmap(dev->parent, NULL); + if (!regmap) + return -ENODEV; + + ret = device_property_read_u32(dev, "reg", ®); + if (ret < 0) + return ret; + + indio_dev = devm_iio_device_alloc(dev, sizeof(*adc)); + if (!indio_dev) + return -ENOMEM; + + adc = iio_priv(indio_dev); + adc->regmap = regmap; + adc->dev = dev; + adc->base = reg; + + init_completion(&adc->complete); + mutex_init(&adc->lock); + + ret = adc5_get_fw_data(adc); + if (ret) + return ret; + + irq_eoc = platform_get_irq(pdev, 0); + if (irq_eoc < 0) { + if (irq_eoc == -EPROBE_DEFER || irq_eoc == -EINVAL) + return irq_eoc; + adc->poll_eoc = true; + } else { + ret = devm_request_irq(dev, irq_eoc, adc5_isr, 0, + "pm-adc5", adc); + if (ret) + return ret; + } + + indio_dev->name = pdev->name; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->info = adc->data->info; + indio_dev->channels = adc->iio_chans; + indio_dev->num_channels = adc->nchannels; + + return devm_iio_device_register(dev, indio_dev); +} + +static struct platform_driver adc5_driver = { + .driver = { + .name = "qcom-spmi-adc5", + .of_match_table = adc5_match_table, + }, + .probe = adc5_probe, +}; +module_platform_driver(adc5_driver); + +MODULE_ALIAS("platform:qcom-spmi-adc5"); +MODULE_DESCRIPTION("Qualcomm Technologies Inc. PMIC5 ADC driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/adc/qcom-spmi-iadc.c b/drivers/iio/adc/qcom-spmi-iadc.c new file mode 100644 index 000000000..0ec3a0c4b --- /dev/null +++ b/drivers/iio/adc/qcom-spmi-iadc.c @@ -0,0 +1,598 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2012-2014, The Linux Foundation. All rights reserved. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* IADC register and bit definition */ +#define IADC_REVISION2 0x1 +#define IADC_REVISION2_SUPPORTED_IADC 1 + +#define IADC_PERPH_TYPE 0x4 +#define IADC_PERPH_TYPE_ADC 8 + +#define IADC_PERPH_SUBTYPE 0x5 +#define IADC_PERPH_SUBTYPE_IADC 3 + +#define IADC_STATUS1 0x8 +#define IADC_STATUS1_OP_MODE 4 +#define IADC_STATUS1_REQ_STS BIT(1) +#define IADC_STATUS1_EOC BIT(0) +#define IADC_STATUS1_REQ_STS_EOC_MASK 0x3 + +#define IADC_MODE_CTL 0x40 +#define IADC_OP_MODE_SHIFT 3 +#define IADC_OP_MODE_NORMAL 0 +#define IADC_TRIM_EN BIT(0) + +#define IADC_EN_CTL1 0x46 +#define IADC_EN_CTL1_SET BIT(7) + +#define IADC_CH_SEL_CTL 0x48 + +#define IADC_DIG_PARAM 0x50 +#define IADC_DIG_DEC_RATIO_SEL_SHIFT 2 + +#define IADC_HW_SETTLE_DELAY 0x51 + +#define IADC_CONV_REQ 0x52 +#define IADC_CONV_REQ_SET BIT(7) + +#define IADC_FAST_AVG_CTL 0x5a +#define IADC_FAST_AVG_EN 0x5b +#define IADC_FAST_AVG_EN_SET BIT(7) + +#define IADC_PERH_RESET_CTL3 0xda +#define IADC_FOLLOW_WARM_RB BIT(2) + +#define IADC_DATA 0x60 /* 16 bits */ + +#define IADC_SEC_ACCESS 0xd0 +#define IADC_SEC_ACCESS_DATA 0xa5 + +#define IADC_NOMINAL_RSENSE 0xf4 +#define IADC_NOMINAL_RSENSE_SIGN_MASK BIT(7) + +#define IADC_REF_GAIN_MICRO_VOLTS 17857 + +#define IADC_INT_RSENSE_DEVIATION 15625 /* nano Ohms per bit */ + +#define IADC_INT_RSENSE_IDEAL_VALUE 10000 /* micro Ohms */ +#define IADC_INT_RSENSE_DEFAULT_VALUE 7800 /* micro Ohms */ +#define IADC_INT_RSENSE_DEFAULT_GF 9000 /* micro Ohms */ +#define IADC_INT_RSENSE_DEFAULT_SMIC 9700 /* micro Ohms */ + +#define IADC_CONV_TIME_MIN_US 2000 +#define IADC_CONV_TIME_MAX_US 2100 + +#define IADC_DEF_PRESCALING 0 /* 1:1 */ +#define IADC_DEF_DECIMATION 0 /* 512 */ +#define IADC_DEF_HW_SETTLE_TIME 0 /* 0 us */ +#define IADC_DEF_AVG_SAMPLES 0 /* 1 sample */ + +/* IADC channel list */ +#define IADC_INT_RSENSE 0 +#define IADC_EXT_RSENSE 1 +#define IADC_GAIN_17P857MV 3 +#define IADC_EXT_OFFSET_CSP_CSN 5 +#define IADC_INT_OFFSET_CSP2_CSN2 6 + +/** + * struct iadc_chip - IADC Current ADC device structure. + * @regmap: regmap for register read/write. + * @dev: This device pointer. + * @base: base offset for the ADC peripheral. + * @rsense: Values of the internal and external sense resister in micro Ohms. + * @poll_eoc: Poll for end of conversion instead of waiting for IRQ. + * @offset: Raw offset values for the internal and external channels. + * @gain: Raw gain of the channels. + * @lock: ADC lock for access to the peripheral. + * @complete: ADC notification after end of conversion interrupt is received. + */ +struct iadc_chip { + struct regmap *regmap; + struct device *dev; + u16 base; + bool poll_eoc; + u32 rsense[2]; + u16 offset[2]; + u16 gain; + struct mutex lock; + struct completion complete; +}; + +static int iadc_read(struct iadc_chip *iadc, u16 offset, u8 *data) +{ + unsigned int val; + int ret; + + ret = regmap_read(iadc->regmap, iadc->base + offset, &val); + if (ret < 0) + return ret; + + *data = val; + return 0; +} + +static int iadc_write(struct iadc_chip *iadc, u16 offset, u8 data) +{ + return regmap_write(iadc->regmap, iadc->base + offset, data); +} + +static int iadc_reset(struct iadc_chip *iadc) +{ + u8 data; + int ret; + + ret = iadc_write(iadc, IADC_SEC_ACCESS, IADC_SEC_ACCESS_DATA); + if (ret < 0) + return ret; + + ret = iadc_read(iadc, IADC_PERH_RESET_CTL3, &data); + if (ret < 0) + return ret; + + ret = iadc_write(iadc, IADC_SEC_ACCESS, IADC_SEC_ACCESS_DATA); + if (ret < 0) + return ret; + + data |= IADC_FOLLOW_WARM_RB; + + return iadc_write(iadc, IADC_PERH_RESET_CTL3, data); +} + +static int iadc_set_state(struct iadc_chip *iadc, bool state) +{ + return iadc_write(iadc, IADC_EN_CTL1, state ? IADC_EN_CTL1_SET : 0); +} + +static void iadc_status_show(struct iadc_chip *iadc) +{ + u8 mode, sta1, chan, dig, en, req; + int ret; + + ret = iadc_read(iadc, IADC_MODE_CTL, &mode); + if (ret < 0) + return; + + ret = iadc_read(iadc, IADC_DIG_PARAM, &dig); + if (ret < 0) + return; + + ret = iadc_read(iadc, IADC_CH_SEL_CTL, &chan); + if (ret < 0) + return; + + ret = iadc_read(iadc, IADC_CONV_REQ, &req); + if (ret < 0) + return; + + ret = iadc_read(iadc, IADC_STATUS1, &sta1); + if (ret < 0) + return; + + ret = iadc_read(iadc, IADC_EN_CTL1, &en); + if (ret < 0) + return; + + dev_err(iadc->dev, + "mode:%02x en:%02x chan:%02x dig:%02x req:%02x sta1:%02x\n", + mode, en, chan, dig, req, sta1); +} + +static int iadc_configure(struct iadc_chip *iadc, int channel) +{ + u8 decim, mode; + int ret; + + /* Mode selection */ + mode = (IADC_OP_MODE_NORMAL << IADC_OP_MODE_SHIFT) | IADC_TRIM_EN; + ret = iadc_write(iadc, IADC_MODE_CTL, mode); + if (ret < 0) + return ret; + + /* Channel selection */ + ret = iadc_write(iadc, IADC_CH_SEL_CTL, channel); + if (ret < 0) + return ret; + + /* Digital parameter setup */ + decim = IADC_DEF_DECIMATION << IADC_DIG_DEC_RATIO_SEL_SHIFT; + ret = iadc_write(iadc, IADC_DIG_PARAM, decim); + if (ret < 0) + return ret; + + /* HW settle time delay */ + ret = iadc_write(iadc, IADC_HW_SETTLE_DELAY, IADC_DEF_HW_SETTLE_TIME); + if (ret < 0) + return ret; + + ret = iadc_write(iadc, IADC_FAST_AVG_CTL, IADC_DEF_AVG_SAMPLES); + if (ret < 0) + return ret; + + if (IADC_DEF_AVG_SAMPLES) + ret = iadc_write(iadc, IADC_FAST_AVG_EN, IADC_FAST_AVG_EN_SET); + else + ret = iadc_write(iadc, IADC_FAST_AVG_EN, 0); + + if (ret < 0) + return ret; + + if (!iadc->poll_eoc) + reinit_completion(&iadc->complete); + + ret = iadc_set_state(iadc, true); + if (ret < 0) + return ret; + + /* Request conversion */ + return iadc_write(iadc, IADC_CONV_REQ, IADC_CONV_REQ_SET); +} + +static int iadc_poll_wait_eoc(struct iadc_chip *iadc, unsigned int interval_us) +{ + unsigned int count, retry; + int ret; + u8 sta1; + + retry = interval_us / IADC_CONV_TIME_MIN_US; + + for (count = 0; count < retry; count++) { + ret = iadc_read(iadc, IADC_STATUS1, &sta1); + if (ret < 0) + return ret; + + sta1 &= IADC_STATUS1_REQ_STS_EOC_MASK; + if (sta1 == IADC_STATUS1_EOC) + return 0; + + usleep_range(IADC_CONV_TIME_MIN_US, IADC_CONV_TIME_MAX_US); + } + + iadc_status_show(iadc); + + return -ETIMEDOUT; +} + +static int iadc_read_result(struct iadc_chip *iadc, u16 *data) +{ + return regmap_bulk_read(iadc->regmap, iadc->base + IADC_DATA, data, 2); +} + +static int iadc_do_conversion(struct iadc_chip *iadc, int chan, u16 *data) +{ + unsigned int wait; + int ret; + + ret = iadc_configure(iadc, chan); + if (ret < 0) + goto exit; + + wait = BIT(IADC_DEF_AVG_SAMPLES) * IADC_CONV_TIME_MIN_US * 2; + + if (iadc->poll_eoc) { + ret = iadc_poll_wait_eoc(iadc, wait); + } else { + ret = wait_for_completion_timeout(&iadc->complete, + usecs_to_jiffies(wait)); + if (!ret) + ret = -ETIMEDOUT; + else + /* double check conversion status */ + ret = iadc_poll_wait_eoc(iadc, IADC_CONV_TIME_MIN_US); + } + + if (!ret) + ret = iadc_read_result(iadc, data); +exit: + iadc_set_state(iadc, false); + if (ret < 0) + dev_err(iadc->dev, "conversion failed\n"); + + return ret; +} + +static int iadc_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct iadc_chip *iadc = iio_priv(indio_dev); + s32 isense_ua, vsense_uv; + u16 adc_raw, vsense_raw; + int ret; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + mutex_lock(&iadc->lock); + ret = iadc_do_conversion(iadc, chan->channel, &adc_raw); + mutex_unlock(&iadc->lock); + if (ret < 0) + return ret; + + vsense_raw = adc_raw - iadc->offset[chan->channel]; + + vsense_uv = vsense_raw * IADC_REF_GAIN_MICRO_VOLTS; + vsense_uv /= (s32)iadc->gain - iadc->offset[chan->channel]; + + isense_ua = vsense_uv / iadc->rsense[chan->channel]; + + dev_dbg(iadc->dev, "off %d gain %d adc %d %duV I %duA\n", + iadc->offset[chan->channel], iadc->gain, + adc_raw, vsense_uv, isense_ua); + + *val = isense_ua; + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + *val = 0; + *val2 = 1000; + return IIO_VAL_INT_PLUS_MICRO; + } + + return -EINVAL; +} + +static const struct iio_info iadc_info = { + .read_raw = iadc_read_raw, +}; + +static irqreturn_t iadc_isr(int irq, void *dev_id) +{ + struct iadc_chip *iadc = dev_id; + + complete(&iadc->complete); + + return IRQ_HANDLED; +} + +static int iadc_update_offset(struct iadc_chip *iadc) +{ + int ret; + + ret = iadc_do_conversion(iadc, IADC_GAIN_17P857MV, &iadc->gain); + if (ret < 0) + return ret; + + ret = iadc_do_conversion(iadc, IADC_INT_OFFSET_CSP2_CSN2, + &iadc->offset[IADC_INT_RSENSE]); + if (ret < 0) + return ret; + + if (iadc->gain == iadc->offset[IADC_INT_RSENSE]) { + dev_err(iadc->dev, "error: internal offset == gain %d\n", + iadc->gain); + return -EINVAL; + } + + ret = iadc_do_conversion(iadc, IADC_EXT_OFFSET_CSP_CSN, + &iadc->offset[IADC_EXT_RSENSE]); + if (ret < 0) + return ret; + + if (iadc->gain == iadc->offset[IADC_EXT_RSENSE]) { + dev_err(iadc->dev, "error: external offset == gain %d\n", + iadc->gain); + return -EINVAL; + } + + return 0; +} + +static int iadc_version_check(struct iadc_chip *iadc) +{ + u8 val; + int ret; + + ret = iadc_read(iadc, IADC_PERPH_TYPE, &val); + if (ret < 0) + return ret; + + if (val < IADC_PERPH_TYPE_ADC) { + dev_err(iadc->dev, "%d is not ADC\n", val); + return -EINVAL; + } + + ret = iadc_read(iadc, IADC_PERPH_SUBTYPE, &val); + if (ret < 0) + return ret; + + if (val < IADC_PERPH_SUBTYPE_IADC) { + dev_err(iadc->dev, "%d is not IADC\n", val); + return -EINVAL; + } + + ret = iadc_read(iadc, IADC_REVISION2, &val); + if (ret < 0) + return ret; + + if (val < IADC_REVISION2_SUPPORTED_IADC) { + dev_err(iadc->dev, "revision %d not supported\n", val); + return -EINVAL; + } + + return 0; +} + +static int iadc_rsense_read(struct iadc_chip *iadc, struct device_node *node) +{ + int ret, sign, int_sense; + u8 deviation; + + ret = of_property_read_u32(node, "qcom,external-resistor-micro-ohms", + &iadc->rsense[IADC_EXT_RSENSE]); + if (ret < 0) + iadc->rsense[IADC_EXT_RSENSE] = IADC_INT_RSENSE_IDEAL_VALUE; + + if (!iadc->rsense[IADC_EXT_RSENSE]) { + dev_err(iadc->dev, "external resistor can't be zero Ohms"); + return -EINVAL; + } + + ret = iadc_read(iadc, IADC_NOMINAL_RSENSE, &deviation); + if (ret < 0) + return ret; + + /* + * Deviation value stored is an offset from 10 mili Ohms, bit 7 is + * the sign, the remaining bits have an LSB of 15625 nano Ohms. + */ + sign = (deviation & IADC_NOMINAL_RSENSE_SIGN_MASK) ? -1 : 1; + + deviation &= ~IADC_NOMINAL_RSENSE_SIGN_MASK; + + /* Scale it to nono Ohms */ + int_sense = IADC_INT_RSENSE_IDEAL_VALUE * 1000; + int_sense += sign * deviation * IADC_INT_RSENSE_DEVIATION; + int_sense /= 1000; /* micro Ohms */ + + iadc->rsense[IADC_INT_RSENSE] = int_sense; + return 0; +} + +static const struct iio_chan_spec iadc_channels[] = { + { + .type = IIO_CURRENT, + .datasheet_name = "INTERNAL_RSENSE", + .channel = 0, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SCALE), + .indexed = 1, + }, + { + .type = IIO_CURRENT, + .datasheet_name = "EXTERNAL_RSENSE", + .channel = 1, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SCALE), + .indexed = 1, + }, +}; + +static void iadc_disable_irq_wake(void *data) +{ + disable_irq_wake((unsigned long)data); +} + +static int iadc_probe(struct platform_device *pdev) +{ + struct device_node *node = pdev->dev.of_node; + struct device *dev = &pdev->dev; + struct iio_dev *indio_dev; + struct iadc_chip *iadc; + int ret, irq_eoc; + u32 res; + + indio_dev = devm_iio_device_alloc(dev, sizeof(*iadc)); + if (!indio_dev) + return -ENOMEM; + + iadc = iio_priv(indio_dev); + iadc->dev = dev; + + iadc->regmap = dev_get_regmap(dev->parent, NULL); + if (!iadc->regmap) + return -ENODEV; + + init_completion(&iadc->complete); + mutex_init(&iadc->lock); + + ret = of_property_read_u32(node, "reg", &res); + if (ret < 0) + return -ENODEV; + + iadc->base = res; + + ret = iadc_version_check(iadc); + if (ret < 0) + return -ENODEV; + + ret = iadc_rsense_read(iadc, node); + if (ret < 0) + return -ENODEV; + + dev_dbg(iadc->dev, "sense resistors %d and %d micro Ohm\n", + iadc->rsense[IADC_INT_RSENSE], + iadc->rsense[IADC_EXT_RSENSE]); + + irq_eoc = platform_get_irq(pdev, 0); + if (irq_eoc == -EPROBE_DEFER) + return irq_eoc; + + if (irq_eoc < 0) + iadc->poll_eoc = true; + + ret = iadc_reset(iadc); + if (ret < 0) { + dev_err(dev, "reset failed\n"); + return ret; + } + + if (!iadc->poll_eoc) { + ret = devm_request_irq(dev, irq_eoc, iadc_isr, 0, + "spmi-iadc", iadc); + if (ret) + return ret; + + ret = enable_irq_wake(irq_eoc); + if (ret) + return ret; + + ret = devm_add_action_or_reset(dev, iadc_disable_irq_wake, + (void *)(unsigned long)irq_eoc); + if (ret) + return ret; + } else { + ret = devm_device_init_wakeup(iadc->dev); + if (ret) + return dev_err_probe(iadc->dev, ret, "Failed to init wakeup\n"); + } + + ret = iadc_update_offset(iadc); + if (ret < 0) { + dev_err(dev, "failed offset calibration\n"); + return ret; + } + + indio_dev->name = pdev->name; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->info = &iadc_info; + indio_dev->channels = iadc_channels; + indio_dev->num_channels = ARRAY_SIZE(iadc_channels); + + return devm_iio_device_register(dev, indio_dev); +} + +static const struct of_device_id iadc_match_table[] = { + { .compatible = "qcom,spmi-iadc" }, + { } +}; + +MODULE_DEVICE_TABLE(of, iadc_match_table); + +static struct platform_driver iadc_driver = { + .driver = { + .name = "qcom-spmi-iadc", + .of_match_table = iadc_match_table, + }, + .probe = iadc_probe, +}; + +module_platform_driver(iadc_driver); + +MODULE_ALIAS("platform:qcom-spmi-iadc"); +MODULE_DESCRIPTION("Qualcomm SPMI PMIC current ADC driver"); +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Ivan T. Ivanov "); diff --git a/drivers/iio/adc/qcom-spmi-rradc.c b/drivers/iio/adc/qcom-spmi-rradc.c new file mode 100644 index 000000000..1682c6faf --- /dev/null +++ b/drivers/iio/adc/qcom-spmi-rradc.c @@ -0,0 +1,1013 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2016-2017, 2019, The Linux Foundation. All rights reserved. + * Copyright (c) 2022 Linaro Limited. + * Author: Casey Connolly + * + * This driver is for the Round Robin ADC found in the pmi8998 and pm660 PMICs. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +#include + +#define DRIVER_NAME "qcom-spmi-rradc" + +#define RR_ADC_EN_CTL 0x46 +#define RR_ADC_SKIN_TEMP_LSB 0x50 +#define RR_ADC_SKIN_TEMP_MSB 0x51 +#define RR_ADC_CTL 0x52 +#define RR_ADC_CTL_CONTINUOUS_SEL BIT(3) +#define RR_ADC_LOG 0x53 +#define RR_ADC_LOG_CLR_CTRL BIT(0) + +#define RR_ADC_FAKE_BATT_LOW_LSB 0x58 +#define RR_ADC_FAKE_BATT_LOW_MSB 0x59 +#define RR_ADC_FAKE_BATT_HIGH_LSB 0x5A +#define RR_ADC_FAKE_BATT_HIGH_MSB 0x5B + +#define RR_ADC_BATT_ID_CTRL 0x60 +#define RR_ADC_BATT_ID_CTRL_CHANNEL_CONV BIT(0) +#define RR_ADC_BATT_ID_TRIGGER 0x61 +#define RR_ADC_BATT_ID_STS 0x62 +#define RR_ADC_BATT_ID_CFG 0x63 +#define BATT_ID_SETTLE_MASK GENMASK(7, 5) +#define RR_ADC_BATT_ID_5_LSB 0x66 +#define RR_ADC_BATT_ID_5_MSB 0x67 +#define RR_ADC_BATT_ID_15_LSB 0x68 +#define RR_ADC_BATT_ID_15_MSB 0x69 +#define RR_ADC_BATT_ID_150_LSB 0x6A +#define RR_ADC_BATT_ID_150_MSB 0x6B + +#define RR_ADC_BATT_THERM_CTRL 0x70 +#define RR_ADC_BATT_THERM_TRIGGER 0x71 +#define RR_ADC_BATT_THERM_STS 0x72 +#define RR_ADC_BATT_THERM_CFG 0x73 +#define RR_ADC_BATT_THERM_LSB 0x74 +#define RR_ADC_BATT_THERM_MSB 0x75 +#define RR_ADC_BATT_THERM_FREQ 0x76 + +#define RR_ADC_AUX_THERM_CTRL 0x80 +#define RR_ADC_AUX_THERM_TRIGGER 0x81 +#define RR_ADC_AUX_THERM_STS 0x82 +#define RR_ADC_AUX_THERM_CFG 0x83 +#define RR_ADC_AUX_THERM_LSB 0x84 +#define RR_ADC_AUX_THERM_MSB 0x85 + +#define RR_ADC_SKIN_HOT 0x86 +#define RR_ADC_SKIN_TOO_HOT 0x87 + +#define RR_ADC_AUX_THERM_C1 0x88 +#define RR_ADC_AUX_THERM_C2 0x89 +#define RR_ADC_AUX_THERM_C3 0x8A +#define RR_ADC_AUX_THERM_HALF_RANGE 0x8B + +#define RR_ADC_USB_IN_V_CTRL 0x90 +#define RR_ADC_USB_IN_V_TRIGGER 0x91 +#define RR_ADC_USB_IN_V_STS 0x92 +#define RR_ADC_USB_IN_V_LSB 0x94 +#define RR_ADC_USB_IN_V_MSB 0x95 +#define RR_ADC_USB_IN_I_CTRL 0x98 +#define RR_ADC_USB_IN_I_TRIGGER 0x99 +#define RR_ADC_USB_IN_I_STS 0x9A +#define RR_ADC_USB_IN_I_LSB 0x9C +#define RR_ADC_USB_IN_I_MSB 0x9D + +#define RR_ADC_DC_IN_V_CTRL 0xA0 +#define RR_ADC_DC_IN_V_TRIGGER 0xA1 +#define RR_ADC_DC_IN_V_STS 0xA2 +#define RR_ADC_DC_IN_V_LSB 0xA4 +#define RR_ADC_DC_IN_V_MSB 0xA5 +#define RR_ADC_DC_IN_I_CTRL 0xA8 +#define RR_ADC_DC_IN_I_TRIGGER 0xA9 +#define RR_ADC_DC_IN_I_STS 0xAA +#define RR_ADC_DC_IN_I_LSB 0xAC +#define RR_ADC_DC_IN_I_MSB 0xAD + +#define RR_ADC_PMI_DIE_TEMP_CTRL 0xB0 +#define RR_ADC_PMI_DIE_TEMP_TRIGGER 0xB1 +#define RR_ADC_PMI_DIE_TEMP_STS 0xB2 +#define RR_ADC_PMI_DIE_TEMP_CFG 0xB3 +#define RR_ADC_PMI_DIE_TEMP_LSB 0xB4 +#define RR_ADC_PMI_DIE_TEMP_MSB 0xB5 + +#define RR_ADC_CHARGER_TEMP_CTRL 0xB8 +#define RR_ADC_CHARGER_TEMP_TRIGGER 0xB9 +#define RR_ADC_CHARGER_TEMP_STS 0xBA +#define RR_ADC_CHARGER_TEMP_CFG 0xBB +#define RR_ADC_CHARGER_TEMP_LSB 0xBC +#define RR_ADC_CHARGER_TEMP_MSB 0xBD +#define RR_ADC_CHARGER_HOT 0xBE +#define RR_ADC_CHARGER_TOO_HOT 0xBF + +#define RR_ADC_GPIO_CTRL 0xC0 +#define RR_ADC_GPIO_TRIGGER 0xC1 +#define RR_ADC_GPIO_STS 0xC2 +#define RR_ADC_GPIO_LSB 0xC4 +#define RR_ADC_GPIO_MSB 0xC5 + +#define RR_ADC_ATEST_CTRL 0xC8 +#define RR_ADC_ATEST_TRIGGER 0xC9 +#define RR_ADC_ATEST_STS 0xCA +#define RR_ADC_ATEST_LSB 0xCC +#define RR_ADC_ATEST_MSB 0xCD +#define RR_ADC_SEC_ACCESS 0xD0 + +#define RR_ADC_PERPH_RESET_CTL2 0xD9 +#define RR_ADC_PERPH_RESET_CTL3 0xDA +#define RR_ADC_PERPH_RESET_CTL4 0xDB +#define RR_ADC_INT_TEST1 0xE0 +#define RR_ADC_INT_TEST_VAL 0xE1 + +#define RR_ADC_TM_TRIGGER_CTRLS 0xE2 +#define RR_ADC_TM_ADC_CTRLS 0xE3 +#define RR_ADC_TM_CNL_CTRL 0xE4 +#define RR_ADC_TM_BATT_ID_CTRL 0xE5 +#define RR_ADC_TM_THERM_CTRL 0xE6 +#define RR_ADC_TM_CONV_STS 0xE7 +#define RR_ADC_TM_ADC_READ_LSB 0xE8 +#define RR_ADC_TM_ADC_READ_MSB 0xE9 +#define RR_ADC_TM_ATEST_MUX_1 0xEA +#define RR_ADC_TM_ATEST_MUX_2 0xEB +#define RR_ADC_TM_REFERENCES 0xED +#define RR_ADC_TM_MISC_CTL 0xEE +#define RR_ADC_TM_RR_CTRL 0xEF + +#define RR_ADC_TRIGGER_EVERY_CYCLE BIT(7) +#define RR_ADC_TRIGGER_CTL BIT(0) + +#define RR_ADC_BATT_ID_RANGE 820 + +#define RR_ADC_BITS 10 +#define RR_ADC_CHAN_MSB (1 << RR_ADC_BITS) +#define RR_ADC_FS_VOLTAGE_MV 2500 + +/* BATT_THERM 0.25K/LSB */ +#define RR_ADC_BATT_THERM_LSB_K 4 + +#define RR_ADC_TEMP_FS_VOLTAGE_NUM 5000000 +#define RR_ADC_TEMP_FS_VOLTAGE_DEN 3 +#define RR_ADC_DIE_TEMP_OFFSET 601400 +#define RR_ADC_DIE_TEMP_SLOPE 2 +#define RR_ADC_DIE_TEMP_OFFSET_MILLI_DEGC 25000 + +#define RR_ADC_CHG_TEMP_GF_OFFSET_UV 1303168 +#define RR_ADC_CHG_TEMP_GF_SLOPE_UV_PER_C 3784 +#define RR_ADC_CHG_TEMP_SMIC_OFFSET_UV 1338433 +#define RR_ADC_CHG_TEMP_SMIC_SLOPE_UV_PER_C 3655 +#define RR_ADC_CHG_TEMP_660_GF_OFFSET_UV 1309001 +#define RR_ADC_CHG_TEMP_660_GF_SLOPE_UV_PER_C 3403 +#define RR_ADC_CHG_TEMP_660_SMIC_OFFSET_UV 1295898 +#define RR_ADC_CHG_TEMP_660_SMIC_SLOPE_UV_PER_C 3596 +#define RR_ADC_CHG_TEMP_660_MGNA_OFFSET_UV 1314779 +#define RR_ADC_CHG_TEMP_660_MGNA_SLOPE_UV_PER_C 3496 +#define RR_ADC_CHG_TEMP_OFFSET_MILLI_DEGC 25000 +#define RR_ADC_CHG_THRESHOLD_SCALE 4 + +#define RR_ADC_VOLT_INPUT_FACTOR 8 +#define RR_ADC_CURR_INPUT_FACTOR 2000 +#define RR_ADC_CURR_USBIN_INPUT_FACTOR_MIL 1886 +#define RR_ADC_CURR_USBIN_660_FACTOR_MIL 9 +#define RR_ADC_CURR_USBIN_660_UV_VAL 579500 + +#define RR_ADC_GPIO_FS_RANGE 5000 +#define RR_ADC_COHERENT_CHECK_RETRY 5 +#define RR_ADC_CHAN_MAX_CONTINUOUS_BUFFER_LEN 16 + +#define RR_ADC_STS_CHANNEL_READING_MASK GENMASK(1, 0) +#define RR_ADC_STS_CHANNEL_STS BIT(1) + +#define RR_ADC_TP_REV_VERSION1 21 +#define RR_ADC_TP_REV_VERSION2 29 +#define RR_ADC_TP_REV_VERSION3 32 + +#define RRADC_BATT_ID_DELAY_MAX 8 + +enum rradc_channel_id { + RR_ADC_BATT_ID = 0, + RR_ADC_BATT_THERM, + RR_ADC_SKIN_TEMP, + RR_ADC_USBIN_I, + RR_ADC_USBIN_V, + RR_ADC_DCIN_I, + RR_ADC_DCIN_V, + RR_ADC_DIE_TEMP, + RR_ADC_CHG_TEMP, + RR_ADC_GPIO, + RR_ADC_CHAN_MAX +}; + +struct rradc_chip; + +/** + * struct rradc_channel - rradc channel data + * @label: channel label + * @lsb: Channel least significant byte + * @status: Channel status address + * @size: number of bytes to read + * @trigger_addr: Trigger address, trigger is only used on some channels + * @trigger_mask: Trigger mask + * @scale_fn: Post process callback for channels which can't be exposed + * as offset + scale. + */ +struct rradc_channel { + const char *label; + u8 lsb; + u8 status; + int size; + int trigger_addr; + int trigger_mask; + int (*scale_fn)(struct rradc_chip *chip, u16 adc_code, int *result); +}; + +struct rradc_chip { + struct device *dev; + const struct qcom_spmi_pmic *pmic; + /* + * Lock held while doing channel conversion + * involving multiple register read/writes + */ + struct mutex conversion_lock; + struct regmap *regmap; + u32 base; + int batt_id_delay; + u16 batt_id_data; +}; + +static const int batt_id_delays[] = { 0, 1, 4, 12, 20, 40, 60, 80 }; +static const struct rradc_channel rradc_chans[RR_ADC_CHAN_MAX]; +static const struct iio_chan_spec rradc_iio_chans[RR_ADC_CHAN_MAX]; + +static int rradc_read(struct rradc_chip *chip, u16 addr, __le16 *buf, int len) +{ + int ret, retry_cnt = 0; + __le16 data_check[RR_ADC_CHAN_MAX_CONTINUOUS_BUFFER_LEN / 2]; + + if (len > RR_ADC_CHAN_MAX_CONTINUOUS_BUFFER_LEN) { + dev_err(chip->dev, + "Can't read more than %d bytes, but asked to read %d bytes.\n", + RR_ADC_CHAN_MAX_CONTINUOUS_BUFFER_LEN, len); + return -EINVAL; + } + + while (retry_cnt < RR_ADC_COHERENT_CHECK_RETRY) { + ret = regmap_bulk_read(chip->regmap, chip->base + addr, buf, + len); + if (ret < 0) { + dev_err(chip->dev, "rr_adc reg 0x%x failed :%d\n", addr, + ret); + return ret; + } + + ret = regmap_bulk_read(chip->regmap, chip->base + addr, + data_check, len); + if (ret < 0) { + dev_err(chip->dev, "rr_adc reg 0x%x failed :%d\n", addr, + ret); + return ret; + } + + if (memcmp(buf, data_check, len) != 0) { + retry_cnt++; + dev_dbg(chip->dev, + "coherent read error, retry_cnt:%d\n", + retry_cnt); + continue; + } + + break; + } + + if (retry_cnt == RR_ADC_COHERENT_CHECK_RETRY) + dev_err(chip->dev, "Retry exceeded for coherency check\n"); + + return ret; +} + +static int rradc_get_fab_coeff(struct rradc_chip *chip, int64_t *offset, + int64_t *slope) +{ + if (chip->pmic->subtype == PM660_SUBTYPE) { + switch (chip->pmic->fab_id) { + case PM660_FAB_ID_GF: + *offset = RR_ADC_CHG_TEMP_660_GF_OFFSET_UV; + *slope = RR_ADC_CHG_TEMP_660_GF_SLOPE_UV_PER_C; + return 0; + case PM660_FAB_ID_TSMC: + *offset = RR_ADC_CHG_TEMP_660_SMIC_OFFSET_UV; + *slope = RR_ADC_CHG_TEMP_660_SMIC_SLOPE_UV_PER_C; + return 0; + default: + *offset = RR_ADC_CHG_TEMP_660_MGNA_OFFSET_UV; + *slope = RR_ADC_CHG_TEMP_660_MGNA_SLOPE_UV_PER_C; + } + } else if (chip->pmic->subtype == PMI8998_SUBTYPE) { + switch (chip->pmic->fab_id) { + case PMI8998_FAB_ID_GF: + *offset = RR_ADC_CHG_TEMP_GF_OFFSET_UV; + *slope = RR_ADC_CHG_TEMP_GF_SLOPE_UV_PER_C; + return 0; + case PMI8998_FAB_ID_SMIC: + *offset = RR_ADC_CHG_TEMP_SMIC_OFFSET_UV; + *slope = RR_ADC_CHG_TEMP_SMIC_SLOPE_UV_PER_C; + return 0; + default: + return -EINVAL; + } + } + + return -EINVAL; +} + +/* + * These functions explicitly cast int64_t to int. + * They will never overflow, as the values are small enough. + */ +static int rradc_post_process_batt_id(struct rradc_chip *chip, u16 adc_code, + int *result_ohms) +{ + uint32_t current_value; + int64_t r_id; + + current_value = chip->batt_id_data; + r_id = ((int64_t)adc_code * RR_ADC_FS_VOLTAGE_MV); + r_id = div64_s64(r_id, (RR_ADC_CHAN_MSB * current_value)); + *result_ohms = (int)(r_id * MILLI); + + return 0; +} + +static int rradc_enable_continuous_mode(struct rradc_chip *chip) +{ + int ret; + + /* Clear channel log */ + ret = regmap_set_bits(chip->regmap, chip->base + RR_ADC_LOG, + RR_ADC_LOG_CLR_CTRL); + if (ret < 0) { + dev_err(chip->dev, "log ctrl update to clear failed:%d\n", ret); + return ret; + } + + ret = regmap_clear_bits(chip->regmap, chip->base + RR_ADC_LOG, + RR_ADC_LOG_CLR_CTRL); + if (ret < 0) { + dev_err(chip->dev, "log ctrl update to not clear failed:%d\n", + ret); + return ret; + } + + /* Switch to continuous mode */ + ret = regmap_set_bits(chip->regmap, chip->base + RR_ADC_CTL, + RR_ADC_CTL_CONTINUOUS_SEL); + if (ret < 0) + dev_err(chip->dev, "Update to continuous mode failed:%d\n", + ret); + + return ret; +} + +static int rradc_disable_continuous_mode(struct rradc_chip *chip) +{ + int ret; + + /* Switch to non continuous mode */ + ret = regmap_clear_bits(chip->regmap, chip->base + RR_ADC_CTL, + RR_ADC_CTL_CONTINUOUS_SEL); + if (ret < 0) + dev_err(chip->dev, "Update to non-continuous mode failed:%d\n", + ret); + + return ret; +} + +static bool rradc_is_ready(struct rradc_chip *chip, + enum rradc_channel_id chan_address) +{ + const struct rradc_channel *chan = &rradc_chans[chan_address]; + int ret; + unsigned int status, mask; + + /* BATT_ID STS bit does not get set initially */ + switch (chan_address) { + case RR_ADC_BATT_ID: + mask = RR_ADC_STS_CHANNEL_STS; + break; + default: + mask = RR_ADC_STS_CHANNEL_READING_MASK; + break; + } + + ret = regmap_read(chip->regmap, chip->base + chan->status, &status); + if (ret < 0 || !(status & mask)) + return false; + + return true; +} + +static int rradc_read_status_in_cont_mode(struct rradc_chip *chip, + enum rradc_channel_id chan_address) +{ + const struct rradc_channel *chan = &rradc_chans[chan_address]; + const struct iio_chan_spec *iio_chan = &rradc_iio_chans[chan_address]; + int ret, i; + + if (chan->trigger_mask == 0) { + dev_err(chip->dev, "Channel doesn't have a trigger mask\n"); + return -EINVAL; + } + + ret = regmap_set_bits(chip->regmap, chip->base + chan->trigger_addr, + chan->trigger_mask); + if (ret < 0) { + dev_err(chip->dev, + "Failed to apply trigger for channel '%s' ret=%d\n", + iio_chan->extend_name, ret); + return ret; + } + + ret = rradc_enable_continuous_mode(chip); + if (ret < 0) { + dev_err(chip->dev, "Failed to switch to continuous mode\n"); + goto disable_trigger; + } + + /* + * The wait/sleep values were found through trial and error, + * this is mostly for the battery ID channel which takes some + * time to settle. + */ + for (i = 0; i < 5; i++) { + if (rradc_is_ready(chip, chan_address)) + break; + usleep_range(50000, 50000 + 500); + } + + if (i == 5) { + dev_err(chip->dev, "Channel '%s' is not ready\n", + iio_chan->extend_name); + ret = -ETIMEDOUT; + } + + rradc_disable_continuous_mode(chip); + +disable_trigger: + regmap_clear_bits(chip->regmap, chip->base + chan->trigger_addr, + chan->trigger_mask); + + return ret; +} + +static int rradc_prepare_batt_id_conversion(struct rradc_chip *chip, + enum rradc_channel_id chan_address, + u16 *data) +{ + int ret; + + ret = regmap_set_bits(chip->regmap, chip->base + RR_ADC_BATT_ID_CTRL, + RR_ADC_BATT_ID_CTRL_CHANNEL_CONV); + if (ret < 0) { + dev_err(chip->dev, "Enabling BATT ID channel failed:%d\n", ret); + return ret; + } + + ret = regmap_set_bits(chip->regmap, + chip->base + RR_ADC_BATT_ID_TRIGGER, + RR_ADC_TRIGGER_CTL); + if (ret < 0) { + dev_err(chip->dev, "BATT_ID trigger set failed:%d\n", ret); + goto out_disable_batt_id; + } + + ret = rradc_read_status_in_cont_mode(chip, chan_address); + + /* Reset registers back to default values */ + regmap_clear_bits(chip->regmap, chip->base + RR_ADC_BATT_ID_TRIGGER, + RR_ADC_TRIGGER_CTL); + +out_disable_batt_id: + regmap_clear_bits(chip->regmap, chip->base + RR_ADC_BATT_ID_CTRL, + RR_ADC_BATT_ID_CTRL_CHANNEL_CONV); + + return ret; +} + +static int rradc_do_conversion(struct rradc_chip *chip, + enum rradc_channel_id chan_address, u16 *data) +{ + const struct rradc_channel *chan = &rradc_chans[chan_address]; + const struct iio_chan_spec *iio_chan = &rradc_iio_chans[chan_address]; + int ret; + __le16 buf[3]; + + mutex_lock(&chip->conversion_lock); + + switch (chan_address) { + case RR_ADC_BATT_ID: + ret = rradc_prepare_batt_id_conversion(chip, chan_address, data); + if (ret < 0) { + dev_err(chip->dev, "Battery ID conversion failed:%d\n", + ret); + goto unlock_out; + } + break; + + case RR_ADC_USBIN_V: + case RR_ADC_DIE_TEMP: + ret = rradc_read_status_in_cont_mode(chip, chan_address); + if (ret < 0) { + dev_err(chip->dev, + "Error reading in continuous mode:%d\n", ret); + goto unlock_out; + } + break; + default: + if (!rradc_is_ready(chip, chan_address)) { + /* + * Usually this means the channel isn't attached, for example + * the in_voltage_usbin_v_input channel will not be ready if + * no USB cable is attached + */ + dev_dbg(chip->dev, "channel '%s' is not ready\n", + iio_chan->extend_name); + ret = -ENODATA; + goto unlock_out; + } + break; + } + + ret = rradc_read(chip, chan->lsb, buf, chan->size); + if (ret) { + dev_err(chip->dev, "read data failed\n"); + goto unlock_out; + } + + /* + * For the battery ID we read the register for every ID ADC and then + * see which one is actually connected. + */ + if (chan_address == RR_ADC_BATT_ID) { + u16 batt_id_150 = le16_to_cpu(buf[2]); + u16 batt_id_15 = le16_to_cpu(buf[1]); + u16 batt_id_5 = le16_to_cpu(buf[0]); + + if (!batt_id_150 && !batt_id_15 && !batt_id_5) { + dev_err(chip->dev, + "Invalid batt_id values with all zeros\n"); + ret = -EINVAL; + goto unlock_out; + } + + if (batt_id_150 <= RR_ADC_BATT_ID_RANGE) { + *data = batt_id_150; + chip->batt_id_data = 150; + } else if (batt_id_15 <= RR_ADC_BATT_ID_RANGE) { + *data = batt_id_15; + chip->batt_id_data = 15; + } else { + *data = batt_id_5; + chip->batt_id_data = 5; + } + } else { + /* + * All of the other channels are either 1 or 2 bytes. + * We can rely on the second byte being 0 for 1-byte channels. + */ + *data = le16_to_cpu(buf[0]); + } + +unlock_out: + mutex_unlock(&chip->conversion_lock); + + return ret; +} + +static int rradc_read_scale(struct rradc_chip *chip, int chan_address, int *val, + int *val2) +{ + int64_t fab_offset, fab_slope; + int ret; + + ret = rradc_get_fab_coeff(chip, &fab_offset, &fab_slope); + if (ret < 0) { + dev_err(chip->dev, "Unable to get fab id coefficients\n"); + return -EINVAL; + } + + switch (chan_address) { + case RR_ADC_SKIN_TEMP: + *val = MILLI; + *val2 = RR_ADC_BATT_THERM_LSB_K; + return IIO_VAL_FRACTIONAL; + case RR_ADC_USBIN_I: + *val = RR_ADC_CURR_USBIN_INPUT_FACTOR_MIL * + RR_ADC_FS_VOLTAGE_MV; + *val2 = RR_ADC_CHAN_MSB; + return IIO_VAL_FRACTIONAL; + case RR_ADC_DCIN_I: + *val = RR_ADC_CURR_INPUT_FACTOR * RR_ADC_FS_VOLTAGE_MV; + *val2 = RR_ADC_CHAN_MSB; + return IIO_VAL_FRACTIONAL; + case RR_ADC_USBIN_V: + case RR_ADC_DCIN_V: + *val = RR_ADC_VOLT_INPUT_FACTOR * RR_ADC_FS_VOLTAGE_MV * MILLI; + *val2 = RR_ADC_CHAN_MSB; + return IIO_VAL_FRACTIONAL; + case RR_ADC_GPIO: + *val = RR_ADC_GPIO_FS_RANGE; + *val2 = RR_ADC_CHAN_MSB; + return IIO_VAL_FRACTIONAL; + case RR_ADC_CHG_TEMP: + /* + * We divide val2 by MILLI instead of multiplying val + * to avoid an integer overflow. + */ + *val = -RR_ADC_TEMP_FS_VOLTAGE_NUM; + *val2 = div64_s64(RR_ADC_TEMP_FS_VOLTAGE_DEN * RR_ADC_CHAN_MSB * + fab_slope, + MILLI); + + return IIO_VAL_FRACTIONAL; + case RR_ADC_DIE_TEMP: + *val = RR_ADC_TEMP_FS_VOLTAGE_NUM; + *val2 = RR_ADC_TEMP_FS_VOLTAGE_DEN * RR_ADC_CHAN_MSB * + RR_ADC_DIE_TEMP_SLOPE; + + return IIO_VAL_FRACTIONAL; + default: + return -EINVAL; + } +} + +static int rradc_read_offset(struct rradc_chip *chip, int chan_address, int *val) +{ + int64_t fab_offset, fab_slope; + int64_t offset1, offset2; + int ret; + + switch (chan_address) { + case RR_ADC_SKIN_TEMP: + /* + * Offset from kelvin to degC, divided by the + * scale factor (250). We lose some precision here. + * 273150 / 250 = 1092.6 + */ + *val = div64_s64(ABSOLUTE_ZERO_MILLICELSIUS, + (MILLI / RR_ADC_BATT_THERM_LSB_K)); + return IIO_VAL_INT; + case RR_ADC_CHG_TEMP: + ret = rradc_get_fab_coeff(chip, &fab_offset, &fab_slope); + if (ret < 0) { + dev_err(chip->dev, + "Unable to get fab id coefficients\n"); + return -EINVAL; + } + offset1 = -(fab_offset * RR_ADC_TEMP_FS_VOLTAGE_DEN * + RR_ADC_CHAN_MSB); + offset1 += (int64_t)RR_ADC_TEMP_FS_VOLTAGE_NUM / 2ULL; + offset1 = div64_s64(offset1, + (int64_t)(RR_ADC_TEMP_FS_VOLTAGE_NUM)); + + offset2 = (int64_t)RR_ADC_CHG_TEMP_OFFSET_MILLI_DEGC * + RR_ADC_TEMP_FS_VOLTAGE_DEN * RR_ADC_CHAN_MSB * + (int64_t)fab_slope; + offset2 += ((int64_t)MILLI * RR_ADC_TEMP_FS_VOLTAGE_NUM) / 2; + offset2 = div64_s64( + offset2, ((int64_t)MILLI * RR_ADC_TEMP_FS_VOLTAGE_NUM)); + + /* + * The -1 is to compensate for lost precision. + * It should actually be -0.7906976744186046. + * This works out to every value being off + * by about +0.091 degrees C after applying offset and scale. + */ + *val = (int)(offset1 - offset2 - 1); + return IIO_VAL_INT; + case RR_ADC_DIE_TEMP: + offset1 = -RR_ADC_DIE_TEMP_OFFSET * + (int64_t)RR_ADC_TEMP_FS_VOLTAGE_DEN * + (int64_t)RR_ADC_CHAN_MSB; + offset1 = div64_s64(offset1, RR_ADC_TEMP_FS_VOLTAGE_NUM); + + offset2 = -(int64_t)RR_ADC_CHG_TEMP_OFFSET_MILLI_DEGC * + RR_ADC_TEMP_FS_VOLTAGE_DEN * RR_ADC_CHAN_MSB * + RR_ADC_DIE_TEMP_SLOPE; + offset2 = div64_s64(offset2, + ((int64_t)RR_ADC_TEMP_FS_VOLTAGE_NUM)); + + /* + * The result is -339, it should be -338.69789, this results + * in the calculated die temp being off by + * -0.004 - -0.0175 degrees C + */ + *val = (int)(offset1 - offset2); + return IIO_VAL_INT; + default: + break; + } + return -EINVAL; +} + +static int rradc_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan_spec, int *val, + int *val2, long mask) +{ + struct rradc_chip *chip = iio_priv(indio_dev); + const struct rradc_channel *chan; + int ret; + u16 adc_code; + + if (chan_spec->address >= RR_ADC_CHAN_MAX) { + dev_err(chip->dev, "Invalid channel index:%lu\n", + chan_spec->address); + return -EINVAL; + } + + switch (mask) { + case IIO_CHAN_INFO_SCALE: + return rradc_read_scale(chip, chan_spec->address, val, val2); + case IIO_CHAN_INFO_OFFSET: + return rradc_read_offset(chip, chan_spec->address, val); + case IIO_CHAN_INFO_RAW: + ret = rradc_do_conversion(chip, chan_spec->address, &adc_code); + if (ret < 0) + return ret; + + *val = adc_code; + return IIO_VAL_INT; + case IIO_CHAN_INFO_PROCESSED: + chan = &rradc_chans[chan_spec->address]; + if (!chan->scale_fn) + return -EINVAL; + ret = rradc_do_conversion(chip, chan_spec->address, &adc_code); + if (ret < 0) + return ret; + + *val = chan->scale_fn(chip, adc_code, val); + return IIO_VAL_INT; + default: + return -EINVAL; + } +} + +static int rradc_read_label(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, char *label) +{ + return sysfs_emit(label, "%s\n", + rradc_chans[chan->address].label); +} + +static const struct iio_info rradc_info = { + .read_raw = rradc_read_raw, + .read_label = rradc_read_label, +}; + +static const struct rradc_channel rradc_chans[RR_ADC_CHAN_MAX] = { + { + .label = "batt_id", + .scale_fn = rradc_post_process_batt_id, + .lsb = RR_ADC_BATT_ID_5_LSB, + .status = RR_ADC_BATT_ID_STS, + .size = 6, + .trigger_addr = RR_ADC_BATT_ID_TRIGGER, + .trigger_mask = BIT(0), + }, { + .label = "batt", + .lsb = RR_ADC_BATT_THERM_LSB, + .status = RR_ADC_BATT_THERM_STS, + .size = 2, + .trigger_addr = RR_ADC_BATT_THERM_TRIGGER, + }, { + .label = "pmi8998_skin", + .lsb = RR_ADC_SKIN_TEMP_LSB, + .status = RR_ADC_AUX_THERM_STS, + .size = 2, + .trigger_addr = RR_ADC_AUX_THERM_TRIGGER, + }, { + .label = "usbin_i", + .lsb = RR_ADC_USB_IN_I_LSB, + .status = RR_ADC_USB_IN_I_STS, + .size = 2, + .trigger_addr = RR_ADC_USB_IN_I_TRIGGER, + }, { + .label = "usbin_v", + .lsb = RR_ADC_USB_IN_V_LSB, + .status = RR_ADC_USB_IN_V_STS, + .size = 2, + .trigger_addr = RR_ADC_USB_IN_V_TRIGGER, + .trigger_mask = BIT(7), + }, { + .label = "dcin_i", + .lsb = RR_ADC_DC_IN_I_LSB, + .status = RR_ADC_DC_IN_I_STS, + .size = 2, + .trigger_addr = RR_ADC_DC_IN_I_TRIGGER, + }, { + .label = "dcin_v", + .lsb = RR_ADC_DC_IN_V_LSB, + .status = RR_ADC_DC_IN_V_STS, + .size = 2, + .trigger_addr = RR_ADC_DC_IN_V_TRIGGER, + }, { + .label = "pmi8998_die", + .lsb = RR_ADC_PMI_DIE_TEMP_LSB, + .status = RR_ADC_PMI_DIE_TEMP_STS, + .size = 2, + .trigger_addr = RR_ADC_PMI_DIE_TEMP_TRIGGER, + .trigger_mask = RR_ADC_TRIGGER_EVERY_CYCLE, + }, { + .label = "chg", + .lsb = RR_ADC_CHARGER_TEMP_LSB, + .status = RR_ADC_CHARGER_TEMP_STS, + .size = 2, + .trigger_addr = RR_ADC_CHARGER_TEMP_TRIGGER, + }, { + .label = "gpio", + .lsb = RR_ADC_GPIO_LSB, + .status = RR_ADC_GPIO_STS, + .size = 2, + .trigger_addr = RR_ADC_GPIO_TRIGGER, + }, +}; + +static const struct iio_chan_spec rradc_iio_chans[RR_ADC_CHAN_MAX] = { + { + .type = IIO_RESISTANCE, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), + .address = RR_ADC_BATT_ID, + .channel = 0, + .indexed = 1, + }, { + .type = IIO_TEMP, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), + .address = RR_ADC_BATT_THERM, + .channel = 0, + .indexed = 1, + }, { + .type = IIO_TEMP, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SCALE) | + BIT(IIO_CHAN_INFO_OFFSET), + .address = RR_ADC_SKIN_TEMP, + .channel = 1, + .indexed = 1, + }, { + .type = IIO_CURRENT, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SCALE), + .address = RR_ADC_USBIN_I, + .channel = 0, + .indexed = 1, + }, { + .type = IIO_VOLTAGE, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SCALE), + .address = RR_ADC_USBIN_V, + .channel = 0, + .indexed = 1, + }, { + .type = IIO_CURRENT, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SCALE), + .address = RR_ADC_DCIN_I, + .channel = 1, + .indexed = 1, + }, { + .type = IIO_VOLTAGE, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SCALE), + .address = RR_ADC_DCIN_V, + .channel = 1, + .indexed = 1, + }, { + .type = IIO_TEMP, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SCALE) | + BIT(IIO_CHAN_INFO_OFFSET), + .address = RR_ADC_DIE_TEMP, + .channel = 2, + .indexed = 1, + }, { + .type = IIO_TEMP, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_OFFSET) | + BIT(IIO_CHAN_INFO_SCALE), + .address = RR_ADC_CHG_TEMP, + .channel = 3, + .indexed = 1, + }, { + .type = IIO_VOLTAGE, + .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED) | + BIT(IIO_CHAN_INFO_SCALE), + .address = RR_ADC_GPIO, + .channel = 2, + .indexed = 1, + }, +}; + +static int rradc_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct iio_dev *indio_dev; + struct rradc_chip *chip; + int ret, i, batt_id_delay; + + indio_dev = devm_iio_device_alloc(dev, sizeof(*chip)); + if (!indio_dev) + return -ENOMEM; + + chip = iio_priv(indio_dev); + chip->regmap = dev_get_regmap(pdev->dev.parent, NULL); + if (!chip->regmap) + return dev_err_probe(dev, -EINVAL, "Couldn't get parent's regmap\n"); + + chip->dev = dev; + mutex_init(&chip->conversion_lock); + + ret = device_property_read_u32(dev, "reg", &chip->base); + if (ret < 0) + return dev_err_probe(dev, ret, "Couldn't find reg address\n"); + + batt_id_delay = -1; + ret = device_property_read_u32(dev, "qcom,batt-id-delay-ms", + &batt_id_delay); + if (!ret) { + for (i = 0; i < RRADC_BATT_ID_DELAY_MAX; i++) { + if (batt_id_delay == batt_id_delays[i]) + break; + } + if (i == RRADC_BATT_ID_DELAY_MAX) + batt_id_delay = -1; + } + + if (batt_id_delay >= 0) { + batt_id_delay = FIELD_PREP(BATT_ID_SETTLE_MASK, batt_id_delay); + ret = regmap_set_bits(chip->regmap, + chip->base + RR_ADC_BATT_ID_CFG, + batt_id_delay); + if (ret < 0) { + dev_err(chip->dev, + "BATT_ID settling time config failed:%d\n", + ret); + } + } + + /* Get the PMIC revision, we need it to handle some varying coefficients */ + chip->pmic = qcom_pmic_get(chip->dev); + if (IS_ERR(chip->pmic)) + return dev_err_probe(dev, PTR_ERR(chip->pmic), + "Unable to get reference to PMIC device\n"); + + switch (chip->pmic->subtype) { + case PMI8998_SUBTYPE: + indio_dev->name = "pmi8998-rradc"; + break; + case PM660_SUBTYPE: + indio_dev->name = "pm660-rradc"; + break; + default: + indio_dev->name = DRIVER_NAME; + break; + } + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->info = &rradc_info; + indio_dev->channels = rradc_iio_chans; + indio_dev->num_channels = ARRAY_SIZE(rradc_iio_chans); + + return devm_iio_device_register(dev, indio_dev); +} + +static const struct of_device_id rradc_match_table[] = { + { .compatible = "qcom,pm660-rradc" }, + { .compatible = "qcom,pmi8998-rradc" }, + { } +}; +MODULE_DEVICE_TABLE(of, rradc_match_table); + +static struct platform_driver rradc_driver = { + .driver = { + .name = DRIVER_NAME, + .of_match_table = rradc_match_table, + }, + .probe = rradc_probe, +}; +module_platform_driver(rradc_driver); + +MODULE_DESCRIPTION("QCOM SPMI PMIC RR ADC driver"); +MODULE_AUTHOR("Casey Connolly "); +MODULE_LICENSE("GPL"); diff --git a/drivers/iio/adc/qcom-spmi-vadc.c b/drivers/iio/adc/qcom-spmi-vadc.c new file mode 100644 index 000000000..d7a2df3c8 --- /dev/null +++ b/drivers/iio/adc/qcom-spmi-vadc.c @@ -0,0 +1,950 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2012-2016, The Linux Foundation. All rights reserved. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +/* VADC register and bit definitions */ +#define VADC_REVISION2 0x1 +#define VADC_REVISION2_SUPPORTED_VADC 1 + +#define VADC_PERPH_TYPE 0x4 +#define VADC_PERPH_TYPE_ADC 8 + +#define VADC_PERPH_SUBTYPE 0x5 +#define VADC_PERPH_SUBTYPE_VADC 1 + +#define VADC_STATUS1 0x8 +#define VADC_STATUS1_OP_MODE 4 +#define VADC_STATUS1_REQ_STS BIT(1) +#define VADC_STATUS1_EOC BIT(0) +#define VADC_STATUS1_REQ_STS_EOC_MASK 0x3 + +#define VADC_MODE_CTL 0x40 +#define VADC_OP_MODE_SHIFT 3 +#define VADC_OP_MODE_NORMAL 0 +#define VADC_AMUX_TRIM_EN BIT(1) +#define VADC_ADC_TRIM_EN BIT(0) + +#define VADC_EN_CTL1 0x46 +#define VADC_EN_CTL1_SET BIT(7) + +#define VADC_ADC_CH_SEL_CTL 0x48 + +#define VADC_ADC_DIG_PARAM 0x50 +#define VADC_ADC_DIG_DEC_RATIO_SEL_SHIFT 2 + +#define VADC_HW_SETTLE_DELAY 0x51 + +#define VADC_CONV_REQ 0x52 +#define VADC_CONV_REQ_SET BIT(7) + +#define VADC_FAST_AVG_CTL 0x5a +#define VADC_FAST_AVG_EN 0x5b +#define VADC_FAST_AVG_EN_SET BIT(7) + +#define VADC_ACCESS 0xd0 +#define VADC_ACCESS_DATA 0xa5 + +#define VADC_PERH_RESET_CTL3 0xda +#define VADC_FOLLOW_WARM_RB BIT(2) + +#define VADC_DATA 0x60 /* 16 bits */ + +#define VADC_CHAN_MIN VADC_USBIN +#define VADC_CHAN_MAX VADC_LR_MUX3_BUF_PU1_PU2_XO_THERM + +/** + * struct vadc_channel_prop - VADC channel property. + * @channel: channel number, refer to the channel list. + * @calibration: calibration type. + * @decimation: sampling rate supported for the channel. + * @prescale: channel scaling performed on the input signal. + * @hw_settle_time: the time between AMUX being configured and the + * start of conversion. + * @avg_samples: ability to provide single result from the ADC + * that is an average of multiple measurements. + * @scale_fn_type: Represents the scaling function to convert voltage + * physical units desired by the client for the channel. + * @channel_name: Channel name used in device tree. + */ +struct vadc_channel_prop { + unsigned int channel; + enum vadc_calibration calibration; + unsigned int decimation; + unsigned int prescale; + unsigned int hw_settle_time; + unsigned int avg_samples; + enum vadc_scale_fn_type scale_fn_type; + const char *channel_name; +}; + +/** + * struct vadc_priv - VADC private structure. + * @regmap: pointer to struct regmap. + * @dev: pointer to struct device. + * @base: base address for the ADC peripheral. + * @nchannels: number of VADC channels. + * @chan_props: array of VADC channel properties. + * @iio_chans: array of IIO channels specification. + * @are_ref_measured: are reference points measured. + * @poll_eoc: use polling instead of interrupt. + * @complete: VADC result notification after interrupt is received. + * @graph: store parameters for calibration. + * @lock: ADC lock for access to the peripheral. + */ +struct vadc_priv { + struct regmap *regmap; + struct device *dev; + u16 base; + unsigned int nchannels; + struct vadc_channel_prop *chan_props; + struct iio_chan_spec *iio_chans; + bool are_ref_measured; + bool poll_eoc; + struct completion complete; + struct vadc_linear_graph graph[2]; + struct mutex lock; +}; + +static const struct u32_fract vadc_prescale_ratios[] = { + { .numerator = 1, .denominator = 1 }, + { .numerator = 1, .denominator = 3 }, + { .numerator = 1, .denominator = 4 }, + { .numerator = 1, .denominator = 6 }, + { .numerator = 1, .denominator = 20 }, + { .numerator = 1, .denominator = 8 }, + { .numerator = 10, .denominator = 81 }, + { .numerator = 1, .denominator = 10 }, +}; + +static int vadc_read(struct vadc_priv *vadc, u16 offset, u8 *data) +{ + return regmap_bulk_read(vadc->regmap, vadc->base + offset, data, 1); +} + +static int vadc_write(struct vadc_priv *vadc, u16 offset, u8 data) +{ + return regmap_write(vadc->regmap, vadc->base + offset, data); +} + +static int vadc_reset(struct vadc_priv *vadc) +{ + u8 data; + int ret; + + ret = vadc_write(vadc, VADC_ACCESS, VADC_ACCESS_DATA); + if (ret) + return ret; + + ret = vadc_read(vadc, VADC_PERH_RESET_CTL3, &data); + if (ret) + return ret; + + ret = vadc_write(vadc, VADC_ACCESS, VADC_ACCESS_DATA); + if (ret) + return ret; + + data |= VADC_FOLLOW_WARM_RB; + + return vadc_write(vadc, VADC_PERH_RESET_CTL3, data); +} + +static int vadc_set_state(struct vadc_priv *vadc, bool state) +{ + return vadc_write(vadc, VADC_EN_CTL1, state ? VADC_EN_CTL1_SET : 0); +} + +static void vadc_show_status(struct vadc_priv *vadc) +{ + u8 mode, sta1, chan, dig, en, req; + int ret; + + ret = vadc_read(vadc, VADC_MODE_CTL, &mode); + if (ret) + return; + + ret = vadc_read(vadc, VADC_ADC_DIG_PARAM, &dig); + if (ret) + return; + + ret = vadc_read(vadc, VADC_ADC_CH_SEL_CTL, &chan); + if (ret) + return; + + ret = vadc_read(vadc, VADC_CONV_REQ, &req); + if (ret) + return; + + ret = vadc_read(vadc, VADC_STATUS1, &sta1); + if (ret) + return; + + ret = vadc_read(vadc, VADC_EN_CTL1, &en); + if (ret) + return; + + dev_err(vadc->dev, + "mode:%02x en:%02x chan:%02x dig:%02x req:%02x sta1:%02x\n", + mode, en, chan, dig, req, sta1); +} + +static int vadc_configure(struct vadc_priv *vadc, + struct vadc_channel_prop *prop) +{ + u8 decimation, mode_ctrl; + int ret; + + /* Mode selection */ + mode_ctrl = (VADC_OP_MODE_NORMAL << VADC_OP_MODE_SHIFT) | + VADC_ADC_TRIM_EN | VADC_AMUX_TRIM_EN; + ret = vadc_write(vadc, VADC_MODE_CTL, mode_ctrl); + if (ret) + return ret; + + /* Channel selection */ + ret = vadc_write(vadc, VADC_ADC_CH_SEL_CTL, prop->channel); + if (ret) + return ret; + + /* Digital parameter setup */ + decimation = prop->decimation << VADC_ADC_DIG_DEC_RATIO_SEL_SHIFT; + ret = vadc_write(vadc, VADC_ADC_DIG_PARAM, decimation); + if (ret) + return ret; + + /* HW settle time delay */ + ret = vadc_write(vadc, VADC_HW_SETTLE_DELAY, prop->hw_settle_time); + if (ret) + return ret; + + ret = vadc_write(vadc, VADC_FAST_AVG_CTL, prop->avg_samples); + if (ret) + return ret; + + if (prop->avg_samples) + ret = vadc_write(vadc, VADC_FAST_AVG_EN, VADC_FAST_AVG_EN_SET); + else + ret = vadc_write(vadc, VADC_FAST_AVG_EN, 0); + + return ret; +} + +static int vadc_poll_wait_eoc(struct vadc_priv *vadc, unsigned int interval_us) +{ + unsigned int count, retry; + u8 sta1; + int ret; + + retry = interval_us / VADC_CONV_TIME_MIN_US; + + for (count = 0; count < retry; count++) { + ret = vadc_read(vadc, VADC_STATUS1, &sta1); + if (ret) + return ret; + + sta1 &= VADC_STATUS1_REQ_STS_EOC_MASK; + if (sta1 == VADC_STATUS1_EOC) + return 0; + + usleep_range(VADC_CONV_TIME_MIN_US, VADC_CONV_TIME_MAX_US); + } + + vadc_show_status(vadc); + + return -ETIMEDOUT; +} + +static int vadc_read_result(struct vadc_priv *vadc, u16 *data) +{ + int ret; + + ret = regmap_bulk_read(vadc->regmap, vadc->base + VADC_DATA, data, 2); + if (ret) + return ret; + + *data = clamp_t(u16, *data, VADC_MIN_ADC_CODE, VADC_MAX_ADC_CODE); + + return 0; +} + +static struct vadc_channel_prop *vadc_get_channel(struct vadc_priv *vadc, + unsigned int num) +{ + unsigned int i; + + for (i = 0; i < vadc->nchannels; i++) + if (vadc->chan_props[i].channel == num) + return &vadc->chan_props[i]; + + dev_dbg(vadc->dev, "no such channel %02x\n", num); + + return NULL; +} + +static int vadc_do_conversion(struct vadc_priv *vadc, + struct vadc_channel_prop *prop, u16 *data) +{ + unsigned int timeout; + int ret; + + mutex_lock(&vadc->lock); + + ret = vadc_configure(vadc, prop); + if (ret) + goto unlock; + + if (!vadc->poll_eoc) + reinit_completion(&vadc->complete); + + ret = vadc_set_state(vadc, true); + if (ret) + goto unlock; + + ret = vadc_write(vadc, VADC_CONV_REQ, VADC_CONV_REQ_SET); + if (ret) + goto err_disable; + + timeout = BIT(prop->avg_samples) * VADC_CONV_TIME_MIN_US * 2; + + if (vadc->poll_eoc) { + ret = vadc_poll_wait_eoc(vadc, timeout); + } else { + ret = wait_for_completion_timeout(&vadc->complete, timeout); + if (!ret) { + ret = -ETIMEDOUT; + goto err_disable; + } + + /* Double check conversion status */ + ret = vadc_poll_wait_eoc(vadc, VADC_CONV_TIME_MIN_US); + if (ret) + goto err_disable; + } + + ret = vadc_read_result(vadc, data); + +err_disable: + vadc_set_state(vadc, false); + if (ret) + dev_err(vadc->dev, "conversion failed\n"); +unlock: + mutex_unlock(&vadc->lock); + return ret; +} + +static int vadc_measure_ref_points(struct vadc_priv *vadc) +{ + struct vadc_channel_prop *prop; + u16 read_1, read_2; + int ret; + + vadc->graph[VADC_CALIB_RATIOMETRIC].dx = VADC_RATIOMETRIC_RANGE; + vadc->graph[VADC_CALIB_ABSOLUTE].dx = VADC_ABSOLUTE_RANGE_UV; + + prop = vadc_get_channel(vadc, VADC_REF_1250MV); + ret = vadc_do_conversion(vadc, prop, &read_1); + if (ret) + goto err; + + /* Try with buffered 625mV channel first */ + prop = vadc_get_channel(vadc, VADC_SPARE1); + if (!prop) + prop = vadc_get_channel(vadc, VADC_REF_625MV); + + ret = vadc_do_conversion(vadc, prop, &read_2); + if (ret) + goto err; + + if (read_1 == read_2) { + ret = -EINVAL; + goto err; + } + + vadc->graph[VADC_CALIB_ABSOLUTE].dy = read_1 - read_2; + vadc->graph[VADC_CALIB_ABSOLUTE].gnd = read_2; + + /* Ratiometric calibration */ + prop = vadc_get_channel(vadc, VADC_VDD_VADC); + ret = vadc_do_conversion(vadc, prop, &read_1); + if (ret) + goto err; + + prop = vadc_get_channel(vadc, VADC_GND_REF); + ret = vadc_do_conversion(vadc, prop, &read_2); + if (ret) + goto err; + + if (read_1 == read_2) { + ret = -EINVAL; + goto err; + } + + vadc->graph[VADC_CALIB_RATIOMETRIC].dy = read_1 - read_2; + vadc->graph[VADC_CALIB_RATIOMETRIC].gnd = read_2; +err: + if (ret) + dev_err(vadc->dev, "measure reference points failed\n"); + + return ret; +} + +static int vadc_prescaling_from_dt(u32 numerator, u32 denominator) +{ + unsigned int pre; + + for (pre = 0; pre < ARRAY_SIZE(vadc_prescale_ratios); pre++) + if (vadc_prescale_ratios[pre].numerator == numerator && + vadc_prescale_ratios[pre].denominator == denominator) + break; + + if (pre == ARRAY_SIZE(vadc_prescale_ratios)) + return -EINVAL; + + return pre; +} + +static int vadc_hw_settle_time_from_dt(u32 value) +{ + if ((value <= 1000 && value % 100) || (value > 1000 && value % 2000)) + return -EINVAL; + + if (value <= 1000) + value /= 100; + else + value = value / 2000 + 10; + + return value; +} + +static int vadc_avg_samples_from_dt(u32 value) +{ + if (!is_power_of_2(value) || value > VADC_AVG_SAMPLES_MAX) + return -EINVAL; + + return __ffs64(value); +} + +static int vadc_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int *val, int *val2, + long mask) +{ + struct vadc_priv *vadc = iio_priv(indio_dev); + struct vadc_channel_prop *prop; + u16 adc_code; + int ret; + + switch (mask) { + case IIO_CHAN_INFO_PROCESSED: + prop = &vadc->chan_props[chan->address]; + ret = vadc_do_conversion(vadc, prop, &adc_code); + if (ret) + break; + + ret = qcom_vadc_scale(prop->scale_fn_type, + &vadc->graph[prop->calibration], + &vadc_prescale_ratios[prop->prescale], + (prop->calibration == VADC_CALIB_ABSOLUTE), + adc_code, val); + if (ret) + break; + + return IIO_VAL_INT; + case IIO_CHAN_INFO_RAW: + prop = &vadc->chan_props[chan->address]; + ret = vadc_do_conversion(vadc, prop, &adc_code); + if (ret) + break; + + *val = (int)adc_code; + return IIO_VAL_INT; + default: + ret = -EINVAL; + break; + } + + return ret; +} + +static int vadc_fwnode_xlate(struct iio_dev *indio_dev, + const struct fwnode_reference_args *iiospec) +{ + struct vadc_priv *vadc = iio_priv(indio_dev); + unsigned int i; + + for (i = 0; i < vadc->nchannels; i++) + if (vadc->iio_chans[i].channel == iiospec->args[0]) + return i; + + return -EINVAL; +} + +static int vadc_read_label(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, char *label) +{ + struct vadc_priv *vadc = iio_priv(indio_dev); + const char *name = vadc->chan_props[chan->address].channel_name; + + return sysfs_emit(label, "%s\n", name); +} + +static const struct iio_info vadc_info = { + .read_raw = vadc_read_raw, + .read_label = vadc_read_label, + .fwnode_xlate = vadc_fwnode_xlate, +}; + +struct vadc_channels { + const char *datasheet_name; + unsigned int prescale_index; + enum iio_chan_type type; + long info_mask; + enum vadc_scale_fn_type scale_fn_type; +}; + +#define VADC_CHAN(_dname, _type, _mask, _pre, _scale) \ + [VADC_##_dname] = { \ + .datasheet_name = __stringify(_dname), \ + .prescale_index = _pre, \ + .type = _type, \ + .info_mask = _mask, \ + .scale_fn_type = _scale \ + }, \ + +#define VADC_NO_CHAN(_dname, _type, _mask, _pre) \ + [VADC_##_dname] = { \ + .datasheet_name = __stringify(_dname), \ + .prescale_index = _pre, \ + .type = _type, \ + .info_mask = _mask \ + }, + +#define VADC_CHAN_TEMP(_dname, _pre, _scale) \ + VADC_CHAN(_dname, IIO_TEMP, \ + BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_PROCESSED), \ + _pre, _scale) \ + +#define VADC_CHAN_VOLT(_dname, _pre, _scale) \ + VADC_CHAN(_dname, IIO_VOLTAGE, \ + BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_PROCESSED),\ + _pre, _scale) \ + +#define VADC_CHAN_NO_SCALE(_dname, _pre) \ + VADC_NO_CHAN(_dname, IIO_VOLTAGE, \ + BIT(IIO_CHAN_INFO_RAW), \ + _pre) \ + +/* + * The array represents all possible ADC channels found in the supported PMICs. + * Every index in the array is equal to the channel number per datasheet. The + * gaps in the array should be treated as reserved channels. + */ +static const struct vadc_channels vadc_chans[] = { + VADC_CHAN_VOLT(USBIN, 4, SCALE_DEFAULT) + VADC_CHAN_VOLT(DCIN, 4, SCALE_DEFAULT) + VADC_CHAN_NO_SCALE(VCHG_SNS, 3) + VADC_CHAN_NO_SCALE(SPARE1_03, 1) + VADC_CHAN_NO_SCALE(USB_ID_MV, 1) + VADC_CHAN_VOLT(VCOIN, 1, SCALE_DEFAULT) + VADC_CHAN_NO_SCALE(VBAT_SNS, 1) + VADC_CHAN_VOLT(VSYS, 1, SCALE_DEFAULT) + VADC_CHAN_TEMP(DIE_TEMP, 0, SCALE_PMIC_THERM) + VADC_CHAN_VOLT(REF_625MV, 0, SCALE_DEFAULT) + VADC_CHAN_VOLT(REF_1250MV, 0, SCALE_DEFAULT) + VADC_CHAN_NO_SCALE(CHG_TEMP, 0) + VADC_CHAN_NO_SCALE(SPARE1, 0) + VADC_CHAN_TEMP(SPARE2, 0, SCALE_PMI_CHG_TEMP) + VADC_CHAN_VOLT(GND_REF, 0, SCALE_DEFAULT) + VADC_CHAN_VOLT(VDD_VADC, 0, SCALE_DEFAULT) + + VADC_CHAN_NO_SCALE(P_MUX1_1_1, 0) + VADC_CHAN_NO_SCALE(P_MUX2_1_1, 0) + VADC_CHAN_NO_SCALE(P_MUX3_1_1, 0) + VADC_CHAN_NO_SCALE(P_MUX4_1_1, 0) + VADC_CHAN_NO_SCALE(P_MUX5_1_1, 0) + VADC_CHAN_NO_SCALE(P_MUX6_1_1, 0) + VADC_CHAN_NO_SCALE(P_MUX7_1_1, 0) + VADC_CHAN_NO_SCALE(P_MUX8_1_1, 0) + VADC_CHAN_NO_SCALE(P_MUX9_1_1, 0) + VADC_CHAN_NO_SCALE(P_MUX10_1_1, 0) + VADC_CHAN_NO_SCALE(P_MUX11_1_1, 0) + VADC_CHAN_NO_SCALE(P_MUX12_1_1, 0) + VADC_CHAN_NO_SCALE(P_MUX13_1_1, 0) + VADC_CHAN_NO_SCALE(P_MUX14_1_1, 0) + VADC_CHAN_NO_SCALE(P_MUX15_1_1, 0) + VADC_CHAN_NO_SCALE(P_MUX16_1_1, 0) + + VADC_CHAN_NO_SCALE(P_MUX1_1_3, 1) + VADC_CHAN_NO_SCALE(P_MUX2_1_3, 1) + VADC_CHAN_NO_SCALE(P_MUX3_1_3, 1) + VADC_CHAN_NO_SCALE(P_MUX4_1_3, 1) + VADC_CHAN_NO_SCALE(P_MUX5_1_3, 1) + VADC_CHAN_NO_SCALE(P_MUX6_1_3, 1) + VADC_CHAN_NO_SCALE(P_MUX7_1_3, 1) + VADC_CHAN_NO_SCALE(P_MUX8_1_3, 1) + VADC_CHAN_NO_SCALE(P_MUX9_1_3, 1) + VADC_CHAN_NO_SCALE(P_MUX10_1_3, 1) + VADC_CHAN_NO_SCALE(P_MUX11_1_3, 1) + VADC_CHAN_NO_SCALE(P_MUX12_1_3, 1) + VADC_CHAN_NO_SCALE(P_MUX13_1_3, 1) + VADC_CHAN_NO_SCALE(P_MUX14_1_3, 1) + VADC_CHAN_NO_SCALE(P_MUX15_1_3, 1) + VADC_CHAN_NO_SCALE(P_MUX16_1_3, 1) + + VADC_CHAN_NO_SCALE(LR_MUX1_BAT_THERM, 0) + VADC_CHAN_VOLT(LR_MUX2_BAT_ID, 0, SCALE_DEFAULT) + VADC_CHAN_NO_SCALE(LR_MUX3_XO_THERM, 0) + VADC_CHAN_NO_SCALE(LR_MUX4_AMUX_THM1, 0) + VADC_CHAN_NO_SCALE(LR_MUX5_AMUX_THM2, 0) + VADC_CHAN_NO_SCALE(LR_MUX6_AMUX_THM3, 0) + VADC_CHAN_NO_SCALE(LR_MUX7_HW_ID, 0) + VADC_CHAN_NO_SCALE(LR_MUX8_AMUX_THM4, 0) + VADC_CHAN_NO_SCALE(LR_MUX9_AMUX_THM5, 0) + VADC_CHAN_NO_SCALE(LR_MUX10_USB_ID, 0) + VADC_CHAN_NO_SCALE(AMUX_PU1, 0) + VADC_CHAN_NO_SCALE(AMUX_PU2, 0) + VADC_CHAN_NO_SCALE(LR_MUX3_BUF_XO_THERM, 0) + + VADC_CHAN_NO_SCALE(LR_MUX1_PU1_BAT_THERM, 0) + VADC_CHAN_NO_SCALE(LR_MUX2_PU1_BAT_ID, 0) + VADC_CHAN_NO_SCALE(LR_MUX3_PU1_XO_THERM, 0) + VADC_CHAN_TEMP(LR_MUX4_PU1_AMUX_THM1, 0, SCALE_THERM_100K_PULLUP) + VADC_CHAN_TEMP(LR_MUX5_PU1_AMUX_THM2, 0, SCALE_THERM_100K_PULLUP) + VADC_CHAN_TEMP(LR_MUX6_PU1_AMUX_THM3, 0, SCALE_THERM_100K_PULLUP) + VADC_CHAN_NO_SCALE(LR_MUX7_PU1_AMUX_HW_ID, 0) + VADC_CHAN_TEMP(LR_MUX8_PU1_AMUX_THM4, 0, SCALE_THERM_100K_PULLUP) + VADC_CHAN_TEMP(LR_MUX9_PU1_AMUX_THM5, 0, SCALE_THERM_100K_PULLUP) + VADC_CHAN_NO_SCALE(LR_MUX10_PU1_AMUX_USB_ID, 0) + VADC_CHAN_TEMP(LR_MUX3_BUF_PU1_XO_THERM, 0, SCALE_XOTHERM) + + VADC_CHAN_NO_SCALE(LR_MUX1_PU2_BAT_THERM, 0) + VADC_CHAN_NO_SCALE(LR_MUX2_PU2_BAT_ID, 0) + VADC_CHAN_NO_SCALE(LR_MUX3_PU2_XO_THERM, 0) + VADC_CHAN_NO_SCALE(LR_MUX4_PU2_AMUX_THM1, 0) + VADC_CHAN_NO_SCALE(LR_MUX5_PU2_AMUX_THM2, 0) + VADC_CHAN_NO_SCALE(LR_MUX6_PU2_AMUX_THM3, 0) + VADC_CHAN_NO_SCALE(LR_MUX7_PU2_AMUX_HW_ID, 0) + VADC_CHAN_NO_SCALE(LR_MUX8_PU2_AMUX_THM4, 0) + VADC_CHAN_NO_SCALE(LR_MUX9_PU2_AMUX_THM5, 0) + VADC_CHAN_NO_SCALE(LR_MUX10_PU2_AMUX_USB_ID, 0) + VADC_CHAN_NO_SCALE(LR_MUX3_BUF_PU2_XO_THERM, 0) + + VADC_CHAN_NO_SCALE(LR_MUX1_PU1_PU2_BAT_THERM, 0) + VADC_CHAN_NO_SCALE(LR_MUX2_PU1_PU2_BAT_ID, 0) + VADC_CHAN_NO_SCALE(LR_MUX3_PU1_PU2_XO_THERM, 0) + VADC_CHAN_NO_SCALE(LR_MUX4_PU1_PU2_AMUX_THM1, 0) + VADC_CHAN_NO_SCALE(LR_MUX5_PU1_PU2_AMUX_THM2, 0) + VADC_CHAN_NO_SCALE(LR_MUX6_PU1_PU2_AMUX_THM3, 0) + VADC_CHAN_NO_SCALE(LR_MUX7_PU1_PU2_AMUX_HW_ID, 0) + VADC_CHAN_NO_SCALE(LR_MUX8_PU1_PU2_AMUX_THM4, 0) + VADC_CHAN_NO_SCALE(LR_MUX9_PU1_PU2_AMUX_THM5, 0) + VADC_CHAN_NO_SCALE(LR_MUX10_PU1_PU2_AMUX_USB_ID, 0) + VADC_CHAN_NO_SCALE(LR_MUX3_BUF_PU1_PU2_XO_THERM, 0) +}; + +static int vadc_get_fw_channel_data(struct device *dev, + struct vadc_channel_prop *prop, + struct fwnode_handle *fwnode) +{ + const char *name = fwnode_get_name(fwnode), *label; + u32 chan, value, varr[2]; + int ret; + + ret = fwnode_property_read_u32(fwnode, "reg", &chan); + if (ret) { + dev_err(dev, "invalid channel number %s\n", name); + return ret; + } + + if (chan > VADC_CHAN_MAX || chan < VADC_CHAN_MIN) { + dev_err(dev, "%s invalid channel number %d\n", name, chan); + return -EINVAL; + } + + ret = fwnode_property_read_string(fwnode, "label", &label); + if (ret) + label = vadc_chans[chan].datasheet_name; + prop->channel_name = label; + + /* the channel has DT description */ + prop->channel = chan; + + ret = fwnode_property_read_u32(fwnode, "qcom,decimation", &value); + if (!ret) { + ret = qcom_vadc_decimation_from_dt(value); + if (ret < 0) { + dev_err(dev, "%02x invalid decimation %d\n", + chan, value); + return ret; + } + prop->decimation = ret; + } else { + prop->decimation = VADC_DEF_DECIMATION; + } + + ret = fwnode_property_read_u32_array(fwnode, "qcom,pre-scaling", varr, 2); + if (!ret) { + ret = vadc_prescaling_from_dt(varr[0], varr[1]); + if (ret < 0) { + dev_err(dev, "%02x invalid pre-scaling <%d %d>\n", + chan, varr[0], varr[1]); + return ret; + } + prop->prescale = ret; + } else { + prop->prescale = vadc_chans[prop->channel].prescale_index; + } + + ret = fwnode_property_read_u32(fwnode, "qcom,hw-settle-time", &value); + if (!ret) { + ret = vadc_hw_settle_time_from_dt(value); + if (ret < 0) { + dev_err(dev, "%02x invalid hw-settle-time %d us\n", + chan, value); + return ret; + } + prop->hw_settle_time = ret; + } else { + prop->hw_settle_time = VADC_DEF_HW_SETTLE_TIME; + } + + ret = fwnode_property_read_u32(fwnode, "qcom,avg-samples", &value); + if (!ret) { + ret = vadc_avg_samples_from_dt(value); + if (ret < 0) { + dev_err(dev, "%02x invalid avg-samples %d\n", + chan, value); + return ret; + } + prop->avg_samples = ret; + } else { + prop->avg_samples = VADC_DEF_AVG_SAMPLES; + } + + if (fwnode_property_read_bool(fwnode, "qcom,ratiometric")) + prop->calibration = VADC_CALIB_RATIOMETRIC; + else + prop->calibration = VADC_CALIB_ABSOLUTE; + + dev_dbg(dev, "%02x name %s\n", chan, name); + + return 0; +} + +static int vadc_get_fw_data(struct vadc_priv *vadc) +{ + const struct vadc_channels *vadc_chan; + struct iio_chan_spec *iio_chan; + struct vadc_channel_prop prop; + unsigned int index = 0; + int ret; + + vadc->nchannels = device_get_child_node_count(vadc->dev); + if (!vadc->nchannels) + return -EINVAL; + + vadc->iio_chans = devm_kcalloc(vadc->dev, vadc->nchannels, + sizeof(*vadc->iio_chans), GFP_KERNEL); + if (!vadc->iio_chans) + return -ENOMEM; + + vadc->chan_props = devm_kcalloc(vadc->dev, vadc->nchannels, + sizeof(*vadc->chan_props), GFP_KERNEL); + if (!vadc->chan_props) + return -ENOMEM; + + iio_chan = vadc->iio_chans; + + device_for_each_child_node_scoped(vadc->dev, child) { + ret = vadc_get_fw_channel_data(vadc->dev, &prop, child); + if (ret) + return ret; + + prop.scale_fn_type = vadc_chans[prop.channel].scale_fn_type; + vadc->chan_props[index] = prop; + + vadc_chan = &vadc_chans[prop.channel]; + + iio_chan->channel = prop.channel; + iio_chan->datasheet_name = vadc_chan->datasheet_name; + iio_chan->info_mask_separate = vadc_chan->info_mask; + iio_chan->type = vadc_chan->type; + iio_chan->indexed = 1; + iio_chan->address = index++; + + iio_chan++; + } + + /* These channels are mandatory, they are used as reference points */ + if (!vadc_get_channel(vadc, VADC_REF_1250MV)) { + dev_err(vadc->dev, "Please define 1.25V channel\n"); + return -ENODEV; + } + + if (!vadc_get_channel(vadc, VADC_REF_625MV)) { + dev_err(vadc->dev, "Please define 0.625V channel\n"); + return -ENODEV; + } + + if (!vadc_get_channel(vadc, VADC_VDD_VADC)) { + dev_err(vadc->dev, "Please define VDD channel\n"); + return -ENODEV; + } + + if (!vadc_get_channel(vadc, VADC_GND_REF)) { + dev_err(vadc->dev, "Please define GND channel\n"); + return -ENODEV; + } + + return 0; +} + +static irqreturn_t vadc_isr(int irq, void *dev_id) +{ + struct vadc_priv *vadc = dev_id; + + complete(&vadc->complete); + + return IRQ_HANDLED; +} + +static int vadc_check_revision(struct vadc_priv *vadc) +{ + u8 val; + int ret; + + ret = vadc_read(vadc, VADC_PERPH_TYPE, &val); + if (ret) + return ret; + + if (val < VADC_PERPH_TYPE_ADC) { + dev_err(vadc->dev, "%d is not ADC\n", val); + return -ENODEV; + } + + ret = vadc_read(vadc, VADC_PERPH_SUBTYPE, &val); + if (ret) + return ret; + + if (val < VADC_PERPH_SUBTYPE_VADC) { + dev_err(vadc->dev, "%d is not VADC\n", val); + return -ENODEV; + } + + ret = vadc_read(vadc, VADC_REVISION2, &val); + if (ret) + return ret; + + if (val < VADC_REVISION2_SUPPORTED_VADC) { + dev_err(vadc->dev, "revision %d not supported\n", val); + return -ENODEV; + } + + return 0; +} + +static int vadc_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct iio_dev *indio_dev; + struct vadc_priv *vadc; + struct regmap *regmap; + int ret, irq_eoc; + u32 reg; + + regmap = dev_get_regmap(dev->parent, NULL); + if (!regmap) + return -ENODEV; + + ret = device_property_read_u32(dev, "reg", ®); + if (ret < 0) + return ret; + + indio_dev = devm_iio_device_alloc(dev, sizeof(*vadc)); + if (!indio_dev) + return -ENOMEM; + + vadc = iio_priv(indio_dev); + vadc->regmap = regmap; + vadc->dev = dev; + vadc->base = reg; + vadc->are_ref_measured = false; + init_completion(&vadc->complete); + mutex_init(&vadc->lock); + + ret = vadc_check_revision(vadc); + if (ret) + return ret; + + ret = vadc_get_fw_data(vadc); + if (ret) + return ret; + + irq_eoc = platform_get_irq(pdev, 0); + if (irq_eoc < 0) { + if (irq_eoc == -EPROBE_DEFER || irq_eoc == -EINVAL) + return irq_eoc; + vadc->poll_eoc = true; + } else { + ret = devm_request_irq(dev, irq_eoc, vadc_isr, 0, + "spmi-vadc", vadc); + if (ret) + return ret; + } + + ret = vadc_reset(vadc); + if (ret) { + dev_err(dev, "reset failed\n"); + return ret; + } + + ret = vadc_measure_ref_points(vadc); + if (ret) + return ret; + + indio_dev->name = pdev->name; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->info = &vadc_info; + indio_dev->channels = vadc->iio_chans; + indio_dev->num_channels = vadc->nchannels; + + return devm_iio_device_register(dev, indio_dev); +} + +static const struct of_device_id vadc_match_table[] = { + { .compatible = "qcom,spmi-vadc" }, + { } +}; +MODULE_DEVICE_TABLE(of, vadc_match_table); + +static struct platform_driver vadc_driver = { + .driver = { + .name = "qcom-spmi-vadc", + .of_match_table = vadc_match_table, + }, + .probe = vadc_probe, +}; +module_platform_driver(vadc_driver); + +MODULE_ALIAS("platform:qcom-spmi-vadc"); +MODULE_DESCRIPTION("Qualcomm SPMI PMIC voltage ADC driver"); +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Stanimir Varbanov "); +MODULE_AUTHOR("Ivan T. Ivanov "); diff --git a/drivers/iio/adc/qcom-vadc-common.c b/drivers/iio/adc/qcom-vadc-common.c new file mode 100644 index 000000000..b03cf584b --- /dev/null +++ b/drivers/iio/adc/qcom-vadc-common.c @@ -0,0 +1,772 @@ +// SPDX-License-Identifier: GPL-2.0 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** + * struct vadc_map_pt - Map the graph representation for ADC channel + * @x: Represent the ADC digitized code. + * @y: Represent the physical data which can be temperature, voltage, + * resistance. + */ +struct vadc_map_pt { + s32 x; + s32 y; +}; + +/* Voltage to temperature */ +static const struct vadc_map_pt adcmap_100k_104ef_104fb[] = { + {1758, -40000 }, + {1742, -35000 }, + {1719, -30000 }, + {1691, -25000 }, + {1654, -20000 }, + {1608, -15000 }, + {1551, -10000 }, + {1483, -5000 }, + {1404, 0 }, + {1315, 5000 }, + {1218, 10000 }, + {1114, 15000 }, + {1007, 20000 }, + {900, 25000 }, + {795, 30000 }, + {696, 35000 }, + {605, 40000 }, + {522, 45000 }, + {448, 50000 }, + {383, 55000 }, + {327, 60000 }, + {278, 65000 }, + {237, 70000 }, + {202, 75000 }, + {172, 80000 }, + {146, 85000 }, + {125, 90000 }, + {107, 95000 }, + {92, 100000 }, + {79, 105000 }, + {68, 110000 }, + {59, 115000 }, + {51, 120000 }, + {44, 125000 } +}; + +/* + * Voltage to temperature table for 100k pull up for NTCG104EF104 with + * 1.875V reference. + */ +static const struct vadc_map_pt adcmap_100k_104ef_104fb_1875_vref[] = { + { 1831, -40000 }, + { 1814, -35000 }, + { 1791, -30000 }, + { 1761, -25000 }, + { 1723, -20000 }, + { 1675, -15000 }, + { 1616, -10000 }, + { 1545, -5000 }, + { 1463, 0 }, + { 1370, 5000 }, + { 1268, 10000 }, + { 1160, 15000 }, + { 1049, 20000 }, + { 937, 25000 }, + { 828, 30000 }, + { 726, 35000 }, + { 630, 40000 }, + { 544, 45000 }, + { 467, 50000 }, + { 399, 55000 }, + { 340, 60000 }, + { 290, 65000 }, + { 247, 70000 }, + { 209, 75000 }, + { 179, 80000 }, + { 153, 85000 }, + { 130, 90000 }, + { 112, 95000 }, + { 96, 100000 }, + { 82, 105000 }, + { 71, 110000 }, + { 62, 115000 }, + { 53, 120000 }, + { 46, 125000 }, +}; + +static const struct vadc_map_pt adcmap7_die_temp[] = { + { 857300, 160000 }, + { 820100, 140000 }, + { 782500, 120000 }, + { 744600, 100000 }, + { 706400, 80000 }, + { 667900, 60000 }, + { 629300, 40000 }, + { 590500, 20000 }, + { 551500, 0 }, + { 512400, -20000 }, + { 473100, -40000 }, + { 433700, -60000 }, +}; + +/* + * Resistance to temperature table for 100k pull up for NTCG104EF104. + */ +static const struct vadc_map_pt adcmap7_100k[] = { + { 4250657, -40960 }, + { 3962085, -39936 }, + { 3694875, -38912 }, + { 3447322, -37888 }, + { 3217867, -36864 }, + { 3005082, -35840 }, + { 2807660, -34816 }, + { 2624405, -33792 }, + { 2454218, -32768 }, + { 2296094, -31744 }, + { 2149108, -30720 }, + { 2012414, -29696 }, + { 1885232, -28672 }, + { 1766846, -27648 }, + { 1656598, -26624 }, + { 1553884, -25600 }, + { 1458147, -24576 }, + { 1368873, -23552 }, + { 1285590, -22528 }, + { 1207863, -21504 }, + { 1135290, -20480 }, + { 1067501, -19456 }, + { 1004155, -18432 }, + { 944935, -17408 }, + { 889550, -16384 }, + { 837731, -15360 }, + { 789229, -14336 }, + { 743813, -13312 }, + { 701271, -12288 }, + { 661405, -11264 }, + { 624032, -10240 }, + { 588982, -9216 }, + { 556100, -8192 }, + { 525239, -7168 }, + { 496264, -6144 }, + { 469050, -5120 }, + { 443480, -4096 }, + { 419448, -3072 }, + { 396851, -2048 }, + { 375597, -1024 }, + { 355598, 0 }, + { 336775, 1024 }, + { 319052, 2048 }, + { 302359, 3072 }, + { 286630, 4096 }, + { 271806, 5120 }, + { 257829, 6144 }, + { 244646, 7168 }, + { 232209, 8192 }, + { 220471, 9216 }, + { 209390, 10240 }, + { 198926, 11264 }, + { 189040, 12288 }, + { 179698, 13312 }, + { 170868, 14336 }, + { 162519, 15360 }, + { 154622, 16384 }, + { 147150, 17408 }, + { 140079, 18432 }, + { 133385, 19456 }, + { 127046, 20480 }, + { 121042, 21504 }, + { 115352, 22528 }, + { 109960, 23552 }, + { 104848, 24576 }, + { 100000, 25600 }, + { 95402, 26624 }, + { 91038, 27648 }, + { 86897, 28672 }, + { 82965, 29696 }, + { 79232, 30720 }, + { 75686, 31744 }, + { 72316, 32768 }, + { 69114, 33792 }, + { 66070, 34816 }, + { 63176, 35840 }, + { 60423, 36864 }, + { 57804, 37888 }, + { 55312, 38912 }, + { 52940, 39936 }, + { 50681, 40960 }, + { 48531, 41984 }, + { 46482, 43008 }, + { 44530, 44032 }, + { 42670, 45056 }, + { 40897, 46080 }, + { 39207, 47104 }, + { 37595, 48128 }, + { 36057, 49152 }, + { 34590, 50176 }, + { 33190, 51200 }, + { 31853, 52224 }, + { 30577, 53248 }, + { 29358, 54272 }, + { 28194, 55296 }, + { 27082, 56320 }, + { 26020, 57344 }, + { 25004, 58368 }, + { 24033, 59392 }, + { 23104, 60416 }, + { 22216, 61440 }, + { 21367, 62464 }, + { 20554, 63488 }, + { 19776, 64512 }, + { 19031, 65536 }, + { 18318, 66560 }, + { 17636, 67584 }, + { 16982, 68608 }, + { 16355, 69632 }, + { 15755, 70656 }, + { 15180, 71680 }, + { 14628, 72704 }, + { 14099, 73728 }, + { 13592, 74752 }, + { 13106, 75776 }, + { 12640, 76800 }, + { 12192, 77824 }, + { 11762, 78848 }, + { 11350, 79872 }, + { 10954, 80896 }, + { 10574, 81920 }, + { 10209, 82944 }, + { 9858, 83968 }, + { 9521, 84992 }, + { 9197, 86016 }, + { 8886, 87040 }, + { 8587, 88064 }, + { 8299, 89088 }, + { 8023, 90112 }, + { 7757, 91136 }, + { 7501, 92160 }, + { 7254, 93184 }, + { 7017, 94208 }, + { 6789, 95232 }, + { 6570, 96256 }, + { 6358, 97280 }, + { 6155, 98304 }, + { 5959, 99328 }, + { 5770, 100352 }, + { 5588, 101376 }, + { 5412, 102400 }, + { 5243, 103424 }, + { 5080, 104448 }, + { 4923, 105472 }, + { 4771, 106496 }, + { 4625, 107520 }, + { 4484, 108544 }, + { 4348, 109568 }, + { 4217, 110592 }, + { 4090, 111616 }, + { 3968, 112640 }, + { 3850, 113664 }, + { 3736, 114688 }, + { 3626, 115712 }, + { 3519, 116736 }, + { 3417, 117760 }, + { 3317, 118784 }, + { 3221, 119808 }, + { 3129, 120832 }, + { 3039, 121856 }, + { 2952, 122880 }, + { 2868, 123904 }, + { 2787, 124928 }, + { 2709, 125952 }, + { 2633, 126976 }, + { 2560, 128000 }, + { 2489, 129024 }, + { 2420, 130048 } +}; + +static const struct u32_fract adc5_prescale_ratios[] = { + { .numerator = 1, .denominator = 1 }, + { .numerator = 1, .denominator = 3 }, + { .numerator = 1, .denominator = 4 }, + { .numerator = 1, .denominator = 6 }, + { .numerator = 1, .denominator = 20 }, + { .numerator = 1, .denominator = 8 }, + { .numerator = 10, .denominator = 81 }, + { .numerator = 1, .denominator = 10 }, + { .numerator = 1, .denominator = 16 }, +}; + +static int qcom_vadc_scale_hw_calib_volt( + const struct u32_fract *prescale, + const struct adc5_data *data, + u16 adc_code, int *result_uv); +static int qcom_vadc_scale_hw_calib_therm( + const struct u32_fract *prescale, + const struct adc5_data *data, + u16 adc_code, int *result_mdec); +static int qcom_vadc7_scale_hw_calib_therm( + const struct u32_fract *prescale, + const struct adc5_data *data, + u16 adc_code, int *result_mdec); +static int qcom_vadc_scale_hw_smb_temp( + const struct u32_fract *prescale, + const struct adc5_data *data, + u16 adc_code, int *result_mdec); +static int qcom_vadc_scale_hw_chg5_temp( + const struct u32_fract *prescale, + const struct adc5_data *data, + u16 adc_code, int *result_mdec); +static int qcom_vadc_scale_hw_calib_die_temp( + const struct u32_fract *prescale, + const struct adc5_data *data, + u16 adc_code, int *result_mdec); +static int qcom_vadc7_scale_hw_calib_die_temp( + const struct u32_fract *prescale, + const struct adc5_data *data, + u16 adc_code, int *result_mdec); + +static const struct qcom_adc5_scale_type scale_adc5_fn[] = { + [SCALE_HW_CALIB_DEFAULT] = {qcom_vadc_scale_hw_calib_volt}, + [SCALE_HW_CALIB_THERM_100K_PULLUP] = {qcom_vadc_scale_hw_calib_therm}, + [SCALE_HW_CALIB_XOTHERM] = {qcom_vadc_scale_hw_calib_therm}, + [SCALE_HW_CALIB_THERM_100K_PU_PM7] = { + qcom_vadc7_scale_hw_calib_therm}, + [SCALE_HW_CALIB_PMIC_THERM] = {qcom_vadc_scale_hw_calib_die_temp}, + [SCALE_HW_CALIB_PMIC_THERM_PM7] = { + qcom_vadc7_scale_hw_calib_die_temp}, + [SCALE_HW_CALIB_PM5_CHG_TEMP] = {qcom_vadc_scale_hw_chg5_temp}, + [SCALE_HW_CALIB_PM5_SMB_TEMP] = {qcom_vadc_scale_hw_smb_temp}, +}; + +static int qcom_vadc_map_voltage_temp(const struct vadc_map_pt *pts, + u32 tablesize, s32 input, int *output) +{ + u32 i = 0; + + if (!pts) + return -EINVAL; + + while (i < tablesize && pts[i].x > input) + i++; + + if (i == 0) { + *output = pts[0].y; + } else if (i == tablesize) { + *output = pts[tablesize - 1].y; + } else { + /* interpolate linearly */ + *output = fixp_linear_interpolate(pts[i - 1].x, pts[i - 1].y, + pts[i].x, pts[i].y, + input); + } + + return 0; +} + +static s32 qcom_vadc_map_temp_voltage(const struct vadc_map_pt *pts, + u32 tablesize, int input) +{ + u32 i = 0; + + /* + * Table must be sorted, find the interval of 'y' which contains value + * 'input' and map it to proper 'x' value + */ + while (i < tablesize && pts[i].y < input) + i++; + + if (i == 0) + return pts[0].x; + if (i == tablesize) + return pts[tablesize - 1].x; + + /* interpolate linearly */ + return fixp_linear_interpolate(pts[i - 1].y, pts[i - 1].x, + pts[i].y, pts[i].x, input); +} + +static void qcom_vadc_scale_calib(const struct vadc_linear_graph *calib_graph, + u16 adc_code, + bool absolute, + s64 *scale_voltage) +{ + *scale_voltage = (adc_code - calib_graph->gnd); + *scale_voltage *= calib_graph->dx; + *scale_voltage = div64_s64(*scale_voltage, calib_graph->dy); + if (absolute) + *scale_voltage += calib_graph->dx; + + if (*scale_voltage < 0) + *scale_voltage = 0; +} + +static int qcom_vadc_scale_volt(const struct vadc_linear_graph *calib_graph, + const struct u32_fract *prescale, + bool absolute, u16 adc_code, + int *result_uv) +{ + s64 voltage = 0, result = 0; + + qcom_vadc_scale_calib(calib_graph, adc_code, absolute, &voltage); + + voltage *= prescale->denominator; + result = div64_s64(voltage, prescale->numerator); + *result_uv = result; + + return 0; +} + +static int qcom_vadc_scale_therm(const struct vadc_linear_graph *calib_graph, + const struct u32_fract *prescale, + bool absolute, u16 adc_code, + int *result_mdec) +{ + s64 voltage = 0; + int ret; + + qcom_vadc_scale_calib(calib_graph, adc_code, absolute, &voltage); + + if (absolute) + voltage = div64_s64(voltage, 1000); + + ret = qcom_vadc_map_voltage_temp(adcmap_100k_104ef_104fb, + ARRAY_SIZE(adcmap_100k_104ef_104fb), + voltage, result_mdec); + if (ret) + return ret; + + return 0; +} + +static int qcom_vadc_scale_die_temp(const struct vadc_linear_graph *calib_graph, + const struct u32_fract *prescale, + bool absolute, + u16 adc_code, int *result_mdec) +{ + s64 voltage = 0; + u64 temp; /* Temporary variable for do_div */ + + qcom_vadc_scale_calib(calib_graph, adc_code, absolute, &voltage); + + if (voltage > 0) { + temp = voltage * prescale->denominator; + do_div(temp, prescale->numerator * 2); + voltage = temp; + } else { + voltage = 0; + } + + *result_mdec = milli_kelvin_to_millicelsius(voltage); + + return 0; +} + +static int qcom_vadc_scale_chg_temp(const struct vadc_linear_graph *calib_graph, + const struct u32_fract *prescale, + bool absolute, + u16 adc_code, int *result_mdec) +{ + s64 voltage = 0, result = 0; + + qcom_vadc_scale_calib(calib_graph, adc_code, absolute, &voltage); + + voltage *= prescale->denominator; + voltage = div64_s64(voltage, prescale->numerator); + voltage = ((PMI_CHG_SCALE_1) * (voltage * 2)); + voltage = (voltage + PMI_CHG_SCALE_2); + result = div64_s64(voltage, 1000000); + *result_mdec = result; + + return 0; +} + +/* convert voltage to ADC code, using 1.875V reference */ +static u16 qcom_vadc_scale_voltage_code(s32 voltage, + const struct u32_fract *prescale, + const u32 full_scale_code_volt, + unsigned int factor) +{ + s64 volt = voltage; + s64 adc_vdd_ref_mv = 1875; /* reference voltage */ + + volt *= prescale->numerator * factor * full_scale_code_volt; + volt = div64_s64(volt, (s64)prescale->denominator * adc_vdd_ref_mv * 1000); + + return volt; +} + +static int qcom_vadc_scale_code_voltage_factor(u16 adc_code, + const struct u32_fract *prescale, + const struct adc5_data *data, + unsigned int factor) +{ + s64 voltage, temp, adc_vdd_ref_mv = 1875; + + /* + * The normal data range is between 0V to 1.875V. On cases where + * we read low voltage values, the ADC code can go beyond the + * range and the scale result is incorrect so we clamp the values + * for the cases where the code represents a value below 0V + */ + if (adc_code > VADC5_MAX_CODE) + adc_code = 0; + + /* (ADC code * vref_vadc (1.875V)) / full_scale_code */ + voltage = (s64) adc_code * adc_vdd_ref_mv * 1000; + voltage = div64_s64(voltage, data->full_scale_code_volt); + if (voltage > 0) { + voltage *= prescale->denominator; + temp = prescale->numerator * factor; + voltage = div64_s64(voltage, temp); + } else { + voltage = 0; + } + + return (int) voltage; +} + +static int qcom_vadc7_scale_hw_calib_therm( + const struct u32_fract *prescale, + const struct adc5_data *data, + u16 adc_code, int *result_mdec) +{ + s64 resistance = adc_code; + int ret, result; + + if (adc_code >= RATIO_MAX_ADC7) + return -EINVAL; + + /* (ADC code * R_PULLUP (100Kohm)) / (full_scale_code - ADC code)*/ + resistance *= R_PU_100K; + resistance = div64_s64(resistance, RATIO_MAX_ADC7 - adc_code); + + ret = qcom_vadc_map_voltage_temp(adcmap7_100k, + ARRAY_SIZE(adcmap7_100k), + resistance, &result); + if (ret) + return ret; + + *result_mdec = result; + + return 0; +} + +static int qcom_vadc_scale_hw_calib_volt( + const struct u32_fract *prescale, + const struct adc5_data *data, + u16 adc_code, int *result_uv) +{ + *result_uv = qcom_vadc_scale_code_voltage_factor(adc_code, + prescale, data, 1); + + return 0; +} + +static int qcom_vadc_scale_hw_calib_therm( + const struct u32_fract *prescale, + const struct adc5_data *data, + u16 adc_code, int *result_mdec) +{ + int voltage; + + voltage = qcom_vadc_scale_code_voltage_factor(adc_code, + prescale, data, 1000); + + /* Map voltage to temperature from look-up table */ + return qcom_vadc_map_voltage_temp(adcmap_100k_104ef_104fb_1875_vref, + ARRAY_SIZE(adcmap_100k_104ef_104fb_1875_vref), + voltage, result_mdec); +} + +static int qcom_vadc_scale_hw_calib_die_temp( + const struct u32_fract *prescale, + const struct adc5_data *data, + u16 adc_code, int *result_mdec) +{ + *result_mdec = qcom_vadc_scale_code_voltage_factor(adc_code, + prescale, data, 2); + *result_mdec = milli_kelvin_to_millicelsius(*result_mdec); + + return 0; +} + +static int qcom_vadc7_scale_hw_calib_die_temp( + const struct u32_fract *prescale, + const struct adc5_data *data, + u16 adc_code, int *result_mdec) +{ + + int voltage; + + voltage = qcom_vadc_scale_code_voltage_factor(adc_code, + prescale, data, 1); + + return qcom_vadc_map_voltage_temp(adcmap7_die_temp, ARRAY_SIZE(adcmap7_die_temp), + voltage, result_mdec); +} + +static int qcom_vadc_scale_hw_smb_temp( + const struct u32_fract *prescale, + const struct adc5_data *data, + u16 adc_code, int *result_mdec) +{ + *result_mdec = qcom_vadc_scale_code_voltage_factor(adc_code * 100, + prescale, data, PMIC5_SMB_TEMP_SCALE_FACTOR); + *result_mdec = PMIC5_SMB_TEMP_CONSTANT - *result_mdec; + + return 0; +} + +static int qcom_vadc_scale_hw_chg5_temp( + const struct u32_fract *prescale, + const struct adc5_data *data, + u16 adc_code, int *result_mdec) +{ + *result_mdec = qcom_vadc_scale_code_voltage_factor(adc_code, + prescale, data, 4); + *result_mdec = PMIC5_CHG_TEMP_SCALE_FACTOR - *result_mdec; + + return 0; +} + +int qcom_vadc_scale(enum vadc_scale_fn_type scaletype, + const struct vadc_linear_graph *calib_graph, + const struct u32_fract *prescale, + bool absolute, + u16 adc_code, int *result) +{ + switch (scaletype) { + case SCALE_DEFAULT: + return qcom_vadc_scale_volt(calib_graph, prescale, + absolute, adc_code, + result); + case SCALE_THERM_100K_PULLUP: + case SCALE_XOTHERM: + return qcom_vadc_scale_therm(calib_graph, prescale, + absolute, adc_code, + result); + case SCALE_PMIC_THERM: + return qcom_vadc_scale_die_temp(calib_graph, prescale, + absolute, adc_code, + result); + case SCALE_PMI_CHG_TEMP: + return qcom_vadc_scale_chg_temp(calib_graph, prescale, + absolute, adc_code, + result); + default: + return -EINVAL; + } +} +EXPORT_SYMBOL(qcom_vadc_scale); + +u16 qcom_adc_tm5_temp_volt_scale(unsigned int prescale_ratio, + u32 full_scale_code_volt, int temp) +{ + const struct u32_fract *prescale = &adc5_prescale_ratios[prescale_ratio]; + s32 voltage; + + voltage = qcom_vadc_map_temp_voltage(adcmap_100k_104ef_104fb_1875_vref, + ARRAY_SIZE(adcmap_100k_104ef_104fb_1875_vref), + temp); + return qcom_vadc_scale_voltage_code(voltage, prescale, full_scale_code_volt, 1000); +} +EXPORT_SYMBOL(qcom_adc_tm5_temp_volt_scale); + +u16 qcom_adc_tm5_gen2_temp_res_scale(int temp) +{ + int64_t resistance; + + resistance = qcom_vadc_map_temp_voltage(adcmap7_100k, + ARRAY_SIZE(adcmap7_100k), temp); + + return div64_s64(resistance * RATIO_MAX_ADC7, resistance + R_PU_100K); +} +EXPORT_SYMBOL(qcom_adc_tm5_gen2_temp_res_scale); + +int qcom_adc5_hw_scale(enum vadc_scale_fn_type scaletype, + unsigned int prescale_ratio, + const struct adc5_data *data, + u16 adc_code, int *result) +{ + const struct u32_fract *prescale = &adc5_prescale_ratios[prescale_ratio]; + + if (!(scaletype >= SCALE_HW_CALIB_DEFAULT && + scaletype < SCALE_HW_CALIB_INVALID)) { + pr_err("Invalid scale type %d\n", scaletype); + return -EINVAL; + } + + return scale_adc5_fn[scaletype].scale_fn(prescale, data, + adc_code, result); +} +EXPORT_SYMBOL(qcom_adc5_hw_scale); + +int qcom_adc5_prescaling_from_dt(u32 numerator, u32 denominator) +{ + unsigned int pre; + + for (pre = 0; pre < ARRAY_SIZE(adc5_prescale_ratios); pre++) + if (adc5_prescale_ratios[pre].numerator == numerator && + adc5_prescale_ratios[pre].denominator == denominator) + break; + + if (pre == ARRAY_SIZE(adc5_prescale_ratios)) + return -EINVAL; + + return pre; +} +EXPORT_SYMBOL(qcom_adc5_prescaling_from_dt); + +int qcom_adc5_hw_settle_time_from_dt(u32 value, + const unsigned int *hw_settle) +{ + unsigned int i; + + for (i = 0; i < VADC_HW_SETTLE_SAMPLES_MAX; i++) { + if (value == hw_settle[i]) + return i; + } + + return -EINVAL; +} +EXPORT_SYMBOL(qcom_adc5_hw_settle_time_from_dt); + +int qcom_adc5_avg_samples_from_dt(u32 value) +{ + if (!is_power_of_2(value) || value > ADC5_AVG_SAMPLES_MAX) + return -EINVAL; + + return __ffs(value); +} +EXPORT_SYMBOL(qcom_adc5_avg_samples_from_dt); + +int qcom_adc5_decimation_from_dt(u32 value, const unsigned int *decimation) +{ + unsigned int i; + + for (i = 0; i < ADC5_DECIMATION_SAMPLES_MAX; i++) { + if (value == decimation[i]) + return i; + } + + return -EINVAL; +} +EXPORT_SYMBOL(qcom_adc5_decimation_from_dt); + +int qcom_vadc_decimation_from_dt(u32 value) +{ + if (!is_power_of_2(value) || value < VADC_DECIMATION_MIN || + value > VADC_DECIMATION_MAX) + return -EINVAL; + + return __ffs64(value / VADC_DECIMATION_MIN); +} +EXPORT_SYMBOL(qcom_vadc_decimation_from_dt); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("Qualcomm ADC common functionality"); diff --git a/drivers/iio/adc/rcar-gyroadc.c b/drivers/iio/adc/rcar-gyroadc.c new file mode 100644 index 000000000..d38cf3cae --- /dev/null +++ b/drivers/iio/adc/rcar-gyroadc.c @@ -0,0 +1,594 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Renesas R-Car GyroADC driver + * + * Copyright 2016 Marek Vasut + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#define DRIVER_NAME "rcar-gyroadc" + +/* GyroADC registers. */ +#define RCAR_GYROADC_MODE_SELECT 0x00 +#define RCAR_GYROADC_MODE_SELECT_1_MB88101A 0x0 +#define RCAR_GYROADC_MODE_SELECT_2_ADCS7476 0x1 +#define RCAR_GYROADC_MODE_SELECT_3_MAX1162 0x3 + +#define RCAR_GYROADC_START_STOP 0x04 +#define RCAR_GYROADC_START_STOP_START BIT(0) + +#define RCAR_GYROADC_CLOCK_LENGTH 0x08 +#define RCAR_GYROADC_1_25MS_LENGTH 0x0c + +#define RCAR_GYROADC_REALTIME_DATA(ch) (0x10 + ((ch) * 4)) +#define RCAR_GYROADC_100MS_ADDED_DATA(ch) (0x30 + ((ch) * 4)) +#define RCAR_GYROADC_10MS_AVG_DATA(ch) (0x50 + ((ch) * 4)) + +#define RCAR_GYROADC_FIFO_STATUS 0x70 +#define RCAR_GYROADC_FIFO_STATUS_EMPTY(ch) BIT(0 + (4 * (ch))) +#define RCAR_GYROADC_FIFO_STATUS_FULL(ch) BIT(1 + (4 * (ch))) +#define RCAR_GYROADC_FIFO_STATUS_ERROR(ch) BIT(2 + (4 * (ch))) + +#define RCAR_GYROADC_INTR 0x74 +#define RCAR_GYROADC_INTR_INT BIT(0) + +#define RCAR_GYROADC_INTENR 0x78 +#define RCAR_GYROADC_INTENR_INTEN BIT(0) + +#define RCAR_GYROADC_SAMPLE_RATE 800 /* Hz */ + +#define RCAR_GYROADC_RUNTIME_PM_DELAY_MS 2000 + +enum rcar_gyroadc_model { + RCAR_GYROADC_MODEL_DEFAULT, + RCAR_GYROADC_MODEL_R8A7792, +}; + +struct rcar_gyroadc { + struct device *dev; + void __iomem *regs; + struct clk *clk; + struct regulator *vref[8]; + unsigned int num_channels; + enum rcar_gyroadc_model model; + unsigned int mode; + unsigned int sample_width; +}; + +static void rcar_gyroadc_hw_init(struct rcar_gyroadc *priv) +{ + const unsigned long clk_mhz = clk_get_rate(priv->clk) / 1000000; + const unsigned long clk_mul = + (priv->mode == RCAR_GYROADC_MODE_SELECT_1_MB88101A) ? 10 : 5; + unsigned long clk_len = clk_mhz * clk_mul; + + /* + * According to the R-Car Gen2 datasheet Rev. 1.01, Sept 08 2014, + * page 77-7, clock length must be even number. If it's odd number, + * add one. + */ + if (clk_len & 1) + clk_len++; + + /* Stop the GyroADC. */ + writel(0, priv->regs + RCAR_GYROADC_START_STOP); + + /* Disable IRQ on V2H. */ + if (priv->model == RCAR_GYROADC_MODEL_R8A7792) + writel(0, priv->regs + RCAR_GYROADC_INTENR); + + /* Set mode and timing. */ + writel(priv->mode, priv->regs + RCAR_GYROADC_MODE_SELECT); + writel(clk_len, priv->regs + RCAR_GYROADC_CLOCK_LENGTH); + writel(clk_mhz * 1250, priv->regs + RCAR_GYROADC_1_25MS_LENGTH); +} + +static void rcar_gyroadc_hw_start(struct rcar_gyroadc *priv) +{ + /* Start sampling. */ + writel(RCAR_GYROADC_START_STOP_START, + priv->regs + RCAR_GYROADC_START_STOP); + + /* + * Wait for the first conversion to complete. This is longer than + * the 1.25 mS in the datasheet because 1.25 mS is not enough for + * the hardware to deliver the first sample and the hardware does + * then return zeroes instead of valid data. + */ + mdelay(3); +} + +static void rcar_gyroadc_hw_stop(struct rcar_gyroadc *priv) +{ + /* Stop the GyroADC. */ + writel(0, priv->regs + RCAR_GYROADC_START_STOP); +} + +#define RCAR_GYROADC_CHAN(_idx) { \ + .type = IIO_VOLTAGE, \ + .indexed = 1, \ + .channel = (_idx), \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ + BIT(IIO_CHAN_INFO_SCALE), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SAMP_FREQ), \ +} + +static const struct iio_chan_spec rcar_gyroadc_iio_channels_1[] = { + RCAR_GYROADC_CHAN(0), + RCAR_GYROADC_CHAN(1), + RCAR_GYROADC_CHAN(2), + RCAR_GYROADC_CHAN(3), +}; + +static const struct iio_chan_spec rcar_gyroadc_iio_channels_2[] = { + RCAR_GYROADC_CHAN(0), + RCAR_GYROADC_CHAN(1), + RCAR_GYROADC_CHAN(2), + RCAR_GYROADC_CHAN(3), + RCAR_GYROADC_CHAN(4), + RCAR_GYROADC_CHAN(5), + RCAR_GYROADC_CHAN(6), + RCAR_GYROADC_CHAN(7), +}; + +static const struct iio_chan_spec rcar_gyroadc_iio_channels_3[] = { + RCAR_GYROADC_CHAN(0), + RCAR_GYROADC_CHAN(1), + RCAR_GYROADC_CHAN(2), + RCAR_GYROADC_CHAN(3), + RCAR_GYROADC_CHAN(4), + RCAR_GYROADC_CHAN(5), + RCAR_GYROADC_CHAN(6), + RCAR_GYROADC_CHAN(7), +}; + +static int rcar_gyroadc_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct rcar_gyroadc *priv = iio_priv(indio_dev); + struct regulator *consumer; + unsigned int datareg = RCAR_GYROADC_REALTIME_DATA(chan->channel); + unsigned int vref; + int ret; + + /* + * MB88101 is special in that it has only single regulator for + * all four channels. + */ + if (priv->mode == RCAR_GYROADC_MODE_SELECT_1_MB88101A) + consumer = priv->vref[0]; + else + consumer = priv->vref[chan->channel]; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + if (chan->type != IIO_VOLTAGE) + return -EINVAL; + + /* Channel not connected. */ + if (!consumer) + return -EINVAL; + + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; + + ret = pm_runtime_resume_and_get(priv->dev); + if (ret < 0) { + iio_device_release_direct(indio_dev); + return ret; + } + + *val = readl(priv->regs + datareg); + *val &= BIT(priv->sample_width) - 1; + + ret = pm_runtime_put_autosuspend(priv->dev); + iio_device_release_direct(indio_dev); + if (ret < 0) + return ret; + + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + /* Channel not connected. */ + if (!consumer) + return -EINVAL; + + vref = regulator_get_voltage(consumer); + *val = vref / 1000; + *val2 = 1 << priv->sample_width; + + return IIO_VAL_FRACTIONAL; + case IIO_CHAN_INFO_SAMP_FREQ: + *val = RCAR_GYROADC_SAMPLE_RATE; + + return IIO_VAL_INT; + default: + return -EINVAL; + } +} + +static int rcar_gyroadc_reg_access(struct iio_dev *indio_dev, + unsigned int reg, unsigned int writeval, + unsigned int *readval) +{ + struct rcar_gyroadc *priv = iio_priv(indio_dev); + unsigned int maxreg = RCAR_GYROADC_FIFO_STATUS; + + if (readval == NULL) + return -EINVAL; + + if (reg % 4) + return -EINVAL; + + /* Handle the V2H case with extra interrupt block. */ + if (priv->model == RCAR_GYROADC_MODEL_R8A7792) + maxreg = RCAR_GYROADC_INTENR; + + if (reg > maxreg) + return -EINVAL; + + *readval = readl(priv->regs + reg); + + return 0; +} + +static const struct iio_info rcar_gyroadc_iio_info = { + .read_raw = rcar_gyroadc_read_raw, + .debugfs_reg_access = rcar_gyroadc_reg_access, +}; + +static const struct of_device_id rcar_gyroadc_match[] = { + { + /* R-Car compatible GyroADC */ + .compatible = "renesas,rcar-gyroadc", + .data = (void *)RCAR_GYROADC_MODEL_DEFAULT, + }, { + /* R-Car V2H specialty with interrupt registers. */ + .compatible = "renesas,r8a7792-gyroadc", + .data = (void *)RCAR_GYROADC_MODEL_R8A7792, + }, { + /* sentinel */ + } +}; + +MODULE_DEVICE_TABLE(of, rcar_gyroadc_match); + +static const struct of_device_id rcar_gyroadc_child_match[] __maybe_unused = { + /* Mode 1 ADCs */ + { + .compatible = "fujitsu,mb88101a", + .data = (void *)RCAR_GYROADC_MODE_SELECT_1_MB88101A, + }, + /* Mode 2 ADCs */ + { + .compatible = "ti,adcs7476", + .data = (void *)RCAR_GYROADC_MODE_SELECT_2_ADCS7476, + }, { + .compatible = "ti,adc121", + .data = (void *)RCAR_GYROADC_MODE_SELECT_2_ADCS7476, + }, { + .compatible = "adi,ad7476", + .data = (void *)RCAR_GYROADC_MODE_SELECT_2_ADCS7476, + }, + /* Mode 3 ADCs */ + { + .compatible = "maxim,max1162", + .data = (void *)RCAR_GYROADC_MODE_SELECT_3_MAX1162, + }, { + .compatible = "maxim,max11100", + .data = (void *)RCAR_GYROADC_MODE_SELECT_3_MAX1162, + }, + { } +}; + +static int rcar_gyroadc_parse_subdevs(struct iio_dev *indio_dev) +{ + const struct of_device_id *of_id; + const struct iio_chan_spec *channels; + struct rcar_gyroadc *priv = iio_priv(indio_dev); + struct device *dev = priv->dev; + struct device_node *np = dev->of_node; + struct regulator *vref; + unsigned int reg; + unsigned int adcmode = -1, childmode; + unsigned int sample_width; + unsigned int num_channels; + int ret, first = 1; + + for_each_available_child_of_node_scoped(np, child) { + of_id = of_match_node(rcar_gyroadc_child_match, child); + if (!of_id) { + dev_err(dev, "Ignoring unsupported ADC \"%pOFn\".", + child); + continue; + } + + childmode = (uintptr_t)of_id->data; + switch (childmode) { + case RCAR_GYROADC_MODE_SELECT_1_MB88101A: + sample_width = 12; + channels = rcar_gyroadc_iio_channels_1; + num_channels = ARRAY_SIZE(rcar_gyroadc_iio_channels_1); + break; + case RCAR_GYROADC_MODE_SELECT_2_ADCS7476: + sample_width = 15; + channels = rcar_gyroadc_iio_channels_2; + num_channels = ARRAY_SIZE(rcar_gyroadc_iio_channels_2); + break; + case RCAR_GYROADC_MODE_SELECT_3_MAX1162: + sample_width = 16; + channels = rcar_gyroadc_iio_channels_3; + num_channels = ARRAY_SIZE(rcar_gyroadc_iio_channels_3); + break; + default: + return -EINVAL; + } + + /* + * MB88101 is special in that it's only a single chip taking + * up all the CHS lines. Thus, the DT binding is also special + * and has no reg property. If we run into such ADC, handle + * it here. + */ + if (childmode == RCAR_GYROADC_MODE_SELECT_1_MB88101A) { + reg = 0; + } else { + ret = of_property_read_u32(child, "reg", ®); + if (ret) { + dev_err(dev, + "Failed to get child reg property of ADC \"%pOFn\".\n", + child); + return ret; + } + + /* Channel number is too high. */ + if (reg >= num_channels) { + dev_err(dev, + "Only %i channels supported with %pOFn, but reg = <%i>.\n", + num_channels, child, reg); + return -EINVAL; + } + } + + /* Child node selected different mode than the rest. */ + if (!first && (adcmode != childmode)) { + dev_err(dev, + "Channel %i uses different ADC mode than the rest.\n", + reg); + return -EINVAL; + } + + /* Channel is valid, grab the regulator. */ + dev->of_node = child; + vref = devm_regulator_get(dev, "vref"); + dev->of_node = np; + if (IS_ERR(vref)) { + dev_dbg(dev, "Channel %i 'vref' supply not connected.\n", + reg); + return PTR_ERR(vref); + } + + priv->vref[reg] = vref; + + if (!first) + continue; + + /* First child node which passed sanity tests. */ + adcmode = childmode; + first = 0; + + priv->num_channels = num_channels; + priv->mode = childmode; + priv->sample_width = sample_width; + + indio_dev->channels = channels; + indio_dev->num_channels = num_channels; + + /* + * MB88101 is special and we only have one such device + * attached to the GyroADC at a time, so if we found it, + * we can stop parsing here. + */ + if (childmode == RCAR_GYROADC_MODE_SELECT_1_MB88101A) { + break; + } + } + + if (first) { + dev_err(dev, "No valid ADC channels found, aborting.\n"); + return -EINVAL; + } + + return 0; +} + +static void rcar_gyroadc_deinit_supplies(struct iio_dev *indio_dev) +{ + struct rcar_gyroadc *priv = iio_priv(indio_dev); + unsigned int i; + + for (i = 0; i < priv->num_channels; i++) { + if (!priv->vref[i]) + continue; + + regulator_disable(priv->vref[i]); + } +} + +static int rcar_gyroadc_init_supplies(struct iio_dev *indio_dev) +{ + struct rcar_gyroadc *priv = iio_priv(indio_dev); + struct device *dev = priv->dev; + unsigned int i; + int ret; + + for (i = 0; i < priv->num_channels; i++) { + if (!priv->vref[i]) + continue; + + ret = regulator_enable(priv->vref[i]); + if (ret) { + dev_err(dev, "Failed to enable regulator %i (ret=%i)\n", + i, ret); + goto err; + } + } + + return 0; + +err: + rcar_gyroadc_deinit_supplies(indio_dev); + return ret; +} + +static int rcar_gyroadc_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct rcar_gyroadc *priv; + struct iio_dev *indio_dev; + int ret; + + indio_dev = devm_iio_device_alloc(dev, sizeof(*priv)); + if (!indio_dev) + return -ENOMEM; + + priv = iio_priv(indio_dev); + priv->dev = dev; + + priv->regs = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(priv->regs)) + return PTR_ERR(priv->regs); + + priv->clk = devm_clk_get(dev, "fck"); + if (IS_ERR(priv->clk)) + return dev_err_probe(dev, PTR_ERR(priv->clk), + "Failed to get IF clock\n"); + + ret = rcar_gyroadc_parse_subdevs(indio_dev); + if (ret) + return ret; + + ret = rcar_gyroadc_init_supplies(indio_dev); + if (ret) + return ret; + + priv->model = (uintptr_t)of_device_get_match_data(&pdev->dev); + + platform_set_drvdata(pdev, indio_dev); + + indio_dev->name = DRIVER_NAME; + indio_dev->info = &rcar_gyroadc_iio_info; + indio_dev->modes = INDIO_DIRECT_MODE; + + ret = clk_prepare_enable(priv->clk); + if (ret) { + dev_err(dev, "Could not prepare or enable the IF clock.\n"); + goto err_clk_if_enable; + } + + pm_runtime_set_autosuspend_delay(dev, RCAR_GYROADC_RUNTIME_PM_DELAY_MS); + pm_runtime_use_autosuspend(dev); + pm_runtime_enable(dev); + + ret = pm_runtime_resume_and_get(dev); + if (ret) + goto err_power_up; + + rcar_gyroadc_hw_init(priv); + rcar_gyroadc_hw_start(priv); + + ret = iio_device_register(indio_dev); + if (ret) { + dev_err(dev, "Couldn't register IIO device.\n"); + goto err_iio_device_register; + } + + pm_runtime_put_sync(dev); + + return 0; + +err_iio_device_register: + rcar_gyroadc_hw_stop(priv); + pm_runtime_put_sync(dev); +err_power_up: + pm_runtime_disable(dev); + pm_runtime_set_suspended(dev); + clk_disable_unprepare(priv->clk); +err_clk_if_enable: + rcar_gyroadc_deinit_supplies(indio_dev); + + return ret; +} + +static void rcar_gyroadc_remove(struct platform_device *pdev) +{ + struct iio_dev *indio_dev = platform_get_drvdata(pdev); + struct rcar_gyroadc *priv = iio_priv(indio_dev); + struct device *dev = priv->dev; + + iio_device_unregister(indio_dev); + pm_runtime_get_sync(dev); + rcar_gyroadc_hw_stop(priv); + pm_runtime_put_sync(dev); + pm_runtime_disable(dev); + pm_runtime_set_suspended(dev); + clk_disable_unprepare(priv->clk); + rcar_gyroadc_deinit_supplies(indio_dev); +} + +static int rcar_gyroadc_suspend(struct device *dev) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct rcar_gyroadc *priv = iio_priv(indio_dev); + + rcar_gyroadc_hw_stop(priv); + + return 0; +} + +static int rcar_gyroadc_resume(struct device *dev) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct rcar_gyroadc *priv = iio_priv(indio_dev); + + rcar_gyroadc_hw_start(priv); + + return 0; +} + +static const struct dev_pm_ops rcar_gyroadc_pm_ops = { + RUNTIME_PM_OPS(rcar_gyroadc_suspend, rcar_gyroadc_resume, NULL) +}; + +static struct platform_driver rcar_gyroadc_driver = { + .probe = rcar_gyroadc_probe, + .remove = rcar_gyroadc_remove, + .driver = { + .name = DRIVER_NAME, + .of_match_table = rcar_gyroadc_match, + .pm = pm_ptr(&rcar_gyroadc_pm_ops), + }, +}; + +module_platform_driver(rcar_gyroadc_driver); + +MODULE_AUTHOR("Marek Vasut "); +MODULE_DESCRIPTION("Renesas R-Car GyroADC driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/iio/adc/rn5t618-adc.c b/drivers/iio/adc/rn5t618-adc.c new file mode 100644 index 000000000..f78fc795b --- /dev/null +++ b/drivers/iio/adc/rn5t618-adc.c @@ -0,0 +1,259 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * ADC driver for the RICOH RN5T618 power management chip family + * + * Copyright (C) 2019 Andreas Kemnade + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define RN5T618_ADC_CONVERSION_TIMEOUT (msecs_to_jiffies(500)) +#define RN5T618_REFERENCE_VOLT 2500 + +/* mask for selecting channels for single conversion */ +#define RN5T618_ADCCNT3_CHANNEL_MASK 0x7 +/* average 4-time conversion mode */ +#define RN5T618_ADCCNT3_AVG BIT(3) +/* set for starting a single conversion, gets cleared by hw when done */ +#define RN5T618_ADCCNT3_GODONE BIT(4) +/* automatic conversion, period is in ADCCNT2, selected channels are + * in ADCCNT1 + */ +#define RN5T618_ADCCNT3_AUTO BIT(5) +#define RN5T618_ADCEND_IRQ BIT(0) + +struct rn5t618_adc_data { + struct device *dev; + struct rn5t618 *rn5t618; + struct completion conv_completion; + int irq; +}; + +enum rn5t618_channels { + LIMMON = 0, + VBAT, + VADP, + VUSB, + VSYS, + VTHM, + AIN1, + AIN0 +}; + +static const struct u16_fract rn5t618_ratios[8] = { + [LIMMON] = {50, 32}, /* measured across 20mOhm, amplified by 32 */ + [VBAT] = {2, 1}, + [VADP] = {3, 1}, + [VUSB] = {3, 1}, + [VSYS] = {3, 1}, + [VTHM] = {1, 1}, + [AIN1] = {1, 1}, + [AIN0] = {1, 1}, +}; + +static int rn5t618_read_adc_reg(struct rn5t618 *rn5t618, int reg, u16 *val) +{ + u8 data[2]; + int ret; + + ret = regmap_bulk_read(rn5t618->regmap, reg, data, sizeof(data)); + if (ret < 0) + return ret; + + *val = (data[0] << 4) | (data[1] & 0xF); + + return 0; +} + +static irqreturn_t rn5t618_adc_irq(int irq, void *data) +{ + struct rn5t618_adc_data *adc = data; + unsigned int r = 0; + int ret; + + /* clear low & high threshold irqs */ + regmap_write(adc->rn5t618->regmap, RN5T618_IR_ADC1, 0); + regmap_write(adc->rn5t618->regmap, RN5T618_IR_ADC2, 0); + + ret = regmap_read(adc->rn5t618->regmap, RN5T618_IR_ADC3, &r); + if (ret < 0) + dev_err(adc->dev, "failed to read IRQ status: %d\n", ret); + + regmap_write(adc->rn5t618->regmap, RN5T618_IR_ADC3, 0); + + if (r & RN5T618_ADCEND_IRQ) + complete(&adc->conv_completion); + + return IRQ_HANDLED; +} + +static int rn5t618_adc_read(struct iio_dev *iio_dev, + const struct iio_chan_spec *chan, + int *val, int *val2, long mask) +{ + struct rn5t618_adc_data *adc = iio_priv(iio_dev); + u16 raw; + int ret; + + if (mask == IIO_CHAN_INFO_SCALE) { + *val = RN5T618_REFERENCE_VOLT * + rn5t618_ratios[chan->channel].numerator; + *val2 = rn5t618_ratios[chan->channel].denominator * 4095; + + return IIO_VAL_FRACTIONAL; + } + + /* select channel */ + ret = regmap_update_bits(adc->rn5t618->regmap, RN5T618_ADCCNT3, + RN5T618_ADCCNT3_CHANNEL_MASK, + chan->channel); + if (ret < 0) + return ret; + + ret = regmap_write(adc->rn5t618->regmap, RN5T618_EN_ADCIR3, + RN5T618_ADCEND_IRQ); + if (ret < 0) + return ret; + + ret = regmap_update_bits(adc->rn5t618->regmap, RN5T618_ADCCNT3, + RN5T618_ADCCNT3_AVG, + mask == IIO_CHAN_INFO_AVERAGE_RAW ? + RN5T618_ADCCNT3_AVG : 0); + if (ret < 0) + return ret; + + init_completion(&adc->conv_completion); + /* single conversion */ + ret = regmap_set_bits(adc->rn5t618->regmap, RN5T618_ADCCNT3, + RN5T618_ADCCNT3_GODONE); + if (ret < 0) + return ret; + + ret = wait_for_completion_timeout(&adc->conv_completion, + RN5T618_ADC_CONVERSION_TIMEOUT); + if (ret == 0) { + dev_warn(adc->dev, "timeout waiting for adc result\n"); + return -ETIMEDOUT; + } + + ret = rn5t618_read_adc_reg(adc->rn5t618, + RN5T618_ILIMDATAH + 2 * chan->channel, + &raw); + if (ret < 0) + return ret; + + *val = raw; + + return IIO_VAL_INT; +} + +static const struct iio_info rn5t618_adc_iio_info = { + .read_raw = &rn5t618_adc_read, +}; + +#define RN5T618_ADC_CHANNEL(_channel, _type, _name) { \ + .type = _type, \ + .channel = _channel, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ + BIT(IIO_CHAN_INFO_AVERAGE_RAW) | \ + BIT(IIO_CHAN_INFO_SCALE), \ + .datasheet_name = _name, \ + .indexed = 1. \ +} + +static const struct iio_chan_spec rn5t618_adc_iio_channels[] = { + RN5T618_ADC_CHANNEL(LIMMON, IIO_CURRENT, "LIMMON"), + RN5T618_ADC_CHANNEL(VBAT, IIO_VOLTAGE, "VBAT"), + RN5T618_ADC_CHANNEL(VADP, IIO_VOLTAGE, "VADP"), + RN5T618_ADC_CHANNEL(VUSB, IIO_VOLTAGE, "VUSB"), + RN5T618_ADC_CHANNEL(VSYS, IIO_VOLTAGE, "VSYS"), + RN5T618_ADC_CHANNEL(VTHM, IIO_VOLTAGE, "VTHM"), + RN5T618_ADC_CHANNEL(AIN1, IIO_VOLTAGE, "AIN1"), + RN5T618_ADC_CHANNEL(AIN0, IIO_VOLTAGE, "AIN0") +}; + +static const struct iio_map rn5t618_maps[] = { + IIO_MAP("VADP", "rn5t618-power", "vadp"), + IIO_MAP("VUSB", "rn5t618-power", "vusb"), + { } +}; + +static int rn5t618_adc_probe(struct platform_device *pdev) +{ + int ret; + struct iio_dev *iio_dev; + struct rn5t618_adc_data *adc; + struct rn5t618 *rn5t618 = dev_get_drvdata(pdev->dev.parent); + + iio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*adc)); + if (!iio_dev) + return -ENOMEM; + + adc = iio_priv(iio_dev); + adc->dev = &pdev->dev; + adc->rn5t618 = rn5t618; + + if (rn5t618->irq_data) + adc->irq = regmap_irq_get_virq(rn5t618->irq_data, + RN5T618_IRQ_ADC); + + if (adc->irq <= 0) { + dev_err(&pdev->dev, "get virq failed\n"); + return -EINVAL; + } + + init_completion(&adc->conv_completion); + + iio_dev->name = dev_name(&pdev->dev); + iio_dev->info = &rn5t618_adc_iio_info; + iio_dev->modes = INDIO_DIRECT_MODE; + iio_dev->channels = rn5t618_adc_iio_channels; + iio_dev->num_channels = ARRAY_SIZE(rn5t618_adc_iio_channels); + + /* stop any auto-conversion */ + ret = regmap_write(rn5t618->regmap, RN5T618_ADCCNT3, 0); + if (ret < 0) + return ret; + + platform_set_drvdata(pdev, iio_dev); + + ret = devm_request_threaded_irq(adc->dev, adc->irq, NULL, + rn5t618_adc_irq, + IRQF_ONESHOT, dev_name(adc->dev), + adc); + if (ret < 0) { + dev_err(adc->dev, "request irq %d failed: %d\n", adc->irq, ret); + return ret; + } + + ret = devm_iio_map_array_register(adc->dev, iio_dev, rn5t618_maps); + if (ret < 0) + return ret; + + return devm_iio_device_register(adc->dev, iio_dev); +} + +static struct platform_driver rn5t618_adc_driver = { + .driver = { + .name = "rn5t618-adc", + }, + .probe = rn5t618_adc_probe, +}; + +module_platform_driver(rn5t618_adc_driver); +MODULE_ALIAS("platform:rn5t618-adc"); +MODULE_DESCRIPTION("RICOH RN5T618 ADC driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/iio/adc/rockchip_saradc.c b/drivers/iio/adc/rockchip_saradc.c new file mode 100644 index 000000000..0f0bf2906 --- /dev/null +++ b/drivers/iio/adc/rockchip_saradc.c @@ -0,0 +1,626 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Rockchip Successive Approximation Register (SAR) A/D Converter + * Copyright (C) 2014 Rockchip Electronics Co., Ltd. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define SARADC_DATA 0x00 + +#define SARADC_STAS 0x04 +#define SARADC_STAS_BUSY BIT(0) + +#define SARADC_CTRL 0x08 +#define SARADC_CTRL_IRQ_STATUS BIT(6) +#define SARADC_CTRL_IRQ_ENABLE BIT(5) +#define SARADC_CTRL_POWER_CTRL BIT(3) +#define SARADC_CTRL_CHN_MASK 0x7 + +#define SARADC_DLY_PU_SOC 0x0c +#define SARADC_DLY_PU_SOC_MASK 0x3f + +#define SARADC_TIMEOUT msecs_to_jiffies(100) +#define SARADC_MAX_CHANNELS 8 + +/* v2 registers */ +#define SARADC2_CONV_CON 0x000 +#define SARADC_T_PD_SOC 0x004 +#define SARADC_T_DAS_SOC 0x00c +#define SARADC2_END_INT_EN 0x104 +#define SARADC2_ST_CON 0x108 +#define SARADC2_STATUS 0x10c +#define SARADC2_END_INT_ST 0x110 +#define SARADC2_DATA_BASE 0x120 + +#define SARADC2_EN_END_INT BIT(0) +#define SARADC2_START BIT(4) +#define SARADC2_SINGLE_MODE BIT(5) + +#define SARADC2_CONV_CHANNELS GENMASK(3, 0) + +struct rockchip_saradc; + +struct rockchip_saradc_data { + const struct iio_chan_spec *channels; + int num_channels; + unsigned long clk_rate; + void (*start)(struct rockchip_saradc *info, int chn); + int (*read)(struct rockchip_saradc *info); + void (*power_down)(struct rockchip_saradc *info); +}; + +struct rockchip_saradc { + void __iomem *regs; + struct clk *pclk; + struct clk *clk; + struct completion completion; + struct regulator *vref; + /* lock to protect against multiple access to the device */ + struct mutex lock; + int uv_vref; + struct reset_control *reset; + const struct rockchip_saradc_data *data; + u16 last_val; + const struct iio_chan_spec *last_chan; + struct notifier_block nb; +}; + +static void rockchip_saradc_reset_controller(struct reset_control *reset); + +static void rockchip_saradc_start_v1(struct rockchip_saradc *info, int chn) +{ + /* 8 clock periods as delay between power up and start cmd */ + writel_relaxed(8, info->regs + SARADC_DLY_PU_SOC); + /* Select the channel to be used and trigger conversion */ + writel(SARADC_CTRL_POWER_CTRL | (chn & SARADC_CTRL_CHN_MASK) | + SARADC_CTRL_IRQ_ENABLE, info->regs + SARADC_CTRL); +} + +static void rockchip_saradc_start_v2(struct rockchip_saradc *info, int chn) +{ + int val; + + if (info->reset) + rockchip_saradc_reset_controller(info->reset); + + writel_relaxed(0xc, info->regs + SARADC_T_DAS_SOC); + writel_relaxed(0x20, info->regs + SARADC_T_PD_SOC); + val = FIELD_PREP(SARADC2_EN_END_INT, 1); + val |= SARADC2_EN_END_INT << 16; + writel_relaxed(val, info->regs + SARADC2_END_INT_EN); + val = FIELD_PREP(SARADC2_START, 1) | + FIELD_PREP(SARADC2_SINGLE_MODE, 1) | + FIELD_PREP(SARADC2_CONV_CHANNELS, chn); + val |= (SARADC2_START | SARADC2_SINGLE_MODE | SARADC2_CONV_CHANNELS) << 16; + writel(val, info->regs + SARADC2_CONV_CON); +} + +static void rockchip_saradc_start(struct rockchip_saradc *info, int chn) +{ + info->data->start(info, chn); +} + +static int rockchip_saradc_read_v1(struct rockchip_saradc *info) +{ + return readl_relaxed(info->regs + SARADC_DATA); +} + +static int rockchip_saradc_read_v2(struct rockchip_saradc *info) +{ + int offset; + + /* Clear irq */ + writel_relaxed(0x1, info->regs + SARADC2_END_INT_ST); + + offset = SARADC2_DATA_BASE + info->last_chan->channel * 0x4; + + return readl_relaxed(info->regs + offset); +} + +static int rockchip_saradc_read(struct rockchip_saradc *info) +{ + return info->data->read(info); +} + +static void rockchip_saradc_power_down_v1(struct rockchip_saradc *info) +{ + writel_relaxed(0, info->regs + SARADC_CTRL); +} + +static void rockchip_saradc_power_down(struct rockchip_saradc *info) +{ + if (info->data->power_down) + info->data->power_down(info); +} + +static int rockchip_saradc_conversion(struct rockchip_saradc *info, + struct iio_chan_spec const *chan) +{ + reinit_completion(&info->completion); + + info->last_chan = chan; + rockchip_saradc_start(info, chan->channel); + + if (!wait_for_completion_timeout(&info->completion, SARADC_TIMEOUT)) + return -ETIMEDOUT; + + return 0; +} + +static int rockchip_saradc_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct rockchip_saradc *info = iio_priv(indio_dev); + int ret; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + mutex_lock(&info->lock); + + ret = rockchip_saradc_conversion(info, chan); + if (ret) { + rockchip_saradc_power_down(info); + mutex_unlock(&info->lock); + return ret; + } + + *val = info->last_val; + mutex_unlock(&info->lock); + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + *val = info->uv_vref / 1000; + *val2 = chan->scan_type.realbits; + return IIO_VAL_FRACTIONAL_LOG2; + default: + return -EINVAL; + } +} + +static irqreturn_t rockchip_saradc_isr(int irq, void *dev_id) +{ + struct rockchip_saradc *info = dev_id; + + /* Read value */ + info->last_val = rockchip_saradc_read(info); + info->last_val &= GENMASK(info->last_chan->scan_type.realbits - 1, 0); + + rockchip_saradc_power_down(info); + + complete(&info->completion); + + return IRQ_HANDLED; +} + +static const struct iio_info rockchip_saradc_iio_info = { + .read_raw = rockchip_saradc_read_raw, +}; + +#define SARADC_CHANNEL(_index, _id, _res) { \ + .type = IIO_VOLTAGE, \ + .indexed = 1, \ + .channel = _index, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ + .datasheet_name = _id, \ + .scan_index = _index, \ + .scan_type = { \ + .sign = 'u', \ + .realbits = _res, \ + .storagebits = 16, \ + .endianness = IIO_CPU, \ + }, \ +} + +static const struct iio_chan_spec rockchip_saradc_iio_channels[] = { + SARADC_CHANNEL(0, "adc0", 10), + SARADC_CHANNEL(1, "adc1", 10), + SARADC_CHANNEL(2, "adc2", 10), +}; + +static const struct rockchip_saradc_data saradc_data = { + .channels = rockchip_saradc_iio_channels, + .num_channels = ARRAY_SIZE(rockchip_saradc_iio_channels), + .clk_rate = 1000000, + .start = rockchip_saradc_start_v1, + .read = rockchip_saradc_read_v1, + .power_down = rockchip_saradc_power_down_v1, +}; + +static const struct iio_chan_spec rockchip_rk3066_tsadc_iio_channels[] = { + SARADC_CHANNEL(0, "adc0", 12), + SARADC_CHANNEL(1, "adc1", 12), +}; + +static const struct rockchip_saradc_data rk3066_tsadc_data = { + .channels = rockchip_rk3066_tsadc_iio_channels, + .num_channels = ARRAY_SIZE(rockchip_rk3066_tsadc_iio_channels), + .clk_rate = 50000, + .start = rockchip_saradc_start_v1, + .read = rockchip_saradc_read_v1, + .power_down = rockchip_saradc_power_down_v1, +}; + +static const struct iio_chan_spec rockchip_rk3399_saradc_iio_channels[] = { + SARADC_CHANNEL(0, "adc0", 10), + SARADC_CHANNEL(1, "adc1", 10), + SARADC_CHANNEL(2, "adc2", 10), + SARADC_CHANNEL(3, "adc3", 10), + SARADC_CHANNEL(4, "adc4", 10), + SARADC_CHANNEL(5, "adc5", 10), +}; + +static const struct rockchip_saradc_data rk3399_saradc_data = { + .channels = rockchip_rk3399_saradc_iio_channels, + .num_channels = ARRAY_SIZE(rockchip_rk3399_saradc_iio_channels), + .clk_rate = 1000000, + .start = rockchip_saradc_start_v1, + .read = rockchip_saradc_read_v1, + .power_down = rockchip_saradc_power_down_v1, +}; + +static const struct iio_chan_spec rockchip_rk3528_saradc_iio_channels[] = { + SARADC_CHANNEL(0, "adc0", 10), + SARADC_CHANNEL(1, "adc1", 10), + SARADC_CHANNEL(2, "adc2", 10), + SARADC_CHANNEL(3, "adc3", 10), +}; + +static const struct rockchip_saradc_data rk3528_saradc_data = { + .channels = rockchip_rk3528_saradc_iio_channels, + .num_channels = ARRAY_SIZE(rockchip_rk3528_saradc_iio_channels), + .clk_rate = 1000000, + .start = rockchip_saradc_start_v2, + .read = rockchip_saradc_read_v2, +}; + +static const struct iio_chan_spec rockchip_rk3562_saradc_iio_channels[] = { + SARADC_CHANNEL(0, "adc0", 10), + SARADC_CHANNEL(1, "adc1", 10), + SARADC_CHANNEL(2, "adc2", 10), + SARADC_CHANNEL(3, "adc3", 10), + SARADC_CHANNEL(4, "adc4", 10), + SARADC_CHANNEL(5, "adc5", 10), + SARADC_CHANNEL(6, "adc6", 10), + SARADC_CHANNEL(7, "adc7", 10), +}; + +static const struct rockchip_saradc_data rk3562_saradc_data = { + .channels = rockchip_rk3562_saradc_iio_channels, + .num_channels = ARRAY_SIZE(rockchip_rk3562_saradc_iio_channels), + .clk_rate = 1000000, + .start = rockchip_saradc_start_v2, + .read = rockchip_saradc_read_v2, +}; + +static const struct iio_chan_spec rockchip_rk3568_saradc_iio_channels[] = { + SARADC_CHANNEL(0, "adc0", 10), + SARADC_CHANNEL(1, "adc1", 10), + SARADC_CHANNEL(2, "adc2", 10), + SARADC_CHANNEL(3, "adc3", 10), + SARADC_CHANNEL(4, "adc4", 10), + SARADC_CHANNEL(5, "adc5", 10), + SARADC_CHANNEL(6, "adc6", 10), + SARADC_CHANNEL(7, "adc7", 10), +}; + +static const struct rockchip_saradc_data rk3568_saradc_data = { + .channels = rockchip_rk3568_saradc_iio_channels, + .num_channels = ARRAY_SIZE(rockchip_rk3568_saradc_iio_channels), + .clk_rate = 1000000, + .start = rockchip_saradc_start_v1, + .read = rockchip_saradc_read_v1, + .power_down = rockchip_saradc_power_down_v1, +}; + +static const struct iio_chan_spec rockchip_rk3588_saradc_iio_channels[] = { + SARADC_CHANNEL(0, "adc0", 12), + SARADC_CHANNEL(1, "adc1", 12), + SARADC_CHANNEL(2, "adc2", 12), + SARADC_CHANNEL(3, "adc3", 12), + SARADC_CHANNEL(4, "adc4", 12), + SARADC_CHANNEL(5, "adc5", 12), + SARADC_CHANNEL(6, "adc6", 12), + SARADC_CHANNEL(7, "adc7", 12), +}; + +static const struct rockchip_saradc_data rk3588_saradc_data = { + .channels = rockchip_rk3588_saradc_iio_channels, + .num_channels = ARRAY_SIZE(rockchip_rk3588_saradc_iio_channels), + .clk_rate = 1000000, + .start = rockchip_saradc_start_v2, + .read = rockchip_saradc_read_v2, +}; + +static const struct of_device_id rockchip_saradc_match[] = { + { + .compatible = "rockchip,saradc", + .data = &saradc_data, + }, { + .compatible = "rockchip,rk3066-tsadc", + .data = &rk3066_tsadc_data, + }, { + .compatible = "rockchip,rk3399-saradc", + .data = &rk3399_saradc_data, + }, { + .compatible = "rockchip,rk3528-saradc", + .data = &rk3528_saradc_data, + }, { + .compatible = "rockchip,rk3562-saradc", + .data = &rk3562_saradc_data, + }, { + .compatible = "rockchip,rk3568-saradc", + .data = &rk3568_saradc_data, + }, { + .compatible = "rockchip,rk3588-saradc", + .data = &rk3588_saradc_data, + }, + { } +}; +MODULE_DEVICE_TABLE(of, rockchip_saradc_match); + +/* + * Reset SARADC Controller. + */ +static void rockchip_saradc_reset_controller(struct reset_control *reset) +{ + reset_control_assert(reset); + usleep_range(10, 20); + reset_control_deassert(reset); +} + +static void rockchip_saradc_regulator_disable(void *data) +{ + struct rockchip_saradc *info = data; + + regulator_disable(info->vref); +} + +static irqreturn_t rockchip_saradc_trigger_handler(int irq, void *p) +{ + struct iio_poll_func *pf = p; + struct iio_dev *i_dev = pf->indio_dev; + struct rockchip_saradc *info = iio_priv(i_dev); + /* + * @values: each channel takes an u16 value + * @timestamp: will be 8-byte aligned automatically + */ + struct { + u16 values[SARADC_MAX_CHANNELS]; + aligned_s64 timestamp; + } data = { }; + int ret; + int i, j = 0; + + mutex_lock(&info->lock); + + iio_for_each_active_channel(i_dev, i) { + const struct iio_chan_spec *chan = &i_dev->channels[i]; + + ret = rockchip_saradc_conversion(info, chan); + if (ret) { + rockchip_saradc_power_down(info); + goto out; + } + + data.values[j] = info->last_val; + j++; + } + + iio_push_to_buffers_with_ts(i_dev, &data, sizeof(data), + iio_get_time_ns(i_dev)); +out: + mutex_unlock(&info->lock); + + iio_trigger_notify_done(i_dev->trig); + + return IRQ_HANDLED; +} + +static int rockchip_saradc_volt_notify(struct notifier_block *nb, + unsigned long event, void *data) +{ + struct rockchip_saradc *info = + container_of(nb, struct rockchip_saradc, nb); + + if (event & REGULATOR_EVENT_VOLTAGE_CHANGE) + info->uv_vref = (unsigned long)data; + + return NOTIFY_OK; +} + +static void rockchip_saradc_regulator_unreg_notifier(void *data) +{ + struct rockchip_saradc *info = data; + + regulator_unregister_notifier(info->vref, &info->nb); +} + +static int rockchip_saradc_probe(struct platform_device *pdev) +{ + const struct rockchip_saradc_data *match_data; + struct rockchip_saradc *info = NULL; + struct device *dev = &pdev->dev; + struct device_node *np = pdev->dev.of_node; + struct iio_dev *indio_dev = NULL; + int ret; + int irq; + + if (!np) + return -ENODEV; + + indio_dev = devm_iio_device_alloc(dev, sizeof(*info)); + if (!indio_dev) + return -ENOMEM; + + info = iio_priv(indio_dev); + + match_data = of_device_get_match_data(dev); + if (!match_data) + return dev_err_probe(dev, -ENODEV, "failed to match device\n"); + + info->data = match_data; + + /* Sanity check for possible later IP variants with more channels */ + if (info->data->num_channels > SARADC_MAX_CHANNELS) + return dev_err_probe(dev, -EINVAL, "max channels exceeded"); + + info->regs = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(info->regs)) + return PTR_ERR(info->regs); + + /* + * The reset should be an optional property, as it should work + * with old devicetrees as well + */ + info->reset = devm_reset_control_get_optional_exclusive(dev, "saradc-apb"); + if (IS_ERR(info->reset)) + return dev_err_probe(dev, PTR_ERR(info->reset), + "failed to get saradc-apb\n"); + + init_completion(&info->completion); + + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return irq; + + ret = devm_request_irq(dev, irq, rockchip_saradc_isr, + 0, dev_name(&pdev->dev), info); + if (ret < 0) + return dev_err_probe(dev, ret, "failed requesting irq %d\n", irq); + + info->vref = devm_regulator_get(dev, "vref"); + if (IS_ERR(info->vref)) + return dev_err_probe(dev, PTR_ERR(info->vref), + "failed to get regulator\n"); + + if (info->reset) + rockchip_saradc_reset_controller(info->reset); + + ret = regulator_enable(info->vref); + if (ret < 0) + return dev_err_probe(dev, ret, "failed to enable vref regulator\n"); + + ret = devm_add_action_or_reset(dev, rockchip_saradc_regulator_disable, info); + if (ret) + return ret; + + ret = regulator_get_voltage(info->vref); + if (ret < 0) + return ret; + + info->uv_vref = ret; + + info->pclk = devm_clk_get_enabled(dev, "apb_pclk"); + if (IS_ERR(info->pclk)) + return dev_err_probe(dev, PTR_ERR(info->pclk), "failed to get pclk\n"); + + info->clk = devm_clk_get_enabled(dev, "saradc"); + if (IS_ERR(info->clk)) + return dev_err_probe(dev, PTR_ERR(info->clk), + "failed to get adc clock\n"); + /* + * Use a default value for the converter clock. + * This may become user-configurable in the future. + */ + ret = clk_set_rate(info->clk, info->data->clk_rate); + if (ret < 0) + return dev_err_probe(dev, ret, "failed to set adc clk rate\n"); + + platform_set_drvdata(pdev, indio_dev); + + indio_dev->name = dev_name(dev); + indio_dev->info = &rockchip_saradc_iio_info; + indio_dev->modes = INDIO_DIRECT_MODE; + + indio_dev->channels = info->data->channels; + indio_dev->num_channels = info->data->num_channels; + ret = devm_iio_triggered_buffer_setup(dev, indio_dev, NULL, + rockchip_saradc_trigger_handler, + NULL); + if (ret) + return ret; + + info->nb.notifier_call = rockchip_saradc_volt_notify; + ret = regulator_register_notifier(info->vref, &info->nb); + if (ret) + return ret; + + ret = devm_add_action_or_reset(dev, + rockchip_saradc_regulator_unreg_notifier, + info); + if (ret) + return ret; + + mutex_init(&info->lock); + + return devm_iio_device_register(dev, indio_dev); +} + +static int rockchip_saradc_suspend(struct device *dev) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct rockchip_saradc *info = iio_priv(indio_dev); + + clk_disable_unprepare(info->clk); + clk_disable_unprepare(info->pclk); + regulator_disable(info->vref); + + return 0; +} + +static int rockchip_saradc_resume(struct device *dev) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct rockchip_saradc *info = iio_priv(indio_dev); + int ret; + + ret = regulator_enable(info->vref); + if (ret) + return ret; + + ret = clk_prepare_enable(info->pclk); + if (ret) + return ret; + + ret = clk_prepare_enable(info->clk); + if (ret) + clk_disable_unprepare(info->pclk); + + return ret; +} + +static DEFINE_SIMPLE_DEV_PM_OPS(rockchip_saradc_pm_ops, + rockchip_saradc_suspend, + rockchip_saradc_resume); + +static struct platform_driver rockchip_saradc_driver = { + .probe = rockchip_saradc_probe, + .driver = { + .name = "rockchip-saradc", + .of_match_table = rockchip_saradc_match, + .pm = pm_sleep_ptr(&rockchip_saradc_pm_ops), + }, +}; + +module_platform_driver(rockchip_saradc_driver); + +MODULE_AUTHOR("Heiko Stuebner "); +MODULE_DESCRIPTION("Rockchip SARADC driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/adc/rohm-bd79112.c b/drivers/iio/adc/rohm-bd79112.c new file mode 100644 index 000000000..2506fec7b --- /dev/null +++ b/drivers/iio/adc/rohm-bd79112.c @@ -0,0 +1,550 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * ROHM ADC driver for BD79112 signal monitoring hub. + * Copyright (C) 2025, ROHM Semiconductor. + * + * SPI communication derived from ad7923.c and ti-ads7950.c + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#define BD79112_MAX_NUM_CHANNELS 32 + +struct bd79112_data { + struct spi_device *spi; + struct regmap *map; + struct device *dev; + struct gpio_chip gc; + unsigned long gpio_valid_mask; + unsigned int vref_mv; + struct spi_transfer read_xfer[2]; + struct spi_transfer write_xfer; + struct spi_message read_msg; + struct spi_message write_msg; + /* 16-bit TX, valid data in high byte */ + u8 read_tx[2] __aligned(IIO_DMA_MINALIGN); + /* 8-bit address followed by 8-bit data */ + u8 reg_write_tx[2]; + /* 12-bit of ADC data or 8 bit of reg data */ + __be16 read_rx; +}; + +/* + * The ADC data is read issuing SPI-command matching the channel number. + * We treat this as a register address. + */ +#define BD79112_REG_AGIO0A 0x00 +#define BD79112_REG_AGIO15B 0x1f + +/* + * ADC STATUS_FLAG appended to ADC data will be set, if the ADC result is being + * read for a channel, which input pin is muxed to be a GPIO. + */ +#define BD79112_ADC_STATUS_FLAG BIT(14) + +/* + * The BD79112 requires "R/W bit" to be set for SPI register (not ADC data) + * reads and an "IOSET bit" to be set for read/write operations (which aren't + * reading the ADC data). + */ +#define BD79112_BIT_RW BIT(4) +#define BD79112_BIT_IO BIT(5) + +#define BD79112_REG_GPI_VALUE_B8_15 (BD79112_BIT_IO | 0x0) +#define BD79112_REG_GPI_VALUE_B0_B7 (BD79112_BIT_IO | 0x1) +#define BD79112_REG_GPI_VALUE_A8_15 (BD79112_BIT_IO | 0x2) +#define BD79112_REG_GPI_VALUE_A0_A7 (BD79112_BIT_IO | 0x3) + +#define BD79112_REG_GPI_EN_B7_B15 (BD79112_BIT_IO | 0x4) +#define BD79112_REG_GPI_EN_B0_B7 (BD79112_BIT_IO | 0x5) +#define BD79112_REG_GPI_EN_A8_A15 (BD79112_BIT_IO | 0x6) +#define BD79112_REG_GPI_EN_A0_A7 (BD79112_BIT_IO | 0x7) + +#define BD79112_REG_GPO_EN_B7_B15 (BD79112_BIT_IO | 0x8) +#define BD79112_REG_GPO_EN_B0_B7 (BD79112_BIT_IO | 0x9) +#define BD79112_REG_GPO_EN_A8_A15 (BD79112_BIT_IO | 0xa) +#define BD79112_REG_GPO_EN_A0_A7 (BD79112_BIT_IO | 0xb) + +#define BD79112_NUM_GPIO_EN_REGS 8 +#define BD79112_FIRST_GPIO_EN_REG BD79112_REG_GPI_EN_B7_B15 + +#define BD79112_REG_GPO_VALUE_B8_15 (BD79112_BIT_IO | 0xc) +#define BD79112_REG_GPO_VALUE_B0_B7 (BD79112_BIT_IO | 0xd) +#define BD79112_REG_GPO_VALUE_A8_15 (BD79112_BIT_IO | 0xe) +#define BD79112_REG_GPO_VALUE_A0_A7 (BD79112_BIT_IO | 0xf) + +#define BD79112_REG_MAX BD79112_REG_GPO_VALUE_A0_A7 + +/* + * Read transaction consists of two 16-bit sequences separated by CSB. + * For register read, 'IOSET' bit must be set. For ADC read, IOSET is cleared + * and ADDR equals the channel number (0 ... 31). + * + * First 16-bit sequence, MOSI as below, MISO data ignored: + * - SCK: | 1 | 2 | 3 | 4 | 5 .. 8 | 9 .. 16 | + * - MOSI:| 0 | 0 | IOSET | RW (1) | ADDR | 8'b0 | + * + * CSB released and re-acquired between these sequences + * + * Second 16-bit sequence, MISO as below, MOSI data ignored: + * For Register read data is 8 bits: + * - SCK: | 1 .. 8 | 9 .. 16 | + * - MISO:| 8'b0 | 8-bit data | + * + * For ADC read data is 12 bits: + * - SCK: | 1 | 2 | 3 4 | 4 .. 16 | + * - MISO:| 0 | STATUS_FLAG | 2'b0 | 12-bit data | + * The 'STATUS_FLAG' is set if the read input pin was configured as a GPIO. + */ +static int bd79112_reg_read(void *context, unsigned int reg, unsigned int *val) +{ + struct bd79112_data *data = context; + int ret; + + if (reg & BD79112_BIT_IO) + reg |= BD79112_BIT_RW; + + data->read_tx[0] = reg; + + ret = spi_sync(data->spi, &data->read_msg); + if (!ret) + *val = be16_to_cpu(data->read_rx); + + return ret; +} + +/* + * Write, single 16-bit sequence (broken down below): + * + * First 8-bit, MOSI as below, MISO data ignored: + * - SCK: | 1 | 2 | 3 | 4 | 5 .. 8 | + * - MOSI:| 0 | 0 |IOSET| RW(0) | ADDR | + * + * Last 8 SCK cycles (b8 ... b15), MISO contains register data, MOSI ignored. + * - SCK: | 9 .. 16 | + * - MISO:| data | + */ +static int bd79112_reg_write(void *context, unsigned int reg, unsigned int val) +{ + struct bd79112_data *data = context; + + data->reg_write_tx[0] = reg; + data->reg_write_tx[1] = val; + + return spi_sync(data->spi, &data->write_msg); +} + +static int _get_gpio_reg(unsigned int offset, unsigned int base) +{ + int regoffset = offset / 8; + + if (offset > 31) + return -EINVAL; + + return base - regoffset; +} + +#define GET_GPIO_BIT(offset) BIT((offset) % 8) +#define GET_GPO_EN_REG(offset) _get_gpio_reg((offset), BD79112_REG_GPO_EN_A0_A7) +#define GET_GPI_EN_REG(offset) _get_gpio_reg((offset), BD79112_REG_GPI_EN_A0_A7) +#define GET_GPO_VAL_REG(offset) _get_gpio_reg((offset), BD79112_REG_GPO_VALUE_A0_A7) +#define GET_GPI_VAL_REG(offset) _get_gpio_reg((offset), BD79112_REG_GPI_VALUE_A0_A7) + +static const struct regmap_range bd71815_volatile_ro_ranges[] = { + /* Read ADC data */ + regmap_reg_range(BD79112_REG_AGIO0A, BD79112_REG_AGIO15B), + /* GPI state */ + regmap_reg_range(BD79112_REG_GPI_VALUE_B8_15, BD79112_REG_GPI_VALUE_A0_A7), +}; + +static const struct regmap_access_table bd79112_volatile_regs = { + .yes_ranges = &bd71815_volatile_ro_ranges[0], + .n_yes_ranges = ARRAY_SIZE(bd71815_volatile_ro_ranges), +}; + +static const struct regmap_access_table bd79112_ro_regs = { + .no_ranges = &bd71815_volatile_ro_ranges[0], + .n_no_ranges = ARRAY_SIZE(bd71815_volatile_ro_ranges), +}; + +static const struct regmap_config bd79112_regmap = { + .reg_read = bd79112_reg_read, + .reg_write = bd79112_reg_write, + .volatile_table = &bd79112_volatile_regs, + .wr_table = &bd79112_ro_regs, + .cache_type = REGCACHE_MAPLE, + .max_register = BD79112_REG_MAX, +}; + +static int bd79112_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int *val, + int *val2, long m) +{ + struct bd79112_data *data = iio_priv(indio_dev); + int ret; + + switch (m) { + case IIO_CHAN_INFO_RAW: + ret = regmap_read(data->map, chan->channel, val); + if (ret < 0) + return ret; + + return IIO_VAL_INT; + + case IIO_CHAN_INFO_SCALE: + *val = data->vref_mv; + *val2 = 12; + + return IIO_VAL_FRACTIONAL_LOG2; + default: + return -EINVAL; + } +} + +static const struct iio_info bd79112_info = { + .read_raw = bd79112_read_raw, +}; + +static const struct iio_chan_spec bd79112_chan_template = { + .type = IIO_VOLTAGE, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), + .indexed = 1, +}; + +static int bd79112_gpio_init_valid_mask(struct gpio_chip *gc, + unsigned long *valid_mask, + unsigned int ngpios) +{ + struct bd79112_data *data = gpiochip_get_data(gc); + + *valid_mask = data->gpio_valid_mask; + + return 0; +} + +static int bd79112_gpio_dir_get(struct gpio_chip *gc, unsigned int offset) +{ + struct bd79112_data *data = gpiochip_get_data(gc); + unsigned int reg, bit, val; + int ret; + + bit = GET_GPIO_BIT(offset); + reg = GET_GPO_EN_REG(offset); + + ret = regmap_read(data->map, reg, &val); + if (ret) + return ret; + + if (bit & val) + return GPIO_LINE_DIRECTION_OUT; + + reg = GET_GPI_EN_REG(offset); + ret = regmap_read(data->map, reg, &val); + if (ret) + return ret; + + if (bit & val) + return GPIO_LINE_DIRECTION_IN; + + /* + * Ouch. Seems the pin is ADC input - shouldn't happen as changing mux + * at runtime is not supported and non GPIO pins should be invalidated + * by the valid_mask at probe. Maybe someone wrote a register bypassing + * the driver? + */ + dev_err(data->dev, "Pin not a GPIO\n"); + + return -EINVAL; +} + +static int bd79112_gpio_get(struct gpio_chip *gc, unsigned int offset) +{ + struct bd79112_data *data = gpiochip_get_data(gc); + unsigned int reg, bit, val; + int ret; + + bit = GET_GPIO_BIT(offset); + reg = GET_GPI_VAL_REG(offset); + + ret = regmap_read(data->map, reg, &val); + if (ret) + return ret; + + return !!(val & bit); +} + +static int bd79112_gpio_set(struct gpio_chip *gc, unsigned int offset, + int value) +{ + struct bd79112_data *data = gpiochip_get_data(gc); + unsigned int reg, bit; + + bit = GET_GPIO_BIT(offset); + reg = GET_GPO_VAL_REG(offset); + + return regmap_assign_bits(data->map, reg, bit, value); +} + +static int bd79112_gpio_set_multiple(struct gpio_chip *gc, unsigned long *mask, + unsigned long *bits) +{ + struct bd79112_data *data = gpiochip_get_data(gc); + unsigned long i, bank_mask; + + for_each_set_clump8(i, bank_mask, mask, gc->ngpio) { + unsigned long bank_bits; + unsigned int reg; + int ret; + + bank_bits = bitmap_get_value8(bits, i); + reg = BD79112_REG_GPO_VALUE_A0_A7 - i / 8; + ret = regmap_update_bits(data->map, reg, bank_mask, bank_bits); + if (ret) + return ret; + } + + return 0; +} + +static int bd79112_gpio_dir_set(struct bd79112_data *data, unsigned int offset, + int dir) +{ + unsigned int gpi_reg, gpo_reg, bit; + int ret; + + bit = GET_GPIO_BIT(offset); + gpi_reg = GET_GPI_EN_REG(offset); + gpo_reg = GET_GPO_EN_REG(offset); + + if (dir == GPIO_LINE_DIRECTION_OUT) { + ret = regmap_clear_bits(data->map, gpi_reg, bit); + if (ret) + return ret; + + return regmap_set_bits(data->map, gpo_reg, bit); + } + + ret = regmap_set_bits(data->map, gpi_reg, bit); + if (ret) + return ret; + + return regmap_clear_bits(data->map, gpo_reg, bit); +} + +static int bd79112_gpio_input(struct gpio_chip *gc, unsigned int offset) +{ + struct bd79112_data *data = gpiochip_get_data(gc); + + return bd79112_gpio_dir_set(data, offset, GPIO_LINE_DIRECTION_IN); +} + +static int bd79112_gpio_output(struct gpio_chip *gc, unsigned int offset, + int value) +{ + struct bd79112_data *data = gpiochip_get_data(gc); + int ret; + + ret = bd79112_gpio_set(gc, offset, value); + if (ret) + return ret; + + return bd79112_gpio_dir_set(data, offset, GPIO_LINE_DIRECTION_OUT); +} + +static const struct gpio_chip bd79112_gpio_chip = { + .label = "bd79112-gpio", + .get_direction = bd79112_gpio_dir_get, + .direction_input = bd79112_gpio_input, + .direction_output = bd79112_gpio_output, + .get = bd79112_gpio_get, + .set = bd79112_gpio_set, + .set_multiple = bd79112_gpio_set_multiple, + .init_valid_mask = bd79112_gpio_init_valid_mask, + .can_sleep = true, + .ngpio = 32, + .base = -1, +}; + +static unsigned int bd79112_get_gpio_pins(const struct iio_chan_spec *cs, int num_channels) +{ + unsigned int i, gpio_channels; + + /* + * Let's initialize the mux config to say that all 32 channels are + * GPIOs. Then we can just loop through the iio_chan_spec and clear the + * bits for found ADC channels. + */ + gpio_channels = GENMASK(31, 0); + for (i = 0; i < num_channels; i++) + gpio_channels &= ~BIT(cs[i].channel); + + return gpio_channels; +} + +/* ADC channels as named in the data-sheet */ +static const char * const bd79112_chan_names[] = { + "AGIO0A", "AGIO1A", "AGIO2A", "AGIO3A", /* 0 - 3 */ + "AGIO4A", "AGIO5A", "AGIO6A", "AGIO7A", /* 4 - 7 */ + "AGIO8A", "AGIO9A", "AGIO10A", "AGIO11A", /* 8 - 11 */ + "AGIO12A", "AGIO13A", "AGIO14A", "AGIO15A", /* 12 - 15 */ + "AGIO0B", "AGIO1B", "AGIO2B", "AGIO3B", /* 16 - 19 */ + "AGIO4B", "AGIO5B", "AGIO6B", "AGIO7B", /* 20 - 23 */ + "AGIO8B", "AGIO9B", "AGIO10B", "AGIO11B", /* 24 - 27 */ + "AGIO12B", "AGIO13B", "AGIO14B", "AGIO15B", /* 28 - 31 */ +}; + +static int bd79112_probe(struct spi_device *spi) +{ + struct bd79112_data *data; + struct iio_dev *iio_dev; + struct iio_chan_spec *cs; + struct device *dev = &spi->dev; + unsigned long gpio_pins, pin; + unsigned int i; + int ret; + + iio_dev = devm_iio_device_alloc(dev, sizeof(*data)); + if (!iio_dev) + return -ENOMEM; + + data = iio_priv(iio_dev); + data->spi = spi; + data->dev = dev; + data->map = devm_regmap_init(dev, NULL, data, &bd79112_regmap); + if (IS_ERR(data->map)) + return dev_err_probe(dev, PTR_ERR(data->map), + "Failed to initialize Regmap\n"); + + ret = devm_regulator_get_enable_read_voltage(dev, "vdd"); + if (ret < 0) + return dev_err_probe(dev, ret, "Failed to get the Vdd\n"); + + data->vref_mv = ret / 1000; + + ret = devm_regulator_get_enable(dev, "iovdd"); + if (ret < 0) + return dev_err_probe(dev, ret, "Failed to enable I/O voltage\n"); + + data->read_xfer[0].tx_buf = &data->read_tx[0]; + data->read_xfer[0].len = sizeof(data->read_tx); + data->read_xfer[0].cs_change = 1; + data->read_xfer[1].rx_buf = &data->read_rx; + data->read_xfer[1].len = sizeof(data->read_rx); + spi_message_init_with_transfers(&data->read_msg, data->read_xfer, 2); + ret = devm_spi_optimize_message(dev, spi, &data->read_msg); + if (ret < 0) + return dev_err_probe(dev, ret, + "Failed to optimize SPI read message\n"); + + data->write_xfer.tx_buf = &data->reg_write_tx[0]; + data->write_xfer.len = sizeof(data->reg_write_tx); + spi_message_init_with_transfers(&data->write_msg, &data->write_xfer, 1); + ret = devm_spi_optimize_message(dev, spi, &data->write_msg); + if (ret < 0) + return dev_err_probe(dev, ret, + "Failed to optimize SPI write message\n"); + + ret = devm_iio_adc_device_alloc_chaninfo_se(dev, &bd79112_chan_template, + BD79112_MAX_NUM_CHANNELS - 1, + &cs); + + /* Register all pins as GPIOs if there are no ADC channels */ + if (ret == -ENOENT) + goto register_gpios; + + if (ret < 0) + return ret; + + iio_dev->num_channels = ret; + iio_dev->channels = cs; + + for (i = 0; i < iio_dev->num_channels; i++) + cs[i].datasheet_name = bd79112_chan_names[cs[i].channel]; + + iio_dev->info = &bd79112_info; + iio_dev->name = "bd79112"; + iio_dev->modes = INDIO_DIRECT_MODE; + + /* + * Ensure all channels are ADCs. This allows us to register the IIO + * device early (before checking which pins are to be used for GPIO) + * without having to worry about some pins being initially used for + * GPIO. + */ + for (i = 0; i < BD79112_NUM_GPIO_EN_REGS; i++) { + ret = regmap_write(data->map, BD79112_FIRST_GPIO_EN_REG + i, 0); + if (ret) + return dev_err_probe(dev, ret, + "Failed to initialize channels\n"); + } + + ret = devm_iio_device_register(data->dev, iio_dev); + if (ret) + return dev_err_probe(data->dev, ret, "Failed to register ADC\n"); + +register_gpios: + gpio_pins = bd79112_get_gpio_pins(iio_dev->channels, + iio_dev->num_channels); + + /* If all channels are reserved for ADC, then we're done. */ + if (!gpio_pins) + return 0; + + /* Default all the GPIO pins to GPI */ + for_each_set_bit(pin, &gpio_pins, BD79112_MAX_NUM_CHANNELS) { + ret = bd79112_gpio_dir_set(data, pin, GPIO_LINE_DIRECTION_IN); + if (ret) + return dev_err_probe(dev, ret, + "Failed to mark pin as GPI\n"); + } + + data->gpio_valid_mask = gpio_pins; + data->gc = bd79112_gpio_chip; + data->gc.parent = dev; + + return devm_gpiochip_add_data(dev, &data->gc, data); +} + +static const struct of_device_id bd79112_of_match[] = { + { .compatible = "rohm,bd79112" }, + { } +}; +MODULE_DEVICE_TABLE(of, bd79112_of_match); + +static const struct spi_device_id bd79112_id[] = { + { .name = "bd79112" }, + { } +}; +MODULE_DEVICE_TABLE(spi, bd79112_id); + +static struct spi_driver bd79112_driver = { + .driver = { + .name = "bd79112", + .of_match_table = bd79112_of_match, + }, + .probe = bd79112_probe, + .id_table = bd79112_id, +}; +module_spi_driver(bd79112_driver); + +MODULE_AUTHOR("Matti Vaittinen "); +MODULE_DESCRIPTION("Driver for ROHM BD79112 ADC/GPIO"); +MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS("IIO_DRIVER"); diff --git a/drivers/iio/adc/rohm-bd79124.c b/drivers/iio/adc/rohm-bd79124.c new file mode 100644 index 000000000..ed5427288 --- /dev/null +++ b/drivers/iio/adc/rohm-bd79124.c @@ -0,0 +1,1124 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * ROHM ADC driver for BD79124 ADC/GPO device + * https://fscdn.rohm.com/en/products/databook/datasheet/ic/data_converter/dac/bd79124muf-c-e.pdf + * + * Copyright (c) 2025, ROHM Semiconductor. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include + +#define BD79124_I2C_MULTI_READ 0x30 +#define BD79124_I2C_MULTI_WRITE 0x28 +#define BD79124_REG_MAX 0xaf + +#define BD79124_REG_SYSTEM_STATUS 0x00 +#define BD79124_REG_GEN_CFG 0x01 +#define BD79124_REG_OPMODE_CFG 0x04 +#define BD79124_REG_PINCFG 0x05 +#define BD79124_REG_GPO_VAL 0x0B +#define BD79124_REG_SEQ_CFG 0x10 +#define BD79124_REG_MANUAL_CHANNELS 0x11 +#define BD79124_REG_AUTO_CHANNELS 0x12 +#define BD79124_REG_ALERT_CH_SEL 0x14 +#define BD79124_REG_EVENT_FLAG 0x18 +#define BD79124_REG_EVENT_FLAG_HI 0x1a +#define BD79124_REG_EVENT_FLAG_LO 0x1c +#define BD79124_REG_HYSTERESIS_CH0 0x20 +#define BD79124_REG_EVENTCOUNT_CH0 0x22 +#define BD79124_REG_RECENT_CH0_LSB 0xa0 +#define BD79124_REG_RECENT_CH7_MSB 0xaf + +#define BD79124_ADC_BITS 12 + +/* Masks for the BD79124_REG_OPMODE_CFG */ +#define BD79124_MSK_CONV_MODE GENMASK(6, 5) +#define BD79124_CONV_MODE_MANSEQ 0 +#define BD79124_CONV_MODE_AUTO 1 +#define BD79124_MSK_AUTO_INTERVAL GENMASK(1, 0) +#define BD79124_INTERVAL_750_US 0 + +/* Masks for the BD79124_REG_GEN_CFG */ +#define BD79124_MSK_DWC_EN BIT(4) +#define BD79124_MSK_STATS_EN BIT(5) + +/* Masks for the BD79124_REG_SEQ_CFG */ +#define BD79124_MSK_SEQ_START BIT(4) +#define BD79124_MSK_SEQ_MODE GENMASK(1, 0) +#define BD79124_MSK_SEQ_MANUAL 0 +#define BD79124_MSK_SEQ_SEQ 1 + +#define BD79124_MSK_HYSTERESIS GENMASK(3, 0) +#define BD79124_LOW_LIMIT_MIN 0 +#define BD79124_HIGH_LIMIT_MAX GENMASK(11, 0) + +/* + * The high limit, low limit and last measurement result are each stored in + * 2 consecutive registers. 4 bits are in the high bits of the first register + * and 8 bits in the next register. + * + * These macros return the address of the first reg for the given channel. + */ +#define BD79124_GET_HIGH_LIMIT_REG(ch) (BD79124_REG_HYSTERESIS_CH0 + (ch) * 4) +#define BD79124_GET_LOW_LIMIT_REG(ch) (BD79124_REG_EVENTCOUNT_CH0 + (ch) * 4) +#define BD79124_GET_LIMIT_REG(ch, dir) ((dir) == IIO_EV_DIR_RISING ? \ + BD79124_GET_HIGH_LIMIT_REG(ch) : BD79124_GET_LOW_LIMIT_REG(ch)) +#define BD79124_GET_RECENT_RES_REG(ch) (BD79124_REG_RECENT_CH0_LSB + (ch) * 2) + +/* + * The hysteresis for a channel is stored in the same register where the + * 4 bits of high limit reside. + */ +#define BD79124_GET_HYSTERESIS_REG(ch) BD79124_GET_HIGH_LIMIT_REG(ch) + +#define BD79124_MAX_NUM_CHANNELS 8 + +struct bd79124_data { + s64 timestamp; + struct regmap *map; + struct device *dev; + int vmax; + /* + * Keep measurement status so read_raw() knows if the measurement needs + * to be started. + */ + int alarm_monitored[BD79124_MAX_NUM_CHANNELS]; + /* + * The BD79124 does not allow disabling/enabling limit separately for + * one direction only. Hence, we do the disabling by changing the limit + * to maximum/minimum measurable value. This means we need to cache + * the limit in order to maintain it over the time limit is disabled. + */ + u16 alarm_r_limit[BD79124_MAX_NUM_CHANNELS]; + u16 alarm_f_limit[BD79124_MAX_NUM_CHANNELS]; + /* Bitmask of disabled events (for rate limiting) for each channel. */ + int alarm_suppressed[BD79124_MAX_NUM_CHANNELS]; + /* + * The BD79124 is configured to run the measurements in the background. + * This is done for the event monitoring as well as for the read_raw(). + * Protect the measurement starting/stopping using a mutex. + */ + struct mutex mutex; + struct delayed_work alm_enable_work; + struct gpio_chip gc; + u8 gpio_valid_mask; +}; + +static const struct regmap_range bd79124_ro_ranges[] = { + regmap_reg_range(BD79124_REG_EVENT_FLAG, BD79124_REG_EVENT_FLAG), + regmap_reg_range(BD79124_REG_RECENT_CH0_LSB, BD79124_REG_RECENT_CH7_MSB), +}; + +static const struct regmap_access_table bd79124_ro_regs = { + .no_ranges = &bd79124_ro_ranges[0], + .n_no_ranges = ARRAY_SIZE(bd79124_ro_ranges), +}; + +static const struct regmap_range bd79124_volatile_ranges[] = { + regmap_reg_range(BD79124_REG_RECENT_CH0_LSB, BD79124_REG_RECENT_CH7_MSB), + regmap_reg_range(BD79124_REG_EVENT_FLAG, BD79124_REG_EVENT_FLAG), + regmap_reg_range(BD79124_REG_EVENT_FLAG_HI, BD79124_REG_EVENT_FLAG_HI), + regmap_reg_range(BD79124_REG_EVENT_FLAG_LO, BD79124_REG_EVENT_FLAG_LO), + regmap_reg_range(BD79124_REG_SYSTEM_STATUS, BD79124_REG_SYSTEM_STATUS), +}; + +static const struct regmap_access_table bd79124_volatile_regs = { + .yes_ranges = &bd79124_volatile_ranges[0], + .n_yes_ranges = ARRAY_SIZE(bd79124_volatile_ranges), +}; + +static const struct regmap_range bd79124_precious_ranges[] = { + regmap_reg_range(BD79124_REG_EVENT_FLAG_HI, BD79124_REG_EVENT_FLAG_HI), + regmap_reg_range(BD79124_REG_EVENT_FLAG_LO, BD79124_REG_EVENT_FLAG_LO), +}; + +static const struct regmap_access_table bd79124_precious_regs = { + .yes_ranges = &bd79124_precious_ranges[0], + .n_yes_ranges = ARRAY_SIZE(bd79124_precious_ranges), +}; + +static const struct regmap_config bd79124_regmap = { + .reg_bits = 16, + .val_bits = 8, + .read_flag_mask = BD79124_I2C_MULTI_READ, + .write_flag_mask = BD79124_I2C_MULTI_WRITE, + .max_register = BD79124_REG_MAX, + .cache_type = REGCACHE_MAPLE, + .volatile_table = &bd79124_volatile_regs, + .wr_table = &bd79124_ro_regs, + .precious_table = &bd79124_precious_regs, +}; + +static int bd79124gpo_direction_get(struct gpio_chip *gc, unsigned int offset) +{ + return GPIO_LINE_DIRECTION_OUT; +} + +static int bd79124gpo_set(struct gpio_chip *gc, unsigned int offset, int value) +{ + struct bd79124_data *data = gpiochip_get_data(gc); + + return regmap_assign_bits(data->map, BD79124_REG_GPO_VAL, BIT(offset), + value); +} + +static int bd79124gpo_set_multiple(struct gpio_chip *gc, unsigned long *mask, + unsigned long *bits) +{ + unsigned int all_gpos; + int ret; + struct bd79124_data *data = gpiochip_get_data(gc); + + /* + * Ensure all GPIOs in 'mask' are set to be GPIOs + * The valid_mask was not obeyed by the gpiolib in all cases prior the + * https://lore.kernel.org/all/cd5e067b80e1bb590027bc3bfa817e7f794f21c3.1741180097.git.mazziesaccount@gmail.com/ + * + * Keep this check here for a couple of cycles. + */ + ret = regmap_read(data->map, BD79124_REG_PINCFG, &all_gpos); + if (ret) + return ret; + + if (all_gpos ^ *mask) { + dev_dbg(data->dev, "Invalid mux config. Can't set value.\n"); + + return -EINVAL; + } + + return regmap_update_bits(data->map, BD79124_REG_GPO_VAL, *mask, *bits); +} + +static int bd79124_init_valid_mask(struct gpio_chip *gc, + unsigned long *valid_mask, + unsigned int ngpios) +{ + struct bd79124_data *data = gpiochip_get_data(gc); + + *valid_mask = data->gpio_valid_mask; + + return 0; +} + +/* Template for GPIO chip */ +static const struct gpio_chip bd79124gpo_chip = { + .label = "bd79124-gpo", + .get_direction = bd79124gpo_direction_get, + .set = bd79124gpo_set, + .set_multiple = bd79124gpo_set_multiple, + .init_valid_mask = bd79124_init_valid_mask, + .can_sleep = true, + .ngpio = 8, + .base = -1, +}; + +struct bd79124_raw { + u8 val_bit3_0; /* Is set in high bits of the byte */ + u8 val_bit11_4; +}; +#define BD79124_RAW_TO_INT(r) ((r.val_bit11_4 << 4) | (r.val_bit3_0 >> 4)) +#define BD79124_INT_TO_RAW(val) { \ + .val_bit11_4 = (val) >> 4, \ + .val_bit3_0 = (val) << 4, \ +} + +/* + * The high and low limits as well as the recent result values are stored in + * the same way in 2 consequent registers. The first register contains 4 bits + * of the value. These bits are stored in the high bits [7:4] of register, but + * they represent the low bits [3:0] of the value. + * The value bits [11:4] are stored in the next register. + * + * Read data from register and convert to integer. + */ +static int bd79124_read_reg_to_int(struct bd79124_data *data, int reg, + unsigned int *val) +{ + int ret; + struct bd79124_raw raw; + + ret = regmap_bulk_read(data->map, reg, &raw, sizeof(raw)); + if (ret) { + dev_dbg(data->dev, "bulk_read failed %d\n", ret); + + return ret; + } + + *val = BD79124_RAW_TO_INT(raw); + + return 0; +} + +/* + * The high and low limits as well as the recent result values are stored in + * the same way in 2 consequent registers. The first register contains 4 bits + * of the value. These bits are stored in the high bits [7:4] of register, but + * they represent the low bits [3:0] of the value. + * The value bits [11:4] are stored in the next register. + * + * Convert the integer to register format and write it using rmw cycle. + */ +static int bd79124_write_int_to_reg(struct bd79124_data *data, int reg, + unsigned int val) +{ + struct bd79124_raw raw = BD79124_INT_TO_RAW(val); + unsigned int tmp; + int ret; + + ret = regmap_read(data->map, reg, &tmp); + if (ret) + return ret; + + raw.val_bit3_0 |= (tmp & 0xf); + + return regmap_bulk_write(data->map, reg, &raw, sizeof(raw)); +} + +static const struct iio_event_spec bd79124_events[] = { + { + .type = IIO_EV_TYPE_THRESH, + .dir = IIO_EV_DIR_RISING, + .mask_separate = BIT(IIO_EV_INFO_VALUE) | + BIT(IIO_EV_INFO_ENABLE), + }, + { + .type = IIO_EV_TYPE_THRESH, + .dir = IIO_EV_DIR_FALLING, + .mask_separate = BIT(IIO_EV_INFO_VALUE) | + BIT(IIO_EV_INFO_ENABLE), + }, + { + .type = IIO_EV_TYPE_THRESH, + .dir = IIO_EV_DIR_EITHER, + .mask_separate = BIT(IIO_EV_INFO_HYSTERESIS), + }, +}; + +static const struct iio_chan_spec bd79124_chan_template_noirq = { + .type = IIO_VOLTAGE, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), + .indexed = 1, +}; + +static const struct iio_chan_spec bd79124_chan_template = { + .type = IIO_VOLTAGE, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), + .indexed = 1, + .event_spec = bd79124_events, + .num_event_specs = ARRAY_SIZE(bd79124_events), +}; + +static int bd79124_read_event_value(struct iio_dev *iio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir, + enum iio_event_info info, int *val, + int *val2) +{ + struct bd79124_data *data = iio_priv(iio_dev); + int ret, reg; + + if (chan->channel >= BD79124_MAX_NUM_CHANNELS) + return -EINVAL; + + switch (info) { + case IIO_EV_INFO_VALUE: + if (dir == IIO_EV_DIR_RISING) + *val = data->alarm_r_limit[chan->channel]; + else if (dir == IIO_EV_DIR_FALLING) + *val = data->alarm_f_limit[chan->channel]; + else + return -EINVAL; + + return IIO_VAL_INT; + + case IIO_EV_INFO_HYSTERESIS: + reg = BD79124_GET_HYSTERESIS_REG(chan->channel); + ret = regmap_read(data->map, reg, val); + if (ret) + return ret; + + *val &= BD79124_MSK_HYSTERESIS; + /* + * The data-sheet says the hysteresis register value needs to be + * shifted left by 3. + */ + *val <<= 3; + + return IIO_VAL_INT; + + default: + return -EINVAL; + } +} + +static int bd79124_start_measurement(struct bd79124_data *data, int chan) +{ + unsigned int val, regval; + int ret; + + /* See if already started */ + ret = regmap_read(data->map, BD79124_REG_AUTO_CHANNELS, &val); + if (val & BIT(chan)) + return 0; + + /* + * The sequencer must be stopped when channels are added/removed from + * the list of the measured channels to ensure the new channel + * configuration is used. + */ + ret = regmap_clear_bits(data->map, BD79124_REG_SEQ_CFG, + BD79124_MSK_SEQ_START); + if (ret) + return ret; + + ret = regmap_write(data->map, BD79124_REG_AUTO_CHANNELS, val | BIT(chan)); + if (ret) + return ret; + + ret = regmap_set_bits(data->map, BD79124_REG_SEQ_CFG, + BD79124_MSK_SEQ_START); + if (ret) + return ret; + + /* + * Start the measurement at the background. Don't bother checking if + * it was started, regmap has cache. + */ + regval = FIELD_PREP(BD79124_MSK_CONV_MODE, BD79124_CONV_MODE_AUTO); + + return regmap_update_bits(data->map, BD79124_REG_OPMODE_CFG, + BD79124_MSK_CONV_MODE, regval); +} + +static int bd79124_stop_measurement(struct bd79124_data *data, int chan) +{ + unsigned int enabled_chans; + int ret; + + /* See if already stopped */ + ret = regmap_read(data->map, BD79124_REG_AUTO_CHANNELS, &enabled_chans); + if (!(enabled_chans & BIT(chan))) + return 0; + + ret = regmap_clear_bits(data->map, BD79124_REG_SEQ_CFG, + BD79124_MSK_SEQ_START); + + /* Clear the channel from the measured channels */ + enabled_chans &= ~BIT(chan); + ret = regmap_write(data->map, BD79124_REG_AUTO_CHANNELS, + enabled_chans); + if (ret) + return ret; + + /* + * Stop background conversion for power saving if it was the last + * channel. + */ + if (!enabled_chans) { + int regval = FIELD_PREP(BD79124_MSK_CONV_MODE, + BD79124_CONV_MODE_MANSEQ); + + ret = regmap_update_bits(data->map, BD79124_REG_OPMODE_CFG, + BD79124_MSK_CONV_MODE, regval); + if (ret) + return ret; + } + + return regmap_set_bits(data->map, BD79124_REG_SEQ_CFG, + BD79124_MSK_SEQ_START); +} + +static int bd79124_read_event_config(struct iio_dev *iio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir) +{ + struct bd79124_data *data = iio_priv(iio_dev); + + if (chan->channel >= BD79124_MAX_NUM_CHANNELS) + return -EINVAL; + + return !!(data->alarm_monitored[chan->channel] & BIT(dir)); +} + +static int bd79124_disable_event(struct bd79124_data *data, + enum iio_event_direction dir, int channel) +{ + int dir_bit = BIT(dir); + int reg; + unsigned int limit; + + guard(mutex)(&data->mutex); + + /* + * Set thresholds either to 0 or to 2^12 - 1 as appropriate to prevent + * alerts and thus disable event generation. + */ + if (dir == IIO_EV_DIR_RISING) { + reg = BD79124_GET_HIGH_LIMIT_REG(channel); + limit = BD79124_HIGH_LIMIT_MAX; + } else if (dir == IIO_EV_DIR_FALLING) { + reg = BD79124_GET_LOW_LIMIT_REG(channel); + limit = BD79124_LOW_LIMIT_MIN; + } else { + return -EINVAL; + } + + data->alarm_monitored[channel] &= ~dir_bit; + + /* + * Stop measurement if there is no more events to monitor. + * We don't bother checking the retval because the limit + * setting should in any case effectively disable the alarm. + */ + if (!data->alarm_monitored[channel]) { + bd79124_stop_measurement(data, channel); + regmap_clear_bits(data->map, BD79124_REG_ALERT_CH_SEL, + BIT(channel)); + } + + return bd79124_write_int_to_reg(data, reg, limit); +} + +static int bd79124_enable_event(struct bd79124_data *data, + enum iio_event_direction dir, + unsigned int channel) +{ + int dir_bit = BIT(dir); + int reg, ret; + u16 *limit; + + guard(mutex)(&data->mutex); + ret = bd79124_start_measurement(data, channel); + if (ret) + return ret; + + data->alarm_monitored[channel] |= dir_bit; + + /* Add the channel to the list of monitored channels */ + ret = regmap_set_bits(data->map, BD79124_REG_ALERT_CH_SEL, BIT(channel)); + if (ret) + return ret; + + if (dir == IIO_EV_DIR_RISING) { + limit = &data->alarm_f_limit[channel]; + reg = BD79124_GET_HIGH_LIMIT_REG(channel); + } else { + limit = &data->alarm_f_limit[channel]; + reg = BD79124_GET_LOW_LIMIT_REG(channel); + } + /* + * Don't write the new limit to the hardware if we are in the + * rate-limit period. The timer which re-enables the event will set + * the limit. + */ + if (!(data->alarm_suppressed[channel] & dir_bit)) { + ret = bd79124_write_int_to_reg(data, reg, *limit); + if (ret) + return ret; + } + + /* + * Enable comparator. Trust the regmap cache, no need to check + * if it was already enabled. + * + * We could do this in the hw-init, but there may be users who + * never enable alarms and for them it makes sense to not + * enable the comparator at probe. + */ + return regmap_set_bits(data->map, BD79124_REG_GEN_CFG, + BD79124_MSK_DWC_EN); +} + +static int bd79124_write_event_config(struct iio_dev *iio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir, bool state) +{ + struct bd79124_data *data = iio_priv(iio_dev); + + if (chan->channel >= BD79124_MAX_NUM_CHANNELS) + return -EINVAL; + + if (state) + return bd79124_enable_event(data, dir, chan->channel); + + return bd79124_disable_event(data, dir, chan->channel); +} + +static int bd79124_write_event_value(struct iio_dev *iio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir, + enum iio_event_info info, int val, + int val2) +{ + struct bd79124_data *data = iio_priv(iio_dev); + int reg; + + if (chan->channel >= BD79124_MAX_NUM_CHANNELS) + return -EINVAL; + + switch (info) { + case IIO_EV_INFO_VALUE: + { + guard(mutex)(&data->mutex); + + if (dir == IIO_EV_DIR_RISING) { + data->alarm_r_limit[chan->channel] = val; + reg = BD79124_GET_HIGH_LIMIT_REG(chan->channel); + } else if (dir == IIO_EV_DIR_FALLING) { + data->alarm_f_limit[chan->channel] = val; + reg = BD79124_GET_LOW_LIMIT_REG(chan->channel); + } else { + return -EINVAL; + } + /* + * We don't want to enable the alarm if it is not enabled or + * if it is suppressed. In that case skip writing to the + * register. + */ + if (!(data->alarm_monitored[chan->channel] & BIT(dir)) || + data->alarm_suppressed[chan->channel] & BIT(dir)) + return 0; + + return bd79124_write_int_to_reg(data, reg, val); + } + case IIO_EV_INFO_HYSTERESIS: + reg = BD79124_GET_HYSTERESIS_REG(chan->channel); + val >>= 3; + + return regmap_update_bits(data->map, reg, BD79124_MSK_HYSTERESIS, + val); + default: + return -EINVAL; + } +} + +static int bd79124_single_chan_seq(struct bd79124_data *data, int chan, unsigned int *old) +{ + int ret; + + ret = regmap_clear_bits(data->map, BD79124_REG_SEQ_CFG, + BD79124_MSK_SEQ_START); + if (ret) + return ret; + + /* + * It may be we have some channels monitored for alarms so we want to + * cache the old config and return it when the single channel + * measurement has been completed. + */ + ret = regmap_read(data->map, BD79124_REG_AUTO_CHANNELS, old); + if (ret) + return ret; + + ret = regmap_write(data->map, BD79124_REG_AUTO_CHANNELS, BIT(chan)); + if (ret) + return ret; + + /* Restart the sequencer */ + return regmap_set_bits(data->map, BD79124_REG_SEQ_CFG, + BD79124_MSK_SEQ_START); +} + +static int bd79124_single_chan_seq_end(struct bd79124_data *data, unsigned int old) +{ + int ret; + + ret = regmap_clear_bits(data->map, BD79124_REG_SEQ_CFG, + BD79124_MSK_SEQ_START); + if (ret) + return ret; + + ret = regmap_write(data->map, BD79124_REG_AUTO_CHANNELS, old); + if (ret) + return ret; + + return regmap_set_bits(data->map, BD79124_REG_SEQ_CFG, + BD79124_MSK_SEQ_START); +} + +static int bd79124_read_raw(struct iio_dev *iio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long m) +{ + struct bd79124_data *data = iio_priv(iio_dev); + int ret; + + if (chan->channel >= BD79124_MAX_NUM_CHANNELS) + return -EINVAL; + + switch (m) { + case IIO_CHAN_INFO_RAW: + { + unsigned int old_chan_cfg, regval; + int tmp; + + guard(mutex)(&data->mutex); + + /* + * Start the automatic conversion. This is needed here if no + * events have been enabled. + */ + regval = FIELD_PREP(BD79124_MSK_CONV_MODE, + BD79124_CONV_MODE_AUTO); + ret = regmap_update_bits(data->map, BD79124_REG_OPMODE_CFG, + BD79124_MSK_CONV_MODE, regval); + if (ret) + return ret; + + ret = bd79124_single_chan_seq(data, chan->channel, &old_chan_cfg); + if (ret) + return ret; + + /* The maximum conversion time is 6 uS. */ + udelay(6); + + ret = bd79124_read_reg_to_int(data, + BD79124_GET_RECENT_RES_REG(chan->channel), val); + /* + * Return the old chan config even if data reading failed in + * order to re-enable the event monitoring. + */ + tmp = bd79124_single_chan_seq_end(data, old_chan_cfg); + if (tmp) + dev_err(data->dev, + "Failed to return config. Alarms may be disabled\n"); + + if (ret) + return ret; + + return IIO_VAL_INT; + } + case IIO_CHAN_INFO_SCALE: + *val = data->vmax / 1000; + *val2 = BD79124_ADC_BITS; + return IIO_VAL_FRACTIONAL_LOG2; + default: + return -EINVAL; + } +} + +static const struct iio_info bd79124_info = { + .read_raw = bd79124_read_raw, + .read_event_config = &bd79124_read_event_config, + .write_event_config = &bd79124_write_event_config, + .read_event_value = &bd79124_read_event_value, + .write_event_value = &bd79124_write_event_value, +}; + +static void bd79124_re_enable_lo(struct bd79124_data *data, unsigned int channel) +{ + int ret, evbit = BIT(IIO_EV_DIR_FALLING); + + /* + * We should not re-enable the event if user has disabled it while + * rate-limiting was enabled. + */ + if (!(data->alarm_suppressed[channel] & evbit)) + return; + + data->alarm_suppressed[channel] &= ~evbit; + + if (!(data->alarm_monitored[channel] & evbit)) + return; + + ret = bd79124_write_int_to_reg(data, BD79124_GET_LOW_LIMIT_REG(channel), + data->alarm_f_limit[channel]); + if (ret) + dev_warn(data->dev, "Low limit enabling failed for channel%d\n", + channel); +} + +static void bd79124_re_enable_hi(struct bd79124_data *data, unsigned int channel) +{ + int ret, evbit = BIT(IIO_EV_DIR_RISING); + + /* + * We should not re-enable the event if user has disabled it while + * rate-limiting was enabled. + */ + if (!(data->alarm_suppressed[channel] & evbit)) + return; + + data->alarm_suppressed[channel] &= ~evbit; + + if (!(data->alarm_monitored[channel] & evbit)) + return; + + ret = bd79124_write_int_to_reg(data, BD79124_GET_HIGH_LIMIT_REG(channel), + data->alarm_r_limit[channel]); + if (ret) + dev_warn(data->dev, "High limit enabling failed for channel%d\n", + channel); +} + +static void bd79124_alm_enable_worker(struct work_struct *work) +{ + int i; + struct bd79124_data *data = container_of(work, struct bd79124_data, + alm_enable_work.work); + + /* Take the mutex so there is no race with user disabling the alarm */ + guard(mutex)(&data->mutex); + for (i = 0; i < BD79124_MAX_NUM_CHANNELS; i++) { + bd79124_re_enable_hi(data, i); + bd79124_re_enable_lo(data, i); + } +} + +static int __bd79124_event_ratelimit(struct bd79124_data *data, int reg, + unsigned int limit) +{ + int ret; + + if (limit > BD79124_HIGH_LIMIT_MAX) + return -EINVAL; + + ret = bd79124_write_int_to_reg(data, reg, limit); + if (ret) + return ret; + + /* + * We use 1 sec 'grace period'. At the moment I see no reason to make + * this user configurable. We need an ABI for this if configuration is + * needed. + */ + schedule_delayed_work(&data->alm_enable_work, msecs_to_jiffies(1000)); + + return 0; +} + +static int bd79124_event_ratelimit_hi(struct bd79124_data *data, + unsigned int channel) +{ + guard(mutex)(&data->mutex); + data->alarm_suppressed[channel] |= BIT(IIO_EV_DIR_RISING); + + return __bd79124_event_ratelimit(data, + BD79124_GET_HIGH_LIMIT_REG(channel), + BD79124_HIGH_LIMIT_MAX); +} + +static int bd79124_event_ratelimit_lo(struct bd79124_data *data, + unsigned int channel) +{ + guard(mutex)(&data->mutex); + data->alarm_suppressed[channel] |= BIT(IIO_EV_DIR_FALLING); + + return __bd79124_event_ratelimit(data, + BD79124_GET_LOW_LIMIT_REG(channel), + BD79124_LOW_LIMIT_MIN); +} + +static irqreturn_t bd79124_event_handler(int irq, void *priv) +{ + unsigned int i_hi, i_lo; + int i, ret; + struct iio_dev *iio_dev = priv; + struct bd79124_data *data = iio_priv(iio_dev); + + /* + * Return IRQ_NONE if bailing-out without acking. This allows the IRQ + * subsystem to disable the offending IRQ line if we get a hardware + * problem. This behaviour has saved my poor bottom a few times in the + * past as, instead of getting unusably unresponsive, the system has + * spilled out the magic words "...nobody cared". + */ + ret = regmap_read(data->map, BD79124_REG_EVENT_FLAG_HI, &i_hi); + if (ret) + return IRQ_NONE; + + ret = regmap_read(data->map, BD79124_REG_EVENT_FLAG_LO, &i_lo); + if (ret) + return IRQ_NONE; + + if (!i_lo && !i_hi) + return IRQ_NONE; + + for (i = 0; i < BD79124_MAX_NUM_CHANNELS; i++) { + u64 ecode; + + if (BIT(i) & i_hi) { + ecode = IIO_UNMOD_EVENT_CODE(IIO_VOLTAGE, i, + IIO_EV_TYPE_THRESH, + IIO_EV_DIR_RISING); + + iio_push_event(iio_dev, ecode, data->timestamp); + /* + * The BD79124 keeps the IRQ asserted for as long as + * the voltage exceeds the threshold. It causes the IRQ + * to keep firing. + * + * Disable the event for the channel and schedule the + * re-enabling the event later to prevent storm of + * events. + */ + ret = bd79124_event_ratelimit_hi(data, i); + if (ret) + return IRQ_NONE; + } + if (BIT(i) & i_lo) { + ecode = IIO_UNMOD_EVENT_CODE(IIO_VOLTAGE, i, + IIO_EV_TYPE_THRESH, + IIO_EV_DIR_FALLING); + + iio_push_event(iio_dev, ecode, data->timestamp); + ret = bd79124_event_ratelimit_lo(data, i); + if (ret) + return IRQ_NONE; + } + } + + ret = regmap_write(data->map, BD79124_REG_EVENT_FLAG_HI, i_hi); + if (ret) + return IRQ_NONE; + + ret = regmap_write(data->map, BD79124_REG_EVENT_FLAG_LO, i_lo); + if (ret) + return IRQ_NONE; + + return IRQ_HANDLED; +} + +static irqreturn_t bd79124_irq_handler(int irq, void *priv) +{ + struct iio_dev *iio_dev = priv; + struct bd79124_data *data = iio_priv(iio_dev); + + data->timestamp = iio_get_time_ns(iio_dev); + + return IRQ_WAKE_THREAD; +} + +static int bd79124_chan_init(struct bd79124_data *data, int channel) +{ + int ret; + + ret = regmap_write(data->map, BD79124_GET_HIGH_LIMIT_REG(channel), + BD79124_HIGH_LIMIT_MAX); + if (ret) + return ret; + + return regmap_write(data->map, BD79124_GET_LOW_LIMIT_REG(channel), + BD79124_LOW_LIMIT_MIN); +} + +static int bd79124_get_gpio_pins(const struct iio_chan_spec *cs, int num_channels) +{ + int i, gpio_channels; + + /* + * Let's initialize the mux config to say that all 8 channels are + * GPIOs. Then we can just loop through the iio_chan_spec and clear the + * bits for found ADC channels. + */ + gpio_channels = GENMASK(7, 0); + for (i = 0; i < num_channels; i++) + gpio_channels &= ~BIT(cs[i].channel); + + return gpio_channels; +} + +static int bd79124_hw_init(struct bd79124_data *data) +{ + unsigned int regval; + int ret, i; + + for (i = 0; i < BD79124_MAX_NUM_CHANNELS; i++) { + ret = bd79124_chan_init(data, i); + if (ret) + return ret; + data->alarm_r_limit[i] = BD79124_HIGH_LIMIT_MAX; + } + /* Stop auto sequencer */ + ret = regmap_clear_bits(data->map, BD79124_REG_SEQ_CFG, + BD79124_MSK_SEQ_START); + if (ret) + return ret; + + /* Enable writing the measured values to the registers */ + ret = regmap_set_bits(data->map, BD79124_REG_GEN_CFG, + BD79124_MSK_STATS_EN); + if (ret) + return ret; + + /* Set no channels to be auto-measured */ + ret = regmap_write(data->map, BD79124_REG_AUTO_CHANNELS, 0x0); + if (ret) + return ret; + + /* Set no channels to be manually measured */ + ret = regmap_write(data->map, BD79124_REG_MANUAL_CHANNELS, 0x0); + if (ret) + return ret; + + regval = FIELD_PREP(BD79124_MSK_AUTO_INTERVAL, BD79124_INTERVAL_750_US); + ret = regmap_update_bits(data->map, BD79124_REG_OPMODE_CFG, + BD79124_MSK_AUTO_INTERVAL, regval); + if (ret) + return ret; + + /* Sequencer mode to auto */ + ret = regmap_set_bits(data->map, BD79124_REG_SEQ_CFG, + BD79124_MSK_SEQ_SEQ); + if (ret) + return ret; + + /* Don't start the measurement */ + regval = FIELD_PREP(BD79124_MSK_CONV_MODE, BD79124_CONV_MODE_MANSEQ); + return regmap_update_bits(data->map, BD79124_REG_OPMODE_CFG, + BD79124_MSK_CONV_MODE, regval); +} + +static int bd79124_probe(struct i2c_client *i2c) +{ + struct bd79124_data *data; + struct iio_dev *iio_dev; + const struct iio_chan_spec *template; + struct iio_chan_spec *cs; + struct device *dev = &i2c->dev; + unsigned int gpio_pins; + int ret; + + iio_dev = devm_iio_device_alloc(dev, sizeof(*data)); + if (!iio_dev) + return -ENOMEM; + + data = iio_priv(iio_dev); + data->dev = dev; + data->map = devm_regmap_init_i2c(i2c, &bd79124_regmap); + if (IS_ERR(data->map)) + return dev_err_probe(dev, PTR_ERR(data->map), + "Failed to initialize Regmap\n"); + + ret = devm_regulator_get_enable_read_voltage(dev, "vdd"); + if (ret < 0) + return dev_err_probe(dev, ret, "Failed to get the Vdd\n"); + + data->vmax = ret; + + ret = devm_regulator_get_enable(dev, "iovdd"); + if (ret < 0) + return dev_err_probe(dev, ret, "Failed to enable I/O voltage\n"); + + ret = devm_delayed_work_autocancel(dev, &data->alm_enable_work, + bd79124_alm_enable_worker); + if (ret) + return ret; + + if (i2c->irq) { + template = &bd79124_chan_template; + } else { + template = &bd79124_chan_template_noirq; + dev_dbg(dev, "No IRQ found, events disabled\n"); + } + + ret = devm_mutex_init(dev, &data->mutex); + if (ret) + return ret; + + ret = devm_iio_adc_device_alloc_chaninfo_se(dev, template, + BD79124_MAX_NUM_CHANNELS - 1, &cs); + if (ret < 0) { + /* Register all pins as GPOs if there are no ADC channels */ + if (ret == -ENOENT) + goto register_gpios; + return ret; + } + iio_dev->channels = cs; + iio_dev->num_channels = ret; + iio_dev->info = &bd79124_info; + iio_dev->name = "bd79124"; + iio_dev->modes = INDIO_DIRECT_MODE; + + ret = bd79124_hw_init(data); + if (ret) + return ret; + + if (i2c->irq > 0) { + ret = devm_request_threaded_irq(dev, i2c->irq, + bd79124_irq_handler, &bd79124_event_handler, + IRQF_ONESHOT, "adc-thresh-alert", iio_dev); + if (ret) + return dev_err_probe(data->dev, ret, + "Failed to register IRQ\n"); + } + + ret = devm_iio_device_register(data->dev, iio_dev); + if (ret) + return dev_err_probe(data->dev, ret, "Failed to register ADC\n"); + +register_gpios: + gpio_pins = bd79124_get_gpio_pins(iio_dev->channels, + iio_dev->num_channels); + + /* + * The mux should default to "all ADCs", but better to not trust it. + * Thus we do set the mux even when we have only ADCs and no GPOs. + */ + ret = regmap_write(data->map, BD79124_REG_PINCFG, gpio_pins); + if (ret) + return ret; + + /* No GPOs if all channels are reserved for ADC, so we're done. */ + if (!gpio_pins) + return 0; + + data->gpio_valid_mask = gpio_pins; + data->gc = bd79124gpo_chip; + data->gc.parent = dev; + + return devm_gpiochip_add_data(dev, &data->gc, data); +} + +static const struct of_device_id bd79124_of_match[] = { + { .compatible = "rohm,bd79124" }, + { } +}; +MODULE_DEVICE_TABLE(of, bd79124_of_match); + +static const struct i2c_device_id bd79124_id[] = { + { .name = "bd79124" }, + { } +}; +MODULE_DEVICE_TABLE(i2c, bd79124_id); + +static struct i2c_driver bd79124_driver = { + .driver = { + .name = "bd79124", + .of_match_table = bd79124_of_match, + }, + .probe = bd79124_probe, + .id_table = bd79124_id, +}; +module_i2c_driver(bd79124_driver); + +MODULE_AUTHOR("Matti Vaittinen "); +MODULE_DESCRIPTION("Driver for ROHM BD79124 ADC"); +MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS("IIO_DRIVER"); diff --git a/drivers/iio/adc/rtq6056.c b/drivers/iio/adc/rtq6056.c new file mode 100644 index 000000000..2083c4351 --- /dev/null +++ b/drivers/iio/adc/rtq6056.c @@ -0,0 +1,900 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2022 Richtek Technology Corp. + * + * ChiYuan Huang + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#define RTQ6056_REG_CONFIG 0x00 +#define RTQ6056_REG_SHUNTVOLT 0x01 +#define RTQ6056_REG_BUSVOLT 0x02 +#define RTQ6056_REG_POWER 0x03 +#define RTQ6056_REG_CURRENT 0x04 +#define RTQ6056_REG_CALIBRATION 0x05 +#define RTQ6056_REG_MASKENABLE 0x06 +#define RTQ6056_REG_ALERTLIMIT 0x07 +#define RTQ6056_REG_MANUFACTID 0xFE +#define RTQ6056_REG_DIEID 0xFF + +#define RTQ6056_VENDOR_ID 0x1214 +#define RTQ6056_DEFAULT_CONFIG 0x4127 +#define RTQ6056_CONT_ALLON 7 + +#define RTQ6059_DEFAULT_CONFIG 0x3C47 +#define RTQ6059_VBUS_LSB_OFFSET 3 +#define RTQ6059_AVG_BASE 8 + +enum { + RTQ6056_CH_VSHUNT = 0, + RTQ6056_CH_VBUS, + RTQ6056_CH_POWER, + RTQ6056_CH_CURRENT, + RTQ6056_MAX_CHANNEL +}; + +/* + * The enum is to present the 0x00 CONFIG RG bitfield for the 16bit RG value + * field value order from LSB to MSB + * RTQ6053/6 is OPMODE->VSHUNTCT->VBUSCT->AVG->RESET + * RTQ6059 is OPMODE->SADC->BADC->PGA->RESET + */ +enum { + F_OPMODE = 0, + F_VSHUNTCT, + F_RTQ6059_SADC = F_VSHUNTCT, + F_VBUSCT, + F_RTQ6059_BADC = F_VBUSCT, + F_AVG, + F_RTQ6059_PGA = F_AVG, + F_RESET, + F_MAX_FIELDS +}; + +struct rtq6056_priv; + +struct richtek_dev_data { + bool fixed_samp_freq; + u8 vbus_offset; + int default_conv_time_us; + unsigned int default_config; + unsigned int calib_coefficient; + const int *avg_sample_list; + int avg_sample_list_length; + const struct reg_field *reg_fields; + const struct iio_chan_spec *channels; + int num_channels; + int (*read_scale)(struct iio_chan_spec const *ch, int *val, int *val2); + int (*set_average)(struct rtq6056_priv *priv, int val); +}; + +struct rtq6056_priv { + struct device *dev; + struct regmap *regmap; + struct regmap_field *rm_fields[F_MAX_FIELDS]; + const struct richtek_dev_data *devdata; + u32 shunt_resistor_uohm; + int vshuntct_us; + int vbusct_us; + int avg_sample; +}; + +static const struct reg_field rtq6056_reg_fields[F_MAX_FIELDS] = { + [F_OPMODE] = REG_FIELD(RTQ6056_REG_CONFIG, 0, 2), + [F_VSHUNTCT] = REG_FIELD(RTQ6056_REG_CONFIG, 3, 5), + [F_VBUSCT] = REG_FIELD(RTQ6056_REG_CONFIG, 6, 8), + [F_AVG] = REG_FIELD(RTQ6056_REG_CONFIG, 9, 11), + [F_RESET] = REG_FIELD(RTQ6056_REG_CONFIG, 15, 15), +}; + +static const struct reg_field rtq6059_reg_fields[F_MAX_FIELDS] = { + [F_OPMODE] = REG_FIELD(RTQ6056_REG_CONFIG, 0, 2), + [F_RTQ6059_SADC] = REG_FIELD(RTQ6056_REG_CONFIG, 3, 6), + [F_RTQ6059_BADC] = REG_FIELD(RTQ6056_REG_CONFIG, 7, 10), + [F_RTQ6059_PGA] = REG_FIELD(RTQ6056_REG_CONFIG, 11, 12), + [F_RESET] = REG_FIELD(RTQ6056_REG_CONFIG, 15, 15), +}; + +static const struct iio_chan_spec rtq6056_channels[RTQ6056_MAX_CHANNEL + 1] = { + { + .type = IIO_VOLTAGE, + .indexed = 1, + .channel = 0, + .address = RTQ6056_REG_SHUNTVOLT, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SCALE) | + BIT(IIO_CHAN_INFO_SAMP_FREQ), + .info_mask_separate_available = BIT(IIO_CHAN_INFO_SAMP_FREQ), + .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), + .info_mask_shared_by_all_available = BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), + .scan_index = 0, + .scan_type = { + .sign = 's', + .realbits = 16, + .storagebits = 16, + .endianness = IIO_CPU, + }, + }, + { + .type = IIO_VOLTAGE, + .indexed = 1, + .channel = 1, + .address = RTQ6056_REG_BUSVOLT, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SCALE) | + BIT(IIO_CHAN_INFO_SAMP_FREQ), + .info_mask_separate_available = BIT(IIO_CHAN_INFO_SAMP_FREQ), + .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), + .info_mask_shared_by_all_available = BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), + .scan_index = 1, + .scan_type = { + .sign = 'u', + .realbits = 16, + .storagebits = 16, + .endianness = IIO_CPU, + }, + }, + { + .type = IIO_POWER, + .indexed = 1, + .channel = 2, + .address = RTQ6056_REG_POWER, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SCALE) | + BIT(IIO_CHAN_INFO_SAMP_FREQ), + .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), + .info_mask_shared_by_all_available = BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), + .scan_index = 2, + .scan_type = { + .sign = 'u', + .realbits = 16, + .storagebits = 16, + .endianness = IIO_CPU, + }, + }, + { + .type = IIO_CURRENT, + .indexed = 1, + .channel = 3, + .address = RTQ6056_REG_CURRENT, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SAMP_FREQ), + .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), + .info_mask_shared_by_all_available = BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), + .scan_index = 3, + .scan_type = { + .sign = 's', + .realbits = 16, + .storagebits = 16, + .endianness = IIO_CPU, + }, + }, + IIO_CHAN_SOFT_TIMESTAMP(RTQ6056_MAX_CHANNEL), +}; + +/* + * Difference between RTQ6056 and RTQ6059 + * - Fixed sampling conversion time + * - Average sample numbers + * - Channel scale + * - calibration coefficient + */ +static const struct iio_chan_spec rtq6059_channels[RTQ6056_MAX_CHANNEL + 1] = { + { + .type = IIO_VOLTAGE, + .indexed = 1, + .channel = 0, + .address = RTQ6056_REG_SHUNTVOLT, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SCALE) | + BIT(IIO_CHAN_INFO_SAMP_FREQ), + .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), + .info_mask_shared_by_all_available = BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), + .scan_index = 0, + .scan_type = { + .sign = 's', + .realbits = 16, + .storagebits = 16, + .endianness = IIO_CPU, + }, + }, + { + .type = IIO_VOLTAGE, + .indexed = 1, + .channel = 1, + .address = RTQ6056_REG_BUSVOLT, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SCALE) | + BIT(IIO_CHAN_INFO_SAMP_FREQ), + .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), + .info_mask_shared_by_all_available = BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), + .scan_index = 1, + .scan_type = { + .sign = 'u', + .realbits = 16, + .storagebits = 16, + .endianness = IIO_CPU, + }, + }, + { + .type = IIO_POWER, + .indexed = 1, + .channel = 2, + .address = RTQ6056_REG_POWER, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SCALE) | + BIT(IIO_CHAN_INFO_SAMP_FREQ), + .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), + .info_mask_shared_by_all_available = BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), + .scan_index = 2, + .scan_type = { + .sign = 'u', + .realbits = 16, + .storagebits = 16, + .endianness = IIO_CPU, + }, + }, + { + .type = IIO_CURRENT, + .indexed = 1, + .channel = 3, + .address = RTQ6056_REG_CURRENT, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SAMP_FREQ), + .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), + .info_mask_shared_by_all_available = BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), + .scan_index = 3, + .scan_type = { + .sign = 's', + .realbits = 16, + .storagebits = 16, + .endianness = IIO_CPU, + }, + }, + IIO_CHAN_SOFT_TIMESTAMP(RTQ6056_MAX_CHANNEL), +}; + +static int rtq6056_adc_read_channel(struct rtq6056_priv *priv, + struct iio_chan_spec const *ch, + int *val) +{ + const struct richtek_dev_data *devdata = priv->devdata; + struct device *dev = priv->dev; + unsigned int addr = ch->address; + unsigned int regval; + int ret; + + pm_runtime_get_sync(dev); + ret = regmap_read(priv->regmap, addr, ®val); + pm_runtime_mark_last_busy(dev); + pm_runtime_put(dev); + if (ret) + return ret; + + /* Power and VBUS is unsigned 16-bit, others are signed 16-bit */ + switch (addr) { + case RTQ6056_REG_BUSVOLT: + regval >>= devdata->vbus_offset; + *val = regval; + return IIO_VAL_INT; + case RTQ6056_REG_POWER: + *val = regval; + return IIO_VAL_INT; + case RTQ6056_REG_SHUNTVOLT: + case RTQ6056_REG_CURRENT: + *val = sign_extend32(regval, 15); + return IIO_VAL_INT; + default: + return -EINVAL; + } +} + +static int rtq6056_adc_read_scale(struct iio_chan_spec const *ch, int *val, + int *val2) +{ + switch (ch->address) { + case RTQ6056_REG_SHUNTVOLT: + /* VSHUNT lsb 2.5uV */ + *val = 2500; + *val2 = 1000000; + return IIO_VAL_FRACTIONAL; + case RTQ6056_REG_BUSVOLT: + /* VBUS lsb 1.25mV */ + *val = 1250; + *val2 = 1000; + return IIO_VAL_FRACTIONAL; + case RTQ6056_REG_POWER: + /* Power lsb 25mW */ + *val = 25; + return IIO_VAL_INT; + default: + return -EINVAL; + } +} + +static int rtq6059_adc_read_scale(struct iio_chan_spec const *ch, int *val, + int *val2) +{ + switch (ch->address) { + case RTQ6056_REG_SHUNTVOLT: + /* VSHUNT lsb 10uV */ + *val = 10000; + *val2 = 1000000; + return IIO_VAL_FRACTIONAL; + case RTQ6056_REG_BUSVOLT: + /* VBUS lsb 4mV */ + *val = 4; + return IIO_VAL_INT; + case RTQ6056_REG_POWER: + /* Power lsb 20mW */ + *val = 20; + return IIO_VAL_INT; + default: + return -EINVAL; + } +} + +/* + * Sample frequency for channel VSHUNT and VBUS. The indices correspond + * with the bit value expected by the chip. And it can be found at + * https://www.richtek.com/assets/product_file/RTQ6056/DSQ6056-00.pdf + */ +static const int rtq6056_samp_freq_list[] = { + 7194, 4926, 3717, 1904, 964, 485, 243, 122, +}; + +static int rtq6056_adc_set_samp_freq(struct rtq6056_priv *priv, + struct iio_chan_spec const *ch, int val) +{ + struct regmap_field *rm_field; + unsigned int selector; + int *ct, ret; + + if (val > 7194 || val < 122) + return -EINVAL; + + if (ch->address == RTQ6056_REG_SHUNTVOLT) { + rm_field = priv->rm_fields[F_VSHUNTCT]; + ct = &priv->vshuntct_us; + } else if (ch->address == RTQ6056_REG_BUSVOLT) { + rm_field = priv->rm_fields[F_VBUSCT]; + ct = &priv->vbusct_us; + } else + return -EINVAL; + + selector = find_closest_descending(val, rtq6056_samp_freq_list, + ARRAY_SIZE(rtq6056_samp_freq_list)); + + ret = regmap_field_write(rm_field, selector); + if (ret) + return ret; + + *ct = 1000000 / rtq6056_samp_freq_list[selector]; + + return 0; +} + +/* + * Available averaging rate for rtq6056. The indices correspond with the bit + * value expected by the chip. And it can be found at + * https://www.richtek.com/assets/product_file/RTQ6056/DSQ6056-00.pdf + */ +static const int rtq6056_avg_sample_list[] = { + 1, 4, 16, 64, 128, 256, 512, 1024, +}; + +static const int rtq6059_avg_sample_list[] = { + 1, 2, 4, 8, 16, 32, 64, 128, +}; + +static int rtq6056_adc_set_average(struct rtq6056_priv *priv, int val) +{ + unsigned int selector; + int ret; + + if (val > 1024 || val < 1) + return -EINVAL; + + selector = find_closest(val, rtq6056_avg_sample_list, + ARRAY_SIZE(rtq6056_avg_sample_list)); + + ret = regmap_field_write(priv->rm_fields[F_AVG], selector); + if (ret) + return ret; + + priv->avg_sample = rtq6056_avg_sample_list[selector]; + + return 0; +} + +static int rtq6059_adc_set_average(struct rtq6056_priv *priv, int val) +{ + unsigned int selector; + int ret; + + if (val > 128 || val < 1) + return -EINVAL; + + /* The supported average sample is 2^x (x from 0 to 7) */ + selector = fls(val) - 1; + + ret = regmap_field_write(priv->rm_fields[F_RTQ6059_BADC], + RTQ6059_AVG_BASE + selector); + if (ret) + return ret; + + ret = regmap_field_write(priv->rm_fields[F_RTQ6059_SADC], + RTQ6059_AVG_BASE + selector); + + priv->avg_sample = BIT(selector); + + return 0; +} + +static int rtq6056_adc_get_sample_freq(struct rtq6056_priv *priv, + struct iio_chan_spec const *ch, int *val) +{ + int sample_time; + + if (ch->address == RTQ6056_REG_SHUNTVOLT) + sample_time = priv->vshuntct_us; + else if (ch->address == RTQ6056_REG_BUSVOLT) + sample_time = priv->vbusct_us; + else { + sample_time = priv->vshuntct_us + priv->vbusct_us; + sample_time *= priv->avg_sample; + } + + *val = 1000000 / sample_time; + + return IIO_VAL_INT; +} + +static int rtq6056_adc_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int *val, + int *val2, long mask) +{ + struct rtq6056_priv *priv = iio_priv(indio_dev); + const struct richtek_dev_data *devdata = priv->devdata; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + return rtq6056_adc_read_channel(priv, chan, val); + case IIO_CHAN_INFO_SCALE: + return devdata->read_scale(chan, val, val2); + case IIO_CHAN_INFO_OVERSAMPLING_RATIO: + *val = priv->avg_sample; + return IIO_VAL_INT; + case IIO_CHAN_INFO_SAMP_FREQ: + return rtq6056_adc_get_sample_freq(priv, chan, val); + default: + return -EINVAL; + } +} + +static int rtq6056_adc_read_avail(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + const int **vals, int *type, int *length, + long mask) +{ + struct rtq6056_priv *priv = iio_priv(indio_dev); + const struct richtek_dev_data *devdata = priv->devdata; + + switch (mask) { + case IIO_CHAN_INFO_SAMP_FREQ: + *vals = rtq6056_samp_freq_list; + *type = IIO_VAL_INT; + *length = ARRAY_SIZE(rtq6056_samp_freq_list); + return IIO_AVAIL_LIST; + case IIO_CHAN_INFO_OVERSAMPLING_RATIO: + *vals = devdata->avg_sample_list; + *length = devdata->avg_sample_list_length; + *type = IIO_VAL_INT; + return IIO_AVAIL_LIST; + default: + return -EINVAL; + } +} + +static int __rtq6056_adc_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int val, + long mask) +{ + struct rtq6056_priv *priv = iio_priv(indio_dev); + const struct richtek_dev_data *devdata = priv->devdata; + + switch (mask) { + case IIO_CHAN_INFO_SAMP_FREQ: + if (devdata->fixed_samp_freq) + return -EINVAL; + return rtq6056_adc_set_samp_freq(priv, chan, val); + case IIO_CHAN_INFO_OVERSAMPLING_RATIO: + return devdata->set_average(priv, val); + default: + return -EINVAL; + } +} + +static int rtq6056_adc_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int val, + int val2, long mask) +{ + int ret; + + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; + + ret = __rtq6056_adc_write_raw(indio_dev, chan, val, mask); + iio_device_release_direct(indio_dev); + return ret; +} + +static const char *rtq6056_channel_labels[RTQ6056_MAX_CHANNEL] = { + [RTQ6056_CH_VSHUNT] = "Vshunt", + [RTQ6056_CH_VBUS] = "Vbus", + [RTQ6056_CH_POWER] = "Power", + [RTQ6056_CH_CURRENT] = "Current", +}; + +static int rtq6056_adc_read_label(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + char *label) +{ + return sysfs_emit(label, "%s\n", rtq6056_channel_labels[chan->channel]); +} + +static int rtq6056_set_shunt_resistor(struct rtq6056_priv *priv, + int resistor_uohm) +{ + const struct richtek_dev_data *devdata = priv->devdata; + unsigned int calib_val; + int ret; + + if (resistor_uohm <= 0) { + dev_err(priv->dev, "Invalid resistor [%d]\n", resistor_uohm); + return -EINVAL; + } + + /* calibration = coefficient / (Rshunt (uOhm) * current lsb (1mA)) */ + calib_val = devdata->calib_coefficient / resistor_uohm; + ret = regmap_write(priv->regmap, RTQ6056_REG_CALIBRATION, calib_val); + if (ret) + return ret; + + priv->shunt_resistor_uohm = resistor_uohm; + + return 0; +} + +static ssize_t shunt_resistor_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct rtq6056_priv *priv = iio_priv(dev_to_iio_dev(dev)); + int vals[2] = { priv->shunt_resistor_uohm, 1000000 }; + + return iio_format_value(buf, IIO_VAL_FRACTIONAL, 1, vals); +} + +static ssize_t shunt_resistor_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t len) +{ + struct iio_dev *indio_dev = dev_to_iio_dev(dev); + struct rtq6056_priv *priv = iio_priv(indio_dev); + int val, val_fract, ret; + + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; + + ret = iio_str_to_fixpoint(buf, 100000, &val, &val_fract); + if (ret) + goto out_store; + + ret = rtq6056_set_shunt_resistor(priv, val * 1000000 + val_fract); + +out_store: + iio_device_release_direct(indio_dev); + + return ret ?: len; +} + +static IIO_DEVICE_ATTR_RW(shunt_resistor, 0); + +static struct attribute *rtq6056_attributes[] = { + &iio_dev_attr_shunt_resistor.dev_attr.attr, + NULL +}; + +static const struct attribute_group rtq6056_attribute_group = { + .attrs = rtq6056_attributes, +}; + +static const struct iio_info rtq6056_info = { + .attrs = &rtq6056_attribute_group, + .read_raw = rtq6056_adc_read_raw, + .read_avail = rtq6056_adc_read_avail, + .write_raw = rtq6056_adc_write_raw, + .read_label = rtq6056_adc_read_label, +}; + +static irqreturn_t rtq6056_buffer_trigger_handler(int irq, void *p) +{ + struct iio_poll_func *pf = p; + struct iio_dev *indio_dev = pf->indio_dev; + struct rtq6056_priv *priv = iio_priv(indio_dev); + const struct richtek_dev_data *devdata = priv->devdata; + struct device *dev = priv->dev; + struct { + u16 vals[RTQ6056_MAX_CHANNEL]; + aligned_s64 timestamp; + } data = { }; + unsigned int raw; + int i = 0, bit, ret; + + pm_runtime_get_sync(dev); + + iio_for_each_active_channel(indio_dev, bit) { + unsigned int addr = rtq6056_channels[bit].address; + + ret = regmap_read(priv->regmap, addr, &raw); + if (ret) + goto out; + + if (addr == RTQ6056_REG_BUSVOLT) + raw >>= devdata->vbus_offset; + + data.vals[i++] = raw; + } + + iio_push_to_buffers_with_ts(indio_dev, &data, sizeof(data), + iio_get_time_ns(indio_dev)); + +out: + pm_runtime_mark_last_busy(dev); + pm_runtime_put(dev); + + iio_trigger_notify_done(indio_dev->trig); + + return IRQ_HANDLED; +} + +static void rtq6056_enter_shutdown_state(void *dev) +{ + struct rtq6056_priv *priv = dev_get_drvdata(dev); + + /* Enter shutdown state */ + regmap_field_write(priv->rm_fields[F_OPMODE], 0); +} + +static bool rtq6056_is_readable_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case RTQ6056_REG_CONFIG ... RTQ6056_REG_ALERTLIMIT: + case RTQ6056_REG_MANUFACTID ... RTQ6056_REG_DIEID: + return true; + default: + return false; + } +} + +static bool rtq6056_is_writeable_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case RTQ6056_REG_CONFIG: + case RTQ6056_REG_CALIBRATION ... RTQ6056_REG_ALERTLIMIT: + return true; + default: + return false; + } +} + +static const struct regmap_config rtq6056_regmap_config = { + .reg_bits = 8, + .val_bits = 16, + .val_format_endian = REGMAP_ENDIAN_BIG, + .max_register = RTQ6056_REG_DIEID, + .readable_reg = rtq6056_is_readable_reg, + .writeable_reg = rtq6056_is_writeable_reg, +}; + +static int rtq6056_probe(struct i2c_client *i2c) +{ + struct iio_dev *indio_dev; + struct rtq6056_priv *priv; + struct device *dev = &i2c->dev; + struct regmap *regmap; + const struct richtek_dev_data *devdata; + unsigned int vendor_id, shunt_resistor_uohm; + int ret; + + if (!i2c_check_functionality(i2c->adapter, I2C_FUNC_SMBUS_WORD_DATA)) + return -EOPNOTSUPP; + + devdata = i2c_get_match_data(i2c); + if (!devdata) + return dev_err_probe(dev, -EINVAL, "Invalid dev data\n"); + + indio_dev = devm_iio_device_alloc(dev, sizeof(*priv)); + if (!indio_dev) + return -ENOMEM; + + priv = iio_priv(indio_dev); + priv->dev = dev; + priv->vshuntct_us = priv->vbusct_us = devdata->default_conv_time_us; + priv->avg_sample = 1; + priv->devdata = devdata; + i2c_set_clientdata(i2c, priv); + + regmap = devm_regmap_init_i2c(i2c, &rtq6056_regmap_config); + if (IS_ERR(regmap)) + return dev_err_probe(dev, PTR_ERR(regmap), + "Failed to init regmap\n"); + + priv->regmap = regmap; + + ret = regmap_read(regmap, RTQ6056_REG_MANUFACTID, &vendor_id); + if (ret) + return dev_err_probe(dev, ret, + "Failed to get manufacturer info\n"); + + if (vendor_id != RTQ6056_VENDOR_ID) + return dev_err_probe(dev, -ENODEV, + "Invalid vendor id 0x%04x\n", vendor_id); + + ret = devm_regmap_field_bulk_alloc(dev, regmap, priv->rm_fields, + devdata->reg_fields, F_MAX_FIELDS); + if (ret) + return dev_err_probe(dev, ret, "Failed to init regmap field\n"); + + ret = regmap_write(regmap, RTQ6056_REG_CONFIG, devdata->default_config); + if (ret) + return dev_err_probe(dev, ret, + "Failed to enable continuous sensing\n"); + + ret = devm_add_action_or_reset(dev, rtq6056_enter_shutdown_state, dev); + if (ret) + return ret; + + pm_runtime_set_autosuspend_delay(dev, MSEC_PER_SEC); + pm_runtime_use_autosuspend(dev); + pm_runtime_set_active(dev); + pm_runtime_mark_last_busy(dev); + ret = devm_pm_runtime_enable(dev); + if (ret) + return dev_err_probe(dev, ret, "Failed to enable pm_runtime\n"); + + /* By default, use 2000 micro-Ohm resistor */ + shunt_resistor_uohm = 2000; + device_property_read_u32(dev, "shunt-resistor-micro-ohms", + &shunt_resistor_uohm); + + ret = rtq6056_set_shunt_resistor(priv, shunt_resistor_uohm); + if (ret) + return dev_err_probe(dev, ret, + "Failed to init shunt resistor\n"); + + indio_dev->name = "rtq6056"; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = devdata->channels; + indio_dev->num_channels = devdata->num_channels; + indio_dev->info = &rtq6056_info; + + ret = devm_iio_triggered_buffer_setup(dev, indio_dev, NULL, + rtq6056_buffer_trigger_handler, + NULL); + if (ret) + return dev_err_probe(dev, ret, + "Failed to allocate iio trigger buffer\n"); + + return devm_iio_device_register(dev, indio_dev); +} + +static int rtq6056_runtime_suspend(struct device *dev) +{ + struct rtq6056_priv *priv = dev_get_drvdata(dev); + + /* Configure to shutdown mode */ + return regmap_field_write(priv->rm_fields[F_OPMODE], 0); +} + +static int rtq6056_runtime_resume(struct device *dev) +{ + struct rtq6056_priv *priv = dev_get_drvdata(dev); + int sample_rdy_time_us, ret; + + ret = regmap_field_write(priv->rm_fields[F_OPMODE], RTQ6056_CONT_ALLON); + if (ret) + return ret; + + sample_rdy_time_us = priv->vbusct_us + priv->vshuntct_us; + sample_rdy_time_us *= priv->avg_sample; + + usleep_range(sample_rdy_time_us, sample_rdy_time_us + 100); + + return 0; +} + +static DEFINE_RUNTIME_DEV_PM_OPS(rtq6056_pm_ops, rtq6056_runtime_suspend, + rtq6056_runtime_resume, NULL); + +static const struct richtek_dev_data rtq6056_devdata = { + .default_conv_time_us = 1037, + .calib_coefficient = 5120000, + /* + * By default, configure average sample as 1, bus and shunt conversion + * time as 1037 microsecond, and operating mode to all on. + */ + .default_config = RTQ6056_DEFAULT_CONFIG, + .avg_sample_list = rtq6056_avg_sample_list, + .avg_sample_list_length = ARRAY_SIZE(rtq6056_avg_sample_list), + .reg_fields = rtq6056_reg_fields, + .channels = rtq6056_channels, + .num_channels = ARRAY_SIZE(rtq6056_channels), + .read_scale = rtq6056_adc_read_scale, + .set_average = rtq6056_adc_set_average, +}; + +static const struct richtek_dev_data rtq6059_devdata = { + .fixed_samp_freq = true, + .vbus_offset = RTQ6059_VBUS_LSB_OFFSET, + .default_conv_time_us = 532, + .calib_coefficient = 40960000, + /* + * By default, configure average sample as 1, bus and shunt conversion + * time as 532 microsecond, and operating mode to all on. + */ + .default_config = RTQ6059_DEFAULT_CONFIG, + .avg_sample_list = rtq6059_avg_sample_list, + .avg_sample_list_length = ARRAY_SIZE(rtq6059_avg_sample_list), + .reg_fields = rtq6059_reg_fields, + .channels = rtq6059_channels, + .num_channels = ARRAY_SIZE(rtq6059_channels), + .read_scale = rtq6059_adc_read_scale, + .set_average = rtq6059_adc_set_average, +}; + +static const struct i2c_device_id rtq6056_id[] = { + { .name = "rtq6056", .driver_data = (kernel_ulong_t)&rtq6056_devdata }, + { .name = "rtq6059", .driver_data = (kernel_ulong_t)&rtq6059_devdata }, + { } +}; +MODULE_DEVICE_TABLE(i2c, rtq6056_id); + +static const struct of_device_id rtq6056_device_match[] = { + { .compatible = "richtek,rtq6056", .data = &rtq6056_devdata }, + { .compatible = "richtek,rtq6059", .data = &rtq6059_devdata }, + { } +}; +MODULE_DEVICE_TABLE(of, rtq6056_device_match); + +static struct i2c_driver rtq6056_driver = { + .driver = { + .name = "rtq6056", + .of_match_table = rtq6056_device_match, + .pm = pm_ptr(&rtq6056_pm_ops), + }, + .probe = rtq6056_probe, + .id_table = rtq6056_id, +}; +module_i2c_driver(rtq6056_driver); + +MODULE_AUTHOR("ChiYuan Huang "); +MODULE_DESCRIPTION("Richtek RTQ6056 Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/adc/rzg2l_adc.c b/drivers/iio/adc/rzg2l_adc.c new file mode 100644 index 000000000..408fbf8c2 --- /dev/null +++ b/drivers/iio/adc/rzg2l_adc.c @@ -0,0 +1,605 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * RZ/G2L A/D Converter driver + * + * Copyright (c) 2021 Renesas Electronics Europe GmbH + * + * Author: Lad Prabhakar + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DRIVER_NAME "rzg2l-adc" + +#define RZG2L_ADM(n) ((n) * 0x4) +#define RZG2L_ADM0_ADCE BIT(0) +#define RZG2L_ADM0_ADBSY BIT(1) +#define RZG2L_ADM0_PWDWNB BIT(2) +#define RZG2L_ADM0_SRESB BIT(15) +#define RZG2L_ADM1_TRG BIT(0) +#define RZG2L_ADM1_MS BIT(2) +#define RZG2L_ADM1_BS BIT(4) +#define RZG2L_ADM1_EGA_MASK GENMASK(13, 12) +#define RZG2L_ADM3_ADIL_MASK GENMASK(31, 24) +#define RZG2L_ADM3_ADCMP_MASK GENMASK(23, 16) + +#define RZG2L_ADINT 0x20 +#define RZG2L_ADINT_CSEEN BIT(16) +#define RZG2L_ADINT_INTS BIT(31) + +#define RZG2L_ADSTS 0x24 +#define RZG2L_ADSTS_CSEST BIT(16) + +#define RZG2L_ADIVC 0x28 +#define RZG2L_ADIVC_DIVADC_MASK GENMASK(8, 0) +#define RZG2L_ADIVC_DIVADC_4 FIELD_PREP(RZG2L_ADIVC_DIVADC_MASK, 0x4) + +#define RZG2L_ADFIL 0x2c + +#define RZG2L_ADCR(n) (0x30 + ((n) * 0x4)) +#define RZG2L_ADCR_AD_MASK GENMASK(11, 0) + +#define RZG2L_ADC_MAX_CHANNELS 9 +#define RZG2L_ADC_TIMEOUT usecs_to_jiffies(1 * 4) + +/** + * struct rzg2l_adc_hw_params - ADC hardware specific parameters + * @default_adsmp: default ADC sampling period (see ADM3 register); index 0 is + * used for voltage channels, index 1 is used for temperature channel + * @adsmp_mask: ADC sampling period mask (see ADM3 register) + * @adint_inten_mask: conversion end interrupt mask (see ADINT register) + * @default_adcmp: default ADC cmp (see ADM3 register) + * @num_channels: number of supported channels + * @adivc: specifies if ADVIC register is available + */ +struct rzg2l_adc_hw_params { + u16 default_adsmp[2]; + u16 adsmp_mask; + u16 adint_inten_mask; + u8 default_adcmp; + u8 num_channels; + bool adivc; +}; + +struct rzg2l_adc_data { + const struct iio_chan_spec *channels; + u8 num_channels; +}; + +struct rzg2l_adc { + void __iomem *base; + struct reset_control *presetn; + struct reset_control *adrstn; + const struct rzg2l_adc_data *data; + const struct rzg2l_adc_hw_params *hw_params; + struct completion completion; + struct mutex lock; + u16 last_val[RZG2L_ADC_MAX_CHANNELS]; +}; + +/** + * struct rzg2l_adc_channel - ADC channel descriptor + * @name: ADC channel name + * @type: ADC channel type + */ +struct rzg2l_adc_channel { + const char * const name; + enum iio_chan_type type; +}; + +static const struct rzg2l_adc_channel rzg2l_adc_channels[] = { + { "adc0", IIO_VOLTAGE }, + { "adc1", IIO_VOLTAGE }, + { "adc2", IIO_VOLTAGE }, + { "adc3", IIO_VOLTAGE }, + { "adc4", IIO_VOLTAGE }, + { "adc5", IIO_VOLTAGE }, + { "adc6", IIO_VOLTAGE }, + { "adc7", IIO_VOLTAGE }, + { "adc8", IIO_TEMP }, +}; + +static unsigned int rzg2l_adc_readl(struct rzg2l_adc *adc, u32 reg) +{ + return readl(adc->base + reg); +} + +static void rzg2l_adc_writel(struct rzg2l_adc *adc, unsigned int reg, u32 val) +{ + writel(val, adc->base + reg); +} + +static void rzg2l_adc_pwr(struct rzg2l_adc *adc, bool on) +{ + u32 reg; + + reg = rzg2l_adc_readl(adc, RZG2L_ADM(0)); + if (on) + reg |= RZG2L_ADM0_PWDWNB; + else + reg &= ~RZG2L_ADM0_PWDWNB; + rzg2l_adc_writel(adc, RZG2L_ADM(0), reg); + udelay(2); +} + +static void rzg2l_adc_start_stop(struct rzg2l_adc *adc, bool start) +{ + int ret; + u32 reg; + + reg = rzg2l_adc_readl(adc, RZG2L_ADM(0)); + if (start) + reg |= RZG2L_ADM0_ADCE; + else + reg &= ~RZG2L_ADM0_ADCE; + rzg2l_adc_writel(adc, RZG2L_ADM(0), reg); + + if (start) + return; + + ret = read_poll_timeout(rzg2l_adc_readl, reg, !(reg & (RZG2L_ADM0_ADBSY | RZG2L_ADM0_ADCE)), + 200, 1000, true, adc, RZG2L_ADM(0)); + if (ret) + pr_err("%s stopping ADC timed out\n", __func__); +} + +static void rzg2l_set_trigger(struct rzg2l_adc *adc) +{ + u32 reg; + + /* + * Setup ADM1 for SW trigger + * EGA[13:12] - Set 00 to indicate hardware trigger is invalid + * BS[4] - Enable 1-buffer mode + * MS[1] - Enable Select mode + * TRG[0] - Enable software trigger mode + */ + reg = rzg2l_adc_readl(adc, RZG2L_ADM(1)); + reg &= ~RZG2L_ADM1_EGA_MASK; + reg &= ~RZG2L_ADM1_BS; + reg &= ~RZG2L_ADM1_TRG; + reg |= RZG2L_ADM1_MS; + rzg2l_adc_writel(adc, RZG2L_ADM(1), reg); +} + +static u8 rzg2l_adc_ch_to_adsmp_index(u8 ch) +{ + if (rzg2l_adc_channels[ch].type == IIO_VOLTAGE) + return 0; + + return 1; +} + +static int rzg2l_adc_conversion_setup(struct rzg2l_adc *adc, u8 ch) +{ + const struct rzg2l_adc_hw_params *hw_params = adc->hw_params; + u8 index = rzg2l_adc_ch_to_adsmp_index(ch); + u32 reg; + + if (rzg2l_adc_readl(adc, RZG2L_ADM(0)) & RZG2L_ADM0_ADBSY) + return -EBUSY; + + rzg2l_set_trigger(adc); + + /* Select analog input channel subjected to conversion. */ + reg = rzg2l_adc_readl(adc, RZG2L_ADM(2)); + reg &= ~GENMASK(hw_params->num_channels - 1, 0); + reg |= BIT(ch); + rzg2l_adc_writel(adc, RZG2L_ADM(2), reg); + + reg = rzg2l_adc_readl(adc, RZG2L_ADM(3)); + reg &= ~hw_params->adsmp_mask; + reg |= hw_params->default_adsmp[index]; + rzg2l_adc_writel(adc, RZG2L_ADM(3), reg); + + /* + * Setup ADINT + * INTS[31] - Select pulse signal + * CSEEN[16] - Enable channel select error interrupt + * INTEN[7:0] - Select channel interrupt + */ + reg = rzg2l_adc_readl(adc, RZG2L_ADINT); + reg &= ~RZG2L_ADINT_INTS; + reg &= ~hw_params->adint_inten_mask; + reg |= (RZG2L_ADINT_CSEEN | BIT(ch)); + rzg2l_adc_writel(adc, RZG2L_ADINT, reg); + + return 0; +} + +static int rzg2l_adc_conversion(struct iio_dev *indio_dev, struct rzg2l_adc *adc, u8 ch) +{ + const struct rzg2l_adc_hw_params *hw_params = adc->hw_params; + struct device *dev = indio_dev->dev.parent; + int ret; + + ret = pm_runtime_resume_and_get(dev); + if (ret) + return ret; + + ret = rzg2l_adc_conversion_setup(adc, ch); + if (ret) + goto rpm_put; + + reinit_completion(&adc->completion); + + rzg2l_adc_start_stop(adc, true); + + if (!wait_for_completion_timeout(&adc->completion, RZG2L_ADC_TIMEOUT)) { + rzg2l_adc_writel(adc, RZG2L_ADINT, + rzg2l_adc_readl(adc, RZG2L_ADINT) & ~hw_params->adint_inten_mask); + ret = -ETIMEDOUT; + } + + rzg2l_adc_start_stop(adc, false); + +rpm_put: + pm_runtime_put_autosuspend(dev); + return ret; +} + +static int rzg2l_adc_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct rzg2l_adc *adc = iio_priv(indio_dev); + int ret; + + switch (mask) { + case IIO_CHAN_INFO_RAW: { + if (chan->type != IIO_VOLTAGE && chan->type != IIO_TEMP) + return -EINVAL; + + guard(mutex)(&adc->lock); + + ret = rzg2l_adc_conversion(indio_dev, adc, chan->channel); + if (ret) + return ret; + + *val = adc->last_val[chan->channel]; + + return IIO_VAL_INT; + } + + default: + return -EINVAL; + } +} + +static int rzg2l_adc_read_label(struct iio_dev *iio_dev, + const struct iio_chan_spec *chan, + char *label) +{ + return sysfs_emit(label, "%s\n", rzg2l_adc_channels[chan->channel].name); +} + +static const struct iio_info rzg2l_adc_iio_info = { + .read_raw = rzg2l_adc_read_raw, + .read_label = rzg2l_adc_read_label, +}; + +static irqreturn_t rzg2l_adc_isr(int irq, void *dev_id) +{ + struct rzg2l_adc *adc = dev_id; + const struct rzg2l_adc_hw_params *hw_params = adc->hw_params; + unsigned long intst; + u32 reg; + int ch; + + reg = rzg2l_adc_readl(adc, RZG2L_ADSTS); + + /* A/D conversion channel select error interrupt */ + if (reg & RZG2L_ADSTS_CSEST) { + rzg2l_adc_writel(adc, RZG2L_ADSTS, reg); + return IRQ_HANDLED; + } + + intst = reg & GENMASK(hw_params->num_channels - 1, 0); + if (!intst) + return IRQ_NONE; + + for_each_set_bit(ch, &intst, hw_params->num_channels) + adc->last_val[ch] = rzg2l_adc_readl(adc, RZG2L_ADCR(ch)) & RZG2L_ADCR_AD_MASK; + + /* clear the channel interrupt */ + rzg2l_adc_writel(adc, RZG2L_ADSTS, reg); + + complete(&adc->completion); + + return IRQ_HANDLED; +} + +static const struct iio_chan_spec rzg2l_adc_chan_template = { + .indexed = 1, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), +}; + +static int rzg2l_adc_parse_properties(struct platform_device *pdev, struct rzg2l_adc *adc) +{ + const struct rzg2l_adc_hw_params *hw_params = adc->hw_params; + struct iio_chan_spec *chan_array; + struct rzg2l_adc_data *data; + int num_channels; + u8 i; + + data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + num_channels = devm_iio_adc_device_alloc_chaninfo_se(&pdev->dev, + &rzg2l_adc_chan_template, + hw_params->num_channels - 1, + &chan_array); + if (num_channels < 0) + return num_channels; + + if (num_channels > hw_params->num_channels) + return dev_err_probe(&pdev->dev, -EINVAL, + "num of channel children out of range\n"); + + for (i = 0; i < num_channels; i++) { + int channel = chan_array[i].channel; + + chan_array[i].datasheet_name = rzg2l_adc_channels[channel].name; + chan_array[i].type = rzg2l_adc_channels[channel].type; + } + + data->num_channels = num_channels; + data->channels = chan_array; + adc->data = data; + + return 0; +} + +static int rzg2l_adc_hw_init(struct device *dev, struct rzg2l_adc *adc) +{ + const struct rzg2l_adc_hw_params *hw_params = adc->hw_params; + u32 reg; + int ret; + + ret = pm_runtime_resume_and_get(dev); + if (ret) + return ret; + + /* SW reset */ + reg = rzg2l_adc_readl(adc, RZG2L_ADM(0)); + reg |= RZG2L_ADM0_SRESB; + rzg2l_adc_writel(adc, RZG2L_ADM(0), reg); + + ret = read_poll_timeout(rzg2l_adc_readl, reg, reg & RZG2L_ADM0_SRESB, + 200, 1000, false, adc, RZG2L_ADM(0)); + if (ret) + goto exit_hw_init; + + if (hw_params->adivc) { + /* Only division by 4 can be set */ + reg = rzg2l_adc_readl(adc, RZG2L_ADIVC); + reg &= ~RZG2L_ADIVC_DIVADC_MASK; + reg |= RZG2L_ADIVC_DIVADC_4; + rzg2l_adc_writel(adc, RZG2L_ADIVC, reg); + } + + /* + * Setup AMD3 + * ADIL[31:24] - Should be always set to 0 + * ADCMP[23:16] - Should be always set to 0xe + * ADSMP[15:0] - Set default (0x578) sampling period + */ + reg = rzg2l_adc_readl(adc, RZG2L_ADM(3)); + reg &= ~RZG2L_ADM3_ADIL_MASK; + reg &= ~RZG2L_ADM3_ADCMP_MASK; + reg &= ~hw_params->adsmp_mask; + reg |= FIELD_PREP(RZG2L_ADM3_ADCMP_MASK, hw_params->default_adcmp) | + hw_params->default_adsmp[0]; + + rzg2l_adc_writel(adc, RZG2L_ADM(3), reg); + +exit_hw_init: + pm_runtime_put_autosuspend(dev); + return ret; +} + +static int rzg2l_adc_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct iio_dev *indio_dev; + struct rzg2l_adc *adc; + int ret; + int irq; + + indio_dev = devm_iio_device_alloc(dev, sizeof(*adc)); + if (!indio_dev) + return -ENOMEM; + + platform_set_drvdata(pdev, indio_dev); + + adc = iio_priv(indio_dev); + + adc->hw_params = device_get_match_data(dev); + if (!adc->hw_params || adc->hw_params->num_channels > RZG2L_ADC_MAX_CHANNELS) + return -EINVAL; + + ret = rzg2l_adc_parse_properties(pdev, adc); + if (ret) + return ret; + + mutex_init(&adc->lock); + + adc->base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(adc->base)) + return PTR_ERR(adc->base); + + adc->adrstn = devm_reset_control_get_exclusive_deasserted(dev, "adrst-n"); + if (IS_ERR(adc->adrstn)) + return dev_err_probe(dev, PTR_ERR(adc->adrstn), + "failed to get/deassert adrst-n\n"); + + adc->presetn = devm_reset_control_get_exclusive_deasserted(dev, "presetn"); + if (IS_ERR(adc->presetn)) + return dev_err_probe(dev, PTR_ERR(adc->presetn), + "failed to get/deassert presetn\n"); + + pm_runtime_set_autosuspend_delay(dev, 300); + pm_runtime_use_autosuspend(dev); + ret = devm_pm_runtime_enable(dev); + if (ret) + return ret; + + ret = rzg2l_adc_hw_init(dev, adc); + if (ret) + return dev_err_probe(&pdev->dev, ret, + "failed to initialize ADC HW\n"); + + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return irq; + + ret = devm_request_irq(dev, irq, rzg2l_adc_isr, + 0, dev_name(dev), adc); + if (ret < 0) + return ret; + + init_completion(&adc->completion); + + indio_dev->name = DRIVER_NAME; + indio_dev->info = &rzg2l_adc_iio_info; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = adc->data->channels; + indio_dev->num_channels = adc->data->num_channels; + + return devm_iio_device_register(dev, indio_dev); +} + +static const struct rzg2l_adc_hw_params rzg2l_hw_params = { + .num_channels = 8, + .default_adcmp = 0xe, + .default_adsmp = { 0x578 }, + .adsmp_mask = GENMASK(15, 0), + .adint_inten_mask = GENMASK(7, 0), + .adivc = true +}; + +static const struct rzg2l_adc_hw_params rzg3s_hw_params = { + .num_channels = 9, + .default_adcmp = 0x1d, + .default_adsmp = { 0x7f, 0xff }, + .adsmp_mask = GENMASK(7, 0), + .adint_inten_mask = GENMASK(11, 0), +}; + +static const struct of_device_id rzg2l_adc_match[] = { + { .compatible = "renesas,r9a08g045-adc", .data = &rzg3s_hw_params }, + { .compatible = "renesas,rzg2l-adc", .data = &rzg2l_hw_params }, + { } +}; +MODULE_DEVICE_TABLE(of, rzg2l_adc_match); + +static int rzg2l_adc_pm_runtime_suspend(struct device *dev) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct rzg2l_adc *adc = iio_priv(indio_dev); + + rzg2l_adc_pwr(adc, false); + + return 0; +} + +static int rzg2l_adc_pm_runtime_resume(struct device *dev) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct rzg2l_adc *adc = iio_priv(indio_dev); + + rzg2l_adc_pwr(adc, true); + + return 0; +} + +static int rzg2l_adc_suspend(struct device *dev) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct rzg2l_adc *adc = iio_priv(indio_dev); + struct reset_control_bulk_data resets[] = { + { .rstc = adc->presetn }, + { .rstc = adc->adrstn }, + }; + int ret; + + ret = pm_runtime_force_suspend(dev); + if (ret) + return ret; + + ret = reset_control_bulk_assert(ARRAY_SIZE(resets), resets); + if (ret) + goto rpm_restore; + + return 0; + +rpm_restore: + pm_runtime_force_resume(dev); + return ret; +} + +static int rzg2l_adc_resume(struct device *dev) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct rzg2l_adc *adc = iio_priv(indio_dev); + struct reset_control_bulk_data resets[] = { + { .rstc = adc->adrstn }, + { .rstc = adc->presetn }, + }; + int ret; + + ret = reset_control_bulk_deassert(ARRAY_SIZE(resets), resets); + if (ret) + return ret; + + ret = pm_runtime_force_resume(dev); + if (ret) + goto resets_restore; + + ret = rzg2l_adc_hw_init(dev, adc); + if (ret) + goto rpm_restore; + + return 0; + +rpm_restore: + pm_runtime_force_suspend(dev); +resets_restore: + reset_control_bulk_assert(ARRAY_SIZE(resets), resets); + return ret; +} + +static const struct dev_pm_ops rzg2l_adc_pm_ops = { + RUNTIME_PM_OPS(rzg2l_adc_pm_runtime_suspend, rzg2l_adc_pm_runtime_resume, NULL) + SYSTEM_SLEEP_PM_OPS(rzg2l_adc_suspend, rzg2l_adc_resume) +}; + +static struct platform_driver rzg2l_adc_driver = { + .probe = rzg2l_adc_probe, + .driver = { + .name = DRIVER_NAME, + .of_match_table = rzg2l_adc_match, + .pm = pm_ptr(&rzg2l_adc_pm_ops), + }, +}; + +module_platform_driver(rzg2l_adc_driver); + +MODULE_AUTHOR("Lad Prabhakar "); +MODULE_DESCRIPTION("Renesas RZ/G2L ADC driver"); +MODULE_LICENSE("GPL v2"); +MODULE_IMPORT_NS("IIO_DRIVER"); diff --git a/drivers/iio/adc/rzn1-adc.c b/drivers/iio/adc/rzn1-adc.c new file mode 100644 index 000000000..f921cd49b --- /dev/null +++ b/drivers/iio/adc/rzn1-adc.c @@ -0,0 +1,489 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Renesas RZ/N1 ADC driver + * + * Copyright (C) 2025 Schneider-Electric + * + * Author: Herve Codina + * + * The RZ/N1 ADC controller can handle channels from its internal ADC1 and/or + * ADC2 cores. The driver use ADC1 and/or ADC2 cores depending on the presence + * of the related power supplies (AVDD and VREF) description in the device-tree. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define RZN1_ADC_CONTROL_REG 0x02c +#define RZN1_ADC_CONTROL_ADC_BUSY BIT(6) + +#define RZN1_ADC_FORCE_REG 0x030 +#define RZN1_ADC_SET_FORCE_REG 0x034 +#define RZN1_ADC_CLEAR_FORCE_REG 0x038 +#define RZN1_ADC_FORCE_VC(_n) BIT(_n) + +#define RZN1_ADC_CONFIG_REG 0x040 +#define RZN1_ADC_CONFIG_ADC_POWER_DOWN BIT(3) + +#define RZN1_ADC_VC_REG(_n) (0x0c0 + 4 * (_n)) +#define RZN1_ADC_VC_ADC2_ENABLE BIT(16) +#define RZN1_ADC_VC_ADC1_ENABLE BIT(15) +#define RZN1_ADC_VC_ADC2_CHANNEL_SEL_MASK GENMASK(5, 3) +#define RZN1_ADC_VC_ADC1_CHANNEL_SEL_MASK GENMASK(2, 0) + +#define RZN1_ADC_ADC1_DATA_REG(_n) (0x100 + 4 * (_n)) +#define RZN1_ADC_ADC2_DATA_REG(_n) (0x140 + 4 * (_n)) +#define RZN1_ADC_ADCX_DATA_DATA_MASK GENMASK(11, 0) + +#define RZN1_ADC_NO_CHANNEL -1 + +#define RZN1_ADC_CHANNEL_SHARED_SCALE(_ch, _ds_name) { \ + .type = IIO_VOLTAGE, \ + .indexed = 1, \ + .channel = (_ch), \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ + .datasheet_name = (_ds_name), \ +} + +#define RZN1_ADC_CHANNEL_SEPARATED_SCALE(_ch, _ds_name) { \ + .type = IIO_VOLTAGE, \ + .indexed = 1, \ + .channel = (_ch), \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ + BIT(IIO_CHAN_INFO_SCALE), \ + .datasheet_name = (_ds_name), \ +} + +/* + * 8 ADC1_IN signals existed numbered 0..4, 6..8 + * ADCx_IN5 doesn't exist in RZ/N1 datasheet + */ +static struct iio_chan_spec rzn1_adc1_channels[] = { + RZN1_ADC_CHANNEL_SHARED_SCALE(0, "ADC1_IN0"), + RZN1_ADC_CHANNEL_SHARED_SCALE(1, "ADC1_IN1"), + RZN1_ADC_CHANNEL_SHARED_SCALE(2, "ADC1_IN2"), + RZN1_ADC_CHANNEL_SHARED_SCALE(3, "ADC1_IN3"), + RZN1_ADC_CHANNEL_SHARED_SCALE(4, "ADC1_IN4"), + RZN1_ADC_CHANNEL_SHARED_SCALE(5, "ADC1_IN6"), + RZN1_ADC_CHANNEL_SHARED_SCALE(6, "ADC1_IN7"), + RZN1_ADC_CHANNEL_SHARED_SCALE(7, "ADC1_IN8"), +}; + +static struct iio_chan_spec rzn1_adc2_channels[] = { + RZN1_ADC_CHANNEL_SHARED_SCALE(8, "ADC2_IN0"), + RZN1_ADC_CHANNEL_SHARED_SCALE(9, "ADC2_IN1"), + RZN1_ADC_CHANNEL_SHARED_SCALE(10, "ADC2_IN2"), + RZN1_ADC_CHANNEL_SHARED_SCALE(11, "ADC2_IN3"), + RZN1_ADC_CHANNEL_SHARED_SCALE(12, "ADC2_IN4"), + RZN1_ADC_CHANNEL_SHARED_SCALE(13, "ADC2_IN6"), + RZN1_ADC_CHANNEL_SHARED_SCALE(14, "ADC2_IN7"), + RZN1_ADC_CHANNEL_SHARED_SCALE(15, "ADC2_IN8"), +}; + +/* + * If both ADCs core are used, scale cannot be common. Indeed, scale is + * based on Vref connected on each ADC core. + */ +static struct iio_chan_spec rzn1_adc1_adc2_channels[] = { + RZN1_ADC_CHANNEL_SEPARATED_SCALE(0, "ADC1_IN0"), + RZN1_ADC_CHANNEL_SEPARATED_SCALE(1, "ADC1_IN1"), + RZN1_ADC_CHANNEL_SEPARATED_SCALE(2, "ADC1_IN2"), + RZN1_ADC_CHANNEL_SEPARATED_SCALE(3, "ADC1_IN3"), + RZN1_ADC_CHANNEL_SEPARATED_SCALE(4, "ADC1_IN4"), + RZN1_ADC_CHANNEL_SEPARATED_SCALE(5, "ADC1_IN6"), + RZN1_ADC_CHANNEL_SEPARATED_SCALE(6, "ADC1_IN7"), + RZN1_ADC_CHANNEL_SEPARATED_SCALE(7, "ADC1_IN8"), + RZN1_ADC_CHANNEL_SEPARATED_SCALE(8, "ADC2_IN0"), + RZN1_ADC_CHANNEL_SEPARATED_SCALE(9, "ADC2_IN1"), + RZN1_ADC_CHANNEL_SEPARATED_SCALE(10, "ADC2_IN2"), + RZN1_ADC_CHANNEL_SEPARATED_SCALE(11, "ADC2_IN3"), + RZN1_ADC_CHANNEL_SEPARATED_SCALE(12, "ADC2_IN4"), + RZN1_ADC_CHANNEL_SEPARATED_SCALE(13, "ADC2_IN6"), + RZN1_ADC_CHANNEL_SEPARATED_SCALE(14, "ADC2_IN7"), + RZN1_ADC_CHANNEL_SEPARATED_SCALE(15, "ADC2_IN8"), +}; + +struct rzn1_adc { + struct device *dev; + void __iomem *regs; + struct mutex lock; /* ADC lock */ + int adc1_vref_mV; /* ADC1 Vref in mV. Negative if ADC1 is not used */ + int adc2_vref_mV; /* ADC2 Vref in mV. Negative if ADC2 is not used */ +}; + +static int rzn1_adc_power(struct rzn1_adc *rzn1_adc, bool power) +{ + u32 v; + + writel(power ? 0 : RZN1_ADC_CONFIG_ADC_POWER_DOWN, + rzn1_adc->regs + RZN1_ADC_CONFIG_REG); + + /* Wait for the ADC_BUSY to clear */ + return readl_poll_timeout_atomic(rzn1_adc->regs + RZN1_ADC_CONTROL_REG, + v, !(v & RZN1_ADC_CONTROL_ADC_BUSY), + 0, 500); +} + +static void rzn1_adc_vc_setup_conversion(struct rzn1_adc *rzn1_adc, u32 ch, + int adc1_ch, int adc2_ch) +{ + u32 vc = 0; + + if (adc1_ch != RZN1_ADC_NO_CHANNEL) + vc |= RZN1_ADC_VC_ADC1_ENABLE | + FIELD_PREP(RZN1_ADC_VC_ADC1_CHANNEL_SEL_MASK, adc1_ch); + + if (adc2_ch != RZN1_ADC_NO_CHANNEL) + vc |= RZN1_ADC_VC_ADC2_ENABLE | + FIELD_PREP(RZN1_ADC_VC_ADC2_CHANNEL_SEL_MASK, adc2_ch); + + writel(vc, rzn1_adc->regs + RZN1_ADC_VC_REG(ch)); +} + +static int rzn1_adc_vc_start_conversion(struct rzn1_adc *rzn1_adc, u32 ch) +{ + u32 val; + + val = readl(rzn1_adc->regs + RZN1_ADC_FORCE_REG); + if (val & RZN1_ADC_FORCE_VC(ch)) + return -EBUSY; + + writel(RZN1_ADC_FORCE_VC(ch), rzn1_adc->regs + RZN1_ADC_SET_FORCE_REG); + + return 0; +} + +static void rzn1_adc_vc_stop_conversion(struct rzn1_adc *rzn1_adc, u32 ch) +{ + writel(RZN1_ADC_FORCE_VC(ch), rzn1_adc->regs + RZN1_ADC_CLEAR_FORCE_REG); +} + +static int rzn1_adc_vc_wait_conversion(struct rzn1_adc *rzn1_adc, u32 ch, + u32 *adc1_data, u32 *adc2_data) +{ + u32 data_reg; + int ret; + u32 v; + + /* + * When a VC is selected, it needs 20 ADC clocks to perform the + * conversion. + * + * The worst case is when the 16 VCs need to perform a conversion and + * our VC is the lowest in term of priority. + * + * In that case, the conversion is performed in 16 * 20 ADC clocks. + * + * The ADC clock can be set from 4MHz to 20MHz. This leads to a worst + * case of 16 * 20 * 1/4Mhz = 80us. + * + * Round it up to 100us. + */ + + /* Wait for the ADC_FORCE_VC(n) to clear */ + ret = readl_poll_timeout_atomic(rzn1_adc->regs + RZN1_ADC_FORCE_REG, + v, !(v & RZN1_ADC_FORCE_VC(ch)), + 0, 100); + if (ret) + return ret; + + if (adc1_data) { + data_reg = readl(rzn1_adc->regs + RZN1_ADC_ADC1_DATA_REG(ch)); + *adc1_data = FIELD_GET(RZN1_ADC_ADCX_DATA_DATA_MASK, data_reg); + } + + if (adc2_data) { + data_reg = readl(rzn1_adc->regs + RZN1_ADC_ADC2_DATA_REG(ch)); + *adc2_data = FIELD_GET(RZN1_ADC_ADCX_DATA_DATA_MASK, data_reg); + } + + return 0; +} + +static int rzn1_adc_read_raw_ch(struct rzn1_adc *rzn1_adc, unsigned int chan, int *val) +{ + u32 *adc1_data, *adc2_data; + int adc1_ch, adc2_ch; + u32 adc_data; + int ret; + + /* + * IIO chan are decoupled from chans used in rzn1_adc_vc_*() functions. + * The RZ/N1 ADC VC controller can handle on a single VC chan one + * channel from the ADC1 core and one channel from the ADC2 core. + * + * Even if IIO chans are mapped 1:1 to ADC core chans and so uses only + * a chan from ADC1 or a chan from ADC2, future improvements can define + * an IIO chan that uses one chan from ADC1 and one chan from ADC2. + */ + + if (chan < 8) { + /* chan 0..7 used to get ADC1 ch 0..7 */ + adc1_ch = chan; + adc1_data = &adc_data; + adc2_ch = RZN1_ADC_NO_CHANNEL; + adc2_data = NULL; + } else if (chan < 16) { + /* chan 8..15 used to get ADC2 ch 0..7 */ + adc1_ch = RZN1_ADC_NO_CHANNEL; + adc1_data = NULL; + adc2_ch = chan - 8; + adc2_data = &adc_data; + } else { + return -EINVAL; + } + + ACQUIRE(pm_runtime_active_auto_try_enabled, pm)(rzn1_adc->dev); + ret = ACQUIRE_ERR(pm_runtime_active_auto_try_enabled, &pm); + if (ret < 0) + return ret; + + scoped_guard(mutex, &rzn1_adc->lock) { + rzn1_adc_vc_setup_conversion(rzn1_adc, chan, adc1_ch, adc2_ch); + + ret = rzn1_adc_vc_start_conversion(rzn1_adc, chan); + if (ret) + return ret; + + ret = rzn1_adc_vc_wait_conversion(rzn1_adc, chan, adc1_data, adc2_data); + if (ret) { + rzn1_adc_vc_stop_conversion(rzn1_adc, chan); + return ret; + } + } + + *val = adc_data; + ret = IIO_VAL_INT; + + return 0; +} + +static int rzn1_adc_get_vref_mV(struct rzn1_adc *rzn1_adc, unsigned int chan) +{ + /* chan 0..7 use ADC1 ch 0..7. Vref related to ADC1 core */ + if (chan < 8) + return rzn1_adc->adc1_vref_mV; + + /* chan 8..15 use ADC2 ch 0..7. Vref related to ADC2 core */ + if (chan < 16) + return rzn1_adc->adc2_vref_mV; + + return -EINVAL; +} + +static int rzn1_adc_read_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct rzn1_adc *rzn1_adc = iio_priv(indio_dev); + int ret; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + ret = rzn1_adc_read_raw_ch(rzn1_adc, chan->channel, val); + if (ret) + return ret; + return IIO_VAL_INT; + + case IIO_CHAN_INFO_SCALE: + ret = rzn1_adc_get_vref_mV(rzn1_adc, chan->channel); + if (ret < 0) + return ret; + *val = ret; + *val2 = 12; + return IIO_VAL_FRACTIONAL_LOG2; + + default: + return -EINVAL; + } +} + +static const struct iio_info rzn1_adc_info = { + .read_raw = &rzn1_adc_read_raw, +}; + +static int rzn1_adc_set_iio_dev_channels(struct rzn1_adc *rzn1_adc, + struct iio_dev *indio_dev) +{ + /* + * When an ADC core is not used, its related vref_mV is set to a + * negative error code. Use the correct IIO channels table based on + * those vref_mV values. + */ + if (rzn1_adc->adc1_vref_mV >= 0) { + if (rzn1_adc->adc2_vref_mV >= 0) { + indio_dev->channels = rzn1_adc1_adc2_channels; + indio_dev->num_channels = ARRAY_SIZE(rzn1_adc1_adc2_channels); + } else { + indio_dev->channels = rzn1_adc1_channels; + indio_dev->num_channels = ARRAY_SIZE(rzn1_adc1_channels); + } + return 0; + } + + if (rzn1_adc->adc2_vref_mV >= 0) { + indio_dev->channels = rzn1_adc2_channels; + indio_dev->num_channels = ARRAY_SIZE(rzn1_adc2_channels); + return 0; + } + + return dev_err_probe(rzn1_adc->dev, -ENODEV, + "Failed to set IIO channels, no ADC core used\n"); +} + +static int rzn1_adc_core_get_regulators(struct rzn1_adc *rzn1_adc, + int *adc_vref_mV, + const char *avdd_name, const char *vref_name) +{ + struct device *dev = rzn1_adc->dev; + int ret; + + /* + * For a given ADC core (ADC1 or ADC2), both regulators (AVDD and VREF) + * must be available in order to have the ADC core used. + * + * We use the regulators presence to check the usage of the related + * ADC core. If both regulators are available, the ADC core is used. + * Otherwise, the ADC core is not used. + * + * The adc_vref_mV value is set to a negative error code (-ENODEV) when + * the ADC core is not used. Otherwise it is set to the VRef mV value. + */ + + *adc_vref_mV = -ENODEV; + + ret = devm_regulator_get_enable_optional(dev, avdd_name); + if (ret == -ENODEV) + return 0; + if (ret < 0) + return dev_err_probe(dev, ret, "Failed to get '%s' regulator\n", + avdd_name); + + ret = devm_regulator_get_enable_read_voltage(dev, vref_name); + if (ret == -ENODEV) + return 0; + if (ret < 0) + return dev_err_probe(dev, ret, "Failed to get '%s' regulator\n", + vref_name); + + /* + * Both regulators are available. + * Set adc_vref_mV to the Vref value in mV. This, as the value set is + * positive, also signals that the ADC is used. + */ + *adc_vref_mV = ret / 1000; + + return 0; +} + +static int rzn1_adc_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct iio_dev *indio_dev; + struct rzn1_adc *rzn1_adc; + struct clk *clk; + int ret; + + indio_dev = devm_iio_device_alloc(dev, sizeof(*rzn1_adc)); + if (!indio_dev) + return -ENOMEM; + + rzn1_adc = iio_priv(indio_dev); + rzn1_adc->dev = dev; + + ret = devm_mutex_init(dev, &rzn1_adc->lock); + if (ret) + return ret; + + rzn1_adc->regs = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(rzn1_adc->regs)) + return PTR_ERR(rzn1_adc->regs); + + clk = devm_clk_get_enabled(dev, "pclk"); + if (IS_ERR(clk)) + return dev_err_probe(dev, PTR_ERR(clk), "Failed to get pclk\n"); + + clk = devm_clk_get_enabled(dev, "adc"); + if (IS_ERR(clk)) + return dev_err_probe(dev, PTR_ERR(clk), "Failed to get adc clk\n"); + + ret = rzn1_adc_core_get_regulators(rzn1_adc, &rzn1_adc->adc1_vref_mV, + "adc1-avdd", "adc1-vref"); + if (ret) + return ret; + + ret = rzn1_adc_core_get_regulators(rzn1_adc, &rzn1_adc->adc2_vref_mV, + "adc2-avdd", "adc2-vref"); + if (ret) + return ret; + + platform_set_drvdata(pdev, rzn1_adc); + + indio_dev->name = "rzn1-adc"; + indio_dev->info = &rzn1_adc_info; + indio_dev->modes = INDIO_DIRECT_MODE; + ret = rzn1_adc_set_iio_dev_channels(rzn1_adc, indio_dev); + if (ret) + return ret; + + pm_runtime_set_autosuspend_delay(dev, 500); + pm_runtime_use_autosuspend(dev); + ret = devm_pm_runtime_enable(dev); + if (ret) + return dev_err_probe(dev, ret, "Failed to enable runtime PM\n"); + + return devm_iio_device_register(dev, indio_dev); +} + +static int rzn1_adc_pm_runtime_suspend(struct device *dev) +{ + struct rzn1_adc *rzn1_adc = dev_get_drvdata(dev); + + return rzn1_adc_power(rzn1_adc, false); +} + +static int rzn1_adc_pm_runtime_resume(struct device *dev) +{ + struct rzn1_adc *rzn1_adc = dev_get_drvdata(dev); + + return rzn1_adc_power(rzn1_adc, true); +} + +static DEFINE_RUNTIME_DEV_PM_OPS(rzn1_adc_pm_ops, + rzn1_adc_pm_runtime_suspend, + rzn1_adc_pm_runtime_resume, + NULL); + +static const struct of_device_id rzn1_adc_of_match[] = { + { .compatible = "renesas,rzn1-adc" }, + { } +}; +MODULE_DEVICE_TABLE(of, rzn1_adc_of_match); + +static struct platform_driver rzn1_adc_driver = { + .probe = rzn1_adc_probe, + .driver = { + .name = "rzn1-adc", + .of_match_table = rzn1_adc_of_match, + .pm = pm_ptr(&rzn1_adc_pm_ops), + }, +}; +module_platform_driver(rzn1_adc_driver); + +MODULE_AUTHOR("Herve Codina "); +MODULE_DESCRIPTION("Renesas RZ/N1 ADC Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/iio/adc/rzt2h_adc.c b/drivers/iio/adc/rzt2h_adc.c new file mode 100644 index 000000000..4e0eb02d3 --- /dev/null +++ b/drivers/iio/adc/rzt2h_adc.c @@ -0,0 +1,303 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define RZT2H_ADCSR_REG 0x00 +#define RZT2H_ADCSR_ADIE_MASK BIT(12) +#define RZT2H_ADCSR_ADCS_MASK GENMASK(14, 13) +#define RZT2H_ADCSR_ADCS_SINGLE 0b00 +#define RZT2H_ADCSR_ADST_MASK BIT(15) + +#define RZT2H_ADANSA0_REG 0x04 +#define RZT2H_ADANSA0_CH_MASK(x) BIT(x) + +#define RZT2H_ADDR_REG(x) (0x20 + 0x2 * (x)) + +#define RZT2H_ADCALCTL_REG 0x1f0 +#define RZT2H_ADCALCTL_CAL_MASK BIT(0) +#define RZT2H_ADCALCTL_CAL_RDY_MASK BIT(1) +#define RZT2H_ADCALCTL_CAL_ERR_MASK BIT(2) + +#define RZT2H_ADC_MAX_CHANNELS 16 + +struct rzt2h_adc { + void __iomem *base; + struct device *dev; + + struct completion completion; + /* lock to protect against multiple access to the device */ + struct mutex lock; + + const struct iio_chan_spec *channels; + unsigned int num_channels; + unsigned int max_channels; +}; + +static void rzt2h_adc_start(struct rzt2h_adc *adc, unsigned int conversion_type) +{ + u16 reg; + + reg = readw(adc->base + RZT2H_ADCSR_REG); + + /* Set conversion type */ + FIELD_MODIFY(RZT2H_ADCSR_ADCS_MASK, ®, conversion_type); + + /* Set end of conversion interrupt and start bit. */ + reg |= RZT2H_ADCSR_ADIE_MASK | RZT2H_ADCSR_ADST_MASK; + + writew(reg, adc->base + RZT2H_ADCSR_REG); +} + +static void rzt2h_adc_stop(struct rzt2h_adc *adc) +{ + u16 reg; + + reg = readw(adc->base + RZT2H_ADCSR_REG); + + /* Clear end of conversion interrupt and start bit. */ + reg &= ~(RZT2H_ADCSR_ADIE_MASK | RZT2H_ADCSR_ADST_MASK); + + writew(reg, adc->base + RZT2H_ADCSR_REG); +} + +static int rzt2h_adc_read_single(struct rzt2h_adc *adc, unsigned int ch, int *val) +{ + int ret; + + ret = pm_runtime_resume_and_get(adc->dev); + if (ret) + return ret; + + mutex_lock(&adc->lock); + + reinit_completion(&adc->completion); + + /* Enable a single channel */ + writew(RZT2H_ADANSA0_CH_MASK(ch), adc->base + RZT2H_ADANSA0_REG); + + rzt2h_adc_start(adc, RZT2H_ADCSR_ADCS_SINGLE); + + /* + * Datasheet Page 2770, Table 41.1: + * 0.32us per channel when sample-and-hold circuits are not in use. + */ + ret = wait_for_completion_timeout(&adc->completion, usecs_to_jiffies(1)); + if (!ret) { + ret = -ETIMEDOUT; + goto disable; + } + + *val = readw(adc->base + RZT2H_ADDR_REG(ch)); + ret = IIO_VAL_INT; + +disable: + rzt2h_adc_stop(adc); + + mutex_unlock(&adc->lock); + + pm_runtime_put_autosuspend(adc->dev); + + return ret; +} + +static void rzt2h_adc_set_cal(struct rzt2h_adc *adc, bool cal) +{ + u16 val; + + val = readw(adc->base + RZT2H_ADCALCTL_REG); + if (cal) + val |= RZT2H_ADCALCTL_CAL_MASK; + else + val &= ~RZT2H_ADCALCTL_CAL_MASK; + + writew(val, adc->base + RZT2H_ADCALCTL_REG); +} + +static int rzt2h_adc_calibrate(struct rzt2h_adc *adc) +{ + u16 val; + int ret; + + rzt2h_adc_set_cal(adc, true); + + ret = read_poll_timeout(readw, val, val & RZT2H_ADCALCTL_CAL_RDY_MASK, + 200, 1000, true, adc->base + RZT2H_ADCALCTL_REG); + if (ret) { + dev_err(adc->dev, "Calibration timed out: %d\n", ret); + return ret; + } + + rzt2h_adc_set_cal(adc, false); + + if (val & RZT2H_ADCALCTL_CAL_ERR_MASK) { + dev_err(adc->dev, "Calibration failed\n"); + return -EINVAL; + } + + return 0; +} + +static int rzt2h_adc_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct rzt2h_adc *adc = iio_priv(indio_dev); + + switch (mask) { + case IIO_CHAN_INFO_RAW: + return rzt2h_adc_read_single(adc, chan->channel, val); + case IIO_CHAN_INFO_SCALE: + *val = 1800; + *val2 = 12; + return IIO_VAL_FRACTIONAL_LOG2; + default: + return -EINVAL; + } +} + +static const struct iio_info rzt2h_adc_iio_info = { + .read_raw = rzt2h_adc_read_raw, +}; + +static irqreturn_t rzt2h_adc_isr(int irq, void *private) +{ + struct rzt2h_adc *adc = private; + + complete(&adc->completion); + + return IRQ_HANDLED; +} + +static const struct iio_chan_spec rzt2h_adc_chan_template = { + .indexed = 1, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SCALE), + .type = IIO_VOLTAGE, +}; + +static int rzt2h_adc_parse_properties(struct rzt2h_adc *adc) +{ + struct iio_chan_spec *chan_array; + unsigned int i; + int ret; + + ret = devm_iio_adc_device_alloc_chaninfo_se(adc->dev, + &rzt2h_adc_chan_template, + RZT2H_ADC_MAX_CHANNELS - 1, + &chan_array); + if (ret < 0) + return dev_err_probe(adc->dev, ret, "Failed to read channel info"); + + adc->num_channels = ret; + adc->channels = chan_array; + + for (i = 0; i < adc->num_channels; i++) + if (chan_array[i].channel + 1 > adc->max_channels) + adc->max_channels = chan_array[i].channel + 1; + + return 0; +} + +static int rzt2h_adc_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct iio_dev *indio_dev; + struct rzt2h_adc *adc; + int ret, irq; + + indio_dev = devm_iio_device_alloc(dev, sizeof(*adc)); + if (!indio_dev) + return -ENOMEM; + + adc = iio_priv(indio_dev); + adc->dev = dev; + init_completion(&adc->completion); + + ret = devm_mutex_init(dev, &adc->lock); + if (ret) + return ret; + + platform_set_drvdata(pdev, adc); + + ret = rzt2h_adc_parse_properties(adc); + if (ret) + return ret; + + adc->base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(adc->base)) + return PTR_ERR(adc->base); + + pm_runtime_set_autosuspend_delay(dev, 300); + pm_runtime_use_autosuspend(dev); + ret = devm_pm_runtime_enable(dev); + if (ret) + return ret; + + irq = platform_get_irq_byname(pdev, "adi"); + if (irq < 0) + return irq; + + ret = devm_request_irq(dev, irq, rzt2h_adc_isr, 0, dev_name(dev), adc); + if (ret) + return ret; + + indio_dev->name = "rzt2h-adc"; + indio_dev->info = &rzt2h_adc_iio_info; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = adc->channels; + indio_dev->num_channels = adc->num_channels; + + return devm_iio_device_register(dev, indio_dev); +} + +static const struct of_device_id rzt2h_adc_match[] = { + { .compatible = "renesas,r9a09g077-adc" }, + { } +}; +MODULE_DEVICE_TABLE(of, rzt2h_adc_match); + +static int rzt2h_adc_pm_runtime_resume(struct device *dev) +{ + struct rzt2h_adc *adc = dev_get_drvdata(dev); + + /* + * Datasheet Page 2810, Section 41.5.6: + * After release from the module-stop state, wait for at least + * 0.5 µs before starting A/D conversion. + */ + fsleep(1); + + return rzt2h_adc_calibrate(adc); +} + +static const struct dev_pm_ops rzt2h_adc_pm_ops = { + RUNTIME_PM_OPS(NULL, rzt2h_adc_pm_runtime_resume, NULL) +}; + +static struct platform_driver rzt2h_adc_driver = { + .probe = rzt2h_adc_probe, + .driver = { + .name = "rzt2h-adc", + .of_match_table = rzt2h_adc_match, + .pm = pm_ptr(&rzt2h_adc_pm_ops), + }, +}; + +module_platform_driver(rzt2h_adc_driver); + +MODULE_AUTHOR("Cosmin Tanislav "); +MODULE_DESCRIPTION("Renesas RZ/T2H / RZ/N2H ADC driver"); +MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS("IIO_DRIVER"); diff --git a/drivers/iio/adc/sc27xx_adc.c b/drivers/iio/adc/sc27xx_adc.c new file mode 100644 index 000000000..6209499c5 --- /dev/null +++ b/drivers/iio/adc/sc27xx_adc.c @@ -0,0 +1,954 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (C) 2018 Spreadtrum Communications Inc. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* PMIC global registers definition */ +#define SC2730_MODULE_EN 0x1808 +#define SC2731_MODULE_EN 0xc08 +#define SC27XX_MODULE_ADC_EN BIT(5) +#define SC2721_ARM_CLK_EN 0xc0c +#define SC2730_ARM_CLK_EN 0x180c +#define SC2731_ARM_CLK_EN 0xc10 +#define SC27XX_CLK_ADC_EN BIT(5) +#define SC27XX_CLK_ADC_CLK_EN BIT(6) + +/* ADC controller registers definition */ +#define SC27XX_ADC_CTL 0x0 +#define SC27XX_ADC_CH_CFG 0x4 +#define SC27XX_ADC_DATA 0x4c +#define SC27XX_ADC_INT_EN 0x50 +#define SC27XX_ADC_INT_CLR 0x54 +#define SC27XX_ADC_INT_STS 0x58 +#define SC27XX_ADC_INT_RAW 0x5c + +/* Bits and mask definition for SC27XX_ADC_CTL register */ +#define SC27XX_ADC_EN BIT(0) +#define SC27XX_ADC_CHN_RUN BIT(1) +#define SC27XX_ADC_12BIT_MODE BIT(2) +#define SC27XX_ADC_RUN_NUM_MASK GENMASK(7, 4) +#define SC27XX_ADC_RUN_NUM_SHIFT 4 + +/* Bits and mask definition for SC27XX_ADC_CH_CFG register */ +#define SC27XX_ADC_CHN_ID_MASK GENMASK(4, 0) +#define SC27XX_ADC_SCALE_MASK GENMASK(10, 9) +#define SC2721_ADC_SCALE_MASK BIT(5) +#define SC27XX_ADC_SCALE_SHIFT 9 +#define SC2721_ADC_SCALE_SHIFT 5 + +/* Bits definitions for SC27XX_ADC_INT_EN registers */ +#define SC27XX_ADC_IRQ_EN BIT(0) + +/* Bits definitions for SC27XX_ADC_INT_CLR registers */ +#define SC27XX_ADC_IRQ_CLR BIT(0) + +/* Bits definitions for SC27XX_ADC_INT_RAW registers */ +#define SC27XX_ADC_IRQ_RAW BIT(0) + +/* Mask definition for SC27XX_ADC_DATA register */ +#define SC27XX_ADC_DATA_MASK GENMASK(11, 0) + +/* Timeout (ms) for the trylock of hardware spinlocks */ +#define SC27XX_ADC_HWLOCK_TIMEOUT 5000 + +/* Timeout (us) for ADC data conversion according to ADC datasheet */ +#define SC27XX_ADC_RDY_TIMEOUT 1000000 +#define SC27XX_ADC_POLL_RAW_STATUS 500 + +/* Maximum ADC channel number */ +#define SC27XX_ADC_CHANNEL_MAX 32 + +/* ADC voltage ratio definition */ +#define SC27XX_VOLT_RATIO(n, d) \ + (((n) << SC27XX_RATIO_NUMERATOR_OFFSET) | (d)) +#define SC27XX_RATIO_NUMERATOR_OFFSET 16 +#define SC27XX_RATIO_DENOMINATOR_MASK GENMASK(15, 0) + +/* ADC specific channel reference voltage 3.5V */ +#define SC27XX_ADC_REFVOL_VDD35 3500000 + +/* ADC default channel reference voltage is 2.8V */ +#define SC27XX_ADC_REFVOL_VDD28 2800000 + +struct sc27xx_adc_data { + struct device *dev; + struct regulator *volref; + struct regmap *regmap; + /* lock to protect against multiple access to the device */ + struct mutex lock; + /* + * One hardware spinlock to synchronize between the multiple + * subsystems which will access the unique ADC controller. + */ + struct hwspinlock *hwlock; + int channel_scale[SC27XX_ADC_CHANNEL_MAX]; + u32 base; + int irq; + const struct sc27xx_adc_variant_data *var_data; +}; + +/* + * Since different PMICs of SC27xx series can have different + * address and ratio, we should save ratio config and base + * in the device data structure. + */ +struct sc27xx_adc_variant_data { + u32 module_en; + u32 clk_en; + u32 scale_shift; + u32 scale_mask; + const struct sc27xx_adc_linear_graph *bscale_cal; + const struct sc27xx_adc_linear_graph *sscale_cal; + void (*init_scale)(struct sc27xx_adc_data *data); + int (*get_ratio)(int channel, int scale); + bool set_volref; +}; + +struct sc27xx_adc_linear_graph { + int volt0; + int adc0; + int volt1; + int adc1; +}; + +/* + * According to the datasheet, we can convert one ADC value to one voltage value + * through 2 points in the linear graph. If the voltage is less than 1.2v, we + * should use the small-scale graph, and if more than 1.2v, we should use the + * big-scale graph. + */ +static struct sc27xx_adc_linear_graph big_scale_graph = { + 4200, 3310, + 3600, 2832, +}; + +static struct sc27xx_adc_linear_graph small_scale_graph = { + 1000, 3413, + 100, 341, +}; + +static const struct sc27xx_adc_linear_graph sc2731_big_scale_graph_calib = { + 4200, 850, + 3600, 728, +}; + +static const struct sc27xx_adc_linear_graph sc2731_small_scale_graph_calib = { + 1000, 838, + 100, 84, +}; + +static const struct sc27xx_adc_linear_graph big_scale_graph_calib = { + 4200, 856, + 3600, 733, +}; + +static const struct sc27xx_adc_linear_graph small_scale_graph_calib = { + 1000, 833, + 100, 80, +}; + +static int sc27xx_adc_get_calib_data(u32 calib_data, int calib_adc) +{ + return ((calib_data & 0xff) + calib_adc - 128) * 4; +} + +/* get the adc nvmem cell calibration data */ +static int adc_nvmem_cell_calib_data(struct sc27xx_adc_data *data, const char *cell_name) +{ + struct nvmem_cell *cell; + void *buf; + u32 origin_calib_data = 0; + size_t len; + + if (!data) + return -EINVAL; + + cell = nvmem_cell_get(data->dev, cell_name); + if (IS_ERR(cell)) + return PTR_ERR(cell); + + buf = nvmem_cell_read(cell, &len); + if (IS_ERR(buf)) { + nvmem_cell_put(cell); + return PTR_ERR(buf); + } + + memcpy(&origin_calib_data, buf, min(len, sizeof(u32))); + + kfree(buf); + nvmem_cell_put(cell); + return origin_calib_data; +} + +static int sc27xx_adc_scale_calibration(struct sc27xx_adc_data *data, + bool big_scale) +{ + const struct sc27xx_adc_linear_graph *calib_graph; + struct sc27xx_adc_linear_graph *graph; + const char *cell_name; + u32 calib_data = 0; + + if (big_scale) { + calib_graph = data->var_data->bscale_cal; + graph = &big_scale_graph; + cell_name = "big_scale_calib"; + } else { + calib_graph = data->var_data->sscale_cal; + graph = &small_scale_graph; + cell_name = "small_scale_calib"; + } + + calib_data = adc_nvmem_cell_calib_data(data, cell_name); + + /* Only need to calibrate the adc values in the linear graph. */ + graph->adc0 = sc27xx_adc_get_calib_data(calib_data, calib_graph->adc0); + graph->adc1 = sc27xx_adc_get_calib_data(calib_data >> 8, + calib_graph->adc1); + + return 0; +} + +static int sc2720_adc_get_ratio(int channel, int scale) +{ + switch (channel) { + case 14: + switch (scale) { + case 0: + return SC27XX_VOLT_RATIO(68, 900); + case 1: + return SC27XX_VOLT_RATIO(68, 1760); + case 2: + return SC27XX_VOLT_RATIO(68, 2327); + case 3: + return SC27XX_VOLT_RATIO(68, 3654); + default: + return SC27XX_VOLT_RATIO(1, 1); + } + case 16: + switch (scale) { + case 0: + return SC27XX_VOLT_RATIO(48, 100); + case 1: + return SC27XX_VOLT_RATIO(480, 1955); + case 2: + return SC27XX_VOLT_RATIO(480, 2586); + case 3: + return SC27XX_VOLT_RATIO(48, 406); + default: + return SC27XX_VOLT_RATIO(1, 1); + } + case 21: + case 22: + case 23: + switch (scale) { + case 0: + return SC27XX_VOLT_RATIO(3, 8); + case 1: + return SC27XX_VOLT_RATIO(375, 1955); + case 2: + return SC27XX_VOLT_RATIO(375, 2586); + case 3: + return SC27XX_VOLT_RATIO(300, 3248); + default: + return SC27XX_VOLT_RATIO(1, 1); + } + default: + switch (scale) { + case 0: + return SC27XX_VOLT_RATIO(1, 1); + case 1: + return SC27XX_VOLT_RATIO(1000, 1955); + case 2: + return SC27XX_VOLT_RATIO(1000, 2586); + case 3: + return SC27XX_VOLT_RATIO(100, 406); + default: + return SC27XX_VOLT_RATIO(1, 1); + } + } + return SC27XX_VOLT_RATIO(1, 1); +} + +static int sc2721_adc_get_ratio(int channel, int scale) +{ + switch (channel) { + case 1: + case 2: + case 3: + case 4: + return scale ? SC27XX_VOLT_RATIO(400, 1025) : + SC27XX_VOLT_RATIO(1, 1); + case 5: + return SC27XX_VOLT_RATIO(7, 29); + case 7: + case 9: + return scale ? SC27XX_VOLT_RATIO(100, 125) : + SC27XX_VOLT_RATIO(1, 1); + case 14: + return SC27XX_VOLT_RATIO(68, 900); + case 16: + return SC27XX_VOLT_RATIO(48, 100); + case 19: + return SC27XX_VOLT_RATIO(1, 3); + default: + return SC27XX_VOLT_RATIO(1, 1); + } + return SC27XX_VOLT_RATIO(1, 1); +} + +static int sc2730_adc_get_ratio(int channel, int scale) +{ + switch (channel) { + case 14: + switch (scale) { + case 0: + return SC27XX_VOLT_RATIO(68, 900); + case 1: + return SC27XX_VOLT_RATIO(68, 1760); + case 2: + return SC27XX_VOLT_RATIO(68, 2327); + case 3: + return SC27XX_VOLT_RATIO(68, 3654); + default: + return SC27XX_VOLT_RATIO(1, 1); + } + case 15: + switch (scale) { + case 0: + return SC27XX_VOLT_RATIO(1, 3); + case 1: + return SC27XX_VOLT_RATIO(1000, 5865); + case 2: + return SC27XX_VOLT_RATIO(500, 3879); + case 3: + return SC27XX_VOLT_RATIO(500, 6090); + default: + return SC27XX_VOLT_RATIO(1, 1); + } + case 16: + switch (scale) { + case 0: + return SC27XX_VOLT_RATIO(48, 100); + case 1: + return SC27XX_VOLT_RATIO(480, 1955); + case 2: + return SC27XX_VOLT_RATIO(480, 2586); + case 3: + return SC27XX_VOLT_RATIO(48, 406); + default: + return SC27XX_VOLT_RATIO(1, 1); + } + case 21: + case 22: + case 23: + switch (scale) { + case 0: + return SC27XX_VOLT_RATIO(3, 8); + case 1: + return SC27XX_VOLT_RATIO(375, 1955); + case 2: + return SC27XX_VOLT_RATIO(375, 2586); + case 3: + return SC27XX_VOLT_RATIO(300, 3248); + default: + return SC27XX_VOLT_RATIO(1, 1); + } + default: + switch (scale) { + case 0: + return SC27XX_VOLT_RATIO(1, 1); + case 1: + return SC27XX_VOLT_RATIO(1000, 1955); + case 2: + return SC27XX_VOLT_RATIO(1000, 2586); + case 3: + return SC27XX_VOLT_RATIO(1000, 4060); + default: + return SC27XX_VOLT_RATIO(1, 1); + } + } + return SC27XX_VOLT_RATIO(1, 1); +} + +static int sc2731_adc_get_ratio(int channel, int scale) +{ + switch (channel) { + case 1: + case 2: + case 3: + case 4: + return scale ? SC27XX_VOLT_RATIO(400, 1025) : + SC27XX_VOLT_RATIO(1, 1); + case 5: + return SC27XX_VOLT_RATIO(7, 29); + case 6: + return SC27XX_VOLT_RATIO(375, 9000); + case 7: + case 8: + return scale ? SC27XX_VOLT_RATIO(100, 125) : + SC27XX_VOLT_RATIO(1, 1); + case 19: + return SC27XX_VOLT_RATIO(1, 3); + default: + return SC27XX_VOLT_RATIO(1, 1); + } + return SC27XX_VOLT_RATIO(1, 1); +} + +/* + * According to the datasheet set specific value on some channel. + */ +static void sc2720_adc_scale_init(struct sc27xx_adc_data *data) +{ + int i; + + for (i = 0; i < SC27XX_ADC_CHANNEL_MAX; i++) { + switch (i) { + case 5: + data->channel_scale[i] = 3; + break; + case 7: + case 9: + data->channel_scale[i] = 2; + break; + case 13: + data->channel_scale[i] = 1; + break; + case 19: + case 30: + case 31: + data->channel_scale[i] = 3; + break; + default: + data->channel_scale[i] = 0; + break; + } + } +} + +static void sc2730_adc_scale_init(struct sc27xx_adc_data *data) +{ + int i; + + for (i = 0; i < SC27XX_ADC_CHANNEL_MAX; i++) { + switch (i) { + case 5: + case 10: + case 19: + case 30: + case 31: + data->channel_scale[i] = 3; + break; + case 7: + case 9: + data->channel_scale[i] = 2; + break; + case 13: + data->channel_scale[i] = 1; + break; + default: + data->channel_scale[i] = 0; + break; + } + } +} + +static void sc2731_adc_scale_init(struct sc27xx_adc_data *data) +{ + int i; + /* + * In the current software design, SC2731 support 2 scales, + * channels 5 uses big scale, others use smale. + */ + for (i = 0; i < SC27XX_ADC_CHANNEL_MAX; i++) { + switch (i) { + case 5: + data->channel_scale[i] = 1; + break; + default: + data->channel_scale[i] = 0; + break; + } + } +} + +static int sc27xx_adc_read(struct sc27xx_adc_data *data, int channel, + int scale, int *val) +{ + int ret, ret_volref; + u32 tmp, value, status; + + ret = hwspin_lock_timeout_raw(data->hwlock, SC27XX_ADC_HWLOCK_TIMEOUT); + if (ret) { + dev_err(data->dev, "timeout to get the hwspinlock\n"); + return ret; + } + + /* + * According to the sc2721 chip data sheet, the reference voltage of + * specific channel 30 and channel 31 in ADC module needs to be set from + * the default 2.8v to 3.5v. + */ + if ((data->var_data->set_volref) && (channel == 30 || channel == 31)) { + ret = regulator_set_voltage(data->volref, + SC27XX_ADC_REFVOL_VDD35, + SC27XX_ADC_REFVOL_VDD35); + if (ret) { + dev_err(data->dev, "failed to set the volref 3.5v\n"); + goto unlock_adc; + } + } + + ret = regmap_set_bits(data->regmap, data->base + SC27XX_ADC_CTL, + SC27XX_ADC_EN); + if (ret) + goto regulator_restore; + + ret = regmap_set_bits(data->regmap, data->base + SC27XX_ADC_INT_CLR, + SC27XX_ADC_IRQ_CLR); + if (ret) + goto disable_adc; + + /* Configure the channel id and scale */ + tmp = (scale << data->var_data->scale_shift) & data->var_data->scale_mask; + tmp |= channel & SC27XX_ADC_CHN_ID_MASK; + ret = regmap_update_bits(data->regmap, data->base + SC27XX_ADC_CH_CFG, + SC27XX_ADC_CHN_ID_MASK | + data->var_data->scale_mask, + tmp); + if (ret) + goto disable_adc; + + /* Select 12bit conversion mode, and only sample 1 time */ + tmp = SC27XX_ADC_12BIT_MODE; + tmp |= (0 << SC27XX_ADC_RUN_NUM_SHIFT) & SC27XX_ADC_RUN_NUM_MASK; + ret = regmap_update_bits(data->regmap, data->base + SC27XX_ADC_CTL, + SC27XX_ADC_RUN_NUM_MASK | SC27XX_ADC_12BIT_MODE, + tmp); + if (ret) + goto disable_adc; + + ret = regmap_set_bits(data->regmap, data->base + SC27XX_ADC_CTL, + SC27XX_ADC_CHN_RUN); + if (ret) + goto disable_adc; + + ret = regmap_read_poll_timeout(data->regmap, + data->base + SC27XX_ADC_INT_RAW, + status, (status & SC27XX_ADC_IRQ_RAW), + SC27XX_ADC_POLL_RAW_STATUS, + SC27XX_ADC_RDY_TIMEOUT); + if (ret) { + dev_err(data->dev, "read adc timeout, status = 0x%x\n", status); + goto disable_adc; + } + + ret = regmap_read(data->regmap, data->base + SC27XX_ADC_DATA, &value); + if (ret) + goto disable_adc; + + value &= SC27XX_ADC_DATA_MASK; + +disable_adc: + regmap_clear_bits(data->regmap, data->base + SC27XX_ADC_CTL, + SC27XX_ADC_EN); +regulator_restore: + if ((data->var_data->set_volref) && (channel == 30 || channel == 31)) { + ret_volref = regulator_set_voltage(data->volref, + SC27XX_ADC_REFVOL_VDD28, + SC27XX_ADC_REFVOL_VDD28); + if (ret_volref) { + dev_err(data->dev, "failed to set the volref 2.8v,ret_volref = 0x%x\n", + ret_volref); + ret = ret || ret_volref; + } + } +unlock_adc: + hwspin_unlock_raw(data->hwlock); + + if (!ret) + *val = value; + + return ret; +} + +static void sc27xx_adc_volt_ratio(struct sc27xx_adc_data *data, int channel, int scale, + struct u32_fract *fract) +{ + u32 ratio; + + ratio = data->var_data->get_ratio(channel, scale); + fract->numerator = ratio >> SC27XX_RATIO_NUMERATOR_OFFSET; + fract->denominator = ratio & SC27XX_RATIO_DENOMINATOR_MASK; +} + +static int adc_to_volt(struct sc27xx_adc_linear_graph *graph, + int raw_adc) +{ + int tmp; + + tmp = (graph->volt0 - graph->volt1) * (raw_adc - graph->adc1); + tmp /= (graph->adc0 - graph->adc1); + tmp += graph->volt1; + + return tmp; +} + +static int sc27xx_adc_to_volt(struct sc27xx_adc_linear_graph *graph, + int raw_adc) +{ + int tmp; + + tmp = adc_to_volt(graph, raw_adc); + + return tmp < 0 ? 0 : tmp; +} + +static int sc27xx_adc_convert_volt(struct sc27xx_adc_data *data, int channel, + int scale, int raw_adc) +{ + struct u32_fract fract; + u32 volt; + + /* + * Convert ADC values to voltage values according to the linear graph, + * and channel 5 and channel 1 has been calibrated, so we can just + * return the voltage values calculated by the linear graph. But other + * channels need be calculated to the real voltage values with the + * voltage ratio. + */ + switch (channel) { + case 5: + return sc27xx_adc_to_volt(&big_scale_graph, raw_adc); + + case 1: + return sc27xx_adc_to_volt(&small_scale_graph, raw_adc); + + default: + volt = sc27xx_adc_to_volt(&small_scale_graph, raw_adc); + break; + } + + sc27xx_adc_volt_ratio(data, channel, scale, &fract); + + return DIV_ROUND_CLOSEST(volt * fract.denominator, fract.numerator); +} + +static int sc27xx_adc_read_processed(struct sc27xx_adc_data *data, + int channel, int scale, int *val) +{ + int ret, raw_adc; + + ret = sc27xx_adc_read(data, channel, scale, &raw_adc); + if (ret) + return ret; + + *val = sc27xx_adc_convert_volt(data, channel, scale, raw_adc); + return 0; +} + +static int sc27xx_adc_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct sc27xx_adc_data *data = iio_priv(indio_dev); + int scale = data->channel_scale[chan->channel]; + int ret, tmp; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + mutex_lock(&data->lock); + ret = sc27xx_adc_read(data, chan->channel, scale, &tmp); + mutex_unlock(&data->lock); + + if (ret) + return ret; + + *val = tmp; + return IIO_VAL_INT; + + case IIO_CHAN_INFO_PROCESSED: + mutex_lock(&data->lock); + ret = sc27xx_adc_read_processed(data, chan->channel, scale, + &tmp); + mutex_unlock(&data->lock); + + if (ret) + return ret; + + *val = tmp; + return IIO_VAL_INT; + + case IIO_CHAN_INFO_SCALE: + *val = scale; + return IIO_VAL_INT; + + default: + return -EINVAL; + } +} + +static int sc27xx_adc_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, int val2, long mask) +{ + struct sc27xx_adc_data *data = iio_priv(indio_dev); + + switch (mask) { + case IIO_CHAN_INFO_SCALE: + data->channel_scale[chan->channel] = val; + return IIO_VAL_INT; + + default: + return -EINVAL; + } +} + +static const struct iio_info sc27xx_info = { + .read_raw = &sc27xx_adc_read_raw, + .write_raw = &sc27xx_adc_write_raw, +}; + +#define SC27XX_ADC_CHANNEL(index, mask) { \ + .type = IIO_VOLTAGE, \ + .channel = index, \ + .info_mask_separate = mask | BIT(IIO_CHAN_INFO_SCALE), \ + .datasheet_name = "CH##index", \ + .indexed = 1, \ +} + +static const struct iio_chan_spec sc27xx_channels[] = { + SC27XX_ADC_CHANNEL(0, BIT(IIO_CHAN_INFO_PROCESSED)), + SC27XX_ADC_CHANNEL(1, BIT(IIO_CHAN_INFO_PROCESSED)), + SC27XX_ADC_CHANNEL(2, BIT(IIO_CHAN_INFO_PROCESSED)), + SC27XX_ADC_CHANNEL(3, BIT(IIO_CHAN_INFO_PROCESSED)), + SC27XX_ADC_CHANNEL(4, BIT(IIO_CHAN_INFO_PROCESSED)), + SC27XX_ADC_CHANNEL(5, BIT(IIO_CHAN_INFO_PROCESSED)), + SC27XX_ADC_CHANNEL(6, BIT(IIO_CHAN_INFO_PROCESSED)), + SC27XX_ADC_CHANNEL(7, BIT(IIO_CHAN_INFO_PROCESSED)), + SC27XX_ADC_CHANNEL(8, BIT(IIO_CHAN_INFO_PROCESSED)), + SC27XX_ADC_CHANNEL(9, BIT(IIO_CHAN_INFO_PROCESSED)), + SC27XX_ADC_CHANNEL(10, BIT(IIO_CHAN_INFO_PROCESSED)), + SC27XX_ADC_CHANNEL(11, BIT(IIO_CHAN_INFO_PROCESSED)), + SC27XX_ADC_CHANNEL(12, BIT(IIO_CHAN_INFO_PROCESSED)), + SC27XX_ADC_CHANNEL(13, BIT(IIO_CHAN_INFO_PROCESSED)), + SC27XX_ADC_CHANNEL(14, BIT(IIO_CHAN_INFO_PROCESSED)), + SC27XX_ADC_CHANNEL(15, BIT(IIO_CHAN_INFO_PROCESSED)), + SC27XX_ADC_CHANNEL(16, BIT(IIO_CHAN_INFO_PROCESSED)), + SC27XX_ADC_CHANNEL(17, BIT(IIO_CHAN_INFO_PROCESSED)), + SC27XX_ADC_CHANNEL(18, BIT(IIO_CHAN_INFO_PROCESSED)), + SC27XX_ADC_CHANNEL(19, BIT(IIO_CHAN_INFO_PROCESSED)), + SC27XX_ADC_CHANNEL(20, BIT(IIO_CHAN_INFO_RAW)), + SC27XX_ADC_CHANNEL(21, BIT(IIO_CHAN_INFO_PROCESSED)), + SC27XX_ADC_CHANNEL(22, BIT(IIO_CHAN_INFO_PROCESSED)), + SC27XX_ADC_CHANNEL(23, BIT(IIO_CHAN_INFO_PROCESSED)), + SC27XX_ADC_CHANNEL(24, BIT(IIO_CHAN_INFO_PROCESSED)), + SC27XX_ADC_CHANNEL(25, BIT(IIO_CHAN_INFO_PROCESSED)), + SC27XX_ADC_CHANNEL(26, BIT(IIO_CHAN_INFO_PROCESSED)), + SC27XX_ADC_CHANNEL(27, BIT(IIO_CHAN_INFO_PROCESSED)), + SC27XX_ADC_CHANNEL(28, BIT(IIO_CHAN_INFO_PROCESSED)), + SC27XX_ADC_CHANNEL(29, BIT(IIO_CHAN_INFO_PROCESSED)), + SC27XX_ADC_CHANNEL(30, BIT(IIO_CHAN_INFO_PROCESSED)), + SC27XX_ADC_CHANNEL(31, BIT(IIO_CHAN_INFO_PROCESSED)), +}; + +static int sc27xx_adc_enable(struct sc27xx_adc_data *data) +{ + int ret; + + ret = regmap_set_bits(data->regmap, data->var_data->module_en, + SC27XX_MODULE_ADC_EN); + if (ret) + return ret; + + /* Enable ADC work clock and controller clock */ + ret = regmap_set_bits(data->regmap, data->var_data->clk_en, + SC27XX_CLK_ADC_EN | SC27XX_CLK_ADC_CLK_EN); + if (ret) + goto disable_adc; + + /* ADC channel scales' calibration from nvmem device */ + ret = sc27xx_adc_scale_calibration(data, true); + if (ret) + goto disable_clk; + + ret = sc27xx_adc_scale_calibration(data, false); + if (ret) + goto disable_clk; + + return 0; + +disable_clk: + regmap_clear_bits(data->regmap, data->var_data->clk_en, + SC27XX_CLK_ADC_EN | SC27XX_CLK_ADC_CLK_EN); +disable_adc: + regmap_clear_bits(data->regmap, data->var_data->module_en, + SC27XX_MODULE_ADC_EN); + + return ret; +} + +static void sc27xx_adc_disable(void *_data) +{ + struct sc27xx_adc_data *data = _data; + + /* Disable ADC work clock and controller clock */ + regmap_clear_bits(data->regmap, data->var_data->clk_en, + SC27XX_CLK_ADC_EN | SC27XX_CLK_ADC_CLK_EN); + + regmap_clear_bits(data->regmap, data->var_data->module_en, + SC27XX_MODULE_ADC_EN); +} + +static const struct sc27xx_adc_variant_data sc2731_data = { + .module_en = SC2731_MODULE_EN, + .clk_en = SC2731_ARM_CLK_EN, + .scale_shift = SC27XX_ADC_SCALE_SHIFT, + .scale_mask = SC27XX_ADC_SCALE_MASK, + .bscale_cal = &sc2731_big_scale_graph_calib, + .sscale_cal = &sc2731_small_scale_graph_calib, + .init_scale = sc2731_adc_scale_init, + .get_ratio = sc2731_adc_get_ratio, + .set_volref = false, +}; + +static const struct sc27xx_adc_variant_data sc2730_data = { + .module_en = SC2730_MODULE_EN, + .clk_en = SC2730_ARM_CLK_EN, + .scale_shift = SC27XX_ADC_SCALE_SHIFT, + .scale_mask = SC27XX_ADC_SCALE_MASK, + .bscale_cal = &big_scale_graph_calib, + .sscale_cal = &small_scale_graph_calib, + .init_scale = sc2730_adc_scale_init, + .get_ratio = sc2730_adc_get_ratio, + .set_volref = false, +}; + +static const struct sc27xx_adc_variant_data sc2721_data = { + .module_en = SC2731_MODULE_EN, + .clk_en = SC2721_ARM_CLK_EN, + .scale_shift = SC2721_ADC_SCALE_SHIFT, + .scale_mask = SC2721_ADC_SCALE_MASK, + .bscale_cal = &sc2731_big_scale_graph_calib, + .sscale_cal = &sc2731_small_scale_graph_calib, + .init_scale = sc2731_adc_scale_init, + .get_ratio = sc2721_adc_get_ratio, + .set_volref = true, +}; + +static const struct sc27xx_adc_variant_data sc2720_data = { + .module_en = SC2731_MODULE_EN, + .clk_en = SC2721_ARM_CLK_EN, + .scale_shift = SC27XX_ADC_SCALE_SHIFT, + .scale_mask = SC27XX_ADC_SCALE_MASK, + .bscale_cal = &big_scale_graph_calib, + .sscale_cal = &small_scale_graph_calib, + .init_scale = sc2720_adc_scale_init, + .get_ratio = sc2720_adc_get_ratio, + .set_volref = false, +}; + +static int sc27xx_adc_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct device_node *np = dev->of_node; + struct sc27xx_adc_data *sc27xx_data; + const struct sc27xx_adc_variant_data *pdata; + struct iio_dev *indio_dev; + int ret; + + pdata = of_device_get_match_data(dev); + if (!pdata) + return dev_err_probe(dev, -EINVAL, "No matching driver data found\n"); + + indio_dev = devm_iio_device_alloc(dev, sizeof(*sc27xx_data)); + if (!indio_dev) + return -ENOMEM; + + sc27xx_data = iio_priv(indio_dev); + + sc27xx_data->regmap = dev_get_regmap(dev->parent, NULL); + if (!sc27xx_data->regmap) + return dev_err_probe(dev, -ENODEV, "failed to get ADC regmap\n"); + + ret = of_property_read_u32(np, "reg", &sc27xx_data->base); + if (ret) + return dev_err_probe(dev, ret, "failed to get ADC base address\n"); + + sc27xx_data->irq = platform_get_irq(pdev, 0); + if (sc27xx_data->irq < 0) + return sc27xx_data->irq; + + ret = of_hwspin_lock_get_id(np, 0); + if (ret < 0) + return dev_err_probe(dev, ret, "failed to get hwspinlock id\n"); + + sc27xx_data->hwlock = devm_hwspin_lock_request_specific(dev, ret); + if (!sc27xx_data->hwlock) + return dev_err_probe(dev, -ENXIO, "failed to request hwspinlock\n"); + + sc27xx_data->dev = dev; + if (pdata->set_volref) { + sc27xx_data->volref = devm_regulator_get(dev, "vref"); + if (IS_ERR(sc27xx_data->volref)) + return dev_err_probe(dev, PTR_ERR(sc27xx_data->volref), + "failed to get ADC volref\n"); + } + + sc27xx_data->var_data = pdata; + sc27xx_data->var_data->init_scale(sc27xx_data); + + ret = sc27xx_adc_enable(sc27xx_data); + if (ret) + return dev_err_probe(dev, ret, "failed to enable ADC module\n"); + + ret = devm_add_action_or_reset(dev, sc27xx_adc_disable, sc27xx_data); + if (ret) + return dev_err_probe(dev, ret, "failed to add ADC disable action\n"); + + indio_dev->name = dev_name(dev); + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->info = &sc27xx_info; + indio_dev->channels = sc27xx_channels; + indio_dev->num_channels = ARRAY_SIZE(sc27xx_channels); + + mutex_init(&sc27xx_data->lock); + + ret = devm_iio_device_register(dev, indio_dev); + if (ret) + dev_err(dev, "could not register iio (ADC)"); + + return ret; +} + +static const struct of_device_id sc27xx_adc_of_match[] = { + { .compatible = "sprd,sc2731-adc", .data = &sc2731_data}, + { .compatible = "sprd,sc2730-adc", .data = &sc2730_data}, + { .compatible = "sprd,sc2721-adc", .data = &sc2721_data}, + { .compatible = "sprd,sc2720-adc", .data = &sc2720_data}, + { } +}; +MODULE_DEVICE_TABLE(of, sc27xx_adc_of_match); + +static struct platform_driver sc27xx_adc_driver = { + .probe = sc27xx_adc_probe, + .driver = { + .name = "sc27xx-adc", + .of_match_table = sc27xx_adc_of_match, + }, +}; + +module_platform_driver(sc27xx_adc_driver); + +MODULE_AUTHOR("Freeman Liu "); +MODULE_DESCRIPTION("Spreadtrum SC27XX ADC Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/adc/sd_adc_modulator.c b/drivers/iio/adc/sd_adc_modulator.c new file mode 100644 index 000000000..def44d883 --- /dev/null +++ b/drivers/iio/adc/sd_adc_modulator.c @@ -0,0 +1,162 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Generic sigma delta modulator driver + * + * Copyright (C) 2017, STMicroelectronics - All Rights Reserved + * Author: Arnaud Pouliquen . + */ + +#include +#include +#include +#include +#include +#include +#include + +static const struct iio_info iio_sd_mod_iio_info; + +static const struct iio_chan_spec iio_sd_mod_ch = { + .type = IIO_VOLTAGE, + .indexed = 1, + .scan_type = { + .sign = 'u', + .realbits = 1, + .shift = 0, + }, +}; + +struct iio_sd_backend_priv { + struct regulator *vref; + int vref_mv; +}; + +static int iio_sd_mod_enable(struct iio_backend *backend) +{ + struct iio_sd_backend_priv *priv = iio_backend_get_priv(backend); + + if (priv->vref) + return regulator_enable(priv->vref); + + return 0; +}; + +static void iio_sd_mod_disable(struct iio_backend *backend) +{ + struct iio_sd_backend_priv *priv = iio_backend_get_priv(backend); + + if (priv->vref) + regulator_disable(priv->vref); +}; + +static int iio_sd_mod_read(struct iio_backend *backend, struct iio_chan_spec const *chan, int *val, + int *val2, long mask) +{ + struct iio_sd_backend_priv *priv = iio_backend_get_priv(backend); + + switch (mask) { + case IIO_CHAN_INFO_SCALE: + *val = priv->vref_mv; + return IIO_VAL_INT; + + case IIO_CHAN_INFO_OFFSET: + *val = 0; + return IIO_VAL_INT; + } + + return -EOPNOTSUPP; +}; + +static const struct iio_backend_ops sd_backend_ops = { + .enable = iio_sd_mod_enable, + .disable = iio_sd_mod_disable, + .read_raw = iio_sd_mod_read, +}; + +static const struct iio_backend_info sd_backend_info = { + .name = "sd-modulator", + .ops = &sd_backend_ops, + .caps = IIO_BACKEND_CAP_ENABLE, +}; + +static int iio_sd_mod_register(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct iio_dev *iio; + + iio = devm_iio_device_alloc(dev, 0); + if (!iio) + return -ENOMEM; + + iio->name = dev_name(dev); + iio->info = &iio_sd_mod_iio_info; + iio->modes = INDIO_BUFFER_HARDWARE; + + iio->num_channels = 1; + iio->channels = &iio_sd_mod_ch; + + platform_set_drvdata(pdev, iio); + + return devm_iio_device_register(&pdev->dev, iio); +} + +static int iio_sd_mod_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct regulator *vref; + struct iio_sd_backend_priv *priv; + int ret; + + /* If sd modulator is not defined as an IIO backend device, fallback to legacy */ + if (!device_property_present(dev, "#io-backend-cells")) + return iio_sd_mod_register(pdev); + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + /* + * Get regulator reference if any, but don't enable regulator right now. + * Rely on enable and disable callbacks to manage regulator power. + */ + vref = devm_regulator_get_optional(dev, "vref"); + if (IS_ERR(vref)) { + if (PTR_ERR(vref) != -ENODEV) + return dev_err_probe(dev, PTR_ERR(vref), "Failed to get vref\n"); + } else { + /* + * Retrieve voltage right now, as regulator_get_voltage() provides it whatever + * the state of the regulator. + */ + ret = regulator_get_voltage(vref); + if (ret < 0) + return ret; + + priv->vref = vref; + priv->vref_mv = ret / 1000; + } + + return devm_iio_backend_register(&pdev->dev, &sd_backend_info, priv); +}; + +static const struct of_device_id sd_adc_of_match[] = { + { .compatible = "sd-modulator" }, + { .compatible = "ads1201" }, + { } +}; +MODULE_DEVICE_TABLE(of, sd_adc_of_match); + +static struct platform_driver iio_sd_mod_adc = { + .driver = { + .name = "iio_sd_adc_mod", + .of_match_table = sd_adc_of_match, + }, + .probe = iio_sd_mod_probe, +}; + +module_platform_driver(iio_sd_mod_adc); + +MODULE_DESCRIPTION("Basic sigma delta modulator"); +MODULE_AUTHOR("Arnaud Pouliquen "); +MODULE_LICENSE("GPL v2"); +MODULE_IMPORT_NS("IIO_BACKEND"); diff --git a/drivers/iio/adc/sophgo-cv1800b-adc.c b/drivers/iio/adc/sophgo-cv1800b-adc.c new file mode 100644 index 000000000..bdc3e1326 --- /dev/null +++ b/drivers/iio/adc/sophgo-cv1800b-adc.c @@ -0,0 +1,226 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Sophgo CV1800B SARADC Driver + * + * Copyright (C) Bootlin 2024 + * Author: Thomas Bonnefille + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define CV1800B_ADC_CTRL_REG 0x04 +#define CV1800B_ADC_EN BIT(0) +#define CV1800B_ADC_SEL(x) BIT((x) + 5) +#define CV1800B_ADC_STATUS_REG 0x08 +#define CV1800B_ADC_BUSY BIT(0) +#define CV1800B_ADC_CYC_SET_REG 0x0C +#define CV1800B_MASK_STARTUP_CYCLE GENMASK(4, 0) +#define CV1800B_MASK_SAMPLE_WINDOW GENMASK(11, 8) +#define CV1800B_MASK_CLKDIV GENMASK(15, 12) +#define CV1800B_MASK_COMPARE_CYCLE GENMASK(19, 16) +#define CV1800B_ADC_CH_RESULT_REG(x) (0x14 + 4 * (x)) +#define CV1800B_ADC_CH_RESULT GENMASK(11, 0) +#define CV1800B_ADC_CH_VALID BIT(15) +#define CV1800B_ADC_INTR_EN_REG 0x20 +#define CV1800B_ADC_INTR_CLR_REG 0x24 +#define CV1800B_ADC_INTR_CLR_BIT BIT(0) +#define CV1800B_ADC_INTR_STA_REG 0x28 +#define CV1800B_ADC_INTR_STA_BIT BIT(0) +#define CV1800B_READ_TIMEOUT_MS 1000 +#define CV1800B_READ_TIMEOUT_US (CV1800B_READ_TIMEOUT_MS * 1000) + +#define CV1800B_ADC_CHANNEL(index) \ + { \ + .type = IIO_VOLTAGE, \ + .indexed = 1, \ + .channel = index, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ + .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ),\ + .scan_index = index, \ + } + +struct cv1800b_adc { + struct completion completion; + void __iomem *regs; + struct mutex lock; /* ADC Control and Result register */ + struct clk *clk; + int irq; +}; + +static const struct iio_chan_spec sophgo_channels[] = { + CV1800B_ADC_CHANNEL(0), + CV1800B_ADC_CHANNEL(1), + CV1800B_ADC_CHANNEL(2), +}; + +static void cv1800b_adc_start_measurement(struct cv1800b_adc *saradc, + int channel) +{ + writel(0, saradc->regs + CV1800B_ADC_CTRL_REG); + writel(CV1800B_ADC_SEL(channel) | CV1800B_ADC_EN, + saradc->regs + CV1800B_ADC_CTRL_REG); +} + +static int cv1800b_adc_wait(struct cv1800b_adc *saradc) +{ + if (saradc->irq < 0) { + u32 reg; + + return readl_poll_timeout(saradc->regs + CV1800B_ADC_STATUS_REG, + reg, !(reg & CV1800B_ADC_BUSY), + 500, CV1800B_READ_TIMEOUT_US); + } + + return wait_for_completion_timeout(&saradc->completion, + msecs_to_jiffies(CV1800B_READ_TIMEOUT_MS)) > 0 ? + 0 : -ETIMEDOUT; +} + +static int cv1800b_adc_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct cv1800b_adc *saradc = iio_priv(indio_dev); + + switch (mask) { + case IIO_CHAN_INFO_RAW: { + u32 sample; + + scoped_guard(mutex, &saradc->lock) { + int ret; + + cv1800b_adc_start_measurement(saradc, chan->scan_index); + ret = cv1800b_adc_wait(saradc); + if (ret < 0) + return ret; + + sample = readl(saradc->regs + CV1800B_ADC_CH_RESULT_REG(chan->scan_index)); + } + if (!(sample & CV1800B_ADC_CH_VALID)) + return -ENODATA; + + *val = sample & CV1800B_ADC_CH_RESULT; + return IIO_VAL_INT; + } + case IIO_CHAN_INFO_SCALE: + *val = 3300; + *val2 = 12; + return IIO_VAL_FRACTIONAL_LOG2; + case IIO_CHAN_INFO_SAMP_FREQ: { + u32 status_reg = readl(saradc->regs + CV1800B_ADC_CYC_SET_REG); + unsigned int clk_div = (1 + FIELD_GET(CV1800B_MASK_CLKDIV, status_reg)); + unsigned int freq = clk_get_rate(saradc->clk) / clk_div; + unsigned int nb_startup_cycle = 1 + FIELD_GET(CV1800B_MASK_STARTUP_CYCLE, status_reg); + unsigned int nb_sample_cycle = 1 + FIELD_GET(CV1800B_MASK_SAMPLE_WINDOW, status_reg); + unsigned int nb_compare_cycle = 1 + FIELD_GET(CV1800B_MASK_COMPARE_CYCLE, status_reg); + + *val = freq / (nb_startup_cycle + nb_sample_cycle + nb_compare_cycle); + return IIO_VAL_INT; + } + default: + return -EINVAL; + } +} + +static irqreturn_t cv1800b_adc_interrupt_handler(int irq, void *private) +{ + struct cv1800b_adc *saradc = private; + u32 reg = readl(saradc->regs + CV1800B_ADC_INTR_STA_REG); + + if (!(FIELD_GET(CV1800B_ADC_INTR_STA_BIT, reg))) + return IRQ_NONE; + + writel(CV1800B_ADC_INTR_CLR_BIT, saradc->regs + CV1800B_ADC_INTR_CLR_REG); + complete(&saradc->completion); + + return IRQ_HANDLED; +} + +static const struct iio_info cv1800b_adc_info = { + .read_raw = &cv1800b_adc_read_raw, +}; + +static int cv1800b_adc_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct cv1800b_adc *saradc; + struct iio_dev *indio_dev; + int ret; + + indio_dev = devm_iio_device_alloc(dev, sizeof(*saradc)); + if (!indio_dev) + return -ENOMEM; + + saradc = iio_priv(indio_dev); + indio_dev->name = "sophgo-cv1800b-adc"; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->info = &cv1800b_adc_info; + indio_dev->num_channels = ARRAY_SIZE(sophgo_channels); + indio_dev->channels = sophgo_channels; + + saradc->clk = devm_clk_get_enabled(dev, NULL); + if (IS_ERR(saradc->clk)) + return PTR_ERR(saradc->clk); + + saradc->regs = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(saradc->regs)) + return PTR_ERR(saradc->regs); + + saradc->irq = platform_get_irq_optional(pdev, 0); + if (saradc->irq > 0) { + init_completion(&saradc->completion); + ret = devm_request_irq(dev, saradc->irq, + cv1800b_adc_interrupt_handler, 0, + dev_name(dev), saradc); + if (ret) + return ret; + + writel(1, saradc->regs + CV1800B_ADC_INTR_EN_REG); + } + + ret = devm_mutex_init(dev, &saradc->lock); + if (ret) + return ret; + + writel(FIELD_PREP(CV1800B_MASK_STARTUP_CYCLE, 15) | + FIELD_PREP(CV1800B_MASK_SAMPLE_WINDOW, 15) | + FIELD_PREP(CV1800B_MASK_CLKDIV, 1) | + FIELD_PREP(CV1800B_MASK_COMPARE_CYCLE, 15), + saradc->regs + CV1800B_ADC_CYC_SET_REG); + + return devm_iio_device_register(dev, indio_dev); +} + +static const struct of_device_id cv1800b_adc_match[] = { + { .compatible = "sophgo,cv1800b-saradc", }, + { } +}; +MODULE_DEVICE_TABLE(of, cv1800b_adc_match); + +static struct platform_driver cv1800b_adc_driver = { + .driver = { + .name = "sophgo-cv1800b-saradc", + .of_match_table = cv1800b_adc_match, + }, + .probe = cv1800b_adc_probe, +}; +module_platform_driver(cv1800b_adc_driver); + +MODULE_AUTHOR("Thomas Bonnefille "); +MODULE_DESCRIPTION("Sophgo CV1800B SARADC driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/iio/adc/spear_adc.c b/drivers/iio/adc/spear_adc.c new file mode 100644 index 000000000..91c0fb1f4 --- /dev/null +++ b/drivers/iio/adc/spear_adc.c @@ -0,0 +1,365 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * ST SPEAr ADC driver + * + * Copyright 2012 Stefan Roese + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +/* SPEAR registers definitions */ +#define SPEAR600_ADC_SCAN_RATE_LO(x) ((x) & 0xFFFF) +#define SPEAR600_ADC_SCAN_RATE_HI(x) (((x) >> 0x10) & 0xFFFF) +#define SPEAR_ADC_CLK_LOW(x) (((x) & 0xf) << 0) +#define SPEAR_ADC_CLK_HIGH(x) (((x) & 0xf) << 4) + +/* Bit definitions for SPEAR_ADC_STATUS */ +#define SPEAR_ADC_STATUS_START_CONVERSION BIT(0) +#define SPEAR_ADC_STATUS_CHANNEL_NUM_MASK GENMASK(3, 1) +#define SPEAR_ADC_STATUS_ADC_ENABLE BIT(4) +#define SPEAR_ADC_STATUS_AVG_SAMPLE_MASK GENMASK(8, 5) +#define SPEAR_ADC_STATUS_VREF_INTERNAL BIT(9) + +#define SPEAR_ADC_DATA_MASK 0x03ff +#define SPEAR_ADC_DATA_BITS 10 + +#define SPEAR_ADC_MOD_NAME "spear-adc" + +#define SPEAR_ADC_CHANNEL_NUM 8 + +#define SPEAR_ADC_CLK_MIN 2500000 +#define SPEAR_ADC_CLK_MAX 20000000 + +struct adc_regs_spear3xx { + u32 status; + u32 average; + u32 scan_rate; + u32 clk; /* Not avail for 1340 & 1310 */ + u32 ch_ctrl[SPEAR_ADC_CHANNEL_NUM]; + u32 ch_data[SPEAR_ADC_CHANNEL_NUM]; +}; + +struct chan_data { + u32 lsb; + u32 msb; +}; + +struct adc_regs_spear6xx { + u32 status; + u32 pad[2]; + u32 clk; + u32 ch_ctrl[SPEAR_ADC_CHANNEL_NUM]; + struct chan_data ch_data[SPEAR_ADC_CHANNEL_NUM]; + u32 scan_rate_lo; + u32 scan_rate_hi; + struct chan_data average; +}; + +struct spear_adc_state { + struct device *dev; + struct adc_regs_spear3xx __iomem *adc_base_spear3xx; + struct adc_regs_spear6xx __iomem *adc_base_spear6xx; + struct clk *clk; + struct completion completion; + /* + * Lock to protect the device state during a potential concurrent + * read access from userspace. Reading a raw value requires a sequence + * of register writes, then a wait for a completion callback, + * and finally a register read, during which userspace could issue + * another read request. This lock protects a read access from + * occurring before another one has finished. + */ + struct mutex lock; + u32 current_clk; + u32 sampling_freq; + u32 avg_samples; + u32 vref_external; + u32 value; +}; + +/* + * Functions to access some SPEAr ADC register. Abstracted into + * static inline functions, because of different register offsets + * on different SoC variants (SPEAr300 vs SPEAr600 etc). + */ +static void spear_adc_set_status(struct spear_adc_state *st, u32 val) +{ + __raw_writel(val, &st->adc_base_spear6xx->status); +} + +static void spear_adc_set_clk(struct spear_adc_state *st, u32 val) +{ + u32 clk_high, clk_low, count; + u32 apb_clk = clk_get_rate(st->clk); + + count = DIV_ROUND_UP(apb_clk, val); + clk_low = count / 2; + clk_high = count - clk_low; + st->current_clk = apb_clk / count; + + __raw_writel(SPEAR_ADC_CLK_LOW(clk_low) | SPEAR_ADC_CLK_HIGH(clk_high), + &st->adc_base_spear6xx->clk); +} + +static void spear_adc_set_ctrl(struct spear_adc_state *st, int n, + u32 val) +{ + __raw_writel(val, &st->adc_base_spear6xx->ch_ctrl[n]); +} + +static u32 spear_adc_get_average(struct spear_adc_state *st) +{ + if (device_is_compatible(st->dev, "st,spear600-adc")) { + return __raw_readl(&st->adc_base_spear6xx->average.msb) & + SPEAR_ADC_DATA_MASK; + } else { + return __raw_readl(&st->adc_base_spear3xx->average) & + SPEAR_ADC_DATA_MASK; + } +} + +static void spear_adc_set_scanrate(struct spear_adc_state *st, u32 rate) +{ + if (device_is_compatible(st->dev, "st,spear600-adc")) { + __raw_writel(SPEAR600_ADC_SCAN_RATE_LO(rate), + &st->adc_base_spear6xx->scan_rate_lo); + __raw_writel(SPEAR600_ADC_SCAN_RATE_HI(rate), + &st->adc_base_spear6xx->scan_rate_hi); + } else { + __raw_writel(rate, &st->adc_base_spear3xx->scan_rate); + } +} + +static int spear_adc_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, + int *val2, + long mask) +{ + struct spear_adc_state *st = iio_priv(indio_dev); + u32 status; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + mutex_lock(&st->lock); + + status = FIELD_PREP(SPEAR_ADC_STATUS_CHANNEL_NUM_MASK, chan->channel) | + FIELD_PREP(SPEAR_ADC_STATUS_AVG_SAMPLE_MASK, st->avg_samples) | + SPEAR_ADC_STATUS_START_CONVERSION | + SPEAR_ADC_STATUS_ADC_ENABLE; + if (st->vref_external == 0) + status |= SPEAR_ADC_STATUS_VREF_INTERNAL; + + spear_adc_set_status(st, status); + wait_for_completion(&st->completion); /* set by ISR */ + *val = st->value; + + mutex_unlock(&st->lock); + + return IIO_VAL_INT; + + case IIO_CHAN_INFO_SCALE: + *val = st->vref_external; + *val2 = SPEAR_ADC_DATA_BITS; + return IIO_VAL_FRACTIONAL_LOG2; + case IIO_CHAN_INFO_SAMP_FREQ: + *val = st->current_clk; + return IIO_VAL_INT; + } + + return -EINVAL; +} + +static int spear_adc_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, + int val2, + long mask) +{ + struct spear_adc_state *st = iio_priv(indio_dev); + int ret = 0; + + if (mask != IIO_CHAN_INFO_SAMP_FREQ) + return -EINVAL; + + mutex_lock(&st->lock); + + if ((val < SPEAR_ADC_CLK_MIN) || + (val > SPEAR_ADC_CLK_MAX) || + (val2 != 0)) { + ret = -EINVAL; + goto out; + } + + spear_adc_set_clk(st, val); + +out: + mutex_unlock(&st->lock); + return ret; +} + +#define SPEAR_ADC_CHAN(idx) { \ + .type = IIO_VOLTAGE, \ + .indexed = 1, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ + .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ),\ + .channel = idx, \ +} + +static const struct iio_chan_spec spear_adc_iio_channels[] = { + SPEAR_ADC_CHAN(0), + SPEAR_ADC_CHAN(1), + SPEAR_ADC_CHAN(2), + SPEAR_ADC_CHAN(3), + SPEAR_ADC_CHAN(4), + SPEAR_ADC_CHAN(5), + SPEAR_ADC_CHAN(6), + SPEAR_ADC_CHAN(7), +}; + +static irqreturn_t spear_adc_isr(int irq, void *dev_id) +{ + struct spear_adc_state *st = dev_id; + + /* Read value to clear IRQ */ + st->value = spear_adc_get_average(st); + complete(&st->completion); + + return IRQ_HANDLED; +} + +static int spear_adc_configure(struct spear_adc_state *st) +{ + int i; + + /* Reset ADC core */ + spear_adc_set_status(st, 0); + __raw_writel(0, &st->adc_base_spear6xx->clk); + for (i = 0; i < 8; i++) + spear_adc_set_ctrl(st, i, 0); + spear_adc_set_scanrate(st, 0); + + spear_adc_set_clk(st, st->sampling_freq); + + return 0; +} + +static const struct iio_info spear_adc_info = { + .read_raw = &spear_adc_read_raw, + .write_raw = &spear_adc_write_raw, +}; + +static int spear_adc_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct spear_adc_state *st; + struct iio_dev *indio_dev = NULL; + int ret = -ENODEV; + int irq; + + indio_dev = devm_iio_device_alloc(dev, sizeof(struct spear_adc_state)); + if (!indio_dev) + return -ENOMEM; + + st = iio_priv(indio_dev); + st->dev = dev; + + init_completion(&st->completion); + mutex_init(&st->lock); + + /* + * SPEAr600 has a different register layout than other SPEAr SoC's + * (e.g. SPEAr3xx). Let's provide two register base addresses + * to support multi-arch kernels. + */ + st->adc_base_spear6xx = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(st->adc_base_spear6xx)) + return PTR_ERR(st->adc_base_spear6xx); + + st->adc_base_spear3xx = + (struct adc_regs_spear3xx __iomem *)st->adc_base_spear6xx; + + st->clk = devm_clk_get_enabled(dev, NULL); + if (IS_ERR(st->clk)) + return dev_err_probe(dev, PTR_ERR(st->clk), + "failed enabling clock\n"); + + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return irq; + + ret = devm_request_irq(dev, irq, spear_adc_isr, 0, SPEAR_ADC_MOD_NAME, + st); + if (ret < 0) + return dev_err_probe(dev, ret, "failed requesting interrupt\n"); + + if (device_property_read_u32(dev, "sampling-frequency", &st->sampling_freq)) + return dev_err_probe(dev, -EINVAL, + "sampling-frequency missing in DT\n"); + + /* + * Optional avg_samples defaults to 0, resulting in single data + * conversion + */ + device_property_read_u32(dev, "average-samples", &st->avg_samples); + + /* + * Optional vref_external defaults to 0, resulting in internal vref + * selection + */ + device_property_read_u32(dev, "vref-external", &st->vref_external); + + spear_adc_configure(st); + + indio_dev->name = SPEAR_ADC_MOD_NAME; + indio_dev->info = &spear_adc_info; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = spear_adc_iio_channels; + indio_dev->num_channels = ARRAY_SIZE(spear_adc_iio_channels); + + ret = devm_iio_device_register(dev, indio_dev); + if (ret) + return ret; + + dev_info(dev, "SPEAR ADC driver loaded, IRQ %d\n", irq); + + return 0; +} + +static const struct of_device_id spear_adc_dt_ids[] = { + { .compatible = "st,spear600-adc", }, + { } +}; +MODULE_DEVICE_TABLE(of, spear_adc_dt_ids); + +static struct platform_driver spear_adc_driver = { + .probe = spear_adc_probe, + .driver = { + .name = SPEAR_ADC_MOD_NAME, + .of_match_table = spear_adc_dt_ids, + }, +}; + +module_platform_driver(spear_adc_driver); + +MODULE_AUTHOR("Stefan Roese "); +MODULE_DESCRIPTION("SPEAr ADC driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/iio/adc/stm32-adc-core.c b/drivers/iio/adc/stm32-adc-core.c new file mode 100644 index 000000000..a42d82d61 --- /dev/null +++ b/drivers/iio/adc/stm32-adc-core.c @@ -0,0 +1,917 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * This file is part of STM32 ADC driver + * + * Copyright (C) 2016, STMicroelectronics - All Rights Reserved + * Author: Fabrice Gasnier . + * + * Inspired from: fsl-imx25-tsadc + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "stm32-adc-core.h" + +#define STM32_ADC_CORE_SLEEP_DELAY_MS 2000 + +/* SYSCFG registers */ +#define STM32MP1_SYSCFG_PMCSETR 0x04 +#define STM32MP1_SYSCFG_PMCCLRR 0x44 + +/* SYSCFG bit fields */ +#define STM32MP1_SYSCFG_ANASWVDD_MASK BIT(9) + +/* SYSCFG capability flags */ +#define HAS_VBOOSTER BIT(0) +#define HAS_ANASWVDD BIT(1) + +/** + * struct stm32_adc_common_regs - stm32 common registers + * @csr: common status register offset + * @ccr: common control register offset + * @eoc_msk: array of eoc (end of conversion flag) masks in csr for adc1..n + * @ovr_msk: array of ovr (overrun flag) masks in csr for adc1..n + * @ier: interrupt enable register offset for each adc + * @eocie_msk: end of conversion interrupt enable mask in @ier + */ +struct stm32_adc_common_regs { + u32 csr; + u32 ccr; + u32 eoc_msk[STM32_ADC_MAX_ADCS]; + u32 ovr_msk[STM32_ADC_MAX_ADCS]; + u32 ier; + u32 eocie_msk; +}; + +struct stm32_adc_priv; + +/** + * struct stm32_adc_priv_cfg - stm32 core compatible configuration data + * @regs: common registers for all instances + * @clk_sel: clock selection routine + * @max_clk_rate_hz: maximum analog clock rate (Hz, from datasheet) + * @ipid: adc identification number + * @has_syscfg: SYSCFG capability flags + * @num_irqs: number of interrupt lines + * @num_adcs: maximum number of ADC instances in the common registers + */ +struct stm32_adc_priv_cfg { + const struct stm32_adc_common_regs *regs; + int (*clk_sel)(struct platform_device *, struct stm32_adc_priv *); + u32 max_clk_rate_hz; + u32 ipid; + unsigned int has_syscfg; + unsigned int num_irqs; + unsigned int num_adcs; +}; + +/** + * struct stm32_adc_priv - stm32 ADC core private data + * @irq: irq(s) for ADC block + * @nb_adc_max: actual maximum number of instance per ADC block + * @domain: irq domain reference + * @aclk: clock reference for the analog circuitry + * @bclk: bus clock common for all ADCs, depends on part used + * @max_clk_rate: desired maximum clock rate + * @booster: booster supply reference + * @vdd: vdd supply reference + * @vdda: vdda analog supply reference + * @vref: regulator reference + * @vdd_uv: vdd supply voltage (microvolts) + * @vdda_uv: vdda supply voltage (microvolts) + * @cfg: compatible configuration data + * @common: common data for all ADC instances + * @ccr_bak: backup CCR in low power mode + * @syscfg: reference to syscon, system control registers + */ +struct stm32_adc_priv { + int irq[STM32_ADC_MAX_ADCS]; + unsigned int nb_adc_max; + struct irq_domain *domain; + struct clk *aclk; + struct clk *bclk; + u32 max_clk_rate; + struct regulator *booster; + struct regulator *vdd; + struct regulator *vdda; + struct regulator *vref; + int vdd_uv; + int vdda_uv; + const struct stm32_adc_priv_cfg *cfg; + struct stm32_adc_common common; + u32 ccr_bak; + struct regmap *syscfg; +}; + +static struct stm32_adc_priv *to_stm32_adc_priv(struct stm32_adc_common *com) +{ + return container_of(com, struct stm32_adc_priv, common); +} + +/* STM32F4 ADC internal common clock prescaler division ratios */ +static int stm32f4_pclk_div[] = {2, 4, 6, 8}; + +/** + * stm32f4_adc_clk_sel() - Select stm32f4 ADC common clock prescaler + * @pdev: platform device + * @priv: stm32 ADC core private data + * Select clock prescaler used for analog conversions, before using ADC. + */ +static int stm32f4_adc_clk_sel(struct platform_device *pdev, + struct stm32_adc_priv *priv) +{ + unsigned long rate; + u32 val; + int i; + + /* stm32f4 has one clk input for analog (mandatory), enforce it here */ + if (!priv->aclk) { + dev_err(&pdev->dev, "No 'adc' clock found\n"); + return -ENOENT; + } + + rate = clk_get_rate(priv->aclk); + if (!rate) { + dev_err(&pdev->dev, "Invalid clock rate: 0\n"); + return -EINVAL; + } + + for (i = 0; i < ARRAY_SIZE(stm32f4_pclk_div); i++) { + if ((rate / stm32f4_pclk_div[i]) <= priv->max_clk_rate) + break; + } + if (i >= ARRAY_SIZE(stm32f4_pclk_div)) { + dev_err(&pdev->dev, "adc clk selection failed\n"); + return -EINVAL; + } + + priv->common.rate = rate / stm32f4_pclk_div[i]; + val = readl_relaxed(priv->common.base + STM32F4_ADC_CCR); + val &= ~STM32F4_ADC_ADCPRE_MASK; + val |= i << STM32F4_ADC_ADCPRE_SHIFT; + writel_relaxed(val, priv->common.base + STM32F4_ADC_CCR); + + dev_dbg(&pdev->dev, "Using analog clock source at %ld kHz\n", + priv->common.rate / 1000); + + return 0; +} + +/** + * struct stm32h7_adc_ck_spec - specification for stm32h7 adc clock + * @ckmode: ADC clock mode, Async or sync with prescaler. + * @presc: prescaler bitfield for async clock mode + * @div: prescaler division ratio + */ +struct stm32h7_adc_ck_spec { + u32 ckmode; + u32 presc; + int div; +}; + +static const struct stm32h7_adc_ck_spec stm32h7_adc_ckmodes_spec[] = { + /* 00: CK_ADC[1..3]: Asynchronous clock modes */ + { 0, 0, 1 }, + { 0, 1, 2 }, + { 0, 2, 4 }, + { 0, 3, 6 }, + { 0, 4, 8 }, + { 0, 5, 10 }, + { 0, 6, 12 }, + { 0, 7, 16 }, + { 0, 8, 32 }, + { 0, 9, 64 }, + { 0, 10, 128 }, + { 0, 11, 256 }, + /* HCLK used: Synchronous clock modes (1, 2 or 4 prescaler) */ + { 1, 0, 1 }, + { 2, 0, 2 }, + { 3, 0, 4 }, +}; + +static int stm32h7_adc_clk_sel(struct platform_device *pdev, + struct stm32_adc_priv *priv) +{ + u32 ckmode, presc, val; + unsigned long rate; + int i, div, duty; + + /* stm32h7 bus clock is common for all ADC instances (mandatory) */ + if (!priv->bclk) { + dev_err(&pdev->dev, "No 'bus' clock found\n"); + return -ENOENT; + } + + /* + * stm32h7 can use either 'bus' or 'adc' clock for analog circuitry. + * So, choice is to have bus clock mandatory and adc clock optional. + * If optional 'adc' clock has been found, then try to use it first. + */ + if (priv->aclk) { + /* + * Asynchronous clock modes (e.g. ckmode == 0) + * From spec: PLL output mustn't exceed max rate + */ + rate = clk_get_rate(priv->aclk); + if (!rate) { + dev_err(&pdev->dev, "Invalid adc clock rate: 0\n"); + return -EINVAL; + } + + /* If duty is an error, kindly use at least /2 divider */ + duty = clk_get_scaled_duty_cycle(priv->aclk, 100); + if (duty < 0) + dev_warn(&pdev->dev, "adc clock duty: %d\n", duty); + + for (i = 0; i < ARRAY_SIZE(stm32h7_adc_ckmodes_spec); i++) { + ckmode = stm32h7_adc_ckmodes_spec[i].ckmode; + presc = stm32h7_adc_ckmodes_spec[i].presc; + div = stm32h7_adc_ckmodes_spec[i].div; + + if (ckmode) + continue; + + /* + * For proper operation, clock duty cycle range is 49% + * to 51%. Apply at least /2 prescaler otherwise. + */ + if (div == 1 && (duty < 49 || duty > 51)) + continue; + + if ((rate / div) <= priv->max_clk_rate) + goto out; + } + } + + /* Synchronous clock modes (e.g. ckmode is 1, 2 or 3) */ + rate = clk_get_rate(priv->bclk); + if (!rate) { + dev_err(&pdev->dev, "Invalid bus clock rate: 0\n"); + return -EINVAL; + } + + duty = clk_get_scaled_duty_cycle(priv->bclk, 100); + if (duty < 0) + dev_warn(&pdev->dev, "bus clock duty: %d\n", duty); + + for (i = 0; i < ARRAY_SIZE(stm32h7_adc_ckmodes_spec); i++) { + ckmode = stm32h7_adc_ckmodes_spec[i].ckmode; + presc = stm32h7_adc_ckmodes_spec[i].presc; + div = stm32h7_adc_ckmodes_spec[i].div; + + if (!ckmode) + continue; + + if (div == 1 && (duty < 49 || duty > 51)) + continue; + + if ((rate / div) <= priv->max_clk_rate) + goto out; + } + + dev_err(&pdev->dev, "adc clk selection failed\n"); + return -EINVAL; + +out: + /* rate used later by each ADC instance to control BOOST mode */ + priv->common.rate = rate / div; + + /* Set common clock mode and prescaler */ + val = readl_relaxed(priv->common.base + STM32H7_ADC_CCR); + val &= ~(STM32H7_CKMODE_MASK | STM32H7_PRESC_MASK); + val |= ckmode << STM32H7_CKMODE_SHIFT; + val |= presc << STM32H7_PRESC_SHIFT; + writel_relaxed(val, priv->common.base + STM32H7_ADC_CCR); + + dev_dbg(&pdev->dev, "Using %s clock/%d source at %ld kHz\n", + ckmode ? "bus" : "adc", div, priv->common.rate / 1000); + + return 0; +} + +/* STM32F4 common registers definitions */ +static const struct stm32_adc_common_regs stm32f4_adc_common_regs = { + .csr = STM32F4_ADC_CSR, + .ccr = STM32F4_ADC_CCR, + .eoc_msk = { STM32F4_EOC1, STM32F4_EOC2, STM32F4_EOC3 }, + .ovr_msk = { STM32F4_OVR1, STM32F4_OVR2, STM32F4_OVR3 }, + .ier = STM32F4_ADC_CR1, + .eocie_msk = STM32F4_EOCIE, +}; + +/* STM32H7 common registers definitions */ +static const struct stm32_adc_common_regs stm32h7_adc_common_regs = { + .csr = STM32H7_ADC_CSR, + .ccr = STM32H7_ADC_CCR, + .eoc_msk = { STM32H7_EOC_MST, STM32H7_EOC_SLV }, + .ovr_msk = { STM32H7_OVR_MST, STM32H7_OVR_SLV }, + .ier = STM32H7_ADC_IER, + .eocie_msk = STM32H7_EOCIE, +}; + +/* STM32MP13 common registers definitions */ +static const struct stm32_adc_common_regs stm32mp13_adc_common_regs = { + .csr = STM32H7_ADC_CSR, + .ccr = STM32H7_ADC_CCR, + .eoc_msk = { STM32H7_EOC_MST }, + .ovr_msk = { STM32H7_OVR_MST }, + .ier = STM32H7_ADC_IER, + .eocie_msk = STM32H7_EOCIE, +}; + +static const unsigned int stm32_adc_offset[STM32_ADC_MAX_ADCS] = { + 0, STM32_ADC_OFFSET, STM32_ADC_OFFSET * 2, +}; + +static unsigned int stm32_adc_eoc_enabled(struct stm32_adc_priv *priv, + unsigned int adc) +{ + u32 ier, offset = stm32_adc_offset[adc]; + + ier = readl_relaxed(priv->common.base + offset + priv->cfg->regs->ier); + + return ier & priv->cfg->regs->eocie_msk; +} + +/* ADC common interrupt for all instances */ +static void stm32_adc_irq_handler(struct irq_desc *desc) +{ + struct stm32_adc_priv *priv = irq_desc_get_handler_data(desc); + struct irq_chip *chip = irq_desc_get_chip(desc); + int i; + u32 status; + + chained_irq_enter(chip, desc); + status = readl_relaxed(priv->common.base + priv->cfg->regs->csr); + + /* + * End of conversion may be handled by using IRQ or DMA. There may be a + * race here when two conversions complete at the same time on several + * ADCs. EOC may be read 'set' for several ADCs, with: + * - an ADC configured to use DMA (EOC triggers the DMA request, and + * is then automatically cleared by DR read in hardware) + * - an ADC configured to use IRQs (EOCIE bit is set. The handler must + * be called in this case) + * So both EOC status bit in CSR and EOCIE control bit must be checked + * before invoking the interrupt handler (e.g. call ISR only for + * IRQ-enabled ADCs). + */ + for (i = 0; i < priv->nb_adc_max; i++) { + if ((status & priv->cfg->regs->eoc_msk[i] && + stm32_adc_eoc_enabled(priv, i)) || + (status & priv->cfg->regs->ovr_msk[i])) + generic_handle_domain_irq(priv->domain, i); + } + + chained_irq_exit(chip, desc); +}; + +static int stm32_adc_domain_map(struct irq_domain *d, unsigned int irq, + irq_hw_number_t hwirq) +{ + irq_set_chip_data(irq, d->host_data); + irq_set_chip_and_handler(irq, &dummy_irq_chip, handle_level_irq); + + return 0; +} + +static void stm32_adc_domain_unmap(struct irq_domain *d, unsigned int irq) +{ + irq_set_chip_and_handler(irq, NULL, NULL); + irq_set_chip_data(irq, NULL); +} + +static const struct irq_domain_ops stm32_adc_domain_ops = { + .map = stm32_adc_domain_map, + .unmap = stm32_adc_domain_unmap, + .xlate = irq_domain_xlate_onecell, +}; + +static int stm32_adc_irq_probe(struct platform_device *pdev, + struct stm32_adc_priv *priv) +{ + unsigned int i; + + /* + * Interrupt(s) must be provided, depending on the compatible: + * - stm32f4/h7 shares a common interrupt line. + * - stm32mp1, has one line per ADC + */ + for (i = 0; i < priv->cfg->num_irqs; i++) { + priv->irq[i] = platform_get_irq(pdev, i); + if (priv->irq[i] < 0) + return priv->irq[i]; + } + + priv->domain = irq_domain_create_simple(dev_fwnode(&pdev->dev), + STM32_ADC_MAX_ADCS, 0, + &stm32_adc_domain_ops, + priv); + if (!priv->domain) { + dev_err(&pdev->dev, "Failed to add irq domain\n"); + return -ENOMEM; + } + + for (i = 0; i < priv->cfg->num_irqs; i++) + irq_set_chained_handler_and_data(priv->irq[i], + stm32_adc_irq_handler, priv); + + return 0; +} + +static void stm32_adc_irq_remove(struct platform_device *pdev, + struct stm32_adc_priv *priv) +{ + int hwirq; + unsigned int i; + + for (hwirq = 0; hwirq < priv->nb_adc_max; hwirq++) + irq_dispose_mapping(irq_find_mapping(priv->domain, hwirq)); + irq_domain_remove(priv->domain); + + for (i = 0; i < priv->cfg->num_irqs; i++) + irq_set_chained_handler(priv->irq[i], NULL); +} + +static int stm32_adc_core_switches_supply_en(struct stm32_adc_priv *priv, + struct device *dev) +{ + int ret; + + /* + * On STM32H7 and STM32MP1, the ADC inputs are multiplexed with analog + * switches (via PCSEL) which have reduced performances when their + * supply is below 2.7V (vdda by default): + * - Voltage booster can be used, to get full ADC performances + * (increases power consumption). + * - Vdd can be used to supply them, if above 2.7V (STM32MP1 only). + * + * Recommended settings for ANASWVDD and EN_BOOSTER: + * - vdda < 2.7V but vdd > 2.7V: ANASWVDD = 1, EN_BOOSTER = 0 (stm32mp1) + * - vdda < 2.7V and vdd < 2.7V: ANASWVDD = 0, EN_BOOSTER = 1 + * - vdda >= 2.7V: ANASWVDD = 0, EN_BOOSTER = 0 (default) + */ + if (priv->vdda_uv < 2700000) { + if (priv->syscfg && priv->vdd_uv > 2700000) { + ret = regulator_enable(priv->vdd); + if (ret < 0) { + dev_err(dev, "vdd enable failed %d\n", ret); + return ret; + } + + ret = regmap_write(priv->syscfg, + STM32MP1_SYSCFG_PMCSETR, + STM32MP1_SYSCFG_ANASWVDD_MASK); + if (ret < 0) { + regulator_disable(priv->vdd); + dev_err(dev, "vdd select failed, %d\n", ret); + return ret; + } + dev_dbg(dev, "analog switches supplied by vdd\n"); + + return 0; + } + + if (priv->booster) { + /* + * This is optional, as this is a trade-off between + * analog performance and power consumption. + */ + ret = regulator_enable(priv->booster); + if (ret < 0) { + dev_err(dev, "booster enable failed %d\n", ret); + return ret; + } + dev_dbg(dev, "analog switches supplied by booster\n"); + + return 0; + } + } + + /* Fallback using vdda (default), nothing to do */ + dev_dbg(dev, "analog switches supplied by vdda (%d uV)\n", + priv->vdda_uv); + + return 0; +} + +static void stm32_adc_core_switches_supply_dis(struct stm32_adc_priv *priv) +{ + if (priv->vdda_uv < 2700000) { + if (priv->syscfg && priv->vdd_uv > 2700000) { + regmap_write(priv->syscfg, STM32MP1_SYSCFG_PMCCLRR, + STM32MP1_SYSCFG_ANASWVDD_MASK); + regulator_disable(priv->vdd); + return; + } + if (priv->booster) + regulator_disable(priv->booster); + } +} + +static int stm32_adc_core_hw_start(struct device *dev) +{ + struct stm32_adc_common *common = dev_get_drvdata(dev); + struct stm32_adc_priv *priv = to_stm32_adc_priv(common); + int ret; + + ret = regulator_enable(priv->vdda); + if (ret < 0) { + dev_err(dev, "vdda enable failed %d\n", ret); + return ret; + } + + ret = regulator_get_voltage(priv->vdda); + if (ret < 0) { + dev_err(dev, "vdda get voltage failed, %d\n", ret); + goto err_vdda_disable; + } + priv->vdda_uv = ret; + + ret = stm32_adc_core_switches_supply_en(priv, dev); + if (ret < 0) + goto err_vdda_disable; + + ret = regulator_enable(priv->vref); + if (ret < 0) { + dev_err(dev, "vref enable failed\n"); + goto err_switches_dis; + } + + ret = clk_prepare_enable(priv->bclk); + if (ret < 0) { + dev_err(dev, "bus clk enable failed\n"); + goto err_regulator_disable; + } + + ret = clk_prepare_enable(priv->aclk); + if (ret < 0) { + dev_err(dev, "adc clk enable failed\n"); + goto err_bclk_disable; + } + + writel_relaxed(priv->ccr_bak, priv->common.base + priv->cfg->regs->ccr); + + return 0; + +err_bclk_disable: + clk_disable_unprepare(priv->bclk); +err_regulator_disable: + regulator_disable(priv->vref); +err_switches_dis: + stm32_adc_core_switches_supply_dis(priv); +err_vdda_disable: + regulator_disable(priv->vdda); + + return ret; +} + +static void stm32_adc_core_hw_stop(struct device *dev) +{ + struct stm32_adc_common *common = dev_get_drvdata(dev); + struct stm32_adc_priv *priv = to_stm32_adc_priv(common); + + /* Backup CCR that may be lost (depends on power state to achieve) */ + priv->ccr_bak = readl_relaxed(priv->common.base + priv->cfg->regs->ccr); + clk_disable_unprepare(priv->aclk); + clk_disable_unprepare(priv->bclk); + regulator_disable(priv->vref); + stm32_adc_core_switches_supply_dis(priv); + regulator_disable(priv->vdda); +} + +static int stm32_adc_core_switches_probe(struct device *dev, + struct stm32_adc_priv *priv) +{ + struct device_node *np = dev->of_node; + int ret; + + /* Analog switches supply can be controlled by syscfg (optional) */ + priv->syscfg = syscon_regmap_lookup_by_phandle(np, "st,syscfg"); + if (IS_ERR(priv->syscfg)) { + ret = PTR_ERR(priv->syscfg); + if (ret != -ENODEV) + return dev_err_probe(dev, ret, "Can't probe syscfg\n"); + + priv->syscfg = NULL; + } + + /* Booster can be used to supply analog switches (optional) */ + if (priv->cfg->has_syscfg & HAS_VBOOSTER) { + priv->booster = devm_regulator_get_optional(dev, "booster"); + if (IS_ERR(priv->booster)) { + ret = PTR_ERR(priv->booster); + if (ret != -ENODEV) + return dev_err_probe(dev, ret, "can't get booster\n"); + + priv->booster = NULL; + } + } + + /* Vdd can be used to supply analog switches (optional) */ + if (priv->cfg->has_syscfg & HAS_ANASWVDD) { + priv->vdd = devm_regulator_get_optional(dev, "vdd"); + if (IS_ERR(priv->vdd)) { + ret = PTR_ERR(priv->vdd); + if (ret != -ENODEV) + return dev_err_probe(dev, ret, "can't get vdd\n"); + + priv->vdd = NULL; + } + } + + if (priv->vdd) { + ret = regulator_enable(priv->vdd); + if (ret < 0) { + dev_err(dev, "vdd enable failed %d\n", ret); + return ret; + } + + ret = regulator_get_voltage(priv->vdd); + if (ret < 0) { + dev_err(dev, "vdd get voltage failed %d\n", ret); + regulator_disable(priv->vdd); + return ret; + } + priv->vdd_uv = ret; + + regulator_disable(priv->vdd); + } + + return 0; +} + +static int stm32_adc_probe_identification(struct platform_device *pdev, + struct stm32_adc_priv *priv) +{ + struct device_node *np = pdev->dev.of_node; + struct device_node *child; + const char *compat; + int ret, count = 0; + u32 id, val; + + if (!priv->cfg->ipid) + return 0; + + id = FIELD_GET(STM32MP1_IPIDR_MASK, + readl_relaxed(priv->common.base + STM32MP1_ADC_IPDR)); + if (id != priv->cfg->ipid) { + dev_err(&pdev->dev, "Unexpected IP version: 0x%x", id); + return -EINVAL; + } + + for_each_child_of_node(np, child) { + ret = of_property_read_string(child, "compatible", &compat); + if (ret) + continue; + /* Count child nodes with stm32 adc compatible */ + if (strstr(compat, "st,stm32") && strstr(compat, "adc")) + count++; + } + + val = readl_relaxed(priv->common.base + STM32MP1_ADC_HWCFGR0); + priv->nb_adc_max = FIELD_GET(STM32MP1_ADCNUM_MASK, val); + if (count > priv->nb_adc_max) { + dev_err(&pdev->dev, "Unexpected child number: %d", count); + return -EINVAL; + } + + val = readl_relaxed(priv->common.base + STM32MP1_ADC_VERR); + dev_dbg(&pdev->dev, "ADC version: %lu.%lu\n", + FIELD_GET(STM32MP1_MAJREV_MASK, val), + FIELD_GET(STM32MP1_MINREV_MASK, val)); + + return 0; +} + +static int stm32_adc_probe(struct platform_device *pdev) +{ + struct stm32_adc_priv *priv; + struct device *dev = &pdev->dev; + struct device_node *np = pdev->dev.of_node; + struct resource *res; + u32 max_rate; + int ret; + + if (!pdev->dev.of_node) + return -ENODEV; + + priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + platform_set_drvdata(pdev, &priv->common); + + priv->cfg = device_get_match_data(dev); + priv->nb_adc_max = priv->cfg->num_adcs; + spin_lock_init(&priv->common.lock); + + priv->common.base = devm_platform_get_and_ioremap_resource(pdev, 0, &res); + if (IS_ERR(priv->common.base)) + return PTR_ERR(priv->common.base); + priv->common.phys_base = res->start; + + priv->vdda = devm_regulator_get(&pdev->dev, "vdda"); + if (IS_ERR(priv->vdda)) + return dev_err_probe(&pdev->dev, PTR_ERR(priv->vdda), + "vdda get failed\n"); + + priv->vref = devm_regulator_get(&pdev->dev, "vref"); + if (IS_ERR(priv->vref)) + return dev_err_probe(&pdev->dev, PTR_ERR(priv->vref), + "vref get failed\n"); + + priv->aclk = devm_clk_get_optional(&pdev->dev, "adc"); + if (IS_ERR(priv->aclk)) + return dev_err_probe(&pdev->dev, PTR_ERR(priv->aclk), + "Can't get 'adc' clock\n"); + + priv->bclk = devm_clk_get_optional(&pdev->dev, "bus"); + if (IS_ERR(priv->bclk)) + return dev_err_probe(&pdev->dev, PTR_ERR(priv->bclk), + "Can't get 'bus' clock\n"); + + ret = stm32_adc_core_switches_probe(dev, priv); + if (ret) + return ret; + + pm_runtime_get_noresume(dev); + pm_runtime_set_active(dev); + pm_runtime_set_autosuspend_delay(dev, STM32_ADC_CORE_SLEEP_DELAY_MS); + pm_runtime_use_autosuspend(dev); + pm_runtime_enable(dev); + + ret = stm32_adc_core_hw_start(dev); + if (ret) + goto err_pm_stop; + + ret = stm32_adc_probe_identification(pdev, priv); + if (ret < 0) + goto err_hw_stop; + + ret = regulator_get_voltage(priv->vref); + if (ret < 0) { + dev_err(&pdev->dev, "vref get voltage failed, %d\n", ret); + goto err_hw_stop; + } + priv->common.vref_mv = ret / 1000; + dev_dbg(&pdev->dev, "vref+=%dmV\n", priv->common.vref_mv); + + ret = of_property_read_u32(pdev->dev.of_node, "st,max-clk-rate-hz", + &max_rate); + if (!ret) + priv->max_clk_rate = min(max_rate, priv->cfg->max_clk_rate_hz); + else + priv->max_clk_rate = priv->cfg->max_clk_rate_hz; + + ret = priv->cfg->clk_sel(pdev, priv); + if (ret < 0) + goto err_hw_stop; + + ret = stm32_adc_irq_probe(pdev, priv); + if (ret < 0) + goto err_hw_stop; + + ret = of_platform_populate(np, NULL, NULL, &pdev->dev); + if (ret < 0) { + dev_err(&pdev->dev, "failed to populate DT children\n"); + goto err_irq_remove; + } + + pm_runtime_put_autosuspend(dev); + + return 0; + +err_irq_remove: + stm32_adc_irq_remove(pdev, priv); +err_hw_stop: + stm32_adc_core_hw_stop(dev); +err_pm_stop: + pm_runtime_disable(dev); + pm_runtime_set_suspended(dev); + pm_runtime_put_noidle(dev); + + return ret; +} + +static void stm32_adc_remove(struct platform_device *pdev) +{ + struct stm32_adc_common *common = platform_get_drvdata(pdev); + struct stm32_adc_priv *priv = to_stm32_adc_priv(common); + + pm_runtime_get_sync(&pdev->dev); + of_platform_depopulate(&pdev->dev); + stm32_adc_irq_remove(pdev, priv); + stm32_adc_core_hw_stop(&pdev->dev); + pm_runtime_disable(&pdev->dev); + pm_runtime_set_suspended(&pdev->dev); + pm_runtime_put_noidle(&pdev->dev); +} + +static int stm32_adc_core_runtime_suspend(struct device *dev) +{ + stm32_adc_core_hw_stop(dev); + + return 0; +} + +static int stm32_adc_core_runtime_resume(struct device *dev) +{ + return stm32_adc_core_hw_start(dev); +} + +static int stm32_adc_core_runtime_idle(struct device *dev) +{ + pm_runtime_mark_last_busy(dev); + + return 0; +} + +static DEFINE_RUNTIME_DEV_PM_OPS(stm32_adc_core_pm_ops, + stm32_adc_core_runtime_suspend, + stm32_adc_core_runtime_resume, + stm32_adc_core_runtime_idle); + +static const struct stm32_adc_priv_cfg stm32f4_adc_priv_cfg = { + .regs = &stm32f4_adc_common_regs, + .clk_sel = stm32f4_adc_clk_sel, + .max_clk_rate_hz = 36000000, + .num_irqs = 1, + .num_adcs = 3, +}; + +static const struct stm32_adc_priv_cfg stm32h7_adc_priv_cfg = { + .regs = &stm32h7_adc_common_regs, + .clk_sel = stm32h7_adc_clk_sel, + .max_clk_rate_hz = 36000000, + .has_syscfg = HAS_VBOOSTER, + .num_irqs = 1, + .num_adcs = 2, +}; + +static const struct stm32_adc_priv_cfg stm32mp1_adc_priv_cfg = { + .regs = &stm32h7_adc_common_regs, + .clk_sel = stm32h7_adc_clk_sel, + .max_clk_rate_hz = 36000000, + .has_syscfg = HAS_VBOOSTER | HAS_ANASWVDD, + .ipid = STM32MP15_IPIDR_NUMBER, + .num_irqs = 2, +}; + +static const struct stm32_adc_priv_cfg stm32mp13_adc_priv_cfg = { + .regs = &stm32mp13_adc_common_regs, + .clk_sel = stm32h7_adc_clk_sel, + .max_clk_rate_hz = 75 * HZ_PER_MHZ, + .ipid = STM32MP13_IPIDR_NUMBER, + .num_irqs = 1, +}; + +static const struct of_device_id stm32_adc_of_match[] = { + { + .compatible = "st,stm32f4-adc-core", + .data = (void *)&stm32f4_adc_priv_cfg + }, { + .compatible = "st,stm32h7-adc-core", + .data = (void *)&stm32h7_adc_priv_cfg + }, { + .compatible = "st,stm32mp1-adc-core", + .data = (void *)&stm32mp1_adc_priv_cfg + }, { + .compatible = "st,stm32mp13-adc-core", + .data = (void *)&stm32mp13_adc_priv_cfg + }, { + }, +}; +MODULE_DEVICE_TABLE(of, stm32_adc_of_match); + +static struct platform_driver stm32_adc_driver = { + .probe = stm32_adc_probe, + .remove = stm32_adc_remove, + .driver = { + .name = "stm32-adc-core", + .of_match_table = stm32_adc_of_match, + .pm = pm_ptr(&stm32_adc_core_pm_ops), + }, +}; +module_platform_driver(stm32_adc_driver); + +MODULE_AUTHOR("Fabrice Gasnier "); +MODULE_DESCRIPTION("STMicroelectronics STM32 ADC core driver"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:stm32-adc-core"); diff --git a/drivers/iio/adc/stm32-adc-core.h b/drivers/iio/adc/stm32-adc-core.h new file mode 100644 index 000000000..db50a9f3b --- /dev/null +++ b/drivers/iio/adc/stm32-adc-core.h @@ -0,0 +1,279 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * This file is part of STM32 ADC driver + * + * Copyright (C) 2016, STMicroelectronics - All Rights Reserved + * Author: Fabrice Gasnier . + * + */ + +#ifndef __STM32_ADC_H +#define __STM32_ADC_H + +#include +#include + +/* + * STM32 - ADC global register map + * ________________________________________________________ + * | Offset | Register | + * -------------------------------------------------------- + * | 0x000 | Master ADC1 | + * -------------------------------------------------------- + * | 0x100 | Slave ADC2 | + * -------------------------------------------------------- + * | 0x200 | Slave ADC3 | + * -------------------------------------------------------- + * | 0x300 | Master & Slave common regs | + * -------------------------------------------------------- + */ +/* Maximum ADC instances number per ADC block for all supported SoCs */ +#define STM32_ADC_MAX_ADCS 3 +#define STM32_ADC_OFFSET 0x100 +#define STM32_ADCX_COMN_OFFSET 0x300 + +/* STM32F4 - Registers for each ADC instance */ +#define STM32F4_ADC_SR 0x00 +#define STM32F4_ADC_CR1 0x04 +#define STM32F4_ADC_CR2 0x08 +#define STM32F4_ADC_SMPR1 0x0C +#define STM32F4_ADC_SMPR2 0x10 +#define STM32F4_ADC_HTR 0x24 +#define STM32F4_ADC_LTR 0x28 +#define STM32F4_ADC_SQR1 0x2C +#define STM32F4_ADC_SQR2 0x30 +#define STM32F4_ADC_SQR3 0x34 +#define STM32F4_ADC_JSQR 0x38 +#define STM32F4_ADC_JDR1 0x3C +#define STM32F4_ADC_JDR2 0x40 +#define STM32F4_ADC_JDR3 0x44 +#define STM32F4_ADC_JDR4 0x48 +#define STM32F4_ADC_DR 0x4C + +/* STM32F4 - common registers for all ADC instances: 1, 2 & 3 */ +#define STM32F4_ADC_CSR (STM32_ADCX_COMN_OFFSET + 0x00) +#define STM32F4_ADC_CCR (STM32_ADCX_COMN_OFFSET + 0x04) + +/* STM32F4_ADC_SR - bit fields */ +#define STM32F4_OVR BIT(5) +#define STM32F4_STRT BIT(4) +#define STM32F4_EOC BIT(1) + +/* STM32F4_ADC_CR1 - bit fields */ +#define STM32F4_OVRIE BIT(26) +#define STM32F4_RES_SHIFT 24 +#define STM32F4_RES_MASK GENMASK(25, 24) +#define STM32F4_SCAN BIT(8) +#define STM32F4_EOCIE BIT(5) + +/* STM32F4_ADC_CR2 - bit fields */ +#define STM32F4_SWSTART BIT(30) +#define STM32F4_EXTEN_SHIFT 28 +#define STM32F4_EXTEN_MASK GENMASK(29, 28) +#define STM32F4_EXTSEL_SHIFT 24 +#define STM32F4_EXTSEL_MASK GENMASK(27, 24) +#define STM32F4_EOCS BIT(10) +#define STM32F4_DDS BIT(9) +#define STM32F4_DMA BIT(8) +#define STM32F4_ADON BIT(0) + +/* STM32F4_ADC_CSR - bit fields */ +#define STM32F4_OVR3 BIT(21) +#define STM32F4_EOC3 BIT(17) +#define STM32F4_OVR2 BIT(13) +#define STM32F4_EOC2 BIT(9) +#define STM32F4_OVR1 BIT(5) +#define STM32F4_EOC1 BIT(1) + +/* STM32F4_ADC_CCR - bit fields */ +#define STM32F4_ADC_ADCPRE_SHIFT 16 +#define STM32F4_ADC_ADCPRE_MASK GENMASK(17, 16) + +/* STM32H7 - Registers for each ADC instance */ +#define STM32H7_ADC_ISR 0x00 +#define STM32H7_ADC_IER 0x04 +#define STM32H7_ADC_CR 0x08 +#define STM32H7_ADC_CFGR 0x0C +#define STM32H7_ADC_CFGR2 0x10 +#define STM32H7_ADC_SMPR1 0x14 +#define STM32H7_ADC_SMPR2 0x18 +#define STM32H7_ADC_PCSEL 0x1C +#define STM32H7_ADC_SQR1 0x30 +#define STM32H7_ADC_SQR2 0x34 +#define STM32H7_ADC_SQR3 0x38 +#define STM32H7_ADC_SQR4 0x3C +#define STM32H7_ADC_DR 0x40 +#define STM32H7_ADC_DIFSEL 0xC0 +#define STM32H7_ADC_CALFACT 0xC4 +#define STM32H7_ADC_CALFACT2 0xC8 + +/* STM32MP1 - ADC2 instance option register */ +#define STM32MP1_ADC2_OR 0xD0 + +/* STM32MP1 - Identification registers */ +#define STM32MP1_ADC_HWCFGR0 0x3F0 +#define STM32MP1_ADC_VERR 0x3F4 +#define STM32MP1_ADC_IPDR 0x3F8 +#define STM32MP1_ADC_SIDR 0x3FC + +/* STM32MP13 - Registers for each ADC instance */ +#define STM32MP13_ADC_DIFSEL 0xB0 +#define STM32MP13_ADC_CALFACT 0xB4 +#define STM32MP13_ADC2_OR 0xC8 + +/* STM32H7 - common registers for all ADC instances */ +#define STM32H7_ADC_CSR (STM32_ADCX_COMN_OFFSET + 0x00) +#define STM32H7_ADC_CCR (STM32_ADCX_COMN_OFFSET + 0x08) + +/* STM32H7_ADC_ISR - bit fields */ +#define STM32MP1_VREGREADY BIT(12) +#define STM32H7_OVR BIT(4) +#define STM32H7_EOC BIT(2) +#define STM32H7_ADRDY BIT(0) + +/* STM32H7_ADC_IER - bit fields */ +#define STM32H7_OVRIE STM32H7_OVR +#define STM32H7_EOCIE STM32H7_EOC + +/* STM32H7_ADC_CR - bit fields */ +#define STM32H7_ADCAL BIT(31) +#define STM32H7_ADCALDIF BIT(30) +#define STM32H7_DEEPPWD BIT(29) +#define STM32H7_ADVREGEN BIT(28) +#define STM32H7_LINCALRDYW6 BIT(27) +#define STM32H7_LINCALRDYW5 BIT(26) +#define STM32H7_LINCALRDYW4 BIT(25) +#define STM32H7_LINCALRDYW3 BIT(24) +#define STM32H7_LINCALRDYW2 BIT(23) +#define STM32H7_LINCALRDYW1 BIT(22) +#define STM32H7_LINCALRDYW_MASK GENMASK(27, 22) +#define STM32H7_ADCALLIN BIT(16) +#define STM32H7_BOOST BIT(8) +#define STM32H7_ADSTP BIT(4) +#define STM32H7_ADSTART BIT(2) +#define STM32H7_ADDIS BIT(1) +#define STM32H7_ADEN BIT(0) + +/* STM32H7_ADC_CFGR bit fields */ +#define STM32H7_EXTEN_SHIFT 10 +#define STM32H7_EXTEN_MASK GENMASK(11, 10) +#define STM32H7_EXTSEL_SHIFT 5 +#define STM32H7_EXTSEL_MASK GENMASK(9, 5) +#define STM32H7_RES_SHIFT 2 +#define STM32H7_RES_MASK GENMASK(4, 2) +#define STM32H7_DMNGT_SHIFT 0 +#define STM32H7_DMNGT_MASK GENMASK(1, 0) + +/* STM32H7_ADC_CFGR2 bit fields */ +#define STM32H7_OVSR_MASK GENMASK(25, 16) /* Correspond to OSVR field in datasheet */ +#define STM32H7_OVSR(v) FIELD_PREP(STM32H7_OVSR_MASK, v) +#define STM32H7_OVSS_MASK GENMASK(8, 5) +#define STM32H7_OVSS(v) FIELD_PREP(STM32H7_OVSS_MASK, v) +#define STM32H7_ROVSE BIT(0) + +enum stm32h7_adc_dmngt { + STM32H7_DMNGT_DR_ONLY, /* Regular data in DR only */ + STM32H7_DMNGT_DMA_ONESHOT, /* DMA one shot mode */ + STM32H7_DMNGT_DFSDM, /* DFSDM mode */ + STM32H7_DMNGT_DMA_CIRC, /* DMA circular mode */ +}; + +/* STM32H7_ADC_DIFSEL - bit fields */ +#define STM32H7_DIFSEL_MASK GENMASK(19, 0) + +/* STM32H7_ADC_CALFACT - bit fields */ +#define STM32H7_CALFACT_D_SHIFT 16 +#define STM32H7_CALFACT_D_MASK GENMASK(26, 16) +#define STM32H7_CALFACT_S_SHIFT 0 +#define STM32H7_CALFACT_S_MASK GENMASK(10, 0) + +/* STM32H7_ADC_CALFACT2 - bit fields */ +#define STM32H7_LINCALFACT_SHIFT 0 +#define STM32H7_LINCALFACT_MASK GENMASK(29, 0) + +/* STM32H7_ADC_CSR - bit fields */ +#define STM32H7_OVR_SLV BIT(20) +#define STM32H7_EOC_SLV BIT(18) +#define STM32H7_OVR_MST BIT(4) +#define STM32H7_EOC_MST BIT(2) + +/* STM32H7_ADC_CCR - bit fields */ +#define STM32H7_VBATEN BIT(24) +#define STM32H7_VREFEN BIT(22) +#define STM32H7_PRESC_SHIFT 18 +#define STM32H7_PRESC_MASK GENMASK(21, 18) +#define STM32H7_CKMODE_SHIFT 16 +#define STM32H7_CKMODE_MASK GENMASK(17, 16) + +/* STM32MP1_ADC2_OR - bit fields */ +#define STM32MP1_VDDCOREEN BIT(0) + +/* STM32MP1_ADC_HWCFGR0 - bit fields */ +#define STM32MP1_ADCNUM_SHIFT 0 +#define STM32MP1_ADCNUM_MASK GENMASK(3, 0) +#define STM32MP1_MULPIPE_SHIFT 4 +#define STM32MP1_MULPIPE_MASK GENMASK(7, 4) +#define STM32MP1_OPBITS_SHIFT 8 +#define STM32MP1_OPBITS_MASK GENMASK(11, 8) +#define STM32MP1_IDLEVALUE_SHIFT 12 +#define STM32MP1_IDLEVALUE_MASK GENMASK(15, 12) + +/* STM32MP1_ADC_VERR - bit fields */ +#define STM32MP1_MINREV_SHIFT 0 +#define STM32MP1_MINREV_MASK GENMASK(3, 0) +#define STM32MP1_MAJREV_SHIFT 4 +#define STM32MP1_MAJREV_MASK GENMASK(7, 4) + +/* STM32MP1_ADC_IPDR - bit fields */ +#define STM32MP1_IPIDR_MASK GENMASK(31, 0) + +/* STM32MP1_ADC_SIDR - bit fields */ +#define STM32MP1_SIDR_MASK GENMASK(31, 0) + +/* STM32MP13_ADC_CFGR specific bit fields */ +#define STM32MP13_DMAEN BIT(0) +#define STM32MP13_DMACFG BIT(1) +#define STM32MP13_DFSDMCFG BIT(2) +#define STM32MP13_RES_SHIFT 3 +#define STM32MP13_RES_MASK GENMASK(4, 3) + +/* STM32MP13_ADC_CFGR2 bit fields */ +#define STM32MP13_OVSR_MASK GENMASK(4, 2) +#define STM32MP13_OVSR(v) FIELD_PREP(STM32MP13_OVSR_MASK, v) +#define STM32MP13_OVSS_MASK GENMASK(8, 5) +#define STM32MP13_OVSS(v) FIELD_PREP(STM32MP13_OVSS_MASK, v) + +/* STM32MP13_ADC_DIFSEL - bit fields */ +#define STM32MP13_DIFSEL_MASK GENMASK(18, 0) + +/* STM32MP13_ADC_CALFACT - bit fields */ +#define STM32MP13_CALFACT_D_SHIFT 16 +#define STM32MP13_CALFACT_D_MASK GENMASK(22, 16) +#define STM32MP13_CALFACT_S_SHIFT 0 +#define STM32MP13_CALFACT_S_MASK GENMASK(6, 0) + +/* STM32MP13_ADC2_OR - bit fields */ +#define STM32MP13_OP2 BIT(2) +#define STM32MP13_OP1 BIT(1) +#define STM32MP13_OP0 BIT(0) + +#define STM32MP15_IPIDR_NUMBER 0x00110005 +#define STM32MP13_IPIDR_NUMBER 0x00110006 + +/** + * struct stm32_adc_common - stm32 ADC driver common data (for all instances) + * @base: control registers base cpu addr + * @phys_base: control registers base physical addr + * @rate: clock rate used for analog circuitry + * @vref_mv: vref voltage (mv) + * @lock: spinlock + */ +struct stm32_adc_common { + void __iomem *base; + phys_addr_t phys_base; + unsigned long rate; + int vref_mv; + spinlock_t lock; /* lock for common register */ +}; + +#endif diff --git a/drivers/iio/adc/stm32-adc.c b/drivers/iio/adc/stm32-adc.c new file mode 100644 index 000000000..5c6c06b26 --- /dev/null +++ b/drivers/iio/adc/stm32-adc.c @@ -0,0 +1,2791 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * This file is part of STM32 ADC driver + * + * Copyright (C) 2016, STMicroelectronics - All Rights Reserved + * Author: Fabrice Gasnier . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "stm32-adc-core.h" + +/* Number of linear calibration shadow registers / LINCALRDYW control bits */ +#define STM32H7_LINCALFACT_NUM 6 + +/* BOOST bit must be set on STM32H7 when ADC clock is above 20MHz */ +#define STM32H7_BOOST_CLKRATE 20000000UL + +#define STM32_ADC_CH_MAX 20 /* max number of channels */ +#define STM32_ADC_CH_SZ 16 /* max channel name size */ +#define STM32_ADC_MAX_SQ 16 /* SQ1..SQ16 */ +#define STM32_ADC_MAX_SMP 7 /* SMPx range is [0..7] */ +#define STM32_ADC_TIMEOUT_US 100000 +#define STM32_ADC_TIMEOUT (msecs_to_jiffies(STM32_ADC_TIMEOUT_US / 1000)) +#define STM32_ADC_HW_STOP_DELAY_MS 100 +#define STM32_ADC_VREFINT_VOLTAGE 3300 + +#define STM32_DMA_BUFFER_SIZE PAGE_SIZE + +/* External trigger enable */ +enum stm32_adc_exten { + STM32_EXTEN_SWTRIG, + STM32_EXTEN_HWTRIG_RISING_EDGE, + STM32_EXTEN_HWTRIG_FALLING_EDGE, + STM32_EXTEN_HWTRIG_BOTH_EDGES, +}; + +/* extsel - trigger mux selection value */ +enum stm32_adc_extsel { + STM32_EXT0, + STM32_EXT1, + STM32_EXT2, + STM32_EXT3, + STM32_EXT4, + STM32_EXT5, + STM32_EXT6, + STM32_EXT7, + STM32_EXT8, + STM32_EXT9, + STM32_EXT10, + STM32_EXT11, + STM32_EXT12, + STM32_EXT13, + STM32_EXT14, + STM32_EXT15, + STM32_EXT16, + STM32_EXT17, + STM32_EXT18, + STM32_EXT19, + STM32_EXT20, +}; + +enum stm32_adc_int_ch { + STM32_ADC_INT_CH_NONE = -1, + STM32_ADC_INT_CH_VDDCORE, + STM32_ADC_INT_CH_VDDCPU, + STM32_ADC_INT_CH_VDDQ_DDR, + STM32_ADC_INT_CH_VREFINT, + STM32_ADC_INT_CH_VBAT, + STM32_ADC_INT_CH_NB, +}; + +/** + * struct stm32_adc_ic - ADC internal channels + * @name: name of the internal channel + * @idx: internal channel enum index + */ +struct stm32_adc_ic { + const char *name; + u32 idx; +}; + +static const struct stm32_adc_ic stm32_adc_ic[STM32_ADC_INT_CH_NB] = { + { "vddcore", STM32_ADC_INT_CH_VDDCORE }, + { "vddcpu", STM32_ADC_INT_CH_VDDCPU }, + { "vddq_ddr", STM32_ADC_INT_CH_VDDQ_DDR }, + { "vrefint", STM32_ADC_INT_CH_VREFINT }, + { "vbat", STM32_ADC_INT_CH_VBAT }, +}; + +/** + * struct stm32_adc_trig_info - ADC trigger info + * @name: name of the trigger, corresponding to its source + * @extsel: trigger selection + */ +struct stm32_adc_trig_info { + const char *name; + enum stm32_adc_extsel extsel; +}; + +/** + * struct stm32_adc_calib - optional adc calibration data + * @lincalfact: Linearity calibration factor + * @lincal_saved: Indicates that linear calibration factors are saved + */ +struct stm32_adc_calib { + u32 lincalfact[STM32H7_LINCALFACT_NUM]; + bool lincal_saved; +}; + +/** + * struct stm32_adc_regs - stm32 ADC misc registers & bitfield desc + * @reg: register offset + * @mask: bitfield mask + * @shift: left shift + */ +struct stm32_adc_regs { + int reg; + int mask; + int shift; +}; + +/** + * struct stm32_adc_vrefint - stm32 ADC internal reference voltage data + * @vrefint_cal: vrefint calibration value from nvmem + * @vrefint_data: vrefint actual value + */ +struct stm32_adc_vrefint { + u32 vrefint_cal; + u32 vrefint_data; +}; + +/** + * struct stm32_adc_regspec - stm32 registers definition + * @dr: data register offset + * @ier_eoc: interrupt enable register & eocie bitfield + * @ier_ovr: interrupt enable register & overrun bitfield + * @isr_eoc: interrupt status register & eoc bitfield + * @isr_ovr: interrupt status register & overrun bitfield + * @sqr: reference to sequence registers array + * @exten: trigger control register & bitfield + * @extsel: trigger selection register & bitfield + * @res: resolution selection register & bitfield + * @difsel: differential mode selection register & bitfield + * @smpr: smpr1 & smpr2 registers offset array + * @smp_bits: smpr1 & smpr2 index and bitfields + * @or_vddcore: option register & vddcore bitfield + * @or_vddcpu: option register & vddcpu bitfield + * @or_vddq_ddr: option register & vddq_ddr bitfield + * @ccr_vbat: common register & vbat bitfield + * @ccr_vref: common register & vrefint bitfield + */ +struct stm32_adc_regspec { + const u32 dr; + const struct stm32_adc_regs ier_eoc; + const struct stm32_adc_regs ier_ovr; + const struct stm32_adc_regs isr_eoc; + const struct stm32_adc_regs isr_ovr; + const struct stm32_adc_regs *sqr; + const struct stm32_adc_regs exten; + const struct stm32_adc_regs extsel; + const struct stm32_adc_regs res; + const struct stm32_adc_regs difsel; + const u32 smpr[2]; + const struct stm32_adc_regs *smp_bits; + const struct stm32_adc_regs or_vddcore; + const struct stm32_adc_regs or_vddcpu; + const struct stm32_adc_regs or_vddq_ddr; + const struct stm32_adc_regs ccr_vbat; + const struct stm32_adc_regs ccr_vref; +}; + +struct stm32_adc; + +/** + * struct stm32_adc_cfg - stm32 compatible configuration data + * @regs: registers descriptions + * @adc_info: per instance input channels definitions + * @trigs: external trigger sources + * @clk_required: clock is required + * @has_vregready: vregready status flag presence + * @has_boostmode: boost mode support flag + * @has_linearcal: linear calibration support flag + * @has_presel: channel preselection support flag + * @has_oversampling: oversampling support flag + * @prepare: optional prepare routine (power-up, enable) + * @start_conv: routine to start conversions + * @stop_conv: routine to stop conversions + * @unprepare: optional unprepare routine (disable, power-down) + * @irq_clear: routine to clear irqs + * @set_ovs: routine to set oversampling configuration + * @smp_cycles: programmable sampling time (ADC clock cycles) + * @ts_int_ch: pointer to array of internal channels minimum sampling time in ns + */ +struct stm32_adc_cfg { + const struct stm32_adc_regspec *regs; + const struct stm32_adc_info *adc_info; + const struct stm32_adc_trig_info *trigs; + bool clk_required; + bool has_vregready; + bool has_boostmode; + bool has_linearcal; + bool has_presel; + bool has_oversampling; + int (*prepare)(struct iio_dev *); + void (*start_conv)(struct iio_dev *, bool dma); + void (*stop_conv)(struct iio_dev *); + void (*unprepare)(struct iio_dev *); + void (*irq_clear)(struct iio_dev *indio_dev, u32 msk); + void (*set_ovs)(struct iio_dev *indio_dev, u32 ovs_idx); + const unsigned int *smp_cycles; + const unsigned int *ts_int_ch; +}; + +/** + * struct stm32_adc - private data of each ADC IIO instance + * @common: reference to ADC block common data + * @offset: ADC instance register offset in ADC block + * @cfg: compatible configuration data + * @completion: end of single conversion completion + * @buffer: data buffer + 8 bytes for timestamp if enabled + * @clk: clock for this adc instance + * @irq: interrupt for this adc instance + * @lock: spinlock + * @bufi: data buffer index + * @num_conv: expected number of scan conversions + * @res: data resolution (e.g. RES bitfield value) + * @trigger_polarity: external trigger polarity (e.g. exten) + * @dma_chan: dma channel + * @rx_buf: dma rx buffer cpu address + * @rx_dma_buf: dma rx buffer bus address + * @rx_buf_sz: dma rx buffer size + * @difsel: bitmask to set single-ended/differential channel + * @pcsel: bitmask to preselect channels on some devices + * @smpr_val: sampling time settings (e.g. smpr1 / smpr2) + * @cal: optional calibration data on some devices + * @vrefint: internal reference voltage data + * @chan_name: channel name array + * @num_diff: number of differential channels + * @int_ch: internal channel indexes array + * @nsmps: number of channels with optional sample time + * @ovs_idx: current oversampling ratio index (in oversampling array) + */ +struct stm32_adc { + struct stm32_adc_common *common; + u32 offset; + const struct stm32_adc_cfg *cfg; + struct completion completion; + u16 buffer[STM32_ADC_MAX_SQ + 4] __aligned(8); + struct clk *clk; + int irq; + spinlock_t lock; /* interrupt lock */ + unsigned int bufi; + unsigned int num_conv; + u32 res; + u32 trigger_polarity; + struct dma_chan *dma_chan; + u8 *rx_buf; + dma_addr_t rx_dma_buf; + unsigned int rx_buf_sz; + u32 difsel; + u32 pcsel; + u32 smpr_val[2]; + struct stm32_adc_calib cal; + struct stm32_adc_vrefint vrefint; + char chan_name[STM32_ADC_CH_MAX][STM32_ADC_CH_SZ]; + u32 num_diff; + int int_ch[STM32_ADC_INT_CH_NB]; + int nsmps; + int ovs_idx; +}; + +struct stm32_adc_diff_channel { + u32 vinp; + u32 vinn; +}; + +/** + * struct stm32_adc_info - stm32 ADC, per instance config data + * @max_channels: Number of channels + * @resolutions: available resolutions + * @oversampling: available oversampling ratios + * @num_res: number of available resolutions + * @num_ovs: number of available oversampling ratios + */ +struct stm32_adc_info { + int max_channels; + const unsigned int *resolutions; + const unsigned int *oversampling; + const unsigned int num_res; + const unsigned int num_ovs; +}; + +static const unsigned int stm32h7_adc_oversampling_avail[] = { + 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, +}; + +static const unsigned int stm32mp13_adc_oversampling_avail[] = { + 1, 2, 4, 8, 16, 32, 64, 128, 256, +}; + +static const unsigned int stm32f4_adc_resolutions[] = { + /* sorted values so the index matches RES[1:0] in STM32F4_ADC_CR1 */ + 12, 10, 8, 6, +}; + +/* stm32f4 can have up to 16 channels */ +static const struct stm32_adc_info stm32f4_adc_info = { + .max_channels = 16, + .resolutions = stm32f4_adc_resolutions, + .num_res = ARRAY_SIZE(stm32f4_adc_resolutions), +}; + +static const unsigned int stm32h7_adc_resolutions[] = { + /* sorted values so the index matches RES[2:0] in STM32H7_ADC_CFGR */ + 16, 14, 12, 10, 8, +}; + +/* stm32h7 can have up to 20 channels */ +static const struct stm32_adc_info stm32h7_adc_info = { + .max_channels = STM32_ADC_CH_MAX, + .resolutions = stm32h7_adc_resolutions, + .oversampling = stm32h7_adc_oversampling_avail, + .num_res = ARRAY_SIZE(stm32h7_adc_resolutions), + .num_ovs = ARRAY_SIZE(stm32h7_adc_oversampling_avail), +}; + +/* stm32mp13 can have up to 19 channels */ +static const struct stm32_adc_info stm32mp13_adc_info = { + .max_channels = 19, + .resolutions = stm32f4_adc_resolutions, + .oversampling = stm32mp13_adc_oversampling_avail, + .num_res = ARRAY_SIZE(stm32f4_adc_resolutions), + .num_ovs = ARRAY_SIZE(stm32mp13_adc_oversampling_avail), +}; + +/* + * stm32f4_sq - describe regular sequence registers + * - L: sequence len (register & bit field) + * - SQ1..SQ16: sequence entries (register & bit field) + */ +static const struct stm32_adc_regs stm32f4_sq[STM32_ADC_MAX_SQ + 1] = { + /* L: len bit field description to be kept as first element */ + { STM32F4_ADC_SQR1, GENMASK(23, 20), 20 }, + /* SQ1..SQ16 registers & bit fields (reg, mask, shift) */ + { STM32F4_ADC_SQR3, GENMASK(4, 0), 0 }, + { STM32F4_ADC_SQR3, GENMASK(9, 5), 5 }, + { STM32F4_ADC_SQR3, GENMASK(14, 10), 10 }, + { STM32F4_ADC_SQR3, GENMASK(19, 15), 15 }, + { STM32F4_ADC_SQR3, GENMASK(24, 20), 20 }, + { STM32F4_ADC_SQR3, GENMASK(29, 25), 25 }, + { STM32F4_ADC_SQR2, GENMASK(4, 0), 0 }, + { STM32F4_ADC_SQR2, GENMASK(9, 5), 5 }, + { STM32F4_ADC_SQR2, GENMASK(14, 10), 10 }, + { STM32F4_ADC_SQR2, GENMASK(19, 15), 15 }, + { STM32F4_ADC_SQR2, GENMASK(24, 20), 20 }, + { STM32F4_ADC_SQR2, GENMASK(29, 25), 25 }, + { STM32F4_ADC_SQR1, GENMASK(4, 0), 0 }, + { STM32F4_ADC_SQR1, GENMASK(9, 5), 5 }, + { STM32F4_ADC_SQR1, GENMASK(14, 10), 10 }, + { STM32F4_ADC_SQR1, GENMASK(19, 15), 15 }, +}; + +/* STM32F4 external trigger sources for all instances */ +static const struct stm32_adc_trig_info stm32f4_adc_trigs[] = { + { TIM1_CH1, STM32_EXT0 }, + { TIM1_CH2, STM32_EXT1 }, + { TIM1_CH3, STM32_EXT2 }, + { TIM2_CH2, STM32_EXT3 }, + { TIM2_CH3, STM32_EXT4 }, + { TIM2_CH4, STM32_EXT5 }, + { TIM2_TRGO, STM32_EXT6 }, + { TIM3_CH1, STM32_EXT7 }, + { TIM3_TRGO, STM32_EXT8 }, + { TIM4_CH4, STM32_EXT9 }, + { TIM5_CH1, STM32_EXT10 }, + { TIM5_CH2, STM32_EXT11 }, + { TIM5_CH3, STM32_EXT12 }, + { TIM8_CH1, STM32_EXT13 }, + { TIM8_TRGO, STM32_EXT14 }, + {}, /* sentinel */ +}; + +/* + * stm32f4_smp_bits[] - describe sampling time register index & bit fields + * Sorted so it can be indexed by channel number. + */ +static const struct stm32_adc_regs stm32f4_smp_bits[] = { + /* STM32F4_ADC_SMPR2: smpr[] index, mask, shift for SMP0 to SMP9 */ + { 1, GENMASK(2, 0), 0 }, + { 1, GENMASK(5, 3), 3 }, + { 1, GENMASK(8, 6), 6 }, + { 1, GENMASK(11, 9), 9 }, + { 1, GENMASK(14, 12), 12 }, + { 1, GENMASK(17, 15), 15 }, + { 1, GENMASK(20, 18), 18 }, + { 1, GENMASK(23, 21), 21 }, + { 1, GENMASK(26, 24), 24 }, + { 1, GENMASK(29, 27), 27 }, + /* STM32F4_ADC_SMPR1, smpr[] index, mask, shift for SMP10 to SMP18 */ + { 0, GENMASK(2, 0), 0 }, + { 0, GENMASK(5, 3), 3 }, + { 0, GENMASK(8, 6), 6 }, + { 0, GENMASK(11, 9), 9 }, + { 0, GENMASK(14, 12), 12 }, + { 0, GENMASK(17, 15), 15 }, + { 0, GENMASK(20, 18), 18 }, + { 0, GENMASK(23, 21), 21 }, + { 0, GENMASK(26, 24), 24 }, +}; + +/* STM32F4 programmable sampling time (ADC clock cycles) */ +static const unsigned int stm32f4_adc_smp_cycles[STM32_ADC_MAX_SMP + 1] = { + 3, 15, 28, 56, 84, 112, 144, 480, +}; + +static const struct stm32_adc_regspec stm32f4_adc_regspec = { + .dr = STM32F4_ADC_DR, + .ier_eoc = { STM32F4_ADC_CR1, STM32F4_EOCIE }, + .ier_ovr = { STM32F4_ADC_CR1, STM32F4_OVRIE }, + .isr_eoc = { STM32F4_ADC_SR, STM32F4_EOC }, + .isr_ovr = { STM32F4_ADC_SR, STM32F4_OVR }, + .sqr = stm32f4_sq, + .exten = { STM32F4_ADC_CR2, STM32F4_EXTEN_MASK, STM32F4_EXTEN_SHIFT }, + .extsel = { STM32F4_ADC_CR2, STM32F4_EXTSEL_MASK, + STM32F4_EXTSEL_SHIFT }, + .res = { STM32F4_ADC_CR1, STM32F4_RES_MASK, STM32F4_RES_SHIFT }, + .smpr = { STM32F4_ADC_SMPR1, STM32F4_ADC_SMPR2 }, + .smp_bits = stm32f4_smp_bits, +}; + +static const struct stm32_adc_regs stm32h7_sq[STM32_ADC_MAX_SQ + 1] = { + /* L: len bit field description to be kept as first element */ + { STM32H7_ADC_SQR1, GENMASK(3, 0), 0 }, + /* SQ1..SQ16 registers & bit fields (reg, mask, shift) */ + { STM32H7_ADC_SQR1, GENMASK(10, 6), 6 }, + { STM32H7_ADC_SQR1, GENMASK(16, 12), 12 }, + { STM32H7_ADC_SQR1, GENMASK(22, 18), 18 }, + { STM32H7_ADC_SQR1, GENMASK(28, 24), 24 }, + { STM32H7_ADC_SQR2, GENMASK(4, 0), 0 }, + { STM32H7_ADC_SQR2, GENMASK(10, 6), 6 }, + { STM32H7_ADC_SQR2, GENMASK(16, 12), 12 }, + { STM32H7_ADC_SQR2, GENMASK(22, 18), 18 }, + { STM32H7_ADC_SQR2, GENMASK(28, 24), 24 }, + { STM32H7_ADC_SQR3, GENMASK(4, 0), 0 }, + { STM32H7_ADC_SQR3, GENMASK(10, 6), 6 }, + { STM32H7_ADC_SQR3, GENMASK(16, 12), 12 }, + { STM32H7_ADC_SQR3, GENMASK(22, 18), 18 }, + { STM32H7_ADC_SQR3, GENMASK(28, 24), 24 }, + { STM32H7_ADC_SQR4, GENMASK(4, 0), 0 }, + { STM32H7_ADC_SQR4, GENMASK(10, 6), 6 }, +}; + +/* STM32H7 external trigger sources for all instances */ +static const struct stm32_adc_trig_info stm32h7_adc_trigs[] = { + { TIM1_CH1, STM32_EXT0 }, + { TIM1_CH2, STM32_EXT1 }, + { TIM1_CH3, STM32_EXT2 }, + { TIM2_CH2, STM32_EXT3 }, + { TIM3_TRGO, STM32_EXT4 }, + { TIM4_CH4, STM32_EXT5 }, + { TIM8_TRGO, STM32_EXT7 }, + { TIM8_TRGO2, STM32_EXT8 }, + { TIM1_TRGO, STM32_EXT9 }, + { TIM1_TRGO2, STM32_EXT10 }, + { TIM2_TRGO, STM32_EXT11 }, + { TIM4_TRGO, STM32_EXT12 }, + { TIM6_TRGO, STM32_EXT13 }, + { TIM15_TRGO, STM32_EXT14 }, + { TIM3_CH4, STM32_EXT15 }, + { LPTIM1_OUT, STM32_EXT18 }, + { LPTIM2_OUT, STM32_EXT19 }, + { LPTIM3_OUT, STM32_EXT20 }, + { } +}; + +/* + * stm32h7_smp_bits - describe sampling time register index & bit fields + * Sorted so it can be indexed by channel number. + */ +static const struct stm32_adc_regs stm32h7_smp_bits[] = { + /* STM32H7_ADC_SMPR1, smpr[] index, mask, shift for SMP0 to SMP9 */ + { 0, GENMASK(2, 0), 0 }, + { 0, GENMASK(5, 3), 3 }, + { 0, GENMASK(8, 6), 6 }, + { 0, GENMASK(11, 9), 9 }, + { 0, GENMASK(14, 12), 12 }, + { 0, GENMASK(17, 15), 15 }, + { 0, GENMASK(20, 18), 18 }, + { 0, GENMASK(23, 21), 21 }, + { 0, GENMASK(26, 24), 24 }, + { 0, GENMASK(29, 27), 27 }, + /* STM32H7_ADC_SMPR2, smpr[] index, mask, shift for SMP10 to SMP19 */ + { 1, GENMASK(2, 0), 0 }, + { 1, GENMASK(5, 3), 3 }, + { 1, GENMASK(8, 6), 6 }, + { 1, GENMASK(11, 9), 9 }, + { 1, GENMASK(14, 12), 12 }, + { 1, GENMASK(17, 15), 15 }, + { 1, GENMASK(20, 18), 18 }, + { 1, GENMASK(23, 21), 21 }, + { 1, GENMASK(26, 24), 24 }, + { 1, GENMASK(29, 27), 27 }, +}; + +/* STM32H7 programmable sampling time (ADC clock cycles, rounded down) */ +static const unsigned int stm32h7_adc_smp_cycles[STM32_ADC_MAX_SMP + 1] = { + 1, 2, 8, 16, 32, 64, 387, 810, +}; + +static const struct stm32_adc_regspec stm32h7_adc_regspec = { + .dr = STM32H7_ADC_DR, + .ier_eoc = { STM32H7_ADC_IER, STM32H7_EOCIE }, + .ier_ovr = { STM32H7_ADC_IER, STM32H7_OVRIE }, + .isr_eoc = { STM32H7_ADC_ISR, STM32H7_EOC }, + .isr_ovr = { STM32H7_ADC_ISR, STM32H7_OVR }, + .sqr = stm32h7_sq, + .exten = { STM32H7_ADC_CFGR, STM32H7_EXTEN_MASK, STM32H7_EXTEN_SHIFT }, + .extsel = { STM32H7_ADC_CFGR, STM32H7_EXTSEL_MASK, + STM32H7_EXTSEL_SHIFT }, + .res = { STM32H7_ADC_CFGR, STM32H7_RES_MASK, STM32H7_RES_SHIFT }, + .difsel = { STM32H7_ADC_DIFSEL, STM32H7_DIFSEL_MASK}, + .smpr = { STM32H7_ADC_SMPR1, STM32H7_ADC_SMPR2 }, + .smp_bits = stm32h7_smp_bits, +}; + +/* STM32MP13 programmable sampling time (ADC clock cycles, rounded down) */ +static const unsigned int stm32mp13_adc_smp_cycles[STM32_ADC_MAX_SMP + 1] = { + 2, 6, 12, 24, 47, 92, 247, 640, +}; + +static const struct stm32_adc_regspec stm32mp13_adc_regspec = { + .dr = STM32H7_ADC_DR, + .ier_eoc = { STM32H7_ADC_IER, STM32H7_EOCIE }, + .ier_ovr = { STM32H7_ADC_IER, STM32H7_OVRIE }, + .isr_eoc = { STM32H7_ADC_ISR, STM32H7_EOC }, + .isr_ovr = { STM32H7_ADC_ISR, STM32H7_OVR }, + .sqr = stm32h7_sq, + .exten = { STM32H7_ADC_CFGR, STM32H7_EXTEN_MASK, STM32H7_EXTEN_SHIFT }, + .extsel = { STM32H7_ADC_CFGR, STM32H7_EXTSEL_MASK, + STM32H7_EXTSEL_SHIFT }, + .res = { STM32H7_ADC_CFGR, STM32MP13_RES_MASK, STM32MP13_RES_SHIFT }, + .difsel = { STM32MP13_ADC_DIFSEL, STM32MP13_DIFSEL_MASK}, + .smpr = { STM32H7_ADC_SMPR1, STM32H7_ADC_SMPR2 }, + .smp_bits = stm32h7_smp_bits, + .or_vddcore = { STM32MP13_ADC2_OR, STM32MP13_OP0 }, + .or_vddcpu = { STM32MP13_ADC2_OR, STM32MP13_OP1 }, + .or_vddq_ddr = { STM32MP13_ADC2_OR, STM32MP13_OP2 }, + .ccr_vbat = { STM32H7_ADC_CCR, STM32H7_VBATEN }, + .ccr_vref = { STM32H7_ADC_CCR, STM32H7_VREFEN }, +}; + +static const struct stm32_adc_regspec stm32mp1_adc_regspec = { + .dr = STM32H7_ADC_DR, + .ier_eoc = { STM32H7_ADC_IER, STM32H7_EOCIE }, + .ier_ovr = { STM32H7_ADC_IER, STM32H7_OVRIE }, + .isr_eoc = { STM32H7_ADC_ISR, STM32H7_EOC }, + .isr_ovr = { STM32H7_ADC_ISR, STM32H7_OVR }, + .sqr = stm32h7_sq, + .exten = { STM32H7_ADC_CFGR, STM32H7_EXTEN_MASK, STM32H7_EXTEN_SHIFT }, + .extsel = { STM32H7_ADC_CFGR, STM32H7_EXTSEL_MASK, + STM32H7_EXTSEL_SHIFT }, + .res = { STM32H7_ADC_CFGR, STM32H7_RES_MASK, STM32H7_RES_SHIFT }, + .difsel = { STM32H7_ADC_DIFSEL, STM32H7_DIFSEL_MASK}, + .smpr = { STM32H7_ADC_SMPR1, STM32H7_ADC_SMPR2 }, + .smp_bits = stm32h7_smp_bits, + .or_vddcore = { STM32MP1_ADC2_OR, STM32MP1_VDDCOREEN }, + .ccr_vbat = { STM32H7_ADC_CCR, STM32H7_VBATEN }, + .ccr_vref = { STM32H7_ADC_CCR, STM32H7_VREFEN }, +}; + +/* + * STM32 ADC registers access routines + * @adc: stm32 adc instance + * @reg: reg offset in adc instance + * + * Note: All instances share same base, with 0x0, 0x100 or 0x200 offset resp. + * for adc1, adc2 and adc3. + */ +static u32 stm32_adc_readl(struct stm32_adc *adc, u32 reg) +{ + return readl_relaxed(adc->common->base + adc->offset + reg); +} + +#define stm32_adc_readl_addr(addr) stm32_adc_readl(adc, addr) + +#define stm32_adc_readl_poll_timeout(reg, val, cond, sleep_us, timeout_us) \ + readx_poll_timeout(stm32_adc_readl_addr, reg, val, \ + cond, sleep_us, timeout_us) + +static u16 stm32_adc_readw(struct stm32_adc *adc, u32 reg) +{ + return readw_relaxed(adc->common->base + adc->offset + reg); +} + +static void stm32_adc_writel(struct stm32_adc *adc, u32 reg, u32 val) +{ + writel_relaxed(val, adc->common->base + adc->offset + reg); +} + +static void stm32_adc_set_bits(struct stm32_adc *adc, u32 reg, u32 bits) +{ + unsigned long flags; + + spin_lock_irqsave(&adc->lock, flags); + stm32_adc_writel(adc, reg, stm32_adc_readl(adc, reg) | bits); + spin_unlock_irqrestore(&adc->lock, flags); +} + +static void stm32_adc_set_bits_common(struct stm32_adc *adc, u32 reg, u32 bits) +{ + spin_lock(&adc->common->lock); + writel_relaxed(readl_relaxed(adc->common->base + reg) | bits, + adc->common->base + reg); + spin_unlock(&adc->common->lock); +} + +static void stm32_adc_clr_bits(struct stm32_adc *adc, u32 reg, u32 bits) +{ + unsigned long flags; + + spin_lock_irqsave(&adc->lock, flags); + stm32_adc_writel(adc, reg, stm32_adc_readl(adc, reg) & ~bits); + spin_unlock_irqrestore(&adc->lock, flags); +} + +static void stm32_adc_clr_bits_common(struct stm32_adc *adc, u32 reg, u32 bits) +{ + spin_lock(&adc->common->lock); + writel_relaxed(readl_relaxed(adc->common->base + reg) & ~bits, + adc->common->base + reg); + spin_unlock(&adc->common->lock); +} + +/** + * stm32_adc_conv_irq_enable() - Enable end of conversion interrupt + * @adc: stm32 adc instance + */ +static void stm32_adc_conv_irq_enable(struct stm32_adc *adc) +{ + stm32_adc_set_bits(adc, adc->cfg->regs->ier_eoc.reg, + adc->cfg->regs->ier_eoc.mask); +}; + +/** + * stm32_adc_conv_irq_disable() - Disable end of conversion interrupt + * @adc: stm32 adc instance + */ +static void stm32_adc_conv_irq_disable(struct stm32_adc *adc) +{ + stm32_adc_clr_bits(adc, adc->cfg->regs->ier_eoc.reg, + adc->cfg->regs->ier_eoc.mask); +} + +static void stm32_adc_ovr_irq_enable(struct stm32_adc *adc) +{ + stm32_adc_set_bits(adc, adc->cfg->regs->ier_ovr.reg, + adc->cfg->regs->ier_ovr.mask); +} + +static void stm32_adc_ovr_irq_disable(struct stm32_adc *adc) +{ + stm32_adc_clr_bits(adc, adc->cfg->regs->ier_ovr.reg, + adc->cfg->regs->ier_ovr.mask); +} + +static void stm32_adc_set_res(struct stm32_adc *adc) +{ + const struct stm32_adc_regs *res = &adc->cfg->regs->res; + u32 val; + + val = stm32_adc_readl(adc, res->reg); + val = (val & ~res->mask) | (adc->res << res->shift); + stm32_adc_writel(adc, res->reg, val); +} + +static int stm32_adc_hw_stop(struct device *dev) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct stm32_adc *adc = iio_priv(indio_dev); + + if (adc->cfg->unprepare) + adc->cfg->unprepare(indio_dev); + + clk_disable_unprepare(adc->clk); + + return 0; +} + +static int stm32_adc_hw_start(struct device *dev) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct stm32_adc *adc = iio_priv(indio_dev); + int ret; + + ret = clk_prepare_enable(adc->clk); + if (ret) + return ret; + + stm32_adc_set_res(adc); + + if (adc->cfg->prepare) { + ret = adc->cfg->prepare(indio_dev); + if (ret) + goto err_clk_dis; + } + + return 0; + +err_clk_dis: + clk_disable_unprepare(adc->clk); + + return ret; +} + +static void stm32_adc_int_ch_enable(struct iio_dev *indio_dev) +{ + struct stm32_adc *adc = iio_priv(indio_dev); + u32 i; + + for (i = 0; i < STM32_ADC_INT_CH_NB; i++) { + if (adc->int_ch[i] == STM32_ADC_INT_CH_NONE) + continue; + + switch (i) { + case STM32_ADC_INT_CH_VDDCORE: + dev_dbg(&indio_dev->dev, "Enable VDDCore\n"); + stm32_adc_set_bits(adc, adc->cfg->regs->or_vddcore.reg, + adc->cfg->regs->or_vddcore.mask); + break; + case STM32_ADC_INT_CH_VDDCPU: + dev_dbg(&indio_dev->dev, "Enable VDDCPU\n"); + stm32_adc_set_bits(adc, adc->cfg->regs->or_vddcpu.reg, + adc->cfg->regs->or_vddcpu.mask); + break; + case STM32_ADC_INT_CH_VDDQ_DDR: + dev_dbg(&indio_dev->dev, "Enable VDDQ_DDR\n"); + stm32_adc_set_bits(adc, adc->cfg->regs->or_vddq_ddr.reg, + adc->cfg->regs->or_vddq_ddr.mask); + break; + case STM32_ADC_INT_CH_VREFINT: + dev_dbg(&indio_dev->dev, "Enable VREFInt\n"); + stm32_adc_set_bits_common(adc, adc->cfg->regs->ccr_vref.reg, + adc->cfg->regs->ccr_vref.mask); + break; + case STM32_ADC_INT_CH_VBAT: + dev_dbg(&indio_dev->dev, "Enable VBAT\n"); + stm32_adc_set_bits_common(adc, adc->cfg->regs->ccr_vbat.reg, + adc->cfg->regs->ccr_vbat.mask); + break; + } + } +} + +static void stm32_adc_int_ch_disable(struct stm32_adc *adc) +{ + u32 i; + + for (i = 0; i < STM32_ADC_INT_CH_NB; i++) { + if (adc->int_ch[i] == STM32_ADC_INT_CH_NONE) + continue; + + switch (i) { + case STM32_ADC_INT_CH_VDDCORE: + stm32_adc_clr_bits(adc, adc->cfg->regs->or_vddcore.reg, + adc->cfg->regs->or_vddcore.mask); + break; + case STM32_ADC_INT_CH_VDDCPU: + stm32_adc_clr_bits(adc, adc->cfg->regs->or_vddcpu.reg, + adc->cfg->regs->or_vddcpu.mask); + break; + case STM32_ADC_INT_CH_VDDQ_DDR: + stm32_adc_clr_bits(adc, adc->cfg->regs->or_vddq_ddr.reg, + adc->cfg->regs->or_vddq_ddr.mask); + break; + case STM32_ADC_INT_CH_VREFINT: + stm32_adc_clr_bits_common(adc, adc->cfg->regs->ccr_vref.reg, + adc->cfg->regs->ccr_vref.mask); + break; + case STM32_ADC_INT_CH_VBAT: + stm32_adc_clr_bits_common(adc, adc->cfg->regs->ccr_vbat.reg, + adc->cfg->regs->ccr_vbat.mask); + break; + } + } +} + +/** + * stm32f4_adc_start_conv() - Start conversions for regular channels. + * @indio_dev: IIO device instance + * @dma: use dma to transfer conversion result + * + * Start conversions for regular channels. + * Also take care of normal or DMA mode. Circular DMA may be used for regular + * conversions, in IIO buffer modes. Otherwise, use ADC interrupt with direct + * DR read instead (e.g. read_raw, or triggered buffer mode without DMA). + */ +static void stm32f4_adc_start_conv(struct iio_dev *indio_dev, bool dma) +{ + struct stm32_adc *adc = iio_priv(indio_dev); + + stm32_adc_set_bits(adc, STM32F4_ADC_CR1, STM32F4_SCAN); + + if (dma) + stm32_adc_set_bits(adc, STM32F4_ADC_CR2, + STM32F4_DMA | STM32F4_DDS); + + stm32_adc_set_bits(adc, STM32F4_ADC_CR2, STM32F4_EOCS | STM32F4_ADON); + + /* Wait for Power-up time (tSTAB from datasheet) */ + usleep_range(2, 3); + + /* Software start ? (e.g. trigger detection disabled ?) */ + if (!(stm32_adc_readl(adc, STM32F4_ADC_CR2) & STM32F4_EXTEN_MASK)) + stm32_adc_set_bits(adc, STM32F4_ADC_CR2, STM32F4_SWSTART); +} + +static void stm32f4_adc_stop_conv(struct iio_dev *indio_dev) +{ + struct stm32_adc *adc = iio_priv(indio_dev); + + stm32_adc_clr_bits(adc, STM32F4_ADC_CR2, STM32F4_EXTEN_MASK); + stm32_adc_clr_bits(adc, STM32F4_ADC_SR, STM32F4_STRT); + + stm32_adc_clr_bits(adc, STM32F4_ADC_CR1, STM32F4_SCAN); + stm32_adc_clr_bits(adc, STM32F4_ADC_CR2, + STM32F4_ADON | STM32F4_DMA | STM32F4_DDS); +} + +static void stm32f4_adc_irq_clear(struct iio_dev *indio_dev, u32 msk) +{ + struct stm32_adc *adc = iio_priv(indio_dev); + + stm32_adc_clr_bits(adc, adc->cfg->regs->isr_eoc.reg, msk); +} + +static void stm32h7_adc_start_conv(struct iio_dev *indio_dev, bool dma) +{ + struct stm32_adc *adc = iio_priv(indio_dev); + enum stm32h7_adc_dmngt dmngt; + unsigned long flags; + u32 val; + + if (dma) + dmngt = STM32H7_DMNGT_DMA_CIRC; + else + dmngt = STM32H7_DMNGT_DR_ONLY; + + spin_lock_irqsave(&adc->lock, flags); + val = stm32_adc_readl(adc, STM32H7_ADC_CFGR); + val = (val & ~STM32H7_DMNGT_MASK) | (dmngt << STM32H7_DMNGT_SHIFT); + stm32_adc_writel(adc, STM32H7_ADC_CFGR, val); + spin_unlock_irqrestore(&adc->lock, flags); + + stm32_adc_set_bits(adc, STM32H7_ADC_CR, STM32H7_ADSTART); +} + +static void stm32h7_adc_stop_conv(struct iio_dev *indio_dev) +{ + struct stm32_adc *adc = iio_priv(indio_dev); + int ret; + u32 val; + + stm32_adc_set_bits(adc, STM32H7_ADC_CR, STM32H7_ADSTP); + + ret = stm32_adc_readl_poll_timeout(STM32H7_ADC_CR, val, + !(val & (STM32H7_ADSTART)), + 100, STM32_ADC_TIMEOUT_US); + if (ret) + dev_warn(&indio_dev->dev, "stop failed\n"); + + /* STM32H7_DMNGT_MASK covers STM32MP13_DMAEN & STM32MP13_DMACFG */ + stm32_adc_clr_bits(adc, STM32H7_ADC_CFGR, STM32H7_DMNGT_MASK); +} + +static void stm32h7_adc_irq_clear(struct iio_dev *indio_dev, u32 msk) +{ + struct stm32_adc *adc = iio_priv(indio_dev); + /* On STM32H7 IRQs are cleared by writing 1 into ISR register */ + stm32_adc_set_bits(adc, adc->cfg->regs->isr_eoc.reg, msk); +} + +static void stm32mp13_adc_start_conv(struct iio_dev *indio_dev, bool dma) +{ + struct stm32_adc *adc = iio_priv(indio_dev); + + if (dma) + stm32_adc_set_bits(adc, STM32H7_ADC_CFGR, + STM32MP13_DMAEN | STM32MP13_DMACFG); + + stm32_adc_set_bits(adc, STM32H7_ADC_CR, STM32H7_ADSTART); +} + +static void stm32h7_adc_set_ovs(struct iio_dev *indio_dev, u32 ovs_idx) +{ + struct stm32_adc *adc = iio_priv(indio_dev); + u32 ovsr_bits, bits, msk; + + msk = STM32H7_ROVSE | STM32H7_OVSR_MASK | STM32H7_OVSS_MASK; + stm32_adc_clr_bits(adc, STM32H7_ADC_CFGR2, msk); + + if (!ovs_idx) + return; + + /* + * Only the oversampling ratios corresponding to 2^ovs_idx are exposed in sysfs. + * Oversampling ratios [2,3,...,1024] are mapped on OVSR register values [1,2,...,1023]. + * OVSR = 2^ovs_idx - 1 + * These ratio increase the resolution by ovs_idx bits. Apply a right shift to keep initial + * resolution given by "assigned-resolution-bits" property. + * OVSS = ovs_idx + */ + ovsr_bits = GENMASK(ovs_idx - 1, 0); + bits = STM32H7_ROVSE | STM32H7_OVSS(ovs_idx) | STM32H7_OVSR(ovsr_bits); + + stm32_adc_set_bits(adc, STM32H7_ADC_CFGR2, bits & msk); +} + +static void stm32mp13_adc_set_ovs(struct iio_dev *indio_dev, u32 ovs_idx) +{ + struct stm32_adc *adc = iio_priv(indio_dev); + u32 bits, msk; + + msk = STM32H7_ROVSE | STM32MP13_OVSR_MASK | STM32MP13_OVSS_MASK; + stm32_adc_clr_bits(adc, STM32H7_ADC_CFGR2, msk); + + if (!ovs_idx) + return; + + /* + * The oversampling ratios [2,4,8,..,256] are mapped on OVSR register values [0,1,...,7]. + * OVSR = ovs_idx - 1 + * These ratio increase the resolution by ovs_idx bits. Apply a right shift to keep initial + * resolution given by "assigned-resolution-bits" property. + * OVSS = ovs_idx + */ + bits = STM32H7_ROVSE | STM32MP13_OVSS(ovs_idx); + if (ovs_idx - 1) + bits |= STM32MP13_OVSR(ovs_idx - 1); + + stm32_adc_set_bits(adc, STM32H7_ADC_CFGR2, bits & msk); +} + +static int stm32h7_adc_exit_pwr_down(struct iio_dev *indio_dev) +{ + struct stm32_adc *adc = iio_priv(indio_dev); + int ret; + u32 val; + + /* Exit deep power down, then enable ADC voltage regulator */ + stm32_adc_clr_bits(adc, STM32H7_ADC_CR, STM32H7_DEEPPWD); + stm32_adc_set_bits(adc, STM32H7_ADC_CR, STM32H7_ADVREGEN); + + if (adc->cfg->has_boostmode && + adc->common->rate > STM32H7_BOOST_CLKRATE) + stm32_adc_set_bits(adc, STM32H7_ADC_CR, STM32H7_BOOST); + + /* Wait for startup time */ + if (!adc->cfg->has_vregready) { + usleep_range(10, 20); + return 0; + } + + ret = stm32_adc_readl_poll_timeout(STM32H7_ADC_ISR, val, + val & STM32MP1_VREGREADY, 100, + STM32_ADC_TIMEOUT_US); + if (ret) { + stm32_adc_set_bits(adc, STM32H7_ADC_CR, STM32H7_DEEPPWD); + dev_err(&indio_dev->dev, "Failed to exit power down\n"); + } + + return ret; +} + +static void stm32h7_adc_enter_pwr_down(struct stm32_adc *adc) +{ + if (adc->cfg->has_boostmode) + stm32_adc_clr_bits(adc, STM32H7_ADC_CR, STM32H7_BOOST); + + /* Setting DEEPPWD disables ADC vreg and clears ADVREGEN */ + stm32_adc_set_bits(adc, STM32H7_ADC_CR, STM32H7_DEEPPWD); +} + +static int stm32h7_adc_enable(struct iio_dev *indio_dev) +{ + struct stm32_adc *adc = iio_priv(indio_dev); + int ret; + u32 val; + + stm32_adc_set_bits(adc, STM32H7_ADC_CR, STM32H7_ADEN); + + /* Poll for ADRDY to be set (after adc startup time) */ + ret = stm32_adc_readl_poll_timeout(STM32H7_ADC_ISR, val, + val & STM32H7_ADRDY, + 100, STM32_ADC_TIMEOUT_US); + if (ret) { + stm32_adc_set_bits(adc, STM32H7_ADC_CR, STM32H7_ADDIS); + dev_err(&indio_dev->dev, "Failed to enable ADC\n"); + } else { + /* Clear ADRDY by writing one */ + stm32_adc_set_bits(adc, STM32H7_ADC_ISR, STM32H7_ADRDY); + } + + return ret; +} + +static void stm32h7_adc_disable(struct iio_dev *indio_dev) +{ + struct stm32_adc *adc = iio_priv(indio_dev); + int ret; + u32 val; + + if (!(stm32_adc_readl(adc, STM32H7_ADC_CR) & STM32H7_ADEN)) + return; + + /* Disable ADC and wait until it's effectively disabled */ + stm32_adc_set_bits(adc, STM32H7_ADC_CR, STM32H7_ADDIS); + ret = stm32_adc_readl_poll_timeout(STM32H7_ADC_CR, val, + !(val & STM32H7_ADEN), 100, + STM32_ADC_TIMEOUT_US); + if (ret) + dev_warn(&indio_dev->dev, "Failed to disable\n"); +} + +/** + * stm32h7_adc_read_selfcalib() - read calibration shadow regs, save result + * @indio_dev: IIO device instance + * Note: Must be called once ADC is enabled, so LINCALRDYW[1..6] are writable + */ +static int stm32h7_adc_read_selfcalib(struct iio_dev *indio_dev) +{ + struct stm32_adc *adc = iio_priv(indio_dev); + int i, ret; + u32 lincalrdyw_mask, val; + + /* Read linearity calibration */ + lincalrdyw_mask = STM32H7_LINCALRDYW6; + for (i = STM32H7_LINCALFACT_NUM - 1; i >= 0; i--) { + /* Clear STM32H7_LINCALRDYW[6..1]: transfer calib to CALFACT2 */ + stm32_adc_clr_bits(adc, STM32H7_ADC_CR, lincalrdyw_mask); + + /* Poll: wait calib data to be ready in CALFACT2 register */ + ret = stm32_adc_readl_poll_timeout(STM32H7_ADC_CR, val, + !(val & lincalrdyw_mask), + 100, STM32_ADC_TIMEOUT_US); + if (ret) { + dev_err(&indio_dev->dev, "Failed to read calfact\n"); + return ret; + } + + val = stm32_adc_readl(adc, STM32H7_ADC_CALFACT2); + adc->cal.lincalfact[i] = (val & STM32H7_LINCALFACT_MASK); + adc->cal.lincalfact[i] >>= STM32H7_LINCALFACT_SHIFT; + + lincalrdyw_mask >>= 1; + } + adc->cal.lincal_saved = true; + + return 0; +} + +/** + * stm32h7_adc_restore_selfcalib() - Restore saved self-calibration result + * @indio_dev: IIO device instance + * Note: ADC must be enabled, with no on-going conversions. + */ +static int stm32h7_adc_restore_selfcalib(struct iio_dev *indio_dev) +{ + struct stm32_adc *adc = iio_priv(indio_dev); + int i, ret; + u32 lincalrdyw_mask, val; + + lincalrdyw_mask = STM32H7_LINCALRDYW6; + for (i = STM32H7_LINCALFACT_NUM - 1; i >= 0; i--) { + /* + * Write saved calibration data to shadow registers: + * Write CALFACT2, and set LINCALRDYW[6..1] bit to trigger + * data write. Then poll to wait for complete transfer. + */ + val = adc->cal.lincalfact[i] << STM32H7_LINCALFACT_SHIFT; + stm32_adc_writel(adc, STM32H7_ADC_CALFACT2, val); + stm32_adc_set_bits(adc, STM32H7_ADC_CR, lincalrdyw_mask); + ret = stm32_adc_readl_poll_timeout(STM32H7_ADC_CR, val, + val & lincalrdyw_mask, + 100, STM32_ADC_TIMEOUT_US); + if (ret) { + dev_err(&indio_dev->dev, "Failed to write calfact\n"); + return ret; + } + + /* + * Read back calibration data, has two effects: + * - It ensures bits LINCALRDYW[6..1] are kept cleared + * for next time calibration needs to be restored. + * - BTW, bit clear triggers a read, then check data has been + * correctly written. + */ + stm32_adc_clr_bits(adc, STM32H7_ADC_CR, lincalrdyw_mask); + ret = stm32_adc_readl_poll_timeout(STM32H7_ADC_CR, val, + !(val & lincalrdyw_mask), + 100, STM32_ADC_TIMEOUT_US); + if (ret) { + dev_err(&indio_dev->dev, "Failed to read calfact\n"); + return ret; + } + val = stm32_adc_readl(adc, STM32H7_ADC_CALFACT2); + if (val != adc->cal.lincalfact[i] << STM32H7_LINCALFACT_SHIFT) { + dev_err(&indio_dev->dev, "calfact not consistent\n"); + return -EIO; + } + + lincalrdyw_mask >>= 1; + } + + return 0; +} + +/* + * Fixed timeout value for ADC calibration. + * worst cases: + * - low clock frequency + * - maximum prescalers + * Calibration requires: + * - 131,072 ADC clock cycle for the linear calibration + * - 20 ADC clock cycle for the offset calibration + * + * Set to 100ms for now + */ +#define STM32H7_ADC_CALIB_TIMEOUT_US 100000 + +/** + * stm32h7_adc_selfcalib() - Procedure to calibrate ADC + * @indio_dev: IIO device instance + * @do_lincal: linear calibration request flag + * Note: Must be called once ADC is out of power down. + * + * Run offset calibration unconditionally. + * Run linear calibration if requested & supported. + */ +static int stm32h7_adc_selfcalib(struct iio_dev *indio_dev, int do_lincal) +{ + struct stm32_adc *adc = iio_priv(indio_dev); + int ret; + u32 msk = STM32H7_ADCALDIF; + u32 val; + + if (adc->cfg->has_linearcal && do_lincal) + msk |= STM32H7_ADCALLIN; + /* ADC must be disabled for calibration */ + stm32h7_adc_disable(indio_dev); + + /* + * Select calibration mode: + * - Offset calibration for single ended inputs + * - No linearity calibration (do it later, before reading it) + */ + stm32_adc_clr_bits(adc, STM32H7_ADC_CR, msk); + + /* Start calibration, then wait for completion */ + stm32_adc_set_bits(adc, STM32H7_ADC_CR, STM32H7_ADCAL); + ret = stm32_adc_readl_poll_timeout(STM32H7_ADC_CR, val, + !(val & STM32H7_ADCAL), 100, + STM32H7_ADC_CALIB_TIMEOUT_US); + if (ret) { + dev_err(&indio_dev->dev, "calibration (single-ended) error %d\n", ret); + goto out; + } + + /* + * Select calibration mode, then start calibration: + * - Offset calibration for differential input + * - Linearity calibration (needs to be done only once for single/diff) + * will run simultaneously with offset calibration. + */ + stm32_adc_set_bits(adc, STM32H7_ADC_CR, msk); + stm32_adc_set_bits(adc, STM32H7_ADC_CR, STM32H7_ADCAL); + ret = stm32_adc_readl_poll_timeout(STM32H7_ADC_CR, val, + !(val & STM32H7_ADCAL), 100, + STM32H7_ADC_CALIB_TIMEOUT_US); + if (ret) { + dev_err(&indio_dev->dev, "calibration (diff%s) error %d\n", + (msk & STM32H7_ADCALLIN) ? "+linear" : "", ret); + goto out; + } + +out: + stm32_adc_clr_bits(adc, STM32H7_ADC_CR, msk); + + return ret; +} + +/** + * stm32h7_adc_check_selfcalib() - Check linear calibration status + * @indio_dev: IIO device instance + * + * Used to check if linear calibration has been done. + * Return true if linear calibration factors are already saved in private data + * or if a linear calibration has been done at boot stage. + */ +static int stm32h7_adc_check_selfcalib(struct iio_dev *indio_dev) +{ + struct stm32_adc *adc = iio_priv(indio_dev); + u32 val; + + if (adc->cal.lincal_saved) + return true; + + /* + * Check if linear calibration factors are available in ADC registers, + * by checking that all LINCALRDYWx bits are set. + */ + val = stm32_adc_readl(adc, STM32H7_ADC_CR) & STM32H7_LINCALRDYW_MASK; + if (val == STM32H7_LINCALRDYW_MASK) + return true; + + return false; +} + +/** + * stm32h7_adc_prepare() - Leave power down mode to enable ADC. + * @indio_dev: IIO device instance + * Leave power down mode. + * Configure channels as single ended or differential before enabling ADC. + * Enable ADC. + * Restore calibration data. + * Pre-select channels that may be used in PCSEL (required by input MUX / IO): + * - Only one input is selected for single ended (e.g. 'vinp') + * - Two inputs are selected for differential channels (e.g. 'vinp' & 'vinn') + */ +static int stm32h7_adc_prepare(struct iio_dev *indio_dev) +{ + struct stm32_adc *adc = iio_priv(indio_dev); + int lincal_done = false; + int ret; + + ret = stm32h7_adc_exit_pwr_down(indio_dev); + if (ret) + return ret; + + if (adc->cfg->has_linearcal) + lincal_done = stm32h7_adc_check_selfcalib(indio_dev); + + /* Always run offset calibration. Run linear calibration only once */ + ret = stm32h7_adc_selfcalib(indio_dev, !lincal_done); + if (ret < 0) + goto pwr_dwn; + + stm32_adc_int_ch_enable(indio_dev); + + stm32_adc_writel(adc, adc->cfg->regs->difsel.reg, adc->difsel); + + ret = stm32h7_adc_enable(indio_dev); + if (ret) + goto ch_disable; + + if (adc->cfg->has_linearcal) { + if (!adc->cal.lincal_saved) + ret = stm32h7_adc_read_selfcalib(indio_dev); + else + ret = stm32h7_adc_restore_selfcalib(indio_dev); + + if (ret) + goto disable; + } + + if (adc->cfg->has_presel) + stm32_adc_writel(adc, STM32H7_ADC_PCSEL, adc->pcsel); + + return 0; + +disable: + stm32h7_adc_disable(indio_dev); +ch_disable: + stm32_adc_int_ch_disable(adc); +pwr_dwn: + stm32h7_adc_enter_pwr_down(adc); + + return ret; +} + +static void stm32h7_adc_unprepare(struct iio_dev *indio_dev) +{ + struct stm32_adc *adc = iio_priv(indio_dev); + + if (adc->cfg->has_presel) + stm32_adc_writel(adc, STM32H7_ADC_PCSEL, 0); + stm32h7_adc_disable(indio_dev); + stm32_adc_int_ch_disable(adc); + stm32h7_adc_enter_pwr_down(adc); +} + +/** + * stm32_adc_conf_scan_seq() - Build regular channels scan sequence + * @indio_dev: IIO device + * @scan_mask: channels to be converted + * + * Conversion sequence : + * Apply sampling time settings for all channels. + * Configure ADC scan sequence based on selected channels in scan_mask. + * Add channels to SQR registers, from scan_mask LSB to MSB, then + * program sequence len. + */ +static int stm32_adc_conf_scan_seq(struct iio_dev *indio_dev, + const unsigned long *scan_mask) +{ + struct stm32_adc *adc = iio_priv(indio_dev); + const struct stm32_adc_regs *sqr = adc->cfg->regs->sqr; + const struct iio_chan_spec *chan; + u32 val, bit; + int i = 0; + + /* Apply sampling time settings */ + stm32_adc_writel(adc, adc->cfg->regs->smpr[0], adc->smpr_val[0]); + stm32_adc_writel(adc, adc->cfg->regs->smpr[1], adc->smpr_val[1]); + + for_each_set_bit(bit, scan_mask, iio_get_masklength(indio_dev)) { + chan = indio_dev->channels + bit; + /* + * Assign one channel per SQ entry in regular + * sequence, starting with SQ1. + */ + i++; + if (i > STM32_ADC_MAX_SQ) + return -EINVAL; + + dev_dbg(&indio_dev->dev, "%s chan %d to SQ%d\n", + __func__, chan->channel, i); + + val = stm32_adc_readl(adc, sqr[i].reg); + val &= ~sqr[i].mask; + val |= chan->channel << sqr[i].shift; + stm32_adc_writel(adc, sqr[i].reg, val); + } + + if (!i) + return -EINVAL; + + /* Sequence len */ + val = stm32_adc_readl(adc, sqr[0].reg); + val &= ~sqr[0].mask; + val |= ((i - 1) << sqr[0].shift); + stm32_adc_writel(adc, sqr[0].reg, val); + + return 0; +} + +/** + * stm32_adc_get_trig_extsel() - Get external trigger selection + * @indio_dev: IIO device structure + * @trig: trigger + * + * Returns trigger extsel value, if trig matches, -EINVAL otherwise. + */ +static int stm32_adc_get_trig_extsel(struct iio_dev *indio_dev, + struct iio_trigger *trig) +{ + struct stm32_adc *adc = iio_priv(indio_dev); + int i; + + /* lookup triggers registered by stm32 timer trigger driver */ + for (i = 0; adc->cfg->trigs[i].name; i++) { + /** + * Checking both stm32 timer trigger type and trig name + * should be safe against arbitrary trigger names. + */ + if ((is_stm32_timer_trigger(trig) || + is_stm32_lptim_trigger(trig)) && + !strcmp(adc->cfg->trigs[i].name, trig->name)) { + return adc->cfg->trigs[i].extsel; + } + } + + return -EINVAL; +} + +/** + * stm32_adc_set_trig() - Set a regular trigger + * @indio_dev: IIO device + * @trig: IIO trigger + * + * Set trigger source/polarity (e.g. SW, or HW with polarity) : + * - if HW trigger disabled (e.g. trig == NULL, conversion launched by sw) + * - if HW trigger enabled, set source & polarity + */ +static int stm32_adc_set_trig(struct iio_dev *indio_dev, + struct iio_trigger *trig) +{ + struct stm32_adc *adc = iio_priv(indio_dev); + u32 val, extsel = 0, exten = STM32_EXTEN_SWTRIG; + unsigned long flags; + int ret; + + if (trig) { + ret = stm32_adc_get_trig_extsel(indio_dev, trig); + if (ret < 0) + return ret; + + /* set trigger source and polarity (default to rising edge) */ + extsel = ret; + exten = adc->trigger_polarity + STM32_EXTEN_HWTRIG_RISING_EDGE; + } + + spin_lock_irqsave(&adc->lock, flags); + val = stm32_adc_readl(adc, adc->cfg->regs->exten.reg); + val &= ~(adc->cfg->regs->exten.mask | adc->cfg->regs->extsel.mask); + val |= exten << adc->cfg->regs->exten.shift; + val |= extsel << adc->cfg->regs->extsel.shift; + stm32_adc_writel(adc, adc->cfg->regs->exten.reg, val); + spin_unlock_irqrestore(&adc->lock, flags); + + return 0; +} + +static int stm32_adc_set_trig_pol(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + unsigned int type) +{ + struct stm32_adc *adc = iio_priv(indio_dev); + + adc->trigger_polarity = type; + + return 0; +} + +static int stm32_adc_get_trig_pol(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan) +{ + struct stm32_adc *adc = iio_priv(indio_dev); + + return adc->trigger_polarity; +} + +static const char * const stm32_trig_pol_items[] = { + "rising-edge", "falling-edge", "both-edges", +}; + +static const struct iio_enum stm32_adc_trig_pol = { + .items = stm32_trig_pol_items, + .num_items = ARRAY_SIZE(stm32_trig_pol_items), + .get = stm32_adc_get_trig_pol, + .set = stm32_adc_set_trig_pol, +}; + +/** + * stm32_adc_single_conv() - Performs a single conversion + * @indio_dev: IIO device + * @chan: IIO channel + * @res: conversion result + * + * The function performs a single conversion on a given channel: + * - Apply sampling time settings + * - Program sequencer with one channel (e.g. in SQ1 with len = 1) + * - Use SW trigger + * - Start conversion, then wait for interrupt completion. + */ +static int stm32_adc_single_conv(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + int *res) +{ + struct stm32_adc *adc = iio_priv(indio_dev); + struct device *dev = indio_dev->dev.parent; + const struct stm32_adc_regspec *regs = adc->cfg->regs; + long time_left; + u32 val; + int ret; + + reinit_completion(&adc->completion); + + adc->bufi = 0; + + ret = pm_runtime_resume_and_get(dev); + if (ret < 0) + return ret; + + /* Apply sampling time settings */ + stm32_adc_writel(adc, regs->smpr[0], adc->smpr_val[0]); + stm32_adc_writel(adc, regs->smpr[1], adc->smpr_val[1]); + + /* Program chan number in regular sequence (SQ1) */ + val = stm32_adc_readl(adc, regs->sqr[1].reg); + val &= ~regs->sqr[1].mask; + val |= chan->channel << regs->sqr[1].shift; + stm32_adc_writel(adc, regs->sqr[1].reg, val); + + /* Set regular sequence len (0 for 1 conversion) */ + stm32_adc_clr_bits(adc, regs->sqr[0].reg, regs->sqr[0].mask); + + /* Trigger detection disabled (conversion can be launched in SW) */ + stm32_adc_clr_bits(adc, regs->exten.reg, regs->exten.mask); + + stm32_adc_conv_irq_enable(adc); + + adc->cfg->start_conv(indio_dev, false); + + time_left = wait_for_completion_interruptible_timeout( + &adc->completion, STM32_ADC_TIMEOUT); + if (time_left == 0) { + ret = -ETIMEDOUT; + } else if (time_left < 0) { + ret = time_left; + } else { + *res = adc->buffer[0]; + ret = IIO_VAL_INT; + } + + adc->cfg->stop_conv(indio_dev); + + stm32_adc_conv_irq_disable(adc); + + pm_runtime_put_autosuspend(dev); + + return ret; +} + +static int stm32_adc_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, int val2, long mask) +{ + struct stm32_adc *adc = iio_priv(indio_dev); + struct device *dev = indio_dev->dev.parent; + int nb = adc->cfg->adc_info->num_ovs; + unsigned int idx; + int ret; + + switch (mask) { + case IIO_CHAN_INFO_OVERSAMPLING_RATIO: + if (val2) + return -EINVAL; + + for (idx = 0; idx < nb; idx++) + if (adc->cfg->adc_info->oversampling[idx] == val) + break; + if (idx >= nb) + return -EINVAL; + + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; + + ret = pm_runtime_resume_and_get(dev); + if (ret < 0) + goto err; + + adc->cfg->set_ovs(indio_dev, idx); + + pm_runtime_put_autosuspend(dev); + + adc->ovs_idx = idx; + +err: + iio_device_release_direct(indio_dev); + + return ret; + default: + return -EINVAL; + } +} + +static int stm32_adc_read_avail(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + const int **vals, int *type, int *length, long m) +{ + struct stm32_adc *adc = iio_priv(indio_dev); + + switch (m) { + case IIO_CHAN_INFO_OVERSAMPLING_RATIO: + *type = IIO_VAL_INT; + *length = adc->cfg->adc_info->num_ovs; + *vals = adc->cfg->adc_info->oversampling; + return IIO_AVAIL_LIST; + default: + return -EINVAL; + } +} + +static int stm32_adc_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct stm32_adc *adc = iio_priv(indio_dev); + int ret; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + case IIO_CHAN_INFO_PROCESSED: + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; + if (chan->type == IIO_VOLTAGE) + ret = stm32_adc_single_conv(indio_dev, chan, val); + else + ret = -EINVAL; + + if (mask == IIO_CHAN_INFO_PROCESSED) + *val = STM32_ADC_VREFINT_VOLTAGE * adc->vrefint.vrefint_cal / *val; + + iio_device_release_direct(indio_dev); + return ret; + + case IIO_CHAN_INFO_SCALE: + if (chan->differential) { + *val = adc->common->vref_mv * 2; + *val2 = chan->scan_type.realbits; + } else { + *val = adc->common->vref_mv; + *val2 = chan->scan_type.realbits; + } + return IIO_VAL_FRACTIONAL_LOG2; + + case IIO_CHAN_INFO_OFFSET: + if (chan->differential) + /* ADC_full_scale / 2 */ + *val = -((1 << chan->scan_type.realbits) / 2); + else + *val = 0; + return IIO_VAL_INT; + + case IIO_CHAN_INFO_OVERSAMPLING_RATIO: + *val = adc->cfg->adc_info->oversampling[adc->ovs_idx]; + return IIO_VAL_INT; + + default: + return -EINVAL; + } +} + +static void stm32_adc_irq_clear(struct iio_dev *indio_dev, u32 msk) +{ + struct stm32_adc *adc = iio_priv(indio_dev); + + adc->cfg->irq_clear(indio_dev, msk); +} + +static irqreturn_t stm32_adc_threaded_isr(int irq, void *data) +{ + struct iio_dev *indio_dev = data; + struct stm32_adc *adc = iio_priv(indio_dev); + const struct stm32_adc_regspec *regs = adc->cfg->regs; + u32 status = stm32_adc_readl(adc, regs->isr_eoc.reg); + + /* Check ovr status right now, as ovr mask should be already disabled */ + if (status & regs->isr_ovr.mask) { + /* + * Clear ovr bit to avoid subsequent calls to IRQ handler. + * This requires to stop ADC first. OVR bit state in ISR, + * is propagated to CSR register by hardware. + */ + adc->cfg->stop_conv(indio_dev); + stm32_adc_irq_clear(indio_dev, regs->isr_ovr.mask); + dev_err(&indio_dev->dev, "Overrun, stopping: restart needed\n"); + return IRQ_HANDLED; + } + + return IRQ_NONE; +} + +static irqreturn_t stm32_adc_isr(int irq, void *data) +{ + struct iio_dev *indio_dev = data; + struct stm32_adc *adc = iio_priv(indio_dev); + const struct stm32_adc_regspec *regs = adc->cfg->regs; + u32 status = stm32_adc_readl(adc, regs->isr_eoc.reg); + + if (status & regs->isr_ovr.mask) { + /* + * Overrun occurred on regular conversions: data for wrong + * channel may be read. Unconditionally disable interrupts + * to stop processing data and print error message. + * Restarting the capture can be done by disabling, then + * re-enabling it (e.g. write 0, then 1 to buffer/enable). + */ + stm32_adc_ovr_irq_disable(adc); + stm32_adc_conv_irq_disable(adc); + return IRQ_WAKE_THREAD; + } + + if (status & regs->isr_eoc.mask) { + /* Reading DR also clears EOC status flag */ + adc->buffer[adc->bufi] = stm32_adc_readw(adc, regs->dr); + if (iio_buffer_enabled(indio_dev)) { + adc->bufi++; + if (adc->bufi >= adc->num_conv) { + stm32_adc_conv_irq_disable(adc); + iio_trigger_poll(indio_dev->trig); + } + } else { + complete(&adc->completion); + } + return IRQ_HANDLED; + } + + return IRQ_NONE; +} + +/** + * stm32_adc_validate_trigger() - validate trigger for stm32 adc + * @indio_dev: IIO device + * @trig: new trigger + * + * Returns: 0 if trig matches one of the triggers registered by stm32 adc + * driver, -EINVAL otherwise. + */ +static int stm32_adc_validate_trigger(struct iio_dev *indio_dev, + struct iio_trigger *trig) +{ + return stm32_adc_get_trig_extsel(indio_dev, trig) < 0 ? -EINVAL : 0; +} + +static int stm32_adc_set_watermark(struct iio_dev *indio_dev, unsigned int val) +{ + struct stm32_adc *adc = iio_priv(indio_dev); + unsigned int watermark = STM32_DMA_BUFFER_SIZE / 2; + unsigned int rx_buf_sz = STM32_DMA_BUFFER_SIZE; + + /* + * dma cyclic transfers are used, buffer is split into two periods. + * There should be : + * - always one buffer (period) dma is working on + * - one buffer (period) driver can push data. + */ + watermark = min(watermark, val * (unsigned)(sizeof(u16))); + adc->rx_buf_sz = min(rx_buf_sz, watermark * 2 * adc->num_conv); + + return 0; +} + +static int stm32_adc_update_scan_mode(struct iio_dev *indio_dev, + const unsigned long *scan_mask) +{ + struct stm32_adc *adc = iio_priv(indio_dev); + struct device *dev = indio_dev->dev.parent; + int ret; + + ret = pm_runtime_resume_and_get(dev); + if (ret < 0) + return ret; + + adc->num_conv = bitmap_weight(scan_mask, iio_get_masklength(indio_dev)); + + ret = stm32_adc_conf_scan_seq(indio_dev, scan_mask); + pm_runtime_put_autosuspend(dev); + + return ret; +} + +static int stm32_adc_fwnode_xlate(struct iio_dev *indio_dev, + const struct fwnode_reference_args *iiospec) +{ + int i; + + for (i = 0; i < indio_dev->num_channels; i++) + if (indio_dev->channels[i].channel == iiospec->args[0]) + return i; + + return -EINVAL; +} + +/** + * stm32_adc_debugfs_reg_access - read or write register value + * @indio_dev: IIO device structure + * @reg: register offset + * @writeval: value to write + * @readval: value to read + * + * To read a value from an ADC register: + * echo [ADC reg offset] > direct_reg_access + * cat direct_reg_access + * + * To write a value in a ADC register: + * echo [ADC_reg_offset] [value] > direct_reg_access + */ +static int stm32_adc_debugfs_reg_access(struct iio_dev *indio_dev, + unsigned reg, unsigned writeval, + unsigned *readval) +{ + struct stm32_adc *adc = iio_priv(indio_dev); + struct device *dev = indio_dev->dev.parent; + int ret; + + ret = pm_runtime_resume_and_get(dev); + if (ret < 0) + return ret; + + if (!readval) + stm32_adc_writel(adc, reg, writeval); + else + *readval = stm32_adc_readl(adc, reg); + + pm_runtime_put_autosuspend(dev); + + return 0; +} + +static const struct iio_info stm32_adc_iio_info = { + .read_raw = stm32_adc_read_raw, + .write_raw = stm32_adc_write_raw, + .read_avail = stm32_adc_read_avail, + .validate_trigger = stm32_adc_validate_trigger, + .hwfifo_set_watermark = stm32_adc_set_watermark, + .update_scan_mode = stm32_adc_update_scan_mode, + .debugfs_reg_access = stm32_adc_debugfs_reg_access, + .fwnode_xlate = stm32_adc_fwnode_xlate, +}; + +static unsigned int stm32_adc_dma_residue(struct stm32_adc *adc) +{ + struct dma_tx_state state; + enum dma_status status; + + status = dmaengine_tx_status(adc->dma_chan, + adc->dma_chan->cookie, + &state); + if (status == DMA_IN_PROGRESS) { + /* Residue is size in bytes from end of buffer */ + unsigned int i = adc->rx_buf_sz - state.residue; + unsigned int size; + + /* Return available bytes */ + if (i >= adc->bufi) + size = i - adc->bufi; + else + size = adc->rx_buf_sz + i - adc->bufi; + + return size; + } + + return 0; +} + +static void stm32_adc_dma_buffer_done(void *data) +{ + struct iio_dev *indio_dev = data; + struct stm32_adc *adc = iio_priv(indio_dev); + int residue = stm32_adc_dma_residue(adc); + + /* + * In DMA mode the trigger services of IIO are not used + * (e.g. no call to iio_trigger_poll). + * Calling irq handler associated to the hardware trigger is not + * relevant as the conversions have already been done. Data + * transfers are performed directly in DMA callback instead. + * This implementation avoids to call trigger irq handler that + * may sleep, in an atomic context (DMA irq handler context). + */ + dev_dbg(&indio_dev->dev, "%s bufi=%d\n", __func__, adc->bufi); + + while (residue >= indio_dev->scan_bytes) { + u16 *buffer = (u16 *)&adc->rx_buf[adc->bufi]; + + iio_push_to_buffers(indio_dev, buffer); + + residue -= indio_dev->scan_bytes; + adc->bufi += indio_dev->scan_bytes; + if (adc->bufi >= adc->rx_buf_sz) + adc->bufi = 0; + } +} + +static int stm32_adc_dma_start(struct iio_dev *indio_dev) +{ + struct stm32_adc *adc = iio_priv(indio_dev); + struct dma_async_tx_descriptor *desc; + dma_cookie_t cookie; + int ret; + + if (!adc->dma_chan) + return 0; + + dev_dbg(&indio_dev->dev, "%s size=%d watermark=%d\n", __func__, + adc->rx_buf_sz, adc->rx_buf_sz / 2); + + /* Prepare a DMA cyclic transaction */ + desc = dmaengine_prep_dma_cyclic(adc->dma_chan, + adc->rx_dma_buf, + adc->rx_buf_sz, adc->rx_buf_sz / 2, + DMA_DEV_TO_MEM, + DMA_PREP_INTERRUPT); + if (!desc) + return -EBUSY; + + desc->callback = stm32_adc_dma_buffer_done; + desc->callback_param = indio_dev; + + cookie = dmaengine_submit(desc); + ret = dma_submit_error(cookie); + if (ret) { + dmaengine_terminate_sync(adc->dma_chan); + return ret; + } + + /* Issue pending DMA requests */ + dma_async_issue_pending(adc->dma_chan); + + return 0; +} + +static int stm32_adc_buffer_postenable(struct iio_dev *indio_dev) +{ + struct stm32_adc *adc = iio_priv(indio_dev); + struct device *dev = indio_dev->dev.parent; + int ret; + + ret = pm_runtime_resume_and_get(dev); + if (ret < 0) + return ret; + + ret = stm32_adc_set_trig(indio_dev, indio_dev->trig); + if (ret) { + dev_err(&indio_dev->dev, "Can't set trigger\n"); + goto err_pm_put; + } + + ret = stm32_adc_dma_start(indio_dev); + if (ret) { + dev_err(&indio_dev->dev, "Can't start dma\n"); + goto err_clr_trig; + } + + /* Reset adc buffer index */ + adc->bufi = 0; + + stm32_adc_ovr_irq_enable(adc); + + if (!adc->dma_chan) + stm32_adc_conv_irq_enable(adc); + + adc->cfg->start_conv(indio_dev, !!adc->dma_chan); + + return 0; + +err_clr_trig: + stm32_adc_set_trig(indio_dev, NULL); +err_pm_put: + pm_runtime_put_autosuspend(dev); + + return ret; +} + +static int stm32_adc_buffer_predisable(struct iio_dev *indio_dev) +{ + struct stm32_adc *adc = iio_priv(indio_dev); + struct device *dev = indio_dev->dev.parent; + + adc->cfg->stop_conv(indio_dev); + if (!adc->dma_chan) + stm32_adc_conv_irq_disable(adc); + + stm32_adc_ovr_irq_disable(adc); + + if (adc->dma_chan) + dmaengine_terminate_sync(adc->dma_chan); + + if (stm32_adc_set_trig(indio_dev, NULL)) + dev_err(&indio_dev->dev, "Can't clear trigger\n"); + + pm_runtime_put_autosuspend(dev); + + return 0; +} + +static const struct iio_buffer_setup_ops stm32_adc_buffer_setup_ops = { + .postenable = &stm32_adc_buffer_postenable, + .predisable = &stm32_adc_buffer_predisable, +}; + +static irqreturn_t stm32_adc_trigger_handler(int irq, void *p) +{ + struct iio_poll_func *pf = p; + struct iio_dev *indio_dev = pf->indio_dev; + struct stm32_adc *adc = iio_priv(indio_dev); + + dev_dbg(&indio_dev->dev, "%s bufi=%d\n", __func__, adc->bufi); + + /* reset buffer index */ + adc->bufi = 0; + iio_push_to_buffers_with_ts(indio_dev, adc->buffer, sizeof(adc->buffer), + pf->timestamp); + iio_trigger_notify_done(indio_dev->trig); + + /* re-enable eoc irq */ + stm32_adc_conv_irq_enable(adc); + + return IRQ_HANDLED; +} + +static const struct iio_chan_spec_ext_info stm32_adc_ext_info[] = { + IIO_ENUM("trigger_polarity", IIO_SHARED_BY_ALL, &stm32_adc_trig_pol), + { + .name = "trigger_polarity_available", + .shared = IIO_SHARED_BY_ALL, + .read = iio_enum_available_read, + .private = (uintptr_t)&stm32_adc_trig_pol, + }, + { } +}; + +static void stm32_adc_debugfs_init(struct iio_dev *indio_dev) +{ + struct stm32_adc *adc = iio_priv(indio_dev); + struct dentry *d = iio_get_debugfs_dentry(indio_dev); + struct stm32_adc_calib *cal = &adc->cal; + char buf[16]; + unsigned int i; + + if (!adc->cfg->has_linearcal) + return; + + for (i = 0; i < STM32H7_LINCALFACT_NUM; i++) { + snprintf(buf, sizeof(buf), "lincalfact%d", i + 1); + debugfs_create_u32(buf, 0444, d, &cal->lincalfact[i]); + } +} + +static int stm32_adc_fw_get_resolution(struct iio_dev *indio_dev) +{ + struct device *dev = &indio_dev->dev; + struct stm32_adc *adc = iio_priv(indio_dev); + unsigned int i; + u32 res; + + if (device_property_read_u32(dev, "assigned-resolution-bits", &res)) + res = adc->cfg->adc_info->resolutions[0]; + + for (i = 0; i < adc->cfg->adc_info->num_res; i++) + if (res == adc->cfg->adc_info->resolutions[i]) + break; + if (i >= adc->cfg->adc_info->num_res) { + dev_err(&indio_dev->dev, "Bad resolution: %u bits\n", res); + return -EINVAL; + } + + dev_dbg(&indio_dev->dev, "Using %u bits resolution\n", res); + adc->res = i; + + return 0; +} + +static void stm32_adc_smpr_init(struct stm32_adc *adc, int channel, u32 smp_ns) +{ + const struct stm32_adc_regs *smpr = &adc->cfg->regs->smp_bits[channel]; + u32 period_ns, shift = smpr->shift, mask = smpr->mask; + unsigned int i, smp, r = smpr->reg; + + /* + * For internal channels, ensure that the sampling time cannot + * be lower than the one specified in the datasheet + */ + for (i = 0; i < STM32_ADC_INT_CH_NB; i++) + if (channel == adc->int_ch[i] && adc->int_ch[i] != STM32_ADC_INT_CH_NONE) + smp_ns = max(smp_ns, adc->cfg->ts_int_ch[i]); + + /* Determine sampling time (ADC clock cycles) */ + period_ns = NSEC_PER_SEC / adc->common->rate; + for (smp = 0; smp <= STM32_ADC_MAX_SMP; smp++) + if ((period_ns * adc->cfg->smp_cycles[smp]) >= smp_ns) + break; + if (smp > STM32_ADC_MAX_SMP) + smp = STM32_ADC_MAX_SMP; + + /* pre-build sampling time registers (e.g. smpr1, smpr2) */ + adc->smpr_val[r] = (adc->smpr_val[r] & ~mask) | (smp << shift); +} + +static void stm32_adc_chan_init_one(struct iio_dev *indio_dev, + struct iio_chan_spec *chan, u32 vinp, + u32 vinn, int scan_index, bool differential) +{ + struct stm32_adc *adc = iio_priv(indio_dev); + char *name = adc->chan_name[vinp]; + + chan->type = IIO_VOLTAGE; + chan->channel = vinp; + if (differential) { + chan->differential = 1; + chan->channel2 = vinn; + snprintf(name, STM32_ADC_CH_SZ, "in%d-in%d", vinp, vinn); + } else { + snprintf(name, STM32_ADC_CH_SZ, "in%d", vinp); + } + chan->datasheet_name = name; + chan->scan_index = scan_index; + chan->indexed = 1; + if (chan->channel == adc->int_ch[STM32_ADC_INT_CH_VREFINT]) + chan->info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED); + else + chan->info_mask_separate = BIT(IIO_CHAN_INFO_RAW); + chan->info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | + BIT(IIO_CHAN_INFO_OFFSET); + if (adc->cfg->has_oversampling) { + chan->info_mask_shared_by_all |= BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO); + chan->info_mask_shared_by_all_available = BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO); + } + chan->scan_type.sign = 'u'; + chan->scan_type.realbits = adc->cfg->adc_info->resolutions[adc->res]; + chan->scan_type.storagebits = 16; + chan->ext_info = stm32_adc_ext_info; + + /* pre-build selected channels mask */ + adc->pcsel |= BIT(chan->channel); + if (differential) { + /* pre-build diff channels mask */ + adc->difsel |= BIT(chan->channel) & adc->cfg->regs->difsel.mask; + /* Also add negative input to pre-selected channels */ + adc->pcsel |= BIT(chan->channel2); + } +} + +static int stm32_adc_get_legacy_chan_count(struct iio_dev *indio_dev, struct stm32_adc *adc) +{ + struct device *dev = &indio_dev->dev; + const struct stm32_adc_info *adc_info = adc->cfg->adc_info; + int num_channels = 0, ret; + + dev_dbg(&indio_dev->dev, "using legacy channel config\n"); + + ret = device_property_count_u32(dev, "st,adc-channels"); + if (ret > adc_info->max_channels) { + dev_err(&indio_dev->dev, "Bad st,adc-channels?\n"); + return -EINVAL; + } else if (ret > 0) { + num_channels += ret; + } + + /* + * each st,adc-diff-channels is a group of 2 u32 so we divide @ret + * to get the *real* number of channels. + */ + ret = device_property_count_u32(dev, "st,adc-diff-channels"); + if (ret > 0) { + ret /= (int)(sizeof(struct stm32_adc_diff_channel) / sizeof(u32)); + if (ret > adc_info->max_channels) { + dev_err(&indio_dev->dev, "Bad st,adc-diff-channels?\n"); + return -EINVAL; + } else if (ret > 0) { + adc->num_diff = ret; + num_channels += ret; + } + } + + /* Optional sample time is provided either for each, or all channels */ + adc->nsmps = device_property_count_u32(dev, "st,min-sample-time-nsecs"); + if (adc->nsmps > 1 && adc->nsmps != num_channels) { + dev_err(&indio_dev->dev, "Invalid st,min-sample-time-nsecs\n"); + return -EINVAL; + } + + return num_channels; +} + +static int stm32_adc_legacy_chan_init(struct iio_dev *indio_dev, + struct stm32_adc *adc, + struct iio_chan_spec *channels, + int nchans) +{ + const struct stm32_adc_info *adc_info = adc->cfg->adc_info; + struct stm32_adc_diff_channel diff[STM32_ADC_CH_MAX]; + struct device *dev = &indio_dev->dev; + u32 num_diff = adc->num_diff; + int num_se = nchans - num_diff; + int size = num_diff * sizeof(*diff) / sizeof(u32); + int scan_index = 0, ret, i, c; + u32 smp = 0, smps[STM32_ADC_CH_MAX], chans[STM32_ADC_CH_MAX]; + + if (num_diff) { + ret = device_property_read_u32_array(dev, "st,adc-diff-channels", + (u32 *)diff, size); + if (ret) { + dev_err(&indio_dev->dev, "Failed to get diff channels %d\n", ret); + return ret; + } + + for (i = 0; i < num_diff; i++) { + if (diff[i].vinp >= adc_info->max_channels || + diff[i].vinn >= adc_info->max_channels) { + dev_err(&indio_dev->dev, "Invalid channel in%d-in%d\n", + diff[i].vinp, diff[i].vinn); + return -EINVAL; + } + + stm32_adc_chan_init_one(indio_dev, &channels[scan_index], + diff[i].vinp, diff[i].vinn, + scan_index, true); + scan_index++; + } + } + if (num_se > 0) { + ret = device_property_read_u32_array(dev, "st,adc-channels", chans, num_se); + if (ret) { + dev_err(&indio_dev->dev, "Failed to get st,adc-channels %d\n", ret); + return ret; + } + + for (c = 0; c < num_se; c++) { + if (chans[c] >= adc_info->max_channels) { + dev_err(&indio_dev->dev, "Invalid channel %d\n", + chans[c]); + return -EINVAL; + } + + /* Channel can't be configured both as single-ended & diff */ + for (i = 0; i < num_diff; i++) { + if (chans[c] == diff[i].vinp) { + dev_err(&indio_dev->dev, "channel %d misconfigured\n", + chans[c]); + return -EINVAL; + } + } + stm32_adc_chan_init_one(indio_dev, &channels[scan_index], + chans[c], 0, scan_index, false); + scan_index++; + } + } + + if (adc->nsmps > 0) { + ret = device_property_read_u32_array(dev, "st,min-sample-time-nsecs", + smps, adc->nsmps); + if (ret) + return ret; + } + + for (i = 0; i < scan_index; i++) { + /* + * This check is used with the above logic so that smp value + * will only be modified if valid u32 value can be decoded. This + * allows to get either no value, 1 shared value for all indexes, + * or one value per channel. The point is to have the same + * behavior as 'of_property_read_u32_index()'. + */ + if (i < adc->nsmps) + smp = smps[i]; + + /* Prepare sampling time settings */ + stm32_adc_smpr_init(adc, channels[i].channel, smp); + } + + return scan_index; +} + +static int stm32_adc_populate_int_ch(struct iio_dev *indio_dev, const char *ch_name, + int chan) +{ + struct stm32_adc *adc = iio_priv(indio_dev); + u16 vrefint; + int i, ret; + + for (i = 0; i < STM32_ADC_INT_CH_NB; i++) { + if (!strncmp(stm32_adc_ic[i].name, ch_name, STM32_ADC_CH_SZ)) { + /* Check internal channel availability */ + switch (i) { + case STM32_ADC_INT_CH_VDDCORE: + if (!adc->cfg->regs->or_vddcore.reg) + dev_warn(&indio_dev->dev, + "%s channel not available\n", ch_name); + break; + case STM32_ADC_INT_CH_VDDCPU: + if (!adc->cfg->regs->or_vddcpu.reg) + dev_warn(&indio_dev->dev, + "%s channel not available\n", ch_name); + break; + case STM32_ADC_INT_CH_VDDQ_DDR: + if (!adc->cfg->regs->or_vddq_ddr.reg) + dev_warn(&indio_dev->dev, + "%s channel not available\n", ch_name); + break; + case STM32_ADC_INT_CH_VREFINT: + if (!adc->cfg->regs->ccr_vref.reg) + dev_warn(&indio_dev->dev, + "%s channel not available\n", ch_name); + break; + case STM32_ADC_INT_CH_VBAT: + if (!adc->cfg->regs->ccr_vbat.reg) + dev_warn(&indio_dev->dev, + "%s channel not available\n", ch_name); + break; + } + + if (stm32_adc_ic[i].idx != STM32_ADC_INT_CH_VREFINT) { + adc->int_ch[i] = chan; + break; + } + + /* Get calibration data for vrefint channel */ + ret = nvmem_cell_read_u16(&indio_dev->dev, "vrefint", &vrefint); + if (ret && ret != -ENOENT) { + return dev_err_probe(indio_dev->dev.parent, ret, + "nvmem access error\n"); + } + if (ret == -ENOENT) { + dev_dbg(&indio_dev->dev, "vrefint calibration not found. Skip vrefint channel\n"); + return ret; + } else if (!vrefint) { + dev_dbg(&indio_dev->dev, "Null vrefint calibration value. Skip vrefint channel\n"); + return -ENOENT; + } + adc->int_ch[i] = chan; + adc->vrefint.vrefint_cal = vrefint; + } + } + + return 0; +} + +static int stm32_adc_generic_chan_init(struct iio_dev *indio_dev, + struct stm32_adc *adc, + struct iio_chan_spec *channels) +{ + const struct stm32_adc_info *adc_info = adc->cfg->adc_info; + struct device *dev = &indio_dev->dev; + const char *name; + int val, scan_index = 0, ret; + bool differential; + u32 vin[2]; + + device_for_each_child_node_scoped(dev, child) { + ret = fwnode_property_read_u32(child, "reg", &val); + if (ret) + return dev_err_probe(dev, ret, + "Missing channel index\n"); + + ret = fwnode_property_read_string(child, "label", &name); + /* label is optional */ + if (!ret) { + if (strlen(name) >= STM32_ADC_CH_SZ) + return dev_err_probe(dev, -EINVAL, + "Label %s exceeds %d characters\n", + name, STM32_ADC_CH_SZ); + + strscpy(adc->chan_name[val], name, STM32_ADC_CH_SZ); + ret = stm32_adc_populate_int_ch(indio_dev, name, val); + if (ret == -ENOENT) + continue; + else if (ret) + return ret; + } else if (ret != -EINVAL) { + return dev_err_probe(dev, ret, "Invalid label\n"); + } + + if (val >= adc_info->max_channels) + return dev_err_probe(dev, -EINVAL, + "Invalid channel %d\n", val); + + differential = false; + ret = fwnode_property_read_u32_array(child, "diff-channels", vin, 2); + /* diff-channels is optional */ + if (!ret) { + differential = true; + if (vin[0] != val || vin[1] >= adc_info->max_channels) + return dev_err_probe(dev, -EINVAL, + "Invalid channel in%d-in%d\n", + vin[0], vin[1]); + } else if (ret != -EINVAL) { + return dev_err_probe(dev, ret, + "Invalid diff-channels property\n"); + } + + stm32_adc_chan_init_one(indio_dev, &channels[scan_index], val, + vin[1], scan_index, differential); + + val = 0; + ret = fwnode_property_read_u32(child, "st,min-sample-time-ns", &val); + /* st,min-sample-time-ns is optional */ + if (ret && ret != -EINVAL) + return dev_err_probe(dev, ret, + "Invalid st,min-sample-time-ns property\n"); + + stm32_adc_smpr_init(adc, channels[scan_index].channel, val); + if (differential) + stm32_adc_smpr_init(adc, vin[1], val); + + scan_index++; + } + + return scan_index; +} + +static int stm32_adc_chan_fw_init(struct iio_dev *indio_dev, bool timestamping) +{ + struct stm32_adc *adc = iio_priv(indio_dev); + const struct stm32_adc_info *adc_info = adc->cfg->adc_info; + struct iio_chan_spec *channels; + int scan_index = 0, num_channels = 0, ret, i; + bool legacy = false; + + for (i = 0; i < STM32_ADC_INT_CH_NB; i++) + adc->int_ch[i] = STM32_ADC_INT_CH_NONE; + + num_channels = device_get_child_node_count(&indio_dev->dev); + /* If no channels have been found, fallback to channels legacy properties. */ + if (!num_channels) { + legacy = true; + + ret = stm32_adc_get_legacy_chan_count(indio_dev, adc); + if (!ret) { + dev_err(indio_dev->dev.parent, "No channel found\n"); + return -ENODATA; + } else if (ret < 0) { + return ret; + } + + num_channels = ret; + } + + if (num_channels > adc_info->max_channels) { + dev_err(&indio_dev->dev, "Channel number [%d] exceeds %d\n", + num_channels, adc_info->max_channels); + return -EINVAL; + } + + if (timestamping) + num_channels++; + + channels = devm_kcalloc(&indio_dev->dev, num_channels, + sizeof(struct iio_chan_spec), GFP_KERNEL); + if (!channels) + return -ENOMEM; + + if (legacy) + ret = stm32_adc_legacy_chan_init(indio_dev, adc, channels, + timestamping ? num_channels - 1 : num_channels); + else + ret = stm32_adc_generic_chan_init(indio_dev, adc, channels); + if (ret < 0) + return ret; + scan_index = ret; + + if (timestamping) { + channels[scan_index] = IIO_CHAN_SOFT_TIMESTAMP(scan_index); + scan_index++; + } + + indio_dev->num_channels = scan_index; + indio_dev->channels = channels; + + return 0; +} + +static int stm32_adc_dma_request(struct device *dev, struct iio_dev *indio_dev) +{ + struct stm32_adc *adc = iio_priv(indio_dev); + struct dma_slave_config config = { }; + int ret; + + adc->dma_chan = dma_request_chan(dev, "rx"); + if (IS_ERR(adc->dma_chan)) { + ret = PTR_ERR(adc->dma_chan); + if (ret != -ENODEV) + return dev_err_probe(dev, ret, + "DMA channel request failed with\n"); + + /* DMA is optional: fall back to IRQ mode */ + adc->dma_chan = NULL; + return 0; + } + + adc->rx_buf = dma_alloc_coherent(adc->dma_chan->device->dev, + STM32_DMA_BUFFER_SIZE, + &adc->rx_dma_buf, GFP_KERNEL); + if (!adc->rx_buf) { + ret = -ENOMEM; + goto err_release; + } + + /* Configure DMA channel to read data register */ + config.src_addr = (dma_addr_t)adc->common->phys_base; + config.src_addr += adc->offset + adc->cfg->regs->dr; + config.src_addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES; + + ret = dmaengine_slave_config(adc->dma_chan, &config); + if (ret) + goto err_free; + + return 0; + +err_free: + dma_free_coherent(adc->dma_chan->device->dev, STM32_DMA_BUFFER_SIZE, + adc->rx_buf, adc->rx_dma_buf); +err_release: + dma_release_channel(adc->dma_chan); + + return ret; +} + +static int stm32_adc_probe(struct platform_device *pdev) +{ + struct iio_dev *indio_dev; + struct device *dev = &pdev->dev; + irqreturn_t (*handler)(int irq, void *p) = NULL; + struct stm32_adc *adc; + bool timestamping = false; + int ret; + + indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*adc)); + if (!indio_dev) + return -ENOMEM; + + adc = iio_priv(indio_dev); + adc->common = dev_get_drvdata(pdev->dev.parent); + spin_lock_init(&adc->lock); + init_completion(&adc->completion); + adc->cfg = device_get_match_data(dev); + + indio_dev->name = dev_name(&pdev->dev); + device_set_node(&indio_dev->dev, dev_fwnode(&pdev->dev)); + indio_dev->info = &stm32_adc_iio_info; + indio_dev->modes = INDIO_DIRECT_MODE | INDIO_HARDWARE_TRIGGERED; + + platform_set_drvdata(pdev, indio_dev); + + ret = device_property_read_u32(dev, "reg", &adc->offset); + if (ret != 0) { + dev_err(&pdev->dev, "missing reg property\n"); + return -EINVAL; + } + + adc->irq = platform_get_irq(pdev, 0); + if (adc->irq < 0) + return adc->irq; + + ret = devm_request_threaded_irq(&pdev->dev, adc->irq, stm32_adc_isr, + stm32_adc_threaded_isr, + 0, pdev->name, indio_dev); + if (ret) { + dev_err(&pdev->dev, "failed to request IRQ\n"); + return ret; + } + + adc->clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(adc->clk)) { + ret = PTR_ERR(adc->clk); + if (ret == -ENOENT && !adc->cfg->clk_required) { + adc->clk = NULL; + } else { + dev_err(&pdev->dev, "Can't get clock\n"); + return ret; + } + } + + ret = stm32_adc_fw_get_resolution(indio_dev); + if (ret < 0) + return ret; + + ret = stm32_adc_dma_request(dev, indio_dev); + if (ret < 0) + return ret; + + if (!adc->dma_chan) { + /* For PIO mode only, iio_pollfunc_store_time stores a timestamp + * in the primary trigger IRQ handler and stm32_adc_trigger_handler + * runs in the IRQ thread to push out buffer along with timestamp. + */ + handler = &stm32_adc_trigger_handler; + timestamping = true; + } + + ret = stm32_adc_chan_fw_init(indio_dev, timestamping); + if (ret < 0) + goto err_dma_disable; + + ret = iio_triggered_buffer_setup(indio_dev, + &iio_pollfunc_store_time, handler, + &stm32_adc_buffer_setup_ops); + if (ret) { + dev_err(&pdev->dev, "buffer setup failed\n"); + goto err_dma_disable; + } + + /* Get stm32-adc-core PM online */ + pm_runtime_get_noresume(dev); + pm_runtime_set_active(dev); + pm_runtime_set_autosuspend_delay(dev, STM32_ADC_HW_STOP_DELAY_MS); + pm_runtime_use_autosuspend(dev); + pm_runtime_enable(dev); + + ret = stm32_adc_hw_start(dev); + if (ret) + goto err_buffer_cleanup; + + ret = iio_device_register(indio_dev); + if (ret) { + dev_err(&pdev->dev, "iio dev register failed\n"); + goto err_hw_stop; + } + + pm_runtime_put_autosuspend(dev); + + if (IS_ENABLED(CONFIG_DEBUG_FS)) + stm32_adc_debugfs_init(indio_dev); + + return 0; + +err_hw_stop: + stm32_adc_hw_stop(dev); + +err_buffer_cleanup: + pm_runtime_disable(dev); + pm_runtime_set_suspended(dev); + pm_runtime_put_noidle(dev); + iio_triggered_buffer_cleanup(indio_dev); + +err_dma_disable: + if (adc->dma_chan) { + dma_free_coherent(adc->dma_chan->device->dev, + STM32_DMA_BUFFER_SIZE, + adc->rx_buf, adc->rx_dma_buf); + dma_release_channel(adc->dma_chan); + } + + return ret; +} + +static void stm32_adc_remove(struct platform_device *pdev) +{ + struct iio_dev *indio_dev = platform_get_drvdata(pdev); + struct stm32_adc *adc = iio_priv(indio_dev); + + pm_runtime_get_sync(&pdev->dev); + /* iio_device_unregister() also removes debugfs entries */ + iio_device_unregister(indio_dev); + stm32_adc_hw_stop(&pdev->dev); + pm_runtime_disable(&pdev->dev); + pm_runtime_set_suspended(&pdev->dev); + pm_runtime_put_noidle(&pdev->dev); + iio_triggered_buffer_cleanup(indio_dev); + if (adc->dma_chan) { + dma_free_coherent(adc->dma_chan->device->dev, + STM32_DMA_BUFFER_SIZE, + adc->rx_buf, adc->rx_dma_buf); + dma_release_channel(adc->dma_chan); + } +} + +static int stm32_adc_suspend(struct device *dev) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + + if (iio_buffer_enabled(indio_dev)) + stm32_adc_buffer_predisable(indio_dev); + + return pm_runtime_force_suspend(dev); +} + +static int stm32_adc_resume(struct device *dev) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + int ret; + + ret = pm_runtime_force_resume(dev); + if (ret < 0) + return ret; + + if (!iio_buffer_enabled(indio_dev)) + return 0; + + ret = stm32_adc_update_scan_mode(indio_dev, + indio_dev->active_scan_mask); + if (ret < 0) + return ret; + + return stm32_adc_buffer_postenable(indio_dev); +} + +static int stm32_adc_runtime_suspend(struct device *dev) +{ + return stm32_adc_hw_stop(dev); +} + +static int stm32_adc_runtime_resume(struct device *dev) +{ + return stm32_adc_hw_start(dev); +} + +static const struct dev_pm_ops stm32_adc_pm_ops = { + SYSTEM_SLEEP_PM_OPS(stm32_adc_suspend, stm32_adc_resume) + RUNTIME_PM_OPS(stm32_adc_runtime_suspend, stm32_adc_runtime_resume, + NULL) +}; + +static const struct stm32_adc_cfg stm32f4_adc_cfg = { + .regs = &stm32f4_adc_regspec, + .adc_info = &stm32f4_adc_info, + .trigs = stm32f4_adc_trigs, + .clk_required = true, + .start_conv = stm32f4_adc_start_conv, + .stop_conv = stm32f4_adc_stop_conv, + .smp_cycles = stm32f4_adc_smp_cycles, + .irq_clear = stm32f4_adc_irq_clear, +}; + +static const unsigned int stm32_adc_min_ts_h7[] = { 0, 0, 0, 4300, 9000 }; +static_assert(ARRAY_SIZE(stm32_adc_min_ts_h7) == STM32_ADC_INT_CH_NB); + +static const struct stm32_adc_cfg stm32h7_adc_cfg = { + .regs = &stm32h7_adc_regspec, + .adc_info = &stm32h7_adc_info, + .trigs = stm32h7_adc_trigs, + .has_boostmode = true, + .has_linearcal = true, + .has_presel = true, + .has_oversampling = true, + .start_conv = stm32h7_adc_start_conv, + .stop_conv = stm32h7_adc_stop_conv, + .prepare = stm32h7_adc_prepare, + .unprepare = stm32h7_adc_unprepare, + .smp_cycles = stm32h7_adc_smp_cycles, + .irq_clear = stm32h7_adc_irq_clear, + .ts_int_ch = stm32_adc_min_ts_h7, + .set_ovs = stm32h7_adc_set_ovs, +}; + +static const unsigned int stm32_adc_min_ts_mp1[] = { 100, 100, 100, 4300, 9800 }; +static_assert(ARRAY_SIZE(stm32_adc_min_ts_mp1) == STM32_ADC_INT_CH_NB); + +static const struct stm32_adc_cfg stm32mp1_adc_cfg = { + .regs = &stm32mp1_adc_regspec, + .adc_info = &stm32h7_adc_info, + .trigs = stm32h7_adc_trigs, + .has_vregready = true, + .has_boostmode = true, + .has_linearcal = true, + .has_presel = true, + .has_oversampling = true, + .start_conv = stm32h7_adc_start_conv, + .stop_conv = stm32h7_adc_stop_conv, + .prepare = stm32h7_adc_prepare, + .unprepare = stm32h7_adc_unprepare, + .smp_cycles = stm32h7_adc_smp_cycles, + .irq_clear = stm32h7_adc_irq_clear, + .ts_int_ch = stm32_adc_min_ts_mp1, + .set_ovs = stm32h7_adc_set_ovs, +}; + +static const unsigned int stm32_adc_min_ts_mp13[] = { 100, 0, 0, 4300, 9800 }; +static_assert(ARRAY_SIZE(stm32_adc_min_ts_mp13) == STM32_ADC_INT_CH_NB); + +static const struct stm32_adc_cfg stm32mp13_adc_cfg = { + .regs = &stm32mp13_adc_regspec, + .adc_info = &stm32mp13_adc_info, + .trigs = stm32h7_adc_trigs, + .has_oversampling = true, + .start_conv = stm32mp13_adc_start_conv, + .stop_conv = stm32h7_adc_stop_conv, + .prepare = stm32h7_adc_prepare, + .unprepare = stm32h7_adc_unprepare, + .smp_cycles = stm32mp13_adc_smp_cycles, + .irq_clear = stm32h7_adc_irq_clear, + .ts_int_ch = stm32_adc_min_ts_mp13, + .set_ovs = stm32mp13_adc_set_ovs, +}; + +static const struct of_device_id stm32_adc_of_match[] = { + { .compatible = "st,stm32f4-adc", .data = (void *)&stm32f4_adc_cfg }, + { .compatible = "st,stm32h7-adc", .data = (void *)&stm32h7_adc_cfg }, + { .compatible = "st,stm32mp1-adc", .data = (void *)&stm32mp1_adc_cfg }, + { .compatible = "st,stm32mp13-adc", .data = (void *)&stm32mp13_adc_cfg }, + { } +}; +MODULE_DEVICE_TABLE(of, stm32_adc_of_match); + +static struct platform_driver stm32_adc_driver = { + .probe = stm32_adc_probe, + .remove = stm32_adc_remove, + .driver = { + .name = "stm32-adc", + .of_match_table = stm32_adc_of_match, + .pm = pm_ptr(&stm32_adc_pm_ops), + }, +}; +module_platform_driver(stm32_adc_driver); + +MODULE_AUTHOR("Fabrice Gasnier "); +MODULE_DESCRIPTION("STMicroelectronics STM32 ADC IIO driver"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:stm32-adc"); diff --git a/drivers/iio/adc/stm32-dfsdm-adc.c b/drivers/iio/adc/stm32-dfsdm-adc.c new file mode 100644 index 000000000..00f05e167 --- /dev/null +++ b/drivers/iio/adc/stm32-dfsdm-adc.c @@ -0,0 +1,1907 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * This file is the ADC part of the STM32 DFSDM driver + * + * Copyright (C) 2017, STMicroelectronics - All Rights Reserved + * Author: Arnaud Pouliquen . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "stm32-dfsdm.h" + +#define DFSDM_DMA_BUFFER_SIZE (4 * PAGE_SIZE) + +/* Conversion timeout */ +#define DFSDM_TIMEOUT_US 100000 +#define DFSDM_TIMEOUT (msecs_to_jiffies(DFSDM_TIMEOUT_US / 1000)) + +/* Oversampling attribute default */ +#define DFSDM_DEFAULT_OVERSAMPLING 100 + +/* Oversampling max values */ +#define DFSDM_MAX_INT_OVERSAMPLING 256 +#define DFSDM_MAX_FL_OVERSAMPLING 1024 + +/* Limit filter output resolution to 31 bits. (i.e. sample range is +/-2^30) */ +#define DFSDM_DATA_MAX BIT(30) +/* + * Data are output as two's complement data in a 24 bit field. + * Data from filters are in the range +/-2^(n-1) + * 2^(n-1) maximum positive value cannot be coded in 2's complement n bits + * An extra bit is required to avoid wrap-around of the binary code for 2^(n-1) + * So, the resolution of samples from filter is actually limited to 23 bits + */ +#define DFSDM_DATA_RES 24 + +/* Filter configuration */ +#define DFSDM_CR1_CFG_MASK (DFSDM_CR1_RCH_MASK | DFSDM_CR1_RCONT_MASK | \ + DFSDM_CR1_RSYNC_MASK | DFSDM_CR1_JSYNC_MASK | \ + DFSDM_CR1_JSCAN_MASK) + +enum sd_converter_type { + DFSDM_AUDIO, + DFSDM_IIO, +}; + +struct stm32_dfsdm_dev_data { + int type; + int (*init)(struct device *dev, struct iio_dev *indio_dev); + unsigned int num_channels; + const struct regmap_config *regmap_cfg; +}; + +struct stm32_dfsdm_adc { + struct stm32_dfsdm *dfsdm; + const struct stm32_dfsdm_dev_data *dev_data; + unsigned int fl_id; + unsigned int nconv; + unsigned long smask; + + /* ADC specific */ + unsigned int oversamp; + struct iio_hw_consumer *hwc; + struct iio_backend **backend; + struct completion completion; + u32 *buffer; + + /* Audio specific */ + unsigned int spi_freq; /* SPI bus clock frequency */ + unsigned int sample_freq; /* Sample frequency after filter decimation */ + int (*cb)(const void *data, size_t size, void *cb_priv); + void *cb_priv; + + /* DMA */ + u8 *rx_buf; + unsigned int bufi; /* Buffer current position */ + unsigned int buf_sz; /* Buffer size */ + struct dma_chan *dma_chan; + dma_addr_t dma_buf; +}; + +struct stm32_dfsdm_str2field { + const char *name; + unsigned int val; +}; + +/* DFSDM channel serial interface type */ +static const struct stm32_dfsdm_str2field stm32_dfsdm_chan_type[] = { + { "SPI_R", 0 }, /* SPI with data on rising edge */ + { "SPI_F", 1 }, /* SPI with data on falling edge */ + { "MANCH_R", 2 }, /* Manchester codec, rising edge = logic 0 */ + { "MANCH_F", 3 }, /* Manchester codec, falling edge = logic 1 */ + { } +}; + +/* DFSDM channel clock source */ +static const struct stm32_dfsdm_str2field stm32_dfsdm_chan_src[] = { + /* External SPI clock (CLKIN x) */ + { "CLKIN", DFSDM_CHANNEL_SPI_CLOCK_EXTERNAL }, + /* Internal SPI clock (CLKOUT) */ + { "CLKOUT", DFSDM_CHANNEL_SPI_CLOCK_INTERNAL }, + /* Internal SPI clock divided by 2 (falling edge) */ + { "CLKOUT_F", DFSDM_CHANNEL_SPI_CLOCK_INTERNAL_DIV2_FALLING }, + /* Internal SPI clock divided by 2 (falling edge) */ + { "CLKOUT_R", DFSDM_CHANNEL_SPI_CLOCK_INTERNAL_DIV2_RISING }, + { } +}; + +static int stm32_dfsdm_str2val(const char *str, + const struct stm32_dfsdm_str2field *list) +{ + const struct stm32_dfsdm_str2field *p = list; + + for (p = list; p && p->name; p++) + if (!strcmp(p->name, str)) + return p->val; + + return -EINVAL; +} + +/** + * struct stm32_dfsdm_trig_info - DFSDM trigger info + * @name: name of the trigger, corresponding to its source + * @jextsel: trigger signal selection + */ +struct stm32_dfsdm_trig_info { + const char *name; + unsigned int jextsel; +}; + +/* hardware injected trigger enable, edge selection */ +enum stm32_dfsdm_jexten { + STM32_DFSDM_JEXTEN_DISABLED, + STM32_DFSDM_JEXTEN_RISING_EDGE, + STM32_DFSDM_JEXTEN_FALLING_EDGE, + STM32_DFSDM_EXTEN_BOTH_EDGES, +}; + +static const struct stm32_dfsdm_trig_info stm32_dfsdm_trigs[] = { + { TIM1_TRGO, 0 }, + { TIM1_TRGO2, 1 }, + { TIM8_TRGO, 2 }, + { TIM8_TRGO2, 3 }, + { TIM3_TRGO, 4 }, + { TIM4_TRGO, 5 }, + { TIM16_OC1, 6 }, + { TIM6_TRGO, 7 }, + { TIM7_TRGO, 8 }, + { LPTIM1_OUT, 26 }, + { LPTIM2_OUT, 27 }, + { LPTIM3_OUT, 28 }, + { } +}; + +static int stm32_dfsdm_get_jextsel(struct iio_dev *indio_dev, + struct iio_trigger *trig) +{ + int i; + + /* lookup triggers registered by stm32 timer trigger driver */ + for (i = 0; stm32_dfsdm_trigs[i].name; i++) { + /** + * Checking both stm32 timer trigger type and trig name + * should be safe against arbitrary trigger names. + */ + if ((is_stm32_timer_trigger(trig) || + is_stm32_lptim_trigger(trig)) && + !strcmp(stm32_dfsdm_trigs[i].name, trig->name)) { + return stm32_dfsdm_trigs[i].jextsel; + } + } + + return -EINVAL; +} + +static int stm32_dfsdm_compute_osrs(struct stm32_dfsdm_filter *fl, + unsigned int fast, unsigned int oversamp) +{ + unsigned int i, d, fosr, iosr; + u64 res, max; + int bits, shift; + unsigned int m = 1; /* multiplication factor */ + unsigned int p = fl->ford; /* filter order (ford) */ + struct stm32_dfsdm_filter_osr *flo = &fl->flo[fast]; + + pr_debug("Requested oversampling: %d\n", oversamp); + /* + * This function tries to compute filter oversampling and integrator + * oversampling, base on oversampling ratio requested by user. + * + * Decimation d depends on the filter order and the oversampling ratios. + * ford: filter order + * fosr: filter over sampling ratio + * iosr: integrator over sampling ratio + */ + if (fl->ford == DFSDM_FASTSINC_ORDER) { + m = 2; + p = 2; + } + + /* + * Look for filter and integrator oversampling ratios which allows + * to maximize data output resolution. + */ + for (fosr = 1; fosr <= DFSDM_MAX_FL_OVERSAMPLING; fosr++) { + for (iosr = 1; iosr <= DFSDM_MAX_INT_OVERSAMPLING; iosr++) { + if (fast) + d = fosr * iosr; + else if (fl->ford == DFSDM_FASTSINC_ORDER) + d = fosr * (iosr + 3) + 2; + else + d = fosr * (iosr - 1 + p) + p; + + if (d > oversamp) + break; + else if (d != oversamp) + continue; + /* + * Check resolution (limited to signed 32 bits) + * res <= 2^31 + * Sincx filters: + * res = m * fosr^p x iosr (with m=1, p=ford) + * FastSinc filter + * res = m * fosr^p x iosr (with m=2, p=2) + */ + res = fosr; + for (i = p - 1; i > 0; i--) { + res = res * (u64)fosr; + if (res > DFSDM_DATA_MAX) + break; + } + if (res > DFSDM_DATA_MAX) + continue; + + res = res * (u64)m * (u64)iosr; + if (res > DFSDM_DATA_MAX) + continue; + + if (res >= flo->res) { + flo->res = res; + flo->fosr = fosr; + flo->iosr = iosr; + + bits = fls(flo->res); + /* 8 LBSs in data register contain chan info */ + max = flo->res << 8; + + /* if resolution is not a power of two */ + if (flo->res > BIT(bits - 1)) + bits++; + else + max--; + + shift = DFSDM_DATA_RES - bits; + /* + * Compute right/left shift + * Right shift is performed by hardware + * when transferring samples to data register. + * Left shift is done by software on buffer + */ + if (shift > 0) { + /* Resolution is lower than 24 bits */ + flo->rshift = 0; + flo->lshift = shift; + } else { + /* + * If resolution is 24 bits or more, + * max positive value may be ambiguous + * (equal to max negative value as sign + * bit is dropped). + * Reduce resolution to 23 bits (rshift) + * to keep the sign on bit 23 and treat + * saturation before rescaling on 24 + * bits (lshift). + */ + flo->rshift = 1 - shift; + flo->lshift = 1; + max >>= flo->rshift; + } + flo->max = (s32)max; + flo->bits = bits; + + pr_debug("fast %d, fosr %d, iosr %d, res 0x%llx/%d bits, rshift %d, lshift %d\n", + fast, flo->fosr, flo->iosr, + flo->res, bits, flo->rshift, + flo->lshift); + } + } + } + + if (!flo->res) + return -EINVAL; + + return 0; +} + +static int stm32_dfsdm_compute_all_osrs(struct iio_dev *indio_dev, + unsigned int oversamp) +{ + struct stm32_dfsdm_adc *adc = iio_priv(indio_dev); + struct stm32_dfsdm_filter *fl = &adc->dfsdm->fl_list[adc->fl_id]; + int ret0, ret1; + + memset(&fl->flo[0], 0, sizeof(fl->flo[0])); + memset(&fl->flo[1], 0, sizeof(fl->flo[1])); + + ret0 = stm32_dfsdm_compute_osrs(fl, 0, oversamp); + ret1 = stm32_dfsdm_compute_osrs(fl, 1, oversamp); + if (ret0 < 0 && ret1 < 0) { + dev_err(&indio_dev->dev, + "Filter parameters not found: errors %d/%d\n", + ret0, ret1); + return -EINVAL; + } + + return 0; +} + +static int stm32_dfsdm_start_channel(struct iio_dev *indio_dev) +{ + struct stm32_dfsdm_adc *adc = iio_priv(indio_dev); + struct regmap *regmap = adc->dfsdm->regmap; + const struct iio_chan_spec *chan; + unsigned int bit; + int ret; + + for_each_set_bit(bit, &adc->smask, sizeof(adc->smask) * BITS_PER_BYTE) { + chan = indio_dev->channels + bit; + ret = regmap_update_bits(regmap, DFSDM_CHCFGR1(chan->channel), + DFSDM_CHCFGR1_CHEN_MASK, + DFSDM_CHCFGR1_CHEN(1)); + if (ret < 0) + return ret; + } + + return 0; +} + +static void stm32_dfsdm_stop_channel(struct iio_dev *indio_dev) +{ + struct stm32_dfsdm_adc *adc = iio_priv(indio_dev); + struct regmap *regmap = adc->dfsdm->regmap; + const struct iio_chan_spec *chan; + unsigned int bit; + + for_each_set_bit(bit, &adc->smask, sizeof(adc->smask) * BITS_PER_BYTE) { + chan = indio_dev->channels + bit; + regmap_update_bits(regmap, DFSDM_CHCFGR1(chan->channel), + DFSDM_CHCFGR1_CHEN_MASK, + DFSDM_CHCFGR1_CHEN(0)); + } +} + +static int stm32_dfsdm_chan_configure(struct stm32_dfsdm *dfsdm, + struct stm32_dfsdm_channel *ch) +{ + unsigned int id = ch->id; + struct regmap *regmap = dfsdm->regmap; + int ret; + + ret = regmap_update_bits(regmap, DFSDM_CHCFGR1(id), + DFSDM_CHCFGR1_SITP_MASK, + DFSDM_CHCFGR1_SITP(ch->type)); + if (ret < 0) + return ret; + ret = regmap_update_bits(regmap, DFSDM_CHCFGR1(id), + DFSDM_CHCFGR1_SPICKSEL_MASK, + DFSDM_CHCFGR1_SPICKSEL(ch->src)); + if (ret < 0) + return ret; + return regmap_update_bits(regmap, DFSDM_CHCFGR1(id), + DFSDM_CHCFGR1_CHINSEL_MASK, + DFSDM_CHCFGR1_CHINSEL(ch->alt_si)); +} + +static int stm32_dfsdm_start_filter(struct stm32_dfsdm_adc *adc, + unsigned int fl_id, + struct iio_trigger *trig) +{ + struct stm32_dfsdm *dfsdm = adc->dfsdm; + int ret; + + /* Enable filter */ + ret = regmap_update_bits(dfsdm->regmap, DFSDM_CR1(fl_id), + DFSDM_CR1_DFEN_MASK, DFSDM_CR1_DFEN(1)); + if (ret < 0) + return ret; + + /* Nothing more to do for injected (scan mode/triggered) conversions */ + if (adc->nconv > 1 || trig) + return 0; + + /* Software start (single or continuous) regular conversion */ + return regmap_update_bits(dfsdm->regmap, DFSDM_CR1(fl_id), + DFSDM_CR1_RSWSTART_MASK, + DFSDM_CR1_RSWSTART(1)); +} + +static void stm32_dfsdm_stop_filter(struct stm32_dfsdm *dfsdm, + unsigned int fl_id) +{ + /* Disable conversion */ + regmap_update_bits(dfsdm->regmap, DFSDM_CR1(fl_id), + DFSDM_CR1_DFEN_MASK, DFSDM_CR1_DFEN(0)); +} + +static int stm32_dfsdm_filter_set_trig(struct iio_dev *indio_dev, + unsigned int fl_id, + struct iio_trigger *trig) +{ + struct stm32_dfsdm_adc *adc = iio_priv(indio_dev); + struct regmap *regmap = adc->dfsdm->regmap; + u32 jextsel = 0, jexten = STM32_DFSDM_JEXTEN_DISABLED; + int ret; + + if (trig) { + ret = stm32_dfsdm_get_jextsel(indio_dev, trig); + if (ret < 0) + return ret; + + /* set trigger source and polarity (default to rising edge) */ + jextsel = ret; + jexten = STM32_DFSDM_JEXTEN_RISING_EDGE; + } + + ret = regmap_update_bits(regmap, DFSDM_CR1(fl_id), + DFSDM_CR1_JEXTSEL_MASK | DFSDM_CR1_JEXTEN_MASK, + DFSDM_CR1_JEXTSEL(jextsel) | + DFSDM_CR1_JEXTEN(jexten)); + if (ret < 0) + return ret; + + return 0; +} + +static int stm32_dfsdm_channels_configure(struct iio_dev *indio_dev, + unsigned int fl_id, + struct iio_trigger *trig) +{ + struct stm32_dfsdm_adc *adc = iio_priv(indio_dev); + struct regmap *regmap = adc->dfsdm->regmap; + struct stm32_dfsdm_filter *fl = &adc->dfsdm->fl_list[fl_id]; + struct stm32_dfsdm_filter_osr *flo = &fl->flo[0]; + const struct iio_chan_spec *chan; + unsigned int bit; + int ret; + + fl->fast = 0; + + /* + * In continuous mode, use fast mode configuration, + * if it provides a better resolution. + */ + if (adc->nconv == 1 && !trig && iio_buffer_enabled(indio_dev)) { + if (fl->flo[1].res >= fl->flo[0].res) { + fl->fast = 1; + flo = &fl->flo[1]; + } + } + + if (!flo->res) + return -EINVAL; + + dev_dbg(&indio_dev->dev, "Samples actual resolution: %d bits", + min(flo->bits, (u32)DFSDM_DATA_RES - 1)); + + for_each_set_bit(bit, &adc->smask, + sizeof(adc->smask) * BITS_PER_BYTE) { + chan = indio_dev->channels + bit; + + ret = regmap_update_bits(regmap, + DFSDM_CHCFGR2(chan->channel), + DFSDM_CHCFGR2_DTRBS_MASK, + DFSDM_CHCFGR2_DTRBS(flo->rshift)); + if (ret) + return ret; + } + + return 0; +} + +static int stm32_dfsdm_filter_configure(struct iio_dev *indio_dev, + unsigned int fl_id, + struct iio_trigger *trig) +{ + struct stm32_dfsdm_adc *adc = iio_priv(indio_dev); + struct regmap *regmap = adc->dfsdm->regmap; + struct stm32_dfsdm_filter *fl = &adc->dfsdm->fl_list[fl_id]; + struct stm32_dfsdm_filter_osr *flo = &fl->flo[fl->fast]; + u32 cr1; + const struct iio_chan_spec *chan; + unsigned int bit, jchg = 0; + int ret; + + /* Average integrator oversampling */ + ret = regmap_update_bits(regmap, DFSDM_FCR(fl_id), DFSDM_FCR_IOSR_MASK, + DFSDM_FCR_IOSR(flo->iosr - 1)); + if (ret) + return ret; + + /* Filter order and Oversampling */ + ret = regmap_update_bits(regmap, DFSDM_FCR(fl_id), DFSDM_FCR_FOSR_MASK, + DFSDM_FCR_FOSR(flo->fosr - 1)); + if (ret) + return ret; + + ret = regmap_update_bits(regmap, DFSDM_FCR(fl_id), DFSDM_FCR_FORD_MASK, + DFSDM_FCR_FORD(fl->ford)); + if (ret) + return ret; + + ret = stm32_dfsdm_filter_set_trig(indio_dev, fl_id, trig); + if (ret) + return ret; + + ret = regmap_update_bits(regmap, DFSDM_CR1(fl_id), + DFSDM_CR1_FAST_MASK, + DFSDM_CR1_FAST(fl->fast)); + if (ret) + return ret; + + /* + * DFSDM modes configuration W.R.T audio/iio type modes + * ---------------------------------------------------------------- + * Modes | regular | regular | injected | injected | + * | | continuous | | + scan | + * --------------|---------|--------------|----------|------------| + * single conv | x | | | | + * (1 chan) | | | | | + * --------------|---------|--------------|----------|------------| + * 1 Audio chan | | sample freq | | | + * | | or sync_mode | | | + * --------------|---------|--------------|----------|------------| + * 1 IIO chan | | sample freq | trigger | | + * | | or sync_mode | | | + * --------------|---------|--------------|----------|------------| + * 2+ IIO chans | | | | trigger or | + * | | | | sync_mode | + * ---------------------------------------------------------------- + */ + if (adc->nconv == 1 && !trig) { + bit = __ffs(adc->smask); + chan = indio_dev->channels + bit; + + /* Use regular conversion for single channel without trigger */ + cr1 = DFSDM_CR1_RCH(chan->channel); + + /* Continuous conversions triggered by SPI clk in buffer mode */ + if (iio_buffer_enabled(indio_dev)) + cr1 |= DFSDM_CR1_RCONT(1); + + cr1 |= DFSDM_CR1_RSYNC(fl->sync_mode); + } else { + /* Use injected conversion for multiple channels */ + for_each_set_bit(bit, &adc->smask, + sizeof(adc->smask) * BITS_PER_BYTE) { + chan = indio_dev->channels + bit; + jchg |= BIT(chan->channel); + } + ret = regmap_write(regmap, DFSDM_JCHGR(fl_id), jchg); + if (ret < 0) + return ret; + + /* Use scan mode for multiple channels */ + cr1 = DFSDM_CR1_JSCAN((adc->nconv > 1) ? 1 : 0); + + /* + * Continuous conversions not supported in injected mode, + * either use: + * - conversions in sync with filter 0 + * - triggered conversions + */ + if (!fl->sync_mode && !trig) + return -EINVAL; + cr1 |= DFSDM_CR1_JSYNC(fl->sync_mode); + } + + return regmap_update_bits(regmap, DFSDM_CR1(fl_id), DFSDM_CR1_CFG_MASK, + cr1); +} + +static int stm32_dfsdm_channel_parse_of(struct stm32_dfsdm *dfsdm, + struct iio_dev *indio_dev, + struct iio_chan_spec *ch) +{ + struct stm32_dfsdm_channel *df_ch; + const char *of_str; + int chan_idx = ch->scan_index; + int ret, val; + + ret = of_property_read_u32_index(indio_dev->dev.of_node, + "st,adc-channels", chan_idx, + &ch->channel); + if (ret < 0) { + dev_err(&indio_dev->dev, + " Error parsing 'st,adc-channels' for idx %d\n", + chan_idx); + return ret; + } + if (ch->channel >= dfsdm->num_chs) { + dev_err(&indio_dev->dev, + " Error bad channel number %d (max = %d)\n", + ch->channel, dfsdm->num_chs); + return -EINVAL; + } + + ret = of_property_read_string_index(indio_dev->dev.of_node, + "st,adc-channel-names", chan_idx, + &ch->datasheet_name); + if (ret < 0) { + dev_err(&indio_dev->dev, + " Error parsing 'st,adc-channel-names' for idx %d\n", + chan_idx); + return ret; + } + + df_ch = &dfsdm->ch_list[ch->channel]; + df_ch->id = ch->channel; + + ret = of_property_read_string_index(indio_dev->dev.of_node, + "st,adc-channel-types", chan_idx, + &of_str); + if (!ret) { + val = stm32_dfsdm_str2val(of_str, stm32_dfsdm_chan_type); + if (val < 0) + return val; + } else { + val = 0; + } + df_ch->type = val; + + ret = of_property_read_string_index(indio_dev->dev.of_node, + "st,adc-channel-clk-src", chan_idx, + &of_str); + if (!ret) { + val = stm32_dfsdm_str2val(of_str, stm32_dfsdm_chan_src); + if (val < 0) + return val; + } else { + val = 0; + } + df_ch->src = val; + + df_ch->alt_si = of_property_present(indio_dev->dev.of_node, + "st,adc-alt-channel"); + + return 0; +} + +static int stm32_dfsdm_generic_channel_parse_of(struct stm32_dfsdm *dfsdm, + struct iio_dev *indio_dev, + struct iio_chan_spec *ch, + struct fwnode_handle *node) +{ + struct stm32_dfsdm_channel *df_ch; + struct stm32_dfsdm_adc *adc = iio_priv(indio_dev); + struct iio_backend *backend; + const char *of_str; + int ret, val; + + ret = fwnode_property_read_u32(node, "reg", &ch->channel); + if (ret < 0) { + dev_err(&indio_dev->dev, "Missing channel index %d\n", ret); + return ret; + } + + if (ch->channel >= dfsdm->num_chs) { + dev_err(&indio_dev->dev, " Error bad channel number %d (max = %d)\n", + ch->channel, dfsdm->num_chs); + return -EINVAL; + } + + if (fwnode_property_present(node, "label")) { + /* label is optional */ + ret = fwnode_property_read_string(node, "label", &ch->datasheet_name); + if (ret < 0) { + dev_err(&indio_dev->dev, + " Error parsing 'label' for idx %d\n", ch->channel); + return ret; + } + } + + df_ch = &dfsdm->ch_list[ch->channel]; + df_ch->id = ch->channel; + + ret = fwnode_property_read_string(node, "st,adc-channel-type", &of_str); + if (!ret) { + val = stm32_dfsdm_str2val(of_str, stm32_dfsdm_chan_type); + if (val < 0) + return val; + } else { + val = 0; + } + df_ch->type = val; + + ret = fwnode_property_read_string(node, "st,adc-channel-clk-src", &of_str); + if (!ret) { + val = stm32_dfsdm_str2val(of_str, stm32_dfsdm_chan_src); + if (val < 0) + return val; + } else { + val = 0; + } + df_ch->src = val; + + if (fwnode_property_present(node, "st,adc-alt-channel")) + df_ch->alt_si = 1; + + if (adc->dev_data->type == DFSDM_IIO) { + backend = devm_iio_backend_fwnode_get(&indio_dev->dev, NULL, node); + if (IS_ERR(backend)) + return dev_err_probe(&indio_dev->dev, PTR_ERR(backend), + "Failed to get backend\n"); + adc->backend[ch->scan_index] = backend; + } + + return 0; +} + +static ssize_t dfsdm_adc_audio_get_spiclk(struct iio_dev *indio_dev, + uintptr_t priv, + const struct iio_chan_spec *chan, + char *buf) +{ + struct stm32_dfsdm_adc *adc = iio_priv(indio_dev); + + return snprintf(buf, PAGE_SIZE, "%d\n", adc->spi_freq); +} + +static int dfsdm_adc_set_samp_freq(struct iio_dev *indio_dev, + unsigned int sample_freq, + unsigned int spi_freq) +{ + struct stm32_dfsdm_adc *adc = iio_priv(indio_dev); + unsigned int oversamp; + int ret; + + oversamp = DIV_ROUND_CLOSEST(spi_freq, sample_freq); + if (spi_freq % sample_freq) + dev_dbg(&indio_dev->dev, + "Rate not accurate. requested (%u), actual (%u)\n", + sample_freq, spi_freq / oversamp); + + ret = stm32_dfsdm_compute_all_osrs(indio_dev, oversamp); + if (ret < 0) + return ret; + + adc->sample_freq = spi_freq / oversamp; + adc->oversamp = oversamp; + + return 0; +} + +static ssize_t dfsdm_adc_audio_set_spiclk(struct iio_dev *indio_dev, + uintptr_t priv, + const struct iio_chan_spec *chan, + const char *buf, size_t len) +{ + struct stm32_dfsdm_adc *adc = iio_priv(indio_dev); + struct stm32_dfsdm_channel *ch = &adc->dfsdm->ch_list[chan->channel]; + unsigned int sample_freq = adc->sample_freq; + unsigned int spi_freq; + int ret; + + dev_err(&indio_dev->dev, "enter %s\n", __func__); + /* If DFSDM is master on SPI, SPI freq can not be updated */ + if (ch->src != DFSDM_CHANNEL_SPI_CLOCK_EXTERNAL) + return -EPERM; + + ret = kstrtoint(buf, 0, &spi_freq); + if (ret) + return ret; + + if (!spi_freq) + return -EINVAL; + + if (sample_freq) { + ret = dfsdm_adc_set_samp_freq(indio_dev, sample_freq, spi_freq); + if (ret < 0) + return ret; + } + adc->spi_freq = spi_freq; + + return len; +} + +static int stm32_dfsdm_start_conv(struct iio_dev *indio_dev, + struct iio_trigger *trig) +{ + struct stm32_dfsdm_adc *adc = iio_priv(indio_dev); + struct regmap *regmap = adc->dfsdm->regmap; + int ret; + + ret = stm32_dfsdm_channels_configure(indio_dev, adc->fl_id, trig); + if (ret < 0) + return ret; + + ret = stm32_dfsdm_start_channel(indio_dev); + if (ret < 0) + return ret; + + ret = stm32_dfsdm_filter_configure(indio_dev, adc->fl_id, trig); + if (ret < 0) + goto stop_channels; + + ret = stm32_dfsdm_start_filter(adc, adc->fl_id, trig); + if (ret < 0) + goto filter_unconfigure; + + return 0; + +filter_unconfigure: + regmap_clear_bits(regmap, DFSDM_CR1(adc->fl_id), DFSDM_CR1_CFG_MASK); +stop_channels: + stm32_dfsdm_stop_channel(indio_dev); + + return ret; +} + +static void stm32_dfsdm_stop_conv(struct iio_dev *indio_dev) +{ + struct stm32_dfsdm_adc *adc = iio_priv(indio_dev); + struct regmap *regmap = adc->dfsdm->regmap; + + stm32_dfsdm_stop_filter(adc->dfsdm, adc->fl_id); + + regmap_clear_bits(regmap, DFSDM_CR1(adc->fl_id), DFSDM_CR1_CFG_MASK); + + stm32_dfsdm_stop_channel(indio_dev); +} + +static int stm32_dfsdm_set_watermark(struct iio_dev *indio_dev, + unsigned int val) +{ + struct stm32_dfsdm_adc *adc = iio_priv(indio_dev); + unsigned int watermark = DFSDM_DMA_BUFFER_SIZE / 2; + unsigned int rx_buf_sz = DFSDM_DMA_BUFFER_SIZE; + + /* + * DMA cyclic transfers are used, buffer is split into two periods. + * There should be : + * - always one buffer (period) DMA is working on + * - one buffer (period) driver pushed to ASoC side. + */ + watermark = min(watermark, val * (unsigned int)(sizeof(u32))); + adc->buf_sz = min(rx_buf_sz, watermark * 2 * adc->nconv); + + return 0; +} + +static unsigned int stm32_dfsdm_adc_dma_residue(struct stm32_dfsdm_adc *adc) +{ + struct dma_tx_state state; + enum dma_status status; + + status = dmaengine_tx_status(adc->dma_chan, + adc->dma_chan->cookie, + &state); + if (status == DMA_IN_PROGRESS) { + /* Residue is size in bytes from end of buffer */ + unsigned int i = adc->buf_sz - state.residue; + unsigned int size; + + /* Return available bytes */ + if (i >= adc->bufi) + size = i - adc->bufi; + else + size = adc->buf_sz + i - adc->bufi; + + return size; + } + + return 0; +} + +static inline void stm32_dfsdm_process_data(struct stm32_dfsdm_adc *adc, + s32 *buffer) +{ + struct stm32_dfsdm_filter *fl = &adc->dfsdm->fl_list[adc->fl_id]; + struct stm32_dfsdm_filter_osr *flo = &fl->flo[fl->fast]; + unsigned int i = adc->nconv; + s32 *ptr = buffer; + + while (i--) { + /* Mask 8 LSB that contains the channel ID */ + *ptr &= 0xFFFFFF00; + /* Convert 2^(n-1) sample to 2^(n-1)-1 to avoid wrap-around */ + if (*ptr > flo->max) + *ptr -= 1; + /* + * Samples from filter are retrieved with 23 bits resolution + * or less. Shift left to align MSB on 24 bits. + */ + *ptr <<= flo->lshift; + + ptr++; + } +} + +static void stm32_dfsdm_dma_buffer_done(void *data) +{ + struct iio_dev *indio_dev = data; + struct stm32_dfsdm_adc *adc = iio_priv(indio_dev); + int available = stm32_dfsdm_adc_dma_residue(adc); + size_t old_pos; + + /* + * FIXME: In Kernel interface does not support cyclic DMA buffer,and + * offers only an interface to push data samples per samples. + * For this reason IIO buffer interface is not used and interface is + * bypassed using a private callback registered by ASoC. + * This should be a temporary solution waiting a cyclic DMA engine + * support in IIO. + */ + + dev_dbg(&indio_dev->dev, "pos = %d, available = %d\n", + adc->bufi, available); + old_pos = adc->bufi; + + while (available >= indio_dev->scan_bytes) { + s32 *buffer = (s32 *)&adc->rx_buf[adc->bufi]; + + stm32_dfsdm_process_data(adc, buffer); + + available -= indio_dev->scan_bytes; + adc->bufi += indio_dev->scan_bytes; + if (adc->bufi >= adc->buf_sz) { + if (adc->cb) + adc->cb(&adc->rx_buf[old_pos], + adc->buf_sz - old_pos, adc->cb_priv); + adc->bufi = 0; + old_pos = 0; + } + /* + * In DMA mode the trigger services of IIO are not used + * (e.g. no call to iio_trigger_poll). + * Calling irq handler associated to the hardware trigger is not + * relevant as the conversions have already been done. Data + * transfers are performed directly in DMA callback instead. + * This implementation avoids to call trigger irq handler that + * may sleep, in an atomic context (DMA irq handler context). + */ + if (adc->dev_data->type == DFSDM_IIO) + iio_push_to_buffers(indio_dev, buffer); + } + if (adc->cb) + adc->cb(&adc->rx_buf[old_pos], adc->bufi - old_pos, + adc->cb_priv); +} + +static int stm32_dfsdm_adc_dma_start(struct iio_dev *indio_dev) +{ + struct stm32_dfsdm_adc *adc = iio_priv(indio_dev); + /* + * The DFSDM supports half-word transfers. However, for 16 bits record, + * 4 bytes buswidth is kept, to avoid losing samples LSBs when left + * shift is required. + */ + struct dma_slave_config config = { + .src_addr = (dma_addr_t)adc->dfsdm->phys_base, + .src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES, + }; + struct dma_async_tx_descriptor *desc; + dma_cookie_t cookie; + int ret; + + if (!adc->dma_chan) + return -EINVAL; + + dev_dbg(&indio_dev->dev, "size=%d watermark=%d\n", + adc->buf_sz, adc->buf_sz / 2); + + if (adc->nconv == 1 && !indio_dev->trig) + config.src_addr += DFSDM_RDATAR(adc->fl_id); + else + config.src_addr += DFSDM_JDATAR(adc->fl_id); + ret = dmaengine_slave_config(adc->dma_chan, &config); + if (ret) + return ret; + + /* Prepare a DMA cyclic transaction */ + desc = dmaengine_prep_dma_cyclic(adc->dma_chan, + adc->dma_buf, + adc->buf_sz, adc->buf_sz / 2, + DMA_DEV_TO_MEM, + DMA_PREP_INTERRUPT); + if (!desc) + return -EBUSY; + + desc->callback = stm32_dfsdm_dma_buffer_done; + desc->callback_param = indio_dev; + + cookie = dmaengine_submit(desc); + ret = dma_submit_error(cookie); + if (ret) + goto err_stop_dma; + + /* Issue pending DMA requests */ + dma_async_issue_pending(adc->dma_chan); + + if (adc->nconv == 1 && !indio_dev->trig) { + /* Enable regular DMA transfer*/ + ret = regmap_set_bits(adc->dfsdm->regmap, + DFSDM_CR1(adc->fl_id), + DFSDM_CR1_RDMAEN_MASK); + } else { + /* Enable injected DMA transfer*/ + ret = regmap_set_bits(adc->dfsdm->regmap, + DFSDM_CR1(adc->fl_id), + DFSDM_CR1_JDMAEN_MASK); + } + + if (ret < 0) + goto err_stop_dma; + + return 0; + +err_stop_dma: + dmaengine_terminate_all(adc->dma_chan); + + return ret; +} + +static void stm32_dfsdm_adc_dma_stop(struct iio_dev *indio_dev) +{ + struct stm32_dfsdm_adc *adc = iio_priv(indio_dev); + + if (!adc->dma_chan) + return; + + regmap_clear_bits(adc->dfsdm->regmap, DFSDM_CR1(adc->fl_id), + DFSDM_CR1_RDMAEN_MASK | DFSDM_CR1_JDMAEN_MASK); + dmaengine_terminate_all(adc->dma_chan); +} + +static int stm32_dfsdm_update_scan_mode(struct iio_dev *indio_dev, + const unsigned long *scan_mask) +{ + struct stm32_dfsdm_adc *adc = iio_priv(indio_dev); + + adc->nconv = bitmap_weight(scan_mask, iio_get_masklength(indio_dev)); + adc->smask = *scan_mask; + + dev_dbg(&indio_dev->dev, "nconv=%d mask=%lx\n", adc->nconv, *scan_mask); + + return 0; +} + +static int stm32_dfsdm_postenable(struct iio_dev *indio_dev) +{ + struct stm32_dfsdm_adc *adc = iio_priv(indio_dev); + int i = 0; + int ret; + + /* Reset adc buffer index */ + adc->bufi = 0; + + if (adc->hwc) { + ret = iio_hw_consumer_enable(adc->hwc); + if (ret < 0) + return ret; + } + + if (adc->backend) { + while (adc->backend[i]) { + ret = iio_backend_enable(adc->backend[i]); + if (ret < 0) + return ret; + i++; + } + } + + ret = stm32_dfsdm_start_dfsdm(adc->dfsdm); + if (ret < 0) + goto err_stop_hwc; + + ret = stm32_dfsdm_adc_dma_start(indio_dev); + if (ret) { + dev_err(&indio_dev->dev, "Can't start DMA\n"); + goto stop_dfsdm; + } + + ret = stm32_dfsdm_start_conv(indio_dev, indio_dev->trig); + if (ret) { + dev_err(&indio_dev->dev, "Can't start conversion\n"); + goto err_stop_dma; + } + + return 0; + +err_stop_dma: + stm32_dfsdm_adc_dma_stop(indio_dev); +stop_dfsdm: + stm32_dfsdm_stop_dfsdm(adc->dfsdm); +err_stop_hwc: + if (adc->hwc) + iio_hw_consumer_disable(adc->hwc); + + return ret; +} + +static int stm32_dfsdm_predisable(struct iio_dev *indio_dev) +{ + struct stm32_dfsdm_adc *adc = iio_priv(indio_dev); + int i = 0; + + stm32_dfsdm_stop_conv(indio_dev); + + stm32_dfsdm_adc_dma_stop(indio_dev); + + stm32_dfsdm_stop_dfsdm(adc->dfsdm); + + if (adc->backend) { + while (adc->backend[i]) { + iio_backend_disable(adc->backend[i]); + i++; + } + } + + if (adc->hwc) + iio_hw_consumer_disable(adc->hwc); + + return 0; +} + +static const struct iio_buffer_setup_ops stm32_dfsdm_buffer_setup_ops = { + .postenable = &stm32_dfsdm_postenable, + .predisable = &stm32_dfsdm_predisable, +}; + +/** + * stm32_dfsdm_get_buff_cb() - register a callback that will be called when + * DMA transfer period is achieved. + * + * @iio_dev: Handle to IIO device. + * @cb: Pointer to callback function: + * - data: pointer to data buffer + * - size: size in byte of the data buffer + * - private: pointer to consumer private structure. + * @private: Pointer to consumer private structure. + */ +int stm32_dfsdm_get_buff_cb(struct iio_dev *iio_dev, + int (*cb)(const void *data, size_t size, + void *private), + void *private) +{ + struct stm32_dfsdm_adc *adc; + + if (!iio_dev) + return -EINVAL; + adc = iio_priv(iio_dev); + + adc->cb = cb; + adc->cb_priv = private; + + return 0; +} +EXPORT_SYMBOL_GPL(stm32_dfsdm_get_buff_cb); + +/** + * stm32_dfsdm_release_buff_cb - unregister buffer callback + * + * @iio_dev: Handle to IIO device. + */ +int stm32_dfsdm_release_buff_cb(struct iio_dev *iio_dev) +{ + struct stm32_dfsdm_adc *adc; + + if (!iio_dev) + return -EINVAL; + adc = iio_priv(iio_dev); + + adc->cb = NULL; + adc->cb_priv = NULL; + + return 0; +} +EXPORT_SYMBOL_GPL(stm32_dfsdm_release_buff_cb); + +static int stm32_dfsdm_single_conv(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, int *res) +{ + struct stm32_dfsdm_adc *adc = iio_priv(indio_dev); + long time_left; + int ret; + + reinit_completion(&adc->completion); + + adc->buffer = res; + + ret = stm32_dfsdm_start_dfsdm(adc->dfsdm); + if (ret < 0) + return ret; + + ret = regmap_update_bits(adc->dfsdm->regmap, DFSDM_CR2(adc->fl_id), + DFSDM_CR2_REOCIE_MASK, DFSDM_CR2_REOCIE(1)); + if (ret < 0) + goto stop_dfsdm; + + adc->nconv = 1; + adc->smask = BIT(chan->scan_index); + ret = stm32_dfsdm_start_conv(indio_dev, NULL); + if (ret < 0) { + regmap_update_bits(adc->dfsdm->regmap, DFSDM_CR2(adc->fl_id), + DFSDM_CR2_REOCIE_MASK, DFSDM_CR2_REOCIE(0)); + goto stop_dfsdm; + } + + time_left = wait_for_completion_interruptible_timeout(&adc->completion, + DFSDM_TIMEOUT); + + /* Mask IRQ for regular conversion achievement*/ + regmap_update_bits(adc->dfsdm->regmap, DFSDM_CR2(adc->fl_id), + DFSDM_CR2_REOCIE_MASK, DFSDM_CR2_REOCIE(0)); + + if (time_left == 0) + ret = -ETIMEDOUT; + else if (time_left < 0) + ret = time_left; + else + ret = IIO_VAL_INT; + + stm32_dfsdm_stop_conv(indio_dev); + + stm32_dfsdm_process_data(adc, res); + +stop_dfsdm: + stm32_dfsdm_stop_dfsdm(adc->dfsdm); + + return ret; +} + +static int stm32_dfsdm_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, int val2, long mask) +{ + struct stm32_dfsdm_adc *adc = iio_priv(indio_dev); + struct stm32_dfsdm_channel *ch = &adc->dfsdm->ch_list[chan->channel]; + unsigned int spi_freq; + int ret = -EINVAL; + + switch (ch->src) { + case DFSDM_CHANNEL_SPI_CLOCK_INTERNAL: + spi_freq = adc->dfsdm->spi_master_freq; + break; + case DFSDM_CHANNEL_SPI_CLOCK_INTERNAL_DIV2_FALLING: + case DFSDM_CHANNEL_SPI_CLOCK_INTERNAL_DIV2_RISING: + spi_freq = adc->dfsdm->spi_master_freq / 2; + break; + default: + spi_freq = adc->spi_freq; + } + + switch (mask) { + case IIO_CHAN_INFO_OVERSAMPLING_RATIO: + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; + + ret = stm32_dfsdm_compute_all_osrs(indio_dev, val); + if (!ret) { + dev_dbg(&indio_dev->dev, + "Sampling rate changed from (%u) to (%u)\n", + adc->sample_freq, spi_freq / val); + adc->oversamp = val; + adc->sample_freq = spi_freq / val; + } + iio_device_release_direct(indio_dev); + return ret; + + case IIO_CHAN_INFO_SAMP_FREQ: + if (!val) + return -EINVAL; + + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; + + ret = dfsdm_adc_set_samp_freq(indio_dev, val, spi_freq); + iio_device_release_direct(indio_dev); + return ret; + } + + return -EINVAL; +} + +static int __stm32_dfsdm_read_info_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val) +{ + struct stm32_dfsdm_adc *adc = iio_priv(indio_dev); + int ret = 0; + + if (adc->hwc) + ret = iio_hw_consumer_enable(adc->hwc); + if (adc->backend) + ret = iio_backend_enable(adc->backend[chan->scan_index]); + if (ret < 0) { + dev_err(&indio_dev->dev, + "%s: IIO enable failed (channel %d)\n", + __func__, chan->channel); + return ret; + } + ret = stm32_dfsdm_single_conv(indio_dev, chan, val); + if (adc->hwc) + iio_hw_consumer_disable(adc->hwc); + if (adc->backend) + iio_backend_disable(adc->backend[chan->scan_index]); + if (ret < 0) { + dev_err(&indio_dev->dev, + "%s: Conversion failed (channel %d)\n", + __func__, chan->channel); + return ret; + } + + return 0; +} + +static int stm32_dfsdm_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int *val, + int *val2, long mask) +{ + struct stm32_dfsdm_adc *adc = iio_priv(indio_dev); + + struct stm32_dfsdm_filter *fl = &adc->dfsdm->fl_list[adc->fl_id]; + struct stm32_dfsdm_filter_osr *flo = &fl->flo[fl->fast]; + u32 max = flo->max << (flo->lshift - chan->scan_type.shift); + int idx = chan->scan_index; + int ret; + + if (flo->lshift < chan->scan_type.shift) + max = flo->max >> (chan->scan_type.shift - flo->lshift); + + switch (mask) { + case IIO_CHAN_INFO_RAW: + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; + + ret = __stm32_dfsdm_read_info_raw(indio_dev, chan, val); + iio_device_release_direct(indio_dev); + if (ret) + return ret; + return IIO_VAL_INT; + + case IIO_CHAN_INFO_OVERSAMPLING_RATIO: + *val = adc->oversamp; + + return IIO_VAL_INT; + + case IIO_CHAN_INFO_SAMP_FREQ: + *val = adc->sample_freq; + + return IIO_VAL_INT; + + case IIO_CHAN_INFO_SCALE: + /* + * Scale is expressed in mV. + * When fast mode is disabled, actual resolution may be lower + * than 2^n, where n = realbits - 1. + * This leads to underestimating the input voltage. + * To compensate this deviation, the voltage reference can be + * corrected with a factor = realbits resolution / actual max + */ + if (adc->backend) { + ret = iio_backend_read_scale(adc->backend[idx], chan, val, NULL); + if (ret < 0) + return ret; + + *val = div_u64((u64)*val * (u64)BIT(DFSDM_DATA_RES - 1), max); + *val2 = chan->scan_type.realbits; + if (chan->differential) + *val *= 2; + } + return IIO_VAL_FRACTIONAL_LOG2; + + case IIO_CHAN_INFO_OFFSET: + /* + * DFSDM output data are in the range [-2^n, 2^n], + * with n = realbits - 1. + * - Differential modulator: + * Offset correspond to SD modulator offset. + * - Single ended modulator: + * Input is in [0V, Vref] range, + * where 0V corresponds to -2^n, and Vref to 2^n. + * Add 2^n to offset. (i.e. middle of input range) + * offset = offset(sd) * vref / res(sd) * max / vref. + */ + if (adc->backend) { + ret = iio_backend_read_offset(adc->backend[idx], chan, val, NULL); + if (ret < 0) + return ret; + + *val = div_u64((u64)max * *val, BIT(*val2 - 1)); + if (!chan->differential) + *val += max; + } + return IIO_VAL_INT; + } + + return -EINVAL; +} + +static int stm32_dfsdm_validate_trigger(struct iio_dev *indio_dev, + struct iio_trigger *trig) +{ + return stm32_dfsdm_get_jextsel(indio_dev, trig) < 0 ? -EINVAL : 0; +} + +static const struct iio_info stm32_dfsdm_info_audio = { + .hwfifo_set_watermark = stm32_dfsdm_set_watermark, + .read_raw = stm32_dfsdm_read_raw, + .write_raw = stm32_dfsdm_write_raw, + .update_scan_mode = stm32_dfsdm_update_scan_mode, +}; + +static const struct iio_info stm32_dfsdm_info_adc = { + .hwfifo_set_watermark = stm32_dfsdm_set_watermark, + .read_raw = stm32_dfsdm_read_raw, + .write_raw = stm32_dfsdm_write_raw, + .update_scan_mode = stm32_dfsdm_update_scan_mode, + .validate_trigger = stm32_dfsdm_validate_trigger, +}; + +static irqreturn_t stm32_dfsdm_irq(int irq, void *arg) +{ + struct iio_dev *indio_dev = arg; + struct stm32_dfsdm_adc *adc = iio_priv(indio_dev); + struct regmap *regmap = adc->dfsdm->regmap; + unsigned int status, int_en; + + regmap_read(regmap, DFSDM_ISR(adc->fl_id), &status); + regmap_read(regmap, DFSDM_CR2(adc->fl_id), &int_en); + + if (status & DFSDM_ISR_REOCF_MASK) { + /* Read the data register clean the IRQ status */ + regmap_read(regmap, DFSDM_RDATAR(adc->fl_id), adc->buffer); + complete(&adc->completion); + } + + if (status & DFSDM_ISR_ROVRF_MASK) { + if (int_en & DFSDM_CR2_ROVRIE_MASK) + dev_warn(&indio_dev->dev, "Overrun detected\n"); + regmap_set_bits(regmap, DFSDM_ICR(adc->fl_id), + DFSDM_ICR_CLRROVRF_MASK); + } + + return IRQ_HANDLED; +} + +/* + * Define external info for SPI Frequency and audio sampling rate that can be + * configured by ASoC driver through consumer.h API + */ +static const struct iio_chan_spec_ext_info dfsdm_adc_audio_ext_info[] = { + /* spi_clk_freq : clock freq on SPI/manchester bus used by channel */ + { + .name = "spi_clk_freq", + .shared = IIO_SHARED_BY_TYPE, + .read = dfsdm_adc_audio_get_spiclk, + .write = dfsdm_adc_audio_set_spiclk, + }, + { } +}; + +static void stm32_dfsdm_dma_release(struct iio_dev *indio_dev) +{ + struct stm32_dfsdm_adc *adc = iio_priv(indio_dev); + + if (adc->dma_chan) { + dma_free_coherent(adc->dma_chan->device->dev, + DFSDM_DMA_BUFFER_SIZE, + adc->rx_buf, adc->dma_buf); + dma_release_channel(adc->dma_chan); + } +} + +static int stm32_dfsdm_dma_request(struct device *dev, + struct iio_dev *indio_dev) +{ + struct stm32_dfsdm_adc *adc = iio_priv(indio_dev); + + adc->dma_chan = dma_request_chan(dev, "rx"); + if (IS_ERR(adc->dma_chan)) { + int ret = PTR_ERR(adc->dma_chan); + + adc->dma_chan = NULL; + return ret; + } + + adc->rx_buf = dma_alloc_coherent(adc->dma_chan->device->dev, + DFSDM_DMA_BUFFER_SIZE, + &adc->dma_buf, GFP_KERNEL); + if (!adc->rx_buf) { + dma_release_channel(adc->dma_chan); + return -ENOMEM; + } + + indio_dev->modes |= INDIO_BUFFER_SOFTWARE; + indio_dev->setup_ops = &stm32_dfsdm_buffer_setup_ops; + + return 0; +} + +static int stm32_dfsdm_adc_chan_init_one(struct iio_dev *indio_dev, struct iio_chan_spec *ch, + struct fwnode_handle *child) +{ + struct stm32_dfsdm_adc *adc = iio_priv(indio_dev); + int ret; + + if (child) + ret = stm32_dfsdm_generic_channel_parse_of(adc->dfsdm, indio_dev, ch, child); + else /* Legacy binding */ + ret = stm32_dfsdm_channel_parse_of(adc->dfsdm, indio_dev, ch); + if (ret < 0) + return dev_err_probe(&indio_dev->dev, ret, "Failed to parse channel\n"); + + ch->type = IIO_VOLTAGE; + ch->indexed = 1; + + /* + * IIO_CHAN_INFO_RAW: used to compute regular conversion + * IIO_CHAN_INFO_OVERSAMPLING_RATIO: used to set oversampling + */ + if (child) { + ch->info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SCALE) | + BIT(IIO_CHAN_INFO_OFFSET); + } else { + /* Legacy. Scaling not supported */ + ch->info_mask_separate = BIT(IIO_CHAN_INFO_RAW); + } + + ch->info_mask_shared_by_all = BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO) | + BIT(IIO_CHAN_INFO_SAMP_FREQ); + + if (adc->dev_data->type == DFSDM_AUDIO) { + ch->ext_info = dfsdm_adc_audio_ext_info; + ch->scan_index = 0; + } else { + ch->scan_type.shift = 8; + } + ch->scan_type.sign = 's'; + ch->scan_type.realbits = 24; + ch->scan_type.storagebits = 32; + + return stm32_dfsdm_chan_configure(adc->dfsdm, + &adc->dfsdm->ch_list[ch->channel]); +} + +static int stm32_dfsdm_chan_init(struct iio_dev *indio_dev, struct iio_chan_spec *channels) +{ + int num_ch = indio_dev->num_channels; + int chan_idx = 0; + int ret; + + for (chan_idx = 0; chan_idx < num_ch; chan_idx++) { + channels[chan_idx].scan_index = chan_idx; + ret = stm32_dfsdm_adc_chan_init_one(indio_dev, &channels[chan_idx], NULL); + if (ret < 0) + return dev_err_probe(&indio_dev->dev, ret, "Channels init failed\n"); + } + + return 0; +} + +static int stm32_dfsdm_generic_chan_init(struct iio_dev *indio_dev, struct iio_chan_spec *channels) +{ + int chan_idx = 0, ret; + + device_for_each_child_node_scoped(&indio_dev->dev, child) { + /* Skip DAI node in DFSDM audio nodes */ + if (fwnode_property_present(child, "compatible")) + continue; + + channels[chan_idx].scan_index = chan_idx; + ret = stm32_dfsdm_adc_chan_init_one(indio_dev, &channels[chan_idx], child); + if (ret < 0) + return dev_err_probe(&indio_dev->dev, ret, "Channels init failed\n"); + + chan_idx++; + } + + return chan_idx; +} + +static int stm32_dfsdm_audio_init(struct device *dev, struct iio_dev *indio_dev) +{ + struct iio_chan_spec *ch; + struct stm32_dfsdm_adc *adc = iio_priv(indio_dev); + struct stm32_dfsdm_channel *d_ch; + bool legacy = false; + int num_ch, ret; + + /* If st,adc-channels is defined legacy binding is used. Else assume generic binding. */ + num_ch = of_property_count_u32_elems(indio_dev->dev.of_node, "st,adc-channels"); + if (num_ch == 1) + legacy = true; + + ch = devm_kzalloc(&indio_dev->dev, sizeof(*ch), GFP_KERNEL); + if (!ch) + return -ENOMEM; + + indio_dev->num_channels = 1; + indio_dev->channels = ch; + + if (legacy) + ret = stm32_dfsdm_chan_init(indio_dev, ch); + else + ret = stm32_dfsdm_generic_chan_init(indio_dev, ch); + + if (ret < 0) { + dev_err(&indio_dev->dev, "Channels init failed\n"); + return ret; + } + ch->info_mask_separate = BIT(IIO_CHAN_INFO_SAMP_FREQ); + + d_ch = &adc->dfsdm->ch_list[ch->channel]; + if (d_ch->src != DFSDM_CHANNEL_SPI_CLOCK_EXTERNAL) + adc->spi_freq = adc->dfsdm->spi_master_freq; + + return stm32_dfsdm_dma_request(dev, indio_dev); +} + +static int stm32_dfsdm_adc_init(struct device *dev, struct iio_dev *indio_dev) +{ + struct iio_chan_spec *ch; + struct stm32_dfsdm_adc *adc = iio_priv(indio_dev); + int num_ch, ret; + bool legacy = false; + + adc->oversamp = DFSDM_DEFAULT_OVERSAMPLING; + ret = stm32_dfsdm_compute_all_osrs(indio_dev, adc->oversamp); + if (ret < 0) + return ret; + + num_ch = device_get_child_node_count(&indio_dev->dev); + if (!num_ch) { + /* No channels nodes found. Assume legacy binding */ + num_ch = of_property_count_u32_elems(indio_dev->dev.of_node, "st,adc-channels"); + if (num_ch < 0) { + dev_err(&indio_dev->dev, "Bad st,adc-channels\n"); + return num_ch; + } + + legacy = true; + } + + if (num_ch > adc->dfsdm->num_chs) { + dev_err(&indio_dev->dev, "Number of channel [%d] exceeds [%d]\n", + num_ch, adc->dfsdm->num_chs); + return -EINVAL; + } + indio_dev->num_channels = num_ch; + + if (legacy) { + /* Bind to SD modulator IIO device. */ + adc->hwc = devm_iio_hw_consumer_alloc(&indio_dev->dev); + if (IS_ERR(adc->hwc)) + return dev_err_probe(&indio_dev->dev, -EPROBE_DEFER, + "waiting for SD modulator\n"); + } else { + /* Generic binding. SD modulator IIO device not used. Use SD modulator backend. */ + adc->hwc = NULL; + + adc->backend = devm_kcalloc(&indio_dev->dev, num_ch, sizeof(*adc->backend), + GFP_KERNEL); + if (!adc->backend) + return -ENOMEM; + } + + ch = devm_kcalloc(&indio_dev->dev, num_ch, sizeof(*ch), GFP_KERNEL); + if (!ch) + return -ENOMEM; + indio_dev->channels = ch; + + if (legacy) + ret = stm32_dfsdm_chan_init(indio_dev, ch); + else + ret = stm32_dfsdm_generic_chan_init(indio_dev, ch); + if (ret < 0) + return ret; + + init_completion(&adc->completion); + + /* Optionally request DMA */ + ret = stm32_dfsdm_dma_request(dev, indio_dev); + if (ret) { + if (ret != -ENODEV) + return dev_err_probe(dev, ret, + "DMA channel request failed with\n"); + + dev_dbg(dev, "No DMA support\n"); + return 0; + } + + ret = iio_triggered_buffer_setup(indio_dev, + &iio_pollfunc_store_time, NULL, + &stm32_dfsdm_buffer_setup_ops); + if (ret) { + stm32_dfsdm_dma_release(indio_dev); + dev_err(&indio_dev->dev, "buffer setup failed\n"); + return ret; + } + + /* lptimer/timer hardware triggers */ + indio_dev->modes |= INDIO_HARDWARE_TRIGGERED; + + return 0; +} + +static const struct stm32_dfsdm_dev_data stm32h7_dfsdm_adc_data = { + .type = DFSDM_IIO, + .init = stm32_dfsdm_adc_init, +}; + +static const struct stm32_dfsdm_dev_data stm32h7_dfsdm_audio_data = { + .type = DFSDM_AUDIO, + .init = stm32_dfsdm_audio_init, +}; + +static const struct of_device_id stm32_dfsdm_adc_match[] = { + { + .compatible = "st,stm32-dfsdm-adc", + .data = &stm32h7_dfsdm_adc_data, + }, + { + .compatible = "st,stm32-dfsdm-dmic", + .data = &stm32h7_dfsdm_audio_data, + }, + { } +}; +MODULE_DEVICE_TABLE(of, stm32_dfsdm_adc_match); + +static int stm32_dfsdm_adc_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct stm32_dfsdm_adc *adc; + struct device_node *np = dev->of_node; + const struct stm32_dfsdm_dev_data *dev_data; + struct iio_dev *iio; + char *name; + int ret, irq, val; + + dev_data = of_device_get_match_data(dev); + iio = devm_iio_device_alloc(dev, sizeof(*adc)); + if (!iio) + return -ENOMEM; + + adc = iio_priv(iio); + adc->dfsdm = dev_get_drvdata(dev->parent); + + iio->dev.of_node = np; + iio->modes = INDIO_DIRECT_MODE; + + platform_set_drvdata(pdev, iio); + + ret = of_property_read_u32(dev->of_node, "reg", &adc->fl_id); + if (ret != 0 || adc->fl_id >= adc->dfsdm->num_fls) { + dev_err(dev, "Missing or bad reg property\n"); + return -EINVAL; + } + + name = devm_kzalloc(dev, sizeof("dfsdm-adc0"), GFP_KERNEL); + if (!name) + return -ENOMEM; + if (dev_data->type == DFSDM_AUDIO) { + iio->info = &stm32_dfsdm_info_audio; + snprintf(name, sizeof("dfsdm-pdm0"), "dfsdm-pdm%d", adc->fl_id); + } else { + iio->info = &stm32_dfsdm_info_adc; + snprintf(name, sizeof("dfsdm-adc0"), "dfsdm-adc%d", adc->fl_id); + } + iio->name = name; + + /* + * In a first step IRQs generated for channels are not treated. + * So IRQ associated to filter instance 0 is dedicated to the Filter 0. + */ + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return irq; + + ret = devm_request_irq(dev, irq, stm32_dfsdm_irq, + 0, pdev->name, iio); + if (ret < 0) { + dev_err(dev, "Failed to request IRQ\n"); + return ret; + } + + ret = of_property_read_u32(dev->of_node, "st,filter-order", &val); + if (ret < 0) { + dev_err(dev, "Failed to set filter order\n"); + return ret; + } + + adc->dfsdm->fl_list[adc->fl_id].ford = val; + + adc->dfsdm->fl_list[adc->fl_id].sync_mode = + of_property_read_bool(dev->of_node, "st,filter0-sync"); + + adc->dev_data = dev_data; + ret = dev_data->init(dev, iio); + if (ret < 0) + return ret; + + ret = iio_device_register(iio); + if (ret < 0) + goto err_cleanup; + + if (dev_data->type == DFSDM_AUDIO) { + ret = of_platform_populate(np, NULL, NULL, dev); + if (ret < 0) { + dev_err(dev, "Failed to find an audio DAI\n"); + goto err_unregister; + } + } + + return 0; + +err_unregister: + iio_device_unregister(iio); +err_cleanup: + stm32_dfsdm_dma_release(iio); + + return ret; +} + +static void stm32_dfsdm_adc_remove(struct platform_device *pdev) +{ + struct iio_dev *indio_dev = platform_get_drvdata(pdev); + struct stm32_dfsdm_adc *adc = iio_priv(indio_dev); + + if (adc->dev_data->type == DFSDM_AUDIO) + of_platform_depopulate(&pdev->dev); + iio_device_unregister(indio_dev); + stm32_dfsdm_dma_release(indio_dev); +} + +static int stm32_dfsdm_adc_suspend(struct device *dev) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + + if (iio_buffer_enabled(indio_dev)) + stm32_dfsdm_predisable(indio_dev); + + return 0; +} + +static int stm32_dfsdm_adc_resume(struct device *dev) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct stm32_dfsdm_adc *adc = iio_priv(indio_dev); + const struct iio_chan_spec *chan; + struct stm32_dfsdm_channel *ch; + int i, ret; + + /* restore channels configuration */ + for (i = 0; i < indio_dev->num_channels; i++) { + chan = indio_dev->channels + i; + ch = &adc->dfsdm->ch_list[chan->channel]; + ret = stm32_dfsdm_chan_configure(adc->dfsdm, ch); + if (ret) + return ret; + } + + if (iio_buffer_enabled(indio_dev)) + stm32_dfsdm_postenable(indio_dev); + + return 0; +} + +static DEFINE_SIMPLE_DEV_PM_OPS(stm32_dfsdm_adc_pm_ops, + stm32_dfsdm_adc_suspend, + stm32_dfsdm_adc_resume); + +static struct platform_driver stm32_dfsdm_adc_driver = { + .driver = { + .name = "stm32-dfsdm-adc", + .of_match_table = stm32_dfsdm_adc_match, + .pm = pm_sleep_ptr(&stm32_dfsdm_adc_pm_ops), + }, + .probe = stm32_dfsdm_adc_probe, + .remove = stm32_dfsdm_adc_remove, +}; +module_platform_driver(stm32_dfsdm_adc_driver); + +MODULE_DESCRIPTION("STM32 sigma delta ADC"); +MODULE_AUTHOR("Arnaud Pouliquen "); +MODULE_LICENSE("GPL v2"); +MODULE_IMPORT_NS("IIO_BACKEND"); diff --git a/drivers/iio/adc/stm32-dfsdm-core.c b/drivers/iio/adc/stm32-dfsdm-core.c new file mode 100644 index 000000000..47e2d1338 --- /dev/null +++ b/drivers/iio/adc/stm32-dfsdm-core.c @@ -0,0 +1,522 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * This file is part the core part STM32 DFSDM driver + * + * Copyright (C) 2017, STMicroelectronics - All Rights Reserved + * Author(s): Arnaud Pouliquen for STMicroelectronics. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "stm32-dfsdm.h" + +/** + * struct stm32_dfsdm_dev_data - DFSDM compatible configuration data + * @ipid: DFSDM identification number. Used only if hardware provides identification registers + * @num_filters: DFSDM number of filters. Unused if identification registers are available + * @num_channels: DFSDM number of channels. Unused if identification registers are available + * @regmap_cfg: SAI register map configuration pointer + */ +struct stm32_dfsdm_dev_data { + u32 ipid; + unsigned int num_filters; + unsigned int num_channels; + const struct regmap_config *regmap_cfg; +}; + +#define STM32H7_DFSDM_NUM_FILTERS 4 +#define STM32H7_DFSDM_NUM_CHANNELS 8 + +static bool stm32_dfsdm_volatile_reg(struct device *dev, unsigned int reg) +{ + if (reg < DFSDM_FILTER_BASE_ADR) + return false; + + /* + * Mask is done on register to avoid to list registers of all + * filter instances. + */ + switch (reg & DFSDM_FILTER_REG_MASK) { + case DFSDM_CR1(0) & DFSDM_FILTER_REG_MASK: + case DFSDM_ISR(0) & DFSDM_FILTER_REG_MASK: + case DFSDM_JDATAR(0) & DFSDM_FILTER_REG_MASK: + case DFSDM_RDATAR(0) & DFSDM_FILTER_REG_MASK: + return true; + } + + return false; +} + +static const struct regmap_config stm32h7_dfsdm_regmap_cfg = { + .reg_bits = 32, + .val_bits = 32, + .reg_stride = sizeof(u32), + .max_register = 0x2B8, + .volatile_reg = stm32_dfsdm_volatile_reg, + .fast_io = true, +}; + +static const struct stm32_dfsdm_dev_data stm32h7_dfsdm_data = { + .num_filters = STM32H7_DFSDM_NUM_FILTERS, + .num_channels = STM32H7_DFSDM_NUM_CHANNELS, + .regmap_cfg = &stm32h7_dfsdm_regmap_cfg, +}; + +static const struct regmap_config stm32mp1_dfsdm_regmap_cfg = { + .reg_bits = 32, + .val_bits = 32, + .reg_stride = sizeof(u32), + .max_register = 0x7fc, + .volatile_reg = stm32_dfsdm_volatile_reg, + .fast_io = true, +}; + +static const struct stm32_dfsdm_dev_data stm32mp1_dfsdm_data = { + .ipid = STM32MP15_IPIDR_NUMBER, + .regmap_cfg = &stm32mp1_dfsdm_regmap_cfg, +}; + +struct dfsdm_priv { + struct platform_device *pdev; /* platform device */ + + struct stm32_dfsdm dfsdm; /* common data exported for all instances */ + + unsigned int spi_clk_out_div; /* SPI clkout divider value */ + atomic_t n_active_ch; /* number of current active channels */ + + struct clk *clk; /* DFSDM clock */ + struct clk *aclk; /* audio clock */ +}; + +static inline struct dfsdm_priv *to_stm32_dfsdm_priv(struct stm32_dfsdm *dfsdm) +{ + return container_of(dfsdm, struct dfsdm_priv, dfsdm); +} + +static int stm32_dfsdm_clk_prepare_enable(struct stm32_dfsdm *dfsdm) +{ + struct dfsdm_priv *priv = to_stm32_dfsdm_priv(dfsdm); + int ret; + + ret = clk_prepare_enable(priv->clk); + if (ret || !priv->aclk) + return ret; + + ret = clk_prepare_enable(priv->aclk); + if (ret) + clk_disable_unprepare(priv->clk); + + return ret; +} + +static void stm32_dfsdm_clk_disable_unprepare(struct stm32_dfsdm *dfsdm) +{ + struct dfsdm_priv *priv = to_stm32_dfsdm_priv(dfsdm); + + clk_disable_unprepare(priv->aclk); + clk_disable_unprepare(priv->clk); +} + +/** + * stm32_dfsdm_start_dfsdm - start global dfsdm interface. + * + * Enable interface if n_active_ch is not null. + * @dfsdm: Handle used to retrieve dfsdm context. + */ +int stm32_dfsdm_start_dfsdm(struct stm32_dfsdm *dfsdm) +{ + struct dfsdm_priv *priv = to_stm32_dfsdm_priv(dfsdm); + struct device *dev = &priv->pdev->dev; + unsigned int clk_div = priv->spi_clk_out_div, clk_src; + int ret; + + if (atomic_inc_return(&priv->n_active_ch) == 1) { + ret = pm_runtime_resume_and_get(dev); + if (ret < 0) + goto error_ret; + + /* select clock source, e.g. 0 for "dfsdm" or 1 for "audio" */ + clk_src = priv->aclk ? 1 : 0; + ret = regmap_update_bits(dfsdm->regmap, DFSDM_CHCFGR1(0), + DFSDM_CHCFGR1_CKOUTSRC_MASK, + DFSDM_CHCFGR1_CKOUTSRC(clk_src)); + if (ret < 0) + goto pm_put; + + /* Output the SPI CLKOUT (if clk_div == 0 clock if OFF) */ + ret = regmap_update_bits(dfsdm->regmap, DFSDM_CHCFGR1(0), + DFSDM_CHCFGR1_CKOUTDIV_MASK, + DFSDM_CHCFGR1_CKOUTDIV(clk_div)); + if (ret < 0) + goto pm_put; + + /* Global enable of DFSDM interface */ + ret = regmap_update_bits(dfsdm->regmap, DFSDM_CHCFGR1(0), + DFSDM_CHCFGR1_DFSDMEN_MASK, + DFSDM_CHCFGR1_DFSDMEN(1)); + if (ret < 0) + goto pm_put; + } + + dev_dbg(dev, "%s: n_active_ch %d\n", __func__, + atomic_read(&priv->n_active_ch)); + + return 0; + +pm_put: + pm_runtime_put_sync(dev); +error_ret: + atomic_dec(&priv->n_active_ch); + + return ret; +} +EXPORT_SYMBOL_GPL(stm32_dfsdm_start_dfsdm); + +/** + * stm32_dfsdm_stop_dfsdm - stop global DFSDM interface. + * + * Disable interface if n_active_ch is null + * @dfsdm: Handle used to retrieve dfsdm context. + */ +int stm32_dfsdm_stop_dfsdm(struct stm32_dfsdm *dfsdm) +{ + struct dfsdm_priv *priv = to_stm32_dfsdm_priv(dfsdm); + int ret; + + if (atomic_dec_and_test(&priv->n_active_ch)) { + /* Global disable of DFSDM interface */ + ret = regmap_update_bits(dfsdm->regmap, DFSDM_CHCFGR1(0), + DFSDM_CHCFGR1_DFSDMEN_MASK, + DFSDM_CHCFGR1_DFSDMEN(0)); + if (ret < 0) + return ret; + + /* Stop SPI CLKOUT */ + ret = regmap_update_bits(dfsdm->regmap, DFSDM_CHCFGR1(0), + DFSDM_CHCFGR1_CKOUTDIV_MASK, + DFSDM_CHCFGR1_CKOUTDIV(0)); + if (ret < 0) + return ret; + + pm_runtime_put_sync(&priv->pdev->dev); + } + dev_dbg(&priv->pdev->dev, "%s: n_active_ch %d\n", __func__, + atomic_read(&priv->n_active_ch)); + + return 0; +} +EXPORT_SYMBOL_GPL(stm32_dfsdm_stop_dfsdm); + +static int stm32_dfsdm_parse_of(struct platform_device *pdev, + struct dfsdm_priv *priv) +{ + struct device_node *node = pdev->dev.of_node; + struct resource *res; + unsigned long clk_freq, divider; + unsigned int spi_freq, rem; + int ret; + + if (!node) + return -EINVAL; + + priv->dfsdm.base = devm_platform_get_and_ioremap_resource(pdev, 0, + &res); + if (IS_ERR(priv->dfsdm.base)) + return PTR_ERR(priv->dfsdm.base); + + priv->dfsdm.phys_base = res->start; + + /* + * "dfsdm" clock is mandatory for DFSDM peripheral clocking. + * "dfsdm" or "audio" clocks can be used as source clock for + * the SPI clock out signal and internal processing, depending + * on use case. + */ + priv->clk = devm_clk_get(&pdev->dev, "dfsdm"); + if (IS_ERR(priv->clk)) + return dev_err_probe(&pdev->dev, PTR_ERR(priv->clk), + "Failed to get clock\n"); + + priv->aclk = devm_clk_get(&pdev->dev, "audio"); + if (IS_ERR(priv->aclk)) + priv->aclk = NULL; + + if (priv->aclk) + clk_freq = clk_get_rate(priv->aclk); + else + clk_freq = clk_get_rate(priv->clk); + + /* SPI clock out frequency */ + ret = of_property_read_u32(pdev->dev.of_node, "spi-max-frequency", + &spi_freq); + if (ret < 0) { + /* No SPI master mode */ + return 0; + } + + divider = div_u64_rem(clk_freq, spi_freq, &rem); + /* Round up divider when ckout isn't precise, not to exceed spi_freq */ + if (rem) + divider++; + + /* programmable divider is in range of [2:256] */ + if (divider < 2 || divider > 256) { + dev_err(&pdev->dev, "spi-max-frequency not achievable\n"); + return -EINVAL; + } + + /* SPI clock output divider is: divider = CKOUTDIV + 1 */ + priv->spi_clk_out_div = divider - 1; + priv->dfsdm.spi_master_freq = clk_freq / (priv->spi_clk_out_div + 1); + + if (rem) { + dev_warn(&pdev->dev, "SPI clock not accurate\n"); + dev_warn(&pdev->dev, "%ld = %d * %d + %d\n", + clk_freq, spi_freq, priv->spi_clk_out_div + 1, rem); + } + + return 0; +}; + +static const struct of_device_id stm32_dfsdm_of_match[] = { + { + .compatible = "st,stm32h7-dfsdm", + .data = &stm32h7_dfsdm_data, + }, + { + .compatible = "st,stm32mp1-dfsdm", + .data = &stm32mp1_dfsdm_data, + }, + { } +}; +MODULE_DEVICE_TABLE(of, stm32_dfsdm_of_match); + +static int stm32_dfsdm_probe_identification(struct platform_device *pdev, + struct dfsdm_priv *priv, + const struct stm32_dfsdm_dev_data *dev_data) +{ + struct device_node *np = pdev->dev.of_node; + struct device_node *child; + struct stm32_dfsdm *dfsdm = &priv->dfsdm; + const char *compat; + int ret, count = 0; + u32 id, val; + + if (!dev_data->ipid) { + dfsdm->num_fls = dev_data->num_filters; + dfsdm->num_chs = dev_data->num_channels; + return 0; + } + + ret = regmap_read(dfsdm->regmap, DFSDM_IPIDR, &id); + if (ret) + return ret; + + if (id != dev_data->ipid) { + dev_err(&pdev->dev, "Unexpected IP version: 0x%x", id); + return -EINVAL; + } + + for_each_child_of_node(np, child) { + ret = of_property_read_string(child, "compatible", &compat); + if (ret) + continue; + /* Count only child nodes with dfsdm compatible */ + if (strstr(compat, "dfsdm")) + count++; + } + + ret = regmap_read(dfsdm->regmap, DFSDM_HWCFGR, &val); + if (ret) + return ret; + + dfsdm->num_fls = FIELD_GET(DFSDM_HWCFGR_NBF_MASK, val); + dfsdm->num_chs = FIELD_GET(DFSDM_HWCFGR_NBT_MASK, val); + + if (count > dfsdm->num_fls) { + dev_err(&pdev->dev, "Unexpected child number: %d", count); + return -EINVAL; + } + + ret = regmap_read(dfsdm->regmap, DFSDM_VERR, &val); + if (ret) + return ret; + + dev_dbg(&pdev->dev, "DFSDM version: %lu.%lu. %d channels/%d filters\n", + FIELD_GET(DFSDM_VERR_MAJREV_MASK, val), + FIELD_GET(DFSDM_VERR_MINREV_MASK, val), + dfsdm->num_chs, dfsdm->num_fls); + + return 0; +} + +static int stm32_dfsdm_probe(struct platform_device *pdev) +{ + struct dfsdm_priv *priv; + const struct stm32_dfsdm_dev_data *dev_data; + struct stm32_dfsdm *dfsdm; + int ret; + + priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->pdev = pdev; + + dev_data = of_device_get_match_data(&pdev->dev); + + dfsdm = &priv->dfsdm; + + ret = stm32_dfsdm_parse_of(pdev, priv); + if (ret < 0) + return ret; + + dfsdm->regmap = devm_regmap_init_mmio_clk(&pdev->dev, "dfsdm", + dfsdm->base, + dev_data->regmap_cfg); + if (IS_ERR(dfsdm->regmap)) { + ret = PTR_ERR(dfsdm->regmap); + dev_err(&pdev->dev, "%s: Failed to allocate regmap: %d\n", + __func__, ret); + return ret; + } + + ret = stm32_dfsdm_probe_identification(pdev, priv, dev_data); + if (ret < 0) + return ret; + + dfsdm->fl_list = devm_kcalloc(&pdev->dev, dfsdm->num_fls, + sizeof(*dfsdm->fl_list), GFP_KERNEL); + if (!dfsdm->fl_list) + return -ENOMEM; + + dfsdm->ch_list = devm_kcalloc(&pdev->dev, dfsdm->num_chs, + sizeof(*dfsdm->ch_list), GFP_KERNEL); + if (!dfsdm->ch_list) + return -ENOMEM; + + platform_set_drvdata(pdev, dfsdm); + + ret = stm32_dfsdm_clk_prepare_enable(dfsdm); + if (ret) { + dev_err(&pdev->dev, "Failed to start clock\n"); + return ret; + } + + pm_runtime_get_noresume(&pdev->dev); + pm_runtime_set_active(&pdev->dev); + pm_runtime_enable(&pdev->dev); + + ret = of_platform_populate(pdev->dev.of_node, NULL, NULL, &pdev->dev); + if (ret) + goto pm_put; + + pm_runtime_put(&pdev->dev); + + return 0; + +pm_put: + pm_runtime_disable(&pdev->dev); + pm_runtime_set_suspended(&pdev->dev); + pm_runtime_put_noidle(&pdev->dev); + stm32_dfsdm_clk_disable_unprepare(dfsdm); + + return ret; +} + +static void stm32_dfsdm_core_remove(struct platform_device *pdev) +{ + struct stm32_dfsdm *dfsdm = platform_get_drvdata(pdev); + + pm_runtime_get_sync(&pdev->dev); + of_platform_depopulate(&pdev->dev); + pm_runtime_disable(&pdev->dev); + pm_runtime_set_suspended(&pdev->dev); + pm_runtime_put_noidle(&pdev->dev); + stm32_dfsdm_clk_disable_unprepare(dfsdm); +} + +static int stm32_dfsdm_core_suspend(struct device *dev) +{ + struct stm32_dfsdm *dfsdm = dev_get_drvdata(dev); + struct dfsdm_priv *priv = to_stm32_dfsdm_priv(dfsdm); + int ret; + + ret = pm_runtime_force_suspend(dev); + if (ret) + return ret; + + /* Balance devm_regmap_init_mmio_clk() clk_prepare() */ + clk_unprepare(priv->clk); + + return pinctrl_pm_select_sleep_state(dev); +} + +static int stm32_dfsdm_core_resume(struct device *dev) +{ + struct stm32_dfsdm *dfsdm = dev_get_drvdata(dev); + struct dfsdm_priv *priv = to_stm32_dfsdm_priv(dfsdm); + int ret; + + ret = pinctrl_pm_select_default_state(dev); + if (ret) + return ret; + + ret = clk_prepare(priv->clk); + if (ret) + return ret; + + return pm_runtime_force_resume(dev); +} + +static int stm32_dfsdm_core_runtime_suspend(struct device *dev) +{ + struct stm32_dfsdm *dfsdm = dev_get_drvdata(dev); + + stm32_dfsdm_clk_disable_unprepare(dfsdm); + + return 0; +} + +static int stm32_dfsdm_core_runtime_resume(struct device *dev) +{ + struct stm32_dfsdm *dfsdm = dev_get_drvdata(dev); + + return stm32_dfsdm_clk_prepare_enable(dfsdm); +} + +static const struct dev_pm_ops stm32_dfsdm_core_pm_ops = { + SYSTEM_SLEEP_PM_OPS(stm32_dfsdm_core_suspend, stm32_dfsdm_core_resume) + RUNTIME_PM_OPS(stm32_dfsdm_core_runtime_suspend, + stm32_dfsdm_core_runtime_resume, + NULL) +}; + +static struct platform_driver stm32_dfsdm_driver = { + .probe = stm32_dfsdm_probe, + .remove = stm32_dfsdm_core_remove, + .driver = { + .name = "stm32-dfsdm", + .of_match_table = stm32_dfsdm_of_match, + .pm = pm_ptr(&stm32_dfsdm_core_pm_ops), + }, +}; + +module_platform_driver(stm32_dfsdm_driver); + +MODULE_AUTHOR("Arnaud Pouliquen "); +MODULE_DESCRIPTION("STMicroelectronics STM32 dfsdm driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/adc/stm32-dfsdm.h b/drivers/iio/adc/stm32-dfsdm.h new file mode 100644 index 000000000..570a1552a --- /dev/null +++ b/drivers/iio/adc/stm32-dfsdm.h @@ -0,0 +1,348 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * This file is part of STM32 DFSDM driver + * + * Copyright (C) 2017, STMicroelectronics - All Rights Reserved + * Author(s): Arnaud Pouliquen . + */ + +#ifndef MDF_STM32_DFSDM__H +#define MDF_STM32_DFSDM__H + +#include + +/* + * STM32 DFSDM - global register map + * __________________________________________________________ + * | Offset | Registers block | + * ---------------------------------------------------------- + * | 0x000 | CHANNEL 0 + COMMON CHANNEL FIELDS | + * ---------------------------------------------------------- + * | 0x020 | CHANNEL 1 | + * ---------------------------------------------------------- + * | ... | ..... | + * ---------------------------------------------------------- + * | 0x20 x n | CHANNEL n | + * ---------------------------------------------------------- + * | 0x100 | FILTER 0 + COMMON FILTER FIELDs | + * ---------------------------------------------------------- + * | 0x200 | FILTER 1 | + * ---------------------------------------------------------- + * | | ..... | + * ---------------------------------------------------------- + * | 0x100 x m | FILTER m | + * ---------------------------------------------------------- + * | | ..... | + * ---------------------------------------------------------- + * | 0x7F0-7FC | Identification registers | + * ---------------------------------------------------------- + */ + +/* + * Channels register definitions + */ +#define DFSDM_CHCFGR1(y) ((y) * 0x20 + 0x00) +#define DFSDM_CHCFGR2(y) ((y) * 0x20 + 0x04) +#define DFSDM_AWSCDR(y) ((y) * 0x20 + 0x08) +#define DFSDM_CHWDATR(y) ((y) * 0x20 + 0x0C) +#define DFSDM_CHDATINR(y) ((y) * 0x20 + 0x10) + +/* CHCFGR1: Channel configuration register 1 */ +#define DFSDM_CHCFGR1_SITP_MASK GENMASK(1, 0) +#define DFSDM_CHCFGR1_SITP(v) FIELD_PREP(DFSDM_CHCFGR1_SITP_MASK, v) +#define DFSDM_CHCFGR1_SPICKSEL_MASK GENMASK(3, 2) +#define DFSDM_CHCFGR1_SPICKSEL(v) FIELD_PREP(DFSDM_CHCFGR1_SPICKSEL_MASK, v) +#define DFSDM_CHCFGR1_SCDEN_MASK BIT(5) +#define DFSDM_CHCFGR1_SCDEN(v) FIELD_PREP(DFSDM_CHCFGR1_SCDEN_MASK, v) +#define DFSDM_CHCFGR1_CKABEN_MASK BIT(6) +#define DFSDM_CHCFGR1_CKABEN(v) FIELD_PREP(DFSDM_CHCFGR1_CKABEN_MASK, v) +#define DFSDM_CHCFGR1_CHEN_MASK BIT(7) +#define DFSDM_CHCFGR1_CHEN(v) FIELD_PREP(DFSDM_CHCFGR1_CHEN_MASK, v) +#define DFSDM_CHCFGR1_CHINSEL_MASK BIT(8) +#define DFSDM_CHCFGR1_CHINSEL(v) FIELD_PREP(DFSDM_CHCFGR1_CHINSEL_MASK, v) +#define DFSDM_CHCFGR1_DATMPX_MASK GENMASK(13, 12) +#define DFSDM_CHCFGR1_DATMPX(v) FIELD_PREP(DFSDM_CHCFGR1_DATMPX_MASK, v) +#define DFSDM_CHCFGR1_DATPACK_MASK GENMASK(15, 14) +#define DFSDM_CHCFGR1_DATPACK(v) FIELD_PREP(DFSDM_CHCFGR1_DATPACK_MASK, v) +#define DFSDM_CHCFGR1_CKOUTDIV_MASK GENMASK(23, 16) +#define DFSDM_CHCFGR1_CKOUTDIV(v) FIELD_PREP(DFSDM_CHCFGR1_CKOUTDIV_MASK, v) +#define DFSDM_CHCFGR1_CKOUTSRC_MASK BIT(30) +#define DFSDM_CHCFGR1_CKOUTSRC(v) FIELD_PREP(DFSDM_CHCFGR1_CKOUTSRC_MASK, v) +#define DFSDM_CHCFGR1_DFSDMEN_MASK BIT(31) +#define DFSDM_CHCFGR1_DFSDMEN(v) FIELD_PREP(DFSDM_CHCFGR1_DFSDMEN_MASK, v) + +/* CHCFGR2: Channel configuration register 2 */ +#define DFSDM_CHCFGR2_DTRBS_MASK GENMASK(7, 3) +#define DFSDM_CHCFGR2_DTRBS(v) FIELD_PREP(DFSDM_CHCFGR2_DTRBS_MASK, v) +#define DFSDM_CHCFGR2_OFFSET_MASK GENMASK(31, 8) +#define DFSDM_CHCFGR2_OFFSET(v) FIELD_PREP(DFSDM_CHCFGR2_OFFSET_MASK, v) + +/* AWSCDR: Channel analog watchdog and short circuit detector */ +#define DFSDM_AWSCDR_SCDT_MASK GENMASK(7, 0) +#define DFSDM_AWSCDR_SCDT(v) FIELD_PREP(DFSDM_AWSCDR_SCDT_MASK, v) +#define DFSDM_AWSCDR_BKSCD_MASK GENMASK(15, 12) +#define DFSDM_AWSCDR_BKSCD(v) FIELD_PREP(DFSDM_AWSCDR_BKSCD_MASK, v) +#define DFSDM_AWSCDR_AWFOSR_MASK GENMASK(20, 16) +#define DFSDM_AWSCDR_AWFOSR(v) FIELD_PREP(DFSDM_AWSCDR_AWFOSR_MASK, v) +#define DFSDM_AWSCDR_AWFORD_MASK GENMASK(23, 22) +#define DFSDM_AWSCDR_AWFORD(v) FIELD_PREP(DFSDM_AWSCDR_AWFORD_MASK, v) + +/* + * Filters register definitions + */ +#define DFSDM_FILTER_BASE_ADR 0x100 +#define DFSDM_FILTER_REG_MASK 0x7F +#define DFSDM_FILTER_X_BASE_ADR(x) ((x) * 0x80 + DFSDM_FILTER_BASE_ADR) + +#define DFSDM_CR1(x) (DFSDM_FILTER_X_BASE_ADR(x) + 0x00) +#define DFSDM_CR2(x) (DFSDM_FILTER_X_BASE_ADR(x) + 0x04) +#define DFSDM_ISR(x) (DFSDM_FILTER_X_BASE_ADR(x) + 0x08) +#define DFSDM_ICR(x) (DFSDM_FILTER_X_BASE_ADR(x) + 0x0C) +#define DFSDM_JCHGR(x) (DFSDM_FILTER_X_BASE_ADR(x) + 0x10) +#define DFSDM_FCR(x) (DFSDM_FILTER_X_BASE_ADR(x) + 0x14) +#define DFSDM_JDATAR(x) (DFSDM_FILTER_X_BASE_ADR(x) + 0x18) +#define DFSDM_RDATAR(x) (DFSDM_FILTER_X_BASE_ADR(x) + 0x1C) +#define DFSDM_AWHTR(x) (DFSDM_FILTER_X_BASE_ADR(x) + 0x20) +#define DFSDM_AWLTR(x) (DFSDM_FILTER_X_BASE_ADR(x) + 0x24) +#define DFSDM_AWSR(x) (DFSDM_FILTER_X_BASE_ADR(x) + 0x28) +#define DFSDM_AWCFR(x) (DFSDM_FILTER_X_BASE_ADR(x) + 0x2C) +#define DFSDM_EXMAX(x) (DFSDM_FILTER_X_BASE_ADR(x) + 0x30) +#define DFSDM_EXMIN(x) (DFSDM_FILTER_X_BASE_ADR(x) + 0x34) +#define DFSDM_CNVTIMR(x) (DFSDM_FILTER_X_BASE_ADR(x) + 0x38) + +/* CR1 Control register 1 */ +#define DFSDM_CR1_DFEN_MASK BIT(0) +#define DFSDM_CR1_DFEN(v) FIELD_PREP(DFSDM_CR1_DFEN_MASK, v) +#define DFSDM_CR1_JSWSTART_MASK BIT(1) +#define DFSDM_CR1_JSWSTART(v) FIELD_PREP(DFSDM_CR1_JSWSTART_MASK, v) +#define DFSDM_CR1_JSYNC_MASK BIT(3) +#define DFSDM_CR1_JSYNC(v) FIELD_PREP(DFSDM_CR1_JSYNC_MASK, v) +#define DFSDM_CR1_JSCAN_MASK BIT(4) +#define DFSDM_CR1_JSCAN(v) FIELD_PREP(DFSDM_CR1_JSCAN_MASK, v) +#define DFSDM_CR1_JDMAEN_MASK BIT(5) +#define DFSDM_CR1_JDMAEN(v) FIELD_PREP(DFSDM_CR1_JDMAEN_MASK, v) +#define DFSDM_CR1_JEXTSEL_MASK GENMASK(12, 8) +#define DFSDM_CR1_JEXTSEL(v) FIELD_PREP(DFSDM_CR1_JEXTSEL_MASK, v) +#define DFSDM_CR1_JEXTEN_MASK GENMASK(14, 13) +#define DFSDM_CR1_JEXTEN(v) FIELD_PREP(DFSDM_CR1_JEXTEN_MASK, v) +#define DFSDM_CR1_RSWSTART_MASK BIT(17) +#define DFSDM_CR1_RSWSTART(v) FIELD_PREP(DFSDM_CR1_RSWSTART_MASK, v) +#define DFSDM_CR1_RCONT_MASK BIT(18) +#define DFSDM_CR1_RCONT(v) FIELD_PREP(DFSDM_CR1_RCONT_MASK, v) +#define DFSDM_CR1_RSYNC_MASK BIT(19) +#define DFSDM_CR1_RSYNC(v) FIELD_PREP(DFSDM_CR1_RSYNC_MASK, v) +#define DFSDM_CR1_RDMAEN_MASK BIT(21) +#define DFSDM_CR1_RDMAEN(v) FIELD_PREP(DFSDM_CR1_RDMAEN_MASK, v) +#define DFSDM_CR1_RCH_MASK GENMASK(26, 24) +#define DFSDM_CR1_RCH(v) FIELD_PREP(DFSDM_CR1_RCH_MASK, v) +#define DFSDM_CR1_FAST_MASK BIT(29) +#define DFSDM_CR1_FAST(v) FIELD_PREP(DFSDM_CR1_FAST_MASK, v) +#define DFSDM_CR1_AWFSEL_MASK BIT(30) +#define DFSDM_CR1_AWFSEL(v) FIELD_PREP(DFSDM_CR1_AWFSEL_MASK, v) + +/* CR2: Control register 2 */ +#define DFSDM_CR2_IE_MASK GENMASK(6, 0) +#define DFSDM_CR2_IE(v) FIELD_PREP(DFSDM_CR2_IE_MASK, v) +#define DFSDM_CR2_JEOCIE_MASK BIT(0) +#define DFSDM_CR2_JEOCIE(v) FIELD_PREP(DFSDM_CR2_JEOCIE_MASK, v) +#define DFSDM_CR2_REOCIE_MASK BIT(1) +#define DFSDM_CR2_REOCIE(v) FIELD_PREP(DFSDM_CR2_REOCIE_MASK, v) +#define DFSDM_CR2_JOVRIE_MASK BIT(2) +#define DFSDM_CR2_JOVRIE(v) FIELD_PREP(DFSDM_CR2_JOVRIE_MASK, v) +#define DFSDM_CR2_ROVRIE_MASK BIT(3) +#define DFSDM_CR2_ROVRIE(v) FIELD_PREP(DFSDM_CR2_ROVRIE_MASK, v) +#define DFSDM_CR2_AWDIE_MASK BIT(4) +#define DFSDM_CR2_AWDIE(v) FIELD_PREP(DFSDM_CR2_AWDIE_MASK, v) +#define DFSDM_CR2_SCDIE_MASK BIT(5) +#define DFSDM_CR2_SCDIE(v) FIELD_PREP(DFSDM_CR2_SCDIE_MASK, v) +#define DFSDM_CR2_CKABIE_MASK BIT(6) +#define DFSDM_CR2_CKABIE(v) FIELD_PREP(DFSDM_CR2_CKABIE_MASK, v) +#define DFSDM_CR2_EXCH_MASK GENMASK(15, 8) +#define DFSDM_CR2_EXCH(v) FIELD_PREP(DFSDM_CR2_EXCH_MASK, v) +#define DFSDM_CR2_AWDCH_MASK GENMASK(23, 16) +#define DFSDM_CR2_AWDCH(v) FIELD_PREP(DFSDM_CR2_AWDCH_MASK, v) + +/* ISR: Interrupt status register */ +#define DFSDM_ISR_JEOCF_MASK BIT(0) +#define DFSDM_ISR_JEOCF(v) FIELD_PREP(DFSDM_ISR_JEOCF_MASK, v) +#define DFSDM_ISR_REOCF_MASK BIT(1) +#define DFSDM_ISR_REOCF(v) FIELD_PREP(DFSDM_ISR_REOCF_MASK, v) +#define DFSDM_ISR_JOVRF_MASK BIT(2) +#define DFSDM_ISR_JOVRF(v) FIELD_PREP(DFSDM_ISR_JOVRF_MASK, v) +#define DFSDM_ISR_ROVRF_MASK BIT(3) +#define DFSDM_ISR_ROVRF(v) FIELD_PREP(DFSDM_ISR_ROVRF_MASK, v) +#define DFSDM_ISR_AWDF_MASK BIT(4) +#define DFSDM_ISR_AWDF(v) FIELD_PREP(DFSDM_ISR_AWDF_MASK, v) +#define DFSDM_ISR_JCIP_MASK BIT(13) +#define DFSDM_ISR_JCIP(v) FIELD_PREP(DFSDM_ISR_JCIP_MASK, v) +#define DFSDM_ISR_RCIP_MASK BIT(14) +#define DFSDM_ISR_RCIP(v) FIELD_PREP(DFSDM_ISR_RCIP, v) +#define DFSDM_ISR_CKABF_MASK GENMASK(23, 16) +#define DFSDM_ISR_CKABF(v) FIELD_PREP(DFSDM_ISR_CKABF_MASK, v) +#define DFSDM_ISR_SCDF_MASK GENMASK(31, 24) +#define DFSDM_ISR_SCDF(v) FIELD_PREP(DFSDM_ISR_SCDF_MASK, v) + +/* ICR: Interrupt flag clear register */ +#define DFSDM_ICR_CLRJOVRF_MASK BIT(2) +#define DFSDM_ICR_CLRJOVRF(v) FIELD_PREP(DFSDM_ICR_CLRJOVRF_MASK, v) +#define DFSDM_ICR_CLRROVRF_MASK BIT(3) +#define DFSDM_ICR_CLRROVRF(v) FIELD_PREP(DFSDM_ICR_CLRROVRF_MASK, v) +#define DFSDM_ICR_CLRCKABF_MASK GENMASK(23, 16) +#define DFSDM_ICR_CLRCKABF(v) FIELD_PREP(DFSDM_ICR_CLRCKABF_MASK, v) +#define DFSDM_ICR_CLRCKABF_CH_MASK(y) BIT(16 + (y)) +#define DFSDM_ICR_CLRCKABF_CH(v, y) \ + (((v) << (16 + (y))) & DFSDM_ICR_CLRCKABF_CH_MASK(y)) +#define DFSDM_ICR_CLRSCDF_MASK GENMASK(31, 24) +#define DFSDM_ICR_CLRSCDF(v) FIELD_PREP(DFSDM_ICR_CLRSCDF_MASK, v) +#define DFSDM_ICR_CLRSCDF_CH_MASK(y) BIT(24 + (y)) +#define DFSDM_ICR_CLRSCDF_CH(v, y) \ + (((v) << (24 + (y))) & DFSDM_ICR_CLRSCDF_MASK(y)) + +/* FCR: Filter control register */ +#define DFSDM_FCR_IOSR_MASK GENMASK(7, 0) +#define DFSDM_FCR_IOSR(v) FIELD_PREP(DFSDM_FCR_IOSR_MASK, v) +#define DFSDM_FCR_FOSR_MASK GENMASK(25, 16) +#define DFSDM_FCR_FOSR(v) FIELD_PREP(DFSDM_FCR_FOSR_MASK, v) +#define DFSDM_FCR_FORD_MASK GENMASK(31, 29) +#define DFSDM_FCR_FORD(v) FIELD_PREP(DFSDM_FCR_FORD_MASK, v) + +/* RDATAR: Filter data register for regular channel */ +#define DFSDM_DATAR_CH_MASK GENMASK(2, 0) +#define DFSDM_DATAR_DATA_OFFSET 8 +#define DFSDM_DATAR_DATA_MASK GENMASK(31, DFSDM_DATAR_DATA_OFFSET) + +/* AWLTR: Filter analog watchdog low threshold register */ +#define DFSDM_AWLTR_BKAWL_MASK GENMASK(3, 0) +#define DFSDM_AWLTR_BKAWL(v) FIELD_PREP(DFSDM_AWLTR_BKAWL_MASK, v) +#define DFSDM_AWLTR_AWLT_MASK GENMASK(31, 8) +#define DFSDM_AWLTR_AWLT(v) FIELD_PREP(DFSDM_AWLTR_AWLT_MASK, v) + +/* AWHTR: Filter analog watchdog low threshold register */ +#define DFSDM_AWHTR_BKAWH_MASK GENMASK(3, 0) +#define DFSDM_AWHTR_BKAWH(v) FIELD_PREP(DFSDM_AWHTR_BKAWH_MASK, v) +#define DFSDM_AWHTR_AWHT_MASK GENMASK(31, 8) +#define DFSDM_AWHTR_AWHT(v) FIELD_PREP(DFSDM_AWHTR_AWHT_MASK, v) + +/* AWSR: Filter watchdog status register */ +#define DFSDM_AWSR_AWLTF_MASK GENMASK(7, 0) +#define DFSDM_AWSR_AWLTF(v) FIELD_PREP(DFSDM_AWSR_AWLTF_MASK, v) +#define DFSDM_AWSR_AWHTF_MASK GENMASK(15, 8) +#define DFSDM_AWSR_AWHTF(v) FIELD_PREP(DFSDM_AWSR_AWHTF_MASK, v) + +/* AWCFR: Filter watchdog status register */ +#define DFSDM_AWCFR_AWLTF_MASK GENMASK(7, 0) +#define DFSDM_AWCFR_AWLTF(v) FIELD_PREP(DFSDM_AWCFR_AWLTF_MASK, v) +#define DFSDM_AWCFR_AWHTF_MASK GENMASK(15, 8) +#define DFSDM_AWCFR_AWHTF(v) FIELD_PREP(DFSDM_AWCFR_AWHTF_MASK, v) + +/* + * Identification register definitions + */ +#define DFSDM_HWCFGR 0x7F0 +#define DFSDM_VERR 0x7F4 +#define DFSDM_IPIDR 0x7F8 +#define DFSDM_SIDR 0x7FC + +/* HWCFGR: Hardware configuration register */ +#define DFSDM_HWCFGR_NBT_MASK GENMASK(7, 0) +#define DFSDM_HWCFGR_NBF_MASK GENMASK(15, 8) + +/* VERR: Version register */ +#define DFSDM_VERR_MINREV_MASK GENMASK(3, 0) +#define DFSDM_VERR_MAJREV_MASK GENMASK(7, 4) + +#define STM32MP15_IPIDR_NUMBER 0x00110031 + +/* DFSDM filter order */ +enum stm32_dfsdm_sinc_order { + DFSDM_FASTSINC_ORDER, /* FastSinc filter type */ + DFSDM_SINC1_ORDER, /* Sinc 1 filter type */ + DFSDM_SINC2_ORDER, /* Sinc 2 filter type */ + DFSDM_SINC3_ORDER, /* Sinc 3 filter type */ + DFSDM_SINC4_ORDER, /* Sinc 4 filter type (N.A. for watchdog) */ + DFSDM_SINC5_ORDER, /* Sinc 5 filter type (N.A. for watchdog) */ + DFSDM_NB_SINC_ORDER, +}; + +/** + * struct stm32_dfsdm_filter_osr - DFSDM filter settings linked to oversampling + * @iosr: integrator oversampling + * @fosr: filter oversampling + * @rshift: output sample right shift (hardware shift) + * @lshift: output sample left shift (software shift) + * @res: output sample resolution + * @bits: output sample resolution in bits + * @max: output sample maximum positive value + */ +struct stm32_dfsdm_filter_osr { + unsigned int iosr; + unsigned int fosr; + unsigned int rshift; + unsigned int lshift; + u64 res; + u32 bits; + s32 max; +}; + +/** + * struct stm32_dfsdm_filter - structure relative to stm32 FDSDM filter + * @ford: filter order + * @flo: filter oversampling data table indexed by fast mode flag + * @sync_mode: filter synchronized with filter 0 + * @fast: filter fast mode + */ +struct stm32_dfsdm_filter { + enum stm32_dfsdm_sinc_order ford; + struct stm32_dfsdm_filter_osr flo[2]; + unsigned int sync_mode; + unsigned int fast; +}; + +/** + * struct stm32_dfsdm_channel - structure relative to stm32 FDSDM channel + * @id: id of the channel + * @type: interface type linked to stm32_dfsdm_chan_type + * @src: interface type linked to stm32_dfsdm_chan_src + * @alt_si: alternative serial input interface + */ +struct stm32_dfsdm_channel { + unsigned int id; + unsigned int type; + unsigned int src; + unsigned int alt_si; +}; + +/** + * struct stm32_dfsdm - stm32 FDSDM driver common data (for all instances) + * @base: control registers base cpu addr + * @phys_base: DFSDM IP register physical address + * @regmap: regmap for register read/write + * @fl_list: filter resources list + * @num_fls: number of filter resources available + * @ch_list: channel resources list + * @num_chs: number of channel resources available + * @spi_master_freq: SPI clock out frequency + */ +struct stm32_dfsdm { + void __iomem *base; + phys_addr_t phys_base; + struct regmap *regmap; + struct stm32_dfsdm_filter *fl_list; + unsigned int num_fls; + struct stm32_dfsdm_channel *ch_list; + unsigned int num_chs; + unsigned int spi_master_freq; +}; + +/* DFSDM channel serial spi clock source */ +enum stm32_dfsdm_spi_clk_src { + DFSDM_CHANNEL_SPI_CLOCK_EXTERNAL, + DFSDM_CHANNEL_SPI_CLOCK_INTERNAL, + DFSDM_CHANNEL_SPI_CLOCK_INTERNAL_DIV2_FALLING, + DFSDM_CHANNEL_SPI_CLOCK_INTERNAL_DIV2_RISING +}; + +int stm32_dfsdm_start_dfsdm(struct stm32_dfsdm *dfsdm); +int stm32_dfsdm_stop_dfsdm(struct stm32_dfsdm *dfsdm); + +#endif diff --git a/drivers/iio/adc/stmpe-adc.c b/drivers/iio/adc/stmpe-adc.c new file mode 100644 index 000000000..8e26c47ed --- /dev/null +++ b/drivers/iio/adc/stmpe-adc.c @@ -0,0 +1,365 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * STMicroelectronics STMPE811 IIO ADC Driver + * + * 4 channel, 10/12-bit ADC + * + * Copyright (C) 2013-2018 Toradex AG + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define STMPE_REG_INT_STA 0x0B +#define STMPE_REG_ADC_INT_EN 0x0E +#define STMPE_REG_ADC_INT_STA 0x0F + +#define STMPE_REG_ADC_CTRL1 0x20 +#define STMPE_REG_ADC_CTRL2 0x21 +#define STMPE_REG_ADC_CAPT 0x22 +#define STMPE_REG_ADC_DATA_CH(channel) (0x30 + 2 * (channel)) + +#define STMPE_REG_TEMP_CTRL 0x60 +#define STMPE_TEMP_CTRL_ENABLE BIT(0) +#define STMPE_TEMP_CTRL_ACQ BIT(1) +#define STMPE_TEMP_CTRL_THRES_EN BIT(3) +#define STMPE_START_ONE_TEMP_CONV (STMPE_TEMP_CTRL_ENABLE | \ + STMPE_TEMP_CTRL_ACQ | \ + STMPE_TEMP_CTRL_THRES_EN) +#define STMPE_REG_TEMP_DATA 0x61 +#define STMPE_REG_TEMP_TH 0x63 +#define STMPE_ADC_LAST_NR 7 +#define STMPE_TEMP_CHANNEL (STMPE_ADC_LAST_NR + 1) + +#define STMPE_ADC_CH(channel) ((1 << (channel)) & 0xff) + +#define STMPE_ADC_TIMEOUT msecs_to_jiffies(1000) + +struct stmpe_adc { + struct stmpe *stmpe; + struct clk *clk; + struct device *dev; + struct mutex lock; + + /* We are allocating plus one for the temperature channel */ + struct iio_chan_spec stmpe_adc_iio_channels[STMPE_ADC_LAST_NR + 2]; + + struct completion completion; + + u8 channel; + u32 value; +}; + +static int stmpe_read_voltage(struct stmpe_adc *info, + struct iio_chan_spec const *chan, int *val) +{ + unsigned long ret; + + mutex_lock(&info->lock); + + reinit_completion(&info->completion); + + info->channel = (u8)chan->channel; + + if (info->channel > STMPE_ADC_LAST_NR) { + mutex_unlock(&info->lock); + return -EINVAL; + } + + stmpe_reg_write(info->stmpe, STMPE_REG_ADC_CAPT, + STMPE_ADC_CH(info->channel)); + + ret = wait_for_completion_timeout(&info->completion, STMPE_ADC_TIMEOUT); + + if (ret == 0) { + stmpe_reg_write(info->stmpe, STMPE_REG_ADC_INT_STA, + STMPE_ADC_CH(info->channel)); + mutex_unlock(&info->lock); + return -ETIMEDOUT; + } + + *val = info->value; + + mutex_unlock(&info->lock); + + return 0; +} + +static int stmpe_read_temp(struct stmpe_adc *info, + struct iio_chan_spec const *chan, int *val) +{ + unsigned long ret; + + mutex_lock(&info->lock); + + reinit_completion(&info->completion); + + info->channel = (u8)chan->channel; + + if (info->channel != STMPE_TEMP_CHANNEL) { + mutex_unlock(&info->lock); + return -EINVAL; + } + + stmpe_reg_write(info->stmpe, STMPE_REG_TEMP_CTRL, + STMPE_START_ONE_TEMP_CONV); + + ret = wait_for_completion_timeout(&info->completion, STMPE_ADC_TIMEOUT); + + if (ret == 0) { + mutex_unlock(&info->lock); + return -ETIMEDOUT; + } + + /* + * absolute temp = +V3.3 * value /7.51 [K] + * scale to [milli °C] + */ + *val = ((449960l * info->value) / 1024l) - 273150; + + mutex_unlock(&info->lock); + + return 0; +} + +static int stmpe_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, + int *val2, + long mask) +{ + struct stmpe_adc *info = iio_priv(indio_dev); + long ret; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + case IIO_CHAN_INFO_PROCESSED: + + switch (chan->type) { + case IIO_VOLTAGE: + ret = stmpe_read_voltage(info, chan, val); + break; + + case IIO_TEMP: + ret = stmpe_read_temp(info, chan, val); + break; + default: + return -EINVAL; + } + + if (ret < 0) + return ret; + + return IIO_VAL_INT; + + case IIO_CHAN_INFO_SCALE: + *val = 3300; + *val2 = info->stmpe->mod_12b ? 12 : 10; + return IIO_VAL_FRACTIONAL_LOG2; + + default: + break; + } + + return -EINVAL; +} + +static irqreturn_t stmpe_adc_isr(int irq, void *dev_id) +{ + struct stmpe_adc *info = (struct stmpe_adc *)dev_id; + __be16 data; + + if (info->channel <= STMPE_ADC_LAST_NR) { + int int_sta; + + int_sta = stmpe_reg_read(info->stmpe, STMPE_REG_ADC_INT_STA); + + /* Is the interrupt relevant */ + if (!(int_sta & STMPE_ADC_CH(info->channel))) + return IRQ_NONE; + + /* Read value */ + stmpe_block_read(info->stmpe, + STMPE_REG_ADC_DATA_CH(info->channel), 2, (u8 *) &data); + + stmpe_reg_write(info->stmpe, STMPE_REG_ADC_INT_STA, int_sta); + } else if (info->channel == STMPE_TEMP_CHANNEL) { + /* Read value */ + stmpe_block_read(info->stmpe, STMPE_REG_TEMP_DATA, 2, + (u8 *) &data); + } else { + return IRQ_NONE; + } + + info->value = (u32) be16_to_cpu(data); + complete(&info->completion); + + return IRQ_HANDLED; +} + +static const struct iio_info stmpe_adc_iio_info = { + .read_raw = &stmpe_read_raw, +}; + +static void stmpe_adc_voltage_chan(struct iio_chan_spec *ics, int chan) +{ + ics->type = IIO_VOLTAGE; + ics->info_mask_separate = BIT(IIO_CHAN_INFO_RAW); + ics->info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE); + ics->indexed = 1; + ics->channel = chan; +} + +static void stmpe_adc_temp_chan(struct iio_chan_spec *ics, int chan) +{ + ics->type = IIO_TEMP; + ics->info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED); + ics->indexed = 1; + ics->channel = chan; +} + +static int stmpe_adc_init_hw(struct stmpe_adc *adc) +{ + int ret; + struct stmpe *stmpe = adc->stmpe; + + ret = stmpe_enable(stmpe, STMPE_BLOCK_ADC); + if (ret) { + dev_err(stmpe->dev, "Could not enable clock for ADC\n"); + return ret; + } + + ret = stmpe811_adc_common_init(stmpe); + if (ret) { + stmpe_disable(stmpe, STMPE_BLOCK_ADC); + return ret; + } + + /* use temp irq for each conversion completion */ + stmpe_reg_write(stmpe, STMPE_REG_TEMP_TH, 0); + stmpe_reg_write(stmpe, STMPE_REG_TEMP_TH + 1, 0); + + return 0; +} + +static int stmpe_adc_probe(struct platform_device *pdev) +{ + struct iio_dev *indio_dev; + struct stmpe_adc *info; + struct device_node *np; + u32 norequest_mask = 0; + unsigned long bits; + int irq_temp, irq_adc; + int num_chan = 0; + int i = 0; + int ret; + + irq_adc = platform_get_irq_byname(pdev, "STMPE_ADC"); + if (irq_adc < 0) + return irq_adc; + + indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(struct stmpe_adc)); + if (!indio_dev) + return -ENOMEM; + + info = iio_priv(indio_dev); + mutex_init(&info->lock); + + init_completion(&info->completion); + ret = devm_request_threaded_irq(&pdev->dev, irq_adc, NULL, + stmpe_adc_isr, IRQF_ONESHOT, + "stmpe-adc", info); + if (ret < 0) { + dev_err(&pdev->dev, "failed requesting irq, irq = %d\n", + irq_adc); + return ret; + } + + irq_temp = platform_get_irq_byname(pdev, "STMPE_TEMP_SENS"); + if (irq_temp >= 0) { + ret = devm_request_threaded_irq(&pdev->dev, irq_temp, NULL, + stmpe_adc_isr, IRQF_ONESHOT, + "stmpe-adc", info); + if (ret < 0) + dev_warn(&pdev->dev, "failed requesting irq for" + " temp sensor, irq = %d\n", irq_temp); + } + + platform_set_drvdata(pdev, indio_dev); + + indio_dev->name = dev_name(&pdev->dev); + indio_dev->info = &stmpe_adc_iio_info; + indio_dev->modes = INDIO_DIRECT_MODE; + + info->stmpe = dev_get_drvdata(pdev->dev.parent); + + np = pdev->dev.of_node; + + if (!np) + dev_err(&pdev->dev, "no device tree node found\n"); + + of_property_read_u32(np, "st,norequest-mask", &norequest_mask); + + bits = norequest_mask; + for_each_clear_bit(i, &bits, (STMPE_ADC_LAST_NR + 1)) { + stmpe_adc_voltage_chan(&info->stmpe_adc_iio_channels[num_chan], i); + num_chan++; + } + stmpe_adc_temp_chan(&info->stmpe_adc_iio_channels[num_chan], i); + num_chan++; + indio_dev->channels = info->stmpe_adc_iio_channels; + indio_dev->num_channels = num_chan; + + ret = stmpe_adc_init_hw(info); + if (ret) + return ret; + + stmpe_reg_write(info->stmpe, STMPE_REG_ADC_INT_EN, + ~(norequest_mask & 0xFF)); + + stmpe_reg_write(info->stmpe, STMPE_REG_ADC_INT_STA, + ~(norequest_mask & 0xFF)); + + return devm_iio_device_register(&pdev->dev, indio_dev); +} + +static int stmpe_adc_resume(struct device *dev) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct stmpe_adc *info = iio_priv(indio_dev); + + stmpe_adc_init_hw(info); + + return 0; +} + +static DEFINE_SIMPLE_DEV_PM_OPS(stmpe_adc_pm_ops, NULL, stmpe_adc_resume); + +static const struct of_device_id stmpe_adc_ids[] = { + { .compatible = "st,stmpe-adc", }, + { } +}; +MODULE_DEVICE_TABLE(of, stmpe_adc_ids); + +static struct platform_driver stmpe_adc_driver = { + .probe = stmpe_adc_probe, + .driver = { + .name = "stmpe-adc", + .pm = pm_sleep_ptr(&stmpe_adc_pm_ops), + .of_match_table = stmpe_adc_ids, + }, +}; +module_platform_driver(stmpe_adc_driver); + +MODULE_AUTHOR("Stefan Agner "); +MODULE_DESCRIPTION("STMPEXXX ADC driver"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:stmpe-adc"); diff --git a/drivers/iio/adc/sun20i-gpadc-iio.c b/drivers/iio/adc/sun20i-gpadc-iio.c new file mode 100644 index 000000000..baa7661db --- /dev/null +++ b/drivers/iio/adc/sun20i-gpadc-iio.c @@ -0,0 +1,279 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * GPADC driver for sunxi platforms (D1, T113-S3 and R329) + * Copyright (c) 2023 Maksim Kiselev + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#define SUN20I_GPADC_DRIVER_NAME "sun20i-gpadc" + +/* Register map definition */ +#define SUN20I_GPADC_SR 0x00 +#define SUN20I_GPADC_CTRL 0x04 +#define SUN20I_GPADC_CS_EN 0x08 +#define SUN20I_GPADC_FIFO_INTC 0x0c +#define SUN20I_GPADC_FIFO_INTS 0x10 +#define SUN20I_GPADC_FIFO_DATA 0X14 +#define SUN20I_GPADC_CB_DATA 0X18 +#define SUN20I_GPADC_DATAL_INTC 0x20 +#define SUN20I_GPADC_DATAH_INTC 0x24 +#define SUN20I_GPADC_DATA_INTC 0x28 +#define SUN20I_GPADC_DATAL_INTS 0x30 +#define SUN20I_GPADC_DATAH_INTS 0x34 +#define SUN20I_GPADC_DATA_INTS 0x38 +#define SUN20I_GPADC_CH_CMP_DATA(x) (0x40 + (x) * 4) +#define SUN20I_GPADC_CH_DATA(x) (0x80 + (x) * 4) + +#define SUN20I_GPADC_CTRL_ADC_AUTOCALI_EN_MASK BIT(23) +#define SUN20I_GPADC_CTRL_WORK_MODE_MASK GENMASK(19, 18) +#define SUN20I_GPADC_CTRL_ADC_EN_MASK BIT(16) +#define SUN20I_GPADC_CS_EN_ADC_CH(x) BIT(x) +#define SUN20I_GPADC_DATA_INTC_CH_DATA_IRQ_EN(x) BIT(x) + +#define SUN20I_GPADC_WORK_MODE_SINGLE 0 + +struct sun20i_gpadc_iio { + void __iomem *regs; + struct completion completion; + int last_channel; + /* + * Lock to protect the device state during a potential concurrent + * read access from userspace. Reading a raw value requires a sequence + * of register writes, then a wait for a completion callback, + * and finally a register read, during which userspace could issue + * another read request. This lock protects a read access from + * occurring before another one has finished. + */ + struct mutex lock; +}; + +static int sun20i_gpadc_adc_read(struct sun20i_gpadc_iio *info, + struct iio_chan_spec const *chan, int *val) +{ + u32 ctrl; + int ret = IIO_VAL_INT; + + mutex_lock(&info->lock); + + reinit_completion(&info->completion); + + if (info->last_channel != chan->channel) { + info->last_channel = chan->channel; + + /* enable the analog input channel */ + writel(SUN20I_GPADC_CS_EN_ADC_CH(chan->channel), + info->regs + SUN20I_GPADC_CS_EN); + + /* enable the data irq for input channel */ + writel(SUN20I_GPADC_DATA_INTC_CH_DATA_IRQ_EN(chan->channel), + info->regs + SUN20I_GPADC_DATA_INTC); + } + + /* enable the ADC function */ + ctrl = readl(info->regs + SUN20I_GPADC_CTRL); + ctrl |= FIELD_PREP(SUN20I_GPADC_CTRL_ADC_EN_MASK, 1); + writel(ctrl, info->regs + SUN20I_GPADC_CTRL); + + /* + * According to the datasheet maximum acquire time(TACQ) can be + * (65535+1)/24Mhz and conversion time(CONV_TIME) is always constant + * and equal to 14/24Mhz, so (TACQ+CONV_TIME) <= 2.73125ms. + * A 10ms delay should be enough to make sure an interrupt occurs in + * normal conditions. If it doesn't occur, then there is a timeout. + */ + if (!wait_for_completion_timeout(&info->completion, msecs_to_jiffies(10))) { + ret = -ETIMEDOUT; + goto err_unlock; + } + + /* read the ADC data */ + *val = readl(info->regs + SUN20I_GPADC_CH_DATA(chan->channel)); + +err_unlock: + mutex_unlock(&info->lock); + + return ret; +} + +static int sun20i_gpadc_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int *val, + int *val2, long mask) +{ + struct sun20i_gpadc_iio *info = iio_priv(indio_dev); + + switch (mask) { + case IIO_CHAN_INFO_RAW: + return sun20i_gpadc_adc_read(info, chan, val); + case IIO_CHAN_INFO_SCALE: + /* value in mv = 1800mV / 4096 raw */ + *val = 1800; + *val2 = 12; + return IIO_VAL_FRACTIONAL_LOG2; + default: + return -EINVAL; + } +} + +static irqreturn_t sun20i_gpadc_irq_handler(int irq, void *data) +{ + struct sun20i_gpadc_iio *info = data; + + /* clear data interrupt status register */ + writel(GENMASK(31, 0), info->regs + SUN20I_GPADC_DATA_INTS); + + complete(&info->completion); + + return IRQ_HANDLED; +} + +static int +sun20i_gpadc_fwnode_xlate(struct iio_dev *indio_dev, + const struct fwnode_reference_args *iiospec) +{ + if (iiospec->nargs != 1) + return -EINVAL; + + for (unsigned int i = 0; i < indio_dev->num_channels; i++) + if (indio_dev->channels[i].channel == iiospec->args[0]) + return i; + + return -EINVAL; +} + +static const struct iio_info sun20i_gpadc_iio_info = { + .read_raw = sun20i_gpadc_read_raw, + .fwnode_xlate = sun20i_gpadc_fwnode_xlate, +}; + +static void sun20i_gpadc_reset_assert(void *data) +{ + struct reset_control *rst = data; + + reset_control_assert(rst); +} + +static const struct iio_chan_spec sun20i_gpadc_chan_template = { + .type = IIO_VOLTAGE, + .indexed = 1, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), +}; + +static int sun20i_gpadc_alloc_channels(struct iio_dev *indio_dev, + struct device *dev) +{ + int num_channels; + struct iio_chan_spec *channels; + + num_channels = devm_iio_adc_device_alloc_chaninfo_se(dev, + &sun20i_gpadc_chan_template, -1, &channels); + if (num_channels < 0) + return num_channels; + + indio_dev->channels = channels; + indio_dev->num_channels = num_channels; + + return 0; +} + +static int sun20i_gpadc_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct sun20i_gpadc_iio *info; + struct clk_bulk_data *clks; + struct reset_control *rst; + struct iio_dev *indio_dev; + int irq; + int ret; + + indio_dev = devm_iio_device_alloc(dev, sizeof(*info)); + if (!indio_dev) + return -ENOMEM; + + info = iio_priv(indio_dev); + info->last_channel = -1; + + mutex_init(&info->lock); + init_completion(&info->completion); + + ret = sun20i_gpadc_alloc_channels(indio_dev, dev); + if (ret) + return ret; + + indio_dev->info = &sun20i_gpadc_iio_info; + indio_dev->name = SUN20I_GPADC_DRIVER_NAME; + + info->regs = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(info->regs)) + return PTR_ERR(info->regs); + + ret = devm_clk_bulk_get_all_enabled(dev, &clks); + if (ret < 0) + return dev_err_probe(dev, ret, "failed to enable clocks\n"); + if (ret == 0) + return dev_err_probe(dev, -ENODEV, "needs at least one clocks\n"); + + rst = devm_reset_control_get_exclusive(dev, NULL); + if (IS_ERR(rst)) + return dev_err_probe(dev, PTR_ERR(rst), "failed to get reset control\n"); + + ret = reset_control_deassert(rst); + if (ret) + return dev_err_probe(dev, ret, "failed to deassert reset\n"); + + ret = devm_add_action_or_reset(dev, sun20i_gpadc_reset_assert, rst); + if (ret) + return ret; + + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return irq; + + ret = devm_request_irq(dev, irq, sun20i_gpadc_irq_handler, 0, + dev_name(dev), info); + if (ret) + return dev_err_probe(dev, ret, "failed requesting irq %d\n", irq); + + writel(FIELD_PREP(SUN20I_GPADC_CTRL_ADC_AUTOCALI_EN_MASK, 1) | + FIELD_PREP(SUN20I_GPADC_CTRL_WORK_MODE_MASK, SUN20I_GPADC_WORK_MODE_SINGLE), + info->regs + SUN20I_GPADC_CTRL); + + ret = devm_iio_device_register(dev, indio_dev); + if (ret) + return dev_err_probe(dev, ret, "could not register the device\n"); + + return 0; +} + +static const struct of_device_id sun20i_gpadc_of_id[] = { + { .compatible = "allwinner,sun20i-d1-gpadc" }, + { .compatible = "allwinner,sun55i-a523-gpadc" }, + { } +}; +MODULE_DEVICE_TABLE(of, sun20i_gpadc_of_id); + +static struct platform_driver sun20i_gpadc_driver = { + .driver = { + .name = SUN20I_GPADC_DRIVER_NAME, + .of_match_table = sun20i_gpadc_of_id, + }, + .probe = sun20i_gpadc_probe, +}; +module_platform_driver(sun20i_gpadc_driver); + +MODULE_DESCRIPTION("ADC driver for sunxi platforms"); +MODULE_AUTHOR("Maksim Kiselev "); +MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS("IIO_DRIVER"); diff --git a/drivers/iio/adc/sun4i-gpadc-iio.c b/drivers/iio/adc/sun4i-gpadc-iio.c new file mode 100644 index 000000000..203459ca9 --- /dev/null +++ b/drivers/iio/adc/sun4i-gpadc-iio.c @@ -0,0 +1,705 @@ +// SPDX-License-Identifier: GPL-2.0 +/* ADC driver for sunxi platforms' (A10, A13 and A31) GPADC + * + * Copyright (c) 2016 Quentin Schulz + * + * The Allwinner SoCs all have an ADC that can also act as a touchscreen + * controller and a thermal sensor. + * The thermal sensor works only when the ADC acts as a touchscreen controller + * and is configured to throw an interrupt every fixed periods of time (let say + * every X seconds). + * One would be tempted to disable the IP on the hardware side rather than + * disabling interrupts to save some power but that resets the internal clock of + * the IP, resulting in having to wait X seconds every time we want to read the + * value of the thermal sensor. + * This is also the reason of using autosuspend in pm_runtime. If there was no + * autosuspend, the thermal sensor would need X seconds after every + * pm_runtime_get_sync to get a value from the ADC. The autosuspend allows the + * thermal sensor to be requested again in a certain time span before it gets + * shutdown for not being used. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +static unsigned int sun4i_gpadc_chan_select(unsigned int chan) +{ + return SUN4I_GPADC_CTRL1_ADC_CHAN_SELECT(chan); +} + +static unsigned int sun6i_gpadc_chan_select(unsigned int chan) +{ + return SUN6I_GPADC_CTRL1_ADC_CHAN_SELECT(chan); +} + +struct gpadc_data { + int temp_offset; + int temp_scale; + unsigned int tp_mode_en; + unsigned int tp_adc_select; + unsigned int (*adc_chan_select)(unsigned int chan); + unsigned int adc_chan_mask; +}; + +static const struct gpadc_data sun4i_gpadc_data = { + .temp_offset = -1932, + .temp_scale = 133, + .tp_mode_en = SUN4I_GPADC_CTRL1_TP_MODE_EN, + .tp_adc_select = SUN4I_GPADC_CTRL1_TP_ADC_SELECT, + .adc_chan_select = &sun4i_gpadc_chan_select, + .adc_chan_mask = SUN4I_GPADC_CTRL1_ADC_CHAN_MASK, +}; + +static const struct gpadc_data sun5i_gpadc_data = { + .temp_offset = -1447, + .temp_scale = 100, + .tp_mode_en = SUN4I_GPADC_CTRL1_TP_MODE_EN, + .tp_adc_select = SUN4I_GPADC_CTRL1_TP_ADC_SELECT, + .adc_chan_select = &sun4i_gpadc_chan_select, + .adc_chan_mask = SUN4I_GPADC_CTRL1_ADC_CHAN_MASK, +}; + +static const struct gpadc_data sun6i_gpadc_data = { + .temp_offset = -1623, + .temp_scale = 167, + .tp_mode_en = SUN6I_GPADC_CTRL1_TP_MODE_EN, + .tp_adc_select = SUN6I_GPADC_CTRL1_TP_ADC_SELECT, + .adc_chan_select = &sun6i_gpadc_chan_select, + .adc_chan_mask = SUN6I_GPADC_CTRL1_ADC_CHAN_MASK, +}; + +static const struct gpadc_data sun8i_a33_gpadc_data = { + .temp_offset = -1662, + .temp_scale = 162, + .tp_mode_en = SUN8I_GPADC_CTRL1_CHOP_TEMP_EN, +}; + +struct sun4i_gpadc_iio { + struct iio_dev *indio_dev; + struct completion completion; + int temp_data; + u32 adc_data; + struct regmap *regmap; + unsigned int fifo_data_irq; + atomic_t ignore_fifo_data_irq; + unsigned int temp_data_irq; + atomic_t ignore_temp_data_irq; + const struct gpadc_data *data; + bool no_irq; + /* prevents concurrent reads of temperature and ADC */ + struct mutex mutex; + struct thermal_zone_device *tzd; + struct device *sensor_device; +}; + +#define SUN4I_GPADC_ADC_CHANNEL(_channel, _name) { \ + .type = IIO_VOLTAGE, \ + .indexed = 1, \ + .channel = _channel, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ + .datasheet_name = _name, \ +} + +static const struct iio_map sun4i_gpadc_hwmon_maps[] = { + IIO_MAP("temp_adc", "iio_hwmon.0", NULL), + { } +}; + +static const struct iio_chan_spec sun4i_gpadc_channels[] = { + SUN4I_GPADC_ADC_CHANNEL(0, "adc_chan0"), + SUN4I_GPADC_ADC_CHANNEL(1, "adc_chan1"), + SUN4I_GPADC_ADC_CHANNEL(2, "adc_chan2"), + SUN4I_GPADC_ADC_CHANNEL(3, "adc_chan3"), + { + .type = IIO_TEMP, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SCALE) | + BIT(IIO_CHAN_INFO_OFFSET), + .datasheet_name = "temp_adc", + }, +}; + +static const struct iio_chan_spec sun4i_gpadc_channels_no_temp[] = { + SUN4I_GPADC_ADC_CHANNEL(0, "adc_chan0"), + SUN4I_GPADC_ADC_CHANNEL(1, "adc_chan1"), + SUN4I_GPADC_ADC_CHANNEL(2, "adc_chan2"), + SUN4I_GPADC_ADC_CHANNEL(3, "adc_chan3"), +}; + +static const struct iio_chan_spec sun8i_a33_gpadc_channels[] = { + { + .type = IIO_TEMP, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SCALE) | + BIT(IIO_CHAN_INFO_OFFSET), + .datasheet_name = "temp_adc", + }, +}; + +static const struct regmap_config sun4i_gpadc_regmap_config = { + .reg_bits = 32, + .val_bits = 32, + .reg_stride = 4, +}; + +static int sun4i_prepare_for_irq(struct iio_dev *indio_dev, int channel, + unsigned int irq) +{ + struct sun4i_gpadc_iio *info = iio_priv(indio_dev); + int ret; + u32 reg; + + pm_runtime_get_sync(indio_dev->dev.parent); + + reinit_completion(&info->completion); + + ret = regmap_write(info->regmap, SUN4I_GPADC_INT_FIFOC, + SUN4I_GPADC_INT_FIFOC_TP_FIFO_TRIG_LEVEL(1) | + SUN4I_GPADC_INT_FIFOC_TP_FIFO_FLUSH); + if (ret) + return ret; + + ret = regmap_read(info->regmap, SUN4I_GPADC_CTRL1, ®); + if (ret) + return ret; + + if (irq == info->fifo_data_irq) { + ret = regmap_write(info->regmap, SUN4I_GPADC_CTRL1, + info->data->tp_mode_en | + info->data->tp_adc_select | + info->data->adc_chan_select(channel)); + /* + * When the IP changes channel, it needs a bit of time to get + * correct values. + */ + if ((reg & info->data->adc_chan_mask) != + info->data->adc_chan_select(channel)) + mdelay(10); + + } else { + /* + * The temperature sensor returns valid data only when the ADC + * operates in touchscreen mode. + */ + ret = regmap_write(info->regmap, SUN4I_GPADC_CTRL1, + info->data->tp_mode_en); + } + + if (ret) + return ret; + + /* + * When the IP changes mode between ADC or touchscreen, it + * needs a bit of time to get correct values. + */ + if ((reg & info->data->tp_adc_select) != info->data->tp_adc_select) + mdelay(100); + + return 0; +} + +static int sun4i_gpadc_read(struct iio_dev *indio_dev, int channel, int *val, + unsigned int irq) +{ + struct sun4i_gpadc_iio *info = iio_priv(indio_dev); + int ret; + + mutex_lock(&info->mutex); + + ret = sun4i_prepare_for_irq(indio_dev, channel, irq); + if (ret) + goto err; + + enable_irq(irq); + + /* + * The temperature sensor throws an interruption periodically (currently + * set at periods of ~0.6s in sun4i_gpadc_runtime_resume). A 1s delay + * makes sure an interruption occurs in normal conditions. If it doesn't + * occur, then there is a timeout. + */ + if (!wait_for_completion_timeout(&info->completion, + msecs_to_jiffies(1000))) { + ret = -ETIMEDOUT; + goto err; + } + + if (irq == info->fifo_data_irq) + *val = info->adc_data; + else + *val = info->temp_data; + + ret = 0; + +err: + pm_runtime_put_autosuspend(indio_dev->dev.parent); + disable_irq(irq); + mutex_unlock(&info->mutex); + + return ret; +} + +static int sun4i_gpadc_adc_read(struct iio_dev *indio_dev, int channel, + int *val) +{ + struct sun4i_gpadc_iio *info = iio_priv(indio_dev); + + return sun4i_gpadc_read(indio_dev, channel, val, info->fifo_data_irq); +} + +static int sun4i_gpadc_temp_read(struct iio_dev *indio_dev, int *val) +{ + struct sun4i_gpadc_iio *info = iio_priv(indio_dev); + + if (info->no_irq) { + pm_runtime_get_sync(indio_dev->dev.parent); + + regmap_read(info->regmap, SUN4I_GPADC_TEMP_DATA, val); + + pm_runtime_put_autosuspend(indio_dev->dev.parent); + + return 0; + } + + return sun4i_gpadc_read(indio_dev, 0, val, info->temp_data_irq); +} + +static int sun4i_gpadc_temp_offset(struct iio_dev *indio_dev, int *val) +{ + struct sun4i_gpadc_iio *info = iio_priv(indio_dev); + + *val = info->data->temp_offset; + + return 0; +} + +static int sun4i_gpadc_temp_scale(struct iio_dev *indio_dev, int *val) +{ + struct sun4i_gpadc_iio *info = iio_priv(indio_dev); + + *val = info->data->temp_scale; + + return 0; +} + +static int sun4i_gpadc_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int *val, + int *val2, long mask) +{ + int ret; + + switch (mask) { + case IIO_CHAN_INFO_OFFSET: + ret = sun4i_gpadc_temp_offset(indio_dev, val); + if (ret) + return ret; + + return IIO_VAL_INT; + case IIO_CHAN_INFO_RAW: + if (chan->type == IIO_VOLTAGE) + ret = sun4i_gpadc_adc_read(indio_dev, chan->channel, + val); + else + ret = sun4i_gpadc_temp_read(indio_dev, val); + + if (ret) + return ret; + + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + if (chan->type == IIO_VOLTAGE) { + /* 3000mV / 4096 * raw */ + *val = 0; + *val2 = 732421875; + return IIO_VAL_INT_PLUS_NANO; + } + + ret = sun4i_gpadc_temp_scale(indio_dev, val); + if (ret) + return ret; + + return IIO_VAL_INT; + default: + return -EINVAL; + } + + return -EINVAL; +} + +static const struct iio_info sun4i_gpadc_iio_info = { + .read_raw = sun4i_gpadc_read_raw, +}; + +static irqreturn_t sun4i_gpadc_temp_data_irq_handler(int irq, void *dev_id) +{ + struct sun4i_gpadc_iio *info = dev_id; + + if (atomic_read(&info->ignore_temp_data_irq)) + goto out; + + if (!regmap_read(info->regmap, SUN4I_GPADC_TEMP_DATA, &info->temp_data)) + complete(&info->completion); + +out: + return IRQ_HANDLED; +} + +static irqreturn_t sun4i_gpadc_fifo_data_irq_handler(int irq, void *dev_id) +{ + struct sun4i_gpadc_iio *info = dev_id; + + if (atomic_read(&info->ignore_fifo_data_irq)) + goto out; + + if (!regmap_read(info->regmap, SUN4I_GPADC_DATA, &info->adc_data)) + complete(&info->completion); + +out: + return IRQ_HANDLED; +} + +static int sun4i_gpadc_runtime_suspend(struct device *dev) +{ + struct sun4i_gpadc_iio *info = iio_priv(dev_get_drvdata(dev)); + + /* Disable the ADC on IP */ + regmap_write(info->regmap, SUN4I_GPADC_CTRL1, 0); + /* Disable temperature sensor on IP */ + regmap_write(info->regmap, SUN4I_GPADC_TPR, 0); + + return 0; +} + +static int sun4i_gpadc_runtime_resume(struct device *dev) +{ + struct sun4i_gpadc_iio *info = iio_priv(dev_get_drvdata(dev)); + + /* clkin = 6MHz */ + regmap_write(info->regmap, SUN4I_GPADC_CTRL0, + SUN4I_GPADC_CTRL0_ADC_CLK_DIVIDER(2) | + SUN4I_GPADC_CTRL0_FS_DIV(7) | + SUN4I_GPADC_CTRL0_T_ACQ(63)); + regmap_write(info->regmap, SUN4I_GPADC_CTRL1, info->data->tp_mode_en); + regmap_write(info->regmap, SUN4I_GPADC_CTRL3, + SUN4I_GPADC_CTRL3_FILTER_EN | + SUN4I_GPADC_CTRL3_FILTER_TYPE(1)); + /* period = SUN4I_GPADC_TPR_TEMP_PERIOD * 256 * 16 / clkin; ~0.6s */ + regmap_write(info->regmap, SUN4I_GPADC_TPR, + SUN4I_GPADC_TPR_TEMP_ENABLE | + SUN4I_GPADC_TPR_TEMP_PERIOD(800)); + + return 0; +} + +static int sun4i_gpadc_get_temp(struct thermal_zone_device *tz, int *temp) +{ + struct sun4i_gpadc_iio *info = thermal_zone_device_priv(tz); + int val, scale, offset; + + if (sun4i_gpadc_temp_read(info->indio_dev, &val)) + return -ETIMEDOUT; + + sun4i_gpadc_temp_scale(info->indio_dev, &scale); + sun4i_gpadc_temp_offset(info->indio_dev, &offset); + + *temp = (val + offset) * scale; + + return 0; +} + +static const struct thermal_zone_device_ops sun4i_ts_tz_ops = { + .get_temp = &sun4i_gpadc_get_temp, +}; + +static const struct dev_pm_ops sun4i_gpadc_pm_ops = { + .runtime_suspend = &sun4i_gpadc_runtime_suspend, + .runtime_resume = &sun4i_gpadc_runtime_resume, +}; + +static int sun4i_irq_init(struct platform_device *pdev, const char *name, + irq_handler_t handler, const char *devname, + unsigned int *irq, atomic_t *atomic) +{ + int ret; + struct sun4i_gpadc_dev *mfd_dev = dev_get_drvdata(pdev->dev.parent); + struct sun4i_gpadc_iio *info = iio_priv(dev_get_drvdata(&pdev->dev)); + + /* + * Once the interrupt is activated, the IP continuously performs + * conversions thus throws interrupts. The interrupt is activated right + * after being requested but we want to control when these interrupts + * occur thus we disable it right after being requested. However, an + * interrupt might occur between these two instructions and we have to + * make sure that does not happen, by using atomic flags. We set the + * flag before requesting the interrupt and unset it right after + * disabling the interrupt. When an interrupt occurs between these two + * instructions, reading the atomic flag will tell us to ignore the + * interrupt. + */ + atomic_set(atomic, 1); + + ret = platform_get_irq_byname(pdev, name); + if (ret < 0) + return ret; + + ret = regmap_irq_get_virq(mfd_dev->regmap_irqc, ret); + if (ret < 0) { + dev_err(&pdev->dev, "failed to get virq for irq %s\n", name); + return ret; + } + + *irq = ret; + ret = devm_request_any_context_irq(&pdev->dev, *irq, handler, + IRQF_NO_AUTOEN, + devname, info); + if (ret < 0) { + dev_err(&pdev->dev, "could not request %s interrupt: %d\n", + name, ret); + return ret; + } + + atomic_set(atomic, 0); + + return 0; +} + +static const struct of_device_id sun4i_gpadc_of_id[] = { + { + .compatible = "allwinner,sun8i-a33-ths", + .data = &sun8i_a33_gpadc_data, + }, + { } +}; + +static int sun4i_gpadc_probe_dt(struct platform_device *pdev, + struct iio_dev *indio_dev) +{ + struct sun4i_gpadc_iio *info = iio_priv(indio_dev); + void __iomem *base; + int ret; + + info->data = of_device_get_match_data(&pdev->dev); + if (!info->data) + return -ENODEV; + + info->no_irq = true; + indio_dev->num_channels = ARRAY_SIZE(sun8i_a33_gpadc_channels); + indio_dev->channels = sun8i_a33_gpadc_channels; + + base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(base)) + return PTR_ERR(base); + + info->regmap = devm_regmap_init_mmio(&pdev->dev, base, + &sun4i_gpadc_regmap_config); + if (IS_ERR(info->regmap)) { + ret = PTR_ERR(info->regmap); + dev_err(&pdev->dev, "failed to init regmap: %d\n", ret); + return ret; + } + + if (IS_ENABLED(CONFIG_THERMAL_OF)) + info->sensor_device = &pdev->dev; + + return 0; +} + +static int sun4i_gpadc_probe_mfd(struct platform_device *pdev, + struct iio_dev *indio_dev) +{ + struct sun4i_gpadc_iio *info = iio_priv(indio_dev); + struct sun4i_gpadc_dev *sun4i_gpadc_dev = + dev_get_drvdata(pdev->dev.parent); + int ret; + + info->no_irq = false; + info->regmap = sun4i_gpadc_dev->regmap; + + indio_dev->num_channels = ARRAY_SIZE(sun4i_gpadc_channels); + indio_dev->channels = sun4i_gpadc_channels; + + info->data = (struct gpadc_data *)platform_get_device_id(pdev)->driver_data; + + /* + * Since the controller needs to be in touchscreen mode for its thermal + * sensor to operate properly, and that switching between the two modes + * needs a delay, always registering in the thermal framework will + * significantly slow down the conversion rate of the ADCs. + * + * Therefore, instead of depending on THERMAL_OF in Kconfig, we only + * register the sensor if that option is enabled, eventually leaving + * that choice to the user. + */ + + if (IS_ENABLED(CONFIG_THERMAL_OF)) { + /* + * This driver is a child of an MFD which has a node in the DT + * but not its children, because of DT backward compatibility + * for A10, A13 and A31 SoCs. Therefore, the resulting devices + * of this driver do not have an of_node variable. + * However, its parent (the MFD driver) has an of_node variable + * and since devm_thermal_zone_of_sensor_register uses its first + * argument to match the phandle defined in the node of the + * thermal driver with the of_node of the device passed as first + * argument and the third argument to call ops from + * thermal_zone_of_device_ops, the solution is to use the parent + * device as first argument to match the phandle with its + * of_node, and the device from this driver as third argument to + * return the temperature. + */ + info->sensor_device = pdev->dev.parent; + } else { + indio_dev->num_channels = + ARRAY_SIZE(sun4i_gpadc_channels_no_temp); + indio_dev->channels = sun4i_gpadc_channels_no_temp; + } + + if (IS_ENABLED(CONFIG_THERMAL_OF)) { + ret = sun4i_irq_init(pdev, "TEMP_DATA_PENDING", + sun4i_gpadc_temp_data_irq_handler, + "temp_data", &info->temp_data_irq, + &info->ignore_temp_data_irq); + if (ret < 0) + return ret; + } + + ret = sun4i_irq_init(pdev, "FIFO_DATA_PENDING", + sun4i_gpadc_fifo_data_irq_handler, "fifo_data", + &info->fifo_data_irq, &info->ignore_fifo_data_irq); + if (ret < 0) + return ret; + + if (IS_ENABLED(CONFIG_THERMAL_OF)) { + ret = iio_map_array_register(indio_dev, sun4i_gpadc_hwmon_maps); + if (ret < 0) { + dev_err(&pdev->dev, + "failed to register iio map array\n"); + return ret; + } + } + + return 0; +} + +static int sun4i_gpadc_probe(struct platform_device *pdev) +{ + struct sun4i_gpadc_iio *info; + struct iio_dev *indio_dev; + int ret; + + indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*info)); + if (!indio_dev) + return -ENOMEM; + + info = iio_priv(indio_dev); + platform_set_drvdata(pdev, indio_dev); + + mutex_init(&info->mutex); + info->indio_dev = indio_dev; + init_completion(&info->completion); + indio_dev->name = dev_name(&pdev->dev); + indio_dev->info = &sun4i_gpadc_iio_info; + indio_dev->modes = INDIO_DIRECT_MODE; + + if (pdev->dev.of_node) + ret = sun4i_gpadc_probe_dt(pdev, indio_dev); + else + ret = sun4i_gpadc_probe_mfd(pdev, indio_dev); + + if (ret) + return ret; + + pm_runtime_set_autosuspend_delay(&pdev->dev, + SUN4I_GPADC_AUTOSUSPEND_DELAY); + pm_runtime_use_autosuspend(&pdev->dev); + pm_runtime_set_suspended(&pdev->dev); + pm_runtime_enable(&pdev->dev); + + if (IS_ENABLED(CONFIG_THERMAL_OF)) { + info->tzd = devm_thermal_of_zone_register(info->sensor_device, + 0, info, + &sun4i_ts_tz_ops); + /* + * Do not fail driver probing when failing to register in + * thermal because no thermal DT node is found. + */ + if (IS_ERR(info->tzd) && PTR_ERR(info->tzd) != -ENODEV) { + dev_err(&pdev->dev, + "could not register thermal sensor: %ld\n", + PTR_ERR(info->tzd)); + return PTR_ERR(info->tzd); + } + } + + ret = devm_iio_device_register(&pdev->dev, indio_dev); + if (ret < 0) { + dev_err(&pdev->dev, "could not register the device\n"); + goto err_map; + } + + return 0; + +err_map: + if (!info->no_irq && IS_ENABLED(CONFIG_THERMAL_OF)) + iio_map_array_unregister(indio_dev); + + pm_runtime_put(&pdev->dev); + pm_runtime_disable(&pdev->dev); + + return ret; +} + +static void sun4i_gpadc_remove(struct platform_device *pdev) +{ + struct iio_dev *indio_dev = platform_get_drvdata(pdev); + struct sun4i_gpadc_iio *info = iio_priv(indio_dev); + + pm_runtime_put(&pdev->dev); + pm_runtime_disable(&pdev->dev); + + if (!IS_ENABLED(CONFIG_THERMAL_OF)) + return; + + if (!info->no_irq) + iio_map_array_unregister(indio_dev); +} + +static const struct platform_device_id sun4i_gpadc_id[] = { + { .name = "sun4i-a10-gpadc-iio", .driver_data = (kernel_ulong_t)&sun4i_gpadc_data }, + { .name = "sun5i-a13-gpadc-iio", .driver_data = (kernel_ulong_t)&sun5i_gpadc_data }, + { .name = "sun6i-a31-gpadc-iio", .driver_data = (kernel_ulong_t)&sun6i_gpadc_data }, + { } +}; +MODULE_DEVICE_TABLE(platform, sun4i_gpadc_id); + +static struct platform_driver sun4i_gpadc_driver = { + .driver = { + .name = "sun4i-gpadc-iio", + .of_match_table = sun4i_gpadc_of_id, + .pm = &sun4i_gpadc_pm_ops, + }, + .id_table = sun4i_gpadc_id, + .probe = sun4i_gpadc_probe, + .remove = sun4i_gpadc_remove, +}; +MODULE_DEVICE_TABLE(of, sun4i_gpadc_of_id); + +module_platform_driver(sun4i_gpadc_driver); + +MODULE_DESCRIPTION("ADC driver for sunxi platforms"); +MODULE_AUTHOR("Quentin Schulz "); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/adc/ti-adc081c.c b/drivers/iio/adc/ti-adc081c.c new file mode 100644 index 000000000..2df8f1c21 --- /dev/null +++ b/drivers/iio/adc/ti-adc081c.c @@ -0,0 +1,234 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * TI ADC081C/ADC101C/ADC121C 8/10/12-bit ADC driver + * + * Copyright (C) 2012 Avionic Design GmbH + * Copyright (C) 2016 Intel + * + * Datasheets: + * https://www.ti.com/lit/ds/symlink/adc081c021.pdf + * https://www.ti.com/lit/ds/symlink/adc101c021.pdf + * https://www.ti.com/lit/ds/symlink/adc121c021.pdf + * + * The devices have a very similar interface and differ mostly in the number of + * bits handled. For the 8-bit and 10-bit models the least-significant 4 or 2 + * bits of value registers are reserved. + */ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +struct adc081c { + struct i2c_client *i2c; + struct regulator *ref; + + /* 8, 10 or 12 */ + int bits; + + /* Ensure natural alignment of buffer elements */ + struct { + u16 channel; + aligned_s64 ts; + } scan; +}; + +#define REG_CONV_RES 0x00 + +static int adc081c_read_raw(struct iio_dev *iio, + struct iio_chan_spec const *channel, int *value, + int *shift, long mask) +{ + struct adc081c *adc = iio_priv(iio); + int err; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + err = i2c_smbus_read_word_swapped(adc->i2c, REG_CONV_RES); + if (err < 0) + return err; + + *value = (err & 0xFFF) >> (12 - adc->bits); + return IIO_VAL_INT; + + case IIO_CHAN_INFO_SCALE: + err = regulator_get_voltage(adc->ref); + if (err < 0) + return err; + + *value = err / 1000; + *shift = adc->bits; + + return IIO_VAL_FRACTIONAL_LOG2; + + default: + break; + } + + return -EINVAL; +} + +#define ADCxx1C_CHAN(_bits) { \ + .type = IIO_VOLTAGE, \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .scan_type = { \ + .sign = 'u', \ + .realbits = (_bits), \ + .storagebits = 16, \ + .shift = 12 - (_bits), \ + .endianness = IIO_CPU, \ + }, \ +} + +#define DEFINE_ADCxx1C_CHANNELS(_name, _bits) \ + static const struct iio_chan_spec _name ## _channels[] = { \ + ADCxx1C_CHAN((_bits)), \ + IIO_CHAN_SOFT_TIMESTAMP(1), \ + }; \ + +#define ADC081C_NUM_CHANNELS 2 + +struct adcxx1c_model { + const struct iio_chan_spec* channels; + int bits; +}; + +DEFINE_ADCxx1C_CHANNELS(adc081c, 8); +DEFINE_ADCxx1C_CHANNELS(adc101c, 10); +DEFINE_ADCxx1C_CHANNELS(adc121c, 12); + +static const struct adcxx1c_model adc081c_model = { + .channels = adc081c_channels, + .bits = 8, +}; + +static const struct adcxx1c_model adc101c_model = { + .channels = adc101c_channels, + .bits = 10, +}; + +static const struct adcxx1c_model adc121c_model = { + .channels = adc121c_channels, + .bits = 12, +}; + +static const struct iio_info adc081c_info = { + .read_raw = adc081c_read_raw, +}; + +static irqreturn_t adc081c_trigger_handler(int irq, void *p) +{ + struct iio_poll_func *pf = p; + struct iio_dev *indio_dev = pf->indio_dev; + struct adc081c *data = iio_priv(indio_dev); + int ret; + + ret = i2c_smbus_read_word_swapped(data->i2c, REG_CONV_RES); + if (ret < 0) + goto out; + data->scan.channel = ret; + iio_push_to_buffers_with_ts(indio_dev, &data->scan, sizeof(data->scan), + iio_get_time_ns(indio_dev)); +out: + iio_trigger_notify_done(indio_dev->trig); + return IRQ_HANDLED; +} + +static void adc081c_reg_disable(void *reg) +{ + regulator_disable(reg); +} + +static int adc081c_probe(struct i2c_client *client) +{ + struct iio_dev *iio; + struct adc081c *adc; + const struct adcxx1c_model *model; + int err; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_WORD_DATA)) + return -EOPNOTSUPP; + + model = i2c_get_match_data(client); + + iio = devm_iio_device_alloc(&client->dev, sizeof(*adc)); + if (!iio) + return -ENOMEM; + + adc = iio_priv(iio); + adc->i2c = client; + adc->bits = model->bits; + + adc->ref = devm_regulator_get(&client->dev, "vref"); + if (IS_ERR(adc->ref)) + return PTR_ERR(adc->ref); + + err = regulator_enable(adc->ref); + if (err < 0) + return err; + + err = devm_add_action_or_reset(&client->dev, adc081c_reg_disable, + adc->ref); + if (err) + return err; + + iio->name = dev_name(&client->dev); + iio->modes = INDIO_DIRECT_MODE; + iio->info = &adc081c_info; + + iio->channels = model->channels; + iio->num_channels = ADC081C_NUM_CHANNELS; + + err = devm_iio_triggered_buffer_setup(&client->dev, iio, NULL, + adc081c_trigger_handler, NULL); + if (err < 0) + return dev_err_probe(&client->dev, err, "iio triggered buffer setup failed\n"); + + return devm_iio_device_register(&client->dev, iio); +} + +static const struct i2c_device_id adc081c_id[] = { + { .name = "adc081c", .driver_data = (kernel_ulong_t)&adc081c_model }, + { .name = "adc101c", .driver_data = (kernel_ulong_t)&adc101c_model }, + { .name = "adc121c", .driver_data = (kernel_ulong_t)&adc121c_model }, + { } +}; +MODULE_DEVICE_TABLE(i2c, adc081c_id); + +static const struct acpi_device_id adc081c_acpi_match[] = { + /* Used on some AAEON boards */ + { "ADC081C", (kernel_ulong_t)&adc081c_model }, + { } +}; +MODULE_DEVICE_TABLE(acpi, adc081c_acpi_match); + +static const struct of_device_id adc081c_of_match[] = { + { .compatible = "ti,adc081c", .data = &adc081c_model }, + { .compatible = "ti,adc101c", .data = &adc101c_model }, + { .compatible = "ti,adc121c", .data = &adc121c_model }, + { } +}; +MODULE_DEVICE_TABLE(of, adc081c_of_match); + +static struct i2c_driver adc081c_driver = { + .driver = { + .name = "adc081c", + .of_match_table = adc081c_of_match, + .acpi_match_table = adc081c_acpi_match, + }, + .probe = adc081c_probe, + .id_table = adc081c_id, +}; +module_i2c_driver(adc081c_driver); + +MODULE_AUTHOR("Thierry Reding "); +MODULE_DESCRIPTION("Texas Instruments ADC081C/ADC101C/ADC121C driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/adc/ti-adc0832.c b/drivers/iio/adc/ti-adc0832.c new file mode 100644 index 000000000..a522d8676 --- /dev/null +++ b/drivers/iio/adc/ti-adc0832.c @@ -0,0 +1,336 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * ADC0831/ADC0832/ADC0834/ADC0838 8-bit ADC driver + * + * Copyright (c) 2016 Akinobu Mita + * + * Datasheet: https://www.ti.com/lit/ds/symlink/adc0832-n.pdf + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +enum { + adc0831, + adc0832, + adc0834, + adc0838, +}; + +struct adc0832 { + struct spi_device *spi; + struct regulator *reg; + struct mutex lock; + u8 mux_bits; + /* + * Max size needed: 16x 1 byte ADC data + 8 bytes timestamp + * May be shorter if not all channels are enabled subject + * to the timestamp remaining 8 byte aligned. + */ + u8 data[24] __aligned(8); + + u8 tx_buf[2] __aligned(IIO_DMA_MINALIGN); + u8 rx_buf[2]; +}; + +#define ADC0832_VOLTAGE_CHANNEL(chan) \ + { \ + .type = IIO_VOLTAGE, \ + .indexed = 1, \ + .channel = chan, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ + .scan_index = chan, \ + .scan_type = { \ + .sign = 'u', \ + .realbits = 8, \ + .storagebits = 8, \ + }, \ + } + +#define ADC0832_VOLTAGE_CHANNEL_DIFF(chan1, chan2, si) \ + { \ + .type = IIO_VOLTAGE, \ + .indexed = 1, \ + .channel = (chan1), \ + .channel2 = (chan2), \ + .differential = 1, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ + .scan_index = si, \ + .scan_type = { \ + .sign = 'u', \ + .realbits = 8, \ + .storagebits = 8, \ + }, \ + } + +static const struct iio_chan_spec adc0831_channels[] = { + ADC0832_VOLTAGE_CHANNEL_DIFF(0, 1, 0), + IIO_CHAN_SOFT_TIMESTAMP(1), +}; + +static const struct iio_chan_spec adc0832_channels[] = { + ADC0832_VOLTAGE_CHANNEL(0), + ADC0832_VOLTAGE_CHANNEL(1), + ADC0832_VOLTAGE_CHANNEL_DIFF(0, 1, 2), + ADC0832_VOLTAGE_CHANNEL_DIFF(1, 0, 3), + IIO_CHAN_SOFT_TIMESTAMP(4), +}; + +static const struct iio_chan_spec adc0834_channels[] = { + ADC0832_VOLTAGE_CHANNEL(0), + ADC0832_VOLTAGE_CHANNEL(1), + ADC0832_VOLTAGE_CHANNEL(2), + ADC0832_VOLTAGE_CHANNEL(3), + ADC0832_VOLTAGE_CHANNEL_DIFF(0, 1, 4), + ADC0832_VOLTAGE_CHANNEL_DIFF(1, 0, 5), + ADC0832_VOLTAGE_CHANNEL_DIFF(2, 3, 6), + ADC0832_VOLTAGE_CHANNEL_DIFF(3, 2, 7), + IIO_CHAN_SOFT_TIMESTAMP(8), +}; + +static const struct iio_chan_spec adc0838_channels[] = { + ADC0832_VOLTAGE_CHANNEL(0), + ADC0832_VOLTAGE_CHANNEL(1), + ADC0832_VOLTAGE_CHANNEL(2), + ADC0832_VOLTAGE_CHANNEL(3), + ADC0832_VOLTAGE_CHANNEL(4), + ADC0832_VOLTAGE_CHANNEL(5), + ADC0832_VOLTAGE_CHANNEL(6), + ADC0832_VOLTAGE_CHANNEL(7), + ADC0832_VOLTAGE_CHANNEL_DIFF(0, 1, 8), + ADC0832_VOLTAGE_CHANNEL_DIFF(1, 0, 9), + ADC0832_VOLTAGE_CHANNEL_DIFF(2, 3, 10), + ADC0832_VOLTAGE_CHANNEL_DIFF(3, 2, 11), + ADC0832_VOLTAGE_CHANNEL_DIFF(4, 5, 12), + ADC0832_VOLTAGE_CHANNEL_DIFF(5, 4, 13), + ADC0832_VOLTAGE_CHANNEL_DIFF(6, 7, 14), + ADC0832_VOLTAGE_CHANNEL_DIFF(7, 6, 15), + IIO_CHAN_SOFT_TIMESTAMP(16), +}; + +static int adc0831_adc_conversion(struct adc0832 *adc) +{ + struct spi_device *spi = adc->spi; + int ret; + + ret = spi_read(spi, &adc->rx_buf, 2); + if (ret) + return ret; + + /* + * Skip TRI-STATE and a leading zero + */ + return (adc->rx_buf[0] << 2 & 0xff) | (adc->rx_buf[1] >> 6); +} + +static int adc0832_adc_conversion(struct adc0832 *adc, int channel, + bool differential) +{ + struct spi_device *spi = adc->spi; + struct spi_transfer xfer = { + .tx_buf = adc->tx_buf, + .rx_buf = adc->rx_buf, + .len = 2, + }; + int ret; + + if (!adc->mux_bits) + return adc0831_adc_conversion(adc); + + /* start bit */ + adc->tx_buf[0] = 1 << (adc->mux_bits + 1); + /* single-ended or differential */ + adc->tx_buf[0] |= differential ? 0 : (1 << adc->mux_bits); + /* odd / sign */ + adc->tx_buf[0] |= (channel % 2) << (adc->mux_bits - 1); + /* select */ + if (adc->mux_bits > 1) + adc->tx_buf[0] |= channel / 2; + + /* align Data output BIT7 (MSB) to 8-bit boundary */ + adc->tx_buf[0] <<= 1; + + ret = spi_sync_transfer(spi, &xfer, 1); + if (ret) + return ret; + + return adc->rx_buf[1]; +} + +static int adc0832_read_raw(struct iio_dev *iio, + struct iio_chan_spec const *channel, int *value, + int *shift, long mask) +{ + struct adc0832 *adc = iio_priv(iio); + + switch (mask) { + case IIO_CHAN_INFO_RAW: + mutex_lock(&adc->lock); + *value = adc0832_adc_conversion(adc, channel->channel, + channel->differential); + mutex_unlock(&adc->lock); + if (*value < 0) + return *value; + + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + *value = regulator_get_voltage(adc->reg); + if (*value < 0) + return *value; + + /* convert regulator output voltage to mV */ + *value /= 1000; + *shift = 8; + + return IIO_VAL_FRACTIONAL_LOG2; + } + + return -EINVAL; +} + +static const struct iio_info adc0832_info = { + .read_raw = adc0832_read_raw, +}; + +static irqreturn_t adc0832_trigger_handler(int irq, void *p) +{ + struct iio_poll_func *pf = p; + struct iio_dev *indio_dev = pf->indio_dev; + struct adc0832 *adc = iio_priv(indio_dev); + int scan_index; + int i = 0; + + mutex_lock(&adc->lock); + + iio_for_each_active_channel(indio_dev, scan_index) { + const struct iio_chan_spec *scan_chan = + &indio_dev->channels[scan_index]; + int ret = adc0832_adc_conversion(adc, scan_chan->channel, + scan_chan->differential); + if (ret < 0) { + dev_warn(&adc->spi->dev, + "failed to get conversion data\n"); + goto out; + } + + adc->data[i] = ret; + i++; + } + iio_push_to_buffers_with_ts(indio_dev, adc->data, sizeof(adc->data), + iio_get_time_ns(indio_dev)); +out: + mutex_unlock(&adc->lock); + + iio_trigger_notify_done(indio_dev->trig); + + return IRQ_HANDLED; +} + +static void adc0832_reg_disable(void *reg) +{ + regulator_disable(reg); +} + +static int adc0832_probe(struct spi_device *spi) +{ + struct iio_dev *indio_dev; + struct adc0832 *adc; + int ret; + + indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*adc)); + if (!indio_dev) + return -ENOMEM; + + adc = iio_priv(indio_dev); + adc->spi = spi; + mutex_init(&adc->lock); + + indio_dev->name = spi_get_device_id(spi)->name; + indio_dev->info = &adc0832_info; + indio_dev->modes = INDIO_DIRECT_MODE; + + switch (spi_get_device_id(spi)->driver_data) { + case adc0831: + adc->mux_bits = 0; + indio_dev->channels = adc0831_channels; + indio_dev->num_channels = ARRAY_SIZE(adc0831_channels); + break; + case adc0832: + adc->mux_bits = 1; + indio_dev->channels = adc0832_channels; + indio_dev->num_channels = ARRAY_SIZE(adc0832_channels); + break; + case adc0834: + adc->mux_bits = 2; + indio_dev->channels = adc0834_channels; + indio_dev->num_channels = ARRAY_SIZE(adc0834_channels); + break; + case adc0838: + adc->mux_bits = 3; + indio_dev->channels = adc0838_channels; + indio_dev->num_channels = ARRAY_SIZE(adc0838_channels); + break; + default: + return -EINVAL; + } + + adc->reg = devm_regulator_get(&spi->dev, "vref"); + if (IS_ERR(adc->reg)) + return PTR_ERR(adc->reg); + + ret = regulator_enable(adc->reg); + if (ret) + return ret; + + ret = devm_add_action_or_reset(&spi->dev, adc0832_reg_disable, + adc->reg); + if (ret) + return ret; + + ret = devm_iio_triggered_buffer_setup(&spi->dev, indio_dev, NULL, + adc0832_trigger_handler, NULL); + if (ret) + return ret; + + return devm_iio_device_register(&spi->dev, indio_dev); +} + +static const struct of_device_id adc0832_dt_ids[] = { + { .compatible = "ti,adc0831", }, + { .compatible = "ti,adc0832", }, + { .compatible = "ti,adc0834", }, + { .compatible = "ti,adc0838", }, + { } +}; +MODULE_DEVICE_TABLE(of, adc0832_dt_ids); + +static const struct spi_device_id adc0832_id[] = { + { .name = "adc0831", .driver_data = adc0831 }, + { .name = "adc0832", .driver_data = adc0832 }, + { .name = "adc0834", .driver_data = adc0834 }, + { .name = "adc0838", .driver_data = adc0838 }, + { } +}; +MODULE_DEVICE_TABLE(spi, adc0832_id); + +static struct spi_driver adc0832_driver = { + .driver = { + .name = "adc0832", + .of_match_table = adc0832_dt_ids, + }, + .probe = adc0832_probe, + .id_table = adc0832_id, +}; +module_spi_driver(adc0832_driver); + +MODULE_AUTHOR("Akinobu Mita "); +MODULE_DESCRIPTION("ADC0831/ADC0832/ADC0834/ADC0838 driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/adc/ti-adc084s021.c b/drivers/iio/adc/ti-adc084s021.c new file mode 100644 index 000000000..cf2f6ee59 --- /dev/null +++ b/drivers/iio/adc/ti-adc084s021.c @@ -0,0 +1,262 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2017 Axis Communications AB + * + * Driver for Texas Instruments' ADC084S021 ADC chip. + * Datasheets can be found here: + * https://www.ti.com/lit/ds/symlink/adc084s021.pdf + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define ADC084S021_DRIVER_NAME "adc084s021" + +struct adc084s021 { + struct spi_device *spi; + struct spi_message message; + struct spi_transfer spi_trans; + struct regulator *reg; + struct mutex lock; + /* Buffer used to align data */ + struct { + __be16 channels[4]; + aligned_s64 ts; + } scan; + /* + * DMA (thus cache coherency maintenance) may require the + * transfer buffers to live in their own cache line. + */ + u16 tx_buf[4] __aligned(IIO_DMA_MINALIGN); + __be16 rx_buf[5]; /* First 16-bits are trash */ +}; + +#define ADC084S021_VOLTAGE_CHANNEL(num) \ + { \ + .type = IIO_VOLTAGE, \ + .channel = (num), \ + .indexed = 1, \ + .scan_index = (num), \ + .scan_type = { \ + .sign = 'u', \ + .realbits = 8, \ + .storagebits = 16, \ + .shift = 4, \ + .endianness = IIO_BE, \ + }, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),\ + } + +static const struct iio_chan_spec adc084s021_channels[] = { + ADC084S021_VOLTAGE_CHANNEL(0), + ADC084S021_VOLTAGE_CHANNEL(1), + ADC084S021_VOLTAGE_CHANNEL(2), + ADC084S021_VOLTAGE_CHANNEL(3), + IIO_CHAN_SOFT_TIMESTAMP(4), +}; + +/** + * adc084s021_adc_conversion() - Read an ADC channel and return its value. + * + * @adc: The ADC SPI data. + * @data: Buffer for converted data. + */ +static int adc084s021_adc_conversion(struct adc084s021 *adc, __be16 *data) +{ + int n_words = (adc->spi_trans.len >> 1) - 1; /* Discard first word */ + int ret, i = 0; + + /* Do the transfer */ + ret = spi_sync(adc->spi, &adc->message); + if (ret < 0) + return ret; + + for (; i < n_words; i++) + *(data + i) = adc->rx_buf[i + 1]; + + return ret; +} + +static int adc084s021_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *channel, int *val, + int *val2, long mask) +{ + struct adc084s021 *adc = iio_priv(indio_dev); + int ret; + __be16 be_val; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; + + ret = regulator_enable(adc->reg); + if (ret) { + iio_device_release_direct(indio_dev); + return ret; + } + + adc->tx_buf[0] = channel->channel << 3; + ret = adc084s021_adc_conversion(adc, &be_val); + iio_device_release_direct(indio_dev); + regulator_disable(adc->reg); + if (ret < 0) + return ret; + + *val = be16_to_cpu(be_val); + *val = (*val >> channel->scan_type.shift) & 0xff; + + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + ret = regulator_enable(adc->reg); + if (ret) + return ret; + + ret = regulator_get_voltage(adc->reg); + regulator_disable(adc->reg); + if (ret < 0) + return ret; + + *val = ret / 1000; + + return IIO_VAL_INT; + default: + return -EINVAL; + } +} + +/** + * adc084s021_buffer_trigger_handler() - Read ADC channels and push to buffer. + * + * @irq: The interrupt number (not used). + * @pollfunc: Pointer to the poll func. + */ +static irqreturn_t adc084s021_buffer_trigger_handler(int irq, void *pollfunc) +{ + struct iio_poll_func *pf = pollfunc; + struct iio_dev *indio_dev = pf->indio_dev; + struct adc084s021 *adc = iio_priv(indio_dev); + + mutex_lock(&adc->lock); + + if (adc084s021_adc_conversion(adc, adc->scan.channels) < 0) + dev_err(&adc->spi->dev, "Failed to read data\n"); + + iio_push_to_buffers_with_ts(indio_dev, &adc->scan, sizeof(adc->scan), + iio_get_time_ns(indio_dev)); + mutex_unlock(&adc->lock); + iio_trigger_notify_done(indio_dev->trig); + + return IRQ_HANDLED; +} + +static int adc084s021_buffer_preenable(struct iio_dev *indio_dev) +{ + struct adc084s021 *adc = iio_priv(indio_dev); + int scan_index; + int i = 0; + + iio_for_each_active_channel(indio_dev, scan_index) { + const struct iio_chan_spec *channel = + &indio_dev->channels[scan_index]; + adc->tx_buf[i++] = channel->channel << 3; + } + adc->spi_trans.len = 2 + (i * sizeof(__be16)); /* Trash + channels */ + + return regulator_enable(adc->reg); +} + +static int adc084s021_buffer_postdisable(struct iio_dev *indio_dev) +{ + struct adc084s021 *adc = iio_priv(indio_dev); + + adc->spi_trans.len = 4; /* Trash + single channel */ + + return regulator_disable(adc->reg); +} + +static const struct iio_info adc084s021_info = { + .read_raw = adc084s021_read_raw, +}; + +static const struct iio_buffer_setup_ops adc084s021_buffer_setup_ops = { + .preenable = adc084s021_buffer_preenable, + .postdisable = adc084s021_buffer_postdisable, +}; + +static int adc084s021_probe(struct spi_device *spi) +{ + struct iio_dev *indio_dev; + struct adc084s021 *adc; + int ret; + + indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*adc)); + if (!indio_dev) + return -ENOMEM; + + adc = iio_priv(indio_dev); + adc->spi = spi; + + /* Initiate the Industrial I/O device */ + indio_dev->name = spi_get_device_id(spi)->name; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->info = &adc084s021_info; + indio_dev->channels = adc084s021_channels; + indio_dev->num_channels = ARRAY_SIZE(adc084s021_channels); + + /* Create SPI transfer for channel reads */ + adc->spi_trans.tx_buf = adc->tx_buf; + adc->spi_trans.rx_buf = adc->rx_buf; + adc->spi_trans.len = 4; /* Trash + single channel */ + spi_message_init_with_transfers(&adc->message, &adc->spi_trans, 1); + + adc->reg = devm_regulator_get(&spi->dev, "vref"); + if (IS_ERR(adc->reg)) + return PTR_ERR(adc->reg); + + mutex_init(&adc->lock); + + /* Setup triggered buffer with pollfunction */ + ret = devm_iio_triggered_buffer_setup(&spi->dev, indio_dev, NULL, + adc084s021_buffer_trigger_handler, + &adc084s021_buffer_setup_ops); + if (ret) + return dev_err_probe(&spi->dev, ret, "Failed to setup triggered buffer\n"); + + return devm_iio_device_register(&spi->dev, indio_dev); +} + +static const struct of_device_id adc084s021_of_match[] = { + { .compatible = "ti,adc084s021", }, + { } +}; +MODULE_DEVICE_TABLE(of, adc084s021_of_match); + +static const struct spi_device_id adc084s021_id[] = { + { .name = ADC084S021_DRIVER_NAME }, + { } +}; +MODULE_DEVICE_TABLE(spi, adc084s021_id); + +static struct spi_driver adc084s021_driver = { + .driver = { + .name = ADC084S021_DRIVER_NAME, + .of_match_table = adc084s021_of_match, + }, + .probe = adc084s021_probe, + .id_table = adc084s021_id, +}; +module_spi_driver(adc084s021_driver); + +MODULE_AUTHOR("Mårten Lindahl "); +MODULE_DESCRIPTION("Texas Instruments ADC084S021"); +MODULE_LICENSE("GPL v2"); +MODULE_VERSION("1.0"); diff --git a/drivers/iio/adc/ti-adc108s102.c b/drivers/iio/adc/ti-adc108s102.c new file mode 100644 index 000000000..10959e835 --- /dev/null +++ b/drivers/iio/adc/ti-adc108s102.c @@ -0,0 +1,297 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * TI ADC108S102 SPI ADC driver + * + * Copyright (c) 2013-2015 Intel Corporation. + * Copyright (c) 2017 Siemens AG + * + * This IIO device driver is designed to work with the following + * analog to digital converters from Texas Instruments: + * ADC108S102 + * ADC128S102 + * The communication with ADC chip is via the SPI bus (mode 3). + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * In case of ACPI, we use the hard-wired 5000 mV of the Galileo and IOT2000 + * boards as default for the reference pin VA. Device tree users encode that + * via the vref-supply regulator. + */ +#define ADC108S102_VA_MV_ACPI_DEFAULT 5000 + +/* + * Defining the ADC resolution being 12 bits, we can use the same driver for + * both ADC108S102 (10 bits resolution) and ADC128S102 (12 bits resolution) + * chips. The ADC108S102 effectively returns a 12-bit result with the 2 + * least-significant bits unset. + */ +#define ADC108S102_BITS 12 +#define ADC108S102_MAX_CHANNELS 8 + +/* + * 16-bit SPI command format: + * [15:14] Ignored + * [13:11] 3-bit channel address + * [10:0] Ignored + */ +#define ADC108S102_CMD(ch) ((u16)(ch) << 11) + +/* + * 16-bit SPI response format: + * [15:12] Zeros + * [11:0] 12-bit ADC sample (for ADC108S102, [1:0] will always be 0). + */ +#define ADC108S102_RES_DATA(res) ((u16)res & GENMASK(11, 0)) + +struct adc108s102_state { + struct spi_device *spi; + u32 va_millivolt; + /* SPI transfer used by triggered buffer handler*/ + struct spi_transfer ring_xfer; + /* SPI transfer used by direct scan */ + struct spi_transfer scan_single_xfer; + /* SPI message used by ring_xfer SPI transfer */ + struct spi_message ring_msg; + /* SPI message used by scan_single_xfer SPI transfer */ + struct spi_message scan_single_msg; + + /* + * SPI message buffers: + * tx_buf: |C0|C1|C2|C3|C4|C5|C6|C7|XX| + * rx_buf: |XX|R0|R1|R2|R3|R4|R5|R6|R7|tt|tt|tt|tt| + * + * tx_buf: 8 channel read commands, plus 1 dummy command + * rx_buf: 1 dummy response, 8 channel responses + */ + __be16 rx_buf[9] __aligned(IIO_DMA_MINALIGN); + __be16 tx_buf[9] __aligned(IIO_DMA_MINALIGN); +}; + +#define ADC108S102_V_CHAN(index) \ + { \ + .type = IIO_VOLTAGE, \ + .indexed = 1, \ + .channel = index, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ + BIT(IIO_CHAN_INFO_SCALE), \ + .address = index, \ + .scan_index = index, \ + .scan_type = { \ + .sign = 'u', \ + .realbits = ADC108S102_BITS, \ + .storagebits = 16, \ + .endianness = IIO_BE, \ + }, \ + } + +static const struct iio_chan_spec adc108s102_channels[] = { + ADC108S102_V_CHAN(0), + ADC108S102_V_CHAN(1), + ADC108S102_V_CHAN(2), + ADC108S102_V_CHAN(3), + ADC108S102_V_CHAN(4), + ADC108S102_V_CHAN(5), + ADC108S102_V_CHAN(6), + ADC108S102_V_CHAN(7), + IIO_CHAN_SOFT_TIMESTAMP(8), +}; + +static int adc108s102_update_scan_mode(struct iio_dev *indio_dev, + unsigned long const *active_scan_mask) +{ + struct adc108s102_state *st = iio_priv(indio_dev); + unsigned int bit, cmds; + + /* + * Fill in the first x shorts of tx_buf with the number of channels + * enabled for sampling by the triggered buffer. + */ + cmds = 0; + for_each_set_bit(bit, active_scan_mask, ADC108S102_MAX_CHANNELS) + st->tx_buf[cmds++] = cpu_to_be16(ADC108S102_CMD(bit)); + + /* One dummy command added, to clock in the last response */ + st->tx_buf[cmds++] = 0x00; + + /* build SPI ring message */ + st->ring_xfer.tx_buf = &st->tx_buf[0]; + st->ring_xfer.rx_buf = &st->rx_buf[0]; + st->ring_xfer.len = cmds * sizeof(st->tx_buf[0]); + + spi_message_init_with_transfers(&st->ring_msg, &st->ring_xfer, 1); + + return 0; +} + +static irqreturn_t adc108s102_trigger_handler(int irq, void *p) +{ + struct iio_poll_func *pf = p; + struct iio_dev *indio_dev = pf->indio_dev; + struct adc108s102_state *st = iio_priv(indio_dev); + int ret; + + ret = spi_sync(st->spi, &st->ring_msg); + if (ret < 0) + goto out_notify; + + /* Skip the dummy response in the first slot */ + iio_push_to_buffers_with_ts_unaligned(indio_dev, + &st->rx_buf[1], + st->ring_xfer.len - sizeof(st->rx_buf[1]), + iio_get_time_ns(indio_dev)); + +out_notify: + iio_trigger_notify_done(indio_dev->trig); + + return IRQ_HANDLED; +} + +static int adc108s102_scan_direct(struct adc108s102_state *st, unsigned int ch) +{ + int ret; + + st->tx_buf[0] = cpu_to_be16(ADC108S102_CMD(ch)); + ret = spi_sync(st->spi, &st->scan_single_msg); + if (ret) + return ret; + + /* Skip the dummy response in the first slot */ + return be16_to_cpu(st->rx_buf[1]); +} + +static int adc108s102_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long m) +{ + struct adc108s102_state *st = iio_priv(indio_dev); + int ret; + + switch (m) { + case IIO_CHAN_INFO_RAW: + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; + + ret = adc108s102_scan_direct(st, chan->address); + + iio_device_release_direct(indio_dev); + + if (ret < 0) + return ret; + + *val = ADC108S102_RES_DATA(ret); + + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + if (chan->type != IIO_VOLTAGE) + break; + + *val = st->va_millivolt; + *val2 = chan->scan_type.realbits; + + return IIO_VAL_FRACTIONAL_LOG2; + default: + break; + } + + return -EINVAL; +} + +static const struct iio_info adc108s102_info = { + .read_raw = &adc108s102_read_raw, + .update_scan_mode = &adc108s102_update_scan_mode, +}; + +static int adc108s102_probe(struct spi_device *spi) +{ + struct adc108s102_state *st; + struct iio_dev *indio_dev; + int ret; + + indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st)); + if (!indio_dev) + return -ENOMEM; + + st = iio_priv(indio_dev); + + if (ACPI_COMPANION(&spi->dev)) { + st->va_millivolt = ADC108S102_VA_MV_ACPI_DEFAULT; + } else { + ret = devm_regulator_get_enable_read_voltage(&spi->dev, "vref"); + if (ret < 0) + return dev_err_probe(&spi->dev, ret, "failed get vref voltage\n"); + + st->va_millivolt = ret / 1000; + } + + st->spi = spi; + + indio_dev->name = spi->modalias; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = adc108s102_channels; + indio_dev->num_channels = ARRAY_SIZE(adc108s102_channels); + indio_dev->info = &adc108s102_info; + + /* Setup default message */ + st->scan_single_xfer.tx_buf = st->tx_buf; + st->scan_single_xfer.rx_buf = st->rx_buf; + st->scan_single_xfer.len = 2 * sizeof(st->tx_buf[0]); + + spi_message_init_with_transfers(&st->scan_single_msg, + &st->scan_single_xfer, 1); + + ret = devm_iio_triggered_buffer_setup(&spi->dev, indio_dev, NULL, + &adc108s102_trigger_handler, + NULL); + if (ret) + return ret; + + ret = devm_iio_device_register(&spi->dev, indio_dev); + if (ret) + dev_err(&spi->dev, "Failed to register IIO device\n"); + return ret; +} + +static const struct of_device_id adc108s102_of_match[] = { + { .compatible = "ti,adc108s102" }, + { } +}; +MODULE_DEVICE_TABLE(of, adc108s102_of_match); + +static const struct acpi_device_id adc108s102_acpi_ids[] = { + { "INT3495", 0 }, + { } +}; +MODULE_DEVICE_TABLE(acpi, adc108s102_acpi_ids); + +static const struct spi_device_id adc108s102_id[] = { + { .name = "adc108s102" }, + { } +}; +MODULE_DEVICE_TABLE(spi, adc108s102_id); + +static struct spi_driver adc108s102_driver = { + .driver = { + .name = "adc108s102", + .of_match_table = adc108s102_of_match, + .acpi_match_table = adc108s102_acpi_ids, + }, + .probe = adc108s102_probe, + .id_table = adc108s102_id, +}; +module_spi_driver(adc108s102_driver); + +MODULE_AUTHOR("Bogdan Pricop "); +MODULE_DESCRIPTION("Texas Instruments ADC108S102 and ADC128S102 driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/adc/ti-adc12138.c b/drivers/iio/adc/ti-adc12138.c new file mode 100644 index 000000000..279e4999e --- /dev/null +++ b/drivers/iio/adc/ti-adc12138.c @@ -0,0 +1,539 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * ADC12130/ADC12132/ADC12138 12-bit plus sign ADC driver + * + * Copyright (c) 2016 Akinobu Mita + * + * Datasheet: http://www.ti.com/lit/ds/symlink/adc12138.pdf + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define ADC12138_MODE_AUTO_CAL 0x08 +#define ADC12138_MODE_READ_STATUS 0x0c +#define ADC12138_MODE_ACQUISITION_TIME_6 0x0e +#define ADC12138_MODE_ACQUISITION_TIME_10 0x4e +#define ADC12138_MODE_ACQUISITION_TIME_18 0x8e +#define ADC12138_MODE_ACQUISITION_TIME_34 0xce + +#define ADC12138_STATUS_CAL BIT(6) + +enum { + adc12130, + adc12132, + adc12138, +}; + +struct adc12138 { + struct spi_device *spi; + unsigned int id; + /* positive analog voltage reference */ + struct regulator *vref_p; + /* negative analog voltage reference */ + struct regulator *vref_n; + struct mutex lock; + struct completion complete; + /* The number of conversion clock periods for the S/H's acquisition time */ + unsigned int acquisition_time; + /* + * Maximum size needed: 16x 2 bytes ADC data + 8 bytes timestamp. + * Less may be need if not all channels are enabled, as long as + * the 8 byte alignment of the timestamp is maintained. + */ + __be16 data[20] __aligned(8); + + u8 tx_buf[2] __aligned(IIO_DMA_MINALIGN); + u8 rx_buf[2]; +}; + +#define ADC12138_VOLTAGE_CHANNEL(chan) \ + { \ + .type = IIO_VOLTAGE, \ + .indexed = 1, \ + .channel = chan, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) \ + | BIT(IIO_CHAN_INFO_OFFSET), \ + .scan_index = chan, \ + .scan_type = { \ + .sign = 's', \ + .realbits = 13, \ + .storagebits = 16, \ + .shift = 3, \ + .endianness = IIO_BE, \ + }, \ + } + +#define ADC12138_VOLTAGE_CHANNEL_DIFF(chan1, chan2, si) \ + { \ + .type = IIO_VOLTAGE, \ + .indexed = 1, \ + .channel = (chan1), \ + .channel2 = (chan2), \ + .differential = 1, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) \ + | BIT(IIO_CHAN_INFO_OFFSET), \ + .scan_index = si, \ + .scan_type = { \ + .sign = 's', \ + .realbits = 13, \ + .storagebits = 16, \ + .shift = 3, \ + .endianness = IIO_BE, \ + }, \ + } + +static const struct iio_chan_spec adc12132_channels[] = { + ADC12138_VOLTAGE_CHANNEL(0), + ADC12138_VOLTAGE_CHANNEL(1), + ADC12138_VOLTAGE_CHANNEL_DIFF(0, 1, 2), + ADC12138_VOLTAGE_CHANNEL_DIFF(1, 0, 3), + IIO_CHAN_SOFT_TIMESTAMP(4), +}; + +static const struct iio_chan_spec adc12138_channels[] = { + ADC12138_VOLTAGE_CHANNEL(0), + ADC12138_VOLTAGE_CHANNEL(1), + ADC12138_VOLTAGE_CHANNEL(2), + ADC12138_VOLTAGE_CHANNEL(3), + ADC12138_VOLTAGE_CHANNEL(4), + ADC12138_VOLTAGE_CHANNEL(5), + ADC12138_VOLTAGE_CHANNEL(6), + ADC12138_VOLTAGE_CHANNEL(7), + ADC12138_VOLTAGE_CHANNEL_DIFF(0, 1, 8), + ADC12138_VOLTAGE_CHANNEL_DIFF(1, 0, 9), + ADC12138_VOLTAGE_CHANNEL_DIFF(2, 3, 10), + ADC12138_VOLTAGE_CHANNEL_DIFF(3, 2, 11), + ADC12138_VOLTAGE_CHANNEL_DIFF(4, 5, 12), + ADC12138_VOLTAGE_CHANNEL_DIFF(5, 4, 13), + ADC12138_VOLTAGE_CHANNEL_DIFF(6, 7, 14), + ADC12138_VOLTAGE_CHANNEL_DIFF(7, 6, 15), + IIO_CHAN_SOFT_TIMESTAMP(16), +}; + +static int adc12138_mode_programming(struct adc12138 *adc, u8 mode, + void *rx_buf, int len) +{ + struct spi_transfer xfer = { + .tx_buf = adc->tx_buf, + .rx_buf = adc->rx_buf, + .len = len, + }; + int ret; + + /* Skip unused bits for ADC12130 and ADC12132 */ + if (adc->id != adc12138) + mode = (mode & 0xc0) | ((mode & 0x0f) << 2); + + adc->tx_buf[0] = mode; + + ret = spi_sync_transfer(adc->spi, &xfer, 1); + if (ret) + return ret; + + memcpy(rx_buf, adc->rx_buf, len); + + return 0; +} + +static int adc12138_read_status(struct adc12138 *adc) +{ + u8 rx_buf[2]; + int ret; + + ret = adc12138_mode_programming(adc, ADC12138_MODE_READ_STATUS, + rx_buf, 2); + if (ret) + return ret; + + return (rx_buf[0] << 1) | (rx_buf[1] >> 7); +} + +static int __adc12138_start_conv(struct adc12138 *adc, + struct iio_chan_spec const *channel, + void *data, int len) + +{ + static const u8 ch_to_mux[] = { 0, 4, 1, 5, 2, 6, 3, 7 }; + u8 mode = (ch_to_mux[channel->channel] << 4) | + (channel->differential ? 0 : 0x80); + + return adc12138_mode_programming(adc, mode, data, len); +} + +static int adc12138_start_conv(struct adc12138 *adc, + struct iio_chan_spec const *channel) +{ + u8 trash; + + return __adc12138_start_conv(adc, channel, &trash, 1); +} + +static int adc12138_start_and_read_conv(struct adc12138 *adc, + struct iio_chan_spec const *channel, + __be16 *data) +{ + return __adc12138_start_conv(adc, channel, data, 2); +} + +static int adc12138_read_conv_data(struct adc12138 *adc, __be16 *value) +{ + /* Issue a read status instruction and read previous conversion data */ + return adc12138_mode_programming(adc, ADC12138_MODE_READ_STATUS, + value, sizeof(*value)); +} + +static int adc12138_wait_eoc(struct adc12138 *adc, unsigned long timeout) +{ + if (!wait_for_completion_timeout(&adc->complete, timeout)) + return -ETIMEDOUT; + + return 0; +} + +static int adc12138_adc_conversion(struct adc12138 *adc, + struct iio_chan_spec const *channel, + __be16 *value) +{ + int ret; + + reinit_completion(&adc->complete); + + ret = adc12138_start_conv(adc, channel); + if (ret) + return ret; + + ret = adc12138_wait_eoc(adc, msecs_to_jiffies(100)); + if (ret) + return ret; + + return adc12138_read_conv_data(adc, value); +} + +static int adc12138_read_raw(struct iio_dev *iio, + struct iio_chan_spec const *channel, int *value, + int *shift, long mask) +{ + struct adc12138 *adc = iio_priv(iio); + int ret; + __be16 data; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + mutex_lock(&adc->lock); + ret = adc12138_adc_conversion(adc, channel, &data); + mutex_unlock(&adc->lock); + if (ret) + return ret; + + *value = sign_extend32(be16_to_cpu(data) >> channel->scan_type.shift, + channel->scan_type.realbits - 1); + + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + ret = regulator_get_voltage(adc->vref_p); + if (ret < 0) + return ret; + *value = ret; + + if (!IS_ERR(adc->vref_n)) { + ret = regulator_get_voltage(adc->vref_n); + if (ret < 0) + return ret; + *value -= ret; + } + + /* convert regulator output voltage to mV */ + *value /= 1000; + *shift = channel->scan_type.realbits - 1; + + return IIO_VAL_FRACTIONAL_LOG2; + case IIO_CHAN_INFO_OFFSET: + if (!IS_ERR(adc->vref_n)) { + *value = regulator_get_voltage(adc->vref_n); + if (*value < 0) + return *value; + } else { + *value = 0; + } + + /* convert regulator output voltage to mV */ + *value /= 1000; + + return IIO_VAL_INT; + } + + return -EINVAL; +} + +static const struct iio_info adc12138_info = { + .read_raw = adc12138_read_raw, +}; + +static int adc12138_init(struct adc12138 *adc) +{ + int ret; + int status; + u8 mode; + u8 trash; + + reinit_completion(&adc->complete); + + ret = adc12138_mode_programming(adc, ADC12138_MODE_AUTO_CAL, &trash, 1); + if (ret) + return ret; + + /* data output at this time has no significance */ + status = adc12138_read_status(adc); + if (status < 0) + return status; + + adc12138_wait_eoc(adc, msecs_to_jiffies(100)); + + status = adc12138_read_status(adc); + if (status & ADC12138_STATUS_CAL) { + dev_warn(&adc->spi->dev, + "Auto Cal sequence is still in progress: %#x\n", + status); + return -EIO; + } + + switch (adc->acquisition_time) { + case 6: + mode = ADC12138_MODE_ACQUISITION_TIME_6; + break; + case 10: + mode = ADC12138_MODE_ACQUISITION_TIME_10; + break; + case 18: + mode = ADC12138_MODE_ACQUISITION_TIME_18; + break; + case 34: + mode = ADC12138_MODE_ACQUISITION_TIME_34; + break; + default: + return -EINVAL; + } + + return adc12138_mode_programming(adc, mode, &trash, 1); +} + +static irqreturn_t adc12138_trigger_handler(int irq, void *p) +{ + struct iio_poll_func *pf = p; + struct iio_dev *indio_dev = pf->indio_dev; + struct adc12138 *adc = iio_priv(indio_dev); + __be16 trash; + int ret; + int scan_index; + int i = 0; + + mutex_lock(&adc->lock); + + iio_for_each_active_channel(indio_dev, scan_index) { + const struct iio_chan_spec *scan_chan = + &indio_dev->channels[scan_index]; + + reinit_completion(&adc->complete); + + ret = adc12138_start_and_read_conv(adc, scan_chan, + i ? &adc->data[i - 1] : &trash); + if (ret) { + dev_warn(&adc->spi->dev, + "failed to start conversion\n"); + goto out; + } + + ret = adc12138_wait_eoc(adc, msecs_to_jiffies(100)); + if (ret) { + dev_warn(&adc->spi->dev, "wait eoc timeout\n"); + goto out; + } + + i++; + } + + if (i) { + ret = adc12138_read_conv_data(adc, &adc->data[i - 1]); + if (ret) { + dev_warn(&adc->spi->dev, + "failed to get conversion data\n"); + goto out; + } + } + + iio_push_to_buffers_with_ts(indio_dev, adc->data, sizeof(adc->data), + iio_get_time_ns(indio_dev)); +out: + mutex_unlock(&adc->lock); + + iio_trigger_notify_done(indio_dev->trig); + + return IRQ_HANDLED; +} + +static irqreturn_t adc12138_eoc_handler(int irq, void *p) +{ + struct iio_dev *indio_dev = p; + struct adc12138 *adc = iio_priv(indio_dev); + + complete(&adc->complete); + + return IRQ_HANDLED; +} + +static int adc12138_probe(struct spi_device *spi) +{ + struct iio_dev *indio_dev; + struct adc12138 *adc; + struct clk *cclk; + int ret; + + indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*adc)); + if (!indio_dev) + return -ENOMEM; + + adc = iio_priv(indio_dev); + adc->spi = spi; + adc->id = spi_get_device_id(spi)->driver_data; + mutex_init(&adc->lock); + init_completion(&adc->complete); + + indio_dev->name = spi_get_device_id(spi)->name; + indio_dev->info = &adc12138_info; + indio_dev->modes = INDIO_DIRECT_MODE; + + switch (adc->id) { + case adc12130: + case adc12132: + indio_dev->channels = adc12132_channels; + indio_dev->num_channels = ARRAY_SIZE(adc12132_channels); + break; + case adc12138: + indio_dev->channels = adc12138_channels; + indio_dev->num_channels = ARRAY_SIZE(adc12138_channels); + break; + default: + return -EINVAL; + } + + ret = device_property_read_u32(&spi->dev, "ti,acquisition-time", + &adc->acquisition_time); + if (ret) + adc->acquisition_time = 10; + + ret = devm_request_irq(&spi->dev, spi->irq, adc12138_eoc_handler, + IRQF_TRIGGER_RISING, indio_dev->name, indio_dev); + if (ret) + return ret; + + cclk = devm_clk_get_enabled(&spi->dev, NULL); + if (IS_ERR(cclk)) + return PTR_ERR(cclk); + + adc->vref_p = devm_regulator_get(&spi->dev, "vref-p"); + if (IS_ERR(adc->vref_p)) + return PTR_ERR(adc->vref_p); + + adc->vref_n = devm_regulator_get_optional(&spi->dev, "vref-n"); + if (IS_ERR(adc->vref_n)) { + /* + * Assume vref_n is 0V if an optional regulator is not + * specified, otherwise return the error code. + */ + ret = PTR_ERR(adc->vref_n); + if (ret != -ENODEV) + return ret; + } + + ret = regulator_enable(adc->vref_p); + if (ret) + return ret; + + if (!IS_ERR(adc->vref_n)) { + ret = regulator_enable(adc->vref_n); + if (ret) + goto err_vref_p_disable; + } + + ret = adc12138_init(adc); + if (ret) + goto err_vref_n_disable; + + spi_set_drvdata(spi, indio_dev); + + ret = iio_triggered_buffer_setup(indio_dev, NULL, + adc12138_trigger_handler, NULL); + if (ret) + goto err_vref_n_disable; + + ret = iio_device_register(indio_dev); + if (ret) + goto err_buffer_cleanup; + + return 0; +err_buffer_cleanup: + iio_triggered_buffer_cleanup(indio_dev); +err_vref_n_disable: + if (!IS_ERR(adc->vref_n)) + regulator_disable(adc->vref_n); +err_vref_p_disable: + regulator_disable(adc->vref_p); + + return ret; +} + +static void adc12138_remove(struct spi_device *spi) +{ + struct iio_dev *indio_dev = spi_get_drvdata(spi); + struct adc12138 *adc = iio_priv(indio_dev); + + iio_device_unregister(indio_dev); + iio_triggered_buffer_cleanup(indio_dev); + if (!IS_ERR(adc->vref_n)) + regulator_disable(adc->vref_n); + regulator_disable(adc->vref_p); +} + +static const struct of_device_id adc12138_dt_ids[] = { + { .compatible = "ti,adc12130", }, + { .compatible = "ti,adc12132", }, + { .compatible = "ti,adc12138", }, + { } +}; +MODULE_DEVICE_TABLE(of, adc12138_dt_ids); + +static const struct spi_device_id adc12138_id[] = { + { .name = "adc12130", .driver_data = adc12130 }, + { .name = "adc12132", .driver_data = adc12132 }, + { .name = "adc12138", .driver_data = adc12138 }, + { } +}; +MODULE_DEVICE_TABLE(spi, adc12138_id); + +static struct spi_driver adc12138_driver = { + .driver = { + .name = "adc12138", + .of_match_table = adc12138_dt_ids, + }, + .probe = adc12138_probe, + .remove = adc12138_remove, + .id_table = adc12138_id, +}; +module_spi_driver(adc12138_driver); + +MODULE_AUTHOR("Akinobu Mita "); +MODULE_DESCRIPTION("ADC12130/ADC12132/ADC12138 driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/adc/ti-adc128s052.c b/drivers/iio/adc/ti-adc128s052.c new file mode 100644 index 000000000..1899813c1 --- /dev/null +++ b/drivers/iio/adc/ti-adc128s052.c @@ -0,0 +1,285 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2014 Angelo Compagnucci + * + * Driver for Texas Instruments' ADC128S052, ADC122S021 and ADC124S021 ADC chip. + * Datasheets can be found here: + * https://www.ti.com/lit/ds/symlink/adc128s052.pdf + * https://www.ti.com/lit/ds/symlink/adc122s021.pdf + * https://www.ti.com/lit/ds/symlink/adc124s021.pdf + */ + +#include +#include +#include +#include +#include +#include +#include + +struct adc128_configuration { + const struct iio_chan_spec *channels; + u8 num_channels; + const char *refname; + int num_other_regulators; + const char * const (*other_regulators)[]; +}; + +struct adc128 { + struct spi_device *spi; + + /* + * Serialize the SPI 'write-channel + read data' accesses and protect + * the shared buffer. + */ + struct mutex lock; + int vref_mv; + union { + __be16 buffer16; + u8 buffer[2]; + } __aligned(IIO_DMA_MINALIGN); +}; + +static int adc128_adc_conversion(struct adc128 *adc, u8 channel) +{ + int ret; + + guard(mutex)(&adc->lock); + + adc->buffer[0] = channel << 3; + adc->buffer[1] = 0; + + ret = spi_write(adc->spi, &adc->buffer, sizeof(adc->buffer)); + if (ret < 0) + return ret; + + ret = spi_read(adc->spi, &adc->buffer16, sizeof(adc->buffer16)); + if (ret < 0) + return ret; + + return be16_to_cpu(adc->buffer16) & 0xFFF; +} + +static int adc128_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *channel, int *val, + int *val2, long mask) +{ + struct adc128 *adc = iio_priv(indio_dev); + int ret; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + + ret = adc128_adc_conversion(adc, channel->channel); + if (ret < 0) + return ret; + + *val = ret; + return IIO_VAL_INT; + + case IIO_CHAN_INFO_SCALE: + + *val = adc->vref_mv; + *val2 = 12; + return IIO_VAL_FRACTIONAL_LOG2; + + default: + return -EINVAL; + } + +} + +#define ADC128_VOLTAGE_CHANNEL(num) \ + { \ + .type = IIO_VOLTAGE, \ + .indexed = 1, \ + .channel = (num), \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) \ + } + +static const struct iio_chan_spec simple_1chan_adc_channels[] = { + ADC128_VOLTAGE_CHANNEL(0), +}; + +static const struct iio_chan_spec simple_2chan_adc_channels[] = { + ADC128_VOLTAGE_CHANNEL(0), + ADC128_VOLTAGE_CHANNEL(1), +}; + +static const struct iio_chan_spec simple_4chan_adc_channels[] = { + ADC128_VOLTAGE_CHANNEL(0), + ADC128_VOLTAGE_CHANNEL(1), + ADC128_VOLTAGE_CHANNEL(2), + ADC128_VOLTAGE_CHANNEL(3), +}; + +static const struct iio_chan_spec simple_8chan_adc_channels[] = { + ADC128_VOLTAGE_CHANNEL(0), + ADC128_VOLTAGE_CHANNEL(1), + ADC128_VOLTAGE_CHANNEL(2), + ADC128_VOLTAGE_CHANNEL(3), + ADC128_VOLTAGE_CHANNEL(4), + ADC128_VOLTAGE_CHANNEL(5), + ADC128_VOLTAGE_CHANNEL(6), + ADC128_VOLTAGE_CHANNEL(7), +}; + +static const char * const bd79104_regulators[] = { "iovdd" }; + +static const struct adc128_configuration adc122s_config = { + .channels = simple_2chan_adc_channels, + .num_channels = ARRAY_SIZE(simple_2chan_adc_channels), + .refname = "vref", +}; + +static const struct adc128_configuration adc124s_config = { + .channels = simple_4chan_adc_channels, + .num_channels = ARRAY_SIZE(simple_4chan_adc_channels), + .refname = "vref", +}; + +static const struct adc128_configuration adc128s_config = { + .channels = simple_8chan_adc_channels, + .num_channels = ARRAY_SIZE(simple_8chan_adc_channels), + .refname = "vref", +}; + +static const struct adc128_configuration bd79100_config = { + .channels = simple_1chan_adc_channels, + .num_channels = ARRAY_SIZE(simple_1chan_adc_channels), + .refname = "vdd", + .other_regulators = &bd79104_regulators, + .num_other_regulators = 1, +}; + +static const struct adc128_configuration bd79101_config = { + .channels = simple_2chan_adc_channels, + .num_channels = ARRAY_SIZE(simple_2chan_adc_channels), + .refname = "vdd", + .other_regulators = &bd79104_regulators, + .num_other_regulators = 1, +}; + +static const struct adc128_configuration bd79102_config = { + .channels = simple_4chan_adc_channels, + .num_channels = ARRAY_SIZE(simple_4chan_adc_channels), + .refname = "vdd", + .other_regulators = &bd79104_regulators, + .num_other_regulators = 1, +}; + +static const struct adc128_configuration bd79104_config = { + .channels = simple_8chan_adc_channels, + .num_channels = ARRAY_SIZE(simple_8chan_adc_channels), + .refname = "vdd", + .other_regulators = &bd79104_regulators, + .num_other_regulators = 1, +}; + +static const struct iio_info adc128_info = { + .read_raw = adc128_read_raw, +}; + +static int adc128_probe(struct spi_device *spi) +{ + const struct adc128_configuration *config; + struct iio_dev *indio_dev; + struct adc128 *adc; + int ret; + + indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*adc)); + if (!indio_dev) + return -ENOMEM; + + adc = iio_priv(indio_dev); + adc->spi = spi; + + indio_dev->name = spi_get_device_id(spi)->name; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->info = &adc128_info; + + config = spi_get_device_match_data(spi); + + indio_dev->channels = config->channels; + indio_dev->num_channels = config->num_channels; + + ret = devm_regulator_get_enable_read_voltage(&spi->dev, + config->refname); + if (ret < 0) + return dev_err_probe(&spi->dev, ret, + "failed to read '%s' voltage", + config->refname); + + adc->vref_mv = ret / 1000; + + if (config->num_other_regulators) { + ret = devm_regulator_bulk_get_enable(&spi->dev, + config->num_other_regulators, + *config->other_regulators); + if (ret) + return dev_err_probe(&spi->dev, ret, + "Failed to enable regulators\n"); + } + + ret = devm_mutex_init(&spi->dev, &adc->lock); + if (ret) + return ret; + + return devm_iio_device_register(&spi->dev, indio_dev); +} + +static const struct of_device_id adc128_of_match[] = { + { .compatible = "ti,adc128s052", .data = &adc128s_config }, + { .compatible = "ti,adc122s021", .data = &adc122s_config }, + { .compatible = "ti,adc122s051", .data = &adc122s_config }, + { .compatible = "ti,adc122s101", .data = &adc122s_config }, + { .compatible = "ti,adc124s021", .data = &adc124s_config }, + { .compatible = "ti,adc124s051", .data = &adc124s_config }, + { .compatible = "ti,adc124s101", .data = &adc124s_config }, + { .compatible = "rohm,bd79100", .data = &bd79100_config }, + { .compatible = "rohm,bd79101", .data = &bd79101_config }, + { .compatible = "rohm,bd79102", .data = &bd79102_config }, + { .compatible = "rohm,bd79103", .data = &bd79104_config }, + { .compatible = "rohm,bd79104", .data = &bd79104_config }, + { } +}; +MODULE_DEVICE_TABLE(of, adc128_of_match); + +static const struct spi_device_id adc128_id[] = { + { .name = "adc128s052", .driver_data = (kernel_ulong_t)&adc128s_config }, + { .name = "adc122s021", .driver_data = (kernel_ulong_t)&adc122s_config }, + { .name = "adc122s051", .driver_data = (kernel_ulong_t)&adc122s_config }, + { .name = "adc122s101", .driver_data = (kernel_ulong_t)&adc122s_config }, + { .name = "adc124s021", .driver_data = (kernel_ulong_t)&adc124s_config }, + { .name = "adc124s051", .driver_data = (kernel_ulong_t)&adc124s_config }, + { .name = "adc124s101", .driver_data = (kernel_ulong_t)&adc124s_config }, + { .name = "bd79100", .driver_data = (kernel_ulong_t)&bd79100_config }, + { .name = "bd79101", .driver_data = (kernel_ulong_t)&bd79101_config }, + { .name = "bd79102", .driver_data = (kernel_ulong_t)&bd79102_config }, + { .name = "bd79103", .driver_data = (kernel_ulong_t)&bd79104_config }, + { .name = "bd79104", .driver_data = (kernel_ulong_t)&bd79104_config }, + { } +}; +MODULE_DEVICE_TABLE(spi, adc128_id); + +static const struct acpi_device_id adc128_acpi_match[] = { + { "AANT1280", (kernel_ulong_t)&adc124s_config }, + { } +}; +MODULE_DEVICE_TABLE(acpi, adc128_acpi_match); + +static struct spi_driver adc128_driver = { + .driver = { + .name = "adc128s052", + .of_match_table = adc128_of_match, + .acpi_match_table = adc128_acpi_match, + }, + .probe = adc128_probe, + .id_table = adc128_id, +}; +module_spi_driver(adc128_driver); + +MODULE_AUTHOR("Angelo Compagnucci "); +MODULE_DESCRIPTION("Texas Instruments ADC128S052"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/adc/ti-adc161s626.c b/drivers/iio/adc/ti-adc161s626.c new file mode 100644 index 000000000..422df0685 --- /dev/null +++ b/drivers/iio/adc/ti-adc161s626.c @@ -0,0 +1,250 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * ti-adc161s626.c - Texas Instruments ADC161S626 1-channel differential ADC + * + * ADC Devices Supported: + * adc141s626 - 14-bit ADC + * adc161s626 - 16-bit ADC + * + * Copyright (C) 2016-2018 + * Author: Matt Ranostay + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define TI_ADC_DRV_NAME "ti-adc161s626" + +enum { + TI_ADC141S626, + TI_ADC161S626, +}; + +static const struct iio_chan_spec ti_adc141s626_channels[] = { + { + .type = IIO_VOLTAGE, + .channel = 0, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SCALE) | + BIT(IIO_CHAN_INFO_OFFSET), + .scan_index = 0, + .scan_type = { + .sign = 's', + .realbits = 14, + .storagebits = 16, + }, + }, + IIO_CHAN_SOFT_TIMESTAMP(1), +}; + +static const struct iio_chan_spec ti_adc161s626_channels[] = { + { + .type = IIO_VOLTAGE, + .channel = 0, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SCALE) | + BIT(IIO_CHAN_INFO_OFFSET), + .scan_index = 0, + .scan_type = { + .sign = 's', + .realbits = 16, + .storagebits = 16, + }, + }, + IIO_CHAN_SOFT_TIMESTAMP(1), +}; + +struct ti_adc_data { + struct iio_dev *indio_dev; + struct spi_device *spi; + struct regulator *ref; + + u8 read_size; + u8 shift; + u8 buf[3] __aligned(IIO_DMA_MINALIGN); +}; + +static int ti_adc_read_measurement(struct ti_adc_data *data, + struct iio_chan_spec const *chan, int *val) +{ + int ret; + + switch (data->read_size) { + case 2: + ret = spi_read(data->spi, data->buf, 2); + if (ret) + return ret; + + *val = get_unaligned_be16(data->buf); + break; + case 3: + ret = spi_read(data->spi, data->buf, 3); + if (ret) + return ret; + + *val = get_unaligned_be24(data->buf); + break; + default: + return -EINVAL; + } + + *val = sign_extend32(*val >> data->shift, chan->scan_type.realbits - 1); + + return 0; +} + +static irqreturn_t ti_adc_trigger_handler(int irq, void *private) +{ + struct iio_poll_func *pf = private; + struct iio_dev *indio_dev = pf->indio_dev; + struct ti_adc_data *data = iio_priv(indio_dev); + struct { + s16 data; + aligned_s64 timestamp; + } scan = { }; + int ret, val; + + ret = ti_adc_read_measurement(data, &indio_dev->channels[0], &val); + if (ret) + goto exit_notify_done; + + scan.data = val; + iio_push_to_buffers_with_timestamp(indio_dev, &scan, iio_get_time_ns(indio_dev)); + + exit_notify_done: + iio_trigger_notify_done(indio_dev->trig); + + return IRQ_HANDLED; +} + +static int ti_adc_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct ti_adc_data *data = iio_priv(indio_dev); + int ret; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; + ret = ti_adc_read_measurement(data, chan, val); + iio_device_release_direct(indio_dev); + if (ret) + return ret; + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + ret = regulator_get_voltage(data->ref); + if (ret < 0) + return ret; + + *val = ret / 1000; + *val2 = chan->scan_type.realbits; + + return IIO_VAL_FRACTIONAL_LOG2; + case IIO_CHAN_INFO_OFFSET: + *val = 1 << (chan->scan_type.realbits - 1); + return IIO_VAL_INT; + } + + return 0; +} + +static const struct iio_info ti_adc_info = { + .read_raw = ti_adc_read_raw, +}; + +static void ti_adc_reg_disable(void *reg) +{ + regulator_disable(reg); +} + +static int ti_adc_probe(struct spi_device *spi) +{ + struct iio_dev *indio_dev; + struct ti_adc_data *data; + int ret; + + indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*data)); + if (!indio_dev) + return -ENOMEM; + + indio_dev->info = &ti_adc_info; + indio_dev->name = TI_ADC_DRV_NAME; + indio_dev->modes = INDIO_DIRECT_MODE; + + data = iio_priv(indio_dev); + data->spi = spi; + + switch (spi_get_device_id(spi)->driver_data) { + case TI_ADC141S626: + indio_dev->channels = ti_adc141s626_channels; + indio_dev->num_channels = ARRAY_SIZE(ti_adc141s626_channels); + data->shift = 0; + data->read_size = 2; + break; + case TI_ADC161S626: + indio_dev->channels = ti_adc161s626_channels; + indio_dev->num_channels = ARRAY_SIZE(ti_adc161s626_channels); + data->shift = 6; + data->read_size = 3; + break; + } + + data->ref = devm_regulator_get(&spi->dev, "vdda"); + if (IS_ERR(data->ref)) + return PTR_ERR(data->ref); + + ret = regulator_enable(data->ref); + if (ret < 0) + return ret; + + ret = devm_add_action_or_reset(&spi->dev, ti_adc_reg_disable, + data->ref); + if (ret) + return ret; + + ret = devm_iio_triggered_buffer_setup(&spi->dev, indio_dev, NULL, + ti_adc_trigger_handler, NULL); + if (ret) + return ret; + + return devm_iio_device_register(&spi->dev, indio_dev); +} + +static const struct of_device_id ti_adc_dt_ids[] = { + { .compatible = "ti,adc141s626", }, + { .compatible = "ti,adc161s626", }, + { } +}; +MODULE_DEVICE_TABLE(of, ti_adc_dt_ids); + +static const struct spi_device_id ti_adc_id[] = { + { .name = "adc141s626", .driver_data = TI_ADC141S626 }, + { .name = "adc161s626", .driver_data = TI_ADC161S626 }, + { } +}; +MODULE_DEVICE_TABLE(spi, ti_adc_id); + +static struct spi_driver ti_adc_driver = { + .driver = { + .name = TI_ADC_DRV_NAME, + .of_match_table = ti_adc_dt_ids, + }, + .probe = ti_adc_probe, + .id_table = ti_adc_id, +}; +module_spi_driver(ti_adc_driver); + +MODULE_AUTHOR("Matt Ranostay "); +MODULE_DESCRIPTION("Texas Instruments ADC1x1S 1-channel differential ADC"); +MODULE_LICENSE("GPL"); diff --git a/drivers/iio/adc/ti-ads1015.c b/drivers/iio/adc/ti-ads1015.c new file mode 100644 index 000000000..0fbfa4e49 --- /dev/null +++ b/drivers/iio/adc/ti-ads1015.c @@ -0,0 +1,1153 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * ADS1015 - Texas Instruments Analog-to-Digital Converter + * + * Copyright (c) 2016, Intel Corporation. + * + * IIO driver for ADS1015 ADC 7-bit I2C slave address: + * * 0x48 - ADDR connected to Ground + * * 0x49 - ADDR connected to Vdd + * * 0x4A - ADDR connected to SDA + * * 0x4B - ADDR connected to SCL + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#define ADS1015_DRV_NAME "ads1015" + +#define ADS1015_CHANNELS 8 + +#define ADS1015_CONV_REG 0x00 +#define ADS1015_CFG_REG 0x01 +#define ADS1015_LO_THRESH_REG 0x02 +#define ADS1015_HI_THRESH_REG 0x03 + +#define ADS1015_CFG_COMP_QUE_SHIFT 0 +#define ADS1015_CFG_COMP_LAT_SHIFT 2 +#define ADS1015_CFG_COMP_POL_SHIFT 3 +#define ADS1015_CFG_COMP_MODE_SHIFT 4 +#define ADS1015_CFG_DR_SHIFT 5 +#define ADS1015_CFG_MOD_SHIFT 8 +#define ADS1015_CFG_PGA_SHIFT 9 +#define ADS1015_CFG_MUX_SHIFT 12 + +#define ADS1015_CFG_COMP_QUE_MASK GENMASK(1, 0) +#define ADS1015_CFG_COMP_LAT_MASK BIT(2) +#define ADS1015_CFG_COMP_POL_MASK BIT(3) +#define ADS1015_CFG_COMP_MODE_MASK BIT(4) +#define ADS1015_CFG_DR_MASK GENMASK(7, 5) +#define ADS1015_CFG_MOD_MASK BIT(8) +#define ADS1015_CFG_PGA_MASK GENMASK(11, 9) +#define ADS1015_CFG_MUX_MASK GENMASK(14, 12) + +/* Comparator queue and disable field */ +#define ADS1015_CFG_COMP_DISABLE 3 + +/* Comparator polarity field */ +#define ADS1015_CFG_COMP_POL_LOW 0 +#define ADS1015_CFG_COMP_POL_HIGH 1 + +/* Comparator mode field */ +#define ADS1015_CFG_COMP_MODE_TRAD 0 +#define ADS1015_CFG_COMP_MODE_WINDOW 1 + +/* device operating modes */ +#define ADS1015_CONTINUOUS 0 +#define ADS1015_SINGLESHOT 1 + +#define ADS1015_SLEEP_DELAY_MS 2000 +#define ADS1015_DEFAULT_PGA 2 +#define ADS1015_DEFAULT_DATA_RATE 4 +#define ADS1015_DEFAULT_CHAN 0 + +struct ads1015_chip_data { + struct iio_chan_spec const *channels; + int num_channels; + const struct iio_info *info; + const int *data_rate; + const int data_rate_len; + const int *scale; + const int scale_len; + bool has_comparator; +}; + +enum ads1015_channels { + ADS1015_AIN0_AIN1 = 0, + ADS1015_AIN0_AIN3, + ADS1015_AIN1_AIN3, + ADS1015_AIN2_AIN3, + ADS1015_AIN0, + ADS1015_AIN1, + ADS1015_AIN2, + ADS1015_AIN3, + ADS1015_TIMESTAMP, +}; + +static const int ads1015_data_rate[] = { + 128, 250, 490, 920, 1600, 2400, 3300, 3300 +}; + +static const int ads1115_data_rate[] = { + 8, 16, 32, 64, 128, 250, 475, 860 +}; + +/* + * Translation from PGA bits to full-scale positive and negative input voltage + * range in mV + */ +static const int ads1015_fullscale_range[] = { + 6144, 4096, 2048, 1024, 512, 256, 256, 256 +}; + +static const int ads1015_scale[] = { /* 12bit ADC */ + 256, 11, + 512, 11, + 1024, 11, + 2048, 11, + 4096, 11, + 6144, 11 +}; + +static const int ads1115_scale[] = { /* 16bit ADC */ + 256, 15, + 512, 15, + 1024, 15, + 2048, 15, + 4096, 15, + 6144, 15 +}; + +/* + * Translation from COMP_QUE field value to the number of successive readings + * exceed the threshold values before an interrupt is generated + */ +static const int ads1015_comp_queue[] = { 1, 2, 4 }; + +static const struct iio_event_spec ads1015_events[] = { + { + .type = IIO_EV_TYPE_THRESH, + .dir = IIO_EV_DIR_RISING, + .mask_separate = BIT(IIO_EV_INFO_VALUE) | + BIT(IIO_EV_INFO_ENABLE), + }, { + .type = IIO_EV_TYPE_THRESH, + .dir = IIO_EV_DIR_FALLING, + .mask_separate = BIT(IIO_EV_INFO_VALUE), + }, { + .type = IIO_EV_TYPE_THRESH, + .dir = IIO_EV_DIR_EITHER, + .mask_separate = BIT(IIO_EV_INFO_ENABLE) | + BIT(IIO_EV_INFO_PERIOD), + }, +}; + +/* + * Compile-time check whether _fitbits can accommodate up to _testbits + * bits. Returns _fitbits on success, fails to compile otherwise. + * + * The test works such that it multiplies constant _fitbits by constant + * double-negation of size of a non-empty structure, i.e. it multiplies + * constant _fitbits by constant 1 in each successful compilation case. + * The non-empty structure may contain C11 _Static_assert(), make use of + * this and place the kernel variant of static assert in there, so that + * it performs the compile-time check for _testbits <= _fitbits. Note + * that it is not possible to directly use static_assert in compound + * statements, hence this convoluted construct. + */ +#define FIT_CHECK(_testbits, _fitbits) \ + ( \ + (_fitbits) * \ + !!sizeof(struct { \ + static_assert((_testbits) <= (_fitbits)); \ + int pad; \ + }) \ + ) + +#define ADS1015_V_CHAN(_chan, _addr, _realbits, _shift, _event_spec, _num_event_specs) { \ + .type = IIO_VOLTAGE, \ + .indexed = 1, \ + .address = _addr, \ + .channel = _chan, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ + BIT(IIO_CHAN_INFO_SCALE) | \ + BIT(IIO_CHAN_INFO_SAMP_FREQ), \ + .info_mask_shared_by_all_available = \ + BIT(IIO_CHAN_INFO_SCALE) | \ + BIT(IIO_CHAN_INFO_SAMP_FREQ), \ + .scan_index = _addr, \ + .scan_type = { \ + .sign = 's', \ + .realbits = (_realbits), \ + .storagebits = FIT_CHECK((_realbits) + (_shift), 16), \ + .shift = (_shift), \ + .endianness = IIO_CPU, \ + }, \ + .event_spec = (_event_spec), \ + .num_event_specs = (_num_event_specs), \ + .datasheet_name = "AIN"#_chan, \ +} + +#define ADS1015_V_DIFF_CHAN(_chan, _chan2, _addr, _realbits, _shift, _event_spec, _num_event_specs) { \ + .type = IIO_VOLTAGE, \ + .differential = 1, \ + .indexed = 1, \ + .address = _addr, \ + .channel = _chan, \ + .channel2 = _chan2, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ + BIT(IIO_CHAN_INFO_SCALE) | \ + BIT(IIO_CHAN_INFO_SAMP_FREQ), \ + .info_mask_shared_by_all_available = \ + BIT(IIO_CHAN_INFO_SCALE) | \ + BIT(IIO_CHAN_INFO_SAMP_FREQ), \ + .scan_index = _addr, \ + .scan_type = { \ + .sign = 's', \ + .realbits = (_realbits), \ + .storagebits = FIT_CHECK((_realbits) + (_shift), 16), \ + .shift = (_shift), \ + .endianness = IIO_CPU, \ + }, \ + .event_spec = (_event_spec), \ + .num_event_specs = (_num_event_specs), \ + .datasheet_name = "AIN"#_chan"-AIN"#_chan2, \ +} + +struct ads1015_channel_data { + unsigned int pga; + unsigned int data_rate; +}; + +struct ads1015_thresh_data { + unsigned int comp_queue; + int high_thresh; + int low_thresh; +}; + +struct ads1015_data { + struct regmap *regmap; + /* + * Protects ADC ops, e.g: concurrent sysfs/buffered + * data reads, configuration updates + */ + struct mutex lock; + struct ads1015_channel_data channel_data[ADS1015_CHANNELS]; + + unsigned int event_channel; + unsigned int comp_mode; + struct ads1015_thresh_data thresh_data[ADS1015_CHANNELS]; + + const struct ads1015_chip_data *chip; + /* + * Set to true when the ADC is switched to the continuous-conversion + * mode and exits from a power-down state. This flag is used to avoid + * getting the stale result from the conversion register. + */ + bool conv_invalid; +}; + +static bool ads1015_event_channel_enabled(struct ads1015_data *data) +{ + return (data->event_channel != ADS1015_CHANNELS); +} + +static void ads1015_event_channel_enable(struct ads1015_data *data, int chan, + int comp_mode) +{ + WARN_ON(ads1015_event_channel_enabled(data)); + + data->event_channel = chan; + data->comp_mode = comp_mode; +} + +static void ads1015_event_channel_disable(struct ads1015_data *data, int chan) +{ + data->event_channel = ADS1015_CHANNELS; +} + +static const struct regmap_range ads1015_writeable_ranges[] = { + regmap_reg_range(ADS1015_CFG_REG, ADS1015_HI_THRESH_REG), +}; + +static const struct regmap_access_table ads1015_writeable_table = { + .yes_ranges = ads1015_writeable_ranges, + .n_yes_ranges = ARRAY_SIZE(ads1015_writeable_ranges), +}; + +static const struct regmap_config ads1015_regmap_config = { + .reg_bits = 8, + .val_bits = 16, + .max_register = ADS1015_HI_THRESH_REG, + .wr_table = &ads1015_writeable_table, +}; + +static const struct regmap_range tla2024_writeable_ranges[] = { + regmap_reg_range(ADS1015_CFG_REG, ADS1015_CFG_REG), +}; + +static const struct regmap_access_table tla2024_writeable_table = { + .yes_ranges = tla2024_writeable_ranges, + .n_yes_ranges = ARRAY_SIZE(tla2024_writeable_ranges), +}; + +static const struct regmap_config tla2024_regmap_config = { + .reg_bits = 8, + .val_bits = 16, + .max_register = ADS1015_CFG_REG, + .wr_table = &tla2024_writeable_table, +}; + +static const struct iio_chan_spec ads1015_channels[] = { + ADS1015_V_DIFF_CHAN(0, 1, ADS1015_AIN0_AIN1, 12, 4, + ads1015_events, ARRAY_SIZE(ads1015_events)), + ADS1015_V_DIFF_CHAN(0, 3, ADS1015_AIN0_AIN3, 12, 4, + ads1015_events, ARRAY_SIZE(ads1015_events)), + ADS1015_V_DIFF_CHAN(1, 3, ADS1015_AIN1_AIN3, 12, 4, + ads1015_events, ARRAY_SIZE(ads1015_events)), + ADS1015_V_DIFF_CHAN(2, 3, ADS1015_AIN2_AIN3, 12, 4, + ads1015_events, ARRAY_SIZE(ads1015_events)), + ADS1015_V_CHAN(0, ADS1015_AIN0, 12, 4, + ads1015_events, ARRAY_SIZE(ads1015_events)), + ADS1015_V_CHAN(1, ADS1015_AIN1, 12, 4, + ads1015_events, ARRAY_SIZE(ads1015_events)), + ADS1015_V_CHAN(2, ADS1015_AIN2, 12, 4, + ads1015_events, ARRAY_SIZE(ads1015_events)), + ADS1015_V_CHAN(3, ADS1015_AIN3, 12, 4, + ads1015_events, ARRAY_SIZE(ads1015_events)), + IIO_CHAN_SOFT_TIMESTAMP(ADS1015_TIMESTAMP), +}; + +static const struct iio_chan_spec ads1115_channels[] = { + ADS1015_V_DIFF_CHAN(0, 1, ADS1015_AIN0_AIN1, 16, 0, + ads1015_events, ARRAY_SIZE(ads1015_events)), + ADS1015_V_DIFF_CHAN(0, 3, ADS1015_AIN0_AIN3, 16, 0, + ads1015_events, ARRAY_SIZE(ads1015_events)), + ADS1015_V_DIFF_CHAN(1, 3, ADS1015_AIN1_AIN3, 16, 0, + ads1015_events, ARRAY_SIZE(ads1015_events)), + ADS1015_V_DIFF_CHAN(2, 3, ADS1015_AIN2_AIN3, 16, 0, + ads1015_events, ARRAY_SIZE(ads1015_events)), + ADS1015_V_CHAN(0, ADS1015_AIN0, 16, 0, + ads1015_events, ARRAY_SIZE(ads1015_events)), + ADS1015_V_CHAN(1, ADS1015_AIN1, 16, 0, + ads1015_events, ARRAY_SIZE(ads1015_events)), + ADS1015_V_CHAN(2, ADS1015_AIN2, 16, 0, + ads1015_events, ARRAY_SIZE(ads1015_events)), + ADS1015_V_CHAN(3, ADS1015_AIN3, 16, 0, + ads1015_events, ARRAY_SIZE(ads1015_events)), + IIO_CHAN_SOFT_TIMESTAMP(ADS1015_TIMESTAMP), +}; + +static const struct iio_chan_spec tla2024_channels[] = { + ADS1015_V_DIFF_CHAN(0, 1, ADS1015_AIN0_AIN1, 12, 4, NULL, 0), + ADS1015_V_DIFF_CHAN(0, 3, ADS1015_AIN0_AIN3, 12, 4, NULL, 0), + ADS1015_V_DIFF_CHAN(1, 3, ADS1015_AIN1_AIN3, 12, 4, NULL, 0), + ADS1015_V_DIFF_CHAN(2, 3, ADS1015_AIN2_AIN3, 12, 4, NULL, 0), + ADS1015_V_CHAN(0, ADS1015_AIN0, 12, 4, NULL, 0), + ADS1015_V_CHAN(1, ADS1015_AIN1, 12, 4, NULL, 0), + ADS1015_V_CHAN(2, ADS1015_AIN2, 12, 4, NULL, 0), + ADS1015_V_CHAN(3, ADS1015_AIN3, 12, 4, NULL, 0), + IIO_CHAN_SOFT_TIMESTAMP(ADS1015_TIMESTAMP), +}; + + +#ifdef CONFIG_PM +static int ads1015_set_power_state(struct ads1015_data *data, bool on) +{ + int ret; + struct device *dev = regmap_get_device(data->regmap); + + if (on) + ret = pm_runtime_resume_and_get(dev); + else + ret = pm_runtime_put_autosuspend(dev); + + return ret < 0 ? ret : 0; +} + +#else /* !CONFIG_PM */ + +static int ads1015_set_power_state(struct ads1015_data *data, bool on) +{ + return 0; +} + +#endif /* !CONFIG_PM */ + +static +int ads1015_get_adc_result(struct ads1015_data *data, int chan, int *val) +{ + const int *data_rate = data->chip->data_rate; + int ret, pga, dr, dr_old, conv_time; + unsigned int old, mask, cfg; + + if (chan < 0 || chan >= ADS1015_CHANNELS) + return -EINVAL; + + ret = regmap_read(data->regmap, ADS1015_CFG_REG, &old); + if (ret) + return ret; + + pga = data->channel_data[chan].pga; + dr = data->channel_data[chan].data_rate; + mask = ADS1015_CFG_MUX_MASK | ADS1015_CFG_PGA_MASK | + ADS1015_CFG_DR_MASK; + cfg = chan << ADS1015_CFG_MUX_SHIFT | pga << ADS1015_CFG_PGA_SHIFT | + dr << ADS1015_CFG_DR_SHIFT; + + if (ads1015_event_channel_enabled(data)) { + mask |= ADS1015_CFG_COMP_QUE_MASK | ADS1015_CFG_COMP_MODE_MASK; + cfg |= data->thresh_data[chan].comp_queue << + ADS1015_CFG_COMP_QUE_SHIFT | + data->comp_mode << + ADS1015_CFG_COMP_MODE_SHIFT; + } + + cfg = (old & ~mask) | (cfg & mask); + if (old != cfg) { + ret = regmap_write(data->regmap, ADS1015_CFG_REG, cfg); + if (ret) + return ret; + data->conv_invalid = true; + } + if (data->conv_invalid) { + dr_old = (old & ADS1015_CFG_DR_MASK) >> ADS1015_CFG_DR_SHIFT; + conv_time = DIV_ROUND_UP(USEC_PER_SEC, data_rate[dr_old]); + conv_time += DIV_ROUND_UP(USEC_PER_SEC, data_rate[dr]); + conv_time += conv_time / 10; /* 10% internal clock inaccuracy */ + usleep_range(conv_time, conv_time + 1); + data->conv_invalid = false; + } + + return regmap_read(data->regmap, ADS1015_CONV_REG, val); +} + +static irqreturn_t ads1015_trigger_handler(int irq, void *p) +{ + struct iio_poll_func *pf = p; + struct iio_dev *indio_dev = pf->indio_dev; + struct ads1015_data *data = iio_priv(indio_dev); + /* Ensure natural alignment of timestamp */ + struct { + s16 chan; + aligned_s64 timestamp; + } scan = { }; + int chan, ret, res; + + mutex_lock(&data->lock); + chan = find_first_bit(indio_dev->active_scan_mask, + iio_get_masklength(indio_dev)); + ret = ads1015_get_adc_result(data, chan, &res); + if (ret < 0) { + mutex_unlock(&data->lock); + goto err; + } + + scan.chan = res; + mutex_unlock(&data->lock); + + iio_push_to_buffers_with_ts(indio_dev, &scan, sizeof(scan), + iio_get_time_ns(indio_dev)); + +err: + iio_trigger_notify_done(indio_dev->trig); + + return IRQ_HANDLED; +} + +static int ads1015_set_scale(struct ads1015_data *data, + struct iio_chan_spec const *chan, + int scale, int uscale) +{ + int i; + int fullscale = div_s64((scale * 1000000LL + uscale) << + (chan->scan_type.realbits - 1), 1000000); + + for (i = 0; i < ARRAY_SIZE(ads1015_fullscale_range); i++) { + if (ads1015_fullscale_range[i] == fullscale) { + data->channel_data[chan->address].pga = i; + return 0; + } + } + + return -EINVAL; +} + +static int ads1015_set_data_rate(struct ads1015_data *data, int chan, int rate) +{ + int i; + + for (i = 0; i < data->chip->data_rate_len; i++) { + if (data->chip->data_rate[i] == rate) { + data->channel_data[chan].data_rate = i; + return 0; + } + } + + return -EINVAL; +} + +static int ads1015_read_avail(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + const int **vals, int *type, int *length, + long mask) +{ + struct ads1015_data *data = iio_priv(indio_dev); + + if (chan->type != IIO_VOLTAGE) + return -EINVAL; + + switch (mask) { + case IIO_CHAN_INFO_SCALE: + *type = IIO_VAL_FRACTIONAL_LOG2; + *vals = data->chip->scale; + *length = data->chip->scale_len; + return IIO_AVAIL_LIST; + case IIO_CHAN_INFO_SAMP_FREQ: + *type = IIO_VAL_INT; + *vals = data->chip->data_rate; + *length = data->chip->data_rate_len; + return IIO_AVAIL_LIST; + default: + return -EINVAL; + } +} + +static int __ads1015_read_info_raw(struct ads1015_data *data, + struct iio_chan_spec const *chan, int *val) +{ + int ret; + + if (ads1015_event_channel_enabled(data) && + data->event_channel != chan->address) + return -EBUSY; + + ret = ads1015_set_power_state(data, true); + if (ret < 0) + return ret; + + ret = ads1015_get_adc_result(data, chan->address, val); + if (ret < 0) { + ads1015_set_power_state(data, false); + return ret; + } + + *val = sign_extend32(*val >> chan->scan_type.shift, + chan->scan_type.realbits - 1); + + return ads1015_set_power_state(data, false); +} + +static int ads1015_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int *val, + int *val2, long mask) +{ + int ret, idx; + struct ads1015_data *data = iio_priv(indio_dev); + + guard(mutex)(&data->lock); + switch (mask) { + case IIO_CHAN_INFO_RAW: + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; + ret = __ads1015_read_info_raw(data, chan, val); + iio_device_release_direct(indio_dev); + if (ret) + return ret; + + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + idx = data->channel_data[chan->address].pga; + *val = ads1015_fullscale_range[idx]; + *val2 = chan->scan_type.realbits - 1; + return IIO_VAL_FRACTIONAL_LOG2; + case IIO_CHAN_INFO_SAMP_FREQ: + idx = data->channel_data[chan->address].data_rate; + *val = data->chip->data_rate[idx]; + return IIO_VAL_INT; + default: + return -EINVAL; + } +} + +static int ads1015_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int val, + int val2, long mask) +{ + struct ads1015_data *data = iio_priv(indio_dev); + + guard(mutex)(&data->lock); + switch (mask) { + case IIO_CHAN_INFO_SCALE: + return ads1015_set_scale(data, chan, val, val2); + case IIO_CHAN_INFO_SAMP_FREQ: + return ads1015_set_data_rate(data, chan->address, val); + default: + return -EINVAL; + } +} + +static int ads1015_read_event(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, enum iio_event_type type, + enum iio_event_direction dir, enum iio_event_info info, int *val, + int *val2) +{ + struct ads1015_data *data = iio_priv(indio_dev); + unsigned int comp_queue; + int period; + int dr; + + guard(mutex)(&data->lock); + + switch (info) { + case IIO_EV_INFO_VALUE: + *val = (dir == IIO_EV_DIR_RISING) ? + data->thresh_data[chan->address].high_thresh : + data->thresh_data[chan->address].low_thresh; + return IIO_VAL_INT; + case IIO_EV_INFO_PERIOD: + dr = data->channel_data[chan->address].data_rate; + comp_queue = data->thresh_data[chan->address].comp_queue; + period = ads1015_comp_queue[comp_queue] * + USEC_PER_SEC / data->chip->data_rate[dr]; + + *val = period / USEC_PER_SEC; + *val2 = period % USEC_PER_SEC; + return IIO_VAL_INT_PLUS_MICRO; + default: + return -EINVAL; + } +} + +static int ads1015_write_event(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, enum iio_event_type type, + enum iio_event_direction dir, enum iio_event_info info, int val, + int val2) +{ + struct ads1015_data *data = iio_priv(indio_dev); + const int *data_rate = data->chip->data_rate; + int realbits = chan->scan_type.realbits; + long long period; + int i; + int dr; + + guard(mutex)(&data->lock); + + switch (info) { + case IIO_EV_INFO_VALUE: + if (val >= 1 << (realbits - 1) || val < -1 << (realbits - 1)) + return -EINVAL; + + if (dir == IIO_EV_DIR_RISING) + data->thresh_data[chan->address].high_thresh = val; + else + data->thresh_data[chan->address].low_thresh = val; + return 0; + case IIO_EV_INFO_PERIOD: + dr = data->channel_data[chan->address].data_rate; + period = val * USEC_PER_SEC + val2; + + for (i = 0; i < ARRAY_SIZE(ads1015_comp_queue) - 1; i++) { + if (period <= ads1015_comp_queue[i] * + USEC_PER_SEC / data_rate[dr]) + break; + } + data->thresh_data[chan->address].comp_queue = i; + return 0; + default: + return -EINVAL; + } +} + +static int ads1015_read_event_config(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, enum iio_event_type type, + enum iio_event_direction dir) +{ + struct ads1015_data *data = iio_priv(indio_dev); + + guard(mutex)(&data->lock); + if (data->event_channel != chan->address) + return 0; + + switch (dir) { + case IIO_EV_DIR_RISING: + return 1; + case IIO_EV_DIR_EITHER: + return (data->comp_mode == ADS1015_CFG_COMP_MODE_WINDOW); + default: + return -EINVAL; + } +} + +static int ads1015_enable_event_config(struct ads1015_data *data, + const struct iio_chan_spec *chan, int comp_mode) +{ + int low_thresh = data->thresh_data[chan->address].low_thresh; + int high_thresh = data->thresh_data[chan->address].high_thresh; + int ret; + unsigned int val; + + if (ads1015_event_channel_enabled(data)) { + if (data->event_channel != chan->address || + (data->comp_mode == ADS1015_CFG_COMP_MODE_TRAD && + comp_mode == ADS1015_CFG_COMP_MODE_WINDOW)) + return -EBUSY; + + return 0; + } + + if (comp_mode == ADS1015_CFG_COMP_MODE_TRAD) { + low_thresh = max(-1 << (chan->scan_type.realbits - 1), + high_thresh - 1); + } + ret = regmap_write(data->regmap, ADS1015_LO_THRESH_REG, + low_thresh << chan->scan_type.shift); + if (ret) + return ret; + + ret = regmap_write(data->regmap, ADS1015_HI_THRESH_REG, + high_thresh << chan->scan_type.shift); + if (ret) + return ret; + + ret = ads1015_set_power_state(data, true); + if (ret < 0) + return ret; + + ads1015_event_channel_enable(data, chan->address, comp_mode); + + ret = ads1015_get_adc_result(data, chan->address, &val); + if (ret) { + ads1015_event_channel_disable(data, chan->address); + ads1015_set_power_state(data, false); + } + + return ret; +} + +static int ads1015_disable_event_config(struct ads1015_data *data, + const struct iio_chan_spec *chan, int comp_mode) +{ + int ret; + + if (!ads1015_event_channel_enabled(data)) + return 0; + + if (data->event_channel != chan->address) + return 0; + + if (data->comp_mode == ADS1015_CFG_COMP_MODE_TRAD && + comp_mode == ADS1015_CFG_COMP_MODE_WINDOW) + return 0; + + ret = regmap_update_bits(data->regmap, ADS1015_CFG_REG, + ADS1015_CFG_COMP_QUE_MASK, + ADS1015_CFG_COMP_DISABLE << + ADS1015_CFG_COMP_QUE_SHIFT); + if (ret) + return ret; + + ads1015_event_channel_disable(data, chan->address); + + return ads1015_set_power_state(data, false); +} + +static int ads1015_write_event_config(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, enum iio_event_type type, + enum iio_event_direction dir, bool state) +{ + struct ads1015_data *data = iio_priv(indio_dev); + int ret; + int comp_mode = (dir == IIO_EV_DIR_EITHER) ? + ADS1015_CFG_COMP_MODE_WINDOW : ADS1015_CFG_COMP_MODE_TRAD; + + guard(mutex)(&data->lock); + + /* Prevent from enabling both buffer and event at a time */ + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; + + if (state) + ret = ads1015_enable_event_config(data, chan, comp_mode); + else + ret = ads1015_disable_event_config(data, chan, comp_mode); + + iio_device_release_direct(indio_dev); + return ret; +} + +static irqreturn_t ads1015_event_handler(int irq, void *priv) +{ + struct iio_dev *indio_dev = priv; + struct ads1015_data *data = iio_priv(indio_dev); + int val; + int ret; + + /* Clear the latched ALERT/RDY pin */ + ret = regmap_read(data->regmap, ADS1015_CONV_REG, &val); + if (ret) + return IRQ_HANDLED; + + if (ads1015_event_channel_enabled(data)) { + enum iio_event_direction dir; + u64 code; + + dir = data->comp_mode == ADS1015_CFG_COMP_MODE_TRAD ? + IIO_EV_DIR_RISING : IIO_EV_DIR_EITHER; + code = IIO_UNMOD_EVENT_CODE(IIO_VOLTAGE, data->event_channel, + IIO_EV_TYPE_THRESH, dir); + iio_push_event(indio_dev, code, iio_get_time_ns(indio_dev)); + } + + return IRQ_HANDLED; +} + +static int ads1015_buffer_preenable(struct iio_dev *indio_dev) +{ + struct ads1015_data *data = iio_priv(indio_dev); + + /* Prevent from enabling both buffer and event at a time */ + if (ads1015_event_channel_enabled(data)) + return -EBUSY; + + return ads1015_set_power_state(iio_priv(indio_dev), true); +} + +static int ads1015_buffer_postdisable(struct iio_dev *indio_dev) +{ + return ads1015_set_power_state(iio_priv(indio_dev), false); +} + +static const struct iio_buffer_setup_ops ads1015_buffer_setup_ops = { + .preenable = ads1015_buffer_preenable, + .postdisable = ads1015_buffer_postdisable, + .validate_scan_mask = &iio_validate_scan_mask_onehot, +}; + +static const struct iio_info ads1015_info = { + .read_avail = ads1015_read_avail, + .read_raw = ads1015_read_raw, + .write_raw = ads1015_write_raw, + .read_event_value = ads1015_read_event, + .write_event_value = ads1015_write_event, + .read_event_config = ads1015_read_event_config, + .write_event_config = ads1015_write_event_config, +}; + +static const struct iio_info tla2024_info = { + .read_avail = ads1015_read_avail, + .read_raw = ads1015_read_raw, + .write_raw = ads1015_write_raw, +}; + +static int ads1015_client_get_channels_config(struct i2c_client *client) +{ + struct iio_dev *indio_dev = i2c_get_clientdata(client); + struct ads1015_data *data = iio_priv(indio_dev); + struct device *dev = &client->dev; + int i = -1; + + device_for_each_child_node_scoped(dev, node) { + u32 pval; + unsigned int channel; + unsigned int pga = ADS1015_DEFAULT_PGA; + unsigned int data_rate = ADS1015_DEFAULT_DATA_RATE; + + if (fwnode_property_read_u32(node, "reg", &pval)) { + dev_err(dev, "invalid reg on %pfw\n", node); + continue; + } + + channel = pval; + if (channel >= ADS1015_CHANNELS) { + dev_err(dev, "invalid channel index %d on %pfw\n", + channel, node); + continue; + } + + if (!fwnode_property_read_u32(node, "ti,gain", &pval)) { + pga = pval; + if (pga > 5) { + dev_err(dev, "invalid gain on %pfw\n", node); + return -EINVAL; + } + } + + if (!fwnode_property_read_u32(node, "ti,datarate", &pval)) { + data_rate = pval; + if (data_rate > 7) { + dev_err(dev, "invalid data_rate on %pfw\n", node); + return -EINVAL; + } + } + + data->channel_data[channel].pga = pga; + data->channel_data[channel].data_rate = data_rate; + + i++; + } + + return i < 0 ? -EINVAL : 0; +} + +static void ads1015_get_channels_config(struct i2c_client *client) +{ + unsigned int k; + + struct iio_dev *indio_dev = i2c_get_clientdata(client); + struct ads1015_data *data = iio_priv(indio_dev); + + if (!ads1015_client_get_channels_config(client)) + return; + + /* fallback on default configuration */ + for (k = 0; k < ADS1015_CHANNELS; ++k) { + data->channel_data[k].pga = ADS1015_DEFAULT_PGA; + data->channel_data[k].data_rate = ADS1015_DEFAULT_DATA_RATE; + } +} + +static int ads1015_set_conv_mode(struct ads1015_data *data, int mode) +{ + return regmap_update_bits(data->regmap, ADS1015_CFG_REG, + ADS1015_CFG_MOD_MASK, + mode << ADS1015_CFG_MOD_SHIFT); +} + +static void ads1015_power_down(void *p) +{ + struct ads1015_data *data = p; + int ret; + + ret = ads1015_set_conv_mode(data, ADS1015_SINGLESHOT); + if (ret) + dev_warn(regmap_get_device(data->regmap), + "Failed to power down (%pe)\n", ERR_PTR(ret)); +} + +static int ads1015_probe(struct i2c_client *client) +{ + const struct ads1015_chip_data *chip; + struct iio_dev *indio_dev; + struct ads1015_data *data; + int ret; + int i; + + chip = i2c_get_match_data(client); + if (!chip) + return dev_err_probe(&client->dev, -EINVAL, "Unknown chip\n"); + + indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data)); + if (!indio_dev) + return -ENOMEM; + + data = iio_priv(indio_dev); + i2c_set_clientdata(client, indio_dev); + + mutex_init(&data->lock); + + indio_dev->name = ADS1015_DRV_NAME; + indio_dev->modes = INDIO_DIRECT_MODE; + + indio_dev->channels = chip->channels; + indio_dev->num_channels = chip->num_channels; + indio_dev->info = chip->info; + data->chip = chip; + data->event_channel = ADS1015_CHANNELS; + + /* + * Set default lower and upper threshold to min and max value + * respectively. + */ + for (i = 0; i < ADS1015_CHANNELS; i++) { + int realbits = indio_dev->channels[i].scan_type.realbits; + + data->thresh_data[i].low_thresh = -1 << (realbits - 1); + data->thresh_data[i].high_thresh = (1 << (realbits - 1)) - 1; + } + + /* we need to keep this ABI the same as used by hwmon ADS1015 driver */ + ads1015_get_channels_config(client); + + data->regmap = devm_regmap_init_i2c(client, chip->has_comparator ? + &ads1015_regmap_config : + &tla2024_regmap_config); + if (IS_ERR(data->regmap)) { + dev_err(&client->dev, "Failed to allocate register map\n"); + return PTR_ERR(data->regmap); + } + + ret = devm_iio_triggered_buffer_setup(&client->dev, indio_dev, NULL, + ads1015_trigger_handler, + &ads1015_buffer_setup_ops); + if (ret < 0) { + dev_err(&client->dev, "iio triggered buffer setup failed\n"); + return ret; + } + + if (client->irq && chip->has_comparator) { + unsigned long irq_trig = irq_get_trigger_type(client->irq); + unsigned int cfg_comp_mask = ADS1015_CFG_COMP_QUE_MASK | + ADS1015_CFG_COMP_LAT_MASK | ADS1015_CFG_COMP_POL_MASK; + unsigned int cfg_comp = + ADS1015_CFG_COMP_DISABLE << ADS1015_CFG_COMP_QUE_SHIFT | + 1 << ADS1015_CFG_COMP_LAT_SHIFT; + + switch (irq_trig) { + case IRQF_TRIGGER_FALLING: + case IRQF_TRIGGER_LOW: + cfg_comp |= ADS1015_CFG_COMP_POL_LOW << + ADS1015_CFG_COMP_POL_SHIFT; + break; + case IRQF_TRIGGER_HIGH: + case IRQF_TRIGGER_RISING: + cfg_comp |= ADS1015_CFG_COMP_POL_HIGH << + ADS1015_CFG_COMP_POL_SHIFT; + break; + default: + return -EINVAL; + } + + ret = regmap_update_bits(data->regmap, ADS1015_CFG_REG, + cfg_comp_mask, cfg_comp); + if (ret) + return ret; + + ret = devm_request_threaded_irq(&client->dev, client->irq, + NULL, ads1015_event_handler, + irq_trig | IRQF_ONESHOT, + client->name, indio_dev); + if (ret) + return ret; + } + + ret = ads1015_set_conv_mode(data, ADS1015_CONTINUOUS); + if (ret) + return ret; + + ret = devm_add_action_or_reset(&client->dev, ads1015_power_down, data); + if (ret) + return ret; + + data->conv_invalid = true; + + ret = pm_runtime_set_active(&client->dev); + if (ret) + return ret; + pm_runtime_set_autosuspend_delay(&client->dev, ADS1015_SLEEP_DELAY_MS); + pm_runtime_use_autosuspend(&client->dev); + ret = devm_pm_runtime_enable(&client->dev); + if (ret) + return ret; + + return devm_iio_device_register(&client->dev, indio_dev); +} + +#ifdef CONFIG_PM +static int ads1015_runtime_suspend(struct device *dev) +{ + struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev)); + struct ads1015_data *data = iio_priv(indio_dev); + + return ads1015_set_conv_mode(data, ADS1015_SINGLESHOT); +} + +static int ads1015_runtime_resume(struct device *dev) +{ + struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev)); + struct ads1015_data *data = iio_priv(indio_dev); + int ret; + + ret = ads1015_set_conv_mode(data, ADS1015_CONTINUOUS); + if (!ret) + data->conv_invalid = true; + + return ret; +} +#endif + +static const struct dev_pm_ops ads1015_pm_ops = { + SET_RUNTIME_PM_OPS(ads1015_runtime_suspend, + ads1015_runtime_resume, NULL) +}; + +static const struct ads1015_chip_data ads1015_data = { + .channels = ads1015_channels, + .num_channels = ARRAY_SIZE(ads1015_channels), + .info = &ads1015_info, + .data_rate = ads1015_data_rate, + .data_rate_len = ARRAY_SIZE(ads1015_data_rate), + .scale = ads1015_scale, + .scale_len = ARRAY_SIZE(ads1015_scale), + .has_comparator = true, +}; + +static const struct ads1015_chip_data ads1115_data = { + .channels = ads1115_channels, + .num_channels = ARRAY_SIZE(ads1115_channels), + .info = &ads1015_info, + .data_rate = ads1115_data_rate, + .data_rate_len = ARRAY_SIZE(ads1115_data_rate), + .scale = ads1115_scale, + .scale_len = ARRAY_SIZE(ads1115_scale), + .has_comparator = true, +}; + +static const struct ads1015_chip_data tla2024_data = { + .channels = tla2024_channels, + .num_channels = ARRAY_SIZE(tla2024_channels), + .info = &tla2024_info, + .data_rate = ads1015_data_rate, + .data_rate_len = ARRAY_SIZE(ads1015_data_rate), + .scale = ads1015_scale, + .scale_len = ARRAY_SIZE(ads1015_scale), + .has_comparator = false, +}; + +static const struct i2c_device_id ads1015_id[] = { + { .name = "ads1015", .driver_data = (kernel_ulong_t)&ads1015_data }, + { .name = "ads1115", .driver_data = (kernel_ulong_t)&ads1115_data }, + { .name = "tla2024", .driver_data = (kernel_ulong_t)&tla2024_data }, + { } +}; +MODULE_DEVICE_TABLE(i2c, ads1015_id); + +static const struct of_device_id ads1015_of_match[] = { + { .compatible = "ti,ads1015", .data = &ads1015_data }, + { .compatible = "ti,ads1115", .data = &ads1115_data }, + { .compatible = "ti,tla2024", .data = &tla2024_data }, + { } +}; +MODULE_DEVICE_TABLE(of, ads1015_of_match); + +static struct i2c_driver ads1015_driver = { + .driver = { + .name = ADS1015_DRV_NAME, + .of_match_table = ads1015_of_match, + .pm = &ads1015_pm_ops, + }, + .probe = ads1015_probe, + .id_table = ads1015_id, +}; + +module_i2c_driver(ads1015_driver); + +MODULE_AUTHOR("Daniel Baluta "); +MODULE_DESCRIPTION("Texas Instruments ADS1015 ADC driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/adc/ti-ads1018.c b/drivers/iio/adc/ti-ads1018.c new file mode 100644 index 000000000..d9ec4a8d7 --- /dev/null +++ b/drivers/iio/adc/ti-ads1018.c @@ -0,0 +1,738 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Texas Instruments ADS1018 ADC driver + * + * Copyright (C) 2025 Kurt Borja + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include + +#define ADS1018_CFG_OS_TRIG BIT(15) +#define ADS1018_CFG_TS_MODE_EN BIT(4) +#define ADS1018_CFG_PULL_UP BIT(3) +#define ADS1018_CFG_NOP BIT(1) +#define ADS1018_CFG_VALID (ADS1018_CFG_PULL_UP | ADS1018_CFG_NOP) + +#define ADS1018_CFG_MUX_MASK GENMASK(14, 12) + +#define ADS1018_CFG_PGA_MASK GENMASK(11, 9) +#define ADS1018_PGA_DEFAULT 2 + +#define ADS1018_CFG_MODE_MASK BIT(8) +#define ADS1018_MODE_CONTINUOUS 0 +#define ADS1018_MODE_ONESHOT 1 + +#define ADS1018_CFG_DRATE_MASK GENMASK(7, 5) +#define ADS1018_DRATE_DEFAULT 4 + +#define ADS1018_NUM_PGA_MODES 6 +#define ADS1018_CHANNELS_MAX 10 + +struct ads1018_chan_data { + u8 pga_mode; + u8 data_rate_mode; +}; + +struct ads1018_chip_info { + const char *name; + const struct iio_chan_spec *channels; + unsigned long num_channels; + + /* IIO_VAL_INT */ + const u32 *data_rate_mode_to_hz; + unsigned long num_data_rate_mode_to_hz; + + /* + * Let `res` be the chip's resolution and `fsr` (millivolts) be the + * full-scale range corresponding to the PGA mode given by the array + * index. Then, the gain is calculated using the following formula: + * + * gain = |fsr| / 2^(res - 1) + * + * This value then has to be represented in IIO_VAL_INT_PLUS_NANO + * format. For example if: + * + * gain = 6144 / 2^(16 - 1) = 0.1875 + * + * ...then the formatted value is: + * + * { 0, 187500000 } + */ + const u32 pga_mode_to_gain[ADS1018_NUM_PGA_MODES][2]; + + /* IIO_VAL_INT_PLUS_MICRO */ + const u32 temp_scale[2]; +}; + +struct ads1018 { + struct spi_device *spi; + struct iio_trigger *indio_trig; + + struct gpio_desc *drdy_gpiod; + int drdy_irq; + + struct ads1018_chan_data chan_data[ADS1018_CHANNELS_MAX]; + const struct ads1018_chip_info *chip_info; + + struct spi_message msg_read; + struct spi_transfer xfer; + __be16 tx_buf[2] __aligned(IIO_DMA_MINALIGN); + __be16 rx_buf[2]; +}; + +#define ADS1018_VOLT_DIFF_CHAN(_index, _chan, _chan2, _realbits) { \ + .type = IIO_VOLTAGE, \ + .channel = _chan, \ + .channel2 = _chan2, \ + .scan_index = _index, \ + .scan_type = { \ + .sign = 's', \ + .realbits = _realbits, \ + .storagebits = 16, \ + .shift = 16 - _realbits, \ + .endianness = IIO_BE, \ + }, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ + BIT(IIO_CHAN_INFO_SCALE) | \ + BIT(IIO_CHAN_INFO_SAMP_FREQ), \ + .info_mask_shared_by_type_available = BIT(IIO_CHAN_INFO_SCALE), \ + .info_mask_shared_by_all_available = BIT(IIO_CHAN_INFO_SAMP_FREQ), \ + .indexed = true, \ + .differential = true, \ +} + +#define ADS1018_VOLT_CHAN(_index, _chan, _realbits) { \ + .type = IIO_VOLTAGE, \ + .channel = _chan, \ + .scan_index = _index, \ + .scan_type = { \ + .sign = 's', \ + .realbits = _realbits, \ + .storagebits = 16, \ + .shift = 16 - _realbits, \ + .endianness = IIO_BE, \ + }, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ + BIT(IIO_CHAN_INFO_SCALE) | \ + BIT(IIO_CHAN_INFO_SAMP_FREQ), \ + .info_mask_shared_by_type_available = BIT(IIO_CHAN_INFO_SCALE), \ + .info_mask_shared_by_all_available = BIT(IIO_CHAN_INFO_SAMP_FREQ), \ + .indexed = true, \ +} + +#define ADS1018_TEMP_CHAN(_index, _realbits) { \ + .type = IIO_TEMP, \ + .scan_index = _index, \ + .scan_type = { \ + .sign = 's', \ + .realbits = _realbits, \ + .storagebits = 16, \ + .shift = 16 - _realbits, \ + .endianness = IIO_BE, \ + }, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ + BIT(IIO_CHAN_INFO_SCALE) | \ + BIT(IIO_CHAN_INFO_SAMP_FREQ), \ + .info_mask_shared_by_all_available = BIT(IIO_CHAN_INFO_SAMP_FREQ), \ +} + +static const struct iio_chan_spec ads1118_iio_channels[] = { + ADS1018_VOLT_DIFF_CHAN(0, 0, 1, 16), + ADS1018_VOLT_DIFF_CHAN(1, 0, 3, 16), + ADS1018_VOLT_DIFF_CHAN(2, 1, 3, 16), + ADS1018_VOLT_DIFF_CHAN(3, 2, 3, 16), + ADS1018_VOLT_CHAN(4, 0, 16), + ADS1018_VOLT_CHAN(5, 1, 16), + ADS1018_VOLT_CHAN(6, 2, 16), + ADS1018_VOLT_CHAN(7, 3, 16), + ADS1018_TEMP_CHAN(8, 14), + IIO_CHAN_SOFT_TIMESTAMP(9), +}; + +static const struct iio_chan_spec ads1018_iio_channels[] = { + ADS1018_VOLT_DIFF_CHAN(0, 0, 1, 12), + ADS1018_VOLT_DIFF_CHAN(1, 0, 3, 12), + ADS1018_VOLT_DIFF_CHAN(2, 1, 3, 12), + ADS1018_VOLT_DIFF_CHAN(3, 2, 3, 12), + ADS1018_VOLT_CHAN(4, 0, 12), + ADS1018_VOLT_CHAN(5, 1, 12), + ADS1018_VOLT_CHAN(6, 2, 12), + ADS1018_VOLT_CHAN(7, 3, 12), + ADS1018_TEMP_CHAN(8, 12), + IIO_CHAN_SOFT_TIMESTAMP(9), +}; + +/** + * ads1018_calc_delay - Calculates a suitable delay for a single-shot reading + * @hz: Sampling frequency + * + * Calculates an appropriate delay for a single shot reading given a sampling + * frequency. + * + * Return: Delay in microseconds (Always greater than 0). + */ +static u32 ads1018_calc_delay(unsigned int hz) +{ + /* + * Calculate the worst-case sampling rate by subtracting 10% error + * specified in the datasheet... + */ + hz -= DIV_ROUND_UP(hz, 10); + + /* ...Then calculate time per sample in microseconds. */ + return DIV_ROUND_UP(HZ_PER_MHZ, hz); +} + +/** + * ads1018_spi_read_exclusive - Reads a conversion value from the device + * @ads1018: Device data + * @cnv: ADC Conversion value (optional) + * @hold_cs: Keep CS line asserted after the SPI transfer + * + * Reads the most recent ADC conversion value, without updating the + * device's configuration. + * + * Context: Expects SPI bus *exclusive* use. + * + * Return: 0 on success, negative errno on error. + */ +static int ads1018_spi_read_exclusive(struct ads1018 *ads1018, __be16 *cnv, + bool hold_cs) +{ + int ret; + + ads1018->xfer.cs_change = hold_cs; + + ret = spi_sync_locked(ads1018->spi, &ads1018->msg_read); + if (ret) + return ret; + + if (cnv) + *cnv = ads1018->rx_buf[0]; + + return 0; +} + +/** + * ads1018_single_shot - Performs a one-shot reading sequence + * @ads1018: Device data + * @chan: Channel specification + * @cnv: Conversion value + * + * Writes a new configuration, waits an appropriate delay, then reads the most + * recent conversion. + * + * Context: Expects iio_device_claim_direct() is held. + * + * Return: 0 on success, negative errno on error. + */ +static int ads1018_single_shot(struct ads1018 *ads1018, + struct iio_chan_spec const *chan, u16 *cnv) +{ + u8 max_drate_mode = ads1018->chip_info->num_data_rate_mode_to_hz - 1; + u32 drate = ads1018->chip_info->data_rate_mode_to_hz[max_drate_mode]; + u8 pga_mode = ads1018->chan_data[chan->scan_index].pga_mode; + struct spi_transfer xfer[2] = { + { + .tx_buf = ads1018->tx_buf, + .len = sizeof(ads1018->tx_buf[0]), + .delay = { + .value = ads1018_calc_delay(drate), + .unit = SPI_DELAY_UNIT_USECS, + }, + .cs_change = 1, /* 16-bit mode requires CS de-assert */ + }, + { + .rx_buf = ads1018->rx_buf, + .len = sizeof(ads1018->rx_buf[0]), + }, + }; + u16 cfg; + int ret; + + cfg = ADS1018_CFG_VALID | ADS1018_CFG_OS_TRIG; + cfg |= FIELD_PREP(ADS1018_CFG_MUX_MASK, chan->scan_index); + cfg |= FIELD_PREP(ADS1018_CFG_PGA_MASK, pga_mode); + cfg |= FIELD_PREP(ADS1018_CFG_MODE_MASK, ADS1018_MODE_ONESHOT); + cfg |= FIELD_PREP(ADS1018_CFG_DRATE_MASK, max_drate_mode); + + if (chan->type == IIO_TEMP) + cfg |= ADS1018_CFG_TS_MODE_EN; + + ads1018->tx_buf[0] = cpu_to_be16(cfg); + ret = spi_sync_transfer(ads1018->spi, xfer, ARRAY_SIZE(xfer)); + if (ret) + return ret; + + *cnv = be16_to_cpu(ads1018->rx_buf[0]); + + return 0; +} + +static int +ads1018_read_raw_direct_mode(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int *val, int *val2, + long mask) +{ + struct ads1018 *ads1018 = iio_priv(indio_dev); + const struct ads1018_chip_info *chip_info = ads1018->chip_info; + u8 addr = chan->scan_index; + u8 pga_mode, drate_mode; + u16 cnv; + int ret; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + ret = ads1018_single_shot(ads1018, chan, &cnv); + if (ret) + return ret; + + cnv >>= chan->scan_type.shift; + *val = sign_extend32(cnv, chan->scan_type.realbits - 1); + + return IIO_VAL_INT; + + case IIO_CHAN_INFO_SCALE: + switch (chan->type) { + case IIO_VOLTAGE: + pga_mode = ads1018->chan_data[addr].pga_mode; + *val = chip_info->pga_mode_to_gain[pga_mode][0]; + *val2 = chip_info->pga_mode_to_gain[pga_mode][1]; + return IIO_VAL_INT_PLUS_NANO; + + case IIO_TEMP: + *val = chip_info->temp_scale[0]; + *val2 = chip_info->temp_scale[1]; + return IIO_VAL_INT_PLUS_MICRO; + + default: + return -EOPNOTSUPP; + } + + case IIO_CHAN_INFO_SAMP_FREQ: + drate_mode = ads1018->chan_data[addr].data_rate_mode; + *val = chip_info->data_rate_mode_to_hz[drate_mode]; + return IIO_VAL_INT; + + default: + return -EOPNOTSUPP; + } +} + +static int +ads1018_read_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + int ret; + + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; + ret = ads1018_read_raw_direct_mode(indio_dev, chan, val, val2, mask); + iio_device_release_direct(indio_dev); + + return ret; +} + +static int +ads1018_read_avail(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, + const int **vals, int *type, int *length, long mask) +{ + struct ads1018 *ads1018 = iio_priv(indio_dev); + + switch (mask) { + case IIO_CHAN_INFO_SCALE: + *type = IIO_VAL_INT_PLUS_NANO; + *vals = (const int *)ads1018->chip_info->pga_mode_to_gain; + *length = ADS1018_NUM_PGA_MODES * 2; + return IIO_AVAIL_LIST; + + case IIO_CHAN_INFO_SAMP_FREQ: + *type = IIO_VAL_INT; + *vals = ads1018->chip_info->data_rate_mode_to_hz; + *length = ads1018->chip_info->num_data_rate_mode_to_hz; + return IIO_AVAIL_LIST; + + default: + return -EOPNOTSUPP; + } +} + +static int +ads1018_write_raw_direct_mode(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int val, int val2, + long mask) +{ + struct ads1018 *ads1018 = iio_priv(indio_dev); + const struct ads1018_chip_info *info = ads1018->chip_info; + unsigned int i; + + switch (mask) { + case IIO_CHAN_INFO_SCALE: + for (i = 0; i < ADS1018_NUM_PGA_MODES; i++) { + if (val == info->pga_mode_to_gain[i][0] && + val2 == info->pga_mode_to_gain[i][1]) + break; + } + if (i == ADS1018_NUM_PGA_MODES) + return -EINVAL; + + ads1018->chan_data[chan->scan_index].pga_mode = i; + return 0; + + case IIO_CHAN_INFO_SAMP_FREQ: + for (i = 0; i < info->num_data_rate_mode_to_hz; i++) { + if (val == info->data_rate_mode_to_hz[i]) + break; + } + if (i == info->num_data_rate_mode_to_hz) + return -EINVAL; + + ads1018->chan_data[chan->scan_index].data_rate_mode = i; + return 0; + + default: + return -EOPNOTSUPP; + } +} + +static int +ads1018_write_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, + int val, int val2, long mask) +{ + int ret; + + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; + ret = ads1018_write_raw_direct_mode(indio_dev, chan, val, val2, mask); + iio_device_release_direct(indio_dev); + + return ret; +} + +static int +ads1018_write_raw_get_fmt(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, long mask) +{ + switch (mask) { + case IIO_CHAN_INFO_SCALE: + return IIO_VAL_INT_PLUS_NANO; + default: + return IIO_VAL_INT_PLUS_MICRO; + } +} + +static const struct iio_info ads1018_iio_info = { + .read_raw = ads1018_read_raw, + .read_avail = ads1018_read_avail, + .write_raw = ads1018_write_raw, + .write_raw_get_fmt = ads1018_write_raw_get_fmt, +}; + +static void ads1018_set_trigger_enable(struct ads1018 *ads1018) +{ + spi_bus_lock(ads1018->spi->controller); + ads1018_spi_read_exclusive(ads1018, NULL, true); + enable_irq(ads1018->drdy_irq); +} + +static void ads1018_set_trigger_disable(struct ads1018 *ads1018) +{ + disable_irq(ads1018->drdy_irq); + ads1018_spi_read_exclusive(ads1018, NULL, false); + spi_bus_unlock(ads1018->spi->controller); +} + +static int ads1018_set_trigger_state(struct iio_trigger *trig, bool state) +{ + struct ads1018 *ads1018 = iio_trigger_get_drvdata(trig); + + /* + * We need to lock the SPI bus and tie CS low (hold_cs) to catch + * data-ready interrupts, otherwise the MISO line enters a Hi-Z state. + */ + + if (state) + ads1018_set_trigger_enable(ads1018); + else + ads1018_set_trigger_disable(ads1018); + + return 0; +} + +static const struct iio_trigger_ops ads1018_trigger_ops = { + .set_trigger_state = ads1018_set_trigger_state, + .validate_device = iio_trigger_validate_own_device, +}; + +static int ads1018_buffer_preenable(struct iio_dev *indio_dev) +{ + struct ads1018 *ads1018 = iio_priv(indio_dev); + const struct ads1018_chip_info *chip_info = ads1018->chip_info; + unsigned int pga, drate, addr; + u16 cfg; + + addr = find_first_bit(indio_dev->active_scan_mask, + iio_get_masklength(indio_dev)); + pga = ads1018->chan_data[addr].pga_mode; + drate = ads1018->chan_data[addr].data_rate_mode; + + cfg = ADS1018_CFG_VALID; + cfg |= FIELD_PREP(ADS1018_CFG_MUX_MASK, addr); + cfg |= FIELD_PREP(ADS1018_CFG_PGA_MASK, pga); + cfg |= FIELD_PREP(ADS1018_CFG_MODE_MASK, ADS1018_MODE_CONTINUOUS); + cfg |= FIELD_PREP(ADS1018_CFG_DRATE_MASK, drate); + + if (chip_info->channels[addr].type == IIO_TEMP) + cfg |= ADS1018_CFG_TS_MODE_EN; + + ads1018->tx_buf[0] = cpu_to_be16(cfg); + ads1018->tx_buf[1] = 0; + + return spi_write(ads1018->spi, ads1018->tx_buf, sizeof(ads1018->tx_buf)); +} + +static int ads1018_buffer_postdisable(struct iio_dev *indio_dev) +{ + struct ads1018 *ads1018 = iio_priv(indio_dev); + u16 cfg; + + cfg = ADS1018_CFG_VALID; + cfg |= FIELD_PREP(ADS1018_CFG_MODE_MASK, ADS1018_MODE_ONESHOT); + + ads1018->tx_buf[0] = cpu_to_be16(cfg); + ads1018->tx_buf[1] = 0; + + return spi_write(ads1018->spi, ads1018->tx_buf, sizeof(ads1018->tx_buf)); +} + +static const struct iio_buffer_setup_ops ads1018_buffer_ops = { + .preenable = ads1018_buffer_preenable, + .postdisable = ads1018_buffer_postdisable, + .validate_scan_mask = iio_validate_scan_mask_onehot, +}; + +static irqreturn_t ads1018_irq_handler(int irq, void *dev_id) +{ + struct ads1018 *ads1018 = dev_id; + + /* + * We need to check if the "drdy" pin is actually active or if it's a + * pending interrupt triggered by the SPI transfer. + */ + if (!gpiod_get_value(ads1018->drdy_gpiod)) + return IRQ_HANDLED; + + iio_trigger_poll(ads1018->indio_trig); + + return IRQ_HANDLED; +} + +static irqreturn_t ads1018_trigger_handler(int irq, void *p) +{ + struct iio_poll_func *pf = p; + struct iio_dev *indio_dev = pf->indio_dev; + struct ads1018 *ads1018 = iio_priv(indio_dev); + struct { + __be16 conv; + aligned_s64 ts; + } scan = {}; + int ret; + + if (iio_trigger_using_own(indio_dev)) { + disable_irq(ads1018->drdy_irq); + ret = ads1018_spi_read_exclusive(ads1018, &scan.conv, true); + enable_irq(ads1018->drdy_irq); + } else { + ret = spi_read(ads1018->spi, ads1018->rx_buf, sizeof(ads1018->rx_buf)); + scan.conv = ads1018->rx_buf[0]; + } + + if (!ret) + iio_push_to_buffers_with_ts(indio_dev, &scan, sizeof(scan), + pf->timestamp); + + iio_trigger_notify_done(indio_dev->trig); + + return IRQ_HANDLED; +} + +static int ads1018_trigger_setup(struct iio_dev *indio_dev) +{ + struct ads1018 *ads1018 = iio_priv(indio_dev); + struct spi_device *spi = ads1018->spi; + struct device *dev = &spi->dev; + const char *con_id = "drdy"; + int ret; + + ads1018->drdy_gpiod = devm_gpiod_get_optional(dev, con_id, GPIOD_IN); + if (IS_ERR(ads1018->drdy_gpiod)) + return dev_err_probe(dev, PTR_ERR(ads1018->drdy_gpiod), + "Failed to get %s GPIO.\n", con_id); + + /* First try to get IRQ from SPI core, then from GPIO */ + if (spi->irq > 0) + ads1018->drdy_irq = spi->irq; + else if (ads1018->drdy_gpiod) + ads1018->drdy_irq = gpiod_to_irq(ads1018->drdy_gpiod); + if (ads1018->drdy_irq < 0) + return dev_err_probe(dev, ads1018->drdy_irq, + "Failed to get IRQ from %s GPIO.\n", con_id); + + /* An IRQ line is only an optional requirement for the IIO trigger */ + if (ads1018->drdy_irq == 0) + return 0; + + ads1018->indio_trig = devm_iio_trigger_alloc(dev, "%s-dev%d-%s", + indio_dev->name, + iio_device_id(indio_dev), + con_id); + if (!ads1018->indio_trig) + return -ENOMEM; + + iio_trigger_set_drvdata(ads1018->indio_trig, ads1018); + ads1018->indio_trig->ops = &ads1018_trigger_ops; + + ret = devm_iio_trigger_register(dev, ads1018->indio_trig); + if (ret) + return ret; + + /* + * The "data-ready" IRQ line is shared with the MOSI pin, thus we need + * to keep it disabled until we actually request data. + */ + return devm_request_irq(dev, ads1018->drdy_irq, ads1018_irq_handler, + IRQF_NO_AUTOEN, ads1018->chip_info->name, ads1018); +} + +static int ads1018_spi_probe(struct spi_device *spi) +{ + const struct ads1018_chip_info *info = spi_get_device_match_data(spi); + struct device *dev = &spi->dev; + struct iio_dev *indio_dev; + struct ads1018 *ads1018; + int ret; + + indio_dev = devm_iio_device_alloc(dev, sizeof(*ads1018)); + if (!indio_dev) + return -ENOMEM; + + ads1018 = iio_priv(indio_dev); + ads1018->spi = spi; + ads1018->chip_info = info; + + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->name = info->name; + indio_dev->info = &ads1018_iio_info; + indio_dev->channels = info->channels; + indio_dev->num_channels = info->num_channels; + + for (unsigned int i = 0; i < ADS1018_CHANNELS_MAX; i++) { + ads1018->chan_data[i].data_rate_mode = ADS1018_DRATE_DEFAULT; + ads1018->chan_data[i].pga_mode = ADS1018_PGA_DEFAULT; + } + + ads1018->xfer.rx_buf = ads1018->rx_buf; + ads1018->xfer.len = sizeof(ads1018->rx_buf); + spi_message_init_with_transfers(&ads1018->msg_read, &ads1018->xfer, 1); + + ret = ads1018_trigger_setup(indio_dev); + if (ret) + return ret; + + ret = devm_iio_triggered_buffer_setup(dev, indio_dev, + iio_pollfunc_store_time, + ads1018_trigger_handler, + &ads1018_buffer_ops); + if (ret) + return ret; + + return devm_iio_device_register(&spi->dev, indio_dev); +} + +static const unsigned int ads1018_data_rate_table[] = { + 128, 250, 490, 920, 1600, 2400, 3300, +}; + +static const unsigned int ads1118_data_rate_table[] = { + 8, 16, 32, 64, 128, 250, 475, 860, +}; + +static const struct ads1018_chip_info ads1018_chip_info = { + .name = "ads1018", + .channels = ads1018_iio_channels, + .num_channels = ARRAY_SIZE(ads1018_iio_channels), + .data_rate_mode_to_hz = ads1018_data_rate_table, + .num_data_rate_mode_to_hz = ARRAY_SIZE(ads1018_data_rate_table), + .pga_mode_to_gain = { + { 3, 0 }, /* fsr = 6144 mV */ + { 2, 0 }, /* fsr = 4096 mV */ + { 1, 0 }, /* fsr = 2048 mV */ + { 0, 500000000 }, /* fsr = 1024 mV */ + { 0, 250000000 }, /* fsr = 512 mV */ + { 0, 125000000 }, /* fsr = 256 mV */ + }, + .temp_scale = { 125, 0 }, +}; + +static const struct ads1018_chip_info ads1118_chip_info = { + .name = "ads1118", + .channels = ads1118_iio_channels, + .num_channels = ARRAY_SIZE(ads1118_iio_channels), + .data_rate_mode_to_hz = ads1118_data_rate_table, + .num_data_rate_mode_to_hz = ARRAY_SIZE(ads1118_data_rate_table), + .pga_mode_to_gain = { + { 0, 187500000 }, /* fsr = 6144 mV */ + { 0, 125000000 }, /* fsr = 4096 mV */ + { 0, 62500000 }, /* fsr = 2048 mV */ + { 0, 31250000 }, /* fsr = 1024 mV */ + { 0, 15625000 }, /* fsr = 512 mV */ + { 0, 7812500 }, /* fsr = 256 mV */ + }, + .temp_scale = { 31, 250000 }, +}; + +static const struct of_device_id ads1018_of_match[] = { + { .compatible = "ti,ads1018", .data = &ads1018_chip_info }, + { .compatible = "ti,ads1118", .data = &ads1118_chip_info }, + { } +}; +MODULE_DEVICE_TABLE(of, ads1018_of_match); + +static const struct spi_device_id ads1018_spi_match[] = { + { .name = "ads1018", .driver_data = (kernel_ulong_t)&ads1018_chip_info }, + { .name = "ads1118", .driver_data = (kernel_ulong_t)&ads1118_chip_info }, + { } +}; +MODULE_DEVICE_TABLE(spi, ads1018_spi_match); + +static struct spi_driver ads1018_spi_driver = { + .driver = { + .name = "ads1018", + .of_match_table = ads1018_of_match, + }, + .probe = ads1018_spi_probe, + .id_table = ads1018_spi_match, +}; +module_spi_driver(ads1018_spi_driver); + +MODULE_DESCRIPTION("Texas Instruments ADS1018 ADC Driver"); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Kurt Borja "); diff --git a/drivers/iio/adc/ti-ads1100.c b/drivers/iio/adc/ti-ads1100.c new file mode 100644 index 000000000..9fe8d54cc --- /dev/null +++ b/drivers/iio/adc/ti-ads1100.c @@ -0,0 +1,432 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * ADS1100 - Texas Instruments Analog-to-Digital Converter + * + * Copyright (c) 2023, Topic Embedded Products + * + * Datasheet: https://www.ti.com/lit/gpn/ads1100 + * IIO driver for ADS1100 and ADS1000 ADC 16-bit I2C + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +/* The ADS1100 has a single byte config register */ + +/* Conversion in progress bit */ +#define ADS1100_CFG_ST_BSY BIT(7) +/* Single conversion bit */ +#define ADS1100_CFG_SC BIT(4) +/* Data rate */ +#define ADS1100_DR_MASK GENMASK(3, 2) +/* Gain */ +#define ADS1100_PGA_MASK GENMASK(1, 0) + +#define ADS1100_CONTINUOUS 0 +#define ADS1100_SINGLESHOT ADS1100_CFG_SC + +#define ADS1100_SLEEP_DELAY_MS 2000 + +static const int ads1100_data_rate[] = { 128, 32, 16, 8 }; +static const int ads1100_data_rate_bits[] = { 12, 14, 15, 16 }; + +struct ads1100_data { + struct i2c_client *client; + struct regulator *reg_vdd; + struct mutex lock; + int scale_avail[2 * 4]; /* 4 gain settings */ + u8 config; + bool supports_data_rate; /* Only the ADS1100 can select the rate */ +}; + +static const struct iio_chan_spec ads1100_channel = { + .type = IIO_VOLTAGE, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), + .info_mask_shared_by_all = + BIT(IIO_CHAN_INFO_SCALE) | BIT(IIO_CHAN_INFO_SAMP_FREQ), + .info_mask_shared_by_all_available = + BIT(IIO_CHAN_INFO_SCALE) | BIT(IIO_CHAN_INFO_SAMP_FREQ), + .scan_type = { + .sign = 's', + .realbits = 16, + .storagebits = 16, + .endianness = IIO_CPU, + }, + .datasheet_name = "AIN", +}; + +static int ads1100_set_config_bits(struct ads1100_data *data, u8 mask, u8 value) +{ + int ret; + u8 config = (data->config & ~mask) | (value & mask); + + if (data->config == config) + return 0; /* Already done */ + + ret = i2c_master_send(data->client, &config, 1); + if (ret < 0) + return ret; + + data->config = config; + + return 0; +}; + +static int ads1100_data_bits(struct ads1100_data *data) +{ + return ads1100_data_rate_bits[FIELD_GET(ADS1100_DR_MASK, data->config)]; +} + +static int ads1100_get_adc_result(struct ads1100_data *data, int chan, int *val) +{ + int ret; + __be16 buffer; + s16 value; + + if (chan != 0) + return -EINVAL; + + ret = pm_runtime_resume_and_get(&data->client->dev); + if (ret < 0) + return ret; + + ret = i2c_master_recv(data->client, (char *)&buffer, sizeof(buffer)); + + pm_runtime_put_autosuspend(&data->client->dev); + + if (ret < 0) { + dev_err(&data->client->dev, "I2C read fail: %d\n", ret); + return ret; + } + + /* Value is always 16-bit 2's complement */ + value = be16_to_cpu(buffer); + + /* Shift result to compensate for bit resolution vs. sample rate */ + value <<= 16 - ads1100_data_bits(data); + + *val = sign_extend32(value, 15); + + return 0; +} + +static int ads1100_set_scale(struct ads1100_data *data, int val, int val2) +{ + int microvolts; + int gain; + + /* With Vdd between 2.7 and 5V, the scale is always below 1 */ + if (val) + return -EINVAL; + + if (!val2) + return -EINVAL; + + microvolts = regulator_get_voltage(data->reg_vdd); + /* + * val2 is in 'micro' units, n = val2 / 1000000 + * result must be millivolts, d = microvolts / 1000 + * the full-scale value is d/n, corresponds to 2^15, + * hence the gain = (d / n) >> 15, factoring out the 1000 and moving the + * bitshift so everything fits in 32-bits yields this formula. + */ + gain = DIV_ROUND_CLOSEST(microvolts, BIT(15)) * MILLI / val2; + if (gain < BIT(0) || gain > BIT(3)) + return -EINVAL; + + ads1100_set_config_bits(data, ADS1100_PGA_MASK, ffs(gain) - 1); + + return 0; +} + +static int ads1100_set_data_rate(struct ads1100_data *data, int chan, int rate) +{ + unsigned int i; + unsigned int size; + + size = data->supports_data_rate ? ARRAY_SIZE(ads1100_data_rate) : 1; + for (i = 0; i < size; i++) { + if (ads1100_data_rate[i] == rate) + return ads1100_set_config_bits(data, ADS1100_DR_MASK, + FIELD_PREP(ADS1100_DR_MASK, i)); + } + + return -EINVAL; +} + +static int ads1100_get_vdd_millivolts(struct ads1100_data *data) +{ + return regulator_get_voltage(data->reg_vdd) / (MICRO / MILLI); +} + +static void ads1100_calc_scale_avail(struct ads1100_data *data) +{ + int millivolts = ads1100_get_vdd_millivolts(data); + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(data->scale_avail) / 2; i++) { + data->scale_avail[i * 2 + 0] = millivolts; + data->scale_avail[i * 2 + 1] = 15 + i; + } +} + +static int ads1100_read_avail(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + const int **vals, int *type, int *length, + long mask) +{ + struct ads1100_data *data = iio_priv(indio_dev); + + if (chan->type != IIO_VOLTAGE) + return -EINVAL; + + switch (mask) { + case IIO_CHAN_INFO_SAMP_FREQ: + *type = IIO_VAL_INT; + *vals = ads1100_data_rate; + if (data->supports_data_rate) + *length = ARRAY_SIZE(ads1100_data_rate); + else + *length = 1; + return IIO_AVAIL_LIST; + case IIO_CHAN_INFO_SCALE: + *type = IIO_VAL_FRACTIONAL_LOG2; + *vals = data->scale_avail; + *length = ARRAY_SIZE(data->scale_avail); + return IIO_AVAIL_LIST; + default: + return -EINVAL; + } +} + +static int ads1100_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int *val, + int *val2, long mask) +{ + int ret; + struct ads1100_data *data = iio_priv(indio_dev); + + guard(mutex)(&data->lock); + switch (mask) { + case IIO_CHAN_INFO_RAW: + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; + + ret = ads1100_get_adc_result(data, chan->address, val); + iio_device_release_direct(indio_dev); + if (ret < 0) + return ret; + + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + /* full-scale is the supply voltage in millivolts */ + *val = ads1100_get_vdd_millivolts(data); + *val2 = 15 + FIELD_GET(ADS1100_PGA_MASK, data->config); + return IIO_VAL_FRACTIONAL_LOG2; + case IIO_CHAN_INFO_SAMP_FREQ: + *val = ads1100_data_rate[FIELD_GET(ADS1100_DR_MASK, + data->config)]; + return IIO_VAL_INT; + default: + return -EINVAL; + } +} + +static int ads1100_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int val, + int val2, long mask) +{ + struct ads1100_data *data = iio_priv(indio_dev); + + guard(mutex)(&data->lock); + switch (mask) { + case IIO_CHAN_INFO_SCALE: + return ads1100_set_scale(data, val, val2); + case IIO_CHAN_INFO_SAMP_FREQ: + return ads1100_set_data_rate(data, chan->address, val); + default: + return -EINVAL; + } +} + +static const struct iio_info ads1100_info = { + .read_avail = ads1100_read_avail, + .read_raw = ads1100_read_raw, + .write_raw = ads1100_write_raw, +}; + +static int ads1100_setup(struct ads1100_data *data) +{ + int ret; + u8 buffer[3]; + + /* Setup continuous sampling mode at 8sps */ + buffer[0] = ADS1100_DR_MASK | ADS1100_CONTINUOUS; + ret = i2c_master_send(data->client, buffer, 1); + if (ret < 0) + return ret; + + ret = i2c_master_recv(data->client, buffer, sizeof(buffer)); + if (ret < 0) + return ret; + + /* Config register returned in third byte, strip away the busy status */ + data->config = buffer[2] & ~ADS1100_CFG_ST_BSY; + + /* Detect the sample rate capability by checking the DR bits */ + data->supports_data_rate = FIELD_GET(ADS1100_DR_MASK, buffer[2]) != 0; + + return 0; +} + +static void ads1100_reg_disable(void *reg) +{ + regulator_disable(reg); +} + +static void ads1100_disable_continuous(void *data) +{ + ads1100_set_config_bits(data, ADS1100_CFG_SC, ADS1100_SINGLESHOT); +} + +static int ads1100_probe(struct i2c_client *client) +{ + struct iio_dev *indio_dev; + struct ads1100_data *data; + struct device *dev = &client->dev; + int ret; + + indio_dev = devm_iio_device_alloc(dev, sizeof(*data)); + if (!indio_dev) + return -ENOMEM; + + data = iio_priv(indio_dev); + dev_set_drvdata(dev, data); + data->client = client; + mutex_init(&data->lock); + + indio_dev->name = "ads1100"; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = &ads1100_channel; + indio_dev->num_channels = 1; + indio_dev->info = &ads1100_info; + + data->reg_vdd = devm_regulator_get(dev, "vdd"); + if (IS_ERR(data->reg_vdd)) + return dev_err_probe(dev, PTR_ERR(data->reg_vdd), + "Failed to get vdd regulator\n"); + + ret = regulator_enable(data->reg_vdd); + if (ret < 0) + return dev_err_probe(dev, ret, + "Failed to enable vdd regulator\n"); + + ret = devm_add_action_or_reset(dev, ads1100_reg_disable, data->reg_vdd); + if (ret) + return ret; + + ret = ads1100_setup(data); + if (ret) + return dev_err_probe(dev, ret, + "Failed to communicate with device\n"); + + ret = devm_add_action_or_reset(dev, ads1100_disable_continuous, data); + if (ret) + return ret; + + ads1100_calc_scale_avail(data); + + pm_runtime_set_autosuspend_delay(dev, ADS1100_SLEEP_DELAY_MS); + pm_runtime_use_autosuspend(dev); + pm_runtime_set_active(dev); + ret = devm_pm_runtime_enable(dev); + if (ret) + return dev_err_probe(dev, ret, "Failed to enable pm_runtime\n"); + + ret = devm_iio_device_register(dev, indio_dev); + if (ret) + return dev_err_probe(dev, ret, + "Failed to register IIO device\n"); + + return 0; +} + +static int ads1100_runtime_suspend(struct device *dev) +{ + struct ads1100_data *data = dev_get_drvdata(dev); + + ads1100_set_config_bits(data, ADS1100_CFG_SC, ADS1100_SINGLESHOT); + regulator_disable(data->reg_vdd); + + return 0; +} + +static int ads1100_runtime_resume(struct device *dev) +{ + struct ads1100_data *data = dev_get_drvdata(dev); + int ret; + + ret = regulator_enable(data->reg_vdd); + if (ret) { + dev_err(&data->client->dev, "Failed to enable Vdd\n"); + return ret; + } + + /* + * We'll always change the mode bit in the config register, so there is + * no need here to "force" a write to the config register. If the device + * has been power-cycled, we'll re-write its config register now. + */ + return ads1100_set_config_bits(data, ADS1100_CFG_SC, + ADS1100_CONTINUOUS); +} + +static DEFINE_RUNTIME_DEV_PM_OPS(ads1100_pm_ops, + ads1100_runtime_suspend, + ads1100_runtime_resume, + NULL); + +static const struct i2c_device_id ads1100_id[] = { + { .name = "ads1100" }, + { .name = "ads1000" }, + { } +}; + +MODULE_DEVICE_TABLE(i2c, ads1100_id); + +static const struct of_device_id ads1100_of_match[] = { + {.compatible = "ti,ads1100" }, + {.compatible = "ti,ads1000" }, + { } +}; + +MODULE_DEVICE_TABLE(of, ads1100_of_match); + +static struct i2c_driver ads1100_driver = { + .driver = { + .name = "ads1100", + .of_match_table = ads1100_of_match, + .pm = pm_ptr(&ads1100_pm_ops), + }, + .probe = ads1100_probe, + .id_table = ads1100_id, +}; + +module_i2c_driver(ads1100_driver); + +MODULE_AUTHOR("Mike Looijmans "); +MODULE_DESCRIPTION("Texas Instruments ADS1100 ADC driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/iio/adc/ti-ads1119.c b/drivers/iio/adc/ti-ads1119.c new file mode 100644 index 000000000..b0f04741d --- /dev/null +++ b/drivers/iio/adc/ti-ads1119.c @@ -0,0 +1,829 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Texas Instruments ADS1119 ADC driver. + * + * Copyright 2024 Toradex + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#define ADS1119_CMD_RESET 0x06 +#define ADS1119_CMD_POWERDOWN 0x02 +#define ADS1119_CMD_START_SYNC 0x08 +#define ADS1119_CMD_RDATA 0x10 +#define ADS1119_CMD_RREG_CONFIG 0x20 +#define ADS1119_CMD_RREG_STATUS 0x24 +#define ADS1119_CMD_WREG 0x40 + +#define ADS1119_CMD_RREG(reg) (0x20 | (reg) << 2) + +/* Config register */ +#define ADS1119_REG_CONFIG 0x00 +#define ADS1119_CONFIG_VREF_FIELD BIT(0) +#define ADS1119_CONFIG_CM_FIELD BIT(1) +#define ADS1119_CONFIG_DR_FIELD GENMASK(3, 2) +#define ADS1119_CONFIG_GAIN_FIELD BIT(4) +#define ADS1119_CONFIG_MUX_FIELD GENMASK(7, 5) + +#define ADS1119_VREF_INTERNAL 0 +#define ADS1119_VREF_EXTERNAL 1 +#define ADS1119_VREF_INTERNAL_VAL 2048000 + +#define ADS1119_CM_SINGLE 0 +#define ADS1119_CM_CONTINUOUS 1 + +#define ADS1119_DR_20_SPS 0 +#define ADS1119_DR_90_SPS 1 +#define ADS1119_DR_330_SPS 2 +#define ADS1119_DR_1000_SPS 3 + +#define ADS1119_GAIN_1 0 +#define ADS1119_GAIN_4 1 + +#define ADS1119_MUX_AIN0_AIN1 0 +#define ADS1119_MUX_AIN2_AIN3 1 +#define ADS1119_MUX_AIN1_AIN2 2 +#define ADS1119_MUX_AIN0 3 +#define ADS1119_MUX_AIN1 4 +#define ADS1119_MUX_AIN2 5 +#define ADS1119_MUX_AIN3 6 +#define ADS1119_MUX_SHORTED 7 + +/* Status register */ +#define ADS1119_REG_STATUS 0x01 +#define ADS1119_STATUS_DRDY_FIELD BIT(7) + +#define ADS1119_DEFAULT_GAIN 1 +#define ADS1119_DEFAULT_DATARATE 20 + +#define ADS1119_SUSPEND_DELAY 2000 + +/* Timeout based on the minimum sample rate of 20 SPS (50000us) */ +#define ADS1119_MAX_DRDY_TIMEOUT 85000 + +#define ADS1119_MAX_CHANNELS 7 +#define ADS1119_MAX_SINGLE_CHANNELS 4 + +struct ads1119_channel_config { + int gain; + int datarate; + int mux; +}; + +struct ads1119_state { + struct completion completion; + struct i2c_client *client; + struct gpio_desc *reset_gpio; + struct iio_trigger *trig; + struct ads1119_channel_config *channels_cfg; + unsigned int num_channels_cfg; + unsigned int cached_config; + int vref_uV; +}; + +static const char * const ads1119_power_supplies[] = { + "avdd", "dvdd" +}; + +static const int ads1119_available_datarates[] = { + 20, 90, 330, 1000, +}; + +static const int ads1119_available_gains[] = { + 1, 1, + 1, 4, +}; + +static int ads1119_upd_cfg_reg(struct ads1119_state *st, unsigned int fields, + unsigned int val) +{ + unsigned int config = st->cached_config; + int ret; + + config &= ~fields; + config |= val; + + ret = i2c_smbus_write_byte_data(st->client, ADS1119_CMD_WREG, config); + if (ret) + return ret; + + st->cached_config = config; + + return 0; +} + +static bool ads1119_data_ready(struct ads1119_state *st) +{ + int status; + + status = i2c_smbus_read_byte_data(st->client, ADS1119_CMD_RREG_STATUS); + if (status < 0) + return false; + + return FIELD_GET(ADS1119_STATUS_DRDY_FIELD, status); +} + +static int ads1119_reset(struct ads1119_state *st) +{ + st->cached_config = 0; + + if (!st->reset_gpio) + return i2c_smbus_write_byte(st->client, ADS1119_CMD_RESET); + + gpiod_set_value_cansleep(st->reset_gpio, 1); + udelay(1); + gpiod_set_value_cansleep(st->reset_gpio, 0); + udelay(1); + + return 0; +} + +static int ads1119_set_conv_mode(struct ads1119_state *st, bool continuous) +{ + unsigned int mode; + + if (continuous) + mode = ADS1119_CM_CONTINUOUS; + else + mode = ADS1119_CM_SINGLE; + + return ads1119_upd_cfg_reg(st, ADS1119_CONFIG_CM_FIELD, + FIELD_PREP(ADS1119_CONFIG_CM_FIELD, mode)); +} + +static int ads1119_get_hw_gain(int gain) +{ + if (gain == 4) + return ADS1119_GAIN_4; + else + return ADS1119_GAIN_1; +} + +static int ads1119_get_hw_datarate(int datarate) +{ + switch (datarate) { + case 90: + return ADS1119_DR_90_SPS; + case 330: + return ADS1119_DR_330_SPS; + case 1000: + return ADS1119_DR_1000_SPS; + case 20: + default: + return ADS1119_DR_20_SPS; + } +} + +static int ads1119_configure_channel(struct ads1119_state *st, int mux, + int gain, int datarate) +{ + int ret; + + ret = ads1119_upd_cfg_reg(st, ADS1119_CONFIG_MUX_FIELD, + FIELD_PREP(ADS1119_CONFIG_MUX_FIELD, mux)); + if (ret) + return ret; + + ret = ads1119_upd_cfg_reg(st, ADS1119_CONFIG_GAIN_FIELD, + FIELD_PREP(ADS1119_CONFIG_GAIN_FIELD, + ads1119_get_hw_gain(gain))); + if (ret) + return ret; + + return ads1119_upd_cfg_reg(st, ADS1119_CONFIG_DR_FIELD, + FIELD_PREP(ADS1119_CONFIG_DR_FIELD, + ads1119_get_hw_datarate(datarate))); +} + +static int ads1119_poll_data_ready(struct ads1119_state *st, + struct iio_chan_spec const *chan) +{ + unsigned int datarate = st->channels_cfg[chan->address].datarate; + unsigned long wait_time; + bool data_ready; + + /* Poll 5 times more than the data rate */ + wait_time = DIV_ROUND_CLOSEST(MICRO, 5 * datarate); + + return read_poll_timeout(ads1119_data_ready, data_ready, + data_ready, wait_time, + ADS1119_MAX_DRDY_TIMEOUT, false, st); +} + +static int ads1119_read_data(struct ads1119_state *st, + struct iio_chan_spec const *chan, + unsigned int *val) +{ + unsigned int timeout; + int ret = 0; + + timeout = msecs_to_jiffies(ADS1119_MAX_DRDY_TIMEOUT); + + if (!st->client->irq) { + ret = ads1119_poll_data_ready(st, chan); + if (ret) + return ret; + } else if (!wait_for_completion_timeout(&st->completion, timeout)) { + return -ETIMEDOUT; + } + + ret = i2c_smbus_read_word_swapped(st->client, ADS1119_CMD_RDATA); + if (ret < 0) + return ret; + + *val = ret; + + return 0; +} + +static int ads1119_single_conversion(struct ads1119_state *st, + struct iio_chan_spec const *chan, + int *val, + bool calib_offset) +{ + struct device *dev = &st->client->dev; + int mux = st->channels_cfg[chan->address].mux; + int gain = st->channels_cfg[chan->address].gain; + int datarate = st->channels_cfg[chan->address].datarate; + unsigned int sample; + int ret; + + if (calib_offset) + mux = ADS1119_MUX_SHORTED; + + ret = pm_runtime_resume_and_get(dev); + if (ret) + return ret; + + ret = ads1119_configure_channel(st, mux, gain, datarate); + if (ret) + goto pdown; + + if (st->client->irq) + reinit_completion(&st->completion); + + ret = i2c_smbus_write_byte(st->client, ADS1119_CMD_START_SYNC); + if (ret) + goto pdown; + + ret = ads1119_read_data(st, chan, &sample); + if (ret) + goto pdown; + + *val = sign_extend32(sample, chan->scan_type.realbits - 1); + ret = IIO_VAL_INT; +pdown: + pm_runtime_put_autosuspend(dev); + return ret; +} + +static int ads1119_validate_datarate(struct ads1119_state *st, int datarate) +{ + switch (datarate) { + case 20: + case 90: + case 330: + case 1000: + return datarate; + default: + return -EINVAL; + } +} + +static int ads1119_read_avail(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + const int **vals, int *type, int *length, + long mask) +{ + switch (mask) { + case IIO_CHAN_INFO_SCALE: + *type = IIO_VAL_FRACTIONAL; + *vals = ads1119_available_gains; + *length = ARRAY_SIZE(ads1119_available_gains); + return IIO_AVAIL_LIST; + case IIO_CHAN_INFO_SAMP_FREQ: + *type = IIO_VAL_INT; + *vals = ads1119_available_datarates; + *length = ARRAY_SIZE(ads1119_available_datarates); + return IIO_AVAIL_LIST; + default: + return -EINVAL; + } +} + +static int ads1119_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int *val, + int *val2, long mask) +{ + struct ads1119_state *st = iio_priv(indio_dev); + unsigned int index = chan->address; + int ret; + + if (index >= st->num_channels_cfg) + return -EINVAL; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; + ret = ads1119_single_conversion(st, chan, val, false); + iio_device_release_direct(indio_dev); + return ret; + case IIO_CHAN_INFO_OFFSET: + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; + ret = ads1119_single_conversion(st, chan, val, true); + iio_device_release_direct(indio_dev); + return ret; + case IIO_CHAN_INFO_SCALE: + *val = st->vref_uV / 1000; + *val /= st->channels_cfg[index].gain; + *val2 = chan->scan_type.realbits - 1; + return IIO_VAL_FRACTIONAL_LOG2; + case IIO_CHAN_INFO_SAMP_FREQ: + *val = st->channels_cfg[index].datarate; + return IIO_VAL_INT; + default: + return -EINVAL; + } +} + +static int ads1119_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int val, + int val2, long mask) +{ + struct ads1119_state *st = iio_priv(indio_dev); + unsigned int index = chan->address; + int ret; + + if (index >= st->num_channels_cfg) + return -EINVAL; + + switch (mask) { + case IIO_CHAN_INFO_SCALE: + ret = MICRO / ((val * MICRO) + val2); + if (ret != 1 && ret != 4) + return -EINVAL; + + st->channels_cfg[index].gain = ret; + return 0; + case IIO_CHAN_INFO_SAMP_FREQ: + ret = ads1119_validate_datarate(st, val); + if (ret < 0) + return ret; + + st->channels_cfg[index].datarate = ret; + return 0; + default: + return -EINVAL; + } +} + +static int ads1119_debugfs_reg_access(struct iio_dev *indio_dev, + unsigned int reg, unsigned int writeval, + unsigned int *readval) +{ + struct ads1119_state *st = iio_priv(indio_dev); + int ret; + + if (reg > ADS1119_REG_STATUS) + return -EINVAL; + + if (readval) { + ret = i2c_smbus_read_byte_data(st->client, + ADS1119_CMD_RREG(reg)); + if (ret < 0) + return ret; + + *readval = ret; + return 0; + } + + if (reg > ADS1119_REG_CONFIG) + return -EINVAL; + + return i2c_smbus_write_byte_data(st->client, ADS1119_CMD_WREG, + writeval); +} + +static const struct iio_info ads1119_info = { + .read_avail = ads1119_read_avail, + .read_raw = ads1119_read_raw, + .write_raw = ads1119_write_raw, + .debugfs_reg_access = ads1119_debugfs_reg_access, +}; + +static int ads1119_triggered_buffer_preenable(struct iio_dev *indio_dev) +{ + struct ads1119_state *st = iio_priv(indio_dev); + struct device *dev = &st->client->dev; + unsigned int index; + int ret; + + index = find_first_bit(indio_dev->active_scan_mask, + iio_get_masklength(indio_dev)); + + ret = ads1119_set_conv_mode(st, true); + if (ret) + return ret; + + ret = ads1119_configure_channel(st, + st->channels_cfg[index].mux, + st->channels_cfg[index].gain, + st->channels_cfg[index].datarate); + if (ret) + return ret; + + ret = pm_runtime_resume_and_get(dev); + if (ret) + return ret; + + ret = i2c_smbus_write_byte(st->client, ADS1119_CMD_START_SYNC); + if (ret) + pm_runtime_put_autosuspend(dev); + + return ret; +} + +static int ads1119_triggered_buffer_postdisable(struct iio_dev *indio_dev) +{ + struct ads1119_state *st = iio_priv(indio_dev); + struct device *dev = &st->client->dev; + int ret; + + ret = ads1119_set_conv_mode(st, false); + if (ret) + return ret; + + pm_runtime_put_autosuspend(dev); + + return 0; +} + +static const struct iio_buffer_setup_ops ads1119_buffer_setup_ops = { + .preenable = ads1119_triggered_buffer_preenable, + .postdisable = ads1119_triggered_buffer_postdisable, + .validate_scan_mask = &iio_validate_scan_mask_onehot, +}; + +static const struct iio_trigger_ops ads1119_trigger_ops = { + .validate_device = &iio_trigger_validate_own_device, +}; + +static irqreturn_t ads1119_irq_handler(int irq, void *dev_id) +{ + struct iio_dev *indio_dev = dev_id; + struct ads1119_state *st = iio_priv(indio_dev); + + if (iio_buffer_enabled(indio_dev) && iio_trigger_using_own(indio_dev)) + iio_trigger_poll(indio_dev->trig); + else + complete(&st->completion); + + return IRQ_HANDLED; +} + +static irqreturn_t ads1119_trigger_handler(int irq, void *private) +{ + struct iio_poll_func *pf = private; + struct iio_dev *indio_dev = pf->indio_dev; + struct ads1119_state *st = iio_priv(indio_dev); + struct { + s16 sample; + aligned_s64 timestamp; + } scan = { }; + unsigned int index; + int ret; + + if (!iio_trigger_using_own(indio_dev)) { + index = find_first_bit(indio_dev->active_scan_mask, + iio_get_masklength(indio_dev)); + + ret = ads1119_poll_data_ready(st, &indio_dev->channels[index]); + if (ret) { + dev_err(&st->client->dev, + "Failed to poll data on trigger (%d)\n", ret); + goto done; + } + } + + ret = i2c_smbus_read_word_swapped(st->client, ADS1119_CMD_RDATA); + if (ret < 0) { + dev_err(&st->client->dev, + "Failed to read data on trigger (%d)\n", ret); + goto done; + } + + scan.sample = ret; + + iio_push_to_buffers_with_ts(indio_dev, &scan, sizeof(scan), + iio_get_time_ns(indio_dev)); +done: + iio_trigger_notify_done(indio_dev->trig); + return IRQ_HANDLED; +} + +static int ads1119_init(struct ads1119_state *st, bool vref_external) +{ + int ret; + + ret = ads1119_reset(st); + if (ret) + return ret; + + if (vref_external) + return ads1119_upd_cfg_reg(st, + ADS1119_CONFIG_VREF_FIELD, + FIELD_PREP(ADS1119_CONFIG_VREF_FIELD, + ADS1119_VREF_EXTERNAL)); + return 0; +} + +static int ads1119_map_analog_inputs_mux(int ain_pos, int ain_neg, + bool differential) +{ + if (ain_pos >= ADS1119_MAX_SINGLE_CHANNELS) + return -EINVAL; + + if (!differential) + return ADS1119_MUX_AIN0 + ain_pos; + + if (ain_pos == 0 && ain_neg == 1) + return ADS1119_MUX_AIN0_AIN1; + else if (ain_pos == 1 && ain_neg == 2) + return ADS1119_MUX_AIN1_AIN2; + else if (ain_pos == 2 && ain_neg == 3) + return ADS1119_MUX_AIN2_AIN3; + + return -EINVAL; +} + +static int ads1119_alloc_and_config_channels(struct iio_dev *indio_dev) +{ + const struct iio_chan_spec ads1119_channel = + (const struct iio_chan_spec) { + .type = IIO_VOLTAGE, + .indexed = 1, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SCALE) | + BIT(IIO_CHAN_INFO_OFFSET) | + BIT(IIO_CHAN_INFO_SAMP_FREQ), + .info_mask_shared_by_all_available = + BIT(IIO_CHAN_INFO_SCALE) | + BIT(IIO_CHAN_INFO_SAMP_FREQ), + .scan_type = { + .sign = 's', + .realbits = 16, + .storagebits = 16, + .endianness = IIO_CPU, + }, + }; + const struct iio_chan_spec ads1119_ts = IIO_CHAN_SOFT_TIMESTAMP(0); + struct ads1119_state *st = iio_priv(indio_dev); + struct iio_chan_spec *iio_channels, *chan; + struct device *dev = &st->client->dev; + unsigned int num_channels, i; + bool differential; + u32 ain[2]; + int ret; + + st->num_channels_cfg = device_get_child_node_count(dev); + if (st->num_channels_cfg > ADS1119_MAX_CHANNELS) + return dev_err_probe(dev, -EINVAL, + "Too many channels %d, max is %d\n", + st->num_channels_cfg, + ADS1119_MAX_CHANNELS); + + st->channels_cfg = devm_kcalloc(dev, st->num_channels_cfg, + sizeof(*st->channels_cfg), GFP_KERNEL); + if (!st->channels_cfg) + return -ENOMEM; + + /* Allocate one more iio channel for the timestamp */ + num_channels = st->num_channels_cfg + 1; + iio_channels = devm_kcalloc(dev, num_channels, sizeof(*iio_channels), + GFP_KERNEL); + if (!iio_channels) + return -ENOMEM; + + i = 0; + + device_for_each_child_node_scoped(dev, child) { + chan = &iio_channels[i]; + + differential = fwnode_property_present(child, "diff-channels"); + if (differential) + ret = fwnode_property_read_u32_array(child, + "diff-channels", + ain, 2); + else + ret = fwnode_property_read_u32(child, "single-channel", + &ain[0]); + + if (ret) + return dev_err_probe(dev, ret, + "Failed to get channel property\n"); + + ret = ads1119_map_analog_inputs_mux(ain[0], ain[1], + differential); + if (ret < 0) + return dev_err_probe(dev, ret, + "Invalid channel value\n"); + + st->channels_cfg[i].mux = ret; + st->channels_cfg[i].gain = ADS1119_DEFAULT_GAIN; + st->channels_cfg[i].datarate = ADS1119_DEFAULT_DATARATE; + + *chan = ads1119_channel; + chan->channel = ain[0]; + chan->address = i; + chan->scan_index = i; + + if (differential) { + chan->channel2 = ain[1]; + chan->differential = 1; + } + + dev_dbg(dev, "channel: index %d, mux %d\n", i, + st->channels_cfg[i].mux); + + i++; + } + + iio_channels[i] = ads1119_ts; + iio_channels[i].address = i; + iio_channels[i].scan_index = i; + + indio_dev->channels = iio_channels; + indio_dev->num_channels = num_channels; + + return 0; +} + +static void ads1119_powerdown(void *data) +{ + struct ads1119_state *st = data; + + i2c_smbus_write_byte(st->client, ADS1119_CMD_POWERDOWN); +} + +static int ads1119_probe(struct i2c_client *client) +{ + struct iio_dev *indio_dev; + struct ads1119_state *st; + struct device *dev = &client->dev; + bool vref_external = true; + int ret; + + indio_dev = devm_iio_device_alloc(dev, sizeof(*st)); + if (!indio_dev) + return -ENOMEM; + + st = iio_priv(indio_dev); + st->client = client; + + indio_dev->name = "ads1119"; + indio_dev->info = &ads1119_info; + indio_dev->modes = INDIO_DIRECT_MODE; + + i2c_set_clientdata(client, indio_dev); + + ret = devm_regulator_bulk_get_enable(dev, + ARRAY_SIZE(ads1119_power_supplies), + ads1119_power_supplies); + if (ret) + return dev_err_probe(dev, ret, + "Failed to get and enable supplies\n"); + + st->vref_uV = devm_regulator_get_enable_read_voltage(dev, "vref"); + if (st->vref_uV == -ENODEV) { + vref_external = false; + st->vref_uV = ADS1119_VREF_INTERNAL_VAL; + } else if (st->vref_uV < 0) { + return dev_err_probe(dev, st->vref_uV, "Failed to get vref\n"); + } + + st->reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW); + if (IS_ERR(st->reset_gpio)) + return dev_err_probe(dev, PTR_ERR(st->reset_gpio), + "Failed to get reset gpio\n"); + + ret = ads1119_alloc_and_config_channels(indio_dev); + if (ret) + return ret; + + init_completion(&st->completion); + + ret = devm_iio_triggered_buffer_setup(dev, indio_dev, NULL, + ads1119_trigger_handler, + &ads1119_buffer_setup_ops); + if (ret) + return dev_err_probe(dev, ret, "Failed to setup IIO buffer\n"); + + if (client->irq > 0) { + ret = devm_request_irq(dev, client->irq, ads1119_irq_handler, + IRQF_NO_THREAD, "ads1119", indio_dev); + if (ret) + return ret; + + st->trig = devm_iio_trigger_alloc(dev, "%s-dev%d", + indio_dev->name, + iio_device_id(indio_dev)); + if (!st->trig) + return -ENOMEM; + + st->trig->ops = &ads1119_trigger_ops; + iio_trigger_set_drvdata(st->trig, indio_dev); + + ret = devm_iio_trigger_register(dev, st->trig); + if (ret) + return dev_err_probe(dev, ret, + "Failed to register IIO trigger\n"); + } + + ret = ads1119_init(st, vref_external); + if (ret) + return dev_err_probe(dev, ret, + "Failed to initialize device\n"); + + pm_runtime_set_autosuspend_delay(dev, ADS1119_SUSPEND_DELAY); + pm_runtime_use_autosuspend(dev); + pm_runtime_mark_last_busy(dev); + pm_runtime_set_active(dev); + + ret = devm_pm_runtime_enable(dev); + if (ret) + return dev_err_probe(dev, ret, "Failed to enable pm runtime\n"); + + ret = devm_add_action_or_reset(dev, ads1119_powerdown, st); + if (ret) + return ret; + + return devm_iio_device_register(dev, indio_dev); +} + +static int ads1119_runtime_suspend(struct device *dev) +{ + struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev)); + struct ads1119_state *st = iio_priv(indio_dev); + + return i2c_smbus_write_byte(st->client, ADS1119_CMD_POWERDOWN); +} + +/* + * The ADS1119 does not require a resume function because it automatically + * powers on after a reset. + * After a power down command, the ADS1119 can still communicate but turns off + * its analog parts. To resume from power down, the device will power up again + * upon receiving a start/sync command. + */ +static DEFINE_RUNTIME_DEV_PM_OPS(ads1119_pm_ops, ads1119_runtime_suspend, + NULL, NULL); + +static const struct of_device_id __maybe_unused ads1119_of_match[] = { + { .compatible = "ti,ads1119" }, + { } +}; +MODULE_DEVICE_TABLE(of, ads1119_of_match); + +static const struct i2c_device_id ads1119_id[] = { + { .name = "ads1119" }, + { } +}; +MODULE_DEVICE_TABLE(i2c, ads1119_id); + +static struct i2c_driver ads1119_driver = { + .driver = { + .name = "ads1119", + .of_match_table = ads1119_of_match, + .pm = pm_ptr(&ads1119_pm_ops), + }, + .probe = ads1119_probe, + .id_table = ads1119_id, +}; +module_i2c_driver(ads1119_driver); + +MODULE_AUTHOR("João Paulo Gonçalves "); +MODULE_DESCRIPTION("Texas Instruments ADS1119 ADC Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/iio/adc/ti-ads112c14.c b/drivers/iio/adc/ti-ads112c14.c new file mode 100644 index 000000000..60eab4852 --- /dev/null +++ b/drivers/iio/adc/ti-ads112c14.c @@ -0,0 +1,1454 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * IIO driver for Texas Instruments ADS112C14 and similar ADCs. + * + * Copyright (C) 2026 Texas Instruments Incorporated - https://www.ti.com/ + * Copyright (C) 2026 Baylibre Inc. + * + * Datasheet: https://www.ti.com/lit/ds/symlink/ads122c14.pdf + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Arbitrary limit since channels are dynamic. */ +#define ADS112C14_MAX_MEASUREMENT_CHANNELS 16 + +/* Datasheet t_d(RST) - time to wait after reset before next I2C use. */ +#define ADS112C14_DELAY_RESET_US 500 + +#define ADS112C14_CMD_RDATA 0x00 +#define ADS112C14_CMD_RREG 0x40 +#define ADS112C14_CMD_WREG 0x80 + +#define ADS112C14_REG_DEVICE_ID 0x00 +#define ADS112C14_DEVICE_ID_BITS GENMASK(3, 0) + +#define ADS112C14_REG_REVISION_ID 0x01 + +#define ADS112C14_REG_STATUS_MSB 0x02 +#define ADS112C14_STATUS_MSB_RESETN BIT(7) +#define ADS112C14_STATUS_MSB_AVDD_UVN BIT(6) +#define ADS112C14_STATUS_MSB_REF_UVN BIT(5) +#define ADS112C14_STATUS_MSB_REG_MAP_CRC_FAULTN BIT(3) +#define ADS112C14_STATUS_MSB_MEM_FAULTN BIT(2) +#define ADS112C14_STATUS_MSB_REG_WRITE_FAULTN BIT(1) +#define ADS112C14_STATUS_MSB_DRDY BIT(0) + +#define ADS112C14_REG_STATUS_LSB 0x03 +#define ADS112C14_STATUS_LSB_CONV_COUNT GENMASK(7, 4) +#define ADS112C14_STATUS_LSB_GPIO3_DAT_IN BIT(3) +#define ADS112C14_STATUS_LSB_GPIO2_DAT_IN BIT(2) +#define ADS112C14_STATUS_LSB_GPIO1_DAT_IN BIT(1) +#define ADS112C14_STATUS_LSB_GPIO0_DAT_IN BIT(0) + +#define ADS112C14_REG_CONVERSION_CTRL 0x04 +#define ADS112C14_CONVERSION_CTRL_RESET GENMASK(7, 2) +#define ADS112C14_CONVERSION_CTRL_START BIT(1) +#define ADS112C14_CONVERSION_CTRL_STOP BIT(0) + +#define ADS112C14_REG_DEVICE_CFG 0x05 +#define ADS112C14_DEVICE_CFG_PWDN BIT(7) +#define ADS112C14_DEVICE_CFG_STBY_MODE BIT(6) +#define ADS112C14_DEVICE_CFG_BOCS GENMASK(5, 4) +#define ADS112C14_DEVICE_CFG_CLK_SEL BIT(3) +#define ADS112C14_DEVICE_CFG_CONV_MODE BIT(2) +#define ADS112C14_DEVICE_CFG_CONV_MODE_CONTINUOUS 0 +#define ADS112C14_DEVICE_CFG_CONV_MODE_SINGLE_SHOT 1 +#define ADS112C14_DEVICE_CFG_SPEED_MODE GENMASK(1, 0) + +#define ADS112C14_REG_DATA_RATE_CFG 0x06 +#define ADS112C14_DATA_RATE_CFG_DELAY GENMASK(7, 4) +#define ADS112C14_DATA_RATE_CFG_GC_EN BIT(3) +#define ADS112C14_DATA_RATE_CFG_FLTR_OSR GENMASK(2, 0) + +#define ADS112C14_REG_MUX_CFG 0x07 +#define ADS112C14_MUX_CFG_AINP GENMASK(7, 4) +#define ADS112C14_MUX_CFG_AINN GENMASK(3, 0) +#define ADS112C14_MUX_CFG_AIN_GND 8 + +#define ADS112C14_REG_GAIN_CFG 0x08 +#define ADS112C14_GAIN_CFG_SPARE BIT(7) +#define ADS112C14_GAIN_CFG_SYS_MON GENMASK(6, 4) +#define ADS112C14_GAIN_CFG_GAIN GENMASK(3, 0) + +#define ADS112C14_REG_REFERENCE_CFG 0x09 +#define ADS112C14_REFERENCE_CFG_REF_UV_EN BIT(7) +#define ADS112C14_REFERENCE_CFG_REFP_BUF_EN BIT(5) +#define ADS112C14_REFERENCE_CFG_REFN_BUF_EN BIT(4) +#define ADS112C14_REFERENCE_CFG_REF_VAL BIT(2) +#define ADS112C14_REFERENCE_CFG_REF_VAL_1_25V 0 +#define ADS112C14_REFERENCE_CFG_REF_VAL_2_5V 1 +#define ADS112C14_REFERENCE_CFG_REF_SEL GENMASK(1, 0) +#define ADS112C14_REFERENCE_CFG_REF_SEL_INTERNAL 0 +#define ADS112C14_REFERENCE_CFG_REF_SEL_EXTERNAL 1 +#define ADS112C14_REFERENCE_CFG_REF_SEL_AVDD 2 + +#define ADS112C14_REG_DIGITAL_CFG 0x0A +#define ADS112C14_DIGITAL_CFG_REG_MAP_CRC_EN BIT(6) +#define ADS112C14_DIGITAL_CFG_I2C_CRC_EN BIT(5) +#define ADS112C14_DIGITAL_CFG_STATUS_EN BIT(4) +#define ADS112C14_DIGITAL_CFG_FAULT_PIN_BEHAVIOR BIT(3) +#define ADS112C14_DIGITAL_CFG_CODING BIT(1) + +#define ADS112C14_REG_GPIO_CFG 0x0B +#define ADS112C14_GPIO_CFG_GPIO3_CFG GENMASK(7, 6) +#define ADS112C14_GPIO_CFG_GPIO2_CFG GENMASK(5, 4) +#define ADS112C14_GPIO_CFG_GPIO1_CFG GENMASK(3, 2) +#define ADS112C14_GPIO_CFG_GPIO0_CFG GENMASK(1, 0) + +#define ADS112C14_REG_GPIO_DATA_OUTPUT 0x0C +#define ADS112C14_GPIO_DATA_OUTPUT_GPIO3_SRC BIT(7) +#define ADS112C14_GPIO_DATA_OUTPUT_GPIO2_SRC BIT(6) +#define ADS112C14_GPIO_DATA_OUTPUT_GPIO3_DAT_OUT BIT(3) +#define ADS112C14_GPIO_DATA_OUTPUT_GPIO2_DAT_OUT BIT(2) +#define ADS112C14_GPIO_DATA_OUTPUT_GPIO1_DAT_OUT BIT(1) +#define ADS112C14_GPIO_DATA_OUTPUT_GPIO0_DAT_OUT BIT(0) + +#define ADS112C14_REG_IDAC_MAG_CFG 0x0D +#define ADS112C14_IDAC_MAG_CFG_I2MAG GENMASK(7, 4) +#define ADS112C14_IDAC_MAG_CFG_I1MAG GENMASK(3, 0) + +#define ADS112C14_REG_IDAC_MUX_CFG 0x0E +#define ADS112C14_IDAC_MUX_CFG_IUNIT BIT(7) +#define ADS112C14_IDAC_MUX_CFG_I2MUX GENMASK(6, 4) +#define ADS112C14_IDAC_MUX_CFG_I1MUX GENMASK(2, 0) + +#define ADS112C14_REG_REG_MAP_CRC 0x0F + +#define ADS112C14_INT_REF0_mV 1250 +#define ADS112C14_INT_REF1_mV 2500 + +enum { + ADS112C14_VREF_SOURCE_INTERNAL_2_5V, + ADS112C14_VREF_SOURCE_INTERNAL_1_25V, + ADS112C14_VREF_SOURCE_EXTERNAL, + ADS112C14_VREF_SOURCE_AVDD, +}; + +static const char * const ads112c14_vref_source_names[] = { + [ADS112C14_VREF_SOURCE_INTERNAL_2_5V] = "internal-2.5v", + [ADS112C14_VREF_SOURCE_INTERNAL_1_25V] = "internal-1.25v", + [ADS112C14_VREF_SOURCE_EXTERNAL] = "external", + [ADS112C14_VREF_SOURCE_AVDD] = "avdd", +}; + +/* + * Available gains as tenths (e.g. value 5 == 0.5 gain). Indexes correspond to + * ADS112C14_GAIN_CFG_GAIN values. + */ +static const u32 ads112c14_pga_gains_x10[] = { + 5, 10, 20, 40, 50, 80, 100, 160, /* 0 - 7 */ + 200, 320, 500, 640, 1000, 1280, 2000, 2560, /* 8 - 15 */ +}; + +#define ADS112C14_I2C_CRC8_POLYNOMIAL 0x07 +DECLARE_CRC8_TABLE(ads112c14_crc8_table); + +struct ads112c14_chip_info { + const char *name; + u8 device_id; + u32 resolution_bits; +}; + +/* Fixed channels for system monitor measurements. */ + +#define ADS112C14_SYS_MON_CHANNEL_BASE 100 + +enum { + ADS112C14_SYS_MON_CHANNEL_TEMP = ADS112C14_SYS_MON_CHANNEL_BASE, + ADS112C14_SYS_MON_CHANNEL_EXT_REF, + ADS112C14_SYS_MON_CHANNEL_AVDD, + ADS112C14_SYS_MON_CHANNEL_DVDD, + ADS112C14_SYS_MON_CHANNEL_SHORT, +}; + +static const struct iio_chan_spec ads112c14_sys_mon_channels[] = { + { + .type = IIO_TEMP, + .indexed = 1, + .channel = ADS112C14_SYS_MON_CHANNEL_TEMP, + .address = 2, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) + | BIT(IIO_CHAN_INFO_SCALE) + | BIT(IIO_CHAN_INFO_OFFSET), + }, + { + .type = IIO_VOLTAGE, + .indexed = 1, + .channel = ADS112C14_SYS_MON_CHANNEL_EXT_REF, + .address = 3, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) + | BIT(IIO_CHAN_INFO_SCALE), + }, + { + .type = IIO_VOLTAGE, + .indexed = 1, + .channel = ADS112C14_SYS_MON_CHANNEL_AVDD, + .address = 4, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) + | BIT(IIO_CHAN_INFO_SCALE), + }, + { + .type = IIO_VOLTAGE, + .indexed = 1, + .channel = ADS112C14_SYS_MON_CHANNEL_DVDD, + .address = 5, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) + | BIT(IIO_CHAN_INFO_SCALE), + }, + { + .type = IIO_VOLTAGE, + .indexed = 1, + .channel = ADS112C14_SYS_MON_CHANNEL_SHORT, + .channel2 = ADS112C14_SYS_MON_CHANNEL_SHORT, + .differential = 1, + .address = 1, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) + | BIT(IIO_CHAN_INFO_SCALE), + .info_mask_separate_available = BIT(IIO_CHAN_INFO_SCALE), + }, +}; + +struct ads112c14_measurement { + const char *label; + u32 vref_source; + u8 iunit; + u8 idac1_mag; + u8 idac2_mag; + u8 idac1_mux; + u8 idac2_mux; + u8 iadc_count; + u8 gain_val; + bool global_chop; + bool bipolar; + int scale_available[ARRAY_SIZE(ads112c14_pga_gains_x10)][2]; +}; + +struct ads112c14_data { + const struct ads112c14_chip_info *chip_info; + struct regmap *regmap; + /* Synchronizes access to register value fields. */ + struct mutex lock; + bool i2c_crc_enabled; + u32 avdd_uV; + u32 ext_ref_uV; + bool refp_is_avdd; + bool refn_is_gnd; + u32 ext_ref_ohms; + struct ads112c14_measurement *measurements; + u32 num_measurements; + u8 sys_mon_chan_short_gain_val; + int sys_mon_chan_short_scale_available[ARRAY_SIZE(ads112c14_pga_gains_x10)][2]; + IIO_DECLARE_BUFFER_WITH_TS(__be32, scan, ADS112C14_MAX_MEASUREMENT_CHANNELS + + ARRAY_SIZE(ads112c14_sys_mon_channels)); +}; + +static bool ads112c14_writeable_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case ADS112C14_REG_DEVICE_ID: + case ADS112C14_REG_REVISION_ID: + case ADS112C14_REG_STATUS_LSB: + return false; + default: + return true; + } +} + +static bool ads112c14_volatile_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case ADS112C14_REG_STATUS_MSB: + case ADS112C14_REG_STATUS_LSB: + case ADS112C14_REG_CONVERSION_CTRL: + return true; + default: + return false; + } +} + +static const struct reg_default ads112c14_reg_defaults[] = { + { ADS112C14_REG_DEVICE_CFG, 0 }, + { ADS112C14_REG_DATA_RATE_CFG, 0 }, + { ADS112C14_REG_MUX_CFG, 0 }, + { ADS112C14_REG_GAIN_CFG, FIELD_PREP_CONST(ADS112C14_GAIN_CFG_GAIN, 1) }, + { ADS112C14_REG_REFERENCE_CFG, 0 }, + { ADS112C14_REG_DIGITAL_CFG, 0 }, + { ADS112C14_REG_GPIO_CFG, 0 }, + { ADS112C14_REG_GPIO_DATA_OUTPUT, 0 }, + { ADS112C14_REG_IDAC_MAG_CFG, 0 }, + { ADS112C14_REG_IDAC_MUX_CFG, FIELD_PREP_CONST(ADS112C14_IDAC_MUX_CFG_I2MUX, 1) }, +}; + +/** + * ads112c14_i2c_read_bytes() - Read bytes from the device over I2C + * @client: I2C client for the device + * @cmd: Command to send to the device before reading + * @buf: Buffer to store the read bytes + * @len: Number of bytes to read + * @use_crc: Whether to use CRC8 for data integrity check + * + * If I2C_CRC is enabled, @use_crc may be set to true to perform a CRC8 check + * on the received data. + */ +static int ads112c14_i2c_read_bytes(struct i2c_client *client, u8 cmd, + u8 *buf, u8 len, bool use_crc) +{ + u8 rx_buf[4]; /* Up to 3 data bytes + 1 CRC byte. */ + u8 rx_len; + int ret; + + rx_len = len + (use_crc ? 1 : 0); + + if (rx_len > sizeof(rx_buf)) + return -EINVAL; + + ret = i2c_smbus_read_i2c_block_data(client, cmd, rx_len, rx_buf); + if (ret < 0) + return ret; + + if (use_crc) { + u8 crc = crc8(ads112c14_crc8_table, rx_buf, len, CRC8_INIT_VALUE); + + if (crc != rx_buf[len]) + return -EBADMSG; + } + + memcpy(buf, rx_buf, len); + + return 0; +} + +/** + * ads112c14_regmap_bus_read() - Read a register from the device + * @context: Pointer to the device context + * @reg_buf: Register address to read + * @reg_size: Size of the register address (should be 1) + * @val_buf: Buffer to store the read value + * @val_size: Size of the value to read + * + * Custom regmap read function that also does CRC check when enabled. + */ +static int ads112c14_regmap_bus_read(void *context, const void *reg_buf, + size_t reg_size, void *val_buf, + size_t val_size) +{ + struct ads112c14_data *data = context; + struct device *dev = regmap_get_device(data->regmap); + struct i2c_client *client = to_i2c_client(dev); + const u8 *cmd = reg_buf; + + if (reg_size != 1) + return -EINVAL; + + return ads112c14_i2c_read_bytes(client, cmd[0], val_buf, val_size, + data->i2c_crc_enabled); +} + +/** + * ads112c14_regmap_bus_write() - Write a register to the device + * @context: Pointer to the device context + * @data_buf: Buffer containing the register address and value to write + * @count: Number of bytes to write + * + * Custom regmap write function that also does readback with CRC check of + * nonvolatile registers when CRC is enabled. + */ +static int ads112c14_regmap_bus_write(void *context, const void *data_buf, + size_t count) +{ + struct ads112c14_data *data = context; + struct device *dev = regmap_get_device(data->regmap); + struct i2c_client *client = to_i2c_client(dev); + const u8 *tx = data_buf; + u8 reg, readback; + int ret; + + if (count != 2) + return -EINVAL; + + ret = i2c_smbus_write_byte_data(client, tx[0], tx[1]); + if (ret) + return ret; + + reg = tx[0] & ~ADS112C14_CMD_WREG; + + if (!data->i2c_crc_enabled || ads112c14_volatile_reg(dev, reg)) + return 0; + + ret = ads112c14_i2c_read_bytes(client, reg | ADS112C14_CMD_RREG, + &readback, sizeof(readback), true); + if (ret) + return ret; + + if (readback != tx[1]) + return -EIO; + + return 0; +} + +static const struct regmap_bus ads112c14_regmap_bus = { + .read = ads112c14_regmap_bus_read, + .write = ads112c14_regmap_bus_write, + .reg_format_endian_default = REGMAP_ENDIAN_BIG, + .val_format_endian_default = REGMAP_ENDIAN_BIG, +}; + +static const struct regmap_config ads112c14_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .read_flag_mask = ADS112C14_CMD_RREG, + .write_flag_mask = ADS112C14_CMD_WREG, + .max_register = ADS112C14_REG_REG_MAP_CRC, + .writeable_reg = ads112c14_writeable_reg, + .volatile_reg = ads112c14_volatile_reg, + .reg_defaults = ads112c14_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(ads112c14_reg_defaults), + .cache_type = REGCACHE_MAPLE, +}; + +static int ads112c14_prepare_measurement_channel(struct ads112c14_data *data, + const struct iio_chan_spec *chan) +{ + struct ads112c14_measurement *measurement = &data->measurements[chan->scan_index]; + u32 refp_buf_en, refn_buf_en, ref_val, ref_sel; + int ret; + + ret = regmap_update_bits(data->regmap, ADS112C14_REG_MUX_CFG, + ADS112C14_MUX_CFG_AINP | ADS112C14_MUX_CFG_AINN, + FIELD_PREP(ADS112C14_MUX_CFG_AINP, chan->channel) | + FIELD_PREP(ADS112C14_MUX_CFG_AINN, chan->channel2)); + if (ret) + return ret; + + ret = regmap_assign_bits(data->regmap, ADS112C14_REG_DIGITAL_CFG, + ADS112C14_DIGITAL_CFG_CODING, + !measurement->bipolar); + if (ret) + return ret; + + ret = regmap_update_bits(data->regmap, ADS112C14_REG_GAIN_CFG, + ADS112C14_GAIN_CFG_SYS_MON | + ADS112C14_GAIN_CFG_GAIN, + FIELD_PREP(ADS112C14_GAIN_CFG_SYS_MON, 0) | + FIELD_PREP(ADS112C14_GAIN_CFG_GAIN, + measurement->gain_val)); + if (ret) + return ret; + + ret = regmap_update_bits(data->regmap, ADS112C14_REG_IDAC_MAG_CFG, + ADS112C14_IDAC_MAG_CFG_I2MAG | + ADS112C14_IDAC_MAG_CFG_I1MAG, + FIELD_PREP(ADS112C14_IDAC_MAG_CFG_I2MAG, + measurement->idac2_mag) | + FIELD_PREP(ADS112C14_IDAC_MAG_CFG_I1MAG, + measurement->idac1_mag)); + if (ret) + return ret; + + ret = regmap_update_bits(data->regmap, ADS112C14_REG_IDAC_MUX_CFG, + ADS112C14_IDAC_MUX_CFG_IUNIT | + ADS112C14_IDAC_MUX_CFG_I2MUX | + ADS112C14_IDAC_MUX_CFG_I1MUX, + FIELD_PREP(ADS112C14_IDAC_MUX_CFG_IUNIT, + measurement->iunit) | + FIELD_PREP(ADS112C14_IDAC_MUX_CFG_I2MUX, + measurement->idac2_mux) | + FIELD_PREP(ADS112C14_IDAC_MUX_CFG_I1MUX, + measurement->idac1_mux)); + if (ret) + return ret; + + ret = regmap_update_bits(data->regmap, ADS112C14_REG_DATA_RATE_CFG, + ADS112C14_DATA_RATE_CFG_GC_EN, + FIELD_PREP(ADS112C14_DATA_RATE_CFG_GC_EN, + measurement->global_chop)); + if (ret) + return ret; + + refp_buf_en = !data->refp_is_avdd && + measurement->vref_source == ADS112C14_VREF_SOURCE_EXTERNAL; + refn_buf_en = !data->refn_is_gnd && + measurement->vref_source == ADS112C14_VREF_SOURCE_EXTERNAL; + + ref_val = measurement->vref_source == ADS112C14_VREF_SOURCE_INTERNAL_2_5V ? + ADS112C14_REFERENCE_CFG_REF_VAL_2_5V : + ADS112C14_REFERENCE_CFG_REF_VAL_1_25V; + + switch (measurement->vref_source) { + case ADS112C14_VREF_SOURCE_AVDD: + ref_sel = ADS112C14_REFERENCE_CFG_REF_SEL_AVDD; + break; + case ADS112C14_VREF_SOURCE_EXTERNAL: + ref_sel = ADS112C14_REFERENCE_CFG_REF_SEL_EXTERNAL; + break; + default: + ref_sel = ADS112C14_REFERENCE_CFG_REF_SEL_INTERNAL; + break; + } + + return regmap_update_bits(data->regmap, ADS112C14_REG_REFERENCE_CFG, + ADS112C14_REFERENCE_CFG_REFP_BUF_EN | + ADS112C14_REFERENCE_CFG_REFN_BUF_EN | + ADS112C14_REFERENCE_CFG_REF_VAL | + ADS112C14_REFERENCE_CFG_REF_SEL, + FIELD_PREP(ADS112C14_REFERENCE_CFG_REFP_BUF_EN, + refp_buf_en) | + FIELD_PREP(ADS112C14_REFERENCE_CFG_REFN_BUF_EN, + refn_buf_en) | + FIELD_PREP(ADS112C14_REFERENCE_CFG_REF_VAL, + ref_val) | + FIELD_PREP(ADS112C14_REFERENCE_CFG_REF_SEL, + ref_sel)); +} + +static int ads112c14_prepare_sys_mon_channel(struct ads112c14_data *data, + const struct iio_chan_spec *chan) +{ + u32 gain_val; + int ret; + + /* + * NB: IDAC registers are left as-is in case they are generating current + * needed for the external reference measurement. + */ + + /* + * All SYS_MON channels use GAIN of 1 to keep it simple. Other than + * the internal short channel, where it is useful in practice. + */ + gain_val = chan->channel == ADS112C14_SYS_MON_CHANNEL_SHORT ? + data->sys_mon_chan_short_gain_val : 1; + + ret = regmap_update_bits(data->regmap, ADS112C14_REG_GAIN_CFG, + ADS112C14_GAIN_CFG_SYS_MON | + ADS112C14_GAIN_CFG_GAIN, + FIELD_PREP(ADS112C14_GAIN_CFG_SYS_MON, chan->address) | + FIELD_PREP(ADS112C14_GAIN_CFG_GAIN, gain_val)); + if (ret) + return ret; + + /* All SYS_MON channels use signed data to keep it simple. */ + ret = regmap_clear_bits(data->regmap, ADS112C14_REG_DIGITAL_CFG, + ADS112C14_DIGITAL_CFG_CODING); + if (ret) + return ret; + + /* + * REVISIT: if we implement regulator support for the REFOUT pin, we + * might need to make this voltage match what is required by that. In + * that case, we could also adjust GAIN so that we still get the same + * range. + */ + /* + * NB: SYS_MON channels ignore REF_SEL except for the shorted input + * channel, so we set it here to internal reference to be consistent. + * If we ever need to make a measurement of shorted input with other + * reference source, we could add additional channels for that. + */ + ret = regmap_update_bits(data->regmap, ADS112C14_REG_REFERENCE_CFG, + ADS112C14_REFERENCE_CFG_REF_VAL | + ADS112C14_REFERENCE_CFG_REF_SEL, + FIELD_PREP(ADS112C14_REFERENCE_CFG_REF_VAL, + ADS112C14_REFERENCE_CFG_REF_VAL_2_5V) | + FIELD_PREP(ADS112C14_REFERENCE_CFG_REF_SEL, + ADS112C14_REFERENCE_CFG_REF_SEL_INTERNAL)); + if (ret) + return ret; + + return 0; +} + +static int ads112c14_single_conversion(struct ads112c14_data *data, + const struct iio_chan_spec *chan, + u8 *buf, bool for_scan) +{ + struct i2c_client *client = to_i2c_client(regmap_get_device(data->regmap)); + u32 reg_val; + int ret; + + guard(mutex)(&data->lock); + + if (chan->channel < ADS112C14_SYS_MON_CHANNEL_BASE) { + ret = ads112c14_prepare_measurement_channel(data, chan); + if (ret) + return ret; + } else { + ret = ads112c14_prepare_sys_mon_channel(data, chan); + if (ret) + return ret; + } + + ret = regmap_write(data->regmap, ADS112C14_REG_CONVERSION_CTRL, + ADS112C14_CONVERSION_CTRL_START); + if (ret) + return ret; + + ret = regmap_read_poll_timeout(data->regmap, + ADS112C14_REG_STATUS_MSB, reg_val, + FIELD_GET(ADS112C14_STATUS_MSB_DRDY, reg_val), + 1 * USEC_PER_MSEC, 100 * USEC_PER_MSEC); + if (ret) + return ret; + + /* + * When doing buffered read, we don't check the CRC, but rather pass it + * along with the raw data. This way, we don't silently drop samples + * with CRC errors, but rather leave it to userspace to decide what to + * do. + */ + if (for_scan) { + u8 len = BITS_TO_BYTES(data->chip_info->resolution_bits) + + (data->i2c_crc_enabled ? 1 : 0); + + ret = i2c_smbus_read_i2c_block_data(client, ADS112C14_CMD_RDATA, + len, buf); + if (ret < 0) + return ret; + + return 0; + } + + return ads112c14_i2c_read_bytes(client, ADS112C14_CMD_RDATA, buf, + BITS_TO_BYTES(data->chip_info->resolution_bits), + data->i2c_crc_enabled); +} + +static int ads112c14_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct ads112c14_data *data = iio_priv(indio_dev); + struct ads112c14_measurement *measurement = NULL; + const int *scale_avail; + u32 vref_uV, fsr_bits; + + /* Selecting V_REF source is not implemented yet. */ + vref_uV = ADS112C14_INT_REF1_mV * (MICRO / MILLI); + + if (chan->channel < ADS112C14_SYS_MON_CHANNEL_BASE) { + measurement = &data->measurements[chan->scan_index]; + fsr_bits = data->chip_info->resolution_bits - measurement->bipolar; + } else { + /* All SYS_MON channels are using signed coding. */ + fsr_bits = data->chip_info->resolution_bits - 1; + } + + switch (mask) { + case IIO_CHAN_INFO_RAW: { + u8 buf[3]; + int ret; + + IIO_DEV_ACQUIRE_DIRECT_MODE(indio_dev, claim); + if (IIO_DEV_ACQUIRE_FAILED(claim)) + return -EBUSY; + + ret = ads112c14_single_conversion(data, chan, buf, false); + if (ret) + return ret; + + switch (data->chip_info->resolution_bits) { + case 16: + *val = get_unaligned_be16(buf); + break; + case 24: + *val = get_unaligned_be24(buf); + break; + default: + return -EINVAL; + } + + if (!measurement || measurement->bipolar) + *val = sign_extend32(*val, fsr_bits); + + return IIO_VAL_INT; + } + case IIO_CHAN_INFO_SCALE: + if (chan->type == IIO_TEMP) { + /* TS_TC (typical) = 405 uV/°C */ + *val = MILLI * vref_uV / 405; + *val2 = fsr_bits; + return IIO_VAL_FRACTIONAL_LOG2; + } + + if (chan->channel < ADS112C14_SYS_MON_CHANNEL_BASE) { + guard(mutex)(&data->lock); + + scale_avail = measurement->scale_available[measurement->gain_val]; + *val = scale_avail[0]; + *val2 = scale_avail[1]; + + return IIO_VAL_DECIMAL64_PICO; + } + + if (chan->channel == ADS112C14_SYS_MON_CHANNEL_SHORT) { + u8 idx; + + guard(mutex)(&data->lock); + + idx = data->sys_mon_chan_short_gain_val; + scale_avail = data->sys_mon_chan_short_scale_available[idx]; + *val = scale_avail[0]; + *val2 = scale_avail[1]; + + return IIO_VAL_DECIMAL64_PICO; + } + + *val = vref_uV / (MICRO / MILLI); + + /* + * Some SYS_MON channels (ext ref, AVDD, DVDD) need to be + * multiplied by 8 to account for internal attenuation of / 8. + */ + switch (chan->address) { + case 3 ... 5: + *val2 = fsr_bits - 3; + break; + default: + *val2 = fsr_bits; + break; + } + + return IIO_VAL_FRACTIONAL_LOG2; + case IIO_CHAN_INFO_OFFSET: + /* Only the temperature channel has an offset. */ + if (chan->type != IIO_TEMP) + return -EINVAL; + /* + * Die temperature [°C] = 25°C + (Measured voltage – TS_Offset) / TS_TC + * TS_TC (typical) = 405 uV/°C + * TS_Offset (typical) = 119.5 mV + */ + *val = div_s64((s64)(25 * 405 - 119500) * BIT(fsr_bits), vref_uV); + return IIO_VAL_INT; + default: + return -EINVAL; + } +} + +static int ads112c14_read_avail(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, const int **vals, + int *type, int *length, long mask) +{ + struct ads112c14_data *data = iio_priv(indio_dev); + + switch (mask) { + case IIO_CHAN_INFO_SCALE: + if (chan->channel < ADS112C14_SYS_MON_CHANNEL_BASE) { + struct ads112c14_measurement *measurement; + + guard(mutex)(&data->lock); + + measurement = &data->measurements[chan->scan_index]; + *vals = (const int *)measurement->scale_available; + *length = 2 * ARRAY_SIZE(measurement->scale_available); + *type = IIO_VAL_DECIMAL64_PICO; + return IIO_AVAIL_LIST; + } + + if (chan->channel == ADS112C14_SYS_MON_CHANNEL_SHORT) { + guard(mutex)(&data->lock); + + *vals = (const int *)data->sys_mon_chan_short_scale_available; + *length = 2 * ARRAY_SIZE(data->sys_mon_chan_short_scale_available); + *type = IIO_VAL_DECIMAL64_PICO; + return IIO_AVAIL_LIST; + } + + return -EINVAL; + default: + return -EINVAL; + } +} + +static int ads112c14_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int val, + int val2, long mask) +{ + struct ads112c14_data *data = iio_priv(indio_dev); + const int (*scale_avail)[2]; + u8 *gain_val; + + IIO_DEV_ACQUIRE_DIRECT_MODE(indio_dev, claim); + if (IIO_DEV_ACQUIRE_FAILED(claim)) + return -EBUSY; + + switch (mask) { + case IIO_CHAN_INFO_SCALE: { + guard(mutex)(&data->lock); + + if (chan->channel < ADS112C14_SYS_MON_CHANNEL_BASE) { + struct ads112c14_measurement *measurement; + + measurement = &data->measurements[chan->scan_index]; + scale_avail = measurement->scale_available; + gain_val = &measurement->gain_val; + } else if (chan->channel == ADS112C14_SYS_MON_CHANNEL_SHORT) { + scale_avail = data->sys_mon_chan_short_scale_available; + gain_val = &data->sys_mon_chan_short_gain_val; + } else { + return -EINVAL; + } + + for (u32 i = 0; i < ARRAY_SIZE(ads112c14_pga_gains_x10); i++) { + if (iio_val_s64_compose(val, val2) == + iio_val_s64_compose(scale_avail[i][0], scale_avail[i][1])) { + *gain_val = i; + return 0; + } + } + + return -EINVAL; + } + default: + return -EINVAL; + } +} + +static int ads112c14_write_raw_get_fmt(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + long mask) +{ + switch (mask) { + case IIO_CHAN_INFO_SCALE: + return IIO_VAL_DECIMAL64_PICO; + default: + return IIO_VAL_INT_PLUS_MICRO; + } +} + +static int ads112c14_debugfs_reg_access(struct iio_dev *indio_dev, + unsigned int reg, + unsigned int writeval, + unsigned int *readval) +{ + struct ads112c14_data *data = iio_priv(indio_dev); + + if (readval) + return regmap_read(data->regmap, reg, readval); + + return regmap_write(data->regmap, reg, writeval); +} + +static int ads112c14_read_label(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, char *label) +{ + struct ads112c14_data *data = iio_priv(indio_dev); + const char *label_source; + + /* measurement channels */ + if (chan->channel < ADS112C14_SYS_MON_CHANNEL_BASE) { + struct ads112c14_measurement *measurement; + + measurement = &data->measurements[chan->scan_index]; + if (!measurement->label) + return -EINVAL; + + return sysfs_emit(label, "%s\n", measurement->label); + } + + /* System monitor channels. */ + switch (chan->channel) { + case ADS112C14_SYS_MON_CHANNEL_TEMP: + label_source = "Internal temperature sensor"; + break; + case ADS112C14_SYS_MON_CHANNEL_EXT_REF: + label_source = "External reference"; + break; + case ADS112C14_SYS_MON_CHANNEL_AVDD: + label_source = "AVDD"; + break; + case ADS112C14_SYS_MON_CHANNEL_DVDD: + label_source = "DVDD"; + break; + case ADS112C14_SYS_MON_CHANNEL_SHORT: + label_source = "Internal short (internal reference source)"; + break; + default: + return -EINVAL; + } + + return sysfs_emit(label, "%s\n", label_source); +} + +static irqreturn_t ads112c14_trigger_handler(int irq, void *private) +{ + struct iio_poll_func *pf = private; + struct iio_dev *indio_dev = pf->indio_dev; + struct ads112c14_data *data = iio_priv(indio_dev); + u32 offset = 0; + u32 i; + int ret; + + iio_for_each_active_channel(indio_dev, i) { + const struct iio_chan_spec *chan = &indio_dev->channels[i]; + + ret = ads112c14_single_conversion(data, chan, + (u8 *)&data->scan[offset++], + true); + if (ret) { + dev_err_once(indio_dev->dev.parent, + "failed to read channel %d: %pe; additional errors will be suppressed\n", + chan->channel, ERR_PTR(ret)); + goto out; + } + } + + iio_push_to_buffers_with_ts(indio_dev, data->scan, + sizeof(data->scan), pf->timestamp); +out: + iio_trigger_notify_done(indio_dev->trig); + + return IRQ_HANDLED; +} + +static const struct iio_info ads112c14_info = { + .read_raw = ads112c14_read_raw, + .read_avail = ads112c14_read_avail, + .write_raw = ads112c14_write_raw, + .write_raw_get_fmt = ads112c14_write_raw_get_fmt, + .debugfs_reg_access = ads112c14_debugfs_reg_access, + .read_label = ads112c14_read_label, +}; + +static int ads112c14_populate_idac_mag(u32 current_nA, u8 *idac_mag) +{ + u32 current_uA = current_nA / (NANO / MICRO); + + /* Convert microamps to IMAG bits */ + if (current_uA == 1) + *idac_mag = 1; + else if (in_range(current_uA, 10, 100) && current_uA % 10 == 0) + *idac_mag = current_uA / 10 + 1; + else + return dev_err_probe(NULL, -EINVAL, + "invalid excitation-current-nanoamp value\n"); + + return 0; +} + +static int ads112c14_parse_channels(struct iio_dev *indio_dev, + bool *need_avdd_ref, bool *need_ext_ref) +{ + struct ads112c14_data *data = iio_priv(indio_dev); + struct device *dev = indio_dev->dev.parent; + struct iio_chan_spec *channels; + u32 num_child_nodes, i, pair[2]; + int ret; + + *need_avdd_ref = false; + *need_ext_ref = false; + + num_child_nodes = device_get_named_child_node_count(dev, "channel"); + + data->measurements = devm_kcalloc(dev, num_child_nodes, + sizeof(*data->measurements), GFP_KERNEL); + if (!data->measurements) + return -ENOMEM; + + channels = devm_kcalloc(dev, num_child_nodes + + ARRAY_SIZE(ads112c14_sys_mon_channels) + 1, + sizeof(*channels), GFP_KERNEL); + if (!channels) + return -ENOMEM; + + i = 0; + device_for_each_named_child_node_scoped(dev, child, "channel") { + struct ads112c14_measurement *measurement = &data->measurements[i]; + struct iio_chan_spec *spec = &channels[i]; + + spec->indexed = 1; + spec->scan_index = i; + measurement->gain_val = 1; + + if (fwnode_property_present(child, "label")) { + ret = fwnode_property_read_string(child, "label", &measurement->label); + if (ret) + return dev_err_probe(dev, ret, + "failed to read label property\n"); + } + + if (fwnode_property_present(child, "single-channel")) { + ret = fwnode_property_read_u32(child, "single-channel", + &pair[0]); + if (ret) + return dev_err_probe(dev, ret, + "failed to read single-channel property\n"); + + if (pair[0] >= 8) + return dev_err_probe(dev, -EINVAL, + "single-channel value must be between 0 and 7\n"); + + spec->channel = pair[0]; + /* + * NB: channel2 is unused by iio core code in this case. + * Let's us avoid special case for negative input mux + * for single-ended channels when taking measurements. + */ + spec->channel2 = ADS112C14_MUX_CFG_AIN_GND; + } else if (fwnode_property_present(child, "diff-channels")) { + ret = fwnode_property_read_u32_array(child, "diff-channels", + pair, ARRAY_SIZE(pair)); + if (ret) + return dev_err_probe(dev, ret, + "failed to read diff-channels property\n"); + + if (pair[0] >= 8 || pair[1] >= 8) + return dev_err_probe(dev, -EINVAL, + "diff-channels values must be between 0 and 7\n"); + + spec->differential = 1; + spec->channel = pair[0]; + spec->channel2 = pair[1]; + } else { + return dev_err_probe(dev, -EINVAL, + "channel node missing channel type property\n"); + } + + if (fwnode_property_present(child, "excitation-channels")) { + ret = fwnode_property_count_u32(child, "excitation-channels"); + if (ret < 0) + return dev_err_probe(dev, ret, + "failed to read excitation-channels property\n"); + + if (ret < 1 || ret > 2) + return dev_err_probe(dev, -EINVAL, + "excitation-channels property must have 1 or 2 values\n"); + + measurement->iadc_count = ret; + pair[1] = 0; + + ret = fwnode_property_read_u32_array(child, "excitation-channels", + pair, measurement->iadc_count); + if (ret) + return dev_err_probe(dev, ret, + "failed to read excitation-channels property\n"); + + if (pair[0] >= 8 || pair[1] >= 8) + return dev_err_probe(dev, -EINVAL, + "excitation-channels values must be between 0 and 7\n"); + + measurement->idac1_mux = pair[0]; + measurement->idac2_mux = measurement->iadc_count > 1 ? pair[1] : 0; + + ret = fwnode_property_read_u32_array(child, "excitation-current-nanoamp", + pair, measurement->iadc_count); + if (ret) + return dev_err_probe(dev, ret, + "failed to read excitation-current-nanoamp property\n"); + + if (pair[0] <= 100 * (NANO / MICRO) && + (measurement->iadc_count == 1 || pair[1] <= 100 * (NANO / MICRO))) { + /* + * If both values are 100µA or less, then we can + * use IUNIT = 1µA for better precision. + */ + ret = ads112c14_populate_idac_mag(pair[0], + &measurement->idac1_mag); + if (ret) + return ret; + + if (measurement->iadc_count > 1) { + ret = ads112c14_populate_idac_mag(pair[1], + &measurement->idac2_mag); + if (ret) + return ret; + } + } else { + /* + * Otherwise, IUINT is 10µA (flag set) and so + * IxMAG is 1/10 of the actual current. + */ + measurement->iunit = 1; + + ret = ads112c14_populate_idac_mag(pair[0] / 10, + &measurement->idac1_mag); + if (ret) + return ret; + + if (measurement->iadc_count > 1) { + ret = ads112c14_populate_idac_mag(pair[1] / 10, + &measurement->idac2_mag); + if (ret) + return ret; + } + } + } + + measurement->bipolar = fwnode_property_read_bool(child, "bipolar"); + measurement->global_chop = fwnode_property_read_bool(child, + "input-chopping"); + + if (fwnode_property_present(child, "reference-sources")) { + ret = fwnode_property_match_property_string(child, + "reference-sources", ads112c14_vref_source_names, + ARRAY_SIZE(ads112c14_vref_source_names)); + if (ret < 0) + return dev_err_probe(dev, ret, + "invalid reference-sources value\n"); + + measurement->vref_source = ret; + } + + if (measurement->vref_source == ADS112C14_VREF_SOURCE_AVDD) + *need_avdd_ref = true; + if (measurement->vref_source == ADS112C14_VREF_SOURCE_EXTERNAL) + *need_ext_ref = true; + + spec->info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE); + spec->info_mask_separate_available = BIT(IIO_CHAN_INFO_SCALE); + + /* + * If reference source is resistor rather than voltage supply, + * then the measurement is effectively a resistance measurement. + */ + spec->type = (measurement->vref_source == ADS112C14_VREF_SOURCE_EXTERNAL && + data->ext_ref_ohms) ? IIO_RESISTANCE : IIO_VOLTAGE; + + if (spec->type == IIO_RESISTANCE) + spec->differential = 0; + + spec->scan_type = (struct iio_scan_type){ + .format = measurement->bipolar ? + IIO_SCAN_FORMAT_SIGNED_INT : + IIO_SCAN_FORMAT_UNSIGNED_INT, + .realbits = data->chip_info->resolution_bits, + .storagebits = 32, + .shift = 32 - data->chip_info->resolution_bits, + .endianness = IIO_BE, + }; + + i++; + } + + data->num_measurements = i; + if (data->num_measurements > ADS112C14_MAX_MEASUREMENT_CHANNELS) + return dev_err_probe(dev, -EINVAL, + "too many measurement channels defined\n"); + + memcpy(channels + i, ads112c14_sys_mon_channels, sizeof(ads112c14_sys_mon_channels)); + + for (u32 j = 0; j < ARRAY_SIZE(ads112c14_sys_mon_channels); j++) { + struct iio_chan_spec *spec = &channels[i]; + + /* Update the template that was already copied with dynamic values. */ + spec->scan_index = i; + spec->scan_type = (struct iio_scan_type){ + .format = IIO_SCAN_FORMAT_SIGNED_INT, + .realbits = data->chip_info->resolution_bits, + .storagebits = 32, + .shift = 32 - data->chip_info->resolution_bits, + .endianness = IIO_BE, + }; + + i++; + } + + channels[i] = IIO_CHAN_SOFT_TIMESTAMP(i); + i++; + + indio_dev->channels = channels; + indio_dev->num_channels = i; + + return 0; +} + +static void ads112c14_populate_scale_available(int (*scale_avail)[2], + u32 full_scale, u32 fsr_bits) +{ + for (u32 i = 0; i < ARRAY_SIZE(ads112c14_pga_gains_x10); i++) { + u64 gain_x10 = ads112c14_pga_gains_x10[i]; + s64 scale; + + scale = div64_u64((u64)PICO * 10U * full_scale, + gain_x10 * BIT(fsr_bits)); + + iio_val_s64_decompose(scale, &scale_avail[i][0], + &scale_avail[i][1]); + } +} + +static void ads112c14_populate_tables(struct ads112c14_data *data) +{ + u32 full_scale, fsr_bits; + + for (u32 i = 0; i < data->num_measurements; i++) { + struct ads112c14_measurement *measurement = &data->measurements[i]; + + switch (measurement->vref_source) { + case ADS112C14_VREF_SOURCE_EXTERNAL: + if (data->ext_ref_ohms) + full_scale = data->ext_ref_ohms; + else + full_scale = data->ext_ref_uV / (MICRO / MILLI); + break; + case ADS112C14_VREF_SOURCE_AVDD: + full_scale = data->avdd_uV / (MICRO / MILLI); + break; + case ADS112C14_VREF_SOURCE_INTERNAL_1_25V: + full_scale = ADS112C14_INT_REF0_mV; + break; + default: + full_scale = ADS112C14_INT_REF1_mV; + break; + } + + fsr_bits = data->chip_info->resolution_bits - measurement->bipolar; + + ads112c14_populate_scale_available(measurement->scale_available, + full_scale, fsr_bits); + } + + /* For now, assuming all sys_mon channels are using 2.5V reference. */ + full_scale = ADS112C14_INT_REF1_mV; + fsr_bits = data->chip_info->resolution_bits - 1; + + ads112c14_populate_scale_available(data->sys_mon_chan_short_scale_available, + full_scale, fsr_bits); +} + +static int ads112c14_probe(struct i2c_client *client) +{ + struct device *dev = &client->dev; + const struct ads112c14_chip_info *info; + struct iio_dev *indio_dev; + struct ads112c14_data *data; + bool need_avdd_ref, need_ext_ref; + u32 refp_uV = 0; + u32 refn_uV = 0; + u32 reg_val; + int ret; + + info = i2c_get_match_data(client); + if (!info) + return dev_err_probe(dev, -ENODEV, "missing match data\n"); + + indio_dev = devm_iio_device_alloc(dev, sizeof(*data)); + if (!indio_dev) + return -ENOMEM; + + data = iio_priv(indio_dev); + data->chip_info = info; + + ret = devm_mutex_init(dev, &data->lock); + if (ret) + return ret; + + if (device_property_present(dev, "ti,refp-refn-resistor-ohms")) { + ret = device_property_read_u32(dev, "ti,refp-refn-resistor-ohms", + &data->ext_ref_ohms); + if (ret) + return dev_err_probe(dev, ret, + "failed to read ti,refp-refn-resistor-ohms property\n"); + } + + ret = ads112c14_parse_channels(indio_dev, &need_avdd_ref, &need_ext_ref); + if (ret) + return ret; + + ret = devm_regulator_get_enable(dev, "dvdd"); + if (ret) + return dev_err_probe(dev, ret, "failed to get dvdd regulator\n"); + + if (need_avdd_ref) { + ret = devm_regulator_get_enable_read_voltage(dev, "avdd"); + if (ret < 0) + return dev_err_probe(dev, ret, "failed to get avdd voltage\n"); + + data->avdd_uV = ret; + } else { + ret = devm_regulator_get_enable(dev, "avdd"); + if (ret) + return dev_err_probe(dev, ret, "failed to get avdd regulator\n"); + } + + if (device_property_present(dev, "refp-supply")) { + ret = devm_regulator_get_enable_read_voltage(dev, "refp"); + if (ret < 0) + return dev_err_probe(dev, ret, "failed to get refp voltage\n"); + + refp_uV = ret; + + struct fwnode_handle *refp_fwnode __free(fwnode_handle) = + fwnode_find_reference(dev->fwnode, "refp-supply", 0); + if (IS_ERR(refp_fwnode)) + return dev_err_probe(dev, PTR_ERR(refp_fwnode), + "failed to get refp fwnode\n"); + + struct fwnode_handle *avdd_fwnode __free(fwnode_handle) = + fwnode_find_reference(dev->fwnode, "avdd-supply", 0); + if (IS_ERR(avdd_fwnode)) + return dev_err_probe(dev, PTR_ERR(avdd_fwnode), + "failed to get avdd fwnode\n"); + + /* REFP buffer should not be enabled when connected to AVDD */ + data->refp_is_avdd = refp_fwnode == avdd_fwnode; + } + + if (device_property_present(dev, "refn-supply")) { + ret = devm_regulator_get_enable_read_voltage(dev, "refn"); + if (ret < 0) + return dev_err_probe(dev, ret, "failed to get refn voltage\n"); + + refn_uV = ret; + } else { + data->refn_is_gnd = true; + } + + data->ext_ref_uV = refp_uV - refn_uV; + + if (data->ext_ref_uV && data->ext_ref_ohms) + return dev_err_probe(dev, -EINVAL, + "ti,refp-refn-resistor-ohms property should not be present when refp-supply or refn-supply is present\n"); + + if (need_ext_ref && !data->ext_ref_uV && !data->ext_ref_ohms) + return dev_err_probe(dev, -EINVAL, + "external reference measurements require either refp-supply or ti,refp-refn-resistor-ohms property\n"); + + /* It takes some time for the internal reference to stabilize. */ + fsleep(10 * USEC_PER_MSEC); + + data->regmap = devm_regmap_init(dev, &ads112c14_regmap_bus, data, + &ads112c14_regmap_config); + if (IS_ERR(data->regmap)) + return dev_err_probe(dev, PTR_ERR(data->regmap), + "failed to init regmap\n"); + + /* + * Write magic reset value (0x16) to ensure known state. The reset may + * cause an error because of failing to get the I2C ACK at the end of + * the message. The device still gets reset so it is safe to ignore the + * return value here. If something else is wrong, later read/write will + * likely have the same error. + */ + regmap_write(data->regmap, ADS112C14_REG_CONVERSION_CTRL, + FIELD_PREP(ADS112C14_CONVERSION_CTRL_RESET, 0x16)); + + fsleep(ADS112C14_DELAY_RESET_US); + + ret = regmap_read(data->regmap, ADS112C14_REG_STATUS_MSB, ®_val); + if (ret) + return ret; + + if (FIELD_GET(ADS112C14_STATUS_MSB_RESETN, reg_val)) + return dev_err_probe(dev, -EIO, "reset failed\n"); + + /* Default gain after reset is 1. */ + data->sys_mon_chan_short_gain_val = 1; + + /* + * Clear reset bit to prepare for next probe. And clear AVDD fault since + * that happens on every reset. + */ + ret = regmap_write(data->regmap, ADS112C14_REG_STATUS_MSB, + ADS112C14_STATUS_MSB_RESETN | + ADS112C14_STATUS_MSB_AVDD_UVN); + if (ret) + return ret; + + ret = regmap_set_bits(data->regmap, ADS112C14_REG_DIGITAL_CFG, + ADS112C14_DIGITAL_CFG_I2C_CRC_EN); + if (ret) + return ret; + + data->i2c_crc_enabled = true; + + ret = regmap_read(data->regmap, ADS112C14_REG_DEVICE_ID, ®_val); + if (ret) + return ret; + + if (FIELD_GET(ADS112C14_DEVICE_ID_BITS, reg_val) != info->device_id) + dev_info(dev, "device ID mismatch, expected 0x%X, got 0x%lX\n", + info->device_id, + FIELD_GET(ADS112C14_DEVICE_ID_BITS, reg_val)); + + ret = regmap_update_bits(data->regmap, ADS112C14_REG_DEVICE_CFG, + ADS112C14_DEVICE_CFG_CONV_MODE, + FIELD_PREP(ADS112C14_DEVICE_CFG_CONV_MODE, + ADS112C14_DEVICE_CFG_CONV_MODE_SINGLE_SHOT)); + if (ret) + return ret; + + ads112c14_populate_tables(data); + + indio_dev->name = info->name; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->info = &ads112c14_info; + + ret = devm_iio_triggered_buffer_setup(dev, indio_dev, + iio_pollfunc_store_time, + ads112c14_trigger_handler, NULL); + if (ret) + return ret; + + return devm_iio_device_register(dev, indio_dev); +} + +static const struct ads112c14_chip_info ads112c14_chip_info = { + .name = "ads112c14", + .device_id = 0xE, + .resolution_bits = 16, +}; + +static const struct ads112c14_chip_info ads122c14_chip_info = { + .name = "ads122c14", + .device_id = 0xF, + .resolution_bits = 24, +}; + +static const struct of_device_id ads112c14_of_match[] = { + { .compatible = "ti,ads112c14", .data = &ads112c14_chip_info }, + { .compatible = "ti,ads122c14", .data = &ads122c14_chip_info }, + { } +}; +MODULE_DEVICE_TABLE(of, ads112c14_of_match); + +static const struct i2c_device_id ads112c14_id[] = { + { .name = "ads112c14", .driver_data = (kernel_ulong_t)&ads112c14_chip_info }, + { .name = "ads122c14", .driver_data = (kernel_ulong_t)&ads122c14_chip_info }, + { } +}; +MODULE_DEVICE_TABLE(i2c, ads112c14_id); + +static int ads112c14_i2c_add_driver(struct i2c_driver *driver) +{ + crc8_populate_msb(ads112c14_crc8_table, ADS112C14_I2C_CRC8_POLYNOMIAL); + + return i2c_add_driver(driver); +} + +static struct i2c_driver ads112c14_driver = { + .driver = { + .name = "ads112c14", + .of_match_table = ads112c14_of_match, + }, + .probe = ads112c14_probe, + .id_table = ads112c14_id, +}; +module_driver(ads112c14_driver, ads112c14_i2c_add_driver, i2c_del_driver); + +MODULE_AUTHOR("David Lechner (TI) "); +MODULE_DESCRIPTION("TI ADS112C14 I2C ADC driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/iio/adc/ti-ads124s08.c b/drivers/iio/adc/ti-ads124s08.c new file mode 100644 index 000000000..49fbc6350 --- /dev/null +++ b/drivers/iio/adc/ti-ads124s08.c @@ -0,0 +1,376 @@ +// SPDX-License-Identifier: GPL-2.0 +/* TI ADS124S0X chip family driver + * Copyright (C) 2018 Texas Instruments Incorporated - https://www.ti.com/ + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include + +#include + +/* Commands */ +#define ADS124S08_CMD_NOP 0x00 +#define ADS124S08_CMD_WAKEUP 0x02 +#define ADS124S08_CMD_PWRDWN 0x04 +#define ADS124S08_CMD_RESET 0x06 +#define ADS124S08_CMD_START 0x08 +#define ADS124S08_CMD_STOP 0x0a +#define ADS124S08_CMD_SYOCAL 0x16 +#define ADS124S08_CMD_SYGCAL 0x17 +#define ADS124S08_CMD_SFOCAL 0x19 +#define ADS124S08_CMD_RDATA 0x12 +#define ADS124S08_CMD_RREG 0x20 +#define ADS124S08_CMD_WREG 0x40 + +/* Registers */ +#define ADS124S08_ID_REG 0x00 +#define ADS124S08_STATUS 0x01 +#define ADS124S08_INPUT_MUX 0x02 +#define ADS124S08_PGA 0x03 +#define ADS124S08_DATA_RATE 0x04 +#define ADS124S08_REF 0x05 +#define ADS124S08_IDACMAG 0x06 +#define ADS124S08_IDACMUX 0x07 +#define ADS124S08_VBIAS 0x08 +#define ADS124S08_SYS 0x09 +#define ADS124S08_OFCAL0 0x0a +#define ADS124S08_OFCAL1 0x0b +#define ADS124S08_OFCAL2 0x0c +#define ADS124S08_FSCAL0 0x0d +#define ADS124S08_FSCAL1 0x0e +#define ADS124S08_FSCAL2 0x0f +#define ADS124S08_GPIODAT 0x10 +#define ADS124S08_GPIOCON 0x11 + +/* ADS124S0x common channels */ +#define ADS124S08_AIN0 0x00 +#define ADS124S08_AIN1 0x01 +#define ADS124S08_AIN2 0x02 +#define ADS124S08_AIN3 0x03 +#define ADS124S08_AIN4 0x04 +#define ADS124S08_AIN5 0x05 +#define ADS124S08_AINCOM 0x0c +/* ADS124S08 only channels */ +#define ADS124S08_AIN6 0x06 +#define ADS124S08_AIN7 0x07 +#define ADS124S08_AIN8 0x08 +#define ADS124S08_AIN9 0x09 +#define ADS124S08_AIN10 0x0a +#define ADS124S08_AIN11 0x0b +#define ADS124S08_MAX_CHANNELS 12 + +#define ADS124S08_POS_MUX_SHIFT 0x04 +#define ADS124S08_INT_REF 0x09 + +#define ADS124S08_START_REG_MASK 0x1f +#define ADS124S08_NUM_BYTES_MASK 0x1f + +#define ADS124S08_START_CONV 0x01 +#define ADS124S08_STOP_CONV 0x00 + +enum ads124s_id { + ADS124S08_ID, + ADS124S06_ID, +}; + +struct ads124s_chip_info { + const struct iio_chan_spec *channels; + unsigned int num_channels; +}; + +struct ads124s_private { + const struct ads124s_chip_info *chip_info; + struct gpio_desc *reset_gpio; + struct spi_device *spi; + struct mutex lock; + /* + * Used to correctly align data. + * Ensure timestamp is naturally aligned. + * Note that the full buffer length may not be needed if not + * all channels are enabled, as long as the alignment of the + * timestamp is maintained. + */ + u32 buffer[ADS124S08_MAX_CHANNELS + sizeof(s64)/sizeof(u32)] __aligned(8); + u8 data[5] __aligned(IIO_DMA_MINALIGN); +}; + +#define ADS124S08_CHAN(index) \ +{ \ + .type = IIO_VOLTAGE, \ + .indexed = 1, \ + .channel = index, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .scan_index = index, \ + .scan_type = { \ + .sign = 'u', \ + .realbits = 32, \ + .storagebits = 32, \ + }, \ +} + +static const struct iio_chan_spec ads124s06_channels[] = { + ADS124S08_CHAN(0), + ADS124S08_CHAN(1), + ADS124S08_CHAN(2), + ADS124S08_CHAN(3), + ADS124S08_CHAN(4), + ADS124S08_CHAN(5), +}; + +static const struct iio_chan_spec ads124s08_channels[] = { + ADS124S08_CHAN(0), + ADS124S08_CHAN(1), + ADS124S08_CHAN(2), + ADS124S08_CHAN(3), + ADS124S08_CHAN(4), + ADS124S08_CHAN(5), + ADS124S08_CHAN(6), + ADS124S08_CHAN(7), + ADS124S08_CHAN(8), + ADS124S08_CHAN(9), + ADS124S08_CHAN(10), + ADS124S08_CHAN(11), +}; + +static const struct ads124s_chip_info ads124s_chip_info_tbl[] = { + [ADS124S08_ID] = { + .channels = ads124s08_channels, + .num_channels = ARRAY_SIZE(ads124s08_channels), + }, + [ADS124S06_ID] = { + .channels = ads124s06_channels, + .num_channels = ARRAY_SIZE(ads124s06_channels), + }, +}; + +static int ads124s_write_cmd(struct iio_dev *indio_dev, u8 command) +{ + struct ads124s_private *priv = iio_priv(indio_dev); + + priv->data[0] = command; + + return spi_write(priv->spi, &priv->data[0], 1); +} + +static int ads124s_write_reg(struct iio_dev *indio_dev, u8 reg, u8 data) +{ + struct ads124s_private *priv = iio_priv(indio_dev); + + priv->data[0] = ADS124S08_CMD_WREG | reg; + priv->data[1] = 0x0; + priv->data[2] = data; + + return spi_write(priv->spi, &priv->data[0], 3); +} + +static int ads124s_reset(struct iio_dev *indio_dev) +{ + struct ads124s_private *priv = iio_priv(indio_dev); + + if (priv->reset_gpio) { + gpiod_set_value_cansleep(priv->reset_gpio, 0); + fsleep(200); + gpiod_set_value_cansleep(priv->reset_gpio, 1); + } else { + return ads124s_write_cmd(indio_dev, ADS124S08_CMD_RESET); + } + + return 0; +}; + +static int ads124s_read(struct iio_dev *indio_dev) +{ + struct ads124s_private *priv = iio_priv(indio_dev); + int ret; + struct spi_transfer t[] = { + { + .tx_buf = &priv->data[0], + .len = 4, + .cs_change = 1, + }, { + .tx_buf = &priv->data[1], + .rx_buf = &priv->data[1], + .len = 4, + }, + }; + + priv->data[0] = ADS124S08_CMD_RDATA; + memset(&priv->data[1], ADS124S08_CMD_NOP, sizeof(priv->data) - 1); + + ret = spi_sync_transfer(priv->spi, t, ARRAY_SIZE(t)); + if (ret < 0) + return ret; + + return get_unaligned_be24(&priv->data[2]); +} + +static int ads124s_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long m) +{ + struct ads124s_private *priv = iio_priv(indio_dev); + int ret; + + mutex_lock(&priv->lock); + switch (m) { + case IIO_CHAN_INFO_RAW: + ret = ads124s_write_reg(indio_dev, ADS124S08_INPUT_MUX, + chan->channel); + if (ret) { + dev_err(&priv->spi->dev, "Set ADC CH failed\n"); + goto out; + } + + ret = ads124s_write_cmd(indio_dev, ADS124S08_START_CONV); + if (ret) { + dev_err(&priv->spi->dev, "Start conversions failed\n"); + goto out; + } + + ret = ads124s_read(indio_dev); + if (ret < 0) { + dev_err(&priv->spi->dev, "Read ADC failed\n"); + goto out; + } + + *val = ret; + + ret = ads124s_write_cmd(indio_dev, ADS124S08_STOP_CONV); + if (ret) { + dev_err(&priv->spi->dev, "Stop conversions failed\n"); + goto out; + } + + ret = IIO_VAL_INT; + break; + default: + ret = -EINVAL; + break; + } +out: + mutex_unlock(&priv->lock); + return ret; +} + +static const struct iio_info ads124s_info = { + .read_raw = &ads124s_read_raw, +}; + +static irqreturn_t ads124s_trigger_handler(int irq, void *p) +{ + struct iio_poll_func *pf = p; + struct iio_dev *indio_dev = pf->indio_dev; + struct ads124s_private *priv = iio_priv(indio_dev); + int scan_index, j = 0; + int ret; + + iio_for_each_active_channel(indio_dev, scan_index) { + ret = ads124s_write_reg(indio_dev, ADS124S08_INPUT_MUX, + scan_index); + if (ret) + dev_err(&priv->spi->dev, "Set ADC CH failed\n"); + + ret = ads124s_write_cmd(indio_dev, ADS124S08_START_CONV); + if (ret) + dev_err(&priv->spi->dev, "Start ADC conversions failed\n"); + + priv->buffer[j] = ads124s_read(indio_dev); + ret = ads124s_write_cmd(indio_dev, ADS124S08_STOP_CONV); + if (ret) + dev_err(&priv->spi->dev, "Stop ADC conversions failed\n"); + + j++; + } + + iio_push_to_buffers_with_ts(indio_dev, priv->buffer, sizeof(priv->buffer), + pf->timestamp); + + iio_trigger_notify_done(indio_dev->trig); + + return IRQ_HANDLED; +} + +static int ads124s_probe(struct spi_device *spi) +{ + struct ads124s_private *ads124s_priv; + struct iio_dev *indio_dev; + const struct spi_device_id *spi_id = spi_get_device_id(spi); + int ret; + + indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*ads124s_priv)); + if (indio_dev == NULL) + return -ENOMEM; + + ads124s_priv = iio_priv(indio_dev); + + ads124s_priv->reset_gpio = devm_gpiod_get_optional(&spi->dev, + "reset", GPIOD_OUT_LOW); + if (IS_ERR(ads124s_priv->reset_gpio)) + return dev_err_probe(&spi->dev, PTR_ERR(ads124s_priv->reset_gpio), + "Failed to get reset GPIO\n"); + + ads124s_priv->chip_info = &ads124s_chip_info_tbl[spi_id->driver_data]; + + ads124s_priv->spi = spi; + + indio_dev->name = spi_id->name; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = ads124s_priv->chip_info->channels; + indio_dev->num_channels = ads124s_priv->chip_info->num_channels; + indio_dev->info = &ads124s_info; + + mutex_init(&ads124s_priv->lock); + + ret = devm_iio_triggered_buffer_setup(&spi->dev, indio_dev, NULL, + ads124s_trigger_handler, NULL); + if (ret) { + dev_err(&spi->dev, "iio triggered buffer setup failed\n"); + return ret; + } + + ads124s_reset(indio_dev); + + return devm_iio_device_register(&spi->dev, indio_dev); +} + +static const struct spi_device_id ads124s_id[] = { + { .name = "ads124s06", .driver_data = ADS124S06_ID }, + { .name = "ads124s08", .driver_data = ADS124S08_ID }, + { } +}; +MODULE_DEVICE_TABLE(spi, ads124s_id); + +static const struct of_device_id ads124s_of_table[] = { + { .compatible = "ti,ads124s06" }, + { .compatible = "ti,ads124s08" }, + { } +}; +MODULE_DEVICE_TABLE(of, ads124s_of_table); + +static struct spi_driver ads124s_driver = { + .driver = { + .name = "ads124s08", + .of_match_table = ads124s_of_table, + }, + .probe = ads124s_probe, + .id_table = ads124s_id, +}; +module_spi_driver(ads124s_driver); + +MODULE_AUTHOR("Dan Murphy "); +MODULE_DESCRIPTION("TI TI_ADS12S0X ADC"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/adc/ti-ads1298.c b/drivers/iio/adc/ti-ads1298.c new file mode 100644 index 000000000..06c6353aa --- /dev/null +++ b/drivers/iio/adc/ti-ads1298.c @@ -0,0 +1,769 @@ +// SPDX-License-Identifier: GPL-2.0 +/* TI ADS1298 chip family driver + * Copyright (C) 2023 - 2024 Topic Embedded Products + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +/* Commands */ +#define ADS1298_CMD_WAKEUP 0x02 +#define ADS1298_CMD_STANDBY 0x04 +#define ADS1298_CMD_RESET 0x06 +#define ADS1298_CMD_START 0x08 +#define ADS1298_CMD_STOP 0x0a +#define ADS1298_CMD_RDATAC 0x10 +#define ADS1298_CMD_SDATAC 0x11 +#define ADS1298_CMD_RDATA 0x12 +#define ADS1298_CMD_RREG 0x20 +#define ADS1298_CMD_WREG 0x40 + +/* Registers */ +#define ADS1298_REG_ID 0x00 +#define ADS1298_MASK_ID_FAMILY GENMASK(7, 3) +#define ADS1298_MASK_ID_CHANNELS GENMASK(2, 0) +#define ADS1298_ID_FAMILY_ADS129X 0x90 +#define ADS1298_ID_FAMILY_ADS129XR 0xd0 + +#define ADS1298_REG_CONFIG1 0x01 +#define ADS1298_MASK_CONFIG1_HR BIT(7) +#define ADS1298_MASK_CONFIG1_DR GENMASK(2, 0) +#define ADS1298_SHIFT_DR_HR 6 +#define ADS1298_SHIFT_DR_LP 7 +#define ADS1298_LOWEST_DR 0x06 + +#define ADS1298_REG_CONFIG2 0x02 +#define ADS1298_MASK_CONFIG2_RESERVED BIT(6) +#define ADS1298_MASK_CONFIG2_WCT_CHOP BIT(5) +#define ADS1298_MASK_CONFIG2_INT_TEST BIT(4) +#define ADS1298_MASK_CONFIG2_TEST_AMP BIT(2) +#define ADS1298_MASK_CONFIG2_TEST_FREQ_DC GENMASK(1, 0) +#define ADS1298_MASK_CONFIG2_TEST_FREQ_SLOW 0 +#define ADS1298_MASK_CONFIG2_TEST_FREQ_FAST BIT(0) + +#define ADS1298_REG_CONFIG3 0x03 +#define ADS1298_MASK_CONFIG3_PWR_REFBUF BIT(7) +#define ADS1298_MASK_CONFIG3_RESERVED BIT(6) +#define ADS1298_MASK_CONFIG3_VREF_4V BIT(5) + +#define ADS1298_REG_LOFF 0x04 +#define ADS1298_REG_CHnSET(n) (0x05 + (n)) +#define ADS1298_MASK_CH_PD BIT(7) +#define ADS1298_MASK_CH_PGA GENMASK(6, 4) +#define ADS1298_MASK_CH_MUX GENMASK(2, 0) + +#define ADS1298_REG_LOFF_STATP 0x12 +#define ADS1298_REG_LOFF_STATN 0x13 +#define ADS1298_REG_CONFIG4 0x17 +#define ADS1298_MASK_CONFIG4_SINGLE_SHOT BIT(3) + +#define ADS1298_REG_WCT1 0x18 +#define ADS1298_REG_WCT2 0x19 + +#define ADS1298_MAX_CHANNELS 8 +#define ADS1298_BITS_PER_SAMPLE 24 +#define ADS1298_CLK_RATE_HZ 2048000 +#define ADS1298_CLOCKS_TO_USECS(x) \ + (DIV_ROUND_UP((x) * MICROHZ_PER_HZ, ADS1298_CLK_RATE_HZ)) +/* + * Read/write register commands require 4 clocks to decode, for speeds above + * 2x the clock rate, this would require extra time between the command byte and + * the data. Much simpler is to just limit the SPI transfer speed while doing + * register access. + */ +#define ADS1298_SPI_BUS_SPEED_SLOW ADS1298_CLK_RATE_HZ +/* For reading and writing registers, we need a 3-byte buffer */ +#define ADS1298_SPI_CMD_BUFFER_SIZE 3 +/* Outputs status word and 'n' 24-bit samples, plus the command byte */ +#define ADS1298_SPI_RDATA_BUFFER_SIZE(n) (((n) + 1) * 3 + 1) +#define ADS1298_SPI_RDATA_BUFFER_SIZE_MAX \ + ADS1298_SPI_RDATA_BUFFER_SIZE(ADS1298_MAX_CHANNELS) + +struct ads1298_private { + const struct ads1298_chip_info *chip_info; + struct spi_device *spi; + struct regulator *reg_avdd; + struct regulator *reg_vref; + struct clk *clk; + struct regmap *regmap; + struct completion completion; + struct iio_trigger *trig; + struct spi_transfer rdata_xfer; + struct spi_message rdata_msg; + spinlock_t irq_busy_lock; /* Handshake between SPI and DRDY irqs */ + /* + * rdata_xfer_busy increments when a DRDY occurs and decrements when SPI + * completion is reported. Hence its meaning is: + * 0 = Waiting for DRDY interrupt + * 1 = SPI transfer in progress + * 2 = DRDY during SPI transfer, start another transfer on completion + * >2 = Multiple DRDY during transfer, lost rdata_xfer_busy - 2 samples + */ + unsigned int rdata_xfer_busy; + + /* Temporary storage for demuxing data after SPI transfer */ + u32 bounce_buffer[ADS1298_MAX_CHANNELS]; + + /* For synchronous SPI exchanges (read/write registers) */ + u8 cmd_buffer[ADS1298_SPI_CMD_BUFFER_SIZE] __aligned(IIO_DMA_MINALIGN); + + /* Buffer used for incoming SPI data */ + u8 rx_buffer[ADS1298_SPI_RDATA_BUFFER_SIZE_MAX]; + /* Contains the RDATA command and zeroes to clock out */ + u8 tx_buffer[ADS1298_SPI_RDATA_BUFFER_SIZE_MAX]; +}; + +/* Three bytes per sample in RX buffer, starting at offset 4 */ +#define ADS1298_OFFSET_IN_RX_BUFFER(index) (3 * (index) + 4) + +#define ADS1298_CHAN(index) \ +{ \ + .type = IIO_VOLTAGE, \ + .indexed = 1, \ + .channel = index, \ + .address = ADS1298_OFFSET_IN_RX_BUFFER(index), \ + .info_mask_separate = \ + BIT(IIO_CHAN_INFO_RAW) | \ + BIT(IIO_CHAN_INFO_SCALE), \ + .info_mask_shared_by_all = \ + BIT(IIO_CHAN_INFO_SAMP_FREQ) | \ + BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \ + .scan_index = index, \ + .scan_type = { \ + .sign = 's', \ + .realbits = ADS1298_BITS_PER_SAMPLE, \ + .storagebits = 32, \ + .endianness = IIO_CPU, \ + }, \ +} + +static const struct iio_chan_spec ads1298_channels[] = { + ADS1298_CHAN(0), + ADS1298_CHAN(1), + ADS1298_CHAN(2), + ADS1298_CHAN(3), + ADS1298_CHAN(4), + ADS1298_CHAN(5), + ADS1298_CHAN(6), + ADS1298_CHAN(7), +}; + +static int ads1298_write_cmd(struct ads1298_private *priv, u8 command) +{ + struct spi_transfer xfer = { + .tx_buf = priv->cmd_buffer, + .rx_buf = priv->cmd_buffer, + .len = 1, + .speed_hz = ADS1298_SPI_BUS_SPEED_SLOW, + .delay = { + .value = 2, + .unit = SPI_DELAY_UNIT_USECS, + }, + }; + + priv->cmd_buffer[0] = command; + + return spi_sync_transfer(priv->spi, &xfer, 1); +} + +static int ads1298_read_one(struct ads1298_private *priv, int chan_index) +{ + int ret; + + /* Enable the channel */ + ret = regmap_update_bits(priv->regmap, ADS1298_REG_CHnSET(chan_index), + ADS1298_MASK_CH_PD, 0); + if (ret) + return ret; + + /* Enable single-shot mode, so we don't need to send a STOP */ + ret = regmap_update_bits(priv->regmap, ADS1298_REG_CONFIG4, + ADS1298_MASK_CONFIG4_SINGLE_SHOT, + ADS1298_MASK_CONFIG4_SINGLE_SHOT); + if (ret) + return ret; + + reinit_completion(&priv->completion); + + ret = ads1298_write_cmd(priv, ADS1298_CMD_START); + if (ret < 0) { + dev_err(&priv->spi->dev, "CMD_START error: %d\n", ret); + return ret; + } + + /* + * One conversion takes at most 4ms at the lowest sample rate (250Hz). + * Use 50ms to allow for kernel scheduling latency. + */ + if (!wait_for_completion_timeout(&priv->completion, msecs_to_jiffies(50))) + return -ETIMEDOUT; + + return 0; +} + +static int ads1298_get_samp_freq(struct ads1298_private *priv, int *val) +{ + unsigned long rate; + unsigned int cfg; + int ret; + + ret = regmap_read(priv->regmap, ADS1298_REG_CONFIG1, &cfg); + if (ret) + return ret; + + if (priv->clk) + rate = clk_get_rate(priv->clk); + else + rate = ADS1298_CLK_RATE_HZ; + if (!rate) + return -EINVAL; + + /* Data rate shift depends on HR/LP mode */ + if (cfg & ADS1298_MASK_CONFIG1_HR) + rate >>= ADS1298_SHIFT_DR_HR; + else + rate >>= ADS1298_SHIFT_DR_LP; + + *val = rate >> (cfg & ADS1298_MASK_CONFIG1_DR); + + return IIO_VAL_INT; +} + +static int ads1298_set_samp_freq(struct ads1298_private *priv, int val) +{ + unsigned long rate; + unsigned int factor; + unsigned int cfg; + + if (priv->clk) + rate = clk_get_rate(priv->clk); + else + rate = ADS1298_CLK_RATE_HZ; + if (!rate) + return -EINVAL; + if (val <= 0) + return -EINVAL; + + factor = (rate >> ADS1298_SHIFT_DR_HR) / val; + if (factor >= BIT(ADS1298_SHIFT_DR_LP)) + cfg = ADS1298_LOWEST_DR; + else if (factor) + cfg = ADS1298_MASK_CONFIG1_HR | ilog2(factor); /* Use HR mode */ + else + cfg = ADS1298_MASK_CONFIG1_HR; /* Fastest possible */ + + return regmap_update_bits(priv->regmap, ADS1298_REG_CONFIG1, + ADS1298_MASK_CONFIG1_HR | ADS1298_MASK_CONFIG1_DR, + cfg); +} + +static const u8 ads1298_pga_settings[] = { 6, 1, 2, 3, 4, 8, 12 }; + +static int ads1298_get_scale(struct ads1298_private *priv, + int channel, int *val, int *val2) +{ + unsigned int pga_idx; + int ret; + unsigned int regval; + u8 gain; + + if (priv->reg_vref) { + ret = regulator_get_voltage(priv->reg_vref); + if (ret < 0) + return ret; + + *val = ret / MILLI; /* Convert to millivolts */ + } else { + ret = regmap_read(priv->regmap, ADS1298_REG_CONFIG3, ®val); + if (ret) + return ret; + + /* Reference in millivolts */ + *val = regval & ADS1298_MASK_CONFIG3_VREF_4V ? 4000 : 2400; + } + + ret = regmap_read(priv->regmap, ADS1298_REG_CHnSET(channel), ®val); + if (ret) + return ret; + + pga_idx = FIELD_GET(ADS1298_MASK_CH_PGA, regval); + if (pga_idx >= ARRAY_SIZE(ads1298_pga_settings)) + return -EINVAL; + + gain = ads1298_pga_settings[pga_idx]; + *val /= gain; /* Full scale is VREF / gain */ + + *val2 = ADS1298_BITS_PER_SAMPLE - 1; /* Signed, hence the -1 */ + + return IIO_VAL_FRACTIONAL_LOG2; +} + +static int ads1298_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct ads1298_private *priv = iio_priv(indio_dev); + int ret; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; + + ret = ads1298_read_one(priv, chan->scan_index); + + iio_device_release_direct(indio_dev); + + if (ret) + return ret; + + *val = sign_extend32(get_unaligned_be24(priv->rx_buffer + chan->address), + ADS1298_BITS_PER_SAMPLE - 1); + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + return ads1298_get_scale(priv, chan->channel, val, val2); + case IIO_CHAN_INFO_SAMP_FREQ: + return ads1298_get_samp_freq(priv, val); + case IIO_CHAN_INFO_OVERSAMPLING_RATIO: + ret = regmap_read(priv->regmap, ADS1298_REG_CONFIG1, val); + if (ret) + return ret; + + *val = 16 << (*val & ADS1298_MASK_CONFIG1_DR); + return IIO_VAL_INT; + default: + return -EINVAL; + } +} + +static int ads1298_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int val, + int val2, long mask) +{ + struct ads1298_private *priv = iio_priv(indio_dev); + + switch (mask) { + case IIO_CHAN_INFO_SAMP_FREQ: + return ads1298_set_samp_freq(priv, val); + default: + return -EINVAL; + } +} + +static int ads1298_reg_write(void *context, unsigned int reg, unsigned int val) +{ + struct ads1298_private *priv = context; + struct spi_transfer reg_write_xfer = { + .tx_buf = priv->cmd_buffer, + .rx_buf = priv->cmd_buffer, + .len = 3, + .speed_hz = ADS1298_SPI_BUS_SPEED_SLOW, + .delay = { + .value = 2, + .unit = SPI_DELAY_UNIT_USECS, + }, + }; + + priv->cmd_buffer[0] = ADS1298_CMD_WREG | reg; + priv->cmd_buffer[1] = 0; /* Number of registers to be written - 1 */ + priv->cmd_buffer[2] = val; + + return spi_sync_transfer(priv->spi, ®_write_xfer, 1); +} + +static int ads1298_reg_read(void *context, unsigned int reg, unsigned int *val) +{ + struct ads1298_private *priv = context; + struct spi_transfer reg_read_xfer = { + .tx_buf = priv->cmd_buffer, + .rx_buf = priv->cmd_buffer, + .len = 3, + .speed_hz = ADS1298_SPI_BUS_SPEED_SLOW, + .delay = { + .value = 2, + .unit = SPI_DELAY_UNIT_USECS, + }, + }; + int ret; + + priv->cmd_buffer[0] = ADS1298_CMD_RREG | reg; + priv->cmd_buffer[1] = 0; /* Number of registers to be read - 1 */ + priv->cmd_buffer[2] = 0; + + ret = spi_sync_transfer(priv->spi, ®_read_xfer, 1); + if (ret) + return ret; + + *val = priv->cmd_buffer[2]; + + return 0; +} + +static int ads1298_reg_access(struct iio_dev *indio_dev, unsigned int reg, + unsigned int writeval, unsigned int *readval) +{ + struct ads1298_private *priv = iio_priv(indio_dev); + + if (readval) + return regmap_read(priv->regmap, reg, readval); + + return regmap_write(priv->regmap, reg, writeval); +} + +static void ads1298_rdata_unmark_busy(struct ads1298_private *priv) +{ + /* Notify we're no longer waiting for the SPI transfer to complete */ + guard(spinlock_irqsave)(&priv->irq_busy_lock); + priv->rdata_xfer_busy = 0; +} + +static int ads1298_update_scan_mode(struct iio_dev *indio_dev, + const unsigned long *scan_mask) +{ + struct ads1298_private *priv = iio_priv(indio_dev); + unsigned int val; + int ret; + int i; + + /* Make the interrupt routines start with a clean slate */ + ads1298_rdata_unmark_busy(priv); + + /* Configure power-down bits to match scan mask */ + for (i = 0; i < indio_dev->num_channels; i++) { + val = test_bit(i, scan_mask) ? 0 : ADS1298_MASK_CH_PD; + ret = regmap_update_bits(priv->regmap, ADS1298_REG_CHnSET(i), + ADS1298_MASK_CH_PD, val); + if (ret) + return ret; + } + + return 0; +} + +static const struct iio_info ads1298_info = { + .read_raw = &ads1298_read_raw, + .write_raw = &ads1298_write_raw, + .update_scan_mode = &ads1298_update_scan_mode, + .debugfs_reg_access = &ads1298_reg_access, +}; + +static void ads1298_rdata_release_busy_or_restart(struct ads1298_private *priv) +{ + guard(spinlock_irqsave)(&priv->irq_busy_lock); + + if (priv->rdata_xfer_busy > 1) { + /* + * DRDY interrupt occurred before SPI completion. Start a new + * SPI transaction now to retrieve the data that wasn't latched + * into the ADS1298 chip's transfer buffer yet. + */ + spi_async(priv->spi, &priv->rdata_msg); + /* + * If more than one DRDY took place, there was an overrun. Since + * the sample is already lost, reset the counter to 1 so that + * we will wait for a DRDY interrupt after this SPI transaction. + */ + priv->rdata_xfer_busy = 1; + } else { + /* No pending data, wait for DRDY */ + priv->rdata_xfer_busy = 0; + } +} + +/* Called from SPI completion interrupt handler */ +static void ads1298_rdata_complete(void *context) +{ + struct iio_dev *indio_dev = context; + struct ads1298_private *priv = iio_priv(indio_dev); + int scan_index; + u32 *bounce = priv->bounce_buffer; + + if (!iio_buffer_enabled(indio_dev)) { + /* + * for a single transfer mode we're kept in direct_mode until + * completion, avoiding a race with buffered IO. + */ + ads1298_rdata_unmark_busy(priv); + complete(&priv->completion); + return; + } + + /* Demux the channel data into our bounce buffer */ + iio_for_each_active_channel(indio_dev, scan_index) { + const struct iio_chan_spec *scan_chan = + &indio_dev->channels[scan_index]; + const u8 *data = priv->rx_buffer + scan_chan->address; + + *bounce++ = get_unaligned_be24(data); + } + + /* rx_buffer can be overwritten from this point on */ + ads1298_rdata_release_busy_or_restart(priv); + + iio_push_to_buffers(indio_dev, priv->bounce_buffer); +} + +static irqreturn_t ads1298_interrupt(int irq, void *dev_id) +{ + struct iio_dev *indio_dev = dev_id; + struct ads1298_private *priv = iio_priv(indio_dev); + unsigned int wasbusy; + + guard(spinlock_irqsave)(&priv->irq_busy_lock); + + wasbusy = priv->rdata_xfer_busy++; + /* When no SPI transfer in transit, start one now */ + if (!wasbusy) + spi_async(priv->spi, &priv->rdata_msg); + + return IRQ_HANDLED; +}; + +static int ads1298_buffer_postenable(struct iio_dev *indio_dev) +{ + struct ads1298_private *priv = iio_priv(indio_dev); + int ret; + + /* Disable single-shot mode */ + ret = regmap_update_bits(priv->regmap, ADS1298_REG_CONFIG4, + ADS1298_MASK_CONFIG4_SINGLE_SHOT, 0); + if (ret) + return ret; + + return ads1298_write_cmd(priv, ADS1298_CMD_START); +} + +static int ads1298_buffer_predisable(struct iio_dev *indio_dev) +{ + struct ads1298_private *priv = iio_priv(indio_dev); + + return ads1298_write_cmd(priv, ADS1298_CMD_STOP); +} + +static const struct iio_buffer_setup_ops ads1298_setup_ops = { + .postenable = &ads1298_buffer_postenable, + .predisable = &ads1298_buffer_predisable, +}; + +static void ads1298_reg_disable(void *reg) +{ + regulator_disable(reg); +} + +static const struct regmap_range ads1298_regmap_volatile_range[] = { + regmap_reg_range(ADS1298_REG_LOFF_STATP, ADS1298_REG_LOFF_STATN), +}; + +static const struct regmap_access_table ads1298_regmap_volatile = { + .yes_ranges = ads1298_regmap_volatile_range, + .n_yes_ranges = ARRAY_SIZE(ads1298_regmap_volatile_range), +}; + +static const struct regmap_config ads1298_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .reg_read = ads1298_reg_read, + .reg_write = ads1298_reg_write, + .max_register = ADS1298_REG_WCT2, + .volatile_table = &ads1298_regmap_volatile, + .cache_type = REGCACHE_MAPLE, +}; + +static int ads1298_init(struct iio_dev *indio_dev) +{ + struct ads1298_private *priv = iio_priv(indio_dev); + struct device *dev = &priv->spi->dev; + const char *suffix; + unsigned int val; + int ret; + + /* Device initializes into RDATAC mode, which we don't want */ + ret = ads1298_write_cmd(priv, ADS1298_CMD_SDATAC); + if (ret) + return ret; + + ret = regmap_read(priv->regmap, ADS1298_REG_ID, &val); + if (ret) + return ret; + + /* Fill in name and channel count based on what the chip told us */ + indio_dev->num_channels = 4 + 2 * (val & ADS1298_MASK_ID_CHANNELS); + switch (val & ADS1298_MASK_ID_FAMILY) { + case ADS1298_ID_FAMILY_ADS129X: + suffix = ""; + break; + case ADS1298_ID_FAMILY_ADS129XR: + suffix = "r"; + break; + default: + return dev_err_probe(dev, -ENODEV, "Unknown ID: 0x%x\n", val); + } + indio_dev->name = devm_kasprintf(dev, GFP_KERNEL, "ads129%u%s", + indio_dev->num_channels, suffix); + if (!indio_dev->name) + return -ENOMEM; + + val = ADS1298_MASK_CONFIG3_RESERVED; /* Must write 1 always */ + if (!priv->reg_vref) { + /* Enable internal reference */ + val |= ADS1298_MASK_CONFIG3_PWR_REFBUF; + /* Use 4V VREF when power supply is at least 4.4V */ + if (regulator_get_voltage(priv->reg_avdd) >= 4400000) + val |= ADS1298_MASK_CONFIG3_VREF_4V; + } + return regmap_write(priv->regmap, ADS1298_REG_CONFIG3, val); +} + +static int ads1298_probe(struct spi_device *spi) +{ + struct ads1298_private *priv; + struct iio_dev *indio_dev; + struct device *dev = &spi->dev; + struct gpio_desc *reset_gpio; + int ret; + + indio_dev = devm_iio_device_alloc(dev, sizeof(*priv)); + if (!indio_dev) + return -ENOMEM; + + priv = iio_priv(indio_dev); + + /* Reset to be asserted before enabling clock and power */ + reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH); + if (IS_ERR(reset_gpio)) + return dev_err_probe(dev, PTR_ERR(reset_gpio), + "Cannot get reset GPIO\n"); + + /* VREF can be supplied externally, otherwise use internal reference */ + priv->reg_vref = devm_regulator_get_optional(dev, "vref"); + if (IS_ERR(priv->reg_vref)) { + if (PTR_ERR(priv->reg_vref) != -ENODEV) + return dev_err_probe(dev, PTR_ERR(priv->reg_vref), + "Failed to get vref regulator\n"); + + priv->reg_vref = NULL; + } else { + ret = regulator_enable(priv->reg_vref); + if (ret) + return ret; + + ret = devm_add_action_or_reset(dev, ads1298_reg_disable, priv->reg_vref); + if (ret) + return ret; + } + + priv->clk = devm_clk_get_optional_enabled(dev, "clk"); + if (IS_ERR(priv->clk)) + return dev_err_probe(dev, PTR_ERR(priv->clk), "Failed to get clk\n"); + + priv->reg_avdd = devm_regulator_get(dev, "avdd"); + if (IS_ERR(priv->reg_avdd)) + return dev_err_probe(dev, PTR_ERR(priv->reg_avdd), + "Failed to get avdd regulator\n"); + + ret = regulator_enable(priv->reg_avdd); + if (ret) + return dev_err_probe(dev, ret, "Failed to enable avdd regulator\n"); + + ret = devm_add_action_or_reset(dev, ads1298_reg_disable, priv->reg_avdd); + if (ret) + return ret; + + priv->spi = spi; + init_completion(&priv->completion); + spin_lock_init(&priv->irq_busy_lock); + priv->regmap = devm_regmap_init(dev, NULL, priv, &ads1298_regmap_config); + if (IS_ERR(priv->regmap)) + return PTR_ERR(priv->regmap); + + indio_dev->modes = INDIO_DIRECT_MODE | INDIO_BUFFER_SOFTWARE; + indio_dev->channels = ads1298_channels; + indio_dev->info = &ads1298_info; + + if (reset_gpio) { + /* + * Deassert reset now that clock and power are active. + * Minimum reset pulsewidth is 2 clock cycles. + */ + fsleep(ADS1298_CLOCKS_TO_USECS(2)); + gpiod_set_value_cansleep(reset_gpio, 0); + } else { + ret = ads1298_write_cmd(priv, ADS1298_CMD_RESET); + if (ret) + return dev_err_probe(dev, ret, "RESET failed\n"); + } + /* Wait 18 clock cycles for reset command to complete */ + fsleep(ADS1298_CLOCKS_TO_USECS(18)); + + ret = ads1298_init(indio_dev); + if (ret) + return dev_err_probe(dev, ret, "Init failed\n"); + + priv->tx_buffer[0] = ADS1298_CMD_RDATA; + priv->rdata_xfer.tx_buf = priv->tx_buffer; + priv->rdata_xfer.rx_buf = priv->rx_buffer; + priv->rdata_xfer.len = ADS1298_SPI_RDATA_BUFFER_SIZE(indio_dev->num_channels); + /* Must keep CS low for 4 clocks */ + priv->rdata_xfer.delay.value = 2; + priv->rdata_xfer.delay.unit = SPI_DELAY_UNIT_USECS; + spi_message_init_with_transfers(&priv->rdata_msg, &priv->rdata_xfer, 1); + priv->rdata_msg.complete = &ads1298_rdata_complete; + priv->rdata_msg.context = indio_dev; + + ret = devm_request_irq(dev, spi->irq, &ads1298_interrupt, + IRQF_TRIGGER_FALLING, indio_dev->name, + indio_dev); + if (ret) + return ret; + + ret = devm_iio_kfifo_buffer_setup(dev, indio_dev, &ads1298_setup_ops); + if (ret) + return ret; + + return devm_iio_device_register(dev, indio_dev); +} + +static const struct spi_device_id ads1298_id[] = { + { .name = "ads1298" }, + { } +}; +MODULE_DEVICE_TABLE(spi, ads1298_id); + +static const struct of_device_id ads1298_of_table[] = { + { .compatible = "ti,ads1298" }, + { } +}; +MODULE_DEVICE_TABLE(of, ads1298_of_table); + +static struct spi_driver ads1298_driver = { + .driver = { + .name = "ads1298", + .of_match_table = ads1298_of_table, + }, + .probe = ads1298_probe, + .id_table = ads1298_id, +}; +module_spi_driver(ads1298_driver); + +MODULE_AUTHOR("Mike Looijmans "); +MODULE_DESCRIPTION("TI ADS1298 ADC"); +MODULE_LICENSE("GPL"); diff --git a/drivers/iio/adc/ti-ads131e08.c b/drivers/iio/adc/ti-ads131e08.c new file mode 100644 index 000000000..1d25f887c --- /dev/null +++ b/drivers/iio/adc/ti-ads131e08.c @@ -0,0 +1,939 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Texas Instruments ADS131E0x 4-, 6- and 8-Channel ADCs + * + * Copyright (c) 2020 AVL DiTEST GmbH + * Tomislav Denis + * + * Datasheet: https://www.ti.com/lit/ds/symlink/ads131e08.pdf + */ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +/* Commands */ +#define ADS131E08_CMD_RESET 0x06 +#define ADS131E08_CMD_START 0x08 +#define ADS131E08_CMD_STOP 0x0A +#define ADS131E08_CMD_OFFSETCAL 0x1A +#define ADS131E08_CMD_SDATAC 0x11 +#define ADS131E08_CMD_RDATA 0x12 +#define ADS131E08_CMD_RREG(r) (BIT(5) | (r & GENMASK(4, 0))) +#define ADS131E08_CMD_WREG(r) (BIT(6) | (r & GENMASK(4, 0))) + +/* Registers */ +#define ADS131E08_ADR_CFG1R 0x01 +#define ADS131E08_ADR_CFG3R 0x03 +#define ADS131E08_ADR_CH0R 0x05 + +/* Configuration register 1 */ +#define ADS131E08_CFG1R_DR_MASK GENMASK(2, 0) + +/* Configuration register 3 */ +#define ADS131E08_CFG3R_PDB_REFBUF_MASK BIT(7) +#define ADS131E08_CFG3R_VREF_4V_MASK BIT(5) + +/* Channel settings register */ +#define ADS131E08_CHR_GAIN_MASK GENMASK(6, 4) +#define ADS131E08_CHR_MUX_MASK GENMASK(2, 0) +#define ADS131E08_CHR_PWD_MASK BIT(7) + +/* ADC misc */ +#define ADS131E08_DEFAULT_DATA_RATE 1 +#define ADS131E08_DEFAULT_PGA_GAIN 1 +#define ADS131E08_DEFAULT_MUX 0 + +#define ADS131E08_VREF_2V4_mV 2400 +#define ADS131E08_VREF_4V_mV 4000 + +#define ADS131E08_WAIT_RESET_CYCLES 18 +#define ADS131E08_WAIT_SDECODE_CYCLES 4 +#define ADS131E08_WAIT_OFFSETCAL_MS 153 +#define ADS131E08_MAX_SETTLING_TIME_MS 6 + +#define ADS131E08_NUM_STATUS_BYTES 3 +#define ADS131E08_NUM_DATA_BYTES_MAX 24 +#define ADS131E08_NUM_DATA_BYTES(dr) (((dr) >= 32) ? 2 : 3) +#define ADS131E08_NUM_DATA_BITS(dr) (ADS131E08_NUM_DATA_BYTES(dr) * 8) +#define ADS131E08_NUM_STORAGE_BYTES 4 + +enum ads131e08_ids { + ads131e04, + ads131e06, + ads131e08, +}; + +struct ads131e08_info { + unsigned int max_channels; + const char *name; +}; + +struct ads131e08_channel_config { + unsigned int pga_gain; + unsigned int mux; +}; + +struct ads131e08_state { + const struct ads131e08_info *info; + struct spi_device *spi; + struct iio_trigger *trig; + struct clk *adc_clk; + struct regulator *vref_reg; + struct ads131e08_channel_config *channel_config; + unsigned int data_rate; + unsigned int vref_mv; + unsigned int sdecode_delay_us; + unsigned int reset_delay_us; + unsigned int readback_len; + struct completion completion; + struct { + u8 data[ADS131E08_NUM_DATA_BYTES_MAX]; + aligned_s64 ts; + } tmp_buf; + + u8 tx_buf[3] __aligned(IIO_DMA_MINALIGN); + /* + * Add extra one padding byte to be able to access the last channel + * value using u32 pointer + */ + u8 rx_buf[ADS131E08_NUM_STATUS_BYTES + + ADS131E08_NUM_DATA_BYTES_MAX + 1]; +}; + +static const struct ads131e08_info ads131e08_info_tbl[] = { + [ads131e04] = { + .max_channels = 4, + .name = "ads131e04", + }, + [ads131e06] = { + .max_channels = 6, + .name = "ads131e06", + }, + [ads131e08] = { + .max_channels = 8, + .name = "ads131e08", + }, +}; + +struct ads131e08_data_rate_desc { + unsigned int rate; /* data rate in kSPS */ + u8 reg; /* reg value */ +}; + +static const struct ads131e08_data_rate_desc ads131e08_data_rate_tbl[] = { + { .rate = 64, .reg = 0x00 }, + { .rate = 32, .reg = 0x01 }, + { .rate = 16, .reg = 0x02 }, + { .rate = 8, .reg = 0x03 }, + { .rate = 4, .reg = 0x04 }, + { .rate = 2, .reg = 0x05 }, + { .rate = 1, .reg = 0x06 }, +}; + +struct ads131e08_pga_gain_desc { + unsigned int gain; /* PGA gain value */ + u8 reg; /* field value */ +}; + +static const struct ads131e08_pga_gain_desc ads131e08_pga_gain_tbl[] = { + { .gain = 1, .reg = 0x01 }, + { .gain = 2, .reg = 0x02 }, + { .gain = 4, .reg = 0x04 }, + { .gain = 8, .reg = 0x05 }, + { .gain = 12, .reg = 0x06 }, +}; + +static const u8 ads131e08_valid_channel_mux_values[] = { 0, 1, 3, 4 }; + +static int ads131e08_exec_cmd(struct ads131e08_state *st, u8 cmd) +{ + int ret; + + ret = spi_write_then_read(st->spi, &cmd, 1, NULL, 0); + if (ret) + dev_err(&st->spi->dev, "Exec cmd(%02x) failed\n", cmd); + + return ret; +} + +static int ads131e08_read_reg(struct ads131e08_state *st, u8 reg) +{ + int ret; + struct spi_transfer transfer[] = { + { + .tx_buf = &st->tx_buf, + .len = 2, + .delay = { + .value = st->sdecode_delay_us, + .unit = SPI_DELAY_UNIT_USECS, + }, + }, { + .rx_buf = &st->rx_buf, + .len = 1, + }, + }; + + st->tx_buf[0] = ADS131E08_CMD_RREG(reg); + st->tx_buf[1] = 0; + + ret = spi_sync_transfer(st->spi, transfer, ARRAY_SIZE(transfer)); + if (ret) { + dev_err(&st->spi->dev, "Read register failed\n"); + return ret; + } + + return st->rx_buf[0]; +} + +static int ads131e08_write_reg(struct ads131e08_state *st, u8 reg, u8 value) +{ + int ret; + struct spi_transfer transfer[] = { + { + .tx_buf = &st->tx_buf, + .len = 3, + .delay = { + .value = st->sdecode_delay_us, + .unit = SPI_DELAY_UNIT_USECS, + }, + } + }; + + st->tx_buf[0] = ADS131E08_CMD_WREG(reg); + st->tx_buf[1] = 0; + st->tx_buf[2] = value; + + ret = spi_sync_transfer(st->spi, transfer, ARRAY_SIZE(transfer)); + if (ret) + dev_err(&st->spi->dev, "Write register failed\n"); + + return ret; +} + +static int ads131e08_read_data(struct ads131e08_state *st, int rx_len) +{ + int ret; + struct spi_transfer transfer[] = { + { + .tx_buf = &st->tx_buf, + .len = 1, + }, { + .rx_buf = &st->rx_buf, + .len = rx_len, + }, + }; + + st->tx_buf[0] = ADS131E08_CMD_RDATA; + + ret = spi_sync_transfer(st->spi, transfer, ARRAY_SIZE(transfer)); + if (ret) + dev_err(&st->spi->dev, "Read data failed\n"); + + return ret; +} + +static int ads131e08_set_data_rate(struct ads131e08_state *st, int data_rate) +{ + int i, reg, ret; + + for (i = 0; i < ARRAY_SIZE(ads131e08_data_rate_tbl); i++) { + if (ads131e08_data_rate_tbl[i].rate == data_rate) + break; + } + + if (i == ARRAY_SIZE(ads131e08_data_rate_tbl)) { + dev_err(&st->spi->dev, "invalid data rate value\n"); + return -EINVAL; + } + + reg = ads131e08_read_reg(st, ADS131E08_ADR_CFG1R); + if (reg < 0) + return reg; + + reg &= ~ADS131E08_CFG1R_DR_MASK; + reg |= FIELD_PREP(ADS131E08_CFG1R_DR_MASK, + ads131e08_data_rate_tbl[i].reg); + + ret = ads131e08_write_reg(st, ADS131E08_ADR_CFG1R, reg); + if (ret) + return ret; + + st->data_rate = data_rate; + st->readback_len = ADS131E08_NUM_STATUS_BYTES + + ADS131E08_NUM_DATA_BYTES(st->data_rate) * + st->info->max_channels; + + return 0; +} + +static int ads131e08_pga_gain_to_field_value(struct ads131e08_state *st, + unsigned int pga_gain) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(ads131e08_pga_gain_tbl); i++) { + if (ads131e08_pga_gain_tbl[i].gain == pga_gain) + break; + } + + if (i == ARRAY_SIZE(ads131e08_pga_gain_tbl)) { + dev_err(&st->spi->dev, "invalid PGA gain value\n"); + return -EINVAL; + } + + return ads131e08_pga_gain_tbl[i].reg; +} + +static int ads131e08_set_pga_gain(struct ads131e08_state *st, + unsigned int channel, unsigned int pga_gain) +{ + int field_value, reg; + + field_value = ads131e08_pga_gain_to_field_value(st, pga_gain); + if (field_value < 0) + return field_value; + + reg = ads131e08_read_reg(st, ADS131E08_ADR_CH0R + channel); + if (reg < 0) + return reg; + + reg &= ~ADS131E08_CHR_GAIN_MASK; + reg |= FIELD_PREP(ADS131E08_CHR_GAIN_MASK, field_value); + + return ads131e08_write_reg(st, ADS131E08_ADR_CH0R + channel, reg); +} + +static int ads131e08_validate_channel_mux(struct ads131e08_state *st, + unsigned int mux) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(ads131e08_valid_channel_mux_values); i++) { + if (ads131e08_valid_channel_mux_values[i] == mux) + break; + } + + if (i == ARRAY_SIZE(ads131e08_valid_channel_mux_values)) { + dev_err(&st->spi->dev, "invalid channel mux value\n"); + return -EINVAL; + } + + return 0; +} + +static int ads131e08_set_channel_mux(struct ads131e08_state *st, + unsigned int channel, unsigned int mux) +{ + int reg; + + reg = ads131e08_read_reg(st, ADS131E08_ADR_CH0R + channel); + if (reg < 0) + return reg; + + reg &= ~ADS131E08_CHR_MUX_MASK; + reg |= FIELD_PREP(ADS131E08_CHR_MUX_MASK, mux); + + return ads131e08_write_reg(st, ADS131E08_ADR_CH0R + channel, reg); +} + +static int ads131e08_power_down_channel(struct ads131e08_state *st, + unsigned int channel, bool value) +{ + int reg; + + reg = ads131e08_read_reg(st, ADS131E08_ADR_CH0R + channel); + if (reg < 0) + return reg; + + reg &= ~ADS131E08_CHR_PWD_MASK; + reg |= FIELD_PREP(ADS131E08_CHR_PWD_MASK, value); + + return ads131e08_write_reg(st, ADS131E08_ADR_CH0R + channel, reg); +} + +static int ads131e08_config_reference_voltage(struct ads131e08_state *st) +{ + int reg; + + reg = ads131e08_read_reg(st, ADS131E08_ADR_CFG3R); + if (reg < 0) + return reg; + + reg &= ~ADS131E08_CFG3R_PDB_REFBUF_MASK; + if (!st->vref_reg) { + reg |= FIELD_PREP(ADS131E08_CFG3R_PDB_REFBUF_MASK, 1); + reg &= ~ADS131E08_CFG3R_VREF_4V_MASK; + reg |= FIELD_PREP(ADS131E08_CFG3R_VREF_4V_MASK, + st->vref_mv == ADS131E08_VREF_4V_mV); + } + + return ads131e08_write_reg(st, ADS131E08_ADR_CFG3R, reg); +} + +static int ads131e08_initial_config(struct iio_dev *indio_dev) +{ + const struct iio_chan_spec *channel = indio_dev->channels; + struct ads131e08_state *st = iio_priv(indio_dev); + unsigned long active_channels = 0; + int ret, i; + + ret = ads131e08_exec_cmd(st, ADS131E08_CMD_RESET); + if (ret) + return ret; + + udelay(st->reset_delay_us); + + /* Disable read data in continuous mode (enabled by default) */ + ret = ads131e08_exec_cmd(st, ADS131E08_CMD_SDATAC); + if (ret) + return ret; + + ret = ads131e08_set_data_rate(st, ADS131E08_DEFAULT_DATA_RATE); + if (ret) + return ret; + + ret = ads131e08_config_reference_voltage(st); + if (ret) + return ret; + + for (i = 0; i < indio_dev->num_channels; i++) { + ret = ads131e08_set_pga_gain(st, channel->channel, + st->channel_config[i].pga_gain); + if (ret) + return ret; + + ret = ads131e08_set_channel_mux(st, channel->channel, + st->channel_config[i].mux); + if (ret) + return ret; + + active_channels |= BIT(channel->channel); + channel++; + } + + /* Power down unused channels */ + for_each_clear_bit(i, &active_channels, st->info->max_channels) { + ret = ads131e08_power_down_channel(st, i, true); + if (ret) + return ret; + } + + /* Request channel offset calibration */ + ret = ads131e08_exec_cmd(st, ADS131E08_CMD_OFFSETCAL); + if (ret) + return ret; + + /* + * Channel offset calibration is triggered with the first START + * command. Since calibration takes more time than settling operation, + * this causes timeout error when command START is sent first + * time (e.g. first call of the ads131e08_read_direct method). + * To avoid this problem offset calibration is triggered here. + */ + ret = ads131e08_exec_cmd(st, ADS131E08_CMD_START); + if (ret) + return ret; + + msleep(ADS131E08_WAIT_OFFSETCAL_MS); + + return ads131e08_exec_cmd(st, ADS131E08_CMD_STOP); +} + +static int ads131e08_pool_data(struct ads131e08_state *st) +{ + unsigned long timeout; + int ret; + + reinit_completion(&st->completion); + + ret = ads131e08_exec_cmd(st, ADS131E08_CMD_START); + if (ret) + return ret; + + timeout = msecs_to_jiffies(ADS131E08_MAX_SETTLING_TIME_MS); + ret = wait_for_completion_timeout(&st->completion, timeout); + if (!ret) + return -ETIMEDOUT; + + ret = ads131e08_read_data(st, st->readback_len); + if (ret) + return ret; + + return ads131e08_exec_cmd(st, ADS131E08_CMD_STOP); +} + +static int ads131e08_read_direct(struct iio_dev *indio_dev, + struct iio_chan_spec const *channel, int *value) +{ + struct ads131e08_state *st = iio_priv(indio_dev); + u8 num_bits, *src; + int ret; + + ret = ads131e08_pool_data(st); + if (ret) + return ret; + + src = st->rx_buf + ADS131E08_NUM_STATUS_BYTES + + channel->channel * ADS131E08_NUM_DATA_BYTES(st->data_rate); + + num_bits = ADS131E08_NUM_DATA_BITS(st->data_rate); + *value = sign_extend32(get_unaligned_be32(src) >> (32 - num_bits), num_bits - 1); + + return 0; +} + +static int ads131e08_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *channel, int *value, + int *value2, long mask) +{ + struct ads131e08_state *st = iio_priv(indio_dev); + int ret; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; + + ret = ads131e08_read_direct(indio_dev, channel, value); + iio_device_release_direct(indio_dev); + if (ret) + return ret; + + return IIO_VAL_INT; + + case IIO_CHAN_INFO_SCALE: + if (st->vref_reg) { + ret = regulator_get_voltage(st->vref_reg); + if (ret < 0) + return ret; + + *value = ret / 1000; + } else { + *value = st->vref_mv; + } + + *value /= st->channel_config[channel->address].pga_gain; + *value2 = ADS131E08_NUM_DATA_BITS(st->data_rate) - 1; + + return IIO_VAL_FRACTIONAL_LOG2; + + case IIO_CHAN_INFO_SAMP_FREQ: + *value = st->data_rate; + + return IIO_VAL_INT; + + default: + return -EINVAL; + } +} + +static int ads131e08_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *channel, int value, + int value2, long mask) +{ + struct ads131e08_state *st = iio_priv(indio_dev); + int ret; + + switch (mask) { + case IIO_CHAN_INFO_SAMP_FREQ: + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; + + ret = ads131e08_set_data_rate(st, value); + iio_device_release_direct(indio_dev); + return ret; + + default: + return -EINVAL; + } +} + +static IIO_CONST_ATTR_SAMP_FREQ_AVAIL("1 2 4 8 16 32 64"); + +static struct attribute *ads131e08_attributes[] = { + &iio_const_attr_sampling_frequency_available.dev_attr.attr, + NULL +}; + +static const struct attribute_group ads131e08_attribute_group = { + .attrs = ads131e08_attributes, +}; + +static int ads131e08_debugfs_reg_access(struct iio_dev *indio_dev, + unsigned int reg, unsigned int writeval, unsigned int *readval) +{ + struct ads131e08_state *st = iio_priv(indio_dev); + + if (readval) { + int ret = ads131e08_read_reg(st, reg); + *readval = ret; + return ret; + } + + return ads131e08_write_reg(st, reg, writeval); +} + +static const struct iio_info ads131e08_iio_info = { + .read_raw = ads131e08_read_raw, + .write_raw = ads131e08_write_raw, + .attrs = &ads131e08_attribute_group, + .debugfs_reg_access = &ads131e08_debugfs_reg_access, +}; + +static int ads131e08_set_trigger_state(struct iio_trigger *trig, bool state) +{ + struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig); + struct ads131e08_state *st = iio_priv(indio_dev); + u8 cmd = state ? ADS131E08_CMD_START : ADS131E08_CMD_STOP; + + return ads131e08_exec_cmd(st, cmd); +} + +static const struct iio_trigger_ops ads131e08_trigger_ops = { + .set_trigger_state = &ads131e08_set_trigger_state, + .validate_device = &iio_trigger_validate_own_device, +}; + +static irqreturn_t ads131e08_trigger_handler(int irq, void *private) +{ + struct iio_poll_func *pf = private; + struct iio_dev *indio_dev = pf->indio_dev; + struct ads131e08_state *st = iio_priv(indio_dev); + unsigned int chn, i = 0; + u8 *src, *dest; + int ret; + + /* + * The number of data bits per channel depends on the data rate. + * For 32 and 64 ksps data rates, number of data bits per channel + * is 16. This case is not compliant with used (fixed) scan element + * type (be:s24/32>>8). So we use a little tweak to pack properly + * 16 bits of data into the buffer. + */ + unsigned int num_bytes = ADS131E08_NUM_DATA_BYTES(st->data_rate); + u8 tweak_offset = num_bytes == 2 ? 1 : 0; + + if (iio_trigger_using_own(indio_dev)) + ret = ads131e08_read_data(st, st->readback_len); + else + ret = ads131e08_pool_data(st); + + if (ret) + goto out; + + iio_for_each_active_channel(indio_dev, chn) { + src = st->rx_buf + ADS131E08_NUM_STATUS_BYTES + chn * num_bytes; + dest = st->tmp_buf.data + i * ADS131E08_NUM_STORAGE_BYTES; + + /* + * Tweak offset is 0: + * +---+---+---+---+ + * |D0 |D1 |D2 | X | (3 data bytes) + * +---+---+---+---+ + * a+0 a+1 a+2 a+3 + * + * Tweak offset is 1: + * +---+---+---+---+ + * |P0 |D0 |D1 | X | (one padding byte and 2 data bytes) + * +---+---+---+---+ + * a+0 a+1 a+2 a+3 + */ + memcpy(dest + tweak_offset, src, num_bytes); + + /* + * Data conversion from 16 bits of data to 24 bits of data + * is done by sign extension (properly filling padding byte). + */ + if (tweak_offset) + *dest = *src & BIT(7) ? 0xff : 0x00; + + i++; + } + + iio_push_to_buffers_with_ts(indio_dev, &st->tmp_buf, sizeof(st->tmp_buf), + iio_get_time_ns(indio_dev)); + +out: + iio_trigger_notify_done(indio_dev->trig); + + return IRQ_HANDLED; +} + +static irqreturn_t ads131e08_interrupt(int irq, void *private) +{ + struct iio_dev *indio_dev = private; + struct ads131e08_state *st = iio_priv(indio_dev); + + if (iio_buffer_enabled(indio_dev) && iio_trigger_using_own(indio_dev)) + iio_trigger_poll(st->trig); + else + complete(&st->completion); + + return IRQ_HANDLED; +} + +static int ads131e08_alloc_channels(struct iio_dev *indio_dev) +{ + struct ads131e08_state *st = iio_priv(indio_dev); + struct ads131e08_channel_config *channel_config; + struct device *dev = &st->spi->dev; + struct iio_chan_spec *channels; + unsigned int channel, tmp; + int num_channels, i, ret; + + ret = device_property_read_u32(dev, "ti,vref-internal", &tmp); + if (ret) + tmp = 0; + + switch (tmp) { + case 0: + st->vref_mv = ADS131E08_VREF_2V4_mV; + break; + case 1: + st->vref_mv = ADS131E08_VREF_4V_mV; + break; + default: + dev_err(&st->spi->dev, "invalid internal voltage reference\n"); + return -EINVAL; + } + + num_channels = device_get_child_node_count(dev); + if (num_channels == 0) { + dev_err(&st->spi->dev, "no channel children\n"); + return -ENODEV; + } + + if (num_channels > st->info->max_channels) { + dev_err(&st->spi->dev, "num of channel children out of range\n"); + return -EINVAL; + } + + channels = devm_kcalloc(&st->spi->dev, num_channels, + sizeof(*channels), GFP_KERNEL); + if (!channels) + return -ENOMEM; + + channel_config = devm_kcalloc(&st->spi->dev, num_channels, + sizeof(*channel_config), GFP_KERNEL); + if (!channel_config) + return -ENOMEM; + + i = 0; + device_for_each_child_node_scoped(dev, node) { + ret = fwnode_property_read_u32(node, "reg", &channel); + if (ret) + return ret; + + ret = fwnode_property_read_u32(node, "ti,gain", &tmp); + if (ret) { + channel_config[i].pga_gain = ADS131E08_DEFAULT_PGA_GAIN; + } else { + ret = ads131e08_pga_gain_to_field_value(st, tmp); + if (ret < 0) + return ret; + + channel_config[i].pga_gain = tmp; + } + + ret = fwnode_property_read_u32(node, "ti,mux", &tmp); + if (ret) { + channel_config[i].mux = ADS131E08_DEFAULT_MUX; + } else { + ret = ads131e08_validate_channel_mux(st, tmp); + if (ret) + return ret; + + channel_config[i].mux = tmp; + } + + channels[i].type = IIO_VOLTAGE; + channels[i].indexed = 1; + channels[i].channel = channel; + channels[i].address = i; + channels[i].info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SCALE); + channels[i].info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SAMP_FREQ); + channels[i].scan_index = channel; + channels[i].scan_type.sign = 's'; + channels[i].scan_type.realbits = 24; + channels[i].scan_type.storagebits = 32; + channels[i].scan_type.shift = 8; + channels[i].scan_type.endianness = IIO_BE; + i++; + } + + indio_dev->channels = channels; + indio_dev->num_channels = num_channels; + st->channel_config = channel_config; + + return 0; + +} + +static void ads131e08_regulator_disable(void *data) +{ + struct ads131e08_state *st = data; + + regulator_disable(st->vref_reg); +} + +static int ads131e08_probe(struct spi_device *spi) +{ + const struct ads131e08_info *info; + struct ads131e08_state *st; + struct iio_dev *indio_dev; + unsigned long adc_clk_hz; + unsigned long adc_clk_ns; + int ret; + + info = spi_get_device_match_data(spi); + if (!info) { + dev_err(&spi->dev, "failed to get match data\n"); + return -ENODEV; + } + + indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st)); + if (!indio_dev) + return -ENOMEM; + + st = iio_priv(indio_dev); + st->info = info; + st->spi = spi; + + ret = ads131e08_alloc_channels(indio_dev); + if (ret) + return ret; + + indio_dev->name = st->info->name; + indio_dev->info = &ads131e08_iio_info; + indio_dev->modes = INDIO_DIRECT_MODE; + + init_completion(&st->completion); + + if (spi->irq) { + ret = devm_request_irq(&spi->dev, spi->irq, + ads131e08_interrupt, + IRQF_TRIGGER_FALLING | IRQF_NO_THREAD, + spi->dev.driver->name, indio_dev); + if (ret) + return dev_err_probe(&spi->dev, ret, + "request irq failed\n"); + } else { + dev_err(&spi->dev, "data ready IRQ missing\n"); + return -ENODEV; + } + + st->trig = devm_iio_trigger_alloc(&spi->dev, "%s-dev%d", + indio_dev->name, iio_device_id(indio_dev)); + if (!st->trig) + return -ENOMEM; + + st->trig->ops = &ads131e08_trigger_ops; + st->trig->dev.parent = &spi->dev; + iio_trigger_set_drvdata(st->trig, indio_dev); + ret = devm_iio_trigger_register(&spi->dev, st->trig); + if (ret) { + dev_err(&spi->dev, "failed to register IIO trigger\n"); + return ret; + } + + indio_dev->trig = iio_trigger_get(st->trig); + + ret = devm_iio_triggered_buffer_setup(&spi->dev, indio_dev, + NULL, &ads131e08_trigger_handler, NULL); + if (ret) { + dev_err(&spi->dev, "failed to setup IIO buffer\n"); + return ret; + } + + st->vref_reg = devm_regulator_get_optional(&spi->dev, "vref"); + if (!IS_ERR(st->vref_reg)) { + ret = regulator_enable(st->vref_reg); + if (ret) { + dev_err(&spi->dev, + "failed to enable external vref supply\n"); + return ret; + } + + ret = devm_add_action_or_reset(&spi->dev, ads131e08_regulator_disable, st); + if (ret) + return ret; + } else { + if (PTR_ERR(st->vref_reg) != -ENODEV) + return PTR_ERR(st->vref_reg); + + st->vref_reg = NULL; + } + + st->adc_clk = devm_clk_get_enabled(&spi->dev, "adc-clk"); + if (IS_ERR(st->adc_clk)) + return dev_err_probe(&spi->dev, PTR_ERR(st->adc_clk), + "failed to get the ADC clock\n"); + + adc_clk_hz = clk_get_rate(st->adc_clk); + if (!adc_clk_hz) { + dev_err(&spi->dev, "failed to get the ADC clock rate\n"); + return -EINVAL; + } + + adc_clk_ns = NSEC_PER_SEC / adc_clk_hz; + st->sdecode_delay_us = DIV_ROUND_UP( + ADS131E08_WAIT_SDECODE_CYCLES * adc_clk_ns, NSEC_PER_USEC); + st->reset_delay_us = DIV_ROUND_UP( + ADS131E08_WAIT_RESET_CYCLES * adc_clk_ns, NSEC_PER_USEC); + + ret = ads131e08_initial_config(indio_dev); + if (ret) { + dev_err(&spi->dev, "initial configuration failed\n"); + return ret; + } + + return devm_iio_device_register(&spi->dev, indio_dev); +} + +static const struct of_device_id ads131e08_of_match[] = { + { .compatible = "ti,ads131e04", + .data = &ads131e08_info_tbl[ads131e04], }, + { .compatible = "ti,ads131e06", + .data = &ads131e08_info_tbl[ads131e06], }, + { .compatible = "ti,ads131e08", + .data = &ads131e08_info_tbl[ads131e08], }, + { } +}; +MODULE_DEVICE_TABLE(of, ads131e08_of_match); + +static const struct spi_device_id ads131e08_ids[] = { + { .name = "ads131e04", .driver_data = (kernel_ulong_t)&ads131e08_info_tbl[ads131e04] }, + { .name = "ads131e06", .driver_data = (kernel_ulong_t)&ads131e08_info_tbl[ads131e06] }, + { .name = "ads131e08", .driver_data = (kernel_ulong_t)&ads131e08_info_tbl[ads131e08] }, + { } +}; +MODULE_DEVICE_TABLE(spi, ads131e08_ids); + +static struct spi_driver ads131e08_driver = { + .driver = { + .name = "ads131e08", + .of_match_table = ads131e08_of_match, + }, + .probe = ads131e08_probe, + .id_table = ads131e08_ids, +}; +module_spi_driver(ads131e08_driver); + +MODULE_AUTHOR("Tomislav Denis "); +MODULE_DESCRIPTION("Driver for ADS131E0x ADC family"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/adc/ti-ads131m02.c b/drivers/iio/adc/ti-ads131m02.c new file mode 100644 index 000000000..2f8f75c82 --- /dev/null +++ b/drivers/iio/adc/ti-ads131m02.c @@ -0,0 +1,967 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Driver for Texas Instruments ADS131M02 family ADC chips. + * + * Copyright (C) 2024 Protonic Holland + * Copyright (C) 2025 Oleksij Rempel , Pengutronix + * + * Primary Datasheet Reference (used for citations): + * ADS131M08 8-Channel, Simultaneously-Sampling, 24-Bit, Delta-Sigma ADC + * Document SBAS950B, Revised February 2021 + * https://www.ti.com/lit/ds/symlink/ads131m08.pdf + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Max channels supported by the largest variant in the family (ADS131M08) */ +#define ADS131M_MAX_CHANNELS 8 + +/* Section 6.7, t_REGACQ (min time after reset) is 5us */ +#define ADS131M_RESET_DELAY_US 5 + +#define ADS131M_WORD_SIZE_BYTES 3 +#define ADS131M_RESPONSE_WORDS 1 +#define ADS131M_CRC_WORDS 1 + +/* + * SPI Frame word count calculation. + * Frame = N channel words + 1 response word + 1 CRC word. + * Word size depends on WLENGTH bits in MODE register (Default 24-bit). + */ +#define ADS131M_FRAME_WORDS(nch) \ + ((nch) + ADS131M_RESPONSE_WORDS + ADS131M_CRC_WORDS) + +/* + * SPI Frame byte size calculation. + * Assumes default word size of 24 bits (3 bytes). + */ +#define ADS131M_FRAME_BYTES(nch) \ + (ADS131M_FRAME_WORDS(nch) * ADS131M_WORD_SIZE_BYTES) + +/* + * Index calculation for the start byte of channel 'x' data within the RX buffer. + * Assumes 24-bit words (3 bytes per word). + * The received frame starts with the response word (e.g., STATUS register + * content when NULL command was sent), followed by data for channels 0 to N-1, + * and finally the output CRC word. + * Response = index 0..2, Chan0 = index 3..5, Chan1 = index 6..8, ... + * Index for ChanX = 3 (response) + x * 3 (channel data size). + */ +#define ADS131M_CHANNEL_INDEX(x) \ + ((x) * ADS131M_WORD_SIZE_BYTES + ADS131M_WORD_SIZE_BYTES) + +#define ADS131M_CMD_NULL 0x0000 +#define ADS131M_CMD_RESET 0x0011 + +#define ADS131M_CMD_ADDR_MASK GENMASK(11, 7) +#define ADS131M_CMD_NUM_MASK GENMASK(6, 0) + +#define ADS131M_CMD_RREG_OP 0xa000 +#define ADS131M_CMD_WREG_OP 0x6000 + +#define ADS131M_CMD_RREG(a, n) \ + (ADS131M_CMD_RREG_OP | \ + FIELD_PREP(ADS131M_CMD_ADDR_MASK, a) | \ + FIELD_PREP(ADS131M_CMD_NUM_MASK, n)) +#define ADS131M_CMD_WREG(a, n) \ + (ADS131M_CMD_WREG_OP | \ + FIELD_PREP(ADS131M_CMD_ADDR_MASK, a) | \ + FIELD_PREP(ADS131M_CMD_NUM_MASK, n)) + +/* STATUS Register (0x01h) bit definitions */ +#define ADS131M_STATUS_CRC_ERR BIT(12) /* Input CRC error */ + +#define ADS131M_REG_MODE 0x02 +#define ADS131M_MODE_RX_CRC_EN BIT(12) /* Enable Input CRC */ +#define ADS131M_MODE_CRC_TYPE_ANSI BIT(11) /* 0 = CCITT, 1 = ANSI */ +#define ADS131M_MODE_RESET_FLAG BIT(10) + +#define ADS131M_REG_CLOCK 0x03 +#define ADS131M_CLOCK_XTAL_DIS BIT(7) +#define ADS131M_CLOCK_EXTREF_EN BIT(6) + +/* 1.2V internal reference, in millivolts, for IIO_VAL_FRACTIONAL_LOG2 */ +#define ADS131M_VREF_INTERNAL_mV 1200 +/* 24-bit resolution */ +#define ADS131M_RESOLUTION_BITS 24 +/* Signed data uses (RESOLUTION_BITS - 1) magnitude bits */ +#define ADS131M_CODE_BITS (ADS131M_RESOLUTION_BITS - 1) + +/* External ref FSR = Vref * 0.96 */ +#define ADS131M_EXTREF_SCALE_NUM 96 +#define ADS131M_EXTREF_SCALE_DEN 100 + +struct ads131m_configuration { + const struct iio_chan_spec *channels; + const char *name; + u16 reset_ack; + u8 num_channels; + u8 supports_extref:1; + u8 supports_xtal:1; +}; + +struct ads131m_priv { + struct iio_dev *indio_dev; + struct spi_device *spi; + const struct ads131m_configuration *config; + + bool use_external_ref; + int scale_val; + int scale_val2; + + struct spi_transfer xfer; + struct spi_message msg; + + /* + * Protects the shared tx_buffer and rx_buffer. More importantly, + * this serializes all SPI communication to ensure the atomicity + * of multi-cycle command sequences (like WREG, RREG, or RESET). + */ + struct mutex lock; + + /* DMA-safe buffers should be placed at the end of the struct. */ + u8 tx_buffer[ADS131M_FRAME_BYTES(ADS131M_MAX_CHANNELS)] + __aligned(IIO_DMA_MINALIGN); + u8 rx_buffer[ADS131M_FRAME_BYTES(ADS131M_MAX_CHANNELS)]; +}; + +/** + * ads131m_tx_frame_unlocked - Sends a command frame with Input CRC + * @priv: Device private data structure. + * @command: The 16-bit command to send (e.g., NULL, RREG, RESET). + * + * This function sends a command in Word 0, and its calculated 16-bit + * CRC in Word 1, as required when Input CRC is enabled. + * + * Return: 0 on success, or a negative error code. + */ +static int ads131m_tx_frame_unlocked(struct ads131m_priv *priv, u32 command) +{ + struct iio_dev *indio_dev = priv->indio_dev; + u16 crc; + + lockdep_assert_held(&priv->lock); + + memset(priv->tx_buffer, 0, ADS131M_FRAME_BYTES(indio_dev->num_channels)); + + /* Word 0: 16-bit command, MSB-aligned in 24-bit word */ + put_unaligned_be16(command, &priv->tx_buffer[0]); + + /* Word 1: Input CRC. Calculated over the 3 bytes of Word 0. */ + crc = crc_itu_t(0xffff, priv->tx_buffer, 3); + put_unaligned_be16(crc, &priv->tx_buffer[3]); + + return spi_sync(priv->spi, &priv->msg); +} + +/** + * ads131m_rx_frame_unlocked - Receives a full SPI data frame. + * @priv: Device private data structure. + * + * This function sends a NULL command (with its CRC) to clock out a + * full SPI frame from the device (e.g., response + channel data + CRC). + * + * Return: 0 on success, or a negative error code. + */ +static int ads131m_rx_frame_unlocked(struct ads131m_priv *priv) +{ + return ads131m_tx_frame_unlocked(priv, ADS131M_CMD_NULL); +} + +/** + * ads131m_check_status_crc_err - Checks for an Input CRC error. + * @priv: Device private data structure. + * + * Sends a NULL command to fetch the STATUS register and checks the + * CRC_ERR bit. This is used to verify the integrity of the previous + * command (like RREG or WREG). + * + * Return: 0 on success, -EIO if CRC_ERR bit is set. + */ +static int ads131m_check_status_crc_err(struct ads131m_priv *priv) +{ + struct device *dev = &priv->spi->dev; + u16 status; + int ret; + + lockdep_assert_held(&priv->lock); + + ret = ads131m_rx_frame_unlocked(priv); + if (ret < 0) { + dev_err_ratelimited(dev, + "SPI error on STATUS read for CRC check\n"); + return ret; + } + + status = get_unaligned_be16(&priv->rx_buffer[0]); + if (status & ADS131M_STATUS_CRC_ERR) { + dev_err_ratelimited(dev, + "Input CRC error reported in STATUS = 0x%04x\n", + status); + return -EIO; + } + + return 0; +} + +/** + * ads131m_write_reg_unlocked - Writes a single register and verifies the ACK. + * @priv: Device private data structure. + * @reg: The 8-bit register address. + * @val: The 16-bit value to write. + * + * This function performs the full 3-cycle WREG operation with Input CRC: + * 1. (Cycle 1) Sends WREG command, data, and its calculated CRC. + * 2. (Cycle 2) Sends NULL+CRC to retrieve the response from Cycle 1. + * 3. Verifies the response is the correct ACK for the WREG. + * 4. (Cycle 3) Sends NULL+CRC to retrieve STATUS and check for CRC_ERR. + * + * Return: 0 on success, or a negative error code. + */ +static int ads131m_write_reg_unlocked(struct ads131m_priv *priv, u8 reg, u16 val) +{ + struct iio_dev *indio_dev = priv->indio_dev; + u16 command, expected_ack, response, crc; + struct device *dev = &priv->spi->dev; + int ret_crc_err = 0; + int ret; + + lockdep_assert_held(&priv->lock); + + command = ADS131M_CMD_WREG(reg, 0); /* n = 0 for 1 register */ + /* + * Per Table 8-11, WREG response is: 010a aaaa ammm mmmm + * For 1 reg (n = 0 -> m = 0): 010a aaaa a000 0000 = 0x4000 | (reg << 7) + */ + expected_ack = 0x4000 | (reg << 7); + + /* Cycle 1: Send WREG Command + Data + Input CRC */ + + memset(priv->tx_buffer, 0, ADS131M_FRAME_BYTES(indio_dev->num_channels)); + + /* Word 0: WREG command, 1 reg (n = 0), MSB-aligned */ + put_unaligned_be16(command, &priv->tx_buffer[0]); + + /* Word 1: Data, MSB-aligned */ + put_unaligned_be16(val, &priv->tx_buffer[3]); + + /* Word 2: Input CRC. Calculated over Word 0 (Cmd) and Word 1 (Data). */ + crc = crc_itu_t(0xffff, priv->tx_buffer, 6); + put_unaligned_be16(crc, &priv->tx_buffer[6]); + + /* Ignore the RX buffer (it's from the previous command) */ + ret = spi_sync(priv->spi, &priv->msg); + if (ret < 0) { + dev_err_ratelimited(dev, "SPI error on WREG (cycle 1)\n"); + return ret; + } + + /* Cycle 2: Send NULL Command to get the WREG response */ + ret = ads131m_rx_frame_unlocked(priv); + if (ret < 0) { + dev_err_ratelimited(dev, "SPI error on WREG ACK (cycle 2)\n"); + return ret; + } + + /* + * Response is in the first 2 bytes of the RX buffer + * (MSB-aligned 16-bit response) + */ + response = get_unaligned_be16(&priv->rx_buffer[0]); + if (response != expected_ack) { + dev_err_ratelimited(dev, "WREG(0x%02x) failed, expected ACK 0x%04x, got 0x%04x\n", + reg, expected_ack, response); + ret_crc_err = -EIO; + /* + * Don't return yet, still need to do Cycle 3 to clear + * any potential CRC_ERR flag from this failed command. + */ + } + + /* + * Cycle 3: Check STATUS for Input CRC error. + * This is necessary even if ACK was wrong, to clear the CRC_ERR flag. + */ + ret = ads131m_check_status_crc_err(priv); + if (ret < 0) + return ret; + + return ret_crc_err; +} + +/** + * ads131m_read_reg_unlocked - Reads a single register from the device. + * @priv: Device private data structure. + * @reg: The 8-bit register address. + * @val: Pointer to store the 16-bit register value. + * + * This function performs the full 3-cycle RREG operation with Input CRC: + * 1. (Cycle 1) Sends the RREG command + Input CRC. + * 2. (Cycle 2) Sends NULL+CRC to retrieve the register data. + * 3. (Cycle 3) Sends NULL+CRC to retrieve STATUS and check for CRC_ERR. + * + * Return: 0 on success, or a negative error code. + */ +static int ads131m_read_reg_unlocked(struct ads131m_priv *priv, u8 reg, u16 *val) +{ + struct device *dev = &priv->spi->dev; + u16 command; + int ret; + + lockdep_assert_held(&priv->lock); + + command = ADS131M_CMD_RREG(reg, 0); /* n=0 for 1 register */ + + /* + * Cycle 1: Send RREG Command + Input CRC + * Ignore the RX buffer (it's from the previous command) + */ + ret = ads131m_tx_frame_unlocked(priv, command); + if (ret < 0) { + dev_err_ratelimited(dev, "SPI error on RREG (cycle 1)\n"); + return ret; + } + + /* Cycle 2: Send NULL Command to get the register data */ + ret = ads131m_rx_frame_unlocked(priv); + if (ret < 0) { + dev_err_ratelimited(dev, "SPI error on RREG data (cycle 2)\n"); + return ret; + } + + /* + * Per datasheet, for a single reg read, the response is the data. + * It's in the first 2 bytes of the RX buffer (MSB-aligned 16-bit). + */ + *val = get_unaligned_be16(&priv->rx_buffer[0]); + + /* + * Cycle 3: Check STATUS for Input CRC error. + * The RREG command does not execute if CRC is bad, but we read + * STATUS anyway to clear the flag in case it was set. + */ + return ads131m_check_status_crc_err(priv); +} + +/** + * ads131m_rmw_reg - Reads, modifies, and writes a single register. + * @priv: Device private data structure. + * @reg: The 8-bit register address. + * @clear: Bitmask of bits to clear. + * @set: Bitmask of bits to set. + * + * This function performs an atomic read-modify-write operation on a register. + * It reads the register, applies the clear and set masks, and writes + * the new value back if it has changed. + * + * Return: 0 on success, or a negative error code. + */ +static int ads131m_rmw_reg(struct ads131m_priv *priv, u8 reg, u16 clear, u16 set) +{ + u16 old_val, new_val; + int ret; + + guard(mutex)(&priv->lock); + + ret = ads131m_read_reg_unlocked(priv, reg, &old_val); + if (ret < 0) + return ret; + + new_val = (old_val & ~clear) | set; + if (new_val == old_val) + return 0; + + return ads131m_write_reg_unlocked(priv, reg, new_val); +} + +/** + * ads131m_verify_output_crc - Verifies the CRC of the received SPI frame. + * @priv: Device private data structure. + * + * This function calculates the CRC-16-CCITT (Poly 0x1021, Seed 0xFFFF) over + * the received response and channel data, and compares it to the CRC word + * received at the end of the SPI frame. + * + * Return: 0 on success, -EIO on CRC mismatch. + */ +static int ads131m_verify_output_crc(struct ads131m_priv *priv) +{ + struct iio_dev *indio_dev = priv->indio_dev; + struct device *dev = &priv->spi->dev; + u16 calculated_crc, received_crc; + size_t data_len; + + lockdep_assert_held(&priv->lock); + + /* + * Frame: [Response][Chan 0]...[Chan N-1][CRC Word] + * Data for CRC: [Response][Chan 0]...[Chan N-1] + * Data length = (N_channels + 1) * 3 bytes (at 24-bit word size) + */ + data_len = ADS131M_FRAME_BYTES(indio_dev->num_channels) - 3; + calculated_crc = crc_itu_t(0xffff, priv->rx_buffer, data_len); + + /* + * The received 16-bit CRC is MSB-aligned in the last 24-bit word. + * We extract it from the first 2 bytes (BE) of that word. + */ + received_crc = get_unaligned_be16(&priv->rx_buffer[data_len]); + if (calculated_crc != received_crc) { + dev_err_ratelimited(dev, "Output CRC error. Got %04x, expected %04x\n", + received_crc, calculated_crc); + return -EIO; + } + + return 0; +} + +/** + * ads131m_adc_read - Reads channel data, checks input and output CRCs. + * @priv: Device private data structure. + * @channel: The channel number to read. + * @val: Pointer to store the raw 24-bit value. + * + * This function sends a NULL command (with Input CRC) to retrieve data. + * It checks the received STATUS word for any Input CRC errors from the + * previous command, and then verifies the Output CRC of the current + * data frame. + * + * Return: 0 on success, or a negative error code. + */ +static int ads131m_adc_read(struct ads131m_priv *priv, u8 channel, s32 *val) +{ + struct device *dev = &priv->spi->dev; + u16 status; + int ret; + u8 *buf; + + guard(mutex)(&priv->lock); + + /* Send NULL command + Input CRC, and receive data frame */ + ret = ads131m_rx_frame_unlocked(priv); + if (ret < 0) + return ret; + + /* + * Check STATUS for Input CRC error from the previous command frame. + * Note: the STATUS word belongs to the frame before this NULL command. + */ + status = get_unaligned_be16(&priv->rx_buffer[0]); + if (status & ADS131M_STATUS_CRC_ERR) { + dev_err_ratelimited(dev, + "Previous input CRC error reported in STATUS (0x%04x)\n", + status); + } + + ret = ads131m_verify_output_crc(priv); + if (ret < 0) + return ret; + + buf = &priv->rx_buffer[ADS131M_CHANNEL_INDEX(channel)]; + *val = sign_extend32(get_unaligned_be24(buf), ADS131M_CODE_BITS); + + return 0; +} + +static int ads131m_read_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *channel, + int *val, int *val2, long mask) +{ + struct ads131m_priv *priv = iio_priv(indio_dev); + int ret; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + ret = ads131m_adc_read(priv, channel->channel, val); + if (ret) + return ret; + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + *val = priv->scale_val; + *val2 = priv->scale_val2; + + return IIO_VAL_FRACTIONAL; + default: + return -EINVAL; + } +} + +#define ADS131M_VOLTAGE_CHANNEL(num) \ + { \ + .type = IIO_VOLTAGE, \ + .differential = 1, \ + .indexed = 1, \ + .channel = (num), \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ + } + +static const struct iio_chan_spec ads131m02_channels[] = { + ADS131M_VOLTAGE_CHANNEL(0), + ADS131M_VOLTAGE_CHANNEL(1), +}; + +static const struct iio_chan_spec ads131m03_channels[] = { + ADS131M_VOLTAGE_CHANNEL(0), + ADS131M_VOLTAGE_CHANNEL(1), + ADS131M_VOLTAGE_CHANNEL(2), +}; + +static const struct iio_chan_spec ads131m04_channels[] = { + ADS131M_VOLTAGE_CHANNEL(0), + ADS131M_VOLTAGE_CHANNEL(1), + ADS131M_VOLTAGE_CHANNEL(2), + ADS131M_VOLTAGE_CHANNEL(3), +}; + +static const struct iio_chan_spec ads131m06_channels[] = { + ADS131M_VOLTAGE_CHANNEL(0), + ADS131M_VOLTAGE_CHANNEL(1), + ADS131M_VOLTAGE_CHANNEL(2), + ADS131M_VOLTAGE_CHANNEL(3), + ADS131M_VOLTAGE_CHANNEL(4), + ADS131M_VOLTAGE_CHANNEL(5), +}; + +static const struct iio_chan_spec ads131m08_channels[] = { + ADS131M_VOLTAGE_CHANNEL(0), + ADS131M_VOLTAGE_CHANNEL(1), + ADS131M_VOLTAGE_CHANNEL(2), + ADS131M_VOLTAGE_CHANNEL(3), + ADS131M_VOLTAGE_CHANNEL(4), + ADS131M_VOLTAGE_CHANNEL(5), + ADS131M_VOLTAGE_CHANNEL(6), + ADS131M_VOLTAGE_CHANNEL(7), +}; + +static const struct ads131m_configuration ads131m02_config = { + .channels = ads131m02_channels, + .num_channels = ARRAY_SIZE(ads131m02_channels), + .reset_ack = 0xff22, + .name = "ads131m02", +}; + +static const struct ads131m_configuration ads131m03_config = { + .channels = ads131m03_channels, + .num_channels = ARRAY_SIZE(ads131m03_channels), + .reset_ack = 0xff23, + .name = "ads131m03", +}; + +static const struct ads131m_configuration ads131m04_config = { + .channels = ads131m04_channels, + .num_channels = ARRAY_SIZE(ads131m04_channels), + .reset_ack = 0xff24, + .name = "ads131m04", +}; + +static const struct ads131m_configuration ads131m06_config = { + .channels = ads131m06_channels, + .num_channels = ARRAY_SIZE(ads131m06_channels), + .reset_ack = 0xff26, + .supports_extref = true, + .supports_xtal = true, + .name = "ads131m06", +}; + +static const struct ads131m_configuration ads131m08_config = { + .channels = ads131m08_channels, + .num_channels = ARRAY_SIZE(ads131m08_channels), + .reset_ack = 0xff28, + .supports_extref = true, + .supports_xtal = true, + .name = "ads131m08", +}; + +static const struct iio_info ads131m_info = { + .read_raw = ads131m_read_raw, +}; + +/* + * Prepares the reusable SPI message structure for a full-duplex transfer. + * The ADS131M requires sending a command frame while simultaneously + * receiving the response/data frame from the previous command cycle. + * + * This message is optimized for the primary data acquisition workflow: + * sending a single-word command (like NULL) and receiving a full data + * frame (Response + N*Channels + CRC). + * + * This message is sized for a full data frame and is reused for all + * command/data cycles. The driver does not implement variable-length SPI + * messages. + * + * Return: 0 on success, or a negative error code. + */ +static int ads131m_prepare_message(struct ads131m_priv *priv) +{ + struct iio_dev *indio_dev = priv->indio_dev; + struct device *dev = &priv->spi->dev; + int ret; + + priv->xfer.tx_buf = priv->tx_buffer; + priv->xfer.rx_buf = priv->rx_buffer; + priv->xfer.len = ADS131M_FRAME_BYTES(indio_dev->num_channels); + spi_message_init_with_transfers(&priv->msg, &priv->xfer, 1); + + ret = devm_spi_optimize_message(dev, priv->spi, &priv->msg); + if (ret) + return dev_err_probe(dev, ret, "failed to optimize SPI message\n"); + + return 0; +} + +/** + * ads131m_hw_reset - Pulses the optional hardware reset. + * @priv: Device private data structure. + * @rstc: Reset control for the /RESET line. + * + * Pulses the /RESET line to perform a hardware reset and waits the + * required t_REGACQ time for the device to be ready. + * + * Return: 0 on success, or a negative error code. + */ +static int ads131m_hw_reset(struct ads131m_priv *priv, + struct reset_control *rstc) +{ + struct device *dev = &priv->spi->dev; + int ret; + + /* + * Manually pulse the reset line using the framework. + * The reset-gpio provider does not implement the .reset op, + * so we must use .assert and .deassert. + */ + ret = reset_control_assert(rstc); + if (ret) + return dev_err_probe(dev, ret, "Failed to assert reset\n"); + + /* Datasheet: Hold /RESET low for > 2 f_CLKIN cycles. 1us is ample. */ + fsleep(1); + + ret = reset_control_deassert(rstc); + if (ret < 0) + return dev_err_probe(dev, ret, "Failed to deassert reset\n"); + + /* Wait t_REGACQ (5us) for registers to be accessible */ + fsleep(ADS131M_RESET_DELAY_US); + + return 0; +} + +/** + * ads131m_sw_reset - Issues a software RESET and verifies ACK. + * @priv: Device private data structure. + * + * This function sends a RESET command (with Input CRC), waits t_REGACQ, + * reads back the RESET ACK, and then sends a final NULL to check for + * any input CRC errors. + * + * Return: 0 on success, or a negative error code. + */ +static int ads131m_sw_reset(struct ads131m_priv *priv) +{ + u16 expected_ack = priv->config->reset_ack; + struct device *dev = &priv->spi->dev; + u16 response; + int ret; + + guard(mutex)(&priv->lock); + + ret = ads131m_tx_frame_unlocked(priv, ADS131M_CMD_RESET); + if (ret < 0) + return dev_err_probe(dev, ret, "Failed to send RESET command\n"); + + /* Wait t_REGACQ (5us) for device to be ready after reset */ + fsleep(ADS131M_RESET_DELAY_US); + + /* Cycle 2: Send NULL + CRC to retrieve the response to the RESET */ + ret = ads131m_rx_frame_unlocked(priv); + if (ret < 0) + return dev_err_probe(dev, ret, "Failed to read RESET ACK\n"); + + response = get_unaligned_be16(&priv->rx_buffer[0]); + + /* Check against the device-specific ACK value */ + if (response != expected_ack) + return dev_err_probe(dev, -EIO, + "RESET ACK mismatch, got 0x%04x, expected 0x%04x\n", + response, expected_ack); + + /* Cycle 3: Check STATUS for Input CRC error on the RESET command. */ + return ads131m_check_status_crc_err(priv); +} + +/** + * ads131m_reset - Resets the device using hardware or software. + * @priv: Device private data structure. + * @rstc: Optional reset control, or NULL for software reset. + * + * This function performs a hardware reset if supported (rstc provided), + * otherwise it issues a software RESET command via SPI. + * + * Note: The software reset path also validates the device's reset + * acknowledgment against the expected ID for the compatible string. + * The hardware reset path bypasses this ID check. + * + * Return: 0 on success, or a negative error code. + */ +static int ads131m_reset(struct ads131m_priv *priv, struct reset_control *rstc) +{ + if (rstc) + return ads131m_hw_reset(priv, rstc); + + return ads131m_sw_reset(priv); +} + +static int ads131m_power_init(struct ads131m_priv *priv) +{ + static const char * const supply_ids[] = { "avdd", "dvdd" }; + struct device *dev = &priv->spi->dev; + int vref_uV; + int ret; + + ret = devm_regulator_bulk_get_enable(dev, ARRAY_SIZE(supply_ids), supply_ids); + if (ret < 0) + return dev_err_probe(dev, ret, "failed to enable regulators\n"); + + /* Default to Internal 1.2V reference: 1200mV / 2^23 */ + priv->scale_val = ADS131M_VREF_INTERNAL_mV; + priv->scale_val2 = BIT(ADS131M_CODE_BITS); + + if (!priv->config->supports_extref) + return 0; + + ret = devm_regulator_get_enable_read_voltage(dev, "refin"); + if (ret < 0 && ret != -ENODEV) + return dev_err_probe(dev, ret, "failed to get refin supply\n"); + + if (ret == 0) + return dev_err_probe(dev, -EINVAL, "refin supply reports 0V\n"); + + if (ret == -ENODEV) + return 0; + + vref_uV = ret; + + /* + * External reference found: Scale(mV) = (vref_uV * 0.96) / 1000 + * The denominator is 100 * 2^23 because of the 0.96 factor (96/100). + */ + priv->scale_val = div_s64((s64)vref_uV * ADS131M_EXTREF_SCALE_NUM, 1000); + priv->scale_val2 = ADS131M_EXTREF_SCALE_DEN * BIT(ADS131M_CODE_BITS); + priv->use_external_ref = true; + + return 0; +} + +/** + * ads131m_hw_init - Initialize the ADC hardware. + * @priv: Device private data structure. + * @rstc: Optional reset control, or NULL for software reset. + * @is_xtal: True if 'clock-names' is "xtal", false if "clkin". + * + * Return: 0 on success, or a negative error code. + */ +static int ads131m_hw_init(struct ads131m_priv *priv, + struct reset_control *rstc, bool is_xtal) +{ + struct device *dev = &priv->spi->dev; + u16 mode_clear, mode_set; + int ret; + + ret = ads131m_reset(priv, rstc); + if (ret < 0) + return ret; + + /* + * Configure CLOCK register (0x03) based on DT properties. + * This register only needs configuration for 32-pin (M06/M08) + * variants, as the configurable bits (XTAL_DIS, EXTREF_EN) + * are reserved on 20-pin (M02/M03/M04) variants. + */ + if (priv->config->supports_xtal || priv->config->supports_extref) { + u16 clk_set = 0; + + if (priv->config->supports_xtal && !is_xtal) + clk_set |= ADS131M_CLOCK_XTAL_DIS; + + if (priv->config->supports_extref && priv->use_external_ref) + clk_set |= ADS131M_CLOCK_EXTREF_EN; + + ret = ads131m_rmw_reg(priv, ADS131M_REG_CLOCK, + ADS131M_CLOCK_EXTREF_EN | ADS131M_CLOCK_XTAL_DIS, + clk_set); + if (ret < 0) + return dev_err_probe(dev, ret, "Failed to configure CLOCK register\n"); + } + + /* + * The RESET command sets all registers to default, which means: + * 1. The RESET bit (Bit 10) in MODE is set to '1'. + * 2. The CRC_TYPE bit (Bit 11) in MODE is '0' (CCITT). + * 3. The RX_CRC_EN bit (Bit 12) in MODE is '0' (Disabled). + * + * We must: + * 1. Clear the RESET bit. + * 2. Enable Input CRC (RX_CRC_EN). + * 3. Explicitly clear the ANSI CRC bit (for certainty). + */ + mode_clear = ADS131M_MODE_CRC_TYPE_ANSI | ADS131M_MODE_RESET_FLAG; + mode_set = ADS131M_MODE_RX_CRC_EN; + + ret = ads131m_rmw_reg(priv, ADS131M_REG_MODE, mode_clear, mode_set); + if (ret < 0) + return dev_err_probe(dev, ret, "Failed to configure MODE register\n"); + + return 0; +} + +/** + * ads131m_parse_clock - enable clock and detect "xtal" selection + * @priv: Device private data structure. + * @is_xtal: result flag (true if "xtal", false if default "clkin") + * + * Return: 0 on success, or a negative error code. + */ +static int ads131m_parse_clock(struct ads131m_priv *priv, bool *is_xtal) +{ + struct device *dev = &priv->spi->dev; + struct clk *clk; + int ret; + + clk = devm_clk_get_enabled(dev, NULL); + if (IS_ERR_OR_NULL(clk)) { + if (IS_ERR(clk)) + ret = PTR_ERR(clk); + else + ret = -ENODEV; + + return dev_err_probe(dev, ret, "clk get enabled failed\n"); + } + + ret = device_property_match_string(dev, "clock-names", "xtal"); + if (ret > 0) + return dev_err_probe(dev, -EINVAL, + "'xtal' must be the only or first clock name"); + + if (ret < 0 && ret != -ENODATA) + return dev_err_probe(dev, ret, + "failed to read 'clock-names' property"); + + if (ret == 0 && !priv->config->supports_xtal) + return dev_err_probe(dev, -EINVAL, + "'xtal' clock not supported on this device"); + + *is_xtal = !ret; + + return 0; +} + +static int ads131m_probe(struct spi_device *spi) +{ + const struct ads131m_configuration *config; + struct device *dev = &spi->dev; + struct reset_control *rstc; + struct iio_dev *indio_dev; + struct ads131m_priv *priv; + bool is_xtal; + int ret; + + indio_dev = devm_iio_device_alloc(dev, sizeof(*priv)); + if (!indio_dev) + return -ENOMEM; + + priv = iio_priv(indio_dev); + priv->indio_dev = indio_dev; + priv->spi = spi; + + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->info = &ads131m_info; + + config = spi_get_device_match_data(spi); + + priv->config = config; + indio_dev->name = config->name; + indio_dev->channels = config->channels; + indio_dev->num_channels = config->num_channels; + + rstc = devm_reset_control_get_optional_exclusive(dev, NULL); + if (IS_ERR(rstc)) + return dev_err_probe(dev, PTR_ERR(rstc), + "Failed to get reset controller\n"); + + ret = devm_mutex_init(dev, &priv->lock); + if (ret < 0) + return ret; + + ret = ads131m_prepare_message(priv); + if (ret < 0) + return ret; + + ret = ads131m_power_init(priv); + if (ret < 0) + return ret; + + /* Power must be applied and stable before the clock is enabled. */ + ret = ads131m_parse_clock(priv, &is_xtal); + if (ret < 0) + return ret; + + ret = ads131m_hw_init(priv, rstc, is_xtal); + if (ret < 0) + return ret; + + return devm_iio_device_register(dev, indio_dev); +} + +static const struct of_device_id ads131m_of_match[] = { + { .compatible = "ti,ads131m02", .data = &ads131m02_config }, + { .compatible = "ti,ads131m03", .data = &ads131m03_config }, + { .compatible = "ti,ads131m04", .data = &ads131m04_config }, + { .compatible = "ti,ads131m06", .data = &ads131m06_config }, + { .compatible = "ti,ads131m08", .data = &ads131m08_config }, + { } +}; +MODULE_DEVICE_TABLE(of, ads131m_of_match); + +static const struct spi_device_id ads131m_id[] = { + { .name = "ads131m02", .driver_data = (kernel_ulong_t)&ads131m02_config }, + { .name = "ads131m03", .driver_data = (kernel_ulong_t)&ads131m03_config }, + { .name = "ads131m04", .driver_data = (kernel_ulong_t)&ads131m04_config }, + { .name = "ads131m06", .driver_data = (kernel_ulong_t)&ads131m06_config }, + { .name = "ads131m08", .driver_data = (kernel_ulong_t)&ads131m08_config }, + { } +}; +MODULE_DEVICE_TABLE(spi, ads131m_id); + +static struct spi_driver ads131m_driver = { + .driver = { + .name = "ads131m02", + .of_match_table = ads131m_of_match, + }, + .probe = ads131m_probe, + .id_table = ads131m_id, +}; +module_spi_driver(ads131m_driver); + +MODULE_AUTHOR("David Jander "); +MODULE_DESCRIPTION("Texas Instruments ADS131M02 ADC driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/iio/adc/ti-ads7138.c b/drivers/iio/adc/ti-ads7138.c new file mode 100644 index 000000000..da82a6947 --- /dev/null +++ b/drivers/iio/adc/ti-ads7138.c @@ -0,0 +1,773 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * ADS7138 - Texas Instruments Analog-to-Digital Converter + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +/* + * Always assume 16 bits resolution as HW registers are aligned like that and + * with enabled oversampling/averaging it actually corresponds to 16 bits. + */ +#define ADS7138_RES_BITS 16 + +/* ADS7138 operation codes */ +#define ADS7138_OPCODE_SINGLE_WRITE 0x08 +#define ADS7138_OPCODE_SET_BIT 0x18 +#define ADS7138_OPCODE_CLEAR_BIT 0x20 +#define ADS7138_OPCODE_BLOCK_WRITE 0x28 +#define ADS7138_OPCODE_BLOCK_READ 0x30 + +/* ADS7138 registers */ +#define ADS7138_REG_GENERAL_CFG 0x01 +#define ADS7138_REG_OSR_CFG 0x03 +#define ADS7138_REG_OPMODE_CFG 0x04 +#define ADS7138_REG_SEQUENCE_CFG 0x10 +#define ADS7138_REG_AUTO_SEQ_CH_SEL 0x12 +#define ADS7138_REG_ALERT_CH_SEL 0x14 +#define ADS7138_REG_EVENT_FLAG 0x18 +#define ADS7138_REG_EVENT_HIGH_FLAG 0x1A +#define ADS7138_REG_EVENT_LOW_FLAG 0x1C +#define ADS7138_REG_HIGH_TH_HYS_CH(x) ((x) * 4 + 0x20) +#define ADS7138_REG_LOW_TH_CNT_CH(x) ((x) * 4 + 0x22) +#define ADS7138_REG_MAX_LSB_CH(x) ((x) * 2 + 0x60) +#define ADS7138_REG_MIN_LSB_CH(x) ((x) * 2 + 0x80) +#define ADS7138_REG_RECENT_LSB_CH(x) ((x) * 2 + 0xA0) + +#define ADS7138_GENERAL_CFG_RST BIT(0) +#define ADS7138_GENERAL_CFG_DWC_EN BIT(4) +#define ADS7138_GENERAL_CFG_STATS_EN BIT(5) +#define ADS7138_OSR_CFG_MASK GENMASK(2, 0) +#define ADS7138_OPMODE_CFG_CONV_MODE BIT(5) +#define ADS7138_OPMODE_CFG_FREQ_MASK GENMASK(4, 0) +#define ADS7138_SEQUENCE_CFG_SEQ_MODE BIT(0) +#define ADS7138_SEQUENCE_CFG_SEQ_START BIT(4) +#define ADS7138_THRESHOLD_LSB_MASK GENMASK(7, 4) + +enum ads7138_modes { + ADS7138_MODE_MANUAL, + ADS7138_MODE_AUTO, +}; + +struct ads7138_chip_data { + const char *name; + const int channel_num; +}; + +struct ads7138_data { + /* Protects RMW access to the I2C interface */ + struct mutex lock; + struct i2c_client *client; + struct regulator *vref_regu; + const struct ads7138_chip_data *chip_data; +}; + +/* + * 2D array of available sampling frequencies and the corresponding register + * values. Structured like this to be easily usable in read_avail function. + */ +static const int ads7138_samp_freqs_bits[2][26] = { + { + 163, 244, 326, 488, 651, 977, 1302, 1953, + 2604, 3906, 5208, 7813, 10417, 15625, 20833, 31250, + 41667, 62500, 83333, 125000, 166667, 250000, 333333, 500000, + 666667, 1000000 + }, { + 0x1f, 0x1e, 0x1d, 0x1c, 0x1b, 0x1a, 0x19, 0x18, + 0x17, 0x16, 0x15, 0x14, 0x13, 0x12, 0x11, 0x10, + /* Here is a hole, due to duplicate frequencies */ + 0x09, 0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, + 0x01, 0x00 + } +}; + +static const int ads7138_oversampling_ratios[] = { + 1, 2, 4, 8, 16, 32, 64, 128 +}; + +static int ads7138_i2c_write_block(const struct i2c_client *client, u8 reg, + u8 *values, u8 length) +{ + int ret; + int len = length + 2; /* "+ 2" for OPCODE and reg */ + + u8 *buf __free(kfree) = kmalloc(len, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + buf[0] = ADS7138_OPCODE_BLOCK_WRITE; + buf[1] = reg; + memcpy(&buf[2], values, length); + + ret = i2c_master_send(client, buf, len); + if (ret < 0) + return ret; + if (ret != len) + return -EIO; + + return 0; +} + +static int ads7138_i2c_write_with_opcode(const struct i2c_client *client, + u8 reg, u8 regval, u8 opcode) +{ + u8 buf[3] = { opcode, reg, regval }; + int ret; + + ret = i2c_master_send(client, buf, ARRAY_SIZE(buf)); + if (ret < 0) + return ret; + if (ret != ARRAY_SIZE(buf)) + return -EIO; + + return 0; +} + +static int ads7138_i2c_write(const struct i2c_client *client, u8 reg, u8 value) +{ + return ads7138_i2c_write_with_opcode(client, reg, value, + ADS7138_OPCODE_SINGLE_WRITE); +} + +static int ads7138_i2c_set_bit(const struct i2c_client *client, u8 reg, u8 bits) +{ + return ads7138_i2c_write_with_opcode(client, reg, bits, + ADS7138_OPCODE_SET_BIT); +} + +static int ads7138_i2c_clear_bit(const struct i2c_client *client, u8 reg, u8 bits) +{ + return ads7138_i2c_write_with_opcode(client, reg, bits, + ADS7138_OPCODE_CLEAR_BIT); +} + +static int ads7138_i2c_read_block(const struct i2c_client *client, u8 reg, + u8 *out_values, u8 length) +{ + u8 buf[2] = { ADS7138_OPCODE_BLOCK_READ, reg }; + int ret; + struct i2c_msg msgs[] = { + { + .addr = client->addr, + .len = ARRAY_SIZE(buf), + .buf = buf, + }, + { + .addr = client->addr, + .flags = I2C_M_RD, + .len = length, + .buf = out_values, + }, + }; + + ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); + if (ret < 0) + return ret; + if (ret != ARRAY_SIZE(msgs)) + return -EIO; + + return 0; +} + +static int ads7138_i2c_read(const struct i2c_client *client, u8 reg) +{ + u8 value; + int ret; + + ret = ads7138_i2c_read_block(client, reg, &value, sizeof(value)); + if (ret) + return ret; + return value; +} + +static int ads7138_freq_to_bits(int freq) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(ads7138_samp_freqs_bits[0]); i++) + if (freq == ads7138_samp_freqs_bits[0][i]) + return ads7138_samp_freqs_bits[1][i]; + + return -EINVAL; +} + +static int ads7138_bits_to_freq(int bits) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(ads7138_samp_freqs_bits[1]); i++) + if (bits == ads7138_samp_freqs_bits[1][i]) + return ads7138_samp_freqs_bits[0][i]; + + return -EINVAL; +} + +static int ads7138_osr_to_bits(int osr) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(ads7138_oversampling_ratios); i++) + if (osr == ads7138_oversampling_ratios[i]) + return i; + + return -EINVAL; +} + +static int ads7138_read_statistics(const struct i2c_client *client, u8 reg, + u8 *out_values, u8 length) +{ + int ret; + + /* Disable statistics update so the value is not updated mid read */ + ret = ads7138_i2c_clear_bit(client, ADS7138_REG_GENERAL_CFG, + ADS7138_GENERAL_CFG_STATS_EN); + if (ret) + return ret; + + ret = ads7138_i2c_read_block(client, reg, out_values, length); + if (ret) + return ret; + + /* Enable statistics update after read */ + return ads7138_i2c_set_bit(client, ADS7138_REG_GENERAL_CFG, + ADS7138_GENERAL_CFG_STATS_EN); +} + +static int ads7138_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int *val, + int *val2, long mask) +{ + struct ads7138_data *data = iio_priv(indio_dev); + int ret, vref, bits; + u8 values[2]; + + switch (mask) { + /* + * Reading the statistics registers reinitializes them. This is + * unfortunate but necessary to prevent data races. + */ + case IIO_CHAN_INFO_RAW: + ret = ads7138_read_statistics(data->client, + ADS7138_REG_RECENT_LSB_CH(chan->channel), + values, ARRAY_SIZE(values)); + if (ret) + return ret; + + *val = get_unaligned_le16(values); + return IIO_VAL_INT; + case IIO_CHAN_INFO_PEAK: + ret = ads7138_read_statistics(data->client, + ADS7138_REG_MAX_LSB_CH(chan->channel), + values, ARRAY_SIZE(values)); + if (ret) + return ret; + + *val = get_unaligned_le16(values); + return IIO_VAL_INT; + case IIO_CHAN_INFO_TROUGH: + ret = ads7138_read_statistics(data->client, + ADS7138_REG_MIN_LSB_CH(chan->channel), + values, ARRAY_SIZE(values)); + if (ret) + return ret; + + *val = get_unaligned_le16(values); + return IIO_VAL_INT; + case IIO_CHAN_INFO_SAMP_FREQ: + ret = ads7138_i2c_read(data->client, ADS7138_REG_OPMODE_CFG); + if (ret < 0) + return ret; + + bits = FIELD_GET(ADS7138_OPMODE_CFG_FREQ_MASK, ret); + *val = ads7138_bits_to_freq(bits); + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + vref = regulator_get_voltage(data->vref_regu); + if (vref < 0) + return vref; + *val = vref / 1000; + *val2 = ADS7138_RES_BITS; + return IIO_VAL_FRACTIONAL_LOG2; + case IIO_CHAN_INFO_OVERSAMPLING_RATIO: + ret = ads7138_i2c_read(data->client, ADS7138_REG_OSR_CFG); + if (ret < 0) + return ret; + + bits = FIELD_GET(ADS7138_OSR_CFG_MASK, ret); + *val = ads7138_oversampling_ratios[bits]; + return IIO_VAL_INT; + default: + return -EINVAL; + } +} + +static int ads7138_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int val, + int val2, long mask) +{ + struct ads7138_data *data = iio_priv(indio_dev); + int bits, ret; + u8 value; + + switch (mask) { + case IIO_CHAN_INFO_SAMP_FREQ: { + bits = ads7138_freq_to_bits(val); + if (bits < 0) + return bits; + + guard(mutex)(&data->lock); + ret = ads7138_i2c_read(data->client, ADS7138_REG_OPMODE_CFG); + if (ret < 0) + return ret; + + value = ret & ~ADS7138_OPMODE_CFG_FREQ_MASK; + value |= FIELD_PREP(ADS7138_OPMODE_CFG_FREQ_MASK, bits); + return ads7138_i2c_write(data->client, ADS7138_REG_OPMODE_CFG, + value); + } + case IIO_CHAN_INFO_OVERSAMPLING_RATIO: + bits = ads7138_osr_to_bits(val); + if (bits < 0) + return bits; + + return ads7138_i2c_write(data->client, ADS7138_REG_OSR_CFG, + bits); + default: + return -EINVAL; + } +} + +static int ads7138_read_event(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir, + enum iio_event_info info, int *val, int *val2) +{ + struct ads7138_data *data = iio_priv(indio_dev); + u8 reg, values[2]; + int ret; + + switch (info) { + case IIO_EV_INFO_VALUE: + reg = (dir == IIO_EV_DIR_RISING) ? + ADS7138_REG_HIGH_TH_HYS_CH(chan->channel) : + ADS7138_REG_LOW_TH_CNT_CH(chan->channel); + ret = ads7138_i2c_read_block(data->client, reg, values, + ARRAY_SIZE(values)); + if (ret) + return ret; + + *val = ((values[1] << 4) | (values[0] >> 4)); + return IIO_VAL_INT; + case IIO_EV_INFO_HYSTERESIS: + ret = ads7138_i2c_read(data->client, + ADS7138_REG_HIGH_TH_HYS_CH(chan->channel)); + if (ret < 0) + return ret; + + *val = ret & ~ADS7138_THRESHOLD_LSB_MASK; + return IIO_VAL_INT; + default: + return -EINVAL; + } +} + +static int ads7138_write_event(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir, + enum iio_event_info info, int val, int val2) +{ + struct ads7138_data *data = iio_priv(indio_dev); + u8 reg, values[2]; + int ret; + + switch (info) { + case IIO_EV_INFO_VALUE: { + if (val >= BIT(12) || val < 0) + return -EINVAL; + + reg = (dir == IIO_EV_DIR_RISING) ? + ADS7138_REG_HIGH_TH_HYS_CH(chan->channel) : + ADS7138_REG_LOW_TH_CNT_CH(chan->channel); + + guard(mutex)(&data->lock); + ret = ads7138_i2c_read(data->client, reg); + if (ret < 0) + return ret; + + values[0] = ret & ~ADS7138_THRESHOLD_LSB_MASK; + values[0] |= FIELD_PREP(ADS7138_THRESHOLD_LSB_MASK, val); + values[1] = (val >> 4); + return ads7138_i2c_write_block(data->client, reg, values, + ARRAY_SIZE(values)); + } + case IIO_EV_INFO_HYSTERESIS: { + if (val >= BIT(4) || val < 0) + return -EINVAL; + + reg = ADS7138_REG_HIGH_TH_HYS_CH(chan->channel); + + guard(mutex)(&data->lock); + ret = ads7138_i2c_read(data->client, reg); + if (ret < 0) + return ret; + + values[0] = val & ~ADS7138_THRESHOLD_LSB_MASK; + values[0] |= FIELD_PREP(ADS7138_THRESHOLD_LSB_MASK, ret >> 4); + return ads7138_i2c_write(data->client, reg, values[0]); + } + default: + return -EINVAL; + } +} + +static int ads7138_read_event_config(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir) +{ + struct ads7138_data *data = iio_priv(indio_dev); + int ret; + + if (dir != IIO_EV_DIR_EITHER) + return -EINVAL; + + ret = ads7138_i2c_read(data->client, ADS7138_REG_ALERT_CH_SEL); + if (ret < 0) + return ret; + + return (ret & BIT(chan->channel)) ? 1 : 0; +} + +static int ads7138_write_event_config(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir, bool state) +{ + struct ads7138_data *data = iio_priv(indio_dev); + + if (dir != IIO_EV_DIR_EITHER) + return -EINVAL; + + if (state) + return ads7138_i2c_set_bit(data->client, + ADS7138_REG_ALERT_CH_SEL, + BIT(chan->channel)); + else + return ads7138_i2c_clear_bit(data->client, + ADS7138_REG_ALERT_CH_SEL, + BIT(chan->channel)); +} + +static int ads7138_read_avail(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + const int **vals, int *type, int *length, + long mask) +{ + switch (mask) { + case IIO_CHAN_INFO_SAMP_FREQ: + *vals = ads7138_samp_freqs_bits[0]; + *length = ARRAY_SIZE(ads7138_samp_freqs_bits[0]); + *type = IIO_VAL_INT; + + return IIO_AVAIL_LIST; + case IIO_CHAN_INFO_OVERSAMPLING_RATIO: + *vals = ads7138_oversampling_ratios; + *length = ARRAY_SIZE(ads7138_oversampling_ratios); + *type = IIO_VAL_INT; + + return IIO_AVAIL_LIST; + default: + return -EINVAL; + } +} + +static const struct iio_info ti_ads7138_info = { + .read_raw = &ads7138_read_raw, + .read_avail = &ads7138_read_avail, + .write_raw = &ads7138_write_raw, + .read_event_value = &ads7138_read_event, + .write_event_value = &ads7138_write_event, + .read_event_config = &ads7138_read_event_config, + .write_event_config = &ads7138_write_event_config, +}; + +static const struct iio_event_spec ads7138_events[] = { + { + .type = IIO_EV_TYPE_THRESH, + .dir = IIO_EV_DIR_RISING, + .mask_separate = BIT(IIO_EV_INFO_VALUE) + }, { + .type = IIO_EV_TYPE_THRESH, + .dir = IIO_EV_DIR_FALLING, + .mask_separate = BIT(IIO_EV_INFO_VALUE), + }, { + .type = IIO_EV_TYPE_THRESH, + .dir = IIO_EV_DIR_EITHER, + .mask_separate = BIT(IIO_EV_INFO_HYSTERESIS) | + BIT(IIO_EV_INFO_ENABLE), + }, +}; + +#define ADS7138_V_CHAN(_chan) { \ + .type = IIO_VOLTAGE, \ + .indexed = 1, \ + .channel = _chan, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ + BIT(IIO_CHAN_INFO_PEAK) | \ + BIT(IIO_CHAN_INFO_TROUGH), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SAMP_FREQ) | \ + BIT(IIO_CHAN_INFO_SCALE) | \ + BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \ + .info_mask_shared_by_type_available = \ + BIT(IIO_CHAN_INFO_SAMP_FREQ) | \ + BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \ + .datasheet_name = "AIN"#_chan, \ + .event_spec = ads7138_events, \ + .num_event_specs = ARRAY_SIZE(ads7138_events), \ +} + +static const struct iio_chan_spec ads7138_channels[] = { + ADS7138_V_CHAN(0), + ADS7138_V_CHAN(1), + ADS7138_V_CHAN(2), + ADS7138_V_CHAN(3), + ADS7138_V_CHAN(4), + ADS7138_V_CHAN(5), + ADS7138_V_CHAN(6), + ADS7138_V_CHAN(7), +}; + +static irqreturn_t ads7138_event_handler(int irq, void *priv) +{ + struct iio_dev *indio_dev = priv; + struct ads7138_data *data = iio_priv(indio_dev); + struct device *dev = &data->client->dev; + u8 i, events_high, events_low; + u64 code; + int ret; + + /* Check if interrupt was trigger by us */ + ret = ads7138_i2c_read(data->client, ADS7138_REG_EVENT_FLAG); + if (ret <= 0) + return IRQ_NONE; + + ret = ads7138_i2c_read(data->client, ADS7138_REG_EVENT_HIGH_FLAG); + if (ret < 0) { + dev_warn(dev, "Failed to read event high flags: %d\n", ret); + return IRQ_HANDLED; + } + events_high = ret; + + ret = ads7138_i2c_read(data->client, ADS7138_REG_EVENT_LOW_FLAG); + if (ret < 0) { + dev_warn(dev, "Failed to read event low flags: %d\n", ret); + return IRQ_HANDLED; + } + events_low = ret; + + for (i = 0; i < data->chip_data->channel_num; i++) { + if (events_high & BIT(i)) { + code = IIO_UNMOD_EVENT_CODE(IIO_VOLTAGE, i, + IIO_EV_TYPE_THRESH, + IIO_EV_DIR_RISING); + iio_push_event(indio_dev, code, + iio_get_time_ns(indio_dev)); + } + if (events_low & BIT(i)) { + code = IIO_UNMOD_EVENT_CODE(IIO_VOLTAGE, i, + IIO_EV_TYPE_THRESH, + IIO_EV_DIR_FALLING); + iio_push_event(indio_dev, code, + iio_get_time_ns(indio_dev)); + } + } + + /* Try to clear all interrupt flags */ + ret = ads7138_i2c_write(data->client, ADS7138_REG_EVENT_HIGH_FLAG, 0xFF); + if (ret) + dev_warn(dev, "Failed to clear event high flags: %d\n", ret); + + ret = ads7138_i2c_write(data->client, ADS7138_REG_EVENT_LOW_FLAG, 0xFF); + if (ret) + dev_warn(dev, "Failed to clear event low flags: %d\n", ret); + + return IRQ_HANDLED; +} + +static int ads7138_set_conv_mode(struct ads7138_data *data, + enum ads7138_modes mode) +{ + if (mode == ADS7138_MODE_AUTO) + return ads7138_i2c_set_bit(data->client, ADS7138_REG_OPMODE_CFG, + ADS7138_OPMODE_CFG_CONV_MODE); + return ads7138_i2c_clear_bit(data->client, ADS7138_REG_OPMODE_CFG, + ADS7138_OPMODE_CFG_CONV_MODE); +} + +static int ads7138_init_hw(struct ads7138_data *data) +{ + struct device *dev = &data->client->dev; + int ret; + + data->vref_regu = devm_regulator_get(dev, "avdd"); + if (IS_ERR(data->vref_regu)) + return dev_err_probe(dev, PTR_ERR(data->vref_regu), + "Failed to get avdd regulator\n"); + + ret = regulator_get_voltage(data->vref_regu); + if (ret < 0) + return dev_err_probe(dev, ret, "Failed to get avdd voltage\n"); + + /* Reset the chip to get a defined starting configuration */ + ret = ads7138_i2c_set_bit(data->client, ADS7138_REG_GENERAL_CFG, + ADS7138_GENERAL_CFG_RST); + if (ret) + return ret; + + ret = ads7138_set_conv_mode(data, ADS7138_MODE_AUTO); + if (ret) + return ret; + + /* Enable statistics and digital window comparator */ + ret = ads7138_i2c_set_bit(data->client, ADS7138_REG_GENERAL_CFG, + ADS7138_GENERAL_CFG_STATS_EN | + ADS7138_GENERAL_CFG_DWC_EN); + if (ret) + return ret; + + /* Enable all channels for auto sequencing */ + ret = ads7138_i2c_set_bit(data->client, ADS7138_REG_AUTO_SEQ_CH_SEL, 0xFF); + if (ret) + return ret; + + /* Set auto sequence mode and start sequencing */ + return ads7138_i2c_set_bit(data->client, ADS7138_REG_SEQUENCE_CFG, + ADS7138_SEQUENCE_CFG_SEQ_START | + ADS7138_SEQUENCE_CFG_SEQ_MODE); +} + +static int ads7138_probe(struct i2c_client *client) +{ + struct device *dev = &client->dev; + struct iio_dev *indio_dev; + struct ads7138_data *data; + int ret; + + indio_dev = devm_iio_device_alloc(dev, sizeof(*data)); + if (!indio_dev) + return -ENOMEM; + + data = iio_priv(indio_dev); + data->client = client; + data->chip_data = i2c_get_match_data(client); + if (!data->chip_data) + return -ENODEV; + + ret = devm_mutex_init(dev, &data->lock); + if (ret) + return ret; + + indio_dev->name = data->chip_data->name; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = ads7138_channels; + indio_dev->num_channels = ARRAY_SIZE(ads7138_channels); + indio_dev->info = &ti_ads7138_info; + + i2c_set_clientdata(client, indio_dev); + + if (client->irq > 0) { + ret = devm_request_threaded_irq(dev, client->irq, + NULL, ads7138_event_handler, + IRQF_TRIGGER_LOW | + IRQF_ONESHOT | IRQF_SHARED, + client->name, indio_dev); + if (ret) + return ret; + } + + ret = ads7138_init_hw(data); + if (ret) + return dev_err_probe(dev, ret, "Failed to initialize device\n"); + + ret = devm_iio_device_register(dev, indio_dev); + if (ret) + return dev_err_probe(dev, ret, "Failed to register iio device\n"); + + return 0; +} + +static int ads7138_runtime_suspend(struct device *dev) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct ads7138_data *data = iio_priv(indio_dev); + + return ads7138_set_conv_mode(data, ADS7138_MODE_MANUAL); +} + +static int ads7138_runtime_resume(struct device *dev) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct ads7138_data *data = iio_priv(indio_dev); + + return ads7138_set_conv_mode(data, ADS7138_MODE_AUTO); +} + +static DEFINE_RUNTIME_DEV_PM_OPS(ads7138_pm_ops, + ads7138_runtime_suspend, + ads7138_runtime_resume, + NULL); + +static const struct ads7138_chip_data ads7128_data = { + .name = "ads7128", + .channel_num = 8, +}; + +static const struct ads7138_chip_data ads7138_data = { + .name = "ads7138", + .channel_num = 8, +}; + +static const struct of_device_id ads7138_of_match[] = { + { .compatible = "ti,ads7128", .data = &ads7128_data }, + { .compatible = "ti,ads7138", .data = &ads7138_data }, + { } +}; +MODULE_DEVICE_TABLE(of, ads7138_of_match); + +static const struct i2c_device_id ads7138_device_ids[] = { + { .name = "ads7128", .driver_data = (kernel_ulong_t)&ads7128_data }, + { .name = "ads7138", .driver_data = (kernel_ulong_t)&ads7138_data }, + { } +}; +MODULE_DEVICE_TABLE(i2c, ads7138_device_ids); + +static struct i2c_driver ads7138_driver = { + .driver = { + .name = "ads7138", + .of_match_table = ads7138_of_match, + .pm = pm_ptr(&ads7138_pm_ops), + }, + .id_table = ads7138_device_ids, + .probe = ads7138_probe, +}; +module_i2c_driver(ads7138_driver); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Tobias Sperling "); +MODULE_DESCRIPTION("Driver for TI ADS7138 ADCs"); diff --git a/drivers/iio/adc/ti-ads7924.c b/drivers/iio/adc/ti-ads7924.c new file mode 100644 index 000000000..ff30a52d2 --- /dev/null +++ b/drivers/iio/adc/ti-ads7924.c @@ -0,0 +1,469 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * IIO driver for Texas Instruments ADS7924 ADC, 12-bit, 4-Channels, I2C + * + * Author: Hugo Villeneuve + * Copyright 2022 DimOnOff + * + * based on iio/adc/ti-ads1015.c + * Copyright (c) 2016, Intel Corporation. + * + * Datasheet: https://www.ti.com/lit/gpn/ads7924 + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#define ADS7924_CHANNELS 4 +#define ADS7924_BITS 12 +#define ADS7924_DATA_SHIFT 4 + +/* Registers. */ +#define ADS7924_MODECNTRL_REG 0x00 +#define ADS7924_INTCNTRL_REG 0x01 +#define ADS7924_DATA0_U_REG 0x02 +#define ADS7924_DATA0_L_REG 0x03 +#define ADS7924_DATA1_U_REG 0x04 +#define ADS7924_DATA1_L_REG 0x05 +#define ADS7924_DATA2_U_REG 0x06 +#define ADS7924_DATA2_L_REG 0x07 +#define ADS7924_DATA3_U_REG 0x08 +#define ADS7924_DATA3_L_REG 0x09 +#define ADS7924_ULR0_REG 0x0A +#define ADS7924_LLR0_REG 0x0B +#define ADS7924_ULR1_REG 0x0C +#define ADS7924_LLR1_REG 0x0D +#define ADS7924_ULR2_REG 0x0E +#define ADS7924_LLR2_REG 0x0F +#define ADS7924_ULR3_REG 0x10 +#define ADS7924_LLR3_REG 0x11 +#define ADS7924_INTCONFIG_REG 0x12 +#define ADS7924_SLPCONFIG_REG 0x13 +#define ADS7924_ACQCONFIG_REG 0x14 +#define ADS7924_PWRCONFIG_REG 0x15 +#define ADS7924_RESET_REG 0x16 + +/* + * Register address INC bit: when set to '1', the register address is + * automatically incremented after every register read which allows convenient + * reading of multiple registers. Set INC to '0' when reading a single register. + */ +#define ADS7924_AUTO_INCREMENT_BIT BIT(7) + +#define ADS7924_MODECNTRL_MODE_MASK GENMASK(7, 2) + +#define ADS7924_MODECNTRL_SEL_MASK GENMASK(1, 0) + +#define ADS7924_CFG_INTPOL_BIT 1 +#define ADS7924_CFG_INTTRIG_BIT 0 + +#define ADS7924_CFG_INTPOL_MASK BIT(ADS7924_CFG_INTPOL_BIT) +#define ADS7924_CFG_INTTRIG_MASK BIT(ADS7924_CFG_INTTRIG_BIT) + +/* Interrupt pin polarity */ +#define ADS7924_CFG_INTPOL_LOW 0 +#define ADS7924_CFG_INTPOL_HIGH 1 + +/* Interrupt pin signaling */ +#define ADS7924_CFG_INTTRIG_LEVEL 0 +#define ADS7924_CFG_INTTRIG_EDGE 1 + +/* Mode control values */ +#define ADS7924_MODECNTRL_IDLE 0x00 +#define ADS7924_MODECNTRL_AWAKE 0x20 +#define ADS7924_MODECNTRL_MANUAL_SINGLE 0x30 +#define ADS7924_MODECNTRL_MANUAL_SCAN 0x32 +#define ADS7924_MODECNTRL_AUTO_SINGLE 0x31 +#define ADS7924_MODECNTRL_AUTO_SCAN 0x33 +#define ADS7924_MODECNTRL_AUTO_SINGLE_SLEEP 0x39 +#define ADS7924_MODECNTRL_AUTO_SCAN_SLEEP 0x3B +#define ADS7924_MODECNTRL_AUTO_BURST_SLEEP 0x3F + +#define ADS7924_ACQTIME_MASK GENMASK(4, 0) + +#define ADS7924_PWRUPTIME_MASK GENMASK(4, 0) + +/* + * The power-up time is allowed to elapse whenever the device has been shutdown + * in idle mode. Power-up time can allow external circuits, such as an + * operational amplifier, between the MUXOUT and ADCIN pins to turn on. + * The nominal time programmed by the PUTIME[4:0] register bits is given by: + * t PU = PWRUPTIME[4:0] × 2 μs + * If a power-up time is not required, set the bits to '0' to effectively bypass. + */ +#define ADS7924_PWRUPTIME_US 0 /* Bypass (0us). */ + +/* + * Acquisition Time according to ACQTIME[4:0] register bits. + * The Acquisition Time is given by: + * t ACQ = (ACQTIME[4:0] × 2 μs) + 6 μs + * Using default value of 0 for ACQTIME[4:0] results in a minimum acquisition + * time of 6us. + */ +#define ADS7924_ACQTIME_US 6 + +/* The conversion time is always 4μs and cannot be programmed by the user. */ +#define ADS7924_CONVTIME_US 4 + +#define ADS7924_TOTAL_CONVTIME_US (ADS7924_PWRUPTIME_US + ADS7924_ACQTIME_US + \ + ADS7924_CONVTIME_US) + +#define ADS7924_V_CHAN(_chan, _addr) { \ + .type = IIO_VOLTAGE, \ + .indexed = 1, \ + .channel = _chan, \ + .address = _addr, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ + .datasheet_name = "AIN"#_chan, \ +} + +struct ads7924_data { + struct device *dev; + struct regmap *regmap; + struct regulator *vref_reg; + + /* GPIO descriptor for device hard-reset pin. */ + struct gpio_desc *reset_gpio; + + /* + * Protects ADC ops, e.g: concurrent sysfs/buffered + * data reads, configuration updates + */ + struct mutex lock; + + /* + * Set to true when the ADC is switched to the continuous-conversion + * mode and exits from a power-down state. This flag is used to avoid + * getting the stale result from the conversion register. + */ + bool conv_invalid; +}; + +static bool ads7924_is_writeable_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case ADS7924_MODECNTRL_REG: + case ADS7924_INTCNTRL_REG: + case ADS7924_ULR0_REG: + case ADS7924_LLR0_REG: + case ADS7924_ULR1_REG: + case ADS7924_LLR1_REG: + case ADS7924_ULR2_REG: + case ADS7924_LLR2_REG: + case ADS7924_ULR3_REG: + case ADS7924_LLR3_REG: + case ADS7924_INTCONFIG_REG: + case ADS7924_SLPCONFIG_REG: + case ADS7924_ACQCONFIG_REG: + case ADS7924_PWRCONFIG_REG: + case ADS7924_RESET_REG: + return true; + default: + return false; + } +} + +static const struct regmap_config ads7924_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .max_register = ADS7924_RESET_REG, + .writeable_reg = ads7924_is_writeable_reg, +}; + +static const struct iio_chan_spec ads7924_channels[] = { + ADS7924_V_CHAN(0, ADS7924_DATA0_U_REG), + ADS7924_V_CHAN(1, ADS7924_DATA1_U_REG), + ADS7924_V_CHAN(2, ADS7924_DATA2_U_REG), + ADS7924_V_CHAN(3, ADS7924_DATA3_U_REG), +}; + +static int ads7924_get_adc_result(struct ads7924_data *data, + struct iio_chan_spec const *chan, int *val) +{ + int ret; + __be16 be_val; + + if (chan->channel < 0 || chan->channel >= ADS7924_CHANNELS) + return -EINVAL; + + guard(mutex)(&data->lock); + + if (data->conv_invalid) { + int conv_time; + + conv_time = ADS7924_TOTAL_CONVTIME_US; + /* Allow 10% for internal clock inaccuracy. */ + conv_time += conv_time / 10; + usleep_range(conv_time, conv_time + 1); + data->conv_invalid = false; + } + + ret = regmap_raw_read(data->regmap, ADS7924_AUTO_INCREMENT_BIT | + chan->address, &be_val, sizeof(be_val)); + if (ret) + return ret; + + *val = be16_to_cpu(be_val) >> ADS7924_DATA_SHIFT; + + return 0; +} + +static int ads7924_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int *val, + int *val2, long mask) +{ + int ret, vref_uv; + struct ads7924_data *data = iio_priv(indio_dev); + + switch (mask) { + case IIO_CHAN_INFO_RAW: + ret = ads7924_get_adc_result(data, chan, val); + if (ret < 0) + return ret; + + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + vref_uv = regulator_get_voltage(data->vref_reg); + if (vref_uv < 0) + return vref_uv; + + *val = vref_uv / 1000; /* Convert reg voltage to mV */ + *val2 = ADS7924_BITS; + return IIO_VAL_FRACTIONAL_LOG2; + default: + return -EINVAL; + } +} + +static const struct iio_info ads7924_info = { + .read_raw = ads7924_read_raw, +}; + +static int ads7924_get_channels_config(struct device *dev) +{ + struct fwnode_handle *node; + int num_channels = 0; + + device_for_each_child_node(dev, node) { + u32 pval; + unsigned int channel; + + if (fwnode_property_read_u32(node, "reg", &pval)) { + dev_err(dev, "invalid reg on %pfw\n", node); + continue; + } + + channel = pval; + if (channel >= ADS7924_CHANNELS) { + dev_err(dev, "invalid channel index %d on %pfw\n", + channel, node); + continue; + } + + num_channels++; + } + + if (!num_channels) + return -EINVAL; + + return 0; +} + +static int ads7924_set_conv_mode(struct ads7924_data *data, int mode) +{ + int ret; + unsigned int mode_field; + struct device *dev = data->dev; + + /* + * When switching between modes, be sure to first select the Awake mode + * and then switch to the desired mode. This procedure ensures the + * internal control logic is properly synchronized. + */ + if (mode != ADS7924_MODECNTRL_IDLE) { + mode_field = FIELD_PREP(ADS7924_MODECNTRL_MODE_MASK, + ADS7924_MODECNTRL_AWAKE); + + ret = regmap_update_bits(data->regmap, ADS7924_MODECNTRL_REG, + ADS7924_MODECNTRL_MODE_MASK, + mode_field); + if (ret) { + dev_err(dev, "failed to set awake mode (%pe)\n", + ERR_PTR(ret)); + return ret; + } + } + + mode_field = FIELD_PREP(ADS7924_MODECNTRL_MODE_MASK, mode); + + ret = regmap_update_bits(data->regmap, ADS7924_MODECNTRL_REG, + ADS7924_MODECNTRL_MODE_MASK, mode_field); + if (ret) + dev_err(dev, "failed to set mode %d (%pe)\n", mode, + ERR_PTR(ret)); + + return ret; +} + +static int ads7924_reset(struct iio_dev *indio_dev) +{ + struct ads7924_data *data = iio_priv(indio_dev); + + if (data->reset_gpio) { + gpiod_set_value(data->reset_gpio, 1); /* Assert. */ + /* Educated guess: assert time not specified in datasheet... */ + mdelay(100); + gpiod_set_value(data->reset_gpio, 0); /* Deassert. */ + return 0; + } + + /* + * A write of 10101010 to this register will generate a + * software reset of the ADS7924. + */ + return regmap_write(data->regmap, ADS7924_RESET_REG, 0b10101010); +}; + +static void ads7924_reg_disable(void *data) +{ + regulator_disable(data); +} + +static void ads7924_set_idle_mode(void *data) +{ + ads7924_set_conv_mode(data, ADS7924_MODECNTRL_IDLE); +} + +static int ads7924_probe(struct i2c_client *client) +{ + struct iio_dev *indio_dev; + struct ads7924_data *data; + struct device *dev = &client->dev; + int ret; + + indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data)); + if (!indio_dev) + return -ENOMEM; + + data = iio_priv(indio_dev); + + data->dev = dev; + + /* Initialize the reset GPIO as output with an initial value of 0. */ + data->reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW); + if (IS_ERR(data->reset_gpio)) + return dev_err_probe(dev, PTR_ERR(data->reset_gpio), + "failed to get request reset GPIO\n"); + + mutex_init(&data->lock); + + indio_dev->name = "ads7924"; + indio_dev->modes = INDIO_DIRECT_MODE; + + indio_dev->channels = ads7924_channels; + indio_dev->num_channels = ARRAY_SIZE(ads7924_channels); + indio_dev->info = &ads7924_info; + + ret = ads7924_get_channels_config(dev); + if (ret < 0) + return dev_err_probe(dev, ret, + "failed to get channels configuration\n"); + + data->regmap = devm_regmap_init_i2c(client, &ads7924_regmap_config); + if (IS_ERR(data->regmap)) + return dev_err_probe(dev, PTR_ERR(data->regmap), + "failed to init regmap\n"); + + data->vref_reg = devm_regulator_get(dev, "vref"); + if (IS_ERR(data->vref_reg)) + return dev_err_probe(dev, PTR_ERR(data->vref_reg), + "failed to get vref regulator\n"); + + ret = regulator_enable(data->vref_reg); + if (ret) + return dev_err_probe(dev, ret, + "failed to enable regulator\n"); + + ret = devm_add_action_or_reset(dev, ads7924_reg_disable, data->vref_reg); + if (ret) + return ret; + + ret = ads7924_reset(indio_dev); + if (ret < 0) + return dev_err_probe(dev, ret, + "failed to reset device\n"); + + ret = ads7924_set_conv_mode(data, ADS7924_MODECNTRL_AUTO_SCAN); + if (ret) + return dev_err_probe(dev, ret, + "failed to set conversion mode\n"); + + ret = devm_add_action_or_reset(dev, ads7924_set_idle_mode, data); + if (ret) + return ret; + + /* Use minimum signal acquire time. */ + ret = regmap_update_bits(data->regmap, ADS7924_ACQCONFIG_REG, + ADS7924_ACQTIME_MASK, + FIELD_PREP(ADS7924_ACQTIME_MASK, 0)); + if (ret < 0) + return dev_err_probe(dev, ret, + "failed to configure signal acquire time\n"); + + /* Disable power-up time. */ + ret = regmap_update_bits(data->regmap, ADS7924_PWRCONFIG_REG, + ADS7924_PWRUPTIME_MASK, + FIELD_PREP(ADS7924_PWRUPTIME_MASK, 0)); + if (ret < 0) + return dev_err_probe(dev, ret, + "failed to configure power-up time\n"); + + data->conv_invalid = true; + + ret = devm_iio_device_register(dev, indio_dev); + if (ret < 0) + return dev_err_probe(dev, ret, + "failed to register IIO device\n"); + + return 0; +} + +static const struct i2c_device_id ads7924_id[] = { + { .name = "ads7924" }, + { } +}; +MODULE_DEVICE_TABLE(i2c, ads7924_id); + +static const struct of_device_id ads7924_of_match[] = { + { .compatible = "ti,ads7924", }, + { } +}; +MODULE_DEVICE_TABLE(of, ads7924_of_match); + +static struct i2c_driver ads7924_driver = { + .driver = { + .name = "ads7924", + .of_match_table = ads7924_of_match, + }, + .probe = ads7924_probe, + .id_table = ads7924_id, +}; + +module_i2c_driver(ads7924_driver); + +MODULE_AUTHOR("Hugo Villeneuve "); +MODULE_DESCRIPTION("Texas Instruments ADS7924 ADC I2C driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/iio/adc/ti-ads7950.c b/drivers/iio/adc/ti-ads7950.c new file mode 100644 index 000000000..d27c1f288 --- /dev/null +++ b/drivers/iio/adc/ti-ads7950.c @@ -0,0 +1,667 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Texas Instruments ADS7950 SPI ADC driver + * + * Copyright 2016 David Lechner + * + * Based on iio/ad7923.c: + * Copyright 2011 Analog Devices Inc + * Copyright 2012 CS Systemes d'Information + * + * And also on hwmon/ads79xx.c + * Copyright (C) 2013 Texas Instruments Incorporated - https://www.ti.com/ + * Nishanth Menon + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +/* + * In case of ACPI, we use the 5000 mV as default for the reference pin. + * Device tree users encode that via the vref-supply regulator. + */ +#define TI_ADS7950_VA_MV_ACPI_DEFAULT 5000 + +#define TI_ADS7950_CR_GPIO BIT(14) +#define TI_ADS7950_CR_MANUAL BIT(12) +#define TI_ADS7950_CR_WRITE BIT(11) +#define TI_ADS7950_CR_CHAN(ch) ((ch) << 7) +#define TI_ADS7950_CR_RANGE_5V BIT(6) +#define TI_ADS7950_CR_GPIO_DATA BIT(4) + +#define TI_ADS7950_MAX_CHAN 16 +#define TI_ADS7950_NUM_GPIOS 4 + +/* val = value, dec = left shift, bits = number of bits of the mask */ +#define TI_ADS7950_EXTRACT(val, dec, bits) \ + (((val) >> (dec)) & ((1 << (bits)) - 1)) + +#define TI_ADS7950_MAN_CMD(cmd) (TI_ADS7950_CR_MANUAL | (cmd)) +#define TI_ADS7950_GPIO_CMD(cmd) (TI_ADS7950_CR_GPIO | (cmd)) + +/* Manual mode configuration */ +#define TI_ADS7950_MAN_CMD_SETTINGS(st) \ + (TI_ADS7950_MAN_CMD(TI_ADS7950_CR_WRITE | st->cmd_settings_bitmask)) +/* GPIO mode configuration */ +#define TI_ADS7950_GPIO_CMD_SETTINGS(st) \ + (TI_ADS7950_GPIO_CMD(st->gpio_cmd_settings_bitmask)) + +struct ti_ads7950_state { + struct spi_device *spi; + struct spi_transfer ring_xfer; + struct spi_transfer scan_single_xfer[3]; + struct spi_message ring_msg; + struct spi_message scan_single_msg; + + /* Lock to protect the spi xfer buffers */ + struct mutex slock; + struct gpio_chip chip; + + struct regulator *reg; + unsigned int vref_mv; + + /* + * Bitmask of lower 7 bits used for configuration + * These bits only can be written when TI_ADS7950_CR_WRITE + * is set, otherwise it retains its original state. + * [0-3] GPIO signal + * [4] Set following frame to return GPIO signal values + * [5] Powers down device + * [6] Sets Vref range1(2.5v) or range2(5v) + * + * Bits present on Manual/Auto1/Auto2 commands + */ + unsigned int cmd_settings_bitmask; + + /* + * Bitmask of GPIO command + * [0-3] GPIO direction + * [4-6] Different GPIO alarm mode configurations + * [7] GPIO 2 as device range input + * [8] GPIO 3 as device power down input + * [9] Reset all registers + * [10-11] N/A + */ + unsigned int gpio_cmd_settings_bitmask; + + /* + * DMA (thus cache coherency maintenance) may require the + * transfer buffers to live in their own cache lines. + */ + u16 rx_buf[TI_ADS7950_MAX_CHAN + 2] __aligned(IIO_DMA_MINALIGN); + u16 tx_buf[TI_ADS7950_MAX_CHAN + 2]; + u16 single_tx; + u16 single_rx; + +}; + +struct ti_ads7950_chip_info { + const struct iio_chan_spec *channels; + unsigned int num_channels; +}; + +#define TI_ADS7950_V_CHAN(index, bits) \ +{ \ + .type = IIO_VOLTAGE, \ + .indexed = 1, \ + .channel = index, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ + .address = index, \ + .datasheet_name = "CH##index", \ + .scan_index = index, \ + .scan_type = { \ + .sign = 'u', \ + .realbits = bits, \ + .storagebits = 16, \ + .shift = 12 - (bits), \ + .endianness = IIO_CPU, \ + }, \ +} + +#define DECLARE_TI_ADS7950_4_CHANNELS(name, bits) \ +const struct iio_chan_spec name ## _channels[] = { \ + TI_ADS7950_V_CHAN(0, bits), \ + TI_ADS7950_V_CHAN(1, bits), \ + TI_ADS7950_V_CHAN(2, bits), \ + TI_ADS7950_V_CHAN(3, bits), \ + IIO_CHAN_SOFT_TIMESTAMP(4), \ +} + +#define DECLARE_TI_ADS7950_8_CHANNELS(name, bits) \ +const struct iio_chan_spec name ## _channels[] = { \ + TI_ADS7950_V_CHAN(0, bits), \ + TI_ADS7950_V_CHAN(1, bits), \ + TI_ADS7950_V_CHAN(2, bits), \ + TI_ADS7950_V_CHAN(3, bits), \ + TI_ADS7950_V_CHAN(4, bits), \ + TI_ADS7950_V_CHAN(5, bits), \ + TI_ADS7950_V_CHAN(6, bits), \ + TI_ADS7950_V_CHAN(7, bits), \ + IIO_CHAN_SOFT_TIMESTAMP(8), \ +} + +#define DECLARE_TI_ADS7950_12_CHANNELS(name, bits) \ +const struct iio_chan_spec name ## _channels[] = { \ + TI_ADS7950_V_CHAN(0, bits), \ + TI_ADS7950_V_CHAN(1, bits), \ + TI_ADS7950_V_CHAN(2, bits), \ + TI_ADS7950_V_CHAN(3, bits), \ + TI_ADS7950_V_CHAN(4, bits), \ + TI_ADS7950_V_CHAN(5, bits), \ + TI_ADS7950_V_CHAN(6, bits), \ + TI_ADS7950_V_CHAN(7, bits), \ + TI_ADS7950_V_CHAN(8, bits), \ + TI_ADS7950_V_CHAN(9, bits), \ + TI_ADS7950_V_CHAN(10, bits), \ + TI_ADS7950_V_CHAN(11, bits), \ + IIO_CHAN_SOFT_TIMESTAMP(12), \ +} + +#define DECLARE_TI_ADS7950_16_CHANNELS(name, bits) \ +const struct iio_chan_spec name ## _channels[] = { \ + TI_ADS7950_V_CHAN(0, bits), \ + TI_ADS7950_V_CHAN(1, bits), \ + TI_ADS7950_V_CHAN(2, bits), \ + TI_ADS7950_V_CHAN(3, bits), \ + TI_ADS7950_V_CHAN(4, bits), \ + TI_ADS7950_V_CHAN(5, bits), \ + TI_ADS7950_V_CHAN(6, bits), \ + TI_ADS7950_V_CHAN(7, bits), \ + TI_ADS7950_V_CHAN(8, bits), \ + TI_ADS7950_V_CHAN(9, bits), \ + TI_ADS7950_V_CHAN(10, bits), \ + TI_ADS7950_V_CHAN(11, bits), \ + TI_ADS7950_V_CHAN(12, bits), \ + TI_ADS7950_V_CHAN(13, bits), \ + TI_ADS7950_V_CHAN(14, bits), \ + TI_ADS7950_V_CHAN(15, bits), \ + IIO_CHAN_SOFT_TIMESTAMP(16), \ +} + +static DECLARE_TI_ADS7950_4_CHANNELS(ti_ads7950, 12); +static DECLARE_TI_ADS7950_8_CHANNELS(ti_ads7951, 12); +static DECLARE_TI_ADS7950_12_CHANNELS(ti_ads7952, 12); +static DECLARE_TI_ADS7950_16_CHANNELS(ti_ads7953, 12); +static DECLARE_TI_ADS7950_4_CHANNELS(ti_ads7954, 10); +static DECLARE_TI_ADS7950_8_CHANNELS(ti_ads7955, 10); +static DECLARE_TI_ADS7950_12_CHANNELS(ti_ads7956, 10); +static DECLARE_TI_ADS7950_16_CHANNELS(ti_ads7957, 10); +static DECLARE_TI_ADS7950_4_CHANNELS(ti_ads7958, 8); +static DECLARE_TI_ADS7950_8_CHANNELS(ti_ads7959, 8); +static DECLARE_TI_ADS7950_12_CHANNELS(ti_ads7960, 8); +static DECLARE_TI_ADS7950_16_CHANNELS(ti_ads7961, 8); + +static const struct ti_ads7950_chip_info ti_ads7950_chip_info = { + .channels = ti_ads7950_channels, + .num_channels = ARRAY_SIZE(ti_ads7950_channels), +}; + +static const struct ti_ads7950_chip_info ti_ads7951_chip_info = { + .channels = ti_ads7951_channels, + .num_channels = ARRAY_SIZE(ti_ads7951_channels), +}; + +static const struct ti_ads7950_chip_info ti_ads7952_chip_info = { + .channels = ti_ads7952_channels, + .num_channels = ARRAY_SIZE(ti_ads7952_channels), +}; + +static const struct ti_ads7950_chip_info ti_ads7953_chip_info = { + .channels = ti_ads7953_channels, + .num_channels = ARRAY_SIZE(ti_ads7953_channels), +}; + +static const struct ti_ads7950_chip_info ti_ads7954_chip_info = { + .channels = ti_ads7954_channels, + .num_channels = ARRAY_SIZE(ti_ads7954_channels), +}; + +static const struct ti_ads7950_chip_info ti_ads7955_chip_info = { + .channels = ti_ads7955_channels, + .num_channels = ARRAY_SIZE(ti_ads7955_channels), +}; + +static const struct ti_ads7950_chip_info ti_ads7956_chip_info = { + .channels = ti_ads7956_channels, + .num_channels = ARRAY_SIZE(ti_ads7956_channels), +}; + +static const struct ti_ads7950_chip_info ti_ads7957_chip_info = { + .channels = ti_ads7957_channels, + .num_channels = ARRAY_SIZE(ti_ads7957_channels), +}; + +static const struct ti_ads7950_chip_info ti_ads7958_chip_info = { + .channels = ti_ads7958_channels, + .num_channels = ARRAY_SIZE(ti_ads7958_channels), +}; + +static const struct ti_ads7950_chip_info ti_ads7959_chip_info = { + .channels = ti_ads7959_channels, + .num_channels = ARRAY_SIZE(ti_ads7959_channels), +}; + +static const struct ti_ads7950_chip_info ti_ads7960_chip_info = { + .channels = ti_ads7960_channels, + .num_channels = ARRAY_SIZE(ti_ads7960_channels), +}; + +static const struct ti_ads7950_chip_info ti_ads7961_chip_info = { + .channels = ti_ads7961_channels, + .num_channels = ARRAY_SIZE(ti_ads7961_channels), +}; + +static irqreturn_t ti_ads7950_trigger_handler(int irq, void *p) +{ + struct iio_poll_func *pf = p; + struct iio_dev *indio_dev = pf->indio_dev; + struct ti_ads7950_state *st = iio_priv(indio_dev); + int ret; + + do { + guard(mutex)(&st->slock); + + ret = spi_sync(st->spi, &st->ring_msg); + if (ret) + break; + + iio_push_to_buffers_with_ts_unaligned(indio_dev, &st->rx_buf[2], + sizeof(*st->rx_buf) * + TI_ADS7950_MAX_CHAN, + iio_get_time_ns(indio_dev)); + } while (0); + + iio_trigger_notify_done(indio_dev->trig); + + return IRQ_HANDLED; +} + +static int ti_ads7950_scan_direct(struct iio_dev *indio_dev, unsigned int ch) +{ + struct ti_ads7950_state *st = iio_priv(indio_dev); + int ret, cmd; + + guard(mutex)(&st->slock); + + cmd = TI_ADS7950_MAN_CMD(TI_ADS7950_CR_CHAN(ch)); + st->single_tx = cmd; + + ret = spi_sync(st->spi, &st->scan_single_msg); + if (ret) + return ret; + + return st->single_rx; +} + +static unsigned int ti_ads7950_get_range(struct ti_ads7950_state *st) +{ + unsigned int vref = st->vref_mv; + + if (st->cmd_settings_bitmask & TI_ADS7950_CR_RANGE_5V) + vref *= 2; + + return vref; +} + +static int ti_ads7950_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long m) +{ + struct ti_ads7950_state *st = iio_priv(indio_dev); + int ret; + + switch (m) { + case IIO_CHAN_INFO_RAW: + ret = ti_ads7950_scan_direct(indio_dev, chan->address); + if (ret < 0) + return ret; + + if (chan->address != TI_ADS7950_EXTRACT(ret, 12, 4)) + return -EIO; + + *val = TI_ADS7950_EXTRACT(ret, chan->scan_type.shift, + chan->scan_type.realbits); + + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + *val = ti_ads7950_get_range(st); + *val2 = (1 << chan->scan_type.realbits) - 1; + + return IIO_VAL_FRACTIONAL; + } + + return -EINVAL; +} + +static const struct iio_info ti_ads7950_info = { + .read_raw = &ti_ads7950_read_raw, +}; + +static int ti_ads7950_buffer_preenable(struct iio_dev *indio_dev) +{ + struct ti_ads7950_state *st = iio_priv(indio_dev); + u32 len = 0; + u32 i; + u16 cmd; + + for_each_set_bit(i, indio_dev->active_scan_mask, indio_dev->num_channels) { + cmd = TI_ADS7950_MAN_CMD(TI_ADS7950_CR_CHAN(i)); + st->tx_buf[len++] = cmd; + } + + /* Data for the 1st channel is not returned until the 3rd transfer */ + st->tx_buf[len++] = 0; + st->tx_buf[len++] = 0; + + st->ring_xfer.len = len * 2; + + return spi_optimize_message(st->spi, &st->ring_msg); +} + +static int ti_ads7950_buffer_postdisable(struct iio_dev *indio_dev) +{ + struct ti_ads7950_state *st = iio_priv(indio_dev); + + spi_unoptimize_message(&st->ring_msg); + + return 0; +} + +static const struct iio_buffer_setup_ops ti_ads7950_buffer_setup_ops = { + .preenable = ti_ads7950_buffer_preenable, + .postdisable = ti_ads7950_buffer_postdisable, +}; + +static int ti_ads7950_set(struct gpio_chip *chip, unsigned int offset, + int value) +{ + struct ti_ads7950_state *st = gpiochip_get_data(chip); + + guard(mutex)(&st->slock); + + if (value) + st->cmd_settings_bitmask |= BIT(offset); + else + st->cmd_settings_bitmask &= ~BIT(offset); + + st->single_tx = TI_ADS7950_MAN_CMD_SETTINGS(st); + + return spi_sync(st->spi, &st->scan_single_msg); +} + +static int ti_ads7950_get(struct gpio_chip *chip, unsigned int offset) +{ + struct ti_ads7950_state *st = gpiochip_get_data(chip); + bool state; + int ret; + + guard(mutex)(&st->slock); + + /* If set as output, return the output */ + if (st->gpio_cmd_settings_bitmask & BIT(offset)) { + state = st->cmd_settings_bitmask & BIT(offset); + return state; + } + + /* GPIO data bit sets SDO bits 12-15 to GPIO input */ + st->cmd_settings_bitmask |= TI_ADS7950_CR_GPIO_DATA; + st->single_tx = TI_ADS7950_MAN_CMD_SETTINGS(st); + ret = spi_sync(st->spi, &st->scan_single_msg); + if (ret) + return ret; + + state = (st->single_rx >> 12) & BIT(offset); + + /* Revert back to original settings */ + st->cmd_settings_bitmask &= ~TI_ADS7950_CR_GPIO_DATA; + st->single_tx = TI_ADS7950_MAN_CMD_SETTINGS(st); + ret = spi_sync(st->spi, &st->scan_single_msg); + if (ret) + return ret; + + return state; +} + +static int ti_ads7950_get_direction(struct gpio_chip *chip, + unsigned int offset) +{ + struct ti_ads7950_state *st = gpiochip_get_data(chip); + + /* Bitmask is inverted from GPIO framework 0=input/1=output */ + return !(st->gpio_cmd_settings_bitmask & BIT(offset)); +} + +static int _ti_ads7950_set_direction(struct gpio_chip *chip, int offset, + int input) +{ + struct ti_ads7950_state *st = gpiochip_get_data(chip); + + guard(mutex)(&st->slock); + + /* Only change direction if needed */ + if (input && (st->gpio_cmd_settings_bitmask & BIT(offset))) + st->gpio_cmd_settings_bitmask &= ~BIT(offset); + else if (!input && !(st->gpio_cmd_settings_bitmask & BIT(offset))) + st->gpio_cmd_settings_bitmask |= BIT(offset); + else + return 0; + + st->single_tx = TI_ADS7950_GPIO_CMD_SETTINGS(st); + + return spi_sync(st->spi, &st->scan_single_msg); +} + +static int ti_ads7950_direction_input(struct gpio_chip *chip, + unsigned int offset) +{ + return _ti_ads7950_set_direction(chip, offset, 1); +} + +static int ti_ads7950_direction_output(struct gpio_chip *chip, + unsigned int offset, int value) +{ + int ret; + + ret = ti_ads7950_set(chip, offset, value); + if (ret) + return ret; + + return _ti_ads7950_set_direction(chip, offset, 0); +} + +static int ti_ads7950_init_hw(struct ti_ads7950_state *st) +{ + int ret; + + guard(mutex)(&st->slock); + + /* Settings for Manual/Auto1/Auto2 commands */ + /* Default to 5v ref */ + st->cmd_settings_bitmask = TI_ADS7950_CR_RANGE_5V; + st->single_tx = TI_ADS7950_MAN_CMD_SETTINGS(st); + ret = spi_sync(st->spi, &st->scan_single_msg); + if (ret) + return ret; + + /* Settings for GPIO command */ + st->gpio_cmd_settings_bitmask = 0x0; + st->single_tx = TI_ADS7950_GPIO_CMD_SETTINGS(st); + return spi_sync(st->spi, &st->scan_single_msg); +} + +static int ti_ads7950_probe(struct spi_device *spi) +{ + struct ti_ads7950_state *st; + struct iio_dev *indio_dev; + const struct ti_ads7950_chip_info *info; + int ret; + + spi->bits_per_word = 16; + spi->mode |= SPI_CS_WORD; + ret = spi_setup(spi); + if (ret) + return dev_err_probe(&spi->dev, ret, "Error in spi setup\n"); + + indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st)); + if (!indio_dev) + return -ENOMEM; + + st = iio_priv(indio_dev); + + st->spi = spi; + + info = spi_get_device_match_data(spi); + + indio_dev->name = spi_get_device_id(spi)->name; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = info->channels; + indio_dev->num_channels = info->num_channels; + indio_dev->info = &ti_ads7950_info; + + /* build spi ring message */ + spi_message_init(&st->ring_msg); + + st->ring_xfer.tx_buf = &st->tx_buf[0]; + st->ring_xfer.rx_buf = &st->rx_buf[0]; + /* len will be set later */ + + spi_message_add_tail(&st->ring_xfer, &st->ring_msg); + + /* + * Setup default message. The sample is read at the end of the first + * transfer, then it takes one full cycle to convert the sample and one + * more cycle to send the value. The conversion process is driven by + * the SPI clock, which is why we have 3 transfers. The middle one is + * just dummy data sent while the chip is converting the sample that + * was read at the end of the first transfer. + */ + + st->scan_single_xfer[0].tx_buf = &st->single_tx; + st->scan_single_xfer[0].len = 2; + st->scan_single_xfer[0].cs_change = 1; + st->scan_single_xfer[1].tx_buf = &st->single_tx; + st->scan_single_xfer[1].len = 2; + st->scan_single_xfer[1].cs_change = 1; + st->scan_single_xfer[2].rx_buf = &st->single_rx; + st->scan_single_xfer[2].len = 2; + + spi_message_init_with_transfers(&st->scan_single_msg, + st->scan_single_xfer, 3); + + ret = devm_mutex_init(&spi->dev, &st->slock); + if (ret) + return ret; + + /* Use hard coded value for reference voltage in ACPI case */ + if (ACPI_COMPANION(&spi->dev)) { + st->vref_mv = TI_ADS7950_VA_MV_ACPI_DEFAULT; + } else { + ret = devm_regulator_get_enable_read_voltage(&spi->dev, "vref"); + if (ret < 0) + return dev_err_probe(&spi->dev, ret, + "Failed to get regulator \"vref\"\n"); + + st->vref_mv = ret / 1000; + } + + ret = devm_iio_triggered_buffer_setup(&spi->dev, indio_dev, NULL, + &ti_ads7950_trigger_handler, + &ti_ads7950_buffer_setup_ops); + if (ret) + return dev_err_probe(&spi->dev, ret, + "Failed to setup triggered buffer\n"); + + ret = ti_ads7950_init_hw(st); + if (ret) + return dev_err_probe(&spi->dev, ret, + "Failed to init adc chip\n"); + + ret = devm_iio_device_register(&spi->dev, indio_dev); + if (ret) + return dev_err_probe(&spi->dev, ret, + "Failed to register iio device\n"); + + /* Add GPIO chip */ + st->chip.label = dev_name(&st->spi->dev); + st->chip.parent = &st->spi->dev; + st->chip.owner = THIS_MODULE; + st->chip.can_sleep = true; + st->chip.base = -1; + st->chip.ngpio = TI_ADS7950_NUM_GPIOS; + st->chip.get_direction = ti_ads7950_get_direction; + st->chip.direction_input = ti_ads7950_direction_input; + st->chip.direction_output = ti_ads7950_direction_output; + st->chip.get = ti_ads7950_get; + st->chip.set = ti_ads7950_set; + + ret = devm_gpiochip_add_data(&spi->dev, &st->chip, st); + if (ret) + return dev_err_probe(&spi->dev, ret, + "Failed to init GPIOs\n"); + + return 0; +} + +static const struct spi_device_id ti_ads7950_id[] = { + { .name = "ads7950", .driver_data = (kernel_ulong_t)&ti_ads7950_chip_info }, + { .name = "ads7951", .driver_data = (kernel_ulong_t)&ti_ads7951_chip_info }, + { .name = "ads7952", .driver_data = (kernel_ulong_t)&ti_ads7952_chip_info }, + { .name = "ads7953", .driver_data = (kernel_ulong_t)&ti_ads7953_chip_info }, + { .name = "ads7954", .driver_data = (kernel_ulong_t)&ti_ads7954_chip_info }, + { .name = "ads7955", .driver_data = (kernel_ulong_t)&ti_ads7955_chip_info }, + { .name = "ads7956", .driver_data = (kernel_ulong_t)&ti_ads7956_chip_info }, + { .name = "ads7957", .driver_data = (kernel_ulong_t)&ti_ads7957_chip_info }, + { .name = "ads7958", .driver_data = (kernel_ulong_t)&ti_ads7958_chip_info }, + { .name = "ads7959", .driver_data = (kernel_ulong_t)&ti_ads7959_chip_info }, + { .name = "ads7960", .driver_data = (kernel_ulong_t)&ti_ads7960_chip_info }, + { .name = "ads7961", .driver_data = (kernel_ulong_t)&ti_ads7961_chip_info }, + { } +}; +MODULE_DEVICE_TABLE(spi, ti_ads7950_id); + +static const struct of_device_id ads7950_of_table[] = { + { .compatible = "ti,ads7950", .data = &ti_ads7950_chip_info }, + { .compatible = "ti,ads7951", .data = &ti_ads7951_chip_info }, + { .compatible = "ti,ads7952", .data = &ti_ads7952_chip_info }, + { .compatible = "ti,ads7953", .data = &ti_ads7953_chip_info }, + { .compatible = "ti,ads7954", .data = &ti_ads7954_chip_info }, + { .compatible = "ti,ads7955", .data = &ti_ads7955_chip_info }, + { .compatible = "ti,ads7956", .data = &ti_ads7956_chip_info }, + { .compatible = "ti,ads7957", .data = &ti_ads7957_chip_info }, + { .compatible = "ti,ads7958", .data = &ti_ads7958_chip_info }, + { .compatible = "ti,ads7959", .data = &ti_ads7959_chip_info }, + { .compatible = "ti,ads7960", .data = &ti_ads7960_chip_info }, + { .compatible = "ti,ads7961", .data = &ti_ads7961_chip_info }, + { } +}; +MODULE_DEVICE_TABLE(of, ads7950_of_table); + +static struct spi_driver ti_ads7950_driver = { + .driver = { + .name = "ads7950", + .of_match_table = ads7950_of_table, + }, + .probe = ti_ads7950_probe, + .id_table = ti_ads7950_id, +}; +module_spi_driver(ti_ads7950_driver); + +MODULE_AUTHOR("David Lechner "); +MODULE_DESCRIPTION("TI TI_ADS7950 ADC"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/adc/ti-ads8344.c b/drivers/iio/adc/ti-ads8344.c new file mode 100644 index 000000000..3bec8a2e6 --- /dev/null +++ b/drivers/iio/adc/ti-ads8344.c @@ -0,0 +1,193 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * ADS8344 16-bit 8-Channel ADC driver + * + * Author: Gregory CLEMENT + * + * Datasheet: https://www.ti.com/lit/ds/symlink/ads8344.pdf + */ + +#include +#include +#include +#include +#include +#include + +#define ADS8344_START BIT(7) +#define ADS8344_SINGLE_END BIT(2) +#define ADS8344_CHANNEL(channel) ((channel) << 4) +#define ADS8344_CLOCK_INTERNAL 0x2 /* PD1 = 1 and PD0 = 0 */ + +struct ads8344 { + struct spi_device *spi; + struct regulator *reg; + /* + * Lock protecting access to adc->tx_buff and rx_buff, + * especially from concurrent read on sysfs file. + */ + struct mutex lock; + + u8 tx_buf __aligned(IIO_DMA_MINALIGN); + u8 rx_buf[3]; +}; + +#define ADS8344_VOLTAGE_CHANNEL(chan, addr) \ + { \ + .type = IIO_VOLTAGE, \ + .indexed = 1, \ + .channel = chan, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ + .address = addr, \ + } + +#define ADS8344_VOLTAGE_CHANNEL_DIFF(chan1, chan2, addr) \ + { \ + .type = IIO_VOLTAGE, \ + .indexed = 1, \ + .channel = (chan1), \ + .channel2 = (chan2), \ + .differential = 1, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ + .address = addr, \ + } + +static const struct iio_chan_spec ads8344_channels[] = { + ADS8344_VOLTAGE_CHANNEL(0, 0), + ADS8344_VOLTAGE_CHANNEL(1, 4), + ADS8344_VOLTAGE_CHANNEL(2, 1), + ADS8344_VOLTAGE_CHANNEL(3, 5), + ADS8344_VOLTAGE_CHANNEL(4, 2), + ADS8344_VOLTAGE_CHANNEL(5, 6), + ADS8344_VOLTAGE_CHANNEL(6, 3), + ADS8344_VOLTAGE_CHANNEL(7, 7), + ADS8344_VOLTAGE_CHANNEL_DIFF(0, 1, 8), + ADS8344_VOLTAGE_CHANNEL_DIFF(2, 3, 9), + ADS8344_VOLTAGE_CHANNEL_DIFF(4, 5, 10), + ADS8344_VOLTAGE_CHANNEL_DIFF(6, 7, 11), + ADS8344_VOLTAGE_CHANNEL_DIFF(1, 0, 12), + ADS8344_VOLTAGE_CHANNEL_DIFF(3, 2, 13), + ADS8344_VOLTAGE_CHANNEL_DIFF(5, 4, 14), + ADS8344_VOLTAGE_CHANNEL_DIFF(7, 6, 15), +}; + +static int ads8344_adc_conversion(struct ads8344 *adc, int channel, + bool differential) +{ + struct spi_device *spi = adc->spi; + int ret; + + adc->tx_buf = ADS8344_START; + if (!differential) + adc->tx_buf |= ADS8344_SINGLE_END; + adc->tx_buf |= ADS8344_CHANNEL(channel); + adc->tx_buf |= ADS8344_CLOCK_INTERNAL; + + ret = spi_write(spi, &adc->tx_buf, 1); + if (ret) + return ret; + + udelay(9); + + ret = spi_read(spi, adc->rx_buf, sizeof(adc->rx_buf)); + if (ret) + return ret; + + return adc->rx_buf[0] << 9 | adc->rx_buf[1] << 1 | adc->rx_buf[2] >> 7; +} + +static int ads8344_read_raw(struct iio_dev *iio, + struct iio_chan_spec const *channel, int *value, + int *shift, long mask) +{ + struct ads8344 *adc = iio_priv(iio); + + switch (mask) { + case IIO_CHAN_INFO_RAW: + mutex_lock(&adc->lock); + *value = ads8344_adc_conversion(adc, channel->address, + channel->differential); + mutex_unlock(&adc->lock); + if (*value < 0) + return *value; + + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + *value = regulator_get_voltage(adc->reg); + if (*value < 0) + return *value; + + /* convert regulator output voltage to mV */ + *value /= 1000; + *shift = 16; + + return IIO_VAL_FRACTIONAL_LOG2; + default: + return -EINVAL; + } +} + +static const struct iio_info ads8344_info = { + .read_raw = ads8344_read_raw, +}; + +static void ads8344_reg_disable(void *data) +{ + regulator_disable(data); +} + +static int ads8344_probe(struct spi_device *spi) +{ + struct iio_dev *indio_dev; + struct ads8344 *adc; + int ret; + + indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*adc)); + if (!indio_dev) + return -ENOMEM; + + adc = iio_priv(indio_dev); + adc->spi = spi; + mutex_init(&adc->lock); + + indio_dev->name = dev_name(&spi->dev); + indio_dev->info = &ads8344_info; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = ads8344_channels; + indio_dev->num_channels = ARRAY_SIZE(ads8344_channels); + + adc->reg = devm_regulator_get(&spi->dev, "vref"); + if (IS_ERR(adc->reg)) + return PTR_ERR(adc->reg); + + ret = regulator_enable(adc->reg); + if (ret) + return ret; + + ret = devm_add_action_or_reset(&spi->dev, ads8344_reg_disable, adc->reg); + if (ret) + return ret; + + return devm_iio_device_register(&spi->dev, indio_dev); +} + +static const struct of_device_id ads8344_of_match[] = { + { .compatible = "ti,ads8344", }, + { } +}; +MODULE_DEVICE_TABLE(of, ads8344_of_match); + +static struct spi_driver ads8344_driver = { + .driver = { + .name = "ads8344", + .of_match_table = ads8344_of_match, + }, + .probe = ads8344_probe, +}; +module_spi_driver(ads8344_driver); + +MODULE_AUTHOR("Gregory CLEMENT "); +MODULE_DESCRIPTION("ADS8344 driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/iio/adc/ti-ads8688.c b/drivers/iio/adc/ti-ads8688.c new file mode 100644 index 000000000..8ad070b5e --- /dev/null +++ b/drivers/iio/adc/ti-ads8688.c @@ -0,0 +1,481 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2015 Prevas A/S + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#define ADS8688_CMD_REG(x) (x << 8) +#define ADS8688_CMD_REG_NOOP 0x00 +#define ADS8688_CMD_REG_RST 0x85 +#define ADS8688_CMD_REG_MAN_CH(chan) (0xC0 | (4 * chan)) +#define ADS8688_CMD_DONT_CARE_BITS 16 + +#define ADS8688_PROG_REG(x) (x << 9) +#define ADS8688_PROG_REG_RANGE_CH(chan) (0x05 + chan) +#define ADS8688_PROG_WR_BIT BIT(8) +#define ADS8688_PROG_DONT_CARE_BITS 8 + +#define ADS8688_REG_PLUSMINUS25VREF 0 +#define ADS8688_REG_PLUSMINUS125VREF 1 +#define ADS8688_REG_PLUSMINUS0625VREF 2 +#define ADS8688_REG_PLUS25VREF 5 +#define ADS8688_REG_PLUS125VREF 6 + +#define ADS8688_VREF_MV 4096 +#define ADS8688_REALBITS 16 +#define ADS8688_MAX_CHANNELS 8 + +/* + * enum ads8688_range - ADS8688 reference voltage range + * @ADS8688_PLUSMINUS25VREF: Device is configured for input range ±2.5 * VREF + * @ADS8688_PLUSMINUS125VREF: Device is configured for input range ±1.25 * VREF + * @ADS8688_PLUSMINUS0625VREF: Device is configured for input range ±0.625 * VREF + * @ADS8688_PLUS25VREF: Device is configured for input range 0 - 2.5 * VREF + * @ADS8688_PLUS125VREF: Device is configured for input range 0 - 1.25 * VREF + */ +enum ads8688_range { + ADS8688_PLUSMINUS25VREF, + ADS8688_PLUSMINUS125VREF, + ADS8688_PLUSMINUS0625VREF, + ADS8688_PLUS25VREF, + ADS8688_PLUS125VREF, +}; + +struct ads8688_chip_info { + const struct iio_chan_spec *channels; + unsigned int num_channels; +}; + +struct ads8688_state { + struct mutex lock; + const struct ads8688_chip_info *chip_info; + struct spi_device *spi; + unsigned int vref_mv; + int scale_avail[3][2]; + enum ads8688_range range[8]; + union { + __be32 d32; + u8 d8[4]; + } data[2] __aligned(IIO_DMA_MINALIGN); +}; + +enum ads8688_id { + ID_ADS8684, + ID_ADS8688, +}; + +struct ads8688_ranges { + enum ads8688_range range; + unsigned int scale; + int offset; + u8 reg; +}; + +static const struct ads8688_ranges ads8688_range_def[5] = { + { + .range = ADS8688_PLUSMINUS25VREF, + .scale = 76295, + .offset = -(1 << (ADS8688_REALBITS - 1)), + .reg = ADS8688_REG_PLUSMINUS25VREF, + }, { + .range = ADS8688_PLUSMINUS125VREF, + .scale = 38148, + .offset = -(1 << (ADS8688_REALBITS - 1)), + .reg = ADS8688_REG_PLUSMINUS125VREF, + }, { + .range = ADS8688_PLUSMINUS0625VREF, + .scale = 19074, + .offset = -(1 << (ADS8688_REALBITS - 1)), + .reg = ADS8688_REG_PLUSMINUS0625VREF, + }, { + .range = ADS8688_PLUS25VREF, + .scale = 38148, + .offset = 0, + .reg = ADS8688_REG_PLUS25VREF, + }, { + .range = ADS8688_PLUS125VREF, + .scale = 19074, + .offset = 0, + .reg = ADS8688_REG_PLUS125VREF, + } +}; + +static const int ads8688_offset_avail[] = { + -(1 << (ADS8688_REALBITS - 1)), + 0 +}; + +#define ADS8688_CHAN(index) \ +{ \ + .type = IIO_VOLTAGE, \ + .indexed = 1, \ + .channel = index, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) \ + | BIT(IIO_CHAN_INFO_SCALE) \ + | BIT(IIO_CHAN_INFO_OFFSET), \ + .info_mask_shared_by_type_available = \ + BIT(IIO_CHAN_INFO_SCALE) \ + | BIT(IIO_CHAN_INFO_OFFSET), \ + .scan_index = index, \ + .scan_type = { \ + .sign = 'u', \ + .realbits = 16, \ + .storagebits = 16, \ + .endianness = IIO_BE, \ + }, \ +} + +static const struct iio_chan_spec ads8684_channels[] = { + ADS8688_CHAN(0), + ADS8688_CHAN(1), + ADS8688_CHAN(2), + ADS8688_CHAN(3), +}; + +static const struct iio_chan_spec ads8688_channels[] = { + ADS8688_CHAN(0), + ADS8688_CHAN(1), + ADS8688_CHAN(2), + ADS8688_CHAN(3), + ADS8688_CHAN(4), + ADS8688_CHAN(5), + ADS8688_CHAN(6), + ADS8688_CHAN(7), +}; + +static int ads8688_prog_write(struct iio_dev *indio_dev, unsigned int addr, + unsigned int val) +{ + struct ads8688_state *st = iio_priv(indio_dev); + u32 tmp; + + tmp = ADS8688_PROG_REG(addr) | ADS8688_PROG_WR_BIT | val; + tmp <<= ADS8688_PROG_DONT_CARE_BITS; + st->data[0].d32 = cpu_to_be32(tmp); + + return spi_write(st->spi, &st->data[0].d8[1], 3); +} + +static int ads8688_reset(struct iio_dev *indio_dev) +{ + struct ads8688_state *st = iio_priv(indio_dev); + u32 tmp; + + tmp = ADS8688_CMD_REG(ADS8688_CMD_REG_RST); + tmp <<= ADS8688_CMD_DONT_CARE_BITS; + st->data[0].d32 = cpu_to_be32(tmp); + + return spi_write(st->spi, &st->data[0].d8[0], 4); +} + +static int ads8688_read(struct iio_dev *indio_dev, unsigned int chan) +{ + struct ads8688_state *st = iio_priv(indio_dev); + int ret; + u32 tmp; + struct spi_transfer t[] = { + { + .tx_buf = &st->data[0].d8[0], + .len = 4, + .cs_change = 1, + }, { + .tx_buf = &st->data[1].d8[0], + .rx_buf = &st->data[1].d8[0], + .len = 4, + }, + }; + + tmp = ADS8688_CMD_REG(ADS8688_CMD_REG_MAN_CH(chan)); + tmp <<= ADS8688_CMD_DONT_CARE_BITS; + st->data[0].d32 = cpu_to_be32(tmp); + + tmp = ADS8688_CMD_REG(ADS8688_CMD_REG_NOOP); + tmp <<= ADS8688_CMD_DONT_CARE_BITS; + st->data[1].d32 = cpu_to_be32(tmp); + + ret = spi_sync_transfer(st->spi, t, ARRAY_SIZE(t)); + if (ret < 0) + return ret; + + return be32_to_cpu(st->data[1].d32) & 0xffff; +} + +static int ads8688_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long m) +{ + int ret, offset; + unsigned long scale_mv; + + struct ads8688_state *st = iio_priv(indio_dev); + + mutex_lock(&st->lock); + switch (m) { + case IIO_CHAN_INFO_RAW: + ret = ads8688_read(indio_dev, chan->channel); + mutex_unlock(&st->lock); + if (ret < 0) + return ret; + *val = ret; + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + scale_mv = st->vref_mv; + scale_mv *= ads8688_range_def[st->range[chan->channel]].scale; + *val = 0; + *val2 = scale_mv; + mutex_unlock(&st->lock); + return IIO_VAL_INT_PLUS_NANO; + case IIO_CHAN_INFO_OFFSET: + offset = ads8688_range_def[st->range[chan->channel]].offset; + *val = offset; + mutex_unlock(&st->lock); + return IIO_VAL_INT; + } + mutex_unlock(&st->lock); + + return -EINVAL; +} + +static int ads8688_write_reg_range(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + enum ads8688_range range) +{ + unsigned int tmp; + + tmp = ADS8688_PROG_REG_RANGE_CH(chan->channel); + + return ads8688_prog_write(indio_dev, tmp, range); +} + +static int ads8688_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, int val2, long mask) +{ + struct ads8688_state *st = iio_priv(indio_dev); + unsigned int scale = 0; + int ret = -EINVAL, i, offset = 0; + + mutex_lock(&st->lock); + switch (mask) { + case IIO_CHAN_INFO_SCALE: + /* If the offset is 0 the ±2.5 * VREF mode is not available */ + offset = ads8688_range_def[st->range[chan->channel]].offset; + if (offset == 0 && val2 == ads8688_range_def[0].scale * st->vref_mv) { + mutex_unlock(&st->lock); + return -EINVAL; + } + + /* Lookup new mode */ + for (i = 0; i < ARRAY_SIZE(ads8688_range_def); i++) + if (val2 == ads8688_range_def[i].scale * st->vref_mv && + offset == ads8688_range_def[i].offset) { + ret = ads8688_write_reg_range(indio_dev, chan, + ads8688_range_def[i].reg); + break; + } + break; + case IIO_CHAN_INFO_OFFSET: + /* + * There are only two available offsets: + * 0 and -(1 << (ADS8688_REALBITS - 1)) + */ + if (!(ads8688_range_def[0].offset == val || + ads8688_range_def[3].offset == val)) { + mutex_unlock(&st->lock); + return -EINVAL; + } + + /* + * If the device are in ±2.5 * VREF mode, it's not allowed to + * switch to a mode where the offset is 0 + */ + if (val == 0 && + st->range[chan->channel] == ADS8688_PLUSMINUS25VREF) { + mutex_unlock(&st->lock); + return -EINVAL; + } + + scale = ads8688_range_def[st->range[chan->channel]].scale; + + /* Lookup new mode */ + for (i = 0; i < ARRAY_SIZE(ads8688_range_def); i++) + if (val == ads8688_range_def[i].offset && + scale == ads8688_range_def[i].scale) { + ret = ads8688_write_reg_range(indio_dev, chan, + ads8688_range_def[i].reg); + break; + } + break; + } + + if (!ret) + st->range[chan->channel] = ads8688_range_def[i].range; + + mutex_unlock(&st->lock); + + return ret; +} + +static int ads8688_write_raw_get_fmt(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + long mask) +{ + switch (mask) { + case IIO_CHAN_INFO_SCALE: + return IIO_VAL_INT_PLUS_NANO; + case IIO_CHAN_INFO_OFFSET: + return IIO_VAL_INT; + } + + return -EINVAL; +} + +static int ads8688_read_avail(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + const int **vals, int *type, int *length, + long mask) +{ + struct ads8688_state *st = iio_priv(indio_dev); + + switch (mask) { + case IIO_CHAN_INFO_SCALE: + *vals = (const int *)st->scale_avail; + *type = IIO_VAL_INT_PLUS_NANO; + *length = ARRAY_SIZE(st->scale_avail) * 2; + return IIO_AVAIL_LIST; + case IIO_CHAN_INFO_OFFSET: + *vals = ads8688_offset_avail; + *type = IIO_VAL_INT; + *length = ARRAY_SIZE(ads8688_offset_avail); + return IIO_AVAIL_LIST; + default: + return -EINVAL; + } +} + +static const struct iio_info ads8688_info = { + .read_raw = &ads8688_read_raw, + .read_avail = &ads8688_read_avail, + .write_raw = &ads8688_write_raw, + .write_raw_get_fmt = &ads8688_write_raw_get_fmt, +}; + +static irqreturn_t ads8688_trigger_handler(int irq, void *p) +{ + struct iio_poll_func *pf = p; + struct iio_dev *indio_dev = pf->indio_dev; + /* Ensure naturally aligned timestamp */ + u16 buffer[ADS8688_MAX_CHANNELS + sizeof(s64)/sizeof(u16)] __aligned(8) = { }; + int i, j = 0; + + iio_for_each_active_channel(indio_dev, i) { + buffer[j] = ads8688_read(indio_dev, i); + j++; + } + + iio_push_to_buffers_with_ts(indio_dev, buffer, sizeof(buffer), + iio_get_time_ns(indio_dev)); + + iio_trigger_notify_done(indio_dev->trig); + + return IRQ_HANDLED; +} + +static const struct ads8688_chip_info ads8688_chip_info_tbl[] = { + [ID_ADS8684] = { + .channels = ads8684_channels, + .num_channels = ARRAY_SIZE(ads8684_channels), + }, + [ID_ADS8688] = { + .channels = ads8688_channels, + .num_channels = ARRAY_SIZE(ads8688_channels), + }, +}; + +static int ads8688_probe(struct spi_device *spi) +{ + struct ads8688_state *st; + struct iio_dev *indio_dev; + int ret; + + indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st)); + if (indio_dev == NULL) + return -ENOMEM; + + st = iio_priv(indio_dev); + + ret = devm_regulator_get_enable_read_voltage(&spi->dev, "vref"); + if (ret < 0 && ret != -ENODEV) + return ret; + + st->vref_mv = ret == -ENODEV ? ADS8688_VREF_MV : ret / 1000; + + for (unsigned int i = 0; i < ARRAY_SIZE(st->scale_avail); i++) { + st->scale_avail[i][0] = 0; + st->scale_avail[i][1] = ads8688_range_def[i].scale * st->vref_mv; + } + + st->chip_info = &ads8688_chip_info_tbl[spi_get_device_id(spi)->driver_data]; + + spi->mode = SPI_MODE_1; + + st->spi = spi; + + indio_dev->name = spi_get_device_id(spi)->name; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = st->chip_info->channels; + indio_dev->num_channels = st->chip_info->num_channels; + indio_dev->info = &ads8688_info; + + ads8688_reset(indio_dev); + + mutex_init(&st->lock); + + ret = devm_iio_triggered_buffer_setup(&spi->dev, indio_dev, NULL, + ads8688_trigger_handler, NULL); + if (ret < 0) + return dev_err_probe(&spi->dev, ret, + "iio triggered buffer setup failed\n"); + + return devm_iio_device_register(&spi->dev, indio_dev); +} + +static const struct spi_device_id ads8688_id[] = { + { .name = "ads8684", .driver_data = ID_ADS8684 }, + { .name = "ads8688", .driver_data = ID_ADS8688 }, + { } +}; +MODULE_DEVICE_TABLE(spi, ads8688_id); + +static const struct of_device_id ads8688_of_match[] = { + { .compatible = "ti,ads8684" }, + { .compatible = "ti,ads8688" }, + { } +}; +MODULE_DEVICE_TABLE(of, ads8688_of_match); + +static struct spi_driver ads8688_driver = { + .driver = { + .name = "ads8688", + .of_match_table = ads8688_of_match, + }, + .probe = ads8688_probe, + .id_table = ads8688_id, +}; +module_spi_driver(ads8688_driver); + +MODULE_AUTHOR("Sean Nyekjaer "); +MODULE_DESCRIPTION("Texas Instruments ADS8688 driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/adc/ti-lmp92064.c b/drivers/iio/adc/ti-lmp92064.c new file mode 100644 index 000000000..d248bbea0 --- /dev/null +++ b/drivers/iio/adc/ti-lmp92064.c @@ -0,0 +1,383 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Texas Instruments LMP92064 SPI ADC driver + * + * Copyright (c) 2022 Leonard Göhrs , Pengutronix + * + * Based on linux/drivers/iio/adc/ti-tsc2046.c + * Copyright (c) 2021 Oleksij Rempel , Pengutronix + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#define TI_LMP92064_REG_CONFIG_A 0x0000 +#define TI_LMP92064_REG_CONFIG_B 0x0001 +#define TI_LMP92064_REG_CHIP_REV 0x0006 + +#define TI_LMP92064_REG_MFR_ID1 0x000C +#define TI_LMP92064_REG_MFR_ID2 0x000D + +#define TI_LMP92064_REG_REG_UPDATE 0x000F +#define TI_LMP92064_REG_CONFIG_REG 0x0100 +#define TI_LMP92064_REG_STATUS 0x0103 + +#define TI_LMP92064_REG_DATA_VOUT_LSB 0x0200 +#define TI_LMP92064_REG_DATA_VOUT_MSB 0x0201 +#define TI_LMP92064_REG_DATA_COUT_LSB 0x0202 +#define TI_LMP92064_REG_DATA_COUT_MSB 0x0203 + +#define TI_LMP92064_VAL_CONFIG_A 0x99 +#define TI_LMP92064_VAL_CONFIG_B 0x00 +#define TI_LMP92064_VAL_STATUS_OK 0x01 + +/* + * Channel number definitions for the two channels of the device + * - IN Current (INC) + * - IN Voltage (INV) + */ +#define TI_LMP92064_CHAN_INC 0 +#define TI_LMP92064_CHAN_INV 1 + +static const struct regmap_range lmp92064_readable_reg_ranges[] = { + regmap_reg_range(TI_LMP92064_REG_CONFIG_A, TI_LMP92064_REG_CHIP_REV), + regmap_reg_range(TI_LMP92064_REG_MFR_ID1, TI_LMP92064_REG_MFR_ID2), + regmap_reg_range(TI_LMP92064_REG_REG_UPDATE, TI_LMP92064_REG_REG_UPDATE), + regmap_reg_range(TI_LMP92064_REG_CONFIG_REG, TI_LMP92064_REG_CONFIG_REG), + regmap_reg_range(TI_LMP92064_REG_STATUS, TI_LMP92064_REG_STATUS), + regmap_reg_range(TI_LMP92064_REG_DATA_VOUT_LSB, TI_LMP92064_REG_DATA_COUT_MSB), +}; + +static const struct regmap_access_table lmp92064_readable_regs = { + .yes_ranges = lmp92064_readable_reg_ranges, + .n_yes_ranges = ARRAY_SIZE(lmp92064_readable_reg_ranges), +}; + +static const struct regmap_range lmp92064_writable_reg_ranges[] = { + regmap_reg_range(TI_LMP92064_REG_CONFIG_A, TI_LMP92064_REG_CONFIG_B), + regmap_reg_range(TI_LMP92064_REG_REG_UPDATE, TI_LMP92064_REG_REG_UPDATE), + regmap_reg_range(TI_LMP92064_REG_CONFIG_REG, TI_LMP92064_REG_CONFIG_REG), +}; + +static const struct regmap_access_table lmp92064_writable_regs = { + .yes_ranges = lmp92064_writable_reg_ranges, + .n_yes_ranges = ARRAY_SIZE(lmp92064_writable_reg_ranges), +}; + +static const struct regmap_config lmp92064_spi_regmap_config = { + .reg_bits = 16, + .val_bits = 8, + .max_register = TI_LMP92064_REG_DATA_COUT_MSB, + .rd_table = &lmp92064_readable_regs, + .wr_table = &lmp92064_writable_regs, +}; + +struct lmp92064_adc_priv { + int shunt_resistor_uohm; + struct spi_device *spi; + struct regmap *regmap; +}; + +static const struct iio_chan_spec lmp92064_adc_channels[] = { + { + .type = IIO_CURRENT, + .address = TI_LMP92064_CHAN_INC, + .info_mask_separate = + BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE), + .scan_index = TI_LMP92064_CHAN_INC, + .scan_type = { + .sign = 'u', + .realbits = 12, + .storagebits = 16, + }, + .datasheet_name = "INC", + }, + { + .type = IIO_VOLTAGE, + .address = TI_LMP92064_CHAN_INV, + .info_mask_separate = + BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE), + .scan_index = TI_LMP92064_CHAN_INV, + .scan_type = { + .sign = 'u', + .realbits = 12, + .storagebits = 16, + }, + .datasheet_name = "INV", + }, + IIO_CHAN_SOFT_TIMESTAMP(2), +}; + +static const unsigned long lmp92064_scan_masks[] = { + BIT(TI_LMP92064_CHAN_INC) | BIT(TI_LMP92064_CHAN_INV), + 0 +}; + +static int lmp92064_read_meas(struct lmp92064_adc_priv *priv, u16 *res) +{ + __be16 raw[2]; + int ret; + + /* + * The ADC only latches in new samples if all DATA registers are read + * in descending sequential order. + * The ADC auto-decrements the register index with each clocked byte. + * Read both channels in single SPI transfer by selecting the highest + * register using the command below and clocking out all four data + * bytes. + */ + + ret = regmap_bulk_read(priv->regmap, TI_LMP92064_REG_DATA_COUT_MSB, + &raw, sizeof(raw)); + + if (ret) { + dev_err(&priv->spi->dev, "regmap_bulk_read failed: %pe\n", + ERR_PTR(ret)); + return ret; + } + + res[0] = be16_to_cpu(raw[0]); + res[1] = be16_to_cpu(raw[1]); + + return 0; +} + +static int lmp92064_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int *val, + int *val2, long mask) +{ + struct lmp92064_adc_priv *priv = iio_priv(indio_dev); + u16 raw[2]; + int ret; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + ret = lmp92064_read_meas(priv, raw); + if (ret < 0) + return ret; + + *val = (chan->address == TI_LMP92064_CHAN_INC) ? raw[0] : raw[1]; + + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + if (chan->address == TI_LMP92064_CHAN_INC) { + /* + * processed (mA) = raw * current_lsb (mA) + * current_lsb (mA) = shunt_voltage_lsb (nV) / shunt_resistor (uOhm) + * shunt_voltage_lsb (nV) = 81920000 / 4096 = 20000 + */ + *val = 20000; + *val2 = priv->shunt_resistor_uohm; + } else { + /* + * processed (mV) = raw * voltage_lsb (mV) + * voltage_lsb (mV) = 2048 / 4096 + */ + *val = 2048; + *val2 = 4096; + } + return IIO_VAL_FRACTIONAL; + default: + return -EINVAL; + } +} + +static irqreturn_t lmp92064_trigger_handler(int irq, void *p) +{ + struct iio_poll_func *pf = p; + struct iio_dev *indio_dev = pf->indio_dev; + struct lmp92064_adc_priv *priv = iio_priv(indio_dev); + struct { + u16 values[2]; + aligned_s64 timestamp; + } data = { }; + int ret; + + ret = lmp92064_read_meas(priv, data.values); + if (ret) + goto err; + + iio_push_to_buffers_with_ts(indio_dev, &data, sizeof(data), + iio_get_time_ns(indio_dev)); + +err: + iio_trigger_notify_done(indio_dev->trig); + + return IRQ_HANDLED; +} + +static int lmp92064_reset(struct lmp92064_adc_priv *priv, + struct gpio_desc *gpio_reset) +{ + unsigned int status; + int ret, i; + + if (gpio_reset) { + /* + * Perform a hard reset if gpio_reset is available. + * The datasheet specifies a very low 3.5ns reset pulse duration and does not + * specify how long to wait after a reset to access the device. + * Use more conservative pulse lengths to allow analog RC filtering of the + * reset line at the board level (as recommended in the datasheet). + */ + gpiod_set_value_cansleep(gpio_reset, 1); + usleep_range(1, 10); + gpiod_set_value_cansleep(gpio_reset, 0); + usleep_range(500, 750); + } else { + /* + * Perform a soft-reset if not. + * Also write default values to the config registers that are not + * affected by soft reset. + */ + ret = regmap_write(priv->regmap, TI_LMP92064_REG_CONFIG_A, + TI_LMP92064_VAL_CONFIG_A); + if (ret < 0) + return ret; + + ret = regmap_write(priv->regmap, TI_LMP92064_REG_CONFIG_B, + TI_LMP92064_VAL_CONFIG_B); + if (ret < 0) + return ret; + } + + /* + * Wait for the device to signal readiness to prevent reading bogus data + * and make sure device is actually connected. + * The datasheet does not specify how long this takes but usually it is + * not more than 3-4 iterations of this loop. + */ + for (i = 0; i < 10; i++) { + ret = regmap_read(priv->regmap, TI_LMP92064_REG_STATUS, &status); + if (ret < 0) + return ret; + + if (status == TI_LMP92064_VAL_STATUS_OK) + return 0; + + usleep_range(1000, 2000); + } + + /* + * No (correct) response received. + * Device is mostly likely not connected to the bus. + */ + return -ENXIO; +} + +static const struct iio_info lmp92064_adc_info = { + .read_raw = lmp92064_read_raw, +}; + +static int lmp92064_adc_probe(struct spi_device *spi) +{ + struct device *dev = &spi->dev; + struct lmp92064_adc_priv *priv; + struct gpio_desc *gpio_reset; + struct iio_dev *indio_dev; + u32 shunt_resistor_uohm; + struct regmap *regmap; + int ret; + + ret = spi_setup(spi); + if (ret < 0) + return dev_err_probe(dev, ret, "Error in SPI setup\n"); + + regmap = devm_regmap_init_spi(spi, &lmp92064_spi_regmap_config); + if (IS_ERR(regmap)) + return dev_err_probe(dev, PTR_ERR(regmap), + "Failed to set up SPI regmap\n"); + + indio_dev = devm_iio_device_alloc(dev, sizeof(*priv)); + if (!indio_dev) + return -ENOMEM; + + priv = iio_priv(indio_dev); + + priv->spi = spi; + priv->regmap = regmap; + + ret = device_property_read_u32(dev, "shunt-resistor-micro-ohms", + &shunt_resistor_uohm); + if (ret < 0) + return dev_err_probe(dev, ret, + "Failed to get shunt-resistor value\n"); + + /* + * The shunt resistance is passed to userspace as the denominator of an iio + * fraction. Make sure it is in range for that. + */ + if (shunt_resistor_uohm == 0 || shunt_resistor_uohm > INT_MAX) { + dev_err(dev, "Shunt resistance is out of range\n"); + return -EINVAL; + } + + priv->shunt_resistor_uohm = shunt_resistor_uohm; + + ret = devm_regulator_get_enable(dev, "vdd"); + if (ret) + return ret; + + ret = devm_regulator_get_enable(dev, "vdig"); + if (ret) + return ret; + + gpio_reset = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH); + if (IS_ERR(gpio_reset)) + return dev_err_probe(dev, PTR_ERR(gpio_reset), + "Failed to get GPIO reset pin\n"); + + ret = lmp92064_reset(priv, gpio_reset); + if (ret < 0) + return dev_err_probe(dev, ret, "Failed to reset device\n"); + + indio_dev->name = "lmp92064"; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = lmp92064_adc_channels; + indio_dev->num_channels = ARRAY_SIZE(lmp92064_adc_channels); + indio_dev->info = &lmp92064_adc_info; + indio_dev->available_scan_masks = lmp92064_scan_masks; + + ret = devm_iio_triggered_buffer_setup(dev, indio_dev, NULL, + lmp92064_trigger_handler, NULL); + if (ret) + return dev_err_probe(dev, ret, "Failed to setup buffered read\n"); + + return devm_iio_device_register(dev, indio_dev); +} + +static const struct spi_device_id lmp92064_id_table[] = { + { .name = "lmp92064" }, + { } +}; +MODULE_DEVICE_TABLE(spi, lmp92064_id_table); + +static const struct of_device_id lmp92064_of_table[] = { + { .compatible = "ti,lmp92064" }, + { } +}; +MODULE_DEVICE_TABLE(of, lmp92064_of_table); + +static struct spi_driver lmp92064_adc_driver = { + .driver = { + .name = "lmp92064", + .of_match_table = lmp92064_of_table, + }, + .probe = lmp92064_adc_probe, + .id_table = lmp92064_id_table, +}; +module_spi_driver(lmp92064_adc_driver); + +MODULE_AUTHOR("Leonard Göhrs "); +MODULE_DESCRIPTION("TI LMP92064 ADC"); +MODULE_LICENSE("GPL"); diff --git a/drivers/iio/adc/ti-tlc4541.c b/drivers/iio/adc/ti-tlc4541.c new file mode 100644 index 000000000..ea776f80d --- /dev/null +++ b/drivers/iio/adc/ti-tlc4541.c @@ -0,0 +1,262 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * TI tlc4541 ADC Driver + * + * Copyright (C) 2017 Phil Reid + * + * Datasheets can be found here: + * https://www.ti.com/lit/gpn/tlc3541 + * https://www.ti.com/lit/gpn/tlc4541 + * + * The tlc4541 requires 24 clock cycles to start a transfer. + * Conversion then takes 2.94us to complete before data is ready + * Data is returned MSB first. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct tlc4541_state { + struct spi_device *spi; + struct regulator *reg; + struct spi_transfer scan_single_xfer[3]; + struct spi_message scan_single_msg; + + /* + * DMA (thus cache coherency maintenance) may require the + * transfer buffers to live in their own cache lines. + * 2 bytes data + 6 bytes padding + 8 bytes timestamp when + * call iio_push_to_buffers_with_timestamp. + */ + __be16 rx_buf[8] __aligned(IIO_DMA_MINALIGN); +}; + +struct tlc4541_chip_info { + const struct iio_chan_spec *channels; + unsigned int num_channels; +}; + +enum tlc4541_id { + TLC3541, + TLC4541, +}; + +#define TLC4541_V_CHAN(bits, bitshift) { \ + .type = IIO_VOLTAGE, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ + .scan_type = { \ + .sign = 'u', \ + .realbits = (bits), \ + .storagebits = 16, \ + .shift = (bitshift), \ + .endianness = IIO_BE, \ + }, \ + } + +#define DECLARE_TLC4541_CHANNELS(name, bits, bitshift) \ +const struct iio_chan_spec name ## _channels[] = { \ + TLC4541_V_CHAN(bits, bitshift), \ + IIO_CHAN_SOFT_TIMESTAMP(1), \ +} + +static DECLARE_TLC4541_CHANNELS(tlc3541, 14, 2); +static DECLARE_TLC4541_CHANNELS(tlc4541, 16, 0); + +static const struct tlc4541_chip_info tlc4541_chip_info[] = { + [TLC3541] = { + .channels = tlc3541_channels, + .num_channels = ARRAY_SIZE(tlc3541_channels), + }, + [TLC4541] = { + .channels = tlc4541_channels, + .num_channels = ARRAY_SIZE(tlc4541_channels), + }, +}; + +static irqreturn_t tlc4541_trigger_handler(int irq, void *p) +{ + struct iio_poll_func *pf = p; + struct iio_dev *indio_dev = pf->indio_dev; + struct tlc4541_state *st = iio_priv(indio_dev); + int ret; + + ret = spi_sync(st->spi, &st->scan_single_msg); + if (ret < 0) + goto done; + + iio_push_to_buffers_with_ts(indio_dev, st->rx_buf, sizeof(st->rx_buf), + iio_get_time_ns(indio_dev)); + +done: + iio_trigger_notify_done(indio_dev->trig); + return IRQ_HANDLED; +} + +static int tlc4541_get_range(struct tlc4541_state *st) +{ + int vref; + + vref = regulator_get_voltage(st->reg); + if (vref < 0) + return vref; + + vref /= 1000; + + return vref; +} + +static int tlc4541_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, + int *val2, + long m) +{ + int ret = 0; + struct tlc4541_state *st = iio_priv(indio_dev); + + switch (m) { + case IIO_CHAN_INFO_RAW: + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; + ret = spi_sync(st->spi, &st->scan_single_msg); + iio_device_release_direct(indio_dev); + if (ret < 0) + return ret; + *val = be16_to_cpu(st->rx_buf[0]); + *val = *val >> chan->scan_type.shift; + *val &= GENMASK(chan->scan_type.realbits - 1, 0); + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + ret = tlc4541_get_range(st); + if (ret < 0) + return ret; + *val = ret; + *val2 = chan->scan_type.realbits; + return IIO_VAL_FRACTIONAL_LOG2; + } + return -EINVAL; +} + +static const struct iio_info tlc4541_info = { + .read_raw = &tlc4541_read_raw, +}; + +static int tlc4541_probe(struct spi_device *spi) +{ + struct tlc4541_state *st; + struct iio_dev *indio_dev; + const struct tlc4541_chip_info *info; + int ret; + int8_t device_init = 0; + + indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st)); + if (indio_dev == NULL) + return -ENOMEM; + + st = iio_priv(indio_dev); + + spi_set_drvdata(spi, indio_dev); + + st->spi = spi; + + info = &tlc4541_chip_info[spi_get_device_id(spi)->driver_data]; + + indio_dev->name = spi_get_device_id(spi)->name; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = info->channels; + indio_dev->num_channels = info->num_channels; + indio_dev->info = &tlc4541_info; + + /* perform reset */ + spi_write(spi, &device_init, 1); + + /* Setup default message */ + st->scan_single_xfer[0].rx_buf = &st->rx_buf[0]; + st->scan_single_xfer[0].len = 3; + st->scan_single_xfer[1].delay.value = 3; + st->scan_single_xfer[1].delay.unit = SPI_DELAY_UNIT_NSECS; + st->scan_single_xfer[2].rx_buf = &st->rx_buf[0]; + st->scan_single_xfer[2].len = 2; + + spi_message_init_with_transfers(&st->scan_single_msg, + st->scan_single_xfer, 3); + + st->reg = devm_regulator_get(&spi->dev, "vref"); + if (IS_ERR(st->reg)) + return PTR_ERR(st->reg); + + ret = regulator_enable(st->reg); + if (ret) + return ret; + + ret = iio_triggered_buffer_setup(indio_dev, NULL, + &tlc4541_trigger_handler, NULL); + if (ret) + goto error_disable_reg; + + ret = iio_device_register(indio_dev); + if (ret) + goto error_cleanup_buffer; + + return 0; + +error_cleanup_buffer: + iio_triggered_buffer_cleanup(indio_dev); +error_disable_reg: + regulator_disable(st->reg); + + return ret; +} + +static void tlc4541_remove(struct spi_device *spi) +{ + struct iio_dev *indio_dev = spi_get_drvdata(spi); + struct tlc4541_state *st = iio_priv(indio_dev); + + iio_device_unregister(indio_dev); + iio_triggered_buffer_cleanup(indio_dev); + regulator_disable(st->reg); +} + +static const struct of_device_id tlc4541_dt_ids[] = { + { .compatible = "ti,tlc3541", }, + { .compatible = "ti,tlc4541", }, + { } +}; +MODULE_DEVICE_TABLE(of, tlc4541_dt_ids); + +static const struct spi_device_id tlc4541_id[] = { + { .name = "tlc3541", .driver_data = TLC3541 }, + { .name = "tlc4541", .driver_data = TLC4541 }, + { } +}; +MODULE_DEVICE_TABLE(spi, tlc4541_id); + +static struct spi_driver tlc4541_driver = { + .driver = { + .name = "tlc4541", + .of_match_table = tlc4541_dt_ids, + }, + .probe = tlc4541_probe, + .remove = tlc4541_remove, + .id_table = tlc4541_id, +}; +module_spi_driver(tlc4541_driver); + +MODULE_AUTHOR("Phil Reid "); +MODULE_DESCRIPTION("Texas Instruments TLC4541 ADC"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/adc/ti-tsc2046.c b/drivers/iio/adc/ti-tsc2046.c new file mode 100644 index 000000000..125f6d7e2 --- /dev/null +++ b/drivers/iio/adc/ti-tsc2046.c @@ -0,0 +1,838 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Texas Instruments TSC2046 SPI ADC driver + * + * Copyright (c) 2021 Oleksij Rempel , Pengutronix + */ + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include + +/* + * The PENIRQ of TSC2046 controller is implemented as level shifter attached to + * the X+ line. If voltage of the X+ line reaches a specific level the IRQ will + * be activated or deactivated. + * To make this kind of IRQ reusable as trigger following additions were + * implemented: + * - rate limiting: + * For typical touchscreen use case, we need to trigger about each 10ms. + * - hrtimer: + * Continue triggering at least once after the IRQ was deactivated. Then + * deactivate this trigger to stop sampling in order to reduce power + * consumption. + */ + +#define TI_TSC2046_NAME "tsc2046" + +/* This driver doesn't aim at the peak continuous sample rate */ +#define TI_TSC2046_MAX_SAMPLE_RATE 125000 +#define TI_TSC2046_SAMPLE_BITS \ + BITS_PER_TYPE(struct tsc2046_adc_atom) +#define TI_TSC2046_MAX_CLK_FREQ \ + (TI_TSC2046_MAX_SAMPLE_RATE * TI_TSC2046_SAMPLE_BITS) + +#define TI_TSC2046_SAMPLE_INTERVAL_US 10000 + +#define TI_TSC2046_START BIT(7) +#define TI_TSC2046_ADDR GENMASK(6, 4) +#define TI_TSC2046_ADDR_TEMP1 7 +#define TI_TSC2046_ADDR_AUX 6 +#define TI_TSC2046_ADDR_X 5 +#define TI_TSC2046_ADDR_Z2 4 +#define TI_TSC2046_ADDR_Z1 3 +#define TI_TSC2046_ADDR_VBAT 2 +#define TI_TSC2046_ADDR_Y 1 +#define TI_TSC2046_ADDR_TEMP0 0 + +/* + * The mode bit sets the resolution of the ADC. With this bit low, the next + * conversion has 12-bit resolution, whereas with this bit high, the next + * conversion has 8-bit resolution. This driver is optimized for 12-bit mode. + * So, for this driver, this bit should stay zero. + */ +#define TI_TSC2046_8BIT_MODE BIT(3) + +/* + * SER/DFR - The SER/DFR bit controls the reference mode, either single-ended + * (high) or differential (low). + */ +#define TI_TSC2046_SER BIT(2) + +/* + * If VREF_ON and ADC_ON are both zero, then the chip operates in + * auto-wake/suspend mode. In most case this bits should stay zero. + */ +#define TI_TSC2046_PD1_VREF_ON BIT(1) +#define TI_TSC2046_PD0_ADC_ON BIT(0) + +/* + * All supported devices can do 8 or 12bit resolution. This driver + * supports only 12bit mode, here we have a 16bit data transfer, where + * the MSB and the 3 LSB are 0. + */ +#define TI_TSC2046_DATA_12BIT GENMASK(14, 3) + +#define TI_TSC2046_MAX_CHAN 8 +#define TI_TSC2046_MIN_POLL_CNT 3 +#define TI_TSC2046_EXT_POLL_CNT 3 +#define TI_TSC2046_POLL_CNT \ + (TI_TSC2046_MIN_POLL_CNT + TI_TSC2046_EXT_POLL_CNT) +#define TI_TSC2046_INT_VREF 2500 + +/* Represents a HW sample */ +struct tsc2046_adc_atom { + /* + * Command transmitted to the controller. This field is empty on the RX + * buffer. + */ + u8 cmd; + /* + * Data received from the controller. This field is empty for the TX + * buffer + */ + __be16 data; +} __packed; + +/* Layout of atomic buffers within big buffer */ +struct tsc2046_adc_group_layout { + /* Group offset within the SPI RX buffer */ + unsigned int offset; + /* + * Amount of tsc2046_adc_atom structs within the same command gathered + * within same group. + */ + unsigned int count; + /* + * Settling samples (tsc2046_adc_atom structs) which should be skipped + * before good samples will start. + */ + unsigned int skip; +}; + +struct tsc2046_adc_ch_cfg { + unsigned int settling_time_us; + unsigned int oversampling_ratio; +}; + +enum tsc2046_state { + TSC2046_STATE_SHUTDOWN, + TSC2046_STATE_STANDBY, + TSC2046_STATE_POLL, + TSC2046_STATE_POLL_IRQ_DISABLE, + TSC2046_STATE_ENABLE_IRQ, +}; + +struct tsc2046_adc_priv { + struct spi_device *spi; + bool internal_vref; + + struct iio_trigger *trig; + struct hrtimer trig_timer; + enum tsc2046_state state; + int poll_cnt; + spinlock_t state_lock; + + struct spi_transfer xfer; + struct spi_message msg; + + struct { + /* Scan data for each channel */ + u16 data[TI_TSC2046_MAX_CHAN]; + /* Timestamp */ + aligned_s64 ts; + } scan_buf; + + /* + * Lock to protect the layout and the SPI transfer buffer. + * tsc2046_adc_group_layout can be changed within update_scan_mode(), + * in this case the l[] and tx/rx buffer will be out of sync to each + * other. + */ + struct mutex slock; + struct tsc2046_adc_group_layout l[TI_TSC2046_MAX_CHAN]; + struct tsc2046_adc_atom *rx; + struct tsc2046_adc_atom *tx; + + unsigned int count; + unsigned int groups; + u32 effective_speed_hz; + u32 scan_interval_us; + u32 time_per_scan_us; + u32 time_per_bit_ns; + unsigned int vref_mv; + + struct tsc2046_adc_ch_cfg ch_cfg[TI_TSC2046_MAX_CHAN]; +}; + +#define TI_TSC2046_V_CHAN(index, bits, name) \ +{ \ + .type = IIO_VOLTAGE, \ + .indexed = 1, \ + .channel = index, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ + .datasheet_name = "#name", \ + .scan_index = index, \ + .scan_type = { \ + .sign = 'u', \ + .realbits = bits, \ + .storagebits = 16, \ + .endianness = IIO_CPU, \ + }, \ +} + +#define DECLARE_TI_TSC2046_8_CHANNELS(name, bits) \ +const struct iio_chan_spec name ## _channels[] = { \ + TI_TSC2046_V_CHAN(0, bits, TEMP0), \ + TI_TSC2046_V_CHAN(1, bits, Y), \ + TI_TSC2046_V_CHAN(2, bits, VBAT), \ + TI_TSC2046_V_CHAN(3, bits, Z1), \ + TI_TSC2046_V_CHAN(4, bits, Z2), \ + TI_TSC2046_V_CHAN(5, bits, X), \ + TI_TSC2046_V_CHAN(6, bits, AUX), \ + TI_TSC2046_V_CHAN(7, bits, TEMP1), \ + IIO_CHAN_SOFT_TIMESTAMP(8), \ +} + +static DECLARE_TI_TSC2046_8_CHANNELS(tsc2046_adc, 12); + +/* + * Convert time to a number of samples which can be transferred within this + * time. + */ +static unsigned int tsc2046_adc_time_to_count(struct tsc2046_adc_priv *priv, + unsigned long time) +{ + unsigned int bit_count, sample_count; + + bit_count = DIV_ROUND_UP(time * NSEC_PER_USEC, priv->time_per_bit_ns); + sample_count = DIV_ROUND_UP(bit_count, TI_TSC2046_SAMPLE_BITS); + + dev_dbg(&priv->spi->dev, "Effective speed %u, time per bit: %u, count bits: %u, count samples: %u\n", + priv->effective_speed_hz, priv->time_per_bit_ns, + bit_count, sample_count); + + return sample_count; +} + +static u8 tsc2046_adc_get_cmd(struct tsc2046_adc_priv *priv, int ch_idx, + bool keep_power) +{ + u32 pd; + + /* + * if PD bits are 0, controller will automatically disable ADC, VREF and + * enable IRQ. + */ + if (keep_power) + pd = TI_TSC2046_PD0_ADC_ON; + else + pd = 0; + + switch (ch_idx) { + case TI_TSC2046_ADDR_TEMP1: + case TI_TSC2046_ADDR_AUX: + case TI_TSC2046_ADDR_VBAT: + case TI_TSC2046_ADDR_TEMP0: + pd |= TI_TSC2046_SER; + if (priv->internal_vref) + pd |= TI_TSC2046_PD1_VREF_ON; + } + + return TI_TSC2046_START | FIELD_PREP(TI_TSC2046_ADDR, ch_idx) | pd; +} + +static u16 tsc2046_adc_get_value(struct tsc2046_adc_atom *buf) +{ + return FIELD_GET(TI_TSC2046_DATA_12BIT, get_unaligned_be16(&buf->data)); +} + +static int tsc2046_adc_read_one(struct tsc2046_adc_priv *priv, int ch_idx, + u32 *effective_speed_hz) +{ + struct tsc2046_adc_ch_cfg *ch = &priv->ch_cfg[ch_idx]; + unsigned int val, val_normalized = 0; + int ret, i, count_skip = 0, max_count; + struct spi_transfer xfer = { }; + struct spi_message msg; + u8 cmd; + + if (!effective_speed_hz) { + count_skip = tsc2046_adc_time_to_count(priv, ch->settling_time_us); + max_count = count_skip + ch->oversampling_ratio; + } else { + max_count = 1; + } + + if (sizeof(struct tsc2046_adc_atom) * max_count > PAGE_SIZE) + return -ENOSPC; + + struct tsc2046_adc_atom *tx_buf __free(kfree) = kzalloc_objs(*tx_buf, + max_count); + if (!tx_buf) + return -ENOMEM; + + struct tsc2046_adc_atom *rx_buf __free(kfree) = kzalloc_objs(*rx_buf, + max_count); + if (!rx_buf) + return -ENOMEM; + + /* + * Do not enable automatic power down on working samples. Otherwise the + * plates will never be completely charged. + */ + cmd = tsc2046_adc_get_cmd(priv, ch_idx, true); + + for (i = 0; i < max_count - 1; i++) + tx_buf[i].cmd = cmd; + + /* automatically power down on last sample */ + tx_buf[i].cmd = tsc2046_adc_get_cmd(priv, ch_idx, false); + + xfer.tx_buf = tx_buf; + xfer.rx_buf = rx_buf; + xfer.len = sizeof(*tx_buf) * max_count; + spi_message_init_with_transfers(&msg, &xfer, 1); + + /* + * We aren't using spi_write_then_read() because we need to be able + * to get hold of the effective_speed_hz from the xfer + */ + ret = spi_sync(priv->spi, &msg); + if (ret) { + dev_err_ratelimited(&priv->spi->dev, "SPI transfer failed %pe\n", + ERR_PTR(ret)); + return ret; + } + + if (effective_speed_hz) + *effective_speed_hz = xfer.effective_speed_hz; + + for (i = 0; i < max_count - count_skip; i++) { + val = tsc2046_adc_get_value(&rx_buf[count_skip + i]); + val_normalized += val; + } + + return DIV_ROUND_UP(val_normalized, max_count - count_skip); +} + +static size_t tsc2046_adc_group_set_layout(struct tsc2046_adc_priv *priv, + unsigned int group, + unsigned int ch_idx) +{ + struct tsc2046_adc_ch_cfg *ch = &priv->ch_cfg[ch_idx]; + struct tsc2046_adc_group_layout *cur; + unsigned int max_count, count_skip; + unsigned int offset = 0; + + if (group) + offset = priv->l[group - 1].offset + priv->l[group - 1].count; + + count_skip = tsc2046_adc_time_to_count(priv, ch->settling_time_us); + max_count = count_skip + ch->oversampling_ratio; + + cur = &priv->l[group]; + cur->offset = offset; + cur->count = max_count; + cur->skip = count_skip; + + return sizeof(*priv->tx) * max_count; +} + +static void tsc2046_adc_group_set_cmd(struct tsc2046_adc_priv *priv, + unsigned int group, int ch_idx) +{ + struct tsc2046_adc_group_layout *l = &priv->l[group]; + unsigned int i; + u8 cmd; + + /* + * Do not enable automatic power down on working samples. Otherwise the + * plates will never be completely charged. + */ + cmd = tsc2046_adc_get_cmd(priv, ch_idx, true); + + for (i = 0; i < l->count - 1; i++) + priv->tx[l->offset + i].cmd = cmd; + + /* automatically power down on last sample */ + priv->tx[l->offset + i].cmd = tsc2046_adc_get_cmd(priv, ch_idx, false); +} + +static u16 tsc2046_adc_get_val(struct tsc2046_adc_priv *priv, int group) +{ + struct tsc2046_adc_group_layout *l; + unsigned int val, val_normalized = 0; + int valid_count, i; + + l = &priv->l[group]; + valid_count = l->count - l->skip; + + for (i = 0; i < valid_count; i++) { + val = tsc2046_adc_get_value(&priv->rx[l->offset + l->skip + i]); + val_normalized += val; + } + + return DIV_ROUND_UP(val_normalized, valid_count); +} + +static int tsc2046_adc_scan(struct iio_dev *indio_dev) +{ + struct tsc2046_adc_priv *priv = iio_priv(indio_dev); + struct device *dev = &priv->spi->dev; + int group; + int ret; + + ret = spi_sync(priv->spi, &priv->msg); + if (ret < 0) { + dev_err_ratelimited(dev, "SPI transfer failed: %pe\n", ERR_PTR(ret)); + return ret; + } + + for (group = 0; group < priv->groups; group++) + priv->scan_buf.data[group] = tsc2046_adc_get_val(priv, group); + + ret = iio_push_to_buffers_with_ts(indio_dev, &priv->scan_buf, + sizeof(priv->scan_buf), + iio_get_time_ns(indio_dev)); + /* If the consumer is kfifo, we may get a EBUSY here - ignore it. */ + if (ret < 0 && ret != -EBUSY) { + dev_err_ratelimited(dev, "Failed to push scan buffer %pe\n", + ERR_PTR(ret)); + + return ret; + } + + return 0; +} + +static irqreturn_t tsc2046_adc_trigger_handler(int irq, void *p) +{ + struct iio_poll_func *pf = p; + struct iio_dev *indio_dev = pf->indio_dev; + struct tsc2046_adc_priv *priv = iio_priv(indio_dev); + + mutex_lock(&priv->slock); + tsc2046_adc_scan(indio_dev); + mutex_unlock(&priv->slock); + + iio_trigger_notify_done(indio_dev->trig); + + return IRQ_HANDLED; +} + +static int tsc2046_adc_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long m) +{ + struct tsc2046_adc_priv *priv = iio_priv(indio_dev); + int ret; + + switch (m) { + case IIO_CHAN_INFO_RAW: + ret = tsc2046_adc_read_one(priv, chan->channel, NULL); + if (ret < 0) + return ret; + + *val = ret; + + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + /* + * Note: the TSC2046 has internal voltage divider on the VBAT + * line. This divider can be influenced by external divider. + * So, it is better to use external voltage-divider driver + * instead, which is calculating complete chain. + */ + *val = priv->vref_mv; + *val2 = chan->scan_type.realbits; + return IIO_VAL_FRACTIONAL_LOG2; + } + + return -EINVAL; +} + +static int tsc2046_adc_update_scan_mode(struct iio_dev *indio_dev, + const unsigned long *active_scan_mask) +{ + struct tsc2046_adc_priv *priv = iio_priv(indio_dev); + unsigned int ch_idx, group = 0; + size_t size; + + mutex_lock(&priv->slock); + + size = 0; + for_each_set_bit(ch_idx, active_scan_mask, ARRAY_SIZE(priv->l)) { + size += tsc2046_adc_group_set_layout(priv, group, ch_idx); + tsc2046_adc_group_set_cmd(priv, group, ch_idx); + group++; + } + + priv->groups = group; + priv->xfer.len = size; + priv->time_per_scan_us = size * 8 * priv->time_per_bit_ns / NSEC_PER_USEC; + + if (priv->scan_interval_us < priv->time_per_scan_us) + dev_warn(&priv->spi->dev, "The scan interval (%d) is less then calculated scan time (%d)\n", + priv->scan_interval_us, priv->time_per_scan_us); + + mutex_unlock(&priv->slock); + + return 0; +} + +static const struct iio_info tsc2046_adc_info = { + .read_raw = tsc2046_adc_read_raw, + .update_scan_mode = tsc2046_adc_update_scan_mode, +}; + +static enum hrtimer_restart tsc2046_adc_timer(struct hrtimer *hrtimer) +{ + struct tsc2046_adc_priv *priv = container_of(hrtimer, + struct tsc2046_adc_priv, + trig_timer); + unsigned long flags; + + /* + * This state machine should address following challenges : + * - the interrupt source is based on level shifter attached to the X + * channel of ADC. It will change the state every time we switch + * between channels. So, we need to disable IRQ if we do + * iio_trigger_poll(). + * - we should do iio_trigger_poll() at some reduced sample rate + * - we should still trigger for some amount of time after last + * interrupt with enabled IRQ was processed. + */ + + spin_lock_irqsave(&priv->state_lock, flags); + switch (priv->state) { + case TSC2046_STATE_ENABLE_IRQ: + if (priv->poll_cnt < TI_TSC2046_POLL_CNT) { + priv->poll_cnt++; + hrtimer_start(&priv->trig_timer, + us_to_ktime(priv->scan_interval_us), + HRTIMER_MODE_REL_SOFT); + + if (priv->poll_cnt >= TI_TSC2046_MIN_POLL_CNT) { + priv->state = TSC2046_STATE_POLL_IRQ_DISABLE; + enable_irq(priv->spi->irq); + } else { + priv->state = TSC2046_STATE_POLL; + } + } else { + priv->state = TSC2046_STATE_STANDBY; + enable_irq(priv->spi->irq); + } + break; + case TSC2046_STATE_POLL_IRQ_DISABLE: + disable_irq_nosync(priv->spi->irq); + fallthrough; + case TSC2046_STATE_POLL: + priv->state = TSC2046_STATE_ENABLE_IRQ; + /* iio_trigger_poll() starts hrtimer */ + iio_trigger_poll(priv->trig); + break; + case TSC2046_STATE_SHUTDOWN: + break; + case TSC2046_STATE_STANDBY: + fallthrough; + default: + dev_warn(&priv->spi->dev, "Got unexpected state: %i\n", + priv->state); + break; + } + spin_unlock_irqrestore(&priv->state_lock, flags); + + return HRTIMER_NORESTART; +} + +static irqreturn_t tsc2046_adc_irq(int irq, void *dev_id) +{ + struct iio_dev *indio_dev = dev_id; + struct tsc2046_adc_priv *priv = iio_priv(indio_dev); + unsigned long flags; + + hrtimer_try_to_cancel(&priv->trig_timer); + + spin_lock_irqsave(&priv->state_lock, flags); + if (priv->state != TSC2046_STATE_SHUTDOWN) { + priv->state = TSC2046_STATE_ENABLE_IRQ; + priv->poll_cnt = 0; + + /* iio_trigger_poll() starts hrtimer */ + disable_irq_nosync(priv->spi->irq); + iio_trigger_poll(priv->trig); + } + spin_unlock_irqrestore(&priv->state_lock, flags); + + return IRQ_HANDLED; +} + +static void tsc2046_adc_reenable_trigger(struct iio_trigger *trig) +{ + struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig); + struct tsc2046_adc_priv *priv = iio_priv(indio_dev); + ktime_t tim; + + /* + * We can sample it as fast as we can, but usually we do not need so + * many samples. Reduce the sample rate for default (touchscreen) use + * case. + */ + tim = us_to_ktime(priv->scan_interval_us - priv->time_per_scan_us); + hrtimer_start(&priv->trig_timer, tim, HRTIMER_MODE_REL_SOFT); +} + +static int tsc2046_adc_set_trigger_state(struct iio_trigger *trig, bool enable) +{ + struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig); + struct tsc2046_adc_priv *priv = iio_priv(indio_dev); + unsigned long flags; + + if (enable) { + spin_lock_irqsave(&priv->state_lock, flags); + if (priv->state == TSC2046_STATE_SHUTDOWN) { + priv->state = TSC2046_STATE_STANDBY; + enable_irq(priv->spi->irq); + } + spin_unlock_irqrestore(&priv->state_lock, flags); + } else { + spin_lock_irqsave(&priv->state_lock, flags); + + if (priv->state == TSC2046_STATE_STANDBY || + priv->state == TSC2046_STATE_POLL_IRQ_DISABLE) + disable_irq_nosync(priv->spi->irq); + + priv->state = TSC2046_STATE_SHUTDOWN; + spin_unlock_irqrestore(&priv->state_lock, flags); + + hrtimer_cancel(&priv->trig_timer); + } + + return 0; +} + +static const struct iio_trigger_ops tsc2046_adc_trigger_ops = { + .set_trigger_state = tsc2046_adc_set_trigger_state, + .reenable = tsc2046_adc_reenable_trigger, +}; + +static int tsc2046_adc_setup_spi_msg(struct tsc2046_adc_priv *priv) +{ + unsigned int ch_idx; + size_t size; + int ret; + + /* + * Make dummy read to set initial power state and get real SPI clock + * freq. It seems to be not important which channel is used for this + * case. + */ + ret = tsc2046_adc_read_one(priv, TI_TSC2046_ADDR_TEMP0, + &priv->effective_speed_hz); + if (ret < 0) + return ret; + + /* + * In case SPI controller do not report effective_speed_hz, use + * configure value and hope it will match. + */ + if (!priv->effective_speed_hz) + priv->effective_speed_hz = priv->spi->max_speed_hz; + + + priv->scan_interval_us = TI_TSC2046_SAMPLE_INTERVAL_US; + priv->time_per_bit_ns = DIV_ROUND_UP(NSEC_PER_SEC, + priv->effective_speed_hz); + + /* + * Calculate and allocate maximal size buffer if all channels are + * enabled. + */ + size = 0; + for (ch_idx = 0; ch_idx < ARRAY_SIZE(priv->l); ch_idx++) + size += tsc2046_adc_group_set_layout(priv, ch_idx, ch_idx); + + if (size > PAGE_SIZE) { + dev_err(&priv->spi->dev, + "Calculated scan buffer is too big. Try to reduce spi-max-frequency, settling-time-us or oversampling-ratio\n"); + return -ENOSPC; + } + + priv->tx = devm_kzalloc(&priv->spi->dev, size, GFP_KERNEL); + if (!priv->tx) + return -ENOMEM; + + priv->rx = devm_kzalloc(&priv->spi->dev, size, GFP_KERNEL); + if (!priv->rx) + return -ENOMEM; + + priv->xfer.tx_buf = priv->tx; + priv->xfer.rx_buf = priv->rx; + priv->xfer.len = size; + spi_message_init_with_transfers(&priv->msg, &priv->xfer, 1); + + return 0; +} + +static void tsc2046_adc_parse_fwnode(struct tsc2046_adc_priv *priv) +{ + struct fwnode_handle *child; + struct device *dev = &priv->spi->dev; + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(priv->ch_cfg); i++) { + priv->ch_cfg[i].settling_time_us = 1; + priv->ch_cfg[i].oversampling_ratio = 1; + } + + device_for_each_child_node(dev, child) { + u32 stl, overs, reg; + int ret; + + ret = fwnode_property_read_u32(child, "reg", ®); + if (ret) { + dev_err(dev, "invalid reg on %pfw, err: %pe\n", child, + ERR_PTR(ret)); + continue; + } + + if (reg >= ARRAY_SIZE(priv->ch_cfg)) { + dev_err(dev, "%pfw: Unsupported reg value: %i, max supported is: %zu.\n", + child, reg, ARRAY_SIZE(priv->ch_cfg)); + continue; + } + + ret = fwnode_property_read_u32(child, "settling-time-us", &stl); + if (!ret) + priv->ch_cfg[reg].settling_time_us = stl; + + ret = fwnode_property_read_u32(child, "oversampling-ratio", + &overs); + if (!ret) + priv->ch_cfg[reg].oversampling_ratio = overs; + } +} + +static int tsc2046_adc_probe(struct spi_device *spi) +{ + struct device *dev = &spi->dev; + struct tsc2046_adc_priv *priv; + struct iio_dev *indio_dev; + struct iio_trigger *trig; + int ret; + + if (spi->max_speed_hz > TI_TSC2046_MAX_CLK_FREQ) { + dev_err(dev, "SPI max_speed_hz is too high: %d Hz. Max supported freq is %zu Hz\n", + spi->max_speed_hz, TI_TSC2046_MAX_CLK_FREQ); + return -EINVAL; + } + + spi->mode &= ~SPI_MODE_X_MASK; + spi->mode |= SPI_MODE_0; + ret = spi_setup(spi); + if (ret < 0) + return dev_err_probe(dev, ret, "Error in SPI setup\n"); + + indio_dev = devm_iio_device_alloc(dev, sizeof(*priv)); + if (!indio_dev) + return -ENOMEM; + + priv = iio_priv(indio_dev); + + priv->spi = spi; + + indio_dev->name = TI_TSC2046_NAME; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = tsc2046_adc_channels; + indio_dev->num_channels = ARRAY_SIZE(tsc2046_adc_channels); + indio_dev->info = &tsc2046_adc_info; + + ret = devm_regulator_get_enable_read_voltage(dev, "vref"); + if (ret < 0 && ret != -ENODEV) + return ret; + + priv->internal_vref = ret == -ENODEV; + priv->vref_mv = priv->internal_vref ? TI_TSC2046_INT_VREF : ret / MILLI; + + tsc2046_adc_parse_fwnode(priv); + + ret = tsc2046_adc_setup_spi_msg(priv); + if (ret) + return ret; + + mutex_init(&priv->slock); + + ret = devm_request_irq(dev, spi->irq, &tsc2046_adc_irq, + IRQF_NO_AUTOEN, indio_dev->name, indio_dev); + if (ret) + return ret; + + trig = devm_iio_trigger_alloc(dev, "touchscreen-%s", indio_dev->name); + if (!trig) + return -ENOMEM; + + priv->trig = trig; + iio_trigger_set_drvdata(trig, indio_dev); + trig->ops = &tsc2046_adc_trigger_ops; + + spin_lock_init(&priv->state_lock); + priv->state = TSC2046_STATE_SHUTDOWN; + hrtimer_setup(&priv->trig_timer, tsc2046_adc_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL_SOFT); + + ret = devm_iio_trigger_register(dev, trig); + if (ret) { + dev_err(dev, "failed to register trigger\n"); + return ret; + } + + ret = devm_iio_triggered_buffer_setup(dev, indio_dev, NULL, + &tsc2046_adc_trigger_handler, NULL); + if (ret) { + dev_err(dev, "Failed to setup triggered buffer\n"); + return ret; + } + + /* set default trigger */ + indio_dev->trig = iio_trigger_get(priv->trig); + + return devm_iio_device_register(dev, indio_dev); +} + +static const struct of_device_id ads7950_of_table[] = { + { .compatible = "ti,tsc2046e-adc" }, + { } +}; +MODULE_DEVICE_TABLE(of, ads7950_of_table); + +static const struct spi_device_id tsc2046_adc_spi_ids[] = { + { .name = "tsc2046e-adc" }, + { } +}; +MODULE_DEVICE_TABLE(spi, tsc2046_adc_spi_ids); + +static struct spi_driver tsc2046_adc_driver = { + .driver = { + .name = "tsc2046", + .of_match_table = ads7950_of_table, + }, + .id_table = tsc2046_adc_spi_ids, + .probe = tsc2046_adc_probe, +}; +module_spi_driver(tsc2046_adc_driver); + +MODULE_AUTHOR("Oleksij Rempel "); +MODULE_DESCRIPTION("TI TSC2046 ADC"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/adc/ti_am335x_adc.c b/drivers/iio/adc/ti_am335x_adc.c new file mode 100644 index 000000000..1516dd332 --- /dev/null +++ b/drivers/iio/adc/ti_am335x_adc.c @@ -0,0 +1,753 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * TI ADC MFD driver + * + * Copyright (C) 2012 Texas Instruments Incorporated - https://www.ti.com/ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +#define DMA_BUFFER_SIZE SZ_2K + +struct tiadc_dma { + struct dma_slave_config conf; + struct dma_chan *chan; + dma_addr_t addr; + dma_cookie_t cookie; + u8 *buf; + int current_period; + int period_size; + u8 fifo_thresh; +}; + +struct tiadc_device { + struct ti_tscadc_dev *mfd_tscadc; + struct tiadc_dma dma; + struct mutex fifo1_lock; /* to protect fifo access */ + int channels; + int total_ch_enabled; + u8 channel_line[8]; + u8 channel_step[8]; + int buffer_en_ch_steps; + u16 data[8]; + u32 open_delay[8], sample_delay[8], step_avg[8]; +}; + +static unsigned int tiadc_readl(struct tiadc_device *adc, unsigned int reg) +{ + return readl(adc->mfd_tscadc->tscadc_base + reg); +} + +static void tiadc_writel(struct tiadc_device *adc, unsigned int reg, + unsigned int val) +{ + writel(val, adc->mfd_tscadc->tscadc_base + reg); +} + +static u32 get_adc_step_mask(struct tiadc_device *adc_dev) +{ + u32 step_en; + + step_en = ((1 << adc_dev->channels) - 1); + step_en <<= TOTAL_STEPS - adc_dev->channels + 1; + return step_en; +} + +static u32 get_adc_chan_step_mask(struct tiadc_device *adc_dev, + struct iio_chan_spec const *chan) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(adc_dev->channel_step); i++) { + if (chan->channel == adc_dev->channel_line[i]) { + u32 step; + + step = adc_dev->channel_step[i]; + /* +1 for the charger */ + return 1 << (step + 1); + } + } + WARN_ON(1); + return 0; +} + +static u32 get_adc_step_bit(struct tiadc_device *adc_dev, int chan) +{ + return 1 << adc_dev->channel_step[chan]; +} + +static int tiadc_wait_idle(struct tiadc_device *adc_dev) +{ + u32 val; + + return readl_poll_timeout(adc_dev->mfd_tscadc->tscadc_base + REG_ADCFSM, + val, !(val & SEQ_STATUS), 10, + IDLE_TIMEOUT_MS * 1000 * adc_dev->channels); +} + +static void tiadc_step_config(struct iio_dev *indio_dev) +{ + struct tiadc_device *adc_dev = iio_priv(indio_dev); + unsigned int stepconfig; + int i, steps = 0; + + /* + * There are 16 configurable steps and 8 analog input + * lines available which are shared between Touchscreen and ADC. + * + * Steps forward, i.e. from 0 towards 16, are used by ADC + * depending on the number of input lines needed. + * Channel would represent which analog input + * needs to be given to ADC to digitize data. + */ + for (i = 0; i < adc_dev->channels; i++) { + int chan; + + chan = adc_dev->channel_line[i]; + + if (adc_dev->step_avg[i] && adc_dev->step_avg[i] <= STEPCONFIG_AVG_16) + stepconfig = STEPCONFIG_AVG(ffs(adc_dev->step_avg[i]) - 1) | + STEPCONFIG_FIFO1; + else + stepconfig = STEPCONFIG_FIFO1; + + if (iio_buffer_enabled(indio_dev)) + stepconfig |= STEPCONFIG_MODE_SWCNT; + + tiadc_writel(adc_dev, REG_STEPCONFIG(steps), + stepconfig | STEPCONFIG_INP(chan) | + STEPCONFIG_INM_ADCREFM | STEPCONFIG_RFP_VREFP | + STEPCONFIG_RFM_VREFN); + + tiadc_writel(adc_dev, REG_STEPDELAY(steps), + STEPDELAY_OPEN(adc_dev->open_delay[i]) | + STEPDELAY_SAMPLE(adc_dev->sample_delay[i])); + + adc_dev->channel_step[i] = steps; + steps++; + } +} + +static irqreturn_t tiadc_irq_h(int irq, void *private) +{ + struct iio_dev *indio_dev = private; + struct tiadc_device *adc_dev = iio_priv(indio_dev); + unsigned int status, config, adc_fsm; + unsigned short count = 0; + + status = tiadc_readl(adc_dev, REG_IRQSTATUS); + + /* + * ADC and touchscreen share the IRQ line. + * FIFO0 interrupts are used by TSC. Handle FIFO1 IRQs here only + */ + if (status & IRQENB_FIFO1OVRRUN) { + /* FIFO Overrun. Clear flag. Disable/Enable ADC to recover */ + config = tiadc_readl(adc_dev, REG_CTRL); + config &= ~(CNTRLREG_SSENB); + tiadc_writel(adc_dev, REG_CTRL, config); + tiadc_writel(adc_dev, REG_IRQSTATUS, + IRQENB_FIFO1OVRRUN | IRQENB_FIFO1UNDRFLW | + IRQENB_FIFO1THRES); + + /* + * Wait for the idle state. + * ADC needs to finish the current conversion + * before disabling the module + */ + do { + adc_fsm = tiadc_readl(adc_dev, REG_ADCFSM); + } while (adc_fsm != 0x10 && count++ < 100); + + tiadc_writel(adc_dev, REG_CTRL, (config | CNTRLREG_SSENB)); + return IRQ_HANDLED; + } else if (status & IRQENB_FIFO1THRES) { + /* Disable irq and wake worker thread */ + tiadc_writel(adc_dev, REG_IRQCLR, IRQENB_FIFO1THRES); + return IRQ_WAKE_THREAD; + } + + return IRQ_NONE; +} + +static irqreturn_t tiadc_worker_h(int irq, void *private) +{ + struct iio_dev *indio_dev = private; + struct tiadc_device *adc_dev = iio_priv(indio_dev); + int i, k, fifo1count, read; + u16 *data = adc_dev->data; + + fifo1count = tiadc_readl(adc_dev, REG_FIFO1CNT); + for (k = 0; k < fifo1count; k = k + i) { + for (i = 0; i < indio_dev->scan_bytes / 2; i++) { + read = tiadc_readl(adc_dev, REG_FIFO1); + data[i] = read & FIFOREAD_DATA_MASK; + } + iio_push_to_buffers(indio_dev, (u8 *)data); + } + + tiadc_writel(adc_dev, REG_IRQSTATUS, IRQENB_FIFO1THRES); + tiadc_writel(adc_dev, REG_IRQENABLE, IRQENB_FIFO1THRES); + + return IRQ_HANDLED; +} + +static void tiadc_dma_rx_complete(void *param) +{ + struct iio_dev *indio_dev = param; + struct tiadc_device *adc_dev = iio_priv(indio_dev); + struct tiadc_dma *dma = &adc_dev->dma; + u8 *data; + int i; + + data = dma->buf + dma->current_period * dma->period_size; + dma->current_period = 1 - dma->current_period; /* swap the buffer ID */ + + for (i = 0; i < dma->period_size; i += indio_dev->scan_bytes) { + iio_push_to_buffers(indio_dev, data); + data += indio_dev->scan_bytes; + } +} + +static int tiadc_start_dma(struct iio_dev *indio_dev) +{ + struct tiadc_device *adc_dev = iio_priv(indio_dev); + struct tiadc_dma *dma = &adc_dev->dma; + struct dma_async_tx_descriptor *desc; + + dma->current_period = 0; /* We start to fill period 0 */ + + /* + * Make the fifo thresh as the multiple of total number of + * channels enabled, so make sure that cyclic DMA period + * length is also a multiple of total number of channels + * enabled. This ensures that no invalid data is reported + * to the stack via iio_push_to_buffers(). + */ + dma->fifo_thresh = rounddown(FIFO1_THRESHOLD + 1, + adc_dev->total_ch_enabled) - 1; + + /* Make sure that period length is multiple of fifo thresh level */ + dma->period_size = rounddown(DMA_BUFFER_SIZE / 2, + (dma->fifo_thresh + 1) * sizeof(u16)); + + dma->conf.src_maxburst = dma->fifo_thresh + 1; + dmaengine_slave_config(dma->chan, &dma->conf); + + desc = dmaengine_prep_dma_cyclic(dma->chan, dma->addr, + dma->period_size * 2, + dma->period_size, DMA_DEV_TO_MEM, + DMA_PREP_INTERRUPT); + if (!desc) + return -EBUSY; + + desc->callback = tiadc_dma_rx_complete; + desc->callback_param = indio_dev; + + dma->cookie = dmaengine_submit(desc); + + dma_async_issue_pending(dma->chan); + + tiadc_writel(adc_dev, REG_FIFO1THR, dma->fifo_thresh); + tiadc_writel(adc_dev, REG_DMA1REQ, dma->fifo_thresh); + tiadc_writel(adc_dev, REG_DMAENABLE_SET, DMA_FIFO1); + + return 0; +} + +static int tiadc_buffer_preenable(struct iio_dev *indio_dev) +{ + struct tiadc_device *adc_dev = iio_priv(indio_dev); + int i, fifo1count; + int ret; + + ret = tiadc_wait_idle(adc_dev); + if (ret) + return ret; + + tiadc_writel(adc_dev, REG_IRQCLR, + IRQENB_FIFO1THRES | IRQENB_FIFO1OVRRUN | + IRQENB_FIFO1UNDRFLW); + + /* Flush FIFO. Needed in corner cases in simultaneous tsc/adc use */ + fifo1count = tiadc_readl(adc_dev, REG_FIFO1CNT); + for (i = 0; i < fifo1count; i++) + tiadc_readl(adc_dev, REG_FIFO1); + + return 0; +} + +static int tiadc_buffer_postenable(struct iio_dev *indio_dev) +{ + struct tiadc_device *adc_dev = iio_priv(indio_dev); + struct tiadc_dma *dma = &adc_dev->dma; + unsigned int irq_enable; + unsigned int enb = 0; + u8 bit; + + tiadc_step_config(indio_dev); + for_each_set_bit(bit, indio_dev->active_scan_mask, adc_dev->channels) { + enb |= (get_adc_step_bit(adc_dev, bit) << 1); + adc_dev->total_ch_enabled++; + } + adc_dev->buffer_en_ch_steps = enb; + + if (dma->chan) + tiadc_start_dma(indio_dev); + + am335x_tsc_se_set_cache(adc_dev->mfd_tscadc, enb); + + tiadc_writel(adc_dev, REG_IRQSTATUS, + IRQENB_FIFO1THRES | IRQENB_FIFO1OVRRUN | + IRQENB_FIFO1UNDRFLW); + + irq_enable = IRQENB_FIFO1OVRRUN; + if (!dma->chan) + irq_enable |= IRQENB_FIFO1THRES; + tiadc_writel(adc_dev, REG_IRQENABLE, irq_enable); + + return 0; +} + +static int tiadc_buffer_predisable(struct iio_dev *indio_dev) +{ + struct tiadc_device *adc_dev = iio_priv(indio_dev); + struct tiadc_dma *dma = &adc_dev->dma; + int fifo1count, i; + + tiadc_writel(adc_dev, REG_IRQCLR, + IRQENB_FIFO1THRES | IRQENB_FIFO1OVRRUN | + IRQENB_FIFO1UNDRFLW); + am335x_tsc_se_clr(adc_dev->mfd_tscadc, adc_dev->buffer_en_ch_steps); + adc_dev->buffer_en_ch_steps = 0; + adc_dev->total_ch_enabled = 0; + if (dma->chan) { + tiadc_writel(adc_dev, REG_DMAENABLE_CLEAR, 0x2); + dmaengine_terminate_async(dma->chan); + } + + /* Flush FIFO of leftover data in the time it takes to disable adc */ + fifo1count = tiadc_readl(adc_dev, REG_FIFO1CNT); + for (i = 0; i < fifo1count; i++) + tiadc_readl(adc_dev, REG_FIFO1); + + return 0; +} + +static int tiadc_buffer_postdisable(struct iio_dev *indio_dev) +{ + tiadc_step_config(indio_dev); + + return 0; +} + +static const struct iio_buffer_setup_ops tiadc_buffer_setup_ops = { + .preenable = &tiadc_buffer_preenable, + .postenable = &tiadc_buffer_postenable, + .predisable = &tiadc_buffer_predisable, + .postdisable = &tiadc_buffer_postdisable, +}; + +static int tiadc_iio_buffered_hardware_setup(struct device *dev, + struct iio_dev *indio_dev, + irqreturn_t (*pollfunc_bh)(int irq, void *p), + irqreturn_t (*pollfunc_th)(int irq, void *p), + int irq, unsigned long flags, + const struct iio_buffer_setup_ops *setup_ops) +{ + int ret; + + ret = devm_iio_kfifo_buffer_setup(dev, indio_dev, setup_ops); + if (ret) + return ret; + + return devm_request_threaded_irq(dev, irq, pollfunc_th, pollfunc_bh, + flags, indio_dev->name, indio_dev); +} + +static const char * const chan_name_ain[] = { + "AIN0", + "AIN1", + "AIN2", + "AIN3", + "AIN4", + "AIN5", + "AIN6", + "AIN7", +}; + +static int tiadc_channel_init(struct device *dev, struct iio_dev *indio_dev, + int channels) +{ + struct tiadc_device *adc_dev = iio_priv(indio_dev); + struct iio_chan_spec *chan_array; + struct iio_chan_spec *chan; + int i; + + indio_dev->num_channels = channels; + chan_array = devm_kcalloc(dev, channels, sizeof(*chan_array), + GFP_KERNEL); + if (!chan_array) + return -ENOMEM; + + chan = chan_array; + for (i = 0; i < channels; i++, chan++) { + chan->type = IIO_VOLTAGE; + chan->indexed = 1; + chan->channel = adc_dev->channel_line[i]; + chan->info_mask_separate = BIT(IIO_CHAN_INFO_RAW); + chan->info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE); + chan->datasheet_name = chan_name_ain[chan->channel]; + chan->scan_index = i; + chan->scan_type.sign = 'u'; + chan->scan_type.realbits = 12; + chan->scan_type.storagebits = 16; + } + + indio_dev->channels = chan_array; + + return 0; +} + +static int tiadc_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int *val, int *val2, + long mask) +{ + struct tiadc_device *adc_dev = iio_priv(indio_dev); + int i, map_val; + unsigned int fifo1count, read, stepid; + bool found = false; + u32 step_en; + unsigned long timeout; + int ret; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + break; + case IIO_CHAN_INFO_SCALE: + switch (chan->type) { + case IIO_VOLTAGE: + *val = 1800; + *val2 = chan->scan_type.realbits; + return IIO_VAL_FRACTIONAL_LOG2; + default: + return -EINVAL; + } + break; + default: + return -EINVAL; + } + + if (iio_buffer_enabled(indio_dev)) + return -EBUSY; + + step_en = get_adc_chan_step_mask(adc_dev, chan); + if (!step_en) + return -EINVAL; + + mutex_lock(&adc_dev->fifo1_lock); + + ret = tiadc_wait_idle(adc_dev); + if (ret) + goto err_unlock; + + fifo1count = tiadc_readl(adc_dev, REG_FIFO1CNT); + while (fifo1count--) + tiadc_readl(adc_dev, REG_FIFO1); + + am335x_tsc_se_set_once(adc_dev->mfd_tscadc, step_en); + + /* Wait for Fifo threshold interrupt */ + timeout = jiffies + msecs_to_jiffies(IDLE_TIMEOUT_MS * adc_dev->channels); + while (1) { + fifo1count = tiadc_readl(adc_dev, REG_FIFO1CNT); + if (fifo1count) + break; + + if (time_after(jiffies, timeout)) { + am335x_tsc_se_adc_done(adc_dev->mfd_tscadc); + ret = -EAGAIN; + goto err_unlock; + } + } + + map_val = adc_dev->channel_step[chan->scan_index]; + + /* + * We check the complete FIFO. We programmed just one entry but in case + * something went wrong we left empty handed (-EAGAIN previously) and + * then the value appeared somehow in the FIFO we would have two entries. + * Therefore we read every item and keep only the latest version of the + * requested channel. + */ + for (i = 0; i < fifo1count; i++) { + read = tiadc_readl(adc_dev, REG_FIFO1); + stepid = read & FIFOREAD_CHNLID_MASK; + stepid = stepid >> 0x10; + + if (stepid == map_val) { + read = read & FIFOREAD_DATA_MASK; + found = true; + *val = (u16)read; + } + } + + am335x_tsc_se_adc_done(adc_dev->mfd_tscadc); + + if (!found) + ret = -EBUSY; + +err_unlock: + mutex_unlock(&adc_dev->fifo1_lock); + return ret ? ret : IIO_VAL_INT; +} + +static const struct iio_info tiadc_info = { + .read_raw = &tiadc_read_raw, +}; + +static int tiadc_request_dma(struct platform_device *pdev, + struct tiadc_device *adc_dev) +{ + struct tiadc_dma *dma = &adc_dev->dma; + dma_cap_mask_t mask; + + /* Default slave configuration parameters */ + dma->conf.direction = DMA_DEV_TO_MEM; + dma->conf.src_addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES; + dma->conf.src_addr = adc_dev->mfd_tscadc->tscadc_phys_base + REG_FIFO1; + + dma_cap_zero(mask); + dma_cap_set(DMA_CYCLIC, mask); + + /* Get a channel for RX */ + dma->chan = dma_request_chan(adc_dev->mfd_tscadc->dev, "fifo1"); + if (IS_ERR(dma->chan)) { + int ret = PTR_ERR(dma->chan); + + dma->chan = NULL; + return ret; + } + + /* RX buffer */ + dma->buf = dma_alloc_coherent(dma->chan->device->dev, DMA_BUFFER_SIZE, + &dma->addr, GFP_KERNEL); + if (!dma->buf) + goto err; + + return 0; + +err: + dma_release_channel(dma->chan); + return -ENOMEM; +} + +static int tiadc_parse_dt(struct platform_device *pdev, + struct tiadc_device *adc_dev) +{ + struct device_node *node = pdev->dev.of_node; + int channels = 0; + u32 val; + int i; + + of_property_for_each_u32(node, "ti,adc-channels", val) { + adc_dev->channel_line[channels] = val; + + /* Set Default values for optional DT parameters */ + adc_dev->open_delay[channels] = STEPCONFIG_OPENDLY; + adc_dev->sample_delay[channels] = STEPCONFIG_SAMPLEDLY; + adc_dev->step_avg[channels] = 16; + + channels++; + } + + adc_dev->channels = channels; + + of_property_read_u32_array(node, "ti,chan-step-avg", + adc_dev->step_avg, channels); + of_property_read_u32_array(node, "ti,chan-step-opendelay", + adc_dev->open_delay, channels); + of_property_read_u32_array(node, "ti,chan-step-sampledelay", + adc_dev->sample_delay, channels); + + for (i = 0; i < adc_dev->channels; i++) { + int chan; + + chan = adc_dev->channel_line[i]; + + if (adc_dev->step_avg[i] > STEPCONFIG_AVG_16) { + dev_warn(&pdev->dev, + "chan %d: wrong step avg, truncated to %ld\n", + chan, STEPCONFIG_AVG_16); + adc_dev->step_avg[i] = STEPCONFIG_AVG_16; + } + + if (adc_dev->open_delay[i] > STEPCONFIG_MAX_OPENDLY) { + dev_warn(&pdev->dev, + "chan %d: wrong open delay, truncated to 0x%lX\n", + chan, STEPCONFIG_MAX_OPENDLY); + adc_dev->open_delay[i] = STEPCONFIG_MAX_OPENDLY; + } + + if (adc_dev->sample_delay[i] > STEPCONFIG_MAX_SAMPLE) { + dev_warn(&pdev->dev, + "chan %d: wrong sample delay, truncated to 0x%lX\n", + chan, STEPCONFIG_MAX_SAMPLE); + adc_dev->sample_delay[i] = STEPCONFIG_MAX_SAMPLE; + } + } + + return 0; +} + +static int tiadc_probe(struct platform_device *pdev) +{ + struct iio_dev *indio_dev; + struct tiadc_device *adc_dev; + struct device_node *node = pdev->dev.of_node; + int err; + + if (!node) { + dev_err(&pdev->dev, "Could not find valid DT data.\n"); + return -EINVAL; + } + + indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*adc_dev)); + if (!indio_dev) + return -ENOMEM; + + adc_dev = iio_priv(indio_dev); + + adc_dev->mfd_tscadc = ti_tscadc_dev_get(pdev); + tiadc_parse_dt(pdev, adc_dev); + + indio_dev->name = dev_name(&pdev->dev); + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->info = &tiadc_info; + + tiadc_step_config(indio_dev); + tiadc_writel(adc_dev, REG_FIFO1THR, FIFO1_THRESHOLD); + mutex_init(&adc_dev->fifo1_lock); + + err = tiadc_channel_init(&pdev->dev, indio_dev, adc_dev->channels); + if (err < 0) + return err; + + err = tiadc_iio_buffered_hardware_setup(&pdev->dev, indio_dev, + &tiadc_worker_h, + &tiadc_irq_h, + adc_dev->mfd_tscadc->irq, + IRQF_SHARED, + &tiadc_buffer_setup_ops); + if (err) + return err; + + err = iio_device_register(indio_dev); + if (err) + return err; + + platform_set_drvdata(pdev, indio_dev); + + err = tiadc_request_dma(pdev, adc_dev); + if (err && err != -ENODEV) { + dev_err_probe(&pdev->dev, err, "DMA request failed\n"); + goto err_dma; + } + + return 0; + +err_dma: + iio_device_unregister(indio_dev); + + return err; +} + +static void tiadc_remove(struct platform_device *pdev) +{ + struct iio_dev *indio_dev = platform_get_drvdata(pdev); + struct tiadc_device *adc_dev = iio_priv(indio_dev); + struct tiadc_dma *dma = &adc_dev->dma; + u32 step_en; + + if (dma->chan) { + dma_free_coherent(dma->chan->device->dev, DMA_BUFFER_SIZE, + dma->buf, dma->addr); + dma_release_channel(dma->chan); + } + iio_device_unregister(indio_dev); + + step_en = get_adc_step_mask(adc_dev); + am335x_tsc_se_clr(adc_dev->mfd_tscadc, step_en); +} + +static int tiadc_suspend(struct device *dev) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct tiadc_device *adc_dev = iio_priv(indio_dev); + unsigned int idle; + + idle = tiadc_readl(adc_dev, REG_CTRL); + idle &= ~(CNTRLREG_SSENB); + tiadc_writel(adc_dev, REG_CTRL, idle | CNTRLREG_POWERDOWN); + + return 0; +} + +static int tiadc_resume(struct device *dev) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct tiadc_device *adc_dev = iio_priv(indio_dev); + unsigned int restore; + + /* Make sure ADC is powered up */ + restore = tiadc_readl(adc_dev, REG_CTRL); + restore &= ~CNTRLREG_POWERDOWN; + tiadc_writel(adc_dev, REG_CTRL, restore); + + tiadc_step_config(indio_dev); + am335x_tsc_se_set_cache(adc_dev->mfd_tscadc, + adc_dev->buffer_en_ch_steps); + return 0; +} + +static DEFINE_SIMPLE_DEV_PM_OPS(tiadc_pm_ops, tiadc_suspend, tiadc_resume); + +static const struct of_device_id ti_adc_dt_ids[] = { + { .compatible = "ti,am3359-adc", }, + { .compatible = "ti,am4372-adc", }, + { } +}; +MODULE_DEVICE_TABLE(of, ti_adc_dt_ids); + +static struct platform_driver tiadc_driver = { + .driver = { + .name = "TI-am335x-adc", + .pm = pm_sleep_ptr(&tiadc_pm_ops), + .of_match_table = ti_adc_dt_ids, + }, + .probe = tiadc_probe, + .remove = tiadc_remove, +}; +module_platform_driver(tiadc_driver); + +MODULE_DESCRIPTION("TI ADC controller driver"); +MODULE_AUTHOR("Rachna Patil "); +MODULE_LICENSE("GPL"); diff --git a/drivers/iio/adc/twl4030-madc.c b/drivers/iio/adc/twl4030-madc.c new file mode 100644 index 000000000..0ee7e16b5 --- /dev/null +++ b/drivers/iio/adc/twl4030-madc.c @@ -0,0 +1,926 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * + * TWL4030 MADC module driver-This driver monitors the real time + * conversion of analog signals like battery temperature, + * battery type, battery level etc. + * + * Copyright (C) 2011 Texas Instruments Incorporated - https://www.ti.com/ + * J Keerthy + * + * Based on twl4030-madc.c + * Copyright (C) 2008 Nokia Corporation + * Mikko Ylinen + * + * Amit Kucheria + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define TWL4030_MADC_MAX_CHANNELS 16 + +#define TWL4030_MADC_CTRL1 0x00 +#define TWL4030_MADC_CTRL2 0x01 + +#define TWL4030_MADC_RTSELECT_LSB 0x02 +#define TWL4030_MADC_SW1SELECT_LSB 0x06 +#define TWL4030_MADC_SW2SELECT_LSB 0x0A + +#define TWL4030_MADC_RTAVERAGE_LSB 0x04 +#define TWL4030_MADC_SW1AVERAGE_LSB 0x08 +#define TWL4030_MADC_SW2AVERAGE_LSB 0x0C + +#define TWL4030_MADC_CTRL_SW1 0x12 +#define TWL4030_MADC_CTRL_SW2 0x13 + +#define TWL4030_MADC_RTCH0_LSB 0x17 +#define TWL4030_MADC_GPCH0_LSB 0x37 + +#define TWL4030_MADC_MADCON (1 << 0) /* MADC power on */ +#define TWL4030_MADC_BUSY (1 << 0) /* MADC busy */ +/* MADC conversion completion */ +#define TWL4030_MADC_EOC_SW (1 << 1) +/* MADC SWx start conversion */ +#define TWL4030_MADC_SW_START (1 << 5) +#define TWL4030_MADC_ADCIN0 (1 << 0) +#define TWL4030_MADC_ADCIN1 (1 << 1) +#define TWL4030_MADC_ADCIN2 (1 << 2) +#define TWL4030_MADC_ADCIN3 (1 << 3) +#define TWL4030_MADC_ADCIN4 (1 << 4) +#define TWL4030_MADC_ADCIN5 (1 << 5) +#define TWL4030_MADC_ADCIN6 (1 << 6) +#define TWL4030_MADC_ADCIN7 (1 << 7) +#define TWL4030_MADC_ADCIN8 (1 << 8) +#define TWL4030_MADC_ADCIN9 (1 << 9) +#define TWL4030_MADC_ADCIN10 (1 << 10) +#define TWL4030_MADC_ADCIN11 (1 << 11) +#define TWL4030_MADC_ADCIN12 (1 << 12) +#define TWL4030_MADC_ADCIN13 (1 << 13) +#define TWL4030_MADC_ADCIN14 (1 << 14) +#define TWL4030_MADC_ADCIN15 (1 << 15) + +/* Fixed channels */ +#define TWL4030_MADC_BTEMP TWL4030_MADC_ADCIN1 +#define TWL4030_MADC_VBUS TWL4030_MADC_ADCIN8 +#define TWL4030_MADC_VBKB TWL4030_MADC_ADCIN9 +#define TWL4030_MADC_ICHG TWL4030_MADC_ADCIN10 +#define TWL4030_MADC_VCHG TWL4030_MADC_ADCIN11 +#define TWL4030_MADC_VBAT TWL4030_MADC_ADCIN12 + +/* Step size and prescaler ratio */ +#define TEMP_STEP_SIZE 147 +#define TEMP_PSR_R 100 +#define CURR_STEP_SIZE 147 +#define CURR_PSR_R1 44 +#define CURR_PSR_R2 88 + +#define TWL4030_BCI_BCICTL1 0x23 +#define TWL4030_BCI_CGAIN 0x020 +#define TWL4030_BCI_MESBAT (1 << 1) +#define TWL4030_BCI_TYPEN (1 << 4) +#define TWL4030_BCI_ITHEN (1 << 3) + +#define REG_BCICTL2 0x024 +#define TWL4030_BCI_ITHSENS 0x007 + +/* Register and bits for GPBR1 register */ +#define TWL4030_REG_GPBR1 0x0c +#define TWL4030_GPBR1_MADC_HFCLK_EN (1 << 7) + +#define TWL4030_USB_SEL_MADC_MCPC (1<<3) +#define TWL4030_USB_CARKIT_ANA_CTRL 0xBB + +struct twl4030_madc_conversion_method { + u8 sel; + u8 avg; + u8 rbase; + u8 ctrl; +}; + +/** + * struct twl4030_madc_request - madc request packet for channel conversion + * @channels: 16 bit bitmap for individual channels + * @do_avg: sample the input channel for 4 consecutive cycles + * @method: RT, SW1, SW2 + * @type: Polling or interrupt based method + * @active: Flag if request is active + * @result_pending: Flag from irq handler, that result is ready + * @raw: Return raw value, do not convert it + * @rbuf: Result buffer + */ +struct twl4030_madc_request { + unsigned long channels; + bool do_avg; + u16 method; + u16 type; + bool active; + bool result_pending; + bool raw; + int rbuf[TWL4030_MADC_MAX_CHANNELS]; +}; + +enum conversion_methods { + TWL4030_MADC_RT, + TWL4030_MADC_SW1, + TWL4030_MADC_SW2, + TWL4030_MADC_NUM_METHODS +}; + +enum sample_type { + TWL4030_MADC_WAIT, + TWL4030_MADC_IRQ_ONESHOT, + TWL4030_MADC_IRQ_REARM +}; + +/** + * struct twl4030_madc_data - a container for madc info + * @dev: Pointer to device structure for madc + * @lock: Mutex protecting this data structure + * @usb3v1: Pointer to bias regulator for madc + * @requests: Array of request struct corresponding to SW1, SW2 and RT + * @use_second_irq: IRQ selection (main or co-processor) + * @imr: Interrupt mask register of MADC + * @isr: Interrupt status register of MADC + */ +struct twl4030_madc_data { + struct device *dev; + struct mutex lock; + struct regulator *usb3v1; + struct twl4030_madc_request requests[TWL4030_MADC_NUM_METHODS]; + bool use_second_irq; + u8 imr; + u8 isr; +}; + +static int twl4030_madc_conversion(struct twl4030_madc_request *req); + +static int twl4030_madc_read(struct iio_dev *iio_dev, + const struct iio_chan_spec *chan, + int *val, int *val2, long mask) +{ + struct twl4030_madc_data *madc = iio_priv(iio_dev); + struct twl4030_madc_request req; + int ret; + + req.method = madc->use_second_irq ? TWL4030_MADC_SW2 : TWL4030_MADC_SW1; + + req.channels = BIT(chan->channel); + req.active = false; + req.type = TWL4030_MADC_WAIT; + req.raw = !(mask == IIO_CHAN_INFO_PROCESSED); + req.do_avg = (mask == IIO_CHAN_INFO_AVERAGE_RAW); + + ret = twl4030_madc_conversion(&req); + if (ret < 0) + return ret; + + *val = req.rbuf[chan->channel]; + + return IIO_VAL_INT; +} + +static const struct iio_info twl4030_madc_iio_info = { + .read_raw = &twl4030_madc_read, +}; + +#define TWL4030_ADC_CHANNEL(_channel, _type, _name) { \ + .type = _type, \ + .channel = _channel, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ + BIT(IIO_CHAN_INFO_AVERAGE_RAW) | \ + BIT(IIO_CHAN_INFO_PROCESSED), \ + .datasheet_name = _name, \ + .indexed = 1, \ +} + +static const struct iio_chan_spec twl4030_madc_iio_channels[] = { + TWL4030_ADC_CHANNEL(0, IIO_VOLTAGE, "ADCIN0"), + TWL4030_ADC_CHANNEL(1, IIO_TEMP, "ADCIN1"), + TWL4030_ADC_CHANNEL(2, IIO_VOLTAGE, "ADCIN2"), + TWL4030_ADC_CHANNEL(3, IIO_VOLTAGE, "ADCIN3"), + TWL4030_ADC_CHANNEL(4, IIO_VOLTAGE, "ADCIN4"), + TWL4030_ADC_CHANNEL(5, IIO_VOLTAGE, "ADCIN5"), + TWL4030_ADC_CHANNEL(6, IIO_VOLTAGE, "ADCIN6"), + TWL4030_ADC_CHANNEL(7, IIO_VOLTAGE, "ADCIN7"), + TWL4030_ADC_CHANNEL(8, IIO_VOLTAGE, "ADCIN8"), + TWL4030_ADC_CHANNEL(9, IIO_VOLTAGE, "ADCIN9"), + TWL4030_ADC_CHANNEL(10, IIO_CURRENT, "ADCIN10"), + TWL4030_ADC_CHANNEL(11, IIO_VOLTAGE, "ADCIN11"), + TWL4030_ADC_CHANNEL(12, IIO_VOLTAGE, "ADCIN12"), + TWL4030_ADC_CHANNEL(13, IIO_VOLTAGE, "ADCIN13"), + TWL4030_ADC_CHANNEL(14, IIO_VOLTAGE, "ADCIN14"), + TWL4030_ADC_CHANNEL(15, IIO_VOLTAGE, "ADCIN15"), +}; + +static struct twl4030_madc_data *twl4030_madc; + +static const struct s16_fract twl4030_divider_ratios[16] = { + {1, 1}, /* CHANNEL 0 No Prescaler */ + {1, 1}, /* CHANNEL 1 No Prescaler */ + {6, 10}, /* CHANNEL 2 */ + {6, 10}, /* CHANNEL 3 */ + {6, 10}, /* CHANNEL 4 */ + {6, 10}, /* CHANNEL 5 */ + {6, 10}, /* CHANNEL 6 */ + {6, 10}, /* CHANNEL 7 */ + {3, 14}, /* CHANNEL 8 */ + {1, 3}, /* CHANNEL 9 */ + {1, 1}, /* CHANNEL 10 No Prescaler */ + {15, 100}, /* CHANNEL 11 */ + {1, 4}, /* CHANNEL 12 */ + {1, 1}, /* CHANNEL 13 Reserved channels */ + {1, 1}, /* CHANNEL 14 Reserved channels */ + {5, 11}, /* CHANNEL 15 */ +}; + +/* Conversion table from -3 to 55 degrees Celsius */ +static int twl4030_therm_tbl[] = { + 30800, 29500, 28300, 27100, + 26000, 24900, 23900, 22900, 22000, 21100, 20300, 19400, 18700, + 17900, 17200, 16500, 15900, 15300, 14700, 14100, 13600, 13100, + 12600, 12100, 11600, 11200, 10800, 10400, 10000, 9630, 9280, + 8950, 8620, 8310, 8020, 7730, 7460, 7200, 6950, 6710, + 6470, 6250, 6040, 5830, 5640, 5450, 5260, 5090, 4920, + 4760, 4600, 4450, 4310, 4170, 4040, 3910, 3790, 3670, + 3550 +}; + +/* + * Structure containing the registers + * of different conversion methods supported by MADC. + * Hardware or RT real time conversion request initiated by external host + * processor for RT Signal conversions. + * External host processors can also request for non RT conversions + * SW1 and SW2 software conversions also called asynchronous or GPC request. + */ +static +const struct twl4030_madc_conversion_method twl4030_conversion_methods[] = { + [TWL4030_MADC_RT] = { + .sel = TWL4030_MADC_RTSELECT_LSB, + .avg = TWL4030_MADC_RTAVERAGE_LSB, + .rbase = TWL4030_MADC_RTCH0_LSB, + }, + [TWL4030_MADC_SW1] = { + .sel = TWL4030_MADC_SW1SELECT_LSB, + .avg = TWL4030_MADC_SW1AVERAGE_LSB, + .rbase = TWL4030_MADC_GPCH0_LSB, + .ctrl = TWL4030_MADC_CTRL_SW1, + }, + [TWL4030_MADC_SW2] = { + .sel = TWL4030_MADC_SW2SELECT_LSB, + .avg = TWL4030_MADC_SW2AVERAGE_LSB, + .rbase = TWL4030_MADC_GPCH0_LSB, + .ctrl = TWL4030_MADC_CTRL_SW2, + }, +}; + +/** + * twl4030_madc_channel_raw_read() - Function to read a particular channel value + * @madc: pointer to struct twl4030_madc_data + * @reg: lsb of ADC Channel + * + * Return: 0 on success, an error code otherwise. + */ +static int twl4030_madc_channel_raw_read(struct twl4030_madc_data *madc, u8 reg) +{ + u16 val; + int ret; + /* + * For each ADC channel, we have MSB and LSB register pair. MSB address + * is always LSB address+1. reg parameter is the address of LSB register + */ + ret = twl_i2c_read_u16(TWL4030_MODULE_MADC, &val, reg); + if (ret) { + dev_err(madc->dev, "unable to read register 0x%X\n", reg); + return ret; + } + + return (int)(val >> 6); +} + +/* + * Return battery temperature in degrees Celsius + * Or < 0 on failure. + */ +static int twl4030battery_temperature(int raw_volt) +{ + u8 val; + int temp, curr, volt, res, ret; + + volt = (raw_volt * TEMP_STEP_SIZE) / TEMP_PSR_R; + /* Getting and calculating the supply current in micro amperes */ + ret = twl_i2c_read_u8(TWL_MODULE_MAIN_CHARGE, &val, + REG_BCICTL2); + if (ret < 0) + return ret; + + curr = ((val & TWL4030_BCI_ITHSENS) + 1) * 10; + /* Getting and calculating the thermistor resistance in ohms */ + res = volt * 1000 / curr; + /* calculating temperature */ + for (temp = 58; temp >= 0; temp--) { + int actual = twl4030_therm_tbl[temp]; + if ((actual - res) >= 0) + break; + } + + return temp + 1; +} + +static int twl4030battery_current(int raw_volt) +{ + int ret; + u8 val; + + ret = twl_i2c_read_u8(TWL_MODULE_MAIN_CHARGE, &val, + TWL4030_BCI_BCICTL1); + if (ret) + return ret; + if (val & TWL4030_BCI_CGAIN) /* slope of 0.44 mV/mA */ + return (raw_volt * CURR_STEP_SIZE) / CURR_PSR_R1; + else /* slope of 0.88 mV/mA */ + return (raw_volt * CURR_STEP_SIZE) / CURR_PSR_R2; +} + +/* + * Function to read channel values + * @madc - pointer to twl4030_madc_data struct + * @reg_base - Base address of the first channel + * @Channels - 16 bit bitmap. If the bit is set, channel's value is read + * @buf - The channel values are stored here. if read fails error + * @raw - Return raw values without conversion + * value is stored + * Returns the number of successfully read channels. + */ +static int twl4030_madc_read_channels(struct twl4030_madc_data *madc, + u8 reg_base, unsigned + long channels, int *buf, + bool raw) +{ + int count = 0; + int i; + u8 reg; + + for_each_set_bit(i, &channels, TWL4030_MADC_MAX_CHANNELS) { + reg = reg_base + (2 * i); + buf[i] = twl4030_madc_channel_raw_read(madc, reg); + if (buf[i] < 0) { + dev_err(madc->dev, "Unable to read register 0x%X\n", + reg); + return buf[i]; + } + if (raw) { + count++; + continue; + } + switch (i) { + case 10: + buf[i] = twl4030battery_current(buf[i]); + if (buf[i] < 0) { + dev_err(madc->dev, "err reading current\n"); + return buf[i]; + } else { + count++; + buf[i] = buf[i] - 750; + } + break; + case 1: + buf[i] = twl4030battery_temperature(buf[i]); + if (buf[i] < 0) { + dev_err(madc->dev, "err reading temperature\n"); + return buf[i]; + } else { + buf[i] -= 3; + count++; + } + break; + default: + count++; + /* Analog Input (V) = conv_result * step_size / R + * conv_result = decimal value of 10-bit conversion + * result + * step size = 1.5 / (2 ^ 10 -1) + * R = Prescaler ratio for input channels. + * Result given in mV hence multiplied by 1000. + */ + buf[i] = (buf[i] * 3 * 1000 * + twl4030_divider_ratios[i].denominator) + / (2 * 1023 * + twl4030_divider_ratios[i].numerator); + } + } + + return count; +} + +/* + * Disables irq. + * @madc - pointer to twl4030_madc_data struct + * @id - irq number to be disabled + * can take one of TWL4030_MADC_RT, TWL4030_MADC_SW1, TWL4030_MADC_SW2 + * corresponding to RT, SW1, SW2 conversion requests. + * Returns error if i2c read/write fails. + */ +static int twl4030_madc_disable_irq(struct twl4030_madc_data *madc, u8 id) +{ + u8 val; + int ret; + + ret = twl_i2c_read_u8(TWL4030_MODULE_MADC, &val, madc->imr); + if (ret) { + dev_err(madc->dev, "unable to read imr register 0x%X\n", + madc->imr); + return ret; + } + val |= (1 << id); + ret = twl_i2c_write_u8(TWL4030_MODULE_MADC, val, madc->imr); + if (ret) { + dev_err(madc->dev, + "unable to write imr register 0x%X\n", madc->imr); + return ret; + } + + return 0; +} + +static irqreturn_t twl4030_madc_threaded_irq_handler(int irq, void *_madc) +{ + struct twl4030_madc_data *madc = _madc; + const struct twl4030_madc_conversion_method *method; + u8 isr_val, imr_val; + int i, ret; + struct twl4030_madc_request *r; + + mutex_lock(&madc->lock); + ret = twl_i2c_read_u8(TWL4030_MODULE_MADC, &isr_val, madc->isr); + if (ret) { + dev_err(madc->dev, "unable to read isr register 0x%X\n", + madc->isr); + goto err_i2c; + } + ret = twl_i2c_read_u8(TWL4030_MODULE_MADC, &imr_val, madc->imr); + if (ret) { + dev_err(madc->dev, "unable to read imr register 0x%X\n", + madc->imr); + goto err_i2c; + } + isr_val &= ~imr_val; + for (i = 0; i < TWL4030_MADC_NUM_METHODS; i++) { + if (!(isr_val & (1 << i))) + continue; + ret = twl4030_madc_disable_irq(madc, i); + if (ret < 0) + dev_dbg(madc->dev, "Disable interrupt failed %d\n", i); + madc->requests[i].result_pending = true; + } + for (i = 0; i < TWL4030_MADC_NUM_METHODS; i++) { + r = &madc->requests[i]; + /* No pending results for this method, move to next one */ + if (!r->result_pending) + continue; + method = &twl4030_conversion_methods[r->method]; + /* Read results */ + twl4030_madc_read_channels(madc, method->rbase, + r->channels, r->rbuf, r->raw); + /* Free request */ + r->result_pending = false; + r->active = false; + } + mutex_unlock(&madc->lock); + + return IRQ_HANDLED; + +err_i2c: + /* + * In case of error check whichever request is active + * and service the same. + */ + for (i = 0; i < TWL4030_MADC_NUM_METHODS; i++) { + r = &madc->requests[i]; + if (!r->active) + continue; + method = &twl4030_conversion_methods[r->method]; + /* Read results */ + twl4030_madc_read_channels(madc, method->rbase, + r->channels, r->rbuf, r->raw); + /* Free request */ + r->result_pending = false; + r->active = false; + } + mutex_unlock(&madc->lock); + + return IRQ_HANDLED; +} + +/* + * Function which enables the madc conversion + * by writing to the control register. + * @madc - pointer to twl4030_madc_data struct + * @conv_method - can be TWL4030_MADC_RT, TWL4030_MADC_SW2, TWL4030_MADC_SW1 + * corresponding to RT SW1 or SW2 conversion methods. + * Returns 0 if succeeds else a negative error value + */ +static int twl4030_madc_start_conversion(struct twl4030_madc_data *madc, + int conv_method) +{ + const struct twl4030_madc_conversion_method *method; + int ret = 0; + + if (conv_method != TWL4030_MADC_SW1 && conv_method != TWL4030_MADC_SW2) + return -ENOTSUPP; + + method = &twl4030_conversion_methods[conv_method]; + ret = twl_i2c_write_u8(TWL4030_MODULE_MADC, TWL4030_MADC_SW_START, + method->ctrl); + if (ret) { + dev_err(madc->dev, "unable to write ctrl register 0x%X\n", + method->ctrl); + return ret; + } + + return 0; +} + +/* + * Function that waits for conversion to be ready + * @madc - pointer to twl4030_madc_data struct + * @timeout_ms - timeout value in milliseconds + * @status_reg - ctrl register + * returns 0 if succeeds else a negative error value + */ +static int twl4030_madc_wait_conversion_ready(struct twl4030_madc_data *madc, + unsigned int timeout_ms, + u8 status_reg) +{ + unsigned long timeout; + int ret; + + timeout = jiffies + msecs_to_jiffies(timeout_ms); + do { + u8 reg; + + ret = twl_i2c_read_u8(TWL4030_MODULE_MADC, ®, status_reg); + if (ret) { + dev_err(madc->dev, + "unable to read status register 0x%X\n", + status_reg); + return ret; + } + if (!(reg & TWL4030_MADC_BUSY) && (reg & TWL4030_MADC_EOC_SW)) + return 0; + usleep_range(500, 2000); + } while (!time_after(jiffies, timeout)); + dev_err(madc->dev, "conversion timeout!\n"); + + return -EAGAIN; +} + +/* + * An exported function which can be called from other kernel drivers. + * @req twl4030_madc_request structure + * req->rbuf will be filled with read values of channels based on the + * channel index. If a particular channel reading fails there will + * be a negative error value in the corresponding array element. + * returns 0 if succeeds else error value + */ +static int twl4030_madc_conversion(struct twl4030_madc_request *req) +{ + const struct twl4030_madc_conversion_method *method; + int ret; + + if (!req || !twl4030_madc) + return -EINVAL; + + mutex_lock(&twl4030_madc->lock); + if (req->method < TWL4030_MADC_RT || req->method > TWL4030_MADC_SW2) { + ret = -EINVAL; + goto out; + } + /* Do we have a conversion request ongoing */ + if (twl4030_madc->requests[req->method].active) { + ret = -EBUSY; + goto out; + } + method = &twl4030_conversion_methods[req->method]; + /* Select channels to be converted */ + ret = twl_i2c_write_u16(TWL4030_MODULE_MADC, req->channels, method->sel); + if (ret) { + dev_err(twl4030_madc->dev, + "unable to write sel register 0x%X\n", method->sel); + goto out; + } + /* Select averaging for all channels if do_avg is set */ + if (req->do_avg) { + ret = twl_i2c_write_u16(TWL4030_MODULE_MADC, req->channels, + method->avg); + if (ret) { + dev_err(twl4030_madc->dev, + "unable to write avg register 0x%X\n", + method->avg); + goto out; + } + } + /* With RT method we should not be here anymore */ + if (req->method == TWL4030_MADC_RT) { + ret = -EINVAL; + goto out; + } + ret = twl4030_madc_start_conversion(twl4030_madc, req->method); + if (ret < 0) + goto out; + twl4030_madc->requests[req->method].active = true; + /* Wait until conversion is ready (ctrl register returns EOC) */ + ret = twl4030_madc_wait_conversion_ready(twl4030_madc, 5, method->ctrl); + if (ret) { + twl4030_madc->requests[req->method].active = false; + goto out; + } + ret = twl4030_madc_read_channels(twl4030_madc, method->rbase, + req->channels, req->rbuf, req->raw); + twl4030_madc->requests[req->method].active = false; + +out: + mutex_unlock(&twl4030_madc->lock); + + return ret; +} + +/** + * twl4030_madc_set_current_generator() - setup bias current + * + * @madc: pointer to twl4030_madc_data struct + * @chan: can be one of the two values: + * 0 - Enables bias current for main battery type reading + * 1 - Enables bias current for main battery temperature sensing + * @on: enable or disable chan. + * + * Function to enable or disable bias current for + * main battery type reading or temperature sensing + */ +static int twl4030_madc_set_current_generator(struct twl4030_madc_data *madc, + int chan, int on) +{ + int ret; + int regmask; + u8 regval; + + ret = twl_i2c_read_u8(TWL_MODULE_MAIN_CHARGE, + ®val, TWL4030_BCI_BCICTL1); + if (ret) { + dev_err(madc->dev, "unable to read BCICTL1 reg 0x%X", + TWL4030_BCI_BCICTL1); + return ret; + } + + regmask = chan ? TWL4030_BCI_ITHEN : TWL4030_BCI_TYPEN; + if (on) + regval |= regmask; + else + regval &= ~regmask; + + ret = twl_i2c_write_u8(TWL_MODULE_MAIN_CHARGE, + regval, TWL4030_BCI_BCICTL1); + if (ret) { + dev_err(madc->dev, "unable to write BCICTL1 reg 0x%X\n", + TWL4030_BCI_BCICTL1); + return ret; + } + + return 0; +} + +/* + * Function that sets MADC software power on bit to enable MADC + * @madc - pointer to twl4030_madc_data struct + * @on - Enable or disable MADC software power on bit. + * returns error if i2c read/write fails else 0 + */ +static int twl4030_madc_set_power(struct twl4030_madc_data *madc, int on) +{ + u8 regval; + int ret; + + ret = twl_i2c_read_u8(TWL_MODULE_MAIN_CHARGE, + ®val, TWL4030_MADC_CTRL1); + if (ret) { + dev_err(madc->dev, "unable to read madc ctrl1 reg 0x%X\n", + TWL4030_MADC_CTRL1); + return ret; + } + if (on) + regval |= TWL4030_MADC_MADCON; + else + regval &= ~TWL4030_MADC_MADCON; + ret = twl_i2c_write_u8(TWL4030_MODULE_MADC, regval, TWL4030_MADC_CTRL1); + if (ret) { + dev_err(madc->dev, "unable to write madc ctrl1 reg 0x%X\n", + TWL4030_MADC_CTRL1); + return ret; + } + + return 0; +} + +/* + * Initialize MADC and request for threaded irq + */ +static int twl4030_madc_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct twl4030_madc_platform_data *pdata = dev_get_platdata(dev); + struct twl4030_madc_data *madc; + int irq, ret; + u8 regval; + struct iio_dev *iio_dev = NULL; + + if (!pdata && !dev_fwnode(dev)) { + dev_err(&pdev->dev, "neither platform data nor Device Tree node available\n"); + return -EINVAL; + } + + iio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*madc)); + if (!iio_dev) + return -ENOMEM; + + madc = iio_priv(iio_dev); + madc->dev = &pdev->dev; + + iio_dev->name = dev_name(&pdev->dev); + iio_dev->info = &twl4030_madc_iio_info; + iio_dev->modes = INDIO_DIRECT_MODE; + iio_dev->channels = twl4030_madc_iio_channels; + iio_dev->num_channels = ARRAY_SIZE(twl4030_madc_iio_channels); + + /* + * Phoenix provides 2 interrupt lines. The first one is connected to + * the OMAP. The other one can be connected to the other processor such + * as modem. Hence two separate ISR and IMR registers. + */ + if (pdata) + madc->use_second_irq = (pdata->irq_line != 1); + else + madc->use_second_irq = device_property_read_bool(dev, + "ti,system-uses-second-madc-irq"); + + madc->imr = madc->use_second_irq ? TWL4030_MADC_IMR2 : + TWL4030_MADC_IMR1; + madc->isr = madc->use_second_irq ? TWL4030_MADC_ISR2 : + TWL4030_MADC_ISR1; + + ret = twl4030_madc_set_power(madc, 1); + if (ret < 0) + return ret; + ret = twl4030_madc_set_current_generator(madc, 0, 1); + if (ret < 0) + goto err_current_generator; + + ret = twl_i2c_read_u8(TWL_MODULE_MAIN_CHARGE, + ®val, TWL4030_BCI_BCICTL1); + if (ret) { + dev_err(&pdev->dev, "unable to read reg BCI CTL1 0x%X\n", + TWL4030_BCI_BCICTL1); + goto err_i2c; + } + regval |= TWL4030_BCI_MESBAT; + ret = twl_i2c_write_u8(TWL_MODULE_MAIN_CHARGE, + regval, TWL4030_BCI_BCICTL1); + if (ret) { + dev_err(&pdev->dev, "unable to write reg BCI Ctl1 0x%X\n", + TWL4030_BCI_BCICTL1); + goto err_i2c; + } + + /* Check that MADC clock is on */ + ret = twl_i2c_read_u8(TWL4030_MODULE_INTBR, ®val, TWL4030_REG_GPBR1); + if (ret) { + dev_err(&pdev->dev, "unable to read reg GPBR1 0x%X\n", + TWL4030_REG_GPBR1); + goto err_i2c; + } + + /* If MADC clk is not on, turn it on */ + if (!(regval & TWL4030_GPBR1_MADC_HFCLK_EN)) { + dev_info(&pdev->dev, "clk disabled, enabling\n"); + regval |= TWL4030_GPBR1_MADC_HFCLK_EN; + ret = twl_i2c_write_u8(TWL4030_MODULE_INTBR, regval, + TWL4030_REG_GPBR1); + if (ret) { + dev_err(&pdev->dev, "unable to write reg GPBR1 0x%X\n", + TWL4030_REG_GPBR1); + goto err_i2c; + } + } + + platform_set_drvdata(pdev, iio_dev); + mutex_init(&madc->lock); + + irq = platform_get_irq(pdev, 0); + ret = devm_request_threaded_irq(&pdev->dev, irq, NULL, + twl4030_madc_threaded_irq_handler, + IRQF_TRIGGER_RISING | IRQF_ONESHOT, + "twl4030_madc", madc); + if (ret) { + dev_err(&pdev->dev, "could not request irq\n"); + goto err_i2c; + } + twl4030_madc = madc; + + /* Configure MADC[3:6] */ + ret = twl_i2c_read_u8(TWL_MODULE_USB, ®val, + TWL4030_USB_CARKIT_ANA_CTRL); + if (ret) { + dev_err(&pdev->dev, "unable to read reg CARKIT_ANA_CTRL 0x%X\n", + TWL4030_USB_CARKIT_ANA_CTRL); + goto err_i2c; + } + regval |= TWL4030_USB_SEL_MADC_MCPC; + ret = twl_i2c_write_u8(TWL_MODULE_USB, regval, + TWL4030_USB_CARKIT_ANA_CTRL); + if (ret) { + dev_err(&pdev->dev, "unable to write reg CARKIT_ANA_CTRL 0x%X\n", + TWL4030_USB_CARKIT_ANA_CTRL); + goto err_i2c; + } + + /* Enable 3v1 bias regulator for MADC[3:6] */ + madc->usb3v1 = devm_regulator_get(madc->dev, "vusb3v1"); + if (IS_ERR(madc->usb3v1)) { + ret = -ENODEV; + goto err_i2c; + } + + ret = regulator_enable(madc->usb3v1); + if (ret) { + dev_err(madc->dev, "could not enable 3v1 bias regulator\n"); + goto err_i2c; + } + + ret = iio_device_register(iio_dev); + if (ret) { + dev_err(&pdev->dev, "could not register iio device\n"); + goto err_usb3v1; + } + + return 0; + +err_usb3v1: + regulator_disable(madc->usb3v1); +err_i2c: + twl4030_madc_set_current_generator(madc, 0, 0); +err_current_generator: + twl4030_madc_set_power(madc, 0); + return ret; +} + +static void twl4030_madc_remove(struct platform_device *pdev) +{ + struct iio_dev *iio_dev = platform_get_drvdata(pdev); + struct twl4030_madc_data *madc = iio_priv(iio_dev); + + iio_device_unregister(iio_dev); + + twl4030_madc_set_current_generator(madc, 0, 0); + twl4030_madc_set_power(madc, 0); + + regulator_disable(madc->usb3v1); +} + +static const struct of_device_id twl_madc_of_match[] = { + { .compatible = "ti,twl4030-madc", }, + { } +}; +MODULE_DEVICE_TABLE(of, twl_madc_of_match); + +static struct platform_driver twl4030_madc_driver = { + .probe = twl4030_madc_probe, + .remove = twl4030_madc_remove, + .driver = { + .name = "twl4030_madc", + .of_match_table = twl_madc_of_match, + }, +}; + +module_platform_driver(twl4030_madc_driver); + +MODULE_DESCRIPTION("TWL4030 ADC driver"); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("J Keerthy"); +MODULE_ALIAS("platform:twl4030_madc"); diff --git a/drivers/iio/adc/twl6030-gpadc.c b/drivers/iio/adc/twl6030-gpadc.c new file mode 100644 index 000000000..31b1c01ba --- /dev/null +++ b/drivers/iio/adc/twl6030-gpadc.c @@ -0,0 +1,1020 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * TWL6030 GPADC module driver + * + * Copyright (C) 2009-2013 Texas Instruments Inc. + * Nishant Kamat + * Balaji T K + * Graeme Gregory + * Girish S Ghongdemath + * Ambresh K + * Oleksandr Kozaruk + */ +#include +#include +#include +#include +#include +#include +#include +#include + +#define DRIVER_NAME "twl6030_gpadc" + +/* + * twl6030 per TRM has 17 channels, and twl6032 has 19 channels + * 2 test network channels are not used, + * 2 die temperature channels are not used either, as it is not + * defined how to convert ADC value to temperature + */ +#define TWL6030_GPADC_USED_CHANNELS 13 +#define TWL6030_GPADC_MAX_CHANNELS 15 +#define TWL6032_GPADC_USED_CHANNELS 15 +#define TWL6032_GPADC_MAX_CHANNELS 19 +#define TWL6030_GPADC_NUM_TRIM_REGS 16 + +#define TWL6030_GPADC_CTRL_P1 0x05 + +#define TWL6032_GPADC_GPSELECT_ISB 0x07 +#define TWL6032_GPADC_CTRL_P1 0x08 + +#define TWL6032_GPADC_GPCH0_LSB 0x0d +#define TWL6032_GPADC_GPCH0_MSB 0x0e + +#define TWL6030_GPADC_CTRL_P1_SP1 BIT(3) + +#define TWL6030_GPADC_GPCH0_LSB (0x29) + +#define TWL6030_GPADC_RT_SW1_EOC_MASK BIT(5) + +#define TWL6030_GPADC_TRIM1 0xCD + +#define TWL6030_REG_TOGGLE1 0x90 +#define TWL6030_GPADCS BIT(1) +#define TWL6030_GPADCR BIT(0) + +#define USB_VBUS_CTRL_SET 0x04 +#define USB_ID_CTRL_SET 0x06 + +#define TWL6030_MISC1 0xE4 +#define VBUS_MEAS 0x01 +#define ID_MEAS 0x01 + +#define VAC_MEAS 0x04 +#define VBAT_MEAS 0x02 +#define BB_MEAS 0x01 + + +/** + * struct twl6030_chnl_calib - channel calibration + * @gain: slope coefficient for ideal curve + * @gain_error: gain error + * @offset_error: offset of the real curve + */ +struct twl6030_chnl_calib { + s32 gain; + s32 gain_error; + s32 offset_error; +}; + +/** + * struct twl6030_ideal_code - GPADC calibration parameters + * GPADC is calibrated in two points: close to the beginning and + * to the and of the measurable input range + * + * @channel: channel number + * @code1: ideal code for the input at the beginning + * @code2: ideal code for at the end of the range + * @volt1: voltage input at the beginning(low voltage) + * @volt2: voltage input at the end(high voltage) + */ +struct twl6030_ideal_code { + int channel; + u16 code1; + u16 code2; + u16 volt1; + u16 volt2; +}; + +struct twl6030_gpadc_data; + +/** + * struct twl6030_gpadc_platform_data - platform specific data + * @nchannels: number of GPADC channels + * @iio_channels: iio channels + * @ideal: pointer to calibration parameters + * @start_conversion: pointer to ADC start conversion function + * @channel_to_reg: pointer to ADC function to convert channel to + * register address for reading conversion result + * @calibrate: pointer to calibration function + */ +struct twl6030_gpadc_platform_data { + const int nchannels; + const struct iio_chan_spec *iio_channels; + const struct twl6030_ideal_code *ideal; + int (*start_conversion)(int channel); + u8 (*channel_to_reg)(int channel); + int (*calibrate)(struct twl6030_gpadc_data *gpadc); +}; + +/** + * struct twl6030_gpadc_data - GPADC data + * @dev: device pointer + * @lock: mutual exclusion lock for the structure + * @irq_complete: completion to signal end of conversion + * @twl6030_cal_tbl: pointer to calibration data for each + * channel with gain error and offset + * @pdata: pointer to device specific data + */ +struct twl6030_gpadc_data { + struct device *dev; + struct mutex lock; + struct completion irq_complete; + struct twl6030_chnl_calib *twl6030_cal_tbl; + const struct twl6030_gpadc_platform_data *pdata; +}; + +/* + * channels 11, 12, 13, 15 and 16 have no calibration data + * calibration offset is same for channels 1, 3, 4, 5 + * + * The data is taken from GPADC_TRIM registers description. + * GPADC_TRIM registers keep difference between the code measured + * at volt1 and volt2 input voltages and corresponding code1 and code2 + */ +static const struct twl6030_ideal_code + twl6030_ideal[TWL6030_GPADC_USED_CHANNELS] = { + [0] = { /* ch 0, external, battery type, resistor value */ + .channel = 0, + .code1 = 116, + .code2 = 745, + .volt1 = 141, + .volt2 = 910, + }, + [1] = { /* ch 1, external, battery temperature, NTC resistor value */ + .channel = 1, + .code1 = 82, + .code2 = 900, + .volt1 = 100, + .volt2 = 1100, + }, + [2] = { /* ch 2, external, audio accessory/general purpose */ + .channel = 2, + .code1 = 55, + .code2 = 818, + .volt1 = 101, + .volt2 = 1499, + }, + [3] = { /* ch 3, external, general purpose */ + .channel = 3, + .code1 = 82, + .code2 = 900, + .volt1 = 100, + .volt2 = 1100, + }, + [4] = { /* ch 4, external, temperature measurement/general purpose */ + .channel = 4, + .code1 = 82, + .code2 = 900, + .volt1 = 100, + .volt2 = 1100, + }, + [5] = { /* ch 5, external, general purpose */ + .channel = 5, + .code1 = 82, + .code2 = 900, + .volt1 = 100, + .volt2 = 1100, + }, + [6] = { /* ch 6, external, general purpose */ + .channel = 6, + .code1 = 82, + .code2 = 900, + .volt1 = 100, + .volt2 = 1100, + }, + [7] = { /* ch 7, internal, main battery */ + .channel = 7, + .code1 = 614, + .code2 = 941, + .volt1 = 3001, + .volt2 = 4599, + }, + [8] = { /* ch 8, internal, backup battery */ + .channel = 8, + .code1 = 82, + .code2 = 688, + .volt1 = 501, + .volt2 = 4203, + }, + [9] = { /* ch 9, internal, external charger input */ + .channel = 9, + .code1 = 182, + .code2 = 818, + .volt1 = 2001, + .volt2 = 8996, + }, + [10] = { /* ch 10, internal, VBUS */ + .channel = 10, + .code1 = 149, + .code2 = 818, + .volt1 = 1001, + .volt2 = 5497, + }, + [11] = { /* ch 11, internal, VBUS charging current */ + .channel = 11, + }, + /* ch 12, internal, Die temperature */ + /* ch 13, internal, Die temperature */ + [12] = { /* ch 14, internal, USB ID line */ + .channel = 14, + .code1 = 48, + .code2 = 714, + .volt1 = 323, + .volt2 = 4800, + }, +}; + +static const struct twl6030_ideal_code + twl6032_ideal[TWL6032_GPADC_USED_CHANNELS] = { + [0] = { /* ch 0, external, battery type, resistor value */ + .channel = 0, + .code1 = 1441, + .code2 = 3276, + .volt1 = 440, + .volt2 = 1000, + }, + [1] = { /* ch 1, external, battery temperature, NTC resistor value */ + .channel = 1, + .code1 = 1441, + .code2 = 3276, + .volt1 = 440, + .volt2 = 1000, + }, + [2] = { /* ch 2, external, audio accessory/general purpose */ + .channel = 2, + .code1 = 1441, + .code2 = 3276, + .volt1 = 660, + .volt2 = 1500, + }, + [3] = { /* ch 3, external, temperature with external diode/general + purpose */ + .channel = 3, + .code1 = 1441, + .code2 = 3276, + .volt1 = 440, + .volt2 = 1000, + }, + [4] = { /* ch 4, external, temperature measurement/general purpose */ + .channel = 4, + .code1 = 1441, + .code2 = 3276, + .volt1 = 440, + .volt2 = 1000, + }, + [5] = { /* ch 5, external, general purpose */ + .channel = 5, + .code1 = 1441, + .code2 = 3276, + .volt1 = 440, + .volt2 = 1000, + }, + [6] = { /* ch 6, external, general purpose */ + .channel = 6, + .code1 = 1441, + .code2 = 3276, + .volt1 = 440, + .volt2 = 1000, + }, + [7] = { /* ch7, internal, system supply */ + .channel = 7, + .code1 = 1441, + .code2 = 3276, + .volt1 = 2200, + .volt2 = 5000, + }, + [8] = { /* ch8, internal, backup battery */ + .channel = 8, + .code1 = 1441, + .code2 = 3276, + .volt1 = 2200, + .volt2 = 5000, + }, + [9] = { /* ch 9, internal, external charger input */ + .channel = 9, + .code1 = 1441, + .code2 = 3276, + .volt1 = 3960, + .volt2 = 9000, + }, + [10] = { /* ch10, internal, VBUS */ + .channel = 10, + .code1 = 150, + .code2 = 751, + .volt1 = 1000, + .volt2 = 5000, + }, + [11] = { /* ch 11, internal, VBUS DC-DC output current */ + .channel = 11, + .code1 = 1441, + .code2 = 3276, + .volt1 = 660, + .volt2 = 1500, + }, + /* ch 12, internal, Die temperature */ + /* ch 13, internal, Die temperature */ + [12] = { /* ch 14, internal, USB ID line */ + .channel = 14, + .code1 = 1441, + .code2 = 3276, + .volt1 = 2420, + .volt2 = 5500, + }, + /* ch 15, internal, test network */ + /* ch 16, internal, test network */ + [13] = { /* ch 17, internal, battery charging current */ + .channel = 17, + }, + [14] = { /* ch 18, internal, battery voltage */ + .channel = 18, + .code1 = 1441, + .code2 = 3276, + .volt1 = 2200, + .volt2 = 5000, + }, +}; + +static inline int twl6030_gpadc_write(u8 reg, u8 val) +{ + return twl_i2c_write_u8(TWL6030_MODULE_GPADC, val, reg); +} + +static inline int twl6030_gpadc_read(u8 reg, u8 *val) +{ + + return twl_i2c_read(TWL6030_MODULE_GPADC, val, reg, 2); +} + +static int twl6030_gpadc_enable_irq(u8 mask) +{ + int ret; + + ret = twl6030_interrupt_unmask(mask, REG_INT_MSK_LINE_B); + if (ret < 0) + return ret; + + ret = twl6030_interrupt_unmask(mask, REG_INT_MSK_STS_B); + + return ret; +} + +static void twl6030_gpadc_disable_irq(u8 mask) +{ + twl6030_interrupt_mask(mask, REG_INT_MSK_LINE_B); + twl6030_interrupt_mask(mask, REG_INT_MSK_STS_B); +} + +static irqreturn_t twl6030_gpadc_irq_handler(int irq, void *indio_dev) +{ + struct twl6030_gpadc_data *gpadc = iio_priv(indio_dev); + + complete(&gpadc->irq_complete); + + return IRQ_HANDLED; +} + +static int twl6030_start_conversion(int channel) +{ + return twl6030_gpadc_write(TWL6030_GPADC_CTRL_P1, + TWL6030_GPADC_CTRL_P1_SP1); +} + +static int twl6032_start_conversion(int channel) +{ + int ret; + + ret = twl6030_gpadc_write(TWL6032_GPADC_GPSELECT_ISB, channel); + if (ret) + return ret; + + return twl6030_gpadc_write(TWL6032_GPADC_CTRL_P1, + TWL6030_GPADC_CTRL_P1_SP1); +} + +static u8 twl6030_channel_to_reg(int channel) +{ + return TWL6030_GPADC_GPCH0_LSB + 2 * channel; +} + +static u8 twl6032_channel_to_reg(int channel) +{ + /* + * for any prior chosen channel, when the conversion is ready + * the result is available in GPCH0_LSB, GPCH0_MSB. + */ + + return TWL6032_GPADC_GPCH0_LSB; +} + +static int twl6030_gpadc_lookup(const struct twl6030_ideal_code *ideal, + int channel, int size) +{ + int i; + + for (i = 0; i < size; i++) + if (ideal[i].channel == channel) + break; + + return i; +} + +static int twl6030_channel_calibrated(const struct twl6030_gpadc_platform_data + *pdata, int channel) +{ + const struct twl6030_ideal_code *ideal = pdata->ideal; + int i; + + i = twl6030_gpadc_lookup(ideal, channel, pdata->nchannels); + /* not calibrated channels have 0 in all structure members */ + return pdata->ideal[i].code2; +} + +static int twl6030_gpadc_make_correction(struct twl6030_gpadc_data *gpadc, + int channel, int raw_code) +{ + const struct twl6030_ideal_code *ideal = gpadc->pdata->ideal; + int corrected_code; + int i; + + i = twl6030_gpadc_lookup(ideal, channel, gpadc->pdata->nchannels); + corrected_code = ((raw_code * 1000) - + gpadc->twl6030_cal_tbl[i].offset_error) / + gpadc->twl6030_cal_tbl[i].gain_error; + + return corrected_code; +} + +static int twl6030_gpadc_get_raw(struct twl6030_gpadc_data *gpadc, + int channel, int *res) +{ + u8 reg = gpadc->pdata->channel_to_reg(channel); + __le16 val; + int raw_code; + int ret; + + ret = twl6030_gpadc_read(reg, (u8 *)&val); + if (ret) { + dev_dbg(gpadc->dev, "unable to read register 0x%X\n", reg); + return ret; + } + + raw_code = le16_to_cpu(val); + dev_dbg(gpadc->dev, "GPADC raw code: %d", raw_code); + + if (twl6030_channel_calibrated(gpadc->pdata, channel)) + *res = twl6030_gpadc_make_correction(gpadc, channel, raw_code); + else + *res = raw_code; + + return ret; +} + +static int twl6030_gpadc_get_processed(struct twl6030_gpadc_data *gpadc, + int channel, int *val) +{ + const struct twl6030_ideal_code *ideal = gpadc->pdata->ideal; + int corrected_code; + int channel_value; + int i; + int ret; + + ret = twl6030_gpadc_get_raw(gpadc, channel, &corrected_code); + if (ret) + return ret; + + i = twl6030_gpadc_lookup(ideal, channel, gpadc->pdata->nchannels); + channel_value = corrected_code * + gpadc->twl6030_cal_tbl[i].gain; + + /* Shift back into mV range */ + channel_value /= 1000; + + dev_dbg(gpadc->dev, "GPADC corrected code: %d", corrected_code); + dev_dbg(gpadc->dev, "GPADC value: %d", channel_value); + + *val = channel_value; + + return ret; +} + +static int twl6030_gpadc_read_raw(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + int *val, int *val2, long mask) +{ + struct twl6030_gpadc_data *gpadc = iio_priv(indio_dev); + int ret; + long time_left; + + mutex_lock(&gpadc->lock); + + ret = gpadc->pdata->start_conversion(chan->channel); + if (ret) { + dev_err(gpadc->dev, "failed to start conversion\n"); + goto err; + } + /* wait for conversion to complete */ + time_left = wait_for_completion_interruptible_timeout( + &gpadc->irq_complete, msecs_to_jiffies(5000)); + if (time_left == 0) { + ret = -ETIMEDOUT; + goto err; + } else if (time_left < 0) { + ret = -EINTR; + goto err; + } + + switch (mask) { + case IIO_CHAN_INFO_RAW: + ret = twl6030_gpadc_get_raw(gpadc, chan->channel, val); + ret = ret ? -EIO : IIO_VAL_INT; + break; + + case IIO_CHAN_INFO_PROCESSED: + ret = twl6030_gpadc_get_processed(gpadc, chan->channel, val); + ret = ret ? -EIO : IIO_VAL_INT; + break; + + default: + break; + } +err: + mutex_unlock(&gpadc->lock); + + return ret; +} + +/* + * The GPADC channels are calibrated using a two point calibration method. + * The channels measured with two known values: volt1 and volt2, and + * ideal corresponding output codes are known: code1, code2. + * The difference(d1, d2) between ideal and measured codes stored in trim + * registers. + * The goal is to find offset and gain of the real curve for each calibrated + * channel. + * gain: k = 1 + ((d2 - d1) / (x2 - x1)) + * offset: b = d1 + (k - 1) * x1 + */ +static void twl6030_calibrate_channel(struct twl6030_gpadc_data *gpadc, + int channel, int d1, int d2) +{ + int b, k, gain, x1, x2, i; + const struct twl6030_ideal_code *ideal = gpadc->pdata->ideal; + + i = twl6030_gpadc_lookup(ideal, channel, gpadc->pdata->nchannels); + + /* Gain */ + gain = ((ideal[i].volt2 - ideal[i].volt1) * 1000) / + (ideal[i].code2 - ideal[i].code1); + + x1 = ideal[i].code1; + x2 = ideal[i].code2; + + /* k - real curve gain */ + k = 1000 + (((d2 - d1) * 1000) / (x2 - x1)); + + /* b - offset of the real curve gain */ + b = (d1 * 1000) - (k - 1000) * x1; + + gpadc->twl6030_cal_tbl[i].gain = gain; + gpadc->twl6030_cal_tbl[i].gain_error = k; + gpadc->twl6030_cal_tbl[i].offset_error = b; + + dev_dbg(gpadc->dev, "GPADC d1 for Chn: %d = %d\n", channel, d1); + dev_dbg(gpadc->dev, "GPADC d2 for Chn: %d = %d\n", channel, d2); + dev_dbg(gpadc->dev, "GPADC x1 for Chn: %d = %d\n", channel, x1); + dev_dbg(gpadc->dev, "GPADC x2 for Chn: %d = %d\n", channel, x2); + dev_dbg(gpadc->dev, "GPADC Gain for Chn: %d = %d\n", channel, gain); + dev_dbg(gpadc->dev, "GPADC k for Chn: %d = %d\n", channel, k); + dev_dbg(gpadc->dev, "GPADC b for Chn: %d = %d\n", channel, b); +} + +static inline int twl6030_gpadc_get_trim_offset(s8 d) +{ + /* + * XXX NOTE! + * bit 0 - sign, bit 7 - reserved, 6..1 - trim value + * though, the documentation states that trim value + * is absolute value, the correct conversion results are + * obtained if the value is interpreted as 2's complement. + */ + __u32 temp = ((d & 0x7f) >> 1) | ((d & 1) << 6); + + return sign_extend32(temp, 6); +} + +static int twl6030_calibration(struct twl6030_gpadc_data *gpadc) +{ + int ret; + int chn; + u8 trim_regs[TWL6030_GPADC_NUM_TRIM_REGS]; + s8 d1, d2; + + /* + * for calibration two measurements have been performed at + * factory, for some channels, during the production test and + * have been stored in registers. This two stored values are + * used to correct the measurements. The values represent + * offsets for the given input from the output on ideal curve. + */ + ret = twl_i2c_read(TWL6030_MODULE_ID2, trim_regs, + TWL6030_GPADC_TRIM1, TWL6030_GPADC_NUM_TRIM_REGS); + if (ret < 0) { + dev_err(gpadc->dev, "calibration failed\n"); + return ret; + } + + for (chn = 0; chn < TWL6030_GPADC_MAX_CHANNELS; chn++) { + + switch (chn) { + case 0: + d1 = trim_regs[0]; + d2 = trim_regs[1]; + break; + case 1: + case 3: + case 4: + case 5: + case 6: + d1 = trim_regs[4]; + d2 = trim_regs[5]; + break; + case 2: + d1 = trim_regs[12]; + d2 = trim_regs[13]; + break; + case 7: + d1 = trim_regs[6]; + d2 = trim_regs[7]; + break; + case 8: + d1 = trim_regs[2]; + d2 = trim_regs[3]; + break; + case 9: + d1 = trim_regs[8]; + d2 = trim_regs[9]; + break; + case 10: + d1 = trim_regs[10]; + d2 = trim_regs[11]; + break; + case 14: + d1 = trim_regs[14]; + d2 = trim_regs[15]; + break; + default: + continue; + } + + d1 = twl6030_gpadc_get_trim_offset(d1); + d2 = twl6030_gpadc_get_trim_offset(d2); + + twl6030_calibrate_channel(gpadc, chn, d1, d2); + } + + return 0; +} + +static int twl6032_get_trim_value(u8 *trim_regs, unsigned int reg0, + unsigned int reg1, unsigned int mask0, unsigned int mask1, + unsigned int shift0) +{ + int val; + + val = (trim_regs[reg0] & mask0) << shift0; + val |= (trim_regs[reg1] & mask1) >> 1; + if (trim_regs[reg1] & 0x01) + val = -val; + + return val; +} + +static int twl6032_calibration(struct twl6030_gpadc_data *gpadc) +{ + int chn, d1 = 0, d2 = 0, temp; + u8 trim_regs[TWL6030_GPADC_NUM_TRIM_REGS]; + int ret; + + ret = twl_i2c_read(TWL6030_MODULE_ID2, trim_regs, + TWL6030_GPADC_TRIM1, TWL6030_GPADC_NUM_TRIM_REGS); + if (ret < 0) { + dev_err(gpadc->dev, "calibration failed\n"); + return ret; + } + + /* + * Loop to calculate the value needed for returning voltages from + * GPADC not values. + * + * gain is calculated to 3 decimal places fixed point. + */ + for (chn = 0; chn < TWL6032_GPADC_MAX_CHANNELS; chn++) { + + switch (chn) { + case 0: + case 1: + case 2: + case 3: + case 4: + case 5: + case 6: + case 11: + case 14: + d1 = twl6032_get_trim_value(trim_regs, 2, 0, 0x1f, + 0x06, 2); + d2 = twl6032_get_trim_value(trim_regs, 3, 1, 0x3f, + 0x06, 2); + break; + case 8: + temp = twl6032_get_trim_value(trim_regs, 2, 0, 0x1f, + 0x06, 2); + d1 = temp + twl6032_get_trim_value(trim_regs, 7, 6, + 0x18, 0x1E, 1); + + temp = twl6032_get_trim_value(trim_regs, 3, 1, 0x3F, + 0x06, 2); + d2 = temp + twl6032_get_trim_value(trim_regs, 9, 7, + 0x1F, 0x06, 2); + break; + case 9: + temp = twl6032_get_trim_value(trim_regs, 2, 0, 0x1f, + 0x06, 2); + d1 = temp + twl6032_get_trim_value(trim_regs, 13, 11, + 0x18, 0x1E, 1); + + temp = twl6032_get_trim_value(trim_regs, 3, 1, 0x3f, + 0x06, 2); + d2 = temp + twl6032_get_trim_value(trim_regs, 15, 13, + 0x1F, 0x06, 1); + break; + case 10: + d1 = twl6032_get_trim_value(trim_regs, 10, 8, 0x0f, + 0x0E, 3); + d2 = twl6032_get_trim_value(trim_regs, 14, 12, 0x0f, + 0x0E, 3); + break; + case 7: + case 18: + temp = twl6032_get_trim_value(trim_regs, 2, 0, 0x1f, + 0x06, 2); + + d1 = (trim_regs[4] & 0x7E) >> 1; + if (trim_regs[4] & 0x01) + d1 = -d1; + d1 += temp; + + temp = twl6032_get_trim_value(trim_regs, 3, 1, 0x3f, + 0x06, 2); + + d2 = (trim_regs[5] & 0xFE) >> 1; + if (trim_regs[5] & 0x01) + d2 = -d2; + + d2 += temp; + break; + default: + /* No data for other channels */ + continue; + } + + twl6030_calibrate_channel(gpadc, chn, d1, d2); + } + + return 0; +} + +#define TWL6030_GPADC_CHAN(chn, _type, chan_info) { \ + .type = _type, \ + .channel = chn, \ + .info_mask_separate = BIT(chan_info), \ + .indexed = 1, \ +} + +static const struct iio_chan_spec twl6030_gpadc_iio_channels[] = { + TWL6030_GPADC_CHAN(0, IIO_VOLTAGE, IIO_CHAN_INFO_PROCESSED), + TWL6030_GPADC_CHAN(1, IIO_TEMP, IIO_CHAN_INFO_RAW), + TWL6030_GPADC_CHAN(2, IIO_VOLTAGE, IIO_CHAN_INFO_PROCESSED), + TWL6030_GPADC_CHAN(3, IIO_VOLTAGE, IIO_CHAN_INFO_PROCESSED), + TWL6030_GPADC_CHAN(4, IIO_TEMP, IIO_CHAN_INFO_RAW), + TWL6030_GPADC_CHAN(5, IIO_VOLTAGE, IIO_CHAN_INFO_PROCESSED), + TWL6030_GPADC_CHAN(6, IIO_VOLTAGE, IIO_CHAN_INFO_PROCESSED), + TWL6030_GPADC_CHAN(7, IIO_VOLTAGE, IIO_CHAN_INFO_PROCESSED), + TWL6030_GPADC_CHAN(8, IIO_VOLTAGE, IIO_CHAN_INFO_PROCESSED), + TWL6030_GPADC_CHAN(9, IIO_VOLTAGE, IIO_CHAN_INFO_PROCESSED), + TWL6030_GPADC_CHAN(10, IIO_VOLTAGE, IIO_CHAN_INFO_PROCESSED), + TWL6030_GPADC_CHAN(11, IIO_VOLTAGE, IIO_CHAN_INFO_RAW), + TWL6030_GPADC_CHAN(14, IIO_VOLTAGE, IIO_CHAN_INFO_PROCESSED), +}; + +static const struct iio_chan_spec twl6032_gpadc_iio_channels[] = { + TWL6030_GPADC_CHAN(0, IIO_VOLTAGE, IIO_CHAN_INFO_PROCESSED), + TWL6030_GPADC_CHAN(1, IIO_TEMP, IIO_CHAN_INFO_RAW), + TWL6030_GPADC_CHAN(2, IIO_VOLTAGE, IIO_CHAN_INFO_PROCESSED), + TWL6030_GPADC_CHAN(3, IIO_VOLTAGE, IIO_CHAN_INFO_PROCESSED), + TWL6030_GPADC_CHAN(4, IIO_TEMP, IIO_CHAN_INFO_RAW), + TWL6030_GPADC_CHAN(5, IIO_VOLTAGE, IIO_CHAN_INFO_PROCESSED), + TWL6030_GPADC_CHAN(6, IIO_VOLTAGE, IIO_CHAN_INFO_PROCESSED), + TWL6030_GPADC_CHAN(7, IIO_VOLTAGE, IIO_CHAN_INFO_PROCESSED), + TWL6030_GPADC_CHAN(8, IIO_VOLTAGE, IIO_CHAN_INFO_PROCESSED), + TWL6030_GPADC_CHAN(9, IIO_VOLTAGE, IIO_CHAN_INFO_PROCESSED), + TWL6030_GPADC_CHAN(10, IIO_VOLTAGE, IIO_CHAN_INFO_PROCESSED), + TWL6030_GPADC_CHAN(11, IIO_VOLTAGE, IIO_CHAN_INFO_PROCESSED), + TWL6030_GPADC_CHAN(14, IIO_VOLTAGE, IIO_CHAN_INFO_PROCESSED), + TWL6030_GPADC_CHAN(17, IIO_VOLTAGE, IIO_CHAN_INFO_RAW), + TWL6030_GPADC_CHAN(18, IIO_VOLTAGE, IIO_CHAN_INFO_PROCESSED), +}; + +static const struct iio_info twl6030_gpadc_iio_info = { + .read_raw = &twl6030_gpadc_read_raw, +}; + +static const struct twl6030_gpadc_platform_data twl6030_pdata = { + .iio_channels = twl6030_gpadc_iio_channels, + .nchannels = TWL6030_GPADC_USED_CHANNELS, + .ideal = twl6030_ideal, + .start_conversion = twl6030_start_conversion, + .channel_to_reg = twl6030_channel_to_reg, + .calibrate = twl6030_calibration, +}; + +static const struct twl6030_gpadc_platform_data twl6032_pdata = { + .iio_channels = twl6032_gpadc_iio_channels, + .nchannels = TWL6032_GPADC_USED_CHANNELS, + .ideal = twl6032_ideal, + .start_conversion = twl6032_start_conversion, + .channel_to_reg = twl6032_channel_to_reg, + .calibrate = twl6032_calibration, +}; + +static const struct of_device_id of_twl6030_match_tbl[] = { + { + .compatible = "ti,twl6030-gpadc", + .data = &twl6030_pdata, + }, + { + .compatible = "ti,twl6032-gpadc", + .data = &twl6032_pdata, + }, + { } +}; +MODULE_DEVICE_TABLE(of, of_twl6030_match_tbl); + +static int twl6030_gpadc_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct twl6030_gpadc_data *gpadc; + const struct twl6030_gpadc_platform_data *pdata; + struct iio_dev *indio_dev; + int irq; + int ret; + + pdata = device_get_match_data(&pdev->dev); + if (!pdata) + return -EINVAL; + + indio_dev = devm_iio_device_alloc(dev, sizeof(*gpadc)); + if (!indio_dev) + return -ENOMEM; + + gpadc = iio_priv(indio_dev); + + gpadc->twl6030_cal_tbl = devm_kcalloc(dev, + pdata->nchannels, + sizeof(*gpadc->twl6030_cal_tbl), + GFP_KERNEL); + if (!gpadc->twl6030_cal_tbl) + return -ENOMEM; + + gpadc->dev = dev; + gpadc->pdata = pdata; + + platform_set_drvdata(pdev, indio_dev); + mutex_init(&gpadc->lock); + init_completion(&gpadc->irq_complete); + + ret = pdata->calibrate(gpadc); + if (ret < 0) { + dev_err(dev, "failed to read calibration registers\n"); + return ret; + } + + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return irq; + + ret = devm_request_threaded_irq(dev, irq, NULL, + twl6030_gpadc_irq_handler, + IRQF_ONESHOT, "twl6030_gpadc", indio_dev); + if (ret) + return ret; + + ret = twl6030_gpadc_enable_irq(TWL6030_GPADC_RT_SW1_EOC_MASK); + if (ret < 0) { + dev_err(dev, "failed to enable GPADC interrupt\n"); + return ret; + } + + ret = twl_i2c_write_u8(TWL6030_MODULE_ID1, TWL6030_GPADCS, + TWL6030_REG_TOGGLE1); + if (ret < 0) { + dev_err(dev, "failed to enable GPADC module\n"); + return ret; + } + + ret = twl_i2c_write_u8(TWL_MODULE_USB, VBUS_MEAS, USB_VBUS_CTRL_SET); + if (ret < 0) { + dev_err(dev, "failed to wire up inputs\n"); + return ret; + } + + ret = twl_i2c_write_u8(TWL_MODULE_USB, ID_MEAS, USB_ID_CTRL_SET); + if (ret < 0) { + dev_err(dev, "failed to wire up inputs\n"); + return ret; + } + + ret = twl_i2c_write_u8(TWL6030_MODULE_ID0, + VBAT_MEAS | BB_MEAS | VAC_MEAS, + TWL6030_MISC1); + if (ret < 0) { + dev_err(dev, "failed to wire up inputs\n"); + return ret; + } + + indio_dev->name = DRIVER_NAME; + indio_dev->info = &twl6030_gpadc_iio_info; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = pdata->iio_channels; + indio_dev->num_channels = pdata->nchannels; + + return iio_device_register(indio_dev); +} + +static void twl6030_gpadc_remove(struct platform_device *pdev) +{ + struct iio_dev *indio_dev = platform_get_drvdata(pdev); + + twl6030_gpadc_disable_irq(TWL6030_GPADC_RT_SW1_EOC_MASK); + iio_device_unregister(indio_dev); +} + +static int twl6030_gpadc_suspend(struct device *pdev) +{ + int ret; + + ret = twl_i2c_write_u8(TWL6030_MODULE_ID1, TWL6030_GPADCR, + TWL6030_REG_TOGGLE1); + if (ret) + dev_err(pdev, "error resetting GPADC (%d)!\n", ret); + + return 0; +}; + +static int twl6030_gpadc_resume(struct device *pdev) +{ + int ret; + + ret = twl_i2c_write_u8(TWL6030_MODULE_ID1, TWL6030_GPADCS, + TWL6030_REG_TOGGLE1); + if (ret) + dev_err(pdev, "error setting GPADC (%d)!\n", ret); + + return 0; +}; + +static DEFINE_SIMPLE_DEV_PM_OPS(twl6030_gpadc_pm_ops, twl6030_gpadc_suspend, + twl6030_gpadc_resume); + +static struct platform_driver twl6030_gpadc_driver = { + .probe = twl6030_gpadc_probe, + .remove = twl6030_gpadc_remove, + .driver = { + .name = DRIVER_NAME, + .pm = pm_sleep_ptr(&twl6030_gpadc_pm_ops), + .of_match_table = of_twl6030_match_tbl, + }, +}; + +module_platform_driver(twl6030_gpadc_driver); + +MODULE_ALIAS("platform:" DRIVER_NAME); +MODULE_AUTHOR("Balaji T K "); +MODULE_AUTHOR("Graeme Gregory "); +MODULE_AUTHOR("Oleksandr Kozaruk +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "versal-sysmon.h" + +/* + * Oversampling ratio values exposed to userspace via IIO. + * Actual number of samples averaged: 1=none, 2=2x, 4=4x, 8=8x, 16=16x. + */ +static const int sysmon_oversampling_avail[] = { 1, 2, 4, 8, 16 }; + +/* TEMP hysteresis mode bit in SYSMON_TEMP_EV_CFG */ +#define SYSMON_TEMP_HYST_MASK BIT(1) + +/* Compute alarm register offset from a channel address */ +#define SYSMON_ALARM_OFFSET(addr) \ + (SYSMON_ALARM_REG + ((addr) / SYSMON_ALARM_BITS_PER_REG) * SYSMON_REG_STRIDE) + +#define SYSMON_CHAN_TEMP(_chan, _address, _name) \ +{ \ + .type = IIO_TEMP, \ + .indexed = 1, \ + .address = _address, \ + .channel = _chan, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_type = \ + BIT(IIO_CHAN_INFO_SCALE) | \ + BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \ + .info_mask_shared_by_type_available = \ + BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \ + .datasheet_name = _name, \ +} + +enum sysmon_alarm_bit { + SYSMON_BIT_ALARM0 = 0, + SYSMON_BIT_ALARM1 = 1, + SYSMON_BIT_ALARM2 = 2, + SYSMON_BIT_ALARM3 = 3, + SYSMON_BIT_ALARM4 = 4, + SYSMON_BIT_TEMP = 9, +}; + +/* Temperature event specification: rising threshold + hysteresis only */ +static const struct iio_event_spec sysmon_temp_events[] = { + { + .type = IIO_EV_TYPE_THRESH, + .dir = IIO_EV_DIR_RISING, + .mask_separate = BIT(IIO_EV_INFO_ENABLE) | + BIT(IIO_EV_INFO_VALUE) | + BIT(IIO_EV_INFO_HYSTERESIS), + }, +}; + +/* Supply event specifications */ +static const struct iio_event_spec sysmon_supply_events[] = { + { + .type = IIO_EV_TYPE_THRESH, + .dir = IIO_EV_DIR_RISING, + .mask_separate = BIT(IIO_EV_INFO_VALUE), + }, + { + .type = IIO_EV_TYPE_THRESH, + .dir = IIO_EV_DIR_FALLING, + .mask_separate = BIT(IIO_EV_INFO_VALUE), + }, + { + .type = IIO_EV_TYPE_THRESH, + .dir = IIO_EV_DIR_EITHER, + .mask_separate = BIT(IIO_EV_INFO_ENABLE), + }, +}; + +/* + * Static temperature channels (always present). + * + * These are hardware-computed aggregate registers across all active + * temperature satellites: + * temp: current max temperature across all active satellites + * min: current min temperature across all active satellites + * max_max: highest peak recorded since last hardware reset + * min_min: lowest trough recorded since last hardware reset + */ +static const struct iio_chan_spec temp_channels[] = { + SYSMON_CHAN_TEMP(0, SYSMON_TEMP_MAX, "temp"), + SYSMON_CHAN_TEMP(1, SYSMON_TEMP_MIN, "min"), + SYSMON_CHAN_TEMP(2, SYSMON_TEMP_MAX_MAX, "max_max"), + SYSMON_CHAN_TEMP(3, SYSMON_TEMP_MIN_MIN, "min_min"), +}; + +static void sysmon_q8p7_to_millicelsius(s16 raw_data, int *val) +{ + *val = (raw_data * MILLIDEGREE_PER_DEGREE) >> SYSMON_FRACTIONAL_SHIFT; +} + +static void sysmon_millicelsius_to_q8p7(u32 *raw_data, int val) +{ + *raw_data = (val << SYSMON_FRACTIONAL_SHIFT) / MILLIDEGREE_PER_DEGREE; +} + +static void sysmon_supply_rawtoprocessed(int raw_data, int *val) +{ + int mantissa, format, exponent; + + mantissa = FIELD_GET(SYSMON_MANTISSA_MASK, raw_data); + exponent = SYSMON_SUPPLY_MANTISSA_BITS - FIELD_GET(SYSMON_MODE_MASK, raw_data); + format = FIELD_GET(SYSMON_FMT_MASK, raw_data); + /* + * When format bit is set the mantissa is two's complement + * (per hardware spec); sign-extend to int for correct arithmetic. + */ + if (format) + mantissa = sign_extend32(mantissa, 15); + + *val = (mantissa * (int)MILLI) >> exponent; +} + +static void sysmon_supply_processedtoraw(int val, u32 reg_val, u32 *raw_data) +{ + int exponent = FIELD_GET(SYSMON_MODE_MASK, reg_val); + int format = FIELD_GET(SYSMON_FMT_MASK, reg_val); + int scale, tmp; + + scale = BIT(SYSMON_SUPPLY_MANTISSA_BITS - exponent); + tmp = (val * scale) / (int)MILLI; + + if (format) + tmp = clamp(tmp, S16_MIN, S16_MAX); + else + tmp = clamp(tmp, 0, U16_MAX); + + *raw_data = (u16)tmp; +} + +static int sysmon_supply_thresh_offset(unsigned long address, enum iio_event_direction dir) +{ + if (dir == IIO_EV_DIR_RISING) + return (address * SYSMON_REG_STRIDE) + SYSMON_SUPPLY_TH_UP; + if (dir == IIO_EV_DIR_FALLING) + return (address * SYSMON_REG_STRIDE) + SYSMON_SUPPLY_TH_LOW; + + return -EINVAL; +} + +static int sysmon_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct sysmon *sysmon = iio_priv(indio_dev); + unsigned int regval; + int ret; + + guard(mutex)(&sysmon->lock); + + if (mask == IIO_CHAN_INFO_OVERSAMPLING_RATIO) { + *val = (chan->type == IIO_TEMP) ? sysmon->temp_oversampling : + sysmon->supply_oversampling; + return IIO_VAL_INT; + } + + switch (chan->type) { + case IIO_TEMP: + if (mask == IIO_CHAN_INFO_SCALE) { + /* Q8.7 to millicelsius: raw * 1000 / 128 */ + *val = MILLIDEGREE_PER_DEGREE; + *val2 = BIT(SYSMON_FRACTIONAL_SHIFT); + return IIO_VAL_FRACTIONAL; + } + if (mask != IIO_CHAN_INFO_RAW) + return -EINVAL; + + ret = regmap_read(sysmon->regmap, chan->address, ®val); + if (ret) + return ret; + + *val = sign_extend32(regval, 15); + return IIO_VAL_INT; + + case IIO_VOLTAGE: + if (mask != IIO_CHAN_INFO_PROCESSED) + return -EINVAL; + + ret = regmap_read(sysmon->regmap, + chan->address * SYSMON_REG_STRIDE + + SYSMON_SUPPLY_BASE, ®val); + if (ret) + return ret; + + sysmon_supply_rawtoprocessed(regval, val); + return IIO_VAL_INT; + + default: + return -EINVAL; + } +} + +static u32 sysmon_get_event_mask(const struct iio_chan_spec *chan) +{ + if (chan->type == IIO_TEMP) + return BIT(SYSMON_BIT_TEMP); + + return BIT(chan->address / SYSMON_ALARM_BITS_PER_REG); +} + +static int sysmon_read_alarm_config(struct sysmon *sysmon, + unsigned long address) +{ + u32 shift = address % SYSMON_ALARM_BITS_PER_REG; + u32 offset = SYSMON_ALARM_OFFSET(address); + + return regmap_test_bits(sysmon->regmap, offset, BIT(shift)); +} + +static int sysmon_write_alarm_config(struct sysmon *sysmon, + unsigned long address, bool enable) +{ + u32 shift = address % SYSMON_ALARM_BITS_PER_REG; + u32 offset = SYSMON_ALARM_OFFSET(address); + + return regmap_assign_bits(sysmon->regmap, offset, BIT(shift), enable); +} + +static int sysmon_read_event_config(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir) +{ + struct sysmon *sysmon = iio_priv(indio_dev); + u32 mask = sysmon_get_event_mask(chan); + unsigned int imr; + int config_value; + int ret; + + ret = regmap_read(sysmon->regmap, SYSMON_IMR, &imr); + if (ret) + return ret; + + /* IMR bits are 1=masked, invert to get 1=enabled */ + imr = ~imr; + + switch (chan->type) { + case IIO_VOLTAGE: + config_value = sysmon_read_alarm_config(sysmon, chan->address); + if (config_value < 0) + return config_value; + return config_value && (imr & mask); + + case IIO_TEMP: + /* + * Return the administrative state, not the hardware IMR. + * The IRQ handler temporarily masks the interrupt during + * the polling window; reading IMR would show it as disabled. + * temp_mask bit is set when administratively disabled. + */ + return !(sysmon->temp_mask & mask); + + default: + return -EINVAL; + } +} + +static int sysmon_write_event_config(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir, + bool state) +{ + u32 offset = SYSMON_ALARM_OFFSET(chan->address); + struct sysmon *sysmon = iio_priv(indio_dev); + u32 mask = sysmon_get_event_mask(chan); + unsigned int alarm_config; + int ret; + + guard(mutex)(&sysmon->lock); + + switch (chan->type) { + case IIO_VOLTAGE: + ret = sysmon_write_alarm_config(sysmon, chan->address, state); + if (ret) + return ret; + + ret = regmap_read(sysmon->regmap, offset, &alarm_config); + if (ret) + return ret; + + if (alarm_config) + return regmap_write(sysmon->regmap, SYSMON_IER, mask); + + return regmap_write(sysmon->regmap, SYSMON_IDR, mask); + + case IIO_TEMP: + if (state) { + ret = regmap_write(sysmon->regmap, SYSMON_IER, mask); + if (ret) + return ret; + + scoped_guard(spinlock_irq, &sysmon->irq_lock) + sysmon->temp_mask &= ~mask; + } else { + ret = regmap_write(sysmon->regmap, SYSMON_IDR, mask); + if (ret) + return ret; + + scoped_guard(spinlock_irq, &sysmon->irq_lock) + sysmon->temp_mask |= mask; + } + return 0; + + default: + return -EINVAL; + } +} + +/* + * Recompute the lower threshold register from upper threshold and + * cached hysteresis. Called when either upper threshold or hysteresis + * is written. + */ +static int sysmon_update_temp_lower(struct sysmon *sysmon) +{ + unsigned int upper_reg; + int upper_mc, lower_mc; + u32 raw_val; + int ret; + + ret = regmap_read(sysmon->regmap, SYSMON_TEMP_TH_UP, &upper_reg); + if (ret) + return ret; + + sysmon_q8p7_to_millicelsius(upper_reg, &upper_mc); + lower_mc = clamp(upper_mc - sysmon->temp_hysteresis, -256000, 255992); + sysmon_millicelsius_to_q8p7(&raw_val, lower_mc); + + return regmap_write(sysmon->regmap, SYSMON_TEMP_TH_LOW, raw_val); +} + +static int sysmon_read_event_value(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir, + enum iio_event_info info, + int *val, int *val2) +{ + struct sysmon *sysmon = iio_priv(indio_dev); + unsigned int reg_val; + int offset; + int ret; + + guard(mutex)(&sysmon->lock); + + switch (chan->type) { + case IIO_TEMP: + switch (info) { + case IIO_EV_INFO_VALUE: + ret = regmap_read(sysmon->regmap, SYSMON_TEMP_TH_UP, ®_val); + if (ret) + return ret; + + sysmon_q8p7_to_millicelsius(reg_val, val); + + return IIO_VAL_INT; + + case IIO_EV_INFO_HYSTERESIS: + *val = sysmon->temp_hysteresis; + return IIO_VAL_INT; + + default: + return -EINVAL; + } + + case IIO_VOLTAGE: + offset = sysmon_supply_thresh_offset(chan->address, dir); + if (offset < 0) + return offset; + + ret = regmap_read(sysmon->regmap, offset, ®_val); + if (ret) + return ret; + + sysmon_supply_rawtoprocessed(reg_val, val); + + return IIO_VAL_INT; + + default: + return -EINVAL; + } +} + +static int sysmon_write_event_value(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir, + enum iio_event_info info, + int val, int val2) +{ + struct sysmon *sysmon = iio_priv(indio_dev); + unsigned int reg_val; + u32 raw_val; + int offset; + int ret; + + guard(mutex)(&sysmon->lock); + + switch (chan->type) { + case IIO_TEMP: + switch (info) { + case IIO_EV_INFO_VALUE: + /* Q8.7 signed range: -256000 to +255992 mC */ + if (val < -256000 || val > 255992) + return -EINVAL; + + sysmon_millicelsius_to_q8p7(&raw_val, val); + + ret = regmap_write(sysmon->regmap, SYSMON_TEMP_TH_UP, raw_val); + if (ret) + return ret; + + /* Recompute lower = upper - hysteresis */ + return sysmon_update_temp_lower(sysmon); + + case IIO_EV_INFO_HYSTERESIS: + if (val < 0) + return -EINVAL; + + sysmon->temp_hysteresis = val; + + return sysmon_update_temp_lower(sysmon); + + default: + return -EINVAL; + } + + case IIO_VOLTAGE: + offset = sysmon_supply_thresh_offset(chan->address, dir); + if (offset < 0) + return offset; + + ret = regmap_read(sysmon->regmap, offset, ®_val); + if (ret) + return ret; + + /* Clamp to prevent overflow in processedtoraw conversion */ + if (val < -32768 || val > 32767) + return -EINVAL; + + sysmon_supply_processedtoraw(val, reg_val, &raw_val); + + /* + * The hardware threshold register returns FMT and MODE + * bits in the upper 16 bits on read, but only the lower + * 16-bit mantissa is used on write. + */ + return regmap_write(sysmon->regmap, offset, raw_val); + + default: + return -EINVAL; + } +} + +static int sysmon_set_avg_enable(struct sysmon *sysmon, + u32 base, u32 count, u32 val) +{ + struct regmap *map = sysmon->regmap; + int ret; + + for (unsigned int i = 0; i < count; i++) { + ret = regmap_write(map, base + i * SYSMON_REG_STRIDE, val); + if (ret) + return ret; + } + + return 0; +} + +static int sysmon_osr_write_temp(struct sysmon *sysmon, unsigned int val) +{ + /* + * HW register encoding is sample_count / 2: + * 0=none, 1=2x, 2=4x, 4=8x, 8=16x (not log2-based). + */ + unsigned int hw_val = val >> 1; + unsigned int readback; + int ret; + + ret = regmap_update_bits(sysmon->regmap, SYSMON_CONFIG, + SYSMON_CONFIG_TEMP_SAT_OSR, + FIELD_PREP(SYSMON_CONFIG_TEMP_SAT_OSR, hw_val)); + if (ret) + return ret; + + /* + * Readback fence: the SysMon CONFIG register resides in the + * PMC domain behind the NoC. A posted write may not reach the + * hardware before the next MMIO access. Reading the register + * back forces the interconnect to complete the write, preventing + * a bus hang on the subsequent access. + */ + regmap_read(sysmon->regmap, SYSMON_CONFIG, &readback); + + return sysmon_set_avg_enable(sysmon, SYSMON_TEMP_EN_AVG_BASE, + SYSMON_TEMP_EN_AVG_COUNT, + hw_val ? ~0 : 0); +} + +static int sysmon_osr_write_supply(struct sysmon *sysmon, unsigned int val) +{ + /* HW encoding: sample_count / 2 (see sysmon_osr_write_temp) */ + unsigned int hw_val = val >> 1; + unsigned int readback; + int ret; + + ret = regmap_update_bits(sysmon->regmap, SYSMON_CONFIG, + SYSMON_CONFIG_SUPPLY_OSR, + FIELD_PREP(SYSMON_CONFIG_SUPPLY_OSR, hw_val)); + if (ret) + return ret; + + /* Readback fence -- see sysmon_osr_write_temp for details */ + regmap_read(sysmon->regmap, SYSMON_CONFIG, &readback); + + return sysmon_set_avg_enable(sysmon, SYSMON_SUPPLY_EN_AVG_BASE, + SYSMON_SUPPLY_EN_AVG_COUNT, + hw_val ? ~0 : 0); +} + +static int sysmon_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, int val2, long mask) +{ + struct sysmon *sysmon = iio_priv(indio_dev); + unsigned int i; + int ret; + + if (mask != IIO_CHAN_INFO_OVERSAMPLING_RATIO) + return -EINVAL; + + for (i = 0; i < ARRAY_SIZE(sysmon_oversampling_avail); i++) { + if (val == sysmon_oversampling_avail[i]) + break; + } + if (i == ARRAY_SIZE(sysmon_oversampling_avail)) + return -EINVAL; + + guard(mutex)(&sysmon->lock); + + if (chan->type == IIO_TEMP) { + ret = sysmon_osr_write_temp(sysmon, val); + if (ret) + return ret; + sysmon->temp_oversampling = val; + } else { + ret = sysmon_osr_write_supply(sysmon, val); + if (ret) + return ret; + sysmon->supply_oversampling = val; + } + + return 0; +} + +static int sysmon_write_raw_get_fmt(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + long mask) +{ + if (mask == IIO_CHAN_INFO_OVERSAMPLING_RATIO) + return IIO_VAL_INT; + + return -EINVAL; +} + +static int sysmon_read_avail(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + const int **vals, int *type, + int *length, long mask) +{ + if (mask != IIO_CHAN_INFO_OVERSAMPLING_RATIO) + return -EINVAL; + + *vals = sysmon_oversampling_avail; + *type = IIO_VAL_INT; + *length = ARRAY_SIZE(sysmon_oversampling_avail); + + return IIO_AVAIL_LIST; +} + +static int sysmon_read_label(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + char *label) +{ + if (chan->datasheet_name) + return sysfs_emit(label, "%s\n", chan->datasheet_name); + + return -EINVAL; +} + +static const struct iio_info sysmon_iio_info = { + .read_raw = sysmon_read_raw, + .write_raw = sysmon_write_raw, + .write_raw_get_fmt = sysmon_write_raw_get_fmt, + .read_avail = sysmon_read_avail, + .read_label = sysmon_read_label, + .read_event_config = sysmon_read_event_config, + .write_event_config = sysmon_write_event_config, + .read_event_value = sysmon_read_event_value, + .write_event_value = sysmon_write_event_value, +}; + +static void sysmon_push_event(struct iio_dev *indio_dev, u32 address) +{ + const struct iio_chan_spec *chan; + enum iio_event_direction dir; + + for (unsigned int i = 0; i < indio_dev->num_channels; i++) { + if (indio_dev->channels[i].address != address) + continue; + + chan = &indio_dev->channels[i]; + /* Temp uses hysteresis mode (rising only), voltage uses window */ + dir = (chan->type == IIO_TEMP) ? IIO_EV_DIR_RISING : + IIO_EV_DIR_EITHER; + iio_push_event(indio_dev, + IIO_UNMOD_EVENT_CODE(chan->type, + chan->channel, + IIO_EV_TYPE_THRESH, + dir), + iio_get_time_ns(indio_dev)); + } +} + +static int sysmon_handle_event(struct iio_dev *indio_dev, u32 event) +{ + u32 alarm_flag_offset = SYSMON_ALARM_FLAG + event * SYSMON_REG_STRIDE; + u32 alarm_reg_offset = SYSMON_ALARM_REG + event * SYSMON_REG_STRIDE; + struct sysmon *sysmon = iio_priv(indio_dev); + unsigned long alarm_flag_reg; + unsigned int reg_val; + u32 address, bit; + int ret; + + switch (event) { + case SYSMON_BIT_TEMP: + sysmon_push_event(indio_dev, SYSMON_TEMP_MAX); + + ret = regmap_write(sysmon->regmap, SYSMON_IDR, BIT(SYSMON_BIT_TEMP)); + if (ret) + return ret; + + sysmon->masked_temp |= BIT(SYSMON_BIT_TEMP); + return 0; + + case SYSMON_BIT_ALARM0: + case SYSMON_BIT_ALARM1: + case SYSMON_BIT_ALARM2: + case SYSMON_BIT_ALARM3: + case SYSMON_BIT_ALARM4: + ret = regmap_read(sysmon->regmap, alarm_flag_offset, ®_val); + if (ret) + return ret; + + alarm_flag_reg = reg_val; + + for_each_set_bit(bit, &alarm_flag_reg, SYSMON_ALARM_BITS_PER_REG) { + address = bit + SYSMON_ALARM_BITS_PER_REG * event; + sysmon_push_event(indio_dev, address); + ret = regmap_clear_bits(sysmon->regmap, alarm_reg_offset, BIT(bit)); + if (ret) + return ret; + } + + return regmap_write(sysmon->regmap, alarm_flag_offset, alarm_flag_reg); + + default: + return -EINVAL; + } +} + +static void sysmon_handle_events(struct iio_dev *indio_dev, + unsigned long events) +{ + unsigned int bit; + + for_each_set_bit(bit, &events, SYSMON_NO_OF_EVENTS) + sysmon_handle_event(indio_dev, bit); +} + +static void sysmon_unmask_temp(struct sysmon *sysmon, unsigned int isr) +{ + unsigned int status; + u32 ier; + + status = isr & SYSMON_TEMP_INTR_MASK; + + ier = ~status & sysmon->masked_temp; + sysmon->masked_temp &= status; + + /* Only unmask if not administratively disabled by userspace */ + ier &= ~sysmon->temp_mask; + + regmap_write(sysmon->regmap, SYSMON_IER, ier); +} + +/* + * Versal threshold interrupts are level-sensitive. Active threshold + * interrupts are masked in the handler and polled via delayed work + * until the condition clears, then unmasked. + */ +static void sysmon_unmask_worker(struct work_struct *work) +{ + struct sysmon *sysmon = + container_of(work, struct sysmon, sysmon_unmask_work.work); + unsigned int isr; + + /* + * If the ISR read fails, skip processing to avoid acting + * on undefined data. + */ + scoped_guard(spinlock_irq, &sysmon->irq_lock) { + if (regmap_read(sysmon->regmap, SYSMON_ISR, &isr)) + break; + regmap_write(sysmon->regmap, SYSMON_ISR, isr); + sysmon_unmask_temp(sysmon, isr); + } + + if (sysmon->masked_temp) + schedule_delayed_work(&sysmon->sysmon_unmask_work, + msecs_to_jiffies(SYSMON_UNMASK_WORK_DELAY_MS)); + else + regmap_write(sysmon->regmap, SYSMON_STATUS_RESET, 1); +} + +static irqreturn_t sysmon_iio_irq(int irq, void *data) +{ + struct iio_dev *indio_dev = data; + struct sysmon *sysmon = iio_priv(indio_dev); + unsigned int isr, imr; + + guard(spinlock)(&sysmon->irq_lock); + + if (regmap_read(sysmon->regmap, SYSMON_ISR, &isr) || + regmap_read(sysmon->regmap, SYSMON_IMR, &imr)) + return IRQ_NONE; + + isr &= ~imr; + if (!isr) + return IRQ_NONE; + + regmap_write(sysmon->regmap, SYSMON_ISR, isr); + + sysmon_handle_events(indio_dev, isr); + schedule_delayed_work(&sysmon->sysmon_unmask_work, + msecs_to_jiffies(SYSMON_UNMASK_WORK_DELAY_MS)); + + return IRQ_HANDLED; +} + +static void sysmon_disable_interrupts(void *data) +{ + struct sysmon *sysmon = data; + + regmap_write(sysmon->regmap, SYSMON_IDR, SYSMON_INTR_ALL_MASK); + + scoped_guard(spinlock_irq, &sysmon->irq_lock) + sysmon->masked_temp = 0; + + cancel_delayed_work_sync(&sysmon->sysmon_unmask_work); +} + +static int sysmon_init_interrupt(struct sysmon *sysmon, + struct device *dev, + struct iio_dev *indio_dev, + int irq) +{ + unsigned int imr; + int ret; + + /* Events not supported without IRQ (e.g. I2C path) */ + if (!irq) + return 0; + + INIT_DELAYED_WORK(&sysmon->sysmon_unmask_work, sysmon_unmask_worker); + + ret = regmap_read(sysmon->regmap, SYSMON_IMR, &imr); + if (ret) + return ret; + sysmon->temp_mask = imr & SYSMON_TEMP_INTR_MASK; + + ret = devm_request_irq(dev, irq, sysmon_iio_irq, 0, "sysmon-irq", indio_dev); + if (ret) + return ret; + + return devm_add_action_or_reset(dev, sysmon_disable_interrupts, sysmon); +} + +/* + * Initialize the cached hysteresis for a temperature channel from the + * current hardware threshold registers: hysteresis = upper - lower. + */ +static int sysmon_init_hysteresis(struct sysmon *sysmon, int *hysteresis) +{ + unsigned int upper_reg, lower_reg; + int upper_mc, lower_mc; + int ret; + + ret = regmap_read(sysmon->regmap, SYSMON_TEMP_TH_UP, &upper_reg); + if (ret) + return ret; + + ret = regmap_read(sysmon->regmap, SYSMON_TEMP_TH_LOW, &lower_reg); + if (ret) + return ret; + + sysmon_q8p7_to_millicelsius(upper_reg, &upper_mc); + sysmon_q8p7_to_millicelsius(lower_reg, &lower_mc); + *hysteresis = upper_mc - lower_mc; + + return 0; +} + +/** + * sysmon_parse_fw() - Parse firmware nodes and configure IIO channels. + * @indio_dev: IIO device instance + * @dev: Parent device + * @irq: IRQ number (positive enables event channels, 0 disables) + * + * Reads voltage-channels and temperature-channels container nodes from + * firmware and builds the IIO channel array. Static temperature channels + * and event channels are prepended, followed by supply and satellite + * channels from DT. + * + * Event channels and per-channel event specs are only added when the + * device has an IRQ. I2C devices have no interrupt line, and the I2C + * regmap cannot be called from atomic context, so events are not + * supported on that path. + * + * Return: 0 on success, negative errno on failure. + */ +static int sysmon_parse_fw(struct iio_dev *indio_dev, struct device *dev, int irq) +{ + unsigned int num_chan, num_static, num_supply, num_temp; + unsigned int idx, temp_chan_idx, volt_chan_idx; + struct iio_chan_spec *sysmon_channels; + const char *label; + u32 reg; + int ret; + + struct fwnode_handle *supply_node __free(fwnode_handle) = + device_get_named_child_node(dev, "voltage-channels"); + num_supply = fwnode_get_child_node_count(supply_node); + + struct fwnode_handle *temp_node __free(fwnode_handle) = + device_get_named_child_node(dev, "temperature-channels"); + num_temp = fwnode_get_child_node_count(temp_node); + + num_static = ARRAY_SIZE(temp_channels); + num_chan = size_add(num_temp, size_add(num_static, num_supply)); + sysmon_channels = devm_kcalloc(dev, num_chan, sizeof(*sysmon_channels), GFP_KERNEL); + if (!sysmon_channels) + return -ENOMEM; + + memcpy(sysmon_channels, temp_channels, sizeof(temp_channels)); + + /* Attach event spec to channel 0 when IRQ is available */ + if (irq > 0) { + sysmon_channels[0].event_spec = sysmon_temp_events; + sysmon_channels[0].num_event_specs = ARRAY_SIZE(sysmon_temp_events); + } + + idx = num_static; + + /* Supply channels from DT */ + fwnode_for_each_child_node_scoped(supply_node, child) { + ret = fwnode_property_read_u32(child, "reg", ®); + if (ret) + return dev_err_probe(dev, ret, + "missing reg for supply channel\n"); + + if (reg > SYSMON_SUPPLY_IDX_MAX) + return dev_err_probe(dev, -EINVAL, + "supply reg %u exceeds max %u\n", + reg, SYSMON_SUPPLY_IDX_MAX); + + ret = fwnode_property_read_string(child, "label", &label); + if (ret) + return dev_err_probe(dev, ret, + "missing label for supply channel\n"); + + sysmon_channels[idx++] = (struct iio_chan_spec) { + .type = IIO_VOLTAGE, + .indexed = 1, + .address = reg, + .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED), + .info_mask_shared_by_type = + BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), + .info_mask_shared_by_type_available = + BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), + .event_spec = irq > 0 ? + sysmon_supply_events : NULL, + .num_event_specs = irq > 0 ? + ARRAY_SIZE(sysmon_supply_events) : 0, + .datasheet_name = label, + }; + } + + /* Temperature satellite channels from DT */ + fwnode_for_each_child_node_scoped(temp_node, child) { + ret = fwnode_property_read_u32(child, "reg", ®); + if (ret) + return dev_err_probe(dev, ret, + "missing reg for temp channel\n"); + + if (reg < 1 || reg > SYSMON_TEMP_SAT_MAX) + return dev_err_probe(dev, -EINVAL, + "temp reg %u out of range [1..%u]\n", + reg, SYSMON_TEMP_SAT_MAX); + + ret = fwnode_property_read_string(child, "label", &label); + if (ret) + return dev_err_probe(dev, ret, + "missing label for temp channel\n"); + + sysmon_channels[idx++] = (struct iio_chan_spec) { + .type = IIO_TEMP, + .indexed = 1, + .address = SYSMON_TEMP_SAT_BASE + + (reg - 1) * SYSMON_REG_STRIDE, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), + .info_mask_shared_by_type = + BIT(IIO_CHAN_INFO_SCALE) | + BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), + .info_mask_shared_by_type_available = + BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), + .datasheet_name = label, + }; + } + + indio_dev->num_channels = idx; + indio_dev->info = &sysmon_iio_info; + + /* + * Assign per-type sequential channel numbers. + * IIO sysfs uses type prefix (in_tempN, in_voltageN) + * so numbers only need to be unique within each type. + */ + temp_chan_idx = 0; + volt_chan_idx = 0; + for (unsigned int idx = 0; idx < indio_dev->num_channels; idx++) { + if (sysmon_channels[idx].type == IIO_TEMP) + sysmon_channels[idx].channel = temp_chan_idx++; + else + sysmon_channels[idx].channel = volt_chan_idx++; + } + + indio_dev->channels = sysmon_channels; + + return 0; +} + +/** + * devm_versal_sysmon_core_probe() - Initialize Versal SysMon core + * @dev: Parent device + * @regmap: Register map for hardware access + * + * Return: 0 on success, negative errno on failure. + */ +int devm_versal_sysmon_core_probe(struct device *dev, struct regmap *regmap) +{ + struct iio_dev *indio_dev; + struct sysmon *sysmon; + int irq; + int ret; + + indio_dev = devm_iio_device_alloc(dev, sizeof(*sysmon)); + if (!indio_dev) + return -ENOMEM; + + sysmon = iio_priv(indio_dev); + sysmon->regmap = regmap; + sysmon->temp_oversampling = 1; + sysmon->supply_oversampling = 1; + + ret = devm_mutex_init(dev, &sysmon->lock); + if (ret) + return ret; + spin_lock_init(&sysmon->irq_lock); + + /* Disable all interrupts and clear pending status */ + ret = regmap_write(sysmon->regmap, SYSMON_IDR, SYSMON_INTR_ALL_MASK); + if (ret) + return ret; + ret = regmap_write(sysmon->regmap, SYSMON_ISR, SYSMON_INTR_ALL_MASK); + if (ret) + return ret; + + irq = fwnode_irq_get(dev_fwnode(dev), 0); + if (irq == -EPROBE_DEFER) + return dev_err_probe(dev, irq, "failed to get IRQ\n"); + + indio_dev->name = "versal-sysmon"; + indio_dev->modes = INDIO_DIRECT_MODE; + + ret = sysmon_parse_fw(indio_dev, dev, irq); + if (ret) + return ret; + + if (irq > 0) { + /* Set hysteresis mode for temperature threshold */ + ret = regmap_set_bits(sysmon->regmap, SYSMON_TEMP_EV_CFG, + SYSMON_TEMP_HYST_MASK); + if (ret) + return ret; + + /* Initialize cached hysteresis from hardware registers */ + ret = sysmon_init_hysteresis(sysmon, &sysmon->temp_hysteresis); + if (ret) + return ret; + + ret = sysmon_init_interrupt(sysmon, dev, indio_dev, irq); + if (ret) + return ret; + } + + return devm_iio_device_register(dev, indio_dev); +} +EXPORT_SYMBOL_NS_GPL(devm_versal_sysmon_core_probe, "VERSAL_SYSMON"); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("AMD Versal SysMon Core Driver"); +MODULE_AUTHOR("Salih Erim "); diff --git a/drivers/iio/adc/versal-sysmon-i2c.c b/drivers/iio/adc/versal-sysmon-i2c.c new file mode 100644 index 000000000..e9a762915 --- /dev/null +++ b/drivers/iio/adc/versal-sysmon-i2c.c @@ -0,0 +1,134 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * AMD Versal SysMon I2C driver + * + * Copyright (C) 2023 - 2026, Advanced Micro Devices, Inc. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "versal-sysmon.h" + +#define SYSMON_I2C_INSTR_READ BIT(2) +#define SYSMON_I2C_INSTR_WRITE BIT(3) + +/* + * I2C command frame layout (8 bytes): + * [0..3] data payload (little-endian u32) + * [4..5] register offset >> 2 (little-endian u16) + * [6] instruction (read/write) + * [7] reserved + */ +#define SYSMON_I2C_DATA_OFS 0 +#define SYSMON_I2C_REG_OFS 4 +#define SYSMON_I2C_INSTR_OFS 6 + +static int sysmon_i2c_reg_read(void *context, unsigned int reg, + unsigned int *val) +{ + struct i2c_client *client = context; + u8 write_buf[8] = { }; + u8 read_buf[4]; + int ret; + + put_unaligned_le16(reg >> 2, &write_buf[SYSMON_I2C_REG_OFS]); + write_buf[SYSMON_I2C_INSTR_OFS] = SYSMON_I2C_INSTR_READ; + + ret = i2c_master_send(client, write_buf, sizeof(write_buf)); + if (ret < 0) + return ret; + if (ret != sizeof(write_buf)) + return -EIO; + + ret = i2c_master_recv(client, read_buf, sizeof(read_buf)); + if (ret < 0) + return ret; + if (ret != sizeof(read_buf)) + return -EIO; + + *val = get_unaligned_le32(read_buf); + + return 0; +} + +static int sysmon_i2c_reg_write(void *context, unsigned int reg, + unsigned int val) +{ + struct i2c_client *client = context; + u8 write_buf[8] = { }; + int ret; + + put_unaligned_le32(val, &write_buf[SYSMON_I2C_DATA_OFS]); + put_unaligned_le16(reg >> 2, &write_buf[SYSMON_I2C_REG_OFS]); + write_buf[SYSMON_I2C_INSTR_OFS] = SYSMON_I2C_INSTR_WRITE; + + ret = i2c_master_send(client, write_buf, sizeof(write_buf)); + if (ret < 0) + return ret; + if (ret != sizeof(write_buf)) + return -EIO; + + return 0; +} + +/* + * Almost all registers are volatile (live ADC readings, interrupt + * status). The rest are not accessed often enough to benefit from + * caching. + */ +static const struct regmap_config sysmon_i2c_regmap_config = { + .reg_bits = 32, + .val_bits = 32, + .reg_stride = SYSMON_REG_STRIDE, + .max_register = SYSMON_MAX_REG, + .reg_read = sysmon_i2c_reg_read, + .reg_write = sysmon_i2c_reg_write, +}; + +static int sysmon_i2c_probe(struct i2c_client *client) +{ + struct device *dev = &client->dev; + struct regmap *regmap; + + regmap = devm_regmap_init(dev, NULL, client, &sysmon_i2c_regmap_config); + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + + /* I2C has no IRQ connection; events are not supported */ + return devm_versal_sysmon_core_probe(dev, regmap); +} + +static const struct of_device_id sysmon_i2c_of_match_table[] = { + { .compatible = "xlnx,versal-sysmon" }, + { } +}; +MODULE_DEVICE_TABLE(of, sysmon_i2c_of_match_table); + +static const struct i2c_device_id sysmon_i2c_id_table[] = { + { .name = "versal-sysmon" }, + { } +}; +MODULE_DEVICE_TABLE(i2c, sysmon_i2c_id_table); + +static struct i2c_driver sysmon_i2c_driver = { + .probe = sysmon_i2c_probe, + .driver = { + .name = "versal-sysmon-i2c", + .of_match_table = sysmon_i2c_of_match_table, + }, + .id_table = sysmon_i2c_id_table, +}; +module_i2c_driver(sysmon_i2c_driver); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("AMD Versal SysMon I2C Driver"); +MODULE_IMPORT_NS("VERSAL_SYSMON"); +MODULE_AUTHOR("Conall O'Griofa "); +MODULE_AUTHOR("Salih Erim "); diff --git a/drivers/iio/adc/versal-sysmon.c b/drivers/iio/adc/versal-sysmon.c new file mode 100644 index 000000000..529d0486c --- /dev/null +++ b/drivers/iio/adc/versal-sysmon.c @@ -0,0 +1,92 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * AMD Versal SysMon MMIO platform driver + * + * Copyright (C) 2019 - 2022, Xilinx, Inc. + * Copyright (C) 2022 - 2026, Advanced Micro Devices, Inc. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "versal-sysmon.h" + +struct sysmon_mmio { + void __iomem *base; +}; + +static int sysmon_mmio_reg_read(void *context, unsigned int reg, unsigned int *val) +{ + struct sysmon_mmio *mmio = context; + + *val = readl(mmio->base + reg); + return 0; +} + +static int sysmon_mmio_reg_write(void *context, unsigned int reg, unsigned int val) +{ + struct sysmon_mmio *mmio = context; + + /* NPI must be unlocked before any register write except to NPI_LOCK */ + if (reg != SYSMON_NPI_LOCK) + writel(SYSMON_NPI_UNLOCK_CODE, mmio->base + SYSMON_NPI_LOCK); + writel(val, mmio->base + reg); + + return 0; +} + +static const struct regmap_config sysmon_mmio_regmap_config = { + .reg_bits = 32, + .val_bits = 32, + .reg_stride = SYSMON_REG_STRIDE, + .max_register = SYSMON_MAX_REG, + .reg_read = sysmon_mmio_reg_read, + .reg_write = sysmon_mmio_reg_write, + .fast_io = true, +}; + +static int sysmon_platform_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct sysmon_mmio *mmio; + struct regmap *regmap; + + mmio = devm_kzalloc(dev, sizeof(*mmio), GFP_KERNEL); + if (!mmio) + return -ENOMEM; + + mmio->base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(mmio->base)) + return PTR_ERR(mmio->base); + + regmap = devm_regmap_init(dev, NULL, mmio, &sysmon_mmio_regmap_config); + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + + return devm_versal_sysmon_core_probe(dev, regmap); +} + +static const struct of_device_id sysmon_of_match_table[] = { + { .compatible = "xlnx,versal-sysmon" }, + { } +}; +MODULE_DEVICE_TABLE(of, sysmon_of_match_table); + +static struct platform_driver sysmon_platform_driver = { + .probe = sysmon_platform_probe, + .driver = { + .name = "versal-sysmon", + .of_match_table = sysmon_of_match_table, + }, +}; +module_platform_driver(sysmon_platform_driver); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("AMD Versal SysMon Platform Driver"); +MODULE_IMPORT_NS("VERSAL_SYSMON"); +MODULE_AUTHOR("Salih Erim "); diff --git a/drivers/iio/adc/versal-sysmon.h b/drivers/iio/adc/versal-sysmon.h new file mode 100644 index 000000000..bb9a75bf7 --- /dev/null +++ b/drivers/iio/adc/versal-sysmon.h @@ -0,0 +1,120 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * AMD Versal SysMon driver + * + * Copyright (C) 2019 - 2022, Xilinx, Inc. + * Copyright (C) 2022 - 2026, Advanced Micro Devices, Inc. + */ + +#ifndef _VERSAL_SYSMON_H_ +#define _VERSAL_SYSMON_H_ + +#include +#include +#include +#include + +struct device; +struct regmap; + +/* Register offsets (sorted by address) */ +#define SYSMON_NPI_LOCK 0x000C +#define SYSMON_ISR 0x0044 +#define SYSMON_IMR 0x0048 +#define SYSMON_IER 0x004C +#define SYSMON_IDR 0x0050 +#define SYSMON_CONFIG 0x0100 +#define SYSMON_TEMP_MAX 0x1030 +#define SYSMON_TEMP_MIN 0x1034 +#define SYSMON_SUPPLY_BASE 0x1040 +#define SYSMON_ALARM_FLAG 0x1018 +#define SYSMON_ALARM_REG 0x1940 +#define SYSMON_SUPPLY_EN_AVG_BASE 0x1958 +#define SYSMON_TEMP_TH_LOW 0x1970 +#define SYSMON_TEMP_TH_UP 0x1974 +#define SYSMON_SUPPLY_TH_LOW 0x1980 +#define SYSMON_SUPPLY_TH_UP 0x1C80 +#define SYSMON_TEMP_EV_CFG 0x1F84 +#define SYSMON_TEMP_MIN_MIN 0x1F8C +#define SYSMON_TEMP_MAX_MAX 0x1F90 +#define SYSMON_STATUS_RESET 0x1F94 +#define SYSMON_TEMP_SAT_BASE 0x1FAC +#define SYSMON_TEMP_EN_AVG_BASE 0x24B4 +#define SYSMON_MAX_REG 0x24C0 + +/* NPI unlock value written to SYSMON_NPI_LOCK */ +#define SYSMON_NPI_UNLOCK_CODE 0xF9E8D7C6 + +/* Register stride: 4 bytes per 32-bit register */ +#define SYSMON_REG_STRIDE 4 + +#define SYSMON_SUPPLY_IDX_MAX 159 +#define SYSMON_TEMP_SAT_MAX 64 +#define SYSMON_NO_OF_EVENTS 32 +#define SYSMON_INTR_ALL_MASK GENMASK(31, 0) + +/* ISR/IMR temperature alarm mask (bit 9) */ +#define SYSMON_TEMP_INTR_MASK BIT(9) + +/* SYSMON_CONFIG: supply oversampling ratio */ +#define SYSMON_CONFIG_SUPPLY_OSR GENMASK(17, 14) + +/* SYSMON_CONFIG: temperature satellite oversampling ratio */ +#define SYSMON_CONFIG_TEMP_SAT_OSR GENMASK(27, 24) + +/* Per-channel averaging enable register counts */ +#define SYSMON_SUPPLY_EN_AVG_COUNT 5 +#define SYSMON_TEMP_EN_AVG_COUNT 2 + +/* Supply voltage conversion register fields */ +#define SYSMON_MANTISSA_MASK GENMASK(15, 0) +#define SYSMON_FMT_MASK BIT(16) +#define SYSMON_MODE_MASK GENMASK(18, 17) + +/* Q8.7 fractional shift */ +#define SYSMON_FRACTIONAL_SHIFT 7U +#define SYSMON_SUPPLY_MANTISSA_BITS 16 + +/* Bits per alarm register */ +#define SYSMON_ALARM_BITS_PER_REG 32 + +#define SYSMON_UNMASK_WORK_DELAY_MS 500 + +/** + * struct sysmon - Driver data for Versal SysMon + * @regmap: register map for hardware access + * @lock: protects read-modify-write sequences on threshold registers + * and cached state that spans multiple regmap calls + * @irq_lock: protects interrupt mask register updates (MMIO path only) + * @masked_temp: currently masked temperature alarm bits + * @temp_mask: temperature interrupt configuration mask + * @temp_hysteresis: cached DEVICE_TEMP hysteresis in millicelsius + * @sysmon_unmask_work: re-enables events after alarm condition clears + * @temp_oversampling: current temp oversampling ratio + * @supply_oversampling: current supply oversampling ratio + */ +struct sysmon { + struct regmap *regmap; + /* + * Protects read-modify-write sequences on threshold registers + * and cached state (oversampling ratios, hysteresis values) + * that spans multiple regmap calls. + */ + struct mutex lock; + /* + * Protects interrupt mask register updates. Only used on the + * MMIO path (fast_io regmap); I2C has no IRQ and never reaches + * the event code that takes this lock. + */ + spinlock_t irq_lock; + unsigned int masked_temp; + unsigned int temp_mask; + int temp_hysteresis; + struct delayed_work sysmon_unmask_work; + unsigned int temp_oversampling; + unsigned int supply_oversampling; +}; + +int devm_versal_sysmon_core_probe(struct device *dev, struct regmap *regmap); + +#endif /* _VERSAL_SYSMON_H_ */ diff --git a/drivers/iio/adc/vf610_adc.c b/drivers/iio/adc/vf610_adc.c new file mode 100644 index 000000000..bcbeb482e --- /dev/null +++ b/drivers/iio/adc/vf610_adc.c @@ -0,0 +1,961 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Freescale Vybrid vf610 ADC driver + * + * Copyright 2013 Freescale Semiconductor, Inc. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +/* Vybrid/IMX ADC registers */ +#define VF610_REG_ADC_HC0 0x00 +#define VF610_REG_ADC_HC1 0x04 +#define VF610_REG_ADC_HS 0x08 +#define VF610_REG_ADC_R0 0x0c +#define VF610_REG_ADC_R1 0x10 +#define VF610_REG_ADC_CFG 0x14 +#define VF610_REG_ADC_GC 0x18 +#define VF610_REG_ADC_GS 0x1c +#define VF610_REG_ADC_CV 0x20 +#define VF610_REG_ADC_OFS 0x24 +#define VF610_REG_ADC_CAL 0x28 +#define VF610_REG_ADC_PCTL 0x30 + +/* Configuration register field define */ +#define VF610_ADC_MODE_BIT8 0x00 +#define VF610_ADC_MODE_BIT10 0x04 +#define VF610_ADC_MODE_BIT12 0x08 +#define VF610_ADC_MODE_MASK 0x0c +#define VF610_ADC_BUSCLK2_SEL 0x01 +#define VF610_ADC_ALTCLK_SEL 0x02 +#define VF610_ADC_ADACK_SEL 0x03 +#define VF610_ADC_ADCCLK_MASK 0x03 +#define VF610_ADC_CLK_DIV2 0x20 +#define VF610_ADC_CLK_DIV4 0x40 +#define VF610_ADC_CLK_DIV8 0x60 +#define VF610_ADC_CLK_MASK 0x60 +#define VF610_ADC_ADLSMP_LONG 0x10 +#define VF610_ADC_ADSTS_SHORT 0x100 +#define VF610_ADC_ADSTS_NORMAL 0x200 +#define VF610_ADC_ADSTS_LONG 0x300 +#define VF610_ADC_ADSTS_MASK 0x300 +#define VF610_ADC_ADLPC_EN 0x80 +#define VF610_ADC_ADHSC_EN 0x400 +#define VF610_ADC_REFSEL_VALT 0x800 +#define VF610_ADC_REFSEL_VBG 0x1000 +#define VF610_ADC_ADTRG_HARD 0x2000 +#define VF610_ADC_AVGS_8 0x4000 +#define VF610_ADC_AVGS_16 0x8000 +#define VF610_ADC_AVGS_32 0xC000 +#define VF610_ADC_AVGS_MASK 0xC000 +#define VF610_ADC_OVWREN 0x10000 + +/* General control register field define */ +#define VF610_ADC_ADACKEN 0x1 +#define VF610_ADC_DMAEN 0x2 +#define VF610_ADC_ACREN 0x4 +#define VF610_ADC_ACFGT 0x8 +#define VF610_ADC_ACFE 0x10 +#define VF610_ADC_AVGEN 0x20 +#define VF610_ADC_ADCON 0x40 +#define VF610_ADC_CAL 0x80 + +/* Other field define */ +#define VF610_ADC_ADCHC(x) ((x) & 0x1F) +#define VF610_ADC_AIEN (0x1 << 7) +#define VF610_ADC_CONV_DISABLE 0x1F +#define VF610_ADC_HS_COCO0 0x1 +#define VF610_ADC_CALF 0x2 +#define VF610_ADC_TIMEOUT msecs_to_jiffies(100) + +#define DEFAULT_SAMPLE_TIME 1000 + +/* V at 25°C of 696 mV */ +#define VF610_VTEMP25_3V0 950 +/* V at 25°C of 699 mV */ +#define VF610_VTEMP25_3V3 867 +/* Typical sensor slope coefficient at all temperatures */ +#define VF610_TEMP_SLOPE_COEFF 1840 + +enum clk_sel { + VF610_ADCIOC_BUSCLK_SET, + VF610_ADCIOC_ALTCLK_SET, + VF610_ADCIOC_ADACK_SET, +}; + +enum vol_ref { + VF610_ADCIOC_VR_VREF_SET, + VF610_ADCIOC_VR_VALT_SET, + VF610_ADCIOC_VR_VBG_SET, +}; + +enum average_sel { + VF610_ADC_SAMPLE_1, + VF610_ADC_SAMPLE_4, + VF610_ADC_SAMPLE_8, + VF610_ADC_SAMPLE_16, + VF610_ADC_SAMPLE_32, +}; + +enum conversion_mode_sel { + VF610_ADC_CONV_NORMAL, + VF610_ADC_CONV_HIGH_SPEED, + VF610_ADC_CONV_LOW_POWER, +}; + +enum lst_adder_sel { + VF610_ADCK_CYCLES_3, + VF610_ADCK_CYCLES_5, + VF610_ADCK_CYCLES_7, + VF610_ADCK_CYCLES_9, + VF610_ADCK_CYCLES_13, + VF610_ADCK_CYCLES_17, + VF610_ADCK_CYCLES_21, + VF610_ADCK_CYCLES_25, +}; + +struct vf610_adc_feature { + enum clk_sel clk_sel; + enum vol_ref vol_ref; + enum conversion_mode_sel conv_mode; + + int clk_div; + int sample_rate; + int res_mode; + u32 lst_adder_index; + u32 default_sample_time; + + bool calibration; + bool ovwren; +}; + +struct vf610_adc { + struct device *dev; + void __iomem *regs; + struct clk *clk; + + /* lock to protect against multiple access to the device */ + struct mutex lock; + + u32 vref_uv; + u32 value; + struct regulator *vref; + + u32 max_adck_rate[3]; + struct vf610_adc_feature adc_feature; + + u32 sample_freq_avail[5]; + + struct completion completion; + /* Ensure the timestamp is naturally aligned */ + struct { + u16 chan; + aligned_s64 timestamp; + } scan; +}; + +struct vf610_chip_info { + u8 num_channels; +}; + +static const u32 vf610_hw_avgs[] = { 1, 4, 8, 16, 32 }; +static const u32 vf610_lst_adder[] = { 3, 5, 7, 9, 13, 17, 21, 25 }; + +static inline void vf610_adc_calculate_rates(struct vf610_adc *info) +{ + struct vf610_adc_feature *adc_feature = &info->adc_feature; + unsigned long adck_rate, ipg_rate = clk_get_rate(info->clk); + u32 adck_period, lst_addr_min; + int divisor, i; + + adck_rate = info->max_adck_rate[adc_feature->conv_mode]; + + if (adck_rate) { + /* calculate clk divider which is within specification */ + divisor = ipg_rate / adck_rate; + adc_feature->clk_div = 1 << fls(divisor + 1); + } else { + /* fall-back value using a safe divisor */ + adc_feature->clk_div = 8; + } + + adck_rate = ipg_rate / adc_feature->clk_div; + + /* + * Determine the long sample time adder value to be used based + * on the default minimum sample time provided. + */ + adck_period = NSEC_PER_SEC / adck_rate; + lst_addr_min = adc_feature->default_sample_time / adck_period; + for (i = 0; i < ARRAY_SIZE(vf610_lst_adder); i++) { + if (vf610_lst_adder[i] > lst_addr_min) { + adc_feature->lst_adder_index = i; + break; + } + } + + /* + * Calculate ADC sample frequencies + * Sample time unit is ADCK cycles. ADCK clk source is ipg clock, + * which is the same as bus clock. + * + * ADC conversion time = SFCAdder + AverageNum x (BCT + LSTAdder) + * SFCAdder: fixed to 6 ADCK cycles + * AverageNum: 1, 4, 8, 16, 32 samples for hardware average. + * BCT (Base Conversion Time): fixed to 25 ADCK cycles for 12 bit mode + * LSTAdder(Long Sample Time): 3, 5, 7, 9, 13, 17, 21, 25 ADCK cycles + */ + for (i = 0; i < ARRAY_SIZE(vf610_hw_avgs); i++) + info->sample_freq_avail[i] = + adck_rate / (6 + vf610_hw_avgs[i] * + (25 + vf610_lst_adder[adc_feature->lst_adder_index])); +} + +static inline void vf610_adc_cfg_init(struct vf610_adc *info) +{ + struct vf610_adc_feature *adc_feature = &info->adc_feature; + + /* set default Configuration for ADC controller */ + adc_feature->clk_sel = VF610_ADCIOC_BUSCLK_SET; + adc_feature->vol_ref = VF610_ADCIOC_VR_VREF_SET; + + adc_feature->calibration = true; + adc_feature->ovwren = true; + + adc_feature->res_mode = 12; + adc_feature->sample_rate = 1; + + adc_feature->conv_mode = VF610_ADC_CONV_LOW_POWER; + + vf610_adc_calculate_rates(info); +} + +static void vf610_adc_cfg_post_set(struct vf610_adc *info) +{ + struct vf610_adc_feature *adc_feature = &info->adc_feature; + int cfg_data = 0; + int gc_data = 0; + + switch (adc_feature->clk_sel) { + case VF610_ADCIOC_ALTCLK_SET: + cfg_data |= VF610_ADC_ALTCLK_SEL; + break; + case VF610_ADCIOC_ADACK_SET: + cfg_data |= VF610_ADC_ADACK_SEL; + break; + default: + break; + } + + /* low power set for calibration */ + cfg_data |= VF610_ADC_ADLPC_EN; + + /* enable high speed for calibration */ + cfg_data |= VF610_ADC_ADHSC_EN; + + /* voltage reference */ + switch (adc_feature->vol_ref) { + case VF610_ADCIOC_VR_VREF_SET: + break; + case VF610_ADCIOC_VR_VALT_SET: + cfg_data |= VF610_ADC_REFSEL_VALT; + break; + case VF610_ADCIOC_VR_VBG_SET: + cfg_data |= VF610_ADC_REFSEL_VBG; + break; + default: + dev_err(info->dev, "error voltage reference\n"); + } + + /* data overwrite enable */ + if (adc_feature->ovwren) + cfg_data |= VF610_ADC_OVWREN; + + writel(cfg_data, info->regs + VF610_REG_ADC_CFG); + writel(gc_data, info->regs + VF610_REG_ADC_GC); +} + +static void vf610_adc_calibration(struct vf610_adc *info) +{ + int adc_gc, hc_cfg; + + if (!info->adc_feature.calibration) + return; + + /* enable calibration interrupt */ + hc_cfg = VF610_ADC_AIEN | VF610_ADC_CONV_DISABLE; + writel(hc_cfg, info->regs + VF610_REG_ADC_HC0); + + adc_gc = readl(info->regs + VF610_REG_ADC_GC); + writel(adc_gc | VF610_ADC_CAL, info->regs + VF610_REG_ADC_GC); + + if (!wait_for_completion_timeout(&info->completion, VF610_ADC_TIMEOUT)) + dev_err(info->dev, "Timeout for adc calibration\n"); + + adc_gc = readl(info->regs + VF610_REG_ADC_GS); + if (adc_gc & VF610_ADC_CALF) + dev_err(info->dev, "ADC calibration failed\n"); + + info->adc_feature.calibration = false; +} + +static void vf610_adc_cfg_set(struct vf610_adc *info) +{ + struct vf610_adc_feature *adc_feature = &(info->adc_feature); + int cfg_data; + + cfg_data = readl(info->regs + VF610_REG_ADC_CFG); + + cfg_data &= ~VF610_ADC_ADLPC_EN; + if (adc_feature->conv_mode == VF610_ADC_CONV_LOW_POWER) + cfg_data |= VF610_ADC_ADLPC_EN; + + cfg_data &= ~VF610_ADC_ADHSC_EN; + if (adc_feature->conv_mode == VF610_ADC_CONV_HIGH_SPEED) + cfg_data |= VF610_ADC_ADHSC_EN; + + writel(cfg_data, info->regs + VF610_REG_ADC_CFG); +} + +static void vf610_adc_sample_set(struct vf610_adc *info) +{ + struct vf610_adc_feature *adc_feature = &(info->adc_feature); + int cfg_data, gc_data; + + cfg_data = readl(info->regs + VF610_REG_ADC_CFG); + gc_data = readl(info->regs + VF610_REG_ADC_GC); + + /* resolution mode */ + cfg_data &= ~VF610_ADC_MODE_MASK; + switch (adc_feature->res_mode) { + case 8: + cfg_data |= VF610_ADC_MODE_BIT8; + break; + case 10: + cfg_data |= VF610_ADC_MODE_BIT10; + break; + case 12: + cfg_data |= VF610_ADC_MODE_BIT12; + break; + default: + dev_err(info->dev, "error resolution mode\n"); + break; + } + + /* clock select and clock divider */ + cfg_data &= ~(VF610_ADC_CLK_MASK | VF610_ADC_ADCCLK_MASK); + switch (adc_feature->clk_div) { + case 1: + break; + case 2: + cfg_data |= VF610_ADC_CLK_DIV2; + break; + case 4: + cfg_data |= VF610_ADC_CLK_DIV4; + break; + case 8: + cfg_data |= VF610_ADC_CLK_DIV8; + break; + case 16: + switch (adc_feature->clk_sel) { + case VF610_ADCIOC_BUSCLK_SET: + cfg_data |= VF610_ADC_BUSCLK2_SEL | VF610_ADC_CLK_DIV8; + break; + default: + dev_err(info->dev, "error clk divider\n"); + break; + } + break; + } + + /* + * Set ADLSMP and ADSTS based on the Long Sample Time Adder value + * determined. + */ + switch (adc_feature->lst_adder_index) { + case VF610_ADCK_CYCLES_3: + break; + case VF610_ADCK_CYCLES_5: + cfg_data |= VF610_ADC_ADSTS_SHORT; + break; + case VF610_ADCK_CYCLES_7: + cfg_data |= VF610_ADC_ADSTS_NORMAL; + break; + case VF610_ADCK_CYCLES_9: + cfg_data |= VF610_ADC_ADSTS_LONG; + break; + case VF610_ADCK_CYCLES_13: + cfg_data |= VF610_ADC_ADLSMP_LONG; + break; + case VF610_ADCK_CYCLES_17: + cfg_data |= VF610_ADC_ADLSMP_LONG; + cfg_data |= VF610_ADC_ADSTS_SHORT; + break; + case VF610_ADCK_CYCLES_21: + cfg_data |= VF610_ADC_ADLSMP_LONG; + cfg_data |= VF610_ADC_ADSTS_NORMAL; + break; + case VF610_ADCK_CYCLES_25: + cfg_data |= VF610_ADC_ADLSMP_LONG; + cfg_data |= VF610_ADC_ADSTS_NORMAL; + break; + default: + dev_err(info->dev, "error in sample time select\n"); + } + + /* update hardware average selection */ + cfg_data &= ~VF610_ADC_AVGS_MASK; + gc_data &= ~VF610_ADC_AVGEN; + switch (adc_feature->sample_rate) { + case VF610_ADC_SAMPLE_1: + break; + case VF610_ADC_SAMPLE_4: + gc_data |= VF610_ADC_AVGEN; + break; + case VF610_ADC_SAMPLE_8: + gc_data |= VF610_ADC_AVGEN; + cfg_data |= VF610_ADC_AVGS_8; + break; + case VF610_ADC_SAMPLE_16: + gc_data |= VF610_ADC_AVGEN; + cfg_data |= VF610_ADC_AVGS_16; + break; + case VF610_ADC_SAMPLE_32: + gc_data |= VF610_ADC_AVGEN; + cfg_data |= VF610_ADC_AVGS_32; + break; + default: + dev_err(info->dev, + "error hardware sample average select\n"); + } + + writel(cfg_data, info->regs + VF610_REG_ADC_CFG); + writel(gc_data, info->regs + VF610_REG_ADC_GC); +} + +static void vf610_adc_hw_init(struct vf610_adc *info) +{ + /* CFG: Feature set */ + vf610_adc_cfg_post_set(info); + vf610_adc_sample_set(info); + + /* adc calibration */ + vf610_adc_calibration(info); + + /* CFG: power and speed set */ + vf610_adc_cfg_set(info); +} + +static int vf610_set_conversion_mode(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + unsigned int mode) +{ + struct vf610_adc *info = iio_priv(indio_dev); + + mutex_lock(&info->lock); + info->adc_feature.conv_mode = mode; + vf610_adc_calculate_rates(info); + vf610_adc_hw_init(info); + mutex_unlock(&info->lock); + + return 0; +} + +static int vf610_get_conversion_mode(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan) +{ + struct vf610_adc *info = iio_priv(indio_dev); + + return info->adc_feature.conv_mode; +} + +static const char * const vf610_conv_modes[] = { "normal", "high-speed", + "low-power" }; + +static const struct iio_enum vf610_conversion_mode = { + .items = vf610_conv_modes, + .num_items = ARRAY_SIZE(vf610_conv_modes), + .get = vf610_get_conversion_mode, + .set = vf610_set_conversion_mode, +}; + +static const struct iio_chan_spec_ext_info vf610_ext_info[] = { + IIO_ENUM("conversion_mode", IIO_SHARED_BY_DIR, &vf610_conversion_mode), + { } +}; + +#define VF610_ADC_CHAN(_idx, _chan_type) { \ + .type = (_chan_type), \ + .indexed = 1, \ + .channel = (_idx), \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | \ + BIT(IIO_CHAN_INFO_SAMP_FREQ), \ + .ext_info = vf610_ext_info, \ + .scan_index = (_idx), \ + .scan_type = { \ + .sign = 'u', \ + .realbits = 12, \ + .storagebits = 16, \ + }, \ +} + +#define VF610_ADC_TEMPERATURE_CHAN(_idx, _chan_type) { \ + .type = (_chan_type), \ + .channel = (_idx), \ + .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED), \ + .scan_index = (_idx), \ + .scan_type = { \ + .sign = 'u', \ + .realbits = 12, \ + .storagebits = 16, \ + }, \ +} + +static const struct iio_chan_spec vf610_adc_iio_channels[] = { + VF610_ADC_CHAN(0, IIO_VOLTAGE), + VF610_ADC_CHAN(1, IIO_VOLTAGE), + VF610_ADC_CHAN(2, IIO_VOLTAGE), + VF610_ADC_CHAN(3, IIO_VOLTAGE), + VF610_ADC_CHAN(4, IIO_VOLTAGE), + VF610_ADC_CHAN(5, IIO_VOLTAGE), + VF610_ADC_CHAN(6, IIO_VOLTAGE), + VF610_ADC_CHAN(7, IIO_VOLTAGE), + VF610_ADC_CHAN(8, IIO_VOLTAGE), + VF610_ADC_CHAN(9, IIO_VOLTAGE), + VF610_ADC_CHAN(10, IIO_VOLTAGE), + VF610_ADC_CHAN(11, IIO_VOLTAGE), + VF610_ADC_CHAN(12, IIO_VOLTAGE), + VF610_ADC_CHAN(13, IIO_VOLTAGE), + VF610_ADC_CHAN(14, IIO_VOLTAGE), + VF610_ADC_CHAN(15, IIO_VOLTAGE), + VF610_ADC_TEMPERATURE_CHAN(26, IIO_TEMP), + IIO_CHAN_SOFT_TIMESTAMP(32), + /* sentinel */ +}; + +static int vf610_adc_read_data(struct vf610_adc *info) +{ + int result; + + result = readl(info->regs + VF610_REG_ADC_R0); + + switch (info->adc_feature.res_mode) { + case 8: + result &= 0xFF; + break; + case 10: + result &= 0x3FF; + break; + case 12: + result &= 0xFFF; + break; + default: + break; + } + + return result; +} + +static irqreturn_t vf610_adc_isr(int irq, void *dev_id) +{ + struct iio_dev *indio_dev = dev_id; + struct vf610_adc *info = iio_priv(indio_dev); + int coco; + + coco = readl(info->regs + VF610_REG_ADC_HS); + if (coco & VF610_ADC_HS_COCO0) { + info->value = vf610_adc_read_data(info); + if (iio_buffer_enabled(indio_dev)) { + info->scan.chan = info->value; + iio_push_to_buffers_with_ts(indio_dev, &info->scan, + sizeof(info->scan), + iio_get_time_ns(indio_dev)); + iio_trigger_notify_done(indio_dev->trig); + } else + complete(&info->completion); + } + + return IRQ_HANDLED; +} + +static ssize_t vf610_show_samp_freq_avail(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct vf610_adc *info = iio_priv(dev_to_iio_dev(dev)); + size_t len = 0; + int i; + + for (i = 0; i < ARRAY_SIZE(info->sample_freq_avail); i++) + len += scnprintf(buf + len, PAGE_SIZE - len, + "%u ", info->sample_freq_avail[i]); + + /* replace trailing space by newline */ + buf[len - 1] = '\n'; + + return len; +} + +static IIO_DEV_ATTR_SAMP_FREQ_AVAIL(vf610_show_samp_freq_avail); + +static struct attribute *vf610_attributes[] = { + &iio_dev_attr_sampling_frequency_available.dev_attr.attr, + NULL +}; + +static const struct attribute_group vf610_attribute_group = { + .attrs = vf610_attributes, +}; + +static int vf610_read_sample(struct vf610_adc *info, + struct iio_chan_spec const *chan, int *val) +{ + unsigned int hc_cfg; + int ret; + + guard(mutex)(&info->lock); + reinit_completion(&info->completion); + hc_cfg = VF610_ADC_ADCHC(chan->channel); + hc_cfg |= VF610_ADC_AIEN; + writel(hc_cfg, info->regs + VF610_REG_ADC_HC0); + ret = wait_for_completion_interruptible_timeout(&info->completion, + VF610_ADC_TIMEOUT); + if (ret == 0) + return -ETIMEDOUT; + + if (ret < 0) + return ret; + + switch (chan->type) { + case IIO_VOLTAGE: + *val = info->value; + return 0; + case IIO_TEMP: + /* + * Calculate in degree Celsius times 1000 + * Using the typical sensor slope of 1.84 mV/°C + * and VREFH_ADC at 3.3V, V at 25°C of 699 mV + */ + *val = 25000 - ((int)info->value - VF610_VTEMP25_3V3) * + 1000000 / VF610_TEMP_SLOPE_COEFF; + + return 0; + default: + return -EINVAL; + } +} + +static int vf610_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, + int *val2, + long mask) +{ + struct vf610_adc *info = iio_priv(indio_dev); + long ret; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + case IIO_CHAN_INFO_PROCESSED: + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; + ret = vf610_read_sample(info, chan, val); + iio_device_release_direct(indio_dev); + if (ret < 0) + return ret; + + return IIO_VAL_INT; + + case IIO_CHAN_INFO_SCALE: + *val = info->vref_uv / 1000; + *val2 = info->adc_feature.res_mode; + return IIO_VAL_FRACTIONAL_LOG2; + + case IIO_CHAN_INFO_SAMP_FREQ: + *val = info->sample_freq_avail[info->adc_feature.sample_rate]; + *val2 = 0; + return IIO_VAL_INT; + + default: + break; + } + + return -EINVAL; +} + +static int vf610_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, + int val2, + long mask) +{ + struct vf610_adc *info = iio_priv(indio_dev); + int i; + + switch (mask) { + case IIO_CHAN_INFO_SAMP_FREQ: + for (i = 0; + i < ARRAY_SIZE(info->sample_freq_avail); + i++) + if (val == info->sample_freq_avail[i]) { + info->adc_feature.sample_rate = i; + vf610_adc_sample_set(info); + return 0; + } + break; + + default: + break; + } + + return -EINVAL; +} + +static int vf610_adc_buffer_postenable(struct iio_dev *indio_dev) +{ + struct vf610_adc *info = iio_priv(indio_dev); + unsigned int channel; + int val; + + val = readl(info->regs + VF610_REG_ADC_GC); + val |= VF610_ADC_ADCON; + writel(val, info->regs + VF610_REG_ADC_GC); + + channel = find_first_bit(indio_dev->active_scan_mask, + iio_get_masklength(indio_dev)); + + val = VF610_ADC_ADCHC(channel); + val |= VF610_ADC_AIEN; + + writel(val, info->regs + VF610_REG_ADC_HC0); + + return 0; +} + +static int vf610_adc_buffer_predisable(struct iio_dev *indio_dev) +{ + struct vf610_adc *info = iio_priv(indio_dev); + unsigned int hc_cfg = 0; + int val; + + val = readl(info->regs + VF610_REG_ADC_GC); + val &= ~VF610_ADC_ADCON; + writel(val, info->regs + VF610_REG_ADC_GC); + + hc_cfg |= VF610_ADC_CONV_DISABLE; + hc_cfg &= ~VF610_ADC_AIEN; + + writel(hc_cfg, info->regs + VF610_REG_ADC_HC0); + + return 0; +} + +static const struct iio_buffer_setup_ops iio_triggered_buffer_setup_ops = { + .postenable = &vf610_adc_buffer_postenable, + .predisable = &vf610_adc_buffer_predisable, + .validate_scan_mask = &iio_validate_scan_mask_onehot, +}; + +static int vf610_adc_reg_access(struct iio_dev *indio_dev, + unsigned reg, unsigned writeval, + unsigned *readval) +{ + struct vf610_adc *info = iio_priv(indio_dev); + + if ((readval == NULL) || + ((reg % 4) || (reg > VF610_REG_ADC_PCTL))) + return -EINVAL; + + *readval = readl(info->regs + reg); + + return 0; +} + +static const struct iio_info vf610_adc_iio_info = { + .read_raw = &vf610_read_raw, + .write_raw = &vf610_write_raw, + .debugfs_reg_access = &vf610_adc_reg_access, + .attrs = &vf610_attribute_group, +}; + +static const struct vf610_chip_info vf610_chip_info = { + .num_channels = ARRAY_SIZE(vf610_adc_iio_channels), +}; + +static const struct vf610_chip_info imx6sx_chip_info = { + .num_channels = 4, +}; + +static const struct of_device_id vf610_adc_match[] = { + { .compatible = "fsl,imx6sx-adc", .data = &imx6sx_chip_info}, + { .compatible = "fsl,vf610-adc", .data = &vf610_chip_info}, + { } +}; +MODULE_DEVICE_TABLE(of, vf610_adc_match); + +static void vf610_adc_action_remove(void *d) +{ + struct vf610_adc *info = d; + + regulator_disable(info->vref); +} + +static int vf610_adc_probe(struct platform_device *pdev) +{ + const struct vf610_chip_info *chip_info; + struct device *dev = &pdev->dev; + struct vf610_adc *info; + struct iio_dev *indio_dev; + int irq; + int ret; + + indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(struct vf610_adc)); + if (!indio_dev) + return -ENOMEM; + + info = iio_priv(indio_dev); + info->dev = &pdev->dev; + + info->regs = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(info->regs)) + return PTR_ERR(info->regs); + + chip_info = device_get_match_data(dev); + + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return irq; + + ret = devm_request_irq(info->dev, irq, + vf610_adc_isr, 0, + dev_name(&pdev->dev), indio_dev); + if (ret < 0) + return dev_err_probe(&pdev->dev, ret, "failed requesting irq, irq = %d\n", irq); + + info->clk = devm_clk_get_enabled(&pdev->dev, "adc"); + if (IS_ERR(info->clk)) + return dev_err_probe(&pdev->dev, PTR_ERR(info->clk), "failed getting clock\n"); + + info->vref = devm_regulator_get(&pdev->dev, "vref"); + if (IS_ERR(info->vref)) + return PTR_ERR(info->vref); + + ret = regulator_enable(info->vref); + if (ret) + return ret; + + ret = devm_add_action_or_reset(&pdev->dev, vf610_adc_action_remove, info); + if (ret) + return ret; + + info->vref_uv = regulator_get_voltage(info->vref); + + device_property_read_u32_array(dev, "fsl,adck-max-frequency", info->max_adck_rate, 3); + + info->adc_feature.default_sample_time = DEFAULT_SAMPLE_TIME; + device_property_read_u32(dev, "min-sample-time", &info->adc_feature.default_sample_time); + + platform_set_drvdata(pdev, indio_dev); + + init_completion(&info->completion); + + indio_dev->name = dev_name(&pdev->dev); + indio_dev->info = &vf610_adc_iio_info; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = vf610_adc_iio_channels; + indio_dev->num_channels = chip_info->num_channels; + + vf610_adc_cfg_init(info); + vf610_adc_hw_init(info); + + ret = devm_iio_triggered_buffer_setup(&pdev->dev, indio_dev, &iio_pollfunc_store_time, + NULL, &iio_triggered_buffer_setup_ops); + if (ret < 0) + return dev_err_probe(&pdev->dev, ret, "Couldn't initialise the buffer\n"); + + mutex_init(&info->lock); + + ret = devm_iio_device_register(&pdev->dev, indio_dev); + if (ret) + return dev_err_probe(&pdev->dev, ret, "Couldn't register the device.\n"); + + return 0; +} + +static int vf610_adc_suspend(struct device *dev) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct vf610_adc *info = iio_priv(indio_dev); + int hc_cfg; + + /* ADC controller enters to stop mode */ + hc_cfg = readl(info->regs + VF610_REG_ADC_HC0); + hc_cfg |= VF610_ADC_CONV_DISABLE; + writel(hc_cfg, info->regs + VF610_REG_ADC_HC0); + + clk_disable_unprepare(info->clk); + regulator_disable(info->vref); + + return 0; +} + +static int vf610_adc_resume(struct device *dev) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct vf610_adc *info = iio_priv(indio_dev); + int ret; + + ret = regulator_enable(info->vref); + if (ret) + return ret; + + ret = clk_prepare_enable(info->clk); + if (ret) + goto disable_reg; + + vf610_adc_hw_init(info); + + return 0; + +disable_reg: + regulator_disable(info->vref); + return ret; +} + +static DEFINE_SIMPLE_DEV_PM_OPS(vf610_adc_pm_ops, vf610_adc_suspend, + vf610_adc_resume); + +static struct platform_driver vf610_adc_driver = { + .probe = vf610_adc_probe, + .driver = { + .name = "vf610-adc", + .of_match_table = vf610_adc_match, + .pm = pm_sleep_ptr(&vf610_adc_pm_ops), + }, +}; + +module_platform_driver(vf610_adc_driver); + +MODULE_AUTHOR("Fugang Duan "); +MODULE_DESCRIPTION("Freescale VF610 ADC driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/adc/viperboard_adc.c b/drivers/iio/adc/viperboard_adc.c new file mode 100644 index 000000000..6efe1c618 --- /dev/null +++ b/drivers/iio/adc/viperboard_adc.c @@ -0,0 +1,150 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Nano River Technologies viperboard IIO ADC driver + * + * (C) 2012 by Lemonage GmbH + * Author: Lars Poeschel + * All rights reserved. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +#define VPRBRD_ADC_CMD_GET 0x00 + +struct vprbrd_adc_msg { + u8 cmd; + u8 chan; + u8 val; +} __packed; + +struct vprbrd_adc { + struct vprbrd *vb; +}; + +#define VPRBRD_ADC_CHANNEL(_index) { \ + .type = IIO_VOLTAGE, \ + .indexed = 1, \ + .channel = _index, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ +} + +static struct iio_chan_spec const vprbrd_adc_iio_channels[] = { + VPRBRD_ADC_CHANNEL(0), + VPRBRD_ADC_CHANNEL(1), + VPRBRD_ADC_CHANNEL(2), + VPRBRD_ADC_CHANNEL(3), +}; + +static int vprbrd_iio_read_raw(struct iio_dev *iio_dev, + struct iio_chan_spec const *chan, + int *val, + int *val2, + long info) +{ + int ret, error = 0; + struct vprbrd_adc *adc = iio_priv(iio_dev); + struct vprbrd *vb = adc->vb; + struct vprbrd_adc_msg *admsg = (struct vprbrd_adc_msg *)vb->buf; + + switch (info) { + case IIO_CHAN_INFO_RAW: + mutex_lock(&vb->lock); + + admsg->cmd = VPRBRD_ADC_CMD_GET; + admsg->chan = chan->channel; + admsg->val = 0x00; + + ret = usb_control_msg(vb->usb_dev, + usb_sndctrlpipe(vb->usb_dev, 0), VPRBRD_USB_REQUEST_ADC, + VPRBRD_USB_TYPE_OUT, 0x0000, 0x0000, admsg, + sizeof(struct vprbrd_adc_msg), VPRBRD_USB_TIMEOUT_MS); + if (ret != sizeof(struct vprbrd_adc_msg)) { + mutex_unlock(&vb->lock); + error = -EREMOTEIO; + dev_err(&iio_dev->dev, "usb send error on adc read\n"); + goto error; + } + + ret = usb_control_msg(vb->usb_dev, + usb_rcvctrlpipe(vb->usb_dev, 0), VPRBRD_USB_REQUEST_ADC, + VPRBRD_USB_TYPE_IN, 0x0000, 0x0000, admsg, + sizeof(struct vprbrd_adc_msg), VPRBRD_USB_TIMEOUT_MS); + + *val = admsg->val; + + mutex_unlock(&vb->lock); + + if (ret != sizeof(struct vprbrd_adc_msg)) { + dev_err(&iio_dev->dev, "usb recv error on adc read\n"); + error = -EREMOTEIO; + } + + if (error) + goto error; + + return IIO_VAL_INT; + default: + error = -EINVAL; + break; + } +error: + return error; +} + +static const struct iio_info vprbrd_adc_iio_info = { + .read_raw = &vprbrd_iio_read_raw, +}; + +static int vprbrd_adc_probe(struct platform_device *pdev) +{ + struct vprbrd *vb = dev_get_drvdata(pdev->dev.parent); + struct vprbrd_adc *adc; + struct iio_dev *indio_dev; + int ret; + + /* registering iio */ + indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*adc)); + if (!indio_dev) + return -ENOMEM; + + adc = iio_priv(indio_dev); + adc->vb = vb; + indio_dev->name = "viperboard adc"; + indio_dev->info = &vprbrd_adc_iio_info; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = vprbrd_adc_iio_channels; + indio_dev->num_channels = ARRAY_SIZE(vprbrd_adc_iio_channels); + + ret = devm_iio_device_register(&pdev->dev, indio_dev); + if (ret) { + dev_err(&pdev->dev, "could not register iio (adc)"); + return ret; + } + + return 0; +} + +static struct platform_driver vprbrd_adc_driver = { + .driver = { + .name = "viperboard-adc", + }, + .probe = vprbrd_adc_probe, +}; + +module_platform_driver(vprbrd_adc_driver); + +MODULE_AUTHOR("Lars Poeschel "); +MODULE_DESCRIPTION("IIO ADC driver for Nano River Techs Viperboard"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:viperboard-adc"); diff --git a/drivers/iio/adc/xilinx-ams.c b/drivers/iio/adc/xilinx-ams.c new file mode 100644 index 000000000..158e6133a --- /dev/null +++ b/drivers/iio/adc/xilinx-ams.c @@ -0,0 +1,1401 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Xilinx AMS driver + * + * Copyright (C) 2021 Xilinx, Inc. + * + * Manish Narani + * Rajnikant Bhojani + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +/* AMS registers definitions */ +#define AMS_ISR_0 0x010 +#define AMS_ISR_1 0x014 +#define AMS_IER_0 0x020 +#define AMS_IER_1 0x024 +#define AMS_IDR_0 0x028 +#define AMS_IDR_1 0x02C +#define AMS_PS_CSTS 0x040 +#define AMS_PL_CSTS 0x044 + +#define AMS_VCC_PSPLL0 0x060 +#define AMS_VCC_PSPLL3 0x06C +#define AMS_VCCINT 0x078 +#define AMS_VCCBRAM 0x07C +#define AMS_VCCAUX 0x080 +#define AMS_PSDDRPLL 0x084 +#define AMS_PSINTFPDDR 0x09C + +#define AMS_VCC_PSPLL0_CH 48 +#define AMS_VCC_PSPLL3_CH 51 +#define AMS_VCCINT_CH 54 +#define AMS_VCCBRAM_CH 55 +#define AMS_VCCAUX_CH 56 +#define AMS_PSDDRPLL_CH 57 +#define AMS_PSINTFPDDR_CH 63 + +#define AMS_REG_CONFIG0 0x100 +#define AMS_REG_CONFIG1 0x104 +#define AMS_REG_CONFIG3 0x10C +#define AMS_REG_CONFIG4 0x110 +#define AMS_REG_SEQ_CH0 0x120 +#define AMS_REG_SEQ_CH1 0x124 +#define AMS_REG_SEQ_CH2 0x118 + +#define AMS_VUSER0_MASK BIT(0) +#define AMS_VUSER1_MASK BIT(1) +#define AMS_VUSER2_MASK BIT(2) +#define AMS_VUSER3_MASK BIT(3) + +#define AMS_TEMP 0x000 +#define AMS_SUPPLY1 0x004 +#define AMS_SUPPLY2 0x008 +#define AMS_VP_VN 0x00C +#define AMS_VREFP 0x010 +#define AMS_VREFN 0x014 +#define AMS_SUPPLY3 0x018 +#define AMS_SUPPLY4 0x034 +#define AMS_SUPPLY5 0x038 +#define AMS_SUPPLY6 0x03C +#define AMS_SUPPLY7 0x200 +#define AMS_SUPPLY8 0x204 +#define AMS_SUPPLY9 0x208 +#define AMS_SUPPLY10 0x20C +#define AMS_VCCAMS 0x210 +#define AMS_TEMP_REMOTE 0x214 + +#define AMS_REG_VAUX(x) (0x40 + 4 * (x)) + +#define AMS_PS_RESET_VALUE 0xFFFF +#define AMS_PL_RESET_VALUE 0xFFFF + +#define AMS_CONF0_CHANNEL_NUM_MASK GENMASK(6, 0) + +#define AMS_CONF1_SEQ_MASK GENMASK(15, 12) +#define AMS_CONF1_SEQ_DEFAULT FIELD_PREP(AMS_CONF1_SEQ_MASK, 0) +#define AMS_CONF1_SEQ_CONTINUOUS FIELD_PREP(AMS_CONF1_SEQ_MASK, 2) +#define AMS_CONF1_SEQ_SINGLE_CHANNEL FIELD_PREP(AMS_CONF1_SEQ_MASK, 3) + +#define AMS_REG_SEQ0_MASK GENMASK(15, 0) +#define AMS_REG_SEQ2_MASK GENMASK(21, 16) +#define AMS_REG_SEQ1_MASK GENMASK_ULL(37, 22) + +#define AMS_PS_SEQ_MASK GENMASK(21, 0) +#define AMS_PL_SEQ_MASK GENMASK_ULL(59, 22) + +#define AMS_ALARM_NONE 0x000 /* not a real offset */ +#define AMS_ALARM_TEMP 0x140 +#define AMS_ALARM_SUPPLY1 0x144 +#define AMS_ALARM_SUPPLY2 0x148 +#define AMS_ALARM_SUPPLY3 0x160 +#define AMS_ALARM_SUPPLY4 0x164 +#define AMS_ALARM_SUPPLY5 0x168 +#define AMS_ALARM_SUPPLY6 0x16C +#define AMS_ALARM_SUPPLY7 0x180 +#define AMS_ALARM_SUPPLY8 0x184 +#define AMS_ALARM_SUPPLY9 0x188 +#define AMS_ALARM_SUPPLY10 0x18C +#define AMS_ALARM_VCCAMS 0x190 +#define AMS_ALARM_TEMP_REMOTE 0x194 +#define AMS_ALARM_THRESHOLD_OFF_10 0x10 +#define AMS_ALARM_THRESHOLD_OFF_20 0x20 + +#define AMS_ALARM_THR_DIRECT_MASK BIT(0) +#define AMS_ALARM_THR_MIN 0x0000 +#define AMS_ALARM_THR_MAX (BIT(16) - 1) + +#define AMS_ALARM_MASK GENMASK_ULL(63, 0) +#define AMS_NO_OF_ALARMS 32 +#define AMS_PL_ALARM_START 16 +#define AMS_PL_ALARM_MASK GENMASK(31, 16) +#define AMS_ISR0_ALARM_MASK GENMASK(31, 0) +#define AMS_ISR1_ALARM_MASK (GENMASK(31, 29) | GENMASK(4, 0)) +#define AMS_ISR1_EOC_MASK BIT(3) +#define AMS_ISR1_INTR_MASK GENMASK_ULL(63, 32) +#define AMS_ISR0_ALARM_2_TO_0_MASK GENMASK(2, 0) +#define AMS_ISR0_ALARM_6_TO_3_MASK GENMASK(6, 3) +#define AMS_ISR0_ALARM_12_TO_7_MASK GENMASK(13, 8) +#define AMS_CONF1_ALARM_2_TO_0_MASK GENMASK(3, 1) +#define AMS_CONF1_ALARM_6_TO_3_MASK GENMASK(11, 8) +#define AMS_CONF1_ALARM_12_TO_7_MASK GENMASK(5, 0) +#define AMS_REGCFG1_ALARM_MASK \ + (AMS_CONF1_ALARM_2_TO_0_MASK | AMS_CONF1_ALARM_6_TO_3_MASK | BIT(0)) +#define AMS_REGCFG3_ALARM_MASK AMS_CONF1_ALARM_12_TO_7_MASK + +#define AMS_PS_CSTS_PS_READY (BIT(27) | BIT(16)) +#define AMS_PL_CSTS_ACCESS_MASK BIT(1) + +#define AMS_PL_MAX_FIXED_CHANNEL 10 +#define AMS_PL_MAX_EXT_CHANNEL 20 + +#define AMS_INIT_POLL_TIME_US 200 +#define AMS_INIT_TIMEOUT_US 10000 +#define AMS_UNMASK_TIMEOUT_MS 500 + +/* + * Following scale and offset value is derived from + * UG580 (v1.7) December 20, 2016 + */ +#define AMS_SUPPLY_SCALE_1VOLT_mV 1000 +#define AMS_SUPPLY_SCALE_3VOLT_mV 3000 +#define AMS_SUPPLY_SCALE_6VOLT_mV 6000 +#define AMS_SUPPLY_SCALE_DIV_BIT 16 + +#define AMS_TEMP_SCALE 509314 +#define AMS_TEMP_SCALE_DIV_BIT 16 +#define AMS_TEMP_OFFSET -((280230LL << 16) / 509314) + +enum ams_alarm_bit { + AMS_ALARM_BIT_TEMP = 0, + AMS_ALARM_BIT_SUPPLY1 = 1, + AMS_ALARM_BIT_SUPPLY2 = 2, + AMS_ALARM_BIT_SUPPLY3 = 3, + AMS_ALARM_BIT_SUPPLY4 = 4, + AMS_ALARM_BIT_SUPPLY5 = 5, + AMS_ALARM_BIT_SUPPLY6 = 6, + AMS_ALARM_BIT_RESERVED = 7, + AMS_ALARM_BIT_SUPPLY7 = 8, + AMS_ALARM_BIT_SUPPLY8 = 9, + AMS_ALARM_BIT_SUPPLY9 = 10, + AMS_ALARM_BIT_SUPPLY10 = 11, + AMS_ALARM_BIT_VCCAMS = 12, + AMS_ALARM_BIT_TEMP_REMOTE = 13, +}; + +enum ams_seq { + AMS_SEQ_VCC_PSPLL = 0, + AMS_SEQ_VCC_PSBATT = 1, + AMS_SEQ_VCCINT = 2, + AMS_SEQ_VCCBRAM = 3, + AMS_SEQ_VCCAUX = 4, + AMS_SEQ_PSDDRPLL = 5, + AMS_SEQ_INTDDR = 6, +}; + +enum ams_ps_pl_seq { + AMS_SEQ_CALIB = 0, + AMS_SEQ_RSVD_1 = 1, + AMS_SEQ_RSVD_2 = 2, + AMS_SEQ_TEST = 3, + AMS_SEQ_RSVD_4 = 4, + AMS_SEQ_SUPPLY4 = 5, + AMS_SEQ_SUPPLY5 = 6, + AMS_SEQ_SUPPLY6 = 7, + AMS_SEQ_TEMP = 8, + AMS_SEQ_SUPPLY2 = 9, + AMS_SEQ_SUPPLY1 = 10, + AMS_SEQ_VP_VN = 11, + AMS_SEQ_VREFP = 12, + AMS_SEQ_VREFN = 13, + AMS_SEQ_SUPPLY3 = 14, + AMS_SEQ_CURRENT_MON = 15, + AMS_SEQ_SUPPLY7 = 16, + AMS_SEQ_SUPPLY8 = 17, + AMS_SEQ_SUPPLY9 = 18, + AMS_SEQ_SUPPLY10 = 19, + AMS_SEQ_VCCAMS = 20, + AMS_SEQ_TEMP_REMOTE = 21, + AMS_SEQ_MAX = 22 +}; + +#define AMS_PS_SEQ_MAX AMS_SEQ_MAX +#define AMS_SEQ(x) (AMS_SEQ_MAX + (x)) +#define PS_SEQ(x) (x) +#define PL_SEQ(x) (AMS_PS_SEQ_MAX + (x)) +#define AMS_CTRL_SEQ_BASE (AMS_PS_SEQ_MAX * 3) + +#define AMS_CHAN_TEMP(_scan_index, _addr, _name) { \ + .type = IIO_TEMP, \ + .indexed = 1, \ + .address = (_addr), \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ + BIT(IIO_CHAN_INFO_SCALE) | \ + BIT(IIO_CHAN_INFO_OFFSET), \ + .event_spec = ams_temp_events, \ + .scan_index = _scan_index, \ + .num_event_specs = ARRAY_SIZE(ams_temp_events), \ + .datasheet_name = _name, \ +} + +#define AMS_CHAN_VOLTAGE(_scan_index, _addr, _alarm, _name) { \ + .type = IIO_VOLTAGE, \ + .indexed = 1, \ + .address = (_addr), \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ + BIT(IIO_CHAN_INFO_SCALE), \ + .event_spec = (_alarm) ? ams_voltage_events : NULL, \ + .scan_index = _scan_index, \ + .num_event_specs = (_alarm) ? ARRAY_SIZE(ams_voltage_events) : 0, \ + .datasheet_name = _name, \ +} + +#define AMS_PS_CHAN_TEMP(_scan_index, _addr, _name) \ + AMS_CHAN_TEMP(PS_SEQ(_scan_index), _addr, _name) +#define AMS_PS_CHAN_VOLTAGE(_scan_index, _addr, _name) \ + AMS_CHAN_VOLTAGE(PS_SEQ(_scan_index), _addr, true, _name) + +#define AMS_PL_CHAN_TEMP(_scan_index, _addr, _name) \ + AMS_CHAN_TEMP(PL_SEQ(_scan_index), _addr, _name) +#define AMS_PL_CHAN_VOLTAGE(_scan_index, _addr, _alarm, _name) \ + AMS_CHAN_VOLTAGE(PL_SEQ(_scan_index), _addr, _alarm, _name) +#define AMS_PL_AUX_CHAN_VOLTAGE(_auxno) \ + AMS_CHAN_VOLTAGE(PL_SEQ(AMS_SEQ(_auxno)), AMS_REG_VAUX(_auxno), false, \ + "VAUX" #_auxno) +#define AMS_CTRL_CHAN_VOLTAGE(_scan_index, _addr, _name) \ + AMS_CHAN_VOLTAGE(PL_SEQ(AMS_SEQ(AMS_SEQ(_scan_index))), _addr, false, \ + _name) + +/** + * struct ams - This structure contains necessary state for xilinx-ams to operate + * @base: physical base address of device + * @ps_base: physical base address of PS device + * @pl_base: physical base address of PL device + * @clk: clocks associated with the device + * @dev: pointer to device struct + * @lock: to handle multiple user interaction + * @intr_lock: to protect interrupt mask values + * @alarm_mask: alarm configuration + * @current_masked_alarm: currently masked due to alarm + * @intr_mask: interrupt configuration + * @ams_unmask_work: re-enables event once the event condition disappears + * + */ +struct ams { + void __iomem *base; + void __iomem *ps_base; + void __iomem *pl_base; + struct clk *clk; + struct device *dev; + struct mutex lock; + spinlock_t intr_lock; + unsigned int alarm_mask; + unsigned int current_masked_alarm; + u64 intr_mask; + struct delayed_work ams_unmask_work; +}; + +static inline void ams_ps_update_reg(struct ams *ams, unsigned int offset, + u32 mask, u32 data) +{ + u32 val, regval; + + val = readl(ams->ps_base + offset); + regval = (val & ~mask) | (data & mask); + writel(regval, ams->ps_base + offset); +} + +static inline void ams_pl_update_reg(struct ams *ams, unsigned int offset, + u32 mask, u32 data) +{ + u32 val, regval; + + val = readl(ams->pl_base + offset); + regval = (val & ~mask) | (data & mask); + writel(regval, ams->pl_base + offset); +} + +static void ams_update_intrmask(struct ams *ams, u64 mask, u64 val) +{ + u32 regval; + + ams->intr_mask = (ams->intr_mask & ~mask) | (val & mask); + + regval = ~(ams->intr_mask | ams->current_masked_alarm); + writel(regval, ams->base + AMS_IER_0); + + regval = ~(FIELD_GET(AMS_ISR1_INTR_MASK, ams->intr_mask)); + writel(regval, ams->base + AMS_IER_1); + + regval = ams->intr_mask | ams->current_masked_alarm; + writel(regval, ams->base + AMS_IDR_0); + + regval = FIELD_GET(AMS_ISR1_INTR_MASK, ams->intr_mask); + writel(regval, ams->base + AMS_IDR_1); +} + +static void ams_disable_all_alarms(struct ams *ams) +{ + /* disable PS module alarm */ + if (ams->ps_base) { + ams_ps_update_reg(ams, AMS_REG_CONFIG1, AMS_REGCFG1_ALARM_MASK, + AMS_REGCFG1_ALARM_MASK); + ams_ps_update_reg(ams, AMS_REG_CONFIG3, AMS_REGCFG3_ALARM_MASK, + AMS_REGCFG3_ALARM_MASK); + } + + /* disable PL module alarm */ + if (ams->pl_base) { + ams_pl_update_reg(ams, AMS_REG_CONFIG1, AMS_REGCFG1_ALARM_MASK, + AMS_REGCFG1_ALARM_MASK); + ams_pl_update_reg(ams, AMS_REG_CONFIG3, AMS_REGCFG3_ALARM_MASK, + AMS_REGCFG3_ALARM_MASK); + } +} + +static void ams_update_ps_alarm(struct ams *ams, unsigned long alarm_mask) +{ + u32 cfg; + u32 val; + + val = FIELD_GET(AMS_ISR0_ALARM_2_TO_0_MASK, alarm_mask); + cfg = ~(FIELD_PREP(AMS_CONF1_ALARM_2_TO_0_MASK, val)); + + val = FIELD_GET(AMS_ISR0_ALARM_6_TO_3_MASK, alarm_mask); + cfg &= ~(FIELD_PREP(AMS_CONF1_ALARM_6_TO_3_MASK, val)); + + ams_ps_update_reg(ams, AMS_REG_CONFIG1, AMS_REGCFG1_ALARM_MASK, cfg); + + val = FIELD_GET(AMS_ISR0_ALARM_12_TO_7_MASK, alarm_mask); + cfg = ~(FIELD_PREP(AMS_CONF1_ALARM_12_TO_7_MASK, val)); + ams_ps_update_reg(ams, AMS_REG_CONFIG3, AMS_REGCFG3_ALARM_MASK, cfg); +} + +static void ams_update_pl_alarm(struct ams *ams, unsigned long alarm_mask) +{ + unsigned long pl_alarm_mask; + u32 cfg; + u32 val; + + pl_alarm_mask = FIELD_GET(AMS_PL_ALARM_MASK, alarm_mask); + + val = FIELD_GET(AMS_ISR0_ALARM_2_TO_0_MASK, pl_alarm_mask); + cfg = ~(FIELD_PREP(AMS_CONF1_ALARM_2_TO_0_MASK, val)); + + val = FIELD_GET(AMS_ISR0_ALARM_6_TO_3_MASK, pl_alarm_mask); + cfg &= ~(FIELD_PREP(AMS_CONF1_ALARM_6_TO_3_MASK, val)); + + ams_pl_update_reg(ams, AMS_REG_CONFIG1, AMS_REGCFG1_ALARM_MASK, cfg); + + val = FIELD_GET(AMS_ISR0_ALARM_12_TO_7_MASK, pl_alarm_mask); + cfg = ~(FIELD_PREP(AMS_CONF1_ALARM_12_TO_7_MASK, val)); + ams_pl_update_reg(ams, AMS_REG_CONFIG3, AMS_REGCFG3_ALARM_MASK, cfg); +} + +static void ams_unmask(struct ams *ams) +{ + unsigned int status, unmask; + + status = readl(ams->base + AMS_ISR_0); + + /* Clear those bits which are not active anymore */ + unmask = (ams->current_masked_alarm ^ status) & ams->current_masked_alarm; + + /* Clear status of disabled alarm */ + unmask |= ams->intr_mask; + + ams->current_masked_alarm &= status; + + /* Also clear those which are masked out anyway */ + ams->current_masked_alarm &= ~ams->intr_mask; + + /* Clear the interrupts before we unmask them */ + writel(unmask, ams->base + AMS_ISR_0); + + ams_update_intrmask(ams, ~AMS_ALARM_MASK, ~AMS_ALARM_MASK); +} + +static void ams_update_alarm(struct ams *ams, unsigned long alarm_mask) +{ + unsigned long flags; + + if (ams->ps_base) + ams_update_ps_alarm(ams, alarm_mask); + + if (ams->pl_base) + ams_update_pl_alarm(ams, alarm_mask); + + spin_lock_irqsave(&ams->intr_lock, flags); + ams_update_intrmask(ams, AMS_ISR0_ALARM_MASK, ~alarm_mask); + ams_unmask(ams); + spin_unlock_irqrestore(&ams->intr_lock, flags); +} + +static void ams_enable_channel_sequence(struct iio_dev *indio_dev) +{ + struct ams *ams = iio_priv(indio_dev); + unsigned long long scan_mask; + int i; + u32 regval; + + /* + * Enable channel sequence. First 22 bits of scan_mask represent + * PS channels, and next remaining bits represent PL channels. + */ + + /* Run calibration of PS & PL as part of the sequence */ + scan_mask = BIT(0) | BIT(AMS_PS_SEQ_MAX); + for (i = 0; i < indio_dev->num_channels; i++) { + const struct iio_chan_spec *chan = &indio_dev->channels[i]; + + if (chan->scan_index < AMS_CTRL_SEQ_BASE) + scan_mask |= BIT_ULL(chan->scan_index); + } + + if (ams->ps_base) { + /* put sysmon in a soft reset to change the sequence */ + ams_ps_update_reg(ams, AMS_REG_CONFIG1, AMS_CONF1_SEQ_MASK, + AMS_CONF1_SEQ_DEFAULT); + + /* configure basic channels */ + regval = FIELD_GET(AMS_REG_SEQ0_MASK, scan_mask); + writel(regval, ams->ps_base + AMS_REG_SEQ_CH0); + + regval = FIELD_GET(AMS_REG_SEQ2_MASK, scan_mask); + writel(regval, ams->ps_base + AMS_REG_SEQ_CH2); + + /* set continuous sequence mode */ + ams_ps_update_reg(ams, AMS_REG_CONFIG1, AMS_CONF1_SEQ_MASK, + AMS_CONF1_SEQ_CONTINUOUS); + } + + if (ams->pl_base) { + /* put sysmon in a soft reset to change the sequence */ + ams_pl_update_reg(ams, AMS_REG_CONFIG1, AMS_CONF1_SEQ_MASK, + AMS_CONF1_SEQ_DEFAULT); + + /* configure basic channels */ + scan_mask = FIELD_GET(AMS_PL_SEQ_MASK, scan_mask); + + regval = FIELD_GET(AMS_REG_SEQ0_MASK, scan_mask); + writel(regval, ams->pl_base + AMS_REG_SEQ_CH0); + + regval = FIELD_GET(AMS_REG_SEQ1_MASK, scan_mask); + writel(regval, ams->pl_base + AMS_REG_SEQ_CH1); + + regval = FIELD_GET(AMS_REG_SEQ2_MASK, scan_mask); + writel(regval, ams->pl_base + AMS_REG_SEQ_CH2); + + /* set continuous sequence mode */ + ams_pl_update_reg(ams, AMS_REG_CONFIG1, AMS_CONF1_SEQ_MASK, + AMS_CONF1_SEQ_CONTINUOUS); + } +} + +static int ams_init_device(struct ams *ams) +{ + u32 expect = AMS_PS_CSTS_PS_READY; + u32 reg, value; + int ret; + + /* reset AMS */ + if (ams->ps_base) { + writel(AMS_PS_RESET_VALUE, ams->ps_base + AMS_VP_VN); + + ret = readl_poll_timeout(ams->base + AMS_PS_CSTS, reg, (reg & expect), + AMS_INIT_POLL_TIME_US, AMS_INIT_TIMEOUT_US); + if (ret) + return ret; + + /* put sysmon in a default state */ + ams_ps_update_reg(ams, AMS_REG_CONFIG1, AMS_CONF1_SEQ_MASK, + AMS_CONF1_SEQ_DEFAULT); + } + + if (ams->pl_base) { + value = readl(ams->base + AMS_PL_CSTS); + if (value == 0) + return 0; + + writel(AMS_PL_RESET_VALUE, ams->pl_base + AMS_VP_VN); + + /* put sysmon in a default state */ + ams_pl_update_reg(ams, AMS_REG_CONFIG1, AMS_CONF1_SEQ_MASK, + AMS_CONF1_SEQ_DEFAULT); + } + + ams_disable_all_alarms(ams); + + /* Disable interrupt */ + ams_update_intrmask(ams, AMS_ALARM_MASK, AMS_ALARM_MASK); + + /* Clear any pending interrupt */ + writel(AMS_ISR0_ALARM_MASK, ams->base + AMS_ISR_0); + writel(AMS_ISR1_ALARM_MASK, ams->base + AMS_ISR_1); + + return 0; +} + +static int ams_read_label(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, char *label) +{ + return sysfs_emit(label, "%s\n", chan->datasheet_name); +} + +static int ams_enable_single_channel(struct ams *ams, unsigned int offset) +{ + u8 channel_num; + + switch (offset) { + case AMS_VCC_PSPLL0: + channel_num = AMS_VCC_PSPLL0_CH; + break; + case AMS_VCC_PSPLL3: + channel_num = AMS_VCC_PSPLL3_CH; + break; + case AMS_VCCINT: + channel_num = AMS_VCCINT_CH; + break; + case AMS_VCCBRAM: + channel_num = AMS_VCCBRAM_CH; + break; + case AMS_VCCAUX: + channel_num = AMS_VCCAUX_CH; + break; + case AMS_PSDDRPLL: + channel_num = AMS_PSDDRPLL_CH; + break; + case AMS_PSINTFPDDR: + channel_num = AMS_PSINTFPDDR_CH; + break; + default: + return -EINVAL; + } + + /* put sysmon in a soft reset to change the sequence */ + ams_ps_update_reg(ams, AMS_REG_CONFIG1, AMS_CONF1_SEQ_MASK, + AMS_CONF1_SEQ_DEFAULT); + + /* write the channel number */ + ams_ps_update_reg(ams, AMS_REG_CONFIG0, AMS_CONF0_CHANNEL_NUM_MASK, + channel_num); + + /* set single channel, sequencer off mode */ + ams_ps_update_reg(ams, AMS_REG_CONFIG1, AMS_CONF1_SEQ_MASK, + AMS_CONF1_SEQ_SINGLE_CHANNEL); + + return 0; +} + +static int ams_read_vcc_reg(struct ams *ams, unsigned int offset, u32 *data) +{ + u32 expect = AMS_ISR1_EOC_MASK; + u32 reg; + int ret; + + ret = ams_enable_single_channel(ams, offset); + if (ret) + return ret; + + /* clear end-of-conversion flag, wait for next conversion to complete */ + writel(expect, ams->base + AMS_ISR_1); + ret = readl_poll_timeout(ams->base + AMS_ISR_1, reg, (reg & expect), + AMS_INIT_POLL_TIME_US, AMS_INIT_TIMEOUT_US); + if (ret) + return ret; + + *data = readl(ams->base + offset); + + return 0; +} + +static int ams_get_ps_scale(int address) +{ + int val; + + switch (address) { + case AMS_SUPPLY1: + case AMS_SUPPLY2: + case AMS_SUPPLY3: + case AMS_SUPPLY4: + case AMS_SUPPLY9: + case AMS_SUPPLY10: + case AMS_VCCAMS: + val = AMS_SUPPLY_SCALE_3VOLT_mV; + break; + case AMS_SUPPLY5: + case AMS_SUPPLY6: + case AMS_SUPPLY7: + case AMS_SUPPLY8: + val = AMS_SUPPLY_SCALE_6VOLT_mV; + break; + default: + val = AMS_SUPPLY_SCALE_1VOLT_mV; + break; + } + + return val; +} + +static int ams_get_pl_scale(struct ams *ams, int address) +{ + int val, regval; + + switch (address) { + case AMS_SUPPLY1: + case AMS_SUPPLY2: + case AMS_SUPPLY3: + case AMS_SUPPLY4: + case AMS_SUPPLY5: + case AMS_SUPPLY6: + case AMS_VCCAMS: + case AMS_VREFP: + case AMS_VREFN: + val = AMS_SUPPLY_SCALE_3VOLT_mV; + break; + case AMS_SUPPLY7: + regval = readl(ams->pl_base + AMS_REG_CONFIG4); + if (FIELD_GET(AMS_VUSER0_MASK, regval)) + val = AMS_SUPPLY_SCALE_6VOLT_mV; + else + val = AMS_SUPPLY_SCALE_3VOLT_mV; + break; + case AMS_SUPPLY8: + regval = readl(ams->pl_base + AMS_REG_CONFIG4); + if (FIELD_GET(AMS_VUSER1_MASK, regval)) + val = AMS_SUPPLY_SCALE_6VOLT_mV; + else + val = AMS_SUPPLY_SCALE_3VOLT_mV; + break; + case AMS_SUPPLY9: + regval = readl(ams->pl_base + AMS_REG_CONFIG4); + if (FIELD_GET(AMS_VUSER2_MASK, regval)) + val = AMS_SUPPLY_SCALE_6VOLT_mV; + else + val = AMS_SUPPLY_SCALE_3VOLT_mV; + break; + case AMS_SUPPLY10: + regval = readl(ams->pl_base + AMS_REG_CONFIG4); + if (FIELD_GET(AMS_VUSER3_MASK, regval)) + val = AMS_SUPPLY_SCALE_6VOLT_mV; + else + val = AMS_SUPPLY_SCALE_3VOLT_mV; + break; + case AMS_VP_VN: + case AMS_REG_VAUX(0) ... AMS_REG_VAUX(15): + val = AMS_SUPPLY_SCALE_1VOLT_mV; + break; + default: + val = AMS_SUPPLY_SCALE_1VOLT_mV; + break; + } + + return val; +} + +static int ams_get_ctrl_scale(int address) +{ + int val; + + switch (address) { + case AMS_VCC_PSPLL0: + case AMS_VCC_PSPLL3: + case AMS_VCCINT: + case AMS_VCCBRAM: + case AMS_VCCAUX: + case AMS_PSDDRPLL: + case AMS_PSINTFPDDR: + val = AMS_SUPPLY_SCALE_3VOLT_mV; + break; + default: + val = AMS_SUPPLY_SCALE_1VOLT_mV; + break; + } + + return val; +} + +static int ams_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct ams *ams = iio_priv(indio_dev); + int ret; + + switch (mask) { + case IIO_CHAN_INFO_RAW: { + guard(mutex)(&ams->lock); + if (chan->scan_index >= AMS_CTRL_SEQ_BASE) { + ret = ams_read_vcc_reg(ams, chan->address, val); + if (ret) + return ret; + ams_enable_channel_sequence(indio_dev); + } else if (chan->scan_index >= AMS_PS_SEQ_MAX) + *val = readl(ams->pl_base + chan->address); + else + *val = readl(ams->ps_base + chan->address); + + return IIO_VAL_INT; + } + case IIO_CHAN_INFO_SCALE: + switch (chan->type) { + case IIO_VOLTAGE: + if (chan->scan_index < AMS_PS_SEQ_MAX) + *val = ams_get_ps_scale(chan->address); + else if (chan->scan_index >= AMS_PS_SEQ_MAX && + chan->scan_index < AMS_CTRL_SEQ_BASE) + *val = ams_get_pl_scale(ams, chan->address); + else + *val = ams_get_ctrl_scale(chan->address); + + *val2 = AMS_SUPPLY_SCALE_DIV_BIT; + return IIO_VAL_FRACTIONAL_LOG2; + case IIO_TEMP: + *val = AMS_TEMP_SCALE; + *val2 = AMS_TEMP_SCALE_DIV_BIT; + return IIO_VAL_FRACTIONAL_LOG2; + default: + return -EINVAL; + } + case IIO_CHAN_INFO_OFFSET: + /* Only the temperature channel has an offset */ + *val = AMS_TEMP_OFFSET; + return IIO_VAL_INT; + default: + return -EINVAL; + } +} + +struct ams_alarm_map { + enum ams_ps_pl_seq scan_index; + unsigned int base_offset; +}; + +/* + * Array index matches enum ams_alarm_bit. + * Entries with base_offset == AMS_ALARM_NONE are unused/invalid + * (e.g. RESERVED) and must be skipped. + */ +static const struct ams_alarm_map alarm_map[] = { + [AMS_ALARM_BIT_TEMP] = { AMS_SEQ_TEMP, AMS_ALARM_TEMP }, + [AMS_ALARM_BIT_SUPPLY1] = { AMS_SEQ_SUPPLY1, AMS_ALARM_SUPPLY1 }, + [AMS_ALARM_BIT_SUPPLY2] = { AMS_SEQ_SUPPLY2, AMS_ALARM_SUPPLY2 }, + [AMS_ALARM_BIT_SUPPLY3] = { AMS_SEQ_SUPPLY3, AMS_ALARM_SUPPLY3 }, + [AMS_ALARM_BIT_SUPPLY4] = { AMS_SEQ_SUPPLY4, AMS_ALARM_SUPPLY4 }, + [AMS_ALARM_BIT_SUPPLY5] = { AMS_SEQ_SUPPLY5, AMS_ALARM_SUPPLY5 }, + [AMS_ALARM_BIT_SUPPLY6] = { AMS_SEQ_SUPPLY6, AMS_ALARM_SUPPLY6 }, + [AMS_ALARM_BIT_RESERVED] = { 0, AMS_ALARM_NONE }, + [AMS_ALARM_BIT_SUPPLY7] = { AMS_SEQ_SUPPLY7, AMS_ALARM_SUPPLY7 }, + [AMS_ALARM_BIT_SUPPLY8] = { AMS_SEQ_SUPPLY8, AMS_ALARM_SUPPLY8 }, + [AMS_ALARM_BIT_SUPPLY9] = { AMS_SEQ_SUPPLY9, AMS_ALARM_SUPPLY9 }, + [AMS_ALARM_BIT_SUPPLY10] = { AMS_SEQ_SUPPLY10, AMS_ALARM_SUPPLY10 }, + [AMS_ALARM_BIT_VCCAMS] = { AMS_SEQ_VCCAMS, AMS_ALARM_VCCAMS }, + [AMS_ALARM_BIT_TEMP_REMOTE] = { AMS_SEQ_TEMP_REMOTE, AMS_ALARM_TEMP_REMOTE }, +}; + +static int ams_scan_index_to_event(int scan_index) +{ + for (unsigned int i = 0; i < ARRAY_SIZE(alarm_map); i++) { + if (alarm_map[i].base_offset == AMS_ALARM_NONE) + continue; + + if (alarm_map[i].scan_index == scan_index) + return i; + } + + return -EINVAL; +} + +static int ams_get_alarm_offset(int scan_index, enum iio_event_direction dir) +{ + int offset, event; + + if (scan_index >= AMS_PS_SEQ_MAX) + scan_index -= AMS_PS_SEQ_MAX; + + if (dir == IIO_EV_DIR_FALLING) { + if (scan_index < AMS_SEQ_SUPPLY7) + offset = AMS_ALARM_THRESHOLD_OFF_10; + else + offset = AMS_ALARM_THRESHOLD_OFF_20; + } else { + offset = 0; + } + + event = ams_scan_index_to_event(scan_index); + if (event < 0 || alarm_map[event].base_offset == AMS_ALARM_NONE) + return 0; + + return alarm_map[event].base_offset + offset; +} + +static const struct iio_chan_spec *ams_event_to_channel(struct iio_dev *dev, + u32 event) +{ + int scan_index = 0, i; + + if (event >= AMS_PL_ALARM_START) { + event -= AMS_PL_ALARM_START; + scan_index = AMS_PS_SEQ_MAX; + } + + if (event >= ARRAY_SIZE(alarm_map)) + return NULL; + + if (alarm_map[event].base_offset == AMS_ALARM_NONE) + return NULL; + + scan_index += alarm_map[event].scan_index; + + for (i = 0; i < dev->num_channels; i++) + if (dev->channels[i].scan_index == scan_index) + break; + + if (i == dev->num_channels) + return NULL; + + return &dev->channels[i]; +} + +static int ams_get_alarm_mask(int scan_index) +{ + int bit = 0, event; + + if (scan_index >= AMS_PS_SEQ_MAX) { + bit = AMS_PL_ALARM_START; + scan_index -= AMS_PS_SEQ_MAX; + } + + event = ams_scan_index_to_event(scan_index); + if (event < 0) + return 0; + + return BIT(event + bit); +} + +static int ams_read_event_config(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir) +{ + struct ams *ams = iio_priv(indio_dev); + + return !!(ams->alarm_mask & ams_get_alarm_mask(chan->scan_index)); +} + +static int ams_write_event_config(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir, + bool state) +{ + struct ams *ams = iio_priv(indio_dev); + unsigned int alarm; + + alarm = ams_get_alarm_mask(chan->scan_index); + + guard(mutex)(&ams->lock); + + if (state) + ams->alarm_mask |= alarm; + else + ams->alarm_mask &= ~alarm; + + ams_update_alarm(ams, ams->alarm_mask); + + return 0; +} + +static int ams_read_event_value(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir, + enum iio_event_info info, int *val, int *val2) +{ + struct ams *ams = iio_priv(indio_dev); + unsigned int offset = ams_get_alarm_offset(chan->scan_index, dir); + + guard(mutex)(&ams->lock); + + if (chan->scan_index >= AMS_PS_SEQ_MAX) + *val = readl(ams->pl_base + offset); + else + *val = readl(ams->ps_base + offset); + + return IIO_VAL_INT; +} + +static int ams_write_event_value(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir, + enum iio_event_info info, int val, int val2) +{ + struct ams *ams = iio_priv(indio_dev); + unsigned int offset; + + guard(mutex)(&ams->lock); + + /* Set temperature channel threshold to direct threshold */ + if (chan->type == IIO_TEMP) { + offset = ams_get_alarm_offset(chan->scan_index, IIO_EV_DIR_FALLING); + + if (chan->scan_index >= AMS_PS_SEQ_MAX) + ams_pl_update_reg(ams, offset, + AMS_ALARM_THR_DIRECT_MASK, + AMS_ALARM_THR_DIRECT_MASK); + else + ams_ps_update_reg(ams, offset, + AMS_ALARM_THR_DIRECT_MASK, + AMS_ALARM_THR_DIRECT_MASK); + } + + offset = ams_get_alarm_offset(chan->scan_index, dir); + if (chan->scan_index >= AMS_PS_SEQ_MAX) + writel(val, ams->pl_base + offset); + else + writel(val, ams->ps_base + offset); + + return 0; +} + +static void ams_handle_event(struct iio_dev *indio_dev, u32 event) +{ + const struct iio_chan_spec *chan; + + chan = ams_event_to_channel(indio_dev, event); + if (!chan) + return; + + if (chan->type == IIO_TEMP) { + /* + * The temperature channel only supports over-temperature + * events. + */ + iio_push_event(indio_dev, + IIO_UNMOD_EVENT_CODE(chan->type, chan->channel, + IIO_EV_TYPE_THRESH, + IIO_EV_DIR_RISING), + iio_get_time_ns(indio_dev)); + } else { + /* + * For other channels we don't know whether it is a upper or + * lower threshold event. Userspace will have to check the + * channel value if it wants to know. + */ + iio_push_event(indio_dev, + IIO_UNMOD_EVENT_CODE(chan->type, chan->channel, + IIO_EV_TYPE_THRESH, + IIO_EV_DIR_EITHER), + iio_get_time_ns(indio_dev)); + } +} + +static void ams_handle_events(struct iio_dev *indio_dev, unsigned long events) +{ + unsigned int bit; + + for_each_set_bit(bit, &events, AMS_NO_OF_ALARMS) + ams_handle_event(indio_dev, bit); +} + +/** + * ams_unmask_worker - ams alarm interrupt unmask worker + * @work: work to be done + * + * The ZynqMP threshold interrupts are level sensitive. Since we can't make the + * threshold condition go way from within the interrupt handler, this means as + * soon as a threshold condition is present we would enter the interrupt handler + * again and again. To work around this we mask all active threshold interrupts + * in the interrupt handler and start a timer. In this timer we poll the + * interrupt status and only if the interrupt is inactive we unmask it again. + */ +static void ams_unmask_worker(struct work_struct *work) +{ + struct ams *ams = container_of(work, struct ams, ams_unmask_work.work); + + spin_lock_irq(&ams->intr_lock); + ams_unmask(ams); + spin_unlock_irq(&ams->intr_lock); + + /* If still pending some alarm re-trigger the timer */ + if (ams->current_masked_alarm) + schedule_delayed_work(&ams->ams_unmask_work, + msecs_to_jiffies(AMS_UNMASK_TIMEOUT_MS)); +} + +static irqreturn_t ams_irq(int irq, void *data) +{ + struct iio_dev *indio_dev = data; + struct ams *ams = iio_priv(indio_dev); + u32 isr0; + + spin_lock(&ams->intr_lock); + + isr0 = readl(ams->base + AMS_ISR_0); + + /* Only process alarms that are not masked */ + isr0 &= ~((ams->intr_mask & AMS_ISR0_ALARM_MASK) | ams->current_masked_alarm); + if (!isr0) { + spin_unlock(&ams->intr_lock); + return IRQ_NONE; + } + + /* Clear interrupt */ + writel(isr0, ams->base + AMS_ISR_0); + + /* Mask the alarm interrupts until cleared */ + ams->current_masked_alarm |= isr0; + ams_update_intrmask(ams, ~AMS_ALARM_MASK, ~AMS_ALARM_MASK); + + ams_handle_events(indio_dev, isr0); + + schedule_delayed_work(&ams->ams_unmask_work, + msecs_to_jiffies(AMS_UNMASK_TIMEOUT_MS)); + + spin_unlock(&ams->intr_lock); + + return IRQ_HANDLED; +} + +static const struct iio_event_spec ams_temp_events[] = { + { + .type = IIO_EV_TYPE_THRESH, + .dir = IIO_EV_DIR_RISING, + .mask_separate = BIT(IIO_EV_INFO_ENABLE) | BIT(IIO_EV_INFO_VALUE), + }, +}; + +static const struct iio_event_spec ams_voltage_events[] = { + { + .type = IIO_EV_TYPE_THRESH, + .dir = IIO_EV_DIR_RISING, + .mask_separate = BIT(IIO_EV_INFO_VALUE), + }, + { + .type = IIO_EV_TYPE_THRESH, + .dir = IIO_EV_DIR_FALLING, + .mask_separate = BIT(IIO_EV_INFO_VALUE), + }, + { + .type = IIO_EV_TYPE_THRESH, + .dir = IIO_EV_DIR_EITHER, + .mask_separate = BIT(IIO_EV_INFO_ENABLE), + }, +}; + +static const struct iio_chan_spec ams_ps_channels[] = { + AMS_PS_CHAN_TEMP(AMS_SEQ_TEMP, AMS_TEMP, "Temp_LPD"), + AMS_PS_CHAN_TEMP(AMS_SEQ_TEMP_REMOTE, AMS_TEMP_REMOTE, "Temp_FPD"), + AMS_PS_CHAN_VOLTAGE(AMS_SEQ_SUPPLY1, AMS_SUPPLY1, "VCC_PSINTLP"), + AMS_PS_CHAN_VOLTAGE(AMS_SEQ_SUPPLY2, AMS_SUPPLY2, "VCC_PSINTFP"), + AMS_PS_CHAN_VOLTAGE(AMS_SEQ_SUPPLY3, AMS_SUPPLY3, "VCC_PSAUX"), + AMS_PS_CHAN_VOLTAGE(AMS_SEQ_SUPPLY4, AMS_SUPPLY4, "VCC_PSDDR"), + AMS_PS_CHAN_VOLTAGE(AMS_SEQ_SUPPLY5, AMS_SUPPLY5, "VCC_PSIO3"), + AMS_PS_CHAN_VOLTAGE(AMS_SEQ_SUPPLY6, AMS_SUPPLY6, "VCC_PSIO0"), + AMS_PS_CHAN_VOLTAGE(AMS_SEQ_SUPPLY7, AMS_SUPPLY7, "VCC_PSIO1"), + AMS_PS_CHAN_VOLTAGE(AMS_SEQ_SUPPLY8, AMS_SUPPLY8, "VCC_PSIO2"), + AMS_PS_CHAN_VOLTAGE(AMS_SEQ_SUPPLY9, AMS_SUPPLY9, "PS_MGTRAVCC"), + AMS_PS_CHAN_VOLTAGE(AMS_SEQ_SUPPLY10, AMS_SUPPLY10, "PS_MGTRAVTT"), + AMS_PS_CHAN_VOLTAGE(AMS_SEQ_VCCAMS, AMS_VCCAMS, "VCC_PSADC"), +}; + +static const struct iio_chan_spec ams_pl_channels[] = { + AMS_PL_CHAN_TEMP(AMS_SEQ_TEMP, AMS_TEMP, "Temp_PL"), + AMS_PL_CHAN_VOLTAGE(AMS_SEQ_SUPPLY1, AMS_SUPPLY1, true, "VCCINT"), + AMS_PL_CHAN_VOLTAGE(AMS_SEQ_SUPPLY2, AMS_SUPPLY2, true, "VCCAUX"), + AMS_PL_CHAN_VOLTAGE(AMS_SEQ_VREFP, AMS_VREFP, false, "VREFP"), + AMS_PL_CHAN_VOLTAGE(AMS_SEQ_VREFN, AMS_VREFN, false, "VREFN"), + AMS_PL_CHAN_VOLTAGE(AMS_SEQ_SUPPLY3, AMS_SUPPLY3, true, "VCCBRAM"), + AMS_PL_CHAN_VOLTAGE(AMS_SEQ_SUPPLY4, AMS_SUPPLY4, true, "VCC_PSINTLP"), + AMS_PL_CHAN_VOLTAGE(AMS_SEQ_SUPPLY5, AMS_SUPPLY5, true, "VCC_PSINTFP"), + AMS_PL_CHAN_VOLTAGE(AMS_SEQ_SUPPLY6, AMS_SUPPLY6, true, "VCC_PSAUX"), + AMS_PL_CHAN_VOLTAGE(AMS_SEQ_VCCAMS, AMS_VCCAMS, true, "VCCAMS"), + AMS_PL_CHAN_VOLTAGE(AMS_SEQ_VP_VN, AMS_VP_VN, false, "VP_VN"), + AMS_PL_CHAN_VOLTAGE(AMS_SEQ_SUPPLY7, AMS_SUPPLY7, true, "VUser0"), + AMS_PL_CHAN_VOLTAGE(AMS_SEQ_SUPPLY8, AMS_SUPPLY8, true, "VUser1"), + AMS_PL_CHAN_VOLTAGE(AMS_SEQ_SUPPLY9, AMS_SUPPLY9, true, "VUser2"), + AMS_PL_CHAN_VOLTAGE(AMS_SEQ_SUPPLY10, AMS_SUPPLY10, true, "VUser3"), + AMS_PL_AUX_CHAN_VOLTAGE(0), + AMS_PL_AUX_CHAN_VOLTAGE(1), + AMS_PL_AUX_CHAN_VOLTAGE(2), + AMS_PL_AUX_CHAN_VOLTAGE(3), + AMS_PL_AUX_CHAN_VOLTAGE(4), + AMS_PL_AUX_CHAN_VOLTAGE(5), + AMS_PL_AUX_CHAN_VOLTAGE(6), + AMS_PL_AUX_CHAN_VOLTAGE(7), + AMS_PL_AUX_CHAN_VOLTAGE(8), + AMS_PL_AUX_CHAN_VOLTAGE(9), + AMS_PL_AUX_CHAN_VOLTAGE(10), + AMS_PL_AUX_CHAN_VOLTAGE(11), + AMS_PL_AUX_CHAN_VOLTAGE(12), + AMS_PL_AUX_CHAN_VOLTAGE(13), + AMS_PL_AUX_CHAN_VOLTAGE(14), + AMS_PL_AUX_CHAN_VOLTAGE(15), +}; + +static const struct iio_chan_spec ams_ctrl_channels[] = { + AMS_CTRL_CHAN_VOLTAGE(AMS_SEQ_VCC_PSPLL, AMS_VCC_PSPLL0, "VCC_PSPLL"), + AMS_CTRL_CHAN_VOLTAGE(AMS_SEQ_VCC_PSBATT, AMS_VCC_PSPLL3, "VCC_PSBATT"), + AMS_CTRL_CHAN_VOLTAGE(AMS_SEQ_VCCINT, AMS_VCCINT, "VCCINT"), + AMS_CTRL_CHAN_VOLTAGE(AMS_SEQ_VCCBRAM, AMS_VCCBRAM, "VCCBRAM"), + AMS_CTRL_CHAN_VOLTAGE(AMS_SEQ_VCCAUX, AMS_VCCAUX, "VCCAUX"), + AMS_CTRL_CHAN_VOLTAGE(AMS_SEQ_PSDDRPLL, AMS_PSDDRPLL, "VCC_PSDDR_PLL"), + AMS_CTRL_CHAN_VOLTAGE(AMS_SEQ_INTDDR, AMS_PSINTFPDDR, "VCC_PSINTFP_DDR"), +}; + +static int ams_get_ext_chan(struct fwnode_handle *chan_node, + struct iio_chan_spec *channels, int num_channels) +{ + struct iio_chan_spec *chan; + struct fwnode_handle *child; + unsigned int reg, ext_chan; + int ret; + + fwnode_for_each_child_node(chan_node, child) { + ret = fwnode_property_read_u32(child, "reg", ®); + if (ret || reg > AMS_PL_MAX_EXT_CHANNEL + 30) + continue; + + chan = &channels[num_channels]; + ext_chan = reg + AMS_PL_MAX_FIXED_CHANNEL - 30; + memcpy(chan, &ams_pl_channels[ext_chan], sizeof(*channels)); + + if (fwnode_property_read_bool(child, "xlnx,bipolar")) + chan->scan_type.sign = 's'; + + num_channels++; + } + + return num_channels; +} + +static void ams_iounmap_ps(void *data) +{ + struct ams *ams = data; + + iounmap(ams->ps_base); +} + +static void ams_iounmap_pl(void *data) +{ + struct ams *ams = data; + + iounmap(ams->pl_base); +} + +static int ams_init_module(struct iio_dev *indio_dev, + struct fwnode_handle *fwnode, + struct iio_chan_spec *channels) +{ + struct device *dev = indio_dev->dev.parent; + struct ams *ams = iio_priv(indio_dev); + int num_channels = 0; + int ret; + + if (fwnode_device_is_compatible(fwnode, "xlnx,zynqmp-ams-ps")) { + ams->ps_base = fwnode_iomap(fwnode, 0); + if (!ams->ps_base) + return -ENXIO; + ret = devm_add_action_or_reset(dev, ams_iounmap_ps, ams); + if (ret < 0) + return ret; + + /* add PS channels to iio device channels */ + memcpy(channels, ams_ps_channels, sizeof(ams_ps_channels)); + num_channels = ARRAY_SIZE(ams_ps_channels); + } else if (fwnode_device_is_compatible(fwnode, "xlnx,zynqmp-ams-pl")) { + ams->pl_base = fwnode_iomap(fwnode, 0); + if (!ams->pl_base) + return -ENXIO; + + ret = devm_add_action_or_reset(dev, ams_iounmap_pl, ams); + if (ret < 0) + return ret; + + /* Copy only first 10 fix channels */ + memcpy(channels, ams_pl_channels, AMS_PL_MAX_FIXED_CHANNEL * sizeof(*channels)); + num_channels += AMS_PL_MAX_FIXED_CHANNEL; + num_channels = ams_get_ext_chan(fwnode, channels, + num_channels); + } else if (fwnode_device_is_compatible(fwnode, "xlnx,zynqmp-ams")) { + /* add AMS channels to iio device channels */ + memcpy(channels, ams_ctrl_channels, sizeof(ams_ctrl_channels)); + num_channels += ARRAY_SIZE(ams_ctrl_channels); + } else { + return -EINVAL; + } + + return num_channels; +} + +static int ams_parse_firmware(struct iio_dev *indio_dev) +{ + struct ams *ams = iio_priv(indio_dev); + struct iio_chan_spec *ams_channels, *dev_channels; + struct device *dev = indio_dev->dev.parent; + struct fwnode_handle *fwnode = dev_fwnode(dev); + size_t ams_size; + int ret, ch_cnt = 0, i, rising_off, falling_off; + unsigned int num_channels = 0; + + ams_size = ARRAY_SIZE(ams_ps_channels) + ARRAY_SIZE(ams_pl_channels) + + ARRAY_SIZE(ams_ctrl_channels); + + /* Initialize buffer for channel specification */ + ams_channels = devm_kcalloc(dev, ams_size, sizeof(*ams_channels), GFP_KERNEL); + if (!ams_channels) + return -ENOMEM; + + if (fwnode_device_is_available(fwnode)) { + ret = ams_init_module(indio_dev, fwnode, ams_channels); + if (ret < 0) + return ret; + + num_channels += ret; + } + + device_for_each_child_node_scoped(dev, child) { + ret = ams_init_module(indio_dev, child, ams_channels + num_channels); + if (ret < 0) + return ret; + + num_channels += ret; + } + + for (i = 0; i < num_channels; i++) { + ams_channels[i].channel = ch_cnt++; + + if (ams_channels[i].scan_index < AMS_CTRL_SEQ_BASE) { + /* set threshold to max and min for each channel */ + falling_off = + ams_get_alarm_offset(ams_channels[i].scan_index, + IIO_EV_DIR_FALLING); + rising_off = + ams_get_alarm_offset(ams_channels[i].scan_index, + IIO_EV_DIR_RISING); + if (ams_channels[i].scan_index >= AMS_PS_SEQ_MAX) { + writel(AMS_ALARM_THR_MIN, + ams->pl_base + falling_off); + writel(AMS_ALARM_THR_MAX, + ams->pl_base + rising_off); + } else { + writel(AMS_ALARM_THR_MIN, + ams->ps_base + falling_off); + writel(AMS_ALARM_THR_MAX, + ams->ps_base + rising_off); + } + } + } + + dev_channels = devm_krealloc_array(dev, ams_channels, num_channels, + sizeof(*dev_channels), GFP_KERNEL); + if (!dev_channels) + return -ENOMEM; + + indio_dev->channels = dev_channels; + indio_dev->num_channels = num_channels; + + return 0; +} + +static const struct iio_info iio_ams_info = { + .read_label = ams_read_label, + .read_raw = &ams_read_raw, + .read_event_config = &ams_read_event_config, + .write_event_config = &ams_write_event_config, + .read_event_value = &ams_read_event_value, + .write_event_value = &ams_write_event_value, +}; + +static const struct of_device_id ams_of_match_table[] = { + { .compatible = "xlnx,zynqmp-ams" }, + { } +}; +MODULE_DEVICE_TABLE(of, ams_of_match_table); + +static int ams_probe(struct platform_device *pdev) +{ + struct iio_dev *indio_dev; + struct ams *ams; + int ret; + int irq; + + indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*ams)); + if (!indio_dev) + return -ENOMEM; + + ams = iio_priv(indio_dev); + mutex_init(&ams->lock); + spin_lock_init(&ams->intr_lock); + + indio_dev->name = "xilinx-ams"; + + indio_dev->info = &iio_ams_info; + indio_dev->modes = INDIO_DIRECT_MODE; + + ams->base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(ams->base)) + return PTR_ERR(ams->base); + + ams->clk = devm_clk_get_enabled(&pdev->dev, NULL); + if (IS_ERR(ams->clk)) + return PTR_ERR(ams->clk); + + ret = devm_delayed_work_autocancel(&pdev->dev, &ams->ams_unmask_work, + ams_unmask_worker); + if (ret < 0) + return ret; + + ret = ams_parse_firmware(indio_dev); + if (ret) + return dev_err_probe(&pdev->dev, ret, "failure in parsing DT\n"); + + ret = ams_init_device(ams); + if (ret) + return dev_err_probe(&pdev->dev, ret, "failed to initialize AMS\n"); + + ams_enable_channel_sequence(indio_dev); + + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return irq; + + ret = devm_request_irq(&pdev->dev, irq, &ams_irq, 0, "ams-irq", + indio_dev); + if (ret < 0) + return dev_err_probe(&pdev->dev, ret, "failed to register interrupt\n"); + + platform_set_drvdata(pdev, indio_dev); + + return devm_iio_device_register(&pdev->dev, indio_dev); +} + +static int ams_suspend(struct device *dev) +{ + struct ams *ams = iio_priv(dev_get_drvdata(dev)); + + clk_disable_unprepare(ams->clk); + + return 0; +} + +static int ams_resume(struct device *dev) +{ + struct ams *ams = iio_priv(dev_get_drvdata(dev)); + + return clk_prepare_enable(ams->clk); +} + +static DEFINE_SIMPLE_DEV_PM_OPS(ams_pm_ops, ams_suspend, ams_resume); + +static struct platform_driver ams_driver = { + .probe = ams_probe, + .driver = { + .name = "xilinx-ams", + .pm = pm_sleep_ptr(&ams_pm_ops), + .of_match_table = ams_of_match_table, + }, +}; +module_platform_driver(ams_driver); + +MODULE_DESCRIPTION("Xilinx AMS driver"); +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Xilinx, Inc."); diff --git a/drivers/iio/adc/xilinx-xadc-core.c b/drivers/iio/adc/xilinx-xadc-core.c new file mode 100644 index 000000000..cab66bb8c --- /dev/null +++ b/drivers/iio/adc/xilinx-xadc-core.c @@ -0,0 +1,1459 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Xilinx XADC driver + * + * Copyright 2013-2014 Analog Devices Inc. + * Author: Lars-Peter Clausen + * + * Documentation for the parts can be found at: + * - XADC hardmacro: Xilinx UG480 + * - ZYNQ XADC interface: Xilinx UG585 + * - AXI XADC interface: Xilinx PG019 + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "xilinx-xadc.h" + +static const unsigned int XADC_ZYNQ_UNMASK_TIMEOUT = 500; + +/* ZYNQ register definitions */ +#define XADC_ZYNQ_REG_CFG 0x00 +#define XADC_ZYNQ_REG_INTSTS 0x04 +#define XADC_ZYNQ_REG_INTMSK 0x08 +#define XADC_ZYNQ_REG_STATUS 0x0c +#define XADC_ZYNQ_REG_CFIFO 0x10 +#define XADC_ZYNQ_REG_DFIFO 0x14 +#define XADC_ZYNQ_REG_CTL 0x18 + +#define XADC_ZYNQ_CFG_ENABLE BIT(31) +#define XADC_ZYNQ_CFG_CFIFOTH_MASK (0xf << 20) +#define XADC_ZYNQ_CFG_CFIFOTH_OFFSET 20 +#define XADC_ZYNQ_CFG_DFIFOTH_MASK (0xf << 16) +#define XADC_ZYNQ_CFG_DFIFOTH_OFFSET 16 +#define XADC_ZYNQ_CFG_WEDGE BIT(13) +#define XADC_ZYNQ_CFG_REDGE BIT(12) +#define XADC_ZYNQ_CFG_TCKRATE_MASK (0x3 << 8) +#define XADC_ZYNQ_CFG_TCKRATE_DIV2 (0x0 << 8) +#define XADC_ZYNQ_CFG_TCKRATE_DIV4 (0x1 << 8) +#define XADC_ZYNQ_CFG_TCKRATE_DIV8 (0x2 << 8) +#define XADC_ZYNQ_CFG_TCKRATE_DIV16 (0x3 << 8) +#define XADC_ZYNQ_CFG_IGAP_MASK 0x1f +#define XADC_ZYNQ_CFG_IGAP(x) (x) + +#define XADC_ZYNQ_INT_CFIFO_LTH BIT(9) +#define XADC_ZYNQ_INT_DFIFO_GTH BIT(8) +#define XADC_ZYNQ_INT_ALARM_MASK 0xff +#define XADC_ZYNQ_INT_ALARM_OFFSET 0 + +#define XADC_ZYNQ_STATUS_CFIFO_LVL_MASK (0xf << 16) +#define XADC_ZYNQ_STATUS_CFIFO_LVL_OFFSET 16 +#define XADC_ZYNQ_STATUS_DFIFO_LVL_MASK (0xf << 12) +#define XADC_ZYNQ_STATUS_DFIFO_LVL_OFFSET 12 +#define XADC_ZYNQ_STATUS_CFIFOF BIT(11) +#define XADC_ZYNQ_STATUS_CFIFOE BIT(10) +#define XADC_ZYNQ_STATUS_DFIFOF BIT(9) +#define XADC_ZYNQ_STATUS_DFIFOE BIT(8) +#define XADC_ZYNQ_STATUS_OT BIT(7) +#define XADC_ZYNQ_STATUS_ALM(x) BIT(x) + +#define XADC_ZYNQ_CTL_RESET BIT(4) + +#define XADC_ZYNQ_CMD_NOP 0x00 +#define XADC_ZYNQ_CMD_READ 0x01 +#define XADC_ZYNQ_CMD_WRITE 0x02 + +#define XADC_ZYNQ_CMD(cmd, addr, data) (((cmd) << 26) | ((addr) << 16) | (data)) + +/* AXI register definitions */ +#define XADC_AXI_REG_RESET 0x00 +#define XADC_AXI_REG_STATUS 0x04 +#define XADC_AXI_REG_ALARM_STATUS 0x08 +#define XADC_AXI_REG_CONVST 0x0c +#define XADC_AXI_REG_XADC_RESET 0x10 +#define XADC_AXI_REG_GIER 0x5c +#define XADC_AXI_REG_IPISR 0x60 +#define XADC_AXI_REG_IPIER 0x68 + +/* 7 Series */ +#define XADC_7S_AXI_ADC_REG_OFFSET 0x200 + +/* UltraScale */ +#define XADC_US_AXI_ADC_REG_OFFSET 0x400 + +#define XADC_AXI_RESET_MAGIC 0xa +#define XADC_AXI_GIER_ENABLE BIT(31) + +#define XADC_AXI_INT_EOS BIT(4) +#define XADC_AXI_INT_ALARM_MASK 0x3c0f + +#define XADC_FLAGS_BUFFERED BIT(0) +#define XADC_FLAGS_IRQ_OPTIONAL BIT(1) + +/* + * The XADC hardware supports a samplerate of up to 1MSPS. Unfortunately it does + * not have a hardware FIFO. Which means an interrupt is generated for each + * conversion sequence. At 1MSPS sample rate the CPU in ZYNQ7000 is completely + * overloaded by the interrupts that it soft-lockups. For this reason the driver + * limits the maximum samplerate 150kSPS. At this rate the CPU is fairly busy, + * but still responsive. + */ +#define XADC_MAX_SAMPLERATE 150000 + +static void xadc_write_reg(struct xadc *xadc, unsigned int reg, + uint32_t val) +{ + writel(val, xadc->base + reg); +} + +static void xadc_read_reg(struct xadc *xadc, unsigned int reg, + uint32_t *val) +{ + *val = readl(xadc->base + reg); +} + +/* + * The ZYNQ interface uses two asynchronous FIFOs for communication with the + * XADC. Reads and writes to the XADC register are performed by submitting a + * request to the command FIFO (CFIFO), once the request has been completed the + * result can be read from the data FIFO (DFIFO). The method currently used in + * this driver is to submit the request for a read/write operation, then go to + * sleep and wait for an interrupt that signals that a response is available in + * the data FIFO. + */ + +static void xadc_zynq_write_fifo(struct xadc *xadc, uint32_t *cmd, + unsigned int n) +{ + unsigned int i; + + for (i = 0; i < n; i++) + xadc_write_reg(xadc, XADC_ZYNQ_REG_CFIFO, cmd[i]); +} + +static void xadc_zynq_drain_fifo(struct xadc *xadc) +{ + uint32_t status, tmp; + + xadc_read_reg(xadc, XADC_ZYNQ_REG_STATUS, &status); + + while (!(status & XADC_ZYNQ_STATUS_DFIFOE)) { + xadc_read_reg(xadc, XADC_ZYNQ_REG_DFIFO, &tmp); + xadc_read_reg(xadc, XADC_ZYNQ_REG_STATUS, &status); + } +} + +static void xadc_zynq_update_intmsk(struct xadc *xadc, unsigned int mask, + unsigned int val) +{ + xadc->zynq_intmask &= ~mask; + xadc->zynq_intmask |= val; + + xadc_write_reg(xadc, XADC_ZYNQ_REG_INTMSK, + xadc->zynq_intmask | xadc->zynq_masked_alarm); +} + +static int xadc_zynq_write_adc_reg(struct xadc *xadc, unsigned int reg, + uint16_t val) +{ + uint32_t cmd[1]; + uint32_t tmp; + int ret; + + spin_lock_irq(&xadc->lock); + xadc_zynq_update_intmsk(xadc, XADC_ZYNQ_INT_DFIFO_GTH, + XADC_ZYNQ_INT_DFIFO_GTH); + + reinit_completion(&xadc->completion); + + cmd[0] = XADC_ZYNQ_CMD(XADC_ZYNQ_CMD_WRITE, reg, val); + xadc_zynq_write_fifo(xadc, cmd, ARRAY_SIZE(cmd)); + xadc_read_reg(xadc, XADC_ZYNQ_REG_CFG, &tmp); + tmp &= ~XADC_ZYNQ_CFG_DFIFOTH_MASK; + tmp |= 0 << XADC_ZYNQ_CFG_DFIFOTH_OFFSET; + xadc_write_reg(xadc, XADC_ZYNQ_REG_CFG, tmp); + + xadc_zynq_update_intmsk(xadc, XADC_ZYNQ_INT_DFIFO_GTH, 0); + spin_unlock_irq(&xadc->lock); + + ret = wait_for_completion_interruptible_timeout(&xadc->completion, HZ); + if (ret == 0) + ret = -EIO; + else + ret = 0; + + xadc_read_reg(xadc, XADC_ZYNQ_REG_DFIFO, &tmp); + + return ret; +} + +static int xadc_zynq_read_adc_reg(struct xadc *xadc, unsigned int reg, + uint16_t *val) +{ + uint32_t cmd[2]; + uint32_t resp, tmp; + int ret; + + cmd[0] = XADC_ZYNQ_CMD(XADC_ZYNQ_CMD_READ, reg, 0); + cmd[1] = XADC_ZYNQ_CMD(XADC_ZYNQ_CMD_NOP, 0, 0); + + spin_lock_irq(&xadc->lock); + xadc_zynq_update_intmsk(xadc, XADC_ZYNQ_INT_DFIFO_GTH, + XADC_ZYNQ_INT_DFIFO_GTH); + xadc_zynq_drain_fifo(xadc); + reinit_completion(&xadc->completion); + + xadc_zynq_write_fifo(xadc, cmd, ARRAY_SIZE(cmd)); + xadc_read_reg(xadc, XADC_ZYNQ_REG_CFG, &tmp); + tmp &= ~XADC_ZYNQ_CFG_DFIFOTH_MASK; + tmp |= 1 << XADC_ZYNQ_CFG_DFIFOTH_OFFSET; + xadc_write_reg(xadc, XADC_ZYNQ_REG_CFG, tmp); + + xadc_zynq_update_intmsk(xadc, XADC_ZYNQ_INT_DFIFO_GTH, 0); + spin_unlock_irq(&xadc->lock); + ret = wait_for_completion_interruptible_timeout(&xadc->completion, HZ); + if (ret == 0) + ret = -EIO; + if (ret < 0) + return ret; + + xadc_read_reg(xadc, XADC_ZYNQ_REG_DFIFO, &resp); + xadc_read_reg(xadc, XADC_ZYNQ_REG_DFIFO, &resp); + + *val = resp & 0xffff; + + return 0; +} + +static unsigned int xadc_zynq_transform_alarm(unsigned int alarm) +{ + return ((alarm & 0x80) >> 4) | + ((alarm & 0x78) << 1) | + (alarm & 0x07); +} + +/* + * The ZYNQ threshold interrupts are level sensitive. Since we can't make the + * threshold condition go way from within the interrupt handler, this means as + * soon as a threshold condition is present we would enter the interrupt handler + * again and again. To work around this we mask all active thresholds interrupts + * in the interrupt handler and start a timer. In this timer we poll the + * interrupt status and only if the interrupt is inactive we unmask it again. + */ +static void xadc_zynq_unmask_worker(struct work_struct *work) +{ + struct xadc *xadc = container_of(work, struct xadc, zynq_unmask_work.work); + unsigned int misc_sts, unmask; + + xadc_read_reg(xadc, XADC_ZYNQ_REG_STATUS, &misc_sts); + + misc_sts &= XADC_ZYNQ_INT_ALARM_MASK; + + spin_lock_irq(&xadc->lock); + + /* Clear those bits which are not active anymore */ + unmask = (xadc->zynq_masked_alarm ^ misc_sts) & xadc->zynq_masked_alarm; + xadc->zynq_masked_alarm &= misc_sts; + + /* Also clear those which are masked out anyway */ + xadc->zynq_masked_alarm &= ~xadc->zynq_intmask; + + /* Clear the interrupts before we unmask them */ + xadc_write_reg(xadc, XADC_ZYNQ_REG_INTSTS, unmask); + + xadc_zynq_update_intmsk(xadc, 0, 0); + + spin_unlock_irq(&xadc->lock); + + /* if still pending some alarm re-trigger the timer */ + if (xadc->zynq_masked_alarm) { + schedule_delayed_work(&xadc->zynq_unmask_work, + msecs_to_jiffies(XADC_ZYNQ_UNMASK_TIMEOUT)); + } + +} + +static irqreturn_t xadc_zynq_interrupt_handler(int irq, void *devid) +{ + struct iio_dev *indio_dev = devid; + struct xadc *xadc = iio_priv(indio_dev); + uint32_t status; + + xadc_read_reg(xadc, XADC_ZYNQ_REG_INTSTS, &status); + + status &= ~(xadc->zynq_intmask | xadc->zynq_masked_alarm); + + if (!status) + return IRQ_NONE; + + spin_lock(&xadc->lock); + + xadc_write_reg(xadc, XADC_ZYNQ_REG_INTSTS, status); + + if (status & XADC_ZYNQ_INT_DFIFO_GTH) { + xadc_zynq_update_intmsk(xadc, XADC_ZYNQ_INT_DFIFO_GTH, + XADC_ZYNQ_INT_DFIFO_GTH); + complete(&xadc->completion); + } + + status &= XADC_ZYNQ_INT_ALARM_MASK; + if (status) { + xadc->zynq_masked_alarm |= status; + /* + * mask the current event interrupt, + * unmask it when the interrupt is no more active. + */ + xadc_zynq_update_intmsk(xadc, 0, 0); + + xadc_handle_events(indio_dev, + xadc_zynq_transform_alarm(status)); + + /* unmask the required interrupts in timer. */ + schedule_delayed_work(&xadc->zynq_unmask_work, + msecs_to_jiffies(XADC_ZYNQ_UNMASK_TIMEOUT)); + } + spin_unlock(&xadc->lock); + + return IRQ_HANDLED; +} + +#define XADC_ZYNQ_TCK_RATE_MAX 50000000 +#define XADC_ZYNQ_IGAP_DEFAULT 20 +#define XADC_ZYNQ_PCAP_RATE_MAX 200000000 + +static int xadc_zynq_setup(struct platform_device *pdev, + struct iio_dev *indio_dev, int irq) +{ + struct xadc *xadc = iio_priv(indio_dev); + unsigned long pcap_rate; + unsigned int tck_div; + unsigned int div; + unsigned int igap; + unsigned int tck_rate; + int ret; + + /* TODO: Figure out how to make igap and tck_rate configurable */ + igap = XADC_ZYNQ_IGAP_DEFAULT; + tck_rate = XADC_ZYNQ_TCK_RATE_MAX; + + xadc->zynq_intmask = ~0; + + pcap_rate = clk_get_rate(xadc->clk); + if (!pcap_rate) + return -EINVAL; + + if (pcap_rate > XADC_ZYNQ_PCAP_RATE_MAX) { + ret = clk_set_rate(xadc->clk, + (unsigned long)XADC_ZYNQ_PCAP_RATE_MAX); + if (ret) + return ret; + } + + if (tck_rate > pcap_rate / 2) { + div = 2; + } else { + div = pcap_rate / tck_rate; + if (pcap_rate / div > XADC_ZYNQ_TCK_RATE_MAX) + div++; + } + + if (div <= 3) + tck_div = XADC_ZYNQ_CFG_TCKRATE_DIV2; + else if (div <= 7) + tck_div = XADC_ZYNQ_CFG_TCKRATE_DIV4; + else if (div <= 15) + tck_div = XADC_ZYNQ_CFG_TCKRATE_DIV8; + else + tck_div = XADC_ZYNQ_CFG_TCKRATE_DIV16; + + xadc_write_reg(xadc, XADC_ZYNQ_REG_CTL, XADC_ZYNQ_CTL_RESET); + xadc_write_reg(xadc, XADC_ZYNQ_REG_CTL, 0); + xadc_write_reg(xadc, XADC_ZYNQ_REG_INTSTS, ~0); + xadc_write_reg(xadc, XADC_ZYNQ_REG_INTMSK, xadc->zynq_intmask); + xadc_write_reg(xadc, XADC_ZYNQ_REG_CFG, XADC_ZYNQ_CFG_ENABLE | + XADC_ZYNQ_CFG_REDGE | XADC_ZYNQ_CFG_WEDGE | + tck_div | XADC_ZYNQ_CFG_IGAP(igap)); + + if (pcap_rate > XADC_ZYNQ_PCAP_RATE_MAX) { + ret = clk_set_rate(xadc->clk, pcap_rate); + if (ret) + return ret; + } + + return 0; +} + +static unsigned long xadc_zynq_get_dclk_rate(struct xadc *xadc) +{ + unsigned int div; + uint32_t val; + + xadc_read_reg(xadc, XADC_ZYNQ_REG_CFG, &val); + + switch (val & XADC_ZYNQ_CFG_TCKRATE_MASK) { + case XADC_ZYNQ_CFG_TCKRATE_DIV4: + div = 4; + break; + case XADC_ZYNQ_CFG_TCKRATE_DIV8: + div = 8; + break; + case XADC_ZYNQ_CFG_TCKRATE_DIV16: + div = 16; + break; + default: + div = 2; + break; + } + + return clk_get_rate(xadc->clk) / div; +} + +static void xadc_zynq_update_alarm(struct xadc *xadc, unsigned int alarm) +{ + unsigned long flags; + uint32_t status; + + /* Move OT to bit 7 */ + alarm = ((alarm & 0x08) << 4) | ((alarm & 0xf0) >> 1) | (alarm & 0x07); + + spin_lock_irqsave(&xadc->lock, flags); + + /* Clear previous interrupts if any. */ + xadc_read_reg(xadc, XADC_ZYNQ_REG_INTSTS, &status); + xadc_write_reg(xadc, XADC_ZYNQ_REG_INTSTS, status & alarm); + + xadc_zynq_update_intmsk(xadc, XADC_ZYNQ_INT_ALARM_MASK, + ~alarm & XADC_ZYNQ_INT_ALARM_MASK); + + spin_unlock_irqrestore(&xadc->lock, flags); +} + +static const struct xadc_ops xadc_zynq_ops = { + .read = xadc_zynq_read_adc_reg, + .write = xadc_zynq_write_adc_reg, + .setup = xadc_zynq_setup, + .get_dclk_rate = xadc_zynq_get_dclk_rate, + .interrupt_handler = xadc_zynq_interrupt_handler, + .update_alarm = xadc_zynq_update_alarm, + .type = XADC_TYPE_S7, + /* Temp in C = (val * 503.975) / 2**bits - 273.15 */ + .temp_scale = 503975, + .temp_offset = 273150, +}; + +static const unsigned int xadc_axi_reg_offsets[] = { + [XADC_TYPE_S7] = XADC_7S_AXI_ADC_REG_OFFSET, + [XADC_TYPE_US] = XADC_US_AXI_ADC_REG_OFFSET, +}; + +static int xadc_axi_read_adc_reg(struct xadc *xadc, unsigned int reg, + uint16_t *val) +{ + uint32_t val32; + + xadc_read_reg(xadc, xadc_axi_reg_offsets[xadc->ops->type] + reg * 4, + &val32); + *val = val32 & 0xffff; + + return 0; +} + +static int xadc_axi_write_adc_reg(struct xadc *xadc, unsigned int reg, + uint16_t val) +{ + xadc_write_reg(xadc, xadc_axi_reg_offsets[xadc->ops->type] + reg * 4, + val); + + return 0; +} + +static int xadc_axi_setup(struct platform_device *pdev, + struct iio_dev *indio_dev, int irq) +{ + struct xadc *xadc = iio_priv(indio_dev); + + xadc_write_reg(xadc, XADC_AXI_REG_RESET, XADC_AXI_RESET_MAGIC); + xadc_write_reg(xadc, XADC_AXI_REG_GIER, XADC_AXI_GIER_ENABLE); + + return 0; +} + +static irqreturn_t xadc_axi_interrupt_handler(int irq, void *devid) +{ + struct iio_dev *indio_dev = devid; + struct xadc *xadc = iio_priv(indio_dev); + uint32_t status, mask; + unsigned int events; + + xadc_read_reg(xadc, XADC_AXI_REG_IPISR, &status); + xadc_read_reg(xadc, XADC_AXI_REG_IPIER, &mask); + status &= mask; + + if (!status) + return IRQ_NONE; + + if ((status & XADC_AXI_INT_EOS) && xadc->trigger) + iio_trigger_poll(xadc->trigger); + + if (status & XADC_AXI_INT_ALARM_MASK) { + /* + * The order of the bits in the AXI-XADC status register does + * not match the order of the bits in the XADC alarm enable + * register. xadc_handle_events() expects the events to be in + * the same order as the XADC alarm enable register. + */ + events = (status & 0x000e) >> 1; + events |= (status & 0x0001) << 3; + events |= (status & 0x3c00) >> 6; + xadc_handle_events(indio_dev, events); + } + + xadc_write_reg(xadc, XADC_AXI_REG_IPISR, status); + + return IRQ_HANDLED; +} + +static void xadc_axi_update_alarm(struct xadc *xadc, unsigned int alarm) +{ + uint32_t val; + unsigned long flags; + + /* + * The order of the bits in the AXI-XADC status register does not match + * the order of the bits in the XADC alarm enable register. We get + * passed the alarm mask in the same order as in the XADC alarm enable + * register. + */ + alarm = ((alarm & 0x07) << 1) | ((alarm & 0x08) >> 3) | + ((alarm & 0xf0) << 6); + + spin_lock_irqsave(&xadc->lock, flags); + xadc_read_reg(xadc, XADC_AXI_REG_IPIER, &val); + val &= ~XADC_AXI_INT_ALARM_MASK; + val |= alarm; + xadc_write_reg(xadc, XADC_AXI_REG_IPIER, val); + spin_unlock_irqrestore(&xadc->lock, flags); +} + +static unsigned long xadc_axi_get_dclk(struct xadc *xadc) +{ + return clk_get_rate(xadc->clk); +} + +static const struct xadc_ops xadc_7s_axi_ops = { + .read = xadc_axi_read_adc_reg, + .write = xadc_axi_write_adc_reg, + .setup = xadc_axi_setup, + .get_dclk_rate = xadc_axi_get_dclk, + .update_alarm = xadc_axi_update_alarm, + .interrupt_handler = xadc_axi_interrupt_handler, + .flags = XADC_FLAGS_BUFFERED | XADC_FLAGS_IRQ_OPTIONAL, + .type = XADC_TYPE_S7, + /* Temp in C = (val * 503.975) / 2**bits - 273.15 */ + .temp_scale = 503975, + .temp_offset = 273150, +}; + +static const struct xadc_ops xadc_us_axi_ops = { + .read = xadc_axi_read_adc_reg, + .write = xadc_axi_write_adc_reg, + .setup = xadc_axi_setup, + .get_dclk_rate = xadc_axi_get_dclk, + .update_alarm = xadc_axi_update_alarm, + .interrupt_handler = xadc_axi_interrupt_handler, + .flags = XADC_FLAGS_BUFFERED | XADC_FLAGS_IRQ_OPTIONAL, + .type = XADC_TYPE_US, + /** + * Values below are for UltraScale+ (SYSMONE4) using internal reference. + * See https://docs.xilinx.com/v/u/en-US/ug580-ultrascale-sysmon + */ + .temp_scale = 509314, + .temp_offset = 280231, +}; + +static int _xadc_update_adc_reg(struct xadc *xadc, unsigned int reg, + uint16_t mask, uint16_t val) +{ + uint16_t tmp; + int ret; + + ret = _xadc_read_adc_reg(xadc, reg, &tmp); + if (ret) + return ret; + + return _xadc_write_adc_reg(xadc, reg, (tmp & ~mask) | val); +} + +static int xadc_update_adc_reg(struct xadc *xadc, unsigned int reg, + uint16_t mask, uint16_t val) +{ + int ret; + + mutex_lock(&xadc->mutex); + ret = _xadc_update_adc_reg(xadc, reg, mask, val); + mutex_unlock(&xadc->mutex); + + return ret; +} + +static unsigned long xadc_get_dclk_rate(struct xadc *xadc) +{ + return xadc->ops->get_dclk_rate(xadc); +} + +static int xadc_update_scan_mode(struct iio_dev *indio_dev, + const unsigned long *mask) +{ + struct xadc *xadc = iio_priv(indio_dev); + size_t n; + void *data; + + n = bitmap_weight(mask, iio_get_masklength(indio_dev)); + + data = devm_krealloc_array(indio_dev->dev.parent, xadc->data, + n, sizeof(*xadc->data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + memset(data, 0, n * sizeof(*xadc->data)); + xadc->data = data; + + return 0; +} + +static unsigned int xadc_scan_index_to_channel(unsigned int scan_index) +{ + switch (scan_index) { + case 5: + return XADC_REG_VCCPINT; + case 6: + return XADC_REG_VCCPAUX; + case 7: + return XADC_REG_VCCO_DDR; + case 8: + return XADC_REG_TEMP; + case 9: + return XADC_REG_VCCINT; + case 10: + return XADC_REG_VCCAUX; + case 11: + return XADC_REG_VPVN; + case 12: + return XADC_REG_VREFP; + case 13: + return XADC_REG_VREFN; + case 14: + return XADC_REG_VCCBRAM; + default: + return XADC_REG_VAUX(scan_index - 16); + } +} + +static irqreturn_t xadc_trigger_handler(int irq, void *p) +{ + struct iio_poll_func *pf = p; + struct iio_dev *indio_dev = pf->indio_dev; + struct xadc *xadc = iio_priv(indio_dev); + unsigned int chan; + int i, j; + + if (!xadc->data) + goto out; + + j = 0; + iio_for_each_active_channel(indio_dev, i) { + chan = xadc_scan_index_to_channel(i); + xadc_read_adc_reg(xadc, chan, &xadc->data[j]); + j++; + } + + iio_push_to_buffers(indio_dev, xadc->data); + +out: + iio_trigger_notify_done(indio_dev->trig); + + return IRQ_HANDLED; +} + +static int xadc_trigger_set_state(struct iio_trigger *trigger, bool state) +{ + struct xadc *xadc = iio_trigger_get_drvdata(trigger); + unsigned long flags; + unsigned int convst; + unsigned int val; + int ret = 0; + + mutex_lock(&xadc->mutex); + + if (state) { + /* Only one of the two triggers can be active at a time. */ + if (xadc->trigger != NULL) { + ret = -EBUSY; + goto err_out; + } else { + xadc->trigger = trigger; + if (trigger == xadc->convst_trigger) + convst = XADC_CONF0_EC; + else + convst = 0; + } + ret = _xadc_update_adc_reg(xadc, XADC_REG_CONF1, XADC_CONF0_EC, + convst); + if (ret) + goto err_out; + } else { + xadc->trigger = NULL; + } + + spin_lock_irqsave(&xadc->lock, flags); + xadc_read_reg(xadc, XADC_AXI_REG_IPIER, &val); + xadc_write_reg(xadc, XADC_AXI_REG_IPISR, XADC_AXI_INT_EOS); + if (state) + val |= XADC_AXI_INT_EOS; + else + val &= ~XADC_AXI_INT_EOS; + xadc_write_reg(xadc, XADC_AXI_REG_IPIER, val); + spin_unlock_irqrestore(&xadc->lock, flags); + +err_out: + mutex_unlock(&xadc->mutex); + + return ret; +} + +static const struct iio_trigger_ops xadc_trigger_ops = { + .set_trigger_state = &xadc_trigger_set_state, +}; + +static struct iio_trigger *xadc_alloc_trigger(struct iio_dev *indio_dev, + const char *name) +{ + struct device *dev = indio_dev->dev.parent; + struct iio_trigger *trig; + int ret; + + trig = devm_iio_trigger_alloc(dev, "%s%d-%s", indio_dev->name, + iio_device_id(indio_dev), name); + if (trig == NULL) + return ERR_PTR(-ENOMEM); + + trig->ops = &xadc_trigger_ops; + iio_trigger_set_drvdata(trig, iio_priv(indio_dev)); + + ret = devm_iio_trigger_register(dev, trig); + if (ret) + return ERR_PTR(ret); + + return trig; +} + +static int xadc_power_adc_b(struct xadc *xadc, unsigned int seq_mode) +{ + uint16_t val; + + /* + * As per datasheet the power-down bits are don't care in the + * UltraScale, but as per reality setting the power-down bit for the + * non-existing ADC-B powers down the main ADC, so just return and don't + * do anything. + */ + if (xadc->ops->type == XADC_TYPE_US) + return 0; + + /* Powerdown the ADC-B when it is not needed. */ + switch (seq_mode) { + case XADC_CONF1_SEQ_SIMULTANEOUS: + case XADC_CONF1_SEQ_INDEPENDENT: + val = 0; + break; + default: + val = XADC_CONF2_PD_ADC_B; + break; + } + + return xadc_update_adc_reg(xadc, XADC_REG_CONF2, XADC_CONF2_PD_MASK, + val); +} + +static int xadc_get_seq_mode(struct xadc *xadc, unsigned long scan_mode) +{ + unsigned int aux_scan_mode = scan_mode >> 16; + + /* UltraScale has only one ADC and supports only continuous mode */ + if (xadc->ops->type == XADC_TYPE_US) + return XADC_CONF1_SEQ_CONTINUOUS; + + if (xadc->external_mux_mode == XADC_EXTERNAL_MUX_DUAL) + return XADC_CONF1_SEQ_SIMULTANEOUS; + + if ((aux_scan_mode & 0xff00) == 0 || + (aux_scan_mode & 0x00ff) == 0) + return XADC_CONF1_SEQ_CONTINUOUS; + + return XADC_CONF1_SEQ_SIMULTANEOUS; +} + +static int xadc_postdisable(struct iio_dev *indio_dev) +{ + struct xadc *xadc = iio_priv(indio_dev); + unsigned long scan_mask; + int seq_mode; + int ret; + int i; + + scan_mask = 1; /* Run calibration as part of the sequence */ + for (i = 0; i < indio_dev->num_channels; i++) + scan_mask |= BIT(indio_dev->channels[i].scan_index); + + /* + * Use the correct sequencer mode for the idle state: simultaneous + * mode for dual external mux configurations, continuous otherwise. + */ + seq_mode = xadc_get_seq_mode(xadc, scan_mask); + + /* Enable all channels and calibration */ + ret = xadc_write_adc_reg(xadc, XADC_REG_SEQ(0), scan_mask & 0xffff); + if (ret) + return ret; + + ret = xadc_write_adc_reg(xadc, XADC_REG_SEQ(1), scan_mask >> 16); + if (ret) + return ret; + + ret = xadc_update_adc_reg(xadc, XADC_REG_CONF1, XADC_CONF1_SEQ_MASK, + seq_mode); + if (ret) + return ret; + + return xadc_power_adc_b(xadc, seq_mode); +} + +static int xadc_preenable(struct iio_dev *indio_dev) +{ + struct xadc *xadc = iio_priv(indio_dev); + unsigned long scan_mask; + int seq_mode; + int ret; + + ret = xadc_update_adc_reg(xadc, XADC_REG_CONF1, XADC_CONF1_SEQ_MASK, + XADC_CONF1_SEQ_DEFAULT); + if (ret) + goto err; + + scan_mask = *indio_dev->active_scan_mask; + seq_mode = xadc_get_seq_mode(xadc, scan_mask); + + ret = xadc_write_adc_reg(xadc, XADC_REG_SEQ(0), scan_mask & 0xffff); + if (ret) + goto err; + + /* + * In simultaneous mode the upper and lower aux channels are samples at + * the same time. In this mode the upper 8 bits in the sequencer + * register are don't care and the lower 8 bits control two channels + * each. As such we must set the bit if either the channel in the lower + * group or the upper group is enabled. + */ + if (seq_mode == XADC_CONF1_SEQ_SIMULTANEOUS) + scan_mask = ((scan_mask >> 8) | scan_mask) & 0xff0000; + + ret = xadc_write_adc_reg(xadc, XADC_REG_SEQ(1), scan_mask >> 16); + if (ret) + goto err; + + ret = xadc_power_adc_b(xadc, seq_mode); + if (ret) + goto err; + + ret = xadc_update_adc_reg(xadc, XADC_REG_CONF1, XADC_CONF1_SEQ_MASK, + seq_mode); + if (ret) + goto err; + + return 0; +err: + xadc_postdisable(indio_dev); + return ret; +} + +static const struct iio_buffer_setup_ops xadc_buffer_ops = { + .preenable = &xadc_preenable, + .postdisable = &xadc_postdisable, +}; + +static int xadc_read_samplerate(struct xadc *xadc) +{ + unsigned int div; + uint16_t val16; + int ret; + + ret = xadc_read_adc_reg(xadc, XADC_REG_CONF2, &val16); + if (ret) + return ret; + + div = (val16 & XADC_CONF2_DIV_MASK) >> XADC_CONF2_DIV_OFFSET; + if (div < 2) + div = 2; + + return xadc_get_dclk_rate(xadc) / div / 26; +} + +static int xadc_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int *val, int *val2, long info) +{ + struct xadc *xadc = iio_priv(indio_dev); + unsigned int bits = chan->scan_type.realbits; + uint16_t val16; + int ret; + + switch (info) { + case IIO_CHAN_INFO_RAW: + if (iio_buffer_enabled(indio_dev)) + return -EBUSY; + ret = xadc_read_adc_reg(xadc, chan->address, &val16); + if (ret < 0) + return ret; + + val16 >>= chan->scan_type.shift; + if (chan->scan_type.sign == 'u') + *val = val16; + else + *val = sign_extend32(val16, bits - 1); + + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + switch (chan->type) { + case IIO_VOLTAGE: + /* V = (val * 3.0) / 2**bits */ + switch (chan->address) { + case XADC_REG_VCCINT: + case XADC_REG_VCCAUX: + case XADC_REG_VREFP: + case XADC_REG_VREFN: + case XADC_REG_VCCBRAM: + case XADC_REG_VCCPINT: + case XADC_REG_VCCPAUX: + case XADC_REG_VCCO_DDR: + *val = 3000; + break; + default: + *val = 1000; + break; + } + *val2 = bits; + return IIO_VAL_FRACTIONAL_LOG2; + case IIO_TEMP: + *val = xadc->ops->temp_scale; + *val2 = bits; + return IIO_VAL_FRACTIONAL_LOG2; + default: + return -EINVAL; + } + case IIO_CHAN_INFO_OFFSET: + /* Only the temperature channel has an offset */ + *val = -((xadc->ops->temp_offset << bits) / xadc->ops->temp_scale); + return IIO_VAL_INT; + case IIO_CHAN_INFO_SAMP_FREQ: + ret = xadc_read_samplerate(xadc); + if (ret < 0) + return ret; + + *val = ret; + return IIO_VAL_INT; + default: + return -EINVAL; + } +} + +static int xadc_write_samplerate(struct xadc *xadc, int val) +{ + unsigned long clk_rate = xadc_get_dclk_rate(xadc); + unsigned int div; + + if (!clk_rate) + return -EINVAL; + + if (val <= 0) + return -EINVAL; + + /* Max. 150 kSPS */ + if (val > XADC_MAX_SAMPLERATE) + val = XADC_MAX_SAMPLERATE; + + val *= 26; + + /* Min 1MHz */ + if (val < 1000000) + val = 1000000; + + /* + * We want to round down, but only if we do not exceed the 150 kSPS + * limit. + */ + div = clk_rate / val; + if (clk_rate / div / 26 > XADC_MAX_SAMPLERATE) + div++; + if (div < 2) + div = 2; + else if (div > 0xff) + div = 0xff; + + return xadc_update_adc_reg(xadc, XADC_REG_CONF2, XADC_CONF2_DIV_MASK, + div << XADC_CONF2_DIV_OFFSET); +} + +static int xadc_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int val, int val2, long info) +{ + struct xadc *xadc = iio_priv(indio_dev); + + if (info != IIO_CHAN_INFO_SAMP_FREQ) + return -EINVAL; + + return xadc_write_samplerate(xadc, val); +} + +static const struct iio_event_spec xadc_temp_events[] = { + { + .type = IIO_EV_TYPE_THRESH, + .dir = IIO_EV_DIR_RISING, + .mask_separate = BIT(IIO_EV_INFO_ENABLE) | + BIT(IIO_EV_INFO_VALUE) | + BIT(IIO_EV_INFO_HYSTERESIS), + }, +}; + +/* Separate values for upper and lower thresholds, but only a shared enabled */ +static const struct iio_event_spec xadc_voltage_events[] = { + { + .type = IIO_EV_TYPE_THRESH, + .dir = IIO_EV_DIR_RISING, + .mask_separate = BIT(IIO_EV_INFO_VALUE), + }, { + .type = IIO_EV_TYPE_THRESH, + .dir = IIO_EV_DIR_FALLING, + .mask_separate = BIT(IIO_EV_INFO_VALUE), + }, { + .type = IIO_EV_TYPE_THRESH, + .dir = IIO_EV_DIR_EITHER, + .mask_separate = BIT(IIO_EV_INFO_ENABLE), + }, +}; + +#define XADC_CHAN_TEMP(_chan, _scan_index, _addr, _bits) { \ + .type = IIO_TEMP, \ + .indexed = 1, \ + .channel = (_chan), \ + .address = (_addr), \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ + BIT(IIO_CHAN_INFO_SCALE) | \ + BIT(IIO_CHAN_INFO_OFFSET), \ + .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ), \ + .event_spec = xadc_temp_events, \ + .num_event_specs = ARRAY_SIZE(xadc_temp_events), \ + .scan_index = (_scan_index), \ + .scan_type = { \ + .sign = 'u', \ + .realbits = (_bits), \ + .storagebits = 16, \ + .shift = 16 - (_bits), \ + .endianness = IIO_CPU, \ + }, \ +} + +#define XADC_CHAN_VOLTAGE(_chan, _scan_index, _addr, _bits, _ext, _alarm) { \ + .type = IIO_VOLTAGE, \ + .indexed = 1, \ + .channel = (_chan), \ + .address = (_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), \ + .event_spec = (_alarm) ? xadc_voltage_events : NULL, \ + .num_event_specs = (_alarm) ? ARRAY_SIZE(xadc_voltage_events) : 0, \ + .scan_index = (_scan_index), \ + .scan_type = { \ + .sign = ((_addr) == XADC_REG_VREFN) ? 's' : 'u', \ + .realbits = (_bits), \ + .storagebits = 16, \ + .shift = 16 - (_bits), \ + .endianness = IIO_CPU, \ + }, \ + .extend_name = _ext, \ +} + +/* 7 Series */ +#define XADC_7S_CHAN_TEMP(_chan, _scan_index, _addr) \ + XADC_CHAN_TEMP(_chan, _scan_index, _addr, 12) +#define XADC_7S_CHAN_VOLTAGE(_chan, _scan_index, _addr, _ext, _alarm) \ + XADC_CHAN_VOLTAGE(_chan, _scan_index, _addr, 12, _ext, _alarm) + +static const struct iio_chan_spec xadc_7s_channels[] = { + XADC_7S_CHAN_TEMP(0, 8, XADC_REG_TEMP), + XADC_7S_CHAN_VOLTAGE(0, 9, XADC_REG_VCCINT, "vccint", true), + XADC_7S_CHAN_VOLTAGE(1, 10, XADC_REG_VCCAUX, "vccaux", true), + XADC_7S_CHAN_VOLTAGE(2, 14, XADC_REG_VCCBRAM, "vccbram", true), + XADC_7S_CHAN_VOLTAGE(3, 5, XADC_REG_VCCPINT, "vccpint", true), + XADC_7S_CHAN_VOLTAGE(4, 6, XADC_REG_VCCPAUX, "vccpaux", true), + XADC_7S_CHAN_VOLTAGE(5, 7, XADC_REG_VCCO_DDR, "vccoddr", true), + XADC_7S_CHAN_VOLTAGE(6, 12, XADC_REG_VREFP, "vrefp", false), + XADC_7S_CHAN_VOLTAGE(7, 13, XADC_REG_VREFN, "vrefn", false), + XADC_7S_CHAN_VOLTAGE(8, 11, XADC_REG_VPVN, NULL, false), + XADC_7S_CHAN_VOLTAGE(9, 16, XADC_REG_VAUX(0), NULL, false), + XADC_7S_CHAN_VOLTAGE(10, 17, XADC_REG_VAUX(1), NULL, false), + XADC_7S_CHAN_VOLTAGE(11, 18, XADC_REG_VAUX(2), NULL, false), + XADC_7S_CHAN_VOLTAGE(12, 19, XADC_REG_VAUX(3), NULL, false), + XADC_7S_CHAN_VOLTAGE(13, 20, XADC_REG_VAUX(4), NULL, false), + XADC_7S_CHAN_VOLTAGE(14, 21, XADC_REG_VAUX(5), NULL, false), + XADC_7S_CHAN_VOLTAGE(15, 22, XADC_REG_VAUX(6), NULL, false), + XADC_7S_CHAN_VOLTAGE(16, 23, XADC_REG_VAUX(7), NULL, false), + XADC_7S_CHAN_VOLTAGE(17, 24, XADC_REG_VAUX(8), NULL, false), + XADC_7S_CHAN_VOLTAGE(18, 25, XADC_REG_VAUX(9), NULL, false), + XADC_7S_CHAN_VOLTAGE(19, 26, XADC_REG_VAUX(10), NULL, false), + XADC_7S_CHAN_VOLTAGE(20, 27, XADC_REG_VAUX(11), NULL, false), + XADC_7S_CHAN_VOLTAGE(21, 28, XADC_REG_VAUX(12), NULL, false), + XADC_7S_CHAN_VOLTAGE(22, 29, XADC_REG_VAUX(13), NULL, false), + XADC_7S_CHAN_VOLTAGE(23, 30, XADC_REG_VAUX(14), NULL, false), + XADC_7S_CHAN_VOLTAGE(24, 31, XADC_REG_VAUX(15), NULL, false), +}; + +/* UltraScale */ +#define XADC_US_CHAN_TEMP(_chan, _scan_index, _addr) \ + XADC_CHAN_TEMP(_chan, _scan_index, _addr, 10) +#define XADC_US_CHAN_VOLTAGE(_chan, _scan_index, _addr, _ext, _alarm) \ + XADC_CHAN_VOLTAGE(_chan, _scan_index, _addr, 10, _ext, _alarm) + +static const struct iio_chan_spec xadc_us_channels[] = { + XADC_US_CHAN_TEMP(0, 8, XADC_REG_TEMP), + XADC_US_CHAN_VOLTAGE(0, 9, XADC_REG_VCCINT, "vccint", true), + XADC_US_CHAN_VOLTAGE(1, 10, XADC_REG_VCCAUX, "vccaux", true), + XADC_US_CHAN_VOLTAGE(2, 14, XADC_REG_VCCBRAM, "vccbram", true), + XADC_US_CHAN_VOLTAGE(3, 5, XADC_REG_VCCPINT, "vccpsintlp", true), + XADC_US_CHAN_VOLTAGE(4, 6, XADC_REG_VCCPAUX, "vccpsintfp", true), + XADC_US_CHAN_VOLTAGE(5, 7, XADC_REG_VCCO_DDR, "vccpsaux", true), + XADC_US_CHAN_VOLTAGE(6, 12, XADC_REG_VREFP, "vrefp", false), + XADC_US_CHAN_VOLTAGE(7, 13, XADC_REG_VREFN, "vrefn", false), + XADC_US_CHAN_VOLTAGE(8, 11, XADC_REG_VPVN, NULL, false), + XADC_US_CHAN_VOLTAGE(9, 16, XADC_REG_VAUX(0), NULL, false), + XADC_US_CHAN_VOLTAGE(10, 17, XADC_REG_VAUX(1), NULL, false), + XADC_US_CHAN_VOLTAGE(11, 18, XADC_REG_VAUX(2), NULL, false), + XADC_US_CHAN_VOLTAGE(12, 19, XADC_REG_VAUX(3), NULL, false), + XADC_US_CHAN_VOLTAGE(13, 20, XADC_REG_VAUX(4), NULL, false), + XADC_US_CHAN_VOLTAGE(14, 21, XADC_REG_VAUX(5), NULL, false), + XADC_US_CHAN_VOLTAGE(15, 22, XADC_REG_VAUX(6), NULL, false), + XADC_US_CHAN_VOLTAGE(16, 23, XADC_REG_VAUX(7), NULL, false), + XADC_US_CHAN_VOLTAGE(17, 24, XADC_REG_VAUX(8), NULL, false), + XADC_US_CHAN_VOLTAGE(18, 25, XADC_REG_VAUX(9), NULL, false), + XADC_US_CHAN_VOLTAGE(19, 26, XADC_REG_VAUX(10), NULL, false), + XADC_US_CHAN_VOLTAGE(20, 27, XADC_REG_VAUX(11), NULL, false), + XADC_US_CHAN_VOLTAGE(21, 28, XADC_REG_VAUX(12), NULL, false), + XADC_US_CHAN_VOLTAGE(22, 29, XADC_REG_VAUX(13), NULL, false), + XADC_US_CHAN_VOLTAGE(23, 30, XADC_REG_VAUX(14), NULL, false), + XADC_US_CHAN_VOLTAGE(24, 31, XADC_REG_VAUX(15), NULL, false), +}; + +static const struct iio_info xadc_info = { + .read_raw = &xadc_read_raw, + .write_raw = &xadc_write_raw, + .read_event_config = &xadc_read_event_config, + .write_event_config = &xadc_write_event_config, + .read_event_value = &xadc_read_event_value, + .write_event_value = &xadc_write_event_value, + .update_scan_mode = &xadc_update_scan_mode, +}; + +static const struct of_device_id xadc_of_match_table[] = { + { + .compatible = "xlnx,zynq-xadc-1.00.a", + .data = &xadc_zynq_ops + }, { + .compatible = "xlnx,axi-xadc-1.00.a", + .data = &xadc_7s_axi_ops + }, { + .compatible = "xlnx,system-management-wiz-1.3", + .data = &xadc_us_axi_ops + }, + { } +}; +MODULE_DEVICE_TABLE(of, xadc_of_match_table); + +static int xadc_parse_dt(struct iio_dev *indio_dev, unsigned int *conf, int irq) +{ + struct device *dev = indio_dev->dev.parent; + struct xadc *xadc = iio_priv(indio_dev); + const struct iio_chan_spec *channel_templates; + struct iio_chan_spec *channels, *chan; + struct fwnode_handle *chan_node, *child; + unsigned int max_channels; + unsigned int num_channels; + const char *external_mux; + u32 ext_mux_chan; + u32 reg; + int ret; + int i; + + *conf = 0; + + ret = device_property_read_string(dev, "xlnx,external-mux", &external_mux); + if (ret < 0 || strcasecmp(external_mux, "none") == 0) + xadc->external_mux_mode = XADC_EXTERNAL_MUX_NONE; + else if (strcasecmp(external_mux, "single") == 0) + xadc->external_mux_mode = XADC_EXTERNAL_MUX_SINGLE; + else if (strcasecmp(external_mux, "dual") == 0) + xadc->external_mux_mode = XADC_EXTERNAL_MUX_DUAL; + else + return -EINVAL; + + if (xadc->external_mux_mode != XADC_EXTERNAL_MUX_NONE) { + ret = device_property_read_u32(dev, "xlnx,external-mux-channel", &ext_mux_chan); + if (ret < 0) + return ret; + + if (xadc->external_mux_mode == XADC_EXTERNAL_MUX_SINGLE) { + if (ext_mux_chan == 0) + ext_mux_chan = XADC_REG_VPVN; + else if (ext_mux_chan <= 16) + ext_mux_chan = XADC_REG_VAUX(ext_mux_chan - 1); + else + return -EINVAL; + } else { + if (ext_mux_chan > 0 && ext_mux_chan <= 8) + ext_mux_chan = XADC_REG_VAUX(ext_mux_chan - 1); + else + return -EINVAL; + } + + *conf |= XADC_CONF0_MUX | XADC_CONF0_CHAN(ext_mux_chan); + } + if (xadc->ops->type == XADC_TYPE_S7) { + channel_templates = xadc_7s_channels; + max_channels = ARRAY_SIZE(xadc_7s_channels); + } else { + channel_templates = xadc_us_channels; + max_channels = ARRAY_SIZE(xadc_us_channels); + } + channels = devm_kmemdup_array(dev, channel_templates, max_channels, + sizeof(*channel_templates), GFP_KERNEL); + if (!channels) + return -ENOMEM; + + num_channels = 9; + chan = &channels[9]; + + chan_node = device_get_named_child_node(dev, "xlnx,channels"); + fwnode_for_each_child_node(chan_node, child) { + if (num_channels >= max_channels) { + fwnode_handle_put(child); + break; + } + + ret = fwnode_property_read_u32(child, "reg", ®); + if (ret || reg > 16) + continue; + + if (fwnode_property_read_bool(child, "xlnx,bipolar")) + chan->scan_type.sign = 's'; + + if (reg == 0) { + chan->scan_index = 11; + chan->address = XADC_REG_VPVN; + } else { + chan->scan_index = 15 + reg; + chan->address = XADC_REG_VAUX(reg - 1); + } + num_channels++; + chan++; + } + fwnode_handle_put(chan_node); + + /* No IRQ => no events */ + if (irq <= 0) { + for (i = 0; i < num_channels; i++) { + channels[i].event_spec = NULL; + channels[i].num_event_specs = 0; + } + } + + indio_dev->num_channels = num_channels; + indio_dev->channels = devm_krealloc_array(dev, channels, + num_channels, sizeof(*channels), + GFP_KERNEL); + /* If we can't resize the channels array, just use the original */ + if (!indio_dev->channels) + indio_dev->channels = channels; + + return 0; +} + +static const char * const xadc_type_names[] = { + [XADC_TYPE_S7] = "xadc", + [XADC_TYPE_US] = "xilinx-system-monitor", +}; + +static void xadc_cancel_delayed_work(void *data) +{ + struct delayed_work *work = data; + + cancel_delayed_work_sync(work); +} + +static int xadc_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + const struct xadc_ops *ops; + struct iio_dev *indio_dev; + unsigned int bipolar_mask; + unsigned int conf0; + struct xadc *xadc; + int ret; + int irq; + int i; + + ops = device_get_match_data(dev); + if (!ops) + return -EINVAL; + + irq = platform_get_irq_optional(pdev, 0); + if (irq < 0 && + (irq != -ENXIO || !(ops->flags & XADC_FLAGS_IRQ_OPTIONAL))) + return irq; + + indio_dev = devm_iio_device_alloc(dev, sizeof(*xadc)); + if (!indio_dev) + return -ENOMEM; + + xadc = iio_priv(indio_dev); + xadc->ops = ops; + init_completion(&xadc->completion); + mutex_init(&xadc->mutex); + spin_lock_init(&xadc->lock); + INIT_DELAYED_WORK(&xadc->zynq_unmask_work, xadc_zynq_unmask_worker); + + xadc->base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(xadc->base)) + return PTR_ERR(xadc->base); + + indio_dev->name = xadc_type_names[xadc->ops->type]; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->info = &xadc_info; + + ret = xadc_parse_dt(indio_dev, &conf0, irq); + if (ret) + return ret; + + if (xadc->ops->flags & XADC_FLAGS_BUFFERED) { + ret = devm_iio_triggered_buffer_setup(dev, indio_dev, + &iio_pollfunc_store_time, + &xadc_trigger_handler, + &xadc_buffer_ops); + if (ret) + return ret; + + if (irq > 0) { + xadc->convst_trigger = xadc_alloc_trigger(indio_dev, "convst"); + if (IS_ERR(xadc->convst_trigger)) + return PTR_ERR(xadc->convst_trigger); + + xadc->samplerate_trigger = xadc_alloc_trigger(indio_dev, + "samplerate"); + if (IS_ERR(xadc->samplerate_trigger)) + return PTR_ERR(xadc->samplerate_trigger); + } + } + + xadc->clk = devm_clk_get_enabled(dev, NULL); + if (IS_ERR(xadc->clk)) + return PTR_ERR(xadc->clk); + + /* + * Make sure not to exceed the maximum samplerate since otherwise the + * resulting interrupt storm will soft-lock the system. + */ + if (xadc->ops->flags & XADC_FLAGS_BUFFERED) { + ret = xadc_read_samplerate(xadc); + if (ret < 0) + return ret; + + if (ret > XADC_MAX_SAMPLERATE) { + ret = xadc_write_samplerate(xadc, XADC_MAX_SAMPLERATE); + if (ret < 0) + return ret; + } + } + + if (irq > 0) { + ret = devm_request_irq(dev, irq, xadc->ops->interrupt_handler, + 0, dev_name(dev), indio_dev); + if (ret) + return ret; + + ret = devm_add_action_or_reset(dev, xadc_cancel_delayed_work, + &xadc->zynq_unmask_work); + if (ret) + return ret; + } + + ret = xadc->ops->setup(pdev, indio_dev, irq); + if (ret) + return ret; + + for (i = 0; i < 16; i++) + xadc_read_adc_reg(xadc, XADC_REG_THRESHOLD(i), + &xadc->threshold[i]); + + ret = xadc_write_adc_reg(xadc, XADC_REG_CONF0, conf0); + if (ret) + return ret; + + bipolar_mask = 0; + for (i = 0; i < indio_dev->num_channels; i++) { + if (indio_dev->channels[i].scan_type.sign == 's') + bipolar_mask |= BIT(indio_dev->channels[i].scan_index); + } + + ret = xadc_write_adc_reg(xadc, XADC_REG_INPUT_MODE(0), bipolar_mask); + if (ret) + return ret; + + ret = xadc_write_adc_reg(xadc, XADC_REG_INPUT_MODE(1), + bipolar_mask >> 16); + if (ret) + return ret; + + /* Go to non-buffered mode */ + xadc_postdisable(indio_dev); + + return devm_iio_device_register(dev, indio_dev); +} + +static struct platform_driver xadc_driver = { + .probe = xadc_probe, + .driver = { + .name = "xadc", + .of_match_table = xadc_of_match_table, + }, +}; +module_platform_driver(xadc_driver); + +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Lars-Peter Clausen "); +MODULE_DESCRIPTION("Xilinx XADC IIO driver"); diff --git a/drivers/iio/adc/xilinx-xadc-events.c b/drivers/iio/adc/xilinx-xadc-events.c new file mode 100644 index 000000000..c188d3dca --- /dev/null +++ b/drivers/iio/adc/xilinx-xadc-events.c @@ -0,0 +1,244 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Xilinx XADC driver + * + * Copyright 2013 Analog Devices Inc. + * Author: Lars-Peter Clausen + */ + +#include +#include +#include + +#include "xilinx-xadc.h" + +static const struct iio_chan_spec *xadc_event_to_channel( + struct iio_dev *indio_dev, unsigned int event) +{ + switch (event) { + case XADC_THRESHOLD_OT_MAX: + case XADC_THRESHOLD_TEMP_MAX: + return &indio_dev->channels[0]; + case XADC_THRESHOLD_VCCINT_MAX: + case XADC_THRESHOLD_VCCAUX_MAX: + return &indio_dev->channels[event]; + default: + return &indio_dev->channels[event-1]; + } +} + +static void xadc_handle_event(struct iio_dev *indio_dev, unsigned int event) +{ + const struct iio_chan_spec *chan; + + /* Temperature threshold error, we don't handle this yet */ + if (event == 0) + return; + + chan = xadc_event_to_channel(indio_dev, event); + + if (chan->type == IIO_TEMP) { + /* + * The temperature channel only supports over-temperature + * events. + */ + iio_push_event(indio_dev, + IIO_UNMOD_EVENT_CODE(chan->type, chan->channel, + IIO_EV_TYPE_THRESH, IIO_EV_DIR_RISING), + iio_get_time_ns(indio_dev)); + } else { + /* + * For other channels we don't know whether it is a upper or + * lower threshold event. Userspace will have to check the + * channel value if it wants to know. + */ + iio_push_event(indio_dev, + IIO_UNMOD_EVENT_CODE(chan->type, chan->channel, + IIO_EV_TYPE_THRESH, IIO_EV_DIR_EITHER), + iio_get_time_ns(indio_dev)); + } +} + +void xadc_handle_events(struct iio_dev *indio_dev, unsigned long events) +{ + unsigned int i; + + for_each_set_bit(i, &events, 8) + xadc_handle_event(indio_dev, i); +} + +static unsigned int xadc_get_threshold_offset(const struct iio_chan_spec *chan, + enum iio_event_direction dir) +{ + unsigned int offset; + + if (chan->type == IIO_TEMP) { + offset = XADC_THRESHOLD_OT_MAX; + } else { + if (chan->channel < 2) + offset = chan->channel + 1; + else + offset = chan->channel + 6; + } + + if (dir == IIO_EV_DIR_FALLING) + offset += 4; + + return offset; +} + +static unsigned int xadc_get_alarm_mask(const struct iio_chan_spec *chan) +{ + if (chan->type == IIO_TEMP) + return XADC_ALARM_OT_MASK; + switch (chan->channel) { + case 0: + return XADC_ALARM_VCCINT_MASK; + case 1: + return XADC_ALARM_VCCAUX_MASK; + case 2: + return XADC_ALARM_VCCBRAM_MASK; + case 3: + return XADC_ALARM_VCCPINT_MASK; + case 4: + return XADC_ALARM_VCCPAUX_MASK; + case 5: + return XADC_ALARM_VCCODDR_MASK; + default: + /* We will never get here */ + return 0; + } +} + +int xadc_read_event_config(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, enum iio_event_type type, + enum iio_event_direction dir) +{ + struct xadc *xadc = iio_priv(indio_dev); + + return (bool)(xadc->alarm_mask & xadc_get_alarm_mask(chan)); +} + +int xadc_write_event_config(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, enum iio_event_type type, + enum iio_event_direction dir, bool state) +{ + unsigned int alarm = xadc_get_alarm_mask(chan); + struct xadc *xadc = iio_priv(indio_dev); + uint16_t cfg, old_cfg; + int ret; + + mutex_lock(&xadc->mutex); + + if (state) + xadc->alarm_mask |= alarm; + else + xadc->alarm_mask &= ~alarm; + + xadc->ops->update_alarm(xadc, xadc->alarm_mask); + + ret = _xadc_read_adc_reg(xadc, XADC_REG_CONF1, &cfg); + if (ret) + goto err_out; + + old_cfg = cfg; + cfg |= XADC_CONF1_ALARM_MASK; + cfg &= ~((xadc->alarm_mask & 0xf0) << 4); /* bram, pint, paux, ddr */ + cfg &= ~((xadc->alarm_mask & 0x08) >> 3); /* ot */ + cfg &= ~((xadc->alarm_mask & 0x07) << 1); /* temp, vccint, vccaux */ + if (old_cfg != cfg) + ret = _xadc_write_adc_reg(xadc, XADC_REG_CONF1, cfg); + +err_out: + mutex_unlock(&xadc->mutex); + + return ret; +} + +int xadc_read_event_value(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, enum iio_event_type type, + enum iio_event_direction dir, enum iio_event_info info, + int *val, int *val2) +{ + unsigned int offset = xadc_get_threshold_offset(chan, dir); + struct xadc *xadc = iio_priv(indio_dev); + + switch (info) { + case IIO_EV_INFO_VALUE: + *val = xadc->threshold[offset]; + break; + case IIO_EV_INFO_HYSTERESIS: + *val = xadc->temp_hysteresis; + break; + default: + return -EINVAL; + } + + /* MSB aligned */ + *val >>= 16 - chan->scan_type.realbits; + + return IIO_VAL_INT; +} + +int xadc_write_event_value(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, enum iio_event_type type, + enum iio_event_direction dir, enum iio_event_info info, + int val, int val2) +{ + unsigned int offset = xadc_get_threshold_offset(chan, dir); + struct xadc *xadc = iio_priv(indio_dev); + int ret = 0; + + /* MSB aligned */ + val <<= 16 - chan->scan_type.realbits; + + if (val < 0 || val > 0xffff) + return -EINVAL; + + mutex_lock(&xadc->mutex); + + switch (info) { + case IIO_EV_INFO_VALUE: + xadc->threshold[offset] = val; + break; + case IIO_EV_INFO_HYSTERESIS: + xadc->temp_hysteresis = val; + break; + default: + mutex_unlock(&xadc->mutex); + return -EINVAL; + } + + if (chan->type == IIO_TEMP) { + /* + * According to the datasheet we need to set the lower 4 bits to + * 0x3, otherwise 125 degree celsius will be used as the + * threshold. + */ + val |= 0x3; + + /* + * Since we store the hysteresis as relative (to the threshold) + * value, but the hardware expects an absolute value we need to + * recalculate this value whenever the hysteresis or the + * threshold changes. + */ + if (xadc->threshold[offset] < xadc->temp_hysteresis) + xadc->threshold[offset + 4] = 0; + else + xadc->threshold[offset + 4] = xadc->threshold[offset] - + xadc->temp_hysteresis; + ret = _xadc_write_adc_reg(xadc, XADC_REG_THRESHOLD(offset + 4), + xadc->threshold[offset + 4]); + if (ret) + goto out_unlock; + } + + if (info == IIO_EV_INFO_VALUE) + ret = _xadc_write_adc_reg(xadc, XADC_REG_THRESHOLD(offset), val); + +out_unlock: + mutex_unlock(&xadc->mutex); + + return ret; +} diff --git a/drivers/iio/adc/xilinx-xadc.h b/drivers/iio/adc/xilinx-xadc.h new file mode 100644 index 000000000..b4d9d4683 --- /dev/null +++ b/drivers/iio/adc/xilinx-xadc.h @@ -0,0 +1,214 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Xilinx XADC driver + * + * Copyright 2013 Analog Devices Inc. + * Author: Lars-Peter Clausen + */ + +#ifndef __IIO_XILINX_XADC__ +#define __IIO_XILINX_XADC__ + +#include +#include +#include + +struct iio_dev; +struct clk; +struct xadc_ops; +struct platform_device; + +void xadc_handle_events(struct iio_dev *indio_dev, unsigned long events); + +int xadc_read_event_config(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, enum iio_event_type type, + enum iio_event_direction dir); +int xadc_write_event_config(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, enum iio_event_type type, + enum iio_event_direction dir, bool state); +int xadc_read_event_value(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, enum iio_event_type type, + enum iio_event_direction dir, enum iio_event_info info, + int *val, int *val2); +int xadc_write_event_value(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, enum iio_event_type type, + enum iio_event_direction dir, enum iio_event_info info, + int val, int val2); + +enum xadc_external_mux_mode { + XADC_EXTERNAL_MUX_NONE, + XADC_EXTERNAL_MUX_SINGLE, + XADC_EXTERNAL_MUX_DUAL, +}; + +struct xadc { + void __iomem *base; + struct clk *clk; + + const struct xadc_ops *ops; + + uint16_t threshold[16]; + uint16_t temp_hysteresis; + unsigned int alarm_mask; + + uint16_t *data; + + struct iio_trigger *trigger; + struct iio_trigger *convst_trigger; + struct iio_trigger *samplerate_trigger; + + enum xadc_external_mux_mode external_mux_mode; + + unsigned int zynq_masked_alarm; + unsigned int zynq_intmask; + struct delayed_work zynq_unmask_work; + + struct mutex mutex; + spinlock_t lock; + + struct completion completion; +}; + +enum xadc_type { + XADC_TYPE_S7, /* Series 7 */ + XADC_TYPE_US, /* UltraScale and UltraScale+ */ +}; + +struct xadc_ops { + int (*read)(struct xadc *xadc, unsigned int reg, uint16_t *val); + int (*write)(struct xadc *xadc, unsigned int reg, uint16_t val); + int (*setup)(struct platform_device *pdev, struct iio_dev *indio_dev, + int irq); + void (*update_alarm)(struct xadc *xadc, unsigned int alarm); + unsigned long (*get_dclk_rate)(struct xadc *xadc); + irqreturn_t (*interrupt_handler)(int irq, void *devid); + + unsigned int flags; + enum xadc_type type; + int temp_scale; + int temp_offset; +}; + +static inline int _xadc_read_adc_reg(struct xadc *xadc, unsigned int reg, + uint16_t *val) +{ + lockdep_assert_held(&xadc->mutex); + return xadc->ops->read(xadc, reg, val); +} + +static inline int _xadc_write_adc_reg(struct xadc *xadc, unsigned int reg, + uint16_t val) +{ + lockdep_assert_held(&xadc->mutex); + return xadc->ops->write(xadc, reg, val); +} + +static inline int xadc_read_adc_reg(struct xadc *xadc, unsigned int reg, + uint16_t *val) +{ + int ret; + + mutex_lock(&xadc->mutex); + ret = _xadc_read_adc_reg(xadc, reg, val); + mutex_unlock(&xadc->mutex); + return ret; +} + +static inline int xadc_write_adc_reg(struct xadc *xadc, unsigned int reg, + uint16_t val) +{ + int ret; + + mutex_lock(&xadc->mutex); + ret = _xadc_write_adc_reg(xadc, reg, val); + mutex_unlock(&xadc->mutex); + return ret; +} + +/* XADC hardmacro register definitions */ +#define XADC_REG_TEMP 0x00 +#define XADC_REG_VCCINT 0x01 +#define XADC_REG_VCCAUX 0x02 +#define XADC_REG_VPVN 0x03 +#define XADC_REG_VREFP 0x04 +#define XADC_REG_VREFN 0x05 +#define XADC_REG_VCCBRAM 0x06 + +#define XADC_REG_VCCPINT 0x0d +#define XADC_REG_VCCPAUX 0x0e +#define XADC_REG_VCCO_DDR 0x0f +#define XADC_REG_VAUX(x) (0x10 + (x)) + +#define XADC_REG_MAX_TEMP 0x20 +#define XADC_REG_MAX_VCCINT 0x21 +#define XADC_REG_MAX_VCCAUX 0x22 +#define XADC_REG_MAX_VCCBRAM 0x23 +#define XADC_REG_MIN_TEMP 0x24 +#define XADC_REG_MIN_VCCINT 0x25 +#define XADC_REG_MIN_VCCAUX 0x26 +#define XADC_REG_MIN_VCCBRAM 0x27 +#define XADC_REG_MAX_VCCPINT 0x28 +#define XADC_REG_MAX_VCCPAUX 0x29 +#define XADC_REG_MAX_VCCO_DDR 0x2a +#define XADC_REG_MIN_VCCPINT 0x2c +#define XADC_REG_MIN_VCCPAUX 0x2d +#define XADC_REG_MIN_VCCO_DDR 0x2e + +#define XADC_REG_CONF0 0x40 +#define XADC_REG_CONF1 0x41 +#define XADC_REG_CONF2 0x42 +#define XADC_REG_SEQ(x) (0x48 + (x)) +#define XADC_REG_INPUT_MODE(x) (0x4c + (x)) +#define XADC_REG_THRESHOLD(x) (0x50 + (x)) + +#define XADC_REG_FLAG 0x3f + +#define XADC_CONF0_EC BIT(9) +#define XADC_CONF0_ACQ BIT(8) +#define XADC_CONF0_MUX BIT(11) +#define XADC_CONF0_CHAN(x) (x) + +#define XADC_CONF1_SEQ_MASK (0xf << 12) +#define XADC_CONF1_SEQ_DEFAULT (0 << 12) +#define XADC_CONF1_SEQ_SINGLE_PASS (1 << 12) +#define XADC_CONF1_SEQ_CONTINUOUS (2 << 12) +#define XADC_CONF1_SEQ_SINGLE_CHANNEL (3 << 12) +#define XADC_CONF1_SEQ_SIMULTANEOUS (4 << 12) +#define XADC_CONF1_SEQ_INDEPENDENT (8 << 12) +#define XADC_CONF1_ALARM_MASK 0x0f0f + +#define XADC_CONF2_DIV_MASK 0xff00 +#define XADC_CONF2_DIV_OFFSET 8 + +#define XADC_CONF2_PD_MASK (0x3 << 4) +#define XADC_CONF2_PD_NONE (0x0 << 4) +#define XADC_CONF2_PD_ADC_B (0x2 << 4) +#define XADC_CONF2_PD_BOTH (0x3 << 4) + +#define XADC_ALARM_TEMP_MASK BIT(0) +#define XADC_ALARM_VCCINT_MASK BIT(1) +#define XADC_ALARM_VCCAUX_MASK BIT(2) +#define XADC_ALARM_OT_MASK BIT(3) +#define XADC_ALARM_VCCBRAM_MASK BIT(4) +#define XADC_ALARM_VCCPINT_MASK BIT(5) +#define XADC_ALARM_VCCPAUX_MASK BIT(6) +#define XADC_ALARM_VCCODDR_MASK BIT(7) + +#define XADC_THRESHOLD_TEMP_MAX 0x0 +#define XADC_THRESHOLD_VCCINT_MAX 0x1 +#define XADC_THRESHOLD_VCCAUX_MAX 0x2 +#define XADC_THRESHOLD_OT_MAX 0x3 +#define XADC_THRESHOLD_TEMP_MIN 0x4 +#define XADC_THRESHOLD_VCCINT_MIN 0x5 +#define XADC_THRESHOLD_VCCAUX_MIN 0x6 +#define XADC_THRESHOLD_OT_MIN 0x7 +#define XADC_THRESHOLD_VCCBRAM_MAX 0x8 +#define XADC_THRESHOLD_VCCPINT_MAX 0x9 +#define XADC_THRESHOLD_VCCPAUX_MAX 0xa +#define XADC_THRESHOLD_VCCODDR_MAX 0xb +#define XADC_THRESHOLD_VCCBRAM_MIN 0xc +#define XADC_THRESHOLD_VCCPINT_MIN 0xd +#define XADC_THRESHOLD_VCCPAUX_MIN 0xe +#define XADC_THRESHOLD_VCCODDR_MIN 0xf + +#endif -- cgit v1.3.1