| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905 |
- // SPDX-License-Identifier: GPL-2.0+
- /*
- * ADXL380 3-Axis Digital Accelerometer core driver
- *
- * Copyright 2024 Analog Devices Inc.
- */
- #include <linux/bitfield.h>
- #include <linux/interrupt.h>
- #include <linux/irq.h>
- #include <linux/module.h>
- #include <linux/property.h>
- #include <linux/regmap.h>
- #include <linux/units.h>
- #include <linux/unaligned.h>
- #include <linux/iio/buffer.h>
- #include <linux/iio/events.h>
- #include <linux/iio/iio.h>
- #include <linux/iio/kfifo_buf.h>
- #include <linux/iio/sysfs.h>
- #include <linux/regulator/consumer.h>
- #include "adxl380.h"
- #define ADXL380_ID_VAL 380
- #define ADXL382_ID_VAL 382
- #define ADXL380_DEVID_AD_REG 0x00
- #define ADLX380_PART_ID_REG 0x02
- #define ADXL380_X_DATA_H_REG 0x15
- #define ADXL380_Y_DATA_H_REG 0x17
- #define ADXL380_Z_DATA_H_REG 0x19
- #define ADXL380_T_DATA_H_REG 0x1B
- #define ADXL380_MISC_0_REG 0x20
- #define ADXL380_XL382_MSK BIT(7)
- #define ADXL380_MISC_1_REG 0x21
- #define ADXL380_X_DSM_OFFSET_REG 0x4D
- #define ADXL380_ACT_INACT_CTL_REG 0x37
- #define ADXL380_INACT_EN_MSK BIT(2)
- #define ADXL380_ACT_EN_MSK BIT(0)
- #define ADXL380_SNSR_AXIS_EN_REG 0x38
- #define ADXL380_ACT_INACT_AXIS_EN_MSK GENMASK(2, 0)
- #define ADXL380_THRESH_ACT_H_REG 0x39
- #define ADXL380_TIME_ACT_H_REG 0x3B
- #define ADXL380_THRESH_INACT_H_REG 0x3E
- #define ADXL380_TIME_INACT_H_REG 0x40
- #define ADXL380_THRESH_MAX GENMASK(12, 0)
- #define ADXL380_TIME_MAX GENMASK(24, 0)
- #define ADXL380_FIFO_CONFIG_0_REG 0x30
- #define ADXL380_FIFO_SAMPLES_8_MSK BIT(0)
- #define ADXL380_FIFO_MODE_MSK GENMASK(5, 4)
- #define ADXL380_FIFO_DISABLED 0
- #define ADXL380_FIFO_NORMAL 1
- #define ADXL380_FIFO_STREAMED 2
- #define ADXL380_FIFO_TRIGGERED 3
- #define ADXL380_FIFO_CONFIG_1_REG 0x31
- #define ADXL380_FIFO_STATUS_0_REG 0x1E
- #define ADXL380_TAP_THRESH_REG 0x43
- #define ADXL380_TAP_DUR_REG 0x44
- #define ADXL380_TAP_LATENT_REG 0x45
- #define ADXL380_TAP_WINDOW_REG 0x46
- #define ADXL380_TAP_TIME_MAX GENMASK(7, 0)
- #define ADXL380_TAP_CFG_REG 0x47
- #define ADXL380_TAP_AXIS_MSK GENMASK(1, 0)
- #define ADXL380_TRIG_CFG_REG 0x49
- #define ADXL380_TRIG_CFG_DEC_2X_MSK BIT(7)
- #define ADXL380_TRIG_CFG_SINC_RATE_MSK BIT(6)
- #define ADXL380_FILTER_REG 0x50
- #define ADXL380_FILTER_EQ_FILT_MSK BIT(6)
- #define ADXL380_FILTER_LPF_MODE_MSK GENMASK(5, 4)
- #define ADXL380_FILTER_HPF_PATH_MSK BIT(3)
- #define ADXL380_FILTER_HPF_CORNER_MSK GENMASK(2, 0)
- #define ADXL380_OP_MODE_REG 0x26
- #define ADXL380_OP_MODE_RANGE_MSK GENMASK(7, 6)
- #define ADXL380_OP_MODE_MSK GENMASK(3, 0)
- #define ADXL380_OP_MODE_STANDBY 0
- #define ADXL380_OP_MODE_HEART_SOUND 1
- #define ADXL380_OP_MODE_ULP 2
- #define ADXL380_OP_MODE_VLP 3
- #define ADXL380_OP_MODE_LP 4
- #define ADXL380_OP_MODE_LP_ULP 6
- #define ADXL380_OP_MODE_LP_VLP 7
- #define ADXL380_OP_MODE_RBW 8
- #define ADXL380_OP_MODE_RBW_ULP 10
- #define ADXL380_OP_MODE_RBW_VLP 11
- #define ADXL380_OP_MODE_HP 12
- #define ADXL380_OP_MODE_HP_ULP 14
- #define ADXL380_OP_MODE_HP_VLP 15
- #define ADXL380_OP_MODE_4G_RANGE 0
- #define ADXL382_OP_MODE_15G_RANGE 0
- #define ADXL380_OP_MODE_8G_RANGE 1
- #define ADXL382_OP_MODE_30G_RANGE 1
- #define ADXL380_OP_MODE_16G_RANGE 2
- #define ADXL382_OP_MODE_60G_RANGE 2
- #define ADXL380_DIG_EN_REG 0x27
- #define ADXL380_CHAN_EN_MSK(chan) BIT(4 + (chan))
- #define ADXL380_FIFO_EN_MSK BIT(3)
- #define ADXL380_INT0_MAP0_REG 0x2B
- #define ADXL380_INT1_MAP0_REG 0x2D
- #define ADXL380_INT_MAP0_INACT_INT0_MSK BIT(6)
- #define ADXL380_INT_MAP0_ACT_INT0_MSK BIT(5)
- #define ADXL380_INT_MAP0_FIFO_WM_INT0_MSK BIT(3)
- #define ADXL380_INT0_MAP1_REG 0x2C
- #define ADXL380_INT1_MAP1_REG 0x2E
- #define ADXL380_INT_MAP1_DOUBLE_TAP_INT0_MSK BIT(1)
- #define ADXL380_INT_MAP1_SINGLE_TAP_INT0_MSK BIT(0)
- #define ADXL380_INT0_REG 0x5D
- #define ADXL380_INT0_POL_MSK BIT(7)
- #define ADXL380_RESET_REG 0x2A
- #define ADXL380_FIFO_DATA 0x1D
- #define ADXL380_DEVID_AD_VAL 0xAD
- #define ADXL380_RESET_CODE 0x52
- #define ADXL380_STATUS_0_REG 0x11
- #define ADXL380_STATUS_0_FIFO_FULL_MSK BIT(1)
- #define ADXL380_STATUS_0_FIFO_WM_MSK BIT(3)
- #define ADXL380_STATUS_1_INACT_MSK BIT(6)
- #define ADXL380_STATUS_1_ACT_MSK BIT(5)
- #define ADXL380_STATUS_1_DOUBLE_TAP_MSK BIT(1)
- #define ADXL380_STATUS_1_SINGLE_TAP_MSK BIT(0)
- #define ADXL380_FIFO_SAMPLES 315UL
- enum adxl380_channels {
- ADXL380_ACCEL_X,
- ADXL380_ACCEL_Y,
- ADXL380_ACCEL_Z,
- ADXL380_TEMP,
- ADXL380_CH_NUM
- };
- enum adxl380_axis {
- ADXL380_X_AXIS,
- ADXL380_Y_AXIS,
- ADXL380_Z_AXIS,
- };
- enum adxl380_activity_type {
- ADXL380_ACTIVITY,
- ADXL380_INACTIVITY,
- };
- enum adxl380_tap_type {
- ADXL380_SINGLE_TAP,
- ADXL380_DOUBLE_TAP,
- };
- enum adxl380_tap_time_type {
- ADXL380_TAP_TIME_LATENT,
- ADXL380_TAP_TIME_WINDOW,
- };
- static const int adxl380_range_scale_factor_tbl[] = { 1, 2, 4 };
- const struct adxl380_chip_info adxl380_chip_info = {
- .name = "adxl380",
- .chip_id = ADXL380_ID_VAL,
- .scale_tbl = {
- [ADXL380_OP_MODE_4G_RANGE] = { 0, 1307226 },
- [ADXL380_OP_MODE_8G_RANGE] = { 0, 2615434 },
- [ADXL380_OP_MODE_16G_RANGE] = { 0, 5229886 },
- },
- .samp_freq_tbl = { 8000, 16000, 32000 },
- /*
- * The datasheet defines an intercept of 470 LSB at 25 degC
- * and a sensitivity of 10.2 LSB/C.
- */
- .temp_offset = 25 * 102 / 10 - 470,
- };
- EXPORT_SYMBOL_NS_GPL(adxl380_chip_info, IIO_ADXL380);
- const struct adxl380_chip_info adxl382_chip_info = {
- .name = "adxl382",
- .chip_id = ADXL382_ID_VAL,
- .scale_tbl = {
- [ADXL382_OP_MODE_15G_RANGE] = { 0, 4903325 },
- [ADXL382_OP_MODE_30G_RANGE] = { 0, 9806650 },
- [ADXL382_OP_MODE_60G_RANGE] = { 0, 19613300 },
- },
- .samp_freq_tbl = { 16000, 32000, 64000 },
- /*
- * The datasheet defines an intercept of 570 LSB at 25 degC
- * and a sensitivity of 10.2 LSB/C.
- */
- .temp_offset = 25 * 102 / 10 - 570,
- };
- EXPORT_SYMBOL_NS_GPL(adxl382_chip_info, IIO_ADXL380);
- static const unsigned int adxl380_th_reg_high_addr[2] = {
- [ADXL380_ACTIVITY] = ADXL380_THRESH_ACT_H_REG,
- [ADXL380_INACTIVITY] = ADXL380_THRESH_INACT_H_REG,
- };
- static const unsigned int adxl380_time_reg_high_addr[2] = {
- [ADXL380_ACTIVITY] = ADXL380_TIME_ACT_H_REG,
- [ADXL380_INACTIVITY] = ADXL380_TIME_INACT_H_REG,
- };
- static const unsigned int adxl380_tap_time_reg[2] = {
- [ADXL380_TAP_TIME_LATENT] = ADXL380_TAP_LATENT_REG,
- [ADXL380_TAP_TIME_WINDOW] = ADXL380_TAP_WINDOW_REG,
- };
- struct adxl380_state {
- struct regmap *regmap;
- struct device *dev;
- const struct adxl380_chip_info *chip_info;
- /*
- * Synchronize access to members of driver state, and ensure atomicity
- * of consecutive regmap operations.
- */
- struct mutex lock;
- enum adxl380_axis tap_axis_en;
- u8 range;
- u8 odr;
- u8 fifo_set_size;
- u8 transf_buf[3];
- u16 watermark;
- u32 act_time_ms;
- u32 act_threshold;
- u32 inact_time_ms;
- u32 inact_threshold;
- u32 tap_latent_us;
- u32 tap_window_us;
- u32 tap_duration_us;
- u32 tap_threshold;
- int irq;
- int int_map[2];
- int lpf_tbl[4];
- int hpf_tbl[7][2];
- __be16 fifo_buf[ADXL380_FIFO_SAMPLES] __aligned(IIO_DMA_MINALIGN);
- };
- bool adxl380_readable_noinc_reg(struct device *dev, unsigned int reg)
- {
- return reg == ADXL380_FIFO_DATA;
- }
- EXPORT_SYMBOL_NS_GPL(adxl380_readable_noinc_reg, IIO_ADXL380);
- static int adxl380_set_measure_en(struct adxl380_state *st, bool en)
- {
- int ret;
- unsigned int act_inact_ctl;
- u8 op_mode = ADXL380_OP_MODE_STANDBY;
- if (en) {
- ret = regmap_read(st->regmap, ADXL380_ACT_INACT_CTL_REG, &act_inact_ctl);
- if (ret)
- return ret;
- /* Activity/ Inactivity detection available only in VLP/ULP mode */
- if (FIELD_GET(ADXL380_ACT_EN_MSK, act_inact_ctl) ||
- FIELD_GET(ADXL380_INACT_EN_MSK, act_inact_ctl))
- op_mode = ADXL380_OP_MODE_VLP;
- else
- op_mode = ADXL380_OP_MODE_HP;
- }
- return regmap_update_bits(st->regmap, ADXL380_OP_MODE_REG,
- ADXL380_OP_MODE_MSK,
- FIELD_PREP(ADXL380_OP_MODE_MSK, op_mode));
- }
- static void adxl380_scale_act_inact_thresholds(struct adxl380_state *st,
- u8 old_range,
- u8 new_range)
- {
- st->act_threshold = mult_frac(st->act_threshold,
- adxl380_range_scale_factor_tbl[old_range],
- adxl380_range_scale_factor_tbl[new_range]);
- st->inact_threshold = mult_frac(st->inact_threshold,
- adxl380_range_scale_factor_tbl[old_range],
- adxl380_range_scale_factor_tbl[new_range]);
- }
- static int adxl380_write_act_inact_threshold(struct adxl380_state *st,
- enum adxl380_activity_type act,
- unsigned int th)
- {
- int ret;
- u8 reg = adxl380_th_reg_high_addr[act];
- if (th > ADXL380_THRESH_MAX)
- return -EINVAL;
- ret = regmap_write(st->regmap, reg + 1, th & GENMASK(7, 0));
- if (ret)
- return ret;
- ret = regmap_update_bits(st->regmap, reg, GENMASK(2, 0), th >> 8);
- if (ret)
- return ret;
- if (act == ADXL380_ACTIVITY)
- st->act_threshold = th;
- else
- st->inact_threshold = th;
- return 0;
- }
- static int adxl380_set_act_inact_threshold(struct iio_dev *indio_dev,
- enum adxl380_activity_type act,
- u16 th)
- {
- struct adxl380_state *st = iio_priv(indio_dev);
- int ret;
- guard(mutex)(&st->lock);
- ret = adxl380_set_measure_en(st, false);
- if (ret)
- return ret;
- ret = adxl380_write_act_inact_threshold(st, act, th);
- if (ret)
- return ret;
- return adxl380_set_measure_en(st, true);
- }
- static int adxl380_set_tap_threshold_value(struct iio_dev *indio_dev, u8 th)
- {
- int ret;
- struct adxl380_state *st = iio_priv(indio_dev);
- guard(mutex)(&st->lock);
- ret = adxl380_set_measure_en(st, false);
- if (ret)
- return ret;
- ret = regmap_write(st->regmap, ADXL380_TAP_THRESH_REG, th);
- if (ret)
- return ret;
- st->tap_threshold = th;
- return adxl380_set_measure_en(st, true);
- }
- static int _adxl380_write_tap_time_us(struct adxl380_state *st,
- enum adxl380_tap_time_type tap_time_type,
- u32 us)
- {
- u8 reg = adxl380_tap_time_reg[tap_time_type];
- unsigned int reg_val;
- int ret;
- /* scale factor for tap window is 1250us / LSB */
- reg_val = DIV_ROUND_CLOSEST(us, 1250);
- if (reg_val > ADXL380_TAP_TIME_MAX)
- reg_val = ADXL380_TAP_TIME_MAX;
- ret = regmap_write(st->regmap, reg, reg_val);
- if (ret)
- return ret;
- if (tap_time_type == ADXL380_TAP_TIME_WINDOW)
- st->tap_window_us = us;
- else
- st->tap_latent_us = us;
- return 0;
- }
- static int adxl380_write_tap_time_us(struct adxl380_state *st,
- enum adxl380_tap_time_type tap_time_type, u32 us)
- {
- int ret;
- guard(mutex)(&st->lock);
- ret = adxl380_set_measure_en(st, false);
- if (ret)
- return ret;
- ret = _adxl380_write_tap_time_us(st, tap_time_type, us);
- if (ret)
- return ret;
- return adxl380_set_measure_en(st, true);
- }
- static int adxl380_write_tap_dur_us(struct iio_dev *indio_dev, u32 us)
- {
- int ret;
- unsigned int reg_val;
- struct adxl380_state *st = iio_priv(indio_dev);
- /* 625us per code is the scale factor of TAP_DUR register */
- reg_val = DIV_ROUND_CLOSEST(us, 625);
- ret = adxl380_set_measure_en(st, false);
- if (ret)
- return ret;
- ret = regmap_write(st->regmap, ADXL380_TAP_DUR_REG, reg_val);
- if (ret)
- return ret;
- return adxl380_set_measure_en(st, true);
- }
- static int adxl380_read_chn(struct adxl380_state *st, u8 addr)
- {
- int ret;
- guard(mutex)(&st->lock);
- ret = regmap_bulk_read(st->regmap, addr, &st->transf_buf, 2);
- if (ret)
- return ret;
- return get_unaligned_be16(st->transf_buf);
- }
- static int adxl380_get_odr(struct adxl380_state *st, int *odr)
- {
- int ret;
- unsigned int trig_cfg, odr_idx;
- ret = regmap_read(st->regmap, ADXL380_TRIG_CFG_REG, &trig_cfg);
- if (ret)
- return ret;
- odr_idx = (FIELD_GET(ADXL380_TRIG_CFG_SINC_RATE_MSK, trig_cfg) << 1) |
- (FIELD_GET(ADXL380_TRIG_CFG_DEC_2X_MSK, trig_cfg) & 1);
- *odr = st->chip_info->samp_freq_tbl[odr_idx];
- return 0;
- }
- static const int adxl380_lpf_div[] = {
- 1, 4, 8, 16,
- };
- static int adxl380_fill_lpf_tbl(struct adxl380_state *st)
- {
- int ret, i;
- int odr;
- ret = adxl380_get_odr(st, &odr);
- if (ret)
- return ret;
- for (i = 0; i < ARRAY_SIZE(st->lpf_tbl); i++)
- st->lpf_tbl[i] = DIV_ROUND_CLOSEST(odr, adxl380_lpf_div[i]);
- return 0;
- }
- static const int adxl380_hpf_mul[] = {
- 0, 247000, 62084, 15545, 3862, 954, 238,
- };
- static int adxl380_fill_hpf_tbl(struct adxl380_state *st)
- {
- int i, ret, odr_hz;
- u32 multiplier;
- u64 div, rem, odr;
- ret = adxl380_get_odr(st, &odr_hz);
- if (ret)
- return ret;
- for (i = 0; i < ARRAY_SIZE(adxl380_hpf_mul); i++) {
- odr = mul_u64_u32_shr(odr_hz, MEGA, 0);
- multiplier = adxl380_hpf_mul[i];
- div = div64_u64_rem(mul_u64_u32_shr(odr, multiplier, 0),
- TERA * 100, &rem);
- st->hpf_tbl[i][0] = div;
- st->hpf_tbl[i][1] = div_u64(rem, MEGA * 100);
- }
- return 0;
- }
- static int adxl380_set_odr(struct adxl380_state *st, u8 odr)
- {
- int ret;
- guard(mutex)(&st->lock);
- ret = adxl380_set_measure_en(st, false);
- if (ret)
- return ret;
- ret = regmap_update_bits(st->regmap, ADXL380_TRIG_CFG_REG,
- ADXL380_TRIG_CFG_DEC_2X_MSK,
- FIELD_PREP(ADXL380_TRIG_CFG_DEC_2X_MSK, odr & 1));
- if (ret)
- return ret;
- ret = regmap_update_bits(st->regmap, ADXL380_TRIG_CFG_REG,
- ADXL380_TRIG_CFG_SINC_RATE_MSK,
- FIELD_PREP(ADXL380_TRIG_CFG_SINC_RATE_MSK, odr >> 1));
- if (ret)
- return ret;
- ret = adxl380_set_measure_en(st, true);
- if (ret)
- return ret;
- ret = adxl380_fill_lpf_tbl(st);
- if (ret)
- return ret;
- return adxl380_fill_hpf_tbl(st);
- }
- static int adxl380_find_match_1d_tbl(const int *array, unsigned int size,
- int val)
- {
- int i;
- for (i = 0; i < size; i++) {
- if (val == array[i])
- return i;
- }
- return size - 1;
- }
- static int adxl380_find_match_2d_tbl(const int (*freq_tbl)[2], int n, int val, int val2)
- {
- int i;
- for (i = 0; i < n; i++) {
- if (freq_tbl[i][0] == val && freq_tbl[i][1] == val2)
- return i;
- }
- return -EINVAL;
- }
- static int adxl380_get_lpf(struct adxl380_state *st, int *lpf)
- {
- int ret;
- unsigned int trig_cfg, lpf_idx;
- guard(mutex)(&st->lock);
- ret = regmap_read(st->regmap, ADXL380_FILTER_REG, &trig_cfg);
- if (ret)
- return ret;
- lpf_idx = FIELD_GET(ADXL380_FILTER_LPF_MODE_MSK, trig_cfg);
- *lpf = st->lpf_tbl[lpf_idx];
- return 0;
- }
- static int adxl380_set_lpf(struct adxl380_state *st, u8 lpf)
- {
- int ret;
- u8 eq_bypass = 0;
- guard(mutex)(&st->lock);
- ret = adxl380_set_measure_en(st, false);
- if (ret)
- return ret;
- if (lpf)
- eq_bypass = 1;
- ret = regmap_update_bits(st->regmap, ADXL380_FILTER_REG,
- ADXL380_FILTER_EQ_FILT_MSK,
- FIELD_PREP(ADXL380_FILTER_EQ_FILT_MSK, eq_bypass));
- if (ret)
- return ret;
- ret = regmap_update_bits(st->regmap, ADXL380_FILTER_REG,
- ADXL380_FILTER_LPF_MODE_MSK,
- FIELD_PREP(ADXL380_FILTER_LPF_MODE_MSK, lpf));
- if (ret)
- return ret;
- return adxl380_set_measure_en(st, true);
- }
- static int adxl380_get_hpf(struct adxl380_state *st, int *hpf_int, int *hpf_frac)
- {
- int ret;
- unsigned int trig_cfg, hpf_idx;
- guard(mutex)(&st->lock);
- ret = regmap_read(st->regmap, ADXL380_FILTER_REG, &trig_cfg);
- if (ret)
- return ret;
- hpf_idx = FIELD_GET(ADXL380_FILTER_HPF_CORNER_MSK, trig_cfg);
- *hpf_int = st->hpf_tbl[hpf_idx][0];
- *hpf_frac = st->hpf_tbl[hpf_idx][1];
- return 0;
- }
- static int adxl380_set_hpf(struct adxl380_state *st, u8 hpf)
- {
- int ret;
- u8 hpf_path = 0;
- guard(mutex)(&st->lock);
- ret = adxl380_set_measure_en(st, false);
- if (ret)
- return ret;
- if (hpf)
- hpf_path = 1;
- ret = regmap_update_bits(st->regmap, ADXL380_FILTER_REG,
- ADXL380_FILTER_HPF_PATH_MSK,
- FIELD_PREP(ADXL380_FILTER_HPF_PATH_MSK, hpf_path));
- if (ret)
- return ret;
- ret = regmap_update_bits(st->regmap, ADXL380_FILTER_REG,
- ADXL380_FILTER_HPF_CORNER_MSK,
- FIELD_PREP(ADXL380_FILTER_HPF_CORNER_MSK, hpf));
- if (ret)
- return ret;
- return adxl380_set_measure_en(st, true);
- }
- static int _adxl380_set_act_inact_time_ms(struct adxl380_state *st,
- enum adxl380_activity_type act,
- u32 ms)
- {
- u8 reg = adxl380_time_reg_high_addr[act];
- unsigned int reg_val;
- int ret;
- /* 500us per code is the scale factor of TIME_ACT / TIME_INACT registers */
- reg_val = min(DIV_ROUND_CLOSEST(ms * 1000, 500), ADXL380_TIME_MAX);
- put_unaligned_be24(reg_val, &st->transf_buf[0]);
- ret = regmap_bulk_write(st->regmap, reg, st->transf_buf, sizeof(st->transf_buf));
- if (ret)
- return ret;
- if (act == ADXL380_ACTIVITY)
- st->act_time_ms = ms;
- else
- st->inact_time_ms = ms;
- return 0;
- }
- static int adxl380_set_act_inact_time_ms(struct adxl380_state *st,
- enum adxl380_activity_type act,
- u32 ms)
- {
- int ret;
- guard(mutex)(&st->lock);
- ret = adxl380_set_measure_en(st, false);
- if (ret)
- return ret;
- ret = _adxl380_set_act_inact_time_ms(st, act, ms);
- if (ret)
- return ret;
- return adxl380_set_measure_en(st, true);
- }
- static int adxl380_set_range(struct adxl380_state *st, u8 range)
- {
- int ret;
- guard(mutex)(&st->lock);
- ret = adxl380_set_measure_en(st, false);
- if (ret)
- return ret;
- ret = regmap_update_bits(st->regmap, ADXL380_OP_MODE_REG,
- ADXL380_OP_MODE_RANGE_MSK,
- FIELD_PREP(ADXL380_OP_MODE_RANGE_MSK, range));
- if (ret)
- return ret;
- adxl380_scale_act_inact_thresholds(st, st->range, range);
- /* Activity thresholds depend on range */
- ret = adxl380_write_act_inact_threshold(st, ADXL380_ACTIVITY,
- st->act_threshold);
- if (ret)
- return ret;
- ret = adxl380_write_act_inact_threshold(st, ADXL380_INACTIVITY,
- st->inact_threshold);
- if (ret)
- return ret;
- st->range = range;
- return adxl380_set_measure_en(st, true);
- }
- static int adxl380_write_act_inact_en(struct adxl380_state *st,
- enum adxl380_activity_type type,
- bool en)
- {
- if (type == ADXL380_ACTIVITY)
- return regmap_update_bits(st->regmap, ADXL380_ACT_INACT_CTL_REG,
- ADXL380_ACT_EN_MSK,
- FIELD_PREP(ADXL380_ACT_EN_MSK, en));
- return regmap_update_bits(st->regmap, ADXL380_ACT_INACT_CTL_REG,
- ADXL380_INACT_EN_MSK,
- FIELD_PREP(ADXL380_INACT_EN_MSK, en));
- }
- static int adxl380_read_act_inact_int(struct adxl380_state *st,
- enum adxl380_activity_type type,
- bool *en)
- {
- int ret;
- unsigned int reg_val;
- guard(mutex)(&st->lock);
- ret = regmap_read(st->regmap, st->int_map[0], ®_val);
- if (ret)
- return ret;
- if (type == ADXL380_ACTIVITY)
- *en = FIELD_GET(ADXL380_INT_MAP0_ACT_INT0_MSK, reg_val);
- else
- *en = FIELD_GET(ADXL380_INT_MAP0_INACT_INT0_MSK, reg_val);
- return 0;
- }
- static int adxl380_write_act_inact_int(struct adxl380_state *st,
- enum adxl380_activity_type act,
- bool en)
- {
- if (act == ADXL380_ACTIVITY)
- return regmap_update_bits(st->regmap, st->int_map[0],
- ADXL380_INT_MAP0_ACT_INT0_MSK,
- FIELD_PREP(ADXL380_INT_MAP0_ACT_INT0_MSK, en));
- return regmap_update_bits(st->regmap, st->int_map[0],
- ADXL380_INT_MAP0_INACT_INT0_MSK,
- FIELD_PREP(ADXL380_INT_MAP0_INACT_INT0_MSK, en));
- }
- static int adxl380_act_inact_config(struct adxl380_state *st,
- enum adxl380_activity_type type,
- bool en)
- {
- int ret;
- guard(mutex)(&st->lock);
- ret = adxl380_set_measure_en(st, false);
- if (ret)
- return ret;
- ret = adxl380_write_act_inact_en(st, type, en);
- if (ret)
- return ret;
- ret = adxl380_write_act_inact_int(st, type, en);
- if (ret)
- return ret;
- return adxl380_set_measure_en(st, true);
- }
- static int adxl380_write_tap_axis(struct adxl380_state *st,
- enum adxl380_axis axis)
- {
- int ret;
- ret = regmap_update_bits(st->regmap, ADXL380_TAP_CFG_REG,
- ADXL380_TAP_AXIS_MSK,
- FIELD_PREP(ADXL380_TAP_AXIS_MSK, axis));
- if (ret)
- return ret;
- st->tap_axis_en = axis;
- return 0;
- }
- static int adxl380_read_tap_int(struct adxl380_state *st, enum adxl380_tap_type type, bool *en)
- {
- int ret;
- unsigned int reg_val;
- ret = regmap_read(st->regmap, st->int_map[1], ®_val);
- if (ret)
- return ret;
- if (type == ADXL380_SINGLE_TAP)
- *en = FIELD_GET(ADXL380_INT_MAP1_SINGLE_TAP_INT0_MSK, reg_val);
- else
- *en = FIELD_GET(ADXL380_INT_MAP1_DOUBLE_TAP_INT0_MSK, reg_val);
- return 0;
- }
- static int adxl380_write_tap_int(struct adxl380_state *st, enum adxl380_tap_type type, bool en)
- {
- if (type == ADXL380_SINGLE_TAP)
- return regmap_update_bits(st->regmap, st->int_map[1],
- ADXL380_INT_MAP1_SINGLE_TAP_INT0_MSK,
- FIELD_PREP(ADXL380_INT_MAP1_SINGLE_TAP_INT0_MSK, en));
- return regmap_update_bits(st->regmap, st->int_map[1],
- ADXL380_INT_MAP1_DOUBLE_TAP_INT0_MSK,
- FIELD_PREP(ADXL380_INT_MAP1_DOUBLE_TAP_INT0_MSK, en));
- }
- static int adxl380_tap_config(struct adxl380_state *st,
- enum adxl380_axis axis,
- enum adxl380_tap_type type,
- bool en)
- {
- int ret;
- guard(mutex)(&st->lock);
- ret = adxl380_set_measure_en(st, false);
- if (ret)
- return ret;
- ret = adxl380_write_tap_axis(st, axis);
- if (ret)
- return ret;
- ret = adxl380_write_tap_int(st, type, en);
- if (ret)
- return ret;
- return adxl380_set_measure_en(st, true);
- }
- static int adxl380_set_fifo_samples(struct adxl380_state *st)
- {
- int ret;
- u16 fifo_samples = st->watermark * st->fifo_set_size;
- ret = regmap_update_bits(st->regmap, ADXL380_FIFO_CONFIG_0_REG,
- ADXL380_FIFO_SAMPLES_8_MSK,
- FIELD_PREP(ADXL380_FIFO_SAMPLES_8_MSK,
- (fifo_samples & BIT(8))));
- if (ret)
- return ret;
- return regmap_write(st->regmap, ADXL380_FIFO_CONFIG_1_REG,
- fifo_samples & 0xFF);
- }
- static int adxl380_get_status(struct adxl380_state *st, u8 *status0, u8 *status1)
- {
- int ret;
- /* STATUS0, STATUS1 are adjacent regs */
- ret = regmap_bulk_read(st->regmap, ADXL380_STATUS_0_REG,
- &st->transf_buf, 2);
- if (ret)
- return ret;
- *status0 = st->transf_buf[0];
- *status1 = st->transf_buf[1];
- return 0;
- }
- static int adxl380_get_fifo_entries(struct adxl380_state *st, u16 *fifo_entries)
- {
- int ret;
- ret = regmap_bulk_read(st->regmap, ADXL380_FIFO_STATUS_0_REG,
- &st->transf_buf, 2);
- if (ret)
- return ret;
- *fifo_entries = st->transf_buf[0] | ((BIT(0) & st->transf_buf[1]) << 8);
- return 0;
- }
- static void adxl380_push_event(struct iio_dev *indio_dev, s64 timestamp,
- u8 status1)
- {
- if (FIELD_GET(ADXL380_STATUS_1_ACT_MSK, status1))
- iio_push_event(indio_dev,
- IIO_MOD_EVENT_CODE(IIO_ACCEL, 0, IIO_MOD_X_OR_Y_OR_Z,
- IIO_EV_TYPE_THRESH, IIO_EV_DIR_RISING),
- timestamp);
- if (FIELD_GET(ADXL380_STATUS_1_INACT_MSK, status1))
- iio_push_event(indio_dev,
- IIO_MOD_EVENT_CODE(IIO_ACCEL, 0, IIO_MOD_X_OR_Y_OR_Z,
- IIO_EV_TYPE_THRESH, IIO_EV_DIR_FALLING),
- timestamp);
- if (FIELD_GET(ADXL380_STATUS_1_SINGLE_TAP_MSK, status1))
- iio_push_event(indio_dev,
- IIO_MOD_EVENT_CODE(IIO_ACCEL, 0, IIO_MOD_X_OR_Y_OR_Z,
- IIO_EV_TYPE_GESTURE, IIO_EV_DIR_SINGLETAP),
- timestamp);
- if (FIELD_GET(ADXL380_STATUS_1_DOUBLE_TAP_MSK, status1))
- iio_push_event(indio_dev,
- IIO_MOD_EVENT_CODE(IIO_ACCEL, 0, IIO_MOD_X_OR_Y_OR_Z,
- IIO_EV_TYPE_GESTURE, IIO_EV_DIR_DOUBLETAP),
- timestamp);
- }
- static irqreturn_t adxl380_irq_handler(int irq, void *p)
- {
- struct iio_dev *indio_dev = p;
- struct adxl380_state *st = iio_priv(indio_dev);
- u8 status0, status1;
- u16 fifo_entries;
- int i;
- int ret;
- guard(mutex)(&st->lock);
- ret = adxl380_get_status(st, &status0, &status1);
- if (ret)
- return IRQ_HANDLED;
- adxl380_push_event(indio_dev, iio_get_time_ns(indio_dev), status1);
- if (!FIELD_GET(ADXL380_STATUS_0_FIFO_WM_MSK, status0))
- return IRQ_HANDLED;
- ret = adxl380_get_fifo_entries(st, &fifo_entries);
- if (ret)
- return IRQ_HANDLED;
- for (i = 0; i < fifo_entries; i += st->fifo_set_size) {
- ret = regmap_noinc_read(st->regmap, ADXL380_FIFO_DATA,
- &st->fifo_buf[i],
- 2 * st->fifo_set_size);
- if (ret)
- return IRQ_HANDLED;
- iio_push_to_buffers(indio_dev, &st->fifo_buf[i]);
- }
- return IRQ_HANDLED;
- }
- static int adxl380_write_calibbias_value(struct adxl380_state *st,
- unsigned long chan_addr,
- s8 calibbias)
- {
- int ret;
- guard(mutex)(&st->lock);
- ret = adxl380_set_measure_en(st, false);
- if (ret)
- return ret;
- ret = regmap_write(st->regmap, ADXL380_X_DSM_OFFSET_REG + chan_addr, calibbias);
- if (ret)
- return ret;
- return adxl380_set_measure_en(st, true);
- }
- static int adxl380_read_calibbias_value(struct adxl380_state *st,
- unsigned long chan_addr,
- int *calibbias)
- {
- int ret;
- unsigned int reg_val;
- guard(mutex)(&st->lock);
- ret = regmap_read(st->regmap, ADXL380_X_DSM_OFFSET_REG + chan_addr, ®_val);
- if (ret)
- return ret;
- *calibbias = sign_extend32(reg_val, 7);
- return 0;
- }
- static ssize_t hwfifo_watermark_min_show(struct device *dev,
- struct device_attribute *attr,
- char *buf)
- {
- return sysfs_emit(buf, "1\n");
- }
- static ssize_t hwfifo_watermark_max_show(struct device *dev,
- struct device_attribute *attr,
- char *buf)
- {
- return sysfs_emit(buf, "%lu\n", ADXL380_FIFO_SAMPLES);
- }
- static ssize_t adxl380_get_fifo_watermark(struct device *dev,
- struct device_attribute *attr,
- char *buf)
- {
- struct iio_dev *indio_dev = dev_to_iio_dev(dev);
- struct adxl380_state *st = iio_priv(indio_dev);
- return sysfs_emit(buf, "%d\n", st->watermark);
- }
- static ssize_t adxl380_get_fifo_enabled(struct device *dev,
- struct device_attribute *attr,
- char *buf)
- {
- struct iio_dev *indio_dev = dev_to_iio_dev(dev);
- struct adxl380_state *st = iio_priv(indio_dev);
- int ret;
- unsigned int reg_val;
- ret = regmap_read(st->regmap, ADXL380_DIG_EN_REG, ®_val);
- if (ret)
- return ret;
- return sysfs_emit(buf, "%lu\n",
- FIELD_GET(ADXL380_FIFO_EN_MSK, reg_val));
- }
- static IIO_DEVICE_ATTR_RO(hwfifo_watermark_min, 0);
- static IIO_DEVICE_ATTR_RO(hwfifo_watermark_max, 0);
- static IIO_DEVICE_ATTR(hwfifo_watermark, 0444,
- adxl380_get_fifo_watermark, NULL, 0);
- static IIO_DEVICE_ATTR(hwfifo_enabled, 0444,
- adxl380_get_fifo_enabled, NULL, 0);
- static const struct iio_dev_attr *adxl380_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 int adxl380_buffer_postenable(struct iio_dev *indio_dev)
- {
- struct adxl380_state *st = iio_priv(indio_dev);
- int i;
- int ret;
- guard(mutex)(&st->lock);
- ret = adxl380_set_measure_en(st, false);
- if (ret)
- return ret;
- ret = regmap_update_bits(st->regmap,
- st->int_map[0],
- ADXL380_INT_MAP0_FIFO_WM_INT0_MSK,
- FIELD_PREP(ADXL380_INT_MAP0_FIFO_WM_INT0_MSK, 1));
- if (ret)
- return ret;
- for_each_clear_bit(i, indio_dev->active_scan_mask, ADXL380_CH_NUM) {
- ret = regmap_update_bits(st->regmap, ADXL380_DIG_EN_REG,
- ADXL380_CHAN_EN_MSK(i),
- 0 << (4 + i));
- if (ret)
- return ret;
- }
- st->fifo_set_size = bitmap_weight(indio_dev->active_scan_mask,
- iio_get_masklength(indio_dev));
- if ((st->watermark * st->fifo_set_size) > ADXL380_FIFO_SAMPLES)
- st->watermark = (ADXL380_FIFO_SAMPLES / st->fifo_set_size);
- ret = adxl380_set_fifo_samples(st);
- if (ret)
- return ret;
- ret = regmap_update_bits(st->regmap, ADXL380_DIG_EN_REG, ADXL380_FIFO_EN_MSK,
- FIELD_PREP(ADXL380_FIFO_EN_MSK, 1));
- if (ret)
- return ret;
- return adxl380_set_measure_en(st, true);
- }
- static int adxl380_buffer_predisable(struct iio_dev *indio_dev)
- {
- struct adxl380_state *st = iio_priv(indio_dev);
- int ret, i;
- guard(mutex)(&st->lock);
- ret = adxl380_set_measure_en(st, false);
- if (ret)
- return ret;
- ret = regmap_update_bits(st->regmap,
- st->int_map[0],
- ADXL380_INT_MAP0_FIFO_WM_INT0_MSK,
- FIELD_PREP(ADXL380_INT_MAP0_FIFO_WM_INT0_MSK, 0));
- if (ret)
- return ret;
- for (i = 0; i < indio_dev->num_channels; i++) {
- ret = regmap_update_bits(st->regmap, ADXL380_DIG_EN_REG,
- ADXL380_CHAN_EN_MSK(i),
- 1 << (4 + i));
- if (ret)
- return ret;
- }
- ret = regmap_update_bits(st->regmap, ADXL380_DIG_EN_REG, ADXL380_FIFO_EN_MSK,
- FIELD_PREP(ADXL380_FIFO_EN_MSK, 0));
- if (ret)
- return ret;
- return adxl380_set_measure_en(st, true);
- }
- static const struct iio_buffer_setup_ops adxl380_buffer_ops = {
- .postenable = adxl380_buffer_postenable,
- .predisable = adxl380_buffer_predisable,
- };
- static int adxl380_read_raw(struct iio_dev *indio_dev,
- struct iio_chan_spec const *chan,
- int *val, int *val2, long info)
- {
- struct adxl380_state *st = iio_priv(indio_dev);
- int ret;
- switch (info) {
- case IIO_CHAN_INFO_RAW:
- ret = iio_device_claim_direct_mode(indio_dev);
- if (ret)
- return ret;
- ret = adxl380_read_chn(st, chan->address);
- iio_device_release_direct_mode(indio_dev);
- if (ret < 0)
- return ret;
- *val = sign_extend32(ret >> chan->scan_type.shift,
- chan->scan_type.realbits - 1);
- return IIO_VAL_INT;
- case IIO_CHAN_INFO_SCALE:
- switch (chan->type) {
- case IIO_ACCEL:
- scoped_guard(mutex, &st->lock) {
- *val = st->chip_info->scale_tbl[st->range][0];
- *val2 = st->chip_info->scale_tbl[st->range][1];
- }
- return IIO_VAL_INT_PLUS_NANO;
- case IIO_TEMP:
- /* 10.2 LSB / Degree Celsius */
- *val = 10000;
- *val2 = 102;
- return IIO_VAL_FRACTIONAL;
- default:
- return -EINVAL;
- }
- case IIO_CHAN_INFO_OFFSET:
- switch (chan->type) {
- case IIO_TEMP:
- *val = st->chip_info->temp_offset;
- return IIO_VAL_INT;
- default:
- return -EINVAL;
- }
- case IIO_CHAN_INFO_CALIBBIAS:
- switch (chan->type) {
- case IIO_ACCEL:
- ret = adxl380_read_calibbias_value(st, chan->scan_index, val);
- if (ret)
- return ret;
- return IIO_VAL_INT;
- default:
- return -EINVAL;
- }
- case IIO_CHAN_INFO_SAMP_FREQ:
- ret = adxl380_get_odr(st, val);
- if (ret)
- return ret;
- return IIO_VAL_INT;
- case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY:
- ret = adxl380_get_lpf(st, val);
- if (ret)
- return ret;
- return IIO_VAL_INT;
- case IIO_CHAN_INFO_HIGH_PASS_FILTER_3DB_FREQUENCY:
- ret = adxl380_get_hpf(st, val, val2);
- if (ret)
- return ret;
- return IIO_VAL_INT_PLUS_MICRO;
- }
- return -EINVAL;
- }
- static int adxl380_read_avail(struct iio_dev *indio_dev,
- struct iio_chan_spec const *chan,
- const int **vals, int *type, int *length,
- long mask)
- {
- struct adxl380_state *st = iio_priv(indio_dev);
- if (chan->type != IIO_ACCEL)
- return -EINVAL;
- switch (mask) {
- case IIO_CHAN_INFO_SCALE:
- *vals = (const int *)st->chip_info->scale_tbl;
- *type = IIO_VAL_INT_PLUS_NANO;
- *length = ARRAY_SIZE(st->chip_info->scale_tbl) * 2;
- return IIO_AVAIL_LIST;
- case IIO_CHAN_INFO_SAMP_FREQ:
- *vals = (const int *)st->chip_info->samp_freq_tbl;
- *type = IIO_VAL_INT;
- *length = ARRAY_SIZE(st->chip_info->samp_freq_tbl);
- return IIO_AVAIL_LIST;
- case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY:
- *vals = (const int *)st->lpf_tbl;
- *type = IIO_VAL_INT;
- *length = ARRAY_SIZE(st->lpf_tbl);
- return IIO_AVAIL_LIST;
- case IIO_CHAN_INFO_HIGH_PASS_FILTER_3DB_FREQUENCY:
- *vals = (const int *)st->hpf_tbl;
- *type = IIO_VAL_INT_PLUS_MICRO;
- /* Values are stored in a 2D matrix */
- *length = ARRAY_SIZE(st->hpf_tbl) * 2;
- return IIO_AVAIL_LIST;
- default:
- return -EINVAL;
- }
- }
- static int adxl380_write_raw(struct iio_dev *indio_dev,
- struct iio_chan_spec const *chan,
- int val, int val2, long info)
- {
- struct adxl380_state *st = iio_priv(indio_dev);
- int odr_index, lpf_index, hpf_index, range_index;
- switch (info) {
- case IIO_CHAN_INFO_SAMP_FREQ:
- odr_index = adxl380_find_match_1d_tbl(st->chip_info->samp_freq_tbl,
- ARRAY_SIZE(st->chip_info->samp_freq_tbl),
- val);
- return adxl380_set_odr(st, odr_index);
- case IIO_CHAN_INFO_CALIBBIAS:
- return adxl380_write_calibbias_value(st, chan->scan_index, val);
- case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY:
- lpf_index = adxl380_find_match_1d_tbl(st->lpf_tbl,
- ARRAY_SIZE(st->lpf_tbl),
- val);
- return adxl380_set_lpf(st, lpf_index);
- case IIO_CHAN_INFO_HIGH_PASS_FILTER_3DB_FREQUENCY:
- hpf_index = adxl380_find_match_2d_tbl(st->hpf_tbl,
- ARRAY_SIZE(st->hpf_tbl),
- val, val2);
- if (hpf_index < 0)
- return hpf_index;
- return adxl380_set_hpf(st, hpf_index);
- case IIO_CHAN_INFO_SCALE:
- range_index = adxl380_find_match_2d_tbl(st->chip_info->scale_tbl,
- ARRAY_SIZE(st->chip_info->scale_tbl),
- val, val2);
- if (range_index < 0)
- return range_index;
- return adxl380_set_range(st, range_index);
- default:
- return -EINVAL;
- }
- }
- static int adxl380_write_raw_get_fmt(struct iio_dev *indio_dev,
- struct iio_chan_spec const *chan,
- long info)
- {
- switch (info) {
- case IIO_CHAN_INFO_SCALE:
- if (chan->type != IIO_ACCEL)
- return -EINVAL;
- return IIO_VAL_INT_PLUS_NANO;
- default:
- return IIO_VAL_INT_PLUS_MICRO;
- }
- }
- static int adxl380_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 adxl380_state *st = iio_priv(indio_dev);
- int ret;
- bool int_en;
- bool tap_axis_en = false;
- switch (chan->channel2) {
- case IIO_MOD_X:
- tap_axis_en = st->tap_axis_en == ADXL380_X_AXIS;
- break;
- case IIO_MOD_Y:
- tap_axis_en = st->tap_axis_en == ADXL380_Y_AXIS;
- break;
- case IIO_MOD_Z:
- tap_axis_en = st->tap_axis_en == ADXL380_Z_AXIS;
- break;
- default:
- return -EINVAL;
- }
- switch (dir) {
- case IIO_EV_DIR_RISING:
- ret = adxl380_read_act_inact_int(st, ADXL380_ACTIVITY, &int_en);
- if (ret)
- return ret;
- return int_en;
- case IIO_EV_DIR_FALLING:
- ret = adxl380_read_act_inact_int(st, ADXL380_INACTIVITY, &int_en);
- if (ret)
- return ret;
- return int_en;
- case IIO_EV_DIR_SINGLETAP:
- ret = adxl380_read_tap_int(st, ADXL380_SINGLE_TAP, &int_en);
- if (ret)
- return ret;
- return int_en && tap_axis_en;
- case IIO_EV_DIR_DOUBLETAP:
- ret = adxl380_read_tap_int(st, ADXL380_DOUBLE_TAP, &int_en);
- if (ret)
- return ret;
- return int_en && tap_axis_en;
- default:
- return -EINVAL;
- }
- }
- static int adxl380_write_event_config(struct iio_dev *indio_dev,
- const struct iio_chan_spec *chan,
- enum iio_event_type type,
- enum iio_event_direction dir,
- int state)
- {
- struct adxl380_state *st = iio_priv(indio_dev);
- enum adxl380_axis axis;
- switch (chan->channel2) {
- case IIO_MOD_X:
- axis = ADXL380_X_AXIS;
- break;
- case IIO_MOD_Y:
- axis = ADXL380_Y_AXIS;
- break;
- case IIO_MOD_Z:
- axis = ADXL380_Z_AXIS;
- break;
- default:
- return -EINVAL;
- }
- switch (dir) {
- case IIO_EV_DIR_RISING:
- return adxl380_act_inact_config(st, ADXL380_ACTIVITY, state);
- case IIO_EV_DIR_FALLING:
- return adxl380_act_inact_config(st, ADXL380_INACTIVITY, state);
- case IIO_EV_DIR_SINGLETAP:
- return adxl380_tap_config(st, axis, ADXL380_SINGLE_TAP, state);
- case IIO_EV_DIR_DOUBLETAP:
- return adxl380_tap_config(st, axis, ADXL380_DOUBLE_TAP, state);
- default:
- return -EINVAL;
- }
- }
- static int adxl380_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 adxl380_state *st = iio_priv(indio_dev);
- guard(mutex)(&st->lock);
- switch (type) {
- case IIO_EV_TYPE_THRESH:
- switch (info) {
- case IIO_EV_INFO_VALUE: {
- switch (dir) {
- case IIO_EV_DIR_RISING:
- *val = st->act_threshold;
- return IIO_VAL_INT;
- case IIO_EV_DIR_FALLING:
- *val = st->inact_threshold;
- return IIO_VAL_INT;
- default:
- return -EINVAL;
- }
- }
- case IIO_EV_INFO_PERIOD:
- switch (dir) {
- case IIO_EV_DIR_RISING:
- *val = st->act_time_ms;
- *val2 = 1000;
- return IIO_VAL_FRACTIONAL;
- case IIO_EV_DIR_FALLING:
- *val = st->inact_time_ms;
- *val2 = 1000;
- return IIO_VAL_FRACTIONAL;
- default:
- return -EINVAL;
- }
- default:
- return -EINVAL;
- }
- case IIO_EV_TYPE_GESTURE:
- switch (info) {
- case IIO_EV_INFO_VALUE:
- *val = st->tap_threshold;
- return IIO_VAL_INT;
- case IIO_EV_INFO_RESET_TIMEOUT:
- *val = st->tap_window_us;
- *val2 = 1000000;
- return IIO_VAL_FRACTIONAL;
- case IIO_EV_INFO_TAP2_MIN_DELAY:
- *val = st->tap_latent_us;
- *val2 = 1000000;
- return IIO_VAL_FRACTIONAL;
- default:
- return -EINVAL;
- }
- default:
- return -EINVAL;
- }
- }
- static int adxl380_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 adxl380_state *st = iio_priv(indio_dev);
- u32 val_ms, val_us;
- if (chan->type != IIO_ACCEL)
- return -EINVAL;
- switch (type) {
- case IIO_EV_TYPE_THRESH:
- switch (info) {
- case IIO_EV_INFO_VALUE:
- switch (dir) {
- case IIO_EV_DIR_RISING:
- return adxl380_set_act_inact_threshold(indio_dev,
- ADXL380_ACTIVITY, val);
- case IIO_EV_DIR_FALLING:
- return adxl380_set_act_inact_threshold(indio_dev,
- ADXL380_INACTIVITY, val);
- default:
- return -EINVAL;
- }
- case IIO_EV_INFO_PERIOD:
- val_ms = val * 1000 + DIV_ROUND_UP(val2, 1000);
- switch (dir) {
- case IIO_EV_DIR_RISING:
- return adxl380_set_act_inact_time_ms(st,
- ADXL380_ACTIVITY, val_ms);
- case IIO_EV_DIR_FALLING:
- return adxl380_set_act_inact_time_ms(st,
- ADXL380_INACTIVITY, val_ms);
- default:
- return -EINVAL;
- }
- default:
- return -EINVAL;
- }
- case IIO_EV_TYPE_GESTURE:
- switch (info) {
- case IIO_EV_INFO_VALUE:
- return adxl380_set_tap_threshold_value(indio_dev, val);
- case IIO_EV_INFO_RESET_TIMEOUT:
- val_us = val * 1000000 + val2;
- return adxl380_write_tap_time_us(st,
- ADXL380_TAP_TIME_WINDOW,
- val_us);
- case IIO_EV_INFO_TAP2_MIN_DELAY:
- val_us = val * 1000000 + val2;
- return adxl380_write_tap_time_us(st,
- ADXL380_TAP_TIME_LATENT,
- val_us);
- default:
- return -EINVAL;
- }
- default:
- return -EINVAL;
- }
- }
- static ssize_t in_accel_gesture_tap_maxtomin_time_show(struct device *dev,
- struct device_attribute *attr,
- char *buf)
- {
- int vals[2];
- struct iio_dev *indio_dev = dev_to_iio_dev(dev);
- struct adxl380_state *st = iio_priv(indio_dev);
- guard(mutex)(&st->lock);
- vals[0] = st->tap_duration_us;
- vals[1] = MICRO;
- return iio_format_value(buf, IIO_VAL_FRACTIONAL, 2, vals);
- }
- static ssize_t in_accel_gesture_tap_maxtomin_time_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 adxl380_state *st = iio_priv(indio_dev);
- int ret, val_int, val_fract_us;
- guard(mutex)(&st->lock);
- ret = iio_str_to_fixpoint(buf, 100000, &val_int, &val_fract_us);
- if (ret)
- return ret;
- /* maximum value is 255 * 625 us = 0.159375 seconds */
- if (val_int || val_fract_us > 159375 || val_fract_us < 0)
- return -EINVAL;
- ret = adxl380_write_tap_dur_us(indio_dev, val_fract_us);
- if (ret)
- return ret;
- return len;
- }
- static IIO_DEVICE_ATTR_RW(in_accel_gesture_tap_maxtomin_time, 0);
- static struct attribute *adxl380_event_attributes[] = {
- &iio_dev_attr_in_accel_gesture_tap_maxtomin_time.dev_attr.attr,
- NULL
- };
- static const struct attribute_group adxl380_event_attribute_group = {
- .attrs = adxl380_event_attributes,
- };
- static int adxl380_reg_access(struct iio_dev *indio_dev,
- unsigned int reg,
- unsigned int writeval,
- unsigned int *readval)
- {
- struct adxl380_state *st = iio_priv(indio_dev);
- if (readval)
- return regmap_read(st->regmap, reg, readval);
- return regmap_write(st->regmap, reg, writeval);
- }
- static int adxl380_set_watermark(struct iio_dev *indio_dev, unsigned int val)
- {
- struct adxl380_state *st = iio_priv(indio_dev);
- st->watermark = min(val, ADXL380_FIFO_SAMPLES);
- return 0;
- }
- static const struct iio_info adxl380_info = {
- .read_raw = adxl380_read_raw,
- .read_avail = &adxl380_read_avail,
- .write_raw = adxl380_write_raw,
- .write_raw_get_fmt = adxl380_write_raw_get_fmt,
- .read_event_config = adxl380_read_event_config,
- .write_event_config = adxl380_write_event_config,
- .read_event_value = adxl380_read_event_value,
- .write_event_value = adxl380_write_event_value,
- .event_attrs = &adxl380_event_attribute_group,
- .debugfs_reg_access = &adxl380_reg_access,
- .hwfifo_set_watermark = adxl380_set_watermark,
- };
- static const struct iio_event_spec adxl380_events[] = {
- {
- .type = IIO_EV_TYPE_THRESH,
- .dir = IIO_EV_DIR_RISING,
- .mask_shared_by_type = BIT(IIO_EV_INFO_ENABLE) |
- BIT(IIO_EV_INFO_VALUE) |
- BIT(IIO_EV_INFO_PERIOD),
- },
- {
- .type = IIO_EV_TYPE_THRESH,
- .dir = IIO_EV_DIR_FALLING,
- .mask_shared_by_type = BIT(IIO_EV_INFO_ENABLE) |
- BIT(IIO_EV_INFO_VALUE) |
- BIT(IIO_EV_INFO_PERIOD),
- },
- {
- .type = IIO_EV_TYPE_GESTURE,
- .dir = IIO_EV_DIR_SINGLETAP,
- .mask_separate = BIT(IIO_EV_INFO_ENABLE),
- .mask_shared_by_type = BIT(IIO_EV_INFO_VALUE) |
- BIT(IIO_EV_INFO_RESET_TIMEOUT),
- },
- {
- .type = IIO_EV_TYPE_GESTURE,
- .dir = IIO_EV_DIR_DOUBLETAP,
- .mask_separate = BIT(IIO_EV_INFO_ENABLE),
- .mask_shared_by_type = BIT(IIO_EV_INFO_VALUE) |
- BIT(IIO_EV_INFO_RESET_TIMEOUT) |
- BIT(IIO_EV_INFO_TAP2_MIN_DELAY),
- },
- };
- #define ADXL380_ACCEL_CHANNEL(index, reg, axis) { \
- .type = IIO_ACCEL, \
- .address = reg, \
- .modified = 1, \
- .channel2 = IIO_MOD_##axis, \
- .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
- BIT(IIO_CHAN_INFO_CALIBBIAS), \
- .info_mask_shared_by_all = 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) | \
- BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY) | \
- BIT(IIO_CHAN_INFO_HIGH_PASS_FILTER_3DB_FREQUENCY), \
- .info_mask_shared_by_type_available = \
- BIT(IIO_CHAN_INFO_SCALE) | \
- BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY) | \
- BIT(IIO_CHAN_INFO_HIGH_PASS_FILTER_3DB_FREQUENCY), \
- .scan_index = index, \
- .scan_type = { \
- .sign = 's', \
- .realbits = 16, \
- .storagebits = 16, \
- .endianness = IIO_BE, \
- }, \
- .event_spec = adxl380_events, \
- .num_event_specs = ARRAY_SIZE(adxl380_events) \
- }
- static const struct iio_chan_spec adxl380_channels[] = {
- ADXL380_ACCEL_CHANNEL(0, ADXL380_X_DATA_H_REG, X),
- ADXL380_ACCEL_CHANNEL(1, ADXL380_Y_DATA_H_REG, Y),
- ADXL380_ACCEL_CHANNEL(2, ADXL380_Z_DATA_H_REG, Z),
- {
- .type = IIO_TEMP,
- .address = ADXL380_T_DATA_H_REG,
- .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
- BIT(IIO_CHAN_INFO_SCALE) |
- BIT(IIO_CHAN_INFO_OFFSET),
- .scan_index = 3,
- .scan_type = {
- .sign = 's',
- .realbits = 12,
- .storagebits = 16,
- .shift = 4,
- .endianness = IIO_BE,
- },
- },
- };
- static int adxl380_config_irq(struct iio_dev *indio_dev)
- {
- struct adxl380_state *st = iio_priv(indio_dev);
- unsigned long irq_flag;
- struct irq_data *desc;
- u32 irq_type;
- u8 polarity;
- int ret;
- st->irq = fwnode_irq_get_byname(dev_fwnode(st->dev), "INT0");
- if (st->irq > 0) {
- st->int_map[0] = ADXL380_INT0_MAP0_REG;
- st->int_map[1] = ADXL380_INT0_MAP1_REG;
- } else {
- st->irq = fwnode_irq_get_byname(dev_fwnode(st->dev), "INT1");
- if (st->irq > 0)
- return dev_err_probe(st->dev, -ENODEV,
- "no interrupt name specified");
- st->int_map[0] = ADXL380_INT1_MAP0_REG;
- st->int_map[1] = ADXL380_INT1_MAP1_REG;
- }
- desc = irq_get_irq_data(st->irq);
- if (!desc)
- return dev_err_probe(st->dev, -EINVAL, "Could not find IRQ %d\n", st->irq);
- irq_type = irqd_get_trigger_type(desc);
- if (irq_type == IRQ_TYPE_LEVEL_HIGH) {
- polarity = 0;
- irq_flag = IRQF_TRIGGER_HIGH | IRQF_ONESHOT;
- } else if (irq_type == IRQ_TYPE_LEVEL_LOW) {
- polarity = 1;
- irq_flag = IRQF_TRIGGER_LOW | IRQF_ONESHOT;
- } else {
- return dev_err_probe(st->dev, -EINVAL,
- "Invalid interrupt 0x%x. Only level interrupts supported\n",
- irq_type);
- }
- ret = regmap_update_bits(st->regmap, ADXL380_INT0_REG,
- ADXL380_INT0_POL_MSK,
- FIELD_PREP(ADXL380_INT0_POL_MSK, polarity));
- if (ret)
- return ret;
- return devm_request_threaded_irq(st->dev, st->irq, NULL,
- adxl380_irq_handler, irq_flag,
- indio_dev->name, indio_dev);
- }
- static int adxl380_setup(struct iio_dev *indio_dev)
- {
- unsigned int reg_val;
- u16 part_id, chip_id;
- int ret, i;
- struct adxl380_state *st = iio_priv(indio_dev);
- ret = regmap_read(st->regmap, ADXL380_DEVID_AD_REG, ®_val);
- if (ret)
- return ret;
- if (reg_val != ADXL380_DEVID_AD_VAL)
- dev_warn(st->dev, "Unknown chip id %x\n", reg_val);
- ret = regmap_bulk_read(st->regmap, ADLX380_PART_ID_REG,
- &st->transf_buf, 2);
- if (ret)
- return ret;
- part_id = get_unaligned_be16(st->transf_buf);
- part_id >>= 4;
- if (part_id != ADXL380_ID_VAL)
- dev_warn(st->dev, "Unknown part id %x\n", part_id);
- ret = regmap_read(st->regmap, ADXL380_MISC_0_REG, ®_val);
- if (ret)
- return ret;
- /* Bit to differentiate between ADXL380/382. */
- if (reg_val & ADXL380_XL382_MSK)
- chip_id = ADXL382_ID_VAL;
- else
- chip_id = ADXL380_ID_VAL;
- if (chip_id != st->chip_info->chip_id)
- dev_warn(st->dev, "Unknown chip id %x\n", chip_id);
- ret = regmap_write(st->regmap, ADXL380_RESET_REG, ADXL380_RESET_CODE);
- if (ret)
- return ret;
- /*
- * A latency of approximately 0.5 ms is required after soft reset.
- * Stated in the register REG_RESET description.
- */
- fsleep(500);
- for (i = 0; i < indio_dev->num_channels; i++) {
- ret = regmap_update_bits(st->regmap, ADXL380_DIG_EN_REG,
- ADXL380_CHAN_EN_MSK(i),
- 1 << (4 + i));
- if (ret)
- return ret;
- }
- ret = regmap_update_bits(st->regmap, ADXL380_FIFO_CONFIG_0_REG,
- ADXL380_FIFO_MODE_MSK,
- FIELD_PREP(ADXL380_FIFO_MODE_MSK, ADXL380_FIFO_STREAMED));
- if (ret)
- return ret;
- /* Select all 3 axis for act/inact detection. */
- ret = regmap_update_bits(st->regmap, ADXL380_SNSR_AXIS_EN_REG,
- ADXL380_ACT_INACT_AXIS_EN_MSK,
- FIELD_PREP(ADXL380_ACT_INACT_AXIS_EN_MSK,
- ADXL380_ACT_INACT_AXIS_EN_MSK));
- if (ret)
- return ret;
- ret = adxl380_config_irq(indio_dev);
- if (ret)
- return ret;
- ret = adxl380_fill_lpf_tbl(st);
- if (ret)
- return ret;
- ret = adxl380_fill_hpf_tbl(st);
- if (ret)
- return ret;
- return adxl380_set_measure_en(st, true);
- }
- int adxl380_probe(struct device *dev, struct regmap *regmap,
- const struct adxl380_chip_info *chip_info)
- {
- struct iio_dev *indio_dev;
- struct adxl380_state *st;
- int ret;
- indio_dev = devm_iio_device_alloc(dev, sizeof(*st));
- if (!indio_dev)
- return -ENOMEM;
- st = iio_priv(indio_dev);
- st->dev = dev;
- st->regmap = regmap;
- st->chip_info = chip_info;
- mutex_init(&st->lock);
- indio_dev->channels = adxl380_channels;
- indio_dev->num_channels = ARRAY_SIZE(adxl380_channels);
- indio_dev->name = chip_info->name;
- indio_dev->info = &adxl380_info;
- indio_dev->modes = INDIO_DIRECT_MODE;
- ret = devm_regulator_get_enable(dev, "vddio");
- if (ret)
- return dev_err_probe(st->dev, ret,
- "Failed to get vddio regulator\n");
- ret = devm_regulator_get_enable(st->dev, "vsupply");
- if (ret)
- return dev_err_probe(st->dev, ret,
- "Failed to get vsupply regulator\n");
- ret = adxl380_setup(indio_dev);
- if (ret)
- return ret;
- ret = devm_iio_kfifo_buffer_setup_ext(st->dev, indio_dev,
- &adxl380_buffer_ops,
- adxl380_fifo_attributes);
- if (ret)
- return ret;
- return devm_iio_device_register(dev, indio_dev);
- }
- EXPORT_SYMBOL_NS_GPL(adxl380_probe, IIO_ADXL380);
- MODULE_AUTHOR("Ramona Gradinariu <ramona.gradinariu@analog.com>");
- MODULE_AUTHOR("Antoniu Miclaus <antoniu.miclaus@analog.com>");
- MODULE_DESCRIPTION("Analog Devices ADXL380 3-axis accelerometer driver");
- MODULE_LICENSE("GPL");
|